Git init
authorKibum Kim <kb0929.kim@samsung.com>
Fri, 6 Jan 2012 16:10:50 +0000 (01:10 +0900)
committerKibum Kim <kb0929.kim@samsung.com>
Fri, 6 Jan 2012 16:10:50 +0000 (01:10 +0900)
3984 files changed:
AUTHORS [new file with mode: 0644]
BIBLIOGRAPHY [new file with mode: 0644]
BUGS [new file with mode: 0644]
CODING_STYLE [new file with mode: 0644]
COPYING [new file with mode: 0644]
COPYING-LGPL-2.1 [new file with mode: 0644]
COPYING-MPL-1.1 [new file with mode: 0644]
ChangeLog [new file with mode: 0644]
HACKING [new file with mode: 0644]
INSTALL [new file with mode: 0644]
KNOWN_ISSUES [new file with mode: 0644]
Makefile.am [new file with mode: 0644]
Makefile.win32 [new file with mode: 0644]
NEWS [new file with mode: 0644]
PORTING_GUIDE [new file with mode: 0644]
README [new file with mode: 0644]
README.win32 [new file with mode: 0644]
RELEASING [new file with mode: 0644]
acinclude.m4 [new file with mode: 0644]
autogen.sh [new file with mode: 0755]
boilerplate/.gitignore [new file with mode: 0644]
boilerplate/Makefile.am [new file with mode: 0644]
boilerplate/Makefile.sources [new file with mode: 0644]
boilerplate/Makefile.win32 [new file with mode: 0644]
boilerplate/Makefile.win32.features [new file with mode: 0644]
boilerplate/README [new file with mode: 0644]
boilerplate/cairo-boilerplate-beos.cpp [new file with mode: 0644]
boilerplate/cairo-boilerplate-directfb.c [new file with mode: 0644]
boilerplate/cairo-boilerplate-drm.c [new file with mode: 0644]
boilerplate/cairo-boilerplate-egl.c [new file with mode: 0644]
boilerplate/cairo-boilerplate-getopt.c [new file with mode: 0644]
boilerplate/cairo-boilerplate-getopt.h [new file with mode: 0644]
boilerplate/cairo-boilerplate-glx.c [new file with mode: 0644]
boilerplate/cairo-boilerplate-pdf.c [new file with mode: 0644]
boilerplate/cairo-boilerplate-private.h [new file with mode: 0644]
boilerplate/cairo-boilerplate-ps.c [new file with mode: 0644]
boilerplate/cairo-boilerplate-qt.cpp [new file with mode: 0644]
boilerplate/cairo-boilerplate-quartz.c [new file with mode: 0644]
boilerplate/cairo-boilerplate-scaled-font.h [new file with mode: 0644]
boilerplate/cairo-boilerplate-script.c [new file with mode: 0644]
boilerplate/cairo-boilerplate-skia.c [new file with mode: 0644]
boilerplate/cairo-boilerplate-svg.c [new file with mode: 0644]
boilerplate/cairo-boilerplate-system.c [new file with mode: 0644]
boilerplate/cairo-boilerplate-system.h [new file with mode: 0644]
boilerplate/cairo-boilerplate-test-surfaces.c [new file with mode: 0644]
boilerplate/cairo-boilerplate-vg.c [new file with mode: 0644]
boilerplate/cairo-boilerplate-wgl.c [new file with mode: 0644]
boilerplate/cairo-boilerplate-win32-printing.c [new file with mode: 0644]
boilerplate/cairo-boilerplate-win32.c [new file with mode: 0644]
boilerplate/cairo-boilerplate-xcb.c [new file with mode: 0644]
boilerplate/cairo-boilerplate-xlib.c [new file with mode: 0644]
boilerplate/cairo-boilerplate-xlib.h [new file with mode: 0644]
boilerplate/cairo-boilerplate.c [new file with mode: 0644]
boilerplate/cairo-boilerplate.h [new file with mode: 0644]
boilerplate/check-link.c [new file with mode: 0644]
boilerplate/make-cairo-boilerplate-constructors.sh [new file with mode: 0644]
build/.gitignore [new file with mode: 0644]
build/Makefile.am.analysis [new file with mode: 0644]
build/Makefile.am.changelog [new file with mode: 0644]
build/Makefile.am.common [new file with mode: 0644]
build/Makefile.am.gtk-doc [new file with mode: 0644]
build/Makefile.am.releasing [new file with mode: 0644]
build/Makefile.win32.common [new file with mode: 0644]
build/Makefile.win32.features [new file with mode: 0644]
build/Makefile.win32.features-h [new file with mode: 0644]
build/aclocal.cairo.m4 [new file with mode: 0644]
build/aclocal.compare.m4 [new file with mode: 0644]
build/aclocal.enable.m4 [new file with mode: 0644]
build/aclocal.float.m4 [new file with mode: 0644]
build/aclocal.gtk-doc.m4 [new file with mode: 0644]
build/aclocal.makefile.m4 [new file with mode: 0644]
build/aclocal.pkg.m4 [new file with mode: 0644]
build/configure.ac.analysis [new file with mode: 0644]
build/configure.ac.features [new file with mode: 0644]
build/configure.ac.noversion [new file with mode: 0644]
build/configure.ac.pthread [new file with mode: 0644]
build/configure.ac.system [new file with mode: 0644]
build/configure.ac.tools [new file with mode: 0644]
build/configure.ac.version [new file with mode: 0644]
build/configure.ac.warnings [new file with mode: 0644]
cairo-version.h [new file with mode: 0644]
configure.ac [new file with mode: 0644]
debian/changelog [new file with mode: 0644]
debian/compat [new file with mode: 0644]
debian/control [new file with mode: 0755]
debian/copyright [new file with mode: 0644]
debian/libcairo2-dev.install [new file with mode: 0644]
debian/libcairo2-doc.doc-base [new file with mode: 0644]
debian/libcairo2-doc.install [new file with mode: 0644]
debian/libcairo2.install [new file with mode: 0644]
debian/libcairo2.install.in [new file with mode: 0644]
debian/libcairo2.install.opt [new file with mode: 0644]
debian/rules [new file with mode: 0755]
debian/watch [new file with mode: 0644]
doc/.gitignore [new file with mode: 0644]
doc/Makefile.am [new file with mode: 0644]
doc/public/.gitignore [new file with mode: 0644]
doc/public/Makefile.am [new file with mode: 0644]
doc/public/README [new file with mode: 0644]
doc/public/cairo-docs.xml [new file with mode: 0644]
doc/public/cairo-overrides.txt [new file with mode: 0644]
doc/public/cairo-sections.txt [new file with mode: 0644]
doc/public/cairo.types [new file with mode: 0644]
doc/public/check-doc-coverage.sh [new file with mode: 0755]
doc/public/check-doc-syntax.sh [new file with mode: 0755]
doc/public/language-bindings.xml [new file with mode: 0644]
doc/tutorial/slides/.gitignore [new file with mode: 0644]
doc/tutorial/slides/cairo-blank.svg [new file with mode: 0644]
doc/tutorial/slides/cairo-code.svg [new file with mode: 0644]
doc/tutorial/slides/cairo-large-content.svg [new file with mode: 0644]
doc/tutorial/slides/cairo-separator.svg [new file with mode: 0644]
doc/tutorial/slides/cairo-title.svg [new file with mode: 0644]
doc/tutorial/slides/cairo.svg [new file with mode: 0644]
doc/tutorial/slides/circle-cairo-large.png [new file with mode: 0644]
doc/tutorial/slides/circle-cairo.png [new file with mode: 0644]
doc/tutorial/slides/circle-ooo-large.png [new file with mode: 0644]
doc/tutorial/slides/circle-ooo.png [new file with mode: 0644]
doc/tutorial/slides/expander-fuzzy-large.png [new file with mode: 0644]
doc/tutorial/slides/expander-fuzzy.png [new file with mode: 0644]
doc/tutorial/slides/expander-sharp-large.png [new file with mode: 0644]
doc/tutorial/slides/expander-sharp.png [new file with mode: 0644]
doc/tutorial/slides/fuzzies.svg [new file with mode: 0644]
doc/tutorial/slides/jaggies.svg [new file with mode: 0644]
doc/tutorial/slides/rendering-model.png [new file with mode: 0644]
doc/tutorial/slides/tutorial.xml [new file with mode: 0644]
doc/tutorial/src/.gitignore [new file with mode: 0644]
doc/tutorial/src/README [new file with mode: 0644]
doc/tutorial/src/circle.c [new file with mode: 0644]
doc/tutorial/src/include/cairo-tutorial-gtk.h [new file with mode: 0644]
doc/tutorial/src/include/cairo-tutorial-pdf.h [new file with mode: 0644]
doc/tutorial/src/include/cairo-tutorial-png.h [new file with mode: 0644]
doc/tutorial/src/include/cairo-tutorial-xlib.h [new file with mode: 0644]
doc/tutorial/src/include/cairo-tutorial.h [new file with mode: 0644]
doc/tutorial/src/lca.c [new file with mode: 0644]
doc/tutorial/src/singular.c [new file with mode: 0644]
doc/tutorial/src/twin.c [new file with mode: 0644]
perf/.gitignore [new file with mode: 0644]
perf/COPYING [new file with mode: 0644]
perf/Makefile.am [new file with mode: 0644]
perf/Makefile.sources [new file with mode: 0644]
perf/Makefile.win32 [new file with mode: 0644]
perf/README [new file with mode: 0644]
perf/cairo-analyse-trace.c [new file with mode: 0644]
perf/cairo-perf-chart.c [new file with mode: 0644]
perf/cairo-perf-compare-backends.c [new file with mode: 0644]
perf/cairo-perf-diff [new file with mode: 0755]
perf/cairo-perf-diff-files.c [new file with mode: 0644]
perf/cairo-perf-graph [new file with mode: 0755]
perf/cairo-perf-graph-files.c [new file with mode: 0644]
perf/cairo-perf-graph-widget.c [new file with mode: 0644]
perf/cairo-perf-graph.h [new file with mode: 0644]
perf/cairo-perf-micro.c [new file with mode: 0644]
perf/cairo-perf-print.c [new file with mode: 0644]
perf/cairo-perf-report.c [new file with mode: 0644]
perf/cairo-perf-trace.c [new file with mode: 0644]
perf/cairo-perf.c [new file with mode: 0644]
perf/cairo-perf.h [new file with mode: 0644]
perf/cairo-stats.c [new file with mode: 0644]
perf/cairo-stats.h [new file with mode: 0644]
perf/dirent-win32.h [new file with mode: 0644]
perf/make-html.py [new file with mode: 0755]
perf/micro/Makefile.am [new file with mode: 0644]
perf/micro/Makefile.sources [new file with mode: 0644]
perf/micro/Makefile.win32 [new file with mode: 0644]
perf/micro/a1-curve.c [new file with mode: 0644]
perf/micro/a1-line.c [new file with mode: 0644]
perf/micro/box-outline.c [new file with mode: 0644]
perf/micro/cairo-perf-cover.c [new file with mode: 0644]
perf/micro/composite-checker.c [new file with mode: 0644]
perf/micro/curve.c [new file with mode: 0644]
perf/micro/disjoint.c [new file with mode: 0644]
perf/micro/dragon.c [new file with mode: 0644]
perf/micro/fill-clip.c [new file with mode: 0644]
perf/micro/fill.c [new file with mode: 0644]
perf/micro/glyphs.c [new file with mode: 0644]
perf/micro/hash-table.c [new file with mode: 0644]
perf/micro/hatching.c [new file with mode: 0644]
perf/micro/intersections.c [new file with mode: 0644]
perf/micro/line.c [new file with mode: 0644]
perf/micro/long-dashed-lines.c [new file with mode: 0644]
perf/micro/long-lines.c [new file with mode: 0644]
perf/micro/many-curves.c [new file with mode: 0644]
perf/micro/many-fills.c [new file with mode: 0644]
perf/micro/many-strokes.c [new file with mode: 0644]
perf/micro/mask.c [new file with mode: 0644]
perf/micro/mosaic.c [new file with mode: 0644]
perf/micro/mosaic.h [new file with mode: 0644]
perf/micro/paint-with-alpha.c [new file with mode: 0644]
perf/micro/paint.c [new file with mode: 0644]
perf/micro/pattern_create_radial.c [new file with mode: 0644]
perf/micro/pixel.c [new file with mode: 0644]
perf/micro/pythagoras-tree.c [new file with mode: 0644]
perf/micro/rectangles.c [new file with mode: 0644]
perf/micro/rounded-rectangles.c [new file with mode: 0644]
perf/micro/sierpinski.c [new file with mode: 0644]
perf/micro/spiral.c [new file with mode: 0644]
perf/micro/stroke.c [new file with mode: 0644]
perf/micro/subimage_copy.c [new file with mode: 0644]
perf/micro/tessellate.c [new file with mode: 0644]
perf/micro/text.c [new file with mode: 0644]
perf/micro/tiger.c [new file with mode: 0644]
perf/micro/twin.c [new file with mode: 0644]
perf/micro/unaligned-clip.c [new file with mode: 0644]
perf/micro/wave.c [new file with mode: 0644]
perf/micro/wide-fills.c [new file with mode: 0644]
perf/micro/wide-strokes.c [new file with mode: 0644]
perf/micro/world-map.c [new file with mode: 0644]
perf/micro/world-map.h [new file with mode: 0644]
perf/micro/zrusin-another.h [new file with mode: 0644]
perf/micro/zrusin.c [new file with mode: 0644]
src/.gitignore [new file with mode: 0644]
src/Makefile.am [new file with mode: 0644]
src/Makefile.am.analysis [new file with mode: 0644]
src/Makefile.sources [new file with mode: 0644]
src/Makefile.win32 [new file with mode: 0644]
src/Makefile.win32.features [new file with mode: 0644]
src/README [new file with mode: 0644]
src/cairo-analysis-surface-private.h [new file with mode: 0644]
src/cairo-analysis-surface.c [new file with mode: 0644]
src/cairo-arc-private.h [new file with mode: 0644]
src/cairo-arc.c [new file with mode: 0644]
src/cairo-array-private.h [new file with mode: 0644]
src/cairo-array.c [new file with mode: 0644]
src/cairo-atomic-private.h [new file with mode: 0644]
src/cairo-atomic.c [new file with mode: 0644]
src/cairo-backend-private.h [new file with mode: 0644]
src/cairo-base64-stream.c [new file with mode: 0644]
src/cairo-base85-stream.c [new file with mode: 0644]
src/cairo-bentley-ottmann-rectangular.c [new file with mode: 0644]
src/cairo-bentley-ottmann-rectilinear.c [new file with mode: 0644]
src/cairo-bentley-ottmann.c [new file with mode: 0644]
src/cairo-beos-surface.cpp [new file with mode: 0644]
src/cairo-beos.h [new file with mode: 0644]
src/cairo-botor-scan-converter.c [new file with mode: 0644]
src/cairo-box-private.h [new file with mode: 0644]
src/cairo-boxes-intersect.c [new file with mode: 0644]
src/cairo-boxes-private.h [new file with mode: 0644]
src/cairo-boxes.c [new file with mode: 0644]
src/cairo-cache-private.h [new file with mode: 0644]
src/cairo-cache.c [new file with mode: 0644]
src/cairo-cff-subset.c [new file with mode: 0644]
src/cairo-clip-boxes.c [new file with mode: 0644]
src/cairo-clip-polygon.c [new file with mode: 0644]
src/cairo-clip-private.h [new file with mode: 0644]
src/cairo-clip-region.c [new file with mode: 0644]
src/cairo-clip-surface.c [new file with mode: 0644]
src/cairo-clip-tor-scan-converter.c [new file with mode: 0644]
src/cairo-clip.c [new file with mode: 0644]
src/cairo-color.c [new file with mode: 0644]
src/cairo-combsort-private.h [new file with mode: 0644]
src/cairo-compiler-private.h [new file with mode: 0644]
src/cairo-composite-rectangles-private.h [new file with mode: 0644]
src/cairo-composite-rectangles.c [new file with mode: 0644]
src/cairo-compositor-private.h [new file with mode: 0644]
src/cairo-compositor.c [new file with mode: 0644]
src/cairo-contour-private.h [new file with mode: 0644]
src/cairo-contour.c [new file with mode: 0644]
src/cairo-debug.c [new file with mode: 0644]
src/cairo-default-context-private.h [new file with mode: 0644]
src/cairo-default-context.c [new file with mode: 0644]
src/cairo-deflate-stream.c [new file with mode: 0644]
src/cairo-deprecated.h [new file with mode: 0644]
src/cairo-device-private.h [new file with mode: 0644]
src/cairo-device.c [new file with mode: 0644]
src/cairo-directfb-surface.c [new file with mode: 0644]
src/cairo-directfb.h [new file with mode: 0644]
src/cairo-drm.h [new file with mode: 0644]
src/cairo-egl-context.c [new file with mode: 0644]
src/cairo-error-private.h [new file with mode: 0644]
src/cairo-error.c [new file with mode: 0644]
src/cairo-fallback-compositor.c [new file with mode: 0644]
src/cairo-features-uninstalled.pc.in [new file with mode: 0644]
src/cairo-features.pc.in [new file with mode: 0644]
src/cairo-fixed-private.h [new file with mode: 0644]
src/cairo-fixed-type-private.h [new file with mode: 0644]
src/cairo-fixed.c [new file with mode: 0644]
src/cairo-font-face-twin-data.c [new file with mode: 0644]
src/cairo-font-face-twin.c [new file with mode: 0644]
src/cairo-font-face.c [new file with mode: 0644]
src/cairo-font-options.c [new file with mode: 0644]
src/cairo-fontconfig-private.h [new file with mode: 0644]
src/cairo-freed-pool-private.h [new file with mode: 0644]
src/cairo-freed-pool.c [new file with mode: 0644]
src/cairo-freelist-private.h [new file with mode: 0644]
src/cairo-freelist-type-private.h [new file with mode: 0644]
src/cairo-freelist.c [new file with mode: 0644]
src/cairo-ft-font.c [new file with mode: 0644]
src/cairo-ft-private.h [new file with mode: 0644]
src/cairo-ft.h [new file with mode: 0644]
src/cairo-gl-composite.c [new file with mode: 0644]
src/cairo-gl-device.c [new file with mode: 0644]
src/cairo-gl-dispatch-private.h [new file with mode: 0644]
src/cairo-gl-dispatch.c [new file with mode: 0644]
src/cairo-gl-ext-def-private.h [new file with mode: 0644]
src/cairo-gl-glyphs.c [new file with mode: 0644]
src/cairo-gl-gradient-private.h [new file with mode: 0644]
src/cairo-gl-gradient.c [new file with mode: 0644]
src/cairo-gl-info.c [new file with mode: 0644]
src/cairo-gl-operand.c [new file with mode: 0644]
src/cairo-gl-private.h [new file with mode: 0644]
src/cairo-gl-shaders.c [new file with mode: 0644]
src/cairo-gl-spans-compositor.c [new file with mode: 0644]
src/cairo-gl-surface-legacy.c [new file with mode: 0644]
src/cairo-gl-surface.c [new file with mode: 0644]
src/cairo-gl-traps-compositor.c [new file with mode: 0644]
src/cairo-gl.h [new file with mode: 0644]
src/cairo-glx-context.c [new file with mode: 0644]
src/cairo-gstate-private.h [new file with mode: 0644]
src/cairo-gstate.c [new file with mode: 0644]
src/cairo-hash-private.h [new file with mode: 0644]
src/cairo-hash.c [new file with mode: 0644]
src/cairo-hull.c [new file with mode: 0644]
src/cairo-image-compositor.c [new file with mode: 0644]
src/cairo-image-info-private.h [new file with mode: 0644]
src/cairo-image-info.c [new file with mode: 0644]
src/cairo-image-mask-compositor.c [new file with mode: 0644]
src/cairo-image-source.c [new file with mode: 0644]
src/cairo-image-spans-compositor.c [new file with mode: 0644]
src/cairo-image-surface-private.h [new file with mode: 0644]
src/cairo-image-surface.c [new file with mode: 0644]
src/cairo-list-private.h [new file with mode: 0644]
src/cairo-lzw.c [new file with mode: 0644]
src/cairo-malloc-private.h [new file with mode: 0644]
src/cairo-mask-compositor.c [new file with mode: 0644]
src/cairo-matrix.c [new file with mode: 0644]
src/cairo-mesh-pattern-rasterizer.c [new file with mode: 0644]
src/cairo-mime-surface.c [new file with mode: 0644]
src/cairo-misc.c [new file with mode: 0644]
src/cairo-mono-scan-converter.c [new file with mode: 0644]
src/cairo-mutex-impl-private.h [new file with mode: 0644]
src/cairo-mutex-list-private.h [new file with mode: 0644]
src/cairo-mutex-private.h [new file with mode: 0644]
src/cairo-mutex-type-private.h [new file with mode: 0644]
src/cairo-mutex.c [new file with mode: 0644]
src/cairo-no-compositor.c [new file with mode: 0644]
src/cairo-observer.c [new file with mode: 0644]
src/cairo-os2-private.h [new file with mode: 0644]
src/cairo-os2-surface.c [new file with mode: 0644]
src/cairo-os2.h [new file with mode: 0644]
src/cairo-output-stream-private.h [new file with mode: 0644]
src/cairo-output-stream.c [new file with mode: 0644]
src/cairo-paginated-private.h [new file with mode: 0644]
src/cairo-paginated-surface-private.h [new file with mode: 0644]
src/cairo-paginated-surface.c [new file with mode: 0644]
src/cairo-path-bounds.c [new file with mode: 0644]
src/cairo-path-fill.c [new file with mode: 0644]
src/cairo-path-fixed-private.h [new file with mode: 0644]
src/cairo-path-fixed.c [new file with mode: 0644]
src/cairo-path-in-fill.c [new file with mode: 0644]
src/cairo-path-private.h [new file with mode: 0644]
src/cairo-path-stroke-boxes.c [new file with mode: 0644]
src/cairo-path-stroke-polygon.c [new file with mode: 0644]
src/cairo-path-stroke-tristrip.c [new file with mode: 0644]
src/cairo-path-stroke.c [new file with mode: 0644]
src/cairo-path.c [new file with mode: 0644]
src/cairo-pattern-private.h [new file with mode: 0644]
src/cairo-pattern.c [new file with mode: 0644]
src/cairo-pdf-operators-private.h [new file with mode: 0644]
src/cairo-pdf-operators.c [new file with mode: 0644]
src/cairo-pdf-shading-private.h [new file with mode: 0644]
src/cairo-pdf-shading.c [new file with mode: 0644]
src/cairo-pdf-surface-private.h [new file with mode: 0644]
src/cairo-pdf-surface.c [new file with mode: 0644]
src/cairo-pdf.h [new file with mode: 0644]
src/cairo-pen.c [new file with mode: 0644]
src/cairo-png.c [new file with mode: 0644]
src/cairo-polygon-intersect.c [new file with mode: 0644]
src/cairo-polygon-reduce.c [new file with mode: 0644]
src/cairo-polygon.c [new file with mode: 0644]
src/cairo-private.h [new file with mode: 0644]
src/cairo-ps-surface-private.h [new file with mode: 0644]
src/cairo-ps-surface.c [new file with mode: 0644]
src/cairo-ps.h [new file with mode: 0644]
src/cairo-qt-surface.cpp [new file with mode: 0644]
src/cairo-qt.h [new file with mode: 0644]
src/cairo-quartz-font.c [new file with mode: 0644]
src/cairo-quartz-image-surface.c [new file with mode: 0644]
src/cairo-quartz-image.h [new file with mode: 0644]
src/cairo-quartz-private.h [new file with mode: 0644]
src/cairo-quartz-surface.c [new file with mode: 0644]
src/cairo-quartz.h [new file with mode: 0644]
src/cairo-recording-surface-private.h [new file with mode: 0644]
src/cairo-recording-surface.c [new file with mode: 0644]
src/cairo-rectangle.c [new file with mode: 0644]
src/cairo-rectangular-scan-converter.c [new file with mode: 0644]
src/cairo-reference-count-private.h [new file with mode: 0644]
src/cairo-region-private.h [new file with mode: 0644]
src/cairo-region.c [new file with mode: 0644]
src/cairo-rtree-private.h [new file with mode: 0644]
src/cairo-rtree.c [new file with mode: 0644]
src/cairo-scaled-font-private.h [new file with mode: 0644]
src/cairo-scaled-font-subsets-private.h [new file with mode: 0644]
src/cairo-scaled-font-subsets.c [new file with mode: 0644]
src/cairo-scaled-font.c [new file with mode: 0644]
src/cairo-script-private.h [new file with mode: 0644]
src/cairo-script-surface.c [new file with mode: 0644]
src/cairo-script.h [new file with mode: 0644]
src/cairo-skia-surface.cpp [new file with mode: 0644]
src/cairo-skia.h [new file with mode: 0644]
src/cairo-slope-private.h [new file with mode: 0644]
src/cairo-slope.c [new file with mode: 0644]
src/cairo-spans-compositor-private.h [new file with mode: 0644]
src/cairo-spans-compositor.c [new file with mode: 0644]
src/cairo-spans-private.h [new file with mode: 0644]
src/cairo-spans.c [new file with mode: 0644]
src/cairo-spline.c [new file with mode: 0644]
src/cairo-stroke-dash-private.h [new file with mode: 0644]
src/cairo-stroke-dash.c [new file with mode: 0644]
src/cairo-stroke-style.c [new file with mode: 0644]
src/cairo-surface-backend-private.h [new file with mode: 0644]
src/cairo-surface-clipper-private.h [new file with mode: 0644]
src/cairo-surface-clipper.c [new file with mode: 0644]
src/cairo-surface-fallback-private.h [new file with mode: 0644]
src/cairo-surface-fallback.c [new file with mode: 0644]
src/cairo-surface-observer-private.h [new file with mode: 0644]
src/cairo-surface-observer.c [new file with mode: 0644]
src/cairo-surface-offset-private.h [new file with mode: 0644]
src/cairo-surface-offset.c [new file with mode: 0644]
src/cairo-surface-private.h [new file with mode: 0644]
src/cairo-surface-snapshot-private.h [new file with mode: 0644]
src/cairo-surface-snapshot.c [new file with mode: 0644]
src/cairo-surface-subsurface-private.h [new file with mode: 0644]
src/cairo-surface-subsurface.c [new file with mode: 0644]
src/cairo-surface-wrapper-private.h [new file with mode: 0644]
src/cairo-surface-wrapper.c [new file with mode: 0644]
src/cairo-surface.c [new file with mode: 0644]
src/cairo-svg-surface-private.h [new file with mode: 0644]
src/cairo-svg-surface.c [new file with mode: 0644]
src/cairo-svg.h [new file with mode: 0644]
src/cairo-system.c [new file with mode: 0644]
src/cairo-tee-surface-private.h [new file with mode: 0644]
src/cairo-tee-surface.c [new file with mode: 0644]
src/cairo-tee.h [new file with mode: 0644]
src/cairo-time-private.h [new file with mode: 0644]
src/cairo-time.c [new file with mode: 0644]
src/cairo-tor-scan-converter.c [new file with mode: 0644]
src/cairo-tor22-scan-converter.c [new file with mode: 0644]
src/cairo-toy-font-face.c [new file with mode: 0644]
src/cairo-traps-compositor.c [new file with mode: 0644]
src/cairo-traps-private.h [new file with mode: 0644]
src/cairo-traps.c [new file with mode: 0644]
src/cairo-tristrip-private.h [new file with mode: 0644]
src/cairo-tristrip.c [new file with mode: 0644]
src/cairo-truetype-subset-private.h [new file with mode: 0644]
src/cairo-truetype-subset.c [new file with mode: 0644]
src/cairo-type1-fallback.c [new file with mode: 0644]
src/cairo-type1-glyph-names.c [new file with mode: 0644]
src/cairo-type1-private.h [new file with mode: 0644]
src/cairo-type1-subset.c [new file with mode: 0644]
src/cairo-type3-glyph-surface-private.h [new file with mode: 0644]
src/cairo-type3-glyph-surface.c [new file with mode: 0644]
src/cairo-types-private.h [new file with mode: 0644]
src/cairo-unicode.c [new file with mode: 0644]
src/cairo-uninstalled.pc.in [new file with mode: 0644]
src/cairo-user-font-private.h [new file with mode: 0644]
src/cairo-user-font.c [new file with mode: 0644]
src/cairo-version.c [new file with mode: 0644]
src/cairo-version.h [new file with mode: 0644]
src/cairo-vg-surface.c [new file with mode: 0644]
src/cairo-vg.h [new file with mode: 0644]
src/cairo-wgl-context.c [new file with mode: 0644]
src/cairo-wideint-private.h [new file with mode: 0644]
src/cairo-wideint-type-private.h [new file with mode: 0644]
src/cairo-wideint.c [new file with mode: 0644]
src/cairo-win32-font.c [new file with mode: 0644]
src/cairo-win32-printing-surface.c [new file with mode: 0644]
src/cairo-win32-private.h [new file with mode: 0644]
src/cairo-win32-surface.c [new file with mode: 0644]
src/cairo-win32.h [new file with mode: 0644]
src/cairo-xcb-connection-core.c [new file with mode: 0644]
src/cairo-xcb-connection-render.c [new file with mode: 0644]
src/cairo-xcb-connection-shm.c [new file with mode: 0644]
src/cairo-xcb-connection.c [new file with mode: 0644]
src/cairo-xcb-private.h [new file with mode: 0644]
src/cairo-xcb-screen.c [new file with mode: 0644]
src/cairo-xcb-shm.c [new file with mode: 0644]
src/cairo-xcb-surface-cairo.c [new file with mode: 0644]
src/cairo-xcb-surface-core.c [new file with mode: 0644]
src/cairo-xcb-surface-render.c [new file with mode: 0644]
src/cairo-xcb-surface.c [new file with mode: 0644]
src/cairo-xcb.h [new file with mode: 0644]
src/cairo-xlib-core-compositor.c [new file with mode: 0644]
src/cairo-xlib-display.c [new file with mode: 0644]
src/cairo-xlib-fallback-compositor.c [new file with mode: 0644]
src/cairo-xlib-private.h [new file with mode: 0644]
src/cairo-xlib-render-compositor.c [new file with mode: 0644]
src/cairo-xlib-screen.c [new file with mode: 0644]
src/cairo-xlib-source.c [new file with mode: 0644]
src/cairo-xlib-surface-private.h [new file with mode: 0644]
src/cairo-xlib-surface.c [new file with mode: 0644]
src/cairo-xlib-visual.c [new file with mode: 0644]
src/cairo-xlib-xcb-surface.c [new file with mode: 0644]
src/cairo-xlib-xrender-private.h [new file with mode: 0644]
src/cairo-xlib-xrender.h [new file with mode: 0644]
src/cairo-xlib.h [new file with mode: 0644]
src/cairo-xml-surface.c [new file with mode: 0644]
src/cairo-xml.h [new file with mode: 0644]
src/cairo.c [new file with mode: 0644]
src/cairo.h [new file with mode: 0644]
src/cairo.pc.in [new file with mode: 0644]
src/cairoint.h [new file with mode: 0644]
src/check-def.sh [new file with mode: 0755]
src/check-doc-syntax.sh [new file with mode: 0755]
src/check-has-hidden-symbols.c [new file with mode: 0644]
src/check-headers.sh [new file with mode: 0755]
src/check-link.c [new file with mode: 0644]
src/check-plt.sh [new file with mode: 0755]
src/check-preprocessor-syntax.sh [new file with mode: 0755]
src/drm/cairo-drm-bo.c [new file with mode: 0644]
src/drm/cairo-drm-gallium-surface.c [new file with mode: 0644]
src/drm/cairo-drm-i915-glyphs.c [new file with mode: 0644]
src/drm/cairo-drm-i915-private.h [new file with mode: 0644]
src/drm/cairo-drm-i915-shader.c [new file with mode: 0644]
src/drm/cairo-drm-i915-spans.c [new file with mode: 0644]
src/drm/cairo-drm-i915-surface.c [new file with mode: 0644]
src/drm/cairo-drm-i965-glyphs.c [new file with mode: 0644]
src/drm/cairo-drm-i965-private.h [new file with mode: 0644]
src/drm/cairo-drm-i965-shader.c [new file with mode: 0644]
src/drm/cairo-drm-i965-spans.c [new file with mode: 0644]
src/drm/cairo-drm-i965-surface.c [new file with mode: 0644]
src/drm/cairo-drm-intel-brw-defines.h [new file with mode: 0644]
src/drm/cairo-drm-intel-brw-eu-emit.c [new file with mode: 0644]
src/drm/cairo-drm-intel-brw-eu-util.c [new file with mode: 0644]
src/drm/cairo-drm-intel-brw-eu.c [new file with mode: 0644]
src/drm/cairo-drm-intel-brw-eu.h [new file with mode: 0644]
src/drm/cairo-drm-intel-brw-structs.h [new file with mode: 0644]
src/drm/cairo-drm-intel-command-private.h [new file with mode: 0644]
src/drm/cairo-drm-intel-debug.c [new file with mode: 0644]
src/drm/cairo-drm-intel-ioctl-private.h [new file with mode: 0644]
src/drm/cairo-drm-intel-private.h [new file with mode: 0644]
src/drm/cairo-drm-intel-surface.c [new file with mode: 0644]
src/drm/cairo-drm-intel.c [new file with mode: 0644]
src/drm/cairo-drm-ioctl-private.h [new file with mode: 0644]
src/drm/cairo-drm-private.h [new file with mode: 0644]
src/drm/cairo-drm-radeon-private.h [new file with mode: 0644]
src/drm/cairo-drm-radeon-surface.c [new file with mode: 0644]
src/drm/cairo-drm-radeon.c [new file with mode: 0644]
src/drm/cairo-drm-surface.c [new file with mode: 0644]
src/drm/cairo-drm.c [new file with mode: 0644]
src/skia/cairo-skia-context.cpp [new file with mode: 0644]
src/skia/cairo-skia-private.h [new file with mode: 0644]
src/skia/cairo-skia-surface.cpp [new file with mode: 0644]
src/test-base-compositor-surface.c [new file with mode: 0644]
src/test-compositor-surface-private.h [new file with mode: 0644]
src/test-compositor-surface.c [new file with mode: 0644]
src/test-compositor-surface.h [new file with mode: 0644]
src/test-null-compositor-surface.c [new file with mode: 0644]
src/test-null-compositor-surface.h [new file with mode: 0644]
src/test-paginated-surface.c [new file with mode: 0644]
src/test-paginated-surface.h [new file with mode: 0644]
test/.gitignore [new file with mode: 0644]
test/.valgrind-suppressions [new file with mode: 0644]
test/6x13.pcf [new file with mode: 0644]
test/COPYING [new file with mode: 0644]
test/Makefile.am [new file with mode: 0644]
test/Makefile.sources [new file with mode: 0644]
test/Makefile.win32 [new file with mode: 0644]
test/README [new file with mode: 0644]
test/a1-bug.c [new file with mode: 0644]
test/a1-clip.c [new file with mode: 0644]
test/a1-fill.c [new file with mode: 0644]
test/a1-image-sample.c [new file with mode: 0644]
test/a1-mask-sample.c [new file with mode: 0644]
test/a1-mask.c [new file with mode: 0644]
test/a1-rasterisation.c [new file with mode: 0644]
test/a1-sample.c [new file with mode: 0644]
test/a1-traps-sample.c [new file with mode: 0644]
test/a8-clear.c [new file with mode: 0644]
test/a8-mask.c [new file with mode: 0644]
test/aliasing.c [new file with mode: 0644]
test/alpha-similar.c [new file with mode: 0644]
test/any2ppm.c [new file with mode: 0644]
test/api-special-cases.c [new file with mode: 0644]
test/arc-infinite-loop.c [new file with mode: 0644]
test/arc-looping-dash.c [new file with mode: 0644]
test/big-empty-box.c [new file with mode: 0644]
test/big-empty-triangle.c [new file with mode: 0644]
test/big-line.c [new file with mode: 0644]
test/big-little-box.c [new file with mode: 0644]
test/big-little-triangle.c [new file with mode: 0644]
test/big-trap.c [new file with mode: 0644]
test/bilevel-image.c [new file with mode: 0644]
test/bitmap-font.c [new file with mode: 0644]
test/buffer-diff.c [new file with mode: 0644]
test/buffer-diff.h [new file with mode: 0644]
test/bug-40410.c [new file with mode: 0644]
test/bug-bo-rectangular.c [new file with mode: 0644]
test/bug-bo-ricotz.c [new file with mode: 0644]
test/bug-extents.c [new file with mode: 0644]
test/bug-seams.c [new file with mode: 0644]
test/cairo-test-private.h [new file with mode: 0644]
test/cairo-test-runner.c [new file with mode: 0644]
test/cairo-test-trace.c [new file with mode: 0644]
test/cairo-test.c [new file with mode: 0644]
test/cairo-test.h [new file with mode: 0644]
test/caps-joins-alpha.c [new file with mode: 0644]
test/caps-joins-curve.c [new file with mode: 0644]
test/caps-joins.c [new file with mode: 0644]
test/caps-sub-paths.c [new file with mode: 0644]
test/caps-tails-curve.c [new file with mode: 0644]
test/caps.c [new file with mode: 0644]
test/checkerboard.c [new file with mode: 0644]
test/clear-source.c [new file with mode: 0644]
test/clear.c [new file with mode: 0644]
test/clip-all.c [new file with mode: 0644]
test/clip-complex-shape.c [new file with mode: 0644]
test/clip-contexts.c [new file with mode: 0644]
test/clip-device-offset.c [new file with mode: 0644]
test/clip-disjoint-hatching.c [new file with mode: 0644]
test/clip-disjoint.c [new file with mode: 0644]
test/clip-double-free.c [new file with mode: 0644]
test/clip-draw-unbounded.c [new file with mode: 0644]
test/clip-empty-group.c [new file with mode: 0644]
test/clip-empty-save.c [new file with mode: 0644]
test/clip-empty.c [new file with mode: 0644]
test/clip-fill-no-op.c [new file with mode: 0644]
test/clip-fill-rule-pixel-aligned.c [new file with mode: 0644]
test/clip-fill-rule.c [new file with mode: 0644]
test/clip-fill.c [new file with mode: 0644]
test/clip-group-shapes.c [new file with mode: 0644]
test/clip-image.c [new file with mode: 0644]
test/clip-intersect.c [new file with mode: 0644]
test/clip-mixed-antialias.c [new file with mode: 0644]
test/clip-nesting.c [new file with mode: 0644]
test/clip-operator.c [new file with mode: 0644]
test/clip-polygons.c [new file with mode: 0644]
test/clip-push-group.c [new file with mode: 0644]
test/clip-rectilinear.c [new file with mode: 0644]
test/clip-shape.c [new file with mode: 0644]
test/clip-stroke-no-op.c [new file with mode: 0644]
test/clip-stroke.c [new file with mode: 0644]
test/clip-text.c [new file with mode: 0644]
test/clip-twice-rectangle.c [new file with mode: 0644]
test/clip-twice.c [new file with mode: 0644]
test/clip-unbounded.c [new file with mode: 0644]
test/clip-zero.c [new file with mode: 0644]
test/clipped-group.c [new file with mode: 0644]
test/clipped-surface.c [new file with mode: 0644]
test/clipped-trapezoids-ref.png [new file with mode: 0644]
test/clipped-trapezoids.c [new file with mode: 0644]
test/close-path-current-point.c [new file with mode: 0644]
test/close-path.c [new file with mode: 0644]
test/composite-integer-translate-over-repeat.c [new file with mode: 0644]
test/composite-integer-translate-over.c [new file with mode: 0644]
test/composite-integer-translate-source.c [new file with mode: 0644]
test/copy-path.c [new file with mode: 0644]
test/coverage.c [new file with mode: 0644]
test/create-for-stream.c [new file with mode: 0644]
test/create-from-png-stream.c [new file with mode: 0644]
test/create-from-png.c [new file with mode: 0644]
test/culled-glyphs.c [new file with mode: 0644]
test/curve-to-as-line-to.c [new file with mode: 0644]
test/dash-caps-joins.c [new file with mode: 0644]
test/dash-curve.c [new file with mode: 0644]
test/dash-infinite-loop.c [new file with mode: 0644]
test/dash-no-dash.c [new file with mode: 0644]
test/dash-offset-negative.c [new file with mode: 0644]
test/dash-offset.c [new file with mode: 0644]
test/dash-scale.c [new file with mode: 0644]
test/dash-state.c [new file with mode: 0644]
test/dash-zero-length.c [new file with mode: 0644]
test/degenerate-arc.c [new file with mode: 0644]
test/degenerate-arcs.c [new file with mode: 0644]
test/degenerate-curve-to.c [new file with mode: 0644]
test/degenerate-dash.c [new file with mode: 0644]
test/degenerate-linear-gradient.c [new file with mode: 0644]
test/degenerate-path.c [new file with mode: 0644]
test/degenerate-pen.c [new file with mode: 0644]
test/degenerate-radial-gradient.c [new file with mode: 0644]
test/degenerate-rel-curve-to.c [new file with mode: 0644]
test/device-offset-fractional.c [new file with mode: 0644]
test/device-offset-positive.c [new file with mode: 0644]
test/device-offset-scale.c [new file with mode: 0644]
test/device-offset.c [new file with mode: 0644]
test/drunkard-tails.c [new file with mode: 0644]
test/error-setters.c [new file with mode: 0644]
test/extend-pad-border.c [new file with mode: 0644]
test/extend-pad-similar.c [new file with mode: 0644]
test/extend-pad.c [new file with mode: 0644]
test/extend-reflect-similar.c [new file with mode: 0644]
test/extend-reflect.c [new file with mode: 0644]
test/extend-repeat-similar.c [new file with mode: 0644]
test/extend-repeat.c [new file with mode: 0644]
test/extended-blend.c [new file with mode: 0644]
test/fallback-resolution.c [new file with mode: 0644]
test/fill-alpha-pattern.c [new file with mode: 0644]
test/fill-alpha.c [new file with mode: 0644]
test/fill-and-stroke-alpha-add.c [new file with mode: 0644]
test/fill-and-stroke-alpha.c [new file with mode: 0644]
test/fill-and-stroke.c [new file with mode: 0644]
test/fill-degenerate-sort-order.c [new file with mode: 0644]
test/fill-empty.c [new file with mode: 0644]
test/fill-image.c [new file with mode: 0644]
test/fill-missed-stop.c [new file with mode: 0644]
test/fill-rule.c [new file with mode: 0644]
test/filter-bilinear-extents.c [new file with mode: 0644]
test/filter-nearest-offset.c [new file with mode: 0644]
test/filter-nearest-transformed.c [new file with mode: 0644]
test/finer-grained-fallbacks.c [new file with mode: 0644]
test/font-face-get-type.c [new file with mode: 0644]
test/font-matrix-translation.c [new file with mode: 0644]
test/font-options.c [new file with mode: 0644]
test/ft-font-create-for-ft-face.c [new file with mode: 0644]
test/ft-show-glyphs-positioning.c [new file with mode: 0644]
test/ft-show-glyphs-table.c [new file with mode: 0644]
test/ft-text-antialias-none.c [new file with mode: 0644]
test/ft-text-vertical-layout-type1.c [new file with mode: 0644]
test/ft-text-vertical-layout-type3.c [new file with mode: 0644]
test/generate_refs.sh [new file with mode: 0755]
test/get-and-set.c [new file with mode: 0644]
test/get-clip.c [new file with mode: 0644]
test/get-group-target.c [new file with mode: 0644]
test/get-path-extents.c [new file with mode: 0644]
test/get-xrender-format.c [new file with mode: 0644]
test/gl-surface-source.c [new file with mode: 0644]
test/glyph-cache-pressure.c [new file with mode: 0644]
test/gradient-alpha.c [new file with mode: 0644]
test/gradient-constant-alpha.c [new file with mode: 0644]
test/gradient-zero-stops-mask.c [new file with mode: 0644]
test/gradient-zero-stops.c [new file with mode: 0644]
test/group-clip.c [new file with mode: 0644]
test/group-paint.c [new file with mode: 0644]
test/group-state.c [new file with mode: 0644]
test/group-unaligned.c [new file with mode: 0644]
test/half-coverage.c [new file with mode: 0644]
test/halo.c [new file with mode: 0644]
test/hatchings.c [new file with mode: 0644]
test/horizontal-clip.c [new file with mode: 0644]
test/huge-linear.c [new file with mode: 0644]
test/huge-radial.c [new file with mode: 0644]
test/image-bug-710072.c [new file with mode: 0644]
test/image-surface-source.c [new file with mode: 0644]
test/imagediff.c [new file with mode: 0644]
test/implicit-close.c [new file with mode: 0644]
test/in-fill-empty-trapezoid.c [new file with mode: 0644]
test/in-fill-trapezoid.c [new file with mode: 0644]
test/index.html [new file with mode: 0644]
test/infinite-join.c [new file with mode: 0644]
test/invalid-matrix.c [new file with mode: 0644]
test/inverse-text.c [new file with mode: 0644]
test/joins-loop.c [new file with mode: 0644]
test/joins-retrace.c [new file with mode: 0644]
test/joins-star.c [new file with mode: 0644]
test/joins.c [new file with mode: 0644]
test/jp2.jp2 [new file with mode: 0644]
test/jpeg.jpg [new file with mode: 0644]
test/large-clip.c [new file with mode: 0644]
test/large-font.c [new file with mode: 0644]
test/large-source-roi.c [new file with mode: 0644]
test/large-source.c [new file with mode: 0644]
test/large-twin-antialias-mixed.c [new file with mode: 0644]
test/leaky-dash.c [new file with mode: 0644]
test/leaky-dashed-rectangle.c [new file with mode: 0644]
test/leaky-dashed-stroke.c [new file with mode: 0644]
test/leaky-polygon.c [new file with mode: 0644]
test/line-width-large-overlap.c [new file with mode: 0644]
test/line-width-overlap.c [new file with mode: 0644]
test/line-width-scale.c [new file with mode: 0644]
test/line-width-tolerance.c [new file with mode: 0644]
test/line-width-zero.c [new file with mode: 0644]
test/line-width.c [new file with mode: 0644]
test/linear-gradient-extend.c [new file with mode: 0644]
test/linear-gradient-large.c [new file with mode: 0644]
test/linear-gradient-one-stop.c [new file with mode: 0644]
test/linear-gradient-reflect.c [new file with mode: 0644]
test/linear-gradient-subset.c [new file with mode: 0644]
test/linear-gradient.c [new file with mode: 0644]
test/linear-step-function.c [new file with mode: 0644]
test/linear-uniform.c [new file with mode: 0644]
test/long-dashed-lines.c [new file with mode: 0644]
test/long-lines.c [new file with mode: 0644]
test/make-cairo-test-constructors.sh [new file with mode: 0644]
test/map-to-image.c [new file with mode: 0644]
test/mask-alpha.c [new file with mode: 0644]
test/mask-ctm.c [new file with mode: 0644]
test/mask-glyphs.c [new file with mode: 0644]
test/mask-surface-ctm.c [new file with mode: 0644]
test/mask-transformed-image.c [new file with mode: 0644]
test/mask-transformed-similar.c [new file with mode: 0644]
test/mask.c [new file with mode: 0644]
test/mesh-pattern-accuracy.c [new file with mode: 0644]
test/mesh-pattern-conical.c [new file with mode: 0644]
test/mesh-pattern-control-points.c [new file with mode: 0644]
test/mesh-pattern-fold.c [new file with mode: 0644]
test/mesh-pattern-overlap.c [new file with mode: 0644]
test/mesh-pattern-transformed.c [new file with mode: 0644]
test/mesh-pattern.c [new file with mode: 0644]
test/mime-data.c [new file with mode: 0644]
test/mime-surface-api.c [new file with mode: 0644]
test/mime-surface.c [new file with mode: 0644]
test/miter-precision.c [new file with mode: 0644]
test/move-to-show-surface.c [new file with mode: 0644]
test/multi-page.c [new file with mode: 0644]
test/new-sub-path.c [new file with mode: 0644]
test/nil-surface.c [new file with mode: 0644]
test/operator-alpha-alpha.c [new file with mode: 0644]
test/operator-alpha.c [new file with mode: 0644]
test/operator-clear.c [new file with mode: 0644]
test/operator-source.c [new file with mode: 0644]
test/operator.c [new file with mode: 0644]
test/over-above-source.c [new file with mode: 0644]
test/over-around-source.c [new file with mode: 0644]
test/over-below-source.c [new file with mode: 0644]
test/over-between-source.c [new file with mode: 0644]
test/overlapping-boxes.c [new file with mode: 0644]
test/overlapping-dash-caps.c [new file with mode: 0644]
test/overlapping-glyphs.c [new file with mode: 0644]
test/paint-clip-fill.c [new file with mode: 0644]
test/paint-repeat.c [new file with mode: 0644]
test/paint-source-alpha.c [new file with mode: 0644]
test/paint-with-alpha.c [new file with mode: 0644]
test/paint.c [new file with mode: 0644]
test/partial-clip-text.c [new file with mode: 0644]
test/partial-coverage.c [new file with mode: 0644]
test/pass-through.c [new file with mode: 0644]
test/path-append.c [new file with mode: 0644]
test/path-precision.c [new file with mode: 0644]
test/path-stroke-twice.c [new file with mode: 0644]
test/pattern-get-type.c [new file with mode: 0644]
test/pattern-getters.c [new file with mode: 0644]
test/pdf-features.c [new file with mode: 0644]
test/pdf-mime-data.c [new file with mode: 0644]
test/pdf-surface-source.c [new file with mode: 0644]
test/pdf2png.c [new file with mode: 0644]
test/pdiff/.gitignore [new file with mode: 0644]
test/pdiff/CMakeLists.txt [new file with mode: 0644]
test/pdiff/Makefile.am [new file with mode: 0644]
test/pdiff/Makefile.win32 [new file with mode: 0644]
test/pdiff/README.txt [new file with mode: 0644]
test/pdiff/args.c [new file with mode: 0644]
test/pdiff/args.h [new file with mode: 0644]
test/pdiff/gpl.txt [new file with mode: 0644]
test/pdiff/lpyramid.c [new file with mode: 0644]
test/pdiff/lpyramid.h [new file with mode: 0644]
test/pdiff/pdiff.c [new file with mode: 0644]
test/pdiff/pdiff.h [new file with mode: 0644]
test/pdiff/perceptualdiff.c [new file with mode: 0644]
test/pixman-rotate.c [new file with mode: 0644]
test/png-flatten.c [new file with mode: 0644]
test/png.c [new file with mode: 0644]
test/png.png [new file with mode: 0644]
test/ps-eps.c [new file with mode: 0644]
test/ps-features.c [new file with mode: 0644]
test/ps-surface-source.c [new file with mode: 0644]
test/ps2png.c [new file with mode: 0644]
test/pthread-same-source.c [new file with mode: 0644]
test/pthread-show-text.c [new file with mode: 0644]
test/pthread-similar.c [new file with mode: 0644]
test/push-group-color.c [new file with mode: 0644]
test/push-group-path-offset.c [new file with mode: 0644]
test/push-group.c [new file with mode: 0644]
test/quartz-surface-source.c [new file with mode: 0644]
test/radial-gradient-extend.c [new file with mode: 0644]
test/radial-gradient.c [new file with mode: 0644]
test/radial-outer-focus.c [new file with mode: 0644]
test/random-clips.c [new file with mode: 0644]
test/random-intersections-curves-eo.c [new file with mode: 0644]
test/random-intersections-curves-nz.c [new file with mode: 0644]
test/random-intersections-eo.c [new file with mode: 0644]
test/random-intersections-nonzero.c [new file with mode: 0644]
test/record-extend.c [new file with mode: 0644]
test/record-mesh.c [new file with mode: 0644]
test/record.c [new file with mode: 0644]
test/recording-surface-extend.c [new file with mode: 0644]
test/recording-surface-pattern.c [new file with mode: 0644]
test/rectangle-rounding-error.c [new file with mode: 0644]
test/rectilinear-dash.c [new file with mode: 0644]
test/rectilinear-fill.c [new file with mode: 0644]
test/rectilinear-grid.c [new file with mode: 0644]
test/rectilinear-miter-limit.c [new file with mode: 0644]
test/rectilinear-stroke.c [new file with mode: 0644]
test/reference/a1-bug.base.argb32.ref.png [new file with mode: 0644]
test/reference/a1-bug.base.rgb24.ref.png [new file with mode: 0644]
test/reference/a1-bug.image16.ref.png [new file with mode: 0644]
test/reference/a1-bug.quartz.xfail.png [new file with mode: 0644]
test/reference/a1-bug.ref.png [new file with mode: 0644]
test/reference/a1-bug.traps.argb32.ref.png [new file with mode: 0644]
test/reference/a1-bug.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/a1-clip-fill-equal.base.argb32.ref.png [new file with mode: 0644]
test/reference/a1-clip-fill-equal.base.rgb24.ref.png [new file with mode: 0644]
test/reference/a1-clip-fill-equal.ref.png [new file with mode: 0644]
test/reference/a1-clip-fill-equal.traps.argb32.ref.png [new file with mode: 0644]
test/reference/a1-clip-fill-equal.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/a1-clip-fill-rule.argb32.ref.png [new file with mode: 0644]
test/reference/a1-clip-fill-rule.base.argb32.ref.png [new file with mode: 0644]
test/reference/a1-clip-fill-rule.base.rgb24.ref.png [new file with mode: 0644]
test/reference/a1-clip-fill-rule.rgb24.ref.png [new file with mode: 0644]
test/reference/a1-clip-fill-rule.traps.argb32.ref.png [new file with mode: 0644]
test/reference/a1-clip-fill-rule.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/a1-clip-fill.base.argb32.ref.png [new file with mode: 0644]
test/reference/a1-clip-fill.base.rgb24.ref.png [new file with mode: 0644]
test/reference/a1-clip-fill.ref.png [new file with mode: 0644]
test/reference/a1-clip-fill.traps.argb32.ref.png [new file with mode: 0644]
test/reference/a1-clip-fill.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/a1-clip-paint.base.argb32.ref.png [new file with mode: 0644]
test/reference/a1-clip-paint.base.rgb24.ref.png [new file with mode: 0644]
test/reference/a1-clip-paint.ref.png [new file with mode: 0644]
test/reference/a1-clip-paint.traps.argb32.ref.png [new file with mode: 0644]
test/reference/a1-clip-paint.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/a1-clip-stroke.base.argb32.ref.png [new file with mode: 0644]
test/reference/a1-clip-stroke.base.rgb24.ref.png [new file with mode: 0644]
test/reference/a1-clip-stroke.ref.png [new file with mode: 0644]
test/reference/a1-clip-stroke.traps.argb32.ref.png [new file with mode: 0644]
test/reference/a1-clip-stroke.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/a1-fill.base.ref.png [new file with mode: 0644]
test/reference/a1-fill.ref.png [new file with mode: 0644]
test/reference/a1-fill.traps.ref.png [new file with mode: 0644]
test/reference/a1-image-sample.base.argb32.ref.png [new file with mode: 0644]
test/reference/a1-image-sample.base.rgb24.ref.png [new file with mode: 0644]
test/reference/a1-image-sample.gl.xfail.png [new file with mode: 0644]
test/reference/a1-image-sample.ref.png [new file with mode: 0644]
test/reference/a1-image-sample.traps.argb32.ref.png [new file with mode: 0644]
test/reference/a1-image-sample.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/a1-line-width.base.argb32.ref.png [new file with mode: 0644]
test/reference/a1-line-width.base.rgb24.ref.png [new file with mode: 0644]
test/reference/a1-line-width.pdf.ref.png [new file with mode: 0644]
test/reference/a1-line-width.ps.ref.png [new file with mode: 0644]
test/reference/a1-line-width.ref.png [new file with mode: 0644]
test/reference/a1-line-width.traps.argb32.ref.png [new file with mode: 0644]
test/reference/a1-line-width.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/a1-mask-sample.base.argb32.ref.png [new file with mode: 0644]
test/reference/a1-mask-sample.base.rgb24.ref.png [new file with mode: 0644]
test/reference/a1-mask-sample.ref.png [new file with mode: 0644]
test/reference/a1-mask-sample.traps.argb32.ref.png [new file with mode: 0644]
test/reference/a1-mask-sample.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/a1-mask.base.argb32.ref.png [new file with mode: 0644]
test/reference/a1-mask.base.rgb24.ref.png [new file with mode: 0644]
test/reference/a1-mask.pdf.ref.png [new file with mode: 0644]
test/reference/a1-mask.ps.ref.png [new file with mode: 0644]
test/reference/a1-mask.ref.png [new file with mode: 0644]
test/reference/a1-mask.traps.argb32.ref.png [new file with mode: 0644]
test/reference/a1-mask.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/a1-rasterisation-rectangles.base.argb32.ref.png [new file with mode: 0644]
test/reference/a1-rasterisation-rectangles.base.rgb24.ref.png [new file with mode: 0644]
test/reference/a1-rasterisation-rectangles.quartz.xfail.png [new file with mode: 0644]
test/reference/a1-rasterisation-rectangles.ref.png [new file with mode: 0644]
test/reference/a1-rasterisation-rectangles.traps.argb32.ref.png [new file with mode: 0644]
test/reference/a1-rasterisation-rectangles.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/a1-rasterisation-triangles.base.argb32.ref.png [new file with mode: 0644]
test/reference/a1-rasterisation-triangles.base.rgb24.ref.png [new file with mode: 0644]
test/reference/a1-rasterisation-triangles.quartz.xfail.png [new file with mode: 0644]
test/reference/a1-rasterisation-triangles.ref.png [new file with mode: 0644]
test/reference/a1-rasterisation-triangles.traps.argb32.ref.png [new file with mode: 0644]
test/reference/a1-rasterisation-triangles.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/a1-rectilinear-grid.base.argb32.ref.png [new file with mode: 0644]
test/reference/a1-rectilinear-grid.base.rgb24.ref.png [new file with mode: 0644]
test/reference/a1-rectilinear-grid.ref.png [new file with mode: 0644]
test/reference/a1-rectilinear-grid.traps.argb32.ref.png [new file with mode: 0644]
test/reference/a1-rectilinear-grid.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/a1-sample.base.argb32.ref.png [new file with mode: 0644]
test/reference/a1-sample.base.rgb24.ref.png [new file with mode: 0644]
test/reference/a1-sample.ref.png [new file with mode: 0644]
test/reference/a1-sample.traps.argb32.ref.png [new file with mode: 0644]
test/reference/a1-sample.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/a1-tiger.base.argb32.ref.png [new file with mode: 0644]
test/reference/a1-tiger.base.rgb24.ref.png [new file with mode: 0644]
test/reference/a1-tiger.ref.png [new file with mode: 0644]
test/reference/a1-tiger.traps.argb32.ref.png [new file with mode: 0644]
test/reference/a1-tiger.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/a1-traps-sample.base.argb32.ref.png [new file with mode: 0644]
test/reference/a1-traps-sample.base.rgb24.ref.png [new file with mode: 0644]
test/reference/a1-traps-sample.quartz.xfail.png [new file with mode: 0644]
test/reference/a1-traps-sample.ref.png [new file with mode: 0644]
test/reference/a1-traps-sample.traps.argb32.ref.png [new file with mode: 0644]
test/reference/a1-traps-sample.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/a8-clear.base.argb32.ref.png [new file with mode: 0644]
test/reference/a8-clear.base.rgb24.ref.png [new file with mode: 0644]
test/reference/a8-clear.quartz.ref.png [new file with mode: 0644]
test/reference/a8-clear.ref.png [new file with mode: 0644]
test/reference/a8-clear.traps.argb32.ref.png [new file with mode: 0644]
test/reference/a8-clear.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/a8-mask.base.argb32.ref.png [new file with mode: 0644]
test/reference/a8-mask.base.rgb24.ref.png [new file with mode: 0644]
test/reference/a8-mask.ref.png [new file with mode: 0644]
test/reference/a8-mask.traps.argb32.ref.png [new file with mode: 0644]
test/reference/a8-mask.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/aliasing.base.argb32.ref.png [new file with mode: 0644]
test/reference/aliasing.base.rgb24.ref.png [new file with mode: 0644]
test/reference/aliasing.image16.ref.png [new file with mode: 0644]
test/reference/aliasing.quartz.ref.png [new file with mode: 0644]
test/reference/aliasing.ref.png [new file with mode: 0644]
test/reference/aliasing.traps.argb32.ref.png [new file with mode: 0644]
test/reference/aliasing.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/alpha-similar.base.argb32.ref.png [new file with mode: 0644]
test/reference/alpha-similar.base.rgb24.ref.png [new file with mode: 0644]
test/reference/alpha-similar.gl.argb32.xfail.png [new file with mode: 0644]
test/reference/alpha-similar.gl.rgb24.xfail.png [new file with mode: 0644]
test/reference/alpha-similar.pdf.argb32.xfail.png [new file with mode: 0644]
test/reference/alpha-similar.pdf.rgb24.xfail.png [new file with mode: 0644]
test/reference/alpha-similar.ps.argb32.xfail.png [new file with mode: 0644]
test/reference/alpha-similar.ps.rgb24.xfail.png [new file with mode: 0644]
test/reference/alpha-similar.ref.png [new file with mode: 0644]
test/reference/alpha-similar.rgb24.ref.png [new file with mode: 0644]
test/reference/alpha-similar.svg.argb32.xfail.png [new file with mode: 0644]
test/reference/alpha-similar.svg.rgb24.xfail.png [new file with mode: 0644]
test/reference/alpha-similar.traps.argb32.ref.png [new file with mode: 0644]
test/reference/alpha-similar.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/api-special-cases.base.argb32.ref.png [new file with mode: 0644]
test/reference/api-special-cases.base.rgb24.ref.png [new file with mode: 0644]
test/reference/api-special-cases.ref.png [new file with mode: 0644]
test/reference/api-special-cases.traps.argb32.ref.png [new file with mode: 0644]
test/reference/api-special-cases.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/arc-infinite-loop.base.argb32.ref.png [new file with mode: 0644]
test/reference/arc-infinite-loop.base.rgb24.ref.png [new file with mode: 0644]
test/reference/arc-infinite-loop.ref.png [new file with mode: 0644]
test/reference/arc-infinite-loop.traps.argb32.ref.png [new file with mode: 0644]
test/reference/arc-infinite-loop.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/arc-looping-dash.base.argb32.ref.png [new file with mode: 0644]
test/reference/arc-looping-dash.base.rgb24.ref.png [new file with mode: 0644]
test/reference/arc-looping-dash.image16.ref.png [new file with mode: 0644]
test/reference/arc-looping-dash.ps.ref.png [new file with mode: 0644]
test/reference/arc-looping-dash.quartz.ref.png [new file with mode: 0644]
test/reference/arc-looping-dash.ref.png [new file with mode: 0644]
test/reference/arc-looping-dash.traps.argb32.ref.png [new file with mode: 0644]
test/reference/arc-looping-dash.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/big-empty-box.argb32.ref.png [new file with mode: 0644]
test/reference/big-empty-box.base.argb32.ref.png [new file with mode: 0644]
test/reference/big-empty-box.base.rgb24.ref.png [new file with mode: 0644]
test/reference/big-empty-box.rgb24.ref.png [new file with mode: 0644]
test/reference/big-empty-box.traps.argb32.ref.png [new file with mode: 0644]
test/reference/big-empty-box.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/big-empty-triangle.argb32.ref.png [new file with mode: 0644]
test/reference/big-empty-triangle.base.argb32.ref.png [new file with mode: 0644]
test/reference/big-empty-triangle.base.rgb24.ref.png [new file with mode: 0644]
test/reference/big-empty-triangle.rgb24.ref.png [new file with mode: 0644]
test/reference/big-empty-triangle.traps.argb32.ref.png [new file with mode: 0644]
test/reference/big-empty-triangle.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/big-line.base.argb32.ref.png [new file with mode: 0644]
test/reference/big-line.base.rgb24.ref.png [new file with mode: 0644]
test/reference/big-line.image16.ref.png [new file with mode: 0644]
test/reference/big-line.ps.ref.png [new file with mode: 0644]
test/reference/big-line.quartz.ref.png [new file with mode: 0644]
test/reference/big-line.ref.png [new file with mode: 0644]
test/reference/big-line.traps.argb32.ref.png [new file with mode: 0644]
test/reference/big-line.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/big-little-box.argb32.ref.png [new file with mode: 0644]
test/reference/big-little-box.base.argb32.ref.png [new file with mode: 0644]
test/reference/big-little-box.base.rgb24.ref.png [new file with mode: 0644]
test/reference/big-little-box.rgb24.ref.png [new file with mode: 0644]
test/reference/big-little-box.traps.argb32.ref.png [new file with mode: 0644]
test/reference/big-little-box.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/big-little-triangle.argb32.ref.png [new file with mode: 0644]
test/reference/big-little-triangle.base.argb32.ref.png [new file with mode: 0644]
test/reference/big-little-triangle.base.rgb24.ref.png [new file with mode: 0644]
test/reference/big-little-triangle.rgb24.ref.png [new file with mode: 0644]
test/reference/big-little-triangle.traps.argb32.ref.png [new file with mode: 0644]
test/reference/big-little-triangle.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/big-trap.base.argb32.ref.png [new file with mode: 0644]
test/reference/big-trap.base.rgb24.ref.png [new file with mode: 0644]
test/reference/big-trap.traps.argb32.ref.png [new file with mode: 0644]
test/reference/big-trap.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/bilevel-image.base.argb32.ref.png [new file with mode: 0644]
test/reference/bilevel-image.base.rgb24.ref.png [new file with mode: 0644]
test/reference/bilevel-image.ref.png [new file with mode: 0644]
test/reference/bilevel-image.traps.argb32.ref.png [new file with mode: 0644]
test/reference/bilevel-image.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/bitmap-font.base.argb32.ref.png [new file with mode: 0644]
test/reference/bitmap-font.base.rgb24.ref.png [new file with mode: 0644]
test/reference/bitmap-font.ref.png [new file with mode: 0644]
test/reference/bitmap-font.rgb24.ref.png [new file with mode: 0644]
test/reference/bitmap-font.traps.argb32.ref.png [new file with mode: 0644]
test/reference/bitmap-font.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/bug-40410.base.argb32.ref.png [new file with mode: 0644]
test/reference/bug-40410.base.rgb24.ref.png [new file with mode: 0644]
test/reference/bug-40410.ref.png [new file with mode: 0644]
test/reference/bug-40410.traps.argb32.ref.png [new file with mode: 0644]
test/reference/bug-40410.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/bug-bo-rectangular.base.argb32.ref.png [new file with mode: 0644]
test/reference/bug-bo-rectangular.base.rgb24.ref.png [new file with mode: 0644]
test/reference/bug-bo-rectangular.image16.ref.png [new file with mode: 0644]
test/reference/bug-bo-rectangular.ps.xfail.png [new file with mode: 0644]
test/reference/bug-bo-rectangular.ref.png [new file with mode: 0644]
test/reference/bug-bo-rectangular.traps.argb32.ref.png [new file with mode: 0644]
test/reference/bug-bo-rectangular.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/bug-bo-ricotz.base.ref.png [new file with mode: 0644]
test/reference/bug-bo-ricotz.ref.png [new file with mode: 0644]
test/reference/bug-bo-ricotz.traps.ref.png [new file with mode: 0644]
test/reference/bug-extents.base.argb32.ref.png [new file with mode: 0644]
test/reference/bug-extents.base.rgb24.ref.png [new file with mode: 0644]
test/reference/bug-extents.image16.ref.png [new file with mode: 0644]
test/reference/bug-extents.ps.ref.png [new file with mode: 0644]
test/reference/bug-extents.quartz.ref.png [new file with mode: 0644]
test/reference/bug-extents.ref.png [new file with mode: 0644]
test/reference/bug-extents.traps.argb32.ref.png [new file with mode: 0644]
test/reference/bug-extents.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/bug-seams.base.argb32.ref.png [new file with mode: 0644]
test/reference/bug-seams.base.rgb24.ref.png [new file with mode: 0644]
test/reference/bug-seams.ref.png [new file with mode: 0644]
test/reference/bug-seams.traps.argb32.ref.png [new file with mode: 0644]
test/reference/bug-seams.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/bug-seams.xlib-fallback.ref.png [new file with mode: 0644]
test/reference/caps-joins-alpha.base.argb32.ref.png [new file with mode: 0644]
test/reference/caps-joins-alpha.base.rgb24.ref.png [new file with mode: 0644]
test/reference/caps-joins-alpha.image16.ref.png [new file with mode: 0644]
test/reference/caps-joins-alpha.quartz.ref.png [new file with mode: 0644]
test/reference/caps-joins-alpha.ref.png [new file with mode: 0644]
test/reference/caps-joins-alpha.traps.argb32.ref.png [new file with mode: 0644]
test/reference/caps-joins-alpha.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/caps-joins-curve.base.argb32.ref.png [new file with mode: 0644]
test/reference/caps-joins-curve.base.rgb24.ref.png [new file with mode: 0644]
test/reference/caps-joins-curve.image16.ref.png [new file with mode: 0644]
test/reference/caps-joins-curve.ps.ref.png [new file with mode: 0644]
test/reference/caps-joins-curve.quartz.ref.png [new file with mode: 0644]
test/reference/caps-joins-curve.ref.png [new file with mode: 0644]
test/reference/caps-joins-curve.traps.argb32.ref.png [new file with mode: 0644]
test/reference/caps-joins-curve.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/caps-joins.base.argb32.ref.png [new file with mode: 0644]
test/reference/caps-joins.base.rgb24.ref.png [new file with mode: 0644]
test/reference/caps-joins.image16.ref.png [new file with mode: 0644]
test/reference/caps-joins.ps.ref.png [new file with mode: 0644]
test/reference/caps-joins.ref.png [new file with mode: 0644]
test/reference/caps-joins.traps.argb32.ref.png [new file with mode: 0644]
test/reference/caps-joins.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/caps-sub-paths.base.argb32.ref.png [new file with mode: 0644]
test/reference/caps-sub-paths.base.rgb24.ref.png [new file with mode: 0644]
test/reference/caps-sub-paths.image16.ref.png [new file with mode: 0644]
test/reference/caps-sub-paths.ps.ref.png [new file with mode: 0644]
test/reference/caps-sub-paths.ref.png [new file with mode: 0644]
test/reference/caps-sub-paths.traps.argb32.ref.png [new file with mode: 0644]
test/reference/caps-sub-paths.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/caps-tails-curve.base.argb32.ref.png [new file with mode: 0644]
test/reference/caps-tails-curve.base.rgb24.ref.png [new file with mode: 0644]
test/reference/caps-tails-curve.ps.ref.png [new file with mode: 0644]
test/reference/caps-tails-curve.ref.png [new file with mode: 0644]
test/reference/caps-tails-curve.traps.argb32.ref.png [new file with mode: 0644]
test/reference/caps-tails-curve.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/caps.base.argb32.ref.png [new file with mode: 0644]
test/reference/caps.base.rgb24.ref.png [new file with mode: 0644]
test/reference/caps.image16.ref.png [new file with mode: 0644]
test/reference/caps.ps.ref.png [new file with mode: 0644]
test/reference/caps.ref.png [new file with mode: 0644]
test/reference/caps.traps.argb32.ref.png [new file with mode: 0644]
test/reference/caps.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/checkerboard.base.argb32.ref.png [new file with mode: 0644]
test/reference/checkerboard.base.rgb24.ref.png [new file with mode: 0644]
test/reference/checkerboard.ref.png [new file with mode: 0644]
test/reference/checkerboard.traps.argb32.ref.png [new file with mode: 0644]
test/reference/checkerboard.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/clear-source.base.argb32.ref.png [new file with mode: 0644]
test/reference/clear-source.base.rgb24.ref.png [new file with mode: 0644]
test/reference/clear-source.image16.ref.png [new file with mode: 0644]
test/reference/clear-source.pdf.xfail.png [new file with mode: 0644]
test/reference/clear-source.ps.xfail.png [new file with mode: 0644]
test/reference/clear-source.ref.png [new file with mode: 0644]
test/reference/clear-source.traps.argb32.ref.png [new file with mode: 0644]
test/reference/clear-source.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/clear.argb32.ref.png [new file with mode: 0644]
test/reference/clear.base.argb32.ref.png [new file with mode: 0644]
test/reference/clear.base.rgb24.ref.png [new file with mode: 0644]
test/reference/clear.pdf.argb32.ref.png [new file with mode: 0644]
test/reference/clear.ps.argb32.ref.png [new file with mode: 0644]
test/reference/clear.quartz.argb32.ref.png [new file with mode: 0644]
test/reference/clear.quartz.rgb24.ref.png [new file with mode: 0644]
test/reference/clear.rgb24.ref.png [new file with mode: 0644]
test/reference/clear.svg12.argb32.xfail.png [new file with mode: 0644]
test/reference/clear.svg12.rgb24.xfail.png [new file with mode: 0644]
test/reference/clear.traps.argb32.ref.png [new file with mode: 0644]
test/reference/clear.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/clip-all.base.argb32.ref.png [new file with mode: 0644]
test/reference/clip-all.base.rgb24.ref.png [new file with mode: 0644]
test/reference/clip-all.ref.png [new file with mode: 0644]
test/reference/clip-all.traps.argb32.ref.png [new file with mode: 0644]
test/reference/clip-all.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/clip-complex-shape-eo-aa.base.argb32.ref.png [new file with mode: 0644]
test/reference/clip-complex-shape-eo-aa.base.rgb24.ref.png [new file with mode: 0644]
test/reference/clip-complex-shape-eo-aa.ref.png [new file with mode: 0644]
test/reference/clip-complex-shape-eo-aa.traps.argb32.ref.png [new file with mode: 0644]
test/reference/clip-complex-shape-eo-aa.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/clip-complex-shape-eo-mono.base.argb32.ref.png [new file with mode: 0644]
test/reference/clip-complex-shape-eo-mono.base.rgb24.ref.png [new file with mode: 0644]
test/reference/clip-complex-shape-eo-mono.ref.png [new file with mode: 0644]
test/reference/clip-complex-shape-eo-mono.traps.argb32.ref.png [new file with mode: 0644]
test/reference/clip-complex-shape-eo-mono.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/clip-contexts.base.argb32.ref.png [new file with mode: 0644]
test/reference/clip-contexts.base.rgb24.ref.png [new file with mode: 0644]
test/reference/clip-contexts.ref.png [new file with mode: 0644]
test/reference/clip-contexts.traps.argb32.ref.png [new file with mode: 0644]
test/reference/clip-contexts.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/clip-device-offset.argb32.ref.png [new file with mode: 0644]
test/reference/clip-device-offset.base.argb32.ref.png [new file with mode: 0644]
test/reference/clip-device-offset.base.rgb24.ref.png [new file with mode: 0644]
test/reference/clip-device-offset.rgb24.ref.png [new file with mode: 0644]
test/reference/clip-device-offset.traps.argb32.ref.png [new file with mode: 0644]
test/reference/clip-device-offset.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/clip-disjoint-hatching.base.argb32.ref.png [new file with mode: 0644]
test/reference/clip-disjoint-hatching.base.rgb24.ref.png [new file with mode: 0644]
test/reference/clip-disjoint-hatching.ref.png [new file with mode: 0644]
test/reference/clip-disjoint-hatching.traps.argb32.ref.png [new file with mode: 0644]
test/reference/clip-disjoint-hatching.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/clip-disjoint.base.argb32.ref.png [new file with mode: 0644]
test/reference/clip-disjoint.base.rgb24.ref.png [new file with mode: 0644]
test/reference/clip-disjoint.image16.ref.png [new file with mode: 0644]
test/reference/clip-disjoint.ps.ref.png [new file with mode: 0644]
test/reference/clip-disjoint.quartz.ref.png [new file with mode: 0644]
test/reference/clip-disjoint.ref.png [new file with mode: 0644]
test/reference/clip-disjoint.traps.argb32.ref.png [new file with mode: 0644]
test/reference/clip-disjoint.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/clip-empty-group.base.argb32.ref.png [new file with mode: 0644]
test/reference/clip-empty-group.base.rgb24.ref.png [new file with mode: 0644]
test/reference/clip-empty-group.ref.png [new file with mode: 0644]
test/reference/clip-empty-group.traps.argb32.ref.png [new file with mode: 0644]
test/reference/clip-empty-group.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/clip-empty-save.base.argb32.ref.png [new file with mode: 0644]
test/reference/clip-empty-save.base.rgb24.ref.png [new file with mode: 0644]
test/reference/clip-empty-save.ref.png [new file with mode: 0644]
test/reference/clip-empty-save.traps.argb32.ref.png [new file with mode: 0644]
test/reference/clip-empty-save.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/clip-empty.base.argb32.ref.png [new file with mode: 0644]
test/reference/clip-empty.base.rgb24.ref.png [new file with mode: 0644]
test/reference/clip-empty.ref.png [new file with mode: 0644]
test/reference/clip-empty.traps.argb32.ref.png [new file with mode: 0644]
test/reference/clip-empty.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/clip-fill-eo-unbounded.argb32.ref.png [new file with mode: 0644]
test/reference/clip-fill-eo-unbounded.base.argb32.ref.png [new file with mode: 0644]
test/reference/clip-fill-eo-unbounded.base.rgb24.ref.png [new file with mode: 0644]
test/reference/clip-fill-eo-unbounded.image16.ref.png [new file with mode: 0644]
test/reference/clip-fill-eo-unbounded.quartz.argb32.ref.png [new file with mode: 0644]
test/reference/clip-fill-eo-unbounded.quartz.rgb24.ref.png [new file with mode: 0644]
test/reference/clip-fill-eo-unbounded.rgb24.ref.png [new file with mode: 0644]
test/reference/clip-fill-eo-unbounded.svg12.rgb24.xfail.png [new file with mode: 0644]
test/reference/clip-fill-eo-unbounded.traps.argb32.ref.png [new file with mode: 0644]
test/reference/clip-fill-eo-unbounded.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/clip-fill-eo-unbounded.xlib-fallback.rgb24.ref.png [new file with mode: 0644]
test/reference/clip-fill-no-op.base.argb32.ref.png [new file with mode: 0644]
test/reference/clip-fill-no-op.base.rgb24.ref.png [new file with mode: 0644]
test/reference/clip-fill-no-op.image16.ref.png [new file with mode: 0644]
test/reference/clip-fill-no-op.ref.png [new file with mode: 0644]
test/reference/clip-fill-no-op.traps.argb32.ref.png [new file with mode: 0644]
test/reference/clip-fill-no-op.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/clip-fill-nz-unbounded.argb32.ref.png [new file with mode: 0644]
test/reference/clip-fill-nz-unbounded.base.argb32.ref.png [new file with mode: 0644]
test/reference/clip-fill-nz-unbounded.base.rgb24.ref.png [new file with mode: 0644]
test/reference/clip-fill-nz-unbounded.image16.ref.png [new file with mode: 0644]
test/reference/clip-fill-nz-unbounded.quartz.argb32.ref.png [new file with mode: 0644]
test/reference/clip-fill-nz-unbounded.quartz.rgb24.ref.png [new file with mode: 0644]
test/reference/clip-fill-nz-unbounded.rgb24.ref.png [new file with mode: 0644]
test/reference/clip-fill-nz-unbounded.svg12.rgb24.xfail.png [new file with mode: 0644]
test/reference/clip-fill-nz-unbounded.traps.argb32.ref.png [new file with mode: 0644]
test/reference/clip-fill-nz-unbounded.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/clip-fill-nz-unbounded.xlib-fallback.rgb24.ref.png [new file with mode: 0644]
test/reference/clip-fill-rule-pixel-aligned.base.argb32.ref.png [new file with mode: 0644]
test/reference/clip-fill-rule-pixel-aligned.base.rgb24.ref.png [new file with mode: 0644]
test/reference/clip-fill-rule-pixel-aligned.ref.png [new file with mode: 0644]
test/reference/clip-fill-rule-pixel-aligned.rgb24.ref.png [new file with mode: 0644]
test/reference/clip-fill-rule-pixel-aligned.traps.argb32.ref.png [new file with mode: 0644]
test/reference/clip-fill-rule-pixel-aligned.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/clip-fill-rule.argb32.ref.png [new file with mode: 0644]
test/reference/clip-fill-rule.base.argb32.ref.png [new file with mode: 0644]
test/reference/clip-fill-rule.base.rgb24.ref.png [new file with mode: 0644]
test/reference/clip-fill-rule.image16.ref.png [new file with mode: 0644]
test/reference/clip-fill-rule.pdf.argb32.ref.png [new file with mode: 0644]
test/reference/clip-fill-rule.ps.argb32.ref.png [new file with mode: 0644]
test/reference/clip-fill-rule.ps.rgb24.ref.png [new file with mode: 0644]
test/reference/clip-fill-rule.quartz.rgb24.ref.png [new file with mode: 0644]
test/reference/clip-fill-rule.rgb24.ref.png [new file with mode: 0644]
test/reference/clip-fill-rule.test-paginated.rgb24.ref.png [new file with mode: 0644]
test/reference/clip-fill-rule.traps.argb32.ref.png [new file with mode: 0644]
test/reference/clip-fill-rule.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/clip-fill.base.argb32.ref.png [new file with mode: 0644]
test/reference/clip-fill.base.rgb24.ref.png [new file with mode: 0644]
test/reference/clip-fill.image16.ref.png [new file with mode: 0644]
test/reference/clip-fill.ps.xfail.png [new file with mode: 0644]
test/reference/clip-fill.quartz.ref.png [new file with mode: 0644]
test/reference/clip-fill.ref.png [new file with mode: 0644]
test/reference/clip-fill.traps.argb32.ref.png [new file with mode: 0644]
test/reference/clip-fill.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/clip-fill.xlib-fallback.ref.png [new file with mode: 0644]
test/reference/clip-group-shapes-aligned-rectangles.base.argb32.ref.png [new file with mode: 0644]
test/reference/clip-group-shapes-aligned-rectangles.base.rgb24.ref.png [new file with mode: 0644]
test/reference/clip-group-shapes-aligned-rectangles.ref.png [new file with mode: 0644]
test/reference/clip-group-shapes-aligned-rectangles.traps.argb32.ref.png [new file with mode: 0644]
test/reference/clip-group-shapes-aligned-rectangles.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/clip-group-shapes-circles.base.argb32.ref.png [new file with mode: 0644]
test/reference/clip-group-shapes-circles.base.rgb24.ref.png [new file with mode: 0644]
test/reference/clip-group-shapes-circles.ps.ref.png [new file with mode: 0644]
test/reference/clip-group-shapes-circles.quartz.ref.png [new file with mode: 0644]
test/reference/clip-group-shapes-circles.ref.png [new file with mode: 0644]
test/reference/clip-group-shapes-circles.traps.argb32.ref.png [new file with mode: 0644]
test/reference/clip-group-shapes-circles.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/clip-group-shapes-unaligned-rectangles.base.argb32.ref.png [new file with mode: 0644]
test/reference/clip-group-shapes-unaligned-rectangles.base.rgb24.ref.png [new file with mode: 0644]
test/reference/clip-group-shapes-unaligned-rectangles.ref.png [new file with mode: 0644]
test/reference/clip-group-shapes-unaligned-rectangles.traps.argb32.ref.png [new file with mode: 0644]
test/reference/clip-group-shapes-unaligned-rectangles.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/clip-image.base.argb32.ref.png [new file with mode: 0644]
test/reference/clip-image.base.rgb24.ref.png [new file with mode: 0644]
test/reference/clip-image.image16.ref.png [new file with mode: 0644]
test/reference/clip-image.ps.ref.png [new file with mode: 0644]
test/reference/clip-image.ref.png [new file with mode: 0644]
test/reference/clip-image.traps.argb32.ref.png [new file with mode: 0644]
test/reference/clip-image.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/clip-intersect.base.argb32.ref.png [new file with mode: 0644]
test/reference/clip-intersect.base.rgb24.ref.png [new file with mode: 0644]
test/reference/clip-intersect.ref.png [new file with mode: 0644]
test/reference/clip-intersect.traps.argb32.ref.png [new file with mode: 0644]
test/reference/clip-intersect.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/clip-mixed-antialias.base.argb32.ref.png [new file with mode: 0644]
test/reference/clip-mixed-antialias.base.rgb24.ref.png [new file with mode: 0644]
test/reference/clip-mixed-antialias.ref.png [new file with mode: 0644]
test/reference/clip-mixed-antialias.traps.argb32.ref.png [new file with mode: 0644]
test/reference/clip-mixed-antialias.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/clip-nesting.argb32.ref.png [new file with mode: 0644]
test/reference/clip-nesting.base.argb32.ref.png [new file with mode: 0644]
test/reference/clip-nesting.base.rgb24.ref.png [new file with mode: 0644]
test/reference/clip-nesting.pdf.argb32.ref.png [new file with mode: 0644]
test/reference/clip-nesting.ps.argb32.ref.png [new file with mode: 0644]
test/reference/clip-nesting.ps.rgb24.ref.png [new file with mode: 0644]
test/reference/clip-nesting.quartz.argb32.ref.png [new file with mode: 0644]
test/reference/clip-nesting.quartz.rgb24.ref.png [new file with mode: 0644]
test/reference/clip-nesting.rgb24.ref.png [new file with mode: 0644]
test/reference/clip-nesting.test-paginated.rgb24.ref.png [new file with mode: 0644]
test/reference/clip-nesting.traps.argb32.ref.png [new file with mode: 0644]
test/reference/clip-nesting.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/clip-operator.argb32.ref.png [new file with mode: 0644]
test/reference/clip-operator.base.argb32.ref.png [new file with mode: 0644]
test/reference/clip-operator.base.rgb24.ref.png [new file with mode: 0644]
test/reference/clip-operator.gl.argb32.ref.png [new file with mode: 0644]
test/reference/clip-operator.image16.ref.png [new file with mode: 0644]
test/reference/clip-operator.pdf.argb32.ref.png [new file with mode: 0644]
test/reference/clip-operator.pdf.rgb24.ref.png [new file with mode: 0644]
test/reference/clip-operator.ps2.rgb24.ref.png [new file with mode: 0644]
test/reference/clip-operator.ps3.argb32.ref.png [new file with mode: 0644]
test/reference/clip-operator.ps3.ref.png [new file with mode: 0644]
test/reference/clip-operator.ps3.rgb24.ref.png [new file with mode: 0644]
test/reference/clip-operator.quartz.argb32.ref.png [new file with mode: 0644]
test/reference/clip-operator.quartz.rgb24.ref.png [new file with mode: 0644]
test/reference/clip-operator.rgb24.ref.png [new file with mode: 0644]
test/reference/clip-operator.svg12.argb32.xfail.png [new file with mode: 0644]
test/reference/clip-operator.svg12.rgb24.xfail.png [new file with mode: 0644]
test/reference/clip-operator.test-paginated.argb32.ref.png [new file with mode: 0644]
test/reference/clip-operator.traps.argb32.ref.png [new file with mode: 0644]
test/reference/clip-operator.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/clip-operator.xlib-fallback.ref.png [new file with mode: 0644]
test/reference/clip-polygons.base.ref.png [new file with mode: 0644]
test/reference/clip-polygons.ref.png [new file with mode: 0644]
test/reference/clip-polygons.traps.ref.png [new file with mode: 0644]
test/reference/clip-push-group.base.argb32.ref.png [new file with mode: 0644]
test/reference/clip-push-group.base.rgb24.ref.png [new file with mode: 0644]
test/reference/clip-push-group.image16.ref.png [new file with mode: 0644]
test/reference/clip-push-group.pdf.ref.png [new file with mode: 0644]
test/reference/clip-push-group.ps.ref.png [new file with mode: 0644]
test/reference/clip-push-group.quartz.ref.png [new file with mode: 0644]
test/reference/clip-push-group.ref.png [new file with mode: 0644]
test/reference/clip-push-group.svg.ref.png [new file with mode: 0644]
test/reference/clip-push-group.traps.argb32.ref.png [new file with mode: 0644]
test/reference/clip-push-group.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/clip-rectilinear.base.ref.png [new file with mode: 0644]
test/reference/clip-rectilinear.ref.png [new file with mode: 0644]
test/reference/clip-rectilinear.traps.ref.png [new file with mode: 0644]
test/reference/clip-rotate-image-surface-paint.base.argb32.ref.png [new file with mode: 0644]
test/reference/clip-rotate-image-surface-paint.base.rgb24.ref.png [new file with mode: 0644]
test/reference/clip-rotate-image-surface-paint.ref.png [new file with mode: 0644]
test/reference/clip-rotate-image-surface-paint.traps.argb32.ref.png [new file with mode: 0644]
test/reference/clip-rotate-image-surface-paint.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/clip-shape.base.argb32.ref.png [new file with mode: 0644]
test/reference/clip-shape.base.rgb24.ref.png [new file with mode: 0644]
test/reference/clip-shape.image16.ref.png [new file with mode: 0644]
test/reference/clip-shape.ps.ref.png [new file with mode: 0644]
test/reference/clip-shape.quartz.ref.png [new file with mode: 0644]
test/reference/clip-shape.ref.png [new file with mode: 0644]
test/reference/clip-shape.traps.argb32.ref.png [new file with mode: 0644]
test/reference/clip-shape.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/clip-shape.xlib-fallback.ref.png [new file with mode: 0644]
test/reference/clip-stroke-no-op.base.argb32.ref.png [new file with mode: 0644]
test/reference/clip-stroke-no-op.base.rgb24.ref.png [new file with mode: 0644]
test/reference/clip-stroke-no-op.image16.ref.png [new file with mode: 0644]
test/reference/clip-stroke-no-op.ref.png [new file with mode: 0644]
test/reference/clip-stroke-no-op.traps.argb32.ref.png [new file with mode: 0644]
test/reference/clip-stroke-no-op.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/clip-stroke-unbounded.argb32.ref.png [new file with mode: 0644]
test/reference/clip-stroke-unbounded.base.argb32.ref.png [new file with mode: 0644]
test/reference/clip-stroke-unbounded.base.rgb24.ref.png [new file with mode: 0644]
test/reference/clip-stroke-unbounded.image16.rgb24.ref.png [new file with mode: 0644]
test/reference/clip-stroke-unbounded.quartz.argb32.ref.png [new file with mode: 0644]
test/reference/clip-stroke-unbounded.quartz.rgb24.ref.png [new file with mode: 0644]
test/reference/clip-stroke-unbounded.rgb24.ref.png [new file with mode: 0644]
test/reference/clip-stroke-unbounded.svg12.rgb24.xfail.png [new file with mode: 0644]
test/reference/clip-stroke-unbounded.traps.argb32.ref.png [new file with mode: 0644]
test/reference/clip-stroke-unbounded.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/clip-stroke-unbounded.xlib-fallback.rgb24.ref.png [new file with mode: 0644]
test/reference/clip-stroke.base.argb32.ref.png [new file with mode: 0644]
test/reference/clip-stroke.base.rgb24.ref.png [new file with mode: 0644]
test/reference/clip-stroke.image16.ref.png [new file with mode: 0644]
test/reference/clip-stroke.ps.xfail.png [new file with mode: 0644]
test/reference/clip-stroke.quartz.ref.png [new file with mode: 0644]
test/reference/clip-stroke.ref.png [new file with mode: 0644]
test/reference/clip-stroke.traps.argb32.ref.png [new file with mode: 0644]
test/reference/clip-stroke.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/clip-stroke.xlib-fallback.ref.png [new file with mode: 0644]
test/reference/clip-text.base.argb32.ref.png [new file with mode: 0644]
test/reference/clip-text.base.rgb24.ref.png [new file with mode: 0644]
test/reference/clip-text.image16.ref.png [new file with mode: 0644]
test/reference/clip-text.ps.xfail.png [new file with mode: 0644]
test/reference/clip-text.quartz.ref.png [new file with mode: 0644]
test/reference/clip-text.ref.png [new file with mode: 0644]
test/reference/clip-text.svg.ref.png [new file with mode: 0644]
test/reference/clip-text.traps.argb32.ref.png [new file with mode: 0644]
test/reference/clip-text.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/clip-twice-rectangle.base.argb32.ref.png [new file with mode: 0644]
test/reference/clip-twice-rectangle.base.rgb24.ref.png [new file with mode: 0644]
test/reference/clip-twice-rectangle.ref.png [new file with mode: 0644]
test/reference/clip-twice-rectangle.traps.argb32.ref.png [new file with mode: 0644]
test/reference/clip-twice-rectangle.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/clip-twice.argb32.ref.png [new file with mode: 0644]
test/reference/clip-twice.base.argb32.ref.png [new file with mode: 0644]
test/reference/clip-twice.base.rgb24.ref.png [new file with mode: 0644]
test/reference/clip-twice.image16.ref.png [new file with mode: 0644]
test/reference/clip-twice.pdf.argb32.ref.png [new file with mode: 0644]
test/reference/clip-twice.ps.argb32.ref.png [new file with mode: 0644]
test/reference/clip-twice.ps.rgb24.ref.png [new file with mode: 0644]
test/reference/clip-twice.quartz.argb32.ref.png [new file with mode: 0644]
test/reference/clip-twice.quartz.rgb24.ref.png [new file with mode: 0644]
test/reference/clip-twice.rgb24.ref.png [new file with mode: 0644]
test/reference/clip-twice.test-paginated.argb32.ref.png [new file with mode: 0644]
test/reference/clip-twice.test-paginated.rgb24.ref.png [new file with mode: 0644]
test/reference/clip-twice.traps.argb32.ref.png [new file with mode: 0644]
test/reference/clip-twice.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/clip-unbounded.base.argb32.ref.png [new file with mode: 0644]
test/reference/clip-unbounded.base.rgb24.ref.png [new file with mode: 0644]
test/reference/clip-unbounded.pdf.argb32.xfail.png [new file with mode: 0644]
test/reference/clip-unbounded.pdf.rgb24.xfail.png [new file with mode: 0644]
test/reference/clip-unbounded.ref.png [new file with mode: 0644]
test/reference/clip-unbounded.rgb24.ref.png [new file with mode: 0644]
test/reference/clip-unbounded.svg12.rgb24.xfail.png [new file with mode: 0644]
test/reference/clip-unbounded.traps.argb32.ref.png [new file with mode: 0644]
test/reference/clip-unbounded.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/clipped-group.base.argb32.ref.png [new file with mode: 0644]
test/reference/clipped-group.base.rgb24.ref.png [new file with mode: 0644]
test/reference/clipped-group.image16.ref.png [new file with mode: 0644]
test/reference/clipped-group.pdf.ref.png [new file with mode: 0644]
test/reference/clipped-group.ps2.ref.png [new file with mode: 0644]
test/reference/clipped-group.ps3.ref.png [new file with mode: 0644]
test/reference/clipped-group.quartz.ref.png [new file with mode: 0644]
test/reference/clipped-group.ref.png [new file with mode: 0644]
test/reference/clipped-group.svg.ref.png [new file with mode: 0644]
test/reference/clipped-group.traps.argb32.ref.png [new file with mode: 0644]
test/reference/clipped-group.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/clipped-group.xlib-fallback.ref.png [new file with mode: 0644]
test/reference/clipped-surface.base.argb32.ref.png [new file with mode: 0644]
test/reference/clipped-surface.base.rgb24.ref.png [new file with mode: 0644]
test/reference/clipped-surface.image16.ref.png [new file with mode: 0644]
test/reference/clipped-surface.ref.png [new file with mode: 0644]
test/reference/clipped-surface.traps.argb32.ref.png [new file with mode: 0644]
test/reference/clipped-surface.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/clipped-trapezoids.ref.png [new file with mode: 0644]
test/reference/close-path-current-point.base.argb32.ref.png [new file with mode: 0644]
test/reference/close-path-current-point.base.rgb24.ref.png [new file with mode: 0644]
test/reference/close-path-current-point.image16.ref.png [new file with mode: 0644]
test/reference/close-path-current-point.ps.ref.png [new file with mode: 0644]
test/reference/close-path-current-point.ref.png [new file with mode: 0644]
test/reference/close-path-current-point.traps.argb32.ref.png [new file with mode: 0644]
test/reference/close-path-current-point.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/close-path.base.argb32.ref.png [new file with mode: 0644]
test/reference/close-path.base.rgb24.ref.png [new file with mode: 0644]
test/reference/close-path.ps2.ref.png [new file with mode: 0644]
test/reference/close-path.ps3.ref.png [new file with mode: 0644]
test/reference/close-path.ref.png [new file with mode: 0644]
test/reference/close-path.traps.argb32.ref.png [new file with mode: 0644]
test/reference/close-path.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/composite-integer-translate-over-repeat.base.argb32.ref.png [new file with mode: 0644]
test/reference/composite-integer-translate-over-repeat.base.rgb24.ref.png [new file with mode: 0644]
test/reference/composite-integer-translate-over-repeat.ps2.ref.png [new file with mode: 0644]
test/reference/composite-integer-translate-over-repeat.ps3.ref.png [new file with mode: 0644]
test/reference/composite-integer-translate-over-repeat.ref.png [new file with mode: 0644]
test/reference/composite-integer-translate-over-repeat.traps.argb32.ref.png [new file with mode: 0644]
test/reference/composite-integer-translate-over-repeat.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/composite-integer-translate-over.base.argb32.ref.png [new file with mode: 0644]
test/reference/composite-integer-translate-over.base.rgb24.ref.png [new file with mode: 0644]
test/reference/composite-integer-translate-over.image16.ref.png [new file with mode: 0644]
test/reference/composite-integer-translate-over.ps2.ref.png [new file with mode: 0644]
test/reference/composite-integer-translate-over.ps3.ref.png [new file with mode: 0644]
test/reference/composite-integer-translate-over.ref.png [new file with mode: 0644]
test/reference/composite-integer-translate-over.traps.argb32.ref.png [new file with mode: 0644]
test/reference/composite-integer-translate-over.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/composite-integer-translate-source.base.argb32.ref.png [new file with mode: 0644]
test/reference/composite-integer-translate-source.base.rgb24.ref.png [new file with mode: 0644]
test/reference/composite-integer-translate-source.image16.ref.png [new file with mode: 0644]
test/reference/composite-integer-translate-source.ps2.ref.png [new file with mode: 0644]
test/reference/composite-integer-translate-source.ps3.ref.png [new file with mode: 0644]
test/reference/composite-integer-translate-source.ref.png [new file with mode: 0644]
test/reference/composite-integer-translate-source.svg12.argb32.xfail.png [new file with mode: 0644]
test/reference/composite-integer-translate-source.svg12.rgb24.xfail.png [new file with mode: 0644]
test/reference/composite-integer-translate-source.traps.argb32.ref.png [new file with mode: 0644]
test/reference/composite-integer-translate-source.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/copy-path.base.argb32.ref.png [new file with mode: 0644]
test/reference/copy-path.base.rgb24.ref.png [new file with mode: 0644]
test/reference/copy-path.image16.ref.png [new file with mode: 0644]
test/reference/copy-path.ps.ref.png [new file with mode: 0644]
test/reference/copy-path.ref.png [new file with mode: 0644]
test/reference/copy-path.traps.argb32.ref.png [new file with mode: 0644]
test/reference/copy-path.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/coverage-column-triangles.ref.png [new file with mode: 0644]
test/reference/coverage-column-triangles.xlib.xfail.png [new file with mode: 0644]
test/reference/coverage-intersecting-quads.ref.png [new file with mode: 0644]
test/reference/coverage-intersecting-quads.xlib.xfail.png [new file with mode: 0644]
test/reference/coverage-intersecting-triangles.ref.png [new file with mode: 0644]
test/reference/coverage-intersecting-triangles.xlib.xfail.png [new file with mode: 0644]
test/reference/coverage-rectangles.ref.png [new file with mode: 0644]
test/reference/coverage-rectangles.xlib.xfail.png [new file with mode: 0644]
test/reference/coverage-row-triangles.ref.png [new file with mode: 0644]
test/reference/coverage-row-triangles.xlib.xfail.png [new file with mode: 0644]
test/reference/coverage-triangles.ref.png [new file with mode: 0644]
test/reference/coverage-triangles.xlib.xfail.png [new file with mode: 0644]
test/reference/create-from-png-stream.base.argb32.ref.png [new file with mode: 0644]
test/reference/create-from-png-stream.base.rgb24.ref.png [new file with mode: 0644]
test/reference/create-from-png-stream.ref.png [new file with mode: 0644]
test/reference/create-from-png-stream.traps.argb32.ref.png [new file with mode: 0644]
test/reference/create-from-png-stream.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/create-from-png.alpha.ref.png [new file with mode: 0644]
test/reference/create-from-png.base.argb32.ref.png [new file with mode: 0644]
test/reference/create-from-png.base.rgb24.ref.png [new file with mode: 0644]
test/reference/create-from-png.gray-alpha.ref.png [new file with mode: 0644]
test/reference/create-from-png.gray.ref.png [new file with mode: 0644]
test/reference/create-from-png.indexed-alpha.ref.png [new file with mode: 0644]
test/reference/create-from-png.indexed.ref.png [new file with mode: 0644]
test/reference/create-from-png.ref.png [new file with mode: 0644]
test/reference/culled-glyphs.base.argb32.ref.png [new file with mode: 0644]
test/reference/culled-glyphs.base.rgb24.ref.png [new file with mode: 0644]
test/reference/culled-glyphs.image16.ref.png [new file with mode: 0644]
test/reference/culled-glyphs.ps.ref.png [new file with mode: 0644]
test/reference/culled-glyphs.quartz.ref.png [new file with mode: 0644]
test/reference/culled-glyphs.ref.png [new file with mode: 0644]
test/reference/culled-glyphs.traps.argb32.ref.png [new file with mode: 0644]
test/reference/culled-glyphs.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/curve-to-as-line-to.base.argb32.ref.png [new file with mode: 0644]
test/reference/curve-to-as-line-to.base.rgb24.ref.png [new file with mode: 0644]
test/reference/curve-to-as-line-to.ps.xfail.png [new file with mode: 0644]
test/reference/curve-to-as-line-to.ref.png [new file with mode: 0644]
test/reference/curve-to-as-line-to.traps.argb32.ref.png [new file with mode: 0644]
test/reference/curve-to-as-line-to.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/dash-caps-joins.base.argb32.ref.png [new file with mode: 0644]
test/reference/dash-caps-joins.base.rgb24.ref.png [new file with mode: 0644]
test/reference/dash-caps-joins.image16.ref.png [new file with mode: 0644]
test/reference/dash-caps-joins.ps.ref.png [new file with mode: 0644]
test/reference/dash-caps-joins.quartz.xfail.png [new file with mode: 0644]
test/reference/dash-caps-joins.ref.png [new file with mode: 0644]
test/reference/dash-caps-joins.traps.argb32.ref.png [new file with mode: 0644]
test/reference/dash-caps-joins.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/dash-curve.base.argb32.ref.png [new file with mode: 0644]
test/reference/dash-curve.base.rgb24.ref.png [new file with mode: 0644]
test/reference/dash-curve.image16.ref.png [new file with mode: 0644]
test/reference/dash-curve.ps2.ref.png [new file with mode: 0644]
test/reference/dash-curve.ps3.ref.png [new file with mode: 0644]
test/reference/dash-curve.quartz.xfail.png [new file with mode: 0644]
test/reference/dash-curve.ref.png [new file with mode: 0644]
test/reference/dash-curve.traps.argb32.ref.png [new file with mode: 0644]
test/reference/dash-curve.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/dash-infinite-loop.base.argb32.ref.png [new file with mode: 0644]
test/reference/dash-infinite-loop.base.rgb24.ref.png [new file with mode: 0644]
test/reference/dash-infinite-loop.ps.ref.png [new file with mode: 0644]
test/reference/dash-infinite-loop.ref.png [new file with mode: 0644]
test/reference/dash-infinite-loop.traps.argb32.ref.png [new file with mode: 0644]
test/reference/dash-infinite-loop.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/dash-no-dash.base.argb32.ref.png [new file with mode: 0644]
test/reference/dash-no-dash.base.rgb24.ref.png [new file with mode: 0644]
test/reference/dash-no-dash.ref.png [new file with mode: 0644]
test/reference/dash-no-dash.traps.argb32.ref.png [new file with mode: 0644]
test/reference/dash-no-dash.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/dash-offset-negative.base.argb32.ref.png [new file with mode: 0644]
test/reference/dash-offset-negative.base.rgb24.ref.png [new file with mode: 0644]
test/reference/dash-offset-negative.pdf.ref.png [new file with mode: 0644]
test/reference/dash-offset-negative.ref.png [new file with mode: 0644]
test/reference/dash-offset-negative.traps.argb32.ref.png [new file with mode: 0644]
test/reference/dash-offset-negative.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/dash-offset.base.argb32.ref.png [new file with mode: 0644]
test/reference/dash-offset.base.rgb24.ref.png [new file with mode: 0644]
test/reference/dash-offset.ref.png [new file with mode: 0644]
test/reference/dash-offset.traps.argb32.ref.png [new file with mode: 0644]
test/reference/dash-offset.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/dash-scale.base.argb32.ref.png [new file with mode: 0644]
test/reference/dash-scale.base.rgb24.ref.png [new file with mode: 0644]
test/reference/dash-scale.image16.ref.png [new file with mode: 0644]
test/reference/dash-scale.ps.ref.png [new file with mode: 0644]
test/reference/dash-scale.quartz.ref.png [new file with mode: 0644]
test/reference/dash-scale.ref.png [new file with mode: 0644]
test/reference/dash-scale.traps.argb32.ref.png [new file with mode: 0644]
test/reference/dash-scale.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/dash-state.base.argb32.ref.png [new file with mode: 0644]
test/reference/dash-state.base.rgb24.ref.png [new file with mode: 0644]
test/reference/dash-state.image16.ref.png [new file with mode: 0644]
test/reference/dash-state.ps2.ref.png [new file with mode: 0644]
test/reference/dash-state.ps3.ref.png [new file with mode: 0644]
test/reference/dash-state.quartz.xfail.png [new file with mode: 0644]
test/reference/dash-state.ref.png [new file with mode: 0644]
test/reference/dash-state.traps.argb32.ref.png [new file with mode: 0644]
test/reference/dash-state.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/dash-zero-length.base.argb32.ref.png [new file with mode: 0644]
test/reference/dash-zero-length.base.rgb24.ref.png [new file with mode: 0644]
test/reference/dash-zero-length.ps2.ref.png [new file with mode: 0644]
test/reference/dash-zero-length.ps2.rgb24.ref.png [new file with mode: 0644]
test/reference/dash-zero-length.ps3.ref.png [new file with mode: 0644]
test/reference/dash-zero-length.ps3.rgb24.ref.png [new file with mode: 0644]
test/reference/dash-zero-length.ref.png [new file with mode: 0644]
test/reference/dash-zero-length.rgb24.ref.png [new file with mode: 0644]
test/reference/dash-zero-length.traps.argb32.ref.png [new file with mode: 0644]
test/reference/dash-zero-length.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/degenerate-arc.base.argb32.ref.png [new file with mode: 0644]
test/reference/degenerate-arc.base.rgb24.ref.png [new file with mode: 0644]
test/reference/degenerate-arc.image16.ref.png [new file with mode: 0644]
test/reference/degenerate-arc.ps2.ref.png [new file with mode: 0644]
test/reference/degenerate-arc.ps3.ref.png [new file with mode: 0644]
test/reference/degenerate-arc.quartz.ref.png [new file with mode: 0644]
test/reference/degenerate-arc.ref.png [new file with mode: 0644]
test/reference/degenerate-arc.traps.argb32.ref.png [new file with mode: 0644]
test/reference/degenerate-arc.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/degenerate-arcs.base.argb32.ref.png [new file with mode: 0644]
test/reference/degenerate-arcs.base.rgb24.ref.png [new file with mode: 0644]
test/reference/degenerate-arcs.image16.ref.png [new file with mode: 0644]
test/reference/degenerate-arcs.ref.png [new file with mode: 0644]
test/reference/degenerate-arcs.traps.argb32.ref.png [new file with mode: 0644]
test/reference/degenerate-arcs.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/degenerate-curve-to.base.argb32.ref.png [new file with mode: 0644]
test/reference/degenerate-curve-to.base.rgb24.ref.png [new file with mode: 0644]
test/reference/degenerate-curve-to.image16.ref.png [new file with mode: 0644]
test/reference/degenerate-curve-to.ps.xfail.png [new file with mode: 0644]
test/reference/degenerate-curve-to.quartz.ref.png [new file with mode: 0644]
test/reference/degenerate-curve-to.ref.png [new file with mode: 0644]
test/reference/degenerate-curve-to.traps.argb32.ref.png [new file with mode: 0644]
test/reference/degenerate-curve-to.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/degenerate-dash.base.argb32.ref.png [new file with mode: 0644]
test/reference/degenerate-dash.base.rgb24.ref.png [new file with mode: 0644]
test/reference/degenerate-dash.ps.xfail.png [new file with mode: 0644]
test/reference/degenerate-dash.quartz.xfail.png [new file with mode: 0644]
test/reference/degenerate-dash.ref.png [new file with mode: 0644]
test/reference/degenerate-dash.traps.argb32.ref.png [new file with mode: 0644]
test/reference/degenerate-dash.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/degenerate-linear-gradient.base.argb32.ref.png [new file with mode: 0644]
test/reference/degenerate-linear-gradient.base.rgb24.ref.png [new file with mode: 0644]
test/reference/degenerate-linear-gradient.ref.png [new file with mode: 0644]
test/reference/degenerate-linear-gradient.traps.argb32.ref.png [new file with mode: 0644]
test/reference/degenerate-linear-gradient.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/degenerate-path.argb32.ref.png [new file with mode: 0644]
test/reference/degenerate-path.base.argb32.ref.png [new file with mode: 0644]
test/reference/degenerate-path.base.rgb24.ref.png [new file with mode: 0644]
test/reference/degenerate-path.ps.argb32.xfail.png [new file with mode: 0644]
test/reference/degenerate-path.ps.rgb24.xfail.png [new file with mode: 0644]
test/reference/degenerate-path.quartz.argb32.xfail.png [new file with mode: 0644]
test/reference/degenerate-path.quartz.rgb24.xfail.png [new file with mode: 0644]
test/reference/degenerate-path.rgb24.ref.png [new file with mode: 0644]
test/reference/degenerate-path.traps.argb32.ref.png [new file with mode: 0644]
test/reference/degenerate-path.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/degenerate-pen.base.argb32.ref.png [new file with mode: 0644]
test/reference/degenerate-pen.base.rgb24.ref.png [new file with mode: 0644]
test/reference/degenerate-pen.image16.ref.png [new file with mode: 0644]
test/reference/degenerate-pen.ps.ref.png [new file with mode: 0644]
test/reference/degenerate-pen.ref.png [new file with mode: 0644]
test/reference/degenerate-pen.traps.argb32.ref.png [new file with mode: 0644]
test/reference/degenerate-pen.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/degenerate-radial-gradient.base.argb32.ref.png [new file with mode: 0644]
test/reference/degenerate-radial-gradient.base.rgb24.ref.png [new file with mode: 0644]
test/reference/degenerate-radial-gradient.ref.png [new file with mode: 0644]
test/reference/degenerate-radial-gradient.traps.argb32.ref.png [new file with mode: 0644]
test/reference/degenerate-radial-gradient.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/degenerate-rel-curve-to.base.argb32.ref.png [new file with mode: 0644]
test/reference/degenerate-rel-curve-to.base.rgb24.ref.png [new file with mode: 0644]
test/reference/degenerate-rel-curve-to.image16.ref.png [new file with mode: 0644]
test/reference/degenerate-rel-curve-to.ps.ref.png [new file with mode: 0644]
test/reference/degenerate-rel-curve-to.quartz.ref.png [new file with mode: 0644]
test/reference/degenerate-rel-curve-to.ref.png [new file with mode: 0644]
test/reference/degenerate-rel-curve-to.traps.argb32.ref.png [new file with mode: 0644]
test/reference/degenerate-rel-curve-to.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/device-offset-fractional.base.argb32.ref.png [new file with mode: 0644]
test/reference/device-offset-fractional.base.rgb24.ref.png [new file with mode: 0644]
test/reference/device-offset-fractional.gl.xfail.png [new file with mode: 0644]
test/reference/device-offset-fractional.pdf.xfail.png [new file with mode: 0644]
test/reference/device-offset-fractional.ps2.ref.png [new file with mode: 0644]
test/reference/device-offset-fractional.ps3.ref.png [new file with mode: 0644]
test/reference/device-offset-fractional.ref.png [new file with mode: 0644]
test/reference/device-offset-fractional.traps.argb32.ref.png [new file with mode: 0644]
test/reference/device-offset-fractional.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/device-offset-positive.base.argb32.ref.png [new file with mode: 0644]
test/reference/device-offset-positive.base.rgb24.ref.png [new file with mode: 0644]
test/reference/device-offset-positive.ref.png [new file with mode: 0644]
test/reference/device-offset-positive.rgb24.ref.png [new file with mode: 0644]
test/reference/device-offset-positive.traps.argb32.ref.png [new file with mode: 0644]
test/reference/device-offset-positive.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/device-offset-scale.base.argb32.ref.png [new file with mode: 0644]
test/reference/device-offset-scale.base.rgb24.ref.png [new file with mode: 0644]
test/reference/device-offset-scale.ref.png [new file with mode: 0644]
test/reference/device-offset-scale.svg.xfail.png [new file with mode: 0644]
test/reference/device-offset-scale.traps.argb32.ref.png [new file with mode: 0644]
test/reference/device-offset-scale.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/device-offset.base.argb32.ref.png [new file with mode: 0644]
test/reference/device-offset.base.rgb24.ref.png [new file with mode: 0644]
test/reference/device-offset.ref.png [new file with mode: 0644]
test/reference/device-offset.rgb24.ref.png [new file with mode: 0644]
test/reference/device-offset.traps.argb32.ref.png [new file with mode: 0644]
test/reference/device-offset.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/drunkard-tails.base.argb32.ref.png [new file with mode: 0644]
test/reference/drunkard-tails.base.rgb24.ref.png [new file with mode: 0644]
test/reference/drunkard-tails.ps.ref.png [new file with mode: 0644]
test/reference/drunkard-tails.ref.png [new file with mode: 0644]
test/reference/drunkard-tails.traps.argb32.ref.png [new file with mode: 0644]
test/reference/drunkard-tails.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/extend-pad-border.base.argb32.ref.png [new file with mode: 0644]
test/reference/extend-pad-border.base.rgb24.ref.png [new file with mode: 0644]
test/reference/extend-pad-border.image16.ref.png [new file with mode: 0644]
test/reference/extend-pad-border.pdf.ref.png [new file with mode: 0644]
test/reference/extend-pad-border.ps.ref.png [new file with mode: 0644]
test/reference/extend-pad-border.quartz.ref.png [new file with mode: 0644]
test/reference/extend-pad-border.ref.png [new file with mode: 0644]
test/reference/extend-pad-border.svg.xfail.png [new file with mode: 0644]
test/reference/extend-pad-border.traps.argb32.ref.png [new file with mode: 0644]
test/reference/extend-pad-border.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/extend-pad-similar.base.argb32.ref.png [new file with mode: 0644]
test/reference/extend-pad-similar.base.rgb24.ref.png [new file with mode: 0644]
test/reference/extend-pad-similar.quartz.xfail.png [new file with mode: 0644]
test/reference/extend-pad-similar.ref.png [new file with mode: 0644]
test/reference/extend-pad-similar.svg.xfail.png [new file with mode: 0644]
test/reference/extend-pad-similar.traps.argb32.ref.png [new file with mode: 0644]
test/reference/extend-pad-similar.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/extend-pad.base.argb32.ref.png [new file with mode: 0644]
test/reference/extend-pad.base.rgb24.ref.png [new file with mode: 0644]
test/reference/extend-pad.ps.ref.png [new file with mode: 0644]
test/reference/extend-pad.quartz.xfail.png [new file with mode: 0644]
test/reference/extend-pad.ref.png [new file with mode: 0644]
test/reference/extend-pad.svg.xfail.png [new file with mode: 0644]
test/reference/extend-pad.traps.argb32.ref.png [new file with mode: 0644]
test/reference/extend-pad.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/extend-reflect-similar.base.argb32.ref.png [new file with mode: 0644]
test/reference/extend-reflect-similar.base.rgb24.ref.png [new file with mode: 0644]
test/reference/extend-reflect-similar.image16.ref.png [new file with mode: 0644]
test/reference/extend-reflect-similar.ps2.ref.png [new file with mode: 0644]
test/reference/extend-reflect-similar.ps3.ref.png [new file with mode: 0644]
test/reference/extend-reflect-similar.ref.png [new file with mode: 0644]
test/reference/extend-reflect-similar.traps.argb32.ref.png [new file with mode: 0644]
test/reference/extend-reflect-similar.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/extend-reflect.base.argb32.ref.png [new file with mode: 0644]
test/reference/extend-reflect.base.rgb24.ref.png [new file with mode: 0644]
test/reference/extend-reflect.image16.ref.png [new file with mode: 0644]
test/reference/extend-reflect.ps2.ref.png [new file with mode: 0644]
test/reference/extend-reflect.ps3.ref.png [new file with mode: 0644]
test/reference/extend-reflect.ref.png [new file with mode: 0644]
test/reference/extend-reflect.traps.argb32.ref.png [new file with mode: 0644]
test/reference/extend-reflect.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/extend-repeat-similar.base.argb32.ref.png [new file with mode: 0644]
test/reference/extend-repeat-similar.base.rgb24.ref.png [new file with mode: 0644]
test/reference/extend-repeat-similar.image16.ref.png [new file with mode: 0644]
test/reference/extend-repeat-similar.ps2.ref.png [new file with mode: 0644]
test/reference/extend-repeat-similar.ps3.ref.png [new file with mode: 0644]
test/reference/extend-repeat-similar.ref.png [new file with mode: 0644]
test/reference/extend-repeat-similar.traps.argb32.ref.png [new file with mode: 0644]
test/reference/extend-repeat-similar.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/extend-repeat.base.argb32.ref.png [new file with mode: 0644]
test/reference/extend-repeat.base.rgb24.ref.png [new file with mode: 0644]
test/reference/extend-repeat.image16.ref.png [new file with mode: 0644]
test/reference/extend-repeat.ps2.ref.png [new file with mode: 0644]
test/reference/extend-repeat.ps3.ref.png [new file with mode: 0644]
test/reference/extend-repeat.ref.png [new file with mode: 0644]
test/reference/extend-repeat.traps.argb32.ref.png [new file with mode: 0644]
test/reference/extend-repeat.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/extended-blend-alpha-mask.argb32.ref.png [new file with mode: 0644]
test/reference/extended-blend-alpha-mask.base.argb32.ref.png [new file with mode: 0644]
test/reference/extended-blend-alpha-mask.base.rgb24.ref.png [new file with mode: 0644]
test/reference/extended-blend-alpha-mask.rgb24.ref.png [new file with mode: 0644]
test/reference/extended-blend-alpha-mask.traps.argb32.ref.png [new file with mode: 0644]
test/reference/extended-blend-alpha-mask.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/extended-blend-alpha.argb32.ref.png [new file with mode: 0644]
test/reference/extended-blend-alpha.base.argb32.ref.png [new file with mode: 0644]
test/reference/extended-blend-alpha.base.rgb24.ref.png [new file with mode: 0644]
test/reference/extended-blend-alpha.image16.ref.png [new file with mode: 0644]
test/reference/extended-blend-alpha.quartz.argb32.ref.png [new file with mode: 0644]
test/reference/extended-blend-alpha.quartz.rgb24.ref.png [new file with mode: 0644]
test/reference/extended-blend-alpha.rgb24.ref.png [new file with mode: 0644]
test/reference/extended-blend-alpha.svg12.argb32.xfail.png [new file with mode: 0644]
test/reference/extended-blend-alpha.svg12.rgb24.xfail.png [new file with mode: 0644]
test/reference/extended-blend-alpha.traps.argb32.ref.png [new file with mode: 0644]
test/reference/extended-blend-alpha.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/extended-blend-mask.argb32.ref.png [new file with mode: 0644]
test/reference/extended-blend-mask.base.argb32.ref.png [new file with mode: 0644]
test/reference/extended-blend-mask.base.rgb24.ref.png [new file with mode: 0644]
test/reference/extended-blend-mask.rgb24.ref.png [new file with mode: 0644]
test/reference/extended-blend-mask.traps.argb32.ref.png [new file with mode: 0644]
test/reference/extended-blend-mask.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/extended-blend-solid-alpha.argb32.ref.png [new file with mode: 0644]
test/reference/extended-blend-solid-alpha.base.argb32.ref.png [new file with mode: 0644]
test/reference/extended-blend-solid-alpha.base.rgb24.ref.png [new file with mode: 0644]
test/reference/extended-blend-solid-alpha.image16.ref.png [new file with mode: 0644]
test/reference/extended-blend-solid-alpha.rgb24.ref.png [new file with mode: 0644]
test/reference/extended-blend-solid-alpha.traps.argb32.ref.png [new file with mode: 0644]
test/reference/extended-blend-solid-alpha.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/extended-blend-solid.argb32.ref.png [new file with mode: 0644]
test/reference/extended-blend-solid.base.argb32.ref.png [new file with mode: 0644]
test/reference/extended-blend-solid.base.rgb24.ref.png [new file with mode: 0644]
test/reference/extended-blend-solid.image16.ref.png [new file with mode: 0644]
test/reference/extended-blend-solid.rgb24.ref.png [new file with mode: 0644]
test/reference/extended-blend-solid.traps.argb32.ref.png [new file with mode: 0644]
test/reference/extended-blend-solid.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/extended-blend.argb32.ref.png [new file with mode: 0644]
test/reference/extended-blend.base.argb32.ref.png [new file with mode: 0644]
test/reference/extended-blend.base.rgb24.ref.png [new file with mode: 0644]
test/reference/extended-blend.image16.ref.png [new file with mode: 0644]
test/reference/extended-blend.quartz.argb32.ref.png [new file with mode: 0644]
test/reference/extended-blend.quartz.rgb24.ref.png [new file with mode: 0644]
test/reference/extended-blend.rgb24.ref.png [new file with mode: 0644]
test/reference/extended-blend.svg12.argb32.xfail.png [new file with mode: 0644]
test/reference/extended-blend.svg12.rgb24.xfail.png [new file with mode: 0644]
test/reference/extended-blend.traps.argb32.ref.png [new file with mode: 0644]
test/reference/extended-blend.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/fallback-resolution.ppi144x144.ps.ref.png [new file with mode: 0644]
test/reference/fallback-resolution.ppi144x144.ref.png [new file with mode: 0644]
test/reference/fallback-resolution.ppi144x72.ps.ref.png [new file with mode: 0644]
test/reference/fallback-resolution.ppi144x72.ref.png [new file with mode: 0644]
test/reference/fallback-resolution.ppi288x288.pdf.ref.png [new file with mode: 0644]
test/reference/fallback-resolution.ppi288x288.ps.ref.png [new file with mode: 0644]
test/reference/fallback-resolution.ppi288x288.svg.ref.png [new file with mode: 0644]
test/reference/fallback-resolution.ppi288x72.ps.ref.png [new file with mode: 0644]
test/reference/fallback-resolution.ppi288x72.ref.png [new file with mode: 0644]
test/reference/fallback-resolution.ppi576x576.pdf.ref.png [new file with mode: 0644]
test/reference/fallback-resolution.ppi576x576.ps.ref.png [new file with mode: 0644]
test/reference/fallback-resolution.ppi576x576.svg.ref.png [new file with mode: 0644]
test/reference/fallback-resolution.ppi576x72.ps.ref.png [new file with mode: 0644]
test/reference/fallback-resolution.ppi576x72.ref.png [new file with mode: 0644]
test/reference/fallback-resolution.ppi72x144.ps.ref.png [new file with mode: 0644]
test/reference/fallback-resolution.ppi72x144.ref.png [new file with mode: 0644]
test/reference/fallback-resolution.ppi72x288.ps.ref.png [new file with mode: 0644]
test/reference/fallback-resolution.ppi72x288.ref.png [new file with mode: 0644]
test/reference/fallback-resolution.ppi72x576.ps.ref.png [new file with mode: 0644]
test/reference/fallback-resolution.ppi72x576.ref.png [new file with mode: 0644]
test/reference/fallback-resolution.ppi72x72.ref.png [new file with mode: 0644]
test/reference/fill-alpha-pattern.base.argb32.ref.png [new file with mode: 0644]
test/reference/fill-alpha-pattern.base.rgb24.ref.png [new file with mode: 0644]
test/reference/fill-alpha-pattern.image16.ref.png [new file with mode: 0644]
test/reference/fill-alpha-pattern.pdf.ref.png [new file with mode: 0644]
test/reference/fill-alpha-pattern.ps3.argb32.ref.png [new file with mode: 0644]
test/reference/fill-alpha-pattern.quartz.ref.png [new file with mode: 0644]
test/reference/fill-alpha-pattern.ref.png [new file with mode: 0644]
test/reference/fill-alpha-pattern.traps.argb32.ref.png [new file with mode: 0644]
test/reference/fill-alpha-pattern.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/fill-alpha.base.argb32.ref.png [new file with mode: 0644]
test/reference/fill-alpha.base.rgb24.ref.png [new file with mode: 0644]
test/reference/fill-alpha.image16.ref.png [new file with mode: 0644]
test/reference/fill-alpha.ps.argb32.ref.png [new file with mode: 0644]
test/reference/fill-alpha.quartz.ref.png [new file with mode: 0644]
test/reference/fill-alpha.ref.png [new file with mode: 0644]
test/reference/fill-alpha.traps.argb32.ref.png [new file with mode: 0644]
test/reference/fill-alpha.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/fill-and-stroke-alpha-add.base.argb32.ref.png [new file with mode: 0644]
test/reference/fill-and-stroke-alpha-add.base.rgb24.ref.png [new file with mode: 0644]
test/reference/fill-and-stroke-alpha-add.image16.ref.png [new file with mode: 0644]
test/reference/fill-and-stroke-alpha-add.quartz.ref.png [new file with mode: 0644]
test/reference/fill-and-stroke-alpha-add.ref.png [new file with mode: 0644]
test/reference/fill-and-stroke-alpha-add.svg12.xfail.png [new file with mode: 0644]
test/reference/fill-and-stroke-alpha-add.traps.argb32.ref.png [new file with mode: 0644]
test/reference/fill-and-stroke-alpha-add.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/fill-and-stroke-alpha.base.argb32.ref.png [new file with mode: 0644]
test/reference/fill-and-stroke-alpha.base.rgb24.ref.png [new file with mode: 0644]
test/reference/fill-and-stroke-alpha.image16.ref.png [new file with mode: 0644]
test/reference/fill-and-stroke-alpha.ref.png [new file with mode: 0644]
test/reference/fill-and-stroke-alpha.traps.argb32.ref.png [new file with mode: 0644]
test/reference/fill-and-stroke-alpha.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/fill-and-stroke.argb32.ref.png [new file with mode: 0644]
test/reference/fill-and-stroke.base.argb32.ref.png [new file with mode: 0644]
test/reference/fill-and-stroke.base.rgb24.ref.png [new file with mode: 0644]
test/reference/fill-and-stroke.image16.ref.png [new file with mode: 0644]
test/reference/fill-and-stroke.ps.argb32.ref.png [new file with mode: 0644]
test/reference/fill-and-stroke.ps.rgb24.ref.png [new file with mode: 0644]
test/reference/fill-and-stroke.quartz.argb32.ref.png [new file with mode: 0644]
test/reference/fill-and-stroke.quartz.rgb24.ref.png [new file with mode: 0644]
test/reference/fill-and-stroke.rgb24.ref.png [new file with mode: 0644]
test/reference/fill-and-stroke.traps.argb32.ref.png [new file with mode: 0644]
test/reference/fill-and-stroke.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/fill-degenerate-sort-order.argb32.ref.png [new file with mode: 0644]
test/reference/fill-degenerate-sort-order.base.argb32.ref.png [new file with mode: 0644]
test/reference/fill-degenerate-sort-order.base.rgb24.ref.png [new file with mode: 0644]
test/reference/fill-degenerate-sort-order.image16.ref.png [new file with mode: 0644]
test/reference/fill-degenerate-sort-order.ps.argb32.xfail.png [new file with mode: 0644]
test/reference/fill-degenerate-sort-order.ps.rgb24.xfail.png [new file with mode: 0644]
test/reference/fill-degenerate-sort-order.quartz.argb32.ref.png [new file with mode: 0644]
test/reference/fill-degenerate-sort-order.quartz.rgb24.ref.png [new file with mode: 0644]
test/reference/fill-degenerate-sort-order.rgb24.ref.png [new file with mode: 0644]
test/reference/fill-degenerate-sort-order.traps.argb32.ref.png [new file with mode: 0644]
test/reference/fill-degenerate-sort-order.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/fill-empty.argb32.ref.png [new file with mode: 0644]
test/reference/fill-empty.base.argb32.ref.png [new file with mode: 0644]
test/reference/fill-empty.base.rgb24.ref.png [new file with mode: 0644]
test/reference/fill-empty.rgb24.ref.png [new file with mode: 0644]
test/reference/fill-empty.svg12.rgb24.xfail.png [new file with mode: 0644]
test/reference/fill-empty.traps.argb32.ref.png [new file with mode: 0644]
test/reference/fill-empty.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/fill-image.base.argb32.ref.png [new file with mode: 0644]
test/reference/fill-image.base.rgb24.ref.png [new file with mode: 0644]
test/reference/fill-image.image16.ref.png [new file with mode: 0644]
test/reference/fill-image.ps.ref.png [new file with mode: 0644]
test/reference/fill-image.quartz.ref.png [new file with mode: 0644]
test/reference/fill-image.ref.png [new file with mode: 0644]
test/reference/fill-image.traps.argb32.ref.png [new file with mode: 0644]
test/reference/fill-image.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/fill-missed-stop.base.argb32.ref.png [new file with mode: 0644]
test/reference/fill-missed-stop.base.rgb24.ref.png [new file with mode: 0644]
test/reference/fill-missed-stop.pdf.argb32.ref.png [new file with mode: 0644]
test/reference/fill-missed-stop.ps2.argb32.ref.png [new file with mode: 0644]
test/reference/fill-missed-stop.ps2.rgb24.ref.png [new file with mode: 0644]
test/reference/fill-missed-stop.ps3.argb32.ref.png [new file with mode: 0644]
test/reference/fill-missed-stop.ps3.rgb24.ref.png [new file with mode: 0644]
test/reference/fill-missed-stop.ref.png [new file with mode: 0644]
test/reference/fill-missed-stop.rgb24.ref.png [new file with mode: 0644]
test/reference/fill-missed-stop.traps.argb32.ref.png [new file with mode: 0644]
test/reference/fill-missed-stop.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/fill-rule.base.argb32.ref.png [new file with mode: 0644]
test/reference/fill-rule.base.rgb24.ref.png [new file with mode: 0644]
test/reference/fill-rule.image16.ref.png [new file with mode: 0644]
test/reference/fill-rule.ps2.argb32.ref.png [new file with mode: 0644]
test/reference/fill-rule.ps2.rgb24.ref.png [new file with mode: 0644]
test/reference/fill-rule.ps3.argb32.ref.png [new file with mode: 0644]
test/reference/fill-rule.ps3.rgb24.ref.png [new file with mode: 0644]
test/reference/fill-rule.quartz.argb32.ref.png [new file with mode: 0644]
test/reference/fill-rule.quartz.rgb24.ref.png [new file with mode: 0644]
test/reference/fill-rule.ref.png [new file with mode: 0644]
test/reference/fill-rule.rgb24.ref.png [new file with mode: 0644]
test/reference/fill-rule.traps.argb32.ref.png [new file with mode: 0644]
test/reference/fill-rule.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/filter-bilinear-extents.base.argb32.ref.png [new file with mode: 0644]
test/reference/filter-bilinear-extents.base.rgb24.ref.png [new file with mode: 0644]
test/reference/filter-bilinear-extents.image16.ref.png [new file with mode: 0644]
test/reference/filter-bilinear-extents.pdf.xfail.png [new file with mode: 0644]
test/reference/filter-bilinear-extents.ps2.ref.png [new file with mode: 0644]
test/reference/filter-bilinear-extents.ps3.ref.png [new file with mode: 0644]
test/reference/filter-bilinear-extents.quartz.xfail.png [new file with mode: 0644]
test/reference/filter-bilinear-extents.ref.png [new file with mode: 0644]
test/reference/filter-bilinear-extents.traps.argb32.ref.png [new file with mode: 0644]
test/reference/filter-bilinear-extents.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/filter-nearest-offset.base.argb32.ref.png [new file with mode: 0644]
test/reference/filter-nearest-offset.base.rgb24.ref.png [new file with mode: 0644]
test/reference/filter-nearest-offset.gl.xfail.png [new file with mode: 0644]
test/reference/filter-nearest-offset.pdf.xfail.png [new file with mode: 0644]
test/reference/filter-nearest-offset.ps2.ref.png [new file with mode: 0644]
test/reference/filter-nearest-offset.ps3.ref.png [new file with mode: 0644]
test/reference/filter-nearest-offset.ref.png [new file with mode: 0644]
test/reference/filter-nearest-offset.svg.xfail.png [new file with mode: 0644]
test/reference/filter-nearest-offset.traps.argb32.ref.png [new file with mode: 0644]
test/reference/filter-nearest-offset.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/filter-nearest-transformed.base.argb32.ref.png [new file with mode: 0644]
test/reference/filter-nearest-transformed.base.rgb24.ref.png [new file with mode: 0644]
test/reference/filter-nearest-transformed.gl.xfail.png [new file with mode: 0644]
test/reference/filter-nearest-transformed.image16.ref.png [new file with mode: 0644]
test/reference/filter-nearest-transformed.pdf.xfail.png [new file with mode: 0644]
test/reference/filter-nearest-transformed.quartz.xfail.png [new file with mode: 0644]
test/reference/filter-nearest-transformed.ref.png [new file with mode: 0644]
test/reference/filter-nearest-transformed.svg.xfail.png [new file with mode: 0644]
test/reference/filter-nearest-transformed.traps.argb32.ref.png [new file with mode: 0644]
test/reference/filter-nearest-transformed.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/finer-grained-fallbacks.argb32.ref.png [new file with mode: 0644]
test/reference/finer-grained-fallbacks.base.argb32.ref.png [new file with mode: 0644]
test/reference/finer-grained-fallbacks.base.rgb24.ref.png [new file with mode: 0644]
test/reference/finer-grained-fallbacks.gl.argb32.ref.png [new file with mode: 0644]
test/reference/finer-grained-fallbacks.image16.ref.png [new file with mode: 0644]
test/reference/finer-grained-fallbacks.ps2.argb32.ref.png [new file with mode: 0644]
test/reference/finer-grained-fallbacks.ps2.ref.png [new file with mode: 0644]
test/reference/finer-grained-fallbacks.ps2.rgb24.ref.png [new file with mode: 0644]
test/reference/finer-grained-fallbacks.ps3.argb32.ref.png [new file with mode: 0644]
test/reference/finer-grained-fallbacks.ps3.ref.png [new file with mode: 0644]
test/reference/finer-grained-fallbacks.ps3.rgb24.ref.png [new file with mode: 0644]
test/reference/finer-grained-fallbacks.quartz.argb32.ref.png [new file with mode: 0644]
test/reference/finer-grained-fallbacks.quartz.rgb24.ref.png [new file with mode: 0644]
test/reference/finer-grained-fallbacks.rgb24.ref.png [new file with mode: 0644]
test/reference/finer-grained-fallbacks.svg12.argb32.ref.png [new file with mode: 0644]
test/reference/finer-grained-fallbacks.svg12.rgb24.ref.png [new file with mode: 0644]
test/reference/finer-grained-fallbacks.traps.argb32.ref.png [new file with mode: 0644]
test/reference/finer-grained-fallbacks.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/finer-grained-fallbacks.xlib-fallback.ref.png [new file with mode: 0644]
test/reference/font-matrix-translation.base.argb32.ref.png [new file with mode: 0644]
test/reference/font-matrix-translation.base.rgb24.ref.png [new file with mode: 0644]
test/reference/font-matrix-translation.image16.ref.png [new file with mode: 0644]
test/reference/font-matrix-translation.ps2.argb32.ref.png [new file with mode: 0644]
test/reference/font-matrix-translation.ps2.rgb24.ref.png [new file with mode: 0644]
test/reference/font-matrix-translation.ps3.argb32.ref.png [new file with mode: 0644]
test/reference/font-matrix-translation.ps3.rgb24.ref.png [new file with mode: 0644]
test/reference/font-matrix-translation.quartz.ref.png [new file with mode: 0644]
test/reference/font-matrix-translation.ref.png [new file with mode: 0644]
test/reference/font-matrix-translation.svg.ref.png [new file with mode: 0644]
test/reference/font-matrix-translation.traps.argb32.ref.png [new file with mode: 0644]
test/reference/font-matrix-translation.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/ft-show-glyphs-positioning.base.argb32.ref.png [new file with mode: 0644]
test/reference/ft-show-glyphs-positioning.base.rgb24.ref.png [new file with mode: 0644]
test/reference/ft-show-glyphs-positioning.image16.ref.png [new file with mode: 0644]
test/reference/ft-show-glyphs-positioning.pdf.ref.png [new file with mode: 0644]
test/reference/ft-show-glyphs-positioning.ps2.ref.png [new file with mode: 0644]
test/reference/ft-show-glyphs-positioning.ps3.ref.png [new file with mode: 0644]
test/reference/ft-show-glyphs-positioning.ref.png [new file with mode: 0644]
test/reference/ft-show-glyphs-positioning.svg.ref.png [new file with mode: 0644]
test/reference/ft-show-glyphs-positioning.traps.argb32.ref.png [new file with mode: 0644]
test/reference/ft-show-glyphs-positioning.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/ft-show-glyphs-table.base.argb32.ref.png [new file with mode: 0644]
test/reference/ft-show-glyphs-table.base.rgb24.ref.png [new file with mode: 0644]
test/reference/ft-show-glyphs-table.image16.ref.png [new file with mode: 0644]
test/reference/ft-show-glyphs-table.ps2.ref.png [new file with mode: 0644]
test/reference/ft-show-glyphs-table.ps3.ref.png [new file with mode: 0644]
test/reference/ft-show-glyphs-table.quartz.xfail.png [new file with mode: 0644]
test/reference/ft-show-glyphs-table.ref.png [new file with mode: 0644]
test/reference/ft-show-glyphs-table.svg.ref.png [new file with mode: 0644]
test/reference/ft-show-glyphs-table.traps.argb32.ref.png [new file with mode: 0644]
test/reference/ft-show-glyphs-table.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/ft-text-antialias-none.base.argb32.ref.png [new file with mode: 0644]
test/reference/ft-text-antialias-none.base.rgb24.ref.png [new file with mode: 0644]
test/reference/ft-text-antialias-none.ps2.argb32.ref.png [new file with mode: 0644]
test/reference/ft-text-antialias-none.ps3.argb32.ref.png [new file with mode: 0644]
test/reference/ft-text-antialias-none.ref.png [new file with mode: 0644]
test/reference/ft-text-antialias-none.traps.argb32.ref.png [new file with mode: 0644]
test/reference/ft-text-antialias-none.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/ft-text-vertical-layout-type1.base.argb32.ref.png [new file with mode: 0644]
test/reference/ft-text-vertical-layout-type1.base.rgb24.ref.png [new file with mode: 0644]
test/reference/ft-text-vertical-layout-type1.image16.ref.png [new file with mode: 0644]
test/reference/ft-text-vertical-layout-type1.pdf.ref.png [new file with mode: 0644]
test/reference/ft-text-vertical-layout-type1.ps.ref.png [new file with mode: 0644]
test/reference/ft-text-vertical-layout-type1.quartz.xfail.png [new file with mode: 0644]
test/reference/ft-text-vertical-layout-type1.ref.png [new file with mode: 0644]
test/reference/ft-text-vertical-layout-type1.svg.ref.png [new file with mode: 0644]
test/reference/ft-text-vertical-layout-type1.traps.argb32.ref.png [new file with mode: 0644]
test/reference/ft-text-vertical-layout-type1.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/ft-text-vertical-layout-type1.xfail.png [new file with mode: 0644]
test/reference/ft-text-vertical-layout-type3.base.argb32.ref.png [new file with mode: 0644]
test/reference/ft-text-vertical-layout-type3.base.rgb24.ref.png [new file with mode: 0644]
test/reference/ft-text-vertical-layout-type3.image16.ref.png [new file with mode: 0644]
test/reference/ft-text-vertical-layout-type3.pdf.ref.png [new file with mode: 0644]
test/reference/ft-text-vertical-layout-type3.ps.ref.png [new file with mode: 0644]
test/reference/ft-text-vertical-layout-type3.quartz.ref.png [new file with mode: 0644]
test/reference/ft-text-vertical-layout-type3.ref.png [new file with mode: 0644]
test/reference/ft-text-vertical-layout-type3.svg.ref.png [new file with mode: 0644]
test/reference/ft-text-vertical-layout-type3.traps.argb32.ref.png [new file with mode: 0644]
test/reference/ft-text-vertical-layout-type3.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/get-group-target.base.argb32.ref.png [new file with mode: 0644]
test/reference/get-group-target.base.rgb24.ref.png [new file with mode: 0644]
test/reference/get-group-target.ref.png [new file with mode: 0644]
test/reference/get-group-target.traps.argb32.ref.png [new file with mode: 0644]
test/reference/get-group-target.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/gl-surface-source.argb32.ref.png [new file with mode: 0644]
test/reference/gl-surface-source.image16.ref.png [new file with mode: 0644]
test/reference/gl-surface-source.rgb24.ref.png [new file with mode: 0644]
test/reference/glyph-cache-pressure.base.argb32.ref.png [new file with mode: 0644]
test/reference/glyph-cache-pressure.base.rgb24.ref.png [new file with mode: 0644]
test/reference/glyph-cache-pressure.image16.ref.png [new file with mode: 0644]
test/reference/glyph-cache-pressure.ps2.ref.png [new file with mode: 0644]
test/reference/glyph-cache-pressure.ps3.ref.png [new file with mode: 0644]
test/reference/glyph-cache-pressure.quartz.ref.png [new file with mode: 0644]
test/reference/glyph-cache-pressure.ref.png [new file with mode: 0644]
test/reference/glyph-cache-pressure.traps.argb32.ref.png [new file with mode: 0644]
test/reference/glyph-cache-pressure.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/gradient-alpha.base.argb32.ref.png [new file with mode: 0644]
test/reference/gradient-alpha.base.rgb24.ref.png [new file with mode: 0644]
test/reference/gradient-alpha.ps2.argb32.ref.png [new file with mode: 0644]
test/reference/gradient-alpha.ps2.rgb24.ref.png [new file with mode: 0644]
test/reference/gradient-alpha.ps3.argb32.ref.png [new file with mode: 0644]
test/reference/gradient-alpha.ps3.rgb24.ref.png [new file with mode: 0644]
test/reference/gradient-alpha.ref.png [new file with mode: 0644]
test/reference/gradient-alpha.rgb24.ref.png [new file with mode: 0644]
test/reference/gradient-alpha.traps.argb32.ref.png [new file with mode: 0644]
test/reference/gradient-alpha.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/gradient-constant-alpha.base.argb32.ref.png [new file with mode: 0644]
test/reference/gradient-constant-alpha.base.rgb24.ref.png [new file with mode: 0644]
test/reference/gradient-constant-alpha.ps3.ref.png [new file with mode: 0644]
test/reference/gradient-constant-alpha.ps3.rgb24.ref.png [new file with mode: 0644]
test/reference/gradient-constant-alpha.quartz.argb32.ref.png [new file with mode: 0644]
test/reference/gradient-constant-alpha.ref.png [new file with mode: 0644]
test/reference/gradient-constant-alpha.rgb24.ref.png [new file with mode: 0644]
test/reference/gradient-constant-alpha.traps.argb32.ref.png [new file with mode: 0644]
test/reference/gradient-constant-alpha.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/gradient-zero-stops-mask.argb32.ref.png [new file with mode: 0644]
test/reference/gradient-zero-stops-mask.base.argb32.ref.png [new file with mode: 0644]
test/reference/gradient-zero-stops-mask.base.rgb24.ref.png [new file with mode: 0644]
test/reference/gradient-zero-stops-mask.rgb24.ref.png [new file with mode: 0644]
test/reference/gradient-zero-stops-mask.traps.argb32.ref.png [new file with mode: 0644]
test/reference/gradient-zero-stops-mask.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/gradient-zero-stops.base.argb32.ref.png [new file with mode: 0644]
test/reference/gradient-zero-stops.base.rgb24.ref.png [new file with mode: 0644]
test/reference/gradient-zero-stops.ref.png [new file with mode: 0644]
test/reference/gradient-zero-stops.rgb24.ref.png [new file with mode: 0644]
test/reference/gradient-zero-stops.traps.argb32.ref.png [new file with mode: 0644]
test/reference/gradient-zero-stops.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/group-clip.base.argb32.ref.png [new file with mode: 0644]
test/reference/group-clip.base.rgb24.ref.png [new file with mode: 0644]
test/reference/group-clip.image16.ref.png [new file with mode: 0644]
test/reference/group-clip.ref.png [new file with mode: 0644]
test/reference/group-clip.traps.argb32.ref.png [new file with mode: 0644]
test/reference/group-clip.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/group-paint.base.argb32.ref.png [new file with mode: 0644]
test/reference/group-paint.base.rgb24.ref.png [new file with mode: 0644]
test/reference/group-paint.ref.png [new file with mode: 0644]
test/reference/group-paint.traps.argb32.ref.png [new file with mode: 0644]
test/reference/group-paint.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/group-unaligned.base.argb32.ref.png [new file with mode: 0644]
test/reference/group-unaligned.base.rgb24.ref.png [new file with mode: 0644]
test/reference/group-unaligned.image16.ref.png [new file with mode: 0644]
test/reference/group-unaligned.ps.ref.png [new file with mode: 0644]
test/reference/group-unaligned.ps.rgb24.xfail.png [new file with mode: 0644]
test/reference/group-unaligned.quartz.ref.png [new file with mode: 0644]
test/reference/group-unaligned.ref.png [new file with mode: 0644]
test/reference/group-unaligned.svg.argb32.xfail.png [new file with mode: 0644]
test/reference/group-unaligned.svg.rgb24.xfail.png [new file with mode: 0644]
test/reference/group-unaligned.traps.argb32.ref.png [new file with mode: 0644]
test/reference/group-unaligned.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/group-unaligned.xlib-fallback.ref.png [new file with mode: 0644]
test/reference/halo-transform.base.argb32.ref.png [new file with mode: 0644]
test/reference/halo-transform.base.rgb24.ref.png [new file with mode: 0644]
test/reference/halo-transform.image16.ref.png [new file with mode: 0644]
test/reference/halo-transform.ps.ref.png [new file with mode: 0644]
test/reference/halo-transform.quartz.ref.png [new file with mode: 0644]
test/reference/halo-transform.ref.png [new file with mode: 0644]
test/reference/halo-transform.traps.argb32.ref.png [new file with mode: 0644]
test/reference/halo-transform.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/halo.base.argb32.ref.png [new file with mode: 0644]
test/reference/halo.base.rgb24.ref.png [new file with mode: 0644]
test/reference/halo.image16.ref.png [new file with mode: 0644]
test/reference/halo.ps.ref.png [new file with mode: 0644]
test/reference/halo.quartz.ref.png [new file with mode: 0644]
test/reference/halo.ref.png [new file with mode: 0644]
test/reference/halo.traps.argb32.ref.png [new file with mode: 0644]
test/reference/halo.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/hatchings.base.argb32.ref.png [new file with mode: 0644]
test/reference/hatchings.base.rgb24.ref.png [new file with mode: 0644]
test/reference/hatchings.ref.png [new file with mode: 0644]
test/reference/hatchings.traps.argb32.ref.png [new file with mode: 0644]
test/reference/hatchings.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/horizontal-clip.base.argb32.ref.png [new file with mode: 0644]
test/reference/horizontal-clip.base.rgb24.ref.png [new file with mode: 0644]
test/reference/horizontal-clip.ref.png [new file with mode: 0644]
test/reference/horizontal-clip.traps.argb32.ref.png [new file with mode: 0644]
test/reference/horizontal-clip.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/huge-linear.base.argb32.ref.png [new file with mode: 0644]
test/reference/huge-linear.base.rgb24.ref.png [new file with mode: 0644]
test/reference/huge-linear.image16.ref.png [new file with mode: 0644]
test/reference/huge-linear.pdf.ref.png [new file with mode: 0644]
test/reference/huge-linear.ps3.ref.png [new file with mode: 0644]
test/reference/huge-linear.quartz.ref.png [new file with mode: 0644]
test/reference/huge-linear.ref.png [new file with mode: 0644]
test/reference/huge-linear.traps.argb32.ref.png [new file with mode: 0644]
test/reference/huge-linear.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/huge-radial.base.argb32.ref.png [new file with mode: 0644]
test/reference/huge-radial.base.rgb24.ref.png [new file with mode: 0644]
test/reference/huge-radial.image16.ref.png [new file with mode: 0644]
test/reference/huge-radial.pdf.argb32.ref.png [new file with mode: 0644]
test/reference/huge-radial.pdf.rgb24.ref.png [new file with mode: 0644]
test/reference/huge-radial.ps3.ref.png [new file with mode: 0644]
test/reference/huge-radial.quartz.ref.png [new file with mode: 0644]
test/reference/huge-radial.ref.png [new file with mode: 0644]
test/reference/huge-radial.traps.argb32.ref.png [new file with mode: 0644]
test/reference/huge-radial.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/image-bug-710072-aligned.base.argb32.ref.png [new file with mode: 0644]
test/reference/image-bug-710072-aligned.base.rgb24.ref.png [new file with mode: 0644]
test/reference/image-bug-710072-aligned.ref.png [new file with mode: 0644]
test/reference/image-bug-710072-aligned.traps.argb32.ref.png [new file with mode: 0644]
test/reference/image-bug-710072-aligned.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/image-bug-710072-unaligned.base.argb32.ref.png [new file with mode: 0644]
test/reference/image-bug-710072-unaligned.base.rgb24.ref.png [new file with mode: 0644]
test/reference/image-bug-710072-unaligned.ref.png [new file with mode: 0644]
test/reference/image-bug-710072-unaligned.traps.argb32.ref.png [new file with mode: 0644]
test/reference/image-bug-710072-unaligned.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/image-surface-source.argb32.ref.png [new file with mode: 0644]
test/reference/image-surface-source.base.argb32.ref.png [new file with mode: 0644]
test/reference/image-surface-source.base.rgb24.ref.png [new file with mode: 0644]
test/reference/image-surface-source.image16.ref.png [new file with mode: 0644]
test/reference/image-surface-source.ps2.ref.png [new file with mode: 0644]
test/reference/image-surface-source.ps3.ref.png [new file with mode: 0644]
test/reference/image-surface-source.rgb24.ref.png [new file with mode: 0644]
test/reference/image-surface-source.svg12.argb32.xfail.png [new file with mode: 0644]
test/reference/image-surface-source.svg12.rgb24.xfail.png [new file with mode: 0644]
test/reference/image-surface-source.traps.argb32.ref.png [new file with mode: 0644]
test/reference/image-surface-source.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/implicit-close.base.argb32.ref.png [new file with mode: 0644]
test/reference/implicit-close.base.rgb24.ref.png [new file with mode: 0644]
test/reference/implicit-close.ps.ref.png [new file with mode: 0644]
test/reference/implicit-close.ref.png [new file with mode: 0644]
test/reference/implicit-close.traps.argb32.ref.png [new file with mode: 0644]
test/reference/implicit-close.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/infinite-join.base.argb32.ref.png [new file with mode: 0644]
test/reference/infinite-join.base.rgb24.ref.png [new file with mode: 0644]
test/reference/infinite-join.ps2.ref.png [new file with mode: 0644]
test/reference/infinite-join.ps3.ref.png [new file with mode: 0644]
test/reference/infinite-join.ref.png [new file with mode: 0644]
test/reference/infinite-join.traps.argb32.ref.png [new file with mode: 0644]
test/reference/infinite-join.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/inverse-text.base.argb32.ref.png [new file with mode: 0644]
test/reference/inverse-text.base.rgb24.ref.png [new file with mode: 0644]
test/reference/inverse-text.image16.ref.png [new file with mode: 0644]
test/reference/inverse-text.ps.ref.png [new file with mode: 0644]
test/reference/inverse-text.quartz.ref.png [new file with mode: 0644]
test/reference/inverse-text.ref.png [new file with mode: 0644]
test/reference/inverse-text.traps.argb32.ref.png [new file with mode: 0644]
test/reference/inverse-text.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/joins-loop.base.argb32.ref.png [new file with mode: 0644]
test/reference/joins-loop.base.rgb24.ref.png [new file with mode: 0644]
test/reference/joins-loop.ref.png [new file with mode: 0644]
test/reference/joins-loop.traps.argb32.ref.png [new file with mode: 0644]
test/reference/joins-loop.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/joins-retrace.base.argb32.ref.png [new file with mode: 0644]
test/reference/joins-retrace.base.rgb24.ref.png [new file with mode: 0644]
test/reference/joins-retrace.ref.png [new file with mode: 0644]
test/reference/joins-retrace.traps.argb32.ref.png [new file with mode: 0644]
test/reference/joins-retrace.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/joins-star.base.argb32.ref.png [new file with mode: 0644]
test/reference/joins-star.base.rgb24.ref.png [new file with mode: 0644]
test/reference/joins-star.ref.png [new file with mode: 0644]
test/reference/joins-star.traps.argb32.ref.png [new file with mode: 0644]
test/reference/joins-star.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/joins.base.argb32.ref.png [new file with mode: 0644]
test/reference/joins.base.rgb24.ref.png [new file with mode: 0644]
test/reference/joins.image16.ref.png [new file with mode: 0644]
test/reference/joins.ps.ref.png [new file with mode: 0644]
test/reference/joins.quartz.ref.png [new file with mode: 0644]
test/reference/joins.ref.png [new file with mode: 0644]
test/reference/joins.traps.argb32.ref.png [new file with mode: 0644]
test/reference/joins.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/large-clip.base.argb32.ref.png [new file with mode: 0644]
test/reference/large-clip.base.rgb24.ref.png [new file with mode: 0644]
test/reference/large-clip.ref.png [new file with mode: 0644]
test/reference/large-clip.traps.argb32.ref.png [new file with mode: 0644]
test/reference/large-clip.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/large-font.base.argb32.ref.png [new file with mode: 0644]
test/reference/large-font.base.rgb24.ref.png [new file with mode: 0644]
test/reference/large-font.image16.ref.png [new file with mode: 0644]
test/reference/large-font.ref.png [new file with mode: 0644]
test/reference/large-font.traps.argb32.ref.png [new file with mode: 0644]
test/reference/large-font.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/large-source-roi.base.argb32.ref.png [new file with mode: 0644]
test/reference/large-source-roi.base.rgb24.ref.png [new file with mode: 0644]
test/reference/large-source-roi.ref.png [new file with mode: 0644]
test/reference/large-source-roi.traps.argb32.ref.png [new file with mode: 0644]
test/reference/large-source-roi.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/large-source.base.argb32.ref.png [new file with mode: 0644]
test/reference/large-source.base.rgb24.ref.png [new file with mode: 0644]
test/reference/large-source.ref.png [new file with mode: 0644]
test/reference/large-source.traps.argb32.ref.png [new file with mode: 0644]
test/reference/large-source.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/large-twin-antialias-mixed.base.argb32.ref.png [new file with mode: 0644]
test/reference/large-twin-antialias-mixed.base.rgb24.ref.png [new file with mode: 0644]
test/reference/large-twin-antialias-mixed.image16.ref.png [new file with mode: 0644]
test/reference/large-twin-antialias-mixed.ref.png [new file with mode: 0644]
test/reference/large-twin-antialias-mixed.traps.argb32.ref.png [new file with mode: 0644]
test/reference/large-twin-antialias-mixed.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/leaky-dash.base.argb32.ref.png [new file with mode: 0644]
test/reference/leaky-dash.base.rgb24.ref.png [new file with mode: 0644]
test/reference/leaky-dash.ps2.argb32.ref.png [new file with mode: 0644]
test/reference/leaky-dash.ps2.rgb24.ref.png [new file with mode: 0644]
test/reference/leaky-dash.ps3.argb32.ref.png [new file with mode: 0644]
test/reference/leaky-dash.ps3.rgb24.ref.png [new file with mode: 0644]
test/reference/leaky-dash.quartz.ref.png [new file with mode: 0644]
test/reference/leaky-dash.ref.png [new file with mode: 0644]
test/reference/leaky-dash.traps.argb32.ref.png [new file with mode: 0644]
test/reference/leaky-dash.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/leaky-dashed-rectangle.base.argb32.ref.png [new file with mode: 0644]
test/reference/leaky-dashed-rectangle.base.rgb24.ref.png [new file with mode: 0644]
test/reference/leaky-dashed-rectangle.image16.ref.png [new file with mode: 0644]
test/reference/leaky-dashed-rectangle.pdf.ref.png [new file with mode: 0644]
test/reference/leaky-dashed-rectangle.ps.ref.png [new file with mode: 0644]
test/reference/leaky-dashed-rectangle.quartz.ref.png [new file with mode: 0644]
test/reference/leaky-dashed-rectangle.ref.png [new file with mode: 0644]
test/reference/leaky-dashed-rectangle.traps.argb32.ref.png [new file with mode: 0644]
test/reference/leaky-dashed-rectangle.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/leaky-dashed-stroke.base.argb32.ref.png [new file with mode: 0644]
test/reference/leaky-dashed-stroke.base.rgb24.ref.png [new file with mode: 0644]
test/reference/leaky-dashed-stroke.image16.ref.png [new file with mode: 0644]
test/reference/leaky-dashed-stroke.ps.ref.png [new file with mode: 0644]
test/reference/leaky-dashed-stroke.quartz.ref.png [new file with mode: 0644]
test/reference/leaky-dashed-stroke.ref.png [new file with mode: 0644]
test/reference/leaky-dashed-stroke.traps.argb32.ref.png [new file with mode: 0644]
test/reference/leaky-dashed-stroke.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/leaky-polygon.base.argb32.ref.png [new file with mode: 0644]
test/reference/leaky-polygon.base.rgb24.ref.png [new file with mode: 0644]
test/reference/leaky-polygon.image16.ref.png [new file with mode: 0644]
test/reference/leaky-polygon.ps.ref.png [new file with mode: 0644]
test/reference/leaky-polygon.ref.png [new file with mode: 0644]
test/reference/leaky-polygon.traps.argb32.ref.png [new file with mode: 0644]
test/reference/leaky-polygon.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/line-width-large-overlap-dashed.ref.png [new file with mode: 0644]
test/reference/line-width-large-overlap-dashed.xfail.png [new file with mode: 0644]
test/reference/line-width-large-overlap-flipped.ref.png [new file with mode: 0644]
test/reference/line-width-large-overlap-flopped.ref.png [new file with mode: 0644]
test/reference/line-width-large-overlap-offset.ref.png [new file with mode: 0644]
test/reference/line-width-large-overlap-rotated.base.ref.png [new file with mode: 0644]
test/reference/line-width-large-overlap-rotated.ref.png [new file with mode: 0644]
test/reference/line-width-large-overlap-rotated.traps.ref.png [new file with mode: 0644]
test/reference/line-width-large-overlap.ref.png [new file with mode: 0644]
test/reference/line-width-overlap-dashed.base.argb32.ref.png [new file with mode: 0644]
test/reference/line-width-overlap-dashed.base.rgb24.ref.png [new file with mode: 0644]
test/reference/line-width-overlap-dashed.traps.argb32.ref.png [new file with mode: 0644]
test/reference/line-width-overlap-dashed.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/line-width-overlap-flipped.base.argb32.ref.png [new file with mode: 0644]
test/reference/line-width-overlap-flipped.base.rgb24.ref.png [new file with mode: 0644]
test/reference/line-width-overlap-flipped.ref.png [new file with mode: 0644]
test/reference/line-width-overlap-flipped.traps.argb32.ref.png [new file with mode: 0644]
test/reference/line-width-overlap-flipped.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/line-width-overlap-flopped.base.argb32.ref.png [new file with mode: 0644]
test/reference/line-width-overlap-flopped.base.rgb24.ref.png [new file with mode: 0644]
test/reference/line-width-overlap-flopped.ref.png [new file with mode: 0644]
test/reference/line-width-overlap-flopped.traps.argb32.ref.png [new file with mode: 0644]
test/reference/line-width-overlap-flopped.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/line-width-overlap-offset.base.argb32.ref.png [new file with mode: 0644]
test/reference/line-width-overlap-offset.base.rgb24.ref.png [new file with mode: 0644]
test/reference/line-width-overlap-offset.ref.png [new file with mode: 0644]
test/reference/line-width-overlap-offset.traps.ref.png [new file with mode: 0644]
test/reference/line-width-overlap-rotated.base.argb32.ref.png [new file with mode: 0644]
test/reference/line-width-overlap-rotated.base.rgb24.ref.png [new file with mode: 0644]
test/reference/line-width-overlap-rotated.ref.png [new file with mode: 0644]
test/reference/line-width-overlap-rotated.traps.argb32.ref.png [new file with mode: 0644]
test/reference/line-width-overlap-rotated.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/line-width-overlap.base.argb32.ref.png [new file with mode: 0644]
test/reference/line-width-overlap.base.rgb24.ref.png [new file with mode: 0644]
test/reference/line-width-overlap.ref.png [new file with mode: 0644]
test/reference/line-width-overlap.traps.argb32.ref.png [new file with mode: 0644]
test/reference/line-width-overlap.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/line-width-scale.base.argb32.ref.png [new file with mode: 0644]
test/reference/line-width-scale.base.rgb24.ref.png [new file with mode: 0644]
test/reference/line-width-scale.image16.ref.png [new file with mode: 0644]
test/reference/line-width-scale.ps2.ref.png [new file with mode: 0644]
test/reference/line-width-scale.ps3.ref.png [new file with mode: 0644]
test/reference/line-width-scale.quartz.ref.png [new file with mode: 0644]
test/reference/line-width-scale.ref.png [new file with mode: 0644]
test/reference/line-width-scale.traps.argb32.ref.png [new file with mode: 0644]
test/reference/line-width-scale.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/line-width-tolerance.base.argb32.ref.png [new file with mode: 0644]
test/reference/line-width-tolerance.base.rgb24.ref.png [new file with mode: 0644]
test/reference/line-width-tolerance.ref.png [new file with mode: 0644]
test/reference/line-width-tolerance.traps.argb32.ref.png [new file with mode: 0644]
test/reference/line-width-tolerance.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/line-width.base.argb32.ref.png [new file with mode: 0644]
test/reference/line-width.base.rgb24.ref.png [new file with mode: 0644]
test/reference/line-width.ref.png [new file with mode: 0644]
test/reference/line-width.traps.argb32.ref.png [new file with mode: 0644]
test/reference/line-width.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/linear-gradient-extend.base.argb32.ref.png [new file with mode: 0644]
test/reference/linear-gradient-extend.base.rgb24.ref.png [new file with mode: 0644]
test/reference/linear-gradient-extend.ref.png [new file with mode: 0644]
test/reference/linear-gradient-extend.traps.argb32.ref.png [new file with mode: 0644]
test/reference/linear-gradient-extend.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/linear-gradient-large.base.argb32.ref.png [new file with mode: 0644]
test/reference/linear-gradient-large.base.rgb24.ref.png [new file with mode: 0644]
test/reference/linear-gradient-large.quartz.ref.png [new file with mode: 0644]
test/reference/linear-gradient-large.ref.png [new file with mode: 0644]
test/reference/linear-gradient-large.traps.argb32.ref.png [new file with mode: 0644]
test/reference/linear-gradient-large.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/linear-gradient-one-stop.argb32.ref.png [new file with mode: 0644]
test/reference/linear-gradient-one-stop.base.argb32.ref.png [new file with mode: 0644]
test/reference/linear-gradient-one-stop.base.rgb24.ref.png [new file with mode: 0644]
test/reference/linear-gradient-one-stop.rgb24.ref.png [new file with mode: 0644]
test/reference/linear-gradient-one-stop.traps.argb32.ref.png [new file with mode: 0644]
test/reference/linear-gradient-one-stop.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/linear-gradient-reflect.base.argb32.ref.png [new file with mode: 0644]
test/reference/linear-gradient-reflect.base.rgb24.ref.png [new file with mode: 0644]
test/reference/linear-gradient-reflect.image16.ref.png [new file with mode: 0644]
test/reference/linear-gradient-reflect.pdf.argb32.ref.png [new file with mode: 0644]
test/reference/linear-gradient-reflect.pdf.rgb24.ref.png [new file with mode: 0644]
test/reference/linear-gradient-reflect.ps3.ref.png [new file with mode: 0644]
test/reference/linear-gradient-reflect.quartz.ref.png [new file with mode: 0644]
test/reference/linear-gradient-reflect.ref.png [new file with mode: 0644]
test/reference/linear-gradient-reflect.traps.argb32.ref.png [new file with mode: 0644]
test/reference/linear-gradient-reflect.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/linear-gradient-subset.base.argb32.ref.png [new file with mode: 0644]
test/reference/linear-gradient-subset.base.rgb24.ref.png [new file with mode: 0644]
test/reference/linear-gradient-subset.image16.ref.png [new file with mode: 0644]
test/reference/linear-gradient-subset.ps3.ref.png [new file with mode: 0644]
test/reference/linear-gradient-subset.quartz.ref.png [new file with mode: 0644]
test/reference/linear-gradient-subset.ref.png [new file with mode: 0644]
test/reference/linear-gradient-subset.traps.argb32.ref.png [new file with mode: 0644]
test/reference/linear-gradient-subset.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/linear-gradient.base.argb32.ref.png [new file with mode: 0644]
test/reference/linear-gradient.base.rgb24.ref.png [new file with mode: 0644]
test/reference/linear-gradient.image16.ref.png [new file with mode: 0644]
test/reference/linear-gradient.ps3.ref.png [new file with mode: 0644]
test/reference/linear-gradient.quartz.ref.png [new file with mode: 0644]
test/reference/linear-gradient.ref.png [new file with mode: 0644]
test/reference/linear-gradient.traps.argb32.ref.png [new file with mode: 0644]
test/reference/linear-gradient.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/linear-step-function.base.argb32.ref.png [new file with mode: 0644]
test/reference/linear-step-function.base.rgb24.ref.png [new file with mode: 0644]
test/reference/linear-step-function.traps.argb32.ref.png [new file with mode: 0644]
test/reference/linear-step-function.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/linear-step-function.xfail.png [new file with mode: 0644]
test/reference/linear-uniform.base.argb32.ref.png [new file with mode: 0644]
test/reference/linear-uniform.base.rgb24.ref.png [new file with mode: 0644]
test/reference/linear-uniform.image16.ref.png [new file with mode: 0644]
test/reference/linear-uniform.ref.png [new file with mode: 0644]
test/reference/linear-uniform.traps.argb32.ref.png [new file with mode: 0644]
test/reference/linear-uniform.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/long-dashed-lines.base.argb32.ref.png [new file with mode: 0644]
test/reference/long-dashed-lines.base.rgb24.ref.png [new file with mode: 0644]
test/reference/long-dashed-lines.image16.ref.png [new file with mode: 0644]
test/reference/long-dashed-lines.ps2.ref.png [new file with mode: 0644]
test/reference/long-dashed-lines.ps3.ref.png [new file with mode: 0644]
test/reference/long-dashed-lines.quartz.ref.png [new file with mode: 0644]
test/reference/long-dashed-lines.ref.png [new file with mode: 0644]
test/reference/long-dashed-lines.traps.argb32.ref.png [new file with mode: 0644]
test/reference/long-dashed-lines.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/long-lines.base.argb32.ref.png [new file with mode: 0644]
test/reference/long-lines.base.rgb24.ref.png [new file with mode: 0644]
test/reference/long-lines.traps.argb32.ref.png [new file with mode: 0644]
test/reference/long-lines.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/map-all-to-image.base.argb32.ref.png [new file with mode: 0644]
test/reference/map-all-to-image.base.rgb24.ref.png [new file with mode: 0644]
test/reference/map-all-to-image.ref.png [new file with mode: 0644]
test/reference/map-all-to-image.traps.argb32.ref.png [new file with mode: 0644]
test/reference/map-all-to-image.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/map-bit-to-image.base.argb32.ref.png [new file with mode: 0644]
test/reference/map-bit-to-image.base.rgb24.ref.png [new file with mode: 0644]
test/reference/map-bit-to-image.ref.png [new file with mode: 0644]
test/reference/map-bit-to-image.traps.argb32.ref.png [new file with mode: 0644]
test/reference/map-bit-to-image.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/map-to-image-fill.base.argb32.ref.png [new file with mode: 0644]
test/reference/map-to-image-fill.base.rgb24.ref.png [new file with mode: 0644]
test/reference/map-to-image-fill.ref.png [new file with mode: 0644]
test/reference/map-to-image-fill.traps.argb32.ref.png [new file with mode: 0644]
test/reference/map-to-image-fill.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/mask-alpha.argb32.ref.png [new file with mode: 0644]
test/reference/mask-alpha.base.argb32.ref.png [new file with mode: 0644]
test/reference/mask-alpha.base.rgb24.ref.png [new file with mode: 0644]
test/reference/mask-alpha.image16.ref.png [new file with mode: 0644]
test/reference/mask-alpha.ps.ref.png [new file with mode: 0644]
test/reference/mask-alpha.quartz.argb32.ref.png [new file with mode: 0644]
test/reference/mask-alpha.rgb24.ref.png [new file with mode: 0644]
test/reference/mask-alpha.svg.rgb24.xfail.png [new file with mode: 0644]
test/reference/mask-alpha.traps.argb32.ref.png [new file with mode: 0644]
test/reference/mask-alpha.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/mask-ctm.base.argb32.ref.png [new file with mode: 0644]
test/reference/mask-ctm.base.rgb24.ref.png [new file with mode: 0644]
test/reference/mask-ctm.ref.png [new file with mode: 0644]
test/reference/mask-ctm.rgb24.ref.png [new file with mode: 0644]
test/reference/mask-ctm.traps.argb32.ref.png [new file with mode: 0644]
test/reference/mask-ctm.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/mask-glyphs.gl.ref.png [new file with mode: 0644]
test/reference/mask-glyphs.image16.ref.png [new file with mode: 0644]
test/reference/mask-glyphs.pdf.ref.png [new file with mode: 0644]
test/reference/mask-glyphs.ref.png [new file with mode: 0644]
test/reference/mask-glyphs.svg.ref.png [new file with mode: 0644]
test/reference/mask-surface-ctm.base.argb32.ref.png [new file with mode: 0644]
test/reference/mask-surface-ctm.base.rgb24.ref.png [new file with mode: 0644]
test/reference/mask-surface-ctm.ref.png [new file with mode: 0644]
test/reference/mask-surface-ctm.rgb24.ref.png [new file with mode: 0644]
test/reference/mask-surface-ctm.traps.argb32.ref.png [new file with mode: 0644]
test/reference/mask-surface-ctm.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/mask-transformed-image.base.argb32.ref.png [new file with mode: 0644]
test/reference/mask-transformed-image.base.rgb24.ref.png [new file with mode: 0644]
test/reference/mask-transformed-image.image16.ref.png [new file with mode: 0644]
test/reference/mask-transformed-image.pdf.ref.png [new file with mode: 0644]
test/reference/mask-transformed-image.quartz.ref.png [new file with mode: 0644]
test/reference/mask-transformed-image.ref.png [new file with mode: 0644]
test/reference/mask-transformed-image.traps.argb32.ref.png [new file with mode: 0644]
test/reference/mask-transformed-image.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/mask-transformed-similar.base.argb32.ref.png [new file with mode: 0644]
test/reference/mask-transformed-similar.base.rgb24.ref.png [new file with mode: 0644]
test/reference/mask-transformed-similar.image16.ref.png [new file with mode: 0644]
test/reference/mask-transformed-similar.pdf.ref.png [new file with mode: 0644]
test/reference/mask-transformed-similar.quartz.ref.png [new file with mode: 0644]
test/reference/mask-transformed-similar.ref.png [new file with mode: 0644]
test/reference/mask-transformed-similar.svg.ref.png [new file with mode: 0644]
test/reference/mask-transformed-similar.traps.argb32.ref.png [new file with mode: 0644]
test/reference/mask-transformed-similar.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/mask.argb32.ref.png [new file with mode: 0644]
test/reference/mask.base.argb32.ref.png [new file with mode: 0644]
test/reference/mask.base.rgb24.ref.png [new file with mode: 0644]
test/reference/mask.image16.ref.png [new file with mode: 0644]
test/reference/mask.pdf.argb32.ref.png [new file with mode: 0644]
test/reference/mask.pdf.rgb24.ref.png [new file with mode: 0644]
test/reference/mask.quartz.argb32.ref.png [new file with mode: 0644]
test/reference/mask.quartz.rgb24.ref.png [new file with mode: 0644]
test/reference/mask.rgb24.ref.png [new file with mode: 0644]
test/reference/mask.svg.argb32.xfail.png [new file with mode: 0644]
test/reference/mask.svg.rgb24.xfail.png [new file with mode: 0644]
test/reference/mask.traps.argb32.ref.png [new file with mode: 0644]
test/reference/mask.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/mesh-pattern-accuracy.base.argb32.ref.png [new file with mode: 0644]
test/reference/mesh-pattern-accuracy.base.rgb24.ref.png [new file with mode: 0644]
test/reference/mesh-pattern-accuracy.image16.ref.png [new file with mode: 0644]
test/reference/mesh-pattern-accuracy.ref.png [new file with mode: 0644]
test/reference/mesh-pattern-accuracy.traps.argb32.ref.png [new file with mode: 0644]
test/reference/mesh-pattern-accuracy.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/mesh-pattern-conical.base.argb32.ref.png [new file with mode: 0644]
test/reference/mesh-pattern-conical.base.rgb24.ref.png [new file with mode: 0644]
test/reference/mesh-pattern-conical.image16.ref.png [new file with mode: 0644]
test/reference/mesh-pattern-conical.ref.png [new file with mode: 0644]
test/reference/mesh-pattern-conical.traps.argb32.ref.png [new file with mode: 0644]
test/reference/mesh-pattern-conical.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/mesh-pattern-control-points.base.argb32.ref.png [new file with mode: 0644]
test/reference/mesh-pattern-control-points.base.rgb24.ref.png [new file with mode: 0644]
test/reference/mesh-pattern-control-points.image16.ref.png [new file with mode: 0644]
test/reference/mesh-pattern-control-points.ref.png [new file with mode: 0644]
test/reference/mesh-pattern-control-points.traps.argb32.ref.png [new file with mode: 0644]
test/reference/mesh-pattern-control-points.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/mesh-pattern-fold.base.argb32.ref.png [new file with mode: 0644]
test/reference/mesh-pattern-fold.base.rgb24.ref.png [new file with mode: 0644]
test/reference/mesh-pattern-fold.image16.ref.png [new file with mode: 0644]
test/reference/mesh-pattern-fold.ref.png [new file with mode: 0644]
test/reference/mesh-pattern-fold.traps.argb32.ref.png [new file with mode: 0644]
test/reference/mesh-pattern-fold.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/mesh-pattern-overlap.base.argb32.ref.png [new file with mode: 0644]
test/reference/mesh-pattern-overlap.base.rgb24.ref.png [new file with mode: 0644]
test/reference/mesh-pattern-overlap.image16.ref.png [new file with mode: 0644]
test/reference/mesh-pattern-overlap.ref.png [new file with mode: 0644]
test/reference/mesh-pattern-overlap.traps.argb32.ref.png [new file with mode: 0644]
test/reference/mesh-pattern-overlap.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/mesh-pattern-transformed.base.argb32.ref.png [new file with mode: 0644]
test/reference/mesh-pattern-transformed.base.rgb24.ref.png [new file with mode: 0644]
test/reference/mesh-pattern-transformed.image16.ref.png [new file with mode: 0644]
test/reference/mesh-pattern-transformed.ref.png [new file with mode: 0644]
test/reference/mesh-pattern-transformed.traps.argb32.ref.png [new file with mode: 0644]
test/reference/mesh-pattern-transformed.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/mesh-pattern.base.argb32.ref.png [new file with mode: 0644]
test/reference/mesh-pattern.base.rgb24.ref.png [new file with mode: 0644]
test/reference/mesh-pattern.image16.ref.png [new file with mode: 0644]
test/reference/mesh-pattern.ref.png [new file with mode: 0644]
test/reference/mesh-pattern.traps.argb32.ref.png [new file with mode: 0644]
test/reference/mesh-pattern.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/mime-data.base.argb32.ref.png [new file with mode: 0644]
test/reference/mime-data.base.rgb24.ref.png [new file with mode: 0644]
test/reference/mime-data.pdf.ref.png [new file with mode: 0644]
test/reference/mime-data.ps.ref.png [new file with mode: 0644]
test/reference/mime-data.ref.png [new file with mode: 0644]
test/reference/mime-data.script.ref.png [new file with mode: 0644]
test/reference/mime-data.svg.ref.png [new file with mode: 0644]
test/reference/mime-data.traps.argb32.ref.png [new file with mode: 0644]
test/reference/mime-data.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/mime-surface.base.argb32.ref.png [new file with mode: 0644]
test/reference/mime-surface.base.rgb24.ref.png [new file with mode: 0644]
test/reference/mime-surface.ref.png [new file with mode: 0644]
test/reference/mime-surface.traps.argb32.ref.png [new file with mode: 0644]
test/reference/mime-surface.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/miter-precision.base.argb32.ref.png [new file with mode: 0644]
test/reference/miter-precision.base.rgb24.ref.png [new file with mode: 0644]
test/reference/miter-precision.ps2.ref.png [new file with mode: 0644]
test/reference/miter-precision.ps3.ref.png [new file with mode: 0644]
test/reference/miter-precision.ref.png [new file with mode: 0644]
test/reference/miter-precision.traps.argb32.ref.png [new file with mode: 0644]
test/reference/miter-precision.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/move-to-show-surface.base.argb32.ref.png [new file with mode: 0644]
test/reference/move-to-show-surface.base.rgb24.ref.png [new file with mode: 0644]
test/reference/move-to-show-surface.ref.png [new file with mode: 0644]
test/reference/move-to-show-surface.traps.argb32.ref.png [new file with mode: 0644]
test/reference/move-to-show-surface.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/new-sub-path.argb32.ref.png [new file with mode: 0644]
test/reference/new-sub-path.base.argb32.ref.png [new file with mode: 0644]
test/reference/new-sub-path.base.rgb24.ref.png [new file with mode: 0644]
test/reference/new-sub-path.pdf.argb32.ref.png [new file with mode: 0644]
test/reference/new-sub-path.ps2.argb32.ref.png [new file with mode: 0644]
test/reference/new-sub-path.ps2.rgb24.ref.png [new file with mode: 0644]
test/reference/new-sub-path.ps3.argb32.ref.png [new file with mode: 0644]
test/reference/new-sub-path.ps3.rgb24.ref.png [new file with mode: 0644]
test/reference/new-sub-path.quartz.ref.png [new file with mode: 0644]
test/reference/new-sub-path.rgb24.ref.png [new file with mode: 0644]
test/reference/new-sub-path.traps.argb32.ref.png [new file with mode: 0644]
test/reference/new-sub-path.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/nil-surface.base.argb32.ref.png [new file with mode: 0644]
test/reference/nil-surface.base.rgb24.ref.png [new file with mode: 0644]
test/reference/nil-surface.ref.png [new file with mode: 0644]
test/reference/nil-surface.rgb24.ref.png [new file with mode: 0644]
test/reference/nil-surface.traps.argb32.ref.png [new file with mode: 0644]
test/reference/nil-surface.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/operator-alpha-alpha.base.argb32.ref.png [new file with mode: 0644]
test/reference/operator-alpha-alpha.base.rgb24.ref.png [new file with mode: 0644]
test/reference/operator-alpha-alpha.image16.ref.png [new file with mode: 0644]
test/reference/operator-alpha-alpha.pdf.xfail.png [new file with mode: 0644]
test/reference/operator-alpha-alpha.ps.xfail.png [new file with mode: 0644]
test/reference/operator-alpha-alpha.ref.png [new file with mode: 0644]
test/reference/operator-alpha-alpha.svg.xfail.png [new file with mode: 0644]
test/reference/operator-alpha-alpha.traps.argb32.ref.png [new file with mode: 0644]
test/reference/operator-alpha-alpha.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/operator-alpha.base.argb32.ref.png [new file with mode: 0644]
test/reference/operator-alpha.base.rgb24.ref.png [new file with mode: 0644]
test/reference/operator-alpha.ref.png [new file with mode: 0644]
test/reference/operator-alpha.rgb24.ref.png [new file with mode: 0644]
test/reference/operator-alpha.svg12.argb32.xfail.png [new file with mode: 0644]
test/reference/operator-alpha.svg12.rgb24.xfail.png [new file with mode: 0644]
test/reference/operator-alpha.traps.argb32.ref.png [new file with mode: 0644]
test/reference/operator-alpha.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/operator-clear.argb32.ref.png [new file with mode: 0644]
test/reference/operator-clear.base.argb32.ref.png [new file with mode: 0644]
test/reference/operator-clear.base.rgb24.ref.png [new file with mode: 0644]
test/reference/operator-clear.ps2.argb32.ref.png [new file with mode: 0644]
test/reference/operator-clear.ps3.argb32.ref.png [new file with mode: 0644]
test/reference/operator-clear.quartz.argb32.ref.png [new file with mode: 0644]
test/reference/operator-clear.quartz.rgb24.ref.png [new file with mode: 0644]
test/reference/operator-clear.rgb24.ref.png [new file with mode: 0644]
test/reference/operator-clear.svg12.argb32.xfail.png [new file with mode: 0644]
test/reference/operator-clear.svg12.rgb24.xfail.png [new file with mode: 0644]
test/reference/operator-clear.traps.argb32.ref.png [new file with mode: 0644]
test/reference/operator-clear.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/operator-source.argb32.ref.png [new file with mode: 0644]
test/reference/operator-source.base.argb32.ref.png [new file with mode: 0644]
test/reference/operator-source.base.rgb24.ref.png [new file with mode: 0644]
test/reference/operator-source.image16.ref.png [new file with mode: 0644]
test/reference/operator-source.rgb24.ref.png [new file with mode: 0644]
test/reference/operator-source.traps.argb32.ref.png [new file with mode: 0644]
test/reference/operator-source.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/operator-source.xlib-fallback.ref.png [new file with mode: 0644]
test/reference/operator.base.argb32.ref.png [new file with mode: 0644]
test/reference/operator.base.rgb24.ref.png [new file with mode: 0644]
test/reference/operator.ref.png [new file with mode: 0644]
test/reference/operator.rgb24.ref.png [new file with mode: 0644]
test/reference/operator.svg12.argb32.xfail.png [new file with mode: 0644]
test/reference/operator.svg12.rgb24.xfail.png [new file with mode: 0644]
test/reference/operator.traps.argb32.ref.png [new file with mode: 0644]
test/reference/operator.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/over-above-source.base.argb32.ref.png [new file with mode: 0644]
test/reference/over-above-source.base.rgb24.ref.png [new file with mode: 0644]
test/reference/over-above-source.ps2.argb32.ref.png [new file with mode: 0644]
test/reference/over-above-source.ps3.argb32.ref.png [new file with mode: 0644]
test/reference/over-above-source.quartz.argb32.ref.png [new file with mode: 0644]
test/reference/over-above-source.quartz.rgb24.ref.png [new file with mode: 0644]
test/reference/over-above-source.ref.png [new file with mode: 0644]
test/reference/over-above-source.rgb24.ref.png [new file with mode: 0644]
test/reference/over-above-source.svg12.rgb24.xfail.png [new file with mode: 0644]
test/reference/over-above-source.traps.argb32.ref.png [new file with mode: 0644]
test/reference/over-above-source.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/over-around-source.base.argb32.ref.png [new file with mode: 0644]
test/reference/over-around-source.base.rgb24.ref.png [new file with mode: 0644]
test/reference/over-around-source.image16.ref.png [new file with mode: 0644]
test/reference/over-around-source.pdf.argb32.ref.png [new file with mode: 0644]
test/reference/over-around-source.ps2.argb32.ref.png [new file with mode: 0644]
test/reference/over-around-source.ps2.rgb24.ref.png [new file with mode: 0644]
test/reference/over-around-source.ps3.argb32.ref.png [new file with mode: 0644]
test/reference/over-around-source.ps3.rgb24.ref.png [new file with mode: 0644]
test/reference/over-around-source.quartz.argb32.ref.png [new file with mode: 0644]
test/reference/over-around-source.ref.png [new file with mode: 0644]
test/reference/over-around-source.rgb24.ref.png [new file with mode: 0644]
test/reference/over-around-source.svg12.argb32.xfail.png [new file with mode: 0644]
test/reference/over-around-source.svg12.rgb24.xfail.png [new file with mode: 0644]
test/reference/over-around-source.traps.argb32.ref.png [new file with mode: 0644]
test/reference/over-around-source.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/over-below-source.base.argb32.ref.png [new file with mode: 0644]
test/reference/over-below-source.base.rgb24.ref.png [new file with mode: 0644]
test/reference/over-below-source.pdf.argb32.ref.png [new file with mode: 0644]
test/reference/over-below-source.ps2.argb32.ref.png [new file with mode: 0644]
test/reference/over-below-source.ps2.rgb24.ref.png [new file with mode: 0644]
test/reference/over-below-source.ps3.argb32.ref.png [new file with mode: 0644]
test/reference/over-below-source.ps3.rgb24.ref.png [new file with mode: 0644]
test/reference/over-below-source.ref.png [new file with mode: 0644]
test/reference/over-below-source.rgb24.ref.png [new file with mode: 0644]
test/reference/over-below-source.svg12.argb32.xfail.png [new file with mode: 0644]
test/reference/over-below-source.svg12.rgb24.xfail.png [new file with mode: 0644]
test/reference/over-below-source.traps.argb32.ref.png [new file with mode: 0644]
test/reference/over-below-source.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/over-between-source.base.argb32.ref.png [new file with mode: 0644]
test/reference/over-between-source.base.rgb24.ref.png [new file with mode: 0644]
test/reference/over-between-source.ps2.argb32.ref.png [new file with mode: 0644]
test/reference/over-between-source.ps3.argb32.ref.png [new file with mode: 0644]
test/reference/over-between-source.quartz.argb32.ref.png [new file with mode: 0644]
test/reference/over-between-source.ref.png [new file with mode: 0644]
test/reference/over-between-source.rgb24.ref.png [new file with mode: 0644]
test/reference/over-between-source.svg12.argb32.xfail.png [new file with mode: 0644]
test/reference/over-between-source.svg12.rgb24.xfail.png [new file with mode: 0644]
test/reference/over-between-source.traps.argb32.ref.png [new file with mode: 0644]
test/reference/over-between-source.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/overlapping-boxes.argb32.ref.png [new file with mode: 0644]
test/reference/overlapping-boxes.base.argb32.ref.png [new file with mode: 0644]
test/reference/overlapping-boxes.base.rgb24.ref.png [new file with mode: 0644]
test/reference/overlapping-boxes.rgb24.ref.png [new file with mode: 0644]
test/reference/overlapping-boxes.traps.argb32.ref.png [new file with mode: 0644]
test/reference/overlapping-boxes.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/overlapping-dash-caps.base.argb32.ref.png [new file with mode: 0644]
test/reference/overlapping-dash-caps.base.rgb24.ref.png [new file with mode: 0644]
test/reference/overlapping-dash-caps.ref.png [new file with mode: 0644]
test/reference/overlapping-dash-caps.traps.argb32.ref.png [new file with mode: 0644]
test/reference/overlapping-dash-caps.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/overlapping-glyphs.argb32.ref.png [new file with mode: 0644]
test/reference/overlapping-glyphs.base.argb32.ref.png [new file with mode: 0644]
test/reference/overlapping-glyphs.base.rgb24.ref.png [new file with mode: 0644]
test/reference/overlapping-glyphs.pdf.argb32.xfail.png [new file with mode: 0644]
test/reference/overlapping-glyphs.pdf.rgb24.xfail.png [new file with mode: 0644]
test/reference/overlapping-glyphs.quartz.argb32.ref.png [new file with mode: 0644]
test/reference/overlapping-glyphs.quartz.rgb24.ref.png [new file with mode: 0644]
test/reference/overlapping-glyphs.rgb24.ref.png [new file with mode: 0644]
test/reference/overlapping-glyphs.svg.argb32.ref.png [new file with mode: 0644]
test/reference/overlapping-glyphs.svg.rgb24.ref.png [new file with mode: 0644]
test/reference/overlapping-glyphs.traps.argb32.ref.png [new file with mode: 0644]
test/reference/overlapping-glyphs.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/paint-clip-fill-aa.base.argb32.ref.png [new file with mode: 0644]
test/reference/paint-clip-fill-aa.base.rgb24.ref.png [new file with mode: 0644]
test/reference/paint-clip-fill-aa.ref.png [new file with mode: 0644]
test/reference/paint-clip-fill-aa.traps.argb32.ref.png [new file with mode: 0644]
test/reference/paint-clip-fill-aa.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/paint-clip-fill-mono.base.argb32.ref.png [new file with mode: 0644]
test/reference/paint-clip-fill-mono.base.rgb24.ref.png [new file with mode: 0644]
test/reference/paint-clip-fill-mono.ref.png [new file with mode: 0644]
test/reference/paint-clip-fill-mono.traps.argb32.ref.png [new file with mode: 0644]
test/reference/paint-clip-fill-mono.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/paint-repeat.base.argb32.ref.png [new file with mode: 0644]
test/reference/paint-repeat.base.rgb24.ref.png [new file with mode: 0644]
test/reference/paint-repeat.ref.png [new file with mode: 0644]
test/reference/paint-repeat.traps.argb32.ref.png [new file with mode: 0644]
test/reference/paint-repeat.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/paint-source-alpha.base.argb32.ref.png [new file with mode: 0644]
test/reference/paint-source-alpha.base.rgb24.ref.png [new file with mode: 0644]
test/reference/paint-source-alpha.image16.ref.png [new file with mode: 0644]
test/reference/paint-source-alpha.ref.png [new file with mode: 0644]
test/reference/paint-source-alpha.svg.ref.png [new file with mode: 0644]
test/reference/paint-source-alpha.traps.argb32.ref.png [new file with mode: 0644]
test/reference/paint-source-alpha.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/paint-with-alpha-clip-mask.base.argb32.ref.png [new file with mode: 0644]
test/reference/paint-with-alpha-clip-mask.base.rgb24.ref.png [new file with mode: 0644]
test/reference/paint-with-alpha-clip-mask.ref.png [new file with mode: 0644]
test/reference/paint-with-alpha-clip-mask.traps.argb32.ref.png [new file with mode: 0644]
test/reference/paint-with-alpha-clip-mask.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/paint-with-alpha-clip.base.argb32.ref.png [new file with mode: 0644]
test/reference/paint-with-alpha-clip.base.rgb24.ref.png [new file with mode: 0644]
test/reference/paint-with-alpha-clip.ref.png [new file with mode: 0644]
test/reference/paint-with-alpha-clip.traps.argb32.ref.png [new file with mode: 0644]
test/reference/paint-with-alpha-clip.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/paint-with-alpha-solid-clip.base.argb32.ref.png [new file with mode: 0644]
test/reference/paint-with-alpha-solid-clip.base.rgb24.ref.png [new file with mode: 0644]
test/reference/paint-with-alpha-solid-clip.ref.png [new file with mode: 0644]
test/reference/paint-with-alpha-solid-clip.traps.argb32.ref.png [new file with mode: 0644]
test/reference/paint-with-alpha-solid-clip.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/paint-with-alpha.base.argb32.ref.png [new file with mode: 0644]
test/reference/paint-with-alpha.base.rgb24.ref.png [new file with mode: 0644]
test/reference/paint-with-alpha.image16.ref.png [new file with mode: 0644]
test/reference/paint-with-alpha.ref.png [new file with mode: 0644]
test/reference/paint-with-alpha.svg.ref.png [new file with mode: 0644]
test/reference/paint-with-alpha.traps.argb32.ref.png [new file with mode: 0644]
test/reference/paint-with-alpha.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/paint.base.argb32.ref.png [new file with mode: 0644]
test/reference/paint.base.rgb24.ref.png [new file with mode: 0644]
test/reference/paint.ref.png [new file with mode: 0644]
test/reference/paint.traps.argb32.ref.png [new file with mode: 0644]
test/reference/paint.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/partial-clip-text-bottom.base.argb32.ref.png [new file with mode: 0644]
test/reference/partial-clip-text-bottom.base.rgb24.ref.png [new file with mode: 0644]
test/reference/partial-clip-text-bottom.ref.png [new file with mode: 0644]
test/reference/partial-clip-text-bottom.traps.argb32.ref.png [new file with mode: 0644]
test/reference/partial-clip-text-bottom.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/partial-clip-text-left.base.argb32.ref.png [new file with mode: 0644]
test/reference/partial-clip-text-left.base.rgb24.ref.png [new file with mode: 0644]
test/reference/partial-clip-text-left.ref.png [new file with mode: 0644]
test/reference/partial-clip-text-left.traps.argb32.ref.png [new file with mode: 0644]
test/reference/partial-clip-text-left.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/partial-clip-text-right.base.argb32.ref.png [new file with mode: 0644]
test/reference/partial-clip-text-right.base.rgb24.ref.png [new file with mode: 0644]
test/reference/partial-clip-text-right.ref.png [new file with mode: 0644]
test/reference/partial-clip-text-right.traps.argb32.ref.png [new file with mode: 0644]
test/reference/partial-clip-text-right.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/partial-clip-text-top.base.argb32.ref.png [new file with mode: 0644]
test/reference/partial-clip-text-top.base.rgb24.ref.png [new file with mode: 0644]
test/reference/partial-clip-text-top.ps.ref.png [new file with mode: 0644]
test/reference/partial-clip-text-top.quartz.ref.png [new file with mode: 0644]
test/reference/partial-clip-text-top.ref.png [new file with mode: 0644]
test/reference/partial-clip-text-top.svg.ref.png [new file with mode: 0644]
test/reference/partial-clip-text-top.traps.argb32.ref.png [new file with mode: 0644]
test/reference/partial-clip-text-top.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/partial-coverage-half-reference.base.argb32.ref.png [new file with mode: 0644]
test/reference/partial-coverage-half-reference.base.rgb24.ref.png [new file with mode: 0644]
test/reference/partial-coverage-half-reference.ref.png [new file with mode: 0644]
test/reference/partial-coverage-half-reference.traps.argb32.ref.png [new file with mode: 0644]
test/reference/partial-coverage-half-reference.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/partial-coverage-half-triangles.ref.png [new file with mode: 0644]
test/reference/partial-coverage-intersecting-quads.ref.png [new file with mode: 0644]
test/reference/partial-coverage-intersecting-quads.xfail.png [new file with mode: 0644]
test/reference/partial-coverage-intersecting-triangles.ref.png [new file with mode: 0644]
test/reference/partial-coverage-overlap-half-triangles-eo.ref.png [new file with mode: 0644]
test/reference/partial-coverage-overlap-half-triangles.ref.png [new file with mode: 0644]
test/reference/partial-coverage-overlap-three-quarter-triangles.ref.png [new file with mode: 0644]
test/reference/partial-coverage-rectangles.ref.png [new file with mode: 0644]
test/reference/partial-coverage-reference.base.argb32.ref.png [new file with mode: 0644]
test/reference/partial-coverage-reference.base.rgb24.ref.png [new file with mode: 0644]
test/reference/partial-coverage-reference.ref.png [new file with mode: 0644]
test/reference/partial-coverage-reference.traps.argb32.ref.png [new file with mode: 0644]
test/reference/partial-coverage-reference.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/partial-coverage-three-quarter-reference.base.argb32.ref.png [new file with mode: 0644]
test/reference/partial-coverage-three-quarter-reference.base.rgb24.ref.png [new file with mode: 0644]
test/reference/partial-coverage-three-quarter-reference.ref.png [new file with mode: 0644]
test/reference/partial-coverage-three-quarter-reference.traps.argb32.ref.png [new file with mode: 0644]
test/reference/partial-coverage-three-quarter-reference.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/partial-coverage-triangles.ref.png [new file with mode: 0644]
test/reference/pass-through.ref.png [new file with mode: 0644]
test/reference/pass-through.rgb24.ref.png [new file with mode: 0644]
test/reference/path-append.base.argb32.ref.png [new file with mode: 0644]
test/reference/path-append.base.rgb24.ref.png [new file with mode: 0644]
test/reference/path-append.image16.ref.png [new file with mode: 0644]
test/reference/path-append.ps.ref.png [new file with mode: 0644]
test/reference/path-append.quartz.ref.png [new file with mode: 0644]
test/reference/path-append.ref.png [new file with mode: 0644]
test/reference/path-append.test-fallback.ref.png [new file with mode: 0644]
test/reference/path-append.traps.argb32.ref.png [new file with mode: 0644]
test/reference/path-append.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/path-append.xlib-fallback.ref.png [new file with mode: 0644]
test/reference/path-stroke-twice.base.argb32.ref.png [new file with mode: 0644]
test/reference/path-stroke-twice.base.rgb24.ref.png [new file with mode: 0644]
test/reference/path-stroke-twice.image16.ref.png [new file with mode: 0644]
test/reference/path-stroke-twice.ps.ref.png [new file with mode: 0644]
test/reference/path-stroke-twice.ref.png [new file with mode: 0644]
test/reference/path-stroke-twice.traps.argb32.ref.png [new file with mode: 0644]
test/reference/path-stroke-twice.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/pattern-getters.base.argb32.ref.png [new file with mode: 0644]
test/reference/pattern-getters.base.rgb24.ref.png [new file with mode: 0644]
test/reference/pattern-getters.ref.png [new file with mode: 0644]
test/reference/pattern-getters.traps.argb32.ref.png [new file with mode: 0644]
test/reference/pattern-getters.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/pdf-surface-source.argb32.ref.png [new file with mode: 0644]
test/reference/pdf-surface-source.base.argb32.ref.png [new file with mode: 0644]
test/reference/pdf-surface-source.base.rgb24.ref.png [new file with mode: 0644]
test/reference/pdf-surface-source.image16.ref.png [new file with mode: 0644]
test/reference/pdf-surface-source.rgb24.ref.png [new file with mode: 0644]
test/reference/pdf-surface-source.svg12.argb32.xfail.png [new file with mode: 0644]
test/reference/pdf-surface-source.svg12.rgb24.xfail.png [new file with mode: 0644]
test/reference/pdf-surface-source.traps.argb32.ref.png [new file with mode: 0644]
test/reference/pdf-surface-source.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/pixman-rotate.base.argb32.ref.png [new file with mode: 0644]
test/reference/pixman-rotate.base.rgb24.ref.png [new file with mode: 0644]
test/reference/pixman-rotate.ps.argb32.ref.png [new file with mode: 0644]
test/reference/pixman-rotate.ref.png [new file with mode: 0644]
test/reference/pixman-rotate.rgb24.ref.png [new file with mode: 0644]
test/reference/pixman-rotate.traps.argb32.ref.png [new file with mode: 0644]
test/reference/pixman-rotate.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/ps-eps.ref.png [new file with mode: 0644]
test/reference/ps-surface-source.argb32.ref.png [new file with mode: 0644]
test/reference/ps-surface-source.base.argb32.ref.png [new file with mode: 0644]
test/reference/ps-surface-source.base.rgb24.ref.png [new file with mode: 0644]
test/reference/ps-surface-source.image16.ref.png [new file with mode: 0644]
test/reference/ps-surface-source.rgb24.ref.png [new file with mode: 0644]
test/reference/ps-surface-source.svg12.argb32.xfail.png [new file with mode: 0644]
test/reference/ps-surface-source.svg12.rgb24.xfail.png [new file with mode: 0644]
test/reference/ps-surface-source.traps.argb32.ref.png [new file with mode: 0644]
test/reference/ps-surface-source.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/pthread-same-source.base.argb32.ref.png [new file with mode: 0644]
test/reference/pthread-same-source.base.rgb24.ref.png [new file with mode: 0644]
test/reference/pthread-same-source.image16.ref.png [new file with mode: 0644]
test/reference/pthread-same-source.quartz.xfail.png [new file with mode: 0644]
test/reference/pthread-same-source.ref.png [new file with mode: 0644]
test/reference/pthread-same-source.traps.argb32.ref.png [new file with mode: 0644]
test/reference/pthread-same-source.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/pthread-show-text.base.argb32.ref.png [new file with mode: 0644]
test/reference/pthread-show-text.base.rgb24.ref.png [new file with mode: 0644]
test/reference/pthread-show-text.image16.ref.png [new file with mode: 0644]
test/reference/pthread-show-text.pdf.ref.png [new file with mode: 0644]
test/reference/pthread-show-text.ps.ref.png [new file with mode: 0644]
test/reference/pthread-show-text.quartz.ref.png [new file with mode: 0644]
test/reference/pthread-show-text.ref.png [new file with mode: 0644]
test/reference/pthread-show-text.traps.argb32.ref.png [new file with mode: 0644]
test/reference/pthread-show-text.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/pthread-show-text.xlib-fallback.ref.png [new file with mode: 0644]
test/reference/pthread-similar.base.argb32.ref.png [new file with mode: 0644]
test/reference/pthread-similar.base.rgb24.ref.png [new file with mode: 0644]
test/reference/pthread-similar.ref.png [new file with mode: 0644]
test/reference/pthread-similar.traps.argb32.ref.png [new file with mode: 0644]
test/reference/pthread-similar.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/push-group-color.base.argb32.ref.png [new file with mode: 0644]
test/reference/push-group-color.base.rgb24.ref.png [new file with mode: 0644]
test/reference/push-group-color.image16.ref.png [new file with mode: 0644]
test/reference/push-group-color.ps2.ref.png [new file with mode: 0644]
test/reference/push-group-color.ps3.ref.png [new file with mode: 0644]
test/reference/push-group-color.quartz.ref.png [new file with mode: 0644]
test/reference/push-group-color.ref.png [new file with mode: 0644]
test/reference/push-group-color.traps.argb32.ref.png [new file with mode: 0644]
test/reference/push-group-color.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/push-group-path-offset.base.argb32.ref.png [new file with mode: 0644]
test/reference/push-group-path-offset.base.rgb24.ref.png [new file with mode: 0644]
test/reference/push-group-path-offset.ref.png [new file with mode: 0644]
test/reference/push-group-path-offset.traps.argb32.ref.png [new file with mode: 0644]
test/reference/push-group-path-offset.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/push-group.base.argb32.ref.png [new file with mode: 0644]
test/reference/push-group.base.rgb24.ref.png [new file with mode: 0644]
test/reference/push-group.image16.ref.png [new file with mode: 0644]
test/reference/push-group.quartz.argb32.ref.png [new file with mode: 0644]
test/reference/push-group.quartz.rgb24.ref.png [new file with mode: 0644]
test/reference/push-group.ref.png [new file with mode: 0644]
test/reference/push-group.rgb24.ref.png [new file with mode: 0644]
test/reference/push-group.traps.argb32.ref.png [new file with mode: 0644]
test/reference/push-group.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/quartz-surface-source.argb32.ref.png [new file with mode: 0644]
test/reference/quartz-surface-source.ps2.ref.png [new file with mode: 0644]
test/reference/quartz-surface-source.ps3.ref.png [new file with mode: 0644]
test/reference/quartz-surface-source.rgb24.ref.png [new file with mode: 0644]
test/reference/radial-gradient-extend.base.argb32.ref.png [new file with mode: 0644]
test/reference/radial-gradient-extend.base.rgb24.ref.png [new file with mode: 0644]
test/reference/radial-gradient-extend.ps3.ref.png [new file with mode: 0644]
test/reference/radial-gradient-extend.ref.png [new file with mode: 0644]
test/reference/radial-gradient-extend.traps.argb32.ref.png [new file with mode: 0644]
test/reference/radial-gradient-extend.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/radial-gradient-mask-source.argb32.ref.png [new file with mode: 0644]
test/reference/radial-gradient-mask-source.base.argb32.ref.png [new file with mode: 0644]
test/reference/radial-gradient-mask-source.base.rgb24.ref.png [new file with mode: 0644]
test/reference/radial-gradient-mask-source.image16.ref.png [new file with mode: 0644]
test/reference/radial-gradient-mask-source.quartz.argb32.ref.png [new file with mode: 0644]
test/reference/radial-gradient-mask-source.quartz.rgb24.ref.png [new file with mode: 0644]
test/reference/radial-gradient-mask-source.traps.argb32.ref.png [new file with mode: 0644]
test/reference/radial-gradient-mask-source.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/radial-gradient-mask.base.argb32.ref.png [new file with mode: 0644]
test/reference/radial-gradient-mask.base.rgb24.ref.png [new file with mode: 0644]
test/reference/radial-gradient-mask.image16.ref.png [new file with mode: 0644]
test/reference/radial-gradient-mask.quartz.ref.png [new file with mode: 0644]
test/reference/radial-gradient-mask.ref.png [new file with mode: 0644]
test/reference/radial-gradient-mask.traps.argb32.ref.png [new file with mode: 0644]
test/reference/radial-gradient-mask.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/radial-gradient-one-stop.base.argb32.ref.png [new file with mode: 0644]
test/reference/radial-gradient-one-stop.base.rgb24.ref.png [new file with mode: 0644]
test/reference/radial-gradient-one-stop.quartz.ref.png [new file with mode: 0644]
test/reference/radial-gradient-one-stop.ref.png [new file with mode: 0644]
test/reference/radial-gradient-one-stop.traps.argb32.ref.png [new file with mode: 0644]
test/reference/radial-gradient-one-stop.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/radial-gradient-source.argb32.ref.png [new file with mode: 0644]
test/reference/radial-gradient-source.base.argb32.ref.png [new file with mode: 0644]
test/reference/radial-gradient-source.base.rgb24.ref.png [new file with mode: 0644]
test/reference/radial-gradient-source.image16.ref.png [new file with mode: 0644]
test/reference/radial-gradient-source.quartz.argb32.ref.png [new file with mode: 0644]
test/reference/radial-gradient-source.quartz.rgb24.ref.png [new file with mode: 0644]
test/reference/radial-gradient-source.rgb24.ref.png [new file with mode: 0644]
test/reference/radial-gradient-source.traps.argb32.ref.png [new file with mode: 0644]
test/reference/radial-gradient-source.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/radial-gradient.base.argb32.ref.png [new file with mode: 0644]
test/reference/radial-gradient.base.rgb24.ref.png [new file with mode: 0644]
test/reference/radial-gradient.image16.ref.png [new file with mode: 0644]
test/reference/radial-gradient.quartz.ref.png [new file with mode: 0644]
test/reference/radial-gradient.ref.png [new file with mode: 0644]
test/reference/radial-gradient.traps.argb32.ref.png [new file with mode: 0644]
test/reference/radial-gradient.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/radial-outer-focus.xfail.png [new file with mode: 0644]
test/reference/random-clip.base.argb32.xfail.png [new file with mode: 0644]
test/reference/random-clip.base.rgb24.xfail.png [new file with mode: 0644]
test/reference/random-clip.ref.png [new file with mode: 0644]
test/reference/random-clip.traps.argb32.ref.png [new file with mode: 0644]
test/reference/random-clip.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/random-intersections-curves-eo.base.argb32.ref.png [new file with mode: 0644]
test/reference/random-intersections-curves-eo.base.rgb24.ref.png [new file with mode: 0644]
test/reference/random-intersections-curves-eo.image16.ref.png [new file with mode: 0644]
test/reference/random-intersections-curves-eo.pdf.ref.png [new file with mode: 0644]
test/reference/random-intersections-curves-eo.ps.ref.png [new file with mode: 0644]
test/reference/random-intersections-curves-eo.quartz.ref.png [new file with mode: 0644]
test/reference/random-intersections-curves-eo.ref.png [new file with mode: 0644]
test/reference/random-intersections-curves-eo.traps.argb32.ref.png [new file with mode: 0644]
test/reference/random-intersections-curves-eo.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/random-intersections-curves-eo.xlib-fallback.ref.png [new file with mode: 0644]
test/reference/random-intersections-curves-nz.base.argb32.ref.png [new file with mode: 0644]
test/reference/random-intersections-curves-nz.base.rgb24.ref.png [new file with mode: 0644]
test/reference/random-intersections-curves-nz.image16.ref.png [new file with mode: 0644]
test/reference/random-intersections-curves-nz.pdf.ref.png [new file with mode: 0644]
test/reference/random-intersections-curves-nz.ps.ref.png [new file with mode: 0644]
test/reference/random-intersections-curves-nz.quartz.ref.png [new file with mode: 0644]
test/reference/random-intersections-curves-nz.ref.png [new file with mode: 0644]
test/reference/random-intersections-curves-nz.traps.argb32.ref.png [new file with mode: 0644]
test/reference/random-intersections-curves-nz.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/random-intersections-curves-nz.xlib-fallback.ref.png [new file with mode: 0644]
test/reference/random-intersections-eo.base.argb32.ref.png [new file with mode: 0644]
test/reference/random-intersections-eo.base.rgb24.ref.png [new file with mode: 0644]
test/reference/random-intersections-eo.image16.ref.png [new file with mode: 0644]
test/reference/random-intersections-eo.ps.ref.png [new file with mode: 0644]
test/reference/random-intersections-eo.quartz.ref.png [new file with mode: 0644]
test/reference/random-intersections-eo.ref.png [new file with mode: 0644]
test/reference/random-intersections-eo.traps.argb32.ref.png [new file with mode: 0644]
test/reference/random-intersections-eo.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/random-intersections-nonzero.base.argb32.ref.png [new file with mode: 0644]
test/reference/random-intersections-nonzero.base.rgb24.ref.png [new file with mode: 0644]
test/reference/random-intersections-nonzero.image16.ref.png [new file with mode: 0644]
test/reference/random-intersections-nonzero.ps.ref.png [new file with mode: 0644]
test/reference/random-intersections-nonzero.quartz.ref.png [new file with mode: 0644]
test/reference/random-intersections-nonzero.ref.png [new file with mode: 0644]
test/reference/random-intersections-nonzero.traps.argb32.ref.png [new file with mode: 0644]
test/reference/random-intersections-nonzero.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/record-extend-none-similar.ref.png [new file with mode: 0644]
test/reference/record-extend-none-similar.traps.argb32.ref.png [new file with mode: 0644]
test/reference/record-extend-none-similar.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/record-extend-none.ref.png [new file with mode: 0644]
test/reference/record-extend-none.traps.argb32.ref.png [new file with mode: 0644]
test/reference/record-extend-none.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/record-extend-pad-similar.ref.png [new file with mode: 0644]
test/reference/record-extend-pad-similar.traps.argb32.ref.png [new file with mode: 0644]
test/reference/record-extend-pad-similar.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/record-extend-pad.ref.png [new file with mode: 0644]
test/reference/record-extend-pad.traps.argb32.ref.png [new file with mode: 0644]
test/reference/record-extend-pad.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/record-extend-reflect-similar.ref.png [new file with mode: 0644]
test/reference/record-extend-reflect-similar.traps.argb32.ref.png [new file with mode: 0644]
test/reference/record-extend-reflect-similar.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/record-extend-reflect.ref.png [new file with mode: 0644]
test/reference/record-extend-reflect.traps.argb32.ref.png [new file with mode: 0644]
test/reference/record-extend-reflect.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/record-extend-repeat-similar.ref.png [new file with mode: 0644]
test/reference/record-extend-repeat-similar.traps.argb32.ref.png [new file with mode: 0644]
test/reference/record-extend-repeat-similar.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/record-extend-repeat.ref.png [new file with mode: 0644]
test/reference/record-extend-repeat.traps.argb32.ref.png [new file with mode: 0644]
test/reference/record-extend-repeat.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/record-fill-alpha.ref.png [new file with mode: 0644]
test/reference/record-fill-alpha.traps.argb32.ref.png [new file with mode: 0644]
test/reference/record-fill-alpha.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/record-mesh.ref.png [new file with mode: 0644]
test/reference/record-mesh.traps.argb32.ref.png [new file with mode: 0644]
test/reference/record-mesh.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/record-paint-alpha-clip-mask.ref.png [new file with mode: 0644]
test/reference/record-paint-alpha-clip-mask.traps.argb32.ref.png [new file with mode: 0644]
test/reference/record-paint-alpha-clip-mask.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/record-paint-alpha-clip.ref.png [new file with mode: 0644]
test/reference/record-paint-alpha-clip.traps.argb32.ref.png [new file with mode: 0644]
test/reference/record-paint-alpha-clip.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/record-paint-alpha-solid-clip.ref.png [new file with mode: 0644]
test/reference/record-paint-alpha-solid-clip.traps.argb32.ref.png [new file with mode: 0644]
test/reference/record-paint-alpha-solid-clip.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/record-paint-alpha.ref.png [new file with mode: 0644]
test/reference/record-paint-alpha.traps.argb32.ref.png [new file with mode: 0644]
test/reference/record-paint-alpha.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/record-paint.ref.png [new file with mode: 0644]
test/reference/record-paint.traps.argb32.ref.png [new file with mode: 0644]
test/reference/record-paint.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/record-select-font-face.ref.png [new file with mode: 0644]
test/reference/record-select-font-face.xfail.png [new file with mode: 0644]
test/reference/record-self-intersecting.ref.png [new file with mode: 0644]
test/reference/record-text-transform.ref.png [new file with mode: 0644]
test/reference/recording-surface-extend-none.argb32.ref.png [new file with mode: 0644]
test/reference/recording-surface-extend-none.base.argb32.ref.png [new file with mode: 0644]
test/reference/recording-surface-extend-none.base.rgb24.ref.png [new file with mode: 0644]
test/reference/recording-surface-extend-none.rgb24.ref.png [new file with mode: 0644]
test/reference/recording-surface-extend-none.traps.argb32.ref.png [new file with mode: 0644]
test/reference/recording-surface-extend-none.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/recording-surface-extend-pad.argb32.ref.png [new file with mode: 0644]
test/reference/recording-surface-extend-pad.base.argb32.ref.png [new file with mode: 0644]
test/reference/recording-surface-extend-pad.base.rgb24.ref.png [new file with mode: 0644]
test/reference/recording-surface-extend-pad.rgb24.ref.png [new file with mode: 0644]
test/reference/recording-surface-extend-pad.traps.argb32.ref.png [new file with mode: 0644]
test/reference/recording-surface-extend-pad.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/recording-surface-extend-reflect.argb32.ref.png [new file with mode: 0644]
test/reference/recording-surface-extend-reflect.base.argb32.ref.png [new file with mode: 0644]
test/reference/recording-surface-extend-reflect.base.rgb24.ref.png [new file with mode: 0644]
test/reference/recording-surface-extend-reflect.rgb24.ref.png [new file with mode: 0644]
test/reference/recording-surface-extend-reflect.traps.argb32.ref.png [new file with mode: 0644]
test/reference/recording-surface-extend-reflect.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/recording-surface-extend-repeat.argb32.ref.png [new file with mode: 0644]
test/reference/recording-surface-extend-repeat.base.argb32.ref.png [new file with mode: 0644]
test/reference/recording-surface-extend-repeat.base.rgb24.ref.png [new file with mode: 0644]
test/reference/recording-surface-extend-repeat.rgb24.ref.png [new file with mode: 0644]
test/reference/recording-surface-extend-repeat.traps.argb32.ref.png [new file with mode: 0644]
test/reference/recording-surface-extend-repeat.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/recording-surface-over.base.argb32.ref.png [new file with mode: 0644]
test/reference/recording-surface-over.base.rgb24.ref.png [new file with mode: 0644]
test/reference/recording-surface-over.gl.argb32.ref.png [new file with mode: 0644]
test/reference/recording-surface-over.image16.ref.png [new file with mode: 0644]
test/reference/recording-surface-over.pdf.argb32.ref.png [new file with mode: 0644]
test/reference/recording-surface-over.pdf.rgb24.ref.png [new file with mode: 0644]
test/reference/recording-surface-over.ps.argb32.ref.png [new file with mode: 0644]
test/reference/recording-surface-over.ps.rgb24.ref.png [new file with mode: 0644]
test/reference/recording-surface-over.quartz.argb32.ref.png [new file with mode: 0644]
test/reference/recording-surface-over.quartz.rgb24.ref.png [new file with mode: 0644]
test/reference/recording-surface-over.ref.png [new file with mode: 0644]
test/reference/recording-surface-over.rgb24.ref.png [new file with mode: 0644]
test/reference/recording-surface-over.svg.argb32.ref.png [new file with mode: 0644]
test/reference/recording-surface-over.svg.rgb24.ref.png [new file with mode: 0644]
test/reference/recording-surface-over.traps.argb32.ref.png [new file with mode: 0644]
test/reference/recording-surface-over.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/recording-surface-source.argb32.ref.png [new file with mode: 0644]
test/reference/recording-surface-source.base.argb32.ref.png [new file with mode: 0644]
test/reference/recording-surface-source.base.rgb24.ref.png [new file with mode: 0644]
test/reference/recording-surface-source.rgb24.ref.png [new file with mode: 0644]
test/reference/recording-surface-source.traps.argb32.ref.png [new file with mode: 0644]
test/reference/recording-surface-source.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/rectangle-rounding-error.base.argb32.ref.png [new file with mode: 0644]
test/reference/rectangle-rounding-error.base.rgb24.ref.png [new file with mode: 0644]
test/reference/rectangle-rounding-error.ref.png [new file with mode: 0644]
test/reference/rectangle-rounding-error.traps.argb32.ref.png [new file with mode: 0644]
test/reference/rectangle-rounding-error.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/rectilinear-dash.base.argb32.ref.png [new file with mode: 0644]
test/reference/rectilinear-dash.base.rgb24.ref.png [new file with mode: 0644]
test/reference/rectilinear-dash.quartz.xfail.png [new file with mode: 0644]
test/reference/rectilinear-dash.ref.png [new file with mode: 0644]
test/reference/rectilinear-dash.traps.argb32.ref.png [new file with mode: 0644]
test/reference/rectilinear-dash.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/rectilinear-fill.base.argb32.ref.png [new file with mode: 0644]
test/reference/rectilinear-fill.base.rgb24.ref.png [new file with mode: 0644]
test/reference/rectilinear-fill.ref.png [new file with mode: 0644]
test/reference/rectilinear-fill.traps.argb32.ref.png [new file with mode: 0644]
test/reference/rectilinear-fill.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/rectilinear-grid.base.argb32.ref.png [new file with mode: 0644]
test/reference/rectilinear-grid.base.rgb24.ref.png [new file with mode: 0644]
test/reference/rectilinear-grid.image16.ref.png [new file with mode: 0644]
test/reference/rectilinear-grid.ref.png [new file with mode: 0644]
test/reference/rectilinear-grid.traps.argb32.ref.png [new file with mode: 0644]
test/reference/rectilinear-grid.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/rectilinear-miter-limit.base.argb32.ref.png [new file with mode: 0644]
test/reference/rectilinear-miter-limit.base.rgb24.ref.png [new file with mode: 0644]
test/reference/rectilinear-miter-limit.ps2.ref.png [new file with mode: 0644]
test/reference/rectilinear-miter-limit.ps3.ref.png [new file with mode: 0644]
test/reference/rectilinear-miter-limit.ref.png [new file with mode: 0644]
test/reference/rectilinear-miter-limit.traps.argb32.ref.png [new file with mode: 0644]
test/reference/rectilinear-miter-limit.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/rectilinear-stroke.base.argb32.ref.png [new file with mode: 0644]
test/reference/rectilinear-stroke.base.rgb24.ref.png [new file with mode: 0644]
test/reference/rectilinear-stroke.quartz.xfail.png [new file with mode: 0644]
test/reference/rectilinear-stroke.ref.png [new file with mode: 0644]
test/reference/rectilinear-stroke.traps.argb32.ref.png [new file with mode: 0644]
test/reference/rectilinear-stroke.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/reflected-stroke.base.argb32.ref.png [new file with mode: 0644]
test/reference/reflected-stroke.base.rgb24.ref.png [new file with mode: 0644]
test/reference/reflected-stroke.image16.ref.png [new file with mode: 0644]
test/reference/reflected-stroke.ps.ref.png [new file with mode: 0644]
test/reference/reflected-stroke.ref.png [new file with mode: 0644]
test/reference/reflected-stroke.traps.argb32.ref.png [new file with mode: 0644]
test/reference/reflected-stroke.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/rel-path.base.argb32.ref.png [new file with mode: 0644]
test/reference/rel-path.base.rgb24.ref.png [new file with mode: 0644]
test/reference/rel-path.ps2.rgb24.ref.png [new file with mode: 0644]
test/reference/rel-path.ps3.rgb24.ref.png [new file with mode: 0644]
test/reference/rel-path.ref.png [new file with mode: 0644]
test/reference/rel-path.rgb24.ref.png [new file with mode: 0644]
test/reference/rel-path.traps.argb32.ref.png [new file with mode: 0644]
test/reference/rel-path.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/rgb24-ignore-alpha.base.argb32.ref.png [new file with mode: 0644]
test/reference/rgb24-ignore-alpha.base.rgb24.ref.png [new file with mode: 0644]
test/reference/rgb24-ignore-alpha.ref.png [new file with mode: 0644]
test/reference/rgb24-ignore-alpha.traps.argb32.ref.png [new file with mode: 0644]
test/reference/rgb24-ignore-alpha.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/rotate-clip-image-surface-paint.base.argb32.ref.png [new file with mode: 0644]
test/reference/rotate-clip-image-surface-paint.base.rgb24.ref.png [new file with mode: 0644]
test/reference/rotate-clip-image-surface-paint.ref.png [new file with mode: 0644]
test/reference/rotate-clip-image-surface-paint.traps.argb32.ref.png [new file with mode: 0644]
test/reference/rotate-clip-image-surface-paint.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/rotate-image-surface-paint.base.argb32.ref.png [new file with mode: 0644]
test/reference/rotate-image-surface-paint.base.rgb24.ref.png [new file with mode: 0644]
test/reference/rotate-image-surface-paint.pdf.xfail.png [new file with mode: 0644]
test/reference/rotate-image-surface-paint.ps.ref.png [new file with mode: 0644]
test/reference/rotate-image-surface-paint.quartz.ref.png [new file with mode: 0644]
test/reference/rotate-image-surface-paint.ref.png [new file with mode: 0644]
test/reference/rotate-image-surface-paint.svg.ref.png [new file with mode: 0644]
test/reference/rotate-image-surface-paint.svg.xfail.png [new file with mode: 0644]
test/reference/rotate-image-surface-paint.traps.argb32.ref.png [new file with mode: 0644]
test/reference/rotate-image-surface-paint.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/rotated-clip.base.argb32.ref.png [new file with mode: 0644]
test/reference/rotated-clip.base.rgb24.ref.png [new file with mode: 0644]
test/reference/rotated-clip.image16.ref.png [new file with mode: 0644]
test/reference/rotated-clip.ps.ref.png [new file with mode: 0644]
test/reference/rotated-clip.quartz.ref.png [new file with mode: 0644]
test/reference/rotated-clip.ref.png [new file with mode: 0644]
test/reference/rotated-clip.traps.argb32.ref.png [new file with mode: 0644]
test/reference/rotated-clip.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/rounded-rectangle-fill.base.argb32.ref.png [new file with mode: 0644]
test/reference/rounded-rectangle-fill.base.rgb24.ref.png [new file with mode: 0644]
test/reference/rounded-rectangle-fill.image16.ref.png [new file with mode: 0644]
test/reference/rounded-rectangle-fill.ps.ref.png [new file with mode: 0644]
test/reference/rounded-rectangle-fill.quartz.ref.png [new file with mode: 0644]
test/reference/rounded-rectangle-fill.ref.png [new file with mode: 0644]
test/reference/rounded-rectangle-fill.traps.argb32.ref.png [new file with mode: 0644]
test/reference/rounded-rectangle-fill.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/rounded-rectangle-stroke.base.argb32.ref.png [new file with mode: 0644]
test/reference/rounded-rectangle-stroke.base.rgb24.ref.png [new file with mode: 0644]
test/reference/rounded-rectangle-stroke.image16.ref.png [new file with mode: 0644]
test/reference/rounded-rectangle-stroke.ps.ref.png [new file with mode: 0644]
test/reference/rounded-rectangle-stroke.ref.png [new file with mode: 0644]
test/reference/rounded-rectangle-stroke.traps.argb32.ref.png [new file with mode: 0644]
test/reference/rounded-rectangle-stroke.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/scale-down-source-surface-paint.base.argb32.ref.png [new file with mode: 0644]
test/reference/scale-down-source-surface-paint.base.rgb24.ref.png [new file with mode: 0644]
test/reference/scale-down-source-surface-paint.ref.png [new file with mode: 0644]
test/reference/scale-down-source-surface-paint.traps.argb32.ref.png [new file with mode: 0644]
test/reference/scale-down-source-surface-paint.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/scale-offset-image.base.argb32.ref.png [new file with mode: 0644]
test/reference/scale-offset-image.base.rgb24.ref.png [new file with mode: 0644]
test/reference/scale-offset-image.gl.ref.png [new file with mode: 0644]
test/reference/scale-offset-image.image16.ref.png [new file with mode: 0644]
test/reference/scale-offset-image.pdf.argb32.ref.png [new file with mode: 0644]
test/reference/scale-offset-image.pdf.rgb24.ref.png [new file with mode: 0644]
test/reference/scale-offset-image.ps.ref.png [new file with mode: 0644]
test/reference/scale-offset-image.quartz.ref.png [new file with mode: 0644]
test/reference/scale-offset-image.ref.png [new file with mode: 0644]
test/reference/scale-offset-image.script.xfail.png [new file with mode: 0644]
test/reference/scale-offset-image.traps.argb32.ref.png [new file with mode: 0644]
test/reference/scale-offset-image.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/scale-offset-image.xfail.png [new file with mode: 0644]
test/reference/scale-offset-image.xlib-fallback.xfail.png [new file with mode: 0644]
test/reference/scale-offset-similar.base.argb32.ref.png [new file with mode: 0644]
test/reference/scale-offset-similar.base.rgb24.ref.png [new file with mode: 0644]
test/reference/scale-offset-similar.gl.ref.png [new file with mode: 0644]
test/reference/scale-offset-similar.image16.ref.png [new file with mode: 0644]
test/reference/scale-offset-similar.pdf.argb32.ref.png [new file with mode: 0644]
test/reference/scale-offset-similar.pdf.rgb24.ref.png [new file with mode: 0644]
test/reference/scale-offset-similar.ps.ref.png [new file with mode: 0644]
test/reference/scale-offset-similar.quartz.ref.png [new file with mode: 0644]
test/reference/scale-offset-similar.recording.xfail.png [new file with mode: 0644]
test/reference/scale-offset-similar.ref.png [new file with mode: 0644]
test/reference/scale-offset-similar.script.xfail.png [new file with mode: 0644]
test/reference/scale-offset-similar.traps.argb32.ref.png [new file with mode: 0644]
test/reference/scale-offset-similar.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/scale-offset-similar.xfail.png [new file with mode: 0644]
test/reference/scale-offset-similar.xlib-fallback.xfail.png [new file with mode: 0644]
test/reference/scale-offset-similar.xlib.xfail.png [new file with mode: 0644]
test/reference/scale-source-surface-paint.base.argb32.ref.png [new file with mode: 0644]
test/reference/scale-source-surface-paint.base.rgb24.ref.png [new file with mode: 0644]
test/reference/scale-source-surface-paint.pdf.argb32.xfail.png [new file with mode: 0644]
test/reference/scale-source-surface-paint.pdf.rgb24.xfail.png [new file with mode: 0644]
test/reference/scale-source-surface-paint.ref.png [new file with mode: 0644]
test/reference/scale-source-surface-paint.rgb24.ref.png [new file with mode: 0644]
test/reference/scale-source-surface-paint.svg.argb32.xfail.png [new file with mode: 0644]
test/reference/scale-source-surface-paint.svg.rgb24.xfail.png [new file with mode: 0644]
test/reference/scale-source-surface-paint.traps.argb32.ref.png [new file with mode: 0644]
test/reference/scale-source-surface-paint.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/select-font-face.base.argb32.ref.png [new file with mode: 0644]
test/reference/select-font-face.base.rgb24.ref.png [new file with mode: 0644]
test/reference/select-font-face.image16.ref.png [new file with mode: 0644]
test/reference/select-font-face.ps2.ref.png [new file with mode: 0644]
test/reference/select-font-face.ps3.ref.png [new file with mode: 0644]
test/reference/select-font-face.quartz.ref.png [new file with mode: 0644]
test/reference/select-font-face.ref.png [new file with mode: 0644]
test/reference/select-font-face.traps.argb32.ref.png [new file with mode: 0644]
test/reference/select-font-face.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/self-copy-overlap.base.argb32.ref.png [new file with mode: 0644]
test/reference/self-copy-overlap.base.rgb24.ref.png [new file with mode: 0644]
test/reference/self-copy-overlap.traps.argb32.ref.png [new file with mode: 0644]
test/reference/self-copy-overlap.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/self-copy.base.argb32.ref.png [new file with mode: 0644]
test/reference/self-copy.base.rgb24.ref.png [new file with mode: 0644]
test/reference/self-copy.ps2.ref.png [new file with mode: 0644]
test/reference/self-copy.ps3.ref.png [new file with mode: 0644]
test/reference/self-copy.ref.png [new file with mode: 0644]
test/reference/self-copy.traps.argb32.ref.png [new file with mode: 0644]
test/reference/self-copy.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/self-intersecting.base.argb32.ref.png [new file with mode: 0644]
test/reference/self-intersecting.base.rgb24.ref.png [new file with mode: 0644]
test/reference/self-intersecting.ps.ref.png [new file with mode: 0644]
test/reference/self-intersecting.quartz.xfail.png [new file with mode: 0644]
test/reference/self-intersecting.ref.png [new file with mode: 0644]
test/reference/self-intersecting.traps.argb32.ref.png [new file with mode: 0644]
test/reference/self-intersecting.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/set-source.base.argb32.ref.png [new file with mode: 0644]
test/reference/set-source.base.rgb24.ref.png [new file with mode: 0644]
test/reference/set-source.ref.png [new file with mode: 0644]
test/reference/set-source.rgb24.ref.png [new file with mode: 0644]
test/reference/set-source.traps.argb32.ref.png [new file with mode: 0644]
test/reference/set-source.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/shape-general-convex.base.argb32.ref.png [new file with mode: 0644]
test/reference/shape-general-convex.base.rgb24.ref.png [new file with mode: 0644]
test/reference/shape-general-convex.ps.ref.png [new file with mode: 0644]
test/reference/shape-general-convex.ref.png [new file with mode: 0644]
test/reference/shape-general-convex.traps.argb32.ref.png [new file with mode: 0644]
test/reference/shape-general-convex.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/shape-sierpinski.base.argb32.ref.png [new file with mode: 0644]
test/reference/shape-sierpinski.base.rgb24.ref.png [new file with mode: 0644]
test/reference/shape-sierpinski.pdf.argb32.ref.png [new file with mode: 0644]
test/reference/shape-sierpinski.pdf.rgb24.ref.png [new file with mode: 0644]
test/reference/shape-sierpinski.ps.ref.png [new file with mode: 0644]
test/reference/shape-sierpinski.ps3.argb32.ref.png [new file with mode: 0644]
test/reference/shape-sierpinski.ps3.rgb24.ref.png [new file with mode: 0644]
test/reference/shape-sierpinski.ref.png [new file with mode: 0644]
test/reference/shape-sierpinski.traps.argb32.ref.png [new file with mode: 0644]
test/reference/shape-sierpinski.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/show-glyphs-advance.base.argb32.ref.png [new file with mode: 0644]
test/reference/show-glyphs-advance.base.rgb24.ref.png [new file with mode: 0644]
test/reference/show-glyphs-advance.image16.ref.png [new file with mode: 0644]
test/reference/show-glyphs-advance.ps.ref.png [new file with mode: 0644]
test/reference/show-glyphs-advance.quartz.ref.png [new file with mode: 0644]
test/reference/show-glyphs-advance.ref.png [new file with mode: 0644]
test/reference/show-glyphs-advance.svg.ref.png [new file with mode: 0644]
test/reference/show-glyphs-advance.traps.argb32.ref.png [new file with mode: 0644]
test/reference/show-glyphs-advance.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/show-glyphs-many.base.argb32.ref.png [new file with mode: 0644]
test/reference/show-glyphs-many.base.rgb24.ref.png [new file with mode: 0644]
test/reference/show-glyphs-many.ref.png [new file with mode: 0644]
test/reference/show-glyphs-many.traps.argb32.ref.png [new file with mode: 0644]
test/reference/show-glyphs-many.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/show-text-current-point.base.argb32.ref.png [new file with mode: 0644]
test/reference/show-text-current-point.base.rgb24.ref.png [new file with mode: 0644]
test/reference/show-text-current-point.image16.ref.png [new file with mode: 0644]
test/reference/show-text-current-point.ps2.ref.png [new file with mode: 0644]
test/reference/show-text-current-point.ps3.ref.png [new file with mode: 0644]
test/reference/show-text-current-point.quartz.ref.png [new file with mode: 0644]
test/reference/show-text-current-point.ref.png [new file with mode: 0644]
test/reference/show-text-current-point.traps.argb32.ref.png [new file with mode: 0644]
test/reference/show-text-current-point.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/skew-extreme.base.argb32.ref.png [new file with mode: 0644]
test/reference/skew-extreme.base.rgb24.ref.png [new file with mode: 0644]
test/reference/skew-extreme.ps2.ref.png [new file with mode: 0644]
test/reference/skew-extreme.ps3.ref.png [new file with mode: 0644]
test/reference/skew-extreme.ref.png [new file with mode: 0644]
test/reference/skew-extreme.traps.argb32.ref.png [new file with mode: 0644]
test/reference/skew-extreme.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/smask-fill.base.argb32.ref.png [new file with mode: 0644]
test/reference/smask-fill.base.rgb24.ref.png [new file with mode: 0644]
test/reference/smask-fill.image16.ref.png [new file with mode: 0644]
test/reference/smask-fill.pdf.ref.png [new file with mode: 0644]
test/reference/smask-fill.quartz.ref.png [new file with mode: 0644]
test/reference/smask-fill.ref.png [new file with mode: 0644]
test/reference/smask-fill.svg.ref.png [new file with mode: 0644]
test/reference/smask-fill.traps.argb32.ref.png [new file with mode: 0644]
test/reference/smask-fill.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/smask-image-mask.base.argb32.ref.png [new file with mode: 0644]
test/reference/smask-image-mask.base.rgb24.ref.png [new file with mode: 0644]
test/reference/smask-image-mask.pdf.ref.png [new file with mode: 0644]
test/reference/smask-image-mask.ref.png [new file with mode: 0644]
test/reference/smask-image-mask.traps.argb32.ref.png [new file with mode: 0644]
test/reference/smask-image-mask.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/smask-mask.base.argb32.ref.png [new file with mode: 0644]
test/reference/smask-mask.base.rgb24.ref.png [new file with mode: 0644]
test/reference/smask-mask.image16.ref.png [new file with mode: 0644]
test/reference/smask-mask.pdf.ref.png [new file with mode: 0644]
test/reference/smask-mask.quartz.ref.png [new file with mode: 0644]
test/reference/smask-mask.ref.png [new file with mode: 0644]
test/reference/smask-mask.svg.ref.png [new file with mode: 0644]
test/reference/smask-mask.traps.argb32.ref.png [new file with mode: 0644]
test/reference/smask-mask.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/smask-paint.base.argb32.ref.png [new file with mode: 0644]
test/reference/smask-paint.base.rgb24.ref.png [new file with mode: 0644]
test/reference/smask-paint.image16.ref.png [new file with mode: 0644]
test/reference/smask-paint.pdf.ref.png [new file with mode: 0644]
test/reference/smask-paint.quartz.ref.png [new file with mode: 0644]
test/reference/smask-paint.ref.png [new file with mode: 0644]
test/reference/smask-paint.svg.ref.png [new file with mode: 0644]
test/reference/smask-paint.traps.argb32.ref.png [new file with mode: 0644]
test/reference/smask-paint.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/smask-stroke.base.argb32.ref.png [new file with mode: 0644]
test/reference/smask-stroke.base.rgb24.ref.png [new file with mode: 0644]
test/reference/smask-stroke.image16.ref.png [new file with mode: 0644]
test/reference/smask-stroke.pdf.xfail.png [new file with mode: 0644]
test/reference/smask-stroke.quartz.ref.png [new file with mode: 0644]
test/reference/smask-stroke.ref.png [new file with mode: 0644]
test/reference/smask-stroke.traps.argb32.ref.png [new file with mode: 0644]
test/reference/smask-stroke.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/smask-text.base.argb32.ref.png [new file with mode: 0644]
test/reference/smask-text.base.rgb24.ref.png [new file with mode: 0644]
test/reference/smask-text.image16.ref.png [new file with mode: 0644]
test/reference/smask-text.pdf.ref.png [new file with mode: 0644]
test/reference/smask-text.ps2.ref.png [new file with mode: 0644]
test/reference/smask-text.ps3.ref.png [new file with mode: 0644]
test/reference/smask-text.quartz.ref.png [new file with mode: 0644]
test/reference/smask-text.ref.png [new file with mode: 0644]
test/reference/smask-text.script.ref.png [new file with mode: 0644]
test/reference/smask-text.svg.ref.png [new file with mode: 0644]
test/reference/smask-text.traps.argb32.ref.png [new file with mode: 0644]
test/reference/smask-text.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/smask-text.xlib-fallback.ref.png [new file with mode: 0644]
test/reference/smask.base.argb32.ref.png [new file with mode: 0644]
test/reference/smask.base.rgb24.ref.png [new file with mode: 0644]
test/reference/smask.image16.ref.png [new file with mode: 0644]
test/reference/smask.pdf.xfail.png [new file with mode: 0644]
test/reference/smask.ps.ref.png [new file with mode: 0644]
test/reference/smask.quartz.ref.png [new file with mode: 0644]
test/reference/smask.ref.png [new file with mode: 0644]
test/reference/smask.script.ref.png [new file with mode: 0644]
test/reference/smask.svg.ref.png [new file with mode: 0644]
test/reference/smask.traps.argb32.ref.png [new file with mode: 0644]
test/reference/smask.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/smask.xlib-fallback.ref.png [new file with mode: 0644]
test/reference/solid-pattern-cache-stress.base.argb32.ref.png [new file with mode: 0644]
test/reference/solid-pattern-cache-stress.base.rgb24.ref.png [new file with mode: 0644]
test/reference/solid-pattern-cache-stress.ref.png [new file with mode: 0644]
test/reference/solid-pattern-cache-stress.traps.argb32.ref.png [new file with mode: 0644]
test/reference/solid-pattern-cache-stress.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/source-clip-scale.base.argb32.ref.png [new file with mode: 0644]
test/reference/source-clip-scale.base.rgb24.ref.png [new file with mode: 0644]
test/reference/source-clip-scale.gl.ref.png [new file with mode: 0644]
test/reference/source-clip-scale.pdf.ref.png [new file with mode: 0644]
test/reference/source-clip-scale.ps2.argb32.ref.png [new file with mode: 0644]
test/reference/source-clip-scale.ps2.rgb24.ref.png [new file with mode: 0644]
test/reference/source-clip-scale.ps3.argb32.ref.png [new file with mode: 0644]
test/reference/source-clip-scale.ps3.rgb24.ref.png [new file with mode: 0644]
test/reference/source-clip-scale.quartz.ref.png [new file with mode: 0644]
test/reference/source-clip-scale.ref.png [new file with mode: 0644]
test/reference/source-clip-scale.svg.ref.png [new file with mode: 0644]
test/reference/source-clip-scale.traps.argb32.ref.png [new file with mode: 0644]
test/reference/source-clip-scale.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/source-clip.base.argb32.ref.png [new file with mode: 0644]
test/reference/source-clip.base.rgb24.ref.png [new file with mode: 0644]
test/reference/source-clip.ref.png [new file with mode: 0644]
test/reference/source-clip.traps.argb32.ref.png [new file with mode: 0644]
test/reference/source-clip.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/source-surface-scale-paint.base.argb32.ref.png [new file with mode: 0644]
test/reference/source-surface-scale-paint.base.rgb24.ref.png [new file with mode: 0644]
test/reference/source-surface-scale-paint.ref.png [new file with mode: 0644]
test/reference/source-surface-scale-paint.rgb24.ref.png [new file with mode: 0644]
test/reference/source-surface-scale-paint.traps.argb32.ref.png [new file with mode: 0644]
test/reference/source-surface-scale-paint.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/spline-decomposition.base.argb32.ref.png [new file with mode: 0644]
test/reference/spline-decomposition.base.rgb24.ref.png [new file with mode: 0644]
test/reference/spline-decomposition.image16.ref.png [new file with mode: 0644]
test/reference/spline-decomposition.pdf.ref.png [new file with mode: 0644]
test/reference/spline-decomposition.ps.ref.png [new file with mode: 0644]
test/reference/spline-decomposition.quartz.xfail.png [new file with mode: 0644]
test/reference/spline-decomposition.ref.png [new file with mode: 0644]
test/reference/spline-decomposition.svg.ref.png [new file with mode: 0644]
test/reference/spline-decomposition.traps.argb32.ref.png [new file with mode: 0644]
test/reference/spline-decomposition.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/stroke-ctm-caps.base.argb32.ref.png [new file with mode: 0644]
test/reference/stroke-ctm-caps.base.rgb24.ref.png [new file with mode: 0644]
test/reference/stroke-ctm-caps.image16.ref.png [new file with mode: 0644]
test/reference/stroke-ctm-caps.ps2.ref.png [new file with mode: 0644]
test/reference/stroke-ctm-caps.ps3.ref.png [new file with mode: 0644]
test/reference/stroke-ctm-caps.quartz.ref.png [new file with mode: 0644]
test/reference/stroke-ctm-caps.ref.png [new file with mode: 0644]
test/reference/stroke-ctm-caps.traps.argb32.ref.png [new file with mode: 0644]
test/reference/stroke-ctm-caps.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/stroke-image.base.argb32.ref.png [new file with mode: 0644]
test/reference/stroke-image.base.rgb24.ref.png [new file with mode: 0644]
test/reference/stroke-image.image16.ref.png [new file with mode: 0644]
test/reference/stroke-image.pdf.ref.png [new file with mode: 0644]
test/reference/stroke-image.ps.ref.png [new file with mode: 0644]
test/reference/stroke-image.quartz.ref.png [new file with mode: 0644]
test/reference/stroke-image.ref.png [new file with mode: 0644]
test/reference/stroke-image.traps.argb32.ref.png [new file with mode: 0644]
test/reference/stroke-image.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/stroke-open-box.base.argb32.ref.png [new file with mode: 0644]
test/reference/stroke-open-box.base.rgb24.ref.png [new file with mode: 0644]
test/reference/stroke-open-box.ref.png [new file with mode: 0644]
test/reference/stroke-open-box.traps.argb32.ref.png [new file with mode: 0644]
test/reference/stroke-open-box.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/stroke-pattern.ref.png [new file with mode: 0644]
test/reference/subsurface-image-repeat.base.argb32.ref.png [new file with mode: 0644]
test/reference/subsurface-image-repeat.base.rgb24.ref.png [new file with mode: 0644]
test/reference/subsurface-image-repeat.image16.ref.png [new file with mode: 0644]
test/reference/subsurface-image-repeat.ref.png [new file with mode: 0644]
test/reference/subsurface-image-repeat.traps.argb32.ref.png [new file with mode: 0644]
test/reference/subsurface-image-repeat.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/subsurface-modify-child.base.argb32.ref.png [new file with mode: 0644]
test/reference/subsurface-modify-child.base.rgb24.ref.png [new file with mode: 0644]
test/reference/subsurface-modify-child.ref.png [new file with mode: 0644]
test/reference/subsurface-modify-child.traps.argb32.ref.png [new file with mode: 0644]
test/reference/subsurface-modify-child.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/subsurface-modify-parent.base.argb32.ref.png [new file with mode: 0644]
test/reference/subsurface-modify-parent.base.rgb24.ref.png [new file with mode: 0644]
test/reference/subsurface-modify-parent.ref.png [new file with mode: 0644]
test/reference/subsurface-modify-parent.traps.argb32.ref.png [new file with mode: 0644]
test/reference/subsurface-modify-parent.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/subsurface-outside-target.argb32.ref.png [new file with mode: 0644]
test/reference/subsurface-outside-target.base.argb32.ref.png [new file with mode: 0644]
test/reference/subsurface-outside-target.base.rgb24.ref.png [new file with mode: 0644]
test/reference/subsurface-outside-target.rgb24.ref.png [new file with mode: 0644]
test/reference/subsurface-pad.base.argb32.ref.png [new file with mode: 0644]
test/reference/subsurface-pad.base.rgb24.ref.png [new file with mode: 0644]
test/reference/subsurface-pad.quartz.xfail.png [new file with mode: 0644]
test/reference/subsurface-pad.ref.png [new file with mode: 0644]
test/reference/subsurface-pad.traps.argb32.ref.png [new file with mode: 0644]
test/reference/subsurface-pad.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/subsurface-reflect.base.argb32.ref.png [new file with mode: 0644]
test/reference/subsurface-reflect.base.rgb24.ref.png [new file with mode: 0644]
test/reference/subsurface-reflect.ref.png [new file with mode: 0644]
test/reference/subsurface-reflect.traps.argb32.ref.png [new file with mode: 0644]
test/reference/subsurface-reflect.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/subsurface-repeat.base.argb32.ref.png [new file with mode: 0644]
test/reference/subsurface-repeat.base.rgb24.ref.png [new file with mode: 0644]
test/reference/subsurface-repeat.ref.png [new file with mode: 0644]
test/reference/subsurface-repeat.traps.argb32.ref.png [new file with mode: 0644]
test/reference/subsurface-repeat.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/subsurface-scale.base.ref.png [new file with mode: 0644]
test/reference/subsurface-scale.ref.png [new file with mode: 0644]
test/reference/subsurface-similar-repeat.base.argb32.ref.png [new file with mode: 0644]
test/reference/subsurface-similar-repeat.base.rgb24.ref.png [new file with mode: 0644]
test/reference/subsurface-similar-repeat.ref.png [new file with mode: 0644]
test/reference/subsurface-similar-repeat.traps.argb32.ref.png [new file with mode: 0644]
test/reference/subsurface-similar-repeat.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/subsurface.base.argb32.ref.png [new file with mode: 0644]
test/reference/subsurface.base.rgb24.ref.png [new file with mode: 0644]
test/reference/subsurface.image16.ref.png [new file with mode: 0644]
test/reference/subsurface.ps.ref.png [new file with mode: 0644]
test/reference/subsurface.ref.png [new file with mode: 0644]
test/reference/subsurface.traps.argb32.ref.png [new file with mode: 0644]
test/reference/subsurface.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/surface-pattern-big-scale-down.base.argb32.ref.png [new file with mode: 0644]
test/reference/surface-pattern-big-scale-down.base.rgb24.ref.png [new file with mode: 0644]
test/reference/surface-pattern-big-scale-down.ps.ref.png [new file with mode: 0644]
test/reference/surface-pattern-big-scale-down.quartz.ref.png [new file with mode: 0644]
test/reference/surface-pattern-big-scale-down.ref.png [new file with mode: 0644]
test/reference/surface-pattern-big-scale-down.traps.argb32.ref.png [new file with mode: 0644]
test/reference/surface-pattern-big-scale-down.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/surface-pattern-operator.argb32.ref.png [new file with mode: 0644]
test/reference/surface-pattern-operator.base.argb32.ref.png [new file with mode: 0644]
test/reference/surface-pattern-operator.base.rgb24.ref.png [new file with mode: 0644]
test/reference/surface-pattern-operator.image16.ref.png [new file with mode: 0644]
test/reference/surface-pattern-operator.pdf.argb32.xfail.png [new file with mode: 0644]
test/reference/surface-pattern-operator.pdf.rgb24.xfail.png [new file with mode: 0644]
test/reference/surface-pattern-operator.quartz.argb32.ref.png [new file with mode: 0644]
test/reference/surface-pattern-operator.quartz.rgb24.ref.png [new file with mode: 0644]
test/reference/surface-pattern-operator.rgb24.ref.png [new file with mode: 0644]
test/reference/surface-pattern-operator.traps.argb32.ref.png [new file with mode: 0644]
test/reference/surface-pattern-operator.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/surface-pattern-scale-down-extend-none.base.argb32.ref.png [new file with mode: 0644]
test/reference/surface-pattern-scale-down-extend-none.base.rgb24.ref.png [new file with mode: 0644]
test/reference/surface-pattern-scale-down-extend-none.quartz.ref.png [new file with mode: 0644]
test/reference/surface-pattern-scale-down-extend-none.ref.png [new file with mode: 0644]
test/reference/surface-pattern-scale-down-extend-none.traps.argb32.ref.png [new file with mode: 0644]
test/reference/surface-pattern-scale-down-extend-none.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/surface-pattern-scale-down-extend-pad.base.argb32.ref.png [new file with mode: 0644]
test/reference/surface-pattern-scale-down-extend-pad.base.rgb24.ref.png [new file with mode: 0644]
test/reference/surface-pattern-scale-down-extend-pad.quartz.xfail.png [new file with mode: 0644]
test/reference/surface-pattern-scale-down-extend-pad.ref.png [new file with mode: 0644]
test/reference/surface-pattern-scale-down-extend-pad.traps.argb32.ref.png [new file with mode: 0644]
test/reference/surface-pattern-scale-down-extend-pad.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/surface-pattern-scale-down-extend-reflect.base.argb32.ref.png [new file with mode: 0644]
test/reference/surface-pattern-scale-down-extend-reflect.base.rgb24.ref.png [new file with mode: 0644]
test/reference/surface-pattern-scale-down-extend-reflect.quartz.ref.png [new file with mode: 0644]
test/reference/surface-pattern-scale-down-extend-reflect.ref.png [new file with mode: 0644]
test/reference/surface-pattern-scale-down-extend-reflect.traps.argb32.ref.png [new file with mode: 0644]
test/reference/surface-pattern-scale-down-extend-reflect.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/surface-pattern-scale-down-extend-repeat.base.argb32.ref.png [new file with mode: 0644]
test/reference/surface-pattern-scale-down-extend-repeat.base.rgb24.ref.png [new file with mode: 0644]
test/reference/surface-pattern-scale-down-extend-repeat.quartz.ref.png [new file with mode: 0644]
test/reference/surface-pattern-scale-down-extend-repeat.ref.png [new file with mode: 0644]
test/reference/surface-pattern-scale-down-extend-repeat.traps.argb32.ref.png [new file with mode: 0644]
test/reference/surface-pattern-scale-down-extend-repeat.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/surface-pattern-scale-down.base.argb32.ref.png [new file with mode: 0644]
test/reference/surface-pattern-scale-down.base.rgb24.ref.png [new file with mode: 0644]
test/reference/surface-pattern-scale-down.image16.ref.png [new file with mode: 0644]
test/reference/surface-pattern-scale-down.pdf.ref.png [new file with mode: 0644]
test/reference/surface-pattern-scale-down.ps2.ref.png [new file with mode: 0644]
test/reference/surface-pattern-scale-down.ps3.ref.png [new file with mode: 0644]
test/reference/surface-pattern-scale-down.quartz.ref.png [new file with mode: 0644]
test/reference/surface-pattern-scale-down.ref.png [new file with mode: 0644]
test/reference/surface-pattern-scale-down.traps.argb32.ref.png [new file with mode: 0644]
test/reference/surface-pattern-scale-down.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/surface-pattern-scale-up.base.argb32.ref.png [new file with mode: 0644]
test/reference/surface-pattern-scale-up.base.rgb24.ref.png [new file with mode: 0644]
test/reference/surface-pattern-scale-up.image16.ref.png [new file with mode: 0644]
test/reference/surface-pattern-scale-up.pdf.ref.png [new file with mode: 0644]
test/reference/surface-pattern-scale-up.ps2.ref.png [new file with mode: 0644]
test/reference/surface-pattern-scale-up.ps3.ref.png [new file with mode: 0644]
test/reference/surface-pattern-scale-up.quartz.xfail.png [new file with mode: 0644]
test/reference/surface-pattern-scale-up.ref.png [new file with mode: 0644]
test/reference/surface-pattern-scale-up.traps.argb32.ref.png [new file with mode: 0644]
test/reference/surface-pattern-scale-up.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/surface-pattern.base.argb32.ref.png [new file with mode: 0644]
test/reference/surface-pattern.base.rgb24.ref.png [new file with mode: 0644]
test/reference/surface-pattern.image16.ref.png [new file with mode: 0644]
test/reference/surface-pattern.pdf.xfail.png [new file with mode: 0644]
test/reference/surface-pattern.ps.xfail.png [new file with mode: 0644]
test/reference/surface-pattern.quartz.xfail.png [new file with mode: 0644]
test/reference/surface-pattern.ref.png [new file with mode: 0644]
test/reference/surface-pattern.svg.xfail.png [new file with mode: 0644]
test/reference/surface-pattern.traps.argb32.ref.png [new file with mode: 0644]
test/reference/surface-pattern.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/svg-surface-source.argb32.ref.png [new file with mode: 0644]
test/reference/svg-surface-source.base.argb32.ref.png [new file with mode: 0644]
test/reference/svg-surface-source.base.rgb24.ref.png [new file with mode: 0644]
test/reference/svg-surface-source.image16.ref.png [new file with mode: 0644]
test/reference/svg-surface-source.rgb24.ref.png [new file with mode: 0644]
test/reference/svg-surface-source.svg12.argb32.xfail.png [new file with mode: 0644]
test/reference/svg-surface-source.svg12.rgb24.xfail.png [new file with mode: 0644]
test/reference/svg-surface-source.traps.argb32.ref.png [new file with mode: 0644]
test/reference/svg-surface-source.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/text-antialias-gray.base.argb32.ref.png [new file with mode: 0644]
test/reference/text-antialias-gray.base.rgb24.ref.png [new file with mode: 0644]
test/reference/text-antialias-gray.image16.ref.png [new file with mode: 0644]
test/reference/text-antialias-gray.quartz.ref.png [new file with mode: 0644]
test/reference/text-antialias-gray.ref.png [new file with mode: 0644]
test/reference/text-antialias-gray.traps.argb32.ref.png [new file with mode: 0644]
test/reference/text-antialias-gray.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/text-antialias-none.base.argb32.ref.png [new file with mode: 0644]
test/reference/text-antialias-none.base.rgb24.ref.png [new file with mode: 0644]
test/reference/text-antialias-none.quartz.ref.png [new file with mode: 0644]
test/reference/text-antialias-none.ref.png [new file with mode: 0644]
test/reference/text-antialias-none.traps.argb32.ref.png [new file with mode: 0644]
test/reference/text-antialias-none.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/text-antialias-subpixel.base.argb32.xfail.png [new file with mode: 0644]
test/reference/text-antialias-subpixel.base.rgb24.xfail.png [new file with mode: 0644]
test/reference/text-antialias-subpixel.image16.ref.png [new file with mode: 0644]
test/reference/text-antialias-subpixel.quartz.ref.png [new file with mode: 0644]
test/reference/text-antialias-subpixel.ref.png [new file with mode: 0644]
test/reference/text-antialias-subpixel.traps.argb32.ref.png [new file with mode: 0644]
test/reference/text-antialias-subpixel.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/text-glyph-range.base.argb32.ref.png [new file with mode: 0644]
test/reference/text-glyph-range.base.rgb24.ref.png [new file with mode: 0644]
test/reference/text-glyph-range.image16.ref.png [new file with mode: 0644]
test/reference/text-glyph-range.ps.ref.png [new file with mode: 0644]
test/reference/text-glyph-range.quartz.ref.png [new file with mode: 0644]
test/reference/text-glyph-range.ref.png [new file with mode: 0644]
test/reference/text-glyph-range.traps.argb32.ref.png [new file with mode: 0644]
test/reference/text-glyph-range.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/text-pattern.base.argb32.ref.png [new file with mode: 0644]
test/reference/text-pattern.base.rgb24.ref.png [new file with mode: 0644]
test/reference/text-pattern.pdf.argb32.ref.png [new file with mode: 0644]
test/reference/text-pattern.pdf.rgb24.ref.png [new file with mode: 0644]
test/reference/text-pattern.ps2.argb32.ref.png [new file with mode: 0644]
test/reference/text-pattern.ps2.rgb24.ref.png [new file with mode: 0644]
test/reference/text-pattern.ps3.argb32.ref.png [new file with mode: 0644]
test/reference/text-pattern.ps3.rgb24.ref.png [new file with mode: 0644]
test/reference/text-pattern.quartz.argb32.ref.png [new file with mode: 0644]
test/reference/text-pattern.quartz.rgb24.ref.png [new file with mode: 0644]
test/reference/text-pattern.ref.png [new file with mode: 0644]
test/reference/text-pattern.svg.argb32.ref.png [new file with mode: 0644]
test/reference/text-pattern.svg.rgb24.ref.png [new file with mode: 0644]
test/reference/text-pattern.traps.argb32.ref.png [new file with mode: 0644]
test/reference/text-pattern.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/text-rotate.base.argb32.ref.png [new file with mode: 0644]
test/reference/text-rotate.base.rgb24.ref.png [new file with mode: 0644]
test/reference/text-rotate.image16.ref.png [new file with mode: 0644]
test/reference/text-rotate.pdf.ref.png [new file with mode: 0644]
test/reference/text-rotate.ps.ref.png [new file with mode: 0644]
test/reference/text-rotate.quartz.ref.png [new file with mode: 0644]
test/reference/text-rotate.ref.png [new file with mode: 0644]
test/reference/text-rotate.svg.ref.png [new file with mode: 0644]
test/reference/text-rotate.traps.argb32.ref.png [new file with mode: 0644]
test/reference/text-rotate.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/text-rotate.xlib-fallback.ref.png [new file with mode: 0644]
test/reference/text-transform.base.argb32.ref.png [new file with mode: 0644]
test/reference/text-transform.base.rgb24.ref.png [new file with mode: 0644]
test/reference/text-transform.image16.ref.png [new file with mode: 0644]
test/reference/text-transform.pdf.argb32.ref.png [new file with mode: 0644]
test/reference/text-transform.pdf.rgb24.ref.png [new file with mode: 0644]
test/reference/text-transform.ps2.ref.png [new file with mode: 0644]
test/reference/text-transform.ps3.ref.png [new file with mode: 0644]
test/reference/text-transform.ref.png [new file with mode: 0644]
test/reference/text-transform.svg.ref.png [new file with mode: 0644]
test/reference/text-transform.traps.argb32.ref.png [new file with mode: 0644]
test/reference/text-transform.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/tiger.base.argb32.ref.png [new file with mode: 0644]
test/reference/tiger.base.rgb24.ref.png [new file with mode: 0644]
test/reference/tiger.ref.png [new file with mode: 0644]
test/reference/tiger.traps.argb32.ref.png [new file with mode: 0644]
test/reference/tiger.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/tighten-bounds.argb32.ref.png [new file with mode: 0644]
test/reference/tighten-bounds.base.argb32.ref.png [new file with mode: 0644]
test/reference/tighten-bounds.base.rgb24.ref.png [new file with mode: 0644]
test/reference/tighten-bounds.rgb24.ref.png [new file with mode: 0644]
test/reference/tighten-bounds.traps.argb32.ref.png [new file with mode: 0644]
test/reference/tighten-bounds.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/transforms.base.argb32.ref.png [new file with mode: 0644]
test/reference/transforms.base.rgb24.ref.png [new file with mode: 0644]
test/reference/transforms.image16.ref.png [new file with mode: 0644]
test/reference/transforms.ps2.ref.png [new file with mode: 0644]
test/reference/transforms.ps3.ref.png [new file with mode: 0644]
test/reference/transforms.ref.png [new file with mode: 0644]
test/reference/transforms.traps.argb32.ref.png [new file with mode: 0644]
test/reference/transforms.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/translate-show-surface.base.argb32.ref.png [new file with mode: 0644]
test/reference/translate-show-surface.base.rgb24.ref.png [new file with mode: 0644]
test/reference/translate-show-surface.ref.png [new file with mode: 0644]
test/reference/translate-show-surface.traps.argb32.ref.png [new file with mode: 0644]
test/reference/translate-show-surface.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/trap-clip.base.argb32.ref.png [new file with mode: 0644]
test/reference/trap-clip.base.rgb24.ref.png [new file with mode: 0644]
test/reference/trap-clip.image16.ref.png [new file with mode: 0644]
test/reference/trap-clip.ps2.argb32.ref.png [new file with mode: 0644]
test/reference/trap-clip.ps2.rgb24.ref.png [new file with mode: 0644]
test/reference/trap-clip.ps3.argb32.ref.png [new file with mode: 0644]
test/reference/trap-clip.ps3.rgb24.ref.png [new file with mode: 0644]
test/reference/trap-clip.quartz.argb32.ref.png [new file with mode: 0644]
test/reference/trap-clip.quartz.rgb24.ref.png [new file with mode: 0644]
test/reference/trap-clip.ref.png [new file with mode: 0644]
test/reference/trap-clip.rgb24.ref.png [new file with mode: 0644]
test/reference/trap-clip.test-paginated.argb32.ref.png [new file with mode: 0644]
test/reference/trap-clip.traps.argb32.ref.png [new file with mode: 0644]
test/reference/trap-clip.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/twin-antialias-gray.base.argb32.ref.png [new file with mode: 0644]
test/reference/twin-antialias-gray.base.rgb24.ref.png [new file with mode: 0644]
test/reference/twin-antialias-gray.image16.ref.png [new file with mode: 0644]
test/reference/twin-antialias-gray.ref.png [new file with mode: 0644]
test/reference/twin-antialias-gray.traps.argb32.ref.png [new file with mode: 0644]
test/reference/twin-antialias-gray.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/twin-antialias-mixed.base.argb32.ref.png [new file with mode: 0644]
test/reference/twin-antialias-mixed.base.rgb24.ref.png [new file with mode: 0644]
test/reference/twin-antialias-mixed.image16.ref.png [new file with mode: 0644]
test/reference/twin-antialias-mixed.ref.png [new file with mode: 0644]
test/reference/twin-antialias-mixed.traps.argb32.ref.png [new file with mode: 0644]
test/reference/twin-antialias-mixed.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/twin-antialias-none.base.argb32.ref.png [new file with mode: 0644]
test/reference/twin-antialias-none.base.rgb24.ref.png [new file with mode: 0644]
test/reference/twin-antialias-none.ref.png [new file with mode: 0644]
test/reference/twin-antialias-none.traps.argb32.ref.png [new file with mode: 0644]
test/reference/twin-antialias-none.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/twin-antialias-subpixel.base.argb32.ref.png [new file with mode: 0644]
test/reference/twin-antialias-subpixel.base.rgb24.ref.png [new file with mode: 0644]
test/reference/twin-antialias-subpixel.image16.ref.png [new file with mode: 0644]
test/reference/twin-antialias-subpixel.ref.png [new file with mode: 0644]
test/reference/twin-antialias-subpixel.traps.argb32.ref.png [new file with mode: 0644]
test/reference/twin-antialias-subpixel.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/twin.base.argb32.ref.png [new file with mode: 0644]
test/reference/twin.base.rgb24.ref.png [new file with mode: 0644]
test/reference/twin.image16.ref.png [new file with mode: 0644]
test/reference/twin.ps.ref.png [new file with mode: 0644]
test/reference/twin.ref.png [new file with mode: 0644]
test/reference/twin.svg.ref.png [new file with mode: 0644]
test/reference/twin.traps.argb32.ref.png [new file with mode: 0644]
test/reference/twin.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/unaligned-box.base.argb32.ref.png [new file with mode: 0644]
test/reference/unaligned-box.base.rgb24.ref.png [new file with mode: 0644]
test/reference/unaligned-box.ref.png [new file with mode: 0644]
test/reference/unaligned-box.traps.argb32.ref.png [new file with mode: 0644]
test/reference/unaligned-box.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/unantialiased-shapes.base.argb32.ref.png [new file with mode: 0644]
test/reference/unantialiased-shapes.base.rgb24.ref.png [new file with mode: 0644]
test/reference/unantialiased-shapes.quartz.ref.png [new file with mode: 0644]
test/reference/unantialiased-shapes.ref.png [new file with mode: 0644]
test/reference/unantialiased-shapes.traps.argb32.ref.png [new file with mode: 0644]
test/reference/unantialiased-shapes.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/unbounded-operator.argb32.ref.png [new file with mode: 0644]
test/reference/unbounded-operator.base.argb32.ref.png [new file with mode: 0644]
test/reference/unbounded-operator.base.rgb24.ref.png [new file with mode: 0644]
test/reference/unbounded-operator.gl.argb32.xfail.png [new file with mode: 0644]
test/reference/unbounded-operator.gl.rgb24.xfail.png [new file with mode: 0644]
test/reference/unbounded-operator.image16.ref.png [new file with mode: 0644]
test/reference/unbounded-operator.pdf.argb32.ref.png [new file with mode: 0644]
test/reference/unbounded-operator.ps2.argb32.ref.png [new file with mode: 0644]
test/reference/unbounded-operator.ps3.argb32.ref.png [new file with mode: 0644]
test/reference/unbounded-operator.quartz.argb32.ref.png [new file with mode: 0644]
test/reference/unbounded-operator.quartz.rgb24.ref.png [new file with mode: 0644]
test/reference/unbounded-operator.rgb24.ref.png [new file with mode: 0644]
test/reference/unbounded-operator.svg12.argb32.ref.png [new file with mode: 0644]
test/reference/unbounded-operator.svg12.rgb24.xfail.png [new file with mode: 0644]
test/reference/unbounded-operator.traps.argb32.ref.png [new file with mode: 0644]
test/reference/unbounded-operator.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/unclosed-strokes.base.argb32.ref.png [new file with mode: 0644]
test/reference/unclosed-strokes.base.rgb24.ref.png [new file with mode: 0644]
test/reference/unclosed-strokes.ref.png [new file with mode: 0644]
test/reference/unclosed-strokes.traps.argb32.ref.png [new file with mode: 0644]
test/reference/unclosed-strokes.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/user-font-mask.base.argb32.ref.png [new file with mode: 0644]
test/reference/user-font-mask.base.rgb24.ref.png [new file with mode: 0644]
test/reference/user-font-mask.image16.ref.png [new file with mode: 0644]
test/reference/user-font-mask.pdf.ref.png [new file with mode: 0644]
test/reference/user-font-mask.ps2.ref.png [new file with mode: 0644]
test/reference/user-font-mask.ps3.ref.png [new file with mode: 0644]
test/reference/user-font-mask.ref.png [new file with mode: 0644]
test/reference/user-font-mask.svg.ref.png [new file with mode: 0644]
test/reference/user-font-mask.traps.argb32.ref.png [new file with mode: 0644]
test/reference/user-font-mask.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/user-font-proxy.base.argb32.ref.png [new file with mode: 0644]
test/reference/user-font-proxy.base.rgb24.ref.png [new file with mode: 0644]
test/reference/user-font-proxy.image16.ref.png [new file with mode: 0644]
test/reference/user-font-proxy.pdf.argb32.ref.png [new file with mode: 0644]
test/reference/user-font-proxy.pdf.ref.png [new file with mode: 0644]
test/reference/user-font-proxy.pdf.rgb24.ref.png [new file with mode: 0644]
test/reference/user-font-proxy.ps.ref.png [new file with mode: 0644]
test/reference/user-font-proxy.quartz.ref.png [new file with mode: 0644]
test/reference/user-font-proxy.ref.png [new file with mode: 0644]
test/reference/user-font-proxy.svg.ref.png [new file with mode: 0644]
test/reference/user-font-proxy.traps.argb32.ref.png [new file with mode: 0644]
test/reference/user-font-proxy.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/user-font-rescale.base.argb32.ref.png [new file with mode: 0644]
test/reference/user-font-rescale.base.rgb24.ref.png [new file with mode: 0644]
test/reference/user-font-rescale.image16.ref.png [new file with mode: 0644]
test/reference/user-font-rescale.ps.ref.png [new file with mode: 0644]
test/reference/user-font-rescale.quartz.ref.png [new file with mode: 0644]
test/reference/user-font-rescale.ref.png [new file with mode: 0644]
test/reference/user-font-rescale.svg.ref.png [new file with mode: 0644]
test/reference/user-font-rescale.traps.argb32.ref.png [new file with mode: 0644]
test/reference/user-font-rescale.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/user-font.base.argb32.ref.png [new file with mode: 0644]
test/reference/user-font.base.rgb24.ref.png [new file with mode: 0644]
test/reference/user-font.image16.ref.png [new file with mode: 0644]
test/reference/user-font.pdf.ref.png [new file with mode: 0644]
test/reference/user-font.ps.ref.png [new file with mode: 0644]
test/reference/user-font.quartz.ref.png [new file with mode: 0644]
test/reference/user-font.ref.png [new file with mode: 0644]
test/reference/user-font.svg.ref.png [new file with mode: 0644]
test/reference/user-font.traps.argb32.ref.png [new file with mode: 0644]
test/reference/user-font.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/white-in-noop.base.argb32.ref.png [new file with mode: 0644]
test/reference/white-in-noop.base.rgb24.ref.png [new file with mode: 0644]
test/reference/white-in-noop.ref.png [new file with mode: 0644]
test/reference/white-in-noop.traps.argb32.ref.png [new file with mode: 0644]
test/reference/white-in-noop.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/world-map-fill.base.argb32.ref.png [new file with mode: 0644]
test/reference/world-map-fill.base.rgb24.ref.png [new file with mode: 0644]
test/reference/world-map-fill.image16.ref.png [new file with mode: 0644]
test/reference/world-map-fill.ref.png [new file with mode: 0644]
test/reference/world-map-fill.traps.argb32.ref.png [new file with mode: 0644]
test/reference/world-map-fill.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/world-map-stroke.base.argb32.ref.png [new file with mode: 0644]
test/reference/world-map-stroke.base.rgb24.ref.png [new file with mode: 0644]
test/reference/world-map-stroke.image16.ref.png [new file with mode: 0644]
test/reference/world-map-stroke.ref.png [new file with mode: 0644]
test/reference/world-map-stroke.traps.argb32.ref.png [new file with mode: 0644]
test/reference/world-map-stroke.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/world-map.base.argb32.ref.png [new file with mode: 0644]
test/reference/world-map.base.rgb24.ref.png [new file with mode: 0644]
test/reference/world-map.image16.ref.png [new file with mode: 0644]
test/reference/world-map.ref.png [new file with mode: 0644]
test/reference/world-map.traps.argb32.ref.png [new file with mode: 0644]
test/reference/world-map.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/xcb-huge-image-shm.base.argb32.ref.png [new file with mode: 0644]
test/reference/xcb-huge-image-shm.base.rgb24.ref.png [new file with mode: 0644]
test/reference/xcb-huge-image-shm.ref.png [new file with mode: 0644]
test/reference/xcb-huge-image-shm.traps.argb32.ref.png [new file with mode: 0644]
test/reference/xcb-huge-image-shm.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/xcb-snapshot-assert.base.argb32.ref.png [new file with mode: 0644]
test/reference/xcb-snapshot-assert.base.rgb24.ref.png [new file with mode: 0644]
test/reference/xcb-snapshot-assert.ref.png [new file with mode: 0644]
test/reference/xcb-snapshot-assert.traps.argb32.ref.png [new file with mode: 0644]
test/reference/xcb-snapshot-assert.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/xcb-stress-cache.base.argb32.ref.png [new file with mode: 0644]
test/reference/xcb-stress-cache.base.rgb24.ref.png [new file with mode: 0644]
test/reference/xcb-stress-cache.ref.png [new file with mode: 0644]
test/reference/xcb-stress-cache.traps.argb32.ref.png [new file with mode: 0644]
test/reference/xcb-stress-cache.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/xcb-surface-source.argb32.ref.png [new file with mode: 0644]
test/reference/xcb-surface-source.base.argb32.ref.png [new file with mode: 0644]
test/reference/xcb-surface-source.base.rgb24.ref.png [new file with mode: 0644]
test/reference/xcb-surface-source.image16.ref.png [new file with mode: 0644]
test/reference/xcb-surface-source.ps.argb32.ref.png [new file with mode: 0644]
test/reference/xcb-surface-source.ps.rgb24.ref.png [new file with mode: 0644]
test/reference/xcb-surface-source.rgb24.ref.png [new file with mode: 0644]
test/reference/xcb-surface-source.traps.argb32.ref.png [new file with mode: 0644]
test/reference/xcb-surface-source.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/xcomposite-projection.base.argb32.ref.png [new file with mode: 0644]
test/reference/xcomposite-projection.base.rgb24.ref.png [new file with mode: 0644]
test/reference/xcomposite-projection.image16.ref.png [new file with mode: 0644]
test/reference/xcomposite-projection.quartz.ref.png [new file with mode: 0644]
test/reference/xcomposite-projection.ref.png [new file with mode: 0644]
test/reference/xcomposite-projection.traps.argb32.ref.png [new file with mode: 0644]
test/reference/xcomposite-projection.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/xlib-expose-event.base.argb32.ref.png [new file with mode: 0644]
test/reference/xlib-expose-event.base.rgb24.ref.png [new file with mode: 0644]
test/reference/xlib-expose-event.image16.ref.png [new file with mode: 0644]
test/reference/xlib-expose-event.ps.ref.png [new file with mode: 0644]
test/reference/xlib-expose-event.ref.png [new file with mode: 0644]
test/reference/xlib-expose-event.traps.argb32.ref.png [new file with mode: 0644]
test/reference/xlib-expose-event.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/xlib-surface-source.argb32.ref.png [new file with mode: 0644]
test/reference/xlib-surface-source.base.argb32.ref.png [new file with mode: 0644]
test/reference/xlib-surface-source.base.rgb24.ref.png [new file with mode: 0644]
test/reference/xlib-surface-source.image16.ref.png [new file with mode: 0644]
test/reference/xlib-surface-source.ps.argb32.ref.png [new file with mode: 0644]
test/reference/xlib-surface-source.ps.rgb24.ref.png [new file with mode: 0644]
test/reference/xlib-surface-source.rgb24.ref.png [new file with mode: 0644]
test/reference/xlib-surface-source.svg12.argb32.xfail.png [new file with mode: 0644]
test/reference/xlib-surface-source.svg12.rgb24.xfail.png [new file with mode: 0644]
test/reference/xlib-surface-source.traps.argb32.ref.png [new file with mode: 0644]
test/reference/xlib-surface-source.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/zero-alpha.base.argb32.ref.png [new file with mode: 0644]
test/reference/zero-alpha.base.rgb24.ref.png [new file with mode: 0644]
test/reference/zero-alpha.ref.png [new file with mode: 0644]
test/reference/zero-alpha.traps.argb32.ref.png [new file with mode: 0644]
test/reference/zero-alpha.traps.rgb24.ref.png [new file with mode: 0644]
test/reference/zero-mask.base.argb32.ref.png [new file with mode: 0644]
test/reference/zero-mask.base.rgb24.ref.png [new file with mode: 0644]
test/reference/zero-mask.ref.png [new file with mode: 0644]
test/reference/zero-mask.rgb24.ref.png [new file with mode: 0644]
test/reference/zero-mask.traps.argb32.ref.png [new file with mode: 0644]
test/reference/zero-mask.traps.rgb24.ref.png [new file with mode: 0644]
test/reflected-stroke.c [new file with mode: 0644]
test/rel-path.c [new file with mode: 0644]
test/rgb24-ignore-alpha.c [new file with mode: 0644]
test/romedalen.jpg [new file with mode: 0644]
test/romedalen.png [new file with mode: 0644]
test/rotate-image-surface-paint.c [new file with mode: 0644]
test/rotated-clip.c [new file with mode: 0644]
test/rounded-rectangle-fill.c [new file with mode: 0644]
test/rounded-rectangle-stroke.c [new file with mode: 0644]
test/run-cairo-test-suite.sh [new file with mode: 0755]
test/scale-down-source-surface-paint.c [new file with mode: 0644]
test/scale-offset-image.c [new file with mode: 0644]
test/scale-offset-similar.c [new file with mode: 0644]
test/scale-source-surface-paint.c [new file with mode: 0644]
test/scaled-font-zero-matrix.c [new file with mode: 0644]
test/scarab.jpg [new file with mode: 0644]
test/select-font-face.c [new file with mode: 0644]
test/select-font-no-show-text.c [new file with mode: 0644]
test/self-copy-overlap.c [new file with mode: 0644]
test/self-copy.c [new file with mode: 0644]
test/self-intersecting.c [new file with mode: 0644]
test/set-source.c [new file with mode: 0644]
test/shape-general-convex.c [new file with mode: 0644]
test/shape-sierpinski.c [new file with mode: 0644]
test/show-glyphs-advance.c [new file with mode: 0644]
test/show-glyphs-many.c [new file with mode: 0644]
test/show-text-current-point.c [new file with mode: 0644]
test/skew-extreme.c [new file with mode: 0644]
test/smask-fill.c [new file with mode: 0644]
test/smask-image-mask.c [new file with mode: 0644]
test/smask-mask.c [new file with mode: 0644]
test/smask-paint.c [new file with mode: 0644]
test/smask-stroke.c [new file with mode: 0644]
test/smask-text.c [new file with mode: 0644]
test/smask.c [new file with mode: 0644]
test/solid-pattern-cache-stress.c [new file with mode: 0644]
test/source-clip-scale.c [new file with mode: 0644]
test/source-clip.c [new file with mode: 0644]
test/source-surface-scale-paint.c [new file with mode: 0644]
test/spline-decomposition.c [new file with mode: 0644]
test/stroke-ctm-caps.c [new file with mode: 0644]
test/stroke-image.c [new file with mode: 0644]
test/stroke-open-box.c [new file with mode: 0644]
test/stroke-pattern.c [new file with mode: 0644]
test/subsurface-image-repeat.c [new file with mode: 0644]
test/subsurface-modify-child.c [new file with mode: 0644]
test/subsurface-modify-parent.c [new file with mode: 0644]
test/subsurface-outside-target.c [new file with mode: 0644]
test/subsurface-pad.c [new file with mode: 0644]
test/subsurface-reflect.c [new file with mode: 0644]
test/subsurface-repeat.c [new file with mode: 0644]
test/subsurface-scale.c [new file with mode: 0644]
test/subsurface-similar-repeat.c [new file with mode: 0644]
test/subsurface.c [new file with mode: 0644]
test/surface-finish-twice.c [new file with mode: 0644]
test/surface-pattern-big-scale-down.c [new file with mode: 0644]
test/surface-pattern-operator.c [new file with mode: 0644]
test/surface-pattern-scale-down-extend.c [new file with mode: 0644]
test/surface-pattern-scale-down.c [new file with mode: 0644]
test/surface-pattern-scale-up.c [new file with mode: 0644]
test/surface-pattern.c [new file with mode: 0644]
test/surface-source.c [new file with mode: 0644]
test/svg-clip.c [new file with mode: 0644]
test/svg-surface-source.c [new file with mode: 0644]
test/svg-surface.c [new file with mode: 0644]
test/svg2png.c [new file with mode: 0644]
test/testsvg [new file with mode: 0755]
test/testtable.js [new file with mode: 0644]
test/text-antialias.c [new file with mode: 0644]
test/text-cache-crash.c [new file with mode: 0644]
test/text-glyph-range.c [new file with mode: 0644]
test/text-pattern.c [new file with mode: 0644]
test/text-rotate.c [new file with mode: 0644]
test/text-transform.c [new file with mode: 0644]
test/text-zero-len.c [new file with mode: 0644]
test/tiger.c [new file with mode: 0644]
test/tiger.inc [new file with mode: 0644]
test/tighten-bounds.c [new file with mode: 0644]
test/toy-font-face.c [new file with mode: 0644]
test/transforms.c [new file with mode: 0644]
test/translate-show-surface.c [new file with mode: 0644]
test/trap-clip.c [new file with mode: 0644]
test/twin-antialias-gray.c [new file with mode: 0644]
test/twin-antialias-mixed.c [new file with mode: 0644]
test/twin-antialias-none.c [new file with mode: 0644]
test/twin-antialias-subpixel.c [new file with mode: 0644]
test/twin.c [new file with mode: 0644]
test/unaligned-box.c [new file with mode: 0644]
test/unantialiased-shapes.c [new file with mode: 0644]
test/unbounded-operator.c [new file with mode: 0644]
test/unclosed-strokes.c [new file with mode: 0644]
test/user-data.c [new file with mode: 0644]
test/user-font-mask.c [new file with mode: 0644]
test/user-font-proxy.c [new file with mode: 0644]
test/user-font-rescale.c [new file with mode: 0644]
test/user-font.c [new file with mode: 0644]
test/white-in-noop.c [new file with mode: 0644]
test/world-map.c [new file with mode: 0644]
test/world-map.h [new file with mode: 0644]
test/xcb-huge-image-shm.c [new file with mode: 0644]
test/xcb-snapshot-assert.c [new file with mode: 0644]
test/xcb-stress-cache.c [new file with mode: 0644]
test/xcb-surface-source.c [new file with mode: 0644]
test/xcomposite-projection.c [new file with mode: 0644]
test/xlib-expose-event.c [new file with mode: 0644]
test/xlib-surface-source.c [new file with mode: 0644]
test/xlib-surface.c [new file with mode: 0644]
test/zero-alpha.c [new file with mode: 0644]
test/zero-mask.c [new file with mode: 0644]
util/.gitignore [new file with mode: 0644]
util/COPYING [new file with mode: 0644]
util/Makefile.am [new file with mode: 0644]
util/README [new file with mode: 0644]
util/backtrace-symbols.c [new file with mode: 0644]
util/cairo-api-update [new file with mode: 0755]
util/cairo-fdr/Makefile.am [new file with mode: 0644]
util/cairo-fdr/fdr.c [new file with mode: 0644]
util/cairo-gobject/Makefile.am [new file with mode: 0644]
util/cairo-gobject/cairo-gobject-enums.c [new file with mode: 0644]
util/cairo-gobject/cairo-gobject-structs.c [new file with mode: 0644]
util/cairo-gobject/cairo-gobject.h [new file with mode: 0644]
util/cairo-missing/Makefile.am [new file with mode: 0644]
util/cairo-missing/Makefile.sources [new file with mode: 0644]
util/cairo-missing/Makefile.win32 [new file with mode: 0644]
util/cairo-missing/cairo-missing.h [new file with mode: 0644]
util/cairo-missing/getline.c [new file with mode: 0644]
util/cairo-missing/strndup.c [new file with mode: 0644]
util/cairo-script/.gitignore [new file with mode: 0644]
util/cairo-script/COPYING [new file with mode: 0644]
util/cairo-script/Makefile.am [new file with mode: 0644]
util/cairo-script/Makefile.sources [new file with mode: 0644]
util/cairo-script/Makefile.win32 [new file with mode: 0644]
util/cairo-script/cairo-script-file.c [new file with mode: 0644]
util/cairo-script/cairo-script-hash.c [new file with mode: 0644]
util/cairo-script/cairo-script-interpreter.c [new file with mode: 0644]
util/cairo-script/cairo-script-interpreter.h [new file with mode: 0644]
util/cairo-script/cairo-script-objects.c [new file with mode: 0644]
util/cairo-script/cairo-script-operators.c [new file with mode: 0644]
util/cairo-script/cairo-script-private.h [new file with mode: 0644]
util/cairo-script/cairo-script-scanner.c [new file with mode: 0644]
util/cairo-script/cairo-script-stack.c [new file with mode: 0644]
util/cairo-script/csi-bind.c [new file with mode: 0644]
util/cairo-script/csi-exec.c [new file with mode: 0644]
util/cairo-script/csi-replay.c [new file with mode: 0644]
util/cairo-script/csi-trace.c [new file with mode: 0644]
util/cairo-script/examples/Makefile.am [new file with mode: 0644]
util/cairo-script/examples/dragon.cs [new file with mode: 0644]
util/cairo-script/examples/hilbert.cs [new file with mode: 0644]
util/cairo-script/examples/infinichess.cs [new file with mode: 0644]
util/cairo-script/examples/interference.cs [new file with mode: 0644]
util/cairo-script/examples/pythagoras-tree.cs [new file with mode: 0644]
util/cairo-script/examples/sierpinski.cs [new file with mode: 0644]
util/cairo-script/examples/wedgeAnnulus_crop_ybRings.cs [new file with mode: 0644]
util/cairo-script/examples/world-map.cs [new file with mode: 0644]
util/cairo-script/examples/zrusin.cs [new file with mode: 0644]
util/cairo-sphinx/.gitignore [new file with mode: 0644]
util/cairo-sphinx/Makefile.am [new file with mode: 0644]
util/cairo-sphinx/fdr.c [new file with mode: 0644]
util/cairo-sphinx/sphinx.c [new file with mode: 0644]
util/cairo-trace/.gitignore [new file with mode: 0644]
util/cairo-trace/COPYING [new file with mode: 0644]
util/cairo-trace/COPYING-GPL-3 [new file with mode: 0644]
util/cairo-trace/Makefile.am [new file with mode: 0644]
util/cairo-trace/cairo-trace.in [new file with mode: 0644]
util/cairo-trace/lookup-symbol.c [new file with mode: 0644]
util/cairo-trace/lookup-symbol.h [new file with mode: 0644]
util/cairo-trace/trace.c [new file with mode: 0644]
util/cairo-view [new file with mode: 0755]
util/cairo.modules [new file with mode: 0644]
util/font-view.c [new file with mode: 0644]
util/malloc-stats.c [new file with mode: 0644]
util/show-contour.c [new file with mode: 0644]
util/show-edges.c [new file with mode: 0644]
util/show-events.c [new file with mode: 0644]
util/show-polygon.c [new file with mode: 0644]
util/show-traps.c [new file with mode: 0644]
util/trace-to-xml.c [new file with mode: 0644]
util/waterfall [new file with mode: 0755]
util/xml-to-trace.c [new file with mode: 0644]
util/xr2cairo [new file with mode: 0755]

diff --git a/AUTHORS b/AUTHORS
new file mode 100644 (file)
index 0000000..a977e73
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,111 @@
+Josh Aas <joshmoz@gmail.com> Memory leak fix for quartz backend
+Daniel Amelang <dan@amelang.net> Many (magic) floating-point optimizations
+Shawn T. Amundson <amundson@gtk.org> Build fix
+Olivier Andrieu <oliv__a@users.sourceforge.net> PNG backend
+Peter Dennis Bartok <peter@novonyx.com> Bug fix for clipping
+Dave Beckett <dajobe@debian.org> Build fixes, Debian packaging
+Kai-Uwe Behrmann <ku.b@gmx.de> SVG bug fixes
+Christian Biesinger <cbiesinger@web.de> BeOS backend
+Billy Biggs <vektor@dumbterm.net> Pixman code merge. Optimization. Fixes for subtle rendering bugs.
+Hans Breuer <hans@breuer.org> win32 bug fixes, build fixes, and improvements
+Brian Cameron <brian.cameron@sun.com> Flag bug in Sun's X server
+Carlos Garcia Campos <carlosgc@gnome.org> libspectre integration into the test-suite
+Andrea Canciani <ranma42@gmail.com> Bugs, quartz backend improvements and type 6/7 patterns.
+Damien Carbery <damien.carbery@sun.com> Build fixes
+Andrew Chant <andrew.chant@utoronto.ca> Adding const where needed
+Steve Chaplin <stevech1097@yahoo.com.au> Bug fixes for PNG reading
+Tomasz Cholewo <cholewo@ieee-cis.org> Bug fixes
+Manu Cornet <manu@manucornet.net> SVG build fix
+Frederic Crozat <fcrozat@mandriva.com> Fix test suite for OPD platforms (IA64 or PPC64)
+Julien Danjou <julien@danjou.info> XCB fixes
+Radek Doulík <rodo@novell.com> Bug report and test case
+John Ehresman <jpe@wingide.com> Build fixes for win32
+John Ellson <ellson@research.att.com> First font/glyph extents functions
+Michael Emmel <mike.emmel@gmail.com> DirectFB backend
+Miklós Erdélyi <erdelyim@gmail.com> Fix typo leading to a crash
+Behdad Esfahbod <behdad@behdad.org> Huge piles of bug fixes, improvements, and general maintenance
+Larry Ewing <lewing@novell.com> Test case for group-clip
+Brian Ewins <Brian.Ewins@gmail.com> ATSUI maintenance (first success at making it really work)
+Bertram Felgenhauer <int-e@gmx.de> Fixes for subtle arithmetic errors
+Damian Frank <damian.frank@gmail.com> Build system improvements for win32
+Bdale Garbee <bdale@gag.com> Provided essential support for cairo achitecture sessions
+Jens Granseuer <jensgr@gmx.net> Fixes to generate proper compiler flags
+Laxmi Harikumar <laxmi.harikumar@digital.com> Build fix
+J. Ali Harlow <ali@avrc.city.ac.uk> win32 backend updates
+Mathias Hasselmann <mathias.hasselmann@gmx.de> Significant reduction of calls to malloc
+Richard Henderson <rth@twiddle.net> "slim" macros for better shared libraries 
+James Henstridge <james@daa.com.au> Build fixes related to freetype
+Graydon Hoare <graydon@redhat.com> Support for non-render X server, first real text support
+Thomas Hunger <info@teh-web.de> Initial version of cairo_in_stroke/fill
+Thomas Jaeger <ThJaeger@gmail.com> Extended repeat modes for X
+Björn Lindqvist <bjourne@gmail.com> Performance test cases
+Kristian Høgsberg <krh@redhat.com> PDF backend, PS backend with meta-surfaces
+Amaury Jacquot <sxpert@esitcom.org> Documentation review, appplication testing
+Adrian Johnson <ajohnson@redneon.com> PDF backend improvement
+Michael Johnson <ahze@ahze.net> Bug fix for pre-C99 compilers
+Jonathon Jongsma <jonathon.jongsma@gmail.com> Fix documentation typos
+Øyvind Kolås <pippin@freedesktop.org> OpenVG backend, Bug fixes. Better default values.
+Martin Kretzschmar <martink@gnome.org> Arithmetic fix for 64-bit architectures
+Mathieu Lacage <Mathieu.Lacage@sophia.inria.fr> several bug/typo fixes
+Dominic Lachowicz <domlachowicz@gmail.com> PDF conformance fix, fix image surface to zero out contents
+Alexander Larsson <alexl@redhat.com> Profiling and performance fixes.
+Tor Lillqvist <tml@novell.com> win32 build fixes, build scripts
+Jinghua Luo <sunmoon1997@gmail.com> Add bitmap glyph transformation, many freetype and glitz fixes
+Luke-Jr <luke-jr@utopios.org> Build fix for cross-compiling
+Kjartan Maraas <kmaraas@gnome.org> Several fixes for sparse, lots of debug help for multi-thread bugs
+Nis Martensen <nis.martensen@web.de> Bug fix for sub paths
+Jordi Mas <jordi@ximian.com> Bug fix for cairo_show_text
+Nicholas Miell <nmiell@gmail.com> Fixes for linking bugs on AMD64
+Eugeniy Meshcheryakov <eugen@debian.org> PS/PDF font subsetting improvements
+Zakharov Mikhail <zmey20000@yahoo.com> Build fix for HP-UX
+Christopher (Monty) Montgomery <xiphmont@gmail.com> Performnace fix (subimage_copy), multi-thread testing
+Tim Mooney <enchanter@users.sourceforge.net> Fix test suite to compile with Solaris compiler
+Jeff Muizelaar <jeff@infidigm.net> Patient, painful, pixman code merge. Many fixes for intricacies of dashing.
+Yevgen Muntyan <muntyan@tamu.edu> win32 build fix
+Declan Naughton <piratepenguin@gmail.com> Fix documentation typos
+Peter Nilsson <c99pnn@cs.umu.se> Glitz backend
+Henning Noren <henning.noren.402@student.lu.se> Fix memory leak
+Geoff Norton <gnorton@customerdna.com> Build fixes
+Robert O'Callahan <rocallahan@novell.com> Const-correctness fixes, several new API functions for completeness (and to help mozilla)
+Ian Osgood <iano@quirkster.com> XCB backend maintenance
+Benjamin Otte <otte@gnome.org> Refinements to cairo/perf timing, OpenGL backend fixups, random fixes
+Mike Owens <etc@filespanker.com> Bug fixes
+Emmanuel Pacaud <emmanuel.pacaud@lapp.in2p3.fr> SVG backend
+Keith Packard <keithp@keithp.com> Original concept, polygon tessellation, dashing, font metrics rewrite
+Stuart Parmenter <pavlov@pavlov.net> Original GDI+ backend, win32 fixes
+Alfred Peng <alfred.peng@sun.com> Fixes for Sun compilers and for a memory leak
+Christof Petig <christof@petig-baender.de> Build fixes related to freetype
+Joonas Pihlaja <jpihlaja@cc.helsinki.fi> Huge improvements to the tessellator performance
+Mart Raudsepp <leio@dustbite.net> Build fixes
+David Reveman <davidr@novell.com> New pattern API, glitz backend
+Calum Robinson <calumr@mac.com> Quartz backend
+Pavel Roskin <proski@gnu.org> Several cleanups to eliminate warnings
+Tim Rowley <tim.rowley@gmail.com> Quartz/ATSUI fixes, X server workarounds, win32 glyph path support, test case to expose gradient regression
+Soeren Sandmann <sandmann@daimi.au.dk> Lots of MMX love for pixman compositing
+Uli Schlachter <psychon@znc.in> Some more XCB fixes
+Torsten Schönfeld <kaffeetisch@gmx.de> Build fixes
+Jamey Sharp <jamey@minilop.net> Surface/font backend virtualization, XCB backend
+Jason Dorje Short <jdorje@users.sf.net> Build fixes and bug fixes
+Jeff Smith <whydoubt@yahoo.com> Fixes for intricacies of stroking code
+Travis Spencer <tspencer@cs.pdx.edu> XCB backend fix
+Bill Spitzak <spitzak@d2.com> Build fix to find Xrender.h without xrender.pc
+Zhe Su <james.su@gmail.com> Add support for fontconfig's embeddedbitmap option
+Owen Taylor <otaylor@redhat.com> Font rewrite, documentation, win32 backend
+Pierre Tardy <tardyp@gmail.com> EGL support and testing, OpenVG backend
+Karl Tomlinson <karlt+@karlt.net> Optimisation and obscure bug fixes (mozilla)
+Alp Toker <alp@atoker.com> Fix several code/comment typos
+Malcolm Tredinnick <malcolm@commsecure.com.au> Documentation fixes
+David Turner <david@freetype.org> Optimize gradient calculations
+Kalle Vahlman <kalle.vahlman@gmail.com> Allow perf reports to be compared across different platforms
+Sasha Vasko <sasha@aftercode.net> Build fix to compile without xlib backend
+Vladimir Vukicevic <vladimir@pobox.com> Quartz backend rewrite, win32/quartz maintenance
+Jonathan Watt <jwatt@jwatt.org> win32 fixes
+Peter Weilbacher <pmw@avila.aip.de> OS/2 backend
+Dan Williams <dcbw@redhat.com> Implemnt MMX function to help OLPC
+Chris Wilson <chris@chris-wilson.co.uk> Large-scale robustness improvements, (warn_unsed_result and malloc failure injection)
+Carl Worth <cworth@isi.edu> Original library, support for paths, images
+Richard D. Worth <richard@theworths.org> Build fixes for cygwin
+Kent Worsnop <kworsnop@accesswave.ca> Fix PDF dashing bug
+Dave Yeo <daveryeo@telus.net> Build fix for win32
+
+(please let us know if we have missed anyone)
diff --git a/BIBLIOGRAPHY b/BIBLIOGRAPHY
new file mode 100644 (file)
index 0000000..90a6cef
--- /dev/null
@@ -0,0 +1,109 @@
+Here's an effort to document some of the academic work that was
+referenced during the implementation of cairo. It is presented in the
+context of operations as they would be performed by either
+cairo_stroke() or cairo_fill():
+
+Given a Bézier path, approximate it with line segments:
+
+       The deCasteljau algorithm
+       "Outillages methodes calcul", P de Casteljau, technical
+       report, - Andre Citroen Automobiles SA, Paris, 1959
+
+       That technical report might be "hard" to find, but fortunately
+       this algorithm will be described in any reasonable textbook on
+       computational geometry. Two that have been recommended by
+       cairo contributors are:
+
+       "Computational Geometry, Algorithms and Applications", M. de
+       Berg, M. van Kreveld, M. Overmars, M. Schwarzkopf;
+       Springer-Verlag, ISBN: 3-540-65620-0.
+
+       "Computational Geometry in C (Second Edition)", Joseph
+       O'Rourke, Cambridge University Press, ISBN 0521640105.
+
+Then, if stroking, construct a polygonal representation of the pen
+approximating a circle (if filling skip three steps):
+
+       "Good approximation of circles by curvature-continuous Bezier
+       curves", Tor Dokken and Morten Daehlen, Computer Aided
+       Geometric Design 8 (1990) 22-41.
+
+Add points to that pen based on the initial/final path faces and take
+the convex hull:
+
+       Convex hull algorithm
+
+       [Again, see your favorite computational geometry
+       textbook. Should cite the name of the algorithm cairo uses
+       here, if it has a name.]
+
+Now, "convolve" the "tracing" of the pen with the tracing of the path:
+
+       "A Kinetic Framework for Computational Geometry", Leonidas
+        J. Guibas, Lyle Ramshaw, and Jorge Stolfi, Proceedings of the
+        24th IEEE Annual Symposium on Foundations of Computer Science
+        (FOCS), November 1983, 100-111.
+
+The result of the convolution is a polygon that must be filled. A fill
+operations begins here. We use a very conventional Bentley-Ottmann
+pass for computing the intersections, informed by some hints on robust
+implementation courtesy of John Hobby:
+
+       John D. Hobby, Practical Segment Intersection with Finite
+       Precision Output, Computation Geometry Theory and
+       Applications, 13(4), 1999.
+
+       http://cm.bell-labs.com/who/hobby/93_2-27.pdf
+
+Hobby's primary contribution in that paper is his "tolerance square"
+algorithm for robustness against edges being "bent" due to restricting
+intersection coordinates to the grid available by finite-precision
+arithmetic. This is one algorithm we have not implemented yet.
+
+We use a data-structure called Skiplists in the our implementation
+of Bentley-Ottmann:
+
+       W. Pugh, Skip Lists: a Probabilistic Alternative to Balanced Trees,
+       Communications of the ACM, vol. 33, no. 6, pp.668-676, 1990.
+
+       http://citeseer.ist.psu.edu/pugh90skip.html
+
+The random number generator used in our skip list implementation is a
+very small generator by Hars and Petruska.  The generator is based on
+an invertable function on Z_{2^32} with full period and is described
+in
+
+       Hars L. and Petruska G.,
+       ``Pseudorandom Recursions: Small and Fast Pseurodandom
+         Number Generators for Embedded Applications'',
+       Hindawi Publishing Corporation
+       EURASIP Journal on Embedded Systems
+       Volume 2007, Article ID 98417, 13 pages
+       doi:10.1155/2007/98417
+
+       http://www.hindawi.com/getarticle.aspx?doi=10.1155/2007/98417&e=cta
+
+From the result of the intersection-finding pass, we are currently
+computing a tessellation of trapezoids, (the exact manner is
+undergoing some work right now with some important speedup), but we
+may want to rasterize directly from those edges at some point.
+
+Given the set of tessellated trapezoids, we currently execute a
+straightforward, (and slow), point-sampled rasterization, (and
+currently with a near-pessimal regular 15x17 grid).
+
+We've now computed a mask which gets fed along with the source and
+destination into cairo's fundamental rendering equation. The most
+basic form of this equation is:
+
+       destination = (source IN mask) OP destination
+
+with the restriction that no part of the destination outside the
+current clip region is affected. In this equation, IN refers to the
+Porter-Duff "in" operation, while OP refers to a any user-selected
+Porter-Duff operator:
+
+       T. Porter & T. Duff, Compositing Digital Images Computer
+       Graphics Volume 18, Number 3 July 1984 pp 253-259
+
+       http://keithp.com/~keithp/porterduff/p253-porter.pdf
diff --git a/BUGS b/BUGS
new file mode 100644 (file)
index 0000000..ef04404
--- /dev/null
+++ b/BUGS
@@ -0,0 +1,85 @@
+If you find a bug in cairo we would love to hear about it. We're also
+trying to make cairo better, and learning about the bugs that users
+encounter is an essential part of that. So we really appreciate the
+extra effort users put in to providing high-quality bug reports.
+
+There are two acceptable ways to report cairo bugs, and you can choose
+which you prefer:
+
+1) Bugzilla bug tracking database:
+
+   You can use the following web interface to report new bugs, follow
+   up on previous bug reports, and search for existing, known
+   bugs. Just use the "cairo" product:
+
+       http://bugs.freedesktop.org
+
+   It is necessary to go through a quick account creation process,
+   (with email address verification), in order to be able to report
+   new bugs in bugzilla. We apologize for any inconvenience that might
+   cause, and hope it won't prevent you from reporting bugs.
+
+2) Cairo mailing list:
+
+   For people who cannot stand the bugzilla interface, you can just
+   send an email to cairo mailing list (cairo@cairographics.org). The
+   mailing list only allows posting from subscribers, so use the
+   following page for subscription instructions:
+
+       http://cairographics.org/lists
+
+   Again, we apologize for any inconvenience this subscription step
+   might cause, but we've found it necessary to require this in order
+   to enjoy spam-free discussions on the list.
+
+   If you don't actually _want_ to be a subscriber to the mailing
+   list, but just want to be able to send a message, the easiest thing
+   to do is to go through the subscription process, and then use the
+   preferences page to disable message delivery to your address.
+
+Which of the above you use to report bugs depends on your own
+preferences. Some people find just typing an email message much easier
+than using the web-based forms on bugzilla. Others greatly prefer the
+ability to check back on a specific bug entry in bugzilla without
+having to ask on the mailing list if an issue has been resolved.
+
+Regardless of which method you use, here are some general tips that
+will help you improve the quality of your bug report, (which will help
+in getting the bug fixed sooner):
+
+1) Check to see if the bug has been reported already. It's pretty easy
+   to run a search or two against the cairo product in the
+   http://bugs.freedesktop.org bugzilla database. Another place to
+   look for known bugs is the cairo ROADMAP:
+
+       http://cairographics.org/ROADMAP
+
+   which shows a planned schedule of releases and which bug fixes are
+   being planned for each release.
+
+2) Provide an accurate description of the bug with detailed steps for
+   how we can reproduce the problem.
+
+3) If possible provide a minimal test case demonstrating the bug. A
+   great test case would be a minimal self-contained function in C or
+   python or whatever language you are using for cairo. The function
+   might accept nothing more than a cairo context, (cairo_t* in C).
+
+4) If you feel like being particularly helpful, you could craft this
+   minimal test case in the form necessary for cairo's test
+   suite. This isn't much more work than writing a minimal
+   function. Just look at the cairo/test/README file and imitate the
+   style of existing test cases.
+
+   If you do submit a test case, be sure to include Copyright
+   information, (with the standard MIT licensing blurb if you want us
+   to include your test in the test case). Also, including a reference
+   image showing the expected result will be extremely useful.
+
+5) Finally, the best bug report also comes attached with a patch to
+   cairo to fix the bug. So send this too if you have it! Otherwise,
+   don't worry about it and we'll try to fix cairo when we can.
+
+Thanks, and have fun with cairo!
+
+-Carl
diff --git a/CODING_STYLE b/CODING_STYLE
new file mode 100644 (file)
index 0000000..95ceac0
--- /dev/null
@@ -0,0 +1,291 @@
+Cairo coding style.
+
+This document is intended to be a short description of the preferred
+coding style for the cairo source code. Good style requires good
+taste, which means this can't all be reduced to automated rules, and
+there are exceptions.
+
+We want the code to be easy to understand and maintain, and consistent
+style plays an important part in that, even if some of the specific
+details seem trivial. If nothing else, this document gives a place to
+put consistent answers for issues that would otherwise be arbitrary.
+
+Most of the guidelines here are demonstrated by examples, (which means
+this document is quicker to read than it might appear given its
+length). Most of the examples are positive examples that you should
+imitate. The few negative examples are clearly marked with a comment
+of /* Yuck! */. Please don't submit code to cairo that looks like any
+of these.
+
+Indentation
+-----------
+Each new level is indented 4 more spaces than the previous level:
+
+       if (condition)
+           do_something ();
+
+This may be achieved with space characters or a combination of tab
+characters and space characters. It may not be achieved with tab
+characters exclusively (see below).
+
+Tab characters
+--------------
+The tab character must always be interpreted according to its
+traditional meaning:
+
+       Advance to the next column which is a multiple of 8.
+
+With this definition, even levels of indentation can be achieved with
+a sequence of tab characters, while odd levels of indentation may
+begin with a sequence of tab character but must end with 4 space
+characters.
+
+Some programmers have been misled by certain text editors into
+thinking that 4-space indentation can be achieved with tab characters
+exclusively by changing the meaning of tab character to be "advance to
+the next column which is a multiple of 4". Code formatted in this way,
+making an assumption of a fictitious 4-character-tab will not be
+accepted into cairo.
+
+The rationale here is that tabs are used in the code for lining things
+up other than indentation, (see the Whitespace section below), and
+changing the interpretation of tab from its traditional meaning will
+break this alignment.
+
+Braces
+------
+Most of the code in cairo uses bracing in the style of K&R:
+
+       if (condition) {
+           do_this ();
+           do_that ();
+       } else {
+           do_the_other ();
+       }
+
+but some of the code uses an alternate style:
+
+       if (condition)
+       {
+           do_this ();
+           do_that ();
+       }
+       else
+       {
+           do_the_other ();
+       }
+
+and that seems just fine. We won't lay down any strict rule on this
+point, (though there should be some local consistency). If you came
+here hoping to find some guidance, then use the first form above.
+
+If all of the substatements of an if statement are single statements,
+the optional braces should not usually appear:
+
+       if (condition)
+           do_this ();
+       else
+           do_that ();
+
+But the braces are mandatory when mixing single statement and compound
+statements in the various clauses. For example, do not do this:
+
+       if (condition) {
+           do_this ();
+           do_that ();
+       } else                  /* Yuck! */
+           do_the_other ();
+
+And of course, there are exceptions for when the code just looks
+better with the braces:
+
+       if (condition) {
+           /* Note that we have to be careful here. */
+           do_something_dangerous (with_care);
+       }
+
+       if (condition &&
+           other_condition &&
+           yet_another)
+       {
+           do_something ();
+       }
+
+And note that this last example also shows a situation in which the
+opening brace really needs to be on its own line. The following looks awful:
+
+       if (condition &&
+           other_condition &&
+           yet_another) {      /* Yuck! */
+           do_something ();
+       }
+
+As we said above, legible code that is easy to understand and maintain
+is the goal, not adherence to strict rules.
+
+Whitespace
+----------
+Separate logically distinct chunks with a single newline. This
+obviously applies between functions, but also applies within a
+function or block and can even be used to good effect within a
+structure definition:
+
+       struct _cairo_gstate {
+           cairo_operator_t op;
+
+           double tolerance;
+
+           /* stroke style */
+           double line_width;
+           cairo_line_cap_t line_cap;
+           cairo_line_join_t line_join;
+           double miter_limit;
+
+           cairo_fill_rule_t fill_rule;
+
+           double *dash;
+           int num_dashes;
+           double dash_offset;
+
+           ...
+       }
+
+Use a single space before a left parenthesis, except where the
+standard will not allow it, (eg. when defining a parameterized macro).
+
+Don't eliminate newlines just because things would still fit on one
+line. This breaks the expected visual structure of the code making it
+much harder to read and understand:
+
+       if (condition) foo (); else bar ();     /* Yuck! */
+
+Do eliminate trailing whitespace (space or tab characters) on any
+line. Also, avoid putting initial or final blank lines into any file,
+and never use multiple blank lines instead of a single blank line.
+
+Do enable the default git pre-commit hook that detect trailing
+whitespace for you and help you to avoid corrupting cairo's tree with
+it. Do that as follows:
+
+       chmod a+x .git/hooks/pre-commit
+
+You might also find the git-stripspace utility helpful which acts as a
+filter to remove trailing whitespace as well as initial, final, and
+duplicate blank lines.
+
+As a special case of the bracing and whitespace guidelines, function
+definitions should always take the following form:
+
+       void
+       my_function (argument)
+       {
+           do_my_things ();
+       }
+
+And function prototypes should similarly have the return type (and
+associated specifiers and qualifiers) on a line above the function, so
+that the function name is flush left.
+
+Break up long lines (> ~80 characters) and use whitespace to align
+things nicely. For example the arguments in a long list to a function
+call should all be aligned with each other:
+
+       align_function_arguments (argument_the_first,
+                                 argument_the_second,
+                                 argument_the_third);
+
+And as a special rule, in a function prototype, (as well as in the
+definition), whitespace should be inserted between the parameter types
+and names so that the names are aligned:
+
+       void
+       align_parameter_names_in_prototypes (const char *char_star_arg,
+                                            int         int_arg,
+                                            double     *double_star_arg,
+                                            double      double_arg);
+
+Note that parameters with a * prefix are aligned one character to the
+left so that the actual names are aligned.
+
+Managing nested blocks
+----------------------
+Long blocks that are deeply nested make the code very hard to
+read. Fortunately such blocks often indicate logically distinct chunks
+of functionality that are begging to be split into their own
+functions. Please listen to the blocks when they beg.
+
+In other cases, gratuitous nesting comes about because the primary
+functionality gets buried in a nested block rather than living at the
+primary level where it belongs. Consider the following:
+
+       foo = malloc (sizeof (foo_t));
+       if (foo) {                                      /* Yuck! */
+           ...
+           /* lots of code to initialize foo */
+           ...
+           return SUCCESS;
+       }
+       return FAILURE;
+
+This kind of gratuitous nesting can be avoided by following a pattern
+of handling exceptional cases early and returning:
+
+       foo = malloc (sizeof (foo_t));
+       if (foo == NULL)
+           return FAILURE;
+
+       ...
+       /* lots of code to initialize foo */
+       ...
+       return SUCCESS;
+
+The return statement is often the best thing to use in a pattern like
+this. If it's not available due to additional nesting above which
+require some cleanup after the current block, then consider splitting
+the current block into a new function before using goto.
+
+Memory allocation
+-----------------
+
+Because much of cairo's data consists of dynamically allocated arrays,
+it's very easy to introduce integer overflow issues whenever malloc()
+is called.  Use the _cairo_malloc2(), _cairo_malloc3(), and
+_cairo_malloc2_add1 macros to avoid these cases; these macros check
+for overflow and will return NULL in that case.
+
+  malloc (n * size) => _cairo_malloc_ab (n, size)
+    e.g. malloc (num_elts * sizeof(some_type)) =>
+         _cairo_malloc2 (num_elts, sizeof(some_type))
+
+  malloc (a * b * size) => _cairo_malloc_abc (a, b, size)
+    e.g. malloc (width * height * 4) =>
+         _cairo_malloc3 (width, height, 4)
+
+  malloc (n * size + k) => _cairo_malloc_ab_plus_c (n, size, k)
+    e.g. malloc (num * sizeof(entry) + sizeof(header)) =>
+         _cairo_malloc2k (num, sizeof(entry), sizeof(header))
+
+In general, be wary of performing any arithmetic operations in an
+argument to malloc.  You should explicitly check for integer overflow
+yourself in any more complex situations.
+
+Mode lines
+----------
+
+So given the rules above, what is the best way to simplify one's life as
+a code monkey? Get your editor to do most of the tedious work of
+beautifying your code!
+
+As a reward for reading this far, here are some mode lines for the more
+popular editors:
+/*
+ * vim:sw=4:sts=4:ts=8:tw=78:fo=tcroq:cindent:cino=\:0,(0
+ * vim:isk=a-z,A-Z,48-57,_,.,-,>
+ */
+
+
+TODO
+----
+
+Write rules for common editors to use this style.  Also cleanup/unify
+the modelines in the source files.
diff --git a/COPYING b/COPYING
new file mode 100644 (file)
index 0000000..f54969f
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,33 @@
+Cairo is free software.
+
+Every source file in the implementation[*] of cairo is available to be
+redistributed and/or modified under the terms of either the GNU Lesser
+General Public License (LGPL) version 2.1 or the Mozilla Public
+License (MPL) version 1.1.  Some files are available under more
+liberal terms, but we believe that in all cases, each file may be used
+under either the LGPL or the MPL.
+
+See the following files in this directory for the precise terms and
+conditions of either license:
+
+       COPYING-LGPL-2.1
+       COPYING-MPL-1.1
+
+Please see each file in the implementation for copyright and licensing
+information, (in the opening comment of each file).
+
+[*] The implementation of cairo is contained entirely within the "src"
+directory of the cairo source distribution. There are other components
+of the cairo source distribution (such as the "test", "util", and "perf")
+that are auxiliary to the library itself. None of the source code in these
+directories contributes to a build of the cairo library itself, (libcairo.so
+or cairo.dll or similar).
+
+These auxiliary components are also free software, but may be under
+different license terms than cairo itself. For example, most of the
+test cases in the perf and test directories are made available under
+an MIT license to simplify any use of this code for reference purposes
+in using cairo itself. Other files might be available under the GNU
+General Public License (GPL), for example. Again, please see the COPYING
+file under each directory and the opening comment of each file for copyright
+and licensing information.
diff --git a/COPYING-LGPL-2.1 b/COPYING-LGPL-2.1
new file mode 100644 (file)
index 0000000..f1ed618
--- /dev/null
@@ -0,0 +1,510 @@
+
+                  GNU LESSER GENERAL PUBLIC LICENSE
+                       Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+     51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL.  It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+                            Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+  This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it.  You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations
+below.
+
+  When we speak of free software, we are referring to freedom of use,
+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 this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+  To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights.  These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+  For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you.  You must make sure that they, too, receive or can get the source
+code.  If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it.  And you must show them these terms so they know their rights.
+
+  We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+  To protect each distributor, we want to make it very clear that
+there is no warranty for the free library.  Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+\f
+  Finally, software patents pose a constant threat to the existence of
+any free program.  We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder.  Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+  Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License.  This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License.  We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+  When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library.  The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom.  The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+  We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License.  It also provides other free software developers Less
+of an advantage over competing non-free programs.  These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries.  However, the Lesser license provides advantages in certain
+special circumstances.
+
+  For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it
+becomes a de-facto standard.  To achieve this, non-free programs must
+be allowed to use the library.  A more frequent case is that a free
+library does the same job as widely used non-free libraries.  In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+  In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software.  For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+  Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.  Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library".  The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+\f
+                  GNU LESSER GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+  A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+  The "Library", below, refers to any such software library or work
+which has been distributed under these terms.  A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language.  (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+  "Source code" for a work means the preferred form of the work for
+making modifications to it.  For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control
+compilation and installation of the library.
+
+  Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it).  Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+  1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+  You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+\f
+  2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) The modified work must itself be a software library.
+
+    b) You must cause the files modified to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    c) You must cause the whole of the work to be licensed at no
+    charge to all third parties under the terms of this License.
+
+    d) If a facility in the modified Library refers to a function or a
+    table of data to be supplied by an application program that uses
+    the facility, other than as an argument passed when the facility
+    is invoked, then you must make a good faith effort to ensure that,
+    in the event an application does not supply such function or
+    table, the facility still operates, and performs whatever part of
+    its purpose remains meaningful.
+
+    (For example, a function in a library to compute square roots has
+    a purpose that is entirely well-defined independent of the
+    application.  Therefore, Subsection 2d requires that any
+    application-supplied function or table used by this function must
+    be optional: if the application does not supply it, the square
+    root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library.  To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License.  (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.)  Do not make any other change in
+these notices.
+\f
+  Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+  This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+  4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+  If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library".  Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+  However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library".  The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+  When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library.  The
+threshold for this to be true is not precisely defined by law.
+
+  If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work.  (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+  Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+\f
+  6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+  You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License.  You must supply a copy of this License.  If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License.  Also, you must do one
+of these things:
+
+    a) Accompany the work with the complete corresponding
+    machine-readable source code for the Library including whatever
+    changes were used in the work (which must be distributed under
+    Sections 1 and 2 above); and, if the work is an executable linked
+    with the Library, with the complete machine-readable "work that
+    uses the Library", as object code and/or source code, so that the
+    user can modify the Library and then relink to produce a modified
+    executable containing the modified Library.  (It is understood
+    that the user who changes the contents of definitions files in the
+    Library will not necessarily be able to recompile the application
+    to use the modified definitions.)
+
+    b) Use a suitable shared library mechanism for linking with the
+    Library.  A suitable mechanism is one that (1) uses at run time a
+    copy of the library already present on the user's computer system,
+    rather than copying library functions into the executable, and (2)
+    will operate properly with a modified version of the library, if
+    the user installs one, as long as the modified version is
+    interface-compatible with the version that the work was made with.
+
+    c) Accompany the work with a written offer, valid for at least
+    three years, to give the same user the materials specified in
+    Subsection 6a, above, for a charge no more than the cost of
+    performing this distribution.
+
+    d) If distribution of the work is made by offering access to copy
+    from a designated place, offer equivalent access to copy the above
+    specified materials from the same place.
+
+    e) Verify that the user has already received a copy of these
+    materials or that you have already sent this user a copy.
+
+  For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it.  However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+  It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system.  Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+\f
+  7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+    a) Accompany the combined library with a copy of the same work
+    based on the Library, uncombined with any other library
+    facilities.  This must be distributed under the terms of the
+    Sections above.
+
+    b) Give prominent notice with the combined library of the fact
+    that part of it is a work based on the Library, and explaining
+    where to find the accompanying uncombined form of the same work.
+
+  8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License.  Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License.  However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+  9. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Library or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+  10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+\f
+  11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+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
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply, and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License
+may add an explicit geographical distribution limitation excluding those
+countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser 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 Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation.  If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+\f
+  14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission.  For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this.  Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+                            NO WARRANTY
+
+  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "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
+LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY 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
+LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+                     END OF TERMS AND CONDITIONS
+\f
+           How to Apply These Terms to Your New Libraries
+
+  If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change.  You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms
+of the ordinary General Public License).
+
+  To apply these terms, attach the following notices to the library.
+It is safest to attach them to the start of each source file to most
+effectively convey 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 library's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library 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
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or
+your school, if any, to sign a "copyright disclaimer" for the library,
+if necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the
+  library `Frob' (a library for tweaking knobs) written by James
+  Random Hacker.
+
+  <signature of Ty Coon>, 1 April 1990
+  Ty Coon, President of Vice
+
+That's all there is to it!
+
+
diff --git a/COPYING-MPL-1.1 b/COPYING-MPL-1.1
new file mode 100644 (file)
index 0000000..7714141
--- /dev/null
@@ -0,0 +1,470 @@
+                          MOZILLA PUBLIC LICENSE
+                                Version 1.1
+
+                              ---------------
+
+1. Definitions.
+
+     1.0.1. "Commercial Use" means distribution or otherwise making the
+     Covered Code available to a third party.
+
+     1.1. "Contributor" means each entity that creates or contributes to
+     the creation of Modifications.
+
+     1.2. "Contributor Version" means the combination of the Original
+     Code, prior Modifications used by a Contributor, and the Modifications
+     made by that particular Contributor.
+
+     1.3. "Covered Code" means the Original Code or Modifications or the
+     combination of the Original Code and Modifications, in each case
+     including portions thereof.
+
+     1.4. "Electronic Distribution Mechanism" means a mechanism generally
+     accepted in the software development community for the electronic
+     transfer of data.
+
+     1.5. "Executable" means Covered Code in any form other than Source
+     Code.
+
+     1.6. "Initial Developer" means the individual or entity identified
+     as the Initial Developer in the Source Code notice required by Exhibit
+     A.
+
+     1.7. "Larger Work" means a work which combines Covered Code or
+     portions thereof with code not governed by the terms of this License.
+
+     1.8. "License" means this document.
+
+     1.8.1. "Licensable" means having the right to grant, to the maximum
+     extent possible, whether at the time of the initial grant or
+     subsequently acquired, any and all of the rights conveyed herein.
+
+     1.9. "Modifications" means any addition to or deletion from the
+     substance or structure of either the Original Code or any previous
+     Modifications. When Covered Code is released as a series of files, a
+     Modification is:
+          A. Any addition to or deletion from the contents of a file
+          containing Original Code or previous Modifications.
+
+          B. Any new file that contains any part of the Original Code or
+          previous Modifications.
+
+     1.10. "Original Code" means Source Code of computer software code
+     which is described in the Source Code notice required by Exhibit A as
+     Original Code, and which, at the time of its release under this
+     License is not already Covered Code governed by this License.
+
+     1.10.1. "Patent Claims" means any patent claim(s), now owned or
+     hereafter acquired, including without limitation,  method, process,
+     and apparatus claims, in any patent Licensable by grantor.
+
+     1.11. "Source Code" means the preferred form of the Covered Code for
+     making modifications to it, including all modules it contains, plus
+     any associated interface definition files, scripts used to control
+     compilation and installation of an Executable, or source code
+     differential comparisons against either the Original Code or another
+     well known, available Covered Code of the Contributor's choice. The
+     Source Code can be in a compressed or archival form, provided the
+     appropriate decompression or de-archiving software is widely available
+     for no charge.
+
+     1.12. "You" (or "Your")  means an individual or a legal entity
+     exercising rights under, and complying with all of the terms of, this
+     License or a future version of this License issued under Section 6.1.
+     For legal entities, "You" includes any entity which controls, is
+     controlled by, or is under common control with You. For purposes of
+     this definition, "control" means (a) the power, direct or indirect,
+     to cause the direction or management of such entity, whether by
+     contract or otherwise, or (b) ownership of more than fifty percent
+     (50%) of the outstanding shares or beneficial ownership of such
+     entity.
+
+2. Source Code License.
+
+     2.1. The Initial Developer Grant.
+     The Initial Developer hereby grants You a world-wide, royalty-free,
+     non-exclusive license, subject to third party intellectual property
+     claims:
+          (a)  under intellectual property rights (other than patent or
+          trademark) Licensable by Initial Developer to use, reproduce,
+          modify, display, perform, sublicense and distribute the Original
+          Code (or portions thereof) with or without Modifications, and/or
+          as part of a Larger Work; and
+
+          (b) under Patents Claims infringed by the making, using or
+          selling of Original Code, to make, have made, use, practice,
+          sell, and offer for sale, and/or otherwise dispose of the
+          Original Code (or portions thereof).
+
+          (c) the licenses granted in this Section 2.1(a) and (b) are
+          effective on the date Initial Developer first distributes
+          Original Code under the terms of this License.
+
+          (d) Notwithstanding Section 2.1(b) above, no patent license is
+          granted: 1) for code that You delete from the Original Code; 2)
+          separate from the Original Code;  or 3) for infringements caused
+          by: i) the modification of the Original Code or ii) the
+          combination of the Original Code with other software or devices.
+
+     2.2. Contributor Grant.
+     Subject to third party intellectual property claims, each Contributor
+     hereby grants You a world-wide, royalty-free, non-exclusive license
+
+          (a)  under intellectual property rights (other than patent or
+          trademark) Licensable by Contributor, to use, reproduce, modify,
+          display, perform, sublicense and distribute the Modifications
+          created by such Contributor (or portions thereof) either on an
+          unmodified basis, with other Modifications, as Covered Code
+          and/or as part of a Larger Work; and
+
+          (b) under Patent Claims infringed by the making, using, or
+          selling of  Modifications made by that Contributor either alone
+          and/or in combination with its Contributor Version (or portions
+          of such combination), to make, use, sell, offer for sale, have
+          made, and/or otherwise dispose of: 1) Modifications made by that
+          Contributor (or portions thereof); and 2) the combination of
+          Modifications made by that Contributor with its Contributor
+          Version (or portions of such combination).
+
+          (c) the licenses granted in Sections 2.2(a) and 2.2(b) are
+          effective on the date Contributor first makes Commercial Use of
+          the Covered Code.
+
+          (d)    Notwithstanding Section 2.2(b) above, no patent license is
+          granted: 1) for any code that Contributor has deleted from the
+          Contributor Version; 2)  separate from the Contributor Version;
+          3)  for infringements caused by: i) third party modifications of
+          Contributor Version or ii)  the combination of Modifications made
+          by that Contributor with other software  (except as part of the
+          Contributor Version) or other devices; or 4) under Patent Claims
+          infringed by Covered Code in the absence of Modifications made by
+          that Contributor.
+
+3. Distribution Obligations.
+
+     3.1. Application of License.
+     The Modifications which You create or to which You contribute are
+     governed by the terms of this License, including without limitation
+     Section 2.2. The Source Code version of Covered Code may be
+     distributed only under the terms of this License or a future version
+     of this License released under Section 6.1, and You must include a
+     copy of this License with every copy of the Source Code You
+     distribute. You may not offer or impose any terms on any Source Code
+     version that alters or restricts the applicable version of this
+     License or the recipients' rights hereunder. However, You may include
+     an additional document offering the additional rights described in
+     Section 3.5.
+
+     3.2. Availability of Source Code.
+     Any Modification which You create or to which You contribute must be
+     made available in Source Code form under the terms of this License
+     either on the same media as an Executable version or via an accepted
+     Electronic Distribution Mechanism to anyone to whom you made an
+     Executable version available; and if made available via Electronic
+     Distribution Mechanism, must remain available for at least twelve (12)
+     months after the date it initially became available, or at least six
+     (6) months after a subsequent version of that particular Modification
+     has been made available to such recipients. You are responsible for
+     ensuring that the Source Code version remains available even if the
+     Electronic Distribution Mechanism is maintained by a third party.
+
+     3.3. Description of Modifications.
+     You must cause all Covered Code to which You contribute to contain a
+     file documenting the changes You made to create that Covered Code and
+     the date of any change. You must include a prominent statement that
+     the Modification is derived, directly or indirectly, from Original
+     Code provided by the Initial Developer and including the name of the
+     Initial Developer in (a) the Source Code, and (b) in any notice in an
+     Executable version or related documentation in which You describe the
+     origin or ownership of the Covered Code.
+
+     3.4. Intellectual Property Matters
+          (a) Third Party Claims.
+          If Contributor has knowledge that a license under a third party's
+          intellectual property rights is required to exercise the rights
+          granted by such Contributor under Sections 2.1 or 2.2,
+          Contributor must include a text file with the Source Code
+          distribution titled "LEGAL" which describes the claim and the
+          party making the claim in sufficient detail that a recipient will
+          know whom to contact. If Contributor obtains such knowledge after
+          the Modification is made available as described in Section 3.2,
+          Contributor shall promptly modify the LEGAL file in all copies
+          Contributor makes available thereafter and shall take other steps
+          (such as notifying appropriate mailing lists or newsgroups)
+          reasonably calculated to inform those who received the Covered
+          Code that new knowledge has been obtained.
+
+          (b) Contributor APIs.
+          If Contributor's Modifications include an application programming
+          interface and Contributor has knowledge of patent licenses which
+          are reasonably necessary to implement that API, Contributor must
+          also include this information in the LEGAL file.
+
+               (c)    Representations.
+          Contributor represents that, except as disclosed pursuant to
+          Section 3.4(a) above, Contributor believes that Contributor's
+          Modifications are Contributor's original creation(s) and/or
+          Contributor has sufficient rights to grant the rights conveyed by
+          this License.
+
+     3.5. Required Notices.
+     You must duplicate the notice in Exhibit A in each file of the Source
+     Code.  If it is not possible to put such notice in a particular Source
+     Code file due to its structure, then You must include such notice in a
+     location (such as a relevant directory) where a user would be likely
+     to look for such a notice.  If You created one or more Modification(s)
+     You may add your name as a Contributor to the notice described in
+     Exhibit A.  You must also duplicate this License in any documentation
+     for the Source Code where You describe recipients' rights or ownership
+     rights relating to Covered Code.  You may choose to offer, and to
+     charge a fee for, warranty, support, indemnity or liability
+     obligations to one or more recipients of Covered Code. However, You
+     may do so only on Your own behalf, and not on behalf of the Initial
+     Developer or any Contributor. You must make it absolutely clear than
+     any such warranty, support, indemnity or liability obligation is
+     offered by You alone, and You hereby agree to indemnify the Initial
+     Developer and every Contributor for any liability incurred by the
+     Initial Developer or such Contributor as a result of warranty,
+     support, indemnity or liability terms You offer.
+
+     3.6. Distribution of Executable Versions.
+     You may distribute Covered Code in Executable form only if the
+     requirements of Section 3.1-3.5 have been met for that Covered Code,
+     and if You include a notice stating that the Source Code version of
+     the Covered Code is available under the terms of this License,
+     including a description of how and where You have fulfilled the
+     obligations of Section 3.2. The notice must be conspicuously included
+     in any notice in an Executable version, related documentation or
+     collateral in which You describe recipients' rights relating to the
+     Covered Code. You may distribute the Executable version of Covered
+     Code or ownership rights under a license of Your choice, which may
+     contain terms different from this License, provided that You are in
+     compliance with the terms of this License and that the license for the
+     Executable version does not attempt to limit or alter the recipient's
+     rights in the Source Code version from the rights set forth in this
+     License. If You distribute the Executable version under a different
+     license You must make it absolutely clear that any terms which differ
+     from this License are offered by You alone, not by the Initial
+     Developer or any Contributor. You hereby agree to indemnify the
+     Initial Developer and every Contributor for any liability incurred by
+     the Initial Developer or such Contributor as a result of any such
+     terms You offer.
+
+     3.7. Larger Works.
+     You may create a Larger Work by combining Covered Code with other code
+     not governed by the terms of this License and distribute the Larger
+     Work as a single product. In such a case, You must make sure the
+     requirements of this License are fulfilled for the Covered Code.
+
+4. Inability to Comply Due to Statute or Regulation.
+
+     If it is impossible for You to comply with any of the terms of this
+     License with respect to some or all of the Covered Code due to
+     statute, judicial order, or regulation then You must: (a) comply with
+     the terms of this License to the maximum extent possible; and (b)
+     describe the limitations and the code they affect. Such description
+     must be included in the LEGAL file described in Section 3.4 and must
+     be included with all distributions of the Source Code. Except to the
+     extent prohibited by statute or regulation, such description must be
+     sufficiently detailed for a recipient of ordinary skill to be able to
+     understand it.
+
+5. Application of this License.
+
+     This License applies to code to which the Initial Developer has
+     attached the notice in Exhibit A and to related Covered Code.
+
+6. Versions of the License.
+
+     6.1. New Versions.
+     Netscape Communications Corporation ("Netscape") may publish revised
+     and/or new versions of the License from time to time. Each version
+     will be given a distinguishing version number.
+
+     6.2. Effect of New Versions.
+     Once Covered Code has been published under a particular version of the
+     License, You may always continue to use it under the terms of that
+     version. You may also choose to use such Covered Code under the terms
+     of any subsequent version of the License published by Netscape. No one
+     other than Netscape has the right to modify the terms applicable to
+     Covered Code created under this License.
+
+     6.3. Derivative Works.
+     If You create or use a modified version of this License (which you may
+     only do in order to apply it to code which is not already Covered Code
+     governed by this License), You must (a) rename Your license so that
+     the phrases "Mozilla", "MOZILLAPL", "MOZPL", "Netscape",
+     "MPL", "NPL" or any confusingly similar phrase do not appear in your
+     license (except to note that your license differs from this License)
+     and (b) otherwise make it clear that Your version of the license
+     contains terms which differ from the Mozilla Public License and
+     Netscape Public License. (Filling in the name of the Initial
+     Developer, Original Code or Contributor in the notice described in
+     Exhibit A shall not of themselves be deemed to be modifications of
+     this License.)
+
+7. DISCLAIMER OF WARRANTY.
+
+     COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS,
+     WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
+     WITHOUT LIMITATION, WARRANTIES THAT THE COVERED CODE IS FREE OF
+     DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING.
+     THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED CODE
+     IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT,
+     YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE
+     COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER
+     OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF
+     ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER.
+
+8. TERMINATION.
+
+     8.1.  This License and the rights granted hereunder will terminate
+     automatically if You fail to comply with terms herein and fail to cure
+     such breach within 30 days of becoming aware of the breach. All
+     sublicenses to the Covered Code which are properly granted shall
+     survive any termination of this License. Provisions which, by their
+     nature, must remain in effect beyond the termination of this License
+     shall survive.
+
+     8.2.  If You initiate litigation by asserting a patent infringement
+     claim (excluding declatory judgment actions) against Initial Developer
+     or a Contributor (the Initial Developer or Contributor against whom
+     You file such action is referred to as "Participant")  alleging that:
+
+     (a)  such Participant's Contributor Version directly or indirectly
+     infringes any patent, then any and all rights granted by such
+     Participant to You under Sections 2.1 and/or 2.2 of this License
+     shall, upon 60 days notice from Participant terminate prospectively,
+     unless if within 60 days after receipt of notice You either: (i)
+     agree in writing to pay Participant a mutually agreeable reasonable
+     royalty for Your past and future use of Modifications made by such
+     Participant, or (ii) withdraw Your litigation claim with respect to
+     the Contributor Version against such Participant.  If within 60 days
+     of notice, a reasonable royalty and payment arrangement are not
+     mutually agreed upon in writing by the parties or the litigation claim
+     is not withdrawn, the rights granted by Participant to You under
+     Sections 2.1 and/or 2.2 automatically terminate at the expiration of
+     the 60 day notice period specified above.
+
+     (b)  any software, hardware, or device, other than such Participant's
+     Contributor Version, directly or indirectly infringes any patent, then
+     any rights granted to You by such Participant under Sections 2.1(b)
+     and 2.2(b) are revoked effective as of the date You first made, used,
+     sold, distributed, or had made, Modifications made by that
+     Participant.
+
+     8.3.  If You assert a patent infringement claim against Participant
+     alleging that such Participant's Contributor Version directly or
+     indirectly infringes any patent where such claim is resolved (such as
+     by license or settlement) prior to the initiation of patent
+     infringement litigation, then the reasonable value of the licenses
+     granted by such Participant under Sections 2.1 or 2.2 shall be taken
+     into account in determining the amount or value of any payment or
+     license.
+
+     8.4.  In the event of termination under Sections 8.1 or 8.2 above,
+     all end user license agreements (excluding distributors and resellers)
+     which have been validly granted by You or any distributor hereunder
+     prior to termination shall survive termination.
+
+9. LIMITATION OF LIABILITY.
+
+     UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT
+     (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE INITIAL
+     DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED CODE,
+     OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY PERSON FOR
+     ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY
+     CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL,
+     WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER
+     COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN
+     INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF
+     LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY
+     RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW
+     PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE
+     EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO
+     THIS EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU.
+
+10. U.S. GOVERNMENT END USERS.
+
+     The Covered Code is a "commercial item," as that term is defined in
+     48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial computer
+     software" and "commercial computer software documentation," as such
+     terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48
+     C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995),
+     all U.S. Government End Users acquire Covered Code with only those
+     rights set forth herein.
+
+11. MISCELLANEOUS.
+
+     This License represents the complete agreement concerning subject
+     matter hereof. If any provision of this License is held to be
+     unenforceable, such provision shall be reformed only to the extent
+     necessary to make it enforceable. This License shall be governed by
+     California law provisions (except to the extent applicable law, if
+     any, provides otherwise), excluding its conflict-of-law provisions.
+     With respect to disputes in which at least one party is a citizen of,
+     or an entity chartered or registered to do business in the United
+     States of America, any litigation relating to this License shall be
+     subject to the jurisdiction of the Federal Courts of the Northern
+     District of California, with venue lying in Santa Clara County,
+     California, with the losing party responsible for costs, including
+     without limitation, court costs and reasonable attorneys' fees and
+     expenses. The application of the United Nations Convention on
+     Contracts for the International Sale of Goods is expressly excluded.
+     Any law or regulation which provides that the language of a contract
+     shall be construed against the drafter shall not apply to this
+     License.
+
+12. RESPONSIBILITY FOR CLAIMS.
+
+     As between Initial Developer and the Contributors, each party is
+     responsible for claims and damages arising, directly or indirectly,
+     out of its utilization of rights under this License and You agree to
+     work with Initial Developer and Contributors to distribute such
+     responsibility on an equitable basis. Nothing herein is intended or
+     shall be deemed to constitute any admission of liability.
+
+13. MULTIPLE-LICENSED CODE.
+
+     Initial Developer may designate portions of the Covered Code as
+     "Multiple-Licensed".  "Multiple-Licensed" means that the Initial
+     Developer permits you to utilize portions of the Covered Code under
+     Your choice of the NPL or the alternative licenses, if any, specified
+     by the Initial Developer in the file described in Exhibit A.
+
+EXHIBIT A -Mozilla Public License.
+
+     ``The contents of this file are subject to the Mozilla Public License
+     Version 1.1 (the "License"); you may not use this file except in
+     compliance with the License. You may obtain a copy of the License at
+     http://www.mozilla.org/MPL/
+
+     Software distributed under the License is distributed on an "AS IS"
+     basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+     License for the specific language governing rights and limitations
+     under the License.
+
+     The Original Code is ______________________________________.
+
+     The Initial Developer of the Original Code is ________________________.
+     Portions created by ______________________ are Copyright (C) ______
+     _______________________. All Rights Reserved.
+
+     Contributor(s): ______________________________________.
+
+     Alternatively, the contents of this file may be used under the terms
+     of the _____ license (the  "[___] License"), in which case the
+     provisions of [______] License are applicable instead of those
+     above.  If you wish to allow use of your version of this file only
+     under the terms of the [____] License and not to allow others to use
+     your version of this file under the MPL, indicate your decision by
+     deleting  the provisions above and replace  them with the notice and
+     other provisions required by the [___] License.  If you do not delete
+     the provisions above, a recipient may use your version of this file
+     under either the MPL or the [___] License."
+
+     [NOTE: The text of this Exhibit A may differ slightly from the text of
+     the notices in the Source Code files of the Original Code. You should
+     use the text of this Exhibit A rather than the text found in the
+     Original Code Source Code for Your Modifications.]
+
diff --git a/ChangeLog b/ChangeLog
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/HACKING b/HACKING
new file mode 100644 (file)
index 0000000..92aa654
--- /dev/null
+++ b/HACKING
@@ -0,0 +1,186 @@
+Hacking Cairo
+=============
+
+This is a high-level guide to how the cairo distribution is organized
+and how to get started hacking on it.  Make sure you read through the
+file README before continuing.
+
+
+Coding Style
+------------
+
+The easiest way to write code in the cairo style is to follow code close
+to the place you are hacking, but if you want a written down set of
+rules, see file CODING_STYLE.
+
+Files for backends that depend on languages other than C (C++ or
+Objective C for example) may use features specific to those languages.
+For example, "//" comments are allowed, though discouraged, in those files.
+
+
+Contact
+-------
+
+Various ways to get in touch with other cairo developers and maintainers
+have been enumerated at:
+
+       http://cairographics.org/contact/
+
+Most of that information is also reflected in the following sections.
+
+
+Mailing Lists
+-------------
+
+There are various mailing lists that are useful when developing cairo
+code.  A complete list is always available at:
+
+       http://cairographics.org/lists/
+
+It is recommended that cairo developers subscribe to all those lists.
+The cairo list by itself generates much more traffic than the others
+combined, so developers and contributors should not be intimidated by
+the -commit and -bugs lists.
+
+
+Bug Tracking System
+-------------------
+
+We use a standard bugzilla bug tracking system available at:
+
+       http://bugs.freedesktop.org/
+
+See file named BUGS for detailed information on reporting bugs.  In short,
+for straight bug reports, it's best to report them there such that they
+are not lost or forgotten.  For discussion of new features or
+complicated issues, use the mailing list.
+
+
+IRC
+---
+
+It's a great idea to hang around the cairo IRC channel if you have any
+interest in cairo.  We use the #cairo channel on irc.freenode.net.
+
+Make sure you introduce yourself if your nick is not easy to match to
+the name you use on the mailing list.
+
+
+Version Control System
+----------------------
+
+We use /git/ for version control.  See:
+
+       http://cairographics.org/download/
+
+TODO:
+Add links to some git tutorials or better, write a few paragraphs
+about how to use git to efficiently hack on cairo.
+
+
+Build System
+------------
+
+We use the autotools build system with cairo, but with various
+customizations and advanced features.  Reading configure.in is your
+best bet to understanding it, or just ask on IRC.
+
+To bootstrap the build system run ./autogen.sh.  After that the
+regular "./configure; make; make install" sequence can be used.
+See file named INSTALL for more details.
+
+There is limited support for a win32 build system.
+See README.win32 and Makefile.win32 files in various directories.
+
+
+ChangeLog
+---------
+
+We generate ChangeLog files automatically from the git commit log.
+No manual ChangeLog writing is necessary.
+
+
+Copyrights and Licensing
+------------------------
+
+The cairo library is dual-licensed under LGPL and MPL.  See file named
+COPYING for details.  The test suites are more liberal.  For example,
+GPL code is allowed in the test suites, though it's always better to
+keep things simple.
+
+When writing new code, update the file headers to add your (or your
+employers) copyright line and contributor line.  If adding new files
+or splitting a file, copy the file header from other files.
+
+
+Source Code
+-----------
+
+The library source code and headers live in the src/ directory.
+See src/README for more information.
+
+
+Regression Test Suite
+---------------------
+
+Cairo has a fairly extensive regression-testing suite.  Indeed, without
+these tests it would be impossible to make a cairo release without
+introducing tens of regressions.  We still manage to introduce
+regressions with each release even with the hundreds of tests we already
+have.
+
+The regression test suite is located under the test/ directory.
+See test/README for more information.
+
+
+Performance Test Suite
+----------------------
+
+There is a small performance-testing suite for cairo.
+
+The performance test suite is located under the perf/ directory.
+See perf/README for more information.
+
+
+Boilerplate
+-----------
+
+The cairo-boilerplate is a small private library used by the regression
+and performance test suites.  It includes the boilerplace code needed
+to initialize various backends for the test suites, as well as allow
+tweaking some of the internal workings of the backends for more testing.
+
+The boilerplate code is localted under the boilerplate/ directory.
+See boilerplate/README for more information.
+
+
+Documentation
+-------------
+
+Cairo uses the gtk-doc system for reference API documentation.
+
+The reference documentation is located under doc/public.
+See doc/public/README for more information.
+
+For more documentation including frequently asked questions, tutorials,
+samples, roadmap, todo list, etc visit:
+
+       http://cairographics.org/documentation/
+
+Some of those should gradually be moved to doc/.
+
+
+Utilities
+---------
+
+There are a few useful utilities we have developed that are either
+useful when writing code using cairo, or writing cairo, or useful in
+general.  These tools can be found under the util/ directory.
+See util/README for more information.
+
+
+Releasing
+---------
+
+Now you are a cairo maintainer, so what?  See file named RELEASING.
+
diff --git a/INSTALL b/INSTALL
new file mode 100644 (file)
index 0000000..9db68de
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,184 @@
+Quick-start build instructions
+------------------------------
+1) Configure the package:
+
+       ./configure
+
+2) Compile it:
+
+       make
+
+3) Install it:
+
+       make install
+
+This final step may require temporary root access (eg. with sudo) if
+you don't have write permission to the directory in which cairo will
+be installed.
+
+NOTE: If you are working with source from git/cvs rather than from a tar
+file, then you should use ./autogen.sh in place of ./configure
+anywhere it is mentioned in these instructions.
+
+More detailed build instructions
+--------------------------------
+1) Configure the package
+
+   The first step in building cairo is to configure the package by
+   running the configure script. [Note: if you don't have a configure
+   script, skip down below to the Extremely detailed build
+   instructions.]
+
+   The configure script attempts to automatically detect as much as
+   possible about your system. So, you should primarily just accept
+   its defaults by running:
+
+       ./configure
+
+   The configure script does accept a large number of options for
+   fine-tuning its behavior. See "./configure --help" for a complete
+   list. The most commonly used options are discussed here.
+
+   --prefix=PREFIX
+
+       This option specifies the directory under which the software
+       should be installed. By default configure will choose a
+       directory such as /usr/local. If you would like to install
+       cairo to some other location, pass the director to configure
+       with the --prefix option. For example:
+
+               ./configure --prefix=/opt/cairo
+
+       would install cairo into the /opt/cairo directory. You could
+       also choose a prefix directory within your home directory if
+       you don't have write access to any system-wide directory.
+
+       After installing into a custom prefix, you will need to set
+       some environment variables to allow the software to be
+       found. Assuming the /opt/cairo prefix and assuming you are
+       using the bash shell, the following environment variables
+       should be set:
+
+               PKG_CONFIG_PATH=/opt/cairo/lib/pkgconfig
+               LD_LIBRARY_PATH=/opt/cairo/lib
+               export PKG_CONFIG_PATH LD_LIBRARY_PATH
+
+       (NOTE: On Mac OS X, at least, use DYLD_LIBRARY_PATH in place
+              of LD_LIBRARY_PATH above.)
+
+    --enable-XYZ
+    --enable-XYZ=yes
+    --enable-XYZ=auto
+    --enable-XYZ=no
+    --disable-XYZ
+
+   Cairo's various font and surface backends and other features can be
+   enabled or disabled at configure time.  Features can be divided into
+   three categories based on their default state:
+
+     * default=yes: These are the recommended features like PNG functions
+       and PS/PDF/SVG backends.  It is highly recommended to not disable
+       these features but if that's really what one wants, they can be
+       disabled using --disable-XYZ.
+
+     * default=auto: These are the "native" features, that is, they are
+       platform specific, like the Xlib surface backend.  You probably
+       want one or two of these.  They will be automatically enabled if
+       all their required facilities are available.  Or you can use
+       --enable-XYZ or --disable-XYZ to make your desire clear, and then
+       cairo errs during configure if your intention cannot be followed.
+
+     * default=no: These are the "experimental" features, and hence by
+       default off.  Use --enabled-XYZ to enable them.
+
+   The list of all features and their default state can be seen in the
+   output of ./configure --help.
+
+2) Compile the package:
+
+   This step is very simple. Just:
+
+       make
+
+   The Makefiles included with cairo are designed to work on as many
+   different systems as possible.
+
+   When cairo is compiled, you can also run some automated tests of
+   cairo with:
+
+       make check
+
+   NOTE: Some versions of X servers will cause the -xlib tests to
+   report failures in make check even when cairo is working just
+   fine. If you see failures in nothing but -xlib tests, please
+   examine the corresponding -xlib-out.png images and compare them to
+   the -ref.png reference images (the -xlib-diff.png images might also
+   be useful). If the results seem "close enough" please do not report
+   a bug against cairo as the "failures" you are seeing are just due
+   to subtle variations in X server implementations.
+
+3) Install the package:
+
+   The final step is to install the package with:
+
+       make install
+
+   If you are installing to a system-wide location you may need to
+   temporarily acquire root access in order to perform this
+   operation. A good way to do this is to use the sudo program:
+
+       sudo make install
+
+Extremely detailed build instructions
+-------------------------------------
+So you want to build cairo but it didn't come with a configure
+script. This is probably because you have checked out the latest
+in-development code via git. If you need to be on the bleeding edge,
+(for example, because you're wanting to develop some aspect of cairo
+itself), then you're in the right place and should read on.
+
+However, if you don't need such a bleeding-edge version of cairo, then
+you might prefer to start by building the latest stable cairo release:
+
+       http://cairographics.org/releases
+
+or perhaps the latest (unstable) development snapshot:
+
+       http://cairographics.org/snapshots
+
+There you'll find nicely packaged tar files that include a configure
+script so you can go back the the simpler instructions above.
+
+But you're still reading, so you're someone that loves to
+learn. Excellent! We hope you'll learn enough to make some excellent
+contributions to cairo. Since you're not using a packaged tar file,
+you're going to need some additional tools beyond just a C compiler in
+order to compile cairo. Specifically, you need the following utilities:
+
+       automake
+       autoconf
+       autoheader
+       aclocal
+       libtoolize
+       pkg-config [at least version 0.16]
+       gtk-doc (recommended)
+
+Hopefully your platform of choice has packages readily available so
+that you can easily install things with your system's package
+management tool, (such as "apt-get install automake" on Debian or "yum
+install automake" on Fedora, etc.). Note that Mac OS X ships with
+glibtoolize instead of libtoolize.
+
+Once you have all of those packages installed, the next step is to run
+the autogen.sh script. That can be as simple as:
+
+       ./autogen.sh
+
+But before you run that command, note that the autogen.sh script
+accepts all the same arguments as the configure script, (and in fact,
+will generate the configure script and run it with the arguments you
+provide). So go back up to step (1) above and see what additional
+arguments you might want to pass, (such as prefix). Then continue with
+the instructions, simply using ./autogen.sh in place of ./configure.
+
+Happy hacking!
diff --git a/KNOWN_ISSUES b/KNOWN_ISSUES
new file mode 100644 (file)
index 0000000..5fda683
--- /dev/null
@@ -0,0 +1,10 @@
+There are a few known bugs in 1.10 that have been fixed in master, but
+appear to be non-trivial to backport without fear of causing other
+regressions. The impact of these bugs is considered to be less than of a
+risk than rewriting the code.
+
+Zero Path Extents
+-----------------
+A closed degenerate path is reported as having extents (0, 0) x (0, 0),
+whereas the expected value is (x, y) x (0, 0). This regression has existed
+since at least 1.2.
diff --git a/Makefile.am b/Makefile.am
new file mode 100644 (file)
index 0000000..7c03294
--- /dev/null
@@ -0,0 +1,82 @@
+include $(top_srcdir)/build/Makefile.am.common
+
+EXTRA_DIST += \
+       KNOWN_ISSUES \
+       README.win32 \
+       Makefile.win32 \
+       build/Makefile.win32.common \
+       build/Makefile.win32.features \
+       build/Makefile.win32.features-h \
+       $(NULL)
+#MAINTAINERCLEANFILES += \
+#      $(srcdir)/build/Makefile.win32.features \
+#      $(srcdir)/build/Makefile.win32.features-h \
+#      $(NULL)
+
+ACLOCAL_AMFLAGS = -I build
+
+DIST_SUBDIRS = src doc util boilerplate test perf
+SUBDIRS = src doc util
+# libpng is required for our test programs
+if CAIRO_HAS_PNG_FUNCTIONS
+SUBDIRS += boilerplate test perf
+endif
+
+configure: cairo-version.h
+
+doc:
+       cd doc && $(MAKE) $(AM_MAKEFLAGS) $@
+test retest recheck: all
+       cd test && $(MAKE) $(AM_MAKEFLAGS) $@
+perf: all
+       cd perf && $(MAKE) $(AM_MAKEFLAGS) $@
+check-valgrind: all
+       cd test && $(MAKE) $(AM_MAKEFLAGS) check-valgrind
+       cd perf && $(MAKE) $(AM_MAKEFLAGS) check-valgrind
+.PHONY: doc test retest recheck perf check-valgrind
+
+
+EXTRA_DIST += \
+       AUTHORS \
+       BIBLIOGRAPHY \
+       BUGS \
+       CODING_STYLE \
+       COPYING \
+       COPYING-LGPL-2.1 \
+       COPYING-MPL-1.1 \
+       HACKING \
+       INSTALL \
+       NEWS \
+       PORTING_GUIDE \
+       README \
+       RELEASING \
+       autogen.sh \
+       cairo-version.h \
+       $(NULL)
+
+DISTCLEANFILES += config.cache
+
+MAINTAINERCLEANFILES += \
+       $(srcdir)/aclocal.m4 \
+       $(srcdir)/autoscan.log \
+       $(srcdir)/build/compile \
+       $(srcdir)/build/config.guess \
+       $(srcdir)/build/config.sub \
+       $(srcdir)/build/depcomp \
+       $(srcdir)/build/install-sh \
+       $(srcdir)/build/ltmain.sh \
+       $(srcdir)/build/missing \
+       $(srcdir)/build/mkinstalldirs \
+       $(srcdir)/config.h.in \
+       $(srcdir)/configure.scan \
+       $(NULL)
+
+DISTCHECK_CONFIGURE_FLAGS = \
+       --enable-gtk-doc \
+       --enable-test-surfaces \
+       --enable-full-testing \
+       $(NULL)
+
+include $(srcdir)/build/Makefile.am.changelog
+include $(srcdir)/build/Makefile.am.releasing
+include $(srcdir)/build/Makefile.am.analysis
diff --git a/Makefile.win32 b/Makefile.win32
new file mode 100644 (file)
index 0000000..f475246
--- /dev/null
@@ -0,0 +1,16 @@
+# Do not edit this file.
+# Edit build/Makefile.win32.common for customization
+
+top_srcdir = .
+include $(top_srcdir)/build/Makefile.win32.common
+
+all: cairo
+
+cairo: inform
+       @$(MAKE) -C src -f Makefile.win32
+
+perf: inform
+       @$(MAKE) -C perf -f Makefile.win32 perf
+
+test: inform
+       @$(MAKE) -C test -f Makefile.win32 test
diff --git a/NEWS b/NEWS
new file mode 100644 (file)
index 0000000..8ec1577
--- /dev/null
+++ b/NEWS
@@ -0,0 +1,6571 @@
+Snapshot 1.11.2 (2011-01-23)
+===========================
+
+In this first snapshot along the way to cairo-1.12.0, we are very excited
+to announce the introduction of Bezier surface gradients, known as type
+6/7 gradients in PS/PDF parlance. This is the culmination of much work by
+the dynamic duo: Adrian Johnson and Andrea Canciani. Thanks guys!
+
+Also, I want to warmly welcome Uli Schlachter who recently joined the
+Cairo community on a mission. That mission is to make cairo-xcb a
+supported backend for 1.12. And for this snapshot he has made great
+strides in fixing all the bugs I had left behind. Thanks Uli!
+
+And we have also seen a new contributor, Alexandros Frantzis, who has
+begun bringing up cairo-gl for GLESv2 devices. Thanks Alex!
+
+And lastly, I must also thank Adrian and Andrea for the vast numbers of
+bugs that they have tackled between them, fixing all those little corner
+cases that lie hidden until too late.
+
+API additions:
+
+The ability to construct piece-wise Bezier surface gradients:
+
+       cairo_pattern_create_mesh
+
+       constructs a pattern of type CAIRO_PATTERN_TYPE_MESH using
+
+       cairo_pattern_mesh_begin_patch
+       cairo_pattern_mesh_end_patch
+       cairo_pattern_mesh_curve_to
+       cairo_pattern_mesh_line_to
+       cairo_pattern_mesh_move_to
+       cairo_pattern_mesh_set_control_point
+       cairo_pattern_mesh_set_corner_color_rgb
+       cairo_pattern_mesh_set_corner_color_rgba
+       cairo_pattern_mesh_get_patch_count
+       cairo_pattern_mesh_get_path
+       cairo_pattern_mesh_get_corner_color_rgba
+       cairo_pattern_mesh_get_control_point
+
+The introduction of a unique ID accessible via the mime data type:
+       CAIRO_MIME_TYPE_UNIQUE_ID
+
+
+
+
+
+Release 1.10.2 (2010-12-25 Chris Wilson <chris@chris-wilson.co.uk>)
+===================================================================
+The cairo community is pleased to announce the 1.10.2 release of the
+cairo graphics library. This is the first update to cairo's stable 1.10
+series and contains a large number of bug fixes.
+
+While many people have contributed and have help to test the release,
+2 people deserve special recognition for their efforts in tracking down
+and fixing bugs, Andrea Canciani and Adrian Johnson. Thanks to their
+tremendous efforts, and of all cairo contributors, it is much
+appreciated.
+
+We recommend everyone upgrade to cairo 1.10.2 and hope that everyone
+will continue to have lots of fun with cairo!
+
+-Chris
+
+Bug fixes
+---------
+
+  Fix embedding of grayscale jpegs in PS.
+  https://bugs.freedesktop.org/show_bug.cgi?id=31632
+
+  Fix the reported path of extents containing a curve.
+
+  Fix the compositing of unaligned boxes.
+
+  Reset the clipper in PDF upon finish.
+
+  Fix degenerates arcs to become a degenerate line.
+
+  Build support for autoconf 2.67
+
+  Fix painting of transformed patterns in PS
+
+  Fix the EPS bounding box for PS
+  https://bugs.freedesktop.org/show_bug.cgi?id=24688
+
+  Fix the missing content for EPS
+  https://bugs.freedesktop.org/show_bug.cgi?id=24688
+
+  Fix regression upon changing page size in PS/PDF
+  https://bugs.freedesktop.org/show_bug.cgi?id=24691
+
+  Only use ActualText with PDF-1.5 documents
+
+  Fix the bbox for type1 fallbacks.
+
+  Reset the color after ending the context in PDF
+  https://bugs.freedesktop.org/show_bug.cgi?id=31140
+
+  Fix the advance of subsetted type1 fonts
+  https://bugs.freedesktop.org/show_bug.cgi?id=31062
+
+  Fix handling of EXTEND_NONE gradients for PDF
+
+  Restrict in-place optimisation for a8 image masks with SOURCE
+
+
+Release 1.10.0 (2010-09-06 Chris Wilson <chris@chris-wilson.co.uk>)
+===================================================================
+The cairo community is astounded (and flabbergast) to finally announce
+the 1.10.0 release of the cairo graphics library. This is a major update
+to cairo, with new features and enhanced functionality which maintains
+compatibility for applications written using any previous major cairo
+release, (1.8, 1.6, 1.4, 1.2, or 1.0). We recommend that anybody using
+a previous version of cairo upgrade to cairo 1.10.0.
+
+One of the more interesting departures for cairo for this release is the
+inclusion of a tracing utility, cairo-trace. cairo-trace generates a
+human-readable, replayable, compact representation of the sequences of
+drawing commands made by an application. This can be used to inspecting
+applications to understand issues and as a means for profiling
+real-world usage of cairo.
+
+The traces generated by cairo-trace have been collected in 
+
+  git://git.cairographics.org/git/cairo-traces
+
+and have driven the performance tuning of cairo over the last couple of
+years. In particular, the image backend is much faster with a new
+polygon rasterisation and a complete overhaul of the tessellator. Not
+only is this faster, but also eliminates visual artifacts from
+self-intersecting strokes. Not only has cairo-trace been driving
+performance improvements within cairo, but as a repeatable means of
+driving complex graphics it has been used to tune OpenGL, DDX, and
+pixman.
+
+Cairo's API has been extended to better support printing, notably
+through the ability to include a single compressed representation of an
+image for patterns used throughout a document, leading to dramatic file
+size reductions. Also the meta-surface used to record the vector
+commands compromising a drawing sequence is now exposed as a
+CAIRO_SURFACE_TYPE_RECORDING, along with a new surface that is a child of a
+larger surface, CAIRO_SURFACE_TYPE_SUBSURFACE. One typical usage of a
+subsurface would be as a source glyph in a texture atlas, or as a
+restricted subwindow within a canvas.
+
+Cairo's API has also resurrected the RGB16 format from the past as
+the prevalence of 16-bit framebuffers has not diminished and is a
+fore-taste of the extended format support we anticipate in the future.
+Increasing cairo's utility, we introduce the cairo_region_t for handling
+sets of pixel aligned rectangles commonly used in graphics applications.
+This is a merger of the GdkRegion and the pixman_region_t, hopefully
+providing the utility of the former with the speed of the latter.
+
+Furthermore cairo has been reworked to interoperate more closely with
+various acceleration architectures, gaining the ability to share
+those hardware resources through the new cairo_device_t. For instance,
+with the new OpenGL backend that supersedes the Glitz backend, hardware
+and rendering operations can be shared between a classic OpenGL
+application mixing libVA for the hardware assisted video decode with
+cairo for high quality overlays all within the same OpenGL canvas.
+
+Many thanks for the hard work of Adrian Johnson, Andrea Canciani, Behdad
+Esfahbod, Benjamin Otte, Carl Worth, Carlos Garcia Campos, Chris Wilson,
+Eric Anholt, Jeff Muizelaar, Karl Tomlinson, M Joonas Pihlaja, Søren
+Sandmann Pedersen and many others that have contributed over the last
+couple of years to cairo. Thank you all!
+
+Snapshot 1.9.14 (2010-07-26)
+============================
+
+  A quiet couple of weeks, hopefully Cairo is seeing widescale deployment and
+  we are being to see the results of the stabilisation effort. Clipping bugs
+  seems to have been the order of the last couple of weeks, with a couple
+  reported and duly fixed. Thank you Igor Nikitin and Karl Tomlinsion for
+  finding those regressions. At this point all that seems to remain to do is
+  to fix the outstanding regressions in the PDF backend...
+
+Bugs fixes
+----------
+
+    Clip doesn't work for text on the image backend
+    https://bugs.freedesktop.org/show_bug.cgi?id=29008
+
+    Add explicit dependency for cxx
+    https://bugs.freedesktop.org/show_bug.cgi?id=29114
+
+    Fix regressions in reporting clip extents
+    https://bugs.freedesktop.org/show_bug.cgi?id=29120
+    https://bugs.freedesktop.org/show_bug.cgi?id=29121
+    https://bugs.freedesktop.org/show_bug.cgi?id=29122
+    https://bugs.freedesktop.org/show_bug.cgi?id=29124
+    https://bugs.freedesktop.org/show_bug.cgi?id=29125
+
+
+Snapshot 1.9.12 (2010-07-12)
+============================
+
+  A couple of weeks spent fixing those annoying bugs and cleaning up the build
+  system; the list of outstanding tasks to complete for the stable release is
+  finally shrinking. The chief bug fixer has been Benjamin Otte who not only
+  made sure that the public API is consistent and being tested for its
+  consistency, but also ensured that the documentation was up-to-date and
+  spent time clarifying cases where even the Cairo developers have come
+  unstuck in the past. Many thanks, Benjamin. However, he was not alone,
+  as Andrea Canciani continued his fine work in isolating broken corner cases
+  and proceeding to fix them, and tidying up the quartz backend. And last, but
+  definitely not least, M Joonas Pihlaja tried building Cairo across a
+  perverse range of systems and fixed up all the loose bits of code that came
+  unravelled. Thanks everybody!
+
+API Changes
+-----------
+
+  cairo_surface_set_mime_data, cairo_surface_get_mime_data:
+
+    The length parameter is now an unsigned long (as opposed to an unsigned
+    int). The parameter is intended to be an equivalent to a size_t without
+    requiring POSIX types and be large enough to store the size of the
+    largest possible allocation.
+
+  cairo_gl_surface_create_for_texture:
+
+    This a new surface constructor for cairo-gl that explicitly enables
+    render-to-texture for foreign, i.e. application, textures.
+
+  cairo_region_xor, cairo_region_xor_rectangle
+
+    A couple of utility routines add to the region handling interface for
+    the purpose of replacing existing GdkRegion functionality.
+
+Bugs fixes
+----------
+
+  https://bugs.launchpad.net/ubuntu/+source/cairo/+bug/600622
+
+    Inkscape was caught in the act of attempting to modify a finished surface.
+    Unfortunately, we had the ordering of our guards and assertions wrong and
+    so an ordinary application error was triggering an assert in Cairo. This
+    lead Benjamin to add a test case to ensure that the entire public API
+    could handle erroneous input and then proceeded to fix a whole slew of
+    uncovered bugs.
+
+
+  https://bugs.freedesktop.org/show_bug.cgi?id=28888
+
+    A regression introduced by the special casing of uploading images to an
+    xlib surface in-place which was ignoring the translation applied to the
+    image.
+
+
+Snapshot 1.9.10 (2010-06-26)
+============================
+
+   The first "quick" snapshot in the run up to the stable release.  The
+   last snapshot was picked up by the bleeding edge distributions and so the
+   bug reports have to started to roll in.  The most frequent of these are the
+   introduction of rendering errors by applications that modify a surface
+   without subsequently calling cairo_surface_mark_dirty(). Make sure the
+   application developers are aware of increased reliance on strict use of the
+   Cairo API before 1.10 is released!
+
+   The usual slew of bugs reported and we would like to thank Zoxc for
+   contributing the WGL interface for cairo-gl, and finding more build
+   failures on win32.  And it just wouldn't be a 1.9 snapshot unless
+   Benjamin Otte improved the error handling within cairo-gl, as well as
+   isolating and fixing some more errors in the test suite. The biggest bug of
+   the snapshot turned out to be a major sign extension issue that had lain
+   hidden for many years and was suddenly exposed by incorrectly rounding
+   rectangles when performing non-antialiased rendering.  Also to the relief
+   of many we have included the downstream patch to honour the user's LCD
+   filtering preferences for subpixel rendering of fonts.  The interface
+   remains private for the time being, whilst the proposed public API is
+   finalized.
+
+API changes
+-----------
+   None.
+
+Snapshot 1.9.8 (2010-06-12)
+===========================
+
+   One major API changes since the last snapshot, and a whole slew of bugs
+   fixed and inconsistencies eliminated. Far too many bugs fixed to
+   individually identify. We need to thank Benjamin Otte for his fantastic
+   work on the cairo-gl backend making it faster and more robust, Andrea
+   Canciani for finding so many bugs and developing test cases for them, as
+   well fixing them. And last but not least we must all thank Adrian Johnson for
+   continuing to eliminate bugs and improving the PostScript and PDF backends.
+
+   This snapshot represents almost 4 months of bug fixing, bringing Cairo to
+   a point where we consider it almost ready to be a candidate for release.
+   There are a few known bugs left to be fixed, being tracked in
+   https://bugs.freedesktop.org/show_bug.cgi?id=24384, so please give Cairo a
+   whirl and report any regressions. The plan is to release a new snapshot
+   every other week leading to a 1.10 release with a target date of
+   2010-08-16.
+
+API additions
+-------------
+  CAIRO_FORMAT_RGB16_565
+
+    16 bit devices still remain popular, and so with great demand,
+    CAIRO_FORMAT_RGB16_565 has been restored enabling applications to create
+    and use 16 bit images as sources and render targets.
+
+  cairo_surface_create_for_rectangle()
+
+    It is common practice to cut an image up into many smaller pieces and use
+    each of those as a source - a technique called texture atlasing.
+    cairo_surface_create_for_rectangle() extends Cairo to directly support use
+    of these subregions of another cairo_surface_t both as a source and as a
+    render target.
+
+  cairo_region_create()
+  cairo_region_create_rectangle()
+  cairo_region_create_rectangles()
+  cairo_region_copy()
+  cairo_region_reference()
+  cairo_region_destroy()
+  cairo_region_equal()
+  cairo_region_status()
+  cairo_region_get_extents()
+  cairo_region_num_rectangles()
+  cairo_region_get_rectangle()
+  cairo_region_is_empty()
+  cairo_region_contains_rectangle()
+  cairo_region_contains_point()
+  cairo_region_translate()
+  cairo_region_subtract()
+  cairo_region_subtract_rectangle()
+  cairo_region_intersect()
+  cairo_region_intersect_rectangle()
+  cairo_region_union()
+  cairo_region_union_rectangle()
+
+    The Cairo region API was actually added a couple of snapshots ago, but we
+    forgot to mention it at the time. A simple API for the handling of
+    rectangular pixel-aligned regions by Soeren Sandmann.
+  
+
+Backend-specific improvements
+-----------------------------
+cairo-gl
+
+  Benjamin Otte made more than 200 commits in which he refactored the cairo-gl
+  backend, reducing a lot of code duplication and enabled him to begin working
+  on improving performance by reducing state changes and associated overhead.
+
+cairo-xlib
+
+  Access to the underlying connection to the Display is now thread-safe
+  enabling cairo-xlib to be used in a multi-threaded application without fear
+  of random corruption. Thanks Benjamin Otte!
+
+  cairo-xlib will now attempt to use PolyModeImprecise when compositing
+  trapezoids (i.e. a fill or a stroke operation with a non-trivial path) which
+  should allow hardware drivers more scope for accelerating the operation at
+  the cost of potentially incurring minute rendering errors. The mode can be
+  forced back to PolyModePrecise by setting the antialias parameter to
+  CAIRO_ANTIALIAS_SUBPIXEL.
+
+cairo-svg
+
+  A notable improvement was contributed by Alexander Shulgin to enable SVG to
+  reference external image through the use an extended MIME data type.
+
+Snapshot 1.9.6 (2010-02-19)
+===========================
+API additions
+-------------
+    Add cairo_device_t
+
+    The device is a generic method for accessing the underlying interface
+    with the native graphics subsystem, typically the X connection or
+    perhaps the GL context. By exposing a cairo_device_t on a surface and
+    its various methods we enable finer control over interoperability with
+    external interactions of the device by applications. The use case in
+    mind is, for example, a multi-threaded gstreamer which needs to serialise
+    its own direct access to the device along with Cairo's across many
+    threads.
+
+    Secondly, the cairo_device_t is a unifying API for the mismash of
+    backend specific methods for controlling creation of surfaces with
+    explicit devices and a convenient hook for debugging and introspection.
+
+    The principal components of the API are the memory management of:
+
+      cairo_device_reference(),
+      cairo_device_finish() and
+      cairo_device_destroy();
+
+    along with a pair of routines for serialising interaction:
+
+      cairo_device_acquire() and
+      cairo_device_release()
+
+    and a method to flush any outstanding accesses:
+
+      cairo_device_flush().
+
+    The device for a particular surface may be retrieved using:
+
+      cairo_surface_get_device().
+
+    The device returned is owned by the surface.
+
+API changes (to API new in the cairo 1.9.x series)
+--------------------------------------------------
+  cairo_recording_surface_create()
+  cairo_recording_surface_ink_extents()
+
+    These are the replacement names for the functions previously named
+    cairo_meta_surface_create and cairo_meta_surface_ink_extents.
+
+  cairo_surface_set_mime_data
+
+    This interface is now changed such that the MIME data will be
+    detached if the surface is modified at all. This guarantees that
+    the MIME data will not become out of synch due to surface
+    modifications, and also means that for the MIME data to be useful,
+    it must be set after all modifications to the surface are
+    complete.
+
+API removal (of experiment API)
+-------------------------------
+  The cairo-glitz backend is removed entirely, (in favor of the new
+  cairo-gl backend). See below for more on cairo-gl.
+
+Generic fixes
+-------------
+
+  Many improvements for drawing of dashed strokes
+
+       Fix incorrect handling of negative offset
+       Faster computation of first dash (avoids near-infinite looping)
+       Approximate extremely fine dash patterns with appropriate alpha value
+
+  Optimize spans-based renderers for repeated rows, (such as in a rounded rectangle)
+
+Backend-specific improvements
+-----------------------------
+cairo-drm
+
+  This is a new, direct-rendering backend that supports Intel graphics
+  chipsets in the i915 and i965 families. It's still experimental and
+  will likely remain that way for a while. It's already got extremely
+  good performance on the hardware it supports, so if nothing else
+  provides a working proof and performance target for the cairo-gl
+  work for Intel graphics.
+
+cairo-gl
+
+  Start using GLSL to accelerate many operations. Many thanks to Eric
+  Anholt and T. Zachary Laine for this work. For the first time, we
+  have what looks like what will be a very compelling OpenGL-based
+  backend for cairo (in terms of both quality and performance).
+
+  See this writeup from Eric for more details on recent progress of
+  cairo-gl (which he presented at FOSDEM 2010):
+
+       http://anholt.livejournal.com/42146.html
+
+cairo-image
+
+  The image backend is made dramatically faster (3-5 times faster for
+  benchmarks consisting primarily of glyph rendering).
+
+cairo-quartz fixes:
+
+  Many fixes from Robert O'Callahan and Andrea Canciani including:
+
+       Fixed gradient pattern painting
+       Improved A8 image handling
+       Fixes for "unbounded" and other compositing operators
+
+cairo-pdf fixes:
+
+  Improvements to embedding of JPEG and JPEG2000 data.
+
+cairo-ps fixes:
+
+  Fix printing of rotated user fonts.
+
+Snapshot 1.9.4 (2009-10-15)
+===========================
+API additions:
+
+  cairo_meta_surface_create()
+  cairo_meta_surface_ink_extents()
+
+    Finally exporting the internal meta-surface so that applications
+    have a method to record and replay a sequence of drawing commands.
+
+  cairo_in_clip()
+
+    Determines whether a given point is inside the current clip.
+    ??? Should this be called cairo_in_paint() instead? in-clip is the test
+    that is performed, but in-paint would be similar to in-fill and in-stroke.
+
+New utilities:
+
+  cairo-test-trace
+
+    A companion to cairo-perf-trace, this utility replays a trace against
+    multiple targets in parallel and looks for differences in the output,
+    and then records any drawing commands that cause a failure.
+    Future plans:
+      Further minimisation of the fail trace using "delta debugging".
+      More control over test/reference targets.
+
+Backend improvements:
+
+  xlib
+
+     Server-side gradients. The theory is that we can offload computation
+     of gradients to the GPU and avoid pushing large images over the
+     connection. Even if the driver has to fallback and use pixman to render
+     a temporary source, it should be able to do so in a more efficient manner
+     than Cairo itself. However, cairo-perf suggests otherwise:
+
+     On tiny, Celeron/i915:
+
+      before: firefox-20090601 211.585
+       after: firefox-20090601 270.939
+
+     and on tiger, CoreDuo/nvidia:
+
+      before: firefox-20090601 70.143
+       after: firefox-20090601 87.326
+
+     In particular, looking at tiny:
+
+     xlib-rgba paint-with-alpha_linear-rgba_over-512   47.11 (47.16 0.05%) -> 123.42 (123.72 0.13%):  2.62x slowdown
+     █▋
+     xlib-rgba paint-with-alpha_linear3-rgba_over-512   47.27 (47.32 0.04%) -> 123.78 (124.04 0.13%):  2.62x slowdown
+     █▋
+
+
+New experimental backends:
+
+   QT
+
+   OpenVG - The initial work was done by Øyvind Kolås, and made ready for
+            inclusion by Pierre Tardy.
+
+   OpenGL - An advanced OpenGL compositor. The aim is to write a integrate
+            directed rendering using OpenGL at a high-level into Cairo. In
+           contrast to the previous attempt using Glitz which tried to
+           implement the RENDER protocol on top of OpenGL, using the
+           high-level interface should permit greater flexibility and
+           more offloading onto the GPU.
+           The initial work on the backend was performed by Eric Anholt.
+
+Long standing bugs fixed:
+
+  Self-intersecting strokes.
+
+    A long standing bug where the coverage from overlapping semi-opaque
+    strokes (including neighbouring edges) was simply summed in lieu of
+    a costly global calculation has been fixed (by performing the costly
+    global calculation!) In order to mitigate the extra cost, the
+    tessellator has been overhauled and tune, which handles the fallback
+    for when we are unable to use the new span rasteriser on the stroke
+    (e.g. when using the current RENDER protocol). The large number of
+    pixel artefacts that implementing self-intersection elimination
+    removes is ample justification for the potential performance
+    regression. If you unfortunately do suffer a substantial performance
+    regression in your application, please consider obtaining a
+    cairo-trace and submitting it to us for analysis and inclusion into
+    our performance suite.
+
+Special thanks:
+
+   To the AuroraUX team for providing access to one of their OpenSolaris
+   machines for cairo and pixman development.  http://www.auroraux.org/
+
+Snapshot 1.9.2 (2009-06-12)
+===========================
+API additions:
+
+  cairo_surface_set_mime_data()
+  cairo_surface_get_mime_data()
+
+    Should this take unsigned int, unsigned long or size_t for the length
+    parameter? (Some datasets may be >4GiB in size.)
+
+    Associate an alternate, compressed, representation for a surface.
+    Currently:
+     "image/jp2" (JPEG2000) is understood by PDF >= 1.5
+     "image/jpeg" is understood by PDF,PS,SVG,win32-printing.
+     "image/png" is understood by SVG.
+
+  cairo_pdf_version_t
+  cairo_pdf_surface_restrict_to_version()
+  cairo_pdf_get_versions()
+  cairo_pdf_version_to_string()
+
+    Similar to restrict to version and level found in SVG and PS,
+    these limit the features used in the output to comply with the PDF
+    specification for that version.
+
+  CAIRO_STATUS_INVALID_SIZE
+    Indicates that the request surface size is not supported by the
+    backend.  This generally indicates that the request is too large.
+
+  CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED
+    Indicates that a required callback for a user-font was not implemented.
+
+  CAIRO_STATUS_LAST_STATUS
+    This is a special value to indicate the number of status values enumerated
+    at compile time. (This may differ to the number known at run-time.)
+
+  The built-in twin font is now called "@cairo:" and supports a limited set
+  of options like "@cairo:mono". Where are these specified?
+
+  cairo_in_fill() now uses HTML Canvas semantics, all edges are inside.
+
+New experimental backends:
+
+   CairoScript
+
+New utility:
+
+  cairo-trace and cairo-perf-trace
+
+    cairo-trace generates a human-readable, replayable, compact(-ish!)
+    representation of the sequences of drawing commands made by an
+    application.
+
+    Under the util/cairo-script directory is a library to replay traces.
+
+    perf/cairo-perf-trace replays traces against multiple backends
+    and makes useful benchmark reports. This is integrated with
+    'make perf'. You may collect your own traces or take advantage
+    of traces collected by the community:
+
+      git://git.cairographics.org/git/cairo-traces
+
+    (Put this into perf/cairo-traces to run these as part of "make perf".)
+
+    There is additional WIP in building a debugging tool for cairo applications
+    based on CairoScript (currently very preliminary, mostly serves to show
+    that GtkSourceView is too slow) :
+
+      people.freedesktop.org:~ickle/sphinx
+
+Test suite overhaul:
+
+  The test suite is undergoing an overhaul, primarily to improve its speed
+  and utility. (Expect more changes in the near future to improve XFAIL
+  handling.)
+
+Optimisations:
+  polygon rasterisation! Joonas implemented the Tor polygon scan converter,
+  on typical geometry is about 30% faster for the image backend.
+
+  Bovine Polaroids! For those not in on the joke, this is the long
+  awaited "copy-on-write snapshot" or "COW snapshot" support. The
+  user-visible feature is that including the same image multiple times
+  into a PDF file should result in only a single instance of that
+  image in the final output. This is unlike previous versions of cairo
+  which would generate very large PDF files with multiple copies of
+  the same image. Adrian says that the PDF is not quite working as
+  well as it should yet, so we hope for futher improvements before
+  cairo 1.10.
+
+Bug fixes:
+
+  EXTEND_PAD.
+
+  Better handling of large scale-factors on image patterns.
+
+  Emit /Interpolate for PS,PDF images.
+
+  Global glyph cache - cap on the total number of inactive glyphs,
+  should prove fairer for fonts with larger glyph sets.
+
+  Compilation without fontconfig
+
+  Improved handling of low-bitdepth sources (e.g. copying the contents
+  of 16-bit xserver windows)
+
+Regressions:
+
+  cairo_traps_extract_region >10x slower. Fix pending.
+
+Still to come:
+
+  Region tracking API (ssp) for damage tracking, hit testing etc
+  mime-surface
+
+  An expiremental OpenGL backend?
+
+  Tweaks to tessellator, allocations of patterns, delayed
+  initialisation of the xlib backend (reduce the cairo overhead of
+  render_bench by ~80%).
+
+
+Release 1.8.8 (2009-06-16 Chris Wilson <chris@chris-wilson.co.uk>)
+==================================================================
+The cairo community is pleased to announce the 1.8.8 release of the
+cairo graphics library. This is the fourth update to cairo's stable
+1.8 series and contains a small number of bug fixes (in particular a
+few corrections to the documentation and a few fixes in the FreeType font
+backend). This is being released just over six months after cairo 1.8.6.
+
+We recommend that everyone using cairo upgrade to 1.8.8.
+
+-Chris
+
+Build fixes
+-----------
+There were reports of incompatibilities with the autotools bundled in with
+the 1.8.6 tarball.  This release has been built with automake-1.10.2 and
+autoconf-2.63.
+
+The configure check for FreeType has been improved:
+
+   typo in check for version of freetype in configure script
+   https://bugs.freedesktop.org/show_bug.cgi?id=19283
+
+Compilation on 64-bit MacOS/X fixes:
+
+  Cannot build cairo_quartz_font_face_create_for_atsu_font_id on 64-bit Mac OS X
+  https://bugs.freedesktop.org/show_bug.cgi?id=15702
+
+Bug fixes
+---------
+Uninitialised status return within _cairo_clip_intersect_mask(). This caused
+random crashes and general mayhem as an error could be generated causing all
+rendering to the context to stop.
+
+Avoid transforming nearly-degenerate matrices into degenerate matrices:
+
+   Painting stops in this case, using -moz-transform: scale, rotate and video
+   https://bugzilla.mozilla.org/show_bug.cgi?id=467423
+
+A few FreeType font handling bugs were fixed:
+
+   Rendering with PANGO_GRAVITY_EAST leads to different results with image and pdf
+   https://bugs.freedesktop.org/show_bug.cgi?id=21985
+
+   Don't call FT_Done_Face() on faces we did not create
+
+   zombie ft_font_face / ft_unscaled_font mutual referencing problems
+   http://bugs.freedesktop.org/show_bug.cgi?id=21706
+
+Ensure win32 font backend sets the return value to -1 (indicating the absent
+glyph) if the font index lookup for the unicode character fails. And
+similarly fix a bug where a fatal error was raised for an invalid glyph.
+
+   cairo_scaled_font_glyph_extents breaks with invalid glyph id
+   http://bugs.freedesktop.org/show_bug.cgi?id=20255
+
+Various improvements to the documentation, reported by Truc Troung:
+
+   https://bugs.freedesktop.org/show_bug.cgi?id=20095
+   https://bugs.freedesktop.org/show_bug.cgi?id=20154
+   https://bugs.freedesktop.org/show_bug.cgi?id=20180
+   https://bugs.freedesktop.org/show_bug.cgi?id=20183
+   https://bugs.freedesktop.org/show_bug.cgi?id=20182
+   https://bugs.freedesktop.org/show_bug.cgi?id=20441
+
+
+Release 1.8.6 (2008-12-13 Chris Wilson <chris@chris-wilson.co.uk>)
+==================================================================
+The cairo community is pleased to announce the 1.8.6 release of the
+cairo graphics library. This is the third update to cairo's stable
+1.8 series and contains a small number of bug fixes (in particular a
+few fixes for failures of cairo 1.8.4 on Quartz and PDF, and build fixes for
+a couple of backends). This is being released just under a month after
+cairo 1.8.4.
+
+We recommend that everyone using cairo upgrade to 1.8.6.
+
+-Chris
+
+Build fixes
+-----------
+Fix build of DirectFB backend with debugging enabled:
+
+   Bug in _cairo_directfb_surface_release_source_image function
+   http://bugs.freedesktop.org/show_bug.cgi?id=18322
+
+Fix build on OS/2.
+
+Bug fixes
+---------
+Workaround a mis-compilation of cairo_matrix_invert() that generated invalid
+matrices and triggered assertion failures later. The issue was reported by
+Peter Hercek.
+
+Invalid computation of the modulus:
+
+   https://bugzilla.mozilla.org/show_bug.cgi?id=466258
+
+Invalid referencing of patterns in the Quartz backend:
+
+   Failed assertion `CAIRO_REFERENCE_COUNT_HAS_REFERENCE
+   (&pattern->ref_count)' when using cairo quartz backend
+   http://bugs.freedesktop.org/show_bug.cgi?id=18632
+
+Invalid references to glyphs after early culling, causing segmentation faults
+in the PDF backend:
+
+   http://lists.cairographics.org/archives/cairo/2008-December/015976.html
+
+Check for XRender in the XCB backend, or else we may attempt an invalid memory
+access:
+
+    XCB backend fails with missing render.
+    https://bugs.freedesktop.org/show_bug.cgi?id=18588
+
+Release 1.8.4 (2008-11-14 Carl Worth <cworth@cworth.org>)
+=========================================================
+The cairo community is pleased to announce the 1.8.4 release of the
+cairo graphics library. This is the second update to cairo's stable
+1.8 series and contains a small number of bug fixes, (in particular a
+few fixes for build failures of cairo 1.8.2 on various systems). This
+is being released just over two weeks after cairo 1.8.2.
+
+We recommend that everyone using cairo upgrade to 1.8.4.
+
+-Carl
+
+Build fixes
+-----------
+Fix build with older XRender that doesn't define RepeatNone:
+
+   Build of xlib backend fails against old XRender (RepeatNone undeclared)
+   https://bugs.freedesktop.org/show_bug.cgi?id=18385
+
+Fix build with bash version <= 3.0:
+
+   doltlibtool broken on linux with bash 3.00.0
+   https://bugs.freedesktop.org/show_bug.cgi?id=18363
+
+Bug fixes
+---------
+Avoid triggering a bug in X.org server 6.9 resulting in a hung machine
+requiring a reboot:
+
+    https://bugs.freedesktop.org/show_bug.cgi?id=15628#c2
+
+Fix display of user fonts as exercised by proposed support for type3
+fonts in poppler (unsigned promotion fixes):
+
+    Use cairo user-font for Type 3 fonts
+    http://lists.freedesktop.org/archives/poppler/2008-October/004181.html
+
+Avoid miscomputing size of fallback images required when rendering
+with CLEAR, IN, or SOURCE operator to vector surfaces, (PS, PDF, SVG,
+etc.).
+
+Be more tolerant of broken fonts when subsetting type1 fonts:
+
+   Error handling in cairo_type1_font_subset_get_glyph_names_and_widths
+   http://lists.cairographics.org/archives/cairo/2008-October/015569.html
+
+Fix cairo_fill_extents, cairo_stroke_extents, cairo_path_extents, to
+correctly allow NULL parameters as documented.
+
+Fix potential crash on emitting a type3 glyph after having drawn text
+paths from the same font, (for example with cairo_text_path).
+
+Release 1.8.2 (2008-10-29 Carl Worth <cworth@cworth.org>)
+=========================================================
+The cairo community is pleased to announce the 1.8.2 release of the
+cairo graphics library. This is the first update to cairo's stable 1.8
+series and contains a large number of bug fixes. It is being released
+just over one month since cairo 1.8.0.
+
+This release consists primarily of bug fixes, but there is one notable
+new feature, (the ability to build cairo without an external font
+backend), and there are a few optimizations as well. See below for
+details on these changes and the most important bug fixes.
+
+While many people have contributed to this release, Chris Wilson
+deserves particular mention. He has contributed well over twice as
+many changes to cairo since 1.8.0 than everyone else combined. We
+greatly appreciate the tremendous efforts of Chris and all cairo
+contributors.
+
+We recommend everyone upgrade to cairo 1.8.2 and hope that everyone
+will have lots of fun with cairo!
+
+-Carl
+
+New feature
+-----------
+It is now possible to build cairo without any font backend, (such as
+freetype, win32 or quartz). This is most useful when the application
+provides custom font rendering through the user-font API. But in the
+case where no external font backend is available, and no user-font is
+provided, cairo will render with a failsafe font, (a stroked font
+covering visible ASCII character). (Behdad Esfahbod)
+
+Optimizations
+-------------
+Dramatically speed up compilation with dolt (removes much of the
+libtool overhead) (Behdad Esfahbod with thanks to Josh Triplett).
+
+Several minor optimizations to tessellator (special-cased comparisons,
+faster insert for skiplist, etc.) (Chris Wilson).
+
+Optimize away fractional translation component when doing
+EXTEND_NEAREST filtering, (for better performance).
+
+General bug fixes
+-----------------
+Allow cloning sub-regions of similar surfaces to fix this bug
+(Chris Wilson):
+
+       Crafted gif file will crash firefox
+       [XError: 'BadAlloc (insufficient resources for operation)']
+       https://bugzilla.mozilla.org/show_bug.cgi?id=424333
+
+Fix some matrix confusion to fix this regression (Chris Wilson):
+
+       Translucent star exports in a wrong way to PDF
+       https://bugs.launchpad.net/inkscape/+bug/234546
+
+Fix some long-standing bugs with respect to properly computing the
+extents of transformed, filtered surfaces (Owen Taylor, Carl Worth,
+and Chris Wilson):
+
+       Bad clipping with EXTEND_NONE
+       http://bugs.freedesktop.org/show_bug.cgi?id=15349
+
+       Improve filtering handling in cairo-pattern.c
+       http://bugs.freedesktop.org/show_bug.cgi?id=15367
+
+       Many thanks to Chris Wilson for digging out and cleaning up
+       these fixes.
+
+Fix compilation on Solaris 10 (Chris Wilson):
+
+       Cairo requires -DREENTRANT (along with  -D_POSIX_THREAD_SEMANTICS)
+       to compile on Solaris 10 with pthreads
+       https://bugs.freedesktop.org/show_bug.cgi?id=18010
+
+Fix very old bug causing dashes to be rendered at the wrong length in
+fallback images (Adrian Johnson)
+
+       Dashed strokes too long in fallback images
+       https://bugs.freedesktop.org/show_bug.cgi?id=9189
+
+Fix broken dashing when a dashed path starts outside the clip region
+(Chris Wilson).
+
+Avoid range overflow when computing large patterns (Benjamin Otte and
+Chris Wilson).
+
+Avoid crashing due to an invalid font with an incorrect entry in its
+CMAP table (Adrian Johnson).
+
+Fix bugs in computing maximum size of text requests that can be sent
+with the Render extension, (avoiding potential crashes when rendering
+large amounts of text) (Behdad Esfahbod and Chris Wilson).
+
+Fix rendering of operators unbounded by the mask (Chris Wilson).
+
+Fix compilation on systems without compiler support for a native
+64-bit type (Chris Wilson).
+
+Fix several cases of missing error-status propagation. (Chris Wilson,
+doing the work he seems to never tire of).
+
+Fix several locking issues found with the lockdep valgrind skin (Chris
+Wilson).
+
+Backend-specific bug fixes
+--------------------------
+xlib: Avoid crash due to attempting XRender calls on pixmaps with
+formats not supported by the Render extension (Chris Wilson):
+
+       XRender crashes due to NULL pointer from Cairo on SGI O2
+       https://bugs.freedesktop.org/show_bug.cgi?id=11734
+
+xlib: Add support for XImages with depth of 4, 20, 24, or 28 bits
+(Chris Wilson):
+
+       cairo doesn't support 24 bits per pixel mode on X11
+       https://bugs.freedesktop.org/show_bug.cgi?id=9102
+
+xlib: Avoid mistakenly considering two surfaces as similar just
+because their depths match (while their Render formats do not) (Karl
+Tomlinson).
+
+ps: Fix slight mis-scaling of bitmapped fonts (Adrian Johnson)
+
+svg: Correctly emit comp-op for paint, mask, and show_glyphs
+operations (Emmanuel Pacaud).
+
+svg: Use finer-grained fallbacks for SVG 1.2 (as PS and PDF backends
+have been doing since 1.6.0) (Chris Wilson).
+
+win32: Fallback to DIB if DDB create fails for
+cairo_surface_create_similar (Vladimir Vukicevic).
+
+win32: Fix compatibility with Windows Mobile (Vladimir Vukicevic).
+
+win32: Fix static builds to not do __declspec(dllimport) on public
+functions. This requires the user to set a CAIRO_WIN32_STATIC_BUILD
+environment variable when compiling (Behdad Esfahbod).
+
+Release 1.8.0 (2008-09-25 Carl Worth <cworth@cworth.org>)
+=========================================================
+The cairo community is happy (and relieved) to announce the 1.8.0
+release of the cairo graphics library. This is a major update to
+cairo, with new features and enhanced functionality which maintains
+compatibility for applications written using any previous major cairo
+release, (1.6, 1.4, 1.2, or 1.0). We recommend that anybody using a
+previous version of cairo upgrade to cairo 1.8.0.
+
+The dominant theme of this release is improvements to cairo's ability
+to handle text. The highlights include a new "user fonts" feature as
+well as a new cairo_show_text_glyphs API which allows glyphs to be
+embedded in PDF output along with their original text, (for searching,
+selection, and copy-and-paste). Another major feature is a revamp of
+cairo's build system making it much easier to build cairo on various
+platforms.
+
+See below for more details.
+
+User fonts
+----------
+This new API allows the user of cairo API to provide drawings for
+glyphs in a font. A common use for this is implementing fonts in
+non-standard formats, like SVG fonts and Flash fonts. This API can
+also be used by applications to provide custom glyph shapes for fonts
+while still getting access to cairo's glyph caches. See
+test/user-font.c and test/user-font-proxy.c for usage examples. This
+is based on early work by Kristian Høgsberg. Thanks Kristian!
+
+This new API consists of the following functions (and corresponding
+_get functions):
+
+       cairo_user_font_face_create
+
+       cairo_user_font_face_set_init_func
+       cairo_user_font_face_set_render_glyph_func
+       cairo_user_font_face_set_text_to_glyphs_func
+       cairo_user_font_face_set_unicode_to_glyph_func
+
+An additional, new API is
+
+       cairo_scaled_font_text_to_glyphs
+
+We were previously reluctant to provide this function as
+text-to-glyphs support in cairo was limited to "toy" font
+functionality, not really interesting for real-world text
+processing. However, with user fonts landing, this API is needed to
+expose full access to how user fonts convert text to glyphs. This is
+expected to be used by text toolkits like Pango, as well as "proxy"
+user-font implementations.
+
+cairo_show_text_glyphs
+----------------------
+This new API allows the caller of cairo to provide text data
+corresponding to glyphs being drawn. The PDF backend implements this
+new API so that complex text can be copied out of cairo's PDF output
+correctly and reliably, (assuming the user of cairo calls
+cairo_show_text_glyphs). The cairo_show_text_glyphs API is definitely
+the most daunting API to debut in cairo. It is anticipated that pango
+(and similar high-level text libraries) will be the primary users of
+this API. In fact, pango 1.22 already uses cairo_show_text_glyphs.
+Behdad was the architect and implementor of this effort. Thanks,
+Behdad!
+
+The cairo_show_text_glyphs API includes the following new functions:
+
+       cairo_show_text_glyphs
+
+       cairo_glyph_allocate
+       cairo_glyph_free
+
+       cairo_text_cluster_allocate
+       cairo_text_cluster_free
+
+       cairo_surface_has_show_text_glyphs
+
+Build system revamp
+-------------------
+The primary goal of the revamp is to make the build system less
+fragile, (particularly for non-Linux platforms). For example, now
+people building on win32 will no longer need to maintain a
+platform-specific list of files to be built. See the new README.win32
+for details. Also, the .so file will now be installed with a different
+naming scheme, (for example, 1.7.6 will install with a .10800
+suffix). Many thanks to Behdad and his small army of helpers!
+
+Assorted API additions
+----------------------
+For API completeness, several missing "getter" functions were added:
+
+       cairo_scaled_font_get_scale_matrix
+
+       cairo_surface_get_fallback_resolution
+
+       cairo_toy_font_face_create
+       cairo_toy_font_face_get_family
+       cairo_toy_font_face_get_slant
+       cairo_toy_font_face_get_weight
+
+The new cairo_toy_font_face functions provide access to functionality
+and settings provided by cairo_select_font_face(). Thanks Behdad!
+
+cairo-ps/cairo-pdf: More efficient output
+-----------------------------------------
+Adrian Johnson has been busy fixing all kinds of bugs in PS and PDF
+backends, as well making them generate much more compact output by
+avoiding things like re-emitting the color or linestyle on every
+drawing operation. Thanks Adrian!
+
+cairo-xlib: dithering
+---------------------
+Dithering: Cairo now does simple dithering when rendering to legacy X
+servers. This is most visible with 8-bit visuals. Thanks Behdad!
+
+cairo-xlib: Avoid rendering glyphs out of surface bounds
+--------------------------------------------------------
+This seemingly harmless optimization exposed a bug in OpenOffice.org 3
+versions where OO.o was passing bogus surface extents to cairo,
+resulting in no text rendered in OO.o. Please contact your
+distribution's OO.o maintainers if you see this bug and point them to
+the following URL:
+
+ https://bugs.freedesktop.org/show_bug.cgi?id=16209
+
+cairo-xlib: Improved performance with X server without Render
+-------------------------------------------------------------
+Cairo now performs better on remote X servers that lack the Render
+extension by being smarter about using X core protocol facilities
+instead of falling back to doing all rendering on the client side.
+
+cairo-ft: respecting FC_FT_FACE
+-------------------------------
+Previously it was impossible to instruct cairo to do emboldening on a
+font face object created from an FT_Face. Cairo now respects and uses
+the FC_FT_FACE fontconfig pattern element, so emboldening can be
+achieved by using cairo_ft_font_face_create_for_pattern() and a
+carefully crafted pattern using FC_FT_FACE and FC_EMBOLDEN. Thanks
+Behdad!
+
+cairo-directfb: backend improvements
+------------------------------------
+The directfb backend, though still unsupported, has seen a good deal
+of improvements. Thanks Vlad!
+
+Bug fixing and optimizations
+----------------------------
+xlib: Faster bookkeeping (Karl Tomlinson)
+       https://bugzilla.mozilla.org/show_bug.cgi?id=453199#c5
+
+PS: Fix gradients with non-constant alpha (Chris Wilson)
+
+Fix deadlock in user-font code (Richard Hughes and Behdad Esfahbod)
+       http://bugs.freedesktop.org/show_bug.cgi?id=16819
+
+Countless other bugs have been fixed and optimizations made, many of
+them thanks to Chris Wilson. Thanks Chris and others!
+
+Note also that the code that had been in cairo 1.7.x calling into
+freetype's optional lcd_filter function was removed from cairo before
+the 1.8.0 release. We do expect this code to come back in some form in
+the future.
+
+Snapshot 1.7.6 (2008-09-17 Carl Worth <cworth@cworth.org>)
+==========================================================
+The cairo community is happy to announce the 1.7.6 snapshot of the
+cairo graphics library. This is a "release candidate" for the upcoming
+1.8.0 release, so we will greatly appreciate any reports of problems
+in this release, and no major changes are currently planned before
+1.8.
+
+Notable changes in 1.7.6
+------------------------
+The largest number of changes since 1.7.4 did not change the
+implementation of cairo itself, but instead revamped cairo's build
+system. The primary goal of the revamp is to make the build system
+less fragile, (particularly for non-Linux platforms). For example, now
+people building on win32 will no longer need to maintain a
+platform-specific list of files to be built. Also, the .so file will
+now be installed with a different naming scheme, (for example, 1.7.6
+will install with a .10706 suffix). Much thanks, Behdad!
+
+And, as usual, Chris Wilson has made another large round of robustness
+improvements, (eliminating dead code, fixing propagation of error
+status values, test suite improvements, etc. etc.). Thanks as always,
+Chris!
+
+API changes since 1.7.4
+-----------------------
+There have been a few changes of API that was new during the 1.7
+series:
+
+* Remove cairo_font_options_set_lcd_filter
+   and cairo_font_options_get_lcd_filter
+
+  Motivation: At the Cairo Summit, this API was determined to be too
+       specific to the freetype font backend to be in the general
+       API. A similar API with a cairo_ft prefix might be introduced
+       in the future. Note that cairo will still respect the
+       corresponding fontconfig settings for these options.
+
+* Replace cairo_has_show_glyphs
+     with cairo_surface_has_show_glyphs
+
+  Motivation: This really is a surface-specific interface, and the
+       convenience function on the cairo_t is not obviously
+       necessary. An application can easily call:
+
+       cairo_surface_has_show_glyphs (cairo_get_target (cr));
+
+       as needed.
+
+* Add cairo_text_cluster_flags_t
+   to cairo_show_text_glyphs
+      cairo_scaled_font_text_to_glyphs
+      cairo_user_scaled_font_text_to_glyphs_func_t
+
+  Motivation: This flag, (and specifically the
+       CAIRO_TEXT_CLUSTER_FLAG_BACKWARD value), replaces the
+       cairo_bool_t backward argument in each of the above
+       interfaces. This leads to more readable user code, and also
+       allows future extensibility.
+
+As always, there are no changes to any API from any major cairo
+release, (1.0.x, 1.2.x, 1.4.x, 1.6.x). Cairo maintains the same
+compatibility promise it always has.
+
+Bug fixes since 1.7.4
+---------------------
+xlib: Faster bookkeeping (Karl Tomlinson)
+       https://bugzilla.mozilla.org/show_bug.cgi?id=453199#c5
+
+PS: Fix gradients with non-constant alpha (Chris Wilson)
+
+Fix deadlock in user-font code (Richard Hughes and Behdad Esfahbod)
+       http://bugs.freedesktop.org/show_bug.cgi?id=16819
+
+Several other minor fixes.
+
+Snapshot 1.7.4 (2008-08-11 Behdad Esfahbod <behdad@behdad.org>)
+===============================================================
+The cairo community is embarrassed to announce availability of the 1.7.4
+snapshot of the cairo graphics library.  This is a followup release to the
+1.7.2 snapshot to ship a tarball that can actually be built.  The only
+change since 1.7.4 is including the missing header file
+cairo-user-font-private.h in the distribution.
+
+Snapshot 1.7.2 (2008-08-11 Behdad Esfahbod <behdad@behdad.org>)
+===============================================================
+The cairo community is finally ready to announce availability of the 1.7.2
+snapshot of the cairo graphics library.  This is embarrassingly the first
+snapshot in the 1.7 unstable series of cairo, leading to the eventual release
+of cairo 1.8, currently planned for late September.
+
+This snapshot comes four months after the 1.6.4 release.  We have done a
+really bad job on getting development snapshots out this cycle, but
+hopefully all the API changes for 1.8 are now finished and the remaining
+weeks will be spent on bug-fixing.  There is more than 400 commits worth
+of changes in this snapshot, and those can use some testing.  Read on!
+
+Text, text, and more text!
+--------------------------
+The dominant theme of this release, and 1.8 in general, is improvements
+around cairo text API.  Here is a high-level list of changes with text
+handling:
+
+User fonts
+----------
+This is new API allowing the user of cairo API to provide drawings for glyphs
+in a font.  This is most useful in implementing fonts in non-standard formats,
+like SVG fonts and Flash fonts, but can also be used by games and other
+applications to draw "funky" fonts.  See test/user-font.c and
+test/user-font-proxy.c for usage examples.  This is based on early work by
+Kristian Høgsberg.  Thanks Kristian!
+
+show_text_glyphs
+----------------
+This new API allows the caller of cairo to mark text glyphs with their
+original text.  The PDF backend implements this new API and latest Pango
+master uses it.  The result is (when bugs are fixed) that complex text can be
+copied out of pangocairo's PDF output correctly and reliably.  There are bugs
+to fix though.  A few poppler bugs, and some more in cairo and pango.
+
+To test show_text_glyph, just grab pango master and this cairo snapshot and
+print text in gedit.  Open in acroread or evince, select all, copy, paste
+in gedit and compare.  The Arabic text with diacritic marks is particularly
+showing bad.  Try with pango/pango-view/HELLO.txt if you are brave
+enough.  The Indic text is showing improvements, but is still coming out
+buggy.
+
+LCD subpixel filtering using FreeType
+-------------------------------------
+FreeType 2.3.5 added support for various LCD subpixel filtering, and
+fontconfig 2.6.0 added support for configuring LCD filter on a font by font
+basis.  Cairo now relies on FreeType and fontconfig for subpixel filtering.
+This work is based on David Turner's original patch to cairo, maintained
+and tested by Sylvain Pasche and others.  Thanks all!
+
+Toy font face constructor and getter
+------------------------------------
+Mostly for API completion, but also useful for higher level (like Pango) to
+hook into what the user has set using cairo_select_font_face(), making that
+toy API a bit more useful.
+
+FreeType: respecting FC_FT_FACE
+-------------------------------
+Previously it was impossible to instruct cairo to do emboldening on a font
+face object created from an FT_Face.  Cairo now respects and uses the
+FC_FT_FACE fontconfig pattern element, so emboldening can be achieved by
+using cairo_ft_font_face_create_for_pattern() and a carefully crafted pattern
+using FC_FT_FACE and FC_EMBOLDEN.
+
+
+PS/PDF: More efficient output
+-----------------------------
+Adrian Johnson has been busy fixing all kinds of bugs in PS and PDF
+backends, as well making them generate much more compact output by avoiding
+things like re-emitting the color or linestyle on every drawing operation.
+Thanks Adrian!
+
+
+Xlib: Dithering
+---------------
+Cairo now does simple dithering when rendering to legacy X servers.  This is
+mostly visible with 8-bit visuals.
+
+Xlib: Avoid rendering glyphs out of surface bounds
+--------------------------------------------------
+This seemingly harmless change manifested a bug with OpenOffice.org 3 versions
+where OO.o was passing bogus surface extents to cairo, resulting in no text
+rendered in OO.o.  Please contact your distro's OO.o maintainers if you see
+this bug and point them to the following URL:
+
+  https://bugs.freedesktop.org/show_bug.cgi?id=16209
+
+Xlib: Improved performance with Xrender-less X servers
+------------------------------------------------------
+Cairo now performs better on remote, Xrender-less X servers by being smarter
+about using X core protocol facilities instead of falling back to doing all
+rendering on the client side.
+
+
+Directfb: backend improvements
+------------------------------
+The directfb backend, though still unsupported, has seen a good deal of
+improvements.  Thanks Vlad!
+
+
+Bug fixing and optimizations
+----------------------------
+Countless bugs have been fixed and optimizations made, many of them thanks to
+Chris Wilson.  Thanks Chris!
+
+
+API additions
+-------------
+
+cairo_show_text_glyphs
+
+  This is a new text rendering API.  Being a more advanced version of
+  cairo_show_glyphs(), it is aimed for use by higher-level text toolkits like
+  Pango, and enables better text extraction from output generated by backends
+  like PDF and SVG.  The PDF backend already implements it, and the upcoming
+  Pango release will use it.
+
+  To make that API work, a bunch of other additions were made:
+
+cairo_glyph_allocate
+cairo_glyph_free
+cairo_text_cluster_t
+cairo_text_cluster_allocate
+cairo_text_cluster_free
+cairo_surface_has_show_text_glyphs
+
+
+cairo_user_font_face_create
+
+  This is the "user" font face constructor, accompanied by a variety of method
+  signatures, getters, and setters for a callback-based font backend:
+
+CAIRO_FONT_TYPE_USER
+cairo_user_scaled_font_init_func_t
+cairo_user_scaled_font_render_glyph_func_t
+cairo_user_scaled_font_text_to_glyphs_func_t
+cairo_user_scaled_font_unicode_to_glyph_func_t
+cairo_user_font_face_set_init_func
+cairo_user_font_face_set_render_glyph_func
+cairo_user_font_face_set_text_to_glyphs_func
+cairo_user_font_face_set_unicode_to_glyph_func
+cairo_user_font_face_get_init_func
+cairo_user_font_face_get_render_glyph_func
+cairo_user_font_face_get_text_to_glyphs_func
+cairo_user_font_face_get_unicode_to_glyph_func
+
+
+cairo_scaled_font_text_to_glyphs
+
+  We were previously reluctant to provide this function as text-to-glyphs
+  support in cairo was limited to "toy" font functionality, not really
+  interesting for real-world text processing.  However, with user-fonts
+  landing, this API is needed to expose full access to how user-fonts
+  convert text to glyphs.  This is expected to be used by text toolkits like
+  Pango, as well as "proxy" user-font implementations.
+
+
+cairo_lcd_filter_t
+cairo_font_options_set_lcd_filter
+cairo_font_options_get_lcd_filter
+
+  These add the possibility to choose between various available LCD subpixel
+  filters.  The available filter values are modeled after what FreeType
+  provides.
+
+
+cairo_toy_font_face_create
+cairo_toy_font_face_get_family
+cairo_toy_font_face_get_slant
+cairo_toy_font_face_get_weight
+
+  These provide access to functionality and settings provided by
+  cairo_select_font_face().
+
+
+cairo_scaled_font_get_scale_matrix
+cairo_surface_get_fallback_resolution
+
+  For API completeness.
+
+
+Various new values for cairo_status_t enum
+
+
+Known issues:
+
+- Type3 fonts generated by cairo's PDF backend may show up in poppler/Evince
+  in a different color than expected.  This is fixed in poppler master branch.
+  This mostly affects cairo user fonts.  The test case test/user-font.c
+  demonstrates this.
+
+- User fonts using other fonts in their rendering are currently embedded in
+  PDF as fallback bitmap glyphs.  This will be (hopefully) fixed before 1.8.
+  The test case test/user-font-proxy.c demonstrates this.
+
+
+Release 1.6.4 (2008-04-11 Carl Worth <cworth@cworth.org>)
+=========================================================
+The cairo community is wildly embarrassed to announce the 1.6.4
+release of the cairo graphics library. This release reverts the xlib
+locking change introduced in 1.6.4, (and the application crashes that
+it caused).  The community would be glad to sack its current release
+manager and is accepting applications for someone who could do the job
+with more discipline.
+
+Revert 'add missing locking in cairo-xlib'
+------------------------------------------
+This change was introduced in cairo 1.6.2, but also introduced a bug
+which causes many cairo-xlib applications to crash, (with a
+segmentation fault inside of XSetClipMask). Instead of attempting
+another fix for the broken fix, the change in 1.6.2 has been
+reverted. The original bug which the change was addressing has been
+present since at least cairo 1.4, so it is not expected that leaving
+this bug unfixed will cause any new problems for applications moving
+from cairo 1.4 to cairo 1.6.
+
+At this point, the code of cairo 1.6.4 differs from cairo 1.6.0 only
+in the fix for the PostScript-printer crashes.
+
+Tweak build to avoid linking with g++
+-------------------------------------
+Cairo 1.6.4 avoids a quirk in automake that was causing the cairo
+library to be linked with g++ and linked against libstdc++ even when
+only C source files were compiled for the library.
+
+Release 1.6.2 (2008-04-11 Carl Worth <cworth@cworth.org>)
+=========================================================
+The cairo community is pleased (but somewhat sheepish) to announce the
+1.6.2 release of the cairo graphics library. This is an update to
+yesterday's 1.6.0 release with an important fix to prevent cairo's
+PostScript output from crashing some printers. This release also
+includes a locking fix for cairo's xlib backend to improve thread
+safety. There are no changes beyond these two fixes.
+
+Fix for PostScript printer crash
+--------------------------------
+Adrian Johnson discovered that cairo 1.6.0 was being a bit hard on
+PostScript printers, by changing the font matrix very frequently. This
+causes some PostScript interpreters to allocate new font objects every
+few glyphs, eventually exhausting available resources. The fix
+involves leaving translational components of the font matrix as zero,
+so that the PostScript interpreter sees an identical font matrix
+repeatedly, and can more easily share internal font object resources.
+
+This fix has been tested to resolve the bugs posted here, (for both
+Xerox and Dell printers):
+
+       Printing some PDFs from evince is crashing our Xerox printer
+       http://bugs.freedesktop.org/show_bug.cgi?id=15348
+
+       Cairo-generated postscript blocks Dell 5100cn
+       http://bugs.freedesktop.org/show_bug.cgi?id=15445
+
+Add missing locking in cairo-xlib
+---------------------------------
+Chris Wilson noticed that cairo 1.6.0 was manipulating an internal
+cache of GC object within cairo's Xlib backend without proper
+locking. The missing locking could cause failures for multi-threaded
+applications. He fixed this in 1.6.2 by adding the missing locks.
+
+Release 1.6.0 (2008-04-10 Carl Worth <cworth@cworth.org>)
+=========================================================
+The cairo community is quite pleased to announce the 1.6.0 release of
+the cairo graphics library. This is a major update to cairo, with new
+features and enhanced functionality which maintains compatibility for
+applications written using cairo 1.4, 1.2, or 1.0. We recommend that
+anybody using a previous version of cairo upgrade to cairo 1.6.0.
+
+The most significant new features in this release are dramatically
+improved PDF and PostScript[*] output, support for arbitrary X server
+visuals (including PseudoColor), a new Quartz backend, and and a new
+"win32 printing" backend. See below for more details on these and
+other new features.
+
+New dependency on external pixman library (Thanks, Søren!)
+----------------------------------------------------------
+As of cairo 1.6, cairo now depends on the pixman library, for which
+the latest release can be obtained alongside cairo:
+
+       http://cairographics.org/releases/pixman-0.10.0.tar.gz
+
+This library provides all software rendering for cairo, (the
+implementation of the image backend as well as any image fallbacks
+required for other backends). This is the same code that was
+previously included as part of cairo itself, but is now an external
+library so that it can be shared by both cairo and by the X server,
+(which is where the code originated).
+
+Improved PDF, PostScript, and SVG output (Thanks, Adrian!)
+----------------------------------------------------------
+Users of the cairo-pdf, cairo-ps, and cairo-svg should see a dramatic
+improvement from cairo 1.2/1.4 to 1.6. With this release there are now
+almost no operations that will result in unnecessary rasterization in
+the PDF and PostScript. Rasterized "image fallbacks" are restricted
+only to minimal portions of the document where something is being
+drawn with cairo that is beyond the native capabilities of the
+document, (this is rare for PDF or SVG, but occurs when blending
+translucent objects for PostScript).
+
+This means that the final output will be of higher quality, and will
+also be much smaller, and therefore will print more quickly. The
+machinery for doing analysis and minimal fallbacks also benefits the
+win32-printing surface described below.
+
+In addition to doing less rasterization, the PostScript and PDF output
+also has several other improvements to make the output more efficient
+and more compatible with specifications.
+
+[*] Note: Just before this release, a bug has been reported that the
+PostScript output from cairo can crash some printers, (so far the
+following models have been reported as problematic Xerox Workcentre
+7228 or 7328 and Dell 5100cn). We will implement a workaround as soon
+as we can learn exactly what in cairo's output these printers object
+to, (and we could use help from users that have access to misbehaving
+printers). This bug is being tracked here:
+
+       Printing some PDFs from evince is crashing our Xerox printer
+       http://bugs.freedesktop.org/show_bug.cgi?id=15348
+
+New support for arbitrary X server visuals (Thanks, Keith and Behdad!)
+----------------------------------------------------------------------
+As of cairo 1.6, cairo should now work with an arbitrary TrueColor or
+8-bit PseudoColor X server visual. Previous versions of cairo did not
+support these X servers and refused to draw anything. We're pleased to
+announce that this limitation has been lifted and people stuck with
+ancient display systems need no longer be stuck with ancient software
+just because of cairo.
+
+New, supported Quartz backend for Mac OS X (Thanks, Brian and Vladimir!)
+------------------------------------------------------------------------
+As of cairo 1.6, the cairo-quartz backend is now marked as "supported"
+rather than "experimental" as in previous cairo releases. Its API now
+has guarantees of API stability into future cairo releases, and its
+output quality is comparable to other backends. There have been
+significant improvements to cairo-quartz since 1.4. It now uses many
+fewer image fallbacks, (meaning better performance), and has greatly
+improved text rendering.
+
+New, "win32 printing" backend (Thanks, Adrian and Vladimir!)
+------------------------------------------------------------
+A new win32-printing surface has been added with an interface very
+similar to the original win32 surface, (both accept an HDC
+parameter). But this new surface should only be called with a printing
+DC, and will result in all drawing commands being stored into a
+meta-surface and emitted after each page is complete. This allows
+cairo to analyze the contents, (as it does with PDF, PostScript, and
+SVG backends), and to do minimal image-based fallbacks as
+necessary. The analysis keeps things as efficient as possible, while
+the presence of fallbacks, (when necessary), ensure the consistent,
+high-quality output expected from cairo.
+
+Robustness fixes (Thanks, Chris!)
+---------------------------------
+There has been a tremendous number of improvements to cairo's
+robustness. Areas that have been improved include:
+
+       * Proper reporting of errors
+
+       * Responding correctly to invalid input
+
+       * Avoiding integer overflows
+
+       * Avoiding memory leaks on error-recovery paths
+
+       * Making reference counting thread safe
+
+       * Exhaustive testing of memory allocation points
+
+Other fixes (Thanks, everybody!)
+--------------------------------
+Cairo's internal fixed-point representation has been changed from
+16.16 to 24.8. This has a direct impact on applications as it allows
+much larger objects to be drawn before internal limits in cairo make
+the drawing not work.
+
+The CAIRO_EXTEND_PAD mode is now fully supported by surface
+patterns. This mode allows applications to use cairo_rectangle and
+cairo_fill to draw scaled images with high-quality bilinear filtering
+for the internal of the image, but without any objectionably blurry
+edges, (as would happen with the default EXTEND_NONE and cairo_paint).
+
+Rendering with CAIRO_ANTIALIAS_NONE has been fixed to be more
+predictable, (previously image rendering and geometry rendering would
+be slightly misaligned with respect to each other).
+
+The reference manual at http://cairographics.org/manual now documents
+100% of the functions and types in cairo's public API.
+
+API additions
+-------------
+Several small features have been added to cairo with new API functions:
+
+cairo_format_stride_for_width
+
+    Must be called to compute a properly aligned stride value before
+    calling cairo_image_surface_create_for_data.
+
+cairo_has_current_point
+
+    Allows querying if there is a current point defined for the
+    current path.
+
+cairo_path_extents
+
+    Allows querying for path extents, (independent of any fill or
+    stroke parameters).
+
+cairo_surface_copy_page
+cairo_surface_show_page
+
+    Allow beginning a new document page without requiring a cairo_t
+    object.
+
+cairo_ps_surface_restrict_to_level
+cairo_ps_get_levels
+cairo_ps_level_to_string
+cairo_ps_surface_set_eps
+
+    Allow controlling the Post PostScript level, (2 or 3), to
+    target, as well as to generate Encapsulated PostScript (EPS).
+
+cairo_quartz_font_face_create_for_cgfont
+
+    Create a quartz-specific cairo_font_face_t from a CGFontRef.
+
+cairo_win32_font_face_create_for_logfontw_hfont
+
+    Create a win32-specific cairo_font_face from a LOGFONTW and an
+    HFONT together.
+
+Thanks, Everyone!
+-----------------
+I've accounted for 32 distinct people with attributed code added to
+cairo between 1.4.14 and 1.6.0, (their names are below). That's an
+impressive number, but there are certainly dozens more that
+contributed with testing, suggestions, clarifying questions, and
+encouragement. I'm grateful for the friendships that have developed as
+we have worked on cairo together. Thanks to everyone for making this
+all so much fun!
+
+Adrian Johnson, Alp Toker, Antoine Azar, Behdad Esfahbod,
+Benjamin Otte, Bernardo Innocenti, Bertram Felgenhauer,
+Boying Lu, Brian Ewins, Carl Worth, Chris Heath, Chris Wilson,
+Claudio Ciccani, Emmanuel Pacaud, Jeff Muizelaar, Jeremy Huddleston,
+Jim Meyering, Jinghua Luo, Jody Goldberg, Jonathan Gramain,
+Keith Packard, Ken Herron, Kouhei Sutou, Kristian Høgsberg,
+Larry Ewing, Martin Ejdestig, Nis Martensen, Peter Weilbacher,
+Richard Hult, Shailendra Jain, Søren Sandmann Pedersen,
+Vladimir Vukicevic
+
+Snapshot 1.5.20 (2008-04-04 Carl Worth <cworth@cworth.org>)
+===========================================================
+This is the tenth snapshot in cairo's unstable 1.5 series. It comes
+just two days (and only one working day) after the 1.5.18
+snapshot. The quick snapshot is due to two embarrassing bugs (both
+affecting cairo-xlib) that had been introduced in the 1.5.18
+snapshot. The fixes for these are described below along with a few
+other fixes, (which hopefully aren't introducing new bugs this time).
+
+cairo-xlib
+----------
+Revert fix from 1.5.18 to allow pattern expansion based on the filter
+mode. This fix seemed so boring, (the use case it addresses is almost
+never used in practice), that it didn't even get mentioned in the
+1.5.18 release notes. However, the "fix" happened to break rendering
+that is always used resulting in corrupt image rendering in mozilla,
+evolution, and probably everything else that uses cairo.
+
+Fix to avoid BadMatch errors in cairo_surface_create_similar. These
+were introduced, (inadvertently, of course), as part of the fix in
+1.5.18 for creating similar surfaces without the Render
+extension. Again, thanks to mozilla, (and Vladimir Vukicevic in
+particular), for noticing our mistake.
+
+general
+-------
+Correctly handle an in-error surface in
+cairo_surface_write_to_png. Previously this function would cause an
+assertion failure if you gave it a finished surface. Now it cleanly
+returns a CAIRO_STATUS_SURFACE_FINISHED result instead.
+
+Avoid potentially infinite wandering through memory inside
+_cairo_hull_prev_valid. Thanks to Jonathan Watt for noticing this
+problem:
+
+       https://bugzilla.mozilla.org/show_bug.cgi?id=306649#c21
+
+cairo-pdf
+---------
+Fix generation of "soft" masks made by drawing to a similar surface
+and then calling cairo_mask_surface() with it.
+
+cairo-svg
+---------
+Fix for code that uses cairo_mask() on an intermediate surface which
+is later passed to cairo_mask_surface().
+
+Snapshot 1.5.18 (2008-04-05 Carl Worth <cworth@cworth.org>)
+===========================================================
+This is the ninth snapshot in cairo's unstable 1.5 series. It comes
+just 4 days after the 1.5.16 snapshot. We had hoped to not need
+another snapshot before the final 1.6.0 release, but several critical
+bugs were found and fixed in the last few days, so we thought it
+important to let people test the fixes with this snapshot. See below
+for details.
+
+documentation
+-------------
+The README now lists necessary dependencies.
+
+Various graphics state defaults are now documented, (source pattern is
+opaque black, line width is 2.0, line join is miter, line cap is butt,
+miter limit is 10.0, etc.).
+
+general
+-------
+Several cleanups have been made along many error-path returns,
+(carefully propagating up the original error status values, cleaning
+up memory leaks during error recovery, etc.). This is yet another in
+Chris "ickle" Wilson's long series of error-handling cleanups during
+the 1.5 series.
+
+Avoid undesired clipping when drawing scaled surface patterns with
+bilinear filtering.
+
+cairo-pdf
+---------
+Fix emission of 1-bit alpha masks in PDF output.
+
+Fix a bug that would cause glyphs to be misplaced along the Y axis:
+
+    http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=%23474136
+
+    Originally, an issue about a crash, but later leading to the
+    misplaced glyphs issue being discovered.
+
+cairo-ps
+--------
+Fix misplaced glyphs in cairo's PostScript output.
+
+    This issue occurs when consecutive glyphs are placed far
+    apart. This case is exercised by the new ft-show-glyphs-table test
+    case, which was originally inspired by the Debian bug #23474136
+    mentioned above.
+
+Fix more misplaced glyphs in cairo's PostScript output:
+
+    The issue here showed up under very particular circumstance, (when
+    converting a PDF file with a CFF font with CID Identity-H encoding
+    and using glyph 0, (defined by the CFF specification as .notdef)
+    as a space instead). More concretely, this problem appeared when
+    converting the UbuntuDesktop.pdf file mentioned in this bug
+    report:
+
+       https://bugs.freedesktop.org/show_bug.cgi?id=15348#c3
+
+    As usual with arcane font-encoding-specific bugs like this, many
+    thanks to Adrian Johnson for his magical ability to dive into
+    specifications and emerge almost instantaneously with fixes. And
+    thanks to Sebastien Bacher for bringing the bug to our attention.
+
+cairo-xlib
+----------
+Fix serious failure on X servers without the Render extension.
+
+    Since the 1.5.14 snapshot (with support for PseudoColor visuals),
+    any application attempting to create a "similar" xlib surface would
+    fail on an X server without the Render extension. Thanks to
+    Frederic Crozat for pointing out that cairo's test suite was
+    entirely failing when run against Xvfb.
+
+Avoid crashing cairo-xlib applications for too-large glyphs
+
+    Naively sending glyphs of any size to the X server will eventually
+    violate the X limit on maximum request sizes. We now properly
+    detect when a glyph would be too large and use existing fallbacks
+    to render the glyph rather than trying to send it to the X server.
+
+Enable the buggy_repeat workaround for Xorg servers < 1.4
+
+    We have determined that Xorg 1.3.0 (as packaged in Fedora 8 at
+    least) has a bug that can result in an X server crash when cairo
+    uses certain X Render repeat operations, (as exercised by cairo's
+    extend-reflect test). We avoid this crash by using fallbacks
+    whenever a repeating surface is needed for any Xorg server with a
+    version less than 1.4. This is slower, but should prevent the
+    crash.
+
+    (Meanwhile, there appears to be a separate bug where some X
+    servers or specific X-server drivers will use random pixmap data
+    when asked to draw a repeating surface. The buggy_repeat
+    workaround would also avoid those problems, but we have not yet
+    characterized whether the new "version < 1.4" is a good
+    characterization of those problems or not.)
+
+cairo-quartz-font
+-----------------
+Implement cairo_font_extents for this backend.
+
+The cairo-quartz-font implementation added in the 1.5.14 snapshot was
+entirely missing support for the cairo_font_extents function. Thanks to
+Richard Hult for pointing out this obvious shortcoming, (and obvious
+lack of coverage in our test suite):
+
+       CGFont backend returns 0 font extents
+       https://bugs.freedesktop.org/show_bug.cgi?id=15319
+
+Snapshot 1.5.16 (2008-04-01 Carl Worth <cworth@cworth.org>)
+===========================================================
+This is the eighth snapshot in cairo's unstable 1.5 series. It comes
+less than two weeks after the 1.5.14 snapshot and it really is a
+legitimate snapshot, (in spite of sharing this date with that of many
+bogus announcements). The major change in this snapshot is that the
+cairo-quartz backend is now officially "supported", including new API
+to construct a font face from a CGFontRef . Also several bug fixes
+have been fixed in many backends. See below for details.
+
+general
+-------
+Cairo now depends on pixman 0.10.0 which was recently released. The
+latest pixman release can always be found alongside cairo releases at:
+
+   http://cairographics.org/releases
+
+Increase the precision of color stops for gradients. This fixes a
+regression in gradient rendering that had been present since the
+1.5.12 snapshot.
+
+paginated (all of ps, pdf, svg, and win32-printing)
+---------------------------------------------------
+Fix assertion failure when some drawing elements are outside the page
+boundaries, (this bug was noticed when using Inkscape to print a
+drawing with landscape orientation to a portrait-oriented piece of
+paper).
+
+cairo-ps
+--------
+Fix of bug causing incorrect glyph positioning.
+
+Fix handling of CAIRO_OPERATOR_SOURCE.
+
+cairo-pdf
+---------
+More reduction of unnecessary digits of precision in PDF output.
+
+Fix handling of CAIRO_OPERATOR_SOURCE.
+
+cairo-svg
+---------
+Fix bug in usage of libpng that was preventing cairo_mask from working
+with the svg backend.
+
+Fix transformation of source pattern for cairo_stroke().
+
+cairo-win32-printing
+--------------------
+Fix fallback resolution, (thanks again to inkscape users/developers
+for helping us find this one).
+
+cairo-quartz
+------------
+Mark the cairo-quartz backend as "supported" rather than
+"experimental". This means the following:
+
+    * The backend will now be built by default (if possible).
+
+    * We are committing that the backend-specific API (as published in
+      cairo-quartz.h) are stable and will be supported in all future
+      cairo 1.x releases.
+
+    * We are committing that the output quality of this backend
+      compares favorably with other cairo backends, (and that quality
+      is ensured by good results from the cairo test suite).
+
+    * We recommend that distributions build and distribute this
+      backend when possible.
+
+Note that the cairo_quartz_image API (in cairo-quartz-image.h) is
+still experimental, will not build by default, (pass
+--enable-quartz-image to configure to build it), and may see API
+changes before it is marked as "supported" in a future release.
+
+Put the CAIRO_FONT_TYPE_ATSUI name back into
+cairo-deprecated.h. Without this, the cairo 1.5.14 snapshot broke all
+builds for applications using the C++ cairomm bindings (and perhaps
+others) which have the CAIRO_FONT_TYPE_ATSUI name in their header
+files. This breakage happened even for applications not using
+cairo-quartz at all.
+
+    Note: Even though the CAIRO_FONT_TYPE_ATSUI name is provided to
+    avoid this build breakage, we still recommend that bindings and
+    applications move to the new, and more accurate,
+    CAIRO_FONT_TYPE_QUARTZ name.
+
+Replace the implementation of cairo-quartz-font to use CFFont instead
+of ATSUI. The CGFont API is a better fit than ATSUI, and this new
+implementation is also more correct than the old one as well.
+
+This also adds the following new API call:
+
+       cairo_public cairo_font_face_t *
+       cairo_quartz_font_face_create_for_cgfont (CGFontRef font);
+
+The previous cairo_quartz_font_face_create_for_atsu_font_id function
+continues to exist and is part of the supported API going
+forward. (However, the old name of that same function, which was
+cairo_atsui_font_face_create_for_atsu_font_id is officially
+deprecated. Any source code using the old name should be updated to
+use the new name.)
+
+Fix transformation of source pattern for cairo_stroke().
+
+cairo-win32
+-----------
+Avoid crash in create_similar is cairo_win32_surface_create fails.
+
+Snapshot 1.5.14 (2008-03-20 Carl Worth <cworth@cworth.org>)
+===========================================================
+This is the seventh snapshot in cairo's unstable 1.5 series. It comes
+3 weeks after the 1.5.12 snapshot. This snapshot includes support for
+arbitrary X server visuals, (including PseudoColor), which was the
+final remaining cairo-specific item on the cairo 1.6 roadmap. It also
+includes a huge number of improvements to the cairo-quartz backend. So
+this is effectively a cairo 1.6 release candidate. We expect very few
+changes from now until 1.6 and only for specific bug fixes.
+
+API Change
+----------
+Rename ATSUI font backend to Quartz font backend. This affects the
+following usage:
+
+       --enable-atsui          -> --enable-quartz-font
+       CAIRO_HAS_ATSUI_FONT    -> CAIRO_HAS_QUARTZ_FONT
+       CAIRO_FONT_TYPE_ATSUI   -> CAIRO_FONT_TYPE_QUARTZ
+
+       cairo_atsui_font_face_create_for_atsu_font_id ->
+       cairo_quartz_font_font_create_for_atsu_font_id
+
+This API change is justified by the cairo-quartz backend still be
+marked as "experimental" rather than "supported", (though this is one
+step toward making the change to "supported" before 1.6). Cairo will
+still provide ABI compatibility with the old symbol name, however.
+
+paginated (all of ps, pdf, svg, and win32-printing)
+---------------------------------------------------
+Optimize by not analyzing an image surface for transparency more than
+once, (previously all images were analyzed twice).
+
+cairo-ps and cairo-pdf
+----------------------
+Avoiding emitting a matrix into the stroke output when unnecessary,
+(making output size more efficient).
+
+Reduce rounding error of path shapes by factoring large scale factors
+out of the path matrix, (ensuring that a fixed-number of printed
+digits for path coordinates contains as much information as possible).
+
+Reduce excess digits for text position coordinates. This makes the
+output file size much smaller without making the result any less
+correct.
+
+cairo-ps
+--------
+Eliminate bug causing extraneous text repetition on Linux PostScript
+output in some cases.
+
+       See: Mozilla Bug 419917 – Printed page contents are reflected
+       inside bordered tables (Linux-only)
+
+       https://bugzilla.mozilla.org/show_bug.cgi?id=419917
+
+Optimize output when EXTEND_PAD is used.
+
+cairo-pdf
+---------
+Fix to not use fill-stroke operator with transparent fill, (else PDF
+output doesn't match the cairo-defined correct result). See:
+
+       https://bugs.launchpad.net/inkscape/+bug/202096
+
+cairo-svg
+---------
+Fix stroke of path with a non-solid-color source pattern:
+
+       http://bugs.freedesktop.org/show_bug.cgi?id=14556
+
+cairo-quartz
+------------
+Fix text rendering with gradient or image source pattern.
+
+Handling antialiasing correctly for cairo_stroke(), cairo_clip(), and
+cairo_show_text()/cairo_show_glyphs().
+
+Correctly handle gradients with non-identity transformations:
+
+       Fixes http://bugs.freedesktop.org/show_bug.cgi?id=14248
+
+Add native implementation of REPEAT and REFLECT extend modes for
+gradients.
+
+Fix implementation for the "unbounded" operators, (CAIRO_OPERATOR_OUT,
+_IN, _DEST_IN, and _DEST_ATOP).
+
+Correctly handle endiannees in multi-architecture compiles on Mac OS
+X.
+
+Avoid behavior which would cause Core Graphics to print warnings to
+the console in some cases.
+
+cairo-win32
+-----------
+Fix handling of miter limit.
+
+cairo-win32-printing
+--------------------
+Fix to not use a 1bpp temporary surface in some cases while printing,
+(so grayscale data is preserved rather than just becoming black and
+white).
+
+cairo-xlib
+----------
+Add support for rendering to arbitrary TrueColor X server
+visuals. This fixes at least the following bugs:
+
+       cairo doesn't support 8-bit truecolor visuals
+       https://bugs.freedesktop.org/show_bug.cgi?id=7735
+
+       cairo doesn't support 655 xlib format
+       https://bugs.freedesktop.org/show_bug.cgi?id=9719
+
+Add support for rendering to 8-bit PseudoColor X server visuals. This
+fixes the following bug:
+
+       Cairo doesn't support 8-bit pseudocolor visuals
+       https://bugs.freedesktop.org/show_bug.cgi?id=4945
+
+Snapshot 1.5.12 (2008-02-28 Carl Worth <cworth@cworth.org>)
+===========================================================
+This is the sixth snapshot in cairo's unstable 1.5 series. It comes 1
+week after the 1.5.10 snapshot. This snapshot includes the
+long-awaited change from 16.16 to 24.8 fixed-point values, (see below
+for why you should care).  It also includes several backend-specific
+bug fixes.
+
+24.8 fixed-point format
+-----------------------
+Cairo has always converted path coordinates to a fixed-point
+representation very early in its processing. Historically, this has
+been a 32-bit representation with 16 bits of integer for the
+device-pixel grid and 16 bits of sub-pixel positioning. The choice of
+16 bits for the integer coordinate space was based on the 16-bit limit
+for X Window drawables.
+
+This 16-bit limit has proven problematic for many applications. It's
+an especially vexing problem when targeting non-X backends that don't
+have any 16-bit restriction. But even when targeting cairo-xlib, it's
+often desirable to draw a large shape, (say a background rectangle),
+that extends beyond the surface bounds and expect it to fill the
+surface completely, (rather than overflowing and triggering random
+behavior).
+
+Meanwhile, nobody has ever really needed 16 bits of sub-pixel
+precision.
+
+With this snapshot, the fixed-point system is still in place and is
+still using a 32-bit representation, (future versions of cairo might
+move entirely to floating-point when targeting PDF output for
+example). But the representation now provides 24 bits of pixel
+addressing and only 8 bits of sub-pixel positioning. This should give
+a much less stifling space to many applications.
+
+However, the underlying pixman library still has 16-bit limitations in
+many places, (it has its roots in the X server as well). Until those
+are also fixed, applications targeting cairo image surfaces, or
+hitting software fallbacks when targeting other surfaces will still
+encounter problems with device-space values needing more than 16
+integer bits.
+
+generic fixes
+-------------
+Add a few tests to the test suite to increase coverage.
+
+Cleanup a few error-handling paths, (propagate error correctly).
+
+cairo-ft
+--------
+Fix handling of font sizes smaller than 1 device pixel.
+
+cairo-pdf
+---------
+Fix to properly save/restore clip when analyzing meta-surface
+patterns, (fixing a couple of test-suite failures).
+
+Implement native support for CAIRO_OPERATOR_SOURCE when the source
+pattern is opaque.
+
+Emit rectangles as PDF rectangles ("re" operator) rather than as
+general paths.
+
+cairo-ps
+--------
+Fix to work properly with the 16.16->24.8 change.
+
+cairo-svg
+---------
+Fix CAIRO_EXTEND_REFLECT by using an image fallback, (there's no
+direct SVG support for reflected patterns).
+
+Fix the use of alpha-only masks, (such as CAIRO_FORMAT_A8).
+
+cairo-quartz
+------------
+Add new API for efficiently using image data as a source:
+
+       cairo_surface_t *
+       cairo_quartz_image_surface_create (cairo_surface_t *image_surface);
+
+       cairo_surface_t *
+       cairo_quartz_image_surface_get_image (cairo_surface_t *surface);
+
+For full documentation, see:
+
+       http://cairographics.org/manual/cairo-Quartz-Surfaces.html#cairo-quartz-image-surface-create
+
+Several fixes for cairo_mask().
+
+cairo-atsui
+-----------
+Change default from from Monaco to Helvetica to be more consistent
+with other font backends.
+
+Snapshot 1.5.10 (2008-02-20 Carl Worth <cworth@cworth.org>)
+===========================================================
+This is the fifth snapshot in cairo's unstable 1.5 series. It comes 3
+weeks after the 1.5.8 snapshot. This snapshot adds one new API
+function, (cairo_has_current_point), and the usual mix of
+improvements, (more efficient PostScript/PDF output, optimized
+stroking), and fixes (more robust error-handling, etc.). See below for
+details.
+
+New API
+-------
+Add a new function to query if there is a current point:
+
+       cairo_bool_t
+       cairo_has_current_point (cairo_t *cr);
+
+There is no current point immediately after cairo_create(), nor after
+cairo_new_path() or cairo_new_sub_path(). There is a current point
+after any of the path-creation functions, (cairo_move_to,
+cairo_line_to, cairo_curve_to, etc.).
+
+With this new function, we also revert the change of the return type
+of cairo_get_current_point from cairo 1.5.8, (it's now a void function
+again).
+
+Optimizations
+-------------
+Optimize stroking code to avoid repeated calculation of redundant
+values, (particularly significant for very large, offscreen paths).
+
+General fixes
+-------------
+Patch a few more potential buffer overruns, (due to integer
+overflow).
+
+Many fixes and improvements to cairo's error-handling, (ensure that
+correct error values are returned, clean up memory leaks on
+error-handling paths, etc.).
+
+Fix a potential infinite loop when stroking a spline with a pen that
+has been transformed to a line segment.
+
+Remove treating NULL as a synonym for a valid cairo_font_options_t*
+with default values, (a change that had been introduced as of cairo
+1.5.8).
+
+Remove the altered handling of tolerance and fallback-resolution that
+had been introduced as of cairo 1.5.4.
+
+cairo-xlib
+----------
+Pass the original Drawable, (as opposed to the root window), to
+XCreatePixmap when creating a similar surface. This gives the X server
+more information so that it can be clever and efficient.
+
+cairo-pdf
+---------
+Fix the rendering of repeating and reflecting patterns.
+
+Ensure miter limit is always >= 1, (smaller limits are not meaningful,
+but they can cause some PDF viewers to fail to display pages).
+
+Generate more efficient output when the same path is used for both
+fill and stroke.
+
+cairo-ps
+--------
+Start sharing much of the cairo-pdf code rather than implementing very
+similar code in cairo-ps.
+
+Implement native support for repeating and reflecting linear
+gradients.
+
+Implement reflected surface patterns.
+
+Ensure miter limit is always >= 1, (smaller limits are not meaningful,
+but they can cause some PostScript viewers to crash).
+
+Generate PostScript that will perform more efficiently and use less
+memory on printers, (use currentfile instead of a giant string array
+for image data, and avoid using PostScript patterns for paint() and
+fill() when possible).
+
+cairo-svg
+---------
+Avoid unnecessary rasterization when copying a "similar" surface to
+another svg surface, (allow the SOURCE operator to be implemented with
+all-vector operations if there are no underlying objects).
+
+cairo-atsui
+-----------
+Eliminate infinite loop when attempting to render an empty string.
+
+Snapshot 1.5.8 (2008-01-30 Carl Worth <cworth@cworth.org>)
+==========================================================
+This is the fourth snapshot in cairo's unstable 1.5 series. It comes 2
+weeks after the 1.5.6 snapshot. It adds a few new API functions. Most
+notably all callers of cairo_image_surface_create_for_data should now
+be calling cairo_format_stride_for_width to compute a legal stride
+value. See below for more details.
+
+New API in cairo 1.5.8
+----------------------
+We've added a new function that should be called to compute a legal
+stride value before allocating data to be used with
+cairo_image_surface_create_for_data:
+
+       int
+       cairo_format_stride_for_width (cairo_format_t   format,
+                                      int              width);
+
+We've also added a new cairo_path_extents function that can be used to
+compute a bounding box for geometry such as a single line segment,
+(contrast with cairo_path_extents and cairo_stroke_extents):
+
+       void
+       cairo_path_extents (cairo_t *cr,
+                           double *x1, double *y1,
+                           double *x2, double *y2);
+
+And finally, we've added a function to allow for querying the
+XRenderPictFormat of a cairo-xlib surface:
+
+       XRenderPictFormat *
+       cairo_xlib_surface_get_xrender_format (cairo_surface_t *surface);
+
+API changes
+-----------
+Fix return types of cairo_surface_show_page and
+cairo_surface_copy_page. This is an API change to functions that are
+new in the 1.5 series, so not an API break compared to any stable
+cairo release, (1.0.x, 1.2.x, 1.4.x).
+
+Change the return type of cairo_get_current_point() from void to
+cairo_status_t. This allows the caller to receive a
+CAIRO_STATUS_NO_CURRENT_POINT value to distinguish the a current point
+at the origin from no current point existing.
+
+Performance improvement
+-----------------------
+Improve performance of clipping by using an optimized code path
+internally, (with the ADD operator instead of IN).
+
+General bug fixes
+-----------------
+Fix various cairo_*_extents functions to initialize the return-value
+variables even in the case of a cairo_t in error.
+
+Treat NULL as a legitimate value for cairo_font_options_t*. [NOTE:
+On discussion afterwards, we decided against this change so it has
+been removed as of cairo 1.5.10.]
+
+Fix rendering with CAIRO_ANTIALIAS_NONE to be more predictable, (that
+is, to avoid seams appearing when geometry and imagery share an
+identical edge). Portions of this fix are in the pixman library and
+will appear in a future release of that library.
+
+Avoid triggering an error for a font size of 0.
+
+Miscellaneous changes
+---------------------
+Require pixman >= 0.9.6.
+
+There has been a tremendous amount improvement to cairo's
+documentation. We're delighted that 100% of the public API has at
+least some documentation in the API reference manual. Many thanks to
+Behdad Esfahbod and Nis Martensen for leading this effort.
+
+cairo-pdf and cairo-ps
+----------------------
+Eliminate failure when a Type 1 font is embedded with an explicit
+glyph 0.
+
+cairo-pdf
+---------
+Implement a more correct and more efficient approach for patterns with
+an extend mode of CAIRO_EXTEND_REFLECT.
+
+cairo-ps
+--------
+Fix image masks to properly pack and pad mask bits.
+
+cairo-quartz
+------------
+Take care to only use DrawTiledImage for integer-aligned images, (and
+use slower paths to get the correct result in other cases).
+
+cairo-win32
+-----------
+Fix for older versions of mingw.
+
+Improve the handling of the clipping with the win32 and win32-printing
+surfaces.
+
+Fix rendering of non black/white text.
+
+Snapshot 1.5.6 (2008-01-15 Carl Worth <cworth@cworth.org>)
+==========================================================
+This is the third snapshot in cairo's unstable 1.5 series. It comes
+about 6 weeks after the 1.5.4 snapshot. The only API addition compared
+to 1.5.4 is very minor, (a new value CAIRO_STATUS_TEMP_FILE_ERROR).
+The remainder of the changes are the usual accumulation of bug fixes
+and improvements. See below for details.
+
+General bug fixes
+-----------------
+Fix handling of fonts that contain a mixture of outline and bitmapped
+glyphs. There was a change in this handling in 1.5.4 that improved
+some cases and also regressed other cases. Now, all cases should be
+handled quite well.
+
+Fix alignment issues that were causing SIGBUS failures on SPARC.
+
+Fix a regression (which first appeared in 1.5.2) where stroking under
+a large scale would sometimes incorrectly replace a miter join with a
+bevel join. (Thanks to Keith Packard.)
+
+Fix reporting of zero-sized extents to be {0,0} rather than
+{INT_MAX,INT_MIN}. This avoids several integer overflow and
+allocations of massive regions in some cases.
+
+Fix failures of gradients with no stops, (quartz, ps, and pdf).
+
+Fix handling of Type 1 fonts on Windows platforms.
+
+Fix handling of Type 1 fonts with no specific family name in the font
+itself, (generate a CairoFont-x-y name).
+
+Handle NULL string values in cairo_show_text, cairo_show_glyphs, and
+friends.
+
+Many robustness improvements along error-handling paths, (thanks as
+always, to Chris "ickle" Wilson).
+
+Various other minor fixes.
+
+Paginated backends (PDF/PostScript/win32-printing)
+--------------------------------------------------
+Avoid unnecessary rasterization when using a paginated surface as a
+source, (such as drawing from one pdf surface to another).
+
+Fix replaying of paginated surface with more than one level of push/pop
+group.
+
+cairo-xlib
+----------
+Fix xlib backend to not consider recent X server release as having a
+buggy repeat implementation in the Render extension.
+
+cairo-pdf
+---------
+Fix PDF output to avoid triggering very slow rendering in PDF viewers,
+(avoid starting and stopping the content stream for each pattern
+emission).
+
+Support CAIRO_OPERATOR_SOURCE in cases where there is nothing below
+the object being drawn.
+
+Fix to avoid seams appearing between multiple fallback regions.
+
+cairo-ps (PostScript)
+---------------------
+Use correct bounding box in Type 3 fonts.
+
+Fix several bugs in cairo's PostScript output. These include making
+the PostScript output more compatible with recent versions of
+ghostscript that are more strict about Type 3 fonts, for
+example.
+
+Fix for win32 to not attempt to create temporary files in the root
+directory, (where the user may not have write permission).
+
+Avoid generating Level 3 PostScript if Level 2 is sufficient. Also,
+add code in output documents to alert the user if Level 3 PostScript
+is handed to a device that cannot handle PostScript beyond Level
+2.
+
+cairo-directfb
+--------------
+Various performance optimizations.
+
+Fixed support for small surfaces (less than 8x8).
+
+Provide support for environment variables CAIRO_DIRECTFB_NO_ACCEL to
+disable acceleration and CAIRO_DIRECTFB_ARGB_FONT to enable ARGB fonts
+instead of A8.
+
+cairo-os2
+---------
+Allow OS/2 APIs instead of C library allocation functions.
+
+Snapshot 1.5.4 (2007-12-05 Carl Worth <cworth@cworth.org>)
+==========================================================
+This is the second snapshot in cairo's unstable 1.5 series. It comes
+just over 1 month after the 1.5.2 snapshot. There are no API changes
+or additions in 1.5.4 compared to 1.5.2, but there are several bug
+fixes, and some optimizations. Most of these apply to particular
+backends. See below for details.
+
+General improvements
+--------------------
+Use less memory for spline approximation calculations.
+
+Change how the tolerance value is interpreted with regard to
+fallback-resolution. [Note: On further discussion, we decided against
+this change for now. It is removed as of cairo 1.5.10.]
+
+Fix precision of floating-point values in vector-output backends to
+avoid rounding errors with very small numbers.
+
+Xlib improvements
+-----------------
+Fix bug in glyph rendering with xlib, (due to everything being clipped
+out). This was a regression in the 1.5.2 snapshot that was visible in
+the GIMP, for example. See:
+
+       cairo 1.5.2 causes font problems in GIMP 2.4 status bar and evolution 2.12.1
+       https://bugs.freedesktop.org/show_bug.cgi?id=13084
+
+PostScript improvements
+-----------------------
+Fix bug leading to invalid PostScript files when rendering
+text, (need "0 0 xyshow" instead of "0 xyshow").
+
+Fix many issues with Type 3 fonts, including making the resulting text
+extractable.
+
+Quartz improvements
+-------------------
+Fix font metrics height value for ATSUI, (helps webkit on GTK+ OS X
+layout nicely).
+
+Fix gradients.
+
+Fix EXTEND_NONE mode for patterns.
+
+Fix cairo_quartz_surface_create to properly clear the new surface
+in cairo_quartz_surface_create.
+
+Fix to correctly handle 0x0 sized surfaces.
+
+Optimize drawing of EXTEND_REPEAT patterns for OS X 10.5.
+
+Snapshot 1.5.2 (2007-10-30 Carl Worth <cworth@cworth.org>)
+==========================================================
+This is the first snapshot in cairo's unstable 1.5 series. It comes 4
+months after the 1.4.10 release. This snapshot includes significant
+improvements to PDF and PostScript output, which is one of the things
+in which we're most interested in getting feedback. There are a couple
+of minor API additions, and several optimizations, (primarily in the
+"print/vector" backends). And there are dozens of bug fixes and
+robustness improvements.
+
+New dependency on external pixman library
+-----------------------------------------
+A significant change in this snapshot compared to all previous cairo
+releases is that cairo now depends on an external "pixman" library for
+its software rendering. Previously this same code was compiled
+internally as part of cairo, but now the code is separate so that both
+cairo and the X server can now share common code, (thanks very much to
+Søren Sandmann for his work on separating pixman and maintaining it).
+
+So users will need to acquire and build pixman before being able to
+build cairo. The current release is 0.9.6 and can be obtained from
+here:
+
+       http://cairographics.org/releases/pixman-0.9.6.tar.gz
+
+ which can be verified with:
+
+       http://cairographics.org/releases/pixman-0.9.6.tar.gz.sha1
+       66f01a682c64403a3d7a855ba5aa609ed93bcb9e  pixman-0.9.6.tar.gz
+
+       http://cairographics.org/releases/pixman-0.9.6.tar.gz.sha1.asc
+       (signed by Carl Worth)
+
+Major PDF/PostScript improvements
+---------------------------------
+Adrian Johnson has done some long-awaited work to make cairo's PDF and
+PostScript output more interesting than ever before. First, many
+operations that previously triggered image fallbacks will now be
+rendered as native vectors. These operations include:
+
+       PDF: cairo_push_group, cairo_surface_create_similar,
+       cairo_mask, A8/A1 surface sources, repeating/reflecting linear
+       gradients.
+
+       PostScript: cairo_push_group, cairo_surface_create_similar,
+       gradients, bilevel alpha masks, (for example, all values either 0 or
+       255 for an A8 mask).
+
+Not only that, but when an image fallback is required, it will now be
+limited to only the necessary region. For example, a tiny translucent
+image overlaying a small portion of text would previously caused an
+entire PostScript page to be rendered as a giant image. Now, the
+majority of that page will be nice text, and there will only be a tiny
+image in the output.
+
+Additionally, the PostScript output now carefully encodes text so that
+if it is subsequently converted to PDF, the text will be
+selectable.
+
+This is very exciting progress, and we're hoping to hear from users
+during the 1.5 series about how things have improved, (for example,
+inkscape users doing cairo-based PDF export: please let us know how
+things look). And feel free to pass your thanks along to Adrian for his excellent work.
+
+NOTE: This much improved PDF output makes more sophisticated use of
+functionality in the PDF specification. This means that cairo's output
+will sometimes expose bugs in some free software PDF viewers, (evince,
+poppler, and xpdf, for example), that are not yet ready for such PDF
+files. We're working with the poppler maintainers to get these bugs
+fixed as quickly as possible. In the meantime, please double-check
+with other PDF viewers if cairo-generated PDF files are not being
+rendered correctly. It may be due to a bug in the viewer rather than
+in the PDF file that cairo has created.
+
+Robustness improvements
+-----------------------
+Chris Wilson has made the largest contribution by far to cairo 1.5.2,
+(in number of commits). His more than 150 commits include a huge
+number of fixes to increase cairo's robustness. These fixes make cairo
+more robust against invalid and degenerate input, (NaN, empty path,
+etc.), against size-0 malloc calls, against memory leaks on
+error-recovery paths, and against other failures during error
+handling. He also implemented atomic operations to cairo, and used
+them to fix cairo's previously non-thread-safe reference counting,
+again improving robustness.
+
+Chris has put a tremendous amount of time and effort into writing
+analysis tools for this work, and in running those tools and fixing
+the problems they report. We're very grateful for this work, and hope
+that all cairo users appreciate the more robust implementation that
+results from it.
+
+This work is largely thankless, so it might make sense to notice
+sometime that cairo has been running quite smoothly for you, and when
+you do, send a quick "thank you" off to Chris Wilson, since it
+is all definitely running smoother thanks to his work.
+
+New API
+-------
+There are no major additions to cairo's core API. The only new,
+generic functions are:
+
+       void
+       cairo_surface_copy_page (cairo_surface_t *surface);
+
+       void
+       cairo_surface_show_page (cairo_surface_t *surface);
+
+which can now be used much more conveniently than the existing
+cairo_copy_page and cairo_show_page functions in some
+situations. These functions act identically, but require only a
+cairo_surface_t* and not a cairo_t*.
+
+All other API additions are specific to particular backends.
+
+New cairo-win32 API (new font face function and "win32 printing" surface)
+-------------------------------------------------------------------------
+There is a new function for creating a win32 font face for both a
+logfontw and an hfont together. This complements the existing
+functions for creating a font face from one or the other:
+
+       cairo_font_face_t *
+       cairo_win32_font_face_create_for_logfontw_hfont (LOGFONTW *logfont,
+                                                        HFONT font);
+
+There is also a new "win32 printing" surface:
+
+       cairo_surface_t *
+       cairo_win32_printing_surface_create (HDC hdc);
+
+This interface looks identical to the original
+cairo_win32_surface_create, (both accept and HDC), but the behavior of
+this new surface is very different. It should only be called with a
+printing DC, and will result in all drawing commands being stored into
+a meta-surface and emitted after each page is complete, with analysis
+to do as minimal image-based fallbacks as necessary. The behavior and
+implementation shares much with the PDF and PostScript backends.
+
+New cairo-ps API (EPS and PostScript level control)
+---------------------------------------------------
+An often requested feature has been the ability to generate
+Encapsulated PostScript (EPS) with cairo. We have that now with the
+following very simple API. Just do cairo_ps_surface_create as usual
+then call this function with a true value:
+
+       void
+       cairo_ps_surface_set_eps (cairo_surface_t       *surface,
+                                 cairo_bool_t           eps);
+
+[NOTE: As always with snapshots, it's possible---though not very
+likely---that the API could still be modified before a final
+release. For example, this is the first public cairo function that
+accepts a Boolean parameter. I'm generally opposed to Boolean
+parameters, but this is probably the one case where I'm willing to
+accept one, (namely a "set" function that accepts a single Boolean).]
+
+Also, it is now possible to control what PostScript level to target,
+(either level 2 or level 3), with the following new API:
+
+       typedef enum _cairo_ps_level {
+           CAIRO_PS_LEVEL_2,
+           CAIRO_PS_LEVEL_3
+       } cairo_ps_level_t;
+
+       void
+       cairo_ps_surface_restrict_to_level (cairo_surface_t    *surface,
+                                           cairo_ps_level_t    level);
+
+       void
+       cairo_ps_get_levels (cairo_ps_level_t const  **levels,
+                            int                      *num_levels);
+
+       const char *
+       cairo_ps_level_to_string (cairo_ps_level_t level);
+
+Improvement for cairo-quartz
+----------------------------
+Brian Ewins had contributed several improvements to cairo-quartz. These
+include an implementation of EXTEND_NONE for linear and radial
+gradients, (so this extend mode will no longer trigger image fallbacks
+for these gradients), as well as native surface-mask clipping, (only
+on OS X 10.4+ where the CGContextClipToMask function is available).
+
+He also fixed a semantic mismatch between cairo and quartz for dashing
+with an odd number of entries in the dash array.
+
+We're grateful for Brian since not many quartz-specific improvements
+to cairo would be happening without him.
+
+Optimizations
+-------------
+Optimize SVG output for when the same path is both filled and stroked,
+and avoid unnecessary identity matrix in SVG output. (Emmanuel Pacaud).
+
+Optimize PS output to take less space (Ken Herron).
+
+Make PS output more compliant with DSC recommendations (avoid initclip
+and copy_page) (Adrian Johnson).
+
+Make PDF output more compact (Adrian Johnson).
+
+Release glyph surfaces after uploading them to the X server, (should
+save some memory for many xlib-using cairo application). (Behdad
+Esfahbod).
+
+Optimize cairo-win32 to use fewer GDI objects (Vladimir Vukicevic).
+
+win32-printing: Avoid falling back to images when alpha == 255
+everywhere. (Adrian Johnson).
+
+win32-printing: Avoid falling back for cairo_push_group and
+cairo_surface_create_similar. (Adrian Johnson)
+
+Bug fixes
+---------
+Avoid potential integer overflows when allocating large buffers
+(Vladimir Vukicevic).
+
+Preparations to allow the 16.16 fixed-point format to change to
+24.8 (Vladimir Vukicevic).
+
+Fix bugs for unsupported X server visuals (rgb565, rgb555, bgr888, and
+abgr8888). (Carl Worth and Vladimir Vukicevic)
+
+Fix bugs in PDF gradients (Adrian Johnson).
+
+Fix cairo-xlib to build without requiring Xrender header
+files (Behdad Esfahbod).
+
+Make cairo more resilient in the case of glyphs not being available in
+the current font. (Behdad Esfahbod)
+
+Prevent crashes when both atsui and ft font backends are compiled in
+(Brian Ewins).
+
+Make font subsetting code more robust against fonts that don't include
+optional tables (Adrian Johnson).
+
+Fix CFF subsetting bug, (which manifested by generating PDF files that
+Apple's Preview viewer could not read) (Adrian Johnson).
+
+Fixed error handling for quartz and ATSUI backends (Brian Ewins).
+
+Avoid rounding problems by pre-transforming to avoid integer-only
+restrictions on transformation in GDI (Adrian Johnson).
+
+Fixed an obscure bug (#7245) computing extents for some stroked
+paths (Carl Worth).
+
+Fix crashes due to extreme transformation of the pen, (seems to show
+up in many .swf files for some reason) (Carl Worth).
+
+Release 1.4.10 (2007-06-27 Carl Worth <cworth@cworth.org>)
+==========================================================
+This is the fifth update in cairo's stable 1.4 series. It comes
+roughly three weeks after the 1.4.8 release. The most significant
+change in this release is a fix to avoid an X error in certain cases,
+(that were causing OpenOffice.org to crash in Fedora). There is also a
+semantic change to include child window contents when using an xlib
+surface as a source, an optimization when drawing many rectangles, and
+several minor fixes.
+
+Eliminate X errors that were killing OO.o (Chris Wilson)
+--------------------------------------------------------
+Cairo is fixed to avoid the X errors propagated when cleaning up
+Render Pictures after the application had already destroyed the
+Drawable they reference. (It would be nice if the X server wouldn't
+complain that some cleanup work is already done, but there you have
+it.) This fixes the bug causing OpenOffice.org to crash as described
+here:
+
+        XError on right click menus in OOo.
+        https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=243811
+
+Use IncludeInferiors when using xlib surface as a source (Ryan Lortie)
+----------------------------------------------------------------------
+When an xlib surface is used as the source of a draw operation the
+contents of child windows are now included in the source data. The
+semantics of drawing to xlib surfaces are unchanged (ie: draws are
+still clipped by child windows overlapping the destination window).
+
+Optimize drawing of many rectangles (Vladimir Vukicevic)
+--------------------------------------------------------
+Avoid O(N*N) loop when filling many axis-aligned rectangles, (either
+many rectangles as separate sub-paths or due to dashing).
+
+Miscellaneous fixes
+-------------------
+Fix cairo-perf on Solaris by linking to librt. (Behdad Esfahbod)
+
+Fix make check for systems that require executable files to have a
+particular extension. (Behdad Esfahbod)
+
+Eliminate some warnings in cairo-quartz. (Brian Ewins)
+
+Fix build-breaking typo for cairo-directfb. (Chris Wilson)
+
+Release 1.4.8 (2007-06-07 Carl Worth <cworth@cworth.org>)
+=========================================================
+This is the fourth update in cairo's stable 1.4 series. It comes just
+over five weeks after the 1.4.6 release. This release includes a
+thread-safe surface-cache for solid patterns which significantly
+improves text rendering with the xlib backend. Also, dozens of error
+paths in cairo have been fixed thanks to extensive fault-injection
+testing by Chris Wilson.
+
+Surface cache for solid patterns
+--------------------------------
+Originally written by Jorn Baayen, the introduction of a small cache
+for surfaces created for solid patterns improves performance
+dramatically. For example, this reduces the volume of X requests
+during text rendering to the same level as Xft.
+
+This cache first made its appearance in a 1.3.x snapshot, but was
+removed before appearing in any previous major release due to
+complications with multi-threaded programs. For example, programs like
+evince that would carefully restrict usage of cairo-xlib to a single
+thread were unpleasantly surprised to find that using cairo-image in a
+separate thread could trigger X requests.
+
+Behdad Esfahbod designed a fix which was implemented by Chris
+Wilson. Now, the necessary X requests are queued up until the next
+time the application directly operates on an xlib surface.
+
+Improved error handling paths
+------------------------------
+Chris Wilson continued the excellent work he started in cairo 1.4.4 to
+make cairo much more robust against out-of-memory and other errors. He
+applied his memory allocation fault injection cairo's main test suite,
+(previously he had applied it to cairo's performance suite).
+
+Chris's testing found dozens of bugs which he fixed. Many of these
+bugs had perhaps never been hit by any users. But at least one was
+hit by the gnome-about program which resulted in dozens of duplicated
+bug reports against that program:
+
+       http://bugzilla.gnome.org/show_bug.cgi?id=431990
+
+We were very pleasantly surprised to see this bug get fixed as a
+side-effect of Chris's work. Well done, Chris!
+
+Other fixes
+-----------
+Cleanup of mutex declarations (Behdad Esfahbod)
+
+Remove unnecessary clip region from SVG output (Emmanuel Pacaud)
+
+Remove Xsun from the buggy_repeat blacklist (Elaine Xiong)
+
+ATSUI: Fix glyph measurement: faster and more correct (Brian Ewins)
+
+Quartz: fixed 'extend' behaviour for patterns, improved pattern performance,
+and a few smaller correctness fixes. (Brian Ewins, Vladimir Vukicevic)
+
+Release 1.4.6 (2007-05-01 Carl Worth <cworth@cworth.org>)
+=========================================================
+This is the third update in cairo's stable 1.4 series. It comes a
+little less than three weeks since the 1.4.4 release. This release
+fixes the broken mutex initialization that made cairo 1.4.4 unusable
+on win32, OS/2, and BeOS systems. This release also adds significant
+improvements to cairo's PDF backend, (native gradients!), and a couple
+of performance optimizations, (one of which is very significant for
+users of the xlib backend). See below for more details.
+
+Repaired mutex initialization
+-----------------------------
+We apologize that cairo 1.4.4 did little more than crash on many
+platforms which are less-frequently used by the most regular cairo
+maintainers, (win32, OS/2, and BeOS). The mutex initialization
+problems that caused those crashes should be fixed now. And to avoid
+similar problems in the future, we've now started posting pre-release
+snapshots to get better testing, (subscribe to cairo@cairographics.org
+if you're interested in getting notified of those and testing them).
+
+PDF Improvements
+----------------
+Thanks to Adrian Johnson, (cairo PDF hacker extraordinaire), we have
+several improvements to cairo's PDF backend to announce:
+
+Native gradients:
+
+  As of cairo 1.4.6, cairo will now generate native PDF gradients in
+  many cases, (previously, the presence of a gradient on any page
+  would force rasterized output for that page). Currently, only
+  gradients with extend types of PAD (the default) or NONE will
+  generate native PDF gradients---others will still trigger
+  rasterization, (but look for support for other extend modes in a
+  future release). Many thanks to Miklós Erdélyi as well, who did the
+  initial work for this support.
+
+Better compatibility with PDF viewers:
+
+  The PDF output from cairo should now be displayed correctly by a
+  wider range of PDF viewers. Adrian tested cairo's PDF output against
+  many PDF viewers, identified a common bug in many of those viewers
+  (ignoring the CTM matrix in some cases), and modified cairo's output
+  to avoid triggering that bugs (pre-transforming coordinates and
+  using an identity matrix).
+
+Better OpenType/CFF subsetting:
+
+  Cairo will now embed CFF and TrueType fonts as CID fonts.
+
+Performance optimizations
+-------------------------
+Faster cairo_paint_with_alpha:
+
+  The cairo_paint_with_alpha call is used to apply a uniform alpha
+  mask to a pattern. For example, it can be used to gradually fade an
+  image out or in. Jeff Muizelaar fixed some missing/broken
+  optimizations within the implementation of this function resulting
+  in cairo_paint_with_alpha being up to 4 times faster when using
+  cairo's image backend.
+
+Optimize rendering of "off-screen" geometry:
+
+  Something that applications often do is to ask cairo to render
+  things that are either partially or wholly outside the current clip
+  region. Since 1.4.0 the image backend has been fixed to not waste
+  too much time in this case. But other backends have still been
+  suffering.
+
+  In particular, the xlib backend has often performed quite badly in
+  this situation. This is due to a bug in the implementation of
+  trapezoid rasterization in many X servers.
+
+  Now, in cairo 1.4.6 there is a higher-level fix for this
+  situation. Cairo now eliminates or clips trapezoids that are wholly
+  or partially outside the clip region before handing the trapezoids
+  to the backend. This means that the X server's performance bug is
+  avoided in almost all cases.
+
+  The net result is that doing an extreme zoom-in of vector-based
+  objects drawn with cairo might have previously brought the X server
+  to its knees as it allocated buffers large enough to fit all of the
+  geometry, (whether visible or not). But now the memory usage should
+  be bounded and performance should be dramatically better.
+
+Miscellaneous
+-------------
+Behdad contributed an impressively long series of changes that
+organizes cairo's internals in several ways that will be very
+beneficial to cairo developers. Thanks, Behdad!
+
+Behdad has also provided a utility for generating malloc statistics,
+(which was used during the great malloc purges of 1.4.2 and
+1.4.4). This utility isn't specific to cairo so may be of benefit to
+others. It is found in cairo/util/malloc-stats.c and here are Behdad's
+notes on using it:
+
+    To build, do:
+
+        make malloc-stats.so
+
+    inside util/, and to use, run:
+
+        LD_PRELOAD=malloc-stats.so some-program
+
+    For binaries managed by libtool, eg, cairo-perf, do:
+
+        ../libtool --mode=execute /bin/true ./cairo-perf
+        LD_PRELOAD="../util/malloc-stats.so" .libs/lt-cairo-perf
+
+Finally, the cairo-perf-diff-files utility was enhanced to allow for
+generating performance reports from several runs of the same backend
+while some system variables were changed. For example, this is now
+being used to allow cairo-perf to measure the performance of various
+different acceleration architectures and configuration options of the
+X.org X server.
+
+Release 1.4.4 (2007-04-13 Carl Worth <cworth@cworth.org>)
+=========================================================
+This is the second update release in cairo's stable 1.4 series. It
+comes just less than a month after 1.4.2. The changes since 1.4.2
+consist primarily of bug fixes, but also include at least one
+optimization. See below for details.
+
+Of all the work that went into the 1.4.4 release
+
+There have been lots of individuals doing lots of great work on cairo,
+but two efforts during the 1.4.4 series deserve particular mention:
+
+Internal cleanup of error handling, (Chris Wilson)
+--------------------------------------------------
+Chris contributed a tremendous series of patches (74 patches!) to
+improve cairo's handling of out-of-memory and other errors. He began
+by adding gcc's warn_unused_attribute to as many functions as
+possible, and then launched into the ambitious efforts of adding
+correct code to quiet the dozens of resulting warnings.
+
+Chris also wrote a custom valgrind skin to systematically inject
+malloc failures into cairo, and did all the work necessary to verify
+that cairo's performance test suite runs to completion without
+crashing.
+
+The end result is a much more robust implementation. Previously, many
+error conditions would have gone unnoticed and would have led to
+assertion failures, segmentation faults, or other harder-to-diagnose
+problems. Now, more than ever, cairo should cleanly let the user know
+of problems through cairo_status and other similar status
+functions. Well done, Chris!
+
+More malloc reduction, (Mathias Hasselmann)
+-------------------------------------------
+After 1.4.0, Behdad launched an effort to chase down excessive calls
+to malloc within the implementation of cairo. He fixed a lot of
+malloc-happy objects for 1.4.2, but one of the worst offenders,
+(pixman regions), was left around. Mathias contributed an excellent
+series of 15 patches to finish off this effort.
+
+The end result is a cairo that calls malloc much less often than it
+did before. Compared to 1.4.2, 55% of the calls to malloc have been
+eliminate, (and 60% have been eliminated compared to 1.4.0). Well
+done, Mathias!
+
+Other improvements since 1.4.2
+------------------------------
+• Centralize mutex declarations (will reduce future build breaks),
+  (Mathias Hasselmann)
+
+• Reduce malloc by caching recently freed pattern objects (Chris
+  Wilson)
+
+• Fix some broken composite operations (David Reveman)
+       https://bugs.freedesktop.org/show_bug.cgi?id=5777
+
+Backend-specific fixes
+----------------------
+PDF:
+ • Use TJ operator for more compact representation of glyphs (Adrian
+   Johnson)
+
+ • Fix glyph positioning bug when glyphs are not horizontal
+       http://lists.freedesktop.org/archives/cairo/2007-April/010337.html
+
+win32:
+ • Fix crash when rendering with bitmap fonts (Carl Worth)
+       https://bugzilla.mozilla.org/show_bug.cgi?id=376498
+
+xlib:
+ • Turn metrics-hinting on by default (Behdad Esfahbod)
+
+ • Fix edge-effect problem with transformed images drawn to xlib
+   (Behdad Esfahbod)
+       https://bugs.freedesktop.org/show_bug.cgi?id=10508
+
+ • Avoid dereferencing a NULL screen. (Chris Wilson)
+       https://bugs.freedesktop.org/show_bug.cgi?id=10517
+
+Quartz/ATSUI:
+ • Fix scaling of glyph surfaces
+   (Brian Ewins)
+       https://bugs.freedesktop.org/show_bug.cgi?id=9568
+
+ • Fix compilation failure when both xlib and quartz enabled
+   (Brian Ewins)
+
+ • Fix rounding bug leading to incorrectly positioned glyphs
+   (Robert O'Callahan)
+       https://bugs.freedesktop.org/show_bug.cgi?id=10531
+
+Release 1.4.2 (2007-03-19 Carl Worth <cworth@cworth.org>)
+=========================================================
+This is the first update release in cairo's stable 1.4 series. It
+comes just less than 2 weeks after 1.4.0. We hadn't anticipated an
+update this early, but we've managed to collect some important fixes
+that we wanted to get out to cairo users as soon as possible, (6 fixes
+for crashes, 1 case where graphical elements would not be drawn at
+all, a handful of backend-specific bugs, and several important build
+fixes).
+
+There's almost nothing but bug fixes in this release, (see below one
+optimization that Behdad did sneak in), so we recommend that everyone
+upgrade to this release when possible.
+
+Thanks to the many people that worked to fix these bugs, and those
+that did the work to report them and to test the fixes, (wherever
+possible both names are credited below).
+
+Critical fixes
+--------------
+• Fix a crash due to a LOCK vs. UNLOCK typo (M. Drochner fixing Carl
+  Worth's embarrassing typo).
+
+  http://bugs.freedesktop.org/show_bug.cgi?id=10235
+
+• Fix potential buffer overflow, which on some systems with a checking
+  variant of snprintf would lead to a crash (Adrian Johnson, Stanislav
+  Brabec, and sangu).
+
+  https://bugs.freedesktop.org/show_bug.cgi?id=10267
+  https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=232576
+
+• Fix a crash in cairo_stroke_extents or cairo_in_stroke when line
+  width is 0.0. (Carl Worth and Sebastien Bacher)
+
+  https://bugs.freedesktop.org/show_bug.cgi?id=10231
+
+• Fix a crash on certain combinations of X server/video drivers (Carl
+  Worth and Tomas Carnecky).
+
+  https://bugs.freedesktop.org/show_bug.cgi?id=10250
+
+• Fix a crash due to mishandling of invalid user input (Carl Worth and
+  Alexander Darovsky).
+
+  https://bugs.freedesktop.org/show_bug.cgi?id=9844
+
+• xlib: Cleanup server-side glyph caches on XCloseDisplay. This
+  eliminated a crash detected by the perf suite, (and that
+  applications could have run into as well). (Chris Wilson)
+
+Other bug fixes
+---------------
+• Fix for some geometry which simply disappeared under some
+  transformations---a stroked line with an extreme skew in X, for
+  example (Carl Worth and Jonathan Watt).
+
+  https://bugzilla.mozilla.org/show_bug.cgi?id=373632
+
+• SVG: Fix radial gradients for CAIRO_EXTEND_REFLECT and when r0 > r1
+  (Emmanuel Pacaud).
+
+• PDF: Set page group color space to DeviceRGB.
+
+  This fixes incorrect (muddy) transparent colors when rendering cairo
+  PDF output in some viewers. (Adrian Johnson, Adam Goode, and
+  MenTaLguY).
+
+  http://lists.freedesktop.org/archives/cairo/2006-November/008551.html
+
+• win32: Return correct metrics when hinting is off, and fix font
+  descent computation (Behdad Esfahbod).
+
+• quartz: Fix glyph interfaces to correctly return user-space rather
+  than device-space coordinates (Brian Ewins).
+
+  https://bugs.freedesktop.org/show_bug.cgi?id=9568
+
+• xcb: Fix parameter-order confusion with xcb_create_pixmap, which now
+  makes all tests that pass with xlib now pass with xcb (Carl Worth,
+  Jamey Sharp).
+
+• Fix some memory leaks in the perf suite (Chris Wilson).
+
+• Fix perf suite to consider changes in pixman/src (Mathias
+  Hasselmann).
+
+Build fixes
+-----------
+• Don't include pre-generated cairo-features.h file. This was causing
+  build failures when building with the directfb backend enabled
+  (Behdad Esfahbod).
+
+  https://bugs.freedesktop.org/show_bug.cgi?id=10189
+
+• Eliminate use of maintainer mode from cairo's automake/configure
+  script. This means that updates to files such as Makefile.am will
+  take effect, (by rerunning automake and friends as necessary) when
+  invoking make rather than being silently ignored.  (Behdad Esfahbod)
+
+• Don't compile cairo-deflate-stream.c, which depends on zlib, unless
+  building the pdf backend which requires it. (Carl Worth, Tor
+  Lillqvist)
+
+  https://bugs.freedesktop.org/show_bug.cgi?id=10202
+
+• Don't make the ps backend link against zlib anymore, since it
+  doesn't require it (Carl Worth).
+
+• Use "find !" rather than "find -not" for better portability (Thomas
+  Klausner).
+
+  https://bugs.freedesktop.org/show_bug.cgi?id=10226
+
+• Don't use unsupported visibility attribute "hidden" on Solaris
+  (Gilles Dauphin, Thomas Klausner).
+
+  https://bugs.freedesktop.org/show_bug.cgi?id=10227
+
+Optimization
+------------
+• It was Behdad that suggested we focus strictly on bug fixes now that
+  we shipped so many performance improvements in 1.4.0, but it was
+  also Behdad that got distracted by the chance to remove a lot of
+  mallocs from cairo. Paths, gstates, trapezoids, splines, polygons,
+  and gradient color stops will now use small, stack-allocated buffers
+  in the most common cases rather than calling malloc as
+  often. (Behdad Esfahbod). And look for more from Mathias Hasselmann
+  soon.
+
+Release 1.4.0 (2007-03-06 Carl Worth <cworth@cworth.org>)
+=========================================================
+The many people[*] who have been working hard on cairo are very
+pleased to announce the long-awaited release of cairo 1.4. This
+release comes 4 months after the last stable update release (1.2.6)
+and 9 months since the initial release of 1.2.0.
+
+The release notes below are intended to capture the highlights of the
+changes that have occurred from the 1.2 series to the new 1.4.0
+release.
+
+Performance improvements
+------------------------
+Within the cairo project, the last 6 months or so has seen an intense
+effort focusing on the performance of cairo itself. That effort has
+paid off considerably, as can be seen in the following highlights of
+some of the performance differences from cairo 1.2.6 to cairo 1.4.0.
+
+(Note: The performance results reported here were measured on an x86
+laptop. Many of the improvements in 1.4---particular those involving
+text rendering---are even more dramatic on embedded platforms without
+hardware floating-point units. Such devices played an important part
+of many of the optimizations that found their way into cairo over the
+last few months.)
+
+• Dramatic improvement when drawing objects that are mostly off-screen
+  with the image backend (with the xlib backend this case is still
+  slow due to an X server bug):
+
+  image-rgba       long-lines-uncropped-100  479.64 ->  4.98: 96.24x speedup
+  ███████████████████████████████████████████████▋
+
+• Dramatic improvement when copying a small fraction of an image
+  surface to an xlib surface:
+
+   xlib-rgba              subimage_copy-512    3.93 ->  0.07: 54.52x speedup
+  ██████████████████████████▊
+
+• Dramatic improvement to tessellation speed for complex objects:
+
+  image-rgb              tessellate-256-100  874.16 -> 34.79: 25.13x speedup
+  ████████████▏
+   xlib-rgba        zrusin_another_fill-415  148.40 -> 13.85: 10.72x speedup
+  ████▉
+   xlib-rgb                  world_map-800  680.20 -> 345.54:  1.97x speedup
+  ▌
+
+• Dramatic improvement to the speed of stroking rectilinear shapes,
+  (such as the outline of a rectangle or "box"):
+
+  image-rgb          box-outline-stroke-100    0.18 ->  0.01: 24.22x speedup
+  ███████████▋
+   xlib-rgb          box-outline-stroke-100    0.46 ->  0.06:  8.05x speedup
+  ███▌
+
+
+• Dramatic improvements to text rendering speeds:
+
+   xlib-rgba       text_image_rgba_over-256   63.12 ->  9.61:  6.57x speedup
+  ██▊
+
+• 3x improvements to floating-point to fixed-point conversion speeds:
+
+  image-rgba      pattern_create_radial-16     9.29 ->  3.44:  2.70x speedup
+  ▉
+
+• 2x improvements to linear gradient computation:
+
+  image-rgb     paint_linear_rgb_source-512   26.22 -> 11.61:  2.26x speedup
+  ▋
+
+• 2x improvement to a case common in PDF rendering:
+
+  image-rgb              unaligned_clip-100    0.10 ->  0.06:  1.81x speedup
+  ▍
+
+• 1.3x improvement to rectangle filling speed (note: this improvement
+  is new since 1.3.16---previously this test case was a 1.3x slowdown
+  compared to 1.2.6):
+
+  image-rgba                 rectangles-512    6.19 ->  4.37:  1.42x speedup
+  ▎
+  xlib-rgba                  rectangles-512    7.48 ->  5.58:  1.34x speedup
+  ▏
+
+NOTE: In spite of our best efforts, there are some measurable
+performance regressions in 1.4 compared to 1.2. It appears that the
+primary problem is the increased overhead of the new tessellator when
+drawing many, very simple shapes. The following test cases capture
+some of that slowdown:
+
+  image-rgba    mosaic_tessellate_lines-800   11.03 -> 14.29:  1.30x slowdown
+  ▏
+  image-rgba           box-outline-fill-100    0.01 ->  0.01:  1.26x slowdown
+  ▏
+  image-rgba        fill_solid_rgb_over-64     0.20 ->  0.22:  1.12x slowdown
+
+  image-rgba       fill_image_rgba_over-64     0.23 ->  0.25:  1.10x slowdown
+
+   xlib-rgb     paint_image_rgba_source-256    3.24 ->  3.47:  1.07x slowdown
+
+We did put some special effort into eliminating this slowdown for the
+very common case of drawing axis-aligned rectangles with an identity
+matrix (see the box-outline-stroke and rectangles speedup numbers
+above). Eliminating the rest of this slowdown will be a worthwhile
+project going forward.
+
+Also note that the "box-outline-fill" case is a slowdown while
+"box-outline-stroke" is a (huge) speedup. These two test cases
+resulted from the fact that some GTK+ theme authors were filling
+between two rectangles to avoid slow performance from the more natural
+means of achieving the same shape by stroking a single rectangle. With
+1.4 that workaround should definitely be eliminated as it will now
+cause things to perform more slowly.
+
+Greatly improved PDF output
+---------------------------
+We are very happy to be able to announce that cairo-generated PDF
+output will now have text that can be selected, cut-and-pasted, and
+searched with most capable PDF viewer applications. This is something
+that was not ever possible with cairo 1.2.
+
+Also, the PDF output now has much more compact encoding of text than
+before. Cairo is now much more careful to not embed multiple copies of
+the same font at different sizes. It also compresses text and font
+streams within the PDF output.
+
+API additions
+-------------
+There are several new functions available in 1.4 that were not
+available in 1.2. Curiously, almost all of the new functions simply
+allow the user to query state that has been set in cairo (many new
+"get" functions) rather than providing any fundamentally new
+operations. The new functionality is:
+
+• Getting information about the current clip region
+
+  cairo_clip_extents
+  cairo_copy_clip_rectangle_list
+  cairo_rectangle_list_destroy
+
+• Getting information about the current dash setting
+
+  cairo_get_dash_count
+  cairo_get_dash
+
+• Getting information from a pattern
+
+  cairo_pattern_get_rgba
+  cairo_pattern_get_surface
+  cairo_pattern_get_color_stop_rgba
+  cairo_pattern_get_color_stop_count
+  cairo_pattern_get_linear_points
+  cairo_pattern_get_radial_circles
+
+• Getting the current scaled font
+
+  cairo_get_scaled_font
+
+• Getting reference counts
+
+  cairo_get_reference_count
+  cairo_surface_get_reference_count
+  cairo_pattern_get_reference_count
+  cairo_font_face_get_reference_count
+  cairo_scaled_font_get_reference_count
+
+• Setting/getting user data on objects
+
+  cairo_set_user_data
+  cairo_get_user_data
+  cairo_pattern_set_user_data
+  cairo_pattern_get_user_data
+  cairo_scaled_font_set_user_data
+  cairo_scaled_font_get_user_data
+
+• New cairo-win32 functions:
+
+  cairo_win32_surface_create_with_ddb
+  cairo_win32_surface_get_image
+  cairo_win32_scaled_font_get_logical_to_device
+  cairo_win32_scaled_font_get_device_to_logical
+
+API deprecation
+---------------
+The CAIRO_FORMAT_RGB16_565 enum value has been deprecated. It never
+worked as a format value for cairo_image_surface_create, and it wasn't
+necessary for supporting 16-bit 565 X server visuals.
+
+A sampling of bug fixes in cairo 1.4
+------------------------------------
+  • Fixed radial gradients
+  • Fixed dashing (degenerate and "leaky" cases)
+  • Fixed transformed images in PDF/PS output (eliminate bogus repeating)
+  • Eliminate errors from CAIRO_EXTEND_REFLECT and CAIRO_EXTEND_PAD
+  • cairo_show_page no longer needed for single-page output
+  • SVG: Fix bug preventing text from appearing in many viewers
+  • cairo-ft: Return correct metrics when hinting is off
+  • Eliminate crash in cairo_create_similar if nil surface is returned
+  • Eliminate crash after INVALID_RESTORE error
+  • Fix many bugs related to multi-threaded use and locking
+  • Fix for glyph spacing 32 times larger than desired (cairo-win32)
+  • Fixed several problems in cairo-atsui (assertion failures)
+  • Fix PDF output to avoid problems when printing from Acrobat Reader
+  • Fix segfault on Mac OS X (measuring a zero-length string)
+  • Fix text extents to not include the size of non-inked characters
+  • Fix for glyph cache race condition in glitz backend (Jinghua Luo)
+  • Fix make check to work on OPD platforms (IA64 or PPC64)
+  • Fix compilation problems of cairo "wideint" code on some platforms
+  • Many, many others...
+
+Experimental backends (quartz, XCB, OS/2, BeOS, directfb)
+---------------------------------------------------------
+None of cairo's experimental backends are graduating to "supported"
+status with 1.4.0, but two of them in particular (quartz and xcb), are
+very close.
+
+The quartz baceknd has been entirely rewritten and is now much more
+efficient. The XCB backend has been updated to track the latest XCB
+API (which recently had a 1.0 release).
+
+We hope to see these backends become supported in a future release,
+(once they are passing all the tests in cairo's test suite).
+
+The experimental OS/2 backend is new in cairo 1.4 compared to cairo
+1.2.
+
+Documentation improvements
+--------------------------
+We have added documentation for several functions and types that
+were previously undocumented, and improved documentation on other
+ones.  As of this release, there remain only two undocumented
+symbols: cairo_filter_t and cairo_operator_t.
+
+[*]Thanks to everyone
+---------------------
+I've accounted for 41 distinct people with attributed code added to
+cairo between 1.2.6 and 1.4.0, (their names are below). That's an
+impressive number, but there are certainly dozens more that
+contributed with testing, suggestions, clarifying questions, and
+encouragement. I'm grateful for the friendships that have developed as
+we have worked on cairo together. Thanks to everyone for making this
+all so much fun!
+
+Adrian Johnson, Alfred Peng, Alp Toker, Behdad Esfahbod,
+Benjamin Otte, Brian Ewins, Carl Worth, Christian Biesinger,
+Christopher (Monty) Montgomery, Daniel Amelang, Dan Williams,
+Dave Yeo, David Turner, Emmanuel Pacaud, Eugeniy Meshcheryakov,
+Frederic Crozat, Hans Breuer, Ian Osgood, Jamey Sharp, Jeff Muizelaar,
+Jeff Smith, Jinghua Luo, Jonathan Watt, Joonas Pihlaja, Jorn Baayen,
+Kalle Vahlman, Kjartan Maraas, Kristian Høgsberg, M Joonas Pihlaja,
+Mathias Hasselmann, Mathieu Lacage, Michael Emmel, Nicholas Miell,
+Pavel Roskin, Peter Weilbacher, Robert O'Callahan,
+Soren Sandmann Pedersen, Stuart Parmenter, T Rowley,
+Vladimir Vukicevic
+
+Snapshot 1.3.16 (2007-03-02 Carl Worth <cworth@cworth.org>)
+===========================================================
+New API functions
+-----------------
+A few new public functions have been added to the cairo API since the
+1.3.14 snapshot. These include a function to query the current scaled
+font:
+
+       cairo_get_scaled_font
+
+New functions to query the reference count of all cairo objects:
+
+       cairo_get_reference_count
+
+       cairo_surface_get_reference_count
+       cairo_pattern_get_reference_count
+
+       cairo_font_face_get_reference_count
+       cairo_scaled_font_get_reference_count
+
+And new functions to allow the use of user_data with any cairo object,
+(previously these were only available on cairo_surface_t and
+cairo_font_face_t objects):
+
+       cairo_set_user_data
+       cairo_get_user_data
+
+       cairo_pattern_set_user_data
+       cairo_pattern_get_user_data
+
+       cairo_scaled_font_set_user_data
+       cairo_scaled_font_get_user_data
+
+Usability improvement for PDF/PS/SVG generation
+-----------------------------------------------
+In previous versions of cairo, generating single-page output with the
+cairo-pdf, cairo-ps, or cairo-svg backends required a final call to
+cairo_show_page. This was often quite confusing as people would port
+functional code from a non-paginated backend and be totally mystified
+as to why the output was blank until they learned to add this call.
+
+Now that call to cairo_show_page is optional, (it will be generated
+implicitly if the user does not call it). So cairo_show_page is only
+needed to explicitly separate multiple pages.
+
+Greatly improved PDF output
+---------------------------
+We are very happy to be able to announce that cairo-generated PDF
+output will now have text that can be selected, cut-and-paste, and
+searched with most capable PDF viewer applications. This is something
+that was not ever possible with cairo 1.2.
+
+Also, the PDF output now has much more compact encoding of text than
+before. Cairo is now much more careful to not embed multiple copies of
+the same font at different sizes. It also compresses text and font
+streams within the PDF output.
+
+Major bug fixes
+---------------
+  • Fixed radial gradients
+
+    The rendering of radial gradients has been greatly improved. In
+    the cairo 1.2 series, there was a serious regression affecting
+    radial gradients---results would be very incorrect unless one of
+    the gradient circles had a radius of 0.0 and a center point within
+    the other circle. These bugs have now been fixed.
+
+  • Fixed dashing
+
+    Several fixes have been made to the implementation of dashed
+    stroking. Previously, some dashed, stroked rectangles would
+    mis-render and fill half of the rectangle with a large triangular
+    shape. This bug has now been fixed.
+
+  • Fixed transformed images in PDF/PS output
+
+    In previous versions of cairo, painting with an image-based source
+    surface pattern to the PDF or PS backends would cause many kinds
+    of incorrect results. One of the most common problems was that an
+    image would be repeated many times even when the user had
+    explicitly requested no repetition with CAIRO_EXTEND_NONE. These
+    bugs have now been fixed.
+
+  • Eliminate errors from CAIRO_EXTEND_REFLECT and CAIRO_EXTEND_PAD
+
+    In the 1.2 version of cairo any use of CAIRO_EXTEND_REFLECT or
+    CAIRO_EXTEND_PAD with a surface-based pattern resulted in an
+    error, (cairo would stop rendering). This bug has now been
+    fixed.
+
+    Now, CAIRO_EXTEND_REFLECT should work properly with surface
+    patterns.
+
+    CAIRO_EXTEND_PAD is still not working correctly, but it will now
+    simply behave as CAIRO_EXTEND_NONE rather than triggering the
+    error.
+
+New rewrite of quartz backend (still experimental)
+--------------------------------------------------
+Cairo's quartz backend has been entirely rewritten and is now much
+more efficient. This backend is still marked as experimental, not
+supported, but it is now much closer to becoming an officially
+supported backend. (For people that used the experimental nquartz
+backend in previous snapshots, that implementation has now been
+renamed from "nquartz" to "quartz" and has replaced the old quartz
+backend.)
+
+Documentation improvements
+--------------------------
+We have added documentation for several functions and types that
+were previously undocumented, and improved documentation on other
+ones.  As of this release, there remain only two undocumented
+symbols: cairo_filter_t and cairo_operator_t.
+
+Other bug fixes
+---------------
+  • cairo-svg: Fix bug that was preventing text from appearing in many
+    viewers
+
+  • cairo-ft: Return correct metrics when hinting is off
+
+  • Cairo 1.3.14 deadlocks in cairo_scaled_font_glyph_extents or
+    _cairo_ft_unscaled_font_lock_face
+
+    https://bugs.freedesktop.org/show_bug.cgi?id=10035
+
+  • cairo crashes in cairo_create_similar if nil surface returned by
+    other->backend->create_similar
+
+    https://bugs.freedesktop.org/show_bug.cgi?id=9844
+
+  • evolution crash in _cairo_gstate_backend_to_user()
+    https://bugs.freedesktop.org/show_bug.cgi?id=9906
+
+  • Fix memory leak in rectilinear stroking code
+
+Things not in this release
+--------------------------
+  • Solid-surface-pattern cache: This patch had been applied during
+    the 1.3.x series, but it was reverted due to some inter-thread
+    problems it caused. The patch is interesting since it made a big
+    benefit for text rendering performance---so we'll work to bring a
+    corrected version of this patch back as soon as possible.
+
+Snapshot 1.3.14 (2006-02-13 Carl Worth <cworth@cworth.org>)
+===========================================================
+This is the seventh development snapshot in the 1.3 series, (and there
+likely won't be many more before the 1.4.0 release). It comes just
+over 3 weeks after the 1.3.12 snapshot.
+
+Since we're so close to the 1.4.0 release, there are not a lot of new
+features nor even a lot of new performance improvements in this
+snapshot. Instead, there are a great number of bug fixes. Some are
+long-standing bugs that we're glad to say goodbye to, and several are
+fixes for regressions that were introduced as part of the optimization
+efforts during the 1.3.x series.
+
+PDF text selection fixed
+------------------------
+The inability to correctly select text in cairo-generated PDF has been
+a defect ever since the initial support for the PDF backend in the
+cairo 1.2.0 release. With the 1.3.14 snapshot, in most situations, and
+with most PDF viewer applications, the PDF generated by cairo will
+allow text to be correctly selected for copy-and-paste, (as well as
+searching).
+
+We're very excited about this new functionality, (and very grateful to
+Adrian Johnson, Behdad Esfahbod, and others that have put a lot of
+work into this lately). Please test this new ability and give feedback
+on the cairo@cairographics.org list.
+
+Many thread-safety issues fixed
+-------------------------------
+We've discovered that no release of cairo has ever provided safe text
+rendering from a multi-threaded application. With the 1.3.14 snapshot
+a huge number of the bugs in this area have been fixed, and multiple
+application dvelopers have now reported success at writing
+multi-threaded applications with cairo.
+
+Other fixes
+-----------
+Fixed a bug that was causing glyph spacing to be 32 times larger than
+desired when using cairo-win32.
+
+Fixed a regression in the rendering of linear gradients that had been
+present since the 1.3.8 snapshot.
+
+Fixed several problems in cairo-atsui that were leading to assertion
+failures when rendering text.
+
+Fix corrupted results when rendering a transformed source image
+surface to an xlib surface. This was a regression that had been
+present since the 1.3.2 snapshot.
+
+Fixed PDF output to prevent problems printing from some versions of
+Acrobat Reader, (a single glyph was being substituted for every
+glyph).
+
+And many other fixes as well, (see the logs for details).
+
+Snapshot 1.3.12 (2007-01-20 Carl Worth <cworth@cworth.org>)
+===========================================================
+The relentless march toward the cairo 1.4 release continues, (even if
+slightly late out of the starting blocks in 2007). This is the sixth
+development snapshot in the 1.3 series. It comes 4 weeks after the
+1.3.10 snapshot.
+
+Performance
+-----------
+As usual, this snapshot has some fun performance improvements to show
+off:
+
+image-rgba long-lines-uncropped-100  470.08 -> 4.95: 94.91x speedup
+███████████████████████████████████████████████
+image-rgb  long-lines-uncropped-100  461.60 -> 4.96: 93.02x speedup
+██████████████████████████████████████████████
+
+This 100x improvement, (and yes, that's 100x, not 100%), in the image
+backend occurs when drawing large shapes where only a fraction of the
+shape actually appears in the final result, (the rest being outside
+the bounds of the destination surface). Many applications should see
+speedups here, and the actual amount of speedup depends on the ratio
+of non-visible to visible portions of geometry.
+
+[Note: There remains a similar performance bug when drawing mostly
+non-visible objects with the xlib backend. This is due to a similar
+bug in the X server itself, but we hope a future cairo snapshot will
+workaround that bug to get a similar speedup with the xlib backend.]
+
+image-rgba       unaligned_clip-100    0.09 -> 0.06:  1.67x speedup
+▍
+image-rgb        unaligned_clip-100    0.09 -> 0.06:  1.66x speedup
+▍
+
+This speedup is due to further MMX optimization by Soeren Sandmann for
+a case commonly hit when rendering PDF files, (and thanks to Jeff
+Muizelaar for writing code to extract the test case for us).
+
+There's another MMX optimization in this snapshot (without a fancy
+speedup chart) by Dan Williams which improves compositing performance
+specifically for the OLPC machine.
+
+Thanks to Adrian Johnson, cairo's PDF output is now much more
+efficient in the way it encodes text output. By reducing redundant
+information and adding compression to text output streams, Adrian
+achieved a ~25x improvement in the efficiency of encoding text in PDF
+files, (was ~45 bytes per glyph and is now ~1.6 bytes per glyph).
+
+Bug fixes
+---------
+In addition to those performance improvements, this snapshot includes
+several bug fixes:
+
+ * A huge number of bug fixes for cairo-atsui text rendering, (for mac
+   OS X). These bugs affect font selection, glyph positioning, glyph
+   rendering, etc. One noteworthy bug fixes is that
+   cairo_select_font_face will no longer arbitrarily select bold nor
+   italic when not requested, (at least not when using a standard CSS2
+   font family name such as "serif", "sans-serif", "monospace", etc.).
+   All these fixes are thanks to Brian Ewins who continues to do a
+   great job as the new cairo-atsui maintainer.
+
+ * Fix PDF output so that images that are scaled down no longer
+   mysteriously repeat (Carl Worth).
+
+ * Fix segfault on Mac OS X dues to attempt to measure extents of a
+   zero-length string (Behdad Esfahbod).
+
+ * Fix text extents to not include the size of initial/trailing
+   non-inked characters (Behdad Esfahbod).
+
+API tweaks
+----------
+Three functions have had API changes to improve consistency. Note that
+the API functions being changed here are all functions that were
+introduced as new functions during these 1.3.x snapshots. As always,
+there will not be any API changes to functions included in a major
+release (1.2.x, 1.4.x, etc.) of cairo.
+
+The changes are as follows:
+
+ * Rename of cairo_copy_clip_rectangles to cairo_copy_clip_rectangle_list.
+
+ * Change cairo_get_dash_count to return an int rather than accepting a
+   pointer to an int for the return value.
+
+ * Change cairo_get_dash to have a void return type rather than
+   returning cairo_status_t.
+
+It's possible there will be one more round of changes to these
+functions, (and perhaps cairo_get_color_stop as well), as we seek to
+establish a unifying convention for returning lists of values.
+
+Snapshot 1.3.10 (2006-12-23 Carl Worth <cworth@cworth.org>)
+===========================================================
+Santa Claus is coming just a little bit early this year, and he's
+bringing a shiny new cairo snapshot for all the good little boys and
+girls to play with.
+
+This is the fifth development snapshot in the 1.3 series. It comes 9
+days after the 1.3.8 snapshot, and still well within our goal of
+having a new snapshot every week, (though don't expect one next
+week---we'll all be too stuffed with sugar plums).
+
+Speaking of sugar plums, there's a sweet treat waiting in this cairo
+snapshot---greatly improved performance for stroking rectilinear
+shapes, like the ever common rectangle:
+
+image-rgb          box-outline-stroke-100 0.18 -> 0.01: 25.58x speedup
+████████████████████████▋
+image-rgba         box-outline-stroke-100 0.18 -> 0.01: 25.57x speedup
+████████████████████████▋
+xlib-rgb          box-outline-stroke-100 0.49 -> 0.06:  8.67x speedup
+███████▋
+xlib-rgba         box-outline-stroke-100 0.22 -> 0.04:  5.39x speedup
+████▍
+
+In past releases of cairo, some people had noticed that using
+cairo_stroke to draw rectilinear shapes could be awfully slow. Many
+people had worked around this by using cairo_fill with a more complex
+path and gotten a 5-15x performance benefit from that.
+
+If you're one of those people, please rip that workaround out, as now
+the more natural use of cairo_stroke should be 1.2-2x faster than the
+unnatural use of cairo_fill.
+
+And if you hadn't ever implemented that workaround, then you just
+might get to see your stroked rectangles now get drawn 5-25x faster.
+
+Beyond that performance fix, there are a handful of bug fixes in this
+snapshot:
+
+ * Fix for glyph cache race condition in glitz backend (Jinghua Luo)
+
+ * Many fixes for ATSUI text rendering (Brian Ewins)
+
+ * Un-break recent optimization-triggered regression in rendering text
+   with a translation in the font matrix (Behdad Esfahbod)
+
+ * Fix make check to work on OPD platforms (IA64 or PPC64)
+   (Frederic Crozat)
+
+ * Fix a couple of character spacing issues on Windows
+    (Jonathan Watt)
+
+Have fun with that, everybody, and we'll be back for more in the new
+year, (with a plan to add the last of our performance improvements in
+this round, fix a few bad, lingering bugs, and then finish off a nice,
+stable 1.4 release before the end of January).
+
+-Carl
+
+Snapshot 1.3.8 (2006-12-14 Carl Worth <cworth@cworth.org>)
+==========================================================
+This is the fourth development snapshot in the 1.3 series. It comes
+just slightly more than one week after the 1.3.6 snapshot.
+
+After the bug fixes in 1.3.6, we're back to our original program of
+weekly snapshots, each one faster than the one from the week
+before. Cairo 1.3.8 brings a 2x improvement in the speed of rendering
+linear gradients (thanks to David Turner), and a significant reduction
+in X traffic when rendering text (thanks to Xan Lopez and Behdad
+Esfahbod), making cairo behave very much like Xft does.
+
+A few other things in the 1.3.8 snapshot worth noting include a more
+forgiving image comparator in the test suite, (using the "perceptual
+diff" metric and GPL implementation by Hector Yee[*]), a bug fix for
+broken linking on x86_64 (thanks to M Joonas Pihlaja) and an even
+better implementation of _cairo_lround, (not faster, but supporting a
+more complete input range), from Daniel Amelang.
+
+[*] http://pdiff.sourceforge.net/
+
+Snapshot 1.3.6 (2006-12-06 Carl Worth <cworth@cworth.org>)
+==========================================================
+This is the third development snapshot in the 1.3 series. It comes two
+weeks after the 1.3.4 snapshot.
+
+We don't have fancy performance charts this week as the primary
+changes in this snapshot are bug fixes. The performance work continues
+and the next snapshot (planned for one week from today) should include
+several improvements. The bug fixes in this snapshot include:
+
+ * Fix undesirable rounding in glyph positioning (Dan Amelang)
+
+   This bug was noticed by several users, most commonly by seeing
+   improper text spacing or scrambled glyphs as drawn by nautilus. For
+   example:
+
+       Update to cairo-1.3.4 worsen font rendering
+       https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=217819
+
+ * Fix reduced range of valid input coordinates to tessellator
+   (M Joonas Pihlaja)
+
+   This bug was causing lots of assertion failures in mozilla as
+   mentioned here:
+
+       CAIRO_BO_GUARD_BITS and coordinate space?
+       http://lists.freedesktop.org/archives/cairo/2006-December/008743.html
+
+ * Fix several regressions in new tessellator (M Joonas Pihlaja)
+
+   Joonas just had a good eye for detail here. I don't think any
+   external cairo users had noticed any of these bugs yet.
+
+ * Fix compilation problems of cairo "wideint" code on some platforms
+   (Mathieu Lacage)
+
+ * Fix failed configure due to broken grep (Dan Amelang)
+
+   This bug was reported here:
+
+       AX_C_FLOAT_WORDS_BIGENDIAN doesn't work because grep doesn't
+       work with binary file
+       https://bugs.freedesktop.org/show_bug.cgi?id=9124
+
+ * Remove the pkg-config minimum version requirement (Behdad Esfahbod)
+
+   Some systems ship with pkg-config 0.15 and there was really no good
+   reason for cairo to insist on having version 0.19 before it would
+   build.
+
+There is also one new (but inert) feature in this snapshot. There's a
+new option that can be passed to cairo's configure script:
+
+       --disable-some-floating-point
+
+       Disable certain code paths that rely heavily on double precision
+       floating-point calculation. This option can improve
+       performance on systems without a double precision floating-point
+       unit, but might degrade performance on those that do.
+
+As of this snapshot, this option does not make any change to cairo,
+but it is possible that future versions of cairo will respect this
+option and change the implementation of various functions as
+appropriate.
+
+Snapshot 1.3.4 (2006-11-22 Carl Worth <cworth@cworth.org>)
+==========================================================
+This is the second development snapshot in the 1.3 series. It comes
+one week after the 1.3.2 snapshot.
+
+This snapshot has a couple of significant performance improvements,
+and also adds new support for producing multi-page SVG output, (when
+targeting SVG 1.2)---thanks to Emmanuel Pacaud. The details of the
+performance improvements are as follows:
+
+1. The long-awaited "new tessellator".
+
+   The credit for this being an improvement goes to Joonas Pihlaja. He
+   took my really slow code and really put it through its paces to get
+   the dramatic performance improvement seen below (up to 38x faster
+   on realistic cases, and more than 10x faster for the zrusin_another
+   test).
+
+   His own writeup of the work he did is quite thorough, but more than
+   can be quoted here. Please see his post for the interesting details:
+
+   http://lists.freedesktop.org/archives/cairo/2006-November/008483.html
+
+   (Though note that this snapshot also includes some additional,
+   significant improvements that were only sketched out in that
+   email---see "Generating fewer trapezoids").
+
+2. More floating-point improvements
+
+   Daniel Amelang continues to work the magic he began in the 1.3.2
+   snapshot. This time he short-circuits floating-point
+   transformations by identity matrices and applies the earlier
+   floating-to-fixed-point technique to the problem of rounding.
+
+   The improvements here will primarily benefit text performance, and
+   will benefit platforms without hardware floating-point more than
+   those that have it, (some text tests show 20% improvement on an x86
+   machine and closer to 80% improvement on arm).
+
+The performance chart comparing 1.3.2 to 1.3.4 really speaks for
+itself, (this is on an x86 laptop). This is quite a lot of progress
+for one week:
+
+ xlib-rgb    stroke_similar_rgba_over-256   74.99 1.45% ->   2.03 68.38%: 36.86x speedup
+███████████████████████████████████▉
+ xlib-rgb  stroke_similar_rgba_source-256   78.23 1.43% ->   3.30 67.05%: 23.71x speedup
+██████████████████████▊
+ xlib-rgba             tessellate-256-100  820.42 0.15% ->  35.06 2.84%: 23.40x speedup
+██████████████████████▍
+image-rgba             tessellate-256-100  819.55 0.32% ->  35.04 3.56%: 23.39x speedup
+██████████████████████▍
+ xlib-rgb      stroke_image_rgba_over-256   78.10 1.43% ->   4.33 65.56%: 18.04x speedup
+█████████████████
+ xlib-rgb    stroke_image_rgba_source-256   80.11 1.63% ->   5.75 63.99%: 13.94x speedup
+█████████████
+ xlib-rgba  zrusin_another_tessellate-415   89.22 0.35% ->   8.38 5.23%: 10.65x speedup
+█████████▋
+image-rgba  zrusin_another_tessellate-415   87.38 0.89% ->   8.37 5.22%: 10.44x speedup
+█████████▍
+image-rgba        zrusin_another_fill-415  117.67 1.34% ->  12.88 2.77%:  9.14x speedup
+████████▏
+ xlib-rgba        zrusin_another_fill-415  140.52 1.57% ->  15.79 2.88%:  8.90x speedup
+███████▉
+image-rgba              tessellate-64-100    9.68 3.42% ->   1.42 0.60%:  6.82x speedup
+█████▉
+ xlib-rgba              tessellate-64-100    9.78 4.35% ->   1.45 0.83%:  6.72x speedup
+█████▊
+ xlib-rgb     stroke_linear_rgba_over-256   46.01 2.44% ->   7.74 54.51%:  5.94x speedup
+█████
+ xlib-rgb   stroke_linear_rgba_source-256   48.09 2.15% ->   9.14 53.00%:  5.26x speedup
+████▎
+ xlib-rgb     stroke_radial_rgba_over-256   50.96 2.34% ->  12.46 47.99%:  4.09x speedup
+███▏
+ xlib-rgb   stroke_radial_rgba_source-256   53.06 1.57% ->  13.96 46.57%:  3.80x speedup
+██▊
+image-rgba  paint_similar_rgba_source-256    0.12 1.57% ->   0.08 9.92%:  1.42x speedup
+▍
+image-rgba    paint_image_rgba_source-256    0.12 2.49% ->   0.08 10.70%:  1.41x speedup
+▍
+image-rgba                  world_map-800  356.28 0.46% -> 275.72 1.15%:  1.29x speedup
+▎
+ xlib-rgba                  world_map-800  456.81 0.39% -> 357.95 1.39%:  1.28x speedup
+▎
+image-rgb               tessellate-16-100    0.09 0.57% ->   0.07 3.43%:  1.23x speedup
+▎
+image-rgba              tessellate-16-100    0.09 0.06% ->   0.07 2.46%:  1.23x speedup
+▎
+image-rgba        text_solid_rgb_over-256    5.39 4.01% ->   4.47 0.70%:  1.21x speedup
+▎
+image-rgba       text_solid_rgba_over-256    5.37 0.82% ->   4.45 0.75%:  1.21x speedup
+▎
+image-rgba        text_image_rgb_over-64     0.78 0.10% ->   0.65 0.74%:  1.20x speedup
+▎
+image-rgba       text_image_rgba_over-64     0.78 0.29% ->   0.65 0.68%:  1.19x speedup
+▎
+image-rgb         text_solid_rgb_over-64     0.76 2.45% ->   0.63 0.81%:  1.19x speedup
+▎
+image-rgba       text_solid_rgba_over-64     0.76 0.33% ->   0.64 0.66%:  1.19x speedup
+▎
+image-rgba     text_similar_rgba_over-256    5.99 4.72% ->   5.04 1.09%:  1.19x speedup
+▎
+
+We should point out that there is some potential for slowdown in this
+snapshot. The following are the worst slowdowns reported by the cairo
+performance suite when comparing 1.3.2 to 1.3.4:
+
+image-rgba              subimage_copy-256    0.01 0.87% ->   0.01 3.61%:  1.45x slowdown
+▌
+ xlib-rgb        paint_solid_rgb_over-256    0.31 10.23% ->   0.38 0.33%:  1.26x slowdown
+▎
+image-rgba           box-outline-fill-100    0.01 0.30% ->   0.01 2.52%:  1.21x slowdown
+▎
+image-rgba        fill_solid_rgb_over-64     0.20 1.22% ->   0.22 1.59%:  1.12x slowdown
+▏
+image-rgb       fill_similar_rgb_over-64     0.21 1.04% ->   0.24 1.06%:  1.11x slowdown
+▏
+image-rgba        fill_image_rgb_over-64     0.21 1.19% ->   0.24 0.72%:  1.11x slowdown
+▏
+image-rgba      fill_similar_rgb_over-64     0.21 0.18% ->   0.24 0.30%:  1.11x slowdown
+▏
+image-rgb        fill_solid_rgba_over-64     0.22 1.66% ->   0.24 1.15%:  1.11x slowdown
+▏
+image-rgb         fill_image_rgb_over-64     0.21 0.14% ->   0.24 0.80%:  1.11x slowdown
+▏
+image-rgba       fill_image_rgba_over-64     0.22 1.34% ->   0.25 0.20%:  1.11x slowdown
+▏
+image-rgba       fill_solid_rgba_over-64     0.22 1.48% ->   0.24 0.95%:  1.11x slowdown
+▏
+image-rgb      fill_similar_rgba_over-64     0.22 1.13% ->   0.25 1.25%:  1.10x slowdown
+▏
+
+The 45% slowdown for subimage_copy is an extreme case. It's unlikely
+to hit many applications unless they often use cairo_rectangle;
+cairo_fill to copy a single pixel at a time. In any case, it shows a
+worst-case impact of the overhead of the new tessellator. The other
+slowdowns (~ 10%) are probably more realistic, and still very
+concerning.
+
+We will work to ensure that performance regressions like these are not
+present from one major release of cairo to the next, (for example,
+from 1.2 to 1.4).
+
+But we're putting this 1.3.4 snapshot out there now, even with this
+potential slowdown so that people can experiment with it. If you've
+got complex geometry, we hope you will see some benefit from the new
+tessellator. If you've got primarily simple geometry, we hope things
+won't slowdown too much, but please let us know what slowdown you see,
+if any, so we can calibrate our performance suite against real-world
+impacts.
+
+Thanks, and have fun with cairo!
+
+Snapshot 1.3.2 (2006-11-14 Carl Worth <cworth@cworth.org>)
+==========================================================
+This is the first development snapshot since the 1.2 stable series
+branched off shortly after the 1.2.4 release in August 2006.
+
+This snapshot includes all the bug fixes from the 1.2.6 release,
+(since they originated here on the 1.3 branch first and were
+cherry-picked over to 1.2). But more importantly, it contains some new
+API in preparation for a future 1.4 release, and most importantly, it
+contains several performance improvements.
+
+The bug fixes will not be reviewed here, as most of them are already
+described in the 1.2.6 release notes. But details for the new API and
+some performance improvements are included here.
+
+As with all snapshots, this is experimental code, and the new API
+added here is still experimental and is not guaranteed to appear
+unchanged in any future release of cairo.
+
+API additions
+-------------
+Several new API additions are available in this release. There is a
+common theme among all the additions in that they allow cairo to
+advertise information about its state that it was refusing to
+volunteer earlier. So this isn't groundbreaking new functionality, but
+it is essential for easily achieving several tasks.
+
+The new functions can be divided into three categories:
+
+       Getting information about the current clip region
+       -------------------------------------------------
+       cairo_clip_extents
+       cairo_copy_clip_rectangles
+       cairo_rectangle_list_destroy
+
+       Getting information about the current dash setting
+       --------------------------------------------------
+       cairo_get_dash_count
+       cairo_get_dash
+
+       Getting information from a pattern
+       ----------------------------------
+       cairo_pattern_get_rgba
+       cairo_pattern_get_surface
+       cairo_pattern_get_color_stop_rgba
+       cairo_pattern_get_color_stop_count
+       cairo_pattern_get_linear_points
+       cairo_pattern_get_radial_circles
+
+In each of these areas, we have new API for providing a list of
+uniform values from cairo. The closest thing we had to this before was
+cairo_copy_path, (which is rather unique in providing a list of
+non-uniform data).
+
+The copy_clip_rectangles/rectangle_list_destroy functions follow a
+style similar to that of cairo_copy_path. Meanwhile, the dash and
+pattern color stop functions introduce a new style in which there is a
+single call to return the number of elements available (get_dash_count
+and get_color_stop_count) and then a function to be called once to get
+each element (get_dash and get_color_stop_rgba).
+
+I'm interested in hearing feedback from users of these new API
+functions, particularly from people writing language bindings. One
+open question is whether the clip "getter" functionality should adopt
+a style similar to that of the new dash and color_stop interfaces.
+
+API deprecation
+---------------
+The CAIRO_FORMAT_RGB16_565 enum value has been deprecated. It never
+worked as a format value for cairo_image_surface_create, and it wasn't
+necessary for supporting 16-bit 565 X server visuals.
+
+XCB backend changes
+-------------------
+The XCB backend has been updated to track the latest XCB API (which
+recently had a 1.0 release).
+
+New quartz backend
+------------------
+Vladimir Vukicevic has written a new "native quartz" backend which
+will eventually replace the current "image-surface wrapping" quartz
+backend. For now, both backends are available, (the old one is
+"quartz" and the new one is "nquartz"). But it is anticipated that the
+new backend will replace the old one and take on the "quartz" name
+before this backend is marked as supported in a release of cairo.
+
+New OS/2 backend
+----------------
+Doodle and Peter Weilbacher have contributed a new, experimental
+backend for using cairo on OS/2 systems.
+
+Performance improvements
+------------------------
+Here are some highlights from cairo's performance suite showing
+improvements from cairo 1.2.6 to cairo 1.3.2. The command used to
+generate this data is:
+
+       ./cairo-perf-diff 1.2.6 HEAD
+
+available in the perf/ directory of a recent checkout of cairo's
+source, (the cairo-perf-diff script does require a git checkout and
+will not work from a tar file---though ./cairo-perf can still be used
+to generate a single report there and ./cairo-perf-diff-files can be
+used to compare two reports).
+
+Results are described below both for an x86 laptop (with an old Radeon
+video card, recent X.org build, XAA, free software drivers), as well
+as for a Nokia 770. First the x86 results with comments on each, (all
+times are reported in milliseconds).
+
+Copying subsets of an image surface to an xlib surface (much faster)
+--------------------------------------------------------------------
+ xlib-rgba              subimage_copy-512   10.50 ->   : 53.97x speedup
+█████████████████████████████████████████████████████
+
+Thanks to Christopher (Monty) Montgomery for this big performance
+improvement. Any application which has a large image surface and is
+copying small pieces of it at a time to an xlib surface, (imagine an
+application that loads a single image containing all the "sprites" for
+that application), will benefit from this fix. The larger the ratio of
+the image surface to the portion being copied, the larger the benefit.
+
+Floating-point conversion (3x faster)
+-------------------------------------
+ xlib-rgba  pattern_create_radial-16    27.75 ->   3.93 :  2.94x speedup
+██
+image-rgb   pattern_create_radial-16    26.06 ->   3.74 :  2.90x speedup
+█▉
+
+Thanks to Daniel Amelang, (and others who had contributed the idea
+earlier), for this nice improvement in the speed of converting
+floating-point values to fixed-point.
+
+Text rendering (1.3 - 2x faster)
+------------------------------
+ xlib-rgba text_image_rgba_source-256  319.73 ->  62.40 :  2.13x speedup
+█▏
+image-rgb    text_solid_rgba_over-64     2.85 ->   0.88 :  1.35x speedup
+▍
+
+I don't think we've ever set out to improve text performance
+specifically, but we did it a bit anyway. I believe the extra
+improvement in the xlib backend is due to Monty's image copying fix
+above, and the rest is due to the floating-point conversion speedup.
+
+Thin stroke improvements (1.5x faster)
+---------------------------------------------
+image-rgb               world_map-800  1641.09 -> 414.77 :  1.65x speedup
+▋
+ xlib-rgba              world_map-800  1939.66 -> 529.94 :  1.52x speedup
+▌
+
+The most modest stuff to announce in this release is the 50%
+improvement I made in the world_map case. This is in improvement that
+should help basically anything that is doing strokes with many
+straight line segments, (and the thinner the better, since that makes
+tessellation dominate rasterization). The fixes here are to use a
+custom quadrilateral tessellator rather than the generic tessellator
+for straight line segments and the miter joins.
+
+Performance results from the Nokia 770
+--------------------------------------
+ xlib-rgba          subimage_copy-512     55.88 ->     2.04 : 27.34x speedup
+██████████████████████████▍
+ xlib-rgb     text_image_rgb_over-256   1487.58 ->   294.43 :  5.05x speedup
+████
+image-rgb   pattern_create_radial-16     187.13 ->    91.86 :  2.04x speedup
+█
+ xlib-rgba              world_map-800  21261.41 -> 15628.02 :  1.36x speedup
+▍
+
+Here we see that the subimage_copy improvement was only about half as
+large as the corresponding improvement on my laptop, (27x faster
+compared to 54x) and the floating-point conversion fix also was quite
+as significant, (2x compared to 3x). Oddly the improvement to text
+rendering performance was more than twice as good (5x compared to
+2x). I don't know what the reason for that is, but I don't think it's
+anything anybody should complain about.
+
+Release 1.2.6 (2006-11-02 Behdad Esfahbod <behdad@behdad.org>)
+==============================================================
+This is the third bug fix release in the 1.2 series, coming less than
+two months after the 1.2.4 release made on August 18.
+
+The 1.2.4 release turned out to be a pretty solid one, except for a crasher
+bug when forwarding an X connection where the client and the server have
+varying byte orders, eg. from a PPC to an i686.  Other than that, various
+other small bugs have been fixed.
+
+Various improvements have been made in the testing infrastructure to prevent
+false positives, and to make sure the generated cairo shared object behaves as
+expected in terms of exported symbols and relocations.
+
+There were a total of 89 changes since 1.2.4.  The following list the most
+important ones:
+
+Common fixes
+------------
+- Avoid unsigned loop control variable to eliminate infinite,
+  memory-scribbling loop. (#7593)
+- Fix cairo_image_surface_create to report INVALID_FORMAT errors.
+  Previously the detected error was being lost and a nil surface was
+  returned that erroneously reported CAIRO_STATUS_NO_MEMORY.
+- Change _cairo_color_compute_shorts to not rely on any particular
+  floating-point epsilon value. (#7497)
+- Fix infinite-join test case (bug #8379)
+- Pass correct surface to create_similar in _cairo_clip_init_deep_copy().
+
+PS/PDF fixes
+------------
+- Fix Type 1 embedding in PDF.
+- Correct the value of /LastChar in the PDF Type 1 font dictionary.
+- Improve error checking in TrueType subsetting.
+- Compute right index when looking up left side bearing. (bug #8180)
+- Correct an unsigned to signed conversion problem in truetype subsetting
+  bbox.
+- Type1 subsetting: Don't put .notdef in Encoding when there are 256 glyphs.
+- Add cairo version to PS header / PDF document info dictionary.
+- Set CTM before path construction.
+
+Win32 fixes
+-----------
+- Get correct unhinted outlines on win32. (bug 7603)
+- Make cairo as a win32 static library possible.
+- Use CAIRO_FORMAT_RGB24 for BITSPIXEL==32 surfaces too.
+
+Build system fixes
+------------------
+- Define WINVER if it's not defined. (bug 6456)
+- Fix the AMD64 final link by removing SLIM from pixman.
+- Misc win32 compilation fixes.
+- Add Sun Pro C definition of pixman_private.
+- Use pixman_private consistently as prefix not suffix.
+- Added three tests check-plt.sh, check-def.sh, and check-header.sh that check
+  that the shared object, the .def file, and the public headers agree about
+  the exported symbols.
+- Require pkg-config 0.19. (#8686)
+
+
+Release 1.2.4 (2006-08-18 Carl Worth <cworth@cworth.org>)
+=========================================================
+This is the second bug fix release in the 1.2 series, coming less than
+two weeks after the 1.2.2 release made on August 8.
+
+The big motivation for a quick release was that there were a log of
+build system snags that people ran into with the 1.2.2 release. But,
+by the time we got those all done, we found that we had a bunch of
+fixes for cairo's rendering as well. So there's a lot of goodness in
+here for such a short time period.
+
+Rendering fixes
+---------------
+Fix image surfaces to not be clipped when used as a source (Vladimir Vukicevic)
+http://gitweb.freedesktop.org/?p=cairo;a=commit;h=72e25648c4c4bc82ddd938aa4e05887a293f0d8b
+
+Fix a couple of corner cases in dashing degenerate paths (Jeff Muizelaar)
+http://gitweb.freedesktop.org/?p=cairo;a=commit;h=fbb1758ba8384650157b2bbbc93d161b0c2a05f0
+
+Fix support for type1 fonts on win32 (Adrian Johnson)
+http://gitweb.freedesktop.org/?p=cairo;a=commit;h=da1019c9138695cb838a54f8b871bbfd0e8996d7
+
+Fix assertion failure when rotating bitmap fonts (Carl Worth)
+http://gitweb.freedesktop.org/?p=cairo;a=commit;h=0bfa6d4f33b8ddb5dc55bbe419c15df4af856ff9
+
+Fix assertion failure when calling cairo_text_path with bitmap fonts (Carl Worth)
+http://gitweb.freedesktop.org/?p=cairo;a=commit;h=9878a033531e6b96b5f27e69e10e90dee7440cd9
+
+Fix mis-handling of cairo_close_path in some situations (Tim Rowley, Carl Worth)
+http://gitweb.freedesktop.org/?p=cairo;a=commit;h=53f74e59faf1af78f2f0741ccf1f23aa5dad4efc
+
+Respect font_matrix translation in _cairo_gstate_glyph_path (Behdad Esfahbod)
+http://gitweb.freedesktop.org/?p=cairo;a=commit;h=f183b835b111d23e838889178aa8106ec84663b3
+
+Fix vertical metrics adjustment to work with non-identity shapes (Behdad Esfahbod)
+http://gitweb.freedesktop.org/?p=cairo;a=commit;h=b7bc263842a798d657a95e539e1693372448837f
+
+[PS] Set correct ImageMatrix in _cairo_ps_surface_emit_bitmap_glyph_data (Behdad Esfahbod)
+http://gitweb.freedesktop.org/?p=cairo;a=commit;h=d47388ad759b0a1a0869655a87d9b5eb6ae2445d
+
+Build system fixes
+------------------
+Fix xlib detection to prefer pkg-config to avoid false libXt dependency (Behdad Esfahbod)
+http://gitweb.freedesktop.org/?p=cairo;a=commit;h=0e78e7144353703cbd28aae6a67cd9ca261f1d68
+
+Fix typos causing win32 build problem with PS,PDF, and SVG backends (Behdad Esfahbod)
+http://gitweb.freedesktop.org/?p=cairo;a=commit;h=aea83b908d020e26732753830bb3056e6702a774
+
+Fix configure cache to not use stale results (Behdad Esfahbod)
+http://gitweb.freedesktop.org/?p=cairo;a=commit;h=6d0e3260444a2d5b6fb0cb223ac79f1c0e7b3a6e
+
+Fix to not pass unsupported warning options to the compiler (Jens Granseuer)
+http://gitweb.freedesktop.org/?p=cairo;a=commit;h=97524a8fdb899de1ae4a3e920fb7bda6d76c5571
+
+Fix to allow env. variables such as png_REQUIRES to override configure detection (Jens Granseuer)
+http://gitweb.freedesktop.org/?p=cairo;a=commit;h=abd16e47d6331bd3811c908e524b4dcb6bd23bf0
+
+Fix test suite to not use an old system cairo when converting svg2png (Behdad Esfahbod)
+http://gitweb.freedesktop.org/?p=cairo;a=commit;h=6122cc85c8f71b1ba2df3ab86907768edebe1781
+
+Fix test suite to not require signal.h to be present (Behdad Esfahbod)
+http://gitweb.freedesktop.org/?p=cairo;a=commit;h=6f8cf53b1e1ccdbe1ab6a275656b19c6e5120e40
+
+Code cleanups
+-------------
+Many useful warnings cleanups from sparse, valgrind, and careful eyes
+(Kjartan Maraas, Pavel Roskin)
+
+Release 1.2.2 (2006-08-08 Carl Worth <cworth@cworth.org>)
+=========================================================
+This is the first bug fix release in the 1.2 series since the original
+1.2.0 release made six weeks ago.
+
+There were some very serious bugs in the 1.2.0 release, (see below),
+so everybody is encouraged to upgrade from 1.2.0 to 1.2.2. The 1.2.2
+release maintains source and binary compatibility with 1.2.0 and does
+not make any API additions.
+
+Fix crashes with BGR X servers
+------------------------------
+With cairo 1.2.0 many people reported problems with all cairo-using
+programs, (including all GTK+ programs with GTK+ >= 2.8) immediately
+crashing with a complaint about an unsupported image format. This bug
+affected X servers that do not provide the Render extension and that
+provide a visual with BGR rather than RGB channel order.
+
+report:        https://bugs.freedesktop.org/show_bug.cgi?id=7294
+fix:   http://gitweb.freedesktop.org/?p=cairo;a=commit;h=9ae66174e774b57f16ad791452ed44efc2770a59
+
+Fix the "disappearing text" bug
+-------------------------------
+With cairo 1.2.0 many people reported that text would disappear from
+applications, sometimes reappearing with mouse motion or
+selection. The text would disappear after the first space in a string
+of text. This bug was caused by an underlying bug in (very common) X
+servers, and only affected text rendered without antialiasing, (either
+a bitmap font or a vector font with antialiasing disabled). The bug
+was also exacerbated by a KDE migration bug that caused antialiasing
+to be disabled more than desired.
+
+report:        https://bugs.freedesktop.org/show_bug.cgi?id=7494
+fix:   http://gitweb.freedesktop.org/?p=cairo;a=commit;h=456cdb3058f3b416109a9600167cd8842300ae14
+see also:
+Xorg:  https://bugs.freedesktop.org/show_bug.cgi?id=7681
+KDE:   http://qa.mandriva.com/show_bug.cgi?id=23990
+
+Fix broken image fallback scaling (aka. "broken printing")
+----------------------------------------------------------
+The various "print" backends, (pdf, ps, and svg), sometimes fallback
+to using image-based rendering for some operations. In cairo 1.2.0
+these image fallbacks were scaled improperly. Applications using cairo
+can influence the resolution of the image fallbacks with
+cairo_surface_set_fallback_resolution. With the bug, any value other
+than 72.0 would lead to incorrect results, (larger values would lead
+to increasingly shrunken output).
+
+report:        https://bugs.freedesktop.org/show_bug.cgi?id=7533
+fix:   http://gitweb.freedesktop.org/?p=cairo;a=commit;h=1feb4291cf7813494355459bb547eec604c54ffb
+
+Fix inadvertent semantic change of font matrix translation (Behdad Esfahbod)
+----------------------------------------------------------------------------
+The 1.2.0 release introduced an inadvertent change to how the
+translation components of a font matrix are interpreted. In the 1.0
+series, font matrix translation could be used to offset the glyph
+origin, (though glyph metrics were reported incorrectly in
+1.0). However in 1.2.0, the translation was applied to the advance
+values between each glyph. The 1.2.0 behavior is fairly useless in
+practice, and it was not intentional to introduce a semantic
+change. With 1.2.2 we return to the 1.0 semantics, with a much better
+implementation that provides correct glyph metrics.
+
+fix:   http://gitweb.freedesktop.org/?p=cairo;a=commit;h=84840e6bba6e72aa88fad7a0ee929e8955ba9051
+
+Fix create_similar to preserve fallback resolution and font options (Behdad Esfahbod)
+-------------------------------------------------------------------------------------
+There has been a long-standing issue with cairo_surface_create_similar
+such that font options and other settings from the original
+destination surface would not be preserved to the intermediate
+"similar" surface. This could result in incorrect rendering
+(particularly with respect to text hinting/antialiasing) with
+fallbacks, for example.
+
+report:        https://bugs.freedesktop.org/show_bug.cgi?id=4106
+fixes: http://gitweb.freedesktop.org/?p=cairo;a=commit;h=9fcb3c32c1f16fe6ab913e27eb54d18b7d9a06b0
+       http://gitweb.freedesktop.org/?p=cairo;a=commit;h=bdb4e1edadb78a2118ff70b28163f8bd4317f1ec
+
+xlib: Fix text performance regression from 1.0 to 1.2.0 (Vladimir Vukicevic)
+----------------------------------------------------------------------------
+Several people noticed that upgrading from cairo 1.0 to cairo 1.2.0
+caused a significant performance regression when using the xlib
+backend. This performance regression was particularly noticeable when
+doing lots of text rendering and when using a high-latency connection
+to the X server, (such as a remote X server over an ssh
+connection). The slowdown was identified and fixed in 1.2.2.
+
+report:        https://bugs.freedesktop.org/show_bug.cgi?id=7514
+fix:   http://gitweb.freedesktop.org/?p=cairo;a=commit;h=b7191885c88068dad57d68ced69a752d1162b12c
+
+PDF: Eliminate dependency on FreeType library dependency (Adrian Johnson)
+-------------------------------------------------------------------------
+The cairo 1.2 series adds a supported pdf backend to cairo. In cairo
+1.2.0 this backend required the freetype library, which was an
+undesirable dependency on systems such as win32, (cairo is designed to
+always prefer the "native" font system). As of cairo 1.2.2 the
+freetype library is not required to use the pdf backend on the win32
+platform.
+
+report:        https://bugs.freedesktop.org/show_bug.cgi?id=7538
+fix:   http://gitweb.freedesktop.org/?p=cairo;a=commit;h=a0989f427be87c60415963dd6822b3c5c3781691
+
+PDF: Fix broken output on amd64 (Adrian Johnson)
+------------------------------------------------
+report:        http://bugzilla.gnome.org/show_bug.cgi?id=349826
+fix:   http://gitweb.freedesktop.org/?p=cairo;a=commit;h=f4b12e497b7ac282b2f6831b8fb68deebc412e60
+
+PS: Fix broken output for truetype fonts > 64k (Adrian Johnson)
+---------------------------------------------------------------
+fix:   http://gitweb.freedesktop.org/?p=cairo;a=commit;h=067d97eb1793a6b0d0dddfbd0b54117844511a94
+
+PDF: Fix so that dashing doesn't get stuck on (Kent Worsnop)
+------------------------------------------------------------
+Kent notices that with the PDF backend in cairo 1.2.0 as soon as a
+stroke was performed with dashing, all subsequent strokes would also
+be dashed. There was no way to turn dashing off again.
+
+fix:   http://gitweb.freedesktop.org/?p=cairo;a=commit;h=778c4730a86296bf0a71080cf7008d7291792256
+
+Fix memory leaks in failure paths in gradient creation (Alfred Peng)
+--------------------------------------------------------------------
+fix:   http://gitweb.freedesktop.org/?p=cairo;a=commit;h=db06681b487873788b51a6766894fc619eb8d8f2
+
+Fix memory leak in _cairo_surface_show_glyphs (Chris Wilson)
+------------------------------------------------------------
+report:        https://bugs.freedesktop.org/show_bug.cgi?id=7766
+fix:   http://gitweb.freedesktop.org/?p=cairo;a=commit;h=e2fddcccb43d06486d3680a19cfdd5a54963fcbd
+
+Solaris: Add definition of cairo_private for some Sun compilers (Alfred Peng)
+-----------------------------------------------------------------------------
+report:        https://bugzilla.mozilla.org/show_bug.cgi?id=341874
+fix:   http://gitweb.freedesktop.org/?p=cairo;a=commit;h=04757a3aa8deeff3265719ebe01b021638990ec6
+
+Solaris: Change version number of Sun's Xorg server with buggy repeat (Brian Cameron)
+-------------------------------------------------------------------------------------
+report: https://bugs.freedesktop.org/show_bug.cgi?id=7483
+fix:   http://gitweb.freedesktop.org/?p=cairo;a=commit;h=e0ad1aa995bcec4246c0b8ab0d5a5a79871ce235
+
+Various memory leak fixes
+-------------------------
+Fix memory leak in _cairo_surface_show_glyphs (bug 7766)
+Fix file handle leak in failure path (bug 7616)
+Fix some memory leaks in the test cases.
+Fix some memory leaks in font subsetting code used in print backends.
+
+Documentation improvements (Behdad Esfahbod)
+--------------------------------------------
+Added new documentation for several functions (cairo_show_page,
+cairo_copy_page, cairo_in_stroke, cairo_in_fill).
+
+Fixed some syntax errors that were preventing some existing
+documentation from being published.
+
+Fixed several minor typographical errors.
+
+Added an index for new symbols in 1.2.
+
+Release 1.2.0 (2006-06-27 Carl Worth <cworth@cworth.org>)
+=========================================================
+This is the culmination of the work that has gone on within the 1.1
+branch of cairo.
+
+There has been one API addition since the cairo 1.1.10 snapshot:
+
+       cairo_xlib_surface_get_width
+       cairo_xlib_surface_get_height
+
+There's also a new feature without any API change:
+
+       Dots can now be drawn by using CAIRO_LINE_CAP_ROUND with
+       degenerate sub-paths, (cairo_move_to() followed by either
+       cairo_close_path() or a cairo_line_to() to the same location).
+
+And at least the following bugs have been fixed:
+
+ 6759  fontconfig option AntiAlias doesn't work in cairo 1.1.2
+ 6955  Some characters aren't displayed when using xlib (cache u...
+ 7268  positive device_offset values don't work as source
+ * PDF emit_glyph function needs to support bitmapped glyphs
+ * PS emit_glyph function needs to support bitmapped glyphs
+ * SVG emit_glyph function needs to support bitmapped glyphs
+ * PDF: minefield page one is falling back unnecessarily
+ * PS/PDF: Fix broken placement for vertical glyphs
+ * PS: Fix to not draw BUTT-capped zero-length dash segments
+ * Do device offset before float->fixed conversion
+   http://bugzilla.gnome.org/show_bug.cgi?id=332266
+ * PS: Fix source surfaces with transformations
+ * PS: Fix to not draw BUTT-capped degnerate sub-paths
+ * PS: Don't walk off end of array when printing "~>"
+ * Fix some memory leaks in the test suite rig
+ * SVG: Fix memory leak when using cairo_mask
+ * Fix EXTEND_REFLECT and EXTEND_PAD to not crash (though these are
+   still not yet fully implemented for surface patterns).
+
+This has been a tremendous effort by everyone, and I'm proud to have
+been a part of it. Congratulations to all contributors to cairo!
+
+Snapshot 1.1.10 (2006-06-16 Carl Worth <cworth@cworth.org>)
+===========================================================
+This is the fifth in a series of snapshots working toward the 1.2
+release of cairo.
+
+The primary motivation for this snapshot is to fix a long-standing bug
+that had long been silent, but as of the 1.1.8 snapshot started
+causing crashes when run against 16-bit depth X servers, (often Xvnc
+or Xnest). The fix for this adds a new CAIRO_FORMAT_RGB16_565 to the
+API.
+
+This snapshot also includes a rewrite of cairo's SVG backend to
+eliminate the dependency on libxml2. With this in place, cairo 1.2
+will not depend on any libraries that cairo 1.0 did not.
+
+As usual, there are also a few fixes for minor bugs.
+
+Snapshot 1.1.8 (2006-06-14 Carl Worth <cworth@cworth.org>)
+==========================================================
+This is the fourth in a series of snapshots working toward the 1.2
+release of cairo. At this point, all major features of the 1.2 release
+are in place, leaving just a few bug fixes left.
+
+In particular, there well be no additional API changes between this
+1.1.8 snapshot and the 1.2 release.
+
+The announcement for 1.1.6 mentioned several API changes being
+considered. Only one of these changes was actually implemented
+(set_dpi -> fallback_resolution). This change does introduce one
+source-level incompatibility with respect to previous 1.1.x snapshots,
+so see below for details.
+
+Here is an abbreviated summary of changes since the 1.1.6 snapshot:
+
+** API Change **
+----------------
+According to the plan mentioned in the 1.1.6 notes, one source-level
+incompatible change has been implemented. The following three
+functions have been removed from cairo's API:
+
+       cairo_pdf_surface_set_dpi
+       cairo_ps_surface_set_dpi
+       cairo_svg_surface_set_dpi
+
+and in their place the following function has been added:
+
+       cairo_surface_set_fallback_resolution
+
+The signature and semantics of the function remains the same, so it is
+a simple matter of changing the name of the function when calling
+it. As a transition mechanism, this snapshot will (on many systems)
+build to include the old symbols so that code previously compiled will
+still run. However, all source code using the old names must be
+updated before it will compile. And the upcoming 1.2 release is not
+anticipated to include the old symbols.
+
+Finally, it should be pointed out that the old symbols never existed
+in the supported API of any stable release of cairo. (In the stable
+1.0 releases the PDF, PS, and SVG backends were advertised as
+experimental and unstable.)
+
+And, as always, cairo continues to maintain source and binary
+compatibility between major releases. So applications compiled against
+supported backends in a stable release of cairo (1.0.4 say) will
+continue to compile and run without modification against new major
+releases (1.2.0 say) without modification.
+
+API additions
+-------------
+The following new functions have been added to cairo's API:
+
+       cairo_surface_get_content
+       cairo_debug_reset_static_data
+       cairo_image_surface_get_data
+       cairo_image_surface_get_format
+       cairo_image_surface_get_stride
+       cairo_win32_font_face_create_for_hfont
+
+New, backend-specific pkg-config files
+--------------------------------------
+In addition to the original cairo.pc file, cairo will also now install
+a pkg-config files for each configured backend, (for example
+cairo-pdf.pc, cairo-svg.pc, cairo-xlib.pc, cairo-win32.pc, etc.) this
+also includes optional font backends (such as cairo-ft.pc) and the
+optional png functionality (cairo-png.pc).
+
+These new pkg-config files should be very convenient for allowing
+cairo-using code to easily check for the existing of optional
+functionality in cairo without having to write complex rules to grub
+through cairo header files or the compiled library looking for
+symbols.
+
+Printing backend (PS, PDF, and SVG)
+-----------------------------------
+Improving the quality of the "printing" backends has been a priority
+of the development between cairo 1.1.6 and cairo 1.1.8.
+
+The big improvement here is in the area of text output. Previously, at
+best, text was output as paths without taking advantage of any font
+support available in the output file format.
+
+Now, at the minimum text paths will be shared by using type3 fonts
+(for PS and PDF---and similarly, defs for SVG). Also, if possible,
+type3 and truetype fonts will be embedded in PostScript and PDF
+output. There are still some known bugs with this, (for example,
+selecting text in a cairo-generated PDF file with an embedded truetype
+font does not work). So there will be some more changes in this area
+before cairo 1.2, but do try test this feature out as it exists so
+far.
+
+Many thanks to Kristian Høgsberg for the truetype and type1 font
+embedding.
+
+win32 backend
+-------------
+Performance improvements by preferring GDI over pixman rendering when possible.
+Fixes for text rendering.
+
+xlib backend
+------------
+Fix potentially big performance bug by making xlib's create_similar
+try harder to create a pixmap of a depth matching that of the screen.
+
+Bug fixes
+---------
+Among various other fixes, the following bugs listed in bugzilla have
+been fixed:
+
+    Bug 2488: Patch to fix pixman samping location bug (#2488).
+    https://bugs.freedesktop.org/show_bug.cgi?id=2488
+
+    Bug 4196: undef MIN an MAX before defining to avoid duplicate definition
+    https://bugs.freedesktop.org/show_bug.cgi?id=4196
+
+    Bug 4723: configure.in: Fix m4 quoting when examining pkg-config version
+    https://bugs.freedesktop.org/show_bug.cgi?id=4723
+
+    Bug 4882: Flag Sun's X server has having buggy_repeat.
+    https://bugs.freedesktop.org/show_bug.cgi?id=4882
+
+    Bug 5306: test/pdf2png: Add missing include of stdio.h
+    https://bugs.freedesktop.org/show_bug.cgi?id=5306
+
+    Bug 7075: Fix make clean to remove cairo.def
+    https://bugs.freedesktop.org/show_bug.cgi?id=7075
+
+(Many thanks to Behdad Esfahbod for helping us track down and fix many
+of these.)
+
+Snapshot 1.1.6 (2006-05-04 Carl Worth <cworth@cworth.org>)
+==========================================================
+This is the third in a series of snapshots working toward the imminent
+1.2 release of cairo. For a list of items still needing work on the
+cairo 1.2 roadmap, please see:
+
+       http://cairographics.org/ROADMAP
+
+As can be seen in that list, there are no longer any API additions
+left on the roadmap. Instead, there is a feature (PDF type 3 fonts) a
+performance optimization (X server gradients) and a list of bug
+fixes. This gives us a fair amount of freedom to cut the 1.2 release
+at almost any point by deciding to defer remaining bug fixes to
+subsequent maintenance releases such as 1.2.2 and 1.2.4.
+
+Before we will do that, we must first be wiling to commit to all the
+new API additions. As a heads-up, there are a couple of potential API
+changes being considered. (Note that these are changes to new API
+introduced during 1.1 so these will not introduce API
+incompatibilities compared to the stable 1.0 series). The changes
+being considered are:
+
+  cairo_get_group_target: may acquire x and y offset return
+       parameters. May also be eliminated in favor of
+       cairo_get_target assuming its role
+
+  cairo_pdf_surface_set_dpi:
+  cairo_ps_surface_set_dpi:
+  cairo_svg_surface_set_dpi: These functions may be removed in favor
+       of a new cairo_surface_set_fallback_resolution
+
+Additionally there is the possibility of a slight change in the
+semantics of cairo_set_line_width. We believe the current behavior of the sequence:
+
+       cairo_set_line_width; ... change CTM ...; cairo_stroke;
+
+is buggy. It is currently behaving the same as:
+
+       ... change CTM ...; cairo_set_line_width; cairo_stroke;
+
+We are considering fixing this bug before 1.2 with the hope that
+nobody is already relying on the buggy behavior described here. Do
+shout if you suspect you might be in that position.
+
+The items included in this snapshot (since the 1.1.4 snapshot) are
+described below.
+
+API additions
+-------------
+The long-awaited group-rendering support is now available with the
+following function calls:
+
+       cairo_push_group
+       cairo_push_group_with_content
+       cairo_pop_group
+       cairo_pop_group_to_source
+       cairo_get_group_target
+
+This API provides a much more convenient mechanism for doing rendering
+to an intermediate surface without the need to manually create a
+temporary cairo_surface_t and a temporary cairo_t and clean them up
+afterwards.
+
+Add the following missing get function to complement
+cairo_surface_set_device_offset:
+
+       cairo_surface_get_device_offset
+
+PDF backend (API addition)
+--------------------------
+The PDF backend now provides for per-page size changes, (similar to
+what the PostScript backend got in the 1.1.4 snapshot). The new API
+is:
+
+       cairo_pdf_surface_set_size
+
+Xlib backend (API additions)
+----------------------------
+The following functions have been added to allow the extraction of
+Xlib surface:
+
+       cairo_xlib_surface_get_display
+       cairo_xlib_surface_get_drawable
+       cairo_xlib_surface_get_screen
+       cairo_xlib_surface_get_visual
+       cairo_xlib_surface_get_depth
+
+XCB backend (experimental)
+--------------------------
+Update backend so that it now compiles with the recent XCB 0.9 release.
+
+Bug fixes and memory leak cleanup
+---------------------------------
+Various little things, nothing too significant though.
+
+Snapshot 1.1.4 (2006-05-03 Carl Worth <cworth@cworth.org>)
+==========================================================
+This is the second in a series of snapshots working toward the
+upcoming 1.2 release of cairo. For a list of items still needing work
+on the cairo 1.2 roadmap, please see:
+
+       http://cairographics.org/ROADMAP
+
+The items included in this snapshot (since the 1.1.2 snapshot) are
+described below.
+
+PostScript backend: new printing-oriented API
+---------------------------------------------
+We anticipate that with cairo 1.2, toolkits will begin to use cairo
+for printing on systems that use PostScript as the spool format. To
+support this use case, we have added 4 new function calls that are
+specific to the PostScript backend:
+
+       cairo_ps_surface_set_size
+        cairo_ps_surface_dsc_comment
+        cairo_ps_surface_dsc_begin_setup
+        cairo_ps_surface_dsc_begin_page_setup
+
+These functions allow variation of the page size/orientation from one
+page to the next in the PostScript output. They also allow the toolkit
+to provide per-document and per-page printer control options in a
+device-independent way, (for example, by using PPD options and
+emitting them as DSC comments into the PostScript output). This should
+allow toolkits to provide very fine-grained control of many options
+available in printers, (media size, media type, tray selection, etc.).
+
+SVG backend: builds by default, version control
+-----------------------------------------------
+The SVG backend continues to see major improvements. It is expected
+that the SVG backend will be a supported backend in the 1.2
+release. This backend will now be built by default if its dependencies
+(freetype and libxml2) are met.
+
+Additionally, the SVG backend now has flexibility with regard to what
+version of SVG it targets. It will target SVG 1.1 by default, which
+will require image fallbacks for some of the "fancier" cairo
+compositing operators. Or with the following new function calls:
+
+       cairo_svg_surface_restrict_to_version
+       cairo_svg_get_versions
+       cairo_svg_version_to_string
+
+it can be made to target SVG 1.2 in which there is native support for
+these compositing operators.
+
+Bug fixes
+---------
+At least the following bugs have been fixed since the 1.1.2 snapshot:
+
+crash at XRenderAddGlyphs
+https://bugs.freedesktop.org/show_bug.cgi?id=4705
+
+Can't build cairo-1.1.2 on opensolaris due to " void function cannot return value"
+https://bugs.freedesktop.org/show_bug.cgi?id=6792
+
+Missing out-of-memory check at gfx/cairo/cairo/src/cairo-atsui-font.c:185
+https://bugzilla.mozilla.org/show_bug.cgi?id=336129
+
+A couple of memory leaks.
+
+Snapshot 1.1.2 (2006-04-25 Carl Worth <cworth@cworth.org>)
+==========================================================
+This is the first in a series of snapshots working toward the upcoming
+1.2 release of cairo. (Subsequent snapshot will use successive even
+numbers for the third digit, 1.1.4, 1.1.6, etc.) This snapshot is
+backwards-compatible with the 1.0 series---it makes a few API
+additions but does not remove any API.
+
+PostScript and PDF backends are no longer "experimental"
+--------------------------------------------------------
+The major theme of the 1.2 release is improved PostScript and PDF
+backends for cairo. Unlike the 1.0 series, in the 1.2 series these
+backends will not be marked as experimental and will be enabled by
+default. We encourage people to test this snapshot and the PS/PDF
+backends in particular as much as possible.
+
+The PostScript and PDF output is not yet ideal.
+
+ * One major problem with the PostScript output is that image
+   fallbacks are used more often than strictly necessary, and the
+   image fallbacks are at a lower resolution than desired, (the
+   cairo_ps_surface_set_dpi call is ignored).
+
+  * The major drawback of the current PDF backend implementation is
+    its text support. Every glyph is represented by a filled path in
+    the PDF file. The causes file sizes to be much larger and
+    rendering to be much slower than desired.
+
+It is anticipated that both of these shortcomings will see some
+improvements before the final 1.2 release.
+
+In spite of those shortcomings, we hope that the PS and PDF backends
+will yield faithful results for pretty much any cairo operations you
+can throw at them. Please let us know if you are getting obviously
+"different" results from the PS/PDF backends than from the image or
+xlib backends.
+
+Other new experimental backends
+-------------------------------
+This snapshot includes three new backends that did not exist in the
+1.0 series:
+
+       * beos backend
+
+       * directfb backend
+
+       * svg backend
+
+These are all currently marked "experimental" and are disabled by
+default. But the SVG backend in particular has seen a lot of recent
+development and is very close to passing the entire cairo test
+suite. It is possible that this backend will become a fully supported
+backend by the time of the cairo 1.2 release.
+
+Public API additions
+--------------------
+There have been a few new API functions added to cairo, including:
+
+New get_type functions for querying sub-types of object:
+
+       cairo_surface_get_type
+       cairo_pattern_get_type
+       cairo_font_face_get_type
+       cairo_scaled_font_get_type
+
+More convenience in working with cairo_scaled_font_t with new getter
+functions:
+
+       cairo_scaled_font_get_font_face
+       cairo_scaled_font_get_font_matrix
+       cairo_scaled_font_get_ctm
+       cairo_scaled_font_get_font_options
+
+As well as a convenience function for setting a scaled font into a
+cairo context:
+
+       cairo_set_scaled_font
+
+and a function to allow text extents to be queried directly from a
+scaled font, (without requiring a cairo_surface_t or a cairo_t):
+
+       cairo_scaled_font_text_extents
+
+These new scaled font functions were motivated by the needs of the
+pango library.
+
+Finally, a new path-construction function was added which clears the
+current point in preparation for a new sub path. This makes cairo_arc
+easier to use in some situations:
+
+       cairo_new_sub_path
+
+Before the 1.2 release is final we do still plan a few more API
+additions specifically motivated by the needs of Mozilla/Firefox.
+
+Optimizations and bug fixes
+---------------------------
+Shortly after the 1.0 maintenance series branched off the mainline
+there was a major rework of the cairo font internals. This should
+provide some good performance benefits, but it's also another area
+people should look at closely for potential regressions.
+
+There has not yet been any widespread, systematic optimization of
+cairo, but various performance improvements have been made, (and some
+of them are fairly significant). So if some things seem faster than
+1.0 then things are good. If there are any performance regressions
+compared to 1.0 then there is a real problem and we would like to hear
+about that.
+
+There has been a huge number of bug fixes---too many to mention in
+detail. Again, things should be better, and never worse compared to
+1.0. Please let us know if your testing shows otherwise.
+
+Release 1.0.2 (2005-10-03 Carl Worth <cworth@cworth.org>)
+=========================================================
+For each bug number XXXX below, see:
+
+       https://bugs.freedesktop.org/show_bug.cgi?id=XXXX
+
+for more details.
+
+General bug fixes
+-----------------
+ * 4408 - Add support for dashing of stroked curves
+         (Carl Worth)
+
+ * 4409 - Fix dashing so that each dash is capped on both ends
+         (Carl Worth)
+
+ * 4414 - Prevent SIGILL failures (proper use of -mmmx and -msse flags)
+         (Sebastien Bacher, Billy Biggs)
+
+ * 4299 - Fix crashes with text display in multi-threaded program
+         (Alexey Shabalin, Carl Worth)
+
+ * 4401 - Do not use sincos function since it is buggy on some platforms)
+         (Tim Mooney, Carl Worth)
+
+ * 4245 - Fix several bugs in the test suite exposed by amd64 systems
+         (Seemant Kulleen, Carl Worth)
+
+ * 4321 - Add missing byteswapping on GetImage/PutImage
+         (Sjoerd Simons, Owen Taylor)
+
+ * 4220 - Make the check for rectangular trapezoids simpler and more accurate
+         (Richard Stellingwerff, Owen Taylor)
+
+ * 4260 - Add missing channel-order swapping for antialised fonts
+         (Barbie LeVile, Owen Taylor)
+
+ * 4283 - Fix compilation failure with aggressive inlining (gcc -O3)
+         (Marco Manfredini, Owen Taylor)
+
+ * 4208 - Fix some warnings from sparse
+         (Kjartan Maraas, Billy Biggs)
+
+ * 4269 - Fix to not crash when compiled with -fomit-frame-pointer
+         (Ronald Wahl, Owen Taylor)
+
+ * 4263 - Improve performance for vertical gradients
+         (Richard Stellingwerff, Owen Taylor)
+
+ * 4231
+ * 4298 - Accomodate gentoo and Mandriva versions in X server vendor string check
+         (Billy Biggs, Frederic Crozat, Owen Taylor)
+
+win32-specific fixes
+--------------------
+ * 4599 - Fix "missing wedges" on some stroked paths (win32)
+         (Tim Rowley, Jonathan Watt, Bertram Felgenhauer, Carl Worth, Keith Packard)
+
+ * 4612 - Fix disappearing text if first character out of surface (win32)
+         (Tim Rowley)
+
+ * 4602 - Fix shutdown of cairo from failing intermediate, size-0 bitmaps (win32)
+         Aka. the "white rectangles" bug from mozilla-svg testing
+         (Tim Rowley)
+
+ * Various portability improvements for win32
+         (Hans Breuer, Owen Taylor, Carl Worth)
+
+ * 4593 - Fix font sizes to match user expectations (win32)
+         (Tor Lillqvist, Owen Taylor)
+
+ * 3927 - Fix to report metrics of size 0 for glyph-not-available (win32)
+         (Hans Breuer, Owen Taylor, Tor Lillqvist)
+
+ * Add locking primitives for win32
+         (Hans Breuer)
+
+xlib-specific fixes
+-------------------
+ * Fix crash from size-0 pixmap due to empty clip region (xlib)
+         (Radek Doulík, Carl Worth)
+
+Release 1.0.0 (2005-08-24 Carl Worth <cworth@cworth.org>)
+=========================================================
+Experimental backends
+---------------------
+ * The Glitz, PS, PDF, Quartz, and XCB backends have been declared
+   experimental, and are not part of the API guarantees that accompany
+   this release. They are not built by default, even when the required
+   libraries are available, and must be enabled explicitly with
+   --enable-ps, --enable-pdf, --enable-quartz or --enable-xcb.
+
+   It is very painful for us to be pushing out a major release without
+   these backends enabled. There has been a tremendous amount of work
+   put into each one and all are quite functional to some
+   extent. However, each also has some limitations. And none of these
+   backends have been tested to the level of completeness and
+   correctness that we expect from cairo backends.
+
+   We do encourage people to experiment with these backends and report
+   success, failure, or means of improving them.
+
+Operator behavior
+-----------------
+ * Prior to 0.9.0 the SOURCE, CLEAR and a number of other operators
+   behaved in an inconsistent and buggy fashion and could affect areas
+   outside the clip mask. In 0.9.0, these six "unbounded" operators
+   were fixed to consistently clear areas outside the shape but within
+   the clip mask.  This is useful behavior for an operator such as IN,
+   but not what was expected for SOURCE and CLEAR. So, in this release
+   the behavior of SOURCE and CLEAR has been changed again. They now
+   affect areas only within both the source and shape. We can write
+   the new operators as:
+
+     SOURCE: dest' = (mask IN clip) ? source : dest
+     CLEAR:  dest' = (mask IN clip) ? 0 : dest
+
+Behavior and API changes
+------------------------
+ * Setting the filter on a gradient pattern would change the
+   interpolation between color stops away from the normal linear
+   interpolation. This dubious behavior has been removed.
+
+ * The CAIRO_CONTENT_VALID() and CAIRO_FORMAT_VALID() macros --
+   implementation details that leaked into cairo.h -- have been moved
+   into an internal header.
+
+ * The cairo_show_text function now advances the current point
+   according to the total advance values of the string.
+
+API additions
+-------------
+ * cairo_set_dash can now detect error and can set
+   CAIRO_STATUS_INVALID_DASH.
+
+Features
+--------
+ * When compiled against recent versions of fontconfig and FreeType,
+   artificial bold fonts can now be turned on from fonts.conf using
+   the FC_EMBOLDEN fontconfig key.
+
+Optimization
+------------
+ * The compositing code from the 'xserver' code tree has now been
+   completely merged into libpixman. This includes MMX optimization of
+   common operations.
+
+ * The image transformation code in libpixman has been improved and
+   now performs significantly faster.
+
+Bug fixes
+---------
+ * Several crashes related to corruption in the font caches have been
+   fixed.
+
+ * All test cases now match pixel-for-pixel on x86 and PPC; this
+   required fixing bugs in the compositing, stroking, and pattern
+   rendering code.
+
+ * Negative dash offsets have been fixed to work correctly.
+
+ * The stroking of paths with mutiple subpaths has now been fixed to
+   apply caps to all subpaths rather than just the last one.
+
+ * Many build fixes for better portability on various systems.
+
+ * Lots of other bug fixes, but we're too tired to describe them in
+   more detail here.
+
+Release 0.9.2 (2005-08-13 Carl Worth <cworth@cworth.org>)
+=========================================================
+Release numbering
+-----------------
+ * You will notice that this release jumped from 0.9.0 to 0.9.2. We've
+   decided to use an odd micro version number (eg. 0.9.1) to indicate
+   in-progress development between releases. As soon as 0.9.2 is
+   tagged, the version will be incremented in CVS to 0.9.3 where it
+   will stay until just before 0.9.4 is built, uploaded, and tagged.
+
+   So, even-micro == a released version, odd-micro == something in-between.
+
+Libpixman dependency dropped
+----------------------------
+ * As of this release, the dependency on an external libpixman has
+   been dropped. Instead, the code from libpixman needed for cairo has
+   been incorporated into the cairo source tree. The motivation for
+   this change is that while cairo's API is stable and ready to be
+   maintained after the 1.0 release, libpixman's API is not, so we do
+   not want to expose it at this time.
+
+   Also, the incorporation of libpixman into cairo also renames all
+   previously-public libpixman symbols in order to avoid any conflict
+   with a future release of libpixman
+
+API additions
+-------------
+ * Macros and functions have been added so that the version of cairo
+   can be queried at either compile-time or at run-time. The version
+   is made available as both a human-readable string and as a single
+   integer:
+
+       CAIRO_VERSION_STRING               eg. "0.9.2"
+       CAIRO_VERSION                      eg. 000902
+
+       const char*
+       cairo_version_string (void);    /* eg. "0.9.2" */
+
+       int
+       cairo_version (void);           /* eg. 000902 */
+
+   A macro is provided to convert a three-part component version into
+   the encoded single-integer form:
+
+       CAIRO_VERSION_ENCODE(X,Y,Z)
+
+   For example, the CAIRO_VERSION value of 000902 is obtained as
+   CAIRO_VERSION_ENCODE(0,9,2). The intent is to make version
+   comparisons easy, either at compile-time:
+
+       #if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(0,9,2)
+       ...
+       #endif
+
+   Or at run-time:
+
+       if (cairo_version() >= CAIRO_VERSION_ENCODE(0,9,2)) { /* ... */ }
+
+Thread safety
+-------------
+ * This release adds pthread-based locking (when available) to make
+   the caches used by cairo safe for threaded programs. Some may
+   remember a failed experiment with this locking between the 0.5.1
+   and 0.5.2 snapshots, (where even single-threaded programs that
+   linked with -lpthread would deadlock). We believe that that problem
+   has been fixed, so we are looking forward to testing and reports
+   from users with threaded applications.
+
+Bug fixes
+---------
+ * The XCB and Quartz backends failed to compiled in the 0.9.0 release
+   due to minor syntax errors. These have now been fixed.
+
+ * Various crashes in glitz and pixman due to size 0 glyphs have been
+   fixed.
+
+Release 0.9.0 (2005-08-08 Carl Worth <cworth@cworth.org>)
+=========================================================
+Soname change
+-------------
+ * In all prior snapshots, the libtool library versioning was set to
+   1:0:0. As this release is intended to mark the beginning of
+   backwards-compatible releases, the versioning has been incremented
+   to 2:0:0. You will notice that the numeric extension on the
+   installed library filename will change similarly.
+
+   This change will also require all cairo-using applications to be
+   recompiled. We recognize that this may cause some frustration since
+   this release is backwards-compatible with 0.6.0 and in that sense
+   "shouldn't" require re-compilation. However, since all historical
+   snapshots have used the same 1:0:0 version in spite of incompatible
+   API changes between them, it was essential that the upcoming 1.0
+   release series have distinct library versioning.
+
+   All future releases will use the library versioning to properly
+   indicate compatibility between releases. So, any application
+   re-compiled now to work with the 0.9.0 will not need to be
+   recompiled when a compatible 1.0 release of cairo is made in the
+   future.
+
+API additions
+-------------
+ * Add new function calls to set/get the current antialiasing mode in
+   the graphics state:
+
+       cairo_set_antialias
+       cairo_get_antialias
+
+   This call accepts the same modes recently added for font options
+   (NONE or GRAY) but affects the rendering of geometry other than
+   text. The intent of this call is to enable more precise control of
+   which pixels are affected by each operation, for example to allow
+   for full-scene antialiasing for seam-free rendering. It is not
+   expected that non-antialiased rendering will perform better than
+   anti-aliased rendering.
+
+ * Three new functions were added to provide support for mixed cairo-
+   and non-cairo drawing to the same surface:
+
+       cairo_surface_mark_dirty
+       cairo_surface_mark_dirty_rectangle
+       cairo_surface_flush
+
+ * The return type of the several "reference" functions was change,
+   (API compatibly), from void to the same type as the argument. The
+   affected functions are:
+
+       cairo_font_face_reference
+       cairo_scaled_font_reference
+       cairo_pattern_reference
+       cairo_surface_reference
+       cairo_reference
+
+   This allows a convenient way to assign and reference in a single
+   statement.
+
+Semantic changes
+----------------
+ * The behavior of cairo_set_source with a pattern with a non-identity
+   matrix was previously not well-defined. The new behavior is as
+   follows:
+
+       The pattern's transformation matrix will be locked to the
+       user space in effect at the time of cairo_set_source(). This means
+       that further modifications of the CTM will not affect the source
+       pattern.
+
+cairo-win32
+-----------
+ * Some portability improvements, (eg. workaround for missing stdint.h).
+
+cairo-ft
+--------
+ * Updated to allow compilation with older versions of freetype.
+
+Bug fixes
+---------
+ * Fix the unbounded operators to actually produce a correct result,
+   (previously the results were artificially restricted to the
+   bounding box of whatever shape was being drawn rather than
+   extending out infinitely). The fixed operators are:
+
+       CAIRO_OPERATOR_CLEAR
+       CAIRO_OPERATOR_SOURCE
+       CAIRO_OPERATOR_OUT
+       CAIRO_OPERATOR_IN
+       CAIRO_OPERATOR_DEST_IN
+       CAIRO_OPERATOR_DEST_ATOP
+
+ * Fix cairo_mask and cairo_mask_surface to transform the mask by the
+   current transformation matrix (CTM).
+
+ * Fix cairo_set_source to lock the CTM used to transform the pattern.
+
+ * Workaround for X server Render bug involving repeating patterns
+   with a general transformation matrix.
+
+ * cairo_get_font_face fixed to return a "nil" font face object rather
+   than NULL on error.
+
+ * cairo_set_font_face fixed to not crash if given a NULL font face,
+   (which is the documented interface for restoring the default font
+   face).
+
+ * Fix xlib glyphset caching to not try to free a NULL glyph.
+
+Snapshot 0.6.0 (2005-07-28 Carl Worth <cworth@cworth.org>)
+==========================================================
+API changes
+-----------
+* The prototypes of the following functions have changed:
+
+       cairo_xlib_surface_create_with_xrender_format
+       cairo_xlib_surface_create_for_bitmap
+
+  A Screen* parameter has been added to each. This allows the cairo
+  xlib backend to work correctly with multi-head X servers.
+
+* The following function has been modified:
+
+       cairo_scaled_font_create
+
+  to accept a cairo_font_options_t*. See below fore more details.
+
+* All opaque, reference-counted cairo objects have now been moved to a
+  standard error-handling scheme. The new objects to receive this
+  treatment are cairo_font_face_t, cairo_scaled_font_t, and
+  cairo_surface_t. (Previous snapshots already provided this scheme
+  for cairo_t, cairo_path_t, and cairo_pattern_t.)
+
+  This changes two functions to have a return type of void rather than
+  cairo_status_t:
+
+       cairo_scaled_font_extent
+       cairo_surface_finish
+
+  And significantly, none of the create functions for any of the
+  objects listed above will return NULL. The pointer returned from any
+  function will now always be a valid pointer and should always be
+  passed to the corresponding destroy function when finished
+
+  The simplest strategy for porting code is to switch from:
+
+       object = cairo_<object>_create ();
+       if (object == NULL)
+           goto BAILOUT;
+
+       /* act on object */
+
+       cairo_<object>_destroy (object);
+
+  to:
+
+       object = cairo_<object>_create ();
+       if (cairo_<object>_status (object))
+           goto BAILOUT;
+
+       /* act on object */
+
+       cairo_<object>_destroy (object);
+
+   But significantly, it is not required to check for an error status
+   before the "act on object" portions of the code above. All
+   operations on an object with an error status are, by definition,
+   no-ops without side effect. So new code might be written in an
+   easier-to-read style of:
+
+       object = cairo_<object>_create ();
+
+       /* act on object */
+
+       cairo_<object>_destroy (object);
+
+   with cairo_<object>_status checks placed only at strategic
+   locations. For example, passing an error object to another object,
+   (eg. cairo_set_source with an in-error pattern), will propagate the
+   error to the subsequent object (eg. the cairo_t). This means that
+   error checking can often be deferred even beyond the destruction of
+   a temporary object.
+
+API additions
+-------------
+* New functions for checking the status of objects that have been
+  switched to the common error-handling scheme:
+
+       cairo_font_face_status
+       cairo_scaled_font_status
+       cairo_surface_status
+
+* The _cairo_error function which was added in 0.5.1 has now been made
+  much more useful. In 0.5.1 only errors on cairo_t objects passed
+  through _cairo_error. Now, an error on any object should pass
+  through _cairo_error making it much more reliable as a debugging
+  mechanism for finding when an error first occurs.
+
+* Added new font options support with a myriad of functions:
+
+       cairo_font_options_create
+       cairo_font_options_copy
+       cairo_font_options_destroy
+
+       cairo_font_options_status
+
+       cairo_font_options_merge
+       cairo_font_options_equal
+       cairo_font_options_hash
+
+       cairo_font_options_set_antialias
+       cairo_font_options_get_antialias
+       cairo_font_options_set_subpixel_order
+       cairo_font_options_get_subpixel_order
+       cairo_font_options_set_hint_style
+       cairo_font_options_get_hint_style
+       cairo_font_options_set_hint_metrics
+       cairo_font_options_get_hint_metrics
+
+       cairo_surface_get_font_options
+
+       cairo_ft_font_options_substitute
+
+       cairo_set_font_options
+       cairo_get_font_options
+
+   This new font options support allows the application to have much
+   more fine-grained control over how fonts are rendered.
+   Significantly, it also allows surface backends to have some
+   influence over the process. For example, the xlib backend now
+   queries existing Xft properties to set font option defaults.
+
+* New function:
+
+       cairo_xlib_surface_set_drawable
+
+  which allows the target drawable for an xlib cairo_surface_t to be
+  changed to another with the same format, screen, and display. This
+  is necessary in certain double-buffering techniques.
+
+New features
+------------
+* Sub-pixel text antialiasing is now supported.
+
+Bug fixes
+---------
+* Fixed assertion failure in cairo_surface_create_similar when
+  application commits an error by passing a cairo_format_t rather than
+  a cairo_content_t.
+
+* Avoid division by zero in various places (cairo-ft).
+
+* Fix infinite loop when using non-default visuals (cairo-xlib).
+
+* Eliminate segfault in cairo_image_surface_create_from_png_stream.
+
+* Prevent errant sign-extension of masks on 64-bit architectures
+  (cairo-xlib and cairo-xcb).
+
+* Other miscellaneous fixes.
+
+Snapshot 0.5.2 (2005-07-18 Carl Worth <cworth@cworth.org>)
+==========================================================
+API changes
+-----------
+* New functions for creating patterns of a single color:
+
+       cairo_pattern_create_rgb
+       cairo_pattern_create_rgba
+
+* Change cairo_surface_create_similar to accept a new type of
+  cairo_content_t rather than cairo_format_t:
+
+       typedef enum _cairo_content {
+           CAIRO_CONTENT_COLOR         = 0x1000,
+           CAIRO_CONTENT_ALPHA         = 0x2000,
+           CAIRO_CONTENT_COLOR_ALPHA   = 0x3000
+       } cairo_content_t;
+
+* Add new CAIRO_FORMAT_VALID and CAIRO_CONTENT_VALID macros.
+
+* Remove unused status value:
+
+       CAIRO_STATUS_NO_TARGET_SURFACE
+
+* Add new status values:
+
+       CAIRO_STATUS_INVALID_STATUS
+
+* Require libpixman >= 0.1.5 (for necessary bug fixes)
+
+Bug fixes
+---------
+* Fix cairo_surface_write_to_png for RGB24 images.
+
+* Fix broken metrics and rendering for bitmap fonts. Add mostly
+  useless bitmap glyph transformation.
+
+* Fix glyph caches to not eject entries that might be immediately
+  needed, (fixing intermittent crashes when rendering text).
+
+* Fix all memory leaks found by running "make check-valgrind".
+
+ATSUI backend changes
+---------------------
+* Allow building against < 10.3 SDK.
+
+* Prevent crash on empty strings.
+
+Glitz backend changes
+---------------------
+* Require glitz >= 0.4.4.
+
+* Use frame buffer objects instead of pbuffers for accelerated
+  offscreen drawing.
+
+* Minor improvement to gradient pattern creation.
+
+PostScript backend fixes
+------------------------
+* Rewrite of the PS backend to generate more interesting output that
+  the old big-image implementation.
+
+Win32 backend fixes
+-------------------
+* Implement glyph path support.
+
+* Fix swap of blue and green values in the fill_rectangles path.
+
+Xlib backend fixes
+------------------
+* Add optimization to use XCopyArea rather than XRenderComposite when
+  transforming only with an integer translation, and using SOURCE
+  operator or OVER with a source pattern without alpha.
+
+Snapshot 0.5.1 (2005-06-20 Carl Worth <cworth@cworth.org>)
+==========================================================
+API changes
+-----------
+* Removed cairo_status_string(cairo_t*) and add
+  cairo_status_to_string(cairo_status_t) in its place. Code using
+  cairo_status_string can be ported forward as follows:
+
+       cairo_status (cr);
+       ->
+       cairo_status_to_string (cairo_status (cr));
+
+* Removed the BAD_NESTING restriction which means that two different
+  cairo_t objects can now interleave drawing to the same
+  cairo_surface_t without causing an error.
+
+* The following functions which previously had a return type of
+  cairo_status_t now have a return type of void:
+
+       cairo_pattern_add_color_stop_rgba
+       cairo_pattern_set_matrix
+       cairo_pattern_get_matrix
+       cairo_pattern_set_extend
+       cairo_pattern_set_filter
+
+  See discussion of cairo_pattern_status below for more details.
+
+API additions
+-------------
+* Improved error handling:
+
+       cairo_status_t
+       cairo_pattern_status (cairo_pattern_t *pattern);
+
+  This snapshot expands the status-based error handling scheme from
+  cairo_t to cairo_path_t and cairo_pattern_t. It also expands the
+  scheme so that object-creating functions, (cairo_create,
+  cairo_pattern_create_*, cairo_copy_path_*), are now guaranteed to
+  not return NULL. Instead, in the case of out-of-memory these
+  functions will return a static object with
+  status==CAIRO_STATUS_NO_MEMORY. The status can be checked with the
+  functions cairo_status and cairo_pattern_status, or by direct
+  inspection of the new status field in cairo_path_t.
+
+  Please note that some objects, including cairo_surface_t and all of
+  the font-related objects have not been converted to this
+  error-handling scheme.
+
+* In addition to the above changes, a new private function has been added:
+
+       _cairo_error
+
+  This function can be used to set a breakpoint in a debugger to make
+  it easier to find programming error in cairo-using code. (Currently,
+  _cairo_error is called when any error is detected within a cairo_t
+  context, but is not called for non-cairo_t errors such as for
+  cairo_path_t and cairo_pattern_t).
+
+* Fixed cairo_path_data_t so that its enum is visible to C++ code, (as
+  cairo_path_data_type_t).
+
+Performance improvements
+------------------------
+* Made a minor performance improvement for clipping, (restrict clip
+  surface to the new intersected bounds).
+
+* Optimize rendering of a solid source pattern with a pixel-aligned
+  rectangular path to use backend clipping rather than rasterization
+  and backend compositing.
+
+* Optimize cairo_paint_with_alpha to defer to cairo_paint when alpha
+  is 1.0.
+
+Bug fixes
+---------
+* Fixed memory leak in cairo_copy_path.
+
+* A build fix for non-srcdir builds.
+
+PDF backend fixes
+-----------------
+* New support for path-based clipping.
+
+* Fix for text rotated to angles other than multiples of π/2.
+
+Win32 backend fixes
+-------------------
+* Fix for text extents.
+
+Xlib backend
+------------
+* Implemented a complex workaround for X server bug[*] related to
+  Render-based compositing with untransformed, repeating source
+  pictures. The workaround uses core Xlib when possible for
+  performance, (ie. with CAIRO_OPERATOR_SOURCE or CAIRO_OPERATOR_OVER
+  with an opaque source surface), and falls back to the pixman
+  image-based compositing otherwise.
+
+  [*] https://bugs.freedesktop.org/show_bug.cgi?id=3566
+
+* Various bug fixes, particularly in the fallback paths.
+
+Snapshot 0.5.0 (2005-05-17 Carl Worth <cworth@cworth.org>)
+==========================================================
+This is a pretty big, and fairly significant snapshot.  It represents
+between 2 and 3 months of solid work from a lot of people on improving
+the API as much as possible. I'd like to express my appreciation and
+congratulations to everyone who has worked on the big API Shakeup,
+(whether in email battles over names, or fixing my silly bugs).
+
+This snapshot will require some effort on the part of users, since
+there are a _lot_ of API changes (ie. no cairo program ever written is
+safe --- they're all broken now in at least one way). But, in spite of
+that, we do encourage everyone to move their code to this snapshot as
+soon as possible. And we're doing everything we can think of to make
+the transition as smooth as possible.
+
+The idea behind 0.5 is that we've tried to make every good API change
+we could want now, and get them all done with. That is, between now
+and the 1.0 release of cairo, we expect very few new API changes,
+(though some will certainly sneak in). We will have some significant
+additions, but the pain of moving code from cairo 0.4 to cairo 0.5
+should be a one time experience, and things should be much smoother as
+we continue to move toward cairo 1.0.
+
+And with so many changes coming out for the first time in this 0.5
+release, we really do need a lot of people trying this out to make
+sure the ideas are solid before we freeze the API in preparation for
+the 1.0 release.
+
+OK, enough introduction. Here is a (not-quite-complete) description of
+the API removals, changes and additions in this snapshot, (compared to
+0.4.0)
+
+API removals
+============
+The following public functions have been removed:
+
+- cairo_set_target_*
+
+       This is a big change. See the description of cairo_create in
+       the API changes section for how to deal with this.
+
+- cairo_set_alpha
+
+       Alpha blending hasn't gone away; there's just a much more
+       unified rendering model now. Almost all uses of
+       cairo_set_alpha will be trivially replaced with
+       cairo_set_source_rgba and a few others will be replaced just
+       as easily with cairo_paint_with_alpha.
+
+- cairo_show_surface
+
+       Another useful function that we realized was muddling up the
+       rendering model. The replacement is quite easy:
+       cairo_set_source_surface and cairo_paint.
+
+- cairo_matrix_create
+- cairo_matrix_destroy
+- cairo_matrix_copy
+- cairo_matrix_get_affine
+
+       These functions supported an opaque cairo_matrix_t. We now
+       have an exposed cairo_matrix_t structure, so these can be
+       dropped.
+
+- cairo_surface_set_repeat
+- cairo_surface_set_matrix
+- cairo_surface_set_filter
+
+       These properties don't belong on surfaces. If you were using
+       them, you'll just want to instead use
+       cairo_pattern_create_for_surface and then set these properties
+       on the pattern.
+
+- cairo_copy
+
+       This was a confusing function and hopefully nobody will miss
+       it. But if you really don't find cairo_save/restore adequate,
+       let us know and we have another idea for a potential
+       replacement.
+
+And while we're on the subject of removals, we carefully tightened up
+the cairo header files so they no longer gratuitously include header
+files that are not strictly necessary, (stdio.h, stdint.h, pixman.h,
+Xrender.h, etc. and their dependencies). This may lead to some
+surprising errors, so keep your eyes open for that.
+
+API changes
+===========
+Here are some of the API changes that have occurred:
+
+~ cairo_create(void) -> cairo_create(cairo_surface_t *)
+
+       This is the big change that breaks every program. The ability
+       to re-target a cairo_t was not particularly useful, but it did
+       introduce a lot of muddy semantic questions. To eliminate
+       that, cairo_create now requires its target surface to be
+       passed in at creation time. This isn't too hard to cope with
+       as the typical first operation after cairo_create was often
+       cairo_set_target_foo. So the order of those two swap and the
+       application instead has cairo_foo_surface_create, then
+       cairo_create.
+
+~ cairo_current_* -> cairo_get_*
+
+       We had a strange mixture of cairo_get and cairo_current
+       functions. They've all been standardized on cairo_get, (though
+       note one is cairo_get_current_point).
+
+~ CAIRO_OPERATOR_SRC -> CAIRO_OPERATOR_SOURCE
+~ CAIRO_OPERATOR_OVER_REVERSE -> CAIRO_OPERATOR_DEST_OVER
+
+       Many of the cairo_operator_t symbolic values were renamed to
+       reduce the amount of abbreviation. The confusing "OP_REVERSE"
+       naming was also changed to use "DEST_OP" instead which is
+       easier to read and has wider acceptance in other
+       libraries/languages.
+
+~ cairo_set_pattern -> cairo_set_source
+~ cairo_set_rgb_color -> cairo_set_source_rgb
+
+       All of the various functions that changed the source
+       color/pattern were unified to use cairo_set_source names to
+       make the relation more clear.
+
+~ cairo_transform_point                   -> cairo_user_to_device
+~ cairo_transform_distance        -> cairo_user_to_device_distance
+~ cairo_inverse_transform_point           -> cairo_device_to_user
+~ cairo_inverse_transform_distance -> cairo_device_to_user_distance
+
+       These names just seemed a lot more clear.
+
+~ cairo_init_clip      -> cairo_reset_clip
+~ cairo_concat_matrix  -> cairo_transform
+
+       More abbreviation elimination
+
+~ cairo_current_path     -> cairo_copy_path
+~ cairo_current_path_flat -> cairo_copy_path_flat
+
+       The former mechanism for examining the current path was a
+       function that required 3 or 4 callbacks. This was more
+       complexity than warranted in most situations. The new
+       cairo_copy_path function copies the current path into an
+       exposed data structure, and the documentation provides a
+       convenient idiom for navigating the path data.
+
+API additions
+-------------
++ cairo_paint
+
+       A generalized version of the painting operators cairo_stroke
+       and cairo_fill. The cairo_paint call applies the source paint
+       everywhere within the current clip region. Very useful for
+       clearing a surface to a solid color, or painting an image,
+       (see cairo_set_source_surface).
+
++ cairo_paint_with_alpha
+
+       Like cairo_paint but applying some alpha to the source,
+       (making the source paint translucent, eg. to blend an image on
+       top of another).
+
++ cairo_mask
+
+       A more generalized version of cairo_paint_with_alpha which
+       allows a pattern to specify the amount of translucence at each
+       point rather than using a constant value everywhere.
+
++ cairo_mask_surface
+
+       A convenience function on cairo_mask for when the mask pattern
+       is already contained within a surface.
+
++ cairo_surface_set_user_data
++ cairo_surface_get_user_data
++ cairo_font_face_set_user_data
++ cairo_font_face_get_user_data
+
+       Associate arbitrary data with a surface or font face for later
+       retrieval. Get notified when a surface or font face object is
+       destroyed.
+
++ cairo_surface_finish
+
+       Allows the user to instruct cairo to finish all of its
+       operations for a given surface. This provides a safe point for
+       doing things such as flushing and closing files that the
+       surface may have had open for writing.
+
++ cairo_fill_preserve
++ cairo_stroke_preserve
++ cairo_clip_preserve
+
+       One interesting change in cairo is that the path is no longer
+       part of the graphics state managed by
+       cairo_save/restore. This allows functions to construct paths
+       without interfering with the graphics state. But it prevents
+       the traditional idiom for fill-and-stroke:
+
+               cairo_save; cairo_fill; cairo_restore; cairo_stroke
+
+       Instead we know have alternate versions cairo cairo_fill,
+       cairo_stroke, and cairo_clip that preserve the current path
+       rather than consuming it. So the idiom now becomes simply:
+
+               cairo_fill_preserve; cairo_stroke
+
++ cairo_surface_write_to_png
++ cairo_surface_write_to_png_stream
+
+       In place of a single PNG backend, now a surface created
+       through any backend (except PDF currently) can be written out
+       to a PNG image.
+
++ cairo_image_surface_create_from_png
++ cairo_image_surface_create_from_png_stream
+
+       And its just as easy to load a PNG image into a surface as well.
+
++ cairo_append_path
+
+       With the new, exposed path data structure, it's now possible
+       to append bulk path data to the current path, (rather than
+       issuing a long sequence of cairo_move_to/line_to/curve_to
+       function calls).
+
+Xlib and XCB backends
+---------------------
+
+Any cairo_format_t and Colormap arguments have been dropped from
+cairo_xlib_surface_create. There are also two new
+cairo_xlib|xcb_surface_create functions:
+
+       cairo_xlib|xcb_surface_create_for_bitmap
+               (Particular for creating A1 surfaces)
+       cairo_xlib|xcb_surface_create_with_xrender_format
+               (For any other surface types, not described by a Visual*)
+
+All of these surface create functions now accept width and height. In
+addition, there are new cairo_xlib|xcb_surface_set_size functions
+which must be called each time a window that is underlying a surface
+changes size.
+
+Print backends (PS and PDF)
+---------------------------
+The old FILE* based interfaces have been eliminated. In their place we
+have two different functions. One accepts a simple const char
+*filename. The other is a more general function which accepts a
+callback write function and a void* closure. This should allow the
+flexibility needed to hook up with various stream object in many
+languages.
+
+In addition, when specifying the surface size during construction, the
+units are now device-space units (ie. points) rather than inches. This
+provides consistency with all the other surface types and also makes
+it much easier to reason about the size of the surface when drawing to
+it with the default identity matrix.
+
+Finally, the DPI parameters, which are only needed to control the
+quality of fallbacks, have been made optional. Nothing is required
+during surface_create (300 DPI is assumed) and
+cairo_ps|pdf_surface_set_dpi can be used to set alternate values if
+needed.
+
+Font system
+-----------
+Owen very graciously listened to feedback after the big font rework he
+had done for 0.4, and came up with way to improve it even more. In 0.4
+there was a cairo_font_t that was always pre-scaled. Now, there is an
+unscaled cairo_font_face_t which is easier to construct, (eg. no
+scaling matrix required) and work with, (it can be scaled and
+transformed after being set on the graphics state). And the font size
+manipulation functions are much easier. You can set an explicit size
+and read/modify/write the font matrix with:
+
+       cairo_set_font_size
+       cairo_get_font_matrix
+       cairo_set_font_matrix
+
+(Previously you could only multiply in a scale factor or a matrix.) A
+pleasant side effect is that we can (and do) now have a default font
+size that is reasonable, as opposed to the old default height of one
+device-space unit which was useless until scaled.
+
+Of course, the old pre-scaled font had allowed some performance
+benefits when getting many metrics for a font. Those benefits are
+still made available through the new cairo_scaled_font_t. And a
+cairo_font_face_t can be "promoted" to a cairo_scaled_font_t by
+suppling a font_matrix and the desired CTM.
+
+Quartz backend
+--------------
+Tim Rowley put in the work to bring the Quartz backend back after it
+had been disabled in the 0.4.0 snapshot. He was not able to bring back
+the function that allows one to create a cairo_font_t from an ATSUI
+style:
+
+       cairo_font_t *
+       cairo_atsui_font_create (ATSUStyle style);
+
+because he didn't have a test case for it. If you care about this
+function, please provide a fairly minimal test and we'll try to bring
+it back in an upcoming snapshot.
+
+Snapshot 0.4.0 (2005-03-08 Carl Worth <cworth@cworth.org>)
+==========================================================
+New documentation
+-----------------
+Owen Taylor has converted cairo's documentation system to gtk-doc and
+has begun some long-needed work on the documentation, which can now be
+viewed online here:
+
+       http://cairographics.org/manual/
+
+New backend: win32
+------------------
+This is the first snapshot to include a functional win32 backend,
+(thanks to Owen Taylor). The interface is as follows:
+
+       #include <cairo-win32.h>
+
+       void
+       cairo_set_target_win32 (cairo_t *cr,
+                               HDC      hdc);
+
+       cairo_surface_t *
+       cairo_win32_surface_create (HDC hdc);
+
+       cairo_font_t *
+       cairo_win32_font_create_for_logfontw (LOGFONTW       *logfont,
+                                             cairo_matrix_t *scale);
+
+       cairo_status_t
+       cairo_win32_font_select_font (cairo_font_t *font,
+                                     HDC           hdc);
+
+       void
+       cairo_win32_font_done_font (cairo_font_t *font);
+
+       double
+       cairo_win32_font_get_scale_factor (cairo_font_t *font);
+
+And see also the documentation at:
+
+http://cairographics.org/manual/cairo-Microsoft-Windows-Backend.html
+
+Disabled backend: quartz
+------------------------
+Unfortunately, the quartz backend code is currently out of date with
+respect to some recent backend interface changes. So, the quartz
+backend is disabled in this snapshot.
+
+If the quartz backend is brought up-to-date before the next snapshot,
+we would be glad to make a 0.4.1 snapshot that re-enables it, (we do
+not expect many more big backend interface changes).
+
+API Changes
+-----------
+The font system has been revamped, (as Owen Taylor's work with
+integrating pango and cairo gave us the first serious usage of the
+non-toy font API).
+
+One fundamental, user-visible change is that the cairo_font_t object
+now represents a font that is scaled to a particular device
+resolution. Further changes are described below.
+
+ cairo.h
+ -------
+ Removed cairo_font_set_transform and cairo_font_current_transform.
+
+ Added cairo_font_extents and cairo_font_glyph_extents. See
+ documentation for details:
+
+ http://cairographics.org/manual/cairo-cairo-t.html#cairo-font-extents
+
+ cairo-ft.h
+ ----------
+ The cairo_ft_font API changed considerably. Please see the
+ documentation for details:
+
+ http://cairographics.org/manual/cairo-FreeType-Fonts.html
+
+Performance
+-----------
+Make the fast-path clipping (pixel-aligned rectangles) faster.
+
+Add optimization for applying a constant alpha to a pattern.
+
+Optimize gradients that are horizontal or vertical in device space.
+
+Xlib: When RENDER is not available, use image surfaces for
+intermediate surfaces rather than xlib surfaces.
+
+Backend-specific changes
+------------------------
+ Glitz
+ -----
+ Major update to glitz backend. The output quality should now be just
+ as good as the image and xlib backends.
+
+ Track changes to glitz 0.4.0.
+
+ PDF
+ ---
+ Various improvements to produce more conformant output.
+
+Internals
+---------
+David Reveman contributed a large re-work of the cairo_pattern_t
+implementation, providing cleaner code and more optimization
+opportunities.
+
+ Backend interface changes
+ -------------------------
+ Rework backend interface to accept patterns, not surfaces for source
+ and mask.
+
+ Remove set_matrix, set_filter, and set_repeat functions.
+
+ More sophisticated backend interface for image fallbacks,
+ ({acquire,release}_{source,dest}_image() and clone_similar).
+
+Bug fixes
+---------
+Only install header files for backends that have been compiled.
+
+Fixed some rounding errors leading to incorrectly placed glyphs.
+
+Many other minor fixes.
+
+Snapshot 0.3.0 (2005-01-21 Carl Worth <cworth@cworth.org>)
+==========================================================
+Major API changes
+-----------------
+1) The public header files will no longer be directly installed into
+   the system include directory. They will now be installed in a
+   subdirectory named "cairo", (eg. in /usr/include/cairo rather than
+   in /usr/include).
+
+   As always, the easiest way for applications to discover the
+   location of the header file is to let pkg-config generate the
+   necessary -I CFLAGS and -L/-l LDFLAGS. For example:
+
+       cc `pkg-config --cflags --libs cairo` -o foo foo.c
+
+   IMPORTANT: Users with old versions of cairo installed will need to
+              manually remove cairo.h and cairo-features.h from the
+              system include directories in order to prevent the old
+              headers from being used in preference to the new ones.
+
+2) The backend-specific portions of the old monolithic cairo.h have
+   been split out into individual public header files. The new files
+   are:
+
+       cairo-atsui.h
+        cairo-ft.h
+        cairo-glitz.h
+        cairo-pdf.h
+        cairo-png.h
+        cairo-ps.h
+       cairo-quartz.h
+        cairo-xcb.h
+        cairo-xlib.h
+
+   Applications will need to be modified to explicitly include the new
+   header files where appropriate.
+
+3) There are two new graphics backends in this snapshot, a PDF
+   backend, and a Quartz backend. There is also one new font backend,
+   ATSUI.
+
+PDF backend
+-----------
+Kristian Høgsberg has contributed a new backend to allow cairo-based
+applications to generate PDF output. The interface for creating a PDF
+surface is similar to that of the PS backend, as can be seen in
+cairo-pdf.h:
+
+       void
+       cairo_set_target_pdf (cairo_t   *cr,
+                             FILE      *file,
+                             double    width_inches,
+                             double    height_inches,
+                             double    x_pixels_per_inch,
+                             double    y_pixels_per_inch);
+
+       cairo_surface_t *
+       cairo_pdf_surface_create (FILE          *file,
+                                 double        width_inches,
+                                 double        height_inches,
+                                 double        x_pixels_per_inch,
+                                 double        y_pixels_per_inch);
+
+Once a PDF surface has been created, applications can draw to it as
+any other cairo surface.
+
+This code is still a bit rough around the edges, and does not yet
+support clipping, surface patterns, or transparent gradients.  Text
+only works with TrueType fonts at this point and only black text is
+supported.  Also, the size of the generated PDF files is currently
+quite big.
+
+Kristian is still actively developing this backend, so watch this
+space for future progress.
+
+Quartz backend
+--------------
+Calum Robinson has contributed a new backend to allow cairo
+applications to target native Mac OS X windows through the Quartz
+API. Geoff Norton integrated this backend into the current
+configure-based build system, while Calum also provided Xcode build
+support in the separate "macosx" module available in CVS.
+
+The new interface, available in cairo-quartz.h, is as follows:
+
+       void
+       cairo_set_target_quartz_context (cairo_t        *cr,
+                                        CGContextRef   context,
+                                        int            width,
+                                        int            height);
+
+       cairo_surface_t *
+       cairo_quartz_surface_create (CGContextRef context,
+                                    int          width,
+                                    int          height);
+
+There is an example program available in CVS in cairo-demo/quartz. It
+is a port of Keith Packard's fdclock program originally written for
+the xlib backend. A screenshot of this program running on Mac OS X is
+available here:
+
+       http://cairographics.org/~cworth/images/fdclock-quartz.png
+
+ATSUI font backend
+------------------
+This new font backend complements the Quartz backend by allowing
+applications to use native font selection on Mac OS X. The interface
+is a single new function:
+
+       cairo_font_t *
+       cairo_atsui_font_create (ATSUStyle style);
+
+Minor API changes
+-----------------
+Prototype for non-existent function "cairo_ft_font_destroy" removed.
+
+Now depends on libpixman 0.1.2 or newer, (0.1.3 is being released
+concurrently and has some useful performance improvements).
+
+Default paint color is now opaque black, (was opaque white). Default
+background color is transparent (as before).
+
+Renamed "struct cairo" to "struct _cairo" to free up the word "cairo"
+from the C++ identifier name space.
+
+Functions returning multiple return values through provided pointers,
+(cairo_matrix_get_affine, cairo_current_point, and
+cairo_current_color_rgb), will now accept NULL for values the user
+wants to ignore.
+
+CAIRO_HAS_FREETYPE_FONT has now been renamed to CAIRO_HAS_FT_FONT.
+
+Performance improvements
+------------------------
+Alexander Larsson provided some fantastic performance improvements
+yielding a 10000% performance improvement in his application, (when
+also including his performance work in libpixman-0.1.3). These include
+
+ * Fixed handling of cache misses.
+
+ * Creating intermediate clip surfaces at the minimal size required.
+
+ * Eliminating roundtrips when creating intermediate Xlib surfaces.
+
+Implementation
+--------------
+Major re-work of font metrics system by Keith Packard. Font metrics
+should now be much more reliable.
+
+Glitz backend
+-------------
+Updated for glitz-0.3.0.
+Bug fixes in reference counting.
+
+Test suite
+----------
+New tests for cache crashing, rotating text, improper filling of
+complex polygons, and leaky rasterization.
+
+Bug fixes
+---------
+Fixed assertion failure when selecting the same font multiple times in
+sequence.
+
+Fixed reference counting so cache_destroy functions work.
+
+Remove unintended copyright statement from files generated with
+PostScript backend.
+
+Fixed to eliminate new warnings from gcc 3.4 and gcc 4.
+
+Snapshot 0.2.0 (2004-10-27 Carl Worth <cworth@cworth.org>)
+===========================================================
+New license: LGPL/MPL
+---------------------
+The most significant news with this release is that the license of
+cairo has changed. It is now dual-licensed under the LGPL and the
+MPL. For details see the COPYING file as well as COPYING-LGPL-2.1 and
+COPYING-MPL-1.1.
+
+I express my thanks to everyone involved in the license change process
+for their patience and support!
+
+New font and glyph internals
+----------------------------
+Graydon Hoare has put a tremendous amount of work into new internals
+for handling fonts and glyphs, including caches where appropriate.
+This work has no impact on the user-level API, but should result in
+great performance improvements for applications using text.
+
+New test suite
+--------------
+This snapshot of cairo includes a (small) test suite in
+cairo/test. The tests can be run with "make check". The test suite was
+designed to make it very easy to add new tests, and we hope to see
+many contributions here. As you find bugs, please try adding a minimal
+test case to the suite, and submit it with the bug report to the
+cairo@cairographics.org mailing list. This will make it much easier
+for us to track progress in fixing bugs.
+
+New name for glitz backend
+--------------------------
+The gl backend has now been renamed to the glitz backend. This means
+that the following names have changed:
+
+       CAIRO_HAS_GL_SURFACE    -> CAIRO_HAS_GLITZ_SURFACE
+       cairo_set_target_gl     -> cairo_set_target_glitz
+       cairo_gl_surface_create -> cairo_glitz_surface_create
+
+This change obviously breaks backwards compatibility for applications
+using the old gl backend.
+
+Up-to-date with latest glitz snapshots
+--------------------------------------
+This snapshot of cairo is now up to date with the latest glitz
+snapshot, (currently 0.2.3). We know that the latest cairo and glitz
+snapshots have been incompatible for a very long time. We've finally
+fixed that now and we're determined to not let that happen again.
+
+Revert some tessellation regression bugs
+----------------------------------------
+People that have been seeing some tessellation bugs, (eg. leaked
+fills), in the CVS version of cairo may have better luck with this
+release. A change since the last snapshot was identified to trigger
+some of these bugs and was reverted before making the snapshot. The
+behavior should be the same as the previous (0.1.23) snapshot.
+
+Miscellaneous changes
+---------------------
+Changed CAIRO_FILTER_DEFAULT to CAIRO_FILTER_BEST to make gradients
+easier.
+
+Track XCB API change regarding iterators.
+
+Various bug fixes
+-----------------
+Fix calculation of required number of vertices for pen.
+
+Fix to avoid zero-dimensioned pixmaps.
+
+Fix broken sort of pen vertices.
+
+Fix bug when cairo_show_text called with a NULL string.
+
+Fix clipping bugs.
+
+Fix bug in computing image length with XCB.
+
+Fix infinite loop bug in cairo_arc.
+
+Fix memory management interactions with libpixman.
+
+Snapshot 0.1.23 (2004-05-11 Carl Worth <cworth@isi.edu>)
+========================================================
+Fixes for gcc 3.4
+-----------------
+Fix prototype mismatches so that cairo can be built by gcc 3.4.
+
+Updates to track glitz
+----------------------
+Various fixes to support the latest glitz snapshot (0.1.2).
+
+Gradient updates
+----------------
+Radial gradients now support both inner and outer circles.
+Transformed linear gradients are now properly handled.
+Fixes for extend type reflect.
+
+Glitz updates
+-------------
+Converted shading routines to use fixed point values and introduced a
+shading operator structure for more efficient shading calculations.
+Support compositing with mask surface when mask is solid or
+multi-texturing is available.
+
+PNG backend cleanups
+--------------------
+Fix output to properly compensate for pre-multiplied alpha format in cairo.
+Add support for A8 and A1 image formats.
+
+Bug fixes
+---------
+Avoid crash or infinite loop on null strings and degeneratively short
+splines.
+
+New? bugs in cairo_clip
+-----------------------
+There are some fairly serious bugs in cairo_clip. It is sometimes
+causing an incorrect result. And even when it does work, it is
+sometimes so slow as to be unusable. Some of these bugs may not be
+new, (indeed cairo_clip has only ever had a braindead-slow
+implementation), but I think they're worth mentioning here.
+
+Snapshot 0.1.22 (2004-04-16 Carl Worth <cworth@isi.edu>)
+========================================================
+Cairo was updated to track the changes in libpixman, and now depends
+on libpixman version 0.1.1.
+
+Snapshot 0.1.21 (2004-04-09 David Reveman <c99drn@cs.umu.se>)
+=============================================================
+New OpenGL backend
+------------------
+The OpenGL backend provides hardware accelerated output for
+X11 and OS X. The significant new functions are:
+
+       cairo_set_target_gl
+       cairo_gl_surface_create
+
+Automatic detection of available backends
+-----------------------------------------
+The configure script now automatically detect what backends are
+available, (use ./configure --disable-`backend' to prevent
+compilation of specific backends).
+
+Snapshot 0.1.20 (2004-04-06 Carl Worth <cworth@isi.edu>)
+========================================================
+New pattern API
+---------------
+David Reveman has contributed a new pattern API which enable linear
+and radial gradient patterns in addition to the original surface-based
+patterns. The significant new top-level functions are:
+
+       cairo_pattern_create_linear
+       cairo_pattern_create_radial
+       cairo_pattern_create_for_surface
+       cairo_pattern_add_color_stop
+       cairo_set_pattern
+
+Any code using the old cairo_set_pattern, (which accepted a
+cairo_surface_t rather than a cairo_pattern_t), will need to be
+updated.
+
+Update to XCB backend
+---------------------
+The XCB backend is now enabled by default, (use ./configure
+--disable-xcb to turn it off).
+
+Faster clipping
+---------------
+Graydon Hoare has added optimizations that make cairo_clip much faster
+when the path is a pixel-aligned, rectangular region.
+
+Bug fixes.
+
+Snapshot 0.1.19 (2004-02-24 Carl Worth <cworth@isi.edu>)
+========================================================
+New PNG backend
+---------------
+Olivier Andrieu contributed a new PNG backend. It builds on the
+existing image backend to make it easy to render "directly" to a
+.png file. The user never needs to deal with the actual image
+buffer. The significant new functions are:
+
+       cairo_set_target_png
+       cairo_png_surface_create
+
+The PNG backend is not enabled by default so that by default there is
+not a new dependency on libpng. Use ./configure --enable-png to enable
+this backend.
+
+Snapshot 0.1.18 (2004-02-17 Carl Worth <cworth@isi.edu>)
+========================================================
+Path query functionality
+------------------------
+It's now possible to query the current path. The two new functions
+are:
+
+       cairo_current_path
+       cairo_current_path_flat
+
+Each function accepts a number of callback functions that will be
+called for each element in the path (move_to, line_to, curve_to,
+close_path). The cairo_current_path_flat function does not accept a
+curve_to callback. Instead, all curved portions of the path will be
+converted to line segments, (within the current tolerance value). This
+can be handy for doing things like text-on-path without having to
+manually interpolate Bézier splines.
+
+New XCB backend
+---------------
+Jamey Sharp has contributed a second X backend that uses the new, lean
+XCB library rather than Xlib. It cannot currently be compiled at the
+same time as the Xlib backend. See ./configure --enable-xcb.
+
+Build fixes for cygwin.
+
+Bug fixes.
+
+Snapshot 0.1.17 (2003-12-16 Carl Worth <cworth@isi.edu>)
+========================================================
+
+Better text support
+-------------------
+This snapshot provides much better text support by implementing the
+following four functions:
+
+        cairo_text_extents
+        cairo_glyph_extents
+        cairo_text_path
+        cairo_glyph_path
+
+The text/glyph_extents functions can be used to determine the bounding
+box (and advance) for text as if drawn by show_text/glyphs.
+
+The text/glyph_path objects functions place text shapes on the current
+path, where they can be subsequently manipulated. For example,
+following these functions with cairo_stroke allows outline text to be
+drawn. Calling cairo_clip allows clipping to a text-shaped region.
+
+Combined dependencies
+---------------------
+The cairo core now depends only on the libpixman library. This single
+library replaces the three previous libraries libic, libpixregion, and
+slim. Thanks to Dave Beckett <dave.beckett@bristol.ac.uk> for all of
+the heavy lifting with this renaming effort.
+
+Conditional compilation of backends
+-----------------------------------
+Cairo now allows optional backends to be disabled at compile time. The
+following options may now be passed to the configure script:
+
+       --disable-xlib
+       --disable-ps
+
+Note that the first option is a change from the old --without-x option
+which will no longer have any effect.
+
+OS X supported - several byte-order issues resolved
+---------------------------------------------------
+Cairo has now been successfully compiled under OS X. Testing revealed
+that there were some byte-order problems in the PostScript backend and
+the PNG generation in the demos. These have now been resolved.
+
+2003-10
+=======
+Graydon Hoare <graydon@redhat.com> implemented the first real text
+support using Freetype/fontconfig, (previous versions of cairo used
+Xft and could only draw text when using an X backend).
+
+2003-09
+=======
+Graydon Hoare <graydon@redhat.com> added the first real support for
+running cairo with a non-render-aware X server.
+
+Jamey Sharp <jamey@minilop.net> virtualized the backend font and
+surface interfaces in September, 2003.
+
+2003-06
+=======
+Xr is renamed cairo to avoid confusion since it no longer had a strict
+dependence on X.
+
+2003-05
+=======
+A new image surface backend is added to Xr. Keith Packard
+<keithp@keithp.com> wrote the image compositing code in libic that is
+used for the image_surface backend. This code was originally written
+as the software fallback for the render extension within the X
+server.
+
+2002-06
+=======
+Carl Worth <cworth@isi.edu> wrote the first lines of Xr, after Keith
+Packard <keithp@keithp.com> proposed the plan for a stateful drawing
+library in C providing a PostScript-like rendering model.
+
+ LocalWords:  mutex BeOS extraordinaire distro's URL lcd bool tarball
diff --git a/PORTING_GUIDE b/PORTING_GUIDE
new file mode 100644 (file)
index 0000000..7488173
--- /dev/null
@@ -0,0 +1,265 @@
+                   ...-----=======-----...
+                   Cairo 1.0 Porting Guide
+                   ...-----=======-----...
+
+Here are some notes on more easily porting cairo_code from cairo 0.4
+to cairo 1.0. It is sorted roughly in order of importance, (the items
+near the top are expected to affect the most people).
+
+Automated API renamings
+=======================
+There have been a lot of simple renamings where the functionality is
+the same but the name of the symbol is different. We have provided a
+script to automate the conversion of these symbols. It can be found
+within the cairo distribution in:
+
+       util/cairo-api-update
+
+This script is used by installing it somewhere on your PATH, and the
+running it and providing the names of your source files on the command
+line. For example:
+
+       cairo-api-update *.[ch]
+
+The script will first save backup copies of each file (renamed with a
+.bak extension) and then will perform all of the simple renamings.
+
+For your benefit, the script also produces messages giving filenames
+and line numbers for several of the manual API updates that you will
+need to perform as described below.
+
+
+Manual API changes
+==================
+This section of the porting guide describes changes you will have to
+manually make to your source code. In addition to the information in
+this guide, the cairo-api-update script will notify you of some of
+these issues as described above.
+
+Cairo's deprecation warnings
+----------------------------
+Also, if your compiler provides warnings for implicit declarations of
+functions, (eg. "gcc -Wall"), then simply attempting to compile your
+program will cause cairo to generate messages intended to guide you
+through the porting process.
+
+For example, if you neglect to update an old call to
+cairo_set_target_drawable, you might see an error message as follows:
+
+       foo.c:10: warning: implicit declaration of function
+        ‘cairo_set_target_drawable_DEPRECATED_BY_cairo_xlib_surface_create’
+
+This message is indicating to you that the deprecatd function
+cairo_set_target_drawable appears in your program foo.c on line 10,
+and you should rewrite your program to call cairo_xlib_surface_create
+instead.
+
+The remainder of this porting guide is arranged as a set of common
+code patterns that appear in old (cairo-0.4) code and how it should be
+transformed to new (cairo-0.5) code.
+
+cairo_create
+------------
+Was:   cr = cairo_create ();
+       cairo_set_target_foo (cr, args);
+       /* draw */
+       cairo_destroy (cr);
+
+Now:   cairo_surface_t *surface;
+
+       surface = cairo_foo_surface_create (args);
+       cr = cairo_create (surface);
+       /* draw */
+       cairo_destroy (cr);
+       cairo_surface_destroy (surface);
+
+Or:    cairo_surface_t *surface;
+
+       surface = cairo_foo_surface_create (args);
+       cr = cairo_create (surface);
+       cairo_surface_destroy (surface);
+       /* draw */
+       cairo_destroy (cr);
+
+NOTE: Many of the cairo_foo_surface_create functions accept the
+      identical arguments as the the old cairo_set_target_foo
+      functions, (minus the cairo_t*), making this transformation
+      quite easy. One notable exception is cairo_set_target_drawable
+      which, when it becomes cairo_xlib_surface_create must pickup new
+      arguments for the Visual*, the width, and the height.
+
+cairo_set_alpha (1)
+-------------------
+Was:   cairo_set_rgb_color (cr, red, green, blue);
+       cairo_set_alpha (cr, alpha);
+
+Now:   cairo_set_source_rgba (cr, red, green, blue, alpha);
+
+cairo_show_surface
+------------------
+Was:   cairo_show_surface (cr, surface, width, height);
+
+Now:   cairo_set_source_surface (cr, surface, x, y);
+       cairo_paint (cr);
+
+NOTE: The type signatures of cairo_show_surface and cairo_set_source
+      are the same, but pay attention that cairo_show_surface required
+      the width and height, while cairo_set_source_surface requires
+      the X,Y location to where the surface will be placed.
+
+cairo_set_alpha (2)
+-------------------
+Was:   cairo_set_alpha (cr, alpha);
+       cairo_show_surface (cr, surface, width, height);
+
+Now:   cairo_set_source_surface (cr, surface, x, y);
+       cairo_paint_with_alpha (cr, alpha);
+
+filling and stroking
+--------------------
+Was:   cairo_save (cr);
+       /* set fill color */
+       cairo_fiill (cr);
+       cairo_restore (cr);
+       /* set stroke color */
+       cairo_stroke (cr);
+
+Now:   /* set fill color */
+       cairo_fill_preserve (cr);
+       /* set stroke color */
+       cairo_stroke (cr);
+
+NOTE: The current path is no longer saved/restored by
+      cairo_save/cairo_restore. This can lead to some subtle
+      surprises, so look out.
+
+cairo_matrix_t
+--------------
+Was:   cairo_matrix_t *matrix;
+
+       matrix = cairo_matrix_create ();
+       /* Do stuff with matrix */
+       cairo_matrix_destroy (matrix);
+
+Now:   cairo_matrix_t matrix;
+       cairo_matrix_init_identity (&matrix);
+       /* Do stuff with &matrix */
+
+NOTE: If you are really lazy, you can still use a cairo_matrix_t* and
+      avoid putting the &matrix all over by just replacing
+      cairo_matrix_create() with malloc() and cairo_matrix_destroy()
+      with free(). That's not as nice, and you still need to be
+      careful to see if you need to initialize it to an identity
+      matrix as cairo_matrix_create() did for you.
+
+Rendering to a temporary surface
+--------------------------------
+Was:   cairo_save (cr);
+       {
+           cairo_set_target_surface (cr, temporary);
+           /* draw through cr onto temporary */
+       }
+       cairo_restore (cr);
+       /* use temporary as source on cr */
+
+Now:   {
+           cr2 = cairo_create (temporary);
+           /* draw through cr2 onto temporary */
+           cairo_destory (cr2);
+       }
+       /* use temporary as source on cr */
+
+NOTE: Having to create another cairo_t is a bit annoying, but having
+      to invent a new name for it is just awful, (imagine a deeply
+      nested version of this code). Fortunately, the style above is
+      just a stop-gap measure until the new group API comes along.
+
+Iterating over a path
+---------------------
+Was:   cairo_current_path (cr,
+                           my_move_to,
+                           my_line_to,
+                           my_curve_to,
+                           my_close_path,
+                           closure);
+
+Now:   int i;
+       cairo_path_t *path;
+       cairo_path_data_t *data;
+  
+       path = cairo_copy_path (cr);
+  
+       for (i=0; i < path->num_data; i += path->data[i].header.length) {
+           data = &path->data[i];
+           switch (data->header.type) {
+           case CAIRO_PATH_MOVE_TO:
+               my_move_to (closure, data[1].point.x, data[1].point.y);
+               break;
+           case CAIRO_PATH_LINE_TO:
+               my_line_to (closure, data[1].point.x, data[1].point.y);
+               break;
+           case CAIRO_PATH_CURVE_TO:
+               my_curve_to (closure, data[1].point.x, data[1].point.y,
+                            data[2].point.x, data[2].point.y,
+                            data[3].point.x, data[3].point.y);
+               break;
+           case CAIRO_PATH_CLOSE_PATH:
+               my_close_path (closure);
+               break;
+           }
+        }
+       cairo_path_destroy (path);
+
+NOTE: This version makes it looks like the new form is a _lot_ more
+      verbose than the old version. But realize that the old version
+      required the support of 4 additional functions. The new approach
+      allows great flexibility including the ability to inline the
+      entire operation within the switch statement when appropriate.
+
+Erasing a surface to transparent
+--------------------------------
+Was:   cairo_set_rgb_color (cr, 0., 0., 0.);
+       cairo_set_alpha (cr, 0.)
+       cairo_set_operator (cr, CAIRO_OPERATOR_SRC);
+       cairo_rectangle (cr, 0., 0., surface_width, surface_height);
+       cairo_fill (cr);
+
+    or:        cairo_set_rgb_color (cr, 0., 0., 0.);
+       cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
+       cairo_rectangle (cr, 0., 0., surface_width, surface_height);
+       cairo_fill (cr);
+
+Now:   cairo_set_source_rgba (cr, 0., 0., 0., 0.);
+       cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+       cairo_paint (cr);
+
+    or:        cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
+       cairo_paint (cr);
+
+NOTE: Using cairo_rectangle and fill would still work just fine. It's
+      just a lot more convenient to use cairo_paint now, (particularly
+      as it doesn't require you to even know what the bounds of the
+      target surface are).
+
+Drawing to a PNG file
+---------------------
+Was:   file = fopen (filename, "w");
+       cr = cairo_create ();
+       cairo_set_target_png (cr, file, format, width, height);
+       /* draw image */
+       cairo_destroy (cr);
+       fclose (file);
+
+Now:   surface = cairo_image_surface_create (format, width, height);
+       cr = cairo_create (surface);
+       /* draw image */
+       cairo_surface_write_to_png (surface, filename);
+       cairo_destroy (cr);
+       cairo_surface_destroy (surface);
+
+NOTE: The png backend is gone. So there is no cairo_png_surface_create
+      to take the place of cairo_set_target_png. And notice that we
+      used an image surface here, but it is just as easy to use
+      cairo_surface_write_to_png with an xlib or other surface, (but
+      not PDF at the moment). This is one of the big advantages of
+      this approach as opposed to a PNG surface.
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..ee7bd50
--- /dev/null
+++ b/README
@@ -0,0 +1,194 @@
+Cairo - Multi-platform 2D graphics library
+http://cairographics.org
+
+What is cairo
+=============
+Cairo is a 2D graphics library with support for multiple output
+devices. Currently supported output targets include the X Window
+System, quartz, win32, and image buffers, as well as PDF, PostScript,
+and SVG file output. Experimental backends include OpenGL, XCB, BeOS,
+OS/2, and DirectFB.
+
+Cairo is designed to produce consistent output on all output media
+while taking advantage of display hardware acceleration when available
+(for example, through the X Render Extension).
+
+The cairo API provides operations similar to the drawing operators of
+PostScript and PDF. Operations in cairo include stroking and filling
+cubic Bézier splines, transforming and compositing translucent images,
+and antialiased text rendering. All drawing operations can be
+transformed by any affine transformation (scale, rotation, shear,
+etc.).
+
+Cairo has been designed to let you draw anything you want in a modern
+2D graphical user interface.  At the same time, the cairo API has been
+designed to be as fun and easy to learn as possible. If you're not
+having fun while programming with cairo, then we have failed
+somewhere---let us know and we'll try to fix it next time around.
+
+Cairo is free software and is available to be redistributed and/or
+modified under the terms of either the GNU Lesser General Public
+License (LGPL) version 2.1 or the Mozilla Public License (MPL) version
+1.1.
+
+Where to get more information about cairo
+=========================================
+The primary source of information about cairo is:
+
+       http://cairographics.org/
+
+The latest versions of cairo can always be found at:
+
+       http://cairographics.org/download
+
+Documentation on using cairo and frequently-asked questions:
+
+       http://cairographics.org/documentation
+       http://cairographics.org/FAQ
+
+Mailing lists for contacting cairo users and developers:
+
+       http://cairographics.org/lists
+
+Roadmap and unscheduled things to do, (please feel free to help out):
+
+       http://cairographics.org/roadmap
+       http://cairographics.org/todo
+
+Dependencies
+============
+The set of libraries needed to compile cairo depends on which backends
+are enabled when cairo is configured. So look at the list below to
+determine which dependencies are needed for the backends of interest.
+
+For the surface backends, we have both "supported" and "experimental"
+backends. Further, the supported backends can be divided into the
+"standard" backends which can be easily built on any platform, and the
+"platform" backends which depend on some underlying platform-specific
+system, (such as the X Window System or some other window system).
+
+As an example, for a standard Linux build, (with image, png, pdf,
+PostScript, svg, and xlib surface backends, and the freetype font
+backend), the following sample commands will install necessary
+dependencies:
+
+    Debian (and similar):
+
+       apt-get install libpng12-dev libz-dev libxrender-dev libfontconfig1-dev
+
+    Fedora (and similar):
+
+       yum install libpng-devel zlib-devel libXrender-devel fontconfig-devel
+
+(Those commands intentionally don't install pixman from a distribution
+package since if you're manually compiling cairo, then you likely want
+to grab pixman from the same place at the same time and compile it as
+well.)
+
+Supported, "standard" surface backends
+------------------------------------
+       image backend (required)
+       ------------------------
+       pixman >= 0.20.2        http://cairographics.org/releases
+
+       png support (can be left out if desired, but many
+       -----------  applications expect it to be present)
+       libpng                  http://www.libpng.org/pub/png/libpng.html
+
+       pdf backend
+       -----------
+       zlib                    http://www.gzip.org/zlib
+
+       postscript backend
+       ------------------
+       zlib                    http://www.gzip.org/zlib
+
+       svg backend
+       -----------
+       [none]
+
+Supported, "platform" surface backends
+-----------------------------------
+       xlib backend
+       ------------
+       X11                     http://freedesktop.org/Software/xlibs
+
+       xlib-xrender backend
+       --------------------
+       Xrender >= 0.6          http://freedesktop.org/Software/xlibs
+
+       quartz backend
+       --------------
+       MacOS X >= 10.4 with Xcode >= 2.4
+
+       win32 backend
+       -------------
+       Microsoft Windows 2000 or newer[*].
+
+Font backends (required to have at least one)
+---------------------------------------------
+       freetype font backend
+       ---------------------
+       freetype >= 2.1.9       http://freetype.org
+       fontconfig              http://fontconfig.org
+
+       quartz-font backend
+       -------------------
+       MacOS X >= 10.4 with Xcode >= 2.4
+
+       win32 font backend
+       ------------------
+       Microsoft Windows 2000 or newer[*].
+
+       [*] The Win32 backend should work on Windows 2000 and newer
+           (excluding Windows Me.) Most testing has been done on
+           Windows XP. While some portions of the code have been
+           adapted to work on older versions of Windows, considerable
+           work still needs to be done to get cairo running in those
+           environments.
+
+           Cairo can be compiled on Windows with either the gcc
+           toolchain (see http://www.mingw.org) or with Microsoft
+           Visual C++.  If the gcc toolchain is used, the standard
+           build instructions using configure apply, (see INSTALL).
+           If Visual C++ is desired, GNU make is required and
+           Makefile.win32 can be used via 'make -f Makefile.win32'.
+           The compiler, include paths, and library paths must be set
+           up correctly in the environment.
+
+           MSVC versions earlier than 7.1 are known to miscompile
+           parts of cairo and pixman, and so should be avoided. MSVC
+           7.1 or later, including the free Microsoft Visual Studio
+           Express editions, produce correct code.
+
+Experimental surface backends
+-----------------------------
+       xcb backend
+       -----------
+       XCB                     http://xcb.freedesktop.org
+
+       beos backend
+       ------------
+       No dependencies in itself other than an installed BeOS system, but cairo
+       requires a font backend. See the freetype dependency list.
+
+       os2 backend
+       -----------
+       Cairo should run on any recent version of OS/2 or eComStation, but it
+       requires a font backend. See the freetype dependency list. Ready to use
+       packages and developer dependencies are available at Netlabs:
+                               ftp://ftp.netlabs.org/pub/cairo
+
+Compiling
+=========
+See the INSTALL document for build instructions.
+
+History
+=======
+Cairo was originally developed by Carl Worth <cworth@cworth.org> and
+Keith Packard <keithp@keithp.com>. Many thanks are due to Lyle Ramshaw
+without whose patient help our ignorance would be much more apparent.
+
+Since the original development, many more people have contributed to
+cairo. See the AUTHORS files for as complete a list as we've been able
+to compile so far.
diff --git a/README.win32 b/README.win32
new file mode 100644 (file)
index 0000000..ff962b7
--- /dev/null
@@ -0,0 +1,66 @@
+Building Cairo on Windows
+=========================
+There are two primary ways to build Cairo on Windows. You can use a
+UNIX emulation based setup, such as Cygwin or MSYS, with the
+conventional configure script shipped with Cairo releases. In this
+configuration, you will build with GCC and (implicitly) libtool. In
+the Cygwin case you end up with a DLL that depends on Cygwin and
+should be used only from Cygwin applications. In the MSYS case you end
+up with a "normal" Win32 DLL that can be used either from GCC- or
+Microsoft Visual C++-compiled code. In theory, this technique is no
+different than the ordinary build process for the Cairo library. In
+practise there are lots of small details that can go wrong.
+
+The second way is to use a GNU-compatible make, but build using
+Microsoft's Visual C++ compiler to produce native libraries.  This is
+the setup this README.win32 is written for. Also the DLL produced this
+way is usable either from GCC- or MSVC-compiled code.
+
+Tools required
+==============
+You will need GNU make, version 3.80 or later.  Earlier versions or
+other modern make implementations may work, but are not guaranteed to.
+
+You will also need Microsoft Visual C++.  Version 7 has been most
+heavily tested, but other versions are likely to work fine.
+
+Libraries required
+==================
+Cairo requires a compatible version of the pixman library.  Full build
+instructions are beyond the scope of this document; however, using the
+same tools, it should be possible to build pixman simply by entering
+the pixman/src directory and typing:
+
+    make -f Makefile.win32 CFG=release
+
+Depending on your feature set, you may also need zlib and libpng.
+
+Building
+========
+There are a few files that you will need to edit.  First, you must
+determine which features will be built.  Edit
+build/Makefile.win32.features and set the features as desired.  Note
+that most features have external dependencies; specifically,
+CAIRO_HAS_PNG_FUNCTIONS requires libpng to be present, and
+CAIRO_HAS_PS_SURFACE and CAIRO_HAS_PDF_SURFACE both require zlib.
+
+To ensure that the compiler can find all dependencies, you may need to
+edit build/Makefile.win32.common.  In particular, ensure that
+PIXMAN_CFLAGS contains a -I parameter pointing to the location of
+your pixman header files and that PIXMAN_LIBS points to the actual
+location of your pixman-1.lib file.  You may also need to edit the
+various occurrences of CAIRO_LIBS to point to other libraries
+correctly.  Note also that if you wish to link statically with zlib,
+you should replace zdll.lib with zlib.lib.
+
+Finally, from the top Cairo directory, type:
+
+    make -f Makefile.win32 CFG=release
+
+If this command succeeds, you will end up with src/release/cairo.dll.
+To successfully use Cairo from your own programs, you will probably
+want to move this file to some central location.  You will also
+probably want to copy the Cairo header files.  These should be placed
+in a cairo subdirectory (for instance, c:/code/common/include/cairo).
+The exact set to copy depends on your features and is reported to you
+at the end of the build.
diff --git a/RELEASING b/RELEASING
new file mode 100644 (file)
index 0000000..a1edceb
--- /dev/null
+++ b/RELEASING
@@ -0,0 +1,140 @@
+Here are the steps to follow to create a new cairo release:
+
+1) Ensure that there are no local, uncommitted/unpushed
+   modifications. You're probably in a good state if both "git diff
+   HEAD" and "git log master..origin/master" give no output.
+
+2) Verify that the code passes "make distcheck"
+
+       First, make sure you have 'nm' and 'readelf' commands in PATH.
+       this should be OK with any Linux distro.
+
+       Running "make distcheck" should result in no warnings or
+       errors and end with a message of the form:
+
+       =============================================
+       cairo-X.Y.Z archives ready for distribution:
+       cairo-X.Y.Z.tar.gz
+       =============================================
+
+       (But the tar file isn't actually ready yet, as we still have
+       some more steps to follow).
+
+       Note that it's allowed (and perhaps recommended) to run the
+       "make distcheck" step against an all-software X server such as
+       Xvfb to avoid getting tripped up by any X-server-driver-specific
+       bugs. See test/README for details
+
+       If you get errors about local PLT entries, you get the list of
+       cairo entries with the error.  For each of these, a call to
+       slim_hidden_def and slim_hidden_proto is needed in the cairo
+       implementation in the style of other similar calls.
+
+       In the unfortunate case that you have to push a snapshot out
+       (note, I said snapshot, not release) without the entire test
+       suite passing, here's the magic env vars to set when doing
+       'make distcheck' and 'make release-publish' that will let you
+       get away with it.  At any cost, never ever release without
+       (implied) distchecking.  Every time we got around it, it turned
+       out to be a disaster.  Anyway, here's the pass code:
+
+               DISPLAY= CAIRO_TEST_TARGET=" "
+
+3) Fill out an entry in the NEWS file
+
+       Sift through the logs since the last release. This is most
+       easily done with a command such as:
+
+               git log --stat X.Y.Z..
+
+       where X.Y.Z is the previous release version.
+
+       Summarize major changes briefly in a style similar to other
+       entries in NEWS. Take special care to note any additions in
+       the API. These should be easy to find by noting modifications
+       to .h files in the log command above. And more specifically,
+       the following command will show each patch that has changed a
+       public header file since the given version:
+
+               find src/ -name '*.h' ! -name '*-private.h' ! -name 'cairoint.h' ! -name 'cairo-*features*.h' | \
+               xargs git diff X.Y.Z.. --
+
+4) Increment cairo_version_{minor|micro} in cairo-version.h:
+
+       If there are backward-incompatible changes in the API, stop
+       now and don't release. Go back and fix the API instead. Cairo
+       is intended to remain backwards-compatible as far as API.
+
+       So cairo_version_major will not be incremented unless we come
+       up with a new versioning scheme to take advantage of it.
+
+       If there are API additions, then increment cairo_version_minor
+       and reset cairo_version_micro to 0. NOTE: The minor version is
+       only incremented for releases, not for snapshots.
+
+       Otherwise, (i.e. there are only bug fixes), increment
+       cairo_version_micro to the next larger (even) number.
+
+5) Commit the changes to NEWS and cairo-version.h
+
+       It's especially important to mention the new version number in your
+       commit log.
+
+6) Run "make release-publish" which will perform the following steps
+   for you:
+
+       * Generate ChangeLog files out of git repository
+       * Check that ChangeLog files were generated properly
+       * Check that the version number ends with an even micro component
+       * Check that no release exists with the current version
+       * Verify that make distcheck completes successfully
+       * Generate the final tar file
+       * Generate an sha1sum file
+       * Sign the sha1sum using your GPG setup (asks for your GPG password)
+       * scp the three files to appear on http://cairographics.org/releases
+       * Generate a versioned manual and upload it to appear as both:
+         http://cairographics.org/manual-X.Y.Z
+         http://cairographics.org/manual
+       * Place local copies of the three files in the releases directory
+       * Create a LATEST-package-version file (after deleting any old one)
+       * Tag the entire source tree with a tag of the form X.Y.Z, and sign
+         the tag with your GPG key (asks for your GPG password, and you
+         may need to set GIT_COMMITTER_NAME and GIT_COMMITTER_EMAIL to match
+         your public-key's setting or this fails.)
+       * Provide some text for the release announcement (see below).
+         If for some reason you lost this message, "make release-publish-message"
+         prints it for you.
+
+7) Increment cairo_version_micro to the next larger (odd) number in
+   cairo-version.h, commit, and push.
+
+8) Push the newly created tag out to the central tree with a command
+   something like:
+
+       git push cairo X.Y.Z
+
+9) Edit the cairo bugzilla product and add the new version numbers. Note
+   that you need to add two versions.  One for the release/snapshot (with
+   an even micro version), another with the post-release version (with an
+   odd micro version).
+
+10) Send a message to cairo-announce@cairographics.org and CC
+    gnome-announce-list@gnome.org and ftp-release@lists.freedesktop.org
+    (pr@lwn.net as well for major releases) to announce the new release
+    using the text provided from "make release-publish", adding the excerpt
+    from NEWS, your signature, followed by the standard "What is cairo" and
+    "Where to get more information about cairo" blurbs from README, and
+    finally the shortlog of all changes since last release, generated by:
+
+       git shortlog X.Y.Z...
+
+    where X.Y.Z is the last released version.
+
+11) Edit the cairo wiki to add the announcement to the NEWS page and
+    the front page. (just the parts before your signature).
+
+12) For minor releases (no X.Y change), notify desktop-devel-list@gnome.org
+    or update the ExternalDependencies page for the current cycle if you
+    know where it is.  Currently it's:
+
+       http://live.gnome.org/TwoPointNineteen/ExternalDependencies
diff --git a/acinclude.m4 b/acinclude.m4
new file mode 100644 (file)
index 0000000..dcf54f9
--- /dev/null
@@ -0,0 +1,55 @@
+dnl -*- mode: autoconf -*-
+
+dnl [m4_newline] didn't appear until autoconf 2.62
+m4_ifdef([m4_newline],,[m4_define([m4_newline],[
+])])
+
+dnl These are not available in autoconf 2.59
+
+m4_ifdef([m4_foreach_w],,[m4_define([m4_foreach_w],
+[m4_foreach([$1], m4_split(m4_normalize([$2]), [ ]), [$3])])])
+
+m4_ifdef([AS_CASE],,[
+m4_define([_AS_CASE],
+[m4_if([$#], 0, [m4_fatal([$0: too few arguments: $#])],
+       [$#], 1, [  *) $1 ;;],
+       [$#], 2, [  $1) m4_default([$2], [:]) ;;],
+       [  $1) m4_default([$2], [:]) ;;
+$0(m4_shift2($@))])dnl
+])
+m4_defun([AS_CASE],
+[m4_ifval([$2$3],
+[case $1 in
+_AS_CASE(m4_shift($@))
+esac
+])dnl
+])# AS_CASE
+])
+
+m4_ifdef([m4_shift2],, [m4_define([m4_shift2], [m4_shift(m4_shift($@))])])
+
+
+dnl ==========================================================================
+
+dnl This has to be in acinclude.m4 as it includes other files
+
+dnl Parse Version.mk and declare m4 variables out of it
+m4_define([CAIRO_PARSE_VERSION],dnl
+               m4_translit(dnl
+               m4_bpatsubst(m4_include(cairo-version.h),
+                            [^.define \([a-zA-Z0-9_]*\)  *\([0-9][0-9]*\)],
+                            [[m4_define(\1, \2)]]),
+                           [A-Z], [a-z])dnl
+)dnl
+
+dnl ==========================================================================
+
+m4_pattern_forbid([^cr_])
+
+dnl AC_AUTOCONF_VERSION was introduced in 2.62, so its definition works as
+dnl a conditional on version >= 2.62.  Older versions did not call
+dnl m4_pattern_allow from AC_DEFINE and friends.  To avoid lots of warnings we
+dnl only forbid CAIRO_ if autoconf is recent enough.
+m4_ifdef([AC_AUTOCONF_VERSION],
+[m4_pattern_forbid([CAIRO])],
+[m4_pattern_forbid([_CAIRO])])
diff --git a/autogen.sh b/autogen.sh
new file mode 100755 (executable)
index 0000000..10c2832
--- /dev/null
@@ -0,0 +1,197 @@
+#!/bin/sh
+# Run this to generate all the initial makefiles, etc.
+set -e
+
+ARGV0=$0
+
+# Allow invocation from a separate build directory; in that case, we change
+# to the source directory to run the auto*, then change back before running configure
+srcdir=`dirname $ARGV0`
+test -z "$srcdir" && srcdir=.
+
+ORIGDIR=`pwd`
+cd $srcdir
+
+PACKAGE=cairo
+
+LIBTOOLIZE_FLAGS="--copy --force --automake"
+ACLOCAL_FLAGS="-I build"
+AUTOHEADER=${AUTOHEADER-autoheader}
+GTKDOCIZE_FLAGS="--copy"
+GTKDOCIZE=${GTKDOCIZE-gtkdocize}
+AUTOMAKE_FLAGS="--add-missing --gnu -Wall"
+AUTOCONF=${AUTOCONF-autoconf}
+
+CONFIGURE_AC=
+test -f configure.ac && CONFIGURE_AC=configure.ac
+
+if test "X$CONFIGURE_AC" = X; then
+  echo "$ARGV0: ERROR: No $srcdir/configure.in or $srcdir/configure.ac found."
+  exit 1
+fi
+
+extract_version() {
+       grep "^ *$1" "$CONFIGURE_AC" | sed 's/.*(\[*\([^])]*\)]*).*/\1/'
+}
+
+autoconf_min_vers=`extract_version AC_PREREQ`
+automake_min_vers=`extract_version AM_INIT_AUTOMAKE`
+libtoolize_min_vers=`extract_version AC_PROG_LIBTOOL`
+aclocal_min_vers=$automake_min_vers
+
+
+# Not all echo versions allow -n, so we check what is possible. This test is
+# based on the one in autoconf.
+case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in
+  *c*,-n*) ECHO_N= ;;
+  *c*,*  ) ECHO_N=-n ;;
+  *)       ECHO_N= ;;
+esac
+
+
+# some terminal codes ...
+boldface="`tput bold 2>/dev/null || true`"
+normal="`tput sgr0 2>/dev/null || true`"
+printbold() {
+    echo $ECHO_N "$boldface"
+    echo "$@"
+    echo $ECHO_N "$normal"
+}
+printerr() {
+    echo "$@" >&2
+}
+
+
+# Usage:
+#     compare_versions MIN_VERSION ACTUAL_VERSION
+# returns true if ACTUAL_VERSION >= MIN_VERSION
+compare_versions() {
+    ch_min_version=$1
+    ch_actual_version=$2
+    ch_status=0
+    IFS="${IFS=         }"; ch_save_IFS="$IFS"; IFS="."
+    set $ch_actual_version
+    for ch_min in $ch_min_version; do
+        ch_cur=`echo $1 | sed 's/[^0-9].*$//'`; # remove letter suffixes
+        if [ $# -gt 0 ]; then shift; fi
+        if [ -z "$ch_min" ]; then break; fi
+        if [ -z "$ch_cur" ]; then ch_status=1; break; fi
+        if [ $ch_cur -gt $ch_min ]; then break; fi
+        if [ $ch_cur -lt $ch_min ]; then ch_status=1; break; fi
+    done
+    IFS="$ch_save_IFS"
+    return $ch_status
+}
+
+# Usage:
+#     version_check PACKAGE VARIABLE CHECKPROGS MIN_VERSION SOURCE
+# checks to see if the package is available
+version_check() {
+    vc_package=$1
+    vc_variable=$2
+    vc_checkprogs=$3
+    vc_min_version=$4
+    vc_source=$5
+    vc_status=1
+
+    vc_checkprog=`eval echo "\\$$vc_variable"`
+    if [ -n "$vc_checkprog" ]; then
+       printbold "using $vc_checkprog for $vc_package"
+       return 0
+    fi
+
+    printbold "checking for $vc_package >= $vc_min_version..."
+    for vc_checkprog in $vc_checkprogs; do
+       echo $ECHO_N "  testing $vc_checkprog... "
+       if $vc_checkprog --version < /dev/null > /dev/null 2>&1; then
+           vc_actual_version=`$vc_checkprog --version | head -n 1 | \
+                               sed 's/^.*[     ]\([0-9.]*[a-z]*\).*$/\1/'`
+           if compare_versions $vc_min_version $vc_actual_version; then
+               echo "found $vc_actual_version"
+               # set variable
+               eval "$vc_variable=$vc_checkprog"
+               vc_status=0
+               break
+           else
+               echo "too old (found version $vc_actual_version)"
+           fi
+       else
+           echo "not found."
+       fi
+    done
+    if [ "$vc_status" != 0 ]; then
+       printerr "***Error***: You must have $vc_package >= $vc_min_version installed"
+       printerr "  to build $PROJECT.  Download the appropriate package for"
+       printerr "  from your distribution or get the source tarball at"
+        printerr "    $vc_source"
+       printerr
+    fi
+    return $vc_status
+}
+
+
+version_check autoconf AUTOCONF $AUTOCONF $autoconf_min_vers \
+    "http://ftp.gnu.org/pub/gnu/autoconf/autoconf-${autoconf_min_vers}.tar.gz" || DIE=1
+
+#
+# Hunt for an appropriate version of automake and aclocal; we can't
+# assume that 'automake' is necessarily the most recent installed version
+# 
+# We check automake first to allow it to be a newer version than we know about.
+#
+version_check automake AUTOMAKE "$AUTOMAKE automake automake-1.10 automake-1.9 automake-1.8 automake-1.7" $automake_min_vers \
+    "http://ftp.gnu.org/pub/gnu/automake/automake-${automake_min_vers}.tar.gz" || DIE=1
+ACLOCAL=`echo $AUTOMAKE | sed s/automake/aclocal/`
+
+
+version_check libtool LIBTOOLIZE "$LIBTOOLIZE glibtoolize libtoolize" $libtoolize_min_vers \
+    "http://ftp.gnu.org/pub/gnu/libtool/libtool-${libtool_min_vers}.tar.gz" || DIE=1
+
+if test -n "$DIE"; then
+  exit 1
+fi
+
+
+if test -z "$*"; then
+  echo "$ARGV0:        Note: \`./configure' will be run with no arguments."
+  echo "               If you wish to pass any to it, please specify them on the"
+  echo "               \`$0' command line."
+  echo
+fi
+
+do_cmd() {
+    echo "$ARGV0: running \`$@'"
+    $@
+}
+
+do_cmd $LIBTOOLIZE $LIBTOOLIZE_FLAGS
+
+do_cmd $ACLOCAL $ACLOCAL_FLAGS
+
+do_cmd $AUTOHEADER
+
+# create dummy */Makefile.am.features and ChangeLog to make automake happy
+> boilerplate/Makefile.am.features
+> src/Makefile.am.features
+touch ChangeLog
+
+# We don't call gtkdocize right now.  When we do, we should then modify
+# the generated gtk-doc.make and move it to build/Makefile.am.gtk-doc.
+# See that file for details.
+#do_cmd $GTKDOCIZE $GTKDOCIZE_FLAGS
+
+do_cmd $AUTOMAKE $AUTOMAKE_FLAGS
+
+do_cmd $AUTOCONF
+
+cd "$ORIGDIR" || exit 1
+
+rm -f config.cache
+
+if test -z "$NOCONFIGURE"; then
+  do_cmd $srcdir/configure \
+       --cache-file=config.cache \
+       --disable-static \
+       --enable-test-surfaces \
+       ${1+"$@"} && echo "Now type \`make' to compile $PROJECT." || exit 1
+fi
diff --git a/boilerplate/.gitignore b/boilerplate/.gitignore
new file mode 100644 (file)
index 0000000..a81663b
--- /dev/null
@@ -0,0 +1,24 @@
+TAGS
+tags
+Makefile
+Makefile.in
+Makefile.am.features
+#Makefile.win32.features
+*.lo
+*.la
+*.exe
+*.manifest
+*.o
+*.gcda
+*.gcno
+*.obj
+*.ilk
+*.suo
+*.lib
+*.pdb
+*~
+.*.sw?
+check-link
+cairo-boilerplate-constructors.c
+cairo-boilerplate-constructors
+make-cairo-boilerplate-constructors
diff --git a/boilerplate/Makefile.am b/boilerplate/Makefile.am
new file mode 100644 (file)
index 0000000..29ad015
--- /dev/null
@@ -0,0 +1,76 @@
+# Note: All source files are listed in Makefile.sources.
+
+include $(top_srcdir)/build/Makefile.am.common
+include $(srcdir)/Makefile.am.features
+
+EXTRA_DIST += Makefile.win32      Makefile.win32.features
+#MAINTAINERCLEANFILES += $(srcdir)/Makefile.win32.features
+
+AM_CPPFLAGS = \
+       -I$(srcdir) \
+       -I$(top_builddir)/src \
+       -I$(top_srcdir)/src \
+       $(CAIRO_CFLAGS) \
+       $(NULL)
+AM_LDFLAGS = $(CAIRO_LDFLAGS)
+
+if BUILD_CXX
+cxx_boilerplate_lib = libcairoboilerplate_cxx.la
+else
+cxx_boilerplate_lib =
+endif
+
+EXTRA_LTLIBRARIES += libcairoboilerplate.la $(cxx_boilerplate_lib)
+
+
+libcairoboilerplate_la_SOURCES = \
+       $(enabled_cairo_boilerplate_headers) \
+       $(enabled_cairo_boilerplate_private) \
+       $(enabled_cairo_boilerplate_sources) \
+       cairo-boilerplate-constructors.c \
+       $(NULL)
+libcairoboilerplate_cxx_la_SOURCES = \
+       $(enabled_cairo_boilerplate_cxx_sources) \
+       $(NULL)
+libcairoboilerplate_la_LIBADD = $(top_builddir)/src/libcairo.la \
+       $(cxx_boilerplate_lib) \
+       $(CAIRO_LIBS) \
+       $(CAIROBOILERPLATE_LIBS) \
+       $(NULL)
+libcairoboilerplate_cxx_la_LIBADD = $(top_builddir)/src/libcairo.la \
+       $(CAIRO_LIBS) \
+       $(CAIROBOILERPLATE_LIBS) \
+       $(NULL)
+libcairoboilerplate_la_DEPENDENCIES = \
+       $(cxx_boilerplate_lib) \
+       $(NULL)
+
+if CAIRO_HAS_DL
+libcairoboilerplate_la_LIBADD += -ldl
+endif
+
+if CAIRO_HAS_BEOS_SURFACE
+# BeOS system headers trigger this warning
+libcairoboilerplate_cxx_la_CXXFLAGS = -Wno-multichar
+endif
+
+if CAIRO_HAS_WIN32_SURFACE
+libcairoboilerplate_la_LIBADD += -lwinspool
+endif
+
+cairo-boilerplate-constructors.c: Makefile $(enabled_cairo_boilerplate_sources) $(enabled_cairo_boilerplate_cxx_sources) make-cairo-boilerplate-constructors.sh
+       (cd $(srcdir) && sh ./make-cairo-boilerplate-constructors.sh $(enabled_cairo_boilerplate_sources) $(enabled_cairo_boilerplate_cxx_sources)) > $@
+
+BUILT_SOURCES += cairo-boilerplate-constructors.c
+EXTRA_DIST += $(BUILT_SOURCES) make-cairo-boilerplate-constructors.sh
+CLEANFILES += $(BUILT_SOURCES)
+
+test: check
+
+if CROSS_COMPILING
+else
+TESTS += check-link$(EXEEXT)
+endif
+
+check_PROGRAMS += check-link
+check_link_LDADD = libcairoboilerplate.la
diff --git a/boilerplate/Makefile.sources b/boilerplate/Makefile.sources
new file mode 100644 (file)
index 0000000..63ef34e
--- /dev/null
@@ -0,0 +1,40 @@
+# Makefile.sources
+#
+# This file is pretty similar to $(top_srcdir)/src/Makefile.sources,
+# but for boilerplate.  Unlike that file, there are no special headers.
+#
+
+cairo_boilerplate_headers = \
+       cairo-boilerplate-getopt.h \
+       cairo-boilerplate-scaled-font.h \
+       cairo-boilerplate-system.h \
+       cairo-boilerplate.h \
+       $(NULL)
+cairo_boilerplate_sources = \
+       cairo-boilerplate-getopt.c \
+       cairo-boilerplate-system.c \
+       cairo-boilerplate.c \
+       $(NULL)
+cairo_boilerplate_private = \
+       cairo-boilerplate-private.h \
+       $(NULL)
+
+cairo_boilerplate_beos_cxx_sources = cairo-boilerplate-beos.cpp
+cairo_boilerplate_directfb_sources = cairo-boilerplate-directfb.c
+cairo_boilerplate_drm_sources = cairo-boilerplate-drm.c
+cairo_boilerplate_glx_sources = cairo-boilerplate-glx.c
+cairo_boilerplate_wgl_sources = cairo-boilerplate-wgl.c
+cairo_boilerplate_egl_sources = cairo-boilerplate-egl.c
+cairo_boilerplate_pdf_sources = cairo-boilerplate-pdf.c
+cairo_boilerplate_ps_sources = cairo-boilerplate-ps.c
+cairo_boilerplate_qt_cxx_sources = cairo-boilerplate-qt.cpp
+cairo_boilerplate_quartz_sources = cairo-boilerplate-quartz.c
+cairo_boilerplate_script_sources = cairo-boilerplate-script.c
+cairo_boilerplate_skia_sources = cairo-boilerplate-skia.c
+cairo_boilerplate_svg_sources = cairo-boilerplate-svg.c
+cairo_boilerplate_test_surfaces_sources = cairo-boilerplate-test-surfaces.c
+cairo_boilerplate_win32_sources = cairo-boilerplate-win32.c cairo-boilerplate-win32-printing.c
+cairo_boilerplate_xcb_sources = cairo-boilerplate-xcb.c
+cairo_boilerplate_xlib_headers = cairo-boilerplate-xlib.h
+cairo_boilerplate_xlib_sources = cairo-boilerplate-xlib.c
+cairo_boilerplate_vg_sources = cairo-boilerplate-vg.c
diff --git a/boilerplate/Makefile.win32 b/boilerplate/Makefile.win32
new file mode 100644 (file)
index 0000000..29df5cf
--- /dev/null
@@ -0,0 +1,24 @@
+top_srcdir = ..
+include $(top_srcdir)/build/Makefile.win32.common
+include Makefile.win32.features
+
+HEADERS = \
+       $(enabled_cairo_boilerplate_headers) \
+       $(enabled_cairo_boilerplate_private) \
+       $(NULL)
+
+SOURCES = \
+       $(enabled_cairo_boilerplate_sources) \
+       cairo-boilerplate-constructors.c \
+       $(NULL)
+
+OBJECTS = $(patsubst %.c, $(CFG)/%-static.obj, $(SOURCES))
+
+cairo-boilerplate-constructors.c: Makefile.sources Makefile.win32 $(enabled_cairo_boilerplate_sources) make-cairo-boilerplate-constructors.sh
+       sh ./make-cairo-boilerplate-constructors.sh $(enabled_cairo_boilerplate_sources) > $@
+
+all: $(CFG)/boiler.lib
+
+
+$(CFG)/boiler.lib: $(OBJECTS)
+       @$(AR) $(CAIRO_ARFLAGS) -OUT:$@ $(OBJECTS)
diff --git a/boilerplate/Makefile.win32.features b/boilerplate/Makefile.win32.features
new file mode 100644 (file)
index 0000000..8b6a673
--- /dev/null
@@ -0,0 +1,511 @@
+# Generated by configure.  Do not edit.
+
+ifeq ($(top_srcdir),)
+include Makefile.sources
+else
+include $(top_srcdir)/boilerplate/Makefile.sources
+endif
+
+supported_cairo_boilerplate_headers = $(cairo_boilerplate_headers)
+unsupported_cairo_boilerplate_headers =
+all_cairo_boilerplate_headers = $(cairo_boilerplate_headers)
+all_cairo_boilerplate_private = $(cairo_boilerplate_private)
+all_cairo_boilerplate_cxx_sources = $(cairo_boilerplate_cxx_sources)
+all_cairo_boilerplate_sources = $(cairo_boilerplate_sources)
+
+enabled_cairo_boilerplate_headers = $(cairo_boilerplate_headers)
+enabled_cairo_boilerplate_private = $(cairo_boilerplate_private)
+enabled_cairo_boilerplate_cxx_sources = $(cairo_boilerplate_cxx_sources)
+enabled_cairo_boilerplate_sources = $(cairo_boilerplate_sources)
+
+
+supported_cairo_boilerplate_headers += $(cairo_boilerplate_xlib_headers)
+all_cairo_boilerplate_headers += $(cairo_boilerplate_xlib_headers)
+all_cairo_boilerplate_private += $(cairo_boilerplate_xlib_private)
+all_cairo_boilerplate_cxx_sources += $(cairo_boilerplate_xlib_cxx_sources)
+all_cairo_boilerplate_sources += $(cairo_boilerplate_xlib_sources)
+ifeq ($(CAIRO_HAS_XLIB_SURFACE),1)
+enabled_cairo_boilerplate_headers += $(cairo_boilerplate_xlib_headers)
+enabled_cairo_boilerplate_private += $(cairo_boilerplate_xlib_private)
+enabled_cairo_boilerplate_cxx_sources += $(cairo_boilerplate_xlib_cxx_sources)
+enabled_cairo_boilerplate_sources += $(cairo_boilerplate_xlib_sources)
+endif
+
+supported_cairo_boilerplate_headers += $(cairo_boilerplate_xlib_xrender_headers)
+all_cairo_boilerplate_headers += $(cairo_boilerplate_xlib_xrender_headers)
+all_cairo_boilerplate_private += $(cairo_boilerplate_xlib_xrender_private)
+all_cairo_boilerplate_cxx_sources += $(cairo_boilerplate_xlib_xrender_cxx_sources)
+all_cairo_boilerplate_sources += $(cairo_boilerplate_xlib_xrender_sources)
+ifeq ($(CAIRO_HAS_XLIB_XRENDER_SURFACE),1)
+enabled_cairo_boilerplate_headers += $(cairo_boilerplate_xlib_xrender_headers)
+enabled_cairo_boilerplate_private += $(cairo_boilerplate_xlib_xrender_private)
+enabled_cairo_boilerplate_cxx_sources += $(cairo_boilerplate_xlib_xrender_cxx_sources)
+enabled_cairo_boilerplate_sources += $(cairo_boilerplate_xlib_xrender_sources)
+endif
+
+supported_cairo_boilerplate_headers += $(cairo_boilerplate_xcb_headers)
+all_cairo_boilerplate_headers += $(cairo_boilerplate_xcb_headers)
+all_cairo_boilerplate_private += $(cairo_boilerplate_xcb_private)
+all_cairo_boilerplate_cxx_sources += $(cairo_boilerplate_xcb_cxx_sources)
+all_cairo_boilerplate_sources += $(cairo_boilerplate_xcb_sources)
+ifeq ($(CAIRO_HAS_XCB_SURFACE),1)
+enabled_cairo_boilerplate_headers += $(cairo_boilerplate_xcb_headers)
+enabled_cairo_boilerplate_private += $(cairo_boilerplate_xcb_private)
+enabled_cairo_boilerplate_cxx_sources += $(cairo_boilerplate_xcb_cxx_sources)
+enabled_cairo_boilerplate_sources += $(cairo_boilerplate_xcb_sources)
+endif
+
+unsupported_cairo_boilerplate_headers += $(cairo_boilerplate_xlib_xcb_headers)
+all_cairo_boilerplate_headers += $(cairo_boilerplate_xlib_xcb_headers)
+all_cairo_boilerplate_private += $(cairo_boilerplate_xlib_xcb_private)
+all_cairo_boilerplate_cxx_sources += $(cairo_boilerplate_xlib_xcb_cxx_sources)
+all_cairo_boilerplate_sources += $(cairo_boilerplate_xlib_xcb_sources)
+ifeq ($(CAIRO_HAS_XLIB_XCB_FUNCTIONS),1)
+enabled_cairo_boilerplate_headers += $(cairo_boilerplate_xlib_xcb_headers)
+enabled_cairo_boilerplate_private += $(cairo_boilerplate_xlib_xcb_private)
+enabled_cairo_boilerplate_cxx_sources += $(cairo_boilerplate_xlib_xcb_cxx_sources)
+enabled_cairo_boilerplate_sources += $(cairo_boilerplate_xlib_xcb_sources)
+endif
+
+supported_cairo_boilerplate_headers += $(cairo_boilerplate_xcb_shm_headers)
+all_cairo_boilerplate_headers += $(cairo_boilerplate_xcb_shm_headers)
+all_cairo_boilerplate_private += $(cairo_boilerplate_xcb_shm_private)
+all_cairo_boilerplate_cxx_sources += $(cairo_boilerplate_xcb_shm_cxx_sources)
+all_cairo_boilerplate_sources += $(cairo_boilerplate_xcb_shm_sources)
+ifeq ($(CAIRO_HAS_XCB_SHM_FUNCTIONS),1)
+enabled_cairo_boilerplate_headers += $(cairo_boilerplate_xcb_shm_headers)
+enabled_cairo_boilerplate_private += $(cairo_boilerplate_xcb_shm_private)
+enabled_cairo_boilerplate_cxx_sources += $(cairo_boilerplate_xcb_shm_cxx_sources)
+enabled_cairo_boilerplate_sources += $(cairo_boilerplate_xcb_shm_sources)
+endif
+
+unsupported_cairo_boilerplate_headers += $(cairo_boilerplate_qt_headers)
+all_cairo_boilerplate_headers += $(cairo_boilerplate_qt_headers)
+all_cairo_boilerplate_private += $(cairo_boilerplate_qt_private)
+all_cairo_boilerplate_cxx_sources += $(cairo_boilerplate_qt_cxx_sources)
+all_cairo_boilerplate_sources += $(cairo_boilerplate_qt_sources)
+ifeq ($(CAIRO_HAS_QT_SURFACE),1)
+enabled_cairo_boilerplate_headers += $(cairo_boilerplate_qt_headers)
+enabled_cairo_boilerplate_private += $(cairo_boilerplate_qt_private)
+enabled_cairo_boilerplate_cxx_sources += $(cairo_boilerplate_qt_cxx_sources)
+enabled_cairo_boilerplate_sources += $(cairo_boilerplate_qt_sources)
+endif
+
+supported_cairo_boilerplate_headers += $(cairo_boilerplate_quartz_headers)
+all_cairo_boilerplate_headers += $(cairo_boilerplate_quartz_headers)
+all_cairo_boilerplate_private += $(cairo_boilerplate_quartz_private)
+all_cairo_boilerplate_cxx_sources += $(cairo_boilerplate_quartz_cxx_sources)
+all_cairo_boilerplate_sources += $(cairo_boilerplate_quartz_sources)
+ifeq ($(CAIRO_HAS_QUARTZ_SURFACE),1)
+enabled_cairo_boilerplate_headers += $(cairo_boilerplate_quartz_headers)
+enabled_cairo_boilerplate_private += $(cairo_boilerplate_quartz_private)
+enabled_cairo_boilerplate_cxx_sources += $(cairo_boilerplate_quartz_cxx_sources)
+enabled_cairo_boilerplate_sources += $(cairo_boilerplate_quartz_sources)
+endif
+
+supported_cairo_boilerplate_headers += $(cairo_boilerplate_quartz_font_headers)
+all_cairo_boilerplate_headers += $(cairo_boilerplate_quartz_font_headers)
+all_cairo_boilerplate_private += $(cairo_boilerplate_quartz_font_private)
+all_cairo_boilerplate_cxx_sources += $(cairo_boilerplate_quartz_font_cxx_sources)
+all_cairo_boilerplate_sources += $(cairo_boilerplate_quartz_font_sources)
+ifeq ($(CAIRO_HAS_QUARTZ_FONT),1)
+enabled_cairo_boilerplate_headers += $(cairo_boilerplate_quartz_font_headers)
+enabled_cairo_boilerplate_private += $(cairo_boilerplate_quartz_font_private)
+enabled_cairo_boilerplate_cxx_sources += $(cairo_boilerplate_quartz_font_cxx_sources)
+enabled_cairo_boilerplate_sources += $(cairo_boilerplate_quartz_font_sources)
+endif
+
+unsupported_cairo_boilerplate_headers += $(cairo_boilerplate_quartz_image_headers)
+all_cairo_boilerplate_headers += $(cairo_boilerplate_quartz_image_headers)
+all_cairo_boilerplate_private += $(cairo_boilerplate_quartz_image_private)
+all_cairo_boilerplate_cxx_sources += $(cairo_boilerplate_quartz_image_cxx_sources)
+all_cairo_boilerplate_sources += $(cairo_boilerplate_quartz_image_sources)
+ifeq ($(CAIRO_HAS_QUARTZ_IMAGE_SURFACE),1)
+enabled_cairo_boilerplate_headers += $(cairo_boilerplate_quartz_image_headers)
+enabled_cairo_boilerplate_private += $(cairo_boilerplate_quartz_image_private)
+enabled_cairo_boilerplate_cxx_sources += $(cairo_boilerplate_quartz_image_cxx_sources)
+enabled_cairo_boilerplate_sources += $(cairo_boilerplate_quartz_image_sources)
+endif
+
+supported_cairo_boilerplate_headers += $(cairo_boilerplate_win32_headers)
+all_cairo_boilerplate_headers += $(cairo_boilerplate_win32_headers)
+all_cairo_boilerplate_private += $(cairo_boilerplate_win32_private)
+all_cairo_boilerplate_cxx_sources += $(cairo_boilerplate_win32_cxx_sources)
+all_cairo_boilerplate_sources += $(cairo_boilerplate_win32_sources)
+ifeq ($(CAIRO_HAS_WIN32_SURFACE),1)
+enabled_cairo_boilerplate_headers += $(cairo_boilerplate_win32_headers)
+enabled_cairo_boilerplate_private += $(cairo_boilerplate_win32_private)
+enabled_cairo_boilerplate_cxx_sources += $(cairo_boilerplate_win32_cxx_sources)
+enabled_cairo_boilerplate_sources += $(cairo_boilerplate_win32_sources)
+endif
+
+supported_cairo_boilerplate_headers += $(cairo_boilerplate_win32_font_headers)
+all_cairo_boilerplate_headers += $(cairo_boilerplate_win32_font_headers)
+all_cairo_boilerplate_private += $(cairo_boilerplate_win32_font_private)
+all_cairo_boilerplate_cxx_sources += $(cairo_boilerplate_win32_font_cxx_sources)
+all_cairo_boilerplate_sources += $(cairo_boilerplate_win32_font_sources)
+ifeq ($(CAIRO_HAS_WIN32_FONT),1)
+enabled_cairo_boilerplate_headers += $(cairo_boilerplate_win32_font_headers)
+enabled_cairo_boilerplate_private += $(cairo_boilerplate_win32_font_private)
+enabled_cairo_boilerplate_cxx_sources += $(cairo_boilerplate_win32_font_cxx_sources)
+enabled_cairo_boilerplate_sources += $(cairo_boilerplate_win32_font_sources)
+endif
+
+unsupported_cairo_boilerplate_headers += $(cairo_boilerplate_skia_headers)
+all_cairo_boilerplate_headers += $(cairo_boilerplate_skia_headers)
+all_cairo_boilerplate_private += $(cairo_boilerplate_skia_private)
+all_cairo_boilerplate_cxx_sources += $(cairo_boilerplate_skia_cxx_sources)
+all_cairo_boilerplate_sources += $(cairo_boilerplate_skia_sources)
+ifeq ($(CAIRO_HAS_SKIA_SURFACE),1)
+enabled_cairo_boilerplate_headers += $(cairo_boilerplate_skia_headers)
+enabled_cairo_boilerplate_private += $(cairo_boilerplate_skia_private)
+enabled_cairo_boilerplate_cxx_sources += $(cairo_boilerplate_skia_cxx_sources)
+enabled_cairo_boilerplate_sources += $(cairo_boilerplate_skia_sources)
+endif
+
+unsupported_cairo_boilerplate_headers += $(cairo_boilerplate_os2_headers)
+all_cairo_boilerplate_headers += $(cairo_boilerplate_os2_headers)
+all_cairo_boilerplate_private += $(cairo_boilerplate_os2_private)
+all_cairo_boilerplate_cxx_sources += $(cairo_boilerplate_os2_cxx_sources)
+all_cairo_boilerplate_sources += $(cairo_boilerplate_os2_sources)
+ifeq ($(CAIRO_HAS_OS2_SURFACE),1)
+enabled_cairo_boilerplate_headers += $(cairo_boilerplate_os2_headers)
+enabled_cairo_boilerplate_private += $(cairo_boilerplate_os2_private)
+enabled_cairo_boilerplate_cxx_sources += $(cairo_boilerplate_os2_cxx_sources)
+enabled_cairo_boilerplate_sources += $(cairo_boilerplate_os2_sources)
+endif
+
+unsupported_cairo_boilerplate_headers += $(cairo_boilerplate_beos_headers)
+all_cairo_boilerplate_headers += $(cairo_boilerplate_beos_headers)
+all_cairo_boilerplate_private += $(cairo_boilerplate_beos_private)
+all_cairo_boilerplate_cxx_sources += $(cairo_boilerplate_beos_cxx_sources)
+all_cairo_boilerplate_sources += $(cairo_boilerplate_beos_sources)
+ifeq ($(CAIRO_HAS_BEOS_SURFACE),1)
+enabled_cairo_boilerplate_headers += $(cairo_boilerplate_beos_headers)
+enabled_cairo_boilerplate_private += $(cairo_boilerplate_beos_private)
+enabled_cairo_boilerplate_cxx_sources += $(cairo_boilerplate_beos_cxx_sources)
+enabled_cairo_boilerplate_sources += $(cairo_boilerplate_beos_sources)
+endif
+
+unsupported_cairo_boilerplate_headers += $(cairo_boilerplate_drm_headers)
+all_cairo_boilerplate_headers += $(cairo_boilerplate_drm_headers)
+all_cairo_boilerplate_private += $(cairo_boilerplate_drm_private)
+all_cairo_boilerplate_cxx_sources += $(cairo_boilerplate_drm_cxx_sources)
+all_cairo_boilerplate_sources += $(cairo_boilerplate_drm_sources)
+ifeq ($(CAIRO_HAS_DRM_SURFACE),1)
+enabled_cairo_boilerplate_headers += $(cairo_boilerplate_drm_headers)
+enabled_cairo_boilerplate_private += $(cairo_boilerplate_drm_private)
+enabled_cairo_boilerplate_cxx_sources += $(cairo_boilerplate_drm_cxx_sources)
+enabled_cairo_boilerplate_sources += $(cairo_boilerplate_drm_sources)
+endif
+
+unsupported_cairo_boilerplate_headers += $(cairo_boilerplate_gallium_headers)
+all_cairo_boilerplate_headers += $(cairo_boilerplate_gallium_headers)
+all_cairo_boilerplate_private += $(cairo_boilerplate_gallium_private)
+all_cairo_boilerplate_cxx_sources += $(cairo_boilerplate_gallium_cxx_sources)
+all_cairo_boilerplate_sources += $(cairo_boilerplate_gallium_sources)
+ifeq ($(CAIRO_HAS_GALLIUM_SURFACE),1)
+enabled_cairo_boilerplate_headers += $(cairo_boilerplate_gallium_headers)
+enabled_cairo_boilerplate_private += $(cairo_boilerplate_gallium_private)
+enabled_cairo_boilerplate_cxx_sources += $(cairo_boilerplate_gallium_cxx_sources)
+enabled_cairo_boilerplate_sources += $(cairo_boilerplate_gallium_sources)
+endif
+
+supported_cairo_boilerplate_headers += $(cairo_boilerplate_png_headers)
+all_cairo_boilerplate_headers += $(cairo_boilerplate_png_headers)
+all_cairo_boilerplate_private += $(cairo_boilerplate_png_private)
+all_cairo_boilerplate_cxx_sources += $(cairo_boilerplate_png_cxx_sources)
+all_cairo_boilerplate_sources += $(cairo_boilerplate_png_sources)
+ifeq ($(CAIRO_HAS_PNG_FUNCTIONS),1)
+enabled_cairo_boilerplate_headers += $(cairo_boilerplate_png_headers)
+enabled_cairo_boilerplate_private += $(cairo_boilerplate_png_private)
+enabled_cairo_boilerplate_cxx_sources += $(cairo_boilerplate_png_cxx_sources)
+enabled_cairo_boilerplate_sources += $(cairo_boilerplate_png_sources)
+endif
+
+unsupported_cairo_boilerplate_headers += $(cairo_boilerplate_gl_headers)
+all_cairo_boilerplate_headers += $(cairo_boilerplate_gl_headers)
+all_cairo_boilerplate_private += $(cairo_boilerplate_gl_private)
+all_cairo_boilerplate_cxx_sources += $(cairo_boilerplate_gl_cxx_sources)
+all_cairo_boilerplate_sources += $(cairo_boilerplate_gl_sources)
+ifeq ($(CAIRO_HAS_GL_SURFACE),1)
+enabled_cairo_boilerplate_headers += $(cairo_boilerplate_gl_headers)
+enabled_cairo_boilerplate_private += $(cairo_boilerplate_gl_private)
+enabled_cairo_boilerplate_cxx_sources += $(cairo_boilerplate_gl_cxx_sources)
+enabled_cairo_boilerplate_sources += $(cairo_boilerplate_gl_sources)
+endif
+
+unsupported_cairo_boilerplate_headers += $(cairo_boilerplate_glesv2_headers)
+all_cairo_boilerplate_headers += $(cairo_boilerplate_glesv2_headers)
+all_cairo_boilerplate_private += $(cairo_boilerplate_glesv2_private)
+all_cairo_boilerplate_cxx_sources += $(cairo_boilerplate_glesv2_cxx_sources)
+all_cairo_boilerplate_sources += $(cairo_boilerplate_glesv2_sources)
+ifeq ($(CAIRO_HAS_GLESV2_SURFACE),1)
+enabled_cairo_boilerplate_headers += $(cairo_boilerplate_glesv2_headers)
+enabled_cairo_boilerplate_private += $(cairo_boilerplate_glesv2_private)
+enabled_cairo_boilerplate_cxx_sources += $(cairo_boilerplate_glesv2_cxx_sources)
+enabled_cairo_boilerplate_sources += $(cairo_boilerplate_glesv2_sources)
+endif
+
+unsupported_cairo_boilerplate_headers += $(cairo_boilerplate_directfb_headers)
+all_cairo_boilerplate_headers += $(cairo_boilerplate_directfb_headers)
+all_cairo_boilerplate_private += $(cairo_boilerplate_directfb_private)
+all_cairo_boilerplate_cxx_sources += $(cairo_boilerplate_directfb_cxx_sources)
+all_cairo_boilerplate_sources += $(cairo_boilerplate_directfb_sources)
+ifeq ($(CAIRO_HAS_DIRECTFB_SURFACE),1)
+enabled_cairo_boilerplate_headers += $(cairo_boilerplate_directfb_headers)
+enabled_cairo_boilerplate_private += $(cairo_boilerplate_directfb_private)
+enabled_cairo_boilerplate_cxx_sources += $(cairo_boilerplate_directfb_cxx_sources)
+enabled_cairo_boilerplate_sources += $(cairo_boilerplate_directfb_sources)
+endif
+
+unsupported_cairo_boilerplate_headers += $(cairo_boilerplate_vg_headers)
+all_cairo_boilerplate_headers += $(cairo_boilerplate_vg_headers)
+all_cairo_boilerplate_private += $(cairo_boilerplate_vg_private)
+all_cairo_boilerplate_cxx_sources += $(cairo_boilerplate_vg_cxx_sources)
+all_cairo_boilerplate_sources += $(cairo_boilerplate_vg_sources)
+ifeq ($(CAIRO_HAS_VG_SURFACE),1)
+enabled_cairo_boilerplate_headers += $(cairo_boilerplate_vg_headers)
+enabled_cairo_boilerplate_private += $(cairo_boilerplate_vg_private)
+enabled_cairo_boilerplate_cxx_sources += $(cairo_boilerplate_vg_cxx_sources)
+enabled_cairo_boilerplate_sources += $(cairo_boilerplate_vg_sources)
+endif
+
+supported_cairo_boilerplate_headers += $(cairo_boilerplate_egl_headers)
+all_cairo_boilerplate_headers += $(cairo_boilerplate_egl_headers)
+all_cairo_boilerplate_private += $(cairo_boilerplate_egl_private)
+all_cairo_boilerplate_cxx_sources += $(cairo_boilerplate_egl_cxx_sources)
+all_cairo_boilerplate_sources += $(cairo_boilerplate_egl_sources)
+ifeq ($(CAIRO_HAS_EGL_FUNCTIONS),1)
+enabled_cairo_boilerplate_headers += $(cairo_boilerplate_egl_headers)
+enabled_cairo_boilerplate_private += $(cairo_boilerplate_egl_private)
+enabled_cairo_boilerplate_cxx_sources += $(cairo_boilerplate_egl_cxx_sources)
+enabled_cairo_boilerplate_sources += $(cairo_boilerplate_egl_sources)
+endif
+
+supported_cairo_boilerplate_headers += $(cairo_boilerplate_glx_headers)
+all_cairo_boilerplate_headers += $(cairo_boilerplate_glx_headers)
+all_cairo_boilerplate_private += $(cairo_boilerplate_glx_private)
+all_cairo_boilerplate_cxx_sources += $(cairo_boilerplate_glx_cxx_sources)
+all_cairo_boilerplate_sources += $(cairo_boilerplate_glx_sources)
+ifeq ($(CAIRO_HAS_GLX_FUNCTIONS),1)
+enabled_cairo_boilerplate_headers += $(cairo_boilerplate_glx_headers)
+enabled_cairo_boilerplate_private += $(cairo_boilerplate_glx_private)
+enabled_cairo_boilerplate_cxx_sources += $(cairo_boilerplate_glx_cxx_sources)
+enabled_cairo_boilerplate_sources += $(cairo_boilerplate_glx_sources)
+endif
+
+supported_cairo_boilerplate_headers += $(cairo_boilerplate_wgl_headers)
+all_cairo_boilerplate_headers += $(cairo_boilerplate_wgl_headers)
+all_cairo_boilerplate_private += $(cairo_boilerplate_wgl_private)
+all_cairo_boilerplate_cxx_sources += $(cairo_boilerplate_wgl_cxx_sources)
+all_cairo_boilerplate_sources += $(cairo_boilerplate_wgl_sources)
+ifeq ($(CAIRO_HAS_WGL_FUNCTIONS),1)
+enabled_cairo_boilerplate_headers += $(cairo_boilerplate_wgl_headers)
+enabled_cairo_boilerplate_private += $(cairo_boilerplate_wgl_private)
+enabled_cairo_boilerplate_cxx_sources += $(cairo_boilerplate_wgl_cxx_sources)
+enabled_cairo_boilerplate_sources += $(cairo_boilerplate_wgl_sources)
+endif
+
+supported_cairo_boilerplate_headers += $(cairo_boilerplate_script_headers)
+all_cairo_boilerplate_headers += $(cairo_boilerplate_script_headers)
+all_cairo_boilerplate_private += $(cairo_boilerplate_script_private)
+all_cairo_boilerplate_cxx_sources += $(cairo_boilerplate_script_cxx_sources)
+all_cairo_boilerplate_sources += $(cairo_boilerplate_script_sources)
+ifeq ($(CAIRO_HAS_SCRIPT_SURFACE),1)
+enabled_cairo_boilerplate_headers += $(cairo_boilerplate_script_headers)
+enabled_cairo_boilerplate_private += $(cairo_boilerplate_script_private)
+enabled_cairo_boilerplate_cxx_sources += $(cairo_boilerplate_script_cxx_sources)
+enabled_cairo_boilerplate_sources += $(cairo_boilerplate_script_sources)
+endif
+
+supported_cairo_boilerplate_headers += $(cairo_boilerplate_ft_headers)
+all_cairo_boilerplate_headers += $(cairo_boilerplate_ft_headers)
+all_cairo_boilerplate_private += $(cairo_boilerplate_ft_private)
+all_cairo_boilerplate_cxx_sources += $(cairo_boilerplate_ft_cxx_sources)
+all_cairo_boilerplate_sources += $(cairo_boilerplate_ft_sources)
+ifeq ($(CAIRO_HAS_FT_FONT),1)
+enabled_cairo_boilerplate_headers += $(cairo_boilerplate_ft_headers)
+enabled_cairo_boilerplate_private += $(cairo_boilerplate_ft_private)
+enabled_cairo_boilerplate_cxx_sources += $(cairo_boilerplate_ft_cxx_sources)
+enabled_cairo_boilerplate_sources += $(cairo_boilerplate_ft_sources)
+endif
+
+supported_cairo_boilerplate_headers += $(cairo_boilerplate_fc_headers)
+all_cairo_boilerplate_headers += $(cairo_boilerplate_fc_headers)
+all_cairo_boilerplate_private += $(cairo_boilerplate_fc_private)
+all_cairo_boilerplate_cxx_sources += $(cairo_boilerplate_fc_cxx_sources)
+all_cairo_boilerplate_sources += $(cairo_boilerplate_fc_sources)
+ifeq ($(CAIRO_HAS_FC_FONT),1)
+enabled_cairo_boilerplate_headers += $(cairo_boilerplate_fc_headers)
+enabled_cairo_boilerplate_private += $(cairo_boilerplate_fc_private)
+enabled_cairo_boilerplate_cxx_sources += $(cairo_boilerplate_fc_cxx_sources)
+enabled_cairo_boilerplate_sources += $(cairo_boilerplate_fc_sources)
+endif
+
+supported_cairo_boilerplate_headers += $(cairo_boilerplate_ps_headers)
+all_cairo_boilerplate_headers += $(cairo_boilerplate_ps_headers)
+all_cairo_boilerplate_private += $(cairo_boilerplate_ps_private)
+all_cairo_boilerplate_cxx_sources += $(cairo_boilerplate_ps_cxx_sources)
+all_cairo_boilerplate_sources += $(cairo_boilerplate_ps_sources)
+ifeq ($(CAIRO_HAS_PS_SURFACE),1)
+enabled_cairo_boilerplate_headers += $(cairo_boilerplate_ps_headers)
+enabled_cairo_boilerplate_private += $(cairo_boilerplate_ps_private)
+enabled_cairo_boilerplate_cxx_sources += $(cairo_boilerplate_ps_cxx_sources)
+enabled_cairo_boilerplate_sources += $(cairo_boilerplate_ps_sources)
+endif
+
+supported_cairo_boilerplate_headers += $(cairo_boilerplate_pdf_headers)
+all_cairo_boilerplate_headers += $(cairo_boilerplate_pdf_headers)
+all_cairo_boilerplate_private += $(cairo_boilerplate_pdf_private)
+all_cairo_boilerplate_cxx_sources += $(cairo_boilerplate_pdf_cxx_sources)
+all_cairo_boilerplate_sources += $(cairo_boilerplate_pdf_sources)
+ifeq ($(CAIRO_HAS_PDF_SURFACE),1)
+enabled_cairo_boilerplate_headers += $(cairo_boilerplate_pdf_headers)
+enabled_cairo_boilerplate_private += $(cairo_boilerplate_pdf_private)
+enabled_cairo_boilerplate_cxx_sources += $(cairo_boilerplate_pdf_cxx_sources)
+enabled_cairo_boilerplate_sources += $(cairo_boilerplate_pdf_sources)
+endif
+
+supported_cairo_boilerplate_headers += $(cairo_boilerplate_svg_headers)
+all_cairo_boilerplate_headers += $(cairo_boilerplate_svg_headers)
+all_cairo_boilerplate_private += $(cairo_boilerplate_svg_private)
+all_cairo_boilerplate_cxx_sources += $(cairo_boilerplate_svg_cxx_sources)
+all_cairo_boilerplate_sources += $(cairo_boilerplate_svg_sources)
+ifeq ($(CAIRO_HAS_SVG_SURFACE),1)
+enabled_cairo_boilerplate_headers += $(cairo_boilerplate_svg_headers)
+enabled_cairo_boilerplate_private += $(cairo_boilerplate_svg_private)
+enabled_cairo_boilerplate_cxx_sources += $(cairo_boilerplate_svg_cxx_sources)
+enabled_cairo_boilerplate_sources += $(cairo_boilerplate_svg_sources)
+endif
+
+all_cairo_boilerplate_private += $(cairo_boilerplate_test_surfaces_private) $(cairo_boilerplate_test_surfaces_headers)
+all_cairo_boilerplate_cxx_sources += $(cairo_boilerplate_test_surfaces_cxx_sources)
+all_cairo_boilerplate_sources += $(cairo_boilerplate_test_surfaces_sources)
+ifeq ($(CAIRO_HAS_TEST_SURFACES),1)
+enabled_cairo_boilerplate_private += $(cairo_boilerplate_test_surfaces_private) $(cairo_boilerplate_test_surfaces_headers)
+enabled_cairo_boilerplate_cxx_sources += $(cairo_boilerplate_test_surfaces_cxx_sources)
+enabled_cairo_boilerplate_sources += $(cairo_boilerplate_test_surfaces_sources)
+endif
+
+supported_cairo_boilerplate_headers += $(cairo_boilerplate_image_headers)
+all_cairo_boilerplate_headers += $(cairo_boilerplate_image_headers)
+all_cairo_boilerplate_private += $(cairo_boilerplate_image_private)
+all_cairo_boilerplate_cxx_sources += $(cairo_boilerplate_image_cxx_sources)
+all_cairo_boilerplate_sources += $(cairo_boilerplate_image_sources)
+enabled_cairo_boilerplate_headers += $(cairo_boilerplate_image_headers)
+enabled_cairo_boilerplate_private += $(cairo_boilerplate_image_private)
+enabled_cairo_boilerplate_cxx_sources += $(cairo_boilerplate_image_cxx_sources)
+enabled_cairo_boilerplate_sources += $(cairo_boilerplate_image_sources)
+
+supported_cairo_boilerplate_headers += $(cairo_boilerplate_mime_headers)
+all_cairo_boilerplate_headers += $(cairo_boilerplate_mime_headers)
+all_cairo_boilerplate_private += $(cairo_boilerplate_mime_private)
+all_cairo_boilerplate_cxx_sources += $(cairo_boilerplate_mime_cxx_sources)
+all_cairo_boilerplate_sources += $(cairo_boilerplate_mime_sources)
+enabled_cairo_boilerplate_headers += $(cairo_boilerplate_mime_headers)
+enabled_cairo_boilerplate_private += $(cairo_boilerplate_mime_private)
+enabled_cairo_boilerplate_cxx_sources += $(cairo_boilerplate_mime_cxx_sources)
+enabled_cairo_boilerplate_sources += $(cairo_boilerplate_mime_sources)
+
+supported_cairo_boilerplate_headers += $(cairo_boilerplate_recording_headers)
+all_cairo_boilerplate_headers += $(cairo_boilerplate_recording_headers)
+all_cairo_boilerplate_private += $(cairo_boilerplate_recording_private)
+all_cairo_boilerplate_cxx_sources += $(cairo_boilerplate_recording_cxx_sources)
+all_cairo_boilerplate_sources += $(cairo_boilerplate_recording_sources)
+enabled_cairo_boilerplate_headers += $(cairo_boilerplate_recording_headers)
+enabled_cairo_boilerplate_private += $(cairo_boilerplate_recording_private)
+enabled_cairo_boilerplate_cxx_sources += $(cairo_boilerplate_recording_cxx_sources)
+enabled_cairo_boilerplate_sources += $(cairo_boilerplate_recording_sources)
+
+supported_cairo_boilerplate_headers += $(cairo_boilerplate_observer_headers)
+all_cairo_boilerplate_headers += $(cairo_boilerplate_observer_headers)
+all_cairo_boilerplate_private += $(cairo_boilerplate_observer_private)
+all_cairo_boilerplate_cxx_sources += $(cairo_boilerplate_observer_cxx_sources)
+all_cairo_boilerplate_sources += $(cairo_boilerplate_observer_sources)
+enabled_cairo_boilerplate_headers += $(cairo_boilerplate_observer_headers)
+enabled_cairo_boilerplate_private += $(cairo_boilerplate_observer_private)
+enabled_cairo_boilerplate_cxx_sources += $(cairo_boilerplate_observer_cxx_sources)
+enabled_cairo_boilerplate_sources += $(cairo_boilerplate_observer_sources)
+
+unsupported_cairo_boilerplate_headers += $(cairo_boilerplate_tee_headers)
+all_cairo_boilerplate_headers += $(cairo_boilerplate_tee_headers)
+all_cairo_boilerplate_private += $(cairo_boilerplate_tee_private)
+all_cairo_boilerplate_cxx_sources += $(cairo_boilerplate_tee_cxx_sources)
+all_cairo_boilerplate_sources += $(cairo_boilerplate_tee_sources)
+ifeq ($(CAIRO_HAS_TEE_SURFACE),1)
+enabled_cairo_boilerplate_headers += $(cairo_boilerplate_tee_headers)
+enabled_cairo_boilerplate_private += $(cairo_boilerplate_tee_private)
+enabled_cairo_boilerplate_cxx_sources += $(cairo_boilerplate_tee_cxx_sources)
+enabled_cairo_boilerplate_sources += $(cairo_boilerplate_tee_sources)
+endif
+
+unsupported_cairo_boilerplate_headers += $(cairo_boilerplate_xml_headers)
+all_cairo_boilerplate_headers += $(cairo_boilerplate_xml_headers)
+all_cairo_boilerplate_private += $(cairo_boilerplate_xml_private)
+all_cairo_boilerplate_cxx_sources += $(cairo_boilerplate_xml_cxx_sources)
+all_cairo_boilerplate_sources += $(cairo_boilerplate_xml_sources)
+ifeq ($(CAIRO_HAS_XML_SURFACE),1)
+enabled_cairo_boilerplate_headers += $(cairo_boilerplate_xml_headers)
+enabled_cairo_boilerplate_private += $(cairo_boilerplate_xml_private)
+enabled_cairo_boilerplate_cxx_sources += $(cairo_boilerplate_xml_cxx_sources)
+enabled_cairo_boilerplate_sources += $(cairo_boilerplate_xml_sources)
+endif
+
+supported_cairo_boilerplate_headers += $(cairo_boilerplate_user_headers)
+all_cairo_boilerplate_headers += $(cairo_boilerplate_user_headers)
+all_cairo_boilerplate_private += $(cairo_boilerplate_user_private)
+all_cairo_boilerplate_cxx_sources += $(cairo_boilerplate_user_cxx_sources)
+all_cairo_boilerplate_sources += $(cairo_boilerplate_user_sources)
+enabled_cairo_boilerplate_headers += $(cairo_boilerplate_user_headers)
+enabled_cairo_boilerplate_private += $(cairo_boilerplate_user_private)
+enabled_cairo_boilerplate_cxx_sources += $(cairo_boilerplate_user_cxx_sources)
+enabled_cairo_boilerplate_sources += $(cairo_boilerplate_user_sources)
+
+all_cairo_boilerplate_private += $(cairo_boilerplate_pthread_private) $(cairo_boilerplate_pthread_headers)
+all_cairo_boilerplate_cxx_sources += $(cairo_boilerplate_pthread_cxx_sources)
+all_cairo_boilerplate_sources += $(cairo_boilerplate_pthread_sources)
+ifeq ($(CAIRO_HAS_PTHREAD),1)
+enabled_cairo_boilerplate_private += $(cairo_boilerplate_pthread_private) $(cairo_boilerplate_pthread_headers)
+enabled_cairo_boilerplate_cxx_sources += $(cairo_boilerplate_pthread_cxx_sources)
+enabled_cairo_boilerplate_sources += $(cairo_boilerplate_pthread_sources)
+endif
+
+supported_cairo_boilerplate_headers += $(cairo_boilerplate_gobject_headers)
+all_cairo_boilerplate_headers += $(cairo_boilerplate_gobject_headers)
+all_cairo_boilerplate_private += $(cairo_boilerplate_gobject_private)
+all_cairo_boilerplate_cxx_sources += $(cairo_boilerplate_gobject_cxx_sources)
+all_cairo_boilerplate_sources += $(cairo_boilerplate_gobject_sources)
+ifeq ($(CAIRO_HAS_GOBJECT_FUNCTIONS),1)
+enabled_cairo_boilerplate_headers += $(cairo_boilerplate_gobject_headers)
+enabled_cairo_boilerplate_private += $(cairo_boilerplate_gobject_private)
+enabled_cairo_boilerplate_cxx_sources += $(cairo_boilerplate_gobject_cxx_sources)
+enabled_cairo_boilerplate_sources += $(cairo_boilerplate_gobject_sources)
+endif
+
+all_cairo_boilerplate_private += $(cairo_boilerplate_trace_private) $(cairo_boilerplate_trace_headers)
+all_cairo_boilerplate_cxx_sources += $(cairo_boilerplate_trace_cxx_sources)
+all_cairo_boilerplate_sources += $(cairo_boilerplate_trace_sources)
+ifeq ($(CAIRO_HAS_TRACE),1)
+enabled_cairo_boilerplate_private += $(cairo_boilerplate_trace_private) $(cairo_boilerplate_trace_headers)
+enabled_cairo_boilerplate_cxx_sources += $(cairo_boilerplate_trace_cxx_sources)
+enabled_cairo_boilerplate_sources += $(cairo_boilerplate_trace_sources)
+endif
+
+all_cairo_boilerplate_private += $(cairo_boilerplate_interpreter_private) $(cairo_boilerplate_interpreter_headers)
+all_cairo_boilerplate_cxx_sources += $(cairo_boilerplate_interpreter_cxx_sources)
+all_cairo_boilerplate_sources += $(cairo_boilerplate_interpreter_sources)
+ifeq ($(CAIRO_HAS_INTERPRETER),1)
+enabled_cairo_boilerplate_private += $(cairo_boilerplate_interpreter_private) $(cairo_boilerplate_interpreter_headers)
+enabled_cairo_boilerplate_cxx_sources += $(cairo_boilerplate_interpreter_cxx_sources)
+enabled_cairo_boilerplate_sources += $(cairo_boilerplate_interpreter_sources)
+endif
+
+all_cairo_boilerplate_private += $(cairo_boilerplate_symbol_lookup_private) $(cairo_boilerplate_symbol_lookup_headers)
+all_cairo_boilerplate_cxx_sources += $(cairo_boilerplate_symbol_lookup_cxx_sources)
+all_cairo_boilerplate_sources += $(cairo_boilerplate_symbol_lookup_sources)
+ifeq ($(CAIRO_HAS_SYMBOL_LOOKUP),1)
+enabled_cairo_boilerplate_private += $(cairo_boilerplate_symbol_lookup_private) $(cairo_boilerplate_symbol_lookup_headers)
+enabled_cairo_boilerplate_cxx_sources += $(cairo_boilerplate_symbol_lookup_cxx_sources)
+enabled_cairo_boilerplate_sources += $(cairo_boilerplate_symbol_lookup_sources)
+endif
diff --git a/boilerplate/README b/boilerplate/README
new file mode 100644 (file)
index 0000000..2a27c41
--- /dev/null
@@ -0,0 +1,14 @@
+This directory provides code that is common to both of cairo's tests
+suites:
+
+ * The test suite for correctness in test/
+ * The test suite for performance in perf/
+
+We call it boilerplate as it consists primarily of the boilerplate
+code necessary for initializing a backend in order to create a surface
+for that backend.
+
+The code here just might be useful for someone looking to get started
+writing cairo code to use a particular backend, (but there are no
+promises that the boilerplate code found here for any particular
+backend is exemplary).
diff --git a/boilerplate/cairo-boilerplate-beos.cpp b/boilerplate/cairo-boilerplate-beos.cpp
new file mode 100644 (file)
index 0000000..8a1b1af
--- /dev/null
@@ -0,0 +1,273 @@
+/* vim:set ts=8 sw=4 noet cin: */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Communicator client code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Takashi Toyoshima <toyoshim@be-in.org>
+ *   Fredrik Holmqvist <thesuckiestemail@yahoo.se>
+ *   Christian Biesinger <cbiesinger@web.de>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "cairo-boilerplate.h"
+#include <cairo-beos.h>
+
+// Part of this code was originally part of
+// xpfe/bootstrap/nsNativeAppSupportBeOS.cpp in the Mozilla source code.
+
+#include <Application.h>
+#include <Window.h>
+#include <View.h>
+#include <Bitmap.h>
+
+class CairoTestWindow : public BWindow
+{
+public:
+    CairoTestWindow(BRect frame, const char* title);
+    virtual ~CairoTestWindow();
+    BView* View() const { return mView; }
+private:
+    BView* mView;
+};
+
+CairoTestWindow::CairoTestWindow(BRect frame, const char* title)
+    : BWindow(frame, title, B_TITLED_WINDOW,
+             B_NOT_RESIZABLE|B_NOT_ZOOMABLE)
+{
+    mView = new BView(frame, "CairoWindowTestView", B_FOLLOW_ALL_SIDES, 0);
+    AddChild(mView);
+    Show();
+
+    // Make sure the window is actually on screen
+    Lock();
+    Sync();
+    mView->SetViewColor(B_TRANSPARENT_COLOR);
+    mView->Sync();
+    Unlock();
+}
+
+CairoTestWindow::~CairoTestWindow()
+{
+    RemoveChild(mView);
+    delete mView;
+}
+
+
+class nsBeOSApp : public BApplication
+{
+public:
+    nsBeOSApp(sem_id sem) : BApplication(GetAppSig()), init(sem)
+    {}
+
+    void ReadyToRun()
+    {
+       release_sem(init);
+    }
+
+    static int32 Main(void *args)
+    {
+       nsBeOSApp *app = new nsBeOSApp( (sem_id)args );
+       if(app == NULL)
+           return B_ERROR;
+       return app->Run();
+    }
+
+private:
+
+    const char *GetAppSig()
+    {
+       return "application/x-vnd.cairo-test-app";
+    }
+
+    sem_id init;
+}; //class nsBeOSApp
+
+class AppRunner
+{
+    public:
+       AppRunner();
+       ~AppRunner();
+};
+
+AppRunner::AppRunner()
+{
+    if (be_app)
+       return;
+
+    sem_id initsem = create_sem(0, "Cairo BApplication init");
+    if (initsem < B_OK) {
+       fprintf (stderr, "Error creating BeOS initialization semaphore\n");
+       return;
+    }
+
+    thread_id tid = spawn_thread(nsBeOSApp::Main, "Cairo/BeOS test", B_NORMAL_PRIORITY, (void *)initsem);
+    if (tid < B_OK || B_OK != resume_thread(tid)) {
+       fprintf (stderr, "Error spawning thread\n");
+       return;
+    }
+
+    if (B_OK != acquire_sem(initsem)) {
+       fprintf (stderr, "Error acquiring semaphore\n");
+       return;
+    }
+
+    delete_sem(initsem);
+    return;
+}
+
+AppRunner::~AppRunner()
+{
+    if (be_app) {
+       if (be_app->Lock())
+           be_app->Quit();
+       delete be_app;
+       be_app = NULL;
+    }
+}
+
+// Make sure that the BApplication is initialized
+static AppRunner sAppRunner;
+
+struct beos_boilerplate_closure {
+    BView* view;
+    BBitmap* bitmap;
+    BWindow* window;
+};
+
+// Test a real window
+static cairo_surface_t *
+_cairo_boilerplate_beos_create_surface (const char               *name,
+                                       cairo_content_t            content,
+                                       double                     width,
+                                       double                     height,
+                                       cairo_boilerplate_mode_t   mode,
+                                       void                     **closure)
+{
+    float right = width ? width - 1 : 0;
+    float bottom = height ? height - 1 : 0;
+    BRect rect(0.0, 0.0, right, bottom);
+    CairoTestWindow* wnd = new CairoTestWindow(rect, name);
+
+    beos_boilerplate_closure* bclosure = new beos_boilerplate_closure;
+    bclosure->view = wnd->View();
+    bclosure->bitmap = NULL;
+    bclosure->window = wnd;
+
+    *closure = bclosure;
+
+    return cairo_beos_surface_create(wnd->View());
+}
+
+static void
+_cairo_boilerplate_beos_cleanup (void *closure)
+{
+    beos_boilerplate_closure* bclosure = reinterpret_cast<beos_boilerplate_closure*>(closure);
+
+    bclosure->window->Lock();
+    bclosure->window->Quit();
+
+    delete bclosure;
+}
+
+// Test a bitmap
+static cairo_surface_t *
+_cairo_boilerplate_beos_create_surface_for_bitmap (const char               *name,
+                                                  cairo_content_t            content,
+                                                  double                     width,
+                                                  double                     height,
+                                                  cairo_boilerplate_mode_t   mode,
+                                                  void                     **closure)
+{
+    BRect rect(0.0, 0.0, width - 1, height - 1);
+    color_space beosformat = (content == CAIRO_CONTENT_COLOR_ALPHA) ? B_RGBA32
+                                                                   : B_RGB32;
+    BBitmap* bmp = new BBitmap(rect, beosformat, true);
+    BView* view = new BView(rect, "Cairo test view", B_FOLLOW_ALL_SIDES, 0);
+    bmp->AddChild(view);
+
+    beos_boilerplate_closure* bclosure = new beos_boilerplate_closure;
+    bclosure->view = view;
+    bclosure->bitmap = bmp;
+    bclosure->window = NULL;
+    *closure = bclosure;
+
+    return cairo_beos_surface_create_for_bitmap(view, bmp);
+}
+
+static void
+_cairo_boilerplate_beos_cleanup_bitmap (void *closure)
+{
+    beos_boilerplate_closure* bclosure = reinterpret_cast<beos_boilerplate_closure*>(closure);
+
+    bclosure->bitmap->RemoveChild(bclosure->view);
+
+
+    delete bclosure->view;
+    delete bclosure->bitmap;
+
+    delete bclosure;
+}
+
+static const cairo_boilerplate_target_t targets[] = {
+    /* BeOS sometimes produces a slightly different image. Perhaps this
+     * is related to the fact that it doesn't use premultiplied alpha...
+     * Just ignore the small difference. */
+    {
+       "beos", "beos", NULL, NULL,
+       CAIRO_SURFACE_TYPE_BEOS, CAIRO_CONTENT_COLOR, 1,
+       _cairo_boilerplate_beos_create_surface,
+       NULL, NULL,
+       _cairo_boilerplate_get_image_surface,
+       cairo_surface_write_to_png,
+       _cairo_boilerplate_beos_cleanup
+    },
+    {
+       "beos-bitmap", "beos", NULL, NULL,
+       CAIRO_SURFACE_TYPE_BEOS, CAIRO_CONTENT_COLOR, 1,
+       _cairo_boilerplate_beos_create_surface_for_bitmap,
+       NULL, NULL,
+       _cairo_boilerplate_get_image_surface,
+       cairo_surface_write_to_png,
+       _cairo_boilerplate_beos_cleanup_bitmap
+    },
+    {
+       "beos-bitmap", "beos", NULL, NULL,
+       CAIRO_SURFACE_TYPE_BEOS, CAIRO_CONTENT_COLOR_ALPHA, 1,
+       _cairo_boilerplate_beos_create_surface_for_bitmap,
+       NULL, NULL,
+       _cairo_boilerplate_get_image_surface,
+       cairo_surface_write_to_png,
+       _cairo_boilerplate_beos_cleanup_bitmap
+    },
+};
+CAIRO_BOILERPLATE (beos, targets)
+
diff --git a/boilerplate/cairo-boilerplate-directfb.c b/boilerplate/cairo-boilerplate-directfb.c
new file mode 100644 (file)
index 0000000..be5efb6
--- /dev/null
@@ -0,0 +1,236 @@
+/*
+Test were run with the following script
+target can be directfb_bitmap or directfb
+
+export CAIRO_TEST_TARGET=directfb_bitmap
+export DFBARGS=quiet,no-banner,no-debug,log-file=dfblog,system=x11
+cd cairo/test
+make check
+
+*/
+
+#include "cairo-boilerplate-private.h"
+
+#include <cairo-directfb.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <direct/debug.h>
+
+D_DEBUG_DOMAIN (CairoDFB_Boiler, "CairoDFB/Boiler", "Cairo DirectFB Boilerplate");
+
+/* macro for a safe call to DirectFB functions */
+#define DFBCHECK(x...) do{                                     \
+    err = x;                                                   \
+    if (err != DFB_OK) {                                       \
+       fprintf (stderr, "%s <%d>:\n\t", __FILE__, __LINE__); \
+       goto ERROR; \
+    }                                                          \
+} while (0)
+
+typedef struct _DFBInfo {
+    IDirectFB             *dfb;
+    IDirectFBDisplayLayer  *layer;
+    IDirectFBWindow       *window;
+    IDirectFBSurface      *surface;
+} DFBInfo;
+
+static void
+_cairo_boilerplate_directfb_cleanup (void *closure)
+{
+    DFBInfo *info = (DFBInfo *) closure;
+
+    if (info->surface)
+       info->surface->Release (info->surface);
+
+    if (info->window)
+       info->window->Release (info->window);
+
+    if (info->layer)
+       info->layer->Release (info->layer);
+
+    if (info->dfb)
+       info->dfb->Release (info->dfb);
+
+    free (info);
+}
+
+static DFBInfo *
+init (void)
+{
+    DFBDisplayLayerConfig       layer_config;
+    DFBGraphicsDeviceDescription desc;
+    int err;
+    DFBInfo *info;
+
+    info = xcalloc (1, sizeof (DFBInfo));
+    if (info == NULL)
+       return NULL;
+
+    DFBCHECK (DirectFBInit (NULL, NULL));
+    DFBCHECK (DirectFBCreate (&info->dfb));
+    info->dfb->GetDeviceDescription (info->dfb, &desc);
+
+    DFBCHECK (info->dfb->GetDisplayLayer (info->dfb,
+                                         DLID_PRIMARY, &info->layer));
+    info->layer->SetCooperativeLevel (info->layer, DLSCL_ADMINISTRATIVE);
+
+    if ((desc.blitting_flags & (DSBLIT_BLEND_ALPHACHANNEL |
+                               DSBLIT_BLEND_COLORALPHA)) !=
+       (DSBLIT_BLEND_ALPHACHANNEL | DSBLIT_BLEND_COLORALPHA))
+    {
+       layer_config.flags = DLCONF_BUFFERMODE;
+       layer_config.buffermode = DLBM_BACKSYSTEM;
+       info->layer->SetConfiguration (info->layer, &layer_config);
+    }
+
+    return info;
+
+ERROR:
+    if (info != NULL)
+       _cairo_boilerplate_directfb_cleanup (info);
+    return NULL;
+}
+
+static cairo_surface_t *
+_cairo_boilerplate_directfb_window_create_surface (DFBInfo        *info,
+                                                  cairo_content_t  content,
+                                                  int              width,
+                                                  int              height)
+{
+    DFBWindowDescription desc;
+    int err;
+
+    D_DEBUG_AT (CairoDFB_Boiler, "%s (%p, %s, %dx%d)\n", __FUNCTION__, info,
+               content == CAIRO_CONTENT_ALPHA       ? "ALPHA" :
+               content == CAIRO_CONTENT_COLOR       ? "RGB"   :
+               content == CAIRO_CONTENT_COLOR_ALPHA ? "ARGB"  : "unknown content!",
+               width, height);
+
+    desc.flags = DWDESC_POSX | DWDESC_POSY |
+                 DWDESC_WIDTH | DWDESC_HEIGHT;
+    desc.caps  = DSCAPS_NONE;
+    desc.posx  = 0;
+    desc.posy  = 0;
+    desc.width = width;
+    desc.height = height;
+    if (content == CAIRO_CONTENT_COLOR_ALPHA) {
+       desc.flags |= DWDESC_CAPS | DWDESC_PIXELFORMAT;
+       desc.caps  |= DWCAPS_DOUBLEBUFFER | DWCAPS_ALPHACHANNEL;
+       desc.pixelformat = DSPF_ARGB;
+    }
+
+    DFBCHECK (info->layer->CreateWindow (info->layer, &desc, &info->window));
+    info->window->SetOpacity (info->window, 0xFF);
+    info->window->GetSurface (info->window, &info->surface);
+    info->surface->SetColor (info->surface, 0xFF, 0xFF, 0xFF, 0xFF);
+    info->surface->FillRectangle (info->surface,0, 0, desc.width, desc.height);
+    info->surface->Flip (info->surface, NULL, 0);
+
+    return cairo_directfb_surface_create (info->dfb, info->surface);
+
+ERROR:
+    _cairo_boilerplate_directfb_cleanup (info);
+    return NULL;
+}
+
+static cairo_surface_t *
+_cairo_boilerplate_directfb_bitmap_create_surface (DFBInfo        *info,
+                                                  cairo_content_t  content,
+                                                  int              width,
+                                                  int              height)
+{
+    int  err;
+    DFBSurfaceDescription  desc;
+
+    D_DEBUG_AT (CairoDFB_Boiler, "%s (%p, %s, %dx%d)\n", __FUNCTION__, info,
+               content == CAIRO_CONTENT_ALPHA       ? "ALPHA" :
+               content == CAIRO_CONTENT_COLOR       ? "RGB"   :
+               content == CAIRO_CONTENT_COLOR_ALPHA ? "ARGB"  : "unknown content!",
+               width, height);
+
+    desc.flags = DSDESC_WIDTH | DSDESC_HEIGHT;
+    desc.caps = DSCAPS_NONE;
+    desc.width = width;
+    desc.height = height;
+    if (content == CAIRO_CONTENT_COLOR_ALPHA) {
+       desc.flags |= DSDESC_PIXELFORMAT;
+       desc.pixelformat = DSPF_ARGB;
+    }
+    DFBCHECK (info->dfb->CreateSurface (info->dfb, &desc, &info->surface));
+
+    return cairo_directfb_surface_create (info->dfb, info->surface);
+
+ERROR:
+    _cairo_boilerplate_directfb_cleanup (info);
+    return NULL;
+}
+
+static cairo_surface_t *
+_cairo_boilerplate_directfb_create_surface (const char               *name,
+                                           cairo_content_t            content,
+                                           double                     width,
+                                           double                     height,
+                                           double                     max_width,
+                                           double                     max_height,
+                                           cairo_boilerplate_mode_t   mode,
+                                           int                        id,
+                                           void                     **closure)
+{
+
+    DFBInfo *info;
+
+    info = init ();
+    if (info == NULL)
+       return NULL;
+
+    *closure = info;
+
+    D_DEBUG_AT (CairoDFB_Boiler, "%s ('%s', %s, %dx%d, %s)\n",
+               __FUNCTION__, name,
+               content == CAIRO_CONTENT_ALPHA       ? "ALPHA" :
+               content == CAIRO_CONTENT_COLOR       ? "RGB"   :
+               content == CAIRO_CONTENT_COLOR_ALPHA ? "ARGB"  : "unknown content!",
+               width, height,
+               mode == CAIRO_BOILERPLATE_MODE_TEST ? "TEST" :
+               mode == CAIRO_BOILERPLATE_MODE_PERF ? "PERF" : "unknown mode!");
+
+    if (width == 0)
+       width = 1;
+    if (height == 0)
+       height = 1;
+
+    if (mode == CAIRO_BOILERPLATE_MODE_TEST)
+       return _cairo_boilerplate_directfb_bitmap_create_surface (info, content, width, height);
+    else /* mode == CAIRO_BOILERPLATE_MODE_PERF */
+       return _cairo_boilerplate_directfb_window_create_surface (info, content, width, height);
+}
+
+static const cairo_boilerplate_target_t targets[] = {
+    {
+       "directfb", "directfb", NULL, NULL,
+       CAIRO_SURFACE_TYPE_DIRECTFB, CAIRO_CONTENT_COLOR, 0,
+       "cairo_directfb_surface_create",
+       _cairo_boilerplate_directfb_create_surface,
+       cairo_surface_create_similar,
+       NULL, NULL,
+       _cairo_boilerplate_get_image_surface,
+       cairo_surface_write_to_png,
+       _cairo_boilerplate_directfb_cleanup,
+       NULL, NULL, TRUE, FALSE, FALSE
+    },
+    {
+       "directfb-bitmap", "directfb", NULL, NULL,
+       CAIRO_SURFACE_TYPE_DIRECTFB, CAIRO_CONTENT_COLOR_ALPHA, 0,
+       "cairo_directfb_surface_create",
+       _cairo_boilerplate_directfb_create_surface,
+       cairo_surface_create_similar,
+       NULL, NULL,
+       _cairo_boilerplate_get_image_surface,
+       cairo_surface_write_to_png,
+       _cairo_boilerplate_directfb_cleanup,
+       NULL, NULL, FALSE, FALSE, FALSE
+    },
+};
+CAIRO_BOILERPLATE (directfb, targets);
diff --git a/boilerplate/cairo-boilerplate-drm.c b/boilerplate/cairo-boilerplate-drm.c
new file mode 100644 (file)
index 0000000..8f04314
--- /dev/null
@@ -0,0 +1,107 @@
+/* Cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Chris Wilson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Chris Wilson.
+ */
+
+#include "cairo-boilerplate-private.h"
+
+#include <cairo-drm.h>
+
+static cairo_surface_t *
+_cairo_boilerplate_drm_create_surface (const char               *name,
+                                      cairo_content_t            content,
+                                      double                     width,
+                                      double                     height,
+                                      double                     max_width,
+                                      double                     max_height,
+                                      cairo_boilerplate_mode_t   mode,
+                                      int                        id,
+                                      void                     **closure)
+{
+    cairo_device_t *device;
+    cairo_format_t format;
+
+    device = cairo_drm_device_default ();
+    if (device == NULL)
+       return NULL; /* skip tests if no supported h/w found */
+
+    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;
+    }
+
+    return *closure = cairo_drm_surface_create (device, format, width, height);
+}
+
+static void
+_cairo_boilerplate_drm_synchronize (void *closure)
+{
+    cairo_surface_t *image;
+
+    image = cairo_drm_surface_map_to_image (closure);
+    if (cairo_surface_status (image) == CAIRO_STATUS_SUCCESS)
+       cairo_drm_surface_unmap (closure, image);
+}
+
+static const cairo_boilerplate_target_t targets[] = {
+    /* Acceleration architectures may make the results differ by a
+     * bit, so we set the error tolerance to 1. */
+    {
+       "drm", "drm", NULL, NULL,
+       CAIRO_SURFACE_TYPE_DRM, CAIRO_CONTENT_COLOR_ALPHA, 1,
+       "cairo_drm_surface_create",
+       _cairo_boilerplate_drm_create_surface,
+       cairo_surface_create_similar,
+       NULL, NULL,
+       _cairo_boilerplate_get_image_surface,
+       cairo_surface_write_to_png,
+       NULL,
+       _cairo_boilerplate_drm_synchronize,
+        NULL,
+       TRUE, FALSE, FALSE
+    },
+    {
+       "drm", "drm", NULL, NULL,
+       CAIRO_SURFACE_TYPE_DRM, CAIRO_CONTENT_COLOR, 1,
+       "cairo_drm_surface_create",
+       _cairo_boilerplate_drm_create_surface,
+       cairo_surface_create_similar,
+       NULL, NULL,
+       _cairo_boilerplate_get_image_surface,
+       cairo_surface_write_to_png,
+       NULL,
+       _cairo_boilerplate_drm_synchronize,
+        NULL,
+       FALSE, FALSE, FALSE
+    },
+};
+CAIRO_BOILERPLATE (drm, targets)
diff --git a/boilerplate/cairo-boilerplate-egl.c b/boilerplate/cairo-boilerplate-egl.c
new file mode 100644 (file)
index 0000000..0e1c9b9
--- /dev/null
@@ -0,0 +1,174 @@
+/* Cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Chris Wilson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Chris Wilson.
+ */
+
+#include "cairo-boilerplate-private.h"
+
+#include <cairo-gl.h>
+#if CAIRO_HAS_GL_SURFACE
+#include <GL/gl.h>
+#elif CAIRO_HAS_GLESV2_SURFACE
+#include <GLES2/gl2.h>
+#endif
+
+static const cairo_user_data_key_t gl_closure_key;
+
+typedef struct _egl_target_closure {
+    EGLDisplay dpy;
+    EGLContext ctx;
+
+    cairo_device_t *device;
+    cairo_surface_t *surface;
+} egl_target_closure_t;
+
+static void
+_cairo_boilerplate_egl_cleanup (void *closure)
+{
+    egl_target_closure_t *gltc = closure;
+
+    cairo_device_finish (gltc->device);
+    cairo_device_destroy (gltc->device);
+
+    eglDestroyContext (gltc->dpy, gltc->ctx);
+    eglMakeCurrent (gltc->dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+    eglTerminate (gltc->dpy);
+
+    free (gltc);
+}
+
+static cairo_surface_t *
+_cairo_boilerplate_egl_create_surface (const char               *name,
+                                      cairo_content_t            content,
+                                      double                     width,
+                                      double                     height,
+                                      double                     max_width,
+                                      double                     max_height,
+                                      cairo_boilerplate_mode_t   mode,
+                                      int                        id,
+                                      void                     **closure)
+{
+    egl_target_closure_t *gltc;
+    cairo_surface_t *surface;
+    int major, minor;
+    EGLConfig config;
+    EGLint numConfigs;
+    EGLint config_attribs[] = {
+       EGL_RED_SIZE, 8,
+       EGL_GREEN_SIZE, 8,
+       EGL_BLUE_SIZE, 8,
+       EGL_ALPHA_SIZE, 8,
+       EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
+#if CAIRO_HAS_GL_SURFACE
+       EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT,
+#elif CAIRO_HAS_GLESV2_SURFACE
+       EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+#endif
+       EGL_NONE
+    };
+    const EGLint ctx_attribs[] = {
+#if CAIRO_HAS_GLESV2_SURFACE
+       EGL_CONTEXT_CLIENT_VERSION, 2,
+#endif
+       EGL_NONE
+    };
+
+    gltc = xcalloc (1, sizeof (egl_target_closure_t));
+    *closure = gltc;
+
+    gltc->dpy = eglGetDisplay (EGL_DEFAULT_DISPLAY);
+
+    if (! eglInitialize (gltc->dpy, &major, &minor)) {
+       free (gltc);
+       return NULL;
+    }
+
+    eglChooseConfig (gltc->dpy, config_attribs, &config, 1, &numConfigs);
+    if (numConfigs == 0) {
+       free (gltc);
+       return NULL;
+    }
+
+#if CAIRO_HAS_GL_SURFACE
+    eglBindAPI (EGL_OPENGL_API);
+#elif CAIRO_HAS_GLESV2_SURFACE
+    eglBindAPI (EGL_OPENGL_ES_API);
+#endif
+
+    gltc->ctx = eglCreateContext (gltc->dpy, config, EGL_NO_CONTEXT,
+                                 ctx_attribs);
+    if (gltc->ctx == EGL_NO_CONTEXT) {
+       eglTerminate (gltc->dpy);
+       free (gltc);
+       return NULL;
+    }
+
+    gltc->device = cairo_egl_device_create (gltc->dpy, gltc->ctx);
+
+    gltc->surface = surface = cairo_gl_surface_create (gltc->device,
+                                                      content,
+                                                      ceil (width),
+                                                      ceil (height));
+    if (cairo_surface_status (surface))
+       _cairo_boilerplate_egl_cleanup (gltc);
+
+    return surface;
+}
+
+static void
+_cairo_boilerplate_egl_synchronize (void *closure)
+{
+    egl_target_closure_t *gltc = closure;
+
+    if (cairo_device_acquire (gltc->device))
+       return;
+
+    glFinish ();
+
+    cairo_device_release (gltc->device);
+}
+
+static const cairo_boilerplate_target_t targets[] = {
+    {
+       "egl", "gl", NULL, NULL,
+       CAIRO_SURFACE_TYPE_GL, CAIRO_CONTENT_COLOR_ALPHA, 1,
+       "cairo_egl_device_create",
+       _cairo_boilerplate_egl_create_surface,
+       cairo_surface_create_similar,
+       NULL, NULL,
+       _cairo_boilerplate_get_image_surface,
+       cairo_surface_write_to_png,
+       _cairo_boilerplate_egl_cleanup,
+       _cairo_boilerplate_egl_synchronize,
+        NULL,
+       TRUE, FALSE, FALSE
+    }
+};
+CAIRO_BOILERPLATE (egl, targets)
diff --git a/boilerplate/cairo-boilerplate-getopt.c b/boilerplate/cairo-boilerplate-getopt.c
new file mode 100644 (file)
index 0000000..53b150c
--- /dev/null
@@ -0,0 +1,247 @@
+/*****************************************************************************
+* getopt.c - competent and free getopt library.
+* $Header: /cvsroot/freegetopt/freegetopt/getopt.c,v 1.2 2003/10/26 03:10:20 vindaci Exp $
+*
+* Copyright (c)2002-2003 Mark K. Kim
+* All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions
+* are met:
+*
+*   * Redistributions of source code must retain the above copyright
+*     notice, this list of conditions and the following disclaimer.
+*
+*   * Redistributions in binary form must reproduce the above copyright
+*     notice, this list of conditions and the following disclaimer in
+*     the documentation and/or other materials provided with the
+*     distribution.
+*
+*   * Neither the original author of this software nor the names of its
+*     contributors may be used to endorse or promote products derived
+*     from this software without specific prior written permission.
+*
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+* DAMAGE.
+*/
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "cairo-boilerplate-getopt.h"
+
+
+char* optarg = NULL;
+int optind = 0;
+int opterr = 1;
+int optopt = '?';
+
+
+static char** prev_argv = NULL;        /* Keep a copy of argv and argc to */
+static int prev_argc = 0;             /*    tell if getopt params change */
+static int argv_index = 0;            /* Option we're checking */
+static int argv_index2 = 0;           /* Option argument we're checking */
+static int opt_offset = 0;            /* Index into compounded "-option" */
+static int dashdash = 0;              /* True if "--" option reached */
+static int nonopt = 0;                /* How many nonopts we've found */
+
+static void increment_index(void)
+{
+       /* Move onto the next option */
+       if(argv_index < argv_index2)
+       {
+               while(prev_argv[++argv_index] && prev_argv[argv_index][0] != '-'
+                               && argv_index < argv_index2+1);
+       }
+       else argv_index++;
+       opt_offset = 1;
+}
+
+
+/*
+* Permutes argv[] so that the argument currently being processed is moved
+* to the end.
+*/
+static int permute_argv_once(void)
+{
+       /* Movability check */
+       if(argv_index + nonopt >= prev_argc) return 1;
+       /* Move the current option to the end, bring the others to front */
+       else
+       {
+               char* tmp = prev_argv[argv_index];
+
+               /* Move the data */
+               memmove(&prev_argv[argv_index], &prev_argv[argv_index+1],
+                               sizeof(char**) * (prev_argc - argv_index - 1));
+               prev_argv[prev_argc - 1] = tmp;
+
+               nonopt++;
+               return 0;
+       }
+}
+
+
+int _cairo_getopt(int argc, char** argv, const char* optstr)
+{
+       int c = 0;
+
+       /* If we have new argv, reinitialize */
+       if(prev_argv != argv || prev_argc != argc)
+       {
+               /* Initialize variables */
+               prev_argv = argv;
+               prev_argc = argc;
+               argv_index = 1;
+               argv_index2 = 1;
+               opt_offset = 1;
+               dashdash = 0;
+               nonopt = 0;
+       }
+
+       /* Jump point in case we want to ignore the current argv_index */
+       getopt_top:
+
+       /* Misc. initializations */
+       optarg = NULL;
+
+       /* Dash-dash check */
+       if(argv[argv_index] && !strcmp(argv[argv_index], "--"))
+       {
+               dashdash = 1;
+               increment_index();
+       }
+
+       /* If we're at the end of argv, that's it. */
+       if(argv[argv_index] == NULL)
+       {
+               c = -1;
+       }
+       /* Are we looking at a string? Single dash is also a string */
+       else if(dashdash || argv[argv_index][0] != '-' || !strcmp(argv[argv_index], "-"))
+       {
+               /* If we want a string... */
+               if(optstr[0] == '-')
+               {
+                       c = 1;
+                       optarg = argv[argv_index];
+                       increment_index();
+               }
+               /* If we really don't want it (we're in POSIX mode), we're done */
+               else if(optstr[0] == '+' || getenv("POSIXLY_CORRECT"))
+               {
+                       c = -1;
+
+                       /* Everything else is a non-opt argument */
+                       nonopt = argc - argv_index;
+               }
+               /* If we mildly don't want it, then move it back */
+               else
+               {
+                       if(!permute_argv_once()) goto getopt_top;
+                       else c = -1;
+               }
+       }
+       /* Otherwise we're looking at an option */
+       else
+       {
+               char* opt_ptr = NULL;
+
+               /* Grab the option */
+               c = argv[argv_index][opt_offset++];
+
+               /* Is the option in the optstr? */
+               if(optstr[0] == '-') opt_ptr = strchr(optstr+1, c);
+               else opt_ptr = strchr(optstr, c);
+               /* Invalid argument */
+               if(!opt_ptr)
+               {
+                       if(opterr)
+                       {
+                               fprintf(stderr, "%s: invalid option -- %c\n", argv[0], c);
+                       }
+
+                       optopt = c;
+                       c = '?';
+
+                       /* Move onto the next option */
+                       increment_index();
+               }
+               /* Option takes argument */
+               else if(opt_ptr[1] == ':')
+               {
+                       /* ie, -oARGUMENT, -xxxoARGUMENT, etc. */
+                       if(argv[argv_index][opt_offset] != '\0')
+                       {
+                               optarg = &argv[argv_index][opt_offset];
+                               increment_index();
+                       }
+                       /* ie, -o ARGUMENT (only if it's a required argument) */
+                       else if(opt_ptr[2] != ':')
+                       {
+                               /* One of those "you're not expected to understand this" moment */
+                               if(argv_index2 < argv_index) argv_index2 = argv_index;
+                               while(argv[++argv_index2] && argv[argv_index2][0] == '-');
+                               optarg = argv[argv_index2];
+
+                               /* Don't cross into the non-option argument list */
+                               if(argv_index2 + nonopt >= prev_argc) optarg = NULL;
+
+                               /* Move onto the next option */
+                               increment_index();
+                       }
+                       else
+                       {
+                               /* Move onto the next option */
+                               increment_index();
+                       }
+
+                       /* In case we got no argument for an option with required argument */
+                       if(optarg == NULL && opt_ptr[2] != ':')
+                       {
+                               optopt = c;
+                               c = '?';
+
+                               if(opterr)
+                               {
+                                       fprintf(stderr,"%s: option requires an argument -- %c\n",
+                                                       argv[0], optopt);
+                               }
+                       }
+               }
+               /* Option does not take argument */
+               else
+               {
+                       /* Next argv_index */
+                       if(argv[argv_index][opt_offset] == '\0')
+                       {
+                               increment_index();
+                       }
+               }
+       }
+
+       /* Calculate optind */
+       if(c == -1)
+       {
+               optind = argc - nonopt;
+       }
+       else
+       {
+               optind = argv_index;
+       }
+
+       return c;
+}
+
+
+/* vim:ts=3
+*/
diff --git a/boilerplate/cairo-boilerplate-getopt.h b/boilerplate/cairo-boilerplate-getopt.h
new file mode 100644 (file)
index 0000000..74bce14
--- /dev/null
@@ -0,0 +1,63 @@
+/*****************************************************************************
+* getopt.h - competent and free getopt library.
+* $Header: /cvsroot/freegetopt/freegetopt/getopt.h,v 1.2 2003/10/26 03:10:20 vindaci Exp $
+*
+* Copyright (c)2002-2003 Mark K. Kim
+* All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions
+* are met:
+*
+*   * Redistributions of source code must retain the above copyright
+*     notice, this list of conditions and the following disclaimer.
+*
+*   * Redistributions in binary form must reproduce the above copyright
+*     notice, this list of conditions and the following disclaimer in
+*     the documentation and/or other materials provided with the
+*     distribution.
+*
+*   * Neither the original author of this software nor the names of its
+*     contributors may be used to endorse or promote products derived
+*     from this software without specific prior written permission.
+*
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+* DAMAGE.
+*/
+#ifndef GETOPT_H_
+#define GETOPT_H_
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+extern char* optarg;
+extern int optind;
+extern int opterr;
+extern int optopt;
+
+int _cairo_getopt(int argc, char** argv, const char* optstr);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /* GETOPT_H_ */
+
+
+/* vim:ts=3
+*/
diff --git a/boilerplate/cairo-boilerplate-glx.c b/boilerplate/cairo-boilerplate-glx.c
new file mode 100644 (file)
index 0000000..690844e
--- /dev/null
@@ -0,0 +1,430 @@
+/* Cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Chris Wilson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Chris Wilson.
+ */
+
+#include "cairo-boilerplate-private.h"
+
+#include <cairo-gl.h>
+
+#include <X11/X.h>
+#include <X11/Xutil.h> /* for XDestroyImage */
+
+static const cairo_user_data_key_t gl_closure_key;
+
+typedef struct _gl_target_closure {
+    Display *dpy;
+    int screen;
+    Window drawable;
+
+    GLXContext ctx;
+    cairo_device_t *device;
+    cairo_surface_t *surface;
+} gl_target_closure_t;
+
+static void
+_cairo_boilerplate_gl_cleanup (void *closure)
+{
+    gl_target_closure_t *gltc = closure;
+
+    cairo_device_finish (gltc->device);
+    cairo_device_destroy (gltc->device);
+
+    glXDestroyContext (gltc->dpy, gltc->ctx);
+
+    if (gltc->drawable)
+       XDestroyWindow (gltc->dpy, gltc->drawable);
+    XCloseDisplay (gltc->dpy);
+
+    free (gltc);
+}
+
+static cairo_surface_t *
+_cairo_boilerplate_gl_create_surface (const char               *name,
+                                     cairo_content_t            content,
+                                     double                     width,
+                                     double                     height,
+                                     double                     max_width,
+                                     double                     max_height,
+                                     cairo_boilerplate_mode_t   mode,
+                                     int                        id,
+                                     void                     **closure)
+{
+    int rgba_attribs[] = { GLX_RGBA,
+                          GLX_RED_SIZE, 1,
+                          GLX_GREEN_SIZE, 1,
+                          GLX_BLUE_SIZE, 1,
+                          GLX_ALPHA_SIZE, 1,
+                          GLX_DOUBLEBUFFER,
+                          None };
+    int rgb_attribs[] = { GLX_RGBA,
+                         GLX_RED_SIZE, 1,
+                         GLX_GREEN_SIZE, 1,
+                         GLX_BLUE_SIZE, 1,
+                         GLX_DOUBLEBUFFER,
+                         None };
+    XVisualInfo *visinfo;
+    GLXContext ctx;
+    gl_target_closure_t *gltc;
+    cairo_surface_t *surface;
+    Display *dpy;
+
+    gltc = calloc (1, sizeof (gl_target_closure_t));
+    *closure = gltc;
+
+    if (width == 0)
+       width = 1;
+    if (height == 0)
+       height = 1;
+
+    dpy = XOpenDisplay (NULL);
+    gltc->dpy = dpy;
+    if (!gltc->dpy) {
+       fprintf (stderr, "Failed to open display: %s\n", XDisplayName(0));
+       free (gltc);
+       return NULL;
+    }
+
+    if (mode == CAIRO_BOILERPLATE_MODE_TEST)
+       XSynchronize (gltc->dpy, 1);
+
+    if (content == CAIRO_CONTENT_COLOR)
+       visinfo = glXChooseVisual (dpy, DefaultScreen (dpy), rgb_attribs);
+    else
+       visinfo = glXChooseVisual (dpy, DefaultScreen (dpy), rgba_attribs);
+
+    if (visinfo == NULL) {
+       fprintf (stderr, "Failed to create RGB, double-buffered visual\n");
+       XCloseDisplay (dpy);
+       free (gltc);
+       return NULL;
+    }
+
+    ctx = glXCreateContext (dpy, visinfo, NULL, True);
+    XFree (visinfo);
+
+    gltc->ctx = ctx;
+    gltc->device = cairo_glx_device_create (dpy, ctx);
+
+    gltc->surface = surface = cairo_gl_surface_create (gltc->device,
+                                                      content,
+                                                      ceil (width),
+                                                      ceil (height));
+    if (cairo_surface_status (surface))
+       _cairo_boilerplate_gl_cleanup (gltc);
+
+    return surface;
+}
+
+static cairo_surface_t *
+_cairo_boilerplate_gl_create_window (const char               *name,
+                                    cairo_content_t            content,
+                                    double                     width,
+                                    double                     height,
+                                    double                     max_width,
+                                    double                     max_height,
+                                    cairo_boilerplate_mode_t   mode,
+                                    int                        id,
+                                    void                     **closure)
+{
+    int rgba_attribs[] = { GLX_RGBA,
+                          GLX_RED_SIZE, 1,
+                          GLX_GREEN_SIZE, 1,
+                          GLX_BLUE_SIZE, 1,
+                          GLX_ALPHA_SIZE, 1,
+                          GLX_DOUBLEBUFFER,
+                          None };
+    XVisualInfo *vi;
+    GLXContext ctx;
+    gl_target_closure_t *gltc;
+    cairo_surface_t *surface;
+    Display *dpy;
+    XSetWindowAttributes attr;
+
+    gltc = calloc (1, sizeof (gl_target_closure_t));
+    *closure = gltc;
+
+    if (width == 0)
+       width = 1;
+    if (height == 0)
+       height = 1;
+
+    dpy = XOpenDisplay (NULL);
+    gltc->dpy = dpy;
+    if (!gltc->dpy) {
+       fprintf (stderr, "Failed to open display: %s\n", XDisplayName(0));
+       free (gltc);
+       return NULL;
+    }
+
+    if (mode == CAIRO_BOILERPLATE_MODE_TEST)
+       XSynchronize (gltc->dpy, 1);
+
+    vi = glXChooseVisual (dpy, DefaultScreen (dpy), rgba_attribs);
+    if (vi == NULL) {
+       fprintf (stderr, "Failed to create RGBA, double-buffered visual\n");
+       XCloseDisplay (dpy);
+       free (gltc);
+       return NULL;
+    }
+
+    attr.colormap = XCreateColormap (dpy,
+                                    RootWindow (dpy, vi->screen),
+                                    vi->visual,
+                                    AllocNone);
+    attr.border_pixel = 0;
+    attr.override_redirect = True;
+    gltc->drawable = XCreateWindow (dpy, DefaultRootWindow (dpy), 0, 0,
+                                   width, height, 0, vi->depth,
+                                   InputOutput, vi->visual,
+                                   CWOverrideRedirect | CWBorderPixel | CWColormap,
+                                   &attr);
+    XMapWindow (dpy, gltc->drawable);
+
+    ctx = glXCreateContext (dpy, vi, NULL, True);
+    XFree (vi);
+
+    gltc->ctx = ctx;
+    gltc->device = cairo_glx_device_create (dpy, ctx);
+
+    gltc->surface = surface = cairo_gl_surface_create_for_window (gltc->device,
+                                                                 gltc->drawable,
+                                                                 ceil (width),
+                                                                 ceil (height));
+    if (cairo_surface_status (surface))
+       _cairo_boilerplate_gl_cleanup (gltc);
+
+    return surface;
+}
+
+static cairo_surface_t *
+_cairo_boilerplate_gl_create_window_db (const char               *name,
+                                       cairo_content_t            content,
+                                       double                     width,
+                                       double                     height,
+                                       double                     max_width,
+                                       double                     max_height,
+                                       cairo_boilerplate_mode_t   mode,
+                                       int                        id,
+                                       void                     **closure)
+{
+    int rgba_attribs[] = { GLX_RGBA,
+                          GLX_RED_SIZE, 1,
+                          GLX_GREEN_SIZE, 1,
+                          GLX_BLUE_SIZE, 1,
+                          GLX_ALPHA_SIZE, 1,
+                          GLX_DOUBLEBUFFER,
+                          None };
+    XVisualInfo *vi;
+    GLXContext ctx;
+    gl_target_closure_t *gltc;
+    cairo_surface_t *surface;
+    Display *dpy;
+    XSetWindowAttributes attr;
+    cairo_status_t status;
+
+    gltc = calloc (1, sizeof (gl_target_closure_t));
+    *closure = gltc;
+
+    if (width == 0)
+       width = 1;
+    if (height == 0)
+       height = 1;
+
+    dpy = XOpenDisplay (NULL);
+    gltc->dpy = dpy;
+    if (!gltc->dpy) {
+       fprintf (stderr, "Failed to open display: %s\n", XDisplayName(0));
+       free (gltc);
+       return NULL;
+    }
+
+    if (mode == CAIRO_BOILERPLATE_MODE_TEST)
+       XSynchronize (gltc->dpy, 1);
+
+    vi = glXChooseVisual (dpy, DefaultScreen (dpy), rgba_attribs);
+    if (vi == NULL) {
+       fprintf (stderr, "Failed to create RGBA, double-buffered visual\n");
+       XCloseDisplay (dpy);
+       free (gltc);
+       return NULL;
+    }
+
+    attr.colormap = XCreateColormap (dpy,
+                                    RootWindow (dpy, vi->screen),
+                                    vi->visual,
+                                    AllocNone);
+    attr.border_pixel = 0;
+    attr.override_redirect = True;
+    gltc->drawable = XCreateWindow (dpy, DefaultRootWindow (dpy), 0, 0,
+                                   width, height, 0, vi->depth,
+                                   InputOutput, vi->visual,
+                                   CWOverrideRedirect | CWBorderPixel | CWColormap,
+                                   &attr);
+    XMapWindow (dpy, gltc->drawable);
+
+    ctx = glXCreateContext (dpy, vi, NULL, True);
+    XFree (vi);
+
+    gltc->ctx = ctx;
+    gltc->device = cairo_glx_device_create (dpy, ctx);
+
+    gltc->surface = cairo_gl_surface_create_for_window (gltc->device,
+                                                       gltc->drawable,
+                                                       ceil (width),
+                                                       ceil (height));
+    surface = cairo_surface_create_similar (gltc->surface, content, width, height);
+    status = cairo_surface_set_user_data (surface, &gl_closure_key, gltc, NULL);
+    if (status == CAIRO_STATUS_SUCCESS)
+       return surface;
+
+    cairo_surface_destroy (surface);
+    _cairo_boilerplate_gl_cleanup (gltc);
+    return cairo_boilerplate_surface_create_in_error (status);
+}
+
+static cairo_status_t
+_cairo_boilerplate_gl_finish_window (cairo_surface_t *surface)
+{
+    gl_target_closure_t *gltc = cairo_surface_get_user_data (surface,
+                                                            &gl_closure_key);
+
+    if (gltc != NULL && gltc->surface != NULL) {
+       cairo_t *cr;
+
+       cr = cairo_create (gltc->surface);
+       cairo_surface_set_device_offset (surface, 0, 0);
+       cairo_set_source_surface (cr, surface, 0, 0);
+       cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+       cairo_paint (cr);
+       cairo_destroy (cr);
+
+       surface = gltc->surface;
+    }
+
+    cairo_gl_surface_swapbuffers (surface);
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_boilerplate_gl_synchronize (void *closure)
+{
+    gl_target_closure_t *gltc = closure;
+
+    if (cairo_device_acquire (gltc->device))
+       return;
+
+    glFinish ();
+
+    cairo_device_release (gltc->device);
+}
+
+static char *
+_cairo_boilerplate_gl_describe (void *closure)
+{
+    gl_target_closure_t *gltc = closure;
+    char *s;
+    const GLubyte *vendor, *renderer, *version;
+
+    if (cairo_device_acquire (gltc->device))
+       return NULL;
+
+    vendor   = glGetString (GL_VENDOR);
+    renderer = glGetString (GL_RENDERER);
+    version  = glGetString (GL_VERSION);
+
+    xasprintf (&s, "%s %s %s", vendor, renderer, version);
+
+    cairo_device_release (gltc->device);
+
+    return s;
+}
+
+static const cairo_boilerplate_target_t targets[] = {
+    {
+       "gl", "gl", NULL, NULL,
+       CAIRO_SURFACE_TYPE_GL, CAIRO_CONTENT_COLOR_ALPHA, 1,
+       "cairo_gl_surface_create",
+       _cairo_boilerplate_gl_create_surface,
+       cairo_surface_create_similar,
+       NULL, NULL,
+       _cairo_boilerplate_get_image_surface,
+       cairo_surface_write_to_png,
+       _cairo_boilerplate_gl_cleanup,
+       _cairo_boilerplate_gl_synchronize,
+        _cairo_boilerplate_gl_describe,
+       TRUE, FALSE, FALSE
+    },
+    {
+       "gl", "gl", NULL, NULL,
+       CAIRO_SURFACE_TYPE_GL, CAIRO_CONTENT_COLOR, 1,
+       "cairo_gl_surface_create",
+       _cairo_boilerplate_gl_create_surface,
+       cairo_surface_create_similar,
+       NULL, NULL,
+       _cairo_boilerplate_get_image_surface,
+       cairo_surface_write_to_png,
+       _cairo_boilerplate_gl_cleanup,
+       _cairo_boilerplate_gl_synchronize,
+        _cairo_boilerplate_gl_describe,
+       FALSE, FALSE, FALSE
+    },
+    {
+       "gl-window", "gl", NULL, NULL,
+       CAIRO_SURFACE_TYPE_GL, CAIRO_CONTENT_COLOR_ALPHA, 1,
+       "cairo_gl_surface_create_for_window",
+       _cairo_boilerplate_gl_create_window,
+       cairo_surface_create_similar,
+       NULL,
+       _cairo_boilerplate_gl_finish_window,
+       _cairo_boilerplate_get_image_surface,
+       cairo_surface_write_to_png,
+       _cairo_boilerplate_gl_cleanup,
+       _cairo_boilerplate_gl_synchronize,
+        _cairo_boilerplate_gl_describe,
+       FALSE, FALSE, FALSE
+    },
+    {
+       "gl-window&", "gl", NULL, NULL,
+       CAIRO_SURFACE_TYPE_GL, CAIRO_CONTENT_COLOR_ALPHA, 1,
+       "cairo_gl_surface_create_for_window",
+       _cairo_boilerplate_gl_create_window_db,
+       cairo_surface_create_similar,
+       NULL,
+       _cairo_boilerplate_gl_finish_window,
+       _cairo_boilerplate_get_image_surface,
+       cairo_surface_write_to_png,
+       _cairo_boilerplate_gl_cleanup,
+       _cairo_boilerplate_gl_synchronize,
+        _cairo_boilerplate_gl_describe,
+       FALSE, FALSE, FALSE
+    },
+};
+CAIRO_BOILERPLATE (gl, targets)
diff --git a/boilerplate/cairo-boilerplate-pdf.c b/boilerplate/cairo-boilerplate-pdf.c
new file mode 100644 (file)
index 0000000..5173780
--- /dev/null
@@ -0,0 +1,281 @@
+/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/*
+ * Copyright © 2004,2006 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ */
+
+#include "cairo-boilerplate-private.h"
+
+#if CAIRO_CAN_TEST_PDF_SURFACE
+
+#include <cairo-pdf.h>
+#include <cairo-pdf-surface-private.h>
+#include <cairo-paginated-surface-private.h>
+
+#if HAVE_SIGNAL_H
+#include <signal.h>
+#endif
+
+#if HAVE_SYS_WAIT_H
+#include <sys/wait.h>
+#endif
+
+#if ! CAIRO_HAS_RECORDING_SURFACE
+#define CAIRO_SURFACE_TYPE_RECORDING CAIRO_INTERNAL_SURFACE_TYPE_RECORDING
+#endif
+
+static const cairo_user_data_key_t pdf_closure_key;
+
+typedef struct _pdf_target_closure
+{
+    char               *filename;
+    int                 width;
+    int                 height;
+    cairo_surface_t    *target;
+} pdf_target_closure_t;
+
+#define ARRAY_LENGTH(__array) ((int) (sizeof (__array) / sizeof (__array[0])))
+
+static cairo_surface_t *
+_cairo_boilerplate_pdf_create_surface (const char               *name,
+                                      cairo_content_t            content,
+                                      double                     width,
+                                      double                     height,
+                                      double                     max_width,
+                                      double                     max_height,
+                                      cairo_boilerplate_mode_t   mode,
+                                      int                        id,
+                                      void                     **closure)
+{
+    pdf_target_closure_t *ptc;
+    cairo_surface_t *surface;
+    cairo_status_t status;
+
+    /* Sanitize back to a real cairo_content_t value. */
+    if (content == CAIRO_TEST_CONTENT_COLOR_ALPHA_FLATTENED)
+       content = CAIRO_CONTENT_COLOR_ALPHA;
+
+    *closure = ptc = xmalloc (sizeof (pdf_target_closure_t));
+
+    ptc->width = ceil (width);
+    ptc->height = ceil (height);
+
+    xasprintf (&ptc->filename, "%s.out.pdf", name);
+    xunlink (ptc->filename);
+
+    surface = cairo_pdf_surface_create (ptc->filename, width, height);
+    if (cairo_surface_status (surface))
+       goto CLEANUP_FILENAME;
+
+    cairo_surface_set_fallback_resolution (surface, 72., 72.);
+
+    if (content == CAIRO_CONTENT_COLOR) {
+       ptc->target = surface;
+       surface = cairo_surface_create_similar (ptc->target,
+                                               CAIRO_CONTENT_COLOR,
+                                               ptc->width, ptc->height);
+       if (cairo_surface_status (surface))
+           goto CLEANUP_TARGET;
+    } else {
+       ptc->target = NULL;
+    }
+
+    status = cairo_surface_set_user_data (surface, &pdf_closure_key, ptc, NULL);
+    if (status == CAIRO_STATUS_SUCCESS)
+       return surface;
+
+    cairo_surface_destroy (surface);
+    surface = cairo_boilerplate_surface_create_in_error (status);
+
+  CLEANUP_TARGET:
+    cairo_surface_destroy (ptc->target);
+  CLEANUP_FILENAME:
+    free (ptc->filename);
+    free (ptc);
+    return surface;
+}
+
+static cairo_status_t
+_cairo_boilerplate_pdf_finish_surface (cairo_surface_t *surface)
+{
+    pdf_target_closure_t *ptc = cairo_surface_get_user_data (surface,
+                                                            &pdf_closure_key);
+    cairo_status_t status;
+
+    /* Both surface and ptc->target were originally created at the
+     * same dimensions. We want a 1:1 copy here, so we first clear any
+     * device offset on surface.
+     *
+     * In a more realistic use case of device offsets, the target of
+     * this copying would be of a different size than the source, and
+     * the offset would be desirable during the copy operation. */
+    cairo_surface_set_device_offset (surface, 0, 0);
+
+    if (ptc->target) {
+       cairo_t *cr;
+       cr = cairo_create (ptc->target);
+       cairo_set_source_surface (cr, surface, 0, 0);
+       cairo_paint (cr);
+       cairo_show_page (cr);
+       status = cairo_status (cr);
+       cairo_destroy (cr);
+
+       if (status)
+           return status;
+
+       cairo_surface_finish (surface);
+       status = cairo_surface_status (surface);
+       if (status)
+           return status;
+
+       surface = ptc->target;
+    }
+
+    cairo_surface_finish (surface);
+    status = cairo_surface_status (surface);
+    if (status)
+       return status;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_boilerplate_pdf_surface_write_to_png (cairo_surface_t *surface,
+                                            const char      *filename)
+{
+    pdf_target_closure_t *ptc = cairo_surface_get_user_data (surface, &pdf_closure_key);
+    char    command[4096];
+    int exitstatus;
+
+    sprintf (command, "./pdf2png %s %s 1",
+            ptc->filename, filename);
+
+    exitstatus = system (command);
+#if _XOPEN_SOURCE && HAVE_SIGNAL_H
+    if (WIFSIGNALED (exitstatus))
+       raise (WTERMSIG (exitstatus));
+#endif
+    if (exitstatus)
+       return CAIRO_STATUS_WRITE_ERROR;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_surface_t *
+_cairo_boilerplate_pdf_convert_to_image (cairo_surface_t *surface,
+                                        int              page)
+{
+    pdf_target_closure_t *ptc = cairo_surface_get_user_data (surface,
+                                                            &pdf_closure_key);
+
+    return cairo_boilerplate_convert_to_image (ptc->filename, page+1);
+}
+
+static cairo_surface_t *
+_cairo_boilerplate_pdf_get_image_surface (cairo_surface_t *surface,
+                                         int              page,
+                                         int              width,
+                                         int              height)
+{
+    cairo_surface_t *image;
+
+    image = _cairo_boilerplate_pdf_convert_to_image (surface, page);
+    cairo_surface_set_device_offset (image,
+                                    cairo_image_surface_get_width (image) - width,
+                                    cairo_image_surface_get_height (image) - height);
+    surface = _cairo_boilerplate_get_image_surface (image, 0, width, height);
+    cairo_surface_destroy (image);
+
+    return surface;
+}
+
+static void
+_cairo_boilerplate_pdf_cleanup (void *closure)
+{
+    pdf_target_closure_t *ptc = closure;
+    if (ptc->target) {
+       cairo_surface_finish (ptc->target);
+       cairo_surface_destroy (ptc->target);
+    }
+    free (ptc->filename);
+    free (ptc);
+}
+
+static void
+_cairo_boilerplate_pdf_force_fallbacks (cairo_surface_t *abstract_surface,
+                                      double            x_pixels_per_inch,
+                                      double            y_pixels_per_inch)
+{
+    pdf_target_closure_t *ptc = cairo_surface_get_user_data (abstract_surface,
+                                                            &pdf_closure_key);
+
+    cairo_paginated_surface_t *paginated;
+    cairo_pdf_surface_t *surface;
+
+    if (ptc->target)
+       abstract_surface = ptc->target;
+
+    paginated = (cairo_paginated_surface_t*) abstract_surface;
+    surface = (cairo_pdf_surface_t*) paginated->target;
+    surface->force_fallbacks = TRUE;
+    cairo_surface_set_fallback_resolution (&paginated->base,
+                                          x_pixels_per_inch,
+                                          y_pixels_per_inch);
+}
+
+static const cairo_boilerplate_target_t targets[] = {
+    {
+       "pdf", "pdf", ".pdf", NULL,
+       CAIRO_SURFACE_TYPE_PDF,
+       CAIRO_TEST_CONTENT_COLOR_ALPHA_FLATTENED, 0,
+       "cairo_pdf_surface_create",
+       _cairo_boilerplate_pdf_create_surface,
+       cairo_surface_create_similar,
+       _cairo_boilerplate_pdf_force_fallbacks,
+       _cairo_boilerplate_pdf_finish_surface,
+       _cairo_boilerplate_pdf_get_image_surface,
+       _cairo_boilerplate_pdf_surface_write_to_png,
+       _cairo_boilerplate_pdf_cleanup,
+       NULL, NULL, FALSE, TRUE, TRUE
+    },
+    {
+       "pdf", "pdf", ".pdf", NULL,
+       CAIRO_SURFACE_TYPE_RECORDING, CAIRO_CONTENT_COLOR, 0,
+       "cairo_pdf_surface_create",
+       _cairo_boilerplate_pdf_create_surface,
+       cairo_surface_create_similar,
+       _cairo_boilerplate_pdf_force_fallbacks,
+       _cairo_boilerplate_pdf_finish_surface,
+       _cairo_boilerplate_pdf_get_image_surface,
+       _cairo_boilerplate_pdf_surface_write_to_png,
+       _cairo_boilerplate_pdf_cleanup,
+       NULL, NULL, FALSE, TRUE, TRUE
+    },
+};
+CAIRO_BOILERPLATE (pdf, targets)
+
+#else
+
+CAIRO_NO_BOILERPLATE (pdf)
+
+#endif
diff --git a/boilerplate/cairo-boilerplate-private.h b/boilerplate/cairo-boilerplate-private.h
new file mode 100644 (file)
index 0000000..a7a2dd0
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Copyright © 2009 Chris Wilson
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#ifndef _CAIRO_BOILERPLATE_PRIVATE_H_
+#define _CAIRO_BOILERPLATE_PRIVATE_H_
+
+#include "cairo-boilerplate.h"
+
+CAIRO_BEGIN_DECLS
+
+void
+_cairo_boilerplate_register_all (void);
+
+void
+_cairo_boilerplate_register_backend (const cairo_boilerplate_target_t *targets,
+                                    unsigned int                      count);
+
+#define CAIRO_BOILERPLATE(name__, targets__) \
+void _register_##name__ (void); \
+void _register_##name__ (void) { \
+    _cairo_boilerplate_register_backend (targets__, \
+                                        sizeof (targets__) / sizeof (targets__[0])); \
+}
+
+#define CAIRO_NO_BOILERPLATE(name__) \
+void _register_##name__ (void); \
+void _register_##name__ (void) { }
+
+CAIRO_END_DECLS
+
+#endif /* _CAIRO_BOILERPLATE_PRIVATE_H_ */
diff --git a/boilerplate/cairo-boilerplate-ps.c b/boilerplate/cairo-boilerplate-ps.c
new file mode 100644 (file)
index 0000000..53c8cb8
--- /dev/null
@@ -0,0 +1,372 @@
+/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/*
+ * Copyright © 2004,2006 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ */
+
+#include "cairo-boilerplate-private.h"
+
+#if CAIRO_CAN_TEST_PS_SURFACE
+
+#include <cairo-ps.h>
+
+#include <cairo-ps-surface-private.h>
+#include <cairo-paginated-surface-private.h>
+
+#if HAVE_SIGNAL_H
+#include <signal.h>
+#endif
+
+#if HAVE_SYS_WAIT_H
+#include <sys/wait.h>
+#endif
+
+#if ! CAIRO_HAS_RECORDING_SURFACE
+#define CAIRO_SURFACE_TYPE_RECORDING CAIRO_INTERNAL_SURFACE_TYPE_RECORDING
+#endif
+
+static const cairo_user_data_key_t ps_closure_key;
+
+typedef struct _ps_target_closure {
+    char               *filename;
+    int                 width;
+    int                 height;
+    cairo_surface_t    *target;
+    cairo_ps_level_t    level;
+} ps_target_closure_t;
+
+static cairo_status_t
+_cairo_boilerplate_ps_surface_set_creation_date (cairo_surface_t *abstract_surface,
+                                                time_t           date)
+{
+    cairo_paginated_surface_t *paginated = (cairo_paginated_surface_t*) abstract_surface;
+    cairo_ps_surface_t *surface;
+
+    if (cairo_surface_get_type (abstract_surface) != CAIRO_SURFACE_TYPE_PS)
+       return CAIRO_STATUS_SURFACE_TYPE_MISMATCH;
+
+    surface = (cairo_ps_surface_t*) paginated->target;
+
+    surface->has_creation_date = TRUE;
+    surface->creation_date = date;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_surface_t *
+_cairo_boilerplate_ps_create_surface (const char               *name,
+                                     cairo_content_t            content,
+                                     cairo_ps_level_t           level,
+                                     double                     width,
+                                     double                     height,
+                                     double                     max_width,
+                                     double                     max_height,
+                                     cairo_boilerplate_mode_t   mode,
+                                     int                        id,
+                                     void                     **closure)
+{
+    ps_target_closure_t *ptc;
+    cairo_surface_t *surface;
+    cairo_status_t status;
+
+    /* Sanitize back to a real cairo_content_t value. */
+    if (content == CAIRO_TEST_CONTENT_COLOR_ALPHA_FLATTENED)
+       content = CAIRO_CONTENT_COLOR_ALPHA;
+
+    *closure = ptc = xmalloc (sizeof (ps_target_closure_t));
+
+    xasprintf (&ptc->filename, "%s.out.ps", name);
+    xunlink (ptc->filename);
+
+    ptc->level = level;
+    ptc->width = ceil (width);
+    ptc->height = ceil (height);
+
+    surface = cairo_ps_surface_create (ptc->filename, width, height);
+    if (cairo_surface_status (surface))
+       goto CLEANUP_FILENAME;
+
+    cairo_ps_surface_restrict_to_level (surface, level);
+    _cairo_boilerplate_ps_surface_set_creation_date (surface, 0);
+    cairo_surface_set_fallback_resolution (surface, 72., 72.);
+
+    if (content == CAIRO_CONTENT_COLOR) {
+       ptc->target = surface;
+       surface = cairo_surface_create_similar (ptc->target,
+                                               CAIRO_CONTENT_COLOR,
+                                               ptc->width, ptc->height);
+       if (cairo_surface_status (surface))
+           goto CLEANUP_TARGET;
+    } else {
+       ptc->target = NULL;
+    }
+
+    status = cairo_surface_set_user_data (surface, &ps_closure_key, ptc, NULL);
+    if (status == CAIRO_STATUS_SUCCESS)
+       return surface;
+
+    cairo_surface_destroy (surface);
+    surface = cairo_boilerplate_surface_create_in_error (status);
+
+  CLEANUP_TARGET:
+    cairo_surface_destroy (ptc->target);
+  CLEANUP_FILENAME:
+    free (ptc->filename);
+    free (ptc);
+    return surface;
+}
+
+static cairo_surface_t *
+_cairo_boilerplate_ps2_create_surface (const char               *name,
+                                      cairo_content_t            content,
+                                      double                     width,
+                                      double                     height,
+                                      double                     max_width,
+                                      double                     max_height,
+                                      cairo_boilerplate_mode_t   mode,
+                                      int                        id,
+                                      void                     **closure)
+{
+    return _cairo_boilerplate_ps_create_surface (name, content,
+                                                CAIRO_PS_LEVEL_2,
+                                                width, height,
+                                                max_width, max_height,
+                                                mode, id,
+                                                closure);
+}
+
+static cairo_surface_t *
+_cairo_boilerplate_ps3_create_surface (const char               *name,
+                                      cairo_content_t            content,
+                                      double                     width,
+                                      double                     height,
+                                      double                     max_width,
+                                      double                     max_height,
+                                      cairo_boilerplate_mode_t   mode,
+                                      int                        id,
+                                      void                     **closure)
+{
+    return _cairo_boilerplate_ps_create_surface (name, content,
+                                                CAIRO_PS_LEVEL_3,
+                                                width, height,
+                                                max_width, max_height,
+                                                mode, id,
+                                                closure);
+}
+
+static cairo_status_t
+_cairo_boilerplate_ps_finish_surface (cairo_surface_t *surface)
+{
+    ps_target_closure_t *ptc = cairo_surface_get_user_data (surface,
+                                                           &ps_closure_key);
+    cairo_status_t status;
+
+    /* Both surface and ptc->target were originally created at the
+     * same dimensions. We want a 1:1 copy here, so we first clear any
+     * device offset on surface.
+     *
+     * In a more realistic use case of device offsets, the target of
+     * this copying would be of a different size than the source, and
+     * the offset would be desirable during the copy operation. */
+    cairo_surface_set_device_offset (surface, 0, 0);
+
+    if (ptc->target) {
+       cairo_t *cr;
+
+       cr = cairo_create (ptc->target);
+       cairo_set_source_surface (cr, surface, 0, 0);
+       cairo_paint (cr);
+       cairo_show_page (cr);
+       status = cairo_status (cr);
+       cairo_destroy (cr);
+
+       if (status)
+           return status;
+
+       cairo_surface_finish (surface);
+       status = cairo_surface_status (surface);
+       if (status)
+           return status;
+
+       surface = ptc->target;
+    }
+
+    cairo_surface_finish (surface);
+    return cairo_surface_status (surface);
+}
+
+static cairo_status_t
+_cairo_boilerplate_ps_surface_write_to_png (cairo_surface_t *surface,
+                                           const char      *filename)
+{
+    ps_target_closure_t *ptc = cairo_surface_get_user_data (surface,
+                                                           &ps_closure_key);
+    char    command[4096];
+    int exitstatus;
+
+    sprintf (command, "gs -q -r72 -g%dx%d -dSAFER -dBATCH -dNOPAUSE -sDEVICE=pngalpha -sOutputFile=%s %s %s",
+            ptc->width, ptc->height, filename,
+            ptc->level == CAIRO_PS_LEVEL_2 ? "-c 2 .setlanguagelevel -f" : "",
+            ptc->filename);
+    exitstatus = system (command);
+#if _XOPEN_SOURCE && HAVE_SIGNAL_H
+    if (WIFSIGNALED (exitstatus))
+       raise (WTERMSIG (exitstatus));
+#endif
+    if (exitstatus)
+       return CAIRO_STATUS_WRITE_ERROR;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_surface_t *
+_cairo_boilerplate_ps_get_image_surface (cairo_surface_t *surface,
+                                        int              page,
+                                        int              width,
+                                        int              height)
+{
+    ps_target_closure_t *ptc = cairo_surface_get_user_data (surface,
+                                                           &ps_closure_key);
+    char *filename;
+    cairo_status_t status;
+
+    if (page == 0)
+       xasprintf (&filename, "%s.png", ptc->filename);
+    else
+       xasprintf (&filename, "%s-%%05d.png", ptc->filename);
+    status = _cairo_boilerplate_ps_surface_write_to_png (surface, filename);
+    if (status)
+       return cairo_boilerplate_surface_create_in_error (status);
+
+    if (page != 0) {
+       free (filename);
+       xasprintf (&filename, "%s-%05d.png", ptc->filename, page);
+    }
+    surface = cairo_boilerplate_get_image_surface_from_png (filename,
+                                                           width,
+                                                           height,
+                                                           ptc->target == NULL);
+
+    remove (filename);
+    free (filename);
+
+    return surface;
+}
+
+static void
+_cairo_boilerplate_ps_cleanup (void *closure)
+{
+    ps_target_closure_t *ptc = closure;
+    if (ptc->target) {
+       cairo_surface_finish (ptc->target);
+       cairo_surface_destroy (ptc->target);
+    }
+    free (ptc->filename);
+    free (ptc);
+}
+
+static void
+_cairo_boilerplate_ps_force_fallbacks (cairo_surface_t *abstract_surface,
+                                      double            x_pixels_per_inch,
+                                      double            y_pixels_per_inch)
+{
+    ps_target_closure_t *ptc = cairo_surface_get_user_data (abstract_surface,
+                                                           &ps_closure_key);
+
+    cairo_paginated_surface_t *paginated;
+    cairo_ps_surface_t *surface;
+
+    if (ptc->target)
+       abstract_surface = ptc->target;
+
+    paginated = (cairo_paginated_surface_t*) abstract_surface;
+    surface = (cairo_ps_surface_t*) paginated->target;
+    surface->force_fallbacks = TRUE;
+    cairo_surface_set_fallback_resolution (&paginated->base,
+                                          x_pixels_per_inch,
+                                          y_pixels_per_inch);
+}
+
+static const cairo_boilerplate_target_t targets[] = {
+    {
+       "ps2", "ps", ".ps", NULL,
+       CAIRO_SURFACE_TYPE_PS,
+       CAIRO_TEST_CONTENT_COLOR_ALPHA_FLATTENED, 0,
+       "cairo_ps_surface_create",
+       _cairo_boilerplate_ps2_create_surface,
+       cairo_surface_create_similar,
+       _cairo_boilerplate_ps_force_fallbacks,
+       _cairo_boilerplate_ps_finish_surface,
+       _cairo_boilerplate_ps_get_image_surface,
+       _cairo_boilerplate_ps_surface_write_to_png,
+       _cairo_boilerplate_ps_cleanup,
+       NULL, NULL, FALSE, TRUE, TRUE
+    },
+    {
+       "ps2", "ps", ".ps", NULL,
+       CAIRO_SURFACE_TYPE_RECORDING, CAIRO_CONTENT_COLOR, 0,
+       "cairo_ps_surface_create",
+       _cairo_boilerplate_ps2_create_surface,
+       cairo_surface_create_similar,
+       _cairo_boilerplate_ps_force_fallbacks,
+       _cairo_boilerplate_ps_finish_surface,
+       _cairo_boilerplate_ps_get_image_surface,
+       _cairo_boilerplate_ps_surface_write_to_png,
+       _cairo_boilerplate_ps_cleanup,
+       NULL, NULL, FALSE, TRUE, TRUE
+    },
+    {
+       "ps3", "ps", ".ps", NULL,
+       CAIRO_SURFACE_TYPE_PS,
+       CAIRO_TEST_CONTENT_COLOR_ALPHA_FLATTENED, 0,
+       "cairo_ps_surface_create",
+       _cairo_boilerplate_ps3_create_surface,
+       cairo_surface_create_similar,
+       _cairo_boilerplate_ps_force_fallbacks,
+       _cairo_boilerplate_ps_finish_surface,
+       _cairo_boilerplate_ps_get_image_surface,
+       _cairo_boilerplate_ps_surface_write_to_png,
+       _cairo_boilerplate_ps_cleanup,
+       NULL, NULL, FALSE, TRUE, TRUE
+    },
+    {
+       "ps3", "ps", ".ps", NULL,
+       CAIRO_SURFACE_TYPE_RECORDING, CAIRO_CONTENT_COLOR, 0,
+       "cairo_ps_surface_create",
+       _cairo_boilerplate_ps3_create_surface,
+       cairo_surface_create_similar,
+       _cairo_boilerplate_ps_force_fallbacks,
+       _cairo_boilerplate_ps_finish_surface,
+       _cairo_boilerplate_ps_get_image_surface,
+       _cairo_boilerplate_ps_surface_write_to_png,
+       _cairo_boilerplate_ps_cleanup,
+       NULL, NULL, FALSE, TRUE, TRUE
+    },
+};
+CAIRO_BOILERPLATE (ps, targets)
+
+#else
+
+CAIRO_NO_BOILERPLATE (ps)
+
+#endif
diff --git a/boilerplate/cairo-boilerplate-qt.cpp b/boilerplate/cairo-boilerplate-qt.cpp
new file mode 100644 (file)
index 0000000..bbeea4a
--- /dev/null
@@ -0,0 +1,115 @@
+/* Cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Chris Wilson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Chris Wilson.
+ */
+
+#include "cairo-boilerplate-private.h"
+
+#include <cairo-qt.h>
+
+#include <qapplication.h>
+#include <X11/Xlib.h>
+
+typedef struct _qt_closure {
+    Display *dpy;
+    QApplication *app;
+} qt_closure_t;
+
+static void
+_cairo_boilerplate_qt_cleanup (void *closure)
+{
+    qt_closure_t *qtc = (qt_closure_t *) closure;
+
+    delete qtc->app;
+    XCloseDisplay (qtc->dpy);
+    free (qtc);
+}
+
+static cairo_surface_t *
+_cairo_boilerplate_qt_create_surface (const char               *name,
+                                     cairo_content_t            content,
+                                     double                     width,
+                                     double                     height,
+                                     double                     max_width,
+                                     double                     max_height,
+                                     cairo_boilerplate_mode_t   mode,
+                                     int                        id,
+                                     void                     **closure)
+{
+    qt_closure_t *qtc;
+
+    qtc = (qt_closure_t *) xcalloc (1, sizeof (qt_closure_t));
+    qtc->dpy = XOpenDisplay (NULL);
+    if (qtc->dpy == NULL) {
+       free (qtc);
+       return NULL;
+    }
+
+    if (mode == CAIRO_BOILERPLATE_MODE_TEST)
+       XSynchronize (qtc->dpy, True);
+
+    qtc->app = new QApplication (qtc->dpy);
+    *closure = qtc;
+    return cairo_qt_surface_create_with_qpixmap (content, width, height);
+}
+
+static void
+_cairo_boilerplate_qt_synchronize (void *closure)
+{
+    qt_closure_t *qtc = (qt_closure_t *) closure;
+
+    qtc->app->flush (); /* not sure if this is sufficient */
+}
+
+static const cairo_boilerplate_target_t targets[] = {
+    {
+       "qt", "qt", NULL, NULL,
+       CAIRO_SURFACE_TYPE_QT, CAIRO_CONTENT_COLOR_ALPHA, 0,
+       "cairo_qt_surface_create",
+       _cairo_boilerplate_qt_create_surface,
+       NULL, NULL,
+       _cairo_boilerplate_get_image_surface,
+       cairo_surface_write_to_png,
+       _cairo_boilerplate_qt_cleanup
+    },
+    {
+       "qt", "qt", NULL, NULL,
+       CAIRO_SURFACE_TYPE_QT, CAIRO_CONTENT_COLOR, 0,
+       "cairo_qt_surface_create",
+       _cairo_boilerplate_qt_create_surface,
+       NULL, NULL,
+       _cairo_boilerplate_get_image_surface,
+       cairo_surface_write_to_png,
+       _cairo_boilerplate_qt_cleanup
+    },
+};
+extern "C" {
+CAIRO_BOILERPLATE (qt, targets)
+}
diff --git a/boilerplate/cairo-boilerplate-quartz.c b/boilerplate/cairo-boilerplate-quartz.c
new file mode 100644 (file)
index 0000000..6b9697a
--- /dev/null
@@ -0,0 +1,77 @@
+/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/*
+ * Copyright © 2004,2007 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ */
+
+#include "cairo-boilerplate-private.h"
+
+#include <cairo-quartz.h>
+
+static cairo_surface_t *
+_cairo_boilerplate_quartz_create_surface (const char               *name,
+                                         cairo_content_t            content,
+                                         double                     width,
+                                         double                     height,
+                                         double                     max_width,
+                                         double                     max_height,
+                                         cairo_boilerplate_mode_t   mode,
+                                         int                        id,
+                                         void                     **closure)
+{
+    cairo_format_t format;
+
+    format = cairo_boilerplate_format_from_content (content);
+
+    *closure = NULL;
+
+    return cairo_quartz_surface_create (format, width, height);
+}
+
+static const cairo_boilerplate_target_t targets[] = {
+    {
+       "quartz", "quartz", NULL, NULL,
+       CAIRO_SURFACE_TYPE_QUARTZ, CAIRO_CONTENT_COLOR_ALPHA, 0,
+       "cairo_quartz_surface_create",
+       _cairo_boilerplate_quartz_create_surface,
+       cairo_surface_create_similar,
+       NULL, NULL,
+       _cairo_boilerplate_get_image_surface,
+       cairo_surface_write_to_png,
+       NULL, NULL, NULL,
+       TRUE, FALSE, FALSE
+    },
+    {
+       "quartz", "quartz", NULL, NULL,
+       CAIRO_SURFACE_TYPE_QUARTZ, CAIRO_CONTENT_COLOR, 0,
+       "cairo_quartz_surface_create",
+       _cairo_boilerplate_quartz_create_surface,
+       cairo_surface_create_similar,
+       NULL, NULL,
+       _cairo_boilerplate_get_image_surface,
+       cairo_surface_write_to_png,
+       NULL, NULL, NULL,
+        FALSE, FALSE, FALSE
+    },
+};
+CAIRO_BOILERPLATE (quartz, targets)
diff --git a/boilerplate/cairo-boilerplate-scaled-font.h b/boilerplate/cairo-boilerplate-scaled-font.h
new file mode 100644 (file)
index 0000000..a7ba2fe
--- /dev/null
@@ -0,0 +1,34 @@
+/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/*
+ * Copyright © 2007 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Behdad Esfahbod <behdad@behdad.org>
+ */
+
+#ifndef _CAIRO_BOILERPLATE_SCALED_FONT_H_
+#define _CAIRO_BOILERPLATE_SCALED_FONT_H_
+
+void
+cairo_boilerplate_scaled_font_set_max_glyphs_cached (cairo_scaled_font_t *scaled_font,
+                                                    int                  max_glyphs);
+
+#endif
diff --git a/boilerplate/cairo-boilerplate-script.c b/boilerplate/cairo-boilerplate-script.c
new file mode 100644 (file)
index 0000000..f031744
--- /dev/null
@@ -0,0 +1,142 @@
+/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/*
+ * Copyright © Chris Wilson
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Chris Wilson not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Chris Wilson makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * CHRIS WILSON DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL CHRIS WILSON BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-boilerplate-private.h"
+
+#include "cairo-script.h"
+
+static cairo_user_data_key_t script_closure_key;
+
+typedef struct _script_target_closure {
+    char               *filename;
+    double              width;
+    double              height;
+} script_target_closure_t;
+
+static cairo_surface_t *
+_cairo_boilerplate_script_create_surface (const char               *name,
+                                         cairo_content_t            content,
+                                         double                     width,
+                                         double                     height,
+                                         double                     max_width,
+                                         double                     max_height,
+                                         cairo_boilerplate_mode_t   mode,
+                                         int                        id,
+                                         void                     **closure)
+{
+    script_target_closure_t *ptc;
+    cairo_device_t *ctx;
+    cairo_surface_t *surface;
+    cairo_status_t status;
+
+    *closure = ptc = xmalloc (sizeof (script_target_closure_t));
+
+    ptc->width = width;
+    ptc->height = height;
+
+    xasprintf (&ptc->filename, "%s.out.cs", name);
+    xunlink (ptc->filename);
+
+    ctx = cairo_script_create (ptc->filename);
+    surface = cairo_script_surface_create (ctx, content, width, height);
+    cairo_device_destroy (ctx);
+
+    status = cairo_surface_set_user_data (surface,
+                                         &script_closure_key, ptc, NULL);
+    if (status == CAIRO_STATUS_SUCCESS)
+       return surface;
+
+    cairo_surface_destroy (surface);
+    surface = cairo_boilerplate_surface_create_in_error (status);
+
+    free (ptc->filename);
+    free (ptc);
+    return surface;
+}
+
+static cairo_status_t
+_cairo_boilerplate_script_finish_surface (cairo_surface_t *surface)
+{
+    cairo_surface_finish (surface);
+    return cairo_surface_status (surface);
+}
+
+static cairo_status_t
+_cairo_boilerplate_script_surface_write_to_png (cairo_surface_t *surface,
+                                               const char      *filename)
+{
+    return CAIRO_STATUS_WRITE_ERROR;
+}
+
+static cairo_surface_t *
+_cairo_boilerplate_script_convert_to_image (cairo_surface_t *surface,
+                                           int              page)
+{
+    script_target_closure_t *ptc = cairo_surface_get_user_data (surface,
+                                                               &script_closure_key);
+    return cairo_boilerplate_convert_to_image (ptc->filename, page);
+}
+
+static cairo_surface_t *
+_cairo_boilerplate_script_get_image_surface (cairo_surface_t *surface,
+                                            int              page,
+                                            int              width,
+                                            int              height)
+{
+    cairo_surface_t *image;
+
+    image = _cairo_boilerplate_script_convert_to_image (surface, page);
+    cairo_surface_set_device_offset (image,
+                                    cairo_image_surface_get_width (image) - width,
+                                    cairo_image_surface_get_height (image) - height);
+    surface = _cairo_boilerplate_get_image_surface (image, 0, width, height);
+    cairo_surface_destroy (image);
+
+    return surface;
+}
+
+static void
+_cairo_boilerplate_script_cleanup (void *closure)
+{
+    script_target_closure_t *ptc = closure;
+    free (ptc->filename);
+    free (ptc);
+}
+
+static const cairo_boilerplate_target_t target[] = {{
+    "script", "script", ".cs", NULL,
+    CAIRO_SURFACE_TYPE_SCRIPT, CAIRO_CONTENT_COLOR_ALPHA, 0,
+    "cairo_script_surface_create",
+    _cairo_boilerplate_script_create_surface,
+    cairo_surface_create_similar,
+    NULL,
+    _cairo_boilerplate_script_finish_surface,
+    _cairo_boilerplate_script_get_image_surface,
+    _cairo_boilerplate_script_surface_write_to_png,
+    _cairo_boilerplate_script_cleanup,
+    NULL, NULL, FALSE, FALSE, FALSE
+}};
+CAIRO_BOILERPLATE (script, target)
diff --git a/boilerplate/cairo-boilerplate-skia.c b/boilerplate/cairo-boilerplate-skia.c
new file mode 100644 (file)
index 0000000..2bd31b0
--- /dev/null
@@ -0,0 +1,56 @@
+
+#include "cairo-boilerplate-private.h"
+
+#include <cairo-skia.h>
+
+static cairo_surface_t *
+_cairo_boilerplate_skia_create_surface (const char               *name,
+                                       cairo_content_t            content,
+                                       double                     width,
+                                       double                     height,
+                                       double                     max_width,
+                                       double                     max_height,
+                                       cairo_boilerplate_mode_t   mode,
+                                       int                        id,
+                                       void                     **closure)
+{
+    cairo_format_t format;
+
+    *closure = NULL;
+
+    if (content == CAIRO_CONTENT_COLOR_ALPHA) {
+       format = CAIRO_FORMAT_ARGB32;
+    } else if (content == CAIRO_CONTENT_COLOR) {
+       format = CAIRO_FORMAT_RGB24;
+    } else {
+       return NULL;
+    }
+
+    return cairo_skia_surface_create (format, width, height);
+}
+
+static const cairo_boilerplate_target_t targets[] = {
+    {
+       "skia", "skia", NULL, NULL,
+       CAIRO_SURFACE_TYPE_SKIA, CAIRO_CONTENT_COLOR_ALPHA, 0,
+       "cairo_skia_surface_create",
+       _cairo_boilerplate_skia_create_surface,
+       cairo_surface_create_similar,
+       NULL, NULL,
+       _cairo_boilerplate_get_image_surface,
+       cairo_surface_write_to_png,
+       NULL, NULL, NULL, TRUE, FALSE, FALSE
+    },
+    {
+       "skia", "skia", NULL, NULL,
+       CAIRO_SURFACE_TYPE_SKIA, CAIRO_CONTENT_COLOR, 0,
+       "cairo_skia_surface_create",
+       _cairo_boilerplate_skia_create_surface,
+       cairo_surface_create_similar,
+       NULL, NULL,
+       _cairo_boilerplate_get_image_surface,
+       cairo_surface_write_to_png,
+       NULL, NULL, NULL, FALSE, FALSE, FALSE
+    },
+};
+CAIRO_BOILERPLATE (skia, targets)
diff --git a/boilerplate/cairo-boilerplate-svg.c b/boilerplate/cairo-boilerplate-svg.c
new file mode 100644 (file)
index 0000000..310dc5d
--- /dev/null
@@ -0,0 +1,347 @@
+/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/*
+ * Copyright © 2004,2006 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ */
+
+#include "cairo-boilerplate-private.h"
+
+#if CAIRO_CAN_TEST_SVG_SURFACE
+
+#include <cairo-svg.h>
+#include <cairo-svg-surface-private.h>
+#include <cairo-paginated-surface-private.h>
+
+#if HAVE_SIGNAL_H
+#include <stdlib.h>
+#include <signal.h>
+#endif
+
+#if HAVE_SYS_WAIT_H
+#include <sys/wait.h>
+#endif
+
+#if ! CAIRO_HAS_RECORDING_SURFACE
+#define CAIRO_SURFACE_TYPE_RECORDING CAIRO_INTERNAL_SURFACE_TYPE_RECORDING
+#endif
+
+static const cairo_user_data_key_t svg_closure_key;
+
+typedef struct _svg_target_closure {
+    char    *filename;
+    int     width, height;
+    cairo_surface_t    *target;
+} svg_target_closure_t;
+
+static cairo_surface_t *
+_cairo_boilerplate_svg_create_surface (const char               *name,
+                                      cairo_content_t            content,
+                                      cairo_svg_version_t        version,
+                                      double                     width,
+                                      double                     height,
+                                      double                     max_width,
+                                      double                     max_height,
+                                      cairo_boilerplate_mode_t   mode,
+                                      int                        id,
+                                      void                     **closure)
+{
+    svg_target_closure_t *ptc;
+    cairo_surface_t *surface;
+    cairo_status_t status;
+
+    *closure = ptc = xmalloc (sizeof (svg_target_closure_t));
+
+    ptc->width = ceil (width);
+    ptc->height = ceil (height);
+
+    xasprintf (&ptc->filename, "%s.out.svg", name);
+    xunlink (ptc->filename);
+
+    surface = cairo_svg_surface_create (ptc->filename, width, height);
+    if (cairo_surface_status (surface))
+       goto CLEANUP_FILENAME;
+
+    cairo_svg_surface_restrict_to_version (surface, version);
+    cairo_surface_set_fallback_resolution (surface, 72., 72.);
+
+    if (content == CAIRO_CONTENT_COLOR) {
+       ptc->target = surface;
+       surface = cairo_surface_create_similar (ptc->target,
+                                               CAIRO_CONTENT_COLOR,
+                                               ptc->width, ptc->height);
+       if (cairo_surface_status (surface))
+           goto CLEANUP_TARGET;
+    } else
+       ptc->target = NULL;
+
+    status = cairo_surface_set_user_data (surface, &svg_closure_key, ptc, NULL);
+    if (status == CAIRO_STATUS_SUCCESS)
+       return surface;
+
+    cairo_surface_destroy (surface);
+    surface = cairo_boilerplate_surface_create_in_error (status);
+
+  CLEANUP_TARGET:
+    cairo_surface_destroy (ptc->target);
+  CLEANUP_FILENAME:
+    free (ptc->filename);
+    free (ptc);
+    return surface;
+}
+
+static cairo_surface_t *
+_cairo_boilerplate_svg11_create_surface (const char               *name,
+                                        cairo_content_t            content,
+                                        double                     width,
+                                        double                     height,
+                                        double                     max_width,
+                                        double                     max_height,
+                                        cairo_boilerplate_mode_t   mode,
+                                        int                        id,
+                                        void                     **closure)
+{
+    /* current default, but be explicit in case the default changes */
+    return _cairo_boilerplate_svg_create_surface (name, content,
+                                                 CAIRO_SVG_VERSION_1_1,
+                                                 width, height,
+                                                 max_width, max_height,
+                                                 mode, id,
+                                                 closure);
+}
+
+static cairo_surface_t *
+_cairo_boilerplate_svg12_create_surface (const char               *name,
+                                        cairo_content_t            content,
+                                        double                     width,
+                                        double                     height,
+                                        double                     max_width,
+                                        double                     max_height,
+                                        cairo_boilerplate_mode_t   mode,
+                                        int                        id,
+                                        void                     **closure)
+{
+    return _cairo_boilerplate_svg_create_surface (name, content,
+                                                 CAIRO_SVG_VERSION_1_2,
+                                                 width, height,
+                                                 max_width, max_height,
+                                                 mode, id,
+                                                 closure);
+}
+
+static cairo_status_t
+_cairo_boilerplate_svg_finish_surface (cairo_surface_t *surface)
+{
+    svg_target_closure_t *ptc = cairo_surface_get_user_data (surface,
+                                                            &svg_closure_key);
+    cairo_status_t status;
+
+    /* Both surface and ptc->target were originally created at the
+     * same dimensions. We want a 1:1 copy here, so we first clear any
+     * device offset on surface.
+     *
+     * In a more realistic use case of device offsets, the target of
+     * this copying would be of a different size than the source, and
+     * the offset would be desirable during the copy operation. */
+    cairo_surface_set_device_offset (surface, 0, 0);
+
+    if (ptc->target) {
+       cairo_t *cr;
+       cr = cairo_create (ptc->target);
+       cairo_set_source_surface (cr, surface, 0, 0);
+       cairo_paint (cr);
+       cairo_show_page (cr);
+       status = cairo_status (cr);
+       cairo_destroy (cr);
+
+       if (status)
+           return status;
+
+       cairo_surface_finish (surface);
+       status = cairo_surface_status (surface);
+       if (status)
+           return status;
+
+       surface = ptc->target;
+    }
+
+    cairo_surface_finish (surface);
+    status = cairo_surface_status (surface);
+    if (status)
+       return status;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_boilerplate_svg_surface_write_to_png (cairo_surface_t *surface,
+                                            const char      *filename)
+{
+    svg_target_closure_t *ptc = cairo_surface_get_user_data (surface,
+                                                            &svg_closure_key);
+    char    command[4096];
+    int exitstatus;
+
+    sprintf (command, "./svg2png %s %s",
+            ptc->filename, filename);
+
+    exitstatus = system (command);
+#if _XOPEN_SOURCE && HAVE_SIGNAL_H
+    if (WIFSIGNALED (exitstatus))
+       raise (WTERMSIG (exitstatus));
+#endif
+    if (exitstatus)
+       return CAIRO_STATUS_WRITE_ERROR;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_surface_t *
+_cairo_boilerplate_svg_convert_to_image (cairo_surface_t *surface)
+{
+    svg_target_closure_t *ptc = cairo_surface_get_user_data (surface,
+                                                            &svg_closure_key);
+
+    return cairo_boilerplate_convert_to_image (ptc->filename, 0);
+}
+
+static cairo_surface_t *
+_cairo_boilerplate_svg_get_image_surface (cairo_surface_t *surface,
+                                         int              page,
+                                         int              width,
+                                         int              height)
+{
+    cairo_surface_t *image;
+
+    if (page != 0)
+       return cairo_boilerplate_surface_create_in_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+
+    image = _cairo_boilerplate_svg_convert_to_image (surface);
+    cairo_surface_set_device_offset (image,
+                                    cairo_image_surface_get_width (image) - width,
+                                    cairo_image_surface_get_height (image) - height);
+    surface = _cairo_boilerplate_get_image_surface (image, 0, width, height);
+    cairo_surface_destroy (image);
+
+    return surface;
+}
+
+static void
+_cairo_boilerplate_svg_cleanup (void *closure)
+{
+    svg_target_closure_t *ptc = closure;
+    if (ptc->target != NULL) {
+       cairo_surface_finish (ptc->target);
+       cairo_surface_destroy (ptc->target);
+    }
+    free (ptc->filename);
+    free (ptc);
+}
+
+static void
+_cairo_boilerplate_svg_force_fallbacks (cairo_surface_t *abstract_surface,
+                                      double            x_pixels_per_inch,
+                                      double            y_pixels_per_inch)
+{
+    svg_target_closure_t *ptc = cairo_surface_get_user_data (abstract_surface,
+                                                            &svg_closure_key);
+
+    cairo_paginated_surface_t *paginated;
+    cairo_svg_surface_t *surface;
+
+    if (ptc->target)
+       abstract_surface = ptc->target;
+
+    paginated = (cairo_paginated_surface_t*) abstract_surface;
+    surface = (cairo_svg_surface_t*) paginated->target;
+    surface->force_fallbacks = TRUE;
+    cairo_surface_set_fallback_resolution (&paginated->base,
+                                          x_pixels_per_inch,
+                                          y_pixels_per_inch);
+}
+
+static const cairo_boilerplate_target_t targets[] = {
+    /* It seems we should be able to round-trip SVG content perfectly
+     * through librsvg and cairo, but for some mysterious reason, some
+     * systems get an error of 1 for some pixels on some of the text
+     * tests. XXX: I'd still like to chase these down at some point.
+     * For now just set the svg error tolerance to 1. */
+    {
+       "svg11", "svg", NULL, NULL,
+       CAIRO_SURFACE_TYPE_SVG, CAIRO_CONTENT_COLOR_ALPHA, 1,
+       "cairo_svg_surface_create",
+       _cairo_boilerplate_svg11_create_surface,
+       cairo_surface_create_similar,
+       _cairo_boilerplate_svg_force_fallbacks,
+       _cairo_boilerplate_svg_finish_surface,
+       _cairo_boilerplate_svg_get_image_surface,
+       _cairo_boilerplate_svg_surface_write_to_png,
+       _cairo_boilerplate_svg_cleanup,
+       NULL, NULL, FALSE, TRUE, TRUE
+    },
+    {
+       "svg11", "svg", NULL, NULL,
+       CAIRO_SURFACE_TYPE_RECORDING, CAIRO_CONTENT_COLOR, 1,
+       "cairo_svg_surface_create",
+       _cairo_boilerplate_svg11_create_surface,
+       cairo_surface_create_similar,
+       _cairo_boilerplate_svg_force_fallbacks,
+       _cairo_boilerplate_svg_finish_surface,
+       _cairo_boilerplate_svg_get_image_surface,
+       _cairo_boilerplate_svg_surface_write_to_png,
+       _cairo_boilerplate_svg_cleanup,
+       NULL, NULL, FALSE, TRUE, TRUE
+    },
+    {
+       "svg12", "svg", NULL, NULL,
+       CAIRO_SURFACE_TYPE_SVG, CAIRO_CONTENT_COLOR_ALPHA, 1,
+       "cairo_svg_surface_create",
+       _cairo_boilerplate_svg12_create_surface,
+       cairo_surface_create_similar,
+       _cairo_boilerplate_svg_force_fallbacks,
+       _cairo_boilerplate_svg_finish_surface,
+       _cairo_boilerplate_svg_get_image_surface,
+       _cairo_boilerplate_svg_surface_write_to_png,
+       _cairo_boilerplate_svg_cleanup,
+       NULL, NULL, FALSE, TRUE, TRUE
+    },
+    {
+       "svg12", "svg", NULL, NULL,
+       CAIRO_SURFACE_TYPE_RECORDING, CAIRO_CONTENT_COLOR, 1,
+       "cairo_svg_surface_create",
+       _cairo_boilerplate_svg12_create_surface,
+       cairo_surface_create_similar,
+       _cairo_boilerplate_svg_force_fallbacks,
+       _cairo_boilerplate_svg_finish_surface,
+       _cairo_boilerplate_svg_get_image_surface,
+       _cairo_boilerplate_svg_surface_write_to_png,
+       _cairo_boilerplate_svg_cleanup,
+       NULL, NULL, FALSE, TRUE, TRUE
+    },
+};
+CAIRO_BOILERPLATE (svg, targets)
+
+#else
+
+CAIRO_NO_BOILERPLATE (svg)
+
+#endif
diff --git a/boilerplate/cairo-boilerplate-system.c b/boilerplate/cairo-boilerplate-system.c
new file mode 100644 (file)
index 0000000..ec23341
--- /dev/null
@@ -0,0 +1,166 @@
+/*
+ * Copyright © 2004 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ */
+
+#define _GNU_SOURCE 1 /* for vasprintf */
+
+#include "cairo-boilerplate.h"
+#include "cairo-boilerplate-system.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <errno.h>
+
+void *
+xmalloc (size_t size)
+{
+    void *buf;
+
+    if (size == 0)
+       return NULL;
+
+    buf = malloc (size);
+    if (buf == NULL) {
+       fprintf (stderr, "Error: Out of memory. Exiting.\n");
+       exit (1);
+    }
+
+    return buf;
+}
+
+void *
+xcalloc (size_t nmemb,
+        size_t size)
+{
+    void *buf;
+
+    if (nmemb == 0 || size == 0)
+       return NULL;
+
+    buf = calloc (nmemb, size);
+    if (buf == NULL) {
+       fprintf (stderr, "Error: Out of memory. Exiting\n");
+       exit (1);
+    }
+
+    return buf;
+}
+
+void *
+xrealloc (void  *buf,
+         size_t  size)
+{
+    buf = realloc (buf, size);
+    if (buf == NULL && size != 0) {
+       fprintf (stderr, "Error: Out of memory. Exiting\n");
+       exit (1);
+    }
+
+    return buf;
+}
+
+void
+xasprintf (char       **strp,
+          const char  *fmt,
+                       ...)
+{
+#ifdef HAVE_VASPRINTF
+    va_list va;
+    int ret;
+
+    va_start (va, fmt);
+    ret = vasprintf (strp, fmt, va);
+    va_end (va);
+
+    if (ret < 0) {
+       fprintf (stderr, "Error: Out of memory. Exiting.\n");
+       exit (1);
+    }
+#else /* !HAVE_VASNPRINTF */
+#define BUF_SIZE 1024
+    va_list va;
+    char buffer[BUF_SIZE];
+    int ret, len;
+
+    va_start (va, fmt);
+    ret = vsnprintf (buffer, sizeof (buffer), fmt, va);
+    va_end (va);
+
+    if (ret < 0) {
+       fprintf (stderr, "Failure in vsnprintf\n");
+       exit (1);
+    }
+
+    len = (ret + sizeof (int)) & -sizeof (int);
+    *strp = malloc (len);
+    if (*strp == NULL) {
+       fprintf (stderr, "Out of memory\n");
+       exit (1);
+    }
+
+    if ((unsigned) ret < sizeof (buffer)) {
+       memcpy (*strp, buffer, ret);
+    } else {
+       va_start (va, fmt);
+       ret = vsnprintf (*strp, len, fmt, va);
+       va_end (va);
+
+       if (ret >= len) {
+           free (*strp);
+           fprintf (stderr, "Overflowed dynamic buffer\n");
+           exit (1);
+       }
+    }
+    memset (*strp + ret, 0, len-ret);
+#endif /* !HAVE_VASNPRINTF */
+}
+
+void
+xunlink (const char *pathname)
+{
+    if (unlink (pathname) < 0 && errno != ENOENT) {
+       fprintf (stderr, "Error: Cannot remove %s: %s\n",
+                              pathname, strerror (errno));
+       exit (1);
+    }
+}
+
+char *
+xstrdup (const char *str)
+{
+    if (str == NULL)
+       return NULL;
+
+    str = strdup (str);
+    if (str == NULL) {
+       fprintf (stderr, "Error: Out of memory. Exiting.\n");
+       exit (1);
+    }
+
+    return (char *) str;
+}
diff --git a/boilerplate/cairo-boilerplate-system.h b/boilerplate/cairo-boilerplate-system.h
new file mode 100644 (file)
index 0000000..2816567
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * Copyright © 2004 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ */
+
+#ifndef _XMALLOC_H_
+#define _XMALLOC_H_
+
+#include "cairo-boilerplate.h"
+
+#define xmalloc cairo_boilerplate_xmalloc
+void *
+xmalloc (size_t size);
+
+#define xcalloc cairo_boilerplate_xcalloc
+void *
+xcalloc (size_t nmemb,
+        size_t size);
+
+#define xrealloc cairo_boilerplate_xrealloc
+void *
+xrealloc (void  *buf,
+         size_t  size);
+
+#define xasprintf cairo_boilerplate_xasprintf
+void
+xasprintf (char       **strp,
+          const char  *fmt,
+                       ...) CAIRO_BOILERPLATE_PRINTF_FORMAT(2, 3);
+
+#define xunlink cairo_boilerplate_xunlink
+void
+xunlink (const char *path);
+
+#define xstrdup cairo_boilerplate_xstrdup
+char *
+xstrdup (const char *str);
+
+#endif
diff --git a/boilerplate/cairo-boilerplate-test-surfaces.c b/boilerplate/cairo-boilerplate-test-surfaces.c
new file mode 100644 (file)
index 0000000..73f2503
--- /dev/null
@@ -0,0 +1,471 @@
+/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/*
+ * Copyright © 2004,2006 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ */
+
+#include "../cairo-version.h"
+
+#include "cairo-boilerplate-private.h"
+
+#include <cairo-types-private.h>
+
+#include <test-compositor-surface.h>
+#include <test-null-compositor-surface.h>
+#if CAIRO_HAS_TEST_PAGINATED_SURFACE
+#include <test-paginated-surface.h>
+#endif
+
+static cairo_surface_t *
+_cairo_boilerplate_test_base_compositor_create_surface (const char                *name,
+                                                       cairo_content_t     content,
+                                                       double              width,
+                                                       double              height,
+                                                       double              max_width,
+                                                       double              max_height,
+                                                       cairo_boilerplate_mode_t mode,
+                                                       int                         id,
+                                                       void              **closure)
+{
+    *closure = NULL;
+    return _cairo_test_base_compositor_surface_create (content, ceil (width), ceil (height));
+}
+
+
+static cairo_surface_t *
+_cairo_boilerplate_test_fallback_compositor_create_surface (const char            *name,
+                                                           cairo_content_t         content,
+                                                           double                  width,
+                                                           double                  height,
+                                                           double                  max_width,
+                                                           double                  max_height,
+                                                           cairo_boilerplate_mode_t mode,
+                                                           int                     id,
+                                                           void                  **closure)
+{
+    *closure = NULL;
+    return _cairo_test_fallback_compositor_surface_create (content, ceil (width), ceil (height));
+}
+
+static cairo_surface_t *
+_cairo_boilerplate_test_mask_compositor_create_surface (const char                *name,
+                                                        cairo_content_t            content,
+                                                        double                     width,
+                                                        double                     height,
+                                                        double                     max_width,
+                                                        double                     max_height,
+                                                        cairo_boilerplate_mode_t   mode,
+                                                        int                        id,
+                                                        void                     **closure)
+{
+    *closure = NULL;
+    return _cairo_test_mask_compositor_surface_create (content, ceil (width), ceil (height));
+}
+
+
+static cairo_surface_t *
+_cairo_boilerplate_test_traps_compositor_create_surface (const char               *name,
+                                                        cairo_content_t            content,
+                                                        double                     width,
+                                                        double                     height,
+                                                        double                     max_width,
+                                                        double                     max_height,
+                                                        cairo_boilerplate_mode_t   mode,
+                                                        int                        id,
+                                                        void                     **closure)
+{
+    *closure = NULL;
+    return _cairo_test_traps_compositor_surface_create (content, ceil (width), ceil (height));
+}
+
+static cairo_surface_t *
+_cairo_boilerplate_test_spans_compositor_create_surface (const char               *name,
+                                                        cairo_content_t            content,
+                                                        double                     width,
+                                                        double                     height,
+                                                        double                     max_width,
+                                                        double                     max_height,
+                                                        cairo_boilerplate_mode_t   mode,
+                                                        int                        id,
+                                                        void                     **closure)
+{
+    *closure = NULL;
+    return _cairo_test_spans_compositor_surface_create (content, ceil (width), ceil (height));
+}
+
+static cairo_surface_t *
+_cairo_boilerplate_test_no_fallback_compositor_create_surface (const char                 *name,
+                                                              cairo_content_t      content,
+                                                              double                       width,
+                                                              double                       height,
+                                                              double                       max_width,
+                                                              double                       max_height,
+                                                              cairo_boilerplate_mode_t   mode,
+                                                              int                          id,
+                                                              void                       **closure)
+{
+    if (mode == CAIRO_BOILERPLATE_MODE_TEST)
+       return NULL;
+
+    *closure = NULL;
+    return _cairo_test_no_fallback_compositor_surface_create (content, ceil (width), ceil (height));
+}
+
+static cairo_surface_t *
+_cairo_boilerplate_test_no_traps_compositor_create_surface (const char            *name,
+                                                        cairo_content_t            content,
+                                                        double                     width,
+                                                        double                     height,
+                                                        double                     max_width,
+                                                        double                     max_height,
+                                                        cairo_boilerplate_mode_t   mode,
+                                                        int                        id,
+                                                        void                     **closure)
+{
+    if (mode == CAIRO_BOILERPLATE_MODE_TEST)
+       return NULL;
+
+    *closure = NULL;
+    return _cairo_test_no_traps_compositor_surface_create (content, ceil (width), ceil (height));
+}
+
+static cairo_surface_t *
+_cairo_boilerplate_test_no_spans_compositor_create_surface (const char            *name,
+                                                        cairo_content_t            content,
+                                                        double                     width,
+                                                        double                     height,
+                                                        double                     max_width,
+                                                        double                     max_height,
+                                                        cairo_boilerplate_mode_t   mode,
+                                                        int                        id,
+                                                        void                     **closure)
+{
+    if (mode == CAIRO_BOILERPLATE_MODE_TEST)
+       return NULL;
+
+    *closure = NULL;
+    return _cairo_test_no_spans_compositor_surface_create (content, ceil (width), ceil (height));
+}
+
+#if CAIRO_HAS_TEST_PAGINATED_SURFACE
+static const cairo_user_data_key_t test_paginated_closure_key;
+
+typedef struct {
+    cairo_surface_t *target;
+} test_paginated_closure_t;
+
+static cairo_surface_t *
+_cairo_boilerplate_test_paginated_create_surface (const char               *name,
+                                                 cairo_content_t            content,
+                                                 double                     width,
+                                                 double                     height,
+                                                 double                     max_width,
+                                                 double                     max_height,
+                                                 cairo_boilerplate_mode_t   mode,
+                                                 int                        id,
+                                                 void                     **closure)
+{
+    test_paginated_closure_t *tpc;
+    cairo_format_t format;
+    cairo_surface_t *surface;
+    cairo_status_t status;
+
+    *closure = tpc = xmalloc (sizeof (test_paginated_closure_t));
+
+    format = cairo_boilerplate_format_from_content (content);
+    tpc->target = cairo_image_surface_create (format,
+                                             ceil (width), ceil (height));
+
+    surface = _cairo_test_paginated_surface_create (tpc->target);
+    if (cairo_surface_status (surface))
+       goto CLEANUP;
+
+    status = cairo_surface_set_user_data (surface,
+                                         &test_paginated_closure_key,
+                                         tpc, NULL);
+    if (status == CAIRO_STATUS_SUCCESS)
+       return surface;
+
+    cairo_surface_destroy (surface);
+    surface = cairo_boilerplate_surface_create_in_error (status);
+
+    cairo_surface_destroy (tpc->target);
+
+  CLEANUP:
+    free (tpc);
+    return surface;
+}
+
+/* The only reason we go through all these machinations to write a PNG
+ * image is to _really ensure_ that the data actually landed in our
+ * buffer through the paginated surface to the test_paginated_surface.
+ *
+ * If we didn't implement this function then the default
+ * cairo_surface_write_to_png would result in the paginated_surface's
+ * acquire_source_image function replaying the recording-surface to an
+ * intermediate image surface. And in that case the
+ * test_paginated_surface would not be involved and wouldn't be
+ * tested.
+ */
+static cairo_status_t
+_cairo_boilerplate_test_paginated_surface_write_to_png (cairo_surface_t *surface,
+                                                       const char      *filename)
+{
+    test_paginated_closure_t *tpc;
+    cairo_status_t status;
+
+    /* show page first.  the automatic show_page is too late for us */
+    cairo_surface_show_page (surface);
+    status = cairo_surface_status (surface);
+    if (status)
+       return status;
+
+    tpc = cairo_surface_get_user_data (surface, &test_paginated_closure_key);
+    return cairo_surface_write_to_png (tpc->target, filename);
+}
+
+static cairo_surface_t *
+_cairo_boilerplate_test_paginated_get_image_surface (cairo_surface_t *surface,
+                                                    int              page,
+                                                    int              width,
+                                                    int              height)
+{
+    test_paginated_closure_t *tpc;
+    cairo_status_t status;
+
+    /* XXX separate finish as per PDF */
+    if (page != 0)
+       return cairo_boilerplate_surface_create_in_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+
+    /* show page first.  the automatic show_page is too late for us */
+    cairo_surface_show_page (surface);
+    status = cairo_surface_status (surface);
+    if (status)
+       return cairo_boilerplate_surface_create_in_error (status);
+
+    tpc = cairo_surface_get_user_data (surface, &test_paginated_closure_key);
+    return _cairo_boilerplate_get_image_surface (tpc->target, 0, width, height);
+}
+
+static void
+_cairo_boilerplate_test_paginated_cleanup (void *closure)
+{
+    test_paginated_closure_t *tpc = closure;
+
+    cairo_surface_destroy (tpc->target);
+    free (tpc);
+}
+#endif
+
+static const cairo_boilerplate_target_t targets[] = {
+    {
+       "test-base", "base", NULL, NULL,
+       CAIRO_SURFACE_TYPE_IMAGE,
+       CAIRO_CONTENT_COLOR_ALPHA, 0,
+       "_cairo_test_base_compositor_surface_create",
+       _cairo_boilerplate_test_base_compositor_create_surface,
+       cairo_surface_create_similar,
+       NULL, NULL,
+       _cairo_boilerplate_get_image_surface,
+       cairo_surface_write_to_png,
+       NULL, NULL, NULL, TRUE, FALSE, FALSE
+    },
+    {
+       "test-base", "base", NULL, NULL,
+       CAIRO_SURFACE_TYPE_IMAGE,
+       CAIRO_CONTENT_COLOR, 0,
+       "_cairo_test_base_compositor_surface_create",
+       _cairo_boilerplate_test_base_compositor_create_surface,
+       cairo_surface_create_similar,
+       NULL, NULL,
+       _cairo_boilerplate_get_image_surface,
+       cairo_surface_write_to_png,
+       NULL, NULL, NULL, FALSE, FALSE, FALSE
+    },
+
+    {
+       "test-fallback", "image", NULL, NULL,
+       CAIRO_SURFACE_TYPE_IMAGE,
+       CAIRO_CONTENT_COLOR_ALPHA, 0,
+       "_cairo_test_fallback_compositor_surface_create",
+       _cairo_boilerplate_test_fallback_compositor_create_surface,
+       cairo_surface_create_similar,
+       NULL, NULL,
+       _cairo_boilerplate_get_image_surface,
+       cairo_surface_write_to_png,
+       NULL, NULL, NULL, FALSE, FALSE, FALSE
+    },
+    {
+       "test-fallback", "image", NULL, NULL,
+       CAIRO_SURFACE_TYPE_IMAGE,
+       CAIRO_CONTENT_COLOR, 0,
+       "_cairo_test_fallback_compositor_surface_create",
+       _cairo_boilerplate_test_fallback_compositor_create_surface,
+       cairo_surface_create_similar,
+       NULL, NULL,
+       _cairo_boilerplate_get_image_surface,
+       cairo_surface_write_to_png,
+       NULL, NULL, NULL, FALSE, FALSE, FALSE
+    },
+
+    {
+       "test-mask", "mask", NULL, NULL,
+       CAIRO_SURFACE_TYPE_IMAGE,
+       CAIRO_CONTENT_COLOR_ALPHA, 0,
+       "_cairo_test_mask_compositor_surface_create",
+       _cairo_boilerplate_test_mask_compositor_create_surface,
+       cairo_surface_create_similar,
+       NULL, NULL,
+       _cairo_boilerplate_get_image_surface,
+       cairo_surface_write_to_png,
+       NULL, NULL, NULL, TRUE, FALSE, FALSE
+    },
+    {
+       "test-mask", "mask", NULL, NULL,
+       CAIRO_SURFACE_TYPE_IMAGE,
+       CAIRO_CONTENT_COLOR, 0,
+       "_cairo_test_mask_compositor_surface_create",
+       _cairo_boilerplate_test_mask_compositor_create_surface,
+       cairo_surface_create_similar,
+       NULL, NULL,
+       _cairo_boilerplate_get_image_surface,
+       cairo_surface_write_to_png,
+       NULL, NULL, NULL, FALSE, FALSE, FALSE
+    },
+
+    {
+       "test-traps", "traps", NULL, NULL,
+       CAIRO_SURFACE_TYPE_IMAGE,
+       CAIRO_CONTENT_COLOR_ALPHA, 0,
+       "_cairo_test_traps_compositor_surface_create",
+       _cairo_boilerplate_test_traps_compositor_create_surface,
+       cairo_surface_create_similar,
+       NULL, NULL,
+       _cairo_boilerplate_get_image_surface,
+       cairo_surface_write_to_png,
+       NULL, NULL, NULL, TRUE, FALSE, FALSE
+    },
+    {
+       "test-traps", "traps", NULL, NULL,
+       CAIRO_SURFACE_TYPE_IMAGE,
+       CAIRO_CONTENT_COLOR, 0,
+       "_cairo_test_traps_compositor_surface_create",
+       _cairo_boilerplate_test_traps_compositor_create_surface,
+       cairo_surface_create_similar,
+       NULL, NULL,
+       _cairo_boilerplate_get_image_surface,
+       cairo_surface_write_to_png,
+       NULL, NULL, NULL, FALSE, FALSE, FALSE
+    },
+
+    {
+       "test-spans", "spans", NULL, NULL,
+       CAIRO_SURFACE_TYPE_IMAGE,
+       CAIRO_CONTENT_COLOR_ALPHA, 0,
+       "_cairo_test_spans_compositor_surface_create",
+       _cairo_boilerplate_test_spans_compositor_create_surface,
+       cairo_surface_create_similar,
+       NULL, NULL,
+       _cairo_boilerplate_get_image_surface,
+       cairo_surface_write_to_png,
+       NULL, NULL, NULL, TRUE, FALSE, FALSE
+    },
+    {
+       "test-spans", "spans", NULL, NULL,
+       CAIRO_SURFACE_TYPE_IMAGE,
+       CAIRO_CONTENT_COLOR, 0,
+       "_cairo_test_spans_compositor_surface_create",
+       _cairo_boilerplate_test_spans_compositor_create_surface,
+       cairo_surface_create_similar,
+       NULL, NULL,
+       _cairo_boilerplate_get_image_surface,
+       cairo_surface_write_to_png,
+       NULL, NULL, NULL, FALSE, FALSE, FALSE
+    },
+
+    {
+       "no-fallback", "image", NULL, NULL,
+       CAIRO_SURFACE_TYPE_IMAGE,
+       CAIRO_CONTENT_COLOR_ALPHA, 0,
+       "_cairo_test_no_fallback_compositor_surface_create",
+       _cairo_boilerplate_test_no_fallback_compositor_create_surface,
+       cairo_surface_create_similar,
+       NULL, NULL,
+       _cairo_boilerplate_get_image_surface,
+       cairo_surface_write_to_png,
+       NULL, NULL, NULL, FALSE, FALSE, FALSE
+    },
+    {
+       "no-traps", "traps", NULL, NULL,
+       CAIRO_SURFACE_TYPE_IMAGE,
+       CAIRO_CONTENT_COLOR_ALPHA, 0,
+       "_cairo_test_no_traps_compositor_surface_create",
+       _cairo_boilerplate_test_no_traps_compositor_create_surface,
+       cairo_surface_create_similar,
+       NULL, NULL,
+       _cairo_boilerplate_get_image_surface,
+       cairo_surface_write_to_png,
+       NULL, NULL, NULL, TRUE, FALSE, FALSE
+    },
+    {
+       "no-spans", "spans", NULL, NULL,
+       CAIRO_SURFACE_TYPE_IMAGE,
+       CAIRO_CONTENT_COLOR_ALPHA, 0,
+       "_cairo_test_no_spans_compositor_surface_create",
+       _cairo_boilerplate_test_no_spans_compositor_create_surface,
+       cairo_surface_create_similar,
+       NULL, NULL,
+       _cairo_boilerplate_get_image_surface,
+       cairo_surface_write_to_png,
+       NULL, NULL, NULL, TRUE, FALSE, FALSE
+    },
+#if CAIRO_HAS_TEST_PAGINATED_SURFACE
+    {
+       "test-paginated", "image", NULL, NULL,
+       CAIRO_INTERNAL_SURFACE_TYPE_TEST_PAGINATED,
+       CAIRO_CONTENT_COLOR_ALPHA, 0,
+       "_cairo_test_paginated_surface_create",
+       _cairo_boilerplate_test_paginated_create_surface,
+       cairo_surface_create_similar,
+       NULL, NULL,
+       _cairo_boilerplate_test_paginated_get_image_surface,
+       _cairo_boilerplate_test_paginated_surface_write_to_png,
+       _cairo_boilerplate_test_paginated_cleanup,
+       NULL, NULL, FALSE, TRUE, FALSE
+    },
+    {
+       "test-paginated", "image", NULL, NULL,
+       CAIRO_INTERNAL_SURFACE_TYPE_TEST_PAGINATED,
+       CAIRO_CONTENT_COLOR, 0,
+       "_cairo_test_paginated_surface_create",
+       _cairo_boilerplate_test_paginated_create_surface,
+       cairo_surface_create_similar,
+       NULL, NULL,
+       _cairo_boilerplate_test_paginated_get_image_surface,
+       _cairo_boilerplate_test_paginated_surface_write_to_png,
+       _cairo_boilerplate_test_paginated_cleanup,
+       NULL, NULL, FALSE, TRUE, FALSE
+    },
+#endif
+};
+CAIRO_BOILERPLATE (test, targets)
diff --git a/boilerplate/cairo-boilerplate-vg.c b/boilerplate/cairo-boilerplate-vg.c
new file mode 100644 (file)
index 0000000..a49ed5d
--- /dev/null
@@ -0,0 +1,365 @@
+/* Cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Chris Wilson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Chris Wilson.
+ */
+
+#include "cairo-boilerplate-private.h"
+
+#include <cairo-vg.h>
+
+ /* XXX Not sure how to handle library specific context initialization */
+//#define USE_SHIVA
+//#define USE_AMANITH
+
+#if CAIRO_HAS_GLX_FUNCTIONS
+
+#include <X11/Xlib.h>
+#include <GL/glx.h>
+
+typedef struct _vg_closure {
+    Display *dpy;
+    int screen;
+    Window win;
+
+    GLXContext ctx;
+    cairo_surface_t *surface;
+} vg_closure_glx_t;
+
+static void
+_cairo_boilerplate_vg_cleanup_glx (void *closure)
+{
+    vg_closure_glx_t *vgc = closure;
+
+#ifdef USE_AMANITH
+    vgDestroyContextAM ();
+#endif
+#ifdef USE_SHIVA
+    vgDestroyContextSH ();
+#endif
+
+    glXDestroyContext (vgc->dpy, vgc->ctx);
+    XDestroyWindow (vgc->dpy, vgc->win);
+    XCloseDisplay (vgc->dpy);
+    free (vgc);
+}
+
+static cairo_surface_t *
+_cairo_boilerplate_vg_create_surface_glx (const char               *name,
+                                         cairo_content_t            content,
+                                         double                     width,
+                                         double                     height,
+                                         double                     max_width,
+                                         double                     max_height,
+                                         cairo_boilerplate_mode_t   mode,
+                                         int                        id,
+                                         void                     **closure)
+{
+    int rgba_attribs[] = {
+       GLX_RGBA,
+       GLX_RED_SIZE, 1,
+       GLX_GREEN_SIZE, 1,
+       GLX_BLUE_SIZE, 1,
+       GLX_ALPHA_SIZE, 1,
+       GLX_DOUBLEBUFFER,
+       None
+    };
+    int rgb_attribs[] = {
+       GLX_RGBA,
+       GLX_RED_SIZE, 1,
+       GLX_GREEN_SIZE, 1,
+       GLX_BLUE_SIZE, 1,
+       GLX_DOUBLEBUFFER,
+       None
+    };
+    XVisualInfo *vi;
+    Display *dpy;
+    Colormap cmap;
+    XSetWindowAttributes swa;
+    cairo_surface_t *surface;
+    cairo_vg_context_t *context;
+    vg_closure_glx_t *vgc;
+
+    vgc = malloc (sizeof (vg_closure_glx_t));
+    *closure = vgc;
+
+    if (width == 0)
+       width = 1;
+    if (height == 0)
+       height = 1;
+
+    dpy = XOpenDisplay (NULL);
+    vgc->dpy = dpy;
+    if (vgc->dpy == NULL) {
+       fprintf (stderr, "Failed to open display: %s\n", XDisplayName(0));
+       free (vgc);
+       return NULL;
+    }
+
+    if (content == CAIRO_CONTENT_COLOR)
+       vi = glXChooseVisual (dpy, DefaultScreen (dpy), rgb_attribs);
+    else
+       vi = glXChooseVisual (dpy, DefaultScreen (dpy), rgba_attribs);
+
+    if (vi == NULL) {
+       fprintf (stderr, "Failed to create RGB, double-buffered visual\n");
+       XCloseDisplay (dpy);
+       free (vgc);
+       return NULL;
+    }
+
+    vgc->ctx = glXCreateContext (dpy, vi, NULL, True);
+    cmap = XCreateColormap (dpy,
+                           RootWindow (dpy, vi->screen),
+                           vi->visual,
+                           AllocNone);
+    swa.colormap = cmap;
+    swa.border_pixel = 0;
+    vgc->win = XCreateWindow (dpy, RootWindow (dpy, vi->screen),
+                             -1, -1, 1, 1, 0,
+                             vi->depth,
+                             InputOutput,
+                             vi->visual,
+                             CWBorderPixel | CWColormap, &swa);
+    XFreeColormap (dpy, cmap);
+    XFree (vi);
+
+    XMapWindow (dpy, vgc->win);
+
+    /* we need an active context to initialise VG */
+    glXMakeContextCurrent (dpy, vgc->win, vgc->win, vgc->ctx);
+
+#ifdef USE_AMANITH
+    vgInitContextAM (width, height, VG_FALSE, VG_TRUE);
+#endif
+#ifdef USE_SHIVA
+    vgCreateContextSH (width, height);
+#endif
+
+    context = cairo_vg_context_create_for_glx (dpy, vgc->ctx);
+    vgc->surface = cairo_vg_surface_create (context, content, width, height);
+    cairo_vg_context_destroy (context);
+
+    surface = vgc->surface;
+    if (cairo_surface_status (surface))
+       _cairo_boilerplate_vg_cleanup_glx (vgc);
+
+    return surface;
+}
+#endif
+
+#if CAIRO_HAS_EGL_FUNCTIONS
+typedef struct _vg_closure_egl {
+    EGLDisplay *dpy;
+    EGLContext *ctx;
+    EGLSurface *dummy;
+} vg_closure_egl_t;
+
+static void
+_cairo_boilerplate_vg_cleanup_egl (void *closure)
+{
+    vg_closure_egl_t *vgc = closure;
+
+#ifdef USE_AMANITH
+    vgDestroyContextAM ();
+#endif
+#ifdef USE_SHIVA
+    vgDestroyContextSH ();
+#endif
+
+    eglDestroyContext (vgc->dpy, vgc->ctx);
+    eglDestroySurface (vgc->dpy, vgc->dummy);
+    eglTerminate (vgc->dpy);
+    free (vgc);
+}
+
+static cairo_surface_t *
+_cairo_boilerplate_vg_create_surface_egl (const char               *name,
+                                         cairo_content_t            content,
+                                         double                     width,
+                                         double                     height,
+                                         double                     max_width,
+                                         double                     max_height,
+                                         cairo_boilerplate_mode_t   mode,
+                                         int                        id,
+                                         void                     **closure)
+{
+    int rgba_attribs[] = {
+       EGL_RED_SIZE, 8,
+       EGL_GREEN_SIZE, 8,
+       EGL_BLUE_SIZE, 8,
+       EGL_ALPHA_SIZE, 8,
+       EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
+       EGL_RENDERABLE_TYPE, EGL_OPENVG_BIT,
+       None
+    };
+    int rgb_attribs[] = {
+       EGL_RED_SIZE, 8,
+       EGL_GREEN_SIZE, 8,
+       EGL_BLUE_SIZE, 8,
+       EGL_ALPHA_SIZE, 8,
+       EGL_VG_ALPHA_FORMAT, EGL_VG_ALPHA_FORMAT_PRE_BIT,
+       EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
+       EGL_RENDERABLE_TYPE, EGL_OPENVG_BIT,
+       None
+    };
+    int dummy_attribs[] = {
+       EGL_WIDTH, 8, EGL_HEIGHT, 8,
+       EGL_NONE
+    };
+    EGLDisplay *dpy;
+    int major, minor;
+    EGLConfig config;
+    int num_configs;
+    EGLContext *egl_context;
+    EGLSurface *dummy;
+    cairo_vg_context_t *context;
+    cairo_surface_t *surface;
+    vg_closure_egl_t *vgc;
+
+    dpy = eglGetDisplay (EGL_DEFAULT_DISPLAY);
+
+    if (! eglInitialize (dpy, &major, &minor))
+       return NULL;
+
+    eglBindAPI (EGL_OPENVG_API);
+
+    if (! eglChooseConfig (dpy,
+                          content == CAIRO_CONTENT_COLOR_ALPHA ?
+                          rgba_attribs : rgb_attribs,
+                          &config, 1, &num_configs) ||
+       num_configs != 1)
+    {
+       return NULL;
+    }
+
+    egl_context = eglCreateContext (dpy, config, NULL, NULL);
+    if (egl_context == NULL)
+       return NULL;
+
+    /* Create a dummy surface in order to enable a context to initialise VG */
+    dummy = eglCreatePbufferSurface (dpy, config, dummy_attribs);
+    if (dummy == NULL)
+       return NULL;
+    if (! eglMakeCurrent (dpy, dummy, dummy, egl_context))
+       return NULL;
+
+#ifdef USE_AMANITH
+    vgInitContextAM (width, height, VG_FALSE, VG_TRUE);
+#endif
+#ifdef USE_SHIVA
+    vgCreateContextSH (width, height);
+#endif
+
+    vgc = xmalloc (sizeof (vg_closure_egl_t));
+    vgc->dpy = dpy;
+    vgc->ctx = egl_context;
+    vgc->dummy = dummy;
+    *closure = vgc;
+
+    context = cairo_vg_context_create_for_egl (vgc->dpy, vgc->ctx);
+    surface = cairo_vg_surface_create (context, content, width, height);
+    cairo_vg_context_destroy (context);
+
+    if (cairo_surface_status (surface))
+       _cairo_boilerplate_vg_cleanup_egl (vgc);
+
+    return surface;
+}
+#endif
+
+static void
+_cairo_boilerplate_vg_synchronize (void *closure)
+{
+    vgFinish ();
+}
+
+static const cairo_boilerplate_target_t targets[] = {
+#if CAIRO_HAS_GLX_FUNCTIONS
+    {
+       "vg-glx", "vg", NULL, NULL,
+       CAIRO_SURFACE_TYPE_VG, CAIRO_CONTENT_COLOR_ALPHA, 1,
+       "cairo_vg_context_create_for_glx",
+       _cairo_boilerplate_vg_create_surface_glx,
+       cairo_surface_create_similar,
+       NULL, NULL,
+       _cairo_boilerplate_get_image_surface,
+       cairo_surface_write_to_png,
+       _cairo_boilerplate_vg_cleanup_glx,
+       _cairo_boilerplate_vg_synchronize,
+        NULL,
+       TRUE, FALSE, FALSE
+    },
+    {
+       "vg-glx", "vg", NULL, NULL,
+       CAIRO_SURFACE_TYPE_VG, CAIRO_CONTENT_COLOR, 1,
+       "cairo_vg_context_create_for_glx",
+       _cairo_boilerplate_vg_create_surface_glx,
+       cairo_surface_create_similar,
+       NULL, NULL,
+       _cairo_boilerplate_get_image_surface,
+       cairo_surface_write_to_png,
+       _cairo_boilerplate_vg_cleanup_glx,
+       _cairo_boilerplate_vg_synchronize,
+        NULL,
+       FALSE, FALSE, FALSE
+    },
+#endif
+#if CAIRO_HAS_EGL_FUNCTIONS
+    {
+       "vg-egl", "vg", NULL, NULL,
+       CAIRO_SURFACE_TYPE_VG, CAIRO_CONTENT_COLOR_ALPHA, 1,
+       "cairo_vg_context_create_for_egl",
+       _cairo_boilerplate_vg_create_surface_egl,
+       cairo_surface_create_similar,
+       NULL, NULL,
+       _cairo_boilerplate_get_image_surface,
+       cairo_surface_write_to_png,
+       _cairo_boilerplate_vg_cleanup_egl,
+       _cairo_boilerplate_vg_synchronize,
+        NULL,
+       TRUE, FALSE, FALSE
+    },
+    {
+       "vg-egl", "vg", NULL, NULL,
+       CAIRO_SURFACE_TYPE_VG, CAIRO_CONTENT_COLOR, 1,
+       "cairo_vg_context_create_for_egl",
+       _cairo_boilerplate_vg_create_surface_egl,
+       cairo_surface_create_similar,
+       NULL, NULL,
+       _cairo_boilerplate_get_image_surface,
+       cairo_surface_write_to_png,
+       _cairo_boilerplate_vg_cleanup_egl,
+       _cairo_boilerplate_vg_synchronize,
+        NULL,
+       FALSE, FALSE, FALSE
+    },
+#endif
+};
+CAIRO_BOILERPLATE (vg, targets)
diff --git a/boilerplate/cairo-boilerplate-wgl.c b/boilerplate/cairo-boilerplate-wgl.c
new file mode 100644 (file)
index 0000000..8ae1dcb
--- /dev/null
@@ -0,0 +1,241 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Chris Wilson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Chris Wilson.
+ *
+ * Contributor(s):
+ *     Zoxc <zoxc32@gmail.com>
+ */
+
+#include "cairo-boilerplate-private.h"
+
+#include <cairo-gl.h>
+
+static const cairo_user_data_key_t gl_closure_key;
+
+typedef struct _wgl_target_closure {
+    HWND wnd;
+    HDC dc;
+    HGLRC rc;
+    cairo_device_t *device;
+    cairo_surface_t *surface;
+} wgl_target_closure_t;
+
+static void
+_cairo_boilerplate_wgl_cleanup (void *closure)
+{
+    wgl_target_closure_t *wgltc = closure;
+
+    cairo_device_finish (wgltc->device);
+    cairo_device_destroy (wgltc->device);
+
+    wglDeleteContext(wgltc->rc);
+
+    ReleaseDC(wgltc->wnd, wgltc->dc);
+    DestroyWindow (wgltc->wnd);
+
+    free (wgltc);
+}
+
+static void
+_cairo_boilerplate_wgl_create_window (int                  width,
+                                     int                   height,
+                                     wgl_target_closure_t *wgltc)
+{
+    WNDCLASSEXA wincl;
+    PIXELFORMATDESCRIPTOR pfd;
+    int format;
+    cairo_surface_t *surface;
+    
+    ZeroMemory (&wincl, sizeof (WNDCLASSEXA));
+    wincl.cbSize = sizeof (WNDCLASSEXA);
+    wincl.hInstance = GetModuleHandle (0);
+    wincl.lpszClassName = "cairo_boilerplate_wgl_dummy";
+    wincl.lpfnWndProc = DefWindowProcA;
+    wincl.style = CS_OWNDC;
+
+    RegisterClassExA (&wincl);
+    
+    wgltc->wnd = CreateWindow ("cairo_boilerplate_wgl_dummy", 0, WS_POPUP, 0, 0, width, height, 0, 0, 0, 0);
+    wgltc->dc = GetDC (wgltc->wnd);
+
+    ZeroMemory (&pfd, sizeof (PIXELFORMATDESCRIPTOR));
+    pfd.nSize = sizeof (PIXELFORMATDESCRIPTOR);
+    pfd.nVersion = 1;
+    pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
+    pfd.iPixelType = PFD_TYPE_RGBA;
+    pfd.cColorBits = 24;
+    pfd.cDepthBits = 16;
+    pfd.iLayerType = PFD_MAIN_PLANE;
+
+    format = ChoosePixelFormat (wgltc->dc, &pfd);
+    SetPixelFormat (wgltc->dc, format, &pfd);
+    
+    wgltc->rc = wglCreateContext (wgltc->dc);
+    wgltc->device = cairo_wgl_device_create (wgltc->rc);
+}
+
+static cairo_surface_t *
+_cairo_boilerplate_wgl_create_surface (const char               *name,
+                                      cairo_content_t            content,
+                                      double                     width,
+                                      double                     height,
+                                      double                     max_width,
+                                      double                     max_height,
+                                      cairo_boilerplate_mode_t   mode,
+                                      int                        id,
+                                      void                     **closure)
+{
+    wgl_target_closure_t *wgltc;
+    cairo_surface_t *surface;
+    
+    wgltc = calloc (1, sizeof (wgl_target_closure_t));
+    
+    *closure = wgltc;
+
+    _cairo_boilerplate_wgl_create_window(0, 0, wgltc);
+    
+    if (width == 0)
+       width = 1;
+    if (height == 0)
+       height = 1;
+    
+    wgltc->surface = surface = cairo_gl_surface_create (wgltc->device,
+                                                      content,
+                                                      ceil (width),
+                                                      ceil (height));
+    if (cairo_surface_status (surface)) {
+       _cairo_boilerplate_wgl_cleanup (wgltc);
+       return NULL;
+    }
+
+    return surface;
+}
+
+static cairo_surface_t *
+_cairo_boilerplate_wgl_for_create_window (const char               *name,
+                                         cairo_content_t            content,
+                                         double                     width,
+                                         double                     height,
+                                         double                     max_width,
+                                         double                     max_height,
+                                         cairo_boilerplate_mode_t   mode,
+                                         int                        id,
+                                         void                     **closure)
+{
+    wgl_target_closure_t *wgltc;
+    cairo_surface_t *surface;
+    
+    wgltc = calloc (1, sizeof (wgl_target_closure_t));
+    
+    *closure = wgltc;
+
+     _cairo_boilerplate_wgl_create_window(width, height, wgltc);
+    
+    wgltc->surface = surface = cairo_gl_surface_create_for_dc (wgltc->device,
+                                                      wgltc->dc,
+                                                      ceil (width),
+                                                      ceil (height));
+    
+    if (cairo_surface_status (surface)) {
+       _cairo_boilerplate_wgl_cleanup (wgltc);
+       return NULL;
+    }
+
+    return surface;
+}
+
+static cairo_status_t
+_cairo_boilerplate_wgl_finish_window (cairo_surface_t *surface)
+{
+    wgl_target_closure_t *wgltc = cairo_surface_get_user_data (surface,
+                                                            &gl_closure_key);
+
+    if (wgltc != NULL && wgltc->surface != NULL) {
+       cairo_t *cr;
+
+       cr = cairo_create (wgltc->surface);
+       cairo_surface_set_device_offset (surface, 0, 0);
+       cairo_set_source_surface (cr, surface, 0, 0);
+       cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+       cairo_paint (cr);
+       cairo_destroy (cr);
+
+       surface = wgltc->surface;
+    }
+
+    cairo_gl_surface_swapbuffers (surface);
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_boilerplate_wgl_synchronize (void *closure)
+{
+    wgl_target_closure_t *wgltc = closure;
+
+    if (cairo_device_acquire (wgltc->device))
+       return;
+
+    glFinish ();
+
+    cairo_device_release (wgltc->device);
+}
+
+static const cairo_boilerplate_target_t targets[] = {
+    {
+       "gl", "gl", NULL, NULL,
+       CAIRO_SURFACE_TYPE_GL, CAIRO_CONTENT_COLOR_ALPHA, 1,
+       "cairo_gl_surface_create",
+       _cairo_boilerplate_wgl_create_surface,
+       cairo_surface_create_similar,
+       NULL, NULL,
+       _cairo_boilerplate_get_image_surface,
+       cairo_surface_write_to_png,
+       _cairo_boilerplate_wgl_cleanup,
+       _cairo_boilerplate_wgl_synchronize,
+        NULL,
+       TRUE, FALSE, FALSE
+    },
+    {
+       "gl-dc", "gl", NULL, NULL,
+       CAIRO_SURFACE_TYPE_GL, CAIRO_CONTENT_COLOR_ALPHA, 1,
+       "cairo_gl_surface_create_for_dc",
+       _cairo_boilerplate_wgl_for_create_window,
+       NULL,
+       _cairo_boilerplate_wgl_finish_window,
+       _cairo_boilerplate_get_image_surface,
+       cairo_surface_write_to_png,
+       _cairo_boilerplate_wgl_cleanup,
+       _cairo_boilerplate_wgl_synchronize,
+        NULL,
+       FALSE, FALSE, FALSE
+    },
+};
+
+CAIRO_BOILERPLATE (wgl, targets)
diff --git a/boilerplate/cairo-boilerplate-win32-printing.c b/boilerplate/cairo-boilerplate-win32-printing.c
new file mode 100644 (file)
index 0000000..0e54512
--- /dev/null
@@ -0,0 +1,380 @@
+/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/*
+ * Copyright © 2004,2006 Red Hat, Inc.
+ * Copyright © 2007, Adrian Johnson
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Authors: Carl D. Worth <cworth@cworth.org>
+ *         Adrian Johnson <ajohnson@redneon.com>
+ */
+
+/* We require Windows 2000 features such as GetDefaultPrinter() */
+#if !defined(WINVER) || (WINVER < 0x0500)
+# define WINVER 0x0500
+#endif
+#if !defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x0500)
+# define _WIN32_WINNT 0x0500
+#endif
+
+#include "cairo-boilerplate-private.h"
+
+#if CAIRO_CAN_TEST_WIN32_PRINTING_SURFACE
+
+#include <cairo-win32.h>
+#include <cairo-win32-private.h>
+#include <cairo-paginated-surface-private.h>
+
+#include <windows.h>
+
+#if !defined(POSTSCRIPT_IDENTIFY)
+# define POSTSCRIPT_IDENTIFY 0x1015
+#endif
+
+#if !defined(PSIDENT_GDICENTRIC)
+# define PSIDENT_GDICENTRIC 0x0000
+#endif
+
+#if !defined(GET_PS_FEATURESETTING)
+# define GET_PS_FEATURESETTING 0x1019
+#endif
+
+#if !defined(FEATURESETTING_PSLEVEL)
+# define FEATURESETTING_PSLEVEL 0x0002
+#endif
+
+static cairo_user_data_key_t win32_closure_key;
+
+typedef struct _win32_target_closure {
+    char *filename;
+    int width;
+    int height;
+    cairo_surface_t *target;
+    HDC dc;
+    int left_margin;
+    int bottom_margin;
+} win32_target_closure_t;
+
+static cairo_bool_t
+printer_is_postscript_level_3 (HDC dc)
+{
+    DWORD word;
+    INT ps_feature, ps_level;
+
+    word = PSIDENT_GDICENTRIC;
+    if (ExtEscape (dc, POSTSCRIPT_IDENTIFY, sizeof(DWORD), (char *)&word, 0, (char *)NULL) <= 0)
+       return FALSE;
+
+    ps_feature = FEATURESETTING_PSLEVEL;
+    if (ExtEscape (dc, GET_PS_FEATURESETTING, sizeof(INT),
+                  (char *)&ps_feature, sizeof(INT), (char *)&ps_level) <= 0)
+       return FALSE;
+
+    if (ps_level >= 3)
+       return TRUE;
+
+    return FALSE;
+}
+
+static void
+create_printer_dc (win32_target_closure_t *ptc)
+{
+    char *printer_name;
+    DWORD size;
+    int x_dpi, y_dpi, left_margin, top_margin, page_height, printable_height;
+    XFORM xform;
+
+    ptc->dc = NULL;
+    GetDefaultPrinter (NULL, &size);
+    printer_name = malloc (size);
+
+    if (printer_name == NULL)
+       return;
+
+    if (GetDefaultPrinter (printer_name, &size) == 0) {
+       free (printer_name);
+       return;
+    }
+
+    /* printf("\nPrinting to : %s\n", printer_name); */
+    ptc->dc = CreateDC (NULL, printer_name, NULL, NULL);
+    free (printer_name);
+
+    if (!printer_is_postscript_level_3 (ptc->dc)) {
+       printf("The default printer driver must be a color PostScript level 3 printer\n");
+       ptc->dc = NULL;
+       return;
+    }
+
+    /* The printer device units on win32 are 1 unit == 1 dot and the
+     * origin is the start of the printable area. We transform the
+     * cordinate space to 1 unit is 1 point as expected by the
+     * tests. As the page size is larger than the test surface, the
+     * origin is translated down so that the each test is drawn at the
+     * bottom left corner of the page. This is because the bottom left
+     * corner of the PNG image that ghostscript creates is positioned
+     * at origin of the PS coordinates (ie the bottom left of the
+     * page).  The left and bottom margins are stored in
+     * win32_target_closure as size of the PNG image needs to be
+     * increased because the test output is offset from the bottom
+     * left by the non printable margins. After the PNG is created the
+     * margins will be chopped off so the image matches the reference
+     * image.
+     */
+    printable_height = GetDeviceCaps (ptc->dc, VERTRES);
+    x_dpi = GetDeviceCaps (ptc->dc, LOGPIXELSX);
+    y_dpi = GetDeviceCaps (ptc->dc, LOGPIXELSY);
+    left_margin = GetDeviceCaps (ptc->dc, PHYSICALOFFSETX);
+    top_margin = GetDeviceCaps (ptc->dc, PHYSICALOFFSETY);
+    page_height = GetDeviceCaps (ptc->dc, PHYSICALHEIGHT);
+
+    SetGraphicsMode (ptc->dc, GM_ADVANCED);
+    xform.eM11 = x_dpi/72.0;
+    xform.eM12 = 0;
+    xform.eM21 = 0;
+    xform.eM22 = y_dpi/72.0;
+    xform.eDx = 0;
+    xform.eDy = printable_height - ptc->height*y_dpi/72.0;
+    if (!SetWorldTransform (ptc->dc, &xform)) {
+       _cairo_win32_print_gdi_error ("cairo-boilerplate-win32-printing:SetWorldTransform");
+       return;
+    }
+
+    ptc->left_margin = 72.0*left_margin/x_dpi;
+    ptc->bottom_margin = 72.0*(page_height - printable_height - top_margin)/y_dpi;
+}
+
+static cairo_surface_t *
+_cairo_boilerplate_win32_printing_create_surface (const char               *name,
+                                                 cairo_content_t            content,
+                                                 double                     width,
+                                                 double                     height,
+                                                 double                     max_width,
+                                                 double                     max_height,
+                                                 cairo_boilerplate_mode_t   mode,
+                                                 int                        id,
+                                                 void                     **closure)
+{
+    win32_target_closure_t *ptc;
+    cairo_surface_t *surface;
+    DOCINFO di;
+
+    if (content == CAIRO_TEST_CONTENT_COLOR_ALPHA_FLATTENED)
+       content = CAIRO_CONTENT_COLOR_ALPHA;
+
+    *closure = ptc = xmalloc (sizeof (win32_target_closure_t));
+
+    xasprintf (&ptc->filename, "%s.out.ps", name);
+    xunlink (ptc->filename);
+
+    memset (&di, 0, sizeof (DOCINFO));
+    di.cbSize = sizeof (DOCINFO);
+    di.lpszDocName = ptc->filename;
+    di.lpszOutput = ptc->filename;
+
+    ptc->width = width;
+    ptc->height = height;
+
+    create_printer_dc (ptc);
+    if (ptc->dc == NULL) {
+       printf("\nFailed to create printer\n");
+       free (ptc->filename);
+       free (ptc);
+       return NULL;
+    }
+    StartDoc (ptc->dc, &di);
+    StartPage (ptc->dc);
+    surface = cairo_win32_printing_surface_create (ptc->dc);
+    if (cairo_surface_status (surface)) {
+       free (ptc->filename);
+       free (ptc);
+       return NULL;
+    }
+    cairo_surface_set_fallback_resolution (surface, 72., 72.);
+
+    if (content == CAIRO_CONTENT_COLOR) {
+       ptc->target = surface;
+       surface = cairo_surface_create_similar (ptc->target,
+                                               CAIRO_CONTENT_COLOR,
+                                               width, height);
+    } else {
+       ptc->target = NULL;
+    }
+
+    if (cairo_surface_set_user_data (surface,
+                                    &win32_closure_key,
+                                    ptc,
+                                    NULL) != CAIRO_STATUS_SUCCESS) {
+       cairo_surface_destroy (surface);
+       if (ptc->target != NULL)
+           cairo_surface_destroy (ptc->target);
+       free (ptc->filename);
+       free (ptc);
+       return NULL;
+    }
+
+    return surface;
+}
+
+static cairo_status_t
+_cairo_boilerplate_win32_printing_surface_write_to_png (cairo_surface_t *surface,
+                                                       const char      *filename)
+{
+    win32_target_closure_t *ptc = cairo_surface_get_user_data (surface, &win32_closure_key);
+    char command[4096];
+    cairo_surface_t *src_image, *dst_image;
+    cairo_t *cr;
+    cairo_status_t status;
+
+    /* Both surface and ptc->target were originally created at the
+     * same dimensions. We want a 1:1 copy here, so we first clear any
+     * device offset on surface.
+     *
+     * In a more realistic use case of device offsets, the target of
+     * this copying would be of a different size than the source, and
+     * the offset would be desirable during the copy operation. */
+    cairo_surface_set_device_offset (surface, 0, 0);
+
+    if (ptc->target) {
+       cairo_t *cr;
+       cr = cairo_create (ptc->target);
+       cairo_set_source_surface (cr, surface, 0, 0);
+       cairo_paint (cr);
+       cairo_show_page (cr);
+       cairo_destroy (cr);
+
+       cairo_surface_finish (surface);
+       surface = ptc->target;
+    }
+
+    cairo_surface_finish (surface);
+    EndPage (ptc->dc);
+    EndDoc (ptc->dc);
+    sprintf (command, "gs -q -r72 -g%dx%d -dSAFER -dBATCH -dNOPAUSE -sDEVICE=pngalpha -sOutputFile=%s %s",
+            ptc->width + ptc->left_margin, ptc->height + ptc->bottom_margin, filename, ptc->filename);
+
+    if (system (command) != 0)
+       return CAIRO_STATUS_WRITE_ERROR;
+
+    /* Create a new image from the ghostscript image that has the
+     * left and bottom margins removed */
+
+    src_image = cairo_image_surface_create_from_png (filename);
+    status = cairo_surface_status (src_image);
+    if (status)
+       return status;
+
+    dst_image = cairo_image_surface_create (CAIRO_FORMAT_RGB24,
+                                           ptc->width,
+                                           ptc->height);
+    cr = cairo_create (dst_image);
+    cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+    cairo_set_source_surface (cr, src_image, -ptc->left_margin, 0);
+    cairo_paint (cr);
+    cairo_destroy (cr);
+
+    cairo_surface_write_to_png (dst_image, filename);
+    status = cairo_surface_status (dst_image);
+    if (status)
+       return status;
+
+    cairo_surface_destroy (src_image);
+    cairo_surface_destroy (dst_image);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_surface_t *
+_cairo_boilerplate_win32_printing_get_image_surface (cairo_surface_t *surface,
+                                                    int              page,
+                                                    int              width,
+                                                    int              height)
+{
+    win32_target_closure_t *ptc = cairo_surface_get_user_data (surface,
+                                                              &win32_closure_key);
+    char *filename;
+    cairo_status_t status;
+
+    /* XXX test paginated interface */
+    if (page != 0)
+       return cairo_boilerplate_surface_create_in_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+
+    xasprintf (&filename, "%s.png", ptc->filename);
+    status = _cairo_boilerplate_win32_printing_surface_write_to_png (surface, filename);
+    if (status)
+       return cairo_boilerplate_surface_create_in_error (status);
+
+    surface = cairo_boilerplate_get_image_surface_from_png (filename,
+                                                           width,
+                                                           height,
+                                                           ptc->target == NULL);
+
+    remove (filename);
+    free (filename);
+
+    return surface;
+}
+
+static void
+_cairo_boilerplate_win32_printing_cleanup (void *closure)
+{
+    win32_target_closure_t *ptc = closure;
+
+    if (ptc->target)
+       cairo_surface_destroy (ptc->target);
+    free (ptc->filename);
+    free (ptc);
+    DeleteDC (ptc->dc);
+}
+
+static const cairo_boilerplate_target_t targets[] = {
+    {
+       "win32-printing", "win32", ".ps", NULL,
+       CAIRO_SURFACE_TYPE_WIN32_PRINTING,
+       CAIRO_TEST_CONTENT_COLOR_ALPHA_FLATTENED, 0,
+       "cairo_win32_printing_surface_create",
+       _cairo_boilerplate_win32_printing_create_surface,
+       cairo_surface_create_similar,
+       NULL, NULL,
+       _cairo_boilerplate_win32_printing_get_image_surface,
+       _cairo_boilerplate_win32_printing_surface_write_to_png,
+       _cairo_boilerplate_win32_printing_cleanup,
+       NULL, NULL, FALSE, TRUE, TRUE
+    },
+    {
+       "win32-printing", "win32", ".ps", NULL,
+       CAIRO_SURFACE_TYPE_RECORDING, CAIRO_CONTENT_COLOR, 0,
+       "cairo_win32_printing_surface_create",
+       _cairo_boilerplate_win32_printing_create_surface,
+       cairo_surface_create_similar,
+       NULL, NULL,
+       _cairo_boilerplate_win32_printing_get_image_surface,
+       _cairo_boilerplate_win32_printing_surface_write_to_png,
+       _cairo_boilerplate_win32_printing_cleanup,
+       NULL, NULL, FALSE, TRUE, TRUE
+    },
+};
+CAIRO_BOILERPLATE (win32_printing, targets)
+
+#else
+
+CAIRO_NO_BOILERPLATE (win32_printing)
+
+#endif
diff --git a/boilerplate/cairo-boilerplate-win32.c b/boilerplate/cairo-boilerplate-win32.c
new file mode 100644 (file)
index 0000000..f7be663
--- /dev/null
@@ -0,0 +1,78 @@
+/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/*
+ * Copyright © 2004,2006 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ */
+
+#include "cairo-boilerplate-private.h"
+
+#include <cairo-win32.h>
+
+static cairo_surface_t *
+_cairo_boilerplate_win32_create_surface (const char               *name,
+                                        cairo_content_t            content,
+                                        double                     width,
+                                        double                     height,
+                                        double                     max_width,
+                                        double                     max_height,
+                                        cairo_boilerplate_mode_t   mode,
+                                        int                        id,
+                                        void                     **closure)
+{
+    cairo_format_t format;
+
+    format = cairo_boilerplate_format_from_content (content);
+
+    *closure = NULL;
+
+    return cairo_win32_surface_create_with_dib (format, width, height);
+}
+
+static const cairo_boilerplate_target_t targets[] = {
+    {
+       "win32", "win32", NULL, NULL,
+       CAIRO_SURFACE_TYPE_WIN32, CAIRO_CONTENT_COLOR, 0,
+       "cairo_win32_surface_create_with_dib",
+       _cairo_boilerplate_win32_create_surface,
+       cairo_surface_create_similar,
+       NULL, NULL,
+       _cairo_boilerplate_get_image_surface,
+       cairo_surface_write_to_png,
+       NULL, NULL, NULL, TRUE, FALSE, FALSE
+    },
+    /* Testing the win32 surface isn't interesting, since for
+     * ARGB images it just chains to the image backend
+     */
+    {
+       "win32", "win32", NULL, NULL,
+       CAIRO_SURFACE_TYPE_WIN32, CAIRO_CONTENT_COLOR_ALPHA, 0,
+       "cairo_win32_surface_create_with_dib",
+       _cairo_boilerplate_win32_create_surface,
+       cairo_surface_create_similar,
+       NULL, NULL,
+       _cairo_boilerplate_get_image_surface,
+       cairo_surface_write_to_png,
+       NULL, NULL, NULL, FALSE, FALSE, FALSE
+    },
+};
+CAIRO_BOILERPLATE (win32, targets)
diff --git a/boilerplate/cairo-boilerplate-xcb.c b/boilerplate/cairo-boilerplate-xcb.c
new file mode 100644 (file)
index 0000000..f9810ee
--- /dev/null
@@ -0,0 +1,875 @@
+/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/*
+ * Copyright © 2004,2006 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ */
+
+#include "cairo-boilerplate-private.h"
+
+#include <cairo-xcb.h>
+
+#include <assert.h>
+
+/* Errors have response_type == 0 */
+#define CAIRO_XCB_ERROR 0
+
+static const cairo_user_data_key_t xcb_closure_key;
+
+typedef struct _xcb_target_closure {
+    xcb_connection_t *c;
+    cairo_device_t *device;
+    uint32_t drawable;
+    cairo_bool_t is_pixmap;
+    cairo_surface_t *surface;
+} xcb_target_closure_t;
+
+static cairo_status_t
+_cairo_boilerplate_xcb_handle_errors (xcb_target_closure_t *xtc)
+{
+    xcb_generic_event_t *ev;
+
+    if ((ev = xcb_poll_for_event (xtc->c)) != NULL) {
+       if (ev->response_type == CAIRO_XCB_ERROR) {
+           xcb_generic_error_t *error = (xcb_generic_error_t *) ev;
+
+           fprintf (stderr,
+                    "Detected error during xcb run: error=%d, "
+                    "seqno=0x%02x, major=%d, minor=%d\n",
+                    error->error_code, error->sequence,
+                    error->major_code, error->minor_code);
+       } else {
+           fprintf (stderr,
+                    "Detected unexpected event during xcb run: type=%d, seqno=0x%02x\n",
+                    ev->response_type, ev->sequence);
+       }
+       free (ev);
+
+       /* Silently discard all following errors */
+       while ((ev = xcb_poll_for_event (xtc->c)) != NULL)
+           free (ev);
+
+       return CAIRO_STATUS_WRITE_ERROR;
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_boilerplate_xcb_sync_server (xcb_target_closure_t *xtc)
+{
+    free (xcb_get_input_focus_reply (xtc->c,
+                                    xcb_get_input_focus (xtc->c), NULL));
+}
+
+static void
+_cairo_boilerplate_xcb_setup_test_surface (cairo_surface_t *surface)
+{
+
+    /* For testing purposes, tell the X server to strictly adhere to the
+     * Render specification.
+     */
+    cairo_xcb_device_debug_set_precision(cairo_surface_get_device(surface),
+                                        XCB_RENDER_POLY_MODE_PRECISE);
+}
+
+static void
+_cairo_boilerplate_xcb_cleanup (void *closure)
+{
+    xcb_target_closure_t *xtc = closure;
+    cairo_status_t status;
+
+    cairo_surface_finish (xtc->surface);
+    if (xtc->is_pixmap)
+       xcb_free_pixmap (xtc->c, xtc->drawable);
+    else
+       xcb_destroy_window (xtc->c, xtc->drawable);
+    cairo_surface_destroy (xtc->surface);
+
+    cairo_device_finish (xtc->device);
+    cairo_device_destroy (xtc->device);
+
+    /* First synchronize with the X server to make sure there are no more errors
+     * in-flight which we would miss otherwise */
+    _cairo_boilerplate_xcb_sync_server (xtc);
+    status = _cairo_boilerplate_xcb_handle_errors (xtc);
+    assert (status == CAIRO_STATUS_SUCCESS);
+
+    xcb_disconnect (xtc->c);
+
+    free (xtc);
+}
+
+static void
+_cairo_boilerplate_xcb_synchronize (void *closure)
+{
+    xcb_target_closure_t *xtc = closure;
+    cairo_status_t status;
+    free (xcb_get_image_reply (xtc->c,
+               xcb_get_image (xtc->c, XCB_IMAGE_FORMAT_Z_PIXMAP,
+                   xtc->drawable, 0, 0, 1, 1, /* AllPlanes */ -1),
+               0));
+
+    status = _cairo_boilerplate_xcb_handle_errors (xtc);
+    assert (status == CAIRO_STATUS_SUCCESS);
+}
+
+static xcb_render_pictforminfo_t *
+find_depth (xcb_connection_t  *connection,
+           int                depth,
+           void             **formats_out)
+{
+    xcb_render_query_pict_formats_reply_t *formats;
+    xcb_render_query_pict_formats_cookie_t cookie;
+    xcb_render_pictforminfo_iterator_t i;
+
+    cookie = xcb_render_query_pict_formats (connection);
+    xcb_flush (connection);
+
+    formats = xcb_render_query_pict_formats_reply (connection, cookie, 0);
+    if (formats == NULL)
+       return NULL;
+
+    for (i = xcb_render_query_pict_formats_formats_iterator (formats);
+        i.rem;
+        xcb_render_pictforminfo_next (&i))
+    {
+       if (XCB_RENDER_PICT_TYPE_DIRECT != i.data->type)
+           continue;
+
+       if (depth != i.data->depth)
+           continue;
+
+       *formats_out = formats;
+       return i.data;
+    }
+
+    free (formats);
+    return NULL;
+}
+
+static const cairo_user_data_key_t key;
+
+struct similar {
+    xcb_connection_t *connection;
+    xcb_drawable_t pixmap;
+};
+
+static void _destroy_similar (void *closure)
+{
+    struct similar *similar = closure;
+
+    xcb_free_pixmap (similar->connection, similar->pixmap);
+    free (similar);
+}
+
+struct xcb_info {
+       xcb_render_query_pict_formats_reply_t *formats;
+       xcb_render_pictforminfo_t *render_format[3];
+};
+
+static cairo_surface_t *
+_cairo_boilerplate_xcb_create_similar (cairo_surface_t *other,
+                                      cairo_content_t content,
+                                      int width, int height)
+{
+    cairo_device_t *device = cairo_surface_get_device (other);
+    struct xcb_info *info = cairo_device_get_user_data (device, &key);
+    xcb_screen_t *root;
+    cairo_surface_t *surface;
+    struct similar *similar;
+    xcb_render_pictforminfo_t *render_format;
+    int depth;
+
+    similar = malloc (sizeof (*similar));
+
+    switch (content) {
+    default:
+    case CAIRO_CONTENT_COLOR_ALPHA:
+           depth = 32;
+           render_format = info->render_format[0];
+           break;
+    case CAIRO_CONTENT_COLOR:
+           depth = 24;
+           render_format = info->render_format[1];
+           break;
+    case CAIRO_CONTENT_ALPHA:
+           depth = 8;
+           render_format = info->render_format[2];
+           break;
+    }
+
+    similar->connection =
+       cairo_xcb_device_get_connection (cairo_surface_get_device(other));
+    similar->pixmap = xcb_generate_id (similar->connection);
+
+    root = xcb_setup_roots_iterator(xcb_get_setup(similar->connection)).data;
+    xcb_create_pixmap (similar->connection, depth,
+                      similar->pixmap, root->root,
+                      width, height);
+
+    surface = cairo_xcb_surface_create_with_xrender_format (similar->connection,
+                                                           root,
+                                                           similar->pixmap,
+                                                           render_format,
+                                                           width, height);
+    cairo_surface_set_user_data (surface, &key, similar, _destroy_similar);
+
+    return surface;
+}
+
+static cairo_surface_t *
+_cairo_boilerplate_xcb_create_surface (const char               *name,
+                                      cairo_content_t            content,
+                                      double                     width,
+                                      double                     height,
+                                      double                     max_width,
+                                      double                     max_height,
+                                      cairo_boilerplate_mode_t   mode,
+                                      int                        id,
+                                      void                     **closure)
+{
+    xcb_screen_t *root;
+    xcb_target_closure_t *xtc;
+    xcb_connection_t *c;
+    xcb_render_query_pict_formats_cookie_t formats_cookie;
+    xcb_render_pictforminfo_t *render_format;
+    xcb_render_pictforminfo_iterator_t i;
+    struct xcb_info *info;
+    int depth;
+    xcb_void_cookie_t cookie;
+    cairo_surface_t *surface;
+    cairo_status_t status;
+
+    *closure = xtc = xmalloc (sizeof (xcb_target_closure_t));
+    info = xcalloc (1, sizeof (struct xcb_info));
+
+    if (width == 0)
+       width = 1;
+    if (height == 0)
+       height = 1;
+
+    xtc->c = c = xcb_connect(NULL,NULL);
+    if (c == NULL || xcb_connection_has_error(c)) {
+       free (xtc);
+       return NULL;
+    }
+
+    root = xcb_setup_roots_iterator(xcb_get_setup(c)).data;
+    formats_cookie = xcb_render_query_pict_formats (c);
+
+    xtc->surface = NULL;
+    xtc->is_pixmap = TRUE;
+    xtc->drawable = xcb_generate_id (c);
+    switch (content) {
+    case CAIRO_CONTENT_COLOR:
+       depth = 24;
+       break;
+
+    case CAIRO_CONTENT_COLOR_ALPHA:
+       depth = 32;
+       break;
+
+    case CAIRO_CONTENT_ALPHA:  /* would be XCB_PICT_STANDARD_A_8 */
+    default:
+       xcb_disconnect (c);
+       free (xtc);
+       return NULL;
+    }
+
+    cookie = xcb_create_pixmap_checked (c, depth,
+                                       xtc->drawable, root->root,
+                                       width, height);
+
+    /* slow, but sure */
+    if (xcb_request_check (c, cookie) != NULL) {
+       xcb_disconnect (c);
+       free (xtc);
+       return NULL;
+    }
+
+    info->formats = xcb_render_query_pict_formats_reply (c, formats_cookie, 0);
+    if (info->formats == NULL)
+       return NULL;
+
+    for (i = xcb_render_query_pict_formats_formats_iterator (info->formats);
+        i.rem;
+        xcb_render_pictforminfo_next (&i))
+    {
+       if (XCB_RENDER_PICT_TYPE_DIRECT != i.data->type)
+           continue;
+
+       if (i.data->depth == 32) {
+               if (info->render_format[0] == 0)
+                       info->render_format[0] = i.data;
+       } else if (i.data->depth == 24) {
+               if (info->render_format[1] == 0)
+                       info->render_format[1] = i.data;
+       } else if (i.data->depth == 8) {
+               if (info->render_format[2] == 0)
+                       info->render_format[2] = i.data;
+       }
+    }
+
+    assert (info->render_format[0]);
+    assert (info->render_format[1]);
+    assert (info->render_format[2]);
+
+    switch (content) {
+    default:
+    case CAIRO_CONTENT_COLOR_ALPHA:
+           render_format = info->render_format[0];
+           break;
+
+    case CAIRO_CONTENT_COLOR:
+           render_format = info->render_format[1];
+           break;
+
+    case CAIRO_CONTENT_ALPHA:  /* would be XCB_PICT_STANDARD_A_8 */
+           render_format = info->render_format[2];
+           break;
+    }
+
+    surface = cairo_xcb_surface_create_with_xrender_format (c, root,
+                                                           xtc->drawable,
+                                                           render_format,
+                                                           width, height);
+    cairo_device_set_user_data (cairo_surface_get_device (surface), &key, info, free);
+    if (mode != CAIRO_BOILERPLATE_MODE_PERF)
+       _cairo_boilerplate_xcb_setup_test_surface(surface);
+
+    xtc->device = cairo_device_reference (cairo_surface_get_device (surface));
+    status = cairo_surface_set_user_data (surface, &xcb_closure_key, xtc, NULL);
+    if (status == CAIRO_STATUS_SUCCESS)
+       return surface;
+
+    cairo_surface_destroy (surface);
+
+    _cairo_boilerplate_xcb_cleanup (xtc);
+    return cairo_boilerplate_surface_create_in_error (status);
+}
+
+static xcb_visualtype_t *
+lookup_visual (xcb_screen_t   *s,
+              xcb_visualid_t  visual)
+{
+    xcb_depth_iterator_t d;
+
+    d = xcb_screen_allowed_depths_iterator (s);
+    for (; d.rem; xcb_depth_next (&d)) {
+       xcb_visualtype_iterator_t v = xcb_depth_visuals_iterator (d.data);
+       for (; v.rem; xcb_visualtype_next (&v)) {
+           if (v.data->visual_id == visual)
+               return v.data;
+       }
+    }
+
+    return 0;
+}
+
+static cairo_surface_t *
+_cairo_boilerplate_xcb_create_window (const char               *name,
+                                     cairo_content_t            content,
+                                     double                     width,
+                                     double                     height,
+                                     double                     max_width,
+                                     double                     max_height,
+                                     cairo_boilerplate_mode_t   mode,
+                                     int                        id,
+                                     void                     **closure)
+{
+    xcb_target_closure_t *xtc;
+    xcb_connection_t *c;
+    xcb_screen_t *s;
+    xcb_void_cookie_t cookie;
+    cairo_surface_t *surface;
+    cairo_status_t status;
+    uint32_t values[] = { 1 };
+
+    *closure = xtc = xmalloc (sizeof (xcb_target_closure_t));
+
+    if (width == 0)
+       width = 1;
+    if (height == 0)
+       height = 1;
+
+    xtc->c = c = xcb_connect(NULL,NULL);
+    if (xcb_connection_has_error(c)) {
+       free (xtc);
+       return NULL;
+    }
+
+    xtc->surface = NULL;
+
+    s = xcb_setup_roots_iterator (xcb_get_setup (c)).data;
+    if (width > s->width_in_pixels || height > s->height_in_pixels) {
+       xcb_disconnect (c);
+       free (xtc);
+       return NULL;
+    }
+
+    xtc->is_pixmap = FALSE;
+    xtc->drawable = xcb_generate_id (c);
+    cookie = xcb_create_window_checked (c,
+                                       s->root_depth,
+                                       xtc->drawable,
+                                       s->root,
+                                       0, 0, width, height, 0,
+                                       XCB_WINDOW_CLASS_INPUT_OUTPUT,
+                                       s->root_visual,
+                                       XCB_CW_OVERRIDE_REDIRECT,
+                                       values);
+    xcb_map_window (c, xtc->drawable);
+
+    /* slow, but sure */
+    if (xcb_request_check (c, cookie) != NULL) {
+       xcb_disconnect (c);
+       free (xtc);
+       return NULL;
+    }
+
+    surface = cairo_xcb_surface_create (c,
+                                       xtc->drawable,
+                                       lookup_visual (s, s->root_visual),
+                                       width, height);
+
+    xtc->device = cairo_device_reference (cairo_surface_get_device (surface));
+    status = cairo_surface_set_user_data (surface, &xcb_closure_key, xtc, NULL);
+    if (status == CAIRO_STATUS_SUCCESS)
+       return surface;
+
+    cairo_surface_destroy (surface);
+
+    _cairo_boilerplate_xcb_cleanup (xtc);
+    return cairo_boilerplate_surface_create_in_error (status);
+}
+
+static cairo_surface_t *
+_cairo_boilerplate_xcb_create_window_db (const char               *name,
+                                        cairo_content_t            content,
+                                        double                     width,
+                                        double                     height,
+                                        double                     max_width,
+                                        double                     max_height,
+                                        cairo_boilerplate_mode_t   mode,
+                                        int                        id,
+                                        void                     **closure)
+{
+    xcb_target_closure_t *xtc;
+    xcb_connection_t *c;
+    xcb_screen_t *s;
+    xcb_void_cookie_t cookie;
+    cairo_surface_t *surface;
+    cairo_status_t status;
+    uint32_t values[] = { 1 };
+
+    *closure = xtc = xmalloc (sizeof (xcb_target_closure_t));
+
+    if (width == 0)
+       width = 1;
+    if (height == 0)
+       height = 1;
+
+    xtc->c = c = xcb_connect(NULL,NULL);
+    if (xcb_connection_has_error(c)) {
+       free (xtc);
+       return NULL;
+    }
+
+    xtc->surface = NULL;
+
+    s = xcb_setup_roots_iterator (xcb_get_setup (c)).data;
+    if (width > s->width_in_pixels || height > s->height_in_pixels) {
+       xcb_disconnect (c);
+       free (xtc);
+       return NULL;
+    }
+
+    xtc->is_pixmap = FALSE;
+    xtc->drawable = xcb_generate_id (c);
+    cookie = xcb_create_window_checked (c,
+                                       s->root_depth,
+                                       xtc->drawable,
+                                       s->root,
+                                       0, 0, width, height, 0,
+                                       XCB_WINDOW_CLASS_INPUT_OUTPUT,
+                                       s->root_visual,
+                                       XCB_CW_OVERRIDE_REDIRECT,
+                                       values);
+    xcb_map_window (c, xtc->drawable);
+
+    /* slow, but sure */
+    if (xcb_request_check (c, cookie) != NULL) {
+       xcb_disconnect (c);
+       free (xtc);
+       return NULL;
+    }
+
+    xtc->surface = cairo_xcb_surface_create (c,
+                                            xtc->drawable,
+                                            lookup_visual (s, s->root_visual),
+                                            width, height);
+    surface = cairo_surface_create_similar (xtc->surface, content, width, height);
+
+    xtc->device = cairo_device_reference (cairo_surface_get_device (surface));
+    status = cairo_surface_set_user_data (surface, &xcb_closure_key, xtc, NULL);
+    if (status == CAIRO_STATUS_SUCCESS)
+       return surface;
+
+    cairo_surface_destroy (surface);
+
+    _cairo_boilerplate_xcb_cleanup (xtc);
+    return cairo_boilerplate_surface_create_in_error (status);
+}
+
+static cairo_surface_t *
+_cairo_boilerplate_xcb_create_render_0_0 (const char               *name,
+                                         cairo_content_t            content,
+                                         double                     width,
+                                         double                     height,
+                                         double                     max_width,
+                                         double                     max_height,
+                                         cairo_boilerplate_mode_t   mode,
+                                         int                        id,
+                                         void                     **closure)
+{
+    xcb_screen_t *root;
+    xcb_target_closure_t *xtc;
+    xcb_connection_t *c;
+    xcb_render_pictforminfo_t *render_format;
+    int depth;
+    xcb_void_cookie_t cookie;
+    cairo_surface_t *surface;
+    cairo_status_t status;
+    void *formats;
+
+    *closure = xtc = xmalloc (sizeof (xcb_target_closure_t));
+
+    if (width == 0)
+       width = 1;
+    if (height == 0)
+       height = 1;
+
+    xtc->c = c = xcb_connect(NULL,NULL);
+    if (xcb_connection_has_error(c)) {
+       free (xtc);
+       return NULL;
+    }
+
+    root = xcb_setup_roots_iterator(xcb_get_setup(c)).data;
+
+    xtc->surface = NULL;
+    xtc->is_pixmap = TRUE;
+    xtc->drawable = xcb_generate_id (c);
+    switch (content) {
+    case CAIRO_CONTENT_COLOR:
+       depth = 24;
+       cookie = xcb_create_pixmap_checked (c, depth,
+                                           xtc->drawable, root->root,
+                                           width, height);
+       break;
+
+    case CAIRO_CONTENT_COLOR_ALPHA:
+       depth = 32;
+       cookie = xcb_create_pixmap_checked (c, depth,
+                                           xtc->drawable, root->root,
+                                           width, height);
+       break;
+
+    case CAIRO_CONTENT_ALPHA:  /* would be XCB_PICT_STANDARD_A_8 */
+    default:
+       xcb_disconnect (c);
+       free (xtc);
+       return NULL;
+    }
+
+    /* slow, but sure */
+    if (xcb_request_check (c, cookie) != NULL) {
+       xcb_disconnect (c);
+       free (xtc);
+       return NULL;
+    }
+    xcb_flush (c);
+
+    render_format = find_depth (c, depth, &formats);
+    if (render_format == NULL) {
+       xcb_disconnect (c);
+       free (xtc);
+       return NULL;
+    }
+
+    surface = cairo_xcb_surface_create_with_xrender_format (c, root,
+                                                           xtc->drawable,
+                                                           render_format,
+                                                           width, height);
+    if (cairo_surface_status (surface)) {
+       free (formats);
+       xcb_disconnect (c);
+       free (xtc);
+       return surface;
+    }
+
+    xtc->device = cairo_device_reference (cairo_surface_get_device (surface));
+    cairo_xcb_device_debug_cap_xrender_version (xtc->device, 0, 0);
+
+    assert (cairo_surface_get_device (surface) == xtc->device);
+
+    status = cairo_surface_set_user_data (surface, &xcb_closure_key, xtc, NULL);
+    if (status == CAIRO_STATUS_SUCCESS)
+       return surface;
+
+    cairo_surface_destroy (surface);
+
+    _cairo_boilerplate_xcb_cleanup (xtc);
+    return cairo_boilerplate_surface_create_in_error (status);
+}
+
+static cairo_surface_t *
+_cairo_boilerplate_xcb_create_fallback (const char               *name,
+                                       cairo_content_t            content,
+                                       double                     width,
+                                       double                     height,
+                                       double                     max_width,
+                                       double                     max_height,
+                                       cairo_boilerplate_mode_t   mode,
+                                       int                        id,
+                                       void                     **closure)
+{
+    xcb_target_closure_t *xtc;
+    xcb_connection_t *c;
+    xcb_screen_t *s;
+    xcb_void_cookie_t cookie;
+    cairo_surface_t *surface;
+    cairo_status_t status;
+    uint32_t values[] = { 1 };
+
+    *closure = xtc = xmalloc (sizeof (xcb_target_closure_t));
+
+    if (width == 0)
+       width = 1;
+    if (height == 0)
+       height = 1;
+
+    xtc->c = c = xcb_connect (NULL,NULL);
+    if (xcb_connection_has_error(c)) {
+       free (xtc);
+       return NULL;
+    }
+
+    s = xcb_setup_roots_iterator (xcb_get_setup (c)).data;
+    if (width > s->width_in_pixels || height > s->height_in_pixels) {
+       xcb_disconnect (c);
+       free (xtc);
+       return NULL;
+    }
+
+    xtc->surface = NULL;
+    xtc->is_pixmap = FALSE;
+    xtc->drawable = xcb_generate_id (c);
+    cookie = xcb_create_window_checked (c,
+                                       s->root_depth,
+                                       xtc->drawable,
+                                       s->root,
+                                       0, 0, width, height, 0,
+                                       XCB_WINDOW_CLASS_INPUT_OUTPUT,
+                                       s->root_visual,
+                                       XCB_CW_OVERRIDE_REDIRECT,
+                                       values);
+    xcb_map_window (c, xtc->drawable);
+
+    /* slow, but sure */
+    if (xcb_request_check (c, cookie) != NULL) {
+       xcb_disconnect (c);
+       free (xtc);
+       return NULL;
+    }
+
+    surface = cairo_xcb_surface_create (c,
+                                       xtc->drawable,
+                                       lookup_visual (s, s->root_visual),
+                                       width, height);
+    if (cairo_surface_status (surface)) {
+       xcb_disconnect (c);
+       free (xtc);
+       return surface;
+    }
+
+    cairo_xcb_device_debug_cap_xrender_version (cairo_surface_get_device (surface),
+                                               -1, -1);
+
+    xtc->device = cairo_device_reference (cairo_surface_get_device (surface));
+    status = cairo_surface_set_user_data (surface, &xcb_closure_key, xtc, NULL);
+    if (status == CAIRO_STATUS_SUCCESS)
+       return surface;
+
+    cairo_surface_destroy (surface);
+
+    _cairo_boilerplate_xcb_cleanup (xtc);
+    return cairo_boilerplate_surface_create_in_error (status);
+}
+
+static cairo_status_t
+_cairo_boilerplate_xcb_finish_surface (cairo_surface_t *surface)
+{
+    xcb_target_closure_t *xtc = cairo_surface_get_user_data (surface,
+                                                            &xcb_closure_key);
+    cairo_status_t status;
+
+    if (xtc->surface != NULL) {
+       cairo_t *cr;
+
+       cr = cairo_create (xtc->surface);
+       cairo_set_source_surface (cr, surface, 0, 0);
+       cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+       cairo_paint (cr);
+       cairo_destroy (cr);
+
+       surface = xtc->surface;
+    }
+
+    cairo_surface_flush (surface);
+    if (cairo_surface_status (surface))
+       return cairo_surface_status (surface);
+
+    /* First synchronize with the X server to make sure there are no more errors
+     * in-flight which we would miss otherwise */
+    _cairo_boilerplate_xcb_sync_server (xtc);
+    status = _cairo_boilerplate_xcb_handle_errors (xtc);
+    if (status)
+       return status;
+
+    if (xcb_connection_has_error (xtc->c))
+       return CAIRO_STATUS_WRITE_ERROR;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static const cairo_boilerplate_target_t targets[] = {
+    /* Acceleration architectures may make the results differ by a
+     * bit, so we set the error tolerance to 1. */
+    {
+       "xcb", "traps", NULL, NULL,
+       CAIRO_SURFACE_TYPE_XCB, CAIRO_CONTENT_COLOR_ALPHA, 1,
+       "cairo_xcb_surface_create_with_xrender_format",
+       _cairo_boilerplate_xcb_create_surface,
+       _cairo_boilerplate_xcb_create_similar,
+       NULL,
+       _cairo_boilerplate_xcb_finish_surface,
+       _cairo_boilerplate_get_image_surface,
+       cairo_surface_write_to_png,
+       _cairo_boilerplate_xcb_cleanup,
+       _cairo_boilerplate_xcb_synchronize,
+        NULL,
+       TRUE, FALSE, FALSE
+    },
+    {
+       "xcb", "traps", NULL, NULL,
+       CAIRO_SURFACE_TYPE_XCB, CAIRO_CONTENT_COLOR, 1,
+       "cairo_xcb_surface_create_with_xrender_format",
+       _cairo_boilerplate_xcb_create_surface,
+       _cairo_boilerplate_xcb_create_similar,
+       NULL,
+       _cairo_boilerplate_xcb_finish_surface,
+       _cairo_boilerplate_get_image_surface,
+       cairo_surface_write_to_png,
+       _cairo_boilerplate_xcb_cleanup,
+       _cairo_boilerplate_xcb_synchronize,
+        NULL,
+       FALSE, FALSE, FALSE
+    },
+    {
+       "xcb-window", "traps", NULL, NULL,
+       CAIRO_SURFACE_TYPE_XCB, CAIRO_CONTENT_COLOR, 1,
+       "cairo_xcb_surface_create_with_xrender_format",
+       _cairo_boilerplate_xcb_create_window,
+       _cairo_boilerplate_xcb_create_similar,
+       NULL,
+       _cairo_boilerplate_xcb_finish_surface,
+       _cairo_boilerplate_get_image_surface,
+       cairo_surface_write_to_png,
+       _cairo_boilerplate_xcb_cleanup,
+       _cairo_boilerplate_xcb_synchronize,
+        NULL,
+       FALSE, FALSE, FALSE
+    },
+    {
+       "xcb-window&", "traps", NULL, NULL,
+       CAIRO_SURFACE_TYPE_XCB, CAIRO_CONTENT_COLOR, 1,
+       "cairo_xcb_surface_create_with_xrender_format",
+       _cairo_boilerplate_xcb_create_window_db,
+       _cairo_boilerplate_xcb_create_similar,
+       NULL,
+       _cairo_boilerplate_xcb_finish_surface,
+       _cairo_boilerplate_get_image_surface,
+       cairo_surface_write_to_png,
+       _cairo_boilerplate_xcb_cleanup,
+       _cairo_boilerplate_xcb_synchronize,
+        NULL,
+       FALSE, FALSE, FALSE
+    },
+    {
+       "xcb-render-0.0", "xlib-fallback", NULL, NULL,
+       CAIRO_SURFACE_TYPE_XCB, CAIRO_CONTENT_COLOR_ALPHA, 1,
+       "cairo_xcb_surface_create_with_xrender_format",
+       _cairo_boilerplate_xcb_create_render_0_0,
+       cairo_surface_create_similar,
+       NULL,
+       _cairo_boilerplate_xcb_finish_surface,
+       _cairo_boilerplate_get_image_surface,
+       cairo_surface_write_to_png,
+       _cairo_boilerplate_xcb_cleanup,
+       _cairo_boilerplate_xcb_synchronize,
+        NULL,
+       FALSE, FALSE, FALSE
+    },
+    {
+       "xcb-render-0.0", "xlib-fallback", NULL, NULL,
+       CAIRO_SURFACE_TYPE_XCB, CAIRO_CONTENT_COLOR, 1,
+       "cairo_xcb_surface_create_with_xrender_format",
+       _cairo_boilerplate_xcb_create_render_0_0,
+       cairo_surface_create_similar,
+       NULL,
+       _cairo_boilerplate_xcb_finish_surface,
+       _cairo_boilerplate_get_image_surface,
+       cairo_surface_write_to_png,
+       _cairo_boilerplate_xcb_cleanup,
+       _cairo_boilerplate_xcb_synchronize,
+        NULL,
+       FALSE, FALSE, FALSE
+    },
+    {
+       "xcb-fallback", "xlib-fallback", NULL, NULL,
+       CAIRO_SURFACE_TYPE_XCB, CAIRO_CONTENT_COLOR, 1,
+       "cairo_xcb_surface_create_with_xrender_format",
+       _cairo_boilerplate_xcb_create_fallback,
+       cairo_surface_create_similar,
+       NULL,
+       _cairo_boilerplate_xcb_finish_surface,
+       _cairo_boilerplate_get_image_surface,
+       cairo_surface_write_to_png,
+       _cairo_boilerplate_xcb_cleanup,
+       _cairo_boilerplate_xcb_synchronize,
+        NULL,
+       FALSE, FALSE, FALSE
+    },
+};
+CAIRO_BOILERPLATE (xcb, targets)
diff --git a/boilerplate/cairo-boilerplate-xlib.c b/boilerplate/cairo-boilerplate-xlib.c
new file mode 100644 (file)
index 0000000..38c1135
--- /dev/null
@@ -0,0 +1,636 @@
+/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/*
+ * Copyright © 2004,2006 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ */
+
+#include "cairo-boilerplate-private.h"
+#include "cairo-boilerplate-xlib.h"
+
+#include <cairo-xlib.h>
+#if CAIRO_HAS_XLIB_XRENDER_SURFACE
+#include <cairo-xlib-xrender.h>
+#endif
+
+#include <X11/Xutil.h> /* for XDestroyImage */
+
+#if !CAIRO_HAS_XLIB_XRENDER_SURFACE
+#define PolyModePrecise                            0
+#endif
+
+static const cairo_user_data_key_t key;
+
+typedef struct _xlib_target_closure {
+    Display *dpy;
+    Drawable drawable;
+    cairo_bool_t drawable_is_pixmap;
+} xlib_target_closure_t;
+
+static void
+_cairo_boilerplate_xlib_cleanup (void *closure)
+{
+    xlib_target_closure_t *xtc = closure;
+
+    if (xtc->drawable) {
+       if (xtc->drawable_is_pixmap)
+           XFreePixmap (xtc->dpy, xtc->drawable);
+       else
+           XDestroyWindow (xtc->dpy, xtc->drawable);
+    }
+    XCloseDisplay (xtc->dpy);
+    free (xtc);
+}
+
+static void
+_cairo_boilerplate_xlib_synchronize (void *closure)
+{
+    xlib_target_closure_t *xtc = closure;
+    XImage *ximage;
+
+    ximage = XGetImage (xtc->dpy, xtc->drawable,
+                       0, 0, 1, 1, AllPlanes, ZPixmap);
+    if (ximage != NULL)
+       XDestroyImage (ximage);
+}
+
+static cairo_bool_t
+_cairo_boilerplate_xlib_check_screen_size (Display *dpy,
+                                          int      screen,
+                                          int      width,
+                                          int      height)
+{
+    Screen *scr = XScreenOfDisplay (dpy, screen);
+    return width <= WidthOfScreen (scr) && height <= HeightOfScreen (scr);
+}
+
+static void
+_cairo_boilerplate_xlib_setup_test_surface (cairo_surface_t *surface)
+{
+
+    /* For testing purposes, tell the X server to strictly adhere to the
+     * Render specification.
+     */
+    cairo_xlib_device_debug_set_precision(cairo_surface_get_device(surface),
+                                         PolyModePrecise);
+}
+
+
+#if CAIRO_HAS_XLIB_XRENDER_SURFACE
+/* For the xlib backend we distinguish between TEST and PERF mode in a
+ * couple of ways.
+ *
+ * For TEST, we always test against pixmaps of depth 32 (for
+ * COLOR_ALPHA) or 24 (for COLOR) and we use XSynchronize to make it
+ * easier to debug problems.
+ *
+ * For PERF, we test against 32-bit pixmaps for COLOR_ALPHA, but for
+ * COLOR we test against _windows_ at the depth of the default visual.
+ * For obvious reasons, we don't use XSynchronize.
+ */
+static cairo_surface_t *
+_cairo_boilerplate_xlib_test_create_surface (Display              *dpy,
+                                            cairo_content_t        content,
+                                            int                    width,
+                                            int                    height,
+                                            xlib_target_closure_t *xtc)
+{
+    XRenderPictFormat *xrender_format;
+    cairo_surface_t *surface;
+
+    /* This kills performance, but it makes debugging much
+     * easier. That's why we have it here when in TEST mode, but not
+     * over in PERF mode. */
+    XSynchronize (xtc->dpy, 1);
+
+    /* XXX: Currently we don't do any xlib testing when the X server
+     * doesn't have the Render extension. We could do better here,
+     * (perhaps by converting the tests from ARGB32 to RGB24). One
+     * step better would be to always test the non-Render fallbacks
+     * for each test even if the server does have the Render
+     * extension. That would probably be through another
+     * cairo_boilerplate_target which would use an extended version of
+     * cairo_test_xlib_disable_render. */
+    switch (content) {
+    case CAIRO_CONTENT_COLOR_ALPHA:
+       xrender_format = XRenderFindStandardFormat (dpy, PictStandardARGB32);
+       break;
+    case CAIRO_CONTENT_COLOR:
+       xrender_format = XRenderFindStandardFormat (dpy, PictStandardRGB24);
+       break;
+    case CAIRO_CONTENT_ALPHA:
+    default:
+       CAIRO_BOILERPLATE_DEBUG (("Invalid content for xlib test: %d\n", content));
+       return NULL;
+    }
+    if (xrender_format == NULL) {
+       CAIRO_BOILERPLATE_DEBUG (("X server does not have the Render extension.\n"));
+       return NULL;
+    }
+
+    xtc->drawable = XCreatePixmap (dpy, DefaultRootWindow (dpy),
+                                  width, height, xrender_format->depth);
+    xtc->drawable_is_pixmap = TRUE;
+
+    surface = cairo_xlib_surface_create_with_xrender_format (dpy, xtc->drawable,
+                                                         DefaultScreenOfDisplay (dpy),
+                                                         xrender_format,
+                                                         width, height);
+
+    _cairo_boilerplate_xlib_setup_test_surface(surface);
+
+    return surface;
+}
+
+static cairo_surface_t *
+_cairo_boilerplate_xlib_perf_create_surface (Display              *dpy,
+                                            cairo_content_t        content,
+                                            int                    width,
+                                            int                    height,
+                                            xlib_target_closure_t *xtc)
+{
+    XSetWindowAttributes attr;
+    XRenderPictFormat *xrender_format;
+    Visual *visual;
+
+    switch (content) {
+    case CAIRO_CONTENT_COLOR_ALPHA:
+       xrender_format = XRenderFindStandardFormat (dpy, PictStandardARGB32);
+       if (xrender_format == NULL) {
+           CAIRO_BOILERPLATE_DEBUG (("X server does not have the Render extension.\n"));
+           return NULL;
+       }
+
+       xtc->drawable = XCreatePixmap (dpy, DefaultRootWindow (dpy),
+                                      width, height, xrender_format->depth);
+       xtc->drawable_is_pixmap = TRUE;
+       break;
+
+    case CAIRO_CONTENT_COLOR:
+       if (! _cairo_boilerplate_xlib_check_screen_size (dpy,
+                                                        DefaultScreen (dpy),
+                                                        width, height)) {
+           CAIRO_BOILERPLATE_DEBUG (("Surface is larger than the Screen.\n"));
+           return NULL;
+       }
+
+       visual = DefaultVisual (dpy, DefaultScreen (dpy));
+       xrender_format = XRenderFindVisualFormat (dpy, visual);
+       if (xrender_format == NULL) {
+           CAIRO_BOILERPLATE_DEBUG (("X server does not have the Render extension.\n"));
+           return NULL;
+       }
+
+       attr.override_redirect = True;
+       xtc->drawable = XCreateWindow (dpy, DefaultRootWindow (dpy), 0, 0,
+                                      width, height, 0, xrender_format->depth,
+                                      InputOutput, visual, CWOverrideRedirect, &attr);
+       XMapWindow (dpy, xtc->drawable);
+       xtc->drawable_is_pixmap = FALSE;
+       break;
+
+    case CAIRO_CONTENT_ALPHA:
+    default:
+       CAIRO_BOILERPLATE_DEBUG (("Invalid content for xlib test: %d\n", content));
+       return NULL;
+    }
+
+    return cairo_xlib_surface_create_with_xrender_format (dpy, xtc->drawable,
+                                                         DefaultScreenOfDisplay (dpy),
+                                                         xrender_format,
+                                                         width, height);
+}
+
+struct similar {
+       Display *dpy;
+       Pixmap pixmap;
+};
+
+static void _destroy_similar (void *closure)
+{
+    struct similar *similar = closure;
+
+    XFreePixmap (similar->dpy, similar->pixmap);
+    free (similar);
+}
+
+static cairo_surface_t *
+_cairo_boilerplate_xlib_create_similar (cairo_surface_t                *other,
+                                       cairo_content_t          content,
+                                       int                      width,
+                                       int                      height)
+{
+    XRenderPictFormat *xrender_format;
+    uint32_t format;
+    struct similar *similar;
+    cairo_surface_t *surface;
+
+    similar = malloc (sizeof (*similar));
+    similar->dpy = cairo_xlib_surface_get_display (other);
+
+    switch (content) {
+    default:
+    case CAIRO_CONTENT_COLOR_ALPHA: format = PictStandardARGB32; break;
+    case CAIRO_CONTENT_COLOR: format = PictStandardRGB24; break;
+    case CAIRO_CONTENT_ALPHA: format = PictStandardA8; break;
+    }
+
+    xrender_format = XRenderFindStandardFormat (similar->dpy, format);
+    similar->pixmap = XCreatePixmap (similar->dpy,
+                                    DefaultRootWindow (similar->dpy),
+                                    width, height,
+                                    xrender_format->depth);
+
+    surface =
+           cairo_xlib_surface_create_with_xrender_format (similar->dpy,
+                                                          similar->pixmap,
+                                                          DefaultScreenOfDisplay (similar->dpy),
+                                                          xrender_format,
+                                                          width, height);
+
+    cairo_surface_set_user_data (surface, &key, similar, _destroy_similar);
+
+    return surface;
+}
+
+static cairo_surface_t *
+_cairo_boilerplate_xlib_create_surface (const char               *name,
+                                       cairo_content_t            content,
+                                       double                     width,
+                                       double                     height,
+                                       double                     max_width,
+                                       double                     max_height,
+                                       cairo_boilerplate_mode_t   mode,
+                                       int                        id,
+                                       void                     **closure)
+{
+    xlib_target_closure_t *xtc;
+    Display *dpy;
+    cairo_surface_t *surface;
+
+    *closure = xtc = xcalloc (1, sizeof (xlib_target_closure_t));
+
+    width = ceil (width);
+    if (width < 1)
+       width = 1;
+
+    height = ceil (height);
+    if (height < 1)
+       height = 1;
+
+    xtc->dpy = dpy = XOpenDisplay (NULL);
+    if (xtc->dpy == NULL) {
+       free (xtc);
+       CAIRO_BOILERPLATE_DEBUG (("Failed to open display: %s\n", XDisplayName(0)));
+       return NULL;
+    }
+
+    if (mode == CAIRO_BOILERPLATE_MODE_TEST)
+       surface = _cairo_boilerplate_xlib_test_create_surface (dpy, content, width, height, xtc);
+    else /* mode == CAIRO_BOILERPLATE_MODE_PERF */
+       surface = _cairo_boilerplate_xlib_perf_create_surface (dpy, content, width, height, xtc);
+
+    if (surface == NULL || cairo_surface_status (surface))
+       _cairo_boilerplate_xlib_cleanup (xtc);
+
+    return surface;
+}
+
+static cairo_surface_t *
+_cairo_boilerplate_xlib_window_create_surface (const char               *name,
+                                              cairo_content_t            content,
+                                              double                     width,
+                                              double                     height,
+                                              double                     max_width,
+                                              double                     max_height,
+                                              cairo_boilerplate_mode_t   mode,
+                                              int                        id,
+                                              void                     **closure)
+{
+    xlib_target_closure_t *xtc;
+    Display *dpy;
+    Screen *scr;
+    int screen, x, y;
+    XSetWindowAttributes attr;
+    cairo_surface_t *surface;
+
+    /* We're not yet bothering to support perf mode for the
+     * xlib-fallback surface. */
+    if (mode == CAIRO_BOILERPLATE_MODE_PERF)
+       return NULL;
+
+    /* We also don't support drawing with destination-alpha in the
+     * xlib-fallback surface. */
+    if (content == CAIRO_CONTENT_COLOR_ALPHA)
+       return NULL;
+
+    *closure = xtc = xmalloc (sizeof (xlib_target_closure_t));
+
+    width = ceil (width);
+    if (width < 1)
+       width = 1;
+
+    height = ceil (height);
+    if (height < 1)
+       height = 1;
+
+    xtc->dpy = dpy = XOpenDisplay (NULL);
+    if (xtc->dpy == NULL) {
+       CAIRO_BOILERPLATE_DEBUG (("Failed to open display: %s\n", XDisplayName(0)));
+       free (xtc);
+       return NULL;
+    }
+
+    /* This kills performance, but it makes debugging much
+     * easier. That's why we have it here only after explicitly not
+     * supporting PERF mode.*/
+    XSynchronize (dpy, 1);
+
+    screen = DefaultScreen (dpy);
+    if (! _cairo_boilerplate_xlib_check_screen_size (dpy, screen,
+                                                    width, height)) {
+       CAIRO_BOILERPLATE_DEBUG (("Surface is larger than the Screen.\n"));
+       XCloseDisplay (dpy);
+       free (xtc);
+       return NULL;
+    }
+
+    /* tile the windows so threads do not overlap */
+    scr = XScreenOfDisplay (dpy, screen);
+    x = 10; y = 15;
+    if (id-- > 1) do {
+       x += max_width;
+       if (x + max_width > WidthOfScreen (scr)) {
+           x = 10;
+           y += max_height;
+           if (y + max_height > HeightOfScreen (scr)) {
+               XCloseDisplay (dpy);
+               free (xtc);
+               return NULL;
+           }
+       }
+    } while (--id);
+
+    attr.override_redirect = True;
+    xtc->drawable = XCreateWindow (dpy, DefaultRootWindow (dpy),
+                                  x, y,
+                                  width, height, 0,
+                                  DefaultDepth (dpy, screen),
+                                  InputOutput,
+                                  DefaultVisual (dpy, screen),
+                                  CWOverrideRedirect, &attr);
+    XMapWindow (dpy, xtc->drawable);
+    xtc->drawable_is_pixmap = FALSE;
+
+    surface = cairo_xlib_surface_create (dpy, xtc->drawable,
+                                        DefaultVisual (dpy, screen),
+                                        width, height);
+    if (cairo_surface_status (surface))
+       _cairo_boilerplate_xlib_cleanup (xtc);
+
+    _cairo_boilerplate_xlib_setup_test_surface(surface);
+
+    return surface;
+}
+
+cairo_status_t
+cairo_boilerplate_xlib_surface_disable_render (cairo_surface_t *abstract_surface)
+{
+#if 0
+    /* The following stunt doesn't work with xlib-xcb because it doesn't use
+     * cairo_xlib_surface_t for its surfaces. Sadly, there is no sane
+     * alternative, so we can't disable render with xlib-xcb.
+     * FIXME: Find an alternative. */
+#if !CAIRO_HAS_XLIB_XCB_FUNCTIONS
+    cairo_xlib_surface_t *surface = (cairo_xlib_surface_t*) abstract_surface;
+
+    if (cairo_surface_get_type (abstract_surface) != CAIRO_SURFACE_TYPE_XLIB)
+       return CAIRO_STATUS_SURFACE_TYPE_MISMATCH;
+
+    surface->render_major = surface->render_minor = -1;
+    surface->xrender_format = NULL;
+
+    /* The content type is forced by _xrender_format_to_content() during
+     * non-Render surface creation, so repeat the procedure here. */
+    surface->base.content = CAIRO_CONTENT_COLOR;
+
+    /* These flags are set based on known bugs and lack of RENDER support */
+#if CAIRO_XLIB_SURFACE_HAS_BUGGY_GRADIENTS
+    surface->buggy_gradients = TRUE;
+#endif
+#if CAIRO_XLIB_SURFACE_HAS_BUGGY_PAD_REFLECT
+    surface->buggy_pad_reflect = TRUE;
+#endif
+#if CAIRO_XLIB_SURFACE_HAS_BUGGY_REPEAT
+    surface->buggy_repeat = TRUE;
+#endif
+#endif
+#endif
+
+    return CAIRO_STATUS_SUCCESS;
+}
+#else
+cairo_status_t
+cairo_boilerplate_xlib_surface_disable_render (cairo_surface_t *abstract_surface)
+{
+    return CAIRO_STATUS_SUCCESS;
+}
+#endif
+
+
+#if CAIRO_HAS_XLIB_SURFACE
+/* The xlib-fallback target differs from the xlib target in two ways:
+ *
+ * 1. It creates its surfaces without relying on the Render extension
+ *
+ * 2. It disables use of the Render extension for its surfaces
+ *
+ * This provides testing of the non-Render fallback paths we have in
+ * cairo-xlib-surface.c
+ */
+static cairo_surface_t *
+_cairo_boilerplate_xlib_fallback_create_surface (const char               *name,
+                                                cairo_content_t            content,
+                                                double                     width,
+                                                double                     height,
+                                                double                     max_width,
+                                                double                     max_height,
+                                                cairo_boilerplate_mode_t   mode,
+                                                int                        id,
+                                                void                     **closure)
+{
+    xlib_target_closure_t *xtc;
+    Display *dpy;
+    Screen *scr;
+    int screen, x, y;
+    XSetWindowAttributes attr;
+    cairo_surface_t *surface;
+
+    /* We're not yet bothering to support perf mode for the
+     * xlib-fallback surface. */
+    if (mode == CAIRO_BOILERPLATE_MODE_PERF)
+       return NULL;
+
+    /* We also don't support drawing with destination-alpha in the
+     * xlib-fallback surface. */
+    if (content == CAIRO_CONTENT_COLOR_ALPHA)
+       return NULL;
+
+    *closure = xtc = xmalloc (sizeof (xlib_target_closure_t));
+
+    width = ceil (width);
+    if (width < 1)
+       width = 1;
+
+    height = ceil (height);
+    if (height < 1)
+       height = 1;
+
+    xtc->dpy = dpy = XOpenDisplay (NULL);
+    if (xtc->dpy == NULL) {
+       CAIRO_BOILERPLATE_DEBUG (("Failed to open display: %s\n", XDisplayName(0)));
+       free (xtc);
+       return NULL;
+    }
+
+    /* This kills performance, but it makes debugging much
+     * easier. That's why we have it here only after explicitly not
+     * supporting PERF mode.*/
+    XSynchronize (dpy, 1);
+
+    screen = DefaultScreen (dpy);
+    if (! _cairo_boilerplate_xlib_check_screen_size (dpy, screen,
+                                                    width, height)) {
+       CAIRO_BOILERPLATE_DEBUG (("Surface is larger than the Screen.\n"));
+       XCloseDisplay (dpy);
+       free (xtc);
+       return NULL;
+    }
+
+    /* tile the windows so threads do not overlap */
+    scr = XScreenOfDisplay (dpy, screen);
+    x = y = 0;
+    if (id-- > 1) do {
+       x += max_width;
+       if (x + max_width > WidthOfScreen (scr)) {
+           x = 0;
+           y += max_height;
+           if (y + max_height > HeightOfScreen (scr)) {
+               XCloseDisplay (dpy);
+               free (xtc);
+               return NULL;
+           }
+       }
+    } while (--id);
+
+    attr.override_redirect = True;
+    xtc->drawable = XCreateWindow (dpy, DefaultRootWindow (dpy),
+                                  x, y,
+                                  width, height, 0,
+                                  DefaultDepth (dpy, screen),
+                                  InputOutput,
+                                  DefaultVisual (dpy, screen),
+                                  CWOverrideRedirect, &attr);
+    XMapWindow (dpy, xtc->drawable);
+    xtc->drawable_is_pixmap = FALSE;
+
+    surface = cairo_xlib_surface_create (dpy, xtc->drawable,
+                                        DefaultVisual (dpy, screen),
+                                        width, height);
+    if (cairo_surface_status (surface))
+       _cairo_boilerplate_xlib_cleanup (xtc);
+    else
+       cairo_boilerplate_xlib_surface_disable_render (surface);
+
+    _cairo_boilerplate_xlib_setup_test_surface(surface);
+
+    return surface;
+}
+#endif
+
+static const cairo_boilerplate_target_t targets[] = {
+#if CAIRO_HAS_XLIB_XRENDER_SURFACE
+    /* Acceleration architectures may make the results differ by a
+     * bit, so we set the error tolerance to 1. */
+    {
+       "xlib", "traps", NULL, "xlib-fallback",
+       CAIRO_SURFACE_TYPE_XLIB, CAIRO_CONTENT_COLOR_ALPHA, 1,
+       "cairo_xlib_surface_create_with_xrender_format",
+       _cairo_boilerplate_xlib_create_surface,
+       _cairo_boilerplate_xlib_create_similar,
+       NULL, NULL,
+       _cairo_boilerplate_get_image_surface,
+       cairo_surface_write_to_png,
+       _cairo_boilerplate_xlib_cleanup,
+       _cairo_boilerplate_xlib_synchronize,
+        NULL,
+       TRUE, FALSE, FALSE
+    },
+    {
+       "xlib", "traps", NULL, "xlib-fallback",
+       CAIRO_SURFACE_TYPE_XLIB, CAIRO_CONTENT_COLOR, 1,
+       "cairo_xlib_surface_create_with_xrender_format",
+       _cairo_boilerplate_xlib_create_surface,
+       _cairo_boilerplate_xlib_create_similar,
+       NULL, NULL,
+       _cairo_boilerplate_get_image_surface,
+       cairo_surface_write_to_png,
+       _cairo_boilerplate_xlib_cleanup,
+       _cairo_boilerplate_xlib_synchronize,
+        NULL,
+       FALSE, FALSE, FALSE
+    },
+    {
+       "xlib-window", "traps", NULL, NULL,
+       CAIRO_SURFACE_TYPE_XLIB, CAIRO_CONTENT_COLOR, 1,
+       "cairo_xlib_surface_create",
+       _cairo_boilerplate_xlib_window_create_surface,
+       cairo_surface_create_similar,
+       NULL, NULL,
+       _cairo_boilerplate_get_image_surface,
+       cairo_surface_write_to_png,
+       _cairo_boilerplate_xlib_cleanup,
+       _cairo_boilerplate_xlib_synchronize,
+        NULL,
+       FALSE, FALSE, FALSE
+    },
+#endif
+#if CAIRO_HAS_XLIB_SURFACE
+    /* This is a fallback surface which uses xlib fallbacks instead of
+     * the Render extension. */
+    {
+       "xlib-fallback", "xlib", NULL, NULL,
+       CAIRO_SURFACE_TYPE_XLIB, CAIRO_CONTENT_COLOR, 1,
+       "cairo_xlib_surface_create",
+       _cairo_boilerplate_xlib_fallback_create_surface,
+       cairo_surface_create_similar,
+       NULL, NULL,
+       _cairo_boilerplate_get_image_surface,
+       cairo_surface_write_to_png,
+       _cairo_boilerplate_xlib_cleanup,
+       _cairo_boilerplate_xlib_synchronize,
+        NULL,
+       FALSE, FALSE, FALSE
+    },
+#endif
+};
+CAIRO_BOILERPLATE (xlib, targets)
diff --git a/boilerplate/cairo-boilerplate-xlib.h b/boilerplate/cairo-boilerplate-xlib.h
new file mode 100644 (file)
index 0000000..9a63918
--- /dev/null
@@ -0,0 +1,33 @@
+/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/*
+ * Copyright © 2007 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Behdad Esfahbod <behdad@behdad.org>
+ */
+
+#ifndef _CAIRO_BOILERPLATE_XLIB_H_
+#define _CAIRO_BOILERPLATE_XLIB_H_
+
+cairo_status_t
+cairo_boilerplate_xlib_surface_disable_render (cairo_surface_t *surface);
+
+#endif
diff --git a/boilerplate/cairo-boilerplate.c b/boilerplate/cairo-boilerplate.c
new file mode 100644 (file)
index 0000000..8e252f5
--- /dev/null
@@ -0,0 +1,982 @@
+/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/*
+ * Copyright © 2004,2006 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ */
+
+#define CAIRO_VERSION_H 1
+
+#include "cairo-boilerplate-private.h"
+#include "cairo-boilerplate-scaled-font.h"
+
+#include <pixman.h>
+
+#include <cairo-types-private.h>
+#include <cairo-scaled-font-private.h>
+
+#if CAIRO_HAS_SCRIPT_SURFACE
+#include <cairo-script.h>
+#endif
+
+/* get the "real" version info instead of dummy cairo-version.h */
+#undef CAIRO_VERSION_H
+#include "../cairo-version.h"
+
+#include <stdlib.h>
+#include <ctype.h>
+#include <assert.h>
+#include <errno.h>
+
+#if HAVE_DLFCN_H
+#include <dlfcn.h>
+#endif
+
+#if HAVE_UNISTD_H && HAVE_FCNTL_H && HAVE_SIGNAL_H && HAVE_SYS_STAT_H && HAVE_SYS_SOCKET_H && HAVE_SYS_UN_H
+#include <unistd.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#define HAS_DAEMON 1
+#define SOCKET_PATH "./.any2ppm"
+#endif
+
+cairo_content_t
+cairo_boilerplate_content (cairo_content_t content)
+{
+    if (content == CAIRO_TEST_CONTENT_COLOR_ALPHA_FLATTENED)
+       content = CAIRO_CONTENT_COLOR_ALPHA;
+
+    return content;
+}
+
+const char *
+cairo_boilerplate_content_name (cairo_content_t content)
+{
+    /* For the purpose of the content name, we don't distinguish the
+     * flattened content value.
+     */
+    switch (cairo_boilerplate_content (content)) {
+    case CAIRO_CONTENT_COLOR:
+       return "rgb24";
+    case CAIRO_CONTENT_COLOR_ALPHA:
+       return "argb32";
+    case CAIRO_CONTENT_ALPHA:
+    default:
+       assert (0); /* not reached */
+       return "---";
+    }
+}
+
+static const char *
+_cairo_boilerplate_content_visible_name (cairo_content_t content)
+{
+    switch (cairo_boilerplate_content (content)) {
+    case CAIRO_CONTENT_COLOR:
+       return "rgb";
+    case CAIRO_CONTENT_COLOR_ALPHA:
+       return "rgba";
+    case CAIRO_CONTENT_ALPHA:
+       return "a";
+    default:
+       assert (0); /* not reached */
+       return "---";
+    }
+}
+
+cairo_format_t
+cairo_boilerplate_format_from_content (cairo_content_t content)
+{
+    cairo_format_t format;
+
+    switch (content) {
+       case CAIRO_CONTENT_COLOR: format = CAIRO_FORMAT_RGB24; break;
+       case CAIRO_CONTENT_COLOR_ALPHA: format = CAIRO_FORMAT_ARGB32; break;
+       case CAIRO_CONTENT_ALPHA: format = CAIRO_FORMAT_A8; break;
+       default:
+           assert (0); /* not reached */
+           format = CAIRO_FORMAT_INVALID;
+           break;
+    }
+
+    return format;
+}
+
+static cairo_surface_t *
+_cairo_boilerplate_image_create_surface (const char               *name,
+                                        cairo_content_t            content,
+                                        double                     width,
+                                        double                     height,
+                                        double                     max_width,
+                                        double                     max_height,
+                                        cairo_boilerplate_mode_t   mode,
+                                        int                        id,
+                                        void                     **closure)
+{
+    cairo_format_t format;
+
+    *closure = NULL;
+
+    if (content == CAIRO_CONTENT_COLOR_ALPHA) {
+       format = CAIRO_FORMAT_ARGB32;
+    } else if (content == CAIRO_CONTENT_COLOR) {
+       format = CAIRO_FORMAT_RGB24;
+    } else {
+       assert (0); /* not reached */
+       return NULL;
+    }
+
+    return cairo_image_surface_create (format, ceil (width), ceil (height));
+}
+
+static const cairo_user_data_key_t key;
+
+static cairo_surface_t *
+_cairo_boilerplate_image_create_similar (cairo_surface_t *other,
+                                        cairo_content_t content,
+                                        int width, int height)
+{
+    cairo_format_t format;
+    cairo_surface_t *surface;
+    int stride;
+    void *ptr;
+
+    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;
+    }
+
+    stride = cairo_format_stride_for_width(format, width);
+    ptr = malloc (stride* height);
+
+    surface = cairo_image_surface_create_for_data (ptr, format,
+                                                  width, height, stride);
+    cairo_surface_set_user_data (surface, &key, ptr, free);
+
+    return surface;
+}
+
+static cairo_surface_t *
+_cairo_boilerplate_image16_create_surface (const char               *name,
+                                          cairo_content_t            content,
+                                          double                     width,
+                                          double                     height,
+                                          double                     max_width,
+                                          double                     max_height,
+                                          cairo_boilerplate_mode_t   mode,
+                                          int                        id,
+                                          void                     **closure)
+{
+    *closure = NULL;
+
+    /* XXX force CAIRO_CONTENT_COLOR */
+    return cairo_image_surface_create (CAIRO_FORMAT_RGB16_565, ceil (width), ceil (height));
+}
+
+static cairo_surface_t *
+_cairo_boilerplate_image16_create_similar (cairo_surface_t *other,
+                                          cairo_content_t content,
+                                          int width, int height)
+{
+    cairo_format_t format;
+    cairo_surface_t *surface;
+    int stride;
+    void *ptr;
+
+    switch (content) {
+    case CAIRO_CONTENT_ALPHA: format = CAIRO_FORMAT_A8; break;
+    case CAIRO_CONTENT_COLOR: format = CAIRO_FORMAT_RGB16_565; break;
+    default:
+    case CAIRO_CONTENT_COLOR_ALPHA: format = CAIRO_FORMAT_ARGB32; break;
+    }
+
+    stride = cairo_format_stride_for_width(format, width);
+    ptr = malloc (stride* height);
+
+    surface = cairo_image_surface_create_for_data (ptr, format,
+                                                  width, height, stride);
+    cairo_surface_set_user_data (surface, &key, ptr, free);
+
+    return surface;
+}
+
+static char *
+_cairo_boilerplate_image_describe (void *closure)
+{
+    char *s;
+  
+    xasprintf (&s, "pixman %s", pixman_version_string ());
+
+    return s;
+}
+
+#if CAIRO_HAS_RECORDING_SURFACE
+static cairo_surface_t *
+_cairo_boilerplate_recording_create_surface (const char               *name,
+                                            cairo_content_t            content,
+                                            double                     width,
+                                            double                     height,
+                                            double                     max_width,
+                                            double                     max_height,
+                                            cairo_boilerplate_mode_t   mode,
+                                            int                        id,
+                                            void                     **closure)
+{
+    cairo_rectangle_t extents;
+
+    extents.x = 0;
+    extents.y = 0;
+    extents.width = width;
+    extents.height = height;
+    return *closure = cairo_surface_reference (cairo_recording_surface_create (content, &extents));
+}
+
+static void
+_cairo_boilerplate_recording_surface_cleanup (void *closure)
+{
+    cairo_surface_finish (closure);
+    cairo_surface_destroy (closure);
+}
+#endif
+
+const cairo_user_data_key_t cairo_boilerplate_output_basename_key;
+
+cairo_surface_t *
+_cairo_boilerplate_get_image_surface (cairo_surface_t *src,
+                                     int              page,
+                                     int              width,
+                                     int              height)
+{
+    cairo_surface_t *surface, *image;
+    cairo_t *cr;
+    cairo_status_t status;
+    cairo_format_t format;
+
+    if (cairo_surface_status (src))
+       return cairo_surface_reference (src);
+
+    if (page != 0)
+       return cairo_boilerplate_surface_create_in_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+
+    /* extract sub-surface */
+    switch (cairo_surface_get_content (src)) {
+    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;
+    }
+    surface = cairo_image_surface_create (format, width, height);
+    assert (cairo_surface_get_content (surface) == cairo_surface_get_content (src));
+    image = cairo_surface_reference (surface);
+
+    /* open a logging channel (only interesting for recording surfaces) */
+#if CAIRO_HAS_SCRIPT_SURFACE && CAIRO_HAS_RECORDING_SURFACE
+    if (cairo_surface_get_type (src) == CAIRO_SURFACE_TYPE_RECORDING) {
+       const char *test_name;
+
+       test_name = cairo_surface_get_user_data (src,
+                                                &cairo_boilerplate_output_basename_key);
+       if (test_name != NULL) {
+           cairo_device_t *ctx;
+           char *filename;
+
+           cairo_surface_destroy (surface);
+
+           xasprintf (&filename, "%s.out.trace", test_name);
+           ctx = cairo_script_create (filename);
+           surface = cairo_script_surface_create_for_target (ctx, image);
+           cairo_device_destroy (ctx);
+           free (filename);
+       }
+    }
+#endif
+
+    cr = cairo_create (surface);
+    cairo_surface_destroy (surface);
+    cairo_set_source_surface (cr, src, 0, 0);
+    cairo_paint (cr);
+
+    status = cairo_status (cr);
+    if (status) {
+       cairo_surface_destroy (image);
+       image = cairo_surface_reference (cairo_get_target (cr));
+    }
+    cairo_destroy (cr);
+
+    return image;
+}
+
+cairo_surface_t *
+cairo_boilerplate_get_image_surface_from_png (const char   *filename,
+                                             int           width,
+                                             int           height,
+                                             cairo_bool_t  flatten)
+{
+    cairo_surface_t *surface;
+
+    surface = cairo_image_surface_create_from_png (filename);
+    if (cairo_surface_status (surface))
+       return surface;
+
+    if (flatten) {
+       cairo_t *cr;
+       cairo_surface_t *flattened;
+
+       flattened = cairo_image_surface_create (cairo_image_surface_get_format (surface),
+                                               width,
+                                               height);
+       cr = cairo_create (flattened);
+       cairo_surface_destroy (flattened);
+
+       cairo_set_source_rgb (cr, 1, 1, 1);
+       cairo_paint (cr);
+
+       cairo_set_source_surface (cr, surface,
+                                 width - cairo_image_surface_get_width (surface),
+                                 height - cairo_image_surface_get_height (surface));
+       cairo_paint (cr);
+
+       cairo_surface_destroy (surface);
+       surface = cairo_surface_reference (cairo_get_target (cr));
+       cairo_destroy (cr);
+    } else if (cairo_image_surface_get_width (surface) != width ||
+              cairo_image_surface_get_height (surface) != height)
+    {
+       cairo_t *cr;
+       cairo_surface_t *sub;
+
+       sub = cairo_image_surface_create (cairo_image_surface_get_format (surface),
+                                         width,
+                                         height);
+       cr = cairo_create (sub);
+       cairo_surface_destroy (sub);
+
+       cairo_set_source_surface (cr, surface,
+                                 width - cairo_image_surface_get_width (surface),
+                                 height - cairo_image_surface_get_height (surface));
+       cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+       cairo_paint (cr);
+
+       cairo_surface_destroy (surface);
+       surface = cairo_surface_reference (cairo_get_target (cr));
+       cairo_destroy (cr);
+    }
+
+    return surface;
+}
+
+static const cairo_boilerplate_target_t builtin_targets[] = {
+    /* I'm uncompromising about leaving the image backend as 0
+     * for tolerance. There shouldn't ever be anything that is out of
+     * our control here. */
+    {
+       "image", "image", NULL, NULL,
+       CAIRO_SURFACE_TYPE_IMAGE, CAIRO_CONTENT_COLOR_ALPHA, 0,
+       NULL,
+       _cairo_boilerplate_image_create_surface,
+       _cairo_boilerplate_image_create_similar,
+       NULL, NULL,
+       _cairo_boilerplate_get_image_surface,
+       cairo_surface_write_to_png,
+       NULL, NULL,
+        _cairo_boilerplate_image_describe,
+       TRUE, FALSE, FALSE
+    },
+    {
+       "image", "image", NULL, NULL,
+       CAIRO_SURFACE_TYPE_IMAGE, CAIRO_CONTENT_COLOR, 0,
+       NULL,
+       _cairo_boilerplate_image_create_surface,
+       _cairo_boilerplate_image_create_similar,
+       NULL, NULL,
+       _cairo_boilerplate_get_image_surface,
+       cairo_surface_write_to_png,
+       NULL, NULL,
+        _cairo_boilerplate_image_describe,
+       FALSE, FALSE, FALSE
+    },
+    {
+       "image16", "image", NULL, NULL,
+       CAIRO_SURFACE_TYPE_IMAGE, CAIRO_CONTENT_COLOR, 0,
+       NULL,
+       _cairo_boilerplate_image16_create_surface,
+       _cairo_boilerplate_image16_create_similar,
+       NULL, NULL,
+       _cairo_boilerplate_get_image_surface,
+       cairo_surface_write_to_png,
+       NULL, NULL,
+        _cairo_boilerplate_image_describe,
+       TRUE, FALSE, FALSE
+    },
+#if CAIRO_HAS_RECORDING_SURFACE
+    {
+       "recording", "image", NULL, NULL,
+       CAIRO_SURFACE_TYPE_RECORDING, CAIRO_CONTENT_COLOR_ALPHA, 0,
+       "cairo_recording_surface_create",
+       _cairo_boilerplate_recording_create_surface,
+       cairo_surface_create_similar,
+       NULL, NULL,
+       _cairo_boilerplate_get_image_surface,
+       cairo_surface_write_to_png,
+       _cairo_boilerplate_recording_surface_cleanup,
+       NULL, NULL,
+       FALSE, FALSE, TRUE
+    },
+    {
+       "recording", "image", NULL, NULL,
+       CAIRO_SURFACE_TYPE_RECORDING, CAIRO_CONTENT_COLOR, 0,
+       "cairo_recording_surface_create",
+       _cairo_boilerplate_recording_create_surface,
+       cairo_surface_create_similar,
+       NULL, NULL,
+       _cairo_boilerplate_get_image_surface,
+       cairo_surface_write_to_png,
+       _cairo_boilerplate_recording_surface_cleanup,
+       NULL, NULL,
+       FALSE, FALSE, TRUE
+    },
+#endif
+};
+CAIRO_BOILERPLATE (builtin, builtin_targets)
+
+static struct cairo_boilerplate_target_list {
+    struct cairo_boilerplate_target_list *next;
+    const cairo_boilerplate_target_t *target;
+} *cairo_boilerplate_targets;
+
+static cairo_bool_t
+probe_target (const cairo_boilerplate_target_t *target)
+{
+    if (target->probe == NULL)
+       return TRUE;
+
+#if HAVE_DLSYM
+    return dlsym (NULL, target->probe) != NULL;
+#else
+    return TRUE;
+#endif
+}
+
+void
+_cairo_boilerplate_register_backend (const cairo_boilerplate_target_t *targets,
+                                    unsigned int                      count)
+{
+    targets += count;
+    while (count--) {
+       struct cairo_boilerplate_target_list *list;
+
+       --targets;
+       if (! probe_target (targets))
+           continue;
+
+       list = xmalloc (sizeof (*list));
+       list->next = cairo_boilerplate_targets;
+       list->target = targets;
+       cairo_boilerplate_targets = list;
+    }
+}
+
+static cairo_bool_t
+_cairo_boilerplate_target_matches_name (const cairo_boilerplate_target_t *target,
+                                       const char                       *tname,
+                                       const char                       *end)
+{
+    char const *content_name;
+    const char *content_start = strpbrk (tname, ".");
+    const char *content_end = end;
+    size_t name_len;
+    size_t content_len;
+
+    if (content_start != NULL)
+       end = content_start++;
+
+    name_len = end - tname;
+
+    /* Check name. */
+    if (! (name_len == 1 && 0 == strncmp (tname, "?", 1))) { /* wildcard? */
+       if (0 != strncmp (target->name, tname, name_len)) /* exact match? */
+           return FALSE;
+       if (isalnum (target->name[name_len]))
+           return FALSE;
+    }
+
+    /* Check optional content. */
+    if (content_start == NULL) /* none given? */
+       return TRUE;
+
+    /* Exact content match? */
+    content_name = _cairo_boilerplate_content_visible_name (target->content);
+    content_len = content_end - content_start;
+    if (strlen(content_name) != content_len)
+       return FALSE;
+    if (0 == strncmp (content_name, content_start, content_len))
+       return TRUE;
+
+    return FALSE;
+}
+
+const cairo_boilerplate_target_t **
+cairo_boilerplate_get_targets (int         *pnum_targets,
+                              cairo_bool_t *plimited_targets)
+{
+    size_t i, num_targets;
+    cairo_bool_t limited_targets = FALSE;
+    const char *tname;
+    const cairo_boilerplate_target_t **targets_to_test;
+    struct cairo_boilerplate_target_list *list;
+
+    if (cairo_boilerplate_targets == NULL)
+       _cairo_boilerplate_register_all ();
+
+    if ((tname = getenv ("CAIRO_TEST_TARGET")) != NULL && *tname) {
+       /* check the list of targets specified by the user */
+       limited_targets = TRUE;
+
+       num_targets = 0;
+       targets_to_test = NULL;
+
+       while (*tname) {
+           int found = 0;
+           const char *end = strpbrk (tname, " \t\r\n;:,");
+           if (!end)
+               end = tname + strlen (tname);
+
+           if (end == tname) {
+               tname = end + 1;
+               continue;
+           }
+
+           for (list = cairo_boilerplate_targets;
+                list != NULL;
+                list = list->next)
+           {
+               const cairo_boilerplate_target_t *target = list->target;
+               if (_cairo_boilerplate_target_matches_name (target, tname, end)) {
+                   /* realloc isn't exactly the best thing here, but meh. */
+                   targets_to_test = xrealloc (targets_to_test, sizeof(cairo_boilerplate_target_t *) * (num_targets+1));
+                   targets_to_test[num_targets++] = target;
+                   found = 1;
+               }
+           }
+
+           if (!found) {
+               const char *last_name = NULL;
+
+               fprintf (stderr, "Cannot find target '%.*s'.\n",
+                        (int)(end - tname), tname);
+               fprintf (stderr, "Known targets:");
+               for (list = cairo_boilerplate_targets;
+                    list != NULL;
+                    list = list->next)
+               {
+                   const cairo_boilerplate_target_t *target = list->target;
+                   if (last_name != NULL) {
+                       if (strcmp (target->name, last_name) == 0) {
+                           /* filter out repeats that differ in content */
+                           continue;
+                       }
+                       fprintf (stderr, ",");
+                   }
+                   fprintf (stderr, " %s", target->name);
+                   last_name = target->name;
+               }
+               fprintf (stderr, "\n");
+               exit(-1);
+           }
+
+           if (*end)
+             end++;
+           tname = end;
+       }
+    } else {
+       /* check all compiled in targets */
+       num_targets = 0;
+       for (list = cairo_boilerplate_targets; list != NULL; list = list->next)
+           num_targets++;
+
+       targets_to_test = xmalloc (sizeof(cairo_boilerplate_target_t*) * num_targets);
+       num_targets = 0;
+       for (list = cairo_boilerplate_targets;
+            list != NULL;
+            list = list->next)
+       {
+           const cairo_boilerplate_target_t *target = list->target;
+           targets_to_test[num_targets++] = target;
+       }
+    }
+
+    /* exclude targets as specified by the user */
+    if ((tname = getenv ("CAIRO_TEST_TARGET_EXCLUDE")) != NULL && *tname) {
+       limited_targets = TRUE;
+
+       while (*tname) {
+           int j;
+           const char *end = strpbrk (tname, " \t\r\n;:,");
+           if (!end)
+               end = tname + strlen (tname);
+
+           if (end == tname) {
+               tname = end + 1;
+               continue;
+           }
+
+           for (i = j = 0; i < num_targets; i++) {
+               const cairo_boilerplate_target_t *target = targets_to_test[i];
+               if (! _cairo_boilerplate_target_matches_name (target,
+                                                             tname, end))
+               {
+                   targets_to_test[j++] = targets_to_test[i];
+               }
+           }
+           num_targets = j;
+
+           if (*end)
+             end++;
+           tname = end;
+       }
+    }
+
+    if (pnum_targets)
+       *pnum_targets = num_targets;
+
+    if (plimited_targets)
+       *plimited_targets = limited_targets;
+
+    return targets_to_test;
+}
+
+const cairo_boilerplate_target_t *
+cairo_boilerplate_get_image_target (cairo_content_t content)
+{
+    struct cairo_boilerplate_target_list *list;
+
+    if (cairo_boilerplate_targets == NULL)
+       _cairo_boilerplate_register_all ();
+
+    switch (content) {
+    default:
+    case CAIRO_CONTENT_ALPHA: return NULL;
+    case CAIRO_CONTENT_COLOR: return &builtin_targets[1];
+    case CAIRO_CONTENT_COLOR_ALPHA: return &builtin_targets[0];
+    }
+}
+
+const cairo_boilerplate_target_t *
+cairo_boilerplate_get_target_by_name (const char      *name,
+                                     cairo_content_t  content)
+{
+    struct cairo_boilerplate_target_list *list;
+
+    if (cairo_boilerplate_targets == NULL)
+       _cairo_boilerplate_register_all ();
+
+    /* first return an exact match */
+    for (list = cairo_boilerplate_targets; list != NULL; list = list->next) {
+       const cairo_boilerplate_target_t *target = list->target;
+       if (strcmp (target->name, name) == 0 &&
+           target->content == content)
+       {
+           return target;
+       }
+    }
+
+    /* otherwise just return a match that may differ in content */
+    for (list = cairo_boilerplate_targets; list != NULL; list = list->next) {
+       const cairo_boilerplate_target_t *target = list->target;
+       if (strcmp (target->name, name) == 0)
+           return target;
+    }
+
+    return NULL;
+}
+
+void
+cairo_boilerplate_free_targets (const cairo_boilerplate_target_t **targets)
+{
+    free (targets);
+}
+
+cairo_surface_t *
+cairo_boilerplate_surface_create_in_error (cairo_status_t status)
+{
+    cairo_surface_t *surface = NULL;
+    int loop = 5;
+
+    do {
+       cairo_surface_t *intermediate;
+       cairo_t *cr;
+       cairo_path_t path;
+
+       intermediate = cairo_image_surface_create (CAIRO_FORMAT_A8, 0, 0);
+       cr = cairo_create (intermediate);
+       cairo_surface_destroy (intermediate);
+
+       path.status = status;
+       cairo_append_path (cr, &path);
+
+       cairo_surface_destroy (surface);
+       surface = cairo_surface_reference (cairo_get_target (cr));
+       cairo_destroy (cr);
+    } while (cairo_surface_status (surface) != status && --loop);
+
+    return surface;
+}
+
+void
+cairo_boilerplate_scaled_font_set_max_glyphs_cached (cairo_scaled_font_t *scaled_font,
+                                                    int                  max_glyphs)
+{
+    /* XXX CAIRO_DEBUG */
+}
+
+#if HAS_DAEMON
+static int
+any2ppm_daemon_exists (void)
+{
+    struct stat st;
+    int fd;
+    char buf[80];
+    int pid;
+    int ret;
+
+    if (stat (SOCKET_PATH, &st) < 0)
+       return 0;
+
+    fd = open (SOCKET_PATH ".pid", O_RDONLY);
+    if (fd < 0)
+       return 0;
+
+    pid = 0;
+    ret = read (fd, buf, sizeof (buf) - 1);
+    if (ret > 0) {
+       buf[ret] = '\0';
+       pid = atoi (buf);
+    }
+    close (fd);
+
+    return pid > 0 && kill (pid, 0) != -1;
+}
+#endif
+
+FILE *
+cairo_boilerplate_open_any2ppm (const char   *filename,
+                               int           page,
+                               unsigned int  flags)
+{
+    char command[4096];
+#if HAS_DAEMON
+    int sk;
+    struct sockaddr_un addr;
+    int len;
+
+    if (flags & CAIRO_BOILERPLATE_OPEN_NO_DAEMON)
+       goto POPEN;
+
+    if (! any2ppm_daemon_exists ()) {
+       if (system ("./any2ppm") != 0)
+           goto POPEN;
+    }
+
+    sk = socket (PF_UNIX, SOCK_STREAM, 0);
+    if (sk == -1)
+       goto POPEN;
+
+    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) {
+       close (sk);
+       goto POPEN;
+    }
+
+    len = sprintf (command, "%s %d\n", filename, page);
+    if (write (sk, command, len) != len) {
+       close (sk);
+       goto POPEN;
+    }
+
+    return fdopen (sk, "r");
+
+POPEN:
+#endif
+    sprintf (command, "./any2ppm %s %d", filename, page);
+    return popen (command, "r");
+}
+
+static cairo_bool_t
+freadn (unsigned char *buf,
+       int            len,
+       FILE          *file)
+{
+    int ret;
+
+    while (len) {
+       ret = fread (buf, 1, len, file);
+       if (ret != len) {
+           if (ferror (file) || feof (file))
+               return FALSE;
+       }
+       len -= ret;
+       buf += len;
+    }
+
+    return TRUE;
+}
+
+cairo_surface_t *
+cairo_boilerplate_image_surface_create_from_ppm_stream (FILE *file)
+{
+    char format;
+    int width, height, stride;
+    int x, y;
+    unsigned char *data;
+    cairo_surface_t *image = NULL;
+
+    if (fscanf (file, "P%c %d %d 255\n", &format, &width, &height) != 3)
+       goto FAIL;
+
+    switch (format) {
+    case '7': /* XXX */
+       image = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
+       break;
+    case '6':
+       image = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, height);
+       break;
+    case '5':
+       image = cairo_image_surface_create (CAIRO_FORMAT_A8, width, height);
+       break;
+    default:
+       goto FAIL;
+    }
+    if (cairo_surface_status (image))
+       return image;
+
+    data = cairo_image_surface_get_data (image);
+    stride = cairo_image_surface_get_stride (image);
+    for (y = 0; y < height; y++) {
+       unsigned char *buf = data + y*stride;
+       switch (format) {
+       case '7':
+           if (! freadn (buf, 4 * width, file))
+               goto FAIL;
+           break;
+       case '6':
+           if (! freadn (buf, 3*width, file))
+               goto FAIL;
+           buf += 3*width;
+           for (x = width; x--; ) {
+               buf -= 3;
+               ((uint32_t *) (data + y*stride))[x] =
+                   (buf[0] << 16) | (buf[1] << 8) | (buf[2] << 0);
+           }
+           break;
+       case '5':
+           if (! freadn (buf, width, file))
+               goto FAIL;
+           break;
+       }
+    }
+    cairo_surface_mark_dirty (image);
+
+    return image;
+
+FAIL:
+    cairo_surface_destroy (image);
+    return cairo_boilerplate_surface_create_in_error (CAIRO_STATUS_READ_ERROR);
+}
+
+cairo_surface_t *
+cairo_boilerplate_convert_to_image (const char *filename,
+                                   int         page)
+{
+    FILE *file;
+    unsigned int flags = 0;
+    cairo_surface_t *image;
+    int ret;
+
+  RETRY:
+    file = cairo_boilerplate_open_any2ppm (filename, page, flags);
+    if (file == NULL) {
+       switch (errno) {
+       case ENOMEM:
+           return cairo_boilerplate_surface_create_in_error (CAIRO_STATUS_NO_MEMORY);
+       default:
+           return cairo_boilerplate_surface_create_in_error (CAIRO_STATUS_READ_ERROR);
+       }
+    }
+
+    image = cairo_boilerplate_image_surface_create_from_ppm_stream (file);
+    ret = pclose (file);
+    /* check for fatal errors from the interpreter */
+    if (ret) { /* any2pmm should never die... */
+       cairo_surface_destroy (image);
+       return cairo_boilerplate_surface_create_in_error (CAIRO_STATUS_INVALID_STATUS);
+    }
+
+    if (ret == 0 && cairo_surface_status (image) == CAIRO_STATUS_READ_ERROR) {
+       if (flags == 0) {
+           /* Try again in a standalone process. */
+           cairo_surface_destroy (image);
+           flags = CAIRO_BOILERPLATE_OPEN_NO_DAEMON;
+           goto RETRY;
+       }
+    }
+
+    return image;
+}
+
+int
+cairo_boilerplate_version (void)
+{
+    return CAIRO_VERSION;
+}
+
+const char*
+cairo_boilerplate_version_string (void)
+{
+    return CAIRO_VERSION_STRING;
+}
+
+void
+cairo_boilerplate_fini (void)
+{
+    while (cairo_boilerplate_targets != NULL) {
+       struct cairo_boilerplate_target_list *next;
+
+       next = cairo_boilerplate_targets->next;
+
+       free (cairo_boilerplate_targets);
+       cairo_boilerplate_targets = next;
+    }
+}
diff --git a/boilerplate/cairo-boilerplate.h b/boilerplate/cairo-boilerplate.h
new file mode 100644 (file)
index 0000000..0615221
--- /dev/null
@@ -0,0 +1,242 @@
+/*
+ * Copyright © 2004,2006 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ */
+
+#ifndef _CAIRO_BOILERPLATE_H_
+#define _CAIRO_BOILERPLATE_H_
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <cairo.h>
+#include <string.h>
+
+#include "cairo-compiler-private.h"
+
+#if   HAVE_STDINT_H
+# include <stdint.h>
+#elif HAVE_INTTYPES_H
+# include <inttypes.h>
+#elif HAVE_SYS_INT_TYPES_H
+# include <sys/int_types.h>
+#elif defined(_MSC_VER)
+# include <stdint.h>
+  typedef __int8 int8_t;
+  typedef unsigned __int8 uint8_t;
+  typedef __int16 int16_t;
+  typedef unsigned __int16 uint16_t;
+  typedef __int32 int32_t;
+  typedef unsigned __int32 uint32_t;
+  typedef __int64 int64_t;
+  typedef unsigned __int64 uint64_t;
+#else
+#error Cannot find definitions for fixed-width integral types (uint8_t, uint32_t, etc.)
+#endif
+
+#ifndef HAVE_UINT64_T
+# define HAVE_UINT64_T 1
+#endif
+#ifndef INT16_MIN
+# define INT16_MIN     (-32767-1)
+#endif
+#ifndef INT16_MAX
+# define INT16_MAX     (32767)
+#endif
+#ifndef UINT16_MAX
+# define UINT16_MAX    (65535)
+#endif
+
+#ifndef CAIRO_BOILERPLATE_DEBUG
+#define CAIRO_BOILERPLATE_DEBUG(x)
+#endif
+
+#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ > 4)
+#define CAIRO_BOILERPLATE_PRINTF_FORMAT(fmt_index, va_index) \
+       __attribute__((__format__(__printf__, fmt_index, va_index)))
+#else
+#define CAIRO_BOILERPLATE_PRINTF_FORMAT(fmt_index, va_index)
+#endif
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#ifndef M_PI
+#define M_PI 3.14159265358979323846
+#endif
+
+CAIRO_BEGIN_DECLS
+
+/* A fake format we use for the flattened ARGB output of the PS and
+ * PDF surfaces. */
+#define CAIRO_TEST_CONTENT_COLOR_ALPHA_FLATTENED ((unsigned int) -1)
+
+extern const cairo_user_data_key_t cairo_boilerplate_output_basename_key;
+
+cairo_content_t
+cairo_boilerplate_content (cairo_content_t content);
+
+const char *
+cairo_boilerplate_content_name (cairo_content_t content);
+
+cairo_format_t
+cairo_boilerplate_format_from_content (cairo_content_t content);
+
+typedef enum {
+    CAIRO_BOILERPLATE_MODE_TEST,
+    CAIRO_BOILERPLATE_MODE_PERF
+} cairo_boilerplate_mode_t;
+
+typedef cairo_surface_t *
+(*cairo_boilerplate_create_surface_t) (const char               *name,
+                                      cairo_content_t            content,
+                                      double                     width,
+                                      double                     height,
+                                      double                     max_width,
+                                      double                     max_height,
+                                      cairo_boilerplate_mode_t   mode,
+                                      int                        id,
+                                      void                     **closure);
+
+typedef cairo_surface_t *
+(*cairo_boilerplate_create_similar_t) (cairo_surface_t          *other,
+                                      cairo_content_t            content,
+                                      int                        width,
+                                      int                        height);
+
+typedef void
+(*cairo_boilerplate_force_fallbacks_t) (cairo_surface_t *surface,
+                                      double            x_pixels_per_inch,
+                                      double            y_pixels_per_inch);
+
+typedef cairo_status_t
+(*cairo_boilerplate_finish_surface_t) (cairo_surface_t *surface);
+
+typedef cairo_surface_t *
+(*cairo_boilerplate_get_image_surface_t) (cairo_surface_t *surface,
+                                         int page,
+                                         int width,
+                                         int height);
+
+typedef cairo_status_t
+(*cairo_boilerplate_write_to_png_t) (cairo_surface_t *surface,
+                                    const char *filename);
+
+typedef void
+(*cairo_boilerplate_cleanup_t) (void *closure);
+
+typedef void
+(*cairo_boilerplate_wait_t) (void *closure);
+
+typedef char *
+(*cairo_boilerplate_describe_t) (void *closure);
+
+typedef struct _cairo_boilerplate_target {
+    const char                                 *name;
+    const char                                 *basename;
+    const char                                 *file_extension;
+    const char                                 *reference_target;
+    cairo_surface_type_t                        expected_type;
+    cairo_content_t                             content;
+    unsigned int                                error_tolerance;
+    const char                                 *probe; /* runtime dl check */
+    cairo_boilerplate_create_surface_t          create_surface;
+    cairo_boilerplate_create_similar_t          create_similar;
+    cairo_boilerplate_force_fallbacks_t                 force_fallbacks;
+    cairo_boilerplate_finish_surface_t          finish_surface;
+    cairo_boilerplate_get_image_surface_t       get_image_surface;
+    cairo_boilerplate_write_to_png_t            write_to_png;
+    cairo_boilerplate_cleanup_t                         cleanup;
+    cairo_boilerplate_wait_t                    synchronize;
+    cairo_boilerplate_describe_t                 describe;
+    cairo_bool_t                                is_measurable;
+    cairo_bool_t                                is_vector;
+    cairo_bool_t                                is_recording;
+} cairo_boilerplate_target_t;
+
+const cairo_boilerplate_target_t *
+cairo_boilerplate_get_image_target (cairo_content_t content);
+
+const cairo_boilerplate_target_t *
+cairo_boilerplate_get_target_by_name (const char      *name,
+                                     cairo_content_t  content);
+
+const cairo_boilerplate_target_t **
+cairo_boilerplate_get_targets (int         *num_targets,
+                              cairo_bool_t *limited_targets);
+
+void
+cairo_boilerplate_free_targets (const cairo_boilerplate_target_t **targets);
+
+cairo_surface_t *
+_cairo_boilerplate_get_image_surface (cairo_surface_t *src,
+                                     int              page,
+                                     int              width,
+                                     int              height);
+cairo_surface_t *
+cairo_boilerplate_get_image_surface_from_png (const char   *filename,
+                                             int           width,
+                                             int           height,
+                                             cairo_bool_t  flatten);
+
+cairo_surface_t *
+cairo_boilerplate_surface_create_in_error (cairo_status_t status);
+
+enum {
+    CAIRO_BOILERPLATE_OPEN_NO_DAEMON = 0x1,
+};
+
+FILE *
+cairo_boilerplate_open_any2ppm (const char   *filename,
+                               int           page,
+                               unsigned int  flags);
+cairo_surface_t *
+cairo_boilerplate_image_surface_create_from_ppm_stream (FILE *file);
+
+cairo_surface_t *
+cairo_boilerplate_convert_to_image (const char *filename,
+                                   int         page);
+
+int
+cairo_boilerplate_version (void);
+
+const char*
+cairo_boilerplate_version_string (void);
+
+void
+cairo_boilerplate_fini (void);
+
+#include "cairo-boilerplate-system.h"
+
+CAIRO_END_DECLS
+
+#endif
diff --git a/boilerplate/check-link.c b/boilerplate/check-link.c
new file mode 100644 (file)
index 0000000..f164448
--- /dev/null
@@ -0,0 +1,24 @@
+#define CAIRO_VERSION_H 1
+
+#include <cairo-boilerplate.h>
+
+/* get the "real" version info instead of dummy cairo-version.h */
+#undef CAIRO_VERSION_H
+#include "../cairo-version.h"
+
+#include <stdio.h>
+
+int
+main (void)
+{
+  printf ("Check linking to the just built cairo boilerplate library\n");
+  if (cairo_boilerplate_version () == CAIRO_VERSION) {
+    return 0;
+  } else {
+    fprintf (stderr,
+            "Error: linked to cairo boilerplate version %s instead of %s\n",
+            cairo_boilerplate_version_string (),
+            CAIRO_VERSION_STRING);
+    return 1;
+  }
+}
diff --git a/boilerplate/make-cairo-boilerplate-constructors.sh b/boilerplate/make-cairo-boilerplate-constructors.sh
new file mode 100644 (file)
index 0000000..09716ca
--- /dev/null
@@ -0,0 +1,29 @@
+#! /bin/sh
+
+if test $# -eq 0; then
+    echo "$0: no input files." >&2
+    exit 0
+fi
+
+cat <<HERE
+/* WARNING: Autogenerated file - see $0! */
+
+#include "cairo-boilerplate-private.h"
+
+void _cairo_boilerplate_register_all (void);
+
+HERE
+
+cat "$@" |  sed '/^CAIRO_BOILERPLATE/!d; s/CAIRO_BOILERPLATE.*(\(.*\),.*/extern void _register_\1 (void);/'
+
+cat <<HERE
+
+void
+_cairo_boilerplate_register_all (void)
+{
+HERE
+
+cat "$@" |  sed '/^CAIRO_BOILERPLATE/!d; s/CAIRO_BOILERPLATE.*(\(.*\),.*/    _register_\1 ();/'
+
+echo "}"
+
diff --git a/build/.gitignore b/build/.gitignore
new file mode 100644 (file)
index 0000000..53f31d7
--- /dev/null
@@ -0,0 +1,12 @@
+compile
+config.guess
+config.sub
+depcomp
+install-sh
+ltmain.sh
+missing
+mkinstalldirs
+#Makefile.win32.features
+#Makefile.win32.features-h
+libtool.m4
+lt*.m4
diff --git a/build/Makefile.am.analysis b/build/Makefile.am.analysis
new file mode 100644 (file)
index 0000000..a44077a
--- /dev/null
@@ -0,0 +1,37 @@
+if CAIRO_HAS_LCOV
+# use recursive makes in order to ignore errors during check/perf
+lcov:
+       -$(MAKE) $(AM_MAKEFLAGS) check
+       $(MAKE) $(AM_MAKEFLAGS) genlcov
+lcov-perf:
+       -$(MAKE) $(AM_MAKEFLAGS) perf
+       $(MAKE) $(AM_MAKEFLAGS) genlcov
+
+# we have to massage the lcov.info file slightly to hide the effect of libtool
+# placing the objects files in the .libs/ directory separate from the *.c
+genlcov:
+       $(LTP) --directory $(top_builddir) --path $(top_builddir) --capture --output-file cairo-lcov.info --test-name CAIRO_TEST --no-checksum
+       $(SED) -e 's#.libs/##' \
+              -e 's#boilerplate/src#src#' \
+              -e 's#$(shell pwd)#$(shell cd $(top_srcdir) && pwd)#' \
+              < cairo-lcov.info > cairo-lcov.info.tmp
+       LANG=C $(LTP_GENHTML) --prefix $(top_builddir) --output-directory cairo-lcov --title "Cairo Code Coverage" --show-details cairo-lcov.info.tmp
+       $(RM) cairo-lcov.info.tmp
+
+html-local: lcov
+else
+lcov lcov-perf genlcov:
+       @echo You need to configure Cairo with support for gcov enabled.
+       @echo e.g, ./configure --enable-gcov
+endif
+
+lcov-clean:
+if CAIRO_HAS_LCOV
+       -$(LTP) --directory $(top_builddir) -z
+endif
+       -$(RM) -r cairo-lcov.info cairo-lcov
+       -$(FIND) -name '*.gcda' -print | $(XARGS) $(RM)
+
+distclean-local: lcov-clean
+
+.PHONY: lcov lcov-perf genlcov lcov-clean
diff --git a/build/Makefile.am.changelog b/build/Makefile.am.changelog
new file mode 100644 (file)
index 0000000..07e6036
--- /dev/null
@@ -0,0 +1,82 @@
+# Creating ChangeLog files from git log:
+
+# We always create a ChangeLog that contains the most recent changes, and
+# multiple others for changes between major releases (other than the last such
+# segment that we put in 'ChangeLog'.  The old ones are named
+# ChangeLog.pre-X.Y where X.Y is the version number of the major release.
+
+CURR_CHANGELOG_VERSION=$(CAIRO_VERSION_MAJOR).$$(echo "($(CAIRO_VERSION_MINOR)+1)/2*2" | bc)
+# examines $version
+PREV_CHANGELOG_VERSION=$$(if test "x$$(echo "($$version-0.1)*2/2"|bc)" = "x$$(echo "$$version*2/2"|bc)"; \
+                         then echo "$$version-$$(echo "$$version" | sed 's/[0-9]/0/g;s/[0-9]$$/2/')"; \
+                         else echo "$$version-1.0"; \
+                         fi | bc | sed 's/[.]0*/./;s/^0[.]\?$$/initial/;s/[.]$$/.0/')
+
+CHANGELOGS = ChangeLog \
+       `version=$(CURR_CHANGELOG_VERSION); \
+       version=$(PREV_CHANGELOG_VERSION); \
+       while test "x$$version" != xinitial; do \
+               echo ChangeLog.pre-$$version; \
+               version=$(PREV_CHANGELOG_VERSION); \
+       done`
+
+MAINTAINERCLEANFILES += $(srcdir)/ChangeLog $(srcdir)/ChangeLog.pre-*
+DISTCLEANFILES += $(srcdir)/ChangeLog.cache-*
+
+changelogs:
+       @$(MAKE) $(AM_MAKEFLAGS) $(CHANGELOGS)
+
+dist-hook: changelogs
+       changelogs="$(CHANGELOGS)"; \
+       for changelog in $$changelogs; do \
+               cp $(srcdir)/$$changelog $(distdir)/ 2>/dev/null || \
+               cp $$changelog $(distdir)/; \
+       done
+
+$(srcdir)/ChangeLog:
+       @if test -d "$(srcdir)/.git"; then \
+               version=$(CURR_CHANGELOG_VERSION); \
+               prev=$(PREV_CHANGELOG_VERSION).0; \
+               nearest_tag=`git describe | sed 's/-.*//'`; \
+               before=$(srcdir)/ChangeLog.cache-$$prev..$$nearest_tag; \
+               after=$(srcdir)/ChangeLog.cache-$$nearest_tag..; \
+               $(MAKE) $(AM_MAKEFLAGS) $$before $$after && \
+               echo Creating $@ && \
+               { echo '# Generated by configure.  Do not edit.'; echo; \
+                 cat $$after; echo; cat $$before; } > $@; \
+       else \
+               test -f $@ || \
+               (echo A git checkout is required to generate $@ >&2 && \
+                echo A git checkout is required to generate this file >> $@); \
+       fi
+
+DISTCLEANFILES += ChangeLog.cache-*
+
+ChangeLog.cache-*..: .git
+
+ChangeLog%: $(srcdir)/ChangeLog%
+
+$(srcdir)/ChangeLog.cache-% $(srcdir)/ChangeLog.pre-%:
+       @echo Creating $@
+       @if test -d "$(srcdir)/.git"; then \
+         (cd "$(srcdir)" && \
+         version=$$(echo "$@" | sed 's/.*ChangeLog\([.].*-\)\?//'); \
+         if echo "$@" | grep -q '^ChangeLog[.]cache'; then \
+               spec=$$version; \
+         else \
+               to=$$version; \
+               test "x$$version" = x && version=$(CURR_CHANGELOG_VERSION); \
+               from=$(PREV_CHANGELOG_VERSION); \
+               test "x$$to" = x || to=$$to.0; \
+               test "x$$from" = xinitial || from=$$from.0; \
+               spec=$$from..$$to; \
+         fi; \
+         $(top_srcdir)/build/missing --run git log --stat "$$spec") > $@.tmp \
+         && mv -f $@.tmp $@ \
+         || ($(RM) $@.tmp; \
+             echo Failed to generate $@, your $@ may be outdated >&2); \
+       else \
+         echo A git checkout is required to generate $@ >&2; \
+       fi
+
+.PHONY: changelogs ChangeLog $(srcdir)/ChangeLog
diff --git a/build/Makefile.am.common b/build/Makefile.am.common
new file mode 100644 (file)
index 0000000..b955af5
--- /dev/null
@@ -0,0 +1,14 @@
+BUILT_SOURCES =
+CLEANFILES =
+DISTCLEANFILES =
+EXTRA_DIST =
+EXTRA_LTLIBRARIES =
+EXTRA_PROGRAMS =
+MAINTAINERCLEANFILES =
+TESTS =
+check_PROGRAMS =
+
+CLEANFILES += *.i *.s *.gch
+CLEANFILES += $(EXTRA_LTLIBRARIES) $(EXTRA_PROGRAMS) $(check_PROGRAMS)
+DISTCLEANFILES += $(BUILT_SOURCES)
+MAINTAINERCLEANFILES += Makefile.in
diff --git a/build/Makefile.am.gtk-doc b/build/Makefile.am.gtk-doc
new file mode 100644 (file)
index 0000000..c3d642b
--- /dev/null
@@ -0,0 +1,190 @@
+# BEFORE MODIFYING THIS FILE:
+#
+# This file is a descendant of an old copy of gtk-doc.make, modified for cairo minimally:
+#
+#   - Moved to build/
+#   - Made it append to EXTRA_DIST and CLEANFILES
+#   - Instead of all-local, make "doc" build docs, and err if gtk-doc not enabled
+#   - Some other changed introduced in 7f114b781f5c530d57530e5f76402e41cdabac6b
+#
+# Before changing it, check to see if a newer gtk-doc.make has fixed the issue you are facing.
+# From time to time, it would be nice to udpate this to the latest copy of gtk-doc.make, but
+# please do review all the differences and port our modifications forward.
+#
+
+# -*- mode: makefile -*-
+
+####################################
+# Everything below here is generic #
+####################################
+
+if GTK_DOC_USE_LIBTOOL
+GTKDOC_CC = $(LIBTOOL) --tag=CC --mode=compile $(CC) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+GTKDOC_LD = $(LIBTOOL) --tag=CC --mode=link $(CC) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS)
+else
+GTKDOC_CC = $(CC) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+GTKDOC_LD = $(CC) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS)
+endif
+
+# We set GPATH here; this gives us semantics for GNU make
+# which are more like other make's VPATH, when it comes to
+# whether a source that is a target of one rule is then
+# searched for in VPATH/GPATH.
+#
+GPATH = $(srcdir)
+
+TARGET_DIR=$(HTML_DIR)/$(DOC_MODULE)
+
+EXTRA_DIST +=                          \
+       $(content_files)                \
+       $(HTML_IMAGES)                  \
+       $(DOC_MAIN_SGML_FILE)           \
+       $(DOC_MODULE)-sections.txt      \
+       $(DOC_MODULE)-overrides.txt
+
+DOC_STAMPS=scan-build.stamp tmpl-build.stamp sgml-build.stamp html-build.stamp \
+          $(srcdir)/tmpl.stamp $(srcdir)/sgml.stamp $(srcdir)/html.stamp
+
+SCANOBJ_FILES =                 \
+       $(DOC_MODULE).args       \
+       $(DOC_MODULE).hierarchy  \
+       $(DOC_MODULE).interfaces \
+       $(DOC_MODULE).prerequisites \
+       $(DOC_MODULE).signals
+
+REPORT_FILES = \
+       $(DOC_MODULE)-undocumented.txt \
+       $(DOC_MODULE)-undeclared.txt \
+       $(DOC_MODULE)-unused.txt
+
+CLEANFILES += $(SCANOBJ_FILES) $(REPORT_FILES) $(DOC_STAMPS)
+
+if ENABLE_GTK_DOC
+doc: html-build.stamp
+else
+doc:
+       @echo "*** gtk-doc must be installed (and --enable-gtk-doc) in order to make doc"
+       @false
+endif
+
+docs: html-build.stamp
+
+#### scan ####
+
+scan-build.stamp: $(HFILE_GLOB) $(CFILE_GLOB) $(EXTRA_HFILES)
+       @echo 'gtk-doc: Scanning header files'
+       @-chmod -R u+w $(srcdir)
+       gtkdoc-scan --module=$(DOC_MODULE) --source-dir=$(DOC_SOURCE_DIR) --ignore-headers="$(IGNORE_HFILES)" $(SCAN_OPTIONS) $(EXTRA_HFILES) --output-dir=$(srcdir)
+       if grep -l '^..*$$' $(srcdir)/$(DOC_MODULE).types > /dev/null 2>&1 ; then \
+           CC="$(GTKDOC_CC)" LD="$(GTKDOC_LD)" CFLAGS="$(GTKDOC_CFLAGS)" LDFLAGS="$(GTKDOC_LIBS)" gtkdoc-scangobj $(SCANGOBJ_OPTIONS) --module=$(DOC_MODULE) --output-dir=$(srcdir) ; \
+       else \
+           cd $(srcdir) ; \
+           for i in $(SCANOBJ_FILES) ; do \
+               test -f $$i || touch $$i ; \
+           done \
+       fi
+       touch scan-build.stamp
+
+$(DOC_MODULE)-decl.txt $(SCANOBJ_FILES) $(DOC_MODULE)-sections.txt $(DOC_MODULE)-overrides.txt: scan-build.stamp
+       @true
+
+#### templates ####
+
+tmpl-build.stamp: $(DOC_MODULE)-decl.txt $(SCANOBJ_FILES) $(DOC_MODULE)-sections.txt $(DOC_MODULE)-overrides.txt
+       @echo 'gtk-doc: Rebuilding template files'
+       @-chmod -R u+w $(srcdir)
+       cd $(srcdir) && gtkdoc-mktmpl --module=$(DOC_MODULE) $(MKTMPL_OPTIONS)
+       touch tmpl-build.stamp
+
+tmpl.stamp: tmpl-build.stamp
+       @true
+
+tmpl/*.sgml:
+       @true
+
+
+#### xml ####
+
+# gtkdoc-mkdb is broken and requires a --root-dir=$(srcdir) option
+# The _srcdir diversion is fragile but works for make check; make distcheck
+sgml-build.stamp: tmpl.stamp $(HFILE_GLOB) $(CFILE_GLOB) $(DOC_MODULE)-sections.txt $(srcdir)/tmpl/*.sgml $(expand_content_files)
+       @echo 'gtk-doc: Building XML'
+       @-chmod -R u+w $(srcdir)
+       _srcdir="`pwd`/$(DOC_SOURCE_DIR)"; \
+       cd $(srcdir) && gtkdoc-mkdb --module=$(DOC_MODULE) --source-dir=$$_srcdir --output-format=xml --expand-content-files="$(expand_content_files)" --main-sgml-file=$(DOC_MAIN_SGML_FILE) $(MKDB_OPTIONS)
+       touch sgml-build.stamp
+
+sgml.stamp: sgml-build.stamp
+       @true
+
+#### html ####
+
+html-build.stamp: sgml.stamp $(DOC_MAIN_SGML_FILE) $(content_files)
+       @echo 'gtk-doc: Building HTML'
+       @-chmod -R u+w $(srcdir)
+       rm -rf $(srcdir)/html
+       mkdir $(srcdir)/html
+       cd $(srcdir)/html && gtkdoc-mkhtml $(DOC_MODULE) ../$(DOC_MAIN_SGML_FILE)
+       test "x$(HTML_IMAGES)" = "x" || ( cd $(srcdir) && cp $(HTML_IMAGES) html )
+       @echo 'gtk-doc: Fixing cross-references'
+       cd $(srcdir) && gtkdoc-fixxref --module-dir=html --html-dir=$(HTML_DIR) $(FIXXREF_OPTIONS)
+       touch html-build.stamp
+
+##############
+
+clean-local:
+       rm -f *~ *.bak
+       rm -rf .libs
+
+distclean-local:
+       cd $(srcdir) && \
+         rm -rf xml $(REPORT_FILES) \
+                $(DOC_MODULE)-decl-list.txt $(DOC_MODULE)-decl.txt
+
+maintainer-clean-local: clean
+       cd $(srcdir) && rm -rf xml html
+
+install-data-local:
+       -installfiles=`echo $(srcdir)/html/*`; \
+       if test "$$installfiles" = '$(srcdir)/html/*'; \
+       then echo '-- Nothing to install' ; \
+       else \
+         $(mkinstalldirs) $(DESTDIR)$(TARGET_DIR); \
+         for i in $$installfiles; do \
+           echo '-- Installing '$$i ; \
+           $(INSTALL_DATA) $$i $(DESTDIR)$(TARGET_DIR); \
+         done; \
+         echo '-- Installing $(srcdir)/html/index.sgml' ; \
+         $(INSTALL_DATA) $(srcdir)/html/index.sgml $(DESTDIR)$(TARGET_DIR) || :; \
+         which gtkdoc-rebase >/dev/null && \
+           gtkdoc-rebase --relative --dest-dir=$(DESTDIR) --html-dir=$(DESTDIR)$(TARGET_DIR) ; \
+       fi
+       
+
+uninstall-local:
+       rm -f $(DESTDIR)$(TARGET_DIR)/*
+
+#
+# Require gtk-doc when making dist
+#
+if ENABLE_GTK_DOC
+dist-check-gtkdoc:
+else
+dist-check-gtkdoc:
+       @echo "*** gtk-doc must be installed (and --enable-gtk-doc) in order to make dist"
+       @false
+endif
+
+dist-hook: dist-check-gtkdoc dist-hook-local
+       mkdir $(distdir)/tmpl
+       mkdir $(distdir)/xml
+       mkdir $(distdir)/html
+       -cp $(srcdir)/tmpl/*.sgml $(distdir)/tmpl
+       -cp $(srcdir)/xml/*.xml $(distdir)/xml
+       cp $(srcdir)/html/* $(distdir)/html
+       -cp $(srcdir)/$(DOC_MODULE).types $(distdir)/
+       -cp $(srcdir)/$(DOC_MODULE)-sections.txt $(distdir)/
+       cd $(distdir) && rm -f $(DISTCLEANFILES)
+       -gtkdoc-rebase --online --relative --html-dir=$(distdir)/html
+
+.PHONY : dist-hook-local docs
diff --git a/build/Makefile.am.releasing b/build/Makefile.am.releasing
new file mode 100644 (file)
index 0000000..3ac479a
--- /dev/null
@@ -0,0 +1,194 @@
+# Some custom targets to make it easier to release things.
+#
+# To make real stable releases or devel snapshots, use either:
+#              make release-check
+# or           make release-publish
+#
+# To make a quick properly named (date and git hash stamped) tarball:
+#              make snapshot
+
+
+TAR_OPTIONS = --owner=0 --group=0
+
+dist-hook: dist-clear-sticky-bits
+
+# Clean up any sticky bits we may inherit from parent dir
+dist-clear-sticky-bits:
+       chmod -R a-s $(distdir)
+
+
+snapshot:
+       distdir="$(distdir)-`date '+%Y%m%d'`"; \
+       test -d "$(srcdir)/.git" && distdir=$$distdir-`cd "$(srcdir)" && git rev-parse HEAD | cut -c 1-6`; \
+       TAR_OPTIONS="$(TAR_OPTIONS)" $(MAKE) $(AM_MAKEFLAGS) distdir="$$distdir" snapshot-dist
+
+snapshot-dist: dist
+       @(echo "$(distdir) archives ready for distribution: "; \
+         list='$(DIST_ARCHIVES)'; for i in $$list; do echo $$i; done) | \
+         sed -e 1h -e 1s/./=/g -e 1p -e 1x -e '$$p' -e '$$x'
+
+RELEASE_OR_SNAPSHOT = $$(if test "x$(CAIRO_VERSION_MINOR)" = "x$$(echo "$(CAIRO_VERSION_MINOR)/2*2" | bc)" ; then echo release; else echo snapshot; fi)
+RELEASE_UPLOAD_HOST =   cairographics.org
+RELEASE_UPLOAD_BASE =  /srv/cairo.freedesktop.org/www
+RELEASE_UPLOAD_DIR =   $(RELEASE_UPLOAD_BASE)/$(RELEASE_OR_SNAPSHOT)s
+RELEASE_URL_BASE =     http://cairographics.org/$(RELEASE_OR_SNAPSHOT)s
+RELEASE_ANNOUNCE_LIST = cairo-announce@cairographics.org (and CC gnome-announce-list@gnome.org)
+
+MANUAL_VERSIONED =             manual-$(VERSION)
+MANUAL_TAR_FILE =              $(MANUAL_VERSIONED).tar.gz
+MANUAL_UPLOAD_DIR =            $(RELEASE_UPLOAD_BASE)
+
+tar_file = $(PACKAGE)-$(VERSION).tar.gz
+sha1_file = $(tar_file).sha1
+gpg_file = $(sha1_file).asc
+
+$(sha1_file): $(tar_file)
+       sha1sum $^ > $@
+
+$(gpg_file): $(sha1_file)
+       @echo "Please enter your GPG password to sign the checksum."
+       gpg --armor --sign $^ 
+
+release-verify-sane-changelogs: changelogs
+       @echo -n "Checking that the ChangeLog files are sane..."
+       @if grep -q "is required to generate" $(CHANGELOGS); then \
+               (echo "Ouch." && echo "Some of the ChangeLogs are not generated correctly." \
+               && echo "Remove ChangeLog* and make changelogs" \
+               && false); else :; fi
+       @echo "Good."
+
+release-verify-sane-tests:
+       @echo "Checking that the test suite is sane..."
+       @cd test && $(MAKE) $(AM_MAKEFLAGS) release-verify-sane-tests
+
+release-verify-even-micro:
+       @echo -n "Checking that $(VERSION) has an even micro component..."
+       @test "$(CAIRO_VERSION_MICRO)" = "`echo $(CAIRO_VERSION_MICRO)/2*2 | bc`" \
+               || (echo "Ouch." && echo "The version micro component '$(CAIRO_VERSION_MICRO)' is not an even number." \
+               && echo "The version in configure.in must be incremented before a new release." \
+               && false)
+       @echo "Good."
+
+release-verify-newer:
+       @echo -n "Checking that no $(VERSION) release already exists..."
+       @ssh $(RELEASE_UPLOAD_HOST) test ! -e $(RELEASE_UPLOAD_DIR)/$(tar_file) \
+               || (echo "Ouch." && echo "Found: $(RELEASE_UPLOAD_HOST):$(RELEASE_UPLOAD_DIR)/$(tar_file)" \
+               && echo "Are you sure you have an updated checkout?" \
+               && echo "This should never happen." \
+               && false)
+       @echo "Good."
+
+release-remove-old:
+       $(RM) $(tar_file) $(sha1_file) $(gpg_file)
+
+
+# Strict ordering enforced for parallel make to work
+release-check: \
+       release-verify-even-micro \
+       release-verify-sane-changelogs \
+       release-verify-sane-tests \
+       release-verify-newer \
+       $(NULL)
+       $(MAKE) $(AM_MAKEFLAGS) release-remove-old
+       TAR_OPTIONS="$(TAR_OPTIONS)" $(MAKE) $(AM_MAKEFLAGS) distcheck
+
+release-upload: $(tar_file) $(sha1_file) $(gpg_file)
+       mkdir -p releases
+       scp $(tar_file) $(sha1_file) $(gpg_file) $(RELEASE_UPLOAD_HOST):$(RELEASE_UPLOAD_DIR)
+       mv $(tar_file) $(sha1_file) $(gpg_file) releases
+       ssh $(RELEASE_UPLOAD_HOST) "rm -f $(RELEASE_UPLOAD_DIR)/LATEST-$(PACKAGE)-[0-9]* && ln -s $(tar_file) $(RELEASE_UPLOAD_DIR)/LATEST-$(PACKAGE)-$(VERSION)"
+       git tag -s  -m "cairo $(CAIRO_VERSION_MAJOR).$(CAIRO_VERSION_MINOR).$(CAIRO_VERSION_MICRO) release" $(CAIRO_VERSION_MAJOR).$(CAIRO_VERSION_MINOR).$(CAIRO_VERSION_MICRO)
+
+release-publish-message: releases/$(sha1_file)
+       @echo "Please follow the instructions in RELEASING to push stuff out and"
+       @echo "send out the announcement mails.  Here is the excerpt you need:"
+       @echo ""
+       @echo "Subject: $(PACKAGE) $(RELEASE_OR_SNAPSHOT) $(VERSION) now available"
+       @echo ""
+       @echo "============================== CUT HERE =============================="
+       @echo "A new $(PACKAGE) $(RELEASE_OR_SNAPSHOT) $(VERSION) is now available from:"
+       @echo ""
+       @echo " $(RELEASE_URL_BASE)/$(tar_file)"
+       @echo ""
+       @echo "    which can be verified with:"
+       @echo ""
+       @echo " $(RELEASE_URL_BASE)/$(sha1_file)"
+       @echo -n "      "
+       @cat releases/$(sha1_file)
+       @echo ""
+       @echo " $(RELEASE_URL_BASE)/$(gpg_file)"
+       @echo " (signed by `getent passwd "$$USER" | cut -d: -f 5 | cut -d, -f 1`)"
+       @echo ""
+       @echo "  Additionally, a git clone of the source tree:"
+       @echo ""
+       @echo " git clone git://git.cairographics.org/git/cairo"
+       @echo ""
+       @echo "    will include a signed $(VERSION) tag which points to a commit named:"
+       @echo " `git cat-file tag $(VERSION) | grep ^object | sed -e 's,object ,,'`"
+       @echo ""
+       @echo "    which can be verified with:"
+       @echo " git verify-tag $(VERSION)"
+       @echo ""
+       @echo "    and can be checked out with a command such as:"
+       @echo " git checkout -b build $(VERSION)"
+       @echo ""
+       @echo "============================== CUT HERE =============================="
+
+doc-publish-versioned: doc
+       rm -rf ./$(MANUAL_VERSIONED)
+       cp -a doc/public/html $(MANUAL_VERSIONED)
+       tar czf $(MANUAL_TAR_FILE) $(MANUAL_VERSIONED)
+       scp $(MANUAL_TAR_FILE) $(RELEASE_UPLOAD_HOST):$(MANUAL_UPLOAD_DIR)
+       ssh $(RELEASE_UPLOAD_HOST) "cd $(MANUAL_UPLOAD_DIR) && tar xzf $(MANUAL_TAR_FILE) && ln -sf $(MANUAL_TAR_FILE) cairo-$(MANUAL_TAR_FILE)"
+
+doc-publish-symlinks:
+       ssh $(RELEASE_UPLOAD_HOST) "cd $(MANUAL_UPLOAD_DIR) && rm -f manual && ln -s $(MANUAL_VERSIONED) manual && ln -sf $(MANUAL_TAR_FILE) cairo-manual.tar.gz"
+
+doc-publish:
+       $(MAKE) $(AM_MAKEFLAGS) doc-publish-versioned
+       @if test "$(RELEASE_OR_SNAPSHOT)" = release; then $(MAKE) $(AM_MAKEFLAGS) doc-publish-symlinks; fi
+
+# Strict ordering enforced for parallel make to work
+release-publish: release-check
+       $(MAKE) $(AM_MAKEFLAGS) release-upload
+       $(MAKE) $(AM_MAKEFLAGS) doc-publish
+       $(MAKE) $(AM_MAKEFLAGS) release-publish-message
+
+if OS_WIN32
+
+# Win32 package zipfiles
+runtime_zip_file = $(PACKAGE)-$(VERSION).zip
+developer_zip_file = $(PACKAGE)-dev-$(VERSION).zip
+
+$(runtime_zip_file): install
+       -$(RM) $@
+       pwd=`pwd`; cd $(prefix); \
+       zip "$$pwd"/$@ bin/libcairo-$(CAIRO_VERSION_SONUM).dll
+
+$(developer_zip_file): install
+       -$(RM) $@
+       pwd=`pwd`; cd $(prefix); \
+       zip -r "$$pwd"/$@ include/cairo lib/libcairo.dll.a lib/cairo.lib lib/pkgconfig/cairo.pc lib/pkgconfig/cairo-*.pc share/gtk-doc/html/cairo
+
+zips: $(runtime_zip_file) $(developer_zip_file)
+
+endif
+
+
+.PHONY: \
+       dist-clear-sticky-bits \
+       doc-publish \
+       doc-publish-symlinks \
+       doc-publish-versioned \
+       release-check \
+       release-publish \
+       release-publish-message \
+       release-remove-old \
+       release-upload \
+       release-verify-even-micro \
+       release-verify-newer \
+       release-verify-sane-changelogs \
+       release-verify-sane-tests \
+       snapshot \
+       snapshot-dist \
+       $(NULL)
diff --git a/build/Makefile.win32.common b/build/Makefile.win32.common
new file mode 100644 (file)
index 0000000..e422500
--- /dev/null
@@ -0,0 +1,76 @@
+default: all
+
+#
+# Edit build/Makefile.win32.features to enable features to build
+#
+include $(top_srcdir)/build/Makefile.win32.features
+include $(top_srcdir)/build/Makefile.win32.features-h
+
+ifeq ($(top_builddir),)
+top_builddir = $(top_srcdir)
+endif
+
+CC := cl
+LD := link
+AR := lib
+
+ifeq ($(CFG),debug)
+CFG_CFLAGS := -MDd -Od -Zi
+CFG_LDFLAGS := -DEBUG
+else
+CFG_CFLAGS := -MD -O2
+CFG_LDFLAGS :=
+endif
+
+PIXMAN_CFLAGS := -I$(top_srcdir)/../pixman/pixman
+PIXMAN_LIBS := $(top_builddir)/../pixman/pixman/$(CFG)/pixman-1.lib
+
+CAIRO_LIBS =  gdi32.lib msimg32.lib user32.lib
+ifeq ($(CAIRO_HAS_PNG_FUNCTIONS),1)
+LIBPNG_CFLAGS += -I$(top_srcdir)/../libpng/
+CAIRO_LIBS +=  $(top_builddir)/../libpng/libpng.lib
+endif
+ifeq ($(CAIRO_HAS_PS_SURFACE)$(CAIRO_HAS_PDF_SURFACE),00)
+else
+ZLIB_CFLAGS += -I$(top_srcdir)/../zlib/
+CAIRO_LIBS += $(top_builddir)/../zlib/zdll.lib
+endif
+
+DEFAULT_CFLAGS = -nologo $(CFG_CFLAGS)
+DEFAULT_CFLAGS += -I. -I$(top_srcdir) -I$(top_srcdir)/src
+DEFAULT_CFLAGS += $(PIXMAN_CFLAGS) $(LIBPNG_CFLAGS) $(ZLIB_CFLAGS)
+
+CAIRO_CFLAGS = $(DEFAULT_CFLAGS) $(CFLAGS)
+
+DEFAULT_LDFLAGS = -nologo $(CFG_LDFLAGS)
+DEFAULT_ARFLAGS = -nologo
+
+CAIRO_LDFLAGS = $(DEFAULT_LDFLAGS) $(LDFLAGS)
+CAIRO_ARFLAGS = $(DEFAULT_ARFLAGS) $(LDFLAGS)
+
+inform:
+       @echo
+ifneq ($(CFG),release)
+ifneq ($(CFG),debug)
+       @echo "Invalid configuration "$(CFG)" specified."
+       @echo -n "You must specify a configuration when "
+       @echo "running make, e.g. make CFG=debug"
+       @echo
+       @echo -n "Possible choices for configuration are "
+       @echo "'release' and 'debug'"
+       @exit 1
+endif
+endif
+
+# Some generic rules
+
+$(CFG)/%.obj: %.c $(top_srcdir)/src/cairo-features.h
+       @mkdir -p $(CFG)
+       @$(CC) $(CAIRO_CFLAGS) -c -Fo"$@" $<
+
+$(CFG)/%-static.obj: %.c $(top_srcdir)/src/cairo-features.h
+       @mkdir -p $(CFG)
+       @$(CC) $(CAIRO_CFLAGS) -c -DCAIRO_WIN32_STATIC_BUILD=1 -Fo"$@" $<
+
+clean:
+       @rm -f $(CFG)/*.obj $(CFG)/*.dll $(CFG)/*.lib $(CFG)/*.pdb $(CFG)/*.ilk || exit 0
diff --git a/build/Makefile.win32.features b/build/Makefile.win32.features
new file mode 100644 (file)
index 0000000..650b224
--- /dev/null
@@ -0,0 +1,40 @@
+# Generated by configure.  Modify to customize.
+
+CAIRO_HAS_XLIB_SURFACE=0
+CAIRO_HAS_XLIB_XRENDER_SURFACE=0
+CAIRO_HAS_XCB_SURFACE=0
+CAIRO_HAS_XLIB_XCB_FUNCTIONS=0
+CAIRO_HAS_XCB_SHM_FUNCTIONS=0
+CAIRO_HAS_QT_SURFACE=0
+CAIRO_HAS_QUARTZ_SURFACE=0
+CAIRO_HAS_QUARTZ_FONT=0
+CAIRO_HAS_QUARTZ_IMAGE_SURFACE=0
+CAIRO_HAS_WIN32_SURFACE=1
+CAIRO_HAS_WIN32_FONT=1
+CAIRO_HAS_SKIA_SURFACE=0
+CAIRO_HAS_OS2_SURFACE=0
+CAIRO_HAS_BEOS_SURFACE=0
+CAIRO_HAS_DRM_SURFACE=0
+CAIRO_HAS_GALLIUM_SURFACE=0
+CAIRO_HAS_PNG_FUNCTIONS=1
+CAIRO_HAS_GL_SURFACE=0
+CAIRO_HAS_GLESV2_SURFACE=0
+CAIRO_HAS_DIRECTFB_SURFACE=0
+CAIRO_HAS_VG_SURFACE=0
+CAIRO_HAS_EGL_FUNCTIONS=0
+CAIRO_HAS_GLX_FUNCTIONS=0
+CAIRO_HAS_WGL_FUNCTIONS=0
+CAIRO_HAS_SCRIPT_SURFACE=1
+CAIRO_HAS_FT_FONT=0
+CAIRO_HAS_FC_FONT=0
+CAIRO_HAS_PS_SURFACE=1
+CAIRO_HAS_PDF_SURFACE=1
+CAIRO_HAS_SVG_SURFACE=1
+CAIRO_HAS_TEST_SURFACES=0
+CAIRO_HAS_TEE_SURFACE=0
+CAIRO_HAS_XML_SURFACE=0
+CAIRO_HAS_PTHREAD=0
+CAIRO_HAS_GOBJECT_FUNCTIONS=0
+CAIRO_HAS_TRACE=0
+CAIRO_HAS_INTERPRETER=1
+CAIRO_HAS_SYMBOL_LOOKUP=0
diff --git a/build/Makefile.win32.features-h b/build/Makefile.win32.features-h
new file mode 100644 (file)
index 0000000..84d65f4
--- /dev/null
@@ -0,0 +1,127 @@
+# Generated by configure.  Do not edit.
+
+$(top_srcdir)/src/cairo-features.h: $(top_srcdir)/build/Makefile.win32.features
+       @echo "Generating src/cairo-features.h"
+       @echo "/* Generated by Makefile.win32.features-h.  Do not edit. */" > $(top_srcdir)/src/cairo-features.h
+       @echo "#ifndef CAIRO_FEATURES_H" >> $(top_srcdir)/src/cairo-features.h
+       @echo "#define CAIRO_FEATURES_H 1" >> $(top_srcdir)/src/cairo-features.h
+ifeq ($(CAIRO_HAS_XLIB_SURFACE),1)
+       @echo "#define CAIRO_HAS_XLIB_SURFACE 1" >> $(top_srcdir)/src/cairo-features.h
+endif
+ifeq ($(CAIRO_HAS_XLIB_XRENDER_SURFACE),1)
+       @echo "#define CAIRO_HAS_XLIB_XRENDER_SURFACE 1" >> $(top_srcdir)/src/cairo-features.h
+endif
+ifeq ($(CAIRO_HAS_XCB_SURFACE),1)
+       @echo "#define CAIRO_HAS_XCB_SURFACE 1" >> $(top_srcdir)/src/cairo-features.h
+endif
+ifeq ($(CAIRO_HAS_XLIB_XCB_FUNCTIONS),1)
+       @echo "#define CAIRO_HAS_XLIB_XCB_FUNCTIONS 1" >> $(top_srcdir)/src/cairo-features.h
+endif
+ifeq ($(CAIRO_HAS_XCB_SHM_FUNCTIONS),1)
+       @echo "#define CAIRO_HAS_XCB_SHM_FUNCTIONS 1" >> $(top_srcdir)/src/cairo-features.h
+endif
+ifeq ($(CAIRO_HAS_QT_SURFACE),1)
+       @echo "#define CAIRO_HAS_QT_SURFACE 1" >> $(top_srcdir)/src/cairo-features.h
+endif
+ifeq ($(CAIRO_HAS_QUARTZ_SURFACE),1)
+       @echo "#define CAIRO_HAS_QUARTZ_SURFACE 1" >> $(top_srcdir)/src/cairo-features.h
+endif
+ifeq ($(CAIRO_HAS_QUARTZ_FONT),1)
+       @echo "#define CAIRO_HAS_QUARTZ_FONT 1" >> $(top_srcdir)/src/cairo-features.h
+endif
+ifeq ($(CAIRO_HAS_QUARTZ_IMAGE_SURFACE),1)
+       @echo "#define CAIRO_HAS_QUARTZ_IMAGE_SURFACE 1" >> $(top_srcdir)/src/cairo-features.h
+endif
+ifeq ($(CAIRO_HAS_WIN32_SURFACE),1)
+       @echo "#define CAIRO_HAS_WIN32_SURFACE 1" >> $(top_srcdir)/src/cairo-features.h
+endif
+ifeq ($(CAIRO_HAS_WIN32_FONT),1)
+       @echo "#define CAIRO_HAS_WIN32_FONT 1" >> $(top_srcdir)/src/cairo-features.h
+endif
+ifeq ($(CAIRO_HAS_SKIA_SURFACE),1)
+       @echo "#define CAIRO_HAS_SKIA_SURFACE 1" >> $(top_srcdir)/src/cairo-features.h
+endif
+ifeq ($(CAIRO_HAS_OS2_SURFACE),1)
+       @echo "#define CAIRO_HAS_OS2_SURFACE 1" >> $(top_srcdir)/src/cairo-features.h
+endif
+ifeq ($(CAIRO_HAS_BEOS_SURFACE),1)
+       @echo "#define CAIRO_HAS_BEOS_SURFACE 1" >> $(top_srcdir)/src/cairo-features.h
+endif
+ifeq ($(CAIRO_HAS_DRM_SURFACE),1)
+       @echo "#define CAIRO_HAS_DRM_SURFACE 1" >> $(top_srcdir)/src/cairo-features.h
+endif
+ifeq ($(CAIRO_HAS_GALLIUM_SURFACE),1)
+       @echo "#define CAIRO_HAS_GALLIUM_SURFACE 1" >> $(top_srcdir)/src/cairo-features.h
+endif
+ifeq ($(CAIRO_HAS_PNG_FUNCTIONS),1)
+       @echo "#define CAIRO_HAS_PNG_FUNCTIONS 1" >> $(top_srcdir)/src/cairo-features.h
+endif
+ifeq ($(CAIRO_HAS_GL_SURFACE),1)
+       @echo "#define CAIRO_HAS_GL_SURFACE 1" >> $(top_srcdir)/src/cairo-features.h
+endif
+ifeq ($(CAIRO_HAS_GLESV2_SURFACE),1)
+       @echo "#define CAIRO_HAS_GLESV2_SURFACE 1" >> $(top_srcdir)/src/cairo-features.h
+endif
+ifeq ($(CAIRO_HAS_DIRECTFB_SURFACE),1)
+       @echo "#define CAIRO_HAS_DIRECTFB_SURFACE 1" >> $(top_srcdir)/src/cairo-features.h
+endif
+ifeq ($(CAIRO_HAS_VG_SURFACE),1)
+       @echo "#define CAIRO_HAS_VG_SURFACE 1" >> $(top_srcdir)/src/cairo-features.h
+endif
+ifeq ($(CAIRO_HAS_EGL_FUNCTIONS),1)
+       @echo "#define CAIRO_HAS_EGL_FUNCTIONS 1" >> $(top_srcdir)/src/cairo-features.h
+endif
+ifeq ($(CAIRO_HAS_GLX_FUNCTIONS),1)
+       @echo "#define CAIRO_HAS_GLX_FUNCTIONS 1" >> $(top_srcdir)/src/cairo-features.h
+endif
+ifeq ($(CAIRO_HAS_WGL_FUNCTIONS),1)
+       @echo "#define CAIRO_HAS_WGL_FUNCTIONS 1" >> $(top_srcdir)/src/cairo-features.h
+endif
+ifeq ($(CAIRO_HAS_SCRIPT_SURFACE),1)
+       @echo "#define CAIRO_HAS_SCRIPT_SURFACE 1" >> $(top_srcdir)/src/cairo-features.h
+endif
+ifeq ($(CAIRO_HAS_FT_FONT),1)
+       @echo "#define CAIRO_HAS_FT_FONT 1" >> $(top_srcdir)/src/cairo-features.h
+endif
+ifeq ($(CAIRO_HAS_FC_FONT),1)
+       @echo "#define CAIRO_HAS_FC_FONT 1" >> $(top_srcdir)/src/cairo-features.h
+endif
+ifeq ($(CAIRO_HAS_PS_SURFACE),1)
+       @echo "#define CAIRO_HAS_PS_SURFACE 1" >> $(top_srcdir)/src/cairo-features.h
+endif
+ifeq ($(CAIRO_HAS_PDF_SURFACE),1)
+       @echo "#define CAIRO_HAS_PDF_SURFACE 1" >> $(top_srcdir)/src/cairo-features.h
+endif
+ifeq ($(CAIRO_HAS_SVG_SURFACE),1)
+       @echo "#define CAIRO_HAS_SVG_SURFACE 1" >> $(top_srcdir)/src/cairo-features.h
+endif
+ifeq ($(CAIRO_HAS_TEST_SURFACES),1)
+       @echo "#define CAIRO_HAS_TEST_SURFACES 1" >> $(top_srcdir)/src/cairo-features.h
+endif
+       @echo "#define CAIRO_HAS_IMAGE_SURFACE 1" >> $(top_srcdir)/src/cairo-features.h
+       @echo "#define CAIRO_HAS_MIME_SURFACE 1" >> $(top_srcdir)/src/cairo-features.h
+       @echo "#define CAIRO_HAS_RECORDING_SURFACE 1" >> $(top_srcdir)/src/cairo-features.h
+       @echo "#define CAIRO_HAS_OBSERVER_SURFACE 1" >> $(top_srcdir)/src/cairo-features.h
+ifeq ($(CAIRO_HAS_TEE_SURFACE),1)
+       @echo "#define CAIRO_HAS_TEE_SURFACE 1" >> $(top_srcdir)/src/cairo-features.h
+endif
+ifeq ($(CAIRO_HAS_XML_SURFACE),1)
+       @echo "#define CAIRO_HAS_XML_SURFACE 1" >> $(top_srcdir)/src/cairo-features.h
+endif
+       @echo "#define CAIRO_HAS_USER_FONT 1" >> $(top_srcdir)/src/cairo-features.h
+ifeq ($(CAIRO_HAS_PTHREAD),1)
+       @echo "#define CAIRO_HAS_PTHREAD 1" >> $(top_srcdir)/src/cairo-features.h
+endif
+ifeq ($(CAIRO_HAS_GOBJECT_FUNCTIONS),1)
+       @echo "#define CAIRO_HAS_GOBJECT_FUNCTIONS 1" >> $(top_srcdir)/src/cairo-features.h
+endif
+ifeq ($(CAIRO_HAS_TRACE),1)
+       @echo "#define CAIRO_HAS_TRACE 1" >> $(top_srcdir)/src/cairo-features.h
+endif
+ifeq ($(CAIRO_HAS_INTERPRETER),1)
+       @echo "#define CAIRO_HAS_INTERPRETER 1" >> $(top_srcdir)/src/cairo-features.h
+endif
+ifeq ($(CAIRO_HAS_SYMBOL_LOOKUP),1)
+       @echo "#define CAIRO_HAS_SYMBOL_LOOKUP 1" >> $(top_srcdir)/src/cairo-features.h
+endif
+       @echo "#endif" >>  $(top_srcdir)/src/cairo-features.h
diff --git a/build/aclocal.cairo.m4 b/build/aclocal.cairo.m4
new file mode 100644 (file)
index 0000000..2f4873b
--- /dev/null
@@ -0,0 +1,216 @@
+dnl ==========================================================================
+dnl
+dnl Cairo-specific macros
+dnl
+
+dnl ==========================================================================
+
+dnl Usage:
+dnl   CAIRO_BIGENDIAN
+dnl
+AC_DEFUN([CAIRO_BIGENDIAN],
+[dnl
+       case $host_os in
+               darwin*)
+       AH_VERBATIM([X_BYTE_ORDER],
+[
+/* Deal with multiple architecture compiles on Mac OS X */
+#ifdef __APPLE_CC__
+#ifdef __BIG_ENDIAN__
+#define WORDS_BIGENDIAN 1
+#define FLOAT_WORDS_BIGENDIAN 1
+#else
+#undef WORDS_BIGENDIAN
+#undef FLOAT_WORDS_BIGENDIAN
+#endif
+#endif
+])
+               ;;
+               *) 
+       AC_C_BIGENDIAN
+       AX_C_FLOAT_WORDS_BIGENDIAN
+               ;;
+       esac
+])
+
+dnl CAIRO_CHECK_FUNCS_WITH_FLAGS(FUNCTION..., CFLAGS, LIBS
+dnl                              [, ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]])
+dnl Like AC_CHECK_FUNCS but with additional CFLAGS and LIBS
+dnl --------------------------------------------------------------------
+AC_DEFUN([CAIRO_CHECK_FUNCS_WITH_FLAGS],
+[dnl 
+       _save_cflags="$CFLAGS"
+       _save_libs="$LIBS"
+       CFLAGS="$CFLAGS $2"
+       LIBS="$LIBS $3"
+       AC_CHECK_FUNCS($1, $4, $5)
+       CFLAGS="$_save_cflags"
+       LIBS="$_save_libs"
+])
+
+dnl CAIRO_CONFIG_COMMANDS is like AC_CONFIG_COMMANDS, except that:
+dnl
+dnl    1) It redirects the stdout of the command to the file.
+dnl    2) It does not recreate the file if contents didn't change.
+dnl
+AC_DEFUN([CAIRO_CONFIG_COMMANDS],
+[dnl
+       AC_CONFIG_COMMANDS($1,
+       [
+               _config_file=$1
+               _tmp_file=cairoconf.tmp
+               AC_MSG_NOTICE([creating $_config_file])
+               {
+                       $2
+               } >> "$_tmp_file" ||
+               AC_MSG_ERROR([failed to write to $_tmp_file])
+
+               if cmp -s "$_tmp_file" "$_config_file"; then
+                 AC_MSG_NOTICE([$_config_file is unchanged])
+                 rm -f "$_tmp_file"
+               else
+                 mv "$_tmp_file" "$_config_file" ||
+                 AC_MSG_ERROR([failed to update $_config_file])
+               fi
+       ], $3)
+])
+
+dnl CAIRO_CC_TRY_LINK_WITH_ENV_SILENT(env-setup, program,
+dnl                                  true-action, false-action)
+dnl
+dnl Compile and link the program with the given environment setup.
+dnl The global cairo_cc_flag is set to "yes" or "no" according as
+dnl the link succeeded or not.  The link step must complete without
+dnl warnings or errors to stderr.
+dnl
+dnl Perform true-action on success and false-action on failure.
+dnl The values of CFLAGS, LIBS, LDFLAGS are saved before env-setup
+dnl is executed and restored right before the end of the macro.
+AC_DEFUN([CAIRO_CC_TRY_LINK_WITH_ENV_SILENT],[dnl
+       # AC_LANG_PROGRAM() produces a main() w/o args,
+       # but -Wold-style-definition doesn't like that.
+       # We need _some_ program so that we don't get
+       # warnings about empty compilation units, so always
+       # append a reasonable main().
+       _compile_program="$2"'
+               int main(int c, char **v) { (void)c; (void)v; return 0; }'
+
+       _save_cflags="$CFLAGS"
+       _save_ldflags="$LDFLAGS"
+       _save_libs="$LIBS"
+       $1
+       AC_LINK_IFELSE(
+               [AC_LANG_SOURCE([$_compile_program])],
+               [cairo_cc_stderr=`test -f conftest.err && cat conftest.err`
+                cairo_cc_flag=yes],
+               [cairo_cc_stderr=`test -f conftest.err && cat conftest.err`
+                cairo_cc_flag=no])
+
+       if test "x$cairo_cc_stderr" != "x"; then
+               cairo_cc_flag=no
+       fi
+
+       if test "x$cairo_cc_flag" = "xyes"; then
+               ifelse([$3], , :, [$3])
+       else
+               ifelse([$4], , :, [$4])
+       fi
+       CFLAGS="$_save_cflags"
+       LDFLAGS="$_save_ldflags"
+       LIBS="$_save_libs"
+])
+
+dnl check compiler flags with a program and no muttering.
+AC_DEFUN([CAIRO_CC_TRY_FLAG_SILENT],
+[dnl     (flags..., optional program, true-action, false-action)
+       CAIRO_CC_TRY_LINK_WITH_ENV_SILENT([CFLAGS="$CFLAGS $1"],
+                                         [$2], [$3], [$4])
+])
+
+dnl find a -Werror equivalent
+AC_DEFUN([CAIRO_CC_CHECK_WERROR],
+[dnl
+       _test_WERROR=${WERROR+set}
+       if test "z$_test_WERROR" != zset; then
+               WERROR=""
+               for _werror in -Werror -errwarn; do
+                       AC_MSG_CHECKING([whether $CC supports $_werror])
+                       CAIRO_CC_TRY_FLAG_SILENT(
+                               [$_werror],,
+                               [WERROR="$WERROR $_werror"],
+                               [:])
+                       AC_MSG_RESULT($cairo_cc_flag)
+               done
+       fi
+])
+
+dnl check compiler flags possibly using -Werror if available.
+AC_DEFUN([CAIRO_CC_TRY_FLAG],
+[dnl     (flags..., optional program, true-action, false-action)
+       CAIRO_CC_CHECK_WERROR
+       AC_MSG_CHECKING([whether $CC supports $1])
+       CAIRO_CC_TRY_FLAG_SILENT([$WERROR $1], [$2], [$3], [$4])
+       AC_MSG_RESULT([$cairo_cc_flag])
+])
+
+dnl Usage:
+dnl   CAIRO_CHECK_NATIVE_ATOMIC_PRIMITIVES
+AC_DEFUN([CAIRO_CHECK_NATIVE_ATOMIC_PRIMITIVES],
+[dnl
+       AC_CACHE_CHECK([for native atomic primitives], cairo_cv_atomic_primitives,
+       [
+               cairo_cv_atomic_primitives="none"
+
+               AC_TRY_LINK([
+int atomic_add(int i) { return __sync_fetch_and_add (&i, 1); }
+int atomic_cmpxchg(int i, int j, int k) { return __sync_val_compare_and_swap (&i, j, k); }
+], [],
+                 cairo_cv_atomic_primitives="Intel"
+                 )
+
+               if test "x$cairo_cv_atomic_primitives" = "xnone"; then
+                       AC_CHECK_HEADER([atomic_ops.h],
+                                       cairo_cv_atomic_primitives="libatomic-ops")
+               fi
+
+               if test "x$cairo_cv_atomic_primitives" = "xnone"; then
+                       AC_CHECK_HEADER([libkern/OSAtomic.h],
+                                       cairo_cv_atomic_primitives="OSAtomic")
+               fi
+       ])
+       if test "x$cairo_cv_atomic_primitives" = xIntel; then
+               AC_DEFINE(HAVE_INTEL_ATOMIC_PRIMITIVES, 1,
+                         [Enable if your compiler supports the Intel __sync_* atomic primitives])
+       fi
+
+       if test "x$cairo_cv_atomic_primitives" = "xlibatomic-ops"; then
+               AC_DEFINE(HAVE_LIB_ATOMIC_OPS, 1,
+                         [Enable if you have libatomic-ops-dev installed])
+       fi
+
+       if test "x$cairo_cv_atomic_primitives" = xOSAtomic; then
+               AC_DEFINE(HAVE_OS_ATOMIC_OPS, 1,
+                         [Enable if you have MacOS X atomic operations])
+       fi
+])
+
+dnl Usage:
+dnl   CAIRO_CHECK_ATOMIC_OP_NEEDS_MEMORY_BARRIER
+AC_DEFUN([CAIRO_CHECK_ATOMIC_OP_NEEDS_MEMORY_BARRIER],
+[dnl
+       AC_CACHE_CHECK([whether atomic ops require a memory barrier], cairo_cv_atomic_op_needs_memory_barrier,
+       [
+               case $host_cpu in
+                   i?86)       cairo_cv_atomic_op_needs_memory_barrier="no"  ;;
+                   x86_64)     cairo_cv_atomic_op_needs_memory_barrier="no"  ;;
+                   arm*)       cairo_cv_atomic_op_needs_memory_barrier="no"  ;;
+                   *)          cairo_cv_atomic_op_needs_memory_barrier="yes" ;;
+               esac
+       ])
+       if test "x$cairo_cv_atomic_op_needs_memory_barrier" = "xyes"; then
+           AC_DEFINE_UNQUOTED(ATOMIC_OP_NEEDS_MEMORY_BARRIER, 1,
+                              [whether memory barriers are needed around atomic operations])
+       fi
+])
+
+AC_DEFUN([CAIRO_TEXT_WRAP], [m4_text_wrap([$1], [$2],, 78)])
diff --git a/build/aclocal.compare.m4 b/build/aclocal.compare.m4
new file mode 100644 (file)
index 0000000..bd6c51b
--- /dev/null
@@ -0,0 +1,162 @@
+dnl @synopsis AX_COMPARE_VERSION(VERSION_A, OP, VERSION_B, [ACTION-IF-TRUE], [ACTION-IF-FALSE])
+dnl
+dnl This macro compares two version strings. It is used heavily in the
+dnl macro _AX_PATH_BDB for library checking. Due to the various number
+dnl of minor-version numbers that can exist, and the fact that string
+dnl comparisons are not compatible with numeric comparisons, this is
+dnl not necessarily trivial to do in a autoconf script. This macro
+dnl makes doing these comparisons easy.
+dnl
+dnl The six basic comparisons are available, as well as checking
+dnl equality limited to a certain number of minor-version levels.
+dnl
+dnl The operator OP determines what type of comparison to do, and can
+dnl be one of:
+dnl
+dnl  eq  - equal (test A == B)
+dnl  ne  - not equal (test A != B)
+dnl  le  - less than or equal (test A <= B)
+dnl  ge  - greater than or equal (test A >= B)
+dnl  lt  - less than (test A < B)
+dnl  gt  - greater than (test A > B)
+dnl
+dnl Additionally, the eq and ne operator can have a number after it to
+dnl limit the test to that number of minor versions.
+dnl
+dnl  eq0 - equal up to the length of the shorter version
+dnl  ne0 - not equal up to the length of the shorter version
+dnl  eqN - equal up to N sub-version levels
+dnl  neN - not equal up to N sub-version levels
+dnl
+dnl When the condition is true, shell commands ACTION-IF-TRUE are run,
+dnl otherwise shell commands ACTION-IF-FALSE are run. The environment
+dnl variable 'ax_compare_version' is always set to either 'true' or
+dnl 'false' as well.
+dnl
+dnl Examples:
+dnl
+dnl   AX_COMPARE_VERSION([3.15.7],[lt],[3.15.8])
+dnl   AX_COMPARE_VERSION([3.15],[lt],[3.15.8])
+dnl
+dnl would both be true.
+dnl
+dnl   AX_COMPARE_VERSION([3.15.7],[eq],[3.15.8])
+dnl   AX_COMPARE_VERSION([3.15],[gt],[3.15.8])
+dnl
+dnl would both be false.
+dnl
+dnl   AX_COMPARE_VERSION([3.15.7],[eq2],[3.15.8])
+dnl
+dnl would be true because it is only comparing two minor versions.
+dnl
+dnl   AX_COMPARE_VERSION([3.15.7],[eq0],[3.15])
+dnl
+dnl would be true because it is only comparing the lesser number of
+dnl minor versions of the two values.
+dnl
+dnl Note: The characters that separate the version numbers do not
+dnl matter. An empty string is the same as version 0. OP is evaluated
+dnl by autoconf, not configure, so must be a string, not a variable.
+dnl
+dnl The author would like to acknowledge Guido Draheim whose advice
+dnl about the m4_case and m4_ifvaln functions make this macro only
+dnl include the portions necessary to perform the specific comparison
+dnl specified by the OP argument in the final configure script.
+dnl
+dnl @category Misc
+dnl @author Tim Toolan <toolan@ele.uri.edu>
+dnl @version 2004-03-01
+dnl @license GPLWithACException
+
+dnl #########################################################################
+AC_DEFUN([AX_COMPARE_VERSION], [
+  # Used to indicate true or false condition
+  ax_compare_version=false
+
+  # Convert the two version strings to be compared into a format that
+  # allows a simple string comparison.  The end result is that a version
+  # string of the form 1.12.5-r617 will be converted to the form
+  # 0001001200050617.  In other words, each number is zero padded to four
+  # digits, and non digits are removed.
+  AS_VAR_PUSHDEF([A],[ax_compare_version_A])
+  A=`echo "$1" | sed -e 's/\([[0-9]]*\)/Z\1Z/g' \
+                     -e 's/Z\([[0-9]]\)Z/Z0\1Z/g' \
+                     -e 's/Z\([[0-9]][[0-9]]\)Z/Z0\1Z/g' \
+                     -e 's/Z\([[0-9]][[0-9]][[0-9]]\)Z/Z0\1Z/g' \
+                     -e 's/[[^0-9]]//g'`
+
+  AS_VAR_PUSHDEF([B],[ax_compare_version_B])
+  B=`echo "$3" | sed -e 's/\([[0-9]]*\)/Z\1Z/g' \
+                     -e 's/Z\([[0-9]]\)Z/Z0\1Z/g' \
+                     -e 's/Z\([[0-9]][[0-9]]\)Z/Z0\1Z/g' \
+                     -e 's/Z\([[0-9]][[0-9]][[0-9]]\)Z/Z0\1Z/g' \
+                     -e 's/[[^0-9]]//g'`
+
+  dnl # In the case of le, ge, lt, and gt, the strings are sorted as necessary
+  dnl # then the first line is used to determine if the condition is true.
+  dnl # The sed right after the echo is to remove any indented white space.
+  m4_case(m4_tolower($2),
+  [lt],[
+    ax_compare_version=`echo "x$A
+x$B" | sed 's/^ *//' | sort -r | sed "s/x${A}/false/;s/x${B}/true/;1q"`
+  ],
+  [gt],[
+    ax_compare_version=`echo "x$A
+x$B" | sed 's/^ *//' | sort | sed "s/x${A}/false/;s/x${B}/true/;1q"`
+  ],
+  [le],[
+    ax_compare_version=`echo "x$A
+x$B" | sed 's/^ *//' | sort | sed "s/x${A}/true/;s/x${B}/false/;1q"`
+  ],
+  [ge],[
+    ax_compare_version=`echo "x$A
+x$B" | sed 's/^ *//' | sort -r | sed "s/x${A}/true/;s/x${B}/false/;1q"`
+  ],[
+    dnl Split the operator from the subversion count if present.
+    m4_bmatch(m4_substr($2,2),
+    [0],[
+      # A count of zero means use the length of the shorter version.
+      # Determine the number of characters in A and B.
+      ax_compare_version_len_A=`echo "$A" | awk '{print(length)}'`
+      ax_compare_version_len_B=`echo "$B" | awk '{print(length)}'`
+
+      # Set A to no more than B's length and B to no more than A's length.
+      A=`echo "$A" | sed "s/\(.\{$ax_compare_version_len_B\}\).*/\1/"`
+      B=`echo "$B" | sed "s/\(.\{$ax_compare_version_len_A\}\).*/\1/"`
+    ],
+    [[0-9]+],[
+      # A count greater than zero means use only that many subversions
+      A=`echo "$A" | sed "s/\(\([[0-9]]\{4\}\)\{m4_substr($2,2)\}\).*/\1/"`
+      B=`echo "$B" | sed "s/\(\([[0-9]]\{4\}\)\{m4_substr($2,2)\}\).*/\1/"`
+    ],
+    [.+],[
+      AC_WARNING(
+        [illegal OP numeric parameter: $2])
+    ],[])
+
+    # Pad zeros at end of numbers to make same length.
+    ax_compare_version_tmp_A="$A`echo $B | sed 's/./0/g'`"
+    B="$B`echo $A | sed 's/./0/g'`"
+    A="$ax_compare_version_tmp_A"
+
+    # Check for equality or inequality as necessary.
+    m4_case(m4_tolower(m4_substr($2,0,2)),
+    [eq],[
+      test "x$A" = "x$B" && ax_compare_version=true
+    ],
+    [ne],[
+      test "x$A" != "x$B" && ax_compare_version=true
+    ],[
+      AC_WARNING([illegal OP parameter: $2])
+    ])
+  ])
+
+  AS_VAR_POPDEF([A])dnl
+  AS_VAR_POPDEF([B])dnl
+
+  dnl # Execute ACTION-IF-TRUE / ACTION-IF-FALSE.
+  if test "$ax_compare_version" = "true" ; then
+    m4_ifvaln([$4],[$4],[:])dnl
+    m4_ifvaln([$5],[else $5])dnl
+  fi
+]) dnl AX_COMPARE_VERSION
diff --git a/build/aclocal.enable.m4 b/build/aclocal.enable.m4
new file mode 100644 (file)
index 0000000..f3522b9
--- /dev/null
@@ -0,0 +1,409 @@
+dnl
+dnl These are the facilities for enable/disabling various features,
+dnl and for collecting CFLAGS/LIBS and generating per feature .pc
+dnl files, assembling list of source files to compile, creating
+dnl cairo-features.h and other generated files, etc...
+dnl
+
+dnl ===========================================================================
+
+dnl
+dnl Define a macro to enable features
+dnl  - Macro: _CAIRO_ENABLE (ID, NAME, WHAT, DEFAULT, COMMANDS)
+dnl
+dnl where:
+dnl
+dnl    ID is the sub-namespace in function names, eg. "ft" for cairo_ft_...
+dnl    NAME is the human-readable name of the feature, eg. "FreeType font"
+dnl    WHAT is the type of feature:
+dnl            "surface" for surface backends
+dnl            "font" for font backends
+dnl            "functions" for set of functions
+dnl            "" for private configurations
+dnl    DEFAULT is the default state of the feature:
+dnl            "no" for experimental features, eg. your favorite new backend
+dnl            "yes" for recommended features, eg. png functions
+dnl            "auto" for other supported features, eg. xlib surface backend
+dnl            "always" for mandatory features (can't be disabled), eg. image surface backend
+dnl    COMMANDS are run to check whether the feature can be enabled.
+dnl            They should set use_$(ID) to something other than yes if the
+dnl            feature cannot be built, eg. "no (requires SomeThing)".  It then
+dnl            should also set $(ID)_REQUIRES/CFLAGS/LIBS/...
+dnl            appropriately.  Look at the macro definition for more details,
+dnl            or ask if in doubt.
+dnl
+AC_DEFUN([_CAIRO_ENABLE],
+[dnl
+       dnl Sanity check ID
+       m4_if(
+               [$1],
+               m4_tolower(AS_TR_SH([$1])),
+               ,
+               [m4_fatal([invalid feature name `$1'])]
+       )dnl
+       m4_pushdef([cr_feature], [$1])dnl
+       m4_pushdef([cr_feature_name], m4_normalize([$2]))dnl
+       m4_pushdef([cr_feature_what], m4_normalize([$3]))dnl
+       m4_pushdef([cr_feature_default], m4_normalize([$4]))dnl
+       m4_pushdef([cr_feature_commands], [$5])dnl
+       dnl
+       m4_pushdef([cr_feature_arg], m4_translit([$1],_,-))dnl
+       dnl
+       dnl Sanity check default
+       m4_case(
+               cr_feature_default,
+               [no],,
+               [yes],,
+               [auto],,
+               [always],,
+               [m4_fatal([Invalid default value `]cr_feature_default[' for feature `]cr_feature['])]
+       )dnl
+       dnl
+       m4_if(cr_feature_default, [always],
+       [dnl
+               enable_$1=yes
+       ],[dnl
+               AC_ARG_ENABLE(cr_feature_arg,
+                             AS_HELP_STRING([--enable-]cr_feature_arg[=@<:@no/auto/yes@:>@],
+                                            [Enable cairo's ]cr_feature_name[ feature @<:@default=]cr_feature_default[@:>@]),
+                             enable_$1=$enableval, enable_$1=cr_feature_default)
+       ])dnl
+       dnl
+       AS_CASE([$enable_$1],
+       [no],[dnl
+               use_$1="no (disabled, use --enable-cr_feature_arg to enable)"
+       ],dnl
+       [yes|auto],[dnl
+               AC_MSG_CHECKING([for cairo's ]cr_feature_name[ feature])
+               echo
+
+               use_[]$1=yes
+               CAIRO_FEATURE_VARS_FOREACH(cr_var, [cr_feature[_]cr_var[=]_CAIRO_SH_ESCAPE_UNQUOTED(m4_do([cr_var_default_]cr_var[_value]))]m4_newline)
+
+               cr_feature_commands
+
+               AC_MSG_CHECKING([whether cairo's ]cr_feature_name[ feature could be enabled])
+               AC_MSG_RESULT([$use_$1])
+
+               AS_IF([test "x$enable_$1" = "xyes" -a "x$use_$1" != xyes],
+               [dnl
+                       AC_MSG_ERROR(
+                               m4_case(cr_feature_default,
+                                       [always],       [mandatory],
+                                       [yes],          [recommended],
+                                       ,               [requested]
+                               ) cr_feature_name[ feature could not be enabled])
+               ])dnl
+       ],dnl
+       [dnl    
+               AC_MSG_ERROR([invalid argument passed to --enable-]cr_feature_arg[: `$use_$1', should be one of @<:@no/auto/yes@:>@])
+       ])dnl
+
+       AS_IF([test "x$use_$1" = "xyes"],
+       [dnl
+               CAIRO_ACCUMULATED_FEATURE_VARS_FOREACH([cr_var],
+               [dnl
+                       CAIRO_ACCUMULATE_UNQUOTED_BEFORE(cr_var, [$]cr_feature[_]cr_var)
+               ])dnl
+       ],[dnl
+               dnl If not enabled, empty the vars so no one accidentally uses them.
+               CAIRO_FEATURE_VARS_FOREACH([cr_var], [unset cr_feature[_]cr_var]m4_newline)
+       ])dnl
+
+       _CAIRO_FEATURE_HOOKS(cr_feature, cr_feature_name, cr_feature_default, cr_feature_what)dnl
+
+       m4_popdef([cr_feature])dnl
+       m4_popdef([cr_feature_name])dnl
+       m4_popdef([cr_feature_what])dnl
+       m4_popdef([cr_feature_default])dnl
+       m4_popdef([cr_feature_commands])dnl
+       m4_popdef([cr_feature_arg])dnl
+])
+
+
+dnl ===========================================================================
+
+m4_define([_CAIRO_FEATURE_VARS])
+
+dnl
+dnl CAIRO_FEATURE_VARS_REGISTER(VARS, DEFAULT-VALUE=[])
+dnl
+dnl Registers variables to be collected from feature-enabling code segments.
+dnl VARS should be a whitespace-separate list of variable names.
+dnl
+dnl DEFAULT-VALUE is m4 macros to set default value of VARS
+dnl
+AC_DEFUN([CAIRO_FEATURE_VARS_REGISTER],
+[dnl
+       m4_foreach_w([cr_var], [$1],
+                    [m4_append_uniq([_CAIRO_FEATURE_VARS], cr_var, [ ],,
+                                    [m4_fatal([Feature variable `]cr_var[' already registered])])])dnl
+       m4_foreach_w([cr_var], [$1],
+       [dnl
+               m4_define([cr_var_default_]cr_var[_value], m4_default([$2],[[$ac_env_[]]cr_feature[[]_]]cr_var[[_value]]))dnl
+       ])dnl
+])
+
+dnl
+dnl CAIRO_FEATURE_VARS_FOREACH(VAR, COMMANDS)
+dnl 
+dnl Run COMMANDS for each registered feature variable.
+dnl Defines VAR to the variable being processed.
+dnl
+AC_DEFUN([CAIRO_FEATURE_VARS_FOREACH],
+[dnl
+       m4_foreach_w([$1], _CAIRO_FEATURE_VARS, [$2])dnl
+])
+
+
+dnl ===========================================================================
+
+m4_define([_CAIRO_ACCUMULATORS])dnl
+
+m4_define([_CAIRO_ACCUMULATORS_REGISTER],
+[dnl
+       m4_foreach_w([cr_var], [$1],
+                    [m4_append_uniq([_CAIRO_ACCUMULATORS], cr_var, [ ],,
+                                    [m4_fatal([Accumulator `]cr_var[' already registered])])])dnl
+       m4_foreach_w([cr_var], [$1], [m4_define([cr_acc_]cr_var[_sep], [$2])])dnl
+       m4_foreach_w([cr_var], [$1], [[CAIRO_]cr_var[=$3]]m4_newline)dnl
+       m4_foreach_w([cr_var], [$1], [m4_pattern_allow([CAIRO_]cr_var)])dnl
+])dnl
+
+m4_define([_CAIRO_SH_ESCAPE],['m4_bpatsubst([$1],['],[\\'])'])dnl
+m4_define([_CAIRO_SH_ESCAPE_UNQUOTED],["m4_bpatsubst([$1],["],[\\"])"])dnl
+
+dnl
+dnl CAIRO_ACCUMULATORS_REGISTER(VARS, SEPARATOR=[], INITIAL-VALUE=[])
+dnl
+dnl Registers accumulators.  An accumulator is a shell variable that can
+dnl be accumulated to.  The macros take care of adding a SEPARATOR between
+dnl accumulated values.
+dnl
+dnl VARS should be a whitespace-separate list of variable names.  The actual
+dnl shell variable resulting for each variable is prefixed with CAIRO_.
+dnl
+AC_DEFUN([CAIRO_ACCUMULATORS_REGISTER],
+[dnl
+       _CAIRO_ACCUMULATORS_REGISTER([$1],[$2],_CAIRO_SH_ESCAPE([$3]))dnl
+])dnl
+
+dnl
+dnl Like CAIRO_ACCUMULATORS_REGISTER but INITIAL-VALUE is left unquoted,
+dnl so it can reference other shell variables for example.
+dnl
+AC_DEFUN([CAIRO_ACCUMULATORS_REGISTER_UNQUOTED],
+[dnl
+       _CAIRO_ACCUMULATORS_REGISTER([$1],[$2],_CAIRO_SH_ESCAPE_UNQUOTED([$3]))dnl
+])dnl
+
+m4_define([_CAIRO_ACCUMULATOR_CHECK],
+[dnl
+        m4_ifdef([cr_acc_$1_sep],,[m4_fatal([Accumulator `]$1[' not defined.])])dnl
+])dnl
+
+m4_define([_CAIRO_ACCUMULATE],
+[dnl
+       _CAIRO_ACCUMULATOR_CHECK([$1])dnl
+       m4_ifval([$2], [$3]m4_newline)dnl
+])dnl
+
+dnl
+dnl CAIRO_ACCUMULATE(VAR, VALUE)
+dnl
+dnl Appends VALUE to accumulator VAR
+dnl
+AC_DEFUN([CAIRO_ACCUMULATE],
+[dnl
+       _CAIRO_ACCUMULATE([$1], [$2], [CAIRO_$1="${CAIRO_$1}]m4_do([cr_acc_$1_sep])["_CAIRO_SH_ESCAPE([$2])])dnl
+])dnl
+
+dnl
+dnl CAIRO_ACCUMULATE_BEFORE(VAR, VALUE)
+dnl
+dnl Prepends VALUE to accumulator VAR
+dnl
+AC_DEFUN([CAIRO_ACCUMULATE_BEFORE],
+[dnl
+       _CAIRO_ACCUMULATE([$1], [$2], [CAIRO_$1=_CAIRO_SH_ESCAPE([$2])"]m4_do([cr_acc_$1_sep])[${CAIRO_$1}"])dnl
+])dnl
+
+m4_define([_CAIRO_ACCUMULATE_UNQUOTED],
+[dnl
+       _CAIRO_ACCUMULATOR_CHECK([$1])dnl
+       m4_ifval([$2], [m4_bmatch([$2],[[$]],[test -n "$2" &&]) $3]m4_newline)dnl
+])dnl
+
+dnl
+dnl CAIRO_ACCUMULATE_UNQUOTED(VAR, VALUE)
+dnl
+dnl Like CAIRO_ACCUMULATE but VALUE is left unquoted,
+dnl so it can reference other shell variables for example.
+dnl
+AC_DEFUN([CAIRO_ACCUMULATE_UNQUOTED],
+[dnl
+       _CAIRO_ACCUMULATE_UNQUOTED([$1], [$2], [CAIRO_$1="${CAIRO_$1}]m4_do([cr_acc_$1_sep])["]_CAIRO_SH_ESCAPE_UNQUOTED([$2]))dnl
+])dnl
+
+dnl
+dnl CAIRO_ACCUMULATE_UNQUOTED_BEFORE(VAR, VALUE)
+dnl
+dnl Like CAIRO_ACCUMULATE_BEFORE but VALUE is left unquoted,
+dnl so it can reference other shell variables for example.
+dnl
+AC_DEFUN([CAIRO_ACCUMULATE_UNQUOTED_BEFORE],
+[dnl
+       _CAIRO_ACCUMULATE_UNQUOTED([$1], [$2], [CAIRO_$1=]_CAIRO_SH_ESCAPE_UNQUOTED([$2])["]m4_do([cr_acc_$1_sep])[${CAIRO_$1}"])dnl
+])dnl
+
+dnl
+dnl CAIRO_ACCUMULATE_UNQUOTED_UNCHECKED(VAR, VALUE)
+dnl
+dnl Like CAIRO_ACCUMULATE_UNQUOTED but VALUE is not tested for emptiness.
+dnl
+AC_DEFUN([CAIRO_ACCUMULATE_UNQUOTED_UNCHECKED],
+[dnl
+       _CAIRO_ACCUMULATE([$1], [$2], [CAIRO_$1="${CAIRO_$1}]m4_do([cr_acc_$1_sep])["]_CAIRO_SH_ESCAPE_UNQUOTED([$2]))dnl
+])dnl
+
+dnl
+dnl CAIRO_ACCUMULATE_UNQUOTED_UNCHECKED_BEFORE(VAR, VALUE)
+dnl
+dnl Like CAIRO_ACCUMULATE_UNQUOTED_BEFORE but VALUE is not tested for emptiness.
+dnl
+AC_DEFUN([CAIRO_ACCUMULATE_UNQUOTED_BEFORE],
+[dnl
+       _CAIRO_ACCUMULATE([$1], [$2], [CAIRO_$1=]_CAIRO_SH_ESCAPE_UNQUOTED([$2])["]m4_do([cr_acc_$1_sep])[${CAIRO_$1}"])dnl
+])dnl
+
+dnl
+dnl CAIRO_ACCUMULATORS_FOREACH(VAR, COMMANDS)
+dnl 
+dnl Run COMMANDS for each registered accumulator.
+dnl Defines VAR to the accumulator being processed.
+dnl
+AC_DEFUN([CAIRO_ACCUMULATORS_FOREACH],
+[dnl
+       m4_foreach_w([$1], _CAIRO_ACCUMULATORS, [$2])dnl
+])dnl
+
+
+dnl ===========================================================================
+
+m4_define([_CAIRO_ACCUMULATED_FEATURE_VARS])dnl
+
+dnl
+dnl CAIRO_ACCUMULATED_FEATURE_VARS_REGISTER(VARS, DEFAULT-VALUE=[], SEPARATOR=[], INITIAL-VALUE=[])
+dnl
+dnl Defines VARS as feature variables and accumulators.  Also accumulates
+dnl (prepending, not appending) feature values for VARS.
+dnl
+AC_DEFUN([CAIRO_ACCUMULATED_FEATURE_VARS_REGISTER],
+[dnl
+       m4_foreach_w([cr_var], [$1],
+                    [m4_append_uniq([_CAIRO_ACCUMULATED_FEATURE_VARS], cr_var, [ ],,
+                                    [m4_fatal([Accumulated feature variable `]cr_var[' already registered])])])dnl
+       CAIRO_FEATURE_VARS_REGISTER([$1],[$2])dnl
+       CAIRO_ACCUMULATORS_REGISTER_UNQUOTED([$1],[$3],[$4])dnl
+])dnl
+
+dnl
+dnl CAIRO_ACCUMULATED_FEATURE_VARS_FOREACH(VAR, COMMANDS)
+dnl 
+dnl Run COMMANDS for each registered accumulated feature variable.
+dnl Defines VAR to the variable being processed.
+dnl
+AC_DEFUN([CAIRO_ACCUMULATED_FEATURE_VARS_FOREACH],
+[dnl
+       m4_foreach_w([$1], _CAIRO_ACCUMULATED_FEATURE_VARS, [$2])dnl
+])dnl
+
+dnl ===========================================================================
+
+dnl
+dnl CAIRO_FEATURE_IF_ENABLED(FEATURE=cr_feature, COMMANDS)
+dnl
+dnl Run COMMANDS if FEATURE is enabled.
+dnl
+AC_DEFUN([CAIRO_FEATURE_IF_ENABLED],
+[dnl
+       AS_IF([test "x$use_]m4_default([$1], cr_feature)[" = xyes], [$2], [$3])dnl
+])dnl
+
+m4_define([_CAIRO_FEATURE_HOOK_MATCH_SH_BOOL],
+[dnl
+       m4_case([$1],
+               [*],    [$3],
+               [no],   [AS_IF([test "x$2" != xyes], [:m4_newline()$3])],
+               [yes],  [AS_IF([test "x$2" = xyes], [:m4_newline()$3])],
+                       [m4_fatal([Invalid ENABLED value `]$1['])])dnl
+])dnl
+
+m4_define([_CAIRO_FEATURE_HOOK_MATCH_M4],
+[dnl
+       m4_case([$1],
+               [*],    [$3],
+               [$2],   [$3],
+               [!$2],  ,
+                       [m4_bmatch([$1], [^!], [$3])])dnl
+])dnl
+
+m4_define([_CAIRO_FEATURE_HOOKS])dnl
+
+dnl
+dnl CAIRO_FEATURE_HOOK_REGISTER(ENABLED, DEFAULT, WHAT, COMMANDS)
+dnl
+dnl    ENABLED is the feature enabledness to match
+dnl    DEFAULT is the default value of features to match
+dnl    WHAT is the type of features to match
+dnl    COMMANDS is commands to run for matched features.
+dnl
+dnl Runs COMMANDS for features matching ENABLED, DEFAULT, and WHAT.
+dnl Hooks are run for each feature in the order they are added.
+dnl
+dnl DEFAULT and WHAT are matched like this:
+dnl    [*]     matches all values
+dnl    [val]   matches [val]
+dnl    [!val]  matches anything other than [val]
+dnl
+dnl ENABLED is matched like this:
+dnl    [yes]   matches enabled features
+dnl    [no]    matches disabled features
+dnl    [*]     matches all features
+dnl
+dnl The following macros can be used in COMMANDS:
+dnl
+dnl    cr_feature expands to the feature id, eg "ft"
+dnl    cr_feature_name expands to the human-readable name of the feature, eg. "FreeType font"
+dnl    cr_feature_default expands to the default state of the feature:
+dnl            "no" for experimental features, eg. your favorite new backend
+dnl            "yes" for recommended features, eg. png functions
+dnl            "auto" for other supported features, eg. xlib surface backend
+dnl            "always" for mandatory features (can't be disabled), eg. image surface backend
+dnl    cr_what expands to the type of feature:
+dnl            "surface" for surface backends
+dnl            "font" for font backends
+dnl            "functions" for set of functions
+dnl            "" for private configurations
+dnl
+dnl These four values are also set as $1 to $4.  To know if feature was
+dnl enabled from within COMMANDS, use CAIRO_FEATURE_IF_ENABLED:
+dnl
+dnl    CAIRO_FEATURE_IF_ENABLED($1, [IF-ENABLED], [IF-DISABLED])
+dnl
+dnl or compare $use_$1 to string "yes".  As in:
+dnl
+dnl    AS_IF([test "x$use_$1" = "xyes"], [IF-ENABLED], [IF-DISABLED])
+dnl
+AC_DEFUN([CAIRO_FEATURE_HOOK_REGISTER],
+[dnl
+       m4_append([_CAIRO_FEATURE_HOOKS],
+       [dnl
+               _CAIRO_FEATURE_HOOK_MATCH_M4([$2], cr_feature_default,
+               [_CAIRO_FEATURE_HOOK_MATCH_M4([$3], cr_feature_what,
+                [_CAIRO_FEATURE_HOOK_MATCH_SH_BOOL([$1], [$use_]cr_feature,
+                 [$4]
+               )])])dnl
+       ], m4_newline)dnl
+])dnl
+
diff --git a/build/aclocal.float.m4 b/build/aclocal.float.m4
new file mode 100644 (file)
index 0000000..18ec316
--- /dev/null
@@ -0,0 +1,64 @@
+# AX_C_FLOAT_WORDS_BIGENDIAN ([ACTION-IF-TRUE], [ACTION-IF-FALSE],
+#                             [ACTION-IF-UNKNOWN])
+#
+# Checks the ordering of words within a multi-word float. This check
+# is necessary because on some systems (e.g. certain ARM systems), the
+# float word ordering can be different from the byte ordering. In a
+# multi-word float context, "big-endian" implies that the word containing
+# the sign bit is found in the memory location with the lowest address.
+# This implemenation was inspired by the AC_C_BIGENDIAN macro in autoconf.
+# -------------------------------------------------------------------------
+AC_DEFUN([AX_C_FLOAT_WORDS_BIGENDIAN],
+  [AC_CACHE_CHECK(whether float word ordering is bigendian,
+                  ax_cv_c_float_words_bigendian, [
+
+# The endianess is detected by first compiling C code that contains a special
+# double float value, then grepping the resulting object file for certain
+# strings of ascii values. The double is specially crafted to have a
+# binary representation that corresponds with a simple string. In this
+# implementation, the string "noonsees" was selected because the individual
+# word values ("noon" and "sees") are palindromes, thus making this test
+# byte-order agnostic. If grep finds the string "noonsees" in the object
+# file, the target platform stores float words in big-endian order. If grep
+# finds "seesnoon", float words are in little-endian order. If neither value
+# is found, the user is instructed to specify the ordering.
+
+ax_cv_c_float_words_bigendian=unknown
+AC_COMPILE_IFELSE([AC_LANG_SOURCE([[
+
+double d = 90904234967036810337470478905505011476211692735615632014797120844053488865816695273723469097858056257517020191247487429516932130503560650002327564517570778480236724525140520121371739201496540132640109977779420565776568942592.0;
+
+]])], [
+
+if strings - conftest.$ac_objext | grep noonsees >/dev/null ; then
+  ax_cv_c_float_words_bigendian=yes
+fi
+if strings - conftest.$ac_objext | grep seesnoon >/dev/null ; then
+  if test "$ax_cv_c_float_words_bigendian" = unknown; then
+    ax_cv_c_float_words_bigendian=no
+  else
+    ax_cv_c_float_words_bigendian=unknown
+  fi
+fi
+
+])])
+
+case $ax_cv_c_float_words_bigendian in
+  yes)
+    m4_default([$1],
+      [AC_DEFINE([FLOAT_WORDS_BIGENDIAN], 1,
+                 [Define to 1 if your system stores words within floats
+                  with the most significant word first])]) ;;
+  no)
+    $2 ;;
+  *)
+    m4_default([$3],
+      [AC_MSG_ERROR([
+
+Unknown float word ordering. You need to manually preset
+ax_cv_c_float_words_bigendian=no (or yes) according to your system.
+
+    ])]) ;;
+esac
+
+])# AX_C_FLOAT_WORDS_BIGENDIAN
diff --git a/build/aclocal.gtk-doc.m4 b/build/aclocal.gtk-doc.m4
new file mode 100644 (file)
index 0000000..bfdfa1d
--- /dev/null
@@ -0,0 +1,39 @@
+dnl -*- mode: autoconf -*-
+
+# serial 1
+
+dnl Usage:
+dnl   GTK_DOC_CHECK([minimum-gtk-doc-version])
+AC_DEFUN([GTK_DOC_CHECK],
+[
+  AC_BEFORE([AC_PROG_LIBTOOL],[$0])dnl setup libtool first
+  AC_BEFORE([AM_PROG_LIBTOOL],[$0])dnl setup libtool first
+  dnl for overriding the documentation installation directory
+  AC_ARG_WITH([html-dir],
+    AS_HELP_STRING([--with-html-dir=PATH], [path to installed docs]),,
+    [with_html_dir='${datadir}/gtk-doc/html'])
+  HTML_DIR="$with_html_dir"
+  AC_SUBST([HTML_DIR])
+
+  dnl enable/disable documentation building
+  AC_ARG_ENABLE([gtk-doc],
+    AS_HELP_STRING([--enable-gtk-doc],
+                   [use gtk-doc to build documentation [[default=no]]]),,
+    [enable_gtk_doc=no])
+
+  if test x$enable_gtk_doc = xyes; then
+    ifelse([$1],[],
+      [PKG_CHECK_EXISTS([gtk-doc],,
+                        AC_MSG_ERROR([gtk-doc not installed and --enable-gtk-doc requested]))],
+      [PKG_CHECK_EXISTS([gtk-doc >= $1],,
+                        AC_MSG_ERROR([You need to have gtk-doc >= $1 installed to build gtk-doc]))])
+  fi
+
+  AC_MSG_CHECKING([whether to build gtk-doc documentation])
+  AC_MSG_RESULT($enable_gtk_doc)
+
+  AC_PATH_PROGS(GTKDOC_CHECK,gtkdoc-check,)
+
+  AM_CONDITIONAL([ENABLE_GTK_DOC], [test x$enable_gtk_doc = xyes])
+  AM_CONDITIONAL([GTK_DOC_USE_LIBTOOL], [test -n "$LIBTOOL"])
+])
diff --git a/build/aclocal.makefile.m4 b/build/aclocal.makefile.m4
new file mode 100644 (file)
index 0000000..7077781
--- /dev/null
@@ -0,0 +1,234 @@
+dnl
+dnl These are the facilities for generating Makefile.am.features and
+dnl Makefile.win32.features files.
+dnl
+
+dnl ===========================================================================
+
+dnl
+dnl Define cr_feature_tag ala other cr_feature_* macros
+dnl Expands to CAIRO_HAS_FEATURE_ID
+dnl
+m4_define([_CAIRO_BUILD_FEATURE_TAG_NORMALIZED],
+       [CAIRO_HAS_[$1]m4_bmatch([$1],[$2$],,[$2])])dnl
+m4_define([_CAIRO_BUILD_FEATURE_TAG],
+       [_CAIRO_BUILD_FEATURE_TAG_NORMALIZED(AS_TR_CPP([$1]),AS_TR_CPP(m4_ifval([$2],[ $2])))])dnl
+m4_define([cr_feature_tag],
+       [_CAIRO_BUILD_FEATURE_TAG(cr_feature,cr_feature_what)])dnl
+
+
+dnl ===========================================================================
+dnl
+dnl CAIRO_INIT_MAKEFILES([AUX-DIR])
+dnl
+dnl Sets up automake and win32 conditionals for all features
+dnl
+AC_DEFUN([CAIRO_INIT_MAKEFILES],
+[dnl
+       dnl Allow feature tags in the output
+       m4_pattern_allow(^CAIRO_HAS_)dnl
+
+       dnl Automake conditionals for non-builtin features
+       CAIRO_FEATURE_HOOK_REGISTER(*,!always,*,
+       [dnl
+               AM_CONDITIONAL(cr_feature_tag, [test "x$use_]cr_feature[" = xyes])dnl
+       ])dnl
+
+       CAIRO_CONFIG_MAKEFILE_PRIVATE_WIN32([_],[$1],[],[[# Generated by configure.  Modify to customize.]])dnl
+       CAIRO_MAKEFILE_ACCUMULATE_FEATURE([_],*,!always,*,[cr_feature_tag=m4_if(cr_feature_default,[yes],1,[m4_bmatch(cr_feature,[win32],1,0)])])dnl
+])dnl
+
+dnl ===========================================================================
+
+m4_define([_CAIRO_MAKEFILES])dnl
+
+dnl
+dnl CAIRO_CONFIG_MAKEFILE(TAG, DIR, [SUFFIX], [HEADER])
+dnl
+dnl Create DIR/Makefile.{am,win32}.SUFFIX files
+dnl TAG is a TAG used by other CAIRO_MAKEFILE_* macros to append to these
+dnl Makefile's.
+dnl
+dnl HEADER is appended at the top of the Makefile's.  If HEADER is not
+dnl set, the generic "Generated by configure.  Do not edit." comment
+dnl is added.
+dnl
+AC_DEFUN([CAIRO_CONFIG_MAKEFILE],
+[dnl
+       m4_append_uniq([_CAIRO_MAKEFILES], [$1], [ ])dnl
+       CAIRO_CONFIG_MAKEFILE_PRIVATE([$1], [$2], [$3], [$4])dnl
+])dnl
+
+dnl Like CAIRO_CONFIG_MAKEFILE but only generate win32 makefile
+AC_DEFUN([CAIRO_CONFIG_MAKEFILE_WIN32],
+[dnl
+       m4_append_uniq([_CAIRO_MAKEFILES], [$1], [ ])dnl
+       CAIRO_CONFIG_MAKEFILE_PRIVATE_WIN32([$1], [$2], [$3], [$4])dnl
+])dnl
+
+dnl Like CAIRO_CONFIG_MAKEFILE but only generate automake makefile
+AC_DEFUN([CAIRO_CONFIG_MAKEFILE_AMAKE],
+[dnl
+       m4_append_uniq([_CAIRO_MAKEFILES], [$1], [ ])dnl
+       CAIRO_CONFIG_MAKEFILE_PRIVATE_AMAKE([$1], [$2], [$3], [$4])dnl
+])dnl
+
+dnl
+dnl CAIRO_CONFIG_MAKEFILE_PRIVATE(TAG, DIR, [SUFFIX], [HEADER])
+dnl
+dnl Like CAIRO_CONFIG_MAKEFILE but this makefile tag won't match
+dnl against '*' in makefile accumulators.
+dnl
+AC_DEFUN([CAIRO_CONFIG_MAKEFILE_PRIVATE],
+[dnl
+       m4_ifdef([cr_make_$1_dir],
+                [m4_fatal([Makefile `$1' already registered])])dnl
+        m4_define([cr_make_$1_dir],[$2])dnl
+
+       CAIRO_CONFIG_MAKEFILE_PRIVATE_AMAKE([$1], [$2], [$3], [$4])dnl
+       CAIRO_CONFIG_MAKEFILE_PRIVATE_WIN32([$1], [$2], [$3], [$4])dnl
+])dnl
+
+dnl Like CAIRO_CONFIG_MAKEFILE_PRIVATE but only generate automake makefile
+AC_DEFUN([CAIRO_CONFIG_MAKEFILE_PRIVATE_AMAKE],
+[dnl
+       m4_ifdef([cr_make_$1_dir_amake],
+                [m4_fatal([Automake makefile `$1' already registered])])dnl
+        m4_define([cr_make_$1_dir_amake],[$2])dnl
+        m4_define([cr_make_$1_dir_any],[$2])dnl
+
+       dnl Accumulators
+       CAIRO_ACCUMULATORS_REGISTER(MAKEFILE_$1_AMAKE, m4_newline, m4_default([$4],[[# Generated by configure.  Do not edit.]])m4_newline)dnl
+
+       dnl Generate
+       CAIRO_CONFIG_COMMANDS([$srcdir/]m4_if([$2],[.],,[$2/])[Makefile.am.]m4_default([$3],[features]),
+                             [echo "$CAIRO_MAKEFILE_$1_AMAKE"],
+                             [CAIRO_MAKEFILE_$1_AMAKE='$CAIRO_MAKEFILE_$1_AMAKE'])dnl
+])dnl
+
+dnl Like CAIRO_CONFIG_MAKEFILE_PRIVATE but only generate win32 makefile
+AC_DEFUN([CAIRO_CONFIG_MAKEFILE_PRIVATE_WIN32],
+[dnl
+       m4_ifdef([cr_make_$1_dir_win32],
+                [m4_fatal([Win32 makefile `$1' already registered])])dnl
+        m4_define([cr_make_$1_dir_win32],[$2])dnl
+        m4_define([cr_make_$1_dir_any],[$2])dnl
+
+       dnl Accumulators
+       CAIRO_ACCUMULATORS_REGISTER(MAKEFILE_$1_WIN32, m4_newline, m4_default([$4],[[# Generated by configure.  Do not edit.]])m4_newline)dnl
+
+       dnl Generate
+       CAIRO_CONFIG_COMMANDS([$srcdir/]m4_if([$2],[.],,[$2/])[Makefile.win32.]m4_default([$3],[features]),
+                             [echo "$CAIRO_MAKEFILE_$1_WIN32"],
+                             [CAIRO_MAKEFILE_$1_WIN32='$CAIRO_MAKEFILE_$1_WIN32'])dnl
+])dnl
+
+
+m4_define([_CAIRO_MAKEFILE_CHECK],
+[dnl
+        m4_ifdef([cr_make_$1_dir_any],,[m4_fatal([Makefile `]$1[' not defined.])])dnl
+])dnl
+
+
+dnl
+dnl CAIRO_MAKEFILE_INCLUDE(TAG, FILE)
+dnl
+dnl Include FILE from Makefile's for TAG.  FILE should be placed
+dnl relative to directory for TAG. If TAG is *, FILE is included from
+dnl all Makefile's.
+dnl
+AC_DEFUN([CAIRO_MAKEFILE_INCLUDE],
+[dnl
+       m4_if([$1],[*],,[_CAIRO_MAKEFILE_CHECK([$1])])dnl
+       m4_foreach_w([cr_makefile], m4_if([$1],[*],_CAIRO_MAKEFILES,[$1]),
+       [dnl
+               m4_ifdef([cr_make_]cr_makefile[_dir_amake],dnl
+                        [CAIRO_ACCUMULATE([MAKEFILE_]cr_makefile[_AMAKE],[include $(top_srcdir)/cr_make_]cr_makefile[_dir_amake/$2]m4_newline)]
+               )dnl
+               m4_ifdef([cr_make_]cr_makefile[_dir_win32],dnl
+                        [CAIRO_ACCUMULATE([MAKEFILE_]cr_makefile[_WIN32],[ifeq ($(top_srcdir),)]m4_newline[include $2]m4_newline[else]m4_newline[include $(top_srcdir)/cr_make_]cr_makefile[_dir_win32/$2]m4_newline[endif]m4_newline)]
+               )dnl
+       ])dnl
+])dnl
+
+
+m4_pattern_allow([cr_make_tmp])
+
+dnl
+dnl CAIRO_MAKEFILE_ACCUMULATE(TAG, CONTENT)
+dnl
+dnl Accumulates CONTENT to Makefile's for TAG.  If TAG is *,
+dnl CONTENT is added to all Makefile's.
+dnl
+AC_DEFUN([CAIRO_MAKEFILE_ACCUMULATE],
+[dnl
+       m4_if([$1],[*],,[_CAIRO_MAKEFILE_CHECK([$1])])dnl
+       m4_foreach_w([cr_makefile], m4_if([$1],[*],_CAIRO_MAKEFILES,[$1]),
+       [dnl
+               m4_pushdef([cr_make_acc_contents],[$2])dnl
+               cr_make_tmp=_CAIRO_SH_ESCAPE(cr_make_acc_contents(cr_makefile))
+               m4_popdef([cr_make_acc_contents])dnl
+               m4_ifdef([cr_make_]cr_makefile[_dir_amake],dnl
+                        [CAIRO_ACCUMULATE_UNQUOTED_UNCHECKED([MAKEFILE_]cr_makefile[_AMAKE], [$cr_make_tmp])]
+               )dnl
+               m4_ifdef([cr_make_]cr_makefile[_dir_win32],dnl
+                        [CAIRO_ACCUMULATE_UNQUOTED_UNCHECKED([MAKEFILE_]cr_makefile[_WIN32], [$cr_make_tmp])]
+               )dnl
+       ])dnl
+])dnl
+
+m4_define([_CAIRO_MAKEFILE_ACCUMULATE_FEATURE],
+[dnl
+       dnl Don't do a conditional for default=always features
+       m4_pushdef([cr_mk_acc_feat_enabled],m4_if([$2],[yes],[m4_if(cr_feature_default,[always],[*],[$2])],[$2]))dnl
+       m4_case(cr_mk_acc_feat_enabled,
+               [*],,
+               [yes],  [CAIRO_ACCUMULATE([$1], [$3])],
+               [no],   [CAIRO_ACCUMULATE([$1], [$3]m4_newline[$4])],
+                       [m4_fatal([Invalid ENABLED value `]$2['])])dnl
+       CAIRO_ACCUMULATE_UNQUOTED_UNCHECKED([$1], [$6])dnl
+       m4_case(cr_mk_acc_feat_enabled,
+               [*],,
+               [yes],  [CAIRO_ACCUMULATE([$1], [$5])],
+               [no],   [CAIRO_ACCUMULATE([$1], [$5])],
+                       [m4_fatal([Invalid ENABLED value `]$2['])])dnl
+       m4_popdef([cr_mk_acc_feat_enabled])dnl
+])dnl
+
+dnl
+dnl CAIRO_MAKEFILE_ACCUMULATE_FEATURE(TAG, ENABLED, DEFAULT, WHAT, CONTENT)
+dnl
+dnl Accumulates CONTENT to Makefile's for TAG for each feature matching
+dnl ENABLED, DEFAULT, and WHAT.  Those parameters are similar to those
+dnl passed to CAIRO_FEATURE_HOOK_REGISTER.
+dnl If TAG is *, CONTENT is added to all Makefile's.
+dnl
+AC_DEFUN([CAIRO_MAKEFILE_ACCUMULATE_FEATURE],
+[dnl
+       m4_if([$1],[*],,[_CAIRO_MAKEFILE_CHECK([$1])])dnl
+       m4_append([cr_make_acc_counter],[1],[])dnl
+       m4_define([cr_make_acc_contents]m4_len(cr_make_acc_counter), [$5])dnl
+       CAIRO_FEATURE_HOOK_REGISTER(*,[$3],[$4],
+       [dnl
+               m4_foreach_w([cr_makefile], m4_if([$1],[*],_CAIRO_MAKEFILES,[$1]),
+               [dnl
+                       cr_make_tmp=_CAIRO_SH_ESCAPE(cr_make_acc_contents]]m4_len(cr_make_acc_counter)([[cr_makefile,]][$][1],[$][2],[$][3],[$][4])[[)
+                       m4_ifdef([cr_make_]cr_makefile[_dir_amake],
+                       [_CAIRO_MAKEFILE_ACCUMULATE_FEATURE(
+                               [MAKEFILE_]cr_makefile[_AMAKE],
+                               [$2],
+                               [if ]cr_feature_tag, [else], [endif],
+                               [$cr_make_tmp])
+                       ])dnl
+                       m4_ifdef([cr_make_]cr_makefile[_dir_win32],
+                       [_CAIRO_MAKEFILE_ACCUMULATE_FEATURE(
+                               [MAKEFILE_]cr_makefile[_WIN32],
+                               [$2],
+                               [ifeq ($(]cr_feature_tag[),1)], [else], [endif],
+                               [$cr_make_tmp])dnl
+                       ])dnl
+               ])dnl
+       ])dnl
+])dnl
+
+m4_define([cr_make_acc_counter])dnl
diff --git a/build/aclocal.pkg.m4 b/build/aclocal.pkg.m4
new file mode 100644 (file)
index 0000000..74306ce
--- /dev/null
@@ -0,0 +1,155 @@
+# pkg.m4 - Macros to locate and utilise pkg-config.            -*- Autoconf -*-
+# 
+# Copyright © 2004 Scott James Remnant <scott@netsplit.com>.
+#
+# 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 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, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# PKG_PROG_PKG_CONFIG([MIN-VERSION])
+# ----------------------------------
+AC_DEFUN([PKG_PROG_PKG_CONFIG],
+[m4_pattern_forbid([^_?PKG_[A-Z_]+$])
+m4_pattern_allow([^PKG_CONFIG(_PATH)?$])
+AC_ARG_VAR([PKG_CONFIG], [path to pkg-config utility])dnl
+if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then
+       AC_PATH_TOOL([PKG_CONFIG], [pkg-config])
+fi
+if test -n "$PKG_CONFIG"; then
+       _pkg_min_version=m4_default([$1], [0.9.0])
+       AC_MSG_CHECKING([pkg-config is at least version $_pkg_min_version])
+       if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then
+               AC_MSG_RESULT([yes])
+       else
+               AC_MSG_RESULT([no])
+               PKG_CONFIG=""
+       fi
+               
+fi[]dnl
+])# PKG_PROG_PKG_CONFIG
+
+# PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
+#
+# Check to see whether a particular set of modules exists.  Similar
+# to PKG_CHECK_MODULES(), but does not set variables or print errors.
+#
+#
+# Similar to PKG_CHECK_MODULES, make sure that the first instance of
+# this or PKG_CHECK_MODULES is called, or make sure to call
+# PKG_CHECK_EXISTS manually
+# --------------------------------------------------------------
+AC_DEFUN([PKG_CHECK_EXISTS],
+[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
+if test -n "$PKG_CONFIG" && \
+    AC_RUN_LOG([$PKG_CONFIG --exists --print-errors "$1"]); then
+  m4_ifval([$2], [$2], [:])
+m4_ifvaln([$3], [else
+  $3])dnl
+fi])
+
+
+# _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES])
+# ---------------------------------------------
+m4_define([_PKG_CONFIG],
+[if test -n "$$1"; then
+    pkg_cv_[]$1="$$1"
+ elif test -n "$PKG_CONFIG"; then
+    PKG_CHECK_EXISTS([$3],
+                     [pkg_cv_[]$1=`$PKG_CONFIG --[]$2 "$3" 2>/dev/null`],
+                    [pkg_failed=yes])
+ else
+    pkg_failed=untried
+fi[]dnl
+])# _PKG_CONFIG
+
+# _PKG_SHORT_ERRORS_SUPPORTED
+# -----------------------------
+AC_DEFUN([_PKG_SHORT_ERRORS_SUPPORTED],
+[AC_REQUIRE([PKG_PROG_PKG_CONFIG])
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+        _pkg_short_errors_supported=yes
+else
+        _pkg_short_errors_supported=no
+fi[]dnl
+])# _PKG_SHORT_ERRORS_SUPPORTED
+
+
+# PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND],
+# [ACTION-IF-NOT-FOUND])
+#
+#
+# Note that if there is a possibility the first call to
+# PKG_CHECK_MODULES might not happen, you should be sure to include an
+# explicit call to PKG_PROG_PKG_CONFIG in your configure.ac
+#
+#
+# --------------------------------------------------------------
+AC_DEFUN([PKG_CHECK_MODULES],
+[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
+AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $1, overriding pkg-config])dnl
+AC_ARG_VAR([$1][_LIBS], [linker flags for $1, overriding pkg-config])dnl
+
+pkg_failed=no
+AC_MSG_CHECKING([for $1])
+
+_PKG_CONFIG([$1][_CFLAGS], [cflags], [$2])
+_PKG_CONFIG([$1][_LIBS], [libs], [$2])
+
+m4_define([_PKG_TEXT], [Alternatively, you may set the environment variables $1[]_CFLAGS
+and $1[]_LIBS to avoid the need to call pkg-config.
+See the pkg-config man page for more details.])
+
+if test $pkg_failed = yes; then
+        _PKG_SHORT_ERRORS_SUPPORTED
+        if test $_pkg_short_errors_supported = yes; then
+               $1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors "$2" 2>&1`
+        else 
+               $1[]_PKG_ERRORS=`$PKG_CONFIG --print-errors "$2" 2>&1`
+        fi
+       # Put the nasty error message in config.log where it belongs
+       echo "$$1[]_PKG_ERRORS" >&AS_MESSAGE_LOG_FD
+
+       ifelse([$4], , [AC_MSG_ERROR(dnl
+[Package requirements ($2) were not met:
+
+$$1_PKG_ERRORS
+
+Consider adjusting the PKG_CONFIG_PATH environment variable if you
+installed software in a non-standard prefix.
+
+_PKG_TEXT
+])],
+               [AC_MSG_RESULT([no])
+                $4])
+elif test $pkg_failed = untried; then
+       ifelse([$4], , [AC_MSG_FAILURE(dnl
+[The pkg-config script could not be found or is too old.  Make sure it
+is in your PATH or set the PKG_CONFIG environment variable to the full
+path to pkg-config.
+
+_PKG_TEXT
+
+To get pkg-config, see <http://pkg-config.freedesktop.org/>.])],
+               [$4])
+else
+       $1[]_CFLAGS=$pkg_cv_[]$1[]_CFLAGS
+       $1[]_LIBS=$pkg_cv_[]$1[]_LIBS
+        AC_MSG_RESULT([yes])
+       ifelse([$3], , :, [$3])
+fi[]dnl
+])# PKG_CHECK_MODULES
diff --git a/build/configure.ac.analysis b/build/configure.ac.analysis
new file mode 100644 (file)
index 0000000..11c52e7
--- /dev/null
@@ -0,0 +1,106 @@
+dnl ===========================================================================
+dnl
+dnl LCOV
+dnl
+cairo_has_lcov=no
+AC_ARG_ENABLE(gcov,
+  AS_HELP_STRING([--enable-gcov],
+                [Enable gcov]),
+  [use_gcov=$enableval], [use_gcov=no])
+
+if test "x$use_gcov" = "xyes"; then
+  dnl we need gcc:
+  if test "$GCC" != "yes"; then
+    AC_MSG_ERROR([GCC is required for --enable-gcov])
+  fi
+
+  dnl Check if ccache is being used
+  AC_CHECK_PROG(SHTOOL, shtool, shtool)
+  case `$SHTOOL path $CC` in
+    *ccache*[)] gcc_ccache=yes;;
+    *[)] gcc_ccache=no;;
+  esac
+
+  if test "$gcc_ccache" = "yes" && (test -z "$CCACHE_DISABLE" || test "$CCACHE_DISABLE" != "1"); then
+    AC_MSG_ERROR([ccache must be disabled when --enable-gcov option is used. You can disable ccache by setting environment variable CCACHE_DISABLE=1.])
+  fi
+
+  ltp_version_list="1.7 1.6 1.5 1.4"
+  AC_CHECK_PROG(LTP, lcov, lcov)
+  AC_CHECK_PROG(LTP_GENHTML, genhtml, genhtml)
+
+  if test "$LTP"; then
+    AC_CACHE_CHECK([for ltp version], cairo_cv_ltp_version, [
+      cairo_cv_ltp_version=invalid
+      ltp_version=`$LTP -v 2>/dev/null | $SED -e 's/^.* //'`
+      for ltp_check_version in $ltp_version_list; do
+        if test "$ltp_version" = "$ltp_check_version"; then
+          cairo_cv_ltp_version="$ltp_check_version (ok)"
+        fi
+      done
+    ])
+  fi
+
+  case $cairo_cv_ltp_version in
+    ""|invalid[)]
+      ;;
+    *)
+      cairo_has_lcov=yes
+      ;;
+  esac
+
+  if test "x$cairo_has_lcov" != "xyes"; then
+    AC_MSG_ERROR([[To enable code coverage reporting you must have one of the following LTP versions installed: $ltp_version_list.
+Please install the Linux Test Project [http://ltp.sourceforge.net/], and try again.]])
+   fi
+
+  if test -z "$LTP_GENHTML"; then
+    AC_MSG_ERROR([[Could not find genhtml from the LTP package.
+Please install the Linux Test Project [http://ltp.sourceforge.net/], and try again.]])
+  fi
+
+  AC_DEFINE(HAVE_GCOV, 1, [Whether you have gcov])
+dnl  PHP_ADD_MAKEFILE_FRAGMENT($abs_srcdir/Makefile.gcov, $abs_srcdir)
+
+  dnl Remove all optimization flags from CFLAGS
+  changequote({,})
+  CFLAGS=`echo "$CFLAGS" | $SED -e 's/-O[0-9]*//g'`
+  CAIRO_CFLAGS=`echo "$CAIRO_CFLAGS" | $SED -e 's/-O[0-9]*//g'`
+  changequote([,])
+
+  dnl Add the special gcc flags
+  dnl In order to workaround a debian bug in libtool where they strip
+  dnl $dependency_libs from the link line and CFLAGS, we need to pass
+  dnl --coverage via LDFLAGS.
+  CAIRO_CC_TRY_FLAG([--coverage],,
+                   [
+                   CAIRO_CFLAGS="$CAIRO_CFLAGS -O0 --coverage"
+                   CAIRO_LDFLAGS="$CAIRO_LDFLAGS -O0 --coverage"
+                   ])
+fi
+AM_CONDITIONAL(CAIRO_HAS_LCOV, test "x$cairo_has_lcov" = "xyes")
+
+dnl ===========================================================================
+dnl Check for some custom valgrind modules
+AC_ARG_ENABLE(valgrind,
+  AS_HELP_STRING([--disable-valgrind],
+                [Disable valgrind support]),
+  [use_valgrind=$enableval], [use_valgrind=yes])
+
+if test "x$use_valgrind" = "xyes"; then
+    PKG_CHECK_MODULES(VALGRIND, valgrind, [
+           _save_CFLAGS="$CFLAGS"
+           _save_CPPFLAGS="$CPPFLAGS"
+           CFLAGS="$CFLAGS $VALGRIND_CFLAGS"
+           CPPFLAGS="$CPPFLAGS $VALGRIND_CFLAGS"
+           AC_CHECK_HEADER([valgrind.h], [AC_DEFINE([HAVE_VALGRIND], [1],
+                           [Define to 1 if you have Valgrind])])
+           AC_CHECK_HEADER([lockdep.h], [AC_DEFINE([HAVE_LOCKDEP], [1],
+                           [Define to 1 if you have the Valgrind lockdep tool])])
+           AC_CHECK_HEADER([memfault.h], [AC_DEFINE([HAVE_MEMFAULT], [1],
+                           [Define to 1 if you have the Valgrind memfault tool])])
+           CAIRO_CFLAGS="$VALGRIND_CFLAGS $CAIRO_CFLAGS"
+           CFLAGS="$_save_CFLAGS"
+           CPPFLAGS="$_save_CPPFLAGS"
+       ], AC_MSG_RESULT(no))
+fi
diff --git a/build/configure.ac.features b/build/configure.ac.features
new file mode 100644 (file)
index 0000000..faa762c
--- /dev/null
@@ -0,0 +1,426 @@
+
+dnl
+dnl Define macros to enable various features.
+dnl  - Macro: CAIRO_ENABLE_* (ID, NAME, DEFAULT, COMMANDS)
+dnl
+dnl where:
+dnl
+dnl    ID is the feature id, eg. "ft" for cairo_ft_...
+dnl    NAME is the human-readable name of the feature, eg. "FreeType"
+dnl    DEFAULT is the default state of the feature:
+dnl            "no" for experimental backends, eg. your favorite new backend
+dnl            "yes" for mandatory backends, eg. png
+dnl            "auto" for other supported backends, eg. xlib
+dnl    COMMANDS are run to check whether the feature can be enabled.  Their
+dnl            result may be cached, so user should not count on them being run.
+dnl            They should set use_$(ID) to something other than yes if the
+dnl            feature cannot be built, eg. "no (requires SomeThing)".  It then
+dnl            should also set $(ID)_REQUIRES/CFLAGS/LIBS/...
+dnl            appropriately.  Look at the macro definition for more details,
+dnl            or ask if in doubt.
+dnl
+
+AC_DEFUN([CAIRO_ENABLE],
+       [_CAIRO_ENABLE([$1],    [$2],                   ,               [$3],[$4])])dnl
+
+AC_DEFUN([CAIRO_ENABLE_SURFACE_BACKEND],
+       [_CAIRO_ENABLE([$1],    [$2 surface backend],   surface,        [$3],[$4])])dnl
+
+AC_DEFUN([CAIRO_ENABLE_FONT_BACKEND],
+       [_CAIRO_ENABLE([$1],    [$2 font backend],      font,           [$3],[$4])])dnl
+
+AC_DEFUN([CAIRO_ENABLE_FUNCTIONS],
+       [_CAIRO_ENABLE([$1],    [$2 functions],         functions,      [$3],[$4])])dnl
+
+
+dnl
+dnl Define cr_feature_pc and friends ala other cr_feature_* macros
+dnl
+m4_define([cr_pc_modname],
+       [[cairo-]m4_translit([$1],_,-)])dnl
+m4_define([cr_feature_pc],
+       [cr_pc_modname(cr_feature)[.pc]])dnl
+m4_define([cr_feature_uninstalled_pc],
+       [cr_pc_modname(cr_feature)[-uninstalled.pc]])dnl
+
+
+dnl ===========================================================================
+dnl
+dnl Hooks
+dnl
+dnl ===========================================================================
+
+
+dnl ===========================================================================
+dnl
+dnl Generate {src,boilerplate}/Makefile.{am,win32}.config
+dnl
+
+CAIRO_INIT_MAKEFILES([build])
+CAIRO_CONFIG_MAKEFILE([cairo], [src])dnl
+CAIRO_CONFIG_MAKEFILE([cairo_boilerplate], [boilerplate])dnl
+CAIRO_MAKEFILE_INCLUDE(*,[Makefile.sources])dnl
+dnl An empty line per feature for readability
+CAIRO_MAKEFILE_ACCUMULATE_FEATURE(*,*,*,*,[])dnl
+
+
+dnl Collect list of all supported public headers
+CAIRO_MAKEFILE_ACCUMULATE(*,
+[supported_$1_headers = $($1_headers)]dnl
+)dnl
+CAIRO_MAKEFILE_ACCUMULATE_FEATURE(*,*,!no,!,
+[supported_$1_headers += $($1_$2_headers)]dnl
+)dnl
+
+dnl Collect list of all unsupported public headers
+CAIRO_MAKEFILE_ACCUMULATE(*,
+[unsupported_$1_headers =]dnl
+)dnl
+CAIRO_MAKEFILE_ACCUMULATE_FEATURE(*,*,no,!,
+[unsupported_$1_headers += $($1_$2_headers)]dnl
+)dnl
+
+dnl Collect list of source files for all public features
+CAIRO_MAKEFILE_ACCUMULATE(*,
+[dnl
+all_$1_headers = $($1_headers)
+all_$1_private = $($1_private)
+all_$1_cxx_sources = $($1_cxx_sources)
+all_$1_sources = $($1_sources)
+])dnl
+CAIRO_MAKEFILE_ACCUMULATE_FEATURE(*,*,*,!,
+[dnl
+all_$1_headers += $($1_$2_headers)
+all_$1_private += $($1_$2_private)
+all_$1_cxx_sources += $($1_$2_cxx_sources)
+all_$1_sources += $($1_$2_sources)]dnl
+)dnl
+
+dnl Collect list of source files for enabled public features
+CAIRO_MAKEFILE_ACCUMULATE(*,
+[dnl
+enabled_$1_headers = $($1_headers)
+enabled_$1_private = $($1_private)
+enabled_$1_cxx_sources = $($1_cxx_sources)
+enabled_$1_sources = $($1_sources)
+])dnl
+CAIRO_MAKEFILE_ACCUMULATE_FEATURE(*,yes,*,!,
+[dnl
+enabled_$1_headers += $($1_$2_headers)
+enabled_$1_private += $($1_$2_private)
+enabled_$1_cxx_sources += $($1_$2_cxx_sources)
+enabled_$1_sources += $($1_$2_sources)]dnl
+)dnl
+
+dnl No public headers for private features
+
+dnl Collect list of source files for all private features
+CAIRO_MAKEFILE_ACCUMULATE_FEATURE(*,*,*,,
+[dnl
+all_$1_private += $($1_$2_private) $($1_$2_headers)
+all_$1_cxx_sources += $($1_$2_cxx_sources)
+all_$1_sources += $($1_$2_sources)]dnl
+)dnl
+
+dnl Collect list of source files for enabled private features
+CAIRO_MAKEFILE_ACCUMULATE_FEATURE(*,yes,*,,
+[dnl
+enabled_$1_private += $($1_$2_private) $($1_$2_headers)
+enabled_$1_cxx_sources += $($1_$2_cxx_sources)
+enabled_$1_sources += $($1_$2_sources)]dnl
+)dnl
+
+
+dnl ===========================================================================
+dnl
+dnl Generate .pc files
+dnl
+
+dnl All .pc files are generated automatically except for this one
+AC_CONFIG_FILES([src/cairo.pc])dnl
+AC_CONFIG_FILES([cairo-uninstalled.pc:src/cairo-uninstalled.pc.in])dnl
+
+dnl pkg-config requires, non-pkgconfig cflags and libs, and total cflags and libs
+CAIRO_FEATURE_VARS_REGISTER([BASE],[cairo])dnl
+CAIRO_ACCUMULATED_FEATURE_VARS_REGISTER([REQUIRES],,[ ])dnl
+CAIRO_ACCUMULATED_FEATURE_VARS_REGISTER([CFLAGS NONPKGCONFIG_CFLAGS],,[ ])dnl
+CAIRO_ACCUMULATED_FEATURE_VARS_REGISTER([LIBS NONPKGCONFIG_LIBS],,[ ],[$LIBS])dnl
+CAIRO_FEATURE_VARS_REGISTER([NONPKGCONFIG_EXTRA_LIBS])dnl
+AC_SUBST(CAIRO_REQUIRES)dnl
+AC_SUBST(CAIRO_CFLAGS)dnl
+AC_SUBST(CAIRO_LDFLAGS)dnl
+AC_SUBST(CAIRO_NONPKGCONFIG_CFLAGS)dnl
+AC_SUBST(CAIRO_LIBS)dnl
+AC_SUBST(CAIRO_NONPKGCONFIG_LIBS)dnl
+
+dnl add non-pkgconfig values
+AC_CONFIG_COMMANDS_PRE(
+[dnl
+CAIRO_CFLAGS="$CAIRO_CFLAGS $CAIRO_NONPKGCONFIG_CFLAGS"
+CAIRO_LIBS="$CAIRO_LIBS $CAIRO_NONPKGCONFIG_LIBS"
+])dnl
+
+m4_define([_CAIRO_FEATURE_CONFIG_PKGCONFIG_FILE],
+[dnl
+       AC_CONFIG_FILES([$3:$4],
+       [dnl
+               mv "$3" "$3.tmp" &&
+               $SED "dnl
+               s%@FEATURE_PC@%]cr_pc_modname([$1])[%g;dnl
+               s%@FEATURE_NAME@%$2%g;dnl
+               s%@FEATURE_BASE@%$$1_BASE%g;dnl
+               s%@FEATURE_REQUIRES@%$$1_REQUIRES%g;dnl
+               s%@FEATURE_NONPKGCONFIG_LIBS@%$$1_NONPKGCONFIG_LIBS%g;dnl
+               s%@FEATURE_NONPKGCONFIG_EXTRA_LIBS@%$$1_NONPKGCONFIG_EXTRA_LIBS%g;dnl
+               s%@FEATURE_NONPKGCONFIG_CFLAGS@%$$1_NONPKGCONFIG_CFLAGS%g;dnl
+               " < "$3.tmp" > "$3" && rm -f "$3.tmp" ||
+               AC_MSG_ERROR(failed to update $3)
+       ],[dnl
+               SED='$SED'
+               $1_BASE='$$1_BASE'
+               $1_REQUIRES='$$1_REQUIRES'
+               $1_NONPKGCONFIG_LIBS='$$1_NONPKGCONFIG_LIBS'
+               $1_NONPKGCONFIG_EXTRA_LIBS='$$1_NONPKGCONFIG_EXTRA_LIBS'
+               $1_NONPKGCONFIG_CFLAGS='$$1_NONPKGCONFIG_CFLAGS'
+       ])dnl
+])dnl
+
+dnl Generate .pc files for enabled non-builtin public features
+CAIRO_FEATURE_HOOK_REGISTER(yes,!always,!,
+[dnl
+       _CAIRO_FEATURE_CONFIG_PKGCONFIG_FILE(
+               [$1],
+               cr_feature_name,
+               [src/]cr_feature_pc,
+               [src/cairo-features.pc.in]
+       )dnl
+])dnl
+
+dnl Generate -uninstalled.pc files for enabled non-builtin public features
+CAIRO_FEATURE_HOOK_REGISTER(yes,!always,!,
+[dnl
+       _CAIRO_FEATURE_CONFIG_PKGCONFIG_FILE(
+               [$1],
+               cr_feature_name,
+               cr_feature_uninstalled_pc,
+               [src/cairo-features-uninstalled.pc.in]
+       )dnl
+])dnl
+
+
+dnl Collect list of .pc files for all non-builtin public features
+CAIRO_MAKEFILE_ACCUMULATE(cairo,
+[all_$1_pkgconf = cairo.pc])dnl
+CAIRO_MAKEFILE_ACCUMULATE_FEATURE(cairo,*,!always,!,
+[all_$1_pkgconf += cr_feature_pc])dnl
+
+dnl Collect list of .pc files for enabled non-builtin public features
+CAIRO_MAKEFILE_ACCUMULATE(cairo,
+[enabled_$1_pkgconf = cairo.pc])dnl
+CAIRO_MAKEFILE_ACCUMULATE_FEATURE(cairo,yes,!always,!,
+[enabled_$1_pkgconf += cr_feature_pc])dnl
+
+
+dnl ===========================================================================
+dnl
+dnl Generate src/cairo-features.h, src/cairo-supported-features.h, and
+dnl src/cairo-features-win32.h
+dnl
+
+dnl Collect list of enabled public features
+CAIRO_ACCUMULATORS_REGISTER(FEATURES,[ ])dnl
+CAIRO_FEATURE_HOOK_REGISTER(yes,*,!,dnl
+[dnl
+       CAIRO_ACCUMULATE(FEATURES, cr_feature_tag)dnl
+])dnl
+dnl Collect list of all supported public features
+CAIRO_ACCUMULATORS_REGISTER(SUPPORTED_FEATURES,[ ])dnl
+CAIRO_FEATURE_HOOK_REGISTER(*,!no,!,dnl
+[dnl
+       CAIRO_ACCUMULATE(SUPPORTED_FEATURES, cr_feature_tag)
+])dnl
+dnl Collect list of all supported disabled public features
+CAIRO_ACCUMULATORS_REGISTER(NO_FEATURES,[ ])dnl
+CAIRO_FEATURE_HOOK_REGISTER(no,!no,!,
+[dnl
+       CAIRO_ACCUMULATE(NO_FEATURES, cr_feature_tag)
+])dnl
+
+dnl Generate src/cairo-features.h
+CAIRO_CONFIG_COMMANDS([src/cairo-features.h],
+[dnl
+       echo '/* Generated by configure.  Do not edit. */'
+       echo '#ifndef CAIRO_FEATURES_H'
+       echo '#define CAIRO_FEATURES_H'
+       echo ''
+       for FEATURE in $CAIRO_FEATURES; do
+               echo "#define $FEATURE 1"
+       done | LANG=C sort
+       echo ''
+       for FEATURE in $CAIRO_NO_FEATURES; do
+               echo "/*#undef $FEATURE */"
+       done | LANG=C sort
+       echo ''
+       echo '#endif'
+],[dnl
+       CAIRO_FEATURES='$CAIRO_FEATURES'
+       CAIRO_NO_FEATURES='$CAIRO_NO_FEATURES'
+])dnl
+dnl Generate src/cairo-supported-features.h
+CAIRO_CONFIG_COMMANDS([src/cairo-supported-features.h],
+[dnl
+       echo '/* Generated by configure.  Do not edit. */'
+       echo '#ifndef CAIRO_SUPPORTED_FEATURES_H'
+       echo '#define CAIRO_SUPPORTED_FEATURES_H'
+       echo ''
+       echo '/* This is a dummy header, to trick gtk-doc only */'
+       echo ''
+       for FEATURE in $CAIRO_SUPPORTED_FEATURES; do
+               echo "#define $FEATURE 1"
+       done
+       echo ''
+       echo '#endif'
+],[dnl
+       CAIRO_SUPPORTED_FEATURES='$CAIRO_SUPPORTED_FEATURES'
+])dnl
+
+dnl For enabled private features just define them in config.h.  No fanfare!
+CAIRO_FEATURE_HOOK_REGISTER(yes,*,,
+[dnl
+       AC_DEFINE(cr_feature_tag, 1, [Define to 1 to enable cairo's ]cr_feature_name[ feature])
+])dnl
+
+
+dnl Generate build/Makefile.win32.features-h that generates src/cairo-features.h
+CAIRO_CONFIG_MAKEFILE_PRIVATE_WIN32([win32_features_h],[build],[features-h])
+dnl
+CAIRO_MAKEFILE_ACCUMULATE([win32_features_h],
+[$(top_srcdir)/src/cairo-features.h: $(top_srcdir)/build/Makefile.win32.features
+       @echo "Generating src/cairo-features.h"
+       @echo "/* Generated by Makefile.win32.features-h.  Do not edit. */" > $(top_srcdir)/src/cairo-features.h
+       @echo "[#]ifndef CAIRO_FEATURES_H" >> $(top_srcdir)/src/cairo-features.h
+       @echo "[#]define CAIRO_FEATURES_H 1" >> $(top_srcdir)/src/cairo-features.h]dnl
+)
+AC_CONFIG_COMMANDS_PRE(
+[dnl
+       CAIRO_MAKEFILE_ACCUMULATE([win32_features_h], [ @echo "[#]endif" >>  $(top_srcdir)/src/cairo-features.h])
+])dnl
+CAIRO_MAKEFILE_ACCUMULATE_FEATURE([win32_features_h],yes,*,*,dnl
+[      @echo "[#]define cr_feature_tag 1" >> $(top_srcdir)/src/cairo-features.h]dnl
+)dnl
+
+
+dnl ===========================================================================
+dnl
+dnl Report
+dnl
+
+CAIRO_ACCUMULATORS_REGISTER([WARNING_MESSAGE],m4_newline()m4_newline)dnl
+
+dnl Collect warning message for enabled unsupported public features
+CAIRO_FEATURE_HOOK_REGISTER(yes,no,!,
+[dnl
+       CAIRO_ACCUMULATE([WARNING_MESSAGE], CAIRO_TEXT_WRAP([The ]cr_feature_name[ feature is still under active development and is included in this release only as a preview. It does NOT fully work yet and incompatible changes may yet be made to ]cr_feature_name[ specific API.], [--- ]))
+])dnl
+
+dnl Collect warning message for disabled recommended features
+CAIRO_FEATURE_HOOK_REGISTER(no,yes,*,
+[dnl
+       CAIRO_ACCUMULATE([WARNING_MESSAGE], CAIRO_TEXT_WRAP([It is strongly recommended that you do NOT disable the ]cr_feature_name[ feature.], [+++ ]))
+])dnl
+
+
+dnl Collect enabled native surface/font backend features
+CAIRO_ACCUMULATORS_REGISTER([NATIVE_SURFACE_BACKENDS])dnl
+CAIRO_ACCUMULATORS_REGISTER([NATIVE_FONT_BACKENDS])dnl
+CAIRO_FEATURE_HOOK_REGISTER(yes,auto,surface,
+[dnl
+       CAIRO_ACCUMULATE([NATIVE_SURFACE_BACKENDS], [$1])
+])dnl
+CAIRO_FEATURE_HOOK_REGISTER(yes,auto,font,
+[dnl
+       CAIRO_ACCUMULATE([NATIVE_FONT_BACKENDS], [$1])
+])dnl
+
+dnl Collect warning message if no native surface/font backend feature enabled
+AC_CONFIG_COMMANDS_PRE(dnl
+[dnl
+       AS_IF([test -z "$CAIRO_NATIVE_SURFACE_BACKENDS"],dnl
+       [dnl
+               CAIRO_ACCUMULATE([WARNING_MESSAGE], CAIRO_TEXT_WRAP([No native surface backends enabled for your platform. It is strongly recommended that you enable the native surface backend feature for your platform.], [*** ]))
+       ])
+       AS_IF([test -z "$CAIRO_NATIVE_FONT_BACKENDS"],dnl
+       [dnl
+               CAIRO_ACCUMULATE([WARNING_MESSAGE], CAIRO_TEXT_WRAP([No native font backends enabled for your platform. It is strongly recommended that you enable the native font backend feature for your platform.], [*** ]))
+       ])
+])dnl
+
+
+AC_DEFUN([CAIRO_REPORT],
+[dnl
+       V="$CAIRO_VERSION_MAJOR.$CAIRO_VERSION_MINOR.$CAIRO_VERSION_MICRO"
+       echo ""
+       echo "cairo (version $V [[$CAIRO_RELEASE_STATUS]]) will be compiled with:"
+       echo ""
+       echo "The following surface backends:"
+       echo "  Image:         yes (always builtin)"
+       echo "  Recording:     yes (always builtin)"
+       echo "  Observer:      yes (always builtin)"
+       echo "  Mime:          yes (always builtin)"
+       echo "  Tee:           $use_tee"
+       echo "  XML:           $use_xml"
+       echo "  Skia:          $use_skia"
+       echo "  Xlib:          $use_xlib"
+       echo "  Xlib Xrender:  $use_xlib_xrender"
+       echo "  Qt:            $use_qt"
+       echo "  Quartz:        $use_quartz"
+       echo "  Quartz-image:  $use_quartz_image"
+       echo "  XCB:           $use_xcb"
+       echo "  Win32:         $use_win32"
+       echo "  OS2:           $use_os2"
+       echo "  CairoScript:   $use_script"
+       echo "  PostScript:    $use_ps"
+       echo "  PDF:           $use_pdf"
+       echo "  SVG:           $use_svg"
+       echo "  OpenGL:        $use_gl"
+       echo "  OpenGL ES 2.0: $use_glesv2"
+       echo "  BeOS:          $use_beos"
+       echo "  DirectFB:      $use_directfb"
+       echo "  OpenVG:        $use_vg"
+       echo "  DRM:           $use_drm"
+       echo ""
+       echo "The following font backends:"
+       echo "  User:          yes (always builtin)"
+       echo "  FreeType:      $use_ft"
+       echo "  Fontconfig:    $use_fc"
+       echo "  Win32:         $use_win32_font"
+       echo "  Quartz:        $use_quartz_font"
+       echo ""
+       echo "The following functions:"
+       echo "  PNG functions:   $use_png"
+       echo "  GLX functions:   $use_glx"
+       echo "  WGL functions:   $use_wgl"
+       echo "  EGL functions:   $use_egl"
+       echo "  X11-xcb functions: $use_xlib_xcb"
+       echo "  XCB-shm functions: $use_xcb_shm"
+       echo ""
+       echo "The following features and utilities:"
+       echo "  cairo-trace:                $use_trace"
+       echo "  cairo-script-interpreter:   $use_interpreter"
+       echo ""
+       echo "And the following internal features:"
+       echo "  pthread:       $use_pthread"
+       echo "  gtk-doc:       $enable_gtk_doc"
+       echo "  gcov support:  $use_gcov"
+       echo "  symbol-lookup: $use_symbol_lookup"
+       echo "  test surfaces: $use_test_surfaces"
+       echo "  ps testing:    $test_ps"
+       echo "  pdf testing:   $test_pdf"
+       echo "  svg testing:   $test_svg"
+       if test x"$use_win32" = "xyes"; then
+               echo "  win32 printing testing:    $test_win32_printing"
+       fi
+       echo "$CAIRO_WARNING_MESSAGE"
+       echo ""
+])dnl
+
diff --git a/build/configure.ac.noversion b/build/configure.ac.noversion
new file mode 100644 (file)
index 0000000..18c4bd5
--- /dev/null
@@ -0,0 +1,23 @@
+dnl
+dnl Version stuff
+dnl
+
+dnl Disable autoconf's version macros.  We try hard to not rebuild the entire
+dnl library just because version changed.  The PACKAGE_VERSION* stuff in
+dnl config.h is negating all the effort.
+dnl
+dnl We're not actually supposed to be doing this, and indeed adding the
+dnl AC_DEFINEs below causes confdefs.h to contain duplicate incompatible
+dnl #defines for the same PACKAGE_* symbols.  Those are provoking warnings
+dnl from the compiler, and that throws our CAIRO_TRY_LINK_*_ checks off,
+dnl because they think that there's something wrong with some flag they're
+dnl testing rather than confdefs.h!  So let's do the gross thing and puke
+dnl into confdefs.h some #undefs.
+echo '#undef PACKAGE_VERSION' >>confdefs.h
+echo '#undef PACKAGE_STRING' >>confdefs.h
+echo '#undef PACKAGE_NAME' >>confdefs.h
+echo '#undef PACKAGE_TARNAME' >>confdefs.h
+AC_DEFINE(PACKAGE_VERSION,     [USE_cairo_version_OR_cairo_version_string_INSTEAD])
+AC_DEFINE(PACKAGE_STRING,      [USE_cairo_version_OR_cairo_version_string_INSTEAD])
+AC_DEFINE(PACKAGE_NAME,                [USE_cairo_INSTEAD])
+AC_DEFINE(PACKAGE_TARNAME,     [USE_cairo_INSTEAD])
diff --git a/build/configure.ac.pthread b/build/configure.ac.pthread
new file mode 100644 (file)
index 0000000..29c930d
--- /dev/null
@@ -0,0 +1,253 @@
+dnl Defines the macro CAIRO_CONFIGURE_PTHREAD to find a suitable
+dnl pthread implementation. There are two levels of pthread conformance
+dnl we are looking for:
+dnl
+dnl a) A minimal level denoted by -DCAIRO_HAS_PTHREAD=1: This level
+dnl requires mutex and recursive mutexattr support.  If possible we try
+dnl to use weakly linked stubs from libc over the real pthread library.
+dnl This level is required by the cairo library proper.  If the user
+dnl invokes configure with --enable-pthread=yes or
+dnl --enable-pthread=always then we avoid trying to use weak stubs.
+dnl
+dnl b) A full level denoted by -DCAIRO_HAS_REAL_PTHREAD=1: This level
+dnl requires full support from a real pthread library, including thread
+dnl creation, joins, thread attribtues, etc.  This level is required by
+dnl multithreaded applications using cairo, such as the test suite
+dnl binaries and cairo utilities.
+dnl
+dnl Usage:
+dnl    CAIRO_ENABLE(pthread, pthread, <default yes|no|auto|always>,
+dnl                    [CAIRO_CONFIGURE_PTHREAD])
+dnl
+dnl    This should be invoked near the end of configure.ac so that
+dnl    the pthread specific CFLAGS and LIBS end up at the front
+dnl    of CAIRO_CFLAGS and CAIRO_LIBS -- this helps ensure that we
+dnl    really do get non-weak symbols from the actual pthread library
+dnl    rather than possible stubs in other libraries.
+dnl
+dnl    The user can override the choices made by
+dnl    CAIRO_CONFIGURE_PTHREAD by using --enable-pthread=yes and
+dnl    giving PTHREAD_CFLAGS and PTHREAD_LIBS to configure.
+dnl
+dnl Sets environment variables:
+dnl    use_pthread="yes" | "no (<errmsg>)"
+dnl    have_pthread="yes" | "no (<errmsg)"
+dnl    have_real_pthread="yes" | "no (<errmsg)"
+dnl    pthread_{CFLAGS,LIBS,REQUIRES}
+dnl    real_pthread_{CFLAGS,LIBS}
+dnl
+dnl Autoconfigured defines in config.h (conditional):
+dnl    CAIRO_HAS_PTHREAD
+dnl    CAIRO_HAS_REAL_PTHREAD
+dnl
+
+dnl -----------------------------------------------------------------------
+dnl A program to test all the pthread features we need to be able to
+dnl compile libcairo itself.  We could test the features independently,
+dnl but we need all of them anyway.
+m4_define([libcairo_pthread_program],[dnl
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE /* for PTHREAD_MUTEX_INITIALIZER under linux */
+#endif
+#include <pthread.h>
+
+pthread_mutex_t test_mutex_initializer = PTHREAD_MUTEX_INITIALIZER;
+int test_mutex (void)
+{
+       int x = 0;
+       pthread_mutex_t mutex;
+       x |= pthread_mutex_init (&mutex, NULL);
+       x |= pthread_mutex_lock (&mutex);
+       x |= pthread_mutex_unlock (&mutex);
+       x |= pthread_mutex_destroy (&mutex);
+       return 0;
+}
+
+int test_mutex_attr (void)
+{
+       int x = 0;
+       pthread_mutexattr_t attr;
+       pthread_mutex_t mutex;
+       x |= pthread_mutexattr_init (&attr);
+       x |= pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_RECURSIVE);
+       x |= pthread_mutex_init (&mutex, &attr);
+       x |= pthread_mutex_lock (&mutex);
+       x |= pthread_mutex_unlock (&mutex);
+       x |= pthread_mutex_destroy (&mutex);
+       x |= pthread_mutexattr_destroy (&attr);
+       return x;
+}])
+
+dnl -----------------------------------------------------------------------
+dnl A program to test all the features we want to be able to run the test
+dnl suite or other thready cairo applications that want real threads.
+m4_define([testsuite_pthread_program],[dnl
+libcairo_pthread_program
+
+pthread_once_t once_control = PTHREAD_ONCE_INIT;
+void test_once_init (void) {}
+int test_once (void)
+{
+       return pthread_once (&once_control, test_once_init);
+}
+
+pthread_key_t test_specific_key;
+int test_specific (void)
+{
+       int x = 0;
+       x |= pthread_key_create (&test_specific_key, NULL);
+       x |= pthread_setspecific (test_specific_key, NULL);
+       x |= pthread_getspecific (test_specific_key) != NULL;
+       return x;
+}
+
+void cleaner (void *arg) { (void)arg; }
+
+void *
+test_thread_main (void *arg)
+{
+       pthread_cleanup_push (cleaner, arg);
+       pthread_exit (arg);
+       pthread_cleanup_pop (1);
+       return arg;
+}
+
+int
+test_threads (void)
+{
+       int x = 0;
+       pthread_t thread;
+       pthread_attr_t attr;
+       void *arg = NULL;
+       x |= pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
+       x |= pthread_create (&thread, &attr, test_thread_main, arg);
+       x |= pthread_equal (pthread_self(), thread);
+       x |= pthread_join (thread, &arg);
+       x |= pthread_attr_destroy (&attr);
+       return x;
+}])
+
+dnl -----------------------------------------------------------------------
+
+dnl CAIRO_CHECK_PTHREAD(tag, cflags, libs, program, true-action, false-action)
+dnl   Set <tag>_{CFLAGS,LIBS} to {<cflags>,<libs>} if we can compile and link
+dnl   <program> with the given flags and libs.  Execute <true-action> on
+dnl   success and <false-action> on failure.
+AC_DEFUN([CAIRO_CHECK_PTHREAD],[dnl
+       CAIRO_CC_TRY_LINK_WITH_ENV_SILENT(
+               [CFLAGS="$CFLAGS $2";
+                LIBS="$LIBS $3"],
+               [$4],
+               [$1_CFLAGS="$2";
+                $1_LIBS="$3";
+                $5],
+               [$1_CFLAGS="";
+                $1_LIBS="";
+                $6])
+])
+
+dnl CAIRO_CONFIGURE_PTHREADS(): Look for pthreads.
+dnl
+dnl If the user specifies PTHREAD_CFLAGS and PTHREAD_LIBS then we use
+dnl those.  Otherwise we try CFLAGS=-D_REENTRANT and LIBS=-lpthread for
+dnl full pthread support, and look for stubs in libc for the minimal
+dnl pthread support.
+dnl
+dnl CFLAGS=-D_REENTRANT LIBS=-lpthread has been tested to work on:
+dnl
+dnl    Solaris 9 (5.9)                 Sun C 5.8 Patch 121015-04 2007/01/10
+dnl    OpenSolaris (5.11)              Sun C 5.9 Patch 124868-08 2008/11/25
+dnl    OpenSolaris (5.11)              clang version 1.1 (trunk 90017)
+dnl    Tru64/OSF1 V5.1                 Compaq C V6.5-003
+dnl    Mac OS X 10.5.5                 gcc 4.0.1 (Apple Inc. build 5465)
+dnl    Mac OS X 10.6                   gcc 4.2.1 (Apple Inc. build 5659)
+dnl    FreeBSD 7.2                     gcc 4.2
+dnl    OpenBSD 4.5                     gcc 3.3.5 (propolice)
+dnl    Debian Linux (Etch)             gcc 4.3
+dnl
+dnl Thread support is also in various libcs directly, so often using no
+dnl flags at all works as well, but unfortunately Solaris 9 has
+dnl practically _all_ of libpthread stubbed out in libc, so we cannot
+dnl distinguish between a working libpthread and a stubbed out one by a
+dnl link-only test.
+dnl
+dnl We also explicitly do not link to pthread-stubs or whatever other
+dnl third-party stubs library, since that forces cairo clients to be
+dnl extra careful when giving both libcairo and libpthread on the
+dnl command line: the user would have to use "-lpthread -lcairo" rather
+dnl than the more common "-lcairo -lpthread" to not accidentally use
+dnl stubs pulled in by libcairo everywhere in the application.  We
+dnl might also need to have a way to teach pkg-config about library
+dnl ordering constraints which aren't actual dependencies, and at this
+dnl point it just starts doing my head in.
+dnl
+dnl If your unix-like doesn't work with the secret handshake
+dnl -D_REENTRANT -lpthread and you can actually compile the rest of
+dnl cairo just fine otherwise, please take a moment complain loudly
+dnl to the cairo mailing list!
+dnl
+AC_DEFUN([CAIRO_CONFIGURE_PTHREAD],[dnl
+       dnl Try to use the user's PTHREAD_LIBS/CFLAGS
+       dnl if they're available.
+       if test "x$PTHREAD_CFLAGS" = "x"; then
+               PTHREAD_CFLAGS="-D_REENTRANT"
+       fi
+       if test "x$PTHREAD_LIBS" = "x"; then
+               PTHREAD_LIBS="-lpthread"
+       fi
+
+       dnl First try to find the real pthreads.
+       CAIRO_CHECK_PTHREAD(
+               [real_pthread], [$PTHREAD_CFLAGS], [$PTHREAD_LIBS],
+               [testsuite_pthread_program],
+               [have_real_pthread=yes],
+               [have_real_pthread=no])
+       if test "x$have_real_pthread" != "xyes"; then
+               dnl Give -pthread a go.
+               CAIRO_CHECK_PTHREAD(
+                       [real_pthread], [-pthread], [],
+                       [testsuite_pthread_program],
+                       [have_real_pthread=yes],
+                       [have_real_pthread="no (can't link with -lpthread or -pthread)"])
+       fi
+       PTHREAD_CFLAGS=
+       PTHREAD_LIBS=
+
+       dnl Check if we can use libc's stubs in libcairo.
+       dnl Only do this if the user hasn't explicitly enabled
+       dnl pthreads, but is relying on automatic configuration.
+       have_pthread="no"
+       if test "x$enable_pthread" != "xyes"; then
+               CAIRO_CHECK_PTHREAD(
+                       [pthread], [-D_REENTRANT], [],
+                       [libcairo_pthread_program],
+                       [have_pthread=yes],
+                       [])
+       fi
+
+       dnl Default to using the real pthreads for libcairo.
+       if test "x$have_pthread" != "xyes"; then
+               have_pthread="$have_real_pthread";
+               pthread_CFLAGS="$real_pthread_CFLAGS";
+               pthread_LIBS="$real_pthread_LIBS";
+       fi
+
+       dnl Tell autoconf about the results.
+       if test "x$have_real_pthread" = "xyes"; then
+                AC_DEFINE([CAIRO_HAS_REAL_PTHREAD], 1, 
+                       [Define to 1 if we have full pthread support])
+       fi
+       if test "x$have_pthread" = "xyes"; then
+               AC_DEFINE([CAIRO_HAS_PTHREAD], 1,
+                       [Define to 1 f we have minimal pthread support])
+       fi
+
+       dnl Make sure we scored some pthreads.
+       if test "x$enable_pthread" = "xyes" -a "x$have_pthread" != "xyes"; then
+               AC_MSG_ERROR([pthread requested but not found])
+       fi
+
+       dnl Set the output variables for CAIRO_ENABLE.
+       use_pthread="$have_pthread"
+       pthread_REQUIRES=""
+])
diff --git a/build/configure.ac.system b/build/configure.ac.system
new file mode 100644 (file)
index 0000000..b9d71c8
--- /dev/null
@@ -0,0 +1,160 @@
+dnl
+dnl Non-failing checks for functions, headers, libraries, etc go here
+dnl
+
+dnl ====================================================================
+dnl Feature checks
+dnl ====================================================================
+
+AM_CONDITIONAL(CROSS_COMPILING, test "x$cross_compiling" = "xyes")
+CAIRO_BIGENDIAN
+AC_ARG_ENABLE(atomic,
+             [AS_HELP_STRING([--disable-atomic],
+                             [disable use of native atomic operations])],
+             [use_atomic=$enableval], [use_atomic=yes])
+AS_IF([test "x$use_atomic" = "xyes"], [
+  CAIRO_CHECK_NATIVE_ATOMIC_PRIMITIVES
+  CAIRO_CHECK_ATOMIC_OP_NEEDS_MEMORY_BARRIER
+])
+AC_CHECK_SIZEOF(void *)
+AC_CHECK_SIZEOF(int)
+AC_CHECK_SIZEOF(long)
+AC_CHECK_SIZEOF(long long)
+AC_CHECK_SIZEOF(size_t)
+
+AC_MSG_CHECKING([for native Win32])
+case "$host" in
+  *-*-mingw*)
+    cairo_os_win32=yes
+    ;;
+  *)
+    cairo_os_win32=no
+    ;;
+esac
+AC_MSG_RESULT([$cairo_os_win32])
+AM_CONDITIONAL(OS_WIN32, test "$cairo_os_win32" = "yes")
+
+AC_MSG_CHECKING([for Sun Solaris (non-POSIX ctime_r)])
+case "$host" in
+    *-*-solaris*)
+       CFLAGS="$CFLAGS -D_POSIX_PTHREAD_SEMANTICS"
+       solaris_posix_pthread=yes
+       ;;
+    *)
+       solaris_posix_pthread=no
+       ;;
+esac
+AC_MSG_RESULT([$solaris_posix_pthread])
+
+dnl ====================================================================
+dnl Library checks
+dnl ====================================================================
+
+AC_CHECK_LIBM
+LIBS="$LIBS $LIBM"
+
+AC_CHECK_LIB(rt, sched_yield)
+
+has_shm_open=
+AC_CHECK_LIB(rt, shm_open, [
+            SHM_LIBS=-lrt
+            has_shm_open=yes
+            ], [SHM_LIBS=])
+AM_CONDITIONAL(HAVE_SHM, test "x$has_shm_open" = "xyes")
+AC_SUBST(SHM_LIBS)
+
+AC_CHECK_LIB(socket, connect, [SOCKET_LIBS=-lsocket], [SOCKET_LIBS=])
+CAIROBOILERPLATE_LIBS=$SOCKET_LIBS
+AC_SUBST(CAIROBOILERPLATE_LIBS)
+
+dnl ====================================================================
+dnl Header/function checks
+dnl ====================================================================
+
+dnl check if we have a __builtin_return_address for the cairo-trace
+dnl utility.
+AC_MSG_CHECKING([for __builtin_return_address(0)])
+AC_TRY_COMPILE([],[__builtin_return_address(0);],
+               [have_builtin_return_address=yes],
+               [have_builtin_return_address=no])
+AC_MSG_RESULT($have_builtin_return_address)
+if test "x$have_builtin_return_address" = "xyes"; then
+    AC_DEFINE(HAVE_BUILTIN_RETURN_ADDRESS, 1,
+       [Define to 1 if your compiler supports the __builtin_return_address() intrinsic.])
+fi
+
+dnl Checks for precise integer types
+AC_CHECK_HEADERS([stdint.h inttypes.h sys/int_types.h])
+AC_CHECK_TYPES([uint64_t, uint128_t, __uint128_t])
+
+dnl Check for socket support for any2ppm daemon
+AC_CHECK_HEADERS([fcntl.h unistd.h signal.h sys/stat.h sys/socket.h sys/poll.h sys/un.h])
+
+dnl Check for infinite loops
+AC_CHECK_FUNCS([alarm])
+
+dnl check for CPU affinity support
+AC_CHECK_HEADERS([sched.h], [AC_CHECK_FUNCS([sched_getaffinity])])
+
+dnl check for mmap support
+AC_CHECK_HEADERS([sys/mman.h], [AC_CHECK_FUNCS([mmap])])
+
+dnl check for clock_gettime() support
+AC_CHECK_HEADERS([time.h], [AC_CHECK_FUNCS([clock_gettime])])
+
+dnl check for GNU-extensions to fenv
+AC_CHECK_HEADER(fenv.h,
+       [AC_CHECK_FUNCS(feenableexcept fedisableexcept feclearexcept)])
+
+dnl check for misc headers and functions
+AC_CHECK_HEADERS([libgen.h byteswap.h signal.h setjmp.h fenv.h sys/wait.h])
+AC_CHECK_FUNCS([ctime_r drand48 flockfile funlockfile getline link strndup])
+
+dnl check for win32 headers (this detects mingw as well)
+AC_CHECK_HEADERS([windows.h], have_windows=yes, have_windows=no)
+
+
+dnl Possible headers for mkdir
+AC_CHECK_HEADERS([sys/stat.h io.h])
+AC_CHECK_FUNC(mkdir,
+             [AC_MSG_CHECKING([mkdir variant])
+             mkdir_variant="unknown"
+             save_CFLAGS="$CFLAGS"
+             CFLAGS=$WARN_CFLAGS
+             AC_TRY_COMPILE([
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_IO_H
+#include <io.h>
+#endif
+                     ],
+                     [mkdir ("hello.world", 0777)],
+                     mkdir_variant="mkdir(path, mode)",
+                     [AC_TRY_COMPILE([
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_IO_H
+#include <io.h>
+#endif
+                             ],
+                             [mkdir ("hello.world")],
+                             mkdir_variant="mkdir(path)")])
+             AC_MSG_RESULT([$mkdir_variant])
+             CFLAGS="$save_CFLAGS"
+             if test "x$mkdir_variant" = "xmkdir(path, mode)"; then
+                 AC_DEFINE(HAVE_MKDIR, 2,
+                           [Define to non-zero if your system has mkdir, and to 2 if your version of mkdir requires a mode parameter])
+             else
+                 AC_DEFINE(HAVE_MKDIR, 1,
+                           [Define to non-zero if your system has mkdir, and to 2 if your version of mkdir requires a mode parameter])
+             fi])
+
+dnl ===========================================================================
+dnl
+dnl Test for the tools required for building one big test binary
+dnl
+
+AC_CHECK_FUNCS(fork waitpid raise)
+
diff --git a/build/configure.ac.tools b/build/configure.ac.tools
new file mode 100644 (file)
index 0000000..a24dbce
--- /dev/null
@@ -0,0 +1,25 @@
+
+AC_PATH_PROG(FIND, find)
+AC_PATH_PROG(XARGS, xargs)
+
+AC_PROG_CC
+AC_PROG_CPP
+AC_PROG_CXX dnl required for BeOS (and cannot be a conditional dependency)
+AM_PROG_CC_C_O
+AC_C_INLINE
+
+dnl ===========================================================================
+
+PKG_PROG_PKG_CONFIG()
+if test "x$PKG_CONFIG" = x; then
+       AC_MSG_ERROR([pkg-config >= $PKGCONFIG_REQUIRED required but not found (http://pkgconfig.freedesktop.org/)])
+fi
+
+dnl Check for recent pkg-config which supports Requires.private
+case `$PKG_CONFIG --version` in
+[0.?|0.?.?|0.1[0-7]|0.1[0-7].?]) PKGCONFIG_REQUIRES="Requires"; ;;
+*) PKGCONFIG_REQUIRES="Requires.private"; ;;
+esac
+
+AC_SUBST(PKGCONFIG_REQUIRES)
+
diff --git a/build/configure.ac.version b/build/configure.ac.version
new file mode 100644 (file)
index 0000000..a91cee3
--- /dev/null
@@ -0,0 +1,42 @@
+dnl
+dnl Version stuff
+dnl
+
+dnl This macro expands to one of 'git', 'snapshot', or 'release'
+m4_define([cairo_release_status],
+         [m4_if(m4_eval(cairo_version_micro % 2), [1], [git],
+                [m4_if(m4_eval(cairo_version_minor % 2), [1], [snapshot],
+                                                              [release])])])
+
+dnl This is the .so/dll number.  2 for cairo-1.x.x
+m4_define([cairo_version_sonum], m4_eval(cairo_version_major + 1))
+
+dnl The libtool shared library version stuff
+m4_define([cairo_version],
+         m4_eval(cairo_version_major*10000 + cairo_version_minor*100 + cairo_version_micro))
+m4_if(m4_eval(cairo_version_minor % 2), [1],
+      [
+       dnl for unstable releases
+       m4_define([cairo_libtool_revision], 0)
+      ],
+      [
+       dnl for stable releases
+       m4_define([cairo_libtool_revision], cairo_version_micro)
+      ])
+m4_define([cairo_libtool_current],
+         m4_eval(cairo_version_sonum + cairo_version - cairo_libtool_revision))
+m4_define([cairo_libtool_age],
+         m4_eval(cairo_libtool_current - cairo_version_sonum))
+
+CAIRO_VERSION_MAJOR=cairo_version_major
+CAIRO_VERSION_MINOR=cairo_version_minor
+CAIRO_VERSION_MICRO=cairo_version_micro
+CAIRO_VERSION_SONUM=cairo_version_sonum
+CAIRO_RELEASE_STATUS=cairo_release_status
+CAIRO_LIBTOOL_VERSION_INFO=cairo_libtool_current:cairo_libtool_revision:cairo_libtool_age
+AC_SUBST(CAIRO_VERSION_MAJOR)
+AC_SUBST(CAIRO_VERSION_MINOR)
+AC_SUBST(CAIRO_VERSION_MICRO)
+AC_SUBST(CAIRO_VERSION_SONUM)
+AC_SUBST(CAIRO_RELEASE_STATUS)
+AC_SUBST(CAIRO_LIBTOOL_VERSION_INFO)
diff --git a/build/configure.ac.warnings b/build/configure.ac.warnings
new file mode 100644 (file)
index 0000000..3eb0104
--- /dev/null
@@ -0,0 +1,90 @@
+dnl Use lots of warning flags with with gcc and compatible compilers
+
+dnl Note: if you change the following variable, the cache is automatically
+dnl skipped and all flags rechecked.  So there's no need to do anything
+dnl else.  If for any reason you need to force a recheck, just change
+dnl MAYBE_WARN in an ignorable way (like adding whitespace)
+
+# -Wcast-align generates lots of false positive reports we need to
+# cast image data from uint8_t to uin32_t.
+
+# -Wlogical-op causes too much noise from strcmp("literal", str)
+
+MAYBE_WARN="-Wall -Wextra \
+-Wold-style-definition -Wdeclaration-after-statement \
+-Wmissing-declarations -Werror-implicit-function-declaration \
+-Wnested-externs -Wpointer-arith -Wwrite-strings \
+-Wsign-compare -Wstrict-prototypes -Wmissing-prototypes \
+-Wpacked -Wswitch-enum -Wmissing-format-attribute \
+-Wbad-function-cast -Wvolatile-register-var \
+-Wstrict-aliasing=2 -Winit-self -Wunsafe-loop-optimizations \
+-Wno-missing-field-initializers -Wno-unused-parameter \
+-Wno-attributes -Wno-long-long -Winline"
+
+dnl Sun Studio 12 likes to rag at us for abusing enums like
+dnl having cairo_status_t variables hold cairo_int_status_t
+dnl values.  It's bad, we know.  Now please be quiet.
+MAYBE_WARN="$MAYBE_WARN -erroff=E_ENUM_TYPE_MISMATCH_ARG \
+                       -erroff=E_ENUM_TYPE_MISMATCH_OP"
+
+dnl We also abuse the warning-flag facility to enable other compiler
+dnl options.  Namely, the following:
+MAYBE_WARN="$MAYBE_WARN -fno-strict-aliasing -fno-common -flto"
+
+dnl Also to turn various gcc/glibc-specific preprocessor checks
+MAYBE_WARN="$MAYBE_WARN -Wp,-D_FORTIFY_SOURCE=2"
+
+# invalidate cached value if MAYBE_WARN has changed
+if test "x$cairo_cv_warn_maybe" != "x$MAYBE_WARN"; then
+       unset cairo_cv_warn_cflags
+fi
+AC_CACHE_CHECK([for supported warning flags], cairo_cv_warn_cflags, [
+       echo
+       WARN_CFLAGS=""
+
+       # Some warning options are not supported by all versions of
+       # gcc, so test all desired options against the current
+       # compiler.
+       #
+       # Note that there are some order dependencies
+       # here. Specifically, an option that disables a warning will
+       # have no net effect if a later option then enables that
+       # warnings, (perhaps implicitly). So we put some grouped
+       # options (-Wall and -Wextra) up front and the -Wno options
+       # last.
+
+       for W in $MAYBE_WARN; do
+               CAIRO_CC_TRY_FLAG([$W],, [WARN_CFLAGS="$WARN_CFLAGS $W"])
+       done
+
+       cairo_cv_warn_cflags=$WARN_CFLAGS
+       cairo_cv_warn_maybe=$MAYBE_WARN
+
+       AC_MSG_CHECKING([which warning flags were supported])
+])
+WARN_CFLAGS="$cairo_cv_warn_cflags"
+CAIRO_CFLAGS="$CAIRO_CFLAGS $WARN_CFLAGS"
+
+# We only wish to enable attribute(warn_unused_result) if we can prevent
+# gcc from generating thousands of warnings about the misapplication of the
+# attribute to void functions and variables.
+AC_CACHE_CHECK([how to enable unused result warnings], cairo_cv_warn_unused_result, [
+        AC_REQUIRE([AC_PROG_GREP])
+       cairo_cv_warn_unused_result=""
+       if echo $WARN_CFLAGS | $GREP -e '-Wno-attributes' >/dev/null; then
+           CAIRO_CC_TRY_FLAG_SILENT(
+                       [-Wno-attributes],
+                       [__attribute__((__warn_unused_result__)) void f (void) {}
+                        __attribute__((__warn_unused_result__)) int g;],
+                       [cairo_cv_warn_unused_result="__attribute__((__warn_unused_result__))"])
+       fi
+])
+AC_DEFINE_UNQUOTED([WARN_UNUSED_RESULT], [$cairo_cv_warn_unused_result],
+         [Define to the value your compiler uses to support the warn-unused-result attribute])
+
+dnl check linker flags
+AC_CACHE_CHECK([how to allow undefined symbols in shared libraries used by test suite], cairo_cv_test_undefined_ldflags,
+              [CAIRO_CC_TRY_FLAG([-Wl,--allow-shlib-undefined], [],
+                                 [cairo_cv_test_undefined_ldflags="-Wl,--allow-shlib-undefined]")])
+CAIRO_TEST_UNDEFINED_LDFLAGS="$cairo_cv_test_undefined_ldflags"
+AC_SUBST(CAIRO_TEST_UNDEFINED_LDFLAGS)
diff --git a/cairo-version.h b/cairo-version.h
new file mode 100644 (file)
index 0000000..da6a305
--- /dev/null
@@ -0,0 +1,8 @@
+#ifndef CAIRO_VERSION_H
+#define CAIRO_VERSION_H
+
+#define CAIRO_VERSION_MAJOR 1
+#define CAIRO_VERSION_MINOR 11
+#define CAIRO_VERSION_MICRO 3
+
+#endif
diff --git a/configure.ac b/configure.ac
new file mode 100644 (file)
index 0000000..f50e69e
--- /dev/null
@@ -0,0 +1,815 @@
+AC_PREREQ(2.59)
+CAIRO_PARSE_VERSION
+AC_INIT([cairo],
+       [cairo_version_major.cairo_version_minor.cairo_version_micro],
+       [http://bugs.freedesktop.org/enter_bug.cgi?product=cairo])
+AC_CONFIG_AUX_DIR(build)
+AC_CONFIG_MACRO_DIR(build)
+AC_GNU_SOURCE
+AC_USE_SYSTEM_EXTENSIONS
+AC_CONFIG_SRCDIR(src/cairo.h)
+AC_CONFIG_HEADERS(config.h)
+AM_INIT_AUTOMAKE([1.9.6 gnu -Wall no-define])
+m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
+AC_LIBTOOL_WIN32_DLL dnl Must be called before AC_PROG_LIBTOOL
+AC_PROG_LIBTOOL dnl ([1.4]) Don't remove!
+GTK_DOC_CHECK([1.6])
+
+AC_SYS_LARGEFILE
+
+dnl ===========================================================================
+dnl
+dnl The order of the includes here is rather important
+dnl
+m4_include(build/configure.ac.version) dnl macros setting up various version declares
+m4_include(build/configure.ac.tools)   dnl checks for tools we use
+m4_include(build/configure.ac.features)        dnl macros for backend/feature handling
+m4_include(build/configure.ac.warnings)        dnl checks for compiler warning
+m4_include(build/configure.ac.system)  dnl checks for system functions, headers, libs
+m4_include(build/configure.ac.analysis)        dnl checks for analysis tools (lcov, etc)
+m4_include(build/configure.ac.noversion) dnl disable builtin libtool versioning
+m4_include(build/configure.ac.pthread)  dnl checks for pthreads
+AC_CACHE_SAVE
+
+dnl ===========================================================================
+
+AC_CHECK_LIB(z, compress,
+        [AC_CHECK_HEADER(zlib.h, [
+         have_libz=yes
+         AC_DEFINE(HAVE_ZLIB, 1, [Define to 1 if you have zlib available])
+        ],
+        [have_libz="no (requires zlib http://www.gzip.org/zlib/)"])],
+        [have_libz="no (requires zlib http://www.gzip.org/zlib/)"])
+
+AC_CHECK_LIB(dl, dlsym,
+            [have_dlsym=yes; have_dl=yes],
+            [have_dlsym=no; have_dl=no])
+if test "x$have_dlsym" = "xno"; then
+   AC_CHECK_FUNC(dlsym, [have_dlsym=yes], [have_dlsym=no])
+fi
+AC_CHECK_HEADERS(dlfcn.h, [have_dlsym=yes], [have_dlsym=no])
+AM_CONDITIONAL(CAIRO_HAS_DL, test "x$have_dl" = "xyes")
+if test "x$have_dlsym" = "xyes"; then
+  AC_DEFINE([CAIRO_HAS_DLSYM], 1, [Define to 1 if dlsym is available])
+fi
+
+dnl ===========================================================================
+
+CAIRO_ENABLE_SURFACE_BACKEND(xlib, Xlib, auto, [
+  xlib_REQUIRES="x11"
+  PKG_CHECK_MODULES(xlib, $xlib_REQUIRES, ,
+                   [AC_MSG_RESULT(no)
+                    xlib_REQUIRES=""
+                    AC_PATH_XTRA
+                    if test "x$no_x" = xyes; then
+                      use_xlib="no (requires X development libraries)"
+                    else
+                      xlib_NONPKGCONFIG_LIBS="$X_PRE_LIBS $X_LIBS -lX11 $X_EXTRA_LIBS"
+                      xlib_NONPKGCONFIG_CFLAGS=$X_CFLAGS
+                    fi])
+])
+
+CAIRO_ENABLE_SURFACE_BACKEND(xlib_xrender, Xlib Xrender, auto, [
+  if test "x$use_xlib" != "xyes"; then
+    use_xlib_xrender="no (requires --enable-xlib)"
+  else
+    dnl Check for Xrender header files if the Xrender package is not installed:
+    xlib_xrender_BASE=cairo-xlib
+    xlib_xrender_REQUIRES="xrender >= 0.6"
+    PKG_CHECK_MODULES(xlib_xrender, $xlib_xrender_REQUIRES, ,
+                     [AC_MSG_RESULT(no)
+                      xlib_xrender_REQUIRES=""
+                      old_CPPFLAGS=$CPPFLAGS
+                      CPPFLAGS="$CPPFLAGS $xlib_CFLAGS $xlib_NONPKGCONFIG_CFLAGS"
+                      AC_CHECK_HEADER(X11/extensions/Xrender.h,
+                                      [xlib_xrender_NONPKGCONFIG_LIBS="-lXrender"],
+                                      [use_xlib_xrender="no (requires $xlib_xrender_REQUIRES http://freedesktop.org/Software/xlibs)"])
+                      CPPFLAGS=$old_CPPFLAGS
+                     ])
+
+    old_CFLAGS=$CFLAGS
+    old_LIBS=$LIBS
+    CFLAGS="$CFLAGS $xlib_CFLAGS $xlib_NONPKGCONFIG_CFLAGS $xlib_xrender_CFLAGS $xlib_xrender_NONPKGCONFIG_CFLAGS"
+    LIBS="$LIBS $xlib_LIBS $xlib_NONPKGCONFIG_LIBS $xlib_xrender_LIBS $xlib_xrender_NONPKGCONFIG_LIBS"
+    AC_CHECK_FUNCS([XRenderCreateLinearGradient XRenderCreateRadialGradient XRenderCreateConicalGradient])
+    CFLAGS=$old_CFLAGS
+    LIBS=$old_LIBS
+
+  fi
+])
+
+dnl ===========================================================================
+
+CAIRO_ENABLE_SURFACE_BACKEND(xcb, XCB, auto, [
+  xcb_REQUIRES="xcb >= 1.6 xcb-render >= 1.6"
+  PKG_CHECK_MODULES(xcb, $xcb_REQUIRES, ,
+                   [AC_MSG_RESULT(no)
+                   use_xcb="no (requires $xcb_REQUIRES http://xcb.freedesktop.org)"])
+])
+
+CAIRO_ENABLE_FUNCTIONS(xlib_xcb, Xlib/XCB, no, [
+  if test "x$use_xcb" = "xyes" -a "x$use_xlib" = "xyes"; then
+    xlib_xcb_REQUIRES="x11-xcb"
+    PKG_CHECK_MODULES(xlib_xcb, $xlib_xcb_REQUIRES, ,
+                     [AC_MSG_RESULT(no)
+                     use_xlib_xcb="no (requires $xlib_xcb_REQUIRES http://xcb.freedesktop.org)"])
+  else
+    use_xlib_xcb="no (requires both --enable-xlib and --enable-xcb)"
+  fi
+])
+
+CAIRO_ENABLE_FUNCTIONS(xcb_shm, XCB/SHM, auto, [
+  if test "x$use_xcb" = "xyes"; then
+      xcb_shm_REQUIRES="xcb-shm"
+      PKG_CHECK_MODULES(xcb_shm, $xcb_shm_REQUIRES, ,
+                       [AC_MSG_RESULT(no)
+                       use_xcb_shm="no (requires $xcb_shm http://xcb.freedesktop.org)"])
+  else
+    use_xcb_shm="no (requires --enable-xcb)"
+  fi
+])
+
+dnl ===========================================================================
+
+CAIRO_ENABLE_SURFACE_BACKEND(qt, Qt, no, [
+  qt_REQUIRES="QtGui >= 4.4.0"
+  PKG_CHECK_MODULES(qt, $qt_REQUIRES, ,
+                   [AC_MSG_RESULT(no)
+                    qt_REQUIRES=""
+                    use_qt="no (requires Qt4 development libraries)"
+                    ])
+])
+
+dnl ===========================================================================
+
+CAIRO_ENABLE_SURFACE_BACKEND(quartz, Quartz, auto, [
+  dnl There is no pkgconfig for quartz; lets do a header check
+  AC_CHECK_HEADER(ApplicationServices/ApplicationServices.h, , [use_quartz="no (requires ApplicationServices framework)"])
+  if test "x$use_quartz" != "xyes" ; then
+    dnl check for CoreGraphics as a separate framework
+    AC_CHECK_HEADER(CoreGraphics/CoreGraphics.h, , [use_quartz="no (requires CoreGraphics framework)"])
+    quartz_LIBS="-Xlinker -framework -Xlinker CoreGraphics"
+  else
+    quartz_LIBS="-Xlinker -framework -Xlinker ApplicationServices"
+  fi
+])
+
+CAIRO_ENABLE_FONT_BACKEND(quartz_font, Quartz, auto, [
+  use_quartz_font=$use_quartz
+])
+
+CAIRO_ENABLE_SURFACE_BACKEND(quartz_image, Quartz Image, no, [
+  use_quartz_image=$use_quartz
+])
+
+dnl ===========================================================================
+
+CAIRO_ENABLE_SURFACE_BACKEND(win32, Microsoft Windows, auto, [
+  if test "x$have_windows" != xyes; then
+    use_win32="no (requires a Win32 platform)"
+  fi
+  win32_LIBS="-lgdi32 -lmsimg32"
+])
+
+CAIRO_ENABLE_FONT_BACKEND(win32_font, Microsoft Windows, auto, [
+  use_win32_font=$use_win32
+])
+
+test_win32_printing=no
+if test "x$use_win32" = "xyes"; then
+  AC_CHECK_PROG(GS, gs, gs)
+  if test "$GS"; then
+    AC_DEFINE([CAIRO_CAN_TEST_WIN32_PRINTING_SURFACE], 1, [Define to 1 if the Win32 Printing backend can be tested (needs ghostscript)])
+    test_win32_printing="yes"
+  else
+    AC_MSG_WARN([Win32 Printing backend will not be tested since ghostscript is not available])
+    test_win32_printing="no (requires ghostscript)"
+  fi
+fi
+
+AM_CONDITIONAL(CAIRO_CAN_TEST_WIN32_PRINTING_SURFACE, test "x$test_win32_printing" = "xyes")
+
+dnl ===========================================================================
+
+CAIRO_ENABLE_SURFACE_BACKEND(skia, Skia, no, [
+  AC_ARG_WITH([skia],
+             [AS_HELP_STRING([--with-skia=/path/to/skia],
+                             [directory to find compiled skia sources])],
+             [skia_DIR="$withval"],
+             [skia_DIR="`pwd`/../skia"])
+  AC_ARG_WITH([skia-bulid],
+             [AS_HELP_STRING([--with-skia-build=(Release|Debug)]
+                             [build of skia to link with, default is Release])],
+             [skia_BUILD="$withval"],
+             [skia_BUILD="Release"])
+  skia_NONPKGCONFIG_CFLAGS="-I$skia_DIR/include/config -I$skia_DIR/include/core -I$skia_DIR/include/effects"
+  if test "x$skia_BUILD" = x"Release"; then
+       skia_NONPKGCONFIG_CFLAGS="-DSK_RELEASE -DSK_CAN_USE_FLOAT $skia_NONPKGCONFIG_CFLAGS"
+  fi
+  skia_NONPKGCONFIG_LIBS="--start-group $skia_DIR/out/$skia_BUILD/obj.target/gyp/libeffects.a $skia_DIR/out/$skia_BUILD/obj.target/gyp/libimages.a $skia_DIR/out/$skia_BUILD/obj.target/gyp/libutils.a $skia_DIR/out/$skia_BUILD/obj.target/gyp/libopts.a $skia_DIR/out/$skia_BUILD/obj.target/gyp/libcore.a -end-group"
+  AC_SUBST(skia_DIR)
+])
+
+dnl ===========================================================================
+
+CAIRO_ENABLE_SURFACE_BACKEND(os2, OS/2, no, [
+  case "$host" in
+    *-*-os2*)
+      :
+      ;;
+    *)
+      use_os2="no (requires an OS/2 platform)"
+      ;;
+  esac
+])
+
+dnl ===========================================================================
+
+CAIRO_ENABLE_SURFACE_BACKEND(beos, BeOS/Zeta, no, [
+  case "$host" in
+    *-*-beos)
+      beos_LIBS=""
+      dnl Add libbe and libzeta if available
+      AC_CHECK_LIB(be,main,beos_LIBS="$beos_LIBS -lbe")
+      AC_CHECK_LIB(zeta,main,beos_LIBS="$beos_LIBS -lzeta")
+      ;;
+    *)
+      use_beos="no (requires a BeOS platform)"
+      ;;
+  esac
+])
+
+dnl ===========================================================================
+
+CAIRO_ENABLE_SURFACE_BACKEND(drm, DRM, no, [
+  drm_REQUIRES="libudev >= 136"
+  PKG_CHECK_MODULES(drm, $drm_REQUIRES, , [AC_MSG_RESULT(no)
+  use_drm="no (requires $drm_REQUIRES, udev is available from git://git.kernel.org/pub/scm/linux/hotplug/udev.git)"])
+])
+
+CAIRO_ENABLE_SURFACE_BACKEND(gallium, Gallium3D, no, [
+  if test "x$use_drm" = "xyes"; then
+      AC_ARG_WITH([gallium],
+                 [AS_HELP_STRING([--with-gallium=/path/to/mesa],
+                                 [directory to find gallium enabled mesa])],
+                 [mesa_DIR="$withval"],
+                 [mesa_DIR="`pwd`/../mesa"])
+      gallium_DIR="$mesa_DIR/src/gallium"
+      gallium_NONPKGCONFIG_CFLAGS="-I$mesa_DIR/include -I$mesa_DIR/src/mesa -I$gallium_DIR/include -I$gallium_DIR/auxiliary"
+      gallium_NONPKGCONFIG_LIBS="-lGL"
+      AC_SUBST(mesa_DIR)
+      AC_SUBST(gallium_DIR)
+  else
+    use_gallium="no (requires --enable-drm)"
+  fi
+])
+
+dnl ===========================================================================
+
+CAIRO_ENABLE_FUNCTIONS(png, PNG, yes, [
+  use_png=no
+  AC_ARG_VAR([png_REQUIRES], [module name for libpng to search for using pkg-config])
+  if test "x$png_REQUIRES" = x; then
+    # libpng13 is GnuWin32's libpng-1.2.8 :-(
+    for l in libpng libpng14 libpng12 libpng13 libpng10; do
+      if $PKG_CONFIG --exists $l ; then
+        png_REQUIRES=$l
+        use_png=yes
+        break
+      fi
+    done
+  else
+    use_png=yes
+  fi
+
+  if test "x$use_png" = "xyes" ; then 
+    PKG_CHECK_MODULES(png, $png_REQUIRES, , AC_MSG_RESULT(no))
+  else
+    AC_MSG_WARN([Could not find libpng in the pkg-config search path])
+  fi    
+])
+
+dnl ===========================================================================
+CAIRO_ENABLE_SURFACE_BACKEND(gl, OpenGL, no, [
+  gl_REQUIRES="gl"
+  PKG_CHECK_MODULES(gl, $gl_REQUIRES,, [
+         dnl Fallback to searching for headers
+         AC_CHECK_HEADER(GL/gl.h,, [use_gl="no (gl.pc nor OpenGL headers not found)"])
+         if test "x$use_gl" = "xyes"; then
+             gl_NONPKGCONFIG_CFLAGS=
+             gl_NONPKGCONFIG_LIBS="-lGL"
+         fi])
+
+  if test "x$have_dl" = "xyes" -a "x$have_dlsym" = "xyes"; then
+    gl_LIBS="$gl_LIBS -ldl"
+  fi
+
+  need_glx_functions=yes
+  need_wgl_functions=yes
+  need_egl_functions=yes
+])
+
+dnl ===========================================================================
+CAIRO_ENABLE_SURFACE_BACKEND(glesv2, OpenGLESv2, no, [
+  glesv2_REQUIRES="glesv2"
+  PKG_CHECK_MODULES(glesv2, $glesv2_REQUIRES,, [
+         dnl Fallback to searching for headers
+         AC_CHECK_HEADER(GLES2/gl2.h,, [use_glesv2="no (glesv2.pc nor OpenGL ES 2.0 headers not found)"])
+         if test "x$use_glesv2" = "xyes"; then
+             glesv2_NONPKGCONFIG_CFLAGS=
+             glesv2_NONPKGCONFIG_LIBS="-lGLESv2"
+         fi])
+
+  if test "x$have_dl" = "xyes" -a "x$have_dlsym" = "xyes"; then
+    glesv2_LIBS="$glesv2_LIBS -ldl"
+  fi
+
+  need_egl_functions=yes
+])
+
+dnl ===========================================================================
+
+CAIRO_ENABLE_SURFACE_BACKEND(directfb, directfb, no, [
+  directfb_REQUIRES=directfb
+  PKG_CHECK_MODULES(directfb, $directfb_REQUIRES, , AC_MSG_RESULT(no)
+                   [use_directfb="no (requires $directfb_REQUIRES http://www.directfb.org)"])
+])
+
+dnl ===========================================================================
+
+CAIRO_ENABLE_SURFACE_BACKEND(vg, OpenVG, no, [
+  dnl There is no pkgconfig for OpenVG; lets do a header check
+  AC_CHECK_HEADER(VG/openvg.h,, [use_vg="no (OpenVG headers not found)"])
+  if test "x$use_vg" = "xyes"; then
+      vg_NONPKGCONFIG_CFLAGS=
+      vg_NONPKGCONFIG_LIBS="-lOpenVG"
+      need_egl_functions=yes
+      need_glx_functions=yes
+  fi
+])
+
+CAIRO_ENABLE_FUNCTIONS(egl, EGL, auto, [
+  if test "x$need_egl_functions" = "xyes"; then
+      egl_REQUIRES="egl"
+      PKG_CHECK_MODULES(egl, $egl_REQUIRES, ,
+                   [egl_REQUIRES=""
+                    AC_CHECK_HEADER(EGL/egl.h,, [use_egl="no (EGL headers not found)"])
+                    if test "x$use_egl" = "xyes"; then
+                        egl_NONPKGCONFIG_CFLAGS=
+                        egl_NONPKGCONFIG_LIBS=
+                        save_LIBS="$LIBS"
+                        other_egl_LIBS=""
+                        # Temporary workaround for missing link from egl13
+                        AC_CHECK_LIB(csi, csi_stream_attachresource, other_egl_LIBS="-lcsi")
+                        LIBS="$other_egl_LIBS $LIBS"
+                        for egl_lib in EGL egl13 egl12 egl11; do
+                            if test -z "$egl_NONPKGCONFIG_LIBS"; then
+                                AC_CHECK_LIB($egl_lib, eglGetError, egl_NONPKGCONFIG_LIBS="-l$egl_lib")
+                            fi
+                        done
+                        if test -z "$egl_NONPKGCONFIG_LIBS"; then
+                            use_egl="no (EGL library not found)"
+                        else
+                            egl_NONPKGCONFIG_LIBS="$egl_NONPKGCONFIG_LIBS $other_egl_LIBS"
+                        fi
+                        LIBS="$save_LIBS"
+                    fi
+       ])
+  else
+      use_egl="no (not required by any backend)"
+  fi
+])
+
+CAIRO_ENABLE_FUNCTIONS(glx, GLX, auto, [
+  if test "x$need_glx_functions" = "xyes"; then
+    save_CFLAGS="$CFLAGS"
+    CFLAGS="$CFLAGS $gl_CFLAGS $gl_NONPKGCONFIG_CFLAGS"
+    AC_CHECK_HEADER(GL/glx.h,, [use_glx="no (GLX headers not found)"])
+    glx_NONPKGCONFIG_CFLAGS=
+    glx_NONPKGCONFIG_LIBS="-lGL"
+    CFLAGS="$save_CFLAGS"
+  else
+      use_glx="no (not required by any backend)"
+  fi
+])
+
+CAIRO_ENABLE_FUNCTIONS(wgl, WGL, auto, [
+  if test "x$need_wgl_functions" = "xyes"; then
+    AC_CHECK_HEADER(windows.h,, [use_wgl="no (WGL headers not found)"])
+  else
+      use_wgl="no (not required by any backend)"
+  fi
+])
+
+dnl ===========================================================================
+
+any2ppm_cs=no
+CAIRO_ENABLE_SURFACE_BACKEND(script, script, yes, [
+  any2ppm_cs=yes
+])
+
+dnl ===========================================================================
+
+# We use pkg-config to look for freetype2, but fall back to
+# freetype-config if it fails.  We prefer pkg-config, since we can
+# then just put freetype2 >= $FREETYPE_MIN_VERSION in
+# Requires.private, but at least up to 2003-06-07, there was no
+# freetype2.pc in the release.
+#
+# FreeType versions come in three forms:
+#   release (such as 2.1.9)
+#   libtool (such as 9.7.3) (returned by freetype-config and pkg-config)
+#   platform-specific/soname (such as 6.3.4)
+# and they recommend you never use the platform-specific version
+# (see docs/VERSION.DLL in freetype2 sources)
+#
+# Set these as appropriate:
+
+# release number - for information only
+FREETYPE_MIN_RELEASE=2.1.9
+# libtool-specific version - this is what is checked
+FREETYPE_MIN_VERSION=9.7.3
+
+CAIRO_ENABLE_FONT_BACKEND(ft, FreeType, auto, [
+
+    PKG_CHECK_MODULES(FREETYPE, freetype2 >= $FREETYPE_MIN_VERSION,
+                      [freetype_pkgconfig=yes],
+                     [AC_MSG_RESULT(no)
+                      freetype_pkgconfig=no])
+  
+    if test "x$freetype_pkgconfig" = "xyes"; then
+      ft_REQUIRES="freetype2 >= $FREETYPE_MIN_VERSION $ft_REQUIRES"
+    else
+  
+      if test -z "$FREETYPE_CONFIG"; then
+        AC_PATH_PROG(FREETYPE_CONFIG, freetype-config, no)
+      fi
+      if test "x$FREETYPE_CONFIG" = "xno" ; then
+        use_ft='no (freetype-config not found in path or $FREETYPE_CONFIG)'
+      else
+        AC_MSG_CHECKING(freetype2 libtool version)
+
+        FREETYPE_VERSION=`$FREETYPE_CONFIG --version`
+       AX_COMPARE_VERSION([$FREETYPE_VERSION], [gt], [$FREETYPE_MIN_VERSION],
+                          [AC_MSG_RESULT($FREETYPE_VERSION - OK)
+                          ft_NONPKGCONFIG_CFLAGS=`$FREETYPE_CONFIG --cflags`
+                          ft_NONPKGCONFIG_LIBS=`$FREETYPE_CONFIG --libs`],
+                          [AC_MSG_RESULT($FREETYPE_VERSION - Too old)
+                          use_ft="no ($FREETYPE_VERSION found; version $FREETYPE_MIN_VERSION from release $FREETYPE_MIN_RELEASE required)"])
+      fi
+    fi
+
+  ft_CFLAGS="$FREETYPE_CFLAGS"
+  ft_LIBS="$FREETYPE_LIBS"
+])
+
+FONTCONFIG_MIN_VERSION=2.2.95
+CAIRO_ENABLE_FONT_BACKEND(fc, Fontconfig, auto, [
+  use_fc=$use_ft
+  if test "x$use_fc" = "xyes"; then
+    fc_REQUIRES="fontconfig >= $FONTCONFIG_MIN_VERSION"
+    PKG_CHECK_MODULES(FONTCONFIG, $fc_REQUIRES,,
+                     [AC_MSG_RESULT(no); use_fc="no (requires $fc_REQUIRES)"])
+  fi
+  fc_CFLAGS="$FONTCONFIG_CFLAGS"
+  fc_LIBS="$FONTCONFIG_LIBS"
+])
+
+if test "x$use_ft" = "xyes"; then
+  _save_libs="$LIBS"
+  _save_cflags="$CFLAGS"
+  LIBS="$LIBS $ft_LIBS"
+  CFLAGS="$CFLAGS $ft_CFLAGS"
+  AC_CHECK_MEMBER(FT_Bitmap_Size.y_ppem,
+                  HAVE_FT_BITMAP_SIZE_Y_PPEM=1,
+                  HAVE_FT_BITMAP_SIZE_Y_PPEM=0,
+                  [#include <ft2build.h>
+                   #include FT_FREETYPE_H])
+  AC_DEFINE_UNQUOTED(HAVE_FT_BITMAP_SIZE_Y_PPEM,$HAVE_FT_BITMAP_SIZE_Y_PPEM,
+                     [FT_Bitmap_Size structure includes y_ppem field])
+
+  AC_CHECK_FUNCS(FT_GlyphSlot_Embolden FT_Load_Sfnt_Table FT_Library_SetLcdFilter)
+
+  LIBS="$_save_libs"
+  CFLAGS="$_save_cflags"
+fi
+
+if test "x$use_fc" = "xyes"; then
+  CAIRO_CHECK_FUNCS_WITH_FLAGS(FcInit FcFini, [$FONTCONFIG_CFLAGS], [$FONTCONFIG_LIBS])
+fi
+
+dnl ===========================================================================
+
+CAIRO_ENABLE_SURFACE_BACKEND(ps, PostScript, yes, [
+    # The ps backend requires zlib.
+    use_ps=$have_libz
+    ps_NONPKGCONFIG_LIBS=-lz
+])
+
+dnl ===========================================================================
+
+SPECTRE_VERSION_REQUIRED=0.2.0
+test_ps=no
+any2ppm_ps=no
+if test "x$use_ps" = "xyes"; then
+  AC_CHECK_PROG(GS, gs, gs)
+  if test "$GS"; then
+    AC_DEFINE([CAIRO_CAN_TEST_PS_SURFACE], 1, [Define to 1 if the PS backend can be tested (needs ghostscript)])
+    test_ps="yes"
+  else
+    AC_MSG_WARN([PS backend will not be tested since ghostscript is not available])
+    test_ps="no (requires ghostscript)"
+  fi
+
+  libspectre_DEPENDENCY="libspectre >= $SPECTRE_VERSION_REQUIRED"
+  PKG_CHECK_MODULES(LIBSPECTRE, $libspectre_DEPENDENCY,
+                   [any2ppm_ps=yes],
+                   [test_ps="no (requires libspectre)"])
+fi
+
+AM_CONDITIONAL(CAIRO_CAN_TEST_PS_SURFACE, test "x$test_ps" = "xyes")
+AM_CONDITIONAL(CAIRO_HAS_SPECTRE, test "x$any2ppm_ps" = "xyes")
+if test "x$any2ppm_ps" = "xyes"; then
+    AC_DEFINE([CAIRO_HAS_SPECTRE], 1, [Define to 1 if libspectre is available])
+fi
+AC_SUBST(LIBSPECTRE_CFLAGS)
+AC_SUBST(LIBSPECTRE_LIBS)
+
+dnl ===========================================================================
+
+CAIRO_ENABLE_SURFACE_BACKEND(pdf, PDF, yes, [
+    # The pdf backend requires zlib.
+    use_pdf=$have_libz
+    pdf_NONPKGCONFIG_LIBS=-lz
+])
+
+dnl ===========================================================================
+
+# poppler-0.17.4 fixes text-pattern and text-transform
+POPPLER_VERSION_REQUIRED=0.17.4
+test_pdf=no
+any2ppm_pdf=no
+if test "x$use_pdf" = "xyes"; then
+  poppler_DEPENDENCY="poppler-glib >= $POPPLER_VERSION_REQUIRED"
+  PKG_CHECK_MODULES(POPPLER, $poppler_DEPENDENCY,
+                   [CAIRO_CHECK_FUNCS_WITH_FLAGS(poppler_page_render, [$POPPLER_CFLAGS], [$POPPLER_LIBS],
+                    [test_pdf=yes; any2ppm_pdf=yes],
+                   [test_pdf="no (requires $poppler_DEPENDENCY)"])],
+                   [test_pdf="no (requires $poppler_DEPENDENCY)"])
+  if test "x$test_pdf" = "xyes"; then
+    AC_DEFINE([CAIRO_CAN_TEST_PDF_SURFACE], 1, [Define to 1 if the PDF backend can be tested (need poppler and other dependencies for pdf2png)])
+  else
+    AC_MSG_WARN([PDF backend will not be tested since poppler >= $POPPLER_VERSION_REQUIRED is not available])
+  fi
+fi
+
+AM_CONDITIONAL(CAIRO_CAN_TEST_PDF_SURFACE, test "x$test_pdf" = "xyes")
+AC_SUBST(POPPLER_CFLAGS)
+AC_SUBST(POPPLER_LIBS)
+
+AM_CONDITIONAL(CAIRO_HAS_MULTI_PAGE_SURFACES, test "x$use_ps" = "xyes" -o "x$use_pdf" = "xyes")
+
+dnl ===========================================================================
+
+CAIRO_ENABLE_SURFACE_BACKEND(svg, SVG, yes, [
+  if test "x$use_png" != "xyes"; then
+    use_svg="no (requires --enable-png)"
+  fi
+])
+
+LIBRSVG_VERSION_REQUIRED=2.15.0
+test_svg=no
+any2ppm_svg=no
+if test "x$use_svg" = "xyes"; then
+  librsvg_DEPENDENCY="librsvg-2.0 >= $LIBRSVG_VERSION_REQUIRED"
+  PKG_CHECK_MODULES(LIBRSVG, $librsvg_DEPENDENCY gdk-2.0,
+                   [CAIRO_CHECK_FUNCS_WITH_FLAGS(rsvg_pixbuf_from_file, [$LIBRSVG_CFLAGS], [$LIBRSVG_LIBS],
+                    [test_svg=yes; any2ppm_svg=yes],
+                   [test_svg="no (requires $librsvg_DEPENDENCY)"])],
+                   [test_svg="no (requires $librsvg_DEPENDENCY)"])
+  if test "x$test_svg" = "xyes"; then
+    AC_DEFINE([CAIRO_CAN_TEST_SVG_SURFACE], 1, [Define to 1 if the SVG backend can be tested])
+  else
+    AC_MSG_WARN([SVG backend will not be tested since librsvg >= $LIBRSVG_VERSION_REQUIRED is not available])
+  fi
+fi
+
+AM_CONDITIONAL(CAIRO_CAN_TEST_SVG_SURFACE, test "x$test_svg" = "xyes")
+AC_SUBST(LIBRSVG_CFLAGS)
+AC_SUBST(LIBRSVG_LIBS)
+
+dnl ===========================================================================
+
+dnl XXX make this a private feature?
+CAIRO_ENABLE(test_surfaces, test surfaces, no)
+
+dnl ===========================================================================
+
+CAIRO_ENABLE_SURFACE_BACKEND(image, image, always, [
+  pixman_REQUIRES="pixman-1 >= 0.21.0"
+  PKG_CHECK_MODULES(pixman, $pixman_REQUIRES, , [AC_MSG_RESULT(no)
+  use_image="no (requires $pixman_REQUIRES http://cairographics.org/releases/)"])
+  image_REQUIRES=$pixman_REQUIRES
+  image_CFLAGS=$pixman_CFLAGS
+  image_LIBS=$pixman_LIBS
+])
+
+dnl ===========================================================================
+
+CAIRO_ENABLE_SURFACE_BACKEND(mime, mime, always)
+CAIRO_ENABLE_SURFACE_BACKEND(recording, recording, always)
+CAIRO_ENABLE_SURFACE_BACKEND(observer, observer, always)
+CAIRO_ENABLE_SURFACE_BACKEND(tee, tee, no)
+CAIRO_ENABLE_SURFACE_BACKEND(xml, xml, no, [
+    use_xml=$have_libz
+    xml_NONPKGCONFIG_LIBS=-lz
+])
+
+dnl ===========================================================================
+
+CAIRO_ENABLE_FONT_BACKEND(user, user, always)
+
+dnl ===========================================================================
+dnl
+dnl This needs to be last on our list of features so that the pthread libs and flags
+dnl gets prefixed in front of everything else in CAIRO_{CFLAGS,LIBS}.
+dnl
+have_real_pthread=no
+have_pthread=no
+CAIRO_ENABLE(pthread, pthread, auto, [CAIRO_CONFIGURE_PTHREAD])
+AM_CONDITIONAL(HAVE_REAL_PTHREAD, test "x$use_pthread" = "xyes" -a "x$have_real_pthread" = "xyes")
+AM_CONDITIONAL(HAVE_PTHREAD, test "x$use_pthread" = "xyes")
+AC_SUBST(pthread_CFLAGS)
+AC_SUBST(pthread_LIBS)
+AC_SUBST(real_pthread_CFLAGS)
+AC_SUBST(real_pthread_LIBS)
+
+
+dnl ===========================================================================
+dnl Build gobject integration library
+
+CAIRO_ENABLE_FUNCTIONS(gobject, gobject, auto, [
+  gobject_REQUIRES="gobject-2.0 glib-2.0"
+  PKG_CHECK_MODULES(GOBJECT, $gobject_REQUIRES, , [AC_MSG_RESULT(no)
+  use_gobject="no (requires $gobject_REQUIRES http://download.gnome.org/pub/GNOME/sources/glib/)"])
+  gobject_NONPKGCONFIG_EXTRA_LIBS="-L\${libdir} -lcairo-gobject"
+])
+dnl I'm too lazy to fix the caching properly
+if test "x$use_gobject" = "xyes"; then
+  PKG_CHECK_MODULES(GOBJECT, $gobject_REQUIRES)
+fi
+
+dnl ===========================================================================
+dnl Default to quick testing during development, but force a full test before
+dnl release
+
+AC_ARG_ENABLE(full-testing,
+  AS_HELP_STRING([--enable-full-testing],
+                 [Sets the test suite to perform full testing by default, which
+                 will dramatically slow down make check, but is a
+                 *requirement* before release.]), [
+if test "x$enableval" = "xyes"; then
+    CAIRO_TEST_MODE=full
+    AC_SUBST(CAIRO_TEST_MODE)
+fi
+])
+
+dnl ===========================================================================
+dnl Build the external converter if we have any of the test backends
+AM_CONDITIONAL(BUILD_ANY2PPM,
+              test "x$any2ppm_svg" = "xyes" \
+                -o "x$any2ppm_pdf" = "xyes" \
+                -o "x$any2ppm_ps"  = "xyes" \
+                -o "x$any2ppm_cs"  = "xyes")
+
+dnl ===========================================================================
+dnl Some utilities need to dlopen the shared libraries, so they need to
+dnl know how libtools will name them
+
+case $host in
+*-*-darwin*)
+       SHLIB_EXT="dylib"
+       ;;
+*)
+       SHLIB_EXT="so"
+       ;;
+esac
+AC_DEFINE_UNQUOTED(SHARED_LIB_EXT, "${SHLIB_EXT}", [Shared library file extension])
+AC_SUBST(SHLIB_EXT)
+
+dnl ===========================================================================
+dnl The tracing utility requires LD_PRELOAD, so only build it for systems
+dnl that are known to work.
+
+case $host in
+*-linux*|*-*bsd*|*-solaris*|*-*-darwin*|*-dragonfly*|*-*-gnu*)
+       have_ld_preload="yes"
+       ;;
+*)
+       have_ld_preload="no"
+       ;;
+esac
+
+CAIRO_ENABLE(trace, cairo-trace, auto, [
+       if test "x$have_ld_preload" != "xyes" -o \
+               "x$have_libz" != "xyes" -o \
+               "x$have_real_pthread" != "xyes" -o \
+               "x$have_dlsym" != "xyes"; then
+               use_trace="no (requires dynamic linker and zlib and real pthreads)"
+       fi
+])
+
+CAIRO_ENABLE(interpreter, cairo-script-interpreter, yes, [
+       if test "x$have_libz" != "xyes"; then
+               use_interpreter="no (requires zlib)"
+       fi
+])
+
+AC_CHECK_LIB(bfd, bfd_openr,
+        [AC_CHECK_HEADER(bfd.h, [have_bfd=yes],
+        [have_bfd=no])], [have_bfd=no])
+AC_CHECK_HEADER(libiberty.h,, [have_bfd=no])
+if test "x$have_bfd" = "xyes"; then
+    AC_DEFINE([HAVE_BFD], [1], [Define to 1 if you have the binutils development files installed])
+    BFD_LIBS=-lbfd
+    AC_SUBST(BFD_LIBS)
+fi
+
+CAIRO_ENABLE(symbol_lookup, symbol-lookup, auto, [
+       if test "x$have_bfd" != "xyes"; then
+               use_symbol_lookup="no (requires bfd)"
+       fi
+])
+
+PKG_CHECK_MODULES(glib, glib-2.0, have_glib=yes, have_glib=no)
+AC_SUBST(glib_CFLAGS)
+AC_SUBST(glib_LIBS)
+AM_CONDITIONAL(BUILD_SPHINX, test "x$have_glib" = "xyes")
+
+save_LIBS="$LIBS"
+AC_CHECK_LIB(rt, shm_open, shm_LIBS="-lrt")
+AC_SUBST(shm_LIBS)
+LIBS="$save_LIBS"
+
+dnl ===========================================================================
+
+AC_ARG_ENABLE(some-floating-point,
+  AS_HELP_STRING([--disable-some-floating-point],
+                 [Disable certain code paths that rely heavily on double precision
+                  floating-point calculation. This option can improve
+                  performance on systems without a double precision floating-point
+                  unit, but might degrade performance on those that do.]), [
+if test "x$enableval" = "xno"; then
+  # A value of 'no' for $enableval means that they want to disable, which
+  # means 'yes' for $disable_some_floating_point.
+  disable_some_floating_point=yes
+fi
+], [disable_some_floating_point=no])
+
+AM_CONDITIONAL(DISABLE_SOME_FLOATING_POINT,
+               test "x$disable_some_floating_point" = "xyes")
+if test "x$disable_some_floating_point" = "xyes"; then
+  AC_DEFINE(DISABLE_SOME_FLOATING_POINT, 1,
+            [Define to 1 to disable certain code paths that rely heavily on
+             double precision floating-point calculation])
+fi
+
+dnl ===========================================================================
+
+dnl Extra stuff we need to do when building C++ code
+need_cxx="no"
+AS_IF([test "x$use_skia" = "xyes"], [need_cxx="yes"])
+AS_IF([test "x$use_qt" = "xyes"], [need_cxx="yes"])
+AS_IF([test "x$use_beos" = "xyes"], [need_cxx="yes"])
+
+AM_CONDITIONAL(BUILD_CXX, test "x$need_cxx" = "xyes")
+
+dnl ===========================================================================
+
+# We use GTK+ for some utility/debugging tools
+PKG_CHECK_MODULES(gtk, "gtk+-2.0",have_gtk=yes, have_gtk=no)
+AM_CONDITIONAL(HAVE_GTK, test "x$have_gtk" = "xyes")
+
+AC_CONFIG_FILES([
+Makefile
+boilerplate/Makefile
+src/Makefile
+test/Makefile
+test/pdiff/Makefile
+perf/Makefile
+perf/micro/Makefile
+util/Makefile
+util/cairo-fdr/Makefile
+util/cairo-gobject/Makefile
+util/cairo-missing/Makefile
+util/cairo-script/Makefile
+util/cairo-script/examples/Makefile
+util/cairo-sphinx/Makefile
+util/cairo-trace/Makefile
+util/cairo-trace/cairo-trace
+doc/Makefile
+doc/public/Makefile
+])
+
+AC_OUTPUT
+CAIRO_REPORT
diff --git a/debian/changelog b/debian/changelog
new file mode 100644 (file)
index 0000000..54a322e
--- /dev/null
@@ -0,0 +1,15 @@
+cairo (1.11.3-slp2+8) unstable; urgency=low
+
+  * Remove SLP keyword
+  * Git: unmodified/cairo.git
+  * Tag: cairo_1.11.3-slp2+8
+
+ -- Seongwon Cho <seongwon1.cho@samsung.com>  Fri, 23 Dec 2011 11:26:04 +0900
+
+cairo (1.11.3-slp2+7) unstable; urgency=low
+
+  * Initial version
+  * Git: unmodified/cairo.git
+  * Tag: cairo_1.11.3-slp2+7
+
+ -- Seongwon Cho <seongwon1.cho@samsung.com>  Thu, 15 Dec 2011 12:54:03 +0900
diff --git a/debian/compat b/debian/compat
new file mode 100644 (file)
index 0000000..7ed6ff8
--- /dev/null
@@ -0,0 +1 @@
+5
diff --git a/debian/control b/debian/control
new file mode 100755 (executable)
index 0000000..f2fd805
--- /dev/null
@@ -0,0 +1,55 @@
+Source: cairo
+Section: libs
+Priority: optional
+Maintainer: Wonsik Jung <sidein@samsung.com>, Deokjin Kim <deokjin81.kim@samsung.com>, Seongwon Cho <seongwon1.cho@samsung.com>
+Build-Depends: debhelper (>= 5.0.22), autotools-dev, pkg-config (>= 0.19), libfontconfig1-dev, libfreetype6-dev (>= 2.1.10), libxrender-dev (>= 0.6.0), libx11-dev, libpng12-dev, libsm-dev, xutils-dev, libxt-dev, dpatch, libpixman-1-dev (>= 0.12.0), libxcb1-dev (>= 0.9.92), libxcb-render0-dev (>= 0.9.92), libxcb-render-util0-dev, libglib2.0-dev, libxcb-shm0-dev
+Standards-Version: 3.8.2
+
+Package: libcairo2-dev
+Provides: libcairo-dev
+Conflicts: libcairo-dev, libcairo0.5.1-dev, libcairo0.6.0-dev, libcairo0.9.0-dev
+Replaces: libcairo0.5.1-dev, libcairo0.6.0-dev, libcairo0.9.0-dev
+Section: libdevel
+Architecture: any
+Depends: libcairo2 (= ${binary:Version}), libfontconfig1-dev, libfreetype6-dev (>= 2.1.10), libxrender-dev (>= 0.6.0), libpng12-dev, libsm-dev, libpixman-1-dev (>= 0.12.0), libxcb1-dev, libxcb-render0-dev, libxcb-render-util0-dev, libxcb-shm0-dev
+Description: Development files for the Cairo 2D graphics library
+ Cairo is a multi-platform library providing anti-aliased
+ vector-based rendering for multiple target backends.
+ .
+ This package contains the development libraries, header files needed by
+ programs that want to compile with Cairo.
+
+Package: libcairo2
+Section: libs
+Architecture: any
+Depends: ${shlibs:Depends}, libpixman-1-0
+Provides: libcairo
+Conflicts: libcairo1
+Replaces: libcairo0.5.1, libcairo0.6.0, libcairo0.9.0, libcairo1
+Description: The Cairo 2D vector graphics library
+ Cairo is a multi-platform library providing anti-aliased
+ vector-based rendering for multiple target backends. Paths consist
+ of line segments and cubic splines and can be rendered at any width
+ with various join and cap styles. All colors may be specified with
+ optional translucence (opacity/alpha) and combined using the
+ extended Porter/Duff compositing algebra as found in the X Render
+ Extension.
+ .
+ Cairo exports a stateful rendering API similar in spirit to the path
+ construction, text, and painting operators of PostScript, (with the
+ significant addition of translucence in the imaging model). When
+ complete, the API is intended to support the complete imaging model of
+ PDF 1.4.
+ .
+ This package contains the shared libraries.
+
+Package: libcairo2-dbg
+Section: debug
+Priority: extra
+Architecture: any
+Depends: libcairo2 (= ${binary:Version})
+Description: The Cairo 2D vector graphics library (debugging symbols)
+ Debugging symbols for the Cairo 2D vector graphics library. This is
+ needed to debug programs linked against libcairo2.
+
+
diff --git a/debian/copyright b/debian/copyright
new file mode 100644 (file)
index 0000000..a162af3
--- /dev/null
@@ -0,0 +1,533 @@
+This is the Debian package of the Cairo multi-platform 2D graphics library
+
+Packaged by Dave Beckett <dajobe@debian.org>
+
+It was downloaded from http://cairographics.org/snapshots/
+
+----------------------------------------------------------------------
+  Copyright 1999 Tom Tromey
+  Copyright 2002, 2003 University of Southern California, Information
+    Sciences Institute  (ISI)
+  Copyright 2000, 2002, 2004, 2005 Keith Packard
+  Copyright 2004 Calum Robinson
+  Copyright 2004 Richard D. Worth
+  Copyright 2004, 2005 Red Hat, Inc.
+
+  Copyright 2004 David Reveman
+    Permission to use, copy, modify, distribute, and sell this software
+    and its documentation for any purpose is hereby granted without
+    fee, provided that the above copyright notice appear in all copies
+    and that both that copyright notice and this permission notice
+    appear in supporting documentation, and that the name of David
+    Reveman not be used in advertising or publicity pertaining to
+    distribution of the software without specific, written prior
+    permission. David Reveman makes no representations about the
+    suitability of this software for any purpose.  It is provided "as
+    is" without express or implied warranty.
+
+    DAVID REVEMAN DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+    SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+    FITNESS, IN NO EVENT SHALL DAVID REVEMAN BE LIABLE FOR ANY SPECIAL,
+    INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+    RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+    OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+    IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+    Author: David Reveman <davidr@novell.com>
+
+----------------------------------------------------------------------
+
+Cairo is free software.
+
+Every source file in the implementation of cairo is available to be
+redistributed and/or modified under the terms of either the GNU Lesser
+General Public License (LPGL) version 2.1 or the Mozilla Public
+License (MPL) version 1.1.  Some files are available under more
+liberal terms, but we believe that in all cases, each file may be used
+under either the LGPL or the MPL.
+
+See the following files in this directory for the precise terms and
+conditions of either license:
+
+       COPYING-LGPL-2.1
+       COPYING-MPL-1.1
+
+Please see each file in the implementation for Copyright and licensing
+information.
+
+----------------------------------------------------------------------
+On Debian systems, the complete text of the GNU General Lesser Public
+License 2.1 can be found in /usr/share/common-licenses/LGPL-2.1
+
+The Mozilla Public License 1.1 (COPYING-MPL-1.1 above) follows:
+
+                          MOZILLA PUBLIC LICENSE
+                                Version 1.1
+
+                              ---------------
+
+1. Definitions.
+
+     1.0.1. "Commercial Use" means distribution or otherwise making the
+     Covered Code available to a third party.
+
+     1.1. "Contributor" means each entity that creates or contributes to
+     the creation of Modifications.
+
+     1.2. "Contributor Version" means the combination of the Original
+     Code, prior Modifications used by a Contributor, and the Modifications
+     made by that particular Contributor.
+
+     1.3. "Covered Code" means the Original Code or Modifications or the
+     combination of the Original Code and Modifications, in each case
+     including portions thereof.
+
+     1.4. "Electronic Distribution Mechanism" means a mechanism generally
+     accepted in the software development community for the electronic
+     transfer of data.
+
+     1.5. "Executable" means Covered Code in any form other than Source
+     Code.
+
+     1.6. "Initial Developer" means the individual or entity identified
+     as the Initial Developer in the Source Code notice required by Exhibit
+     A.
+
+     1.7. "Larger Work" means a work which combines Covered Code or
+     portions thereof with code not governed by the terms of this License.
+
+     1.8. "License" means this document.
+
+     1.8.1. "Licensable" means having the right to grant, to the maximum
+     extent possible, whether at the time of the initial grant or
+     subsequently acquired, any and all of the rights conveyed herein.
+
+     1.9. "Modifications" means any addition to or deletion from the
+     substance or structure of either the Original Code or any previous
+     Modifications. When Covered Code is released as a series of files, a
+     Modification is:
+          A. Any addition to or deletion from the contents of a file
+          containing Original Code or previous Modifications.
+
+          B. Any new file that contains any part of the Original Code or
+          previous Modifications.
+
+     1.10. "Original Code" means Source Code of computer software code
+     which is described in the Source Code notice required by Exhibit A as
+     Original Code, and which, at the time of its release under this
+     License is not already Covered Code governed by this License.
+
+     1.10.1. "Patent Claims" means any patent claim(s), now owned or
+     hereafter acquired, including without limitation,  method, process,
+     and apparatus claims, in any patent Licensable by grantor.
+
+     1.11. "Source Code" means the preferred form of the Covered Code for
+     making modifications to it, including all modules it contains, plus
+     any associated interface definition files, scripts used to control
+     compilation and installation of an Executable, or source code
+     differential comparisons against either the Original Code or another
+     well known, available Covered Code of the Contributor's choice. The
+     Source Code can be in a compressed or archival form, provided the
+     appropriate decompression or de-archiving software is widely available
+     for no charge.
+
+     1.12. "You" (or "Your")  means an individual or a legal entity
+     exercising rights under, and complying with all of the terms of, this
+     License or a future version of this License issued under Section 6.1.
+     For legal entities, "You" includes any entity which controls, is
+     controlled by, or is under common control with You. For purposes of
+     this definition, "control" means (a) the power, direct or indirect,
+     to cause the direction or management of such entity, whether by
+     contract or otherwise, or (b) ownership of more than fifty percent
+     (50%) of the outstanding shares or beneficial ownership of such
+     entity.
+
+2. Source Code License.
+
+     2.1. The Initial Developer Grant.
+     The Initial Developer hereby grants You a world-wide, royalty-free,
+     non-exclusive license, subject to third party intellectual property
+     claims:
+          (a)  under intellectual property rights (other than patent or
+          trademark) Licensable by Initial Developer to use, reproduce,
+          modify, display, perform, sublicense and distribute the Original
+          Code (or portions thereof) with or without Modifications, and/or
+          as part of a Larger Work; and
+
+          (b) under Patents Claims infringed by the making, using or
+          selling of Original Code, to make, have made, use, practice,
+          sell, and offer for sale, and/or otherwise dispose of the
+          Original Code (or portions thereof).
+
+          (c) the licenses granted in this Section 2.1(a) and (b) are
+          effective on the date Initial Developer first distributes
+          Original Code under the terms of this License.
+
+          (d) Notwithstanding Section 2.1(b) above, no patent license is
+          granted: 1) for code that You delete from the Original Code; 2)
+          separate from the Original Code;  or 3) for infringements caused
+          by: i) the modification of the Original Code or ii) the
+          combination of the Original Code with other software or devices.
+
+     2.2. Contributor Grant.
+     Subject to third party intellectual property claims, each Contributor
+     hereby grants You a world-wide, royalty-free, non-exclusive license
+
+          (a)  under intellectual property rights (other than patent or
+          trademark) Licensable by Contributor, to use, reproduce, modify,
+          display, perform, sublicense and distribute the Modifications
+          created by such Contributor (or portions thereof) either on an
+          unmodified basis, with other Modifications, as Covered Code
+          and/or as part of a Larger Work; and
+
+          (b) under Patent Claims infringed by the making, using, or
+          selling of  Modifications made by that Contributor either alone
+          and/or in combination with its Contributor Version (or portions
+          of such combination), to make, use, sell, offer for sale, have
+          made, and/or otherwise dispose of: 1) Modifications made by that
+          Contributor (or portions thereof); and 2) the combination of
+          Modifications made by that Contributor with its Contributor
+          Version (or portions of such combination).
+
+          (c) the licenses granted in Sections 2.2(a) and 2.2(b) are
+          effective on the date Contributor first makes Commercial Use of
+          the Covered Code.
+
+          (d)    Notwithstanding Section 2.2(b) above, no patent license is
+          granted: 1) for any code that Contributor has deleted from the
+          Contributor Version; 2)  separate from the Contributor Version;
+          3)  for infringements caused by: i) third party modifications of
+          Contributor Version or ii)  the combination of Modifications made
+          by that Contributor with other software  (except as part of the
+          Contributor Version) or other devices; or 4) under Patent Claims
+          infringed by Covered Code in the absence of Modifications made by
+          that Contributor.
+
+3. Distribution Obligations.
+
+     3.1. Application of License.
+     The Modifications which You create or to which You contribute are
+     governed by the terms of this License, including without limitation
+     Section 2.2. The Source Code version of Covered Code may be
+     distributed only under the terms of this License or a future version
+     of this License released under Section 6.1, and You must include a
+     copy of this License with every copy of the Source Code You
+     distribute. You may not offer or impose any terms on any Source Code
+     version that alters or restricts the applicable version of this
+     License or the recipients' rights hereunder. However, You may include
+     an additional document offering the additional rights described in
+     Section 3.5.
+
+     3.2. Availability of Source Code.
+     Any Modification which You create or to which You contribute must be
+     made available in Source Code form under the terms of this License
+     either on the same media as an Executable version or via an accepted
+     Electronic Distribution Mechanism to anyone to whom you made an
+     Executable version available; and if made available via Electronic
+     Distribution Mechanism, must remain available for at least twelve (12)
+     months after the date it initially became available, or at least six
+     (6) months after a subsequent version of that particular Modification
+     has been made available to such recipients. You are responsible for
+     ensuring that the Source Code version remains available even if the
+     Electronic Distribution Mechanism is maintained by a third party.
+
+     3.3. Description of Modifications.
+     You must cause all Covered Code to which You contribute to contain a
+     file documenting the changes You made to create that Covered Code and
+     the date of any change. You must include a prominent statement that
+     the Modification is derived, directly or indirectly, from Original
+     Code provided by the Initial Developer and including the name of the
+     Initial Developer in (a) the Source Code, and (b) in any notice in an
+     Executable version or related documentation in which You describe the
+     origin or ownership of the Covered Code.
+
+     3.4. Intellectual Property Matters
+          (a) Third Party Claims.
+          If Contributor has knowledge that a license under a third party's
+          intellectual property rights is required to exercise the rights
+          granted by such Contributor under Sections 2.1 or 2.2,
+          Contributor must include a text file with the Source Code
+          distribution titled "LEGAL" which describes the claim and the
+          party making the claim in sufficient detail that a recipient will
+          know whom to contact. If Contributor obtains such knowledge after
+          the Modification is made available as described in Section 3.2,
+          Contributor shall promptly modify the LEGAL file in all copies
+          Contributor makes available thereafter and shall take other steps
+          (such as notifying appropriate mailing lists or newsgroups)
+          reasonably calculated to inform those who received the Covered
+          Code that new knowledge has been obtained.
+
+          (b) Contributor APIs.
+          If Contributor's Modifications include an application programming
+          interface and Contributor has knowledge of patent licenses which
+          are reasonably necessary to implement that API, Contributor must
+          also include this information in the LEGAL file.
+
+               (c)    Representations.
+          Contributor represents that, except as disclosed pursuant to
+          Section 3.4(a) above, Contributor believes that Contributor's
+          Modifications are Contributor's original creation(s) and/or
+          Contributor has sufficient rights to grant the rights conveyed by
+          this License.
+
+     3.5. Required Notices.
+     You must duplicate the notice in Exhibit A in each file of the Source
+     Code.  If it is not possible to put such notice in a particular Source
+     Code file due to its structure, then You must include such notice in a
+     location (such as a relevant directory) where a user would be likely
+     to look for such a notice.  If You created one or more Modification(s)
+     You may add your name as a Contributor to the notice described in
+     Exhibit A.  You must also duplicate this License in any documentation
+     for the Source Code where You describe recipients' rights or ownership
+     rights relating to Covered Code.  You may choose to offer, and to
+     charge a fee for, warranty, support, indemnity or liability
+     obligations to one or more recipients of Covered Code. However, You
+     may do so only on Your own behalf, and not on behalf of the Initial
+     Developer or any Contributor. You must make it absolutely clear than
+     any such warranty, support, indemnity or liability obligation is
+     offered by You alone, and You hereby agree to indemnify the Initial
+     Developer and every Contributor for any liability incurred by the
+     Initial Developer or such Contributor as a result of warranty,
+     support, indemnity or liability terms You offer.
+
+     3.6. Distribution of Executable Versions.
+     You may distribute Covered Code in Executable form only if the
+     requirements of Section 3.1-3.5 have been met for that Covered Code,
+     and if You include a notice stating that the Source Code version of
+     the Covered Code is available under the terms of this License,
+     including a description of how and where You have fulfilled the
+     obligations of Section 3.2. The notice must be conspicuously included
+     in any notice in an Executable version, related documentation or
+     collateral in which You describe recipients' rights relating to the
+     Covered Code. You may distribute the Executable version of Covered
+     Code or ownership rights under a license of Your choice, which may
+     contain terms different from this License, provided that You are in
+     compliance with the terms of this License and that the license for the
+     Executable version does not attempt to limit or alter the recipient's
+     rights in the Source Code version from the rights set forth in this
+     License. If You distribute the Executable version under a different
+     license You must make it absolutely clear that any terms which differ
+     from this License are offered by You alone, not by the Initial
+     Developer or any Contributor. You hereby agree to indemnify the
+     Initial Developer and every Contributor for any liability incurred by
+     the Initial Developer or such Contributor as a result of any such
+     terms You offer.
+
+     3.7. Larger Works.
+     You may create a Larger Work by combining Covered Code with other code
+     not governed by the terms of this License and distribute the Larger
+     Work as a single product. In such a case, You must make sure the
+     requirements of this License are fulfilled for the Covered Code.
+
+4. Inability to Comply Due to Statute or Regulation.
+
+     If it is impossible for You to comply with any of the terms of this
+     License with respect to some or all of the Covered Code due to
+     statute, judicial order, or regulation then You must: (a) comply with
+     the terms of this License to the maximum extent possible; and (b)
+     describe the limitations and the code they affect. Such description
+     must be included in the LEGAL file described in Section 3.4 and must
+     be included with all distributions of the Source Code. Except to the
+     extent prohibited by statute or regulation, such description must be
+     sufficiently detailed for a recipient of ordinary skill to be able to
+     understand it.
+
+5. Application of this License.
+
+     This License applies to code to which the Initial Developer has
+     attached the notice in Exhibit A and to related Covered Code.
+
+6. Versions of the License.
+
+     6.1. New Versions.
+     Netscape Communications Corporation ("Netscape") may publish revised
+     and/or new versions of the License from time to time. Each version
+     will be given a distinguishing version number.
+
+     6.2. Effect of New Versions.
+     Once Covered Code has been published under a particular version of the
+     License, You may always continue to use it under the terms of that
+     version. You may also choose to use such Covered Code under the terms
+     of any subsequent version of the License published by Netscape. No one
+     other than Netscape has the right to modify the terms applicable to
+     Covered Code created under this License.
+
+     6.3. Derivative Works.
+     If You create or use a modified version of this License (which you may
+     only do in order to apply it to code which is not already Covered Code
+     governed by this License), You must (a) rename Your license so that
+     the phrases "Mozilla", "MOZILLAPL", "MOZPL", "Netscape",
+     "MPL", "NPL" or any confusingly similar phrase do not appear in your
+     license (except to note that your license differs from this License)
+     and (b) otherwise make it clear that Your version of the license
+     contains terms which differ from the Mozilla Public License and
+     Netscape Public License. (Filling in the name of the Initial
+     Developer, Original Code or Contributor in the notice described in
+     Exhibit A shall not of themselves be deemed to be modifications of
+     this License.)
+
+7. DISCLAIMER OF WARRANTY.
+
+     COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS,
+     WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
+     WITHOUT LIMITATION, WARRANTIES THAT THE COVERED CODE IS FREE OF
+     DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING.
+     THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED CODE
+     IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT,
+     YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE
+     COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER
+     OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF
+     ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER.
+
+8. TERMINATION.
+
+     8.1.  This License and the rights granted hereunder will terminate
+     automatically if You fail to comply with terms herein and fail to cure
+     such breach within 30 days of becoming aware of the breach. All
+     sublicenses to the Covered Code which are properly granted shall
+     survive any termination of this License. Provisions which, by their
+     nature, must remain in effect beyond the termination of this License
+     shall survive.
+
+     8.2.  If You initiate litigation by asserting a patent infringement
+     claim (excluding declatory judgment actions) against Initial Developer
+     or a Contributor (the Initial Developer or Contributor against whom
+     You file such action is referred to as "Participant")  alleging that:
+
+     (a)  such Participant's Contributor Version directly or indirectly
+     infringes any patent, then any and all rights granted by such
+     Participant to You under Sections 2.1 and/or 2.2 of this License
+     shall, upon 60 days notice from Participant terminate prospectively,
+     unless if within 60 days after receipt of notice You either: (i)
+     agree in writing to pay Participant a mutually agreeable reasonable
+     royalty for Your past and future use of Modifications made by such
+     Participant, or (ii) withdraw Your litigation claim with respect to
+     the Contributor Version against such Participant.  If within 60 days
+     of notice, a reasonable royalty and payment arrangement are not
+     mutually agreed upon in writing by the parties or the litigation claim
+     is not withdrawn, the rights granted by Participant to You under
+     Sections 2.1 and/or 2.2 automatically terminate at the expiration of
+     the 60 day notice period specified above.
+
+     (b)  any software, hardware, or device, other than such Participant's
+     Contributor Version, directly or indirectly infringes any patent, then
+     any rights granted to You by such Participant under Sections 2.1(b)
+     and 2.2(b) are revoked effective as of the date You first made, used,
+     sold, distributed, or had made, Modifications made by that
+     Participant.
+
+     8.3.  If You assert a patent infringement claim against Participant
+     alleging that such Participant's Contributor Version directly or
+     indirectly infringes any patent where such claim is resolved (such as
+     by license or settlement) prior to the initiation of patent
+     infringement litigation, then the reasonable value of the licenses
+     granted by such Participant under Sections 2.1 or 2.2 shall be taken
+     into account in determining the amount or value of any payment or
+     license.
+
+     8.4.  In the event of termination under Sections 8.1 or 8.2 above,
+     all end user license agreements (excluding distributors and resellers)
+     which have been validly granted by You or any distributor hereunder
+     prior to termination shall survive termination.
+
+9. LIMITATION OF LIABILITY.
+
+     UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT
+     (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE INITIAL
+     DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED CODE,
+     OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY PERSON FOR
+     ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY
+     CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL,
+     WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER
+     COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN
+     INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF
+     LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY
+     RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW
+     PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE
+     EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO
+     THIS EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU.
+
+10. U.S. GOVERNMENT END USERS.
+
+     The Covered Code is a "commercial item," as that term is defined in
+     48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial computer
+     software" and "commercial computer software documentation," as such
+     terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48
+     C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995),
+     all U.S. Government End Users acquire Covered Code with only those
+     rights set forth herein.
+
+11. MISCELLANEOUS.
+
+     This License represents the complete agreement concerning subject
+     matter hereof. If any provision of this License is held to be
+     unenforceable, such provision shall be reformed only to the extent
+     necessary to make it enforceable. This License shall be governed by
+     California law provisions (except to the extent applicable law, if
+     any, provides otherwise), excluding its conflict-of-law provisions.
+     With respect to disputes in which at least one party is a citizen of,
+     or an entity chartered or registered to do business in the United
+     States of America, any litigation relating to this License shall be
+     subject to the jurisdiction of the Federal Courts of the Northern
+     District of California, with venue lying in Santa Clara County,
+     California, with the losing party responsible for costs, including
+     without limitation, court costs and reasonable attorneys' fees and
+     expenses. The application of the United Nations Convention on
+     Contracts for the International Sale of Goods is expressly excluded.
+     Any law or regulation which provides that the language of a contract
+     shall be construed against the drafter shall not apply to this
+     License.
+
+12. RESPONSIBILITY FOR CLAIMS.
+
+     As between Initial Developer and the Contributors, each party is
+     responsible for claims and damages arising, directly or indirectly,
+     out of its utilization of rights under this License and You agree to
+     work with Initial Developer and Contributors to distribute such
+     responsibility on an equitable basis. Nothing herein is intended or
+     shall be deemed to constitute any admission of liability.
+
+13. MULTIPLE-LICENSED CODE.
+
+     Initial Developer may designate portions of the Covered Code as
+     "Multiple-Licensed".  "Multiple-Licensed" means that the Initial
+     Developer permits you to utilize portions of the Covered Code under
+     Your choice of the NPL or the alternative licenses, if any, specified
+     by the Initial Developer in the file described in Exhibit A.
+
+EXHIBIT A -Mozilla Public License.
+
+     ``The contents of this file are subject to the Mozilla Public License
+     Version 1.1 (the "License"); you may not use this file except in
+     compliance with the License. You may obtain a copy of the License at
+     http://www.mozilla.org/MPL/
+
+     Software distributed under the License is distributed on an "AS IS"
+     basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+     License for the specific language governing rights and limitations
+     under the License.
+
+     The Original Code is ______________________________________.
+
+     The Initial Developer of the Original Code is ________________________.
+     Portions created by ______________________ are Copyright (C) ______
+     _______________________. All Rights Reserved.
+
+     Contributor(s): ______________________________________.
+
+     Alternatively, the contents of this file may be used under the terms
+     of the _____ license (the  "[___] License"), in which case the
+     provisions of [______] License are applicable instead of those
+     above.  If you wish to allow use of your version of this file only
+     under the terms of the [____] License and not to allow others to use
+     your version of this file under the MPL, indicate your decision by
+     deleting  the provisions above and replace  them with the notice and
+     other provisions required by the [___] License.  If you do not delete
+     the provisions above, a recipient may use your version of this file
+     under either the MPL or the [___] License."
+
+     [NOTE: The text of this Exhibit A may differ slightly from the text of
+     the notices in the Source Code files of the Original Code. You should
+     use the text of this Exhibit A rather than the text found in the
+     Original Code Source Code for Your Modifications.]
+
diff --git a/debian/libcairo2-dev.install b/debian/libcairo2-dev.install
new file mode 100644 (file)
index 0000000..6101b59
--- /dev/null
@@ -0,0 +1,5 @@
+debian/install/main/usr/include/cairo/*.h /usr/include/cairo
+debian/install/main/usr/lib/libcairo.so /usr/lib
+debian/install/main/usr/lib/libcairo*.a /usr/lib
+debian/install/main/usr/lib/libcairo*.la /usr/lib
+debian/install/main/usr/lib/pkgconfig/cairo*.pc /usr/lib/pkgconfig
diff --git a/debian/libcairo2-doc.doc-base b/debian/libcairo2-doc.doc-base
new file mode 100644 (file)
index 0000000..291a6b2
--- /dev/null
@@ -0,0 +1,11 @@
+Document: libcairo2-doc
+Title: Cairo Reference Manual
+Author: Cairo Project
+Abstract: Cairo is a multi-platform library providing anti-aliased
+ vector-based rendering for multiple target backends.
+ This document covers programming using the Cairo library.
+Section: Programming/C
+
+Format: HTML
+Index: /usr/share/gtk-doc/html/cairo/index.html
+Files: /usr/share/gtk-doc/html/cairo/*.html
diff --git a/debian/libcairo2-doc.install b/debian/libcairo2-doc.install
new file mode 100644 (file)
index 0000000..030ef08
--- /dev/null
@@ -0,0 +1 @@
+debian/install/main/usr/share/gtk-doc/html/cairo /usr/share/gtk-doc/html
diff --git a/debian/libcairo2.install b/debian/libcairo2.install
new file mode 100644 (file)
index 0000000..1be4a14
--- /dev/null
@@ -0,0 +1 @@
+debian/install/main/usr/lib/libcairo.so.* /usr/lib
diff --git a/debian/libcairo2.install.in b/debian/libcairo2.install.in
new file mode 100644 (file)
index 0000000..1be4a14
--- /dev/null
@@ -0,0 +1 @@
+debian/install/main/usr/lib/libcairo.so.* /usr/lib
diff --git a/debian/libcairo2.install.opt b/debian/libcairo2.install.opt
new file mode 100644 (file)
index 0000000..769bad5
--- /dev/null
@@ -0,0 +1 @@
+debian/install/opt/usr/lib/libcairo.so.* @OPTLIBDIR@
diff --git a/debian/rules b/debian/rules
new file mode 100755 (executable)
index 0000000..b166edb
--- /dev/null
@@ -0,0 +1,186 @@
+#!/usr/bin/make -f
+
+DISTRIBUTION   = TIZEN
+
+# list of flavors we build; each gets a builddir, a configure pass (configure
+# args are defined below), a build pass, and an install pass
+FLAVORS := main udeb
+
+# current flavor we're building; this is only expanded in flavor specific
+# targets
+current_flavor = $*
+
+# macro to get a value for the current flavor we're building; for example
+# when building the main flavor, $(call flavor_get,CFLAGS) will expand to
+# main_CFLAGS if it's set or to CFLAGS otherwise; pay attention to not adding
+# superflous spaces when for the arguments of $(call ); only some vars can
+# be expanded in this way though
+flavor_get = $(or $($(current_flavor)_$(1)),$($(1)))
+
+# Supported backends (as of Cairo 1.7.4):
+#  (internal) Image
+#  xlib       Xlib
+#  pdf        PDF
+#  ps         PostScript
+#  svg        SVG
+#
+# Experimental and unsupported backends:
+#  directfb  DirectFB (requires Build-Depend on libdirectfb-dev (>=0.9.25) )
+#  glitz     OpenGL (glitz)
+#  xcb       XCB  (requires Build-Depend on at least libxcb1-dev)
+#
+# Other platform backends are auto disabled: quartz/OSX, win32/Win32
+#
+# Features:
+# --enable-png  PNG (default enabled)
+
+DEB_HOST_GNU_TYPE  ?= $(shell dpkg-architecture -qDEB_HOST_GNU_TYPE)
+DEB_BUILD_GNU_TYPE = $(shell dpkg-architecture -qDEB_HOST_GNU_TYPE)
+#DEB_BUILD_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_BUILD_GNU_TYPE)
+
+PREFIX = /usr
+configure_flags += \
+--prefix=$(PREFIX) \
+--mandir=/usr/share/man \
+--infodir=/usr/share/info \
+--disable-glitz \
+--build=$(DEB_BUILD_GNU_TYPE) \
+--disable-win32 \
+--enable-directfb=no \
+--enable-xlib \
+--with-x \
+--x-includes=$(PREFIX)/include \
+--x-libraries=$(PREFIX)/lib \
+--enable-static 
+
+
+ifneq ($(DEB_BUILD_GNU_TYPE),$(DEB_HOST_GNU_TYPE))
+configure_flags += --host=$(DEB_HOST_GNU_TYPE)
+endif
+
+ifeq (arm, $(findstring arm, $(DEB_HOST_GNU_TYPE)))
+CONFIGURE_OPTIONS += \
+--enable-svg=no 
+endif
+
+main_configure_flags += \
+$(configure_flags) 
+
+ifeq ($(DISTRIBUTION),TIZEN)
+configure_flags += --enable-xcb
+endif
+
+builddir = $(buildbasedir)/$(current_flavor)
+buildbasedir = $(CURDIR)/debian/build
+
+installdir = $(installbasedir)/$(current_flavor)
+installbasedir = $(CURDIR)/debian/install
+
+# default CFLAGS; these can be expanded with $(call flavor_get, )
+CFLAGS += -Wall -g
+
+ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS)))
+       CFLAGS += -O0
+else
+       CFLAGS += -O2
+endif
+
+CFLAGS += -fPIC -rdynamic
+LDFLAGS += -Wl,--hash-style=both -Wl,--as-needed
+
+ifeq (armel, $(DEB_HOST_ARCH))
+        CFLAGS += -mfpu=vfp -mfloat-abi=softfp
+endif
+
+include /usr/share/dpatch/dpatch.make
+
+configure-common-stamp: patch-stamp
+       dh_testdir
+       NOCONFIGURE=1 ./autogen.sh
+       touch $@
+
+configure-stamp-%: configure-common-stamp
+       dh_testdir
+       mkdir -p $(builddir); \
+       cd $(builddir); \
+       $(CURDIR)/configure \
+               CFLAGS="$(call flavor_get,CFLAGS)" \
+               $(call flavor_get,configure_flags)
+       touch $@
+
+build: $(addprefix build-stamp-, $(FLAVORS))
+
+build-stamp-%: configure-stamp-%
+       dh_testdir
+       $(MAKE) -C $(builddir)
+       touch $@
+
+clean: unpatch
+       dh_testdir
+       dh_testroot
+       # restore files from backup
+       [ ! -r config.sub.orig ] || mv -f config.sub.orig config.sub
+       [ ! -r config.guess.orig ] || mv -f config.guess.orig config.guess
+       rm -f src/cairo-features.h
+       rm -f *-stamp
+       rm -rf $(buildbasedir) $(installbasedir)
+       # Generated by configure and breaks the build if already existing
+       rm -f src/cairo-features.h
+       # Copy in fresh copies of config.{sub,guess}
+       # (these are from autotools-dev, so must Build-Depend on it)
+       -test -r /usr/share/misc/config.sub && \
+         cp -f /usr/share/misc/config.sub config.sub
+       -test -r /usr/share/misc/config.guess && \
+         cp -f /usr/share/misc/config.guess config.guess
+       dh_clean
+
+install-%: build-stamp-%
+       $(MAKE) -C $(builddir) install DESTDIR=$(installdir)
+       sed -i "/dependency_libs/ s/'.*'/''/" $(installdir)/usr/lib/*.la
+
+install:
+       dh_testdir
+       dh_testroot
+       dh_clean -k
+       dh_installdirs
+       for f in $(FLAVORS); do \
+           debian/rules install-$$f; \
+       done
+
+binary-indep: build install
+       dh_testdir
+       dh_testroot
+       dh_install -i
+       dh_installchangelogs -i ChangeLog
+       dh_installdocs -i -A NEWS README AUTHORS
+       dh_link -i
+       dh_compress -i
+       dh_fixperms -i
+       dh_installdeb -i
+       dh_gencontrol -i
+       dh_md5sums -i
+       dh_builddeb -i
+
+binary-arch: build install
+       dh_testdir
+       dh_testroot
+       dh_install -s
+       dh_installdocs -s -A README NEWS AUTHORS
+       dh_installchangelogs -s ChangeLog
+       dh_installman -s
+       dh_installexamples -s
+       dh_link -s
+       dh_strip -s --dbg-package=libcairo2-dbg
+       dh_compress -s
+       dh_fixperms -s
+       dh_makeshlibs -plibcairo2 -V 'libcairo2 (>= 1.8.0-2)' -- -c4
+       dh_installdeb -s
+       dh_shlibdeps -s
+       dh_perl -s
+       dh_gencontrol -s
+       dh_md5sums -s
+       dh_builddeb -s
+
+binary: binary-indep binary-arch
+
+.PHONY: build clean binary-indep binary-arch binary install patch unpatch clean
diff --git a/debian/watch b/debian/watch
new file mode 100644 (file)
index 0000000..c8ddd8c
--- /dev/null
@@ -0,0 +1,2 @@
+version=2
+http://cairographics.org/releases/ cairo-([\d+\.]+|\d+)\.tar\.gz debian uupdate
diff --git a/doc/.gitignore b/doc/.gitignore
new file mode 100644 (file)
index 0000000..23c1897
--- /dev/null
@@ -0,0 +1,3 @@
+Makefile
+Makefile.in
+*~
diff --git a/doc/Makefile.am b/doc/Makefile.am
new file mode 100644 (file)
index 0000000..864a9f1
--- /dev/null
@@ -0,0 +1,7 @@
+include $(top_srcdir)/build/Makefile.am.common
+
+SUBDIRS=public
+
+doc:
+       cd public && $(MAKE) $(AM_MAKEFLAGS) doc
+.PHONY: doc
diff --git a/doc/public/.gitignore b/doc/public/.gitignore
new file mode 100644 (file)
index 0000000..493a241
--- /dev/null
@@ -0,0 +1,19 @@
+*.stamp
+Makefile
+Makefile.in
+cairo-decl-list.txt
+cairo-decl.txt
+cairo-docs.sgml
+cairo-undeclared.txt
+cairo-undocumented.txt
+cairo-unused.txt
+cairo.hierarchy
+cairo.interfaces
+cairo.prerequisites
+cairo.args
+cairo.signals
+html
+xml
+version.xml
+*~
+*.bak
diff --git a/doc/public/Makefile.am b/doc/public/Makefile.am
new file mode 100644 (file)
index 0000000..11f9e7b
--- /dev/null
@@ -0,0 +1,64 @@
+include $(top_srcdir)/build/Makefile.am.common
+include $(top_srcdir)/src/Makefile.am.features
+
+# The name of the module.
+DOC_MODULE=cairo
+
+# The top-level SGML file.
+DOC_MAIN_SGML_FILE=cairo-docs.xml
+
+# Extra options to supply to gtkdoc-scan
+SCAN_OPTIONS=--deprecated-guards="CAIRO_DISABLE_DEPRECATED" --ignore-decorators="cairo_public|cairo_private"
+
+# The directory containing the source code.
+DOC_SOURCE_DIR=$(top_srcdir)/src
+
+# Used for dependencies
+HFILE_GLOB=$(top_srcdir)/src/cairo*.h
+CFILE_GLOB=$(top_srcdir)/src/cairo*.c
+EXTRA_HFILES=$(top_builddir)/src/cairo-supported-features.h
+
+# Headers to ignore
+IGNORE_HFILES= \
+       drm \
+       cairo-features.h \
+       cairo-features-win32.h \
+       $(all_cairo_private) \
+       $(unsupported_cairo_headers) \
+       $(NULL)
+
+# Extra options to supply to gtkdoc-mkdb
+MKDB_OPTIONS=--sgml-mode --output-format=xml --name-space=cairo
+
+# Extra options to supply to gtkdoc-mktmpl
+MKTMPL_OPTIONS=
+
+# Non-autogenerated SGML files to be included in $(DOC_MAIN_SGML_FILE)
+content_files =        \
+       language-bindings.xml \
+       version.xml \
+       $(NULL)
+
+version.xml: $(top_srcdir)/cairo-version.h
+       echo $(CAIRO_VERSION_MAJOR).$(CAIRO_VERSION_MINOR).$(CAIRO_VERSION_MICRO) > $@
+
+# Images to copy into HTML directory
+HTML_IMAGES =
+
+# Extra options to supply to gtkdoc-fixref
+FIXXREF_OPTIONS=
+
+include $(top_srcdir)/build/Makefile.am.gtk-doc
+
+dist-hook: doc
+
+# This line really belongs in gtk-doc.mk
+$(REPORT_FILES): sgml-build.stamp
+
+if ENABLE_GTK_DOC
+TESTS += check-doc-coverage.sh
+endif
+
+TESTS += check-doc-syntax.sh
+EXTRA_DIST += check-doc-coverage.sh check-doc-syntax.sh
+TESTS_ENVIRONMENT = srcdir="$(srcdir)" top_srcdir="$(top_srcdir)" MAKE="$(MAKE) $(AM_MAKEFLAGS)" DOC_MODULE="$(DOC_MODULE)" REPORT_FILES="$(REPORT_FILES)"
diff --git a/doc/public/README b/doc/public/README
new file mode 100644 (file)
index 0000000..f3d157b
--- /dev/null
@@ -0,0 +1,37 @@
+Cairo Reference Documentation
+=============================
+
+The API documentation is generated using gtk-doc.
+
+
+Building
+--------
+
+The documentation is not built by default.  To build it you need to
+configure with gtk-doc enabled (--enable-gtk-doc), and run:
+
+       make doc
+
+
+Adding New API
+--------------
+
+When adding new symbols and macros to the public API, modify
+cairo-section.txt and add new symbols to the right place.
+
+When adding whole new features, you also need to modify cairo-docs.xml
+and add a new file under tmpl/.  Beware that the files are tmpl/ are
+both manually edited AND modified by gtk-doc, gathering documentation
+stub from source files.
+
+
+Tests
+-----
+
+There are some tests in this directory, ensuring proper documentation
+syntax as well as checking that all public symbols are fully documented.
+
+After adding any new API, just run:
+
+       make check
+
diff --git a/doc/public/cairo-docs.xml b/doc/public/cairo-docs.xml
new file mode 100644 (file)
index 0000000..c2ffead
--- /dev/null
@@ -0,0 +1,74 @@
+<?xml version='1.0' encoding='UTF-8'?> 
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+                "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd" [
+<!ENTITY version SYSTEM "version.xml">
+]>
+<book lang="en" id="cairo" xmlns:xi="http://www.w3.org/2003/XInclude"> 
+<title>Cairo: A Vector Graphics Library</title>
+  <bookinfo>
+    <title>Cairo: A Vector Graphics Library</title>
+    <releaseinfo>for Cairo &version;</releaseinfo>
+  </bookinfo>
+  <chapter id="cairo-drawing">
+    <title>Drawing</title>
+    <xi:include href="xml/cairo.xml"/>
+    <xi:include href="xml/cairo-paths.xml"/>
+    <xi:include href="xml/cairo-pattern.xml"/>
+    <xi:include href="xml/cairo-region.xml"/>
+    <xi:include href="xml/cairo-transforms.xml"/>
+    <xi:include href="xml/cairo-text.xml"/>
+  </chapter>
+  <chapter id="cairo-fonts">
+    <title>Fonts</title>
+    <xi:include href="xml/cairo-font-face.xml"/>
+    <xi:include href="xml/cairo-scaled-font.xml"/>
+    <xi:include href="xml/cairo-font-options.xml"/>
+    <xi:include href="xml/cairo-ft.xml"/>
+    <xi:include href="xml/cairo-win32-fonts.xml"/>
+    <xi:include href="xml/cairo-quartz-fonts.xml"/>
+    <xi:include href="xml/cairo-user-fonts.xml"/>
+  </chapter>
+  <chapter id="cairo-surfaces">
+    <title>Surfaces</title>
+    <xi:include href="xml/cairo-device.xml"/>
+    <xi:include href="xml/cairo-surface.xml"/>
+    <xi:include href="xml/cairo-image.xml"/>
+    <xi:include href="xml/cairo-pdf.xml"/>
+    <xi:include href="xml/cairo-png.xml"/>
+    <xi:include href="xml/cairo-ps.xml"/>
+    <xi:include href="xml/cairo-recording.xml"/>
+    <xi:include href="xml/cairo-win32.xml"/>
+    <!--xi:include href="xml/cairo-beos.xml"/-->
+    <xi:include href="xml/cairo-svg.xml"/>
+    <xi:include href="xml/cairo-quartz.xml" />
+    <!--xi:include href="xml/cairo-quartz-image.xml"/-->
+    <!--xi:include href="xml/cairo-xcb.xml"/-->
+    <xi:include href="xml/cairo-xlib.xml"/>
+  </chapter>
+  <chapter id="cairo-support">
+    <title>Utilities</title>
+    <xi:include href="xml/cairo-matrix.xml"/>
+    <xi:include href="xml/cairo-status.xml"/>
+    <xi:include href="xml/cairo-version.xml"/>
+    <xi:include href="xml/cairo-types.xml"/>
+  </chapter>
+  <index id="index-all">
+    <title>Index</title>
+  </index>
+  <index id="index-1.2" role="1.2">
+    <title>Index of new symbols in 1.2</title>
+  </index>
+  <index id="index-1.4" role="1.4">
+    <title>Index of new symbols in 1.4</title>
+  </index>
+  <index id="index-1.6" role="1.6">
+    <title>Index of new symbols in 1.6</title>
+  </index>
+  <index id="index-1.8" role="1.8">
+    <title>Index of new symbols in 1.8</title>
+  </index>
+  <index id="index-1.10" role="1.10">
+    <title>Index of new symbols in 1.10</title>
+  </index>
+  <xi:include href="language-bindings.xml"/>
+</book>
diff --git a/doc/public/cairo-overrides.txt b/doc/public/cairo-overrides.txt
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/doc/public/cairo-sections.txt b/doc/public/cairo-sections.txt
new file mode 100644 (file)
index 0000000..11439ae
--- /dev/null
@@ -0,0 +1,620 @@
+<SECTION>
+<FILE>cairo-ft</FILE>
+CAIRO_HAS_FT_FONT
+CAIRO_HAS_FC_FONT
+cairo_ft_font_face_create_for_ft_face
+cairo_ft_font_face_create_for_pattern
+cairo_ft_font_options_substitute
+cairo_ft_scaled_font_lock_face
+cairo_ft_scaled_font_unlock_face
+</SECTION>
+
+<SECTION>
+<FILE>cairo-win32-fonts</FILE>
+CAIRO_HAS_WIN32_FONT
+cairo_win32_font_face_create_for_logfontw
+cairo_win32_font_face_create_for_hfont
+cairo_win32_font_face_create_for_logfontw_hfont
+cairo_win32_scaled_font_select_font
+cairo_win32_scaled_font_done_font
+cairo_win32_scaled_font_get_metrics_factor
+cairo_win32_scaled_font_get_logical_to_device
+cairo_win32_scaled_font_get_device_to_logical
+</SECTION>
+
+<SECTION>
+<FILE>cairo-quartz-fonts</FILE>
+CAIRO_HAS_QUARTZ_FONT
+cairo_quartz_font_face_create_for_cgfont
+cairo_quartz_font_face_create_for_atsu_font_id
+</SECTION>
+
+<SECTION>
+<FILE>cairo-user-fonts</FILE>
+CAIRO_HAS_USER_FONT
+cairo_user_scaled_font_init_func_t
+cairo_user_scaled_font_render_glyph_func_t
+cairo_user_scaled_font_text_to_glyphs_func_t
+cairo_user_scaled_font_unicode_to_glyph_func_t
+cairo_user_font_face_create
+cairo_user_font_face_set_init_func
+cairo_user_font_face_get_init_func
+cairo_user_font_face_set_render_glyph_func
+cairo_user_font_face_get_render_glyph_func
+cairo_user_font_face_set_unicode_to_glyph_func
+cairo_user_font_face_get_unicode_to_glyph_func
+cairo_user_font_face_set_text_to_glyphs_func
+cairo_user_font_face_get_text_to_glyphs_func
+</SECTION>
+
+<SECTION>
+<FILE>cairo-image</FILE>
+CAIRO_HAS_IMAGE_SURFACE
+cairo_format_t
+cairo_format_stride_for_width
+cairo_image_surface_create
+cairo_image_surface_create_for_data
+cairo_image_surface_get_data
+cairo_image_surface_get_format
+cairo_image_surface_get_width
+cairo_image_surface_get_height
+cairo_image_surface_get_stride
+</SECTION>
+
+<SECTION>
+<FILE>cairo-pdf</FILE>
+CAIRO_HAS_PDF_SURFACE
+cairo_pdf_surface_create
+cairo_pdf_surface_create_for_stream
+cairo_pdf_surface_restrict_to_version
+cairo_pdf_version_t
+cairo_pdf_get_versions
+cairo_pdf_version_to_string
+cairo_pdf_surface_set_size
+</SECTION>
+
+<SECTION>
+<FILE>cairo-png</FILE>
+CAIRO_HAS_PNG_FUNCTIONS
+cairo_image_surface_create_from_png
+cairo_read_func_t
+cairo_image_surface_create_from_png_stream
+cairo_surface_write_to_png
+cairo_write_func_t
+cairo_surface_write_to_png_stream
+</SECTION>
+
+<SECTION>
+<FILE>cairo-ps</FILE>
+CAIRO_HAS_PS_SURFACE
+cairo_ps_surface_create
+cairo_ps_surface_create_for_stream
+cairo_ps_surface_restrict_to_level
+cairo_ps_level_t
+cairo_ps_get_levels
+cairo_ps_level_to_string
+cairo_ps_surface_set_eps
+cairo_ps_surface_get_eps
+cairo_ps_surface_set_size
+cairo_ps_surface_dsc_begin_setup
+cairo_ps_surface_dsc_begin_page_setup
+cairo_ps_surface_dsc_comment
+</SECTION>
+
+<SECTION>
+<FILE>cairo-recording</FILE>
+CAIRO_HAS_RECORDING_SURFACE
+cairo_recording_surface_create
+cairo_recording_surface_ink_extents
+</SECTION>
+
+<SECTION>
+<FILE>cairo-win32</FILE>
+CAIRO_HAS_WIN32_SURFACE
+cairo_win32_surface_create
+cairo_win32_surface_create_with_dib
+cairo_win32_surface_create_with_ddb
+cairo_win32_printing_surface_create
+cairo_win32_surface_get_dc
+cairo_win32_surface_get_image
+</SECTION>
+
+<SECTION>
+<FILE>cairo-quartz</FILE>
+CAIRO_HAS_QUARTZ_SURFACE
+cairo_quartz_surface_create
+cairo_quartz_surface_create_for_cg_context
+cairo_quartz_surface_get_cg_context
+<SUBSECTION Private>
+cairo_quartz_image_surface_create
+cairo_quartz_image_surface_get_image
+</SECTION>
+
+<SECTION>
+<FILE>cairo-xlib</FILE>
+CAIRO_HAS_XLIB_SURFACE
+cairo_xlib_surface_create
+cairo_xlib_surface_create_for_bitmap
+cairo_xlib_surface_set_size
+cairo_xlib_surface_get_display
+cairo_xlib_surface_get_screen
+cairo_xlib_surface_set_drawable
+cairo_xlib_surface_get_drawable
+cairo_xlib_surface_get_visual
+cairo_xlib_surface_get_width
+cairo_xlib_surface_get_height
+cairo_xlib_surface_get_depth
+</SECTION>
+
+<SECTION>
+<FILE>cairo-xlib-xrender</FILE>
+CAIRO_HAS_XLIB_XRENDER_SURFACE
+cairo_xlib_surface_create_with_xrender_format
+cairo_xlib_surface_get_xrender_format
+</SECTION>
+
+<SECTION>
+<FILE>cairo-svg</FILE>
+CAIRO_HAS_SVG_SURFACE
+cairo_svg_surface_create
+cairo_svg_surface_create_for_stream
+cairo_svg_surface_restrict_to_version
+cairo_svg_version_t
+cairo_svg_get_versions
+cairo_svg_version_to_string 
+</SECTION>
+
+<SECTION>
+<FILE>cairo-device</FILE>
+cairo_device_t
+cairo_device_reference
+cairo_device_destroy
+cairo_device_status
+cairo_device_finish
+cairo_device_flush
+cairo_device_type_t
+cairo_device_get_type
+cairo_device_get_reference_count
+cairo_device_set_user_data
+cairo_device_get_user_data
+cairo_device_acquire
+cairo_device_release
+</SECTION>
+
+<SECTION>
+<FILE>cairo-surface</FILE>
+CAIRO_MIME_TYPE_JP2
+CAIRO_MIME_TYPE_JPEG
+CAIRO_MIME_TYPE_PNG
+CAIRO_MIME_TYPE_URI
+CAIRO_MIME_TYPE_UNIQUE_ID
+cairo_surface_t
+cairo_content_t
+cairo_surface_create_similar
+cairo_surface_create_for_rectangle
+cairo_surface_reference
+cairo_surface_destroy
+cairo_surface_status
+cairo_surface_finish
+cairo_surface_flush
+cairo_surface_get_device
+cairo_surface_get_font_options
+cairo_surface_get_content
+cairo_surface_mark_dirty
+cairo_surface_mark_dirty_rectangle
+cairo_surface_set_device_offset
+cairo_surface_get_device_offset
+cairo_surface_set_fallback_resolution
+cairo_surface_get_fallback_resolution
+cairo_surface_type_t
+cairo_surface_get_type
+cairo_surface_get_reference_count
+cairo_surface_set_user_data
+cairo_surface_get_user_data
+cairo_surface_copy_page
+cairo_surface_show_page
+cairo_surface_has_show_text_glyphs
+cairo_surface_set_mime_data
+cairo_surface_get_mime_data
+</SECTION>
+
+<SECTION>
+<FILE>cairo-version</FILE>
+CAIRO_VERSION
+CAIRO_VERSION_MAJOR
+CAIRO_VERSION_MINOR
+CAIRO_VERSION_MICRO
+CAIRO_VERSION_STRING
+CAIRO_VERSION_ENCODE
+CAIRO_VERSION_STRINGIZE
+cairo_version
+cairo_version_string
+<SUBSECTION Private>
+CAIRO_VERSION_STRINGIZE_
+</SECTION>
+
+<SECTION>
+<FILE>cairo-region</FILE>
+cairo_region_t
+cairo_region_create
+cairo_region_create_rectangle
+cairo_region_create_rectangles
+cairo_region_copy
+cairo_region_reference
+cairo_region_destroy
+cairo_region_status
+cairo_region_get_extents
+cairo_region_num_rectangles
+cairo_region_get_rectangle
+cairo_region_is_empty
+cairo_region_contains_point
+cairo_region_overlap_t
+cairo_region_contains_rectangle
+cairo_region_equal
+cairo_region_translate
+cairo_region_intersect
+cairo_region_intersect_rectangle
+cairo_region_subtract
+cairo_region_subtract_rectangle
+cairo_region_union
+cairo_region_union_rectangle
+cairo_region_xor
+cairo_region_xor_rectangle
+</SECTION>
+
+<SECTION>
+<FILE>cairo-pattern</FILE>
+cairo_pattern_t
+cairo_pattern_add_color_stop_rgb
+cairo_pattern_add_color_stop_rgba
+cairo_pattern_get_color_stop_count
+cairo_pattern_get_color_stop_rgba
+cairo_pattern_create_rgb
+cairo_pattern_create_rgba
+cairo_pattern_get_rgba
+cairo_pattern_create_for_surface
+cairo_pattern_get_surface
+cairo_pattern_create_linear
+cairo_pattern_get_linear_points
+cairo_pattern_create_radial
+cairo_pattern_get_radial_circles
+cairo_pattern_create_mesh
+cairo_mesh_pattern_begin_patch
+cairo_mesh_pattern_end_patch
+cairo_mesh_pattern_move_to
+cairo_mesh_pattern_line_to
+cairo_mesh_pattern_curve_to
+cairo_mesh_pattern_set_control_point
+cairo_mesh_pattern_set_corner_color_rgb
+cairo_mesh_pattern_set_corner_color_rgba
+cairo_mesh_pattern_get_patch_count
+cairo_mesh_pattern_get_path
+cairo_mesh_pattern_get_control_point
+cairo_mesh_pattern_get_corner_color_rgba
+cairo_pattern_reference
+cairo_pattern_destroy
+cairo_pattern_status
+cairo_extend_t
+cairo_pattern_set_extend
+cairo_pattern_get_extend
+cairo_filter_t
+cairo_pattern_set_filter
+cairo_pattern_get_filter
+cairo_pattern_set_matrix
+cairo_pattern_get_matrix
+cairo_pattern_type_t
+cairo_pattern_get_type
+cairo_pattern_get_reference_count
+cairo_pattern_set_user_data
+cairo_pattern_get_user_data
+</SECTION>
+
+<SECTION>
+<FILE>cairo-matrix</FILE>
+cairo_matrix_t
+cairo_matrix_init
+cairo_matrix_init_identity
+cairo_matrix_init_translate
+cairo_matrix_init_scale
+cairo_matrix_init_rotate
+cairo_matrix_translate
+cairo_matrix_scale
+cairo_matrix_rotate
+cairo_matrix_invert
+cairo_matrix_multiply
+cairo_matrix_transform_distance
+cairo_matrix_transform_point
+</SECTION>
+
+<SECTION>
+<FILE>cairo-status</FILE>
+cairo_status_t
+cairo_status_to_string
+cairo_debug_reset_static_data
+</SECTION>
+
+<SECTION>
+<FILE>cairo-font-face</FILE>
+cairo_font_face_t
+cairo_font_face_reference
+cairo_font_face_destroy
+cairo_font_face_status
+cairo_font_type_t
+cairo_font_face_get_type
+cairo_font_face_get_reference_count
+cairo_font_face_set_user_data
+cairo_font_face_get_user_data
+</SECTION>
+
+<SECTION>
+<FILE>cairo-scaled-font</FILE>
+cairo_scaled_font_t
+cairo_scaled_font_create
+cairo_scaled_font_reference
+cairo_scaled_font_destroy
+cairo_scaled_font_status
+cairo_font_extents_t
+cairo_scaled_font_extents
+cairo_text_extents_t
+cairo_scaled_font_text_extents
+cairo_scaled_font_glyph_extents
+cairo_scaled_font_text_to_glyphs
+cairo_scaled_font_get_font_face
+cairo_scaled_font_get_font_options
+cairo_scaled_font_get_font_matrix
+cairo_scaled_font_get_ctm
+cairo_scaled_font_get_scale_matrix
+cairo_scaled_font_get_type
+cairo_scaled_font_get_reference_count
+cairo_scaled_font_set_user_data
+cairo_scaled_font_get_user_data
+</SECTION>
+
+<SECTION>
+<FILE>cairo-font-options</FILE>
+cairo_font_options_t
+cairo_font_options_create
+cairo_font_options_copy
+cairo_font_options_destroy
+cairo_font_options_status
+cairo_font_options_merge
+cairo_font_options_hash
+cairo_font_options_equal
+cairo_font_options_set_antialias
+cairo_font_options_get_antialias
+cairo_subpixel_order_t
+cairo_font_options_set_subpixel_order
+cairo_font_options_get_subpixel_order
+cairo_hint_style_t
+cairo_font_options_set_hint_style
+cairo_font_options_get_hint_style
+cairo_hint_metrics_t
+cairo_font_options_set_hint_metrics
+cairo_font_options_get_hint_metrics
+</SECTION>
+
+<SECTION>
+<FILE>cairo-types</FILE>
+cairo_bool_t
+cairo_user_data_key_t
+cairo_destroy_func_t
+cairo_rectangle_int_t
+</SECTION>
+
+<SECTION>
+<FILE>cairo-transforms</FILE>
+cairo_translate
+cairo_scale
+cairo_rotate
+cairo_transform
+cairo_set_matrix
+cairo_get_matrix
+cairo_identity_matrix
+cairo_user_to_device
+cairo_user_to_device_distance
+cairo_device_to_user
+cairo_device_to_user_distance
+</SECTION>
+
+
+<SECTION>
+<FILE>cairo-paths</FILE>
+cairo_path_t
+cairo_path_data_t
+cairo_path_data_type_t
+cairo_copy_path
+cairo_copy_path_flat
+cairo_path_destroy
+cairo_append_path
+cairo_has_current_point
+cairo_get_current_point
+cairo_new_path
+cairo_new_sub_path
+cairo_close_path
+cairo_arc
+cairo_arc_negative
+cairo_curve_to
+cairo_line_to
+cairo_move_to
+cairo_rectangle
+cairo_glyph_path
+cairo_text_path
+cairo_rel_curve_to
+cairo_rel_line_to
+cairo_rel_move_to
+cairo_path_extents
+</SECTION>
+
+<SECTION>
+<FILE>cairo-text</FILE>
+cairo_glyph_t
+cairo_font_slant_t
+cairo_font_weight_t
+cairo_text_cluster_t
+cairo_text_cluster_flags_t
+cairo_select_font_face
+cairo_set_font_size
+cairo_set_font_matrix
+cairo_get_font_matrix
+cairo_set_font_options
+cairo_get_font_options
+cairo_set_font_face
+cairo_get_font_face
+cairo_set_scaled_font
+cairo_get_scaled_font
+cairo_show_text
+cairo_show_glyphs
+cairo_show_text_glyphs
+cairo_font_extents
+cairo_text_extents
+cairo_glyph_extents
+cairo_toy_font_face_create
+cairo_toy_font_face_get_family
+cairo_toy_font_face_get_slant
+cairo_toy_font_face_get_weight
+cairo_glyph_allocate
+cairo_glyph_free
+cairo_text_cluster_allocate
+cairo_text_cluster_free
+</SECTION>
+
+<SECTION>
+<FILE>cairo</FILE>
+cairo_t
+cairo_create
+cairo_reference
+cairo_destroy
+cairo_status
+cairo_save
+cairo_restore
+cairo_get_target
+cairo_push_group
+cairo_push_group_with_content
+cairo_pop_group
+cairo_pop_group_to_source
+cairo_get_group_target
+cairo_set_source_rgb
+cairo_set_source_rgba
+cairo_set_source
+cairo_set_source_surface
+cairo_get_source
+cairo_antialias_t
+cairo_set_antialias
+cairo_get_antialias
+cairo_set_dash
+cairo_get_dash_count
+cairo_get_dash
+cairo_fill_rule_t
+cairo_set_fill_rule
+cairo_get_fill_rule
+cairo_line_cap_t
+cairo_set_line_cap
+cairo_get_line_cap
+cairo_line_join_t
+cairo_set_line_join
+cairo_get_line_join
+cairo_set_line_width
+cairo_get_line_width
+cairo_set_miter_limit
+cairo_get_miter_limit
+cairo_operator_t
+cairo_set_operator
+cairo_get_operator
+cairo_set_tolerance
+cairo_get_tolerance
+cairo_clip
+cairo_clip_preserve
+cairo_clip_extents
+cairo_in_clip
+cairo_reset_clip
+cairo_rectangle_t
+cairo_rectangle_list_t
+cairo_rectangle_list_destroy
+cairo_copy_clip_rectangle_list
+cairo_fill
+cairo_fill_preserve
+cairo_fill_extents
+cairo_in_fill
+cairo_mask
+cairo_mask_surface
+cairo_paint
+cairo_paint_with_alpha
+cairo_stroke
+cairo_stroke_preserve
+cairo_stroke_extents
+cairo_in_stroke
+cairo_copy_page
+cairo_show_page
+cairo_get_reference_count
+cairo_set_user_data
+cairo_get_user_data
+<SUBSECTION Private>
+cairo_public
+CAIRO_BEGIN_DECLS
+CAIRO_END_DECLS
+cairo_current_font_extents
+cairo_get_font_extents
+cairo_current_operator
+cairo_current_tolerance
+cairo_current_point
+cairo_current_fill_rule
+cairo_current_line_width
+cairo_current_line_cap
+cairo_current_line_join
+cairo_current_miter_limit
+cairo_current_matrix
+cairo_current_target_surface
+cairo_get_status
+cairo_concat_matrix
+cairo_scale_font
+cairo_select_font
+cairo_transform_font
+cairo_transform_point
+cairo_transform_distance
+cairo_inverse_transform_point
+cairo_inverse_transform_distance
+cairo_init_clip
+cairo_surface_create_for_image
+cairo_default_matrix
+cairo_matrix_set_affine
+cairo_matrix_set_identity
+cairo_pattern_add_color_stop
+cairo_set_rgb_color
+cairo_set_pattern
+cairo_xlib_surface_create_for_pixmap_with_visual
+cairo_xlib_surface_create_for_window_with_visual
+cairo_xcb_surface_create_for_pixmap_with_visual
+cairo_xcb_surface_create_for_window_with_visual
+cairo_ps_surface_set_dpi
+cairo_pdf_surface_set_dpi
+cairo_svg_surface_set_dpi
+cairo_current_path
+cairo_current_path_flat
+cairo_get_path
+cairo_get_path_flat
+cairo_set_alpha
+cairo_show_surface
+cairo_copy
+cairo_surface_set_repeat
+cairo_surface_set_matrix
+cairo_surface_get_matrix
+cairo_surface_set_filter
+cairo_surface_get_filter
+cairo_matrix_create
+cairo_matrix_destroy
+cairo_matrix_copy
+cairo_matrix_get_affine
+cairo_set_target_surface
+cairo_set_target_image
+cairo_set_target_pdf
+cairo_set_target_png
+cairo_set_target_ps
+cairo_set_target_quartz
+cairo_set_target_win32
+cairo_set_target_xcb
+cairo_set_target_drawable
+cairo_get_status_string
+cairo_status_string
+CAIRO_FONT_TYPE_ATSUI
+cairo_atsui_font_face_create_for_atsu_font_id
+</SECTION>
diff --git a/doc/public/cairo.types b/doc/public/cairo.types
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/doc/public/check-doc-coverage.sh b/doc/public/check-doc-coverage.sh
new file mode 100755 (executable)
index 0000000..581f324
--- /dev/null
@@ -0,0 +1,54 @@
+#!/bin/sh
+
+LC_ALL=C
+export LC_ALL
+
+if test -z "$DOC_MODULE"; then
+       # extract from Makefile
+       eval `grep '^DOC_MODULE' Makefile | sed 's/ //g'`
+       if test -z "$DOC_MODULE"; then
+               echo Failed extracting DOC_MODULE from Makefile 1>&2
+               echo Try setting DOC_MODULE env var manually 1>&2
+               exit 1
+       fi
+fi
+
+if test -n "$REPORT_FILES"; then
+       $MAKE $REPORT_FILES || exit 1
+fi
+
+test -z "$srcdir" && srcdir=.
+stat=0
+
+if test -f "$DOC_MODULE-undeclared.txt"; then
+       undeclared=`cat "$DOC_MODULE-undeclared.txt"`
+       if test -n "$undeclared"; then
+               echo "*** ERROR: Undeclared documentation symbols:" 1>&2
+               cat "$DOC_MODULE-undeclared.txt" 1>&2
+               stat=1
+       fi
+fi >&2
+if test -f "$DOC_MODULE-unused.txt"; then
+       unused=`cat "$DOC_MODULE-unused.txt"`
+       if test -n "$unused"; then
+               echo "*** ERROR: Unused documentated symbols:" 1>&2
+               cat "$DOC_MODULE-unused.txt" 1>&2
+               stat=1
+       fi
+fi >&2
+if test -f "$DOC_MODULE-undocumented.txt"; then
+       if grep '^0 symbols incomplete' "$DOC_MODULE-undocumented.txt" >/dev/null &&
+          grep '^0 not documented'     "$DOC_MODULE-undocumented.txt" >/dev/null; then
+               :
+       else
+               echo "*** ERROR: Incomplete or undocumented symbols:" 1>&2
+               cat "$DOC_MODULE-undocumented.txt" 1>&2
+               stat=1
+       fi
+fi >&2
+
+if test $stat != 0; then
+       echo "*** IGNORING ERROR ***"
+fi
+#exit $stat
+exit 0
diff --git a/doc/public/check-doc-syntax.sh b/doc/public/check-doc-syntax.sh
new file mode 100755 (executable)
index 0000000..129065d
--- /dev/null
@@ -0,0 +1,12 @@
+#!/bin/sh
+
+LC_ALL=C
+export LC_ALL
+
+test -z "$srcdir" && srcdir=.
+test -z "$top_srcdir" && top_srcdir=$srcdir/../..
+
+SGML_DOCS=true
+FILES=`echo $srcdir/tmpl/*.sgml`
+
+. "$top_srcdir/src/check-doc-syntax.sh"
diff --git a/doc/public/language-bindings.xml b/doc/public/language-bindings.xml
new file mode 100644 (file)
index 0000000..ce437ef
--- /dev/null
@@ -0,0 +1,745 @@
+<appendix id="language-bindings">
+  <title>Creating a language binding for cairo</title>
+  <para>
+    While cairo is implemented and C, and has a C API, it is expected
+    that many users of cairo will be using it from languages other
+    than C. The glue that connects the core cairo library to another
+    language is known as a <firstterm>language
+    binding</firstterm>. This appendix attempts to collect together
+    issues that come up when creating a language bindings for cairo
+    and present standardized solutions to promote consistency among
+    the different language bindings.
+  </para>
+  <sect1 id="bindings-general">
+    <title>General considerations</title>
+    <para>
+      The naming of the central <link
+       linkend="cairo-t"><type>cairo_t</type></link> type is a
+      special exception. The object is “a cairo context” not “a
+      cairo”, and names such as <type>cairo_t</type> rather than
+      <type>cairo_context_t</type> and
+      <function>cairo_set_source()</function> rather than
+      <function>cairo_context_set_source()</function> are simply
+      abbreviations to make the C API more palatable. In languages
+      which have object-oriented syntax, this abbreviation is much
+      less useful. In fact, if ‘Cairo’ is used as a namespace, then
+      in many languages, you'd end up with a ridiculous type name
+      like ‘Cairo.Cairo’. For this reason, and for inter-language
+      consistency all object-oriented languages should name this
+      type as if it were <type>cairo_context_t</type>.
+    </para>
+    <para>
+      The punctuation and casing of the type names and
+      method names of cairo should be changed to match the general
+      convention of the language. In Java, where type names are written
+      in StudlyCaps and method names in javaCaps, cairo_font_extents_t
+      will become FontExtents and
+      <literal>cairo_set_source(cr,source)</literal>,
+      <literal>cr.setSource(source)</literal>.
+      As compared to changing the punctuation, and casing, much
+      more reluctance should be used in changing the method names
+      themselves. Even if get is usually omitted from getters in
+      your language, you shouldn't bind cairo_get_source() as
+      cr.source().
+    </para>
+  </sect1>
+  <sect1 id="bindings-memory">
+    <title>Memory management</title>
+    <para>
+      The objects in cairo can roughly be divided into two types:
+      reference-counted, opaque types like
+      <link
+      linkend="cairo-surface-t"><type>cairo_surface_t</type></link>
+      and plain structures like
+      <link
+       linkend="cairo-glyph-t"><type>cairo_glyph_t</type></link>.
+      <link
+       linkend="cairo-path-t"><type>cairo_path_t</type></link>
+      and 
+      <link
+       linkend="cairo-path-data-t"><type>cairo_path_data_t</type></link>
+      are special cases and are treated separately in this appendix.
+    </para>
+    <para>
+      Refcounted opaque types all have a
+      <function>..._reference()</function>
+      function to increase the refcount by one and a
+      <function>..._destroy()</function> to decrease the refcount
+      by one. These should not be exposed to the user of the language
+      binding, but rather used to implement memory management within
+      the language binding. The simplest way to do memory management
+      for a language binding is to treat the language binding object
+      as a simple handle to the cairo object. The language binding
+      object references the cairo object, and unreferences it when
+      finalized. This is the recommended method, though there are
+      a couple of caveats to be noted:
+    </para>
+    <itemizedlist>
+      <listitem>
+       <para>
+         Equality won't work as expected. You can have two language
+         objects for the same cairo and they won't necessarily
+         compare equal. If the language allows customizing the
+         equality operation, then this is fixable by comparing
+         the underlying pointers. It also can be fixed by creating
+         at most one language object per cairo object, and
+         uniquifying via a <firstterm>pin table</firstterm> (a hash
+         table that goes from cairo object to language object).
+         For <type>cairo_surface_t</type> you can use also 
+         <link
+         linkend="cairo-surface-set-user-data"><function>cairo_surface_set_user_data()</function></link>
+         instead of a separate pin table.
+       </para>
+      </listitem>
+      <listitem>
+       <para>
+         Derivation from the language object doesn't work because
+         you can lose the language object while keeping the Cairo
+         object. Code like:
+       </para>
+<programlisting>
+public class MySurface (ImageSurface) {
+   public MySurface (width, height) {
+      super (Format.ARGB32, width, height);
+   }
+   public int get42 () {
+      return 42;         
+   }
+}
+
+   cr = Cairo(MySurface(width, height));
+   surface = cr.getTarget();
+</programlisting>
+       <para>
+         Can result in <varname>surface</varname> containing an
+         <classname>ImageSurface</classname> not a <classname>MySurface</classname>.
+         This is not easily fixable without creating memory leaks,
+         and it's probably best to simply forbid deriving from the
+         language objects.
+       </para>
+      </listitem>
+    </itemizedlist>
+    <para>
+      When a plain structure is used as a return value from cairo,
+      this is done by passing it as a “out parameter”.
+    </para>
+<programlisting>
+cairo_font_extents_t extents;      
+
+cairo_font_extents (cr, &amp;extents);</programlisting>
+    <para>
+      In a language binding, this should typically be treated
+      as a return value:
+    </para>
+<programlisting>
+FontExtents extents = cr.fontExtents ();</programlisting>
+    <para>
+      A language binding has a choice in how it implements the
+      language objects for plain structures. It can use a pure
+      language object with fields corresponding to those of the C
+      structure, and convert from and to the C structure when calling
+      cairo functions or converting cairo return values. Or it
+      can keep a pointer to the C structure internally and wrap
+      it inside a language object much like occurs for refcounted
+      objects. The choice should be invisible to the user: they should
+      be able to imagine that it is implemented as a pure language
+      object.
+    </para>
+  </sect1>
+  <sect1 id="bindings-return-values">
+    <title>Multiple return values</title>
+    <para>
+      There are a number of functions in the cairo API that have
+      multiple <firstterm>out parameters</firstterm> or
+      <firstterm>in-out parameters</firstterm>. In some languages
+      these can be translated into multiple return values. In Python,
+      what is:
+    </para>
+    <programlisting>
+cairo_user_to_device (cr, &amp;x, &amp;y);</programlisting>
+    <para>
+      can by mapped to:
+    </para>
+    <programlisting>
+(x, y) = cr.user_to_device (cr, x, y);</programlisting>
+    <para>
+      but many languages don't have provisions for multiple return
+      values, so it is necessary to introduce auxiliary types.
+      Most of the functions that require the auxiliary types
+      require a type that would, in C, look like
+    </para>
+    <programlisting>
+typedef struct _cairo_point cairo_point_t;
+struct _cairo_point {
+    double x;
+    double y;
+}</programlisting>
+    <para>
+      The same type should be used both for functions that use a pair
+      of coordinates as an absolute position, and functions that use
+      a pair of coordinates as a displacement. While an argument could
+      be made that having a separate “distance” type is more correct,
+      it is more likely just to confuse users.
+    </para>
+    <programlisting>
+void
+cairo_user_to_device (cairo_t *cr, double *x, double *y);
+
+void
+cairo_user_to_device_distance (cairo_t *cr, double *dx, double *dy);
+
+void
+cairo_device_to_user (cairo_t *cr, double *x, double *y);
+
+void
+cairo_device_to_user_distance (cairo_t *cr, double *dx, double *dy);
+
+void
+cairo_matrix_transform_distance (cairo_matrix_t *matrix, double *dx, double *dy);
+
+void
+cairo_matrix_transform_point (cairo_matrix_t *matrix, double *x, double *y);
+
+void
+cairo_get_current_point (cairo_t *cr, double *x, double *y);
+    </programlisting>
+    <para>
+      There are also a couple of functions that return four values
+      representing a rectangle. These should be mapped to a
+      “rectangle” type that looks like:
+    </para>
+    <programlisting>
+typedef struct _cairo_rectangle cairo_rectangle_t;
+struct _cairo_rectangle {
+    double x;
+    double y;
+    double width;
+    double height;
+}</programlisting>
+    <para>
+      The C function returns the rectangle as a set of two points to
+      facilitate rounding to integral extents, but this isn't worth
+      adding a “box” type to go along with the more obvious
+      “rectangle” representation.
+    </para>
+    <remark>
+      Q: Would it make sense here to define a standard
+      <function>cairo_rectangle_round()</function> method
+      that language bindings should map?
+    </remark>
+    <programlisting>
+void
+cairo_stroke_extents (cairo_t *cr,
+                     double *x1, double *y1,
+                     double *x2, double *y2);
+
+void
+cairo_fill_extents (cairo_t *cr,
+                   double *x1, double *y1,
+                   double *x2, double *y2);
+    </programlisting>
+  </sect1>
+  <sect1 id="bindings-overloading">
+    <title>Overloading and optional arguments</title>
+    <para>
+      Function overloading (having a several variants of a function
+      with the same name and different arguments) is a language
+      feature available in many languages but not in C.
+    </para>
+    <para>
+      In general, language binding authors should use restraint in
+      combining functions in the cairo API via function
+      overloading. What may seem like an obvious overload now may
+      turn out to be strange with future additions to cairo.
+      It might seem logical to make
+      <link
+      linkend="cairo-set-source-rgb"><function>cairo_set_source_rgb()</function></link>
+       an overload of <function>cairo_set_source()</function>, but future plans to add
+       <function>cairo_set_source_rgb_premultiplied()</function>,
+      which will also take three doubles make this a bad idea. For
+      this reason, only the following pairs of functions should
+      be combined via overloading
+    </para>
+    <programlisting>
+void
+cairo_set_source (cairo_t *cr, cairo_pattern_t *source);
+
+void
+cairo_set_source_surface (cairo_t          *cr,
+                          cairo_surface_t  *source,
+                          double            surface_x,
+                          double            surface_y);
+      
+void
+cairo_mask (cairo_t         *cr,
+           cairo_pattern_t *pattern);
+
+void
+cairo_mask_surface (cairo_t         *cr,
+                   cairo_surface_t *surface,
+                   double           surface_x,
+                   double           surface_y);
+      
+cairo_surface_t *
+cairo_image_surface_create (cairo_format_t     format,
+                           int                 width,
+                           int                 height);
+cairo_surface_t *
+cairo_image_surface_create_for_data (unsigned char            *data,
+                                    cairo_format_t             format,
+                                    int                        width,
+                                    int                        height,
+                                    int                        stride);
+
+cairo_status_t
+cairo_surface_write_to_png (cairo_surface_t    *surface,
+                           const char          *filename);
+
+cairo_status_t
+cairo_surface_write_to_png_stream (cairo_surface_t     *surface,
+                                  cairo_write_func_t   write_func,
+                                  void                 *closure);
+
+cairo_surface_t *
+cairo_image_surface_create_from_png (const char        *filename);
+
+cairo_surface_t *
+cairo_image_surface_create_from_png_stream (cairo_read_func_t  read_func,
+                                           void                *closure);
+    </programlisting>
+    <para>
+      Note that there are cases where all constructors for a type
+      aren't overloaded together. For example
+      <link
+       linkend="cairo-image-surface-create-from-png"><function>cairo_image_surface_create_from_png()</function></link>
+      should <emphasis>not</emphasis> be overloaded together with
+      <link
+       linkend="cairo-image-surface-create"><function>cairo_image_surface_create()</function></link>.
+      In such cases, the remaining constructors will typically need to
+      be bound as static methods. In Java, for example, we might have:
+    </para>
+<programlisting>
+Surface surface1 = ImageSurface(Format.RGB24, 100, 100);
+Surface surface2 = ImageSurface.createFromPNG("camera.png");</programlisting>
+    <para>
+      Some other overloads that add combinations not found in C may be
+      convenient for users for language bindings that provide
+      <type>cairo_point_t</type> and <type>cairo_rectangle_t</type>
+      types, for example:
+    </para>
+    <programlisting>
+void
+cairo_move_to (cairo_t       *cr,
+               cairo_point_t *point);
+void
+cairo_rectangle (cairo_t           *cr,
+                 cairo_rectangle_t *rectangle);
+    </programlisting>
+  </sect1>
+  <sect1 id="bindings-streams">
+    <title>Streams and File I/O</title>
+    <para>
+      Various places in the cairo API deal with reading and writing
+      data, whether from and to files, or to other sources and
+      destinations. In these cases, what is typically provided in the
+      C API is a simple version that just takes a filename, and a
+      complex version that takes a callback function.
+      An example is the PNG handling functions:
+    </para>
+<programlisting>
+cairo_surface_t *
+cairo_image_surface_create_from_png (const char        *filename);
+
+cairo_surface_t *
+cairo_image_surface_create_from_png_stream (cairo_read_func_t read_func,
+                                           void             *closure);
+
+cairo_status_t
+cairo_surface_write_to_png (cairo_surface_t    *surface,
+                           const char          *filename);
+
+cairo_status_t
+cairo_surface_write_to_png_stream (cairo_surface_t     *surface,
+                                  cairo_write_func_t   write_func,
+                                  void                 *closure);</programlisting>
+    <para>
+      The expectation is that the filename version will be mapped
+      literally in the language binding, but the callback version
+      will be mapped to a version that takes a language stream
+      object. For example, in Java, the four functions above
+      might be mapped to:
+    </para>
+<programlisting>
+static public ImageSurface createFromPNG (String filename) throws IOException;
+static public ImageSurface createFromPNG (InputStream stream) throws IOException;
+public void writeToPNG (String filename) throws IOException;
+public void writeToPNG (OutputStream stream) throws IOException;
+</programlisting>
+    <para>
+      In many cases, it will be better to
+      implement the filename version internally
+      using the stream version, rather than building it on top of the
+      filename version in C. The reason for this is that will
+      naturally give a more standard handling of file errors for
+      the language, as seen in the above Java example, where
+      <methodname>createFromPNG()</methodname> is marked as raising
+      an exception. Propagating exceptions from inside the callback
+      function to the caller will pose a challenge to the language
+      binding implementor, since an exception must not propagate
+      through the Cairo code. A technique that will be useful in
+      some cases is to catch the exception in the callback,
+      store the exception object inside a structure pointed to by
+      <parameter>closure</parameter>, and then rethrow it once
+      the function returns.
+    </para>
+    <remark>
+      I'm not sure how to handle this for
+      <link
+      linkend="cairo-pdf-surface-create-for-stream"><function>cairo_pdf_surface_create_for_stream()</function></link>.
+      Other than keep a “exception to rethrow” thread-specific
+      variable
+      that is checked after <emphasis>every</emphasis> call to a Cairo
+      function.
+    </remark>
+  </sect1>
+  <sect1 id="bindings-errors">
+    <title>Error handling</title>
+    <para>
+      The error handling approach in C for Cairo has multiple
+      elements:
+    </para>
+    <itemizedlist>
+      <listitem><para>
+         When a method on an object fails, the object is put into
+         an error state. Subsequent operations on the object do
+         nothing. The status of the object can be queried with
+         a function like <link
+           linkend="cairo-status"><function>status()</function></link>.
+      </para></listitem>
+      <listitem><para>
+         Constructors, rather than
+         returning <constant>NULL</constant> on out-of-memory failure,
+         return a special singleton object on which all
+         operations do nothing. Retrieving the status of the
+         singleton object returns <constant>CAIRO_STATUS_NO_MEMORY</constant>
+         </para>
+         <remark>
+           Is this going to apply to
+         <type>cairo_surface_t</type> as well?
+       </remark>
+       <remark>
+         What about cairo_copy_path_data()? It's probably going to
+         have to return <constant>NULL</constant>.
+       </remark>
+      </listitem>
+      <listitem><para>
+         Errors propagate from object to object. Setting a pattern
+         in an out-of-memory state as the source of a
+         <type>cairo_t</type> puts the type into an error state.
+      </para></listitem>
+    </itemizedlist>
+    <remark>Much of the above is not yet implemented at the time of
+      this writing</remark>
+    <para>
+      A language binding could copy the C approach, and for a 
+      language without exceptions, this is likely the right thing
+      to do. However, for a language with exceptions, exposing
+      a completely different style of error handling for cairo
+      would be strange. So, instead, status should be checked
+      after every call to cairo, and exceptions thrown as necessary.
+    </para>
+    <para>
+      One problem that can arise with this, in languages
+      where handling exceptions is mandatory (like Java), is that almost
+      every cairo function can result in a status being set,
+      usually because of an out-of-memory condition. This could make
+      cairo hard to use. To resolve this problem, let's classify then
+      cairo status codes:
+    </para>
+<programlisting>
+/* Memory */      
+CAIRO_STATUS_NO_MEMORY,
+
+/* Programmer error */      
+CAIRO_STATUS_INVALID_RESTORE
+CAIRO_STATUS_INVALID_POP_GROUP
+CAIRO_STATUS_NO_CURRENT_POINT
+CAIRO_STATUS_INVALID_MATRIX
+CAIRO_STATUS_NO_TARGET_SURFACE
+CAIRO_STATUS_INVALID_STRING
+CAIRO_STATUS_SURFACE_FINISHED
+CAIRO_STATUS_BAD_NESTING
+
+/* Language binding implementation */
+CAIRO_STATUS_NULL_POINTER
+CAIRO_STATUS_INVALID_PATH_DATA
+CAIRO_STATUS_SURFACE_TYPE_MISMATCH
+
+/* Other */      
+CAIRO_STATUS_READ_ERROR
+CAIRO_STATUS_WRITE_ERROR
+</programlisting>
+    <para>
+      If we look at these, the
+      <constant>CAIRO_STATUS_NO_MEMORY</constant>
+      should map to the native out-of-memory exception, which could
+      happen at any point in any case. Most of the others indicate
+      programmer error, and handling them in user code would be
+      silly. These should be mapped into whatever the language uses
+      for assertion failures, rather than errors that are normally
+      handled. (In Java, a subclass of Error rather than Exception,
+      perhaps.) And <constant>CAIRO_STATUS_READ_ERROR</constant>,
+      and <constant>CAIRO_STATUS_WRITE_ERROR</constant> can occur
+      only in very specific places. (In fact, as described
+      in <xref linkend="bindings-streams"/>, these errors may be
+      mapped into the language's native I/O error types.)
+      So, there really aren't exceptions that the programmer must
+      handle at most points in the Cairo API.
+    </para>
+  </sect1>
+  <sect1 id="bindings-patterns">
+    <title>Patterns</title>
+    <para>
+      The cairo C API allows for creating a number of different types
+      of patterns. All of these different types of patterns map to
+      <link
+      linkend="cairo-pattern-t"><type>cairo_pattern_t</type></link>
+      in C, but in an object oriented language, there should instead
+      be a hierarchy of types. (The functions that should map to
+      constructors or static methods for the various types are listed
+      after the type, methods on that type are listed below. Note that
+      cairo_pattern_create_rgb() and cairo_pattern_create_rgba()
+      should not be overloaded with each other as a SolidPattern()
+      constructor, but should appear as static methods instead.  This
+      is to maintain code clarity by making it clear how the arguments
+      relate to color components.)
+    </para>
+    <programlisting>
+cairo_pattern_t
+      <link linkend="cairo-pattern-set-matrix"><function>cairo_pattern_set_matrix()</function></link>
+      <link linkend="cairo-pattern-get-matrix"><function>cairo_pattern_get_matrix()</function></link>
+   cairo_solid_pattern_t (<link linkend="cairo-pattern-create-rgb"><function>cairo_pattern_create_rgb()</function></link> and <link linkend="cairo-pattern-create-rgba"><function>cairo_pattern_create_rgba()</function></link>)
+   cairo_surface_pattern_t (<link linkend="cairo-pattern-create-for-surface"><function>cairo_pattern_create_for_surface()</function></link>)
+         <link linkend="cairo-pattern-set-extend"><function>cairo_pattern_set_extend()</function></link>
+         <link linkend="cairo-pattern-get-extend"><function>cairo_pattern_get_extend()</function></link>
+         <link linkend="cairo-pattern-set-filter"><function>cairo_pattern_set_filter()</function></link>
+         <link linkend="cairo-pattern-get-filter"><function>cairo_pattern_get_filter()</function></link>
+   cairo_gradient_t
+         <link linkend="cairo-pattern-add-color-stop-rgb"><function>cairo_pattern_add_color_stop_rgb()</function></link>
+         <link linkend="cairo-pattern-add-color-stop-rgba"><function>cairo_pattern_add_color_stop_rgba()</function></link>
+      cairo_linear_gradient_t (<link linkend="cairo-pattern-create-linear"><function>cairo_pattern_create_linear()</function></link>)
+      cairo_radial_gradient_t (<link linkend="cairo-pattern-create-radial"><function>cairo_pattern_create_radial()</function></link>)
+   cairo_mesh_t (<link linkend="cairo-pattern-create-mesh"><function>cairo_pattern_create_mesh()</function></link>)
+         <link linkend="cairo-mesh-pattern-begin-patch"><function>cairo_mesh_pattern_begin_patch()</function></link>
+         <link linkend="cairo-mesh-pattern-end-patch"><function>cairo_mesh_pattern_end_patch()</function></link>
+         <link linkend="cairo-mesh-pattern-move-to"><function>cairo_mesh_pattern_move_to()</function></link>
+         <link linkend="cairo-mesh-pattern-line-to"><function>cairo_mesh_pattern_line_to()</function></link>
+         <link linkend="cairo-mesh-pattern-curve-to"><function>cairo_mesh_pattern_curve_to()</function></link>
+         <link linkend="cairo-mesh-pattern-set-control-point"><function>cairo_mesh_pattern_set_control_point()</function></link>
+         <link linkend="cairo-mesh-pattern-set-corner-color-rgb"><function>cairo_mesh_pattern_set_corner_color_rgb()</function></link>
+         <link linkend="cairo-mesh-pattern-set-corner-color-rgba"><function>cairo_mesh_pattern_set_corner_color_rgba()</function></link>
+         <link linkend="cairo-mesh-pattern-get-patch-count"><function>cairo_mesh_pattern_get_patch_count()</function></link>
+         <link linkend="cairo-mesh-pattern-get-path"><function>cairo_mesh_pattern_get_path()</function></link>
+         <link linkend="cairo-mesh-pattern-get-control-point"><function>cairo_mesh_pattern_get_control_point()</function></link>
+         <link linkend="cairo-mesh-pattern-get-corner-color-rgba"><function>cairo_mesh_pattern_get_corner_color_rgba()</function></link>
+    </programlisting>
+    <para>
+    </para>
+  </sect1>
+  <sect1 id="bindings-surfaces">
+    <title>Surfaces</title>
+    <para>
+      Like patterns, surfaces, which use only the
+      <link
+      linkend="cairo-surface-t"><type>cairo_surface_t</type></link>
+      type in the C API should be broken up into a hierarchy of types
+      in a language binding.
+    </para>
+    <programlisting>
+cairo_surface_t
+    cairo_image_surface_t
+    cairo_atsui_surface_t
+    cairo_win32_surface_t
+    cairo_xlib_surface_t
+    cairo_beos_surface_t
+    </programlisting>
+    <para>
+      Unlike patterns, the constructors and methods on these types are
+      clearly named, and can be trivially associated with the
+      appropriate subtype. Many language bindings will want to avoid
+      binding the platform-specific subtypes at all, since the
+      methods on these types are not useful without passing in native
+      C types. Unless there is a language binding for Xlib available,
+      there is no way to represent a XLib <type>Display</type> * in
+      that language.
+    </para>
+    <para>
+      This doesn't mean that platform-specific surface types can't
+      be used in a language binding that doesn't bind the constructor.
+      A very common situation is to use a cairo language binding in
+      combination with a binding for a higher level system like
+      the <ulink url="http://www.gtk.org/">GTK+</ulink> widget
+      toolkit. In such a situation, the higher level toolkit provides
+      ways to get references to platform specific surfaces.
+    </para>
+    <para>
+      The <link
+         linkend="cairo-surface-set-user-data"><function>cairo_surface_set_user_data()</function></link>,
+      and <link
+         linkend="cairo-surface-get-user-data"><function>cairo_surface_get_user_data()</function></link>
+      methods are provided for use in language bindings, and should
+      not be directly exposed to applications. One example of the use
+      of these functions in a language binding is creating a binding for:
+    </para>
+<programlisting>
+cairo_surface_t *
+<link linkend="cairo-image-surface-create-for-data"><function>cairo_image_surface_create_for_data</function></link> (unsigned char            *data,
+                                    cairo_format_t             format,
+                                    int                        width,
+                                    int                        height,
+                                    int                        stride);
+</programlisting>
+    <para>
+      The memory block passed in for <parameter>data</parameter> must be
+      kept around until the surface is destroyed, so the language
+      binding must have some way of determining when that happens. The
+      way to do this is to use the <parameter>destroy</parameter>
+      argument to <function>cairo_surface_set_user_data()</function>.
+    </para>
+    <remark>
+      Some languages may not have a suitable “pointer to a block of
+      data” type to pass in for <parameter>data</parameter>. And even
+      where a language does have such a type, the user will be
+      frequently able to cause the backing store to be reallocated
+      to a different location or truncated. Should we recommend a
+      standard type name and binding for a buffer object here?
+    </remark>
+  </sect1>
+  <sect1 id="bindings-fonts">
+    <title>Fonts</title>
+    <para>
+      Fonts are once more an area where there is a hierarchy of types:
+    </para>
+<programlisting>
+cairo_font_face_t
+   cairo_ft_font_face_t
+   cairo_win32_font_face_t
+cairo_scaled_font_t
+   cairo_ft_scaled_font_t      
+   cairo_win32_scaled_font_t   
+</programlisting>
+    <para>
+      The methods on the subtypes are, however, not useful without
+      bindings for fontconfig and FreeType or for the Win32 GDI,
+      so most language bindings will choose not to bind these
+      types.
+    </para>
+    <para>
+      The <link
+         linkend="cairo-font-face-set-user-data"><function>cairo_font_face_set_user_data()</function></link>,
+      and <link
+         linkend="cairo-font-face-get-user-data"><function>cairo_font_face_get_user_data()</function></link>
+      methods are provided for use in language bindings, and should
+      not be directly exposed to applications.
+    </para>
+  </sect1>
+  <sect1 id="bindings-path">
+    <title>cairo_path_t</title>
+    <para>
+      The <link linkend="cairo-path-t"><type>cairo_path_t</type></link> type is one
+      area in which most language bindings will differ significantly
+      from the C API. The C API for <type>cairo_path_t</type> is
+      designed for efficiency and to avoid auxiliary objects that
+      would be have to be manually memory managed by the
+      application. However,
+      a language binding should not present <type>cairo_path_t</type> as an
+      array, but rather as an opaque that can be iterated
+      over. Different languages have quite different conventions for
+      how iterators work, so it is impossible to give an exact
+      specification for how this API should work, but the type names
+      and methods should be similar to the language's mapping of the following:
+    </para>
+    <programlisting>
+typedef struct cairo_path_iterator cairo_path_iterator_t;
+typedef struct cairo_path_element cairo_path_element_t;
+
+cairo_path_iterator_t *
+cairo_path_get_iterator (cairo_path_t *path);
+
+cairo_bool_t
+cairo_path_iterator_has_next (cairo_path_iterator_t *iterator);
+      
+cairo_path_element_t *
+cairo_path_iterator_next (cairo_path_iterator_t *iterator);
+
+cairo_path_element_type_t
+cairo_path_element_get_type (cairo_path_element_t *element);
+      
+void
+cairo_path_element_get_point (cairo_path_element_t *element,
+                              int                   index,
+                              double                *x,
+                              double                *y);
+    </programlisting>
+    <para>
+      The above is written using the Java conventions for
+      iterators. To illustrate how the API for PathIterator might
+      depend on the native iteration conventions of the API, examine
+      three versions of the loop, first written in a hypothetical Java
+      binding:
+    </para>
+    <programlisting>
+PathIterator iter = cr.copyPath().iterator();
+while (cr.hasNext()) {
+    PathElement element = iter.next();
+    if (element.getType() == PathElementType.MOVE_TO) {
+        Point p = element.getPoint(0);
+        doMoveTo (p.x, p.y);
+    }
+}</programlisting>
+    <para>
+      And then in a hypothetical C++ binding:
+    </para>
+    <programlisting>
+Path path = cr.copyPath();
+for (PathIterator iter = path.begin(); iter != path.end(); iter++) {
+    PathElement element = *iter;
+    if (element.getType() == PathElementType.MOVE_TO) {
+        Point p = element.getPoint(0);
+        doMoveTo (p.x, p.y);
+    }
+}</programlisting>
+    <para>
+      And then finally in a Python binding:
+    </para>
+<programlisting>
+for element in cr.copy_path():
+    if element.getType == cairo.PATH_ELEMENT_MOVE_TO:
+        (x, y) = element.getPoint(0)
+        doMoveTo (x, y);</programlisting>      
+    <para>
+      While many of the API elements stay the same in the three
+      examples, the exact iteration mechanism is quite different, to
+      match how users of the language would expect to iterate over
+      a container.
+    </para>
+    <para>
+      You should not present an API for mutating or for creating new
+      <type>cairo_path_t</type> objects. In the future, these
+      guidelines may be extended to present an API for creating a
+      <type>cairo_path_t</type> from scratch for use with
+      <link
+      linkend="cairo-append-path"><function>cairo_append_path()</function></link>
+      but the current expectation is that <function>cairo_append_path()</function> will
+      mostly be used with paths from
+      <link
+      linkend="cairo-append-path"><function>cairo_copy_path()</function></link>.
+    </para>
+  </sect1>
+</appendix>
+<!--
+Local variables:
+mode: sgml
+sgml-parent-document: ("cairo-docs.xml" "book" "book" "appendix")
+End:
+-->
diff --git a/doc/tutorial/slides/.gitignore b/doc/tutorial/slides/.gitignore
new file mode 100644 (file)
index 0000000..72a67a0
--- /dev/null
@@ -0,0 +1,7 @@
+tutorial-???.html
+tutorial-???.png
+tutorial-???.svg
+tutorial-index.xml
+tutorial.pdf
+index.html
+*~
diff --git a/doc/tutorial/slides/cairo-blank.svg b/doc/tutorial/slides/cairo-blank.svg
new file mode 100644 (file)
index 0000000..ac91186
--- /dev/null
@@ -0,0 +1,477 @@
+<?xml version="1.0" ?>
+<svg width="1024" height="768"
+    xmlns="http://www.w3.org/2000/svg"
+    xmlns:xlink="http://www.w3.org/1999/xlink"
+    xmlns:ss="http://www.svgslides.org/svgslides0.1"
+    fill="black">
+
+
+       <defs id="cairo-artwork_defs">
+               <g id="hacker_emblem">
+                       <!-- Note: This is similar though not identical to Keith Packard's SVG version
+                               of the hacker emblem (http://www.catb.org/hacker-emblem/glider.svg) -->
+                       <g id="hacker_emblem_grid" fill="white" stroke="none">
+                               <!-- Outside: Top, Right, Bottom, Left -->
+                               <rect x="-2.95" y="-3.05" width="6"   height="0.1" />
+                               <rect x="2.95"  y="-2.95" width="0.1" height="6" />
+                               <rect x="-3.05" y="2.95"  width="6"   height="0.1" />
+                               <rect x="-3.05" y="-3.05" width="0.1" height="6" />
+                               <!-- Vertical: Left, Right -->
+                               <rect x="-1.05" y="-2.95" width="0.1" height="5.9" />
+                               <rect x="0.95"  y="-2.95" width="0.1" height="5.9" />
+                               <!-- Horizontal: TopLeft, TopMiddle, TopRight -->
+                               <rect x="-2.95" y="-1.05" width="1.9" height="0.1" />
+                               <rect x="-0.95" y="-1.05" width="1.9" height="0.1" />
+                               <rect x="1.05"  y="-1.05" width="1.9" height="0.1" />
+                               <!-- Horizontal: BottomLeft, BottomMiddle, BottomRight -->
+                               <rect x="-2.95" y="0.95"  width="1.9" height="0.1" />
+                               <rect x="-0.95" y="0.95"  width="1.9" height="0.1" />
+                               <rect x="1.05"  y="0.95"  width="1.9" height="0.1" />
+                       </g>
+                       <g id="hacker_emblem_dots" fill="white">
+                               <circle cx="0"  cy="-2" r="0.7" />
+                               <circle cx="2"  cy="0"  r="0.7" />
+                               <circle cx="-2" cy="2"  r="0.7" />
+                               <circle cx="0"  cy="2"  r="0.7" />
+                               <circle cx="2"  cy="2"  r="0.7" />
+                       </g>
+               </g>
+               <g id="scarab" fill="#3B80AE">
+                       <g transform="translate(-150, -170)">
+                       <path id="scarab_head" d="M205.599,94.567c0-11.668-24.914-21.129-55.628-21.129
+                               c-30.723,0-55.624,9.46-55.624,21.129c0,10.203,24.901,7.346,55.624,7.346C180.685,101.913,205.599,104.233,205.599,94.567z"/>
+                       <path id="scarab_torso" d="M136.423,161.506c0,0,12.751,12.577,13.547,13.362
+                               c2.262-2.232,13.545-13.362,13.545-13.362c7.135-7.036,87.111-6.399,91.066-6.363c-0.469-6.298-1.254-12.472-2.325-18.519
+                               c-15.183-19.279-42.811-32.225-74.485-32.225h-55.518c-31.745,0-59.439,13.011-74.598,32.37c-1.054,6-1.829,12.128-2.296,18.374
+                               C49.321,155.106,129.288,154.47,136.423,161.506z"/>
+                       <path id="scarab_spine" d="M149.97,301.187c2.005-24.729,8.386-103.483,8.405-103.721
+                               c-0.09-0.219-6.478-15.578-8.405-20.214c-1.936,4.655-8.316,19.995-8.408,20.214C141.582,197.704,147.965,276.458,149.97,301.187z"/>
+                       <path id="scarab_wing_left" d="M140.403,197.149l8.862-21.31l-13.686-13.499
+                               c-5.65-5.573-67.074-6.235-90.259-6.019l-0.006-0.622c-0.154,2.144-0.271,4.302-0.35,6.475
+                               c-0.076,2.207,10.392,4.706,10.392,6.717c0,2.319-10.457,5.084-10.359,7.631c2.993,73.349,48.53,131.631,104.372,132.048
+                               l-9.02-111.29L140.403,197.149z"/>
+                       <path id="scarab_wing_right" d="M244.585,168.891c0-2.011,10.467-4.506,10.391-6.715
+                               c-0.079-2.174-0.195-4.332-0.351-6.479l-0.004,0.624c-23.186-0.216-84.608,0.445-90.26,6.017l-13.688,13.502l8.915,21.438
+                               l-9.017,111.29c55.854-0.417,101.378-58.698,104.373-132.049C255.04,173.976,244.585,171.209,244.585,168.891z"/>
+                       <path id="scarab_leg_front_left" d="M44.506,141.12c-4.135-0.856-4.895-1.54-7.935-2.92
+                               c-9.59-3.364-10.376-5.481-16.08-11.86c-7.426-8.306-12.661-20.142-17.1-29.463c-3.576-7.525-3.984-16.409-2.86-24.273
+                               c0.991-6.935,7.144-12.869,12.074-18.92c5.844-7.191,10.356-14.822,17.924-21.354c7.736-6.682,23.203-9.809,26.168-19.648
+                               C57.86,8.819,54.334,1.766,61.482,0c-0.366,4.703,3.639,8.477,2.397,13.575c-1.129,4.627-4.368,5.811-9.611,9.099
+                               c-7.564,4.746-18.366,8.779-24.748,13.965c-7.175,5.827-4.369,13.771-10.569,20.057c-2.001,2.03-7.901,4.706-9.137,6.83
+                               c-1.861,3.199-0.297,9.572-0.116,13.12c0.425,8.284,5.588,14.244,9.555,22.045c4.152,8.141,6.429,15.409,13.411,22.519
+                               c4.183,4.262,11.429,4.802,16.21,10.647l-3.555,4.186L44.506,141.12z"/>
+                       <path id="scarab_leg_middle_left" d="M43.94,191.922l-0.809-7.346
+                               c-9.506-4.579-10.339-9.772-20.738-12.466c-23.728-6.151-21.361,11.25-15.532,26.373c5.676,14.726,8.237,30.23,14.345,44.795
+                               c2.805,6.688,6.919,13.213,14.298,15.127c0.372-8.435-0.917-10.651-6.113-16.919c-4.395-5.293-3.326-12.548-6.072-18.504
+                               c-3.581-7.804-4.196-15.646-7.279-23.502c-1.363-3.479-8.33-13.966-6.452-17.861c3.183-6.603,9.178-0.083,12.179,2.077
+                               c4.218,3.036,6.467,2.223,11.681,2.898C34.041,186.673,37.005,188.756,43.94,191.922z"/>
+                       <path id="scarab_leg_back_left" d="M65.839,257.063l-2.771-4.837
+                               c-6.68,8.928-6.993,16.228-10.056,23.347c-5.277,12.263-0.157,28.851,9.854,37.676c6.052,5.375,15.907,9.618,23.122,13.136
+                               c10.035,4.892,20.113,11.286,31.336,13.396c2.482,0.466,8.798,1.295,6.693-3.522c-0.975-2.237-8.091-4.591-10.146-5.734
+                               c-8.312-4.623-16.377-10.524-24.142-16.176c-9.498-6.862-20.843-11.186-28.311-20.684c-3.054-3.885-3.544-4.922-2.816-9.39
+                               c0.693-4.263,1.344-9.174,2.241-13.439C61.855,266.029,63.274,261.378,65.839,257.063z"/>
+                       <path id="scarab_leg_front_right" d="M255.487,141.12c4.134-0.856,4.896-1.54,7.936-2.92
+                               c9.583-3.364,10.369-5.481,16.071-11.86c7.428-8.306,12.661-20.142,17.115-29.463c3.574-7.525,3.983-16.409,2.86-24.273
+                               c-0.992-6.935-7.157-12.869-12.087-18.92c-5.843-7.191-10.356-14.822-17.919-21.354c-7.735-6.682-23.202-9.809-26.167-19.648
+                               C242.135,8.819,245.66,1.766,238.511,0c0.366,4.703-3.637,8.477-2.396,13.575c1.131,4.627,4.368,5.811,9.611,9.099
+                               c7.563,4.746,18.367,8.779,24.747,13.965c7.17,5.827,4.362,13.771,10.563,20.057c2.001,2.03,7.901,4.706,9.139,6.83
+                               c1.859,3.199,0.295,9.572,0.113,13.12c-0.424,8.284-5.588,14.244-9.553,22.045c-4.152,8.141-6.431,15.409-13.404,22.519
+                               c-4.184,4.262-11.429,4.802-16.211,10.647l3.556,4.186L255.487,141.12z"/>
+                       <path id="scarab_leg_middle_right" d="M256.053,191.922l0.81-7.346
+                               c9.507-4.579,10.34-9.772,20.73-12.466c23.741-6.151,21.374,11.25,15.534,26.373c-5.676,14.726-8.238,30.23-14.347,44.795
+                               c-2.804,6.688-6.911,13.213-14.291,15.127c-0.371-8.435,0.918-10.651,6.113-16.919c4.39-5.293,3.319-12.548,6.066-18.504
+                               c3.58-7.804,4.197-15.646,7.278-23.502c1.363-3.479,8.33-13.966,6.453-17.861c-3.184-6.603-9.179-0.083-12.181,2.077
+                               c-4.217,3.036-6.458,2.223-11.672,2.898C265.951,186.673,262.986,188.756,256.053,191.922z"/>
+                       <path id="scarab_leg_back_right" d="M234.155,257.063l2.771-4.837
+                               c6.679,8.928,6.991,16.228,10.057,23.347c5.274,12.263,0.154,28.851-9.854,37.676c-6.055,5.375-15.903,9.618-23.117,13.136
+                               c-10.034,4.892-20.127,11.286-31.351,13.396c-2.481,0.466-8.789,1.295-6.691-3.522c0.976-2.237,8.092-4.591,10.146-5.734
+                               c8.312-4.623,16.392-10.524,24.155-16.176c9.498-6.862,20.838-11.186,28.305-20.684c3.055-3.885,3.543-4.922,2.818-9.39
+                               c-0.696-4.263-1.346-9.174-2.244-13.439C238.137,266.029,236.718,261.378,234.155,257.063z"/>
+                       </g>
+               </g>
+               <radialGradient id="gradient_radial_dung"
+                               cx="0" cy="0" r="60"
+                               fx="0" fy="0" gradientUnits="userSpaceOnUse"
+               >
+                       <stop offset="0"    stop-color="#9a9a9a" />
+                       <stop offset="0.70" stop-color="#bababa" />
+                       <stop offset="0.95" stop-color="#FFFFFF" />
+               </radialGradient>
+               <g id="dung">
+                       <circle cx="0" cy="0" r="60" fill="url(#gradient_radial_dung)" />
+                       <g transform="translate(-61, -61)">
+                               <!-- rough equivalent: <circle cx="0" cy="0" r="60" stroke="#8a8a8a" stroke-width="2" /> -->
+                               <path fill="#8a8a8a" d="M0,61c0,33.636,27.364,61,61,61s61-27.364,61-61S94.636,0,61,0S0,27.364,0,61z
+                                                       M2,61C2,28.467,28.467,2,61,2c32.532,0,59,26.467,59,59c0,32.533-26.468,59-59,59C28.467,120,2,93.533,2,61z"/>
+                       </g>
+                       <use xlink:href="#hacker_emblem" x="0" y="0" transform="scale(9)" />
+               </g>
+
+               <!-- scarab dimensions: 300x340 -->
+               <!-- dung dimensions: 120x120 (radius: 60) -->
+               <!-- scarab and dung dimensions: 300x400 -->
+
+               <g id="cairo_logo">
+                       <!-- dimensions: 300x400, centered -->
+                       <!-- The logo (scarab and dung), with the center-point of the bounding box at (0,0) -->
+                       <use xlink:href="#dung" x="0" y="0" transform="translate(0, -140)" />
+                       <use xlink:href="#scarab" x="0" y="0" transform="translate(0, 30)" />
+               </g>
+               <g id="cairo_logo_dung-centered">
+                       <!-- The logo (scarab and dung), with the dung at (0,0), the scarab below -->
+                       <use xlink:href="#dung" x="0" y="0" />
+                       <use xlink:href="#scarab" x="0" y="0" transform="translate(0,170)" />
+               </g>
+               <g id="cairo_logo_scarab-centered">
+                       <!-- The logo (scarab and dung), with the scarab's rotational center at (0,0), the dung above -->
+                       <!-- The scarab's rotational center in this case is not the center of its bounding box,
+                               but is calculated to be the intersection-point of the torso, spine and wings -->
+                       <use xlink:href="#dung" x="0" y="0" transform="translate(0, -175.85)" />
+                       <use xlink:href="#scarab" x="0" y="0" transform="translate(0, -5.85)" />
+               </g>
+               <g id="cairo_logo_top-centered">
+                       <!-- The logo (scarab and dung), with the top-center point of the bounding box at (0,0) -->
+                       <use xlink:href="#dung" x="0" y="0" transform="translate(0, 60)" />
+                       <use xlink:href="#scarab" x="0" y="0" transform="translate(0, 230)" /><!-- (0,170+60) -->
+               </g>
+               <g id="cairo_logo_bottom-centered">
+                       <!-- The logo (scarab and dung), with the bottom-center point of the bounding box at (0,0) -->
+                       <use xlink:href="#dung" x="0" y="0" transform="translate(0, -340)" />
+                       <use xlink:href="#scarab" x="0" y="0" transform="translate(0, -170)" />
+               </g>
+               <g id="cairo_logo_right-centered">
+                       <!-- The logo (scarab and dung), with the right-center point of the bounding box at (0,0) -->
+                       <use xlink:href="#dung" x="0" y="0" transform="translate(-150, -140)" />
+                       <use xlink:href="#scarab" x="0" y="0" transform="translate(-150, 30)" />
+               </g>
+               <g id="cairo_logo_left-centered">
+                       <!-- The logo (scarab and dung), with the left-center point of the bounding box at (0,0) -->
+                       <use xlink:href="#dung" x="0" y="0" transform="translate(150, -140)" />
+                       <use xlink:href="#scarab" x="0" y="0" transform="translate(150, 30)" />
+               </g>
+               <g id="cairo_logo_topleft-centered">
+                       <!-- The logo (scarab and dung), with the top-left point of the bounding box at (0,0) -->
+                       <use xlink:href="#dung" x="0" y="0" transform="translate(150, 60)" />
+                       <use xlink:href="#scarab" x="0" y="0" transform="translate(150, 230)" /><!-- (150, 170+60) -->
+               </g>
+               <g id="cairo_logo_topright-centered">
+                       <!-- The logo (scarab and dung), with the top-right point of the bounding box at (0,0) -->
+                       <use xlink:href="#dung" x="0" y="0" transform="translate(-150, 60)" />
+                       <use xlink:href="#scarab" x="0" y="0" transform="translate(-150, 230)" /><!-- (-150,170+60) -->
+               </g>
+               <g id="cairo_logo_bottomleft-centered">
+                       <!-- The logo (scarab and dung), with the bottom-left point of the bounding box at (0,0) -->
+                       <use xlink:href="#dung" x="0" y="0" transform="translate(150, -340)" />
+                       <use xlink:href="#scarab" x="0" y="0" transform="translate(150, -170)" />
+               </g>
+               <g id="cairo_logo_bottomright-centered">
+                       <!-- The logo (scarab and dung), with the bottom-right point of the bounding box at (0,0) -->
+                       <use xlink:href="#dung" x="0" y="0" transform="translate(-150, -340)" />
+                       <use xlink:href="#scarab" x="0" y="0" transform="translate(-150, -170)" />
+               </g>
+
+               <g id="cairo_text" transform="translate(0,-97)">
+                   <g transform="scale(0.1484,0.1484)"> <g transform="translate(-1139,-208.5)">
+                        <!-- 63 (c), advance 444, 0 horiBearing 38,522 -->
+                        <path transform="translate(65,0)" d="
+                              M 412, 433 
+                              C 385, 422 336, 413 298, 413 
+                              C 142, 413 38, 525 38, 680 
+                              C 38, 826 144, 947 298, 947 
+                              C 332, 947 377, 944 416, 926 
+                              L 409, 842 
+                              C 380, 861 340, 871 308, 871 
+                              C 187, 871 138, 771 138, 680 
+                              C 138, 583 197, 489 302, 489 
+                              C 332, 489 368, 496 404, 511 
+                              L 412, 433 " />
+                        <!-- 61 (a), advance 556, 0 horiBearing 46,522 -->
+                        <path transform="translate(486.75,0)" d="
+                              M 109, 541 
+                              C 147, 509 204, 489 257, 489 
+                              C 351, 489 383, 534 383, 622 
+                              C 346, 620 320, 620 283, 620 
+                              C 186, 620 46, 660 46, 788 
+                              C 46, 899 123, 947 233, 947 
+                              C 319, 947 369, 900 391, 869 
+                              L 393, 869 
+                              L 393, 935 
+                              L 481, 935 
+                              C 479, 920 477, 893 477, 835 
+                              L 477, 624 
+                              C 477, 485 418, 413 272, 413 
+                              C 207, 413 151, 433 104, 461 
+                              L 109, 541 
+                              M 383, 737 
+                              C 383, 813 334, 871 241, 871 
+                              C 198, 871 146, 842 146, 788 
+                              C 146, 698 272, 690 323, 690 
+                              C 343, 690 363, 692 383, 692 
+                              L 383, 737 " />
+                        <!-- 69 (i), advance 278, 0 horiBearing 86,730 -->
+                        <path transform="translate(1000,0)" d="
+                              M 92, 935 
+                              L 186, 935 
+                              L 186, 425 
+                              L 92, 425 
+                              L 92, 935 
+                              M 88, 261
+                              A 51, 51 0 1 1 190,261
+                              A 51, 51 0 1 1 88,261" />
+                        <!-- 72 (r), advance 389, 0 horiBearing 80,522 -->
+                        <path transform="translate(1234.25,0)" d="
+                              M 80, 935 
+                              L 174, 935 
+                              L 174, 703 
+                              C 174, 575 229, 495 313, 495 
+                              C 329, 495 348, 497 365, 504 
+                              L 365, 420 
+                              C 345, 416 331, 413 303, 413 
+                              C 249, 413 195, 451 170, 504 
+                              L 168, 504 
+                              L 168, 425 
+                              L 80, 425 
+                              L 80, 935 " />
+                        <!-- 6f (o), advance 611, 0 horiBearing 46,522 -->
+                        <path transform="translate(1610,0)" d="
+                              M 46, 680 
+                              C 46, 826 152, 947 306, 947 
+                              C 459, 947 565, 826 565, 680 
+                              C 565, 525 461, 413 306, 413 
+                              C 150, 413 46, 525 46, 680 
+                              M 146, 680 
+                              C 146, 583 205, 489 306, 489 
+                              C 406, 489 465, 583 465, 680 
+                              C 465, 771 416, 871 306, 871 
+                              C 195, 871 146, 771 146, 680 " />
+                        <!-- bounds: 38, 205 <-> 2232, 947 -->
+                   </g> </g>
+               </g>
+
+               <!-- scaled by 0.72, shifted around to hit pixel boundaries -->
+               <g id="cairo_text_small_spaced" transform="translate(0,-71)">
+                   <g transform="scale(0.085,0.085)"> <g transform="translate(-1139,-208.5)">
+                        <!-- 63 (c), advance 444, 0 horiBearing 38,522 -->
+                        <path transform="translate(-151,0)" d="
+                              M 412, 433 
+                              C 385, 422 336, 413 298, 413 
+                              C 142, 413 38, 525 38, 680 
+                              C 38, 826 144, 947 298, 947 
+                              C 332, 947 377, 944 416, 926 
+                              L 409, 842 
+                              C 380, 861 340, 871 308, 871 
+                              C 187, 871 138, 771 138, 680 
+                              C 138, 583 197, 489 302, 489 
+                              C 332, 489 368, 496 404, 511 
+                              L 412, 433 " />
+                        <!-- 61 (a), advance 556, 0 horiBearing 46,522 -->
+                        <path transform="translate(379.5,0)" d="
+                              M 109, 541 
+                              C 147, 509 204, 489 257, 489 
+                              C 351, 489 383, 534 383, 622 
+                              C 346, 620 320, 620 283, 620 
+                              C 186, 620 46, 660 46, 788 
+                              C 46, 899 123, 947 233, 947 
+                              C 319, 947 369, 900 391, 869 
+                              L 393, 869 
+                              L 393, 935 
+                              L 481, 935 
+                              C 479, 920 477, 893 477, 835 
+                              L 477, 624 
+                              C 477, 485 418, 413 272, 413 
+                              C 207, 413 151, 433 104, 461 
+                              L 109, 541 
+                              M 383, 737 
+                              C 383, 813 334, 871 241, 871 
+                              C 198, 871 146, 842 146, 788 
+                              C 146, 698 272, 690 323, 690 
+                              C 343, 690 363, 692 383, 692 
+                              L 383, 737 " />
+                        <!-- 69 (i), advance 278, 0 horiBearing 86,730 -->
+                        <path transform="translate(1000,0)" d="
+                              M 92, 935 
+                              L 186, 935 
+                              L 186, 425 
+                              L 92, 425 
+                              L 92, 935 
+                              M 88, 261
+                              A 51, 51 0 1 1 190,261
+                              A 51, 51 0 1 1 88,261" />
+                        <!-- 72 (r), advance 389, 0 horiBearing 80,522 -->
+                        <path transform="translate(1341.5,0)" d="
+                              M 80, 935 
+                              L 174, 935 
+                              L 174, 703 
+                              C 174, 575 229, 495 313, 495 
+                              C 329, 495 348, 497 365, 504 
+                              L 365, 420 
+                              C 345, 416 331, 413 303, 413 
+                              C 249, 413 195, 451 170, 504 
+                              L 168, 504 
+                              L 168, 425 
+                              L 80, 425 
+                              L 80, 935 " />
+                        <!-- 6f (o), advance 611, 0 horiBearing 46,522 -->
+                        <path transform="translate(1826,0)" d="
+                              M 46, 680 
+                              C 46, 826 152, 947 306, 947 
+                              C 459, 947 565, 826 565, 680 
+                              C 565, 525 461, 413 306, 413 
+                              C 150, 413 46, 525 46, 680 
+                              M 146, 680 
+                              C 146, 583 205, 489 306, 489 
+                              C 406, 489 465, 583 465, 680 
+                              C 465, 771 416, 871 306, 871 
+                              C 195, 871 146, 771 146, 680 " />
+                        <!-- bounds: 38, 205 <-> 2232, 947 -->
+                   </g> </g>
+               </g>
+
+
+               <!-- scaled by 0.72, shifted around to hit pixel boundaries -->
+               <g id="cairo_text_small" transform="translate(0,-71)">
+                   <g transform="scale(0.085,0.085)"> <g transform="translate(-1139,-208.5)">
+                        <!-- 63 (c), advance 444, 0 horiBearing 38,522 -->
+                        <path transform="translate(-151,0)" d="
+                              M 412, 433 
+                              C 385, 422 336, 413 298, 413 
+                              C 142, 413 38, 525 38, 680 
+                              C 38, 826 144, 947 298, 947 
+                              C 332, 947 377, 944 416, 926 
+                              L 409, 842 
+                              C 380, 861 340, 871 308, 871 
+                              C 187, 871 138, 771 138, 680 
+                              C 138, 583 197, 489 302, 489 
+                              C 332, 489 368, 496 404, 511 
+                              L 412, 433 " />
+                        <!-- 61 (a), advance 556, 0 horiBearing 46,522 -->
+                        <path transform="translate(261.75,0)" d="
+                              M 109, 541 
+                              C 147, 509 204, 489 257, 489 
+                              C 351, 489 383, 534 383, 622 
+                              C 346, 620 320, 620 283, 620 
+                              C 186, 620 46, 660 46, 788 
+                              C 46, 899 123, 947 233, 947 
+                              C 319, 947 369, 900 391, 869 
+                              L 393, 869 
+                              L 393, 935 
+                              L 481, 935 
+                              C 479, 920 477, 893 477, 835 
+                              L 477, 624 
+                              C 477, 485 418, 413 272, 413 
+                              C 207, 413 151, 433 104, 461 
+                              L 109, 541 
+                              M 383, 737 
+                              C 383, 813 334, 871 241, 871 
+                              C 198, 871 146, 842 146, 788 
+                              C 146, 698 272, 690 323, 690 
+                              C 343, 690 363, 692 383, 692 
+                              L 383, 737 " />
+                        <!-- 69 (i), advance 278, 0 horiBearing 86,730 -->
+                        <path transform="translate(764.75)" d="
+                              M 92, 935 
+                              L 186, 935 
+                              L 186, 425 
+                              L 92, 425 
+                              L 92, 935 
+                              M 88, 261
+                              A 51, 51 0 1 1 190,261
+                              A 51, 51 0 1 1 88,261" />
+                        <!-- 72 (r), advance 389, 0 horiBearing 80,522 -->
+                        <path transform="translate(988.5,0)" d="
+                              M 80, 935 
+                              L 174, 935 
+                              L 174, 703 
+                              C 174, 575 229, 495 313, 495 
+                              C 329, 495 348, 497 365, 504 
+                              L 365, 420 
+                              C 345, 416 331, 413 303, 413 
+                              C 249, 413 195, 451 170, 504 
+                              L 168, 504 
+                              L 168, 425 
+                              L 80, 425 
+                              L 80, 935 " />
+                        <!-- 6f (o), advance 611, 0 horiBearing 46,522 -->
+                        <path transform="translate(1355.5,0)" d="
+                              M 46, 680 
+                              C 46, 826 152, 947 306, 947 
+                              C 459, 947 565, 826 565, 680 
+                              C 565, 525 461, 413 306, 413 
+                              C 150, 413 46, 525 46, 680 
+                              M 146, 680 
+                              C 146, 583 205, 489 306, 489 
+                              C 406, 489 465, 583 465, 680 
+                              C 465, 771 416, 871 306, 871 
+                              C 195, 871 146, 771 146, 680 " />
+                        <!-- bounds: 38, 205 <-> 2232, 947 -->
+                   </g> </g>
+               </g>
+
+               <g id="cairo_logo_text_small">
+                       <!-- The logo on the left, the text 'cairo' on the right -->
+                       <use xlink:href="#cairo_logo_bottomleft-centered" transform="translate(0, 78), scale(0.1944)" />
+                       <use xlink:href="#cairo_text_small" transform="translate(175,82)"/>
+               </g>
+
+               <g id="cairo_logo_with_text">
+                       <!-- The logo (scarab and dung), with the text 'cairo' below, the dot of the 'i' positioned between the hind legs of the scarab -->
+                       <!-- dimensions: 300x490, centered -->
+                       <use xlink:href="#cairo_logo_top-centered" transform="translate(0, -245)" />
+                       <use xlink:href="#cairo_text" transform="translate(0, 245)" />
+               </g>
+
+               <g id="cairo_banner">
+                       <!-- The logo on the left, the text 'cairo' in the center, and a mirror image of the logo on the right -->
+                       <!-- The logos are scaled such that the scarab body nearly matches the height of the text characters (excepting the 'i')
+                               and the dung should nearly aligns with the dot of the 'i'. The bottoms of the logos are aligned with the bottom of the text. -->
+                       <!-- dimensions: 370x88, centered -->
+                       <use xlink:href="#cairo_logo_bottomleft-centered" transform="translate(-180, 40), scale(0.1944)" />
+                       <use xlink:href="#cairo_text_small" transform="translate(0, 42)" fill="black" />
+                       <use xlink:href="#cairo_logo_bottomleft-centered" transform="translate(180, 40), scale(0.1944), scale(-1, 1)" />
+               </g>
+
+               <g id="freedesktop_org_logo" style="fill:#FFFFFF;stroke:#3B80AE;stroke-width:2.4588;">
+                 <g>
+                   <path style="stroke:#BABABA;" d="M85.277,40.796c2.058,7.884-2.667,15.942-10.551,17.999L27.143,71.21c-7.884,2.057-15.943-2.667-18-10.552
+                                                    l-7.448-28.55c-2.057-7.884,2.667-15.942,10.551-17.999L59.83,1.695c7.884-2.057,15.942,2.667,17.999,10.551
+                                                    l7.449,28.55z"/>>
+                   <path style="fill:#3B80AE;stroke:none;" d="M80.444,39.778c1.749,7.854-1.816,13.621-9.504,15.447l-42.236,11.02c-7.569,2.396-14.089-1.181
+                                                              -15.838-8.836L6.53,33.127c-1.749-8.145,0.709-12.889,9.503-15.447L58.27,6.661
+                                                              c8.144-1.826,14.089,1.363,15.838,8.835l6.336,24.282z"/>>
+                   </g>g>
+                   <path style="opacity:0.5;fill:none;stroke:#FFFFFF;" d="M45.542,51.793L24.104,31.102l38.1-4.393L45.542,51.793z"/>>
+                   <path d="M72.325,28.769c0.405,1.55-0.525,3.136-2.075,3.541l-12.331,3.217c-1.551,0.404-3.137-0.525-3.542-2.076l-2.295-8.801
+                            c-0.405-1.551,0.524-3.137,2.076-3.542l12.33-3.217c1.551-0.405,3.137,0.525,3.542,2.076l2.295,8.801z"/>>
+                   <path d="M36.51,33.625c0.496,1.9-0.645,3.844-2.545,4.34l-15.112,3.943c-1.901,0.496-3.845-0.644-4.34-2.544l-2.814-10.786
+                            c-0.496-1.901,0.644-3.844,2.544-4.34l15.113-3.942c1.901-0.496,3.845,0.643,4.34,2.544l2.814,10.786z"/>>
+                   <path d="M52.493,53.208c0.278,1.065-0.36,2.154-1.425,2.432L42.6,57.848c-1.064,0.277-2.153-0.36-2.431-1.426l-1.577-6.043
+                            c-0.277-1.064,0.36-2.153,1.425-2.432l8.468-2.209c1.064-0.277,2.154,0.361,2.431,1.426l1.577,6.043z"/>>
+                   </g>g>
+       </defs>
+
+  <!-- Blue bar at top of slide -->
+  <rect x="0" y="0" width="1024" height="170" fill="#162284" />
+  
+  <g font-family="Frutiger">
+    <!-- Slide title -->
+    <g id="slide_title" transform="translate(512, 133)">
+      <text text-anchor="middle"
+           fill="white"
+           font-weight="bold"
+           x="0"
+           y="4" font-size="55"
+           ss:variable="title">Slide Title</text>
+    </g>
+    
+    <!-- Slide content -->
+    <g ss:region="default">
+      <rect x="112" y="200" width="800" height="480" fill="none" stroke="blue"/>
+      <text font-size="35" fill="black"
+           x="112" y="232">Slide content</text>
+    </g>
+    
+    <!-- Footer -->
+    <text ss:variable="URL" x="1016" y="753" text-anchor="end" font-size="20">http://cairographics.org</text>
+  </g>
+
+</svg>
diff --git a/doc/tutorial/slides/cairo-code.svg b/doc/tutorial/slides/cairo-code.svg
new file mode 100644 (file)
index 0000000..09e26bf
--- /dev/null
@@ -0,0 +1,913 @@
+<?xml version="1.0" ?>
+<svg width="1024" height="768"
+    xmlns="http://www.w3.org/2000/svg"
+    xmlns:xlink="http://www.w3.org/1999/xlink"
+    xmlns:ss="http://www.svgslides.org/svgslides0.1"
+    fill="black">
+
+
+
+       <defs id="cairo-artwork_defs">
+               <g id="hacker_emblem">
+                       <!-- Note: This is similar though not identical to Keith Packard's SVG version
+                               of the hacker emblem (http://www.catb.org/hacker-emblem/glider.svg) -->
+                       <g id="hacker_emblem_grid" fill="white" stroke="none">
+                               <!-- Outside: Top, Right, Bottom, Left -->
+                               <rect x="-2.95" y="-3.05" width="6"   height="0.1" />
+                               <rect x="2.95"  y="-2.95" width="0.1" height="6" />
+                               <rect x="-3.05" y="2.95"  width="6"   height="0.1" />
+                               <rect x="-3.05" y="-3.05" width="0.1" height="6" />
+                               <!-- Vertical: Left, Right -->
+                               <rect x="-1.05" y="-2.95" width="0.1" height="5.9" />
+                               <rect x="0.95"  y="-2.95" width="0.1" height="5.9" />
+                               <!-- Horizontal: TopLeft, TopMiddle, TopRight -->
+                               <rect x="-2.95" y="-1.05" width="1.9" height="0.1" />
+                               <rect x="-0.95" y="-1.05" width="1.9" height="0.1" />
+                               <rect x="1.05"  y="-1.05" width="1.9" height="0.1" />
+                               <!-- Horizontal: BottomLeft, BottomMiddle, BottomRight -->
+                               <rect x="-2.95" y="0.95"  width="1.9" height="0.1" />
+                               <rect x="-0.95" y="0.95"  width="1.9" height="0.1" />
+                               <rect x="1.05"  y="0.95"  width="1.9" height="0.1" />
+                       </g>
+                       <g id="hacker_emblem_dots" fill="white">
+                               <circle cx="0"  cy="-2" r="0.7" />
+                               <circle cx="2"  cy="0"  r="0.7" />
+                               <circle cx="-2" cy="2"  r="0.7" />
+                               <circle cx="0"  cy="2"  r="0.7" />
+                               <circle cx="2"  cy="2"  r="0.7" />
+                       </g>
+               </g>
+               <g id="scarab" fill="#f19a14">
+                       <g transform="translate(-150, -170)">
+                       <path id="scarab_head" d="M205.599,94.567c0-11.668-24.914-21.129-55.628-21.129
+                               c-30.723,0-55.624,9.46-55.624,21.129c0,10.203,24.901,7.346,55.624,7.346C180.685,101.913,205.599,104.233,205.599,94.567z"/>
+                       <path id="scarab_torso" d="M136.423,161.506c0,0,12.751,12.577,13.547,13.362
+                               c2.262-2.232,13.545-13.362,13.545-13.362c7.135-7.036,87.111-6.399,91.066-6.363c-0.469-6.298-1.254-12.472-2.325-18.519
+                               c-15.183-19.279-42.811-32.225-74.485-32.225h-55.518c-31.745,0-59.439,13.011-74.598,32.37c-1.054,6-1.829,12.128-2.296,18.374
+                               C49.321,155.106,129.288,154.47,136.423,161.506z"/>
+                       <path id="scarab_spine" d="M149.97,301.187c2.005-24.729,8.386-103.483,8.405-103.721
+                               c-0.09-0.219-6.478-15.578-8.405-20.214c-1.936,4.655-8.316,19.995-8.408,20.214C141.582,197.704,147.965,276.458,149.97,301.187z"/>
+                       <path id="scarab_wing_left" d="M140.403,197.149l8.862-21.31l-13.686-13.499
+                               c-5.65-5.573-67.074-6.235-90.259-6.019l-0.006-0.622c-0.154,2.144-0.271,4.302-0.35,6.475
+                               c-0.076,2.207,10.392,4.706,10.392,6.717c0,2.319-10.457,5.084-10.359,7.631c2.993,73.349,48.53,131.631,104.372,132.048
+                               l-9.02-111.29L140.403,197.149z"/>
+                       <path id="scarab_wing_right" d="M244.585,168.891c0-2.011,10.467-4.506,10.391-6.715
+                               c-0.079-2.174-0.195-4.332-0.351-6.479l-0.004,0.624c-23.186-0.216-84.608,0.445-90.26,6.017l-13.688,13.502l8.915,21.438
+                               l-9.017,111.29c55.854-0.417,101.378-58.698,104.373-132.049C255.04,173.976,244.585,171.209,244.585,168.891z"/>
+                       <path id="scarab_leg_front_left" d="M44.506,141.12c-4.135-0.856-4.895-1.54-7.935-2.92
+                               c-9.59-3.364-10.376-5.481-16.08-11.86c-7.426-8.306-12.661-20.142-17.1-29.463c-3.576-7.525-3.984-16.409-2.86-24.273
+                               c0.991-6.935,7.144-12.869,12.074-18.92c5.844-7.191,10.356-14.822,17.924-21.354c7.736-6.682,23.203-9.809,26.168-19.648
+                               C57.86,8.819,54.334,1.766,61.482,0c-0.366,4.703,3.639,8.477,2.397,13.575c-1.129,4.627-4.368,5.811-9.611,9.099
+                               c-7.564,4.746-18.366,8.779-24.748,13.965c-7.175,5.827-4.369,13.771-10.569,20.057c-2.001,2.03-7.901,4.706-9.137,6.83
+                               c-1.861,3.199-0.297,9.572-0.116,13.12c0.425,8.284,5.588,14.244,9.555,22.045c4.152,8.141,6.429,15.409,13.411,22.519
+                               c4.183,4.262,11.429,4.802,16.21,10.647l-3.555,4.186L44.506,141.12z"/>
+                       <path id="scarab_leg_middle_left" d="M43.94,191.922l-0.809-7.346
+                               c-9.506-4.579-10.339-9.772-20.738-12.466c-23.728-6.151-21.361,11.25-15.532,26.373c5.676,14.726,8.237,30.23,14.345,44.795
+                               c2.805,6.688,6.919,13.213,14.298,15.127c0.372-8.435-0.917-10.651-6.113-16.919c-4.395-5.293-3.326-12.548-6.072-18.504
+                               c-3.581-7.804-4.196-15.646-7.279-23.502c-1.363-3.479-8.33-13.966-6.452-17.861c3.183-6.603,9.178-0.083,12.179,2.077
+                               c4.218,3.036,6.467,2.223,11.681,2.898C34.041,186.673,37.005,188.756,43.94,191.922z"/>
+                       <path id="scarab_leg_back_left" d="M65.839,257.063l-2.771-4.837
+                               c-6.68,8.928-6.993,16.228-10.056,23.347c-5.277,12.263-0.157,28.851,9.854,37.676c6.052,5.375,15.907,9.618,23.122,13.136
+                               c10.035,4.892,20.113,11.286,31.336,13.396c2.482,0.466,8.798,1.295,6.693-3.522c-0.975-2.237-8.091-4.591-10.146-5.734
+                               c-8.312-4.623-16.377-10.524-24.142-16.176c-9.498-6.862-20.843-11.186-28.311-20.684c-3.054-3.885-3.544-4.922-2.816-9.39
+                               c0.693-4.263,1.344-9.174,2.241-13.439C61.855,266.029,63.274,261.378,65.839,257.063z"/>
+                       <path id="scarab_leg_front_right" d="M255.487,141.12c4.134-0.856,4.896-1.54,7.936-2.92
+                               c9.583-3.364,10.369-5.481,16.071-11.86c7.428-8.306,12.661-20.142,17.115-29.463c3.574-7.525,3.983-16.409,2.86-24.273
+                               c-0.992-6.935-7.157-12.869-12.087-18.92c-5.843-7.191-10.356-14.822-17.919-21.354c-7.735-6.682-23.202-9.809-26.167-19.648
+                               C242.135,8.819,245.66,1.766,238.511,0c0.366,4.703-3.637,8.477-2.396,13.575c1.131,4.627,4.368,5.811,9.611,9.099
+                               c7.563,4.746,18.367,8.779,24.747,13.965c7.17,5.827,4.362,13.771,10.563,20.057c2.001,2.03,7.901,4.706,9.139,6.83
+                               c1.859,3.199,0.295,9.572,0.113,13.12c-0.424,8.284-5.588,14.244-9.553,22.045c-4.152,8.141-6.431,15.409-13.404,22.519
+                               c-4.184,4.262-11.429,4.802-16.211,10.647l3.556,4.186L255.487,141.12z"/>
+                       <path id="scarab_leg_middle_right" d="M256.053,191.922l0.81-7.346
+                               c9.507-4.579,10.34-9.772,20.73-12.466c23.741-6.151,21.374,11.25,15.534,26.373c-5.676,14.726-8.238,30.23-14.347,44.795
+                               c-2.804,6.688-6.911,13.213-14.291,15.127c-0.371-8.435,0.918-10.651,6.113-16.919c4.39-5.293,3.319-12.548,6.066-18.504
+                               c3.58-7.804,4.197-15.646,7.278-23.502c1.363-3.479,8.33-13.966,6.453-17.861c-3.184-6.603-9.179-0.083-12.181,2.077
+                               c-4.217,3.036-6.458,2.223-11.672,2.898C265.951,186.673,262.986,188.756,256.053,191.922z"/>
+                       <path id="scarab_leg_back_right" d="M234.155,257.063l2.771-4.837
+                               c6.679,8.928,6.991,16.228,10.057,23.347c5.274,12.263,0.154,28.851-9.854,37.676c-6.055,5.375-15.903,9.618-23.117,13.136
+                               c-10.034,4.892-20.127,11.286-31.351,13.396c-2.481,0.466-8.789,1.295-6.691-3.522c0.976-2.237,8.092-4.591,10.146-5.734
+                               c8.312-4.623,16.392-10.524,24.155-16.176c9.498-6.862,20.838-11.186,28.305-20.684c3.055-3.885,3.543-4.922,2.818-9.39
+                               c-0.696-4.263-1.346-9.174-2.244-13.439C238.137,266.029,236.718,261.378,234.155,257.063z"/>
+                       </g>
+               </g>
+               <radialGradient id="gradient_radial_dung"
+                               cx="0" cy="0" r="60"
+                               fx="0" fy="0" gradientUnits="userSpaceOnUse"
+               >
+                       <stop offset="0"    stop-color="#9a9a9a" />
+                       <stop offset="0.70" stop-color="#bababa" />
+                       <stop offset="0.95" stop-color="#FFFFFF" />
+               </radialGradient>
+               <g id="dung">
+                       <circle cx="0" cy="0" r="60" fill="url(#gradient_radial_dung)" />
+                       <g transform="translate(-61, -61)">
+                               <!-- rough equivalent: <circle cx="0" cy="0" r="60" stroke="#8a8a8a" stroke-width="2" /> -->
+                               <path fill="#8a8a8a" d="M0,61c0,33.636,27.364,61,61,61s61-27.364,61-61S94.636,0,61,0S0,27.364,0,61z
+                                                       M2,61C2,28.467,28.467,2,61,2c32.532,0,59,26.467,59,59c0,32.533-26.468,59-59,59C28.467,120,2,93.533,2,61z"/>
+                       </g>
+                       <use xlink:href="#hacker_emblem" x="0" y="0" transform="scale(9)" />
+               </g>
+               <g id="dung_2_color">
+                       <!-- This would be simple a circle like so:
+                               <circle cx="0" cy="0" r="48" stroke-width="2" fill="none" stroke="white"/>
+                            but there appears to currently be a bug in the cairo
+                            PDF backend that results in an ugly spike in that
+                            case. So we use 8 splines instead.
+                       -->
+                       <path stroke-width="4" stroke="white" fill="none" d="
+M 48,                  0
+C 48,                  12.730391512298112, 
+  42.94287166245995,   24.939379331448613
+  33.941125496954285,  33.941125496954278
+C 24.939379331448624,  42.942871662459943
+  12.730391512298114,  48
+  0,                   48
+C -12.730391512298109, 48, 
+  -24.939379331448613, 42.94287166245995
+  -33.941125496954278, 33.941125496954285
+C -42.942871662459943, 24.939379331448624,
+  -48,                 12.730391512298118,
+  -48,                 0,
+C -48,                 -12.730391512298105, 
+  -42.94287166245995,  -24.939379331448613,
+  -33.941125496954285, -33.941125496954278
+C -24.939379331448624, -42.942871662459943,
+  -12.730391512298119, -48, 
+  0,                   -48
+C 12.730391512298104,  -48, 
+  24.939379331448606,  -42.942871662459943,
+  33.941125496954271,  -33.941125496954285
+C 42.942871662459936,  -24.939379331448624,
+  48,                  -12.730391512298123,
+  48,                  0" />
+                       <g transform="scale(9)" fill="white" stroke="none" stroke-width="0.22222">
+                               <!-- Hacker emblem grid -->
+                               <!--
+                               <path stroke="white" fill="none"
+                                     d="M -3,-3 L  3,-3 L 3,3 L-3,3 Z
+                                        M -1,-3 L -1, 3
+                                        M  1,-3 L  1, 3
+                                        M -3,-1 L  3,-1
+                                        M -3, 1 L  3, 1"/>
+                                        -->
+
+                               <!-- Hacker emblem dots -->
+                               <circle cx="0"  cy="-2" r="0.7" />
+                               <circle cx="2"  cy="0"  r="0.7" />
+                               <circle cx="-2" cy="2"  r="0.7" />
+                               <circle cx="0"  cy="2"  r="0.7" />
+                               <circle cx="2"  cy="2"  r="0.7" />
+                       </g>
+               </g>
+
+               <!-- scarab dimensions: 300x340 -->
+               <!-- dung dimensions: 120x120 (radius: 60) -->
+               <!-- scarab and dung dimensions: 300x400 -->
+
+               <g id="cairo_logo">
+                       <!-- dimensions: 300x400, centered -->
+                       <!-- The logo (scarab and dung), with the center-point of the bounding box at (0,0) -->
+                       <use xlink:href="#dung_2_color" x="0" y="0" transform="translate(0, -140)" />
+                       <use xlink:href="#scarab" x="0" y="0" transform="translate(0, 30)" />
+               </g>
+               <g id="cairo_logo_dung-centered">
+                       <!-- The logo (scarab and dung), with the dung at (0,0), the scarab below -->
+                       <use xlink:href="#dung_2_color" x="0" y="0" />
+                       <use xlink:href="#scarab" x="0" y="0" transform="translate(0,170)" />
+               </g>
+               <g id="cairo_logo_scarab-centered">
+                       <!-- The logo (scarab and dung), with the scarab's rotational center at (0,0), the dung above -->
+                       <!-- The scarab's rotational center in this case is not the center of its bounding box,
+                               but is calculated to be the intersection-point of the torso, spine and wings -->
+                       <use xlink:href="#dung_2_color" x="0" y="0" transform="translate(0, -175.85)" />
+                       <use xlink:href="#scarab" x="0" y="0" transform="translate(0, -5.85)" />
+               </g>
+               <g id="cairo_logo_top-centered">
+                       <!-- The logo (scarab and dung), with the top-center point of the bounding box at (0,0) -->
+                       <use xlink:href="#dung_2_color" x="0" y="0" transform="translate(0, 60)" />
+                       <use xlink:href="#scarab" x="0" y="0" transform="translate(0, 230)" /><!-- (0,170+60) -->
+               </g>
+               <g id="cairo_logo_bottom-centered">
+                       <!-- The logo (scarab and dung), with the bottom-center point of the bounding box at (0,0) -->
+                       <use xlink:href="#dung_2_color" x="0" y="0" transform="translate(0, -340)" />
+                       <use xlink:href="#scarab" x="0" y="0" transform="translate(0, -170)" />
+               </g>
+               <g id="cairo_logo_right-centered">
+                       <!-- The logo (scarab and dung), with the right-center point of the bounding box at (0,0) -->
+                       <use xlink:href="#dung_2_color" x="0" y="0" transform="translate(-150, -140)" />
+                       <use xlink:href="#scarab" x="0" y="0" transform="translate(-150, 30)" />
+               </g>
+               <g id="cairo_logo_left-centered">
+                       <!-- The logo (scarab and dung), with the left-center point of the bounding box at (0,0) -->
+                       <use xlink:href="#dung_2_color" x="0" y="0" transform="translate(150, -140)" />
+                       <use xlink:href="#scarab" x="0" y="0" transform="translate(150, 30)" />
+               </g>
+               <g id="cairo_logo_topleft-centered">
+                       <!-- The logo (scarab and dung), with the top-left point of the bounding box at (0,0) -->
+                       <use xlink:href="#dung_2_color" x="0" y="0" transform="translate(150, 60)" />
+                       <use xlink:href="#scarab" x="0" y="0" transform="translate(150, 230)" /><!-- (150, 170+60) -->
+               </g>
+               <g id="cairo_logo_topright-centered">
+                       <!-- The logo (scarab and dung), with the top-right point of the bounding box at (0,0) -->
+                       <use xlink:href="#dung_2_color" x="0" y="0" transform="translate(-150, 60)" />
+                       <use xlink:href="#scarab" x="0" y="0" transform="translate(-150, 230)" /><!-- (-150,170+60) -->
+               </g>
+               <g id="cairo_logo_bottomleft-centered">
+                       <!-- The logo (scarab and dung), with the bottom-left point of the bounding box at (0,0) -->
+                       <use xlink:href="#dung_2_color" x="0" y="0" transform="translate(150, -340)" />
+                       <use xlink:href="#scarab" x="0" y="0" transform="translate(150, -170)" />
+               </g>
+               <g id="cairo_logo_bottomright-centered">
+                       <!-- The logo (scarab and dung), with the bottom-right point of the bounding box at (0,0) -->
+                       <use xlink:href="#dung_2_color" x="0" y="0" transform="translate(-150, -340)" />
+                       <use xlink:href="#scarab" x="0" y="0" transform="translate(-150, -170)" />
+               </g>
+
+               <g id="cairo_text" transform="translate(0,-97)">
+                   <g transform="scale(0.1484,0.1484)"> <g transform="translate(-1139,-208.5)">
+                        <!-- 63 (c), advance 444, 0 horiBearing 38,522 -->
+                        <path transform="translate(65,0)" d="
+                              M 412, 433 
+                              C 385, 422 336, 413 298, 413 
+                              C 142, 413 38, 525 38, 680 
+                              C 38, 826 144, 947 298, 947 
+                              C 332, 947 377, 944 416, 926 
+                              L 409, 842 
+                              C 380, 861 340, 871 308, 871 
+                              C 187, 871 138, 771 138, 680 
+                              C 138, 583 197, 489 302, 489 
+                              C 332, 489 368, 496 404, 511 
+                              L 412, 433 " />
+                        <!-- 61 (a), advance 556, 0 horiBearing 46,522 -->
+                        <path transform="translate(486.75,0)" d="
+                              M 109, 541 
+                              C 147, 509 204, 489 257, 489 
+                              C 351, 489 383, 534 383, 622 
+                              C 346, 620 320, 620 283, 620 
+                              C 186, 620 46, 660 46, 788 
+                              C 46, 899 123, 947 233, 947 
+                              C 319, 947 369, 900 391, 869 
+                              L 393, 869 
+                              L 393, 935 
+                              L 481, 935 
+                              C 479, 920 477, 893 477, 835 
+                              L 477, 624 
+                              C 477, 485 418, 413 272, 413 
+                              C 207, 413 151, 433 104, 461 
+                              L 109, 541 
+                              M 383, 737 
+                              C 383, 813 334, 871 241, 871 
+                              C 198, 871 146, 842 146, 788 
+                              C 146, 698 272, 690 323, 690 
+                              C 343, 690 363, 692 383, 692 
+                              L 383, 737 " />
+                        <!-- 69 (i), advance 278, 0 horiBearing 86,730 -->
+                        <path transform="translate(1000,0)" d="
+                              M 92, 935 
+                              L 186, 935 
+                              L 186, 425 
+                              L 92, 425 
+                              L 92, 935 
+                              M 88, 261
+                              A 51, 51 0 1 1 190,261
+                              A 51, 51 0 1 1 88,261" />
+                        <!-- 72 (r), advance 389, 0 horiBearing 80,522 -->
+                        <path transform="translate(1234.25,0)" d="
+                              M 80, 935 
+                              L 174, 935 
+                              L 174, 703 
+                              C 174, 575 229, 495 313, 495 
+                              C 329, 495 348, 497 365, 504 
+                              L 365, 420 
+                              C 345, 416 331, 413 303, 413 
+                              C 249, 413 195, 451 170, 504 
+                              L 168, 504 
+                              L 168, 425 
+                              L 80, 425 
+                              L 80, 935 " />
+                        <!-- 6f (o), advance 611, 0 horiBearing 46,522 -->
+                        <path transform="translate(1610,0)" d="
+                              M 46, 680 
+                              C 46, 826 152, 947 306, 947 
+                              C 459, 947 565, 826 565, 680 
+                              C 565, 525 461, 413 306, 413 
+                              C 150, 413 46, 525 46, 680 
+                              M 146, 680 
+                              C 146, 583 205, 489 306, 489 
+                              C 406, 489 465, 583 465, 680 
+                              C 465, 771 416, 871 306, 871 
+                              C 195, 871 146, 771 146, 680 " />
+                        <!-- bounds: 38, 205 <-> 2232, 947 -->
+                   </g> </g>
+               </g>
+
+               <!-- scaled by 0.72, shifted around to hit pixel boundaries -->
+               <g id="cairo_text_small_spaced" transform="translate(0,-71)">
+                   <g transform="scale(0.085,0.085)"> <g transform="translate(-1139,-208.5)">
+                        <!-- 63 (c), advance 444, 0 horiBearing 38,522 -->
+                        <path transform="translate(-151,0)" d="
+                              M 412, 433 
+                              C 385, 422 336, 413 298, 413 
+                              C 142, 413 38, 525 38, 680 
+                              C 38, 826 144, 947 298, 947 
+                              C 332, 947 377, 944 416, 926 
+                              L 409, 842 
+                              C 380, 861 340, 871 308, 871 
+                              C 187, 871 138, 771 138, 680 
+                              C 138, 583 197, 489 302, 489 
+                              C 332, 489 368, 496 404, 511 
+                              L 412, 433 " />
+                        <!-- 61 (a), advance 556, 0 horiBearing 46,522 -->
+                        <path transform="translate(379.5,0)" d="
+                              M 109, 541 
+                              C 147, 509 204, 489 257, 489 
+                              C 351, 489 383, 534 383, 622 
+                              C 346, 620 320, 620 283, 620 
+                              C 186, 620 46, 660 46, 788 
+                              C 46, 899 123, 947 233, 947 
+                              C 319, 947 369, 900 391, 869 
+                              L 393, 869 
+                              L 393, 935 
+                              L 481, 935 
+                              C 479, 920 477, 893 477, 835 
+                              L 477, 624 
+                              C 477, 485 418, 413 272, 413 
+                              C 207, 413 151, 433 104, 461 
+                              L 109, 541 
+                              M 383, 737 
+                              C 383, 813 334, 871 241, 871 
+                              C 198, 871 146, 842 146, 788 
+                              C 146, 698 272, 690 323, 690 
+                              C 343, 690 363, 692 383, 692 
+                              L 383, 737 " />
+                        <!-- 69 (i), advance 278, 0 horiBearing 86,730 -->
+                        <path transform="translate(1000,0)" d="
+                              M 92, 935 
+                              L 186, 935 
+                              L 186, 425 
+                              L 92, 425 
+                              L 92, 935 
+                              M 88, 261
+                              A 51, 51 0 1 1 190,261
+                              A 51, 51 0 1 1 88,261" />
+                        <!-- 72 (r), advance 389, 0 horiBearing 80,522 -->
+                        <path transform="translate(1341.5,0)" d="
+                              M 80, 935 
+                              L 174, 935 
+                              L 174, 703 
+                              C 174, 575 229, 495 313, 495 
+                              C 329, 495 348, 497 365, 504 
+                              L 365, 420 
+                              C 345, 416 331, 413 303, 413 
+                              C 249, 413 195, 451 170, 504 
+                              L 168, 504 
+                              L 168, 425 
+                              L 80, 425 
+                              L 80, 935 " />
+                        <!-- 6f (o), advance 611, 0 horiBearing 46,522 -->
+                        <path transform="translate(1826,0)" d="
+                              M 46, 680 
+                              C 46, 826 152, 947 306, 947 
+                              C 459, 947 565, 826 565, 680 
+                              C 565, 525 461, 413 306, 413 
+                              C 150, 413 46, 525 46, 680 
+                              M 146, 680 
+                              C 146, 583 205, 489 306, 489 
+                              C 406, 489 465, 583 465, 680 
+                              C 465, 771 416, 871 306, 871 
+                              C 195, 871 146, 771 146, 680 " />
+                        <!-- bounds: 38, 205 <-> 2232, 947 -->
+                   </g> </g>
+               </g>
+
+
+               <!-- scaled by 0.72, shifted around to hit pixel boundaries -->
+               <g id="cairo_text_small" transform="translate(0,-71)">
+                   <g transform="scale(0.085,0.085)"> <g transform="translate(-1139,-208.5)">
+                        <!-- 63 (c), advance 444, 0 horiBearing 38,522 -->
+                        <path transform="translate(-151,0)" d="
+                              M 412, 433 
+                              C 385, 422 336, 413 298, 413 
+                              C 142, 413 38, 525 38, 680 
+                              C 38, 826 144, 947 298, 947 
+                              C 332, 947 377, 944 416, 926 
+                              L 409, 842 
+                              C 380, 861 340, 871 308, 871 
+                              C 187, 871 138, 771 138, 680 
+                              C 138, 583 197, 489 302, 489 
+                              C 332, 489 368, 496 404, 511 
+                              L 412, 433 " />
+                        <!-- 61 (a), advance 556, 0 horiBearing 46,522 -->
+                        <path transform="translate(261.75,0)" d="
+                              M 109, 541 
+                              C 147, 509 204, 489 257, 489 
+                              C 351, 489 383, 534 383, 622 
+                              C 346, 620 320, 620 283, 620 
+                              C 186, 620 46, 660 46, 788 
+                              C 46, 899 123, 947 233, 947 
+                              C 319, 947 369, 900 391, 869 
+                              L 393, 869 
+                              L 393, 935 
+                              L 481, 935 
+                              C 479, 920 477, 893 477, 835 
+                              L 477, 624 
+                              C 477, 485 418, 413 272, 413 
+                              C 207, 413 151, 433 104, 461 
+                              L 109, 541 
+                              M 383, 737 
+                              C 383, 813 334, 871 241, 871 
+                              C 198, 871 146, 842 146, 788 
+                              C 146, 698 272, 690 323, 690 
+                              C 343, 690 363, 692 383, 692 
+                              L 383, 737 " />
+                        <!-- 69 (i), advance 278, 0 horiBearing 86,730 -->
+                        <path transform="translate(764.75)" d="
+                              M 92, 935 
+                              L 186, 935 
+                              L 186, 425 
+                              L 92, 425 
+                              L 92, 935 
+                              M 88, 261
+                              A 51, 51 0 1 1 190,261
+                              A 51, 51 0 1 1 88,261" />
+                        <!-- 72 (r), advance 389, 0 horiBearing 80,522 -->
+                        <path transform="translate(988.5,0)" d="
+                              M 80, 935 
+                              L 174, 935 
+                              L 174, 703 
+                              C 174, 575 229, 495 313, 495 
+                              C 329, 495 348, 497 365, 504 
+                              L 365, 420 
+                              C 345, 416 331, 413 303, 413 
+                              C 249, 413 195, 451 170, 504 
+                              L 168, 504 
+                              L 168, 425 
+                              L 80, 425 
+                              L 80, 935 " />
+                        <!-- 6f (o), advance 611, 0 horiBearing 46,522 -->
+                        <path transform="translate(1355.5,0)" d="
+                              M 46, 680 
+                              C 46, 826 152, 947 306, 947 
+                              C 459, 947 565, 826 565, 680 
+                              C 565, 525 461, 413 306, 413 
+                              C 150, 413 46, 525 46, 680 
+                              M 146, 680 
+                              C 146, 583 205, 489 306, 489 
+                              C 406, 489 465, 583 465, 680 
+                              C 465, 771 416, 871 306, 871 
+                              C 195, 871 146, 771 146, 680 " />
+                        <!-- bounds: 38, 205 <-> 2232, 947 -->
+                   </g> </g>
+               </g>
+
+               <g id="cairo_logo_text_small">
+                       <!-- The logo on the left, the text 'cairo' on the right -->
+                       <use xlink:href="#cairo_logo_bottomleft-centered" transform="translate(0, 78), scale(0.1944)" />
+                       <use xlink:href="#cairo_text_small" fill="white" transform="translate(175,82)"/>
+               </g>
+
+               <g id="cairo_logo_with_text">
+                       <!-- The logo (scarab and dung), with the text 'cairo' below, the dot of the 'i' positioned between the hind legs of the scarab -->
+                       <!-- dimensions: 300x490, centered -->
+                       <use xlink:href="#cairo_logo_top-centered" transform="translate(0, -245)" />
+                       <use xlink:href="#cairo_text" transform="translate(0, 245)" />
+               </g>
+
+               <g id="cairo_banner">
+                       <!-- The logo on the left, the text 'cairo' in the center, and a mirror image of the logo on the right -->
+                       <!-- The logos are scaled such that the scarab body nearly matches the height of the text characters (excepting the 'i')
+                               and the dung should nearly aligns with the dot of the 'i'. The bottoms of the logos are aligned with the bottom of the text. -->
+                       <!-- dimensions: 370x88, centered -->
+                       <use xlink:href="#cairo_logo_bottomleft-centered" transform="translate(-180, 40), scale(0.1944)" />
+                       <use xlink:href="#cairo_text_small" transform="translate(0, 42)" fill="black" />
+                       <use xlink:href="#cairo_logo_bottomleft-centered" transform="translate(180, 40), scale(0.1944), scale(-1, 1)" />
+               </g>
+
+               <g id="freedesktop_org_logo" style="fill:#FFFFFF;stroke:#3B80AE;stroke-width:2.4588;">
+                 <g>
+                   <path style="stroke:#BABABA;" d="M85.277,40.796c2.058,7.884-2.667,15.942-10.551,17.999L27.143,71.21c-7.884,2.057-15.943-2.667-18-10.552
+                                                    l-7.448-28.55c-2.057-7.884,2.667-15.942,10.551-17.999L59.83,1.695c7.884-2.057,15.942,2.667,17.999,10.551
+                                                    l7.449,28.55z"/>>
+                   <path style="fill:#3B80AE;stroke:none;" d="M80.444,39.778c1.749,7.854-1.816,13.621-9.504,15.447l-42.236,11.02c-7.569,2.396-14.089-1.181
+                                                              -15.838-8.836L6.53,33.127c-1.749-8.145,0.709-12.889,9.503-15.447L58.27,6.661
+                                                              c8.144-1.826,14.089,1.363,15.838,8.835l6.336,24.282z"/>>
+                   </g>g>
+                   <path style="opacity:0.5;fill:none;stroke:#FFFFFF;" d="M45.542,51.793L24.104,31.102l38.1-4.393L45.542,51.793z"/>>
+                   <path d="M72.325,28.769c0.405,1.55-0.525,3.136-2.075,3.541l-12.331,3.217c-1.551,0.404-3.137-0.525-3.542-2.076l-2.295-8.801
+                            c-0.405-1.551,0.524-3.137,2.076-3.542l12.33-3.217c1.551-0.405,3.137,0.525,3.542,2.076l2.295,8.801z"/>>
+                   <path d="M36.51,33.625c0.496,1.9-0.645,3.844-2.545,4.34l-15.112,3.943c-1.901,0.496-3.845-0.644-4.34-2.544l-2.814-10.786
+                            c-0.496-1.901,0.644-3.844,2.544-4.34l15.113-3.942c1.901-0.496,3.845,0.643,4.34,2.544l2.814,10.786z"/>>
+                   <path d="M52.493,53.208c0.278,1.065-0.36,2.154-1.425,2.432L42.6,57.848c-1.064,0.277-2.153-0.36-2.431-1.426l-1.577-6.043
+                            c-0.277-1.064,0.36-2.153,1.425-2.432l8.468-2.209c1.064-0.277,2.154,0.361,2.431,1.426l1.577,6.043z"/>>
+                   </g>g>
+                   <g id="bullet">
+                     <use x="0" y="0" xlink:href="#cairo_logo" transform="translate(-6,-2) scale(0.1, 0.1)"/>>
+                   </g>
+                   <g id="redhat_logo_horizontal">
+                     <!-- 380x125 Red Hat log (horizontal layout) -->
+                      <g fill="black" stroke="none" 
+                       transform="translate(0,124),scale(1,-1),translate(-214,-258)"
+                       fill-rule="evenodd"
+                      >
+                       <!-- r -->
+                       <path fill="black" d="
+                        M 367.0625 315.3203
+                        C 367.0625 320.8765 366.9463 324.9644 366.7227 328.6597
+                        L 375.811 328.6597
+                        L 376.2002 320.7764
+                        L 376.4971 320.7764
+                        C 378.5391 326.6221 383.3809 329.5996 387.8594 329.5996
+                        C 388.8843 329.5996 389.4824 329.5601 390.3218 329.373
+                        L 390.3218 319.4863
+                        C 389.3398 319.6763 388.4224 319.7842 387.1592 319.7842
+                        C 382.1597 319.7842 378.688 316.6006 377.751 311.8447
+                        C 377.5732 310.918 377.4805 309.8086 377.4805 308.6777
+                        L 377.4805 287.1504
+                        L 366.9766 287.1504
+                        L 367.0625 315.3203
+                       " />
+
+                       <!-- e -->
+                       <path fill="black" d="
+                        M 402.9927 305.0791
+                        C 403.2715 297.5586 409.0918 294.2695 415.814 294.2695
+                        C 420.6406 294.2695 424.0942 295.0234 427.2681 296.1924
+                        L 428.8232 288.9678
+                        C 425.269 287.4629 420.3413 286.3359 414.3149 286.3359
+                        C 400.8384 286.3359 392.9409 294.6592 392.9409 307.3809
+                        C 392.9409 318.8369 399.8911 329.6772 413.2437 329.6772
+                        C 426.7397 329.6772 431.1338 318.5771 431.1338 309.4893
+                        C 431.1338 307.5381 430.9624 305.9707 430.7593 305.0059
+                        L 402.9927 305.0791
+
+                        M 421.2485 312.3926
+                        C 421.2954 316.2388 419.6206 322.5088 412.5903 322.5088
+                        C 406.1299 322.5088 403.4438 316.645 402.9722 312.3926
+                        L 421.2485 312.3926
+                       " />
+
+                       <!-- d -->
+                       <path fill="black" d="
+                        M 476.355 344.667
+                        L 465.8638 347.5083
+                        L 465.8638 324.1914
+                        L 465.6904 324.1914
+                        C 463.8335 327.2563 459.7407 329.5996 454.0571 329.5996
+                        C 444.0762 329.5996 435.3828 321.3374 435.4478 307.4307
+                        C 435.4478 294.6719 443.2983 286.2168 453.2119 286.2168
+                        C 459.2017 286.2168 464.2114 289.0723 466.6909 293.7217
+                        L 466.8779 293.7217
+                        L 467.3491 287.1504
+                        L 476.6997 287.1504
+                        C 476.5083 289.9717 476.355 294.543 476.355 298.792
+                        L 476.355 344.667
+
+                        M 465.8638 305.1504
+                        C 465.8638 304.0479 465.7856 303.0234 465.5454 302.0869
+                        C 464.4873 297.5439 460.7734 294.6172 456.4819 294.6172
+                        C 449.8721 294.6172 446.0903 300.1885 446.0903 307.8164
+                        C 446.0903 315.5166 449.8384 321.4761 456.6016 321.4761
+                        C 461.3208 321.4761 464.6992 318.1484 465.6274 314.1064
+                        C 465.8071 313.2559 465.8638 312.208 465.8638 311.3711
+                        L 465.8638 305.1504
+                       " />
+
+                       <!-- h -->
+                       <path fill="black" d="
+                        M 503.7964 329.0176
+                        C 500.6836 329.0176 497.8926 328.1187 495.5493 326.6714
+                        C 493.1162 325.2461 491.1353 323.0464 489.959 320.7666
+                        L 489.7915 320.7666
+                        L 489.7915 341.0195
+                        L 485.7427 342.1226
+                        L 485.7427 287.1504
+                        L 489.7915 287.1504
+                        L 489.7915 312.1787
+                        C 489.7915 313.8408 489.9204 314.9946 490.3462 316.2109
+                        C 492.0928 321.3013 496.8896 325.4805 502.689 325.4805
+                        C 511.0664 325.4805 513.9673 318.7603 513.9673 311.3906
+                        L 513.9673 287.1504
+                        L 518.0137 287.1504
+                        L 518.0137 311.8359
+                        C 518.0137 327.0791 507.6753 329.0176 503.7964 329.0176
+                       " />
+
+                       <!-- a -->
+                       <path fill="black" d="
+                        M 554.3413 296.873
+                        C 554.3413 293.6357 554.4692 290.2832 554.9375 287.1504
+                        L 551.2085 287.1504
+                        L 550.6128 293.0156
+                        L 550.4209 293.0156
+                        C 548.438 289.8594 543.8765 286.2168 537.3726 286.2168
+                        C 529.1392 286.2168 525.3057 292.0117 525.3057 297.4688
+                        C 525.3057 306.9121 533.6421 312.6064 550.292 312.4321
+                        L 550.292 313.5234
+                        C 550.292 317.5718 549.5044 325.6475 539.8242 325.5859
+                        C 536.2446 325.5859 532.5132 324.6255 529.5513 322.5366
+                        L 528.2632 325.4805
+                        C 532.0015 328.0137 536.5659 329.0176 540.2705 329.0176
+                        C 552.0801 329.0176 554.3413 320.1509 554.3413 312.8379
+                        L 554.3413 296.873
+
+                        M 550.292 309.0234
+                        C 541.3813 309.2813 529.6128 307.9336 529.6128 298.1055
+                        C 529.6128 292.2246 533.4946 289.5811 537.7578 289.5811
+                        C 544.5796 289.5811 548.4561 293.8018 549.8677 297.7871
+                        C 550.1646 298.6621 550.292 299.5371 550.292 300.2402
+                        L 550.292 309.0234
+                       " />
+
+                       <!-- t -->
+                       <path fill="black" d="
+                        M 570.459 337.0996
+                        L 570.459 328.0801
+                        L 582.1235 328.0801
+                        L 582.1235 324.7959
+                        L 570.459 324.7959
+                        L 570.459 298.1943
+                        C 570.459 292.9912 572.0757 289.7285 576.4692 289.7285
+                        C 578.5815 289.7285 580.0757 290.0078 581.1206 290.3711
+                        L 581.6099 287.2354
+                        C 580.2871 286.6836 578.4302 286.2539 575.9619 286.2539
+                        C 572.9741 286.2539 570.4995 287.1934 568.8994 289.1543
+                        C 567.0469 291.3057 566.4116 294.7412 566.4116 298.916
+                        L 566.4116 324.7959
+                        L 559.5059 324.7959
+                        L 559.5059 328.0801
+                        L 566.4116 328.0801
+                        L 566.4116 335.606
+                        L 570.459 337.0996
+                       " />
+
+                       <!-- ® for 'redhat' -->
+                       <path fill="black" d="
+                        M 335.5 288.9707
+                        L 336.0352 288.9707
+                        L 336.8408 287.6445
+                        L 337.3608 287.6445
+                        L 336.4888 288.9937
+                        C 336.9404 289.0498 337.2832 289.2881 337.2832 289.834
+                        C 337.2832 290.4385 336.9258 290.7051 336.2017 290.7051
+                        L 335.0366 290.7051
+                        L 335.0366 287.6445
+                        L 335.5 287.6445
+                        L 335.5 288.9707
+
+                        M 335.5 289.3643
+                        L 335.5 290.3101
+                        L 336.1318 290.3101
+                        C 336.4531 290.3101 336.7979 290.2402 336.7979 289.8647
+                        C 336.7979 289.3916 336.4492 289.3643 336.0566 289.3643
+                        L 335.5 289.3643
+                       " />
+
+                       <path fill="black" d="
+                        M 339.0439 289.1719
+                        C 339.0439 287.5176 337.7041 286.1763 336.0493 286.1763
+                        C 334.395 286.1763 333.0527 287.5176 333.0527 289.1719
+                        C 333.0527 290.8271 334.395 292.1675 336.0493 292.1675
+                        C 337.7041 292.1675 339.0439 290.8271 339.0439 289.1719
+
+                        M 336.0493 291.6367
+                        C 334.6865 291.6367 333.5835 290.5332 333.5835 289.1719
+                        C 333.5835 287.8096 334.6865 286.7061 336.0493 286.7061
+                        C 337.4082 286.7061 338.5117 287.8096 338.5117 289.1719
+                        C 338.5117 290.5332 337.4082 291.6367 336.0493 291.6367
+                       " />
+
+                       <!-- Black background behind The Shadowman -->
+                       <path fill="black" d="
+                        M 326.4531 286.208
+                        C 324.1177 286.7451 321.6396 287.0801 319.1338 287.0801
+                        C 314.8496 287.0801 310.9502 286.3389 308.0732 285.1426
+                        C 307.7559 284.9844 307.5303 284.6533 307.5303 284.2764
+                        C 307.5303 284.1406 307.5654 283.999 307.6172 283.8838
+                        C 307.957 282.8975 307.3984 281.8281 304.6157 281.2158
+                        C 300.4883 280.3096 297.8833 276.0527 296.3916 274.6367
+                        C 294.6411 272.9756 289.6973 271.9531 290.4404 272.9434
+                        C 291.0225 273.7188 293.2485 276.1348 294.6016 278.748
+                        C 295.8125 281.083 296.8906 281.7461 298.375 283.9736
+                        C 298.811 284.627 300.4995 286.9219 300.9912 288.7373
+                        C 301.543 290.5107 301.356 292.7344 301.5679 293.6494
+                        C 301.8721 294.9697 303.1182 297.8369 303.2129 299.4531
+                        C 303.2666 300.3691 299.3916 298.1494 297.5532 298.1494
+                        C 295.7144 298.1494 293.9233 299.248 292.2808 299.3281
+                        C 290.248 299.4248 288.9414 297.7607 287.1025 298.0508
+                        C 286.0518 298.2178 285.167 299.1426 283.3311 299.2129
+                        C 280.7178 299.3086 277.5244 297.7607 271.5264 297.9531
+                        C 265.6255 298.1436 260.1753 305.4082 259.4312 306.5635
+                        C 258.5605 307.9199 257.4961 307.9199 256.335 306.8555
+                        C 255.1738 305.792 253.7432 306.627 253.3359 307.3389
+                        C 252.5615 308.6943 250.4927 312.6543 247.2881 313.4824
+                        C 242.8564 314.6309 240.6118 311.0283 240.9033 308.1621
+                        C 241.1987 305.252 243.0801 304.4375 243.9512 302.8906
+                        C 244.8213 301.3428 245.2671 300.3428 246.9053 299.6572
+                        C 248.0674 299.1758 248.5 298.458 248.1533 297.5049
+                        C 247.8506 296.6738 246.6416 296.4834 245.8477 296.4463
+                        C 244.1592 296.3662 242.9756 296.8242 242.1123 297.376
+                        C 241.1084 298.0137 240.292 298.9033 239.416 300.4131
+                        C 238.4023 302.0781 236.8052 302.8037 234.9453 302.8037
+                        C 234.0586 302.8037 233.2295 302.5693 232.4922 302.1895
+                        C 229.5771 300.6748 226.1064 299.7744 222.3706 299.7744
+                        L 218.1572 299.7734
+                        C 216.1064 305.8555 214.9951 312.3682 214.9951 319.1416
+                        C 214.9951 352.6064 242.1226 379.7334 275.5859 379.7334
+                        C 309.0498 379.7334 336.1758 352.6064 336.1758 319.1416
+                        C 336.1758 307 332.6035 295.6895 326.4531 286.208
+                       " />
+
+                       <!-- The Shadowman's face -->
+                       <path fill="white" d="
+                        M 326.4531 286.209
+                        C 324.1177 286.7461 321.6396 287.084 319.1338 287.084
+                        C 314.8496 287.084 310.9502 286.3418 308.0732 285.1436
+                        C 307.7559 284.9873 307.5303 284.6553 307.5303 284.2783
+                        C 307.5303 284.1416 307.5654 284.001 307.6172 283.8838
+                        C 307.957 282.8994 307.3984 281.8311 304.6157 281.2178
+                        C 300.4883 280.3115 297.8833 276.0537 296.3916 274.6416
+                        C 294.6411 272.9766 289.6973 271.9551 290.4404 272.9463
+                        C 291.0225 273.7197 293.2485 276.1367 294.6016 278.749
+                        C 295.8125 281.083 296.8906 281.75 298.375 283.9756
+                        C 298.811 284.627 300.4995 286.9238 300.9912 288.7402
+                        C 301.543 290.5117 301.356 292.7354 301.5679 293.6514
+                        C 301.8721 294.9727 303.1182 297.8379 303.2129 299.457
+                        C 303.2666 300.3721 299.3916 298.1504 297.5532 298.1504
+                        C 295.7144 298.1504 293.9233 299.251 292.2808 299.3301
+                        C 290.248 299.4258 288.9414 297.7627 287.1025 298.0518
+                        C 286.0518 298.2207 285.167 299.1465 283.3311 299.2148
+                        C 280.7178 299.3096 277.5244 297.7627 271.5264 297.9561
+                        C 265.6255 298.1475 260.1753 305.4121 259.4312 306.5674
+                        C 258.5605 307.9219 257.4961 307.9219 256.335 306.8574
+                        C 255.1738 305.7939 253.7432 306.6299 253.3359 307.3438
+                        C 252.5615 308.6963 250.4927 312.6553 247.2881 313.4854
+                        C 242.8564 314.6338 240.6118 311.0313 240.9033 308.1641
+                        C 241.1987 305.2539 243.0801 304.4395 243.9512 302.8926
+                        C 244.8213 301.3438 245.2671 300.3457 246.9053 299.6621
+                        C 248.0674 299.1768 248.5 298.4609 248.1533 297.5068
+                        C 247.8506 296.6768 246.6416 296.4873 245.8477 296.4492
+                        C 244.1592 296.3672 242.9756 296.8262 242.1123 297.376
+                        C 241.1084 298.0176 240.292 298.9043 239.416 300.416
+                        C 238.4023 302.0801 236.8052 302.8086 234.9453 302.8086
+                        C 234.0586 302.8086 233.2295 302.5723 232.4922 302.1934
+                        C 229.5771 300.6748 226.1064 299.7773 222.3706 299.7773
+                        L 218.1572 299.7744
+                        C 226.2363 275.8105 248.8965 258.5527 275.5859 258.5527
+                        C 296.9063 258.5527 315.6538 269.5635 326.4531 286.209
+                       " />
+
+                       <!-- nose shadow -->
+                       <path fill="black" d="
+                        M 288.9307 291.7637
+                        C 289.2422 291.46 289.7793 290.4375 289.1226 289.1396
+                        C 288.7544 288.4521 288.3579 287.9678 287.6489 287.4023
+                        C 286.7969 286.7188 285.1309 285.9307 282.8457 287.3799
+                        C 281.6172 288.1592 281.543 288.4209 279.8467 288.2012
+                        C 278.6348 288.043 278.1533 289.2656 278.5884 290.2832
+                        C 279.0244 291.2969 280.8145 292.1191 283.04 290.8135
+                        C 284.041 290.2256 285.6025 288.9844 286.9688 290.084
+                        C 287.5356 290.5381 287.875 290.8408 288.6611 291.75
+                        C 288.6963 291.7881 288.7461 291.8105 288.8018 291.8105
+                        C 288.8516 291.8105 288.8965 291.793 288.9307 291.7637
+                       " />
+
+                       <!-- The Shadowman's red hat -->
+                       <path fill="#cc0000" d="
+                        M 309.7769 335.2627
+                        C 309.1787 333.251 308.3271 330.6763 304.5391 328.7314
+                        C 303.9878 328.4497 303.7764 328.9126 304.0313 329.3477
+                        C 305.4629 331.7832 305.7168 332.3921 306.1328 333.3525
+                        C 306.7148 334.7568 307.02 336.7549 305.8618 340.9219
+                        C 303.5835 349.1221 298.8296 360.083 295.375 363.6392
+                        C 292.04 367.0698 285.998 368.0361 280.5371 366.6348
+                        C 278.5264 366.1191 274.5918 364.0732 267.2939 365.7168
+                        C 254.665 368.5605 252.7939 362.2368 252.0693 359.4824
+                        C 251.3438 356.7271 249.6045 348.897 249.6045 348.897
+                        C 249.0244 345.7085 248.2646 340.1631 267.874 336.4287
+                        C 277.0088 334.6885 277.4736 332.3276 277.8779 330.6289
+                        C 278.603 327.585 279.7627 325.8438 281.0674 324.9746
+                        C 282.373 324.1035 281.0674 323.3828 279.6187 323.2349
+                        C 275.7285 322.8311 261.3491 326.9541 252.8428 331.7881
+                        C 245.8828 336.0415 245.7656 339.8721 247.3584 343.1211
+                        C 236.8452 344.2573 228.9561 342.1348 227.5254 337.1582
+                        C 225.0693 328.6157 246.3047 314.0264 270.4839 306.7061
+                        C 295.8579 299.0225 321.9556 304.3857 324.8564 320.335
+                        C 326.1738 327.5811 320.0713 332.9419 309.7769 335.2627
+                       " />
+
+                       <!-- shadow on hat -->
+                       <path fill="black" d="
+                        M 270.8711 350.8813
+                        C 263.8721 350.375 263.145 349.6191 261.834 348.2227
+                        C 259.9854 346.2539 257.5508 350.7773 257.5508 350.7773
+                        C 256.0898 351.085 254.3179 353.4404 255.2744 355.6411
+                        C 256.2158 357.8174 257.9551 357.1641 258.5 356.4868
+                        C 259.1626 355.6621 260.5771 354.3125 262.4141 354.3613
+                        C 264.251 354.4097 266.3706 354.7959 269.3262 354.7959
+                        C 272.3213 354.7959 274.335 353.6777 274.4487 352.7168
+                        C 274.5459 351.8965 274.2061 351.1226 270.8711 350.8813
+                       " />
+
+                       <!-- another shadow on hat -->
+                       <path fill="black" d="
+                        M 278.2236 362.4463
+                        C 278.2129 362.4453 278.2021 362.4438 278.1919 362.4438
+                        C 278.084 362.4438 277.9961 362.5273 277.9961 362.6274
+                        C 277.9961 362.7007 278.041 362.7646 278.106 362.7939
+                        C 279.4629 363.5107 281.4873 364.0811 283.8042 364.3169
+                        C 284.499 364.3887 285.1787 364.4248 285.832 364.4307
+                        C 285.9478 364.4307 286.0615 364.4297 286.1787 364.4277
+                        C 290.062 364.3398 293.1719 362.7974 293.1255 360.9814
+                        C 293.0791 359.165 289.8955 357.7637 286.0112 357.8506
+                        C 284.7529 357.8794 283.5732 358.0615 282.5576 358.3545
+                        C 282.4385 358.3857 282.3506 358.4883 282.3506 358.6094
+                        C 282.3506 358.731 282.4385 358.834 282.5605 358.8638
+                        C 284.9839 359.4248 286.6191 360.3408 286.5039 361.207
+                        C 286.3511 362.3545 283.1816 362.979 279.4248 362.6011
+                        C 279.0137 362.5596 278.6118 362.5068 278.2236 362.4463
+                       " />
+
+                       <!-- ® for The Shadowman -->
+                       <path fill="black" d="
+                        M 588.3018 288.9707
+                        L 588.8369 288.9707
+                        L 589.6426 287.6445
+                        L 590.1626 287.6445
+                        L 589.2905 288.9937
+                        C 589.7422 289.0498 590.085 289.2881 590.085 289.834
+                        C 590.085 290.4385 589.7275 290.7051 589.0034 290.7051
+                        L 587.8384 290.7051
+                        L 587.8384 287.6445
+                        L 588.3018 287.6445
+                        L 588.3018 288.9707
+
+                        M 588.3018 289.3643
+                        L 588.3018 290.3101
+                        L 588.9336 290.3101
+                        C 589.2549 290.3101 589.5996 290.2402 589.5996 289.8647
+                        C 589.5996 289.3916 589.251 289.3643 588.8584 289.3643
+                        L 588.3018 289.3643
+                       " />
+
+                       <path fill="black" d="
+                        M 591.8457 289.1719
+                        C 591.8457 287.5176 590.5059 286.1763 588.8511 286.1763
+                        C 587.1968 286.1763 585.8545 287.5176 585.8545 289.1719
+                        C 585.8545 290.8271 587.1968 292.1675 588.8511 292.1675
+                        C 590.5059 292.1675 591.8457 290.8271 591.8457 289.1719
+
+                        M 588.8511 291.6367
+                        C 587.4883 291.6367 586.3853 290.5332 586.3853 289.1719
+                        C 586.3853 287.8096 587.4883 286.7061 588.8511 286.7061
+                        C 590.21 286.7061 591.3135 287.8096 591.3135 289.1719
+                        C 591.3135 290.5332 590.21 291.6367 588.8511 291.6367
+                       " />
+
+                     </g>
+                   </g>
+
+       </defs>
+
+  <g id="watermark" transform="translate(200, 185), rotate(-50), scale(2.5)">
+    <use xlink:href="#scarab" x="0" y="170" fill-opacity="0.2"/>
+  </g>
+
+
+  <!-- Blue bar at top of slide -->
+  <rect x="0" y="0" width="1024" height="170" fill="#162284" />
+
+  <!-- Scarab and "cairo" at upper-left -->
+  <g transform="translate(10,0)">
+    <use stroke="none" xlink:href="#cairo_logo_text_small"/>
+  </g>
+
+  <!-- Presentation title at upper-right -->
+  <text ss:variable="presentation-subtitle" text-anchor="end"
+     fill="white" x="1016" y="28" font-size="20">Presentation Sub-title</text>
+
+  <!-- Red Hat logo at lower-left -->
+  <use xlink:href="#redhat_logo_horizontal" transform="translate(8,768),scale(.5,.5),translate(0, -125)" />
+  <g font-family="Frutiger">
+    <!-- Slide title -->
+    <g id="slide_title" transform="translate(512, 133)">
+      <text text-anchor="middle"
+           fill="white"
+           font-weight="bold"
+           x="0"
+           y="4" font-size="55"
+           ss:variable="title">Slide Title</text>
+    </g>
+    
+    <!-- Slide content -->
+    <g ss:region="default" font-family="Mono">
+      <rect x="112" y="170" width="800" height="480" fill="none" stroke="blue"/>
+      <text font-size="20" fill="black"
+           x="112" y="190">Slide content</text>
+    </g>
+    
+    <!-- Footer -->
+    <text ss:variable="URL" x="1016" y="753" text-anchor="end" font-size="20">http://cairographics.org</text>
+  </g>
+
+</svg>
diff --git a/doc/tutorial/slides/cairo-large-content.svg b/doc/tutorial/slides/cairo-large-content.svg
new file mode 100644 (file)
index 0000000..5acf4c9
--- /dev/null
@@ -0,0 +1,899 @@
+<?xml version="1.0" ?>
+<svg width="1024" height="768"
+    xmlns="http://www.w3.org/2000/svg"
+    xmlns:xlink="http://www.w3.org/1999/xlink"
+    xmlns:ss="http://www.svgslides.org/svgslides0.1"
+    fill="black">
+
+
+
+       <defs id="cairo-artwork_defs">
+               <g id="hacker_emblem">
+                       <!-- Note: This is similar though not identical to Keith Packard's SVG version
+                               of the hacker emblem (http://www.catb.org/hacker-emblem/glider.svg) -->
+                       <g id="hacker_emblem_grid" fill="white" stroke="none">
+                               <!-- Outside: Top, Right, Bottom, Left -->
+                               <rect x="-2.95" y="-3.05" width="6"   height="0.1" />
+                               <rect x="2.95"  y="-2.95" width="0.1" height="6" />
+                               <rect x="-3.05" y="2.95"  width="6"   height="0.1" />
+                               <rect x="-3.05" y="-3.05" width="0.1" height="6" />
+                               <!-- Vertical: Left, Right -->
+                               <rect x="-1.05" y="-2.95" width="0.1" height="5.9" />
+                               <rect x="0.95"  y="-2.95" width="0.1" height="5.9" />
+                               <!-- Horizontal: TopLeft, TopMiddle, TopRight -->
+                               <rect x="-2.95" y="-1.05" width="1.9" height="0.1" />
+                               <rect x="-0.95" y="-1.05" width="1.9" height="0.1" />
+                               <rect x="1.05"  y="-1.05" width="1.9" height="0.1" />
+                               <!-- Horizontal: BottomLeft, BottomMiddle, BottomRight -->
+                               <rect x="-2.95" y="0.95"  width="1.9" height="0.1" />
+                               <rect x="-0.95" y="0.95"  width="1.9" height="0.1" />
+                               <rect x="1.05"  y="0.95"  width="1.9" height="0.1" />
+                       </g>
+                       <g id="hacker_emblem_dots" fill="white">
+                               <circle cx="0"  cy="-2" r="0.7" />
+                               <circle cx="2"  cy="0"  r="0.7" />
+                               <circle cx="-2" cy="2"  r="0.7" />
+                               <circle cx="0"  cy="2"  r="0.7" />
+                               <circle cx="2"  cy="2"  r="0.7" />
+                       </g>
+               </g>
+               <g id="scarab" fill="#f19a14">
+                       <g transform="translate(-150, -170)">
+                       <path id="scarab_head" d="M205.599,94.567c0-11.668-24.914-21.129-55.628-21.129
+                               c-30.723,0-55.624,9.46-55.624,21.129c0,10.203,24.901,7.346,55.624,7.346C180.685,101.913,205.599,104.233,205.599,94.567z"/>
+                       <path id="scarab_torso" d="M136.423,161.506c0,0,12.751,12.577,13.547,13.362
+                               c2.262-2.232,13.545-13.362,13.545-13.362c7.135-7.036,87.111-6.399,91.066-6.363c-0.469-6.298-1.254-12.472-2.325-18.519
+                               c-15.183-19.279-42.811-32.225-74.485-32.225h-55.518c-31.745,0-59.439,13.011-74.598,32.37c-1.054,6-1.829,12.128-2.296,18.374
+                               C49.321,155.106,129.288,154.47,136.423,161.506z"/>
+                       <path id="scarab_spine" d="M149.97,301.187c2.005-24.729,8.386-103.483,8.405-103.721
+                               c-0.09-0.219-6.478-15.578-8.405-20.214c-1.936,4.655-8.316,19.995-8.408,20.214C141.582,197.704,147.965,276.458,149.97,301.187z"/>
+                       <path id="scarab_wing_left" d="M140.403,197.149l8.862-21.31l-13.686-13.499
+                               c-5.65-5.573-67.074-6.235-90.259-6.019l-0.006-0.622c-0.154,2.144-0.271,4.302-0.35,6.475
+                               c-0.076,2.207,10.392,4.706,10.392,6.717c0,2.319-10.457,5.084-10.359,7.631c2.993,73.349,48.53,131.631,104.372,132.048
+                               l-9.02-111.29L140.403,197.149z"/>
+                       <path id="scarab_wing_right" d="M244.585,168.891c0-2.011,10.467-4.506,10.391-6.715
+                               c-0.079-2.174-0.195-4.332-0.351-6.479l-0.004,0.624c-23.186-0.216-84.608,0.445-90.26,6.017l-13.688,13.502l8.915,21.438
+                               l-9.017,111.29c55.854-0.417,101.378-58.698,104.373-132.049C255.04,173.976,244.585,171.209,244.585,168.891z"/>
+                       <path id="scarab_leg_front_left" d="M44.506,141.12c-4.135-0.856-4.895-1.54-7.935-2.92
+                               c-9.59-3.364-10.376-5.481-16.08-11.86c-7.426-8.306-12.661-20.142-17.1-29.463c-3.576-7.525-3.984-16.409-2.86-24.273
+                               c0.991-6.935,7.144-12.869,12.074-18.92c5.844-7.191,10.356-14.822,17.924-21.354c7.736-6.682,23.203-9.809,26.168-19.648
+                               C57.86,8.819,54.334,1.766,61.482,0c-0.366,4.703,3.639,8.477,2.397,13.575c-1.129,4.627-4.368,5.811-9.611,9.099
+                               c-7.564,4.746-18.366,8.779-24.748,13.965c-7.175,5.827-4.369,13.771-10.569,20.057c-2.001,2.03-7.901,4.706-9.137,6.83
+                               c-1.861,3.199-0.297,9.572-0.116,13.12c0.425,8.284,5.588,14.244,9.555,22.045c4.152,8.141,6.429,15.409,13.411,22.519
+                               c4.183,4.262,11.429,4.802,16.21,10.647l-3.555,4.186L44.506,141.12z"/>
+                       <path id="scarab_leg_middle_left" d="M43.94,191.922l-0.809-7.346
+                               c-9.506-4.579-10.339-9.772-20.738-12.466c-23.728-6.151-21.361,11.25-15.532,26.373c5.676,14.726,8.237,30.23,14.345,44.795
+                               c2.805,6.688,6.919,13.213,14.298,15.127c0.372-8.435-0.917-10.651-6.113-16.919c-4.395-5.293-3.326-12.548-6.072-18.504
+                               c-3.581-7.804-4.196-15.646-7.279-23.502c-1.363-3.479-8.33-13.966-6.452-17.861c3.183-6.603,9.178-0.083,12.179,2.077
+                               c4.218,3.036,6.467,2.223,11.681,2.898C34.041,186.673,37.005,188.756,43.94,191.922z"/>
+                       <path id="scarab_leg_back_left" d="M65.839,257.063l-2.771-4.837
+                               c-6.68,8.928-6.993,16.228-10.056,23.347c-5.277,12.263-0.157,28.851,9.854,37.676c6.052,5.375,15.907,9.618,23.122,13.136
+                               c10.035,4.892,20.113,11.286,31.336,13.396c2.482,0.466,8.798,1.295,6.693-3.522c-0.975-2.237-8.091-4.591-10.146-5.734
+                               c-8.312-4.623-16.377-10.524-24.142-16.176c-9.498-6.862-20.843-11.186-28.311-20.684c-3.054-3.885-3.544-4.922-2.816-9.39
+                               c0.693-4.263,1.344-9.174,2.241-13.439C61.855,266.029,63.274,261.378,65.839,257.063z"/>
+                       <path id="scarab_leg_front_right" d="M255.487,141.12c4.134-0.856,4.896-1.54,7.936-2.92
+                               c9.583-3.364,10.369-5.481,16.071-11.86c7.428-8.306,12.661-20.142,17.115-29.463c3.574-7.525,3.983-16.409,2.86-24.273
+                               c-0.992-6.935-7.157-12.869-12.087-18.92c-5.843-7.191-10.356-14.822-17.919-21.354c-7.735-6.682-23.202-9.809-26.167-19.648
+                               C242.135,8.819,245.66,1.766,238.511,0c0.366,4.703-3.637,8.477-2.396,13.575c1.131,4.627,4.368,5.811,9.611,9.099
+                               c7.563,4.746,18.367,8.779,24.747,13.965c7.17,5.827,4.362,13.771,10.563,20.057c2.001,2.03,7.901,4.706,9.139,6.83
+                               c1.859,3.199,0.295,9.572,0.113,13.12c-0.424,8.284-5.588,14.244-9.553,22.045c-4.152,8.141-6.431,15.409-13.404,22.519
+                               c-4.184,4.262-11.429,4.802-16.211,10.647l3.556,4.186L255.487,141.12z"/>
+                       <path id="scarab_leg_middle_right" d="M256.053,191.922l0.81-7.346
+                               c9.507-4.579,10.34-9.772,20.73-12.466c23.741-6.151,21.374,11.25,15.534,26.373c-5.676,14.726-8.238,30.23-14.347,44.795
+                               c-2.804,6.688-6.911,13.213-14.291,15.127c-0.371-8.435,0.918-10.651,6.113-16.919c4.39-5.293,3.319-12.548,6.066-18.504
+                               c3.58-7.804,4.197-15.646,7.278-23.502c1.363-3.479,8.33-13.966,6.453-17.861c-3.184-6.603-9.179-0.083-12.181,2.077
+                               c-4.217,3.036-6.458,2.223-11.672,2.898C265.951,186.673,262.986,188.756,256.053,191.922z"/>
+                       <path id="scarab_leg_back_right" d="M234.155,257.063l2.771-4.837
+                               c6.679,8.928,6.991,16.228,10.057,23.347c5.274,12.263,0.154,28.851-9.854,37.676c-6.055,5.375-15.903,9.618-23.117,13.136
+                               c-10.034,4.892-20.127,11.286-31.351,13.396c-2.481,0.466-8.789,1.295-6.691-3.522c0.976-2.237,8.092-4.591,10.146-5.734
+                               c8.312-4.623,16.392-10.524,24.155-16.176c9.498-6.862,20.838-11.186,28.305-20.684c3.055-3.885,3.543-4.922,2.818-9.39
+                               c-0.696-4.263-1.346-9.174-2.244-13.439C238.137,266.029,236.718,261.378,234.155,257.063z"/>
+                       </g>
+               </g>
+               <radialGradient id="gradient_radial_dung"
+                               cx="0" cy="0" r="60"
+                               fx="0" fy="0" gradientUnits="userSpaceOnUse"
+               >
+                       <stop offset="0"    stop-color="#9a9a9a" />
+                       <stop offset="0.70" stop-color="#bababa" />
+                       <stop offset="0.95" stop-color="#FFFFFF" />
+               </radialGradient>
+               <g id="dung">
+                       <circle cx="0" cy="0" r="60" fill="url(#gradient_radial_dung)" />
+                       <g transform="translate(-61, -61)">
+                               <!-- rough equivalent: <circle cx="0" cy="0" r="60" stroke="#8a8a8a" stroke-width="2" /> -->
+                               <path fill="#8a8a8a" d="M0,61c0,33.636,27.364,61,61,61s61-27.364,61-61S94.636,0,61,0S0,27.364,0,61z
+                                                       M2,61C2,28.467,28.467,2,61,2c32.532,0,59,26.467,59,59c0,32.533-26.468,59-59,59C28.467,120,2,93.533,2,61z"/>
+                       </g>
+                       <use xlink:href="#hacker_emblem" x="0" y="0" transform="scale(9)" />
+               </g>
+               <g id="dung_2_color">
+                       <!-- This would be simple a circle like so:
+                               <circle cx="0" cy="0" r="48" stroke-width="2" fill="none" stroke="white"/>
+                            but there appears to currently be a bug in the cairo
+                            PDF backend that results in an ugly spike in that
+                            case. So we use 8 splines instead.
+                       -->
+                       <path stroke-width="4" stroke="white" fill="none" d="
+M 48,                  0
+C 48,                  12.730391512298112, 
+  42.94287166245995,   24.939379331448613
+  33.941125496954285,  33.941125496954278
+C 24.939379331448624,  42.942871662459943
+  12.730391512298114,  48
+  0,                   48
+C -12.730391512298109, 48, 
+  -24.939379331448613, 42.94287166245995
+  -33.941125496954278, 33.941125496954285
+C -42.942871662459943, 24.939379331448624,
+  -48,                 12.730391512298118,
+  -48,                 0,
+C -48,                 -12.730391512298105, 
+  -42.94287166245995,  -24.939379331448613,
+  -33.941125496954285, -33.941125496954278
+C -24.939379331448624, -42.942871662459943,
+  -12.730391512298119, -48, 
+  0,                   -48
+C 12.730391512298104,  -48, 
+  24.939379331448606,  -42.942871662459943,
+  33.941125496954271,  -33.941125496954285
+C 42.942871662459936,  -24.939379331448624,
+  48,                  -12.730391512298123,
+  48,                  0" />
+                       <g transform="scale(9)" fill="white" stroke="none" stroke-width="0.22222">
+                               <!-- Hacker emblem grid -->
+                               <!--
+                               <path stroke="white" fill="none"
+                                     d="M -3,-3 L  3,-3 L 3,3 L-3,3 Z
+                                        M -1,-3 L -1, 3
+                                        M  1,-3 L  1, 3
+                                        M -3,-1 L  3,-1
+                                        M -3, 1 L  3, 1"/>
+                                        -->
+
+                               <!-- Hacker emblem dots -->
+                               <circle cx="0"  cy="-2" r="0.7" />
+                               <circle cx="2"  cy="0"  r="0.7" />
+                               <circle cx="-2" cy="2"  r="0.7" />
+                               <circle cx="0"  cy="2"  r="0.7" />
+                               <circle cx="2"  cy="2"  r="0.7" />
+                       </g>
+               </g>
+
+               <!-- scarab dimensions: 300x340 -->
+               <!-- dung dimensions: 120x120 (radius: 60) -->
+               <!-- scarab and dung dimensions: 300x400 -->
+
+               <g id="cairo_logo">
+                       <!-- dimensions: 300x400, centered -->
+                       <!-- The logo (scarab and dung), with the center-point of the bounding box at (0,0) -->
+                       <use xlink:href="#dung_2_color" x="0" y="0" transform="translate(0, -140)" />
+                       <use xlink:href="#scarab" x="0" y="0" transform="translate(0, 30)" />
+               </g>
+               <g id="cairo_logo_dung-centered">
+                       <!-- The logo (scarab and dung), with the dung at (0,0), the scarab below -->
+                       <use xlink:href="#dung_2_color" x="0" y="0" />
+                       <use xlink:href="#scarab" x="0" y="0" transform="translate(0,170)" />
+               </g>
+               <g id="cairo_logo_scarab-centered">
+                       <!-- The logo (scarab and dung), with the scarab's rotational center at (0,0), the dung above -->
+                       <!-- The scarab's rotational center in this case is not the center of its bounding box,
+                               but is calculated to be the intersection-point of the torso, spine and wings -->
+                       <use xlink:href="#dung_2_color" x="0" y="0" transform="translate(0, -175.85)" />
+                       <use xlink:href="#scarab" x="0" y="0" transform="translate(0, -5.85)" />
+               </g>
+               <g id="cairo_logo_top-centered">
+                       <!-- The logo (scarab and dung), with the top-center point of the bounding box at (0,0) -->
+                       <use xlink:href="#dung_2_color" x="0" y="0" transform="translate(0, 60)" />
+                       <use xlink:href="#scarab" x="0" y="0" transform="translate(0, 230)" /><!-- (0,170+60) -->
+               </g>
+               <g id="cairo_logo_bottom-centered">
+                       <!-- The logo (scarab and dung), with the bottom-center point of the bounding box at (0,0) -->
+                       <use xlink:href="#dung_2_color" x="0" y="0" transform="translate(0, -340)" />
+                       <use xlink:href="#scarab" x="0" y="0" transform="translate(0, -170)" />
+               </g>
+               <g id="cairo_logo_right-centered">
+                       <!-- The logo (scarab and dung), with the right-center point of the bounding box at (0,0) -->
+                       <use xlink:href="#dung_2_color" x="0" y="0" transform="translate(-150, -140)" />
+                       <use xlink:href="#scarab" x="0" y="0" transform="translate(-150, 30)" />
+               </g>
+               <g id="cairo_logo_left-centered">
+                       <!-- The logo (scarab and dung), with the left-center point of the bounding box at (0,0) -->
+                       <use xlink:href="#dung_2_color" x="0" y="0" transform="translate(150, -140)" />
+                       <use xlink:href="#scarab" x="0" y="0" transform="translate(150, 30)" />
+               </g>
+               <g id="cairo_logo_topleft-centered">
+                       <!-- The logo (scarab and dung), with the top-left point of the bounding box at (0,0) -->
+                       <use xlink:href="#dung_2_color" x="0" y="0" transform="translate(150, 60)" />
+                       <use xlink:href="#scarab" x="0" y="0" transform="translate(150, 230)" /><!-- (150, 170+60) -->
+               </g>
+               <g id="cairo_logo_topright-centered">
+                       <!-- The logo (scarab and dung), with the top-right point of the bounding box at (0,0) -->
+                       <use xlink:href="#dung_2_color" x="0" y="0" transform="translate(-150, 60)" />
+                       <use xlink:href="#scarab" x="0" y="0" transform="translate(-150, 230)" /><!-- (-150,170+60) -->
+               </g>
+               <g id="cairo_logo_bottomleft-centered">
+                       <!-- The logo (scarab and dung), with the bottom-left point of the bounding box at (0,0) -->
+                       <use xlink:href="#dung_2_color" x="0" y="0" transform="translate(150, -340)" />
+                       <use xlink:href="#scarab" x="0" y="0" transform="translate(150, -170)" />
+               </g>
+               <g id="cairo_logo_bottomright-centered">
+                       <!-- The logo (scarab and dung), with the bottom-right point of the bounding box at (0,0) -->
+                       <use xlink:href="#dung_2_color" x="0" y="0" transform="translate(-150, -340)" />
+                       <use xlink:href="#scarab" x="0" y="0" transform="translate(-150, -170)" />
+               </g>
+
+               <g id="cairo_text" transform="translate(0,-97)">
+                   <g transform="scale(0.1484,0.1484)"> <g transform="translate(-1139,-208.5)">
+                        <!-- 63 (c), advance 444, 0 horiBearing 38,522 -->
+                        <path transform="translate(65,0)" d="
+                              M 412, 433 
+                              C 385, 422 336, 413 298, 413 
+                              C 142, 413 38, 525 38, 680 
+                              C 38, 826 144, 947 298, 947 
+                              C 332, 947 377, 944 416, 926 
+                              L 409, 842 
+                              C 380, 861 340, 871 308, 871 
+                              C 187, 871 138, 771 138, 680 
+                              C 138, 583 197, 489 302, 489 
+                              C 332, 489 368, 496 404, 511 
+                              L 412, 433 " />
+                        <!-- 61 (a), advance 556, 0 horiBearing 46,522 -->
+                        <path transform="translate(486.75,0)" d="
+                              M 109, 541 
+                              C 147, 509 204, 489 257, 489 
+                              C 351, 489 383, 534 383, 622 
+                              C 346, 620 320, 620 283, 620 
+                              C 186, 620 46, 660 46, 788 
+                              C 46, 899 123, 947 233, 947 
+                              C 319, 947 369, 900 391, 869 
+                              L 393, 869 
+                              L 393, 935 
+                              L 481, 935 
+                              C 479, 920 477, 893 477, 835 
+                              L 477, 624 
+                              C 477, 485 418, 413 272, 413 
+                              C 207, 413 151, 433 104, 461 
+                              L 109, 541 
+                              M 383, 737 
+                              C 383, 813 334, 871 241, 871 
+                              C 198, 871 146, 842 146, 788 
+                              C 146, 698 272, 690 323, 690 
+                              C 343, 690 363, 692 383, 692 
+                              L 383, 737 " />
+                        <!-- 69 (i), advance 278, 0 horiBearing 86,730 -->
+                        <path transform="translate(1000,0)" d="
+                              M 92, 935 
+                              L 186, 935 
+                              L 186, 425 
+                              L 92, 425 
+                              L 92, 935 
+                              M 88, 261
+                              A 51, 51 0 1 1 190,261
+                              A 51, 51 0 1 1 88,261" />
+                        <!-- 72 (r), advance 389, 0 horiBearing 80,522 -->
+                        <path transform="translate(1234.25,0)" d="
+                              M 80, 935 
+                              L 174, 935 
+                              L 174, 703 
+                              C 174, 575 229, 495 313, 495 
+                              C 329, 495 348, 497 365, 504 
+                              L 365, 420 
+                              C 345, 416 331, 413 303, 413 
+                              C 249, 413 195, 451 170, 504 
+                              L 168, 504 
+                              L 168, 425 
+                              L 80, 425 
+                              L 80, 935 " />
+                        <!-- 6f (o), advance 611, 0 horiBearing 46,522 -->
+                        <path transform="translate(1610,0)" d="
+                              M 46, 680 
+                              C 46, 826 152, 947 306, 947 
+                              C 459, 947 565, 826 565, 680 
+                              C 565, 525 461, 413 306, 413 
+                              C 150, 413 46, 525 46, 680 
+                              M 146, 680 
+                              C 146, 583 205, 489 306, 489 
+                              C 406, 489 465, 583 465, 680 
+                              C 465, 771 416, 871 306, 871 
+                              C 195, 871 146, 771 146, 680 " />
+                        <!-- bounds: 38, 205 <-> 2232, 947 -->
+                   </g> </g>
+               </g>
+
+               <!-- scaled by 0.72, shifted around to hit pixel boundaries -->
+               <g id="cairo_text_small_spaced" transform="translate(0,-71)">
+                   <g transform="scale(0.085,0.085)"> <g transform="translate(-1139,-208.5)">
+                        <!-- 63 (c), advance 444, 0 horiBearing 38,522 -->
+                        <path transform="translate(-151,0)" d="
+                              M 412, 433 
+                              C 385, 422 336, 413 298, 413 
+                              C 142, 413 38, 525 38, 680 
+                              C 38, 826 144, 947 298, 947 
+                              C 332, 947 377, 944 416, 926 
+                              L 409, 842 
+                              C 380, 861 340, 871 308, 871 
+                              C 187, 871 138, 771 138, 680 
+                              C 138, 583 197, 489 302, 489 
+                              C 332, 489 368, 496 404, 511 
+                              L 412, 433 " />
+                        <!-- 61 (a), advance 556, 0 horiBearing 46,522 -->
+                        <path transform="translate(379.5,0)" d="
+                              M 109, 541 
+                              C 147, 509 204, 489 257, 489 
+                              C 351, 489 383, 534 383, 622 
+                              C 346, 620 320, 620 283, 620 
+                              C 186, 620 46, 660 46, 788 
+                              C 46, 899 123, 947 233, 947 
+                              C 319, 947 369, 900 391, 869 
+                              L 393, 869 
+                              L 393, 935 
+                              L 481, 935 
+                              C 479, 920 477, 893 477, 835 
+                              L 477, 624 
+                              C 477, 485 418, 413 272, 413 
+                              C 207, 413 151, 433 104, 461 
+                              L 109, 541 
+                              M 383, 737 
+                              C 383, 813 334, 871 241, 871 
+                              C 198, 871 146, 842 146, 788 
+                              C 146, 698 272, 690 323, 690 
+                              C 343, 690 363, 692 383, 692 
+                              L 383, 737 " />
+                        <!-- 69 (i), advance 278, 0 horiBearing 86,730 -->
+                        <path transform="translate(1000,0)" d="
+                              M 92, 935 
+                              L 186, 935 
+                              L 186, 425 
+                              L 92, 425 
+                              L 92, 935 
+                              M 88, 261
+                              A 51, 51 0 1 1 190,261
+                              A 51, 51 0 1 1 88,261" />
+                        <!-- 72 (r), advance 389, 0 horiBearing 80,522 -->
+                        <path transform="translate(1341.5,0)" d="
+                              M 80, 935 
+                              L 174, 935 
+                              L 174, 703 
+                              C 174, 575 229, 495 313, 495 
+                              C 329, 495 348, 497 365, 504 
+                              L 365, 420 
+                              C 345, 416 331, 413 303, 413 
+                              C 249, 413 195, 451 170, 504 
+                              L 168, 504 
+                              L 168, 425 
+                              L 80, 425 
+                              L 80, 935 " />
+                        <!-- 6f (o), advance 611, 0 horiBearing 46,522 -->
+                        <path transform="translate(1826,0)" d="
+                              M 46, 680 
+                              C 46, 826 152, 947 306, 947 
+                              C 459, 947 565, 826 565, 680 
+                              C 565, 525 461, 413 306, 413 
+                              C 150, 413 46, 525 46, 680 
+                              M 146, 680 
+                              C 146, 583 205, 489 306, 489 
+                              C 406, 489 465, 583 465, 680 
+                              C 465, 771 416, 871 306, 871 
+                              C 195, 871 146, 771 146, 680 " />
+                        <!-- bounds: 38, 205 <-> 2232, 947 -->
+                   </g> </g>
+               </g>
+
+
+               <!-- scaled by 0.72, shifted around to hit pixel boundaries -->
+               <g id="cairo_text_small" transform="translate(0,-71)">
+                   <g transform="scale(0.085,0.085)"> <g transform="translate(-1139,-208.5)">
+                        <!-- 63 (c), advance 444, 0 horiBearing 38,522 -->
+                        <path transform="translate(-151,0)" d="
+                              M 412, 433 
+                              C 385, 422 336, 413 298, 413 
+                              C 142, 413 38, 525 38, 680 
+                              C 38, 826 144, 947 298, 947 
+                              C 332, 947 377, 944 416, 926 
+                              L 409, 842 
+                              C 380, 861 340, 871 308, 871 
+                              C 187, 871 138, 771 138, 680 
+                              C 138, 583 197, 489 302, 489 
+                              C 332, 489 368, 496 404, 511 
+                              L 412, 433 " />
+                        <!-- 61 (a), advance 556, 0 horiBearing 46,522 -->
+                        <path transform="translate(261.75,0)" d="
+                              M 109, 541 
+                              C 147, 509 204, 489 257, 489 
+                              C 351, 489 383, 534 383, 622 
+                              C 346, 620 320, 620 283, 620 
+                              C 186, 620 46, 660 46, 788 
+                              C 46, 899 123, 947 233, 947 
+                              C 319, 947 369, 900 391, 869 
+                              L 393, 869 
+                              L 393, 935 
+                              L 481, 935 
+                              C 479, 920 477, 893 477, 835 
+                              L 477, 624 
+                              C 477, 485 418, 413 272, 413 
+                              C 207, 413 151, 433 104, 461 
+                              L 109, 541 
+                              M 383, 737 
+                              C 383, 813 334, 871 241, 871 
+                              C 198, 871 146, 842 146, 788 
+                              C 146, 698 272, 690 323, 690 
+                              C 343, 690 363, 692 383, 692 
+                              L 383, 737 " />
+                        <!-- 69 (i), advance 278, 0 horiBearing 86,730 -->
+                        <path transform="translate(764.75)" d="
+                              M 92, 935 
+                              L 186, 935 
+                              L 186, 425 
+                              L 92, 425 
+                              L 92, 935 
+                              M 88, 261
+                              A 51, 51 0 1 1 190,261
+                              A 51, 51 0 1 1 88,261" />
+                        <!-- 72 (r), advance 389, 0 horiBearing 80,522 -->
+                        <path transform="translate(988.5,0)" d="
+                              M 80, 935 
+                              L 174, 935 
+                              L 174, 703 
+                              C 174, 575 229, 495 313, 495 
+                              C 329, 495 348, 497 365, 504 
+                              L 365, 420 
+                              C 345, 416 331, 413 303, 413 
+                              C 249, 413 195, 451 170, 504 
+                              L 168, 504 
+                              L 168, 425 
+                              L 80, 425 
+                              L 80, 935 " />
+                        <!-- 6f (o), advance 611, 0 horiBearing 46,522 -->
+                        <path transform="translate(1355.5,0)" d="
+                              M 46, 680 
+                              C 46, 826 152, 947 306, 947 
+                              C 459, 947 565, 826 565, 680 
+                              C 565, 525 461, 413 306, 413 
+                              C 150, 413 46, 525 46, 680 
+                              M 146, 680 
+                              C 146, 583 205, 489 306, 489 
+                              C 406, 489 465, 583 465, 680 
+                              C 465, 771 416, 871 306, 871 
+                              C 195, 871 146, 771 146, 680 " />
+                        <!-- bounds: 38, 205 <-> 2232, 947 -->
+                   </g> </g>
+               </g>
+
+               <g id="cairo_logo_text_small">
+                       <!-- The logo on the left, the text 'cairo' on the right -->
+                       <use xlink:href="#cairo_logo_bottomleft-centered" transform="translate(0, 78), scale(0.1944)" />
+                       <use xlink:href="#cairo_text_small" fill="white" transform="translate(175,82)"/>
+               </g>
+
+               <g id="cairo_logo_with_text">
+                       <!-- The logo (scarab and dung), with the text 'cairo' below, the dot of the 'i' positioned between the hind legs of the scarab -->
+                       <!-- dimensions: 300x490, centered -->
+                       <use xlink:href="#cairo_logo_top-centered" transform="translate(0, -245)" />
+                       <use xlink:href="#cairo_text" transform="translate(0, 245)" />
+               </g>
+
+               <g id="cairo_banner">
+                       <!-- The logo on the left, the text 'cairo' in the center, and a mirror image of the logo on the right -->
+                       <!-- The logos are scaled such that the scarab body nearly matches the height of the text characters (excepting the 'i')
+                               and the dung should nearly aligns with the dot of the 'i'. The bottoms of the logos are aligned with the bottom of the text. -->
+                       <!-- dimensions: 370x88, centered -->
+                       <use xlink:href="#cairo_logo_bottomleft-centered" transform="translate(-180, 40), scale(0.1944)" />
+                       <use xlink:href="#cairo_text_small" transform="translate(0, 42)" fill="black" />
+                       <use xlink:href="#cairo_logo_bottomleft-centered" transform="translate(180, 40), scale(0.1944), scale(-1, 1)" />
+               </g>
+
+               <g id="freedesktop_org_logo" style="fill:#FFFFFF;stroke:#3B80AE;stroke-width:2.4588;">
+                 <g>
+                   <path style="stroke:#BABABA;" d="M85.277,40.796c2.058,7.884-2.667,15.942-10.551,17.999L27.143,71.21c-7.884,2.057-15.943-2.667-18-10.552
+                                                    l-7.448-28.55c-2.057-7.884,2.667-15.942,10.551-17.999L59.83,1.695c7.884-2.057,15.942,2.667,17.999,10.551
+                                                    l7.449,28.55z"/>>
+                   <path style="fill:#3B80AE;stroke:none;" d="M80.444,39.778c1.749,7.854-1.816,13.621-9.504,15.447l-42.236,11.02c-7.569,2.396-14.089-1.181
+                                                              -15.838-8.836L6.53,33.127c-1.749-8.145,0.709-12.889,9.503-15.447L58.27,6.661
+                                                              c8.144-1.826,14.089,1.363,15.838,8.835l6.336,24.282z"/>>
+                   </g>g>
+                   <path style="opacity:0.5;fill:none;stroke:#FFFFFF;" d="M45.542,51.793L24.104,31.102l38.1-4.393L45.542,51.793z"/>>
+                   <path d="M72.325,28.769c0.405,1.55-0.525,3.136-2.075,3.541l-12.331,3.217c-1.551,0.404-3.137-0.525-3.542-2.076l-2.295-8.801
+                            c-0.405-1.551,0.524-3.137,2.076-3.542l12.33-3.217c1.551-0.405,3.137,0.525,3.542,2.076l2.295,8.801z"/>>
+                   <path d="M36.51,33.625c0.496,1.9-0.645,3.844-2.545,4.34l-15.112,3.943c-1.901,0.496-3.845-0.644-4.34-2.544l-2.814-10.786
+                            c-0.496-1.901,0.644-3.844,2.544-4.34l15.113-3.942c1.901-0.496,3.845,0.643,4.34,2.544l2.814,10.786z"/>>
+                   <path d="M52.493,53.208c0.278,1.065-0.36,2.154-1.425,2.432L42.6,57.848c-1.064,0.277-2.153-0.36-2.431-1.426l-1.577-6.043
+                            c-0.277-1.064,0.36-2.153,1.425-2.432l8.468-2.209c1.064-0.277,2.154,0.361,2.431,1.426l1.577,6.043z"/>>
+                   </g>g>
+                   <g id="bullet">
+                     <use x="0" y="0" xlink:href="#cairo_logo" transform="translate(-6,-2) scale(0.1, 0.1)"/>>
+                   </g>
+                   <g id="redhat_logo_horizontal">
+                     <!-- 380x125 Red Hat log (horizontal layout) -->
+                      <g fill="black" stroke="none" 
+                       transform="translate(0,124),scale(1,-1),translate(-214,-258)"
+                       fill-rule="evenodd"
+                      >
+                       <!-- r -->
+                       <path fill="black" d="
+                        M 367.0625 315.3203
+                        C 367.0625 320.8765 366.9463 324.9644 366.7227 328.6597
+                        L 375.811 328.6597
+                        L 376.2002 320.7764
+                        L 376.4971 320.7764
+                        C 378.5391 326.6221 383.3809 329.5996 387.8594 329.5996
+                        C 388.8843 329.5996 389.4824 329.5601 390.3218 329.373
+                        L 390.3218 319.4863
+                        C 389.3398 319.6763 388.4224 319.7842 387.1592 319.7842
+                        C 382.1597 319.7842 378.688 316.6006 377.751 311.8447
+                        C 377.5732 310.918 377.4805 309.8086 377.4805 308.6777
+                        L 377.4805 287.1504
+                        L 366.9766 287.1504
+                        L 367.0625 315.3203
+                       " />
+
+                       <!-- e -->
+                       <path fill="black" d="
+                        M 402.9927 305.0791
+                        C 403.2715 297.5586 409.0918 294.2695 415.814 294.2695
+                        C 420.6406 294.2695 424.0942 295.0234 427.2681 296.1924
+                        L 428.8232 288.9678
+                        C 425.269 287.4629 420.3413 286.3359 414.3149 286.3359
+                        C 400.8384 286.3359 392.9409 294.6592 392.9409 307.3809
+                        C 392.9409 318.8369 399.8911 329.6772 413.2437 329.6772
+                        C 426.7397 329.6772 431.1338 318.5771 431.1338 309.4893
+                        C 431.1338 307.5381 430.9624 305.9707 430.7593 305.0059
+                        L 402.9927 305.0791
+
+                        M 421.2485 312.3926
+                        C 421.2954 316.2388 419.6206 322.5088 412.5903 322.5088
+                        C 406.1299 322.5088 403.4438 316.645 402.9722 312.3926
+                        L 421.2485 312.3926
+                       " />
+
+                       <!-- d -->
+                       <path fill="black" d="
+                        M 476.355 344.667
+                        L 465.8638 347.5083
+                        L 465.8638 324.1914
+                        L 465.6904 324.1914
+                        C 463.8335 327.2563 459.7407 329.5996 454.0571 329.5996
+                        C 444.0762 329.5996 435.3828 321.3374 435.4478 307.4307
+                        C 435.4478 294.6719 443.2983 286.2168 453.2119 286.2168
+                        C 459.2017 286.2168 464.2114 289.0723 466.6909 293.7217
+                        L 466.8779 293.7217
+                        L 467.3491 287.1504
+                        L 476.6997 287.1504
+                        C 476.5083 289.9717 476.355 294.543 476.355 298.792
+                        L 476.355 344.667
+
+                        M 465.8638 305.1504
+                        C 465.8638 304.0479 465.7856 303.0234 465.5454 302.0869
+                        C 464.4873 297.5439 460.7734 294.6172 456.4819 294.6172
+                        C 449.8721 294.6172 446.0903 300.1885 446.0903 307.8164
+                        C 446.0903 315.5166 449.8384 321.4761 456.6016 321.4761
+                        C 461.3208 321.4761 464.6992 318.1484 465.6274 314.1064
+                        C 465.8071 313.2559 465.8638 312.208 465.8638 311.3711
+                        L 465.8638 305.1504
+                       " />
+
+                       <!-- h -->
+                       <path fill="black" d="
+                        M 503.7964 329.0176
+                        C 500.6836 329.0176 497.8926 328.1187 495.5493 326.6714
+                        C 493.1162 325.2461 491.1353 323.0464 489.959 320.7666
+                        L 489.7915 320.7666
+                        L 489.7915 341.0195
+                        L 485.7427 342.1226
+                        L 485.7427 287.1504
+                        L 489.7915 287.1504
+                        L 489.7915 312.1787
+                        C 489.7915 313.8408 489.9204 314.9946 490.3462 316.2109
+                        C 492.0928 321.3013 496.8896 325.4805 502.689 325.4805
+                        C 511.0664 325.4805 513.9673 318.7603 513.9673 311.3906
+                        L 513.9673 287.1504
+                        L 518.0137 287.1504
+                        L 518.0137 311.8359
+                        C 518.0137 327.0791 507.6753 329.0176 503.7964 329.0176
+                       " />
+
+                       <!-- a -->
+                       <path fill="black" d="
+                        M 554.3413 296.873
+                        C 554.3413 293.6357 554.4692 290.2832 554.9375 287.1504
+                        L 551.2085 287.1504
+                        L 550.6128 293.0156
+                        L 550.4209 293.0156
+                        C 548.438 289.8594 543.8765 286.2168 537.3726 286.2168
+                        C 529.1392 286.2168 525.3057 292.0117 525.3057 297.4688
+                        C 525.3057 306.9121 533.6421 312.6064 550.292 312.4321
+                        L 550.292 313.5234
+                        C 550.292 317.5718 549.5044 325.6475 539.8242 325.5859
+                        C 536.2446 325.5859 532.5132 324.6255 529.5513 322.5366
+                        L 528.2632 325.4805
+                        C 532.0015 328.0137 536.5659 329.0176 540.2705 329.0176
+                        C 552.0801 329.0176 554.3413 320.1509 554.3413 312.8379
+                        L 554.3413 296.873
+
+                        M 550.292 309.0234
+                        C 541.3813 309.2813 529.6128 307.9336 529.6128 298.1055
+                        C 529.6128 292.2246 533.4946 289.5811 537.7578 289.5811
+                        C 544.5796 289.5811 548.4561 293.8018 549.8677 297.7871
+                        C 550.1646 298.6621 550.292 299.5371 550.292 300.2402
+                        L 550.292 309.0234
+                       " />
+
+                       <!-- t -->
+                       <path fill="black" d="
+                        M 570.459 337.0996
+                        L 570.459 328.0801
+                        L 582.1235 328.0801
+                        L 582.1235 324.7959
+                        L 570.459 324.7959
+                        L 570.459 298.1943
+                        C 570.459 292.9912 572.0757 289.7285 576.4692 289.7285
+                        C 578.5815 289.7285 580.0757 290.0078 581.1206 290.3711
+                        L 581.6099 287.2354
+                        C 580.2871 286.6836 578.4302 286.2539 575.9619 286.2539
+                        C 572.9741 286.2539 570.4995 287.1934 568.8994 289.1543
+                        C 567.0469 291.3057 566.4116 294.7412 566.4116 298.916
+                        L 566.4116 324.7959
+                        L 559.5059 324.7959
+                        L 559.5059 328.0801
+                        L 566.4116 328.0801
+                        L 566.4116 335.606
+                        L 570.459 337.0996
+                       " />
+
+                       <!-- ® for 'redhat' -->
+                       <path fill="black" d="
+                        M 335.5 288.9707
+                        L 336.0352 288.9707
+                        L 336.8408 287.6445
+                        L 337.3608 287.6445
+                        L 336.4888 288.9937
+                        C 336.9404 289.0498 337.2832 289.2881 337.2832 289.834
+                        C 337.2832 290.4385 336.9258 290.7051 336.2017 290.7051
+                        L 335.0366 290.7051
+                        L 335.0366 287.6445
+                        L 335.5 287.6445
+                        L 335.5 288.9707
+
+                        M 335.5 289.3643
+                        L 335.5 290.3101
+                        L 336.1318 290.3101
+                        C 336.4531 290.3101 336.7979 290.2402 336.7979 289.8647
+                        C 336.7979 289.3916 336.4492 289.3643 336.0566 289.3643
+                        L 335.5 289.3643
+                       " />
+
+                       <path fill="black" d="
+                        M 339.0439 289.1719
+                        C 339.0439 287.5176 337.7041 286.1763 336.0493 286.1763
+                        C 334.395 286.1763 333.0527 287.5176 333.0527 289.1719
+                        C 333.0527 290.8271 334.395 292.1675 336.0493 292.1675
+                        C 337.7041 292.1675 339.0439 290.8271 339.0439 289.1719
+
+                        M 336.0493 291.6367
+                        C 334.6865 291.6367 333.5835 290.5332 333.5835 289.1719
+                        C 333.5835 287.8096 334.6865 286.7061 336.0493 286.7061
+                        C 337.4082 286.7061 338.5117 287.8096 338.5117 289.1719
+                        C 338.5117 290.5332 337.4082 291.6367 336.0493 291.6367
+                       " />
+
+                       <!-- Black background behind The Shadowman -->
+                       <path fill="black" d="
+                        M 326.4531 286.208
+                        C 324.1177 286.7451 321.6396 287.0801 319.1338 287.0801
+                        C 314.8496 287.0801 310.9502 286.3389 308.0732 285.1426
+                        C 307.7559 284.9844 307.5303 284.6533 307.5303 284.2764
+                        C 307.5303 284.1406 307.5654 283.999 307.6172 283.8838
+                        C 307.957 282.8975 307.3984 281.8281 304.6157 281.2158
+                        C 300.4883 280.3096 297.8833 276.0527 296.3916 274.6367
+                        C 294.6411 272.9756 289.6973 271.9531 290.4404 272.9434
+                        C 291.0225 273.7188 293.2485 276.1348 294.6016 278.748
+                        C 295.8125 281.083 296.8906 281.7461 298.375 283.9736
+                        C 298.811 284.627 300.4995 286.9219 300.9912 288.7373
+                        C 301.543 290.5107 301.356 292.7344 301.5679 293.6494
+                        C 301.8721 294.9697 303.1182 297.8369 303.2129 299.4531
+                        C 303.2666 300.3691 299.3916 298.1494 297.5532 298.1494
+                        C 295.7144 298.1494 293.9233 299.248 292.2808 299.3281
+                        C 290.248 299.4248 288.9414 297.7607 287.1025 298.0508
+                        C 286.0518 298.2178 285.167 299.1426 283.3311 299.2129
+                        C 280.7178 299.3086 277.5244 297.7607 271.5264 297.9531
+                        C 265.6255 298.1436 260.1753 305.4082 259.4312 306.5635
+                        C 258.5605 307.9199 257.4961 307.9199 256.335 306.8555
+                        C 255.1738 305.792 253.7432 306.627 253.3359 307.3389
+                        C 252.5615 308.6943 250.4927 312.6543 247.2881 313.4824
+                        C 242.8564 314.6309 240.6118 311.0283 240.9033 308.1621
+                        C 241.1987 305.252 243.0801 304.4375 243.9512 302.8906
+                        C 244.8213 301.3428 245.2671 300.3428 246.9053 299.6572
+                        C 248.0674 299.1758 248.5 298.458 248.1533 297.5049
+                        C 247.8506 296.6738 246.6416 296.4834 245.8477 296.4463
+                        C 244.1592 296.3662 242.9756 296.8242 242.1123 297.376
+                        C 241.1084 298.0137 240.292 298.9033 239.416 300.4131
+                        C 238.4023 302.0781 236.8052 302.8037 234.9453 302.8037
+                        C 234.0586 302.8037 233.2295 302.5693 232.4922 302.1895
+                        C 229.5771 300.6748 226.1064 299.7744 222.3706 299.7744
+                        L 218.1572 299.7734
+                        C 216.1064 305.8555 214.9951 312.3682 214.9951 319.1416
+                        C 214.9951 352.6064 242.1226 379.7334 275.5859 379.7334
+                        C 309.0498 379.7334 336.1758 352.6064 336.1758 319.1416
+                        C 336.1758 307 332.6035 295.6895 326.4531 286.208
+                       " />
+
+                       <!-- The Shadowman's face -->
+                       <path fill="white" d="
+                        M 326.4531 286.209
+                        C 324.1177 286.7461 321.6396 287.084 319.1338 287.084
+                        C 314.8496 287.084 310.9502 286.3418 308.0732 285.1436
+                        C 307.7559 284.9873 307.5303 284.6553 307.5303 284.2783
+                        C 307.5303 284.1416 307.5654 284.001 307.6172 283.8838
+                        C 307.957 282.8994 307.3984 281.8311 304.6157 281.2178
+                        C 300.4883 280.3115 297.8833 276.0537 296.3916 274.6416
+                        C 294.6411 272.9766 289.6973 271.9551 290.4404 272.9463
+                        C 291.0225 273.7197 293.2485 276.1367 294.6016 278.749
+                        C 295.8125 281.083 296.8906 281.75 298.375 283.9756
+                        C 298.811 284.627 300.4995 286.9238 300.9912 288.7402
+                        C 301.543 290.5117 301.356 292.7354 301.5679 293.6514
+                        C 301.8721 294.9727 303.1182 297.8379 303.2129 299.457
+                        C 303.2666 300.3721 299.3916 298.1504 297.5532 298.1504
+                        C 295.7144 298.1504 293.9233 299.251 292.2808 299.3301
+                        C 290.248 299.4258 288.9414 297.7627 287.1025 298.0518
+                        C 286.0518 298.2207 285.167 299.1465 283.3311 299.2148
+                        C 280.7178 299.3096 277.5244 297.7627 271.5264 297.9561
+                        C 265.6255 298.1475 260.1753 305.4121 259.4312 306.5674
+                        C 258.5605 307.9219 257.4961 307.9219 256.335 306.8574
+                        C 255.1738 305.7939 253.7432 306.6299 253.3359 307.3438
+                        C 252.5615 308.6963 250.4927 312.6553 247.2881 313.4854
+                        C 242.8564 314.6338 240.6118 311.0313 240.9033 308.1641
+                        C 241.1987 305.2539 243.0801 304.4395 243.9512 302.8926
+                        C 244.8213 301.3438 245.2671 300.3457 246.9053 299.6621
+                        C 248.0674 299.1768 248.5 298.4609 248.1533 297.5068
+                        C 247.8506 296.6768 246.6416 296.4873 245.8477 296.4492
+                        C 244.1592 296.3672 242.9756 296.8262 242.1123 297.376
+                        C 241.1084 298.0176 240.292 298.9043 239.416 300.416
+                        C 238.4023 302.0801 236.8052 302.8086 234.9453 302.8086
+                        C 234.0586 302.8086 233.2295 302.5723 232.4922 302.1934
+                        C 229.5771 300.6748 226.1064 299.7773 222.3706 299.7773
+                        L 218.1572 299.7744
+                        C 226.2363 275.8105 248.8965 258.5527 275.5859 258.5527
+                        C 296.9063 258.5527 315.6538 269.5635 326.4531 286.209
+                       " />
+
+                       <!-- nose shadow -->
+                       <path fill="black" d="
+                        M 288.9307 291.7637
+                        C 289.2422 291.46 289.7793 290.4375 289.1226 289.1396
+                        C 288.7544 288.4521 288.3579 287.9678 287.6489 287.4023
+                        C 286.7969 286.7188 285.1309 285.9307 282.8457 287.3799
+                        C 281.6172 288.1592 281.543 288.4209 279.8467 288.2012
+                        C 278.6348 288.043 278.1533 289.2656 278.5884 290.2832
+                        C 279.0244 291.2969 280.8145 292.1191 283.04 290.8135
+                        C 284.041 290.2256 285.6025 288.9844 286.9688 290.084
+                        C 287.5356 290.5381 287.875 290.8408 288.6611 291.75
+                        C 288.6963 291.7881 288.7461 291.8105 288.8018 291.8105
+                        C 288.8516 291.8105 288.8965 291.793 288.9307 291.7637
+                       " />
+
+                       <!-- The Shadowman's red hat -->
+                       <path fill="#cc0000" d="
+                        M 309.7769 335.2627
+                        C 309.1787 333.251 308.3271 330.6763 304.5391 328.7314
+                        C 303.9878 328.4497 303.7764 328.9126 304.0313 329.3477
+                        C 305.4629 331.7832 305.7168 332.3921 306.1328 333.3525
+                        C 306.7148 334.7568 307.02 336.7549 305.8618 340.9219
+                        C 303.5835 349.1221 298.8296 360.083 295.375 363.6392
+                        C 292.04 367.0698 285.998 368.0361 280.5371 366.6348
+                        C 278.5264 366.1191 274.5918 364.0732 267.2939 365.7168
+                        C 254.665 368.5605 252.7939 362.2368 252.0693 359.4824
+                        C 251.3438 356.7271 249.6045 348.897 249.6045 348.897
+                        C 249.0244 345.7085 248.2646 340.1631 267.874 336.4287
+                        C 277.0088 334.6885 277.4736 332.3276 277.8779 330.6289
+                        C 278.603 327.585 279.7627 325.8438 281.0674 324.9746
+                        C 282.373 324.1035 281.0674 323.3828 279.6187 323.2349
+                        C 275.7285 322.8311 261.3491 326.9541 252.8428 331.7881
+                        C 245.8828 336.0415 245.7656 339.8721 247.3584 343.1211
+                        C 236.8452 344.2573 228.9561 342.1348 227.5254 337.1582
+                        C 225.0693 328.6157 246.3047 314.0264 270.4839 306.7061
+                        C 295.8579 299.0225 321.9556 304.3857 324.8564 320.335
+                        C 326.1738 327.5811 320.0713 332.9419 309.7769 335.2627
+                       " />
+
+                       <!-- shadow on hat -->
+                       <path fill="black" d="
+                        M 270.8711 350.8813
+                        C 263.8721 350.375 263.145 349.6191 261.834 348.2227
+                        C 259.9854 346.2539 257.5508 350.7773 257.5508 350.7773
+                        C 256.0898 351.085 254.3179 353.4404 255.2744 355.6411
+                        C 256.2158 357.8174 257.9551 357.1641 258.5 356.4868
+                        C 259.1626 355.6621 260.5771 354.3125 262.4141 354.3613
+                        C 264.251 354.4097 266.3706 354.7959 269.3262 354.7959
+                        C 272.3213 354.7959 274.335 353.6777 274.4487 352.7168
+                        C 274.5459 351.8965 274.2061 351.1226 270.8711 350.8813
+                       " />
+
+                       <!-- another shadow on hat -->
+                       <path fill="black" d="
+                        M 278.2236 362.4463
+                        C 278.2129 362.4453 278.2021 362.4438 278.1919 362.4438
+                        C 278.084 362.4438 277.9961 362.5273 277.9961 362.6274
+                        C 277.9961 362.7007 278.041 362.7646 278.106 362.7939
+                        C 279.4629 363.5107 281.4873 364.0811 283.8042 364.3169
+                        C 284.499 364.3887 285.1787 364.4248 285.832 364.4307
+                        C 285.9478 364.4307 286.0615 364.4297 286.1787 364.4277
+                        C 290.062 364.3398 293.1719 362.7974 293.1255 360.9814
+                        C 293.0791 359.165 289.8955 357.7637 286.0112 357.8506
+                        C 284.7529 357.8794 283.5732 358.0615 282.5576 358.3545
+                        C 282.4385 358.3857 282.3506 358.4883 282.3506 358.6094
+                        C 282.3506 358.731 282.4385 358.834 282.5605 358.8638
+                        C 284.9839 359.4248 286.6191 360.3408 286.5039 361.207
+                        C 286.3511 362.3545 283.1816 362.979 279.4248 362.6011
+                        C 279.0137 362.5596 278.6118 362.5068 278.2236 362.4463
+                       " />
+
+                       <!-- ® for The Shadowman -->
+                       <path fill="black" d="
+                        M 588.3018 288.9707
+                        L 588.8369 288.9707
+                        L 589.6426 287.6445
+                        L 590.1626 287.6445
+                        L 589.2905 288.9937
+                        C 589.7422 289.0498 590.085 289.2881 590.085 289.834
+                        C 590.085 290.4385 589.7275 290.7051 589.0034 290.7051
+                        L 587.8384 290.7051
+                        L 587.8384 287.6445
+                        L 588.3018 287.6445
+                        L 588.3018 288.9707
+
+                        M 588.3018 289.3643
+                        L 588.3018 290.3101
+                        L 588.9336 290.3101
+                        C 589.2549 290.3101 589.5996 290.2402 589.5996 289.8647
+                        C 589.5996 289.3916 589.251 289.3643 588.8584 289.3643
+                        L 588.3018 289.3643
+                       " />
+
+                       <path fill="black" d="
+                        M 591.8457 289.1719
+                        C 591.8457 287.5176 590.5059 286.1763 588.8511 286.1763
+                        C 587.1968 286.1763 585.8545 287.5176 585.8545 289.1719
+                        C 585.8545 290.8271 587.1968 292.1675 588.8511 292.1675
+                        C 590.5059 292.1675 591.8457 290.8271 591.8457 289.1719
+
+                        M 588.8511 291.6367
+                        C 587.4883 291.6367 586.3853 290.5332 586.3853 289.1719
+                        C 586.3853 287.8096 587.4883 286.7061 588.8511 286.7061
+                        C 590.21 286.7061 591.3135 287.8096 591.3135 289.1719
+                        C 591.3135 290.5332 590.21 291.6367 588.8511 291.6367
+                       " />
+
+                     </g>
+                   </g>
+
+       </defs>
+
+  <g id="watermark" transform="translate(200, 185), rotate(-50), scale(2.5)">
+    <use xlink:href="#scarab" x="0" y="170" fill-opacity="0.2"/>
+  </g>
+
+  <!-- Blue bar at top of slide -->
+  <rect x="0" y="0" width="1024" height="80" fill="#162284" />
+
+  <g font-family="Frutiger">
+    <!-- Slide title -->
+    <!-- Slide title -->
+    <g id="slide_title" transform="translate(512, 60)">
+      <text text-anchor="middle"
+           fill="white"
+           font-weight="bold"
+           x="0"
+           y="4" font-size="55"
+           ss:variable="title">Slide Title</text>
+    </g>
+    
+    <!-- Slide content -->
+    <g ss:region="default">
+      <rect x="112" y="120" width="800" height="580" fill="none" stroke="blue"/>
+      <text font-size="40" fill="black"
+           x="112" y="152">Slide content</text>
+    </g>
+    
+  </g>
+
+</svg>
diff --git a/doc/tutorial/slides/cairo-separator.svg b/doc/tutorial/slides/cairo-separator.svg
new file mode 100644 (file)
index 0000000..8d2c840
--- /dev/null
@@ -0,0 +1,909 @@
+<?xml version="1.0" ?>
+<svg width="1024" height="768"
+    xmlns="http://www.w3.org/2000/svg"
+    xmlns:xlink="http://www.w3.org/1999/xlink"
+    xmlns:ss="http://www.svgslides.org/svgslides0.1"
+    fill="black">
+
+
+
+       <defs id="cairo-artwork_defs">
+               <g id="hacker_emblem">
+                       <!-- Note: This is similar though not identical to Keith Packard's SVG version
+                               of the hacker emblem (http://www.catb.org/hacker-emblem/glider.svg) -->
+                       <g id="hacker_emblem_grid" fill="white" stroke="none">
+                               <!-- Outside: Top, Right, Bottom, Left -->
+                               <rect x="-2.95" y="-3.05" width="6"   height="0.1" />
+                               <rect x="2.95"  y="-2.95" width="0.1" height="6" />
+                               <rect x="-3.05" y="2.95"  width="6"   height="0.1" />
+                               <rect x="-3.05" y="-3.05" width="0.1" height="6" />
+                               <!-- Vertical: Left, Right -->
+                               <rect x="-1.05" y="-2.95" width="0.1" height="5.9" />
+                               <rect x="0.95"  y="-2.95" width="0.1" height="5.9" />
+                               <!-- Horizontal: TopLeft, TopMiddle, TopRight -->
+                               <rect x="-2.95" y="-1.05" width="1.9" height="0.1" />
+                               <rect x="-0.95" y="-1.05" width="1.9" height="0.1" />
+                               <rect x="1.05"  y="-1.05" width="1.9" height="0.1" />
+                               <!-- Horizontal: BottomLeft, BottomMiddle, BottomRight -->
+                               <rect x="-2.95" y="0.95"  width="1.9" height="0.1" />
+                               <rect x="-0.95" y="0.95"  width="1.9" height="0.1" />
+                               <rect x="1.05"  y="0.95"  width="1.9" height="0.1" />
+                       </g>
+                       <g id="hacker_emblem_dots" fill="white">
+                               <circle cx="0"  cy="-2" r="0.7" />
+                               <circle cx="2"  cy="0"  r="0.7" />
+                               <circle cx="-2" cy="2"  r="0.7" />
+                               <circle cx="0"  cy="2"  r="0.7" />
+                               <circle cx="2"  cy="2"  r="0.7" />
+                       </g>
+               </g>
+               <g id="scarab" fill="#f19a14">
+                       <g transform="translate(-150, -170)">
+                       <path id="scarab_head" d="M205.599,94.567c0-11.668-24.914-21.129-55.628-21.129
+                               c-30.723,0-55.624,9.46-55.624,21.129c0,10.203,24.901,7.346,55.624,7.346C180.685,101.913,205.599,104.233,205.599,94.567z"/>
+                       <path id="scarab_torso" d="M136.423,161.506c0,0,12.751,12.577,13.547,13.362
+                               c2.262-2.232,13.545-13.362,13.545-13.362c7.135-7.036,87.111-6.399,91.066-6.363c-0.469-6.298-1.254-12.472-2.325-18.519
+                               c-15.183-19.279-42.811-32.225-74.485-32.225h-55.518c-31.745,0-59.439,13.011-74.598,32.37c-1.054,6-1.829,12.128-2.296,18.374
+                               C49.321,155.106,129.288,154.47,136.423,161.506z"/>
+                       <path id="scarab_spine" d="M149.97,301.187c2.005-24.729,8.386-103.483,8.405-103.721
+                               c-0.09-0.219-6.478-15.578-8.405-20.214c-1.936,4.655-8.316,19.995-8.408,20.214C141.582,197.704,147.965,276.458,149.97,301.187z"/>
+                       <path id="scarab_wing_left" d="M140.403,197.149l8.862-21.31l-13.686-13.499
+                               c-5.65-5.573-67.074-6.235-90.259-6.019l-0.006-0.622c-0.154,2.144-0.271,4.302-0.35,6.475
+                               c-0.076,2.207,10.392,4.706,10.392,6.717c0,2.319-10.457,5.084-10.359,7.631c2.993,73.349,48.53,131.631,104.372,132.048
+                               l-9.02-111.29L140.403,197.149z"/>
+                       <path id="scarab_wing_right" d="M244.585,168.891c0-2.011,10.467-4.506,10.391-6.715
+                               c-0.079-2.174-0.195-4.332-0.351-6.479l-0.004,0.624c-23.186-0.216-84.608,0.445-90.26,6.017l-13.688,13.502l8.915,21.438
+                               l-9.017,111.29c55.854-0.417,101.378-58.698,104.373-132.049C255.04,173.976,244.585,171.209,244.585,168.891z"/>
+                       <path id="scarab_leg_front_left" d="M44.506,141.12c-4.135-0.856-4.895-1.54-7.935-2.92
+                               c-9.59-3.364-10.376-5.481-16.08-11.86c-7.426-8.306-12.661-20.142-17.1-29.463c-3.576-7.525-3.984-16.409-2.86-24.273
+                               c0.991-6.935,7.144-12.869,12.074-18.92c5.844-7.191,10.356-14.822,17.924-21.354c7.736-6.682,23.203-9.809,26.168-19.648
+                               C57.86,8.819,54.334,1.766,61.482,0c-0.366,4.703,3.639,8.477,2.397,13.575c-1.129,4.627-4.368,5.811-9.611,9.099
+                               c-7.564,4.746-18.366,8.779-24.748,13.965c-7.175,5.827-4.369,13.771-10.569,20.057c-2.001,2.03-7.901,4.706-9.137,6.83
+                               c-1.861,3.199-0.297,9.572-0.116,13.12c0.425,8.284,5.588,14.244,9.555,22.045c4.152,8.141,6.429,15.409,13.411,22.519
+                               c4.183,4.262,11.429,4.802,16.21,10.647l-3.555,4.186L44.506,141.12z"/>
+                       <path id="scarab_leg_middle_left" d="M43.94,191.922l-0.809-7.346
+                               c-9.506-4.579-10.339-9.772-20.738-12.466c-23.728-6.151-21.361,11.25-15.532,26.373c5.676,14.726,8.237,30.23,14.345,44.795
+                               c2.805,6.688,6.919,13.213,14.298,15.127c0.372-8.435-0.917-10.651-6.113-16.919c-4.395-5.293-3.326-12.548-6.072-18.504
+                               c-3.581-7.804-4.196-15.646-7.279-23.502c-1.363-3.479-8.33-13.966-6.452-17.861c3.183-6.603,9.178-0.083,12.179,2.077
+                               c4.218,3.036,6.467,2.223,11.681,2.898C34.041,186.673,37.005,188.756,43.94,191.922z"/>
+                       <path id="scarab_leg_back_left" d="M65.839,257.063l-2.771-4.837
+                               c-6.68,8.928-6.993,16.228-10.056,23.347c-5.277,12.263-0.157,28.851,9.854,37.676c6.052,5.375,15.907,9.618,23.122,13.136
+                               c10.035,4.892,20.113,11.286,31.336,13.396c2.482,0.466,8.798,1.295,6.693-3.522c-0.975-2.237-8.091-4.591-10.146-5.734
+                               c-8.312-4.623-16.377-10.524-24.142-16.176c-9.498-6.862-20.843-11.186-28.311-20.684c-3.054-3.885-3.544-4.922-2.816-9.39
+                               c0.693-4.263,1.344-9.174,2.241-13.439C61.855,266.029,63.274,261.378,65.839,257.063z"/>
+                       <path id="scarab_leg_front_right" d="M255.487,141.12c4.134-0.856,4.896-1.54,7.936-2.92
+                               c9.583-3.364,10.369-5.481,16.071-11.86c7.428-8.306,12.661-20.142,17.115-29.463c3.574-7.525,3.983-16.409,2.86-24.273
+                               c-0.992-6.935-7.157-12.869-12.087-18.92c-5.843-7.191-10.356-14.822-17.919-21.354c-7.735-6.682-23.202-9.809-26.167-19.648
+                               C242.135,8.819,245.66,1.766,238.511,0c0.366,4.703-3.637,8.477-2.396,13.575c1.131,4.627,4.368,5.811,9.611,9.099
+                               c7.563,4.746,18.367,8.779,24.747,13.965c7.17,5.827,4.362,13.771,10.563,20.057c2.001,2.03,7.901,4.706,9.139,6.83
+                               c1.859,3.199,0.295,9.572,0.113,13.12c-0.424,8.284-5.588,14.244-9.553,22.045c-4.152,8.141-6.431,15.409-13.404,22.519
+                               c-4.184,4.262-11.429,4.802-16.211,10.647l3.556,4.186L255.487,141.12z"/>
+                       <path id="scarab_leg_middle_right" d="M256.053,191.922l0.81-7.346
+                               c9.507-4.579,10.34-9.772,20.73-12.466c23.741-6.151,21.374,11.25,15.534,26.373c-5.676,14.726-8.238,30.23-14.347,44.795
+                               c-2.804,6.688-6.911,13.213-14.291,15.127c-0.371-8.435,0.918-10.651,6.113-16.919c4.39-5.293,3.319-12.548,6.066-18.504
+                               c3.58-7.804,4.197-15.646,7.278-23.502c1.363-3.479,8.33-13.966,6.453-17.861c-3.184-6.603-9.179-0.083-12.181,2.077
+                               c-4.217,3.036-6.458,2.223-11.672,2.898C265.951,186.673,262.986,188.756,256.053,191.922z"/>
+                       <path id="scarab_leg_back_right" d="M234.155,257.063l2.771-4.837
+                               c6.679,8.928,6.991,16.228,10.057,23.347c5.274,12.263,0.154,28.851-9.854,37.676c-6.055,5.375-15.903,9.618-23.117,13.136
+                               c-10.034,4.892-20.127,11.286-31.351,13.396c-2.481,0.466-8.789,1.295-6.691-3.522c0.976-2.237,8.092-4.591,10.146-5.734
+                               c8.312-4.623,16.392-10.524,24.155-16.176c9.498-6.862,20.838-11.186,28.305-20.684c3.055-3.885,3.543-4.922,2.818-9.39
+                               c-0.696-4.263-1.346-9.174-2.244-13.439C238.137,266.029,236.718,261.378,234.155,257.063z"/>
+                       </g>
+               </g>
+               <radialGradient id="gradient_radial_dung"
+                               cx="0" cy="0" r="60"
+                               fx="0" fy="0" gradientUnits="userSpaceOnUse"
+               >
+                       <stop offset="0"    stop-color="#9a9a9a" />
+                       <stop offset="0.70" stop-color="#bababa" />
+                       <stop offset="0.95" stop-color="#FFFFFF" />
+               </radialGradient>
+               <g id="dung">
+                       <circle cx="0" cy="0" r="60" fill="url(#gradient_radial_dung)" />
+                       <g transform="translate(-61, -61)">
+                               <!-- rough equivalent: <circle cx="0" cy="0" r="60" stroke="#8a8a8a" stroke-width="2" /> -->
+                               <path fill="#8a8a8a" d="M0,61c0,33.636,27.364,61,61,61s61-27.364,61-61S94.636,0,61,0S0,27.364,0,61z
+                                                       M2,61C2,28.467,28.467,2,61,2c32.532,0,59,26.467,59,59c0,32.533-26.468,59-59,59C28.467,120,2,93.533,2,61z"/>
+                       </g>
+                       <use xlink:href="#hacker_emblem" x="0" y="0" transform="scale(9)" />
+               </g>
+               <g id="dung_2_color">
+                       <!-- This would be simple a circle like so:
+                               <circle cx="0" cy="0" r="48" stroke-width="2" fill="none" stroke="white"/>
+                            but there appears to currently be a bug in the cairo
+                            PDF backend that results in an ugly spike in that
+                            case. So we use 8 splines instead.
+                       -->
+                       <path stroke-width="4" stroke="white" fill="none" d="
+M 48,                  0
+C 48,                  12.730391512298112, 
+  42.94287166245995,   24.939379331448613
+  33.941125496954285,  33.941125496954278
+C 24.939379331448624,  42.942871662459943
+  12.730391512298114,  48
+  0,                   48
+C -12.730391512298109, 48, 
+  -24.939379331448613, 42.94287166245995
+  -33.941125496954278, 33.941125496954285
+C -42.942871662459943, 24.939379331448624,
+  -48,                 12.730391512298118,
+  -48,                 0,
+C -48,                 -12.730391512298105, 
+  -42.94287166245995,  -24.939379331448613,
+  -33.941125496954285, -33.941125496954278
+C -24.939379331448624, -42.942871662459943,
+  -12.730391512298119, -48, 
+  0,                   -48
+C 12.730391512298104,  -48, 
+  24.939379331448606,  -42.942871662459943,
+  33.941125496954271,  -33.941125496954285
+C 42.942871662459936,  -24.939379331448624,
+  48,                  -12.730391512298123,
+  48,                  0" />
+                       <g transform="scale(9)" fill="white" stroke="none" stroke-width="0.22222">
+                               <!-- Hacker emblem grid -->
+                               <!--
+                               <path stroke="white" fill="none"
+                                     d="M -3,-3 L  3,-3 L 3,3 L-3,3 Z
+                                        M -1,-3 L -1, 3
+                                        M  1,-3 L  1, 3
+                                        M -3,-1 L  3,-1
+                                        M -3, 1 L  3, 1"/>
+                                        -->
+
+                               <!-- Hacker emblem dots -->
+                               <circle cx="0"  cy="-2" r="0.7" />
+                               <circle cx="2"  cy="0"  r="0.7" />
+                               <circle cx="-2" cy="2"  r="0.7" />
+                               <circle cx="0"  cy="2"  r="0.7" />
+                               <circle cx="2"  cy="2"  r="0.7" />
+                       </g>
+               </g>
+
+               <!-- scarab dimensions: 300x340 -->
+               <!-- dung dimensions: 120x120 (radius: 60) -->
+               <!-- scarab and dung dimensions: 300x400 -->
+
+               <g id="cairo_logo">
+                       <!-- dimensions: 300x400, centered -->
+                       <!-- The logo (scarab and dung), with the center-point of the bounding box at (0,0) -->
+                       <use xlink:href="#dung_2_color" x="0" y="0" transform="translate(0, -140)" />
+                       <use xlink:href="#scarab" x="0" y="0" transform="translate(0, 30)" />
+               </g>
+               <g id="cairo_logo_dung-centered">
+                       <!-- The logo (scarab and dung), with the dung at (0,0), the scarab below -->
+                       <use xlink:href="#dung_2_color" x="0" y="0" />
+                       <use xlink:href="#scarab" x="0" y="0" transform="translate(0,170)" />
+               </g>
+               <g id="cairo_logo_scarab-centered">
+                       <!-- The logo (scarab and dung), with the scarab's rotational center at (0,0), the dung above -->
+                       <!-- The scarab's rotational center in this case is not the center of its bounding box,
+                               but is calculated to be the intersection-point of the torso, spine and wings -->
+                       <use xlink:href="#dung_2_color" x="0" y="0" transform="translate(0, -175.85)" />
+                       <use xlink:href="#scarab" x="0" y="0" transform="translate(0, -5.85)" />
+               </g>
+               <g id="cairo_logo_top-centered">
+                       <!-- The logo (scarab and dung), with the top-center point of the bounding box at (0,0) -->
+                       <use xlink:href="#dung_2_color" x="0" y="0" transform="translate(0, 60)" />
+                       <use xlink:href="#scarab" x="0" y="0" transform="translate(0, 230)" /><!-- (0,170+60) -->
+               </g>
+               <g id="cairo_logo_bottom-centered">
+                       <!-- The logo (scarab and dung), with the bottom-center point of the bounding box at (0,0) -->
+                       <use xlink:href="#dung_2_color" x="0" y="0" transform="translate(0, -340)" />
+                       <use xlink:href="#scarab" x="0" y="0" transform="translate(0, -170)" />
+               </g>
+               <g id="cairo_logo_right-centered">
+                       <!-- The logo (scarab and dung), with the right-center point of the bounding box at (0,0) -->
+                       <use xlink:href="#dung_2_color" x="0" y="0" transform="translate(-150, -140)" />
+                       <use xlink:href="#scarab" x="0" y="0" transform="translate(-150, 30)" />
+               </g>
+               <g id="cairo_logo_left-centered">
+                       <!-- The logo (scarab and dung), with the left-center point of the bounding box at (0,0) -->
+                       <use xlink:href="#dung_2_color" x="0" y="0" transform="translate(150, -140)" />
+                       <use xlink:href="#scarab" x="0" y="0" transform="translate(150, 30)" />
+               </g>
+               <g id="cairo_logo_topleft-centered">
+                       <!-- The logo (scarab and dung), with the top-left point of the bounding box at (0,0) -->
+                       <use xlink:href="#dung_2_color" x="0" y="0" transform="translate(150, 60)" />
+                       <use xlink:href="#scarab" x="0" y="0" transform="translate(150, 230)" /><!-- (150, 170+60) -->
+               </g>
+               <g id="cairo_logo_topright-centered">
+                       <!-- The logo (scarab and dung), with the top-right point of the bounding box at (0,0) -->
+                       <use xlink:href="#dung_2_color" x="0" y="0" transform="translate(-150, 60)" />
+                       <use xlink:href="#scarab" x="0" y="0" transform="translate(-150, 230)" /><!-- (-150,170+60) -->
+               </g>
+               <g id="cairo_logo_bottomleft-centered">
+                       <!-- The logo (scarab and dung), with the bottom-left point of the bounding box at (0,0) -->
+                       <use xlink:href="#dung_2_color" x="0" y="0" transform="translate(150, -340)" />
+                       <use xlink:href="#scarab" x="0" y="0" transform="translate(150, -170)" />
+               </g>
+               <g id="cairo_logo_bottomright-centered">
+                       <!-- The logo (scarab and dung), with the bottom-right point of the bounding box at (0,0) -->
+                       <use xlink:href="#dung_2_color" x="0" y="0" transform="translate(-150, -340)" />
+                       <use xlink:href="#scarab" x="0" y="0" transform="translate(-150, -170)" />
+               </g>
+
+               <g id="cairo_text" transform="translate(0,-97)">
+                   <g transform="scale(0.1484,0.1484)"> <g transform="translate(-1139,-208.5)">
+                        <!-- 63 (c), advance 444, 0 horiBearing 38,522 -->
+                        <path transform="translate(65,0)" d="
+                              M 412, 433 
+                              C 385, 422 336, 413 298, 413 
+                              C 142, 413 38, 525 38, 680 
+                              C 38, 826 144, 947 298, 947 
+                              C 332, 947 377, 944 416, 926 
+                              L 409, 842 
+                              C 380, 861 340, 871 308, 871 
+                              C 187, 871 138, 771 138, 680 
+                              C 138, 583 197, 489 302, 489 
+                              C 332, 489 368, 496 404, 511 
+                              L 412, 433 " />
+                        <!-- 61 (a), advance 556, 0 horiBearing 46,522 -->
+                        <path transform="translate(486.75,0)" d="
+                              M 109, 541 
+                              C 147, 509 204, 489 257, 489 
+                              C 351, 489 383, 534 383, 622 
+                              C 346, 620 320, 620 283, 620 
+                              C 186, 620 46, 660 46, 788 
+                              C 46, 899 123, 947 233, 947 
+                              C 319, 947 369, 900 391, 869 
+                              L 393, 869 
+                              L 393, 935 
+                              L 481, 935 
+                              C 479, 920 477, 893 477, 835 
+                              L 477, 624 
+                              C 477, 485 418, 413 272, 413 
+                              C 207, 413 151, 433 104, 461 
+                              L 109, 541 
+                              M 383, 737 
+                              C 383, 813 334, 871 241, 871 
+                              C 198, 871 146, 842 146, 788 
+                              C 146, 698 272, 690 323, 690 
+                              C 343, 690 363, 692 383, 692 
+                              L 383, 737 " />
+                        <!-- 69 (i), advance 278, 0 horiBearing 86,730 -->
+                        <path transform="translate(1000,0)" d="
+                              M 92, 935 
+                              L 186, 935 
+                              L 186, 425 
+                              L 92, 425 
+                              L 92, 935 
+                              M 88, 261
+                              A 51, 51 0 1 1 190,261
+                              A 51, 51 0 1 1 88,261" />
+                        <!-- 72 (r), advance 389, 0 horiBearing 80,522 -->
+                        <path transform="translate(1234.25,0)" d="
+                              M 80, 935 
+                              L 174, 935 
+                              L 174, 703 
+                              C 174, 575 229, 495 313, 495 
+                              C 329, 495 348, 497 365, 504 
+                              L 365, 420 
+                              C 345, 416 331, 413 303, 413 
+                              C 249, 413 195, 451 170, 504 
+                              L 168, 504 
+                              L 168, 425 
+                              L 80, 425 
+                              L 80, 935 " />
+                        <!-- 6f (o), advance 611, 0 horiBearing 46,522 -->
+                        <path transform="translate(1610,0)" d="
+                              M 46, 680 
+                              C 46, 826 152, 947 306, 947 
+                              C 459, 947 565, 826 565, 680 
+                              C 565, 525 461, 413 306, 413 
+                              C 150, 413 46, 525 46, 680 
+                              M 146, 680 
+                              C 146, 583 205, 489 306, 489 
+                              C 406, 489 465, 583 465, 680 
+                              C 465, 771 416, 871 306, 871 
+                              C 195, 871 146, 771 146, 680 " />
+                        <!-- bounds: 38, 205 <-> 2232, 947 -->
+                   </g> </g>
+               </g>
+
+               <!-- scaled by 0.72, shifted around to hit pixel boundaries -->
+               <g id="cairo_text_small_spaced" transform="translate(0,-71)">
+                   <g transform="scale(0.085,0.085)"> <g transform="translate(-1139,-208.5)">
+                        <!-- 63 (c), advance 444, 0 horiBearing 38,522 -->
+                        <path transform="translate(-151,0)" d="
+                              M 412, 433 
+                              C 385, 422 336, 413 298, 413 
+                              C 142, 413 38, 525 38, 680 
+                              C 38, 826 144, 947 298, 947 
+                              C 332, 947 377, 944 416, 926 
+                              L 409, 842 
+                              C 380, 861 340, 871 308, 871 
+                              C 187, 871 138, 771 138, 680 
+                              C 138, 583 197, 489 302, 489 
+                              C 332, 489 368, 496 404, 511 
+                              L 412, 433 " />
+                        <!-- 61 (a), advance 556, 0 horiBearing 46,522 -->
+                        <path transform="translate(379.5,0)" d="
+                              M 109, 541 
+                              C 147, 509 204, 489 257, 489 
+                              C 351, 489 383, 534 383, 622 
+                              C 346, 620 320, 620 283, 620 
+                              C 186, 620 46, 660 46, 788 
+                              C 46, 899 123, 947 233, 947 
+                              C 319, 947 369, 900 391, 869 
+                              L 393, 869 
+                              L 393, 935 
+                              L 481, 935 
+                              C 479, 920 477, 893 477, 835 
+                              L 477, 624 
+                              C 477, 485 418, 413 272, 413 
+                              C 207, 413 151, 433 104, 461 
+                              L 109, 541 
+                              M 383, 737 
+                              C 383, 813 334, 871 241, 871 
+                              C 198, 871 146, 842 146, 788 
+                              C 146, 698 272, 690 323, 690 
+                              C 343, 690 363, 692 383, 692 
+                              L 383, 737 " />
+                        <!-- 69 (i), advance 278, 0 horiBearing 86,730 -->
+                        <path transform="translate(1000,0)" d="
+                              M 92, 935 
+                              L 186, 935 
+                              L 186, 425 
+                              L 92, 425 
+                              L 92, 935 
+                              M 88, 261
+                              A 51, 51 0 1 1 190,261
+                              A 51, 51 0 1 1 88,261" />
+                        <!-- 72 (r), advance 389, 0 horiBearing 80,522 -->
+                        <path transform="translate(1341.5,0)" d="
+                              M 80, 935 
+                              L 174, 935 
+                              L 174, 703 
+                              C 174, 575 229, 495 313, 495 
+                              C 329, 495 348, 497 365, 504 
+                              L 365, 420 
+                              C 345, 416 331, 413 303, 413 
+                              C 249, 413 195, 451 170, 504 
+                              L 168, 504 
+                              L 168, 425 
+                              L 80, 425 
+                              L 80, 935 " />
+                        <!-- 6f (o), advance 611, 0 horiBearing 46,522 -->
+                        <path transform="translate(1826,0)" d="
+                              M 46, 680 
+                              C 46, 826 152, 947 306, 947 
+                              C 459, 947 565, 826 565, 680 
+                              C 565, 525 461, 413 306, 413 
+                              C 150, 413 46, 525 46, 680 
+                              M 146, 680 
+                              C 146, 583 205, 489 306, 489 
+                              C 406, 489 465, 583 465, 680 
+                              C 465, 771 416, 871 306, 871 
+                              C 195, 871 146, 771 146, 680 " />
+                        <!-- bounds: 38, 205 <-> 2232, 947 -->
+                   </g> </g>
+               </g>
+
+
+               <!-- scaled by 0.72, shifted around to hit pixel boundaries -->
+               <g id="cairo_text_small" transform="translate(0,-71)">
+                   <g transform="scale(0.085,0.085)"> <g transform="translate(-1139,-208.5)">
+                        <!-- 63 (c), advance 444, 0 horiBearing 38,522 -->
+                        <path transform="translate(-151,0)" d="
+                              M 412, 433 
+                              C 385, 422 336, 413 298, 413 
+                              C 142, 413 38, 525 38, 680 
+                              C 38, 826 144, 947 298, 947 
+                              C 332, 947 377, 944 416, 926 
+                              L 409, 842 
+                              C 380, 861 340, 871 308, 871 
+                              C 187, 871 138, 771 138, 680 
+                              C 138, 583 197, 489 302, 489 
+                              C 332, 489 368, 496 404, 511 
+                              L 412, 433 " />
+                        <!-- 61 (a), advance 556, 0 horiBearing 46,522 -->
+                        <path transform="translate(261.75,0)" d="
+                              M 109, 541 
+                              C 147, 509 204, 489 257, 489 
+                              C 351, 489 383, 534 383, 622 
+                              C 346, 620 320, 620 283, 620 
+                              C 186, 620 46, 660 46, 788 
+                              C 46, 899 123, 947 233, 947 
+                              C 319, 947 369, 900 391, 869 
+                              L 393, 869 
+                              L 393, 935 
+                              L 481, 935 
+                              C 479, 920 477, 893 477, 835 
+                              L 477, 624 
+                              C 477, 485 418, 413 272, 413 
+                              C 207, 413 151, 433 104, 461 
+                              L 109, 541 
+                              M 383, 737 
+                              C 383, 813 334, 871 241, 871 
+                              C 198, 871 146, 842 146, 788 
+                              C 146, 698 272, 690 323, 690 
+                              C 343, 690 363, 692 383, 692 
+                              L 383, 737 " />
+                        <!-- 69 (i), advance 278, 0 horiBearing 86,730 -->
+                        <path transform="translate(764.75)" d="
+                              M 92, 935 
+                              L 186, 935 
+                              L 186, 425 
+                              L 92, 425 
+                              L 92, 935 
+                              M 88, 261
+                              A 51, 51 0 1 1 190,261
+                              A 51, 51 0 1 1 88,261" />
+                        <!-- 72 (r), advance 389, 0 horiBearing 80,522 -->
+                        <path transform="translate(988.5,0)" d="
+                              M 80, 935 
+                              L 174, 935 
+                              L 174, 703 
+                              C 174, 575 229, 495 313, 495 
+                              C 329, 495 348, 497 365, 504 
+                              L 365, 420 
+                              C 345, 416 331, 413 303, 413 
+                              C 249, 413 195, 451 170, 504 
+                              L 168, 504 
+                              L 168, 425 
+                              L 80, 425 
+                              L 80, 935 " />
+                        <!-- 6f (o), advance 611, 0 horiBearing 46,522 -->
+                        <path transform="translate(1355.5,0)" d="
+                              M 46, 680 
+                              C 46, 826 152, 947 306, 947 
+                              C 459, 947 565, 826 565, 680 
+                              C 565, 525 461, 413 306, 413 
+                              C 150, 413 46, 525 46, 680 
+                              M 146, 680 
+                              C 146, 583 205, 489 306, 489 
+                              C 406, 489 465, 583 465, 680 
+                              C 465, 771 416, 871 306, 871 
+                              C 195, 871 146, 771 146, 680 " />
+                        <!-- bounds: 38, 205 <-> 2232, 947 -->
+                   </g> </g>
+               </g>
+
+               <g id="cairo_logo_text_small">
+                       <!-- The logo on the left, the text 'cairo' on the right -->
+                       <use xlink:href="#cairo_logo_bottomleft-centered" transform="translate(0, 78), scale(0.1944)" />
+                       <use xlink:href="#cairo_text_small" fill="white" transform="translate(175,82)"/>
+               </g>
+
+               <g id="cairo_logo_with_text">
+                       <!-- The logo (scarab and dung), with the text 'cairo' below, the dot of the 'i' positioned between the hind legs of the scarab -->
+                       <!-- dimensions: 300x490, centered -->
+                       <use xlink:href="#cairo_logo_top-centered" transform="translate(0, -245)" />
+                       <use xlink:href="#cairo_text" transform="translate(0, 245)" />
+               </g>
+
+               <g id="cairo_banner">
+                       <!-- The logo on the left, the text 'cairo' in the center, and a mirror image of the logo on the right -->
+                       <!-- The logos are scaled such that the scarab body nearly matches the height of the text characters (excepting the 'i')
+                               and the dung should nearly aligns with the dot of the 'i'. The bottoms of the logos are aligned with the bottom of the text. -->
+                       <!-- dimensions: 370x88, centered -->
+                       <use xlink:href="#cairo_logo_bottomleft-centered" transform="translate(-180, 40), scale(0.1944)" />
+                       <use xlink:href="#cairo_text_small" transform="translate(0, 42)" fill="black" />
+                       <use xlink:href="#cairo_logo_bottomleft-centered" transform="translate(180, 40), scale(0.1944), scale(-1, 1)" />
+               </g>
+
+               <g id="freedesktop_org_logo" style="fill:#FFFFFF;stroke:#3B80AE;stroke-width:2.4588;">
+                 <g>
+                   <path style="stroke:#BABABA;" d="M85.277,40.796c2.058,7.884-2.667,15.942-10.551,17.999L27.143,71.21c-7.884,2.057-15.943-2.667-18-10.552
+                                                    l-7.448-28.55c-2.057-7.884,2.667-15.942,10.551-17.999L59.83,1.695c7.884-2.057,15.942,2.667,17.999,10.551
+                                                    l7.449,28.55z"/>>
+                   <path style="fill:#3B80AE;stroke:none;" d="M80.444,39.778c1.749,7.854-1.816,13.621-9.504,15.447l-42.236,11.02c-7.569,2.396-14.089-1.181
+                                                              -15.838-8.836L6.53,33.127c-1.749-8.145,0.709-12.889,9.503-15.447L58.27,6.661
+                                                              c8.144-1.826,14.089,1.363,15.838,8.835l6.336,24.282z"/>>
+                   </g>g>
+                   <path style="opacity:0.5;fill:none;stroke:#FFFFFF;" d="M45.542,51.793L24.104,31.102l38.1-4.393L45.542,51.793z"/>>
+                   <path d="M72.325,28.769c0.405,1.55-0.525,3.136-2.075,3.541l-12.331,3.217c-1.551,0.404-3.137-0.525-3.542-2.076l-2.295-8.801
+                            c-0.405-1.551,0.524-3.137,2.076-3.542l12.33-3.217c1.551-0.405,3.137,0.525,3.542,2.076l2.295,8.801z"/>>
+                   <path d="M36.51,33.625c0.496,1.9-0.645,3.844-2.545,4.34l-15.112,3.943c-1.901,0.496-3.845-0.644-4.34-2.544l-2.814-10.786
+                            c-0.496-1.901,0.644-3.844,2.544-4.34l15.113-3.942c1.901-0.496,3.845,0.643,4.34,2.544l2.814,10.786z"/>>
+                   <path d="M52.493,53.208c0.278,1.065-0.36,2.154-1.425,2.432L42.6,57.848c-1.064,0.277-2.153-0.36-2.431-1.426l-1.577-6.043
+                            c-0.277-1.064,0.36-2.153,1.425-2.432l8.468-2.209c1.064-0.277,2.154,0.361,2.431,1.426l1.577,6.043z"/>>
+                   </g>g>
+                   <g id="bullet">
+                     <use x="0" y="0" xlink:href="#cairo_logo" transform="translate(-6,-2) scale(0.1, 0.1)"/>>
+                   </g>
+                   <g id="redhat_logo_horizontal">
+                     <!-- 380x125 Red Hat log (horizontal layout) -->
+                      <g fill="black" stroke="none" 
+                       transform="translate(0,124),scale(1,-1),translate(-214,-258)"
+                       fill-rule="evenodd"
+                      >
+                       <!-- r -->
+                       <path fill="black" d="
+                        M 367.0625 315.3203
+                        C 367.0625 320.8765 366.9463 324.9644 366.7227 328.6597
+                        L 375.811 328.6597
+                        L 376.2002 320.7764
+                        L 376.4971 320.7764
+                        C 378.5391 326.6221 383.3809 329.5996 387.8594 329.5996
+                        C 388.8843 329.5996 389.4824 329.5601 390.3218 329.373
+                        L 390.3218 319.4863
+                        C 389.3398 319.6763 388.4224 319.7842 387.1592 319.7842
+                        C 382.1597 319.7842 378.688 316.6006 377.751 311.8447
+                        C 377.5732 310.918 377.4805 309.8086 377.4805 308.6777
+                        L 377.4805 287.1504
+                        L 366.9766 287.1504
+                        L 367.0625 315.3203
+                       " />
+
+                       <!-- e -->
+                       <path fill="black" d="
+                        M 402.9927 305.0791
+                        C 403.2715 297.5586 409.0918 294.2695 415.814 294.2695
+                        C 420.6406 294.2695 424.0942 295.0234 427.2681 296.1924
+                        L 428.8232 288.9678
+                        C 425.269 287.4629 420.3413 286.3359 414.3149 286.3359
+                        C 400.8384 286.3359 392.9409 294.6592 392.9409 307.3809
+                        C 392.9409 318.8369 399.8911 329.6772 413.2437 329.6772
+                        C 426.7397 329.6772 431.1338 318.5771 431.1338 309.4893
+                        C 431.1338 307.5381 430.9624 305.9707 430.7593 305.0059
+                        L 402.9927 305.0791
+
+                        M 421.2485 312.3926
+                        C 421.2954 316.2388 419.6206 322.5088 412.5903 322.5088
+                        C 406.1299 322.5088 403.4438 316.645 402.9722 312.3926
+                        L 421.2485 312.3926
+                       " />
+
+                       <!-- d -->
+                       <path fill="black" d="
+                        M 476.355 344.667
+                        L 465.8638 347.5083
+                        L 465.8638 324.1914
+                        L 465.6904 324.1914
+                        C 463.8335 327.2563 459.7407 329.5996 454.0571 329.5996
+                        C 444.0762 329.5996 435.3828 321.3374 435.4478 307.4307
+                        C 435.4478 294.6719 443.2983 286.2168 453.2119 286.2168
+                        C 459.2017 286.2168 464.2114 289.0723 466.6909 293.7217
+                        L 466.8779 293.7217
+                        L 467.3491 287.1504
+                        L 476.6997 287.1504
+                        C 476.5083 289.9717 476.355 294.543 476.355 298.792
+                        L 476.355 344.667
+
+                        M 465.8638 305.1504
+                        C 465.8638 304.0479 465.7856 303.0234 465.5454 302.0869
+                        C 464.4873 297.5439 460.7734 294.6172 456.4819 294.6172
+                        C 449.8721 294.6172 446.0903 300.1885 446.0903 307.8164
+                        C 446.0903 315.5166 449.8384 321.4761 456.6016 321.4761
+                        C 461.3208 321.4761 464.6992 318.1484 465.6274 314.1064
+                        C 465.8071 313.2559 465.8638 312.208 465.8638 311.3711
+                        L 465.8638 305.1504
+                       " />
+
+                       <!-- h -->
+                       <path fill="black" d="
+                        M 503.7964 329.0176
+                        C 500.6836 329.0176 497.8926 328.1187 495.5493 326.6714
+                        C 493.1162 325.2461 491.1353 323.0464 489.959 320.7666
+                        L 489.7915 320.7666
+                        L 489.7915 341.0195
+                        L 485.7427 342.1226
+                        L 485.7427 287.1504
+                        L 489.7915 287.1504
+                        L 489.7915 312.1787
+                        C 489.7915 313.8408 489.9204 314.9946 490.3462 316.2109
+                        C 492.0928 321.3013 496.8896 325.4805 502.689 325.4805
+                        C 511.0664 325.4805 513.9673 318.7603 513.9673 311.3906
+                        L 513.9673 287.1504
+                        L 518.0137 287.1504
+                        L 518.0137 311.8359
+                        C 518.0137 327.0791 507.6753 329.0176 503.7964 329.0176
+                       " />
+
+                       <!-- a -->
+                       <path fill="black" d="
+                        M 554.3413 296.873
+                        C 554.3413 293.6357 554.4692 290.2832 554.9375 287.1504
+                        L 551.2085 287.1504
+                        L 550.6128 293.0156
+                        L 550.4209 293.0156
+                        C 548.438 289.8594 543.8765 286.2168 537.3726 286.2168
+                        C 529.1392 286.2168 525.3057 292.0117 525.3057 297.4688
+                        C 525.3057 306.9121 533.6421 312.6064 550.292 312.4321
+                        L 550.292 313.5234
+                        C 550.292 317.5718 549.5044 325.6475 539.8242 325.5859
+                        C 536.2446 325.5859 532.5132 324.6255 529.5513 322.5366
+                        L 528.2632 325.4805
+                        C 532.0015 328.0137 536.5659 329.0176 540.2705 329.0176
+                        C 552.0801 329.0176 554.3413 320.1509 554.3413 312.8379
+                        L 554.3413 296.873
+
+                        M 550.292 309.0234
+                        C 541.3813 309.2813 529.6128 307.9336 529.6128 298.1055
+                        C 529.6128 292.2246 533.4946 289.5811 537.7578 289.5811
+                        C 544.5796 289.5811 548.4561 293.8018 549.8677 297.7871
+                        C 550.1646 298.6621 550.292 299.5371 550.292 300.2402
+                        L 550.292 309.0234
+                       " />
+
+                       <!-- t -->
+                       <path fill="black" d="
+                        M 570.459 337.0996
+                        L 570.459 328.0801
+                        L 582.1235 328.0801
+                        L 582.1235 324.7959
+                        L 570.459 324.7959
+                        L 570.459 298.1943
+                        C 570.459 292.9912 572.0757 289.7285 576.4692 289.7285
+                        C 578.5815 289.7285 580.0757 290.0078 581.1206 290.3711
+                        L 581.6099 287.2354
+                        C 580.2871 286.6836 578.4302 286.2539 575.9619 286.2539
+                        C 572.9741 286.2539 570.4995 287.1934 568.8994 289.1543
+                        C 567.0469 291.3057 566.4116 294.7412 566.4116 298.916
+                        L 566.4116 324.7959
+                        L 559.5059 324.7959
+                        L 559.5059 328.0801
+                        L 566.4116 328.0801
+                        L 566.4116 335.606
+                        L 570.459 337.0996
+                       " />
+
+                       <!-- ® for 'redhat' -->
+                       <path fill="black" d="
+                        M 335.5 288.9707
+                        L 336.0352 288.9707
+                        L 336.8408 287.6445
+                        L 337.3608 287.6445
+                        L 336.4888 288.9937
+                        C 336.9404 289.0498 337.2832 289.2881 337.2832 289.834
+                        C 337.2832 290.4385 336.9258 290.7051 336.2017 290.7051
+                        L 335.0366 290.7051
+                        L 335.0366 287.6445
+                        L 335.5 287.6445
+                        L 335.5 288.9707
+
+                        M 335.5 289.3643
+                        L 335.5 290.3101
+                        L 336.1318 290.3101
+                        C 336.4531 290.3101 336.7979 290.2402 336.7979 289.8647
+                        C 336.7979 289.3916 336.4492 289.3643 336.0566 289.3643
+                        L 335.5 289.3643
+                       " />
+
+                       <path fill="black" d="
+                        M 339.0439 289.1719
+                        C 339.0439 287.5176 337.7041 286.1763 336.0493 286.1763
+                        C 334.395 286.1763 333.0527 287.5176 333.0527 289.1719
+                        C 333.0527 290.8271 334.395 292.1675 336.0493 292.1675
+                        C 337.7041 292.1675 339.0439 290.8271 339.0439 289.1719
+
+                        M 336.0493 291.6367
+                        C 334.6865 291.6367 333.5835 290.5332 333.5835 289.1719
+                        C 333.5835 287.8096 334.6865 286.7061 336.0493 286.7061
+                        C 337.4082 286.7061 338.5117 287.8096 338.5117 289.1719
+                        C 338.5117 290.5332 337.4082 291.6367 336.0493 291.6367
+                       " />
+
+                       <!-- Black background behind The Shadowman -->
+                       <path fill="black" d="
+                        M 326.4531 286.208
+                        C 324.1177 286.7451 321.6396 287.0801 319.1338 287.0801
+                        C 314.8496 287.0801 310.9502 286.3389 308.0732 285.1426
+                        C 307.7559 284.9844 307.5303 284.6533 307.5303 284.2764
+                        C 307.5303 284.1406 307.5654 283.999 307.6172 283.8838
+                        C 307.957 282.8975 307.3984 281.8281 304.6157 281.2158
+                        C 300.4883 280.3096 297.8833 276.0527 296.3916 274.6367
+                        C 294.6411 272.9756 289.6973 271.9531 290.4404 272.9434
+                        C 291.0225 273.7188 293.2485 276.1348 294.6016 278.748
+                        C 295.8125 281.083 296.8906 281.7461 298.375 283.9736
+                        C 298.811 284.627 300.4995 286.9219 300.9912 288.7373
+                        C 301.543 290.5107 301.356 292.7344 301.5679 293.6494
+                        C 301.8721 294.9697 303.1182 297.8369 303.2129 299.4531
+                        C 303.2666 300.3691 299.3916 298.1494 297.5532 298.1494
+                        C 295.7144 298.1494 293.9233 299.248 292.2808 299.3281
+                        C 290.248 299.4248 288.9414 297.7607 287.1025 298.0508
+                        C 286.0518 298.2178 285.167 299.1426 283.3311 299.2129
+                        C 280.7178 299.3086 277.5244 297.7607 271.5264 297.9531
+                        C 265.6255 298.1436 260.1753 305.4082 259.4312 306.5635
+                        C 258.5605 307.9199 257.4961 307.9199 256.335 306.8555
+                        C 255.1738 305.792 253.7432 306.627 253.3359 307.3389
+                        C 252.5615 308.6943 250.4927 312.6543 247.2881 313.4824
+                        C 242.8564 314.6309 240.6118 311.0283 240.9033 308.1621
+                        C 241.1987 305.252 243.0801 304.4375 243.9512 302.8906
+                        C 244.8213 301.3428 245.2671 300.3428 246.9053 299.6572
+                        C 248.0674 299.1758 248.5 298.458 248.1533 297.5049
+                        C 247.8506 296.6738 246.6416 296.4834 245.8477 296.4463
+                        C 244.1592 296.3662 242.9756 296.8242 242.1123 297.376
+                        C 241.1084 298.0137 240.292 298.9033 239.416 300.4131
+                        C 238.4023 302.0781 236.8052 302.8037 234.9453 302.8037
+                        C 234.0586 302.8037 233.2295 302.5693 232.4922 302.1895
+                        C 229.5771 300.6748 226.1064 299.7744 222.3706 299.7744
+                        L 218.1572 299.7734
+                        C 216.1064 305.8555 214.9951 312.3682 214.9951 319.1416
+                        C 214.9951 352.6064 242.1226 379.7334 275.5859 379.7334
+                        C 309.0498 379.7334 336.1758 352.6064 336.1758 319.1416
+                        C 336.1758 307 332.6035 295.6895 326.4531 286.208
+                       " />
+
+                       <!-- The Shadowman's face -->
+                       <path fill="white" d="
+                        M 326.4531 286.209
+                        C 324.1177 286.7461 321.6396 287.084 319.1338 287.084
+                        C 314.8496 287.084 310.9502 286.3418 308.0732 285.1436
+                        C 307.7559 284.9873 307.5303 284.6553 307.5303 284.2783
+                        C 307.5303 284.1416 307.5654 284.001 307.6172 283.8838
+                        C 307.957 282.8994 307.3984 281.8311 304.6157 281.2178
+                        C 300.4883 280.3115 297.8833 276.0537 296.3916 274.6416
+                        C 294.6411 272.9766 289.6973 271.9551 290.4404 272.9463
+                        C 291.0225 273.7197 293.2485 276.1367 294.6016 278.749
+                        C 295.8125 281.083 296.8906 281.75 298.375 283.9756
+                        C 298.811 284.627 300.4995 286.9238 300.9912 288.7402
+                        C 301.543 290.5117 301.356 292.7354 301.5679 293.6514
+                        C 301.8721 294.9727 303.1182 297.8379 303.2129 299.457
+                        C 303.2666 300.3721 299.3916 298.1504 297.5532 298.1504
+                        C 295.7144 298.1504 293.9233 299.251 292.2808 299.3301
+                        C 290.248 299.4258 288.9414 297.7627 287.1025 298.0518
+                        C 286.0518 298.2207 285.167 299.1465 283.3311 299.2148
+                        C 280.7178 299.3096 277.5244 297.7627 271.5264 297.9561
+                        C 265.6255 298.1475 260.1753 305.4121 259.4312 306.5674
+                        C 258.5605 307.9219 257.4961 307.9219 256.335 306.8574
+                        C 255.1738 305.7939 253.7432 306.6299 253.3359 307.3438
+                        C 252.5615 308.6963 250.4927 312.6553 247.2881 313.4854
+                        C 242.8564 314.6338 240.6118 311.0313 240.9033 308.1641
+                        C 241.1987 305.2539 243.0801 304.4395 243.9512 302.8926
+                        C 244.8213 301.3438 245.2671 300.3457 246.9053 299.6621
+                        C 248.0674 299.1768 248.5 298.4609 248.1533 297.5068
+                        C 247.8506 296.6768 246.6416 296.4873 245.8477 296.4492
+                        C 244.1592 296.3672 242.9756 296.8262 242.1123 297.376
+                        C 241.1084 298.0176 240.292 298.9043 239.416 300.416
+                        C 238.4023 302.0801 236.8052 302.8086 234.9453 302.8086
+                        C 234.0586 302.8086 233.2295 302.5723 232.4922 302.1934
+                        C 229.5771 300.6748 226.1064 299.7773 222.3706 299.7773
+                        L 218.1572 299.7744
+                        C 226.2363 275.8105 248.8965 258.5527 275.5859 258.5527
+                        C 296.9063 258.5527 315.6538 269.5635 326.4531 286.209
+                       " />
+
+                       <!-- nose shadow -->
+                       <path fill="black" d="
+                        M 288.9307 291.7637
+                        C 289.2422 291.46 289.7793 290.4375 289.1226 289.1396
+                        C 288.7544 288.4521 288.3579 287.9678 287.6489 287.4023
+                        C 286.7969 286.7188 285.1309 285.9307 282.8457 287.3799
+                        C 281.6172 288.1592 281.543 288.4209 279.8467 288.2012
+                        C 278.6348 288.043 278.1533 289.2656 278.5884 290.2832
+                        C 279.0244 291.2969 280.8145 292.1191 283.04 290.8135
+                        C 284.041 290.2256 285.6025 288.9844 286.9688 290.084
+                        C 287.5356 290.5381 287.875 290.8408 288.6611 291.75
+                        C 288.6963 291.7881 288.7461 291.8105 288.8018 291.8105
+                        C 288.8516 291.8105 288.8965 291.793 288.9307 291.7637
+                       " />
+
+                       <!-- The Shadowman's red hat -->
+                       <path fill="#cc0000" d="
+                        M 309.7769 335.2627
+                        C 309.1787 333.251 308.3271 330.6763 304.5391 328.7314
+                        C 303.9878 328.4497 303.7764 328.9126 304.0313 329.3477
+                        C 305.4629 331.7832 305.7168 332.3921 306.1328 333.3525
+                        C 306.7148 334.7568 307.02 336.7549 305.8618 340.9219
+                        C 303.5835 349.1221 298.8296 360.083 295.375 363.6392
+                        C 292.04 367.0698 285.998 368.0361 280.5371 366.6348
+                        C 278.5264 366.1191 274.5918 364.0732 267.2939 365.7168
+                        C 254.665 368.5605 252.7939 362.2368 252.0693 359.4824
+                        C 251.3438 356.7271 249.6045 348.897 249.6045 348.897
+                        C 249.0244 345.7085 248.2646 340.1631 267.874 336.4287
+                        C 277.0088 334.6885 277.4736 332.3276 277.8779 330.6289
+                        C 278.603 327.585 279.7627 325.8438 281.0674 324.9746
+                        C 282.373 324.1035 281.0674 323.3828 279.6187 323.2349
+                        C 275.7285 322.8311 261.3491 326.9541 252.8428 331.7881
+                        C 245.8828 336.0415 245.7656 339.8721 247.3584 343.1211
+                        C 236.8452 344.2573 228.9561 342.1348 227.5254 337.1582
+                        C 225.0693 328.6157 246.3047 314.0264 270.4839 306.7061
+                        C 295.8579 299.0225 321.9556 304.3857 324.8564 320.335
+                        C 326.1738 327.5811 320.0713 332.9419 309.7769 335.2627
+                       " />
+
+                       <!-- shadow on hat -->
+                       <path fill="black" d="
+                        M 270.8711 350.8813
+                        C 263.8721 350.375 263.145 349.6191 261.834 348.2227
+                        C 259.9854 346.2539 257.5508 350.7773 257.5508 350.7773
+                        C 256.0898 351.085 254.3179 353.4404 255.2744 355.6411
+                        C 256.2158 357.8174 257.9551 357.1641 258.5 356.4868
+                        C 259.1626 355.6621 260.5771 354.3125 262.4141 354.3613
+                        C 264.251 354.4097 266.3706 354.7959 269.3262 354.7959
+                        C 272.3213 354.7959 274.335 353.6777 274.4487 352.7168
+                        C 274.5459 351.8965 274.2061 351.1226 270.8711 350.8813
+                       " />
+
+                       <!-- another shadow on hat -->
+                       <path fill="black" d="
+                        M 278.2236 362.4463
+                        C 278.2129 362.4453 278.2021 362.4438 278.1919 362.4438
+                        C 278.084 362.4438 277.9961 362.5273 277.9961 362.6274
+                        C 277.9961 362.7007 278.041 362.7646 278.106 362.7939
+                        C 279.4629 363.5107 281.4873 364.0811 283.8042 364.3169
+                        C 284.499 364.3887 285.1787 364.4248 285.832 364.4307
+                        C 285.9478 364.4307 286.0615 364.4297 286.1787 364.4277
+                        C 290.062 364.3398 293.1719 362.7974 293.1255 360.9814
+                        C 293.0791 359.165 289.8955 357.7637 286.0112 357.8506
+                        C 284.7529 357.8794 283.5732 358.0615 282.5576 358.3545
+                        C 282.4385 358.3857 282.3506 358.4883 282.3506 358.6094
+                        C 282.3506 358.731 282.4385 358.834 282.5605 358.8638
+                        C 284.9839 359.4248 286.6191 360.3408 286.5039 361.207
+                        C 286.3511 362.3545 283.1816 362.979 279.4248 362.6011
+                        C 279.0137 362.5596 278.6118 362.5068 278.2236 362.4463
+                       " />
+
+                       <!-- ® for The Shadowman -->
+                       <path fill="black" d="
+                        M 588.3018 288.9707
+                        L 588.8369 288.9707
+                        L 589.6426 287.6445
+                        L 590.1626 287.6445
+                        L 589.2905 288.9937
+                        C 589.7422 289.0498 590.085 289.2881 590.085 289.834
+                        C 590.085 290.4385 589.7275 290.7051 589.0034 290.7051
+                        L 587.8384 290.7051
+                        L 587.8384 287.6445
+                        L 588.3018 287.6445
+                        L 588.3018 288.9707
+
+                        M 588.3018 289.3643
+                        L 588.3018 290.3101
+                        L 588.9336 290.3101
+                        C 589.2549 290.3101 589.5996 290.2402 589.5996 289.8647
+                        C 589.5996 289.3916 589.251 289.3643 588.8584 289.3643
+                        L 588.3018 289.3643
+                       " />
+
+                       <path fill="black" d="
+                        M 591.8457 289.1719
+                        C 591.8457 287.5176 590.5059 286.1763 588.8511 286.1763
+                        C 587.1968 286.1763 585.8545 287.5176 585.8545 289.1719
+                        C 585.8545 290.8271 587.1968 292.1675 588.8511 292.1675
+                        C 590.5059 292.1675 591.8457 290.8271 591.8457 289.1719
+
+                        M 588.8511 291.6367
+                        C 587.4883 291.6367 586.3853 290.5332 586.3853 289.1719
+                        C 586.3853 287.8096 587.4883 286.7061 588.8511 286.7061
+                        C 590.21 286.7061 591.3135 287.8096 591.3135 289.1719
+                        C 591.3135 290.5332 590.21 291.6367 588.8511 291.6367
+                       " />
+
+                     </g>
+                   </g>
+
+       </defs>
+
+  <g id="watermark" transform="translate(200, 185), rotate(-50), scale(2.5)">
+    <use xlink:href="#scarab" x="0" y="170" fill-opacity="0.2"/>
+  </g>
+
+  <!-- Blue bar at top of slide -->
+  <rect x="0" y="0" width="1024" height="170" fill="#162284" />
+
+  <!-- Scarab and "cairo" at upper-left -->
+  <g transform="translate(10,0)">
+    <use stroke="none" xlink:href="#cairo_logo_text_small"/>
+  </g>
+
+  <!-- Presentation title at upper-right -->
+  <text ss:variable="presentation-subtitle" text-anchor="end"
+     fill="white" x="1016" y="28" font-size="20">Presentation Sub-title</text>
+
+  <!-- Red Hat logo at lower-left -->
+  <use xlink:href="#redhat_logo_horizontal" transform="translate(8,768),scale(.5,.5),translate(0, -125)" />
+
+  <g font-family="Frutiger">
+    <text text-anchor="middle"
+         fill="black"
+         x="512"
+         y="300" font-size="80"
+         font-weight="bold"
+         ss:variable="title">Slide Title</text>
+    
+    <!-- Slide content -->
+    <g ss:region="default">
+      <rect x="512" y="400" width="2" height="280" fill="none" stroke="blue"/>
+      <text font-size="50" fill="black" text-anchor="middle"
+           x="512" y="450">Slide content</text>
+    </g>
+    
+    <!-- Footer -->
+    <text ss:variable="URL" x="1016" y="753" text-anchor="end" font-size="20">http://cairographics.org</text>
+  </g>
+
+</svg>
diff --git a/doc/tutorial/slides/cairo-title.svg b/doc/tutorial/slides/cairo-title.svg
new file mode 100644 (file)
index 0000000..31b14f8
--- /dev/null
@@ -0,0 +1,898 @@
+<?xml version="1.0" ?>
+<svg width="1024" height="768"
+    xmlns="http://www.w3.org/2000/svg"
+    xmlns:xlink="http://www.w3.org/1999/xlink"
+    xmlns:ss="http://www.svgslides.org/svgslides0.1"
+    fill="black">
+
+
+       <defs id="cairo-artwork_defs">
+               <g id="hacker_emblem">
+                       <!-- Note: This is similar though not identical to Keith Packard's SVG version
+                               of the hacker emblem (http://www.catb.org/hacker-emblem/glider.svg) -->
+                       <g id="hacker_emblem_grid" fill="white" stroke="none">
+                               <!-- Outside: Top, Right, Bottom, Left -->
+                               <rect x="-2.95" y="-3.05" width="6"   height="0.1" />
+                               <rect x="2.95"  y="-2.95" width="0.1" height="6" />
+                               <rect x="-3.05" y="2.95"  width="6"   height="0.1" />
+                               <rect x="-3.05" y="-3.05" width="0.1" height="6" />
+                               <!-- Vertical: Left, Right -->
+                               <rect x="-1.05" y="-2.95" width="0.1" height="5.9" />
+                               <rect x="0.95"  y="-2.95" width="0.1" height="5.9" />
+                               <!-- Horizontal: TopLeft, TopMiddle, TopRight -->
+                               <rect x="-2.95" y="-1.05" width="1.9" height="0.1" />
+                               <rect x="-0.95" y="-1.05" width="1.9" height="0.1" />
+                               <rect x="1.05"  y="-1.05" width="1.9" height="0.1" />
+                               <!-- Horizontal: BottomLeft, BottomMiddle, BottomRight -->
+                               <rect x="-2.95" y="0.95"  width="1.9" height="0.1" />
+                               <rect x="-0.95" y="0.95"  width="1.9" height="0.1" />
+                               <rect x="1.05"  y="0.95"  width="1.9" height="0.1" />
+                       </g>
+                       <g id="hacker_emblem_dots" fill="white">
+                               <circle cx="0"  cy="-2" r="0.7" />
+                               <circle cx="2"  cy="0"  r="0.7" />
+                               <circle cx="-2" cy="2"  r="0.7" />
+                               <circle cx="0"  cy="2"  r="0.7" />
+                               <circle cx="2"  cy="2"  r="0.7" />
+                       </g>
+               </g>
+               <g id="scarab" fill="#f19a14">
+                       <g transform="translate(-150, -170)">
+                       <path id="scarab_head" d="M205.599,94.567c0-11.668-24.914-21.129-55.628-21.129
+                               c-30.723,0-55.624,9.46-55.624,21.129c0,10.203,24.901,7.346,55.624,7.346C180.685,101.913,205.599,104.233,205.599,94.567z"/>
+                       <path id="scarab_torso" d="M136.423,161.506c0,0,12.751,12.577,13.547,13.362
+                               c2.262-2.232,13.545-13.362,13.545-13.362c7.135-7.036,87.111-6.399,91.066-6.363c-0.469-6.298-1.254-12.472-2.325-18.519
+                               c-15.183-19.279-42.811-32.225-74.485-32.225h-55.518c-31.745,0-59.439,13.011-74.598,32.37c-1.054,6-1.829,12.128-2.296,18.374
+                               C49.321,155.106,129.288,154.47,136.423,161.506z"/>
+                       <path id="scarab_spine" d="M149.97,301.187c2.005-24.729,8.386-103.483,8.405-103.721
+                               c-0.09-0.219-6.478-15.578-8.405-20.214c-1.936,4.655-8.316,19.995-8.408,20.214C141.582,197.704,147.965,276.458,149.97,301.187z"/>
+                       <path id="scarab_wing_left" d="M140.403,197.149l8.862-21.31l-13.686-13.499
+                               c-5.65-5.573-67.074-6.235-90.259-6.019l-0.006-0.622c-0.154,2.144-0.271,4.302-0.35,6.475
+                               c-0.076,2.207,10.392,4.706,10.392,6.717c0,2.319-10.457,5.084-10.359,7.631c2.993,73.349,48.53,131.631,104.372,132.048
+                               l-9.02-111.29L140.403,197.149z"/>
+                       <path id="scarab_wing_right" d="M244.585,168.891c0-2.011,10.467-4.506,10.391-6.715
+                               c-0.079-2.174-0.195-4.332-0.351-6.479l-0.004,0.624c-23.186-0.216-84.608,0.445-90.26,6.017l-13.688,13.502l8.915,21.438
+                               l-9.017,111.29c55.854-0.417,101.378-58.698,104.373-132.049C255.04,173.976,244.585,171.209,244.585,168.891z"/>
+                       <path id="scarab_leg_front_left" d="M44.506,141.12c-4.135-0.856-4.895-1.54-7.935-2.92
+                               c-9.59-3.364-10.376-5.481-16.08-11.86c-7.426-8.306-12.661-20.142-17.1-29.463c-3.576-7.525-3.984-16.409-2.86-24.273
+                               c0.991-6.935,7.144-12.869,12.074-18.92c5.844-7.191,10.356-14.822,17.924-21.354c7.736-6.682,23.203-9.809,26.168-19.648
+                               C57.86,8.819,54.334,1.766,61.482,0c-0.366,4.703,3.639,8.477,2.397,13.575c-1.129,4.627-4.368,5.811-9.611,9.099
+                               c-7.564,4.746-18.366,8.779-24.748,13.965c-7.175,5.827-4.369,13.771-10.569,20.057c-2.001,2.03-7.901,4.706-9.137,6.83
+                               c-1.861,3.199-0.297,9.572-0.116,13.12c0.425,8.284,5.588,14.244,9.555,22.045c4.152,8.141,6.429,15.409,13.411,22.519
+                               c4.183,4.262,11.429,4.802,16.21,10.647l-3.555,4.186L44.506,141.12z"/>
+                       <path id="scarab_leg_middle_left" d="M43.94,191.922l-0.809-7.346
+                               c-9.506-4.579-10.339-9.772-20.738-12.466c-23.728-6.151-21.361,11.25-15.532,26.373c5.676,14.726,8.237,30.23,14.345,44.795
+                               c2.805,6.688,6.919,13.213,14.298,15.127c0.372-8.435-0.917-10.651-6.113-16.919c-4.395-5.293-3.326-12.548-6.072-18.504
+                               c-3.581-7.804-4.196-15.646-7.279-23.502c-1.363-3.479-8.33-13.966-6.452-17.861c3.183-6.603,9.178-0.083,12.179,2.077
+                               c4.218,3.036,6.467,2.223,11.681,2.898C34.041,186.673,37.005,188.756,43.94,191.922z"/>
+                       <path id="scarab_leg_back_left" d="M65.839,257.063l-2.771-4.837
+                               c-6.68,8.928-6.993,16.228-10.056,23.347c-5.277,12.263-0.157,28.851,9.854,37.676c6.052,5.375,15.907,9.618,23.122,13.136
+                               c10.035,4.892,20.113,11.286,31.336,13.396c2.482,0.466,8.798,1.295,6.693-3.522c-0.975-2.237-8.091-4.591-10.146-5.734
+                               c-8.312-4.623-16.377-10.524-24.142-16.176c-9.498-6.862-20.843-11.186-28.311-20.684c-3.054-3.885-3.544-4.922-2.816-9.39
+                               c0.693-4.263,1.344-9.174,2.241-13.439C61.855,266.029,63.274,261.378,65.839,257.063z"/>
+                       <path id="scarab_leg_front_right" d="M255.487,141.12c4.134-0.856,4.896-1.54,7.936-2.92
+                               c9.583-3.364,10.369-5.481,16.071-11.86c7.428-8.306,12.661-20.142,17.115-29.463c3.574-7.525,3.983-16.409,2.86-24.273
+                               c-0.992-6.935-7.157-12.869-12.087-18.92c-5.843-7.191-10.356-14.822-17.919-21.354c-7.735-6.682-23.202-9.809-26.167-19.648
+                               C242.135,8.819,245.66,1.766,238.511,0c0.366,4.703-3.637,8.477-2.396,13.575c1.131,4.627,4.368,5.811,9.611,9.099
+                               c7.563,4.746,18.367,8.779,24.747,13.965c7.17,5.827,4.362,13.771,10.563,20.057c2.001,2.03,7.901,4.706,9.139,6.83
+                               c1.859,3.199,0.295,9.572,0.113,13.12c-0.424,8.284-5.588,14.244-9.553,22.045c-4.152,8.141-6.431,15.409-13.404,22.519
+                               c-4.184,4.262-11.429,4.802-16.211,10.647l3.556,4.186L255.487,141.12z"/>
+                       <path id="scarab_leg_middle_right" d="M256.053,191.922l0.81-7.346
+                               c9.507-4.579,10.34-9.772,20.73-12.466c23.741-6.151,21.374,11.25,15.534,26.373c-5.676,14.726-8.238,30.23-14.347,44.795
+                               c-2.804,6.688-6.911,13.213-14.291,15.127c-0.371-8.435,0.918-10.651,6.113-16.919c4.39-5.293,3.319-12.548,6.066-18.504
+                               c3.58-7.804,4.197-15.646,7.278-23.502c1.363-3.479,8.33-13.966,6.453-17.861c-3.184-6.603-9.179-0.083-12.181,2.077
+                               c-4.217,3.036-6.458,2.223-11.672,2.898C265.951,186.673,262.986,188.756,256.053,191.922z"/>
+                       <path id="scarab_leg_back_right" d="M234.155,257.063l2.771-4.837
+                               c6.679,8.928,6.991,16.228,10.057,23.347c5.274,12.263,0.154,28.851-9.854,37.676c-6.055,5.375-15.903,9.618-23.117,13.136
+                               c-10.034,4.892-20.127,11.286-31.351,13.396c-2.481,0.466-8.789,1.295-6.691-3.522c0.976-2.237,8.092-4.591,10.146-5.734
+                               c8.312-4.623,16.392-10.524,24.155-16.176c9.498-6.862,20.838-11.186,28.305-20.684c3.055-3.885,3.543-4.922,2.818-9.39
+                               c-0.696-4.263-1.346-9.174-2.244-13.439C238.137,266.029,236.718,261.378,234.155,257.063z"/>
+                       </g>
+               </g>
+               <radialGradient id="gradient_radial_dung"
+                               cx="0" cy="0" r="60"
+                               fx="0" fy="0" gradientUnits="userSpaceOnUse"
+               >
+                       <stop offset="0"    stop-color="#9a9a9a" />
+                       <stop offset="0.70" stop-color="#bababa" />
+                       <stop offset="0.95" stop-color="#FFFFFF" />
+               </radialGradient>
+               <g id="dung">
+                       <circle cx="0" cy="0" r="60" fill="url(#gradient_radial_dung)" />
+                       <g transform="translate(-61, -61)">
+                               <!-- rough equivalent: <circle cx="0" cy="0" r="60" stroke="#8a8a8a" stroke-width="2" /> -->
+                               <path fill="#8a8a8a" d="M0,61c0,33.636,27.364,61,61,61s61-27.364,61-61S94.636,0,61,0S0,27.364,0,61z
+                                                       M2,61C2,28.467,28.467,2,61,2c32.532,0,59,26.467,59,59c0,32.533-26.468,59-59,59C28.467,120,2,93.533,2,61z"/>
+                       </g>
+                       <use xlink:href="#hacker_emblem" x="0" y="0" transform="scale(9)" />
+               </g>
+               <g id="dung_2_color">
+                       <!-- This would be simple a circle like so:
+                               <circle cx="0" cy="0" r="48" stroke-width="2" fill="none" stroke="white"/>
+                            but there appears to currently be a bug in the cairo
+                            PDF backend that results in an ugly spike in that
+                            case. So we use 8 splines instead.
+                       -->
+                       <path stroke-width="4" stroke="white" fill="none" d="
+M 48,                  0
+C 48,                  12.730391512298112, 
+  42.94287166245995,   24.939379331448613
+  33.941125496954285,  33.941125496954278
+C 24.939379331448624,  42.942871662459943
+  12.730391512298114,  48
+  0,                   48
+C -12.730391512298109, 48, 
+  -24.939379331448613, 42.94287166245995
+  -33.941125496954278, 33.941125496954285
+C -42.942871662459943, 24.939379331448624,
+  -48,                 12.730391512298118,
+  -48,                 0,
+C -48,                 -12.730391512298105, 
+  -42.94287166245995,  -24.939379331448613,
+  -33.941125496954285, -33.941125496954278
+C -24.939379331448624, -42.942871662459943,
+  -12.730391512298119, -48, 
+  0,                   -48
+C 12.730391512298104,  -48, 
+  24.939379331448606,  -42.942871662459943,
+  33.941125496954271,  -33.941125496954285
+C 42.942871662459936,  -24.939379331448624,
+  48,                  -12.730391512298123,
+  48,                  0" />
+                       <g transform="scale(9)" fill="white" stroke="none"
+stroke-width="0.2">
+                               <!-- Hacker emblem grid -->
+                               <path stroke="white" fill="none"
+                                     d="M -3,-3 L  3,-3 L 3,3 L-3,3 Z
+                                        M -1,-3 L -1, 3
+                                        M  1,-3 L  1, 3
+                                        M -3,-1 L  3,-1
+                                        M -3, 1 L  3, 1"/>
+
+                               <!-- Hacker emblem dots -->
+                               <circle cx="0"  cy="-2" r="0.7" />
+                               <circle cx="2"  cy="0"  r="0.7" />
+                               <circle cx="-2" cy="2"  r="0.7" />
+                               <circle cx="0"  cy="2"  r="0.7" />
+                               <circle cx="2"  cy="2"  r="0.7" />
+                       </g>
+               </g>
+
+               <!-- scarab dimensions: 300x340 -->
+               <!-- dung dimensions: 120x120 (radius: 60) -->
+               <!-- scarab and dung dimensions: 300x400 -->
+
+               <g id="cairo_logo">
+                       <!-- dimensions: 300x400, centered -->
+                       <!-- The logo (scarab and dung), with the center-point of the bounding box at (0,0) -->
+                       <use xlink:href="#dung_2_color" x="0" y="0" transform="translate(0, -140)" />
+                       <use xlink:href="#scarab" x="0" y="0" transform="translate(0, 30)" />
+               </g>
+               <g id="cairo_logo_dung-centered">
+                       <!-- The logo (scarab and dung), with the dung at (0,0), the scarab below -->
+                       <use xlink:href="#dung_2_color" x="0" y="0" />
+                       <use xlink:href="#scarab" x="0" y="0" transform="translate(0,170)" />
+               </g>
+               <g id="cairo_logo_scarab-centered">
+                       <!-- The logo (scarab and dung), with the scarab's rotational center at (0,0), the dung above -->
+                       <!-- The scarab's rotational center in this case is not the center of its bounding box,
+                               but is calculated to be the intersection-point of the torso, spine and wings -->
+                       <use xlink:href="#dung_2_color" x="0" y="0" transform="translate(0, -175.85)" />
+                       <use xlink:href="#scarab" x="0" y="0" transform="translate(0, -5.85)" />
+               </g>
+               <g id="cairo_logo_top-centered">
+                       <!-- The logo (scarab and dung), with the top-center point of the bounding box at (0,0) -->
+                       <use xlink:href="#dung_2_color" x="0" y="0" transform="translate(0, 60)" />
+                       <use xlink:href="#scarab" x="0" y="0" transform="translate(0, 230)" /><!-- (0,170+60) -->
+               </g>
+               <g id="cairo_logo_bottom-centered">
+                       <!-- The logo (scarab and dung), with the bottom-center point of the bounding box at (0,0) -->
+                       <use xlink:href="#dung_2_color" x="0" y="0" transform="translate(0, -340)" />
+                       <use xlink:href="#scarab" x="0" y="0" transform="translate(0, -170)" />
+               </g>
+               <g id="cairo_logo_right-centered">
+                       <!-- The logo (scarab and dung), with the right-center point of the bounding box at (0,0) -->
+                       <use xlink:href="#dung_2_color" x="0" y="0" transform="translate(-150, -140)" />
+                       <use xlink:href="#scarab" x="0" y="0" transform="translate(-150, 30)" />
+               </g>
+               <g id="cairo_logo_left-centered">
+                       <!-- The logo (scarab and dung), with the left-center point of the bounding box at (0,0) -->
+                       <use xlink:href="#dung_2_color" x="0" y="0" transform="translate(150, -140)" />
+                       <use xlink:href="#scarab" x="0" y="0" transform="translate(150, 30)" />
+               </g>
+               <g id="cairo_logo_topleft-centered">
+                       <!-- The logo (scarab and dung), with the top-left point of the bounding box at (0,0) -->
+                       <use xlink:href="#dung_2_color" x="0" y="0" transform="translate(150, 60)" />
+                       <use xlink:href="#scarab" x="0" y="0" transform="translate(150, 230)" /><!-- (150, 170+60) -->
+               </g>
+               <g id="cairo_logo_topright-centered">
+                       <!-- The logo (scarab and dung), with the top-right point of the bounding box at (0,0) -->
+                       <use xlink:href="#dung_2_color" x="0" y="0" transform="translate(-150, 60)" />
+                       <use xlink:href="#scarab" x="0" y="0" transform="translate(-150, 230)" /><!-- (-150,170+60) -->
+               </g>
+               <g id="cairo_logo_bottomleft-centered">
+                       <!-- The logo (scarab and dung), with the bottom-left point of the bounding box at (0,0) -->
+                       <use xlink:href="#dung_2_color" x="0" y="0" transform="translate(150, -340)" />
+                       <use xlink:href="#scarab" x="0" y="0" transform="translate(150, -170)" />
+               </g>
+               <g id="cairo_logo_bottomright-centered">
+                       <!-- The logo (scarab and dung), with the bottom-right point of the bounding box at (0,0) -->
+                       <use xlink:href="#dung_2_color" x="0" y="0" transform="translate(-150, -340)" />
+                       <use xlink:href="#scarab" x="0" y="0" transform="translate(-150, -170)" />
+               </g>
+
+               <g id="cairo_text" transform="translate(0,-97)">
+                   <g transform="scale(0.1484,0.1484)"> <g transform="translate(-1139,-208.5)">
+                        <!-- 63 (c), advance 444, 0 horiBearing 38,522 -->
+                        <path transform="translate(65,0)" d="
+                              M 412, 433 
+                              C 385, 422 336, 413 298, 413 
+                              C 142, 413 38, 525 38, 680 
+                              C 38, 826 144, 947 298, 947 
+                              C 332, 947 377, 944 416, 926 
+                              L 409, 842 
+                              C 380, 861 340, 871 308, 871 
+                              C 187, 871 138, 771 138, 680 
+                              C 138, 583 197, 489 302, 489 
+                              C 332, 489 368, 496 404, 511 
+                              L 412, 433 " />
+                        <!-- 61 (a), advance 556, 0 horiBearing 46,522 -->
+                        <path transform="translate(486.75,0)" d="
+                              M 109, 541 
+                              C 147, 509 204, 489 257, 489 
+                              C 351, 489 383, 534 383, 622 
+                              C 346, 620 320, 620 283, 620 
+                              C 186, 620 46, 660 46, 788 
+                              C 46, 899 123, 947 233, 947 
+                              C 319, 947 369, 900 391, 869 
+                              L 393, 869 
+                              L 393, 935 
+                              L 481, 935 
+                              C 479, 920 477, 893 477, 835 
+                              L 477, 624 
+                              C 477, 485 418, 413 272, 413 
+                              C 207, 413 151, 433 104, 461 
+                              L 109, 541 
+                              M 383, 737 
+                              C 383, 813 334, 871 241, 871 
+                              C 198, 871 146, 842 146, 788 
+                              C 146, 698 272, 690 323, 690 
+                              C 343, 690 363, 692 383, 692 
+                              L 383, 737 " />
+                        <!-- 69 (i), advance 278, 0 horiBearing 86,730 -->
+                        <path transform="translate(1000,0)" d="
+                              M 92, 935 
+                              L 186, 935 
+                              L 186, 425 
+                              L 92, 425 
+                              L 92, 935 
+                              M 88, 261
+                              A 51, 51 0 1 1 190,261
+                              A 51, 51 0 1 1 88,261" />
+                        <!-- 72 (r), advance 389, 0 horiBearing 80,522 -->
+                        <path transform="translate(1234.25,0)" d="
+                              M 80, 935 
+                              L 174, 935 
+                              L 174, 703 
+                              C 174, 575 229, 495 313, 495 
+                              C 329, 495 348, 497 365, 504 
+                              L 365, 420 
+                              C 345, 416 331, 413 303, 413 
+                              C 249, 413 195, 451 170, 504 
+                              L 168, 504 
+                              L 168, 425 
+                              L 80, 425 
+                              L 80, 935 " />
+                        <!-- 6f (o), advance 611, 0 horiBearing 46,522 -->
+                        <path transform="translate(1610,0)" d="
+                              M 46, 680 
+                              C 46, 826 152, 947 306, 947 
+                              C 459, 947 565, 826 565, 680 
+                              C 565, 525 461, 413 306, 413 
+                              C 150, 413 46, 525 46, 680 
+                              M 146, 680 
+                              C 146, 583 205, 489 306, 489 
+                              C 406, 489 465, 583 465, 680 
+                              C 465, 771 416, 871 306, 871 
+                              C 195, 871 146, 771 146, 680 " />
+                        <!-- bounds: 38, 205 <-> 2232, 947 -->
+                   </g> </g>
+               </g>
+
+               <!-- scaled by 0.72, shifted around to hit pixel boundaries -->
+               <g id="cairo_text_small_spaced" transform="translate(0,-71)">
+                   <g transform="scale(0.085,0.085)"> <g transform="translate(-1139,-208.5)">
+                        <!-- 63 (c), advance 444, 0 horiBearing 38,522 -->
+                        <path transform="translate(-151,0)" d="
+                              M 412, 433 
+                              C 385, 422 336, 413 298, 413 
+                              C 142, 413 38, 525 38, 680 
+                              C 38, 826 144, 947 298, 947 
+                              C 332, 947 377, 944 416, 926 
+                              L 409, 842 
+                              C 380, 861 340, 871 308, 871 
+                              C 187, 871 138, 771 138, 680 
+                              C 138, 583 197, 489 302, 489 
+                              C 332, 489 368, 496 404, 511 
+                              L 412, 433 " />
+                        <!-- 61 (a), advance 556, 0 horiBearing 46,522 -->
+                        <path transform="translate(379.5,0)" d="
+                              M 109, 541 
+                              C 147, 509 204, 489 257, 489 
+                              C 351, 489 383, 534 383, 622 
+                              C 346, 620 320, 620 283, 620 
+                              C 186, 620 46, 660 46, 788 
+                              C 46, 899 123, 947 233, 947 
+                              C 319, 947 369, 900 391, 869 
+                              L 393, 869 
+                              L 393, 935 
+                              L 481, 935 
+                              C 479, 920 477, 893 477, 835 
+                              L 477, 624 
+                              C 477, 485 418, 413 272, 413 
+                              C 207, 413 151, 433 104, 461 
+                              L 109, 541 
+                              M 383, 737 
+                              C 383, 813 334, 871 241, 871 
+                              C 198, 871 146, 842 146, 788 
+                              C 146, 698 272, 690 323, 690 
+                              C 343, 690 363, 692 383, 692 
+                              L 383, 737 " />
+                        <!-- 69 (i), advance 278, 0 horiBearing 86,730 -->
+                        <path transform="translate(1000,0)" d="
+                              M 92, 935 
+                              L 186, 935 
+                              L 186, 425 
+                              L 92, 425 
+                              L 92, 935 
+                              M 88, 261
+                              A 51, 51 0 1 1 190,261
+                              A 51, 51 0 1 1 88,261" />
+                        <!-- 72 (r), advance 389, 0 horiBearing 80,522 -->
+                        <path transform="translate(1341.5,0)" d="
+                              M 80, 935 
+                              L 174, 935 
+                              L 174, 703 
+                              C 174, 575 229, 495 313, 495 
+                              C 329, 495 348, 497 365, 504 
+                              L 365, 420 
+                              C 345, 416 331, 413 303, 413 
+                              C 249, 413 195, 451 170, 504 
+                              L 168, 504 
+                              L 168, 425 
+                              L 80, 425 
+                              L 80, 935 " />
+                        <!-- 6f (o), advance 611, 0 horiBearing 46,522 -->
+                        <path transform="translate(1826,0)" d="
+                              M 46, 680 
+                              C 46, 826 152, 947 306, 947 
+                              C 459, 947 565, 826 565, 680 
+                              C 565, 525 461, 413 306, 413 
+                              C 150, 413 46, 525 46, 680 
+                              M 146, 680 
+                              C 146, 583 205, 489 306, 489 
+                              C 406, 489 465, 583 465, 680 
+                              C 465, 771 416, 871 306, 871 
+                              C 195, 871 146, 771 146, 680 " />
+                        <!-- bounds: 38, 205 <-> 2232, 947 -->
+                   </g> </g>
+               </g>
+
+
+               <!-- scaled by 0.72, shifted around to hit pixel boundaries -->
+               <g id="cairo_text_small" transform="translate(0,-71)">
+                   <g transform="scale(0.085,0.085)"> <g transform="translate(-1139,-208.5)">
+                        <!-- 63 (c), advance 444, 0 horiBearing 38,522 -->
+                        <path transform="translate(-151,0)" d="
+                              M 412, 433 
+                              C 385, 422 336, 413 298, 413 
+                              C 142, 413 38, 525 38, 680 
+                              C 38, 826 144, 947 298, 947 
+                              C 332, 947 377, 944 416, 926 
+                              L 409, 842 
+                              C 380, 861 340, 871 308, 871 
+                              C 187, 871 138, 771 138, 680 
+                              C 138, 583 197, 489 302, 489 
+                              C 332, 489 368, 496 404, 511 
+                              L 412, 433 " />
+                        <!-- 61 (a), advance 556, 0 horiBearing 46,522 -->
+                        <path transform="translate(261.75,0)" d="
+                              M 109, 541 
+                              C 147, 509 204, 489 257, 489 
+                              C 351, 489 383, 534 383, 622 
+                              C 346, 620 320, 620 283, 620 
+                              C 186, 620 46, 660 46, 788 
+                              C 46, 899 123, 947 233, 947 
+                              C 319, 947 369, 900 391, 869 
+                              L 393, 869 
+                              L 393, 935 
+                              L 481, 935 
+                              C 479, 920 477, 893 477, 835 
+                              L 477, 624 
+                              C 477, 485 418, 413 272, 413 
+                              C 207, 413 151, 433 104, 461 
+                              L 109, 541 
+                              M 383, 737 
+                              C 383, 813 334, 871 241, 871 
+                              C 198, 871 146, 842 146, 788 
+                              C 146, 698 272, 690 323, 690 
+                              C 343, 690 363, 692 383, 692 
+                              L 383, 737 " />
+                        <!-- 69 (i), advance 278, 0 horiBearing 86,730 -->
+                        <path transform="translate(764.75)" d="
+                              M 92, 935 
+                              L 186, 935 
+                              L 186, 425 
+                              L 92, 425 
+                              L 92, 935 
+                              M 88, 261
+                              A 51, 51 0 1 1 190,261
+                              A 51, 51 0 1 1 88,261" />
+                        <!-- 72 (r), advance 389, 0 horiBearing 80,522 -->
+                        <path transform="translate(988.5,0)" d="
+                              M 80, 935 
+                              L 174, 935 
+                              L 174, 703 
+                              C 174, 575 229, 495 313, 495 
+                              C 329, 495 348, 497 365, 504 
+                              L 365, 420 
+                              C 345, 416 331, 413 303, 413 
+                              C 249, 413 195, 451 170, 504 
+                              L 168, 504 
+                              L 168, 425 
+                              L 80, 425 
+                              L 80, 935 " />
+                        <!-- 6f (o), advance 611, 0 horiBearing 46,522 -->
+                        <path transform="translate(1355.5,0)" d="
+                              M 46, 680 
+                              C 46, 826 152, 947 306, 947 
+                              C 459, 947 565, 826 565, 680 
+                              C 565, 525 461, 413 306, 413 
+                              C 150, 413 46, 525 46, 680 
+                              M 146, 680 
+                              C 146, 583 205, 489 306, 489 
+                              C 406, 489 465, 583 465, 680 
+                              C 465, 771 416, 871 306, 871 
+                              C 195, 871 146, 771 146, 680 " />
+                        <!-- bounds: 38, 205 <-> 2232, 947 -->
+                   </g> </g>
+               </g>
+
+               <g id="cairo_logo_text_small">
+                       <!-- The logo on the left, the text 'cairo' on the right -->
+                       <use xlink:href="#cairo_logo_bottomleft-centered" transform="translate(0, 78), scale(0.1944)" />
+                       <use xlink:href="#cairo_text_small" fill="white" transform="translate(175,82)"/>
+               </g>
+
+               <g id="cairo_logo_with_text">
+                       <!-- The logo (scarab and dung), with the text 'cairo' below, the dot of the 'i' positioned between the hind legs of the scarab -->
+                       <!-- dimensions: 300x490, centered -->
+                       <use xlink:href="#cairo_logo_top-centered" transform="translate(0, -245)" />
+                       <use xlink:href="#cairo_text" transform="translate(0, 245)" />
+               </g>
+
+               <g id="cairo_banner">
+                       <!-- The logo on the left, the text 'cairo' in the center, and a mirror image of the logo on the right -->
+                       <!-- The logos are scaled such that the scarab body nearly matches the height of the text characters (excepting the 'i')
+                               and the dung should nearly aligns with the dot of the 'i'. The bottoms of the logos are aligned with the bottom of the text. -->
+                       <!-- dimensions: 370x88, centered -->
+                       <use xlink:href="#cairo_logo_bottomleft-centered" transform="translate(-180, 40), scale(0.1944)" />
+                       <use xlink:href="#cairo_text_small" transform="translate(0, 42)" fill="black" />
+                       <use xlink:href="#cairo_logo_bottomleft-centered" transform="translate(180, 40), scale(0.1944), scale(-1, 1)" />
+               </g>
+
+               <g id="freedesktop_org_logo" style="fill:#FFFFFF;stroke:#3B80AE;stroke-width:2.4588;">
+                 <g>
+                   <path style="stroke:#BABABA;" d="M85.277,40.796c2.058,7.884-2.667,15.942-10.551,17.999L27.143,71.21c-7.884,2.057-15.943-2.667-18-10.552
+                                                    l-7.448-28.55c-2.057-7.884,2.667-15.942,10.551-17.999L59.83,1.695c7.884-2.057,15.942,2.667,17.999,10.551
+                                                    l7.449,28.55z"/>>
+                   <path style="fill:#3B80AE;stroke:none;" d="M80.444,39.778c1.749,7.854-1.816,13.621-9.504,15.447l-42.236,11.02c-7.569,2.396-14.089-1.181
+                                                              -15.838-8.836L6.53,33.127c-1.749-8.145,0.709-12.889,9.503-15.447L58.27,6.661
+                                                              c8.144-1.826,14.089,1.363,15.838,8.835l6.336,24.282z"/>>
+                   </g>g>
+                   <path style="opacity:0.5;fill:none;stroke:#FFFFFF;" d="M45.542,51.793L24.104,31.102l38.1-4.393L45.542,51.793z"/>>
+                   <path d="M72.325,28.769c0.405,1.55-0.525,3.136-2.075,3.541l-12.331,3.217c-1.551,0.404-3.137-0.525-3.542-2.076l-2.295-8.801
+                            c-0.405-1.551,0.524-3.137,2.076-3.542l12.33-3.217c1.551-0.405,3.137,0.525,3.542,2.076l2.295,8.801z"/>>
+                   <path d="M36.51,33.625c0.496,1.9-0.645,3.844-2.545,4.34l-15.112,3.943c-1.901,0.496-3.845-0.644-4.34-2.544l-2.814-10.786
+                            c-0.496-1.901,0.644-3.844,2.544-4.34l15.113-3.942c1.901-0.496,3.845,0.643,4.34,2.544l2.814,10.786z"/>>
+                   <path d="M52.493,53.208c0.278,1.065-0.36,2.154-1.425,2.432L42.6,57.848c-1.064,0.277-2.153-0.36-2.431-1.426l-1.577-6.043
+                            c-0.277-1.064,0.36-2.153,1.425-2.432l8.468-2.209c1.064-0.277,2.154,0.361,2.431,1.426l1.577,6.043z"/>>
+                   </g>g>
+                   <g id="bullet">
+                     <use x="0" y="0" xlink:href="#cairo_logo" transform="translate(-6,-2) scale(0.1, 0.1)"/>>
+                   </g>
+                   <g id="redhat_logo_horizontal">
+                     <!-- 380x125 Red Hat log (horizontal layout) -->
+                      <g fill="black" stroke="none" 
+                       transform="translate(0,124),scale(1,-1),translate(-214,-258)"
+                       fill-rule="evenodd"
+                      >
+                       <!-- r -->
+                       <path fill="black" d="
+                        M 367.0625 315.3203
+                        C 367.0625 320.8765 366.9463 324.9644 366.7227 328.6597
+                        L 375.811 328.6597
+                        L 376.2002 320.7764
+                        L 376.4971 320.7764
+                        C 378.5391 326.6221 383.3809 329.5996 387.8594 329.5996
+                        C 388.8843 329.5996 389.4824 329.5601 390.3218 329.373
+                        L 390.3218 319.4863
+                        C 389.3398 319.6763 388.4224 319.7842 387.1592 319.7842
+                        C 382.1597 319.7842 378.688 316.6006 377.751 311.8447
+                        C 377.5732 310.918 377.4805 309.8086 377.4805 308.6777
+                        L 377.4805 287.1504
+                        L 366.9766 287.1504
+                        L 367.0625 315.3203
+                       " />
+
+                       <!-- e -->
+                       <path fill="black" d="
+                        M 402.9927 305.0791
+                        C 403.2715 297.5586 409.0918 294.2695 415.814 294.2695
+                        C 420.6406 294.2695 424.0942 295.0234 427.2681 296.1924
+                        L 428.8232 288.9678
+                        C 425.269 287.4629 420.3413 286.3359 414.3149 286.3359
+                        C 400.8384 286.3359 392.9409 294.6592 392.9409 307.3809
+                        C 392.9409 318.8369 399.8911 329.6772 413.2437 329.6772
+                        C 426.7397 329.6772 431.1338 318.5771 431.1338 309.4893
+                        C 431.1338 307.5381 430.9624 305.9707 430.7593 305.0059
+                        L 402.9927 305.0791
+
+                        M 421.2485 312.3926
+                        C 421.2954 316.2388 419.6206 322.5088 412.5903 322.5088
+                        C 406.1299 322.5088 403.4438 316.645 402.9722 312.3926
+                        L 421.2485 312.3926
+                       " />
+
+                       <!-- d -->
+                       <path fill="black" d="
+                        M 476.355 344.667
+                        L 465.8638 347.5083
+                        L 465.8638 324.1914
+                        L 465.6904 324.1914
+                        C 463.8335 327.2563 459.7407 329.5996 454.0571 329.5996
+                        C 444.0762 329.5996 435.3828 321.3374 435.4478 307.4307
+                        C 435.4478 294.6719 443.2983 286.2168 453.2119 286.2168
+                        C 459.2017 286.2168 464.2114 289.0723 466.6909 293.7217
+                        L 466.8779 293.7217
+                        L 467.3491 287.1504
+                        L 476.6997 287.1504
+                        C 476.5083 289.9717 476.355 294.543 476.355 298.792
+                        L 476.355 344.667
+
+                        M 465.8638 305.1504
+                        C 465.8638 304.0479 465.7856 303.0234 465.5454 302.0869
+                        C 464.4873 297.5439 460.7734 294.6172 456.4819 294.6172
+                        C 449.8721 294.6172 446.0903 300.1885 446.0903 307.8164
+                        C 446.0903 315.5166 449.8384 321.4761 456.6016 321.4761
+                        C 461.3208 321.4761 464.6992 318.1484 465.6274 314.1064
+                        C 465.8071 313.2559 465.8638 312.208 465.8638 311.3711
+                        L 465.8638 305.1504
+                       " />
+
+                       <!-- h -->
+                       <path fill="black" d="
+                        M 503.7964 329.0176
+                        C 500.6836 329.0176 497.8926 328.1187 495.5493 326.6714
+                        C 493.1162 325.2461 491.1353 323.0464 489.959 320.7666
+                        L 489.7915 320.7666
+                        L 489.7915 341.0195
+                        L 485.7427 342.1226
+                        L 485.7427 287.1504
+                        L 489.7915 287.1504
+                        L 489.7915 312.1787
+                        C 489.7915 313.8408 489.9204 314.9946 490.3462 316.2109
+                        C 492.0928 321.3013 496.8896 325.4805 502.689 325.4805
+                        C 511.0664 325.4805 513.9673 318.7603 513.9673 311.3906
+                        L 513.9673 287.1504
+                        L 518.0137 287.1504
+                        L 518.0137 311.8359
+                        C 518.0137 327.0791 507.6753 329.0176 503.7964 329.0176
+                       " />
+
+                       <!-- a -->
+                       <path fill="black" d="
+                        M 554.3413 296.873
+                        C 554.3413 293.6357 554.4692 290.2832 554.9375 287.1504
+                        L 551.2085 287.1504
+                        L 550.6128 293.0156
+                        L 550.4209 293.0156
+                        C 548.438 289.8594 543.8765 286.2168 537.3726 286.2168
+                        C 529.1392 286.2168 525.3057 292.0117 525.3057 297.4688
+                        C 525.3057 306.9121 533.6421 312.6064 550.292 312.4321
+                        L 550.292 313.5234
+                        C 550.292 317.5718 549.5044 325.6475 539.8242 325.5859
+                        C 536.2446 325.5859 532.5132 324.6255 529.5513 322.5366
+                        L 528.2632 325.4805
+                        C 532.0015 328.0137 536.5659 329.0176 540.2705 329.0176
+                        C 552.0801 329.0176 554.3413 320.1509 554.3413 312.8379
+                        L 554.3413 296.873
+
+                        M 550.292 309.0234
+                        C 541.3813 309.2813 529.6128 307.9336 529.6128 298.1055
+                        C 529.6128 292.2246 533.4946 289.5811 537.7578 289.5811
+                        C 544.5796 289.5811 548.4561 293.8018 549.8677 297.7871
+                        C 550.1646 298.6621 550.292 299.5371 550.292 300.2402
+                        L 550.292 309.0234
+                       " />
+
+                       <!-- t -->
+                       <path fill="black" d="
+                        M 570.459 337.0996
+                        L 570.459 328.0801
+                        L 582.1235 328.0801
+                        L 582.1235 324.7959
+                        L 570.459 324.7959
+                        L 570.459 298.1943
+                        C 570.459 292.9912 572.0757 289.7285 576.4692 289.7285
+                        C 578.5815 289.7285 580.0757 290.0078 581.1206 290.3711
+                        L 581.6099 287.2354
+                        C 580.2871 286.6836 578.4302 286.2539 575.9619 286.2539
+                        C 572.9741 286.2539 570.4995 287.1934 568.8994 289.1543
+                        C 567.0469 291.3057 566.4116 294.7412 566.4116 298.916
+                        L 566.4116 324.7959
+                        L 559.5059 324.7959
+                        L 559.5059 328.0801
+                        L 566.4116 328.0801
+                        L 566.4116 335.606
+                        L 570.459 337.0996
+                       " />
+
+                       <!-- ® for 'redhat' -->
+                       <path fill="black" d="
+                        M 335.5 288.9707
+                        L 336.0352 288.9707
+                        L 336.8408 287.6445
+                        L 337.3608 287.6445
+                        L 336.4888 288.9937
+                        C 336.9404 289.0498 337.2832 289.2881 337.2832 289.834
+                        C 337.2832 290.4385 336.9258 290.7051 336.2017 290.7051
+                        L 335.0366 290.7051
+                        L 335.0366 287.6445
+                        L 335.5 287.6445
+                        L 335.5 288.9707
+
+                        M 335.5 289.3643
+                        L 335.5 290.3101
+                        L 336.1318 290.3101
+                        C 336.4531 290.3101 336.7979 290.2402 336.7979 289.8647
+                        C 336.7979 289.3916 336.4492 289.3643 336.0566 289.3643
+                        L 335.5 289.3643
+                       " />
+
+                       <path fill="black" d="
+                        M 339.0439 289.1719
+                        C 339.0439 287.5176 337.7041 286.1763 336.0493 286.1763
+                        C 334.395 286.1763 333.0527 287.5176 333.0527 289.1719
+                        C 333.0527 290.8271 334.395 292.1675 336.0493 292.1675
+                        C 337.7041 292.1675 339.0439 290.8271 339.0439 289.1719
+
+                        M 336.0493 291.6367
+                        C 334.6865 291.6367 333.5835 290.5332 333.5835 289.1719
+                        C 333.5835 287.8096 334.6865 286.7061 336.0493 286.7061
+                        C 337.4082 286.7061 338.5117 287.8096 338.5117 289.1719
+                        C 338.5117 290.5332 337.4082 291.6367 336.0493 291.6367
+                       " />
+
+                       <!-- Black background behind The Shadowman -->
+                       <path fill="black" d="
+                        M 326.4531 286.208
+                        C 324.1177 286.7451 321.6396 287.0801 319.1338 287.0801
+                        C 314.8496 287.0801 310.9502 286.3389 308.0732 285.1426
+                        C 307.7559 284.9844 307.5303 284.6533 307.5303 284.2764
+                        C 307.5303 284.1406 307.5654 283.999 307.6172 283.8838
+                        C 307.957 282.8975 307.3984 281.8281 304.6157 281.2158
+                        C 300.4883 280.3096 297.8833 276.0527 296.3916 274.6367
+                        C 294.6411 272.9756 289.6973 271.9531 290.4404 272.9434
+                        C 291.0225 273.7188 293.2485 276.1348 294.6016 278.748
+                        C 295.8125 281.083 296.8906 281.7461 298.375 283.9736
+                        C 298.811 284.627 300.4995 286.9219 300.9912 288.7373
+                        C 301.543 290.5107 301.356 292.7344 301.5679 293.6494
+                        C 301.8721 294.9697 303.1182 297.8369 303.2129 299.4531
+                        C 303.2666 300.3691 299.3916 298.1494 297.5532 298.1494
+                        C 295.7144 298.1494 293.9233 299.248 292.2808 299.3281
+                        C 290.248 299.4248 288.9414 297.7607 287.1025 298.0508
+                        C 286.0518 298.2178 285.167 299.1426 283.3311 299.2129
+                        C 280.7178 299.3086 277.5244 297.7607 271.5264 297.9531
+                        C 265.6255 298.1436 260.1753 305.4082 259.4312 306.5635
+                        C 258.5605 307.9199 257.4961 307.9199 256.335 306.8555
+                        C 255.1738 305.792 253.7432 306.627 253.3359 307.3389
+                        C 252.5615 308.6943 250.4927 312.6543 247.2881 313.4824
+                        C 242.8564 314.6309 240.6118 311.0283 240.9033 308.1621
+                        C 241.1987 305.252 243.0801 304.4375 243.9512 302.8906
+                        C 244.8213 301.3428 245.2671 300.3428 246.9053 299.6572
+                        C 248.0674 299.1758 248.5 298.458 248.1533 297.5049
+                        C 247.8506 296.6738 246.6416 296.4834 245.8477 296.4463
+                        C 244.1592 296.3662 242.9756 296.8242 242.1123 297.376
+                        C 241.1084 298.0137 240.292 298.9033 239.416 300.4131
+                        C 238.4023 302.0781 236.8052 302.8037 234.9453 302.8037
+                        C 234.0586 302.8037 233.2295 302.5693 232.4922 302.1895
+                        C 229.5771 300.6748 226.1064 299.7744 222.3706 299.7744
+                        L 218.1572 299.7734
+                        C 216.1064 305.8555 214.9951 312.3682 214.9951 319.1416
+                        C 214.9951 352.6064 242.1226 379.7334 275.5859 379.7334
+                        C 309.0498 379.7334 336.1758 352.6064 336.1758 319.1416
+                        C 336.1758 307 332.6035 295.6895 326.4531 286.208
+                       " />
+
+                       <!-- The Shadowman's face -->
+                       <path fill="white" d="
+                        M 326.4531 286.209
+                        C 324.1177 286.7461 321.6396 287.084 319.1338 287.084
+                        C 314.8496 287.084 310.9502 286.3418 308.0732 285.1436
+                        C 307.7559 284.9873 307.5303 284.6553 307.5303 284.2783
+                        C 307.5303 284.1416 307.5654 284.001 307.6172 283.8838
+                        C 307.957 282.8994 307.3984 281.8311 304.6157 281.2178
+                        C 300.4883 280.3115 297.8833 276.0537 296.3916 274.6416
+                        C 294.6411 272.9766 289.6973 271.9551 290.4404 272.9463
+                        C 291.0225 273.7197 293.2485 276.1367 294.6016 278.749
+                        C 295.8125 281.083 296.8906 281.75 298.375 283.9756
+                        C 298.811 284.627 300.4995 286.9238 300.9912 288.7402
+                        C 301.543 290.5117 301.356 292.7354 301.5679 293.6514
+                        C 301.8721 294.9727 303.1182 297.8379 303.2129 299.457
+                        C 303.2666 300.3721 299.3916 298.1504 297.5532 298.1504
+                        C 295.7144 298.1504 293.9233 299.251 292.2808 299.3301
+                        C 290.248 299.4258 288.9414 297.7627 287.1025 298.0518
+                        C 286.0518 298.2207 285.167 299.1465 283.3311 299.2148
+                        C 280.7178 299.3096 277.5244 297.7627 271.5264 297.9561
+                        C 265.6255 298.1475 260.1753 305.4121 259.4312 306.5674
+                        C 258.5605 307.9219 257.4961 307.9219 256.335 306.8574
+                        C 255.1738 305.7939 253.7432 306.6299 253.3359 307.3438
+                        C 252.5615 308.6963 250.4927 312.6553 247.2881 313.4854
+                        C 242.8564 314.6338 240.6118 311.0313 240.9033 308.1641
+                        C 241.1987 305.2539 243.0801 304.4395 243.9512 302.8926
+                        C 244.8213 301.3438 245.2671 300.3457 246.9053 299.6621
+                        C 248.0674 299.1768 248.5 298.4609 248.1533 297.5068
+                        C 247.8506 296.6768 246.6416 296.4873 245.8477 296.4492
+                        C 244.1592 296.3672 242.9756 296.8262 242.1123 297.376
+                        C 241.1084 298.0176 240.292 298.9043 239.416 300.416
+                        C 238.4023 302.0801 236.8052 302.8086 234.9453 302.8086
+                        C 234.0586 302.8086 233.2295 302.5723 232.4922 302.1934
+                        C 229.5771 300.6748 226.1064 299.7773 222.3706 299.7773
+                        L 218.1572 299.7744
+                        C 226.2363 275.8105 248.8965 258.5527 275.5859 258.5527
+                        C 296.9063 258.5527 315.6538 269.5635 326.4531 286.209
+                       " />
+
+                       <!-- nose shadow -->
+                       <path fill="black" d="
+                        M 288.9307 291.7637
+                        C 289.2422 291.46 289.7793 290.4375 289.1226 289.1396
+                        C 288.7544 288.4521 288.3579 287.9678 287.6489 287.4023
+                        C 286.7969 286.7188 285.1309 285.9307 282.8457 287.3799
+                        C 281.6172 288.1592 281.543 288.4209 279.8467 288.2012
+                        C 278.6348 288.043 278.1533 289.2656 278.5884 290.2832
+                        C 279.0244 291.2969 280.8145 292.1191 283.04 290.8135
+                        C 284.041 290.2256 285.6025 288.9844 286.9688 290.084
+                        C 287.5356 290.5381 287.875 290.8408 288.6611 291.75
+                        C 288.6963 291.7881 288.7461 291.8105 288.8018 291.8105
+                        C 288.8516 291.8105 288.8965 291.793 288.9307 291.7637
+                       " />
+
+                       <!-- The Shadowman's red hat -->
+                       <path fill="#cc0000" d="
+                        M 309.7769 335.2627
+                        C 309.1787 333.251 308.3271 330.6763 304.5391 328.7314
+                        C 303.9878 328.4497 303.7764 328.9126 304.0313 329.3477
+                        C 305.4629 331.7832 305.7168 332.3921 306.1328 333.3525
+                        C 306.7148 334.7568 307.02 336.7549 305.8618 340.9219
+                        C 303.5835 349.1221 298.8296 360.083 295.375 363.6392
+                        C 292.04 367.0698 285.998 368.0361 280.5371 366.6348
+                        C 278.5264 366.1191 274.5918 364.0732 267.2939 365.7168
+                        C 254.665 368.5605 252.7939 362.2368 252.0693 359.4824
+                        C 251.3438 356.7271 249.6045 348.897 249.6045 348.897
+                        C 249.0244 345.7085 248.2646 340.1631 267.874 336.4287
+                        C 277.0088 334.6885 277.4736 332.3276 277.8779 330.6289
+                        C 278.603 327.585 279.7627 325.8438 281.0674 324.9746
+                        C 282.373 324.1035 281.0674 323.3828 279.6187 323.2349
+                        C 275.7285 322.8311 261.3491 326.9541 252.8428 331.7881
+                        C 245.8828 336.0415 245.7656 339.8721 247.3584 343.1211
+                        C 236.8452 344.2573 228.9561 342.1348 227.5254 337.1582
+                        C 225.0693 328.6157 246.3047 314.0264 270.4839 306.7061
+                        C 295.8579 299.0225 321.9556 304.3857 324.8564 320.335
+                        C 326.1738 327.5811 320.0713 332.9419 309.7769 335.2627
+                       " />
+
+                       <!-- shadow on hat -->
+                       <path fill="black" d="
+                        M 270.8711 350.8813
+                        C 263.8721 350.375 263.145 349.6191 261.834 348.2227
+                        C 259.9854 346.2539 257.5508 350.7773 257.5508 350.7773
+                        C 256.0898 351.085 254.3179 353.4404 255.2744 355.6411
+                        C 256.2158 357.8174 257.9551 357.1641 258.5 356.4868
+                        C 259.1626 355.6621 260.5771 354.3125 262.4141 354.3613
+                        C 264.251 354.4097 266.3706 354.7959 269.3262 354.7959
+                        C 272.3213 354.7959 274.335 353.6777 274.4487 352.7168
+                        C 274.5459 351.8965 274.2061 351.1226 270.8711 350.8813
+                       " />
+
+                       <!-- another shadow on hat -->
+                       <path fill="black" d="
+                        M 278.2236 362.4463
+                        C 278.2129 362.4453 278.2021 362.4438 278.1919 362.4438
+                        C 278.084 362.4438 277.9961 362.5273 277.9961 362.6274
+                        C 277.9961 362.7007 278.041 362.7646 278.106 362.7939
+                        C 279.4629 363.5107 281.4873 364.0811 283.8042 364.3169
+                        C 284.499 364.3887 285.1787 364.4248 285.832 364.4307
+                        C 285.9478 364.4307 286.0615 364.4297 286.1787 364.4277
+                        C 290.062 364.3398 293.1719 362.7974 293.1255 360.9814
+                        C 293.0791 359.165 289.8955 357.7637 286.0112 357.8506
+                        C 284.7529 357.8794 283.5732 358.0615 282.5576 358.3545
+                        C 282.4385 358.3857 282.3506 358.4883 282.3506 358.6094
+                        C 282.3506 358.731 282.4385 358.834 282.5605 358.8638
+                        C 284.9839 359.4248 286.6191 360.3408 286.5039 361.207
+                        C 286.3511 362.3545 283.1816 362.979 279.4248 362.6011
+                        C 279.0137 362.5596 278.6118 362.5068 278.2236 362.4463
+                       " />
+
+                       <!-- ® for The Shadowman -->
+                       <path fill="black" d="
+                        M 588.3018 288.9707
+                        L 588.8369 288.9707
+                        L 589.6426 287.6445
+                        L 590.1626 287.6445
+                        L 589.2905 288.9937
+                        C 589.7422 289.0498 590.085 289.2881 590.085 289.834
+                        C 590.085 290.4385 589.7275 290.7051 589.0034 290.7051
+                        L 587.8384 290.7051
+                        L 587.8384 287.6445
+                        L 588.3018 287.6445
+                        L 588.3018 288.9707
+
+                        M 588.3018 289.3643
+                        L 588.3018 290.3101
+                        L 588.9336 290.3101
+                        C 589.2549 290.3101 589.5996 290.2402 589.5996 289.8647
+                        C 589.5996 289.3916 589.251 289.3643 588.8584 289.3643
+                        L 588.3018 289.3643
+                       " />
+
+                       <path fill="black" d="
+                        M 591.8457 289.1719
+                        C 591.8457 287.5176 590.5059 286.1763 588.8511 286.1763
+                        C 587.1968 286.1763 585.8545 287.5176 585.8545 289.1719
+                        C 585.8545 290.8271 587.1968 292.1675 588.8511 292.1675
+                        C 590.5059 292.1675 591.8457 290.8271 591.8457 289.1719
+
+                        M 588.8511 291.6367
+                        C 587.4883 291.6367 586.3853 290.5332 586.3853 289.1719
+                        C 586.3853 287.8096 587.4883 286.7061 588.8511 286.7061
+                        C 590.21 286.7061 591.3135 287.8096 591.3135 289.1719
+                        C 591.3135 290.5332 590.21 291.6367 588.8511 291.6367
+                       " />
+
+                     </g>
+                   </g>
+
+       </defs>
+
+<g fill="black">
+
+  <g font-family="Frutiger">
+
+    <rect x="0" y="0" width="341" height="768" fill="#162284"/>
+    
+    <use xlink:href="#cairo_logo_with_text" fill="white" transform="translate(170, 420)" />
+
+    <use xlink:href="#redhat_logo_horizontal" 
+     transform="translate(826,768),scale(.5,.5),translate(0,-125)"/>
+    
+    <g id="slide_title" transform="translate(683, 80)">
+      <text text-anchor="middle"
+           fill="black"
+           font-weight="bold"
+           x="0"
+           y="10" font-size="50"
+           ss:variable="presentation">Presentation Title</text>
+    </g>
+  
+    <g ss:region="default" text-anchor="middle">
+      <rect x="683" y="270" width="2" height="350" fill="none" stroke="blue"/>
+      <text font-size="40" fill="black"
+           x="683" y="310">Slide content</text>
+    </g>
+  </g>
+
+</g>
+
+</svg>
diff --git a/doc/tutorial/slides/cairo.svg b/doc/tutorial/slides/cairo.svg
new file mode 100644 (file)
index 0000000..3d96abc
--- /dev/null
@@ -0,0 +1,898 @@
+<?xml version="1.0" ?>
+<svg width="1024" height="768"
+    xmlns="http://www.w3.org/2000/svg"
+    xmlns:xlink="http://www.w3.org/1999/xlink"
+    xmlns:ss="http://www.svgslides.org/svgslides0.1"
+    fill="black">
+
+
+       <defs id="cairo-artwork_defs">
+               <g id="hacker_emblem">
+                       <!-- Note: This is similar though not identical to Keith Packard's SVG version
+                               of the hacker emblem (http://www.catb.org/hacker-emblem/glider.svg) -->
+                       <g id="hacker_emblem_grid" fill="white" stroke="none">
+                               <!-- Outside: Top, Right, Bottom, Left -->
+                               <rect x="-2.95" y="-3.05" width="6"   height="0.1" />
+                               <rect x="2.95"  y="-2.95" width="0.1" height="6" />
+                               <rect x="-3.05" y="2.95"  width="6"   height="0.1" />
+                               <rect x="-3.05" y="-3.05" width="0.1" height="6" />
+                               <!-- Vertical: Left, Right -->
+                               <rect x="-1.05" y="-2.95" width="0.1" height="5.9" />
+                               <rect x="0.95"  y="-2.95" width="0.1" height="5.9" />
+                               <!-- Horizontal: TopLeft, TopMiddle, TopRight -->
+                               <rect x="-2.95" y="-1.05" width="1.9" height="0.1" />
+                               <rect x="-0.95" y="-1.05" width="1.9" height="0.1" />
+                               <rect x="1.05"  y="-1.05" width="1.9" height="0.1" />
+                               <!-- Horizontal: BottomLeft, BottomMiddle, BottomRight -->
+                               <rect x="-2.95" y="0.95"  width="1.9" height="0.1" />
+                               <rect x="-0.95" y="0.95"  width="1.9" height="0.1" />
+                               <rect x="1.05"  y="0.95"  width="1.9" height="0.1" />
+                       </g>
+                       <g id="hacker_emblem_dots" fill="white">
+                               <circle cx="0"  cy="-2" r="0.7" />
+                               <circle cx="2"  cy="0"  r="0.7" />
+                               <circle cx="-2" cy="2"  r="0.7" />
+                               <circle cx="0"  cy="2"  r="0.7" />
+                               <circle cx="2"  cy="2"  r="0.7" />
+                       </g>
+               </g>
+               <g id="scarab" fill="#f19a14" stroke="none">
+                       <g transform="translate(-150, -170)">
+                       <path id="scarab_head" d="M205.599,94.567c0-11.668-24.914-21.129-55.628-21.129
+                               c-30.723,0-55.624,9.46-55.624,21.129c0,10.203,24.901,7.346,55.624,7.346C180.685,101.913,205.599,104.233,205.599,94.567z"/>
+                       <path id="scarab_torso" d="M136.423,161.506c0,0,12.751,12.577,13.547,13.362
+                               c2.262-2.232,13.545-13.362,13.545-13.362c7.135-7.036,87.111-6.399,91.066-6.363c-0.469-6.298-1.254-12.472-2.325-18.519
+                               c-15.183-19.279-42.811-32.225-74.485-32.225h-55.518c-31.745,0-59.439,13.011-74.598,32.37c-1.054,6-1.829,12.128-2.296,18.374
+                               C49.321,155.106,129.288,154.47,136.423,161.506z"/>
+                       <path id="scarab_spine" d="M149.97,301.187c2.005-24.729,8.386-103.483,8.405-103.721
+                               c-0.09-0.219-6.478-15.578-8.405-20.214c-1.936,4.655-8.316,19.995-8.408,20.214C141.582,197.704,147.965,276.458,149.97,301.187z"/>
+                       <path id="scarab_wing_left" d="M140.403,197.149l8.862-21.31l-13.686-13.499
+                               c-5.65-5.573-67.074-6.235-90.259-6.019l-0.006-0.622c-0.154,2.144-0.271,4.302-0.35,6.475
+                               c-0.076,2.207,10.392,4.706,10.392,6.717c0,2.319-10.457,5.084-10.359,7.631c2.993,73.349,48.53,131.631,104.372,132.048
+                               l-9.02-111.29L140.403,197.149z"/>
+                       <path id="scarab_wing_right" d="M244.585,168.891c0-2.011,10.467-4.506,10.391-6.715
+                               c-0.079-2.174-0.195-4.332-0.351-6.479l-0.004,0.624c-23.186-0.216-84.608,0.445-90.26,6.017l-13.688,13.502l8.915,21.438
+                               l-9.017,111.29c55.854-0.417,101.378-58.698,104.373-132.049C255.04,173.976,244.585,171.209,244.585,168.891z"/>
+                       <path id="scarab_leg_front_left" d="M44.506,141.12c-4.135-0.856-4.895-1.54-7.935-2.92
+                               c-9.59-3.364-10.376-5.481-16.08-11.86c-7.426-8.306-12.661-20.142-17.1-29.463c-3.576-7.525-3.984-16.409-2.86-24.273
+                               c0.991-6.935,7.144-12.869,12.074-18.92c5.844-7.191,10.356-14.822,17.924-21.354c7.736-6.682,23.203-9.809,26.168-19.648
+                               C57.86,8.819,54.334,1.766,61.482,0c-0.366,4.703,3.639,8.477,2.397,13.575c-1.129,4.627-4.368,5.811-9.611,9.099
+                               c-7.564,4.746-18.366,8.779-24.748,13.965c-7.175,5.827-4.369,13.771-10.569,20.057c-2.001,2.03-7.901,4.706-9.137,6.83
+                               c-1.861,3.199-0.297,9.572-0.116,13.12c0.425,8.284,5.588,14.244,9.555,22.045c4.152,8.141,6.429,15.409,13.411,22.519
+                               c4.183,4.262,11.429,4.802,16.21,10.647l-3.555,4.186L44.506,141.12z"/>
+                       <path id="scarab_leg_middle_left" d="M43.94,191.922l-0.809-7.346
+                               c-9.506-4.579-10.339-9.772-20.738-12.466c-23.728-6.151-21.361,11.25-15.532,26.373c5.676,14.726,8.237,30.23,14.345,44.795
+                               c2.805,6.688,6.919,13.213,14.298,15.127c0.372-8.435-0.917-10.651-6.113-16.919c-4.395-5.293-3.326-12.548-6.072-18.504
+                               c-3.581-7.804-4.196-15.646-7.279-23.502c-1.363-3.479-8.33-13.966-6.452-17.861c3.183-6.603,9.178-0.083,12.179,2.077
+                               c4.218,3.036,6.467,2.223,11.681,2.898C34.041,186.673,37.005,188.756,43.94,191.922z"/>
+                       <path id="scarab_leg_back_left" d="M65.839,257.063l-2.771-4.837
+                               c-6.68,8.928-6.993,16.228-10.056,23.347c-5.277,12.263-0.157,28.851,9.854,37.676c6.052,5.375,15.907,9.618,23.122,13.136
+                               c10.035,4.892,20.113,11.286,31.336,13.396c2.482,0.466,8.798,1.295,6.693-3.522c-0.975-2.237-8.091-4.591-10.146-5.734
+                               c-8.312-4.623-16.377-10.524-24.142-16.176c-9.498-6.862-20.843-11.186-28.311-20.684c-3.054-3.885-3.544-4.922-2.816-9.39
+                               c0.693-4.263,1.344-9.174,2.241-13.439C61.855,266.029,63.274,261.378,65.839,257.063z"/>
+                       <path id="scarab_leg_front_right" d="M255.487,141.12c4.134-0.856,4.896-1.54,7.936-2.92
+                               c9.583-3.364,10.369-5.481,16.071-11.86c7.428-8.306,12.661-20.142,17.115-29.463c3.574-7.525,3.983-16.409,2.86-24.273
+                               c-0.992-6.935-7.157-12.869-12.087-18.92c-5.843-7.191-10.356-14.822-17.919-21.354c-7.735-6.682-23.202-9.809-26.167-19.648
+                               C242.135,8.819,245.66,1.766,238.511,0c0.366,4.703-3.637,8.477-2.396,13.575c1.131,4.627,4.368,5.811,9.611,9.099
+                               c7.563,4.746,18.367,8.779,24.747,13.965c7.17,5.827,4.362,13.771,10.563,20.057c2.001,2.03,7.901,4.706,9.139,6.83
+                               c1.859,3.199,0.295,9.572,0.113,13.12c-0.424,8.284-5.588,14.244-9.553,22.045c-4.152,8.141-6.431,15.409-13.404,22.519
+                               c-4.184,4.262-11.429,4.802-16.211,10.647l3.556,4.186L255.487,141.12z"/>
+                       <path id="scarab_leg_middle_right" d="M256.053,191.922l0.81-7.346
+                               c9.507-4.579,10.34-9.772,20.73-12.466c23.741-6.151,21.374,11.25,15.534,26.373c-5.676,14.726-8.238,30.23-14.347,44.795
+                               c-2.804,6.688-6.911,13.213-14.291,15.127c-0.371-8.435,0.918-10.651,6.113-16.919c4.39-5.293,3.319-12.548,6.066-18.504
+                               c3.58-7.804,4.197-15.646,7.278-23.502c1.363-3.479,8.33-13.966,6.453-17.861c-3.184-6.603-9.179-0.083-12.181,2.077
+                               c-4.217,3.036-6.458,2.223-11.672,2.898C265.951,186.673,262.986,188.756,256.053,191.922z"/>
+                       <path id="scarab_leg_back_right" d="M234.155,257.063l2.771-4.837
+                               c6.679,8.928,6.991,16.228,10.057,23.347c5.274,12.263,0.154,28.851-9.854,37.676c-6.055,5.375-15.903,9.618-23.117,13.136
+                               c-10.034,4.892-20.127,11.286-31.351,13.396c-2.481,0.466-8.789,1.295-6.691-3.522c0.976-2.237,8.092-4.591,10.146-5.734
+                               c8.312-4.623,16.392-10.524,24.155-16.176c9.498-6.862,20.838-11.186,28.305-20.684c3.055-3.885,3.543-4.922,2.818-9.39
+                               c-0.696-4.263-1.346-9.174-2.244-13.439C238.137,266.029,236.718,261.378,234.155,257.063z"/>
+                       </g>
+               </g>
+               <radialGradient id="gradient_radial_dung"
+                               cx="0" cy="0" r="60"
+                               fx="0" fy="0" gradientUnits="userSpaceOnUse"
+               >
+                       <stop offset="0"    stop-color="#9a9a9a" />
+                       <stop offset="0.70" stop-color="#bababa" />
+                       <stop offset="0.95" stop-color="#FFFFFF" />
+               </radialGradient>
+               <g id="dung">
+                       <circle cx="0" cy="0" r="60" fill="url(#gradient_radial_dung)" />
+                       <g transform="translate(-61, -61)">
+                               <!-- rough equivalent: <circle cx="0" cy="0" r="60" stroke="#8a8a8a" stroke-width="2" /> -->
+                               <path fill="#8a8a8a" d="M0,61c0,33.636,27.364,61,61,61s61-27.364,61-61S94.636,0,61,0S0,27.364,0,61z
+                                                       M2,61C2,28.467,28.467,2,61,2c32.532,0,59,26.467,59,59c0,32.533-26.468,59-59,59C28.467,120,2,93.533,2,61z"/>
+                       </g>
+                       <use xlink:href="#hacker_emblem" x="0" y="0" transform="scale(9)" />
+               </g>
+               <g id="dung_2_color">
+                       <path stroke="none" d="
+M -46,                 0
+A 46,                  46,
+  0,
+  0, 1,
+  46,                  0
+A 46,                  46,
+  0,
+  0, 1,
+  -46,                 0
+M -50,                 0
+A 50,                  50,
+  0,
+  0, 0,
+  50,                  0
+A 50,                  50,
+  0,
+  0, 0,
+  -50,                 0
+Z" />
+                       <g transform="scale(9)">
+                               <!-- Hacker emblem grid -->
+                               <path fill="none" stroke-width="0.2"
+                                     d="M -3,-3 L  3,-3 L 3,3 L-3,3 Z
+                                        M -1,-3 L -1, 3
+                                        M  1,-3 L  1, 3
+                                        M -3,-1 L  3,-1
+                                        M -3, 1 L  3, 1"/>
+                               <g stroke="none">
+                                       <!-- Hacker emblem dots -->
+                                       <circle cx="0"  cy="-2" r="0.7" />
+                                       <circle cx="2"  cy="0"  r="0.7" />
+                                       <circle cx="-2" cy="2"  r="0.7" />
+                                       <circle cx="0"  cy="2"  r="0.7" />
+                                       <circle cx="2"  cy="2"  r="0.7" />
+                               </g>
+                       </g>
+               </g>
+
+               <!-- scarab dimensions: 300x340 -->
+               <!-- dung dimensions: 120x120 (radius: 60) -->
+               <!-- scarab and dung dimensions: 300x400 -->
+
+               <g id="cairo_logo">
+                       <!-- dimensions: 300x400, centered -->
+                       <!-- The logo (scarab and dung), with the center-point of the bounding box at (0,0) -->
+                       <use xlink:href="#dung_2_color" x="0" y="0" transform="translate(0, -140)" />
+                       <use xlink:href="#scarab" x="0" y="0" transform="translate(0, 30)" />
+               </g>
+               <g id="cairo_logo_dung-centered">
+                       <!-- The logo (scarab and dung), with the dung at (0,0), the scarab below -->
+                       <use xlink:href="#dung_2_color" x="0" y="0" />
+                       <use xlink:href="#scarab" x="0" y="0" transform="translate(0,170)" />
+               </g>
+               <g id="cairo_logo_scarab-centered">
+                       <!-- The logo (scarab and dung), with the scarab's rotational center at (0,0), the dung above -->
+                       <!-- The scarab's rotational center in this case is not the center of its bounding box,
+                               but is calculated to be the intersection-point of the torso, spine and wings -->
+                       <use xlink:href="#dung_2_color" x="0" y="0" transform="translate(0, -175.85)" />
+                       <use xlink:href="#scarab" x="0" y="0" transform="translate(0, -5.85)" />
+               </g>
+               <g id="cairo_logo_top-centered">
+                       <!-- The logo (scarab and dung), with the top-center point of the bounding box at (0,0) -->
+                       <use xlink:href="#dung_2_color" x="0" y="0" transform="translate(0, 60)" />
+                       <use xlink:href="#scarab" x="0" y="0" transform="translate(0, 230)" /><!-- (0,170+60) -->
+               </g>
+               <g id="cairo_logo_bottom-centered">
+                       <!-- The logo (scarab and dung), with the bottom-center point of the bounding box at (0,0) -->
+                       <use xlink:href="#dung_2_color" x="0" y="0" transform="translate(0, -340)" />
+                       <use xlink:href="#scarab" x="0" y="0" transform="translate(0, -170)" />
+               </g>
+               <g id="cairo_logo_right-centered">
+                       <!-- The logo (scarab and dung), with the right-center point of the bounding box at (0,0) -->
+                       <use xlink:href="#dung_2_color" x="0" y="0" transform="translate(-150, -140)" />
+                       <use xlink:href="#scarab" x="0" y="0" transform="translate(-150, 30)" />
+               </g>
+               <g id="cairo_logo_left-centered">
+                       <!-- The logo (scarab and dung), with the left-center point of the bounding box at (0,0) -->
+                       <use xlink:href="#dung_2_color" x="0" y="0" transform="translate(150, -140)" />
+                       <use xlink:href="#scarab" x="0" y="0" transform="translate(150, 30)" />
+               </g>
+               <g id="cairo_logo_topleft-centered">
+                       <!-- The logo (scarab and dung), with the top-left point of the bounding box at (0,0) -->
+                       <use xlink:href="#dung_2_color" x="0" y="0" transform="translate(150, 60)" />
+                       <use xlink:href="#scarab" x="0" y="0" transform="translate(150, 230)" /><!-- (150, 170+60) -->
+               </g>
+               <g id="cairo_logo_topright-centered">
+                       <!-- The logo (scarab and dung), with the top-right point of the bounding box at (0,0) -->
+                       <use xlink:href="#dung_2_color" x="0" y="0" transform="translate(-150, 60)" />
+                       <use xlink:href="#scarab" x="0" y="0" transform="translate(-150, 230)" /><!-- (-150,170+60) -->
+               </g>
+               <g id="cairo_logo_bottomleft-centered">
+                       <!-- The logo (scarab and dung), with the bottom-left point of the bounding box at (0,0) -->
+                       <use xlink:href="#dung_2_color" x="0" y="0" transform="translate(150, -340)" />
+                       <use xlink:href="#scarab" x="0" y="0" transform="translate(150, -170)" />
+               </g>
+               <g id="cairo_logo_bottomright-centered">
+                       <!-- The logo (scarab and dung), with the bottom-right point of the bounding box at (0,0) -->
+                       <use xlink:href="#dung_2_color" x="0" y="0" transform="translate(-150, -340)" />
+                       <use xlink:href="#scarab" x="0" y="0" transform="translate(-150, -170)" />
+               </g>
+
+               <g id="cairo_text" transform="translate(0,-97)">
+                   <g transform="scale(0.1484,0.1484)"> <g transform="translate(-1139,-208.5)">
+                        <!-- 63 (c), advance 444, 0 horiBearing 38,522 -->
+                        <path transform="translate(65,0)" d="
+                              M 412, 433 
+                              C 385, 422 336, 413 298, 413 
+                              C 142, 413 38, 525 38, 680 
+                              C 38, 826 144, 947 298, 947 
+                              C 332, 947 377, 944 416, 926 
+                              L 409, 842 
+                              C 380, 861 340, 871 308, 871 
+                              C 187, 871 138, 771 138, 680 
+                              C 138, 583 197, 489 302, 489 
+                              C 332, 489 368, 496 404, 511 
+                              L 412, 433 " />
+                        <!-- 61 (a), advance 556, 0 horiBearing 46,522 -->
+                        <path transform="translate(486.75,0)" d="
+                              M 109, 541 
+                              C 147, 509 204, 489 257, 489 
+                              C 351, 489 383, 534 383, 622 
+                              C 346, 620 320, 620 283, 620 
+                              C 186, 620 46, 660 46, 788 
+                              C 46, 899 123, 947 233, 947 
+                              C 319, 947 369, 900 391, 869 
+                              L 393, 869 
+                              L 393, 935 
+                              L 481, 935 
+                              C 479, 920 477, 893 477, 835 
+                              L 477, 624 
+                              C 477, 485 418, 413 272, 413 
+                              C 207, 413 151, 433 104, 461 
+                              L 109, 541 
+                              M 383, 737 
+                              C 383, 813 334, 871 241, 871 
+                              C 198, 871 146, 842 146, 788 
+                              C 146, 698 272, 690 323, 690 
+                              C 343, 690 363, 692 383, 692 
+                              L 383, 737 " />
+                        <!-- 69 (i), advance 278, 0 horiBearing 86,730 -->
+                        <path transform="translate(1000,0)" d="
+                              M 92, 935 
+                              L 186, 935 
+                              L 186, 425 
+                              L 92, 425 
+                              L 92, 935 
+                              M 88, 261
+                              A 51, 51 0 1 1 190,261
+                              A 51, 51 0 1 1 88,261" />
+                        <!-- 72 (r), advance 389, 0 horiBearing 80,522 -->
+                        <path transform="translate(1234.25,0)" d="
+                              M 80, 935 
+                              L 174, 935 
+                              L 174, 703 
+                              C 174, 575 229, 495 313, 495 
+                              C 329, 495 348, 497 365, 504 
+                              L 365, 420 
+                              C 345, 416 331, 413 303, 413 
+                              C 249, 413 195, 451 170, 504 
+                              L 168, 504 
+                              L 168, 425 
+                              L 80, 425 
+                              L 80, 935 " />
+                        <!-- 6f (o), advance 611, 0 horiBearing 46,522 -->
+                        <path transform="translate(1610,0)" d="
+                              M 46, 680 
+                              C 46, 826 152, 947 306, 947 
+                              C 459, 947 565, 826 565, 680 
+                              C 565, 525 461, 413 306, 413 
+                              C 150, 413 46, 525 46, 680 
+                              M 146, 680 
+                              C 146, 583 205, 489 306, 489 
+                              C 406, 489 465, 583 465, 680 
+                              C 465, 771 416, 871 306, 871 
+                              C 195, 871 146, 771 146, 680 " />
+                        <!-- bounds: 38, 205 <-> 2232, 947 -->
+                   </g> </g>
+               </g>
+
+               <!-- scaled by 0.72, shifted around to hit pixel boundaries -->
+               <g id="cairo_text_small_spaced" transform="translate(0,-71)">
+                   <g transform="scale(0.085,0.085)"> <g transform="translate(-1139,-208.5)">
+                        <!-- 63 (c), advance 444, 0 horiBearing 38,522 -->
+                        <path transform="translate(-151,0)" d="
+                              M 412, 433 
+                              C 385, 422 336, 413 298, 413 
+                              C 142, 413 38, 525 38, 680 
+                              C 38, 826 144, 947 298, 947 
+                              C 332, 947 377, 944 416, 926 
+                              L 409, 842 
+                              C 380, 861 340, 871 308, 871 
+                              C 187, 871 138, 771 138, 680 
+                              C 138, 583 197, 489 302, 489 
+                              C 332, 489 368, 496 404, 511 
+                              L 412, 433 " />
+                        <!-- 61 (a), advance 556, 0 horiBearing 46,522 -->
+                        <path transform="translate(379.5,0)" d="
+                              M 109, 541 
+                              C 147, 509 204, 489 257, 489 
+                              C 351, 489 383, 534 383, 622 
+                              C 346, 620 320, 620 283, 620 
+                              C 186, 620 46, 660 46, 788 
+                              C 46, 899 123, 947 233, 947 
+                              C 319, 947 369, 900 391, 869 
+                              L 393, 869 
+                              L 393, 935 
+                              L 481, 935 
+                              C 479, 920 477, 893 477, 835 
+                              L 477, 624 
+                              C 477, 485 418, 413 272, 413 
+                              C 207, 413 151, 433 104, 461 
+                              L 109, 541 
+                              M 383, 737 
+                              C 383, 813 334, 871 241, 871 
+                              C 198, 871 146, 842 146, 788 
+                              C 146, 698 272, 690 323, 690 
+                              C 343, 690 363, 692 383, 692 
+                              L 383, 737 " />
+                        <!-- 69 (i), advance 278, 0 horiBearing 86,730 -->
+                        <path transform="translate(1000,0)" d="
+                              M 92, 935 
+                              L 186, 935 
+                              L 186, 425 
+                              L 92, 425 
+                              L 92, 935 
+                              M 88, 261
+                              A 51, 51 0 1 1 190,261
+                              A 51, 51 0 1 1 88,261" />
+                        <!-- 72 (r), advance 389, 0 horiBearing 80,522 -->
+                        <path transform="translate(1341.5,0)" d="
+                              M 80, 935 
+                              L 174, 935 
+                              L 174, 703 
+                              C 174, 575 229, 495 313, 495 
+                              C 329, 495 348, 497 365, 504 
+                              L 365, 420 
+                              C 345, 416 331, 413 303, 413 
+                              C 249, 413 195, 451 170, 504 
+                              L 168, 504 
+                              L 168, 425 
+                              L 80, 425 
+                              L 80, 935 " />
+                        <!-- 6f (o), advance 611, 0 horiBearing 46,522 -->
+                        <path transform="translate(1826,0)" d="
+                              M 46, 680 
+                              C 46, 826 152, 947 306, 947 
+                              C 459, 947 565, 826 565, 680 
+                              C 565, 525 461, 413 306, 413 
+                              C 150, 413 46, 525 46, 680 
+                              M 146, 680 
+                              C 146, 583 205, 489 306, 489 
+                              C 406, 489 465, 583 465, 680 
+                              C 465, 771 416, 871 306, 871 
+                              C 195, 871 146, 771 146, 680 " />
+                        <!-- bounds: 38, 205 <-> 2232, 947 -->
+                   </g> </g>
+               </g>
+
+
+               <!-- scaled by 0.72, shifted around to hit pixel boundaries -->
+               <g id="cairo_text_small" transform="translate(0,-71)">
+                   <g transform="scale(0.085,0.085)"> <g transform="translate(-1139,-208.5)">
+                        <!-- 63 (c), advance 444, 0 horiBearing 38,522 -->
+                        <path transform="translate(-151,0)" d="
+                              M 412, 433 
+                              C 385, 422 336, 413 298, 413 
+                              C 142, 413 38, 525 38, 680 
+                              C 38, 826 144, 947 298, 947 
+                              C 332, 947 377, 944 416, 926 
+                              L 409, 842 
+                              C 380, 861 340, 871 308, 871 
+                              C 187, 871 138, 771 138, 680 
+                              C 138, 583 197, 489 302, 489 
+                              C 332, 489 368, 496 404, 511 
+                              L 412, 433 " />
+                        <!-- 61 (a), advance 556, 0 horiBearing 46,522 -->
+                        <path transform="translate(261.75,0)" d="
+                              M 109, 541 
+                              C 147, 509 204, 489 257, 489 
+                              C 351, 489 383, 534 383, 622 
+                              C 346, 620 320, 620 283, 620 
+                              C 186, 620 46, 660 46, 788 
+                              C 46, 899 123, 947 233, 947 
+                              C 319, 947 369, 900 391, 869 
+                              L 393, 869 
+                              L 393, 935 
+                              L 481, 935 
+                              C 479, 920 477, 893 477, 835 
+                              L 477, 624 
+                              C 477, 485 418, 413 272, 413 
+                              C 207, 413 151, 433 104, 461 
+                              L 109, 541 
+                              M 383, 737 
+                              C 383, 813 334, 871 241, 871 
+                              C 198, 871 146, 842 146, 788 
+                              C 146, 698 272, 690 323, 690 
+                              C 343, 690 363, 692 383, 692 
+                              L 383, 737 " />
+                        <!-- 69 (i), advance 278, 0 horiBearing 86,730 -->
+                        <path transform="translate(764.75)" d="
+                              M 92, 935 
+                              L 186, 935 
+                              L 186, 425 
+                              L 92, 425 
+                              L 92, 935 
+                              M 88, 261
+                              A 51, 51 0 1 1 190,261
+                              A 51, 51 0 1 1 88,261" />
+                        <!-- 72 (r), advance 389, 0 horiBearing 80,522 -->
+                        <path transform="translate(988.5,0)" d="
+                              M 80, 935 
+                              L 174, 935 
+                              L 174, 703 
+                              C 174, 575 229, 495 313, 495 
+                              C 329, 495 348, 497 365, 504 
+                              L 365, 420 
+                              C 345, 416 331, 413 303, 413 
+                              C 249, 413 195, 451 170, 504 
+                              L 168, 504 
+                              L 168, 425 
+                              L 80, 425 
+                              L 80, 935 " />
+                        <!-- 6f (o), advance 611, 0 horiBearing 46,522 -->
+                        <path transform="translate(1355.5,0)" d="
+                              M 46, 680 
+                              C 46, 826 152, 947 306, 947 
+                              C 459, 947 565, 826 565, 680 
+                              C 565, 525 461, 413 306, 413 
+                              C 150, 413 46, 525 46, 680 
+                              M 146, 680 
+                              C 146, 583 205, 489 306, 489 
+                              C 406, 489 465, 583 465, 680 
+                              C 465, 771 416, 871 306, 871 
+                              C 195, 871 146, 771 146, 680 " />
+                        <!-- bounds: 38, 205 <-> 2232, 947 -->
+                   </g> </g>
+               </g>
+
+               <g id="cairo_logo_text_small">
+                       <!-- The logo on the left, the text 'cairo' on the right -->
+                       <use xlink:href="#cairo_logo_bottomleft-centered" transform="translate(0, 78), scale(0.1944)" />
+                       <use xlink:href="#cairo_text_small" fill="white" transform="translate(175,82)"/>
+               </g>
+
+               <g id="cairo_logo_with_text">
+                       <!-- The logo (scarab and dung), with the text 'cairo' below, the dot of the 'i' positioned between the hind legs of the scarab -->
+                       <!-- dimensions: 300x490, centered -->
+                       <use xlink:href="#cairo_logo_top-centered" transform="translate(0, -245)" />
+                       <use xlink:href="#cairo_text" transform="translate(0, 245)" />
+               </g>
+
+               <g id="cairo_banner">
+                       <!-- The logo on the left, the text 'cairo' in the center, and a mirror image of the logo on the right -->
+                       <!-- The logos are scaled such that the scarab body nearly matches the height of the text characters (excepting the 'i')
+                               and the dung should nearly aligns with the dot of the 'i'. The bottoms of the logos are aligned with the bottom of the text. -->
+                       <!-- dimensions: 370x88, centered -->
+                       <use xlink:href="#cairo_logo_bottomleft-centered" transform="translate(-180, 40), scale(0.1944)" />
+                       <use xlink:href="#cairo_text_small" transform="translate(0, 42)" fill="black" />
+                       <use xlink:href="#cairo_logo_bottomleft-centered" transform="translate(180, 40), scale(0.1944), scale(-1, 1)" />
+               </g>
+
+               <g id="freedesktop_org_logo" style="fill:#FFFFFF;stroke:#3B80AE;stroke-width:2.4588;">
+                 <g>
+                   <path style="stroke:#BABABA;" d="M85.277,40.796c2.058,7.884-2.667,15.942-10.551,17.999L27.143,71.21c-7.884,2.057-15.943-2.667-18-10.552
+                                                    l-7.448-28.55c-2.057-7.884,2.667-15.942,10.551-17.999L59.83,1.695c7.884-2.057,15.942,2.667,17.999,10.551
+                                                    l7.449,28.55z"/>>
+                   <path style="fill:#3B80AE;stroke:none;" d="M80.444,39.778c1.749,7.854-1.816,13.621-9.504,15.447l-42.236,11.02c-7.569,2.396-14.089-1.181
+                                                              -15.838-8.836L6.53,33.127c-1.749-8.145,0.709-12.889,9.503-15.447L58.27,6.661
+                                                              c8.144-1.826,14.089,1.363,15.838,8.835l6.336,24.282z"/>>
+                   </g>g>
+                   <path style="opacity:0.5;fill:none;stroke:#FFFFFF;" d="M45.542,51.793L24.104,31.102l38.1-4.393L45.542,51.793z"/>>
+                   <path d="M72.325,28.769c0.405,1.55-0.525,3.136-2.075,3.541l-12.331,3.217c-1.551,0.404-3.137-0.525-3.542-2.076l-2.295-8.801
+                            c-0.405-1.551,0.524-3.137,2.076-3.542l12.33-3.217c1.551-0.405,3.137,0.525,3.542,2.076l2.295,8.801z"/>>
+                   <path d="M36.51,33.625c0.496,1.9-0.645,3.844-2.545,4.34l-15.112,3.943c-1.901,0.496-3.845-0.644-4.34-2.544l-2.814-10.786
+                            c-0.496-1.901,0.644-3.844,2.544-4.34l15.113-3.942c1.901-0.496,3.845,0.643,4.34,2.544l2.814,10.786z"/>>
+                   <path d="M52.493,53.208c0.278,1.065-0.36,2.154-1.425,2.432L42.6,57.848c-1.064,0.277-2.153-0.36-2.431-1.426l-1.577-6.043
+                            c-0.277-1.064,0.36-2.153,1.425-2.432l8.468-2.209c1.064-0.277,2.154,0.361,2.431,1.426l1.577,6.043z"/>>
+                   </g>g>
+                   <g id="bullet">
+                     <use x="0" y="0" xlink:href="#cairo_logo" transform="translate(-6,-2) scale(0.1, 0.1)"/>>
+                   </g>
+                   <g id="redhat_logo_horizontal">
+                     <!-- 380x125 Red Hat log (horizontal layout) -->
+                      <g fill="black" stroke="none" 
+                       transform="translate(0,124),scale(1,-1),translate(-214,-258)"
+                       fill-rule="evenodd"
+                      >
+                       <!-- r -->
+                       <path fill="black" d="
+                        M 367.0625 315.3203
+                        C 367.0625 320.8765 366.9463 324.9644 366.7227 328.6597
+                        L 375.811 328.6597
+                        L 376.2002 320.7764
+                        L 376.4971 320.7764
+                        C 378.5391 326.6221 383.3809 329.5996 387.8594 329.5996
+                        C 388.8843 329.5996 389.4824 329.5601 390.3218 329.373
+                        L 390.3218 319.4863
+                        C 389.3398 319.6763 388.4224 319.7842 387.1592 319.7842
+                        C 382.1597 319.7842 378.688 316.6006 377.751 311.8447
+                        C 377.5732 310.918 377.4805 309.8086 377.4805 308.6777
+                        L 377.4805 287.1504
+                        L 366.9766 287.1504
+                        L 367.0625 315.3203
+                       " />
+
+                       <!-- e -->
+                       <path fill="black" d="
+                        M 402.9927 305.0791
+                        C 403.2715 297.5586 409.0918 294.2695 415.814 294.2695
+                        C 420.6406 294.2695 424.0942 295.0234 427.2681 296.1924
+                        L 428.8232 288.9678
+                        C 425.269 287.4629 420.3413 286.3359 414.3149 286.3359
+                        C 400.8384 286.3359 392.9409 294.6592 392.9409 307.3809
+                        C 392.9409 318.8369 399.8911 329.6772 413.2437 329.6772
+                        C 426.7397 329.6772 431.1338 318.5771 431.1338 309.4893
+                        C 431.1338 307.5381 430.9624 305.9707 430.7593 305.0059
+                        L 402.9927 305.0791
+
+                        M 421.2485 312.3926
+                        C 421.2954 316.2388 419.6206 322.5088 412.5903 322.5088
+                        C 406.1299 322.5088 403.4438 316.645 402.9722 312.3926
+                        L 421.2485 312.3926
+                       " />
+
+                       <!-- d -->
+                       <path fill="black" d="
+                        M 476.355 344.667
+                        L 465.8638 347.5083
+                        L 465.8638 324.1914
+                        L 465.6904 324.1914
+                        C 463.8335 327.2563 459.7407 329.5996 454.0571 329.5996
+                        C 444.0762 329.5996 435.3828 321.3374 435.4478 307.4307
+                        C 435.4478 294.6719 443.2983 286.2168 453.2119 286.2168
+                        C 459.2017 286.2168 464.2114 289.0723 466.6909 293.7217
+                        L 466.8779 293.7217
+                        L 467.3491 287.1504
+                        L 476.6997 287.1504
+                        C 476.5083 289.9717 476.355 294.543 476.355 298.792
+                        L 476.355 344.667
+
+                        M 465.8638 305.1504
+                        C 465.8638 304.0479 465.7856 303.0234 465.5454 302.0869
+                        C 464.4873 297.5439 460.7734 294.6172 456.4819 294.6172
+                        C 449.8721 294.6172 446.0903 300.1885 446.0903 307.8164
+                        C 446.0903 315.5166 449.8384 321.4761 456.6016 321.4761
+                        C 461.3208 321.4761 464.6992 318.1484 465.6274 314.1064
+                        C 465.8071 313.2559 465.8638 312.208 465.8638 311.3711
+                        L 465.8638 305.1504
+                       " />
+
+                       <!-- h -->
+                       <path fill="black" d="
+                        M 503.7964 329.0176
+                        C 500.6836 329.0176 497.8926 328.1187 495.5493 326.6714
+                        C 493.1162 325.2461 491.1353 323.0464 489.959 320.7666
+                        L 489.7915 320.7666
+                        L 489.7915 341.0195
+                        L 485.7427 342.1226
+                        L 485.7427 287.1504
+                        L 489.7915 287.1504
+                        L 489.7915 312.1787
+                        C 489.7915 313.8408 489.9204 314.9946 490.3462 316.2109
+                        C 492.0928 321.3013 496.8896 325.4805 502.689 325.4805
+                        C 511.0664 325.4805 513.9673 318.7603 513.9673 311.3906
+                        L 513.9673 287.1504
+                        L 518.0137 287.1504
+                        L 518.0137 311.8359
+                        C 518.0137 327.0791 507.6753 329.0176 503.7964 329.0176
+                       " />
+
+                       <!-- a -->
+                       <path fill="black" d="
+                        M 554.3413 296.873
+                        C 554.3413 293.6357 554.4692 290.2832 554.9375 287.1504
+                        L 551.2085 287.1504
+                        L 550.6128 293.0156
+                        L 550.4209 293.0156
+                        C 548.438 289.8594 543.8765 286.2168 537.3726 286.2168
+                        C 529.1392 286.2168 525.3057 292.0117 525.3057 297.4688
+                        C 525.3057 306.9121 533.6421 312.6064 550.292 312.4321
+                        L 550.292 313.5234
+                        C 550.292 317.5718 549.5044 325.6475 539.8242 325.5859
+                        C 536.2446 325.5859 532.5132 324.6255 529.5513 322.5366
+                        L 528.2632 325.4805
+                        C 532.0015 328.0137 536.5659 329.0176 540.2705 329.0176
+                        C 552.0801 329.0176 554.3413 320.1509 554.3413 312.8379
+                        L 554.3413 296.873
+
+                        M 550.292 309.0234
+                        C 541.3813 309.2813 529.6128 307.9336 529.6128 298.1055
+                        C 529.6128 292.2246 533.4946 289.5811 537.7578 289.5811
+                        C 544.5796 289.5811 548.4561 293.8018 549.8677 297.7871
+                        C 550.1646 298.6621 550.292 299.5371 550.292 300.2402
+                        L 550.292 309.0234
+                       " />
+
+                       <!-- t -->
+                       <path fill="black" d="
+                        M 570.459 337.0996
+                        L 570.459 328.0801
+                        L 582.1235 328.0801
+                        L 582.1235 324.7959
+                        L 570.459 324.7959
+                        L 570.459 298.1943
+                        C 570.459 292.9912 572.0757 289.7285 576.4692 289.7285
+                        C 578.5815 289.7285 580.0757 290.0078 581.1206 290.3711
+                        L 581.6099 287.2354
+                        C 580.2871 286.6836 578.4302 286.2539 575.9619 286.2539
+                        C 572.9741 286.2539 570.4995 287.1934 568.8994 289.1543
+                        C 567.0469 291.3057 566.4116 294.7412 566.4116 298.916
+                        L 566.4116 324.7959
+                        L 559.5059 324.7959
+                        L 559.5059 328.0801
+                        L 566.4116 328.0801
+                        L 566.4116 335.606
+                        L 570.459 337.0996
+                       " />
+
+                       <!-- ® for 'redhat' -->
+                       <path fill="black" d="
+                        M 335.5 288.9707
+                        L 336.0352 288.9707
+                        L 336.8408 287.6445
+                        L 337.3608 287.6445
+                        L 336.4888 288.9937
+                        C 336.9404 289.0498 337.2832 289.2881 337.2832 289.834
+                        C 337.2832 290.4385 336.9258 290.7051 336.2017 290.7051
+                        L 335.0366 290.7051
+                        L 335.0366 287.6445
+                        L 335.5 287.6445
+                        L 335.5 288.9707
+
+                        M 335.5 289.3643
+                        L 335.5 290.3101
+                        L 336.1318 290.3101
+                        C 336.4531 290.3101 336.7979 290.2402 336.7979 289.8647
+                        C 336.7979 289.3916 336.4492 289.3643 336.0566 289.3643
+                        L 335.5 289.3643
+                       " />
+
+                       <path fill="black" d="
+                        M 339.0439 289.1719
+                        C 339.0439 287.5176 337.7041 286.1763 336.0493 286.1763
+                        C 334.395 286.1763 333.0527 287.5176 333.0527 289.1719
+                        C 333.0527 290.8271 334.395 292.1675 336.0493 292.1675
+                        C 337.7041 292.1675 339.0439 290.8271 339.0439 289.1719
+
+                        M 336.0493 291.6367
+                        C 334.6865 291.6367 333.5835 290.5332 333.5835 289.1719
+                        C 333.5835 287.8096 334.6865 286.7061 336.0493 286.7061
+                        C 337.4082 286.7061 338.5117 287.8096 338.5117 289.1719
+                        C 338.5117 290.5332 337.4082 291.6367 336.0493 291.6367
+                       " />
+
+                       <!-- Black background behind The Shadowman -->
+                       <path fill="black" d="
+                        M 326.4531 286.208
+                        C 324.1177 286.7451 321.6396 287.0801 319.1338 287.0801
+                        C 314.8496 287.0801 310.9502 286.3389 308.0732 285.1426
+                        C 307.7559 284.9844 307.5303 284.6533 307.5303 284.2764
+                        C 307.5303 284.1406 307.5654 283.999 307.6172 283.8838
+                        C 307.957 282.8975 307.3984 281.8281 304.6157 281.2158
+                        C 300.4883 280.3096 297.8833 276.0527 296.3916 274.6367
+                        C 294.6411 272.9756 289.6973 271.9531 290.4404 272.9434
+                        C 291.0225 273.7188 293.2485 276.1348 294.6016 278.748
+                        C 295.8125 281.083 296.8906 281.7461 298.375 283.9736
+                        C 298.811 284.627 300.4995 286.9219 300.9912 288.7373
+                        C 301.543 290.5107 301.356 292.7344 301.5679 293.6494
+                        C 301.8721 294.9697 303.1182 297.8369 303.2129 299.4531
+                        C 303.2666 300.3691 299.3916 298.1494 297.5532 298.1494
+                        C 295.7144 298.1494 293.9233 299.248 292.2808 299.3281
+                        C 290.248 299.4248 288.9414 297.7607 287.1025 298.0508
+                        C 286.0518 298.2178 285.167 299.1426 283.3311 299.2129
+                        C 280.7178 299.3086 277.5244 297.7607 271.5264 297.9531
+                        C 265.6255 298.1436 260.1753 305.4082 259.4312 306.5635
+                        C 258.5605 307.9199 257.4961 307.9199 256.335 306.8555
+                        C 255.1738 305.792 253.7432 306.627 253.3359 307.3389
+                        C 252.5615 308.6943 250.4927 312.6543 247.2881 313.4824
+                        C 242.8564 314.6309 240.6118 311.0283 240.9033 308.1621
+                        C 241.1987 305.252 243.0801 304.4375 243.9512 302.8906
+                        C 244.8213 301.3428 245.2671 300.3428 246.9053 299.6572
+                        C 248.0674 299.1758 248.5 298.458 248.1533 297.5049
+                        C 247.8506 296.6738 246.6416 296.4834 245.8477 296.4463
+                        C 244.1592 296.3662 242.9756 296.8242 242.1123 297.376
+                        C 241.1084 298.0137 240.292 298.9033 239.416 300.4131
+                        C 238.4023 302.0781 236.8052 302.8037 234.9453 302.8037
+                        C 234.0586 302.8037 233.2295 302.5693 232.4922 302.1895
+                        C 229.5771 300.6748 226.1064 299.7744 222.3706 299.7744
+                        L 218.1572 299.7734
+                        C 216.1064 305.8555 214.9951 312.3682 214.9951 319.1416
+                        C 214.9951 352.6064 242.1226 379.7334 275.5859 379.7334
+                        C 309.0498 379.7334 336.1758 352.6064 336.1758 319.1416
+                        C 336.1758 307 332.6035 295.6895 326.4531 286.208
+                       " />
+
+                       <!-- The Shadowman's face -->
+                       <path fill="white" d="
+                        M 326.4531 286.209
+                        C 324.1177 286.7461 321.6396 287.084 319.1338 287.084
+                        C 314.8496 287.084 310.9502 286.3418 308.0732 285.1436
+                        C 307.7559 284.9873 307.5303 284.6553 307.5303 284.2783
+                        C 307.5303 284.1416 307.5654 284.001 307.6172 283.8838
+                        C 307.957 282.8994 307.3984 281.8311 304.6157 281.2178
+                        C 300.4883 280.3115 297.8833 276.0537 296.3916 274.6416
+                        C 294.6411 272.9766 289.6973 271.9551 290.4404 272.9463
+                        C 291.0225 273.7197 293.2485 276.1367 294.6016 278.749
+                        C 295.8125 281.083 296.8906 281.75 298.375 283.9756
+                        C 298.811 284.627 300.4995 286.9238 300.9912 288.7402
+                        C 301.543 290.5117 301.356 292.7354 301.5679 293.6514
+                        C 301.8721 294.9727 303.1182 297.8379 303.2129 299.457
+                        C 303.2666 300.3721 299.3916 298.1504 297.5532 298.1504
+                        C 295.7144 298.1504 293.9233 299.251 292.2808 299.3301
+                        C 290.248 299.4258 288.9414 297.7627 287.1025 298.0518
+                        C 286.0518 298.2207 285.167 299.1465 283.3311 299.2148
+                        C 280.7178 299.3096 277.5244 297.7627 271.5264 297.9561
+                        C 265.6255 298.1475 260.1753 305.4121 259.4312 306.5674
+                        C 258.5605 307.9219 257.4961 307.9219 256.335 306.8574
+                        C 255.1738 305.7939 253.7432 306.6299 253.3359 307.3438
+                        C 252.5615 308.6963 250.4927 312.6553 247.2881 313.4854
+                        C 242.8564 314.6338 240.6118 311.0313 240.9033 308.1641
+                        C 241.1987 305.2539 243.0801 304.4395 243.9512 302.8926
+                        C 244.8213 301.3438 245.2671 300.3457 246.9053 299.6621
+                        C 248.0674 299.1768 248.5 298.4609 248.1533 297.5068
+                        C 247.8506 296.6768 246.6416 296.4873 245.8477 296.4492
+                        C 244.1592 296.3672 242.9756 296.8262 242.1123 297.376
+                        C 241.1084 298.0176 240.292 298.9043 239.416 300.416
+                        C 238.4023 302.0801 236.8052 302.8086 234.9453 302.8086
+                        C 234.0586 302.8086 233.2295 302.5723 232.4922 302.1934
+                        C 229.5771 300.6748 226.1064 299.7773 222.3706 299.7773
+                        L 218.1572 299.7744
+                        C 226.2363 275.8105 248.8965 258.5527 275.5859 258.5527
+                        C 296.9063 258.5527 315.6538 269.5635 326.4531 286.209
+                       " />
+
+                       <!-- nose shadow -->
+                       <path fill="black" d="
+                        M 288.9307 291.7637
+                        C 289.2422 291.46 289.7793 290.4375 289.1226 289.1396
+                        C 288.7544 288.4521 288.3579 287.9678 287.6489 287.4023
+                        C 286.7969 286.7188 285.1309 285.9307 282.8457 287.3799
+                        C 281.6172 288.1592 281.543 288.4209 279.8467 288.2012
+                        C 278.6348 288.043 278.1533 289.2656 278.5884 290.2832
+                        C 279.0244 291.2969 280.8145 292.1191 283.04 290.8135
+                        C 284.041 290.2256 285.6025 288.9844 286.9688 290.084
+                        C 287.5356 290.5381 287.875 290.8408 288.6611 291.75
+                        C 288.6963 291.7881 288.7461 291.8105 288.8018 291.8105
+                        C 288.8516 291.8105 288.8965 291.793 288.9307 291.7637
+                       " />
+
+                       <!-- The Shadowman's red hat -->
+                       <path fill="#cc0000" d="
+                        M 309.7769 335.2627
+                        C 309.1787 333.251 308.3271 330.6763 304.5391 328.7314
+                        C 303.9878 328.4497 303.7764 328.9126 304.0313 329.3477
+                        C 305.4629 331.7832 305.7168 332.3921 306.1328 333.3525
+                        C 306.7148 334.7568 307.02 336.7549 305.8618 340.9219
+                        C 303.5835 349.1221 298.8296 360.083 295.375 363.6392
+                        C 292.04 367.0698 285.998 368.0361 280.5371 366.6348
+                        C 278.5264 366.1191 274.5918 364.0732 267.2939 365.7168
+                        C 254.665 368.5605 252.7939 362.2368 252.0693 359.4824
+                        C 251.3438 356.7271 249.6045 348.897 249.6045 348.897
+                        C 249.0244 345.7085 248.2646 340.1631 267.874 336.4287
+                        C 277.0088 334.6885 277.4736 332.3276 277.8779 330.6289
+                        C 278.603 327.585 279.7627 325.8438 281.0674 324.9746
+                        C 282.373 324.1035 281.0674 323.3828 279.6187 323.2349
+                        C 275.7285 322.8311 261.3491 326.9541 252.8428 331.7881
+                        C 245.8828 336.0415 245.7656 339.8721 247.3584 343.1211
+                        C 236.8452 344.2573 228.9561 342.1348 227.5254 337.1582
+                        C 225.0693 328.6157 246.3047 314.0264 270.4839 306.7061
+                        C 295.8579 299.0225 321.9556 304.3857 324.8564 320.335
+                        C 326.1738 327.5811 320.0713 332.9419 309.7769 335.2627
+                       " />
+
+                       <!-- shadow on hat -->
+                       <path fill="black" d="
+                        M 270.8711 350.8813
+                        C 263.8721 350.375 263.145 349.6191 261.834 348.2227
+                        C 259.9854 346.2539 257.5508 350.7773 257.5508 350.7773
+                        C 256.0898 351.085 254.3179 353.4404 255.2744 355.6411
+                        C 256.2158 357.8174 257.9551 357.1641 258.5 356.4868
+                        C 259.1626 355.6621 260.5771 354.3125 262.4141 354.3613
+                        C 264.251 354.4097 266.3706 354.7959 269.3262 354.7959
+                        C 272.3213 354.7959 274.335 353.6777 274.4487 352.7168
+                        C 274.5459 351.8965 274.2061 351.1226 270.8711 350.8813
+                       " />
+
+                       <!-- another shadow on hat -->
+                       <path fill="black" d="
+                        M 278.2236 362.4463
+                        C 278.2129 362.4453 278.2021 362.4438 278.1919 362.4438
+                        C 278.084 362.4438 277.9961 362.5273 277.9961 362.6274
+                        C 277.9961 362.7007 278.041 362.7646 278.106 362.7939
+                        C 279.4629 363.5107 281.4873 364.0811 283.8042 364.3169
+                        C 284.499 364.3887 285.1787 364.4248 285.832 364.4307
+                        C 285.9478 364.4307 286.0615 364.4297 286.1787 364.4277
+                        C 290.062 364.3398 293.1719 362.7974 293.1255 360.9814
+                        C 293.0791 359.165 289.8955 357.7637 286.0112 357.8506
+                        C 284.7529 357.8794 283.5732 358.0615 282.5576 358.3545
+                        C 282.4385 358.3857 282.3506 358.4883 282.3506 358.6094
+                        C 282.3506 358.731 282.4385 358.834 282.5605 358.8638
+                        C 284.9839 359.4248 286.6191 360.3408 286.5039 361.207
+                        C 286.3511 362.3545 283.1816 362.979 279.4248 362.6011
+                        C 279.0137 362.5596 278.6118 362.5068 278.2236 362.4463
+                       " />
+
+                       <!-- ® for The Shadowman -->
+                       <path fill="black" d="
+                        M 588.3018 288.9707
+                        L 588.8369 288.9707
+                        L 589.6426 287.6445
+                        L 590.1626 287.6445
+                        L 589.2905 288.9937
+                        C 589.7422 289.0498 590.085 289.2881 590.085 289.834
+                        C 590.085 290.4385 589.7275 290.7051 589.0034 290.7051
+                        L 587.8384 290.7051
+                        L 587.8384 287.6445
+                        L 588.3018 287.6445
+                        L 588.3018 288.9707
+
+                        M 588.3018 289.3643
+                        L 588.3018 290.3101
+                        L 588.9336 290.3101
+                        C 589.2549 290.3101 589.5996 290.2402 589.5996 289.8647
+                        C 589.5996 289.3916 589.251 289.3643 588.8584 289.3643
+                        L 588.3018 289.3643
+                       " />
+
+                       <path fill="black" d="
+                        M 591.8457 289.1719
+                        C 591.8457 287.5176 590.5059 286.1763 588.8511 286.1763
+                        C 587.1968 286.1763 585.8545 287.5176 585.8545 289.1719
+                        C 585.8545 290.8271 587.1968 292.1675 588.8511 292.1675
+                        C 590.5059 292.1675 591.8457 290.8271 591.8457 289.1719
+
+                        M 588.8511 291.6367
+                        C 587.4883 291.6367 586.3853 290.5332 586.3853 289.1719
+                        C 586.3853 287.8096 587.4883 286.7061 588.8511 286.7061
+                        C 590.21 286.7061 591.3135 287.8096 591.3135 289.1719
+                        C 591.3135 290.5332 590.21 291.6367 588.8511 291.6367
+                       " />
+
+                     </g>
+                   </g>
+
+       </defs>
+
+  <g id="watermark" transform="translate(200, 185), rotate(-50), scale(2.5)">
+    <use xlink:href="#scarab" x="0" y="170" fill-opacity="0.2"/>
+  </g>
+
+  <!-- Blue bar at top of slide -->
+  <rect x="0" y="0" width="1024" height="170" fill="#162284" />
+
+  <!-- Scarab and "cairo" at upper-left -->
+  <g transform="translate(10,0)">
+    <use stroke="none" fill="white" xlink:href="#cairo_logo_text_small"/>
+  </g>
+
+  <!-- Presentation title at upper-right -->
+  <text ss:variable="presentation-subtitle" text-anchor="end"
+     fill="white" x="1016" y="28" font-size="20">Presentation Sub-title</text>
+
+  <!-- Red Hat logo at lower-left -->
+  <use xlink:href="#redhat_logo_horizontal" transform="translate(8,768),scale(.5,.5),translate(0, -125)" />
+
+  <g font-family="Frutiger">
+    <!-- Slide title -->
+    <g id="slide_title" transform="translate(512, 133)">
+      <text text-anchor="middle"
+           fill="white"
+           font-weight="bold"
+           x="0"
+           y="4" font-size="55"
+           ss:variable="title">Really long slide title so you can see</text>
+    </g>
+    
+    <!-- Slide content -->
+    <g ss:region="default">
+      <rect x="112" y="200" width="800" height="480" fill="none" stroke="blue"/>
+      <text font-size="40" fill="black"
+           x="112" y="232">Slide content</text>
+    </g>
+    
+    <!-- Footer -->
+    <text ss:variable="URL" x="1016" y="753" text-anchor="end" font-size="20">http://cairographics.org</text>
+  </g>
+
+</svg>
diff --git a/doc/tutorial/slides/circle-cairo-large.png b/doc/tutorial/slides/circle-cairo-large.png
new file mode 100644 (file)
index 0000000..a08e119
Binary files /dev/null and b/doc/tutorial/slides/circle-cairo-large.png differ
diff --git a/doc/tutorial/slides/circle-cairo.png b/doc/tutorial/slides/circle-cairo.png
new file mode 100644 (file)
index 0000000..f5d8cd4
Binary files /dev/null and b/doc/tutorial/slides/circle-cairo.png differ
diff --git a/doc/tutorial/slides/circle-ooo-large.png b/doc/tutorial/slides/circle-ooo-large.png
new file mode 100644 (file)
index 0000000..dfa4099
Binary files /dev/null and b/doc/tutorial/slides/circle-ooo-large.png differ
diff --git a/doc/tutorial/slides/circle-ooo.png b/doc/tutorial/slides/circle-ooo.png
new file mode 100644 (file)
index 0000000..eb90666
Binary files /dev/null and b/doc/tutorial/slides/circle-ooo.png differ
diff --git a/doc/tutorial/slides/expander-fuzzy-large.png b/doc/tutorial/slides/expander-fuzzy-large.png
new file mode 100644 (file)
index 0000000..3a485da
Binary files /dev/null and b/doc/tutorial/slides/expander-fuzzy-large.png differ
diff --git a/doc/tutorial/slides/expander-fuzzy.png b/doc/tutorial/slides/expander-fuzzy.png
new file mode 100644 (file)
index 0000000..b01fd63
Binary files /dev/null and b/doc/tutorial/slides/expander-fuzzy.png differ
diff --git a/doc/tutorial/slides/expander-sharp-large.png b/doc/tutorial/slides/expander-sharp-large.png
new file mode 100644 (file)
index 0000000..f97fa06
Binary files /dev/null and b/doc/tutorial/slides/expander-sharp-large.png differ
diff --git a/doc/tutorial/slides/expander-sharp.png b/doc/tutorial/slides/expander-sharp.png
new file mode 100644 (file)
index 0000000..40759ae
Binary files /dev/null and b/doc/tutorial/slides/expander-sharp.png differ
diff --git a/doc/tutorial/slides/fuzzies.svg b/doc/tutorial/slides/fuzzies.svg
new file mode 100644 (file)
index 0000000..df12b8a
--- /dev/null
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg xmlns="http://www.w3.org/2000/svg"
+     xmlns:xlink="http://www.w3.org/1999/xlink"
+     width="800" height="500">
+
+  <image xlink:href="expander-fuzzy.png" x="175" y="25" width="50" height="50"/>
+  <image xlink:href="expander-sharp.png" x="575" y="25" width="50" height="50"/>
+
+  <image xlink:href="expander-fuzzy-large.png" x="0" y="100" width="400" height="400"/>
+  <image xlink:href="expander-sharp-large.png" x="400" y="100" width="400" height="400"/>
+</svg>
diff --git a/doc/tutorial/slides/jaggies.svg b/doc/tutorial/slides/jaggies.svg
new file mode 100644 (file)
index 0000000..e99d07b
--- /dev/null
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg xmlns="http://www.w3.org/2000/svg"
+     xmlns:xlink="http://www.w3.org/1999/xlink"
+     width="800" height="500">
+
+  <image xlink:href="circle-ooo.png" x="175" y="25" width="50" height="50"/>
+  <image xlink:href="circle-cairo.png" x="575" y="25" width="50" height="50"/>
+
+  <image xlink:href="circle-ooo-large.png" x="0" y="100" width="400" height="400"/>
+  <image xlink:href="circle-cairo-large.png" x="400" y="100" width="400" height="400"/>
+</svg>
diff --git a/doc/tutorial/slides/rendering-model.png b/doc/tutorial/slides/rendering-model.png
new file mode 100644 (file)
index 0000000..9008984
Binary files /dev/null and b/doc/tutorial/slides/rendering-model.png differ
diff --git a/doc/tutorial/slides/tutorial.xml b/doc/tutorial/slides/tutorial.xml
new file mode 100644 (file)
index 0000000..3224bea
--- /dev/null
@@ -0,0 +1,535 @@
+<?xml version="1.0" ?>
+<svgslides
+    xmlns="http://www.svgslides.org/svgslides0.1"
+    xmlns:svg="http://www.w3.org/2000/svg"
+    xmlns:xlink="http://www.w3.org/1999/xlink"
+    >
+
+  <slides theme="cairo"
+         presentation="Drawing with cairo"
+         presentation-subtitle="drawing with cairo"
+         URL="http://cairographics.org"
+         bullet="bullet">
+
+    <slide title="Tutorial preparation" variant="blank" bullet="">
+      <lc></lc>
+      <lc align="center">http://cairographics.org/tutorial</lc>
+      <lc></lc>
+      <li>wget http://cairographics.org/cairo-tutorial.tar.gz</li>
+      <li>tar xzf cairo-tutorial.tar.gz</li>
+      <li>cd cairo-tutorial</li>
+      <li>make</li>
+      <li>cat README</li>
+      <lc></lc>
+      <lc align="center">IRC: irc.freenode.net #cairo</lc>
+    </slide>
+
+    <slide variant="title">
+      <lc>Carl Worth</lc>
+      <lc>Red Hat, Inc.</lc>
+      <lc></lc>
+      <lc>linux.conf.au</lc>
+      <lc>2006-01-26</lc>
+      <lc>http://cairographics.org</lc>
+    </slide>
+
+    <slide title="Ugly graphics" variant="separator">
+      <lc align="left">Jaggies</lc>
+      <lc align="center">Fuzzies</lc>
+      <lc align="right">Fireworks</lc>
+    </slide>
+
+    <slide title="Jaggies">
+      <img src="jaggies.svg"/>
+    </slide>
+
+    <slide title="Fuzzies">
+      <img src="fuzzies.svg"/>
+    </slide>
+
+    <slide title="Fireworks">
+    </slide>
+
+    <slide title="What you can do about it">
+      <li>Let application authors know there are options</li>
+      <li>Learn to use cairo—patch the applications</li>
+      <li>Write your own cairo-using applications</li>
+    </slide>
+
+    <slide title="Getting started" variant="separator">
+      <lc align="center">Various shell cairo programs</lc>
+    </slide>
+
+    <slide title="Minimal cairo-xlib program" variant="code">
+      <lc>#include &lt;cairo.h&gt;</lc>
+      <lc>#include &lt;cairo-xlib.h&gt;</lc>
+      <lc>int main (void) {</lc>
+      <lc>    Display *dpy = XOpenDisplay (0);</lc>
+      <lc>    Window  w = XCreateSimpleWindow (dpy,RootWindow (dpy, 0),</lc>
+      <lc>                0, 0, WIDTH, HEIGHT, 0, 0, WhitePixel (dpy, 0));</lc>
+      <lc>    cairo_surface_t *surface = cairo_xlib_surface_create (dpy, w,</lc>
+      <lc>                        DefaultVisual (dpy, DefaultScreen (dpy)),</lc>
+      <lc>                                                   WIDTH, HEIGHT);</lc>
+      <lc>    XEvent  ev;</lc>
+      <lc>    XSelectInput (dpy, w, ExposureMask);</lc>
+      <lc>    XMapWindow (dpy, w);</lc>
+      <lc>    while (XNextEvent (dpy, &amp;ev) == 0)</lc>
+      <lc>        if (ev.type == Expose &amp;&amp; !ev.xexpose.count) {</lc>
+      <lc>            cairo_t *cr = cairo_create (surface);</lc>
+      <lc>            draw (cr);</lc>
+      <lc>            cairo_destroy (cr);</lc>
+      <lc>        }</lc>
+      <lc>}</lc>
+    </slide>
+
+    <slide title="Minimal cairo-gtk program" variant="code">
+      <lc>#include &lt;gtk/gtk.h&gt;</lc>
+      <lc>static gboolean</lc>
+      <lc>handle_expose (GtkWidget *widget, GdkEventExpose *event, gpointer data) {</lc>
+      <lc>    cairo_t *cr = gdk_cairo_create (widget->window);</lc>
+      <lc>    draw (cr);</lc>
+      <lc>    cairo_destroy (cr);</lc>
+      <lc>    return FALSE;</lc>
+      <lc>}</lc>
+      <lc>int main (int argc, char **argv) {</lc>
+      <lc>    GtkWidget *window, *drawing_area;</lc>
+      <lc>    gtk_init (&amp;argc, &amp;argv);</lc>
+      <lc>    window = gtk_window_new (GTK_WINDOW_TOPLEVEL);</lc>
+      <lc>    gtk_window_set_default_size (GTK_WINDOW (window), WIDTH, HEIGHT);</lc>
+      <lc>    drawing_area = gtk_drawing_area_new ();</lc>
+      <lc>    gtk_container_add (GTK_CONTAINER (window), drawing_area);</lc>
+      <lc>    g_signal_connect (drawing_area, "expose-event",</lc>
+      <lc>                      G_CALLBACK (handle_expose), NULL);</lc>
+      <lc>    gtk_widget_show_all (window); gtk_main ();</lc>
+      <lc>}</lc>
+    </slide>
+
+    <slide title="Minimal cairo-png program" variant="code">
+      <lc>#include &lt;cairo.h&gt;</lc>
+      <lc></lc>
+      <lc>int main (void)</lc>
+      <lc>{</lc>
+      <lc>    cairo_surface_t *surface;</lc>
+      <lc>    cairo_t *cr;</lc>
+      <lc></lc>
+      <lc>    surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,</lc>
+      <lc>                                          WIDTH, HEIGHT);</lc>
+      <lc>    cr = cairo_create (surface);</lc>
+      <lc>    draw (cr);</lc>
+      <lc>    cairo_surface_write_to_png (surface, "foo.png");</lc>
+      <lc></lc>
+      <lc>    cairo_surface_destroy (surface);</lc>
+      <lc>    cairo_destroy (cr);</lc>
+      <lc></lc>
+      <lc>    return 0;</lc>
+      <lc>}</lc>
+    </slide>
+
+    <slide title="Minimal cairo-pdf program" variant="code">
+      <lc>#include &lt;cairo.h&gt;</lc>
+      <lc>#include &lt;cairo-pdf.h&gt;</lc>
+      <lc>int main (void)</lc>
+      <lc>{</lc>
+      <lc>    cairo_surface_t *surface;</lc>
+      <lc>    cairo_t *cr;</lc>
+      <lc></lc>
+      <lc>    surface = cairo_pdf_surface_create ("foo.pdf", WIDTH, HEIGHT);</lc>
+      <lc>    </lc>
+      <lc>    cr = cairo_create (surface);</lc>
+      <lc>    draw (cr);</lc>
+      <lc>    cairo_show_page (cr);</lc>
+      <lc></lc>
+      <lc>    cairo_surface_destroy (surface);</lc>
+      <lc>    cairo_destroy (cr);</lc>
+      <lc></lc>
+      <lc>    return 0;</lc>
+      <lc>}</lc>
+    </slide>
+
+    <slide title="Minimal pycairo-gtk shell" variant="code">
+      <lc>#!/usr/bin/env python</lc>
+      <lc>import gtk</lc>
+      <lc>import cairo</lc>
+      <lc></lc>
+      <lc>def expose (widget, event):</lc>
+      <lc>    cr = widget.window.cairo_create ()</lc>
+      <lc>    draw (cr)</lc>
+      <lc>      </lc>
+      <lc>win = gtk.Window ()</lc>
+      <lc>win.connect ('destroy', gtk.main_quit)</lc>
+      <lc>win.set_default_size(WIDTH, HEIGHT)</lc>
+      <lc>drawingarea = gtk.DrawingArea ()</lc>
+      <lc>win.add (drawingarea)</lc>
+      <lc>drawingarea.connect ('expose_event', expose)</lc>
+      <lc>win.show_all ()</lc>
+      <lc>gtk.main ()</lc>
+    </slide>
+
+    <slide title="Minimal pycairo-png shell" variant="code">
+      <lc>#!/usr/bin/env python</lc>
+      <lc>import cairo</lc>
+      <lc></lc>
+      <lc>surface = cairo.ImageSurface (cairo.FORMAT_ARGB32, WIDTH, HEIGHT)</lc>
+      <lc>cr = cairo.Context (surface)</lc>
+      <lc></lc>
+      <lc>draw (cr)</lc>
+      <lc></lc>
+      <lc>surface.write_to_png ('foo.png')</lc>
+    </slide>
+
+    <slide title="Minimal nickle program" variant="code">
+      <lc>autoimport Cairo;</lc>
+      <lc></lc>
+      <lc>cairo_t cr = new (WIDTH, HEIGHT);</lc>
+      <lc>draw (cr);</lc>
+    </slide>
+
+    <slide title="Available bindings:" variant="large-content">
+      <li>C++ (cairomm)</li>
+      <li>Haskell (hscairo)</li>
+      <li>Java (CairoJava, cairo-java)</li>
+      <li>Common Lisp (cl-cairo)</li>
+      <li>Nickle (cairo-5c)</li>
+      <li>Objective Caml (cairo-ocaml)</li>
+      <li>perl (cairo-perl)</li>
+      <li>python (pycairo)</li>
+      <li>ruby (rcairo)</li>
+      <li>squeak (cairo-squeak)</li>
+    </slide>
+
+    <slide title="Following along" >
+      <lc></lc>
+      <lc align="center">http://cairographics.org/tutorial</lc>
+      <lc align="center">http://cairographics.org/cairo-tutorial.tar.gz</lc>
+      <li>tar xzf cairo-tutorial.tar.gz</li>
+      <li>cd cairo-tutorial</li>
+      <li>make</li>
+      <li>cat README</li>
+      <lc align="center">IRC: irc.freenode.net #cairo</lc>
+    </slide>
+
+    <slide title="Cairo's rendering model">
+      <g>
+       <image x="0" y="0" xlink:href="rendering-model.png" width="800" height="500" />
+      </g>
+    </slide>
+
+    <slide title="Let's start drawing" variant="separator">
+      <lc align="center">Here comes the fun part</lc>
+    </slide>
+
+    <slide title="API Overview" variant="large-content">
+      <ul>
+       <li>Paths</li>
+       <ul>
+         <li>construction</li>
+         <li>filling, stroking</li>
+       </ul>
+       <li>Images</li>
+       <ul>
+         <li>loading from disk</li>
+         <li>using as source</li>
+         <li>transforming/filtering</li>
+       </ul>
+       <li>Text</li>
+       <ul>
+         <li>“Toy” API</li>
+         <li>“Full” API</li>
+       </ul>
+      </ul>
+    </slide>
+
+    <slide title="Drawing functions" variant="large-content">
+      <ul>
+       <li>Paths</li>
+       <ul>
+         <li>cairo_stroke</li>
+         <li>cairo_fill</li>
+       </ul>
+       <li>Images</li>
+       <ul>
+         <li>cairo_paint</li>
+         <li>cairo_mask</li>
+         <li>cairo_paint_with_alpha</li>
+       </ul>
+       <li>Text</li>
+       <ul>
+         <li>cairo_show_text (“toy”)</li>
+         <li>cairo_show_glyphs (“full”)</li>
+       </ul>
+      </ul>
+    </slide>
+
+    <slide title="Basic path building">
+      <ul>
+       <li>Paths consist of lines and splines:</li>
+       <ul>
+         <li>cairo_move_to</li>
+         <lc>  start new sub path, set current point</lc>
+         <li>cairo_line_to</li>
+         <lc>  add line segment to path</lc>
+         <li>cairo_curve_to</li>
+         <lc>  add Bézier spline to path</lc>
+         <li>cairo_close_path</li>
+         <lc>  add line segment to last move_to point</lc>
+       </ul>
+      </ul>
+    </slide>
+
+    <slide title="Stroking paths">
+      <ul>
+       <li>cairo_stroke</li>
+       <ul>
+         <li>Paints the path itself as stroked by a pen</li>
+         <li>Controlled by various stroke parameters:</li>
+         <ul>
+           <li>cairo_set_line_width</li>
+           <li>cairo_set_line_cap</li>
+           <li>cairo_set_line_join</li>
+           <li>cairo_set_miter_limit</li>
+           <li>cairo_set_dash</li>
+         </ul>
+       </ul>
+      </ul>
+    </slide>
+
+    <slide title="cairo_stroke example">
+    </slide>
+
+    <slide title="cairo_set_line_width">
+    </slide>
+
+    <slide title="cairo_set_line_cap example">
+      <ul>
+       <li>CAIRO_LINE_CAP_BUTT</li>
+       <li>CAIRO_LINE_CAP_ROUND</li>
+       <li>CAIRO_LINE_CAP_SQUARE</li>
+      </ul>
+    </slide>
+
+    <slide title="cairo_set_line_join example">
+      <ul>
+       <li>CAIRO_LINE_JOIN_BEVEL</li>
+       <li>CAIRO_LINE_JOIN_ROUND</li>
+       <li>CAIRO_LINE_JOIN_MITER</li>
+      </ul>
+    </slide>
+
+    <slide title="cairo_close_path example">
+      <li>Adds a line segment (if necessary) to the</li>
+      <lc>start of the path</lc>
+      <li>Draws a join from that line to the first</li>
+      <lc>element of the path</lc>
+    </slide>
+
+    <slide title="cairo_set_dash example">
+    </slide>
+
+    <slide title="cairo_fill">
+      <ul>
+       <li>Paints the “inside” of the path</li>
+       <li>Two different ways to determine inside-ness</li>
+       <ul>
+         <li>cairo_set_fill_rule:</li>
+         <lc>  CAIRO_FILL_RULE_WINDING</lc>
+         <lc>  CAIRO_FILL_RULE_EVEN_ODD</lc>
+       </ul>
+      </ul>
+    </slide>
+
+    <slide title="cairo_fill example">
+    </slide>
+
+    <slide title="cairo_set_fill_rule">
+      <li>To test a point for inside-ness, imagine a ray</li>
+      <lc>from the point and extending to infinity</lc>
+      <lc>(any direction) and examine path crossings</lc>
+      <li>EVEN_ODD counts crossings and fills when odd</li>
+      <li>WINDING counts up/down for left/right crossings</li>
+      <lc>and fills when the final number is not zero</lc>
+    </slide>
+
+    <slide title="Fill rule example">
+    </slide>
+
+    <slide title="Something besides black?">
+      <li>All drawing occurs with the source pattern</li>
+      <li>Uniform-color sources can be set</li>
+      <lc>with or without alpha:</lc>
+      <ul>
+       <li>cairo_set_source_rgb</li>
+       <li>cairo_set_source_rgba</li>
+      </ul>
+    </slide>
+
+    <slide title="cairo_set_source example">
+    </slide>
+
+    <slide title="Fill and stroke the same path">
+      <li>cairo_stroke and cairo_fill consume the path</li>
+      <li>Variants are available to preserve the path:</li>
+      <ul>
+       <li>cairo_stroke_preserve</li>
+       <li>cairo_fill_preserve</li>
+      </ul>
+      <li>This is a variation from PostScript and PDF</li>
+      <ul>
+       <li>The cairo approach differs in different ways</li>
+       <li>We think this way is better (of course)</li>
+      </ul>
+    </slide>
+
+    <slide title="Fill and stroke example">
+    </slide>
+
+    <slide title="More path building">
+      <ul>
+       <li>cairo_rectangle</li>
+       <li>cairo_arc</li>
+       <li>cairo_text_path</li>
+       <li>cairo_glyph_path</li>
+       <li>Relative-coordinate variants:</li>
+       <ul>
+         <li>cairo_rel_move_to</li>
+         <li>cairo_rel_line_to</li>
+         <li>cairo_rel_curve_to</li>
+       </ul>
+      </ul>
+    </slide>
+
+    <slide title="cairo_arc example">
+    </slide>
+
+    <slide title="Outline text">
+    </slide>
+
+    <slide title="Affine Transformations">
+      <ul>
+       <li>Single matrix combines rotation, translation,</li>
+       <lc>scale and shear</lc>
+       <li>Non-projective transformations</li>
+       <ul>
+         <li>Pen doesn't change shape along the stroke</li>
+       </ul>
+       <li>Transformations are cumulative</li>
+       <ul>
+         <li>translate, scale != scale, translate</li>
+       </ul>
+      </ul>
+    </slide>
+
+    <slide title="Affine Transform Example">
+    </slide>
+
+    <slide title="Save/Restore Graphics States">
+      <ul>
+       <li>cairo_save and cairo_restore</li>
+       <li>Very useful for bracketing function bodies</li>
+       <li>Eliminates graphics state side effects</li>
+       <lc></lc>
+       <li>NOTE: Path is not part of graphics state</li>
+       <ul>
+         <li>Path is not affected by cairo_save/restore</li>
+         <li>Useful for path-generating functions</li>
+       </ul>
+      </ul>
+    </slide>
+
+    <slide title="Clipping">
+      <li>cairo_clip</li>
+      <li>Intersects path with current clip to further</li>
+      <lc>restrict drawing</lc>
+      <li>Pixel-aligned clipping is much faster</li>
+      <li>cairo_clip_preserve is available as well</li>
+    </slide>
+
+    <slide title="Other source patterns">
+      <ul>
+       <li>Surface patterns</li>
+       <ul>
+         <li>Image from disk</li>
+         <li>Results of cairo drawing</li>
+       </ul>
+       <li>Gradient patterns</li>
+       <ul>
+         <li>linear gradients</li>
+         <li>radial gradients</li>
+       </ul>
+      </ul>
+    </slide>
+
+    <slide title="Loading an image file">
+    </slide>
+
+    <slide title="Painting an image">
+    </slide>
+
+    <slide title="Pattern transformation">
+    </slide>
+
+    <slide title="Pattern filtering">
+    </slide>
+
+    <slide title="Linear gradient example">
+    </slide>
+
+    <slide title="Radial gradient example">
+    </slide>
+
+    <slide title="Text">
+      <ul>
+       <li>“Toy” API:</li>
+       <ul>
+         <li>Useful for demos like we are writing today</li>
+         <li>No real layout—not useful for the majority</li>
+         <lc>of writing systems in the world</lc>
+       </ul>
+       <li>“Full” API:</li>
+       <ul>
+         <li>User must do OS-dependent font selection</li>
+         <li>User must access font glyph IDs directly</li>
+         <li>User must do all text layout</li>
+       </ul>
+      </ul>
+    </slide>
+
+    <slide title="“Toy” text API"/>
+    <ul>
+      <li>Simple font selection</li>
+      <ul>
+       <li>family, weight, slant</li>
+       <li>OS independent</li>
+       <li>No font listing support</li>
+      </ul>
+      <li>UTF-8 text drawing and extents functions</li>
+      <li>Still supports full font transformations</li>
+    </ul>
+
+    <slide title="“Toy” text example">
+    </slide>
+
+    <slide title="“Real” text example">
+      <li>Just use pango</li>
+    </slide>
+
+    <slide title="Error handling in C">
+      <li>C has no exceptions</li>
+      <li>Checking each return is tedious</li>
+      <li>C programmers rarely bother</li>
+      <li>Lots of broken programs result</li>
+    </slide>
+      
+      <slide title="Cairo error handling">
+       <li>Cairo stores status value when error occurs</li>
+       <li>API “shuts down” when an error occurs</li>
+       <li>All cairo functions are benign</li>
+       <lc>(and well defined) after any error.</lc>
+       <li>cairo_status can be called when convenient</li>
+       <li>Error status values propagate across objects</li>
+      </slide>
+
+      <slide title="Cairo error example">
+      </slide>
+
+  </slides>
+</svgslides>
diff --git a/doc/tutorial/src/.gitignore b/doc/tutorial/src/.gitignore
new file mode 100644 (file)
index 0000000..68af59e
--- /dev/null
@@ -0,0 +1,8 @@
+*-gtk
+*-pdf
+*-png
+*-xlib
+*.pdf
+*.png
+*.o
+*~
diff --git a/doc/tutorial/src/README b/doc/tutorial/src/README
new file mode 100644 (file)
index 0000000..a2738da
--- /dev/null
@@ -0,0 +1,66 @@
+Welcome to the cairo tutorial:
+
++--------------------------------+
+| How to Recognize Ugly Graphics |
+|(and what you can do about them)|
++--------------------------------+
+
+This directory is your personal playground for following along with
+the examples. In order for you to make use of these files you will
+need to have cairo and its header files installed. You can find
+instructions for doing this at:
+
+       http://cairographics.org/tutorial
+
+Notice that there are a few .c files in this directory.
+
+You should start out by just typing "make" which will turn each .c
+file into several different programs. Go ahead and run each of the
+programs and see what they do. Some of them will open up new X windows
+while others will simply write their output to files (such .png or
+.pdf).
+
+After you play with those a bit, go ahead and take a look at the
+contents of the .c files. You'll see that each file contains a draw()
+function that does all of the drawing.
+
+You might be surprised to notice that there is no main() function in
+any of the files. Instead, main is hidden away by means of
+cairo-tutorial.h. This rather non-conventional style is used to allow
+you to focus on the actual drawing code involved in using cairo, while
+not having to worry about the setup semantics. We don't recommend that
+you follow this style for real projects.
+
+As you follow along during the tutorial and get some ideas for things
+to draw, you'll want to start making your own .c files. You can copy
+an existing file or make your own by following this simple minimal
+template:
+
+       #include "cairo-tutorial.h"
+
+       static void
+       draw (cairo_t *cr, int width, int height)
+       {
+           /* Put your drawing code here. */
+       }
+
+Any new file you create will automatically get picked up by the
+Makefile so that "make" will compile your file into several different
+programs, just like the existing examples.
+
+If you'd like to control the initial size of the output, you may
+define WIDTH and HEIGHT before including cairo-tutorial.h like so:
+
+       #define WIDTH  100
+       #define HEIGHT 100
+
+       #include "cairo-tutorial.h"
+
+If you would like to change the set of cairo-backend target programs
+that are compiled, you may edit the "all" target in the Makefile.
+
+Have fun!
+
+-Carl Worth
+
+cworth@redhat.com
diff --git a/doc/tutorial/src/circle.c b/doc/tutorial/src/circle.c
new file mode 100644 (file)
index 0000000..6bd02e8
--- /dev/null
@@ -0,0 +1,22 @@
+#include "cairo-tutorial.h"
+
+static void
+draw (cairo_t *cr, int width, int height)
+{
+    int radius;
+
+    if (width < height)
+       radius = width/2 - 4;
+    else
+       radius = height/2 - 4;
+
+    cairo_move_to (cr, width/2 + radius, height/2);
+    cairo_arc (cr, width/2, height/2, radius,
+              0.0, 2 * M_PI);
+
+    cairo_set_source_rgb (cr, 0.6, 0.8, 1.0);
+    cairo_fill_preserve (cr);
+
+    cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
+    cairo_stroke (cr);
+}
diff --git a/doc/tutorial/src/include/cairo-tutorial-gtk.h b/doc/tutorial/src/include/cairo-tutorial-gtk.h
new file mode 100644 (file)
index 0000000..60516a3
--- /dev/null
@@ -0,0 +1,133 @@
+/* cairo-tutorial-gtk.h - a tutorial framework for cairo with gtk+
+ *
+ * Copyright © 2005, Carl Worth
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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,
+ * Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA.
+ */
+
+#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
+
+#include <cairo.h>
+
+#ifndef WIDTH
+#define WIDTH 400
+#endif
+
+#ifndef HEIGHT
+#define HEIGHT 400
+#endif
+
+static void
+draw (cairo_t *cr, int width, int height);
+
+#if ! GTK_CHECK_VERSION(2,7,0)
+/* copied from gtk+/gdk/gdkcairo.c and gtk+/gdk/x11/gdkdrawable-x11.c
+ * gdk_cairo_create() which is available in 2.7.0 and later.
+ */
+static cairo_t *
+gdk_cairo_create (GdkDrawable *drawable)
+{
+    int width, height;
+    cairo_t *cr = NULL;
+    cairo_surface_t *surface = NULL;
+    GdkVisual *visual = gdk_drawable_get_visual (drawable);
+
+    gdk_drawable_get_size (drawable, &width, &height);
+    if (visual)
+       surface = cairo_xlib_surface_create (GDK_DRAWABLE_XDISPLAY (drawable),
+                                            GDK_DRAWABLE_XID (drawable),
+                                            GDK_VISUAL_XVISUAL (visual),
+                                            width, height);
+    else if (gdk_drawable_get_depth (drawable) == 1)
+       surface = cairo_xlib_surface_create_for_bitmap
+           (GDK_PIXMAP_XDISPLAY (drawable),
+            GDK_PIXMAP_XID (drawable),
+            GDK_SCREEN_XSCREEN (gdk_drawable_get_screen (drawable)),
+            width, height);
+    else {
+       g_warning ("Using Cairo rendering requires the drawable argument to\n"
+                  "have a specified colormap. All windows have a colormap,\n"
+                  "however, pixmaps only have colormap by default if they\n"
+                  "were created with a non-NULL window argument. Otherwise\n"
+                  "a colormap must be set on them with "
+                  "gdk_drawable_set_colormap");
+       return NULL;
+    }
+    if (surface) {
+       cr = cairo_create (surface);
+       cairo_surface_destroy (surface);
+    }
+    return cr;
+}
+#endif
+
+static gboolean
+handle_expose (GtkWidget      *widget,
+              GdkEventExpose *event,
+              gpointer        data)
+{
+    cairo_t *cr;
+
+    cr = gdk_cairo_create (widget->window);
+
+    draw (cr, widget->allocation.width, widget->allocation.height);
+
+    cairo_destroy (cr);
+
+    return FALSE;
+}
+
+static gboolean
+handle_key_press (GtkWidget *widget,
+                 GdkEventKey *event,
+                 gpointer data)
+{
+    if ((event->keyval == GDK_Q ||
+        event->keyval == GDK_q) && (event->state & GDK_CONTROL_MASK))
+       gtk_main_quit ();
+
+    return FALSE;
+}
+
+int
+main (int argc, char **argv)
+{
+    GtkWidget *window, *drawing_area;
+
+    gtk_init (&argc, &argv);
+
+    window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+
+    gtk_window_set_default_size (GTK_WINDOW (window), WIDTH, HEIGHT);
+    gtk_window_set_title (GTK_WINDOW (window), "cairo demo");
+
+    g_signal_connect (window, "destroy",
+                     G_CALLBACK (gtk_main_quit), NULL);
+
+    drawing_area = gtk_drawing_area_new ();
+    gtk_container_add (GTK_CONTAINER (window), drawing_area);
+
+    g_signal_connect (drawing_area, "expose-event",
+                     G_CALLBACK (handle_expose), NULL);
+
+    g_signal_connect (window, "key-press-event",
+                     G_CALLBACK (handle_key_press), NULL);
+
+    gtk_widget_show_all (window);
+
+    gtk_main ();
+
+    return 0;
+}
diff --git a/doc/tutorial/src/include/cairo-tutorial-pdf.h b/doc/tutorial/src/include/cairo-tutorial-pdf.h
new file mode 100644 (file)
index 0000000..00d0f18
--- /dev/null
@@ -0,0 +1,74 @@
+/* cairo-tutorial-png.h - a tutorial framework for cairo to write a PNG image
+ *
+ * Copyright © 2005, Carl Worth
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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,
+ * Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA.
+ */
+
+#include <cairo.h>
+#include <cairo-pdf.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <string.h>
+
+#ifndef WIDTH
+#define WIDTH 400
+#endif
+
+#ifndef HEIGHT
+#define HEIGHT 400
+#endif
+
+static void
+draw (cairo_t *cr, int width, int height);
+
+int
+main (int argc, char **argv)
+{
+    cairo_surface_t *surface;
+    cairo_t *cr;
+    char *filename, *dash;
+
+    filename = strdup (argv[0]);
+    assert (filename != NULL);
+
+    dash = strrchr (filename, '-');
+
+    if (strcmp (dash, "-pdf") == 0) {
+       *dash = '.';
+    } else {
+       char *new_filename;
+       new_filename = malloc (strlen (filename) + 5);
+       sprintf (new_filename, "%s.pdf", filename);
+       free (filename);
+       filename = new_filename;
+    }
+
+    surface = cairo_pdf_surface_create (filename, WIDTH, HEIGHT);
+
+    cr = cairo_create (surface);
+
+    draw (cr, WIDTH, HEIGHT);
+
+    cairo_show_page (cr);
+
+    cairo_surface_destroy (surface);
+    cairo_destroy (cr);
+
+    free (filename);
+
+    return 0;
+}
diff --git a/doc/tutorial/src/include/cairo-tutorial-png.h b/doc/tutorial/src/include/cairo-tutorial-png.h
new file mode 100644 (file)
index 0000000..428baf2
--- /dev/null
@@ -0,0 +1,74 @@
+/* cairo-tutorial-png.h - a tutorial framework for cairo to write a PNG image
+ *
+ * Copyright © 2005, Carl Worth
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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,
+ * Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA.
+ */
+
+#include <cairo.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <string.h>
+
+#ifndef WIDTH
+#define WIDTH 400
+#endif
+
+#ifndef HEIGHT
+#define HEIGHT 400
+#endif
+
+static void
+draw (cairo_t *cr, int width, int height);
+
+int
+main (int argc, char **argv)
+{
+    cairo_surface_t *surface;
+    cairo_t *cr;
+    char *filename, *dash;
+
+    surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
+                                         WIDTH, HEIGHT);
+
+    cr = cairo_create (surface);
+
+    draw (cr, WIDTH, HEIGHT);
+
+    filename = strdup (argv[0]);
+    assert (filename != NULL);
+
+    dash = strrchr (filename, '-');
+
+    if (strcmp (dash, "-png") == 0) {
+       *dash = '.';
+    } else {
+       char *new_filename;
+       new_filename = malloc (strlen (filename) + 5);
+       sprintf (new_filename, "%s.png", filename);
+       free (filename);
+       filename = new_filename;
+    }
+
+    cairo_surface_write_to_png (surface, filename);
+
+    free (filename);
+
+    cairo_surface_destroy (surface);
+    cairo_destroy (cr);
+
+    return 0;
+}
diff --git a/doc/tutorial/src/include/cairo-tutorial-xlib.h b/doc/tutorial/src/include/cairo-tutorial-xlib.h
new file mode 100644 (file)
index 0000000..5e78d03
--- /dev/null
@@ -0,0 +1,251 @@
+/* cairo-tutorial-xlib.h - a tutorial framework for cairo with xlib
+ *
+ * Copyright © 2005, Keith Packard
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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,
+ * Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA.
+ */
+
+#include    <stdio.h>
+#include    <stdlib.h>
+#include    <ctype.h>
+#include    <strings.h>
+#include    <X11/Xos.h>
+#include    <X11/Xlib.h>
+#include    <X11/Xutil.h>
+#include    <X11/Xatom.h>
+#include    <cairo.h>
+#include    <cairo-xlib.h>
+
+#ifndef WIDTH
+#define WIDTH 400
+#endif
+
+#ifndef HEIGHT
+#define HEIGHT 400
+#endif
+
+#ifndef DEFAULT_VISUAL
+#define DEFAULT_VISUAL 0
+#endif
+
+static void
+Usage (char *program)
+{
+    fprintf (stderr, "Usage: %s\n", program);
+    fprintf (stderr, "\t-display <display-name>\n");
+    fprintf (stderr, "\t-geometry <geometry>\n");
+    exit (1);
+}
+
+char       *dpy_name;
+VisualID    vid = DEFAULT_VISUAL;
+Colormap    colormap;
+Visual     *visual;
+int        depth;
+unsigned int width = WIDTH, height = HEIGHT;
+Window     win;
+Pixmap     pix;
+GC         gc;
+
+static void
+draw (cairo_t *cr, int width, int height);
+
+static void
+draw_to_pixmap (Display *dpy, Pixmap pix)
+{
+    cairo_surface_t *surface;
+    cairo_t *cr;
+
+    surface = cairo_xlib_surface_create (dpy, pix, visual,
+                                        width, height);
+    cr = cairo_create (surface);
+
+    draw (cr, width, height);
+
+    cairo_destroy (cr);
+    cairo_surface_destroy (surface);
+}
+
+static void
+handle_configure (Display *dpy, XConfigureEvent *cev)
+{
+    width = cev->width;
+    height = cev->height;
+
+    XFreePixmap(dpy, pix);
+    pix = XCreatePixmap(dpy, win, width, height, depth);
+    XFillRectangle(dpy, pix, gc, 0, 0, width, height);
+    draw_to_pixmap (dpy, pix);
+}
+
+static void
+handle_expose (Display *dpy, XExposeEvent *eev)
+{
+    XCopyArea (dpy, pix, win, gc,
+              eev->x, eev->y,
+              eev->width, eev->height,
+              eev->x, eev->y);
+}
+
+int
+main (argc, argv)
+    int            argc;
+    char    **argv;
+{
+    Display *dpy;
+    Window  root = 0;
+    char    **init_argv = argv;
+    XSetWindowAttributes    attr;
+    int            scr;
+    int            x = 0, y = 0;
+    int            geometryMask;
+    int            border_width = 1;
+    XSizeHints sizeHints;
+    XWMHints   wmHints;
+    XClassHint classHints;
+    XEvent     ev;
+    XEvent     eev;
+    int                HasExpose = 0;
+    int                sync = 0;
+    XTextProperty   wm_name, icon_name;
+    Atom       wm_delete_window;
+    unsigned long   gc_mask;
+    XGCValues      gcv;
+    char       quit_string[10];
+    unsigned long   window_mask;
+    int                has_colormap = 0;
+
+    wm_name.value = (unsigned char *) argv[0];
+    wm_name.encoding = XA_STRING;
+    wm_name.format = 8;
+    wm_name.nitems = strlen (argv[0]) + 1;
+    icon_name = wm_name;
+    gc_mask = 0;
+    while (*++argv) {
+       if (!strcmp (*argv, "-display"))
+           dpy_name = *++argv;
+       else if (!strcmp (*argv, "-visual"))
+           vid = strtol(*++argv, NULL, 0);
+       else if (!strcmp (*argv, "-geometry"))
+           geometryMask = XParseGeometry (*++argv, &x, &y, &width, &height);
+       else if (!strcmp (*argv, "-sync"))
+           sync = 1;
+       else if (!strcmp (*argv, "-bw"))
+           border_width = strtol(*++argv, NULL, 0);
+       else if (!strcmp (*argv, "-root"))
+           root = strtol (*++argv, NULL, 0);
+       else
+           Usage (*init_argv);
+    }
+    sizeHints.flags = 0;
+    wmHints.flags = InputHint;
+    wmHints.input = True;
+    classHints.res_name = init_argv[0];
+    classHints.res_class = init_argv[0];
+    dpy = XOpenDisplay (dpy_name);
+    if (!dpy) {
+       fprintf (stderr, "Error: failed to open display: %s\n",
+                XDisplayName (dpy_name));
+       exit (1);
+    }
+    if (sync)
+       XSynchronize (dpy, sync);
+    scr = DefaultScreen (dpy);
+    if (!root)
+       root = RootWindow (dpy, scr);
+    window_mask = CWBackPixel|CWBorderPixel|CWEventMask;
+    if (!has_colormap)
+       colormap = DefaultColormap (dpy, scr);
+    else
+    {
+       window_mask |= CWColormap;
+       attr.colormap = colormap;
+    }
+    visual = DefaultVisual (dpy, scr);
+    depth = DefaultDepth (dpy, scr);
+    if (vid)
+    {
+       XVisualInfo vi, *vi_ret;
+       int         n;
+
+       vi.visualid = vid;
+       vi.screen = scr;
+       vi_ret = XGetVisualInfo (dpy, VisualIDMask|VisualScreenMask,
+                                &vi, &n);
+       if (vi_ret)
+       {
+           visual = vi_ret->visual;
+           if (!has_colormap)
+           {
+               colormap = XCreateColormap (dpy, root, visual, AllocNone);
+               window_mask |= CWColormap;
+               attr.colormap = colormap;
+           }
+           depth = vi_ret->depth;
+       }
+    }
+    attr.background_pixel = WhitePixel (dpy, scr);
+    attr.border_pixel = 0;
+    attr.event_mask = ExposureMask|KeyPressMask|KeyReleaseMask|StructureNotifyMask;
+    wm_delete_window = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
+    win = XCreateWindow (dpy, root, x, y, width, height, border_width,
+                        depth, InputOutput,
+                        visual,
+                        window_mask,
+                        &attr);
+    pix = XCreatePixmap (dpy, win, width, height, depth);
+    gcv.foreground = WhitePixel (dpy, scr);
+    gc = XCreateGC (dpy, pix, GCForeground, &gcv);
+    XFillRectangle(dpy, pix, gc, 0, 0, width, height);
+    draw_to_pixmap (dpy, pix);
+    XSetWMProperties (dpy, win,
+                     &wm_name, &icon_name,
+                     init_argv, argc,
+                     &sizeHints, &wmHints, 0);
+    XSetWMProtocols (dpy, win, &wm_delete_window, 1);
+    XMapWindow (dpy, win);
+    for (;;) {
+       XNextEvent (dpy, &ev);
+       if (HasExpose && ev.type != Expose) {
+           HasExpose = 0;
+           handle_expose (dpy, &eev.xexpose);
+       }
+       switch (ev.type) {
+       case ConfigureNotify:
+           handle_configure (dpy, &ev.xconfigure);
+           break;
+       case Expose:
+           if (QLength(dpy)) {
+               eev = ev;
+               HasExpose = 1;
+           } else if (ev.xexpose.count == 0) {
+               handle_expose (dpy, &ev.xexpose);
+           }
+           break;
+       case KeyPress:
+           if (XLookupString ((XKeyEvent *) &ev, quit_string, sizeof (quit_string), 0, 0) == 1) {
+               switch (quit_string[0]) {
+               case 'q':
+                   exit (0);
+               case 'c':
+                   XClearArea (dpy, ev.xkey.window, 0, 0, 0, 0, True);
+                   break;
+               }
+           }
+           break;
+       case ClientMessage:
+           exit (0);
+       }
+    }
+}
diff --git a/doc/tutorial/src/include/cairo-tutorial.h b/doc/tutorial/src/include/cairo-tutorial.h
new file mode 100644 (file)
index 0000000..ec738e9
--- /dev/null
@@ -0,0 +1,40 @@
+/* cairo-tutorial-gtk.h - a tutorial framework for cairo
+ *
+ * Copyright © 2005, Carl Worth
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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,
+ * Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA.
+ */
+
+#include <cairo.h>
+#include <math.h>
+
+/* The application program may override these before including
+ * cairo-tutorial.h in order to get a window of a different size. */
+#ifndef WIDTH
+#define WIDTH 400
+#endif
+
+#ifndef HEIGHT
+#define HEIGHT 400
+#endif
+
+#ifdef CAIRO_TUTORIAL_GTK
+#include "cairo-tutorial-gtk.h"
+#elif CAIRO_TUTORIAL_XLIB
+#include "cairo-tutorial-xlib.h"
+#elif CAIRO_TUTORIAL_PDF
+#include "cairo-tutorial-pdf.h"
+#elif CAIRO_TUTORIAL_PNG
+#include "cairo-tutorial-png.h"
+#endif
diff --git a/doc/tutorial/src/lca.c b/doc/tutorial/src/lca.c
new file mode 100644 (file)
index 0000000..0b131af
--- /dev/null
@@ -0,0 +1,32 @@
+#define WIDTH 750
+#define HEIGHT 360
+
+#include "cairo-tutorial.h"
+
+static void
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_save (cr);
+
+    cairo_translate (cr, 60, 60);
+    cairo_scale (cr, 3, 3);
+
+    cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
+    cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND);
+    cairo_set_line_width (cr, 20);
+
+    /* L */
+    cairo_move_to (cr, 0, 0);
+    cairo_line_to (cr, 0, 80);
+    cairo_line_to (cr, 50, 80);
+
+    /* C */
+    cairo_move_to (cr, 110 + 40 * cos (M_PI / 3), 40 + 40 * sin(M_PI / 3));
+    cairo_arc (cr, 110, 40, 40, M_PI / 3, -M_PI / 3);
+
+    /* A */
+    cairo_move_to (cr, 160, 80);
+    cairo_curve_to (cr, 160, -30, 210, -30, 210, 80);
+
+    cairo_stroke (cr);
+}
diff --git a/doc/tutorial/src/singular.c b/doc/tutorial/src/singular.c
new file mode 100644 (file)
index 0000000..958b2b0
--- /dev/null
@@ -0,0 +1,162 @@
+/**
+ * Uses Singular values of transformation matrix to find the length of the
+ * major and minor axes of the scaled pen.
+ *
+ * Put this file in cairo/doc/tutorial/src and type "make"
+ */
+
+#define WIDTH 300
+#define HEIGHT 300
+
+#include "cairo-tutorial.h"
+
+#include <math.h>
+
+/*
+ * Finds the singular values of the non-translation part of matrix.
+ *
+ * Let M be the cairo transformation matrix in question:
+ *
+ *       ⌈xx xy⌉
+ *   M = |yx yy|
+ *       ⌊x0 y0⌋
+ *
+ * The non-translation part is:
+ *
+ *   A = ⌈xx xy⌉
+ *       ⌊yx yy⌋
+ *
+ * The non-zero singular values of A are the square roots of the non-zero
+ * eigenvalues of A⁺ A, where A⁺ is A-transpose.
+ *
+ *   A⁺ A = ⌈xx yx⌉⌈xx xy⌉ = ⌈xx²+yx²     xx*xy+yx*yy⌉
+ *          ⌊xy yy⌋⌊yx yy⌋   ⌊xx*xy+yx*yy     xy²+yy²⌋
+ *
+ * Name those:
+ *
+ *   B = A⁺ A = ⌈a k⌉
+ *              ⌊k b⌋
+ *
+ * The eigenvalues of B satisfy:
+ *
+ *   λ² - (a+b).λ + a.b - k² = 0
+ *
+ * The eigenvalues are:
+ *                __________________
+ *       (a+b) ± √(a+b)² - 4(a.b-k²)
+ *   λ = ---------------------------
+ *                   2
+ * that simplifies to:
+ *                  _______________
+ *   λ = (a+b)/2 ± √((a-b)/2)² + k²
+ *
+ * And the Singular values are the root of λs.
+ *
+ */
+static void
+get_singular_values (const cairo_matrix_t *matrix,
+                    double *major,
+                    double *minor)
+{
+    double xx = matrix->xx, xy = matrix->xy;
+    double yx = matrix->yx, yy = matrix->yy;
+
+    double a = xx*xx+yx*yx;
+    double b = xy*xy+yy*yy;
+    double k = xx*xy+yx*yy;
+
+    double f = (a+b) * .5;
+    double g = (a-b) * .5;
+    double delta = sqrt (g*g + k*k);
+
+    if (major)
+       *major = sqrt (f + delta);
+    if (minor)
+       *minor = sqrt (f - delta);
+}
+
+/*
+ * Finds the length of the major and minor axes of the pen for a cairo_t,
+ * identified by the current transformation matrix and line width.
+ *
+ * Returned values are in device units.
+ */
+static void
+get_pen_axes (cairo_t *cr,
+             double *major,
+             double *minor)
+{
+    double width;
+    cairo_matrix_t matrix;
+
+    width = cairo_get_line_width (cr);
+    cairo_get_matrix (cr, &matrix);
+
+    get_singular_values (&matrix, major, minor);
+
+    if (major)
+       *major *= width;
+    if (minor)
+       *minor *= width;
+}
+
+static void
+draw (cairo_t *cr, int width, int height)
+{
+    double major_width, minor_width;
+
+    /* clear background */
+    cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); /* white */
+    cairo_paint (cr);
+
+#define W width
+#define H height
+#define B ((width+height)/16)
+
+    /* the spline we want to stroke */
+    cairo_move_to  (cr, W-B, B);
+    cairo_curve_to (cr, -W,   B,
+                       2*W, H-B,
+                       B,   H-B);
+
+    /* the effect is show better with round caps */
+    cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
+
+    /* set the skewed pen */
+    cairo_rotate (cr, +.7);
+    cairo_scale  (cr, .5, 2.);
+    cairo_rotate (cr, -.7);
+    cairo_set_line_width (cr, B);
+
+    get_pen_axes (cr, &major_width, &minor_width);
+
+    /* stroke with "major" pen in translucent red */
+    cairo_save (cr);
+    cairo_identity_matrix (cr);
+    cairo_set_line_width (cr, major_width);
+    cairo_set_source_rgba (cr, 1.0, 0.0, 0.0, .9);
+    cairo_stroke_preserve (cr);
+    cairo_restore (cr);
+
+    /* stroke with skewed pen in translucent black */
+    cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, .9);
+    cairo_stroke_preserve (cr);
+
+    /* stroke with "minor" pen in translucent yellow */
+    cairo_save (cr);
+    cairo_identity_matrix (cr);
+    cairo_set_line_width (cr, minor_width);
+    cairo_set_source_rgba (cr, 1.0, 1.0, 0.0, .9);
+    cairo_stroke_preserve (cr);
+    cairo_restore (cr);
+
+    /* stroke with hairline in black */
+    cairo_save (cr);
+    cairo_identity_matrix (cr);
+    cairo_set_line_width (cr, 1);
+    cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
+    cairo_stroke_preserve (cr);
+    cairo_restore (cr);
+
+    cairo_new_path (cr);
+}
diff --git a/doc/tutorial/src/twin.c b/doc/tutorial/src/twin.c
new file mode 100644 (file)
index 0000000..14347ba
--- /dev/null
@@ -0,0 +1,39 @@
+/**
+ * Put this file in cairo/doc/tutorial/src and type "make"
+ */
+
+#define WIDTH 1350
+#define HEIGHT 900
+
+#include "cairo-tutorial.h"
+
+
+static void
+draw (cairo_t *cr, int width, int height)
+{
+    int i, j, h;
+    unsigned char s[2] = {0, 0};
+
+    /* clear background */
+    cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); /* white */
+    cairo_paint (cr);
+
+    cairo_set_source_rgb (cr, 0, 0, 0);
+    cairo_select_font_face (cr,
+                           "@cairo:",
+                           CAIRO_FONT_SLANT_NORMAL,
+                           CAIRO_FONT_WEIGHT_NORMAL);
+
+    h = 2;
+    for (i = 8; i < 48; i >= 24 ? i+=3 : i++) {
+       cairo_set_font_size (cr, i);
+       for (j = 33; j < 128; j++) {
+           if (j == 33 || (j == 80 && i > 24)) {
+               h += i + 2;
+               cairo_move_to (cr, 10, h);
+           }
+           s[0] = j;
+           cairo_show_text (cr, (const char *) s);
+       }
+    }
+}
diff --git a/perf/.gitignore b/perf/.gitignore
new file mode 100644 (file)
index 0000000..3ac12bb
--- /dev/null
@@ -0,0 +1,29 @@
+TAGS
+tags
+cairo-analyse-trace
+cairo-perf
+cairo-perf-micro
+cairo-perf-print
+cairo-perf-trace
+cairo-perf-compare-backends
+cairo-perf-diff-files
+cairo-perf-graph-files
+cairo-traces
+valgrind-log
+callgrind.out.*
+index.html
+*.png
+*.perf
+*.o
+*.gcda
+*.gcno
+*.exe
+*.manifest
+*.obj
+*.ilk
+*.suo
+*.lib
+*.pdb
+*~
+.*.sw?
+*.data
diff --git a/perf/COPYING b/perf/COPYING
new file mode 100644 (file)
index 0000000..17754cf
--- /dev/null
@@ -0,0 +1,5 @@
+Cairo is free software.
+
+These tests are mainly available under a liberal MIT license to simplify
+any use of the code for reference purposes. Please check the opening comment
+of each file for copyright and licensing information.
diff --git a/perf/Makefile.am b/perf/Makefile.am
new file mode 100644 (file)
index 0000000..92f0dfc
--- /dev/null
@@ -0,0 +1,153 @@
+include $(top_srcdir)/build/Makefile.am.common
+
+include $(top_srcdir)/perf/Makefile.sources
+
+AM_CPPFLAGS =                                  \
+       -I$(srcdir)                             \
+       -I$(top_srcdir)/boilerplate             \
+       -I$(top_srcdir)/src                     \
+       -I$(top_srcdir)/util/cairo-missing      \
+       -I$(top_srcdir)/util/cairo-script       \
+       -I$(top_builddir)/src                   \
+       $(CAIRO_CFLAGS)
+
+AM_LDFLAGS = $(CAIRO_LDFLAGS)
+
+SUBDIRS = micro
+
+noinst_PROGRAMS = \
+       cairo-analyse-trace \
+       cairo-perf-trace \
+       cairo-perf-micro \
+       $(NULL)
+
+EXTRA_PROGRAMS += \
+       cairo-analyse-trace \
+       cairo-perf-micro \
+       cairo-perf-trace \
+       cairo-perf-diff-files \
+       cairo-perf-print \
+       cairo-perf-chart \
+       cairo-perf-compare-backends \
+       cairo-perf-graph-files \
+       $(NULL)
+EXTRA_DIST += cairo-perf-diff COPYING
+EXTRA_LTLIBRARIES += libcairoperf.la
+
+LDADD = libcairoperf.la \
+       $(top_builddir)/boilerplate/libcairoboilerplate.la \
+       $(top_builddir)/src/libcairo.la
+
+cairo_perf_micro_SOURCES = $(cairo_perf_micro_sources)
+cairo_perf_micro_LDADD = \
+       $(top_builddir)/perf/micro/libcairo-perf-micro.la \
+       $(LDADD)
+cairo_perf_micro_DEPENDENCIES = \
+       $(top_builddir)/perf/micro/libcairo-perf-micro.la \
+       $(LDADD)
+
+libcairoperf_la_SOURCES = \
+       $(libcairoperf_sources) \
+       $(libcairoperf_external_sources) \
+       $(libcairoperf_headers) \
+       $(NULL)
+
+cairo_analyse_trace_SOURCES = \
+       $(cairo_analyse_trace_sources)  \
+       $(cairo_analyse_trace_external_sources)
+cairo_analyse_trace_LDADD =            \
+       $(top_builddir)/util/cairo-script/libcairo-script-interpreter.la \
+       $(top_builddir)/util/cairo-missing/libcairo-missing.la \
+       $(LDADD)
+cairo_analyse_trace_DEPENDENCIES = \
+       $(top_builddir)/util/cairo-script/libcairo-script-interpreter.la \
+       $(top_builddir)/util/cairo-missing/libcairo-missing.la \
+       $(LDADD)
+
+cairo_perf_trace_SOURCES = \
+       $(cairo_perf_trace_sources)     \
+       $(cairo_perf_trace_external_sources)
+cairo_perf_trace_LDADD =               \
+       $(top_builddir)/util/cairo-script/libcairo-script-interpreter.la \
+       $(top_builddir)/util/cairo-missing/libcairo-missing.la \
+       $(LDADD)
+cairo_perf_trace_DEPENDENCIES = \
+       $(top_builddir)/util/cairo-script/libcairo-script-interpreter.la \
+       $(top_builddir)/util/cairo-missing/libcairo-missing.la \
+       $(LDADD)
+
+cairo_perf_diff_files_SOURCES =        $(cairo_perf_diff_files_sources)
+cairo_perf_print_SOURCES = $(cairo_perf_print_sources)
+cairo_perf_chart_SOURCES = $(cairo_perf_chart_sources)
+cairo_perf_compare_backends_SOURCES = $(cairo_perf_compare_backends_sources)
+
+cairo_perf_graph_files_SOURCES = \
+       $(cairo_perf_graph_files_sources)       \
+       $(cairo_perf_graph_files_headers)
+cairo_perf_graph_files_CFLAGS = @gtk_CFLAGS@
+cairo_perf_graph_files_LDADD = @gtk_LIBS@ $(LDADD)
+
+# Install rules to rebuild the libraries and add explicit dependencies
+$(top_builddir)/perf/micro/libcairo-perf-micro.la:
+       cd $(top_builddir)/perf/micro && $(MAKE) $(AM_MAKEFLAGS) libcairo-perf-micro.la
+
+$(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
+
+
+# Do a funny transition of CAIRO_TEST_TARGET through TARGETS such that
+# one can limit tested targets both through CAIRO_TEST_TARGET env var
+# and TARGETS make var on the command line.  Same for the rest.
+TARGETS = $(CAIRO_TEST_TARGET)
+TARGETS_EXCLUDE = $(CAIRO_TEST_TARGET_EXCLUDE)
+ITERS = $(CAIRO_PERF_ITERATIONS)
+
+CAIRO_PERF_ENVIRONMENT = CAIRO_PERF_ITERATIONS="$(ITERS)" CAIRO_TEST_TARGET="$(TARGETS)" CAIRO_TEST_TARGET_EXCLUDE="$(TARGETS_EXCLUDE)"
+
+perf: cairo-perf-micro$(EXEEXT) cairo-perf-trace$(EXEEXT)
+       -$(CAIRO_PERF_ENVIRONMENT) ./cairo-perf-micro$(EXEEXT)
+       -$(CAIRO_PERF_ENVIRONMENT) ./cairo-perf-trace$(EXEEXT)
+
+html-local: index.html
+
+perf-tag.html : cairo-perf-micro${EXEEXT}
+       $(CAIRO_PERF_ENVIRONMENT) ./cairo-perf-diff -t -h $@ `git describe --abbrev=0` HEAD
+perf-commit.html : cairo-perf-micro${EXEEXT}
+       $(CAIRO_PERF_ENVIRONMENT) ./cairo-perf-diff -t -h $@ HEAD
+
+# Summarise changes in index.html, with details in links
+index.html: perf-tag.html perf-commit.html
+       echo "<html><head><title>Performance Changes</title></head><body>Against <a href=\"perf-tag.html\">"`git describe --abbrev=0`"</a><br><a href=\"perf-commit.html\">Latest commit</a></body>" > $@
+
+EXTRA_VALGRIND_FLAGS = $(CAIRO_EXTRA_VALGRIND_FLAGS)
+VALGRIND_MEMCHECK_FLAGS = \
+       --tool=memcheck \
+       --suppressions=$(top_srcdir)/test/.valgrind-suppressions \
+       --leak-check=yes --show-reachable=yes
+VALGRIND_CALLGRIND_FLAGS = \
+       --tool=callgrind
+CLEANFILES += \
+       valgrind-log \
+       callgrind.out.* \
+       index.html
+
+perf-valgrind:
+       $(MAKE) $(AM_MAKEFLAGS) perf \
+           $(top_builddir)/libtool --mode=execute \
+           valgrind $(VALGRIND_MEMCHECK_FLAGS) $(EXTRA_VALGRIND_FLAGS)' \
+           | tee valgrind-log
+
+perf-callgrind:
+       $(MAKE) $(AM_MAKEFLAGS) perf \
+           $(top_builddir)/libtool --mode=execute \
+           valgrind $(VALGRIND_CALLGRIND_FLAGS) $(EXTRA_VALGRIND_FLAGS)'
+
+.PHONY: perf perf-valgrind perf-callgrind
+
+EXTRA_DIST += Makefile.win32
diff --git a/perf/Makefile.sources b/perf/Makefile.sources
new file mode 100644 (file)
index 0000000..1fcf148
--- /dev/null
@@ -0,0 +1,38 @@
+libcairoperf_sources = \
+       cairo-perf.c            \
+       cairo-perf-report.c     \
+       cairo-stats.c           \
+       $(NULL)
+
+libcairoperf_external_sources = ../src/cairo-time.c
+
+libcairoperf_headers = \
+       cairo-perf.h   \
+       cairo-stats.h  \
+       $(NULL)
+
+cairo_analyse_trace_sources = cairo-analyse-trace.c
+cairo_analyse_trace_external_sources = ../src/cairo-error.c
+
+cairo_perf_trace_sources = cairo-perf-trace.c
+cairo_perf_trace_external_sources = \
+       ../src/cairo-error.c \
+       ../src/cairo-hash.c \
+       $(NULL)
+
+cairo_perf_micro_sources = cairo-perf-micro.c
+
+cairo_perf_diff_files_sources =        cairo-perf-diff-files.c
+
+cairo_perf_print_sources = cairo-perf-print.c
+
+cairo_perf_chart_sources = cairo-perf-chart.c
+
+cairo_perf_compare_backends_sources = cairo-perf-compare-backends.c
+
+cairo_perf_graph_files_sources =       \
+       cairo-perf-graph-files.c        \
+       cairo-perf-graph-widget.c       \
+       $(NULL)
+
+cairo_perf_graph_files_headers = cairo-perf-graph.h
diff --git a/perf/Makefile.win32 b/perf/Makefile.win32
new file mode 100644 (file)
index 0000000..084abf0
--- /dev/null
@@ -0,0 +1,78 @@
+top_srcdir = ..
+include $(top_srcdir)/build/Makefile.win32.common
+include $(top_srcdir)/perf/Makefile.sources
+
+CFLAGS += -I$(top_srcdir)/boilerplate -I$(top_srcdir)/util/cairo-script/
+
+PERF_LIBS = \
+         $(CFG)/libcairoperf.lib       \
+         $(top_builddir)/boilerplate/$(CFG)/boiler.lib \
+         $(top_builddir)/src/$(CFG)/cairo-static.lib   \
+         $(NULL)
+
+PERF_EXES = \
+         $(CFG)/cairo-perf-trace.exe   \
+         $(CFG)/cairo-perf-micro.exe   \
+         $(CFG)/cairo-perf-diff-files.exe      \
+         $(CFG)/cairo-perf-print.exe   \
+         $(CFG)/cairo-perf-chart.exe   \
+         $(CFG)/cairo-perf-compare-backends.exe        \
+         $(NULL)
+
+all: inform $(PERF_EXES)
+
+perf: inform $(CFG)/cairo-perf-micro.exe
+       ./$(CFG)/cairo-perf-micro.exe
+
+
+libcairoperf_OBJECTS = $(patsubst %.c, $(CFG)/%-static.obj, $(libcairoperf_sources))
+
+$(CFG)/libcairoperf.lib: $(libcairoperf_OBJECTS)
+       @$(AR) $(CAIRO_ARFLAGS) -OUT:$@ $(libcairoperf_OBJECTS)
+
+cairo_perf_trace_OBJECTS = \
+       $(patsubst %.c, $(CFG)/%-static.obj, $(cairo_perf_trace_sources))               \
+       $(top_builddir)/util/cairo-script/$(CFG)/libcairo-script-interpreter.lib        \
+       $(NULL)
+
+cairo_perf_micro_OBJECTS = \
+       $(patsubst %.c, $(CFG)/%-static.obj, $(cairo_perf_micro_sources))       \
+       ./micro/$(CFG)/libcairo-perf-micro.lib                                  \
+       $(NULL)
+
+cairo_perf_diff_files_OBJECTS = $(patsubst %.c, $(CFG)/%-static.obj, $(cairo_perf_diff_files_sources))
+cairo_perf_print_OBJECTS = $(patsubst %.c, $(CFG)/%-static.obj, $(cairo_perf_print_sources))
+cairo_perf_chart_OBJECTS = $(patsubst %.c, $(CFG)/%-static.obj, $(cairo_perf_chart_sources))
+cairo_perf_compare_backends_OBJECTS = $(patsubst %.c, $(CFG)/%-static.obj, $(cairo_perf_compare_backends_sources))
+
+
+$(CFG)/cairo-perf-trace.exe: $(cairo_perf_trace_OBJECTS) $(PERF_LIBS)
+       @$(LD) $(CAIRO_LDFLAGS) -OUT:$@ $(cairo_perf_trace_OBJECTS) $(PERF_LIBS) $(CAIRO_LIBS)
+
+$(CFG)/cairo-perf-micro.exe: $(cairo_perf_micro_OBJECTS) $(PERF_LIBS)
+       @$(LD) $(CAIRO_LDFLAGS) -OUT:$@ $(cairo_perf_micro_OBJECTS) $(PERF_LIBS) $(CAIRO_LIBS)
+
+$(CFG)/cairo-perf-diff-files.exe: $(cairo_perf_diff_files_OBJECTS) $(PERF_LIBS)
+       @$(LD) $(CAIRO_LDFLAGS) -OUT:$@ $(cairo_perf_diff_files_OBJECTS) $(PERF_LIBS) $(CAIRO_LIBS)
+
+$(CFG)/cairo-perf-print.exe: $(cairo_perf_print_OBJECTS) $(PERF_LIBS)
+       @$(LD) $(CAIRO_LDFLAGS) -OUT:$@ $(cairo_perf_print_OBJECTS) $(PERF_LIBS) $(CAIRO_LIBS)
+
+$(CFG)/cairo-perf-chart.exe: $(cairo_perf_chart_OBJECTS) $(PERF_LIBS)
+       @$(LD) $(CAIRO_LDFLAGS) -OUT:$@ $(cairo_perf_chart_OBJECTS) $(PERF_LIBS) $(CAIRO_LIBS)
+
+$(CFG)/cairo-perf-compare-backends.exe: $(cairo_perf_compare_backends_OBJECTS) $(PERF_LIBS)
+       @$(LD) $(CAIRO_LDFLAGS) -OUT:$@ $(cairo_perf_compare_backends_OBJECTS) $(PERF_LIBS) $(CAIRO_LIBS)
+
+
+./micro/$(CFG)/libcairo-perf-micro.lib:
+       $(MAKE) -C micro -f Makefile.win32
+
+$(top_builddir)/src/$(CFG)/cairo-static.lib:
+       $(MAKE) -C $(top_srcdir)/src -f Makefile.win32
+
+$(top_builddir)/boilerplate/$(CFG)/boiler.lib:
+       $(MAKE) -C $(top_srcdir)/boilerplate -f Makefile.win32
+
+$(top_builddir)/util/cairo-script/$(CFG)/libcairo-script-interpreter.lib:
+       $(MAKE) -C $(top_srcdir)/util/cairo-script -f Makefile.win32
diff --git a/perf/README b/perf/README
new file mode 100644 (file)
index 0000000..42a6148
--- /dev/null
@@ -0,0 +1,288 @@
+This is cairo's performance test suite.
+
+One of the simplest ways to run the performance suite is:
+
+    make perf
+
+which will give a report of the speed of each individual test. See
+more details on other options for running the suite below.
+
+Running the cairo performance suite
+-----------------------------------
+The performance suite is composed of two types of tests, micro- and
+macro-benchmarks. The micro-benchmarks are a series of hand-written,
+short, synthetic tests that measure the speed of doing a simple
+operation such as painting a surface or showing glyphs. These aim to
+give very good feedback on whether a performance related patch is
+successful without causing any performance degradations elsewhere. The
+second type of benchmark consists of replaying a cairo-trace from a
+large application during typical usage. These aim to give an overall
+feel as to whether cairo is faster for everyday use.
+
+Running the micro-benchmarks
+----------------------------
+The micro-benchmarks are compiled into a single executable called
+cairo-perf-micro, which is what "make perf" executes. Some
+examples of running it:
+
+    # Report on all tests with default number of iterations:
+    ./cairo-perf-micro
+
+    # Report on 100 iterations of all gradient tests:
+    ./cairo-perf-micro -i 100 gradient
+
+    # Generate raw results for 10 iterations into cairo.perf
+    ./cairo-perf-micro -r -i 10 > cairo.perf
+    # Append 10 more iterations of the paint test
+    ./cairo-perf-micro -r -i 10 paint >> cairo.perf
+
+Raw results aren't useful for reading directly, but are quite useful
+when using cairo-perf-diff to compare separate runs (see more
+below). The advantage of using the raw mode is that test runs can be
+generated incrementally and appended to existing reports.
+
+Running the macro-benchmarks
+----------------------------
+The macro-benchmarks are run by a single program called
+cairo-perf-trace, which is also executed by "make perf".
+cairo-perf-trace loops over the series of traces stored beneath
+cairo-traces/. cairo-perf-trace produces the same output and takes the
+same arguments as cairo-perf-micro.  Some examples of running it:
+
+    # Report on all tests with default number of iterations:
+    ./cairo-perf-trace
+
+    # Report on 100 iterations of all firefox tests:
+    ./cairo-perf-trace -i 100 firefox
+
+    # Generate raw results for 10 iterations into cairo.perf
+    ./cairo-perf-trace -r -i 10 > cairo.perf
+    # Append 10 more iterations of the poppler tests
+    ./cairo-perf-trace -r -i 10 poppler >> cairo.perf
+
+Generating comparisons of separate runs
+---------------------------------------
+It's often useful to generate a chart showing the comparison of two
+separate runs of the cairo performance suite, (for example, after
+applying a patch intended to improve cairo's performance). The
+cairo-perf-diff script can be used to compare two report files
+generated by cairo-perf.
+
+Again, by way of example:
+
+    # Show performance changes from cairo-orig.perf to cairo-patched.perf
+    ./cairo-perf-diff cairo-orig.perf cairo-patched.perf
+
+This will work whether the data files were generate in raw mode (with
+cairo-perf -r) or cooked, (cairo-perf without -r).
+
+Finally, in its most powerful mode, cairo-perf-diff accepts two git
+revisions and will do all the work of checking each revision out,
+building it, running cairo-perf for each revision, and finally
+generating the report. Obviously, this mode only works if you are
+using cairo within a git repository, (and not from a tar file). Using
+this mode is as simple as passing the git revisions to be compared to
+cairo-perf-diff:
+
+    # Compare cairo 1.2.6 to cairo 1.4.0
+    ./cairo-perf-diff 1.2.6 1.4.0
+
+    # Measure the impact of the latest commit
+    ./cairo-perf-diff HEAD~1 HEAD
+
+As a convenience, this common desire to measure a single commit is
+supported by passing a single revision to cairo-perf-diff, in which
+case it will compare it to the immediately preceeding commit. So for
+example:
+
+    # Measure the impact of the latest commit
+    ./cairo-perf-diff HEAD
+
+    # Measure the impact of an arbitrary commit by SHA-1
+    ./cairo-perf-diff aa883123d2af90
+
+Also, when passing git revisions to cairo-perf-diff like this, it will
+automatically cache results and re-use them rather than re-running
+cairo-perf over and over on the same versions. This means that if you
+ask for a report that you've generated in the past, cairo-perf-diff
+should return it immediately.
+
+Now, sometimes it is desirable to generate more iterations rather than
+re-using cached results. In this case, the -f flag can be used to
+force cairo-perf-diff to generate additional results in addition to
+what has been cached:
+
+    # Measure the impact of latest commit (force more measurement)
+    ./cairo-perf-diff -f
+
+And finally, the -f mode is most useful in conjunction with the --
+option to cairo-perf-diff which allows you to pass options to the
+underlying cairo-perf runs. This allows you to restrict the additonal
+test runs to a limited subset of the tests.
+
+For example, a frequently used trick is to first generate a chart with
+a very small number of iterations for all tests:
+
+    ./cairo-perf-diff HEAD
+
+Then, if any of the results look suspicious, (say there's a slowdown
+reported in the text tests, but you think the text test shouldn't be
+affected), then you can force more iterations to be tested for only
+those tests:
+
+    ./cairo-perf-diff -f HEAD -- text
+
+Generating comparisons of different backends
+--------------------------------------------
+An alternate question that is often asked is, "how does the speed of one
+backend compare to another?". cairo-perf-compare-backends can read files
+generated by cairo-perf and produces a comparison of the backends for every
+test.
+
+Again, by way of example:
+
+    # Show relative performance of the backends
+    ./cairo-perf-compare-backends cairo.perf
+
+This will work whether the data files were generate in raw mode (with
+cairo-perf -r) or cooked, (cairo-perf without -r).
+
+
+Creating a new performance test
+-------------------------------
+This is where we could use everybody's help. If you have encountered a
+sequence of cairo operations that are slower than you would like, then
+please provide a performance test. Writing a test is very simple, it
+requires you to write only a small C file with a couple of functions,
+one of which exercises the cairo calls of interest.
+
+Here is the basic structure of a performance test file:
+
+    /* Copyright © 2006 Kind Cairo User
+     *
+     * ... Licensing information here ...
+     * Please copy the MIT blurb as in other tests
+     */
+
+    #include "cairo-perf.h"
+
+    static cairo_time_t
+    do_my_new_test (cairo_t *cr, int width, int height)
+    {
+       cairo_perf_timer_start ();
+
+       /* Make the cairo calls to be measured */
+
+       cairo_perf_timer_stop ();
+
+       return cairo_perf_timer_elapsed ();
+    }
+
+    void
+    my_new_test (cairo_perf_t *perf, cairo_t *cr, int width, int height)
+    {
+       /* First do any setup for which the execution time should not
+        * be measured. For example, this might include loading
+        * images from disk, creating patterns, etc. */
+
+       /* Then launch the actual performance testing. */
+       cairo_perf_run (perf, "my_new_test", do_my_new_test);
+
+       /* Finally, perform any cleanup from the setup above. */
+    }
+
+That's really all there is to writing a new test. The first function
+above is the one that does the real work and returns a timing
+number. The second function is the one that will be called by the
+performance test rig (see below for how to accomplish that), and
+allows for multiple performance cases to be written in one file,
+(simply call cairo_perf_run once for each case, passing the
+appropriate callback function to each).
+
+We go through this dance of indirectly calling your own function
+through cairo_perf_run so that cairo_perf_run can call your function
+many times and measure statistical properties over the many runs.
+
+Finally, to fully integrate your new test case you just need to add
+your new test to three different lists. (TODO: We should set this up
+better so that the lists are maintained automatically---computed from
+the list of files in cairo/perf, for example). Here's what needs to be
+added:
+
+ 1. Makefile.am: Add the new file name to the cairo_perf_SOURCES list
+
+ 2. cairo-perf.h: Add a new CAIRO_PERF_DECL line with the name of your
+    function, (my_new_test in the example above)
+
+ 3. cairo-perf-micro.c: Add a new row to the list at the end of the
+    file. A typical entry would look like:
+
+       { my_new_test, 16, 64 }
+
+    The last two numbers are a minimum and a maximum image size at
+    which your test should be exercised. If these values are the same,
+    then only that size will be used. If they are different, then
+    intermediate sizes will be used by doubling. So in the example
+    above, three tests would be performed at sizes of 16x16, 32x32 and
+    64x64.
+
+
+How to record new traces
+-----------------------
+Using cairo-trace you can record the exact sequence of graphic operations
+made by an application and replay them later. These traces can then be
+used by cairo-perf-trace to benchmark the various backends and patches.
+
+To record a trace:
+$ cairo-trace --no-mark-dirty --no-callers $APPLICATION [$ARGV]
+
+--no-mark-dirty is useful for applications that are paranoid about
+surfaces being modified by external plugins outside of their control, the
+prime example here is firefox.
+--no-callers disables the symbolic caller lookup and so speeds tracing
+(dramatically for large c++ programs) and similarly speeds up the replay
+as the files are much smaller.
+
+The output file will be called $APPLICATION.$PID.trace, the actual path
+written to will be displayed on the terminal.
+
+Alternatively you can use:
+$ cairo-trace --profile $APPLICATION [$ARGV]
+which automatically passes --no-mark-dirty and --no-callers and compresses
+the resultant trace using LZMA. To use the trace with cairo-perf-trace you
+will first need to decompress it.
+
+Then to use cairo-perf-trace:
+$ ./cairo-perf-trace $APPLICATION.$PID.trace
+
+Alternatively you can put the trace into perf/cairo-traces, or set
+CAIRO_TRACE_DIR to point to your trace directory, and the trace will be
+included in the performance tests.
+
+If you record an interesting trace, please consider sharing it by compressing
+it, LZMA preferred, and posting a link to cairo@cairographics.org, or by
+uploading it to git.cairographics.org/cairo-traces.
+
+
+How to run cairo-perf-diff on WINDOWS
+-------------------------------------
+This section explains the specifics of running cairo-perf-diff under
+win32 plateforms. It assumes that you have installed a UNIX-like shell
+environment such as MSYS (distributed as part of MinGW).
+
+ 1. From your Mingw32 window, be sure to have all of your MSVC environ-
+    ment variables set up for proper compilation using 'make'
+
+ 2. Add the %GitBaseDir%/Git/bin path to your environment, replacing the 
+    %GitBaseDir% by whatever directory your Git version is installed to.
+
+ 3. Comment out the "UNSET CDPATH" line in the git-sh-setup script 
+    (located inside the ...Git/bin directory) by putting a "#" at the 
+    beginning of the line.
+
+you should be ready to go !
+
+From your mingw32 window, go to your cairo/perf directory and run the 
+cairo-perf-diff script with the right arguments.
+
+Thanks for your contributions and have fun with cairo!
diff --git a/perf/cairo-analyse-trace.c b/perf/cairo-analyse-trace.c
new file mode 100644 (file)
index 0000000..ac2a4e7
--- /dev/null
@@ -0,0 +1,594 @@
+/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/*
+ * Copyright © 2006 Mozilla Corporation
+ * Copyright © 2006 Red Hat, Inc.
+ * Copyright © 2009 Chris Wilson
+ * Copyright © 2011 Intel Corporation
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * the authors not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. The authors make no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE AUTHORS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Authors: Vladimir Vukicevic <vladimir@pobox.com>
+ *         Carl Worth <cworth@cworth.org>
+ *         Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#define _GNU_SOURCE 1  /* for sched_getaffinity() and getline() */
+
+#include "../cairo-version.h" /* for the real version */
+
+#include "cairo-perf.h"
+#include "cairo-stats.h"
+
+#include "cairo-boilerplate-getopt.h"
+#include <cairo-script-interpreter.h>
+#include "cairo-missing.h"
+
+/* rudely reuse bits of the library... */
+#include "../src/cairo-error-private.h"
+
+/* For basename */
+#ifdef HAVE_LIBGEN_H
+#include <libgen.h>
+#endif
+#include <ctype.h> /* isspace() */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#ifdef _MSC_VER
+#include "dirent-win32.h"
+
+static char *
+basename_no_ext (char *path)
+{
+    static char name[_MAX_FNAME + 1];
+
+    _splitpath (path, NULL, NULL, name, NULL);
+
+    name[_MAX_FNAME] = '\0';
+
+    return name;
+}
+
+
+#else
+#include <dirent.h>
+
+static char *
+basename_no_ext (char *path)
+{
+    char *dot, *name;
+
+    name = basename (path);
+
+    dot = strchr (name, '.');
+    if (dot)
+       *dot = '\0';
+
+    return name;
+}
+
+#endif
+
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <signal.h>
+
+#if HAVE_FCFINI
+#include <fontconfig/fontconfig.h>
+#endif
+
+struct trace {
+    const cairo_boilerplate_target_t *target;
+    void            *closure;
+    cairo_surface_t *surface;
+};
+
+cairo_bool_t
+cairo_perf_can_run (cairo_perf_t *perf,
+                   const char   *name,
+                   cairo_bool_t *is_explicit)
+{
+    unsigned int i;
+    char *copy, *dot;
+    cairo_bool_t ret;
+
+    if (is_explicit)
+       *is_explicit = FALSE;
+
+    if (perf->exact_names) {
+       if (is_explicit)
+           *is_explicit = TRUE;
+       return TRUE;
+    }
+
+    if (perf->num_names == 0 && perf->num_exclude_names == 0)
+       return TRUE;
+
+    copy = xstrdup (name);
+    dot = strchr (copy, '.');
+    if (dot != NULL)
+       *dot = '\0';
+
+    if (perf->num_names) {
+       ret = TRUE;
+       for (i = 0; i < perf->num_names; i++)
+           if (strstr (copy, perf->names[i])) {
+               if (is_explicit)
+                   *is_explicit = strcmp (copy, perf->names[i]) == 0;
+               goto check_exclude;
+           }
+
+       ret = FALSE;
+       goto done;
+    }
+
+check_exclude:
+    if (perf->num_exclude_names) {
+       ret = FALSE;
+       for (i = 0; i < perf->num_exclude_names; i++)
+           if (strstr (copy, perf->exclude_names[i])) {
+               if (is_explicit)
+                   *is_explicit = strcmp (copy, perf->exclude_names[i]) == 0;
+               goto done;
+           }
+
+       ret = TRUE;
+       goto done;
+    }
+
+done:
+    free (copy);
+
+    return ret;
+}
+
+static cairo_surface_t *
+surface_create (void            *closure,
+               cairo_content_t  content,
+               double            width,
+               double            height,
+               long              uid)
+{
+    struct trace *args = closure;
+    return cairo_surface_create_similar (args->surface, content, width, height);
+}
+
+static int user_interrupt;
+
+static void
+interrupt (int sig)
+{
+    if (user_interrupt) {
+       signal (sig, SIG_DFL);
+       raise (sig);
+    }
+
+    user_interrupt = 1;
+}
+
+static void
+describe (cairo_perf_t *perf,
+          void *closure)
+{
+    char *description = NULL;
+
+    if (perf->has_described_backend)
+           return;
+    perf->has_described_backend = TRUE;
+
+    if (perf->target->describe)
+        description = perf->target->describe (closure);
+
+    if (description == NULL)
+        return;
+
+    free (description);
+}
+
+static void
+execute (cairo_perf_t   *perf,
+        struct trace    *args,
+        const char      *trace)
+{
+    char *trace_cpy, *name;
+    const cairo_script_interpreter_hooks_t hooks = {
+       .closure = args,
+       .surface_create = surface_create,
+    };
+
+    trace_cpy = xstrdup (trace);
+    name = basename_no_ext (trace_cpy);
+
+    if (perf->list_only) {
+       printf ("%s\n", name);
+       free (trace_cpy);
+       return;
+    }
+
+    describe (perf, args->closure);
+
+    {
+       cairo_script_interpreter_t *csi;
+       cairo_status_t status;
+       unsigned int line_no;
+
+       csi = cairo_script_interpreter_create ();
+       cairo_script_interpreter_install_hooks (csi, &hooks);
+
+       cairo_script_interpreter_run (csi, trace);
+
+       cairo_script_interpreter_finish (csi);
+
+       line_no = cairo_script_interpreter_get_line_number (csi);
+       status = cairo_script_interpreter_destroy (csi);
+       if (status) {
+           /* XXXX cairo_status_to_string is just wrong! */
+           fprintf (stderr, "Error during replay, line %d: %s\n",
+                    line_no, cairo_status_to_string (status));
+       }
+    }
+    user_interrupt = 0;
+
+    free (trace_cpy);
+}
+
+static void
+usage (const char *argv0)
+{
+    fprintf (stderr,
+"Usage: %s [-l] [-r] [-v] [-i iterations] [test-names ... | traces ...]\n"
+"       %s -l\n"
+"\n"
+"Run the cairo performance test suite over the given tests (all by default)\n"
+"The command-line arguments are interpreted as follows:\n"
+"\n"
+"  -v  verbose\n"
+"  -x   exclude; specify a file to read a list of traces to exclude\n"
+"  -l  list only; just list selected test case names without executing\n"
+"\n"
+"If test names are given they are used as sub-string matches so a command\n"
+"such as \"cairo-perf-trace firefox\" can be used to run all firefox traces.\n"
+"Alternatively, you can specify a list of filenames to execute.\n",
+            argv0, argv0);
+}
+
+static cairo_bool_t
+read_excludes (cairo_perf_t *perf,
+              const char   *filename)
+{
+    FILE *file;
+    char *line = NULL;
+    size_t line_size = 0;
+    char *s, *t;
+
+    file = fopen (filename, "r");
+    if (file == NULL)
+       return FALSE;
+
+    while (getline (&line, &line_size, file) != -1) {
+       /* terminate the line at a comment marker '#' */
+       s = strchr (line, '#');
+       if (s)
+           *s = '\0';
+
+       /* whitespace delimits */
+       s = line;
+       while (*s != '\0' && isspace (*s))
+           s++;
+
+       t = s;
+       while (*t != '\0' && ! isspace (*t))
+           t++;
+
+       if (s != t) {
+           int i = perf->num_exclude_names;
+           perf->exclude_names = xrealloc (perf->exclude_names,
+                                           sizeof (char *) * (i+1));
+           perf->exclude_names[i] = strndup (s, t-s);
+           perf->num_exclude_names++;
+       }
+    }
+    free (line);
+
+    fclose (file);
+
+    return TRUE;
+}
+
+static void
+parse_options (cairo_perf_t *perf,
+              int           argc,
+              char         *argv[])
+{
+    char *end;
+    int c;
+
+    perf->list_only = FALSE;
+    perf->names = NULL;
+    perf->num_names = 0;
+    perf->exclude_names = NULL;
+    perf->num_exclude_names = 0;
+
+    while (1) {
+       c = _cairo_getopt (argc, argv, "i:x:lrvc");
+       if (c == -1)
+           break;
+
+       switch (c) {
+       case 'i':
+           perf->exact_iterations = TRUE;
+           perf->iterations = strtoul (optarg, &end, 10);
+           if (*end != '\0') {
+               fprintf (stderr, "Invalid argument for -i (not an integer): %s\n",
+                        optarg);
+               exit (1);
+           }
+           break;
+       case 'l':
+           perf->list_only = TRUE;
+           break;
+       case 'x':
+           if (! read_excludes (perf, optarg)) {
+               fprintf (stderr, "Invalid argument for -x (not readable file): %s\n",
+                        optarg);
+               exit (1);
+           }
+           break;
+       default:
+           fprintf (stderr, "Internal error: unhandled option: %c\n", c);
+           /* fall-through */
+       case '?':
+           usage (argv[0]);
+           exit (1);
+       }
+    }
+
+    if (optind < argc) {
+       perf->names = &argv[optind];
+       perf->num_names = argc - optind;
+    }
+}
+
+static void
+cairo_perf_fini (cairo_perf_t *perf)
+{
+    cairo_boilerplate_free_targets (perf->targets);
+    cairo_boilerplate_fini ();
+
+    cairo_debug_reset_static_data ();
+#if HAVE_FCFINI
+    FcFini ();
+#endif
+}
+
+static cairo_bool_t
+have_trace_filenames (cairo_perf_t *perf)
+{
+    unsigned int i;
+
+    if (perf->num_names == 0)
+       return FALSE;
+
+#if HAVE_UNISTD_H
+    for (i = 0; i < perf->num_names; i++)
+       if (access (perf->names[i], R_OK) == 0)
+           return TRUE;
+#endif
+
+    return FALSE;
+}
+
+static cairo_status_t
+print (void *closure, const unsigned char *data, unsigned int length)
+{
+    fwrite (data, length, 1, closure);
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+cairo_perf_trace (cairo_perf_t                    *perf,
+                 const cairo_boilerplate_target_t *target,
+                 const char                       *trace)
+{
+    struct trace args;
+    cairo_surface_t *real;
+
+    args.target = target;
+    real = target->create_surface (NULL,
+                                  CAIRO_CONTENT_COLOR_ALPHA,
+                                  1, 1,
+                                  1, 1,
+                                  CAIRO_BOILERPLATE_MODE_PERF,
+                                  0,
+                                  &args.closure);
+    args.surface =
+           cairo_surface_create_observer (real,
+                                          CAIRO_SURFACE_OBSERVER_RECORD_OPERATIONS);
+    cairo_surface_destroy (real);
+    if (cairo_surface_status (args.surface)) {
+       fprintf (stderr,
+                "Error: Failed to create target surface: %s\n",
+                target->name);
+       return;
+    }
+
+    printf ("Observing '%s'...", trace);
+    fflush (stdout);
+
+    execute (perf, &args, trace);
+
+    printf ("\n");
+    cairo_device_observer_print (cairo_surface_get_device (args.surface),
+                                print, stdout);
+    fflush (stdout);
+
+    cairo_surface_destroy (args.surface);
+
+    if (target->cleanup)
+       target->cleanup (args.closure);
+}
+
+static void
+warn_no_traces (const char *message,
+               const char *trace_dir)
+{
+    fprintf (stderr,
+"Error: %s '%s'.\n"
+"Have you cloned the cairo-traces repository and uncompressed the traces?\n"
+"  git clone git://anongit.freedesktop.org/cairo-traces\n"
+"  cd cairo-traces && make\n"
+"Or set the env.var CAIRO_TRACE_DIR to point to your traces?\n",
+           message, trace_dir);
+}
+
+static int
+cairo_perf_trace_dir (cairo_perf_t                    *perf,
+                     const cairo_boilerplate_target_t *target,
+                     const char                       *dirname)
+{
+    DIR *dir;
+    struct dirent *de;
+    int num_traces = 0;
+    cairo_bool_t force;
+    cairo_bool_t is_explicit;
+
+    dir = opendir (dirname);
+    if (dir == NULL)
+       return 0;
+
+    force = FALSE;
+    if (cairo_perf_can_run (perf, dirname, &is_explicit))
+       force = is_explicit;
+
+    while ((de = readdir (dir)) != NULL) {
+       char *trace;
+       struct stat st;
+
+       if (de->d_name[0] == '.')
+           continue;
+
+       xasprintf (&trace, "%s/%s", dirname, de->d_name);
+       if (stat (trace, &st) != 0)
+           goto next;
+
+       if (S_ISDIR(st.st_mode)) {
+           num_traces += cairo_perf_trace_dir (perf, target, trace);
+       } else {
+           const char *dot;
+
+           dot = strrchr (de->d_name, '.');
+           if (dot == NULL)
+               goto next;
+           if (strcmp (dot, ".trace"))
+               goto next;
+
+           num_traces++;
+           if (!force && ! cairo_perf_can_run (perf, de->d_name, NULL))
+               goto next;
+
+           cairo_perf_trace (perf, target, trace);
+       }
+next:
+       free (trace);
+
+    }
+    closedir (dir);
+
+    return num_traces;
+}
+
+int
+main (int   argc,
+      char *argv[])
+{
+    cairo_perf_t perf;
+    const char *trace_dir = "cairo-traces:/usr/src/cairo-traces:/usr/share/cairo-traces";
+    unsigned int n;
+    int i;
+
+    parse_options (&perf, argc, argv);
+
+    signal (SIGINT, interrupt);
+
+    if (getenv ("CAIRO_TRACE_DIR") != NULL)
+       trace_dir = getenv ("CAIRO_TRACE_DIR");
+
+    perf.targets = cairo_boilerplate_get_targets (&perf.num_targets, NULL);
+
+    /* do we have a list of filenames? */
+    perf.exact_names = have_trace_filenames (&perf);
+
+    for (i = 0; i < perf.num_targets; i++) {
+       const cairo_boilerplate_target_t *target = perf.targets[i];
+
+       if (! perf.list_only && ! target->is_measurable)
+           continue;
+
+       perf.target = target;
+       perf.has_described_backend = FALSE;
+
+       if (perf.exact_names) {
+           for (n = 0; n < perf.num_names; n++) {
+               struct stat st;
+
+               if (stat (perf.names[n], &st) == 0) {
+                   if (S_ISDIR (st.st_mode)) {
+                       cairo_perf_trace_dir (&perf, target, perf.names[n]);
+                   } else
+                       cairo_perf_trace (&perf, target, perf.names[n]);
+               }
+           }
+       } else {
+           int num_traces = 0;
+           const char *dir;
+
+           dir = trace_dir;
+           do {
+               char buf[1024];
+               const char *end = strchr (dir, ':');
+               if (end != NULL) {
+                   memcpy (buf, dir, end-dir);
+                   buf[end-dir] = '\0';
+                   end++;
+
+                   dir = buf;
+               }
+
+               num_traces += cairo_perf_trace_dir (&perf, target, dir);
+               dir = end;
+           } while (dir != NULL);
+
+           if (num_traces == 0) {
+               warn_no_traces ("Found no traces in", trace_dir);
+               return 1;
+           }
+       }
+
+       if (perf.list_only)
+           break;
+    }
+
+    cairo_perf_fini (&perf);
+
+    return 0;
+}
diff --git a/perf/cairo-perf-chart.c b/perf/cairo-perf-chart.c
new file mode 100644 (file)
index 0000000..70ff353
--- /dev/null
@@ -0,0 +1,898 @@
+/*
+ * Copyright © 2006 Red Hat, Inc.
+ * Copyright © 2009 Chris Wilson
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of the
+ * copyright holders not be used in advertising or publicity
+ * pertaining to distribution of the software without specific,
+ * written prior permission. The copyright holders make no
+ * representations about the suitability of this software for any
+ * purpose.  It is provided "as is" without express or implied
+ * warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ * SOFTWARE.
+ *
+ * Authors: Carl Worth <cworth@cworth.org>
+ *         Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-perf.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include <math.h>
+#include <assert.h>
+
+struct chart {
+    cairo_perf_report_t *reports;
+    const char **names;
+
+    cairo_t *cr;
+    int width, height;
+    int num_tests, num_reports;
+    double min_value, max_value;
+
+    cairo_bool_t use_html;
+    cairo_bool_t relative;
+};
+struct color {
+    double red, green, blue;
+};
+
+#define FONT_SIZE 12
+#define PAD (FONT_SIZE/2+1)
+
+#define MAX(a,b) ((a) > (b) ? (a) : (b))
+
+static double
+to_factor (double x)
+{
+#if 1
+    if (x > 1.)
+       return (x-1) * 100.;
+    else
+       return (1. - 1./x) * 100.;
+#else
+    return log (x);
+#endif
+}
+
+static int
+_double_cmp (const void *_a,
+            const void *_b)
+{
+    const double *a = _a;
+    const double *b = _b;
+
+    if (*a > *b)
+       return 1;
+    if (*a < *b)
+       return -1;
+    return 0;
+}
+
+static void
+trim_outliers (double *values,
+              int     num_values,
+              double *min,
+              double *max)
+{
+    double q1, q3, iqr;
+    double outlier_min, outlier_max;
+    int i;
+
+    /* First, identify any outliers, using the definition of "mild
+     * outliers" from:
+     *
+     *         http://en.wikipedia.org/wiki/Outliers
+     *
+     * Which is that outliers are any values less than Q1 - 1.5 * IQR
+     * or greater than Q3 + 1.5 * IQR where Q1 and Q3 are the first
+     * and third quartiles and IQR is the inter-quartile range (Q3 -
+     * Q1).
+     */
+    qsort (values, num_values,
+          sizeof (double), _double_cmp);
+
+    q1         = values[1*num_values / 4];
+    q3         = values[3*num_values / 4];
+
+    iqr = q3 - q1;
+
+    outlier_min = q1 - 1.5 * iqr;
+    outlier_max = q3 + 1.5 * iqr;
+
+    i = 0;
+    while (i < num_values && values[i] < outlier_min)
+       i++;
+    if (i == num_values)
+       return;
+
+    *min = values[i];
+
+    while (i < num_values && values[i] <= outlier_max)
+       i++;
+
+    *max = values[i-1];
+}
+
+static void
+find_ranges (struct chart *chart)
+{
+    test_report_t **tests, *min_test;
+    double *values;
+    int num_values, size_values;
+    double min = 0, max = 0;
+    double test_time;
+    int seen_non_null;
+    int num_tests = 0;
+    int i;
+
+    num_values = 0;
+    size_values = 64;
+    values = xmalloc (size_values * sizeof (double));
+
+    tests = xmalloc (chart->num_reports * sizeof (test_report_t *));
+    for (i = 0; i < chart->num_reports; i++)
+       tests[i] = chart->reports[i].tests;
+
+    while (1) {
+       /* We expect iterations values of 0 when multiple raw reports
+        * for the same test have been condensed into the stats of the
+        * first. So we just skip these later reports that have no
+        * stats. */
+       seen_non_null = 0;
+       for (i = 0; i < chart->num_reports; i++) {
+           while (tests[i]->name && tests[i]->stats.iterations == 0)
+               tests[i]++;
+           if (tests[i]->name)
+               seen_non_null++;
+       }
+       if (! seen_non_null)
+           break;
+
+       num_tests++;
+
+       /* Find the minimum of all current tests, (we have to do this
+        * in case some reports don't have a particular test). */
+       for (i = 0; i < chart->num_reports; i++) {
+           if (tests[i]->name) {
+               min_test = tests[i];
+               break;
+           }
+       }
+       for (++i; i < chart->num_reports; i++) {
+           if (tests[i]->name && test_report_cmp_name (tests[i], min_test) < 0)
+               min_test = tests[i];
+       }
+
+       test_time = 0;
+       for (i = 0; i < chart->num_reports; i++) {
+           double report_time = HUGE_VAL;
+
+           while (tests[i]->name &&
+                  test_report_cmp_name (tests[i], min_test) == 0)
+           {
+               double time = tests[i]->stats.min_ticks;
+               if (time < report_time) {
+                   time /= tests[i]->stats.ticks_per_ms;
+                   if (time < report_time)
+                       report_time = time;
+               }
+               tests[i]++;
+           }
+
+           if (report_time != HUGE_VAL) {
+               if (test_time == 0)
+                   test_time = report_time;
+
+               if (chart->relative) {
+                   double v = to_factor (test_time / report_time);
+                   if (num_values == size_values) {
+                       size_values *= 2;
+                       values = xrealloc (values,
+                                          size_values * sizeof (double));
+                   }
+                   values[num_values++] = v;
+                   if (v < min)
+                       min = v;
+                   if (v > max)
+                       max = v;
+               } else {
+                   if (report_time < min)
+                       min = report_time;
+                   if (report_time > max)
+                       max = report_time;
+               }
+           }
+       }
+    }
+
+    if (chart->relative)
+       trim_outliers (values, num_values, &min, &max);
+    chart->min_value = min;
+    chart->max_value = max;
+    chart->num_tests = num_tests;
+
+    free (values);
+    free (tests);
+}
+
+#define SET_COLOR(C, R, G, B) (C)->red = (R), (C)->green = (G), (C)->blue = (B)
+static void
+hsv_to_rgb (double       h,
+           double        s,
+           double        v,
+           struct color *color)
+{
+    double m, n, f;
+    int i;
+
+    while (h < 0)
+       h += 6.;
+    while (h > 6.)
+       h -= 6.;
+
+    if (s < 0.)
+       s = 0.;
+    if (s > 1.)
+       s = 1.;
+
+    if (v < 0.)
+       v = 0.;
+    if (v > 1.)
+       v = 1.;
+
+    i = floor (h);
+    f = h - i;
+    if ((i & 1) == 0)
+       f = 1 - f;
+
+    m = v * (1 - s);
+    n = v * (1 - s * f);
+    switch(i){
+    default:
+    case 6:
+    case 0: SET_COLOR (color, v, n, m); break;
+    case 1: SET_COLOR (color, n, v, m); break;
+    case 2: SET_COLOR (color, m, v, n); break;
+    case 3: SET_COLOR (color, m, n, v); break;
+    case 4: SET_COLOR (color, n, m, v); break;
+    case 5: SET_COLOR (color, v, m, n); break;
+    }
+}
+
+static void set_report_color (struct chart *chart, int report)
+{
+    struct color color;
+
+    hsv_to_rgb (6. / chart->num_reports * report, .7, .7, &color);
+    cairo_set_source_rgb (chart->cr, color.red, color.green, color.blue);
+}
+
+static void
+test_background (struct chart *c,
+                int           test)
+{
+    double dx, x;
+
+    dx = c->width / (double) c->num_tests;
+    x = dx * test;
+
+    if (test & 1)
+       cairo_set_source_rgba (c->cr, .2, .2, .2, .2);
+    else
+       cairo_set_source_rgba (c->cr, .8, .8, .8, .2);
+
+    cairo_rectangle (c->cr, floor (x), 0,
+                    floor (dx + x) - floor (x), c->height);
+    cairo_fill (c->cr);
+}
+
+static void
+add_chart (struct chart *c,
+          int           test,
+          int           report,
+          double        value)
+{
+    double dx, dy, x;
+
+    if (fabs (value) < 0.1)
+       return;
+
+    set_report_color (c, report);
+
+    if (c->relative) {
+       cairo_text_extents_t extents;
+       cairo_bool_t show_label;
+       char buf[80];
+       double y;
+
+       dy = (c->height/2. - PAD) / MAX (-c->min_value, c->max_value);
+       /* the first report is always skipped, as it is used as the baseline */
+       dx = c->width / (double) (c->num_tests * c->num_reports);
+       x = dx * (c->num_reports * test + report - .5);
+
+       cairo_rectangle (c->cr,
+                        floor (x), c->height / 2.,
+                        floor (x + dx) - floor (x),
+                        ceil (-dy*value - c->height/2.) + c->height/2.);
+       cairo_fill (c->cr);
+
+       cairo_save (c->cr);
+       cairo_set_font_size (c->cr, dx - 2);
+
+       if (value < 0) {
+           sprintf (buf, "%.1f", value/100 - 1);
+       } else {
+           sprintf (buf, "%.1f", value/100 + 1);
+       }
+       cairo_text_extents (c->cr, buf, &extents);
+
+       /* will it be clipped? */
+       y = -dy * value;
+       if (y < -c->height/2) {
+           y = -c->height/2;
+       } else if (y > c->height/2) {
+           y = c->height/2;
+       }
+
+       cairo_translate (c->cr,
+                        floor (x) + (floor (x + dx) - floor (x))/2,
+                        floor (y) + c->height/2.);
+       cairo_rotate (c->cr, -M_PI/2);
+       if (y < 0) {
+           cairo_move_to (c->cr, -extents.x_bearing -extents.width - 4, -extents.y_bearing/2);
+           show_label = y < -extents.width - 6;
+       } else {
+           cairo_move_to (c->cr, 2, -extents.y_bearing/2);
+           show_label = y > extents.width + 6;
+       }
+
+       cairo_set_source_rgb (c->cr, .95, .95, .95);
+       if (show_label)
+           cairo_show_text (c->cr, buf);
+       cairo_restore (c->cr);
+    } else {
+       dy = (c->height - PAD) / c->max_value;
+       dx = c->width / (double) (c->num_tests * (c->num_reports+1));
+       x = dx * ((c->num_reports+1) * test + report + .5);
+
+       cairo_rectangle (c->cr,
+                        floor (x), c->height,
+                        floor (x + dx) - floor (x),
+                        floor (c->height - dy*value) - c->height);
+       cairo_fill (c->cr);
+    }
+}
+
+static void
+add_label (struct chart *c,
+          int           test,
+          const char   *label)
+{
+    cairo_text_extents_t extents;
+    double dx, x;
+
+    cairo_save (c->cr);
+    dx = c->width / (double) c->num_tests;
+    if (dx / 2 - PAD < 6)
+       return;
+    cairo_set_font_size (c->cr, dx / 2 - PAD);
+    cairo_text_extents (c->cr, label, &extents);
+
+    x = (test + .5) * dx;
+    cairo_translate (c->cr, x, PAD / 2);
+    cairo_rotate (c->cr, -M_PI/2);
+
+    cairo_set_source_rgb (c->cr, .5, .5, .5);
+    cairo_move_to (c->cr, -extents.width, -extents.y_bearing/2);
+    cairo_show_text (c->cr, label);
+    cairo_restore (c->cr);
+}
+
+static void
+add_base_line (struct chart *c)
+{
+    double y;
+
+    cairo_save (c->cr);
+    cairo_set_line_width (c->cr, 2.);
+    if (c->relative) {
+       y = c->height / 2.;
+    } else {
+       y = c->height;
+    }
+    cairo_move_to (c->cr, 0, y);
+    cairo_line_to (c->cr, c->width, y);
+    cairo_set_source_rgb (c->cr, 1, 1, 1);
+    cairo_stroke (c->cr);
+    cairo_restore (c->cr);
+}
+
+static void
+add_absolute_lines (struct chart *c)
+{
+    const double dashes[] = { 2, 4 };
+    const double vlog_steps[] = { 10, 5, 4, 3, 2, 1, .5, .4, .3, .2, .1};
+    double v, y, dy;
+    unsigned int i;
+    char buf[80];
+    cairo_text_extents_t extents;
+
+    v = c->max_value / 2.;
+
+    for (i = 0; i < sizeof (vlog_steps) / sizeof (vlog_steps[0]); i++) {
+       double vlog = log (v) / log (vlog_steps[i]);
+       if (vlog > 1) {
+           v = pow (vlog_steps[i], floor (vlog));
+           goto done;
+       }
+    }
+    return;
+done:
+
+    dy = (c->height - PAD) / c->max_value;
+
+    cairo_save (c->cr);
+    cairo_set_line_width (c->cr, 1.);
+    cairo_set_dash (c->cr, dashes, sizeof (dashes) / sizeof (dashes[0]), 0);
+
+    i = 0;
+    do {
+       y = c->height - ++i * v * dy;
+       if (y < PAD)
+           break;
+
+       cairo_set_font_size (c->cr, 8);
+
+       sprintf (buf, "%.0fs", i*v/1000);
+       cairo_text_extents (c->cr, buf, &extents);
+
+       cairo_set_source_rgba (c->cr, .75, 0, 0, .95);
+       cairo_move_to (c->cr, -extents.x_bearing, floor (y) - (extents.height/2 + extents.y_bearing) + .5);
+       cairo_show_text (c->cr, buf);
+
+       cairo_move_to (c->cr, c->width-extents.width+extents.x_bearing, floor (y) - (extents.height/2 + extents.y_bearing) + .5);
+       cairo_show_text (c->cr, buf);
+
+       cairo_set_source_rgba (c->cr, .75, 0, 0, .5);
+       cairo_move_to (c->cr,
+                      ceil (extents.width + extents.x_bearing + 2),
+                      floor (y) + .5);
+       cairo_line_to (c->cr,
+                      floor (c->width - (extents.width + extents.x_bearing + 2)),
+                      floor (y) + .5);
+       cairo_stroke (c->cr);
+    } while (1);
+
+    cairo_restore (c->cr);
+}
+
+static void
+add_relative_lines (struct chart *c)
+{
+    const double dashes[] = { 2, 4 };
+    const double v_steps[] = { 10, 5, 1, .5, .1, .05, .01};
+    const int precision_steps[] = { 0, 0, 0, 1, 1, 2, 2};
+    int precision;
+    double v, y, dy, mid;
+    unsigned int i;
+    char buf[80];
+    cairo_text_extents_t extents;
+
+    v = MAX (-c->min_value, c->max_value) / 200;
+
+    for (i = 0; i < sizeof (v_steps) / sizeof (v_steps[0]); i++) {
+       if (v > v_steps[i]) {
+           v = v_steps[i];
+           precision = precision_steps[i];
+           goto done;
+       }
+    }
+    return;
+done:
+
+    mid = c->height/2.;
+    dy = (mid - PAD) / MAX (-c->min_value, c->max_value);
+
+    cairo_save (c->cr);
+    cairo_set_line_width (c->cr, 1.);
+    cairo_set_dash (c->cr, dashes, sizeof (dashes) / sizeof (dashes[0]), 0);
+    cairo_set_font_size (c->cr, 8);
+
+    i = 0;
+    do {
+       y = ++i * v * dy * 100;
+       if (y > mid)
+           break;
+
+       sprintf (buf, "%.*fx", precision, i*v + 1);
+       cairo_text_extents (c->cr, buf, &extents);
+
+       cairo_set_source_rgba (c->cr, .75, 0, 0, .95);
+       cairo_move_to (c->cr, -extents.x_bearing, floor (mid + y) - (extents.height/2 + extents.y_bearing)+ .5);
+       cairo_show_text (c->cr, buf);
+
+       cairo_move_to (c->cr, c->width-extents.width+extents.x_bearing, floor (mid + y) - (extents.height/2 + extents.y_bearing)+ .5);
+       cairo_show_text (c->cr, buf);
+
+       cairo_set_source_rgba (c->cr, 0, .75, 0, .95);
+       cairo_move_to (c->cr, -extents.x_bearing, ceil (mid - y) - (extents.height/2 + extents.y_bearing)+ .5);
+       cairo_show_text (c->cr, buf);
+
+       cairo_move_to (c->cr, c->width-extents.width+extents.x_bearing, ceil (mid - y) - (extents.height/2 + extents.y_bearing)+ .5);
+       cairo_show_text (c->cr, buf);
+
+       /* trim the dashes to no obscure the labels */
+       cairo_set_source_rgba (c->cr, .75, 0, 0, .5);
+       cairo_move_to (c->cr,
+                      ceil (extents.width + extents.x_bearing + 2),
+                      floor (mid + y) + .5);
+       cairo_line_to (c->cr,
+                      floor (c->width - (extents.width + extents.x_bearing + 2)),
+                      floor (mid + y) + .5);
+       cairo_stroke (c->cr);
+
+       cairo_set_source_rgba (c->cr, 0, .75, 0, .5);
+       cairo_move_to (c->cr,
+                      ceil (extents.width + extents.x_bearing + 2),
+                      ceil (mid - y) + .5);
+       cairo_line_to (c->cr,
+                      floor (c->width - (extents.width + extents.x_bearing + 2)),
+                      ceil (mid - y) + .5);
+       cairo_stroke (c->cr);
+
+    } while (1);
+
+    cairo_restore (c->cr);
+}
+
+static void
+add_slower_faster_guide (struct chart *c)
+{
+    cairo_text_extents_t extents;
+
+    cairo_save (c->cr);
+
+    cairo_set_font_size (c->cr, FONT_SIZE);
+
+    cairo_text_extents (c->cr, "FASTER", &extents);
+    cairo_set_source_rgba (c->cr, 0, .75, 0, .5);
+    cairo_move_to (c->cr,
+                  c->width/4. - extents.width/2. + extents.x_bearing,
+                  1 - extents.y_bearing);
+    cairo_show_text (c->cr, "FASTER");
+    cairo_move_to (c->cr,
+                  3*c->width/4. - extents.width/2. + extents.x_bearing,
+                  1 - extents.y_bearing);
+    cairo_show_text (c->cr, "FASTER");
+
+    cairo_text_extents (c->cr, "SLOWER", &extents);
+    cairo_set_source_rgba (c->cr, .75, 0, 0, .5);
+    cairo_move_to (c->cr,
+                  c->width/4. - extents.width/2. + extents.x_bearing,
+                  c->height - 1);
+    cairo_show_text (c->cr, "SLOWER");
+    cairo_move_to (c->cr,
+                  3*c->width/4. - extents.width/2. + extents.x_bearing,
+                  c->height - 1);
+    cairo_show_text (c->cr, "SLOWER");
+
+    cairo_restore (c->cr);
+}
+
+static void
+cairo_perf_reports_compare (struct chart *chart,
+                           cairo_bool_t  print)
+{
+    test_report_t **tests, *min_test;
+    double test_time, best_time;
+    int num_test = 0;
+    int seen_non_null;
+    int i;
+
+    tests = xmalloc (chart->num_reports * sizeof (test_report_t *));
+    for (i = 0; i < chart->num_reports; i++)
+       tests[i] = chart->reports[i].tests;
+
+    if (print) {
+       if (chart->use_html) {
+           printf ("<table style=\"text-align:right\" cellspacing=\"4\">\n");
+           printf ("<tr><td></td>");
+           for (i = 0; i < chart->num_reports; i++) {
+               printf ("<td>%s</td>", chart->names[i] ? chart->names[i] : "");
+           }
+           printf ("</tr>\n");
+       }
+    }
+
+    while (1) {
+       /* We expect iterations values of 0 when multiple raw reports
+        * for the same test have been condensed into the stats of the
+        * first. So we just skip these later reports that have no
+        * stats. */
+       seen_non_null = 0;
+       for (i = 0; i < chart->num_reports; i++) {
+           while (tests[i]->name && tests[i]->stats.iterations == 0)
+               tests[i]++;
+           if (tests[i]->name)
+               seen_non_null++;
+       }
+       if (! seen_non_null)
+           break;
+
+       /* Find the minimum of all current tests, (we have to do this
+        * in case some reports don't have a particular test). */
+       for (i = 0; i < chart->num_reports; i++) {
+           if (tests[i]->name) {
+               min_test = tests[i];
+               break;
+           }
+       }
+       for (++i; i < chart->num_reports; i++) {
+           if (tests[i]->name && test_report_cmp_name (tests[i], min_test) < 0)
+               min_test = tests[i];
+       }
+
+       add_label (chart, num_test, min_test->name);
+       if (print) {
+           if (chart->use_html) {
+               printf ("<tr><td>%s</td>", min_test->name);
+           } else {
+               if (min_test->size) {
+                   printf ("%16s, size %4d:\n",
+                           min_test->name,
+                           min_test->size);
+               } else {
+                   printf ("%26s:",
+                           min_test->name);
+               }
+           }
+       }
+
+       test_time = 0;
+       best_time = HUGE_VAL;
+       for (i = 0; i < chart->num_reports; i++) {
+           test_report_t *initial = tests[i];
+           double report_time = HUGE_VAL;
+
+           while (tests[i]->name &&
+                  test_report_cmp_name (tests[i], min_test) == 0)
+           {
+               double time = tests[i]->stats.min_ticks;
+               if (time < report_time) {
+                   time /= tests[i]->stats.ticks_per_ms;
+                   if (time < report_time)
+                       report_time = time;
+               }
+               tests[i]++;
+           }
+
+           if (test_time == 0 && report_time != HUGE_VAL)
+               test_time = report_time;
+           if (report_time < best_time)
+               best_time = report_time;
+
+           tests[i] = initial;
+       }
+
+       for (i = 0; i < chart->num_reports; i++) {
+           double report_time = HUGE_VAL;
+
+           while (tests[i]->name &&
+                  test_report_cmp_name (tests[i], min_test) == 0)
+           {
+               double time = tests[i]->stats.min_ticks;
+               if (time > 0) {
+                   time /= tests[i]->stats.ticks_per_ms;
+                   if (time < report_time)
+                       report_time = time;
+               }
+               tests[i]++;
+           }
+
+           if (print) {
+               if (chart->use_html) {
+                   if (report_time < HUGE_VAL) {
+                       if (report_time / best_time < 1.01) {
+                           printf ("<td><strong>%.1f</strong></td>", report_time/1000);
+                       } else {
+                           printf ("<td>%.1f</td>", report_time/1000);
+                       }
+                   } else {
+                       printf ("<td></td>");
+                   }
+               } else {
+                   if (report_time < HUGE_VAL)
+                       printf (" %6.1f",  report_time/1000);
+                   else
+                       printf ("    ---");
+               }
+           }
+
+           if (report_time < HUGE_VAL) {
+               if (chart->relative) {
+                   add_chart (chart, num_test, i,
+                              to_factor (test_time / report_time));
+               } else {
+                   add_chart (chart, num_test, i, report_time);
+               }
+           }
+       }
+
+       if (print) {
+           if (chart->use_html) {
+               printf ("</tr>\n");
+           } else {
+               printf ("\n");
+           }
+       }
+
+       num_test++;
+    }
+    free (tests);
+
+    if (print) {
+       if (chart->use_html)
+           printf ("</table>\n");
+
+       printf ("\n");
+       for (i = 0; i < chart->num_reports; i++) {
+           if (chart->names[i]) {
+               printf ("[%s] %s\n",
+                       chart->names[i], chart->reports[i].configuration);
+           } else {
+               printf ("[%d] %s\n",
+                       i, chart->reports[i].configuration);
+           }
+       }
+    }
+}
+
+static void
+add_legend (struct chart *chart)
+{
+    cairo_text_extents_t extents;
+    const char *str;
+    int i, x, y;
+
+    cairo_set_font_size (chart->cr, FONT_SIZE);
+
+    x = PAD;
+    y = chart->height + PAD;
+    for (i = chart->relative; i < chart->num_reports; i++) {
+       str = chart->names[i] ?
+             chart->names[i] : chart->reports[i].configuration;
+
+       set_report_color (chart, i);
+
+       cairo_rectangle (chart->cr, x, y + 6, 8, 8);
+       cairo_fill (chart->cr);
+
+       cairo_set_source_rgb (chart->cr, 1, 1, 1);
+       cairo_move_to (chart->cr, x + 10, y + FONT_SIZE + PAD / 2.);
+       cairo_text_extents (chart->cr, str, &extents);
+       cairo_show_text (chart->cr, str);
+
+       x += 10 + 2 * PAD + ceil (extents.width);
+    }
+
+    if (chart->relative) {
+       char buf[80];
+
+       str = chart->names[0] ?
+             chart->names[0] : chart->reports[0].configuration;
+
+       sprintf (buf, "(relative to %s)", str);
+       cairo_text_extents (chart->cr, buf, &extents);
+
+       cairo_set_source_rgb (chart->cr, 1, 1, 1);
+       cairo_move_to (chart->cr,
+                      chart->width - 1 - extents.width,
+                      y + FONT_SIZE + PAD / 2.);
+       cairo_show_text (chart->cr, buf);
+    }
+}
+
+int
+main (int        argc,
+      const char *argv[])
+{
+    cairo_surface_t *surface;
+    struct chart chart;
+    test_report_t *t;
+    int i;
+
+    chart.use_html = 0;
+    chart.width = 640;
+    chart.height = 480;
+
+    chart.reports = xcalloc (argc-1, sizeof (cairo_perf_report_t));
+    chart.names = xcalloc (argc-1, sizeof (cairo_perf_report_t));
+
+    chart.num_reports = 0;
+    for (i = 1; i < argc; i++) {
+       if (strcmp (argv[i], "--html") == 0) {
+           chart.use_html = 1;
+       } else if (strncmp (argv[i], "--width=", 8) == 0) {
+           chart.width = atoi (argv[i] + 8);
+       } else if (strncmp (argv[i], "--height=", 9) == 0) {
+           chart.height = atoi (argv[i] + 9);
+       } else if (strcmp (argv[i], "--name") == 0) {
+           if (i + 1 < argc)
+               chart.names[chart.num_reports] = argv[++i];
+       } else if (strncmp (argv[i], "--name=", 7) == 0) {
+           chart.names[chart.num_reports] = argv[i] + 7;
+       } else {
+           cairo_perf_report_load (&chart.reports[chart.num_reports++],
+                                   argv[i],
+                                   test_report_cmp_name);
+       }
+    }
+
+    for (chart.relative = 0; chart.relative <= 1; chart.relative++) {
+       surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
+                                             chart.width,
+                                             chart.height + (FONT_SIZE + PAD) + 2*PAD);
+       chart.cr = cairo_create (surface);
+       cairo_surface_destroy (surface);
+
+       cairo_set_source_rgb (chart.cr, 0, 0, 0);
+       cairo_paint (chart.cr);
+
+       find_ranges (&chart);
+
+       for (i = 0; i < chart.num_tests; i++)
+           test_background (&chart, i);
+       if (chart.relative) {
+           add_relative_lines (&chart);
+           add_slower_faster_guide (&chart);
+       } else
+           add_absolute_lines (&chart);
+
+       cairo_save (chart.cr);
+       cairo_rectangle (chart.cr, 0, 0, chart.width, chart.height);
+       cairo_clip (chart.cr);
+       cairo_perf_reports_compare (&chart, !chart.relative);
+       cairo_restore (chart.cr);
+
+       add_base_line (&chart);
+       add_legend (&chart);
+
+       cairo_surface_write_to_png (cairo_get_target (chart.cr),
+                                   chart.relative ?
+                                   "cairo-perf-chart-relative.png" :
+                                   "cairo-perf-chart-absolute.png");
+       cairo_destroy (chart.cr);
+    }
+
+    /* Pointless memory cleanup, (would be a great place for talloc) */
+    for (i = 0; i < chart.num_reports; i++) {
+       for (t = chart.reports[i].tests; t->name; t++) {
+           free (t->samples);
+           free (t->backend);
+           free (t->name);
+       }
+       free (chart.reports[i].tests);
+       free (chart.reports[i].configuration);
+    }
+    free (chart.names);
+    free (chart.reports);
+
+    return 0;
+}
diff --git a/perf/cairo-perf-compare-backends.c b/perf/cairo-perf-compare-backends.c
new file mode 100644 (file)
index 0000000..2cbb24c
--- /dev/null
@@ -0,0 +1,399 @@
+/*
+ * Copyright © 2006 Red Hat, Inc.
+ * Copyright © 2009 Chris Wilson
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of the
+ * copyright holders not be used in advertising or publicity
+ * pertaining to distribution of the software without specific,
+ * written prior permission. The copyright holders make no
+ * representations about the suitability of this software for any
+ * purpose.  It is provided "as is" without express or implied
+ * warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ * SOFTWARE.
+ *
+ * Authors: Carl Worth <cworth@cworth.org>
+ *         Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-perf.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include <math.h>
+#include <assert.h>
+
+typedef struct _cairo_perf_report_options {
+    double min_change;
+    int use_utf;
+    int print_change_bars;
+} cairo_perf_report_options_t;
+
+typedef struct _cairo_perf_diff_files_args {
+    const char **filenames;
+    int num_filenames;
+    cairo_perf_report_options_t options;
+} cairo_perf_diff_files_args_t;
+
+static int
+test_diff_cmp (const void *a,
+              const void *b)
+{
+    const test_diff_t *a_diff = a;
+    const test_diff_t *b_diff = b;
+
+    /* Reverse sort by magnitude of change so larger changes come
+     * first */
+    if (a_diff->change > b_diff->change)
+       return -1;
+
+    if (a_diff->change < b_diff->change)
+       return 1;
+
+    return 0;
+}
+
+#define CHANGE_BAR_WIDTH 70
+static void
+print_change_bar (double change,
+                 double max_change,
+                 int    use_utf)
+{
+    int units_per_cell = ceil (max_change / CHANGE_BAR_WIDTH);
+    static char const *ascii_boxes[8] = {
+       "****","***" ,"***", "**",
+       "**",  "*",   "*",   ""
+    };
+    static char const *utf_boxes[8] = {
+       "█", "▉", "▊", "▋",
+       "▌", "▍", "▎", "▏"
+    };
+    char const **boxes = use_utf ? utf_boxes : ascii_boxes;
+
+    /* For a 1.0x speedup we want a zero-size bar to show "no
+     * change". */
+    change -= 1.0;
+
+    while (change > units_per_cell) {
+       printf ("%s", boxes[0]);
+       change -= units_per_cell;
+    }
+
+    change /= units_per_cell;
+
+    if (change > 7.5/8.0)
+       printf ("%s", boxes[0]);
+    else if (change > 6.5/8.0)
+       printf ("%s", boxes[1]);
+    else if (change > 5.5/8.0)
+       printf ("%s", boxes[2]);
+    else if (change > 4.5/8.0)
+       printf ("%s", boxes[3]);
+    else if (change > 3.5/8.0)
+       printf ("%s", boxes[4]);
+    else if (change > 2.5/8.0)
+       printf ("%s", boxes[5]);
+    else if (change > 1.5/8.0)
+       printf ("%s", boxes[6]);
+    else if (change > 0.5/8.0)
+       printf ("%s", boxes[7]);
+}
+
+static void
+test_diff_print (test_diff_t                *diff,
+                double                       max_change,
+                cairo_perf_report_options_t *options)
+{
+    int i;
+    double test_time;
+    double change;
+
+    if (diff->tests[0]->size != 0) {
+       printf ("(%s, size: %d)\n",
+               diff->tests[0]->name,
+               diff->tests[0]->size);
+    } else {
+       printf ("(%s)\n", diff->tests[0]->name);
+    }
+
+    for (i = 0; i < diff->num_tests; i++) {
+       test_time = diff->tests[i]->stats.min_ticks;
+       test_time /= diff->tests[i]->stats.ticks_per_ms;
+       change = diff->max / test_time;
+       printf ("%8s-%s-%s\t%6.2f: %5.2fx ",
+               diff->tests[i]->backend,
+               diff->tests[i]->content,
+               diff->tests[i]->configuration,
+               diff->tests[i]->stats.min_ticks / diff->tests[i]->stats.ticks_per_ms,
+               change);
+
+       if (options->print_change_bars)
+           print_change_bar (change, max_change, options->use_utf);
+       printf ("\n");
+    }
+
+    printf("\n");
+}
+
+#define MAX(a,b) ((a) > (b) ? (a) : (b))
+static void
+cairo_perf_reports_compare (cairo_perf_report_t        *reports,
+                           int                          num_reports,
+                           cairo_perf_report_options_t *options)
+{
+    int i;
+    test_report_t **tests, *min_test;
+    test_diff_t *diff, *diffs;
+    int num_diffs, max_diffs;
+    double max_change;
+    double test_time;
+    int seen_non_null;
+
+    tests = xmalloc (num_reports * sizeof (test_report_t *));
+
+    max_diffs = reports[0].tests_count;
+    for (i = 0; i < num_reports; i++) {
+       tests[i] = reports[i].tests;
+       if (reports[i].tests_count > max_diffs)
+           max_diffs = reports[i].tests_count;
+    }
+
+    diff = diffs = xmalloc (max_diffs * sizeof (test_diff_t));
+
+    num_diffs = 0;
+    while (1) {
+       int num_tests;
+
+       /* We expect iterations values of 0 when multiple raw reports
+        * for the same test have been condensed into the stats of the
+        * first. So we just skip these later reports that have no
+        * stats. */
+       seen_non_null = 0;
+       for (i = 0; i < num_reports; i++) {
+           while (tests[i]->name && tests[i]->stats.iterations == 0)
+               tests[i]++;
+           if (tests[i]->name)
+               seen_non_null++;
+       }
+       if (! seen_non_null)
+           break;
+
+       /* Find the minimum of all current tests, (we have to do this
+        * in case some reports don't have a particular test). */
+       for (i = 0; i < num_reports; i++) {
+           if (tests[i]->name) {
+               min_test = tests[i];
+               break;
+           }
+       }
+       for (++i; i < num_reports; i++) {
+           if (tests[i]->name && test_report_cmp_name (tests[i], min_test) < 0)
+               min_test = tests[i];
+       }
+
+       num_tests = 0;
+       for (i = 0; i < num_reports; i++) {
+           test_report_t *test;
+           int n = 0;
+
+           test = tests[i];
+           while (test[n].name &&
+                   test_report_cmp_name (&test[n], min_test) == 0)
+           {
+               n++;
+           }
+
+           num_tests += n;
+       }
+
+       /* For each report that has the current test, record it into
+        * the diff structure. */
+       diff->num_tests = 0;
+       diff->tests = xmalloc (num_tests * sizeof (test_diff_t));
+       for (i = 0; i < num_reports; i++) {
+           while (tests[i]->name &&
+                   test_report_cmp_name (tests[i], min_test) == 0)
+           {
+               test_time = tests[i]->stats.min_ticks;
+               if (test_time > 0) {
+                   test_time /= tests[i]->stats.ticks_per_ms;
+                   if (diff->num_tests == 0) {
+                       diff->min = test_time;
+                       diff->max = test_time;
+                   } else {
+                       if (test_time < diff->min)
+                           diff->min = test_time;
+                       if (test_time > diff->max)
+                           diff->max = test_time;
+                   }
+                   diff->tests[diff->num_tests++] = tests[i];
+               }
+               tests[i]++;
+           }
+       }
+       diff->change = diff->max / diff->min;
+
+       diff++;
+       num_diffs++;
+    }
+    if (num_diffs == 0)
+       goto DONE;
+
+    qsort (diffs, num_diffs, sizeof (test_diff_t), test_diff_cmp);
+
+    max_change = 1.0;
+    for (i = 0; i < num_diffs; i++) {
+       if (fabs (diffs[i].change) > max_change)
+           max_change = fabs (diffs[i].change);
+    }
+
+    for (i = 0; i < num_diffs; i++) {
+       diff = &diffs[i];
+
+       /* Discard as uninteresting a change which is less than the
+        * minimum change required, (default may be overridden on
+        * command-line). */
+       if (fabs (diff->change) - 1.0 < options->min_change)
+           continue;
+
+       test_diff_print (diff, max_change, options);
+    }
+
+    for (i = 0; i < num_diffs; i++)
+       free (diffs[i].tests);
+ DONE:
+    free (diffs);
+    free (tests);
+}
+
+static void
+usage (const char *argv0)
+{
+    char const *basename = strrchr(argv0, '/');
+    basename = basename ? basename+1 : argv0;
+    fprintf (stderr,
+            "Usage: %s [options] file [...]\n\n",
+            basename);
+    fprintf (stderr,
+            "Computes significant performance differences for cairo performance reports.\n"
+            "Each file should be the output of the cairo-perf program (or \"make perf\").\n"
+            "The following options are available:\n"
+            "\n"
+            "--no-utf    Use ascii stars instead of utf-8 change bars.\n"
+            "            Four stars are printed per factor of speedup.\n"
+            "\n"
+            "--no-bars   Don't display change bars at all.\n\n"
+            "\n"
+            "--use-ms    Use milliseconds to calculate differences.\n"
+            "            (instead of ticks which are hardware dependent)\n"
+            "\n"
+            "--min-change threshold[%%]\n"
+            "            Suppress all changes below the given threshold.\n"
+            "            The default threshold of 0.05 or 5%% ignores any\n"
+            "            speedup or slowdown of 1.05 or less. A threshold\n"
+            "            of 0 will cause all output to be reported.\n"
+       );
+    exit(1);
+}
+
+static void
+parse_args (int                           argc,
+           char const                   **argv,
+           cairo_perf_diff_files_args_t  *args)
+{
+    int i;
+
+    for (i = 1; i < argc; i++) {
+       if (strcmp (argv[i], "--no-utf") == 0) {
+           args->options.use_utf = 0;
+       }
+       else if (strcmp (argv[i], "--no-bars") == 0) {
+           args->options.print_change_bars = 0;
+       }
+       else if (strcmp (argv[i], "--min-change") == 0) {
+           char *end = NULL;
+           i++;
+           if (i >= argc)
+               usage (argv[0]);
+           args->options.min_change = strtod (argv[i], &end);
+           if (*end) {
+               if (*end == '%') {
+                   args->options.min_change /= 100;
+               } else {
+                   usage (argv[0]);
+               }
+           }
+       }
+       else {
+           args->num_filenames++;
+           args->filenames = xrealloc (args->filenames,
+                                       args->num_filenames * sizeof (char *));
+           args->filenames[args->num_filenames - 1] = argv[i];
+       }
+    }
+}
+
+int
+main (int        argc,
+      const char *argv[])
+{
+    cairo_perf_diff_files_args_t args = {
+       NULL,                   /* filenames */
+       0,                      /* num_filenames */
+       {
+           0.05,               /* min change */
+           1,                  /* use UTF-8? */
+           1,                  /* display change bars? */
+       }
+    };
+    cairo_perf_report_t *reports;
+    test_report_t *t;
+    int i;
+
+    parse_args (argc, argv, &args);
+
+    if (args.num_filenames) {
+       reports = xcalloc (args.num_filenames, sizeof (cairo_perf_report_t));
+       for (i = 0; i < args.num_filenames; i++) {
+           cairo_perf_report_load (&reports[i], args.filenames[i], i,
+                                   test_report_cmp_name);
+           printf ("loaded: %s, %d tests\n",
+                   args.filenames[i], reports[i].tests_count);
+       }
+    } else {
+       args.num_filenames = 1;
+       reports = xcalloc (args.num_filenames, sizeof (cairo_perf_report_t));
+       cairo_perf_report_load (&reports[0], NULL, 0, test_report_cmp_name);
+    }
+
+    cairo_perf_reports_compare (reports, args.num_filenames, &args.options);
+
+    /* Pointless memory cleanup, (would be a great place for talloc) */
+    free (args.filenames);
+    for (i = 0; i < args.num_filenames; i++) {
+       for (t = reports[i].tests; t->name; t++) {
+           free (t->samples);
+           free (t->backend);
+           free (t->name);
+       }
+       free (reports[i].tests);
+       free (reports[i].configuration);
+    }
+    free (reports);
+
+    return 0;
+}
diff --git a/perf/cairo-perf-diff b/perf/cairo-perf-diff
new file mode 100755 (executable)
index 0000000..5a4b7b3
--- /dev/null
@@ -0,0 +1,255 @@
+#!/bin/sh
+set -e
+
+usage() {
+    argv0=`basename $0`
+
+    cat >&2 << END
+Usage:
+For comparing files created my cairo-perf:
+
+       $argv0 old.perf new.perf
+
+For comparing (cached) performance of revisions:
+
+       $argv0 [OPTIONS] <revision> [-- cairo-perf options]
+       $argv0 [OPTIONS] <rev1> <rev2> [-- cairo-perf-options]
+
+If given a single revision, compares its results to that of its
+(first-parent) predecessor. Otherwise compares the two given revisions.
+The revisions can be any revision accepted by git. For example:
+
+       $argv0 HEAD             # Show impact of latest commit
+       $argv0 1.2.0 1.2.4      # Compare performance of 1.2.0 to 1.2.4
+
+Options:
+
+-f, --force
+       Forces cairo-perf-diff to re-run performance tests
+       even if cached performance data is available.
+
+-h, --html
+       With this option performance changes are summarized
+       as HTML table.
+
+-t, --trace
+       Compare performance using trace replays instead of
+       microbenchmarks.
+
+Additional options can be passed the child cairo-perf process
+by separating them with a double hyphen (--). For example, to
+examine what the impact of the latest change is on the stroke
+test you might use:
+
+       $argv0 HEAD -- stroke
+
+The performance results are cached in .perf next to the .git directory.
+
+Set CAIRO_AUTOGEN_OPTIONS to pass options to autogen for both
+builds.
+END
+
+    exit 1
+}
+
+benchmark="cairo-perf-micro"
+
+# First, pull off any known options
+while true; do
+    case $1 in
+        -f|--force) force_cairo_perf="true";;
+        -h|--html) html_output="$2"; shift ;;
+        -t|--trace) benchmark="cairo-perf-trace";;
+        *) break;;
+    esac
+
+    shift
+done
+
+# Then if anything is left that still looks like an option, (begins
+# with a dash), give usage to catch --help or any other -garbage
+if [ $# -eq 0 ] || [ "`echo "$1" | sed 's/^-//'`" != "$1" ]; then
+    usage
+fi
+
+# Finally, pick up the actual revision arguments
+if [ $# -eq 1 ] || [ "$2" = "--" ]; then
+    old="$1^"
+    new="$1"
+    shift 1
+else
+    old="$1"
+    new="$2"
+    shift 2
+fi
+
+# And post-finally, pass anything after -- on to cairo-perf
+CAIRO_PERF_OPTIONS="-r -i 10"
+if [ $# -gt 0 ]; then
+    if [ "$1" = "--" ]; then
+       shift 1
+       CAIRO_PERF_OPTIONS="$CAIRO_PERF_OPTIONS $@"
+    else
+       usage
+    fi
+fi
+
+git_setup() {
+    SUBDIRECTORY_OK='Yes'
+    . "$(git --exec-path)/git-sh-setup"
+    CAIRO_DIR=`dirname $GIT_DIR`
+    if [ "$CAIRO_DIR" = "." ]; then
+       CAIRO_DIR=`pwd`
+    fi
+    CAIRO_PERF_DIR=$CAIRO_DIR/.perf
+}
+
+rev2sha() {
+    rev=$1
+    git rev-parse --verify $rev || ( echo "Cannot resolve $rev as a git object" && exit 1 )
+}
+
+cpu_count() {
+    test -f /proc/cpuinfo &&
+    grep -c '^processor[[:blank:]]\+:' /proc/cpuinfo ||
+    echo 1
+}
+
+# We cache performance output based on a two-part name capturing the
+# current performance test suite and the library being tested. We
+# capture these as the tree object of the perf directory in HEAD and
+# the tree object of the src directory of the revision being tested.
+#
+# This way, whenever the performance suite is updated, cached output
+# from old versions of the suite are automatically invalidated. Also,
+# if a commit just changes things outside of the src tree, (say it
+# changes the "test" test suite, or README or configure.in, or
+# whatever), cairo-perf-diff will be smart enough to still use cached
+# results from a run with an equivalent src tree.
+rev2perf() {
+    rev=$1
+    sha=`rev2sha $rev`
+    src_tree_sha=`rev2sha $rev:src`
+    perf_tree_sha=`rev2sha HEAD:perf`
+    script_tree_sha=`rev2sha HEAD:util/cairo-script`
+    echo "$CAIRO_PERF_DIR/${sha}-${perf_tree_sha}-${script_tree_sha}-${src_tree_sha}.perf"
+}
+rev2perf_glob() {
+    rev=$1
+    src_tree_sha=`rev2sha $rev:src`
+    perf_tree_sha=`rev2sha HEAD:perf`
+    script_tree_sha=`rev2sha HEAD:util/cairo-script`
+    echo "$CAIRO_PERF_DIR/*-${perf_tree_sha}-${script_tree_sha}-${src_tree_sha}.perf"
+}
+
+build() {
+    build_dir=$1
+    sha=$2
+
+    if [ ! -d $build_dir ]; then
+       git clone -s $CAIRO_DIR $build_dir
+       (cd $build_dir; git checkout -b tmp-cairo-perf-diff $sha)
+    fi
+    cd $build_dir
+
+    git checkout tmp-cairo-perf-diff
+    git reset --hard $sha
+
+    if [ -z "$MAKEFLAGS" ]; then
+       CPU_COUNT=`cpu_count`
+        export MAKEFLAGS="-j`expr $CPU_COUNT + 1`"
+    fi
+
+    if [ ! -e Makefile ]; then
+       ./autogen.sh $CAIRO_AUTOGEN_OPTIONS
+    fi
+
+    for file in $boilerplate_files; do
+       rsync $CAIRO_DIR/$file boilerplate
+    done
+    for file in $perf_files; do
+       rsync $CAIRO_DIR/$file perf
+    done
+    for file in $script_files; do
+       rsync $CAIRO_DIR/$file util/cairo-script
+    done
+
+    make || (rm config.cache && make)
+    (cd boilerplate && make libcairoboilerplate.la)
+
+    cd perf
+    make ${benchmark}
+}
+
+# Usage: run_cairo_perf_if_not_cached <rev> <suffix>
+# The <rev> argument must be a valid git ref-spec that can
+# be resolved to a commit. The suffix is just something
+# unique so that build directories can be separated for
+# multiple calls to this function.
+run_cairo_perf_if_not_cached() {
+    rev=$1
+    build_dir="build-$2"
+
+    owd=`pwd`
+    sha=`rev2sha $rev`
+    perf=`rev2perf $rev`
+    glob=`rev2perf_glob $rev`
+    if [ -e $glob ] && [ "$force_cairo_perf" != "true" ]; then
+       return 0
+    fi
+    if [ ! -d $CAIRO_PERF_DIR ]; then
+       echo "Creating new perf cache in $CAIRO_PERF_DIR"
+       mkdir $CAIRO_PERF_DIR
+    fi
+
+    cd $CAIRO_DIR
+    boilerplate_files=`git ls-tree --name-only HEAD boilerplate/*`
+    perf_files=`git ls-tree --name-only HEAD perf/*`
+    script_files=`git ls-tree --name-only HEAD util/cairo-script/*`
+    cd $CAIRO_PERF_DIR
+
+    build $build_dir $sha || {
+       rm -rf $build_dir
+       build $build_dir $sha || exit 1
+    }
+
+    echo "Running \"cairo-perf $CAIRO_PERF_OPTIONS\" against $rev. Results will be cached in:"
+    { ./$benchmark $CAIRO_PERF_OPTIONS || echo "*** Performance test crashed"; } >> $perf
+
+    cd $owd
+}
+
+git_setup
+
+if [ -e ./cairo-perf-diff-files ]; then
+       bindir="."
+else
+       bindir=$CAIRO_DIR/perf
+
+       # Build cairo-perf-diff-files if not available
+       if [ ! -e $bindir/cairo-perf-diff-files ]; then
+           echo "Building cairo-perf-diff-files"
+           if [ "x$OS" = "xWindows_NT" ]; then
+               make -f Makefile.win32 -C $bindir cairo-perf-diff-files CFG=debug
+           else
+               make -C $bindir cairo-perf-diff-files
+           fi
+       fi
+fi
+
+if [ ! -e $old ]; then
+    run_cairo_perf_if_not_cached $old old
+    old=`rev2perf $old`
+fi
+
+if [ ! -e $new ]; then
+    run_cairo_perf_if_not_cached $new new
+    new=`rev2perf $new`
+fi
+
+if [ -z "$html_output" ]; then
+    $bindir/cairo-perf-diff-files $old $new
+else
+    $bindir/cairo-perf-diff-files $old $new |
+    $CAIRO_DIR/perf/make-html.py > $html_output
+fi
diff --git a/perf/cairo-perf-diff-files.c b/perf/cairo-perf-diff-files.c
new file mode 100644 (file)
index 0000000..0850927
--- /dev/null
@@ -0,0 +1,507 @@
+/*
+ * Copyright © 2006 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of the
+ * copyright holders not be used in advertising or publicity
+ * pertaining to distribution of the software without specific,
+ * written prior permission. The copyright holders make no
+ * representations about the suitability of this software for any
+ * purpose.  It is provided "as is" without express or implied
+ * warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ * SOFTWARE.
+ *
+ * Authors: Carl Worth <cworth@cworth.org>
+ */
+
+#include "cairo-perf.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include <math.h>
+#include <assert.h>
+
+typedef struct _cairo_perf_report_options {
+    double min_change;
+    int use_utf;
+    int print_change_bars;
+    int use_ticks;
+} cairo_perf_report_options_t;
+
+typedef struct _cairo_perf_diff_files_args {
+    const char **filenames;
+    int num_filenames;
+    cairo_perf_report_options_t options;
+} cairo_perf_diff_files_args_t;
+
+static int
+test_diff_cmp_speedup_before_slowdown (const void *a,
+                                      const void *b)
+{
+    const test_diff_t *a_diff = a;
+    const test_diff_t *b_diff = b;
+
+    /* First make all speedups come before all slowdowns. */
+    if (a_diff->change > 0 && b_diff->change < 0)
+       return -1;
+    if (a_diff->change < 0 && b_diff->change > 0)
+       return 1;
+
+    if (a_diff->change == b_diff->change)
+       return 0;
+
+    /* Large speedups come first. */
+    if (a_diff->change > 0) {
+           if (a_diff->change > b_diff->change)
+                   return -1;
+           else
+                   return 1;
+    }
+
+    /* Large slowdowns come last. */
+    if (a_diff->change < 0) {
+           if (a_diff->change < b_diff->change)
+                   return 1;
+           else
+                   return -1;
+    }
+
+    return 0;
+}
+
+static int
+test_diff_cmp (const void *a,
+              const void *b)
+{
+    const test_diff_t *a_diff = a;
+    const test_diff_t *b_diff = b;
+
+    /* Reverse sort by magnitude of change so larger changes come
+     * first */
+    if (a_diff->change > b_diff->change)
+       return -1;
+
+    if (a_diff->change < b_diff->change)
+       return 1;
+
+    return 0;
+}
+
+#define CHANGE_BAR_WIDTH 70
+static void
+print_change_bar (double change,
+                 double max_change,
+                 int    use_utf)
+{
+    int units_per_cell = ceil (max_change / CHANGE_BAR_WIDTH);
+    static char const *ascii_boxes[8] = {
+       "****","***" ,"***", "**",
+       "**",  "*",   "*",   ""
+    };
+    static char const *utf_boxes[8] = {
+       "█", "▉", "▊", "▋",
+       "▌", "▍", "▎", "▏"
+    };
+    char const **boxes = use_utf ? utf_boxes : ascii_boxes;
+
+    /* For a 1.0x speedup we want a zero-size bar to show "no
+     * change". */
+    change -= 1.0;
+
+    while (change > units_per_cell) {
+       printf ("%s", boxes[0]);
+       change -= units_per_cell;
+    }
+
+    change /= units_per_cell;
+
+    if (change > 7.5/8.0)
+       printf ("%s", boxes[0]);
+    else if (change > 6.5/8.0)
+       printf ("%s", boxes[1]);
+    else if (change > 5.5/8.0)
+       printf ("%s", boxes[2]);
+    else if (change > 4.5/8.0)
+       printf ("%s", boxes[3]);
+    else if (change > 3.5/8.0)
+       printf ("%s", boxes[4]);
+    else if (change > 2.5/8.0)
+       printf ("%s", boxes[5]);
+    else if (change > 1.5/8.0)
+       printf ("%s", boxes[6]);
+    else if (change > 0.5/8.0)
+       printf ("%s", boxes[7]);
+
+    printf ("\n");
+}
+
+static void
+test_diff_print_binary (test_diff_t                *diff,
+                       double                       max_change,
+                       cairo_perf_report_options_t *options)
+{
+    if (diff->tests[0]->size)
+       printf ("%5s-%-4s %26s-%-3d",
+               diff->tests[0]->backend, diff->tests[0]->content,
+               diff->tests[0]->name, diff->tests[0]->size);
+    else
+       printf ("%5s %26s", diff->tests[0]->backend, diff->tests[0]->name);
+
+    printf ("  %6.2f (%.2f %4.2f%%) -> %6.2f (%.2f %4.2f%%): %5.2fx ",
+           diff->tests[0]->stats.min_ticks / diff->tests[0]->stats.ticks_per_ms,
+           diff->tests[0]->stats.median_ticks / diff->tests[0]->stats.ticks_per_ms,
+           diff->tests[0]->stats.std_dev * 100,
+           diff->tests[1]->stats.min_ticks / diff->tests[1]->stats.ticks_per_ms,
+           diff->tests[1]->stats.median_ticks / diff->tests[1]->stats.ticks_per_ms,
+           diff->tests[1]->stats.std_dev * 100,
+           fabs (diff->change));
+
+    if (diff->change > 1.0)
+       printf ("speedup\n");
+    else
+       printf ("slowdown\n");
+
+    if (options->print_change_bars)
+       print_change_bar (fabs (diff->change), max_change,
+                         options->use_utf);
+}
+
+static void
+test_diff_print_multi (test_diff_t                *diff,
+                      double                       max_change,
+                      cairo_perf_report_options_t *options)
+{
+    int i;
+    double test_time;
+    double change;
+
+    if (diff->tests[0]->size) {
+       printf ("%s (backend: %s-%s, size: %d)\n",
+               diff->tests[0]->name,
+               diff->tests[0]->backend,
+               diff->tests[0]->content,
+               diff->tests[0]->size);
+    } else {
+       printf ("%s (backend: %s)\n",
+               diff->tests[0]->name,
+               diff->tests[0]->backend);
+    }
+
+    for (i = 0; i < diff->num_tests; i++) {
+       test_time = diff->tests[i]->stats.min_ticks;
+       if (! options->use_ticks)
+           test_time /= diff->tests[i]->stats.ticks_per_ms;
+       change = diff->max / test_time;
+       printf ("[%d] %6.2f: %5.2fx ",
+               diff->tests[i]->fileno,
+               diff->tests[i]->stats.min_ticks / diff->tests[i]->stats.ticks_per_ms,
+               change);
+
+       if (options->print_change_bars)
+           print_change_bar (change, max_change, options->use_utf);
+       else
+           printf("\n");
+    }
+
+    printf("\n");
+}
+
+#define MAX(a,b) ((a) > (b) ? (a) : (b))
+static void
+cairo_perf_reports_compare (cairo_perf_report_t        *reports,
+                           int                          num_reports,
+                           cairo_perf_report_options_t *options)
+{
+    int i;
+    test_report_t **tests, *min_test;
+    test_diff_t *diff, *diffs;
+    int num_diffs, max_diffs;
+    double max_change;
+    double test_time;
+    int seen_non_null;
+    cairo_bool_t printed_speedup = FALSE;
+    cairo_bool_t printed_slowdown = FALSE;
+
+    assert (num_reports >= 2);
+
+    tests = xmalloc (num_reports * sizeof (test_report_t *));
+
+    max_diffs = reports[0].tests_count;
+    for (i = 0; i < num_reports; i++) {
+       tests[i] = reports[i].tests;
+       if (reports[i].tests_count > max_diffs)
+           max_diffs = reports[i].tests_count;
+    }
+
+    diff = diffs = xmalloc (max_diffs * sizeof (test_diff_t));
+
+    num_diffs = 0;
+    while (1) {
+       /* We expect iterations values of 0 when multiple raw reports
+        * for the same test have been condensed into the stats of the
+        * first. So we just skip these later reports that have no
+        * stats. */
+       seen_non_null = 0;
+       for (i = 0; i < num_reports; i++) {
+           while (tests[i]->name && tests[i]->stats.iterations == 0)
+               tests[i]++;
+           if (tests[i]->name)
+               seen_non_null++;
+       }
+
+       if (seen_non_null < 2)
+           break;
+
+       /* Find the minimum of all current tests, (we have to do this
+        * in case some reports don't have a particular test). */
+       for (i = 0; i < num_reports; i++) {
+           if (tests[i]->name) {
+               min_test = tests[i];
+               break;
+           }
+       }
+       for (++i; i < num_reports; i++) {
+           if (tests[i]->name &&
+               test_report_cmp_backend_then_name (tests[i], min_test) < 0)
+           {
+               min_test = tests[i];
+           }
+       }
+
+       /* For each report that has the current test, record it into
+        * the diff structure. */
+       diff->num_tests = 0;
+       diff->tests = xmalloc (num_reports * sizeof (test_diff_t));
+       for (i = 0; i < num_reports; i++) {
+           if (tests[i]->name &&
+               test_report_cmp_backend_then_name (tests[i], min_test) == 0)
+           {
+               test_time = tests[i]->stats.min_ticks;
+               if (! options->use_ticks)
+                   test_time /= tests[i]->stats.ticks_per_ms;
+               if (diff->num_tests == 0) {
+                   diff->min = test_time;
+                   diff->max = test_time;
+               } else {
+                   if (test_time < diff->min)
+                       diff->min = test_time;
+                   if (test_time > diff->max)
+                       diff->max = test_time;
+               }
+               diff->tests[diff->num_tests++] = tests[i];
+               tests[i]++;
+           }
+       }
+       diff->change = diff->max / diff->min;
+
+       if (num_reports == 2) {
+           double old_time, new_time;
+           if (diff->num_tests == 1) {
+               printf ("Only in %s: %s %s\n",
+                       diff->tests[0]->configuration,
+                       diff->tests[0]->backend,
+                       diff->tests[0]->name);
+               continue;
+           }
+           old_time = diff->tests[0]->stats.min_ticks;
+           new_time = diff->tests[1]->stats.min_ticks;
+           if (! options->use_ticks) {
+               old_time /= diff->tests[0]->stats.ticks_per_ms;
+               new_time /= diff->tests[1]->stats.ticks_per_ms;
+           }
+           diff->change = old_time / new_time;
+           if (diff->change < 1.0)
+               diff->change = - 1.0 / diff->change;
+       }
+
+       diff++;
+       num_diffs++;
+    }
+    if (num_diffs == 0)
+       goto DONE;
+
+    if (num_reports == 2)
+       qsort (diffs, num_diffs, sizeof (test_diff_t),
+              test_diff_cmp_speedup_before_slowdown);
+    else
+       qsort (diffs, num_diffs, sizeof (test_diff_t), test_diff_cmp);
+
+    max_change = 1.0;
+    for (i = 0; i < num_diffs; i++) {
+       if (fabs (diffs[i].change) > max_change)
+           max_change = fabs (diffs[i].change);
+    }
+
+    if (num_reports == 2)
+       printf ("old: %s\n"
+               "new: %s\n",
+               diffs->tests[0]->configuration,
+               diffs->tests[1]->configuration);
+
+    for (i = 0; i < num_diffs; i++) {
+       diff = &diffs[i];
+
+       /* Discard as uninteresting a change which is less than the
+        * minimum change required, (default may be overriden on
+        * command-line). */
+       if (fabs (diff->change) - 1.0 < options->min_change)
+           continue;
+
+       if (num_reports == 2) {
+           if (diff->change > 1.0 && ! printed_speedup) {
+               printf ("Speedups\n"
+                       "========\n");
+               printed_speedup = TRUE;
+           }
+           if (diff->change < 1.0 && ! printed_slowdown) {
+               printf ("Slowdowns\n"
+                       "=========\n");
+               printed_slowdown = TRUE;
+           }
+           test_diff_print_binary (diff, max_change, options);
+       } else {
+           test_diff_print_multi (diff, max_change, options);
+       }
+    }
+
+ DONE:
+    for (i = 0; i < num_diffs; i++)
+       free (diffs[i].tests);
+    free (diffs);
+    free (tests);
+}
+
+static void
+usage (const char *argv0)
+{
+    char const *basename = strrchr(argv0, '/');
+    basename = basename ? basename+1 : argv0;
+    fprintf (stderr,
+            "Usage: %s [options] file1 file2 [...]\n\n",
+            basename);
+    fprintf (stderr,
+            "Computes significant performance differences for cairo performance reports.\n"
+            "Each file should be the output of the cairo-perf program (or \"make perf\").\n"
+            "The following options are available:\n"
+            "\n"
+            "--no-utf    Use ascii stars instead of utf-8 change bars.\n"
+            "            Four stars are printed per factor of speedup.\n"
+            "\n"
+            "--no-bars   Don't display change bars at all.\n\n"
+            "\n"
+            "--use-ms    Use milliseconds to calculate differences.\n"
+            "            (instead of ticks which are hardware dependent)\n"
+            "\n"
+            "--min-change threshold[%%]\n"
+            "            Suppress all changes below the given threshold.\n"
+            "            The default threshold of 0.05 or 5%% ignores any\n"
+            "            speedup or slowdown of 1.05 or less. A threshold\n"
+            "            of 0 will cause all output to be reported.\n"
+       );
+    exit(1);
+}
+
+static void
+parse_args (int                           argc,
+           char const                   **argv,
+           cairo_perf_diff_files_args_t  *args)
+{
+    int i;
+
+    for (i = 1; i < argc; i++) {
+       if (strcmp (argv[i], "--no-utf") == 0) {
+           args->options.use_utf = 0;
+       }
+       else if (strcmp (argv[i], "--no-bars") == 0) {
+           args->options.print_change_bars = 0;
+       }
+       else if (strcmp (argv[i], "--use-ms") == 0) {
+           /* default */
+       }
+       else if (strcmp (argv[i], "--use-ticks") == 0) {
+           args->options.use_ticks = 1;
+       }
+       else if (strcmp (argv[i], "--min-change") == 0) {
+           char *end = NULL;
+           i++;
+           if (i >= argc)
+               usage (argv[0]);
+           args->options.min_change = strtod (argv[i], &end);
+           if (*end) {
+               if (*end == '%') {
+                   args->options.min_change /= 100;
+               } else {
+                   usage (argv[0]);
+               }
+           }
+       }
+       else {
+           args->num_filenames++;
+           args->filenames = xrealloc (args->filenames,
+                                       args->num_filenames * sizeof (char *));
+           args->filenames[args->num_filenames - 1] = argv[i];
+       }
+    }
+}
+
+int
+main (int        argc,
+      const char *argv[])
+{
+    cairo_perf_diff_files_args_t args = {
+       NULL,                   /* filenames */
+       0,                      /* num_filenames */
+       {
+           0.05,               /* min change */
+           1,                  /* use UTF-8? */
+           1,                  /* display change bars? */
+       }
+    };
+    cairo_perf_report_t *reports;
+    test_report_t *t;
+    int i;
+
+    parse_args (argc, argv, &args);
+
+    if (args.num_filenames < 2)
+       usage (argv[0]);
+
+    reports = xmalloc (args.num_filenames * sizeof (cairo_perf_report_t));
+
+    for (i = 0; i < args.num_filenames; i++ ) {
+       cairo_perf_report_load (&reports[i], args.filenames[i], i, NULL);
+       printf ("[%d] %s\n", i, args.filenames[i]);
+    }
+    printf ("\n");
+
+    cairo_perf_reports_compare (reports, args.num_filenames, &args.options);
+
+    /* Pointless memory cleanup, (would be a great place for talloc) */
+    free (args.filenames);
+    for (i = 0; i < args.num_filenames; i++) {
+       for (t = reports[i].tests; t->name; t++) {
+           free (t->samples);
+           free (t->backend);
+           free (t->name);
+       }
+       free (reports[i].tests);
+       free (reports[i].configuration);
+    }
+    free (reports);
+
+    return 0;
+}
diff --git a/perf/cairo-perf-graph b/perf/cairo-perf-graph
new file mode 100755 (executable)
index 0000000..a167351
--- /dev/null
@@ -0,0 +1,206 @@
+#!/bin/sh
+set -e
+
+###
+### XXX Source common functions from cairo-perf-diff
+###
+
+usage() {
+    argv0=`basename $0`
+
+    cat >&2 << END
+Usage:
+As opposed to its sibling, cairo-perf-diff, cairo-perf-graph targets
+reviewing changes between series by graphically comparing the performance
+at each commit.
+
+The two revisions can be any revision accepted by git. For example:
+
+       $argv0 1.2.0 1.2.4      # Compare performance of 1.2.0 to 1.2.4
+
+Options:
+
+-f, --force
+       Forces cairo-perf-diff to re-run performance tests
+       even if cached performance data is available.
+
+-h, --html
+       With this option performance changes are summarized
+       as HTML table.
+
+Additional options can be passed the child cairo-perf process
+by separating them with a double hyphen (--). For example, to
+examine what the impact of the latest change is on the stroke
+test you might use:
+
+       $argv0 HEAD -- stroke
+
+The performance results are cached in .perf next to the .git directory.
+
+Set CAIRO_AUTOGEN_OPTIONS to pass options to autogen for both
+builds.
+END
+
+    exit 1
+}
+
+# First, pull off any known options
+while true; do
+    case $1 in
+        -f|--force) force_cairo_perf="true";;
+        -h|--html) html_output="true";;
+        -s|--show) show_only="true";;
+        *) break;;
+    esac
+
+    shift
+done
+
+# Then if anything is left that still looks like an option, (begins
+# with a dash), give usage to catch --help or any other -garbage
+if [ $# -eq 0 ] || [ "`echo "$1" | sed 's/^-//'`" != "$1" ]; then
+    usage
+fi
+
+# Finally, pick up the actual revision arguments
+old="$1"
+new="$2"
+shift 2
+
+# And post-finally, pass anything after -- on to cairo-perf
+CAIRO_PERF_OPTIONS="-r -i 25"
+if [ $# -gt 0 ]; then
+    if [ "$1" = "--" ]; then
+       shift 1
+       CAIRO_PERF_OPTIONS="$CAIRO_PERF_OPTIONS $@"
+    else
+       usage
+    fi
+fi
+
+git_setup() {
+    SUBDIRECTORY_OK='Yes'
+    . "$(git --exec-path)/git-sh-setup"
+    CAIRO_DIR=`dirname $GIT_DIR`
+    if [ "$CAIRO_DIR" = "." ]; then
+       CAIRO_DIR=`pwd`
+    fi
+    CAIRO_PERF_DIR=$CAIRO_DIR/.perf
+}
+
+rev2sha() {
+    rev=$1
+    git rev-parse --verify $rev || ( echo "Cannot resolve $rev as a git object" && exit 1 )
+}
+
+cpu_count() {
+    test -f /proc/cpuinfo &&
+    grep -c '^processor[[:blank:]]\+:' /proc/cpuinfo ||
+    echo 1
+}
+
+# We cache performance output based on a two-part name capturing the
+# current performance test suite and the library being tested. We
+# capture these as the tree object of the perf directory in HEAD and
+# the tree object of the src directory of the revision being tested.
+#
+# This way, whenever the performance suite is updated, cached output
+# from old versions of the suite are automatically invalidated. Also,
+# if a commit just changes things outside of the src tree, (say it
+# changes the "test" test suite, or README or configure.in, or
+# whatever), cairo-perf-diff will be smart enough to still use cached
+# results from a run with an equivalent src tree.
+rev2perf() {
+    rev=$1
+    sha=`rev2sha $rev`
+    src_tree_sha=`rev2sha $rev:src`
+    perf_tree_sha=`rev2sha HEAD:perf`
+    echo "$CAIRO_PERF_DIR/${sha}-${perf_tree_sha}-${src_tree_sha}.perf"
+}
+rev2perf_glob() {
+    rev=$1
+    src_tree_sha=`rev2sha $rev:src`
+    perf_tree_sha=`rev2sha HEAD:perf`
+    echo "$CAIRO_PERF_DIR/*-${perf_tree_sha}-${src_tree_sha}.perf"
+}
+
+# Usage: run_cairo_perf_if_not_cached <rev> <suffix>
+# The <rev> argument must be a valid git ref-spec that can
+# be resolved to a commit. The suffix is just something
+# unique so that build directories can be separated for
+# multiple calls to this function.
+run_cairo_perf_if_not_cached() {
+    rev=$1
+    build_dir="build-$2"
+
+    owd=`pwd`
+    sha=`rev2sha $rev`
+    perf=`rev2perf $rev`
+    glob=`rev2perf_glob $rev`
+    if [ -e $glob ] && [ "$force_cairo_perf" != "true" ]  || [ -n "$show_only" ]; then
+       return 0
+    fi
+    if [ ! -d $CAIRO_PERF_DIR ]; then
+       echo "Creating new perf cache in $CAIRO_PERF_DIR"
+       mkdir $CAIRO_PERF_DIR
+    fi
+
+    cd $CAIRO_DIR
+    boilerplate_files=`git ls-tree --name-only HEAD boilerplate/*`
+    perf_files=`git ls-tree --name-only HEAD perf/*`
+    cd $CAIRO_PERF_DIR
+
+    if [ ! -d $build_dir ]; then
+       git clone -s $CAIRO_DIR $build_dir
+       (cd $build_dir; git checkout -b tmp-cairo-perf-diff $sha)
+    fi
+    cd $build_dir
+
+    git checkout tmp-cairo-perf-diff
+    git reset --hard $sha
+
+    if [ -z "$MAKEFLAGS" ]; then
+       CPU_COUNT=`cpu_count`
+        export MAKEFLAGS="-j`expr $CPU_COUNT + 1`"
+    fi
+
+    if [ ! -e Makefile ]; then
+       CFLAGS="-O2" ./autogen.sh $CAIRO_AUTOGEN_OPTIONS
+    fi
+    make CFLAGS="-O2" || (rm config.cache && make CFLAGS="-O2")
+    for file in $boilerplate_files; do
+       rsync $CAIRO_DIR/$file boilerplate
+    done
+    (cd boilerplate; make)
+    for file in $perf_files; do
+       rsync $CAIRO_DIR/$file perf
+    done
+    cd perf;
+    make cairo-perf || exit 1
+    echo "Running \"cairo-perf $CAIRO_PERF_OPTIONS\" against $rev. Results will be cached in:"
+    echo "$perf"
+    (./cairo-perf $CAIRO_PERF_OPTIONS || echo "*** Performance test crashed") >> $perf
+    cd $owd
+}
+
+git_setup
+
+# Build cairo-perf-graph-files if not available
+if [ ! -e $CAIRO_DIR/perf/cairo-perf-graph-files ]; then
+    echo "Building cairo-perf-graph-files"
+    if [ "x$OS" = "xWindows_NT" ]; then
+        make -f Makefile.win32 -C $CAIRO_DIR/perf/ cairo-perf-graph-files CFG=debug
+    else
+        make -C $CAIRO_DIR/perf/ cairo-perf-graph-files
+    fi
+fi
+
+revs=""
+for rev in `git rev-list --reverse $old..$new`; do
+    run_cairo_perf_if_not_cached $rev rev
+    perf=`rev2perf $rev`
+    [ -e "$perf" ] && revs="$revs $perf"
+done
+
+exec $CAIRO_DIR/perf/cairo-perf-graph-files $revs
+#exec $CAIRO_DIR/libtool --mode=execute gdb --args $CAIRO_DIR/perf/cairo-perf-graph-files $revs
diff --git a/perf/cairo-perf-graph-files.c b/perf/cairo-perf-graph-files.c
new file mode 100644 (file)
index 0000000..0ac4b7c
--- /dev/null
@@ -0,0 +1,604 @@
+/*
+ * Copyright © 2008 Chris Wilson
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of the
+ * copyright holders not be used in advertising or publicity
+ * pertaining to distribution of the software without specific,
+ * written prior permission. The copyright holders make no
+ * representations about the suitability of this software for any
+ * purpose.  It is provided "as is" without express or implied
+ * warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ * SOFTWARE.
+ *
+ * Authors: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-perf.h"
+#include "cairo-perf-graph.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#include <cairo.h>
+
+static void
+usage (const char *argv0)
+{
+    char const *basename = strrchr (argv0, '/');
+    basename = basename ? basename+1 : argv0;
+    g_printerr ("Usage: %s [options] file1 file2 [...]\n\n", basename);
+    g_printerr ("Draws a graph illustrating the change in performance over a series.\n");
+    exit(1);
+}
+
+enum {
+    CASE_SHOWN,
+    CASE_INCONSISTENT,
+    CASE_BACKEND,
+    CASE_CONTENT,
+    CASE_NAME,
+    CASE_SIZE,
+    CASE_FG_COLOR,
+    CASE_DATA,
+    CASE_NCOLS
+};
+
+static GtkTreeStore *
+cases_to_store (test_case_t *cases)
+{
+    GtkTreeStore *store;
+    GtkTreeIter backend_iter;
+    GtkTreeIter content_iter;
+    const char *backend = NULL;
+    const char *content = NULL;
+
+    store = gtk_tree_store_new (CASE_NCOLS,
+                               G_TYPE_BOOLEAN, /* shown */
+                               G_TYPE_BOOLEAN, /* inconsistent */
+                               G_TYPE_STRING, /* backend */
+                               G_TYPE_STRING, /* content */
+                               G_TYPE_STRING, /* name */
+                               G_TYPE_INT, /* size */
+                               GDK_TYPE_COLOR, /* fg color */
+                               G_TYPE_POINTER); /* data */
+    while (cases->backend != NULL) {
+       GtkTreeIter iter;
+
+       if (backend == NULL || strcmp (backend, cases->backend)) {
+           gtk_tree_store_append (store, &backend_iter, NULL);
+           gtk_tree_store_set (store, &backend_iter,
+                               CASE_SHOWN, TRUE,
+                               CASE_BACKEND, cases->backend,
+                               -1);
+           backend = cases->backend;
+           content = NULL;
+       }
+       if (content == NULL || strcmp (content, cases->content)) {
+           gtk_tree_store_append (store, &content_iter, &backend_iter);
+           gtk_tree_store_set (store, &content_iter,
+                               CASE_SHOWN, TRUE,
+                               CASE_BACKEND, cases->backend,
+                               CASE_CONTENT, cases->content,
+                               -1);
+           content = cases->content;
+       }
+
+       gtk_tree_store_append (store, &iter, &content_iter);
+       gtk_tree_store_set (store, &iter,
+                           CASE_SHOWN, TRUE,
+                           CASE_BACKEND, cases->backend,
+                           CASE_CONTENT, cases->content,
+                           CASE_NAME, cases->name,
+                           CASE_SIZE, cases->size,
+                           CASE_FG_COLOR, &cases->color,
+                           CASE_DATA, cases,
+                           -1);
+       cases++;
+    }
+
+    return store;
+}
+
+struct _app_data {
+    GtkWidget *window;
+
+    test_case_t *cases;
+    cairo_perf_report_t *reports;
+    int num_reports;
+
+    GtkTreeStore *case_store;
+
+    GIOChannel *git_io;
+    GtkTextBuffer *git_buffer;
+
+    GtkWidget *gv;
+};
+
+static void
+recurse_set_shown (GtkTreeModel *model,
+                  GtkTreeIter  *parent,
+                  gboolean      shown)
+{
+    GtkTreeIter iter;
+
+    if (gtk_tree_model_iter_children (model, &iter, parent)) do {
+       test_case_t *c;
+
+       gtk_tree_model_get (model, &iter, CASE_DATA, &c, -1);
+       if (c == NULL) {
+           recurse_set_shown (model, &iter, shown);
+       } else if (shown != c->shown) {
+           c->shown = shown;
+           gtk_tree_store_set (GTK_TREE_STORE (model), &iter,
+                               CASE_SHOWN, shown,
+                               CASE_INCONSISTENT, FALSE,
+                               -1);
+       }
+    } while (gtk_tree_model_iter_next (model, &iter));
+}
+
+static gboolean
+children_consistent (GtkTreeModel *model,
+                    GtkTreeIter  *parent)
+{
+    GtkTreeIter iter;
+    gboolean first = TRUE;
+    gboolean first_active;
+
+    if (gtk_tree_model_iter_children (model, &iter, parent)) do {
+       gboolean active, inconsistent;
+
+       gtk_tree_model_get (model, &iter,
+                           CASE_INCONSISTENT, &inconsistent,
+                           CASE_SHOWN, &active,
+                           -1);
+       if (inconsistent)
+           return FALSE;
+
+       if (first) {
+           first_active = active;
+           first = FALSE;
+       } else if (active != first_active)
+           return FALSE;
+    } while (gtk_tree_model_iter_next (model, &iter));
+
+    return TRUE;
+}
+
+static void
+check_consistent (GtkTreeModel *model,
+                 GtkTreeIter  *child)
+{
+    GtkTreeIter parent;
+
+    if (gtk_tree_model_iter_parent (model, &parent, child)) {
+       gtk_tree_store_set (GTK_TREE_STORE (model), &parent,
+                           CASE_INCONSISTENT,
+                           ! children_consistent (model, &parent),
+                           -1);
+       check_consistent (model, &parent);
+    }
+}
+
+static void
+show_case_toggled (GtkCellRendererToggle *cell,
+                  gchar                 *str,
+                  struct _app_data      *app)
+{
+    GtkTreeModel *model;
+    GtkTreePath *path;
+    GtkTreeIter iter;
+    test_case_t *c;
+    gboolean active;
+
+    active = ! gtk_cell_renderer_toggle_get_active (cell);
+
+    model = GTK_TREE_MODEL (app->case_store);
+
+    path = gtk_tree_path_new_from_string (str);
+    gtk_tree_model_get_iter (model, &iter, path);
+    gtk_tree_path_free (path);
+
+    gtk_tree_store_set (app->case_store, &iter,
+                       CASE_SHOWN, active,
+                       CASE_INCONSISTENT, FALSE,
+                       -1);
+    gtk_tree_model_get (model, &iter, CASE_DATA, &c, -1);
+    if (c != NULL) {
+       if (active == c->shown)
+           return;
+
+       c->shown = active;
+    } else {
+       recurse_set_shown (model, &iter, active);
+    }
+    check_consistent (model, &iter);
+
+    graph_view_update_visible ((GraphView *) app->gv);
+}
+
+static gboolean
+git_read (GIOChannel      *io,
+         GIOCondition      cond,
+         struct _app_data *app)
+{
+    int fd;
+
+    fd = g_io_channel_unix_get_fd (io);
+    do {
+       char buf[4096];
+       int len;
+       GtkTextIter end;
+
+       len = read (fd, buf, sizeof (buf));
+       if (len <= 0) {
+           int err = len ? errno : 0;
+           switch (err) {
+           case EAGAIN:
+           case EINTR:
+               return TRUE;
+           default:
+               g_io_channel_unref (app->git_io);
+               app->git_io = NULL;
+               return FALSE;
+           }
+       }
+
+       gtk_text_buffer_get_end_iter (app->git_buffer, &end);
+       gtk_text_buffer_insert (app->git_buffer, &end, buf, len);
+    } while (TRUE);
+}
+
+static void
+do_git (struct _app_data  *app,
+       char             **argv)
+{
+    gint output;
+    GError *error = NULL;
+    GtkTextIter start, stop;
+    long flags;
+
+    if (! g_spawn_async_with_pipes (NULL, argv, NULL,
+                                   G_SPAWN_SEARCH_PATH |
+                                   G_SPAWN_STDERR_TO_DEV_NULL |
+                                   G_SPAWN_FILE_AND_ARGV_ZERO,
+                                   NULL, NULL, NULL,
+                                   NULL, &output, NULL,
+                                   &error))
+    {
+       g_error ("spawn failed: %s", error->message);
+    }
+
+    if (app->git_io) {
+       g_io_channel_shutdown (app->git_io, FALSE, NULL);
+       g_io_channel_unref (app->git_io);
+    }
+
+    gtk_text_buffer_get_bounds (app->git_buffer, &start, &stop);
+    gtk_text_buffer_delete (app->git_buffer, &start, &stop);
+
+    flags = fcntl (output, F_GETFL);
+    if ((flags & O_NONBLOCK) == 0)
+       fcntl (output, F_SETFL, flags | O_NONBLOCK);
+
+    app->git_io = g_io_channel_unix_new (output);
+    g_io_add_watch (app->git_io, G_IO_IN | G_IO_HUP, (GIOFunc) git_read, app);
+}
+
+static void
+gv_report_selected (GraphView       *gv,
+                   int               i,
+                   struct _app_data *app)
+{
+    cairo_perf_report_t *report;
+    char *hyphen;
+
+    if (i == -1)
+       return;
+
+    report = &app->reports[i];
+    hyphen = strchr (report->configuration, '-');
+    if (hyphen != NULL) {
+       int len = hyphen - report->configuration;
+       char *id = g_malloc (len + 1);
+       char *argv[5];
+
+       memcpy (id, report->configuration, len);
+       id[len] = '\0';
+
+       argv[0] = (char *) "git";
+       argv[1] = (char *) "git";
+       argv[2] = (char *) "show";
+       argv[3] = id;
+       argv[4] = NULL;
+
+       do_git (app, argv);
+       g_free (id);
+    }
+}
+
+static GtkWidget *
+window_create (test_case_t        *cases,
+              cairo_perf_report_t *reports,
+              int                  num_reports)
+{
+    GtkWidget *window, *table, *w;
+    GtkWidget *tv, *sw;
+    GtkTreeStore *store;
+    GtkTreeViewColumn *column;
+    GtkCellRenderer *renderer;
+    struct _app_data *data;
+
+
+    data = g_new0 (struct _app_data, 1);
+    data->cases = cases;
+    data->reports = reports;
+    data->num_reports = num_reports;
+
+    window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+    gtk_window_set_title (GTK_WINDOW (window), "Cairo Performance Graph");
+    g_object_set_data_full (G_OBJECT (window),
+                           "app-data", data, (GDestroyNotify)g_free);
+
+    data->window = window;
+
+    table = gtk_table_new (2, 2, FALSE);
+
+    /* legend & show/hide lines (categorised) */
+    tv = gtk_tree_view_new ();
+    store = cases_to_store (cases);
+    data->case_store = store;
+    gtk_tree_view_set_model (GTK_TREE_VIEW (tv), GTK_TREE_MODEL (store));
+
+    renderer = gtk_cell_renderer_toggle_new ();
+    column = gtk_tree_view_column_new_with_attributes (NULL,
+           renderer,
+           "active", CASE_SHOWN,
+           "inconsistent", CASE_INCONSISTENT,
+           NULL);
+    gtk_tree_view_append_column (GTK_TREE_VIEW (tv), column);
+    g_signal_connect (renderer, "toggled",
+                     G_CALLBACK (show_case_toggled), data);
+
+    renderer = gtk_cell_renderer_text_new ();
+    column = gtk_tree_view_column_new_with_attributes ("Backend",
+           renderer,
+           "text", CASE_BACKEND,
+           NULL);
+    gtk_tree_view_append_column (GTK_TREE_VIEW (tv), column);
+
+    renderer = gtk_cell_renderer_text_new ();
+    column = gtk_tree_view_column_new_with_attributes ("Content",
+           renderer,
+           "text", CASE_CONTENT,
+           NULL);
+    gtk_tree_view_append_column (GTK_TREE_VIEW (tv), column);
+
+    renderer = gtk_cell_renderer_text_new ();
+    column = gtk_tree_view_column_new_with_attributes ("Test",
+           renderer,
+           "text", CASE_NAME,
+           "foreground-gdk", CASE_FG_COLOR,
+           NULL);
+    gtk_tree_view_append_column (GTK_TREE_VIEW (tv), column);
+
+    renderer = gtk_cell_renderer_text_new ();
+    column = gtk_tree_view_column_new_with_attributes ("Size",
+           renderer,
+           "text", CASE_SIZE,
+           NULL);
+    gtk_tree_view_append_column (GTK_TREE_VIEW (tv), column);
+
+    gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (tv), TRUE);
+    g_object_unref (store);
+
+    sw = gtk_scrolled_window_new (NULL, NULL);
+    gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
+                                   GTK_POLICY_NEVER,
+                                   GTK_POLICY_AUTOMATIC);
+    gtk_container_add (GTK_CONTAINER (sw), tv);
+    gtk_widget_show (tv);
+    gtk_table_attach (GTK_TABLE (table), sw,
+                     0, 1, 0, 2,
+                     GTK_FILL, GTK_FILL,
+                     4, 4);
+    gtk_widget_show (sw);
+
+    /* the performance chart */
+    w = graph_view_new ();
+    data->gv = w;
+    g_signal_connect (w, "report-selected",
+                     G_CALLBACK (gv_report_selected), data);
+    graph_view_set_reports ((GraphView *)w, cases, reports, num_reports);
+    gtk_table_attach (GTK_TABLE (table), w,
+                     1, 2, 0, 1,
+                     GTK_FILL | GTK_EXPAND, GTK_FILL | GTK_EXPAND,
+                     4, 4);
+    gtk_widget_show (w);
+
+    /* interesting information - presumably the commit log */
+    w = gtk_text_view_new ();
+    data->git_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (w));
+    sw = gtk_scrolled_window_new (NULL, NULL);
+    gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
+                                   GTK_POLICY_NEVER,
+                                   GTK_POLICY_AUTOMATIC);
+    gtk_container_add (GTK_CONTAINER (sw), w);
+    gtk_widget_show (w);
+    gtk_table_attach (GTK_TABLE (table), sw,
+                     1, 2, 1, 2,
+                     GTK_FILL, GTK_FILL | GTK_EXPAND,
+                     4, 4);
+    gtk_widget_show (sw);
+
+    gtk_container_add (GTK_CONTAINER (window), table);
+    gtk_widget_show (table);
+
+    return window;
+}
+
+static void
+name_to_color (const char *name,
+              GdkColor   *color)
+{
+    gint v = g_str_hash (name);
+
+    color->red = ((v >>  0) & 0xff) / 384. * 0xffff;
+    color->green = ((v >>  8) & 0xff) / 384. * 0xffff;
+    color->blue = ((v >> 16) & 0xff) / 384. * 0xffff;
+}
+
+static test_case_t *
+test_cases_from_reports (cairo_perf_report_t *reports,
+                        int                  num_reports)
+{
+    test_case_t *cases, *c;
+    test_report_t **tests;
+    int i, j;
+    int num_tests;
+
+    num_tests = 0;
+    for (i = 0; i < num_reports; i++) {
+       for (j = 0; reports[i].tests[j].name != NULL; j++)
+           ;
+       if (j > num_tests)
+           num_tests = j;
+    }
+
+    cases = xcalloc (num_tests+1, sizeof (test_case_t));
+    tests = xmalloc (num_reports * sizeof (test_report_t *));
+    for (i = 0; i < num_reports; i++)
+       tests[i] = reports[i].tests;
+
+    c = cases;
+    while (1) {
+       int seen_non_null;
+       test_report_t *min_test;
+
+       /* We expect iterations values of 0 when multiple raw reports
+        * for the same test have been condensed into the stats of the
+        * first. So we just skip these later reports that have no
+        * stats. */
+       seen_non_null = 0;
+       for (i = 0; i < num_reports; i++) {
+           while (tests[i]->name && tests[i]->stats.iterations == 0)
+               tests[i]++;
+           if (tests[i]->name)
+               seen_non_null++;
+       }
+
+       if (seen_non_null < 2)
+           break;
+
+       /* Find the minimum of all current tests, (we have to do this
+        * in case some reports don't have a particular test). */
+       for (i = 0; i < num_reports; i++) {
+           if (tests[i]->name) {
+               min_test = tests[i];
+               break;
+           }
+       }
+       for (++i; i < num_reports; i++) {
+           if (tests[i]->name &&
+               test_report_cmp_backend_then_name (tests[i], min_test) < 0)
+           {
+               min_test = tests[i];
+           }
+       }
+
+       c->min_test = min_test;
+       c->backend = min_test->backend;
+       c->content = min_test->content;
+       c->name = min_test->name;
+       c->size = min_test->size;
+       c->baseline = min_test->stats.min_ticks;
+       c->min = c->max = 1.;
+       c->shown = TRUE;
+       name_to_color (c->name, &c->color);
+
+       for (i = 0; i < num_reports; i++) {
+           if (tests[i]->name &&
+               test_report_cmp_backend_then_name (tests[i], min_test) == 0)
+           {
+               tests[i]++;
+               break;
+           }
+       }
+
+       for (++i; i < num_reports; i++) {
+           if (tests[i]->name &&
+               test_report_cmp_backend_then_name (tests[i], min_test) == 0)
+           {
+               double v = tests[i]->stats.min_ticks / c->baseline;
+               if (v < c->min)
+                   c->min = v;
+               if (v > c->max)
+                   c->max = v;
+               tests[i]++;
+           }
+       }
+
+       c++;
+    }
+    free (tests);
+
+    return cases;
+}
+int
+main (int   argc,
+      char *argv[])
+{
+    cairo_perf_report_t *reports;
+    test_case_t *cases;
+    test_report_t *t;
+    int i;
+    GtkWidget *window;
+
+    gtk_init (&argc, &argv);
+
+    if (argc < 3)
+       usage (argv[0]);
+
+    reports = xmalloc ((argc-1) * sizeof (cairo_perf_report_t));
+    for (i = 1; i < argc; i++ )
+       cairo_perf_report_load (&reports[i-1], argv[i], NULL);
+
+    cases = test_cases_from_reports (reports, argc-1);
+
+    window = window_create (cases, reports, argc-1);
+    g_signal_connect (window, "delete-event",
+                     G_CALLBACK (gtk_main_quit), NULL);
+    gtk_widget_show (window);
+
+    gtk_main ();
+
+    /* Pointless memory cleanup, (would be a great place for talloc) */
+    free (cases);
+    for (i = 0; i < argc-1; i++) {
+       for (t = reports[i].tests; t->name; t++) {
+           free (t->samples);
+           free (t->backend);
+           free (t->name);
+       }
+       free (reports[i].tests);
+       free (reports[i].configuration);
+    }
+    free (reports);
+
+    return 0;
+}
diff --git a/perf/cairo-perf-graph-widget.c b/perf/cairo-perf-graph-widget.c
new file mode 100644 (file)
index 0000000..41311f7
--- /dev/null
@@ -0,0 +1,604 @@
+/*
+ * Copyright © 2008 Chris Wilson
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of the
+ * copyright holders not be used in advertising or publicity
+ * pertaining to distribution of the software without specific,
+ * written prior permission. The copyright holders make no
+ * representations about the suitability of this software for any
+ * purpose.  It is provided "as is" without express or implied
+ * warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ * SOFTWARE.
+ *
+ * Authors: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-perf.h"
+#include "cairo-perf-graph.h"
+
+#include <gtk/gtk.h>
+
+struct _GraphView {
+    GtkWidget widget;
+
+    test_case_t *cases;
+    cairo_perf_report_t *reports;
+    int num_reports;
+    double ymin, ymax;
+
+    int selected_report;
+};
+
+typedef struct _GraphViewClass {
+    GtkWidgetClass parent_class;
+} GraphViewClass;
+
+static GType graph_view_get_type (void);
+
+enum {
+    REPORT_SELECTED,
+    LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL];
+
+G_DEFINE_TYPE (GraphView, graph_view, GTK_TYPE_WIDGET)
+
+static void
+draw_baseline_performance (test_case_t         *cases,
+                          cairo_perf_report_t  *reports,
+                          int                   num_reports,
+                          cairo_t              *cr,
+                          const cairo_matrix_t *m)
+{
+    test_report_t **tests;
+    double dots[2] = { 0, 1.};
+    int i;
+
+    tests = xmalloc (num_reports * sizeof (test_report_t *));
+    for (i = 0; i < num_reports; i++)
+       tests[i] = reports[i].tests;
+
+    while (cases->backend != NULL) {
+       test_report_t *min_test;
+       double baseline, last_y;
+       double x, y;
+
+       if (! cases->shown) {
+           cases++;
+           continue;
+       }
+
+       min_test = cases->min_test;
+
+       for (i = 0; i < num_reports; i++) {
+           while (tests[i]->name &&
+               test_report_cmp_backend_then_name (tests[i], min_test) < 0)
+           {
+               tests[i]++;
+           }
+       }
+
+       /* first the stroke */
+       cairo_save (cr);
+       cairo_set_line_width (cr, 2.);
+       gdk_cairo_set_source_color (cr, &cases->color);
+       for (i = 0; i < num_reports; i++) {
+           if (tests[i]->name &&
+               test_report_cmp_backend_then_name (tests[i], min_test) == 0)
+           {
+               baseline = tests[i]->stats.min_ticks;
+
+               x = i; y = 0;
+               cairo_matrix_transform_point (m, &x, &y);
+               x = floor (x);
+               y = floor (y);
+               cairo_move_to (cr, x, y);
+               last_y = y;
+               break;
+           }
+       }
+
+       for (++i; i < num_reports; i++) {
+           if (tests[i]->name &&
+               test_report_cmp_backend_then_name (tests[i], min_test) == 0)
+           {
+               x = i, y = tests[i]->stats.min_ticks / baseline;
+
+               if (y < 1.)
+                   y = -1./y + 1;
+               else
+                   y -= 1;
+
+               cairo_matrix_transform_point (m, &x, &y);
+               x = floor (x);
+               y = floor (y);
+               cairo_line_to (cr, x, last_y);
+               cairo_line_to (cr, x, y);
+               last_y = y;
+           }
+       }
+       {
+           x = num_reports, y = 0;
+           cairo_matrix_transform_point (m, &x, &y);
+           x = floor (x);
+           cairo_line_to (cr, x, last_y);
+       }
+
+       cairo_set_line_width (cr, 1.);
+       cairo_stroke (cr);
+
+       /* then draw the points */
+       for (i = 0; i < num_reports; i++) {
+           if (tests[i]->name &&
+               test_report_cmp_backend_then_name (tests[i], min_test) == 0)
+           {
+               baseline = tests[i]->stats.min_ticks;
+
+               x = i; y = 0;
+               cairo_matrix_transform_point (m, &x, &y);
+               x = floor (x);
+               y = floor (y);
+               cairo_move_to (cr, x, y);
+               cairo_close_path (cr);
+               last_y = y;
+
+               tests[i]++;
+               break;
+           }
+       }
+
+       for (++i; i < num_reports; i++) {
+           if (tests[i]->name &&
+               test_report_cmp_backend_then_name (tests[i], min_test) == 0)
+           {
+               x = i, y = tests[i]->stats.min_ticks / baseline;
+
+               if (y < 1.)
+                   y = -1./y + 1;
+               else
+                   y -= 1;
+
+               cairo_matrix_transform_point (m, &x, &y);
+               x = floor (x);
+               y = floor (y);
+               cairo_move_to (cr, x, last_y);
+               cairo_close_path (cr);
+               cairo_move_to (cr, x, y);
+               cairo_close_path (cr);
+               last_y = y;
+
+               tests[i]++;
+           }
+       }
+       {
+           x = num_reports, y = 0;
+           cairo_matrix_transform_point (m, &x, &y);
+           x = floor (x);
+           cairo_move_to (cr, x, last_y);
+           cairo_close_path (cr);
+       }
+       cairo_set_source_rgba (cr, 0, 0, 0, .5);
+       cairo_set_dash (cr, dots, 2, 0.);
+       cairo_set_line_width (cr, 3.);
+       cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
+       cairo_stroke (cr);
+       cairo_restore (cr);
+
+       cases++;
+    }
+    free (tests);
+}
+
+static void
+draw_hline (cairo_t             *cr,
+           const cairo_matrix_t *m,
+           double                y0,
+           double                xmin,
+           double                xmax)
+{
+    double x, y;
+    double py_offset;
+
+    py_offset = fmod (cairo_get_line_width (cr) / 2., 1.);
+
+    x = xmin; y = y0;
+    cairo_matrix_transform_point (m, &x, &y);
+    cairo_move_to (cr, floor (x), floor (y) + py_offset);
+
+    x = xmax; y = y0;
+    cairo_matrix_transform_point (m, &x, &y);
+    cairo_line_to (cr, ceil (x), floor (y) + py_offset);
+
+    cairo_stroke (cr);
+}
+
+static void
+draw_label (cairo_t             *cr,
+           const cairo_matrix_t *m,
+           double                y0,
+           double                xmin,
+           double                xmax)
+{
+    double x, y;
+    char buf[80];
+    cairo_text_extents_t extents;
+
+    snprintf (buf, sizeof (buf), "%.0fx", fabs (y0));
+    cairo_text_extents (cr, buf, &extents);
+
+    x = xmin; y = y0;
+    cairo_matrix_transform_point (m, &x, &y);
+    cairo_move_to (cr,
+                  x - extents.width - 4,
+                  y - (extents.height/2. + extents.y_bearing));
+    cairo_show_text (cr, buf);
+
+
+    snprintf (buf, sizeof (buf), "%.0fx", fabs (y0));
+    cairo_text_extents (cr, buf, &extents);
+
+    x = xmax; y = y0;
+    cairo_matrix_transform_point (m, &x, &y);
+    cairo_move_to (cr,
+                  x + 4,
+                  y - (extents.height/2. + extents.y_bearing));
+    cairo_show_text (cr, buf);
+}
+
+#define ALIGN_X(v) ((v)<<0)
+#define ALIGN_Y(v) ((v)<<2)
+static void
+draw_rotated_label (cairo_t    *cr,
+                   const char *text,
+                   double      x,
+                   double      y,
+                   double      angle,
+                   int         align)
+{
+    cairo_text_extents_t extents;
+
+    cairo_text_extents (cr, text, &extents);
+
+    cairo_save (cr); {
+       cairo_translate (cr, x, y);
+       cairo_rotate (cr, angle);
+       switch (align) {
+       case ALIGN_X(0) | ALIGN_Y(0):
+           cairo_move_to (cr,
+                          -extents.x_bearing,
+                          -extents.y_bearing);
+           break;
+       case ALIGN_X(0) | ALIGN_Y(1):
+           cairo_move_to (cr,
+                          -extents.x_bearing,
+                          - (extents.height/2. + extents.y_bearing));
+           break;
+       case ALIGN_X(0) | ALIGN_Y(2):
+           cairo_move_to (cr,
+                          -extents.x_bearing,
+                          - (extents.height + extents.y_bearing));
+           break;
+
+       case ALIGN_X(1) | ALIGN_Y(0):
+           cairo_move_to (cr,
+                          - (extents.width/2. + extents.x_bearing),
+                          -extents.y_bearing);
+           break;
+       case ALIGN_X(1) | ALIGN_Y(1):
+           cairo_move_to (cr,
+                          - (extents.width/2. + extents.x_bearing),
+                          - (extents.height/2. + extents.y_bearing));
+           break;
+       case ALIGN_X(1) | ALIGN_Y(2):
+           cairo_move_to (cr,
+                          - (extents.width/2. + extents.x_bearing),
+                          - (extents.height + extents.y_bearing));
+           break;
+
+       case ALIGN_X(2) | ALIGN_Y(0):
+           cairo_move_to (cr,
+                          - (extents.width + extents.x_bearing),
+                          -extents.y_bearing);
+           break;
+       case ALIGN_X(2) | ALIGN_Y(1):
+           cairo_move_to (cr,
+                          - (extents.width + extents.x_bearing),
+                          - (extents.height/2. + extents.y_bearing));
+           break;
+       case ALIGN_X(2) | ALIGN_Y(2):
+           cairo_move_to (cr,
+                          - (extents.width + extents.x_bearing),
+                          - (extents.height + extents.y_bearing));
+           break;
+       }
+       cairo_show_text (cr, text);
+    } cairo_restore (cr);
+}
+
+#define PAD 36
+static void
+graph_view_draw (GraphView *self,
+                cairo_t   *cr)
+{
+    cairo_matrix_t m;
+    const double dash[2] = {4, 4};
+    double range;
+    int i;
+
+    if (self->widget.allocation.width < 4 *PAD)
+       return;
+    if (self->widget.allocation.height < 3 *PAD)
+       return;
+
+    range = floor (self->ymax+1) - ceil (self->ymin-1);
+
+    cairo_matrix_init_translate (&m, PAD, self->widget.allocation.height - PAD);
+    cairo_matrix_scale (&m,
+                       (self->widget.allocation.width-2*PAD)/(self->num_reports),
+                       -(self->widget.allocation.height-2*PAD)/range);
+    cairo_matrix_translate (&m, 0,   floor (self->ymax+1));
+
+    if (self->selected_report != -1) {
+       cairo_save (cr); {
+           double x0, x1, y;
+           x0 = self->selected_report; y = 0;
+           cairo_matrix_transform_point (&m, &x0, &y);
+           x0 = floor (x0);
+           x1 = self->selected_report + 1; y = 0;
+           cairo_matrix_transform_point (&m, &x1, &y);
+           x1 = ceil (x1);
+           y = (x1 - x0) / 8;
+           y = MIN (y, PAD / 2);
+           x0 -= y;
+           x1 += y;
+           cairo_rectangle (cr, x0, PAD/2, x1-x0, self->widget.allocation.height-2*PAD + PAD);
+           gdk_cairo_set_source_color (cr, &self->widget.style->base[GTK_STATE_SELECTED]);
+           cairo_fill (cr);
+       } cairo_restore (cr);
+    }
+
+    cairo_save (cr); {
+       cairo_pattern_t *linear;
+       double x, y;
+
+       gdk_cairo_set_source_color (cr,
+                                   &self->widget.style->fg[GTK_WIDGET_STATE (self)]);
+       cairo_set_line_width (cr, 2.);
+       draw_hline (cr, &m, 0, 0, self->num_reports);
+
+       cairo_set_line_width (cr, 1.);
+       cairo_set_dash (cr, NULL, 0, 0);
+
+       for (i = ceil (self->ymin-1); i <= floor (self->ymax+1); i++) {
+           if (i != 0)
+               draw_hline (cr, &m, i, 0, self->num_reports);
+       }
+
+       cairo_set_font_size (cr, 11);
+
+       linear = cairo_pattern_create_linear (0, PAD, 0, self->widget.allocation.height-2*PAD);
+       cairo_pattern_add_color_stop_rgb (linear, 0, 0, 1, 0);
+       cairo_pattern_add_color_stop_rgb (linear, 1, 1, 0, 0);
+       cairo_set_source (cr, linear);
+       cairo_pattern_destroy (linear);
+
+       for (i = ceil (self->ymin-1); i <= floor (self->ymax+1); i++) {
+           if (i != 0)
+               draw_label (cr, &m, i, 0, self->num_reports);
+       }
+
+       x = 0, y = floor (self->ymax+1);
+       cairo_matrix_transform_point (&m, &x, &y);
+       draw_rotated_label (cr, "Faster", x - 7, y + 14,
+                           270./360 * 2 * G_PI,
+                           ALIGN_X(2) | ALIGN_Y(1));
+       x = self->num_reports, y = floor (self->ymax+1);
+       cairo_matrix_transform_point (&m, &x, &y);
+       draw_rotated_label (cr, "Faster", x + 11, y + 14,
+                           270./360 * 2 * G_PI,
+                           ALIGN_X(2) | ALIGN_Y(1));
+
+       x = 0, y = ceil (self->ymin-1);
+       cairo_matrix_transform_point (&m, &x, &y);
+       draw_rotated_label (cr, "Slower", x - 7, y - 14,
+                           90./360 * 2 * G_PI,
+                           ALIGN_X(2) | ALIGN_Y(1));
+       x = self->num_reports, y = ceil (self->ymin-1);
+       cairo_matrix_transform_point (&m, &x, &y);
+       draw_rotated_label (cr, "Slower", x + 11, y - 14,
+                           90./360 * 2 * G_PI,
+                           ALIGN_X(2) | ALIGN_Y(1));
+    } cairo_restore (cr);
+
+    draw_baseline_performance (self->cases,
+                              self->reports, self->num_reports,
+                              cr, &m);
+
+    cairo_save (cr); {
+       cairo_set_source_rgb (cr, 0.7, 0.7, 0.7);
+       cairo_set_line_width (cr, 1.);
+       cairo_set_dash (cr, dash, 2, 0);
+       draw_hline (cr, &m, 0, 0, self->num_reports);
+    } cairo_restore (cr);
+}
+
+static gboolean
+graph_view_expose (GtkWidget     *w,
+                  GdkEventExpose *ev)
+{
+    GraphView *self = (GraphView *) w;
+    cairo_t *cr;
+
+    cr = gdk_cairo_create (w->window);
+    gdk_cairo_set_source_color (cr, &w->style->base[GTK_WIDGET_STATE (w)]);
+    cairo_paint (cr);
+
+    graph_view_draw (self, cr);
+
+    cairo_destroy (cr);
+
+    return FALSE;
+}
+
+static gboolean
+graph_view_button_press (GtkWidget     *w,
+                        GdkEventButton *ev)
+{
+    GraphView *self = (GraphView *) w;
+    cairo_matrix_t m;
+    double x,y;
+    int i;
+
+    cairo_matrix_init_translate (&m, PAD, self->widget.allocation.height-PAD);
+    cairo_matrix_scale (&m, (self->widget.allocation.width-2*PAD)/self->num_reports, -(self->widget.allocation.height-2*PAD)/(self->ymax - self->ymin));
+    cairo_matrix_translate (&m, 0, -self->ymin);
+    cairo_matrix_invert (&m);
+
+    x = ev->x;
+    y = ev->y;
+    cairo_matrix_transform_point (&m, &x, &y);
+
+    i = floor (x);
+    if (i < 0 || i >= self->num_reports)
+       i = -1;
+
+    if (i != self->selected_report) {
+       self->selected_report = i;
+       gtk_widget_queue_draw (w);
+
+       g_signal_emit (w, signals[REPORT_SELECTED], 0, i);
+    }
+
+    return FALSE;
+}
+
+static gboolean
+graph_view_button_release (GtkWidget     *w,
+                          GdkEventButton *ev)
+{
+    GraphView *self = (GraphView *) w;
+
+    return FALSE;
+}
+
+static void
+graph_view_realize (GtkWidget *widget)
+{
+    GdkWindowAttr attributes;
+
+    GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
+
+    attributes.window_type = GDK_WINDOW_CHILD;
+    attributes.x = widget->allocation.x;
+    attributes.y = widget->allocation.y;
+    attributes.width  = widget->allocation.width;
+    attributes.height = widget->allocation.height;
+    attributes.wclass = GDK_INPUT_OUTPUT;
+    attributes.visual = gtk_widget_get_visual (widget);
+    attributes.colormap = gtk_widget_get_colormap (widget);
+    attributes.event_mask = gtk_widget_get_events (widget) |
+                           GDK_BUTTON_PRESS_MASK |
+                           GDK_BUTTON_RELEASE_MASK |
+                           GDK_EXPOSURE_MASK;
+
+    widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
+                                    &attributes,
+                                    GDK_WA_X | GDK_WA_Y |
+                                    GDK_WA_VISUAL | GDK_WA_COLORMAP);
+    gdk_window_set_user_data (widget->window, widget);
+
+    widget->style = gtk_style_attach (widget->style, widget->window);
+    gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
+}
+
+static void
+graph_view_finalize (GObject *obj)
+{
+    G_OBJECT_CLASS (graph_view_parent_class)->finalize (obj);
+}
+
+static void
+graph_view_class_init (GraphViewClass *klass)
+{
+    GObjectClass *object_class = (GObjectClass *) klass;
+    GtkWidgetClass *widget_class = (GtkWidgetClass *) klass;
+
+    object_class->finalize = graph_view_finalize;
+
+    widget_class->realize = graph_view_realize;
+    widget_class->expose_event = graph_view_expose;
+    widget_class->button_press_event = graph_view_button_press;
+    widget_class->button_release_event = graph_view_button_release;
+
+    signals[REPORT_SELECTED] =
+       g_signal_new ("report-selected",
+                     G_TYPE_FROM_CLASS (object_class),
+                     G_SIGNAL_RUN_FIRST,
+                     0,//G_STRUCT_OFFSET (GraphView, report_selected),
+                     NULL, NULL,
+                     g_cclosure_marshal_VOID__INT,
+                     G_TYPE_NONE, 1, G_TYPE_INT);
+}
+
+static void
+graph_view_init (GraphView *self)
+{
+    self->selected_report = -1;
+}
+
+GtkWidget *
+graph_view_new (void)
+{
+    return g_object_new (graph_view_get_type (), NULL);
+}
+
+void
+graph_view_update_visible (GraphView *gv)
+{
+    double min, max;
+    test_case_t *cases;
+
+    cases = gv->cases;
+
+    min = max = 1.;
+    while (cases->name != NULL) {
+       if (cases->shown) {
+           if (cases->min < min)
+               min = cases->min;
+           if (cases->max > max)
+               max = cases->max;
+       }
+       cases++;
+    }
+    gv->ymin = -1/min + 1;
+    gv->ymax = max - 1;
+
+    gtk_widget_queue_draw (&gv->widget);
+}
+
+void
+graph_view_set_reports (GraphView          *gv,
+                       test_case_t         *cases,
+                       cairo_perf_report_t *reports,
+                       int                  num_reports)
+{
+    /* XXX ownership? */
+    gv->cases = cases;
+    gv->reports = reports;
+    gv->num_reports = num_reports;
+
+    graph_view_update_visible (gv);
+}
diff --git a/perf/cairo-perf-graph.h b/perf/cairo-perf-graph.h
new file mode 100644 (file)
index 0000000..4cb6216
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Copyright © 2008 Chris Wilson
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of the
+ * copyright holders not be used in advertising or publicity
+ * pertaining to distribution of the software without specific,
+ * written prior permission. The copyright holders make no
+ * representations about the suitability of this software for any
+ * purpose.  It is provided "as is" without express or implied
+ * warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ * SOFTWARE.
+ *
+ * Authors: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#ifndef CAIRO_PERF_GRAPH_H
+#define CAIRO_PERF_GRAPH_H
+
+#include <gtk/gtk.h>
+
+#include "cairo-perf.h"
+
+typedef struct _test_case {
+    const char *backend;
+    const char *content;
+    const char *name;
+    int size;
+
+    test_report_t *min_test;
+
+    cairo_bool_t shown;
+    double baseline;
+    double min, max;
+    GdkColor color;
+} test_case_t;
+
+typedef struct _GraphView GraphView;
+
+GtkWidget *
+graph_view_new (void);
+
+void
+graph_view_set_reports (GraphView          *gv,
+                       test_case_t         *tests,
+                       cairo_perf_report_t *reports,
+                       int                  num_reports);
+
+void
+graph_view_update_visible (GraphView *gv);
+
+#endif
diff --git a/perf/cairo-perf-micro.c b/perf/cairo-perf-micro.c
new file mode 100644 (file)
index 0000000..61ed55f
--- /dev/null
@@ -0,0 +1,591 @@
+/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/*
+ * Copyright © 2006 Mozilla Corporation
+ * Copyright © 2006 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * the authors not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. The authors make no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE AUTHORS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Authors: Vladimir Vukicevic <vladimir@pobox.com>
+ *         Carl Worth <cworth@cworth.org>
+ */
+
+#define _GNU_SOURCE 1  /* for sched_getaffinity() */
+
+#include "../cairo-version.h" /* for the real version */
+
+#include "cairo-perf.h"
+#include "cairo-stats.h"
+
+#include "cairo-boilerplate-getopt.h"
+
+/* For basename */
+#ifdef HAVE_LIBGEN_H
+#include <libgen.h>
+#endif
+
+#if HAVE_FCFINI
+#include <fontconfig/fontconfig.h>
+#endif
+
+#ifdef HAVE_SCHED_H
+#include <sched.h>
+#endif
+
+#define CAIRO_PERF_ITERATIONS_DEFAULT          100
+#define CAIRO_PERF_LOW_STD_DEV                 0.03
+#define CAIRO_PERF_STABLE_STD_DEV_COUNT                5
+#define CAIRO_PERF_ITERATION_MS_DEFAULT                2000
+#define CAIRO_PERF_ITERATION_MS_FAST           5
+
+typedef struct _cairo_perf_case {
+    CAIRO_PERF_RUN_DECL (*run);
+    cairo_bool_t (*enabled) (cairo_perf_t *perf);
+    unsigned int min_size;
+    unsigned int max_size;
+} cairo_perf_case_t;
+
+const cairo_perf_case_t perf_cases[];
+
+static const char *
+_content_to_string (cairo_content_t content,
+                   cairo_bool_t    similar)
+{
+    switch (content|similar) {
+    case CAIRO_CONTENT_COLOR:
+       return "rgb";
+    case CAIRO_CONTENT_COLOR|1:
+       return "rgb&";
+    case CAIRO_CONTENT_ALPHA:
+       return "a";
+    case CAIRO_CONTENT_ALPHA|1:
+       return "a&";
+    case CAIRO_CONTENT_COLOR_ALPHA:
+       return "rgba";
+    case CAIRO_CONTENT_COLOR_ALPHA|1:
+       return "rgba&";
+    default:
+       return "<unknown_content>";
+    }
+}
+
+static cairo_bool_t
+cairo_perf_has_similar (cairo_perf_t *perf)
+{
+    cairo_surface_t *target;
+
+    if (getenv ("CAIRO_TEST_SIMILAR") == NULL)
+       return FALSE;
+
+    /* exclude the image backend */
+    target = cairo_get_target (perf->cr);
+    if (cairo_surface_get_type (target) == CAIRO_SURFACE_TYPE_IMAGE)
+       return FALSE;
+
+    return TRUE;
+}
+
+cairo_bool_t
+cairo_perf_can_run (cairo_perf_t *perf,
+                   const char   *name,
+                   cairo_bool_t *is_explicit)
+{
+    unsigned int i;
+
+    if (is_explicit)
+       *is_explicit = FALSE;
+
+    if (perf->num_names == 0)
+       return TRUE;
+
+    for (i = 0; i < perf->num_names; i++) {
+       if (strstr (name, perf->names[i])) {
+           if (is_explicit)
+               *is_explicit = FALSE;
+           return TRUE;
+       }
+    }
+
+    return FALSE;
+}
+
+static unsigned
+cairo_perf_calibrate (cairo_perf_t     *perf,
+                     cairo_perf_func_t  perf_func)
+{
+    cairo_time_t calibration, calibration_max;
+    unsigned loops, min_loops;
+
+    min_loops = 1;
+    calibration = perf_func (perf->cr, perf->size, perf->size, min_loops);
+
+    if (!perf->fast_and_sloppy) {
+       calibration_max = _cairo_time_from_s (perf->ms_per_iteration * 0.0001 / 4);
+       while (calibration < calibration_max) {
+           min_loops *= 2;
+           calibration = perf_func (perf->cr, perf->size, perf->size, min_loops);
+       }
+    }
+
+    /* XXX
+     * Compute the number of loops required for the timing
+     * interval to be perf->ms_per_iteration milliseconds. This
+     * helps to eliminate sampling variance due to timing and
+     * other systematic errors.  However, it also hides
+     * synchronisation overhead as we attempt to process a large
+     * batch of identical operations in a single shot. This can be
+     * considered both good and bad... It would be good to perform
+     * a more rigorous analysis of the synchronisation overhead,
+     * that is to estimate the time for loop=0.
+     */
+    loops = _cairo_time_from_s (perf->ms_per_iteration * 0.001 * min_loops / calibration);
+    min_loops = perf->fast_and_sloppy ? 1 : 10;
+    if (loops < min_loops)
+       loops = min_loops;
+
+    return loops;
+}
+
+void
+cairo_perf_run (cairo_perf_t      *perf,
+               const char         *name,
+               cairo_perf_func_t   perf_func,
+               cairo_count_func_t  count_func)
+{
+    static cairo_bool_t first_run = TRUE;
+    unsigned int i, similar, has_similar;
+    cairo_time_t *times;
+    cairo_stats_t stats = {0.0, 0.0};
+    int low_std_dev_count;
+
+    if (perf->list_only) {
+       printf ("%s\n", name);
+       return;
+    }
+
+    if (first_run) {
+       if (perf->raw) {
+           printf ("[ # ] %s.%-s %s %s %s ...\n",
+                   "backend", "content", "test-size", "ticks-per-ms", "time(ticks)");
+       }
+
+       if (perf->summary) {
+           fprintf (perf->summary,
+                    "[ # ] %8s.%-4s %28s %8s %8s %5s %5s %s %s\n",
+                    "backend", "content", "test-size", "min(ticks)", "min(ms)", "median(ms)",
+                    "stddev.", "iterations", "overhead");
+       }
+       first_run = FALSE;
+    }
+
+    times = perf->times;
+
+    if (getenv ("CAIRO_PERF_OUTPUT") != NULL) { /* check output */
+       char *filename;
+       cairo_status_t status;
+
+       xasprintf (&filename, "%s.%s.%s.%d.out.png",
+                  name, perf->target->name,
+                  _content_to_string (perf->target->content, 0),
+                  perf->size);
+       cairo_save (perf->cr);
+       perf_func (perf->cr, perf->size, perf->size, 1);
+       cairo_restore (perf->cr);
+       status = cairo_surface_write_to_png (cairo_get_target (perf->cr), filename);
+       if (status) {
+           fprintf (stderr, "Failed to generate output check '%s': %s\n",
+                    filename, cairo_status_to_string (status));
+           return;
+       }
+
+       free (filename);
+    }
+
+    has_similar = cairo_perf_has_similar (perf);
+    for (similar = 0; similar <= has_similar; similar++) {
+       unsigned loops;
+
+       if (perf->summary) {
+           fprintf (perf->summary,
+                    "[%3d] %8s.%-5s %26s.%-3d ",
+                    perf->test_number, perf->target->name,
+                    _content_to_string (perf->target->content, similar),
+                    name, perf->size);
+           fflush (perf->summary);
+       }
+
+       /* We run one iteration in advance to warm caches and calibrate. */
+       cairo_perf_yield ();
+       if (similar)
+           cairo_push_group_with_content (perf->cr,
+                                          cairo_boilerplate_content (perf->target->content));
+       else
+           cairo_save (perf->cr);
+       perf_func (perf->cr, perf->size, perf->size, 1);
+       loops = cairo_perf_calibrate (perf, perf_func);
+       if (similar)
+           cairo_pattern_destroy (cairo_pop_group (perf->cr));
+       else
+           cairo_restore (perf->cr);
+
+       low_std_dev_count = 0;
+       for (i =0; i < perf->iterations; i++) {
+           cairo_perf_yield ();
+           if (similar)
+               cairo_push_group_with_content (perf->cr,
+                                              cairo_boilerplate_content (perf->target->content));
+           else
+               cairo_save (perf->cr);
+           times[i] = perf_func (perf->cr, perf->size, perf->size, loops) ;
+           if (similar)
+               cairo_pattern_destroy (cairo_pop_group (perf->cr));
+           else
+               cairo_restore (perf->cr);
+           if (perf->raw) {
+               if (i == 0)
+                   printf ("[*] %s.%s %s.%d %g",
+                           perf->target->name,
+                           _content_to_string (perf->target->content, similar),
+                           name, perf->size,
+                           _cairo_time_to_double (_cairo_time_from_s (1.)) / 1000.);
+               printf (" %lld", (long long) (times[i] / (double) loops));
+           } else if (! perf->exact_iterations) {
+               if (i > 0) {
+                   _cairo_stats_compute (&stats, times, i+1);
+
+                   if (stats.std_dev <= CAIRO_PERF_LOW_STD_DEV) {
+                       low_std_dev_count++;
+                       if (low_std_dev_count >= CAIRO_PERF_STABLE_STD_DEV_COUNT)
+                           break;
+                   } else {
+                       low_std_dev_count = 0;
+                   }
+               }
+           }
+       }
+
+       if (perf->raw)
+           printf ("\n");
+
+       if (perf->summary) {
+           _cairo_stats_compute (&stats, times, i);
+           if (count_func != NULL) {
+               double count = count_func (perf->cr, perf->size, perf->size);
+               fprintf (perf->summary,
+                        "%.3f [%10lld/%d] %#8.3f %#8.3f %#5.2f%% %3d: %.2f\n",
+                        stats.min_ticks /(double) loops,
+                        (long long) stats.min_ticks, loops,
+                        _cairo_time_to_s (stats.min_ticks) * 1000.0 / loops,
+                        _cairo_time_to_s (stats.median_ticks) * 1000.0 / loops,
+                        stats.std_dev * 100.0, stats.iterations,
+                        count / _cairo_time_to_s (stats.min_ticks));
+           } else {
+               fprintf (perf->summary,
+                        "%.3f [%10lld/%d] %#8.3f %#8.3f %#5.2f%% %3d\n",
+                        stats.min_ticks /(double) loops,
+                        (long long) stats.min_ticks, loops,
+                        _cairo_time_to_s (stats.min_ticks) * 1000.0 / loops,
+                        _cairo_time_to_s (stats.median_ticks) * 1000.0 / loops,
+                        stats.std_dev * 100.0, stats.iterations);
+           }
+           fflush (perf->summary);
+       }
+
+       perf->test_number++;
+    }
+}
+
+static void
+usage (const char *argv0)
+{
+    fprintf (stderr,
+"Usage: %s [-l] [-r] [-v] [-i iterations] [test-names ...]\n"
+"       %s -l\n"
+"\n"
+"Run the cairo performance test suite over the given tests (all by default)\n"
+"The command-line arguments are interpreted as follows:\n"
+"\n"
+"  -r  raw; display each time measurement instead of summary statistics\n"
+"  -v  verbose; in raw mode also show the summaries\n"
+"  -i  iterations; specify the number of iterations per test case\n"
+"  -l  list only; just list selected test case names without executing\n"
+"\n"
+"If test names are given they are used as sub-string matches so a command\n"
+"such as \"cairo-perf text\" can be used to run all text test cases.\n",
+            argv0, argv0);
+}
+
+static void
+parse_options (cairo_perf_t *perf,
+              int           argc,
+              char         *argv[])
+{
+    int c;
+    const char *iters;
+    const char *ms = NULL;
+    char *end;
+    int verbose = 0;
+
+    if ((iters = getenv("CAIRO_PERF_ITERATIONS")) && *iters)
+       perf->iterations = strtol(iters, NULL, 0);
+    else
+       perf->iterations = CAIRO_PERF_ITERATIONS_DEFAULT;
+    perf->exact_iterations = 0;
+
+    perf->fast_and_sloppy = FALSE;
+    perf->ms_per_iteration = CAIRO_PERF_ITERATION_MS_DEFAULT;
+    if ((ms = getenv("CAIRO_PERF_ITERATION_MS")) && *ms) {
+       perf->ms_per_iteration = atof(ms);
+    }
+
+    perf->raw = FALSE;
+    perf->list_only = FALSE;
+    perf->names = NULL;
+    perf->num_names = 0;
+    perf->summary = stdout;
+
+    while (1) {
+       c = _cairo_getopt (argc, argv, "i:lrvf");
+       if (c == -1)
+           break;
+
+       switch (c) {
+       case 'i':
+           perf->exact_iterations = TRUE;
+           perf->iterations = strtoul (optarg, &end, 10);
+           if (*end != '\0') {
+               fprintf (stderr, "Invalid argument for -i (not an integer): %s\n",
+                        optarg);
+               exit (1);
+           }
+           break;
+       case 'l':
+           perf->list_only = TRUE;
+           break;
+       case 'r':
+           perf->raw = TRUE;
+           perf->summary = NULL;
+           break;
+       case 'f':
+           perf->fast_and_sloppy = TRUE;
+           if (ms == NULL)
+               perf->ms_per_iteration = CAIRO_PERF_ITERATION_MS_FAST;
+           break;
+       case 'v':
+           verbose = 1;
+           break;
+       default:
+           fprintf (stderr, "Internal error: unhandled option: %c\n", c);
+           /* fall-through */
+       case '?':
+           usage (argv[0]);
+           exit (1);
+       }
+    }
+
+    if (verbose && perf->summary == NULL)
+       perf->summary = stderr;
+
+    if (optind < argc) {
+       perf->names = &argv[optind];
+       perf->num_names = argc - optind;
+    }
+}
+
+static int 
+check_cpu_affinity (void)
+{
+#ifdef HAVE_SCHED_GETAFFINITY
+
+    cpu_set_t affinity;
+    int i, cpu_count;
+
+    if (sched_getaffinity(0, sizeof(affinity), &affinity)) {
+       perror("sched_getaffinity");
+       return -1;
+    }
+
+    for(i = 0, cpu_count = 0; i < CPU_SETSIZE; ++i) {
+       if (CPU_ISSET(i, &affinity))
+           ++cpu_count;
+    }
+
+    if (cpu_count > 1) {
+       fputs(
+           "WARNING: cairo-perf has not been bound to a single CPU.\n",
+           stderr);
+       return -1;
+    }
+
+    return 0;
+#else
+    fputs(
+       "WARNING: Cannot check CPU affinity for this platform.\n",
+       stderr);
+    return -1;
+#endif
+}
+
+static void
+cairo_perf_fini (cairo_perf_t *perf)
+{
+    cairo_boilerplate_free_targets (perf->targets);
+    cairo_boilerplate_fini ();
+
+    free (perf->times);
+    cairo_debug_reset_static_data ();
+#if HAVE_FCFINI
+    FcFini ();
+#endif
+}
+
+
+int
+main (int   argc,
+      char *argv[])
+{
+    int i, j;
+    cairo_perf_t perf;
+    cairo_surface_t *surface;
+
+    parse_options (&perf, argc, argv);
+
+    if (check_cpu_affinity()) {
+       fputs(
+           "NOTICE: cairo-perf and the X server should be bound to CPUs (either the same\n"
+           "or separate) on SMP systems. Not doing so causes random results when the X\n"
+           "server is moved to or from cairo-perf's CPU during the benchmarks:\n"
+           "\n"
+           "    $ sudo taskset -cp 0 $(pidof X)\n"
+           "    $ taskset -cp 1 $$\n"
+           "\n"
+           "See taskset(1) for information about changing CPU affinity.\n",
+           stderr);
+    }
+
+    perf.targets = cairo_boilerplate_get_targets (&perf.num_targets, NULL);
+    perf.times = xmalloc (perf.iterations * sizeof (cairo_time_t));
+
+    for (i = 0; i < perf.num_targets; i++) {
+       const cairo_boilerplate_target_t *target = perf.targets[i];
+
+       if (! target->is_measurable)
+           continue;
+
+       perf.target = target;
+       perf.test_number = 0;
+
+       for (j = 0; perf_cases[j].run; j++) {
+           const cairo_perf_case_t *perf_case = &perf_cases[j];
+
+           if (! perf_case->enabled (&perf))
+               continue;
+
+           for (perf.size = perf_case->min_size;
+                perf.size <= perf_case->max_size;
+                perf.size *= 2)
+           {
+               void *closure;
+
+               surface = (target->create_surface) (NULL,
+                                                   target->content,
+                                                   perf.size, perf.size,
+                                                   perf.size, perf.size,
+                                                   CAIRO_BOILERPLATE_MODE_PERF,
+                                                   0,
+                                                   &closure);
+               if (surface == NULL) {
+                   fprintf (stderr,
+                            "Error: Failed to create target surface: %s\n",
+                            target->name);
+                   continue;
+               }
+
+               cairo_perf_timer_set_synchronize (target->synchronize, closure);
+
+               perf.cr = cairo_create (surface);
+
+               perf_case->run (&perf, perf.cr, perf.size, perf.size);
+
+               if (cairo_status (perf.cr)) {
+                   fprintf (stderr, "Error: Test left cairo in an error state: %s\n",
+                            cairo_status_to_string (cairo_status (perf.cr)));
+               }
+
+               cairo_destroy (perf.cr);
+               cairo_surface_destroy (surface);
+
+               if (target->cleanup)
+                   target->cleanup (closure);
+           }
+       }
+    }
+
+    cairo_perf_fini (&perf);
+
+    return 0;
+}
+
+#define FUNC(f) f, f##_enabled
+const cairo_perf_case_t perf_cases[] = {
+    { FUNC(pixel),  1, 1 },
+    { FUNC(a1_pixel),  1, 1 },
+    { FUNC(paint),  64, 512},
+    { FUNC(paint_with_alpha),  64, 512},
+    { FUNC(fill),   64, 512},
+    { FUNC(stroke), 64, 512},
+    { FUNC(text),   64, 512},
+    { FUNC(glyphs), 64, 512},
+    { FUNC(mask),   64, 512},
+    { FUNC(line),  32, 512},
+    { FUNC(a1_line),  32, 512},
+    { FUNC(curve),  32, 512},
+    { FUNC(a1_curve),  32, 512},
+    { FUNC(disjoint),   64, 512},
+    { FUNC(hatching),   64, 512},
+    { FUNC(tessellate), 100, 100},
+    { FUNC(subimage_copy), 16, 512},
+    { FUNC(hash_table), 16, 16},
+    { FUNC(pattern_create_radial), 16, 16},
+    { FUNC(zrusin), 415, 415},
+    { FUNC(world_map), 800, 800},
+    { FUNC(box_outline), 100, 100},
+    { FUNC(mosaic), 800, 800 },
+    { FUNC(long_lines), 100, 100},
+    { FUNC(unaligned_clip), 100, 100},
+    { FUNC(rectangles), 512, 512},
+    { FUNC(rounded_rectangles), 512, 512},
+    { FUNC(long_dashed_lines), 512, 512},
+    { FUNC(composite_checker), 16, 512},
+    { FUNC(twin), 800, 800},
+    { FUNC(dragon), 1024, 1024 },
+    { FUNC(sierpinski), 32, 1024 },
+    { FUNC(pythagoras_tree), 768, 768 },
+    { FUNC(intersections), 512, 512 },
+    { FUNC(many_strokes), 32, 512 },
+    { FUNC(wide_strokes), 32, 512 },
+    { FUNC(many_fills), 32, 512 },
+    { FUNC(wide_fills), 32, 512 },
+    { FUNC(many_curves), 32, 512 },
+    { FUNC(spiral), 512, 512 },
+    { FUNC(wave), 500, 500 },
+    { FUNC(fill_clip), 16, 512 },
+    { FUNC(tiger), 16, 1024 },
+    { NULL }
+};
diff --git a/perf/cairo-perf-print.c b/perf/cairo-perf-print.c
new file mode 100644 (file)
index 0000000..146f79b
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * Copyright © 2006 Red Hat, Inc.
+ * Copyright © 2009 Chris Wilson
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of the
+ * copyright holders not be used in advertising or publicity
+ * pertaining to distribution of the software without specific,
+ * written prior permission. The copyright holders make no
+ * representations about the suitability of this software for any
+ * purpose.  It is provided "as is" without express or implied
+ * warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ * SOFTWARE.
+ *
+ * Authors: Carl Worth <cworth@cworth.org>
+ *         Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-perf.h"
+
+#include <stdio.h>
+
+static void
+report_print (const cairo_perf_report_t *report)
+{
+    const test_report_t *tests;
+
+    tests = report->tests;
+    for (tests = report->tests; tests->name != NULL; tests++) {
+       if (tests->stats.iterations == 0)
+           continue;
+
+       if (tests->size) {
+           printf ("%5s-%-4s %26s-%-3d  %6.2f %4.2f%%\n",
+                   tests->backend, tests->content,
+                   tests->name, tests->size,
+                   tests->stats.median_ticks / tests->stats.ticks_per_ms,
+                   tests->stats.std_dev * 100);
+       } else {
+           printf ("%5s %26s  %6.2f %4.2f%%\n",
+                   tests->backend, tests->name,
+                   tests->stats.median_ticks / tests->stats.ticks_per_ms,
+                   tests->stats.std_dev * 100);
+       }
+    }
+}
+
+int
+main (int        argc,
+      const char *argv[])
+{
+    int i;
+
+    for (i = 1; i < argc; i++ ) {
+       cairo_perf_report_t report;
+
+       cairo_perf_report_load (&report, argv[i], NULL);
+       report_print (&report);
+    }
+
+    return 0;
+}
diff --git a/perf/cairo-perf-report.c b/perf/cairo-perf-report.c
new file mode 100644 (file)
index 0000000..8df78c6
--- /dev/null
@@ -0,0 +1,454 @@
+/*
+ * Copyright © 2006 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of the
+ * copyright holders not be used in advertising or publicity
+ * pertaining to distribution of the software without specific,
+ * written prior permission. The copyright holders make no
+ * representations about the suitability of this software for any
+ * purpose.  It is provided "as is" without express or implied
+ * warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ * SOFTWARE.
+ *
+ * Authors: Carl Worth <cworth@cworth.org>
+ */
+
+#include "cairo-missing.h"
+#include "cairo-perf.h"
+#include "cairo-stats.h"
+
+/* We use _GNU_SOURCE for getline and strndup if available. */
+#ifndef _GNU_SOURCE
+# define _GNU_SOURCE
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include <math.h>
+#include <assert.h>
+#ifdef HAVE_LIBGEN_H
+#include <libgen.h>
+#endif
+
+#ifdef _MSC_VER
+static long long
+strtoll (const char  *nptr,
+        char       **endptr,
+        int          base);
+
+static char *
+basename (char *path);
+#endif
+
+/* Ad-hoc parsing, macros with a strong dependence on the calling
+ * context, and plenty of other ugliness is here.  But at least it's
+ * not perl... */
+#define parse_error(...) fprintf(stderr, __VA_ARGS__); return TEST_REPORT_STATUS_ERROR;
+#define skip_char(c)                                                   \
+do {                                                                   \
+    if (*s && *s == (c)) {                                             \
+       s++;                                                            \
+    } else {                                                           \
+        parse_error ("expected '%c' but found '%c'", c, *s);           \
+    }                                                                  \
+} while (0)
+#define skip_space() while (*s && (*s == ' ' || *s == '\t')) s++;
+#define parse_int(result)                                              \
+do {                                                                   \
+    (result) = strtol (s, &end, 10);                                   \
+    if (*s && end != s) {                                              \
+       s = end;                                                        \
+    } else {                                                           \
+       parse_error("expected integer but found %s", s);                \
+    }                                                                  \
+} while (0)
+#define parse_long_long(result)                                        \
+do {                                                                   \
+    (result) = strtoll (s, &end, 10);                                  \
+    if (*s && end != s) {                                              \
+       s = end;                                                        \
+    } else {                                                           \
+       parse_error("expected integer but found %s", s);                \
+    }                                                                  \
+} while (0)
+#define parse_double(result)                                           \
+do {                                                                   \
+    (result) = strtod (s, &end);                                       \
+    if (*s && end != s) {                                              \
+       s = end;                                                        \
+    } else {                                                           \
+       parse_error("expected floating-point value but found %s", s);   \
+    }                                                                  \
+} while (0)
+/* Here a string is simply a sequence of non-whitespace */
+#define parse_string(result)                                           \
+do {                                                                   \
+    for (end = s; *end; end++)                                         \
+       if (isspace (*end))                                             \
+           break;                                                      \
+    (result) = strndup (s, end - s);                                   \
+    if ((result) == NULL) {                                            \
+       fprintf (stderr, "Out of memory.\n");                           \
+       exit (1);                                                       \
+    }                                                                  \
+    s = end;                                                           \
+} while (0)
+
+static test_report_status_t
+test_report_parse (test_report_t *report,
+                  int fileno,
+                  char          *line,
+                  char          *configuration)
+{
+    char *end;
+    char *s = line;
+    cairo_bool_t is_raw = FALSE;
+    double min_time, median_time;
+
+    /* The code here looks funny unless you understand that these are
+     * all macro calls, (and then the code just looks sick). */
+    if (*s == '\n')
+       return TEST_REPORT_STATUS_COMMENT;
+
+    skip_char ('[');
+    skip_space ();
+    if (*s == '#')
+       return TEST_REPORT_STATUS_COMMENT;
+    if (*s == '*') {
+       s++;
+       is_raw = TRUE;
+    } else {
+       parse_int (report->id);
+    }
+    skip_char (']');
+
+    skip_space ();
+
+    report->fileno = fileno;
+    report->configuration = configuration;
+    parse_string (report->backend);
+    end = strrchr (report->backend, '.');
+    if (end)
+       *end++ = '\0';
+    report->content = end ? end : xstrdup ("???");
+
+    skip_space ();
+
+    parse_string (report->name);
+    end = strrchr (report->name, '.');
+    if (end)
+       *end++ = '\0';
+    report->size = end ? atoi (end) : 0;
+
+    skip_space ();
+
+    report->samples = NULL;
+    report->samples_size = 0;
+    report->samples_count = 0;
+
+    if (is_raw) {
+       parse_double (report->stats.ticks_per_ms);
+       skip_space ();
+
+       report->samples_size = 5;
+       report->samples = xmalloc (report->samples_size * sizeof (cairo_time_t));
+       report->stats.min_ticks = 0;
+       do {
+           if (report->samples_count == report->samples_size) {
+               report->samples_size *= 2;
+               report->samples = xrealloc (report->samples,
+                                           report->samples_size * sizeof (cairo_time_t));
+           }
+           parse_long_long (report->samples[report->samples_count]);
+           if (report->samples_count == 0) {
+               report->stats.min_ticks =
+                   report->samples[report->samples_count];
+           } else if (report->stats.min_ticks >
+                      report->samples[report->samples_count]){
+               report->stats.min_ticks =
+                   report->samples[report->samples_count];
+           }
+           report->samples_count++;
+           skip_space ();
+       } while (*s && *s != '\n');
+       report->stats.iterations = 0;
+       skip_char ('\n');
+    } else {
+       parse_double (report->stats.min_ticks);
+       skip_space ();
+
+       parse_double (min_time);
+       report->stats.ticks_per_ms = report->stats.min_ticks / min_time;
+
+       skip_space ();
+
+       parse_double (median_time);
+       report->stats.median_ticks = median_time * report->stats.ticks_per_ms;
+
+       skip_space ();
+
+       parse_double (report->stats.std_dev);
+       report->stats.std_dev /= 100.0;
+       skip_char ('%');
+
+       skip_space ();
+
+       parse_int (report->stats.iterations);
+
+       skip_space ();
+       skip_char ('\n');
+    }
+
+    return TEST_REPORT_STATUS_SUCCESS;
+}
+
+/* We provide hereafter a win32 implementation of the basename
+ * and strtoll functions which are not available otherwise.
+ * The basename function is fully compliant to its GNU specs.
+ */
+#ifdef _MSC_VER
+long long
+strtoll (const char  *nptr,
+        char       **endptr,
+        int          base)
+{
+    return _atoi64(nptr);
+}
+
+static char *
+basename (char *path)
+{
+    char *end, *s;
+
+    end = (path + strlen(path) - 1);
+    while (end && (end >= path + 1) && (*end == '/')) {
+       *end = '\0';
+       end--;
+    }
+
+    s = strrchr(path, '/');
+    if (s) {
+       if (s == end) {
+           return s;
+       } else {
+           return s+1;
+       }
+    } else {
+       return path;
+    }
+}
+#endif /* ifndef _MSC_VER */
+
+int
+test_report_cmp_backend_then_name (const void *a,
+                                  const void *b)
+{
+    const test_report_t *a_test = a;
+    const test_report_t *b_test = b;
+
+    int cmp;
+
+    cmp = strcmp (a_test->backend, b_test->backend);
+    if (cmp)
+       return cmp;
+
+    cmp = strcmp (a_test->content, b_test->content);
+    if (cmp)
+       return cmp;
+
+    /* A NULL name is a list-termination marker, so force it last. */
+    if (a_test->name == NULL)
+       if (b_test->name == NULL)
+           return 0;
+       else
+           return 1;
+    else if (b_test->name == NULL)
+       return -1;
+
+    cmp = strcmp (a_test->name, b_test->name);
+    if (cmp)
+       return cmp;
+
+    if (a_test->size < b_test->size)
+       return -1;
+    if (a_test->size > b_test->size)
+       return 1;
+
+    return 0;
+}
+
+int
+test_report_cmp_name (const void *a,
+                     const void *b)
+{
+    const test_report_t *a_test = a;
+    const test_report_t *b_test = b;
+
+    int cmp;
+
+    /* A NULL name is a list-termination marker, so force it last. */
+    if (a_test->name == NULL)
+       if (b_test->name == NULL)
+           return 0;
+       else
+           return 1;
+    else if (b_test->name == NULL)
+       return -1;
+
+    cmp = strcmp (a_test->name, b_test->name);
+    if (cmp)
+       return cmp;
+
+    if (a_test->size < b_test->size)
+       return -1;
+    if (a_test->size > b_test->size)
+       return 1;
+
+    return 0;
+}
+
+void
+cairo_perf_report_sort_and_compute_stats (cairo_perf_report_t *report,
+                                         int (*cmp) (const void*, const void*))
+{
+    test_report_t *base, *next, *last, *t;
+
+    if (cmp == NULL)
+       cmp = test_report_cmp_backend_then_name;
+
+    /* First we sort, since the diff needs both lists in the same
+     * order */
+    qsort (report->tests, report->tests_count, sizeof (test_report_t), cmp);
+
+    /* The sorting also brings all related raw reports together so we
+     * can condense them and compute the stats.
+     */
+    base = &report->tests[0];
+    last = &report->tests[report->tests_count - 1];
+    while (base <= last) {
+       next = base+1;
+       if (next <= last) {
+           while (next <= last &&
+                  test_report_cmp_backend_then_name (base, next) == 0)
+           {
+               next++;
+           }
+           if (next != base) {
+               unsigned int new_samples_count = base->samples_count;
+               for (t = base + 1; t < next; t++)
+                   new_samples_count += t->samples_count;
+               if (new_samples_count > base->samples_size) {
+                   base->samples_size = new_samples_count;
+                   base->samples = xrealloc (base->samples,
+                                             base->samples_size * sizeof (cairo_time_t));
+               }
+               for (t = base + 1; t < next; t++) {
+                   memcpy (&base->samples[base->samples_count], t->samples,
+                           t->samples_count * sizeof (cairo_time_t));
+                   base->samples_count += t->samples_count;
+               }
+           }
+       }
+       if (base->samples)
+           _cairo_stats_compute (&base->stats, base->samples, base->samples_count);
+       base = next;
+    }
+}
+
+void
+cairo_perf_report_load (cairo_perf_report_t *report,
+                       const char *filename, int id,
+                       int (*cmp) (const void *, const void *))
+{
+    FILE *file;
+    test_report_status_t status;
+    int line_number = 0;
+    char *line = NULL;
+    size_t line_size = 0;
+    char *configuration;
+    char *dot;
+    char *baseName;
+    const char *name;
+
+    name = filename;
+    if (name == NULL)
+       name = "stdin";
+
+    configuration = xmalloc (strlen (name) * sizeof (char) + 1);
+    strcpy (configuration, name);
+    baseName = basename (configuration);
+    report->configuration = xmalloc (strlen (baseName) * sizeof (char) + 1);
+    strcpy (report->configuration, baseName);
+    free (configuration);
+
+    dot = strrchr (report->configuration, '.');
+    if (dot)
+       *dot = '\0';
+
+    report->name = name;
+    report->tests_size = 16;
+    report->tests = xmalloc (report->tests_size * sizeof (test_report_t));
+    report->tests_count = 0;
+    report->fileno = id;
+
+    if (filename == NULL) {
+       file = stdin;
+    } else {
+       file = fopen (filename, "r");
+       if (file == NULL) {
+           fprintf (stderr, "Failed to open %s: %s\n",
+                    filename, strerror (errno));
+           exit (1);
+       }
+    }
+
+    while (1) {
+       if (report->tests_count == report->tests_size) {
+           report->tests_size *= 2;
+           report->tests = xrealloc (report->tests,
+                                     report->tests_size * sizeof (test_report_t));
+       }
+
+       line_number++;
+       if (getline (&line, &line_size, file) == -1)
+           break;
+
+       status = test_report_parse (&report->tests[report->tests_count],
+                                   id, line, report->configuration);
+       if (status == TEST_REPORT_STATUS_ERROR)
+           fprintf (stderr, "Ignoring unrecognized line %d of %s:\n%s",
+                    line_number, filename, line);
+       if (status == TEST_REPORT_STATUS_SUCCESS)
+           report->tests_count++;
+       /* Do nothing on TEST_REPORT_STATUS_COMMENT */
+    }
+
+    free (line);
+
+    if (filename != NULL)
+       fclose (file);
+
+    cairo_perf_report_sort_and_compute_stats (report, cmp);
+
+    /* Add one final report with a NULL name to terminate the list. */
+    if (report->tests_count == report->tests_size) {
+       report->tests_size *= 2;
+       report->tests = xrealloc (report->tests,
+                                 report->tests_size * sizeof (test_report_t));
+    }
+    report->tests[report->tests_count].name = NULL;
+}
diff --git a/perf/cairo-perf-trace.c b/perf/cairo-perf-trace.c
new file mode 100644 (file)
index 0000000..8e637a7
--- /dev/null
@@ -0,0 +1,1050 @@
+/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/*
+ * Copyright © 2006 Mozilla Corporation
+ * Copyright © 2006 Red Hat, Inc.
+ * Copyright © 2009 Chris Wilson
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * the authors not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. The authors make no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE AUTHORS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Authors: Vladimir Vukicevic <vladimir@pobox.com>
+ *         Carl Worth <cworth@cworth.org>
+ *         Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#define _GNU_SOURCE 1  /* for sched_getaffinity() and getline() */
+
+#include "../cairo-version.h" /* for the real version */
+
+#include "cairo-missing.h"
+#include "cairo-perf.h"
+#include "cairo-stats.h"
+
+#include "cairo-boilerplate-getopt.h"
+#include <cairo-script-interpreter.h>
+#include <cairo-types-private.h> /* for INTERNAL_SURFACE_TYPE */
+
+/* rudely reuse bits of the library... */
+#include "../src/cairo-hash-private.h"
+#include "../src/cairo-error-private.h"
+
+/* For basename */
+#ifdef HAVE_LIBGEN_H
+#include <libgen.h>
+#endif
+#include <ctype.h> /* isspace() */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#ifdef _MSC_VER
+#include "dirent-win32.h"
+
+static char *
+basename_no_ext (char *path)
+{
+    static char name[_MAX_FNAME + 1];
+
+    _splitpath (path, NULL, NULL, name, NULL);
+
+    name[_MAX_FNAME] = '\0';
+
+    return name;
+}
+
+
+#else
+#include <dirent.h>
+
+static char *
+basename_no_ext (char *path)
+{
+    char *dot, *name;
+
+    name = basename (path);
+
+    dot = strrchr (name, '.');
+    if (dot)
+       *dot = '\0';
+
+    return name;
+}
+
+#endif
+
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <signal.h>
+
+#if HAVE_FCFINI
+#include <fontconfig/fontconfig.h>
+#endif
+
+#define CAIRO_PERF_ITERATIONS_DEFAULT  15
+#define CAIRO_PERF_LOW_STD_DEV         0.05
+#define CAIRO_PERF_MIN_STD_DEV_COUNT   3
+#define CAIRO_PERF_STABLE_STD_DEV_COUNT 3
+
+struct trace {
+    const cairo_boilerplate_target_t *target;
+    void            *closure;
+    cairo_surface_t *surface;
+    cairo_bool_t observe;
+    int tile_size;
+};
+
+cairo_bool_t
+cairo_perf_can_run (cairo_perf_t *perf,
+                   const char   *name,
+                   cairo_bool_t *is_explicit)
+{
+    unsigned int i;
+    char *copy, *dot;
+    cairo_bool_t ret;
+
+    if (is_explicit)
+       *is_explicit = FALSE;
+
+    if (perf->exact_names) {
+       if (is_explicit)
+           *is_explicit = TRUE;
+       return TRUE;
+    }
+
+    if (perf->num_names == 0 && perf->num_exclude_names == 0)
+       return TRUE;
+
+    copy = xstrdup (name);
+    dot = strrchr (copy, '.');
+    if (dot != NULL)
+       *dot = '\0';
+
+    if (perf->num_names) {
+       ret = TRUE;
+       for (i = 0; i < perf->num_names; i++)
+           if (strstr (copy, perf->names[i])) {
+               if (is_explicit)
+                   *is_explicit = strcmp (copy, perf->names[i]) == 0;
+               goto check_exclude;
+           }
+
+       ret = FALSE;
+       goto done;
+    }
+
+check_exclude:
+    if (perf->num_exclude_names) {
+       ret = FALSE;
+       for (i = 0; i < perf->num_exclude_names; i++)
+           if (strstr (copy, perf->exclude_names[i])) {
+               if (is_explicit)
+                   *is_explicit = strcmp (copy, perf->exclude_names[i]) == 0;
+               goto done;
+           }
+
+       ret = TRUE;
+       goto done;
+    }
+
+done:
+    free (copy);
+
+    return ret;
+}
+
+static void
+clear_surface (cairo_surface_t *surface)
+{
+    cairo_t *cr = cairo_create (surface);
+    cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
+    cairo_paint (cr);
+    cairo_destroy (cr);
+}
+
+struct scache {
+    cairo_hash_entry_t entry;
+    cairo_content_t content;
+    int width, height;
+    cairo_surface_t *surface;
+};
+
+static cairo_hash_table_t *surface_cache;
+static cairo_surface_t *surface_holdovers[16];
+
+static cairo_bool_t
+scache_equal (const void *A,
+             const void *B)
+{
+    const struct scache *a = A, *b = B;
+    return a->entry.hash == b->entry.hash;
+}
+
+#define ARRAY_SIZE(A) (sizeof(A)/sizeof(A[0]))
+static void
+scache_mark_active (cairo_surface_t *surface)
+{
+    cairo_surface_t *t0, *t1;
+    unsigned n;
+
+    if (surface_cache == NULL)
+       return;
+
+    t0 = cairo_surface_reference (surface);
+    for (n = 0; n < ARRAY_SIZE (surface_holdovers); n++) {
+       if (surface_holdovers[n] == surface) {
+           surface_holdovers[n] = t0;
+           t0 = surface;
+           break;
+       }
+
+       t1 = surface_holdovers[n];
+       surface_holdovers[n] = t0;
+       t0 = t1;
+    }
+    cairo_surface_destroy (t0);
+}
+
+static void
+scache_clear (void)
+{
+    unsigned n;
+
+    if (surface_cache == NULL)
+       return;
+
+    for (n = 0; n < ARRAY_SIZE (surface_holdovers); n++) {
+       cairo_surface_destroy (surface_holdovers[n]);
+       surface_holdovers[n] = NULL;
+    }
+}
+
+static void
+scache_remove (void *closure)
+{
+    _cairo_hash_table_remove (surface_cache, closure);
+    free (closure);
+}
+
+static cairo_surface_t *
+_similar_surface_create (void           *closure,
+                        cairo_content_t  content,
+                        double           width,
+                        double           height,
+                        long             uid)
+{
+    struct trace *args = closure;
+    cairo_surface_t *surface;
+    struct scache skey, *s;
+
+    if (args->observe)
+           return cairo_surface_create_similar (args->surface,
+                                                content, width, height);
+
+    if (uid == 0 || surface_cache == NULL)
+       return args->target->create_similar (args->surface, content, width, height);
+
+    skey.entry.hash = uid;
+    s = _cairo_hash_table_lookup (surface_cache, &skey.entry);
+    if (s != NULL) {
+       if (s->content == content &&
+           s->width   == width   &&
+           s->height  == height)
+       {
+           return cairo_surface_reference (s->surface);
+       }
+
+       /* The surface has been resized, allow the original entry to expire
+        * as it becomes inactive.
+        */
+    }
+
+    surface = args->target->create_similar (args->surface, content, width, height);
+    s = malloc (sizeof (struct scache));
+    if (s == NULL)
+       return surface;
+
+    s->entry.hash = uid;
+    s->content = content;
+    s->width = width;
+    s->height = height;
+    s->surface = surface;
+    if (_cairo_hash_table_insert (surface_cache, &s->entry)) {
+       free (s);
+    } else if (cairo_surface_set_user_data
+              (surface,
+               (const cairo_user_data_key_t *) &surface_cache,
+               s, scache_remove))
+    {
+       scache_remove (s);
+    }
+
+    return surface;
+}
+
+static cairo_t *
+_context_create (void           *closure,
+                cairo_surface_t *surface)
+{
+    scache_mark_active (surface);
+    return cairo_create (surface);
+}
+
+static int user_interrupt;
+
+static void
+interrupt (int sig)
+{
+    if (user_interrupt) {
+       signal (sig, SIG_DFL);
+       raise (sig);
+    }
+
+    user_interrupt = 1;
+}
+
+static void
+describe (cairo_perf_t *perf,
+          void *closure)
+{
+    char *description = NULL;
+
+    if (perf->has_described_backend)
+           return;
+    perf->has_described_backend = TRUE;
+
+    if (perf->target->describe)
+        description = perf->target->describe (closure);
+
+    if (description == NULL)
+        return;
+
+    if (perf->raw) {
+        printf ("[ # ] %s: %s\n", perf->target->name, description);
+    }
+
+    if (perf->summary) {
+        fprintf (perf->summary,
+                 "[ # ] %8s: %s\n",
+                 perf->target->name,
+                 description);
+    }
+
+    free (description);
+}
+
+static void
+usage (const char *argv0)
+{
+    fprintf (stderr,
+"Usage: %s [-l] [-r] [-v] [-i iterations] [test-names ... | traces ...]\n"
+"       %s -l\n"
+"\n"
+"Run the cairo performance test suite over the given tests (all by default)\n"
+"The command-line arguments are interpreted as follows:\n"
+"\n"
+"  -r  raw; display each time measurement instead of summary statistics\n"
+"  -s  sync; only sum the elapsed time of the indiviual operations\n"
+"  -v  verbose; in raw mode also show the summaries\n"
+"  -i  iterations; specify the number of iterations per test case\n"
+"  -x   exclude; specify a file to read a list of traces to exclude\n"
+"  -l  list only; just list selected test case names without executing\n"
+"\n"
+"If test names are given they are used as sub-string matches so a command\n"
+"such as \"cairo-perf-trace firefox\" can be used to run all firefox traces.\n"
+"Alternatively, you can specify a list of filenames to execute.\n",
+            argv0, argv0);
+}
+
+static cairo_bool_t
+read_excludes (cairo_perf_t *perf,
+              const char   *filename)
+{
+    FILE *file;
+    char *line = NULL;
+    size_t line_size = 0;
+    char *s, *t;
+
+    file = fopen (filename, "r");
+    if (file == NULL)
+       return FALSE;
+
+    while (getline (&line, &line_size, file) != -1) {
+       /* terminate the line at a comment marker '#' */
+       s = strchr (line, '#');
+       if (s)
+           *s = '\0';
+
+       /* whitespace delimits */
+       s = line;
+       while (*s != '\0' && isspace (*s))
+           s++;
+
+       t = s;
+       while (*t != '\0' && ! isspace (*t))
+           t++;
+
+       if (s != t) {
+           int i = perf->num_exclude_names;
+           perf->exclude_names = xrealloc (perf->exclude_names,
+                                           sizeof (char *) * (i+1));
+           perf->exclude_names[i] = strndup (s, t-s);
+           perf->num_exclude_names++;
+       }
+    }
+    free (line);
+
+    fclose (file);
+
+    return TRUE;
+}
+
+static void
+parse_options (cairo_perf_t *perf,
+              int           argc,
+              char         *argv[])
+{
+    int c;
+    const char *iters;
+    char *end;
+    int verbose = 0;
+    int use_surface_cache = 0;
+
+    if ((iters = getenv ("CAIRO_PERF_ITERATIONS")) && *iters)
+       perf->iterations = strtol (iters, NULL, 0);
+    else
+       perf->iterations = CAIRO_PERF_ITERATIONS_DEFAULT;
+    perf->exact_iterations = 0;
+
+    perf->raw = FALSE;
+    perf->observe = FALSE;
+    perf->list_only = FALSE;
+    perf->tile_size = 0;
+    perf->names = NULL;
+    perf->num_names = 0;
+    perf->summary = stdout;
+    perf->summary_continuous = FALSE;
+    perf->exclude_names = NULL;
+    perf->num_exclude_names = 0;
+
+    while (1) {
+       c = _cairo_getopt (argc, argv, "t:i:x:lsrvc");
+       if (c == -1)
+           break;
+
+       switch (c) {
+       case 'i':
+           perf->exact_iterations = TRUE;
+           perf->iterations = strtoul (optarg, &end, 10);
+           if (*end != '\0') {
+               fprintf (stderr, "Invalid argument for -i (not an integer): %s\n",
+                        optarg);
+               exit (1);
+           }
+           break;
+       case 't':
+           perf->tile_size = strtoul (optarg, &end, 10);
+           if (*end != '\0') {
+               fprintf (stderr, "Invalid argument for -t (not an integer): %s\n",
+                        optarg);
+               exit (1);
+           }
+           break;
+       case 'l':
+           perf->list_only = TRUE;
+           break;
+       case 'r':
+           perf->raw = TRUE;
+           perf->summary = NULL;
+           break;
+       case 's':
+           perf->observe = TRUE;
+           break;
+       case 'v':
+           verbose = 1;
+           break;
+       case 'c':
+           use_surface_cache = 1;
+           break;
+       case 'x':
+           if (! read_excludes (perf, optarg)) {
+               fprintf (stderr, "Invalid argument for -x (not readable file): %s\n",
+                        optarg);
+               exit (1);
+           }
+           break;
+       default:
+           fprintf (stderr, "Internal error: unhandled option: %c\n", c);
+           /* fall-through */
+       case '?':
+           usage (argv[0]);
+           exit (1);
+       }
+    }
+
+    if (perf->observe && perf->tile_size) {
+       fprintf (stderr, "Can't mix observer and tiling. Sorry.\n");
+       exit (1);
+    }
+
+    if (verbose && perf->summary == NULL)
+       perf->summary = stderr;
+#if HAVE_UNISTD_H
+    if (perf->summary && isatty (fileno (perf->summary)))
+       perf->summary_continuous = TRUE;
+#endif
+
+    if (optind < argc) {
+       perf->names = &argv[optind];
+       perf->num_names = argc - optind;
+    }
+
+    if (use_surface_cache)
+       surface_cache = _cairo_hash_table_create (scache_equal);
+}
+
+static void
+cairo_perf_fini (cairo_perf_t *perf)
+{
+    cairo_boilerplate_free_targets (perf->targets);
+    cairo_boilerplate_fini ();
+
+    free (perf->times);
+    cairo_debug_reset_static_data ();
+#if HAVE_FCFINI
+    FcFini ();
+#endif
+}
+
+static cairo_bool_t
+have_trace_filenames (cairo_perf_t *perf)
+{
+    unsigned int i;
+
+    if (perf->num_names == 0)
+       return FALSE;
+
+#if HAVE_UNISTD_H
+    for (i = 0; i < perf->num_names; i++)
+       if (access (perf->names[i], R_OK) == 0)
+           return TRUE;
+#endif
+
+    return FALSE;
+}
+
+static void
+_tiling_surface_finish (cairo_surface_t *observer,
+                       cairo_surface_t *target,
+                       void *closure)
+{
+    struct trace *args = closure;
+    cairo_surface_t *surface;
+    cairo_content_t content;
+    cairo_rectangle_t r;
+    int width, height;
+    int x, y, w, h;
+
+    cairo_recording_surface_get_extents (target, &r);
+    w = r.width;
+    h = r.height;
+
+    content = cairo_surface_get_content (target);
+
+    for (y = 0; y < h; y += args->tile_size) {
+       height = args->tile_size;
+       if (y + height > h)
+           height = h - y;
+
+       for (x = 0; x < w; x += args->tile_size) {
+           cairo_t *cr;
+
+           width = args->tile_size;
+           if (x + width > w)
+               width = w - x;
+
+           /* XXX to correctly observe the playback we would need
+            * to replay the target onto the observer directly.
+            */
+           surface = args->target->create_similar (args->surface,
+                                                   content, width, height);
+
+           cr = cairo_create (surface);
+           cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+           cairo_set_source_surface (cr, target, -x, -y);
+           cairo_paint (cr);
+           cairo_destroy (cr);
+
+           cairo_surface_destroy (surface);
+       }
+    }
+}
+
+static cairo_surface_t *
+_tiling_surface_create (void            *closure,
+                       cairo_content_t  content,
+                       double            width,
+                       double            height,
+                       long              uid)
+{
+    cairo_rectangle_t r;
+    cairo_surface_t *surface, *observer;
+
+    r.x = r.y = 0;
+    r.width = width;
+    r.height = height;
+
+    surface = cairo_recording_surface_create (content, &r);
+    observer = cairo_surface_create_observer (surface,
+                                             CAIRO_SURFACE_OBSERVER_NORMAL);
+    cairo_surface_destroy (surface);
+
+    cairo_surface_observer_add_finish_callback (observer,
+                                               _tiling_surface_finish,
+                                               closure);
+
+    return observer;
+}
+
+static void
+cairo_perf_trace (cairo_perf_t                    *perf,
+                 const cairo_boilerplate_target_t *target,
+                 const char                       *trace)
+{
+    static cairo_bool_t first_run = TRUE;
+    unsigned int i;
+    cairo_time_t *times, *paint, *mask, *fill, *stroke, *glyphs;
+    cairo_stats_t stats = {0.0, 0.0};
+    struct trace args = { target };
+    int low_std_dev_count;
+    char *trace_cpy, *name;
+    const cairo_script_interpreter_hooks_t hooks = {
+       &args,
+       perf->tile_size ? _tiling_surface_create : _similar_surface_create,
+       NULL, /* surface_destroy */
+       _context_create,
+       NULL, /* context_destroy */
+       NULL, /* show_page */
+       NULL /* copy_page */
+    };
+
+    args.tile_size = perf->tile_size;
+    args.observe = perf->observe;
+
+    trace_cpy = xstrdup (trace);
+    name = basename_no_ext (trace_cpy);
+
+    if (perf->list_only) {
+       printf ("%s\n", name);
+       free (trace_cpy);
+       return;
+    }
+
+    if (first_run) {
+       if (perf->raw) {
+           printf ("[ # ] %s.%-s %s %s %s ...\n",
+                   "backend", "content", "test-size", "ticks-per-ms", "time(ticks)");
+       }
+
+       if (perf->summary) {
+           if (perf->observe) {
+               fprintf (perf->summary,
+                        "[ # ] %8s %28s  %9s %9s %9s %9s %9s %9s %5s\n",
+                        "backend", "test",
+                        "total(s)", "paint(s)", "mask(s)", "fill(s)", "stroke(s)", "glyphs(s)",
+                        "count");
+           } else {
+               fprintf (perf->summary,
+                        "[ # ] %8s %28s %8s %5s %5s %s\n",
+                        "backend", "test", "min(s)", "median(s)",
+                        "stddev.", "count");
+           }
+       }
+       first_run = FALSE;
+    }
+
+    times = perf->times;
+    paint = times + perf->iterations;
+    mask = paint + perf->iterations;
+    stroke = mask + perf->iterations;
+    fill = stroke + perf->iterations;
+    glyphs = fill + perf->iterations;
+
+    low_std_dev_count = 0;
+    for (i = 0; i < perf->iterations && ! user_interrupt; i++) {
+       cairo_script_interpreter_t *csi;
+       cairo_status_t status;
+       unsigned int line_no;
+
+       args.surface = target->create_surface (NULL,
+                                              CAIRO_CONTENT_COLOR_ALPHA,
+                                              1, 1,
+                                              1, 1,
+                                              CAIRO_BOILERPLATE_MODE_PERF,
+                                              0,
+                                              &args.closure);
+       if (perf->observe) {
+           cairo_surface_t *obs;
+           obs = cairo_surface_create_observer (args.surface,
+                                                CAIRO_SURFACE_OBSERVER_NORMAL);
+           cairo_surface_destroy (args.surface);
+           args.surface = obs;
+       }
+       if (cairo_surface_status (args.surface)) {
+           fprintf (stderr,
+                    "Error: Failed to create target surface: %s\n",
+                    target->name);
+           return;
+       }
+
+       cairo_perf_timer_set_synchronize (target->synchronize, args.closure);
+
+       if (i == 0) {
+           describe (perf, args.closure);
+           if (perf->summary) {
+               fprintf (perf->summary,
+                        "[%3d] %8s %28s ",
+                        perf->test_number,
+                        perf->target->name,
+                        name);
+               fflush (perf->summary);
+           }
+       }
+
+       csi = cairo_script_interpreter_create ();
+       cairo_script_interpreter_install_hooks (csi, &hooks);
+
+       if (! perf->observe) {
+           cairo_perf_yield ();
+           cairo_perf_timer_start ();
+       }
+
+       cairo_script_interpreter_run (csi, trace);
+       line_no = cairo_script_interpreter_get_line_number (csi);
+
+       /* Finish before querying timings in case we are using an intermediate
+        * target and so need to destroy all surfaces before rendering
+        * commences.
+        */
+       cairo_script_interpreter_finish (csi);
+
+       if (perf->observe) {
+           cairo_device_t *observer = cairo_surface_get_device (args.surface);
+           times[i] = _cairo_time_from_s (1.e-9 * cairo_device_observer_elapsed (observer));
+           paint[i] = _cairo_time_from_s (1.e-9 * cairo_device_observer_paint_elapsed (observer));
+           mask[i] = _cairo_time_from_s (1.e-9 * cairo_device_observer_mask_elapsed (observer));
+           stroke[i] = _cairo_time_from_s (1.e-9 * cairo_device_observer_stroke_elapsed (observer));
+           fill[i] = _cairo_time_from_s (1.e-9 * cairo_device_observer_fill_elapsed (observer));
+           glyphs[i] = _cairo_time_from_s (1.e-9 * cairo_device_observer_glyphs_elapsed (observer));
+       } else {
+           clear_surface (args.surface); /* queue a write to the sync'ed surface */
+           cairo_perf_timer_stop ();
+           times[i] = cairo_perf_timer_elapsed ();
+       }
+
+       scache_clear ();
+
+       cairo_surface_destroy (args.surface);
+
+       if (target->cleanup)
+           target->cleanup (args.closure);
+
+       status = cairo_script_interpreter_destroy (csi);
+       if (status) {
+           if (perf->summary) {
+               fprintf (perf->summary, "Error during replay, line %d: %s\n",
+                        line_no,
+                        cairo_status_to_string (status));
+           }
+           goto out;
+       }
+
+       if (perf->raw) {
+           if (i == 0)
+               printf ("[*] %s.%s %s.%d %g",
+                       perf->target->name,
+                       "rgba",
+                       name,
+                       0,
+                       _cairo_time_to_double (_cairo_time_from_s (1)) / 1000.);
+           printf (" %lld", (long long) times[i]);
+           fflush (stdout);
+       } else if (! perf->exact_iterations) {
+           if (i > CAIRO_PERF_MIN_STD_DEV_COUNT) {
+               _cairo_stats_compute (&stats, times, i+1);
+
+               if (stats.std_dev <= CAIRO_PERF_LOW_STD_DEV) {
+                   if (++low_std_dev_count >= CAIRO_PERF_STABLE_STD_DEV_COUNT)
+                       break;
+               } else {
+                   low_std_dev_count = 0;
+               }
+           }
+       }
+
+       if (perf->summary && perf->summary_continuous) {
+           _cairo_stats_compute (&stats, times, i+1);
+
+           fprintf (perf->summary,
+                    "\r[%3d] %8s %28s ",
+                    perf->test_number,
+                    perf->target->name,
+                    name);
+           if (perf->observe) {
+               fprintf (perf->summary,
+                        " %#9.3f", _cairo_time_to_s (stats.median_ticks));
+
+               _cairo_stats_compute (&stats, paint, i+1);
+               fprintf (perf->summary,
+                        " %#9.3f", _cairo_time_to_s (stats.median_ticks));
+
+               _cairo_stats_compute (&stats, mask, i+1);
+               fprintf (perf->summary,
+                        " %#9.3f", _cairo_time_to_s (stats.median_ticks));
+
+               _cairo_stats_compute (&stats, fill, i+1);
+               fprintf (perf->summary,
+                        " %#9.3f", _cairo_time_to_s (stats.median_ticks));
+
+               _cairo_stats_compute (&stats, stroke, i+1);
+               fprintf (perf->summary,
+                        " %#9.3f", _cairo_time_to_s (stats.median_ticks));
+
+               _cairo_stats_compute (&stats, glyphs, i+1);
+               fprintf (perf->summary,
+                        " %#9.3f", _cairo_time_to_s (stats.median_ticks));
+
+               fprintf (perf->summary,
+                        " %5d", i+1);
+           } else {
+               fprintf (perf->summary,
+                        "%#8.3f %#8.3f %#6.2f%% %4d/%d",
+                        _cairo_time_to_s (stats.min_ticks),
+                        _cairo_time_to_s (stats.median_ticks),
+                        stats.std_dev * 100.0,
+                        stats.iterations, i+1);
+           }
+           fflush (perf->summary);
+       }
+    }
+    user_interrupt = 0;
+
+    if (perf->summary) {
+       _cairo_stats_compute (&stats, times, i);
+       if (perf->summary_continuous) {
+           fprintf (perf->summary,
+                    "\r[%3d] %8s %28s ",
+                    perf->test_number,
+                    perf->target->name,
+                    name);
+       }
+       if (perf->observe) {
+           fprintf (perf->summary,
+                    " %#9.3f", _cairo_time_to_s (stats.median_ticks));
+
+           _cairo_stats_compute (&stats, paint, i);
+           fprintf (perf->summary,
+                    " %#9.3f", _cairo_time_to_s (stats.median_ticks));
+
+           _cairo_stats_compute (&stats, mask, i);
+           fprintf (perf->summary,
+                    " %#9.3f", _cairo_time_to_s (stats.median_ticks));
+
+           _cairo_stats_compute (&stats, fill, i);
+           fprintf (perf->summary,
+                    " %#9.3f", _cairo_time_to_s (stats.median_ticks));
+
+           _cairo_stats_compute (&stats, stroke, i);
+           fprintf (perf->summary,
+                    " %#9.3f", _cairo_time_to_s (stats.median_ticks));
+
+           _cairo_stats_compute (&stats, glyphs, i);
+           fprintf (perf->summary,
+                    " %#9.3f", _cairo_time_to_s (stats.median_ticks));
+
+           fprintf (perf->summary,
+                    " %5d\n", i);
+       } else {
+           fprintf (perf->summary,
+                    "%#8.3f %#8.3f %#6.2f%% %4d/%d\n",
+                    _cairo_time_to_s (stats.min_ticks),
+                    _cairo_time_to_s (stats.median_ticks),
+                    stats.std_dev * 100.0,
+                    stats.iterations, i);
+       }
+       fflush (perf->summary);
+    }
+
+out:
+    if (perf->raw) {
+       printf ("\n");
+       fflush (stdout);
+    }
+
+    perf->test_number++;
+    free (trace_cpy);
+}
+
+static void
+warn_no_traces (const char *message,
+               const char *trace_dir)
+{
+    fprintf (stderr,
+"Error: %s '%s'.\n"
+"Have you cloned the cairo-traces repository and uncompressed the traces?\n"
+"  git clone git://anongit.freedesktop.org/cairo-traces\n"
+"  cd cairo-traces && make\n"
+"Or set the env.var CAIRO_TRACE_DIR to point to your traces?\n",
+           message, trace_dir);
+}
+
+static int
+cairo_perf_trace_dir (cairo_perf_t                    *perf,
+                     const cairo_boilerplate_target_t *target,
+                     const char                       *dirname)
+{
+    DIR *dir;
+    struct dirent *de;
+    int num_traces = 0;
+    cairo_bool_t force;
+    cairo_bool_t is_explicit;
+
+    dir = opendir (dirname);
+    if (dir == NULL)
+       return 0;
+
+    force = FALSE;
+    if (cairo_perf_can_run (perf, dirname, &is_explicit))
+       force = is_explicit;
+
+    while ((de = readdir (dir)) != NULL) {
+       char *trace;
+       struct stat st;
+
+       if (de->d_name[0] == '.')
+           continue;
+
+       xasprintf (&trace, "%s/%s", dirname, de->d_name);
+       if (stat (trace, &st) != 0)
+           goto next;
+
+       if (S_ISDIR(st.st_mode)) {
+           num_traces += cairo_perf_trace_dir (perf, target, trace);
+       } else {
+           const char *dot;
+
+           dot = strrchr (de->d_name, '.');
+           if (dot == NULL)
+               goto next;
+           if (strcmp (dot, ".trace"))
+               goto next;
+
+           num_traces++;
+           if (!force && ! cairo_perf_can_run (perf, de->d_name, NULL))
+               goto next;
+
+           cairo_perf_trace (perf, target, trace);
+       }
+next:
+       free (trace);
+
+    }
+    closedir (dir);
+
+    return num_traces;
+}
+
+int
+main (int   argc,
+      char *argv[])
+{
+    cairo_perf_t perf;
+    const char *trace_dir = "cairo-traces:/usr/src/cairo-traces:/usr/share/cairo-traces";
+    unsigned int n;
+    int i;
+
+    parse_options (&perf, argc, argv);
+
+    signal (SIGINT, interrupt);
+
+    if (getenv ("CAIRO_TRACE_DIR") != NULL)
+       trace_dir = getenv ("CAIRO_TRACE_DIR");
+
+    perf.targets = cairo_boilerplate_get_targets (&perf.num_targets, NULL);
+    perf.times = xmalloc (6 * perf.iterations * sizeof (cairo_time_t));
+
+    /* do we have a list of filenames? */
+    perf.exact_names = have_trace_filenames (&perf);
+
+    for (i = 0; i < perf.num_targets; i++) {
+       const cairo_boilerplate_target_t *target = perf.targets[i];
+
+       if (! perf.list_only && ! target->is_measurable)
+           continue;
+
+       perf.target = target;
+       perf.test_number = 0;
+       perf.has_described_backend = FALSE;
+
+       if (perf.exact_names) {
+           for (n = 0; n < perf.num_names; n++) {
+               struct stat st;
+
+               if (stat (perf.names[n], &st) == 0) {
+                   if (S_ISDIR (st.st_mode)) {
+                       cairo_perf_trace_dir (&perf, target, perf.names[n]);
+                   } else
+                       cairo_perf_trace (&perf, target, perf.names[n]);
+               }
+           }
+       } else {
+           int num_traces = 0;
+           const char *dir;
+
+           dir = trace_dir;
+           do {
+               char buf[1024];
+               const char *end = strchr (dir, ':');
+               if (end != NULL) {
+                   memcpy (buf, dir, end-dir);
+                   buf[end-dir] = '\0';
+                   end++;
+
+                   dir = buf;
+               }
+
+               num_traces += cairo_perf_trace_dir (&perf, target, dir);
+               dir = end;
+           } while (dir != NULL);
+
+           if (num_traces == 0) {
+               warn_no_traces ("Found no traces in", trace_dir);
+               return 1;
+           }
+       }
+
+       if (perf.list_only)
+           break;
+    }
+
+    cairo_perf_fini (&perf);
+
+    return 0;
+}
diff --git a/perf/cairo-perf.c b/perf/cairo-perf.c
new file mode 100644 (file)
index 0000000..7f2ff5b
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2007 Netlabs
+ * Copyright (c) 2006 Mozilla Corporation
+ * Copyright (c) 2006 Red Hat, Inc.
+ * Copyright (c) 2011 Andrea Canciani
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * the authors not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. The authors make no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE AUTHORS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Authors: Peter Weilbacher <mozilla@weilbacher.org>
+ *         Vladimir Vukicevic <vladimir@pobox.com> (win32/linux code)
+ *         Carl Worth <cworth@cworth.org> (win32/linux code)
+ *          Andrea Canciani <ranma42@gmail.com>
+ */
+
+#include "cairo-perf.h"
+#include "../src/cairo-time-private.h"
+
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#if defined(__OS2__)
+#define INCL_BASE
+#include <os2.h>
+#elif defined(_WIN32)
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#elif defined(_POSIX_PRIORITY_SCHEDULING)
+#include <sched.h>
+#endif
+
+
+/* timers */
+static cairo_time_t timer;
+static cairo_perf_timer_synchronize_t cairo_perf_timer_synchronize = NULL;
+static void *cairo_perf_timer_synchronize_closure = NULL;
+
+void
+cairo_perf_timer_set_synchronize (cairo_perf_timer_synchronize_t  synchronize,
+                                 void                           *closure)
+{
+    cairo_perf_timer_synchronize = synchronize;
+    cairo_perf_timer_synchronize_closure = closure;
+}
+
+void
+cairo_perf_timer_start (void)
+{
+    timer = _cairo_time_get ();
+}
+
+void
+cairo_perf_timer_stop (void)
+{
+    timer = _cairo_time_get_delta (timer);
+}
+
+cairo_time_t
+cairo_perf_timer_elapsed (void)
+{
+    return timer;
+}
+
+void
+cairo_perf_yield (void)
+{
+    /* try to deactivate this thread until the scheduler calls it again */
+
+#if defined(__OS2__)
+    DosSleep (0);
+#elif defined(_WIN32)
+    SleepEx(0, TRUE);
+#elif defined(_POSIX_PRIORITY_SCHEDULING)
+    sched_yield ();
+#else
+    sleep (0);
+#endif
+}
diff --git a/perf/cairo-perf.h b/perf/cairo-perf.h
new file mode 100644 (file)
index 0000000..feab74b
--- /dev/null
@@ -0,0 +1,233 @@
+/*
+ * Copyright © 2006 Mozilla Corporation
+ * Copyright © 2006 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * the authors not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. The authors make no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE AUTHORS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Authors: Vladimir Vukicevic <vladimir@pobox.com>
+ *         Carl Worth <cworth@cworth.org>
+ */
+
+#ifndef _CAIRO_PERF_H_
+#define _CAIRO_PERF_H_
+
+#include "cairo-boilerplate.h"
+#include "../src/cairo-time-private.h"
+#include <stdio.h>
+
+typedef struct _cairo_stats {
+    cairo_time_t min_ticks;
+    cairo_time_t median_ticks;
+    double ticks_per_ms;
+    double std_dev;
+    int iterations;
+} cairo_stats_t;
+
+/* timers */
+
+void
+cairo_perf_timer_start (void);
+
+void
+cairo_perf_timer_stop (void);
+
+typedef void
+(*cairo_perf_timer_synchronize_t) (void *closure);
+
+void
+cairo_perf_timer_set_synchronize (cairo_perf_timer_synchronize_t  synchronize,
+                                 void                           *closure);
+
+cairo_time_t
+cairo_perf_timer_elapsed (void);
+
+/* yield */
+
+void
+cairo_perf_yield (void);
+
+/* running a test case */
+typedef struct _cairo_perf {
+    FILE *summary;
+    cairo_bool_t summary_continuous;
+
+    /* Options from command-line */
+    unsigned int iterations;
+    cairo_bool_t exact_iterations;
+    cairo_bool_t raw;
+    cairo_bool_t list_only;
+    cairo_bool_t observe;
+    char **names;
+    unsigned int num_names;
+    char **exclude_names;
+    unsigned int num_exclude_names;
+    cairo_bool_t exact_names;
+
+    double ms_per_iteration;
+    cairo_bool_t fast_and_sloppy;
+
+    unsigned int tile_size;
+
+    /* Stuff used internally */
+    cairo_time_t *times;
+    const cairo_boilerplate_target_t **targets;
+    int num_targets;
+    const cairo_boilerplate_target_t *target;
+    cairo_bool_t has_described_backend;
+    unsigned int test_number;
+    unsigned int size;
+    cairo_t *cr;
+} cairo_perf_t;
+
+typedef cairo_time_t
+(*cairo_perf_func_t) (cairo_t *cr, int width, int height, int loops);
+
+typedef double
+(*cairo_count_func_t) (cairo_t *cr, int width, int height);
+
+cairo_bool_t
+cairo_perf_can_run (cairo_perf_t *perf,
+                   const char   *name,
+                   cairo_bool_t *is_explicit);
+
+void
+cairo_perf_run (cairo_perf_t      *perf,
+               const char         *name,
+               cairo_perf_func_t   perf_func,
+               cairo_count_func_t  count_func);
+
+void
+cairo_perf_cover_sources_and_operators (cairo_perf_t      *perf,
+                                       const char         *name,
+                                       cairo_perf_func_t   perf_func,
+                                       cairo_count_func_t  count_func);
+
+/* reporter convenience routines */
+
+typedef struct _test_report {
+    int id;
+    int fileno;
+    const char *configuration;
+    char *backend;
+    char *content;
+    char *name;
+    int size;
+
+    /* The samples only exists for "raw" reports */
+    cairo_time_t *samples;
+    unsigned int samples_size;
+    unsigned int samples_count;
+
+    /* The stats are either read directly or computed from samples.
+     * If the stats have not yet been computed from samples, then
+     * iterations will be 0. */
+    cairo_stats_t stats;
+} test_report_t;
+
+typedef struct _test_diff {
+    test_report_t **tests;
+    int num_tests;
+    double min;
+    double max;
+    double change;
+} test_diff_t;
+
+typedef struct _cairo_perf_report {
+    char *configuration;
+    const char *name;
+    int fileno;
+    test_report_t *tests;
+    int tests_size;
+    int tests_count;
+} cairo_perf_report_t;
+
+typedef enum {
+    TEST_REPORT_STATUS_SUCCESS,
+    TEST_REPORT_STATUS_COMMENT,
+    TEST_REPORT_STATUS_ERROR
+} test_report_status_t;
+
+void
+cairo_perf_report_load (cairo_perf_report_t *report,
+                       const char *filename, int id,
+                       int (*cmp) (const void *, const void *));
+
+void
+cairo_perf_report_sort_and_compute_stats (cairo_perf_report_t *report,
+                                         int (*cmp) (const void *, const void *));
+
+int
+test_report_cmp_backend_then_name (const void *a,
+                                  const void *b);
+
+int
+test_report_cmp_name (const void *a,
+                     const void *b);
+
+#define CAIRO_PERF_ENABLED_DECL(func) cairo_bool_t (func ## _enabled) (cairo_perf_t *perf)
+#define CAIRO_PERF_RUN_DECL(func) void (func) (cairo_perf_t *perf, cairo_t *cr, int width, int height)
+
+#define CAIRO_PERF_DECL(func) CAIRO_PERF_RUN_DECL(func); CAIRO_PERF_ENABLED_DECL(func)
+
+CAIRO_PERF_DECL (fill);
+CAIRO_PERF_DECL (paint);
+CAIRO_PERF_DECL (paint_with_alpha);
+CAIRO_PERF_DECL (mask);
+CAIRO_PERF_DECL (stroke);
+CAIRO_PERF_DECL (subimage_copy);
+CAIRO_PERF_DECL (disjoint);
+CAIRO_PERF_DECL (hatching);
+CAIRO_PERF_DECL (tessellate);
+CAIRO_PERF_DECL (text);
+CAIRO_PERF_DECL (glyphs);
+CAIRO_PERF_DECL (hash_table);
+CAIRO_PERF_DECL (pattern_create_radial);
+CAIRO_PERF_DECL (zrusin);
+CAIRO_PERF_DECL (world_map);
+CAIRO_PERF_DECL (box_outline);
+CAIRO_PERF_DECL (mosaic);
+CAIRO_PERF_DECL (long_lines);
+CAIRO_PERF_DECL (unaligned_clip);
+CAIRO_PERF_DECL (rectangles);
+CAIRO_PERF_DECL (rounded_rectangles);
+CAIRO_PERF_DECL (long_dashed_lines);
+CAIRO_PERF_DECL (composite_checker);
+CAIRO_PERF_DECL (twin);
+CAIRO_PERF_DECL (dragon);
+CAIRO_PERF_DECL (pythagoras_tree);
+CAIRO_PERF_DECL (intersections);
+CAIRO_PERF_DECL (spiral);
+CAIRO_PERF_DECL (wave);
+CAIRO_PERF_DECL (many_strokes);
+CAIRO_PERF_DECL (wide_strokes);
+CAIRO_PERF_DECL (many_fills);
+CAIRO_PERF_DECL (wide_fills);
+CAIRO_PERF_DECL (many_curves);
+CAIRO_PERF_DECL (curve);
+CAIRO_PERF_DECL (a1_curve);
+CAIRO_PERF_DECL (line);
+CAIRO_PERF_DECL (a1_line);
+CAIRO_PERF_DECL (pixel);
+CAIRO_PERF_DECL (a1_pixel);
+CAIRO_PERF_DECL (sierpinski);
+CAIRO_PERF_DECL (fill_clip);
+CAIRO_PERF_DECL (tiger);
+
+#endif
diff --git a/perf/cairo-stats.c b/perf/cairo-stats.c
new file mode 100644 (file)
index 0000000..1e25aef
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * Copyright © 2006 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * the authors not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. The authors make no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE AUTHORS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Authors: Carl Worth <cworth@cworth.org>
+ */
+
+#include "cairo-stats.h"
+
+#include <assert.h>
+
+void
+_cairo_stats_compute (cairo_stats_t *stats,
+                     cairo_time_t  *values,
+                     int            num_values)
+{
+    cairo_time_t sum, mean, delta, q1, q3, iqr;
+    cairo_time_t outlier_min, outlier_max;
+    int i, min_valid, num_valid;
+    double s;
+
+    assert (num_values > 0);
+
+    if (num_values == 1) {
+       stats->min_ticks = stats->median_ticks = values[0];
+       stats->std_dev = 0;
+       stats->iterations = 1;
+       return;
+    }
+
+    /* First, identify any outliers, using the definition of "mild
+     * outliers" from:
+     *
+     *         http://en.wikipedia.org/wiki/Outliers
+     *
+     * Which is that outliers are any values less than Q1 - 1.5 * IQR
+     * or greater than Q3 + 1.5 * IQR where Q1 and Q3 are the first
+     * and third quartiles and IQR is the inter-quartile range (Q3 -
+     * Q1).
+     */
+    qsort (values, num_values, sizeof (cairo_time_t), _cairo_time_cmp);
+
+    q1 = values[1*num_values/4];
+    q3 = values[3*num_values/4];
+
+    /* XXX assumes we have native uint64_t */
+    iqr = q3 - q1;
+    outlier_min = q1 - 3 * iqr / 2;
+    outlier_max = q3 + 3 * iqr / 2;
+
+    for (i = 0; i < num_values && values[i] < outlier_min; i++)
+       ;
+    min_valid = i;
+
+    for (i = 0; i < num_values && values[i] <= outlier_max; i++)
+       ;
+    num_valid = i - min_valid;
+    assert(num_valid);
+
+    stats->iterations = num_valid;
+    stats->min_ticks = values[min_valid];
+    stats->median_ticks = values[min_valid + num_valid / 2];
+
+    sum = 0;
+    for (i = min_valid; i < min_valid + num_valid; i++)
+       sum = _cairo_time_add (sum, values[i]);
+    mean = sum / num_valid;
+
+    /* Let's use a normalized std. deviation for easier comparison. */
+    s = 0;
+    for (i = min_valid; i < min_valid + num_valid; i++) {
+       double delta = (values[i] - mean) / (double)mean;
+       s += delta * delta;
+    }
+    stats->std_dev = sqrt(s / num_valid);
+}
diff --git a/perf/cairo-stats.h b/perf/cairo-stats.h
new file mode 100644 (file)
index 0000000..8406e65
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright © 2006 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * the authors not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. The authors make no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE AUTHORS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Authors: Carl Worth <cworth@cworth.org>
+ */
+
+#ifndef _CAIRO_STATS_H_
+#define _CAIRO_STATS_H_
+
+#include "cairo-perf.h"
+
+void
+_cairo_stats_compute (cairo_stats_t *stats,
+                     cairo_time_t  *values,
+                     int            num_values);
+
+#endif /* _CAIRO_STATS_H_ */
diff --git a/perf/dirent-win32.h b/perf/dirent-win32.h
new file mode 100644 (file)
index 0000000..0f2ed05
--- /dev/null
@@ -0,0 +1,102 @@
+/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/*
+ * Copyright 2011 Andrea Canciani
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * the authors not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. The authors make no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE AUTHORS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Authors: Andrea Canciani <ranma42@gmail.com>
+ */
+
+#include "cairo-compiler-private.h"
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+#define stat _stat
+
+#define S_ISDIR(s) ((s) & _S_IFDIR)
+
+struct dirent {
+    ino_t d_ino;
+    char d_name[FILENAME_MAX + 1];
+};
+
+typedef struct _DIR {
+    HANDLE handle;
+    cairo_bool_t has_next;
+    WIN32_FIND_DATA data;
+    struct dirent de;
+} DIR;
+
+static DIR *
+opendir(const char *dirname)
+{
+    DIR *dirp;
+
+    dirp = malloc (sizeof (*dirp));
+    if (unlikely (dirp == NULL))
+       return NULL;
+
+    dirp->handle = FindFirstFile(dirname, &dirp->data);
+
+    if (unlikely (dirp->handle == INVALID_HANDLE_VALUE)) {
+       free (dirp);
+       return NULL;
+    }
+
+    memcpy (dirp->de.d_name, dirp->data.cFileName,
+           sizeof (dirp->data.cFileName));
+    dirp->de.d_name[FILENAME_MAX] = '\0';
+
+    dirp->has_next = TRUE;
+
+    return dirp;
+}
+
+static int
+closedir(DIR *dirp)
+{
+    int ret;
+
+    ret = ! FindClose (dirp->handle);
+
+    free (dirp);
+
+    /* TODO: set errno */
+
+    return ret;
+}
+
+static struct dirent *
+readdir(DIR *dirp)
+{
+    if (! dirp->has_next)
+       return NULL;
+
+    /* COMPILE_TIME_ASSERT (FILENAME_MAX == sizeof (dirp->data.cFileName)); */
+
+    memcpy (dirp->de.d_name, dirp->data.cFileName,
+           sizeof (dirp->data.cFileName));
+    dirp->de.d_name[FILENAME_MAX] = '\0';
+
+    dirp->has_next = FindNextFile (dirp->handle, &dirp->data);
+
+    return &dirp->de;
+}
diff --git a/perf/make-html.py b/perf/make-html.py
new file mode 100755 (executable)
index 0000000..0b45335
--- /dev/null
@@ -0,0 +1,88 @@
+#!/usr/bin/python
+
+from string import strip
+from sys import stdin
+
+targets = {}
+smilies = {'slowdown': '&#9785;' , 'speedup': '&#9786;'}
+
+for line in stdin:
+       line = map(strip, filter(None, line.split(' ')))
+
+       if 9 == len(line):
+               target, name = line[0:2]
+               factor, dir = line[-2:]
+
+               name = name.split('-')
+               name, size = '-'.join(name[:-1]), name[-1]
+
+               target_tests = targets.get(target, {})
+               name_tests = target_tests.get(name, {})
+
+               name_tests[int(size)] = (factor, dir)
+               target_tests[name] = name_tests
+               targets[target] = target_tests
+
+print '''\
+<html><head>
+<title>Performance Changes</title>
+<style type="text/css">/*<![CDATA[*/
+    body { background: white; color: black; }
+    table { border-collapse: collapse; }
+
+    th, td { border: 1px solid silver; padding: 0.2em; }
+    td { text-align: center; }
+    th:first-child { text-align: left; }
+    th { background: #eee; }
+
+ /* those colors also should work for color blinds */
+    td.slowdown { background: #f93; }
+    td.speedup { background: #6f9; }
+/*]]>*/</style>
+</head><body>
+<h1>Performance Changes</h1>'''
+
+targets = targets.items()
+targets.sort(lambda a, b: cmp(a[0], b[0]))
+
+for target, names in targets:
+       sizes = {}
+
+       for tests in names.values():
+               for size in tests.keys():
+                       sizes[size] = True      
+
+       sizes = sizes.keys()
+       sizes.sort()
+
+       names = names.items()
+       names.sort(lambda a, b: cmp(a[0], b[0]))
+
+       print '<h2><a name="%s">%s</a></h2>' % (target, target)
+       print '<table><thead><tr><th>&nbsp;</th>'
+
+       for size in sizes:
+               print '<th>%s</th>' % size
+
+       print '</tr></thead><tbody>'
+
+       for name, tests in names:
+               print '<tr><th>%s</th>' % name
+               
+               for size in sizes:
+                       result = tests.get(size)
+
+                       if result:
+                               factor, dir = result
+                               print '<td class="%s">%s %s</td>' % (
+                                       dir, factor, smilies[dir])
+
+                       else:
+                               print '<td>&nbsp;</td>'
+
+               print '</tr>'
+
+
+       print '</tbody></table>'
+
+print '</body></html>'
diff --git a/perf/micro/Makefile.am b/perf/micro/Makefile.am
new file mode 100644 (file)
index 0000000..3edbf53
--- /dev/null
@@ -0,0 +1,16 @@
+include $(top_srcdir)/build/Makefile.am.common
+
+include $(top_srcdir)/perf/micro/Makefile.sources
+
+noinst_LTLIBRARIES = libcairo-perf-micro.la
+libcairo_perf_micro_la_SOURCES = \
+       $(libcairo_perf_micro_sources)  \
+       $(libcairo_perf_micro_headers)
+
+AM_CPPFLAGS =                          \
+       -I$(srcdir)                     \
+       -I$(top_srcdir)/boilerplate     \
+       -I$(top_srcdir)/src             \
+       -I$(top_srcdir)/perf            \
+       -I$(top_builddir)/src           \
+       $(CAIRO_CFLAGS)
diff --git a/perf/micro/Makefile.sources b/perf/micro/Makefile.sources
new file mode 100644 (file)
index 0000000..19ead17
--- /dev/null
@@ -0,0 +1,51 @@
+libcairo_perf_micro_sources = \
+       cairo-perf-cover.c      \
+       box-outline.c           \
+       composite-checker.c     \
+       disjoint.c              \
+       fill.c                  \
+       hatching.c              \
+       hash-table.c            \
+       line.c                  \
+       a1-line.c               \
+       long-lines.c            \
+       mosaic.c                \
+       paint.c                 \
+       paint-with-alpha.c      \
+       mask.c                  \
+       pattern_create_radial.c \
+       rectangles.c            \
+       rounded-rectangles.c    \
+       stroke.c                \
+       subimage_copy.c         \
+       tessellate.c            \
+       text.c                  \
+       tiger.c                 \
+       glyphs.c                \
+       twin.c                  \
+       unaligned-clip.c        \
+       wave.c                  \
+       world-map.c             \
+       zrusin.c                \
+       long-dashed-lines.c     \
+       dragon.c                \
+       pythagoras-tree.c       \
+       intersections.c         \
+       many-strokes.c          \
+       wide-strokes.c          \
+       many-fills.c            \
+       wide-fills.c            \
+       many-curves.c           \
+       curve.c                 \
+       a1-curve.c              \
+       spiral.c                \
+       pixel.c                 \
+       sierpinski.c            \
+       fill-clip.c             \
+       $(NULL)
+
+libcairo_perf_micro_headers = \
+       mosaic.h                \
+       world-map.h             \
+       zrusin-another.h        \
+       $(NULL)
diff --git a/perf/micro/Makefile.win32 b/perf/micro/Makefile.win32
new file mode 100644 (file)
index 0000000..f41f781
--- /dev/null
@@ -0,0 +1,12 @@
+top_srcdir = ../..
+include $(top_srcdir)/build/Makefile.win32.common
+include $(top_srcdir)/perf/micro/Makefile.sources
+
+CFLAGS += -I$(top_srcdir)/perf -I$(top_srcdir)/boilerplate/
+
+OBJECTS = $(patsubst %.c, $(CFG)/%-static.obj, $(libcairo_perf_micro_sources))
+
+all: inform $(CFG)/libcairo-perf-micro.lib
+
+$(CFG)/libcairo-perf-micro.lib: $(OBJECTS)
+       @$(AR) $(CAIRO_ARFLAGS) -OUT:$@ $(OBJECTS)
diff --git a/perf/micro/a1-curve.c b/perf/micro/a1-curve.c
new file mode 100644 (file)
index 0000000..594c46d
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ * Copyright © 2011 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-perf.h"
+
+static uint32_t state;
+
+static double
+uniform_random (double minval, double maxval)
+{
+    static uint32_t const poly = 0x9a795537U;
+    uint32_t n = 32;
+    while (n-->0)
+       state = 2*state < state ? (2*state ^ poly) : 2*state;
+    return minval + state * (maxval - minval) / 4294967296.0;
+}
+
+static cairo_time_t
+do_curve_stroke (cairo_t *cr, int width, int height, int loops)
+{
+    state = 0xc0ffee;
+    cairo_set_line_width (cr, 2.);
+    cairo_perf_timer_start ();
+
+    while (loops--) {
+       double x1 = uniform_random (0, width);
+       double x2 = uniform_random (0, width);
+       double x3 = uniform_random (0, width);
+       double y1 = uniform_random (0, height);
+       double y2 = uniform_random (0, height);
+       double y3 = uniform_random (0, height);
+       cairo_move_to (cr, uniform_random (0, width), uniform_random (0, height));
+       cairo_curve_to (cr, x1, y1, x2, y2, x3, y3);
+       cairo_stroke(cr);
+    }
+
+    cairo_perf_timer_stop ();
+
+    return cairo_perf_timer_elapsed ();
+}
+
+static cairo_time_t
+do_curve_fill (cairo_t *cr, int width, int height, int loops)
+{
+    state = 0xc0ffee;
+    cairo_perf_timer_start ();
+
+    while (loops--) {
+       double x0 = uniform_random (0, width);
+       double x1 = uniform_random (0, width);
+       double x2 = uniform_random (0, width);
+       double x3 = uniform_random (0, width);
+       double xm = uniform_random (0, width);
+       double xn = uniform_random (0, width);
+       double y0 = uniform_random (0, height);
+       double y1 = uniform_random (0, height);
+       double y2 = uniform_random (0, height);
+       double y3 = uniform_random (0, height);
+       double ym = uniform_random (0, height);
+       double yn = uniform_random (0, height);
+
+       cairo_move_to (cr, xm, ym);
+       cairo_curve_to (cr, x1, y1, x2, y2, xn, yn);
+       cairo_curve_to (cr, x3, y3, x0, y0, xm, ym);
+       cairo_close_path (cr);
+
+       cairo_fill(cr);
+    }
+
+    cairo_perf_timer_stop ();
+
+    return cairo_perf_timer_elapsed ();
+}
+
+cairo_bool_t
+a1_curve_enabled (cairo_perf_t *perf)
+{
+    return cairo_perf_can_run (perf, "a1-curve", NULL);
+}
+
+void
+a1_curve (cairo_perf_t *perf, cairo_t *cr, int width, int height)
+{
+    cairo_set_source_rgb (cr, 1., 1., 1.);
+    cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
+
+    cairo_perf_run (perf, "a1-curve-stroked", do_curve_stroke, NULL);
+    cairo_perf_run (perf, "a1-curve-filled", do_curve_fill, NULL);
+}
diff --git a/perf/micro/a1-line.c b/perf/micro/a1-line.c
new file mode 100644 (file)
index 0000000..ae86602
--- /dev/null
@@ -0,0 +1,223 @@
+/*
+ * Copyright © 2011 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-perf.h"
+
+static cairo_time_t
+horizontal (cairo_t *cr, int width, int height, int loops)
+{
+    double h = height/2 + .5;
+
+    cairo_move_to (cr, 0, h);
+    cairo_line_to (cr, width, h);
+
+    cairo_perf_timer_start ();
+
+    while (loops--)
+       cairo_stroke_preserve (cr);
+
+    cairo_perf_timer_stop ();
+
+    cairo_new_path (cr);
+
+    return cairo_perf_timer_elapsed ();
+}
+
+static cairo_time_t
+horizontal_hair (cairo_t *cr, int width, int height, int loops)
+{
+    cairo_set_line_width (cr, 1.);
+    return horizontal (cr, width, height, loops);
+}
+
+static cairo_time_t
+horizontal_wide (cairo_t *cr, int width, int height, int loops)
+{
+    cairo_set_line_width (cr, 5.);
+    return horizontal (cr, width, height, loops);
+}
+
+static cairo_time_t
+nearly_horizontal (cairo_t *cr, int width, int height, int loops)
+{
+    double h = height/2;
+
+    cairo_move_to (cr, 0, h);
+    cairo_line_to (cr, width, h+1);
+
+    cairo_perf_timer_start ();
+
+    while (loops--)
+       cairo_stroke_preserve (cr);
+
+    cairo_perf_timer_stop ();
+
+    cairo_new_path (cr);
+
+    return cairo_perf_timer_elapsed ();
+}
+
+static cairo_time_t
+nearly_horizontal_hair (cairo_t *cr, int width, int height, int loops)
+{
+    cairo_set_line_width (cr, 1.);
+    return nearly_horizontal (cr, width, height, loops);
+}
+
+static cairo_time_t
+nearly_horizontal_wide (cairo_t *cr, int width, int height, int loops)
+{
+    cairo_set_line_width (cr, 5.);
+    return nearly_horizontal (cr, width, height, loops);
+}
+
+
+static cairo_time_t
+vertical (cairo_t *cr, int width, int height, int loops)
+{
+    double w = width/2 + .5;
+
+    cairo_move_to (cr, w, 0);
+    cairo_line_to (cr, w, height);
+
+    cairo_perf_timer_start ();
+
+    while (loops--)
+       cairo_stroke_preserve (cr);
+
+    cairo_perf_timer_stop ();
+
+    cairo_new_path (cr);
+
+    return cairo_perf_timer_elapsed ();
+}
+
+static cairo_time_t
+vertical_hair (cairo_t *cr, int width, int height, int loops)
+{
+    cairo_set_line_width (cr, 1.);
+    return vertical (cr, width, height, loops);
+}
+
+static cairo_time_t
+vertical_wide (cairo_t *cr, int width, int height, int loops)
+{
+    cairo_set_line_width (cr, 5.);
+    return vertical (cr, width, height, loops);
+}
+
+static cairo_time_t
+nearly_vertical (cairo_t *cr, int width, int height, int loops)
+{
+    double w = width/2;
+
+    cairo_move_to (cr, w, 0);
+    cairo_line_to (cr, w+1, height);
+
+    cairo_perf_timer_start ();
+
+    while (loops--)
+       cairo_stroke_preserve (cr);
+
+    cairo_perf_timer_stop ();
+
+    cairo_new_path (cr);
+
+    return cairo_perf_timer_elapsed ();
+}
+
+static cairo_time_t
+nearly_vertical_hair (cairo_t *cr, int width, int height, int loops)
+{
+    cairo_set_line_width (cr, 1.);
+    return nearly_vertical (cr, width, height, loops);
+}
+
+static cairo_time_t
+nearly_vertical_wide (cairo_t *cr, int width, int height, int loops)
+{
+    cairo_set_line_width (cr, 5.);
+    return nearly_vertical (cr, width, height, loops);
+}
+
+
+static cairo_time_t
+diagonal (cairo_t *cr, int width, int height, int loops)
+{
+    cairo_move_to (cr, 0, 0);
+    cairo_line_to (cr, width, height);
+
+    cairo_perf_timer_start ();
+
+    while (loops--)
+       cairo_stroke_preserve (cr);
+
+    cairo_perf_timer_stop ();
+
+    cairo_new_path (cr);
+
+    return cairo_perf_timer_elapsed ();
+}
+
+static cairo_time_t
+diagonal_hair (cairo_t *cr, int width, int height, int loops)
+{
+    cairo_set_line_width (cr, 1.);
+    return diagonal (cr, width, height, loops);
+}
+
+static cairo_time_t
+diagonal_wide (cairo_t *cr, int width, int height, int loops)
+{
+    cairo_set_line_width (cr, 5.);
+    return diagonal (cr, width, height, loops);
+}
+
+cairo_bool_t
+a1_line_enabled (cairo_perf_t *perf)
+{
+    return cairo_perf_can_run (perf, "a1-line", NULL);
+}
+
+void
+a1_line (cairo_perf_t *perf, cairo_t *cr, int width, int height)
+{
+    cairo_set_source_rgb (cr, 1., 1., 1.);
+    cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
+
+    cairo_perf_run (perf, "a1-line-hh", horizontal_hair, NULL);
+    cairo_perf_run (perf, "a1-line-hw", horizontal_wide, NULL);
+    cairo_perf_run (perf, "a1-line-nhh", nearly_horizontal_hair, NULL);
+    cairo_perf_run (perf, "a1-line-nhw", nearly_horizontal_wide, NULL);
+
+    cairo_perf_run (perf, "a1-line-vh", vertical_hair, NULL);
+    cairo_perf_run (perf, "a1-line-vw", vertical_wide, NULL);
+    cairo_perf_run (perf, "a1-line-nvh", nearly_vertical_hair, NULL);
+    cairo_perf_run (perf, "a1-line-nvw", nearly_vertical_wide, NULL);
+
+    cairo_perf_run (perf, "a1-line-dh", diagonal_hair, NULL);
+    cairo_perf_run (perf, "a1-line-dw", diagonal_wide, NULL);
+}
diff --git a/perf/micro/box-outline.c b/perf/micro/box-outline.c
new file mode 100644 (file)
index 0000000..1e654eb
--- /dev/null
@@ -0,0 +1,215 @@
+/*
+ * Copyright © 2006 Red Hat, Inc.
+ *
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ */
+
+#include "cairo-perf.h"
+
+/* This test case is designed to illustrate a performance bug that
+ * exists in cairo in which using cairo_stroke is much slower than
+ * cairo_fill to draw an identical figure, (and in particular a figure
+ * that is much more natural to draw with cairo_stroke). The figure is
+ * a 100x100 square outline 1-pixel wide, nicely pixel aligned.
+ *
+ * The performance bug should affect any path whose resulting contour
+ * consists only of pixel-aligned horizontal and vertical elements.
+ *
+ * Initial testing on on machine shows stroke as 5x slower than fill
+ * for the xlib backend and 16x slower for the image backend.
+ */
+
+static cairo_time_t
+box_outline_stroke (cairo_t *cr, int width, int height, int loops)
+{
+    cairo_set_source_rgb (cr, 0, 0, 1); /* blue */
+    cairo_paint (cr);
+
+    cairo_rectangle (cr,
+                    1.5, 1.5,
+                    width - 3, height - 3);
+    cairo_set_line_width (cr, 1.0);
+    cairo_set_source_rgb (cr, 1, 0, 0); /* red */
+
+    cairo_perf_timer_start ();
+
+    while (loops--)
+       cairo_stroke_preserve (cr);
+
+    cairo_perf_timer_stop ();
+
+    cairo_new_path (cr);
+
+    return cairo_perf_timer_elapsed ();
+}
+
+static cairo_time_t
+box_outline_alpha_stroke (cairo_t *cr, int width, int height, int loops)
+{
+    cairo_set_source_rgb (cr, 0, 0, 1); /* blue */
+    cairo_paint (cr);
+
+    cairo_rectangle (cr,
+                    1.5, 1.5,
+                    width - 3, height - 3);
+    cairo_set_line_width (cr, 1.0);
+    cairo_set_source_rgba (cr, 1, 0, 0, .5); /* red */
+
+    cairo_perf_timer_start ();
+
+    while (loops--)
+       cairo_stroke_preserve (cr);
+
+    cairo_perf_timer_stop ();
+
+    cairo_new_path (cr);
+
+    return cairo_perf_timer_elapsed ();
+}
+
+static cairo_time_t
+box_outline_aa_stroke (cairo_t *cr, int width, int height, int loops)
+{
+    cairo_set_source_rgb (cr, 0, 0, 1); /* blue */
+    cairo_paint (cr);
+
+    cairo_translate (cr, .5, .5);
+    cairo_rectangle (cr,
+                    1.5, 1.5,
+                    width - 3, height - 3);
+    cairo_set_line_width (cr, 1.0);
+    cairo_set_source_rgb (cr, 1, 0, 0); /* red */
+
+    cairo_perf_timer_start ();
+
+    while (loops--)
+       cairo_stroke_preserve (cr);
+
+    cairo_perf_timer_stop ();
+
+    cairo_new_path (cr);
+
+    return cairo_perf_timer_elapsed ();
+}
+
+static cairo_time_t
+box_outline_fill (cairo_t *cr, int width, int height, int loops)
+{
+    cairo_set_source_rgb (cr, 0, 0, 1); /* blue */
+    cairo_paint (cr);
+
+    cairo_rectangle (cr,
+                    1.0, 1.0,
+                    width - 2, height - 2);
+    cairo_rectangle (cr,
+                    2.0, 2.0,
+                    width - 4, height - 4);
+    cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD);
+    cairo_set_source_rgb (cr, 0, 1, 0); /* green */
+
+    cairo_perf_timer_start ();
+
+    while (loops--)
+       cairo_fill_preserve (cr);
+
+    cairo_perf_timer_stop ();
+
+    cairo_new_path (cr);
+
+    return cairo_perf_timer_elapsed ();
+}
+
+static cairo_time_t
+box_outline_alpha_fill (cairo_t *cr, int width, int height, int loops)
+{
+    cairo_set_source_rgb (cr, 0, 0, 1); /* blue */
+    cairo_paint (cr);
+
+    cairo_rectangle (cr,
+                    1.0, 1.0,
+                    width - 2, height - 2);
+    cairo_rectangle (cr,
+                    2.0, 2.0,
+                    width - 4, height - 4);
+    cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD);
+    cairo_set_source_rgba (cr, 0, 1, 0, .5); /* green */
+
+    cairo_perf_timer_start ();
+
+    while (loops--)
+       cairo_fill_preserve (cr);
+
+    cairo_perf_timer_stop ();
+
+    cairo_new_path (cr);
+
+    return cairo_perf_timer_elapsed ();
+}
+
+static cairo_time_t
+box_outline_aa_fill (cairo_t *cr, int width, int height, int loops)
+{
+    cairo_set_source_rgb (cr, 0, 0, 1); /* blue */
+    cairo_paint (cr);
+
+    cairo_translate (cr, .5, .5);
+    cairo_rectangle (cr,
+                    1.0, 1.0,
+                    width - 2, height - 2);
+    cairo_rectangle (cr,
+                    2.0, 2.0,
+                    width - 4, height - 4);
+    cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD);
+    cairo_set_source_rgb (cr, 0, 1, 0); /* green */
+
+    cairo_perf_timer_start ();
+
+    while (loops--)
+       cairo_fill_preserve (cr);
+
+    cairo_perf_timer_stop ();
+
+    cairo_new_path (cr);
+
+    return cairo_perf_timer_elapsed ();
+}
+
+cairo_bool_t
+box_outline_enabled (cairo_perf_t *perf)
+{
+    return cairo_perf_can_run (perf, "box-outline", NULL);
+}
+
+void
+box_outline (cairo_perf_t *perf, cairo_t *cr, int width, int height)
+{
+    cairo_perf_run (perf, "box-outline-stroke", box_outline_stroke, NULL);
+    cairo_perf_run (perf, "box-outline-fill", box_outline_fill, NULL);
+
+    cairo_perf_run (perf, "box-outline-alpha-stroke", box_outline_alpha_stroke, NULL);
+    cairo_perf_run (perf, "box-outline-alpha-fill", box_outline_alpha_fill, NULL);
+
+    cairo_perf_run (perf, "box-outline-aa-stroke", box_outline_aa_stroke, NULL);
+    cairo_perf_run (perf, "box-outline-aa-fill", box_outline_aa_fill, NULL);
+}
diff --git a/perf/micro/cairo-perf-cover.c b/perf/micro/cairo-perf-cover.c
new file mode 100644 (file)
index 0000000..151a2e6
--- /dev/null
@@ -0,0 +1,340 @@
+/*
+ * Copyright © 2006 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ */
+
+#include "cairo-perf.h"
+
+static void
+init_and_set_source_surface (cairo_t           *cr,
+                            cairo_surface_t    *source,
+                            int                 width,
+                            int                 height)
+{
+    cairo_t *cr2;
+
+    /* Fill it with something known */
+    cr2 = cairo_create (source);
+    cairo_set_operator (cr2, CAIRO_OPERATOR_SOURCE);
+    cairo_set_source_rgb (cr2, 0, 0, 1); /* blue */
+    cairo_paint (cr2);
+
+    cairo_set_source_rgba (cr2, 1, 0, 0, 0.5); /* 50% red */
+    cairo_new_path (cr2);
+    cairo_rectangle (cr2, 0, 0, width/2.0, height/2.0);
+    cairo_rectangle (cr2, width/2.0, height/2.0, width/2.0, height/2.0);
+    cairo_fill (cr2);
+
+    cairo_set_source_surface (cr, cairo_get_target (cr2), 0, 0);
+    cairo_destroy (cr2);
+}
+
+static void
+set_source_solid_rgb (cairo_t  *cr,
+                     int        width,
+                     int        height)
+{
+    cairo_set_source_rgb (cr, 0.2, 0.6, 0.9);
+}
+
+static void
+set_source_solid_rgba (cairo_t *cr,
+                      int       width,
+                      int       height)
+{
+    cairo_set_source_rgba (cr, 0.2, 0.6, 0.9, 0.7);
+}
+
+static void
+set_source_image_surface_rgb (cairo_t  *cr,
+                             int        width,
+                             int        height)
+{
+    cairo_surface_t *source;
+
+    source = cairo_image_surface_create (CAIRO_FORMAT_RGB24,
+                                        width, height);
+    init_and_set_source_surface (cr, source, width, height);
+
+    cairo_surface_destroy (source);
+}
+
+static void
+set_source_image_surface_rgba (cairo_t *cr,
+                              int       width,
+                              int       height)
+{
+    cairo_surface_t *source;
+
+    source = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
+                                        width, height);
+    init_and_set_source_surface (cr, source, width, height);
+
+    cairo_surface_destroy (source);
+}
+
+static void
+set_source_image_surface_rgba_mag (cairo_t     *cr,
+                                  int          width,
+                                  int          height)
+{
+    cairo_surface_t *source;
+
+    source = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
+                                        width/2, height/2);
+    cairo_scale(cr, 2.1, 2.1);
+    init_and_set_source_surface (cr, source, width/2, height/2);
+    cairo_scale(cr, 1/2.1, 1/2.1);
+
+    cairo_surface_destroy (source);
+}
+
+static void
+set_source_image_surface_rgba_min (cairo_t     *cr,
+                                  int          width,
+                                  int          height)
+{
+    cairo_surface_t *source;
+
+    source = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
+                                        width*2, height*2);
+    cairo_scale(cr, 1/1.9, 1/1.9);
+    init_and_set_source_surface (cr, source, width*2, height*2);
+    cairo_scale(cr, 1.9, 1.9);
+
+    cairo_surface_destroy (source);
+}
+
+static void
+set_source_similar_surface_rgb (cairo_t        *cr,
+                               int      width,
+                               int      height)
+{
+    cairo_surface_t *source;
+
+    source = cairo_surface_create_similar (cairo_get_group_target (cr),
+                                          CAIRO_CONTENT_COLOR,
+                                          width, height);
+    init_and_set_source_surface (cr, source, width, height);
+
+    cairo_surface_destroy (source);
+}
+
+static void
+set_source_similar_surface_rgba (cairo_t       *cr,
+                                int             width,
+                                int             height)
+{
+    cairo_surface_t *source;
+
+    source = cairo_surface_create_similar (cairo_get_group_target (cr),
+                                          CAIRO_CONTENT_COLOR_ALPHA,
+                                          width, height);
+    init_and_set_source_surface (cr, source, width, height);
+
+    cairo_surface_destroy (source);
+}
+
+static void
+set_source_similar_surface_rgba_mag (cairo_t   *cr,
+                                    int        width,
+                                    int        height)
+{
+    cairo_surface_t *source;
+
+    source = cairo_surface_create_similar (cairo_get_group_target (cr),
+                                          CAIRO_CONTENT_COLOR_ALPHA,
+                                          width/2, height/2);
+    cairo_scale(cr, 2.1, 2.1);
+    init_and_set_source_surface (cr, source, width/2, height/2);
+    cairo_scale(cr, 1/2.1, 1/2.1);
+
+    cairo_surface_destroy (source);
+}
+
+static void
+set_source_similar_surface_rgba_min (cairo_t   *cr,
+                                    int        width,
+                                    int        height)
+{
+    cairo_surface_t *source;
+
+    source = cairo_surface_create_similar (cairo_get_group_target (cr),
+                                          CAIRO_CONTENT_COLOR_ALPHA,
+                                          width*2, height*2);
+    cairo_scale(cr, 1/1.9, 1/1.9);
+    init_and_set_source_surface (cr, source, width*2, height*2);
+    cairo_scale(cr, 1.9, 1.9);
+
+    cairo_surface_destroy (source);
+}
+
+static void
+set_source_linear_rgb (cairo_t *cr,
+                      int      width,
+                      int      height)
+{
+    cairo_pattern_t *linear;
+
+    linear = cairo_pattern_create_linear (0.0, 0.0, width, height);
+    cairo_pattern_add_color_stop_rgb (linear, 0.0, 1, 0, 0); /* red */
+    cairo_pattern_add_color_stop_rgb (linear, 1.0, 0, 0, 1); /* blue */
+
+    cairo_set_source (cr, linear);
+
+    cairo_pattern_destroy (linear);
+}
+
+static void
+set_source_linear_rgba (cairo_t *cr,
+                       int     width,
+                       int     height)
+{
+    cairo_pattern_t *linear;
+
+    linear = cairo_pattern_create_linear (0.0, 0.0, width, height);
+    cairo_pattern_add_color_stop_rgba (linear, 0.0, 1, 0, 0, 0.5); /* 50% red */
+    cairo_pattern_add_color_stop_rgba (linear, 1.0, 0, 0, 1, 0.0); /*  0% blue */
+
+    cairo_set_source (cr, linear);
+
+    cairo_pattern_destroy (linear);
+}
+
+static void
+set_source_linear3_rgb (cairo_t *cr,
+                      int      width,
+                      int      height)
+{
+    cairo_pattern_t *linear;
+
+    linear = cairo_pattern_create_linear (0.0, 0.0, width, height);
+    cairo_pattern_add_color_stop_rgb (linear, 0.0, 1, 0, 0); /* red */
+    cairo_pattern_add_color_stop_rgb (linear, 0.5, 0, 1, 0); /* green */
+    cairo_pattern_add_color_stop_rgb (linear, 1.0, 0, 0, 1); /* blue */
+
+    cairo_set_source (cr, linear);
+
+    cairo_pattern_destroy (linear);
+}
+
+static void
+set_source_linear3_rgba (cairo_t *cr,
+                       int     width,
+                       int     height)
+{
+    cairo_pattern_t *linear;
+
+    linear = cairo_pattern_create_linear (0.0, 0.0, width, height);
+    cairo_pattern_add_color_stop_rgba (linear, 0.0, 1, 0, 0, 0.5); /* 50% red */
+    cairo_pattern_add_color_stop_rgba (linear, 0.5, 0, 1, 0, 0.0); /*  0% green */
+    cairo_pattern_add_color_stop_rgba (linear, 1.0, 0, 0, 1, 0.5); /*  50% blue */
+
+    cairo_set_source (cr, linear);
+
+    cairo_pattern_destroy (linear);
+}
+
+static void
+set_source_radial_rgb (cairo_t *cr,
+                      int      width,
+                      int      height)
+{
+    cairo_pattern_t *radial;
+
+    radial = cairo_pattern_create_radial (width/2.0, height/2.0, 0.0,
+                                         width/2.0, height/2.0, width/2.0);
+    cairo_pattern_add_color_stop_rgb (radial, 0.0, 1, 0, 0); /* red */
+    cairo_pattern_add_color_stop_rgb (radial, 1.0, 0, 0, 1); /* blue */
+
+    cairo_set_source (cr, radial);
+
+    cairo_pattern_destroy (radial);
+}
+
+static void
+set_source_radial_rgba (cairo_t *cr,
+                       int     width,
+                       int     height)
+{
+    cairo_pattern_t *radial;
+
+    radial = cairo_pattern_create_radial (width/2.0, height/2.0, 0.0,
+                                         width/2.0, height/2.0, width/2.0);
+    cairo_pattern_add_color_stop_rgba (radial, 0.0, 1, 0, 0, 0.5); /* 50% red */
+    cairo_pattern_add_color_stop_rgba (radial, 1.0, 0, 0, 1, 0.0); /*  0% blue */
+
+    cairo_set_source (cr, radial);
+
+    cairo_pattern_destroy (radial);
+}
+
+typedef void (*set_source_func_t) (cairo_t *cr, int width, int height);
+#define ARRAY_SIZE(arr) (sizeof(arr)/sizeof((arr)[0]))
+
+void
+cairo_perf_cover_sources_and_operators (cairo_perf_t           *perf,
+                                       const char              *name,
+                                       cairo_perf_func_t        perf_func,
+                                       cairo_count_func_t       count_func)
+{
+    unsigned int i, j;
+    char *expanded_name;
+
+    struct { set_source_func_t set_source; const char *name; } sources[] = {
+       { set_source_solid_rgb, "solid-rgb" },
+       { set_source_solid_rgba, "solid-rgba" },
+       { set_source_image_surface_rgb, "image-rgb" },
+       { set_source_image_surface_rgba, "image-rgba" },
+       { set_source_image_surface_rgba_mag, "image-rgba-mag" },
+       { set_source_image_surface_rgba_min, "image-rgba-min" },
+       { set_source_similar_surface_rgb, "similar-rgb" },
+       { set_source_similar_surface_rgba, "similar-rgba" },
+       { set_source_similar_surface_rgba_mag, "similar-rgba-mag" },
+       { set_source_similar_surface_rgba_min, "similar-rgba-min" },
+       { set_source_linear_rgb, "linear-rgb" },
+       { set_source_linear_rgba, "linear-rgba" },
+       { set_source_linear3_rgb, "linear3-rgb" },
+       { set_source_linear3_rgba, "linear3-rgba" },
+       { set_source_radial_rgb, "radial-rgb" },
+       { set_source_radial_rgba, "radial-rgba" }
+    };
+
+    struct { cairo_operator_t op; const char *name; } operators[] = {
+       { CAIRO_OPERATOR_OVER, "over" },
+       { CAIRO_OPERATOR_SOURCE, "source" }
+    };
+
+    for (i = 0; i < ARRAY_SIZE (sources); i++) {
+       (sources[i].set_source) (perf->cr, perf->size, perf->size);
+
+       for (j = 0; j < ARRAY_SIZE (operators); j++) {
+           cairo_set_operator (perf->cr, operators[j].op);
+
+           xasprintf (&expanded_name, "%s_%s_%s",
+                      name, sources[i].name, operators[j].name);
+           cairo_perf_run (perf, expanded_name, perf_func, count_func);
+           free (expanded_name);
+       }
+    }
+}
diff --git a/perf/micro/composite-checker.c b/perf/micro/composite-checker.c
new file mode 100644 (file)
index 0000000..d6d17ab
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+ * Copyright © 2007 Björn Lindqvist
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Björn Lindqvist <bjourne@gmail.com>
+ */
+
+#include "cairo-perf.h"
+
+/* This test case measures how much time cairo takes to render the
+ * equivalent of the following gdk-pixbuf operation:
+ *
+ *    gdk_pixbuf_composite_color(dest,
+ *                               0, 0, DST_SIZE, DST_SIZE,
+ *                               0, 0,
+ *                               SCALE, SCALE,
+ *                               gdk.INTERP_NEAREST,
+ *                               255,
+ *                               0, 0,
+ *                               8, 0x33333333, 0x88888888);
+ *
+ * Cairo is (at the time of writing) about 2-3 times as slow as
+ * gdk-pixbuf.
+ */
+#define PAT_SIZE    16
+#define SRC_SIZE    64
+
+static cairo_pattern_t *checkerboard = NULL;
+static cairo_pattern_t *src_pattern = NULL;
+
+static cairo_time_t
+do_composite_checker (cairo_t *cr,
+                      int      width,
+                      int      height,
+                     int loops)
+{
+    /* Compute zoom so that the src_pattern covers the whole output image. */
+    double xscale = width / (double) SRC_SIZE;
+    double yscale = height / (double) SRC_SIZE;
+
+    cairo_perf_timer_start ();
+
+    while (loops--) {
+       /* Fill the surface with our background. */
+       cairo_identity_matrix (cr);
+       cairo_set_source (cr, checkerboard);
+       cairo_paint (cr);
+
+       /* Draw the scaled image on top. */
+       cairo_scale (cr, xscale, yscale);
+       cairo_set_source (cr, src_pattern);
+       cairo_paint (cr);
+    }
+
+    cairo_perf_timer_stop ();
+    return cairo_perf_timer_elapsed ();
+}
+
+cairo_bool_t
+composite_checker_enabled (cairo_perf_t *perf)
+{
+    return cairo_perf_can_run (perf, "composite-checker", NULL);
+}
+
+void
+composite_checker (cairo_perf_t *perf,
+                   cairo_t      *cr,
+                   int           width,
+                   int           height)
+{
+    cairo_surface_t *image;
+
+    /* Create the checker pattern. We don't actually need to draw
+     * anything on it since that wouldn't affect performance.
+     */
+    image = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
+                                        PAT_SIZE,
+                                        PAT_SIZE);
+    checkerboard = cairo_pattern_create_for_surface (image);
+    cairo_pattern_set_filter (checkerboard, CAIRO_FILTER_NEAREST);
+    cairo_pattern_set_extend (checkerboard, CAIRO_EXTEND_REPEAT);
+    cairo_surface_destroy (image);
+
+    /* Create the image source pattern. Again we use the NEAREST
+     * filtering which should be fastest.
+    */
+    image = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
+                                        SRC_SIZE,
+                                        SRC_SIZE);
+    src_pattern = cairo_pattern_create_for_surface (image);
+    cairo_pattern_set_filter (src_pattern, CAIRO_FILTER_NEAREST);
+    cairo_surface_destroy (image);
+
+    cairo_perf_run (perf, "composite-checker", do_composite_checker, NULL);
+
+    cairo_pattern_destroy (checkerboard);
+    cairo_pattern_destroy (src_pattern);
+}
diff --git a/perf/micro/curve.c b/perf/micro/curve.c
new file mode 100644 (file)
index 0000000..3b5a163
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * Copyright © 2011 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-perf.h"
+
+static uint32_t state;
+
+static double
+uniform_random (double minval, double maxval)
+{
+    static uint32_t const poly = 0x9a795537U;
+    uint32_t n = 32;
+    while (n-->0)
+       state = 2*state < state ? (2*state ^ poly) : 2*state;
+    return minval + state * (maxval - minval) / 4294967296.0;
+}
+
+static cairo_time_t
+do_curve_stroke (cairo_t *cr, int width, int height, int loops)
+{
+    state = 0xc0ffee;
+    cairo_set_line_width (cr, 2.);
+    cairo_perf_timer_start ();
+
+    while (loops--) {
+       double x1 = uniform_random (0, width);
+       double x2 = uniform_random (0, width);
+       double x3 = uniform_random (0, width);
+       double y1 = uniform_random (0, height);
+       double y2 = uniform_random (0, height);
+       double y3 = uniform_random (0, height);
+       cairo_move_to (cr, uniform_random (0, width), uniform_random (0, height));
+       cairo_curve_to (cr, x1, y1, x2, y2, x3, y3);
+       cairo_stroke(cr);
+    }
+
+    cairo_perf_timer_stop ();
+
+    return cairo_perf_timer_elapsed ();
+}
+
+static cairo_time_t
+do_curve_fill (cairo_t *cr, int width, int height, int loops)
+{
+    state = 0xc0ffee;
+    cairo_perf_timer_start ();
+
+    while (loops--) {
+       double x0 = uniform_random (0, width);
+       double x1 = uniform_random (0, width);
+       double x2 = uniform_random (0, width);
+       double x3 = uniform_random (0, width);
+       double xm = uniform_random (0, width);
+       double xn = uniform_random (0, width);
+       double y0 = uniform_random (0, height);
+       double y1 = uniform_random (0, height);
+       double y2 = uniform_random (0, height);
+       double y3 = uniform_random (0, height);
+       double ym = uniform_random (0, height);
+       double yn = uniform_random (0, height);
+
+       cairo_move_to (cr, xm, ym);
+       cairo_curve_to (cr, x1, y1, x2, y2, xn, yn);
+       cairo_curve_to (cr, x3, y3, x0, y0, xm, ym);
+       cairo_close_path (cr);
+
+       cairo_fill(cr);
+    }
+
+    cairo_perf_timer_stop ();
+
+    return cairo_perf_timer_elapsed ();
+}
+
+cairo_bool_t
+curve_enabled (cairo_perf_t *perf)
+{
+    return cairo_perf_can_run (perf, "curve", NULL);
+}
+
+void
+curve (cairo_perf_t *perf, cairo_t *cr, int width, int height)
+{
+    cairo_set_source_rgb (cr, 1., 1., 1.);
+
+    cairo_perf_run (perf, "curve-stroked", do_curve_stroke, NULL);
+    cairo_perf_run (perf, "curve-filled", do_curve_fill, NULL);
+}
diff --git a/perf/micro/disjoint.c b/perf/micro/disjoint.c
new file mode 100644 (file)
index 0000000..623eb6f
--- /dev/null
@@ -0,0 +1,101 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright (c) 2011 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include <assert.h>
+#include "cairo-perf.h"
+
+#define STEP   5
+
+static void path (cairo_t *cr, int width, int height)
+{
+    int i;
+
+    cairo_rectangle (cr, 0, 0, width, height);
+    cairo_clip (cr);
+
+    cairo_translate (cr, width/2, height/2);
+    cairo_rotate (cr, M_PI/4);
+    cairo_translate (cr, -width/2, -height/2);
+
+    for (i = 0; i < width; i += STEP) {
+       cairo_rectangle (cr, i, -2, 1, height+4);
+       cairo_rectangle (cr, -2, i, width+4, 1);
+    }
+}
+
+static void clip (cairo_t *cr, int width, int height)
+{
+    int i, j;
+
+    for (j = 0; j < height; j += 2*STEP) {
+       for (i = 0; i < width; i += 2*STEP)
+           cairo_rectangle (cr, i, j, STEP, STEP);
+
+       j += 2*STEP;
+       for (i = 0; i < width; i += 2*STEP)
+           cairo_rectangle (cr, i+STEP/2, j, STEP, STEP);
+    }
+
+    cairo_clip (cr);
+}
+
+static cairo_time_t
+draw (cairo_t *cr, int width, int height, int loops)
+{
+    cairo_save (cr);
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_paint (cr);
+    cairo_set_source_rgb (cr, 1, 0, 0);
+
+    cairo_perf_timer_start ();
+    while (loops--) {
+       cairo_save (cr);
+       clip (cr, width, height);
+       path (cr, width, height);
+       cairo_fill (cr);
+       cairo_restore (cr);
+    }
+    cairo_perf_timer_stop ();
+
+    cairo_restore (cr);
+
+    return cairo_perf_timer_elapsed ();
+}
+
+cairo_bool_t
+disjoint_enabled (cairo_perf_t *perf)
+{
+    return cairo_perf_can_run (perf, "disjoint", NULL);
+}
+
+void
+disjoint (cairo_perf_t *perf, cairo_t *cr, int width, int height)
+{
+    if (! cairo_perf_can_run (perf, "disjoint", NULL))
+       return;
+
+    cairo_perf_run (perf, "disjoint", draw, NULL);
+}
diff --git a/perf/micro/dragon.c b/perf/micro/dragon.c
new file mode 100644 (file)
index 0000000..e215eac
--- /dev/null
@@ -0,0 +1,285 @@
+/*
+ * Copyright © 2007 Chris Wilson
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Chris Wilson not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Chris Wilson makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * CHRIS WILSON DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL CHRIS WILSON BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ *
+ * Inspiration (and path!) taken from
+ * http://labs.trolltech.com/blogs/2007/08/31/rasterizing-dragons/
+ */
+
+#include "cairo-perf.h"
+
+#ifndef MIN
+#define MIN(a,b) (((a) < (b)) ? (a) : (b))
+#endif
+
+#ifndef MAX
+#define MAX(a,b) (((a) > (b)) ? (a) : (b))
+#endif
+
+static inline int
+next_pot (int v)
+{
+    v--;
+    v |= v >> 1;
+    v |= v >> 2;
+    v |= v >> 4;
+    v |= v >> 8;
+    v |= v >> 16;
+    v++;
+    return v;
+}
+
+static cairo_bool_t
+direction (int i)
+{
+    int pivot, np2;
+
+    if (i < 2)
+       return TRUE;
+
+    np2 = next_pot (i + 1);
+    if (np2 == i + 1)
+       return TRUE;
+
+    pivot = np2 / 2 - 1;
+    return ! direction (2 * pivot - i);
+}
+
+static void
+path (cairo_t *cr, int step, int dir, int iterations)
+{
+    double dx, dy;
+    int i;
+
+    switch (dir) {
+       default:
+       case 0: dx =  step; dy =  0; break;
+       case 1: dx = -step; dy =  0; break;
+       case 2: dx =  0; dy =  step; break;
+       case 3: dx =  0; dy = -step; break;
+    }
+
+    for (i = 0; i < iterations; i++) {
+       cairo_rel_line_to (cr, dx, dy);
+
+       if (direction (i)) {
+           double t = dx;
+           dx = dy;
+           dy = -t;
+       } else {
+           double t = dx;
+           dx = -dy;
+           dy = t;
+       }
+    }
+}
+
+static cairo_time_t
+do_dragon (cairo_t *cr, int width, int height, int loops)
+{
+    cairo_pattern_t *pattern;
+    double cx, cy, r;
+
+    cx = cy = .5 * MAX (width, height);
+    r = .5 * MIN (width, height);
+
+    cairo_perf_timer_start ();
+
+    while (loops--) {
+       pattern = cairo_pattern_create_radial (cx, cy, 0., cx, cy, r);
+       cairo_pattern_add_color_stop_rgb (pattern, 0., .0, .0, .0);
+       cairo_pattern_add_color_stop_rgb (pattern, 0.25, .5, .4, .4);
+       cairo_pattern_add_color_stop_rgb (pattern, .5, .8, .8, .9);
+       cairo_pattern_add_color_stop_rgb (pattern, 1., .9, .9, 1.);
+       cairo_set_source (cr, pattern);
+       cairo_pattern_destroy (pattern);
+       cairo_paint (cr);
+
+       cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
+       cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND);
+       cairo_set_line_width (cr, 4.);
+
+       cairo_move_to (cr, cx, cy);
+       path (cr, 12, 0, 2048);
+       pattern = cairo_pattern_create_radial (cx, cy, 0., cx, cy, r);
+       cairo_pattern_add_color_stop_rgb (pattern, 0., 1., 1., 1.);
+       cairo_pattern_add_color_stop_rgb (pattern, 1., 0., 0., 0.);
+       cairo_set_source (cr, pattern);
+       cairo_pattern_destroy (pattern);
+       cairo_stroke(cr);
+
+       cairo_move_to (cr, cx, cy);
+       path (cr, 12, 1, 2048);
+       pattern = cairo_pattern_create_radial (cx, cy, 0., cx, cy, r);
+       cairo_pattern_add_color_stop_rgb (pattern, 1., 1., 1., 0.);
+       cairo_pattern_add_color_stop_rgb (pattern, 0., 1., 0., 0.);
+       cairo_set_source (cr, pattern);
+       cairo_pattern_destroy (pattern);
+       cairo_stroke(cr);
+
+       cairo_move_to (cr, cx, cy);
+       path (cr, 12, 2, 2048);
+       pattern = cairo_pattern_create_radial (cx, cy, 0., cx, cy, r);
+       cairo_pattern_add_color_stop_rgb (pattern, 1., 0., 1., 1.);
+       cairo_pattern_add_color_stop_rgb (pattern, 0., 0., 1., 0.);
+       cairo_set_source (cr, pattern);
+       cairo_pattern_destroy (pattern);
+       cairo_stroke(cr);
+
+       cairo_move_to (cr, cx, cy);
+       path (cr, 12, 3, 2048);
+       pattern = cairo_pattern_create_radial (cx, cy, 0., cx, cy, r);
+       cairo_pattern_add_color_stop_rgb (pattern, 1., 1., 0., 1.);
+       cairo_pattern_add_color_stop_rgb (pattern, 0., 0., 0., 1.);
+       cairo_set_source (cr, pattern);
+       cairo_pattern_destroy (pattern);
+       cairo_stroke(cr);
+    }
+
+    cairo_perf_timer_stop ();
+
+    return cairo_perf_timer_elapsed ();
+}
+
+static cairo_time_t
+do_dragon_solid (cairo_t *cr, int width, int height, int loops)
+{
+    double cx, cy;
+
+    cx = cy = .5 * MAX (width, height);
+
+    cairo_perf_timer_start ();
+
+    while (loops--) {
+       cairo_set_source_rgb (cr, 0, 0, 0);
+       cairo_paint (cr);
+
+       cairo_set_line_width (cr, 4.);
+
+       cairo_move_to (cr, cx, cy);
+       path (cr, 12, 0, 2048);
+       cairo_set_source_rgb (cr, 1, 0, 0);
+       cairo_stroke(cr);
+
+       cairo_move_to (cr, cx, cy);
+       path (cr, 12, 1, 2048);
+       cairo_set_source_rgb (cr, 0, 1, 0);
+       cairo_stroke(cr);
+
+       cairo_move_to (cr, cx, cy);
+       path (cr, 12, 2, 2048);
+       cairo_set_source_rgb (cr, 0, 0, 1);
+       cairo_stroke(cr);
+
+       cairo_move_to (cr, cx, cy);
+       path (cr, 12, 3, 2048);
+       cairo_set_source_rgb (cr, 1, 1, 1);
+       cairo_stroke(cr);
+    }
+
+    cairo_perf_timer_stop ();
+
+    return cairo_perf_timer_elapsed ();
+}
+
+static cairo_time_t
+do_dragon_solid_unaligned (cairo_t *cr, int width, int height, int loops)
+{
+    cairo_translate (cr, 0.01, 0.01);
+    return do_dragon_solid (cr, width, height, loops);
+}
+
+static cairo_time_t
+do_dragon_solid_aligned_clip (cairo_t *cr, int width, int height, int loops)
+{
+    cairo_reset_clip (cr);
+    cairo_rectangle (cr, 10, 10, width/2 + 10, height/2 + 10);
+    cairo_rectangle (cr, width/2-20, height/2-20, width/2 + 10, height/2 + 10);
+    cairo_clip (cr);
+
+    return do_dragon_solid (cr, width, height, loops);
+}
+
+static cairo_time_t
+do_dragon_unaligned_solid_aligned_clip (cairo_t *cr, int width, int height, int loops)
+{
+    cairo_translate (cr, 0.01, 0.01);
+    cairo_reset_clip (cr);
+    cairo_rectangle (cr, 10, 10, width/2 + 10, height/2 + 10);
+    cairo_rectangle (cr, width/2-20, height/2-20, width/2 + 10, height/2 + 10);
+    cairo_clip (cr);
+
+    return do_dragon_solid (cr, width, height, loops);
+}
+
+static cairo_time_t
+do_dragon_solid_unaligned_clip (cairo_t *cr, int width, int height, int loops)
+{
+    cairo_reset_clip (cr);
+    cairo_rectangle (cr, 10.5, 10.5, width/2 + 10, height/2 + 10);
+    cairo_rectangle (cr, width/2-20, height/2-20, width/2 + 9.5, height/2 + 9.5);
+    cairo_clip (cr);
+
+    return do_dragon_solid (cr, width, height, loops);
+}
+
+static cairo_time_t
+do_dragon_unaligned_solid_unaligned_clip (cairo_t *cr, int width, int height, int loops)
+{
+    cairo_translate (cr, 0.01, 0.01);
+    cairo_reset_clip (cr);
+    cairo_rectangle (cr, 10.5, 10.5, width/2 + 10, height/2 + 10);
+    cairo_rectangle (cr, width/2-20, height/2-20, width/2 + 9.5, height/2 + 9.5);
+    cairo_clip (cr);
+
+    return do_dragon_solid (cr, width, height, loops);
+}
+
+static cairo_time_t
+do_dragon_solid_circle_clip (cairo_t *cr, int width, int height, int loops)
+{
+    cairo_reset_clip (cr);
+    cairo_arc (cr, width/2., height/2., MIN (width, height)/2. - 10, 0, 2 * M_PI);
+    cairo_clip (cr);
+
+    return do_dragon_solid (cr, width, height, loops);
+}
+
+cairo_bool_t
+dragon_enabled (cairo_perf_t *perf)
+{
+    return cairo_perf_can_run (perf, "dragon", NULL);
+}
+
+void
+dragon (cairo_perf_t *perf, cairo_t *cr, int width, int height)
+{
+    cairo_perf_run (perf, "dragon-solid", do_dragon_solid, NULL);
+    cairo_perf_run (perf, "dragon-unaligned-solid", do_dragon_solid_unaligned, NULL);
+    cairo_perf_run (perf, "dragon-solid-aligned-clip", do_dragon_solid_aligned_clip, NULL);
+    cairo_perf_run (perf, "dragon-unaligned-solid-aligned-clip", do_dragon_unaligned_solid_aligned_clip, NULL);
+    cairo_perf_run (perf, "dragon-solid-unaligned-clip", do_dragon_solid_unaligned_clip, NULL);
+    cairo_perf_run (perf, "dragon-unaligned-solid-unaligned-clip", do_dragon_unaligned_solid_unaligned_clip, NULL);
+    cairo_perf_run (perf, "dragon-solid-circle-clip", do_dragon_solid_circle_clip, NULL);
+    cairo_perf_run (perf, "dragon", do_dragon, NULL);
+}
diff --git a/perf/micro/fill-clip.c b/perf/micro/fill-clip.c
new file mode 100644 (file)
index 0000000..2d014ac
--- /dev/null
@@ -0,0 +1,126 @@
+/*
+ * Copyright © 2011 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+/* Compares the overhead for WebKit's drawRect() */
+
+#include "cairo-perf.h"
+
+#include <pixman.h>
+
+static cairo_time_t
+clip_paint (cairo_t *cr, int width, int height, int loops)
+{
+    int x = width/4, w = width/2;
+    int y = height/4, h = height/2;
+
+    cairo_perf_timer_start ();
+
+    while (loops--) {
+       cairo_reset_clip (cr);
+       cairo_rectangle (cr, x, y, w, h);
+       cairo_clip (cr);
+       cairo_paint (cr);
+    }
+
+    cairo_perf_timer_stop ();
+
+    return cairo_perf_timer_elapsed ();
+}
+
+static cairo_time_t
+rect_fill (cairo_t *cr, int width, int height, int loops)
+{
+    int x = width/4, w = width/2;
+    int y = height/4, h = height/2;
+
+    cairo_perf_timer_start ();
+
+    while (loops--) {
+       cairo_rectangle (cr, x, y, w, h);
+       cairo_fill (cr);
+    }
+
+    cairo_perf_timer_stop ();
+
+    return cairo_perf_timer_elapsed ();
+}
+
+static cairo_time_t
+direct (cairo_t *cr, int width, int height, int loops)
+{
+    int x = width/4, w = width/2;
+    int y = height/4, h = height/2;
+    cairo_surface_t *surface, *image;
+    uint8_t *data;
+    int stride, bpp;
+
+
+    surface = cairo_get_target (cr);
+    image = cairo_surface_map_to_image (surface, NULL);
+    data = cairo_image_surface_get_data (image);
+    stride = cairo_image_surface_get_stride (image);
+
+    switch (cairo_image_surface_get_format (image)) {
+    default:
+    case CAIRO_FORMAT_INVALID:
+    case CAIRO_FORMAT_A1: bpp = 0; break;
+    case CAIRO_FORMAT_A8: bpp = 8; break;
+    case CAIRO_FORMAT_RGB16_565: bpp = 16; break;
+    case CAIRO_FORMAT_RGB24:
+    case CAIRO_FORMAT_RGB30:
+    case CAIRO_FORMAT_ARGB32: bpp = 32; break;
+    }
+
+    cairo_perf_timer_start ();
+
+    while (loops--) {
+       pixman_fill ((uint32_t *)data, stride / sizeof(uint32_t), bpp,
+                    x, y, w, h,
+                    -1);
+    }
+
+    cairo_perf_timer_stop ();
+
+    cairo_surface_unmap_image (surface, image);
+
+    return cairo_perf_timer_elapsed ();
+}
+
+cairo_bool_t
+fill_clip_enabled (cairo_perf_t *perf)
+{
+    return cairo_perf_can_run (perf, "fillclip", NULL);
+}
+
+void
+fill_clip (cairo_perf_t *perf, cairo_t *cr, int width, int height)
+{
+    cairo_set_source_rgb (cr, 1., 1., 1.);
+
+    cairo_perf_run (perf, "fillclip-clip", clip_paint, NULL);
+    cairo_perf_run (perf, "fillclip-fill", rect_fill, NULL);
+    cairo_perf_run (perf, "fillclip-direct", direct, NULL);
+}
diff --git a/perf/micro/fill.c b/perf/micro/fill.c
new file mode 100644 (file)
index 0000000..d356c26
--- /dev/null
@@ -0,0 +1,122 @@
+/*
+ * Copyright © 2006 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ */
+
+#include "cairo-perf.h"
+
+static cairo_time_t
+do_fill (cairo_t *cr, int width, int height, int loops)
+{
+    cairo_arc (cr,
+              width/2.0, height/2.0,
+              width/3.0,
+              0, 2 * M_PI);
+
+    cairo_perf_timer_start ();
+
+    while (loops--)
+       cairo_fill_preserve (cr);
+
+    cairo_perf_timer_stop ();
+
+    cairo_new_path (cr);
+
+    return cairo_perf_timer_elapsed ();
+}
+
+static cairo_time_t
+do_fill_annuli (cairo_t *cr, int width, int height, int loops)
+{
+    cairo_new_sub_path (cr);
+    cairo_arc (cr,
+              width/2.0, height/2.0,
+              width/3.0,
+              0, 2 * M_PI);
+
+    cairo_new_sub_path (cr);
+    cairo_arc_negative (cr,
+              width/2.0, height/2.0,
+              width/4.0,
+              2 * M_PI, 0);
+
+    cairo_new_sub_path (cr);
+    cairo_arc (cr,
+              width/2.0, height/2.0,
+              width/6.0,
+              0, 2 * M_PI);
+
+    cairo_new_sub_path (cr);
+    cairo_arc_negative (cr,
+              width/2.0, height/2.0,
+              width/8.0,
+              2 * M_PI, 0);
+
+    cairo_perf_timer_start ();
+
+    while (loops--)
+       cairo_fill_preserve (cr);
+
+    cairo_perf_timer_stop ();
+
+    cairo_new_path (cr);
+
+    return cairo_perf_timer_elapsed ();
+}
+
+static cairo_time_t
+do_fill_eo_noaa (cairo_t *cr, int width, int height, int loops)
+{
+    cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
+    cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD);
+
+    cairo_arc (cr,
+              width/2.0, height/2.0,
+              width/3.0,
+              0, 2 * M_PI);
+
+    cairo_perf_timer_start ();
+
+    while (loops--)
+       cairo_fill_preserve (cr);
+
+    cairo_perf_timer_stop ();
+
+    cairo_new_path (cr);
+
+    return cairo_perf_timer_elapsed ();
+}
+
+cairo_bool_t
+fill_enabled (cairo_perf_t *perf)
+{
+    return cairo_perf_can_run (perf, "fill", NULL);
+}
+
+void
+fill (cairo_perf_t *perf, cairo_t *cr, int width, int height)
+{
+    cairo_perf_cover_sources_and_operators (perf, "fill", do_fill, NULL);
+    cairo_perf_cover_sources_and_operators (perf, "fill-annuli", do_fill_annuli, NULL);
+    cairo_perf_cover_sources_and_operators (perf, "fill-eo-noaa", do_fill_eo_noaa, NULL);
+}
diff --git a/perf/micro/glyphs.c b/perf/micro/glyphs.c
new file mode 100644 (file)
index 0000000..5f088b2
--- /dev/null
@@ -0,0 +1,202 @@
+/*
+ * Copyright © 2006 Red Hat, Inc.
+ * Copyright © 2009 Chris Wilson
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ *         Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-perf.h"
+
+static cairo_time_t
+do_glyphs (double font_size,
+          cairo_antialias_t antialias,
+          cairo_t *cr, int width, int height, int loops)
+{
+    const char text[] = "the jay, pig, fox, zebra and my wolves quack";
+    cairo_scaled_font_t *scaled_font;
+    cairo_glyph_t *glyphs = NULL, *glyphs_copy;
+    cairo_text_extents_t extents;
+    cairo_font_options_t *options;
+    cairo_status_t status;
+    double x, y;
+    int num_glyphs, n;
+
+    options = cairo_font_options_create ();
+    cairo_font_options_set_antialias (options, antialias);
+    cairo_set_font_options (cr, options);
+    cairo_font_options_destroy (options);
+
+    cairo_select_font_face (cr,
+                           "@cairo:",
+                           CAIRO_FONT_SLANT_NORMAL,
+                           CAIRO_FONT_WEIGHT_NORMAL);
+    cairo_set_font_size (cr, font_size);
+    scaled_font = cairo_get_scaled_font (cr);
+    status = cairo_scaled_font_text_to_glyphs (scaled_font, 0., 0.,
+                                              text, -1,
+                                              &glyphs, &num_glyphs,
+                                              NULL, NULL,
+                                              NULL);
+    if (status)
+       return 0;
+
+    glyphs_copy = cairo_glyph_allocate (num_glyphs);
+    if (glyphs_copy == NULL) {
+       cairo_glyph_free (glyphs);
+       return 0;
+    }
+
+    cairo_scaled_font_glyph_extents (scaled_font,
+                                    glyphs, num_glyphs,
+                                    &extents);
+
+    cairo_perf_timer_start ();
+
+    while (loops--) {
+       y = 0;
+       do {
+           x = 0;
+           do {
+               for (n = 0; n < num_glyphs; n++) {
+                   glyphs_copy[n] = glyphs[n];
+                   glyphs_copy[n].x += x;
+                   glyphs_copy[n].y += y;
+               }
+               cairo_show_glyphs (cr, glyphs_copy, num_glyphs);
+
+               x += extents.width;
+           } while (x < width);
+           y += extents.height;
+       } while (y < height);
+    }
+
+    cairo_perf_timer_stop ();
+
+    cairo_glyph_free (glyphs);
+    cairo_glyph_free (glyphs_copy);
+
+    return cairo_perf_timer_elapsed ();
+}
+
+static double
+count_glyphs (double font_size,
+             cairo_antialias_t antialias,
+             cairo_t *cr, int width, int height)
+{
+    const char text[] = "the jay, pig, fox, zebra and my wolves quack";
+    cairo_scaled_font_t *scaled_font;
+    cairo_glyph_t *glyphs = NULL;
+    cairo_text_extents_t extents;
+    cairo_font_options_t *options;
+    cairo_status_t status;
+    int num_glyphs;
+    int glyphs_per_line, lines_per_loop;
+
+    options = cairo_font_options_create ();
+    cairo_font_options_set_antialias (options, antialias);
+    cairo_set_font_options (cr, options);
+    cairo_font_options_destroy (options);
+
+    cairo_select_font_face (cr,
+                           "@cairo:",
+                           CAIRO_FONT_SLANT_NORMAL,
+                           CAIRO_FONT_WEIGHT_NORMAL);
+    cairo_set_font_size (cr, font_size);
+    scaled_font = cairo_get_scaled_font (cr);
+    status = cairo_scaled_font_text_to_glyphs (scaled_font, 0., 0.,
+                                              text, -1,
+                                              &glyphs, &num_glyphs,
+                                              NULL, NULL,
+                                              NULL);
+    if (status)
+       return 0;
+
+    cairo_scaled_font_glyph_extents (scaled_font,
+                                    glyphs, num_glyphs,
+                                    &extents);
+    cairo_glyph_free (glyphs);
+
+    glyphs_per_line = num_glyphs * width / extents.width + 1;
+    lines_per_loop = height / extents.height + 1;
+    return glyphs_per_line * lines_per_loop / 1000.; /* kiloglyphs */
+}
+
+#define DECL(name,size, aa) \
+static cairo_time_t \
+do_glyphs##name (cairo_t *cr, int width, int height, int loops) \
+{ \
+    return do_glyphs (size, aa, cr, width, height, loops); \
+} \
+\
+static double \
+count_glyphs##name (cairo_t *cr, int width, int height) \
+{ \
+    return count_glyphs (size, aa, cr, width, height); \
+}
+
+DECL(8, 8, CAIRO_ANTIALIAS_GRAY)
+DECL(10, 10, CAIRO_ANTIALIAS_GRAY)
+DECL(12, 12, CAIRO_ANTIALIAS_GRAY)
+DECL(16, 16, CAIRO_ANTIALIAS_GRAY)
+DECL(20, 20, CAIRO_ANTIALIAS_GRAY)
+DECL(24, 24, CAIRO_ANTIALIAS_GRAY)
+DECL(32, 32, CAIRO_ANTIALIAS_GRAY)
+DECL(40, 40, CAIRO_ANTIALIAS_GRAY)
+DECL(48, 48, CAIRO_ANTIALIAS_GRAY)
+
+DECL(8ca, 8, CAIRO_ANTIALIAS_SUBPIXEL)
+DECL(48ca, 48, CAIRO_ANTIALIAS_SUBPIXEL)
+
+DECL(8mono, 8, CAIRO_ANTIALIAS_NONE)
+DECL(48mono, 48, CAIRO_ANTIALIAS_NONE)
+
+cairo_bool_t
+glyphs_enabled (cairo_perf_t *perf)
+{
+    return cairo_perf_can_run (perf, "glyphs", NULL);
+}
+
+void
+glyphs (cairo_perf_t *perf, cairo_t *cr, int width, int height)
+{
+    cairo_perf_cover_sources_and_operators (perf, "glyphs8mono", do_glyphs8mono, count_glyphs8mono);
+    cairo_perf_cover_sources_and_operators (perf, "glyphs8", do_glyphs8, count_glyphs8);
+    cairo_perf_cover_sources_and_operators (perf, "glyphs8ca", do_glyphs8ca, count_glyphs8ca);
+
+    cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
+    cairo_set_source_rgb (cr, 0, 0, 0);
+
+    cairo_perf_run (perf, "glyphs8", do_glyphs8, count_glyphs8);
+    cairo_perf_run (perf, "glyphs10", do_glyphs10, count_glyphs10);
+    cairo_perf_run (perf, "glyphs12", do_glyphs12, count_glyphs12);
+    cairo_perf_run (perf, "glyphs16", do_glyphs16, count_glyphs16);
+    cairo_perf_run (perf, "glyphs20", do_glyphs20, count_glyphs20);
+    cairo_perf_run (perf, "glyphs24", do_glyphs24, count_glyphs24);
+    cairo_perf_run (perf, "glyphs32", do_glyphs32, count_glyphs32);
+    cairo_perf_run (perf, "glyphs40", do_glyphs40, count_glyphs40);
+    cairo_perf_run (perf, "glyphs48", do_glyphs48, count_glyphs48);
+
+    cairo_perf_cover_sources_and_operators (perf, "glyphs48mono", do_glyphs48mono, count_glyphs48mono);
+    cairo_perf_cover_sources_and_operators (perf, "glyphs48", do_glyphs48, count_glyphs48);
+    cairo_perf_cover_sources_and_operators (perf, "glyphs48ca", do_glyphs48ca, count_glyphs48ca);
+}
diff --git a/perf/micro/hash-table.c b/perf/micro/hash-table.c
new file mode 100644 (file)
index 0000000..d162917
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ * Copyright © 2011 Andrea Canciani
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Andrea Canciani <ranma42@gmail.com>
+ */
+
+#include "cairo-perf.h"
+
+#define ITER 1000
+#define HOLDOVERS 256
+#define LIVE_ENTRIES 257
+#define ACTIVE_FONTS (LIVE_ENTRIES - HOLDOVERS - 1)
+
+/*
+ * The original implementation of hash tables was very inefficient, as
+ * pointed out in https://bugs.freedesktop.org/show_bug.cgi?id=17399
+ *
+ * This benchmark tries to fill up the scaled_font_map hash table to
+ * show the O(n) behavior.
+ */
+
+static cairo_time_t
+do_hash_table (cairo_t *cr, int width, int height, int loops)
+{
+    /*
+     * Microsoft C Compiler complains that:
+     * error C2466: cannot allocate an array of constant size 0
+     * so we add an unused element to make it happy
+     */
+    cairo_scaled_font_t *active_fonts[ACTIVE_FONTS + 1];
+    cairo_matrix_t m;
+    int i;
+
+    cairo_matrix_init_identity (&m);
+
+    /* Touch HOLDOVERS scaled fonts to fill up the holdover list. */
+    for (i = 0; i < HOLDOVERS; i++) {
+       m.yy = m.xx * (i + 1);
+       cairo_set_font_matrix (cr, &m);
+       cairo_get_scaled_font (cr);
+    }
+
+    /*
+     * Reference some scaled fonts so that they will be kept in the
+     * scaled fonts map. We want LIVE_ENTRIES elements in the font
+     * map, but cairo keeps HOLDOVERS recently used fonts in it and we
+     * will be activating a new font in the cr context, so we just
+     * keep references to ACTIVE_FONTS fonts.
+     *
+     * Note: setting LIVE_ENTRIES == HOLDOVERS+1 means that we keep no
+     * font in active_fonts and the slowness is caused by the holdover
+     * fonts only.
+     */
+    for (i = 0; i < ACTIVE_FONTS; i++) {
+       cairo_scaled_font_t *scaled_font;
+
+       m.yy = m.xx * (i + 1);
+       cairo_set_font_matrix (cr, &m);
+
+       scaled_font = cairo_get_scaled_font (cr);
+       active_fonts[i] = cairo_scaled_font_reference (scaled_font);
+    }
+
+    cairo_perf_timer_start ();
+
+    while (loops--) {
+       m.xx += 1.0;
+
+       /* Generate ITER new scaled fonts per loop */
+       for (i = 0; i < ITER; i++) {
+           m.yy = m.xx * (i + 1);
+           cairo_set_font_matrix (cr, &m);
+           cairo_get_scaled_font (cr);
+       }
+    }
+
+    cairo_perf_timer_stop ();
+
+    for (i = 0; i < ACTIVE_FONTS; i++)
+       cairo_scaled_font_destroy (active_fonts[i]);
+
+    return cairo_perf_timer_elapsed ();
+}
+
+cairo_bool_t
+hash_table_enabled (cairo_perf_t *perf)
+{
+    return cairo_perf_can_run (perf, "hash-table", NULL);
+}
+
+void
+hash_table (cairo_perf_t *perf, cairo_t *cr, int width, int height)
+{
+    cairo_perf_cover_sources_and_operators (perf, "hash-table",
+                                           do_hash_table, NULL);
+}
diff --git a/perf/micro/hatching.c b/perf/micro/hatching.c
new file mode 100644 (file)
index 0000000..b51acec
--- /dev/null
@@ -0,0 +1,202 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright (c) 2011 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include <assert.h>
+#include "cairo-perf.h"
+
+
+#define STEP   5
+#define WIDTH  100
+#define HEIGHT 100
+
+static void path (cairo_t *cr, int width, int height)
+{
+    int i;
+
+    for (i = 0; i < width+1; i += STEP) {
+       cairo_rectangle (cr, i-1, -1, 2, height+2);
+       cairo_rectangle (cr, -1, i-1, width+2, 2);
+    }
+}
+
+static void aa (cairo_t *cr)
+{
+    cairo_set_antialias (cr, CAIRO_ANTIALIAS_DEFAULT);
+}
+
+static void mono (cairo_t *cr)
+{
+    cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
+}
+
+static void aligned (cairo_t *cr, int width, int height)
+{
+}
+
+static void misaligned (cairo_t *cr, int width, int height)
+{
+    cairo_translate (cr, 0.25, 0.25);
+}
+
+static void rotated (cairo_t *cr, int width, int height)
+{
+    cairo_translate (cr, width/2, height/2);
+    cairo_rotate (cr, M_PI/4);
+    cairo_translate (cr, -width/2, -height/2);
+}
+
+static void clip (cairo_t *cr)
+{
+    cairo_clip (cr);
+    cairo_paint (cr);
+}
+
+static void clip_alpha (cairo_t *cr)
+{
+    cairo_clip (cr);
+    cairo_paint_with_alpha (cr, .5);
+}
+
+static cairo_time_t
+draw (cairo_t *cr,
+      void (*prepare) (cairo_t *cr),
+      void (*transform) (cairo_t *cr, int width, int height),
+      void (*op) (cairo_t *cr),
+      int width, int height, int loops)
+{
+    cairo_save (cr);
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_paint (cr);
+    cairo_set_source_rgb (cr, 1, 0, 0);
+
+    prepare (cr);
+
+    cairo_perf_timer_start ();
+    while (loops--) {
+       cairo_save (cr);
+       transform (cr, width, height);
+       path (cr, width, height);
+       op (cr);
+       cairo_restore (cr);
+    }
+    cairo_perf_timer_stop ();
+
+    cairo_restore (cr);
+
+    return cairo_perf_timer_elapsed ();
+}
+
+static cairo_time_t
+draw_aligned_aa (cairo_t *cr, int width, int height, int loops)
+{
+    return draw(cr, aa, aligned, cairo_fill,
+               width, height, loops);
+}
+
+static cairo_time_t
+draw_misaligned_aa (cairo_t *cr, int width, int height, int loops)
+{
+    return draw(cr, aa, misaligned, cairo_fill,
+               width, height, loops);
+}
+
+static cairo_time_t
+draw_rotated_aa (cairo_t *cr, int width, int height, int loops)
+{
+    return draw(cr, aa, rotated, cairo_fill,
+               width, height, loops);
+}
+
+static cairo_time_t
+draw_aligned_mono (cairo_t *cr, int width, int height, int loops)
+{
+    return draw(cr, mono, aligned, cairo_fill,
+               width, height, loops);
+}
+
+static cairo_time_t
+draw_misaligned_mono (cairo_t *cr, int width, int height, int loops)
+{
+    return draw(cr, mono, misaligned, cairo_fill,
+               width, height, loops);
+}
+
+static cairo_time_t
+draw_rotated_mono (cairo_t *cr, int width, int height, int loops)
+{
+    return draw(cr, mono, rotated, cairo_fill,
+               width, height, loops);
+}
+
+#define F(name, op,transform,aa) \
+static cairo_time_t \
+draw_##name (cairo_t *cr, int width, int height, int loops) \
+{ return draw(cr, (aa), (transform), (op), width, height, loops); }
+
+F(clip_aligned, clip, aligned, aa)
+F(clip_misaligned, clip, misaligned, aa)
+F(clip_rotated, clip, rotated, aa)
+F(clip_aligned_mono, clip, aligned, mono)
+F(clip_misaligned_mono, clip, misaligned, mono)
+F(clip_rotated_mono, clip, rotated, mono)
+
+F(clip_alpha_aligned, clip_alpha, aligned, aa)
+F(clip_alpha_misaligned, clip_alpha, misaligned, aa)
+F(clip_alpha_rotated, clip_alpha, rotated, aa)
+F(clip_alpha_aligned_mono, clip_alpha, aligned, mono)
+F(clip_alpha_misaligned_mono, clip_alpha, misaligned, mono)
+F(clip_alpha_rotated_mono, clip_alpha, rotated, mono)
+
+cairo_bool_t
+hatching_enabled (cairo_perf_t *perf)
+{
+    return cairo_perf_can_run (perf, "hatching", NULL);
+}
+
+void
+hatching (cairo_perf_t *perf, cairo_t *cr, int width, int height)
+{
+    cairo_perf_run (perf, "hatching-aligned-aa", draw_aligned_aa, NULL);
+    cairo_perf_run (perf, "hatching-misaligned-aa", draw_misaligned_aa, NULL);
+    cairo_perf_run (perf, "hatching-rotated-aa", draw_rotated_aa, NULL);
+    cairo_perf_run (perf, "hatching-aligned-mono", draw_aligned_mono, NULL);
+    cairo_perf_run (perf, "hatching-misaligned-mono", draw_misaligned_mono, NULL);
+    cairo_perf_run (perf, "hatching-rotated-mono", draw_rotated_mono, NULL);
+
+    cairo_perf_run (perf, "hatching-clip-aligned-aa", draw_clip_aligned, NULL);
+    cairo_perf_run (perf, "hatching-clip-misaligned-aa", draw_clip_misaligned, NULL);
+    cairo_perf_run (perf, "hatching-clip-rotated-aa", draw_clip_rotated, NULL);
+    cairo_perf_run (perf, "hatching-clip-aligned-mono", draw_clip_aligned_mono, NULL);
+    cairo_perf_run (perf, "hatching-clip-misaligned-mono", draw_clip_misaligned_mono, NULL);
+    cairo_perf_run (perf, "hatching-clip-rotated-mono", draw_clip_rotated_mono, NULL);
+
+    cairo_perf_run (perf, "hatching-clip-alpha-aligned-aa", draw_clip_alpha_aligned, NULL);
+    cairo_perf_run (perf, "hatching-clip-alpha-misaligned-aa", draw_clip_alpha_misaligned, NULL);
+    cairo_perf_run (perf, "hatching-clip-alpha-rotated-aa", draw_clip_alpha_rotated, NULL);
+    cairo_perf_run (perf, "hatching-clip-alpha-aligned-mono", draw_clip_alpha_aligned_mono, NULL);
+    cairo_perf_run (perf, "hatching-clip-alpha-misaligned-mono", draw_clip_alpha_misaligned_mono, NULL);
+    cairo_perf_run (perf, "hatching-clip-alpha-rotated-mono", draw_clip_alpha_rotated_mono, NULL);
+}
diff --git a/perf/micro/intersections.c b/perf/micro/intersections.c
new file mode 100644 (file)
index 0000000..57931fa
--- /dev/null
@@ -0,0 +1,160 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright (c) 2008  M Joonas Pihlaja
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "cairo-perf.h"
+
+#define NUM_SEGMENTS 256
+
+static unsigned state;
+static double
+uniform_random (double minval, double maxval)
+{
+    static unsigned const poly = 0x9a795537U;
+    unsigned n = 32;
+    while (n-->0)
+       state = 2*state < state ? (2*state ^ poly) : 2*state;
+    return minval + state * (maxval - minval) / 4294967296.0;
+}
+
+static cairo_time_t
+draw_random (cairo_t *cr, cairo_fill_rule_t fill_rule,
+            int width, int height, int loops)
+{
+    double x[NUM_SEGMENTS];
+    double y[NUM_SEGMENTS];
+    int i;
+
+    cairo_save (cr);
+    cairo_set_source_rgb (cr, 0, 0, 0);
+    cairo_paint (cr);
+
+    for (i = 0; i < NUM_SEGMENTS; i++) {
+         x[i] = uniform_random (0, width);
+         y[i] = uniform_random (0, height);
+    }
+
+    state = 0x12345678;
+    cairo_translate (cr, 1, 1);
+    cairo_set_fill_rule (cr, fill_rule);
+    cairo_set_source_rgb (cr, 1, 0, 0);
+
+    cairo_new_path (cr);
+    cairo_move_to (cr, 0, 0);
+    for (i = 0; i < NUM_SEGMENTS; i++)
+       cairo_line_to (cr, x[i], y[i]);
+    cairo_close_path (cr);
+
+    cairo_perf_timer_start ();
+    while (loops--)
+        cairo_fill_preserve (cr);
+    cairo_perf_timer_stop ();
+
+    cairo_restore (cr);
+
+    return cairo_perf_timer_elapsed ();
+}
+
+static cairo_time_t
+draw_random_curve (cairo_t *cr, cairo_fill_rule_t fill_rule,
+                  int width, int height, int loops)
+{
+    double x[3*NUM_SEGMENTS];
+    double y[3*NUM_SEGMENTS];
+    int i;
+
+    cairo_save (cr);
+    cairo_set_source_rgb (cr, 0, 0, 0);
+    cairo_paint (cr);
+
+    for (i = 0; i < 3*NUM_SEGMENTS; i++) {
+         x[i] = uniform_random (0, width);
+         y[i] = uniform_random (0, height);
+    }
+
+    state = 0x12345678;
+    cairo_translate (cr, 1, 1);
+    cairo_set_fill_rule (cr, fill_rule);
+    cairo_set_source_rgb (cr, 1, 0, 0);
+
+    cairo_new_path (cr);
+    cairo_move_to (cr, 0, 0);
+    for (i = 0; i < NUM_SEGMENTS; i++) {
+       cairo_curve_to (cr,
+                       x[3*i+0], y[3*i+0],
+                       x[3*i+1], y[3*i+1],
+                       x[3*i+2], y[3*i+2]);
+    }
+    cairo_close_path (cr);
+
+    cairo_perf_timer_start ();
+    while (loops--)
+        cairo_fill_preserve (cr);
+    cairo_perf_timer_stop ();
+
+    cairo_restore (cr);
+
+    return cairo_perf_timer_elapsed ();
+}
+
+static cairo_time_t
+random_eo (cairo_t *cr, int width, int height, int loops)
+{
+    return draw_random (cr, CAIRO_FILL_RULE_EVEN_ODD, width, height, loops);
+}
+
+static cairo_time_t
+random_nz (cairo_t *cr, int width, int height, int loops)
+{
+    return draw_random (cr, CAIRO_FILL_RULE_WINDING, width, height, loops);
+}
+
+static cairo_time_t
+random_curve_eo (cairo_t *cr, int width, int height, int loops)
+{
+    return draw_random_curve (cr, CAIRO_FILL_RULE_EVEN_ODD, width, height, loops);
+}
+
+static cairo_time_t
+random_curve_nz (cairo_t *cr, int width, int height, int loops)
+{
+    return draw_random_curve (cr, CAIRO_FILL_RULE_WINDING, width, height, loops);
+}
+
+cairo_bool_t
+intersections_enabled (cairo_perf_t *perf)
+{
+    return cairo_perf_can_run (perf, "intersections", NULL);
+}
+
+void
+intersections (cairo_perf_t *perf, cairo_t *cr, int width, int height)
+{
+    cairo_perf_run (perf, "intersections-nz-fill", random_nz, NULL);
+    cairo_perf_run (perf, "intersections-eo-fill", random_eo, NULL);
+
+    cairo_perf_run (perf, "intersections-nz-curve-fill", random_curve_nz, NULL);
+    cairo_perf_run (perf, "intersections-eo-curve-fill", random_curve_eo, NULL);
+}
diff --git a/perf/micro/line.c b/perf/micro/line.c
new file mode 100644 (file)
index 0000000..3ed5f8d
--- /dev/null
@@ -0,0 +1,222 @@
+/*
+ * Copyright © 2011 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-perf.h"
+
+static cairo_time_t
+horizontal (cairo_t *cr, int width, int height, int loops)
+{
+    double h = height/2 + .5;
+
+    cairo_move_to (cr, 0, h);
+    cairo_line_to (cr, width, h);
+
+    cairo_perf_timer_start ();
+
+    while (loops--)
+       cairo_stroke_preserve (cr);
+
+    cairo_perf_timer_stop ();
+
+    cairo_new_path (cr);
+
+    return cairo_perf_timer_elapsed ();
+}
+
+static cairo_time_t
+horizontal_hair (cairo_t *cr, int width, int height, int loops)
+{
+    cairo_set_line_width (cr, 1.);
+    return horizontal (cr, width, height, loops);
+}
+
+static cairo_time_t
+horizontal_wide (cairo_t *cr, int width, int height, int loops)
+{
+    cairo_set_line_width (cr, 5.);
+    return horizontal (cr, width, height, loops);
+}
+
+static cairo_time_t
+nearly_horizontal (cairo_t *cr, int width, int height, int loops)
+{
+    double h = height/2;
+
+    cairo_move_to (cr, 0, h);
+    cairo_line_to (cr, width, h+1);
+
+    cairo_perf_timer_start ();
+
+    while (loops--)
+       cairo_stroke_preserve (cr);
+
+    cairo_perf_timer_stop ();
+
+    cairo_new_path (cr);
+
+    return cairo_perf_timer_elapsed ();
+}
+
+static cairo_time_t
+nearly_horizontal_hair (cairo_t *cr, int width, int height, int loops)
+{
+    cairo_set_line_width (cr, 1.);
+    return nearly_horizontal (cr, width, height, loops);
+}
+
+static cairo_time_t
+nearly_horizontal_wide (cairo_t *cr, int width, int height, int loops)
+{
+    cairo_set_line_width (cr, 5.);
+    return nearly_horizontal (cr, width, height, loops);
+}
+
+
+static cairo_time_t
+vertical (cairo_t *cr, int width, int height, int loops)
+{
+    double w = width/2 + .5;
+
+    cairo_move_to (cr, w, 0);
+    cairo_line_to (cr, w, height);
+
+    cairo_perf_timer_start ();
+
+    while (loops--)
+       cairo_stroke_preserve (cr);
+
+    cairo_perf_timer_stop ();
+
+    cairo_new_path (cr);
+
+    return cairo_perf_timer_elapsed ();
+}
+
+static cairo_time_t
+vertical_hair (cairo_t *cr, int width, int height, int loops)
+{
+    cairo_set_line_width (cr, 1.);
+    return vertical (cr, width, height, loops);
+}
+
+static cairo_time_t
+vertical_wide (cairo_t *cr, int width, int height, int loops)
+{
+    cairo_set_line_width (cr, 5.);
+    return vertical (cr, width, height, loops);
+}
+
+static cairo_time_t
+nearly_vertical (cairo_t *cr, int width, int height, int loops)
+{
+    double w = width/2;
+
+    cairo_move_to (cr, w, 0);
+    cairo_line_to (cr, w+1, height);
+
+    cairo_perf_timer_start ();
+
+    while (loops--)
+       cairo_stroke_preserve (cr);
+
+    cairo_perf_timer_stop ();
+
+    cairo_new_path (cr);
+
+    return cairo_perf_timer_elapsed ();
+}
+
+static cairo_time_t
+nearly_vertical_hair (cairo_t *cr, int width, int height, int loops)
+{
+    cairo_set_line_width (cr, 1.);
+    return nearly_vertical (cr, width, height, loops);
+}
+
+static cairo_time_t
+nearly_vertical_wide (cairo_t *cr, int width, int height, int loops)
+{
+    cairo_set_line_width (cr, 5.);
+    return nearly_vertical (cr, width, height, loops);
+}
+
+
+static cairo_time_t
+diagonal (cairo_t *cr, int width, int height, int loops)
+{
+    cairo_move_to (cr, 0, 0);
+    cairo_line_to (cr, width, height);
+
+    cairo_perf_timer_start ();
+
+    while (loops--)
+       cairo_stroke_preserve (cr);
+
+    cairo_perf_timer_stop ();
+
+    cairo_new_path (cr);
+
+    return cairo_perf_timer_elapsed ();
+}
+
+static cairo_time_t
+diagonal_hair (cairo_t *cr, int width, int height, int loops)
+{
+    cairo_set_line_width (cr, 1.);
+    return diagonal (cr, width, height, loops);
+}
+
+static cairo_time_t
+diagonal_wide (cairo_t *cr, int width, int height, int loops)
+{
+    cairo_set_line_width (cr, 5.);
+    return diagonal (cr, width, height, loops);
+}
+
+cairo_bool_t
+line_enabled (cairo_perf_t *perf)
+{
+    return cairo_perf_can_run (perf, "line", NULL);
+}
+
+void
+line (cairo_perf_t *perf, cairo_t *cr, int width, int height)
+{
+    cairo_set_source_rgb (cr, 1., 1., 1.);
+
+    cairo_perf_run (perf, "line-hh", horizontal_hair, NULL);
+    cairo_perf_run (perf, "line-hw", horizontal_wide, NULL);
+    cairo_perf_run (perf, "line-nhh", nearly_horizontal_hair, NULL);
+    cairo_perf_run (perf, "line-nhw", nearly_horizontal_wide, NULL);
+
+    cairo_perf_run (perf, "line-vh", vertical_hair, NULL);
+    cairo_perf_run (perf, "line-vw", vertical_wide, NULL);
+    cairo_perf_run (perf, "line-nvh", nearly_vertical_hair, NULL);
+    cairo_perf_run (perf, "line-nvw", nearly_vertical_wide, NULL);
+
+    cairo_perf_run (perf, "line-dh", diagonal_hair, NULL);
+    cairo_perf_run (perf, "line-dw", diagonal_wide, NULL);
+}
diff --git a/perf/micro/long-dashed-lines.c b/perf/micro/long-dashed-lines.c
new file mode 100644 (file)
index 0000000..ba66a4a
--- /dev/null
@@ -0,0 +1,74 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/*
+ * Copyright © 2007 Mozilla Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Vladimir Vukicevic <vladimir@pobox.com>
+ */
+
+#include "cairo-perf.h"
+
+static cairo_time_t
+do_long_dashed_lines (cairo_t *cr, int width, int height, int loops)
+{
+    double dash[2] = { 2.0, 2.0 };
+    int i;
+
+    cairo_save (cr);
+    cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
+    cairo_paint (cr);
+
+    cairo_set_source_rgb (cr, 1.0, 0.0, 0.0);
+    cairo_set_dash (cr, dash, 2, 0.0);
+
+    cairo_new_path (cr);
+    cairo_set_line_width (cr, 1.0);
+
+    for (i = 0; i < height-1; i++) {
+       double y0 = (double) i + 0.5;
+       cairo_move_to (cr, 0.0, y0);
+       cairo_line_to (cr, width, y0);
+    }
+
+    cairo_perf_timer_start ();
+
+    while (loops--)
+       cairo_stroke_preserve (cr);
+
+    cairo_perf_timer_stop ();
+
+    cairo_restore (cr);
+
+    return cairo_perf_timer_elapsed ();
+}
+
+cairo_bool_t
+long_dashed_lines_enabled (cairo_perf_t *perf)
+{
+    return cairo_perf_can_run (perf, "long-dashed-lines", NULL);
+}
+
+void
+long_dashed_lines (cairo_perf_t *perf, cairo_t *cr, int width, int height)
+{
+    cairo_perf_run (perf, "long-dashed-lines", do_long_dashed_lines, NULL);
+}
diff --git a/perf/micro/long-lines.c b/perf/micro/long-lines.c
new file mode 100644 (file)
index 0000000..a0d134c
--- /dev/null
@@ -0,0 +1,148 @@
+/*
+ * Copyright © 2006 Red Hat, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ */
+
+#include "cairo-perf.h"
+
+/* This test case is designed to illustrate a performance bug in
+ * drawing very long lines, where most of the line is out of bounds of
+ * the destination surface, (but some portion of the line is
+ * visibile). These results are in the "long-lines-uncropped" report.
+ *
+ * For comparison, this test also renders the visible portions of the
+ * same lines, (this is the "long-lines-cropped" report).
+ */
+
+typedef enum {
+    LONG_LINES_CROPPED = 0x1,
+    LONG_LINES_ONCE = 0x2,
+} long_lines_crop_t;
+#define NUM_LINES    20
+#define LONG_FACTOR  50.0
+
+static cairo_time_t
+do_long_lines (cairo_t *cr, int width, int height, int loops, long_lines_crop_t crop)
+{
+    int i;
+    double x, y, dx, dy, min_x, min_y, max_x, max_y;
+    double outer_width, outer_height;
+
+    cairo_save (cr);
+
+    cairo_translate (cr, width / 2, height / 2);
+
+    if (crop & LONG_LINES_CROPPED) {
+       outer_width = width;
+       outer_height = height;
+       cairo_set_source_rgb (cr, 0.0, 1.0, 0.0); /* green */
+    } else {
+       outer_width = LONG_FACTOR * width;
+       outer_height = LONG_FACTOR * height;
+       cairo_set_source_rgb (cr, 1.0, 0.0, 0.0); /* red */
+    }
+
+    min_x = x = - outer_width / 2.0;
+    min_y = y = - outer_height / 2.0;
+    max_x = outer_width / 2.0;
+    max_y = outer_height / 2.0;
+    dx = outer_width / NUM_LINES;
+    dy = outer_height / NUM_LINES;
+
+    cairo_perf_timer_start ();
+
+    while (loops--) {
+       for (i = 0; i <= NUM_LINES; i++) {
+           cairo_move_to (cr, 0, 0);
+           cairo_line_to (cr, x, min_y);
+           if ((crop & LONG_LINES_ONCE) == 0)
+               cairo_stroke (cr);
+
+           cairo_move_to (cr, 0, 0);
+           cairo_line_to (cr, x, max_y);
+           if ((crop & LONG_LINES_ONCE) == 0)
+               cairo_stroke (cr);
+
+           cairo_move_to (cr, 0, 0);
+           cairo_line_to (cr, min_x, y);
+           if ((crop & LONG_LINES_ONCE) == 0)
+               cairo_stroke (cr);
+
+           cairo_move_to (cr, 0, 0);
+           cairo_line_to (cr, max_x, y);
+           if ((crop & LONG_LINES_ONCE) == 0)
+               cairo_stroke (cr);
+
+           x += dx;
+           y += dy;
+       }
+       if (crop & LONG_LINES_ONCE)
+           cairo_stroke (cr);
+    }
+
+    cairo_perf_timer_stop ();
+
+    cairo_restore (cr);
+
+    return cairo_perf_timer_elapsed ();
+}
+
+static cairo_time_t
+long_lines_uncropped (cairo_t *cr, int width, int height, int loops)
+{
+    return do_long_lines (cr, width, height, loops, 0);
+}
+
+static cairo_time_t
+long_lines_uncropped_once (cairo_t *cr, int width, int height, int loops)
+{
+    return do_long_lines (cr, width, height, loops, LONG_LINES_ONCE);
+}
+
+static cairo_time_t
+long_lines_cropped (cairo_t *cr, int width, int height, int loops)
+{
+    return do_long_lines (cr, width, height, loops, LONG_LINES_CROPPED);
+}
+
+static cairo_time_t
+long_lines_cropped_once (cairo_t *cr, int width, int height, int loops)
+{
+    return do_long_lines (cr, width, height, loops, LONG_LINES_CROPPED | LONG_LINES_ONCE);
+}
+
+cairo_bool_t
+long_lines_enabled (cairo_perf_t *perf)
+{
+    return cairo_perf_can_run (perf, "long-lines", NULL);
+}
+
+void
+long_lines (cairo_perf_t *perf, cairo_t *cr, int width, int height)
+{
+    cairo_perf_run (perf, "long-lines-uncropped", long_lines_uncropped, NULL);
+    cairo_perf_run (perf, "long-lines-uncropped-once", long_lines_uncropped_once, NULL);
+    cairo_perf_run (perf, "long-lines-cropped", long_lines_cropped, NULL);
+    cairo_perf_run (perf, "long-lines-cropped-once", long_lines_cropped_once, NULL);
+}
diff --git a/perf/micro/many-curves.c b/perf/micro/many-curves.c
new file mode 100644 (file)
index 0000000..f985d34
--- /dev/null
@@ -0,0 +1,135 @@
+/*
+ * Copyright © 2011 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-perf.h"
+
+static uint32_t state;
+
+static double
+uniform_random (double minval, double maxval)
+{
+    static uint32_t const poly = 0x9a795537U;
+    uint32_t n = 32;
+    while (n-->0)
+       state = 2*state < state ? (2*state ^ poly) : 2*state;
+    return minval + state * (maxval - minval) / 4294967296.0;
+}
+
+static cairo_time_t
+do_many_curves_stroked (cairo_t *cr, int width, int height, int loops)
+{
+    int count;
+
+    state = 0xc0ffee;
+    cairo_move_to (cr, uniform_random (0, width), uniform_random (0, height));
+    for (count = 0; count < 1000; count++) {
+       double x1 = uniform_random (0, width);
+       double x2 = uniform_random (0, width);
+       double x3 = uniform_random (0, width);
+       double y1 = uniform_random (0, height);
+       double y2 = uniform_random (0, height);
+       double y3 = uniform_random (0, height);
+       cairo_curve_to (cr, x1, y1, x2, y2, x3, y3);
+    }
+
+    cairo_perf_timer_start ();
+
+    while (loops--)
+       cairo_stroke_preserve (cr);
+
+    cairo_perf_timer_stop ();
+
+    cairo_new_path (cr);
+
+    return cairo_perf_timer_elapsed ();
+}
+
+static cairo_time_t
+do_many_curves_hair_stroked (cairo_t *cr, int width, int height, int loops)
+{
+    cairo_set_line_width (cr, 1.);
+    return do_many_curves_stroked (cr, width, height, loops);
+}
+
+static cairo_time_t
+do_many_curves_wide_stroked (cairo_t *cr, int width, int height, int loops)
+{
+    cairo_set_line_width (cr, 5.);
+    return do_many_curves_stroked (cr, width, height, loops);
+}
+
+static cairo_time_t
+do_many_curves_filled (cairo_t *cr, int width, int height, int loops)
+{
+    int count;
+
+    state = 0xc0ffee;
+    for (count = 0; count < 1000; count++) {
+       double x0 = uniform_random (0, width);
+       double x1 = uniform_random (0, width);
+       double x2 = uniform_random (0, width);
+       double x3 = uniform_random (0, width);
+       double xm = uniform_random (0, width);
+       double xn = uniform_random (0, width);
+       double y0 = uniform_random (0, height);
+       double y1 = uniform_random (0, height);
+       double y2 = uniform_random (0, height);
+       double y3 = uniform_random (0, height);
+       double ym = uniform_random (0, height);
+       double yn = uniform_random (0, height);
+       cairo_move_to (cr, xm, ym);
+       cairo_curve_to (cr, x1, y1, x2, y2, xn, yn);
+       cairo_curve_to (cr, x3, y3, x0, y0, xm, ym);
+       cairo_close_path (cr);
+    }
+
+    cairo_perf_timer_start ();
+
+    while (loops--)
+       cairo_fill_preserve (cr);
+
+    cairo_perf_timer_stop ();
+
+    cairo_new_path (cr);
+
+    return cairo_perf_timer_elapsed ();
+}
+
+cairo_bool_t
+many_curves_enabled (cairo_perf_t *perf)
+{
+    return cairo_perf_can_run (perf, "many-curves", NULL);
+}
+
+void
+many_curves (cairo_perf_t *perf, cairo_t *cr, int width, int height)
+{
+    cairo_set_source_rgb (cr, 1., 1., 1.);
+
+    cairo_perf_run (perf, "many-curves-hair-stroked", do_many_curves_hair_stroked, NULL);
+    cairo_perf_run (perf, "many-curves-wide-stroked", do_many_curves_wide_stroked, NULL);
+    cairo_perf_run (perf, "many-curves-filled", do_many_curves_filled, NULL);
+}
diff --git a/perf/micro/many-fills.c b/perf/micro/many-fills.c
new file mode 100644 (file)
index 0000000..9d3fd64
--- /dev/null
@@ -0,0 +1,187 @@
+/*
+ * Copyright © 2011 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+
+/* This is a variant on many strokes where we precompute
+ * a simplified stroke-to-path.
+ * When we have a real stroke-to-path, it would useful to compare the cost
+ * of stroking vs filling the "identical" paths.
+ */
+
+#include "cairo-perf.h"
+
+static uint32_t state;
+
+static double
+uniform_random (double minval, double maxval)
+{
+    static uint32_t const poly = 0x9a795537U;
+    uint32_t n = 32;
+    while (n-->0)
+       state = 2*state < state ? (2*state ^ poly) : 2*state;
+    return minval + state * (maxval - minval) / 4294967296.0;
+}
+
+static cairo_time_t
+do_many_fills_ha (cairo_t *cr, int width, int height, int loops)
+{
+    int count;
+
+    state = 0xc0ffee;
+    for (count = 0; count < 1000; count++) {
+       double y = floor (uniform_random (0, height));
+       double x = floor (uniform_random (0, width));
+       cairo_rectangle (cr, x, y, ceil (uniform_random (0, width)) - x, 1);
+    }
+
+    cairo_perf_timer_start ();
+
+    while (loops--)
+       cairo_fill_preserve (cr);
+
+    cairo_perf_timer_stop ();
+
+    cairo_new_path (cr);
+
+    return cairo_perf_timer_elapsed ();
+}
+
+static cairo_time_t
+do_many_fills_h (cairo_t *cr, int width, int height, int loops)
+{
+    int count;
+
+    state = 0xc0ffee;
+    for (count = 0; count < 1000; count++) {
+       double y = uniform_random (0, height);
+       double x = uniform_random (0, width);
+       cairo_rectangle (cr, x, y, uniform_random (0, width) - x, 1);
+    }
+
+    cairo_perf_timer_start ();
+
+    while (loops--)
+       cairo_fill_preserve (cr);
+
+    cairo_perf_timer_stop ();
+
+    cairo_new_path (cr);
+
+    return cairo_perf_timer_elapsed ();
+}
+
+static cairo_time_t
+do_many_fills_va (cairo_t *cr, int width, int height, int loops)
+{
+    int count;
+
+    state = 0xc0ffee;
+    for (count = 0; count < 1000; count++) {
+       double x = floor (uniform_random (0, width));
+       double y = floor (uniform_random (0, height));
+       cairo_rectangle (cr, x, y, 1, ceil (uniform_random (0, height) - y));
+    }
+
+    cairo_perf_timer_start ();
+
+    while (loops--)
+       cairo_fill_preserve (cr);
+
+    cairo_perf_timer_stop ();
+
+    cairo_new_path (cr);
+
+    return cairo_perf_timer_elapsed ();
+}
+
+static cairo_time_t
+do_many_fills_v (cairo_t *cr, int width, int height, int loops)
+{
+    int count;
+
+    state = 0xc0ffee;
+    for (count = 0; count < 1000; count++) {
+       double x = uniform_random (0, width);
+       double y = uniform_random (0, height);
+       cairo_rectangle (cr, x, y, 1, uniform_random (0, height) - y);
+    }
+
+    cairo_perf_timer_start ();
+
+    while (loops--)
+       cairo_fill_preserve (cr);
+
+    cairo_perf_timer_stop ();
+
+    cairo_new_path (cr);
+
+    return cairo_perf_timer_elapsed ();
+}
+
+static cairo_time_t
+do_many_fills (cairo_t *cr, int width, int height, int loops)
+{
+    int count;
+
+    /* lots and lots of overlapping stroke-like fills */
+    state = 0xc0ffee;
+    for (count = 0; count < 1000; count++) {
+       cairo_save (cr);
+       cairo_translate (cr,
+                        uniform_random (0, width),
+                        uniform_random (0, height));
+       cairo_rotate (cr, uniform_random (-M_PI,M_PI));
+       cairo_rectangle (cr, 0, 0, uniform_random (0, width), 1);
+       cairo_restore (cr);
+    }
+
+    cairo_perf_timer_start ();
+
+    while (loops--)
+       cairo_fill_preserve (cr);
+
+    cairo_perf_timer_stop ();
+
+    cairo_new_path (cr);
+
+    return cairo_perf_timer_elapsed ();
+}
+
+cairo_bool_t
+many_fills_enabled (cairo_perf_t *perf)
+{
+    return cairo_perf_can_run (perf, "many-fills", NULL);
+}
+
+void
+many_fills (cairo_perf_t *perf, cairo_t *cr, int width, int height)
+{
+    cairo_perf_run (perf, "many-fills-halign", do_many_fills_ha, NULL);
+    cairo_perf_run (perf, "many-fills-valign", do_many_fills_va, NULL);
+    cairo_perf_run (perf, "many-fills-horizontal", do_many_fills_h, NULL);
+    cairo_perf_run (perf, "many-fills-vertical", do_many_fills_v, NULL);
+    cairo_perf_run (perf, "many-fills-random", do_many_fills, NULL);
+}
diff --git a/perf/micro/many-strokes.c b/perf/micro/many-strokes.c
new file mode 100644 (file)
index 0000000..9aeb393
--- /dev/null
@@ -0,0 +1,186 @@
+/*
+ * Copyright © 2011 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-perf.h"
+
+static uint32_t state;
+
+static double
+uniform_random (double minval, double maxval)
+{
+    static uint32_t const poly = 0x9a795537U;
+    uint32_t n = 32;
+    while (n-->0)
+       state = 2*state < state ? (2*state ^ poly) : 2*state;
+    return minval + state * (maxval - minval) / 4294967296.0;
+}
+
+static cairo_time_t
+do_many_strokes_ha (cairo_t *cr, int width, int height, int loops)
+{
+    int count;
+
+    state = 0xc0ffee;
+    for (count = 0; count < 1000; count++) {
+       double h = floor (uniform_random (0, height)) + .5;
+       cairo_move_to (cr, floor (uniform_random (0, width)), h);
+       cairo_line_to (cr, ceil (uniform_random (0, width)), h);
+    }
+
+    cairo_set_line_width (cr, 1.);
+
+    cairo_perf_timer_start ();
+
+    while (loops--)
+       cairo_stroke_preserve (cr);
+
+    cairo_perf_timer_stop ();
+
+    cairo_new_path (cr);
+
+    return cairo_perf_timer_elapsed ();
+}
+
+static cairo_time_t
+do_many_strokes_h (cairo_t *cr, int width, int height, int loops)
+{
+    int count;
+
+    state = 0xc0ffee;
+    for (count = 0; count < 1000; count++) {
+       double h = uniform_random (0, height);
+       cairo_move_to (cr, uniform_random (0, width), h);
+       cairo_line_to (cr, uniform_random (0, width), h);
+    }
+
+    cairo_set_line_width (cr, 1.);
+
+    cairo_perf_timer_start ();
+
+    while (loops--)
+       cairo_stroke_preserve (cr);
+
+    cairo_perf_timer_stop ();
+
+    cairo_new_path (cr);
+
+    return cairo_perf_timer_elapsed ();
+}
+
+static cairo_time_t
+do_many_strokes_va (cairo_t *cr, int width, int height, int loops)
+{
+    int count;
+
+    state = 0xc0ffee;
+    for (count = 0; count < 1000; count++) {
+       double v = floor (uniform_random (0, width)) + .5;
+       cairo_move_to (cr, v, floor (uniform_random (0, height)));
+       cairo_line_to (cr, v, ceil (uniform_random (0, height)));
+    }
+
+    cairo_set_line_width (cr, 1.);
+
+    cairo_perf_timer_start ();
+
+    while (loops--)
+       cairo_stroke_preserve (cr);
+
+    cairo_perf_timer_stop ();
+
+    cairo_new_path (cr);
+
+    return cairo_perf_timer_elapsed ();
+}
+
+static cairo_time_t
+do_many_strokes_v (cairo_t *cr, int width, int height, int loops)
+{
+    int count;
+
+    state = 0xc0ffee;
+    for (count = 0; count < 1000; count++) {
+       double v = uniform_random (0, width);
+       cairo_move_to (cr, v, uniform_random (0, height));
+       cairo_line_to (cr, v, uniform_random (0, height));
+    }
+
+    cairo_set_line_width (cr, 1.);
+
+    cairo_perf_timer_start ();
+
+    while (loops--)
+       cairo_stroke_preserve (cr);
+
+    cairo_perf_timer_stop ();
+
+    cairo_new_path (cr);
+
+    return cairo_perf_timer_elapsed ();
+}
+
+static cairo_time_t
+do_many_strokes (cairo_t *cr, int width, int height, int loops)
+{
+    int count;
+
+    /* lots and lots of overlapping strokes */
+    state = 0xc0ffee;
+    for (count = 0; count < 1000; count++) {
+       cairo_line_to (cr,
+                      uniform_random (0, width),
+                      uniform_random (0, height));
+    }
+
+    cairo_set_line_width (cr, 1.);
+
+    cairo_perf_timer_start ();
+
+    while (loops--)
+       cairo_stroke_preserve (cr);
+
+    cairo_perf_timer_stop ();
+
+    cairo_new_path (cr);
+
+    return cairo_perf_timer_elapsed ();
+}
+
+cairo_bool_t
+many_strokes_enabled (cairo_perf_t *perf)
+{
+    return cairo_perf_can_run (perf, "many-strokes", NULL);
+}
+
+void
+many_strokes (cairo_perf_t *perf, cairo_t *cr, int width, int height)
+{
+    cairo_perf_run (perf, "many-strokes-halign", do_many_strokes_ha, NULL);
+    cairo_perf_run (perf, "many-strokes-valign", do_many_strokes_va, NULL);
+    cairo_perf_run (perf, "many-strokes-horizontal", do_many_strokes_h, NULL);
+    cairo_perf_run (perf, "many-strokes-vertical", do_many_strokes_v, NULL);
+    cairo_perf_run (perf, "many-strokes-random", do_many_strokes, NULL);
+}
diff --git a/perf/micro/mask.c b/perf/micro/mask.c
new file mode 100644 (file)
index 0000000..11a3ba7
--- /dev/null
@@ -0,0 +1,305 @@
+/*
+ * Copyright © 2006 Red Hat, Inc.
+ * Copyright © 2009 Chris Wilson
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ *         Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-perf.h"
+
+static cairo_time_t
+do_mask_solid (cairo_t *cr, int width, int height, int loops)
+{
+    cairo_pattern_t *mask;
+
+    mask = cairo_pattern_create_rgba (0, 0, 0, .5);
+
+    cairo_perf_timer_start ();
+
+    while (loops--)
+       cairo_mask (cr, mask);
+
+    cairo_perf_timer_stop ();
+
+    cairo_pattern_destroy (mask);
+
+    return cairo_perf_timer_elapsed ();
+}
+
+static cairo_surface_t *
+init_surface (cairo_surface_t *surface, int width, int height)
+{
+    cairo_t *cr;
+
+    cr = cairo_create (surface);
+    cairo_surface_destroy (surface);
+
+    cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+    cairo_set_source_rgb (cr, 0, 0, 0); /* back */
+    cairo_paint (cr);
+
+    cairo_set_source_rgba (cr, 1, 1, 1, 0.5); /* 50% */
+    cairo_new_path (cr);
+    cairo_rectangle (cr, 0, 0, width/2.0, height/2.0);
+    cairo_rectangle (cr, width/2.0, height/2.0, width/2.0, height/2.0);
+    cairo_fill (cr);
+
+    surface = cairo_surface_reference (cairo_get_target (cr));
+    cairo_destroy (cr);
+
+    return surface;
+}
+
+static cairo_time_t
+do_mask_image (cairo_t *cr, int width, int height, int loops)
+{
+    cairo_surface_t *surface;
+    cairo_pattern_t *mask;
+
+    surface = cairo_image_surface_create (CAIRO_FORMAT_A8, width, height);
+    mask = cairo_pattern_create_for_surface (init_surface (surface,
+                                                          width,
+                                                          height));
+    cairo_surface_destroy (surface);
+
+    cairo_perf_timer_start ();
+
+    while (loops--)
+       cairo_mask (cr, mask);
+
+    cairo_perf_timer_stop ();
+
+    cairo_pattern_destroy (mask);
+
+    return cairo_perf_timer_elapsed ();
+}
+
+static cairo_time_t
+do_mask_image_half (cairo_t *cr, int width, int height, int loops)
+{
+    cairo_surface_t *surface;
+    cairo_pattern_t *mask;
+    cairo_matrix_t matrix;
+
+    surface = cairo_image_surface_create (CAIRO_FORMAT_A8, width, height);
+    mask = cairo_pattern_create_for_surface (init_surface (surface,
+                                                          width,
+                                                          height));
+    cairo_surface_destroy (surface);
+    cairo_matrix_init_scale (&matrix, .5, .5);
+    cairo_pattern_set_matrix (mask, &matrix);
+
+    cairo_perf_timer_start ();
+
+    while (loops--)
+       cairo_mask (cr, mask);
+
+    cairo_perf_timer_stop ();
+
+    cairo_pattern_destroy (mask);
+
+    return cairo_perf_timer_elapsed ();
+}
+
+static cairo_time_t
+do_mask_image_double (cairo_t *cr, int width, int height, int loops)
+{
+    cairo_surface_t *surface;
+    cairo_pattern_t *mask;
+    cairo_matrix_t matrix;
+
+    surface = cairo_image_surface_create (CAIRO_FORMAT_A8, width, height);
+    mask = cairo_pattern_create_for_surface (init_surface (surface,
+                                                          width,
+                                                          height));
+    cairo_surface_destroy (surface);
+    cairo_matrix_init_scale (&matrix, 2., 2.);
+    cairo_pattern_set_matrix (mask, &matrix);
+
+    cairo_perf_timer_start ();
+
+    while (loops--)
+       cairo_mask (cr, mask);
+
+    cairo_perf_timer_stop ();
+
+    cairo_pattern_destroy (mask);
+
+    return cairo_perf_timer_elapsed ();
+}
+
+static cairo_time_t
+do_mask_similar (cairo_t *cr, int width, int height, int loops)
+{
+    cairo_surface_t *surface;
+    cairo_pattern_t *mask;
+
+    surface = cairo_surface_create_similar (cairo_get_group_target (cr),
+                                           CAIRO_CONTENT_ALPHA, width, height);
+    mask = cairo_pattern_create_for_surface (init_surface (surface,
+                                                          width,
+                                                          height));
+    cairo_surface_destroy (surface);
+
+    cairo_perf_timer_start ();
+
+    while (loops--)
+       cairo_mask (cr, mask);
+
+    cairo_perf_timer_stop ();
+
+    cairo_pattern_destroy (mask);
+
+    return cairo_perf_timer_elapsed ();
+}
+
+static cairo_time_t
+do_mask_similar_half (cairo_t *cr, int width, int height, int loops)
+{
+    cairo_surface_t *surface;
+    cairo_pattern_t *mask;
+    cairo_matrix_t matrix;
+
+    surface = cairo_surface_create_similar (cairo_get_group_target (cr),
+                                           CAIRO_CONTENT_ALPHA, width, height);
+    mask = cairo_pattern_create_for_surface (init_surface (surface,
+                                                          width,
+                                                          height));
+    cairo_surface_destroy (surface);
+    cairo_matrix_init_scale (&matrix, .5, .5);
+    cairo_pattern_set_matrix (mask, &matrix);
+
+    cairo_perf_timer_start ();
+
+    while (loops--)
+       cairo_mask (cr, mask);
+
+    cairo_perf_timer_stop ();
+
+    cairo_pattern_destroy (mask);
+
+    return cairo_perf_timer_elapsed ();
+}
+
+static cairo_time_t
+do_mask_similar_double (cairo_t *cr, int width, int height, int loops)
+{
+    cairo_surface_t *surface;
+    cairo_pattern_t *mask;
+    cairo_matrix_t matrix;
+
+    surface = cairo_surface_create_similar (cairo_get_group_target (cr),
+                                           CAIRO_CONTENT_ALPHA, width, height);
+    mask = cairo_pattern_create_for_surface (init_surface (surface,
+                                                          width,
+                                                          height));
+    cairo_surface_destroy (surface);
+    cairo_matrix_init_scale (&matrix, 2., 2.);
+    cairo_pattern_set_matrix (mask, &matrix);
+
+    cairo_perf_timer_start ();
+
+    while (loops--)
+       cairo_mask (cr, mask);
+
+    cairo_perf_timer_stop ();
+
+    cairo_pattern_destroy (mask);
+
+    return cairo_perf_timer_elapsed ();
+}
+
+static cairo_time_t
+do_mask_linear (cairo_t *cr, int width, int height, int loops)
+{
+    cairo_pattern_t *mask;
+
+    mask = cairo_pattern_create_linear (0.0, 0.0, width, height);
+    cairo_pattern_add_color_stop_rgba (mask, 0.0, 0, 0, 0, 0.5); /*  50% */
+    cairo_pattern_add_color_stop_rgba (mask, 0.0, 0, 0, 0, 1.0); /* 100% */
+
+    cairo_perf_timer_start ();
+
+    while (loops--)
+       cairo_mask (cr, mask);
+
+    cairo_perf_timer_stop ();
+
+    cairo_pattern_destroy (mask);
+
+    return cairo_perf_timer_elapsed ();
+}
+
+static cairo_time_t
+do_mask_radial (cairo_t *cr, int width, int height, int loops)
+{
+    cairo_pattern_t *mask;
+
+    mask = cairo_pattern_create_radial (width/2.0, height/2.0, 0.0,
+                                       width/2.0, height/2.0, width/2.0);
+    cairo_pattern_add_color_stop_rgba (mask, 0.0, 0, 0, 0, 0.5); /*  50% */
+    cairo_pattern_add_color_stop_rgba (mask, 0.0, 0, 0, 0, 1.0); /* 100% */
+
+    cairo_perf_timer_start ();
+
+    while (loops--)
+       cairo_mask (cr, mask);
+
+    cairo_perf_timer_stop ();
+
+    cairo_pattern_destroy (mask);
+
+    return cairo_perf_timer_elapsed ();
+}
+
+cairo_bool_t
+mask_enabled (cairo_perf_t *perf)
+{
+    return cairo_perf_can_run (perf, "mask", NULL);
+}
+
+void
+mask (cairo_perf_t *perf, cairo_t *cr, int width, int height)
+{
+    if (! cairo_perf_can_run (perf, "mask", NULL))
+       return;
+
+    cairo_perf_cover_sources_and_operators (perf, "mask-solid",
+                                           do_mask_solid, NULL);
+    cairo_perf_cover_sources_and_operators (perf, "mask-image",
+                                           do_mask_image, NULL);
+    cairo_perf_cover_sources_and_operators (perf, "mask-image-half",
+                                           do_mask_image_half, NULL);
+    cairo_perf_cover_sources_and_operators (perf, "mask-image-double",
+                                           do_mask_image_double, NULL);
+    cairo_perf_cover_sources_and_operators (perf, "mask-similar",
+                                           do_mask_similar, NULL);
+    cairo_perf_cover_sources_and_operators (perf, "mask-similar-half",
+                                           do_mask_similar_half, NULL);
+    cairo_perf_cover_sources_and_operators (perf, "mask-similar-double",
+                                           do_mask_similar_double, NULL);
+    cairo_perf_cover_sources_and_operators (perf, "mask-linear",
+                                           do_mask_linear, NULL);
+    cairo_perf_cover_sources_and_operators (perf, "mask-radial",
+                                           do_mask_radial, NULL);
+}
diff --git a/perf/micro/mosaic.c b/perf/micro/mosaic.c
new file mode 100644 (file)
index 0000000..ed30ae5
--- /dev/null
@@ -0,0 +1,176 @@
+/*
+ * Copyright © 2006 Joonas Pihlaja
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Joonas Pihlaja <jpihlaja@cc.helsinki.fi>
+ */
+#include "cairo-perf.h"
+
+/* Options passed in flags to mosaic_perform(): */
+#define MOSAIC_FILL 1          /* do rasterise */
+#define MOSAIC_TESSELLATE 0    /* just tessellate */
+#define MOSAIC_CURVE_TO 2      /* use curve bounded regions */
+#define MOSAIC_LINE_TO 0       /* use line bounded regions */
+
+struct mosaic_region {
+    unsigned rgb;              /* colour of this region in 0xRRGGBB format */
+    unsigned ncurves;          /* number of boundary curves. */
+};
+
+struct mosaic_region_iter {
+    int do_curves;
+    struct mosaic_region const *region;
+    double const *points;
+};
+
+#include "mosaic.h"
+
+static void
+mosaic_region_iter_init (struct mosaic_region_iter *iter, int do_curves)
+{
+    iter->region = mosaic_regions;
+    iter->points = mosaic_curve_points;
+    iter->do_curves = do_curves;
+}
+
+/* Create the next closed region as a path. */
+static int
+mosaic_next_path (cairo_t *cr, struct mosaic_region_iter *iter)
+{
+    double const *points = iter->points;
+    unsigned i;
+    unsigned ncurves = iter->region->ncurves;
+    if (0 == ncurves) {
+       return 0;
+    }
+
+    cairo_new_path (cr);
+    cairo_move_to (cr, points[0], points[1]);
+    points += 2;
+    for (i=0; i < ncurves; i++, points += 6) {
+       if (iter->do_curves) {
+           cairo_curve_to (cr,
+                           points[0], points[1],
+                           points[2], points[3],
+                           points[4], points[5]);
+       }
+       else {
+           cairo_line_to (cr,
+                           points[4], points[5]);
+       }
+    }
+    cairo_close_path (cr);
+    {
+       unsigned rgb = iter->region->rgb;
+       double r = ((rgb >> 16) & 255) / 255.0;
+       double g = ((rgb >>  8) & 255) / 255.0;
+       double b = ((rgb >>  0) & 255) / 255.0;
+       cairo_set_source_rgb (cr, r, g, b);
+    }
+
+    iter->points = iter->points + 2*(1 + 3*iter->region->ncurves);
+    iter->region++;
+    return 1;
+}
+
+static cairo_time_t
+mosaic_perform(cairo_t *cr, unsigned flags, int width, int height, int loops)
+{
+    struct mosaic_region_iter iter;
+
+    /* Scale to fit the window.*/
+    double minx = -40.7;
+    double maxx = 955.1;
+    double miny = -88.4;
+    double maxy = 884.5;
+
+    cairo_identity_matrix (cr);
+
+    if (flags & MOSAIC_FILL) {
+       cairo_set_source_rgb (cr, 1, 1, 1);
+       cairo_rectangle (cr, 0, 0, width, height);
+       cairo_fill (cr);
+    }
+
+    cairo_scale (cr, width / (maxx - minx) , height / (maxy - miny));
+    cairo_translate (cr, -minx, -miny);
+
+    /* Iterate over all closed regions in the mosaic filling or
+     * tessellating them as dictated by the flags.  */
+
+    cairo_perf_timer_start ();
+    while (loops--) {
+       mosaic_region_iter_init (&iter, flags & MOSAIC_CURVE_TO);
+       while (mosaic_next_path (cr, &iter)) {
+           if (flags & MOSAIC_FILL) {
+               cairo_fill (cr);
+           }
+           else {
+               double x, y;
+               cairo_get_current_point (cr, &x, &y);
+               cairo_in_fill (cr, x, y);
+           }
+       }
+    }
+    cairo_perf_timer_stop ();
+
+    return cairo_perf_timer_elapsed ();
+}
+
+static cairo_time_t
+mosaic_fill_curves (cairo_t *cr, int width, int height, int loops)
+{
+    return mosaic_perform (cr, MOSAIC_FILL | MOSAIC_CURVE_TO, width, height, loops);
+}
+
+static cairo_time_t
+mosaic_fill_lines (cairo_t *cr, int width, int height, int loops)
+{
+    return mosaic_perform (cr, MOSAIC_FILL | MOSAIC_LINE_TO, width, height, loops);
+}
+
+static cairo_time_t
+mosaic_tessellate_lines (cairo_t *cr, int width, int height, int loops)
+{
+    return mosaic_perform (cr, MOSAIC_TESSELLATE | MOSAIC_LINE_TO, width, height, loops);
+}
+
+static cairo_time_t
+mosaic_tessellate_curves (cairo_t *cr, int width, int height, int loops)
+{
+    return mosaic_perform (cr, MOSAIC_TESSELLATE | MOSAIC_CURVE_TO, width, height, loops);
+}
+
+cairo_bool_t
+mosaic_enabled (cairo_perf_t *perf)
+{
+    return cairo_perf_can_run (perf, "mosaic", NULL);
+}
+
+void
+mosaic (cairo_perf_t *perf, cairo_t *cr, int width, int height)
+{
+    cairo_perf_run (perf, "mosaic-fill-curves", mosaic_fill_curves, NULL);
+    cairo_perf_run (perf, "mosaic-fill-lines", mosaic_fill_lines, NULL);
+    cairo_perf_run (perf, "mosaic-tessellate-curves", mosaic_tessellate_curves, NULL);
+    cairo_perf_run (perf, "mosaic-tessellate-lines", mosaic_tessellate_lines, NULL);
+}
diff --git a/perf/micro/mosaic.h b/perf/micro/mosaic.h
new file mode 100644 (file)
index 0000000..e106864
--- /dev/null
@@ -0,0 +1,4387 @@
+/* Synthesised data. */
+static struct mosaic_region const mosaic_regions[] = {
+{0xef55b4,7},{0xf93bc6,11},{0xf28085,5},{0x92acbc,7},
+{0x85c3b1,7},{0xc3bc79,5},{0xd09d8d,5},{0xa788cb,8},
+{0xeff90f,14},{0x976ef2,4},{0xf230d8,5},{0xe226ef,6},
+{0xf49571,9},{0xcb6cc1,4},{0xc8af80,5},{0xf71ce7,8},
+{0xd38b9a,6},{0xf70ff2,8},{0xec907e,4},{0xf44abc,5},
+{0xf42bd8,7},{0xcbbc71,5},{0xf7a45f,5},{0xb15aec,3},
+{0xfc6995,9},{0x42bef9,7},{0x83afc6,5},{0xbe9f9a,6},
+{0x95ef76,6},{0xfcc33b,20},{0xbc5ae2,5},{0x69f997,7},
+{0x9f69f2,7},{0x79bec3,7},{0xda3de2,6},{0x76dda7,6},
+{0xc888aa,7},{0xd871b1,9},{0xda29f7,14},{0xe03dda,9},
+{0xec29e2,5},{0xd82bf4,6},{0x8b8de2,6},{0xf7af52,3},
+{0x9f69f2,7},{0xf46c9a,9},{0xf74fb1,5},{0x21eaef,9},
+{0xea769a,8},{0x0feffc,5},{0xdada45,8},{0xafce7b,6},
+{0x48bef4,6},{0x9a79e5,3},{0xe2fc1c,7},{0xc3be76,9},
+{0xb9c37e,5},{0xc6b183,10},{0xa2acac,3},{0xc395a2,4},
+{0xbc88b4,7},{0xeaf41c,5},{0xea24ea,4},{0xd038f2,5},
+{0x5ae2bc,9},{0xdac15f,8},{0x5fe5b4,6},{0xce5ad3,15},
+{0xf419ec,4},{0xa7bc97,11},{0xea4ac3,6},{0xd02ef9,5},
+{0xb995aa,5},{0xef808b,6},{0xe22bea,34},{0xe24fc6,4},
+{0xef12f9,7},{0x7b9de0,8},{0x85ec85,4},{0xfc7e80,12},
+{0x9a85d8,5},{0xfc00fc,7},{0x9d90ce,5},{0xf4be45,9},
+{0xe5e52e,3},{0xfc0cef,7},{0xd83be5,8},{0x4dc6e5,5},
+{0xacda73,9},{0x4fc8e0,6},{0xbc88b6,4},{0xb4bc8b,6},
+{0xec45c6,8},{0x71d3b6,4},{0xe73dd5,5},{0x21fcda,4},
+{0xe055c6,9},{0x3becd0,8},{0x57e0c1,5},{0x71bec8,9},
+{0x8be28b,4},{0x4dddd0,9},{0xda9a85,6},{0xaa6ce2,4},
+{0x5ad5cb,9},{0x67f49d,12},{0xfc07f7,9},{0x42c8ec,16},
+{0xefc148,4},{0xda6eb1,7},{0x55bee7,4},{0xd33bea,6},
+{0x809fd8,14},{0xd82ef2,4},{0x21ddfc,5},{0x83e097,7},
+{0x8bcba2,9},{0xd864bc,8},{0xa7d87b,6},{0xc3a78d,6},
+{0xb9a79a,8},{0xd59590,3},{0xfc0af2,6},{0xe52be7,7},
+{0xce52d8,5},{0xe22bea,4},{0xe719f7,5},{0x909fcb,3},
+{0xa7da76,6},{0xdd38e5,4},{0xfc55aa,6},{0x19f2ef,6},
+{0x83da9d,5},{0x9fc892,6},{0xe51ef4,9},{0xce3dec,5},
+{0xea3dd0,7},{0x4ad3dd,6},{0x6ea2e7,5},{0xf92bd3,4},
+{0xc34aea,8},{0x2eddef,4},{0xe7799a,8},{0xe5799a,11},
+{0xe224f2,3},{0xf90af7,6},{0x61a2f4,6},{0xb67ec6,13},
+{0xe75cb4,7},{0xfcb945,16},{0xd3b173,7},{0xfc00fc,6},
+{0xd5ef33,5},{0x699df4,9},{0xf707f9,4},{0xdaea33,6},
+{0xe2dd38,3},{0xf2d036,5},{0xf248c1,6},{0xeaf21c,5},
+{0xec40cb,7},{0xf24abc,5},{0xf42bd8,4},{0xa757f9,5},
+{0xd02ef9,6},{0xfc0cf2,7},{0xb16cda,5},{0xea36d8,8},
+{0xd036f4,7},{0xb648fc,3},{0xea4dc1,4},{0x64c8cb,7},
+{0xfc05f7,3},{0xfc00fc,4},{0xf4a75f,6},{0xc85ad5,5},
+{0xd873ac,5},{0xd5a77b,11},{0xe7c84a,4},{0xec19f4,7},
+{0xc864ce,7},{0xcece5c,5},{0xe08b8d,5},{0xd84dd3,3},
+{0x07f9f7,9},{0x5cefac,3},{0xfc02f9,4},{0xd048e0,8},
+{0xef55b6,4},{0xa269ec,7},{0xec12f9,3},{0xd380a4,11},
+{0x5fcbd0,6},{0x90c1a7,5},{0x95dd85,5},{0xceda4f,9},
+{0x4fb9f2,14},{0xb65aea,7},{0xea838d,5},{0xf917ea,4},
+{0xc833fc,9},{0xe252c3,9},{0xef0ffc,7},{0x1cf2ec,9},
+{0x9f6cef,4},{0xf73dc6,12},{0x90b6b4,5},{0xb44ff7,7},
+{0xea2be2,12},{0xd02efc,17},{0xa276e2,4},{0x79c1c1,3},
+{0xc855dd,9},{0xb1d571,8},{0xb645fc,6},{0xd03bef,6},
+{0x42eccb,9},{0xea67aa,5},{0x9079ef,6},{0x9de773,7},
+{0xe55abc,6},{0xf421e5,3},{0x5cf9a4,6},{0xc373c1,5},
+{0x5fe2b6,4},{0xea24ec,6},{0x9af967,4},{0xf7be45,5},
+{0xd333f4,5},{0xc6d061,8},{0xb96cd3,8},{0xda64b9,6},
+{0xe79779,4},{0xac67e7,6},{0xcb67c8,4},{0xa45cf7,3},
+{0xd3859f,3},{0xd857c8,4},{0x42c1f7,8},{0xaf61e7,6},
+{0xea14fc,6},{0xda33ea,10},{0x5af2af,5},{0xbe40f9,3},
+{0x9de773,3},{0xe72be5,4},{0x4ae7c6,5},{0xaa88c8,8},
+{0x36f9cb,3},{0xc1a790,8},{0xf905fc,5},{0xbcaa95,6},
+{0x839dda,3},{0xfc00fc,6},{0xb6d86c,3},{0xdd40da,6},
+{0xb46cda,7},{0x839fd5,4},{0xc167d3,13},{0xf20ff7,9},
+{0xe024f4,9},{0xf41ee5,4},{0x909ad0,10},{0xb1d376,8},
+{0xce2efc,5},{0x29d5f9,7},{0xd0b96e,6},{0xdd69b4,5},
+{0xfc05f7,5},{0xdd26f7,4},{0xea1eef,6},{0xb64df7,9},
+{0x21e5f2,3},{0x4fd8d0,6},{0xd345e0,7},{0xfc00fc,3},
+{0xe569ac,5},{0xecb655,11},{0xfc5aa2,9},{0xb4d571,7},
+{0xaa57f7,6},{0xfc00fc,7},{0xe729ea,10},{0xea24ea,11},
+{0xb14df9,5},{0xf707fc,7},{0x5cd8c3,5},{0xea17f9,9},
+{0xc3d364,6},{0xa77ed3,4},{0x9569f9,8},{0x0ff4f4,4},
+{0xbc9aa4,7},{0xc140f7,10},{0xf705fc,8},{0xb44df9,4},
+{0xa479dd,5},{0xf705fc,5},{0xce8d9d,5},{0xf905f9,5},
+{0xdd2bef,7},{0xea14f9,5},{0xddbc61,5},{0xc13dfc,8},
+{0x79c8b6,10},{0xf79a67,6},{0xb4a79f,12},{0x97a7bc,7},
+{0xb9c17e,4},{0xf430d5,3},{0x2ee0ea,7},{0xf919e7,4},
+{0xeada33,9},{0xa2da7e,7},{0x8bec80,6},{0xf412f4,5},
+{0xa767ec,3},{0xd824fc,7},{0xe224f2,3},{0xfc07f7,4},
+{0xfc00fc,3},{0xe55cb6,8},{0xf9768b,7},{0xe5b45f,3},
+{0xd5978b,4},{0xf917e7,5},{0xf70ff4,7},{0x95e283,6},
+{0xb4b98d,5},{0xc83bf4,8},{0x9f88d3,7},{0xa473e0,9},
+{0x9569fc,10},{0x926cf9,7},{0xef14f4,7},{0xf7679a,4},
+{0xaa80ce,9},{0x45f9b9,5},{0xb4a2a2,10},{0xac88c6,5},
+{0xe75ab6,6},{0xa2fc5a,3},{0xc65cd8,7},{0x64e0b6,5},
+{0xfc6992,7},{0xf717ec,4},{0xf438ce,10},{0xf755ac,4},
+{0xf7aa57,6},{0xb9b688,3},{0xcb4de2,4},{0xf9f70a,9},
+{0x92d392,8},{0xef2be0,7},{0x92a7be,4},{0xa276e2,4},
+{0xc34fe5,5},{0xd5c164,9},{0xf219ec,4},{0xef5aaf,8},
+{0xa7c68d,6},{0xd369bc,7},{0xf912ef,4},{0xb9b48b,6},
+{0x3bc6f9,6},{0xe54ac8,5},{0xf71ce7,9},{0xbc4def,8},
+{0xe271a7,3},{0xcbc16e,5},{0x3dfcc1,3},{0x97bea2,7},
+{0xaaa4aa,13},{0x73ef95,5},{0xf74fb1,4},{0xc148ef,17},
+{0xfc26d8,4},{0xfc02fc,6},{0xb1da6c,6},{0xd5d84d,7},
+{0xf96c95,5},{0xf90fef,3},{0xea7b92,7},{0xbc4def,4},
+{0xf2a264,8},{0x40dadd,12},{0xf214f4,4},{0x38c3fc,3},
+{0xf705fc,7},{0xa29db9,3},{0xaaf25f,4},{0xdd9a83,12},
+{0xc188b1,6},{0xa27bda,9},{0x36dde5,4},{0x85bcb9,7},
+{0xfc00fc,5},{0xe76ca4,4},{0x95b6ac,6},{0xb6d071,7},
+{0xc857d8,3},{0xfc2bd3,4},{0xfc5aa4,4},{0xf417ec,5},
+{0xb471d3,7},{0x92b1b6,4},{0x6c90fc,7},{0x5abce2,7},
+{0xcb55d8,3},{0xce30f9,5},{0xea7b95,3},{0xec17f7,6},
+{0xf70ff4,3},{0xaa73dd,9},{0x9d69f4,3},{0xb457ef,6},
+{0xf236d0,6},{0x64e0b4,7},{0xddcb52,4},{0xd0d355,7},
+{0x5fd5c3,6},{0xf42bd8,12},{0x55e2c1,4},{0xb1b492,4},
+{0xdd4dd0,4},{0xbc88b6,3},{0xea21ec,4},{0xf7649d,8},
+{0xb16cda,3},{0x9fa4b6,8},{0xea17f9,4},{0x1efce0,3},
+{0xbec179,7},{0xbeb18b,6},{0x9da4b6,6},{0x48b4fc,4},
+{0x7bfc83,14},{0xacf25c,10},{0xe0d842,4},{0xf9768b,5},
+{0xf90ff2,7},{0xfc00fc,8},{0xf20ff7,5},{0xec7e90,7},
+{0xbc42f9,3},{0xf712ef,5},{0xf902fc,6},{0xf77e83,7},
+{0xb1b197,5},{0xda6cb4,7},{0xf4a264,7},{0xa78dc6,6},
+{0xf21cea,4},{0xa48dc8,6},{0x6997f9,5},{0x67d3be,5},
+{0x4ad0e0,6},{0xcbbe6e,6},{0xcb76b6,10},{0xd030f9,6},
+{0xa29fb6,6},{0xda24fc,5},{0xf4bc48,3},{0xd0909a,6},
+{0xf252b6,8},{0x6cb4da,4},{0xdd40dd,11},{0x8b71fc,9},
+{0x80c8af,7},{0xb15fe7,4},{0x79ef90,6},{0x71b1d8,7},
+{0x61a4f2,9},{0x42dadd,6},{0xaf4ffc,3},{0x9a79e7,5},
+{0xea17f7,6},{0xc89f92,4},{0xb6d869,12},{0xda2eef,4},
+{0xf72ed5,6},{0xe057c3,6},{0xb6b190,6},{0xda48d5,5},
+{0xe0a476,3},{0xfce01c,6},{0x9dbe9f,13},{0xafce7e,4},
+{0xf905fc,6},{0xfc0cef,5},{0x9facaf,6},{0xb6af95,6},
+{0x52ced8,6},{0xd373b4,3},{0xce5cd0,8},{0xe752c1,5},
+{0xf7ac55,8},{0x7bbec1,6},{0xb4d571,4},{0xf90af7,5},
+{0xc833fc,7},{0xbe4dec,8},{0x92beaa,8},{0xfcb945,7},
+{0xc1d067,3},{0xdd45d8,7},{0xceb971,3},{0xf44ab9,4},
+{0xbeac8d,4},{0xf2af5a,15},{0xe05ac1,11},{0xd38b9d,4},
+{0xec0ffc,4},{0x80ec8d,7},{0xd0af7b,6},{0xef5cac,5},
+{0xdaa776,4},{0xd052d8,5},{0xb461e5,8},{0xef8388,7},
+{0xbea297,8},{0xce83a7,5},{0xda2ef2,3},{0x73b6ce,5},
+{0x83ec8b,9},{0xc840f2,7},{0xfc4faf,8},{0xfc14e7,4},
+{0xe742d0,9},{0x52aff7,4},{0xf214f4,4},{0x26f7da,5},
+{0xf2b94d,6},{0xe02eea,6},{0x76fc85,3},{0x858de7,4},
+{0xbc97a4,6},{0xd8aa79,4},{0x9d73ea,4},{0xdda279,5},
+{0xafc385,7},{0x33dde7,6},{0x64acea,8},{0xacce80,7},
+{0xef26e2,7},{0xb957e7,4},{0xdad548,4},{0xaf57f4,5},
+{0x9f73e5,3},{0xeae02e,7},{0xf70af7,8},{0xfc00fc,4},
+{0xea6ca4,6},{0x8885ec,3},{0xe23dda,4},{0xb461e5,10},
+{0xdd928b,6},{0x76cbb6,7},{0xfc00fc,5},{0xce8d9d,8},
+{0xafb692,12},{0xe79083,7},{0xfc0af2,3},{0xf926d8,8},
+{0xe219fc,5},{0xe7bc55,5},{0xcebc6e,3},{0xda73aa,4},
+{0xfc00fc,4},{0x52dace,5},{0xbcd864,6},{0x67e7ac,4},
+{0x33f9cb,3},{0x8bafc1,11},{0xd871af,6},{0xe78390,10},
+{0xb688bc,8},{0x5addc1,6},{0xf2d036,4},{0xf70ff2,9},
+{0xf4ea19,9},{0xfc02f9,6},{0x839fd8,3},{0xdd4dce,5},
+{0xec3dce,6},{0xcea783,6},{0xcbe745,5},{0xc1b483,7},
+{0x799fe0,5},{0xd340e7,7},{0x29ddf2,3},{0x73daac,6},
+{0xea33da,5},{0xe224f2,3},{0xd34fd5,5},{0xe245d3,8},
+{0xfc1edd,10},{0x3bf2cb,4},{0xdd9f7e,26},{0xe02eec,5},
+{0xf47195,4},{0xa461f4,5},{0x3ddae0,3},{0xaf5fea,6},
+{0xbccb73,6},{0xea26ea,8},{0xd82bf7,6},{0xac6ce0,7},
+{0xe524ef,4},{0xb6da69,6},{0xf224e2,5},{0xb95ce5,7},
+{0x73a4e0,7},{0xa4e076,7},{0xfc02f9,8},{0xec1eec,4},
+{0xf7da29,6},{0xc369ce,6},{0xd5c65c,9},{0xe51ef4,4},
+{0xf2a264,4},{0x809ddd,5},{0xd045e2,7},{0xaac888,6},
+{0xb44af9,3},{0xd84ad8,7},{0xf4ef14,5},{0x3bf7c8,5},
+{0xe730e0,4},{0x97b6ac,7},{0x8bda95,5},{0x88ec83,8},
+{0xece529,8},{0x9a80dd,10},{0xf412f4,7},{0x36efd5,6},
+{0xf95ca4,9},{0xd838e7,6},{0x9a69f7,7},{0xf730d3,4},
+{0xd526fc,5},{0x9f76e2,6},{0xe28395,8},{0xecaa61,6},
+{0x4ff4b4,7},{0xd079af,7},{0xa469ea,10},{0xfc21da,7},
+{0xb471d5,6},{0x7b88f7,4},{0xfc6797,4},{0x9daab4,5},
+{0x29f2dd,7},{0x8388ec,7},{0x4df9b1,4},{0xeaf21e,5},
+{0xf4c340,10},{0xf212f7,3},{0xf407fc,5},{0x4fb9ef,9},
+{0xd3b471,6},{0xef4dbe,12},{0x4af4bc,7},{0xe224f2,6},
+{0x36d0f2,8},{0xf707fc,4},{0xfc8d71,9},{0x95bea4,6},
+{0x90ef7b,4},{0x5adac6,5},{0x97b9a7,4},{0xc3c671,6},
+{0xfc12ec,5},{0xea1ef2,5},{0x8dd09d,4},{0xd82bf7,7},
+{0xb18dbc,7},{0xbc64da,6},{0xf28d7b,4},{0xef19ef,5},
+{0xc1e555,6},{0xfc3bc3,6},{0x9f88d3,5},{0xc3ec48,9},
+{0xc8979a,5},{0x5cc6d8,4},{0xf78080,6},{0xb17bcb,8},
+{0xea12fc,6},{0xc195a2,7},{0xef33d8,3},{0xa783ce,3},
+{0xf40af9,5},{0xa288d0,6},{0xf248be,4},{0x14e7fc,5},
+{0x83d3a4,8},{0xb4cb79,6},{0xbc7ebe,3},{0xfc8873,6},
+{0xe038e0,6},{0xc3ef48,6},{0xa25afc,10},{0xc1a792,6},
+{0x8d90dd,9},{0xc1be7b,5},{0x8bec80,5},{0xa4ef64,5},
+{0x38f7c8,11},{0xaf6eda,4},{0xd073b4,5},{0x79ceb4,7},
+{0xd5e540,6},{0xfc0fec,5},{0xf917ea,7},{0x64c8cb,4},
+{0x69e7a7,6},{0xa77ed3,5},{0xe52be7,6},{0x5aaaf4,7},
+{0xcb85a7,5},{0x8392e2,4},{0x5cb4ea,6},{0xe729e7,6},
+{0xc873bc,5},{0xeabc52,7},{0xf438ce,7},{0xea48c8,5},
+{0xef17f4,4},{0xfc00fc,6},{0xf97b85,14},{0x67ace7,3},
+{0xf452b1,7},{0xe2e038,4},{0xa4a7af,5},{0xddb469,3},
+{0xf21cec,7},{0x7bbec1,3},{0xda38e5,8},{0xc6a292,9},
+{0x6ecebc,9},{0x8dda90,4},{0xf921e0,8},{0xc88da4,5},
+{0x92aabc,4},{0x9a9dc3,5},{0x8dd895,4},{0x64d3c3,9},
+{0xa2f95f,7},{0xdd36e5,8},{0x83e790,11},{0xef26e2,5},
+{0xef55b4,9},{0x5fc3d8,6},{0xfc57a4,7},{0x9785dd,5},
+{0x809ddd,4},{0xc66cc8,8},{0xf236d3,5},{0xd338ef,6},
+{0xd8839f,4},{0xef30d8,3},{0x9a9ac6,5},{0xfc05f9,11},
+{0x958bda,11},{0xf902fc,8},{0xbe42f9,4},{0xbce557,6},
+{0xef4fb9,6},{0xf9af52,5},{0x97d38d,5},{0xf902fc,7},
+{0xf4a261,4},{0xd88897,7},{0x92e77e,7},{0x6c9df2,6},
+{0xce85a4,4},{0x9ae07e,8},{0xb1f255,8},{0xc6929f,6},
+{0xf2af5a,10},{0xe2977e,5},{0xe5cb4a,5},{0xfc14e7,3},
+{0xc14aef,3},{0xaff257,3},{0xe21cf9,11},{0xd05ccb,5},
+{0xf2ec1c,8},{0xb4b98b,9},{0xc183b6,3},{0xb455f2,7},
+{0x83aace,4},{0xe51cf9,4},{0xdd4fce,4},{0xb47ec8,3},
+{0x92da8b,4},{0xecb952,4},{0xf70af7,6},{0x7bbcc1,6},
+{0xf40cf9,5},{0xe2ce48,5},{0xf7b14f,5},{0xdd40da,4},
+{0x4fb9f2,7},{0x6eb1da,5},{0x9d90ce,6},{0xcb55d8,5},
+{0xd02efc,8},{0xd36eb9,6},{0xfc0cef,5},{0xe071aa,9},
+{0xe7ac67,3},{0xf4f40f,6},{0xe267af,4},{0xc3da5c,5},
+{0xb945fc,4},{0xeada33,6},{0x7197f2,7},{0xfc00fc,8},
+{0xd09d8d,5},{0x6e9fea,8},{0xd3a77e,4},{0xaa5ff2,6},
+{0xe269ac,5},{0xf426e0,6},{0xfc00fc,5},{0xec6ca2,10},
+{0xc638fc,7},{0xe54fc6,5},{0x5ac6da,3},{0xce67c6,6},
+{0xbe42f9,5},{0xce38f2,5},{0xbe5ae2,3},{0xd3ec38,8},
+{0x8b76f7,8},{0xb180c8,4},{0xd538ec,4},{0xddef2b,9},
+{0xf9c838,5},{0xf457ac,4},{0xf95aa7,7},{0xaf69e0,9},
+{0x67efa2,5},{0xa280d8,4},{0xa7d57b,7},{0xcbaf7e,4},
+{0x837bf9,4},{0xddc15c,6},{0xe221f4,3},{0xc6b480,5},
+{0xbc90af,10},{0x4dd3da,4},{0xbcb983,4},{0xe77997,7},
+{0xe05fb9,9},{0xc1b980,8},{0xdd55c6,7},{0xaf97b1,9},
+{0xec838b,4},{0xc39d9a,9},{0x6c97f7,5},{0xf7b44f,3},
+{0xb69fa4,7},{0x90ea7e,7},{0xe067b1,5},{0xd5cb5a,4},
+{0xc19a9f,8},{0xac67e7,8},{0xcee745,4},{0xf436d0,4},
+{0x73b1d5,7},{0xd3d84f,5},{0xdd42d8,5},{0xea907e,6},
+{0xc83bf4,7},{0xd84fd3,6},{0xe28890,3},{0x38e2e0,8},
+{0xec67a7,6},{0xcbc16e,8},{0x4ab1fc,7},{0x92d590,3},
+{0xaad876,4},{0xb683c1,4},{0xaa55f9,5},{0xea24ea,6},
+{0xf90fef,6},{0xf738c8,3},{0xcb5ad5,7},{0xec21ec,5},
+{0xce61c8,3},{0xa76ce7,3},{0xf41ee5,7},{0xcebe6c,3},
+{0x57dac6,9},{0xf46e97,3},{0x8be08d,7},{0xc148ef,9},
+{0xf926da,8},{0xe029ef,6},{0xfc00fc,5},{0x3dcbf2,4},
+{0xef0ffc,5},{0x95b6ac,9},{0x7be59a,5},{0xda2ef2,8},
+{0xda30ef,5},{0xfc24d8,3},{0x73d3b4,3},{0xddf229,4},
+{0x8883ef,5},{0x5aefaf,6},{0x9797cb,6},{0xcbc16c,5},
+{0xef19f2,5},{0xd3bc6c,3},{0xda5cc3,8},{0x52f9ac,7},
+{0xa7c888,4},{0x69d8b6,7},{0xbc45f7,7},{0xfc02fc,4},
+{0xb4b195,6},{0xe58d85,4},{0x9776ea,6},{0xf255b1,6},
+{0x76dda7,3},{0xe55ab9,8},{0xe230e7,5},{0x3bd0ec,5},
+{0xe219fc,6},{0xcbac83,5},{0xe548ce,7},{0xdd5cbe,3},
+{0xf412f4,5},{0xf9738d,9},{0xdd40da,4},{0x90c6a2,10},
+{0xe038e2,4},{0x6eddaf,8},{0xf23dcb,12},{0xec4fbc,6},
+{0xc8d35c,6},{0xf238d0,6},{0x90cb9f,6},{0xa46ee5,5},
+{0xaf8bbe,5},{0xc1a495,5},{0xc679b9,4},{0xec29e2,6},
+{0xf9649d,6},{0xf924da,6},{0xe224f2,5},{0x8d71f9,7},
+{0xf2cb3d,5},{0xb6cb79,4},{0xa4dd76,6},{0xe52ee5,3},
+{0xaa8bc3,3},{0x838dea,7},{0xf9ea17,5},{0x88c8aa,6},
+{0x73e0a7,5},{0xd09595,6},{0xf7d82b,10},{0xec7397,4},
+{0xfc02fc,7},{0xf72ed3,5},{0xd03dec,4},{0x80cbaf,5},
+{0xf921dd,7},{0x9fcb90,8},{0xbe95a7,4},{0x69eaa4,8},
+{0xf20ff7,5},{0x69e7a7,9},{0xf924da,3},{0xecdd30,6},
+{0xc1d861,4},{0xb967d8,5},{0xb157ef,6},{0xb9bc85,5},
+{0xb192b4,8},{0xda38e5,3},{0xea1eef,6},{0x12f7ef,4},
+{0x6e90f9,6},{0xd573b1,6},{0xdd24f7,5},{0xd5d055,7},
+{0xc86ec1,4},{0xf21ee7,7},{0xa261f4,6},{0xe555c1,4},
+{0xf78b79,6},{0xf78080,5},{0x80b4c3,7},{0xf414ef,4},
+{0xf70ff2,6},{0xea52bc,6},{0xd04ddd,7},{0xf90af4,5},
+{0xe78b88,4},{0xf29f67,6},{0x79b6c8,4},{0xa48dc8,5},
+{0x5fa4f4,4},{0xea14fc,3},{0xf23bcb,7},{0xf738cb,5},
+{0x4fb1f9,3},{0x5ae2bc,7},{0xfcac4f,5},{0xa283d3,7},
+{0xef24e7,7},{0xe5e52e,4},{0x929ace,4},{0xcb36f9,8},
+{0xea21ef,5},{0xf2d038,6},{0x48d8d8,4},{0xec40ce,6},
+{0xf90af7,5},{0xf921dd,7},{0xd8e042,5},{0x21ddf9,4},
+{0x5fb9e2,8},{0xf72ed3,4},{0xfc2ed0,5},{0xf712ef,6},
+{0x57d8cb,5},{0x19fce2,9},{0xe7f71c,9},{0x7be59a,4},
+{0x6cfc90,5},{0xec3bd0,5},{0xea26e7,8},{0x36d5ec,5},
+{0xac76d8,3},{0xa4a4b1,3},{0xef12f7,4},{0x6eb9d3,6},
+{0xe714fc,5},{0xe224f2,5},{0x3bead5,4},{0x8de783,9},
+{0x40d5e2,8},{0x9764fc,7},{0xf705fc,3},{0x8d88e2,8},
+{0xe269ac,6},{0xda33ec,7},{0xd03bec,3},{0xbe6cd0,6},
+{0x8bf479,5},{0xf912ec,8},{0xe5b461,7},{0xaf5aef,6},
+{0xc34fe5,7},{0xd038ef,6},{0x38f9c8,7},{0xf43dc6,3},
+{0xceb973,5},{0xe526ec,4},{0xf902fc,7},{0xbc48f7,4},
+{0,0}
+};
+
+static double const mosaic_curve_points[] = {
+20.3, 216.5, 27.0, 209.8, 17.4, 200.0, 14.5, 183.5, 14.2, 181.8,
+13.5, 181.6, 13.9, 180.0, 28.3, 126.1, 29.2, 126.3, 44.2, 72.5,
+44.4, 71.7, 44.2, 70.8, 44.2, 70.8, 44.2, 70.8, 44.4, 71.7,
+44.2, 72.5, 36.1, 121.4, 36.0, 121.3, 27.9, 170.2, 24.1, 193.3,
+33.8, 203.1, 20.3, 216.5, 82.6, 583.9, 82.5, 583.6, 84.3, 583.3,
+84.2, 583.1, 88.1, 600.0, 87.2, 600.2, 90.2, 617.3, 92.4, 630.1,
+92.4, 630.1, 94.7, 642.9, 96.8, 655.3, 96.8, 655.3, 99.0, 667.8,
+100.0, 673.7, 100.1, 673.7, 101.1, 679.7, 107.4, 716.3, 107.3, 716.3,
+113.7, 752.9, 115.3, 762.1, 117.1, 771.4, 117.1, 771.4, 117.1, 771.4,
+115.3, 762.1, 113.7, 752.9, 108.9, 726.8, 108.9, 726.8, 104.1, 700.7,
+97.1, 662.7, 97.1, 662.7, 90.1, 624.8, 86.3, 604.3, 85.5, 604.5,
+82.6, 583.9, 37.6, 265.6, 38.8, 286.4, 41.0, 287.6, 36.1, 307.5,
+38.5, 298.0, 34.3, 297.0, 32.5, 286.5, 26.4, 251.5, 21.6, 251.7,
+20.3, 216.5, 33.8, 203.1, 24.1, 193.3, 27.9, 170.2, 43.4, 214.1,
+34.7, 217.8, 37.6, 265.6, 27.9, 170.2, 36.0, 121.3, 36.1, 121.4,
+44.2, 72.5, 43.5, 76.1, 44.0, 76.2, 43.9, 79.8, 43.7, 85.4,
+43.7, 85.4, 43.5, 91.0, 41.9, 137.6, 41.9, 137.6, 40.3, 184.2,
+39.2, 217.8, 39.2, 217.8, 38.1, 251.5, 37.8, 258.5, 36.7, 258.7,
+37.6, 265.6, 34.7, 217.8, 43.4, 214.1, 27.9, 170.2, -16.2, 299.0,
+-16.2, 299.0, -15.8, 297.4, -15.4, 295.8, -10.3, 274.5, -10.2, 274.5,
+-5.1, 253.1, -1.7, 238.6, -1.9, 238.6, 1.6, 224.2, 2.5, 220.5,
+2.8, 220.6, 3.6, 216.9, -0.6, 235.0, -0.7, 235.0, -5.1, 253.1,
+-10.2, 274.5, -10.3, 274.5, -15.4, 295.8, -15.8, 297.4, -16.2, 299.0,
+-16.2, 299.0, 242.4, -11.8, 258.9, -17.0, 261.0, -11.0, 277.2, -16.9,
+183.6, 17.7, 182.4, 14.3, 87.6, 45.5, 87.3, 45.5, 87.3, 45.5,
+87.1, 45.6, 71.7, 49.5, 71.2, 48.0, 56.4, 53.5, 148.8, 19.4,
+148.5, 18.2, 242.4, -11.8, 56.4, 53.5, 52.5, 54.8, 51.1, 57.0,
+48.5, 55.6, 47.9, 55.2, 48.4, 50.6, 50.0, 50.0, 134.7, 18.5,
+135.0, 19.0, 221.1, -8.6, 231.2, -11.9, 232.5, -8.1, 242.4, -11.8,
+148.5, 18.2, 148.8, 19.4, 56.4, 53.5, 194.7, 17.8, 158.4, 27.2,
+158.4, 27.2, 122.0, 36.6, 117.5, 37.7, 117.5, 37.7, 112.9, 38.9,
+100.2, 42.2, 100.2, 42.2, 87.6, 45.5, 87.3, 45.5, 87.3, 45.5,
+87.1, 45.6, 182.1, 14.3, 181.1, 10.8, 277.2, -16.9, 303.4, -24.5,
+307.1, -14.6, 331.7, -25.0, 266.4, 2.6, 263.7, -3.7, 195.7, 17.5,
+195.2, 17.7, 195.2, 17.7, 194.7, 17.8, 331.7, -25.0, 364.2, -33.4,
+365.0, -31.6, 398.6, -34.9, 387.8, -33.8, 388.0, -32.1, 377.4, -29.4,
+370.0, -27.5, 370.0, -27.5, 362.6, -25.6, 362.3, -25.5, 362.3, -25.5,
+362.0, -25.4, 355.9, -23.9, 355.9, -23.9, 349.8, -22.3, 341.8, -20.2,
+341.8, -20.2, 333.7, -18.1, 318.3, -14.2, 318.3, -14.2, 303.0, -10.2,
+300.6, -9.6, 300.6, -9.6, 298.2, -9.0, 281.5, -4.7, 281.5, -4.7,
+264.9, -0.3, 254.1, 2.4, 254.1, 2.4, 243.2, 5.2, 227.9, 9.2,
+227.9, 9.2, 212.6, 13.1, 204.2, 15.3, 204.2, 15.3, 195.7, 17.5,
+195.2, 17.7, 195.2, 17.7, 194.7, 17.8, 263.2, -3.6, 262.3, -7.1,
+331.7, -25.0, -5.1, 253.1, -0.7, 235.0, -0.6, 235.0, 3.6, 216.9,
+7.2, 203.0, 5.7, 202.2, 11.4, 189.1, 6.4, 200.6, 8.2, 201.4,
+5.0, 213.7, -0.0, 233.4, 2.9, 234.7, -5.1, 253.1, 0.7, 536.2,
+13.6, 573.8, 15.5, 574.7, 18.7, 614.1, 18.8, 616.3, 7.9, 620.2,
+7.3, 619.4, -4.6, 602.1, -1.2, 599.1, -6.2, 577.9, -16.0, 536.4,
+-28.8, 533.6, -22.2, 494.1, -25.4, 512.7, -6.9, 513.8, 0.7, 536.2,
+-22.2, 494.1, -26.0, 485.6, -28.6, 477.0, -25.8, 475.4, -22.7, 473.6,
+-14.7, 479.5, -10.5, 487.2, -3.6, 500.3, -7.1, 502.1, -3.7, 517.0,
+-2.5, 522.1, -2.5, 522.1, -1.4, 527.1, -0.3, 531.7, -1.6, 532.5,
+0.7, 536.2, -6.9, 513.8, -25.4, 512.7, -22.2, 494.1, 19.0, 510.1,
+21.7, 515.4, 24.2, 514.1, 29.4, 518.2, 30.9, 519.3, 30.9, 519.3,
+32.3, 520.4, 56.0, 538.7, 58.2, 536.4, 79.7, 557.1, 82.9, 560.1,
+80.6, 562.5, 81.6, 567.8, 82.9, 575.5, 83.7, 575.4, 84.2, 583.1,
+84.3, 583.3, 82.5, 583.6, 82.6, 583.9, 75.0, 587.4, 75.2, 587.9,
+67.4, 591.0, 67.4, 591.1, 67.2, 590.6, 66.9, 590.2, 42.9, 550.1,
+40.5, 551.4, 19.0, 510.1, 121.8, 800.9, 121.7, 801.1, 122.3, 801.2,
+122.4, 801.6, 123.7, 808.3, 126.5, 809.3, 124.7, 815.1, 125.1, 813.8,
+120.1, 813.3, 119.5, 810.6, 118.6, 806.2, 120.3, 805.6, 121.8, 800.9,
+0.7, 495.9, 3.7, 500.5, 5.2, 499.5, 9.8, 503.0, 14.4, 506.5,
+15.9, 505.5, 19.0, 510.1, 40.5, 551.4, 42.9, 550.1, 66.9, 590.2,
+63.6, 584.5, 63.1, 584.8, 59.4, 579.4, 30.0, 537.7, 28.5, 538.7,
+0.7, 495.9, 113.7, 752.9, 115.3, 762.1, 117.1, 771.4, 117.1, 771.4,
+117.4, 773.4, 117.4, 773.4, 117.8, 775.3, 120.1, 788.5, 120.3, 788.4,
+122.4, 801.6, 122.3, 801.2, 121.7, 801.1, 121.8, 800.9, 121.4, 800.4,
+121.4, 800.4, 121.0, 799.9, 120.6, 799.4, 120.6, 799.4, 120.3, 798.9,
+95.0, 767.1, 74.8, 770.0, 69.8, 735.3, 71.5, 747.0, 95.6, 739.1,
+113.7, 752.9, 69.8, 735.3, 69.0, 734.9, 68.4, 733.7, 68.5, 733.6,
+75.3, 725.8, 76.1, 726.5, 83.8, 719.4, 89.1, 714.5, 89.1, 714.5,
+94.4, 709.6, 99.2, 705.1, 104.9, 702.6, 104.1, 700.7, 108.9, 726.8,
+108.9, 726.8, 113.7, 752.9, 95.6, 739.1, 71.5, 747.0, 69.8, 735.3,
+19.0, 613.9, 22.4, 604.2, 32.4, 607.6, 45.7, 601.3, 56.0, 596.4,
+56.0, 596.4, 66.3, 591.6, 66.9, 591.3, 66.9, 591.3, 67.4, 591.0,
+75.2, 587.9, 75.0, 587.4, 82.6, 583.9, 85.5, 604.5, 86.3, 604.3,
+90.1, 624.8, 90.1, 624.5, 88.9, 624.6, 87.8, 624.5, 64.0, 620.8,
+64.0, 620.8, 40.2, 617.2, 29.6, 615.5, 19.7, 612.1, 19.0, 613.9,
+121.0, 799.9, 121.4, 800.4, 121.4, 800.4, 121.8, 800.9, 120.3, 805.6,
+118.6, 806.2, 119.5, 810.6, 108.1, 806.7, 98.9, 800.6, 99.9, 793.4,
+99.6, 795.2, 110.5, 796.3, 121.0, 799.9, 191.6, 801.6, 213.6, 817.8,
+197.0, 840.4, 202.3, 879.2, 202.4, 880.2, 203.0, 880.7, 202.6, 881.2,
+202.1, 881.7, 201.1, 881.7, 200.4, 881.1, 187.5, 870.7, 176.8, 871.5,
+175.5, 859.3, 172.4, 831.7, 200.2, 807.9, 191.6, 801.6, 32.3, 520.4,
+30.9, 519.3, 30.9, 519.3, 29.4, 518.2, 27.6, 505.3, 28.9, 505.1,
+28.3, 492.1, 26.0, 440.3, 25.5, 440.3, 23.7, 388.5, 24.2, 405.2,
+24.7, 405.2, 25.8, 421.8, 27.0, 439.3, 27.0, 439.3, 28.1, 456.8,
+29.1, 470.9, 29.1, 470.9, 30.0, 485.1, 31.1, 502.7, 32.6, 502.7,
+32.3, 520.4, -3.7, 517.0, -7.1, 502.1, -3.6, 500.3, -10.5, 487.2,
+-10.8, 490.3, -6.1, 490.7, -1.6, 494.1, -0.5, 495.0, -0.1, 494.8,
+0.7, 495.9, 28.5, 538.7, 30.0, 537.7, 59.4, 579.4, 64.3, 555.8,
+22.9, 552.0, -3.7, 517.0, 83.8, 719.4, 76.1, 726.5, 75.3, 725.8,
+68.5, 733.6, 49.9, 724.3, 45.1, 717.9, 40.0, 697.6, 42.0, 705.5,
+51.1, 703.2, 62.2, 708.7, 69.0, 712.1, 69.0, 712.1, 75.8, 715.5,
+79.8, 717.4, 83.9, 719.1, 83.8, 719.4, 69.7, 767.2, 66.2, 756.5,
+57.4, 760.4, 50.0, 750.0, 44.4, 742.1, 50.3, 736.4, 43.7, 730.7,
+60.1, 745.0, 63.1, 746.8, 69.7, 767.2, 943.6, 608.8, 941.3, 609.3,
+947.5, 623.9, 942.6, 627.8, 892.0, 668.3, 889.0, 665.2, 832.4, 697.7,
+848.6, 688.4, 847.2, 685.9, 861.9, 674.1, 867.3, 669.8, 867.3, 669.8,
+872.6, 665.6, 875.3, 663.4, 875.3, 663.4, 878.0, 661.2, 891.5, 650.5,
+891.5, 650.5, 904.9, 639.8, 906.2, 638.7, 906.2, 638.7, 907.5, 637.7,
+909.8, 635.8, 909.8, 635.8, 912.2, 633.9, 927.9, 621.4, 926.1, 612.3,
+943.6, 608.8, 743.8, 661.6, 749.6, 670.9, 741.5, 681.2, 747.9, 684.8,
+733.2, 676.6, 737.6, 668.6, 727.2, 652.4, 722.1, 644.3, 722.1, 644.3,
+716.9, 636.3, 711.2, 627.4, 711.2, 627.4, 705.5, 618.5, 701.1, 611.6,
+695.6, 610.5, 696.7, 604.7, 695.8, 609.3, 701.3, 610.3, 706.0, 615.9,
+724.9, 638.7, 728.6, 636.5, 743.8, 661.6, 947.6, 328.1, 940.6, 361.9,
+953.7, 364.0, 954.4, 400.0, 955.1, 438.8, 952.6, 438.9, 950.4, 477.7,
+950.5, 475.0, 950.3, 475.0, 950.3, 472.2, 949.8, 448.4, 949.8, 448.4,
+949.4, 424.6, 948.5, 376.3, 938.1, 374.2, 947.6, 328.1, 846.3, 117.0,
+890.0, 147.0, 890.5, 146.4, 933.3, 177.7, 934.1, 178.3, 932.8, 180.3,
+933.6, 180.8, 919.3, 171.9, 920.0, 170.9, 906.3, 160.9, 890.8, 149.6,
+890.8, 149.6, 875.3, 138.2, 872.0, 135.8, 872.0, 135.8, 868.7, 133.4,
+857.5, 125.2, 857.7, 124.9, 846.3, 117.0, 408.1, 154.0, 408.9, 154.9,
+409.6, 155.6, 409.4, 156.0, 393.6, 178.5, 392.8, 177.9, 376.1, 199.9,
+371.9, 205.6, 372.2, 205.9, 367.6, 211.2, 370.8, 207.5, 370.5, 207.2,
+373.3, 203.2, 390.6, 178.7, 390.6, 178.7, 407.9, 154.3, 408.0, 154.2,
+408.2, 154.1, 408.1, 154.0, 142.2, 652.8, 141.2, 609.9, 142.1, 609.9,
+142.0, 567.0, 142.0, 543.5, 142.0, 543.5, 141.9, 520.0, 141.9, 517.7,
+141.9, 517.7, 141.9, 515.4, 141.9, 494.5, 141.9, 494.5, 141.9, 473.6,
+141.9, 472.3, 141.9, 472.3, 141.9, 471.0, 141.8, 468.7, 141.8, 468.7,
+141.8, 466.3, 141.8, 452.9, 141.5, 452.9, 141.8, 439.5, 141.8, 441.3,
+142.0, 441.3, 142.3, 443.2, 144.2, 457.2, 144.2, 457.2, 146.2, 471.3,
+146.6, 474.8, 146.6, 474.8, 147.1, 478.3, 147.4, 480.0, 147.4, 480.0,
+147.6, 481.7, 148.8, 490.4, 148.8, 490.4, 150.0, 499.1, 150.2, 500.3,
+150.2, 500.3, 150.3, 501.5, 153.2, 522.1, 153.2, 522.1, 156.0, 542.8,
+156.8, 548.2, 156.8, 548.2, 157.5, 553.7, 157.8, 556.1, 158.5, 556.2,
+158.2, 558.4, 155.4, 578.7, 154.7, 578.6, 151.3, 598.8, 147.6, 620.7,
+147.6, 620.7, 143.9, 642.6, 143.9, 642.7, 143.9, 642.7, 143.9, 642.7,
+143.1, 647.7, 142.1, 647.8, 142.2, 652.8, -1.6, 494.1, -6.1, 490.7,
+-10.8, 490.3, -10.5, 487.2, -14.7, 479.5, -22.7, 473.6, -25.8, 475.4,
+-37.3, 443.0, -39.2, 441.2, -38.9, 407.2, -39.0, 422.7, -32.2, 422.8,
+-25.5, 438.4, -13.6, 466.3, -10.5, 465.3, -1.6, 494.1, 15.1, 196.4,
+24.9, 282.2, 18.9, 282.9, 22.8, 369.5, 22.8, 369.5, 22.8, 369.6,
+22.8, 369.6, 19.8, 367.5, 17.9, 368.2, 16.8, 365.3, 14.8, 360.0,
+16.8, 359.2, 16.7, 353.2, 16.6, 347.1, 16.6, 347.1, 16.6, 341.0,
+16.4, 326.7, 16.4, 326.7, 16.3, 312.3, 15.7, 254.3, 21.6, 253.7,
+15.1, 196.4, -1.4, 527.1, -2.5, 522.1, -2.5, 522.1, -3.7, 517.0,
+22.9, 552.0, 64.3, 555.8, 59.4, 579.4, 63.1, 584.8, 63.6, 584.5,
+66.9, 590.2, 67.2, 590.6, 67.4, 591.1, 67.4, 591.0, 66.9, 591.3,
+66.9, 591.3, 66.3, 591.6, 44.0, 571.1, 44.3, 570.7, 22.4, 549.7,
+10.5, 538.4, 8.8, 539.8, -1.4, 527.1, 87.8, 624.5, 88.9, 624.6,
+90.1, 624.5, 90.1, 624.8, 97.1, 662.7, 97.1, 662.7, 104.1, 700.7,
+104.9, 702.6, 99.2, 705.1, 94.4, 709.6, 91.2, 708.1, 93.8, 702.6,
+93.3, 695.5, 92.9, 690.2, 92.9, 690.2, 92.5, 684.9, 91.2, 668.6,
+91.2, 668.6, 89.9, 652.3, 88.9, 638.4, 87.7, 638.4, 87.8, 624.5,
+120.3, 798.9, 120.6, 799.4, 120.6, 799.4, 121.0, 799.9, 110.5, 796.3,
+99.6, 795.2, 99.9, 793.4, 83.2, 782.8, 82.9, 782.0, 69.7, 767.2,
+63.1, 746.8, 60.1, 745.0, 43.7, 730.7, 42.4, 728.8, 43.7, 727.6,
+42.2, 726.3, 82.0, 761.7, 81.3, 762.5, 120.3, 798.9, 42.2, 726.3,
+28.8, 709.3, 29.6, 704.7, 27.9, 682.3, 28.5, 690.4, 33.9, 690.0,
+40.0, 697.6, 45.1, 717.9, 49.9, 724.3, 68.5, 733.6, 68.4, 733.7,
+69.0, 734.9, 69.8, 735.3, 74.8, 770.0, 95.0, 767.1, 120.3, 798.9,
+81.3, 762.5, 82.0, 761.7, 42.2, 726.3, 703.4, 562.7, 701.7, 552.1,
+711.8, 549.5, 722.0, 537.9, 721.3, 538.6, 722.2, 539.4, 722.5, 540.9,
+723.8, 548.1, 723.8, 548.1, 725.0, 555.3, 730.5, 586.1, 730.5, 586.1,
+735.9, 616.8, 737.4, 625.4, 738.6, 625.3, 739.0, 634.0, 738.8, 631.5,
+737.7, 631.5, 736.5, 629.0, 719.9, 595.9, 708.9, 597.6, 703.4, 562.7,
+539.8, 359.6, 539.7, 358.7, 539.2, 358.7, 538.6, 357.9, 519.1, 327.4,
+519.1, 327.4, 499.6, 296.9, 492.6, 286.0, 492.4, 286.2, 485.7, 275.1,
+486.0, 275.6, 486.2, 275.5, 486.8, 275.9, 488.5, 277.1, 488.5, 277.1,
+490.3, 278.3, 512.4, 293.3, 515.9, 289.9, 534.6, 308.4, 538.8, 312.6,
+535.4, 316.0, 536.1, 323.7, 536.4, 326.7, 536.4, 326.7, 536.7, 329.8,
+538.3, 344.7, 538.8, 344.7, 539.8, 359.6, 672.2, 402.1, 674.0, 400.5,
+680.5, 407.7, 688.8, 413.4, 692.6, 416.0, 692.6, 416.0, 696.4, 418.5,
+698.9, 420.3, 700.7, 419.7, 701.5, 422.0, 705.6, 434.7, 703.8, 435.3,
+706.2, 448.6, 706.9, 452.6, 706.9, 452.6, 707.6, 456.6, 710.3, 471.8,
+713.6, 472.0, 713.0, 487.0, 713.1, 483.1, 709.8, 483.0, 706.7, 478.9,
+704.9, 476.6, 704.9, 476.6, 703.1, 474.3, 701.9, 472.8, 701.9, 472.8,
+700.8, 471.3, 696.3, 465.5, 696.3, 465.5, 691.8, 459.8, 689.2, 456.4,
+689.2, 456.4, 686.6, 453.1, 678.3, 442.4, 678.3, 442.4, 669.9, 431.6,
+669.6, 431.2, 669.3, 431.2, 669.3, 430.8, 670.4, 416.4, 664.2, 409.2,
+672.2, 402.1, 669.3, 430.8, 662.6, 432.2, 659.8, 418.7, 650.4, 406.5,
+639.5, 392.5, 639.5, 392.5, 628.6, 378.5, 623.6, 372.1, 625.2, 368.3,
+618.6, 365.6, 636.3, 372.9, 634.8, 376.6, 650.9, 387.6, 657.0, 391.8,
+657.0, 391.8, 663.1, 395.9, 666.5, 398.2, 666.5, 398.2, 669.9, 400.6,
+670.3, 400.8, 670.3, 400.8, 670.6, 401.0, 671.4, 401.6, 672.2, 401.6,
+672.2, 402.1, 664.2, 409.2, 670.4, 416.4, 669.3, 430.8, 150.3, 501.5,
+150.2, 500.3, 150.2, 500.3, 150.0, 499.1, 164.5, 478.5, 165.2, 479.0,
+180.4, 458.9, 203.0, 428.9, 200.5, 426.6, 225.7, 398.9, 203.9, 422.9,
+206.4, 425.2, 187.1, 451.5, 168.7, 476.5, 169.4, 477.1, 150.3, 501.5,
+198.1, 70.3, 213.7, 72.4, 227.1, 73.6, 228.3, 81.5, 229.1, 86.3,
+215.3, 88.6, 202.2, 95.7, 194.5, 100.0, 194.5, 100.0, 186.7, 104.2,
+177.5, 109.3, 170.2, 107.4, 168.4, 114.3, 173.7, 93.6, 181.1, 95.5,
+193.8, 76.7, 195.9, 73.5, 196.4, 70.1, 198.1, 70.3, 140.7, 133.4,
+137.8, 135.0, 133.7, 132.2, 133.2, 133.5, 134.7, 129.7, 137.9, 130.9,
+142.6, 128.4, 147.4, 125.8, 147.4, 125.8, 152.1, 123.1, 160.3, 118.7,
+160.3, 118.7, 168.4, 114.3, 170.2, 107.4, 177.5, 109.3, 186.7, 104.2,
+164.3, 119.7, 164.5, 120.3, 140.7, 133.4, 134.9, 228.4, 136.6, 240.3,
+129.2, 247.8, 116.4, 255.3, 118.9, 253.8, 111.2, 244.9, 114.4, 240.4,
+120.5, 231.5, 135.5, 232.9, 134.9, 228.4, 151.3, 598.8, 154.7, 578.6,
+155.4, 578.7, 158.2, 558.4, 157.7, 560.5, 158.5, 560.6, 158.8, 562.8,
+161.6, 583.3, 161.6, 583.3, 164.4, 603.8, 165.1, 609.2, 165.1, 609.2,
+165.9, 614.5, 169.9, 643.5, 169.9, 643.5, 173.9, 672.6, 174.5, 677.0,
+173.8, 677.2, 175.1, 681.5, 162.5, 640.3, 157.1, 641.0, 151.3, 598.8,
+141.4, 568.5, 141.4, 567.7, 142.1, 567.7, 142.0, 567.0, 142.1, 609.9,
+141.2, 609.9, 142.2, 652.8, 142.4, 726.7, 142.4, 726.7, 142.5, 800.7,
+142.5, 811.8, 142.5, 811.8, 142.6, 822.9, 142.6, 826.8, 142.7, 830.7,
+142.6, 830.7, 138.4, 830.8, 134.1, 827.1, 133.9, 823.2, 131.5, 765.1,
+135.6, 765.0, 137.3, 706.8, 137.6, 699.6, 137.6, 699.6, 137.8, 692.5,
+139.6, 630.5, 139.3, 630.5, 141.4, 568.5, 94.7, 642.9, 92.4, 630.1,
+92.4, 630.1, 90.2, 617.3, 90.6, 608.0, 93.4, 608.1, 96.7, 598.9,
+98.3, 594.2, 99.8, 594.3, 100.0, 589.5, 98.9, 614.8, 97.5, 614.8,
+95.0, 640.0, 94.8, 641.5, 94.4, 641.6, 94.7, 642.9, 118.9, 272.9,
+120.5, 292.1, 121.5, 292.0, 124.1, 311.0, 126.7, 329.9, 126.7, 329.9,
+129.3, 348.8, 135.5, 394.1, 136.9, 394.0, 141.8, 439.5, 141.5, 452.9,
+141.8, 452.9, 141.8, 466.3, 142.0, 464.7, 139.8, 464.7, 139.2, 462.7,
+138.1, 459.0, 138.8, 458.8, 138.4, 454.9, 135.2, 425.6, 135.2, 425.6,
+132.1, 396.2, 130.6, 382.7, 130.6, 382.7, 129.2, 369.3, 124.0, 321.1,
+123.1, 321.2, 118.9, 272.9, 44.2, 353.6, 40.0, 349.1, 42.9, 346.4,
+41.7, 339.3, 40.6, 332.9, 43.2, 329.6, 39.5, 326.5, 51.9, 336.6,
+49.3, 339.9, 59.1, 353.3, 60.0, 354.6, 60.0, 354.6, 60.9, 355.8,
+66.5, 363.4, 66.5, 363.4, 72.1, 371.0, 73.1, 372.4, 73.1, 372.4,
+74.1, 373.8, 74.2, 373.9, 74.1, 374.0, 74.3, 374.1, 59.2, 363.9,
+56.3, 366.5, 44.2, 353.6, -15.4, 295.8, -10.3, 274.5, -10.2, 274.5,
+-5.1, 253.1, 2.9, 234.7, -0.0, 233.4, 5.0, 213.7, 2.7, 222.5,
+2.8, 222.5, 0.6, 231.3, -3.2, 247.1, -3.2, 247.2, -7.1, 263.0,
+-11.2, 279.4, -12.0, 279.2, -15.4, 295.8, 22.8, 369.5, 18.9, 282.9,
+24.9, 282.2, 15.1, 196.4, 14.8, 189.9, 16.1, 189.6, 14.5, 183.5,
+17.4, 200.0, 27.0, 209.8, 20.3, 216.5, 21.6, 251.7, 26.4, 251.5,
+32.5, 286.5, 33.4, 293.8, 31.6, 294.0, 30.7, 301.5, 30.7, 301.8,
+30.7, 301.8, 30.7, 302.0, 29.9, 308.7, 29.9, 308.7, 29.1, 315.3,
+26.0, 342.4, 19.7, 343.1, 22.8, 369.5, 78.4, 549.9, 78.8, 550.4,
+78.6, 550.6, 78.7, 551.4, 79.2, 554.2, 78.0, 556.0, 79.7, 557.1,
+58.2, 536.4, 56.0, 538.7, 32.3, 520.4, 32.6, 502.7, 31.1, 502.7,
+30.0, 485.1, 30.0, 486.7, 31.2, 486.7, 32.4, 488.3, 55.4, 519.1,
+55.7, 518.9, 78.4, 549.9, 45.7, 601.3, 32.4, 607.6, 22.4, 604.2,
+19.0, 613.9, 18.8, 614.0, 18.6, 614.0, 18.7, 614.1, 15.5, 574.7,
+13.6, 573.8, 0.7, 536.2, -1.6, 532.5, -0.3, 531.7, -1.4, 527.1,
+8.8, 539.8, 10.5, 538.4, 22.4, 549.7, 37.4, 573.4, 47.0, 576.2,
+45.7, 601.3, 22.4, 549.7, 44.3, 570.7, 44.0, 571.1, 66.3, 591.6,
+56.0, 596.4, 56.0, 596.4, 45.7, 601.3, 47.0, 576.2, 37.4, 573.4,
+22.4, 549.7, 62.2, 708.7, 51.1, 703.2, 42.0, 705.5, 40.0, 697.6,
+33.9, 690.0, 28.5, 690.4, 27.9, 682.3, 21.2, 668.6, 25.2, 654.1,
+18.3, 653.0, 32.9, 655.3, 30.8, 668.9, 43.3, 684.7, 49.6, 692.7,
+49.6, 692.7, 55.9, 700.7, 56.1, 701.0, 56.1, 701.0, 56.3, 701.2,
+59.3, 705.0, 58.6, 707.9, 62.2, 708.7, 773.1, 619.7, 757.0, 625.1,
+754.8, 618.3, 736.4, 616.8, 736.2, 616.8, 735.9, 616.9, 735.9, 616.8,
+730.5, 586.1, 730.5, 586.1, 725.0, 555.3, 725.3, 557.0, 726.8, 556.9,
+728.5, 558.4, 734.7, 564.0, 734.7, 564.0, 741.0, 569.6, 750.7, 578.2,
+750.7, 578.2, 760.4, 586.8, 768.9, 594.4, 768.9, 594.4, 777.5, 602.0,
+778.0, 602.4, 778.5, 602.6, 778.4, 602.8, 776.3, 611.4, 778.0, 618.1,
+773.1, 619.7, 778.4, 602.8, 780.2, 601.8, 783.2, 607.1, 787.9, 611.3,
+791.7, 614.6, 792.8, 613.9, 795.5, 618.0, 796.2, 619.0, 795.2, 621.3,
+794.7, 621.4, 784.0, 622.2, 778.2, 625.5, 773.1, 619.7, 778.0, 618.1,
+776.3, 611.4, 778.4, 602.8, 767.2, 714.9, 766.5, 712.4, 765.7, 712.6,
+764.3, 710.3, 759.7, 703.1, 759.7, 703.1, 755.2, 696.0, 751.5, 690.4,
+750.0, 690.9, 747.9, 684.8, 741.5, 681.2, 749.6, 670.9, 743.8, 661.6,
+741.7, 649.6, 741.7, 649.6, 739.6, 637.7, 739.3, 635.8, 739.3, 635.8,
+739.0, 634.0, 738.6, 625.3, 737.4, 625.4, 735.9, 616.8, 735.9, 616.9,
+736.2, 616.8, 736.4, 616.8, 741.7, 632.9, 741.5, 633.0, 746.6, 649.2,
+756.9, 682.0, 757.7, 681.8, 767.2, 714.9, 787.2, 480.4, 785.4, 487.8,
+809.3, 488.9, 823.0, 504.7, 825.6, 507.8, 817.7, 516.8, 819.7, 518.2,
+799.8, 504.6, 783.7, 494.5, 787.2, 480.4, 760.1, 584.4, 760.7, 585.0,
+759.7, 586.2, 760.4, 586.8, 750.7, 578.2, 750.7, 578.2, 741.0, 569.6,
+744.4, 572.8, 744.8, 572.5, 748.5, 575.4, 754.3, 579.9, 754.8, 579.3,
+760.1, 584.4, 781.1, 677.4, 781.0, 677.3, 781.2, 676.2, 781.4, 676.2,
+796.6, 675.2, 796.6, 675.8, 811.9, 675.4, 814.5, 675.3, 814.5, 675.3,
+817.1, 675.3, 839.5, 674.7, 855.1, 664.1, 861.9, 674.1, 847.2, 685.9,
+848.6, 688.4, 832.4, 697.7, 829.2, 700.2, 827.7, 703.0, 825.4, 702.1,
+802.0, 692.9, 803.0, 690.2, 781.1, 677.4, 825.4, 702.1, 824.6, 718.3,
+804.0, 723.6, 778.2, 732.1, 776.6, 732.6, 771.7, 726.6, 770.7, 720.3,
+769.6, 713.9, 772.4, 713.4, 774.0, 706.6, 777.6, 692.0, 776.3, 677.8,
+781.1, 677.4, 803.0, 690.2, 802.0, 692.9, 825.4, 702.1, 736.5, 629.0,
+737.7, 631.5, 738.8, 631.5, 739.0, 634.0, 739.3, 635.8, 739.3, 635.8,
+739.6, 637.7, 737.6, 633.7, 738.1, 633.5, 736.6, 629.3, 736.5, 629.2,
+736.6, 629.2, 736.5, 629.0, 939.3, 263.2, 939.2, 262.7, 941.3, 262.5,
+941.3, 262.2, 945.5, 295.0, 945.9, 295.1, 947.6, 328.1, 938.1, 374.2,
+948.5, 376.3, 949.4, 424.6, 949.3, 413.7, 948.7, 413.7, 948.0, 402.8,
+943.6, 333.0, 942.6, 333.0, 939.3, 263.2, 933.8, 398.2, 945.1, 423.2,
+939.9, 425.5, 945.9, 452.8, 948.1, 462.5, 948.5, 462.5, 950.3, 472.2,
+950.3, 475.0, 950.5, 475.0, 950.4, 477.7, 949.9, 489.9, 944.7, 492.2,
+949.1, 502.2, 932.5, 464.7, 937.5, 462.4, 926.0, 422.7, 923.8, 415.2,
+923.8, 415.2, 921.6, 407.7, 920.4, 403.4, 920.4, 403.4, 919.1, 399.2,
+916.7, 390.9, 916.1, 382.5, 914.3, 382.6, 923.5, 382.0, 929.2, 388.1,
+933.8, 398.2, 924.1, 500.9, 935.4, 520.1, 931.7, 522.3, 939.4, 543.8,
+942.7, 553.0, 944.8, 552.8, 946.0, 562.3, 947.5, 574.7, 945.5, 574.9,
+944.7, 587.6, 944.7, 587.5, 944.5, 587.5, 944.3, 587.3, 934.6, 580.7,
+934.6, 580.7, 924.9, 574.1, 919.9, 570.7, 915.1, 570.9, 915.0, 567.3,
+913.8, 541.2, 918.6, 541.0, 922.2, 514.6, 923.1, 507.7, 926.8, 505.5,
+924.1, 500.9, 926.0, 422.7, 937.5, 462.4, 932.5, 464.7, 949.1, 502.2,
+953.3, 531.4, 952.7, 533.4, 946.0, 562.3, 944.8, 552.8, 942.7, 553.0,
+939.4, 543.8, 934.7, 516.5, 936.3, 516.2, 933.3, 488.7, 932.5, 482.0,
+932.5, 482.0, 931.8, 475.4, 928.9, 449.0, 938.8, 442.6, 926.0, 422.7,
+669.9, 400.6, 666.5, 398.2, 666.5, 398.2, 663.1, 395.9, 663.0, 395.4,
+665.6, 394.7, 668.1, 393.5, 684.8, 385.5, 684.8, 385.5, 701.4, 377.5,
+711.7, 372.6, 711.7, 372.6, 722.0, 367.6, 725.4, 366.0, 725.4, 366.0,
+728.7, 364.4, 730.3, 363.7, 730.3, 363.7, 731.8, 362.9, 732.7, 362.5,
+732.7, 362.5, 733.6, 362.1, 740.2, 358.9, 740.2, 358.9, 746.8, 355.7,
+750.9, 353.7, 750.9, 353.7, 755.0, 351.7, 767.9, 345.5, 767.9, 345.5,
+780.8, 339.3, 815.8, 322.6, 815.8, 322.7, 850.7, 305.9, 851.2, 305.7,
+851.6, 305.3, 851.6, 305.3, 851.6, 305.3, 851.2, 305.7, 850.7, 305.9,
+805.7, 329.5, 805.7, 329.5, 760.6, 353.1, 715.3, 376.8, 716.7, 380.0,
+669.9, 400.6, 676.7, 19.0, 678.1, 20.5, 689.6, 2.0, 699.1, 3.6,
+713.8, 6.3, 713.2, 14.6, 725.3, 27.5, 724.0, 26.2, 723.0, 27.1,
+720.8, 26.7, 698.7, 22.8, 689.0, 32.0, 676.7, 19.0, 446.7, 132.3,
+433.1, 136.8, 432.6, 135.3, 418.6, 138.4, 413.9, 139.4, 413.9, 139.4,
+409.3, 140.3, 408.6, 140.5, 408.6, 140.5, 408.0, 140.6, 404.3, 141.4,
+404.3, 142.2, 400.6, 142.2, 400.0, 142.2, 399.3, 141.0, 399.5, 140.5,
+401.5, 136.3, 401.3, 134.8, 405.1, 132.7, 408.4, 130.9, 409.4, 132.7,
+413.7, 132.6, 414.0, 132.6, 414.0, 132.6, 414.2, 132.6, 424.1, 132.5,
+424.1, 132.5, 434.0, 132.5, 435.4, 132.4, 435.4, 132.4, 436.8, 132.4,
+441.7, 132.4, 442.2, 133.8, 446.7, 132.3, 359.9, 57.9, 372.4, 77.4,
+383.0, 94.8, 375.4, 102.9, 379.1, 99.0, 363.7, 84.6, 352.0, 66.3,
+345.7, 56.6, 345.7, 56.6, 339.5, 46.8, 338.4, 45.2, 338.4, 45.2,
+337.4, 43.5, 327.7, 28.4, 331.8, 22.0, 318.0, 13.2, 343.0, 29.2,
+343.7, 32.5, 359.9, 57.9, 318.0, 13.2, 309.1, 2.6, 299.8, -3.8,
+303.0, -10.2, 318.3, -14.2, 318.3, -14.2, 333.7, -18.1, 336.3, -17.9,
+335.7, -12.3, 337.7, -6.6, 348.8, 25.7, 370.5, 47.3, 359.9, 57.9,
+343.7, 32.5, 343.0, 29.2, 318.0, 13.2, 393.4, 131.0, 382.3, 118.8,
+382.3, 117.9, 375.4, 102.9, 383.0, 94.8, 372.4, 77.4, 359.9, 57.9,
+370.5, 47.3, 348.8, 25.7, 337.7, -6.6, 365.8, 53.1, 362.5, 54.7,
+387.3, 116.0, 390.3, 123.5, 388.3, 125.4, 393.4, 131.0, 290.4, 2.4,
+289.0, 0.5, 293.4, -4.2, 298.2, -9.0, 300.6, -9.6, 300.6, -9.6,
+303.0, -10.2, 299.8, -3.8, 309.1, 2.6, 318.0, 13.2, 331.8, 22.0,
+327.7, 28.4, 337.4, 43.5, 336.2, 41.4, 335.5, 41.8, 333.5, 40.1,
+311.9, 21.3, 306.7, 25.1, 290.4, 2.4, 432.1, 236.3, 431.1, 225.7,
+431.2, 225.7, 430.8, 215.1, 431.0, 219.5, 431.2, 219.5, 431.6, 223.8,
+431.8, 226.0, 431.8, 226.0, 432.0, 228.2, 435.6, 267.5, 435.6, 267.5,
+439.3, 306.7, 441.3, 329.1, 441.3, 329.1, 443.4, 351.4, 443.9, 357.8,
+443.9, 357.8, 444.5, 364.1, 444.8, 366.7, 444.8, 366.7, 445.0, 369.4,
+446.9, 389.4, 446.9, 389.4, 448.7, 409.5, 452.1, 447.0, 452.1, 447.0,
+455.6, 484.5, 456.5, 494.1, 456.5, 494.1, 457.3, 503.6, 461.1, 544.5,
+461.1, 544.5, 464.8, 585.3, 466.5, 603.5, 466.6, 603.5, 468.2, 621.6,
+469.3, 634.5, 469.2, 634.5, 470.3, 647.3, 471.5, 660.3, 471.7, 660.3,
+472.9, 673.2, 473.3, 677.4, 473.3, 677.4, 473.7, 681.5, 473.9, 684.3,
+474.0, 684.3, 474.2, 687.1, 474.9, 695.0, 474.8, 695.0, 475.5, 702.9,
+476.7, 715.9, 476.8, 715.9, 478.0, 728.9, 480.1, 750.5, 480.0, 750.5,
+482.0, 772.1, 482.3, 775.1, 482.3, 775.1, 482.5, 778.1, 483.9, 793.3,
+485.3, 808.6, 485.3, 808.6, 485.3, 808.6, 483.9, 793.3, 482.5, 778.1,
+482.3, 775.1, 482.3, 775.1, 482.0, 772.1, 480.0, 750.5, 480.1, 750.5,
+478.0, 728.9, 476.8, 715.9, 476.7, 715.9, 475.5, 702.9, 472.9, 675.1,
+472.9, 675.1, 470.3, 647.3, 469.3, 635.9, 469.4, 635.9, 468.4, 624.5,
+468.3, 623.1, 468.3, 623.1, 468.2, 621.6, 466.6, 603.5, 466.5, 603.5,
+464.8, 585.3, 461.1, 544.5, 461.1, 544.5, 457.3, 503.6, 456.5, 494.1,
+456.5, 494.1, 455.6, 484.5, 452.1, 447.0, 452.1, 447.0, 448.7, 409.5,
+446.9, 389.4, 446.9, 389.4, 445.0, 369.4, 438.6, 302.8, 438.2, 302.9,
+432.1, 236.3, 431.6, 223.8, 431.2, 219.5, 431.0, 219.5, 430.8, 215.1,
+429.9, 201.0, 423.5, 197.5, 429.1, 186.9, 427.2, 190.5, 437.8, 193.7,
+438.3, 201.1, 439.0, 212.2, 437.1, 213.6, 431.6, 223.8, 94.7, 97.2,
+96.3, 96.3, 94.0, 92.2, 93.3, 87.3, 91.4, 73.5, 91.4, 73.5,
+89.5, 59.8, 88.5, 52.6, 84.6, 48.1, 87.6, 45.5, 100.2, 42.2,
+100.2, 42.2, 112.9, 38.9, 122.8, 39.1, 132.9, 43.7, 132.6, 46.2,
+130.6, 63.5, 120.5, 62.4, 108.5, 78.6, 101.6, 87.9, 103.9, 91.9,
+94.7, 97.2, 236.1, 70.1, 237.2, 70.5, 237.2, 74.9, 235.3, 77.7,
+233.3, 80.6, 231.3, 82.1, 228.3, 81.5, 227.1, 73.6, 213.7, 72.4,
+198.1, 70.3, 187.3, 66.3, 178.2, 59.9, 176.5, 62.4, 179.9, 57.6,
+189.0, 64.0, 201.4, 65.6, 203.1, 65.8, 203.1, 65.8, 204.7, 66.0,
+207.8, 66.4, 207.8, 66.4, 210.8, 66.8, 223.5, 68.4, 225.0, 65.1,
+236.1, 70.1, 239.1, 42.7, 241.4, 39.6, 239.6, 38.3, 240.1, 33.8,
+241.7, 19.5, 235.9, 15.3, 243.2, 5.2, 254.1, 2.4, 254.1, 2.4,
+264.9, -0.3, 262.4, 22.3, 253.8, 22.5, 239.1, 42.7, 264.9, -0.3,
+281.5, -4.7, 281.5, -4.7, 298.2, -9.0, 293.4, -4.2, 289.0, 0.5,
+290.4, 2.4, 281.5, 15.3, 281.5, 15.3, 272.6, 28.3, 271.6, 29.7,
+271.6, 29.7, 270.6, 31.1, 270.6, 31.2, 270.6, 31.2, 270.6, 31.2,
+267.6, 35.6, 267.6, 35.6, 264.6, 39.9, 257.8, 49.7, 257.8, 49.7,
+251.1, 59.5, 245.9, 67.1, 246.6, 67.8, 240.6, 74.8, 238.8, 76.9,
+235.8, 78.2, 235.3, 77.7, 237.2, 74.9, 237.2, 70.5, 236.1, 70.1,
+237.6, 56.4, 234.1, 55.0, 239.1, 42.7, 253.8, 22.5, 262.4, 22.3,
+264.9, -0.3, 202.2, 95.7, 215.3, 88.6, 229.1, 86.3, 228.3, 81.5,
+231.3, 82.1, 233.3, 80.6, 235.3, 77.7, 235.8, 78.2, 238.8, 76.9,
+240.6, 74.8, 223.0, 102.9, 229.0, 115.3, 202.9, 129.5, 209.8, 125.8,
+195.3, 108.8, 202.2, 95.7, 202.9, 129.5, 202.9, 131.1, 202.0, 132.5,
+200.8, 132.6, 185.0, 134.2, 184.8, 132.8, 168.8, 133.0, 156.7, 133.2,
+156.7, 133.2, 144.7, 133.3, 142.7, 133.4, 142.0, 132.4, 140.7, 133.4,
+164.5, 120.3, 164.3, 119.7, 186.7, 104.2, 194.5, 100.0, 194.5, 100.0,
+202.2, 95.7, 195.3, 108.8, 209.8, 125.8, 202.9, 129.5, 167.7, 180.7,
+147.7, 192.0, 153.2, 201.8, 138.8, 222.8, 136.8, 225.6, 137.5, 226.5,
+134.9, 228.4, 135.5, 232.9, 120.5, 231.5, 114.4, 240.4, 114.8, 240.2,
+113.1, 237.7, 113.9, 236.9, 139.8, 207.9, 135.3, 199.1, 167.7, 180.7,
+143.9, 642.6, 147.6, 620.7, 147.6, 620.7, 151.3, 598.8, 157.1, 641.0,
+162.5, 640.3, 175.1, 681.5, 177.5, 690.6, 176.4, 690.9, 177.7, 700.4,
+179.3, 711.9, 179.3, 711.9, 180.9, 723.4, 182.7, 737.0, 182.7, 737.0,
+184.6, 750.6, 184.7, 751.0, 184.6, 751.1, 184.7, 751.5, 171.2, 715.6,
+171.3, 715.6, 157.8, 679.7, 156.0, 674.9, 156.0, 674.9, 154.3, 670.2,
+149.1, 656.4, 144.5, 656.6, 143.9, 642.6, 125.1, 606.5, 125.7, 587.3,
+144.0, 585.9, 141.4, 568.5, 139.3, 630.5, 139.6, 630.5, 137.8, 692.5,
+152.3, 658.4, 123.9, 649.3, 125.1, 606.5, 95.0, 640.0, 97.5, 614.8,
+98.9, 614.8, 100.0, 589.5, 104.7, 567.6, 107.5, 568.2, 115.1, 546.9,
+120.5, 531.4, 124.2, 531.8, 126.0, 515.9, 122.8, 543.6, 119.2, 543.2,
+112.4, 570.4, 108.5, 585.7, 108.5, 585.7, 104.7, 601.1, 102.4, 610.2,
+102.4, 610.2, 100.1, 619.4, 97.5, 629.7, 94.9, 629.7, 95.0, 640.0,
+10.6, 447.0, 22.3, 456.0, 16.9, 463.2, 23.2, 479.3, 25.8, 485.7,
+27.2, 485.5, 28.3, 492.1, 28.9, 505.1, 27.6, 505.3, 29.4, 518.2,
+24.2, 514.1, 21.7, 515.4, 19.0, 510.1, 15.9, 505.5, 14.4, 506.5,
+9.8, 503.0, 7.7, 499.0, 9.9, 497.9, 9.9, 492.8, 10.1, 481.1,
+10.1, 481.1, 10.2, 469.4, 10.4, 458.2, 15.8, 451.1, 10.6, 447.0,
+-22.1, 363.9, -22.7, 362.5, -22.7, 362.5, -23.3, 361.1, -25.7, 355.0,
+-26.1, 355.1, -28.1, 348.9, -28.3, 348.1, -27.4, 347.8, -27.6, 347.1,
+-24.5, 355.0, -25.0, 355.2, -22.3, 363.3, -22.2, 363.6, -22.2, 363.6,
+-22.1, 363.9, -24.7, 334.6, -20.5, 316.8, -20.4, 316.8, -16.2, 299.0,
+-16.2, 299.0, -15.8, 297.4, -15.4, 295.8, -12.0, 279.2, -11.2, 279.4,
+-7.1, 263.0, -3.2, 247.2, -3.2, 247.1, 0.6, 231.3, -3.2, 247.1,
+-3.2, 247.2, -7.1, 263.0, -11.2, 279.4, -12.0, 279.2, -15.4, 295.8,
+-15.8, 297.4, -16.2, 299.0, -16.2, 299.0, -19.1, 311.6, -19.1, 311.6,
+-22.1, 324.1, -23.3, 329.4, -23.4, 329.3, -24.7, 334.6, 30.7, 301.5,
+31.6, 294.0, 33.4, 293.8, 32.5, 286.5, 34.3, 297.0, 38.5, 298.0,
+36.1, 307.5, 37.1, 313.2, 37.1, 313.2, 38.1, 318.9, 38.5, 321.0,
+38.5, 321.0, 38.9, 323.2, 39.0, 324.0, 38.9, 324.0, 39.1, 324.7,
+34.8, 313.2, 32.8, 313.5, 30.7, 301.5, 77.0, 541.9, 79.2, 544.8,
+76.2, 547.2, 78.4, 549.9, 55.7, 518.9, 55.4, 519.1, 32.4, 488.3,
+38.9, 497.4, 39.6, 496.9, 46.7, 505.5, 61.9, 523.7, 63.3, 522.7,
+77.0, 541.9, 774.0, 706.6, 772.4, 713.4, 769.6, 713.9, 770.7, 720.3,
+770.8, 720.0, 770.4, 719.9, 770.2, 719.5, 768.7, 717.2, 768.1, 717.4,
+767.2, 714.9, 757.7, 681.8, 756.9, 682.0, 746.6, 649.2, 747.7, 652.9,
+748.3, 652.7, 750.0, 656.2, 762.0, 681.4, 765.5, 680.3, 774.0, 706.6,
+745.8, 465.5, 745.6, 465.3, 745.7, 465.1, 745.7, 464.8, 744.9, 457.9,
+747.0, 452.1, 744.0, 451.0, 767.8, 459.9, 767.8, 463.2, 787.2, 480.4,
+783.7, 494.5, 799.8, 504.6, 819.7, 518.2, 820.2, 518.7, 819.5, 519.3,
+819.2, 520.3, 818.8, 522.1, 819.5, 522.9, 818.4, 523.8, 818.2, 523.9,
+817.5, 523.1, 816.7, 522.4, 781.2, 494.0, 781.1, 494.2, 745.8, 465.5,
+728.5, 558.4, 726.8, 556.9, 725.3, 557.0, 725.0, 555.3, 723.8, 548.1,
+723.8, 548.1, 722.5, 540.9, 722.7, 542.5, 723.0, 542.4, 723.5, 543.9,
+726.0, 551.2, 727.5, 551.0, 728.5, 558.4, 811.9, 675.4, 796.6, 675.8,
+796.6, 675.2, 781.4, 676.2, 776.8, 671.7, 782.5, 663.2, 787.3, 651.6,
+787.0, 652.4, 788.9, 653.1, 790.4, 654.6, 797.3, 661.3, 797.3, 661.3,
+804.3, 668.0, 808.1, 671.7, 809.7, 676.2, 811.9, 675.4, 736.6, 629.3,
+738.1, 633.5, 737.6, 633.7, 739.6, 637.7, 741.7, 649.6, 741.7, 649.6,
+743.8, 661.6, 728.6, 636.5, 724.9, 638.7, 706.0, 615.9, 708.2, 625.6,
+722.7, 620.4, 736.6, 629.3, 706.0, 615.9, 701.3, 610.3, 695.8, 609.3,
+696.7, 604.7, 696.3, 604.2, 696.3, 604.1, 696.0, 603.6, 690.6, 595.2,
+687.4, 596.0, 685.3, 586.9, 684.6, 584.2, 687.8, 583.4, 690.4, 580.0,
+694.8, 574.1, 694.8, 574.1, 699.3, 568.1, 701.3, 565.4, 704.0, 563.8,
+703.4, 562.7, 708.9, 597.6, 719.9, 595.9, 736.5, 629.0, 736.6, 629.2,
+736.5, 629.2, 736.6, 629.3, 722.7, 620.4, 708.2, 625.6, 706.0, 615.9,
+628.6, 378.5, 639.5, 392.5, 639.5, 392.5, 650.4, 406.5, 654.9, 441.5,
+646.0, 442.7, 641.6, 478.9, 641.5, 479.0, 641.5, 479.0, 641.5, 479.0,
+641.1, 482.9, 641.1, 482.9, 640.6, 486.8, 639.5, 496.1, 635.1, 498.0,
+638.3, 505.4, 632.9, 492.8, 637.2, 490.9, 636.1, 476.4, 634.1, 450.4,
+634.1, 450.4, 632.1, 424.4, 630.4, 401.5, 618.0, 388.9, 628.6, 378.5,
+641.6, 478.9, 646.0, 442.7, 654.9, 441.5, 650.4, 406.5, 659.8, 418.7,
+662.6, 432.2, 669.3, 430.8, 669.3, 431.2, 669.6, 431.2, 669.9, 431.6,
+665.0, 440.8, 664.6, 440.6, 659.2, 449.5, 650.4, 464.2, 639.0, 466.5,
+641.6, 478.9, 914.0, 381.7, 920.2, 370.0, 908.9, 364.0, 903.7, 346.4,
+903.5, 345.5, 903.5, 345.5, 903.2, 344.7, 896.6, 321.9, 896.6, 321.9,
+890.0, 299.2, 888.4, 293.8, 888.2, 293.8, 886.8, 288.4, 886.8, 288.3,
+887.1, 288.3, 887.3, 288.2, 895.2, 284.4, 895.2, 284.4, 903.1, 280.6,
+905.1, 279.6, 907.3, 278.8, 907.2, 278.6, 914.2, 299.5, 915.4, 300.0,
+916.8, 321.8, 918.8, 351.5, 926.7, 357.7, 914.0, 381.7, 916.8, 321.8,
+940.6, 341.7, 935.4, 360.4, 933.8, 398.2, 929.2, 388.1, 923.5, 382.0,
+914.3, 382.6, 914.0, 382.4, 914.0, 382.1, 914.0, 381.7, 926.7, 357.7,
+918.8, 351.5, 916.8, 321.8, 869.9, 395.5, 869.6, 395.5, 869.6, 393.4,
+869.3, 391.2, 866.2, 370.9, 866.2, 370.9, 863.2, 350.6, 862.8, 348.1,
+862.8, 348.1, 862.4, 345.6, 861.4, 338.7, 861.4, 338.7, 860.4, 331.9,
+859.2, 323.7, 857.8, 323.7, 858.0, 315.5, 857.9, 319.1, 859.2, 319.2,
+860.5, 322.8, 865.3, 336.2, 868.2, 335.8, 870.1, 349.6, 872.9, 370.0,
+870.0, 370.4, 869.9, 391.2, 869.9, 393.4, 869.9, 395.5, 869.9, 395.5,
+860.5, 322.8, 859.2, 319.2, 857.9, 319.1, 858.0, 315.5, 856.6, 309.4,
+853.5, 305.6, 856.1, 303.1, 867.9, 292.0, 870.8, 290.2, 886.8, 288.4,
+888.2, 293.8, 888.4, 293.8, 890.0, 299.2, 887.6, 305.1, 884.3, 303.7,
+878.7, 308.3, 869.6, 315.5, 870.9, 319.1, 860.5, 322.8, 694.7, 409.5,
+693.6, 408.7, 690.9, 414.1, 688.8, 413.4, 680.5, 407.7, 674.0, 400.5,
+672.2, 402.1, 672.2, 401.6, 671.4, 401.6, 670.6, 401.0, 682.8, 405.0,
+684.5, 402.5, 694.7, 409.5, 860.4, 331.9, 861.4, 338.7, 861.4, 338.7,
+862.4, 345.6, 858.9, 351.0, 855.0, 348.4, 847.6, 351.2, 844.2, 352.5,
+844.2, 352.5, 840.8, 353.8, 820.4, 361.5, 820.4, 361.5, 799.9, 369.3,
+792.1, 372.2, 792.1, 372.2, 784.2, 375.2, 782.6, 375.8, 782.5, 375.6,
+781.0, 376.4, 805.0, 362.6, 805.1, 362.9, 829.3, 349.3, 835.8, 345.6,
+835.8, 345.6, 842.4, 342.0, 851.4, 336.9, 852.6, 330.4, 860.4, 331.9,
+625.4, 361.6, 634.8, 368.9, 633.8, 370.2, 642.2, 378.7, 646.5, 383.2,
+645.7, 384.7, 650.9, 387.6, 634.8, 376.6, 636.3, 372.9, 618.6, 365.6,
+607.2, 357.8, 607.2, 357.8, 595.7, 350.0, 591.1, 346.9, 591.1, 346.9,
+586.5, 343.8, 560.5, 326.1, 560.5, 326.1, 534.6, 308.4, 515.9, 289.9,
+512.4, 293.3, 490.3, 278.3, 494.6, 281.3, 494.8, 281.0, 499.3, 283.8,
+500.2, 284.4, 500.2, 284.4, 501.2, 285.0, 543.6, 311.2, 543.6, 311.2,
+586.0, 337.3, 592.2, 341.2, 592.2, 341.2, 598.4, 345.0, 611.9, 353.3,
+612.9, 352.0, 625.4, 361.6, 251.1, 59.5, 257.8, 49.7, 257.8, 49.7,
+264.6, 39.9, 245.2, 81.7, 243.4, 80.9, 222.1, 121.8, 201.5, 161.7,
+201.5, 161.7, 180.9, 201.5, 171.0, 220.5, 170.9, 220.4, 161.1, 239.4,
+158.7, 243.9, 156.5, 248.6, 156.5, 248.6, 156.5, 248.6, 158.7, 243.9,
+161.1, 239.4, 180.0, 201.5, 180.0, 201.5, 199.0, 163.7, 219.1, 123.5,
+219.1, 123.5, 239.2, 83.3, 245.2, 71.4, 244.4, 70.9, 251.1, 59.5,
+474.9, 881.8, 473.8, 882.9, 470.8, 882.1, 470.7, 882.0, 466.7, 848.4,
+468.7, 848.1, 466.7, 814.3, 465.8, 799.3, 465.8, 799.3, 464.9, 784.3,
+463.3, 759.1, 463.2, 759.1, 461.7, 734.0, 460.1, 708.3, 460.2, 708.3,
+458.7, 682.6, 457.9, 669.3, 457.9, 669.3, 457.1, 655.9, 455.3, 625.3,
+455.3, 625.3, 453.5, 594.6, 453.1, 588.3, 453.1, 588.3, 452.8, 581.9,
+452.6, 578.7, 452.6, 578.7, 452.4, 575.5, 451.9, 567.7, 451.9, 567.7,
+451.5, 559.9, 449.9, 533.5, 450.3, 533.5, 448.3, 507.2, 454.4, 587.2,
+454.0, 587.3, 459.7, 667.4, 462.4, 705.5, 462.4, 705.5, 465.1, 743.6,
+468.5, 791.8, 468.5, 791.8, 471.9, 840.0, 473.3, 859.1, 473.3, 859.1,
+474.7, 878.3, 474.8, 880.1, 475.7, 881.1, 474.9, 881.8, 140.0, 48.9,
+137.3, 46.4, 136.3, 47.5, 132.6, 46.2, 132.9, 43.7, 122.8, 39.1,
+112.9, 38.9, 117.5, 37.7, 117.5, 37.7, 122.0, 36.6, 132.0, 40.2,
+132.1, 41.6, 140.0, 48.9, 168.8, 133.0, 184.8, 132.8, 185.0, 134.2,
+200.8, 132.6, 200.1, 132.2, 196.4, 139.0, 192.0, 145.4, 182.2, 159.7,
+183.0, 160.3, 172.4, 174.0, 173.0, 173.1, 172.2, 172.5, 172.1, 171.1,
+171.0, 158.5, 171.0, 158.5, 169.9, 145.9, 169.7, 143.6, 169.7, 143.6,
+169.5, 141.3, 169.2, 137.2, 168.1, 133.2, 168.8, 133.0, 142.5, 800.7,
+142.4, 726.7, 142.4, 726.7, 142.2, 652.8, 142.1, 647.8, 143.1, 647.7,
+143.9, 642.7, 144.9, 660.2, 143.8, 660.2, 143.6, 677.7, 143.1, 739.2,
+139.1, 739.4, 142.5, 800.7, 112.4, 570.4, 119.2, 543.2, 122.8, 543.6,
+126.0, 515.9, 127.1, 511.9, 127.4, 512.0, 128.8, 508.0, 135.1, 490.1,
+135.1, 490.1, 141.5, 472.1, 141.7, 471.6, 142.0, 471.5, 141.9, 471.0,
+141.9, 472.3, 141.9, 472.3, 141.9, 473.6, 127.5, 522.1, 130.1, 523.1,
+112.4, 570.4, 132.1, 396.2, 135.2, 425.6, 135.2, 425.6, 138.4, 454.9,
+109.9, 423.8, 122.8, 412.0, 107.3, 369.1, 106.3, 366.5, 106.3, 366.5,
+105.4, 363.9, 98.1, 343.8, 98.1, 343.8, 90.8, 323.7, 84.3, 305.5,
+84.3, 305.5, 77.7, 287.3, 72.4, 272.6, 72.4, 272.6, 67.0, 257.9,
+64.0, 249.4, 64.0, 249.4, 60.9, 240.9, 58.1, 233.2, 58.4, 225.5,
+55.3, 225.4, 59.1, 225.6, 58.8, 233.2, 62.3, 240.9, 63.3, 243.2,
+63.3, 243.2, 64.3, 245.5, 70.8, 259.9, 70.8, 259.9, 77.2, 274.3,
+90.8, 304.5, 90.8, 304.5, 104.4, 334.7, 106.6, 339.6, 106.6, 339.6,
+108.8, 344.5, 120.5, 370.3, 124.8, 369.2, 132.1, 396.2, 73.9, 510.3,
+74.7, 516.4, 72.5, 516.8, 73.7, 522.7, 63.8, 473.9, 62.6, 474.0,
+56.6, 424.6, 60.5, 456.8, 63.0, 456.5, 69.5, 488.4, 71.7, 499.4,
+72.6, 499.3, 73.9, 510.3, -25.5, 438.4, -32.2, 422.8, -39.0, 422.7,
+-38.9, 407.2, -40.1, 403.8, -40.7, 403.4, -40.2, 400.0, -38.5, 387.8,
+-34.0, 376.1, -34.5, 375.9, -29.2, 377.4, -32.6, 389.3, -30.6, 402.7,
+-28.1, 420.5, -20.0, 435.3, -25.5, 438.4, 22.8, 369.7, 23.3, 379.1,
+23.2, 379.1, 23.7, 388.5, 25.5, 440.3, 26.0, 440.3, 28.3, 492.1,
+27.2, 485.5, 25.8, 485.7, 23.2, 479.3, 22.2, 474.7, 23.2, 474.4,
+23.2, 469.6, 23.1, 435.6, 23.1, 435.6, 22.9, 401.7, 22.9, 387.5,
+22.9, 387.5, 22.8, 373.4, 22.8, 371.5, 22.9, 371.5, 22.8, 369.7,
+-7.1, 263.0, -3.2, 247.2, -3.2, 247.1, 0.6, 231.3, 2.8, 222.5,
+2.7, 222.5, 5.0, 213.7, 8.2, 201.4, 6.4, 200.6, 11.4, 189.1,
+12.6, 184.6, 11.6, 184.0, 13.9, 180.0, 13.5, 181.6, 14.2, 181.8,
+14.5, 183.5, 16.1, 189.6, 14.8, 189.9, 15.1, 196.4, 21.6, 253.7,
+15.7, 254.3, 16.3, 312.3, 18.3, 298.8, 9.2, 297.5, 2.2, 282.6,
+-2.5, 272.8, -6.8, 272.9, -7.1, 263.0, 25.8, 421.8, 24.7, 405.2,
+24.2, 405.2, 23.7, 388.5, 23.2, 379.1, 23.3, 379.1, 22.8, 369.7,
+22.8, 369.7, 22.8, 369.7, 22.8, 369.6, 22.8, 369.6, 22.8, 369.5,
+22.8, 369.5, 19.7, 343.1, 26.0, 342.4, 29.1, 315.3, 35.6, 352.4,
+27.9, 353.7, 26.8, 392.2, 26.7, 393.2, 26.7, 393.2, 26.7, 394.2,
+26.2, 408.0, 20.2, 411.2, 25.8, 421.8, 55.5, 418.2, 55.0, 421.2,
+56.0, 421.4, 56.6, 424.6, 62.6, 474.0, 63.8, 473.9, 73.7, 522.7,
+75.4, 532.3, 71.5, 538.3, 77.0, 541.9, 63.3, 522.7, 61.9, 523.7,
+46.7, 505.5, 40.6, 486.7, 48.9, 484.0, 51.0, 462.5, 53.2, 440.3,
+52.2, 440.2, 55.5, 418.2, 40.2, 617.2, 64.0, 620.8, 64.0, 620.8,
+87.8, 624.5, 87.7, 638.4, 88.9, 638.4, 89.9, 652.3, 89.9, 652.3,
+89.7, 652.3, 89.4, 652.2, 80.8, 650.5, 80.8, 650.5, 72.2, 648.9,
+62.8, 647.0, 59.0, 650.8, 53.4, 645.1, 43.1, 634.9, 38.9, 618.0,
+40.2, 617.2, 53.4, 645.1, 53.2, 643.6, 47.9, 644.1, 42.5, 643.0,
+35.6, 641.6, 35.6, 641.6, 28.8, 640.3, 21.0, 638.8, 17.9, 641.9,
+13.1, 637.2, 7.2, 631.4, 5.5, 627.2, 7.3, 619.4, 7.9, 620.2,
+18.8, 616.3, 18.7, 614.1, 18.6, 614.0, 18.8, 614.0, 19.0, 613.9,
+19.7, 612.1, 29.6, 615.5, 40.2, 617.2, 38.9, 618.0, 43.1, 634.9,
+53.4, 645.1, 75.8, 715.5, 69.0, 712.1, 69.0, 712.1, 62.2, 708.7,
+58.6, 707.9, 59.3, 705.0, 56.3, 701.2, 67.0, 706.5, 68.6, 706.3,
+75.8, 715.5, 789.1, 644.2, 787.8, 644.4, 788.2, 647.9, 787.3, 651.6,
+782.5, 663.2, 776.8, 671.7, 781.4, 676.2, 781.2, 676.2, 781.0, 677.3,
+781.1, 677.4, 776.3, 677.8, 777.6, 692.0, 774.0, 706.6, 765.5, 680.3,
+762.0, 681.4, 750.0, 656.2, 752.4, 646.1, 769.1, 646.7, 789.1, 644.2,
+750.0, 656.2, 748.3, 652.7, 747.7, 652.9, 746.6, 649.2, 741.5, 633.0,
+741.7, 632.9, 736.4, 616.8, 754.8, 618.3, 757.0, 625.1, 773.1, 619.7,
+778.2, 625.5, 784.0, 622.2, 794.7, 621.4, 794.7, 621.4, 794.2, 623.3,
+793.8, 625.1, 791.5, 634.7, 795.2, 639.9, 789.1, 644.2, 769.1, 646.7,
+752.4, 646.1, 750.0, 656.2, 748.1, 484.6, 742.7, 479.3, 750.7, 468.2,
+745.8, 465.5, 781.1, 494.2, 781.2, 494.0, 816.7, 522.4, 809.9, 516.5,
+808.6, 518.0, 800.5, 513.5, 793.3, 509.5, 793.3, 509.5, 786.1, 505.6,
+767.1, 495.1, 762.9, 499.3, 748.1, 484.6, 907.8, 630.0, 907.7, 629.7,
+907.1, 629.8, 907.1, 629.4, 904.8, 615.5, 904.2, 615.5, 903.1, 601.5,
+903.9, 612.2, 904.8, 612.1, 906.6, 622.8, 907.2, 626.4, 907.5, 626.4,
+907.8, 630.0, 595.7, 350.0, 607.2, 357.8, 607.2, 357.8, 618.6, 365.6,
+625.2, 368.3, 623.6, 372.1, 628.6, 378.5, 618.0, 388.9, 630.4, 401.5,
+632.1, 424.4, 604.2, 402.0, 614.7, 388.9, 597.3, 353.4, 596.5, 351.7,
+597.0, 350.8, 595.7, 350.0, 659.2, 449.5, 664.6, 440.6, 665.0, 440.8,
+669.9, 431.6, 678.3, 442.4, 678.3, 442.4, 686.6, 453.1, 683.8, 457.8,
+665.3, 457.3, 659.2, 449.5, 948.0, 402.8, 948.7, 413.7, 949.3, 413.7,
+949.4, 424.6, 949.8, 448.4, 949.8, 448.4, 950.3, 472.2, 948.5, 462.5,
+948.1, 462.5, 945.9, 452.8, 942.7, 430.1, 943.4, 430.0, 940.8, 407.3,
+937.7, 379.2, 924.2, 357.6, 934.6, 351.1, 927.8, 355.4, 942.7, 376.7,
+948.0, 402.8, 934.6, 351.1, 927.7, 310.9, 923.3, 310.1, 925.5, 269.8,
+925.6, 266.1, 939.6, 265.2, 939.3, 263.2, 942.6, 333.0, 943.6, 333.0,
+948.0, 402.8, 942.7, 376.7, 927.8, 355.4, 934.6, 351.1, 914.7, 275.0,
+914.5, 273.9, 918.0, 273.4, 921.4, 271.7, 923.2, 270.9, 923.2, 270.9,
+925.0, 270.0, 925.2, 269.9, 925.5, 269.9, 925.5, 269.8, 923.3, 310.1,
+927.7, 310.9, 934.6, 351.1, 924.2, 357.6, 937.7, 379.2, 940.8, 407.3,
+923.8, 342.2, 924.2, 341.6, 914.7, 275.0, 940.8, 407.3, 943.4, 430.0,
+942.7, 430.1, 945.9, 452.8, 939.9, 425.5, 945.1, 423.2, 933.8, 398.2,
+935.4, 360.4, 940.6, 341.7, 916.8, 321.8, 915.4, 300.0, 914.2, 299.5,
+907.2, 278.6, 907.1, 277.0, 914.9, 275.8, 914.7, 275.0, 924.2, 341.6,
+923.8, 342.2, 940.8, 407.3, 760.6, 353.1, 805.7, 329.5, 805.7, 329.5,
+850.7, 305.9, 823.3, 315.5, 825.7, 322.5, 800.7, 339.1, 776.8, 355.0,
+776.8, 355.0, 752.8, 370.9, 744.0, 376.7, 738.7, 374.4, 735.2, 382.6,
+742.6, 365.5, 745.0, 363.5, 760.6, 353.1, 735.2, 382.6, 717.6, 397.7,
+715.7, 395.5, 696.2, 408.5, 695.5, 409.0, 695.3, 409.6, 694.7, 409.5,
+684.5, 402.5, 682.8, 405.0, 670.6, 401.0, 670.3, 400.8, 670.3, 400.8,
+669.9, 400.6, 716.7, 380.0, 715.3, 376.8, 760.6, 353.1, 745.0, 363.5,
+742.6, 365.5, 735.2, 382.6, 829.3, 349.3, 805.1, 362.9, 805.0, 362.6,
+781.0, 376.4, 775.0, 379.5, 774.6, 378.8, 768.3, 381.2, 766.2, 382.0,
+766.2, 382.0, 764.0, 382.8, 751.7, 387.5, 751.7, 387.5, 739.4, 392.2,
+727.0, 396.9, 727.0, 396.9, 714.5, 401.6, 705.4, 405.0, 702.9, 402.3,
+696.2, 408.5, 715.7, 395.5, 717.6, 397.7, 735.2, 382.6, 738.7, 374.4,
+744.0, 376.7, 752.8, 370.9, 790.2, 357.7, 794.5, 342.5, 829.3, 349.3,
+823.9, 404.0, 808.4, 391.3, 812.1, 386.9, 800.2, 369.8, 800.1, 369.5,
+800.0, 369.2, 799.9, 369.3, 820.4, 361.5, 820.4, 361.5, 840.8, 353.8,
+846.5, 358.5, 834.3, 373.1, 827.8, 392.5, 825.8, 398.3, 822.2, 402.7,
+823.9, 404.0, 520.2, 128.3, 522.8, 135.7, 526.6, 134.1, 530.8, 141.2,
+541.2, 158.9, 540.1, 159.5, 549.5, 177.8, 563.9, 205.9, 566.4, 204.8,
+578.3, 234.0, 576.1, 228.6, 572.1, 230.5, 569.0, 225.3, 554.9, 201.4,
+556.5, 200.5, 544.1, 175.8, 542.3, 172.2, 542.3, 172.2, 540.5, 168.7,
+530.3, 148.5, 527.6, 149.4, 520.2, 128.3, 649.0, 299.7, 641.0, 276.2,
+628.3, 280.5, 607.6, 261.2, 603.6, 257.5, 603.6, 257.5, 599.6, 253.8,
+588.9, 243.9, 586.1, 245.8, 578.3, 234.0, 566.4, 204.8, 563.9, 205.9,
+549.5, 177.8, 581.3, 186.8, 575.0, 209.0, 600.5, 240.3, 624.8, 270.0,
+637.5, 265.7, 649.0, 299.7, 621.3, 242.7, 629.3, 253.2, 624.4, 257.5,
+633.4, 266.7, 601.5, 234.1, 604.4, 231.3, 575.4, 195.8, 570.5, 189.8,
+571.2, 189.2, 565.6, 183.9, 578.8, 196.4, 578.2, 197.1, 590.7, 210.4,
+606.0, 226.5, 607.9, 225.1, 621.3, 242.7, 607.6, 261.2, 628.3, 280.5,
+641.0, 276.2, 649.0, 299.7, 666.4, 315.9, 667.6, 314.8, 683.8, 332.1,
+680.1, 328.1, 678.1, 330.1, 674.0, 326.3, 640.0, 294.7, 639.4, 295.1,
+607.6, 261.2, 538.5, 273.3, 548.2, 283.1, 548.2, 283.1, 557.8, 293.0,
+582.8, 318.3, 582.8, 318.3, 607.8, 343.7, 616.6, 352.7, 617.9, 362.6,
+625.4, 361.6, 612.9, 352.0, 611.9, 353.3, 598.4, 345.0, 568.0, 314.6,
+570.6, 312.1, 542.8, 279.0, 542.2, 278.3, 542.2, 278.2, 541.6, 277.4,
+540.5, 275.8, 540.5, 275.8, 539.3, 274.3, 538.9, 273.8, 538.9, 273.7,
+538.5, 273.3, 671.6, 22.5, 673.6, 20.2, 674.3, 18.8, 676.7, 19.0,
+689.0, 32.0, 698.7, 22.8, 720.8, 26.7, 691.3, 60.0, 674.9, 71.6,
+633.3, 68.0, 650.3, 69.5, 651.8, 44.7, 671.6, 22.5, 633.3, 68.0,
+619.2, 79.9, 616.0, 76.2, 598.7, 84.3, 570.9, 97.4, 571.6, 99.2,
+543.0, 110.6, 550.9, 107.4, 550.2, 105.6, 557.4, 100.7, 560.1, 98.9,
+560.1, 98.9, 562.7, 97.1, 566.7, 94.4, 566.7, 94.4, 570.6, 91.6,
+605.0, 68.1, 605.0, 68.1, 639.3, 44.6, 655.4, 33.6, 667.9, 36.8,
+671.6, 22.5, 651.8, 44.7, 650.3, 69.5, 633.3, 68.0, 623.1, 94.6,
+622.0, 94.0, 621.5, 95.0, 619.9, 95.3, 615.1, 96.3, 615.1, 96.3,
+610.4, 97.3, 600.3, 99.5, 600.3, 99.5, 590.2, 101.6, 580.0, 103.8,
+580.0, 103.8, 569.8, 106.0, 569.2, 106.1, 569.2, 106.1, 568.7, 106.2,
+561.4, 107.8, 561.4, 107.8, 554.1, 109.4, 547.3, 110.8, 547.4, 111.5,
+540.5, 112.3, 541.8, 112.1, 541.7, 111.2, 543.0, 110.6, 571.6, 99.2,
+570.9, 97.4, 598.7, 84.3, 599.2, 84.1, 599.4, 84.6, 600.1, 84.9,
+611.6, 89.7, 612.1, 88.8, 623.1, 94.6, 528.8, -39.2, 527.3, -47.3,
+543.8, -57.6, 542.7, -58.6, 554.3, -47.9, 557.9, -30.8, 549.9, -19.7,
+550.9, -21.1, 530.9, -27.9, 528.8, -39.2, 686.9, -7.4, 688.8, -8.6,
+700.2, 0.9, 699.1, 3.6, 689.6, 2.0, 678.1, 20.5, 676.7, 19.0,
+674.3, 18.8, 673.6, 20.2, 671.6, 22.5, 667.9, 36.8, 655.4, 33.6,
+639.3, 44.6, 643.9, 42.0, 643.1, 40.5, 646.9, 36.3, 666.9, 14.4,
+662.7, 7.7, 686.9, -7.4, 432.7, -43.8, 494.1, -60.9, 493.9, -61.6,
+555.8, -76.8, 554.4, -76.4, 555.1, -74.3, 553.5, -73.6, 548.9, -71.4,
+548.5, -72.3, 543.4, -71.1, 508.4, -62.5, 508.4, -62.5, 473.5, -53.9,
+471.6, -53.4, 471.6, -53.4, 469.8, -53.0, 451.2, -48.4, 451.1, -48.9,
+432.7, -43.8, 223.1, 199.1, 223.1, 199.1, 232.3, 166.6, 241.5, 134.0,
+245.6, 119.3, 245.6, 119.3, 249.8, 104.6, 253.8, 90.4, 253.9, 90.4,
+257.9, 76.1, 260.2, 67.8, 260.2, 67.8, 262.6, 59.6, 266.1, 47.2,
+266.1, 47.2, 269.6, 34.9, 270.1, 33.1, 270.0, 33.1, 270.6, 31.2,
+270.6, 31.2, 270.6, 31.2, 270.6, 31.1, 270.1, 33.0, 270.1, 33.0,
+269.6, 34.9, 266.1, 47.2, 266.1, 47.2, 262.6, 59.6, 260.2, 67.8,
+260.2, 67.8, 257.9, 76.1, 253.9, 90.4, 253.8, 90.4, 249.8, 104.6,
+245.6, 119.3, 245.6, 119.3, 241.5, 134.0, 232.3, 166.6, 223.1, 199.1,
+223.1, 199.1, 149.0, 461.3, 149.0, 461.3, 149.7, 458.6, 150.5, 455.9,
+178.7, 356.3, 175.0, 355.1, 206.8, 256.7, 206.6, 257.2, 214.0, 259.0,
+213.8, 260.1, 210.0, 283.9, 206.3, 283.4, 198.8, 306.6, 194.5, 320.1,
+194.5, 320.1, 190.1, 333.6, 170.3, 394.7, 170.2, 394.7, 150.5, 455.9,
+149.7, 458.6, 149.0, 461.3, 149.0, 461.3, 238.0, 140.7, 239.4, 137.2,
+240.6, 137.6, 241.5, 134.0, 232.3, 166.6, 223.1, 199.1, 223.1, 199.1,
+215.0, 227.9, 215.1, 227.9, 206.8, 256.7, 194.4, 299.9, 194.2, 299.8,
+181.8, 343.0, 179.0, 352.7, 179.1, 352.8, 176.4, 362.5, 165.4, 402.3,
+165.4, 402.3, 154.3, 442.2, 152.4, 449.0, 150.5, 455.9, 150.5, 455.9,
+150.5, 455.9, 152.4, 449.0, 154.3, 442.2, 165.4, 402.3, 165.4, 402.3,
+176.4, 362.5, 179.1, 352.8, 179.0, 352.7, 181.8, 343.0, 189.6, 315.0,
+189.6, 315.0, 197.4, 286.9, 200.1, 277.3, 200.1, 277.3, 202.7, 267.7,
+214.8, 224.2, 214.8, 224.2, 226.9, 180.6, 231.1, 165.6, 231.1, 165.6,
+235.3, 150.5, 235.5, 149.5, 235.5, 149.5, 235.8, 148.5, 236.9, 144.6,
+236.5, 144.5, 238.0, 140.7, 222.1, 121.8, 243.4, 80.9, 245.2, 81.7,
+264.6, 39.9, 267.6, 35.6, 267.6, 35.6, 270.6, 31.2, 269.1, 45.7,
+266.6, 45.4, 262.6, 59.6, 260.2, 67.8, 260.2, 67.8, 257.9, 76.1,
+253.9, 90.4, 253.8, 90.4, 249.8, 104.6, 247.0, 109.7, 244.2, 108.1,
+238.5, 111.6, 230.3, 116.7, 224.7, 114.7, 222.1, 121.8, 161.1, 239.4,
+158.7, 243.9, 156.5, 248.6, 156.5, 248.6, 156.0, 249.5, 156.0, 249.5,
+155.5, 250.4, 147.2, 266.5, 138.9, 282.6, 138.9, 282.6, 138.9, 282.6,
+147.2, 266.5, 155.5, 250.4, 156.0, 249.5, 156.0, 249.5, 156.5, 248.6,
+156.5, 248.6, 158.7, 243.9, 161.1, 239.4, 464.8, 585.3, 461.1, 544.5,
+461.1, 544.5, 457.3, 503.6, 457.3, 502.5, 458.0, 502.4, 458.7, 501.2,
+460.2, 498.5, 460.2, 498.5, 461.7, 495.7, 462.7, 493.9, 463.8, 493.9,
+463.8, 492.1, 465.4, 538.7, 484.5, 550.0, 464.8, 585.3, 209.1, 647.2,
+209.1, 646.8, 209.3, 646.8, 209.5, 646.5, 212.6, 641.2, 212.6, 641.2,
+215.7, 636.0, 218.1, 631.9, 217.3, 628.6, 220.5, 627.8, 219.5, 628.1,
+220.3, 631.4, 220.0, 634.9, 219.1, 648.1, 219.1, 648.1, 218.1, 661.3,
+214.3, 713.1, 225.7, 716.7, 210.6, 764.9, 221.5, 730.2, 210.1, 726.6,
+209.6, 688.4, 209.5, 676.0, 209.5, 676.0, 209.3, 663.7, 209.2, 655.4,
+209.0, 655.4, 209.1, 647.2, 466.7, 814.3, 468.7, 848.1, 466.7, 848.4,
+470.7, 882.0, 470.1, 884.5, 460.2, 884.2, 450.3, 882.5, 450.5, 882.6,
+450.8, 880.6, 451.3, 878.7, 459.0, 846.5, 464.5, 813.9, 466.7, 814.3,
+131.9, 102.5, 132.2, 103.4, 132.6, 103.2, 133.3, 103.9, 134.2, 104.8,
+134.2, 104.8, 135.1, 105.7, 136.0, 106.7, 136.0, 106.7, 136.9, 107.6,
+144.5, 115.4, 149.7, 114.0, 152.1, 123.1, 147.4, 125.8, 147.4, 125.8,
+142.6, 128.4, 133.5, 119.1, 136.9, 115.6, 131.9, 102.5, 172.1, 171.1,
+172.2, 172.5, 173.0, 173.1, 172.4, 174.0, 172.0, 175.3, 170.6, 175.3,
+170.7, 176.4, 170.5, 173.8, 170.6, 173.2, 172.1, 171.1, 188.5, 778.9,
+183.6, 784.8, 193.4, 790.8, 191.6, 801.6, 200.2, 807.9, 172.4, 831.7,
+175.5, 859.3, 176.2, 858.1, 164.7, 854.8, 164.5, 849.8, 163.8, 836.8,
+169.0, 836.5, 173.5, 823.2, 181.0, 801.1, 174.5, 795.6, 188.5, 778.9,
+124.4, 697.1, 123.6, 699.9, 136.9, 701.5, 137.3, 706.8, 135.6, 765.0,
+131.5, 765.1, 133.9, 823.2, 134.3, 819.7, 127.4, 820.2, 124.7, 815.1,
+126.5, 809.3, 123.7, 808.3, 122.4, 801.6, 120.3, 788.4, 120.1, 788.5,
+117.8, 775.3, 118.5, 736.2, 113.8, 734.1, 124.4, 697.1, 117.8, 775.3,
+117.4, 773.4, 117.4, 773.4, 117.1, 771.4, 109.1, 725.5, 98.5, 724.9,
+101.1, 679.7, 100.7, 686.7, 111.3, 687.3, 121.5, 694.9, 123.0, 696.0,
+124.5, 696.1, 124.4, 697.1, 113.8, 734.1, 118.5, 736.2, 117.8, 775.3,
+113.5, 633.8, 113.4, 625.4, 117.1, 625.4, 120.7, 617.0, 122.9, 611.8,
+126.2, 611.0, 125.1, 606.5, 123.9, 649.3, 152.3, 658.4, 137.8, 692.5,
+137.6, 699.6, 137.6, 699.6, 137.3, 706.8, 136.9, 701.5, 123.6, 699.9,
+124.4, 697.1, 124.5, 696.1, 123.0, 696.0, 121.5, 694.9, 116.3, 664.6,
+113.8, 664.4, 113.5, 633.8, 121.5, 694.9, 111.3, 687.3, 100.7, 686.7,
+101.1, 679.7, 100.1, 673.7, 100.0, 673.7, 99.0, 667.8, 98.9, 666.1,
+99.7, 666.1, 100.4, 664.5, 107.0, 649.1, 118.6, 641.1, 113.5, 633.8,
+113.8, 664.4, 116.3, 664.6, 121.5, 694.9, 104.7, 601.1, 108.5, 585.7,
+108.5, 585.7, 112.4, 570.4, 130.1, 523.1, 127.5, 522.1, 141.9, 473.6,
+141.9, 494.5, 141.9, 494.5, 141.9, 515.4, 129.0, 559.9, 126.7, 560.0,
+104.7, 601.1, 74.1, 373.8, 73.1, 372.4, 73.1, 372.4, 72.1, 371.0,
+72.8, 344.1, 73.9, 344.1, 75.7, 317.2, 75.8, 314.6, 76.0, 314.7,
+76.0, 312.1, 75.6, 328.2, 75.5, 328.2, 75.0, 344.2, 74.5, 359.0,
+75.7, 359.1, 74.1, 373.8, 38.1, 251.5, 39.2, 217.8, 39.2, 217.8,
+40.3, 184.2, 38.1, 193.9, 44.4, 195.3, 48.4, 206.4, 49.9, 210.5,
+49.9, 210.5, 51.4, 214.6, 53.3, 220.0, 53.3, 220.0, 55.3, 225.4,
+58.4, 225.5, 58.1, 233.2, 60.9, 240.9, 55.0, 249.8, 37.7, 250.5,
+38.1, 251.5, 69.5, 488.4, 63.0, 456.5, 60.5, 456.8, 56.6, 424.6,
+56.0, 421.4, 55.0, 421.2, 55.5, 418.2, 52.4, 400.5, 52.4, 400.5,
+49.3, 382.8, 48.7, 379.6, 48.7, 379.6, 48.2, 376.4, 46.4, 366.3,
+46.6, 366.3, 44.6, 356.3, 49.8, 382.7, 49.6, 382.7, 54.6, 409.2,
+62.0, 448.8, 64.4, 448.5, 69.5, 488.4, 104.4, 482.2, 105.1, 470.7,
+92.8, 469.9, 81.3, 457.6, 78.2, 454.3, 75.5, 454.7, 75.1, 451.0,
+72.7, 430.8, 69.3, 428.3, 75.8, 409.9, 73.3, 416.9, 79.5, 419.1,
+83.1, 428.3, 93.7, 455.2, 106.0, 456.0, 104.4, 482.2, 9.9, 492.8,
+9.9, 497.9, 7.7, 499.0, 9.8, 503.0, 5.2, 499.5, 3.7, 500.5,
+0.7, 495.9, -0.1, 494.8, -0.5, 495.0, -1.6, 494.1, -10.5, 465.3,
+-13.6, 466.3, -25.5, 438.4, -20.0, 435.3, -28.1, 420.5, -30.6, 402.7,
+-30.4, 406.9, -28.8, 406.8, -27.0, 410.9, -26.2, 412.5, -26.2, 412.5,
+-25.5, 414.1, -7.8, 453.5, -6.0, 452.8, 9.9, 492.8, 23.2, 469.6,
+23.2, 474.4, 22.2, 474.7, 23.2, 479.3, 16.9, 463.2, 22.3, 456.0,
+10.6, 447.0, -2.7, 413.2, -2.5, 413.1, -15.8, 379.3, -17.1, 376.1,
+-18.6, 372.9, -18.6, 372.9, -18.6, 372.9, -17.1, 376.1, -15.8, 379.3,
+-10.6, 391.4, -10.6, 391.4, -5.3, 403.6, 8.9, 436.6, 10.5, 436.0,
+23.2, 469.6, 16.7, 353.2, 16.8, 359.2, 14.8, 360.0, 16.8, 365.3,
+9.7, 365.8, 1.8, 359.9, 1.7, 354.3, 1.7, 353.8, 12.1, 349.8,
+16.7, 353.2, 26.7, 394.2, 27.9, 354.8, 27.4, 354.7, 29.1, 315.3,
+29.9, 308.7, 29.9, 308.7, 30.7, 302.0, 29.3, 347.1, 28.7, 347.1,
+26.8, 392.2, 26.7, 393.2, 26.7, 393.2, 26.7, 394.2, 51.0, 462.5,
+48.9, 484.0, 40.6, 486.7, 46.7, 505.5, 39.6, 496.9, 38.9, 497.4,
+32.4, 488.3, 31.2, 486.7, 30.0, 486.7, 30.0, 485.1, 29.1, 470.9,
+29.1, 470.9, 28.1, 456.8, 34.9, 416.1, 36.8, 416.2, 48.2, 376.4,
+48.7, 379.6, 48.7, 379.6, 49.3, 382.8, 50.6, 422.6, 51.9, 422.7,
+51.0, 462.5, 49.3, 382.8, 52.4, 400.5, 52.4, 400.5, 55.5, 418.2,
+52.2, 440.2, 53.2, 440.3, 51.0, 462.5, 51.9, 422.7, 50.6, 422.6,
+49.3, 382.8, 43.3, 684.7, 30.8, 668.9, 32.9, 655.3, 18.3, 653.0,
+14.0, 646.2, 10.3, 640.7, 13.1, 637.2, 17.9, 641.9, 21.0, 638.8,
+28.8, 640.3, 40.9, 659.4, 51.2, 675.0, 43.3, 684.7, 713.3, 489.1,
+712.7, 488.6, 713.1, 488.1, 713.0, 487.0, 713.6, 472.0, 710.3, 471.8,
+707.6, 456.6, 726.2, 469.7, 719.9, 478.6, 732.3, 500.6, 732.8, 501.5,
+732.5, 501.9, 733.3, 502.3, 728.0, 499.4, 728.2, 499.0, 723.2, 495.6,
+718.2, 492.3, 717.8, 492.9, 713.3, 489.1, 752.2, 518.9, 756.0, 528.8,
+751.3, 539.8, 754.8, 540.6, 745.8, 538.6, 748.0, 528.5, 741.2, 516.5,
+739.2, 512.8, 736.7, 509.4, 737.1, 509.1, 738.4, 508.1, 740.9, 511.5,
+744.7, 514.0, 748.4, 516.4, 750.9, 515.5, 752.2, 518.9, 839.3, 624.0,
+867.6, 630.5, 867.6, 630.8, 895.8, 637.6, 900.4, 638.7, 903.8, 641.2,
+904.9, 639.8, 891.5, 650.5, 891.5, 650.5, 878.0, 661.2, 856.0, 656.1,
+859.1, 643.0, 840.1, 624.8, 839.7, 624.4, 839.8, 624.1, 839.3, 624.0,
+835.3, 566.8, 835.3, 566.8, 836.0, 567.4, 836.7, 568.0, 857.1, 585.8,
+857.1, 585.8, 877.5, 603.6, 892.3, 616.5, 892.3, 616.5, 907.1, 629.4,
+907.1, 629.8, 907.7, 629.7, 907.8, 630.0, 910.0, 632.0, 912.3, 632.1,
+912.2, 633.9, 909.8, 635.8, 909.8, 635.8, 907.5, 637.7, 884.6, 619.5,
+886.4, 617.2, 865.4, 596.6, 865.0, 596.2, 865.1, 596.1, 864.8, 595.8,
+852.2, 583.3, 852.1, 583.4, 839.5, 570.9, 838.1, 569.5, 838.2, 569.5,
+836.7, 568.0, 836.0, 567.4, 835.3, 566.8, 835.3, 566.8, 845.9, 561.2,
+855.0, 547.1, 858.7, 547.4, 874.5, 539.8, 876.3, 539.0, 881.2, 544.2,
+881.1, 544.3, 877.6, 549.8, 874.2, 547.6, 867.3, 550.9, 856.6, 556.1,
+851.4, 552.7, 845.9, 561.2, 819.2, 520.3, 819.5, 519.3, 820.2, 518.7,
+819.7, 518.2, 817.7, 516.8, 825.6, 507.8, 823.0, 504.7, 839.9, 508.5,
+838.4, 515.2, 853.8, 525.7, 864.1, 532.8, 875.5, 535.3, 874.5, 539.8,
+858.7, 547.4, 855.0, 547.1, 845.9, 561.2, 842.7, 563.6, 839.5, 565.7,
+839.6, 565.9, 826.2, 545.2, 828.8, 543.4, 819.2, 520.3, 839.6, 565.9,
+839.8, 566.4, 838.3, 567.7, 836.7, 568.0, 836.0, 567.4, 835.3, 566.8,
+835.3, 566.8, 824.6, 557.5, 824.6, 557.5, 813.9, 548.1, 813.3, 547.6,
+812.6, 547.5, 812.7, 547.1, 814.9, 535.3, 815.5, 535.4, 818.4, 523.8,
+819.5, 522.9, 818.8, 522.1, 819.2, 520.3, 828.8, 543.4, 826.2, 545.2,
+839.6, 565.9, 906.6, 622.8, 904.8, 612.1, 903.9, 612.2, 903.1, 601.5,
+900.4, 583.4, 900.5, 583.4, 897.9, 565.3, 897.9, 565.1, 897.9, 565.1,
+897.9, 564.9, 899.5, 575.6, 899.5, 575.6, 901.1, 586.3, 903.8, 604.5,
+904.2, 604.5, 906.6, 622.8, 597.3, 353.4, 614.7, 388.9, 604.2, 402.0,
+632.1, 424.4, 634.1, 450.4, 634.1, 450.4, 636.1, 476.4, 636.0, 474.9,
+635.6, 474.9, 635.2, 473.4, 630.7, 459.4, 630.7, 459.4, 626.3, 445.3,
+611.8, 399.4, 586.5, 392.3, 597.3, 353.4, 690.4, 580.0, 687.8, 583.4,
+684.6, 584.2, 685.3, 586.9, 684.2, 587.1, 681.3, 582.6, 682.0, 581.7,
+683.9, 579.2, 689.6, 578.7, 690.4, 580.0, 911.3, 564.9, 903.7, 560.0,
+903.9, 559.8, 896.4, 554.7, 894.9, 553.7, 894.4, 554.0, 893.3, 552.6,
+892.7, 551.8, 893.2, 551.5, 893.0, 550.3, 892.0, 543.8, 892.0, 543.8,
+891.0, 537.3, 890.9, 536.6, 891.2, 536.4, 890.9, 536.0, 895.6, 542.1,
+895.3, 542.3, 899.8, 548.7, 905.1, 556.1, 905.1, 556.1, 910.3, 563.5,
+910.4, 563.6, 910.4, 563.6, 910.5, 563.7, 910.9, 564.3, 910.8, 564.5,
+911.3, 564.9, 881.4, 472.9, 881.6, 434.2, 875.4, 434.2, 869.9, 395.5,
+869.9, 395.5, 869.9, 393.4, 869.9, 391.2, 876.0, 432.0, 881.6, 432.1,
+881.4, 472.9, 921.6, 407.7, 923.8, 415.2, 923.8, 415.2, 926.0, 422.7,
+938.8, 442.6, 928.9, 449.0, 931.8, 475.4, 930.9, 463.2, 930.0, 463.3,
+928.2, 451.2, 924.9, 429.4, 923.3, 429.6, 921.6, 407.7, 903.7, 346.4,
+908.9, 364.0, 920.2, 370.0, 914.0, 381.7, 914.0, 382.1, 914.0, 382.4,
+914.3, 382.6, 916.1, 382.5, 916.7, 390.9, 919.1, 399.2, 917.7, 404.6,
+913.4, 403.5, 907.7, 407.8, 901.1, 412.8, 896.0, 419.1, 894.4, 417.8,
+884.3, 409.7, 889.3, 403.4, 884.2, 389.0, 883.6, 387.4, 882.6, 387.0,
+883.1, 385.9, 892.3, 365.7, 890.9, 348.1, 903.7, 346.4, 842.4, 342.0,
+835.8, 345.6, 835.8, 345.6, 829.3, 349.3, 794.5, 342.5, 790.2, 357.7,
+752.8, 370.9, 776.8, 355.0, 776.8, 355.0, 800.7, 339.1, 819.2, 333.1,
+823.5, 335.2, 842.4, 342.0, 800.7, 339.1, 825.7, 322.5, 823.3, 315.5,
+850.7, 305.9, 851.2, 305.7, 851.6, 305.3, 851.6, 305.3, 853.8, 304.2,
+856.0, 302.9, 856.1, 303.1, 853.5, 305.6, 856.6, 309.4, 858.0, 315.5,
+857.8, 323.7, 859.2, 323.7, 860.4, 331.9, 852.6, 330.4, 851.4, 336.9,
+842.4, 342.0, 823.5, 335.2, 819.2, 333.1, 800.7, 339.1, 847.6, 351.2,
+855.0, 348.4, 858.9, 351.0, 862.4, 345.6, 862.8, 348.1, 862.8, 348.1,
+863.2, 350.6, 856.3, 353.2, 847.8, 352.0, 847.6, 351.2, 765.4, 446.9,
+783.7, 457.2, 781.5, 461.2, 797.5, 475.5, 815.2, 491.3, 815.2, 491.3,
+832.8, 507.0, 837.8, 511.5, 837.8, 511.5, 842.9, 516.0, 848.3, 520.8,
+847.5, 522.2, 853.8, 525.7, 838.4, 515.2, 839.9, 508.5, 823.0, 504.7,
+809.3, 488.9, 785.4, 487.8, 787.2, 480.4, 767.8, 463.2, 767.8, 459.9,
+744.0, 451.0, 728.2, 440.2, 728.2, 440.2, 712.4, 429.4, 706.9, 425.7,
+706.9, 425.7, 701.5, 422.0, 700.7, 419.7, 698.9, 420.3, 696.4, 418.5,
+731.1, 432.1, 733.1, 428.7, 765.4, 446.9, 696.4, 418.5, 692.6, 416.0,
+692.6, 416.0, 688.8, 413.4, 690.9, 414.1, 693.6, 408.7, 694.7, 409.5,
+695.3, 409.6, 695.5, 409.0, 696.2, 408.5, 702.9, 402.3, 705.4, 405.0,
+714.5, 401.6, 743.6, 417.7, 763.2, 449.1, 765.4, 446.9, 733.1, 428.7,
+731.1, 432.1, 696.4, 418.5, 493.2, 122.4, 496.6, 123.8, 497.6, 121.5,
+502.0, 120.5, 507.0, 119.4, 507.6, 117.2, 512.1, 118.4, 515.0, 119.1,
+517.6, 122.8, 516.9, 124.3, 516.4, 125.4, 513.3, 124.0, 509.6, 123.7,
+501.4, 123.1, 500.4, 125.4, 493.2, 122.4, 600.5, 240.3, 575.0, 209.0,
+581.3, 186.8, 549.5, 177.8, 540.1, 159.5, 541.2, 158.9, 530.8, 141.2,
+553.1, 149.7, 548.2, 162.6, 565.6, 183.9, 571.2, 189.2, 570.5, 189.8,
+575.4, 195.8, 588.8, 217.5, 581.6, 227.2, 600.5, 240.3, 575.4, 195.8,
+604.4, 231.3, 601.5, 234.1, 633.4, 266.7, 639.4, 274.1, 639.4, 274.1,
+645.4, 281.4, 659.5, 298.6, 659.5, 298.6, 673.5, 315.8, 673.6, 315.8,
+673.6, 315.8, 673.6, 315.9, 685.1, 330.0, 687.0, 328.8, 696.7, 344.0,
+692.1, 336.9, 690.2, 338.1, 683.8, 332.1, 667.6, 314.8, 666.4, 315.9,
+649.0, 299.7, 637.5, 265.7, 624.8, 270.0, 600.5, 240.3, 581.6, 227.2,
+588.8, 217.5, 575.4, 195.8, 903.1, 280.6, 895.2, 284.4, 895.2, 284.4,
+887.3, 288.2, 883.2, 288.4, 883.0, 285.2, 878.8, 282.3, 854.6, 265.6,
+854.6, 265.6, 830.4, 249.0, 825.3, 245.5, 825.3, 245.5, 820.2, 241.9,
+794.8, 224.5, 794.8, 224.5, 769.5, 207.0, 768.4, 206.2, 768.4, 206.2,
+767.2, 205.4, 760.5, 200.8, 761.2, 199.3, 753.8, 196.2, 766.9, 201.7,
+766.2, 203.2, 778.7, 210.2, 792.9, 218.3, 792.9, 218.3, 807.2, 226.4,
+812.6, 229.4, 812.6, 229.4, 817.9, 232.4, 842.6, 246.4, 842.6, 246.4,
+867.2, 260.3, 875.7, 265.1, 875.7, 265.1, 884.2, 269.9, 892.2, 274.4,
+892.2, 274.4, 900.1, 278.9, 901.6, 279.7, 902.5, 281.0, 903.1, 280.6,
+517.7, 117.1, 520.7, 121.1, 529.1, 114.7, 540.5, 112.3, 547.4, 111.5,
+547.3, 110.8, 554.1, 109.4, 555.5, 109.5, 555.2, 111.7, 556.4, 114.1,
+567.2, 135.5, 567.2, 135.5, 578.0, 156.8, 579.1, 158.9, 579.1, 158.9,
+580.1, 161.0, 582.9, 166.5, 581.2, 168.9, 585.7, 172.0, 550.0, 147.0,
+543.3, 151.0, 517.7, 117.1, 599.6, 253.8, 603.6, 257.5, 603.6, 257.5,
+607.6, 261.2, 639.4, 295.1, 640.0, 294.7, 674.0, 326.3, 671.5, 323.8,
+670.3, 325.0, 667.7, 322.6, 653.1, 308.9, 653.6, 308.4, 639.5, 294.1,
+619.6, 274.0, 619.4, 274.2, 599.6, 253.8, 701.4, 377.5, 684.8, 385.5,
+684.8, 385.5, 668.1, 393.5, 665.9, 393.6, 665.6, 386.2, 663.1, 378.8,
+655.2, 355.4, 654.4, 331.7, 647.2, 331.9, 673.6, 331.1, 691.7, 348.8,
+701.4, 377.5, 439.9, 203.6, 440.2, 203.0, 439.1, 202.4, 438.3, 201.1,
+437.8, 193.7, 427.2, 190.5, 429.1, 186.9, 422.4, 176.3, 422.4, 176.3,
+415.7, 165.8, 412.5, 160.9, 412.5, 160.9, 409.4, 156.0, 409.6, 155.6,
+408.9, 154.9, 408.1, 154.0, 404.3, 148.1, 400.6, 148.2, 400.6, 142.2,
+404.3, 142.2, 404.3, 141.4, 408.0, 140.6, 430.6, 159.3, 436.6, 157.6,
+449.3, 182.6, 452.6, 189.1, 445.7, 193.8, 439.9, 203.6, 600.1, 84.9,
+599.4, 84.6, 599.2, 84.1, 598.7, 84.3, 616.0, 76.2, 619.2, 79.9,
+633.3, 68.0, 674.9, 71.6, 691.3, 60.0, 720.8, 26.7, 723.0, 27.1,
+724.0, 26.2, 725.3, 27.5, 726.3, 27.9, 726.7, 29.3, 727.1, 29.1,
+715.7, 35.3, 715.2, 34.3, 703.4, 39.5, 701.1, 40.5, 701.1, 40.5,
+698.9, 41.5, 662.7, 57.4, 662.7, 57.4, 626.5, 73.3, 613.3, 79.1,
+613.5, 79.6, 600.1, 84.9, 557.4, 100.7, 550.2, 105.6, 550.9, 107.4,
+543.0, 110.6, 541.7, 111.2, 541.8, 112.1, 540.5, 112.3, 529.1, 114.7,
+520.7, 121.1, 517.7, 117.1, 514.9, 117.8, 514.9, 117.8, 512.1, 118.4,
+507.6, 117.2, 507.0, 119.4, 502.0, 120.5, 503.0, 121.8, 506.6, 118.9,
+511.2, 117.2, 534.3, 109.0, 533.6, 105.7, 557.4, 100.7, 387.3, 116.0,
+362.5, 54.7, 365.8, 53.1, 337.7, -6.6, 335.7, -12.3, 336.3, -17.9,
+333.7, -18.1, 341.8, -20.2, 341.8, -20.2, 349.8, -22.3, 351.7, -22.3,
+352.8, -20.5, 353.5, -18.0, 364.2, 19.6, 363.0, 19.9, 372.6, 57.9,
+374.7, 66.1, 374.7, 66.1, 376.8, 74.3, 379.0, 83.0, 379.0, 83.0,
+381.2, 91.7, 383.3, 100.2, 383.3, 100.2, 385.5, 108.7, 386.4, 112.4,
+385.9, 112.6, 387.3, 116.0, 428.6, -42.8, 428.6, -42.8, 430.6, -43.3,
+432.7, -43.8, 451.1, -48.9, 451.2, -48.4, 469.8, -53.0, 451.2, -48.4,
+451.1, -48.9, 432.7, -43.8, 430.6, -43.3, 428.6, -42.8, 428.6, -42.8,
+215.0, 260.7, 214.8, 260.9, 214.4, 260.4, 213.8, 260.1, 214.0, 259.0,
+206.6, 257.2, 206.8, 256.7, 215.1, 227.9, 215.0, 227.9, 223.1, 199.1,
+223.1, 199.1, 232.3, 166.6, 241.5, 134.0, 245.6, 119.3, 245.6, 119.3,
+249.8, 104.6, 253.8, 90.4, 253.9, 90.4, 257.9, 76.1, 257.0, 79.3,
+257.2, 79.3, 256.4, 82.5, 248.0, 118.7, 248.0, 118.7, 239.6, 154.9,
+232.4, 185.6, 232.4, 185.6, 225.3, 216.3, 225.1, 217.0, 225.1, 217.0,
+225.0, 217.7, 220.2, 238.1, 220.2, 238.1, 215.5, 258.6, 215.2, 259.7,
+215.6, 260.1, 215.0, 260.7, 142.3, 443.2, 142.0, 441.3, 141.8, 441.3,
+141.8, 439.5, 136.9, 394.0, 135.5, 394.1, 129.3, 348.8, 129.9, 332.1,
+133.8, 320.3, 146.4, 316.0, 141.9, 317.5, 146.0, 329.6, 145.6, 343.2,
+143.9, 393.2, 144.3, 393.2, 142.3, 443.2, 192.0, 145.4, 196.4, 139.0,
+200.1, 132.2, 200.8, 132.6, 202.0, 132.5, 202.9, 131.1, 202.9, 129.5,
+229.0, 115.3, 223.0, 102.9, 240.6, 74.8, 246.6, 67.8, 245.9, 67.1,
+251.1, 59.5, 244.4, 70.9, 245.2, 71.4, 239.2, 83.3, 236.4, 88.2,
+235.7, 87.8, 232.3, 92.4, 212.1, 118.9, 212.5, 119.2, 192.0, 145.4,
+470.3, 647.3, 471.5, 660.3, 471.7, 660.3, 472.9, 673.2, 473.3, 677.4,
+473.3, 677.4, 473.7, 681.5, 473.9, 684.3, 474.0, 684.3, 474.2, 687.1,
+474.9, 695.0, 474.8, 695.0, 475.5, 702.9, 468.6, 699.9, 471.3, 693.5,
+467.1, 684.1, 466.6, 682.9, 466.6, 682.9, 466.1, 681.7, 464.4, 678.0,
+464.4, 678.0, 462.8, 674.3, 461.2, 670.9, 460.0, 671.0, 459.7, 667.4,
+454.0, 587.3, 454.4, 587.2, 448.3, 507.2, 447.9, 501.2, 448.0, 501.2,
+447.6, 495.3, 446.7, 480.6, 448.0, 480.4, 445.8, 465.9, 459.4, 556.4,
+458.5, 556.6, 470.3, 647.3, 445.8, 465.9, 444.2, 452.7, 445.1, 452.6,
+444.3, 439.3, 443.4, 424.6, 443.4, 424.6, 442.5, 409.9, 442.2, 404.5,
+442.2, 404.5, 441.8, 399.1, 440.9, 383.7, 440.9, 383.7, 440.0, 368.4,
+440.0, 367.4, 440.0, 367.4, 439.9, 366.4, 436.0, 301.4, 432.8, 301.4,
+432.1, 236.3, 432.5, 271.5, 435.8, 271.5, 439.3, 306.7, 441.3, 329.1,
+441.3, 329.1, 443.4, 351.4, 443.9, 357.8, 443.9, 357.8, 444.5, 364.1,
+444.8, 366.7, 444.8, 366.7, 445.0, 369.4, 446.9, 389.4, 446.9, 389.4,
+448.7, 409.5, 452.1, 447.0, 452.1, 447.0, 455.6, 484.5, 456.5, 494.1,
+456.5, 494.1, 457.3, 503.6, 461.1, 544.5, 461.1, 544.5, 464.8, 585.3,
+466.5, 603.5, 466.6, 603.5, 468.2, 621.6, 469.3, 634.5, 469.2, 634.5,
+470.3, 647.3, 458.5, 556.6, 459.4, 556.4, 445.8, 465.9, 465.1, 743.6,
+462.4, 705.5, 462.4, 705.5, 459.7, 667.4, 460.0, 671.0, 461.2, 670.9,
+462.8, 674.3, 463.8, 680.1, 463.0, 680.2, 463.2, 686.2, 464.1, 714.9,
+459.9, 715.7, 465.1, 743.6, 519.2, 392.8, 519.8, 404.6, 512.4, 405.1,
+505.5, 417.3, 505.6, 417.1, 505.4, 417.0, 505.5, 416.9, 512.2, 404.8,
+519.7, 404.5, 519.2, 392.8, 210.2, 770.6, 213.3, 782.9, 209.2, 783.9,
+208.2, 797.2, 205.3, 838.2, 215.2, 875.8, 202.3, 879.2, 197.0, 840.4,
+213.6, 817.8, 191.6, 801.6, 193.4, 790.8, 183.6, 784.8, 188.5, 778.9,
+187.3, 770.3, 187.3, 770.3, 186.1, 761.7, 185.4, 756.6, 185.4, 756.6,
+184.7, 751.5, 184.6, 751.1, 184.7, 751.0, 184.6, 750.6, 192.1, 756.1,
+191.9, 756.3, 199.2, 762.1, 204.7, 766.3, 208.8, 765.3, 210.2, 770.6,
+156.0, 542.8, 153.2, 522.1, 153.2, 522.1, 150.3, 501.5, 169.4, 477.1,
+168.7, 476.5, 187.1, 451.5, 174.1, 457.5, 180.4, 471.0, 173.8, 490.6,
+173.3, 491.8, 173.3, 491.8, 172.9, 493.1, 172.9, 493.2, 172.9, 493.2,
+172.8, 493.4, 170.6, 500.0, 170.6, 500.0, 168.3, 506.7, 165.8, 514.0,
+165.8, 514.0, 163.3, 521.4, 159.6, 532.1, 152.2, 536.9, 156.0, 542.8,
+224.3, 529.2, 225.0, 523.0, 227.6, 517.0, 228.5, 517.2, 229.2, 517.3,
+228.1, 523.5, 227.6, 529.8, 227.1, 537.3, 227.1, 537.3, 226.5, 544.9,
+223.5, 586.4, 225.4, 586.6, 220.5, 627.8, 217.3, 628.6, 218.1, 631.9,
+215.7, 636.0, 217.8, 582.5, 218.6, 582.4, 224.3, 529.2, 348.9, 883.3,
+337.1, 885.5, 330.9, 889.1, 324.4, 882.9, 320.9, 879.5, 326.7, 873.5,
+329.0, 864.1, 329.5, 862.2, 329.5, 862.2, 329.9, 860.4, 336.9, 831.8,
+354.5, 813.4, 343.8, 803.1, 363.8, 822.4, 346.2, 840.7, 348.6, 878.3,
+348.7, 880.8, 349.2, 883.2, 348.9, 883.3, 415.7, 165.8, 422.4, 176.3,
+422.4, 176.3, 429.1, 186.9, 423.5, 197.5, 429.9, 201.0, 430.8, 215.1,
+431.2, 225.7, 431.1, 225.7, 432.1, 236.3, 432.8, 301.4, 436.0, 301.4,
+439.9, 366.4, 439.3, 344.4, 437.2, 344.4, 434.6, 322.4, 429.7, 281.8,
+429.7, 281.8, 424.8, 241.2, 422.1, 218.7, 422.1, 218.7, 419.4, 196.3,
+417.8, 183.6, 417.8, 183.6, 416.3, 171.0, 416.0, 168.4, 417.1, 167.5,
+415.7, 165.8, 101.2, 144.6, 99.2, 144.7, 92.1, 120.2, 94.7, 97.2,
+103.9, 91.9, 101.6, 87.9, 108.5, 78.6, 110.8, 77.7, 113.0, 83.2,
+117.6, 87.9, 121.3, 91.7, 125.8, 93.0, 125.1, 95.6, 117.7, 121.4,
+114.4, 143.8, 101.2, 144.6, 125.1, 95.6, 124.6, 96.2, 129.7, 98.4,
+131.9, 102.5, 136.9, 115.6, 133.5, 119.1, 142.6, 128.4, 137.9, 130.9,
+134.7, 129.7, 133.2, 133.5, 117.6, 142.0, 118.6, 144.8, 102.0, 150.6,
+102.6, 150.4, 100.2, 147.0, 101.2, 144.6, 114.4, 143.8, 117.7, 121.4,
+125.1, 95.6, 117.6, 87.9, 113.0, 83.2, 110.8, 77.7, 108.5, 78.6,
+120.5, 62.4, 130.6, 63.5, 132.6, 46.2, 136.3, 47.5, 137.3, 46.4,
+140.0, 48.9, 141.8, 49.5, 141.8, 49.5, 143.5, 50.2, 158.4, 55.7,
+158.7, 55.1, 173.3, 61.2, 173.1, 61.1, 172.9, 61.9, 172.3, 62.2,
+145.1, 75.3, 146.4, 80.5, 117.6, 87.9, 172.3, 62.2, 164.6, 64.4,
+165.9, 69.1, 159.4, 76.0, 146.4, 90.0, 147.1, 90.7, 133.3, 103.9,
+132.6, 103.2, 132.2, 103.4, 131.9, 102.5, 129.7, 98.4, 124.6, 96.2,
+125.1, 95.6, 125.8, 93.0, 121.3, 91.7, 117.6, 87.9, 146.4, 80.5,
+145.1, 75.3, 172.3, 62.2, 135.1, 105.7, 134.2, 104.8, 134.2, 104.8,
+133.3, 103.9, 147.1, 90.7, 146.4, 90.0, 159.4, 76.0, 160.9, 77.5,
+148.2, 91.7, 135.1, 105.7, 143.5, 50.2, 141.8, 49.5, 141.8, 49.5,
+140.0, 48.9, 132.1, 41.6, 132.0, 40.2, 122.0, 36.6, 158.4, 27.2,
+158.4, 27.2, 194.7, 17.8, 195.2, 17.7, 195.2, 17.7, 195.7, 17.5,
+195.6, 18.0, 195.1, 17.9, 194.6, 18.2, 169.0, 34.2, 169.7, 35.4,
+143.5, 50.2, 210.8, 66.8, 207.8, 66.4, 207.8, 66.4, 204.7, 66.0,
+205.6, 39.5, 199.5, 33.8, 212.6, 13.1, 227.9, 9.2, 227.9, 9.2,
+243.2, 5.2, 235.9, 15.3, 241.7, 19.5, 240.1, 33.8, 230.1, 52.8,
+227.1, 52.1, 210.8, 66.8, 240.1, 33.8, 239.6, 38.3, 241.4, 39.6,
+239.1, 42.7, 234.1, 55.0, 237.6, 56.4, 236.1, 70.1, 225.0, 65.1,
+223.5, 68.4, 210.8, 66.8, 227.1, 52.1, 230.1, 52.8, 240.1, 33.8,
+169.9, 145.9, 171.0, 158.5, 171.0, 158.5, 172.1, 171.1, 170.6, 173.2,
+170.5, 173.8, 170.7, 176.4, 169.6, 178.8, 169.5, 178.8, 167.7, 180.7,
+135.3, 199.1, 139.8, 207.9, 113.9, 236.9, 114.0, 236.7, 113.7, 236.5,
+113.8, 236.3, 141.7, 191.0, 135.0, 185.0, 169.9, 145.9, 142.6, 822.9,
+142.5, 811.8, 142.5, 811.8, 142.5, 800.7, 139.1, 739.4, 143.1, 739.2,
+143.6, 677.7, 143.1, 739.2, 139.1, 739.4, 142.5, 800.7, 142.5, 811.8,
+142.5, 811.8, 142.6, 822.9, 100.4, 664.5, 99.7, 666.1, 98.9, 666.1,
+99.0, 667.8, 96.8, 655.3, 96.8, 655.3, 94.7, 642.9, 94.4, 641.6,
+94.8, 641.5, 95.0, 640.0, 94.9, 629.7, 97.5, 629.7, 100.1, 619.4,
+104.9, 640.9, 100.9, 641.9, 100.4, 664.5, 107.3, 369.1, 122.8, 412.0,
+109.9, 423.8, 138.4, 454.9, 138.8, 458.8, 138.1, 459.0, 139.2, 462.7,
+139.3, 461.7, 138.0, 461.7, 137.6, 460.5, 133.2, 448.6, 133.6, 448.4,
+129.6, 436.3, 118.4, 402.7, 122.3, 400.9, 107.3, 369.1, 38.1, 318.9,
+37.1, 313.2, 37.1, 313.2, 36.1, 307.5, 41.0, 287.6, 38.8, 286.4,
+37.6, 265.6, 36.7, 258.7, 37.8, 258.5, 38.1, 251.5, 37.7, 250.5,
+55.0, 249.8, 60.9, 240.9, 64.0, 249.4, 64.0, 249.4, 67.0, 257.9,
+67.4, 260.4, 65.7, 260.7, 64.4, 263.4, 62.8, 266.8, 62.8, 266.8,
+61.2, 270.1, 49.7, 294.5, 53.0, 296.7, 38.1, 318.9, 75.3, 375.5,
+75.1, 375.7, 76.4, 376.3, 76.5, 377.0, 76.7, 393.4, 76.1, 393.5,
+75.8, 409.9, 69.3, 428.3, 72.7, 430.8, 75.1, 451.0, 74.5, 479.7,
+74.5, 479.7, 74.0, 508.4, 74.0, 509.4, 73.7, 509.4, 73.9, 510.3,
+72.6, 499.3, 71.7, 499.4, 69.5, 488.4, 64.4, 448.5, 62.0, 448.8,
+54.6, 409.2, 55.5, 391.8, 64.2, 391.8, 75.3, 375.5, 54.6, 409.2,
+49.6, 382.7, 49.8, 382.7, 44.6, 356.3, 44.4, 354.9, 44.9, 354.0,
+44.2, 353.6, 59.7, 362.8, 59.2, 363.6, 74.1, 373.8, 74.2, 373.9,
+74.1, 374.0, 74.3, 374.1, 74.8, 374.8, 75.5, 375.2, 75.3, 375.5,
+64.2, 391.8, 55.5, 391.8, 54.6, 409.2, 124.4, 503.4, 112.1, 498.8,
+111.2, 494.5, 104.4, 482.2, 106.0, 456.0, 93.7, 455.2, 83.1, 428.3,
+106.5, 437.0, 99.0, 457.2, 114.9, 486.1, 119.6, 494.7, 117.4, 500.8,
+124.4, 503.4, 81.3, 457.6, 92.8, 469.9, 105.1, 470.7, 104.4, 482.2,
+111.2, 494.5, 112.1, 498.8, 124.4, 503.4, 124.6, 503.6, 124.8, 503.8,
+124.8, 503.8, 118.5, 510.7, 114.8, 518.5, 111.8, 517.1, 102.1, 512.9,
+105.6, 505.0, 99.3, 492.8, 90.3, 475.2, 76.1, 468.4, 81.3, 457.6,
+96.7, 598.9, 93.4, 608.1, 90.6, 608.0, 90.2, 617.3, 87.2, 600.2,
+88.1, 600.0, 84.2, 583.1, 83.7, 575.4, 82.9, 575.5, 81.6, 567.8,
+92.0, 581.1, 93.8, 582.6, 96.7, 598.9, 10.2, 469.4, 10.1, 481.1,
+10.1, 481.1, 9.9, 492.8, -6.0, 452.8, -7.8, 453.5, -25.5, 414.1,
+-25.9, 441.5, -3.1, 439.7, 10.2, 469.4, 22.9, 401.7, 23.1, 435.6,
+23.1, 435.6, 23.2, 469.6, 10.5, 436.0, 8.9, 436.6, -5.3, 403.6,
+-5.3, 402.7, 21.1, 397.3, 22.9, 401.7, 16.6, 341.0, 16.6, 347.1,
+16.6, 347.1, 16.7, 353.2, 12.1, 349.8, 1.7, 353.8, 1.7, 354.3,
+-2.9, 353.3, -5.9, 347.4, -6.7, 348.2, 1.5, 340.8, 6.2, 338.8,
+16.6, 341.0, 41.7, 339.3, 42.9, 346.4, 40.0, 349.1, 44.2, 353.6,
+44.9, 354.0, 44.4, 354.9, 44.6, 356.3, 46.6, 366.3, 46.4, 366.3,
+48.2, 376.4, 36.8, 416.2, 34.9, 416.1, 28.1, 456.8, 27.0, 439.3,
+27.0, 439.3, 25.8, 421.8, 20.2, 411.2, 26.2, 408.0, 26.7, 394.2,
+26.7, 393.2, 26.7, 393.2, 26.8, 392.2, 34.0, 365.7, 30.5, 364.1,
+41.7, 339.3, 26.8, 392.2, 28.7, 347.1, 29.3, 347.1, 30.7, 302.0,
+30.7, 301.8, 30.7, 301.8, 30.7, 301.5, 32.8, 313.5, 34.8, 313.2,
+39.1, 324.7, 39.4, 325.6, 39.3, 325.6, 39.5, 326.5, 43.2, 329.6,
+40.6, 332.9, 41.7, 339.3, 30.5, 364.1, 34.0, 365.7, 26.8, 392.2,
+55.9, 700.7, 49.6, 692.7, 49.6, 692.7, 43.3, 684.7, 51.2, 675.0,
+40.9, 659.4, 28.8, 640.3, 35.6, 641.6, 35.6, 641.6, 42.5, 643.0,
+49.8, 654.0, 45.7, 656.7, 48.9, 670.5, 49.3, 672.3, 49.3, 672.3,
+49.7, 674.1, 52.8, 687.4, 61.0, 692.4, 55.9, 700.7, 782.5, 583.1,
+784.1, 591.8, 781.5, 601.4, 777.5, 602.0, 768.9, 594.4, 768.9, 594.4,
+760.4, 586.8, 759.7, 586.2, 760.7, 585.0, 760.1, 584.4, 759.3, 578.0,
+759.3, 578.0, 758.5, 571.7, 756.7, 556.1, 756.7, 556.1, 754.8, 540.6,
+751.3, 539.8, 756.0, 528.8, 752.2, 518.9, 751.4, 512.5, 751.4, 512.5,
+750.7, 506.1, 750.4, 503.8, 751.0, 503.5, 750.1, 501.5, 758.6, 521.0,
+758.0, 521.3, 765.8, 541.1, 774.1, 562.1, 778.3, 561.3, 782.5, 583.1,
+797.6, 609.5, 797.4, 609.5, 797.2, 611.1, 796.8, 612.7, 796.7, 613.2,
+796.7, 613.2, 796.5, 613.7, 796.0, 615.9, 795.4, 618.0, 795.5, 618.0,
+792.8, 613.9, 791.7, 614.6, 787.9, 611.3, 788.4, 609.3, 793.0, 608.8,
+797.6, 609.5, 741.2, 516.5, 748.0, 528.5, 745.8, 538.6, 754.8, 540.6,
+756.7, 556.1, 756.7, 556.1, 758.5, 571.7, 744.7, 547.0, 744.6, 544.7,
+741.2, 516.5, 712.4, 429.4, 728.2, 440.2, 728.2, 440.2, 744.0, 451.0,
+747.0, 452.1, 744.9, 457.9, 745.7, 464.8, 726.1, 451.4, 714.7, 448.8,
+712.4, 429.4, 790.4, 654.6, 788.9, 653.1, 787.0, 652.4, 787.3, 651.6,
+788.2, 647.9, 787.8, 644.4, 789.1, 644.2, 795.2, 639.9, 791.5, 634.7,
+793.8, 625.1, 795.5, 639.4, 793.9, 640.3, 790.4, 654.6, 864.8, 595.8,
+886.2, 616.7, 887.3, 615.6, 907.5, 637.7, 906.2, 638.7, 906.2, 638.7,
+904.9, 639.8, 903.8, 641.2, 900.4, 638.7, 895.8, 637.6, 878.7, 618.8,
+880.6, 617.1, 865.4, 596.6, 865.0, 596.2, 865.1, 596.1, 864.8, 595.8,
+867.3, 550.9, 874.2, 547.6, 877.6, 549.8, 881.1, 544.3, 882.5, 544.0,
+882.9, 545.5, 884.7, 546.7, 889.0, 549.7, 889.0, 549.7, 893.3, 552.6,
+894.4, 554.0, 894.9, 553.7, 896.4, 554.7, 898.2, 559.5, 897.2, 559.8,
+897.9, 564.9, 897.9, 565.1, 897.9, 565.1, 897.9, 565.3, 887.3, 560.5,
+887.4, 560.3, 876.8, 555.4, 872.1, 553.1, 867.2, 551.2, 867.3, 550.9,
+944.3, 587.3, 944.5, 587.5, 944.7, 587.5, 944.7, 587.6, 944.5, 594.2,
+941.1, 599.0, 944.0, 600.9, 941.0, 598.8, 944.0, 594.1, 944.3, 587.3,
+699.3, 568.1, 694.8, 574.1, 694.8, 574.1, 690.4, 580.0, 689.6, 578.7,
+683.9, 579.2, 682.0, 581.7, 676.7, 578.5, 678.1, 576.1, 674.6, 570.2,
+674.6, 570.3, 674.8, 570.2, 675.1, 570.2, 678.1, 569.9, 678.1, 569.9,
+681.0, 569.7, 683.3, 569.5, 683.3, 569.5, 685.5, 569.3, 689.4, 569.0,
+689.4, 569.0, 693.3, 568.6, 696.3, 568.4, 699.0, 569.1, 699.3, 568.1,
+902.2, 454.4, 901.6, 452.4, 906.3, 450.0, 905.6, 449.0, 911.4, 456.8,
+909.0, 458.6, 912.4, 468.1, 913.8, 472.2, 916.2, 472.8, 915.3, 476.2,
+915.9, 473.9, 913.5, 473.3, 911.8, 470.4, 907.0, 462.4, 904.7, 463.1,
+902.2, 454.4, 904.5, 446.0, 904.0, 446.7, 905.9, 447.7, 905.6, 449.0,
+906.3, 450.0, 901.6, 452.4, 902.2, 454.4, 895.7, 464.8, 895.7, 464.8,
+889.2, 475.3, 886.7, 479.4, 886.2, 479.1, 884.2, 483.4, 888.4, 474.4,
+888.9, 474.6, 893.7, 465.8, 899.1, 455.9, 898.0, 455.1, 904.5, 446.0,
+928.2, 451.2, 930.0, 463.3, 930.9, 463.2, 931.8, 475.4, 932.5, 482.0,
+932.5, 482.0, 933.3, 488.7, 930.5, 470.0, 930.2, 470.0, 928.2, 451.2,
+903.2, 344.7, 903.5, 345.5, 903.5, 345.5, 903.7, 346.4, 890.9, 348.1,
+892.3, 365.7, 883.1, 385.9, 878.4, 386.3, 876.6, 367.7, 870.1, 349.6,
+868.2, 335.8, 865.3, 336.2, 860.5, 322.8, 870.9, 319.1, 869.6, 315.5,
+878.7, 308.3, 895.8, 317.0, 891.2, 326.3, 903.2, 344.7, 878.7, 308.3,
+884.3, 303.7, 887.6, 305.1, 890.0, 299.2, 896.6, 321.9, 896.6, 321.9,
+903.2, 344.7, 891.2, 326.3, 895.8, 317.0, 878.7, 308.3, 881.9, 476.0,
+884.0, 479.6, 882.6, 480.5, 883.2, 484.9, 883.7, 488.2, 882.5, 489.1,
+884.2, 491.5, 852.8, 448.7, 850.1, 450.0, 823.9, 404.0, 822.2, 402.7,
+825.8, 398.3, 827.8, 392.5, 836.4, 396.3, 833.9, 401.9, 840.0, 411.3,
+860.9, 443.7, 862.4, 442.8, 881.9, 476.0, 818.3, 452.4, 792.2, 417.7,
+787.3, 419.8, 768.3, 381.2, 774.6, 378.8, 775.0, 379.5, 781.0, 376.4,
+782.5, 375.6, 782.6, 375.8, 784.2, 375.2, 788.2, 380.2, 786.9, 381.2,
+789.5, 387.2, 790.0, 388.3, 790.0, 388.3, 790.5, 389.3, 801.8, 415.0,
+801.8, 415.0, 813.2, 440.7, 815.7, 446.5, 814.6, 447.4, 818.3, 452.4,
+466.5, 128.1, 478.5, 122.4, 479.7, 123.8, 493.2, 122.4, 500.4, 125.4,
+501.4, 123.1, 509.6, 123.7, 499.2, 128.7, 497.4, 124.9, 485.3, 126.2,
+475.9, 127.1, 474.5, 124.3, 466.5, 128.1, 585.0, 128.1, 613.5, 142.1,
+613.5, 142.1, 641.9, 156.2, 676.6, 173.2, 676.6, 173.2, 711.3, 190.3,
+757.7, 213.1, 757.7, 213.1, 804.0, 236.0, 817.2, 242.5, 818.0, 241.3,
+830.4, 249.0, 854.6, 265.6, 854.6, 265.6, 878.8, 282.3, 875.3, 279.8,
+874.9, 280.3, 871.1, 278.3, 836.2, 260.0, 836.2, 260.0, 801.3, 241.7,
+762.4, 221.2, 762.4, 221.2, 723.5, 200.8, 706.2, 191.7, 706.2, 191.7,
+689.0, 182.7, 673.1, 174.3, 673.1, 174.3, 657.2, 166.0, 632.0, 152.8,
+632.0, 152.8, 606.8, 139.6, 596.9, 134.4, 596.9, 134.4, 587.0, 129.2,
+586.0, 128.7, 586.0, 128.6, 585.0, 128.1, 641.9, 156.2, 613.0, 150.9,
+614.5, 142.7, 587.0, 129.2, 586.0, 128.7, 586.0, 128.6, 585.0, 128.1,
+580.8, 126.0, 580.8, 126.0, 576.5, 124.0, 566.5, 119.0, 565.8, 120.1,
+556.4, 114.1, 555.2, 111.7, 555.5, 109.5, 554.1, 109.4, 561.4, 107.8,
+561.4, 107.8, 568.7, 106.2, 569.6, 106.1, 569.7, 106.9, 570.7, 107.6,
+589.3, 120.3, 589.3, 120.3, 607.9, 133.0, 624.9, 144.6, 623.4, 152.8,
+641.9, 156.2, 590.7, 210.4, 578.2, 197.1, 578.8, 196.4, 565.6, 183.9,
+548.2, 162.6, 553.1, 149.7, 530.8, 141.2, 526.6, 134.1, 522.8, 135.7,
+520.2, 128.3, 518.5, 126.3, 518.5, 126.3, 516.9, 124.3, 517.6, 122.8,
+515.0, 119.1, 512.1, 118.4, 514.9, 117.8, 514.9, 117.8, 517.7, 117.1,
+543.3, 151.0, 550.0, 147.0, 585.7, 172.0, 592.1, 178.0, 592.9, 180.0,
+594.1, 188.7, 595.5, 199.2, 590.6, 210.4, 590.7, 210.4, 594.1, 188.7,
+611.0, 203.2, 605.0, 210.3, 615.8, 231.9, 618.6, 237.3, 617.0, 239.1,
+621.3, 242.7, 607.9, 225.1, 606.0, 226.5, 590.7, 210.4, 590.6, 210.4,
+595.5, 199.2, 594.1, 188.7, 639.5, 294.1, 653.6, 308.4, 653.1, 308.9,
+667.7, 322.6, 668.5, 315.8, 652.9, 313.9, 638.2, 305.2, 631.6, 301.3,
+631.6, 301.3, 625.1, 297.5, 617.2, 292.8, 617.2, 292.8, 609.4, 288.2,
+606.9, 286.8, 606.9, 286.8, 604.4, 285.3, 563.4, 261.1, 563.4, 261.1,
+522.4, 236.9, 515.1, 232.6, 515.4, 231.9, 507.7, 228.3, 542.5, 244.8,
+542.1, 245.5, 576.5, 262.7, 583.2, 266.0, 583.2, 266.0, 589.9, 269.3,
+614.7, 281.7, 616.9, 278.6, 639.5, 294.1, 645.5, 326.8, 643.2, 325.5,
+644.4, 323.5, 643.3, 320.2, 640.7, 312.7, 641.4, 305.5, 638.2, 305.2,
+652.9, 313.9, 668.5, 315.8, 667.7, 322.6, 670.3, 325.0, 671.5, 323.8,
+674.0, 326.3, 678.1, 330.1, 680.1, 328.1, 683.8, 332.1, 690.2, 338.1,
+692.1, 336.9, 696.7, 344.0, 707.9, 354.5, 705.9, 360.5, 719.2, 365.0,
+680.3, 351.9, 681.2, 347.8, 645.5, 326.8, 719.2, 365.0, 720.8, 365.9,
+721.7, 367.9, 722.0, 367.6, 711.7, 372.6, 711.7, 372.6, 701.4, 377.5,
+691.7, 348.8, 673.6, 331.1, 647.2, 331.9, 645.5, 330.3, 647.2, 327.6,
+645.5, 326.8, 681.2, 347.8, 680.3, 351.9, 719.2, 365.0, 557.8, 293.0,
+554.0, 281.8, 548.5, 283.7, 539.3, 274.3, 538.9, 273.8, 538.9, 273.7,
+538.5, 273.3, 507.0, 241.3, 500.5, 245.8, 475.5, 209.3, 485.1, 223.3,
+491.6, 218.8, 507.7, 228.3, 515.4, 231.9, 515.1, 232.6, 522.4, 236.9,
+528.3, 242.6, 526.9, 244.1, 531.4, 251.2, 544.6, 272.1, 550.1, 270.2,
+557.8, 293.0, 642.2, 378.7, 633.8, 370.2, 634.8, 368.9, 625.4, 361.6,
+617.9, 362.6, 616.6, 352.7, 607.8, 343.7, 603.3, 334.3, 607.1, 332.5,
+606.5, 321.3, 605.9, 310.6, 602.1, 301.3, 605.3, 300.0, 605.8, 299.8,
+609.5, 309.1, 613.8, 318.3, 628.0, 348.5, 633.7, 347.0, 642.2, 378.7,
+539.3, 274.3, 568.9, 309.5, 571.0, 308.0, 598.4, 345.0, 592.2, 341.2,
+592.2, 341.2, 586.0, 337.3, 562.7, 309.6, 564.4, 308.2, 542.8, 279.0,
+542.2, 278.3, 542.2, 278.2, 541.6, 277.4, 540.5, 275.8, 540.5, 275.8,
+539.3, 274.3, 752.7, 51.9, 761.8, 53.9, 768.8, 63.8, 769.2, 63.4,
+761.8, 72.8, 753.9, 66.6, 738.7, 69.9, 734.5, 70.8, 731.7, 69.2,
+730.2, 71.7, 735.2, 63.3, 738.0, 64.9, 745.7, 58.1, 749.2, 55.0,
+750.0, 51.3, 752.7, 51.9, 646.9, 36.3, 643.1, 40.5, 643.9, 42.0,
+639.3, 44.6, 605.0, 68.1, 605.0, 68.1, 570.6, 91.6, 571.5, 91.2,
+568.6, 86.9, 569.6, 86.2, 606.8, 59.2, 607.4, 59.9, 646.9, 36.3,
+570.6, -20.1, 571.5, -18.3, 569.8, -17.4, 568.9, -14.7, 563.3, 3.6,
+558.4, 22.2, 557.7, 22.0, 548.9, 19.7, 553.8, 1.1, 549.9, -19.7,
+557.9, -30.8, 554.3, -47.9, 542.7, -58.6, 542.5, -60.0, 545.4, -61.3,
+545.1, -61.8, 559.4, -42.1, 559.6, -41.9, 570.6, -20.1, 545.1, -61.8,
+544.5, -62.9, 546.5, -63.9, 548.0, -65.9, 548.5, -66.6, 548.5, -66.6,
+549.1, -67.4, 551.3, -70.5, 551.3, -70.5, 553.5, -73.6, 555.1, -74.3,
+554.4, -76.4, 555.8, -76.8, 573.0, -82.6, 573.8, -87.4, 590.9, -86.2,
+589.5, -86.3, 589.1, -80.4, 587.3, -74.6, 582.2, -57.8, 582.2, -57.8,
+577.0, -41.0, 573.8, -30.5, 568.1, -21.8, 570.6, -20.1, 559.6, -41.9,
+559.4, -42.1, 545.1, -61.8, 372.6, 57.9, 363.0, 19.9, 364.2, 19.6,
+353.5, -18.0, 364.4, -22.7, 397.1, 8.3, 406.0, 43.4, 406.7, 46.2,
+376.1, 61.9, 372.6, 57.9, 413.0, -8.1, 406.0, -8.8, 406.1, -14.5,
+400.1, -21.7, 400.6, -21.2, 401.0, -21.6, 402.0, -21.5, 407.8, -20.9,
+407.8, -20.9, 413.6, -20.3, 432.0, -18.4, 432.0, -18.4, 450.5, -16.5,
+460.8, -15.4, 461.5, -12.2, 471.0, -14.3, 442.7, -8.0, 441.4, -5.2,
+413.0, -8.1, 376.9, -24.1, 383.0, -17.7, 390.0, -26.2, 400.1, -21.7,
+406.1, -14.5, 406.0, -8.8, 413.0, -8.1, 426.1, 5.7, 426.1, 5.7,
+439.2, 19.5, 452.4, 33.4, 452.6, 33.3, 465.6, 47.3, 464.7, 46.3,
+464.5, 46.4, 463.4, 45.6, 462.7, 45.0, 462.7, 45.0, 462.0, 44.4,
+419.5, 10.2, 413.9, 15.4, 376.9, -24.1, 543.4, -71.1, 548.5, -72.3,
+548.9, -71.4, 553.5, -73.6, 551.3, -70.5, 551.3, -70.5, 549.1, -67.4,
+546.8, -66.8, 543.5, -71.2, 543.4, -71.1, 198.8, 306.6, 206.3, 283.4,
+210.0, 283.9, 213.8, 260.1, 214.4, 260.4, 214.8, 260.9, 215.0, 260.7,
+220.4, 263.4, 226.1, 264.6, 225.8, 266.0, 222.7, 280.5, 217.0, 279.3,
+208.2, 292.5, 203.5, 299.5, 200.1, 299.0, 198.8, 306.6, 147.1, 478.3,
+146.6, 474.8, 146.6, 474.8, 146.2, 471.3, 146.7, 466.2, 147.4, 466.2,
+149.0, 461.3, 169.4, 397.4, 166.0, 396.1, 190.1, 333.6, 191.7, 329.4,
+197.8, 326.4, 200.5, 328.0, 201.3, 328.4, 198.8, 332.8, 197.1, 337.6,
+193.8, 346.9, 193.8, 346.9, 190.4, 356.3, 186.4, 367.7, 186.4, 367.7,
+182.4, 379.0, 167.7, 420.4, 167.8, 420.5, 153.1, 461.8, 153.0, 462.3,
+152.8, 462.2, 152.7, 462.6, 150.9, 467.6, 150.9, 467.6, 149.2, 472.6,
+148.1, 475.5, 146.9, 478.2, 147.1, 478.3, 154.3, 442.2, 152.4, 449.0,
+150.5, 455.9, 150.5, 455.9, 149.7, 458.6, 149.0, 461.3, 149.0, 461.3,
+147.4, 466.2, 146.7, 466.2, 146.2, 471.3, 144.2, 457.2, 144.2, 457.2,
+142.3, 443.2, 144.3, 393.2, 143.9, 393.2, 145.6, 343.2, 145.5, 345.7,
+145.8, 345.7, 146.0, 348.1, 147.8, 368.2, 147.8, 368.2, 149.5, 388.3,
+149.9, 392.5, 149.9, 392.5, 150.3, 396.7, 152.3, 419.4, 154.2, 419.4,
+154.3, 442.2, 179.6, 252.5, 179.3, 254.6, 178.6, 254.5, 177.5, 256.5,
+176.7, 258.1, 176.2, 259.8, 175.9, 259.7, 165.2, 256.7, 165.2, 255.9,
+155.5, 250.4, 156.0, 249.5, 156.0, 249.5, 156.5, 248.6, 168.7, 225.0,
+162.9, 207.3, 180.9, 201.5, 174.6, 203.5, 180.4, 221.2, 179.9, 241.0,
+179.8, 246.7, 180.5, 246.8, 179.6, 252.5, 158.1, 293.8, 157.1, 294.1,
+157.4, 294.9, 156.8, 296.1, 151.6, 306.0, 151.6, 306.0, 146.4, 316.0,
+133.8, 320.3, 129.9, 332.1, 129.3, 348.8, 126.7, 329.9, 126.7, 329.9,
+124.1, 311.0, 124.0, 309.1, 124.5, 307.9, 126.2, 307.0, 141.5, 299.2,
+141.8, 299.5, 158.1, 293.8, 126.2, 307.0, 128.3, 307.6, 131.2, 297.3,
+136.3, 287.5, 137.6, 285.0, 137.6, 285.0, 138.9, 282.6, 138.9, 282.6,
+147.2, 266.5, 155.5, 250.4, 165.2, 255.9, 165.2, 256.7, 175.9, 259.7,
+178.1, 263.9, 172.0, 267.1, 168.1, 274.5, 163.1, 284.1, 165.8, 287.7,
+158.1, 293.8, 141.8, 299.5, 141.5, 299.2, 126.2, 307.0, 136.3, 287.5,
+131.2, 297.3, 128.3, 307.6, 126.2, 307.0, 124.5, 307.9, 124.0, 309.1,
+124.1, 311.0, 121.5, 292.0, 120.5, 292.1, 118.9, 272.9, 118.7, 271.9,
+118.7, 271.9, 118.6, 270.9, 117.5, 263.1, 114.0, 261.7, 116.4, 255.3,
+129.2, 247.8, 136.6, 240.3, 134.9, 228.4, 137.5, 226.5, 136.8, 225.6,
+138.8, 222.8, 141.1, 225.7, 138.6, 227.7, 138.4, 232.7, 137.5, 255.7,
+137.5, 255.7, 136.6, 278.7, 136.5, 283.1, 137.7, 283.6, 136.3, 287.5,
+467.1, 684.1, 471.3, 693.5, 468.6, 699.9, 475.5, 702.9, 476.7, 715.9,
+476.8, 715.9, 478.0, 728.9, 480.1, 750.5, 480.0, 750.5, 482.0, 772.1,
+482.3, 775.1, 482.3, 775.1, 482.5, 778.1, 483.9, 793.3, 485.3, 808.6,
+485.3, 808.6, 486.4, 819.9, 486.4, 819.9, 487.4, 831.1, 488.3, 840.2,
+487.8, 840.3, 489.1, 849.3, 483.2, 808.5, 483.6, 808.5, 478.2, 767.6,
+477.9, 765.3, 477.9, 765.2, 477.6, 762.9, 474.2, 737.2, 474.2, 737.2,
+470.8, 711.5, 469.0, 697.8, 461.8, 693.8, 467.1, 684.1, 537.5, 359.8,
+537.4, 359.8, 528.4, 376.3, 519.2, 392.8, 519.7, 404.5, 512.2, 404.8,
+505.5, 416.9, 507.3, 414.7, 505.3, 413.0, 505.1, 409.1, 504.5, 396.8,
+498.4, 392.8, 503.9, 384.4, 514.7, 368.1, 529.8, 355.6, 537.5, 359.8,
+503.9, 384.4, 505.6, 383.9, 503.6, 377.8, 503.3, 371.1, 503.1, 368.1,
+503.1, 368.1, 503.0, 365.1, 501.6, 337.8, 501.6, 337.8, 500.3, 310.6,
+500.0, 303.7, 503.1, 301.2, 499.6, 296.9, 519.1, 327.4, 519.1, 327.4,
+538.6, 357.9, 538.9, 358.3, 538.4, 359.2, 537.5, 359.8, 529.8, 355.6,
+514.7, 368.1, 503.9, 384.4, 481.0, 448.9, 480.0, 450.8, 484.4, 452.5,
+484.1, 455.6, 483.6, 460.0, 481.8, 459.8, 479.4, 464.0, 471.6, 478.1,
+471.6, 478.1, 463.8, 492.1, 463.8, 493.9, 462.7, 493.9, 461.7, 495.7,
+471.1, 472.2, 468.8, 470.9, 481.0, 448.9, 505.1, 409.1, 505.3, 413.0,
+507.3, 414.7, 505.5, 416.9, 505.4, 417.0, 505.6, 417.1, 505.5, 417.3,
+495.2, 435.9, 495.2, 435.8, 484.8, 454.4, 484.4, 455.0, 483.9, 455.4,
+484.1, 455.6, 484.4, 452.5, 480.0, 450.8, 481.0, 448.9, 480.5, 447.8,
+480.5, 447.8, 480.0, 446.7, 467.1, 418.0, 463.1, 419.2, 454.1, 389.4,
+455.0, 392.5, 459.0, 391.3, 463.9, 393.2, 484.5, 401.1, 486.1, 398.3,
+505.1, 409.1, 180.9, 723.4, 179.3, 711.9, 179.3, 711.9, 177.7, 700.4,
+188.2, 671.9, 189.3, 670.2, 209.1, 647.2, 209.0, 655.4, 209.2, 655.4,
+209.3, 663.7, 208.7, 667.8, 207.4, 667.6, 205.6, 671.5, 199.2, 685.0,
+199.2, 685.0, 192.8, 698.4, 186.8, 710.9, 188.3, 722.5, 180.9, 723.4,
+157.5, 553.7, 156.8, 548.2, 156.8, 548.2, 156.0, 542.8, 152.2, 536.9,
+159.6, 532.1, 163.3, 521.4, 165.9, 522.6, 161.2, 532.8, 159.2, 544.2,
+158.4, 549.0, 156.4, 553.2, 157.5, 553.7, 209.5, 646.5, 209.3, 646.8,
+209.1, 646.8, 209.1, 647.2, 189.3, 670.2, 188.2, 671.9, 177.7, 700.4,
+176.4, 690.9, 177.5, 690.6, 175.1, 681.5, 173.8, 677.2, 174.5, 677.0,
+173.9, 672.6, 177.4, 657.8, 178.9, 658.2, 184.0, 643.8, 187.0, 635.3,
+187.0, 635.3, 189.9, 626.9, 194.9, 612.9, 204.3, 603.4, 199.8, 598.9,
+214.1, 613.2, 204.9, 622.6, 209.5, 646.5, 199.8, 598.9, 204.3, 563.1,
+235.1, 554.2, 224.3, 529.2, 218.6, 582.4, 217.8, 582.5, 215.7, 636.0,
+212.6, 641.2, 212.6, 641.2, 209.5, 646.5, 204.9, 622.6, 214.1, 613.2,
+199.8, 598.9, 464.9, 784.3, 465.8, 799.3, 465.8, 799.3, 466.7, 814.3,
+464.5, 813.9, 459.0, 846.5, 451.3, 878.7, 452.4, 874.2, 451.9, 874.1,
+452.6, 869.4, 453.3, 865.0, 453.3, 865.0, 453.9, 860.5, 457.1, 838.5,
+457.1, 838.5, 460.2, 816.6, 462.5, 801.0, 462.5, 801.0, 464.7, 785.5,
+464.8, 784.9, 464.9, 784.9, 464.9, 784.3, 387.9, 282.3, 363.9, 266.8,
+367.7, 256.8, 355.0, 227.9, 354.1, 225.8, 357.9, 224.1, 360.7, 220.3,
+361.3, 219.5, 361.3, 219.5, 361.9, 218.8, 364.7, 215.0, 364.7, 215.0,
+367.6, 211.2, 372.2, 205.9, 371.9, 205.6, 376.1, 199.9, 376.0, 199.9,
+376.8, 204.7, 377.5, 209.4, 377.6, 210.3, 377.6, 210.3, 377.8, 211.3,
+380.0, 227.2, 380.0, 227.2, 382.3, 243.1, 382.5, 244.4, 382.5, 244.4,
+382.7, 245.8, 385.3, 264.0, 377.8, 275.8, 387.9, 282.3, 136.9, 107.6,
+136.0, 106.7, 136.0, 106.7, 135.1, 105.7, 148.2, 91.7, 160.9, 77.5,
+159.4, 76.0, 165.9, 69.1, 164.6, 64.4, 172.3, 62.2, 172.9, 61.9,
+173.1, 61.1, 173.3, 61.2, 174.9, 61.2, 174.9, 61.8, 176.5, 62.4,
+178.2, 59.9, 187.3, 66.3, 198.1, 70.3, 196.4, 70.1, 195.9, 73.5,
+193.8, 76.7, 166.2, 93.6, 165.9, 93.3, 136.9, 107.6, 193.8, 76.7,
+181.1, 95.5, 173.7, 93.6, 168.4, 114.3, 160.3, 118.7, 160.3, 118.7,
+152.1, 123.1, 149.7, 114.0, 144.5, 115.4, 136.9, 107.6, 165.9, 93.3,
+166.2, 93.6, 193.8, 76.7, 194.6, 18.2, 195.1, 17.9, 195.6, 18.0,
+195.7, 17.5, 204.2, 15.3, 204.2, 15.3, 212.6, 13.1, 199.5, 33.8,
+205.6, 39.5, 204.7, 66.0, 203.1, 65.8, 203.1, 65.8, 201.4, 65.6,
+196.5, 42.2, 197.4, 42.0, 194.6, 18.2, 201.4, 65.6, 189.0, 64.0,
+179.9, 57.6, 176.5, 62.4, 174.9, 61.8, 174.9, 61.2, 173.3, 61.2,
+158.7, 55.1, 158.4, 55.7, 143.5, 50.2, 169.7, 35.4, 169.0, 34.2,
+194.6, 18.2, 197.4, 42.0, 196.5, 42.2, 201.4, 65.6, 169.5, 141.3,
+169.7, 143.6, 169.7, 143.6, 169.9, 145.9, 135.0, 185.0, 141.7, 191.0,
+113.8, 236.3, 122.6, 230.0, 103.4, 212.0, 107.5, 190.5, 109.2, 182.0,
+116.5, 183.4, 125.4, 176.3, 147.5, 158.8, 146.3, 157.1, 169.5, 141.3,
+157.8, 679.7, 171.7, 714.9, 171.3, 715.1, 184.6, 750.6, 184.7, 751.0,
+184.6, 751.1, 184.7, 751.5, 185.4, 756.6, 185.4, 756.6, 186.1, 761.7,
+175.0, 733.1, 176.1, 732.7, 166.1, 703.6, 162.0, 691.7, 162.5, 691.5,
+157.8, 679.7, 120.7, 617.0, 117.1, 625.4, 113.4, 625.4, 113.5, 633.8,
+118.6, 641.1, 107.0, 649.1, 100.4, 664.5, 100.9, 641.9, 104.9, 640.9,
+100.1, 619.4, 102.4, 610.2, 102.4, 610.2, 104.7, 601.1, 126.7, 560.0,
+129.0, 559.9, 141.9, 515.4, 141.9, 517.7, 141.9, 517.7, 141.9, 520.0,
+131.8, 568.6, 132.7, 568.8, 120.7, 617.0, 141.9, 520.0, 142.0, 543.5,
+142.0, 543.5, 142.0, 567.0, 142.1, 567.7, 141.4, 567.7, 141.4, 568.5,
+144.0, 585.9, 125.7, 587.3, 125.1, 606.5, 126.2, 611.0, 122.9, 611.8,
+120.7, 617.0, 132.7, 568.8, 131.8, 568.6, 141.9, 520.0, 69.2, 152.0,
+67.3, 142.3, 67.1, 140.6, 70.8, 131.7, 69.4, 135.0, 72.3, 136.2,
+73.9, 140.7, 79.7, 157.5, 83.2, 157.1, 85.5, 174.4, 84.6, 167.9,
+81.1, 168.4, 76.7, 162.3, 73.0, 157.2, 70.3, 157.7, 69.2, 152.0,
+67.4, 174.6, 67.4, 174.8, 67.4, 174.8, 67.4, 175.0, 67.2, 178.0,
+67.2, 178.0, 66.9, 181.0, 64.6, 210.9, 69.8, 212.3, 62.3, 240.9,
+58.8, 233.2, 59.1, 225.6, 55.3, 225.4, 53.3, 220.0, 53.3, 220.0,
+51.4, 214.6, 54.3, 199.5, 57.2, 200.1, 63.0, 185.6, 63.0, 185.5,
+63.0, 185.5, 63.0, 185.5, 65.2, 180.0, 65.2, 180.0, 67.4, 174.6,
+55.5, 87.2, 51.3, 86.0, 53.0, 79.9, 50.5, 72.6, 49.1, 68.4,
+49.1, 68.4, 47.6, 64.1, 47.2, 62.9, 46.6, 62.8, 46.8, 61.7,
+47.1, 58.6, 46.5, 57.3, 48.5, 55.6, 51.1, 57.0, 52.5, 54.8,
+56.4, 53.5, 71.2, 48.0, 71.7, 49.5, 87.1, 45.6, 87.3, 45.5,
+87.3, 45.5, 87.6, 45.5, 84.6, 48.1, 88.5, 52.6, 89.5, 59.8,
+88.8, 71.2, 92.0, 76.9, 85.1, 82.4, 75.0, 90.6, 68.6, 90.9,
+55.5, 87.2, 85.1, 82.4, 93.5, 106.7, 82.1, 112.3, 73.9, 140.7,
+72.3, 136.2, 69.4, 135.0, 70.8, 131.7, 65.7, 116.8, 65.7, 116.8,
+60.6, 101.9, 58.2, 95.1, 58.2, 95.1, 55.9, 88.2, 55.7, 87.7,
+55.6, 87.2, 55.5, 87.2, 68.6, 90.9, 75.0, 90.6, 85.1, 82.4,
+93.3, 87.3, 94.0, 92.2, 96.3, 96.3, 94.7, 97.2, 92.1, 120.2,
+99.2, 144.7, 101.2, 144.6, 100.2, 147.0, 102.6, 150.4, 102.0, 150.6,
+104.8, 170.5, 104.8, 170.5, 107.5, 190.5, 103.4, 212.0, 122.6, 230.0,
+113.8, 236.3, 113.7, 236.5, 114.0, 236.7, 113.9, 236.9, 113.1, 237.7,
+114.8, 240.2, 114.4, 240.4, 111.2, 244.9, 118.9, 253.8, 116.4, 255.3,
+114.0, 261.7, 117.5, 263.1, 118.6, 270.9, 119.2, 266.7, 117.0, 266.4,
+115.5, 261.9, 104.1, 228.6, 97.0, 229.5, 92.6, 195.2, 85.9, 142.1,
+92.2, 141.2, 93.3, 87.3, 92.6, 195.2, 92.7, 191.9, 91.4, 191.9,
+90.3, 188.5, 87.9, 181.5, 87.9, 181.5, 85.5, 174.4, 83.2, 157.1,
+79.7, 157.5, 73.9, 140.7, 82.1, 112.3, 93.5, 106.7, 85.1, 82.4,
+92.0, 76.9, 88.8, 71.2, 89.5, 59.8, 91.4, 73.5, 91.4, 73.5,
+93.3, 87.3, 92.2, 141.2, 85.9, 142.1, 92.6, 195.2, 75.0, 344.2,
+75.5, 328.2, 75.6, 328.2, 76.0, 312.1, 76.6, 299.7, 83.2, 291.6,
+77.7, 287.3, 84.3, 305.5, 84.3, 305.5, 90.8, 323.7, 90.3, 334.3,
+75.0, 344.2, 75.0, 344.2, 75.7, 317.2, 73.9, 344.1, 72.8, 344.1,
+72.1, 371.0, 66.5, 363.4, 66.5, 363.4, 60.9, 355.8, 62.3, 336.1,
+82.3, 326.3, 75.7, 317.2, 114.9, 486.1, 99.0, 457.2, 106.5, 437.0,
+83.1, 428.3, 79.5, 419.1, 73.3, 416.9, 75.8, 409.9, 76.1, 393.5,
+76.7, 393.4, 76.5, 377.0, 75.7, 380.0, 79.3, 381.0, 82.2, 384.9,
+82.6, 385.4, 82.7, 385.3, 82.9, 385.9, 88.9, 404.1, 88.8, 404.2,
+94.6, 422.5, 104.7, 454.3, 119.4, 483.8, 114.9, 486.1, 74.0, 508.4,
+74.5, 479.7, 74.5, 479.7, 75.1, 451.0, 75.5, 454.7, 78.2, 454.3,
+81.3, 457.6, 76.1, 468.4, 90.3, 475.2, 99.3, 492.8, 97.9, 502.2,
+73.8, 508.0, 74.0, 508.4, 99.3, 492.8, 105.6, 505.0, 102.1, 512.9,
+111.8, 517.1, 106.8, 534.3, 97.8, 531.6, 83.8, 546.1, 81.2, 548.7,
+81.6, 549.3, 78.7, 551.4, 78.6, 550.6, 78.8, 550.4, 78.4, 549.9,
+76.2, 547.2, 79.2, 544.8, 77.0, 541.9, 71.5, 538.3, 75.4, 532.3,
+73.7, 522.7, 72.5, 516.8, 74.7, 516.4, 73.9, 510.3, 73.7, 509.4,
+74.0, 509.4, 74.0, 508.4, 73.8, 508.0, 97.9, 502.2, 99.3, 492.8,
+115.1, 546.9, 107.5, 568.2, 104.7, 567.6, 100.0, 589.5, 99.8, 594.3,
+98.3, 594.2, 96.7, 598.9, 93.8, 582.6, 92.0, 581.1, 81.6, 567.8,
+80.6, 562.5, 82.9, 560.1, 79.7, 557.1, 78.0, 556.0, 79.2, 554.2,
+78.7, 551.4, 81.6, 549.3, 81.2, 548.7, 83.8, 546.1, 99.1, 544.2,
+113.0, 541.4, 115.1, 546.9, 83.8, 546.1, 97.8, 531.6, 106.8, 534.3,
+111.8, 517.1, 114.8, 518.5, 118.5, 510.7, 124.8, 503.8, 125.4, 503.4,
+128.5, 505.7, 128.8, 508.0, 127.4, 512.0, 127.1, 511.9, 126.0, 515.9,
+124.2, 531.8, 120.5, 531.4, 115.1, 546.9, 113.0, 541.4, 99.1, 544.2,
+83.8, 546.1, -27.0, 410.9, -28.8, 406.8, -30.4, 406.9, -30.6, 402.7,
+-32.6, 389.3, -29.2, 377.4, -34.5, 375.9, -34.7, 373.3, -32.7, 373.0,
+-33.2, 370.6, -29.2, 388.7, -30.4, 389.0, -27.5, 407.4, -27.2, 409.2,
+-27.8, 409.6, -27.0, 410.9, -22.3, 363.3, -25.0, 355.2, -24.5, 355.0,
+-27.6, 347.1, -27.8, 346.5, -27.1, 346.3, -27.3, 345.7, -24.5, 354.4,
+-20.5, 355.6, -22.3, 363.3, 2.2, 282.6, 9.2, 297.5, 18.3, 298.8,
+16.3, 312.3, 16.4, 326.7, 16.4, 326.7, 16.6, 341.0, 6.2, 338.8,
+1.5, 340.8, -6.7, 348.2, -16.5, 346.7, -15.9, 341.8, -24.8, 335.0,
+-24.8, 335.0, -24.7, 334.8, -24.7, 334.6, -23.4, 329.3, -23.3, 329.4,
+-22.1, 324.1, -11.1, 302.8, -14.7, 287.8, 2.2, 282.6, -22.1, 324.1,
+-32.6, 302.0, -22.3, 289.0, -7.1, 263.0, -6.8, 272.9, -2.5, 272.8,
+2.2, 282.6, -14.7, 287.8, -11.1, 302.8, -22.1, 324.1, 48.9, 670.5,
+45.7, 656.7, 49.8, 654.0, 42.5, 643.0, 47.9, 644.1, 53.2, 643.6,
+53.4, 645.1, 59.0, 650.8, 62.8, 647.0, 72.2, 648.9, 70.0, 661.3,
+58.1, 672.3, 48.9, 670.5, 749.0, 492.6, 747.0, 491.5, 749.8, 485.2,
+748.1, 484.6, 762.9, 499.3, 767.1, 495.1, 786.1, 505.6, 785.1, 497.0,
+766.0, 502.0, 749.0, 492.6, 804.0, 583.1, 804.0, 596.3, 804.0, 598.2,
+797.6, 609.5, 793.0, 608.8, 788.4, 609.3, 787.9, 611.3, 783.2, 607.1,
+780.2, 601.8, 778.4, 602.8, 778.5, 602.6, 778.0, 602.4, 777.5, 602.0,
+781.5, 601.4, 784.1, 591.8, 782.5, 583.1, 787.8, 562.4, 787.8, 562.4,
+793.2, 541.7, 795.3, 533.5, 799.3, 532.4, 797.4, 525.2, 804.7, 553.1,
+803.9, 554.2, 804.0, 583.1, 797.4, 525.2, 797.1, 519.5, 798.1, 513.8,
+800.5, 513.5, 808.6, 518.0, 809.9, 516.5, 816.7, 522.4, 817.5, 523.1,
+818.2, 523.9, 818.4, 523.8, 815.5, 535.4, 814.9, 535.3, 812.7, 547.1,
+810.7, 555.2, 810.7, 555.2, 808.8, 563.4, 806.4, 573.2, 801.5, 574.7,
+804.0, 583.1, 803.9, 554.2, 804.7, 553.1, 797.4, 525.2, 713.5, 490.2,
+713.2, 490.0, 713.6, 489.3, 713.3, 489.1, 717.8, 492.9, 718.2, 492.3,
+723.2, 495.6, 722.6, 492.1, 718.1, 493.2, 713.5, 490.2, 744.7, 514.0,
+740.9, 511.5, 738.4, 508.1, 737.1, 509.1, 734.5, 506.4, 735.2, 505.7,
+733.3, 502.3, 732.5, 501.9, 732.8, 501.5, 732.3, 500.6, 738.7, 507.1,
+740.3, 506.3, 744.7, 514.0, 840.1, 624.8, 859.1, 643.0, 856.0, 656.1,
+878.0, 661.2, 875.3, 663.4, 875.3, 663.4, 872.6, 665.6, 862.1, 657.6,
+864.1, 655.0, 855.7, 644.3, 849.0, 635.9, 849.0, 635.9, 842.2, 627.4,
+841.2, 626.1, 841.3, 625.9, 840.1, 624.8, 839.5, 570.9, 839.5, 570.9,
+847.4, 578.7, 855.2, 586.5, 855.4, 586.6, 855.4, 586.6, 855.5, 586.8,
+860.1, 591.3, 860.2, 591.2, 864.8, 595.8, 865.1, 596.1, 865.0, 596.2,
+865.4, 596.6, 860.4, 591.7, 860.4, 591.7, 855.5, 586.8, 855.4, 586.6,
+855.4, 586.6, 855.2, 586.5, 847.4, 578.7, 839.5, 570.9, 839.5, 570.9,
+877.5, 603.6, 857.1, 585.8, 857.1, 585.8, 836.7, 568.0, 838.3, 567.7,
+839.8, 566.4, 839.6, 565.9, 839.5, 565.7, 842.7, 563.6, 845.9, 561.2,
+851.4, 552.7, 856.6, 556.1, 867.3, 550.9, 867.2, 551.2, 872.1, 553.1,
+876.8, 555.4, 881.3, 578.7, 884.1, 601.5, 877.5, 603.6, 876.8, 555.4,
+887.4, 560.3, 887.3, 560.5, 897.9, 565.3, 900.5, 583.4, 900.4, 583.4,
+903.1, 601.5, 904.2, 615.5, 904.8, 615.5, 907.1, 629.4, 892.3, 616.5,
+892.3, 616.5, 877.5, 603.6, 884.1, 601.5, 881.3, 578.7, 876.8, 555.4,
+924.9, 574.1, 934.6, 580.7, 934.6, 580.7, 944.3, 587.3, 944.0, 594.1,
+941.0, 598.8, 944.0, 600.9, 943.8, 604.9, 945.7, 606.7, 943.6, 608.8,
+926.1, 612.3, 927.9, 621.4, 912.2, 633.9, 912.3, 632.1, 910.0, 632.0,
+907.8, 630.0, 907.5, 626.4, 907.2, 626.4, 906.6, 622.8, 904.2, 604.5,
+903.8, 604.5, 901.1, 586.3, 904.6, 577.0, 913.1, 573.8, 924.9, 574.1,
+901.1, 586.3, 896.6, 584.8, 899.5, 575.8, 897.9, 565.3, 897.9, 565.1,
+897.9, 565.1, 897.9, 564.9, 897.2, 559.8, 898.2, 559.5, 896.4, 554.7,
+903.9, 559.8, 903.7, 560.0, 911.3, 564.9, 913.1, 566.1, 913.1, 566.1,
+915.0, 567.3, 915.1, 570.9, 919.9, 570.7, 924.9, 574.1, 913.1, 573.8,
+904.6, 577.0, 901.1, 586.3, 556.4, 358.5, 563.7, 373.2, 565.8, 372.2,
+575.2, 385.9, 582.9, 397.1, 582.9, 397.1, 590.6, 408.4, 612.9, 440.9,
+613.2, 440.7, 635.2, 473.4, 635.6, 474.9, 636.0, 474.9, 636.1, 476.4,
+637.2, 490.9, 632.9, 492.8, 638.3, 505.4, 638.4, 507.7, 638.1, 507.7,
+637.8, 510.0, 637.6, 511.1, 638.0, 511.3, 637.5, 512.3, 637.6, 512.1,
+637.2, 511.8, 636.9, 511.4, 596.6, 435.0, 594.5, 436.0, 556.4, 358.5,
+636.9, 511.4, 631.2, 500.0, 630.0, 500.6, 623.1, 489.8, 602.2, 457.1,
+602.2, 457.1, 581.3, 424.5, 580.7, 423.6, 580.7, 423.6, 580.1, 422.7,
+576.4, 416.8, 576.4, 416.8, 572.6, 410.9, 568.0, 403.6, 568.0, 403.6,
+563.3, 396.4, 554.4, 382.5, 554.4, 382.5, 545.5, 368.5, 542.6, 364.1,
+540.9, 364.5, 539.8, 359.6, 538.8, 344.7, 538.3, 344.7, 536.7, 329.8,
+545.6, 329.2, 548.0, 343.3, 556.4, 358.5, 594.5, 436.0, 596.6, 435.0,
+636.9, 511.4, 675.1, 570.2, 674.8, 570.2, 674.6, 570.3, 674.6, 570.2,
+665.5, 556.2, 665.7, 556.0, 656.7, 541.9, 653.1, 536.2, 653.0, 536.3,
+649.4, 530.7, 645.6, 524.9, 641.9, 519.1, 641.9, 519.1, 641.9, 519.1,
+645.6, 524.9, 649.4, 530.7, 653.0, 536.3, 653.1, 536.2, 656.7, 541.9,
+665.9, 556.0, 666.1, 555.9, 675.1, 570.2, 899.8, 548.7, 895.3, 542.3,
+895.6, 542.1, 890.9, 536.0, 885.0, 514.4, 887.5, 513.7, 884.2, 491.5,
+882.5, 489.1, 883.7, 488.2, 883.2, 484.9, 883.2, 484.2, 883.7, 484.2,
+884.2, 483.4, 886.2, 479.1, 886.7, 479.4, 889.2, 475.3, 894.8, 486.7,
+891.1, 488.4, 893.0, 501.6, 896.4, 525.1, 901.3, 525.5, 899.8, 548.7,
+884.2, 389.0, 889.3, 403.4, 884.3, 409.7, 894.4, 417.8, 899.5, 431.9,
+904.7, 432.0, 904.5, 446.0, 898.0, 455.1, 899.1, 455.9, 893.7, 465.8,
+881.0, 430.2, 883.6, 427.3, 884.2, 389.0, 893.7, 465.8, 888.9, 474.6,
+888.4, 474.4, 884.2, 483.4, 883.7, 484.2, 883.2, 484.2, 883.2, 484.9,
+882.6, 480.5, 884.0, 479.6, 881.9, 476.0, 881.7, 474.5, 881.7, 474.5,
+881.4, 472.9, 881.6, 432.1, 876.0, 432.0, 869.9, 391.2, 870.0, 370.4,
+872.9, 370.0, 870.1, 349.6, 876.6, 367.7, 878.4, 386.3, 883.1, 385.9,
+882.6, 387.0, 883.6, 387.4, 884.2, 389.0, 883.6, 427.3, 881.0, 430.2,
+893.7, 465.8, 912.4, 468.1, 909.0, 458.6, 911.4, 456.8, 905.6, 449.0,
+905.9, 447.7, 904.0, 446.7, 904.5, 446.0, 904.7, 432.0, 899.5, 431.9,
+894.4, 417.8, 896.0, 419.1, 901.1, 412.8, 907.7, 407.8, 917.5, 435.3,
+913.9, 438.2, 912.4, 468.1, 907.7, 407.8, 913.4, 403.5, 917.7, 404.6,
+919.1, 399.2, 920.4, 403.4, 920.4, 403.4, 921.6, 407.7, 923.3, 429.6,
+924.9, 429.4, 928.2, 451.2, 930.2, 470.0, 930.5, 470.0, 933.3, 488.7,
+936.3, 516.2, 934.7, 516.5, 939.4, 543.8, 931.7, 522.3, 935.4, 520.1,
+924.1, 500.9, 921.1, 492.4, 921.1, 492.4, 918.1, 484.0, 916.7, 480.1,
+916.7, 480.1, 915.3, 476.2, 916.2, 472.8, 913.8, 472.2, 912.4, 468.1,
+913.9, 438.2, 917.5, 435.3, 907.7, 407.8, 869.3, 391.2, 869.6, 393.4,
+869.6, 395.5, 869.9, 395.5, 875.4, 434.2, 881.6, 434.2, 881.4, 472.9,
+881.7, 474.5, 881.7, 474.5, 881.9, 476.0, 862.4, 442.8, 860.9, 443.7,
+840.0, 411.3, 838.8, 403.6, 853.7, 399.5, 869.3, 391.2, 840.0, 411.3,
+833.9, 401.9, 836.4, 396.3, 827.8, 392.5, 834.3, 373.1, 846.5, 358.5,
+840.8, 353.8, 844.2, 352.5, 844.2, 352.5, 847.6, 351.2, 847.8, 352.0,
+856.3, 353.2, 863.2, 350.6, 866.2, 370.9, 866.2, 370.9, 869.3, 391.2,
+853.7, 399.5, 838.8, 403.6, 840.0, 411.3, 797.4, 463.4, 818.1, 481.9,
+832.6, 485.1, 832.8, 507.0, 815.2, 491.3, 815.2, 491.3, 797.5, 475.5,
+794.5, 471.9, 800.4, 466.1, 797.4, 463.4, 797.5, 475.5, 781.5, 461.2,
+783.7, 457.2, 765.4, 446.9, 763.2, 449.1, 743.6, 417.7, 714.5, 401.6,
+727.0, 396.9, 727.0, 396.9, 739.4, 392.2, 746.0, 393.0, 745.1, 399.2,
+750.9, 406.3, 764.3, 422.8, 764.3, 422.8, 777.8, 439.4, 787.6, 451.4,
+790.1, 450.1, 797.4, 463.4, 800.4, 466.1, 794.5, 471.9, 797.5, 475.5,
+764.0, 382.8, 766.2, 382.0, 766.2, 382.0, 768.3, 381.2, 787.3, 419.8,
+792.2, 417.7, 818.3, 452.4, 839.0, 481.8, 837.6, 483.0, 859.8, 511.3,
+811.2, 449.2, 812.7, 448.1, 765.5, 384.9, 764.8, 383.9, 763.8, 383.1,
+764.0, 382.8, 875.4, 533.5, 871.8, 528.1, 871.7, 528.3, 868.0, 523.0,
+863.9, 517.2, 863.9, 517.2, 859.8, 511.3, 837.6, 483.0, 839.0, 481.8,
+818.3, 452.4, 814.6, 447.4, 815.7, 446.5, 813.2, 440.7, 831.1, 465.0,
+830.0, 465.8, 846.8, 490.9, 848.1, 492.9, 848.1, 492.9, 849.5, 494.8,
+862.4, 514.2, 862.6, 514.1, 875.4, 533.5, 544.1, 175.8, 556.5, 200.5,
+554.9, 201.4, 569.0, 225.3, 561.9, 229.4, 547.8, 205.6, 526.7, 186.0,
+525.3, 184.7, 524.4, 183.1, 523.9, 183.4, 533.0, 178.0, 540.5, 172.5,
+544.1, 175.8, 526.7, 186.0, 547.8, 205.6, 561.9, 229.4, 569.0, 225.3,
+572.1, 230.5, 576.1, 228.6, 578.3, 234.0, 586.1, 245.8, 588.9, 243.9,
+599.6, 253.8, 619.4, 274.2, 619.6, 274.0, 639.5, 294.1, 616.9, 278.6,
+614.7, 281.7, 589.9, 269.3, 577.6, 260.4, 580.3, 256.7, 570.7, 244.0,
+560.1, 230.0, 560.1, 230.0, 549.5, 216.0, 544.8, 209.8, 544.8, 209.8,
+540.0, 203.6, 538.5, 201.6, 538.5, 201.6, 537.0, 199.6, 531.9, 192.8,
+533.2, 191.2, 526.7, 186.0, 606.5, 321.3, 607.1, 332.5, 603.3, 334.3,
+607.8, 343.7, 582.8, 318.3, 582.8, 318.3, 557.8, 293.0, 550.1, 270.2,
+544.6, 272.1, 531.4, 251.2, 574.7, 276.3, 574.1, 282.0, 606.5, 321.3,
+531.4, 251.2, 526.9, 244.1, 528.3, 242.6, 522.4, 236.9, 563.4, 261.1,
+563.4, 261.1, 604.4, 285.3, 606.6, 286.8, 604.7, 289.6, 604.9, 294.0,
+605.1, 297.0, 605.1, 297.0, 605.3, 300.0, 602.1, 301.3, 605.9, 310.6,
+606.5, 321.3, 574.1, 282.0, 574.7, 276.3, 531.4, 251.2, 604.9, 294.0,
+604.7, 289.6, 606.6, 286.8, 604.4, 285.3, 606.9, 286.8, 606.9, 286.8,
+609.4, 288.2, 609.6, 291.0, 605.3, 294.2, 604.9, 294.0, 542.8, 279.0,
+564.4, 308.2, 562.7, 309.6, 586.0, 337.3, 543.6, 311.2, 543.6, 311.2,
+501.2, 285.0, 505.9, 288.8, 510.0, 283.7, 518.8, 282.5, 530.8, 280.7,
+536.5, 273.8, 542.8, 279.0, 567.3, 73.7, 569.0, 78.4, 568.2, 78.7,
+569.1, 83.6, 569.4, 84.9, 569.4, 84.9, 569.6, 86.2, 568.6, 86.9,
+571.5, 91.2, 570.6, 91.6, 566.7, 94.4, 566.7, 94.4, 562.7, 97.1,
+561.8, 97.0, 561.9, 94.1, 561.1, 91.2, 553.1, 61.2, 553.1, 61.2,
+545.1, 31.1, 543.1, 23.6, 543.1, 23.6, 541.1, 16.0, 535.7, -4.2,
+538.8, -5.6, 530.3, -24.4, 551.9, 23.2, 549.6, 24.4, 567.3, 73.7,
+530.3, -24.4, 529.7, -26.0, 529.8, -26.1, 529.4, -27.8, 528.2, -32.2,
+527.2, -32.2, 527.0, -36.7, 526.9, -37.9, 528.8, -39.2, 528.8, -39.2,
+530.9, -27.9, 550.9, -21.1, 549.9, -19.7, 553.8, 1.1, 548.9, 19.7,
+557.7, 22.0, 560.4, 36.7, 560.4, 36.7, 563.1, 51.4, 565.2, 62.6,
+562.9, 63.6, 567.3, 73.7, 549.6, 24.4, 551.9, 23.2, 530.3, -24.4,
+682.2, -11.8, 682.8, -12.2, 687.1, -7.7, 686.9, -7.4, 662.7, 7.7,
+666.9, 14.4, 646.9, 36.3, 607.4, 59.9, 606.8, 59.2, 569.6, 86.2,
+569.4, 84.9, 569.4, 84.9, 569.1, 83.6, 588.0, 66.3, 588.8, 67.1,
+608.4, 50.5, 624.1, 37.2, 624.1, 37.2, 639.9, 23.9, 661.0, 6.1,
+659.3, 3.5, 682.2, -11.8, 376.8, 74.3, 374.7, 66.1, 374.7, 66.1,
+372.6, 57.9, 376.1, 61.9, 406.7, 46.2, 406.0, 43.4, 413.5, 43.8,
+421.0, 59.2, 420.2, 60.1, 406.4, 74.7, 398.1, 75.3, 376.8, 74.3,
+439.2, 19.5, 426.1, 5.7, 426.1, 5.7, 413.0, -8.1, 441.4, -5.2,
+442.7, -8.0, 471.0, -14.3, 488.5, -14.8, 498.8, -2.9, 506.1, -10.7,
+482.9, 14.0, 473.1, 18.6, 439.2, 19.5, 506.1, -10.7, 506.9, -11.0,
+507.9, -10.1, 508.1, -10.5, 487.9, 19.2, 487.4, 18.9, 466.1, 47.9,
+466.2, 47.8, 465.9, 47.6, 465.6, 47.3, 452.6, 33.3, 452.4, 33.4,
+439.2, 19.5, 473.1, 18.6, 482.9, 14.0, 506.1, -10.7, 424.5, 65.0,
+422.7, 62.3, 422.4, 62.5, 420.2, 60.1, 421.0, 59.2, 413.5, 43.8,
+406.0, 43.4, 397.1, 8.3, 364.4, -22.7, 353.5, -18.0, 352.8, -20.5,
+351.7, -22.3, 349.8, -22.3, 355.9, -23.9, 355.9, -23.9, 362.0, -25.4,
+371.9, -18.7, 369.3, -14.8, 376.6, -4.2, 383.0, 5.0, 383.0, 5.0,
+389.3, 14.2, 396.6, 24.7, 396.6, 24.7, 403.8, 35.1, 414.2, 50.1,
+414.5, 49.8, 424.5, 65.0, 414.0, -38.8, 421.4, -40.7, 421.3, -40.9,
+428.6, -42.8, 449.2, -48.0, 449.2, -47.9, 469.8, -53.0, 471.6, -53.4,
+471.6, -53.4, 473.5, -53.9, 443.7, -46.3, 443.8, -46.2, 414.0, -38.8,
+208.2, 292.5, 217.0, 279.3, 222.7, 280.5, 225.8, 266.0, 239.8, 263.1,
+242.0, 274.1, 258.3, 282.1, 260.9, 283.4, 260.9, 283.4, 263.4, 284.6,
+267.9, 286.8, 272.4, 286.8, 272.4, 289.1, 272.4, 291.5, 267.9, 291.5,
+263.4, 293.9, 252.2, 300.0, 252.2, 300.0, 241.0, 306.0, 240.2, 306.5,
+239.9, 307.2, 239.3, 307.0, 223.5, 300.4, 211.9, 303.7, 208.2, 292.5,
+239.3, 307.0, 240.1, 309.9, 227.5, 313.4, 215.6, 319.8, 208.1, 323.9,
+208.1, 323.9, 200.5, 328.0, 197.8, 326.4, 191.7, 329.4, 190.1, 333.6,
+194.5, 320.1, 194.5, 320.1, 198.8, 306.6, 200.1, 299.0, 203.5, 299.5,
+208.2, 292.5, 211.9, 303.7, 223.5, 300.4, 239.3, 307.0, 238.5, 111.6,
+244.2, 108.1, 247.0, 109.7, 249.8, 104.6, 245.6, 119.3, 245.6, 119.3,
+241.5, 134.0, 240.6, 137.6, 239.4, 137.2, 238.0, 140.7, 230.1, 155.8,
+230.1, 155.8, 222.2, 171.0, 200.9, 211.7, 203.3, 213.2, 179.6, 252.5,
+180.5, 246.8, 179.8, 246.7, 179.9, 241.0, 207.3, 175.5, 206.2, 174.7,
+238.5, 111.6, 179.9, 241.0, 180.4, 221.2, 174.6, 203.5, 180.9, 201.5,
+201.5, 161.7, 201.5, 161.7, 222.1, 121.8, 224.7, 114.7, 230.3, 116.7,
+238.5, 111.6, 206.2, 174.7, 207.3, 175.5, 179.9, 241.0, 199.0, 163.7,
+180.0, 201.5, 180.0, 201.5, 161.1, 239.4, 150.1, 261.1, 150.0, 261.0,
+138.9, 282.6, 137.6, 285.0, 137.6, 285.0, 136.3, 287.5, 137.7, 283.6,
+136.5, 283.1, 136.6, 278.7, 146.3, 257.4, 147.8, 258.1, 159.0, 237.4,
+179.0, 200.6, 206.9, 171.3, 199.0, 163.7, 477.6, 762.9, 483.4, 806.1,
+483.5, 806.0, 489.1, 849.3, 489.8, 854.9, 489.6, 854.9, 490.1, 860.6,
+490.8, 868.4, 490.2, 868.5, 491.6, 876.2, 489.4, 864.4, 490.1, 864.3,
+488.6, 852.4, 483.4, 810.0, 483.4, 810.0, 478.2, 767.6, 477.9, 765.3,
+477.9, 765.2, 477.6, 762.9, 491.9, 847.9, 491.7, 854.3, 493.4, 859.1,
+490.1, 860.6, 489.6, 854.9, 489.8, 854.9, 489.1, 849.3, 487.8, 840.3,
+488.3, 840.2, 487.4, 831.1, 485.8, 836.4, 489.1, 837.4, 490.8, 843.6,
+491.4, 845.8, 492.0, 845.8, 491.9, 847.9, 501.5, 451.5, 496.1, 456.3,
+485.1, 452.0, 484.8, 454.4, 495.2, 435.8, 495.2, 435.9, 505.5, 417.3,
+512.4, 405.1, 519.8, 404.6, 519.2, 392.8, 528.4, 376.3, 537.4, 359.8,
+537.5, 359.8, 538.4, 359.2, 538.9, 358.3, 538.6, 357.9, 539.2, 358.7,
+539.7, 358.7, 539.8, 359.6, 540.9, 364.5, 542.6, 364.1, 545.5, 368.5,
+533.3, 400.4, 529.4, 398.9, 513.3, 429.3, 507.4, 440.4, 510.3, 443.7,
+501.5, 451.5, 468.2, 621.6, 466.6, 603.5, 466.5, 603.5, 464.8, 585.3,
+484.5, 550.0, 465.4, 538.7, 463.8, 492.1, 471.6, 478.1, 471.6, 478.1,
+479.4, 464.0, 488.2, 493.2, 477.1, 496.5, 474.8, 529.0, 474.7, 530.5,
+474.7, 530.5, 474.6, 531.9, 472.3, 563.6, 472.3, 563.6, 470.1, 595.2,
+469.2, 607.8, 469.2, 607.8, 468.3, 620.3, 468.2, 621.0, 468.1, 621.0,
+468.2, 621.6, 458.7, 501.2, 458.0, 502.4, 457.3, 502.5, 457.3, 503.6,
+456.5, 494.1, 456.5, 494.1, 455.6, 484.5, 452.4, 490.2, 457.9, 492.8,
+458.7, 501.2, 480.2, 266.5, 481.8, 271.2, 482.9, 270.8, 485.7, 275.1,
+492.4, 286.2, 492.6, 286.0, 499.6, 296.9, 503.1, 301.2, 500.0, 303.7,
+500.3, 310.6, 492.2, 303.8, 495.4, 300.0, 490.6, 289.4, 485.4, 277.9,
+484.3, 278.3, 480.2, 266.5, 205.6, 671.5, 207.4, 667.6, 208.7, 667.8,
+209.3, 663.7, 209.5, 676.0, 209.5, 676.0, 209.6, 688.4, 212.5, 682.3,
+205.7, 680.0, 205.6, 671.5, 173.8, 490.6, 180.4, 471.0, 174.1, 457.5,
+187.1, 451.5, 206.4, 425.2, 203.9, 422.9, 225.7, 398.9, 231.9, 390.5,
+230.2, 388.0, 238.3, 382.2, 236.2, 383.7, 239.4, 387.0, 237.7, 390.2,
+229.6, 406.0, 228.1, 405.3, 218.5, 420.3, 211.9, 430.8, 211.9, 430.8,
+205.2, 441.3, 189.5, 466.0, 196.2, 478.0, 173.8, 490.6, 434.6, 322.4,
+437.2, 344.4, 439.3, 344.4, 439.9, 366.4, 440.0, 367.4, 440.0, 367.4,
+440.0, 368.4, 432.0, 356.0, 432.4, 355.7, 424.7, 343.1, 424.2, 342.3,
+424.2, 342.3, 423.8, 341.6, 419.6, 334.6, 419.6, 334.6, 415.3, 327.6,
+405.7, 311.6, 405.7, 311.6, 396.0, 295.6, 392.0, 289.0, 389.9, 289.5,
+387.9, 282.3, 377.8, 275.8, 385.3, 264.0, 382.7, 245.8, 379.9, 250.3,
+390.0, 256.5, 397.3, 267.3, 407.0, 281.7, 407.0, 281.7, 416.8, 296.1,
+417.2, 296.7, 417.2, 296.7, 417.5, 297.2, 420.5, 301.6, 420.5, 301.6,
+423.4, 305.9, 429.0, 314.1, 432.1, 313.3, 434.6, 322.4, 209.1, 881.3,
+209.0, 881.8, 205.6, 882.1, 202.6, 881.2, 203.0, 880.7, 202.4, 880.2,
+202.3, 879.2, 215.2, 875.8, 205.3, 838.2, 208.2, 797.2, 203.1, 821.6,
+208.5, 822.8, 208.8, 848.4, 209.0, 864.8, 212.1, 865.4, 209.1, 881.3,
+322.4, 303.2, 317.5, 312.0, 320.3, 313.4, 316.6, 322.9, 314.1, 329.5,
+313.2, 329.1, 309.8, 335.3, 309.5, 336.0, 309.4, 336.0, 309.1, 336.7,
+315.6, 319.9, 313.8, 318.9, 322.4, 303.2, 309.8, 335.3, 313.2, 329.1,
+314.1, 329.5, 316.6, 322.9, 311.6, 347.8, 309.5, 347.3, 302.4, 371.7,
+301.0, 376.6, 301.0, 376.6, 299.6, 381.6, 296.0, 393.7, 296.0, 393.7,
+292.5, 405.8, 292.3, 406.5, 292.3, 406.5, 292.1, 407.2, 291.4, 409.5,
+291.4, 409.5, 290.7, 411.9, 283.9, 435.5, 283.9, 435.5, 277.0, 459.0,
+274.5, 467.6, 274.5, 467.6, 272.0, 476.1, 270.8, 480.4, 270.8, 480.4,
+269.5, 484.6, 256.2, 529.7, 256.2, 529.6, 242.9, 574.7, 241.7, 578.5,
+241.8, 578.5, 240.7, 582.4, 236.3, 598.2, 231.9, 614.0, 231.9, 614.0,
+231.9, 614.0, 236.3, 598.2, 240.7, 582.4, 241.8, 578.5, 241.7, 578.5,
+242.9, 574.7, 267.7, 486.1, 267.8, 486.1, 292.4, 397.5, 300.9, 367.2,
+300.6, 367.1, 309.1, 336.7, 309.4, 336.0, 309.5, 336.0, 309.8, 335.3,
+144.7, 133.3, 156.7, 133.2, 156.7, 133.2, 168.8, 133.0, 168.1, 133.2,
+169.2, 137.2, 169.5, 141.3, 146.3, 157.1, 147.5, 158.8, 125.4, 176.3,
+127.9, 177.1, 129.1, 148.9, 144.7, 133.3, 125.4, 176.3, 116.5, 183.4,
+109.2, 182.0, 107.5, 190.5, 104.8, 170.5, 104.8, 170.5, 102.0, 150.6,
+118.6, 144.8, 117.6, 142.0, 133.2, 133.5, 133.7, 132.2, 137.8, 135.0,
+140.7, 133.4, 142.0, 132.4, 142.7, 133.4, 144.7, 133.3, 129.1, 148.9,
+127.9, 177.1, 125.4, 176.3, 115.5, 261.9, 117.0, 266.4, 119.2, 266.7,
+118.6, 270.9, 118.7, 271.9, 118.7, 271.9, 118.9, 272.9, 123.6, 317.8,
+123.5, 317.8, 128.3, 362.6, 128.7, 366.0, 129.2, 369.3, 129.2, 369.3,
+129.2, 369.3, 128.7, 366.0, 128.3, 362.6, 121.9, 312.3, 120.9, 312.4,
+115.5, 261.9, 60.6, 101.9, 65.7, 116.8, 65.7, 116.8, 70.8, 131.7,
+67.1, 140.6, 67.3, 142.3, 69.2, 152.0, 68.3, 163.3, 69.5, 163.5,
+67.4, 174.6, 65.2, 180.0, 65.2, 180.0, 63.0, 185.5, 63.2, 185.1,
+63.0, 185.0, 63.0, 184.6, 62.5, 168.3, 62.5, 168.3, 62.1, 152.1,
+61.3, 127.0, 51.6, 122.9, 60.6, 101.9, 129.6, 436.3, 133.6, 448.4,
+133.2, 448.6, 137.6, 460.5, 122.5, 453.7, 126.3, 445.1, 115.1, 429.8,
+105.8, 417.1, 108.9, 409.5, 96.5, 404.4, 115.6, 412.2, 112.5, 419.8,
+128.4, 435.2, 129.0, 435.8, 129.3, 435.7, 129.6, 436.3, 38.9, 323.2,
+38.5, 321.0, 38.5, 321.0, 38.1, 318.9, 53.0, 296.7, 49.7, 294.5,
+61.2, 270.1, 65.4, 294.0, 51.2, 297.2, 38.9, 323.2, 115.1, 429.8,
+126.3, 445.1, 122.5, 453.7, 137.6, 460.5, 138.0, 461.7, 139.3, 461.7,
+139.2, 462.7, 139.8, 464.7, 142.0, 464.7, 141.8, 466.3, 141.8, 468.7,
+141.8, 468.7, 141.9, 471.0, 142.0, 471.5, 141.7, 471.6, 141.5, 472.1,
+130.0, 454.6, 130.4, 454.3, 119.3, 436.5, 117.2, 433.1, 117.5, 432.9,
+115.1, 429.8, -27.5, 407.4, -30.4, 389.0, -29.2, 388.7, -33.2, 370.6,
+-33.3, 359.7, -34.8, 355.3, -28.1, 348.9, -26.1, 355.1, -25.7, 355.0,
+-23.3, 361.1, -23.0, 384.2, -14.8, 395.3, -27.5, 407.4, -23.3, 361.1,
+-22.7, 362.5, -22.7, 362.5, -22.1, 363.9, -20.4, 368.4, -20.4, 368.4,
+-18.6, 372.9, -4.0, 410.0, -0.8, 409.0, 10.6, 447.0, 15.8, 451.1,
+10.4, 458.2, 10.2, 469.4, -3.1, 439.7, -25.9, 441.5, -25.5, 414.1,
+-26.2, 412.5, -26.2, 412.5, -27.0, 410.9, -27.8, 409.6, -27.2, 409.2,
+-27.5, 407.4, -14.8, 395.3, -23.0, 384.2, -23.3, 361.1, -15.8, 379.3,
+-17.1, 376.1, -18.6, 372.9, -18.6, 372.9, -20.4, 368.4, -20.4, 368.4,
+-22.1, 363.9, -22.2, 363.6, -22.2, 363.6, -22.3, 363.3, -20.5, 355.6,
+-24.5, 354.4, -27.3, 345.7, -27.8, 340.5, -25.4, 335.0, -24.8, 335.0,
+-15.9, 341.8, -16.5, 346.7, -6.7, 348.2, -5.9, 347.4, -2.9, 353.3,
+1.7, 354.3, 1.8, 359.9, 9.7, 365.8, 16.8, 365.3, 17.9, 368.2,
+19.8, 367.5, 22.8, 369.6, 22.8, 369.7, 22.8, 369.7, 22.8, 369.7,
+22.9, 371.5, 22.8, 371.5, 22.8, 373.4, 3.9, 378.1, 3.5, 379.5,
+-15.8, 379.3, 22.8, 373.4, 22.9, 387.5, 22.9, 387.5, 22.9, 401.7,
+21.1, 397.3, -5.3, 402.7, -5.3, 403.6, -10.6, 391.4, -10.6, 391.4,
+-15.8, 379.3, 3.5, 379.5, 3.9, 378.1, 22.8, 373.4, 89.4, 652.2,
+89.7, 652.3, 89.9, 652.3, 89.9, 652.3, 91.2, 668.6, 91.2, 668.6,
+92.5, 684.9, 87.9, 684.0, 90.7, 668.6, 89.4, 652.2, 765.8, 541.1,
+758.0, 521.3, 758.6, 521.0, 750.1, 501.5, 748.7, 497.4, 750.1, 492.7,
+749.0, 492.6, 766.0, 502.0, 785.1, 497.0, 786.1, 505.6, 793.3, 509.5,
+793.3, 509.5, 800.5, 513.5, 798.1, 513.8, 797.1, 519.5, 797.4, 525.2,
+799.3, 532.4, 795.3, 533.5, 793.2, 541.7, 782.4, 547.1, 773.3, 548.1,
+765.8, 541.1, 793.2, 541.7, 787.8, 562.4, 787.8, 562.4, 782.5, 583.1,
+778.3, 561.3, 774.1, 562.1, 765.8, 541.1, 773.3, 548.1, 782.4, 547.1,
+793.2, 541.7, 748.5, 575.4, 744.8, 572.5, 744.4, 572.8, 741.0, 569.6,
+734.7, 564.0, 734.7, 564.0, 728.5, 558.4, 727.5, 551.0, 726.0, 551.2,
+723.5, 543.9, 738.5, 556.7, 737.2, 558.8, 748.5, 575.4, 723.5, 543.9,
+723.0, 542.4, 722.7, 542.5, 722.5, 540.9, 722.2, 539.4, 721.3, 538.6,
+722.0, 537.9, 720.3, 528.5, 720.3, 528.5, 718.6, 519.2, 716.1, 504.7,
+710.9, 503.7, 713.5, 490.2, 718.1, 493.2, 722.6, 492.1, 723.2, 495.6,
+728.2, 499.0, 728.0, 499.4, 733.3, 502.3, 735.2, 505.7, 734.5, 506.4,
+737.1, 509.1, 736.7, 509.4, 739.2, 512.8, 741.2, 516.5, 744.6, 544.7,
+744.7, 547.0, 758.5, 571.7, 759.3, 578.0, 759.3, 578.0, 760.1, 584.4,
+754.8, 579.3, 754.3, 579.9, 748.5, 575.4, 737.2, 558.8, 738.5, 556.7,
+723.5, 543.9, 750.7, 506.1, 751.4, 512.5, 751.4, 512.5, 752.2, 518.9,
+750.9, 515.5, 748.4, 516.4, 744.7, 514.0, 740.3, 506.3, 738.7, 507.1,
+732.3, 500.6, 719.9, 478.6, 726.2, 469.7, 707.6, 456.6, 706.9, 452.6,
+706.9, 452.6, 706.2, 448.6, 730.0, 476.0, 730.8, 475.8, 750.7, 506.1,
+706.2, 448.6, 703.8, 435.3, 705.6, 434.7, 701.5, 422.0, 706.9, 425.7,
+706.9, 425.7, 712.4, 429.4, 714.7, 448.8, 726.1, 451.4, 745.7, 464.8,
+745.7, 465.1, 745.6, 465.3, 745.8, 465.5, 750.7, 468.2, 742.7, 479.3,
+748.1, 484.6, 749.8, 485.2, 747.0, 491.5, 749.0, 492.6, 750.1, 492.7,
+748.7, 497.4, 750.1, 501.5, 751.0, 503.5, 750.4, 503.8, 750.7, 506.1,
+730.8, 475.8, 730.0, 476.0, 706.2, 448.6, 835.7, 623.1, 836.7, 624.3,
+837.6, 623.4, 839.3, 624.0, 839.8, 624.1, 839.7, 624.4, 840.1, 624.8,
+841.3, 625.9, 841.2, 626.1, 842.2, 627.4, 838.6, 626.1, 838.1, 626.0,
+835.7, 623.1, 855.5, 586.8, 860.4, 591.7, 860.4, 591.7, 865.4, 596.6,
+880.6, 617.1, 878.7, 618.8, 895.8, 637.6, 867.6, 630.8, 867.6, 630.5,
+839.3, 624.0, 837.6, 623.4, 836.7, 624.3, 835.7, 623.1, 831.7, 622.2,
+828.2, 620.5, 827.8, 621.3, 835.7, 605.4, 839.2, 607.1, 850.6, 593.0,
+853.0, 589.9, 853.9, 586.4, 855.5, 586.8, 575.2, 385.9, 565.8, 372.2,
+563.7, 373.2, 556.4, 358.5, 548.0, 343.3, 545.6, 329.2, 536.7, 329.8,
+536.4, 326.7, 536.4, 326.7, 536.1, 323.7, 544.7, 334.1, 543.4, 335.2,
+550.6, 346.8, 562.9, 366.3, 565.0, 365.3, 575.2, 385.9, 626.3, 445.3,
+630.7, 459.4, 630.7, 459.4, 635.2, 473.4, 613.2, 440.7, 612.9, 440.9,
+590.6, 408.4, 585.9, 399.5, 595.0, 392.5, 589.2, 386.5, 612.9, 411.0,
+610.1, 414.7, 626.3, 445.3, 589.2, 386.5, 586.2, 380.6, 588.8, 379.2,
+588.3, 372.0, 587.4, 357.9, 582.3, 356.2, 586.5, 343.8, 591.1, 346.9,
+591.1, 346.9, 595.7, 350.0, 597.0, 350.8, 596.5, 351.7, 597.3, 353.4,
+586.5, 392.3, 611.8, 399.4, 626.3, 445.3, 610.1, 414.7, 612.9, 411.0,
+589.2, 386.5, 718.6, 519.2, 720.3, 528.5, 720.3, 528.5, 722.0, 537.9,
+711.8, 549.5, 701.7, 552.1, 703.4, 562.7, 704.0, 563.8, 701.3, 565.4,
+699.3, 568.1, 699.0, 569.1, 696.3, 568.4, 693.3, 568.6, 692.5, 565.5,
+695.3, 564.7, 697.3, 560.8, 700.6, 554.3, 700.6, 554.3, 704.0, 547.8,
+711.3, 533.5, 707.0, 525.6, 718.6, 519.2, 910.3, 563.5, 905.1, 556.1,
+905.1, 556.1, 899.8, 548.7, 901.3, 525.5, 896.4, 525.1, 893.0, 501.6,
+910.5, 526.3, 905.8, 531.9, 910.3, 563.5, 842.9, 516.0, 837.8, 511.5,
+837.8, 511.5, 832.8, 507.0, 832.6, 485.1, 818.1, 481.9, 797.4, 463.4,
+790.1, 450.1, 787.6, 451.4, 777.8, 439.4, 810.7, 477.4, 811.2, 477.0,
+842.9, 516.0, 846.8, 490.9, 830.0, 465.8, 831.1, 465.0, 813.2, 440.7,
+801.8, 415.0, 801.8, 415.0, 790.5, 389.3, 819.3, 429.5, 814.6, 432.8,
+838.7, 476.3, 842.8, 483.6, 842.0, 484.2, 846.8, 490.9, 485.3, 126.2,
+497.4, 124.9, 499.2, 128.7, 509.6, 123.7, 513.3, 124.0, 516.4, 125.4,
+516.9, 124.3, 518.5, 126.3, 518.5, 126.3, 520.2, 128.3, 527.6, 149.4,
+530.3, 148.5, 540.5, 168.7, 511.7, 166.9, 504.0, 153.5, 485.3, 126.2,
+540.5, 168.7, 542.3, 172.2, 542.3, 172.2, 544.1, 175.8, 540.5, 172.5,
+533.0, 178.0, 523.9, 183.4, 497.6, 169.5, 501.3, 162.4, 478.7, 141.4,
+471.8, 134.9, 471.2, 135.4, 464.8, 128.5, 465.1, 128.8, 465.7, 128.2,
+466.5, 128.1, 474.5, 124.3, 475.9, 127.1, 485.3, 126.2, 504.0, 153.5,
+511.7, 166.9, 540.5, 168.7, 804.5, 87.9, 824.8, 103.2, 825.5, 102.3,
+846.3, 117.0, 857.7, 124.9, 857.5, 125.2, 868.7, 133.4, 863.2, 129.3,
+863.1, 129.5, 857.5, 125.5, 831.0, 106.7, 830.4, 107.5, 804.5, 87.9,
+936.1, 245.9, 931.1, 239.5, 929.4, 240.9, 922.7, 235.8, 918.0, 232.3,
+918.1, 232.3, 913.4, 228.8, 876.6, 201.0, 876.6, 201.0, 839.8, 173.3,
+834.8, 169.5, 834.0, 165.0, 829.8, 165.7, 840.9, 163.7, 841.7, 168.3,
+853.6, 170.8, 864.6, 173.1, 868.4, 168.9, 875.6, 175.5, 909.7, 206.5,
+907.6, 209.3, 936.1, 245.9, 686.0, 149.5, 684.1, 135.7, 669.1, 137.8,
+652.1, 126.1, 638.8, 117.0, 638.8, 117.0, 625.6, 107.8, 618.0, 102.6,
+613.6, 104.6, 610.4, 97.3, 615.1, 96.3, 615.1, 96.3, 619.9, 95.3,
+629.9, 99.9, 628.7, 102.5, 637.5, 109.8, 644.5, 115.4, 644.5, 115.4,
+651.4, 121.1, 668.7, 135.3, 683.8, 133.2, 686.0, 149.5, 576.5, 124.0,
+580.8, 126.0, 580.8, 126.0, 585.0, 128.1, 586.0, 128.6, 586.0, 128.7,
+587.0, 129.2, 581.8, 126.6, 581.7, 126.7, 576.5, 124.0, 795.1, 271.6,
+794.1, 271.2, 796.8, 263.4, 798.5, 255.3, 799.9, 248.5, 802.0, 241.9,
+801.3, 241.7, 836.2, 260.0, 836.2, 260.0, 871.1, 278.3, 867.9, 276.4,
+867.0, 277.9, 862.9, 277.6, 829.0, 274.6, 826.3, 282.4, 795.1, 271.6,
+820.2, 241.9, 825.3, 245.5, 825.3, 245.5, 830.4, 249.0, 818.0, 241.3,
+817.2, 242.5, 804.0, 236.0, 810.9, 240.7, 812.5, 238.1, 820.2, 241.9,
+707.8, 186.5, 709.9, 187.9, 709.2, 189.3, 711.3, 190.3, 676.6, 173.2,
+676.6, 173.2, 641.9, 156.2, 623.4, 152.8, 624.9, 144.6, 607.9, 133.0,
+634.7, 139.2, 633.0, 146.5, 658.2, 159.9, 674.4, 168.6, 674.4, 168.6,
+690.7, 177.3, 699.2, 181.9, 699.6, 181.4, 707.8, 186.5, 625.1, 297.5,
+631.6, 301.3, 631.6, 301.3, 638.2, 305.2, 641.4, 305.5, 640.7, 312.7,
+643.3, 320.2, 631.5, 313.3, 629.5, 310.4, 625.1, 297.5, 444.3, 210.5,
+441.2, 208.2, 439.3, 206.5, 439.9, 203.6, 441.9, 192.6, 444.6, 193.1,
+449.3, 182.6, 449.3, 182.6, 449.3, 182.6, 449.3, 182.6, 449.3, 182.6,
+449.3, 182.6, 449.3, 182.6, 462.4, 196.0, 462.4, 196.0, 475.5, 209.3,
+500.5, 245.8, 507.0, 241.3, 538.5, 273.3, 538.9, 273.7, 538.9, 273.8,
+539.3, 274.3, 540.5, 275.8, 540.5, 275.8, 541.6, 277.4, 492.6, 244.5,
+492.1, 245.1, 444.3, 210.5, 626.5, 73.3, 662.7, 57.4, 662.7, 57.4,
+698.9, 41.5, 692.2, 43.1, 696.0, 54.8, 688.7, 58.0, 659.8, 70.7,
+638.4, 53.9, 626.5, 73.3, 688.7, 58.0, 684.0, 56.9, 681.7, 71.7,
+672.7, 84.0, 671.9, 85.1, 670.9, 84.4, 669.1, 84.8, 646.1, 89.7,
+646.1, 94.6, 623.1, 94.6, 612.1, 88.8, 611.6, 89.7, 600.1, 84.9,
+613.5, 79.6, 613.3, 79.1, 626.5, 73.3, 638.4, 53.9, 659.8, 70.7,
+688.7, 58.0, 682.9, 81.8, 678.3, 83.9, 673.3, 81.8, 672.7, 84.0,
+681.7, 71.7, 684.0, 56.9, 688.7, 58.0, 696.0, 54.8, 692.2, 43.1,
+698.9, 41.5, 701.1, 40.5, 701.1, 40.5, 703.4, 39.5, 695.5, 59.7,
+693.9, 59.1, 684.5, 78.6, 683.7, 80.2, 684.2, 81.2, 682.9, 81.8,
+438.1, 128.0, 441.8, 124.7, 441.4, 124.4, 444.8, 120.8, 470.4, 92.8,
+470.4, 92.8, 496.1, 64.9, 506.3, 53.8, 506.3, 53.8, 516.5, 42.7,
+528.8, 29.4, 525.8, 22.1, 541.1, 16.0, 543.1, 23.6, 543.1, 23.6,
+545.1, 31.1, 545.2, 32.4, 543.6, 32.5, 542.1, 33.9, 490.1, 80.9,
+490.4, 81.3, 438.1, 128.0, 444.8, 120.8, 441.4, 124.4, 441.8, 124.7,
+438.1, 128.0, 436.1, 130.2, 436.4, 132.0, 434.0, 132.5, 424.1, 132.5,
+424.1, 132.5, 414.2, 132.6, 424.2, 121.8, 428.9, 123.9, 444.8, 120.8,
+428.3, 100.5, 430.0, 102.1, 417.9, 114.9, 407.5, 129.4, 406.3, 131.1,
+406.3, 131.1, 405.1, 132.7, 401.3, 134.8, 401.5, 136.3, 399.5, 140.5,
+395.8, 140.0, 395.9, 136.0, 393.4, 131.0, 388.3, 125.4, 390.3, 123.5,
+387.3, 116.0, 385.9, 112.6, 386.4, 112.4, 385.5, 108.7, 405.6, 101.1,
+419.0, 91.7, 428.3, 100.5, 376.6, -4.2, 369.3, -14.8, 371.9, -18.7,
+362.0, -25.4, 362.3, -25.5, 362.3, -25.5, 362.6, -25.6, 369.8, -25.0,
+371.8, -27.7, 376.9, -24.1, 413.9, 15.4, 419.5, 10.2, 462.0, 44.4,
+443.6, 22.3, 435.3, 29.2, 408.7, 14.0, 392.6, 4.9, 390.5, 7.5,
+376.6, -4.2, 354.9, 111.6, 352.6, 104.8, 354.4, 104.1, 353.9, 96.6,
+353.5, 89.0, 353.5, 89.0, 353.0, 81.4, 352.5, 73.8, 356.0, 70.2,
+352.0, 66.3, 363.7, 84.6, 379.1, 99.0, 375.4, 102.9, 382.3, 117.9,
+382.3, 118.8, 393.4, 131.0, 395.9, 136.0, 395.8, 140.0, 399.5, 140.5,
+399.3, 141.0, 400.0, 142.2, 400.6, 142.2, 400.6, 148.2, 404.3, 148.1,
+408.1, 154.0, 408.2, 154.1, 408.0, 154.2, 407.9, 154.3, 404.6, 151.9,
+404.7, 151.7, 401.4, 149.1, 381.1, 132.7, 381.1, 132.7, 360.7, 116.3,
+357.8, 114.0, 355.9, 114.6, 354.9, 111.6, 354.1, 229.1, 355.0, 230.2,
+349.9, 234.6, 345.7, 240.1, 343.2, 243.5, 340.1, 246.3, 340.6, 246.9,
+334.1, 239.5, 330.2, 231.4, 333.8, 226.6, 336.9, 222.5, 349.0, 223.4,
+354.1, 229.1, 180.4, 458.9, 165.2, 479.0, 164.5, 478.5, 150.0, 499.1,
+148.8, 490.4, 148.8, 490.4, 147.6, 481.7, 151.1, 474.2, 155.4, 476.2,
+163.2, 470.8, 171.8, 464.8, 176.5, 467.1, 180.4, 458.9, 215.6, 319.8,
+227.5, 313.4, 240.1, 309.9, 239.3, 307.0, 239.9, 307.2, 240.2, 306.5,
+241.0, 306.0, 240.7, 308.1, 240.5, 309.0, 238.9, 309.9, 227.8, 315.9,
+216.7, 313.2, 215.6, 319.8, 222.2, 171.0, 230.1, 155.8, 230.1, 155.8,
+238.0, 140.7, 236.5, 144.5, 236.9, 144.6, 235.8, 148.5, 229.8, 160.1,
+225.6, 158.8, 222.2, 171.0, 176.4, 362.5, 165.4, 402.3, 165.4, 402.3,
+154.3, 442.2, 154.2, 419.4, 152.3, 419.4, 150.3, 396.7, 150.3, 396.6,
+150.3, 396.6, 150.4, 396.6, 163.4, 379.5, 177.8, 378.3, 176.4, 362.5,
+138.4, 232.7, 138.6, 227.7, 141.1, 225.7, 138.8, 222.8, 153.2, 201.8,
+147.7, 192.0, 167.7, 180.7, 169.5, 178.8, 169.6, 178.8, 170.7, 176.4,
+170.6, 175.3, 172.0, 175.3, 172.4, 174.0, 183.0, 160.3, 182.2, 159.7,
+192.0, 145.4, 212.5, 119.2, 212.1, 118.9, 232.3, 92.4, 204.5, 142.4,
+200.4, 140.1, 168.4, 187.8, 153.4, 210.2, 155.7, 212.2, 138.4, 232.7,
+470.8, 711.5, 474.2, 737.2, 474.2, 737.2, 477.6, 762.9, 477.9, 765.2,
+477.9, 765.3, 478.2, 767.6, 474.5, 739.6, 474.2, 739.6, 470.8, 711.5,
+498.0, 805.1, 500.3, 782.9, 501.2, 783.0, 504.3, 760.8, 504.4, 760.3,
+504.4, 760.3, 504.4, 759.9, 513.1, 698.6, 512.1, 698.4, 521.8, 637.3,
+523.2, 628.6, 524.2, 628.7, 526.6, 620.2, 535.0, 590.1, 535.0, 590.1,
+543.4, 560.1, 544.6, 555.9, 545.0, 555.9, 545.8, 551.6, 524.2, 668.1,
+523.8, 668.0, 501.9, 784.4, 500.0, 794.8, 499.1, 794.7, 498.0, 805.1,
+504.3, 760.8, 501.2, 783.0, 500.3, 782.9, 498.0, 805.1, 495.0, 826.5,
+495.9, 826.7, 491.9, 847.9, 492.0, 845.8, 491.4, 845.8, 490.8, 843.6,
+496.7, 802.1, 496.6, 802.0, 504.3, 760.8, 580.1, 422.7, 580.7, 423.6,
+580.7, 423.6, 581.3, 424.5, 581.4, 425.1, 580.6, 425.6, 580.8, 426.0,
+580.0, 424.7, 579.7, 424.1, 580.1, 422.7, 448.7, 409.5, 446.9, 389.4,
+446.9, 389.4, 445.0, 369.4, 445.1, 370.9, 445.7, 370.9, 446.4, 372.4,
+447.4, 374.6, 447.4, 374.6, 448.4, 376.8, 449.8, 380.1, 449.8, 380.1,
+451.3, 383.3, 452.7, 386.4, 452.7, 386.4, 454.1, 389.4, 463.1, 419.2,
+467.1, 418.0, 480.0, 446.7, 483.6, 433.2, 458.4, 430.8, 448.7, 409.5,
+480.0, 446.7, 480.5, 447.8, 480.5, 447.8, 481.0, 448.9, 468.8, 470.9,
+471.1, 472.2, 461.7, 495.7, 460.2, 498.5, 460.2, 498.5, 458.7, 501.2,
+457.9, 492.8, 452.4, 490.2, 455.6, 484.5, 452.1, 447.0, 452.1, 447.0,
+448.7, 409.5, 458.4, 430.8, 483.6, 433.2, 480.0, 446.7, 192.8, 698.4,
+199.2, 685.0, 199.2, 685.0, 205.6, 671.5, 205.7, 680.0, 212.5, 682.3,
+209.6, 688.4, 210.1, 726.6, 221.5, 730.2, 210.6, 764.9, 210.6, 767.8,
+209.7, 770.5, 210.2, 770.6, 208.8, 765.3, 204.7, 766.3, 199.2, 762.1,
+191.8, 731.2, 190.6, 729.9, 192.8, 698.4, 199.2, 762.1, 191.9, 756.3,
+192.1, 756.1, 184.6, 750.6, 182.7, 737.0, 182.7, 737.0, 180.9, 723.4,
+188.3, 722.5, 186.8, 710.9, 192.8, 698.4, 190.6, 729.9, 191.8, 731.2,
+199.2, 762.1, 236.2, 411.3, 233.8, 419.9, 235.6, 420.4, 234.9, 429.5,
+234.5, 434.5, 234.5, 434.5, 234.2, 439.5, 233.2, 453.4, 233.2, 453.4,
+232.2, 467.3, 231.7, 473.0, 231.7, 473.0, 231.3, 478.7, 229.9, 497.9,
+231.2, 498.1, 228.5, 517.2, 227.6, 517.0, 225.0, 523.0, 224.3, 529.2,
+235.1, 554.2, 204.3, 563.1, 199.8, 598.9, 204.3, 603.4, 194.9, 612.9,
+189.9, 626.9, 193.1, 620.6, 191.5, 619.8, 193.0, 612.6, 197.7, 591.0,
+197.7, 591.0, 202.3, 569.3, 204.0, 561.3, 204.0, 561.3, 205.8, 553.2,
+209.1, 537.8, 209.1, 537.8, 212.4, 522.3, 218.1, 495.7, 218.1, 495.7,
+223.8, 469.2, 230.0, 440.3, 228.3, 439.8, 236.2, 411.3, 348.6, 878.3,
+346.2, 840.7, 363.8, 822.4, 343.8, 803.1, 345.5, 776.0, 350.3, 776.3,
+356.8, 749.4, 361.1, 731.7, 358.3, 730.5, 365.4, 714.0, 361.6, 722.8,
+364.4, 724.0, 363.4, 734.1, 362.4, 743.8, 362.4, 743.8, 361.4, 753.6,
+360.9, 758.8, 360.9, 758.8, 360.3, 764.1, 360.0, 767.1, 360.0, 767.1,
+359.7, 770.2, 359.2, 775.3, 359.1, 775.3, 358.6, 780.4, 354.5, 820.5,
+354.5, 820.5, 350.4, 860.7, 349.5, 869.5, 347.6, 869.7, 348.6, 878.3,
+424.8, 241.2, 429.7, 281.8, 429.7, 281.8, 434.6, 322.4, 432.1, 313.3,
+429.0, 314.1, 423.4, 305.9, 419.6, 275.8, 424.1, 275.2, 424.7, 244.6,
+424.8, 242.9, 425.0, 242.8, 424.8, 241.2, 278.9, 391.8, 258.1, 432.7,
+256.9, 432.0, 234.8, 472.2, 233.1, 475.5, 230.4, 476.9, 231.3, 478.7,
+231.7, 473.0, 231.7, 473.0, 232.2, 467.3, 244.9, 442.6, 246.9, 443.6,
+261.6, 419.8, 270.2, 405.8, 271.4, 406.4, 278.9, 391.8, 142.6, 812.3,
+142.0, 745.0, 143.1, 745.0, 143.6, 677.7, 143.8, 660.2, 144.9, 660.2,
+143.9, 642.7, 143.9, 642.7, 143.9, 642.7, 143.9, 642.6, 144.5, 656.6,
+149.1, 656.4, 154.3, 670.2, 155.2, 672.8, 155.2, 673.1, 155.0, 675.9,
+150.4, 732.9, 149.8, 732.8, 144.7, 789.8, 143.7, 801.1, 142.5, 801.1,
+142.6, 812.3, 166.1, 703.6, 176.1, 732.7, 175.0, 733.1, 186.1, 761.7,
+187.3, 770.3, 187.3, 770.3, 188.5, 778.9, 183.8, 801.5, 180.9, 800.9,
+173.4, 823.0, 173.4, 823.1, 173.5, 823.2, 173.5, 823.2, 173.5, 823.2,
+173.4, 823.1, 173.4, 823.0, 171.5, 807.5, 171.5, 807.5, 169.5, 791.9,
+165.2, 757.5, 162.3, 757.6, 160.9, 723.1, 160.5, 713.5, 169.8, 709.3,
+166.1, 703.6, 160.9, 723.1, 152.3, 701.7, 157.9, 699.5, 155.0, 675.9,
+155.2, 673.1, 155.2, 672.8, 154.3, 670.2, 156.0, 674.9, 156.0, 674.9,
+157.8, 679.7, 162.5, 691.5, 162.0, 691.7, 166.1, 703.6, 169.8, 709.3,
+160.5, 713.5, 160.9, 723.1, 90.3, 188.5, 91.4, 191.9, 92.7, 191.9,
+92.6, 195.2, 97.0, 229.5, 104.1, 228.6, 115.5, 261.9, 120.9, 312.4,
+121.9, 312.3, 128.3, 362.6, 127.0, 342.9, 124.1, 343.2, 119.8, 323.7,
+119.2, 321.0, 119.2, 321.0, 118.7, 318.3, 114.6, 299.8, 114.6, 299.8,
+110.6, 281.3, 100.4, 234.9, 100.0, 235.0, 90.3, 188.5, 55.9, 88.2,
+58.2, 95.1, 58.2, 95.1, 60.6, 101.9, 51.6, 122.9, 61.3, 127.0,
+62.1, 152.1, 51.9, 122.6, 56.8, 120.2, 55.9, 88.2, 128.4, 435.2,
+112.5, 419.8, 115.6, 412.2, 96.5, 404.4, 88.8, 396.0, 89.7, 395.1,
+82.9, 385.9, 82.7, 385.3, 82.6, 385.4, 82.2, 384.9, 88.2, 391.3,
+88.1, 391.4, 94.0, 397.8, 111.2, 416.5, 120.2, 413.2, 128.4, 435.2,
+59.1, 353.3, 49.3, 339.9, 51.9, 336.6, 39.5, 326.5, 39.3, 325.6,
+39.4, 325.6, 39.1, 324.7, 38.9, 324.0, 39.0, 324.0, 38.9, 323.2,
+51.2, 297.2, 65.4, 294.0, 61.2, 270.1, 62.8, 266.8, 62.8, 266.8,
+64.4, 263.4, 63.2, 308.4, 74.8, 313.5, 59.1, 353.3, 64.4, 263.4,
+65.7, 260.7, 67.4, 260.4, 67.0, 257.9, 72.4, 272.6, 72.4, 272.6,
+77.7, 287.3, 83.2, 291.6, 76.6, 299.7, 76.0, 312.1, 76.0, 314.7,
+75.8, 314.6, 75.7, 317.2, 82.3, 326.3, 62.3, 336.1, 60.9, 355.8,
+60.0, 354.6, 60.0, 354.6, 59.1, 353.3, 74.8, 313.5, 63.2, 308.4,
+64.4, 263.4, 94.6, 422.5, 88.8, 404.2, 88.9, 404.1, 82.9, 385.9,
+89.7, 395.1, 88.8, 396.0, 96.5, 404.4, 108.9, 409.5, 105.8, 417.1,
+115.1, 429.8, 117.5, 432.9, 117.2, 433.1, 119.3, 436.5, 106.1, 431.9,
+102.1, 433.0, 94.6, 422.5, 119.3, 436.5, 130.4, 454.3, 130.0, 454.6,
+141.5, 472.1, 135.1, 490.1, 135.1, 490.1, 128.8, 508.0, 128.5, 505.7,
+125.4, 503.4, 124.8, 503.8, 124.8, 503.8, 124.6, 503.6, 124.4, 503.4,
+117.4, 500.8, 119.6, 494.7, 114.9, 486.1, 119.4, 483.8, 104.7, 454.3,
+94.6, 422.5, 102.1, 433.0, 106.1, 431.9, 119.3, 436.5, 93.3, 695.5,
+93.8, 702.6, 91.2, 708.1, 94.4, 709.6, 89.1, 714.5, 89.1, 714.5,
+83.8, 719.4, 83.9, 719.1, 79.8, 717.4, 75.8, 715.5, 68.6, 706.3,
+67.0, 706.5, 56.3, 701.2, 56.1, 701.0, 56.1, 701.0, 55.9, 700.7,
+61.0, 692.4, 52.8, 687.4, 49.7, 674.1, 69.7, 671.4, 74.7, 680.8,
+93.3, 695.5, 49.7, 674.1, 49.3, 672.3, 49.3, 672.3, 48.9, 670.5,
+58.1, 672.3, 70.0, 661.3, 72.2, 648.9, 80.8, 650.5, 80.8, 650.5,
+89.4, 652.2, 90.7, 668.6, 87.9, 684.0, 92.5, 684.9, 92.9, 690.2,
+92.9, 690.2, 93.3, 695.5, 74.7, 680.8, 69.7, 671.4, 49.7, 674.1,
+855.7, 644.3, 864.1, 655.0, 862.1, 657.6, 872.6, 665.6, 867.3, 669.8,
+867.3, 669.8, 861.9, 674.1, 855.1, 664.1, 839.5, 674.7, 817.1, 675.3,
+814.8, 664.3, 834.4, 648.1, 855.7, 644.3, 850.6, 593.0, 839.2, 607.1,
+835.7, 605.4, 827.8, 621.3, 824.4, 623.7, 821.5, 619.7, 815.2, 618.2,
+805.9, 616.0, 805.7, 616.5, 796.5, 613.7, 796.7, 613.2, 796.7, 613.2,
+796.8, 612.7, 806.7, 608.6, 806.9, 609.0, 817.0, 605.3, 833.8, 599.1,
+848.1, 589.4, 850.6, 593.0, 550.6, 346.8, 543.4, 335.2, 544.7, 334.1,
+536.1, 323.7, 535.4, 316.0, 538.8, 312.6, 534.6, 308.4, 560.5, 326.1,
+560.5, 326.1, 586.5, 343.8, 582.3, 356.2, 587.4, 357.9, 588.3, 372.0,
+570.6, 373.5, 567.0, 362.0, 550.6, 346.8, 588.3, 372.0, 588.8, 379.2,
+586.2, 380.6, 589.2, 386.5, 595.0, 392.5, 585.9, 399.5, 590.6, 408.4,
+582.9, 397.1, 582.9, 397.1, 575.2, 385.9, 565.0, 365.3, 562.9, 366.3,
+550.6, 346.8, 567.0, 362.0, 570.6, 373.5, 588.3, 372.0, 706.7, 478.9,
+709.8, 483.0, 713.1, 483.1, 713.0, 487.0, 713.1, 488.1, 712.7, 488.6,
+713.3, 489.1, 713.6, 489.3, 713.2, 490.0, 713.5, 490.2, 710.9, 503.7,
+716.1, 504.7, 718.6, 519.2, 707.0, 525.6, 711.3, 533.5, 704.0, 547.8,
+694.7, 516.5, 701.7, 512.8, 706.7, 478.9, 922.2, 514.6, 918.6, 541.0,
+913.8, 541.2, 915.0, 567.3, 913.1, 566.1, 913.1, 566.1, 911.3, 564.9,
+910.8, 564.5, 910.9, 564.3, 910.5, 563.7, 912.9, 551.4, 913.4, 551.5,
+916.3, 539.2, 916.9, 536.7, 916.9, 536.7, 917.5, 534.2, 919.9, 524.4,
+921.4, 524.5, 922.2, 514.6, 868.0, 523.0, 871.7, 528.3, 871.8, 528.1,
+875.4, 533.5, 880.0, 540.1, 880.9, 539.6, 884.7, 546.7, 882.9, 545.5,
+882.5, 544.0, 881.1, 544.3, 881.2, 544.2, 876.3, 539.0, 874.5, 539.8,
+875.5, 535.3, 864.1, 532.8, 853.8, 525.7, 847.5, 522.2, 848.3, 520.8,
+842.9, 516.0, 811.2, 477.0, 810.7, 477.4, 777.8, 439.4, 764.3, 422.8,
+764.3, 422.8, 750.9, 406.3, 794.4, 444.7, 792.0, 447.3, 833.2, 488.3,
+850.6, 505.7, 851.2, 505.1, 868.0, 523.0, 800.2, 369.8, 812.1, 386.9,
+808.4, 391.3, 823.9, 404.0, 850.1, 450.0, 852.8, 448.7, 884.2, 491.5,
+887.5, 513.7, 885.0, 514.4, 890.9, 536.0, 891.2, 536.4, 890.9, 536.6,
+891.0, 537.3, 856.2, 473.4, 856.3, 473.3, 821.7, 409.3, 810.9, 389.5,
+794.8, 382.7, 800.2, 369.8, 906.3, 160.9, 920.0, 170.9, 919.3, 171.9,
+933.6, 180.8, 935.5, 182.7, 933.6, 187.9, 934.2, 187.9, 907.8, 185.9,
+908.1, 182.4, 882.0, 176.8, 878.8, 176.1, 878.8, 176.1, 875.6, 175.5,
+868.4, 168.9, 864.6, 173.1, 853.6, 170.8, 877.1, 159.7, 881.0, 157.7,
+906.3, 160.9, 938.0, 227.0, 943.7, 234.1, 941.1, 238.1, 940.0, 248.8,
+940.2, 247.6, 937.7, 247.7, 936.1, 245.9, 907.6, 209.3, 909.7, 206.5,
+875.6, 175.5, 878.8, 176.1, 878.8, 176.1, 882.0, 176.8, 910.9, 200.7,
+914.7, 198.1, 938.0, 227.0, 882.0, 176.8, 908.1, 182.4, 907.8, 185.9,
+934.2, 187.9, 945.0, 197.5, 932.2, 225.8, 938.0, 227.0, 914.7, 198.1,
+910.9, 200.7, 882.0, 176.8, 913.4, 228.8, 924.8, 237.3, 924.8, 237.3,
+936.1, 245.9, 937.7, 247.7, 940.2, 247.6, 940.0, 248.8, 940.1, 248.9,
+940.0, 249.0, 940.1, 249.1, 934.5, 244.9, 934.5, 244.9, 929.0, 240.6,
+925.8, 238.2, 925.8, 238.2, 922.7, 235.8, 918.0, 232.3, 918.1, 232.3,
+913.4, 228.8, 681.9, 99.4, 674.2, 94.0, 674.0, 93.0, 669.1, 84.8,
+670.9, 84.4, 671.9, 85.1, 672.7, 84.0, 673.3, 81.8, 678.3, 83.9,
+682.9, 81.8, 696.7, 78.9, 697.8, 73.4, 710.6, 75.9, 721.9, 78.2,
+720.8, 83.6, 731.1, 91.4, 735.2, 94.5, 735.9, 98.2, 739.3, 97.6,
+715.3, 102.1, 714.6, 98.4, 689.9, 99.1, 685.9, 99.3, 684.6, 101.2,
+681.9, 99.4, 767.4, 197.3, 774.3, 201.8, 772.5, 209.7, 778.7, 210.2,
+766.2, 203.2, 766.9, 201.7, 753.8, 196.2, 720.3, 174.2, 722.5, 153.6,
+687.8, 150.7, 729.3, 154.2, 728.8, 172.1, 767.4, 197.3, 862.9, 277.6,
+867.0, 277.9, 867.9, 276.4, 871.1, 278.3, 874.9, 280.3, 875.3, 279.8,
+878.8, 282.3, 883.0, 285.2, 883.2, 288.4, 887.3, 288.2, 887.1, 288.3,
+886.8, 288.3, 886.8, 288.4, 870.8, 290.2, 867.9, 292.0, 856.1, 303.1,
+856.0, 302.9, 853.8, 304.2, 851.6, 305.3, 816.2, 322.3, 817.1, 324.5,
+780.8, 339.3, 782.3, 338.7, 780.3, 335.4, 782.0, 333.8, 789.5, 326.7,
+790.6, 327.8, 799.3, 321.8, 802.4, 319.6, 802.4, 319.6, 805.6, 317.4,
+834.2, 297.5, 833.0, 295.4, 862.9, 277.6, 657.9, 132.4, 654.5, 130.0,
+655.6, 128.2, 652.1, 126.1, 669.1, 137.8, 684.1, 135.7, 686.0, 149.5,
+686.9, 150.1, 686.9, 150.1, 687.8, 150.7, 720.3, 173.2, 720.2, 173.3,
+752.8, 195.8, 753.2, 196.1, 753.4, 195.9, 753.8, 196.2, 761.2, 199.3,
+760.5, 200.8, 767.2, 205.4, 764.9, 203.8, 764.9, 203.9, 762.5, 202.3,
+757.6, 199.0, 757.6, 199.0, 752.8, 195.8, 705.4, 164.1, 704.8, 164.9,
+657.9, 132.4, 625.6, 107.8, 638.8, 117.0, 638.8, 117.0, 652.1, 126.1,
+655.6, 128.2, 654.5, 130.0, 657.9, 132.4, 661.1, 135.9, 661.1, 135.9,
+664.3, 139.4, 666.3, 141.5, 666.0, 142.0, 668.3, 143.7, 662.0, 139.2,
+662.4, 138.7, 656.5, 133.7, 646.2, 125.1, 646.2, 125.1, 635.9, 116.5,
+630.8, 112.2, 631.3, 111.2, 625.6, 107.8, 529.6, 223.2, 534.4, 238.6,
+552.7, 230.3, 570.7, 244.0, 580.3, 256.7, 577.6, 260.4, 589.9, 269.3,
+583.2, 266.0, 583.2, 266.0, 576.5, 262.7, 552.2, 244.1, 537.3, 247.9,
+529.6, 223.2, 576.5, 262.7, 542.1, 245.5, 542.5, 244.8, 507.7, 228.3,
+491.6, 218.8, 485.1, 223.3, 475.5, 209.3, 462.4, 196.0, 462.4, 196.0,
+449.3, 182.6, 451.0, 184.5, 451.8, 183.9, 454.2, 185.1, 491.9, 204.2,
+494.1, 200.7, 529.6, 223.2, 537.3, 247.9, 552.2, 244.1, 576.5, 262.7,
+454.2, 185.1, 451.8, 183.9, 451.0, 184.5, 449.3, 182.6, 449.3, 182.6,
+449.3, 182.6, 449.3, 182.6, 436.6, 157.6, 430.6, 159.3, 408.0, 140.6,
+408.6, 140.5, 408.6, 140.5, 409.3, 140.3, 415.1, 145.5, 414.8, 145.9,
+420.4, 151.4, 434.4, 165.4, 434.4, 165.4, 448.4, 179.4, 451.3, 182.2,
+453.2, 181.7, 454.2, 185.1, 613.8, 318.3, 609.5, 309.1, 605.8, 299.8,
+605.3, 300.0, 605.1, 297.0, 605.1, 297.0, 604.9, 294.0, 605.3, 294.2,
+609.6, 291.0, 609.4, 288.2, 617.2, 292.8, 617.2, 292.8, 625.1, 297.5,
+629.5, 310.4, 631.5, 313.3, 643.3, 320.2, 644.4, 323.5, 643.2, 325.5,
+645.5, 326.8, 647.2, 327.6, 645.5, 330.3, 647.2, 331.9, 654.4, 331.7,
+655.2, 355.4, 663.1, 378.8, 631.4, 365.8, 636.9, 349.7, 613.8, 318.3,
+663.1, 378.8, 665.6, 386.2, 665.9, 393.6, 668.1, 393.5, 665.6, 394.7,
+663.0, 395.4, 663.1, 395.9, 657.0, 391.8, 657.0, 391.8, 650.9, 387.6,
+645.7, 384.7, 646.5, 383.2, 642.2, 378.7, 633.7, 347.0, 628.0, 348.5,
+613.8, 318.3, 636.9, 349.7, 631.4, 365.8, 663.1, 378.8, 499.3, 283.8,
+494.8, 281.0, 494.6, 281.3, 490.3, 278.3, 488.5, 277.1, 488.5, 277.1,
+486.8, 275.9, 493.1, 279.8, 493.2, 279.7, 499.3, 283.8, 745.7, 58.1,
+738.0, 64.9, 735.2, 63.3, 730.2, 71.7, 721.6, 76.1, 720.4, 73.8,
+710.6, 75.9, 697.8, 73.4, 696.7, 78.9, 682.9, 81.8, 684.2, 81.2,
+683.7, 80.2, 684.5, 78.6, 714.6, 67.2, 714.3, 62.8, 745.7, 58.1,
+684.5, 78.6, 693.9, 59.1, 695.5, 59.7, 703.4, 39.5, 715.2, 34.3,
+715.7, 35.3, 727.1, 29.1, 740.2, 32.1, 738.4, 39.7, 750.0, 50.0,
+751.2, 51.1, 752.8, 51.7, 752.7, 51.9, 750.0, 51.3, 749.2, 55.0,
+745.7, 58.1, 714.3, 62.8, 714.6, 67.2, 684.5, 78.6, 542.1, 33.9,
+543.6, 32.5, 545.2, 32.4, 545.1, 31.1, 553.1, 61.2, 553.1, 61.2,
+561.1, 91.2, 561.8, 66.9, 553.0, 66.6, 544.8, 42.1, 543.5, 38.0,
+542.0, 37.9, 542.1, 33.9, 407.5, 129.4, 417.9, 114.9, 430.0, 102.1,
+428.3, 100.5, 434.4, 92.1, 434.4, 92.1, 440.4, 83.6, 447.0, 74.6,
+447.0, 74.6, 453.5, 65.5, 459.8, 56.7, 459.8, 56.7, 466.1, 47.9,
+487.4, 18.9, 487.9, 19.2, 508.1, -10.5, 509.0, -11.6, 509.0, -11.6,
+509.8, -12.8, 518.4, -24.7, 515.8, -28.1, 527.0, -36.7, 527.2, -32.2,
+528.2, -32.2, 529.4, -27.8, 519.7, -10.1, 516.9, -11.6, 504.3, 4.5,
+492.4, 20.0, 492.4, 20.0, 480.4, 35.4, 462.3, 58.7, 462.3, 58.7,
+444.3, 82.0, 425.9, 105.7, 427.1, 106.7, 407.5, 129.4, 638.1, -51.9,
+648.3, -56.7, 681.7, -32.3, 682.2, -11.8, 659.3, 3.5, 661.0, 6.1,
+639.9, 23.9, 642.7, 22.6, 639.7, 16.2, 639.5, 8.4, 638.8, -21.7,
+627.0, -46.6, 638.1, -51.9, 590.4, 20.7, 590.0, 21.0, 591.9, 23.2,
+593.4, 25.8, 600.9, 38.2, 611.7, 42.7, 608.4, 50.5, 588.8, 67.1,
+588.0, 66.3, 569.1, 83.6, 568.2, 78.7, 569.0, 78.4, 567.3, 73.7,
+562.9, 63.6, 565.2, 62.6, 563.1, 51.4, 570.9, 33.5, 574.9, 33.8,
+590.4, 20.7, 563.1, 51.4, 560.4, 36.7, 560.4, 36.7, 557.7, 22.0,
+558.4, 22.2, 563.3, 3.6, 568.9, -14.7, 567.4, -13.1, 573.7, -6.8,
+578.5, 1.0, 583.6, 9.6, 583.6, 9.6, 588.8, 18.1, 589.6, 19.4,
+590.7, 20.3, 590.4, 20.7, 574.9, 33.8, 570.9, 33.5, 563.1, 51.4,
+381.2, 91.7, 379.0, 83.0, 379.0, 83.0, 376.8, 74.3, 398.1, 75.3,
+406.4, 74.7, 420.2, 60.1, 422.4, 62.5, 422.7, 62.3, 424.5, 65.0,
+424.7, 65.3, 424.7, 65.3, 424.9, 65.5, 429.1, 70.4, 432.4, 75.9,
+433.3, 75.3, 410.5, 89.0, 407.1, 92.2, 381.2, 91.7, 433.3, 75.3,
+436.2, 74.9, 441.1, 80.3, 440.4, 83.6, 434.4, 92.1, 434.4, 92.1,
+428.3, 100.5, 419.0, 91.7, 405.6, 101.1, 385.5, 108.7, 383.3, 100.2,
+383.3, 100.2, 381.2, 91.7, 407.1, 92.2, 410.5, 89.0, 433.3, 75.3,
+389.3, 14.2, 383.0, 5.0, 383.0, 5.0, 376.6, -4.2, 390.5, 7.5,
+392.6, 4.9, 408.7, 14.0, 405.6, 9.6, 396.6, 18.3, 389.3, 14.2,
+436.9, -32.1, 422.6, -36.7, 419.5, -26.5, 402.0, -21.5, 401.0, -21.6,
+400.6, -21.2, 400.1, -21.7, 390.0, -26.2, 383.0, -17.7, 376.9, -24.1,
+371.8, -27.7, 369.8, -25.0, 362.6, -25.6, 370.0, -27.5, 370.0, -27.5,
+377.4, -29.4, 407.0, -32.0, 410.3, -40.7, 436.9, -32.1, 377.4, -29.4,
+388.0, -32.1, 387.8, -33.8, 398.6, -34.9, 399.3, -35.1, 399.3, -34.9,
+400.0, -35.1, 407.0, -36.9, 407.0, -37.0, 414.0, -38.8, 441.9, -46.0,
+441.9, -45.9, 469.8, -53.0, 471.6, -53.4, 471.6, -53.4, 473.5, -53.9,
+508.4, -62.5, 508.4, -62.5, 543.4, -71.1, 543.5, -71.2, 546.8, -66.8,
+549.1, -67.4, 548.5, -66.6, 548.5, -66.6, 548.0, -65.9, 523.9, -58.0,
+523.8, -58.5, 499.5, -51.2, 493.8, -49.4, 493.8, -49.4, 488.0, -47.7,
+474.0, -43.4, 474.0, -43.4, 460.0, -39.1, 448.4, -35.6, 448.7, -33.5,
+436.9, -32.1, 410.3, -40.7, 407.0, -32.0, 377.4, -29.4, 413.6, -20.3,
+407.8, -20.9, 407.8, -20.9, 402.0, -21.5, 419.5, -26.5, 422.6, -36.7,
+436.9, -32.1, 448.7, -33.5, 448.4, -35.6, 460.0, -39.1, 437.7, -27.9,
+437.4, -27.6, 413.6, -20.3, 262.6, 59.6, 266.1, 47.2, 266.1, 47.2,
+269.6, 34.9, 275.0, 35.4, 273.9, 47.9, 278.3, 61.0, 280.9, 68.7,
+282.6, 68.5, 283.5, 76.5, 283.3, 74.8, 281.6, 75.0, 279.7, 73.4,
+278.0, 72.0, 278.0, 72.0, 276.3, 70.7, 269.4, 65.1, 263.8, 66.2,
+262.6, 59.6, 258.3, 282.1, 242.0, 274.1, 239.8, 263.1, 225.8, 266.0,
+226.1, 264.6, 220.4, 263.4, 215.0, 260.7, 215.6, 260.1, 215.2, 259.7,
+215.5, 258.6, 231.4, 266.1, 230.9, 267.1, 246.4, 275.6, 252.4, 278.8,
+252.2, 279.3, 258.3, 282.1, 328.2, 263.3, 327.4, 271.4, 317.3, 278.9,
+316.7, 278.6, 304.2, 270.5, 309.3, 262.4, 302.0, 246.3, 292.6, 225.6,
+289.8, 226.5, 283.1, 204.9, 281.7, 200.4, 287.8, 196.8, 285.8, 194.3,
+306.2, 219.1, 302.8, 221.9, 319.8, 249.5, 324.0, 256.4, 328.9, 256.9,
+328.2, 263.3, 302.0, 246.3, 309.3, 262.4, 304.2, 270.5, 316.7, 278.6,
+317.4, 281.6, 313.8, 282.4, 310.9, 286.3, 304.9, 294.2, 305.5, 301.5,
+298.9, 302.1, 287.0, 303.2, 286.3, 295.9, 273.8, 289.7, 273.2, 289.5,
+272.7, 289.1, 272.7, 289.2, 286.8, 267.4, 283.3, 250.8, 302.0, 246.3,
+272.7, 289.2, 272.7, 289.2, 272.6, 289.1, 272.4, 289.1, 272.4, 286.8,
+267.9, 286.8, 263.4, 284.6, 262.5, 271.2, 266.8, 270.9, 270.2, 257.1,
+276.7, 231.0, 269.4, 209.6, 283.1, 204.9, 289.8, 226.5, 292.6, 225.6,
+302.0, 246.3, 283.3, 250.8, 286.8, 267.4, 272.7, 289.2, 355.1, 114.2,
+354.4, 113.6, 355.4, 111.8, 354.9, 111.6, 355.9, 114.6, 357.8, 114.0,
+360.7, 116.3, 360.6, 115.1, 357.3, 116.0, 355.1, 114.2, 290.3, 313.5,
+264.4, 347.9, 264.3, 347.9, 238.3, 382.2, 230.2, 388.0, 231.9, 390.5,
+225.7, 398.9, 200.5, 426.6, 203.0, 428.9, 180.4, 458.9, 176.5, 467.1,
+171.8, 464.8, 163.2, 470.8, 180.2, 443.2, 183.7, 445.4, 204.3, 420.0,
+243.4, 371.5, 243.4, 371.5, 282.5, 323.1, 284.3, 320.9, 284.3, 320.9,
+286.0, 318.8, 288.1, 316.1, 288.2, 316.2, 290.3, 313.5, 176.4, 420.6,
+168.2, 442.5, 164.8, 441.2, 153.1, 461.8, 153.0, 462.3, 152.8, 462.2,
+152.7, 462.6, 167.5, 420.8, 166.8, 420.5, 182.4, 379.0, 181.0, 382.6,
+181.8, 382.9, 181.3, 386.7, 178.8, 403.7, 182.3, 404.9, 176.4, 420.6,
+177.5, 256.5, 178.6, 254.5, 179.3, 254.6, 179.6, 252.5, 203.3, 213.2,
+200.9, 211.7, 222.2, 171.0, 225.6, 158.8, 229.8, 160.1, 235.8, 148.5,
+235.5, 149.5, 235.5, 149.5, 235.3, 150.5, 230.0, 160.7, 229.8, 160.6,
+224.3, 170.6, 214.9, 187.9, 214.9, 187.9, 205.5, 205.2, 192.6, 228.8,
+192.5, 228.8, 179.6, 252.5, 179.3, 254.6, 178.6, 254.5, 177.5, 256.5,
+146.0, 348.1, 145.8, 345.7, 145.5, 345.7, 145.6, 343.2, 146.0, 329.6,
+141.9, 317.5, 146.4, 316.0, 151.6, 306.0, 151.6, 306.0, 156.8, 296.1,
+155.4, 298.3, 156.2, 298.9, 155.7, 301.6, 154.8, 305.8, 154.8, 305.8,
+154.0, 309.9, 150.0, 329.0, 150.8, 329.2, 146.0, 348.1, 159.0, 237.4,
+147.8, 258.1, 146.3, 257.4, 136.6, 278.7, 137.5, 255.7, 137.5, 255.7,
+138.4, 232.7, 155.7, 212.2, 153.4, 210.2, 168.4, 187.8, 169.4, 188.0,
+167.3, 213.8, 159.0, 237.4, 168.4, 187.8, 200.4, 140.1, 204.5, 142.4,
+232.3, 92.4, 235.7, 87.8, 236.4, 88.2, 239.2, 83.3, 219.1, 123.5,
+219.1, 123.5, 199.0, 163.7, 206.9, 171.3, 179.0, 200.6, 159.0, 237.4,
+167.3, 213.8, 169.4, 188.0, 168.4, 187.8, 471.9, 840.0, 468.5, 791.8,
+468.5, 791.8, 465.1, 743.6, 463.7, 718.3, 464.4, 718.2, 463.6, 692.9,
+463.5, 690.2, 463.4, 690.2, 463.3, 687.6, 463.2, 686.9, 463.2, 686.2,
+463.2, 686.2, 463.2, 686.2, 463.2, 686.9, 463.3, 687.6, 463.4, 690.2,
+463.5, 690.2, 463.6, 692.9, 467.8, 766.4, 469.7, 766.4, 471.9, 840.0,
+704.6, 772.1, 709.6, 768.3, 709.0, 767.6, 713.4, 763.0, 716.6, 759.7,
+716.6, 759.7, 719.8, 756.4, 725.4, 750.5, 725.4, 750.5, 731.1, 744.7,
+735.2, 740.4, 735.2, 740.4, 739.3, 736.1, 751.8, 723.2, 750.1, 721.0,
+764.3, 710.3, 765.7, 712.6, 766.5, 712.4, 767.2, 714.9, 768.1, 717.4,
+768.7, 717.2, 770.2, 719.5, 739.2, 747.8, 738.0, 746.6, 704.6, 772.1,
+530.8, 680.6, 529.6, 688.2, 529.9, 688.2, 529.0, 695.8, 526.4, 716.5,
+526.4, 716.5, 523.8, 737.3, 519.5, 772.2, 519.5, 772.2, 515.2, 807.1,
+512.6, 828.3, 512.6, 828.3, 510.0, 849.6, 508.0, 865.3, 511.1, 866.3,
+506.1, 880.9, 505.7, 882.1, 499.2, 880.7, 499.2, 881.1, 505.3, 820.4,
+508.7, 820.8, 518.2, 760.4, 524.5, 720.5, 524.2, 720.5, 530.8, 680.6,
+504.4, 759.9, 504.4, 760.3, 504.4, 760.3, 504.3, 760.8, 496.6, 802.0,
+496.7, 802.1, 490.8, 843.6, 489.1, 837.4, 485.8, 836.4, 487.4, 831.1,
+486.4, 819.9, 486.4, 819.9, 485.3, 808.6, 485.3, 808.6, 483.9, 793.3,
+482.5, 778.1, 485.1, 756.8, 480.5, 747.8, 494.2, 736.4, 491.4, 738.7,
+499.5, 748.0, 504.4, 759.9, 494.2, 736.4, 501.4, 685.9, 537.8, 673.8,
+521.8, 637.3, 512.1, 698.4, 513.1, 698.6, 504.4, 759.9, 499.5, 748.0,
+491.4, 738.7, 494.2, 736.4, 572.6, 410.9, 576.4, 416.8, 576.4, 416.8,
+580.1, 422.7, 579.7, 424.1, 580.0, 424.7, 580.8, 426.0, 580.2, 430.4,
+579.6, 430.3, 578.5, 434.6, 577.9, 436.5, 578.4, 438.1, 577.4, 438.4,
+563.8, 442.5, 563.3, 440.9, 549.3, 443.3, 545.6, 443.9, 543.5, 442.4,
+541.9, 444.6, 555.1, 426.2, 553.8, 421.7, 572.6, 410.9, 482.0, 772.1,
+480.0, 750.5, 480.1, 750.5, 478.0, 728.9, 478.3, 721.1, 484.7, 720.8,
+484.0, 713.4, 486.7, 742.4, 492.0, 746.3, 482.0, 772.1, 446.9, 345.2,
+448.3, 351.9, 446.0, 352.4, 445.1, 359.6, 444.8, 361.9, 444.1, 362.0,
+444.5, 364.1, 443.9, 357.8, 443.9, 357.8, 443.4, 351.4, 443.8, 348.2,
+447.5, 347.8, 446.9, 345.2, 406.2, 718.4, 412.9, 726.6, 407.7, 747.1,
+403.8, 747.8, 396.2, 749.3, 393.5, 735.4, 383.3, 722.9, 376.1, 714.2,
+374.3, 704.6, 369.0, 705.6, 385.7, 702.3, 395.6, 705.4, 406.2, 718.4,
+451.5, 559.9, 451.9, 567.7, 451.9, 567.7, 452.4, 575.5, 452.6, 578.7,
+452.6, 578.7, 452.8, 581.9, 453.1, 588.3, 453.1, 588.3, 453.5, 594.6,
+455.3, 625.3, 455.3, 625.3, 457.1, 655.9, 457.9, 669.3, 457.9, 669.3,
+458.7, 682.6, 460.2, 708.3, 460.1, 708.3, 461.7, 734.0, 463.2, 759.1,
+463.3, 759.1, 464.9, 784.3, 464.9, 784.9, 464.8, 784.9, 464.7, 785.5,
+463.1, 759.8, 463.2, 759.7, 461.7, 734.0, 460.1, 708.3, 460.2, 708.3,
+458.7, 682.6, 457.9, 669.3, 457.9, 669.3, 457.1, 655.9, 455.3, 625.3,
+455.3, 625.3, 453.5, 594.6, 453.1, 588.3, 453.1, 588.3, 452.8, 581.9,
+452.6, 578.7, 452.6, 578.7, 452.4, 575.5, 451.9, 567.7, 451.9, 567.7,
+451.5, 559.9, 358.6, 780.4, 359.1, 775.3, 359.2, 775.3, 359.7, 770.2,
+360.0, 767.1, 360.0, 767.1, 360.3, 764.1, 360.9, 758.8, 360.9, 758.8,
+361.4, 753.6, 362.4, 743.8, 362.4, 743.8, 363.4, 734.1, 364.4, 724.0,
+361.6, 722.8, 365.4, 714.0, 365.5, 713.7, 365.5, 713.7, 365.5, 713.5,
+364.5, 723.8, 364.5, 723.8, 363.4, 734.1, 362.4, 743.8, 362.4, 743.8,
+361.4, 753.6, 360.9, 758.8, 360.9, 758.8, 360.3, 764.1, 360.0, 767.1,
+360.0, 767.1, 359.7, 770.2, 359.2, 775.3, 359.1, 775.3, 358.6, 780.4,
+419.4, 196.3, 422.1, 218.7, 422.1, 218.7, 424.8, 241.2, 425.0, 242.8,
+424.8, 242.9, 424.7, 244.6, 423.4, 234.8, 423.6, 234.8, 422.5, 225.0,
+421.0, 210.6, 421.3, 210.6, 419.4, 196.3, 356.8, 749.4, 350.3, 776.3,
+345.5, 776.0, 343.8, 803.1, 354.5, 813.4, 336.9, 831.8, 329.9, 860.4,
+330.6, 857.7, 330.0, 857.6, 330.6, 854.9, 343.4, 802.1, 343.4, 802.1,
+356.8, 749.4, 369.0, 519.0, 376.4, 514.0, 386.1, 528.3, 403.2, 537.7,
+403.4, 537.8, 403.4, 537.8, 403.6, 538.0, 405.6, 539.1, 407.7, 539.3,
+407.6, 540.2, 405.2, 559.0, 409.8, 571.4, 398.6, 577.3, 394.5, 579.5,
+387.8, 566.8, 377.0, 556.3, 376.7, 556.1, 376.5, 556.1, 376.5, 555.8,
+372.5, 537.4, 363.0, 523.0, 369.0, 519.0, 376.5, 555.8, 376.3, 552.5,
+373.2, 552.6, 370.0, 549.5, 362.3, 542.1, 362.3, 542.1, 354.7, 534.6,
+341.8, 522.1, 341.8, 522.1, 329.0, 509.6, 314.1, 495.1, 316.5, 484.7,
+299.2, 480.6, 336.5, 489.3, 341.5, 492.2, 369.0, 519.0, 363.0, 523.0,
+372.5, 537.4, 376.5, 555.8, 403.2, 537.7, 386.1, 528.3, 376.4, 514.0,
+369.0, 519.0, 341.5, 492.2, 336.5, 489.3, 299.2, 480.6, 295.0, 478.2,
+296.3, 475.1, 292.1, 473.7, 302.0, 476.9, 301.3, 479.0, 310.5, 484.3,
+356.8, 511.0, 357.2, 510.5, 403.2, 537.7, 324.0, 409.5, 317.7, 409.2,
+311.5, 396.8, 312.2, 396.0, 316.9, 390.7, 323.6, 396.7, 334.9, 397.3,
+338.5, 397.5, 340.5, 399.4, 342.0, 397.7, 335.1, 405.5, 332.6, 410.0,
+324.0, 409.5, 278.7, 425.6, 285.6, 411.6, 288.9, 412.4, 292.4, 397.5,
+267.8, 486.1, 267.7, 486.1, 242.9, 574.7, 250.8, 548.1, 249.4, 547.7,
+255.8, 520.7, 267.2, 473.2, 267.2, 473.2, 278.6, 425.7, 278.7, 425.6,
+278.7, 425.6, 278.7, 425.6, 278.6, 425.7, 275.1, 429.0, 276.4, 430.3,
+274.1, 435.0, 273.4, 436.3, 273.4, 436.3, 272.8, 437.6, 262.3, 459.0,
+262.3, 459.0, 251.8, 480.4, 239.7, 505.1, 242.2, 506.7, 227.6, 529.8,
+228.1, 523.5, 229.2, 517.3, 228.5, 517.2, 231.2, 498.1, 229.9, 497.9,
+231.3, 478.7, 230.4, 476.9, 233.1, 475.5, 234.8, 472.2, 256.1, 448.4,
+255.5, 447.6, 278.6, 425.7, 234.8, 472.2, 256.9, 432.0, 258.1, 432.7,
+278.9, 391.8, 294.0, 364.3, 294.0, 364.3, 309.1, 336.7, 309.4, 336.0,
+309.5, 336.0, 309.8, 335.3, 301.3, 366.5, 302.9, 367.1, 292.4, 397.5,
+288.9, 412.4, 285.6, 411.6, 278.7, 425.6, 278.7, 425.6, 278.7, 425.6,
+278.6, 425.7, 255.5, 447.6, 256.1, 448.4, 234.8, 472.2, 173.4, 823.0,
+173.4, 823.1, 173.5, 823.2, 173.5, 823.2, 169.0, 836.5, 163.8, 836.8,
+164.5, 849.8, 156.3, 851.7, 151.6, 841.8, 142.6, 830.7, 142.7, 830.7,
+142.6, 826.8, 142.6, 822.9, 142.6, 817.6, 142.3, 817.6, 142.6, 812.3,
+142.5, 801.1, 143.7, 801.1, 144.7, 789.8, 143.9, 791.3, 147.5, 793.0,
+150.3, 796.2, 161.8, 809.6, 161.9, 809.6, 173.4, 823.0, 119.8, 323.7,
+124.1, 343.2, 127.0, 342.9, 128.3, 362.6, 128.7, 366.0, 129.2, 369.3,
+129.2, 369.3, 130.6, 382.7, 130.6, 382.7, 132.1, 396.2, 124.8, 369.2,
+120.5, 370.3, 108.8, 344.5, 107.3, 335.3, 122.1, 325.8, 119.8, 323.7,
+63.0, 185.6, 57.2, 200.1, 54.3, 199.5, 51.4, 214.6, 49.9, 210.5,
+49.9, 210.5, 48.4, 206.4, 53.0, 194.9, 65.5, 192.5, 63.0, 185.6,
+105.4, 363.9, 106.3, 366.5, 106.3, 366.5, 107.3, 369.1, 122.3, 400.9,
+118.4, 402.7, 129.6, 436.3, 129.3, 435.7, 129.0, 435.8, 128.4, 435.2,
+120.2, 413.2, 111.2, 416.5, 94.0, 397.8, 89.6, 384.1, 97.9, 380.0,
+105.4, 363.9, 94.0, 397.8, 88.1, 391.4, 88.2, 391.3, 82.2, 384.9,
+79.3, 381.0, 75.7, 380.0, 76.5, 377.0, 76.4, 376.3, 75.1, 375.7,
+75.3, 375.5, 75.5, 375.2, 74.8, 374.8, 74.3, 374.1, 74.1, 374.0,
+74.2, 373.9, 74.1, 373.8, 75.7, 359.1, 74.5, 359.0, 75.0, 344.2,
+75.0, 344.2, 90.3, 334.3, 90.8, 323.7, 98.1, 343.8, 98.1, 343.8,
+105.4, 363.9, 97.9, 380.0, 89.6, 384.1, 94.0, 397.8, 804.3, 668.0,
+797.3, 661.3, 797.3, 661.3, 790.4, 654.6, 793.9, 640.3, 795.5, 639.4,
+793.8, 625.1, 794.2, 623.3, 794.7, 621.4, 794.7, 621.4, 795.2, 621.3,
+796.2, 619.0, 795.5, 618.0, 795.4, 618.0, 796.0, 615.9, 796.5, 613.7,
+805.7, 616.5, 805.9, 616.0, 815.2, 618.2, 818.6, 641.9, 817.6, 648.5,
+804.3, 668.0, 815.2, 618.2, 821.5, 619.7, 824.4, 623.7, 827.8, 621.3,
+828.2, 620.5, 831.7, 622.2, 835.7, 623.1, 838.1, 626.0, 838.6, 626.1,
+842.2, 627.4, 849.0, 635.9, 849.0, 635.9, 855.7, 644.3, 834.4, 648.1,
+814.8, 664.3, 817.1, 675.3, 814.5, 675.3, 814.5, 675.3, 811.9, 675.4,
+809.7, 676.2, 808.1, 671.7, 804.3, 668.0, 817.6, 648.5, 818.6, 641.9,
+815.2, 618.2, 813.9, 548.1, 824.6, 557.5, 824.6, 557.5, 835.3, 566.8,
+837.5, 568.8, 837.4, 568.9, 839.5, 570.9, 839.5, 570.9, 847.4, 578.7,
+855.2, 586.5, 834.2, 567.7, 833.9, 567.9, 813.9, 548.1, 703.1, 474.3,
+704.9, 476.6, 704.9, 476.6, 706.7, 478.9, 701.7, 512.8, 694.7, 516.5,
+704.0, 547.8, 700.6, 554.3, 700.6, 554.3, 697.3, 560.8, 694.3, 542.7,
+698.6, 542.0, 699.8, 523.1, 700.3, 515.6, 700.3, 515.6, 700.8, 508.2,
+701.6, 497.2, 701.6, 497.2, 702.3, 486.2, 702.5, 483.1, 702.5, 483.1,
+702.7, 480.0, 702.8, 478.7, 702.8, 478.7, 702.9, 477.3, 703.0, 475.8,
+703.5, 474.5, 703.1, 474.3, 918.1, 484.0, 921.1, 492.4, 921.1, 492.4,
+924.1, 500.9, 926.8, 505.5, 923.1, 507.7, 922.2, 514.6, 921.4, 524.5,
+919.9, 524.4, 917.5, 534.2, 914.2, 509.6, 913.3, 508.2, 918.1, 484.0,
+765.5, 384.9, 812.7, 448.1, 811.2, 449.2, 859.8, 511.3, 863.9, 517.2,
+863.9, 517.2, 868.0, 523.0, 851.2, 505.1, 850.6, 505.7, 833.2, 488.3,
+796.5, 438.8, 810.5, 423.8, 765.5, 384.9, 833.2, 488.3, 792.0, 447.3,
+794.4, 444.7, 750.9, 406.3, 745.1, 399.2, 746.0, 393.0, 739.4, 392.2,
+751.7, 387.5, 751.7, 387.5, 764.0, 382.8, 763.8, 383.1, 764.8, 383.9,
+765.5, 384.9, 810.5, 423.8, 796.5, 438.8, 833.2, 488.3, 789.5, 387.2,
+786.9, 381.2, 788.2, 380.2, 784.2, 375.2, 792.1, 372.2, 792.1, 372.2,
+799.9, 369.3, 800.0, 369.2, 800.1, 369.5, 800.2, 369.8, 794.8, 382.7,
+810.9, 389.5, 821.7, 409.3, 820.1, 403.3, 814.9, 404.7, 808.2, 400.0,
+798.9, 393.6, 797.2, 395.2, 789.5, 387.2, 875.3, 138.2, 890.8, 149.6,
+890.8, 149.6, 906.3, 160.9, 881.0, 157.7, 877.1, 159.7, 853.6, 170.8,
+841.7, 168.3, 840.9, 163.7, 829.8, 165.7, 822.6, 162.9, 818.5, 154.8,
+816.9, 156.1, 832.1, 143.9, 836.9, 149.9, 857.0, 143.8, 866.1, 141.0,
+868.0, 135.7, 875.3, 138.2, 839.8, 173.3, 876.6, 201.0, 876.6, 201.0,
+913.4, 228.8, 918.1, 232.3, 918.0, 232.3, 922.7, 235.8, 881.2, 204.6,
+881.1, 204.8, 839.8, 173.3, 774.9, 157.7, 800.5, 192.0, 799.6, 193.6,
+817.9, 232.4, 812.6, 229.4, 812.6, 229.4, 807.2, 226.4, 799.9, 216.7,
+801.9, 215.2, 796.7, 204.0, 785.8, 180.8, 789.9, 177.8, 774.9, 157.7,
+742.1, 168.4, 748.6, 172.2, 747.3, 174.3, 752.5, 180.3, 759.9, 188.8,
+758.0, 193.0, 767.4, 197.3, 728.8, 172.1, 729.3, 154.2, 687.8, 150.7,
+686.9, 150.1, 686.9, 150.1, 686.0, 149.5, 683.8, 133.2, 668.7, 135.3,
+651.4, 121.1, 699.3, 137.2, 698.0, 142.6, 742.1, 168.4, 799.3, 321.8,
+790.6, 327.8, 789.5, 326.7, 782.0, 333.8, 782.8, 333.4, 782.3, 332.4,
+782.6, 330.9, 783.7, 325.5, 781.7, 321.8, 784.9, 320.1, 790.0, 317.2,
+799.1, 322.4, 799.3, 321.8, 664.3, 139.4, 661.1, 135.9, 661.1, 135.9,
+657.9, 132.4, 704.8, 164.9, 705.4, 164.1, 752.8, 195.8, 716.3, 163.2,
+711.0, 169.1, 669.2, 142.5, 666.8, 140.9, 666.5, 141.3, 664.3, 139.4,
+658.2, 159.9, 633.0, 146.5, 634.7, 139.2, 607.9, 133.0, 589.3, 120.3,
+589.3, 120.3, 570.7, 107.6, 572.4, 108.8, 572.5, 108.7, 574.2, 109.7,
+593.2, 121.1, 593.2, 121.1, 612.2, 132.4, 635.2, 146.2, 637.3, 162.6,
+658.2, 159.9, 549.5, 216.0, 560.1, 230.0, 560.1, 230.0, 570.7, 244.0,
+552.7, 230.3, 534.4, 238.6, 529.6, 223.2, 494.1, 200.7, 491.9, 204.2,
+454.2, 185.1, 453.2, 181.7, 451.3, 182.2, 448.4, 179.4, 466.8, 183.8,
+466.3, 185.9, 484.2, 192.4, 511.4, 202.2, 511.4, 202.2, 538.5, 212.0,
+544.0, 214.0, 545.7, 212.3, 549.5, 216.0, 468.4, 248.2, 463.1, 244.0,
+464.6, 242.2, 460.7, 236.1, 452.5, 223.3, 456.0, 216.4, 444.3, 210.5,
+492.1, 245.1, 492.6, 244.5, 541.6, 277.4, 542.2, 278.2, 542.2, 278.3,
+542.8, 279.0, 536.5, 273.8, 530.8, 280.7, 518.8, 282.5, 491.3, 271.0,
+492.2, 267.1, 468.4, 248.2, 518.8, 282.5, 510.0, 283.7, 505.9, 288.8,
+501.2, 285.0, 500.2, 284.4, 500.2, 284.4, 499.3, 283.8, 493.2, 279.7,
+493.1, 279.8, 486.8, 275.9, 486.2, 275.5, 486.0, 275.6, 485.7, 275.1,
+482.9, 270.8, 481.8, 271.2, 480.2, 266.5, 477.7, 262.6, 477.7, 262.6,
+475.1, 258.7, 471.8, 253.4, 473.2, 250.8, 468.4, 248.2, 492.2, 267.1,
+491.3, 271.0, 518.8, 282.5, 504.3, 4.5, 516.9, -11.6, 519.7, -10.1,
+529.4, -27.8, 529.8, -26.1, 529.7, -26.0, 530.3, -24.4, 538.8, -5.6,
+535.7, -4.2, 541.1, 16.0, 525.8, 22.1, 528.8, 29.4, 516.5, 42.7,
+516.9, 42.3, 516.0, 41.4, 515.6, 40.0, 510.5, 23.9, 510.5, 23.9,
+505.4, 7.7, 504.9, 6.1, 503.8, 5.4, 504.3, 4.5, 639.5, 8.4,
+639.7, 16.2, 642.7, 22.6, 639.9, 23.9, 624.1, 37.2, 624.1, 37.2,
+608.4, 50.5, 611.7, 42.7, 600.9, 38.2, 593.4, 25.8, 593.3, 25.3,
+597.4, 24.3, 601.4, 22.8, 610.8, 19.2, 610.8, 19.2, 620.2, 15.7,
+624.8, 14.0, 624.8, 14.0, 629.3, 12.3, 634.4, 10.4, 638.2, 7.0,
+639.5, 8.4, 453.5, 65.5, 447.0, 74.6, 447.0, 74.6, 440.4, 83.6,
+441.1, 80.3, 436.2, 74.9, 433.3, 75.3, 432.4, 75.9, 429.1, 70.4,
+424.9, 65.5, 436.5, 59.9, 447.5, 58.4, 453.5, 65.5, 450.5, -16.5,
+432.0, -18.4, 432.0, -18.4, 413.6, -20.3, 437.4, -27.6, 437.7, -27.9,
+460.0, -39.1, 474.0, -43.4, 474.0, -43.4, 488.0, -47.7, 479.7, -27.7,
+472.1, -24.4, 450.5, -16.5, 265.1, 182.1, 248.9, 202.2, 226.0, 217.1,
+225.3, 216.3, 232.4, 185.6, 232.4, 185.6, 239.6, 154.9, 239.5, 155.4,
+240.3, 155.6, 240.9, 156.3, 249.7, 165.6, 249.7, 165.6, 258.5, 175.0,
+261.8, 178.5, 265.5, 181.5, 265.1, 182.1, 286.3, 163.8, 287.4, 159.2,
+290.1, 157.2, 295.2, 156.1, 293.8, 156.4, 295.6, 160.6, 293.7, 162.2,
+291.1, 164.4, 286.7, 162.2, 286.3, 163.8, 319.8, 249.5, 302.8, 221.9,
+306.2, 219.1, 285.8, 194.3, 281.7, 180.7, 289.7, 178.2, 293.7, 162.2,
+295.6, 160.6, 293.8, 156.4, 295.2, 156.1, 298.4, 143.3, 298.4, 143.3,
+301.6, 130.4, 301.6, 130.4, 301.5, 130.3, 301.5, 130.3, 301.5, 130.3,
+301.6, 130.4, 301.6, 130.4, 310.7, 190.0, 333.2, 195.5, 319.8, 249.5,
+301.6, 130.4, 302.9, 144.6, 306.2, 144.2, 310.9, 158.1, 320.4, 186.5,
+320.4, 186.5, 329.9, 214.9, 331.8, 220.7, 331.8, 220.7, 333.8, 226.6,
+330.2, 231.4, 334.1, 239.5, 340.6, 246.9, 340.9, 248.6, 339.1, 248.9,
+337.6, 250.9, 332.9, 257.1, 332.4, 263.6, 328.2, 263.3, 328.9, 256.9,
+324.0, 256.4, 319.8, 249.5, 333.2, 195.5, 310.7, 190.0, 301.6, 130.4,
+356.7, 139.2, 350.2, 134.3, 353.0, 126.2, 355.1, 114.2, 357.3, 116.0,
+360.6, 115.1, 360.7, 116.3, 381.1, 132.7, 381.1, 132.7, 401.4, 149.1,
+401.6, 148.1, 373.4, 151.7, 356.7, 139.2, 299.7, 125.0, 289.1, 108.1,
+282.8, 103.1, 286.7, 86.0, 287.4, 82.9, 297.8, 85.2, 308.9, 84.4,
+315.9, 83.9, 321.9, 81.1, 323.0, 83.4, 325.0, 87.7, 319.0, 90.5,
+315.0, 97.6, 310.1, 106.4, 310.1, 106.4, 305.2, 115.3, 302.5, 120.1,
+298.3, 122.7, 299.7, 125.0, 181.3, 386.7, 181.8, 382.9, 181.0, 382.6,
+182.4, 379.0, 186.4, 367.7, 186.4, 367.7, 190.4, 356.3, 188.2, 371.8,
+186.6, 371.8, 181.3, 386.7, 224.3, 170.6, 229.8, 160.6, 230.0, 160.7,
+235.3, 150.5, 231.1, 165.6, 231.1, 165.6, 226.9, 180.6, 227.0, 180.2,
+226.8, 180.1, 226.7, 179.6, 225.5, 175.1, 223.2, 174.4, 224.3, 170.6,
+155.7, 301.6, 156.2, 298.9, 155.4, 298.3, 156.8, 296.1, 157.4, 294.9,
+157.1, 294.1, 158.1, 293.8, 165.8, 287.7, 163.1, 284.1, 168.1, 274.5,
+182.5, 266.1, 189.3, 262.0, 202.7, 267.7, 200.1, 277.3, 200.1, 277.3,
+197.4, 286.9, 194.8, 290.7, 192.0, 288.8, 186.7, 290.7, 179.3, 293.3,
+179.3, 293.3, 172.0, 295.9, 171.2, 296.2, 171.2, 296.2, 170.5, 296.4,
+164.9, 298.4, 164.9, 298.4, 159.2, 300.4, 157.5, 301.0, 155.8, 301.9,
+155.7, 301.6, 474.7, 878.3, 473.3, 859.1, 473.3, 859.1, 471.9, 840.0,
+469.7, 766.4, 467.8, 766.4, 463.6, 692.9, 463.5, 690.2, 463.4, 690.2,
+463.3, 687.6, 463.4, 690.2, 463.5, 690.2, 463.6, 692.9, 467.8, 766.4,
+469.7, 766.4, 471.9, 840.0, 473.3, 859.1, 473.3, 859.1, 474.7, 878.3,
+703.6, 773.3, 704.1, 772.7, 704.0, 772.6, 704.6, 772.1, 738.0, 746.6,
+739.2, 747.8, 770.2, 719.5, 770.4, 719.9, 770.8, 720.0, 770.7, 720.3,
+771.7, 726.6, 776.6, 732.6, 778.2, 732.1, 769.4, 744.7, 763.0, 739.6,
+750.0, 750.0, 696.0, 793.3, 704.4, 809.3, 644.3, 839.4, 681.2, 820.9,
+673.9, 806.3, 703.6, 773.3, 644.3, 839.4, 633.2, 850.9, 632.6, 850.6,
+619.8, 860.2, 624.5, 856.6, 623.9, 855.9, 628.1, 851.5, 661.0, 817.4,
+661.0, 817.4, 693.8, 783.4, 698.7, 778.3, 698.9, 778.5, 703.6, 773.3,
+673.9, 806.3, 681.2, 820.9, 644.3, 839.4, 518.2, 760.4, 508.7, 820.8,
+505.3, 820.4, 499.2, 881.1, 499.0, 882.0, 494.5, 882.9, 492.0, 881.3,
+490.7, 880.5, 491.8, 878.8, 491.6, 876.2, 490.2, 868.5, 490.8, 868.4,
+490.1, 860.6, 493.4, 859.1, 491.7, 854.3, 491.9, 847.9, 495.9, 826.7,
+495.0, 826.5, 498.0, 805.1, 499.1, 794.7, 500.0, 794.8, 501.9, 784.4,
+507.8, 771.4, 517.9, 772.6, 518.2, 760.4, 501.9, 784.4, 516.1, 671.5,
+522.3, 672.2, 543.4, 560.1, 544.6, 555.9, 545.0, 555.9, 545.8, 551.6,
+546.7, 548.5, 546.1, 548.2, 547.5, 545.3, 546.7, 546.9, 547.3, 547.2,
+547.1, 549.0, 545.2, 564.5, 545.2, 564.5, 543.2, 580.0, 542.6, 585.6,
+542.6, 585.6, 541.9, 591.3, 540.1, 605.1, 540.1, 605.1, 538.4, 619.0,
+537.3, 627.8, 537.3, 627.8, 536.3, 636.7, 533.8, 656.3, 533.8, 656.3,
+531.4, 675.9, 531.1, 678.3, 531.2, 678.3, 530.8, 680.6, 524.2, 720.5,
+524.5, 720.5, 518.2, 760.4, 517.9, 772.6, 507.8, 771.4, 501.9, 784.4,
+563.3, 396.4, 568.0, 403.6, 568.0, 403.6, 572.6, 410.9, 553.8, 421.7,
+555.1, 426.2, 541.9, 444.6, 541.1, 445.4, 540.4, 444.8, 539.0, 445.1,
+528.2, 446.9, 528.2, 446.9, 517.4, 448.8, 509.5, 450.2, 501.5, 451.9,
+501.5, 451.5, 510.3, 443.7, 507.4, 440.4, 513.3, 429.3, 535.4, 409.6,
+535.8, 404.9, 563.3, 396.4, 513.3, 429.3, 529.4, 398.9, 533.3, 400.4,
+545.5, 368.5, 554.4, 382.5, 554.4, 382.5, 563.3, 396.4, 535.8, 404.9,
+535.4, 409.6, 513.3, 429.3, 526.6, 620.2, 524.2, 628.7, 523.2, 628.6,
+521.8, 637.3, 537.8, 673.8, 501.4, 685.9, 494.2, 736.4, 480.5, 747.8,
+485.1, 756.8, 482.5, 778.1, 482.3, 775.1, 482.3, 775.1, 482.0, 772.1,
+492.0, 746.3, 486.7, 742.4, 484.0, 713.4, 490.1, 684.0, 495.0, 685.1,
+506.0, 656.7, 507.2, 653.4, 506.8, 653.2, 508.5, 650.2, 517.1, 634.9,
+514.4, 631.9, 526.6, 620.2, 508.5, 650.2, 506.3, 648.9, 514.9, 633.7,
+521.3, 617.1, 524.2, 609.7, 524.2, 609.7, 527.1, 602.3, 535.2, 581.2,
+542.9, 581.4, 543.4, 560.1, 535.0, 590.1, 535.0, 590.1, 526.6, 620.2,
+514.4, 631.9, 517.1, 634.9, 508.5, 650.2, 451.2, 311.6, 452.2, 312.4,
+450.9, 313.9, 450.6, 316.3, 448.7, 330.7, 449.8, 331.0, 446.9, 345.2,
+447.5, 347.8, 443.8, 348.2, 443.4, 351.4, 441.3, 329.1, 441.3, 329.1,
+439.3, 306.7, 439.0, 307.9, 446.5, 307.6, 451.2, 311.6, 490.6, 289.4,
+495.4, 300.0, 492.2, 303.8, 500.3, 310.6, 501.6, 337.8, 501.6, 337.8,
+503.0, 365.1, 487.6, 331.4, 492.5, 327.4, 490.6, 289.4, 453.6, 292.6,
+451.1, 287.9, 456.2, 280.0, 455.2, 279.7, 461.1, 281.5, 459.3, 287.6,
+463.5, 295.5, 465.0, 298.4, 464.4, 300.7, 466.5, 301.3, 459.4, 299.2,
+456.8, 298.7, 453.6, 292.6, 184.0, 643.8, 178.9, 658.2, 177.4, 657.8,
+173.9, 672.6, 169.9, 643.5, 169.9, 643.5, 165.9, 614.5, 163.2, 624.8,
+174.0, 627.6, 182.2, 640.8, 183.1, 642.3, 184.3, 642.6, 184.0, 643.8,
+218.5, 420.3, 228.1, 405.3, 229.6, 406.0, 237.7, 390.2, 232.9, 392.7,
+238.0, 401.0, 236.2, 411.3, 228.3, 439.8, 230.0, 440.3, 223.8, 469.2,
+233.1, 461.9, 221.4, 447.0, 219.0, 424.9, 218.8, 422.6, 217.7, 422.0,
+218.5, 420.3, 421.1, 484.6, 422.3, 473.1, 423.9, 473.2, 426.6, 461.9,
+427.4, 458.4, 428.0, 458.4, 428.3, 454.9, 425.3, 497.4, 424.7, 497.4,
+421.2, 539.9, 420.9, 543.2, 420.6, 543.2, 420.6, 546.5, 420.4, 532.8,
+420.7, 532.8, 420.8, 519.2, 421.0, 501.9, 419.4, 501.7, 421.1, 484.6,
+422.5, 225.0, 423.6, 234.8, 423.4, 234.8, 424.7, 244.6, 424.1, 275.2,
+419.6, 275.8, 423.4, 305.9, 420.5, 301.6, 420.5, 301.6, 417.5, 297.2,
+417.2, 261.1, 417.7, 260.8, 422.5, 225.0, 355.0, 647.4, 355.7, 641.6,
+358.4, 641.9, 358.8, 636.1, 356.5, 665.5, 360.6, 667.3, 351.3, 694.7,
+358.7, 672.9, 352.0, 670.9, 355.0, 647.4, 376.3, 669.4, 377.1, 669.7,
+372.0, 686.8, 367.8, 704.1, 366.7, 708.8, 366.7, 708.8, 365.5, 713.5,
+365.5, 713.7, 365.5, 713.7, 365.4, 714.0, 358.3, 730.5, 361.1, 731.7,
+356.8, 749.4, 343.4, 802.1, 343.4, 802.1, 330.6, 854.9, 337.9, 833.6,
+333.6, 832.1, 336.5, 809.4, 337.2, 803.9, 337.2, 803.9, 337.9, 798.5,
+338.4, 794.6, 338.4, 794.6, 338.9, 790.7, 339.9, 783.2, 339.9, 783.2,
+340.8, 775.8, 345.8, 737.4, 338.7, 735.0, 350.7, 699.0, 356.4, 681.9,
+368.6, 667.1, 376.3, 669.4, 292.5, 405.8, 296.0, 393.7, 296.0, 393.7,
+299.6, 381.6, 301.7, 380.5, 305.9, 388.8, 312.2, 396.0, 311.5, 396.8,
+317.7, 409.2, 324.0, 409.5, 326.5, 412.4, 326.5, 412.4, 329.1, 415.3,
+332.3, 419.0, 331.6, 421.7, 335.5, 422.7, 313.3, 417.0, 306.0, 421.3,
+292.5, 405.8, 302.4, 371.7, 309.5, 347.3, 311.6, 347.8, 316.6, 322.9,
+320.3, 313.4, 317.5, 312.0, 322.4, 303.2, 327.4, 285.8, 327.4, 285.8,
+332.5, 268.4, 332.7, 267.9, 333.0, 267.6, 332.8, 267.4, 338.5, 275.2,
+344.3, 276.1, 343.5, 283.5, 341.6, 301.3, 335.5, 300.7, 327.5, 317.9,
+326.6, 319.9, 326.6, 319.9, 325.6, 322.0, 319.1, 335.8, 319.1, 335.8,
+312.7, 349.7, 310.3, 354.7, 310.3, 354.7, 308.0, 359.7, 305.2, 365.7,
+303.9, 365.4, 302.4, 371.7, 420.4, 487.6, 421.6, 491.2, 419.3, 491.9,
+418.3, 496.3, 418.2, 496.3, 418.2, 496.3, 418.2, 496.4, 416.2, 504.6,
+411.6, 510.8, 414.3, 512.8, 385.3, 491.1, 389.9, 484.9, 365.6, 457.1,
+362.0, 453.0, 362.0, 453.0, 358.5, 448.9, 350.7, 440.1, 352.6, 437.1,
+343.0, 431.2, 380.2, 454.0, 378.3, 457.0, 413.7, 482.7, 417.0, 485.2,
+419.3, 484.4, 420.4, 487.6, 303.1, 319.5, 315.6, 292.9, 311.4, 286.3,
+332.5, 268.4, 327.4, 285.8, 327.4, 285.8, 322.4, 303.2, 313.8, 318.9,
+315.6, 319.9, 309.1, 336.7, 294.0, 364.3, 294.0, 364.3, 278.9, 391.8,
+271.4, 406.4, 270.2, 405.8, 261.6, 419.8, 278.8, 368.5, 280.1, 368.6,
+303.1, 319.5, 261.6, 419.8, 246.9, 443.6, 244.9, 442.6, 232.2, 467.3,
+233.2, 453.4, 233.2, 453.4, 234.2, 439.5, 264.1, 377.3, 252.0, 356.3,
+303.1, 319.5, 280.1, 368.6, 278.8, 368.5, 261.6, 419.8, 234.9, 429.5,
+235.6, 420.4, 233.8, 419.9, 236.2, 411.3, 238.0, 401.0, 232.9, 392.7,
+237.7, 390.2, 239.4, 387.0, 236.2, 383.7, 238.3, 382.2, 258.8, 351.6,
+260.3, 352.6, 282.5, 323.1, 284.3, 320.9, 284.3, 320.9, 286.0, 318.8,
+288.1, 316.1, 288.2, 316.2, 290.3, 313.5, 294.6, 307.8, 294.6, 307.8,
+298.9, 302.1, 305.5, 301.5, 304.9, 294.2, 310.9, 286.3, 274.5, 358.7,
+276.4, 360.0, 234.9, 429.5, 310.9, 286.3, 313.8, 282.4, 317.4, 281.6,
+316.7, 278.6, 317.3, 278.9, 327.4, 271.4, 328.2, 263.3, 332.4, 263.6,
+332.9, 257.1, 337.6, 250.9, 340.4, 253.4, 335.2, 259.1, 332.8, 267.4,
+333.0, 267.6, 332.7, 267.9, 332.5, 268.4, 311.4, 286.3, 315.6, 292.9,
+303.1, 319.5, 252.0, 356.3, 264.1, 377.3, 234.2, 439.5, 234.5, 434.5,
+234.5, 434.5, 234.9, 429.5, 276.4, 360.0, 274.5, 358.7, 310.9, 286.3,
+240.7, 582.4, 236.3, 598.2, 231.9, 614.0, 231.9, 614.0, 226.4, 633.0,
+226.3, 633.0, 220.8, 652.1, 219.4, 656.7, 217.9, 656.8, 218.1, 661.3,
+219.1, 648.1, 219.1, 648.1, 220.0, 634.9, 225.6, 614.0, 228.0, 614.6,
+236.0, 594.4, 238.4, 588.4, 239.4, 588.6, 240.7, 582.4, 169.5, 791.9,
+171.5, 807.5, 171.5, 807.5, 173.4, 823.0, 161.9, 809.6, 161.8, 809.6,
+150.3, 796.2, 150.1, 795.3, 166.5, 788.4, 169.5, 791.9, 150.3, 796.2,
+147.5, 793.0, 143.9, 791.3, 144.7, 789.8, 149.8, 732.8, 150.4, 732.9,
+155.0, 675.9, 157.9, 699.5, 152.3, 701.7, 160.9, 723.1, 162.3, 757.6,
+165.2, 757.5, 169.5, 791.9, 166.5, 788.4, 150.1, 795.3, 150.3, 796.2,
+76.7, 162.3, 81.1, 168.4, 84.6, 167.9, 85.5, 174.4, 87.9, 181.5,
+87.9, 181.5, 90.3, 188.5, 100.0, 235.0, 100.4, 234.9, 110.6, 281.3,
+108.8, 272.7, 108.2, 272.8, 105.7, 264.3, 104.5, 259.8, 104.5, 259.8,
+103.2, 255.3, 89.9, 208.8, 87.0, 209.4, 76.7, 162.3, 50.5, 72.6,
+53.0, 79.9, 51.3, 86.0, 55.5, 87.2, 55.6, 87.2, 55.7, 87.7,
+55.9, 88.2, 56.8, 120.2, 51.9, 122.6, 62.1, 152.1, 62.5, 168.3,
+62.5, 168.3, 63.0, 184.6, 66.0, 173.1, 61.6, 171.9, 60.2, 159.3,
+55.4, 116.0, 53.3, 116.1, 50.5, 72.6, 817.0, 605.3, 806.9, 609.0,
+806.7, 608.6, 796.8, 612.7, 797.2, 611.1, 797.4, 609.5, 797.6, 609.5,
+804.0, 598.2, 804.0, 596.3, 804.0, 583.1, 801.5, 574.7, 806.4, 573.2,
+808.8, 563.4, 819.0, 580.8, 821.5, 586.4, 817.0, 605.3, 808.8, 563.4,
+810.7, 555.2, 810.7, 555.2, 812.7, 547.1, 812.6, 547.5, 813.3, 547.6,
+813.9, 548.1, 833.9, 567.9, 834.2, 567.7, 855.2, 586.5, 855.4, 586.6,
+855.4, 586.6, 855.5, 586.8, 853.9, 586.4, 853.0, 589.9, 850.6, 593.0,
+848.1, 589.4, 833.8, 599.1, 817.0, 605.3, 821.5, 586.4, 819.0, 580.8,
+808.8, 563.4, 911.8, 470.4, 913.5, 473.3, 915.9, 473.9, 915.3, 476.2,
+916.7, 480.1, 916.7, 480.1, 918.1, 484.0, 913.3, 508.2, 914.2, 509.6,
+917.5, 534.2, 916.9, 536.7, 916.9, 536.7, 916.3, 539.2, 913.2, 504.9,
+912.4, 504.8, 911.8, 470.4, 916.3, 539.2, 913.4, 551.5, 912.9, 551.4,
+910.5, 563.7, 910.4, 563.6, 910.4, 563.6, 910.3, 563.5, 905.8, 531.9,
+910.5, 526.3, 893.0, 501.6, 891.1, 488.4, 894.8, 486.7, 889.2, 475.3,
+895.7, 464.8, 895.7, 464.8, 902.2, 454.4, 904.7, 463.1, 907.0, 462.4,
+911.8, 470.4, 912.4, 504.8, 913.2, 504.9, 916.3, 539.2, 849.5, 494.8,
+848.1, 492.9, 848.1, 492.9, 846.8, 490.9, 842.0, 484.2, 842.8, 483.6,
+838.7, 476.3, 844.7, 485.2, 844.3, 485.5, 849.5, 494.8, 738.7, 69.9,
+753.9, 66.6, 761.8, 72.8, 769.2, 63.4, 788.7, 68.7, 786.9, 75.6,
+804.5, 87.9, 830.4, 107.5, 831.0, 106.7, 857.5, 125.5, 854.9, 123.5,
+854.5, 124.1, 851.4, 122.6, 813.2, 104.8, 813.2, 104.8, 775.0, 86.9,
+756.8, 78.4, 741.7, 82.2, 738.7, 69.9, 760.7, 133.2, 746.8, 120.3,
+752.8, 100.3, 740.9, 98.7, 768.4, 102.3, 766.4, 118.0, 791.8, 137.2,
+792.9, 137.9, 792.8, 138.7, 793.9, 138.7, 788.0, 138.5, 788.0, 137.7,
+782.2, 136.8, 771.4, 135.0, 767.4, 139.3, 760.7, 133.2, 684.7, 102.6,
+683.4, 102.7, 682.1, 99.2, 681.9, 99.4, 684.6, 101.2, 685.9, 99.3,
+689.9, 99.1, 689.7, 98.8, 687.4, 102.5, 684.7, 102.6, 705.7, 126.7,
+727.5, 138.7, 723.1, 146.6, 740.5, 166.5, 741.3, 167.5, 741.1, 167.9,
+742.1, 168.4, 698.0, 142.6, 699.3, 137.2, 651.4, 121.1, 644.5, 115.4,
+644.5, 115.4, 637.5, 109.8, 672.1, 113.3, 676.0, 110.3, 705.7, 126.7,
+637.5, 109.8, 628.7, 102.5, 629.9, 99.9, 619.9, 95.3, 621.5, 95.0,
+622.0, 94.0, 623.1, 94.6, 646.1, 94.6, 646.1, 89.7, 669.1, 84.8,
+674.0, 93.0, 674.2, 94.0, 681.9, 99.4, 682.1, 99.2, 683.4, 102.7,
+684.7, 102.6, 692.8, 111.8, 692.8, 111.8, 700.8, 121.1, 703.3, 123.9,
+702.9, 126.2, 705.7, 126.7, 676.0, 110.3, 672.1, 113.3, 637.5, 109.8,
+629.3, 214.7, 629.4, 214.6, 627.9, 213.2, 626.6, 211.7, 620.0, 204.5,
+620.0, 204.5, 613.4, 197.3, 608.6, 192.1, 608.6, 192.1, 603.8, 186.8,
+591.9, 173.9, 591.5, 174.3, 580.1, 161.0, 579.1, 158.9, 579.1, 158.9,
+578.0, 156.8, 578.1, 157.0, 578.3, 156.9, 578.7, 157.1, 580.9, 157.7,
+580.9, 157.7, 583.1, 158.4, 618.0, 168.9, 623.6, 160.4, 653.0, 179.5,
+652.8, 179.4, 647.2, 188.0, 641.5, 196.5, 635.4, 205.6, 636.9, 207.0,
+629.3, 214.7, 689.0, 182.7, 706.2, 191.7, 706.2, 191.7, 723.5, 200.8,
+715.9, 194.0, 712.7, 197.5, 701.9, 194.3, 698.4, 193.2, 697.4, 194.4,
+695.0, 192.2, 691.0, 188.6, 693.0, 183.9, 689.0, 182.7, 626.8, 212.0,
+626.7, 211.8, 626.7, 211.8, 626.6, 211.7, 627.9, 213.2, 629.4, 214.6,
+629.3, 214.7, 633.7, 219.4, 633.7, 219.4, 638.0, 224.2, 638.4, 224.6,
+638.4, 224.6, 638.7, 224.9, 645.6, 232.5, 645.6, 232.5, 652.5, 240.0,
+665.2, 253.8, 665.2, 253.8, 677.8, 267.5, 681.5, 271.6, 681.5, 271.6,
+685.2, 275.6, 685.8, 276.2, 685.8, 276.2, 686.3, 276.8, 688.5, 279.2,
+688.5, 279.2, 690.7, 281.6, 697.1, 288.6, 697.1, 288.6, 703.6, 295.6,
+710.4, 303.0, 710.4, 303.0, 717.1, 310.4, 720.6, 314.2, 720.6, 314.2,
+724.0, 317.9, 730.1, 324.6, 736.2, 331.2, 736.2, 331.2, 736.2, 331.2,
+730.1, 324.6, 724.0, 317.9, 720.6, 314.2, 720.6, 314.2, 717.1, 310.4,
+710.4, 303.0, 710.4, 303.0, 703.6, 295.6, 697.1, 288.6, 697.1, 288.6,
+690.7, 281.6, 688.5, 279.2, 688.5, 279.2, 686.3, 276.8, 685.8, 276.2,
+685.8, 276.2, 685.2, 275.6, 681.5, 271.6, 681.5, 271.6, 677.8, 267.5,
+665.2, 253.8, 665.2, 253.8, 652.5, 240.0, 645.6, 232.5, 645.6, 232.5,
+638.7, 224.9, 638.4, 224.6, 638.4, 224.6, 638.0, 224.2, 633.7, 219.4,
+633.7, 219.4, 629.3, 214.7, 628.1, 213.3, 628.0, 213.3, 626.8, 212.0,
+626.8, 212.0, 626.8, 212.0, 626.8, 212.0, 645.8, 239.4, 690.1, 300.2,
+690.2, 300.2, 731.8, 362.9, 730.3, 363.7, 730.3, 363.7, 728.7, 364.4,
+716.2, 348.6, 717.6, 347.6, 706.4, 330.7, 696.0, 315.0, 696.0, 315.0,
+685.6, 299.3, 665.7, 269.4, 667.0, 268.4, 645.8, 239.4, 792.6, 283.1,
+791.4, 277.9, 793.4, 271.7, 795.1, 271.6, 826.3, 282.4, 829.0, 274.6,
+862.9, 277.6, 833.0, 295.4, 834.2, 297.5, 805.6, 317.4, 800.5, 317.8,
+796.7, 300.8, 792.6, 283.1, 805.6, 317.4, 802.4, 319.6, 802.4, 319.6,
+799.3, 321.8, 799.1, 322.4, 790.0, 317.2, 784.9, 320.1, 784.8, 320.1,
+785.1, 318.9, 785.4, 317.7, 789.0, 300.4, 788.8, 283.1, 792.6, 283.1,
+796.7, 300.8, 800.5, 317.8, 805.6, 317.4, 669.2, 142.5, 711.0, 169.1,
+716.3, 163.2, 752.8, 195.8, 757.6, 199.0, 757.6, 199.0, 762.5, 202.3,
+715.8, 172.5, 715.3, 173.2, 669.2, 142.5, 656.5, 133.7, 662.4, 138.7,
+662.0, 139.2, 668.3, 143.7, 682.3, 157.8, 681.8, 158.3, 695.4, 173.0,
+701.6, 179.8, 707.8, 186.6, 707.8, 186.5, 699.6, 181.4, 699.2, 181.9,
+690.7, 177.3, 677.9, 165.3, 679.7, 163.4, 668.8, 149.5, 662.6, 141.6,
+657.2, 142.1, 656.5, 133.7, 484.2, 192.4, 466.3, 185.9, 466.8, 183.8,
+448.4, 179.4, 434.4, 165.4, 434.4, 165.4, 420.4, 151.4, 423.0, 154.3,
+423.8, 153.6, 427.2, 155.8, 427.8, 156.2, 427.8, 156.2, 428.4, 156.5,
+441.0, 164.6, 441.0, 164.6, 453.6, 172.7, 468.9, 182.5, 483.2, 193.7,
+484.2, 192.4, 511.2, 117.2, 506.6, 118.9, 503.0, 121.8, 502.0, 120.5,
+497.6, 121.5, 496.6, 123.8, 493.2, 122.4, 479.7, 123.8, 478.5, 122.4,
+466.5, 128.1, 465.7, 128.2, 465.1, 128.8, 464.8, 128.5, 464.3, 128.6,
+464.0, 128.4, 463.7, 128.7, 497.3, 92.4, 497.5, 92.6, 531.3, 56.5,
+537.7, 49.7, 541.4, 50.9, 544.1, 42.8, 531.4, 81.3, 530.5, 81.6,
+511.2, 117.2, 544.1, 42.8, 544.3, 42.4, 544.9, 42.3, 544.8, 42.1,
+553.0, 66.6, 561.8, 66.9, 561.1, 91.2, 561.9, 94.1, 561.8, 97.0,
+562.7, 97.1, 560.1, 98.9, 560.1, 98.9, 557.4, 100.7, 533.6, 105.7,
+534.3, 109.0, 511.2, 117.2, 530.5, 81.6, 531.4, 81.3, 544.1, 42.8,
+436.8, 132.4, 435.4, 132.4, 435.4, 132.4, 434.0, 132.5, 436.4, 132.0,
+436.1, 130.2, 438.1, 128.0, 490.4, 81.3, 490.1, 80.9, 542.1, 33.9,
+542.0, 37.9, 543.5, 38.0, 544.8, 42.1, 544.9, 42.3, 544.3, 42.4,
+544.1, 42.8, 541.4, 50.9, 537.7, 49.7, 531.3, 56.5, 484.8, 95.3,
+484.6, 95.1, 436.8, 132.4, 531.3, 56.5, 497.5, 92.6, 497.3, 92.4,
+463.7, 128.7, 458.1, 133.7, 455.3, 131.1, 446.7, 132.3, 442.2, 133.8,
+441.7, 132.4, 436.8, 132.4, 484.6, 95.1, 484.8, 95.3, 531.3, 56.5,
+612.3, -75.3, 626.2, -73.9, 633.4, -66.3, 638.1, -51.9, 627.0, -46.6,
+638.8, -21.7, 639.5, 8.4, 638.2, 7.0, 634.4, 10.4, 629.3, 12.3,
+619.7, -9.4, 624.7, -11.6, 620.1, -35.4, 618.2, -44.7, 618.2, -44.7,
+616.4, -54.1, 614.4, -64.7, 615.4, -75.0, 612.3, -75.3, 403.8, 35.1,
+396.6, 24.7, 396.6, 24.7, 389.3, 14.2, 396.6, 18.3, 405.6, 9.6,
+408.7, 14.0, 435.3, 29.2, 443.6, 22.3, 462.0, 44.4, 462.7, 45.0,
+462.7, 45.0, 463.4, 45.6, 433.6, 40.8, 431.0, 46.6, 403.8, 35.1,
+463.4, 45.6, 464.5, 46.4, 464.7, 46.3, 465.6, 47.3, 465.9, 47.6,
+466.2, 47.8, 466.1, 47.9, 459.8, 56.7, 459.8, 56.7, 453.5, 65.5,
+447.5, 58.4, 436.5, 59.9, 424.9, 65.5, 424.7, 65.3, 424.7, 65.3,
+424.5, 65.0, 414.5, 49.8, 414.2, 50.1, 403.8, 35.1, 431.0, 46.6,
+433.6, 40.8, 463.4, 45.6, 509.8, -12.8, 509.0, -11.6, 509.0, -11.6,
+508.1, -10.5, 507.9, -10.1, 506.9, -11.0, 506.1, -10.7, 498.8, -2.9,
+488.5, -14.8, 471.0, -14.3, 461.5, -12.2, 460.8, -15.4, 450.5, -16.5,
+472.1, -24.4, 479.7, -27.7, 488.0, -47.7, 493.8, -49.4, 493.8, -49.4,
+499.5, -51.2, 509.6, -35.0, 505.7, -32.2, 509.8, -12.8, 499.5, -51.2,
+523.8, -58.5, 523.9, -58.0, 548.0, -65.9, 546.5, -63.9, 544.5, -62.9,
+545.1, -61.8, 545.4, -61.3, 542.5, -60.0, 542.7, -58.6, 543.8, -57.6,
+527.3, -47.3, 528.8, -39.2, 528.8, -39.2, 526.9, -37.9, 527.0, -36.7,
+515.8, -28.1, 518.4, -24.7, 509.8, -12.8, 505.7, -32.2, 509.6, -35.0,
+499.5, -51.2, 284.2, 78.5, 283.5, 81.8, 285.4, 82.2, 286.7, 86.0,
+282.8, 103.1, 289.1, 108.1, 299.7, 125.0, 300.6, 127.6, 301.9, 127.8,
+301.5, 130.3, 299.6, 143.4, 300.6, 144.3, 295.2, 156.1, 290.1, 157.2,
+287.4, 159.2, 286.3, 163.8, 280.3, 169.0, 280.3, 169.0, 274.3, 174.1,
+271.1, 176.8, 268.3, 176.5, 268.0, 179.5, 273.3, 128.7, 274.2, 128.6,
+284.2, 78.5, 373.3, 203.2, 370.5, 207.2, 370.8, 207.5, 367.6, 211.2,
+364.7, 215.0, 364.7, 215.0, 361.9, 218.8, 356.5, 189.2, 344.2, 177.1,
+358.0, 158.3, 349.9, 169.3, 369.1, 180.1, 373.3, 203.2, 358.0, 158.3,
+356.2, 151.9, 357.5, 151.5, 357.1, 144.7, 356.9, 141.9, 357.2, 139.2,
+356.7, 139.2, 373.4, 151.7, 401.6, 148.1, 401.4, 149.1, 404.7, 151.7,
+404.6, 151.9, 407.9, 154.3, 390.6, 178.7, 390.6, 178.7, 373.3, 203.2,
+369.1, 180.1, 349.9, 169.3, 358.0, 158.3, 308.9, 84.4, 297.8, 85.2,
+287.4, 82.9, 286.7, 86.0, 285.4, 82.2, 283.5, 81.8, 284.2, 78.5,
+283.8, 77.5, 283.8, 77.5, 283.5, 76.5, 282.6, 68.5, 280.9, 68.7,
+278.3, 61.0, 282.4, 55.0, 292.3, 52.8, 297.3, 56.6, 307.6, 64.5,
+312.6, 74.0, 308.9, 84.4, 297.3, 56.6, 297.3, 55.1, 303.3, 55.2,
+309.4, 53.8, 320.2, 51.3, 320.2, 51.3, 331.0, 48.8, 335.2, 47.8,
+337.5, 45.1, 339.5, 46.8, 345.7, 56.6, 345.7, 56.6, 352.0, 66.3,
+356.0, 70.2, 352.5, 73.8, 353.0, 81.4, 343.1, 87.8, 339.5, 82.3,
+326.0, 83.2, 324.5, 83.3, 324.5, 83.3, 323.0, 83.4, 321.9, 81.1,
+315.9, 83.9, 308.9, 84.4, 312.6, 74.0, 307.6, 64.5, 297.3, 56.6,
+333.5, 40.1, 335.5, 41.8, 336.2, 41.4, 337.4, 43.5, 338.4, 45.2,
+338.4, 45.2, 339.5, 46.8, 337.5, 45.1, 335.2, 47.8, 331.0, 48.8,
+329.4, 47.0, 330.6, 42.5, 333.5, 40.1, 273.8, 289.7, 286.3, 295.9,
+287.0, 303.2, 298.9, 302.1, 294.6, 307.8, 294.6, 307.8, 290.3, 313.5,
+288.2, 316.2, 288.1, 316.1, 286.0, 318.8, 277.3, 306.1, 269.1, 295.9,
+273.8, 289.7, 204.3, 420.0, 183.7, 445.4, 180.2, 443.2, 163.2, 470.8,
+155.4, 476.2, 151.1, 474.2, 147.6, 481.7, 147.4, 480.0, 147.4, 480.0,
+147.1, 478.3, 146.9, 478.2, 148.1, 475.5, 149.2, 472.6, 175.8, 445.4,
+173.8, 423.9, 204.3, 420.0, 213.9, 354.1, 211.2, 376.4, 202.0, 375.3,
+190.1, 396.4, 185.5, 404.4, 185.5, 404.4, 181.0, 412.4, 178.7, 416.5,
+176.4, 416.5, 176.4, 420.6, 182.3, 404.9, 178.8, 403.7, 181.3, 386.7,
+186.6, 371.8, 188.2, 371.8, 190.4, 356.3, 193.8, 346.9, 193.8, 346.9,
+197.1, 337.6, 204.7, 336.9, 214.8, 347.0, 213.9, 354.1, 197.1, 337.6,
+198.8, 332.8, 201.3, 328.4, 200.5, 328.0, 208.1, 323.9, 208.1, 323.9,
+215.6, 319.8, 216.7, 313.2, 227.8, 315.9, 238.9, 309.9, 237.8, 332.4,
+226.4, 331.9, 214.0, 353.9, 214.0, 354.0, 213.9, 354.1, 213.9, 354.1,
+214.8, 347.0, 204.7, 336.9, 197.1, 337.6, 205.5, 205.2, 214.9, 187.9,
+214.9, 187.9, 224.3, 170.6, 223.2, 174.4, 225.5, 175.1, 226.7, 179.6,
+218.9, 193.9, 203.1, 195.7, 205.5, 205.2, 226.7, 179.6, 226.8, 180.1,
+227.0, 180.2, 226.9, 180.6, 214.8, 224.2, 214.8, 224.2, 202.7, 267.7,
+189.3, 262.0, 182.5, 266.1, 168.1, 274.5, 172.0, 267.1, 178.1, 263.9,
+175.9, 259.7, 176.2, 259.8, 176.7, 258.1, 177.5, 256.5, 191.5, 230.8,
+189.8, 229.7, 205.5, 205.2, 203.1, 195.7, 218.9, 193.9, 226.7, 179.6,
+186.7, 290.7, 192.0, 288.8, 194.8, 290.7, 197.4, 286.9, 189.6, 315.0,
+189.6, 315.0, 181.8, 343.0, 182.1, 342.0, 181.9, 342.0, 182.0, 340.9,
+183.7, 322.7, 183.7, 322.7, 185.4, 304.5, 186.1, 297.6, 183.3, 295.7,
+186.7, 290.7, 463.6, 692.9, 463.5, 690.2, 463.4, 690.2, 463.3, 687.6,
+463.2, 686.9, 463.2, 686.2, 463.2, 686.2, 463.0, 680.2, 463.8, 680.1,
+462.8, 674.3, 464.4, 678.0, 464.4, 678.0, 466.1, 681.7, 466.3, 687.2,
+466.0, 687.8, 463.6, 692.9, 578.5, 434.6, 579.6, 430.3, 580.2, 430.4,
+580.8, 426.0, 580.6, 425.6, 581.4, 425.1, 581.3, 424.5, 602.2, 457.1,
+602.2, 457.1, 623.1, 489.8, 628.7, 470.0, 598.0, 464.0, 578.5, 434.6,
+673.6, 757.7, 672.3, 759.2, 679.5, 765.3, 685.5, 772.8, 689.7, 778.1,
+694.9, 781.9, 693.8, 783.4, 661.0, 817.4, 661.0, 817.4, 628.1, 851.5,
+629.8, 849.8, 629.2, 849.3, 630.3, 847.0, 634.0, 839.4, 634.0, 839.4,
+637.6, 831.9, 639.3, 828.4, 639.3, 828.4, 641.0, 824.9, 657.3, 791.3,
+650.1, 785.2, 673.6, 757.7, 491.4, 532.6, 491.9, 530.4, 503.1, 533.1,
+514.8, 533.6, 520.0, 533.8, 525.1, 533.3, 525.2, 534.0, 525.8, 540.2,
+520.7, 540.8, 516.3, 547.5, 512.5, 553.3, 509.0, 559.2, 508.7, 559.0,
+496.5, 551.7, 488.9, 543.1, 491.4, 532.6, 474.8, 529.0, 477.1, 496.5,
+488.2, 493.2, 479.4, 464.0, 481.8, 459.8, 483.6, 460.0, 484.1, 455.6,
+483.9, 455.4, 484.4, 455.0, 484.8, 454.4, 485.1, 452.0, 496.1, 456.3,
+501.5, 451.5, 501.5, 451.9, 509.5, 450.2, 517.4, 448.8, 517.2, 464.4,
+509.2, 464.3, 501.0, 479.8, 499.7, 482.2, 499.7, 482.2, 498.4, 484.5,
+486.6, 506.8, 469.2, 522.9, 474.8, 529.0, 527.1, 602.3, 524.2, 609.7,
+524.2, 609.7, 521.3, 617.1, 515.0, 592.1, 519.4, 590.9, 517.5, 564.7,
+517.0, 557.6, 517.0, 557.6, 516.5, 550.4, 516.4, 549.0, 515.7, 548.6,
+516.3, 547.5, 520.7, 540.8, 525.8, 540.2, 525.2, 534.0, 536.3, 517.1,
+536.3, 517.1, 547.5, 500.3, 551.2, 494.6, 553.7, 495.1, 555.0, 488.9,
+543.5, 546.1, 541.9, 545.8, 527.1, 602.3, 555.0, 488.9, 555.3, 487.8,
+555.7, 487.9, 556.3, 486.9, 562.9, 477.0, 563.1, 466.9, 569.4, 467.1,
+566.8, 467.0, 566.6, 477.0, 563.8, 487.0, 562.8, 490.6, 562.8, 490.6,
+561.8, 494.2, 556.7, 512.6, 556.7, 512.6, 551.5, 531.1, 549.5, 538.2,
+549.5, 538.2, 547.5, 545.3, 546.1, 548.2, 546.7, 548.5, 545.8, 551.6,
+545.0, 555.9, 544.6, 555.9, 543.4, 560.1, 542.9, 581.4, 535.2, 581.2,
+527.1, 602.3, 541.9, 545.8, 543.5, 546.1, 555.0, 488.9, 432.0, 228.2,
+431.8, 226.0, 431.8, 226.0, 431.6, 223.8, 437.1, 213.6, 439.0, 212.2,
+438.3, 201.1, 439.1, 202.4, 440.2, 203.0, 439.9, 203.6, 439.3, 206.5,
+441.2, 208.2, 444.3, 210.5, 456.0, 216.4, 452.5, 223.3, 460.7, 236.1,
+464.0, 254.6, 453.1, 274.4, 455.8, 275.1, 438.7, 270.4, 443.2, 252.0,
+432.0, 228.2, 455.8, 275.1, 456.6, 276.9, 455.5, 277.4, 455.2, 279.7,
+456.2, 280.0, 451.1, 287.9, 453.6, 292.6, 452.4, 302.1, 456.5, 306.3,
+451.2, 311.6, 446.5, 307.6, 439.0, 307.9, 439.3, 306.7, 435.6, 267.5,
+435.6, 267.5, 432.0, 228.2, 443.2, 252.0, 438.7, 270.4, 455.8, 275.1,
+475.1, 258.7, 477.7, 262.6, 477.7, 262.6, 480.2, 266.5, 484.3, 278.3,
+485.4, 277.9, 490.6, 289.4, 492.5, 327.4, 487.6, 331.4, 503.0, 365.1,
+503.1, 368.1, 503.1, 368.1, 503.3, 371.1, 497.3, 363.5, 498.7, 362.4,
+494.1, 353.7, 486.4, 339.0, 486.4, 339.0, 478.6, 324.2, 472.6, 312.8,
+472.6, 312.8, 466.5, 301.3, 464.4, 300.7, 465.0, 298.4, 463.5, 295.5,
+467.3, 276.7, 465.7, 275.0, 475.1, 258.7, 463.5, 295.5, 459.3, 287.6,
+461.1, 281.5, 455.2, 279.7, 455.5, 277.4, 456.6, 276.9, 455.8, 275.1,
+453.1, 274.4, 464.0, 254.6, 460.7, 236.1, 464.6, 242.2, 463.1, 244.0,
+468.4, 248.2, 473.2, 250.8, 471.8, 253.4, 475.1, 258.7, 465.7, 275.0,
+467.3, 276.7, 463.5, 295.5, 494.1, 353.7, 498.7, 362.4, 497.3, 363.5,
+503.3, 371.1, 503.6, 377.8, 505.6, 383.9, 503.9, 384.4, 498.4, 392.8,
+504.5, 396.8, 505.1, 409.1, 486.1, 398.3, 484.5, 401.1, 463.9, 393.2,
+464.2, 393.4, 466.8, 389.5, 469.6, 385.7, 480.9, 370.9, 480.9, 370.9,
+492.2, 356.2, 493.2, 355.0, 494.4, 354.1, 494.1, 353.7, 185.8, 516.6,
+187.6, 526.3, 190.7, 525.7, 195.7, 534.8, 200.7, 544.0, 203.9, 543.4,
+205.8, 553.2, 204.0, 561.3, 204.0, 561.3, 202.3, 569.3, 189.3, 545.5,
+190.9, 543.6, 185.8, 516.6, 195.7, 534.8, 190.7, 525.7, 187.6, 526.3,
+185.8, 516.6, 179.3, 504.9, 178.5, 505.2, 172.9, 493.1, 173.3, 491.8,
+173.3, 491.8, 173.8, 490.6, 196.2, 478.0, 189.5, 466.0, 205.2, 441.3,
+222.8, 476.9, 207.3, 489.8, 195.7, 534.8, 418.3, 496.3, 419.3, 491.9,
+421.6, 491.2, 420.4, 487.6, 420.7, 486.1, 421.1, 486.1, 421.1, 484.6,
+419.4, 501.7, 421.0, 501.9, 420.8, 519.2, 421.1, 514.2, 420.3, 514.1,
+419.7, 509.1, 419.0, 502.7, 419.0, 502.7, 418.3, 496.4, 418.3, 496.3,
+418.3, 496.3, 418.3, 496.3, 412.4, 667.3, 410.4, 684.4, 413.1, 685.3,
+407.6, 701.4, 411.8, 689.1, 408.8, 688.1, 409.9, 674.8, 410.7, 664.7,
+410.7, 664.7, 411.6, 654.6, 415.4, 609.0, 415.4, 609.0, 419.2, 563.4,
+419.9, 555.0, 419.9, 555.0, 420.6, 546.5, 420.6, 543.2, 420.9, 543.2,
+421.2, 539.9, 417.4, 595.7, 417.3, 595.6, 413.5, 651.4, 413.0, 659.4,
+413.3, 659.4, 412.4, 667.3, 427.3, 776.5, 428.0, 780.3, 430.1, 779.9,
+433.0, 783.4, 446.6, 800.0, 455.1, 797.7, 460.2, 816.6, 457.1, 838.5,
+457.1, 838.5, 453.9, 860.5, 455.4, 855.6, 452.0, 854.6, 450.1, 848.6,
+444.2, 830.0, 444.2, 830.0, 438.3, 811.3, 432.8, 793.9, 430.7, 794.3,
+427.3, 776.5, 406.6, 883.8, 406.0, 882.6, 403.3, 884.1, 400.0, 884.0,
+374.4, 883.8, 374.2, 886.1, 348.9, 883.3, 349.2, 883.2, 348.7, 880.8,
+348.6, 878.3, 347.6, 869.7, 349.5, 869.5, 350.4, 860.7, 355.0, 857.9,
+358.6, 864.1, 366.8, 867.4, 382.6, 873.9, 382.6, 873.9, 398.3, 880.4,
+402.4, 882.1, 405.1, 880.8, 406.6, 883.8, 377.5, 209.4, 376.8, 204.7,
+376.0, 199.9, 376.1, 199.9, 392.8, 177.9, 393.6, 178.5, 409.4, 156.0,
+412.5, 160.9, 412.5, 160.9, 415.7, 165.8, 417.1, 167.5, 416.0, 168.4,
+416.3, 171.0, 416.3, 171.1, 416.1, 171.1, 416.0, 171.3, 413.9, 173.3,
+413.9, 173.3, 411.9, 175.4, 394.7, 192.4, 397.1, 195.9, 377.5, 209.4,
+225.8, 746.3, 226.0, 752.9, 224.7, 752.9, 223.6, 759.5, 217.9, 793.6,
+217.9, 793.6, 212.2, 827.7, 210.5, 838.1, 207.5, 838.4, 208.8, 848.4,
+208.5, 822.8, 203.1, 821.6, 208.2, 797.2, 209.2, 783.9, 213.3, 782.9,
+210.2, 770.6, 209.7, 770.5, 210.6, 767.8, 210.6, 764.9, 225.7, 716.7,
+214.3, 713.1, 218.1, 661.3, 217.9, 656.8, 219.4, 656.7, 220.8, 652.1,
+223.9, 661.3, 221.3, 662.2, 221.9, 672.3, 223.8, 709.3, 225.1, 709.3,
+225.8, 746.3, 377.7, 663.3, 378.7, 665.6, 378.0, 667.1, 376.3, 669.4,
+368.6, 667.1, 356.4, 681.9, 350.7, 699.0, 351.9, 697.8, 351.0, 696.9,
+351.3, 694.7, 360.6, 667.3, 356.5, 665.5, 358.8, 636.1, 359.1, 633.6,
+359.1, 633.6, 359.5, 631.2, 359.5, 631.2, 359.5, 631.2, 359.5, 631.2,
+368.6, 647.3, 370.4, 646.5, 377.7, 663.3, 365.6, 457.1, 389.9, 484.9,
+385.3, 491.1, 414.3, 512.8, 419.1, 522.3, 413.0, 527.4, 407.6, 540.2,
+407.7, 539.3, 405.6, 539.1, 403.6, 538.0, 401.5, 535.4, 402.1, 534.9,
+400.7, 531.7, 387.8, 504.4, 387.8, 504.4, 374.9, 477.0, 370.3, 467.0,
+373.1, 463.9, 365.6, 457.1, 396.0, 295.6, 405.7, 311.6, 405.7, 311.6,
+415.3, 327.6, 415.1, 326.1, 403.1, 330.3, 400.9, 326.8, 393.5, 314.7,
+398.5, 311.6, 396.1, 296.5, 396.1, 296.0, 396.2, 296.0, 396.0, 295.6,
+429.5, 402.8, 428.6, 401.2, 424.0, 404.3, 422.4, 402.4, 409.9, 388.2,
+410.4, 387.3, 401.3, 370.6, 404.8, 377.1, 406.3, 376.3, 411.3, 382.1,
+420.4, 392.5, 423.1, 391.0, 429.5, 402.8, 327.5, 317.9, 335.5, 300.7,
+341.6, 301.3, 343.5, 283.5, 344.7, 282.9, 350.0, 293.2, 356.4, 303.0,
+363.8, 314.1, 363.8, 314.1, 371.2, 325.2, 376.4, 333.1, 375.3, 339.9,
+381.6, 341.0, 353.4, 336.2, 341.1, 338.4, 327.5, 317.9, 413.7, 482.7,
+378.3, 457.0, 380.2, 454.0, 343.0, 431.2, 338.6, 427.9, 339.3, 427.0,
+335.5, 422.7, 331.6, 421.7, 332.3, 419.0, 329.1, 415.3, 342.3, 424.3,
+341.6, 425.3, 354.1, 435.3, 359.8, 439.8, 359.8, 439.8, 365.4, 444.3,
+366.7, 445.3, 366.7, 445.3, 367.9, 446.3, 390.8, 464.5, 388.8, 467.8,
+413.7, 482.7, 274.1, 435.0, 276.4, 430.3, 275.1, 429.0, 278.6, 425.7,
+278.7, 425.6, 278.7, 425.6, 278.7, 425.6, 268.4, 467.8, 268.3, 467.8,
+258.1, 510.1, 256.8, 515.4, 255.8, 520.7, 255.8, 520.7, 255.8, 520.7,
+256.8, 515.4, 258.1, 510.1, 259.6, 503.2, 259.6, 503.2, 261.0, 496.3,
+267.6, 465.7, 266.5, 465.4, 274.1, 435.0, 64.3, 245.5, 63.3, 243.2,
+63.3, 243.2, 62.3, 240.9, 69.8, 212.3, 64.6, 210.9, 66.9, 181.0,
+65.6, 193.9, 66.4, 193.9, 65.9, 206.9, 65.1, 226.2, 66.4, 226.4,
+64.3, 245.5, 43.9, 79.8, 44.0, 76.2, 43.5, 76.1, 44.2, 72.5,
+44.4, 71.7, 44.2, 70.8, 44.2, 70.8, 45.3, 66.2, 44.7, 65.8,
+46.8, 61.7, 46.6, 62.8, 47.2, 62.9, 47.6, 64.1, 46.3, 72.1,
+46.9, 72.5, 43.9, 79.8, 649.4, 530.7, 645.6, 524.9, 641.9, 519.1,
+641.9, 519.1, 639.7, 515.7, 639.7, 515.7, 637.5, 512.3, 637.5, 512.3,
+637.5, 512.3, 637.5, 512.3, 638.0, 511.3, 637.6, 511.1, 637.8, 510.0,
+652.4, 503.5, 652.8, 504.4, 667.8, 498.9, 677.4, 495.4, 677.4, 495.4,
+687.0, 491.8, 694.7, 489.0, 700.5, 484.1, 702.3, 486.2, 701.6, 497.2,
+701.6, 497.2, 700.8, 508.2, 698.8, 512.4, 695.2, 510.7, 689.5, 513.1,
+669.4, 521.9, 670.2, 528.0, 649.4, 530.7, 700.8, 471.3, 701.9, 472.8,
+701.9, 472.8, 703.1, 474.3, 703.5, 474.5, 703.0, 475.8, 702.9, 477.3,
+701.0, 474.9, 700.6, 474.2, 700.8, 471.3, 808.2, 400.0, 814.9, 404.7,
+820.1, 403.3, 821.7, 409.3, 859.1, 470.9, 856.4, 472.5, 890.9, 536.0,
+891.2, 536.4, 890.9, 536.6, 891.0, 537.3, 892.0, 543.8, 892.0, 543.8,
+893.0, 550.3, 848.4, 476.5, 847.1, 476.9, 808.2, 400.0, 893.0, 550.3,
+893.2, 551.5, 892.7, 551.8, 893.3, 552.6, 889.0, 549.7, 889.0, 549.7,
+884.7, 546.7, 880.9, 539.6, 880.0, 540.1, 875.4, 533.5, 862.6, 514.1,
+862.4, 514.2, 849.5, 494.8, 844.3, 485.5, 844.7, 485.2, 838.7, 476.3,
+814.6, 432.8, 819.3, 429.5, 790.5, 389.3, 790.0, 388.3, 790.0, 388.3,
+789.5, 387.2, 797.2, 395.2, 798.9, 393.6, 808.2, 400.0, 847.1, 476.9,
+848.4, 476.5, 893.0, 550.3, 731.1, 91.4, 720.8, 83.6, 721.9, 78.2,
+710.6, 75.9, 720.4, 73.8, 721.6, 76.1, 730.2, 71.7, 731.7, 69.2,
+734.5, 70.8, 738.7, 69.9, 741.7, 82.2, 756.8, 78.4, 775.0, 86.9,
+771.2, 83.6, 767.7, 87.7, 760.4, 88.4, 745.7, 89.9, 744.5, 94.7,
+731.1, 91.4, 782.2, 136.8, 788.0, 137.7, 788.0, 138.5, 793.9, 138.7,
+800.7, 141.9, 800.0, 143.3, 806.2, 148.0, 811.6, 152.0, 811.6, 152.0,
+816.9, 156.1, 818.5, 154.8, 822.6, 162.9, 829.8, 165.7, 834.0, 165.0,
+834.8, 169.5, 839.8, 173.3, 876.6, 201.0, 876.6, 201.0, 913.4, 228.8,
+918.1, 232.3, 918.0, 232.3, 922.7, 235.8, 925.8, 238.2, 925.8, 238.2,
+929.0, 240.6, 912.7, 229.3, 912.8, 229.1, 896.6, 217.7, 896.5, 217.6,
+896.4, 217.7, 896.3, 217.7, 858.2, 190.7, 858.3, 190.6, 820.2, 163.7,
+801.2, 150.2, 798.9, 152.6, 782.2, 136.8, 782.6, 330.9, 782.3, 332.4,
+782.8, 333.4, 782.0, 333.8, 780.3, 335.4, 782.3, 338.7, 780.8, 339.3,
+767.9, 345.5, 767.9, 345.5, 755.0, 351.7, 744.1, 349.8, 745.6, 341.5,
+736.2, 331.2, 736.2, 331.2, 730.1, 324.6, 724.0, 317.9, 748.1, 316.3,
+748.5, 323.3, 773.0, 328.8, 777.8, 329.9, 778.5, 328.6, 782.6, 330.9,
+578.7, 157.1, 578.3, 156.9, 578.1, 157.0, 578.0, 156.8, 567.2, 135.5,
+567.2, 135.5, 556.4, 114.1, 565.8, 120.1, 566.5, 119.0, 576.5, 124.0,
+581.7, 126.7, 581.8, 126.6, 587.0, 129.2, 596.9, 134.4, 596.9, 134.4,
+606.8, 139.6, 603.3, 151.5, 592.9, 148.5, 578.7, 157.1, 690.7, 281.6,
+697.1, 288.6, 697.1, 288.6, 703.6, 295.6, 710.4, 303.0, 710.4, 303.0,
+717.1, 310.4, 720.6, 314.2, 720.6, 314.2, 724.0, 317.9, 730.1, 324.6,
+736.2, 331.2, 736.2, 331.2, 745.6, 341.5, 744.1, 349.8, 755.0, 351.7,
+750.9, 353.7, 750.9, 353.7, 746.8, 355.7, 734.4, 346.0, 737.0, 342.8,
+727.2, 329.8, 708.9, 305.7, 707.9, 306.5, 690.7, 281.6, 645.4, 281.4,
+639.4, 274.1, 639.4, 274.1, 633.4, 266.7, 624.4, 257.5, 629.3, 253.2,
+621.3, 242.7, 617.0, 239.1, 618.6, 237.3, 615.8, 231.9, 631.1, 256.4,
+632.5, 255.7, 645.4, 281.4, 695.4, 173.0, 681.8, 158.3, 682.3, 157.8,
+668.3, 143.7, 666.0, 142.0, 666.3, 141.5, 664.3, 139.4, 666.5, 141.3,
+666.8, 140.9, 669.2, 142.5, 711.0, 169.1, 716.3, 163.2, 752.8, 195.8,
+753.2, 196.1, 753.4, 195.9, 753.8, 196.2, 758.2, 199.1, 758.1, 199.2,
+762.5, 202.3, 764.9, 203.9, 764.9, 203.8, 767.2, 205.4, 768.4, 206.2,
+768.4, 206.2, 769.5, 207.0, 732.3, 190.2, 729.6, 194.5, 695.4, 173.0,
+769.5, 207.0, 794.8, 224.5, 794.8, 224.5, 820.2, 241.9, 812.5, 238.1,
+810.9, 240.7, 804.0, 236.0, 757.7, 213.1, 757.7, 213.1, 711.3, 190.3,
+709.2, 189.3, 709.9, 187.9, 707.8, 186.5, 707.8, 186.6, 701.6, 179.8,
+695.4, 173.0, 729.6, 194.5, 732.3, 190.2, 769.5, 207.0, 574.2, 109.7,
+572.5, 108.7, 572.4, 108.8, 570.7, 107.6, 569.7, 106.9, 569.6, 106.1,
+568.7, 106.2, 569.2, 106.1, 569.2, 106.1, 569.8, 106.0, 572.2, 107.5,
+572.8, 107.4, 574.2, 109.7, 538.5, 212.0, 511.4, 202.2, 511.4, 202.2,
+484.2, 192.4, 483.2, 193.7, 468.9, 182.5, 453.6, 172.7, 477.3, 175.2,
+476.5, 183.3, 499.3, 193.9, 505.2, 196.6, 505.2, 196.6, 511.1, 199.4,
+524.8, 205.7, 524.1, 208.3, 538.5, 212.0, 480.4, 35.4, 492.4, 20.0,
+492.4, 20.0, 504.3, 4.5, 503.8, 5.4, 504.9, 6.1, 505.4, 7.7,
+500.4, 15.2, 499.3, 14.5, 493.2, 21.2, 486.8, 28.3, 485.7, 27.5,
+480.4, 35.4, 413.7, 132.6, 409.4, 132.7, 408.4, 130.9, 405.1, 132.7,
+406.3, 131.1, 406.3, 131.1, 407.5, 129.4, 427.1, 106.7, 425.9, 105.7,
+444.3, 82.0, 453.8, 73.5, 465.3, 82.3, 470.6, 73.3, 450.2, 107.6,
+442.5, 103.1, 414.2, 132.6, 414.0, 132.6, 414.0, 132.6, 413.7, 132.6,
+470.6, 73.3, 480.3, 65.3, 491.4, 73.5, 496.1, 64.9, 470.4, 92.8,
+470.4, 92.8, 444.8, 120.8, 428.9, 123.9, 424.2, 121.8, 414.2, 132.6,
+414.0, 132.6, 414.0, 132.6, 413.7, 132.6, 442.0, 102.8, 439.1, 99.2,
+470.6, 73.3, 578.5, 1.0, 573.7, -6.8, 567.4, -13.1, 568.9, -14.7,
+569.8, -17.4, 571.5, -18.3, 570.6, -20.1, 568.1, -21.8, 573.8, -30.5,
+577.0, -41.0, 574.6, -40.2, 577.3, -31.7, 577.7, -22.4, 578.1, -10.7,
+584.0, -3.9, 578.5, 1.0, 240.9, 156.3, 240.3, 155.6, 239.5, 155.4,
+239.6, 154.9, 248.0, 118.7, 248.0, 118.7, 256.4, 82.5, 253.0, 96.4,
+253.5, 96.5, 250.6, 110.4, 245.7, 133.4, 246.6, 133.6, 240.9, 156.3,
+254.9, 243.8, 255.7, 242.2, 250.0, 239.5, 245.2, 235.3, 235.1, 226.5,
+234.7, 226.9, 225.0, 217.7, 225.1, 217.0, 225.1, 217.0, 225.3, 216.3,
+226.0, 217.1, 248.9, 202.2, 265.1, 182.1, 266.6, 180.8, 266.6, 180.8,
+268.0, 179.5, 268.3, 176.5, 271.1, 176.8, 274.3, 174.1, 267.1, 209.5,
+270.2, 211.6, 254.9, 243.8, 274.3, 174.1, 280.3, 169.0, 280.3, 169.0,
+286.3, 163.8, 286.7, 162.2, 291.1, 164.4, 293.7, 162.2, 289.7, 178.2,
+281.7, 180.7, 285.8, 194.3, 287.8, 196.8, 281.7, 200.4, 283.1, 204.9,
+269.4, 209.6, 276.7, 231.0, 270.2, 257.1, 270.8, 256.4, 254.6, 250.1,
+254.9, 243.8, 270.2, 211.6, 267.1, 209.5, 274.3, 174.1, 245.2, 235.3,
+250.0, 239.5, 255.7, 242.2, 254.9, 243.8, 254.6, 250.1, 270.8, 256.4,
+270.2, 257.1, 266.8, 270.9, 262.5, 271.2, 263.4, 284.6, 260.9, 283.4,
+260.9, 283.4, 258.3, 282.1, 252.2, 279.3, 252.4, 278.8, 246.4, 275.6,
+241.1, 256.7, 240.2, 253.9, 245.2, 235.3, 246.4, 275.6, 230.9, 267.1,
+231.4, 266.1, 215.5, 258.6, 220.2, 238.1, 220.2, 238.1, 225.0, 217.7,
+234.7, 226.9, 235.1, 226.5, 245.2, 235.3, 240.2, 253.9, 241.1, 256.7,
+246.4, 275.6, 357.1, 144.7, 357.5, 151.5, 356.2, 151.9, 358.0, 158.3,
+344.2, 177.1, 356.5, 189.2, 361.9, 218.8, 361.3, 219.5, 361.3, 219.5,
+360.7, 220.3, 355.7, 209.1, 352.7, 208.9, 352.1, 197.2, 350.9, 171.1,
+353.2, 170.7, 357.1, 144.7, 310.9, 158.1, 306.2, 144.2, 302.9, 144.6,
+301.6, 130.4, 301.6, 130.4, 301.5, 130.3, 301.5, 130.3, 301.9, 127.8,
+300.6, 127.6, 299.7, 125.0, 298.3, 122.7, 302.5, 120.1, 305.2, 115.3,
+310.9, 124.6, 306.7, 127.1, 308.3, 138.9, 309.6, 148.5, 305.7, 151.6,
+310.9, 158.1, 309.4, 53.8, 303.3, 55.2, 297.3, 55.1, 297.3, 56.6,
+292.3, 52.8, 282.4, 55.0, 278.3, 61.0, 273.9, 47.9, 275.0, 35.4,
+269.6, 34.9, 270.1, 33.0, 270.1, 33.0, 270.6, 31.1, 271.6, 29.7,
+271.6, 29.7, 272.6, 28.3, 291.9, 39.5, 295.2, 37.4, 309.4, 53.8,
+272.6, 28.3, 281.5, 15.3, 281.5, 15.3, 290.4, 2.4, 306.7, 25.1,
+311.9, 21.3, 333.5, 40.1, 330.6, 42.5, 329.4, 47.0, 331.0, 48.8,
+320.2, 51.3, 320.2, 51.3, 309.4, 53.8, 295.2, 37.4, 291.9, 39.5,
+272.6, 28.3, 263.4, 293.9, 267.9, 291.5, 272.4, 291.5, 272.4, 289.1,
+272.6, 289.1, 272.7, 289.2, 272.7, 289.2, 272.7, 289.1, 273.2, 289.5,
+273.8, 289.7, 269.1, 295.9, 277.3, 306.1, 286.0, 318.8, 284.3, 320.9,
+284.3, 320.9, 282.5, 323.1, 275.2, 330.1, 274.9, 329.8, 267.3, 336.5,
+265.5, 338.1, 265.4, 339.8, 263.7, 339.6, 263.6, 339.6, 263.7, 337.9,
+263.7, 336.2, 263.6, 315.1, 259.6, 314.3, 263.4, 293.9, 153.1, 461.8,
+164.8, 441.2, 168.2, 442.5, 176.4, 420.6, 176.4, 416.5, 178.7, 416.5,
+181.0, 412.4, 186.9, 405.9, 190.4, 408.1, 194.2, 400.8, 180.4, 426.9,
+177.6, 425.5, 161.0, 450.1, 157.1, 456.0, 156.4, 455.6, 153.1, 461.8,
+149.5, 388.3, 147.8, 368.2, 147.8, 368.2, 146.0, 348.1, 150.8, 329.2,
+150.0, 329.0, 154.0, 309.9, 158.2, 344.5, 152.0, 345.3, 150.0, 380.7,
+149.8, 384.5, 149.1, 384.6, 149.5, 388.3, 488.6, 852.4, 490.1, 864.3,
+489.4, 864.4, 491.6, 876.2, 491.8, 878.8, 490.7, 880.5, 492.0, 881.3,
+484.3, 884.0, 483.2, 883.3, 474.9, 881.8, 475.7, 881.1, 474.8, 880.1,
+474.7, 878.3, 473.9, 865.1, 467.8, 861.9, 473.1, 852.0, 474.8, 848.9,
+485.7, 848.5, 488.6, 852.4, 473.1, 852.0, 460.8, 773.6, 466.9, 772.5,
+463.6, 692.9, 466.0, 687.8, 466.3, 687.2, 466.1, 681.7, 466.6, 682.9,
+466.6, 682.9, 467.1, 684.1, 461.8, 693.8, 469.0, 697.8, 470.8, 711.5,
+474.2, 737.2, 474.2, 737.2, 477.6, 762.9, 477.9, 765.2, 477.9, 765.3,
+478.2, 767.6, 483.4, 810.0, 483.4, 810.0, 488.6, 852.4, 485.7, 848.5,
+474.8, 848.9, 473.1, 852.0, 580.6, 496.9, 580.8, 495.0, 576.3, 494.4,
+572.1, 491.9, 568.0, 489.5, 564.1, 489.9, 563.8, 487.0, 566.6, 477.0,
+566.8, 467.0, 569.4, 467.1, 572.1, 457.2, 568.4, 453.0, 574.9, 447.3,
+572.0, 449.9, 575.7, 454.1, 576.4, 460.9, 578.5, 478.9, 583.0, 479.5,
+580.6, 496.9, 570.5, 776.6, 568.1, 777.6, 568.6, 778.9, 566.7, 781.1,
+553.8, 796.7, 553.8, 796.7, 540.9, 812.3, 525.4, 830.9, 521.3, 851.8,
+510.0, 849.6, 512.6, 828.3, 512.6, 828.3, 515.2, 807.1, 515.6, 805.4,
+517.5, 805.8, 519.9, 804.5, 533.1, 797.2, 533.1, 797.2, 546.4, 789.9,
+558.4, 783.3, 557.9, 782.0, 570.5, 776.6, 538.4, 619.0, 540.1, 605.1,
+540.1, 605.1, 541.9, 591.3, 541.1, 592.1, 553.2, 598.7, 552.4, 604.6,
+551.4, 612.5, 537.6, 618.0, 538.4, 619.0, 476.1, 608.4, 475.7, 614.6,
+466.7, 616.9, 468.3, 620.3, 469.2, 607.8, 469.2, 607.8, 470.1, 595.2,
+468.0, 598.4, 476.6, 602.1, 476.1, 608.4, 470.1, 595.2, 472.3, 563.6,
+472.3, 563.6, 474.6, 531.9, 475.4, 529.7, 486.1, 528.4, 491.4, 532.6,
+488.9, 543.1, 496.5, 551.7, 508.7, 559.0, 503.3, 585.9, 497.1, 588.7,
+476.1, 608.4, 476.6, 602.1, 468.0, 598.4, 470.1, 595.2, 549.3, 443.3,
+563.3, 440.9, 563.8, 442.5, 577.4, 438.4, 576.8, 438.4, 576.1, 442.9,
+574.9, 447.3, 568.4, 453.0, 572.1, 457.2, 569.4, 467.1, 563.1, 466.9,
+562.9, 477.0, 556.3, 486.9, 544.2, 472.5, 552.8, 465.2, 549.3, 443.5,
+549.3, 443.4, 549.3, 443.3, 549.3, 443.3, 516.5, 550.4, 517.0, 557.6,
+517.0, 557.6, 517.5, 564.7, 517.6, 565.4, 517.1, 566.1, 517.0, 566.1,
+516.8, 566.1, 517.0, 565.3, 517.0, 564.6, 516.7, 557.5, 518.6, 551.0,
+516.5, 550.4, 474.2, 687.1, 474.0, 684.3, 473.9, 684.3, 473.7, 681.5,
+492.4, 628.3, 493.5, 628.7, 513.3, 575.9, 514.3, 573.4, 514.3, 573.5,
+515.2, 571.0, 505.3, 599.1, 505.3, 599.1, 495.3, 627.2, 484.8, 657.2,
+485.9, 657.6, 474.2, 687.1, 450.6, 316.3, 450.9, 313.9, 452.2, 312.4,
+451.2, 311.6, 456.5, 306.3, 452.4, 302.1, 453.6, 292.6, 456.8, 298.7,
+459.4, 299.2, 466.5, 301.3, 472.6, 312.8, 472.6, 312.8, 478.6, 324.2,
+478.7, 323.4, 471.0, 322.1, 463.3, 319.9, 461.5, 319.4, 461.5, 319.4,
+459.7, 318.9, 458.1, 318.4, 458.1, 318.4, 456.6, 318.0, 453.6, 317.2,
+452.2, 318.3, 450.6, 316.3, 193.0, 612.6, 191.5, 619.8, 193.1, 620.6,
+189.9, 626.9, 187.0, 635.3, 187.0, 635.3, 184.0, 643.8, 184.3, 642.6,
+183.1, 642.3, 182.2, 640.8, 182.7, 636.7, 183.7, 636.8, 185.3, 632.9,
+188.4, 624.7, 188.4, 624.7, 191.5, 616.5, 192.3, 614.5, 192.7, 614.6,
+193.0, 612.6, 219.0, 424.9, 221.4, 447.0, 233.1, 461.9, 223.8, 469.2,
+218.1, 495.7, 218.1, 495.7, 212.4, 522.3, 207.3, 474.5, 208.9, 472.1,
+219.0, 424.9, 212.4, 522.3, 209.1, 537.8, 209.1, 537.8, 205.8, 553.2,
+203.9, 543.4, 200.7, 544.0, 195.7, 534.8, 207.3, 489.8, 222.8, 476.9,
+205.2, 441.3, 211.9, 430.8, 211.9, 430.8, 218.5, 420.3, 217.7, 422.0,
+218.8, 422.6, 219.0, 424.9, 208.9, 472.1, 207.3, 474.5, 212.4, 522.3,
+461.7, 734.0, 463.2, 759.7, 463.1, 759.8, 464.7, 785.5, 462.5, 801.0,
+462.5, 801.0, 460.2, 816.6, 455.1, 797.7, 446.6, 800.0, 433.0, 783.4,
+430.7, 774.5, 439.5, 772.2, 446.0, 761.1, 450.1, 753.9, 450.1, 753.9,
+454.2, 746.8, 458.0, 740.4, 463.1, 739.0, 461.7, 734.0, 447.6, 495.3,
+448.0, 501.2, 447.9, 501.2, 448.3, 507.2, 450.3, 533.5, 449.9, 533.5,
+451.5, 559.9, 451.9, 567.7, 451.9, 567.7, 452.4, 575.5, 451.9, 567.7,
+451.9, 567.7, 451.5, 559.9, 449.9, 533.5, 450.3, 533.5, 448.3, 507.2,
+447.9, 501.2, 448.0, 501.2, 447.6, 495.3, 413.5, 651.4, 420.1, 608.0,
+416.2, 607.4, 419.2, 563.4, 419.9, 555.0, 419.9, 555.0, 420.6, 546.5,
+420.6, 543.2, 420.9, 543.2, 421.2, 539.9, 424.7, 497.4, 425.3, 497.4,
+428.3, 454.9, 429.0, 448.0, 429.9, 448.1, 431.6, 441.3, 431.6, 441.3,
+431.6, 441.3, 431.6, 441.3, 436.2, 422.4, 436.2, 422.4, 440.8, 403.5,
+441.3, 401.3, 442.4, 401.0, 441.8, 399.1, 442.2, 404.5, 442.2, 404.5,
+442.5, 409.9, 428.9, 530.8, 431.7, 531.2, 413.5, 651.4, 442.5, 409.9,
+443.4, 424.6, 443.4, 424.6, 444.3, 439.3, 444.3, 445.8, 443.4, 445.8,
+442.5, 452.2, 432.1, 526.1, 432.1, 526.1, 421.8, 600.0, 417.1, 633.6,
+417.8, 633.8, 412.4, 667.3, 413.3, 659.4, 413.0, 659.4, 413.5, 651.4,
+431.7, 531.2, 428.9, 530.8, 442.5, 409.9, 383.3, 722.9, 393.5, 735.4,
+396.2, 749.3, 403.8, 747.8, 409.1, 754.3, 409.1, 754.3, 414.4, 760.8,
+416.7, 763.5, 416.7, 763.5, 418.9, 766.3, 423.1, 771.4, 424.8, 770.6,
+427.3, 776.5, 434.5, 793.1, 432.8, 793.9, 438.3, 811.2, 438.3, 811.3,
+438.3, 811.3, 438.3, 811.3, 438.3, 811.3, 438.3, 811.3, 438.3, 811.2,
+420.6, 782.9, 420.6, 782.9, 403.0, 754.5, 393.1, 738.7, 381.6, 737.4,
+383.3, 722.9, 366.8, 867.4, 358.6, 864.1, 355.0, 857.9, 350.4, 860.7,
+354.5, 820.5, 354.5, 820.5, 358.6, 780.4, 359.1, 775.3, 359.2, 775.3,
+359.7, 770.2, 363.7, 811.1, 362.7, 811.2, 365.7, 852.2, 366.3, 859.8,
+369.6, 865.9, 366.8, 867.4, 377.8, 211.3, 377.6, 210.3, 377.6, 210.3,
+377.5, 209.4, 397.1, 195.9, 394.7, 192.4, 411.9, 175.4, 400.3, 185.7,
+401.1, 186.7, 390.4, 198.0, 389.4, 199.1, 389.4, 199.1, 388.3, 200.2,
+383.0, 205.7, 383.6, 206.3, 377.8, 211.3, 239.8, 662.8, 239.0, 701.7,
+233.3, 701.6, 226.8, 740.4, 226.3, 743.4, 225.6, 743.4, 225.8, 746.3,
+225.1, 709.3, 223.8, 709.3, 221.9, 672.3, 221.9, 670.5, 225.1, 670.6,
+228.3, 668.9, 234.0, 665.8, 239.7, 665.9, 239.8, 662.8, 223.6, 759.5,
+224.7, 752.9, 226.0, 752.9, 225.8, 746.3, 225.6, 743.4, 226.3, 743.4,
+226.8, 740.4, 243.2, 724.1, 244.4, 725.4, 262.0, 710.4, 263.0, 709.5,
+263.0, 709.5, 264.0, 708.7, 312.0, 667.8, 311.7, 667.4, 360.0, 626.8,
+359.6, 627.2, 360.2, 627.9, 359.8, 628.3, 353.5, 635.0, 353.1, 634.7,
+346.4, 641.2, 315.7, 670.8, 315.7, 670.8, 284.9, 700.5, 262.0, 722.6,
+262.0, 722.6, 239.0, 744.7, 237.6, 746.0, 237.6, 746.0, 236.2, 747.4,
+229.9, 753.5, 229.3, 760.1, 223.6, 759.5, 285.2, 852.2, 296.7, 804.1,
+297.5, 803.6, 317.2, 758.2, 316.2, 760.6, 323.3, 763.1, 322.6, 766.3,
+318.0, 785.6, 314.5, 784.8, 306.5, 803.3, 295.8, 827.8, 291.3, 826.7,
+285.2, 852.2, 349.9, 662.3, 350.7, 654.7, 355.3, 654.7, 355.0, 647.4,
+352.0, 670.9, 358.7, 672.9, 351.3, 694.7, 351.0, 696.9, 351.9, 697.8,
+350.7, 699.0, 338.7, 735.0, 345.8, 737.4, 340.8, 775.8, 339.6, 719.2,
+343.7, 718.9, 349.9, 662.3, 340.8, 775.8, 339.9, 783.2, 339.9, 783.2,
+338.9, 790.7, 327.5, 784.7, 330.7, 778.5, 322.6, 766.3, 323.3, 763.1,
+316.2, 760.6, 317.2, 758.2, 319.6, 741.4, 322.8, 741.8, 328.4, 725.4,
+338.4, 696.1, 338.4, 696.1, 348.4, 666.8, 349.1, 664.5, 349.7, 664.6,
+349.9, 662.3, 343.7, 718.9, 339.6, 719.2, 340.8, 775.8, 385.0, 633.5,
+387.8, 636.1, 383.1, 641.1, 381.3, 648.8, 380.1, 653.4, 380.1, 653.4,
+379.0, 658.0, 378.4, 660.7, 376.7, 661.9, 377.7, 663.3, 370.4, 646.5,
+368.6, 647.3, 359.5, 631.2, 359.8, 632.0, 361.1, 631.4, 362.7, 631.5,
+373.8, 632.5, 378.5, 627.5, 385.0, 633.5, 400.7, 531.7, 402.1, 534.9,
+401.5, 535.4, 403.6, 538.0, 403.4, 537.8, 403.4, 537.8, 403.2, 537.7,
+357.2, 510.5, 356.8, 511.0, 310.5, 484.3, 343.2, 510.4, 348.2, 504.2,
+386.0, 524.0, 393.3, 527.9, 394.3, 526.7, 400.7, 531.7, 356.4, 303.0,
+350.0, 293.2, 344.7, 282.9, 343.5, 283.5, 344.3, 276.1, 338.5, 275.2,
+332.8, 267.4, 335.2, 259.1, 340.4, 253.4, 337.6, 250.9, 339.1, 248.9,
+340.9, 248.6, 340.6, 246.9, 340.1, 246.3, 343.2, 243.5, 345.7, 240.1,
+353.6, 266.3, 350.3, 267.3, 355.0, 294.4, 355.7, 298.7, 353.9, 300.6,
+356.4, 303.0, 384.0, 325.9, 390.1, 329.9, 392.4, 326.4, 400.9, 326.8,
+403.1, 330.3, 415.1, 326.1, 415.3, 327.6, 419.6, 334.6, 419.6, 334.6,
+423.8, 341.6, 403.5, 340.5, 401.6, 337.3, 384.0, 325.9, 354.1, 435.3,
+341.6, 425.3, 342.3, 424.3, 329.1, 415.3, 326.5, 412.4, 326.5, 412.4,
+324.0, 409.5, 332.6, 410.0, 335.1, 405.5, 342.0, 397.7, 367.3, 394.6,
+370.0, 392.8, 393.7, 400.8, 390.0, 399.5, 387.8, 405.9, 381.9, 411.1,
+368.0, 423.2, 368.9, 434.1, 354.1, 435.3, 272.8, 437.6, 273.4, 436.3,
+273.4, 436.3, 274.1, 435.0, 268.9, 461.0, 268.4, 460.9, 262.9, 486.8,
+261.9, 491.5, 261.0, 496.3, 261.0, 496.3, 261.0, 496.3, 261.9, 491.5,
+262.9, 486.8, 267.9, 462.2, 267.5, 462.1, 272.8, 437.6, 67.4, 175.0,
+67.4, 174.8, 67.4, 174.8, 67.4, 174.6, 69.5, 163.5, 68.3, 163.3,
+69.2, 152.0, 70.3, 157.7, 73.0, 157.2, 76.7, 162.3, 87.0, 209.4,
+89.9, 208.8, 103.2, 255.3, 115.4, 232.0, 85.9, 216.5, 68.6, 177.8,
+68.0, 176.4, 67.9, 176.4, 67.4, 175.0, 60.2, 159.3, 61.6, 171.9,
+66.0, 173.1, 63.0, 184.6, 63.0, 185.0, 63.2, 185.1, 63.0, 185.5,
+63.0, 185.5, 63.0, 185.5, 63.0, 185.6, 65.5, 192.5, 53.0, 194.9,
+48.4, 206.4, 44.4, 195.3, 38.1, 193.9, 40.3, 184.2, 41.9, 137.6,
+41.9, 137.6, 43.5, 91.0, 30.1, 107.8, 53.0, 124.9, 60.2, 159.3,
+43.5, 91.0, 43.7, 85.4, 43.7, 85.4, 43.9, 79.8, 46.9, 72.5,
+46.3, 72.1, 47.6, 64.1, 49.1, 68.4, 49.1, 68.4, 50.5, 72.6,
+53.3, 116.1, 55.4, 116.0, 60.2, 159.3, 53.0, 124.9, 30.1, 107.8,
+43.5, 91.0, 681.0, 569.7, 678.1, 569.9, 678.1, 569.9, 675.1, 570.2,
+666.1, 555.9, 665.9, 556.0, 656.7, 541.9, 658.6, 545.0, 659.2, 544.7,
+661.6, 547.5, 671.3, 558.6, 673.6, 557.2, 681.0, 569.7, 641.5, 479.0,
+641.5, 479.0, 641.5, 479.0, 641.6, 478.9, 639.0, 466.5, 650.4, 464.2,
+659.2, 449.5, 665.3, 457.3, 683.8, 457.8, 686.6, 453.1, 689.2, 456.4,
+689.2, 456.4, 691.8, 459.8, 668.7, 472.9, 666.6, 469.3, 641.6, 478.9,
+641.5, 479.0, 641.5, 479.0, 641.5, 479.0, 791.8, 137.2, 766.4, 118.0,
+768.4, 102.3, 740.9, 98.7, 740.1, 98.2, 740.1, 98.2, 739.3, 97.6,
+735.9, 98.2, 735.2, 94.5, 731.1, 91.4, 744.5, 94.7, 745.7, 89.9,
+760.4, 88.4, 777.7, 99.6, 772.6, 107.4, 784.9, 126.3, 788.4, 131.7,
+786.9, 134.1, 791.8, 137.2, 796.7, 204.0, 801.9, 215.2, 799.9, 216.7,
+807.2, 226.4, 792.9, 218.3, 792.9, 218.3, 778.7, 210.2, 772.5, 209.7,
+774.3, 201.8, 767.4, 197.3, 758.0, 193.0, 759.9, 188.8, 752.5, 180.3,
+776.1, 185.6, 777.9, 188.2, 796.7, 204.0, 752.5, 180.3, 747.3, 174.3,
+748.6, 172.2, 742.1, 168.4, 741.1, 167.9, 741.3, 167.5, 740.5, 166.5,
+752.1, 153.2, 750.4, 148.0, 765.4, 141.4, 766.1, 141.1, 768.7, 147.1,
+772.0, 152.8, 773.5, 155.3, 773.6, 155.2, 774.9, 157.7, 789.9, 177.8,
+785.8, 180.8, 796.7, 204.0, 777.9, 188.2, 776.1, 185.6, 752.5, 180.3,
+700.8, 121.1, 692.8, 111.8, 692.8, 111.8, 684.7, 102.6, 687.4, 102.5,
+689.7, 98.8, 689.9, 99.1, 714.6, 98.4, 715.3, 102.1, 739.3, 97.6,
+740.1, 98.2, 740.1, 98.2, 740.9, 98.7, 752.8, 100.3, 746.8, 120.3,
+760.7, 133.2, 761.0, 133.7, 760.9, 134.2, 761.4, 134.3, 731.0, 128.2,
+729.0, 132.7, 700.8, 121.1, 761.4, 134.3, 764.2, 135.2, 766.5, 139.8,
+765.4, 141.4, 750.4, 148.0, 752.1, 153.2, 740.5, 166.5, 723.1, 146.6,
+727.5, 138.7, 705.7, 126.7, 702.9, 126.2, 703.3, 123.9, 700.8, 121.1,
+729.0, 132.7, 731.0, 128.2, 761.4, 134.3, 583.1, 158.4, 580.9, 157.7,
+580.9, 157.7, 578.7, 157.1, 592.9, 148.5, 603.3, 151.5, 606.8, 139.6,
+632.0, 152.8, 632.0, 152.8, 657.2, 166.0, 636.3, 182.6, 620.1, 162.6,
+583.1, 158.4, 657.2, 166.0, 673.1, 174.3, 673.1, 174.3, 689.0, 182.7,
+693.0, 183.9, 691.0, 188.6, 695.0, 192.2, 690.8, 194.8, 687.8, 190.0,
+680.7, 187.9, 666.8, 183.7, 666.8, 183.7, 653.0, 179.5, 623.6, 160.4,
+618.0, 168.9, 583.1, 158.4, 620.1, 162.6, 636.3, 182.6, 657.2, 166.0,
+733.6, 362.1, 732.7, 362.5, 732.7, 362.5, 731.8, 362.9, 690.2, 300.2,
+690.1, 300.2, 645.8, 239.4, 637.4, 227.1, 637.8, 226.9, 629.3, 214.7,
+628.1, 213.3, 628.0, 213.3, 626.8, 212.0, 626.8, 212.0, 626.8, 212.0,
+626.8, 212.0, 632.4, 218.1, 632.4, 218.1, 638.0, 224.2, 638.4, 224.6,
+638.4, 224.6, 638.7, 224.9, 645.6, 232.5, 645.6, 232.5, 652.5, 240.0,
+665.2, 253.8, 665.2, 253.8, 677.8, 267.5, 681.5, 271.6, 681.5, 271.6,
+685.2, 275.6, 685.8, 276.2, 685.8, 276.2, 686.3, 276.8, 688.5, 279.2,
+688.5, 279.2, 690.7, 281.6, 707.9, 306.5, 708.9, 305.7, 727.2, 329.8,
+734.6, 343.7, 731.3, 345.8, 733.6, 362.1, 727.2, 329.8, 737.0, 342.8,
+734.4, 346.0, 746.8, 355.7, 740.2, 358.9, 740.2, 358.9, 733.6, 362.1,
+731.3, 345.8, 734.6, 343.7, 727.2, 329.8, 706.4, 330.7, 717.6, 347.6,
+716.2, 348.6, 728.7, 364.4, 725.4, 366.0, 725.4, 366.0, 722.0, 367.6,
+721.7, 367.9, 720.8, 365.9, 719.2, 365.0, 705.9, 360.5, 707.9, 354.5,
+696.7, 344.0, 687.0, 328.8, 685.1, 330.0, 673.6, 315.9, 675.5, 321.6,
+681.7, 319.5, 689.7, 323.2, 698.1, 326.9, 700.6, 324.6, 706.4, 330.7,
+668.8, 149.5, 679.7, 163.4, 677.9, 165.3, 690.7, 177.3, 674.4, 168.6,
+674.4, 168.6, 658.2, 159.9, 637.3, 162.6, 635.2, 146.2, 612.2, 132.4,
+622.3, 122.5, 643.8, 135.2, 668.8, 149.5, 540.0, 203.6, 544.8, 209.8,
+544.8, 209.8, 549.5, 216.0, 545.7, 212.3, 544.0, 214.0, 538.5, 212.0,
+524.1, 208.3, 524.8, 205.7, 511.1, 199.4, 515.0, 201.9, 516.3, 200.1,
+521.4, 200.9, 530.7, 202.2, 532.3, 199.4, 540.0, 203.6, 493.2, 21.2,
+499.3, 14.5, 500.4, 15.2, 505.4, 7.7, 510.5, 23.9, 510.5, 23.9,
+515.6, 40.0, 515.2, 40.4, 496.9, 33.0, 493.2, 21.2, 515.6, 40.0,
+516.0, 41.4, 516.9, 42.3, 516.5, 42.7, 506.3, 53.8, 506.3, 53.8,
+496.1, 64.9, 491.4, 73.5, 480.3, 65.3, 470.6, 73.3, 465.3, 82.3,
+453.8, 73.5, 444.3, 82.0, 462.3, 58.7, 462.3, 58.7, 480.4, 35.4,
+485.7, 27.5, 486.8, 28.3, 493.2, 21.2, 496.9, 33.0, 515.2, 40.4,
+515.6, 40.0, 587.3, -74.6, 589.1, -80.4, 589.5, -86.3, 590.9, -86.2,
+592.6, -88.4, 597.0, -86.2, 597.6, -88.0, 595.3, -80.4, 593.8, -76.3,
+587.3, -74.6, 279.7, 73.4, 281.6, 75.0, 283.3, 74.8, 283.5, 76.5,
+283.8, 77.5, 283.8, 77.5, 284.2, 78.5, 274.2, 128.6, 273.3, 128.7,
+268.0, 179.5, 266.6, 180.8, 266.6, 180.8, 265.1, 182.1, 265.5, 181.5,
+261.8, 178.5, 258.5, 175.0, 257.9, 162.7, 261.1, 162.6, 263.7, 150.1,
+265.7, 140.4, 265.7, 140.4, 267.7, 130.7, 273.7, 102.0, 271.5, 101.4,
+279.7, 73.4, 353.9, 96.6, 354.4, 104.1, 352.6, 104.8, 354.9, 111.6,
+355.4, 111.8, 354.4, 113.6, 355.1, 114.2, 353.0, 126.2, 350.2, 134.3,
+356.7, 139.2, 357.2, 139.2, 356.9, 141.9, 357.1, 144.7, 353.2, 170.7,
+350.9, 171.1, 352.1, 197.2, 352.7, 194.0, 350.3, 193.8, 349.5, 190.1,
+349.1, 188.4, 349.6, 188.2, 349.7, 186.4, 350.0, 180.1, 350.0, 180.1,
+350.3, 173.8, 352.1, 135.2, 351.1, 135.1, 353.9, 96.6, 336.8, 156.0,
+346.6, 170.8, 343.1, 173.1, 349.5, 190.1, 350.3, 193.8, 352.7, 194.0,
+352.1, 197.2, 352.7, 208.9, 355.7, 209.1, 360.7, 220.3, 357.9, 224.1,
+354.1, 225.8, 355.0, 227.9, 354.5, 228.5, 354.5, 229.1, 354.1, 229.1,
+349.0, 223.4, 336.9, 222.5, 333.8, 226.6, 331.8, 220.7, 331.8, 220.7,
+329.9, 214.9, 330.5, 191.8, 332.6, 191.9, 335.3, 168.8, 336.0, 162.4,
+339.5, 160.1, 336.8, 156.0, 197.4, 398.0, 197.4, 397.9, 195.8, 399.4,
+194.2, 400.8, 190.4, 408.1, 186.9, 405.9, 181.0, 412.4, 185.5, 404.4,
+185.5, 404.4, 190.1, 396.4, 191.7, 394.9, 195.3, 395.7, 197.4, 398.0,
+267.3, 336.5, 274.9, 329.8, 275.2, 330.1, 282.5, 323.1, 243.4, 371.5,
+243.4, 371.5, 204.3, 420.0, 173.8, 423.9, 175.8, 445.4, 149.2, 472.6,
+150.9, 467.6, 150.9, 467.6, 152.7, 462.6, 152.8, 462.2, 153.0, 462.3,
+153.1, 461.8, 156.4, 455.6, 157.1, 456.0, 161.0, 450.1, 211.6, 394.2,
+212.2, 394.7, 263.7, 339.6, 265.4, 339.8, 265.5, 338.1, 267.3, 336.5,
+161.0, 450.1, 177.6, 425.5, 180.4, 426.9, 194.2, 400.8, 195.8, 399.4,
+197.4, 397.9, 197.4, 398.0, 230.6, 368.8, 230.6, 368.8, 263.7, 339.6,
+265.4, 339.8, 265.5, 338.1, 267.3, 336.5, 214.3, 393.5, 219.1, 399.0,
+161.0, 450.1, 182.0, 340.9, 181.9, 342.0, 182.1, 342.0, 181.8, 343.0,
+179.0, 352.7, 179.1, 352.8, 176.4, 362.5, 177.8, 378.3, 163.4, 379.5,
+150.4, 396.6, 158.1, 366.7, 165.8, 368.5, 182.0, 340.9, 606.6, 614.7,
+613.0, 605.6, 611.2, 604.3, 615.8, 594.0, 621.3, 581.5, 619.6, 580.4,
+626.8, 569.1, 625.3, 571.4, 628.0, 572.7, 627.2, 575.8, 626.3, 579.7,
+625.3, 579.5, 623.3, 583.1, 615.0, 598.9, 616.8, 600.2, 606.6, 614.7,
+755.2, 696.0, 759.7, 703.1, 759.7, 703.1, 764.3, 710.3, 750.1, 721.0,
+751.8, 723.2, 739.3, 736.1, 743.2, 733.3, 741.4, 730.8, 743.5, 725.6,
+749.3, 710.8, 744.3, 704.0, 755.2, 696.0, 615.8, 594.0, 611.2, 604.3,
+613.0, 605.6, 606.6, 614.7, 598.3, 633.5, 603.5, 638.6, 590.1, 652.2,
+590.7, 651.6, 585.5, 646.4, 580.9, 640.6, 579.6, 639.0, 579.6, 639.0,
+578.3, 637.3, 577.2, 636.0, 576.0, 635.8, 576.1, 634.6, 576.4, 631.8,
+577.6, 632.0, 579.1, 629.3, 579.9, 628.0, 579.9, 628.0, 580.6, 626.7,
+586.2, 616.8, 583.9, 614.2, 591.8, 606.9, 601.4, 597.9, 609.1, 590.4,
+615.8, 594.0, 591.8, 606.9, 587.9, 600.6, 599.4, 593.4, 607.0, 580.0,
+621.3, 554.7, 618.1, 552.2, 635.6, 529.4, 635.9, 529.0, 641.7, 530.6,
+642.5, 533.4, 643.4, 536.7, 640.7, 537.4, 639.0, 541.4, 632.9, 555.2,
+632.9, 555.2, 626.8, 569.1, 619.6, 580.4, 621.3, 581.5, 615.8, 594.0,
+609.1, 590.4, 601.4, 597.9, 591.8, 606.9, 576.4, 460.9, 575.7, 454.1,
+572.0, 449.9, 574.9, 447.3, 576.1, 442.9, 576.8, 438.4, 577.4, 438.4,
+578.4, 438.1, 577.9, 436.5, 578.5, 434.6, 598.0, 464.0, 628.7, 470.0,
+623.1, 489.8, 630.0, 500.6, 631.2, 500.0, 636.9, 511.4, 637.2, 511.8,
+637.6, 512.1, 637.5, 512.3, 637.5, 512.3, 637.5, 512.3, 637.5, 512.3,
+607.0, 486.6, 604.0, 489.4, 576.4, 460.9, 637.5, 512.3, 639.7, 515.7,
+639.7, 515.7, 641.9, 519.1, 649.1, 530.3, 655.8, 530.1, 656.3, 541.6,
+656.1, 537.2, 649.4, 537.5, 642.5, 533.4, 641.7, 530.6, 635.9, 529.0,
+635.6, 529.4, 620.6, 520.5, 620.6, 520.5, 605.6, 511.6, 602.6, 509.9,
+602.6, 509.9, 599.7, 508.2, 590.1, 502.5, 584.7, 505.2, 580.6, 496.9,
+583.0, 479.5, 578.5, 478.9, 576.4, 460.9, 610.9, 469.7, 606.8, 486.0,
+636.9, 511.4, 637.2, 511.8, 637.6, 512.1, 637.5, 512.3, 637.5, 512.3,
+637.5, 512.3, 637.5, 512.3, 573.7, 767.7, 573.8, 767.6, 575.8, 769.6,
+575.5, 770.6, 574.2, 774.0, 573.6, 774.5, 570.5, 776.6, 557.9, 782.0,
+558.4, 783.3, 546.4, 789.9, 553.7, 788.1, 552.8, 784.7, 559.2, 779.4,
+566.5, 773.6, 565.6, 772.1, 573.7, 767.7, 595.9, 745.9, 603.8, 735.1,
+604.5, 735.6, 613.0, 725.3, 621.9, 714.5, 620.4, 712.7, 630.9, 703.8,
+630.8, 703.8, 632.3, 705.6, 633.8, 707.5, 634.9, 708.8, 636.2, 709.4,
+635.9, 710.2, 634.5, 713.3, 633.1, 712.6, 630.4, 715.1, 623.2, 721.6,
+623.2, 721.6, 616.0, 728.0, 609.7, 733.6, 609.7, 733.6, 603.4, 739.3,
+602.5, 740.0, 602.5, 740.0, 601.6, 740.8, 598.8, 743.4, 598.1, 742.9,
+595.9, 745.9, 545.8, 831.4, 545.8, 831.4, 550.9, 851.6, 556.0, 871.7,
+556.0, 871.9, 556.0, 871.9, 556.1, 872.2, 556.7, 874.7, 556.7, 874.7,
+557.4, 877.2, 557.6, 878.3, 557.7, 879.4, 557.9, 879.4, 539.7, 881.0,
+522.2, 884.3, 521.2, 880.5, 516.2, 860.4, 528.4, 835.8, 545.8, 831.4,
+521.2, 880.5, 518.2, 884.3, 506.9, 883.2, 506.1, 880.9, 511.1, 866.3,
+508.0, 865.3, 510.0, 849.6, 521.3, 851.8, 525.4, 830.9, 540.9, 812.3,
+538.9, 813.6, 542.7, 819.2, 544.4, 826.1, 544.5, 826.3, 544.5, 826.3,
+544.5, 826.6, 545.2, 829.0, 546.6, 829.6, 545.8, 831.4, 528.4, 835.8,
+516.2, 860.4, 521.2, 880.5, 501.0, 479.8, 509.2, 464.3, 517.2, 464.4,
+517.4, 448.8, 528.2, 446.9, 528.2, 446.9, 539.0, 445.1, 538.5, 445.1,
+538.5, 445.4, 538.1, 445.8, 529.7, 453.6, 529.7, 453.6, 521.2, 461.3,
+511.1, 470.5, 499.2, 474.1, 501.0, 479.8, 513.3, 575.9, 493.5, 628.7,
+492.4, 628.3, 473.7, 681.5, 473.3, 677.4, 473.3, 677.4, 472.9, 673.2,
+478.0, 656.7, 479.6, 657.2, 486.2, 641.2, 499.8, 608.6, 502.9, 609.6,
+513.3, 575.9, 495.3, 627.2, 497.7, 601.0, 504.3, 601.6, 513.3, 575.9,
+514.3, 573.4, 514.3, 573.5, 515.2, 571.0, 516.1, 568.5, 516.1, 568.5,
+517.0, 566.1, 517.1, 566.1, 517.6, 565.4, 517.5, 564.7, 519.4, 590.9,
+515.0, 592.1, 521.3, 617.1, 514.9, 633.7, 506.3, 648.9, 508.5, 650.2,
+506.8, 653.2, 507.2, 653.4, 506.0, 656.7, 498.3, 643.3, 494.0, 641.4,
+495.3, 627.2, 506.0, 656.7, 495.0, 685.1, 490.1, 684.0, 484.0, 713.4,
+484.7, 720.8, 478.3, 721.1, 478.0, 728.9, 471.0, 709.9, 470.8, 707.1,
+474.2, 687.1, 485.9, 657.6, 484.8, 657.2, 495.3, 627.2, 494.0, 641.4,
+498.3, 643.3, 506.0, 656.7, 445.1, 359.6, 446.0, 352.4, 448.3, 351.9,
+446.9, 345.2, 449.8, 331.0, 448.7, 330.7, 450.6, 316.3, 452.2, 318.3,
+453.6, 317.2, 456.6, 318.0, 457.2, 318.6, 456.1, 319.7, 455.6, 321.4,
+451.7, 335.6, 451.7, 335.6, 447.8, 349.9, 446.4, 354.8, 444.3, 355.2,
+445.1, 359.6, 158.8, 562.8, 158.5, 560.6, 157.7, 560.5, 158.2, 558.4,
+158.5, 556.2, 157.8, 556.1, 157.5, 553.7, 156.4, 553.2, 158.4, 549.0,
+159.2, 544.2, 160.5, 553.3, 159.4, 553.5, 158.8, 562.8, 418.2, 496.4,
+418.2, 496.3, 418.2, 496.3, 418.3, 496.3, 418.3, 496.3, 418.3, 496.3,
+418.3, 496.4, 418.3, 496.4, 418.2, 496.4, 418.2, 496.4, 379.0, 658.0,
+380.1, 653.4, 380.1, 653.4, 381.3, 648.8, 391.3, 646.7, 392.2, 650.9,
+403.1, 653.0, 407.3, 653.8, 411.0, 652.7, 411.6, 654.6, 410.7, 664.7,
+410.7, 664.7, 409.9, 674.8, 395.5, 676.3, 391.6, 669.5, 379.0, 658.0,
+409.9, 674.8, 408.8, 688.1, 411.8, 689.1, 407.6, 701.4, 407.5, 703.4,
+407.5, 703.4, 407.3, 705.4, 406.9, 710.5, 406.9, 710.5, 406.5, 715.6,
+406.4, 715.7, 406.4, 715.7, 406.4, 715.8, 406.3, 717.1, 406.0, 718.3,
+406.2, 718.4, 395.6, 705.4, 385.7, 702.3, 369.0, 705.6, 368.3, 705.3,
+367.7, 704.7, 367.8, 704.1, 372.0, 686.8, 377.1, 669.7, 376.3, 669.4,
+378.0, 667.1, 378.7, 665.6, 377.7, 663.3, 376.7, 661.9, 378.4, 660.7,
+379.0, 658.0, 391.6, 669.5, 395.5, 676.3, 409.9, 674.8, 442.5, 452.2,
+443.4, 445.8, 444.3, 445.8, 444.3, 439.3, 445.1, 452.6, 444.2, 452.7,
+445.8, 465.9, 448.0, 480.4, 446.7, 480.6, 447.6, 495.3, 448.0, 501.2,
+447.9, 501.2, 448.3, 507.2, 450.3, 533.5, 449.9, 533.5, 451.5, 559.9,
+451.9, 567.7, 451.9, 567.7, 452.4, 575.5, 452.6, 578.7, 452.6, 578.7,
+452.8, 581.9, 452.2, 585.4, 451.5, 585.3, 450.2, 588.8, 447.3, 596.8,
+443.2, 597.4, 444.3, 604.8, 439.9, 575.1, 444.0, 574.5, 443.6, 544.2,
+443.0, 498.2, 442.2, 498.2, 442.5, 452.2, 363.4, 734.1, 364.4, 724.0,
+361.6, 722.8, 365.4, 714.0, 365.5, 713.7, 365.5, 713.7, 365.5, 713.5,
+366.7, 708.8, 366.7, 708.8, 367.8, 704.1, 367.7, 704.7, 368.3, 705.3,
+369.0, 705.6, 374.3, 704.6, 376.1, 714.2, 383.3, 722.9, 381.6, 737.4,
+393.1, 738.7, 403.0, 754.5, 402.3, 755.6, 388.1, 746.8, 373.2, 739.2,
+368.3, 736.6, 364.5, 737.8, 363.4, 734.1, 382.3, 243.1, 380.0, 227.2,
+380.0, 227.2, 377.8, 211.3, 383.6, 206.3, 383.0, 205.7, 388.3, 200.2,
+392.4, 214.6, 386.1, 216.4, 383.8, 232.7, 383.0, 237.9, 381.0, 238.5,
+382.3, 243.1, 262.2, 528.4, 258.2, 543.6, 259.6, 544.0, 257.0, 559.6,
+255.0, 571.8, 255.0, 571.8, 252.9, 584.1, 247.6, 616.0, 247.6, 616.0,
+242.3, 647.9, 241.0, 655.3, 243.6, 657.0, 239.8, 662.8, 239.7, 665.9,
+234.0, 665.8, 228.3, 668.9, 240.2, 597.8, 243.9, 598.3, 262.2, 528.4,
+228.3, 668.9, 225.1, 670.6, 221.9, 670.5, 221.9, 672.3, 221.3, 662.2,
+223.9, 661.3, 220.8, 652.1, 226.3, 633.0, 226.4, 633.0, 231.9, 614.0,
+250.7, 549.3, 247.1, 548.0, 269.5, 484.6, 262.2, 505.2, 267.1, 506.8,
+262.2, 528.4, 243.9, 598.3, 240.2, 597.8, 228.3, 668.9, 212.2, 827.7,
+217.9, 793.6, 217.9, 793.6, 223.6, 759.5, 229.3, 760.1, 229.9, 753.5,
+236.2, 747.4, 233.8, 748.3, 235.0, 751.5, 233.8, 755.6, 232.7, 759.2,
+232.7, 759.2, 231.7, 762.7, 221.9, 795.2, 209.5, 826.6, 212.2, 827.7,
+252.9, 584.1, 255.0, 571.8, 255.0, 571.8, 257.0, 559.6, 255.6, 564.2,
+257.2, 564.7, 257.4, 569.9, 257.5, 573.8, 257.5, 573.8, 257.6, 577.7,
+258.6, 606.4, 272.9, 617.1, 259.5, 635.1, 270.6, 620.2, 253.8, 609.7,
+252.9, 584.1, 354.7, 534.6, 362.3, 542.1, 362.3, 542.1, 370.0, 549.5,
+371.9, 552.9, 369.4, 554.3, 368.8, 559.1, 364.9, 589.4, 368.6, 590.3,
+361.0, 619.6, 363.8, 608.6, 360.1, 607.6, 359.2, 595.6, 358.0, 579.6,
+358.0, 579.6, 356.8, 563.5, 356.1, 553.6, 356.1, 553.6, 355.4, 543.6,
+355.0, 539.1, 356.5, 535.4, 354.7, 534.6, 368.8, 559.1, 369.4, 554.3,
+371.9, 552.9, 370.0, 549.5, 373.2, 552.6, 376.3, 552.5, 376.5, 555.8,
+376.5, 556.1, 376.7, 556.1, 377.0, 556.3, 373.0, 558.1, 370.2, 560.5,
+368.8, 559.1, 386.0, 524.0, 348.2, 504.2, 343.2, 510.4, 310.5, 484.3,
+301.3, 479.0, 302.0, 476.9, 292.1, 473.7, 287.7, 470.6, 288.2, 469.9,
+284.3, 466.1, 282.0, 463.9, 282.5, 463.1, 279.8, 461.8, 294.6, 469.4,
+294.1, 470.2, 308.5, 478.6, 314.9, 482.3, 314.9, 482.3, 321.3, 486.1,
+353.7, 505.1, 351.5, 518.3, 386.0, 524.0, 355.0, 294.4, 350.3, 267.3,
+353.6, 266.3, 345.7, 240.1, 349.9, 234.6, 355.0, 230.2, 354.1, 229.1,
+354.5, 229.1, 354.5, 228.5, 355.0, 227.9, 367.7, 256.8, 363.9, 266.8,
+387.9, 282.3, 389.9, 289.5, 392.0, 289.0, 396.0, 295.6, 396.2, 296.0,
+396.1, 296.0, 396.1, 296.5, 375.5, 295.8, 364.6, 305.2, 355.0, 294.4,
+396.1, 296.5, 398.5, 311.6, 393.5, 314.7, 400.9, 326.8, 392.4, 326.4,
+390.1, 329.9, 384.0, 325.9, 377.6, 325.6, 375.1, 328.5, 371.2, 325.2,
+363.8, 314.1, 363.8, 314.1, 356.4, 303.0, 353.9, 300.6, 355.7, 298.7,
+355.0, 294.4, 364.6, 305.2, 375.5, 295.8, 396.1, 296.5, 437.6, 403.3,
+437.5, 403.4, 432.1, 404.9, 429.5, 402.8, 423.1, 391.0, 420.4, 392.5,
+411.3, 382.1, 410.7, 388.7, 420.7, 389.6, 430.0, 397.2, 433.8, 400.3,
+437.8, 400.6, 437.6, 403.3, 325.6, 322.0, 326.6, 319.9, 326.6, 319.9,
+327.5, 317.9, 341.1, 338.4, 353.4, 336.2, 381.6, 341.0, 386.2, 343.5,
+389.5, 348.0, 388.2, 350.8, 383.4, 361.2, 378.8, 359.0, 369.4, 367.2,
+365.1, 371.0, 364.9, 374.9, 360.7, 374.8, 357.9, 374.7, 358.1, 370.8,
+355.4, 366.8, 346.5, 353.4, 346.5, 353.4, 337.7, 340.1, 331.6, 331.0,
+330.1, 331.7, 325.6, 322.0, 420.5, 402.3, 415.5, 395.5, 407.1, 401.5,
+393.7, 400.8, 370.0, 392.8, 367.3, 394.6, 342.0, 397.7, 340.5, 399.4,
+338.5, 397.5, 334.9, 397.3, 334.6, 396.8, 337.2, 395.3, 339.5, 393.3,
+341.5, 391.6, 341.5, 391.6, 343.5, 389.8, 352.1, 382.3, 352.1, 382.3,
+360.7, 374.8, 364.9, 374.9, 365.1, 371.0, 369.4, 367.2, 397.1, 380.0,
+403.3, 378.7, 420.5, 402.3, 369.4, 367.2, 378.8, 359.0, 383.4, 361.2,
+388.2, 350.8, 388.6, 350.6, 390.1, 353.7, 392.0, 356.6, 396.6, 363.6,
+396.6, 363.6, 401.3, 370.6, 410.4, 387.3, 409.9, 388.2, 422.4, 402.4,
+422.1, 402.0, 421.2, 402.8, 420.5, 402.3, 403.3, 378.7, 397.1, 380.0,
+369.4, 367.2, 381.9, 411.1, 387.8, 405.9, 390.0, 399.5, 393.7, 400.8,
+407.1, 401.5, 415.5, 395.5, 420.5, 402.3, 421.2, 402.8, 422.1, 402.0,
+422.4, 402.4, 424.0, 404.3, 428.6, 401.2, 429.5, 402.8, 432.1, 404.9,
+437.5, 403.4, 437.6, 403.3, 439.2, 403.4, 440.7, 403.7, 440.8, 403.5,
+436.2, 422.4, 436.2, 422.4, 431.6, 441.3, 431.6, 441.2, 431.5, 441.2,
+431.3, 441.1, 421.1, 434.9, 421.1, 434.9, 410.9, 428.7, 396.4, 419.9,
+389.8, 423.9, 381.9, 411.1, 226.5, 544.9, 227.1, 537.3, 227.1, 537.3,
+227.6, 529.8, 242.2, 506.7, 239.7, 505.1, 251.8, 480.4, 251.0, 482.0,
+251.2, 482.0, 250.5, 483.7, 242.6, 503.9, 242.6, 503.9, 234.6, 524.2,
+230.6, 534.6, 234.1, 539.0, 226.5, 544.9, 68.6, 177.8, 85.9, 216.5,
+115.4, 232.0, 103.2, 255.3, 104.5, 259.8, 104.5, 259.8, 105.7, 264.3,
+105.7, 264.1, 105.7, 264.1, 105.6, 264.0, 103.7, 259.7, 103.7, 259.7,
+101.9, 255.4, 85.3, 216.6, 82.1, 176.7, 68.6, 177.8, 685.5, 569.3,
+683.3, 569.5, 683.3, 569.5, 681.0, 569.7, 673.6, 557.2, 671.3, 558.6,
+661.6, 547.5, 672.6, 546.6, 674.9, 557.2, 685.5, 569.3, 702.7, 480.0,
+702.5, 483.1, 702.5, 483.1, 702.3, 486.2, 700.5, 484.1, 694.7, 489.0,
+687.0, 491.8, 687.5, 485.4, 693.9, 483.3, 702.7, 480.0, 857.0, 143.8,
+836.9, 149.9, 832.1, 143.9, 816.9, 156.1, 811.6, 152.0, 811.6, 152.0,
+806.2, 148.0, 830.0, 140.7, 841.8, 132.4, 857.0, 143.8, 792.8, 174.5,
+792.8, 174.5, 795.0, 176.8, 797.2, 179.1, 799.7, 181.7, 799.7, 181.7,
+802.1, 184.2, 802.4, 184.5, 802.4, 184.5, 802.7, 184.8, 809.4, 191.8,
+809.4, 191.8, 816.1, 198.8, 816.8, 199.5, 816.8, 199.5, 817.5, 200.3,
+850.9, 235.1, 853.6, 232.9, 884.2, 269.9, 875.7, 265.1, 875.7, 265.1,
+867.2, 260.3, 832.9, 224.5, 835.0, 222.5, 802.7, 184.8, 802.4, 184.5,
+802.4, 184.5, 802.1, 184.2, 799.7, 181.7, 799.7, 181.7, 797.2, 179.1,
+795.0, 176.8, 792.8, 174.5, 792.8, 174.5, 867.2, 260.3, 842.6, 246.4,
+842.6, 246.4, 817.9, 232.4, 799.6, 193.6, 800.5, 192.0, 774.9, 157.7,
+773.6, 155.2, 773.5, 155.3, 772.0, 152.8, 783.0, 163.0, 782.9, 163.3,
+792.8, 174.5, 830.4, 217.0, 845.6, 210.6, 867.2, 260.3, 797.2, 179.1,
+795.0, 176.8, 792.8, 174.5, 792.8, 174.5, 782.9, 163.3, 783.0, 163.0,
+772.0, 152.8, 768.7, 147.1, 766.1, 141.1, 765.4, 141.4, 766.5, 139.8,
+764.2, 135.2, 761.4, 134.3, 760.9, 134.2, 761.0, 133.7, 760.7, 133.2,
+767.4, 139.3, 771.4, 135.0, 782.2, 136.8, 798.9, 152.6, 801.2, 150.2,
+820.2, 163.7, 821.3, 166.9, 809.8, 174.1, 797.2, 179.1, 701.9, 194.3,
+712.7, 197.5, 715.9, 194.0, 723.5, 200.8, 762.4, 221.2, 762.4, 221.2,
+801.3, 241.7, 802.0, 241.9, 799.9, 248.5, 798.5, 255.3, 795.0, 256.8,
+792.7, 251.7, 787.0, 248.0, 786.8, 247.9, 786.8, 247.9, 786.5, 247.7,
+784.3, 246.3, 784.3, 246.3, 782.1, 244.9, 769.7, 237.1, 769.7, 237.1,
+757.4, 229.3, 757.4, 229.3, 757.4, 229.3, 757.4, 229.3, 729.6, 211.8,
+726.8, 215.2, 701.9, 194.3, 626.8, 212.0, 636.0, 225.9, 636.5, 225.5,
+645.8, 239.4, 667.0, 268.4, 665.7, 269.4, 685.6, 299.3, 655.5, 256.1,
+655.9, 255.9, 626.8, 212.0, 635.9, 116.5, 646.2, 125.1, 646.2, 125.1,
+656.5, 133.7, 657.2, 142.1, 662.6, 141.6, 668.8, 149.5, 643.8, 135.2,
+622.3, 122.5, 612.2, 132.4, 593.2, 121.1, 593.2, 121.1, 574.2, 109.7,
+572.8, 107.4, 572.2, 107.5, 569.8, 106.0, 580.0, 103.8, 580.0, 103.8,
+590.2, 101.6, 613.7, 105.4, 614.5, 106.1, 635.9, 116.5, 590.2, 101.6,
+600.3, 99.5, 600.3, 99.5, 610.4, 97.3, 613.6, 104.6, 618.0, 102.6,
+625.6, 107.8, 631.3, 111.2, 630.8, 112.2, 635.9, 116.5, 614.5, 106.1,
+613.7, 105.4, 590.2, 101.6, 499.3, 193.9, 476.5, 183.3, 477.3, 175.2,
+453.6, 172.7, 441.0, 164.6, 441.0, 164.6, 428.4, 156.5, 457.3, 169.0,
+456.3, 171.2, 484.2, 186.0, 491.8, 189.9, 491.5, 190.5, 499.3, 193.9,
+601.4, 22.8, 597.4, 24.3, 593.3, 25.3, 593.4, 25.8, 591.9, 23.2,
+590.0, 21.0, 590.4, 20.7, 590.7, 20.3, 589.6, 19.4, 588.8, 18.1,
+595.3, 19.3, 598.7, 18.3, 601.4, 22.8, 276.3, 70.7, 278.0, 72.0,
+278.0, 72.0, 279.7, 73.4, 271.5, 101.4, 273.7, 102.0, 267.7, 130.7,
+255.6, 120.9, 270.1, 100.3, 276.3, 70.7, 349.7, 186.4, 349.6, 188.2,
+349.1, 188.4, 349.5, 190.1, 343.1, 173.1, 346.6, 170.8, 336.8, 156.0,
+330.5, 139.2, 331.3, 138.9, 324.3, 122.5, 337.7, 154.1, 337.7, 154.2,
+349.7, 186.4, 335.3, 168.8, 332.6, 191.9, 330.5, 191.8, 329.9, 214.9,
+320.4, 186.5, 320.4, 186.5, 310.9, 158.1, 305.7, 151.6, 309.6, 148.5,
+308.3, 138.9, 325.1, 146.3, 330.3, 151.4, 335.3, 168.8, 308.3, 138.9,
+306.7, 127.1, 310.9, 124.6, 305.2, 115.3, 310.1, 106.4, 310.1, 106.4,
+315.0, 97.6, 323.2, 100.7, 319.7, 110.0, 324.3, 122.5, 331.3, 138.9,
+330.5, 139.2, 336.8, 156.0, 339.5, 160.1, 336.0, 162.4, 335.3, 168.8,
+330.3, 151.4, 325.1, 146.3, 308.3, 138.9, 263.7, 336.2, 263.7, 337.9,
+263.6, 339.6, 263.7, 339.6, 230.6, 368.8, 230.6, 368.8, 197.4, 398.0,
+195.3, 395.7, 191.7, 394.9, 190.1, 396.4, 202.0, 375.3, 211.2, 376.4,
+213.9, 354.1, 213.9, 354.1, 214.0, 354.0, 214.0, 353.9, 238.8, 344.9,
+238.4, 343.4, 263.7, 336.2, 214.0, 353.9, 226.4, 331.9, 237.8, 332.4,
+238.9, 309.9, 240.5, 309.0, 240.7, 308.1, 241.0, 306.0, 252.2, 300.0,
+252.2, 300.0, 263.4, 293.9, 259.6, 314.3, 263.6, 315.1, 263.7, 336.2,
+238.4, 343.4, 238.8, 344.9, 214.0, 353.9, 150.0, 380.7, 152.0, 345.3,
+158.2, 344.5, 154.0, 309.9, 154.8, 305.8, 154.8, 305.8, 155.7, 301.6,
+155.8, 301.9, 157.5, 301.0, 159.2, 300.4, 157.2, 333.4, 155.5, 333.2,
+151.7, 366.1, 150.8, 373.4, 150.3, 373.4, 150.0, 380.7, 591.4, 653.9,
+591.6, 653.5, 589.9, 652.7, 590.1, 652.2, 603.5, 638.6, 598.3, 633.5,
+606.6, 614.7, 616.8, 600.2, 615.0, 598.9, 623.3, 583.1, 612.9, 586.0,
+616.4, 598.5, 609.4, 614.0, 600.4, 633.9, 601.3, 634.3, 591.4, 653.9,
+607.0, 580.0, 599.4, 593.4, 587.9, 600.6, 591.8, 606.9, 583.9, 614.2,
+586.2, 616.8, 580.6, 626.7, 575.6, 621.5, 580.0, 612.0, 586.2, 601.1,
+593.2, 588.6, 607.2, 580.2, 607.0, 580.0, 586.2, 601.1, 582.8, 599.4,
+589.3, 586.8, 592.4, 572.5, 593.4, 567.5, 593.4, 567.5, 594.5, 562.6,
+599.8, 538.4, 599.8, 538.4, 605.0, 514.1, 605.3, 512.9, 605.8, 511.8,
+605.6, 511.6, 620.6, 520.5, 620.6, 520.5, 635.6, 529.4, 618.1, 552.2,
+621.3, 554.7, 607.0, 580.0, 607.2, 580.2, 593.2, 588.6, 586.2, 601.1,
+523.8, 737.3, 526.4, 716.5, 526.4, 716.5, 529.0, 695.8, 528.6, 697.6,
+530.3, 697.9, 531.6, 700.1, 538.0, 710.4, 538.1, 710.3, 544.4, 720.7,
+544.4, 720.7, 544.4, 720.7, 544.4, 720.7, 534.1, 729.0, 523.0, 735.9,
+523.8, 737.3, 573.1, 676.7, 567.3, 670.3, 574.9, 663.4, 576.6, 650.0,
+576.6, 649.8, 576.6, 649.8, 576.7, 649.6, 577.5, 643.4, 575.7, 642.6,
+578.3, 637.3, 579.6, 639.0, 579.6, 639.0, 580.9, 640.6, 581.0, 646.3,
+579.7, 646.3, 578.4, 652.1, 575.8, 664.4, 568.2, 671.3, 573.1, 676.7,
+536.3, 636.7, 537.3, 627.8, 537.3, 627.8, 538.4, 619.0, 537.6, 618.0,
+551.4, 612.5, 552.4, 604.6, 552.6, 604.5, 553.8, 606.4, 555.2, 608.1,
+563.2, 618.2, 573.3, 624.9, 571.1, 628.3, 563.9, 639.2, 551.3, 641.0,
+536.3, 636.7, 549.3, 443.5, 552.8, 465.2, 544.2, 472.5, 556.3, 486.9,
+555.7, 487.9, 555.3, 487.8, 555.0, 488.9, 553.7, 495.1, 551.2, 494.6,
+547.5, 500.3, 547.7, 500.0, 547.5, 499.8, 547.5, 499.4, 547.6, 497.4,
+547.6, 497.4, 547.6, 495.4, 547.8, 490.8, 547.8, 490.8, 547.9, 486.2,
+548.0, 483.4, 548.0, 483.4, 548.1, 480.6, 548.7, 462.0, 557.9, 450.1,
+549.3, 443.5, 517.0, 564.6, 517.0, 565.3, 516.8, 566.1, 517.0, 566.1,
+516.1, 568.5, 516.1, 568.5, 515.2, 571.0, 514.3, 573.5, 514.3, 573.4,
+513.3, 575.9, 502.9, 609.6, 499.8, 608.6, 486.2, 641.2, 493.4, 624.6,
+492.9, 624.4, 499.7, 607.6, 508.3, 586.1, 508.0, 586.0, 517.0, 564.6,
+469.6, 385.7, 466.8, 389.5, 464.2, 393.4, 463.9, 393.2, 459.0, 391.3,
+455.0, 392.5, 454.1, 389.4, 452.7, 386.4, 452.7, 386.4, 451.3, 383.3,
+452.5, 381.9, 454.8, 383.8, 458.3, 384.2, 463.9, 385.0, 467.7, 382.6,
+469.6, 385.7, 396.3, 586.8, 397.2, 582.8, 397.3, 582.8, 398.2, 578.9,
+398.4, 578.1, 398.4, 578.1, 398.6, 577.3, 409.8, 571.4, 405.2, 559.0,
+407.6, 540.2, 413.0, 527.4, 419.1, 522.3, 414.3, 512.8, 411.6, 510.8,
+416.2, 504.6, 418.2, 496.4, 418.2, 496.4, 418.3, 496.4, 418.3, 496.4,
+419.0, 502.7, 419.0, 502.7, 419.7, 509.1, 410.4, 547.4, 408.3, 546.9,
+396.9, 584.7, 396.6, 585.7, 396.5, 585.7, 396.3, 586.8, 406.5, 715.6,
+406.9, 710.5, 406.9, 710.5, 407.3, 705.4, 414.6, 682.2, 422.2, 683.1,
+424.1, 659.7, 421.7, 688.2, 416.6, 688.1, 406.5, 715.6, 425.5, 707.4,
+429.7, 691.9, 428.8, 691.6, 432.1, 675.9, 441.2, 632.3, 440.7, 632.2,
+450.2, 588.8, 451.5, 585.3, 452.2, 585.4, 452.8, 581.9, 453.1, 588.3,
+453.1, 588.3, 453.5, 594.6, 451.7, 607.4, 450.4, 607.2, 447.3, 619.8,
+436.4, 663.6, 437.3, 663.9, 425.5, 707.4, 421.8, 600.0, 432.1, 526.1,
+432.1, 526.1, 442.5, 452.2, 442.2, 498.2, 443.0, 498.2, 443.6, 544.2,
+443.5, 549.1, 441.7, 549.1, 439.7, 554.0, 430.8, 577.0, 422.4, 576.8,
+421.8, 600.0, 450.1, 848.6, 452.0, 854.6, 455.4, 855.6, 453.9, 860.5,
+453.3, 865.0, 453.3, 865.0, 452.6, 869.4, 442.1, 862.3, 444.4, 858.9,
+436.1, 848.5, 428.4, 838.7, 428.5, 829.0, 420.7, 828.9, 435.5, 829.1,
+437.5, 836.6, 450.1, 848.6, 365.7, 852.2, 362.7, 811.2, 363.7, 811.1,
+359.7, 770.2, 360.0, 767.1, 360.0, 767.1, 360.3, 764.1, 361.6, 774.6,
+361.0, 774.7, 361.6, 785.4, 363.7, 818.8, 361.5, 819.0, 365.7, 852.2,
+444.9, 882.7, 427.4, 888.3, 425.7, 884.8, 406.6, 883.8, 405.1, 880.8,
+402.4, 882.1, 398.3, 880.4, 394.0, 872.0, 396.6, 870.7, 395.0, 861.0,
+392.2, 844.6, 396.7, 830.6, 389.3, 828.2, 417.3, 837.2, 412.8, 851.2,
+436.3, 874.3, 440.6, 878.5, 442.2, 883.6, 444.9, 882.7, 416.0, 171.3,
+416.1, 171.1, 416.3, 171.1, 416.3, 171.0, 417.8, 183.6, 417.8, 183.6,
+419.4, 196.3, 421.3, 210.6, 421.0, 210.6, 422.5, 225.0, 417.7, 260.8,
+417.2, 261.1, 417.5, 297.2, 417.2, 296.7, 417.2, 296.7, 416.8, 296.1,
+416.0, 242.1, 416.4, 242.1, 416.1, 188.1, 416.0, 179.7, 415.9, 179.7,
+416.0, 171.3, 346.4, 641.2, 353.1, 634.7, 353.5, 635.0, 359.8, 628.3,
+359.0, 628.9, 359.6, 629.7, 359.5, 631.2, 359.1, 633.6, 359.1, 633.6,
+358.8, 636.1, 358.4, 641.9, 355.7, 641.6, 355.0, 647.4, 355.3, 654.7,
+350.7, 654.7, 349.9, 662.3, 349.7, 664.6, 349.1, 664.5, 348.4, 666.8,
+346.5, 658.6, 347.7, 658.4, 347.1, 650.0, 346.8, 645.6, 344.6, 644.3,
+346.4, 641.2, 261.3, 688.3, 264.5, 698.4, 267.0, 702.9, 262.0, 710.4,
+244.4, 725.4, 243.2, 724.1, 226.8, 740.4, 233.3, 701.6, 239.0, 701.7,
+239.8, 662.8, 243.6, 657.0, 241.0, 655.3, 242.3, 647.9, 256.2, 664.4,
+254.6, 667.2, 261.3, 688.3, 242.3, 647.9, 247.6, 616.0, 247.6, 616.0,
+252.9, 584.1, 253.8, 609.7, 270.6, 620.2, 259.5, 635.1, 259.7, 636.7,
+259.6, 636.7, 259.6, 638.3, 260.0, 649.2, 260.0, 649.2, 260.4, 660.1,
+260.4, 662.7, 260.4, 662.7, 260.5, 665.3, 260.8, 672.8, 260.8, 672.8,
+261.0, 680.4, 261.2, 684.4, 259.5, 685.3, 261.3, 688.3, 254.6, 667.2,
+256.2, 664.4, 242.3, 647.9, 306.5, 803.3, 314.5, 784.8, 318.0, 785.6,
+322.6, 766.3, 330.7, 778.5, 327.5, 784.7, 338.9, 790.7, 338.4, 794.6,
+338.4, 794.6, 337.9, 798.5, 323.4, 804.1, 308.4, 807.4, 306.5, 803.3,
+337.9, 798.5, 337.2, 803.9, 337.2, 803.9, 336.5, 809.4, 321.1, 829.8,
+319.0, 828.2, 301.5, 847.1, 290.0, 859.4, 292.5, 867.4, 278.5, 871.8,
+284.4, 870.0, 281.3, 861.8, 285.2, 852.2, 291.3, 826.7, 295.8, 827.8,
+306.5, 803.3, 308.4, 807.4, 323.4, 804.1, 337.9, 798.5, 301.5, 847.1,
+319.0, 828.2, 321.1, 829.8, 336.5, 809.4, 333.6, 832.1, 337.9, 833.6,
+330.6, 854.9, 330.0, 857.6, 330.6, 857.7, 329.9, 860.4, 329.5, 862.2,
+329.5, 862.2, 329.0, 864.1, 314.4, 857.3, 300.6, 853.6, 301.5, 847.1,
+329.0, 864.1, 326.7, 873.5, 320.9, 879.5, 324.4, 882.9, 302.3, 890.3,
+299.7, 883.1, 275.0, 882.2, 275.2, 882.2, 275.2, 881.7, 275.3, 881.2,
+276.9, 876.5, 275.7, 875.5, 278.5, 871.8, 292.5, 867.4, 290.0, 859.4,
+301.5, 847.1, 300.6, 853.6, 314.4, 857.3, 329.0, 864.1, 398.2, 578.9,
+397.3, 582.8, 397.2, 582.8, 396.3, 586.8, 394.8, 593.0, 394.8, 593.0,
+393.3, 599.3, 389.1, 616.4, 395.5, 622.4, 385.0, 633.5, 378.5, 627.5,
+373.8, 632.5, 362.7, 631.5, 369.7, 602.6, 379.2, 604.2, 398.2, 578.9,
+362.7, 631.5, 361.1, 631.4, 359.8, 632.0, 359.5, 631.2, 359.5, 631.2,
+359.5, 631.2, 359.5, 631.2, 359.6, 629.7, 359.0, 628.9, 359.8, 628.3,
+360.2, 627.9, 359.6, 627.2, 360.0, 626.8, 360.5, 623.2, 360.5, 623.2,
+361.0, 619.6, 368.6, 590.3, 364.9, 589.4, 368.8, 559.1, 370.2, 560.5,
+373.0, 558.1, 377.0, 556.3, 387.8, 566.8, 394.5, 579.5, 398.6, 577.3,
+398.4, 578.1, 398.4, 578.1, 398.2, 578.9, 379.2, 604.2, 369.7, 602.6,
+362.7, 631.5, 374.9, 477.0, 387.8, 504.4, 387.8, 504.4, 400.7, 531.7,
+394.3, 526.7, 393.3, 527.9, 386.0, 524.0, 351.5, 518.3, 353.7, 505.1,
+321.3, 486.1, 322.1, 486.6, 322.6, 485.9, 323.8, 485.7, 332.3, 484.2,
+332.3, 484.2, 340.7, 482.8, 347.2, 481.7, 347.2, 481.7, 353.6, 480.6,
+364.3, 478.8, 371.0, 472.7, 374.9, 477.0, 430.0, 397.2, 420.7, 389.6,
+410.7, 388.7, 411.3, 382.1, 406.3, 376.3, 404.8, 377.1, 401.3, 370.6,
+396.6, 363.6, 396.6, 363.6, 392.0, 356.6, 406.4, 368.4, 404.9, 370.3,
+417.7, 384.1, 423.9, 390.6, 422.5, 394.9, 430.0, 397.2, 312.7, 349.7,
+319.1, 335.8, 319.1, 335.8, 325.6, 322.0, 330.1, 331.7, 331.6, 331.0,
+337.7, 340.1, 333.6, 348.9, 314.3, 352.2, 312.7, 349.7, 426.6, 461.9,
+423.9, 473.2, 422.3, 473.1, 421.1, 484.6, 421.1, 486.1, 420.7, 486.1,
+420.4, 487.6, 419.3, 484.4, 417.0, 485.2, 413.7, 482.7, 388.8, 467.8,
+390.8, 464.5, 367.9, 446.3, 367.7, 447.2, 393.4, 453.0, 418.9, 459.8,
+422.7, 460.9, 426.5, 460.5, 426.6, 461.9, 236.0, 594.4, 228.0, 614.6,
+225.6, 614.0, 220.0, 634.9, 220.3, 631.4, 219.5, 628.1, 220.5, 627.8,
+225.4, 586.6, 223.5, 586.4, 226.5, 544.9, 234.1, 539.0, 230.6, 534.6,
+234.6, 524.2, 241.1, 558.2, 240.5, 560.0, 236.0, 594.4, 77.2, 274.3,
+70.8, 259.9, 70.8, 259.9, 64.3, 245.5, 66.4, 226.4, 65.1, 226.2,
+65.9, 206.9, 64.9, 213.5, 67.1, 213.8, 68.2, 220.7, 71.9, 242.5,
+71.9, 242.5, 75.5, 264.2, 76.4, 269.2, 74.8, 270.2, 77.2, 274.3,
+689.5, 513.1, 695.2, 510.7, 698.8, 512.4, 700.8, 508.2, 700.3, 515.6,
+700.3, 515.6, 699.8, 523.1, 697.3, 524.2, 689.2, 517.7, 689.5, 513.1,
+699.8, 523.1, 698.6, 542.0, 694.3, 542.7, 697.3, 560.8, 695.3, 564.7,
+692.5, 565.5, 693.3, 568.6, 689.4, 569.0, 689.4, 569.0, 685.5, 569.3,
+674.9, 557.2, 672.6, 546.6, 661.6, 547.5, 659.2, 544.7, 658.6, 545.0,
+656.7, 541.9, 653.1, 536.2, 653.0, 536.3, 649.4, 530.7, 670.2, 528.0,
+669.4, 521.9, 689.5, 513.1, 689.2, 517.7, 697.3, 524.2, 699.8, 523.1,
+640.6, 486.8, 641.1, 482.9, 641.1, 482.9, 641.5, 479.0, 665.1, 466.5,
+665.8, 463.1, 691.8, 459.8, 696.3, 465.5, 696.3, 465.5, 700.8, 471.3,
+700.6, 474.2, 701.0, 474.9, 702.9, 477.3, 702.8, 478.7, 702.8, 478.7,
+702.7, 480.0, 693.9, 483.3, 687.5, 485.4, 687.0, 491.8, 677.4, 495.4,
+677.4, 495.4, 667.8, 498.9, 653.7, 497.4, 652.1, 495.6, 640.6, 486.8,
+667.8, 498.9, 652.8, 504.4, 652.4, 503.5, 637.8, 510.0, 638.1, 507.7,
+638.4, 507.7, 638.3, 505.4, 635.1, 498.0, 639.5, 496.1, 640.6, 486.8,
+652.1, 495.6, 653.7, 497.4, 667.8, 498.9, 784.9, 126.3, 772.6, 107.4,
+777.7, 99.6, 760.4, 88.4, 767.7, 87.7, 771.2, 83.6, 775.0, 86.9,
+813.2, 104.8, 813.2, 104.8, 851.4, 122.6, 849.9, 116.6, 813.4, 137.1,
+784.9, 126.3, 851.4, 122.6, 854.5, 124.1, 854.9, 123.5, 857.5, 125.5,
+863.1, 129.5, 863.2, 129.3, 868.7, 133.4, 872.0, 135.8, 872.0, 135.8,
+875.3, 138.2, 868.0, 135.7, 866.1, 141.0, 857.0, 143.8, 841.8, 132.4,
+830.0, 140.7, 806.2, 148.0, 800.0, 143.3, 800.7, 141.9, 793.9, 138.7,
+792.8, 138.7, 792.9, 137.9, 791.8, 137.2, 786.9, 134.1, 788.4, 131.7,
+784.9, 126.3, 813.4, 137.1, 849.9, 116.6, 851.4, 122.6, 921.4, 271.7,
+918.0, 273.4, 914.5, 273.9, 914.7, 275.0, 914.9, 275.8, 907.1, 277.0,
+907.2, 278.6, 907.3, 278.8, 905.1, 279.6, 903.1, 280.6, 902.5, 281.0,
+901.6, 279.7, 900.1, 278.9, 910.2, 274.1, 910.6, 274.7, 921.4, 271.7,
+773.0, 328.8, 748.5, 323.3, 748.1, 316.3, 724.0, 317.9, 720.6, 314.2,
+720.6, 314.2, 717.1, 310.4, 741.1, 315.2, 740.5, 318.1, 763.9, 325.8,
+768.5, 327.3, 768.4, 327.9, 773.0, 328.8, 787.0, 248.0, 792.7, 251.7,
+795.0, 256.8, 798.5, 255.3, 796.8, 263.4, 794.1, 271.2, 795.1, 271.6,
+793.4, 271.7, 791.4, 277.9, 792.6, 283.1, 788.8, 283.1, 789.0, 300.4,
+785.4, 317.7, 785.5, 317.1, 784.0, 316.8, 784.0, 315.9, 783.8, 302.9,
+784.5, 302.9, 785.1, 289.9, 786.1, 269.0, 779.8, 266.6, 787.0, 248.0,
+673.5, 315.8, 659.5, 298.6, 659.5, 298.6, 645.4, 281.4, 632.5, 255.7,
+631.1, 256.4, 615.8, 231.9, 605.0, 210.3, 611.0, 203.2, 594.1, 188.7,
+592.9, 180.0, 592.1, 178.0, 585.7, 172.0, 581.2, 168.9, 582.9, 166.5,
+580.1, 161.0, 591.5, 174.3, 591.9, 173.9, 603.8, 186.8, 616.0, 204.4,
+614.0, 205.7, 624.2, 224.7, 638.5, 251.0, 638.5, 251.0, 652.8, 277.4,
+663.2, 296.6, 673.6, 315.7, 673.5, 315.8, 478.7, 141.4, 501.3, 162.4,
+497.6, 169.5, 523.9, 183.4, 524.4, 183.1, 525.3, 184.7, 526.7, 186.0,
+533.2, 191.2, 531.9, 192.8, 537.0, 199.6, 526.3, 193.5, 528.0, 190.6,
+519.0, 181.6, 498.8, 161.5, 503.0, 150.3, 478.7, 141.4, 620.1, -35.4,
+624.7, -11.6, 619.7, -9.4, 629.3, 12.3, 624.8, 14.0, 624.8, 14.0,
+620.2, 15.7, 617.8, 13.9, 620.2, 10.6, 620.2, 5.5, 620.1, -14.9,
+629.9, -28.1, 620.1, -35.4, 250.6, 110.4, 253.5, 96.5, 253.0, 96.4,
+256.4, 82.5, 257.2, 79.3, 257.0, 79.3, 257.9, 76.1, 260.2, 67.8,
+260.2, 67.8, 262.6, 59.6, 263.8, 66.2, 269.4, 65.1, 276.3, 70.7,
+270.1, 100.3, 255.6, 120.9, 267.7, 130.7, 265.7, 140.4, 265.7, 140.4,
+263.7, 150.1, 251.1, 135.4, 252.7, 130.8, 250.6, 110.4, 263.7, 150.1,
+261.1, 162.6, 257.9, 162.7, 258.5, 175.0, 249.7, 165.6, 249.7, 165.6,
+240.9, 156.3, 246.6, 133.6, 245.7, 133.4, 250.6, 110.4, 252.7, 130.8,
+251.1, 135.4, 263.7, 150.1, 326.0, 83.2, 339.5, 82.3, 343.1, 87.8,
+353.0, 81.4, 353.5, 89.0, 353.5, 89.0, 353.9, 96.6, 351.1, 135.1,
+352.1, 135.2, 350.3, 173.8, 322.8, 160.6, 324.7, 128.1, 326.0, 83.2,
+350.3, 173.8, 350.0, 180.1, 350.0, 180.1, 349.7, 186.4, 337.7, 154.2,
+337.7, 154.1, 324.3, 122.5, 319.7, 110.0, 323.2, 100.7, 315.0, 97.6,
+319.0, 90.5, 325.0, 87.7, 323.0, 83.4, 324.5, 83.3, 324.5, 83.3,
+326.0, 83.2, 324.7, 128.1, 322.8, 160.6, 350.3, 173.8, 172.0, 295.9,
+179.3, 293.3, 179.3, 293.3, 186.7, 290.7, 183.3, 295.7, 186.1, 297.6,
+185.4, 304.5, 180.8, 306.1, 171.6, 299.5, 172.0, 295.9, 602.3, 667.7,
+604.0, 664.1, 590.6, 659.9, 591.4, 653.9, 601.3, 634.3, 600.4, 633.9,
+609.4, 614.0, 609.2, 614.5, 609.3, 614.5, 609.3, 615.1, 609.3, 615.2,
+609.3, 615.2, 609.2, 615.2, 605.8, 641.4, 612.9, 644.7, 602.3, 667.7,
+662.7, 698.1, 662.6, 699.1, 661.9, 699.0, 661.0, 700.0, 658.8, 702.5,
+658.8, 702.5, 656.6, 705.1, 654.4, 707.5, 654.4, 707.5, 652.3, 710.0,
+647.9, 714.9, 648.2, 719.6, 643.6, 719.9, 640.3, 720.1, 637.3, 715.8,
+636.4, 710.8, 632.1, 687.1, 631.2, 686.3, 633.1, 662.4, 632.8, 666.0,
+636.4, 666.3, 639.6, 670.2, 647.5, 679.7, 647.5, 679.7, 655.4, 689.2,
+658.3, 692.8, 658.3, 692.8, 661.2, 696.3, 661.9, 697.2, 662.7, 697.2,
+662.7, 698.1, 555.2, 608.1, 553.8, 606.4, 552.6, 604.5, 552.4, 604.6,
+553.2, 598.7, 541.1, 592.1, 541.9, 591.3, 542.6, 585.6, 542.6, 585.6,
+543.2, 580.0, 552.4, 591.6, 549.9, 593.8, 555.2, 608.1, 561.8, 494.2,
+562.8, 490.6, 562.8, 490.6, 563.8, 487.0, 564.1, 489.9, 568.0, 489.5,
+572.1, 491.9, 572.1, 492.7, 568.0, 492.8, 564.0, 493.7, 562.9, 493.9,
+561.8, 493.9, 561.8, 494.2, 544.4, 720.7, 544.4, 720.7, 544.4, 720.7,
+544.4, 720.7, 549.1, 728.1, 549.1, 728.1, 553.7, 735.5, 558.5, 743.3,
+558.5, 743.3, 563.4, 751.1, 568.5, 759.4, 574.8, 760.3, 573.7, 767.7,
+565.6, 772.1, 566.5, 773.6, 559.2, 779.4, 548.5, 769.2, 554.9, 762.5,
+550.7, 745.6, 547.5, 733.2, 547.5, 733.2, 544.4, 720.7, 589.1, 754.2,
+588.6, 757.8, 586.3, 757.5, 583.5, 760.9, 579.5, 765.7, 580.5, 767.1,
+575.5, 770.6, 575.8, 769.6, 573.8, 767.6, 573.7, 767.7, 574.8, 760.3,
+568.5, 759.4, 563.4, 751.1, 563.1, 714.0, 567.5, 713.8, 573.1, 676.7,
+568.2, 671.3, 575.8, 664.4, 578.4, 652.1, 575.3, 653.6, 579.5, 662.6,
+580.6, 673.2, 583.7, 702.7, 583.7, 702.7, 586.8, 732.3, 587.9, 743.2,
+590.3, 743.5, 589.1, 754.2, 531.4, 675.9, 533.8, 656.3, 533.8, 656.3,
+536.3, 636.7, 551.3, 641.0, 563.9, 639.2, 571.1, 628.3, 572.8, 628.1,
+573.1, 630.7, 575.0, 633.2, 575.6, 633.9, 575.6, 633.9, 576.1, 634.6,
+576.0, 635.8, 577.2, 636.0, 578.3, 637.3, 575.7, 642.6, 577.5, 643.4,
+576.7, 649.6, 571.0, 656.4, 568.5, 654.3, 560.3, 659.1, 545.9, 667.5,
+535.0, 679.2, 531.4, 675.9, 630.3, 847.0, 629.2, 849.3, 629.8, 849.8,
+628.1, 851.5, 623.9, 855.9, 624.5, 856.6, 619.8, 860.2, 609.4, 869.5,
+610.7, 874.4, 598.4, 878.2, 579.8, 884.0, 578.1, 879.9, 557.9, 879.4,
+557.7, 879.4, 557.6, 878.3, 557.4, 877.2, 591.6, 861.8, 592.0, 862.9,
+626.7, 848.5, 628.5, 847.7, 630.3, 846.9, 630.3, 847.0, 538.1, 445.8,
+538.5, 445.4, 538.5, 445.1, 539.0, 445.1, 540.4, 444.8, 541.1, 445.4,
+541.9, 444.6, 543.5, 442.4, 545.6, 443.9, 549.3, 443.3, 549.3, 443.3,
+549.3, 443.4, 549.3, 443.5, 557.9, 450.1, 548.7, 462.0, 548.1, 480.6,
+548.3, 478.7, 547.6, 478.7, 547.0, 476.8, 544.5, 467.9, 544.5, 467.9,
+541.9, 459.0, 540.7, 454.7, 540.7, 454.7, 539.5, 450.4, 538.8, 448.1,
+538.4, 448.1, 538.1, 445.8, 499.7, 607.6, 492.9, 624.4, 493.4, 624.6,
+486.2, 641.2, 479.6, 657.2, 478.0, 656.7, 472.9, 673.2, 459.6, 660.6,
+460.3, 644.6, 468.4, 624.5, 473.6, 611.8, 495.6, 603.8, 499.7, 607.6,
+468.4, 624.5, 468.3, 623.1, 468.3, 623.1, 468.2, 621.6, 468.1, 621.0,
+468.2, 621.0, 468.3, 620.3, 466.7, 616.9, 475.7, 614.6, 476.1, 608.4,
+497.1, 588.7, 503.3, 585.9, 508.7, 559.0, 509.0, 559.2, 512.5, 553.3,
+516.3, 547.5, 515.7, 548.6, 516.4, 549.0, 516.5, 550.4, 518.6, 551.0,
+516.7, 557.5, 517.0, 564.6, 508.0, 586.0, 508.3, 586.1, 499.7, 607.6,
+495.6, 603.8, 473.6, 611.8, 468.4, 624.5, 446.4, 372.4, 445.7, 370.9,
+445.1, 370.9, 445.0, 369.4, 444.8, 366.7, 444.8, 366.7, 444.5, 364.1,
+444.1, 362.0, 444.8, 361.9, 445.1, 359.6, 444.3, 355.2, 446.4, 354.8,
+447.8, 349.9, 448.9, 361.0, 448.0, 361.3, 446.4, 372.4, 191.5, 616.5,
+188.4, 624.7, 188.4, 624.7, 185.3, 632.9, 179.2, 628.3, 179.6, 619.8,
+183.2, 610.3, 182.7, 611.6, 191.2, 613.0, 191.5, 616.5, 183.2, 610.3,
+174.4, 552.3, 178.0, 551.8, 172.8, 493.4, 172.9, 493.2, 172.9, 493.2,
+172.9, 493.1, 178.5, 505.2, 179.3, 504.9, 185.8, 516.6, 190.9, 543.6,
+189.3, 545.5, 202.3, 569.3, 197.7, 591.0, 197.7, 591.0, 193.0, 612.6,
+192.7, 614.6, 192.3, 614.5, 191.5, 616.5, 191.2, 613.0, 182.7, 611.6,
+183.2, 610.3, 168.3, 506.7, 170.6, 500.0, 170.6, 500.0, 172.8, 493.4,
+178.0, 551.8, 174.4, 552.3, 183.2, 610.3, 179.6, 619.8, 179.2, 628.3,
+185.3, 632.9, 183.7, 636.8, 182.7, 636.7, 182.2, 640.8, 174.0, 627.6,
+163.2, 624.8, 165.9, 614.5, 165.1, 609.2, 165.1, 609.2, 164.4, 603.8,
+165.5, 555.2, 164.6, 555.1, 168.3, 506.7, 164.4, 603.8, 161.6, 583.3,
+161.6, 583.3, 158.8, 562.8, 159.4, 553.5, 160.5, 553.3, 159.2, 544.2,
+161.2, 532.8, 165.9, 522.6, 163.3, 521.4, 165.8, 514.0, 165.8, 514.0,
+168.3, 506.7, 164.6, 555.1, 165.5, 555.2, 164.4, 603.8, 403.1, 653.0,
+392.2, 650.9, 391.3, 646.7, 381.3, 648.8, 383.1, 641.1, 387.8, 636.1,
+385.0, 633.5, 395.5, 622.4, 389.1, 616.4, 393.3, 599.3, 408.9, 616.2,
+408.9, 628.7, 403.1, 653.0, 406.4, 715.8, 406.4, 715.7, 406.4, 715.7,
+406.5, 715.6, 416.6, 688.1, 421.7, 688.2, 424.1, 659.7, 430.8, 639.9,
+431.3, 640.1, 438.6, 620.5, 441.4, 612.6, 441.4, 612.6, 444.3, 604.8,
+443.2, 597.4, 447.3, 596.8, 450.2, 588.8, 440.7, 632.2, 441.2, 632.3,
+432.1, 675.9, 424.6, 697.5, 419.2, 695.7, 406.5, 715.6, 406.4, 715.7,
+406.4, 715.7, 406.4, 715.8, 432.1, 675.9, 428.8, 691.6, 429.7, 691.9,
+425.5, 707.4, 421.8, 725.3, 421.8, 725.3, 418.1, 743.1, 416.7, 749.7,
+416.7, 749.7, 415.4, 756.3, 414.9, 758.5, 413.6, 760.2, 414.4, 760.8,
+409.1, 754.3, 409.1, 754.3, 403.8, 747.8, 407.7, 747.1, 412.9, 726.6,
+406.2, 718.4, 406.0, 718.3, 406.3, 717.1, 406.4, 715.8, 418.8, 695.5,
+414.0, 683.8, 432.1, 675.9, 458.7, 682.6, 460.2, 708.3, 460.1, 708.3,
+461.7, 734.0, 463.1, 739.0, 458.0, 740.4, 454.2, 746.8, 450.4, 731.2,
+455.4, 730.0, 456.6, 713.1, 457.6, 697.9, 462.2, 696.8, 458.7, 682.6,
+439.7, 554.0, 441.7, 549.1, 443.5, 549.1, 443.6, 544.2, 444.0, 574.5,
+439.9, 575.1, 444.3, 604.8, 441.4, 612.6, 441.4, 612.6, 438.6, 620.5,
+435.6, 587.6, 437.6, 587.1, 439.7, 554.0, 438.6, 620.5, 431.3, 640.1,
+430.8, 639.9, 424.1, 659.7, 422.2, 683.1, 414.6, 682.2, 407.3, 705.4,
+407.5, 703.4, 407.5, 703.4, 407.6, 701.4, 413.1, 685.3, 410.4, 684.4,
+412.4, 667.3, 417.8, 633.8, 417.1, 633.6, 421.8, 600.0, 422.4, 576.8,
+430.8, 577.0, 439.7, 554.0, 437.6, 587.1, 435.6, 587.6, 438.6, 620.5,
+392.3, 792.8, 390.7, 784.7, 386.5, 785.5, 380.8, 778.2, 371.1, 765.9,
+366.9, 767.5, 361.4, 753.6, 362.4, 743.8, 362.4, 743.8, 363.4, 734.1,
+364.5, 737.8, 368.3, 736.6, 373.2, 739.2, 385.7, 764.5, 386.9, 765.2,
+392.3, 792.8, 395.0, 861.0, 396.6, 870.7, 394.0, 872.0, 398.3, 880.4,
+382.6, 873.9, 382.6, 873.9, 366.8, 867.4, 369.6, 865.9, 366.3, 859.8,
+365.7, 852.2, 361.5, 819.0, 363.7, 818.8, 361.6, 785.4, 387.3, 793.1,
+380.1, 822.5, 395.0, 861.0, 361.6, 785.4, 361.0, 774.7, 361.6, 774.6,
+360.3, 764.1, 360.9, 758.8, 360.9, 758.8, 361.4, 753.6, 366.9, 767.5,
+371.1, 765.9, 380.8, 778.2, 389.5, 801.5, 385.1, 803.2, 389.3, 828.2,
+396.7, 830.6, 392.2, 844.6, 395.0, 861.0, 380.1, 822.5, 387.3, 793.1,
+361.6, 785.4, 436.3, 874.3, 412.8, 851.2, 417.3, 837.2, 389.3, 828.2,
+385.1, 803.2, 389.5, 801.5, 380.8, 778.2, 386.5, 785.5, 390.7, 784.7,
+392.3, 792.8, 393.5, 794.3, 393.5, 794.3, 394.7, 795.9, 407.7, 812.4,
+407.7, 812.4, 420.7, 828.9, 428.5, 829.0, 428.4, 838.7, 436.1, 848.5,
+440.1, 860.0, 431.5, 872.2, 436.3, 874.3, 436.1, 848.5, 444.4, 858.9,
+442.1, 862.3, 452.6, 869.4, 451.9, 874.1, 452.4, 874.2, 451.3, 878.7,
+450.8, 880.6, 450.5, 882.6, 450.3, 882.5, 448.4, 883.8, 446.9, 883.9,
+444.9, 882.7, 442.2, 883.6, 440.6, 878.5, 436.3, 874.3, 431.5, 872.2,
+440.1, 860.0, 436.1, 848.5, 383.8, 232.7, 386.1, 216.4, 392.4, 214.6,
+388.3, 200.2, 389.4, 199.1, 389.4, 199.1, 390.4, 198.0, 388.0, 215.4,
+394.0, 221.9, 383.8, 232.7, 284.3, 466.1, 288.2, 469.9, 287.7, 470.6,
+292.1, 473.7, 296.3, 475.1, 295.0, 478.2, 299.2, 480.6, 316.5, 484.7,
+314.1, 495.1, 329.0, 509.6, 330.1, 511.2, 327.3, 513.3, 325.6, 516.9,
+320.7, 527.7, 323.9, 534.9, 315.8, 538.5, 315.8, 538.5, 312.6, 531.3,
+309.5, 524.1, 301.1, 504.9, 301.1, 504.9, 292.8, 485.6, 288.5, 475.9,
+284.9, 476.1, 284.3, 466.1, 358.5, 448.9, 362.0, 453.0, 362.0, 453.0,
+365.6, 457.1, 373.1, 463.9, 370.3, 467.0, 374.9, 477.0, 371.0, 472.7,
+364.3, 478.8, 353.6, 480.6, 349.6, 477.1, 355.1, 470.8, 356.6, 461.0,
+357.2, 457.1, 357.2, 457.1, 357.8, 453.2, 358.1, 451.1, 359.1, 449.2,
+358.5, 448.9, 417.7, 384.1, 404.9, 370.3, 406.4, 368.4, 392.0, 356.6,
+390.1, 353.7, 388.6, 350.6, 388.2, 350.8, 389.5, 348.0, 386.2, 343.5,
+381.6, 341.0, 375.3, 339.9, 376.4, 333.1, 371.2, 325.2, 375.1, 328.5,
+377.6, 325.6, 384.0, 325.9, 401.6, 337.3, 403.5, 340.5, 423.8, 341.6,
+424.2, 342.3, 424.2, 342.3, 424.7, 343.1, 421.8, 363.6, 427.9, 379.8,
+417.7, 384.1, 424.7, 343.1, 432.4, 355.7, 432.0, 356.0, 440.0, 368.4,
+440.9, 383.7, 440.9, 383.7, 441.8, 399.1, 442.4, 401.0, 441.3, 401.3,
+440.8, 403.5, 440.7, 403.7, 439.2, 403.4, 437.6, 403.3, 437.8, 400.6,
+433.8, 400.3, 430.0, 397.2, 422.5, 394.9, 423.9, 390.6, 417.7, 384.1,
+427.9, 379.8, 421.8, 363.6, 424.7, 343.1, 355.4, 366.8, 358.1, 370.8,
+357.9, 374.7, 360.7, 374.8, 352.1, 382.3, 352.1, 382.3, 343.5, 389.8,
+340.0, 384.6, 346.3, 374.7, 355.4, 366.8, 418.9, 459.8, 393.4, 453.0,
+367.7, 447.2, 367.9, 446.3, 366.7, 445.3, 366.7, 445.3, 365.4, 444.3,
+365.4, 444.7, 366.6, 444.6, 367.8, 445.0, 393.3, 452.4, 417.7, 449.6,
+418.9, 459.8, 262.9, 486.8, 261.9, 491.5, 261.0, 496.3, 261.0, 496.3,
+259.6, 503.2, 259.6, 503.2, 258.1, 510.1, 259.7, 503.9, 259.4, 503.9,
+260.7, 497.6, 261.8, 492.2, 261.5, 492.1, 262.9, 486.8, 118.7, 318.3,
+119.2, 321.0, 119.2, 321.0, 119.8, 323.7, 122.1, 325.8, 107.3, 335.3,
+108.8, 344.5, 106.6, 339.6, 106.6, 339.6, 104.4, 334.7, 106.1, 328.2,
+109.4, 329.0, 114.3, 323.3, 116.5, 320.8, 116.7, 318.2, 118.7, 318.3,
+925.0, 270.0, 923.2, 270.9, 923.2, 270.9, 921.4, 271.7, 910.6, 274.7,
+910.2, 274.1, 900.1, 278.9, 892.2, 274.4, 892.2, 274.4, 884.2, 269.9,
+853.6, 232.9, 850.9, 235.1, 817.5, 200.3, 861.1, 193.4, 867.2, 232.5,
+916.9, 264.8, 921.0, 267.4, 922.6, 266.3, 925.0, 270.0, 704.8, 239.4,
+704.8, 239.2, 714.5, 236.3, 722.0, 239.1, 727.1, 241.1, 726.0, 244.0,
+729.9, 248.9, 747.4, 270.5, 766.2, 271.9, 764.8, 292.2, 765.0, 289.6,
+744.5, 291.9, 727.4, 284.4, 723.0, 282.4, 724.6, 278.8, 721.8, 273.1,
+713.3, 256.2, 704.7, 256.2, 704.8, 239.4, 666.3, 239.8, 663.2, 236.4,
+657.8, 242.9, 652.5, 240.0, 645.6, 232.5, 645.6, 232.5, 638.7, 224.9,
+652.4, 224.8, 656.3, 228.9, 666.3, 239.8, 773.6, 303.1, 775.2, 302.3,
+778.8, 309.5, 784.0, 315.9, 784.0, 316.8, 785.5, 317.1, 785.4, 317.7,
+785.1, 318.9, 784.8, 320.1, 784.9, 320.1, 781.7, 321.8, 783.7, 325.5,
+782.6, 330.9, 778.5, 328.6, 777.8, 329.9, 773.0, 328.8, 768.4, 327.9,
+768.5, 327.3, 763.9, 325.8, 764.2, 314.4, 765.2, 307.2, 773.6, 303.1,
+697.2, 208.4, 686.5, 202.6, 682.2, 199.0, 680.7, 187.9, 687.8, 190.0,
+690.8, 194.8, 695.0, 192.2, 697.4, 194.4, 698.4, 193.2, 701.9, 194.3,
+726.8, 215.2, 729.6, 211.8, 757.4, 229.3, 757.5, 228.9, 724.9, 223.3,
+697.2, 208.4, 624.2, 224.7, 614.0, 205.7, 616.0, 204.4, 603.8, 186.8,
+608.6, 192.1, 608.6, 192.1, 613.4, 197.3, 620.3, 210.2, 614.9, 214.5,
+624.2, 224.7, 484.2, 186.0, 456.3, 171.2, 457.3, 169.0, 428.4, 156.5,
+427.8, 156.2, 427.8, 156.2, 427.2, 155.8, 455.7, 170.8, 457.2, 168.6,
+484.2, 186.0, 620.2, 5.5, 620.2, 10.6, 617.8, 13.9, 620.2, 15.7,
+610.8, 19.2, 610.8, 19.2, 601.4, 22.8, 598.7, 18.3, 595.3, 19.3,
+588.8, 18.1, 583.6, 9.6, 583.6, 9.6, 578.5, 1.0, 584.0, -3.9,
+578.1, -10.7, 577.7, -22.4, 577.6, -21.7, 578.8, -21.6, 580.0, -20.8,
+600.1, -7.7, 602.6, -10.4, 620.2, 5.5, 151.7, 366.1, 155.5, 333.2,
+157.2, 333.4, 159.2, 300.4, 164.9, 298.4, 164.9, 298.4, 170.5, 296.4,
+166.4, 331.9, 170.9, 359.3, 151.7, 366.1, 170.5, 296.4, 171.2, 296.2,
+171.2, 296.2, 172.0, 295.9, 171.6, 299.5, 180.8, 306.1, 185.4, 304.5,
+183.7, 322.7, 183.7, 322.7, 182.0, 340.9, 165.8, 368.5, 158.1, 366.7,
+150.4, 396.6, 150.3, 396.6, 150.3, 396.6, 150.3, 396.7, 149.9, 392.5,
+149.9, 392.5, 149.5, 388.3, 149.1, 384.6, 149.8, 384.5, 150.0, 380.7,
+150.3, 373.4, 150.8, 373.4, 151.7, 366.1, 170.9, 359.3, 166.4, 331.9,
+170.5, 296.4, 618.3, 687.8, 619.4, 679.0, 603.6, 678.6, 602.3, 667.7,
+612.9, 644.7, 605.8, 641.4, 609.2, 615.2, 628.7, 639.7, 622.9, 652.7,
+618.3, 687.8, 705.5, 618.5, 711.2, 627.4, 711.2, 627.4, 716.9, 636.3,
+710.2, 655.5, 702.4, 652.8, 687.9, 669.3, 675.3, 683.7, 676.1, 684.5,
+662.7, 698.1, 662.7, 697.2, 661.9, 697.2, 661.2, 696.3, 664.7, 687.9,
+665.7, 688.3, 670.2, 680.4, 674.1, 673.6, 674.1, 673.6, 678.0, 666.8,
+691.7, 642.6, 685.3, 634.3, 705.5, 618.5, 743.5, 725.6, 741.4, 730.8,
+743.2, 733.3, 739.3, 736.1, 735.2, 740.4, 735.2, 740.4, 731.1, 744.7,
+721.4, 738.3, 724.7, 733.4, 718.2, 722.2, 717.3, 720.6, 717.3, 720.6,
+716.3, 718.9, 710.1, 708.1, 710.1, 708.1, 703.9, 697.2, 703.3, 696.1,
+703.6, 695.6, 702.6, 694.9, 712.2, 701.4, 711.9, 701.9, 721.2, 708.8,
+730.6, 715.9, 730.6, 715.9, 739.9, 722.9, 741.7, 724.2, 743.6, 724.3,
+743.5, 725.6, 685.5, 772.8, 679.5, 765.3, 672.3, 759.2, 673.6, 757.7,
+672.1, 756.0, 672.1, 756.0, 670.7, 754.2, 667.7, 750.4, 667.7, 750.4,
+664.7, 746.6, 664.1, 745.8, 664.1, 745.8, 663.5, 745.0, 653.5, 732.4,
+648.2, 734.2, 643.6, 719.9, 648.2, 719.6, 647.9, 714.9, 652.3, 710.0,
+670.8, 731.5, 665.9, 735.7, 679.5, 761.5, 682.5, 767.1, 680.8, 769.9,
+685.5, 772.8, 575.0, 633.2, 573.1, 630.7, 572.8, 628.1, 571.1, 628.3,
+573.3, 624.9, 563.2, 618.2, 555.2, 608.1, 549.9, 593.8, 552.4, 591.6,
+543.2, 580.0, 545.2, 564.5, 545.2, 564.5, 547.1, 549.0, 544.2, 551.1,
+550.9, 560.4, 554.6, 571.8, 564.8, 602.5, 566.0, 602.2, 575.0, 633.2,
+552.2, 531.6, 552.3, 531.5, 551.5, 531.2, 551.5, 531.1, 556.7, 512.6,
+556.7, 512.6, 561.8, 494.2, 561.8, 493.9, 562.9, 493.9, 564.0, 493.7,
+563.0, 500.5, 561.9, 500.3, 559.9, 507.0, 556.0, 519.3, 556.4, 519.4,
+552.2, 531.6, 550.7, 745.6, 554.9, 762.5, 548.5, 769.2, 559.2, 779.4,
+552.8, 784.7, 553.7, 788.1, 546.4, 789.9, 533.1, 797.2, 533.1, 797.2,
+519.9, 804.5, 522.8, 773.9, 526.4, 761.1, 550.7, 745.6, 519.9, 804.5,
+517.5, 805.8, 515.6, 805.4, 515.2, 807.1, 519.5, 772.2, 519.5, 772.2,
+523.8, 737.3, 523.0, 735.9, 534.1, 729.0, 544.4, 720.7, 547.5, 733.2,
+547.5, 733.2, 550.7, 745.6, 526.4, 761.1, 522.8, 773.9, 519.9, 804.5,
+580.6, 673.2, 579.5, 662.6, 575.3, 653.6, 578.4, 652.1, 579.7, 646.3,
+581.0, 646.3, 580.9, 640.6, 585.5, 646.4, 590.7, 651.6, 590.1, 652.2,
+589.9, 652.7, 591.6, 653.5, 591.4, 653.9, 590.6, 659.9, 604.0, 664.1,
+602.3, 667.7, 603.6, 678.6, 619.4, 679.0, 618.3, 687.8, 618.4, 688.0,
+618.4, 688.2, 618.6, 688.3, 602.2, 681.9, 602.3, 681.8, 586.0, 675.3,
+583.3, 674.2, 581.2, 674.9, 580.6, 673.2, 531.6, 700.1, 530.3, 697.9,
+528.6, 697.6, 529.0, 695.8, 529.9, 688.2, 529.6, 688.2, 530.8, 680.6,
+531.2, 678.3, 531.1, 678.3, 531.4, 675.9, 535.0, 679.2, 545.9, 667.5,
+560.3, 659.1, 560.5, 679.5, 547.8, 681.1, 531.6, 700.1, 611.2, 757.1,
+629.3, 783.0, 624.2, 786.6, 637.1, 816.1, 639.1, 820.5, 640.9, 820.5,
+641.0, 824.9, 639.3, 828.4, 639.3, 828.4, 637.6, 831.9, 624.6, 834.2,
+616.6, 837.5, 610.6, 830.4, 603.2, 821.6, 610.7, 815.3, 610.8, 800.2,
+610.9, 794.7, 610.9, 794.7, 610.9, 789.2, 611.0, 784.0, 611.0, 784.0,
+611.0, 778.7, 611.1, 767.9, 616.2, 764.3, 611.2, 757.1, 670.7, 754.2,
+672.1, 756.0, 672.1, 756.0, 673.6, 757.7, 650.1, 785.2, 657.3, 791.3,
+641.0, 824.9, 640.9, 820.5, 639.1, 820.5, 637.1, 816.1, 641.6, 801.5,
+644.6, 802.4, 652.0, 788.7, 661.4, 771.4, 659.6, 770.2, 670.7, 754.2,
+626.7, 848.5, 592.0, 862.9, 591.6, 861.8, 557.4, 877.2, 556.7, 874.7,
+556.7, 874.7, 556.1, 872.2, 590.4, 857.9, 627.4, 851.2, 626.7, 848.5,
+547.0, 476.8, 547.6, 478.7, 548.3, 478.7, 548.1, 480.6, 548.0, 483.4,
+548.0, 483.4, 547.9, 486.2, 546.7, 481.7, 546.9, 481.5, 547.0, 476.8,
+492.2, 356.2, 480.9, 370.9, 480.9, 370.9, 469.6, 385.7, 467.7, 382.6,
+463.9, 385.0, 458.3, 384.2, 462.7, 374.2, 467.7, 376.4, 477.2, 368.6,
+484.7, 362.4, 488.7, 364.1, 492.2, 356.2, 396.9, 584.7, 408.3, 546.9,
+410.4, 547.4, 419.7, 509.1, 420.3, 514.1, 421.1, 514.2, 420.8, 519.2,
+420.7, 532.8, 420.4, 532.8, 420.6, 546.5, 419.9, 555.0, 419.9, 555.0,
+419.2, 563.4, 411.4, 576.1, 397.0, 574.0, 396.9, 584.7, 419.2, 563.4,
+415.4, 609.0, 415.4, 609.0, 411.6, 654.6, 411.0, 652.7, 407.3, 653.8,
+403.1, 653.0, 408.9, 628.7, 408.9, 616.2, 393.3, 599.3, 394.8, 593.0,
+394.8, 593.0, 396.3, 586.8, 396.5, 585.7, 396.6, 585.7, 396.9, 584.7,
+397.0, 574.0, 411.4, 576.1, 419.2, 563.4, 418.1, 743.1, 421.8, 725.3,
+421.8, 725.3, 425.5, 707.4, 433.4, 674.3, 434.0, 674.4, 442.2, 641.3,
+444.8, 630.6, 447.3, 619.8, 447.3, 619.8, 447.3, 619.8, 444.8, 630.6,
+442.2, 641.3, 435.7, 668.8, 435.7, 668.8, 429.2, 696.3, 423.6, 719.7,
+425.6, 720.3, 418.1, 743.1, 394.7, 795.9, 393.5, 794.3, 393.5, 794.3,
+392.3, 792.8, 386.9, 765.2, 385.7, 764.5, 373.2, 739.2, 388.1, 746.8,
+402.3, 755.6, 403.0, 754.5, 420.6, 782.9, 420.6, 782.9, 438.3, 811.2,
+438.9, 808.0, 416.1, 804.5, 394.7, 795.9, 438.3, 811.2, 438.3, 811.3,
+438.3, 811.3, 438.3, 811.3, 444.2, 830.0, 444.2, 830.0, 450.1, 848.6,
+437.5, 836.6, 435.5, 829.1, 420.7, 828.9, 407.7, 812.4, 407.7, 812.4,
+394.7, 795.9, 416.1, 804.5, 438.9, 808.0, 438.3, 811.2, 416.1, 188.1,
+416.4, 242.1, 416.0, 242.1, 416.8, 296.1, 407.0, 281.7, 407.0, 281.7,
+397.3, 267.3, 397.0, 227.8, 431.5, 210.9, 416.1, 188.1, 397.3, 267.3,
+390.0, 256.5, 379.9, 250.3, 382.7, 245.8, 382.5, 244.4, 382.5, 244.4,
+382.3, 243.1, 381.0, 238.5, 383.0, 237.9, 383.8, 232.7, 394.0, 221.9,
+388.0, 215.4, 390.4, 198.0, 401.1, 186.7, 400.3, 185.7, 411.9, 175.4,
+413.9, 173.3, 413.9, 173.3, 416.0, 171.3, 415.9, 179.7, 416.0, 179.7,
+416.1, 188.1, 431.5, 210.9, 397.0, 227.8, 397.3, 267.3, 275.3, 881.2,
+275.2, 881.7, 275.2, 882.2, 275.0, 882.2, 263.9, 882.6, 255.8, 887.0,
+252.7, 881.9, 240.2, 861.3, 244.8, 856.5, 243.7, 830.9, 244.0, 836.8,
+247.3, 836.7, 251.0, 842.4, 255.4, 849.5, 255.4, 849.5, 259.8, 856.5,
+264.9, 864.6, 264.9, 864.6, 270.1, 872.8, 272.7, 877.0, 273.1, 876.8,
+275.3, 881.2, 239.4, 806.4, 234.4, 814.0, 241.5, 818.6, 243.7, 830.9,
+244.8, 856.5, 240.2, 861.3, 252.7, 881.9, 246.6, 891.2, 211.6, 885.1,
+210.9, 881.3, 205.0, 847.4, 218.0, 839.2, 239.4, 806.4, 210.9, 881.3,
+210.8, 881.6, 209.1, 881.3, 209.1, 881.3, 212.1, 865.4, 209.0, 864.8,
+208.8, 848.4, 207.5, 838.4, 210.5, 838.1, 212.2, 827.7, 209.5, 826.6,
+221.9, 795.2, 231.7, 762.7, 223.7, 770.7, 235.1, 782.2, 238.5, 801.7,
+239.0, 804.0, 240.0, 804.4, 239.4, 806.4, 218.0, 839.2, 205.0, 847.4,
+210.9, 881.3, 359.2, 595.6, 360.1, 607.6, 363.8, 608.6, 361.0, 619.6,
+360.5, 623.2, 360.5, 623.2, 360.0, 626.8, 311.7, 667.4, 312.0, 667.8,
+264.0, 708.7, 275.7, 699.7, 273.7, 697.1, 283.5, 685.5, 292.3, 675.0,
+292.3, 675.0, 301.1, 664.5, 313.0, 650.4, 313.0, 650.4, 324.9, 636.3,
+342.0, 615.9, 337.3, 605.7, 359.2, 595.6, 355.4, 543.6, 356.1, 553.6,
+356.1, 553.6, 356.8, 563.5, 339.0, 591.0, 311.6, 612.4, 311.1, 611.9,
+307.1, 608.3, 329.4, 583.6, 347.8, 555.3, 351.6, 549.5, 356.2, 544.4,
+355.4, 543.6, 257.4, 569.9, 257.2, 564.7, 255.6, 564.2, 257.0, 559.6,
+259.6, 544.0, 258.2, 543.6, 262.2, 528.4, 267.1, 506.8, 262.2, 505.2,
+269.5, 484.6, 270.8, 480.4, 270.8, 480.4, 272.0, 476.1, 273.8, 480.1,
+271.2, 481.3, 270.4, 486.5, 263.9, 528.2, 265.0, 528.4, 257.4, 569.9,
+279.0, 460.9, 279.1, 460.5, 278.2, 460.2, 277.5, 459.5, 277.2, 459.3,
+277.0, 459.2, 277.0, 459.0, 283.9, 435.5, 283.9, 435.5, 290.7, 411.9,
+300.0, 420.9, 285.7, 436.7, 279.0, 460.9, 308.0, 359.7, 310.3, 354.7,
+310.3, 354.7, 312.7, 349.7, 314.3, 352.2, 333.6, 348.9, 337.7, 340.1,
+346.5, 353.4, 346.5, 353.4, 355.4, 366.8, 346.3, 374.7, 340.0, 384.6,
+343.5, 389.8, 341.5, 391.6, 341.5, 391.6, 339.5, 393.3, 322.1, 378.5,
+319.9, 378.9, 308.0, 359.7, 339.5, 393.3, 337.2, 395.3, 334.6, 396.8,
+334.9, 397.3, 323.6, 396.7, 316.9, 390.7, 312.2, 396.0, 305.9, 388.8,
+301.7, 380.5, 299.6, 381.6, 301.0, 376.6, 301.0, 376.6, 302.4, 371.7,
+303.9, 365.4, 305.2, 365.7, 308.0, 359.7, 319.9, 378.9, 322.1, 378.5,
+339.5, 393.3, 431.3, 441.1, 431.5, 441.2, 431.6, 441.2, 431.6, 441.3,
+431.6, 441.3, 431.6, 441.3, 431.6, 441.3, 431.5, 441.2, 431.3, 441.1,
+431.3, 441.1, 260.7, 497.6, 259.4, 503.9, 259.7, 503.9, 258.1, 510.1,
+256.8, 515.4, 255.8, 520.7, 255.8, 520.7, 249.4, 547.7, 250.8, 548.1,
+242.9, 574.7, 241.7, 578.5, 241.8, 578.5, 240.7, 582.4, 239.4, 588.6,
+238.4, 588.4, 236.0, 594.4, 240.5, 560.0, 241.1, 558.2, 234.6, 524.2,
+242.6, 503.9, 242.6, 503.9, 250.5, 483.7, 249.5, 484.7, 258.5, 489.8,
+260.7, 497.6, 250.5, 483.7, 251.2, 482.0, 251.0, 482.0, 251.8, 480.4,
+262.3, 459.0, 262.3, 459.0, 272.8, 437.6, 267.5, 462.1, 267.9, 462.2,
+262.9, 486.8, 261.5, 492.1, 261.8, 492.2, 260.7, 497.6, 258.5, 489.8,
+249.5, 484.7, 250.5, 483.7, 114.3, 323.3, 109.4, 329.0, 106.1, 328.2,
+104.4, 334.7, 90.8, 304.5, 90.8, 304.5, 77.2, 274.3, 74.8, 270.2,
+76.4, 269.2, 75.5, 264.2, 92.4, 285.4, 90.4, 286.9, 105.3, 309.6,
+109.8, 316.5, 114.5, 316.6, 114.3, 323.3, 721.8, 273.1, 724.6, 278.8,
+723.0, 282.4, 727.4, 284.4, 727.4, 284.5, 723.5, 283.6, 719.5, 282.8,
+717.0, 282.2, 717.0, 282.2, 714.4, 281.7, 699.8, 278.7, 698.9, 280.9,
+685.2, 275.6, 681.5, 271.6, 681.5, 271.6, 677.8, 267.5, 699.5, 266.0,
+701.2, 266.2, 721.8, 273.1, 677.8, 267.5, 665.2, 253.8, 665.2, 253.8,
+652.5, 240.0, 657.8, 242.9, 663.2, 236.4, 666.3, 239.8, 685.6, 239.6,
+690.8, 230.9, 704.8, 239.4, 704.7, 256.2, 713.3, 256.2, 721.8, 273.1,
+701.2, 266.2, 699.5, 266.0, 677.8, 267.5, 641.5, 196.5, 647.2, 188.0,
+652.8, 179.4, 653.0, 179.5, 666.8, 183.7, 666.8, 183.7, 680.7, 187.9,
+682.2, 199.0, 686.5, 202.6, 697.2, 208.4, 705.9, 219.2, 705.9, 219.2,
+714.6, 230.0, 715.2, 230.7, 715.9, 231.3, 715.8, 231.4, 715.7, 231.6,
+715.0, 231.0, 714.1, 230.6, 677.8, 213.5, 671.3, 221.4, 641.5, 196.5,
+686.3, 276.8, 685.8, 276.2, 685.8, 276.2, 685.2, 275.6, 698.9, 280.9,
+699.8, 278.7, 714.4, 281.7, 706.7, 273.4, 700.2, 279.7, 686.3, 276.8,
+714.6, 230.0, 705.9, 219.2, 705.9, 219.2, 697.2, 208.4, 724.9, 223.3,
+757.5, 228.9, 757.4, 229.3, 757.4, 229.3, 757.4, 229.3, 757.4, 229.3,
+741.4, 229.6, 741.4, 229.6, 725.3, 229.8, 720.0, 229.9, 718.0, 232.5,
+714.6, 230.0, 652.8, 277.4, 638.5, 251.0, 638.5, 251.0, 624.2, 224.7,
+614.9, 214.5, 620.3, 210.2, 613.4, 197.3, 620.0, 204.5, 620.0, 204.5,
+626.6, 211.7, 626.7, 211.8, 626.7, 211.8, 626.8, 212.0, 626.8, 212.0,
+626.8, 212.0, 626.8, 212.0, 655.9, 255.9, 655.5, 256.1, 685.6, 299.3,
+696.0, 315.0, 696.0, 315.0, 706.4, 330.7, 700.6, 324.6, 698.1, 326.9,
+689.7, 323.2, 669.0, 302.6, 669.0, 301.8, 652.8, 277.4, 689.7, 323.2,
+681.7, 319.5, 675.5, 321.6, 673.6, 315.9, 673.6, 315.8, 673.6, 315.8,
+673.5, 315.8, 673.6, 315.7, 663.2, 296.6, 652.8, 277.4, 669.0, 301.8,
+669.0, 302.6, 689.7, 323.2, 521.4, 200.9, 516.3, 200.1, 515.0, 201.9,
+511.1, 199.4, 505.2, 196.6, 505.2, 196.6, 499.3, 193.9, 491.5, 190.5,
+491.8, 189.9, 484.2, 186.0, 456.3, 171.2, 457.3, 169.0, 428.4, 156.5,
+427.8, 156.2, 427.8, 156.2, 427.2, 155.8, 423.8, 153.6, 423.0, 154.3,
+420.4, 151.4, 414.8, 145.9, 415.1, 145.5, 409.3, 140.3, 413.9, 139.4,
+413.9, 139.4, 418.6, 138.4, 425.7, 140.2, 425.1, 142.3, 431.7, 146.3,
+476.5, 173.6, 477.8, 171.7, 521.4, 200.9, 616.4, -54.1, 618.2, -44.7,
+618.2, -44.7, 620.1, -35.4, 629.9, -28.1, 620.1, -14.9, 620.2, 5.5,
+602.6, -10.4, 600.1, -7.7, 580.0, -20.8, 579.0, -36.3, 595.0, -46.3,
+616.4, -54.1, 580.0, -20.8, 578.8, -21.6, 577.6, -21.7, 577.7, -22.4,
+577.3, -31.7, 574.6, -40.2, 577.0, -41.0, 582.2, -57.8, 582.2, -57.8,
+587.3, -74.6, 593.8, -76.3, 595.3, -80.4, 597.6, -88.0, 597.7, -88.2,
+598.1, -88.3, 598.2, -88.1, 605.5, -82.0, 608.1, -83.2, 612.3, -75.3,
+615.4, -75.0, 614.4, -64.7, 616.4, -54.1, 595.0, -46.3, 579.0, -36.3,
+580.0, -20.8, 609.3, 615.1, 609.3, 614.5, 609.2, 614.5, 609.4, 614.0,
+616.4, 598.5, 612.9, 586.0, 623.3, 583.1, 625.3, 579.5, 626.3, 579.7,
+627.2, 575.8, 627.0, 576.2, 627.3, 576.4, 627.3, 576.9, 628.7, 597.0,
+628.7, 597.0, 630.0, 617.0, 631.0, 631.0, 631.0, 631.0, 631.9, 644.9,
+632.5, 653.6, 632.6, 653.6, 633.1, 662.4, 634.6, 686.3, 634.3, 686.3,
+635.9, 710.2, 635.9, 710.5, 636.4, 710.8, 636.4, 710.8, 636.4, 710.8,
+635.9, 710.5, 635.9, 710.2, 636.2, 709.4, 634.9, 708.8, 633.8, 707.5,
+620.9, 661.5, 621.3, 661.4, 609.3, 615.1, 633.8, 707.5, 632.3, 705.6,
+630.8, 703.8, 630.9, 703.8, 624.7, 696.0, 624.7, 696.0, 618.6, 688.3,
+618.4, 688.2, 618.4, 688.0, 618.3, 687.8, 622.9, 652.7, 628.7, 639.7,
+609.2, 615.2, 609.3, 615.2, 609.3, 615.2, 609.3, 615.1, 621.3, 661.4,
+620.9, 661.5, 633.8, 707.5, 696.0, 603.6, 696.3, 604.1, 696.3, 604.2,
+696.7, 604.7, 695.6, 610.5, 701.1, 611.6, 705.5, 618.5, 685.3, 634.3,
+691.7, 642.6, 678.0, 666.8, 683.9, 659.1, 680.8, 656.7, 683.7, 646.7,
+686.2, 637.8, 686.2, 637.8, 688.7, 629.0, 692.4, 616.3, 691.9, 616.1,
+696.0, 603.6, 727.2, 652.4, 737.6, 668.6, 733.2, 676.6, 747.9, 684.8,
+750.0, 690.9, 751.5, 690.4, 755.2, 696.0, 744.3, 704.0, 749.3, 710.8,
+743.5, 725.6, 743.6, 724.3, 741.7, 724.2, 739.9, 722.9, 734.8, 702.4,
+736.2, 702.0, 732.4, 681.1, 729.8, 666.8, 732.9, 653.7, 727.2, 652.4,
+679.5, 761.5, 665.9, 735.7, 670.8, 731.5, 652.3, 710.0, 654.4, 707.5,
+654.4, 707.5, 656.6, 705.1, 664.6, 716.4, 661.9, 718.3, 667.3, 731.5,
+671.8, 742.5, 671.8, 742.5, 676.3, 753.6, 677.2, 755.8, 677.2, 755.8,
+678.1, 758.1, 678.8, 759.8, 678.6, 759.8, 679.5, 761.5, 579.1, 629.3,
+577.6, 632.0, 576.4, 631.8, 576.1, 634.6, 575.6, 633.9, 575.6, 633.9,
+575.0, 633.2, 566.0, 602.2, 564.8, 602.5, 554.6, 571.8, 558.1, 584.8,
+560.0, 584.3, 565.3, 596.8, 572.2, 613.1, 574.3, 612.5, 579.1, 629.3,
+559.9, 507.0, 561.9, 500.3, 563.0, 500.5, 564.0, 493.7, 568.0, 492.8,
+572.1, 492.7, 572.1, 491.9, 576.3, 494.4, 580.8, 495.0, 580.6, 496.9,
+584.7, 505.2, 590.1, 502.5, 599.7, 508.2, 583.8, 515.9, 577.2, 514.0,
+559.9, 507.0, 613.0, 725.3, 604.5, 735.6, 603.8, 735.1, 595.9, 745.9,
+592.5, 750.0, 588.2, 752.9, 589.1, 754.2, 590.3, 743.5, 587.9, 743.2,
+586.8, 732.3, 588.7, 728.1, 593.8, 730.4, 600.8, 728.6, 606.9, 726.9,
+612.1, 728.4, 613.0, 725.3, 576.6, 650.0, 574.9, 663.4, 567.3, 670.3,
+573.1, 676.7, 567.5, 713.8, 563.1, 714.0, 563.4, 751.1, 558.5, 743.3,
+558.5, 743.3, 553.7, 735.5, 559.4, 692.0, 562.7, 692.0, 576.6, 650.0,
+553.7, 735.5, 549.1, 728.1, 549.1, 728.1, 544.4, 720.7, 538.1, 710.3,
+538.0, 710.4, 531.6, 700.1, 547.8, 681.1, 560.5, 679.5, 560.3, 659.1,
+568.5, 654.3, 571.0, 656.4, 576.7, 649.6, 576.6, 649.8, 576.6, 649.8,
+576.6, 650.0, 562.7, 692.0, 559.4, 692.0, 553.7, 735.5, 601.6, 740.8,
+602.5, 740.0, 602.5, 740.0, 603.4, 739.3, 608.2, 747.5, 607.2, 748.1,
+611.1, 756.9, 611.2, 757.0, 611.2, 757.0, 611.2, 757.1, 616.2, 764.3,
+611.1, 767.9, 611.0, 778.7, 611.9, 774.5, 609.9, 774.1, 608.7, 769.4,
+605.2, 755.1, 604.2, 755.3, 601.6, 740.8, 630.4, 715.1, 633.1, 712.6,
+634.5, 713.3, 635.9, 710.2, 635.9, 710.5, 636.4, 710.8, 636.4, 710.8,
+637.3, 715.8, 640.3, 720.1, 643.6, 719.9, 648.2, 734.2, 653.5, 732.4,
+663.5, 745.0, 660.2, 736.0, 655.5, 737.8, 647.5, 730.5, 638.9, 722.8,
+635.8, 724.6, 630.4, 715.1, 556.0, 871.7, 550.9, 851.6, 545.8, 831.4,
+545.8, 831.4, 546.6, 829.6, 545.2, 829.0, 544.5, 826.6, 554.0, 824.7,
+563.2, 824.2, 564.4, 827.7, 569.6, 842.9, 560.9, 845.9, 557.4, 864.1,
+556.7, 867.9, 554.8, 868.6, 556.0, 871.7, 514.8, 533.6, 503.1, 533.1,
+491.9, 530.4, 491.4, 532.6, 486.1, 528.4, 475.4, 529.7, 474.6, 531.9,
+474.7, 530.5, 474.7, 530.5, 474.8, 529.0, 469.2, 522.9, 486.6, 506.8,
+498.4, 484.5, 495.6, 487.0, 501.4, 493.6, 504.5, 502.6, 508.3, 514.0,
+508.3, 514.0, 512.1, 525.5, 513.5, 529.6, 514.9, 533.6, 514.8, 533.6,
+448.4, 376.8, 447.4, 374.6, 447.4, 374.6, 446.4, 372.4, 448.0, 361.3,
+448.9, 361.0, 447.8, 349.9, 451.7, 335.6, 451.7, 335.6, 455.6, 321.4,
+462.3, 334.2, 453.4, 338.8, 451.1, 356.2, 449.7, 366.5, 451.2, 367.0,
+448.4, 376.8, 442.2, 641.3, 444.8, 630.6, 447.3, 619.8, 447.3, 619.8,
+450.4, 607.2, 451.7, 607.4, 453.5, 594.6, 455.3, 625.3, 455.3, 625.3,
+457.1, 655.9, 458.4, 650.7, 444.6, 650.0, 442.2, 641.3, 305.5, 617.8,
+304.9, 615.7, 308.3, 614.8, 311.1, 611.9, 311.6, 612.4, 339.0, 591.0,
+356.8, 563.5, 358.0, 579.6, 358.0, 579.6, 359.2, 595.6, 337.3, 605.7,
+342.0, 615.9, 324.9, 636.3, 323.8, 637.1, 319.9, 631.5, 314.9, 626.8,
+310.2, 622.3, 306.8, 623.1, 305.5, 617.8, 273.1, 632.1, 273.6, 645.9,
+270.9, 655.3, 260.4, 660.1, 260.0, 649.2, 260.0, 649.2, 259.6, 638.3,
+262.0, 633.2, 273.2, 635.0, 273.1, 632.1, 356.6, 461.0, 355.1, 470.8,
+349.6, 477.1, 353.6, 480.6, 347.2, 481.7, 347.2, 481.7, 340.7, 482.8,
+342.5, 471.0, 347.1, 462.7, 356.6, 461.0, 410.9, 428.7, 421.1, 434.9,
+421.1, 434.9, 431.3, 441.1, 431.3, 441.1, 431.5, 441.2, 431.6, 441.3,
+429.9, 448.1, 429.0, 448.0, 428.3, 454.9, 428.0, 458.4, 427.4, 458.4,
+426.6, 461.9, 426.5, 460.5, 422.7, 460.9, 418.9, 459.8, 417.7, 449.6,
+393.3, 452.4, 367.8, 445.0, 367.1, 442.5, 388.9, 430.0, 410.9, 428.7,
+367.8, 445.0, 366.6, 444.6, 365.4, 444.7, 365.4, 444.3, 359.8, 439.8,
+359.8, 439.8, 354.1, 435.3, 368.9, 434.1, 368.0, 423.2, 381.9, 411.1,
+389.8, 423.9, 396.4, 419.9, 410.9, 428.7, 388.9, 430.0, 367.1, 442.5,
+367.8, 445.0, 68.2, 220.7, 67.1, 213.8, 64.9, 213.5, 65.9, 206.9,
+66.4, 193.9, 65.6, 193.9, 66.9, 181.0, 67.2, 178.0, 67.2, 178.0,
+67.4, 175.0, 67.9, 176.4, 68.0, 176.4, 68.6, 177.8, 82.1, 176.7,
+85.3, 216.6, 101.9, 255.4, 102.1, 238.2, 82.5, 239.9, 68.2, 220.7,
+819.3, 199.5, 818.6, 198.6, 817.2, 199.7, 816.1, 198.8, 809.4, 191.8,
+809.4, 191.8, 802.7, 184.8, 805.2, 187.7, 805.6, 187.3, 808.4, 189.9,
+810.7, 191.9, 810.7, 191.9, 813.0, 194.0, 816.1, 196.8, 817.1, 196.1,
+819.3, 199.5, 916.9, 264.8, 867.2, 232.5, 861.1, 193.4, 817.5, 200.3,
+816.8, 199.5, 816.8, 199.5, 816.1, 198.8, 817.2, 199.7, 818.6, 198.6,
+819.3, 199.5, 857.7, 208.5, 857.7, 208.3, 896.1, 217.4, 896.2, 217.4,
+896.3, 217.5, 896.3, 217.7, 906.7, 241.2, 902.2, 261.5, 916.9, 264.8,
+896.3, 217.7, 896.4, 217.7, 896.5, 217.6, 896.6, 217.7, 912.8, 229.1,
+912.7, 229.3, 929.0, 240.6, 934.5, 244.9, 934.5, 244.9, 940.1, 249.1,
+943.0, 254.3, 941.7, 255.7, 941.3, 262.2, 941.3, 262.5, 939.2, 262.7,
+939.3, 263.2, 939.6, 265.2, 925.6, 266.1, 925.5, 269.8, 925.5, 269.9,
+925.2, 269.9, 925.0, 270.0, 922.6, 266.3, 921.0, 267.4, 916.9, 264.8,
+904.8, 242.3, 906.9, 241.2, 896.6, 217.7, 896.5, 217.6, 896.4, 217.7,
+896.3, 217.7, 638.0, 224.2, 633.7, 219.4, 633.7, 219.4, 629.3, 214.7,
+636.9, 207.0, 635.4, 205.6, 641.5, 196.5, 671.3, 221.4, 677.8, 213.5,
+714.1, 230.6, 713.9, 232.2, 675.4, 231.2, 638.0, 224.2, 714.1, 230.6,
+715.0, 231.0, 715.7, 231.6, 715.8, 231.4, 719.2, 235.0, 721.8, 239.3,
+722.0, 239.1, 714.5, 236.3, 704.8, 239.2, 704.8, 239.4, 690.8, 230.9,
+685.6, 239.6, 666.3, 239.8, 656.3, 228.9, 652.4, 224.8, 638.7, 224.9,
+638.4, 224.6, 638.4, 224.6, 638.0, 224.2, 675.4, 231.2, 713.9, 232.2,
+714.1, 230.6, 719.5, 282.8, 723.5, 283.6, 727.4, 284.5, 727.4, 284.4,
+744.5, 291.9, 765.0, 289.6, 764.8, 292.2, 770.3, 294.4, 771.1, 303.5,
+773.3, 302.8, 766.0, 305.3, 763.9, 299.3, 754.4, 295.7, 737.0, 289.2,
+736.7, 290.0, 719.5, 282.8, 725.3, 229.8, 741.4, 229.6, 741.4, 229.6,
+757.4, 229.3, 769.7, 237.1, 769.7, 237.1, 782.1, 244.9, 778.4, 241.6,
+776.7, 243.5, 771.3, 242.0, 748.3, 235.9, 740.9, 244.0, 725.3, 229.8,
+519.0, 181.6, 528.0, 190.6, 526.3, 193.5, 537.0, 199.6, 538.5, 201.6,
+538.5, 201.6, 540.0, 203.6, 532.3, 199.4, 530.7, 202.2, 521.4, 200.9,
+477.8, 171.7, 476.5, 173.6, 431.7, 146.3, 434.6, 169.1, 477.1, 160.4,
+519.0, 181.6, 431.7, 146.3, 425.1, 142.3, 425.7, 140.2, 418.6, 138.4,
+432.6, 135.3, 433.1, 136.8, 446.7, 132.3, 455.3, 131.1, 458.1, 133.7,
+463.7, 128.7, 464.0, 128.4, 464.3, 128.6, 464.8, 128.5, 471.2, 135.4,
+471.8, 134.9, 478.7, 141.4, 503.0, 150.3, 498.8, 161.5, 519.0, 181.6,
+477.1, 160.4, 434.6, 169.1, 431.7, 146.3, 662.2, 550.8, 669.9, 559.1,
+668.4, 560.5, 674.6, 570.2, 678.1, 576.1, 676.7, 578.5, 682.0, 581.7,
+681.3, 582.6, 684.2, 587.1, 685.3, 586.9, 687.4, 596.0, 690.6, 595.2,
+696.0, 603.6, 691.9, 616.1, 692.4, 616.3, 688.7, 629.0, 673.2, 615.5,
+681.1, 606.5, 673.4, 583.9, 669.5, 572.2, 669.5, 572.2, 665.5, 560.5,
+663.8, 555.6, 665.4, 554.2, 662.2, 550.8, 721.2, 708.8, 711.9, 701.9,
+712.2, 701.4, 702.6, 694.9, 693.5, 684.1, 688.8, 682.7, 688.3, 669.9,
+688.6, 678.5, 695.3, 678.2, 702.3, 686.5, 711.7, 697.7, 720.7, 697.3,
+721.2, 708.8, 661.0, 700.0, 661.9, 699.0, 662.6, 699.1, 662.7, 698.1,
+676.1, 684.5, 675.3, 683.7, 687.9, 669.3, 687.9, 669.4, 688.1, 669.6,
+688.3, 669.9, 688.8, 682.7, 693.5, 684.1, 702.6, 694.9, 703.6, 695.6,
+703.3, 696.1, 703.9, 697.2, 692.5, 699.2, 692.3, 698.0, 680.6, 698.7,
+675.1, 699.1, 675.1, 699.1, 669.6, 699.4, 665.3, 699.7, 665.1, 700.8,
+661.0, 700.0, 592.4, 572.5, 589.3, 586.8, 582.8, 599.4, 586.2, 601.1,
+580.0, 612.0, 575.6, 621.5, 580.6, 626.7, 579.9, 628.0, 579.9, 628.0,
+579.1, 629.3, 574.3, 612.5, 572.2, 613.1, 565.3, 596.8, 568.7, 582.3,
+581.8, 570.3, 592.4, 572.5, 565.3, 596.8, 560.0, 584.3, 558.1, 584.8,
+554.6, 571.8, 550.9, 560.4, 544.2, 551.1, 547.1, 549.0, 547.3, 547.2,
+546.7, 546.9, 547.5, 545.3, 549.5, 538.2, 549.5, 538.2, 551.5, 531.1,
+551.5, 531.2, 552.3, 531.5, 552.2, 531.6, 554.8, 533.5, 554.8, 533.5,
+557.4, 535.4, 576.0, 549.0, 579.0, 546.1, 594.5, 562.6, 593.4, 567.5,
+593.4, 567.5, 592.4, 572.5, 581.8, 570.3, 568.7, 582.3, 565.3, 596.8,
+605.0, 514.1, 599.8, 538.4, 599.8, 538.4, 594.5, 562.6, 579.0, 546.1,
+576.0, 549.0, 557.4, 535.4, 560.7, 520.3, 594.6, 506.5, 605.0, 514.1,
+557.4, 535.4, 554.8, 533.5, 554.8, 533.5, 552.2, 531.6, 556.4, 519.4,
+556.0, 519.3, 559.9, 507.0, 577.2, 514.0, 583.8, 515.9, 599.7, 508.2,
+602.6, 509.9, 602.6, 509.9, 605.6, 511.6, 605.8, 511.8, 605.3, 512.9,
+605.0, 514.1, 594.6, 506.5, 560.7, 520.3, 557.4, 535.4, 600.8, 728.6,
+593.8, 730.4, 588.7, 728.1, 586.8, 732.3, 583.7, 702.7, 583.7, 702.7,
+580.6, 673.2, 581.2, 674.9, 583.3, 674.2, 586.0, 675.3, 595.4, 701.2,
+600.4, 701.8, 600.8, 728.6, 586.0, 675.3, 602.3, 681.8, 602.2, 681.9,
+618.6, 688.3, 624.7, 696.0, 624.7, 696.0, 630.9, 703.8, 620.4, 712.7,
+621.9, 714.5, 613.0, 725.3, 612.1, 728.4, 606.9, 726.9, 600.8, 728.6,
+600.4, 701.8, 595.4, 701.2, 586.0, 675.3, 610.8, 800.2, 610.7, 815.3,
+603.2, 821.6, 610.6, 830.4, 610.6, 830.5, 608.9, 830.3, 607.3, 830.2,
+585.8, 828.9, 585.8, 828.9, 564.4, 827.7, 563.2, 824.2, 554.0, 824.7,
+544.5, 826.6, 544.5, 826.3, 544.5, 826.3, 544.4, 826.1, 577.5, 812.9,
+578.6, 798.2, 610.8, 800.2, 611.1, 756.9, 607.2, 748.1, 608.2, 747.5,
+603.4, 739.3, 609.7, 733.6, 609.7, 733.6, 616.0, 728.0, 614.4, 728.3,
+615.2, 732.7, 614.4, 737.3, 614.3, 737.8, 614.3, 737.8, 614.2, 738.3,
+612.7, 747.6, 613.5, 756.7, 611.1, 756.9, 607.3, 830.2, 608.9, 830.3,
+610.6, 830.5, 610.6, 830.4, 616.6, 837.5, 624.6, 834.2, 637.6, 831.9,
+634.0, 839.4, 634.0, 839.4, 630.3, 847.0, 630.3, 846.9, 628.5, 847.7,
+626.7, 848.5, 627.4, 851.2, 590.4, 857.9, 556.1, 872.2, 556.0, 871.9,
+556.0, 871.9, 556.0, 871.7, 554.8, 868.6, 556.7, 867.9, 557.4, 864.1,
+580.8, 845.2, 581.8, 846.3, 607.3, 830.2, 557.4, 864.1, 560.9, 845.9,
+569.6, 842.9, 564.4, 827.7, 585.8, 828.9, 585.8, 828.9, 607.3, 830.2,
+581.8, 846.3, 580.8, 845.2, 557.4, 864.1, 547.5, 499.4, 547.5, 499.8,
+547.7, 500.0, 547.5, 500.3, 536.3, 517.1, 536.3, 517.1, 525.2, 534.0,
+525.1, 533.3, 520.0, 533.8, 514.8, 533.6, 514.9, 533.6, 513.5, 529.6,
+512.1, 525.5, 518.9, 516.3, 521.8, 518.4, 531.4, 511.3, 539.5, 505.4,
+539.3, 505.1, 547.5, 499.4, 451.1, 356.2, 453.4, 338.8, 462.3, 334.2,
+455.6, 321.4, 456.1, 319.7, 457.2, 318.6, 456.6, 318.0, 458.1, 318.4,
+458.1, 318.4, 459.7, 318.9, 457.0, 337.8, 462.6, 349.1, 451.1, 356.2,
+429.2, 696.3, 435.7, 668.8, 435.7, 668.8, 442.2, 641.3, 444.6, 650.0,
+458.4, 650.7, 457.1, 655.9, 457.9, 669.3, 457.9, 669.3, 458.7, 682.6,
+462.2, 696.8, 457.6, 697.9, 456.6, 713.1, 453.4, 715.7, 446.3, 706.8,
+436.1, 700.5, 432.6, 698.4, 429.0, 698.0, 429.2, 696.3, 347.1, 650.0,
+347.7, 658.4, 346.5, 658.6, 348.4, 666.8, 338.4, 696.1, 338.4, 696.1,
+328.4, 725.4, 328.1, 726.3, 328.0, 726.3, 327.5, 727.1, 325.7, 730.5,
+324.9, 730.3, 324.0, 733.9, 334.6, 692.0, 335.4, 692.2, 346.9, 650.6,
+347.0, 650.3, 347.1, 650.3, 347.1, 650.0, 270.1, 872.8, 264.9, 864.6,
+264.9, 864.6, 259.8, 856.5, 259.4, 855.5, 260.7, 854.9, 261.5, 853.3,
+292.7, 793.6, 292.7, 793.6, 324.0, 733.9, 324.9, 730.3, 325.7, 730.5,
+327.5, 727.1, 299.2, 800.1, 306.2, 803.8, 270.1, 872.8, 327.5, 727.1,
+328.0, 726.3, 328.1, 726.3, 328.4, 725.4, 322.8, 741.8, 319.6, 741.4,
+317.2, 758.2, 297.5, 803.6, 296.7, 804.1, 285.2, 852.2, 281.3, 861.8,
+284.4, 870.0, 278.5, 871.8, 275.7, 875.5, 276.9, 876.5, 275.3, 881.2,
+273.1, 876.8, 272.7, 877.0, 270.1, 872.8, 306.2, 803.8, 299.2, 800.1,
+327.5, 727.1, 314.9, 626.8, 319.9, 631.5, 323.8, 637.1, 324.9, 636.3,
+313.0, 650.4, 313.0, 650.4, 301.1, 664.5, 306.3, 660.7, 303.8, 657.3,
+306.4, 650.2, 310.6, 638.5, 306.5, 633.0, 314.9, 626.8, 347.1, 555.7,
+347.3, 561.2, 337.1, 561.5, 327.0, 567.2, 310.5, 576.7, 311.8, 580.6,
+294.0, 586.2, 297.6, 585.1, 296.3, 581.2, 298.6, 576.2, 300.9, 571.2,
+300.9, 571.2, 303.2, 566.1, 309.5, 552.3, 309.5, 552.3, 315.8, 538.5,
+323.9, 534.9, 320.7, 527.7, 325.6, 516.9, 342.1, 525.9, 346.6, 536.0,
+347.1, 555.7, 325.6, 516.9, 327.3, 513.3, 330.1, 511.2, 329.0, 509.6,
+341.8, 522.1, 341.8, 522.1, 354.7, 534.6, 356.5, 535.4, 355.0, 539.1,
+355.4, 543.6, 356.2, 544.4, 351.6, 549.5, 347.8, 555.3, 347.6, 555.6,
+347.1, 555.7, 347.1, 555.7, 346.6, 536.0, 342.1, 525.9, 325.6, 516.9,
+278.9, 619.5, 273.7, 609.2, 282.3, 599.0, 294.0, 586.2, 311.8, 580.6,
+310.5, 576.7, 327.0, 567.2, 330.2, 573.7, 313.3, 582.2, 299.5, 597.1,
+289.2, 608.3, 276.5, 614.7, 278.9, 619.5, 308.5, 478.6, 294.1, 470.2,
+294.6, 469.4, 279.8, 461.8, 279.3, 461.5, 278.9, 461.2, 279.0, 460.9,
+285.7, 436.7, 300.0, 420.9, 290.7, 411.9, 291.4, 409.5, 291.4, 409.5,
+292.1, 407.2, 292.0, 407.2, 292.6, 409.2, 293.0, 411.3, 300.8, 444.9,
+316.2, 449.0, 308.5, 478.6, 105.3, 309.6, 90.4, 286.9, 92.4, 285.4,
+75.5, 264.2, 71.9, 242.5, 71.9, 242.5, 68.2, 220.7, 82.5, 239.9,
+102.1, 238.2, 101.9, 255.4, 103.7, 259.7, 103.7, 259.7, 105.6, 264.0,
+107.0, 286.7, 105.6, 309.6, 105.3, 309.6, 105.6, 264.0, 105.7, 264.1,
+105.7, 264.1, 105.7, 264.3, 108.2, 272.8, 108.8, 272.7, 110.6, 281.3,
+114.6, 299.8, 114.6, 299.8, 118.7, 318.3, 116.7, 318.2, 116.5, 320.8,
+114.3, 323.3, 114.5, 316.6, 109.8, 316.5, 105.3, 309.6, 102.1, 287.5,
+105.6, 287.0, 105.7, 264.3, 105.7, 264.1, 105.7, 264.1, 105.6, 264.0,
+813.0, 194.0, 807.8, 189.4, 807.8, 189.4, 802.7, 184.8, 802.4, 184.5,
+802.4, 184.5, 802.1, 184.2, 805.3, 187.0, 805.3, 187.0, 808.4, 189.9,
+810.7, 191.9, 810.7, 191.9, 813.0, 194.0, 703.6, 295.6, 697.1, 288.6,
+697.1, 288.6, 690.7, 281.6, 688.5, 279.2, 688.5, 279.2, 686.3, 276.8,
+700.2, 279.7, 706.7, 273.4, 714.4, 281.7, 717.0, 282.2, 717.0, 282.2,
+719.5, 282.8, 736.7, 290.0, 737.0, 289.2, 754.4, 295.7, 739.0, 308.1,
+727.8, 301.0, 703.6, 295.6, 754.4, 295.7, 763.9, 299.3, 766.0, 305.3,
+773.3, 302.8, 773.5, 302.8, 773.7, 303.0, 773.6, 303.1, 765.2, 307.2,
+764.2, 314.4, 763.9, 325.8, 740.5, 318.1, 741.1, 315.2, 717.1, 310.4,
+710.4, 303.0, 710.4, 303.0, 703.6, 295.6, 727.8, 301.0, 739.0, 308.1,
+754.4, 295.7, 729.9, 248.9, 726.0, 244.0, 727.1, 241.1, 722.0, 239.1,
+721.8, 239.3, 719.2, 235.0, 715.8, 231.4, 715.9, 231.3, 715.2, 230.7,
+714.6, 230.0, 718.0, 232.5, 720.0, 229.9, 725.3, 229.8, 740.9, 244.0,
+748.3, 235.9, 771.3, 242.0, 765.5, 235.9, 756.5, 244.5, 741.7, 247.0,
+735.8, 247.9, 734.7, 250.8, 729.9, 248.9, 627.3, 576.9, 627.3, 576.4,
+627.0, 576.2, 627.2, 575.8, 628.0, 572.7, 625.3, 571.4, 626.8, 569.1,
+632.9, 555.2, 632.9, 555.2, 639.0, 541.4, 632.2, 549.5, 635.4, 552.2,
+631.9, 562.9, 629.6, 569.9, 629.8, 570.0, 627.3, 576.9, 732.4, 681.1,
+736.2, 702.0, 734.8, 702.4, 739.9, 722.9, 730.6, 715.9, 730.6, 715.9,
+721.2, 708.8, 720.7, 697.3, 711.7, 697.7, 702.3, 686.5, 705.4, 678.9,
+726.0, 674.9, 732.4, 681.1, 702.3, 686.5, 695.3, 678.2, 688.6, 678.5,
+688.3, 669.9, 688.1, 669.6, 687.9, 669.4, 687.9, 669.3, 702.4, 652.8,
+710.2, 655.5, 716.9, 636.3, 722.1, 644.3, 722.1, 644.3, 727.2, 652.4,
+732.9, 653.7, 729.8, 666.8, 732.4, 681.1, 726.0, 674.9, 705.4, 678.9,
+702.3, 686.5, 667.3, 731.5, 661.9, 718.3, 664.6, 716.4, 656.6, 705.1,
+658.8, 702.5, 658.8, 702.5, 661.0, 700.0, 665.1, 700.8, 665.3, 699.7,
+669.6, 699.4, 672.6, 714.6, 674.1, 728.5, 667.3, 731.5, 583.5, 760.9,
+586.3, 757.5, 588.6, 757.8, 589.1, 754.2, 588.2, 752.9, 592.5, 750.0,
+595.9, 745.9, 598.1, 742.9, 598.8, 743.4, 601.6, 740.8, 604.2, 755.3,
+605.2, 755.1, 608.7, 769.4, 605.2, 773.4, 593.4, 768.6, 583.5, 760.9,
+647.5, 730.5, 655.5, 737.8, 660.2, 736.0, 663.5, 745.0, 664.1, 745.8,
+664.1, 745.8, 664.7, 746.6, 662.4, 744.7, 662.5, 744.5, 660.3, 742.5,
+653.9, 736.5, 654.3, 736.0, 647.5, 730.5, 521.2, 461.3, 529.7, 453.6,
+529.7, 453.6, 538.1, 445.8, 538.4, 448.1, 538.8, 448.1, 539.5, 450.4,
+531.8, 457.4, 520.4, 458.7, 521.2, 461.3, 477.2, 368.6, 467.7, 376.4,
+462.7, 374.2, 458.3, 384.2, 454.8, 383.8, 452.5, 381.9, 451.3, 383.3,
+449.8, 380.1, 449.8, 380.1, 448.4, 376.8, 451.2, 367.0, 449.7, 366.5,
+451.1, 356.2, 462.6, 349.1, 457.0, 337.8, 459.7, 318.9, 461.5, 319.4,
+461.5, 319.4, 463.3, 319.9, 471.7, 343.7, 479.1, 344.9, 477.2, 368.6,
+463.3, 319.9, 471.0, 322.1, 478.7, 323.4, 478.6, 324.2, 486.4, 339.0,
+486.4, 339.0, 494.1, 353.7, 494.4, 354.1, 493.2, 355.0, 492.2, 356.2,
+488.7, 364.1, 484.7, 362.4, 477.2, 368.6, 479.1, 344.9, 471.7, 343.7,
+463.3, 319.9, 418.9, 766.3, 416.7, 763.5, 416.7, 763.5, 414.4, 760.8,
+413.6, 760.2, 414.9, 758.5, 415.4, 756.3, 418.5, 760.1, 419.8, 761.8,
+418.9, 766.3, 238.5, 801.7, 235.1, 782.2, 223.7, 770.7, 231.7, 762.7,
+232.7, 759.2, 232.7, 759.2, 233.8, 755.6, 236.8, 761.2, 234.5, 762.4,
+235.2, 769.2, 235.4, 771.3, 235.4, 771.3, 235.6, 773.3, 236.2, 779.1,
+236.2, 779.1, 236.8, 784.9, 237.4, 790.6, 237.4, 790.6, 238.0, 796.2,
+238.3, 799.0, 238.0, 799.0, 238.5, 801.7, 283.5, 685.5, 273.7, 697.1,
+275.7, 699.7, 264.0, 708.7, 263.0, 709.5, 263.0, 709.5, 262.0, 710.4,
+267.0, 702.9, 264.5, 698.4, 261.3, 688.3, 259.5, 685.3, 261.2, 684.4,
+261.0, 680.4, 271.8, 679.0, 283.1, 681.8, 283.5, 685.5, 297.7, 626.0,
+292.4, 638.3, 287.9, 636.3, 278.2, 646.7, 269.3, 656.0, 270.6, 657.7,
+260.5, 665.3, 260.4, 662.7, 260.4, 662.7, 260.4, 660.1, 270.9, 655.3,
+273.6, 645.9, 273.1, 632.1, 276.0, 625.8, 274.8, 624.9, 278.9, 619.5,
+276.5, 614.7, 289.2, 608.3, 299.5, 597.1, 303.2, 598.4, 303.1, 613.5,
+297.7, 626.0, 299.5, 597.1, 313.3, 582.2, 330.2, 573.7, 327.0, 567.2,
+337.1, 561.5, 347.3, 561.2, 347.1, 555.7, 347.1, 555.7, 347.6, 555.6,
+347.8, 555.3, 329.4, 583.6, 307.1, 608.3, 311.1, 611.9, 308.3, 614.8,
+304.9, 615.7, 305.5, 617.8, 301.6, 621.9, 296.9, 623.2, 297.7, 626.0,
+303.1, 613.5, 303.2, 598.4, 299.5, 597.1, 270.4, 486.5, 271.2, 481.3,
+273.8, 480.1, 272.0, 476.1, 274.5, 467.6, 274.5, 467.6, 277.0, 459.0,
+277.0, 459.2, 277.2, 459.3, 277.5, 459.5, 274.2, 473.1, 274.7, 473.3,
+270.4, 486.5, 273.8, 561.0, 276.9, 540.3, 287.9, 535.6, 309.5, 524.1,
+312.6, 531.3, 315.8, 538.5, 315.8, 538.5, 309.5, 552.3, 309.5, 552.3,
+303.2, 566.1, 292.8, 571.7, 273.8, 561.4, 273.8, 561.0, 293.0, 411.3,
+292.6, 409.2, 292.0, 407.2, 292.1, 407.2, 292.3, 406.5, 292.3, 406.5,
+292.5, 405.8, 306.0, 421.3, 313.3, 417.0, 335.5, 422.7, 339.3, 427.0,
+338.6, 427.9, 343.0, 431.2, 352.6, 437.1, 350.7, 440.1, 358.5, 448.9,
+359.1, 449.2, 358.1, 451.1, 357.8, 453.2, 346.9, 448.6, 347.8, 446.7,
+337.7, 440.2, 315.4, 425.7, 314.6, 426.8, 293.0, 411.3, 808.4, 189.9,
+805.3, 187.0, 805.3, 187.0, 802.1, 184.2, 799.7, 181.7, 799.7, 181.7,
+797.2, 179.1, 809.8, 174.1, 821.3, 166.9, 820.2, 163.7, 862.2, 179.9,
+899.8, 209.2, 896.1, 217.4, 893.9, 222.3, 851.7, 205.1, 808.4, 189.9,
+896.1, 217.4, 896.4, 217.5, 896.4, 217.5, 896.6, 217.7, 896.5, 217.6,
+896.4, 217.7, 896.3, 217.7, 857.8, 208.6, 857.3, 210.4, 819.3, 199.5,
+817.1, 196.1, 816.1, 196.8, 813.0, 194.0, 810.7, 191.9, 810.7, 191.9,
+808.4, 189.9, 851.7, 205.1, 893.9, 222.3, 896.1, 217.4, 786.5, 247.7,
+786.8, 247.9, 786.8, 247.9, 787.0, 248.0, 779.8, 266.6, 786.1, 269.0,
+785.1, 289.9, 785.8, 278.9, 785.5, 278.9, 785.9, 267.8, 786.2, 257.8,
+786.0, 257.7, 786.5, 247.7, 676.3, 753.6, 671.8, 742.5, 671.8, 742.5,
+667.3, 731.5, 674.1, 728.5, 672.6, 714.6, 669.6, 699.4, 675.1, 699.1,
+675.1, 699.1, 680.6, 698.7, 684.3, 705.4, 679.9, 707.8, 679.2, 716.9,
+679.2, 717.0, 679.2, 717.0, 679.2, 717.0, 677.7, 735.3, 685.8, 742.0,
+676.3, 753.6, 610.9, 789.2, 610.9, 794.7, 610.9, 794.7, 610.8, 800.2,
+578.6, 798.2, 577.5, 812.9, 544.4, 826.1, 542.7, 819.2, 538.9, 813.6,
+540.9, 812.3, 553.8, 796.7, 553.8, 796.7, 566.7, 781.1, 585.5, 775.0,
+590.8, 780.5, 610.9, 789.2, 566.7, 781.1, 568.6, 778.9, 568.1, 777.6,
+570.5, 776.6, 573.6, 774.5, 574.2, 774.0, 575.5, 770.6, 580.5, 767.1,
+579.5, 765.7, 583.5, 760.9, 593.4, 768.6, 605.2, 773.4, 608.7, 769.4,
+609.9, 774.1, 611.9, 774.5, 611.0, 778.7, 611.0, 784.0, 611.0, 784.0,
+610.9, 789.2, 590.8, 780.5, 585.5, 775.0, 566.7, 781.1, 614.4, 737.3,
+615.2, 732.7, 614.4, 728.3, 616.0, 728.0, 623.2, 721.6, 623.2, 721.6,
+630.4, 715.1, 635.8, 724.6, 638.9, 722.8, 647.5, 730.5, 654.3, 736.0,
+653.9, 736.5, 660.3, 742.5, 638.8, 746.9, 635.9, 744.3, 614.4, 737.3,
+547.6, 495.4, 547.6, 497.4, 547.6, 497.4, 547.5, 499.4, 539.3, 505.1,
+539.5, 505.4, 531.4, 511.3, 537.2, 509.1, 536.2, 506.6, 541.1, 501.8,
+544.4, 498.6, 543.7, 496.9, 547.6, 495.4, 446.0, 761.1, 439.5, 772.2,
+430.7, 774.5, 433.0, 783.4, 430.1, 779.9, 428.0, 780.3, 427.3, 776.5,
+424.8, 770.6, 423.1, 771.4, 418.9, 766.3, 419.8, 761.8, 418.5, 760.1,
+415.4, 756.3, 416.7, 749.7, 416.7, 749.7, 418.1, 743.1, 425.6, 720.3,
+423.6, 719.7, 429.2, 696.3, 429.0, 698.0, 432.6, 698.4, 436.1, 700.5,
+443.7, 730.1, 447.1, 731.0, 446.0, 761.1, 436.1, 700.5, 446.3, 706.8,
+453.4, 715.7, 456.6, 713.1, 455.4, 730.0, 450.4, 731.2, 454.2, 746.8,
+450.1, 753.9, 450.1, 753.9, 446.0, 761.1, 447.1, 731.0, 443.7, 730.1,
+436.1, 700.5, 278.2, 646.7, 287.9, 636.3, 292.4, 638.3, 297.7, 626.0,
+296.9, 623.2, 301.6, 621.9, 305.5, 617.8, 306.8, 623.1, 310.2, 622.3,
+314.9, 626.8, 306.5, 633.0, 310.6, 638.5, 306.4, 650.2, 296.2, 655.7,
+280.3, 652.7, 278.2, 646.7, 306.4, 650.2, 303.8, 657.3, 306.3, 660.7,
+301.1, 664.5, 292.3, 675.0, 292.3, 675.0, 283.5, 685.5, 283.1, 681.8,
+271.8, 679.0, 261.0, 680.4, 260.8, 672.8, 260.8, 672.8, 260.5, 665.3,
+270.6, 657.7, 269.3, 656.0, 278.2, 646.7, 280.3, 652.7, 296.2, 655.7,
+306.4, 650.2, 292.8, 485.6, 301.1, 504.9, 301.1, 504.9, 309.5, 524.1,
+287.9, 535.6, 276.9, 540.3, 273.8, 561.0, 268.3, 566.7, 268.3, 566.7,
+262.7, 572.5, 261.5, 573.7, 260.9, 573.4, 260.4, 574.9, 275.9, 530.0,
+262.6, 516.8, 292.8, 485.6, 260.4, 574.9, 259.7, 576.6, 257.4, 577.3,
+257.6, 577.7, 257.5, 573.8, 257.5, 573.8, 257.4, 569.9, 265.0, 528.4,
+263.9, 528.2, 270.4, 486.5, 274.7, 473.3, 274.2, 473.1, 277.5, 459.5,
+278.2, 460.2, 279.1, 460.5, 279.0, 460.9, 278.9, 461.2, 279.3, 461.5,
+279.8, 461.8, 282.5, 463.1, 282.0, 463.9, 284.3, 466.1, 284.9, 476.1,
+288.5, 475.9, 292.8, 485.6, 262.6, 516.8, 275.9, 530.0, 260.4, 574.9,
+298.6, 576.2, 296.3, 581.2, 297.6, 585.1, 294.0, 586.2, 282.3, 599.0,
+273.7, 609.2, 278.9, 619.5, 274.8, 624.9, 276.0, 625.8, 273.1, 632.1,
+273.2, 635.0, 262.0, 633.2, 259.6, 638.3, 259.6, 636.7, 259.7, 636.7,
+259.5, 635.1, 272.9, 617.1, 258.6, 606.4, 257.6, 577.7, 257.4, 577.3,
+259.7, 576.6, 260.4, 574.9, 260.9, 573.4, 261.5, 573.7, 262.7, 572.5,
+280.7, 573.1, 282.9, 569.3, 298.6, 576.2, 262.7, 572.5, 268.3, 566.7,
+268.3, 566.7, 273.8, 561.0, 273.8, 561.4, 292.8, 571.7, 303.2, 566.1,
+300.9, 571.2, 300.9, 571.2, 298.6, 576.2, 282.9, 569.3, 280.7, 573.1,
+262.7, 572.5, 337.7, 440.2, 347.8, 446.7, 346.9, 448.6, 357.8, 453.2,
+357.2, 457.1, 357.2, 457.1, 356.6, 461.0, 347.1, 462.7, 342.5, 471.0,
+340.7, 482.8, 332.3, 484.2, 332.3, 484.2, 323.8, 485.7, 322.2, 463.5,
+322.7, 454.5, 337.7, 440.2, 323.8, 485.7, 322.6, 485.9, 322.1, 486.6,
+321.3, 486.1, 314.9, 482.3, 314.9, 482.3, 308.5, 478.6, 316.2, 449.0,
+300.8, 444.9, 293.0, 411.3, 314.6, 426.8, 315.4, 425.7, 337.7, 440.2,
+322.7, 454.5, 322.2, 463.5, 323.8, 485.7, 785.9, 267.8, 785.5, 278.9,
+785.8, 278.9, 785.1, 289.9, 784.5, 302.9, 783.8, 302.9, 784.0, 315.9,
+778.8, 309.5, 775.2, 302.3, 773.6, 303.1, 773.7, 303.0, 773.5, 302.8,
+773.3, 302.8, 771.1, 303.5, 770.3, 294.4, 764.8, 292.2, 766.2, 271.9,
+747.4, 270.5, 729.9, 248.9, 734.7, 250.8, 735.8, 247.9, 741.7, 247.0,
+764.7, 254.7, 769.5, 251.6, 785.9, 267.8, 741.7, 247.0, 756.5, 244.5,
+765.5, 235.9, 771.3, 242.0, 776.7, 243.5, 778.4, 241.6, 782.1, 244.9,
+784.3, 246.3, 784.3, 246.3, 786.5, 247.7, 786.0, 257.7, 786.2, 257.8,
+785.9, 267.8, 769.5, 251.6, 764.7, 254.7, 741.7, 247.0, 630.0, 617.0,
+628.7, 597.0, 628.7, 597.0, 627.3, 576.9, 629.8, 570.0, 629.6, 569.9,
+631.9, 562.9, 633.7, 589.8, 638.1, 592.3, 630.0, 617.0, 673.4, 583.9,
+681.1, 606.5, 673.2, 615.5, 688.7, 629.0, 686.2, 637.8, 686.2, 637.8,
+683.7, 646.7, 673.6, 617.0, 665.6, 611.6, 673.4, 583.9, 678.1, 758.1,
+677.2, 755.8, 677.2, 755.8, 676.3, 753.6, 685.8, 742.0, 677.7, 735.3,
+679.2, 717.0, 670.4, 723.5, 678.7, 734.6, 678.3, 752.1, 678.2, 755.1,
+679.6, 757.0, 678.1, 758.1, 614.2, 738.3, 614.3, 737.8, 614.3, 737.8,
+614.4, 737.3, 635.9, 744.3, 638.8, 746.9, 660.3, 742.5, 662.5, 744.5,
+662.4, 744.7, 664.7, 746.6, 667.7, 750.4, 667.7, 750.4, 670.7, 754.2,
+659.6, 770.2, 661.4, 771.4, 652.0, 788.7, 627.9, 781.9, 632.9, 763.7,
+614.2, 738.3, 652.0, 788.7, 644.6, 802.4, 641.6, 801.5, 637.1, 816.1,
+624.2, 786.6, 629.3, 783.0, 611.2, 757.1, 611.2, 757.0, 611.2, 757.0,
+611.1, 756.9, 613.5, 756.7, 612.7, 747.6, 614.2, 738.3, 632.9, 763.7,
+627.9, 781.9, 652.0, 788.7, 541.1, 501.8, 536.2, 506.6, 537.2, 509.1,
+531.4, 511.3, 521.8, 518.4, 518.9, 516.3, 512.1, 525.5, 508.3, 514.0,
+508.3, 514.0, 504.5, 502.6, 509.2, 496.3, 517.1, 502.3, 529.8, 502.0,
+535.4, 501.9, 540.9, 501.0, 541.1, 501.8, 235.2, 769.2, 234.5, 762.4,
+236.8, 761.2, 233.8, 755.6, 235.0, 751.5, 233.8, 748.3, 236.2, 747.4,
+237.6, 746.0, 237.6, 746.0, 239.0, 744.7, 238.4, 757.0, 240.3, 758.5,
+235.2, 769.2, 238.0, 796.2, 237.4, 790.6, 237.4, 790.6, 236.8, 784.9,
+241.5, 774.8, 244.1, 776.0, 251.5, 767.1, 255.0, 762.8, 255.0, 762.8,
+258.5, 758.5, 296.1, 712.6, 296.1, 712.7, 333.7, 666.8, 334.2, 666.2,
+334.6, 665.6, 334.6, 665.6, 334.6, 665.6, 334.2, 666.2, 333.7, 666.8,
+306.0, 704.2, 306.0, 704.2, 278.3, 741.7, 270.2, 752.7, 270.2, 752.7,
+262.0, 763.8, 250.0, 780.0, 253.0, 783.6, 238.0, 796.2, 660.5, 548.2,
+660.2, 549.1, 661.6, 549.4, 662.2, 550.8, 665.4, 554.2, 663.8, 555.6,
+665.5, 560.5, 664.0, 575.9, 661.9, 575.7, 658.3, 591.0, 652.1, 617.1,
+652.1, 617.1, 646.0, 643.2, 642.8, 656.7, 646.9, 659.5, 639.6, 670.2,
+636.4, 666.3, 632.8, 666.0, 633.1, 662.4, 632.6, 653.6, 632.5, 653.6,
+631.9, 644.9, 643.7, 595.9, 645.0, 596.2, 660.5, 548.2, 631.9, 644.9,
+631.0, 631.0, 631.0, 631.0, 630.0, 617.0, 638.1, 592.3, 633.7, 589.8,
+631.9, 562.9, 635.4, 552.2, 632.2, 549.5, 639.0, 541.4, 640.7, 537.4,
+643.4, 536.7, 642.5, 533.4, 649.4, 537.5, 656.1, 537.2, 656.3, 541.6,
+659.1, 543.9, 661.1, 545.5, 660.5, 548.2, 645.0, 596.2, 643.7, 595.9,
+631.9, 644.9, 655.4, 689.2, 647.5, 679.7, 647.5, 679.7, 639.6, 670.2,
+646.9, 659.5, 642.8, 656.7, 646.0, 643.2, 659.2, 659.2, 660.3, 668.5,
+655.4, 689.2, 679.2, 716.9, 679.9, 707.8, 684.3, 705.4, 680.6, 698.7,
+692.3, 698.0, 692.5, 699.2, 703.9, 697.2, 710.1, 708.1, 710.1, 708.1,
+716.3, 718.9, 705.5, 727.6, 697.8, 717.9, 679.2, 716.9, 679.2, 717.0,
+679.2, 717.0, 679.2, 717.0, 679.2, 717.0, 679.2, 717.0, 679.2, 716.9,
+679.2, 717.0, 679.2, 717.0, 679.2, 717.0, 679.2, 717.0, 679.2, 717.0,
+679.2, 716.9, 541.9, 459.0, 544.5, 467.9, 544.5, 467.9, 547.0, 476.8,
+546.9, 481.5, 546.7, 481.7, 547.9, 486.2, 547.8, 490.8, 547.8, 490.8,
+547.6, 495.4, 543.7, 496.9, 544.4, 498.6, 541.1, 501.8, 540.9, 501.0,
+535.4, 501.9, 529.8, 502.0, 530.2, 480.4, 530.0, 476.5, 541.9, 459.0,
+529.8, 502.0, 517.1, 502.3, 509.2, 496.3, 504.5, 502.6, 501.4, 493.6,
+495.6, 487.0, 498.4, 484.5, 499.7, 482.2, 499.7, 482.2, 501.0, 479.8,
+499.2, 474.1, 511.1, 470.5, 521.2, 461.3, 520.4, 458.7, 531.8, 457.4,
+539.5, 450.4, 540.7, 454.7, 540.7, 454.7, 541.9, 459.0, 530.0, 476.5,
+530.2, 480.4, 529.8, 502.0, 251.5, 767.1, 244.1, 776.0, 241.5, 774.8,
+236.8, 784.9, 236.2, 779.1, 236.2, 779.1, 235.6, 773.3, 240.3, 767.5,
+251.7, 769.3, 251.5, 767.1, 658.3, 591.0, 661.9, 575.7, 664.0, 575.9,
+665.5, 560.5, 669.5, 572.2, 669.5, 572.2, 673.4, 583.9, 665.6, 611.6,
+673.6, 617.0, 683.7, 646.7, 680.8, 656.7, 683.9, 659.1, 678.0, 666.8,
+674.1, 673.6, 674.1, 673.6, 670.2, 680.4, 659.0, 637.1, 660.1, 635.9,
+658.3, 591.0, 670.2, 680.4, 665.7, 688.3, 664.7, 687.9, 661.2, 696.3,
+658.3, 692.8, 658.3, 692.8, 655.4, 689.2, 660.3, 668.5, 659.2, 659.2,
+646.0, 643.2, 652.1, 617.1, 652.1, 617.1, 658.3, 591.0, 660.1, 635.9,
+659.0, 637.1, 670.2, 680.4, 713.4, 763.0, 709.0, 767.6, 709.6, 768.3,
+704.6, 772.1, 704.0, 772.6, 704.1, 772.7, 703.6, 773.3, 698.9, 778.5,
+698.7, 778.3, 693.8, 783.4, 694.9, 781.9, 689.7, 778.1, 685.5, 772.8,
+680.8, 769.9, 682.5, 767.1, 679.5, 761.5, 678.6, 759.8, 678.8, 759.8,
+678.1, 758.1, 679.6, 757.0, 678.2, 755.1, 678.3, 752.1, 696.3, 754.7,
+699.7, 752.5, 713.4, 763.0, 284.9, 700.5, 315.7, 670.8, 315.7, 670.8,
+346.4, 641.2, 344.6, 644.3, 346.8, 645.6, 347.1, 650.0, 347.1, 650.3,
+347.0, 650.3, 346.9, 650.6, 345.3, 652.8, 345.2, 652.7, 343.5, 654.8,
+339.0, 660.2, 339.0, 660.2, 334.6, 665.6, 296.6, 712.0, 305.8, 725.3,
+258.5, 758.5, 281.0, 742.7, 266.3, 725.4, 284.9, 700.5, 258.5, 758.5,
+255.0, 762.8, 255.0, 762.8, 251.5, 767.1, 251.7, 769.3, 240.3, 767.5,
+235.6, 773.3, 235.4, 771.3, 235.4, 771.3, 235.2, 769.2, 240.3, 758.5,
+238.4, 757.0, 239.0, 744.7, 262.0, 722.6, 262.0, 722.6, 284.9, 700.5,
+266.3, 725.4, 281.0, 742.7, 258.5, 758.5, 262.0, 763.8, 270.2, 752.7,
+270.2, 752.7, 278.3, 741.7, 282.8, 773.8, 273.3, 775.1, 268.2, 808.5,
+264.9, 830.4, 264.9, 830.4, 261.6, 852.3, 261.6, 852.8, 261.7, 852.9,
+261.5, 853.3, 260.7, 854.9, 259.4, 855.5, 259.8, 856.5, 255.4, 849.5,
+255.4, 849.5, 251.0, 842.4, 251.9, 803.0, 251.7, 801.8, 262.0, 763.8,
+251.0, 842.4, 247.3, 836.7, 244.0, 836.8, 243.7, 830.9, 241.5, 818.6,
+234.4, 814.0, 239.4, 806.4, 240.0, 804.4, 239.0, 804.0, 238.5, 801.7,
+238.0, 799.0, 238.3, 799.0, 238.0, 796.2, 253.0, 783.6, 250.0, 780.0,
+262.0, 763.8, 251.7, 801.8, 251.9, 803.0, 251.0, 842.4, 719.8, 756.4,
+716.6, 759.7, 716.6, 759.7, 713.4, 763.0, 699.7, 752.5, 696.3, 754.7,
+678.3, 752.1, 678.7, 734.6, 670.4, 723.5, 679.2, 717.0, 679.2, 717.0,
+679.2, 717.0, 679.2, 716.9, 697.8, 717.9, 705.5, 727.6, 716.3, 718.9,
+717.3, 720.6, 717.3, 720.6, 718.2, 722.2, 719.8, 739.2, 721.7, 739.6,
+719.8, 756.4, 718.2, 722.2, 724.7, 733.4, 721.4, 738.3, 731.1, 744.7,
+725.4, 750.5, 725.4, 750.5, 719.8, 756.4, 721.7, 739.6, 719.8, 739.2,
+718.2, 722.2, 333.7, 666.8, 334.2, 666.2, 334.6, 665.6, 334.6, 665.6,
+339.0, 660.2, 339.0, 660.2, 343.5, 654.8, 342.0, 665.7, 338.4, 665.2,
+333.3, 675.6, 324.0, 694.6, 315.7, 694.2, 314.7, 713.7, 315.9, 689.8,
+324.0, 690.2, 333.7, 666.8, 314.7, 713.7, 292.5, 761.6, 305.7, 779.6,
+268.2, 808.5, 273.3, 775.1, 282.8, 773.8, 278.3, 741.7, 306.0, 704.2,
+306.0, 704.2, 333.7, 666.8, 324.0, 690.2, 315.9, 689.8, 314.7, 713.7,
+333.3, 675.6, 338.4, 665.2, 342.0, 665.7, 343.5, 654.8, 345.2, 652.7,
+345.3, 652.8, 346.9, 650.6, 335.4, 692.2, 334.6, 692.0, 324.0, 733.9,
+298.0, 795.3, 292.6, 793.0, 261.6, 852.3, 261.6, 852.8, 261.7, 852.9,
+261.5, 853.3, 261.7, 852.9, 261.6, 852.8, 261.6, 852.3, 297.4, 763.9,
+296.8, 763.7, 333.3, 675.6, 261.6, 852.3, 264.9, 830.4, 264.9, 830.4,
+268.2, 808.5, 305.7, 779.6, 292.5, 761.6, 314.7, 713.7, 315.7, 694.2,
+324.0, 694.6, 333.3, 675.6, 296.8, 763.7, 297.4, 763.9, 261.6, 852.3
+};
diff --git a/perf/micro/paint-with-alpha.c b/perf/micro/paint-with-alpha.c
new file mode 100644 (file)
index 0000000..047e35c
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * Copyright © 2006 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ */
+
+#include "cairo-perf.h"
+
+static cairo_time_t
+do_paint_with_alpha (cairo_t *cr, int width, int height, int loops)
+{
+    cairo_perf_timer_start ();
+
+    while (loops--)
+       cairo_paint_with_alpha (cr, 0.5);
+
+    cairo_perf_timer_stop ();
+
+    return cairo_perf_timer_elapsed ();
+}
+
+static double
+count_paint_with_alpha (cairo_t *cr, int width, int height)
+{
+    return width * height / 1e6; /* Mpix/s */
+}
+
+cairo_bool_t
+paint_with_alpha_enabled (cairo_perf_t *perf)
+{
+    return cairo_perf_can_run (perf, "paint-with-alpha", NULL);
+}
+
+void
+paint_with_alpha (cairo_perf_t *perf, cairo_t *cr, int width, int height)
+{
+    cairo_perf_cover_sources_and_operators (perf, "paint-with-alpha",
+                                           do_paint_with_alpha,
+                                           count_paint_with_alpha);
+}
diff --git a/perf/micro/paint.c b/perf/micro/paint.c
new file mode 100644 (file)
index 0000000..2a59a45
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Copyright © 2006 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ */
+
+#include "cairo-perf.h"
+
+static cairo_time_t
+do_paint (cairo_t *cr, int width, int height, int loops)
+{
+    cairo_perf_timer_start ();
+
+    while (loops--)
+       cairo_paint (cr);
+
+    cairo_perf_timer_stop ();
+
+    return cairo_perf_timer_elapsed ();
+}
+
+static double
+count_paint (cairo_t *cr, int width, int height)
+{
+    return width * height / 1e6; /* Mpix/s */
+}
+
+cairo_bool_t
+paint_enabled (cairo_perf_t *perf)
+{
+    return cairo_perf_can_run (perf, "paint", NULL);
+}
+
+void
+paint (cairo_perf_t *perf, cairo_t *cr, int width, int height)
+{
+    cairo_perf_cover_sources_and_operators (perf, "paint", do_paint, count_paint);
+}
diff --git a/perf/micro/pattern_create_radial.c b/perf/micro/pattern_create_radial.c
new file mode 100644 (file)
index 0000000..f236ef5
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ * Copyright © 2006 Dan Amelang
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * the authors not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. The authors make no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE AUTHORS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Authors: Dan Amelang <dan@amelang.net>
+ *
+ * This test was originally created to test _cairo_fixed_from_double.
+ * cairo_pattern_create_radial was selected as the entry point into
+ * cairo as it makes several calls to _cairo_fixed_from_double and
+ * presents a somewhat realistic use-case (although the RADIALS_COUNT
+ * isn't very realistic).
+ */
+#include <time.h>
+#include "cairo-perf.h"
+
+#define RADIALS_COUNT (10000)
+
+static struct
+{
+    double cx0;
+    double cy0;
+    double radius0;
+    double cx1;
+    double cy1;
+    double radius1;
+} radials[RADIALS_COUNT];
+
+static double
+generate_double_in_range (double min, double max)
+{
+    double d;
+
+    d = rand () / (double) RAND_MAX;
+    d *= max - min;
+    d += min;
+
+    return d;
+}
+
+static cairo_time_t
+do_pattern_create_radial (cairo_t *cr, int width, int height, int loops)
+{
+    cairo_perf_timer_start ();
+
+    while (loops--) {
+       cairo_pattern_t *pattern;
+       int i;
+
+       for (i = 0; i < RADIALS_COUNT; i++) {
+           pattern =
+               cairo_pattern_create_radial (radials[i].cx0, radials[i].cy0,
+                                            radials[i].radius0,
+                                            radials[i].cx1, radials[i].cy1,
+                                            radials[i].radius1);
+           cairo_pattern_destroy (pattern);
+       }
+    }
+
+    cairo_perf_timer_stop ();
+
+    return cairo_perf_timer_elapsed ();
+}
+
+cairo_bool_t
+pattern_create_radial_enabled (cairo_perf_t *perf)
+{
+    return cairo_perf_can_run (perf, "pattern-create-radial", NULL);
+}
+
+void
+pattern_create_radial (cairo_perf_t *perf, cairo_t *cr, int width, int height)
+{
+    int i;
+
+    srand (time (0));
+    for (i = 0; i < RADIALS_COUNT; i++)
+    {
+        radials[i].cx0 = generate_double_in_range (-50000.0, 50000.0);
+        radials[i].cy0 = generate_double_in_range (-50000.0, 50000.0);
+        radials[i].radius0 = generate_double_in_range (0.0, 1000.0);
+        radials[i].cx1 = generate_double_in_range (-50000.0, 50000.0);
+        radials[i].cy1 = generate_double_in_range (-50000.0, 50000.0);
+        radials[i].radius1 = generate_double_in_range (0.0, 1000.0);
+    }
+
+    cairo_perf_run (perf, "pattern-create-radial",
+                          do_pattern_create_radial, NULL);
+}
diff --git a/perf/micro/pixel.c b/perf/micro/pixel.c
new file mode 100644 (file)
index 0000000..b600b51
--- /dev/null
@@ -0,0 +1,237 @@
+/*
+ * Copyright © 2011 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+/* Measure the overhead in setting a single pixel */
+
+#include "cairo-perf.h"
+
+#include <pixman.h>
+
+static cairo_time_t
+pixel_direct (cairo_t *cr, int width, int height, int loops)
+{
+    cairo_surface_t *surface, *image;
+    uint32_t *data;
+    int stride, bpp;
+
+    surface = cairo_get_target (cr);
+    image = cairo_surface_map_to_image (surface, NULL);
+    data = (uint32_t *) cairo_image_surface_get_data (image);
+    stride = cairo_image_surface_get_stride (image) / sizeof (uint32_t);
+
+    switch (cairo_image_surface_get_format (image)) {
+    default:
+    case CAIRO_FORMAT_INVALID:
+    case CAIRO_FORMAT_A1: bpp = 0; break;
+    case CAIRO_FORMAT_A8: bpp = 8; break;
+    case CAIRO_FORMAT_RGB16_565: bpp = 16; break;
+    case CAIRO_FORMAT_RGB24:
+    case CAIRO_FORMAT_RGB30:
+    case CAIRO_FORMAT_ARGB32: bpp = 32; break;
+    }
+
+    cairo_perf_timer_start ();
+
+    while (loops--)
+       pixman_fill (data, stride, bpp, 0, 0, 1, 1, -1);
+
+    cairo_perf_timer_stop ();
+
+    cairo_surface_unmap_image (surface, image);
+
+    return cairo_perf_timer_elapsed ();
+}
+
+static cairo_time_t
+pixel_paint (cairo_t *cr, int width, int height, int loops)
+{
+    cairo_perf_timer_start ();
+
+    while (loops--)
+       cairo_paint (cr);
+
+    cairo_perf_timer_stop ();
+
+    return cairo_perf_timer_elapsed ();
+}
+
+static cairo_time_t
+pixel_mask (cairo_t *cr, int width, int height, int loops)
+{
+    cairo_surface_t *mask;
+    cairo_t *cr2;
+
+    mask = cairo_surface_create_similar (cairo_get_target (cr),
+                                        CAIRO_CONTENT_ALPHA,
+                                        1, 1);
+    cr2 = cairo_create (mask);
+    cairo_set_source_rgb (cr2, 1,1,1);
+    cairo_paint (cr2);
+    cairo_destroy (cr2);
+
+    cairo_perf_timer_start ();
+
+    while (loops--)
+       cairo_mask_surface (cr, mask, 0, 0);
+
+    cairo_perf_timer_stop ();
+
+    cairo_surface_destroy (mask);
+
+    return cairo_perf_timer_elapsed ();
+}
+
+static cairo_time_t
+pixel_rectangle (cairo_t *cr, int width, int height, int loops)
+{
+    cairo_new_path (cr);
+    cairo_rectangle (cr, 0, 0, 1, 1);
+
+    cairo_perf_timer_start ();
+
+    while (loops--)
+       cairo_fill_preserve (cr);
+
+    cairo_perf_timer_stop ();
+
+    cairo_new_path (cr);
+    return cairo_perf_timer_elapsed ();
+}
+
+static cairo_time_t
+pixel_subrectangle (cairo_t *cr, int width, int height, int loops)
+{
+    cairo_new_path (cr);
+    cairo_rectangle (cr, 0.1, 0.1, .8, .8);
+
+    cairo_perf_timer_start ();
+
+    while (loops--)
+       cairo_fill_preserve (cr);
+
+    cairo_perf_timer_stop ();
+
+    cairo_new_path (cr);
+    return cairo_perf_timer_elapsed ();
+}
+
+static cairo_time_t
+pixel_triangle (cairo_t *cr, int width, int height, int loops)
+{
+    cairo_new_path (cr);
+    cairo_move_to (cr, 0, 0);
+    cairo_line_to (cr, 1, 1);
+    cairo_line_to (cr, 0, 1);
+
+    cairo_perf_timer_start ();
+
+    while (loops--)
+       cairo_fill_preserve (cr);
+
+    cairo_perf_timer_stop ();
+
+    cairo_new_path (cr);
+    return cairo_perf_timer_elapsed ();
+}
+
+static cairo_time_t
+pixel_circle (cairo_t *cr, int width, int height, int loops)
+{
+    cairo_new_path (cr);
+    cairo_arc (cr, 0.5, 0.5, 0.5, 0, 2 * M_PI);
+
+    cairo_perf_timer_start ();
+
+    while (loops--)
+       cairo_fill_preserve (cr);
+
+    cairo_perf_timer_stop ();
+
+    cairo_new_path (cr);
+    return cairo_perf_timer_elapsed ();
+}
+
+static cairo_time_t
+pixel_stroke (cairo_t *cr, int width, int height, int loops)
+{
+    cairo_set_line_width (cr, 1.);
+    cairo_new_path (cr);
+    cairo_move_to (cr, 0, 0.5);
+    cairo_line_to (cr, 1, 0.5);
+
+    cairo_perf_timer_start ();
+
+    while (loops--)
+       cairo_stroke_preserve (cr);
+
+    cairo_perf_timer_stop ();
+
+    cairo_new_path (cr);
+    return cairo_perf_timer_elapsed ();
+}
+
+cairo_bool_t
+pixel_enabled (cairo_perf_t *perf)
+{
+    return cairo_perf_can_run (perf, "pixel", NULL);
+}
+
+void
+pixel (cairo_perf_t *perf, cairo_t *cr, int width, int height)
+{
+    cairo_set_source_rgb (cr, 1., 1., 1.);
+
+    cairo_perf_run (perf, "pixel-direct", pixel_direct, NULL);
+    cairo_perf_run (perf, "pixel-paint", pixel_paint, NULL);
+    cairo_perf_run (perf, "pixel-mask", pixel_mask, NULL);
+    cairo_perf_run (perf, "pixel-rectangle", pixel_rectangle, NULL);
+    cairo_perf_run (perf, "pixel-subrectangle", pixel_subrectangle, NULL);
+    cairo_perf_run (perf, "pixel-triangle", pixel_triangle, NULL);
+    cairo_perf_run (perf, "pixel-circle", pixel_circle, NULL);
+    cairo_perf_run (perf, "pixel-stroke", pixel_stroke, NULL);
+}
+
+cairo_bool_t
+a1_pixel_enabled (cairo_perf_t *perf)
+{
+    return cairo_perf_can_run (perf, "a1-pixel", NULL);
+}
+
+void
+a1_pixel (cairo_perf_t *perf, cairo_t *cr, int width, int height)
+{
+    cairo_set_source_rgb (cr, 1., 1., 1.);
+    cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
+
+    cairo_perf_run (perf, "a1-pixel-direct", pixel_direct, NULL);
+    cairo_perf_run (perf, "a1-pixel-paint", pixel_paint, NULL);
+    cairo_perf_run (perf, "a1-pixel-mask", pixel_mask, NULL);
+    cairo_perf_run (perf, "a1-pixel-rectangle", pixel_rectangle, NULL);
+    cairo_perf_run (perf, "a1-pixel-subrectangle", pixel_subrectangle, NULL);
+    cairo_perf_run (perf, "a1-pixel-triangle", pixel_triangle, NULL);
+    cairo_perf_run (perf, "a1-pixel-circle", pixel_circle, NULL);
+    cairo_perf_run (perf, "a1-pixel-stroke", pixel_stroke, NULL);
+}
diff --git a/perf/micro/pythagoras-tree.c b/perf/micro/pythagoras-tree.c
new file mode 100644 (file)
index 0000000..9d3ca11
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * Copyright © 2007 Chris Wilson
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Chris Wilson not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Chris Wilson makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * CHRIS WILSON DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL CHRIS WILSON BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-perf.h"
+#define _USE_MATH_DEFINES /* for M_SQRT2 on win32 */
+#include <math.h>
+
+static void
+add_rectangle (cairo_t *cr, double size)
+{
+    double x, y;
+
+    if (size < 1)
+       return;
+
+    cairo_get_current_point (cr, &x, &y);
+
+    cairo_rel_move_to (cr, -size/2., -size/2.);
+    cairo_rel_line_to (cr, size, 0);
+    cairo_rel_line_to (cr, 0, size);
+    cairo_rel_line_to (cr, -size, 0);
+    cairo_close_path (cr);
+
+    cairo_save (cr);
+    cairo_translate (cr, -size/2., size);
+    cairo_move_to (cr, x, y);
+    cairo_rotate (cr, M_PI/4);
+    add_rectangle (cr, size / M_SQRT2);
+    cairo_restore (cr);
+
+    cairo_save (cr);
+    cairo_translate (cr, size/2., size);
+    cairo_move_to (cr, x, y);
+    cairo_rotate (cr, -M_PI/4);
+    add_rectangle (cr, size / M_SQRT2);
+    cairo_restore (cr);
+}
+
+static cairo_time_t
+do_pythagoras_tree (cairo_t *cr, int width, int height, int loops)
+{
+    double size = 128;
+
+    cairo_perf_timer_start ();
+
+    while (loops--) {
+       cairo_save (cr);
+       cairo_translate (cr, 0, height);
+       cairo_scale (cr, 1, -1);
+
+       cairo_move_to (cr, width/2, size/2);
+       add_rectangle (cr, size);
+       cairo_set_source_rgb (cr, 0., 0., 0.);
+       cairo_fill (cr);
+       cairo_restore (cr);
+    }
+
+    cairo_perf_timer_stop ();
+
+    return cairo_perf_timer_elapsed ();
+}
+
+cairo_bool_t
+pythagoras_tree_enabled (cairo_perf_t *perf)
+{
+    return cairo_perf_can_run (perf, "pythagoras-tree", NULL);
+}
+
+void
+pythagoras_tree (cairo_perf_t *perf, cairo_t *cr, int width, int height)
+{
+    cairo_perf_run (perf, "pythagoras-tree", do_pythagoras_tree, NULL);
+}
diff --git a/perf/micro/rectangles.c b/perf/micro/rectangles.c
new file mode 100644 (file)
index 0000000..9228a4e
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+ * Copyright © 2006 Dan Amelang
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * the authors not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. The authors make no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE AUTHORS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Authors: Dan Amelang <dan@amelang.net>
+ */
+#include "cairo-perf.h"
+
+#if 0
+#define MODE cairo_perf_run
+#else
+#define MODE cairo_perf_cover_sources_and_operators
+#endif
+
+#define RECTANGLE_COUNT (1000)
+
+static struct {
+    double x;
+    double y;
+    double width;
+    double height;
+} rects[RECTANGLE_COUNT];
+
+static cairo_time_t
+do_rectangles (cairo_t *cr, int width, int height, int loops)
+{
+    int i;
+
+    cairo_perf_timer_start ();
+
+    while (loops--) {
+       for (i = 0; i < RECTANGLE_COUNT; i++) {
+           cairo_rectangle (cr, rects[i].x, rects[i].y,
+                            rects[i].width, rects[i].height);
+           cairo_fill (cr);
+       }
+    }
+
+    cairo_perf_timer_stop ();
+
+    return cairo_perf_timer_elapsed ();
+}
+
+static cairo_time_t
+do_rectangles_once (cairo_t *cr, int width, int height, int loops)
+{
+    int i;
+
+    cairo_perf_timer_start ();
+
+    while (loops--) {
+       for (i = 0; i < RECTANGLE_COUNT; i++) {
+           cairo_rectangle (cr, rects[i].x, rects[i].y,
+                            rects[i].width, rects[i].height);
+       }
+
+       cairo_fill (cr);
+    }
+
+    cairo_perf_timer_stop ();
+
+    return cairo_perf_timer_elapsed ();
+}
+
+static cairo_time_t
+do_rectangle (cairo_t *cr, int width, int height, int loops)
+{
+    cairo_perf_timer_start ();
+
+    while (loops--) {
+       cairo_rectangle (cr, 0, 0, width, height);
+       cairo_fill (cr);
+    }
+
+    cairo_perf_timer_stop ();
+
+    return cairo_perf_timer_elapsed ();
+}
+
+cairo_bool_t
+rectangles_enabled (cairo_perf_t *perf)
+{
+    return cairo_perf_can_run (perf, "rectangles", NULL);
+}
+
+void
+rectangles (cairo_perf_t *perf, cairo_t *cr, int width, int height)
+{
+    int i;
+
+    srand (8478232);
+    for (i = 0; i < RECTANGLE_COUNT; i++)
+    {
+        rects[i].x = rand () % width;
+        rects[i].y = rand () % height;
+        rects[i].width  = (rand () % (width / 10)) + 1;
+        rects[i].height = (rand () % (height / 10)) + 1;
+    }
+
+    MODE (perf, "one-rectangle", do_rectangle, NULL);
+    MODE (perf, "rectangles", do_rectangles, NULL);
+    MODE (perf, "rectangles-once", do_rectangles_once, NULL);
+}
diff --git a/perf/micro/rounded-rectangles.c b/perf/micro/rounded-rectangles.c
new file mode 100644 (file)
index 0000000..1e432dd
--- /dev/null
@@ -0,0 +1,144 @@
+/*
+ * Copyright © 2005 Owen Taylor
+ * Copyright © 2007 Dan Amelang
+ * Copyright © 2007 Chris Wilson
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * the authors not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. The authors make no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE AUTHORS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Authors: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+/* This perf case is derived from the bug report
+ *   Gradient on 'rounded rectangle' MUCH slower than normal rectangle
+ *   https://bugs.freedesktop.org/show_bug.cgi?id=4263.
+ */
+
+#include "cairo-perf.h"
+
+#define RECTANGLE_COUNT (1000)
+
+#if 0
+#define MODE cairo_perf_run
+#else
+#define MODE cairo_perf_cover_sources_and_operators
+#endif
+
+static struct
+{
+    double x;
+    double y;
+    double width;
+    double height;
+} rects[RECTANGLE_COUNT];
+
+static void
+rounded_rectangle (cairo_t *cr,
+                  double x, double y, double w, double h,
+                  double radius)
+{
+    cairo_move_to (cr, x+radius, y);
+    cairo_arc (cr, x+w-radius, y+radius,   radius, M_PI + M_PI / 2, M_PI * 2        );
+    cairo_arc (cr, x+w-radius, y+h-radius, radius, 0,               M_PI / 2        );
+    cairo_arc (cr, x+radius,   y+h-radius, radius, M_PI/2,          M_PI            );
+    cairo_arc (cr, x+radius,   y+radius,   radius, M_PI,            270 * M_PI / 180);
+}
+
+static cairo_time_t
+do_rectangle (cairo_t *cr, int width, int height, int loops)
+{
+    cairo_perf_timer_start ();
+
+    while (loops--) {
+       rounded_rectangle (cr, 0, 0, width, height, 3.0);
+       cairo_fill (cr);
+    }
+
+    cairo_perf_timer_stop ();
+
+    return cairo_perf_timer_elapsed ();
+}
+
+static cairo_time_t
+do_rectangles (cairo_t *cr, int width, int height, int loops)
+{
+    int i;
+
+    cairo_perf_timer_start ();
+
+    while (loops--) {
+       for (i = 0; i < RECTANGLE_COUNT; i++) {
+           rounded_rectangle (cr,
+                              rects[i].x, rects[i].y,
+                              rects[i].width, rects[i].height,
+                              3.0);
+           cairo_fill (cr);
+       }
+    }
+
+    cairo_perf_timer_stop ();
+
+    return cairo_perf_timer_elapsed ();
+}
+
+static cairo_time_t
+do_rectangles_once (cairo_t *cr, int width, int height, int loops)
+{
+    int i;
+
+    cairo_perf_timer_start ();
+
+    while (loops--) {
+       for (i = 0; i < RECTANGLE_COUNT; i++) {
+           rounded_rectangle (cr,
+                              rects[i].x, rects[i].y,
+                              rects[i].width, rects[i].height,
+                              3.0);
+       }
+       cairo_fill (cr);
+    }
+
+    cairo_perf_timer_stop ();
+
+    return cairo_perf_timer_elapsed ();
+}
+
+cairo_bool_t
+rounded_rectangles_enabled (cairo_perf_t *perf)
+{
+    return cairo_perf_can_run (perf, "rounded-rectangles", NULL);
+}
+
+void
+rounded_rectangles (cairo_perf_t *perf, cairo_t *cr, int width, int height)
+{
+    int i;
+
+    srand (8478232);
+    for (i = 0; i < RECTANGLE_COUNT; i++) {
+        rects[i].x = rand () % width;
+        rects[i].y = rand () % height;
+        rects[i].width  = (rand () % (width / 10)) + 1;
+        rects[i].height = (rand () % (height / 10)) + 1;
+    }
+
+    MODE (perf, "one-rounded-rectangle", do_rectangle, NULL);
+    MODE (perf, "rounded-rectangles", do_rectangles, NULL);
+    MODE (perf, "rounded-rectangles-once", do_rectangles_once, NULL);
+}
diff --git a/perf/micro/sierpinski.c b/perf/micro/sierpinski.c
new file mode 100644 (file)
index 0000000..c6f5fad
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * Copyright © 2011 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-perf.h"
+
+static const double m_1_sqrt_3 = 0.577359269;
+
+static void
+T (cairo_t *cr, int size)
+{
+    cairo_move_to (cr, 0, 0);
+    cairo_line_to (cr, size, 0);
+    cairo_line_to (cr, size/2, size*m_1_sqrt_3);
+
+    size /= 2;
+    if (size >= 4) {
+       T (cr, size);
+       cairo_save (cr); {
+           cairo_translate (cr, size, 0);
+           T (cr, size);
+       } cairo_restore (cr);
+       cairo_save (cr); {
+           cairo_translate (cr, size/2, size*m_1_sqrt_3);
+           T (cr, size);
+       } cairo_restore (cr);
+    }
+}
+
+static cairo_time_t
+draw (cairo_t *cr, int width, int height, int loops)
+{
+    int t_height = height/2;
+    int t_width = t_height / m_1_sqrt_3;
+
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_paint (cr);
+
+    cairo_set_source_rgb (cr, 0, 0, 0);
+    cairo_set_line_width (cr, 1.);
+
+    cairo_perf_timer_start ();
+
+    while (loops--) {
+       cairo_save (cr);
+       T (cr, t_width);
+
+       cairo_translate (cr, 0, height);
+       cairo_scale (cr, 1, -1);
+
+       T (cr, t_width);
+
+       cairo_stroke (cr);
+       cairo_restore (cr);
+    }
+
+    cairo_perf_timer_stop ();
+
+    return cairo_perf_timer_elapsed ();
+}
+
+cairo_bool_t
+sierpinski_enabled (cairo_perf_t *perf)
+{
+    return cairo_perf_can_run (perf, "sierpinski", NULL);
+}
+
+void
+sierpinski (cairo_perf_t *perf, cairo_t *cr, int width, int height)
+{
+    cairo_perf_run (perf, "sierpinski", draw, NULL);
+}
diff --git a/perf/micro/spiral.c b/perf/micro/spiral.c
new file mode 100644 (file)
index 0000000..87dbcb5
--- /dev/null
@@ -0,0 +1,352 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright (c) 2008  M Joonas Pihlaja
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include <assert.h>
+#include "cairo-perf.h"
+
+#define MAX_SEGMENTS 2560
+
+typedef enum {
+    PIXALIGN,                   /* pixel aligned path */
+    NONALIGN                    /* unaligned path. */
+} align_t;
+
+typedef enum {
+    RECTCLOSE,                  /* keeps the path rectilinear */
+    DIAGCLOSE                   /* forces a diagonal */
+} close_t;
+
+static cairo_time_t
+draw_spiral (cairo_t *cr,
+             cairo_fill_rule_t fill_rule,
+             align_t align,
+             close_t close,
+             int width, int height, int loops)
+{
+    int i;
+    int n=0;
+    double x[MAX_SEGMENTS];
+    double y[MAX_SEGMENTS];
+    int step = 3;
+    int side = width < height ? width : height;
+
+    assert(5*(side/step/2+1)+2 < MAX_SEGMENTS);
+
+#define L(x_,y_) (x[n] = (x_), y[n] = (y_), n++)
+#define M(x_,y_) L(x_,y_)
+#define v(t) L(x[n-1], y[n-1] + (t))
+#define h(t) L(x[n-1] + (t), y[n-1])
+
+    switch (align) {
+    case PIXALIGN: M(0,0); break;
+    case NONALIGN: M(0.1415926, 0.7182818); break;
+    }
+
+    while (side >= step && side >= 0) {
+        v(side);
+        h(side);
+        v(-side);
+        h(-side+step);
+        v(step);
+        side -= 2*step;
+    }
+
+    switch (close) {
+    case RECTCLOSE: L(x[n-1],y[0]); break;
+    case DIAGCLOSE: L(x[0],y[0]); break;
+    }
+
+    assert(n < MAX_SEGMENTS);
+
+    cairo_save (cr);
+    cairo_set_source_rgb (cr, 0, 0, 0);
+    cairo_paint (cr);
+
+    cairo_translate (cr, 1, 1);
+    cairo_set_fill_rule (cr, fill_rule);
+    cairo_set_source_rgb (cr, 1, 0, 0);
+
+    cairo_new_path (cr);
+    cairo_move_to (cr, x[0], y[0]);
+    for (i = 1; i < n; i++) {
+       cairo_line_to (cr, x[i], y[i]);
+    }
+    cairo_close_path (cr);
+
+    cairo_perf_timer_start ();
+    while (loops--)
+        cairo_fill_preserve (cr);
+    cairo_perf_timer_stop ();
+
+    cairo_restore (cr);
+
+    return cairo_perf_timer_elapsed ();
+}
+
+static cairo_time_t
+draw_spiral_box (cairo_t *cr,
+                cairo_fill_rule_t fill_rule,
+                align_t align,
+                int width, int height, int loops)
+{
+    const int step = 3;
+    int side = (width < height ? width : height) - 2;
+
+    cairo_save (cr);
+    cairo_set_source_rgb (cr, 0, 0, 0);
+    cairo_paint (cr);
+
+    cairo_set_source_rgb (cr, 1, 0, 0);
+    cairo_set_fill_rule (cr, fill_rule);
+    cairo_translate (cr, 1, 1);
+    if (align == NONALIGN)
+       cairo_translate (cr, 0.1415926, 0.7182818);
+
+    cairo_new_path (cr);
+    while (side >= step) {
+       cairo_rectangle (cr, 0, 0, side, side);
+       cairo_translate (cr, step, step);
+       side -= 2*step;
+    }
+
+    cairo_perf_timer_start ();
+    while (loops--)
+        cairo_fill_preserve (cr);
+    cairo_perf_timer_stop ();
+
+    cairo_restore (cr);
+
+    return cairo_perf_timer_elapsed ();
+}
+
+static cairo_time_t
+draw_spiral_stroke (cairo_t *cr,
+                   align_t align,
+                   int width, int height, int loops)
+{
+    const int step = 3;
+    int side = width < height ? width : height;
+
+    cairo_save (cr);
+    cairo_set_source_rgb (cr, 0, 0, 0);
+    cairo_paint (cr);
+
+    cairo_translate (cr, 1, 1);
+    cairo_set_source_rgb (cr, 1, 0, 0);
+    cairo_set_line_width (cr, 4.);
+    cairo_set_line_join (cr, CAIRO_LINE_JOIN_MITER);
+    cairo_set_line_cap (cr, CAIRO_LINE_CAP_BUTT);
+
+    cairo_new_path (cr);
+    switch (align) {
+    case PIXALIGN: cairo_move_to (cr, 0,0); break;
+    case NONALIGN: cairo_move_to (cr, 0.1415926, 0.7182818); break;
+    }
+    while (side >= step) {
+       cairo_rel_line_to (cr, 0, side);
+        side -= step;
+       if (side <= 0)
+           break;
+
+       cairo_rel_line_to (cr, side, 0);
+        side -= step;
+       if (side <= 0)
+           break;
+
+       cairo_rel_line_to (cr, 0, -side);
+        side -= step;
+       if (side <= 0)
+           break;
+
+       cairo_rel_line_to (cr, -side, 0);
+        side -= step;
+       if (side <= 0)
+           break;
+    }
+
+    cairo_perf_timer_start ();
+    while (loops--)
+        cairo_stroke_preserve (cr);
+    cairo_perf_timer_stop ();
+
+    cairo_restore (cr);
+
+    return cairo_perf_timer_elapsed ();
+}
+
+static cairo_time_t
+draw_spiral_eo_pa_re (cairo_t *cr, int width, int height, int loops)
+{
+    return draw_spiral (cr,
+                        CAIRO_FILL_RULE_EVEN_ODD,
+                        PIXALIGN,
+                        RECTCLOSE,
+                        width, height, loops);
+}
+
+static cairo_time_t
+draw_spiral_nz_pa_re (cairo_t *cr, int width, int height, int loops)
+{
+    return draw_spiral (cr,
+                        CAIRO_FILL_RULE_WINDING,
+                        PIXALIGN,
+                        RECTCLOSE,
+                        width, height, loops);
+}
+
+static cairo_time_t
+draw_spiral_eo_na_re (cairo_t *cr, int width, int height, int loops)
+{
+    return draw_spiral (cr,
+                        CAIRO_FILL_RULE_EVEN_ODD,
+                        NONALIGN,
+                        RECTCLOSE,
+                        width, height, loops);
+}
+
+static cairo_time_t
+draw_spiral_nz_na_re (cairo_t *cr, int width, int height, int loops)
+{
+    return draw_spiral (cr,
+                        CAIRO_FILL_RULE_WINDING,
+                        NONALIGN,
+                        RECTCLOSE,
+                        width, height, loops);
+}
+
+static cairo_time_t
+draw_spiral_eo_pa_di (cairo_t *cr, int width, int height, int loops)
+{
+    return draw_spiral (cr,
+                        CAIRO_FILL_RULE_EVEN_ODD,
+                        PIXALIGN,
+                        DIAGCLOSE,
+                        width, height, loops);
+}
+
+static cairo_time_t
+draw_spiral_nz_pa_di (cairo_t *cr, int width, int height, int loops)
+{
+    return draw_spiral (cr,
+                        CAIRO_FILL_RULE_WINDING,
+                        PIXALIGN,
+                        DIAGCLOSE,
+                        width, height, loops);
+}
+
+static cairo_time_t
+draw_spiral_eo_na_di (cairo_t *cr, int width, int height, int loops)
+{
+    return draw_spiral (cr,
+                        CAIRO_FILL_RULE_EVEN_ODD,
+                        NONALIGN,
+                        DIAGCLOSE,
+                        width, height, loops);
+}
+
+static cairo_time_t
+draw_spiral_nz_na_di (cairo_t *cr, int width, int height, int loops)
+{
+    return draw_spiral (cr,
+                        CAIRO_FILL_RULE_WINDING,
+                        NONALIGN,
+                        DIAGCLOSE,
+                        width, height, loops);
+}
+
+static cairo_time_t
+draw_spiral_nz_pa_box (cairo_t *cr, int width, int height, int loops)
+{
+    return draw_spiral_box (cr,
+                           CAIRO_FILL_RULE_WINDING, PIXALIGN,
+                           width, height, loops);
+}
+
+static cairo_time_t
+draw_spiral_nz_na_box (cairo_t *cr, int width, int height, int loops)
+{
+    return draw_spiral_box (cr,
+                           CAIRO_FILL_RULE_WINDING, NONALIGN,
+                           width, height, loops);
+}
+
+
+static cairo_time_t
+draw_spiral_eo_pa_box (cairo_t *cr, int width, int height, int loops)
+{
+    return draw_spiral_box (cr,
+                           CAIRO_FILL_RULE_EVEN_ODD, PIXALIGN,
+                           width, height, loops);
+}
+
+static cairo_time_t
+draw_spiral_eo_na_box (cairo_t *cr, int width, int height, int loops)
+{
+    return draw_spiral_box (cr,
+                           CAIRO_FILL_RULE_EVEN_ODD, NONALIGN,
+                           width, height, loops);
+}
+
+static cairo_time_t
+draw_spiral_stroke_pa (cairo_t *cr, int width, int height, int loops)
+{
+    return draw_spiral_stroke (cr,
+                              PIXALIGN,
+                              width, height, loops);
+}
+
+static cairo_time_t
+draw_spiral_stroke_na (cairo_t *cr, int width, int height, int loops)
+{
+    return draw_spiral_stroke (cr,
+                              NONALIGN,
+                              width, height, loops);
+}
+
+cairo_bool_t
+spiral_enabled (cairo_perf_t *perf)
+{
+    return cairo_perf_can_run (perf, "spiral", NULL);
+}
+
+void
+spiral (cairo_perf_t *perf, cairo_t *cr, int width, int height)
+{
+    cairo_perf_run (perf, "spiral-box-nonalign-evenodd-fill", draw_spiral_eo_na_box, NULL);
+    cairo_perf_run (perf, "spiral-box-nonalign-nonzero-fill", draw_spiral_nz_na_box, NULL);
+    cairo_perf_run (perf, "spiral-box-pixalign-evenodd-fill", draw_spiral_eo_pa_box, NULL);
+    cairo_perf_run (perf, "spiral-box-pixalign-nonzero-fill", draw_spiral_nz_pa_box, NULL);
+    cairo_perf_run (perf, "spiral-diag-nonalign-evenodd-fill", draw_spiral_eo_na_di, NULL);
+    cairo_perf_run (perf, "spiral-diag-nonalign-nonzero-fill", draw_spiral_nz_na_di, NULL);
+    cairo_perf_run (perf, "spiral-diag-pixalign-evenodd-fill", draw_spiral_eo_pa_di, NULL);
+    cairo_perf_run (perf, "spiral-diag-pixalign-nonzero-fill", draw_spiral_nz_pa_di, NULL);
+    cairo_perf_run (perf, "spiral-rect-nonalign-evenodd-fill", draw_spiral_eo_na_re, NULL);
+    cairo_perf_run (perf, "spiral-rect-nonalign-nonzero-fill", draw_spiral_nz_na_re, NULL);
+    cairo_perf_run (perf, "spiral-rect-pixalign-evenodd-fill", draw_spiral_eo_pa_re, NULL);
+    cairo_perf_run (perf, "spiral-rect-pixalign-nonzero-fill", draw_spiral_nz_pa_re, NULL);
+    cairo_perf_run (perf, "spiral-nonalign-stroke", draw_spiral_stroke_na, NULL);
+    cairo_perf_run (perf, "spiral-pixalign-stroke", draw_spiral_stroke_pa, NULL);
+}
diff --git a/perf/micro/stroke.c b/perf/micro/stroke.c
new file mode 100644 (file)
index 0000000..4b29547
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * Copyright © 2006 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ */
+
+#include "cairo-perf.h"
+
+static cairo_time_t
+do_stroke (cairo_t *cr, int width, int height, int loops)
+{
+    cairo_arc (cr,
+              width/2.0, height/2.0,
+              width/3.0,
+              0, 2 * M_PI);
+    cairo_close_path (cr);
+
+    cairo_set_line_width (cr, width/5.0);
+
+    cairo_perf_timer_start ();
+
+    while (loops--)
+       cairo_stroke_preserve (cr);
+
+    cairo_perf_timer_stop ();
+
+    cairo_new_path (cr);
+
+    return cairo_perf_timer_elapsed ();
+}
+
+static void
+rounded_rectangle (cairo_t *cr,
+                  double x, double y, double w, double h,
+                  double radius)
+{
+    cairo_move_to (cr, x+radius, y);
+    cairo_arc (cr, x+w-radius, y+radius,   radius, M_PI + M_PI / 2, M_PI * 2        );
+    cairo_arc (cr, x+w-radius, y+h-radius, radius, 0,               M_PI / 2        );
+    cairo_arc (cr, x+radius,   y+h-radius, radius, M_PI/2,          M_PI            );
+    cairo_arc (cr, x+radius,   y+radius,   radius, M_PI,            270 * M_PI / 180);
+}
+
+static cairo_time_t
+do_strokes (cairo_t *cr, int width, int height, int loops)
+{
+    /* a pair of overlapping rectangles */
+    rounded_rectangle (cr,
+                      2, 2, width/2. + 10, height/2. + 10,
+                      10);
+    rounded_rectangle (cr,
+                      width/2. - 10, height/2. - 10,
+                      width/2. - 2, height/2. - 2,
+                      10);
+
+    cairo_set_line_width (cr, 2.);
+
+    cairo_perf_timer_start ();
+
+    while (loops--)
+       cairo_stroke_preserve (cr);
+
+    cairo_perf_timer_stop ();
+
+    cairo_new_path (cr);
+
+    return cairo_perf_timer_elapsed ();
+}
+
+cairo_bool_t
+stroke_enabled (cairo_perf_t *perf)
+{
+    return cairo_perf_can_run (perf, "stroke", NULL);
+}
+
+void
+stroke (cairo_perf_t *perf, cairo_t *cr, int width, int height)
+{
+    cairo_perf_cover_sources_and_operators (perf, "stroke", do_stroke, NULL);
+    cairo_perf_cover_sources_and_operators (perf, "strokes", do_strokes, NULL);
+}
diff --git a/perf/micro/subimage_copy.c b/perf/micro/subimage_copy.c
new file mode 100644 (file)
index 0000000..e749c06
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * Copyright © 2006 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ */
+
+#include "cairo-perf.h"
+
+/* This case exposes a performance bug found by Christopher "Monty"
+ * Montgomery in that copying a tiny portion of an image surface to an
+ * X surface causes the entire image surface to be copied to an
+ * intermediate surface.
+ *
+ * If the performance bug is fixed, then the time this test takes
+ * should be independent of the source and destination surface sizes.
+ */
+
+static cairo_time_t
+do_subimage_copy (cairo_t *cr, int width, int height, int loops)
+{
+    cairo_rectangle (cr, 2, 2, 4, 4);
+    cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+
+    cairo_perf_timer_start ();
+
+    while (loops--)
+       cairo_fill_preserve (cr);
+
+    cairo_perf_timer_stop ();
+
+    cairo_new_path (cr);
+
+    return cairo_perf_timer_elapsed ();
+}
+
+cairo_bool_t
+subimage_copy_enabled (cairo_perf_t *perf)
+{
+    return cairo_perf_can_run (perf, "subimage-copy", NULL);
+}
+
+void
+subimage_copy (cairo_perf_t *perf, cairo_t *cr, int width, int height)
+{
+    cairo_surface_t *image;
+    cairo_t *cr2;
+
+    cairo_set_source_rgb (cr, 0, 0, 1); /* blue */
+    cairo_paint (cr);
+
+    image = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
+    cr2 = cairo_create (image);
+    cairo_set_source_rgb (cr2, 1, 0, 0); /* red */
+    cairo_paint (cr2);
+    cairo_destroy (cr2);
+
+    cairo_set_source_surface (cr, image, 0, 0);
+    cairo_surface_destroy (image);
+
+    cairo_perf_run (perf, "subimage-copy", do_subimage_copy, NULL);
+}
diff --git a/perf/micro/tessellate.c b/perf/micro/tessellate.c
new file mode 100644 (file)
index 0000000..b6277fe
--- /dev/null
@@ -0,0 +1,181 @@
+/*
+ * Copyright © 2006 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ */
+
+#include "cairo-perf.h"
+
+typedef struct {
+    double x;
+    double y;
+} point_t;
+
+point_t points[300] = {
+ {39.4383,84.0188}, {79.844,78.3099}, {19.7551,91.1647}, {76.823,33.5223}, {55.397,27.7775},
+ {62.8871,47.7397}, {51.3401,36.4784}, {91.6195,95.223}, {71.7297,63.5712}, {60.6969,14.1603},
+ {24.2887,1.63006}, {80.4177,13.7232}, {40.0944,15.6679}, {10.8809,12.979}, {21.8257,99.8925},
+ {83.9112,51.2932}, {29.6032,61.264}, {52.4287,63.7552}, {97.2775,49.3583}, {77.1358,29.2517},
+ {76.9914,52.6745}, {89.1529,40.0229}, {35.2458,28.3315}, {91.9026,80.7725}, {94.9327,6.97553},
+ {8.60558,52.5995}, {66.3227,19.2214}, {34.8893,89.0233}, {2.0023,6.41713}, {6.30958,45.7702},
+ {97.0634,23.828}, {85.092,90.2208}, {53.976,26.6666}, {76.0249,37.5207}, {66.7724,51.2535},
+ {3.92803,53.1606}, {93.1835,43.7638}, {72.0952,93.081}, {73.8534,28.4293}, {35.4049,63.9979},
+ {16.5974,68.7861}, {88.0075,44.0105}, {33.0337,82.9201}, {89.3372,22.8968}, {68.667,35.036},
+ {58.864,95.6468}, {85.8676,65.7304}, {92.397,43.956}, {81.4767,39.8437}, {91.0972,68.4219},
+
+ {21.5825,48.2491}, {92.0128,95.0252}, {88.1062,14.766}, {43.1953,64.1081}, {28.1059,61.9596},
+ {30.7458,78.6002}, {22.6107,44.7034}, {27.6235,18.7533}, {41.6501,55.6444}, {90.6804,16.9607},
+ {12.6075,10.3171}, {76.0475,49.5444}, {93.5004,98.4752}, {38.3188,68.4445}, {36.8664,74.9771},
+ {23.2262,29.416}, {24.4413,58.4489}, {73.2149,15.239}, {79.347,12.5475}, {74.5071,16.4102},
+ {95.0104,7.45298}, {52.1563,5.25293}, {24.0062,17.6211}, {73.2654,79.7798}, {96.7405,65.6564},
+ {75.9735,63.9458}, {13.4902,9.34805}, {7.82321,52.021}, {20.4655,6.99064}, {81.9677,46.142},
+ {75.5581,57.3319}, {15.7807,5.19388}, {20.4329,99.9994}, {12.5468,88.9956}, {5.40576,99.7799},
+ {7.23288,87.054}, {92.3069,0.416161}, {18.0372,59.3892}, {39.169,16.3131}, {81.9695,91.3027},
+ {55.2485,35.9095}, {45.2576,57.943}, {9.96401,68.7387}, {75.7294,53.0808}, {99.2228,30.4295},
+ {87.7614,57.6971}, {62.891,74.7809}, {74.7803,3.54209}, {92.5377,83.3239}, {83.1038,87.3271},
+
+ {74.3811,97.9434}, {98.3596,90.3366}, {49.7259,66.688}, {83.0012,16.3968}, {7.69947,88.8949},
+ {24.8044,64.9707}, {22.9137,62.948}, {31.6867,70.062}, {23.1428,32.8777}, {63.3072,7.4161},
+ {65.1132,22.3656}, {97.1466,51.0686}, {54.6107,28.0042}, {11.3281,71.9269}, {59.254,47.1483},
+ {45.0918,94.4318}, {84.7684,33.6351}, {0.323146,43.4513}, {59.8481,34.4943}, {23.3892,83.3243},
+ {48.295,67.5476}, {30.4956,48.1936}, {18.2556,71.2087}, {4.08643,62.1823}, {69.5984,41.3984},
+ {63.764,67.3936}, {18.4622,34.7116}, {62.7158,60.9106}, {32.8374,73.0729}, {20.2213,74.0438},
+ {68.4757,92.0914}, {25.7265,65.313}, {8.76436,53.2441}, {87.7384,26.0497}, {9.37402,68.6125},
+ {36.1601,11.1276}, {59.3211,57.6691}, {28.8778,66.6557}, {28.8379,77.5767}, {18.9751,32.9642},
+ {0.357857,98.4363}, {33.1479,82.7391}, {43.6497,18.8201}, {91.893,95.8637}, {69.9075,76.4871},
+ {68.5786,12.1143}, {77.4273,38.3832}, {91.6273,94.3051}, {20.3548,86.1917}, {54.8042,79.3657},
+
+ {90.4932,29.7288}, {87.3979,90.9643}, {57.62,49.8144}, {27.3911,16.2757}, {49.2399,86.4579},
+ {84.8942,46.3662}, {29.1053,49.5977}, {68.4178,18.0421}, {13.9058,72.755}, {49.2422,60.3109},
+ {72.4252,83.8134}, {22.1966,17.8208}, {12.1259,49.8525}, {36.0443,13.8238}, {93.1895,32.4807},
+ {62.2095,90.8485}, {81.8128,83.6828}, {33.4972,49.6074}, {65.8831,39.4327}, {25.8906,60.8883},
+ {7.2545,15.123}, {64.7207,10.7848}, {28.827,36.3598}, {9.11486,33.1386}, {93.4495,42.7328},
+ {26.5461,58.357}, {76.1778,65.8747}, {15.7272,48.7427}, {62.5665,88.3037}, {20.7844,51.7715},
+ {42.6199,55.7561}, {39.4388,82.9939}, {32.6013,24.4327}, {63.8654,72.936}, {33.8243,98.4845},
+ {13.6075,89.756}, {0.540855,41.0788}, {77.4386,78.3282}, {11.4668,29.3678}, {72.1006,86.5535},
+ {44.9105,4.91625}, {70.7909,98.6467}, {47.3894,21.0883}, {9.39195,86.5181}, {38.2896,9.95593},
+ {65.712,30.1763}, {13.1702,80.9095}, {5.34223,5.15083}, {78.0868,45.7716}, {44.256,69.2076},
+
+ {58.9637,11.9111}, {52.9899,57.8635}, {36.1917,59.5045}, {88.8723,30.4285}, {16.982,47.6585},
+ {52.5747,60.9729}, {59.6196,61.8925}, {82.9808,23.3656}, {9.88374,7.00902}, {16.965,92.3728},
+ {22.5491,48.1733}, {29.0829,82.6769}, {87.8278,35.7193}, {81.4909,34.4251}, {3.63274,65.9146},
+ {77.8257,25.7469}, {83.6104,62.5964}, {22.1009,30.8157}, {61.2442,19.8021}, {67.4605,10.9733},
+ {71.9462,78.2262}, {40.1188,20.0352}, {43.4009,31.5658}, {38.5748,23.0996}, {15.4724,53.2846},
+ {1.45793,55.5398}, {38.2167,38.0215}, {73.7408,30.5408}, {64.9659,26.0445}, {91.9591,55.2316},
+ {80.9785,68.5986}, {31.195,69.7848}, {0.600477,64.5889}, {84.391,53.296}, {64.2693,61.8447},
+ {40.0709,51.8515}, {71.8867,36.2154}, {67.7812,80.1897}, {3.28927,15.2876}, {68.5722,6.35606},
+ {61.8958,18.7616}, {56.7831,70.0301}, {0.570914,0.112548}, {26.157,30.5239}, {85.7555,65.5368},
+ {34.1354,18.1161}, {87.9009,66.7341}, {31.323,65.3305}, {18.6265,88.5014}, {50.3461,15.7139},
+
+ {67.5654,82.8957}, {19.1112,90.417}, {70.6067,39.4521}, {54.7397,86.8924}, {93.2485,73.8959},
+ {92.6576,23.3119}, {93.342,55.1443}, {55.2568,49.4407}, {79.9646,93.9129}, {59.4497,81.4139},
+ {99.53,65.7201}, {32.4541,93.5852}, {58.9157,87.4309}, {75.9324,63.7771}, {79.491,77.5421},
+ {60.4379,26.2785}, {16.6955,47.0564}, {86.5086,79.549}, {66.4414,87.3021}, {61.1981,41.2483},
+ {64.5601,59.6899}, {14.8342,53.8557}, {3.29634,57.9022}, {51.8151,70.091}, {51.5049,83.2609},
+ {48.981,11.2648}, {4.84997,51.0349}, {38.4658,81.4351}, {45.2122,63.7656}, {41.3078,14.3982},
+ {40.6767,24.7033}, {71.7597,1.74566}, {81.2947,57.3721}, {44.6743,58.2682}, {99.5165,47.7361},
+ {7.42604,5.87232}, {59.728,64.0766}, {21.9788,22.2602}, {92.3513,63.0243}, {46.2852,73.7939},
+ {85.0586,43.8562}, {94.8911,95.2662}, {76.7014,89.9086}, {53.6742,33.3569}, {47.7551,21.9136},
+ {46.6169,94.982}, {96.7277,88.4318}, {45.8039,18.3765}, {76.6448,78.0224}, {25.7585,90.4782}
+};
+
+static cairo_time_t
+do_tessellate (cairo_t *cr, int num_points, int loops)
+{
+    int i;
+
+    for (i=0; i < num_points; i++)
+       cairo_line_to (cr, points[i].x, points[i].y);
+
+    cairo_perf_timer_start ();
+
+    /* We'd like to measure just tessellation without
+     * rasterization. For now, we can do that with cairo_in_fill. But
+     * we'll have to be careful since cairo_in_fill might eventually
+     * be optimized to have an implementation that doesn't necessarily
+     * include tessellation. */
+    while (loops--)
+       cairo_in_fill (cr, 50, 50);
+
+    cairo_perf_timer_stop ();
+
+    cairo_new_path (cr);
+
+    return cairo_perf_timer_elapsed ();
+}
+
+static cairo_time_t
+tessellate_16 (cairo_t *cr, int width, int height, int loops)
+{
+    return do_tessellate (cr, 16, loops);
+}
+
+static cairo_time_t
+tessellate_64 (cairo_t *cr, int width, int height, int loops)
+{
+    return do_tessellate (cr, 64, loops);
+}
+
+static cairo_time_t
+tessellate_256 (cairo_t *cr, int width, int height, int loops)
+{
+    return do_tessellate (cr, 256, loops);
+}
+
+cairo_bool_t
+tessellate_enabled (cairo_perf_t *perf)
+{
+    return cairo_perf_can_run (perf, "tessellate", NULL);
+}
+
+void
+tessellate (cairo_perf_t *perf, cairo_t *cr, int width, int height)
+{
+    cairo_perf_run (perf, "tessellate-16", tessellate_16, NULL);
+    cairo_perf_run (perf, "tessellate-64", tessellate_64, NULL);
+    cairo_perf_run (perf, "tessellate-256", tessellate_256, NULL);
+}
+
+#if 0
+double
+rand_within (double range)
+{
+    return range * (rand() / (RAND_MAX + 1.0));
+}
+
+int
+main (void)
+{
+#define SIZE 100
+    int i;
+
+    printf ("point_t points[] = {\n");
+
+    for (i=0; i < 1000; i++) {
+       printf (" {%g,%g},", rand_within (SIZE), rand_within (SIZE));
+       if (i % 5 == 4)
+           printf ("\n");
+    }
+
+    printf ("};\n");
+}
+#endif
diff --git a/perf/micro/text.c b/perf/micro/text.c
new file mode 100644 (file)
index 0000000..cdb3199
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * Copyright © 2006 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ */
+
+#include "cairo-perf.h"
+
+static cairo_time_t
+do_text (cairo_t *cr, int width, int height, int loops)
+{
+    const char text[] = "the jay, pig, fox, zebra and my wolves quack";
+    int len = strlen (text);
+    double x, y;
+    int i = 0, j = 0;
+
+    cairo_set_font_size (cr, 9);
+
+    cairo_perf_timer_start ();
+
+    while (loops--) {
+       do {
+           cairo_move_to (cr, 0, j++ * 10);
+           cairo_show_text (cr, text + i);
+           cairo_get_current_point (cr, &x, &y);
+           while (x < width && cairo_status (cr) == CAIRO_STATUS_SUCCESS) {
+               cairo_show_text (cr, text);
+               cairo_get_current_point (cr, &x, &y);
+           }
+           if (++i >= len)
+               i = 0;
+       } while (y < height && cairo_status (cr) == CAIRO_STATUS_SUCCESS);
+    }
+
+    cairo_perf_timer_stop ();
+
+    return cairo_perf_timer_elapsed ();
+}
+
+cairo_bool_t
+text_enabled (cairo_perf_t *perf)
+{
+    return cairo_perf_can_run (perf, "text", NULL);
+}
+
+void
+text (cairo_perf_t *perf, cairo_t *cr, int width, int height)
+{
+    cairo_perf_cover_sources_and_operators (perf, "text", do_text, NULL);
+}
diff --git a/perf/micro/tiger.c b/perf/micro/tiger.c
new file mode 100644 (file)
index 0000000..9e7a941
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ * Copyright © 2011 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.o.uk>
+ */
+
+#include "cairo-perf.h"
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "../../test/tiger.inc"
+
+static cairo_time_t
+do_tiger (cairo_t *cr, int width, int height, int loops)
+{
+    unsigned int i;
+
+    cairo_perf_timer_start ();
+
+    while (loops--) {
+       cairo_identity_matrix (cr);
+
+       cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+       cairo_set_source_rgba (cr, 0.1, 0.2, 0.3, 1.0);
+       cairo_paint (cr);
+       cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
+
+       cairo_translate (cr, width/2, height/2);
+       cairo_scale (cr, .85 * width/500, .85 * height/500);
+
+       for (i = 0; i < sizeof (tiger_commands)/sizeof(tiger_commands[0]);i++) {
+           const struct command *cmd = &tiger_commands[i];
+           switch (cmd->type) {
+           case 'm':
+               cairo_move_to (cr, cmd->x0, cmd->y0);
+               break;
+           case 'l':
+               cairo_line_to (cr, cmd->x0, cmd->y0);
+               break;
+           case 'c':
+               cairo_curve_to (cr,
+                               cmd->x0, cmd->y0,
+                               cmd->x1, cmd->y1,
+                               cmd->x2, cmd->y2);
+               break;
+           case 'f':
+               cairo_set_source_rgba (cr,
+                                      cmd->x0, cmd->y0, cmd->x1, cmd->y1);
+               cairo_fill (cr);
+               break;
+           }
+       }
+    }
+
+    cairo_perf_timer_stop ();
+
+    return cairo_perf_timer_elapsed ();
+}
+
+static cairo_time_t
+do_mono_tiger (cairo_t *cr, int width, int height, int loops)
+{
+    cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
+    return do_tiger (cr, width, height, loops);
+}
+
+static cairo_time_t
+do_fast_tiger (cairo_t *cr, int width, int height, int loops)
+{
+    cairo_set_antialias (cr, CAIRO_ANTIALIAS_FAST);
+    return do_tiger (cr, width, height, loops);
+}
+
+static cairo_time_t
+do_best_tiger (cairo_t *cr, int width, int height, int loops)
+{
+    cairo_set_antialias (cr, CAIRO_ANTIALIAS_BEST);
+    return do_tiger (cr, width, height, loops);
+}
+
+cairo_bool_t
+tiger_enabled (cairo_perf_t *perf)
+{
+    return cairo_perf_can_run (perf, "tiger", NULL);
+}
+
+void
+tiger (cairo_perf_t *perf, cairo_t *cr, int width, int height)
+{
+    cairo_perf_run (perf, "tiger-mono", do_mono_tiger, NULL);
+    cairo_perf_run (perf, "tiger-fast", do_fast_tiger, NULL);
+    cairo_perf_run (perf, "tiger-best", do_best_tiger, NULL);
+}
diff --git a/perf/micro/twin.c b/perf/micro/twin.c
new file mode 100644 (file)
index 0000000..99433bd
--- /dev/null
@@ -0,0 +1,59 @@
+#define WIDTH 1350
+#define HEIGHT 900
+
+#include "cairo-perf.h"
+
+static cairo_time_t
+do_twin (cairo_t *cr,
+        int width,
+        int height,
+        int loops)
+{
+    int i, j, h;
+    unsigned char s[2] = {0, 0};
+
+    cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); /* white */
+    cairo_paint (cr);
+    cairo_set_source_rgb (cr, 0, 0, 0);
+
+    cairo_select_font_face (cr,
+                           "@cairo:",
+                           CAIRO_FONT_SLANT_NORMAL,
+                           CAIRO_FONT_WEIGHT_NORMAL);
+
+    cairo_perf_timer_start ();
+
+    while (loops--) {
+       h = 2;
+       for (i = 8; i < 48; i >= 24 ? i+=3 : i++) {
+           cairo_set_font_size (cr, i);
+           for (j = 33; j < 128; j++) {
+               if (j == 33 || (j == 80 && i > 24)) {
+                   h += i + 2;
+                   cairo_move_to (cr, 10, h);
+               }
+               s[0] = j;
+               cairo_text_path (cr, (const char *) s);
+           }
+       }
+       cairo_fill (cr);
+    }
+
+    cairo_perf_timer_stop ();
+    return cairo_perf_timer_elapsed ();
+}
+
+cairo_bool_t
+twin_enabled (cairo_perf_t *perf)
+{
+    return cairo_perf_can_run (perf, "twin", NULL);
+}
+
+void
+twin (cairo_perf_t *perf,
+      cairo_t      *cr,
+      int           width,
+      int           height)
+{
+    cairo_perf_run (perf, "twin", do_twin, NULL);
+}
diff --git a/perf/micro/unaligned-clip.c b/perf/micro/unaligned-clip.c
new file mode 100644 (file)
index 0000000..41e327f
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * Copyright © 2006 Jeff Muizelaar <jeff@infidigm.net>
+ * Copyright © 2006 Red Hat, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Authors: Jeff Muizelaar <jeff@infidigm.net>
+ *          Carl Worth <cworth@cworth.org>
+ */
+
+#include "cairo-perf.h"
+
+static cairo_time_t
+do_unaligned_clip (cairo_t *cr, int width, int height, int loops)
+{
+    cairo_perf_timer_start ();
+
+    while (loops--) {
+       cairo_save (cr);
+
+       /* First a triangular clip that obviously isn't along device-pixel
+        * boundaries. */
+       cairo_move_to (cr, 50, 50);
+       cairo_line_to (cr, 50, 90);
+       cairo_line_to (cr, 90, 90);
+       cairo_close_path (cr);
+       cairo_clip (cr);
+
+       /* Then a rectangular clip that would be but for the non-integer
+        * scaling. */
+       cairo_scale (cr, 1.1, 1.1);
+       cairo_rectangle (cr, 55, 55, 35, 35);
+       cairo_clip (cr);
+
+       /* And paint something to force the clip to be evaluated. */
+       cairo_paint (cr);
+
+       cairo_restore (cr);
+    }
+    cairo_perf_timer_stop ();
+
+    return cairo_perf_timer_elapsed ();
+}
+
+cairo_bool_t
+unaligned_clip_enabled (cairo_perf_t *perf)
+{
+    return cairo_perf_can_run (perf, "unaligned-clip", NULL);
+}
+
+void
+unaligned_clip (cairo_perf_t *perf, cairo_t *cr, int width, int height)
+{
+    cairo_perf_run (perf, "unaligned-clip", do_unaligned_clip, NULL);
+}
diff --git a/perf/micro/wave.c b/perf/micro/wave.c
new file mode 100644 (file)
index 0000000..f6e6f74
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2011 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Benjamin Otte <otte@redhat.com>
+ */
+
+#include "cairo-perf.h"
+
+static cairo_surface_t *
+generate_random_waveform(cairo_t *target, int width, int height)
+{
+    cairo_surface_t *surface;
+    cairo_t *cr;
+    int i, r;
+
+    srand (0xdeadbeef);
+
+    surface = cairo_surface_create_similar (cairo_get_target (target),
+                                           CAIRO_CONTENT_ALPHA,
+                                           width, height);
+    cr = cairo_create (surface);
+
+    r = height / 2;
+
+    for (i = 0; i < width; i++)
+    {
+       r += rand () % (height / 4) - (height / 8);
+       if (r < 0)
+           r = 0;
+       else if (r > height)
+           r = height;
+       cairo_rectangle (cr, i, (height - r) / 2, 1, r);
+    }
+    cairo_fill (cr);
+    cairo_destroy (cr);
+
+    return surface;
+}
+
+static cairo_time_t
+do_wave (cairo_t *cr, int width, int height, int loops)
+{
+    cairo_surface_t *wave;
+    cairo_pattern_t *mask;
+
+    wave = generate_random_waveform (cr, width, height);
+
+    cairo_perf_timer_start ();
+
+    while (loops--) {
+       /* paint outline (and contents) */
+       cairo_set_source_rgb (cr, 1, 0, 0);
+       cairo_mask_surface (cr, wave, 0, 0);
+
+       /* overdraw inline */
+       /* first, create a mask */
+       cairo_push_group_with_content (cr, CAIRO_CONTENT_ALPHA);
+       cairo_set_source_surface (cr, wave, 0, 0);
+       cairo_paint (cr);
+       cairo_set_operator (cr, CAIRO_OPERATOR_IN);
+       cairo_set_source_surface (cr, wave, 1, 0);
+       cairo_paint (cr);
+       cairo_set_source_surface (cr, wave, -1, 0);
+       cairo_paint (cr);
+       cairo_set_source_surface (cr, wave, 0, 1);
+       cairo_paint (cr);
+       cairo_set_source_surface (cr, wave, 0, -1);
+       cairo_paint (cr);
+       mask = cairo_pop_group (cr);
+
+       /* second, paint the mask */
+       cairo_set_source_rgb (cr, 0, 1, 0);
+       cairo_mask (cr, mask);
+
+       cairo_pattern_destroy (mask);
+    }
+
+    cairo_perf_timer_stop ();
+
+    cairo_surface_destroy (wave);
+
+    return cairo_perf_timer_elapsed ();
+}
+
+cairo_bool_t
+wave_enabled (cairo_perf_t *perf)
+{
+    return cairo_perf_can_run (perf, "wave", NULL);
+}
+
+void
+wave (cairo_perf_t *perf, cairo_t *cr, int width, int height)
+{
+    cairo_perf_run (perf, "wave", do_wave, NULL);
+}
diff --git a/perf/micro/wide-fills.c b/perf/micro/wide-fills.c
new file mode 100644 (file)
index 0000000..0747e6e
--- /dev/null
@@ -0,0 +1,187 @@
+/*
+ * Copyright © 2011 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+
+/* This is a variant on wide strokes where we precompute
+ * a simplified stroke-to-path.
+ * When we have a real stroke-to-path, it would useful to compare the cost
+ * of stroking vs filling the "identical" paths.
+ */
+
+#include "cairo-perf.h"
+
+static uint32_t state;
+
+static double
+uniform_random (double minval, double maxval)
+{
+    static uint32_t const poly = 0x9a795537U;
+    uint32_t n = 32;
+    while (n-->0)
+       state = 2*state < state ? (2*state ^ poly) : 2*state;
+    return minval + state * (maxval - minval) / 4294967296.0;
+}
+
+static cairo_time_t
+do_wide_fills_ha (cairo_t *cr, int width, int height, int loops)
+{
+    int count;
+
+    state = 0xc0ffee;
+    for (count = 0; count < 1000; count++) {
+       double y = floor (uniform_random (0, height));
+       double x = floor (uniform_random (0, width));
+       cairo_rectangle (cr, x, y, ceil (uniform_random (0, width)) - x, 5);
+    }
+
+    cairo_perf_timer_start ();
+
+    while (loops--)
+       cairo_fill_preserve (cr);
+
+    cairo_perf_timer_stop ();
+
+    cairo_new_path (cr);
+
+    return cairo_perf_timer_elapsed ();
+}
+
+static cairo_time_t
+do_wide_fills_h (cairo_t *cr, int width, int height, int loops)
+{
+    int count;
+
+    state = 0xc0ffee;
+    for (count = 0; count < 1000; count++) {
+       double y = uniform_random (0, height);
+       double x = uniform_random (0, width);
+       cairo_rectangle (cr, x, y, uniform_random (0, width) - x, 5);
+    }
+
+    cairo_perf_timer_start ();
+
+    while (loops--)
+       cairo_fill_preserve (cr);
+
+    cairo_perf_timer_stop ();
+
+    cairo_new_path (cr);
+
+    return cairo_perf_timer_elapsed ();
+}
+
+static cairo_time_t
+do_wide_fills_va (cairo_t *cr, int width, int height, int loops)
+{
+    int count;
+
+    state = 0xc0ffee;
+    for (count = 0; count < 1000; count++) {
+       double x = floor (uniform_random (0, width));
+       double y = floor (uniform_random (0, height));
+       cairo_rectangle (cr, x, y, 5, ceil (uniform_random (0, height) - y));
+    }
+
+    cairo_perf_timer_start ();
+
+    while (loops--)
+       cairo_fill_preserve (cr);
+
+    cairo_perf_timer_stop ();
+
+    cairo_new_path (cr);
+
+    return cairo_perf_timer_elapsed ();
+}
+
+static cairo_time_t
+do_wide_fills_v (cairo_t *cr, int width, int height, int loops)
+{
+    int count;
+
+    state = 0xc0ffee;
+    for (count = 0; count < 1000; count++) {
+       double x = uniform_random (0, width);
+       double y = uniform_random (0, height);
+       cairo_rectangle (cr, x, y, 5, uniform_random (0, height) - y);
+    }
+
+    cairo_perf_timer_start ();
+
+    while (loops--)
+       cairo_fill_preserve (cr);
+
+    cairo_perf_timer_stop ();
+
+    cairo_new_path (cr);
+
+    return cairo_perf_timer_elapsed ();
+}
+
+static cairo_time_t
+do_wide_fills (cairo_t *cr, int width, int height, int loops)
+{
+    int count;
+
+    /* lots and lots of overlapping stroke-like fills */
+    state = 0xc0ffee;
+    for (count = 0; count < 1000; count++) {
+       cairo_save (cr);
+       cairo_translate (cr,
+                        uniform_random (0, width),
+                        uniform_random (0, height));
+       cairo_rotate (cr, uniform_random (-M_PI,M_PI));
+       cairo_rectangle (cr, 0, 0, uniform_random (0, width), 5);
+       cairo_restore (cr);
+    }
+
+    cairo_perf_timer_start ();
+
+    while (loops--)
+       cairo_fill_preserve (cr);
+
+    cairo_perf_timer_stop ();
+
+    cairo_new_path (cr);
+
+    return cairo_perf_timer_elapsed ();
+}
+
+cairo_bool_t
+wide_fills_enabled (cairo_perf_t *perf)
+{
+    return cairo_perf_can_run (perf, "wide-fills", NULL);
+}
+
+void
+wide_fills (cairo_perf_t *perf, cairo_t *cr, int width, int height)
+{
+    cairo_perf_run (perf, "wide-fills-halign", do_wide_fills_ha, NULL);
+    cairo_perf_run (perf, "wide-fills-valign", do_wide_fills_va, NULL);
+    cairo_perf_run (perf, "wide-fills-horizontal", do_wide_fills_h, NULL);
+    cairo_perf_run (perf, "wide-fills-vertical", do_wide_fills_v, NULL);
+    cairo_perf_run (perf, "wide-fills-random", do_wide_fills, NULL);
+}
diff --git a/perf/micro/wide-strokes.c b/perf/micro/wide-strokes.c
new file mode 100644 (file)
index 0000000..1413091
--- /dev/null
@@ -0,0 +1,188 @@
+/*
+ * Copyright © 2011 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-perf.h"
+
+static uint32_t state;
+
+static double
+uniform_random (double minval, double maxval)
+{
+    static uint32_t const poly = 0x9a795537U;
+    uint32_t n = 32;
+    while (n-->0)
+       state = 2*state < state ? (2*state ^ poly) : 2*state;
+    return minval + state * (maxval - minval) / 4294967296.0;
+}
+
+static cairo_time_t
+do_wide_strokes_ha (cairo_t *cr, int width, int height, int loops)
+{
+    int count;
+
+    state = 0xc0ffee;
+    for (count = 0; count < 1000; count++) {
+       double h = floor (uniform_random (0, height));
+       cairo_move_to (cr, floor (uniform_random (0, width)), h);
+       cairo_line_to (cr, ceil (uniform_random (0, width)), h);
+    }
+
+    cairo_set_line_width (cr, 5.);
+
+    cairo_perf_timer_start ();
+
+    while (loops--)
+       cairo_stroke_preserve (cr);
+
+    cairo_perf_timer_stop ();
+
+    cairo_new_path (cr);
+
+    return cairo_perf_timer_elapsed ();
+}
+
+static cairo_time_t
+do_wide_strokes_h (cairo_t *cr, int width, int height, int loops)
+{
+    int count;
+
+    state = 0xc0ffee;
+    for (count = 0; count < 1000; count++) {
+       double h = uniform_random (0, height);
+       cairo_move_to (cr, uniform_random (0, width), h);
+       cairo_line_to (cr, uniform_random (0, width), h);
+    }
+
+    cairo_set_line_width (cr, 5.);
+
+    cairo_perf_timer_start ();
+
+    while (loops--)
+       cairo_stroke_preserve (cr);
+
+    cairo_perf_timer_stop ();
+
+    cairo_new_path (cr);
+
+    return cairo_perf_timer_elapsed ();
+}
+
+static cairo_time_t
+do_wide_strokes_va (cairo_t *cr, int width, int height, int loops)
+{
+    int count;
+
+    state = 0xc0ffee;
+    for (count = 0; count < 1000; count++) {
+       double v = floor (uniform_random (0, width));
+       cairo_move_to (cr, v, floor (uniform_random (0, height)));
+       cairo_line_to (cr, v, ceil (uniform_random (0, height)));
+    }
+
+    cairo_set_line_width (cr, 5.);
+
+    cairo_perf_timer_start ();
+
+    while (loops--)
+       cairo_stroke_preserve (cr);
+
+    cairo_perf_timer_stop ();
+
+    cairo_new_path (cr);
+
+    return cairo_perf_timer_elapsed ();
+}
+
+static cairo_time_t
+do_wide_strokes_v (cairo_t *cr, int width, int height, int loops)
+{
+    int count;
+
+    state = 0xc0ffee;
+    for (count = 0; count < 1000; count++) {
+       double v = uniform_random (0, width);
+       cairo_move_to (cr, v, uniform_random (0, height));
+       cairo_line_to (cr, v, uniform_random (0, height));
+    }
+
+    cairo_set_line_width (cr, 5.);
+
+    cairo_perf_timer_start ();
+
+    while (loops--)
+       cairo_stroke_preserve (cr);
+
+    cairo_perf_timer_stop ();
+
+    cairo_new_path (cr);
+
+    return cairo_perf_timer_elapsed ();
+}
+
+static cairo_time_t
+do_wide_strokes (cairo_t *cr, int width, int height, int loops)
+{
+    int count;
+
+    /* lots and lots of overlapping strokes */
+    state = 0xc0ffee;
+    for (count = 0; count < 1000; count++) {
+       cairo_line_to (cr,
+                      uniform_random (0, width),
+                      uniform_random (0, height));
+    }
+
+    cairo_set_line_width (cr, 5.);
+
+    cairo_perf_timer_start ();
+
+    while (loops--)
+       cairo_stroke_preserve (cr);
+
+    cairo_perf_timer_stop ();
+
+    cairo_new_path (cr);
+
+    return cairo_perf_timer_elapsed ();
+}
+
+cairo_bool_t
+wide_strokes_enabled (cairo_perf_t *perf)
+{
+    return cairo_perf_can_run (perf, "wide-strokes", NULL);
+}
+
+void
+wide_strokes (cairo_perf_t *perf, cairo_t *cr, int width, int height)
+{
+    cairo_set_source_rgb (cr, 1., 1., 1.);
+
+    cairo_perf_run (perf, "wide-strokes-halign", do_wide_strokes_ha, NULL);
+    cairo_perf_run (perf, "wide-strokes-valign", do_wide_strokes_va, NULL);
+    cairo_perf_run (perf, "wide-strokes-horizontal", do_wide_strokes_h, NULL);
+    cairo_perf_run (perf, "wide-strokes-vertical", do_wide_strokes_v, NULL);
+    cairo_perf_run (perf, "wide-strokes-random", do_wide_strokes, NULL);
+}
diff --git a/perf/micro/world-map.c b/perf/micro/world-map.c
new file mode 100644 (file)
index 0000000..ff22eeb
--- /dev/null
@@ -0,0 +1,149 @@
+/*
+ * Copyright © 2006 Red Hat, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ */
+
+#include "cairo-perf.h"
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+typedef enum {
+    WM_NEW_PATH,
+    WM_MOVE_TO,
+    WM_LINE_TO,
+    WM_HLINE_TO,
+    WM_VLINE_TO,
+    WM_REL_LINE_TO,
+    WM_END
+} wm_type_t;
+
+typedef struct _wm_element {
+    wm_type_t type;
+    double x;
+    double y;
+} wm_element_t;
+
+#include "world-map.h"
+
+enum {
+    STROKE = 1,
+    FILL = 2,
+};
+
+static cairo_time_t
+do_world_map (cairo_t *cr, int width, int height, int loops, int mode)
+{
+    const wm_element_t *e;
+    double cx, cy;
+
+    cairo_set_line_width (cr, 0.2);
+
+    cairo_perf_timer_start ();
+
+    while (loops--) {
+       cairo_set_source_rgb (cr, .68, .85, .90); /* lightblue */
+       cairo_rectangle (cr, 0, 0, 800, 400);
+       cairo_fill (cr);
+
+       e = &countries[0];
+       while (1) {
+           switch (e->type) {
+           case WM_NEW_PATH:
+           case WM_END:
+               if (mode & FILL) {
+                   cairo_set_source_rgb (cr, .75, .75, .75); /* silver */
+                   cairo_fill_preserve (cr);
+               }
+               if (mode & STROKE) {
+                   cairo_set_source_rgb (cr, .50, .50, .50); /* gray */
+                   cairo_stroke (cr);
+               }
+               cairo_new_path (cr);
+               cairo_move_to (cr, e->x, e->y);
+               break;
+           case WM_MOVE_TO:
+               cairo_close_path (cr);
+               cairo_move_to (cr, e->x, e->y);
+               break;
+           case WM_LINE_TO:
+               cairo_line_to (cr, e->x, e->y);
+               break;
+           case WM_HLINE_TO:
+               cairo_get_current_point (cr, &cx, &cy);
+               cairo_line_to (cr, e->x, cy);
+               break;
+           case WM_VLINE_TO:
+               cairo_get_current_point (cr, &cx, &cy);
+               cairo_line_to (cr, cx, e->y);
+               break;
+           case WM_REL_LINE_TO:
+               cairo_rel_line_to (cr, e->x, e->y);
+               break;
+           }
+           if (e->type == WM_END)
+               break;
+           e++;
+       }
+
+       cairo_new_path (cr);
+    }
+
+    cairo_perf_timer_stop ();
+
+    return cairo_perf_timer_elapsed ();
+}
+
+static cairo_time_t
+do_world_map_stroke (cairo_t *cr, int width, int height, int loops)
+{
+    return do_world_map (cr, width, height, loops, STROKE);
+}
+
+static cairo_time_t
+do_world_map_fill (cairo_t *cr, int width, int height, int loops)
+{
+    return do_world_map (cr, width, height, loops, FILL);
+}
+
+static cairo_time_t
+do_world_map_both (cairo_t *cr, int width, int height, int loops)
+{
+    return do_world_map (cr, width, height, loops, FILL | STROKE);
+}
+
+cairo_bool_t
+world_map_enabled (cairo_perf_t *perf)
+{
+    return cairo_perf_can_run (perf, "world-map", NULL);
+}
+
+void
+world_map (cairo_perf_t *perf, cairo_t *cr, int width, int height)
+{
+    cairo_perf_run (perf, "world-map-stroke", do_world_map_stroke, NULL);
+    cairo_perf_run (perf, "world-map-fill", do_world_map_fill, NULL);
+    cairo_perf_run (perf, "world-map", do_world_map_both, NULL);
+}
diff --git a/perf/micro/world-map.h b/perf/micro/world-map.h
new file mode 100644 (file)
index 0000000..af58b80
--- /dev/null
@@ -0,0 +1,196 @@
+/* The data for this test case was provided by Gary Nicholson
+ * <gary@imapping.co.nz> who originally created an interactive SVG map
+ * of the world as can be seen here:
+ *
+ *     http://www.wherearewe.co.nz/svg.html
+ *
+ * The data is used here by permission from Gary given on 2006-11-08:
+ *
+ *     Thanks for asking, I don't need any attribution if you are
+ *     only using the vectors and not the entire map with
+ *     interactivity etc. So feel free to do what you wish with
+ *     the data.
+ */
+
+#define N(x,y) { WM_NEW_PATH,    x, y }
+#define M(x,y) { WM_MOVE_TO,     x, y }
+#define L(x,y) { WM_LINE_TO,     x, y }
+#define H(x,y) { WM_HLINE_TO,    x, y }
+#define V(x,y) { WM_VLINE_TO,    x, y }
+#define l(x,y) { WM_REL_LINE_TO, x, y }
+#define E(x,y) { WM_END,         x, y }
+
+static const wm_element_t countries[] = {
+N(413.519,90.071),l(.136,-.348),l(-.31,-.204),l(-.017,-.327),l(.213,-.322),l(.245,-.147),l(.142,-.08),l(.225,.072),l(.062,.301),l(.41,.312),l(.466,.096),l(-.044,.288),l(-.248,.144),l(.074,.353),l(-.145,-.063),l(-.568,-.011),l(-.642,-.063),
+N(421.683,94.397),l(.193,.336),l(-.266,.274),l(.214,.288),l(-.09,.192),l(.622,.062),l(.008,.144),l(.55,.242),l(.579,-.332),l(.215,.117),l(-.029,.171),l(-.126,.309),l(.112,.212),l(-.038,.192),l(-.315,-.051),l(-.176,-.162),l(-.283,.091),l(-.081,.273),l(.244,.131),l(-.228,.415),l(-.244,-.333),l(-.469,.05),l(-.071,.122),l(-.216,.03),l(-.23,-.142),l(-.143,-.354),l(-.371,.081),l(.019,.333),l(-.425,.384),l(-.018,.535),l(-.285,.151),l(-.385,-.312),l(.098,-.182),l(-.311,-.071),l(-.534,-.363),l(-.016,-.415),l(-.777,.404),l(.103,.212),l(-.349,.432),l(-.275,.16),l(-.629,-.168),l(-.627,.204),l(-.599,-.062),l(-.102,-.424),l(-.312,-.806),l(-.616,.147),l(-.854,.668),l(-.369,-.111),l(.238,-.226),l(.013,-.322),l(-.08,-.137),l(.089,-.294),l(.718,-.418),l(-.038,-.315),l(.575,-.24),l(.012,-.076),l(.528,-.494),l(.173,-.035),l(-.116,-.089),l(-.153,-.028),l(.221,-.302),l(.446,.007),l(-.005,.096),l(.473,.007),l(.385,-.309),l(.271,.089),l(.272,-.117),l(.271,.096),l(.567,-.158),l(.278,.11),l(.354,-.021),l(-.179,-.199),l(.709,-.199),l(.017,.151),l(.199,-.014),l(.149,.089),l(.852,.007),l(.664,.261),
+N(421.394,104.558),l(.104,.175),l(.04,.256),l(-.06,.475),l(.118,.054),l(.062,.333),l(-.076,.795),l(-.211,.327),l(-.118,.724),l(-.292,.501),l(-.298,-.043),l(-.057,-.196),l(-.41,-.076),l(-.227,-.152),l(.284,-.207),l(-.07,-.076),l(-.437,-.098),l(.257,-.332),l(-.11,-.071),l(-.291,.071),l(-.053,-.147),l(.115,-.022),l(.175,-.158),l(-.094,-.153),l(-.257,-.082),l(.015,-.164),l(.247,-.12),l(-.284,-.218),l(.241,-.284),l(.6,-.305),l(.27,-.022),l(.04,-.125),l(.292,-.043),l(.195,.104),l(.096,-.142),l(-.022,-.344),l(.072,-.224),l(.143,-.011),M(396.323,103.853),l(.375,-.122),l(.411,-.365),l(.549,-2.299),l(.397,-.091),l(-.21,-.29),l(-.226,.259),l(.125,-1.144),l(.223,-.826),l(.115,.153),l(.496,.306),l(.191,.382),l(.191,.229),l(-.281,-.673),l(-.63,-.582),l(-.242,-.233),l(.024,-.249),l(.359,-.477),l(-.202,-.375),l(-.2,-.274),l(-.326,-.216),l(-.685,-.1),l(-.515,-.571),l(-.416,-.323),l(.278,-.426),l(-.233,-.181),l(-.343,-.131),l(-.511,-.142),l(-.184,-.173),l(.247,-.376),l(-.329,-.173),l(-.509,.179),l(-.489,-.249),l(-.824,-.251),l(-.619,-.181),l(-.325,.014),l(-.215,-.25),l(-.91,.167),l(-.059,-.25),l(-.265,-.125),l(-.367,-.042),l(-.056,-.104),l(.861,-.083),l(-.085,-.229),l(-.526,-.104),l(.442,-.104),l(.086,-.205),l(-.275,.017),l(-.263,-.021),l(-.417,.083),l(.04,-.438),l(.303,.012),l(.305,-.146),l(.526,-.088),l(.562,-.174),l(.215,.188),l(.18,-.167),l(.474,.063),l(.112,-.26),l(.272,-.059),l(.764,-.078),l(.393,.366),l(.275,.26),l(.342,.083),l(.652,-.271),l(.317,.167),l(.276,-.127),l(.457,-.124),l(.029,.23),l(.591,-.065),l(.3,-.103),l(-.265,-.188),l(-.028,-.251),l(.056,-.345),l(-.037,-.428),l(-.131,.021),l(-.562,-.682),l(-.11,-.407),l(.927,.126),l(.607,-.105),l(-.084,.397),l(.248,.419),l(.342,-.146),l(1.241,.104),l(.501,.146),l(.079,-.014),l(.525,-.093),l(.662,-.27),l(-.534,-.124),l(.055,-.204),l(.166,-.175),l(.753,-.322),l(.756,-.181),l(.902,-.215),l(.314,-.235),l(.302,-.264),l(-.053,-.775),l(.135,-.542),l(.521,-.25),l(.46,-.16),l(.916,-.092),l(.177,-.096),l(.208,.447),l(.311,.335),l(.266,.127),l(.141,-.071),l(.41,-.208),l(.153,.17),l(.202,.458),l(.194,.133),l(.518,-.012),l(.159,.301),l(.259,-.012),l(.576,.048),l(.375,.168),l(-.159,.241),l(.091,.175),l(-.072,.198),l(.285,.122),l(.406,-.075),l(.446,-.035),l(.193,-.313),l(.245,-.072),l(-.119,.373),l(.146,.18),l(-.039,.228),l(.529,.048),l(.341,.192),l(.371,.204),l(.127,.228),l(.694,-.174),l(.079,.114),l(.642,.063),l(.568,.011),l(.145,.063),l(.428,.319),l(.337,.277),l(.395,-.055),l(.045,.145),l(.689,-.062),l(.072,-.048),l(.233,.007),l(.095,.186),l(.456,.09),l(.479,-.014),l(.605,.193),l(-.954,.806),l(-.054,.213),l(-.054,.358),l(-.321,.372),l(-.075,.295),l(.091,.076),l(-.216,.701),l(.135,.233),l(-.385,.309),l(-.473,-.007),l(.005,-.096),L(415.96,94.5),l(-.221,.302),l(.153,.028),l(.116,.089),l(-.173,.035),l(-.528,.494),l(-.012,.076),l(-.575,.24),l(.038,.315),l(-.718,.418),l(-.089,.294),l(.08,.137),l(-.013,.322),l(-.238,.226),l(.369,.111),l(.854,-.668),l(.616,-.147),l(.312,.806),l(.102,.424),l(-.624,.301),l(.532,.344),l(.025,.292),l(.43,.192),l(-.199,.272),l(-.541,.353),l(-.183,-.111),l(-.437,.186),l(.352,.358),l(.616,.191),l(.135,.331),l(-.175,.01),l(-.315,.371),l(.193,.442),l(.754,.391),l(.849,-.07),l(.062,.281),l(-.146,.469),l(-.346,.23),l(-.221,.215),l(-.833,.488),l(-.889,.659),l(-.427,.087),l(-.318,.043),l(-.798,.159),l(-.405,-.028),l(-.471,-.156),l(-.851,-.499),l(-.315,-.085),l(-.354,.029),l(-.231,.072),l(-.511,-.056),l(-.752,-.313),l(-.602,.044),l(-.731,.345),l(-.357,.258),l(-.555,.559),l(-.147,.386),l(.099,.514),l(.091,.379),l(-.334,-.091),l(-.75,.137),l(-.039,.136),l(-.485,-.015),l(-.427,-.197),l(-.395,.167),l(-.261,-.015),l(-.036,-.152),l(-.335,-.091),l(-.206,.03),l(-.374,.076),l(-.187,-.076),l(-.035,-.289),l(-.091,-.213),l(-1.252,-.304),l(-.355,0),l(.017,.319),l(-.542,-.015),l(-.337,.061),l(-.037,-.122),l(-.767,.03),l(-.084,-.114),l(-.028,-.038),l(-.431,-.152),l(-.131,.076),l(-.262,-.03),l(-.056,.076),l(-.507,-.395),l(-.15,.061),l(-1.088,-.334),l(-.112,.106),l(-.15,-.03),l(-.094,-.106),l(.205,-.243),l(-.058,-.122),l(-.469,.03),l(-.472,-.243),
+N(681.312,116.395),l(.235,-.171),l(.283,-.256),l(.633,-.738),l(.315,-.157),l(.595,.011),l(.579,.068),l(.511,.096),l(.309,-.115),l(.571,-.678),l(.682,.621),l(1.178,1.611),l(.329,.495),l(.269,.664),l(.002,.75),l(-.034,.947),l(-.129,.637),l(.143,.113),l(.5,-.043),l(-.121,.41),l(-.282,.523),l(-.5,.75),l(-.316,.312),l(-.243,.043),l(-.567,-.211),l(-.256,.1),l(-.607,.58),l(-.431,-.083),l(-.289,-.225),l(-.544,.1),l(-.526,.199),l(-1.188,.835),l(-.462,.043),l(-.46,.312),l(-.055,-.564),l(-.056,-.324),l(-.163,-.705),l(-.137,-.395),l(.167,-.453),l(.499,-.468),l(0,-.353),l(.226,-.425),l(-.044,-.141),l(-.378,-.311),l(-.095,-.296),l(.015,-.467),l(-.087,-.339),l(-.289,-.126),l(-.603,-.084),l(.654,-.411),l(.303,-.114),l(.654,.268),l(.254,-.241),l(-.029,-.283),l(-.764,-.89),l(-.113,-.311),l(-.137,-.105),
+N(475.646,121.847),l(-.018,.175),l(.338,.391),l(-.295,-.009),l(-.132,.108),l(-.104,-.059),l(-.327,-.021),l(-.121,.33),l(-.783,.257),l(-.384,.046),l(-.099,.053),l(0,.21),l(-.217,.006),l(-.072,-.192),l(-.402,.023),l(-.547,-.146),l(-.191,-.087),l(0,-.21),l(-.161,-.105),l(-.122,-.403),l(.082,-.035),l(.12,.1),l(.147,-.006),l(.405,-.304),l(.253,-.006),l(.328,.092),l(.077,-.086),l(.088,-.286),l(-.053,-.175),l(.627,.093),l(.658,.027),l(.367,-.056),l(.818,-.233),l(.689,-.304),l(.535,-.158),l(-.475,.295),l(-.436,.231),l(-.596,.444),
+N(704.404,117.274),l(.197,-.099),l(1.108,-.271),l(.057,.354),l(-.481,.284),l(-.232,.241),l(-.068,.453),l(.139,.367),l(.291,.056),l(.221,-.114),l(.418,-.354),l(.24,-.085),l(1.656,-.697),l(.389,-.213),l(.46,-.326),l(.349,-.638),l(.76,-.412),l(.347,-.327),l(.191,-.269),l(.142,-.51),l(.538,-.582),l(-.01,-.142),l(.344,-.567),l(.159,-.468),l(.139,-.609),l(-.043,-.467),l(-.33,-.198),l(-.128,-.24),l(.234,-.213),l(.166,-.284),l(-.155,-1.023),l(.544,-.343),l(.176,-.242),l(.327,-.328),l(.192,0),l(.21,.355),l(.199,.227),l(.303,-.058),l(.799,-.257),l(-.169,-.526),l(-.311,-.028),l(-.36,-.312),l(.694,-.415),l(.441,.156),l(.336,.227),l(.025,.199),l(-.016,.868),l(.058,.611),l(.22,.127),l(.243,.312),l(.717,1.432),l(.001,.496),l(-.246,.709),l(-.709,.766),l(-.226,.439),l(.064,.368),l(-.15,.071),l(-.737,.285),l(-.161,.113),l(-.164,.199),l(-.174,.453),l(.02,.396),l(.094,.254),l(.131,.792),l(-.04,.693),l(-.686,.751),l(-.242,.736),l(.02,.707),l(.198,.296),l(.422,.353),l(-.617,.298),l(-.193,.127),l(-.166,.17),l(-.174,.834),l(-1.081,.439),l(-.094,-.282),l(.294,-.665),l(.184,-.523),l(-.198,-.126),l(-.514,.241),l(-.578,.623),l(-.476,.001),l(-.346,.312),l(-.066,.748),l(-.354,.269),l(-.188,-.028),l(-.066,-.155),l(.003,-.606),l(-.149,-.155),l(-.211,.042),l(-.309,.156),l(-.344,.311),l(-.325,.523),l(-.866,-.055),l(-.505,.057),l(-.631,.1),l(-.458,-.549),l(-.685,-.323),l(-.26,.254),l(-.067,.184),l(-.177,.353),l(.037,.056),l(.417,.197),l(.416,.323),l(-.293,.198),l(-.829,.129),l(-.433,.241),l(-.463,.622),l(-.522,.847),l(-.688,-.365),l(-.565,-.21),l(-.285,-.197),l(-.014,-.169),l(-.194,-.818),l(.099,-.155),l(.495,-.325),l(.179,-.269),l(-.067,-.282),l(-.18,-.042),l(-.601,.17),l(-.341,-.028),l(-.789,-.167),l(-.475,.128),l(-.427,.227),l(-.437,.184),l(-.269,-.098),l(-.256,-.027),l(-1.647,.398),l(-.814,.298),l(-.21,-.31),l(-.452,-.042),l(-.413,.438),l(-.006,.635),l(-.756,-.238),l(-.579,-.055),l(-1.1,.073),l(-.267,-.14),l(.072,-.339),l(.179,-.283),l(.483,.013),l(.499,-.114),l(.751,-.467),l(2.201,-1.953),l(.28,-.015),l(.427,-.128),l(.056,.424),l(.495,-.128),l(1.278,-.257),l(.933,-.058),l(1.183,-.172),l(.892,-.256),l(.068,.452),l(.377,.268),l(.167,-.085),l(.654,-.199),l(.446,-.34),l(-.003,-.353),l(.114,-.467),l(.465,-.51),l(.698,-.581),l(.371,-.453),l(-.062,-1.117),l(.182,-.213),M(695.464,127.756),l(-.292,-.197),l(-.223,-.268),l(-.101,-.381),l(-.177,-.395),l(-.492,-.535),l(.731,-.382),l(.287,-.269),l(.456,-.593),l(.409,.253),l(.615,-.015),l(.483,-.185),l(.311,-.339),l(.451,-.311),l(.454,-.029),l(.316,.169),l(.862,.224),l(.153,.254),l(-.1,.127),l(-.102,.423),l(-.292,.24),l(-.864,.876),l(-.181,-.211),l(-.424,-.295),l(-.467,-.042),l(-.612,.213),l(-.193,.184),l(-.245,.495),l(-.165,.508),l(-.153,.212),l(-.448,.269),M(691.12,131.448),l(-.366,-.042),l(-.056,-.141),l(.268,-.537),l(.128,-.593),l(-.334,-.112),l(-.239,.198),l(-.155,.466),l(-.381,.452),l(-.326,-.211),l(-.059,-.211),l(.322,-.466),l(.032,-.296),l(-.356,-.917),l(.169,-.113),l(.687,-.58),l(.083,-.141),l(.034,-.466),l(-.532,-.789),l(-.333,-.042),l(-.162,.269),l(-.419,.495),l(-.249,-.112),l(-.23,-.508),l(-.376,-.267),l(-.261,-.366),l(.41,-.325),l(.733,.083),l(.706,-.171),l(.315,-.466),l(.241,-.283),l(.484,-.058),l(.478,.056),l(.249,.38),l(.27,.168),l(.43,.084),l(.628,-.213),l(.225,.395),l(-.569,.438),l(.405,.239),l(.443,.437),l(.079,.254),l(-.596,.58),l(-.242,.41),l(-.104,.367),l(-.085,.621),l(-.109,.649),l(-.242,.353),l(-.194,.099),l(-.165,.071),l(-.197,.184),l(-.479,.678),M(711.938,108.369),l(-.222,-.241),l(-.077,-.271),l(.325,-.642),l(-.055,-.342),l(-.549,-.198),l(-.168,-.171),l(-.146,-.812),l(.583,-.386),l(.522,-.172),l(.646,-.373),l(.037,-.356),l(-.318,-.285),l(.277,-.3),l(.224,-.015),l(.661,.427),l(.373,.085),l(.532,-.201),l(-.004,-1.186),l(.455,-.187),l(.45,-.244),l(.074,-.743),l(.007,-.844),l(-.398,-.758),l(-.098,-.473),l(.166,-.216),l(.618,-.346),l(.063,.072),l(.507,.43),l(.904,.816),l(1.07,.842),l(1.083,.684),l(.627,.285),l(.528,.17),l(1.02,.198),l(.282,.042),l(.304,-.086),l(.866,-.66),l(.461,-.144),l(.002,.1),l(-.308,.358),l(-.335,.558),l(.198,.414),l(.469,.599),l(.197,.356),l(-.561,.272),l(-.447,.244),l(-.534,.158),l(-.365,.015),l(-.488,-.199),l(-.453,.015),l(-.363,.144),l(-.345,.229),l(-.754,.786),l(-.396,.5),l(-.26,.599),l(-.4,-.07),l(-.425,-.241),l(-2.031,-.965),l(-.461,-.085),l(-.72,.044),l(-1.038,.587),l(-.153,-.299),l(-.372,-.356),l(-.339,.029),l(-.266,.115),l(-.439,.272),l(.049,.299),l(1.16,.497),l(.56,.298),l(.302,.27),l(-.391,.214),l(-.303,.029),l(-.305,-.128),l(-.261,.043),l(-.324,.314),l(-.388,.471),l(-.347,.114),
+N(597.917,139.685),l(1.251,-1.545),l(.609,-.539),l(.348,-.239),l(.149,-.103),l(.417,-.016),l(.309,.294),l(.479,.208),l(1.659,.047),l(.371,.041),l(.312,.209),l(.329,.619),l(-.07,.156),l(.042,.24),l(.326,.294),l(.313,.069),l(.258,.238),l(.017,.282),l(-.217,.58),l(-.624,.06),l(-1.036,.062),l(-1.238,-.063),l(-.335,-.125),l(-.301,-.055),l(-.531,.313),l(-.544,.074),l(-.085,-.021),l(-.869,-.214),l(-.559,-.081),l(-.637,-.18),l(-.235,-.493),l(.092,-.113),
+N(362.764,152.723),l(.072,-.625),l(.395,-.876),l(.52,-.552),l(.488,-.566),l(.244,-.509),l(1.175,-2.559),l(.238,-.241),l(1.404,-1.175),l(.345,-.495),l(.051,-.918),l(.305,-1.088),l(.651,-1.075),l(.399,-.34),l(.404,-.198),l(.838,-.51),l(.361,-.495),l(.334,-.777),l(.428,-.851),l(1.635,-.04),l(2.511,0),l(2.677,-.001),l(1.718,.004),l(1.42,-.008),l(.027,.876),l(-.03,1.752),l(.002,.65),l(-.104,.396),l(-.56,-.011),l(-6.005,-.022),l(-.557,.074),l(-.047,.509),l(-.07,2.261),l(-.099,2.6),l(-.144,.128),l(-.809,.287),l(-.726,.315),l(-.575,.427),l(-.249,.383),l(-.01,.707),l(.164,1.539),l(.051,1.102),l(-.212,-.027),l(-.732,.033),l(-2.396,-.014),l(-5.055,-.056),l(-.474,-.013),
+N(514.551,145.841),l(-.374,.027),l(-.336,-.083),l(-.008,-.615),l(-.153,-.437),l(-.108,-.791),l(.187,-.607),l(.188,-.11),l(-.059,-.187),l(.177,-.607),l(.33,-.269),l(.312,.083),l(.069,.315),l(.26,.093),l(.063,.199),l(.116,.326),l(-.106,.42),l(.031,.708),l(.118,.254),l(-.104,.381),l(-.327,.467),l(-.275,.433),
+N(514.177,145.868),l(.374,-.027),l(.008,.288),l(.361,.14),l(.153,.128),l(.186,-.093),l(-.046,.443),l(.397,.001),l(.402,.127),l(.687,-.093),l(.103,-.21),l(.183,-.058),l(.218,.117),l(.424,-.042),l(.595,.112),l(.224,-.035),l(.079,-.105),l(1.358,.222),l(.732,-.14),l(-.022,-.292),l(.225,.175),l(.375,-.016),l(.157,-.099),l(.312,-.422),l(.232,-.073),l(.267,-.495),l(.131,-.297),l(.711,-.637),l(.813,-.889),l(.163,.105),l(.229,-.178),l(.85,-.708),l(.313,-.433),l(.15,.161),l(-.248,.42),l(-.107,.299),l(-.004,.176),l(.099,.064),l(.121,-.024),l(.454,.042),l(.09,.324),l(.001,.508),l(-.003,.358),l(-.49,.034),l(-.401,-.083),l(-.107,.396),l(.073,1.326),l(-.199,.34),l(-.536,.596),l(.003,.946),l(.024,2.075),l(.063,.183),l(-.152,.057),l(-.584,.469),l(-.839,-.108),l(-3.387,-.446),l(-3.362,-.375),l(-.261,-.902),l(-.548,-1.154),l(-1.043,-2.198),
+N(668.627,151.365),l(-.102,-.056),l(-.107,-.325),l(-.922,-1.212),l(-.332,-.987),l(-.03,-.438),l(.156,-.749),l(.546,-.792),l(1.312,-1.852),l(.259,-.184),l(.425,-.128),l(.229,-.184),l(.358,-.227),l(.228,.127),l(.554,.394),l(-.334,.424),l(-.084,.142),l(.023,.31),l(-.067,.622),l(-.203,.296),l(-.182,.354),l(-.065,.692),l(-.1,.494),l(-.317,.805),l(-.473,.707),l(-.417,.833),l(-.014,.353),l(-.114,.438),l(-.228,.142),
+N(389.765,144.543),l(.1,.084),l(.895,.531),l(2.054,1.344),l(.811,.575),l(3.283,2.241),l(1.924,1.26),l(1.292,.824),l(.397,.253),l(2.472,1.469),l(.181,.253),l(-.096,.396),l(.082,.183),l(.393,.28),l(1.111,1.039),l(.229,.027),l(.47,-.314),l(.588,.562),l(.375,.167),l(.748,.024),l(.309,.111),l(.277,.352),l(.099,.522),l(-.161,.679),l(.146,.564),l(2.176,-.408),l(.064,1.017),l(.034,2.203),l(.001,.96),l(-.08,.89),l(-.145,.919),l(-.434,1.246),l(-.596,.794),l(-.339,.271),l(-.29,.129),l(-2.533,.085),l(-1.808,.124),l(-.209,.072),l(-.562,.427),l(-.579,.272),l(-.678,-.053),l(-.581,-.081),l(-1.062,-.173),l(-.36,-.059),l(-.356,-.125),l(-.37,.073),l(-1.22,.713),l(-.947,.458),l(-.304,.228),l(-.314,.793),l(-.274,-.027),l(-.324,-.182),l(-.518,-.209),l(-.272,.101),l(-.638,.625),l(-.492,.667),l(-.393,.822),l(-.174,.227),l(-.45,.102),l(-.551,-.364),l(-.293,-.281),l(-.273,.058),l(-.397,.384),l(-.355,1.217),l(-.292,1.047),l(-.317,.369),l(-.543,.271),l(-.448,.158),l(-.257,.016),l(-.141,.255),l(.058,.749),l(-.133,.876),l(-.261,.92),l(-.172,.326),l(-.046,.156),l(-.08,.043),l(-.159,.1),l(-.604,.399),l(-.352,.059),l(-.148,-.239),l(-.117,-.381),l(-.004,-.297),l(-.147,-.211),l(-.257,-.041),l(-.239,.114),l(-.571,.483),l(-.362,.469),l(-.35,.228),l(-.455,-.436),l(-.566,-.321),l(-.352,.059),l(-.522,.54),l(-.559,-.901),l(-.194,-1.143),l(-.349,-.718),l(-.474,-.478),l(-.265,-.451),l(-.271,-.832),l(-.022,-.339),l(-.246,-.281),l(-.323,-.055),l(-.684,.428),l(-.3,.327),l(-.43,.243),l(-.565,-.152),l(-.356,-.153),l(-.338,-.026),l(-.475,.413),l(-.252,.256),l(-.536,-.265),l(-.882,-.715),l(-.18,-.183),l(-.113,-.028),l(.062,-.142),l(.004,-.565),l(-.082,-.833),l(-.265,-.337),l(-.554,-.322),l(-.181,-.197),l(-.22,-.479),l(-.144,-.663),l(-.251,-1.1),l(.057,-.339),l(.506,-.399),l(.332,-.284),l(.018,-.607),l(.181,-.552),l(.252,-.256),l(.402,-.073),l(.261,.111),l(.568,.83),l(.214,.168),l(.454,.082),l(.107,-.269),l(-.055,-.296),l(.06,-.212),l(.535,.124),l(.713,.137),l(.485,.054),l(.387,-.031),l(.945,-.344),l(1.953,-.026),l(6.457,-.01),l(.379,-1.613),l(-.724,-.787),l(-.186,-1.468),l(-.202,-2.386),l(-.325,-2.753),l(-.178,-1.736),l(-.19,-1.468),l(-.908,-7.962),l(-.049,-.776),l(3.231,-.089),l(.523,-.13),
+N(525.37,142.384),l(.312,-.429),l(.155,-.17),l(.084,.833),l(-.423,.707),l(-.118,.156),l(-.121,.024),l(-.099,-.064),l(.004,-.176),l(.107,-.299),l(.248,-.42),l(-.15,-.161),M(525.923,144.712),l(0,.22),l(.456,.762),l(.408,.465),l(.782,.634),l(.677,.394),l(1.008,.52),l(.392,.154),l(.277,.014),l(.576,-.029),l(.364,.112),l(.873,.973),l(.518,.648),l(.46,.422),l(.81,.365),l(.025,.212),l(-.67,1.06),l(-.615,.721),l(-.883,.807),l(-.776,1.541),l(-.242,.142),l(-.562,-.083),l(-.235,-.084),l(-.252,.071),l(-.278,.509),l(-.062,1.115),l(.001,.791),l(.134,.621),l(-.403,.142),l(-1.046,.073),l(-.627,.27),l(-.367,.283),l(-.29,.495),l(-.131,.551),l(-.204,.283),l(-.444,.255),l(-.544,.1),l(-.292,0),l(-.386,-.042),l(-.326,.029),l(-.382,.283),l(-.22,.297),l(-.125,.508),l(.003,.353),l(-.091,.311),l(-.631,.396),l(-.344,.043),l(-.776,-.21),l(-.717,.058),l(-.896,.27),l(-.768,.298),l(-.283,.099),l(-.416,.145),l(-.241,-.306),l(-.483,-.689),l(.006,-.296),l(-.127,-.253),l(-.933,-1.364),l(-.604,-.971),l(-.226,-.634),l(-.092,-.663),l(1.691,-.815),l(2.35,-1.213),l(5.346,-2.982),l(-.155,-1.453),l(-.581,-.914),l(-.063,-.183),l(-.024,-2.075),l(-.003,-.946),l(.536,-.596),l(.199,-.34),l(-.073,-1.326),l(.107,-.396),l(.401,.083),l(.49,-.034),
+N(405.733,173.04),l(-.568,-.971),l(-.562,-.025),l(-.37,.044),l(-.516,-.181),l(-.97,-.757),l(-.114,-.226),l(.335,-.439),l(-.018,-.212),l(-.179,-.268),l(-.502,-.42),l(-.389,-.266),l(-.422,-.492),l(-.426,-.93),l(-.019,-.396),l(.173,-.665),l(.581,.081),l(.678,.053),l(.579,-.272),l(.562,-.427),l(.209,-.072),l(1.808,-.124),l(2.533,-.085),l(.29,-.129),l(.339,-.271),l(.596,-.794),l(.434,-1.246),l(.145,-.919),l(.08,-.89),l(-.001,-.96),l(-.034,-2.203),l(-.064,-1.017),l(1.672,-.321),l(1.82,-.364),l(3.084,-2.617),l(.834,-.655),l(2.308,-1.454),l(1.607,-.956),l(4.012,-2.241),l(1.632,-.843),l(.265,-.186),l(.832,.137),l(1.646,.442),l(1.008,.333),l(.258,.182),l(1.192,.911),l(.231,-.157),l(1.519,-.729),l(.364,2.145),l(.169,1.298),l(.42,1.028),l(.554,.802),l(.703,.604),l(-.388,.722),l(-.265,.99),l(-.168,1.088),l(-.084,.989),l(.022,.537),l(-.062,.707),l(-.019,1.045),l(-.034,1.088),l(-.056,.466),l(-2.43,2.613),l(-.591,.78),l(-.87,1.333),l(-.572,.794),l(-.007,.678),l(.123,.719),l(.014,.269),l(-.951,.034),l(-.437,.2),l(-.453,.299),l(-.761,.697),l(-.259,.058),l(-.609,-.208),l(-.724,-.193),l(-.884,-.221),l(-.531,-.04),l(-.709,.047),l(-.628,.103),l(-.774,.287),l(-.403,.327),l(-.629,.399),l(-.273,.059),l(-.934,.005),l(-.965,-.277),l(-1.173,-.742),l(-.354,-.083),l(-.467,.116),l(-1.337,.544),l(-.37,.002),l(-.209,-.098),l(-1.095,-1.223),l(-.821,-.277),l(-1.111,-.121),l(-1.174,.108),l(-1.064,.188),l(-.676,.4),l(-.687,1.614),l(-.353,.482),l(-.158,.849),l(-.092,.961),l(-.902,-.503),l(-.727,-.589),l(-.339,-.28),l(-.321,.073),l(-.577,.3),
+N(431.763,171.063),l(-.351,-.407),l(-.575,-.52),l(-.173,-.394),l(-.014,-.269),l(-.123,-.719),l(.007,-.678),l(.572,-.794),l(.87,-1.333),l(.591,-.78),l(2.43,-2.613),l(.056,-.466),l(.034,-1.088),l(.019,-1.045),l(.062,-.707),l(-.022,-.537),l(.084,-.989),l(.168,-1.088),l(.265,-.99),l(.388,-.722),l(-.703,-.604),l(-.554,-.802),l(-.42,-1.028),l(-.169,-1.298),l(-.364,-2.145),l(1.818,-.858),l(.41,-.059),l(5.231,2.554),l(4.941,2.372),l(5.577,2.792),l(1.981,.963),l(-.02,1.045),l(-.016,.946),l(-.036,.636),l(.085,2.5),l(-.038,.749),l(.036,1.002),l(.031,1.229),l(-.04,.283),l(-.839,-.009),l(-1.245,.05),l(-.229,.143),l(-.417,1.245),l(-.583,.809),l(-.122,.438),l(.131,.677),l(-.149,.212),l(-.718,.428),l(-.053,.24),l(.342,.662),l(-.087,.34),l(-.542,.596),l(-.316,.609),l(.219,.352),l(.517,-.088),l(.338,.012),l(.141,.225),l(.221,1.228),l(.137,.522),l(.155,.295),l(.444,.407),l(.266,.465),l(.026,.367),l(-.15,.425),l(-.559,-.208),l(-.321,-.012),l(-.322,.086),l(-.939,.613),l(-.372,.228),l(-.165,.382),l(-.005,.41),l(-.196,.284),l(-2.649,2.275),l(-.386,.087),l(-2.181,.055),l(-.434,.059),l(-.209,.199),l(-.117,.806),l(-.646,1.176),l(-.258,.143),l(-.368,.031),l(-.881,-.009),l(-.818,.273),l(-.754,.386),l(-.466,.271),l(-.224,.03),l(-.225,-.069),l(-.494,-.661),l(-1.363,.686),l(-.449,.158),l(-.24,-.027),l(-.096,-.084),l(-.208,-.183),l(-.382,-1.057),l(-.638,-1.07),l(-1.343,-1.179),l(-1.088,-1.067),l(.323,-.539),l(.29,-.312),l(.24,-.1),l(.481,.082),l(1.187,.191),l(.674,-.032),l(.225,-.143),l(-.047,-.127),l(-.208,-.21),l(-.381,-.633),l(-.205,-.578),l(-.169,-1.228),l(.134,-.651),l(-.119,-1.2),l(-.395,-.887),l(-.923,-1.238),l(-.208,-.083),l(-.627,-.109),
+N(627.173,150.012),l(.483,-.229),l(.515,-.13),l(.341,.012),l(.597,.392),l(.325,.097),l(.584,-.413),l(.332,-.115),l(1.595,-.052),l(.807,-.117),l(.341,-.157),l(.696,-.554),l(.521,-.328),l(.298,-.101),l(.623,.575),l(.771,.235),l(.66,.053),l(.777,-.047),l(.237,.21),l(.056,.38),l(-.472,.75),l(.096,.521),l(.273,.365),l(.943,.615),l(.621,.166),l(.909,.107),l(.197,.143),l(-.19,.132),l(-.826,.482),l(.106,.465),l(-.203,.212),l(-1.261,-.054),l(-.136,.198),l(.057,.395),l(-.283,.382),l(-.585,.792),l(-.221,.142),l(-.533,.241),l(-.171,.127),l(-.27,.396),l(-.303,.932),l(-.388,.975),l(.268,.225),l(.469,.563),l(1.112,1.071),l(.023,.24),l(.042,.522),l(.087,.254),l(.42,.493),l(1.096,.83),l(1.282,1.296),l(.26,.197),l(.636,.069),l(.313,.38),l(.282,1.016),l(.302,.578),l(.638,.605),l(.293,.663),l(.341,1.382),l(.524,2.809),l(-.295,.438),l(-.235,.495),l(.05,.819),l(-.095,.41),l(.056,.664),l(-.027,.099),l(-.364,.551),l(-.447,.439),l(-.254,.127),l(-.509,.1),l(-.419,.17),l(-.501,.354),l(-.591,.622),l(-.579,.354),l(-.325,.043),l(-.512,-.197),l(-.404,-.31),l(-.179,-.141),l(-.153,.424),l(.051,.494),l(.048,.353),l(-.205,.721),l(-.388,.424),l(-.326,.071),l(-.235,-.07),l(-.246,.481),l(-.427,.326),l(-.523,.142),l(-.417,.213),l(-.459,.565),l(-.196,.269),l(-.406,.297),l(-.264,.099),l(-.365,-.042),l(.078,-.861),l(.1,-1.313),l(.151,-.494),l(.215,-.283),l(-.02,-.353),l(-.475,-.437),l(-.749,-.238),l(-.091,-.066),l(.3,-.289),l(.646,-.229),l(.915,-.528),l(.599,-.229),l(.497,.011),l(.688,.194),l(.17,-.27),l(-.03,-.197),l(-.568,-.435),l(-.216,-.422),l(.234,-.425),l(.99,-.571),l(.521,-.229),l(.932,-.443),l(.599,-.187),l(.385,-.285),l(.217,-.509),l(-.054,-1.073),l(.05,-.424),l(.076,-.367),l(-.455,-1.014),l(-.029,-.663),l(.215,-.905),l(.155,-.918),l(-.064,-.578),l(-.214,-.437),l(-.529,-.477),l(-.072,-.282),l(.226,-.439),l(-.136,-.395),l(-.358,-.308),l(-.685,-.391),l(-.471,-.52),l(-.57,-.914),l(-1.683,-2.121),l(-.698,-.772),l(-.637,-.646),l(-.632,-.476),l(-1.234,-.741),l(-.162,-.098),l(-.043,-.494),l(.277,-.369),l(.311,-.101),l(.476,.068),l(.287,-.058),l(.261,-.185),l(.255,-.326),l(-.009,-.508),l(-.87,-.968),l(-.434,-.675),l(-.262,-.083),l(-.39,.171),l(-.509,.483),l(-.287,.058),l(-.47,-.195),l(-.607,-.434),l(-.334,-.689),l(-.338,-.929),l(-.543,-.604),l(-.613,-.575),l(-.45,-.745),
+N(217.961,150.385),l(.304,-.043),l(.84,-.27),l(-.17,-.254),l(-.312,-.112),l(-.369,-.056),l(-.651,.016),l(-.497,-.042),l(-.645,.157),l(-1.193,.92),l(-.371,.029),l(-.653,.001),l(-.211,.113),l(-.189,.452),l(-.396,.284),l(-.32,.043),l(-.786,.086),l(.259,-.325),l(.473,-.312),l(-.128,-.593),l(.282,-.382),l(.114,-.099),l(1.258,-.61),l(1.625,-.47),l(1.164,-.087),l(.842,-.157),l(.825,.041),l(.566,-.044),l(.73,.168),l(.848,.083),l(.603,.197),l(.557,.112),l(.477,.013),l(.499,.268),l(.573,.536),l(.382,.253),l(.581,.168),l(.768,.111),l(1.229,.351),l(1.02,.492),l(.453,.31),l(.374,.55),l(.33,.141),l(.479,.041),l(1.704,.519),l(1.018,.167),l(.327,.239),l(-.344,.58),l(.233,.155),l(.559,.042),l(.756,-.072),l(.495,.168),l(.507,.38),l(.591,.281),l(.381,.296),l(-.233,.085),l(-.981,.087),l(-1.15,.398),l(-.626,.058),l(-1.054,-.209),l(-.9,-.041),l(-.934,.186),l(-.943,.115),l(-.484,.029),l(-.449,-.07),l(.353,-.382),l(.728,-.623),l(.173,-.396),L(229,154.204),l(-.181,-.127),l(-.622,-.14),l(-.7,.001),l(-.603,-.112),l(-.651,-.338),l(-.141,-.748),l(-.258,-.536),l(-.218,-.155),l(-.396,-.027),l(-1.005,.044),l(-.836,-.139),l(-.621,-.225),l(-.956,-.493),l(-.739,-.238),l(-.615,-.069),l(-1.154,-.068),l(-.489,-.098),l(-.855,-.352),
+N(634.036,168.444),l(.808,-.64),l(.121,-.438),l(-.002,-.945),l(-.157,-.507),l(-.419,-.703),l(-.979,-1.279),l(-.255,-.464),l(-.107,-.366),l(-.058,-1.524),l(-.435,-.632),l(-.688,-.659),l(-.285,-.535),l(-.052,-.282),l(-.266,-.153),l(-.893,-.192),l(-.403,-.012),l(-.286,.453),l(-.2,.538),l(-.543,.257),l(-.223,.072),l(-.59,-.265),l(-.835,-.348),l(-.346,.03),l(-1.173,1.178),l(-.37,.411),l(-.481,-.138),l(-.145,-.324),l(.027,-.494),l(.117,-.438),l(.528,-1.569),l(.085,-.41),l(-.249,-1.311),l(-.045,-.113),l(-.414,.045),l(-.489,.2),l(-.423,.003),l(-.186,-.154),l(-.066,-.367),l(.106,-.805),l(-.01,-.423),l(-.118,-.168),l(-.295,-.182),l(-.541,-.166),l(.193,-.185),l(.582,-.455),l(.442,-.581),l(.53,-.61),l(.502,-.355),l(.178,.196),l(.321,.21),l(.769,.08),l(.266,-.213),l(.109,-.339),l(-.119,-.521),l(-.228,-.366),l(-.138,-.592),l(.043,-.325),l(.24,-.241),l(.679,-.314),l(.45,.745),l(.613,.575),l(.543,.604),l(.338,.929),l(.334,.689),l(.607,.434),l(.47,.195),l(.287,-.058),l(.509,-.483),l(.39,-.171),l(.262,.083),l(.434,.675),l(.87,.968),l(.009,.508),l(-.255,.326),l(-.261,.185),l(-.287,.058),l(-.476,-.068),l(-.311,.101),l(-.277,.369),l(.043,.494),l(.162,.098),l(1.234,.741),l(.632,.476),l(.637,.646),l(.698,.772),l(1.683,2.121),l(.57,.914),l(.471,.52),l(.685,.391),l(.358,.308),l(.136,.395),l(-.226,.439),l(.072,.282),l(.529,.477),l(.214,.437),l(.064,.578),l(-.155,.918),l(-.209,.114),l(-.975,.429),l(-.3,.072),l(-.373,-.351),l(-.444,-.181),l(-.476,.186),l(-.392,.285),l(.107,.296),l(.187,.182),l(.103,.211),l(-.095,.24),l(-.248,.058),l(-.469,-.251),l(-.341,-.111),l(-.736,-.165),l(-.533,-.251),
+N(60.074,72.607),l(-.099,.228),l(-.491,.472),l(-.395,.183),l(-.462,.062),L(58,73.461),l(-.961,-.362),l(-.153,-.197),l(.169,-.289),l(.54,-.274),l(.341,-.32),l(.716,.364),l(.3,.091),l(.465,-.26),l(.215,-.213),l(.064,-.366),l(.485,-.047),l(1.107,.135),l(.536,.334),l(.133,.213),l(-.756,.062),l(-.429,0),l(-.59,.184),l(-.11,.092),M(40.092,77.571),l(-.729,-.029),l(-.097,-.24),l(.011,-.3),l(.802,-.243),l(.326,-.211),l(.593,-.423),l(.448,-.137),l(.646,-.077),l(1.427,.253),l(.711,.24),l(-.079,.211),l(-.303,.046),l(-.754,-.074),l(-.496,.031),l(-1.077,.183),l(-.269,.226),l(-1.161,.543),M(38.426,77.979),l(-.515,-.209),l(-.139,-.285),l(.381,-.227),l(.674,.27),l(.093,.195),l(-.122,.15),l(-.372,.105),M(37.896,78.449),l(-.256,.084),l(-.558,.151),l(-1.109,-.058),l(-.387,.135),l(-.398,.434),l(-.31,.15),l(-.854,-.207),l(-.135,-.224),l(.497,-.359),l(.5,-.315),l(.955,-.166),l(.863,-.346),l(.39,.089),l(.461,.224),l(.341,.409),M(29.628,81.29),l(-.168,-.594),l(-.324,-.476),l(.839,-.136),l(.424,.088),l(.436,.238),l(-.244,.268),l(-.26,.06),l(-.073,.297),l(-.22,.09),l(-.412,.164),M(27.543,81.591),l(-.39,.031),l(-.741,.165),l(-.311,-.133),l(-.088,-.178),l(.104,-.119),l(.336,-.268),l(.294,-.09),l(.584,.222),l(.212,.371),M(54.394,157.986),l(-.559,-.356),l(-.044,-.884),l(-.243,-.677),l(.482,-.402),l(-.035,-.2),l(-.156,-.26),l(.052,-.149),l(.173,-.046),l(.354,.158),l(.652,.279),l(.593,.425),l(-.015,.275),l(.238,.046),l(.12,.287),l(.306,.149),l(-.062,.161),L(56,156.933),l(-.172,.204),l(-.766,.195),l(-.374,.23),l(-.295,.425),M(23.015,59.92),l(-1.613,-.646),l(-.75,-.205),l(-.792,-.062),l(-.9,.065),l(-.291,-.095),l(-.431,-.222),l(.179,-.287),l(.516,-.049),l(1.135,.221),l(.579,-.001),l(.543,-.081),l(.538,-.001),l(.828,.285),l(1.725,.362),l(.429,.237),l(.046,.111),l(-.569,-.03),l(-.646,.033),l(-.527,.365),M(99.855,70.993),l(.467,.929),l(-.071,.167),l(-.879,-.272),l(-.621,-.075),l(.067,.441),l(-.056,.228),l(-.935,-.607),L(97.03,71.41),l(.396,-.458),l(.263,-.153),l(.612,-.078),l(.784,.38),l(.771,-.108),M(100.975,73.606),l(.128,.272),l(-.086,.273),l(-.318,.483),l(-.559,-.815),l(-.597,-.909),l(-.04,-.228),l(.095,-.213),l(.407,.029),l(.568,.197),l(.402,.91),M(106.858,78.207),l(-1.872,-1.166),l(-.566,-.555),l(.01,-.467),l(-.559,-.843),l(.071,-.106),l(.456,.06),l(.274,.256),l(1.165,.48),l(.086,.196),l(-.059,.136),l(-.149,.226),l(.296,.436),l(.839,.374),l(.007,.974),M(140.191,127.819),l(-.043,-.094),l(-.198,-.36),l(-.049,-.067),l(-.032,.042),l(-.028,.05),l(-.04,-.092),l(.002,-.664),l(-.331,-.604),l(-.472,-.451),l(-.661,-.451),l(-.512,-.197),l(-.114,-.052),l(-.145,.034),l(.002,.092),l(-.088,.025),l(-.1,-.042),l(-.146,-.143),l(.076,-.076),l(-.073,-.202),l(-.228,-.252),l(-.283,-.025),l(-.312,.084),l(-.932,-.336),l(-.286,-.328),l(-.428,-.244),l(-.383,.042),l(-.932,-.16),l(-.721,.051),l(-.12,-.185),l(-.234,-.067),l(-.046,-.177),l(.094,-.117),l(-.157,-.504),l(.133,-.464),l(-.227,-.096),l(-.127,.008),l(-.249,-.134),l(0,-.101),l(.075,-.093),l(-.029,-.219),l(-.347,-.185),l(-.254,-.286),l(-.415,-.219),l(-.391,-.623),l(-.202,-.076),l(-.203,-.311),l(-.409,-.219),l(-.156,-.529),l(-.002,-.227),l(.178,.007),l(.147,-.164),l(.029,-.326),l(-.208,-.251),l(-.192,-.045),l(-.22,.037),l(-.303,-.126),l(-.535,-.514),l(.078,-.21),l(-.091,-.312),l(-.129,-.067),l(-.044,-.463),l(.058,-.152),l(.119,-.025),l(.099,.067),l(.073,.076),l(-.086,.101),l(.153,.202),l(.285,.126),l(.116,.118),l(.203,.017),l(-.385,-.564),l(-.183,-.144),l(-.021,-.236),l(-.184,-.109),l(-.051,-.344),l(-.13,.006),l(-.011,.144),l(.048,.446),l(-.093,.017),l(-.293,-.194),l(-.119,.042),l(-.516,-.404),l(-.136,-.363),l(-.377,-.514),l(-.531,-.379),l(-.624,-.583),l(-.123,-.142),l(.114,-.101),l(-.327,-.751),l(.161,-.43),l(-.254,-.479),l(-.22,-.355),l(-.738,-.782),l(-.104,-.299),l(.099,-.627),l(.252,-.628),l(.166,-.357),l(.059,-.856),l(-.215,-.785),l(-.692,-1.486),l(-.153,-.916),l(.096,-.287),l(.231,-.244),l(.402,-.201),l(.365,-.717),l(-.365,-.573),l(-.066,-.33),l(.424,-1.593),l(.153,-.575),l(.061,-.634),l(.091,-.778),l(.019,-.179),l(-.153,-.16),l(.08,-.231),l(-.103,-.167),l(.157,-.077),l(-.03,-.186),l(-.046,-.186),l(-.031,-.103),l(-.004,-.058),l(.322,.096),l(.209,-.019),l(.062,-.097),l(-.211,.006),l(-.614,-.122),l(.062,-.707),l(-.103,-.328),l(.017,-.277),l(.587,-.225),l(-.345,-.019),l(-.16,-.142),l(-.129,0),l(-.053,.045),l(.042,.116),l(-.12,.052),l(-.133,-.979),l(-.927,-1.463),l(-.017,-.465),l(.129,-.131),l(.544,.086),l(.632,.217),l(.785,.114),l(.641,.028),l(.546,-.044),l(.415,.086),l(.547,.318),l(.039,.435),l(-.42,.407),l(-.413,.347),l(.532,.146),l(.184,.188),l(.251,.169),l(.029,-.228),l(.161,-.232),l(.393,-.305),l(.21,-.581),l(.102,-.465),l(-.064,-.421),l(-.356,-.958),l(-.158,-.305),l(-.655,-.516),l(.194,.013),l(2.579,.001),l(1.335,.022),l(4.588,-.025),l(3.938,.008),l(2.87,-.001),l(1.687,.006),l(5.117,-.028),l(.74,.011),l(4.13,.021),l(1.089,-.035),l(3.821,.023),l(.875,-.005),l(3.617,-.004),l(4.84,.018),l(.601,-.003),l(2.935,.014),l(2.131,-.012),l(2.781,.029),l(2.915,-.016),l(2.105,.003),l(1.348,-.007),l(2.798,.029),l(2.687,-.029),l(.68,.003),l(-.387,-.588),l(-.131,-.347),l(.501,-.036),l(.896,.748),l(.279,.371),l(.468,.46),l(.833,.451),l(.518,-.076),l(1.425,.208),l(.02,.185),l(.271,-.012),l(.338,.48),l(.16,-.247),l(.502,.013),l(.241,.271),l(.469,.086),l(.064,.185),l(.506,.098),l(.573,-.141),l(.219,-.06),l(.412,-.191),l(.373,-.075),l(.028,.282),l(.197,.116),l(.855,-.083),l(.474,.041),l(.156,.115),l(.196,.144),l(.542,-.049),l(.707,.074),l(1.469,-.592),l(.805,-.189),l(.797,.227),l(.977,.386),l(3.975,1.576),l(2.15,1.061),l(.101,.429),l(.46,.465),l(.628,-.024),l(.178,.135),l(.184,.294),l(.916,.181),l(.307,.235),l(-.11,.318),l(.26,.33),l(2.529,1.05),l(.876,3.16),l(.054,.545),l(-.028,.746),l(-.377,.576),l(-.294,.544),l(-.264,.433),l(-.414,.294),l(-.707,.525),l(-.044,.218),l(.012,.33),l(.371,.427),l(.497,.169),l(.573,.068),l(.524,-.117),l(.925,-.506),l(.939,-.478),l(.88,-.262),l(.919,-.062),l(.944,-.163),l(1.464,-.452),l(.875,-.427),l(-.047,-.362),l(-.16,-.471),l(-.018,-.319),l(.162,-.375),l(.47,-.203),l(.93,-.091),l(1.123,.01),l(1.305,.138),l(1.156,-.197),l(.44,-.275),l(.163,-.512),l(.146,-.434),l(.545,-.164),l(1.754,-.814),l(.534,-.305),l(.968,-.523),l(1.76,-.009),l(2.508,.029),l(1.855,.004),l(1.093,.095),l(.174,-.375),l(.363,-.435),l(.402,-.06),l(1.161,.124),l(1.139,-1.45),l(1.139,-2.22),l(.514,-.626),l(.632,-.526),l(.273,.085),l(.505,.36),l(.381,.085),l(.41,-.176),l(.771,.025),l(.488,.288),l(.174,.274),l(.31,2.819),l(-.077,.229),l(.606,.231),l(.224,0),l(.042,.154),l(-.143,.069),l(.02,.256),l(-.192,.077),l(.16,.291),l(.188,-.153),l(.349,.495),l(-.268,.281),l(.299,-.04),l(.171,.093),l(-.511,.374),l(-.509,.093),l(-.297,-.12),l(-.013,.253),l(-.138,.067),l(-.077,-.107),l(-.231,-.08),l(-.277,.133),l(-.101,.28),l(-.171,-.013),l(-.15,.16),l(-.175,-.347),l(-.746,.28),l(-.204,-.093),l(.12,.413),l(-.666,-.213),l(.199,-.48),l(-.149,-.04),l(-.364,.52),l(-.332,.56),l(-.342,.333),l(-.324,-.227),l(-.249,.439),l(-.346,-.08),l(.122,-.307),l(-.325,.253),l(.165,.16),l(-.326,.293),l(-.318,-.133),l(.105,-.226),l(-.654,.253),l(.065,.359),l(-.264,.04),l(-.161,.373),l(-.352,.106),l(-.333,.679),l(-.404,.505),l(.173,.146),l(.068,.212),l(.168,.053),l(.083,-.08),l(.169,.013),l(-.122,.146),l(-.547,.106),l(.053,.093),l(-.392,.292),l(-.068,.159),l(.337,.027),l(.282,.093),l(.599,.704),l(.055,.398),l(.399,.106),l(.691,-.239),l(-.022,-.186),l(-.14,-.027),l(-.254,-.279),l(-.097,-.04),l(-.009,-.066),l(.196,0),l(.23,.133),l(.218,.358),l(.031,.425),l(-1.599,.292),l(-.032,-.385),l(-.124,-.066),l(-.109,.226),l(-.164,.04),l(-.03,.093),l(-.105,-.106),l(-.159,.266),l(-.164,.04),l(-.294,.04),l(-.045,-.332),l(.198,-.332),l(-.443,.119),l(-.154,-.146),l(-.082,.252),l(-.087,.664),l(-1.429,.132),l(-1.694,.159),l(-1.182,.345),l(-.787,.358),l(-.097,.212),l(-.32,.053),l(-.144,.172),l(-.032,-.04),l(.308,-.756),l(.024,-.106),l(-.071,.027),l(-.41,.994),l(-.079,-.08),l(-.406,.292),l(.218,.318),l(.553,.093),l(-.46,1.515),l(-.302,.429),l(-.259,-.092),l(.043,.251),l(-.062,.185),l(-.237,.145),l(-.462,.501),l(-.292,.304),l(-.167,.026),l(-.075,-.119),l(.177,-.31),l(-.113,-.178),l(-.43,.013),l(-.447,-.343),l(-.148,-.053),l(-.329,-.541),l(.315,-.257),l(.151,-.245),l(-.271,.119),l(-.362,.37),l(.489,.845),l(.033,.356),l(.387,.581),l(.28,.066),l(.104,.765),l(-.101,.238),l(-.151,.23),l(-.125,-.013),l(-.487,.666),l(-.396,.798),l(.034,.053),l(-.13,.132),l(-.107,-.125),l(-.374,.725),l(.026,.125),l(-.226,.04),l(-.137,-.263),l(.342,-.864),l(.195,-.29),l(.247,-.119),l(.061,-.237),l(-.093,-.059),l(-.374,.119),l(.226,-.383),l(-.218,.04),l(-.176,-.093),l(.012,-.191),l(.242,-.04),l(-.077,-.33),l(-.439,.296),l(-.241,-.204),l(-.157,.053),l(-.23,-.396),l(.355,-.171),l(.357,-.053),l(-.005,-.06),l(-.604,-.316),l(-.092,.165),l(-.072,0),l(.107,-.323),l(.089,-.02),l(.21,.159),l(.131,-.06),l(-.098,-.224),l(-.353,-.066),l(-.065,-.112),l(.096,-.112),l(.336,.02),l(.193,-.284),l(-.281,.046),l(-.158,-.059),l(.241,-.37),l(.652,-.152),l(-.328,-.06),l(.146,-.409),l(-.28,.093),l(-.096,.132),l(.11,.079),l(-.315,.191),l(-.035,-.224),l(-.093,.053),l(.051,.224),l(-.081,.086),l(-.051,-.158),l(-.097,-.066),l(-.103,.416),l(-.447,-.079),l(.402,.501),l(-.294,.666),l(.07,.237),l(.272,.488),l(-.055,.139),l(-.466,-.317),l(-.1,-.211),l(.026,.205),l(.174,.218),l(.421,.237),l(.132,.508),l(-.631,-.402),l(-.354,-.007),l(-.118,-.283),l(-.155,-.053),l(.066,.25),l(-.541,-.323),l(-.33,.04),l(.015,-.29),l(.427,-.323),l(-.428,.079),l(-.19,.468),l(.204,.231),l(.457,.046),l(.202,.25),l(.954,.297),l(-.047,.092),l(.554,.165),l(.158,.132),l(-.22,.468),l(-.227,.06),l(-1.042,-.804),l(.708,.811),l(.626,.171),l(-.248,.092),l(.323,.079),l(-.045,.079),l(.061,.06),l(-.034,.25),l(-.312,-.191),l(-.071,.073),l(.104,.211),l(-.216,.02),l(-.656,-.56),l(-.023,.026),l(.419,.475),l(.309,.158),l(.182,-.026),l(.191,.21),l(.018,.31),l(-.298,.059),l(-.492,-.534),l(-.474,-.198),l(-.199,.16),l(.046,.044),l(.44,.145),l(.488,.382),l(-.047,.237),l(.442,-.033),l(.031,-.119),l(.748,.119),l(.151,.382),l(.406,1.212),l(.448,.803),l(-.14,-.092),l(-.262,-.349),l(-.059,-.132),l(-.359,-1.172),l(-.147,-.277),l(-.056,.31),l(.135,0),l(.034,.198),l(-.292,-.066),l(.173,.283),l(.144,.099),l(.228,.58),l(-.144,-.053),l(-.211,-.382),l(.002,.224),l(-.52,-.303),l(-.06,.059),l(.266,.303),l(-.247,.119),l(-.526,-.204),l(.225,.204),l(-.375,.211),l(-.173,-.02),l(-.251,-.21),l(-.024,-.217),l(.083,-.158),l(-.081,-.053),l(-.091,.204),l(.044,.23),l(.116,.211),l(-.107,.158),l(.894,.02),l(.571,-.145),l(.125,.165),l(-.113,.191),l(-.072,.369),l(.14,.066),l(.092,-.257),l(.135,-.369),l(.18,-.105),l(.266,.31),l(.047,.296),l(-.166,.25),l(-.163,-.013),l(-.063,-.099),l(-.316,.474),l(-.254,.197),l(-.483,-.053),l(-.203,-.065),l(-.147,-.066),l(-.136,-.245),l(-.151,.014),l(.141,.244),l(-.075,.013),l(-.538,-.125),l(-.436,-.151),l(.162,.185),l(.269,.026),l(.833,.335),l(-.034,.119),l(-.396,.145),l(.247,.007),l(-.19,.25),l(-.281,.138),l(-.149,0),l(-.481,-.375),l(.242,.395),l(.43,.164),l(.302,-.171),l(.292,.026),l(.11,-.204),l(.04,.178),l(.217,.04),l(.048,.079),l(-.428,.322),l(-.013,.085),l(-.261,.072),l(-1.498,.214),l(-.865,.895),l(-.487,.609),l(-.13,.127),l(-.935,.143),l(-.528,.128),l(-.617,.241),l(-.678,.539),l(-.225,.424),l(-.096,.354),l(-.819,.694),l(-.693,.383),l(-.429,.199),l(-.797,.086),l(-.35,.58),l(-.177,.198),l(-.809,1.125),l(-.273,.781),l(-.459,1.249),l(.236,1.455),l(.387,.925),l(.456,.873),l(.934,1.562),l(.352,1.746),l(.486,1.194),l(-.075,.092),l(.287,.276),l(.123,.333),l(.062,.827),l(-.301,1.536),l(-.064,.278),l(-.31,.415),l(.108,.424),l(-.02,.252),l(-.393,.551),l(-.017,-.092),l(.129,-.241),l(-.025,-.138),l(-.256,.035),l(-.38,.137),l(-.291,-.126),l(-.509,.138),l(-.12,-.329),l(.014,-.233),l(-.567,-1.068),l(-.764,-.138),l(-.204,-.352),l(-.113,-.819),l(-.423,-.229),l(-.144,-.702),l(-.373,.093),l(-.608,-1.08),l(-.375,-.482),l(.296,0),l(.375,-.438),l(.048,-.226),l(-.167,-.226),l(-.471,.407),l(-.277,-.208),l(.126,-.573),l(.147,-.758),l(.158,-1.043),l(-.293,-.452),l(-.258,-.169),l(-.496,-.126),l(-.832,-.987),l(-.875,-.804),l(-.528,-.168),l(-.43,.072),l(-.536,.298),l(-.456,.354),l(-1.202,.299),l(-.273,-.213),l(-.131,-.62),l(-.253,-.254),l(-.264,-.113),l(-.752,-.069),l(-.516,-.296),l(-.22,-.233),l(-.504,.138),l(-1.052,.115),l(-.653,-.184),l(-.047,.298),l(-.64,.099),l(-.183,0),l(-.578,-.926),l(-.238,.781),l(-.447,-.135),l(-.65,.001),l(-1.328,-.04),l(-.672,.439),l(-.39,.055),l(-1,-.459),l(-.096,.009),l(-.142,.014),l(-.362,.528),l(.201,.229),l(.303,0),l(.211,0),l(.537,-.207),l(.406,.092),l(.676,.482),l(-.68,.373),l(.02,.254),l(.263,.353),l(.593,.146),l(.229,.217),l(.35,.334),l(-.533,.136),l(-.503,-.084),l(-.276,-.419),l(-.79,-.271),l(-.224,-.211),l(-.265,-.056),l(-.013,.02),l(-.209,.32),l(.209,.154),l(.248,.183),l(-.248,.179),l(-.069,.05),l(-.447,-.459),l(-.476,.192),l(-.287,.291),l(-1.025,-.472),l(-.419,-.494),l(-1.16,-.642),l(-.615,.066),l(.554,.393),l(-.307,.187),l(-1.17,-.083),l(-.886,-.252),l(-.896,-.168),l(-1.547,.173),l(-.632,.328),l(-.392,-.015),l(-.433,-.031),l(-.135,-.49),l(-.333,.057),l(-.112,.184),l(.474,.731),l(-.877,.64),l(-.808,.577),l(-.915,.317),l(-.419,.043),l(-.414,-.056),l(-.728,-.111),l(-.126,.198),l(.437,.437),l(-.239,.396),l(-.327,.199),l(-.631,.114),l(-.737,.27),l(-.268,.17),l(.558,.352),l(.111,.169),l(-.659,.694),l(-.154,.297),l(-.012,.848),l(.144,.636),l(.271,.762),l(.425,.903),l(-.347,-.119),l(-.816,-.377),l(-.296,.001),l(-.416,.116),l(-.264,-.069),l(-1.029,-.56),l(-.921,-.32),l(-.375,-.365),l(-.336,-.592),l(-.332,-.932),l(-.078,-.467),l(-.268,-.253),l(-.657,-.576),l(-.845,-1.042),l(-.744,-1.227),l(-.663,-1.029),l(-.363,-.366),l(-.412,-.252),l(-.783,-.321),l(-.475,-.082),l(-.643,.018),l(-.468,.201),l(-.576,.541),l(-.418,.413),l(-.283,.37),l(-.416,.158),l(-.501,-.011),l(-.337,-.069),l(-1.104,-.503),l(-1.092,-.659),l(-.445,-.549),l(-.318,-.847),l(-.284,-.678),l(-.179,-.226),l(-.708,-.491),l(-.837,-.519),l(-.766,-.632),l(-.631,-.662),l(-.209,-.112),l(-1.892,-.046),l(-1.858,-.003),l(-.096,.892),l(-.213,.101),l(-1.867,.011),l(-.966,-.037),l(-1.544,-.02),l(-1.662,-.019),l(-.338,-.055),l(-3.516,-1.112),l(-2.811,-.933),l(-1.186,-.39),l(-.267,-.154),l(-.316,-.31),l(-2.381,.084),l(-2.367,.155),l(-.34,.017),M(49.818,152.776),l(-.122,.086),l(-.279,.03),l(-.111,-.131),l(-.177,-.005),l(-.324,.051),l(-.304,-.39),l(-.071,-.263),l(.339,-.01),l(.299,-.253),l(.188,.218),l(.106,.294),l(.223,.096),l(.233,.279),M(52.785,154.312),l(-.155,-.081),l(-.085,-.356),l(-.461,-.321),l(.095,-.229),l(.143,-.058),l(.366,.209),l(.344,.055),l(.616,.356),l(-.005,.172),l(-.294,.184),l(-.563,.069),M(111.683,77.224),l(-.138,.415),l(-.45,.067),l(-.324,.113),l(-.295,.247),l(-.321,-.137),l(-.185,-.21),l(.087,-.443),l(.086,-.443),l(-.438,-.675),l(-.463,-.319),l(-.199,-.271),l(-1.281,.055),l(-.437,.098),l(-.153,.161),l(-.496,.097),l(-.019,-.193),l(-.034,-.432),l(.212,-.272),l(.184,-.212),l(-.378,-.347),l(-.641,-.438),l(-.693,-.696),l(-.723,-.317),l(-.453,-.136),l(.132,-.35),l(-.569,-.592),l(-.099,-.213),l(.371,-.229),l(-.068,-.122),l(-.301,-.152),l(-.445,-.076),l(-.392,-.274),l(-.237,-.259),l(-.57,-.305),l(-1,-.411),l(-.479,-.765),l(-.217,-.583),l(-.367,-.399),l(-.357,.016),l(-.101,.814),l(.42,.873),l(.104,.306),l(-.047,.153),l(-.701,-.136),l(-.272,-.076),l(-.511,-.504),l(-.4,-.459),l(-.537,.139),l(-1.219,-.228),l(1.263,.718),l(.032,.214),l(-1.62,.171),l(-1.093,-.35),l(-1.388,-.948),l(-.543,-.292),l(-.664,-.043),l(-.079,0),l(-.933,-.213),l(-1.3,-.536),l(.928,-.248),l(.135,-.169),L(90.8,67.129),l(-.384,-.153),l(-.792,.156),l(-.454,.14),l(-.656,.017),l(-1.058,-.06),l(-1.068,-.245),l(.027,-.247),l(-.148,-.186),l(-.325,-.108),l(-.359,.016),l(-.47,.202),l(-1.036,.049),l(-1.465,-.122),L(80.46,66.64),l(-.786,-.091),l(-.248,-.108),l(-.651,-.387),l(-.427,-.527),l(-.301,.218),l(-.788,.157),l(-.89,-.293),l(-.234,-.326),l(-.417,-.139),l(-.872,-.248),l(-1.538,-.23),l(-.817,-.248),l(-.671,-.342),l(-.553,.235),l(-.675,.079),l(.06,.437),l(-.193,.062),l(-.389,.25),l(-.249,.405),l(1.119,.293),l(.174,.294),l(-.096,.388),l(-.428,.449),l(-.458,.001),l(-.804,-.214),l(-.586,-.061),l(-.568,.094),l(-.978,.603),l(-1.066,.217),l(-.936,.448),l(-1.035,.448),l(-1.095,.109),l(.178,-.308),l(.063,-.123),l(.72,-.401),l(-.093,-.385),l(-.655,-.523),l(.004,-.108),L(64.1,66.19),l(.411,-.482),l(.157,-.42),l(.736,-.312),l(.87,-.235),l(1.165,-.018),l(1.085,.123),l(.239,-.156),l(-1.239,-.466),l(-.971,-.389),l(-1.043,.049),l(-.226,.219),l(-.449,.095),l(-.573,.438),l(-.865,.375),l(-1.019,.282),L(61.553,65.9),l(-.406,.094),l(-.298,.14),l(.131,.325),l(-.177,.526),l(-.563,.34),l(-.564,.078),L(59,67.544),l(-.592,.278),l(-.681,.601),l(-.035,.292),l(.38,.168),l(.36,.03),l(.667,.106),l(.465,.229),l(-.075,.184),l(-.43,.338),l(-.625,.2),l(-.557,.277),l(-.423,.398),l(-.544,.383),l(-.675,.093),l(-1.434,.308),l(-.678,.397),l(-1.036,.337),l(-.7,.367),l(.52,.5),l(-.1,.167),l(-1.106,.412),l(-.897,.153),l(-.778,.168),L(49.51,74.19),l(-1.214,.456),L(48.1,74.828),l(-.274,.394),l(-.753,.439),l(-1.193,.229),l(-1.234,.184),l(-.973,-.345),l(.001,-.181),l(.332,-.348),l(.763,-.273),l(.306,-.212),l(.445,-.364),l(1.171,-.441),l(1.403,.073),l(-.12,-.212),l(.02,-.516),l(.47,-.304),l(.725,-.291),l(.754,-.366),l(.266,-.214),l(.002,-.731),l(.246,-.749),l(.693,-.49),l(.194,-.398),l(.034,-.412),l(-.633,.122),l(-1.251,.186),l(-.676,-.029),l(-.5,-.597),l(-.266,.062),l(-.613,.216),l(-.136,.23),l(.239,.352),l(.021,.352),l(-.169,.046),L(47,70.115),l(-.567,-.32),l(-.487,-.397),l(-.509,-.291),l(-.56,-.03),l(-.812,-.106),l(-.91,.094),l(.028,.138),l(-.644,.338),l(-1.175,.14),l(-.649,-.09),l(-.064,-.123),l(-.082,-.107),l(-.125,-1.028),l(.3,-.508),l(-.142,-.431),l(-.864,-1.002),l(-1.43,.437),l(-.738,.078),l(-.406,.202),l(-1.091,.094),l(-.4,-.23),l(-.394,-.355),l(-.466,-.325),l(-1.007,-.463),l(-.179,-.28),l(.292,-.171),l(.337,-.437),l(.704,.139),l(1.312,.309),l(.69,.03),l(.238,-.234),l(-.375,-.482),l(-.458,-.264),l(-.363,0),l(-.541,.22),l(-.528,-.015),l(-1.342,-.513),l(-.623,-.186),l(-.197,.016),l(-.858,-.029),l(-.024,-.078),l(-.623,-.985),l(.79,-.19),l(.071,-.456),l(.495,-.221),l(.102,-.299),l(.227,-.347),l(.893,-.491),l(.337,-.38),l(.386,-.301),l(.527,-.476),l(.39,-.175),l(.719,.109),l(.98,.268),l(.485,.094),l(.752,-.144),l(.427,-.254),l(.675,-.429),l(1.252,-.257),l(.774,-.033),l(.955,-.049),l(.354,-.175),l(.187,-.478),l(-.259,-.669),l(-.814,-.686),l(.026,-.096),l(.927,-.034),l(.343,-.256),l(-.25,-.384),l(-.497,-.256),l(-.367,-.08),l(-1.88,.389),L(39.33,56.53),l(-.534,.289),l(-.496,.065),l(-1.907,-.333),l(-.848,-.031),L(34.8,56.49),l(-1.27,.162),l(-1.265,-.029),l(-1.349,-.174),l(-.53,-.207),l(-.183,-.788),l(.144,-.146),l(.636,-.033),l(1.008,-.002),l(.446,-.179),l(-1.057,-.241),l(-1.912,-.304),L(28.13,54.31),l(-1.285,-.208),l(.219,-.114),l(.781,-.262),l(1.568,-.214),l(1.325,-.328),l(.958,-.214),l(1.058,-.361),l(.953,-.23),l(1.399,-.281),l(1.513,.128),l(-.158,.523),l(.023,.277),l(1.051,.161),l(1.359,.095),l(1.074,-.019),l(.657,-.099),l(.784,-.246),l(.55,-.295),l(.262,-.083),l(.752,.064),l(.751,.113),l(1.021,-.051),l(.2,-.327),l(.007,-.18),l(-.689,-.064),l(-1.946,-.292),l(-1.283,-.047),l(-.312,-.755),l(-1.473,-.162),l(-.96,-.031),l(-1.573,-.096),l(-.194,-.511),l(-.484,-.312),l(-1.042,-.461),l(-.512,-.148),l(-2.66,-.659),l(-2.008,-.545),l(.317,-.167),l(.812,-.402),l(.086,-.485),l(2.136,-.054),l(.99,-.069),l(1.829,-.288),l(.784,-.354),l(.452,-.623),l(.788,-.575),l(.616,-.525),l(.818,-.41),l(.742,-.224),l(1.066,-.104),l(1.133,-.241),l(1.047,-.036),l(1.804,.065),l(.146,-.188),l(-.156,-.205),L(44.47,42.83),l(.018,-.206),l(1.936,-.089),l(1.143,.032),l(.974,-.054),l(1.344,-.14),l(1.098,-.416),l(.918,-.417),l(.957,-.019),l(.282,.051),l(.675,.241),l(.156,.172),l(-.383,.139),l(.017,.344),l(1.049,.136),l(.424,.034),l(.536,-.293),l(.297,-.208),l(1.419,.187),l(1.534,.049),l(1.062,.049),l(.715,.033),l(.711,.257),l(.359,.274),l(.783,.358),l(.494,.085),l(.421,-.086),l(1.292,.117),l(1.124,.015),l(1.556,-.054),l(1.449,-.088),l(1.213,.1),l(1.377,.254),l(.883,.118),l(3.424,.13),l(1.279,.168),l(.743,.169),l(2.027,-.038),l(2.339,-.141),l(1.123,.236),l(2.441,.791),l(1.206,.301),l(.227,.008),l(.102,.483),l(-.003,2.855),l(.039,2.259),l(.052,2.335),l(.129,2.796),l(-.026,2.183),l(-.043,4.334),l(.026,2.167),l(.089,1.046),l(.196,.279),l(.84,.074),l(2.424,-.122),l(.739,.059),l(.332,.388),l(.173,.387),l(.348,.292),l(2.162,1.318),l(.945,.673),l(.238,-.325),l(.848,-.205),l(1.225,-.67),l(.731,-.498),l(.495,-.126),l(.832,.073),l(.316,.199),l(.371,.492),l(.35,.322),l(2.048,1.175),l(.814,.58),l(1.769,1.768),l(1.67,1.882),l(.512,.393),l(.189,.029),l(.98,.314),l(2.025,.763),l(.402,.255),l(-.163,.788),l(.393,.777),
+N(643.755,159.873),l(-1.092,-.52),l(-.637,-.337),l(-.203,-1.284),l(.036,-.282),l(.24,-.241),l(.42,-.241),l(.721,-.623),l(.493,.056),l(.049,-.17),l(.24,-.396),l(.239,.028),l(.573,.225),l(.321,-.312),l(.439,-.001),l(.798,-.171),l(.596,.69),l(-.163,.17),l(-.443,.354),l(-.412,.538),l(-.285,.734),l(.14,.296),l(-.22,.311),l(-.292,.085),l(-1.026,.383),l(-.532,.707),M(627.173,150.012),l(-.679,.314),l(-.24,.241),l(-.043,.325),l(.138,.592),l(.228,.366),l(.119,.521),l(-.109,.339),l(-.266,.213),l(-.769,-.08),l(-.321,-.21),l(-.178,-.196),l(-.153,-.239),l(-.111,-.38),l(-.628,.413),l(-.647,.159),l(-.246,-.083),l(-.378,-.266),l(-.341,-.746),l(-.291,-.379),l(-.481,.045),l(-.507,.003),l(-.228,-.098),l(-.117,-.352),l(.729,-1.584),l(-.033,-.268),l(-.521,-.096),l(-.554,.074),l(-.202,-.324),l(-.277,-1.762),l(-.156,-.126),l(-.479,.017),l(-.771,.089),l(-.819,.442),l(-.312,.086),l(-.216,-.069),l(0,-.268),l(.224,-.58),l(-.163,-.705),l(-.075,-.465),l(.617,-.85),l(.191,-.198),l(.487,-.271),l(.611,-.525),l(.429,-.722),l(.353,-.862),l(-.02,-.875),l(-.195,-1.649),l(-.146,-.14),l(-.504,.031),l(-.287,-.041),l(-.217,-.309),l(-.243,-.901),l(-.397,-.435),l(-.504,-.279),l(-.277,.044),l(-.306,.34),l(-.001,.127),l(-.624,-.081),l(-.73,-.179),l(-.657,-.081),l(-.3,-.055),l(-.102,-.056),l(.138,-.269),l(.354,-.454),l(-.046,-.38),l(-.716,-.715),l(-.455,-.392),l(-1.377,.84),l(-.377,.044),l(-.975,-.319),l(-.286,-.167),l(-.355,.087),l(-.546,.299),l(-1.105,.726),l(-.829,.258),l(-.543,.37),l(-1.123,1.107),l(-.397,.27),l(-.714,.216),l(-.784,.033),l(-.189,.1),l(-.329,-.619),l(-.312,-.209),l(-.371,-.041),l(-1.659,-.047),L(601,137.538),l(-.309,-.294),l(-.417,.016),l(-.149,.103),l(-.348,.239),l(-.609,.539),l(-1.251,1.545),l(-.212,-.662),l(.052,-.861),l(-.139,-.183),l(-.231,-.069),l(-.471,.102),l(-.345,.129),l(-.655,-.159),l(-.339,.281),l(-.341,-.116),l(-.849,.066),l(-.319,-.364),l(-.63,-.281),l(-.407,0),l(-.08,.331),l(-.271,.083),l(-.685,-.38),l(.01,.364),l(-.237,.099),l(-.141,-.463),l(-.54,-.496),l(-.365,.066),l(-.935,-.066),l(-.014,-.265),l(.175,-.396),l(-.326,-.017),l(-.333,.248),l(-1.451,-.893),l(.069,-.281),l(-.178,-.38),l(-.289,-.166),l(-.71,.116),l(-.158,.166),l(-.657,-.794),l(-.454,-.281),l(-.15,.132),l(-.472,-.215),l(-.726,-.595),l(-.867,-.264),l(-.132,-.612),l(-1.079,-.199),l(-.186,.182),l(-.275,-.066),l(-.134,.513),l(-.276,.314),l(-.299,-.05),l(-.24,-.43),l(-.859,-.596),l(-.154,.066),l(-.756,-.248),l(.116,-.364),l(-1.078,-.579),l(-.363,.116),l(-.772,-.843),l(-.383,-.248),l(-.477,.314),l(-.198,-.066),l(-.099,-.43),l(.215,-.215),l(-.272,-.364),l(.104,-.446),l(-.579,-.595),l(-.157,-.694),l(.785,-.198),l(.033,.364),l(.337,.264),l(.368,-.049),l(.376,-.281),l(.578,-.364),l(-.367,-.694),l(.104,-.414),l(-.68,-.099),l(-.044,-.182),l(-.092,-.078),l(-.718,-.096),l(-.294,-.221),l(-.037,-.552),l(-.073,-.589),l(.184,-.184),l(.331,-.221),l(-.221,-.258),l(-.441,-.405),l(-.81,-.11),l(-.221,-.515),l(-.441,-1.03),l(0,-.515),l(-.502,.152),l(-.147,-.126),l(-.305,-.111),l(-1.337,-.104),l(-.565,-.436),l(-.178,-.09),l(-.104,-.199),l(-.239,0),l(-.52,.196),l(-.252,-.281),l(-.198,-.218),l(-.301,-.07),l(.312,-.516),l(-.007,-.501),l(-.213,-.247),l(-.373,-.323),l(-.625,.009),l(-.282,.128),l(-.004,-.456),l(-.554,-.117),l(-.296,-.047),l(-.281,.21),l(-.105,-.112),l(-.313,-.14),l(-.048,-.088),l(-.236,-.012),l(-.01,-.269),l(.574,.059),l(.192,-.145),l(.354,-.136),l(.08,-.249),l(.181,-.185),l(-.2,-.163),l(.152,-.36),l(-.245,-.237),l(.126,-.428),l(-.049,-.123),l(-.152,-.012),l(-.028,-.246),l(.009,-.284),l(-.295,-.494),l(-.273,-.154),l(-.692,-.039),l(-.22,-.06),l(-.229,.162),l(-.463,.045),l(-.325,-.394),l(-.077,-.305),l(.207,-.223),l(-.023,-1.031),l(.011,-.069),l(.139,-.739),l(.129,-.213),l(.274,-.186),l(1.422,-.704),l(1.737,-.734),l(.359,-.03),l(.06,.071),l(.183,.567),l(.344,.055),l(.507,-.145),l(.885,-.132),l(.268,-.243),l(.463,-.784),l(.467,-.472),l(.238,-.03),l(1.248,.235),l(.459,-.017),l(.478,-.102),l(.646,-.345),l(.638,-.701),l(.405,-.301),l(.445,-.145),l(1.338,-.349),l(.97,-.219),l(.325,-.187),l(.051,-.157),l(-.031,-.194),l(-.139,-.86),l(.02,-.399),l(.32,-.401),l(.705,-.546),l(.222,-.33),l(-.119,-.47),l(-.29,-.441),l(-.345,-.627),l(-.03,-1.357),l(-.483,-.141),l(-.366,.06),l(-.232,-.185),l(.068,-.215),l(.215,-.302),l(.393,-.188),l(1.799,-.254),l(1.082,-.207),l(.35,-.06),l(.5,.184),l(1.133,.238),l(.394,-.074),l(.231,-.145),l(.156,-.245),l(-.126,-.286),l(-.622,-.514),l(.137,-.418),l(.286,-.605),l(.438,-.794),l(.516,-1.141),l(.427,-.507),l(1.096,.254),l(.721,.141),l(.594,.141),l(1.402,.079),l(.718,-.062),l(.417,-.103),l(.444,-.392),l(.157,-.39),l(-.213,-.707),l(-.097,-.75),l(.34,-.581),l(.428,-.277),l(1.199,-.093),l(.289,-.06),l(.306,-.219),l(.035,-.478),l(.011,-.275),l(.279,-.262),l(.542,-.148),l(.551,-.034),l(.228,-.014),l(.569,-.003),l(.244,-.074),l(.062,.145),l(.131,.405),l(.24,.563),l(.371,.433),l(1.301,.745),l(.834,.415),l(.614,.069),l(.731,.167),l(.633,.144),l(.354,.143),l(.568,.618),l(1.07,1.451),l(.401,.66),l(-.215,.447),l(-.237,.75),l(-.214,.389),l(-.369,.347),l(-.035,.129),l(.105,.43),l(.233,.229),l(.724,.312),l(1.062,.181),l(1.505,.021),l(.995,.038),l(.668,.083),l(.998,.224),l(.632,.268),l(1.645,.806),l(.839,.31),l(.744,.096),l(.105,.542),l(1.571,2.161),l(.467,.439),l(.444,.254),l(1.979,.018),l(1.241,.207),l(.802,.109),l(1.165,.022),l(2.861,-.059),l(.937,.023),l(1.164,.022),l(1.69,.119),l(.521,.168),l(.815,.551),l(1.006,.365),l(1.599,.433),l(.929,.123),l(.663,-.061),l(.61,.067),l(.253,.297),l(.433,.197),l(.481,-.017),l(.686,-.289),l(1.44,-.534),l(.303,-.101),l(.736,-.274),l(.816,-.289),l(1.204,-.349),l(1.339,-.007),l(1.514,-.065),l(.987,.08),l(.651,-.061),l(1.941,-.737),l(.265,-.172),l(1.111,-1.046),l(.67,-.389),l(1.265,-.292),l(.326,-.259),l(.123,-.271),l(-.188,-.228),l(-.599,-.411),l(-.389,-.569),l(-.003,-.343),l(.214,-.401),l(.539,-.589),l(.457,-.231),l(.316,-.073),l(.718,.154),l(.668,.382),l(.592,.125),l(.982,-.005),l(.744,-.047),l(.742,-.433),l(1.192,-.91),l(.224,.013),l(.438,.012),l(.624,.054),l(.896,-.134),l(.638,-.248),l(.347,-.188),l(.241,-.216),l(.312,-.82),l(.363,-.333),l(.47,-.204),l(.464,-.045),l(.483,.127),l(.353,-.189),l(.831,-.278),l(.539,-.146),l(.937,-.221),l(.854,-.033),l(.432,.099),l(1.074,.008),l(.464,.127),l(.414,-.218),l(.107,-.217),l(-.048,-.273),l(-.599,-.501),l(-.879,-.99),l(-.797,-.5),l(-.538,-.199),l(-.928,-.212),l(-.438,.002),l(-1.191,.786),l(-.292,-.07),l(-.431,-.416),l(-.317,-.085),l(-.576,.018),l(-.754,.062),l(-.929,.395),l(-.342,.045),l(-.051,-.029),l(-.269,-.836),l(.381,-.58),l(1.224,-1.959),l(.687,-1.207),l(.295,-.31),l(.046,.018),l(.452,.172),l(1.126,.574),l(.343,-.016),l(.438,-.089),l(2.44,-.752),l(.779,-.339),l(.123,-.233),l(-.056,-.306),l(-.35,-.348),l(-.062,-.146),l(.103,-.249),l(.422,-.426),l(.416,-.543),l(.667,-.779),l(.599,-.545),l(1.371,-.608),l(.167,-.794),l(-.107,-.249),l(-.465,-.306),l(-.558,-.026),l(-.822,.078),l(.119,-.25),l(.375,-.282),l(1.193,-.787),l(.478,-.165),l(.602,-.136),l(1.854,-.143),l(.836,-.123),l(1.203,-.109),l(.917,-.049),l(1.148,.215),l(1.037,.481),l(.683,.188),l(1.386,-.125),l(.539,.026),l(.763,.467),l(.742,.952),l(1.087,2.384),l(.94,1.588),l(.927,1.903),l(.436,.389),l(.507,.2),l(1.247,.341),l(1.523,.253),l(2.659,.839),l(.205,.144),l(.297,.866),l(.44,1.283),l(.261,.446),l(.68,-.033),l(.649,-.018),l(1.657,-.052),l(.604,-.22),l(.953,-.308),l(1.357,-.31),l(1.181,-.208),l(.902,-.034),l(.246,.114),l(.064,.259),l(.116,.389),l(.017,.504),l(-.566,.407),l(-.66,.393),l(-.291,.707),l(-.278,.893),l(-.538,1.066),l(-.627,1.08),l(-.329,.432),l(-.551,.69),l(-.47,.347),l(-.547,-.098),l(-.679,-.225),l(-.685,-.24),l(-.396,-.041),l(-1.664,.982),l(-.048,.557),l(.332,.897),l(.062,.656),l(-.006,.613),l(-.025,.385),l(-.097,.128),l(.112,.299),l(-.156,.329),l(-.511,.43),l(-1.252,.462),l(-.111,.058),l(-.579,-.68),l(-.247,.001),l(-.253,.129),l(-.383,.358),l(-.23,.713),l(-.955,.532),l(-.62,.259),l(-.538,.017),l(-.452,-.054),l(-.333,-.126),l(-.392,.059),l(-.273,.243),l(-.025,.342),l(.508,.765),l(.046,.113),l(-.527,.159),l(-.975,.048),l(-.508,-.153),l(-.493,-.253),l(-.273,-.396),l(-.448,.017),l(-.386,.13),l(-.686,1.027),l(-.636,.543),l(-1.032,.545),l(-1.533,.604),l(-.52,.329),l(-.415,.442),l(-.379,.528),l(-.066,-.092),l(-.417,.171),l(-1.222,.13),l(-.728,.171),l(-2.248,.925),l(-.632,.37),l(-.566,.469),l(-.604,.271),l(-.336,.015),l(.13,-.255),l(.862,-.682),l(.33,-.354),l(-.47,-.113),l(-.37,.072),l(-.153,-.297),l(.058,-.156),l(.953,-.781),l(.269,-.384),l(.55,-.413),l(.159,-.2),l(-.057,-.298),l(-.73,-.553),l(-.727,-.283),l(-.131,-.014),l(-.628,.03),l(-.166,.015),l(-.494,.371),l(-1.146,1.183),l(-.355,.157),l(-.643,.086),l(-.613,.243),l(-.36,.199),L(665.49,112),l(-.136,.411),l(-.131,.255),l(-.251,.255),l(-.437,.128),l(-.493,-.013),l(-.326,.27),l(-.307,.114),l(-.455,-.565),l(-.355,-.014),l(-.349,.128),l(-.396,.638),l(-.301,.694),l(.088,.34),l(.245,.368),l(.558,.268),l(.8,.268),l(1.21,-.045),l(.29,.254),l(-.019,.538),l(-.123,.581),l(.057,.538),l(.261,.283),l(.733,.069),l(.698,-.157),l(.76,-.525),l(.509,-.497),l(.552,-.228),l(.534,-.128),l(.287,.07),l(.895,.621),l(.543,.197),l(1.023,-.087),l(.361,.027),l(.471,.141),l(.274,0),l(-.248,.708),l(-.057,.552),l(-.612,-.197),l(-.297,-.084),l(-.525,.058),l(-1.677,.555),l(-.707,.44),l(-.072,.735),l(-.522,.157),l(-.146,-.113),l(-.017,-.269),l(-.127,-.084),l(-.501,.114),l(.138,.466),l(-.152,.368),l(-.485,.496),l(-1.397,1.119),l(-.126,.226),l(.039,.55),l(.62,.225),l(.712,.492),l(.785,.521),l(.391,.535),l(.424,1.241),l(.668,.647),l(.175,.437),l(-.13,.677),l(.172,.183),l(.694,.295),l(.399,.592),l(.562,.253),l(.272,.268),l(.087,.31),l(-.049,.155),l(-.789,.369),l(.088,.07),l(.425,.31),l(.314,.79),l(-.019,.296),l(-.141,.184),l(-.534,.043),l(-.651,.213),l(-.948,.552),l(-.849,.213),l(-.629,.297),l(.72,.21),l(.378,.056),l(.944,-.425),l(.488,-.058),l(.162,.056),l(.524,.592),l(.387,.168),l(.456,.027),l(.009,.155),l(-.231,.311),l(-.382,.227),l(-.304,.241),l(.11,.155),l(.326,-.029),l(.202,.084),l(-.184,.325),l(-.298,.749),l(-.192,.649),l(.028,.353),l(-.22,.452),l(-.209,.127),l(-.35,-.338),l(-.146,.142),l(-.569,.763),l(-.401,.622),l(-.215,.622),l(-.127,.296),l(-.595,.425),l(-.251,.438),l(-.254,.184),l(-.569,.029),l(-.382,.227),l(.279,.719),l(-.264,.508),l(.076,.593),l(-.093,.269),l(-.207,.269),l(-.532,.199),l(-.161,.282),l(-.174,.396),l(-.294,.636),l(-.626,.354),l(-.412,.495),l(-.492,-.14),l(-.443,-.069),l(-.142,.113),l(-.145,.198),l(.118,.833),l(-.213,.142),l(-.772,.651),l(-.356,.127),l(-.628,.171),l(-.563,.467),l(-.571,.213),l(-.107,.113),l(-.008,.48),l(-.133,.156),l(-.568,.058),l(-.5,.114),l(-.341,.438),l(-.364,-.126),l(-.52,-.168),l(-.274,.057),l(-.315,.326),l(-.435,.198),l(-.643,.1),l(-.047,-.465),l(-.52,.057),l(-.699,.213),l(-.32,.198),l(-.285,-.042),l(-.401,-.493),l(-.163,-.155),l(-.191,.283),l(.095,.169),l(-.045,.212),l(-.047,.691),l(-.209,.297),l(-.416,.114),l(-.501,-.182),l(-.123,.282),l(-.001,.24),l(-.146,.155),l(-.615,.058),l(-.366,.114),l(-.596,.043),l(-.463,-.211),l(-.217,.1),l(-.439,.48),l(-.227,.071),l(-.774,-.041),l(-.747,.227),l(-.406,.326),l(-.451,-.027),l(-.277,-.084),l(-.011,.057),l(-.069,.353),l(-.29,.396),l(.011,.113),l(.48,.634),l(.269,.126),l(.043,.198),l(-.36,.269),l(-.763,.157),l(-.481,-.719),l(-.241,-.691),l(.012,-.395),l(.396,-.777),l(-.015,-.169),l(-.587,-.253),l(-.226,.071),l(-.206,.297),l(-.454,.072),l(-.676,-.21),l(-.574,-.733),l(-.196,.085),l(-.017,.169),l(-.159,.396),l(-.27,.128),l(-.332,-.056),l(-.481,.043),l(-.055,.038),l(-.197,-.143),l(-.909,-.107),l(-.621,-.166),l(-.943,-.615),l(-.273,-.365),l(-.096,-.521),l(.472,-.75),l(-.056,-.38),l(-.237,-.21),l(-.777,.047),l(-.66,-.053),l(-.771,-.235),l(-.623,-.575),l(-.298,.101),l(-.521,.328),l(-.696,.554),l(-.341,.157),l(-.807,.117),l(-1.595,.052),l(-.332,.115),l(-.584,.413),l(-.325,-.097),l(-.597,-.392),l(-.341,-.012),l(-.515,.13),l(-.483,.229),
+N(241.073,156.152),l(.017,.52),l(.098,1.215),l(.012,.212),l(-.379,.455),l(-.011,.17),l(.485,1.358),l(-.669,-.577),l(-.445,-.056),l(-.761,.143),l(-.877,-.012),l(-.666,-.14),l(-.574,-.056),l(-.474,.1),l(-.378,.354),l(-.135,-.042),l(-.993,-.549),l(-.171,-.325),l(.04,-.198),l(.269,-.184),l(1.051,.097),l(.631,.111),l(1.125,.167),l(.654,.041),l(.61,-.185),l(.386,-.156),l(-.198,-.155),l(-.692,-.464),l(-.136,-.296),l(.184,-.707),l(-.202,-.296),l(-.394,-.154),l(-.913,-.14),l(-.305,-.211),l(.04,-.184),l(.119,-.085),l(.344,-.1),l(.724,-.058),l(.781,.125),l(1.081,.294),l(.576,.056),l(.147,-.089),
+N(241.295,160.082),l(-.485,-1.358),l(.011,-.17),l(.379,-.455),l(-.012,-.212),l(-.098,-1.215),l(-.017,-.52),l(.503,-.279),l(.393,.14),l(.342,0),l(.384,-.17),l(.369,-.043),l(.14,.198),l(.177,.112),l(1,.309),l(.657,-.072),l(.213,.395),l(.335,.338),l(.528,.324),l(.335,.084),l(.643,.21),l(.916,.45),l(.399,.352),l(.231,.311),l(-.191,.17),l(-.144,.297),l(-.314,.368),l(-.238,-.098),l(-.476,-.592),l(-.378,-.042),l(-.788,.058),l(-.288,-.098),l(-.373,0),l(-.329,.1),l(-.763,.539),l(-.396,-.056),l(-.319,-.494),l(-.166,-.028),l(-.155,.057),l(-.658,.326),l(-.344,.778),l(-.41,.65),l(-.289,-.112),l(-.325,-.551),
+N(668.053,167.796),l(-.131,-.099),l(-.74,-.732),l(-.444,-1.255),l(.037,-.424),l(.054,-.706),l(-.292,-.465),l(.18,-.382),l(.978,.704),l(.202,-.424),l(.023,-.41),l(-.101,-.438),l(-.026,-.579),l(.145,-.438),l(.025,-.664),l(.082,-.861),l(.074,-.636),l(.38,-.862),l(.188,-.127),l(.337,-.142),l(.523,.055),l(1.21,.52),l(.576,.042),l(.188,-.212),l(.277,-.17),l(.199,.141),l(.018,.396),l(-.266,.438),l(-.045,.48),l(.14,.79),l(.541,.394),l(.182,.325),l(-.427,1.271),l(-.31,.467),l(-.834,.608),l(-.555,.312),l(-.082,.099),l(.003,.268),l(-.078,.565),l(-.28,.424),l(.127,.211),l(.35,.733),l(.345,1.101),l(.26,.62),l(.093,.028),l(.451,.324),l(.296,.07),l(.161,-.325),l(.485,-.537),l(.197,-.029),l(.418,.112),l(.226,.211),l(.179,.635),l(.286,.353),l(.326,.084),l(.505,-.58),l(.621,.267),l(.141,.028),l(.078,.127),l(-.168,.156),l(-.378,.156),l(-.208,.212),l(.936,1.226),l(-.315,.184),l(-.568,-.196),l(-.404,-.098),l(-.094,-.48),l(-.229,-.31),l(-.599,-.535),l(-.753,-.577),l(-.258,0),l(-.099,.226),l(.371,.776),l(-.218,.283),l(-.727,-.775),l(-.982,-.548),l(-.309,.015),l(-.344,.142),l(-.182,.255),l(-.14,.071),l(-.395,.057),l(-.322,-.225),l(-.591,-.366),l(-.195,-.423),l(.64,-.608),l(.323,.211),l(.358,.084),l(.4,-.199),l(-.151,-.169),l(-.713,-.295),l(-.422,-.395),l(-.522,-.168),l(-.239,.607),M(669.676,172.974),l(-.452,-.366),l(-.349,-.408),l(-.051,-.226),l(-.364,-1.114),l(-.459,-.521),l(.685,-.058),l(.868,.012),l(.314,.169),l(.274,.451),l(.028,.861),l(-.097,.48),l(-.214,.283),l(-.185,.438),M(679.073,175.368),l(-.562,-.267),l(-.178,-.254),l(-.237,-.169),l(-.064,-.127),l(.139,-.339),l(-.091,-.423),l(-.55,-.352),l(-.662,-.479),l(-.147,-.466),l(.513,-.1),l(1.017,-.143),l(.371,.112),l(.283,.409),l(.069,.818),l(.123,.805),l(.257,.79),l(-.112,.127),l(-.167,.057),M(671.406,176.824),l(.022,-.353),l(.186,-1.342),l(.061,-1.172),l(.482,.395),l(.576,.281),l(.366,.028),l(.634,-.185),l(.027,.438),l(-.079,.396),l(-.24,.269),l(-.184,.198),l(-.908,.651),l(-.943,.397),M(664.721,177.812),l(-.366,-.027),l(.127,-.297),l(.202,-.099),l(.314,-.396),l(.265,-.523),l(.082,-.607),l(.138,-.509),l(.326,-.184),l(.14,.183),l(-.16,.283),l(.276,.55),l(.212,.564),l(-.108,.113),l(-.664,.354),l(-.406,.368),l(-.377,.227),M(673.781,179.981),l(-.385,-.366),l(-.828,-.591),l(-.206,-.38),l(.099,-.368),l(.565,-.213),l(.22,-.269),l(.487,-1.822),l(.438,-.185),l(.271,.028),l(.113,.126),l(.049,.282),l(-.373,.905),l(-.089,.226),l(-.315,1.413),l(.165,.296),l(.214,.465),l(-.213,.41),l(-.212,.043),M(661.179,181.308),l(-.317,-.042),l(.215,-.48),l(.343,-.467),l(.35,-.34),l(.592,-.354),l(.636,-.496),l(.933,-1.089),l(.11,.55),l(-.118,.424),l(-.267,.297),l(-.24,.184),l(-.516,.213),l(-.172,.127),l(-.244,.622),l(-.327,.269),l(-.591,.171),l(-.386,.41),M(680.678,185.402),l(-.201,-.098),l(-.098,-.438),l(-.361,-.748),l(-.297,-.112),l(-.381,.608),l(-.361,.82),l(.246,.38),l(.166,.395),l(.148,.677),l(-.158,.495),l(-.383,.509),l(-.305,-.253),l(-.176,-.268),l(.201,-.41),l(-.076,-.325),l(-.363,-.07),l(-.361,.665),l(-.173,-.056),l(-1.114,-.619),l(-.557,-.549),l(-.206,-.508),l(-.037,-.635),l(.466,-.905),l(-.108,-.479),l(-.466,-.409),l(-.778,-.295),l(-.292,.043),l(-.062,.17),l(-.129,.226),l(-.5,.255),l(-.661,.058),l(-.027,-.494),l(-.174,-.24),l(-.399,.199),l(-.504,.961),l(-.525,.863),l(-.219,-.084),l(-.119,-.127),l(-.034,-.197),l(.054,-.311),l(.359,-.749),l(.185,-.537),l(.263,-.283),l(.383,-.184),l(.564,-.044),l(.219,-.127),l(.134,-.466),l(.205,-.156),l(.549,-.241),l(.777,-.101),l(.292,.353),l(.102,.72),l(.317,-.156),l(.485,-.058),l(.207,-.184),l(.207,-.565),l(.358,-.255),l(.479,.21),l(.186,.084),l(.158,-1.087),l(.29,-.015),l(.264,.465),l(.315,-.481),l(.205,-.142),l(.392,.098),l(.133,-.227),l(-.048,-.706),l(-.172,-.564),l(.146,-.353),l(.413,.549),l(.711,.803),l(.229,.48),l(.083,.324),l(-.336,.735),l(.237,.226),l(.537,-.1),l(.076,.423),l(-.114,.424),l(.056,.692),l(.207,.437),l(-.002,.438),l(-.111,.424),l(-.283,.579),l(-.332,.622),
+N(251.898,160.229),l(-.547,-.112),l(-.073,-.212),l(.051,-.551),l(-.109,-.24),l(.11,-.17),l(.235,-.071),l(.543,.069),l(.404,-.015),l(1.505,.11),l(.393,.168),l(.113,.141),l(-.188,.354),l(-.07,.099),l(-.283,.227),l(-.335,.043),l(-.739,-.083),l(-.657,.072),l(-.354,.17),
+N(228.82,160.519),l(-.299,-.056),l(-.693,-.224),l(-.229,-.268),l(-.47,-.366),l(-.465,-.084),l(-.142,-.211),l(.53,-.284),l(.704,-.072),l(1.364,.251),l(.97,.351),l(.651,.267),l(.279,.282),l(-.04,.198),l(-.332,.071),l(-.751,-.295),l(-.543,.058),l(-.227,.255),l(-.308,.128),
+N(400.72,175.499),l(-.595,-.119),l(-.161,-.032),l(-.976,.458),l(-1.429,-.006),l(-.867,-.037),l(-2.119,.041),l(-.271,.157),l(-.125,.34),l(.261,1.934),l(.02,.41),l(-.191,.17),l(-.63,-.434),l(-.644,-.166),l(-.769,.075),l(-.608,.159),l(-.446,.257),l(-.368,.115),l(-.354,-.083),l(-.452,-.223),l(-.52,-.562),l(-.15,-.465),l(-.308,-.252),l(-.545,.003),l(-.259,-.168),l(.08,-.043),l(.046,-.156),l(.172,-.326),l(.261,-.92),l(.133,-.876),l(-.058,-.749),l(.141,-.255),l(.257,-.016),l(.448,-.158),l(.543,-.271),l(.317,-.369),l(.292,-1.047),l(.355,-1.217),l(.397,-.384),l(.273,-.058),l(.293,.281),l(.551,.364),l(.45,-.102),l(.174,-.227),l(.393,-.822),l(.492,-.667),l(.638,-.625),l(.272,-.101),l(.518,.209),l(.324,.182),l(.274,.027),l(.314,-.793),l(.304,-.228),l(.947,-.458),l(1.22,-.713),l(.37,-.073),l(.356,.125),l(.36,.059),l(1.062,.173),l(-.173,.665),l(.019,.396),l(.426,.93),l(.422,.492),l(.389,.266),l(.502,.42),l(.179,.268),l(.018,.212),l(-.335,.439),l(.114,.226),l(.97,.757),l(.516,.181),l(.37,-.044),l(.562,.025),l(.568,.971),l(.131,.367),l(-.237,.764),l(-.415,.468),l(-.337,.158),l(-.739,-.095),l(-.418,.045),l(-.415,.271),l(-.366,.553),l(-.24,.157),l(-.062,.142),l(-.29,-.041),l(-.611,-.166),l(-1.013,-.163),
+N(209.823,175.47),l(-.388,-.645),l(-.932,-.888),l(-1.003,-1.085),l(-.837,-.817),l(-.723,-.464),l(-.196,-.183),l(-.023,-.226),l(.625,-.03),l(1.001,-.125),l(.29,-.143),l(.293,-.412),l(.005,-.961),l(.882,-.034),l(.513,-.583),l(.725,.42),l(1.207,-.997),l(.503,-.794),l(.294,-.242),l(.259,.013),l(.328,.182),l(.463,.097),l(.248,-.086),l(.424,-.229),l(1.425,-.486),l(.23,.519),l(.038,.339),l(-.057,.509),l(-.214,.707),l(-.543,.806),l(-.1,.749),l(.042,.904),l(-.245,1.13),l(-.188,.735),l(-.101,1.385),l(.031,.55),l(.184,.466),l(.188,.363),l(-.281,.274),l(-.767,-.149),l(-.241,-.46),l(-.285,.077),l(-.394,-.107),l(-.603,.25),l(-1.651,-.599),l(-.43,.271),
+N(634.036,168.444),l(.533,.251),l(.736,.165),l(.341,.111),l(.469,.251),l(.248,-.058),l(.095,-.24),l(-.103,-.211),l(-.187,-.182),l(-.107,-.296),l(.392,-.285),l(.476,-.186),l(.444,.181),l(.373,.351),l(.3,-.072),l(.975,-.429),l(.209,-.114),l(-.215,.905),l(.029,.663),l(.455,1.014),l(-.076,.367),l(-.05,.424),l(.054,1.073),l(-.217,.509),l(-.385,.285),l(-.599,.187),l(-.932,.443),l(-.521,.229),l(-.99,.571),l(-.234,.425),l(.216,.422),l(.568,.435),l(.03,.197),l(-.17,.27),l(-.688,-.194),l(-.497,-.011),l(-.599,.229),l(-.915,.528),l(-.646,.229),l(-.3,.289),l(-.256,-.188),l(-.248,-.268),l(-.35,-.042),l(-.382,.142),l(-.205,-.042),l(-.293,.043),l(-.183,-.113),l(.142,-.311),l(.182,-.226),l(-.04,-.254),l(-.283,-.395),l(-.277,.043),l(-.462,.298),l(-.339,.015),l(-.171,-1.044),l(-.649,-1.488),l(.146,-.176),l(-.16,-.479),l(-.487,-.717),l(-.219,-.648),l(-.026,-.635),l(.076,-.382),l(.146,-.297),l(.92,-1.233),l(.521,-.441),l(.383,-.101),l(1.172,-.091),l(.798,.066),l(.558,-.074),l(.575,.039),l(.599,.109),l(.301,.167),
+N(214.474,175.913),l(.821,.884),l(.385,.623),l(.314,.322),l(.225,.046),l(.465,.645),l(.441,.352),l(-.014,.006),l(-.074,.123),l(-.478,-.184),l(-.219,.205),l(-.014,.321),l(0,.58),l(.507,.307),l(-.396,.368),l(.125,.532),l(-.374,.369),l(.243,.184),l(-.204,.609),l(.003,-.466),l(-.296,-.307),l(.01,-.568),l(-.377,-.148),l(-.238,-.102),l(-.111,.082),l(.325,.41),l(.084,.225),l(-.113,.062),l(-.726,-.299),l(-.13,-.282),l(.251,-.339),l(.04,-.382),l(-.182,-.338),l(-.486,-.324),l(-.695,-.287),l(-.079,-.144),l(-.689,-.103),l(-.316,-.327),l(.007,-.421),l(-.146,-.255),l(-.249,-.098),l(-.576,-.353),l(-.416,-.266),l(.225,.512),l(.086,.222),l(.422,.044),l(.181,.266),l(-.544,.573),l(-.144,-.262),l(-.356,-.282),l(-.561,-.211),l(-.323,-.239),l(-.147,-.24),l(-.104,-.494),l(.128,-.421),l(.332,-.225),l(-.008,-.389),l(-.554,-.225),l(.363,-.123),l(-.057,-.227),l(-.238,.017),l(.43,-.271),l(1.651,.599),l(.603,-.25),l(.394,.107),l(.285,-.077),l(.241,.46),l(.767,.149),l(.281,-.274),
+N(436.304,195.359),l(-.209,-.451),l(-.194,-.804),l(-.498,-.802),l(-1.217,-1.236),l(-.112,-.324),l(-.064,-.791),l(-.432,-.605),l(-.4,-.661),l(-.207,-.592),l(-.273,-1.185),l(-.112,-.776),l(.064,-.424),l(.144,-.198),l(.528,-.399),l(.4,-.511),l(.866,-1.743),l(.354,-.327),l(.208,-.114),l(.096,.084),l(.24,.027),l(.449,-.158),l(1.363,-.686),l(.494,.661),l(.225,.069),l(.224,-.03),l(.466,-.271),l(.754,-.386),l(.818,-.273),l(.881,.009),l(.368,-.031),l(.258,-.143),l(.646,-1.176),l(.117,-.806),l(.209,-.199),l(.434,-.059),l(2.181,-.055),l(.386,-.087),l(2.649,-2.275),l(.196,-.284),l(.005,-.41),l(.165,-.382),l(.372,-.228),l(.939,-.613),l(.322,-.086),l(.321,.012),l(.559,.208),l(1.342,1.673),l(.347,.549),l(.122,.536),l(-.416,1.472),l(.01,.664),l(.158,.211),l(.802,-.019),l(.272,-.001),l(.207,.126),l(.252,.493),l(.223,.225),l(.797,.447),l(.799,.235),l(.351,.196),l(.223,.267),l(-.148,.481),l(.173,.395),l(.445,.351),l(.558,.378),l(.717,.434),l(.207,.168),l(.206,.366),l(.059,.72),l(.285,.436),l(.367,.224),l(.814,.165),l(.558,.477),l(.157,.352),l(-.083,.679),l(.031,.07),l(-.496,.145),l(-.962,.344),l(-.319,-.026),l(-.287,-.167),l(-.687,-.264),l(-1.15,-.361),l(-.4,.13),l(-.082,.551),l(-.13,.241),l(-.256,.058),l(-.399,-.026),l(-.862,-.207),l(-.593,.102),l(-.93,.373),l(-.93,.486),l(-.271,.016),l(-.559,-.35),l(-.383,-.153),l(-.353,.186),l(-.677,1.36),l(-.176,.157),l(-.591,-.067),l(-1.934,-.511),l(-1.422,-.189),l(-.512,-.322),l(-.159,-.239),l(-.334,-.394),l(-.75,-.518),l(-.432,-.167),l(-.479,.06),l(-.529,.3),l(-.353,.341),l(-.785,.979),l(-.097,.297),l(.096,.452),l(-.017,.353),l(-.063,.594),l(-.16,.058),l(-.751,.287),l(-.895,-.093),l(-.624,-.138),l(-.367,-.054),l(-.975,.175),l(-.479,.102),l(-.255,.228),l(-.222,1.287),l(-.158,.523),l(-.365,.497),l(-.303,.312),
+N(371.324,180.419),l(1.088,-1.235),l(.643,-.866),l(.238,-.157),l(.594,.039),l(1.137,-.148),l(.466,.04),l(.42,.224),l(.456,.421),l(.475,.619),l(.252,.761),l(.165,1.369),l(.26,.656),l(-.259,.502),l(-1.184,1.151),l(-.63,.724),l(-.285,.256),l(-.061,.103),l(-.631,-.523),l(-.661,-.408),l(-.483,-.196),l(-.033,-.141),l(.061,-.297),l(-.05,-.184),l(-.531,-.21),l(-.792,-.549),l(-.389,-.366),l(-.375,-.465),l(.494,-.241),l(.174,-.227),l(-.034,-.155),l(-.484,-.211),l(-.065,-.113),l(.022,-.173),
+N(579.606,186.906),l(-.493,-.083),l(-.265,-.197),l(-.21,-.353),l(-.246,-.621),l(-.361,-1.256),l(-.034,-.649),l(.005,-.763),l(-.02,-.904),l(-.24,-.696),l(.188,-.513),l(-.298,-.35),l(.068,-.28),l(.423,.023),l(.349,-.43),l(.053,-.367),l(.226,-.369),l(-.152,-.537),l(.529,.48),l(.699,.38),l(.234,.395),l(.549,.776),l(.175,.324),l(-.099,.339),l(.024,.141),l(.592,.338),l(.266,.733),l(.798,1.368),l(.136,.508),l(-.193,.735),l(-.397,.679),l(-.369,.396),l(-.514,.425),l(-.78,.284),l(-.642,.043),
+N(217.111,178.792),l(.52,.307),l(.195,.512),l(.02,.374),l(.363,.155),l(.628,.024),l(.244,-.205),l(.398,.43),l(.726,.082),l(.458,-.083),l(1.348,-.751),l(.514,-.046),l(1.387,-.921),l(.373,.144),l(.742,.069),l(.071,.156),l(.789,-.017),l(.767,.21),l(.666,.38),l(.644,.563),l(.406,.666),l(.084,.327),l(.228,.149),l(.509,1.038),l(-.322,.062),l(-.094,.43),l(-.584,.409),l(-.085,-.307),l(-.19,-.082),l(.045,.45),l(-.228,.082),l(-.256,.753),l(-.378,-.825),l(-.441,-.762),l(-.137,-.452),l(.179,-.24),l(.22,-.085),l(.786,.125),l(-.336,-.193),l(-.125,-.164),l(-.096,-.471),l(-.309,.307),l(-.439,.041),l(-.244,-.378),l(-.031,-.269),l(-.193,-.282),l(-.132,.151),l(-.226,-.287),l(-.11,.102),l(-.132,-.266),l(-.456,-.192),l(-.562,-.013),l(-.499,.241),l(-.382,.108),l(-.07,.359),l(.081,.234),l(-.529,.318),l(-.374,.184),l(-.335,.029),l(-.345,.41),l(.049,.296),l(.316,.297),l(.464,.43),l(.178,.386),l(-.011,.146),l(-.281,.081),l(-.243,-.042),l(-.431,.391),l(-.568,.105),l(-.339,-.042),l(-.189,-.146),l(.108,-.164),l(-.349,-.833),l(-.244,-.353),l(-.177,.674),l(-.812,-.409),l(-.227,-.757),l(-.207,.041),l(-.96,-.123),l(-.434,-.266),l(-.599,0),l(-.314,.113),l(-.361,.495),l(.204,-.609),l(-.243,-.184),l(.374,-.369),l(-.125,-.532),l(.396,-.368),l(-.507,-.307),l(0,-.58),l(.014,-.321),l(.219,-.205),l(.478,.184),l(.074,-.123),
+N(266.015,188.956),l(-.503,-.647),l(-.732,-.745),l(-.324,-.521),l(.071,-.212),l(.395,-.539),l(-.008,-.58),l(.061,-.452),l(1.032,-.19),l(.41,-.144),l(.308,-.299),l(-.141,-.282),l(-.62,-.604),l(-.074,-.212),l(.101,-.255),l(1.655,-1.239),l(.061,-.41),l(-.095,-.296),l(.072,.049),l(.496,.338),l(.856,.521),l(.695,.521),l(.587,.663),l(.128,.409),l(-.036,.734),l(.148,.945),l(.298,-.593),l(.22,-.099),l(.6,.182),l(.101,.127),l(.507,.295),l(.708,.549),l(.401,.493),l(.374,.649),l(-.015,.339),l(-.142,.496),l(-.082,.862),l(-.183,.27),l(-1.131,.091),l(-.169,.17),l(.078,.777),l(-.095,.467),l(-.328,.694),l(.571,.831),l(.532,.675),l(.561,.067),l(.185,.295),l(.246,.832),l(.49,1.127),l(.332,.563),l(-.06,.212),l(-.943,-.022),l(-.934,.429),l(-1,.331),l(-.505,.314),l(-.694,.513),l(-.686,.075),l(-.613,-.392),l(-1.103,-.897),l(-.15,-.296),l(-.008,-.396),l(.072,-.354),l(-.167,-.31),l(-.281,-.394),l(-.156,-.465),l(.213,-.962),l(.547,-1.416),l(.179,-.368),l(-.435,-.958),l(-.533,-.138),l(-.304,.001),l(.452,-1.09),l(-.04,-.212),l(-.26,-.111),l(-.516,-.124),l(-.302,.058),l(-.375,.257),
+N(377.518,182.142),l(.193,-.376),l(.252,-.242),l(.367,-.143),l(.528,-.046),l(.338,.097),l(.165,.366),l(.22,.846),l(.009,.65),l(-.044,.283),l(.277,.323),l(.404,.322),l(.321,.026),l(.756,-.922),l(.143,-.086),l(.337,.097),l(.339,.224),l(-.062,.17),l(.119,.55),l(-.059,.438),l(-.501,.865),l(.05,.183),l(.194,.183),l(.369,.097),l(.592,-.032),l(1.264,1.334),l(.131,.282),l(-.059,.452),l(-.247,.849),l(-.105,.565),l(-.041,.919),l(-1.513,-.643),l(-1.221,-.619),l(-1.012,-.562),l(-.403,-.423),l(-1.129,-1.113),l(-1.111,-.76),l(-.723,-.337),l(-.901,-.535),l(-.66,-.548),l(.061,-.103),l(.285,-.256),l(.63,-.724),l(1.184,-1.151),l(.259,-.502),
+N(429.505,210.684),l(.484,.336),l(.177,.013),l(.443,-.271),l(.327,-.581),l(1.495,-.532),l(.054,.424),l(.042,.664),l(.147,.211),l(.57,-.328),l(.554,-.399),l(.931,-.811),l(.364,-.229),l(1.025,-.938),l(.086,-.706),l(-.122,-.72),l(.074,-.396),l(.193,-1.159),l(.343,-.751),l(.47,-.836),l(.41,-.454),l(.809,-.599),l(.525,-.229),l(.459,-.427),l(.139,-.523),l(.169,-.708),l(.115,-.61),l(.254,-1.342),l(.196,-2.021),l(.156,-.764),l(.094,-.551),l(.349,-.821),l(.558,-.837),l(.398,-1.203),l(.031,-.156),l(-.128,-.197),l(.16,-.058),l(.063,-.594),l(.017,-.353),l(-.096,-.452),l(.097,-.297),l(.785,-.979),l(.353,-.341),l(.529,-.3),l(.479,-.06),l(.432,.167),l(.75,.518),l(.334,.394),l(.159,.239),l(.512,.322),l(1.422,.189),l(1.934,.511),l(.591,.067),l(.176,-.157),l(.677,-1.36),l(.353,-.186),l(.383,.153),l(.559,.35),l(.271,-.016),l(.93,-.486),l(.93,-.373),l(.593,-.102),l(.862,.207),l(.399,.026),l(.256,-.058),l(.13,-.241),l(.082,-.551),l(.4,-.13),l(1.15,.361),l(.687,.264),l(.287,.167),l(.319,.026),l(.962,-.344),l(.496,-.145),l(.143,.239),l(.795,.772),l(.348,.493),l(.525,.477),l(.366,.195),l(.352,-.016),l(.258,-.242),l(.529,-.37),l(.384,.012),l(.684,.631),l(.16,-.1),l(.436,-.582),l(.258,-.157),l(.207,.083),l(1.032,.926),l(1.288,1.32),l(.063,.028),l(.159,.183),l(-.018,.424),l(-.26,1.88),l(.128,.253),l(.191,.027),l(.479,.04),l(.271,.182),l(.111,.31),l(-.191,.453),l(-1.195,1.066),l(-1.241,.996),l(-.255,.284),l(-.395,.681),l(-.217,1.02),l(-.107,.507),l(.021,.593),l(-.025,.734),l(.057,.748),l(-.076,.27),l(-.188,.298),l(-.426,.412),l(-.301,.171),l(-.269,.256),l(-.122,.425),l(-.49,1.358),l(.197,.338),l(.689,.999),l(.087,.381),l(-.04,.438),l(.014,.636),l(.237,.634),l(.017,1.329),l(-.064,.762),l(.425,1.439),l(-.102,.848),l(.122,.693),l(.486,.631),l(.936,.898),l(.428,.78),l(.689,1.804),l(-.563,.068),l(-3.015,.499),l(-.347,.214),l(-.154,.198),l(-.797,1.276),l(-.029,.622),l(.372,2.088),l(-.377,1.175),l(-.3,1.02),l(.043,.296),l(.456,.605),l(.837,.843),l(.445,.279),l(.515,.012),l(.448,-.455),l(.362,-.186),l(.136,.183),l(.032,.791),l(.028,1.427),l(-.041,.551),l(-.371,-.012),l(-1.355,-.091),l(-.348,-.21),l(-.381,-.647),l(-.561,-.731),l(-.416,-.351),l(-.477,-.252),l(-.895,-.263),l(-.38,-.28),l(-.728,-1.423),l(-.212,.354),l(-.522,.682),l(-.366,.087),l(-.325,-.111),l(-.922,-.164),l(-.925,-.249),l(-.489,-.194),l(-.58,-1.014),l(-.175,.071),l(-1.146,.289),l(-.195,-.056),l(-.172,-.352),l(-.365,-.379),l(-.678,-.096),l(-1.24,-.147),l(-.991,.146),l(-.859,.26),l(-.61,.004),l(-.199,-.197),l(-.007,-.254),l(.198,-.905),l(.003,-.466),l(-.306,-.62),l(-.844,-.998),l(-.117,-.211),l(-.021,-.212),l(.296,-1.033),l(.07,-1.06),l(-.109,-.607),l(-.303,-.605),l(.057,-.354),l(.177,-.693),l(-.052,-.183),l(-.322,-.098),l(-1.231,.093),l(-.88,.019),l(-.212,-.168),l(.055,-.48),l(-.181,-.225),l(-2.016,.082),l(-.112,.001),l(-.089,.354),l(.027,.593),l(-.286,.892),l(-.184,.411),l(-.993,.006),l(-.899,-.122),l(-1.021,.246),l(-.88,.034),l(-.292,-.168),l(-.64,-.787),l(-.597,-1.07),l(-.537,-1.409),l(-.059,-.579),l(-.121,-.521),l(-.082,-.127),l(-.321,-.111),l(-.385,-.012),l(-1.104,-.008),l(-.88,.034),l(-.624,.003),l(-1.312,-.048),l(-.945,-.052),l(-.783,.02),l(-.432,.03),l(-.318,.101),l(-.896,.062),l(-.699,.314),l(-.56,.06),l(-.112,-.013),l(-.322,-.922),l(.012,-.053),l(.74,-.3),l(-.017,-.085),l(.1,-.89),l(.094,-.143),l(.317,-.199),l(.583,-.568),
+N(468.568,202.653),l(1.277,-.05),l(4.659,0),l(1.448,-.033),l(4.663,2.568),l(3.553,1.984),l(.137,.381),l(-.116,.552),l(.07,.239),l(2.427,1.752),l(1.067,.807),l(-.237,.427),l(-.419,1.329),l(-.137,1.017),l(.109,.551),l(.665,.987),l(.768,.577),l(-.024,.282),l(-.263,.354),l(-.371,1.046),l(.037,.17),l(.306,.041),l(.118,.226),l(-.172,.692),l(-.019,.946),l(.411,1.793),l(.18,.564),l(.401,.465),l(.823,.55),l(.396,.268),l(.177,.228),l(-1.157,.836),l(-.515,.342),l(-1.218,.402),l(-1.243,.559),l(-.448,.031),l(-.646,-.42),l(-.276,-.083),l(-.21,.326),l(-.543,.766),l(-.396,.144),l(-.42,-.054),l(-.964,.006),l(-.963,.02),l(-.512,-.294),l(-.323,-.394),l(-.229,-.083),l(-.123,.128),l(-.643,.356),l(-.526,.088),l(-1.019,-.149),l(-.898,-.018),l(-.196,-.559),l(-.135,-.766),l(-.053,-.584),l(-.135,-.946),l(-.448,-.841),l(-.663,-.538),l(-1.014,-.107),l(-.223,.016),l(-.385,-.407),l(-.732,-.349),l(-1.574,-.57),l(-1.269,-.6),l(-.729,-.265),l(-.263,-.21),l(-.703,-.641),l(-.689,-1.804),l(-.428,-.78),l(-.936,-.898),l(-.486,-.631),l(-.122,-.693),l(.102,-.848),l(-.425,-1.439),l(.064,-.762),l(1.252,-.348),l(.388,-.539),l(.645,-1.204),l(.687,-.853),l(.346,-.271),l(.044,-.212),l(-.148,-.239),l(-.273,-.125),l(-.401,-.068),l(-.211,-.211),l(.158,-.976),l(.304,-.016),l(.396,-.158),l(.142,-.17),l(.104,-.481),l(-.167,-.493),l(-.364,-.888),l(-.121,-.748),
+N(464.786,206.235),l(-.197,-.338),l(.49,-1.358),l(.122,-.425),l(.269,-.256),l(.301,-.171),l(.426,-.412),l(.064,.042),l(.546,.223),l(.431,-.045),l(.458,-.427),l(.554,-.398),l(.319,-.017),l(.121,.748),l(.364,.888),l(.167,.493),l(-.104,.481),l(-.142,.17),l(-.396,.158),l(-.304,.016),l(-.559,.031),l(-.508,.159),l(-.391,.567),l(-.158,.085),l(-.396,.13),l(-.789,-.32),l(-.688,-.024),
+N(465.79,210.652),l(-.017,-1.329),l(-.237,-.634),l(-.014,-.636),l(.04,-.438),l(-.087,-.381),l(-.689,-.999),l(.688,.024),l(.789,.32),l(.396,-.13),l(.158,-.085),l(.391,-.567),l(.508,-.159),l(.559,-.031),l(-.158,.976),l(.211,.211),l(.401,.068),l(.273,.125),l(.148,.239),l(-.044,.212),l(-.346,.271),l(-.687,.853),l(-.645,1.204),l(-.388,.539),l(-1.252,.348),
+N(427.243,211.207),l(.536,-.414),l(.68,-.513),l(.351,-.13),l(.695,.533),l(-.583,.568),l(-.317,.199),l(-.094,.143),l(-.1,.89),l(.017,.085),l(-.74,.3),l(.143,-.625),l(-.082,-.24),l(-.505,-.796),M(427.998,213.843),l(.112,.013),l(.56,-.06),l(.699,-.314),l(.896,-.062),l(.318,-.101),l(.432,-.03),l(.783,-.02),l(.945,.052),l(1.312,.048),l(.624,-.003),l(.88,-.034),l(1.104,.008),l(.385,.012),l(.321,.111),l(.082,.127),l(.121,.521),l(.059,.579),l(.537,1.409),l(.597,1.07),l(.64,.787),l(.292,.168),l(.88,-.034),l(1.021,-.246),l(.899,.122),l(.993,-.006),l(.184,-.411),l(.286,-.892),l(-.027,-.593),l(.089,-.354),l(.112,-.001),l(2.016,-.082),l(.181,.225),l(-.055,.48),l(.212,.168),l(.88,-.019),l(1.231,-.093),l(.322,.098),l(.052,.183),l(-.177,.693),l(-.057,.354),l(.303,.605),l(.109,.607),l(-.07,1.06),l(-.296,1.033),l(.021,.212),l(.117,.211),l(.844,.998),l(.306,.62),l(-.003,.466),l(-.198,.905),l(.007,.254),l(.199,.197),l(.61,-.004),l(.859,-.26),l(.991,-.146),l(1.24,.147),l(-.214,1.344),l(.195,1.059),l(.269,.323),l(.089,.254),l(-.132,.368),l(-.436,.44),l(-.124,.594),l(-.125,.086),l(-.517,-.04),l(-3.408,.091),l(-.051,.877),l(.015,1.342),l(-.024,3.249),l(.017,.848),l(.023,.594),l(.099,.438),l(.308,.394),l(.471,.436),l(1.354,1.391),l(.611,.632),l(-.93,.218),l(-1.96,.379),l(-1.044,.203),l(-.717,-.08),l(-1.164,.063),l(-.408,-.083),l(-.349,-.21),l(-2.024,.026),l(-.697,-.024),l(-.622,-.151),l(-.401,-.322),l(-.305,-.366),l(-.408,-.096),l(-.989,-.023),l(-2.59,.016),l(-1.636,-.019),l(-.631,-.011),l(-1.296,-.006),l(-2.201,.013),l(-.636,-.151),l(-.463,-.309),l(-.45,-.478),l(-.294,-.083),l(-.499,.088),l(-.591,.286),l(-.778,.513),l(-.758,-.462),l(-.352,.144),l(-.248,.197),l(.048,-1.809),l(-.017,-.805),l(-.029,-.649),l(.397,-.34),l(.221,-.269),l(.26,-.707),l(.163,-.734),l(.184,-1.398),l(.239,-.976),l(.321,-.918),l(.584,-.665),l(.183,-.565),l(.268,-.354),l(.64,-.228),l(.268,-.325),l(.423,-.679),l(.364,-1.201),l(.053,-.664),l(-.046,-.848),l(-.191,-.959),l(-.201,-.536),l(-.492,-.705),l(-.476,-.776),l(-.268,-.775),l(.139,-.495),l(.476,-.382),l(.158,-.156),l(.107,-.424),l(-.006,-.479),l(-.199,-.579),l(-.489,-.69),l(-.441,-.733),l(-.203,-1.031),l(-.181,-.423),l(-.276,-.366),l(-.666,-.974),l(-.072,-.15),
+N(452.198,239.34),l(-.611,-.632),l(-1.354,-1.391),l(-.471,-.436),l(-.308,-.394),l(-.099,-.438),l(-.023,-.594),l(-.017,-.848),l(.024,-3.249),l(-.015,-1.342),l(.051,-.877),l(3.408,-.091),l(.517,.04),l(.125,-.086),l(.124,-.594),l(.436,-.44),l(.132,-.368),l(-.089,-.254),l(-.269,-.323),l(-.195,-1.059),l(.214,-1.344),l(.678,.096),l(.365,.379),l(.172,.352),l(.195,.056),l(1.146,-.289),l(.175,-.071),l(.58,1.014),l(.489,.194),l(.925,.249),l(.922,.164),l(.325,.111),l(.366,-.087),l(.522,-.682),l(.212,-.354),l(.728,1.423),l(.38,.28),l(.895,.263),l(.477,.252),l(.416,.351),l(.561,.731),l(.381,.647),l(.348,.21),l(1.355,.091),l(.371,.012),l(.041,-.551),l(-.028,-1.427),l(-.032,-.791),l(-.136,-.183),l(-.362,.186),l(-.448,.455),l(-.515,-.012),l(-.445,-.279),l(-.837,-.843),l(-.456,-.605),l(-.043,-.296),l(.3,-1.02),l(.377,-1.175),l(-.372,-2.088),l(.029,-.622),l(.797,-1.276),l(.154,-.198),l(.347,-.214),l(3.015,-.499),l(.563,-.068),l(.703,.641),l(.263,.21),l(.729,.265),l(1.269,.6),l(1.574,.57),l(.732,.349),l(.385,.407),l(.559,.887),l(.434,.859),l(.013,.324),l(-.183,.27),l(-.709,.344),l(-.011,.127),l(.04,.594),l(-.091,1.682),l(.08,.353),l(.216,.168),l(.511,.266),l(.072,.197),l(-.197,.241),l(-.472,.258),l(-.792,.259),l(-.146,.34),l(-.094,.764),l(-.3,.807),l(.2,.479),l(.261,.408),l(.394,.125),l(-1.264,.53),l(-1.696,.575),l(-.932,.4),l(-2.165,.578),l(-.187,.128),l(-.009,.495),l(.152,.465),l(.087,.438),l(-.505,-.096),l(-.999,.049),l(-.794,.259),l(-.636,.54),l(-.312,.539),l(-.019,.579),l(-.168,.199),l(-.285,.114),l(-.999,.062),l(-.621,.202),l(-.306,.341),l(-.777,.937),l(-.562,.738),l(-.825,.951),l(-.354,.045),l(-.803,-.165),l(-.421,-.309),l(-.334,.129),l(-.521,.286),l(-.404,.017),l(-.594,-.209),l(-.264,-.097),l(-.154,-.169),l(-.163,-.027),l(-.187,-.154),l(-.456,-.393),l(-.294,-.055),l(-1.089,-.093),l(-.086,-.099),l(-.165,-.056),l(-1.845,.364),
+N(477.231,225.874),l(-.224,1.184),l(-.107,.48),l(.252,.917),l(.177,1.017),l(.149,.408),l(.238,.268),l(.803,.588),l(1.189,1.166),l(.454,.661),l(.14,.48),l(-.155,2.346),l(-.04,.41),l(-.214,.213),l(-.432,.073),l(-.322,.017),l(-.229,.213),l(-.076,.622),l(.08,.509),l(.029,.479),l(-.096,.283),l(-.185,-.111),l(-.562,-.463),l(-.763,-1.112),l(-.484,-.548),l(-.234,-.423),l(.036,-.212),l(.499,-.61),l(.116,-.227),l(.025,-.693),l(-.1,-.96),l(-.22,-.479),l(-.261,-.056),l(-.674,.061),l(-.702,.132),l(-.27,-.211),l(-.343,-.407),l(-.382,-.549),l(-.195,-.041),l(-.394,-.125),l(-.261,-.408),l(-.2,-.479),l(.3,-.807),l(.094,-.764),l(.146,-.34),l(.792,-.259),l(.472,-.258),l(.197,-.241),l(-.072,-.197),l(-.511,-.266),l(-.216,-.168),l(-.08,-.353),l(.091,-1.682),l(-.04,-.594),l(.011,-.127),l(.709,-.344),l(.183,-.27),l(-.013,-.324),l(-.434,-.859),l(-.559,-.887),l(.223,-.016),l(1.014,.107),l(.663,.538),l(.448,.841),l(.135,.946),l(.053,.584),l(.135,.766),l(.196,.559),
+N(245.934,224.314),l(.939,.136),l(1.122,.304),l(.355,-.03),l(.946,-.824),l(.336,.026),l(.48,.025),l(.415,-.243),l(1.471,-1.109),l(.874,-.485),l(.36,-.158),l(.934,-.076),l(1.283,.021),l(.045,.748),l(-.079,.621),l(-.064,.622),l(.036,.818),l(.141,.635),l(.335,.591),l(.813,.928),l(1.1,.939),l(.316,.097),l(.787,.023),l(.355,-.03),l(.676,.25),l(.688,.307),l(.75,.603),l(.3,.098),l(.882,.037),l(.096,.014),l(.385,.774),l(.398,.308),l(.22,.084),l(1.148,-.077),l(.636,.123),l(.537,.166),l(.403,.237),l(.085,.169),l(-.038,.565),l(.203,1.029),l(.03,.706),l(.138,2.032),l(.249,.944),l(.153,.112),l(.967,.036),l(.5,.012),l(1.615,.019),l(.693,.024),l(.042,.296),l(-.261,.835),l(.067,.563),l(.436,.407),l(.73,.362),l(.316,.479),l(.307,.774),l(.022,.494),l(-.185,1.173),l(-.238,.834),l(-.38,.765),l(-.421,.666),l(-.089,-.084),l(-1.952,-.991),l(-.352,-.054),l(-.928,.02),l(-.843,-.01),l(-.126,.128),l(-1.076,.204),l(-1.104,.162),l(-.784,.202),l(-.33,.044),l(-.332,.383),l(-.698,1.105),l(-.278,.341),l(-.133,.509),l(.016,.635),l(-.385,1.188),l(-.395,1.104),l(-.149,.325),l(-.592,-.109),l(-1.33,-.077),l(-.686,.004),l(-1.034,1.784),l(-.416,-1.084),l(-.341,-.309),l(-.37,-.195),l(-.531,-.067),l(-.527,.045),l(-.901,.034),l(-.615,-.194),l(-.193,-.169),l(-.322,-.181),l(-.292,.27),l(-2.026,2.087),l(-1.047,.006),l(-.272,-.182),l(-.397,-2.144),l(-.278,-.973),l(-.212,-.563),l(-.769,-1.11),l(-.249,-.676),l(.04,-.354),l(.437,-1.555),l(-.017,-.282),l(-.761,-.744),l(-.25,-.521),l(-.193,-1.213),l(-.304,-.647),l(-.555,-.745),l(-.152,-.253),l(-.018,-.142),l(-.132,-.295),l(-.049,-.48),l(.12,-.227),l(.723,-.61),l(.285,-.439),l(-.015,-.522),l(-.604,-1.168),l(-.022,-.48),l(.159,-.34),l(.21,-.368),l(-.347,-.845),l(.102,-.452),l(.532,-.582),l(.221,-.34),l(.156,-.34),l(-.236,-.902),l(-.057,-.522),l(.143,-.848),l(.15,-.523),l(.437,-.736),l(-.08,-.24),l(-.922,-1.646),l(-1.109,-1.843),
+N(473.575,260.04),l(-1.331,.011),l(-.192,.058),l(-.068,-.382),l(-.261,-.889),l(.071,-.495),l(-.075,-.296),l(-.095,-.324),l(.03,-.806),l(.057,-1.301),l(-.072,-.763),l(-.147,-.678),l(-.33,-.944),l(-.441,-.689),l(-.181,-.946),l(-.295,-1.199),l(-.159,-.183),l(.448,-.384),l(.396,-.412),l(1.68,-1.706),l(.114,-.227),l(-.09,-.367),l(.075,-.834),l(.229,-.481),l(.736,-.683),l(.205,-.341),l(.168,-.41),l(-.594,-.845),l(-.118,-.805),l(-.113,-.494),l(.128,-.283),l(.448,-.596),l(.201,-.411),l(-.132,-.805),l(-.086,-1.144),l(-.031,-.791),l(-.178,-.818),l(-.441,-.379),l(-.515,-.224),l(-1.167,-.347),l(-1.042,-.445),l(-.658,-.223),l(-1.438,-.006),l(-.137,-.14),l(-.025,-.495),l(-.011,-.212),l(-.087,-.438),l(-.152,-.465),l(.009,-.495),l(.187,-.128),l(2.165,-.578),l(.932,-.4),l(1.696,-.575),l(1.264,-.53),l(.195,.041),l(.382,.549),l(.343,.407),l(.27,.211),l(.702,-.132),l(.674,-.061),l(.261,.056),l(.22,.479),l(.1,.96),l(-.025,.693),l(-.116,.227),l(-.499,.61),l(-.036,.212),l(.234,.423),l(.484,.548),l(.763,1.112),l(.562,.463),l(.185,.111),l(.096,-.283),l(-.029,-.479),l(-.08,-.509),l(.076,-.622),l(.229,-.213),l(.322,-.017),l(.432,-.073),l(.214,-.213),l(.04,-.41),l(.155,-2.346),l(-.14,-.48),l(-.454,-.661),l(-1.189,-1.166),l(-.803,-.588),l(-.238,-.268),l(-.149,-.408),l(-.177,-1.017),l(-.252,-.917),l(.107,-.48),l(.224,-1.184),l(.898,.018),l(1.019,.149),l(.526,-.088),l(.643,-.356),l(.123,-.128),l(.229,.083),l(.323,.394),l(.512,.294),l(.963,-.02),l(.964,-.006),l(.42,.054),l(.396,-.144),l(.543,-.766),l(.21,-.326),l(.276,.083),l(.646,.42),l(.448,-.031),l(1.243,-.559),l(1.218,-.402),l(.515,-.342),l(1.157,-.836),l(.128,.167),l(.212,.479),l(-.185,.579),l(-.302,.453),l(-.198,.255),l(.181,.451),l(.129,.72),l(-.012,.466),l(.182,1.115),l(-.101,.58),l(-.258,.325),l(.374,.705),l(.154,.494),l(-.006,1.115),l(-.004,.819),l(.043,.184),l(.185,.127),l(.327,.084),l(.015,.269),l(-.165,.494),l(-.563,.58),l(.184,.381),l(-.08,.283),l(-.418,.565),l(-.802,.906),l(-.512,.622),l(-.72,.651),l(-1.36,.751),l(-1.48,.653),l(-.73,.228),l(-1.308,.582),l(-.852,.637),l(-1.286,1.443),l(-.886,.85),l(-1.193,.878),l(-1.181,.836),l(-.268,.128),l(-.035,.96),l(-.083,.495),l(.058,.127),l(.719,.535),l(.188,.381),l(-.166,.452),l(-.085,.184),l(.461,1.511),l(.071,.564),l(.06,.155),l(.246,.014),l(.171,-.128),l(.141,-.085),l(.043,.607),l(-.234,2.218),l(-.284,.82),l(.325,.196),l(.152,.057),l(-.025,.325),l(-.157,.311),l(-.516,.566),l(-.699,.538),l(-.664,.34),l(-1.266,.412),l(-.796,.312),l(-.688,.228),l(-.895,.524),l(-.652,.665),l(-.337,.51),l(.292,.338),l(.589,.338),l(.045,.325),l(-.149,1.022),
+N(499.85,236.166),l(.544,-.071),l(.622,-.369),l(.18,.028),l(.346,.098),l(.269,-.085),l(.396,-.368),l(.911,-.143),l(.311,.281),l(.305,-.028),l(.101,-.185),l(-.171,-.366),l(.771,-.539),l(.423,-.198),l(.322,.226),l(.389,-.213),l(-.308,-.494),l(.207,-.282),l(.505,-.425),l(.229,.296),l(.229,.056),l(.558,-.594),l(-.158,-.197),l(-.253,-.409),l(.094,-.297),l(.243,.014),l(.27,-.071),l(.172,-.34),l(-.297,-.875),l(.06,-.339),l(.255,-.043),l(.117,.07),l(.253,.438),l(.28,.099),l(.2,-.41),l(.692,-.524),l(.235,-.367),l(.134,-.452),l(.168,-.692),l(-.133,-.354),l(.003,-.226),l(.537,-.468),l(.356,.324),l(.455,.648),l(.612,.281),l(.141,.198),l(.213,.847),l(.294,1.821),l(.093,.663),l(.231,.791),l(.391,.733),l(.163,.466),l(-.038,.367),l(-.069,.155),l(-.058,.099),l(-.537,.807),l(-.22,-.127),l(-.189,-.366),l(-.555,-.748),l(-.297,.143),l(-.05,.424),l(.193,.875),l(.396,.521),l(.079,.396),l(-.307,.636),l(-.746,1.005),l(-.045,.452),l(.041,.89),l(-.18,.946),l(-.709,2.12),l(-.825,2.572),l(-1.254,3.788),l(-1.324,4.48),l(-.518,1.568),l(-.188,.255),l(-.508,.637),l(-.2,.113),l(-1.369,.102),l(-.999,.327),l(-.474,.468),l(-.813,.086),l(-.363,-.465),l(-.196,-.142),l(-.546,-.182),l(-.37,.071),l(-.269,-.057),l(-.863,-.718),l(-.104,-.24),l(-.02,-.565),l(-.104,-.239),l(-.46,-.366),l(-.124,-.282),l(.001,-.721),l(.345,-1.088),l(-.094,-.325),l(-.287,-.479),l(-.62,-.931),l(-.189,-.494),l(.075,-.664),l(.391,-1.37),l(.228,-.213),l(.474,-.185),l(.768,-1.371),l(.686,-1.174),l(.104,-.325),l(.151,-1.103),l(-.11,-.353),l(-.278,-.226),l(-.354,-.366),l(.066,-.184),l(.252,-.509),l(-.521,-.861),l(-.117,-.677),l(-.069,-.494),l(-.231,-.721),l(.024,-.112),l(.517,-.693),l(.362,-.594),l(.163,-.438),l(.007,-1.073),l(.484,-.016),
+N(468.138,234.908),l(.011,.212),l(.025,.495),l(.137,.14),l(1.438,.006),l(.658,.223),l(1.042,.445),l(1.167,.347),l(.515,.224),l(.441,.379),l(.178,.818),l(.031,.791),l(.086,1.144),l(.132,.805),l(-.201,.411),l(-.448,.596),l(-.128,.283),l(.113,.494),l(.118,.805),l(.594,.845),l(-.168,.41),l(-.205,.341),l(-.736,.683),l(-.229,.481),l(-.075,.834),l(.09,.367),l(-.114,.227),l(-1.68,1.706),l(-.396,.412),l(-.448,.384),l(-.342,-.225),l(-.547,-.124),l(-.442,-.025),l(-.529,.145),l(-.31,.016),l(-.99,-.403),l(-.597,-.139),l(-.72,-.023),l(-.067,-.042),l(-.186,-.098),l(-.306,-.451),l(-.479,-.35),l(-.549,-.167),l(-.938,-.136),l(-.352,-.153),l(-.524,-.873),l(-.163,-.564),l(.032,-.565),l(-.127,-.239),l(-.78,.019),l(-.201,-.098),l(-.109,-.211),l(.051,-.537),l(-.106,-.169),l(-.552,-.266),l(-.533,-.223),l(-.57,-.321),l(-.563,-.491),l(-.377,-.662),l(-.246,-.96),l(-.469,-.604),l(-.43,-.478),l(-.267,-.451),l(.103,-.227),l(.594,.209),l(.404,-.017),l(.521,-.286),l(.334,-.129),l(.421,.309),l(.803,.165),l(.354,-.045),l(.825,-.951),l(.562,-.738),l(.777,-.937),l(.306,-.341),l(.621,-.202),l(.999,-.062),l(.285,-.114),l(.168,-.199),l(.019,-.579),l(.312,-.539),l(.636,-.54),l(.794,-.259),l(.999,-.049),l(.505,.096),
+N(444.673,255.519),l(-.006,3.434),l(.031,1.894),l(.025,2.246),l(-.057,.205),l(-.454,.318),l(-.545,.302),l(-.581,.498),l(-.427,.034),l(-.581,-.166),l(-.745,-.042),l(-.892,.048),l(-.517,-.039),l(-.296,-.212),l(-.055,-.528),l(-.042,-.345),l(-.193,-.222),l(-.637,-.348),l(-.329,-.127),l(-.335,.116),l(-.109,.217),l(-.317,.416),l(-.584,.27),l(-.152,.068),l(-.458,-.491),l(-1.041,-1.001),l(-.458,-.606),l(-.359,-1.03),l(-.345,-.72),l(-.136,-.493),l(.12,-.269),l(.053,-.34),l(-.458,-.719),l(-.231,-.861),l(.148,-.861),l(-.002,-.48),l(-.537,-1.326),l(-.496,-2.244),l(.772,-.02),l(.04,-.678),l(-.077,-.749),l(-.456,-.006),l(.016,-.06),l(-.099,-.522),l(-.26,-.508),l(-1.018,-1.283),l(-.343,-.55),l(-1.102,-2.158),l(-.841,-1.623),l(-.9,-1.509),l(-.988,-1.269),l(-.511,-1.044),l(-.122,-.396),l(-.045,-.551),l(.015,-.578),l(.248,-.197),l(.352,-.144),l(.758,.462),l(.778,-.513),l(.591,-.286),l(.499,-.088),l(.294,.083),l(.45,.478),l(.463,.309),l(.636,.151),l(2.201,-.013),l(1.296,.006),l(.631,.011),l(1.636,.019),l(2.59,-.016),l(.989,.023),l(.408,.096),l(.305,.366),l(.401,.322),l(.622,.151),l(.697,.024),l(2.024,-.026),l(.349,.21),l(.408,.083),l(1.164,-.063),l(.717,.08),l(1.044,-.203),l(1.96,-.379),l(.93,-.218),l(1.845,-.364),l(.165,.056),l(.086,.099),l(1.089,.093),l(.294,.055),l(.456,.393),l(-.811,.315),l(-.891,.02),l(-.284,.143),l(-.993,.938),l(-.209,.029),l(-.62,-.773),l(-1.048,.134),l(-2.962,.47),l(-1.183,.021),l(.005,1.215),l(-.007,1.286),l(-.025,.876),l(-.043,1.201),l(.002,3.561),l(-.586,.046),l(-1.564,.052),l(-.146,.028),l(-.106,2.657),l(-.009,1.201),l(.013,1.624),l(.007,.806),
+N(248.453,316.576),l(-.306,.101),l(-.892,-.087),l(-.538,-.293),l(-.236,-.015),l(-.311,.163),l(-.418,.398),l(-.498,.192),l(-1.156,.091),l(-.349,.09),l(-.358,.207),l(-.267,.621),l(-.114,.341),l(.06,.532),l(-.163,.622),l(-.104,.148),l(-.453,.031),l(-.534,.104),l(-.956,-.413),l(.667,-.639),l(.326,-.444),l(.582,-.4),l(.025,-.147),l(-.372,-.177),l(-.273,-.117),l(-1.353,.534),l(-1.01,-.013),l(-.545,.163),l(-.202,-.339),l(.128,-.192),l(.959,-.268),l(.266,.028),l(.792,-.208),l(.441,-.118),l(-.605,-.162),l(-.582,.002),l(-.77,.001),l(-.014,-.413),l(.265,-.31),l(-.007,-.191),l(-.446,-.073),l(-.356,-.44),l(-.66,.384),l(-.669,-.175),l(.292,-.53),l(.041,-.177),l(-.378,.045),l(-.361,.147),l(-.416,-.396),l(-.215,-.117),l(.413,-.279),l(.114,-.177),l(-.091,-.278),l(-.053,-.073),l(-.351,.03),l(-.773,-.424),l(-.135,-.059),l(.844,-.192),l(.253,-.161),l(.1,-.294),l(.396,-.366),l(.049,-.234),l(-.641,.06),l(-.257,.104),l(-.312,-.073),l(-.256,-.672),l(.573,-.395),l(-.565,-.378),l(-.12,-.421),l(.757,-.452),l(-.14,-.421),l(-.686,.422),l(-.091,-1.523),l(.399,-.596),l(-.185,-.825),l(.013,-.218),l(.593,.014),l(.41,.245),l(.711,.071),l(.171,-.246),l(.002,-.159),l(-.896,-.447),l(-.867,.146),l(-.317,-.173),l(-.536,.059),l(-.017,-.231),l(.339,-.333),l(.025,-.246),l(-.067,-.087),l(.186,-.202),l(.536,.014),l(.229,-.377),l(.01,-.216),l(-.722,-.389),l(-.354,-.129),l(-.886,.045),l(-.332,-.101),l(-.024,-.49),l(-.939,.16),l(-.115,-.101),l(.122,-.145),l(1.032,-.521),l(.251,-.116),l(.4,-.404),l(.266,-.389),l(.833,-.06),l(.268,.201),l(.059,.346),l(-.648,.202),l(-.323,.274),l(.11,.505),l(.117,.058),l(.191,-.102),l(.268,-.39),l(.183,-.087),l(.242,.101),l(-.037,.317),l(.057,.504),l(.886,-.996),l(.161,-.678),l(.056,-.647),l(.237,-.375),l(.079,-.058),l(.631,-.217),l(-.201,-.071),l(-.438,-.143),l(-.056,-.158),l(.101,-.273),l(.246,-.072),l(.571,-.245),l(.599,-.431),l(.271,-.459),l(-.061,-.229),l(-.394,-.157),l(-.662,-.399),l(-.053,-.372),l(.139,-.243),l(.105,-.458),l(-.06,-.828),l(.366,-.33),l(.676,-.272),l(-.431,-.585),l(-.053,-.784),l(.133,-.158),l(.554,-.157),l(.054,-.314),l(-.116,-.285),l(-.317,-.085),l(-.272,-.198),l(.233,-.329),l(.087,-.313),l(-.401,-.185),l(-.274,-.014),l(-.161,.101),l(-.476,.414),l(-.548,.058),l(-.087,.001),l(-.289,-.199),l(-.16,-.484),l(-.399,-.726),l(-.133,-.697),l(.188,-.911),l(.137,-.413),l(.722,-.739),l(.535,-.767),l(-.006,-.326),l(-.544,-1.757),l(.001,-.608),l(.088,-.567),l(-.076,-.438),l(-.528,-.891),l(-.04,-.298),l(.236,-.198),l(.499,.098),l(.182,-.085),l(.2,-.142),l(.097,-.143),l(.41,-1.288),l(.252,-.481),l(.304,-.935),l(.18,-.65),l(.664,-.808),l(.363,-.722),l(.201,-.636),l(.252,-.946),l(.311,-.691),l(.187,-.128),l(.273,-.382),l(.013,-.296),l(-.312,-.847),l(.082,-.184),l(.455,-.452),l(.206,-.339),l(.028,-.24),l(-.093,-.226),l(-.166,-.805),l(-.292,-2.088),l(-.098,-.86),l(.031,-.565),l(.412,-.565),l(.37,-.537),l(.207,-.564),l(.007,-.734),l(-.339,-.521),l(-.098,-.409),l(.295,-.96),l(.218,-.941),l(.127,-.556),l(.461,-.594),l(.171,-.918),l(.243,-.975),l(.126,-.805),l(.082,-.565),l(-.063,-1.087),l(.422,-.664),l(.211,-.494),l(-.221,-.932),l(-.048,-.833),l(.148,-.24),l(-.022,-.917),l(.229,-.607),l(-.124,-.297),l(-.365,-.084),l(.06,-.324),l(-.046,-.396),l(.223,-.198),l(.402,-.198),l(.137,-.424),l(-.008,-.819),l(.093,-.593),l(.182,-.918),l(-.004,-2.344),l(-.172,-2.074),l(-.042,-1.539),l(-.194,-.974),l(-.15,-.387),l(.852,-.275),l(.259,-.298),l(.228,-.933),l(.15,-.199),l(.586,-.187),l(.152,.253),l(.555,.745),l(.304,.647),l(.193,1.213),l(.25,.521),l(.761,.744),l(.017,.282),l(-.437,1.555),l(-.04,.354),l(.249,.676),l(.769,1.11),l(.212,.563),l(.278,.973),l(.397,2.144),l(.272,.182),l(1.047,-.006),l(.107,.056),l(.212,.14),l(.161,.154),l(-.093,.636),l(-.541,1.457),l(-.36,.256),l(-1.346,.53),l(-.819,.372),l(-.204,.312),l(.25,.817),l(-.421,.722),l(-.007,.579),l(.113,.437),l(.34,.449),l(.03,.226),l(-.273,.369),l(-.161,.382),l(.114,.507),l(.53,.477),l(.011,.227),l(-.152,.311),l(-.333,.017),l(-.791,.089),l(-.329,.835),l(-.464,.835),l(-.608,.694),l(-.282,.439),l(-.823,1.839),l(.167,1.466),l(.054,.762),l(-.124,.185),l(-.492,.271),l(-.292,.34),l(-.388,1.201),l(-.156,.961),l(.285,.633),l(.408,1.154),l(.784,2.816),l(.055,.69),l(-.075,.41),l(-1.083,1.854),l(-.319,.595),l(.088,.409),l(.189,1.06),l(-.078,.325),l(-1.334,1.11),l(-.231,.312),l(-.142,1.075),l(.014,1.16),l(.249,1.103),l(.437,.932),l(-.202,.143),l(-.951,.529),l(-.126,.17),l(-.053,.312),l(-.511,2.427),l(-.316,.541),l(-.1,.37),l(.123,1.835),l(.231,.867),l(.012,.427),l(-.596,.317),l(-.172,.172),l(-.106,.271),l(.094,1.226),l(.125,.128),l(.555,.111),l(.088,.655),l(-.191,1.458),l(.252,.585),l(.26,.185),l(.789,.11),l(.302,.17),l(-.007,.186),l(-.245,.202),l(-.322,.13),l(-.726,.033),l(-.757,.146),l(.176,.171),l(.586,.298),l(.552,.385),l(.017,.216),l(-.767,.794),l(-.059,1.094),l(.158,1.035),l(-.216,.896),l(-.212,.434),l(-.226,.262),l(-.598,.161),l(-.28,.219),l(-.249,.781),l(.446,.648),l(-.069,.188),l(-.296,1.218),l(-.307,.263),l(-1.729,1.01),l(-.271,.292),l(-.037,.45),l(.28,1.309),l(.508,1.123),l(.218,.043),l(.961,-.283),l(.654,-.121),l(.187,.248),l(.231,2.285),l(.778,.568),l(.669,.041),l(1.41,-.052),l(2.827,.132),l(.841,.217),l(1.385,.36),l(.286,.039),M(236.642,296.773),l(-.394,-.113),l(-.43,-.028),l(-.21,-.171),l(-.133,-.229),l(.21,-.457),l(.15,-.657),l(-.087,-.514),l(.011,-.414),l(.364,-.728),l(.817,-.116),l(.36,.327),l(.044,.328),l(-.688,.443),l(-.146,.229),l(.493,.771),l(-.194,1.058),l(-.167,.271),M(238.177,317.937),l(-.445,-.177),l(.083,-.842),l(-.849,.075),l(-.073,-.368),l(.218,-.354),l(.823,.102),l(.508,-.207),l(.205,.103),l(.054,.812),l(-.267,.34),l(-.257,.518),M(247.801,322.062),l(-1.033,.102),l(-.467,-.118),l(-.55,-.237),l(-.42,.001),l(-.481,.104),l(-.935,.226),l(-.496,.03),l(.125,-.343),l(.202,-.312),l(-.104,-.312),l(.906,-.15),l(1.434,.058),l(.433,.323),l(.706,-.007),l(-.622,-.689),l(-.307,-.163),l(-.56,-.117),l(-.178,-.089),l(-.188,.03),l(-.338,-.341),l(-.229,-.4),l(1.611,-.581),l(-.044,-.296),l(-.165,-.147),l(-1.819,.285),l(-.292,-.222),l(.263,-.474),l(.146,-.163),l(1.074,-.52),l(.868,-.637),l(.414,.28),l(1.153,.062),l(-.008,1.144),l(-.098,3.675),
+N(456.133,239.67),l(.187,.154),l(.163,.027),l(.154,.169),l(.264,.097),l(-.103,.227),l(.267,.451),l(.43,.478),l(.469,.604),l(.246,.96),l(.377,.662),l(.563,.491),l(.57,.321),l(.533,.223),l(.552,.266),l(.106,.169),l(-.051,.537),l(.109,.211),l(.201,.098),l(.78,-.019),l(.127,.239),l(-.032,.565),l(.163,.564),l(.524,.873),l(.352,.153),l(.938,.136),l(.549,.167),l(.479,.35),l(.306,.451),l(.186,.098),l(-.317,.411),l(-.388,.327),l(-.507,.243),l(-.747,.075),l(-.304,.115),l(-.7,.823),l(-.586,.583),l(-.362,.229),l(-.747,.357),l(-.388,.355),l(-.107,.636),l(-.222,.666),l(-.247,.241),l(-.634,.357),l(-.98,.33),l(-.249,.214),l(-.217,.708),l(-.345,.963),l(-.288,.354),l(-.237,.129),l(-.584,.116),l(-.43,-.026),l(-.473,.06),l(-.511,-.011),l(-.819,-.193),l(-.744,-.32),l(-.979,-.645),l(-.545,-.039),l(-.333,.186),l(-.084,.24),l(-.585,1.134),l(-.382,.469),l(-.651,.625),l(-.632,.428),l(-.8,.372),l(-.823,.033),l(-.854,.047),l(-.368,-.097),l(-.11,-.183),l(.003,-.48),l(.243,-.609),l(.166,-.538),l(-.21,-.762),l(-.547,-.943),l(-.716,-.787),l(-.528,-.067),l(-.007,-.806),l(-.013,-1.624),l(.009,-1.201),l(.106,-2.657),l(.146,-.028),l(1.564,-.052),l(.586,-.046),l(-.002,-3.561),l(.043,-1.201),l(.025,-.876),l(.007,-1.286),l(-.005,-1.215),l(1.183,-.021),l(2.962,-.47),l(1.048,-.134),l(.62,.773),l(.209,-.029),l(.993,-.938),l(.284,-.143),l(.891,-.02),l(.811,-.315),
+N(279.288,257.295),l(.02,.239),l(-.544,1.57),l(-.375,.468),l(-1.007,.74),l(-.301,.27),l(-.352,.51),l(-.609,-.363),l(-.35,-.097),l(-.235,.029),l(-.387,.172),l(-.745,.131),l(-.71,.005),l(-.564,-.096),l(-.992,-.333),l(-.607,-.025),l(-1.187,.332),l(-.19,-.056),l(.064,-.212),l(.425,-.426),l(.486,-.398),l(.11,-.198),l(-.21,-.619),l(.048,-.227),l(.625,-.851),l(.617,-1.203),l(.018,-.268),l(-.939,-.503),l(-.65,-.18),l(-1.448,-.697),l(-1.632,-1.106),l(-.671,-.307),l(-1.173,-.204),l(-.498,-.237),l(-.835,-.588),l(-.576,-.562),l(-1.271,-1.376),l(-.782,-.913),l(-.225,-.337),l(-.19,-.056),l(.149,-.325),l(.395,-1.104),l(.385,-1.188),l(-.016,-.635),l(.133,-.509),l(.278,-.341),l(.698,-1.105),l(.332,-.383),l(.33,-.044),l(.784,-.202),l(1.104,-.162),l(1.076,-.204),l(.126,-.128),l(.843,.01),l(.928,-.02),l(.352,.054),l(1.952,.991),l(.089,.084),l(.253,.408),l(.084,.663),l(.521,.872),l(.104,.959),l(-.132,.862),l(-.086,.721),l(-.006,.72),l(.372,.04),l(.818,.15),l(.925,.221),l(.346,-.03),l(.709,-.413),l(.115,-.001),l(.724,.278),l(.843,.404),l(.121,.437),l(.487,2.524),l(.254,.563),l(.224,.055),l(1.29,-.445),l(.234,.112),l(.491,.336),l(.019,.141),l(-.321,.75),l(-.298,.835),l(-.222,.819),l(-.027,.777),l(.063,.423),
+N(444.673,255.519),l(.528,.067),l(.716,.787),l(.547,.943),l(.21,.762),l(-.166,.538),l(-.243,.609),l(-.003,.48),l(.11,.183),l(.368,.097),l(.854,-.047),l(.823,-.033),l(.8,-.372),l(.632,-.428),l(.651,-.625),l(.382,-.469),l(.585,-1.134),l(.084,-.24),l(.333,-.186),l(.545,.039),l(.979,.645),l(.744,.32),l(.819,.193),l(.511,.011),l(.473,-.06),l(.43,.026),l(.584,-.116),l(.237,-.129),l(.288,-.354),l(.345,-.963),l(.217,-.708),l(.249,-.214),l(.98,-.33),l(.634,-.357),l(.247,-.241),l(.222,-.666),l(.107,-.636),l(.388,-.355),l(.747,-.357),l(.362,-.229),l(.586,-.583),l(.7,-.823),l(.304,-.115),l(.747,-.075),l(.507,-.243),l(.388,-.327),l(.317,-.411),l(.067,.042),l(.72,.023),l(.597,.139),l(.99,.403),l(.31,-.016),l(.529,-.145),l(.442,.025),l(.547,.124),l(.342,.225),l(.159,.183),l(.295,1.199),l(.181,.946),l(.441,.689),l(.33,.944),l(.147,.678),l(.072,.763),l(-.057,1.301),l(-.03,.806),l(.095,.324),l(-1.449,-.585),l(-.279,.199),l(-.657,.979),l(-.28,.567),l(-.005,.325),l(.39,.676),l(.307,.465),l(.458,.322),l(.671,.109),l(.595,-.004),l(.076,-.594),l(.064,-.198),l(.312,-.186),l(.131,-.015),l(.192,-.058),l(1.331,-.011),l(-.182,1.195),l(-.352,.849),l(-.182,.184),l(-.404,.1),l(-.093,.24),l(.199,.536),l(-.104,.467),l(-.248,.354),l(-.569,.453),l(-.923,.581),l(-.591,.75),l(-1.383,1.98),l(-.631,.834),l(-1.242,1.373),l(-1.193,1.062),l(-.829,.863),l(-1.434,1.034),l(-1.379,1.091),l(-.552,.382),l(-.989,.638),l(-.676,.298),l(-.782,.101),l(-.98,-.012),l(-.549,.071),l(-.132,.354),l(.013,.127),l(-.109,.085),l(-.449,-.098),l(-.553,-.126),l(-.303,.015),l(-.25,.198),l(-.272,.312),l(-.226,.113),l(-.36,-.056),l(-.768,-.408),l(-.759,-.168),l(-.542,-.013),l(-.31,.113),l(-.38,.27),l(-.482,-.099),l(-.645,-.21),l(-.295,.1),l(-.638,.043),l(-.589,.214),l(-.729,.538),l(-.72,.086),l(-.44,-.013),l(-.667,-.084),l(-.738,.072),l(-.575,.199),l(-.827,.82),l(-1.251,-.576),l(-.092,-.509),l(-.218,-.183),l(-.479,-.056),l(-.28,.085),l(-.257,-.479),l(-.343,-.056),l(-.149,.255),l(-.035,.197),l(-.312,-.112),l(-.119,-.226),l(.267,-.495),l(.103,-.424),l(-.857,-1.354),l(-.323,-.282),l(-.134,-.226),l(.173,-.17),l(.636,-.044),l(.172,-.424),l(.06,-.819),l(-.163,-.663),l(-.186,-.522),l(-.61,-1.143),l(-.75,-1.029),l(-.472,-.903),l(-.612,-1.383),l(-.646,-1.467),l(-.573,-.818),l(-.436,-.467),l(.152,-.068),l(.584,-.27),l(.317,-.416),l(.109,-.217),l(.335,-.116),l(.329,.127),l(.637,.348),l(.193,.222),l(.042,.345),l(.055,.528),l(.296,.212),l(.517,.039),l(.892,-.048),l(.745,.042),l(.581,.166),l(.427,-.034),l(.581,-.498),l(.545,-.302),l(.454,-.318),l(.057,-.205),l(-.025,-2.246),l(-.031,-1.894),l(.006,-3.434),M(462.462,268.501),l(.412,-.044),l(.194,-.553),l(.633,-.343),l(1.035,-.303),l(.263,-.199),l(.582,-1.007),l(.268,-.326),l(.143,-.241),l(-.104,-.226),l(-.967,-.744),l(-.33,-.337),l(-.422,-.266),l(-.308,.086),l(-.995,.359),l(-.65,.329),l(-.513,.567),l(-.275,.44),l(-.691,.611),l(.12,.409),l(.582,.858),l(.285,.366),l(.739,.561),M(432.955,250.661),l(.456,.006),l(.077,.749),l(-.04,.678),l(-.772,.02),l(-.078,-.354),l(-.028,-.396),l(.285,-.297),l(.101,-.406),
+N(471.719,258.047),l(.075,.296),l(-.071,.495),l(.261,.889),l(.068,.382),l(-.131,.015),l(-.312,.186),l(-.064,.198),l(-.076,.594),l(-.595,.004),l(-.671,-.109),l(-.458,-.322),l(-.307,-.465),l(-.39,-.676),l(.005,-.325),l(.28,-.567),l(.657,-.979),l(.279,-.199),l(1.449,.585),
+N(462.462,268.501),l(-.739,-.561),l(-.285,-.366),l(-.582,-.858),l(-.12,-.409),l(.691,-.611),l(.275,-.44),l(.513,-.567),l(.65,-.329),l(.995,-.359),l(.308,-.086),l(.422,.266),l(.33,.337),l(.967,.744),l(.104,.226),l(-.143,.241),l(-.268,.326),l(-.582,1.007),l(-.263,.199),l(-1.035,.303),l(-.633,.343),l(-.194,.553),l(-.412,.044),
+N(790.15,283.022),l(.738,.197),l(.008,-.227),l(-.242,-.524),l(.052,-.284),l(.233,.014),l(.389,.17),l(.37,.751),l(.277,.964),l(.48,.17),l(1.753,.691),l(.506,.113),l(.37,-.072),l(.699,-.483),l(.885,-.343),l(.4,.027),l(.329,.17),l(.066,.454),l(-.022,.198),l(-.402,1.236),l(-.283,.072),l(-.761,.058),l(-.035,.683),l(-.124,.156),l(-.424,.029),l(-.746,.016),l(-.432,.2),l(-.271,.284),l(.041,.384),l(.254,.525),l(-.002,.213),l(-.151,.199),l(-.646,.515),l(-.898,1.129),l(-.847,1.058),l(-.756,.587),l(-.68,.316),l(-.337,-.171),l(-.47,-.313),l(-.237,-.328),l(.056,-.314),l(.288,-.386),l(.307,-.671),l(.398,-.5),l(-.031,-.343),l(-.271,-.128),l(-.761,-.582),l(-.421,-.185),l(-.593,-.184),l(-.98,-.452),l(-.306,-.256),l(-.11,-.17),l(.081,-.128),l(.419,-.157),l(1.389,-.685),l(.209,-.512),l(-.078,-.695),l(.087,-.312),l(.396,-.441),l(.032,-.383),l(-.482,-.837),l(.081,-.567),l(-.156,-.311),l(-.479,-.655),l(-.574,-.678),l(.102,-.164),l(-.145,-.304),l(-.291,-.351),l(-.336,-.188),l(-.29,-.163),l(.117,.233),l(.497,.515),l(.049,.141),l(-.169,0),l(-.211,-.281),l(-.525,-.631),l(-.622,-.771),l(-.518,-.561),l(.001,-.117),l(-.268,-.257),l(.04,-.141),l(.013,-.14),l(-.048,-.188),l(-.197,-.396),l(-.379,-.42),l(-.347,-.257),l(.163,-.046),l(.205,.093),l(.358,-.047),l(.131,-.093),l(.084,.28),l(-.149,.187),l(.186,.303),l(.177,.21),l(.167,.116),l(.228,.164),l(.041,-.141),l(.269,.023),l(.519,.257),l(.42,.117),l(.274,.07),l(.128,.257),l(-.011,.141),l(.185,.023),l(.146,-.188),l(.185,.023),l(-.022,.164),l(.227,.351),l(.249,.187),l(.233,.28),l(-.18,.023),l(-.076,.164),l(.093,.163),l(-.242,-.023),l(-.175,-.047),l(.143,.117),l(.251,.188),l(.23,.233),l(.352,.28),l(.063,.234),l(.019,.21),l(-.261,-.047),l(.096,.164),l(.239,.351),l(.256,.188),l(-.292,.023),l(-.226,0),l(-.205,-.047),l(-.006,.141),l(.306,.14),l(.324,.164),l(-.09,.211),l(.205,.046),l(.265,-.023),l(.226,0),l(.223,.141),l(-.114,.07),l(-.031,.141),l(.025,.141),l(.136,.06),M(782.939,297.694),l(-.088,.158),l(-.558,.13),l(-.309,.288),l(-.322,.101),l(-.246,.244),l(-.692,-.242),l(-.16,.086),l(.15,.216),l(.429,.415),l(-.141,.173),l(.02,.259),l(-.064,.431),l(-.218,-.071),l(-.976,-.099),l(.418,.229),l(.449,.244),l(-.278,.49),l(-.427,.896),l(-.212,.549),l(-.418,.318),l(-.673,.349),l(-.171,.246),l(-.259,.145),l(-.581,.233),l(-.593,.406),l(-.398,.015),l(-1.156,-.258),l(-.628,.112),l(-.585,-.442),l(-.812,-.158),l(-.373,-.066),l(-.162,-.308),l(-.467,-.098),l(-.24,.142),l(-.062,.168),l(-.78,.095),l(-.214,-.166),l(-.515,-.095),l(-.146,-.286),l(.432,-.089),l(-.223,-.216),l(.328,-.116),l(.322,-.001),l(-.452,-.482),l(.82,.266),l(-.464,-.576),l(.121,-.145),l(.946,.156),l(.082,-.13),l(-.141,-.173),l(-.201,-.216),l(-.06,-.288),l(.283,-.303),l(.569,-.246),l(.328,-.374),l(.561,-.375),l(.102,-.302),l(.998,-.575),l(1.106,-.275),l(.713,-.331),l(.544,-.36),l(.377,-.101),l(.685,-.575),l(.066,-.272),l(.48,-.302),l(.373,-.015),l(.787,-.331),l(.664,-.402),l(.126,-.215),l(-.008,-.172),l(.266,-.144),l(.448,-.302),l(-.109,-.501),l(.076,-.214),l(.166,-.44),l(.306,.048),l(.066,-.152),l(.58,-.259),l(.444,-.272),l(.137,-.285),l(.131,-1.187),l(.512,-.647),l(.372,.047),l(.365,.165),l(.032,.259),l(.337,.06),l(.187,.186),l(.231,.799),l(.312,.242),l(.973,-.645),l(.426,-.029),l(.367,.113),l(.222,.5),l(-.197,.399),l(.299,.429),l(.066,.271),l(-.611,.659),l(-.261,.401),l(-.476,.358),l(-.868,.746),l(-.578,.359),l(-.295,.13),l(-.236,.258),l(-.389,.159),l(-.271,.258),l(.416,.407),l(.428,.047),l(.421,.289),l(-.276,.113),l(-.484,.07),l(-.503,-.296),l(-.488,.131),l(-.352,.158),
+N(247.899,318.387),l(.008,-1.144),l(.821,.289),l(.06,.206),l(-.354,.312),l(-.534,.337),M(248.453,316.576),l(-.286,-.039),l(-1.385,-.36),l(-.841,-.217),l(-2.827,-.132),l(-1.41,.052),l(-.669,-.041),l(-.778,-.568),l(-.231,-2.285),l(-.187,-.248),l(-.654,.121),l(-.961,.283),l(-.218,-.043),l(-.508,-1.123),l(-.28,-1.309),l(.037,-.45),l(.271,-.292),l(1.729,-1.01),l(.307,-.263),l(.296,-1.218),l(.069,-.188),l(-.446,-.648),l(.249,-.781),l(.28,-.219),l(.598,-.161),l(.226,-.262),l(.212,-.434),l(.216,-.896),l(-.158,-1.035),l(.059,-1.094),l(.767,-.794),l(-.017,-.216),l(-.552,-.385),l(-.586,-.298),l(-.176,-.171),l(.757,-.146),l(.726,-.033),l(.322,-.13),l(.245,-.202),l(.007,-.186),l(-.302,-.17),l(-.789,-.11),l(-.26,-.185),l(-.252,-.585),l(.191,-1.458),l(-.088,-.655),l(-.555,-.111),l(-.125,-.128),l(-.094,-1.226),l(.106,-.271),l(.172,-.172),l(.596,-.317),l(-.012,-.427),l(-.231,-.867),l(-.123,-1.835),l(.1,-.37),l(.316,-.541),l(.511,-2.427),l(.053,-.312),l(.126,-.17),l(.951,-.529),l(.202,-.143),l(-.437,-.932),l(-.249,-1.103),l(-.014,-1.16),l(.142,-1.075),l(.231,-.312),l(1.334,-1.11),l(.078,-.325),l(-.189,-1.06),l(-.088,-.409),l(.319,-.595),l(1.083,-1.854),l(.075,-.41),l(-.055,-.69),l(-.784,-2.816),l(-.408,-1.154),l(-.285,-.633),l(.156,-.961),l(.388,-1.201),l(.292,-.34),l(.492,-.271),l(.124,-.185),l(-.054,-.762),l(-.167,-1.466),l(.823,-1.839),l(.282,-.439),l(.608,-.694),l(.464,-.835),l(.329,-.835),l(.791,-.089),l(.333,-.017),l(.152,-.311),l(-.011,-.227),l(-.53,-.477),l(-.114,-.507),l(.161,-.382),l(.273,-.369),l(-.03,-.226),l(-.34,-.449),l(-.113,-.437),l(.007,-.579),l(.421,-.722),l(-.25,-.817),l(.204,-.312),l(.819,-.372),l(1.346,-.53),l(.36,-.256),l(.541,-1.457),l(.093,-.636),l(-.161,-.154),l(-.212,-.14),l(-.107,-.056),l(2.026,-2.087),l(.292,-.27),l(.322,.181),l(.193,.169),l(.615,.194),l(.901,-.034),l(.527,-.045),l(.531,.067),l(.37,.195),l(.341,.309),l(.416,1.084),l(1.034,-1.784),l(.686,-.004),l(1.33,.077),l(.592,.109),l(.19,.056),l(.225,.337),l(.782,.913),l(1.271,1.376),l(.576,.562),l(.835,.588),l(.498,.237),l(1.173,.204),l(.671,.307),l(1.632,1.106),l(1.448,.697),l(.65,.18),l(.939,.503),l(-.018,.268),l(-.617,1.203),l(-.625,.851),l(-.048,.227),l(.21,.619),l(-.11,.198),l(-.486,.398),l(-.425,.426),l(-.064,.212),l(.19,.056),l(1.187,-.332),l(.607,.025),l(.992,.333),l(.564,.096),l(.71,-.005),l(.745,-.131),l(.387,-.172),l(.235,-.029),l(.35,.097),l(.609,.363),l(.352,-.51),l(.301,-.27),l(1.007,-.74),l(.375,-.468),l(.544,-1.57),l(-.02,-.239),l(.957,-.161),l(.462,-.017),l(.206,.196),l(.517,1.154),l(-.094,1.638),l(-.161,.467),l(-.521,.313),l(-1.754,.744),l(-.348,.242),l(-1.633,1.448),l(-1.435,1.363),l(-1.805,1.816),l(-.833,.88),l(-.214,.27),l(-.443,.524),l(-.065,.452),l(-.595,2.359),l(-.103,.522),l(.049,.847),l(.168,.986),l(-.118,.325),l(-.48,.524),l(-.24,.495),l(-.011,.522),l(.194,.577),l(-.054,.338),l(-.162,.273),l(-.26,.325),l(.015,.226),l(.924,.831),l(.68,.281),l(.715,.281),l(.283,.169),l(.281,.325),l(-.025,.325),l(-.362,.523),l(-.043,.396),l(.105,.339),l(.174,.269),l(.466,.325),l(.522,.168),l(.109,.113),l(.226,.892),l(-.308,.481),l(-.75,.937),l(-.729,.766),l(-.313,.737),l(-.368,.284),l(-.832,.342),l(-1.04,.342),l(-1.92,.401),l(-1.795,.188),l(-1.361,.116),l(-.945,.044),l(-1.175,-.11),l(-.934,-.226),l(-.128,.199),l(.036,.808),l(.322,.312),l(.308,.184),l(-.09,.298),l(-.381,.624),l(-.345,.498),l(-.069,.385),l(.392,.682),l(.066,.285),l(-.208,.214),l(-.105,.057),l(-1.251,.473),l(-1.137,.116),l(-.814,-.069),l(-.967,-.34),l(-1.47,-.396),l(-.246,.057),l(-.23,.271),l(.041,.598),l(.429,.684),l(.037,.398),l(-.242,.643),l(.092,.385),l(.773,.54),l(.796,.084),l(.369,-.2),l(-.387,-.398),l(.872,-.188),l(.383,-.043),l(.234,1.041),l(.052,.3),l(-.144,.157),l(-.299,.101),l(-.448,.072),l(-.261,-.157),l(-.104,-.299),l(-.115,-.071),l(-1.046,.073),l(-.67,.201),l(-.212,.101),l(.151,.214),l(.591,.07),l(.47,.113),l(.452,.113),l(.06,.028),l(-.864,.388),l(-.776,.287),l(-.577,.602),l(.003,.414),l(.161,.787),l(-.081,.258),l(-.815,.817),l(.022,.215),l(.423,.371),l(-.491,.116),l(-1.194,.088),l(-.48,.087),l(-.632,.246),l(-.619,.389),l(-.56,.548),l(-.549,.821),l(-.052,.389),l(.061,.375),l(.312,.591),l(.48,.446),l(.98,.633),l(.657,.244),l(.97,.143),l(.362,.086),l(.14,.274),l(-.151,.796),l(-.128,.348),l(-.342,.464),l(-.189,.145),l(-1.08,.524),l(-1.541,.814),l(-.712,.698),l(-.179,.276),l(-.093,.45),l(.111,.523),l(-.169,.451),l(-.239,.32),l(-.97,.454),l(-.969,.25),l(-.421,.221),l(-.323,.396),l(-.226,.791),l(-.054,.514),l(.2,.777),l(.547,.896),l(.699,.779),l(.235,.339),l(-.101,.032),M(247.899,318.387),l(.18,.182),l(.148,.073),l(.607,-.075),l(.344,.133),l(.648,.725),l(.908,.665),l(.993,.756),l(.525,.222),l(.73,.37),l(.246,.074),l(.42,-.001),l(.483,.163),l(1.283,.027),l(.142,-.016),l(-.006,.224),l(-.19,.209),l(-.492,.06),l(-1.198,.092),l(-.777,.196),l(-.364,0),l(-1.179,-.355),l(-.753,-.088),l(-1.15,-.027),l(-.814,-.014),l(-.831,.081),l(.098,-3.675),
+N(346.758,54.457),l(.127,-.162),l(.42,-.179),l(.894,.015),l(.674,-.098),l(-.354,-.227),l(-.405,-.34),l(.317,-.342),l(.277,0),l(.956,.42),l(.67,.048),l(.3,-.163),l(-.191,-.26),l(-.625,-.373),l(.366,-.245),l(1.037,.226),l(.957,.08),l(.746,.275),l(.446,.551),l(-.119,.405),l(-.441,.292),l(.922,.806),l(.553,-.356),l(.352,-.13),l(.63,-.114),l(.49,-.179),l(.061,-.21),l(-.169,-.778),l(.542,-.245),l(.501,.454),l(.426,.307),l(.489,.209),l(.215,.016),l(.185,-.13),l(-.264,-.454),l(.243,-.163),l(.27,-.033),l(.817,-.164),l(.683,.438),l(.536,.242),l(.542,.063),l(.05,-.178),l(-.358,-.535),l(1.006,.145),l(1.439,.079),l(.787,-.115),l(.427,-.229),l(-.021,-.716),l(1.167,.08),l(.635,.471),l(1.118,.323),l(.683,.015),l(.273,.243),l(-.252,.552),l(.786,.29),l(1.674,.159),l(.127,.145),l(.143,.548),l(-.07,.387),l(-.152,.256),l(-.152,.256),l(-.443,.129),l(-.815,.018),l(-.195,.096),l(-.04,.431),l(-.515,.463),l(-.497,.16),l(-.568,-.031),l(-.422,-.159),l(-.817,.495),l(-.539,.144),l(-1.46,.463),l(-.853,.113),l(-.726,.001),l(-.771,.097),l(-.784,.587),l(-.473,.127),l(-1.078,.097),l(-.709,-.03),l(-1.316,-.171),l(-.608,-.142),l(-1.283,-.489),l(-1.058,-.093),l(-.443,.064),l(-1.041,-.014),l(-1.834,-.124),l(-.297,-.206),l(.434,-.191),l(1.127,-.352),l(.701,-.59),l(-.818,-.015),l(-.51,-.126),l(-.428,-.398),l(-.253,-.095),l(-.358,.081),l(-1.564,.115),l(-.557,.033),l(-.37,-.223),l(.141,-.192),l(.388,-.129),l(.669,-.097),l(.794,-.017),l(.729,-.114),l(1.049,-.098),l(.376,-.194),l(.178,-.322),l(-.144,-.258),l(-.358,-.177),l(-.426,-.015),l(-.478,-.145),l(-1.005,-.047),l(-.821,.099),l(-.424,.162),l(-.676,.082),l(-1.041,-.272),l(-.16,-.21),
+N(462.829,67.958),l(.145,.053),l(.16,.131),l(-.043,.174),l(-.175,.044),l(-.189,0),l(-.116,0),l(-.088,.043),l(-.058,.131),l(-.175,.277),l(-.219,.204),l(-.319,.131),l(-.204,.131),l(-.088,.16),l(.029,.16),l(.131,.437),l(.073,.189),l(.029,.16),l(-.175,.131),l(0,.175),l(.175,.233),l(.203,.087),l(.088,.073),l(0,.073),l(.015,.087),l(.043,.146),l(.175,.043),l(.059,.059),l(-.175,.073),l(-.262,.043),l(-.16,.058),l(-.059,.102),l(-.087,.116),l(-.131,0),L(461.402,72),l(-.015,.087),l(-.116,.058),l(.081,.106),l(-.125,.025),l(-.087,-.015),l(-.306,-.043),l(-.16,-.058),l(-.175,-.073),l(-.204,-.044),l(-.131,.102),l(-.204,-.029),l(-.131,.044),l(-.16,.087),l(-.146,.014),l(-.146,-.087),l(-.116,-.029),l(-.175,0),l(-.204,-.117),l(-.116,-.174),l(-.203,-.117),l(-.146,-.043),l(-.072,-.117),l(-.189,0),l(-.247,-.043),l(-.204,-.117),l(-.029,0),l(-.276,-.014),l(-.102,-.102),l(-.248,-.043),l(-.116,-.102),l(-.189,-.087),l(0,.073),l(-.116,.087),l(-.131,-.058),l(-.015,-.073),l(-.087,-.029),l(-.103,0),l(-.276,.117),l(-.102,.029),l(-.131,.015),l(-.219,.015),l(-.146,.043),l(-.262,.029),l(-.276,.087),l(-.116,.102),l(-.087,0),l(.156,-.19),l(-.003,-.351),l(.183,-.238),l(-.368,-.21),l(-.605,.437),l(-.334,-.251),l(-.527,-.038),l(.043,-.942),l(-.396,.188),l(-.336,-.415),l(.158,-.202),l(-.209,-.254),l(.265,-.074),l(-.092,-.252),l(.344,-.042),l(1.026,-.084),l(-.006,-.132),l(.561,-.108),l(.133,-.188),l(.436,.101),l(.074,-.113),l(.317,.05),l(.083,-.215),l(1.104,.193),l(.446,-.294),l(.091,.165),l(.514,-.089),l(1.383,.029),l(1.152,.167),l(.305,.12),l(.592,-.045),l(.971,.09),l(.426,-.108),l(.271,-.24),l(-.006,-.009),
+N(461.353,72.251),l(.37,-.004),l(-.015,.116),l(0,.117),l(.232,.073),l(.204,.087),l(.087,.073),l(.204,.058),l(-.015,.087),l(-.029,.116),l(-.015,.087),l(-.102,.073),l(-.087,.015),l(-.103,.029),l(.131,.087),l(-.072,.131),l(.029,.117),l(-.073,.087),l(-.102,.073),l(-.044,.102),l(.204,-.059),l(.146,-.015),l(.131,.073),l(.087,.014),l(.073,.044),l(-.059,.087),l(-.043,.073),l(.116,.102),l(.131,.058),l(.029,.16),l(.146,.102),l(.16,.043),l(-.059,.087),l(.131,.117),l(-.015,.189),l(.088,.233),l(-.044,.087),l(-.015,.117),l(-.061,.042),l(-.241,.044),l(-.24,.051),l(-.12,.12),l(-.223,.034),l(-.137,.154),l(-.137,.085),l(-.069,.103),l(-.068,.206),l(-.188,.035),l(-.239,-.035),l(-.24,-.034),l(-.325,-.034),l(-.359,0),l(-.172,.068),l(-.103,.137),l(-.223,.085),l(-.154,0),l(-.103,-.017),l(-.086,.052),l(-.377,-.035),l(-.273,-.068),l(-.154,-.171),l(-.138,-.154),l(-.325,-.137),l(-.497,-.223),l(-.342,-.24),l(-.377,-.034),l(-.583,-.035),l(-.325,-.12),l(-.291,-.188),l(-.086,-.257),l(-.188,.017),l(-.171,.068),l(-.36,.171),l(-.394,.017),l(-.24,0),l(-.273,.085),l(-.24,-.085),l(-.309,-.103),l(-.6,-.017),l(-.291,.069),l(-.359,-.069),l(-.291,-.051),l(-.154,.034),l(-.274,.069),l(-.103,-.052),l(-.12,-.137),l(-.154,0),l(-.257,.068),l(-.188,0),l(-.754,-.017),l(-.445,-.085),l(-.754,.171),l(-.599,.154),l(-.429,.103),l(-.257,.137),l(-.052,.188),l(-.526,.026),l(-.065,-.059),l(.073,-.837),l(.035,-.302),l(.127,-.167),l(.672,-.379),l(.034,-.717),l(.267,-.162),l(.267,-.273),l(.217,-.203),l(.296,-.026),l(1.056,-.199),l(.166,-.046),l(.162,-.066),l(.29,0),l(.049,.237),l(.657,.388),l(.422,.162),l(.23,.473),l(.091,.15),l(.441,.196),l(.785,.059),l(.868,-.244),l(.24,-.122),l(.178,-.288),l(-.052,-.394),l(-.193,-.869),l(.198,-.243),l(.045,-.055),l(.087,0),l(.116,-.102),l(.276,-.087),l(.262,-.029),l(.146,-.043),l(.219,-.015),l(.131,-.015),l(.102,-.029),l(.276,-.117),l(.103,0),l(.087,.029),l(.015,.073),l(.131,.058),l(.116,-.087),l(0,-.073),l(.189,.087),l(.116,.102),l(.248,.043),l(.102,.102),l(.276,.014),l(.029,0),l(.204,.117),l(.247,.043),l(.189,0),l(.072,.117),l(.146,.043),l(.203,.117),l(.116,.174),l(.204,.117),l(.175,0),l(.116,.029),l(.146,.087),l(.146,-.014),l(.16,-.087),l(.131,-.044),l(.204,.029),l(.131,-.102),l(.204,.044),l(.175,.073),l(.16,.058),l(.306,.043),l(.087,.015),l(.125,-.025),
+N(451.02,79.165),l(-.029,-.038),l(-.034,-.137),l(-.018,-.171),l(.068,-.206),l(.068,-.154),l(.224,-.12),l(-.052,-.12),l(-.018,-.137),l(-.171,-.069),l(-.188,-.034),l(-.103,-.103),l(-.086,-.137),l(-.223,.017),l(-.257,0),l(-.445,0),l(-.223,.051),l(-.086,-.103),l(-.514,-.068),l(-.257,-.069),l(-.223,-.12),l(-.24,0),l(-.086,-.052),l(-.051,-.154),l(-.12,.034),l(-.353,.096),l(-.043,-.077),l(.128,-.012),l(.034,-.183),l(-.439,-.646),l(-.008,-.14),l(-.042,-.727),l(-.112,-.102),l(.526,-.026),l(.052,-.188),l(.257,-.137),l(.429,-.103),l(.599,-.154),l(.754,-.171),l(.445,.085),l(.754,.017),l(.188,0),l(.257,-.068),l(.154,0),l(.12,.137),l(.103,.052),l(.274,-.069),l(.154,-.034),l(.291,.051),l(.359,.069),l(.291,-.069),l(.6,.017),l(.309,.103),l(.24,.085),l(.273,-.085),l(.24,0),l(.394,-.017),l(.36,-.171),l(.171,-.068),l(.188,-.017),l(.086,.257),l(.291,.188),l(.325,.12),l(.583,.035),l(.377,.034),l(.342,.24),l(.497,.223),l(.325,.137),l(.138,.154),l(.154,.171),l(.273,.068),l(.377,.035),l(-.017,.171),l(-.086,.154),l(-.034,.12),l(-.12,.137),l(-.086,.137),l(.343,.034),l(.274,.052),l(.085,.051),l(-.119,.051),l(-.086,0),l(-.103,.154),l(-.018,.154),l(-.171,.017),l(-.12,-.086),l(-.12,.051),l(-.239,-.034),l(-.154,.034),l(-.086,.154),l(-.103,.154),l(-.257,.068),l(-.429,0),l(-.137,.137),l(-.12,.12),l(-.034,.154),l(-.086,.171),l(.103,.171),l(-.068,.137),l(-.239,.154),l(0,.137),l(-.068,.085),l(-.069,.137),l(.172,.034),l(.205,0),l(.138,.206),l(-.086,.188),l(-.274,.017),l(-.223,-.068),l(0,-.154),l(-.034,-.085),l(-.086,-.069),l(-.171,.051),l(-.12,.086),l(-.291,-.034),l(-.068,.137),l(-.24,.12),l(-.154,0),l(-.188,-.034),l(-.273,.103),l(.086,.171),l(-.069,.12),l(-.171,.034),l(-.137,-.034),l(-.206,.051),l(-.377,.154),l(-.291,0),l(-.068,-.103),l(-.12,-.051),l(-.239,.051),l(-.377,.017),l(-.24,.034),l(-.291,-.034),l(-.154,.034),l(-.093,-.035),l(-.09,-.171),l(-.016,-.029),l(-.099,-.186),l(-.284,-.487),l(-.679,-.243),l(-.04,-.014),l(-.641,.021),
+N(452.867,80.273),l(.093,.035),l(.154,-.034),l(.291,.034),l(.24,-.034),l(.377,-.017),l(.239,-.051),l(.12,.051),l(.068,.103),l(.291,0),l(.377,-.154),l(.206,-.051),l(.137,.034),l(.171,-.034),l(.069,-.12),l(-.086,-.171),l(.273,-.103),l(.188,.034),l(.154,0),l(.24,-.12),l(.068,-.137),l(.291,.034),l(.12,-.086),l(.171,-.051),l(.086,.069),l(.034,.085),l(0,.154),l(.223,.068),l(.274,-.017),l(.086,-.188),l(-.138,-.206),l(-.205,0),l(-.172,-.034),l(.069,-.137),l(.068,-.085),l(0,-.137),l(.239,-.154),l(.068,-.137),l(-.103,-.171),l(.086,-.171),l(.034,-.154),l(.12,-.12),l(.137,-.137),l(.429,0),l(.257,-.068),l(.103,-.154),l(.086,-.154),l(.154,-.034),l(.239,.034),l(.12,-.051),l(.12,.086),l(.171,-.017),l(.018,-.154),l(.103,-.154),l(.086,0),l(.119,-.051),l(-.085,-.051),l(-.274,-.052),l(-.343,-.034),l(.086,-.137),l(.12,-.137),l(.034,-.12),l(.086,-.154),l(.017,-.171),l(.086,-.052),l(.103,.017),l(.154,0),l(.223,-.085),l(.103,-.137),l(.172,-.068),l(.359,0),l(.325,.034),l(.24,.034),l(.239,.035),l(.188,-.035),l(.068,-.206),l(.069,-.103),l(.137,-.085),l(.137,-.154),l(.223,-.034),l(.12,-.12),l(.24,-.051),l(.241,-.044),l(.165,.147),l(.229,.066),l(.197,-.131),l(.181,.016),l(.312,.033),l(.132,.148),l(.082,.148),l(.197,-.033),l(.214,-.065),l(.361,-.049),l(.312,.049),l(.296,.065),l(.147,.017),l(0,.115),l(-.164,.099),l(-.017,.099),l(.065,.148),l(.164,.148),l(.197,0),l(.214,-.197),l(.279,-.016),l(.165,0),l(.147,-.099),l(.23,-.083),l(.131,.049),l(.099,.049),l(.247,-.049),l(.542,.115),l(.132,.131),l(.279,.099),l(.099,.099),l(.147,.099),l(.165,.033),l(.147,-.033),l(.049,.115),l(-.065,.115),l(0,.083),l(-.033,.164),l(-.131,.165),l(.197,.247),l(.147,.099),l(.05,.164),l(-.066,.131),l(-.114,0),l(0,.083),l(-.115,.082),l(-.099,.049),l(-.033,.165),l(-.049,.147),l(.345,.049),l(.132,.181),l(.082,.115),l(.181,-.049),l(.132,.033),l(-.099,.115),l(-.066,.131),l(.017,.099),l(.214,.017),l(.164,.263),l(.115,.23),l(.443,.213),l(.23,.066),l(.279,.082),l(.164,.099),l(.066,.148),l(-.099,.197),l(-.066,.181),l(.182,.066),l(.361,-.066),l(.378,.066),l(.361,.099),l(.263,.066),l(-.032,.065),l(-.066,.049),l(-.082,.017),l(.099,.181),l(.296,.148),l(.279,.066),l(.033,.131),l(-.065,.164),l(-.296,0),l(-.148,.083),l(-.049,.065),l(-.444,.247),l(-.525,.099),l(-.51,-.016),l(-.197,-.132),l(-.328,-.049),l(-.362,.017),l(-.131,.165),l(-.099,.131),l(.017,.164),l(.279,.263),l(.296,.164),l(0,.165),l(-.132,.066),l(.099,.148),l(.147,.131),l(-.082,.099),l(.033,.164),l(.033,.23),l(-.033,.099),l(.164,.082),l(.082,.115),l(.165,.066),l(.002,.142),l(-.519,-.005),l(-.522,.056),l(-.112,.131),l(-.205,-.056),l(-.187,-.037),l(-.336,.075),l(-.057,.13),l(-.111,.112),l(-.317,.187),l(-.188,.261),l(-.261,.224),l(-.057,.206),l(.243,.205),l(.056,.149),l(-.131,.206),l(-.261,-.019),l(-.149,-.056),l(-.149,-.206),l(-.112,-.056),l(-.187,-.075),l(-.224,-.037),l(-.225,.037),l(-.242,.075),l(-.299,.019),l(-.149,-.131),l(-.224,.075),l(-.188,.093),l(-.316,.056),l(-.188,-.056),l(-.037,-.205),l(-.112,-.131),l(-.168,-.131),l(-.13,.038),l(-.131,.075),l(-.168,0),l(-.299,.149),l(-.131,.149),l(-.168,0),l(-.094,-.187),l(-.13,-.075),l(-.206,0),l(-.224,.112),l(-.131,-.187),l(-.224,-.093),l(-.131,.112),l(-.354,.056),l(-.262,-.112),l(-.112,0),l(-.019,.205),l(-.168,.093),l(-.093,-.056),l(.056,-.224),l(-.243,-.038),l(-.187,-.056),l(-.541,.112),l(0,-.149),l(-.187,0),l(.019,-.224),l(-.28,-.037),l(-.242,.075),l(-.523,-.112),l(-.578,-.056),l(-.075,-.056),l(-.522,0),l(-.205,-.168),l(-.262,.019),l(-.522,-.093),l(-.467,.075),l(-.485,0),l(-.354,-.056),l(-.355,.056),l(-.354,.056),l(-.485,-.038),l(-.485,.019),l(-.205,.187),l(-.037,.168),l(-.374,.168),l(-.373,.206),l(-.112,-.112),l(-.261,0),l(-.374,-.019),l(-.037,.131),l(0,.045),l(-.126,-.137),l(.315,-.752),l(-.013,-.25),l(-.218,-.146),l(-.149,-.176),l(-.421,-.146),l(-.289,-.012),l(.128,-.292),l(.291,-.328),l(.571,-.244),l(.44,-.03),l(.263,-.208),l(.023,-.236),l(-.172,-.502),l(-.615,-1.5),l(-.16,-.302),
+N(400.125,81.146),l(.633,.305),l(.208,.207),l(.208,.37),l(-.038,.193),l(-.545,.563),l(.714,.176),l(.396,-.311),l(.527,-.119),l(.602,.028),l(.807,.176),l(.467,.354),l(.235,.752),l(-.077,.221),l(-.322,.414),l(-1.068,.473),l(-.767,.561),l(-.96,.237),l(1.223,.167),l(.501,.043),l(.354,-.104),l(.39,.117),l(-.066,.516),l(-.997,.308),l(.005,.199),l(-.479,-.084),l(-1.068,.443),l(-.879,-.142),l(-.293,-.048),l(-1.119,.211),l(-.587,-.211),l(-.598,.112),l(-1.584,.141),l(.137,.295),l(-.907,-.168),l(-.264,.168),l(-.911,-.337),l(-.334,.143),l(-.913,.089),l(-.093,.569),l(-.337,.316),l(-.37,.042),l(-.272,-.252),l(-.53,-.172),l(-.135,.151),l(-.527,-.077),l(-.948,.324),l(-.701,.552),l(-.326,-.231),l(-.775,-.147),l(1.2,-.472),l(.492,-.476),l(.447,-.097),l(.468,-.388),l(.118,-.485),l(.242,.063),l(.367,-.211),l(-.008,-.274),l(1.013,-.105),l(.76,.119),l(.927,.007),l(.073,-.338),l(.308,-.142),l(.321,-.556),l(-.939,.394),l(-.725,.016),l(-1.467,-.482),l(-1.866,-.055),l(-.399,-.191),l(-.156,-.162),l(.417,-.325),l(.983,-.194),l(.721,-.237),l(.38,-.384),l(.066,-.827),l(-.119,-.192),l(-.713,.046),l(-.34,-.044),l(.288,-.355),l(.387,-.223),l(.802,-.253),l(1.394,-.062),l(.922,-.076),l(-.316,-.548),l(.172,-.683),l(.253,-.461),l(-.045,-.312),l(-.834,.061),l(-.484,-.296),l(-.2,-.312),l(.309,-.507),l(.617,-.433),l(-.347,-.104),l(-.596,-.088),l(-.735,.3),l(-.476,.061),l(-.753,-.222),l(-.088,.194),l(-.222,.194),l(-.672,-.103),l(-.464,-.133),l(.1,-.343),l(.348,-.36),l(.604,-.706),l(-.293,-.134),l(-.305,-.39),l(.028,-.24),l(.304,-.106),l(.3,-.196),l(-.194,-.496),l(-.306,.076),l(-.636,.453),l(-.442,.031),l(-.526,.287),l(-.14,-.421),l(.302,-.527),l(.438,-.559),l(.065,-.257),l(-.417,-.195),l(-.196,-.045),l(-.218,.302),l(-.394,.257),l(-.493,-.165),l(.299,-.575),l(.54,-.455),l(.09,-.136),l(-.225,-.575),l(.366,-.092),l(.225,-.197),l(-.672,-.515),l(.432,-.351),l(.752,.151),l(.387,-.092),l(-.645,-.759),l(.975,-.169),l(-.457,-.502),l(.416,-.382),l(.924,.365),l(.824,-.093),l(.686,-.139),l(.867,-.047),l(.612,.014),l(.303,.259),l(-.307,.29),l(-1.726,.704),l(-.46,.274),l(-.218,.441),l(.222,.182),l(.784,.029),l(.877,-.078),l(.685,-.001),l(.53,.075),l(1.563,-.064),l(.458,.378),l(-.363,.425),l(-.212,.323),l(.098,.112),l(-.565,.66),l(-.226,.111),l(-.339,.437),l(-.696,.261),l(-.382,.038),l(.451,.186),l(.508,.167),l(-.116,.015),l(-.272,.19),l(-.61,.052),l(-.275,.196),l(-1.337,-.025),l(.404,.223),l(.302,0),l(.492,.093),l(.432,-.006),l(.519,-.223),l(.413,-.025),l(.449,.161),l(.656,.164),l(.673,.566),l(.496,.228),l(.118,.165),l(-.067,.238),l(.312,.78),l(.371,.536),l(.438,.189),l(.714,.107),l(.59,.549),l(.688,.593),l(.135,.52),l(-.188,.49),l(.257,.124),M(387.915,77.13),l(-.128,-.325),l(.149,-.335),l(.38,-.089),l(.079,.501),l(-.307,.251),l(-.173,-.003),M(386.786,80.184),l(-.178,-.272),l(-.967,.072),l(.123,-.256),l(-.364,-.15),l(-.26,-.257),l(-.335,-.107),l(-.253,.364),l(-.751,.257),l(-.778,-.192),l(-.401,-.278),l(-.101,-.278),l(.86,-.278),l(-.483,-.257),l(.817,-.107),l(.385,-.484),l(-.029,-.235),l(.449,-.09),l(.508,-.15),l(.781,-.077),l(.424,.044),l(.389,.104),l(.362,-.046),l(.218,.149),l(.519,.791),l(.047,.179),l(-.081,.298),l(.308,.446),l(-.155,.328),l(-.402,.328),l(-.354,.12),l(-.299,.038),
+N(578.943,106.217),l(-.41,-.375),l(-.466,-.098),l(-.663,0),l(-.196,-.27),l(-.27,-.147),l(-.147,-.344),l(-.564,.049),l(-.981,-.246),l(-.662,.074),l(-1.35,-.024),l(-.662,-.098),l(-.712,-.221),l(-.785,.147),l(-.761,0),l(-.858,.024),l(-.441,.27),l(-.54,-.098),l(-.908,-.196),l(-.735,-.246),l(-.761,-.27),l(-.589,-.074),l(-.688,.123),l(-.466,.368),l(-.245,.736),l(.024,.442),l(-.344,-.123),l(-.81,-.123),l(-.688,-.196),l(-.883,-.245),l(-.883,-.147),l(-.663,.098),l(-.736,.123),l(-.318,.368),l(-.393,.442),l(.044,.273),l(-.322,.031),l(-.377,.377),l(-.283,-.126),l(-.22,.063),l(-.346,.283),l(-.534,.471),l(-.755,.189),l(-.943,.377),l(-.282,.188),l(-.221,.472),l(-.439,.188),l(-.504,.44),l(.157,.409),l(-.125,.188),l(-.66,0),l(-.44,-.346),l(.062,-.283),l(-.062,-.283),l(-.44,-.314),l(-.346,0),l(-1.006,.094),l(-.691,.032),l(-.503,-.063),l(-.346,-1.069),l(-.221,-.817),l(-1.006,0),l(-.031,-.754),l(.188,-.409),l(.031,-1.038),l(-.66,.314),l(-.66,-1.006),l(-.597,-.22),l(-.724,-.723),l(-1.1,.409),l(-2.767,-.188),l(-2.578,.346),l(-2.012,-1.666),l(-5.722,-2.986),l(-5.658,1.289),l(-.056,8.174),l(-.158,-.014),l(-.341,.106),l(-.489,.043),l(-.447,-.255),l(-.638,-.703),l(-.256,-.511),l(-.617,-.383),l(-.681,-.383),l(-.512,-.234),l(-.979,.085),l(-1.277,.298),l(-.937,.532),l(-.529,.453),l(.092,-.399),l(-.06,-.18),l(-.12,-.12),l(.14,-.26),l(.2,-.2),l(.14,-.32),l(.04,-.3),l(.18,-.2),l(-.159,-.24),l(-.4,-.16),l(-.459,.06),l(-.18,-.16),l(-.3,.06),l(-.2,.04),l(-.199,-.18),l(-.221,-.32),l(-.319,-.28),l(-.34,0),l(-.359,.02),l(0,-.2),l(.08,-.28),l(-.2,-.379),l(-.239,-.12),l(-.2,-.24),l(-.399,-.799),l(-.08,-.28),l(-.56,-.12),l(-.699,-.08),l(-.14,-.16),l(.02,-.439),l(.16,-.12),l(.3,-.06),l(.399,.02),l(.34,.02),l(.479,.14),l(.539,.18),l(.18,-.08),l(.36,-.08),l(-.2,-.16),l(-.26,-.12),l(-.399,-.2),l(-.2,-.24),l(.26,-.36),l(.28,-.04),l(.08,-.26),l(.18,-.299),l(.12,-.14),l(.26,.04),l(.319,-.08),l(.16,-.1),l(.339,.12),l(.24,0),l(1.119,-.04),l(.999,.14),l(.499,.02),l(-.159,-.08),l(-.34,-.2),l(-.479,-.12),l(-.021,-.3),l(.2,-.2),l(.279,-.22),l(.221,-.28),l(.119,-.52),l(.12,-.28),l(-.16,-.24),l(-.14,-.16),l(.1,-.2),l(.26,-.2),l(-.119,-.12),l(-.101,-.3),l(-.359,-.12),l(-.359,-.04),l(-.68,-.1),l(-.2,.16),l(-.199,.08),l(-.52,.08),l(-.46,-.12),l(-.319,-.26),l(-.26,-.06),l(-.68,-.12),l(-.56,.06),l(-.659,.319),l(-.42,.02),l(-.799,.5),l(-.72,.28),l(-.499,.06),l(-.42,-.02),l(-.279,.24),l(-.213,.18),l(-.616,-.19),l(-.857,-.377),l(-.068,-.308),l(.343,-.103),l(.309,.103),l(.445,.103),l(.138,-.103),l(-.96,-1.131),l(-.343,-.514),l(-.479,-.206),l(-.515,-.445),l(-.514,-.034),l(-.343,.034),l(-.583,-.206),l(-.103,.343),l(-.514,-.514),l(.068,-.309),l(-.138,-.377),l(-1.37,-.343),l(.65,-1.165),l(.446,-.274),l(.239,-.206),l(-.239,-.274),l(-.343,-.171),l(.205,-1.303),l(.823,-.137),l(.343,-.549),l(.103,-.308),l(.411,-.069),l(.514,.24),l(.48,.548),l(.514,.411),l(.651,0),l(.411,-.24),l(.068,-.446),l(-.171,-.411),l(-.068,-.445),l(.479,-.206),l(.891,-.411),l(.172,-.24),l(.309,-.309),l(.514,-.171),l(.549,-.068),l(.788,-.377),l(.548,-.343),l(.515,-.309),l(.651,.069),l(.479,0),l(.309,.274),l(.651,-.137),l(.273,-.137),l(.617,-.24),l(.411,.069),l(.411,.514),l(.788,.035),l(.617,-.069),l(.96,.171),l(0,.343),l(.582,.206),l(.789,.343),l(.411,.274),l(.068,.583),l(.274,.137),l(.239,-.274),l(-.205,-.48),l(-.034,-.24),l(.72,.068),l(.582,.548),l(.686,.137),l(.411,.24),l(.686,-.171),l(.274,-.274),l(.377,-.343),l(.514,-.377),l(.823,.068),l(.65,.035),l(.651,.411),l(.617,-.068),l(.137,-.412),l(1.062,-.103),l(.754,.103),l(.274,.548),l(.926,.309),l(.754,.137),l(.411,.171),l(.651,-.343),l(.171,-.309),l(.24,0),l(.343,.343),l(.959,.034),l(1.577,-.411),l(.137,-.309),l(.138,-.686),l(-.24,-.24),l(-1.165,-.171),l(-.274,-.308),l(-.651,-.069),l(-.377,-.137),l(.068,-.171),l(-.377,-.137),l(-.239,0),l(-.164,-.274),l(.467,-.067),l(.735,-.368),l(.588,-.147),l(.331,-.294),l(-.441,-.478),l(-.146,-.257),l(.662,-.515),l(.698,-.184),l(1.103,.147),l(.515,-.073),l(.11,-.257),l(-.956,-.294),l(-1.065,-.11),l(0,-.331),l(.294,-.074),l(-.294,-.221),l(-.074,-.441),l(.185,-.515),l(.33,-.074),l(1.066,.147),l(.515,0),l(.772,0),l(.368,-.184),l(1.396,-.405),l(1.029,-.037),l(.735,-.11),l(1.545,-.11),l(.588,-.073),l(.331,.073),l(.221,-.331),l(.625,-.331),l(1.177,-.037),l(2.021,-.405),l(1.876,-.073),l(.625,-.074),l(.367,-.368),V(0,77.39),l(.515,-.037),l(.589,-.184),l(.11,-.221),l(.735,-.037),l(.919,.147),l(.515,.11),l(.772,.257),l(.625,-.11),l(.882,-.037),l(.368,.404),l(-.037,.331),l(.147,.221),l(.515,.22),l(-.11,.331),l(-.147,.257),l(.073,.331),l(-.33,.037),l(.184,.257),l(.478,.074),l(.295,-.147),l(.44,.11),l(.368,-.147),l(.367,.074),l(.331,-.221),l(.294,.11),l(.295,.368),l(.367,.221),l(.147,-.147),l(.184,-.147),l(.478,.037),l(.405,.294),l(.478,.11),l(.441,-.221),l(.367,0),l(-.146,.294),l(-.441,.184),l(-.331,.441),l(.331,.184),l(.441,-.11),l(.771,-.073),l(.441,.037),l(.552,.184),l(.294,-.294),l(.772,-.441),l(1.103,-.257),l(.956,-.515),l(.772,-.221),l(.515,-.22),l(.809,-.074),l(0,.441),l(-.515,.11),l(-.11,.368),l(1.104,.588),l(.809,.294),l(1.287,.772),l(1.066,1.029),l(1.69,2.133),l(.846,.882),l(1.104,1.434),l(.515,-.257),l(.331,-.257),l(.367,-.515),l(.92,0),l(.367,.331),l(0,.368),l(.478,0),l(.258,.257),l(.184,.184),l(.589,0),l(.992,0),l(.993,-.221),l(.771,-.221),l(.993,-.037),l(.698,.441),l(.772,.588),l(.331,.625),l(.956,.147),l(.588,.552),l(.662,.699),l(.882,.073),l(.993,.074),l(.478,-.368),l(.625,-.184),l(-.073,.331),l(.441,.331),l(.294,.478),l(.589,0),l(.064,.145),l(-.551,.034),l(-.542,.148),l(-.279,.262),l(-.011,.275),l(-.035,.478),l(-.306,.219),l(-.289,.06),l(-1.199,.093),l(-.428,.277),l(-.34,.581),l(.097,.75),l(.213,.707),l(-.157,.39),l(-.444,.392),l(-.417,.103),l(-.718,.062),l(-1.402,-.079),l(-.594,-.141),l(-.721,-.141),l(-1.096,-.254),l(-.427,.507),l(-.516,1.141),L(584.2,97.43),l(-.286,.605),l(-.137,.418),l(.622,.514),l(.126,.286),l(-.156,.245),l(-.231,.145),l(-.394,.074),l(-1.133,-.238),l(-.5,-.184),l(-.35,.06),l(-1.082,.207),l(-1.799,.254),l(-.393,.188),l(-.215,.302),l(-.068,.215),l(.232,.185),l(.366,-.06),l(.483,.141),l(.03,1.357),l(.345,.627),l(.29,.441),l(.119,.47),l(-.222,.33),l(-.705,.546),l(-.32,.401),l(-.02,.399),l(.139,.86),
+N(386.786,80.184),l(-.304,.038),l(-.223,.09),l(.241,.252),l(.361,.771),l(.287,1.213),l(-.061,.281),l(-.359,.341),l(-.242,.414),l(-.145,.473),l(-.185,.044),l(-.284,-.058),l(-.616,.031),l(-.15,.212),l(-.913,.042),l(-.84,.132),l(-.247,.144),l(-.661,.286),l(-.903,.498),l(-.628,.035),l(-.879,.283),l(-1.28,.084),l(.053,-.378),l(-.089,-.441),l(-.848,.1),l(-.171,-.487),l(.734,-.254),l(-1.186,-.021),l(.062,-.233),l(1.286,.027),l(.198,-.104),l(.039,-.222),l(.107,-.31),l(.515,-.134),l(.692,-.031),l(.13,-.281),l(-1.07,.099),l(.387,-.437),l(-.187,-.159),l(.481,-.468),l(.694,-.011),l(.163,-.089),l(-.174,-.311),l(-.348,.177),l(-.309,-.131),l(-.319,.03),l(-.391,-.177),l(-.414,.001),l(-.182,.106),L(378,81.478),l(.309,-.306),l(-.29,-.142),l(.759,-.126),l(-.139,-.301),l(.391,-.235),l(-.481,-.214),l(-.59,.128),L(378,79.792),l(.38,-.268),l(.215,-.16),l(.928,.187),l(.336,-.075),l(.527,.038),l(1.102,.123),l(-.214,-.358),L(382.132,79),l(.198,-.321),l(-1.373,0),l(.154,-.15),l(.569,-.107),l(.061,-.29),l(.291,-.479),l(.505,-.181),l(.804,-.169),l(.22,.302),l(.354,.149),l(.156,-.031),l(.029,.235),l(-.385,.484),l(-.817,.107),l(.483,.257),l(-.86,.278),l(.101,.278),l(.401,.278),l(.778,.192),l(.751,-.257),l(.253,-.364),l(.335,.107),l(.26,.257),l(.364,.15),l(-.123,.256),l(.967,-.072),l(.178,.272),
+N(452.998,85.535),V(0,85.49),l(.037,-.131),l(.374,.019),l(.261,0),l(.112,.112),l(.373,-.206),l(.374,-.168),l(.037,-.168),l(.205,-.187),l(.485,-.019),l(.485,.038),l(.354,-.056),l(.355,-.056),l(.354,.056),l(.485,0),l(.467,-.075),l(.522,.093),l(.262,-.019),l(.205,.168),l(.522,0),l(.075,.056),l(.578,.056),l(.523,.112),l(.242,-.075),l(.28,.037),l(-.019,.224),l(.187,0),l(0,.149),l(.541,-.112),l(.187,.056),l(.243,.038),l(-.056,.224),l(.093,.056),l(.168,-.093),l(.019,-.205),l(.112,0),l(.262,.112),l(.354,-.056),l(.131,-.112),l(.224,.093),l(.131,.187),l(.224,-.112),l(.206,0),l(.13,.075),l(.094,.187),l(.168,0),l(.131,-.149),l(.299,-.149),l(.168,0),l(.131,-.075),l(.13,-.038),l(.168,.131),l(.112,.131),l(.037,.205),l(.188,.056),l(.316,-.056),l(.188,-.093),l(.224,-.075),l(.149,.131),l(.299,-.019),l(.242,-.075),l(.225,-.037),l(.224,.037),l(.187,.075),l(.112,.056),l(.149,.206),l(.149,.056),l(.261,.019),l(.131,-.206),l(-.056,-.149),l(-.243,-.205),l(.057,-.206),l(.261,-.224),l(.188,-.261),l(.317,-.187),l(.111,-.112),l(.057,-.13),l(.336,-.075),l(.187,.037),l(.205,.056),l(.112,-.131),l(.522,-.056),l(.519,.005),l(.357,.089),l(.469,.022),l(.313,-.156),l(.179,-.291),l(.134,-.268),l(.536,.246),l(.536,-.022),l(.67,-.223),l(.692,.112),l(.514,-.134),l(.201,.268),l(.312,.134),l(.246,.335),l(.134,.201),l(.246,.156),l(.312,.156),l(0,.268),l(-.312,-.022),l(-.312,.134),l(.134,.291),l(.111,.357),l(.269,.29),l(.647,0),l(.156,.112),l(.514,-.067),l(.38,.022),l(0,.312),l(.402,0),l(0,.357),l(.224,.268),l(.089,.246),l(-.089,.179),l(.089,.224),l(.179,.089),l(.291,.29),l(.268,-.179),l(.47,-.067),l(.268,.067),l(.469,.291),l(.201,-.067),l(.179,.022),l(.179,.156),l(.425,-.112),l(.312,-.112),l(.269,0),l(.536,-.134),l(.357,-.067),l(.111,.156),l(.268,.179),l(0,.134),l(.201,.179),l(.022,.134),l(.402,.044),l(.179,.179),l(.224,.112),l(.29,-.134),l(.045,-.157),l(.224,-.067),l(.29,.268),l(.425,.067),l(.469,.112),l(.268,.112),l(.357,-.067),l(.201,.179),l(.291,.089),l(.469,.022),l(.111,.224),l(.357,.156),l(.269,0),l(.134,-.044),l(.201,-.089),l(.156,.089),l(-.089,.111),l(-.022,.179),l(.111,.089),l(.09,.179),l(-.045,.224),l(-.201,.089),l(-.156,.067),l(-.357,.201),l(-.312,.044),l(.223,.246),l(.269,.089),l(.29,.044),l(-.134,.156),l(-.312,0),l(-.246,0),l(-.045,.179),l(-.044,.224),l(.156,.067),l(.179,.067),l(.044,.134),l(.045,.179),l(.09,.201),l(.066,.067),l(-.156,.491),l(-.156,.291),l(0,.156),l(-.335,.134),l(-.805,-.157),l(-.736,.045),l(-.269,0),l(-.022,.179),l(-.223,.179),l(-.38,.134),l(-.357,.022),l(-.224,.089),l(-.09,.514),l(0,.224),l(-.021,.112),l(-.012,.126),l(-.779,.104),l(-.971,.06),l(-.511,.405),l(-.729,.189),l(-1.135,.075),l(-1.119,.248),l(-.502,.318),l(-.463,.059),l(-.453,-.316),l(-.369,.621),l(-.31,.188),l(-.477,.044),l(-.438,-.057),l(-.959,.031),l(-.5,.16),l(.641,.287),l(1.957,1.004),l(.053,.172),l(-.093,.188),l(.163,.244),l(.562,.042),l(.511,-.13),l(.675,-.146),l(1.052,.013),l(.439,.114),l(-.235,.259),l(-.106,.245),l(-.228,.144),l(-.578,.116),l(-.31,.029),l(-.591,-.157),l(-.473,.044),l(-.71,.489),l(-1.007,.045),l(-.538,.188),l(-.527,.488),l(-.269,.101),l(-.786,-.07),l(-.588,-.171),l(.364,-.746),l(-.096,-.416),l(-.264,-.287),l(-.854,-.286),l(-.193,-.014),l(-.629,.016),l(-.151,.043),l(-.16,-.187),l(.887,-.505),l(.644,-.261),l(.772,-.188),l(.221,-.116),l(-.246,-.46),l(-.435,-.071),l(-.799,.044),l(-1.015,.045),l(-.698,-.1),l(-.195,-.101),l(-.418,-.432),l(.584,-.405),l(-.528,-.605),l(-.378,.361),l(-.541,.001),l(-1.001,.146),l(-.565,.131),l(-.694,.722),l(-1.003,.867),l(-.754,.203),l(-.223,.044),l(-.287,.504),l(.079,.158),l(.178,.093),l(-.706,-.131),l(-.665,.261),l(-.457,0),l(-.033,.189),l(-.609,-.047),l(-.398,-.166),l(-.119,-.249),l(-.15,.02),l(.055,-.077),l(.102,-.025),l(.126,.013),l(.113,.013),l(.189,0),l(.088,-.114),l(0,-.088),l(-.063,-.113),l(.025,-.113),l(.126,-.063),l(.051,-.063),l(.075,-.013),l(.089,-.025),l(.088,-.063),l(.089,-.088),l(.024,-.126),l(-.013,-.114),l(.14,-.013),l(.29,-.063),l(.075,-.076),l(-.025,-.088),l(-.062,-.088),l(.126,-.114),l(.037,-.063),l(-.012,-.088),l(-.114,-.113),l(.051,-.101),l(-.088,-.151),l(-.063,-.101),l(.202,-.151),l(.239,-.025),l(.126,-.088),l(.113,.025),l(.013,.088),l(-.013,.214),l(.063,.013),l(.113,0),V(0,96.92),l(-.013,-.063),l(.101,.038),l(.063,.051),l(.025,-.076),l(.075,-.038),l(.139,-.012),l(0,.075),l(.089,.063),l(.075,0),l(.126,.164),l(.076,-.076),l(.075,-.076),l(.013,-.05),l(.101,-.025),l(.177,0),l(-.037,.189),l(.176,.025),l(.038,-.038),l(.038,-.038),l(.139,.013),l(.227,0),l(.038,-.025),l(.075,-.076),l(-.126,-.013),l(-.164,-.126),l(-.101,-.051),l(-.075,-.05),l(.013,-.038),l(.101,-.063),l(-.025,-.113),l(.038,-.101),l(-.013,-.126),l(-.051,-.139),l(-.101,-.063),l(-.177,-.076),l(-.075,0),l(-.151,-.126),l(-.151,-.063),l(-.151,-.038),l(.051,-.151),l(.037,-.088),l(-.037,-.051),l(-.127,.038),l(-.062,-.114),l(.113,-.038),l(-.013,-.189),l(.089,-.075),l(-.025,-.101),l(-.038,-.088),l(-.113,0),l(-.102,.05),l(-.088,.051),l(-.113,-.088),l(-.089,-.101),l(-.188,-.101),l(-.139,-.025),l(-.102,-.139),l(-.05,-.139),l(.177,-.139),l(0,-.189),l(.024,-.114),l(.051,-.05),l(-.126,-.063),l(.164,-.151),l(-.113,-.025),l(-.076,-.063),l(-.062,-.126),l(-.14,-.013),l(-.062,.101),l(-.126,-.025),l(-.215,-.025),l(-.126,-.189),l(-.05,-.189),l(-.417,-.075),l(-.277,.012),l(-.062,.051),l(-.076,.101),l(-.062,-.05),l(0,-.076),l(-.089,-.025),l(-.101,.038),l(.038,-.05),l(.088,-.101),l(-.025,-.063),l(-.113,0),l(-.177,.038),l(-.126,-.025),l(-.101,.013),l(-.076,-.076),l(-.05,-.063),l(-.101,-.063),l(-.151,-.013),l(-.139,-.05),l(-.14,-.126),l(-.214,-.088),l(-.038,-.013),l(-.126,.025),l(-.05,.025),l(-.114,-.051),l(-.088,-.025),l(-.139,.025),l(-.177,.051),l(-.177,-.025),l(-.062,.038),l(-.126,.114),l(-.202,0),l(-.265,-.038),l(-.126,.051),l(-.315,-.114),l(-.088,.101),l(.012,.113),l(-.126,0),l(-.075,-.063),l(-.126,.114),l(-.06,.052),l(-.634,.08),l(-.151,.311),l(-.278,.178),l(-1.992,.191),l(-.186,.215),l(-.243,.119),l(-.339,.06),l(-.188,-.227),l(-.327,.004),l(-.025,-.231),l(-.363,.045),l(-1.115,-.066),l(-.958,-.193),l(-.241,.107),l(-.787,-.121),l(-.136,.085),l(-.678,-.387),l(-.554,-.2),l(-.668,-.301),l(-.166,.015),l(1.047,-1.471),l(.653,.018),l(-.349,-.383),l(-.044,-.552),l(.082,-.306),l(1.509,-1.218),l(.599,-.398),l(.286,-.181),l(.429,-.013),l(.255,-.24),l(.009,-.314),l(-.328,-.302),l(.085,-.133),l(.298,-.048),l(-.316,-.193),l(-.816,-.835),l(.074,-.242),l(-.161,-.175),
+N(660.044,89.132),l(-.295,.31),l(-.687,1.207),l(-1.224,1.959),l(-.381,.58),l(.269,.836),l(.051,.029),l(.342,-.045),l(.929,-.395),l(.754,-.062),l(.576,-.018),l(.317,.085),l(.431,.416),l(.292,.07),l(1.191,-.786),l(.438,-.002),l(.928,.212),l(.538,.199),l(.797,.5),l(.879,.99),l(.599,.501),l(.048,.273),l(-.107,.217),l(-.414,.218),l(-.464,-.127),l(-1.074,-.008),l(-.432,-.099),l(-.854,.033),l(-.937,.221),l(-.539,.146),l(-.831,.278),l(-.353,.189),l(-.483,-.127),l(-.464,.045),l(-.47,.204),l(-.363,.333),l(-.312,.82),l(-.241,.216),l(-.347,.188),l(-.638,.248),l(-.896,.134),l(-.624,-.054),l(-.438,-.012),l(-.224,-.013),l(-1.192,.91),l(-.742,.433),l(-.744,.047),l(-.982,.005),l(-.592,-.125),l(-.668,-.382),l(-.718,-.154),l(-.316,.073),l(-.457,.231),l(-.539,.589),l(-.214,.401),l(.003,.343),l(.389,.569),l(.599,.411),l(.188,.228),l(-.123,.271),l(-.326,.259),l(-1.265,.292),l(-.67,.389),l(-1.111,1.046),l(-.265,.172),l(-1.941,.737),l(-.651,.061),l(-.987,-.08),l(-1.514,.065),l(-1.339,.007),l(-1.204,.349),l(-.816,.289),l(-.736,.274),l(-.303,.101),l(-1.44,.534),l(-.686,.289),l(-.481,.017),l(-.433,-.197),l(-.253,-.297),l(-.61,-.067),l(-.663,.061),l(-.929,-.123),l(-1.599,-.433),l(-1.006,-.365),l(-.815,-.551),l(-.521,-.168),l(-1.69,-.119),l(-1.164,-.022),l(-.937,-.023),l(-2.861,.059),l(-1.165,-.022),l(-.802,-.109),l(-1.241,-.207),l(-1.979,-.018),l(-.444,-.254),l(-.467,-.439),l(-1.571,-2.161),l(-.105,-.542),l(-.744,-.096),l(-.839,-.31),l(-1.645,-.806),l(-.632,-.268),l(-.998,-.224),l(-.668,-.083),l(-.995,-.038),l(-1.505,-.021),l(-1.062,-.181),l(-.724,-.312),l(-.233,-.229),l(-.105,-.43),l(.035,-.129),l(.369,-.347),l(.214,-.389),l(.237,-.75),l(.215,-.447),l(-.401,-.66),l(-1.07,-1.451),l(-.568,-.618),l(-.354,-.143),l(-.633,-.144),l(-.731,-.167),l(-.614,-.069),l(-.834,-.415),l(-1.301,-.745),l(-.371,-.433),l(-.24,-.563),l(-.131,-.405),l(-.062,-.145),l(.154,-.044),l(.799,-.425),l(.599,-.207),l(1.387,-.08),l(.603,-.148),l(.727,-.381),l(.017,-.012),l(.971,-.692),l(.787,-.398),l(1.143,-.341),l(1.512,-.476),l(.84,-.18),l(.953,.097),l(.932,.156),l(1.842,.122),l(.831,.083),l(.694,.755),l(.393,.406),l(.699,.113),l(1.458,-.008),l(.719,.083),l(.85,-.004),l(.875,.068),l(.312,.114),l(.576,.186),l(.562,-.018),l(.755,-.28),l(.31,-.162),l(.744,-.572),l(.163,-.526),l(-.116,-.204),l(-.396,-.304),l(-.409,-.86),l(.098,-.293),l(.905,-.839),l(1.269,-.96),l(.84,.201),l(1.028,.098),l(1.036,.185),l(1.748,.328),l(.702,.231),l(.989,.317),l(.767,.143),l(.145,.204),l(.004,.541),l(.182,.481),l(.408,.451),l(.421,.333),l(1.643,.531),l(.673,.113),l(2.48,-.538),l(.796,-.077),l(1.172,.037),l(1.423,.022),l(.769,.229),l(1.333,.75),l(.623,.331),l(1.132,.313),l(.812,.373),l(1.318,.254),l(.905,.241),l(.984,.082),l(.739,.039),l(1.602,-.11),l(1.018,-.063),l(.532,-.075),l(.867,-.106),l(1.147,-.136),l(.526,-.163),l(.604,-.264),l(.447,-.394),l(.755,-.498),l(1.165,-.487),l(.333,-.002),l(.609,-.047),l(.74,.156),l(.751,.506),l(.34,.129),l(.86,.169),l(1.228,-.297),l(.622,-.018),l(.431,.168),
+N(406.183,86.551),l(1.051,-.494),l(.485,-.089),l(.574,.087),l(.465,-.016),l(.209,-.147),l(.477,.098),l(.407,.042),l(.52,-.034),l(-.025,-.157),l(.307,.012),l(.307,0),l(.267,-.182),l(.313,.242),l(.173,-.121),l(.228,.061),l(.292,.375),l(.535,-.109),l(.754,.375),l(-.11,.423),l(-.172,.097),l(.001,.338),l(.672,-.024),l(.344,.177),l(.282,.365),l(.038,.468),l(-.422,.376),l(-.225,-.072),l(-.142,.08),l(-.245,.147),l(-.213,.322),l(.017,.327),l(.31,.204),l(-.136,.348),l(-.079,-.114),l(-.694,.174),l(-.127,-.228),l(-.371,-.204),l(-.341,-.192),l(-.529,-.048),l(.039,-.228),l(-.146,-.18),l(.119,-.373),l(-.245,.072),l(-.193,.313),l(-.446,.035),l(-.406,.075),l(-.285,-.122),l(.072,-.198),l(-.091,-.175),l(.159,-.241),l(-.375,-.168),l(-.576,-.048),l(-.259,.012),l(-.159,-.301),l(-.518,.012),l(-.194,-.133),l(-.202,-.458),l(-.153,-.17),l(-.41,.208),l(-.141,.071),l(-.266,-.127),l(-.311,-.335),l(-.208,-.447),
+N(438.22,91.952),l(.039,-.044),l(.065,-.105),l(.014,-.131),l(.092,-.066),l(.146,-.119),l(.026,-.04),l(.171,-.053),l(.093,-.026),l(.092,.053),l(.132,.053),l(.158,0),l(.065,-.026),l(.093,0),l(.065,.026),l(.065,.026),l(.093,-.026),l(.145,-.04),l(.132,0),l(.118,-.053),l(.079,-.053),l(.066,-.026),l(.105,-.026),l(.039,0),l(.053,-.079),l(.04,-.092),l(.079,-.079),l(.092,.026),l(.105,-.04),l(.145,-.066),l(.053,-.105),l(.053,-.079),l(.026,-.132),l(.026,-.092),l(.053,-.092),l(.118,-.013),l(.105,-.013),l(.132,-.079),l(.119,-.053),l(.118,-.092),l(.053,-.079),l(.132,-.066),l(.065,-.04),L(442,89.998),l(.145,.013),l(.105,.026),l(.066,-.04),l(.065,-.066),l(.071,.012),l(.285,.041),l(.03,.228),l(.43,-.048),l(.183,-.24),l(.193,.016),l(.062,-.112),l(.261,-.024),l(.194,.24),l(.073,.169),l(.331,-.025),l(.066,.18),l(-.026,.083),l(.003,.204),l(.389,-.083),l(.18,.12),l(.149,-.135),l(.104,-.177),l(.558,-.204),l(.168,.056),l(.483,-.046),l(.46,.254),l(.373,-.18),l(.073,-.137),l(.508,.041),l(.561,-.076),l(.129,.13),l(.703,.186),l(.104,.216),l(.424,.101),l(.831,.33),l(-1.047,1.471),l(-.629,.076),l(-.437,-.143),l(-.534,-.359),l(-1.062,.035),l(-.717,.047),l(-1.024,.759),L(444.857,93),l(-.59,-.072),l(-.499,.061),l(-.761,.134),l(-.255,.001),l(-.334,.568),l(-1.651,-.036),l(-.414,-.027),l(-.617,-.17),l(-.399,-.172),l(-.245,.146),l(-.761,-.547),l(-.155,-.26),l(.097,-.581),l(-.053,-.093),
+N(442.391,98.111),l(-.589,.203),l(-.433,.031),l(-.668,.047),l(-.58,-.098),l(-1.116,-.671),l(-1.412,-.612),l(-.215,-.197),l(-.364,-.333),l(-.304,-.59),l(.346,-.299),l(.154,-.294),l(-.204,-.188),l(.04,-.375),l(.409,-.062),l(.157,-.206),l(-.136,-.196),l(-.452,-.063),l(.223,-.197),l(.325,0),l(.164,.134),l(.701,-.054),l(.019,-.367),l(.636,-.291),l(.245,-.146),l(.399,.172),l(.617,.17),l(.414,.027),l(1.651,.036),l(.334,-.568),l(.255,-.001),l(.761,-.134),l(.499,-.061),l(.59,.072),l(.427,-.063),l(1.024,-.759),l(.717,-.047),l(1.062,-.035),l(.534,.359),l(.437,.143),l(.629,-.076),l(.166,-.015),l(.668,.301),l(.554,.2),l(.678,.387),l(-.45,.338),l(-1.125,.267),l(-.581,.408),l(-.968,1.451),l(-.63,.84),l(-.753,.567),l(-.361,.16),l(-.724,.047),l(-.264,.103),l(-.176,-.002),l(-.907,-.067),l(-.889,.077),l(-1.535,.529),
+N(459.717,92.836),l(.06,-.052),l(.126,-.114),l(.075,.063),l(.126,0),l(-.012,-.113),l(.088,-.101),l(.315,.114),l(.126,-.051),l(.265,.038),l(.202,0),l(.126,-.114),l(.062,-.038),l(.177,.025),l(.177,-.051),l(.139,-.025),l(.088,.025),l(.114,.051),l(.05,-.025),l(.126,-.025),l(.038,.013),l(.214,.088),l(.14,.126),l(.139,.05),l(.151,.013),l(.101,.063),l(.05,.063),l(.076,.076),l(.101,-.013),l(.126,.025),l(.177,-.038),l(.113,0),l(.025,.063),l(-.088,.101),l(-.038,.05),l(.101,-.038),l(.089,.025),l(0,.076),l(.062,.05),l(.076,-.101),l(.062,-.051),l(.277,-.012),l(.417,.075),l(.05,.189),l(.126,.189),l(.215,.025),l(.126,.025),l(.062,-.101),l(.14,.013),l(.062,.126),l(.076,.063),l(.113,.025),l(-.164,.151),l(.126,.063),l(-.051,.05),l(-.024,.114),l(0,.189),l(-.177,.139),l(.05,.139),l(.102,.139),l(.139,.025),l(.188,.101),l(.089,.101),l(.113,.088),l(.088,-.051),l(.102,-.05),l(.113,0),l(.038,.088),l(.025,.101),l(-.089,.075),l(.013,.189),l(-.113,.038),l(.062,.114),l(.127,-.038),l(.037,.051),l(-.037,.088),l(-.051,.151),l(.151,.038),l(.151,.063),l(.151,.126),l(.075,0),l(.177,.076),l(.101,.063),l(.051,.139),l(.013,.126),l(-.038,.101),l(.025,.113),l(-.101,.063),l(-.013,.038),l(.075,.05),l(.101,.051),l(.164,.126),l(.126,.013),l(-.075,.076),l(-.038,.025),l(-.227,0),l(-.139,-.013),l(-.038,.038),l(-.038,.038),l(-.176,-.025),l(.037,-.189),l(-.177,0),l(-.101,.025),l(-.013,.05),l(-.075,.076),l(-.076,.076),l(-.126,-.164),l(-.075,0),l(-.089,-.063),l(0,-.075),l(-.139,.012),l(-.075,.038),l(-.025,.076),l(-.063,-.051),l(-.101,-.038),l(.013,.063),l(0,.088),l(-.113,0),l(-.063,-.013),l(.013,-.214),l(-.013,-.088),l(-.113,-.025),l(-.126,.088),l(-.239,.025),l(-.202,.151),l(.063,.101),l(.088,.151),l(-.051,.101),l(.114,.113),l(.012,.088),l(-.037,.063),l(-.126,.114),l(.062,.088),l(.025,.088),l(-.075,.076),l(-.29,.063),l(-.14,.013),l(.013,.114),l(-.024,.126),l(-.089,.088),l(-.088,.063),l(-.089,.025),l(-.075,.013),l(-.051,.063),l(-.126,.063),l(-.025,.113),l(.063,.113),l(0,.088),l(-.088,.114),l(-.189,0),l(-.113,-.013),l(-.126,-.013),l(-.102,.025),l(-.055,.077),l(-.03,.004),l(-.062,-.237),l(-.218,-.106),l(.16,-.071),l(-.021,-.267),l(-.104,-.561),l(.323,-.978),l(.027,-.404),l(-.353,-.856),l(-.604,-.286),l(-1.037,-1.119),L(460.567,93),l(-.626,-.191),l(-.225,.028),
+N(445.722,97.573),l(.176,.002),l(.264,-.103),l(.724,-.047),l(.361,-.16),l(.753,-.567),l(.63,-.84),l(.968,-1.451),l(.581,-.408),l(1.125,-.267),l(.45,-.338),l(.136,-.085),l(.787,.121),l(.241,-.107),l(.958,.193),l(1.115,.066),l(.363,-.045),l(.025,.231),l(.327,-.004),l(.188,.227),l(.339,-.06),l(.243,-.119),l(.186,-.215),l(1.992,-.191),l(.278,-.178),l(.151,-.311),l(.634,-.08),l(.225,-.028),L(460.567,93),l(.767,1.17),l(1.037,1.119),l(.604,.286),l(.353,.856),l(-.027,.404),l(-.323,.978),l(.104,.561),l(.021,.267),l(-.16,.071),l(.218,.106),l(.062,.237),l(.03,-.004),l(.15,-.02),l(.119,.249),l(.398,.166),l(.609,.047),l(.033,-.189),l(.457,0),l(.665,-.261),l(.706,.131),l(.149,.079),l(.062,.259),l(-.293,.446),l(-.27,.316),l(-.436,.044),l(-.382,.043),l(-.382,.245),l(-.515,.617),l(-.252,.645),l(-.096,.787),l(-.044,.223),l(-.671,-.12),l(-1.346,-.336),l(-.514,-.226),l(-.295,-.042),l(-.671,-.369),l(-.562,-.04),l(-.618,.218),l(-1.904,.771),l(-.38,.059),l(-1.385,-.35),l(-.3,-.013),l(-.69,.261),l(-.34,.031),l(-1.151,-.395),l(-.506,-.002),l(-.771,.189),l(-.266,.023),l(-.048,-.189),l(.234,-.318),l(-.352,-.106),l(-.392,-.204),l(-.418,-.186),l(-.146,-.33),l(.32,-.201),l(.351,.012),l(-.114,-.13),l(-.625,-.248),l(-.253,.13),l(-.215,.283),l(-.147,.118),l(-.414,-.239),l(-.194,-.139),l(-.594,-.059),l(-.02,-.189),l(-.234,0),l(-.245,-.036),l(-.052,-.165),l(.178,-.094),l(.271,-.071),l(-.239,-.083),l(-.183,-.059),l(.124,-.146),l(.19,-.127),l(-.069,-.142),l(-.306,-.118),l(-.555,-.141),l(-.712,-.471),l(.058,-.088),l(-.104,-.119),l(.075,-.356),l(-.202,-.036),l(-.19,-.237),l(-.569,-.178),l(-.054,-.309),
+N(420.177,113.472),l(-.274,-.042),l(-.253,-.155),l(-.367,-.325),l(-.096,-.213),l(.202,-.738),l(.097,-.681),l(-.046,-.583),l(-.133,-.569),l(-.503,-.44),l(-.094,-.271),l(.181,-.157),l(.366,-.015),l(.801,-.001),l(.339,-.172),l(.861,-.543),l(.633,.625),l(.451,.754),l(-.014,.271),l(-.204,.285),l(-.145,.484),l(.149,.894),l(-.11,.525),l(-.377,.695),l(-.405,-.198),l(-.52,.03),l(-.143,.1),l(-.149,.27),l(-.248,.17),M(433.783,118.446),l(-.712,-.084),l(-.902,-.607),l(-.772,-.239),l(-1.904,-.817),l(-.833,-.126),l(-.232,-.127),l(-.173,-.283),l(.139,-.34),l(.328,-.34),l(.264,-.1),l(.629,.112),l(.569,-.341),l(.68,.424),l(.403,.141),l(.722,-.016),l(1.403,-.187),l(1.38,-.329),l(.148,.085),l(.043,.127),l(-.112,.127),l(-.536,.823),l(-.153,.497),l(.009,.382),l(.411,.509),l(-.179,.128),l(-.43,.567),l(-.188,.015),M(431.244,98.829),l(-.281,-.329),l(-.242,-.027),l(-.281,.196),l(-.156,-.125),l(-.47,-.071),l(-.114,.32),l(-.458,.054),l(-1.001,.364),l(.078,-.151),l(-.452,.133),l(-.063,.249),l(-.157,.044),l(-.01,.125),l(.303,.08),l(.021,.302),l(.193,.119),l(.253,.236),l(-.104,.213),l(-.449,.254),l(.016,.272),l(.143,.554),l(.783,.814),l(2.008,.889),l(.29,.357),l(.134,.558),l(.274,.557),l(.395,.585),l(.694,.57),l(.254,.274),l(.446,.195),l(.041,.21),l(.408,.167),l(1.17,.255),l(1.254,-.105),l(.388,.141),l(.024,.212),l(-.465,.247),l(-.258,.294),l(.262,.213),l(.954,.283),l(1.168,.411),l(.829,.366),l(1.589,.739),l(.058,.185),l(.719,.458),l(.31,.475),l(-.198,.435),l(-.152,.337),l(-.455,-.281),l(-.318,-.167),l(-.109,-.486),l(-.263,-.17),l(-.512,-.099),l(-.483,-.009),l(-.439,-.236),l(.086,-.217),l(-.353,-.065),l(-.301,.098),l(-.232,.262),l(-.259,.399),l(-.273,.208),l(.043,.271),l(-.197,.303),l(-.007,.298),l(.76,.342),l(.611,.271),l(-.093,.314),l(.03,.432),l(.133,.142),l(-.191,.238),l(-.659,-.024),l(-.41,.219),l(-.202,.228),l(.11,.595),l(-.536,.303),l(-.617,.866),l(-.595,.048),l(-.167,-.071),l(-.184,-.14),l(-.002,-.508),l(.364,-.141),l(.317,-.542),l(-.236,-.184),l(.361,-.249),l(.361,.074),l(.133,-.17),l(-.077,-.34),l(-.211,-.181),l(-.206,-.924),l(-.367,-.516),l(-.15,-.607),l(-.201,-.352),l(-.334,.058),l(-.187,.171),l(-.899,-.496),l(-.286,-.065),l(.208,-.291),l(-.092,-.398),l(-.461,-.34),l(-.909,.247),l(.034,-.109),l(.322,-.194),l(-.276,-.27),l(-.29,-.003),l(-.42,.19),l(-.242,-.512),l(-.198,-.207),l(-.124,-.228),l(-.663,-.241),l(-.505,-.027),l(-.654,-.127),l(-.745,-.355),l(-.548,-.441),l(-.959,-.612),l(-1.036,-.826),l(-.872,-.384),l(-.805,-.67),l(-.566,-.856),l(-.434,-1.043),l(-.347,-.443),l(-.505,-.457),l(-.483,-.243),l(-1.188,-.341),l(-.579,-.142),l(-.5,.044),l(-1.078,.647),l(-.46,.359),l(-.646,.173),l(-.303,.043),l(.146,-.469),l(-.062,-.281),l(-.849,.07),l(-.754,-.391),l(-.193,-.442),l(.315,-.371),l(.175,-.01),l(-.135,-.331),l(-.616,-.191),l(-.352,-.358),l(.437,-.186),l(.183,.111),l(.541,-.353),l(.199,-.272),l(-.43,-.192),l(-.025,-.292),l(-.532,-.344),l(.624,-.301),l(.599,.062),l(.627,-.204),l(.629,.168),l(.275,-.16),l(.349,-.432),l(-.103,-.212),l(.777,-.404),l(.016,.415),l(.534,.363),l(.311,.071),l(-.098,.182),l(.385,.312),l(.285,-.151),l(.018,-.535),l(.425,-.384),l(-.019,-.333),l(.371,-.081),l(.143,.354),l(.23,.142),l(.216,-.03),l(.071,-.122),l(.469,-.05),l(.244,.333),l(.228,-.415),l(-.244,-.131),l(.081,-.273),l(.283,-.091),l(.176,.162),l(.315,.051),l(.038,-.192),l(-.112,-.212),l(.126,-.309),l(.631,.171),l(.597,.034),l(.329,-.411),l(.366,-.096),l(.183,.083),l(.445,-.11),l(.301,.103),l(.856,-.227),l(.023,.363),l(.318,.096),l(.32,.391),l(1.311,.247),l(.894,.082),l(.478,.112),l(.116,.199),l(-.614,.303),l(.098,.151),l(.297,.002),l(.187,.185),l(-.367,.285),l(.336,.089),l(-.127,.361),l(.36,.11),l(.284,.198),l(-.056,.214),
+N(430.73,96.731),l(1.04,.065),l(.179,.107),l(.612,-.009),l(.287,.152),l(.646,-.5),l(.566,-.107),l(.85,.08),l(.298,-.196),l(.89,.116),l(-.082,-.393),l(.693,-.157),l(.304,.59),l(.364,.333),l(-.035,-.009),l(-.1,-.073),l(-.145,-.036),l(-.172,0),l(-.145,.009),l(-.055,.063),l(0,.072),l(.019,.09),l(.009,.082),l(-.063,.009),l(-.136,-.009),l(-.108,-.036),l(-.091,.063),l(-.045,.082),l(-.081,.063),l(-.082,.045),l(-.081,.009),l(-.163,.036),l(-.117,.036),l(-.108,.036),l(-.055,.045),l(-.153,-.009),l(-.127,.072),l(-.063,.054),l(-.018,.082),l(.036,.072),l(.081,.054),l(.063,.055),l(.045,.045),l(.019,.063),l(.018,.09),l(-.036,.108),l(-.018,.063),l(-.046,.1),l(-.108,0),l(-.081,-.009),l(-.091,.027),l(-.108,.009),l(-.117,.054),l(-.091,.018),l(-.081,.027),l(-.1,.045),l(-.055,.063),l(-.036,.027),l(.055,.018),l(.063,.009),l(.026,.027),l(.037,.072),l(-.046,.063),l(-.027,.009),l(-.081,.027),l(-.009,.045),l(.045,.081),l(0,.072),l(.045,.1),l(-.054,.072),l(-.063,-.018),l(-.1,.045),l(-.117,.018),l(-.127,-.036),l(-.063,-.027),l(-.1,-.063),l(-.099,0),l(-.063,-.027),l(-.118,-.045),l(-.018,.045),l(-.027,.045),l(-.1,.027),l(-.136,0),l(-.054,-.045),l(-.072,-.063),l(-.127,-.018),l(-.019,-.09),l(-.026,-.018),l(-.063,-.054),l(-.055,-.027),l(-.018,-.054),l(-.01,-.054),l(-.036,-.009),l(-.063,.018),l(-.036,.054),l(-.009,.027),l(-.054,.063),l(-.019,.018),l(-.018,.081),l(-.063,.045),l(-.046,.018),l(-.062,.054),l(-.036,.009),l(-.254,0),l(-.108,-.027),l(-.108,.027),l(-.145,.009),l(-.1,-.009),l(-.1,-.036),l(-.045,-.019),l(-.055,0),l(0,.037),l(0,.036),l(-.045,.027),l(-.045,.018),l(-.136,-.009),l(-.027,-.036),l(-.108,.018),l(-.019,.018),l(-.136,.018),l(-.063,.018),l(-.126,.018),l(-.272,-.063),l(.428,-.077),l(.113,-.16),l(.056,-.214),l(-.284,-.198),l(-.36,-.11),l(.127,-.361),l(-.336,-.089),l(.367,-.285),l(-.187,-.185),l(-.297,-.002),l(-.098,-.151),l(.614,-.303),l(-.116,-.199),
+N(439.573,104.709),l(-1.051,-.672),l(-.185,-.222),l(-.783,-.149),l(-.203,-.159),l(-.403,-.115),l(-.683,.177),l(-.326,-.486),l(-1.112,-.627),l(-.584,-.678),l(.277,.007),l(.608,.016),l(-.583,-.221),l(-.659,-.469),l(-.183,-.407),l(.086,-.452),l(-.289,-.336),l(-.646,-.418),l(-.378,-.126),l(-.258,.579),l(-.142,.116),l(.03,.15),l(-.284,.106),l(-.154,.248),l(-.213,.053),l(-.496,-.647),l(-.063,-.286),l(-.259,-.612),l(.065,-.012),l(.272,.063),l(.126,-.018),l(.063,-.018),l(.136,-.018),l(.019,-.018),l(.108,-.018),l(.027,.036),l(.136,.009),l(.045,-.018),l(.045,-.027),l(0,-.036),l(0,-.037),l(.055,0),l(.045,.019),l(.1,.036),l(.1,.009),l(.145,-.009),l(.108,-.027),l(.108,.027),l(.254,0),l(.036,-.009),l(.062,-.054),l(.046,-.018),l(.063,-.045),l(.018,-.081),l(.019,-.018),l(.054,-.063),l(.009,-.027),l(.036,-.054),l(.063,-.018),l(.036,.009),l(.01,.054),l(.018,.054),l(.055,.027),l(.063,.054),l(.026,.018),l(.019,.09),l(.127,.018),l(.072,.063),l(.054,.045),l(.136,0),l(.1,-.027),l(.027,-.045),l(.018,-.045),l(.118,.045),l(.063,.027),l(.099,0),l(.1,.063),l(.063,.027),l(.127,.036),l(.117,-.018),l(.1,-.045),l(.063,.018),l(.054,-.072),l(-.045,-.1),l(0,-.072),l(-.045,-.081),l(.009,-.045),l(.081,-.027),l(.027,-.009),l(.046,-.063),l(-.037,-.072),l(-.026,-.027),l(-.063,-.009),l(-.055,-.018),l(.036,-.027),l(.055,-.063),l(.1,-.045),l(.081,-.027),l(.091,-.018),l(.117,-.054),l(.108,-.009),l(.091,-.027),l(.081,.009),l(.108,0),l(.046,-.1),l(.018,-.063),l(.036,-.108),l(-.018,-.09),l(-.019,-.063),l(-.045,-.045),l(-.063,-.055),l(-.081,-.054),l(-.036,-.072),l(.018,-.082),l(.063,-.054),l(.127,-.072),l(.153,.009),l(.055,-.045),l(.108,-.036),l(.117,-.036),l(.163,-.036),l(.081,-.009),l(.082,-.045),l(.081,-.063),l(.045,-.082),l(.091,-.063),l(.108,.036),l(.136,.009),l(.063,-.009),l(-.009,-.082),l(-.019,-.09),l(0,-.072),l(.055,-.063),l(.145,-.009),l(.172,0),l(.145,.036),l(.1,.073),l(.035,.009),l(.215,.197),l(1.412,.612),l(1.116,.671),l(.58,.098),l(.668,-.047),l(.433,-.031),l(.589,-.203),l(.201,.142),l(.056,.089),l(.022,.112),l(-.022,.078),l(.045,.044),l(.011,.067),l(-.078,.056),l(-.011,.146),l(.078,.067),l(.145,-.034),l(.101,.034),l(.045,.089),l(-.078,.011),l(-.056,-.022),l(-.022,.078),l(.033,.1),l(-.045,.034),l(-.044,.022),l(.066,.111),l(.168,-.022),l(.033,.078),l(.123,.1),l(.122,0),l(.101,0),l(.09,.078),l(.122,.011),l(.134,0),l(.012,.078),l(-.033,.056),l(-.135,-.011),l(-.089,-.034),l(-.067,.022),l(-.078,-.011),l(-.066,-.045),l(-.056,-.011),l(-.045,.011),l(.033,.067),l(-.101,.089),l(-.078,0),l(0,.156),l(.045,.067),l(-.033,.078),l(.022,.078),l(.011,.078),l(-.089,.033),l(-.09,-.033),l(-.056,.067),l(.078,.089),l(-.078,.011),l(-.189,.022),l(-.201,-.022),l(-.145,-.123),l(.056,-.101),l(-.045,-.089),l(-.123,-.011),l(-.022,-.112),l(-.145,-.056),l(-.146,-.045),l(-.101,.089),l(-.1,-.011),l(-.156,-.078),l(-.067,-.022),l(-.146,0),l(-.156,-.045),l(-.111,.067),l(-.134,.045),l(-.134,-.045),l(-.111,-.067),l(-.112,0),l(-.122,.089),l(-.168,.078),l(-.156,-.067),l(-.268,-.089),l(-.179,.011),l(-.156,.011),l(-.189,-.056),l(-.168,-.011),l(-.156,-.089),l(-.089,.078),l(-.111,.022),l(-.057,-.056),l(-.234,-.078),l(-.156,-.056),l(-.134,-.045),l(-.089,-.011),l(-.134,.123),l(-.112,-.011),l(-.223,-.022),l(-.168,-.033),l(-.212,.022),l(-.101,.111),l(-.145,.145),l(-.123,.201),l(-.201,-.022),l(-.256,-.134),l(-.156,-.19),l(-.101,-.111),l(-.312,-.034),l(-.123,.044),l(-.089,.179),l(-.045,.167),l(.045,.134),l(0,.078),l(.033,.212),l(-.123,.067),l(.022,.089),l(.134,.078),l(.09,.089),l(.122,.034),l(.101,.033),l(.179,.179),l(.146,.234),l(.089,.134),l(.022,.123),l(.156,.111),l(-.078,.056),l(-.012,.1),l(.022,.146),l(.168,-.011),l(.089,.111),l(.056,.123),l(.112,.111),l(.167,.045),l(.167,.033),l(.369,.357),l(.021,.167),l(.078,.044),l(.213,.078),l(.379,.357),l(.224,.123),l(.223,.067),l(.101,.056),l(0,.112),l(.078,.279),l(.201,.078),l(.189,.167),l(.146,.112),l(.245,.123),l(.067,.212),l(-.284,.083),M(439.792,104.833),l(.132,-.118),l(.134,.011),l(.123,.034),l(.045,.078),l(.066,.089),l(.146,.089),l(.179,.078),l(.212,.011),l(.312,.257),l(.045,.067),l(.134,-.033),l(.123,.022),l(.089,.034),l(.062,.063),l(.005,.004),l(-.022,.089),l(.033,.078),l(.082,.072),l(.029,.092),l(-.002,.1),l(-.589,-.367),l(-.549,-.371),l(-.789,-.378),
+N(451.009,101.725),l(-.328,.346),l(-.383,.374),l(-.18,.302),l(.056,.271),l(1.326,1.122),l(.028,.2),l(-.302,.302),l(-.762,.333),l(-.246,.301),l(-.008,.514),l(-.013,.208),l(-.058,-.017),l(-.072,.029),l(-.16,.022),l(-.145,.021),l(-.116,.022),l(-.058,.015),l(-.102,-.051),l(-.087,.043),l(-.088,.021),l(-.102,-.043),l(-.064,-.021),l(-.131,.116),l(-.087,.08),l(-.152,-.015),l(-.196,-.007),l(-.064,.007),l(-.175,-.043),l(-.152,.087),l(-.151,.102),l(-.109,.058),l(.059,.072),l(-.029,.058),l(-.116,0),l(-.094,-.109),l(-.131,-.058),l(-.087,-.073),l(-.08,.065),l(-.116,.058),l(-.246,.058),l(-.225,.058),l(-.088,.058),l(-.058,.167),l(.029,.13),l(-.029,.072),l(-.072,.087),l(-.188,0),l(-.14,-.049),l(-.018,-.109),l(-.733,-.866),l(-.382,-.369),l(-.058,-.004),l(.109,-.286),l(0,-.067),l(-.078,-.067),l(-.101,0),l(-.056,-.056),l(.022,-.089),l(.111,-.033),l(.146,.011),l(.167,.033),l(.057,-.033),l(.021,-.067),l(.09,-.044),l(.134,-.022),l(.089,-.011),l(-.011,-.089),l(-.101,-.101),l(-.167,-.067),l(-.134,-.045),l(-.057,-.044),l(-.111,.022),l(-.078,-.045),l(-.033,-.067),l(-.123,-.101),l(-.078,-.1),l(-.066,-.022),l(-.067,.044),l(-.078,-.011),l(-.101,-.056),l(-.279,-.078),l(-.078,-.022),l(-.056,-.033),l(-.167,-.134),l(-.101,-.146),l(-.111,-.111),l(-.168,-.078),l(-.156,-.101),l(-.223,-.056),l(0,-.101),l(.179,-.101),l(.089,-.111),l(.078,-.011),l(.067,.034),l(.078,.044),l(.1,.022),l(.045,-.022),l(.012,-.134),l(.011,-.19),l(-.134,-.145),l(-.179,-.19),l(-.212,-.134),l(-.101,-.145),l(.101,.022),l(.101,.011),l(.145,.056),l(.224,.044),l(.134,-.078),l(.089,-.056),l(.067,-.078),l(-.089,-.044),l(-.135,-.022),l(-.089,-.089),l(-.123,-.078),l(-.156,-.089),l(-.033,-.101),l(-.045,-.1),l(-.212,.011),l(-.167,-.056),l(-.078,-.1),l(-.022,-.134),l(.078,-.067),l(0,-.089),l(-.033,-.1),l(.056,-.056),l(.066,-.078),l(.156,-.156),l(.156,-.223),l(.034,-.167),l(.056,-.1),l(-.022,-.067),l(-.123,-.022),l(-.179,-.011),l(-.156,0),l(-.212,.112),l(-.078,-.089),l(.056,-.067),l(.09,.033),l(.089,-.033),l(-.011,-.078),l(-.022,-.078),l(.033,-.078),l(-.045,-.067),l(0,-.156),l(.078,0),l(.101,-.089),l(-.033,-.067),l(.045,-.011),l(.056,.011),l(.066,.045),l(.078,.011),l(.067,-.022),l(.089,.034),l(.135,.011),l(.033,-.056),l(-.012,-.078),l(-.134,0),l(-.122,-.011),l(-.09,-.078),l(-.101,0),l(-.122,0),l(-.123,-.1),l(-.033,-.078),l(-.168,.022),l(-.066,-.111),l(.044,-.022),l(.045,-.034),l(-.033,-.1),l(.022,-.078),l(.056,.022),l(.078,-.011),l(-.045,-.089),l(-.101,-.034),l(-.145,.034),l(-.078,-.067),l(.011,-.146),l(.078,-.056),l(-.011,-.067),l(-.045,-.044),l(.022,-.078),l(-.022,-.112),l(-.056,-.089),l(-.201,-.142),l(1.535,-.529),l(.889,-.077),l(.907,.067),l(.054,.309),l(.569,.178),l(.19,.237),l(.202,.036),l(-.075,.356),l(.104,.119),l(-.058,.088),l(.712,.471),l(.555,.141),l(.306,.118),l(.069,.142),l(-.19,.127),l(-.124,.146),l(.183,.059),l(.239,.083),l(-.271,.071),l(-.178,.094),l(.052,.165),l(.245,.036),l(.234,0),l(.02,.189),l(.594,.059),l(.194,.139),l(.414,.239),l(.147,-.118),l(.215,-.283),l(.253,-.13),l(.625,.248),l(.114,.13),l(-.351,-.012),l(-.32,.201),l(.146,.33),l(.418,.186),
+N(551.198,117.997),l(-.351,-.48),l(-.236,-.126),l(-1.217,-.05),l(-.646,-.011),l(-.096,-.016),l(.091,-.726),l(-.062,-.503),l(.157,-.251),l(.062,-.22),l(-.503,-.094),l(-.534,-.283),l(-.566,-.189),l(-.471,.063),l(-.378,-.251),l(-1.132,-.597),l(-.565,-.22),l(-.943,-.597),l(-.314,.063),l(-1.006,-.503),l(-.377,-.44),l(-1.194,-.597),l(-1.384,-.975),l(0,-.283),l(-.188,-.44),l(-.283,-.188),l(-.408,-.597),l(-.126,-.566),l(-.22,-.377),l(-.881,-.251),l(-.188,.157),l(-.439,.063),l(-.535,-.126),l(-.439,.032),l(-.503,.094),l(-.314,-.157),l(-.691,-.314),l(.094,-.22),l(.157,-.188),l(-.188,-.22),l(.031,-.188),l(.188,-.157),l(-.439,-.283),l(0,-.22),l(-.032,-.22),l(-.251,-.22),l(-.534,-.094),l(-.692,-.095),l(-.22,-.314),l(-.346,-.032),l(-.629,-.377),l(-.472,-.095),l(-.188,.063),l(-.565,.157),l(.251,.251),l(.188,.377),l(-.597,-.283),l(-.283,0),l(-.126,.126),l(-.22,.346),l(-.283,.126),l(-.629,0),l(-.503,.251),l(-.503,.409),l(-.062,.628),l(.314,.409),l(-.126,.314),l(-1.383,.032),l(-1.03,-.063),l(.056,-8.174),l(5.658,-1.289),l(5.722,2.986),l(2.012,1.666),l(2.578,-.346),l(2.767,.188),l(1.1,-.409),l(.724,.723),l(.597,.22),l(.66,1.006),l(.66,-.314),l(-.031,1.038),l(-.188,.409),l(.031,.754),l(1.006,0),l(.221,.817),l(.346,1.069),l(.503,.063),l(.691,-.032),l(1.006,-.094),l(.346,0),l(.44,.314),l(.062,.283),l(-.062,.283),l(.44,.346),l(.66,0),l(.125,-.188),l(-.157,-.409),l(.504,-.44),l(.439,-.188),l(.221,-.472),l(.282,-.188),l(.943,-.377),l(.755,-.189),l(.534,-.471),l(.346,-.283),l(.22,-.063),l(.283,.126),l(.377,-.377),l(.322,-.031),l(.349,-.126),l(.441,.246),l(-.368,.172),l(-.368,.171),l(-.221,.049),l(-.073,.196),l(-.295,.049),l(-.294,.172),l(-.196,.147),l(-.441,.295),l(-.172,.098),l(-.024,.123),l(.294,.049),l(.295,.074),l(.146,.123),l(.418,-.147),l(.098,.221),l(.172,.221),l(.368,.27),l(.589,0),l(.393,0),l(.049,-.393),l(.221,.049),l(.196,-.196),l(.024,-.245),l(.196,.098),l(.196,.172),l(.172,.294),l(.049,.147),l(.393,.024),l(.147,-.024),l(.073,.246),l(.025,.098),l(.343,-.025),l(.319,.147),l(.245,.196),l(.516,.074),l(.466,.024),l(.172,.123),l(-.49,.221),l(-.197,.147),l(-.221,.147),l(-.49,-.024),l(-.245,-.049),l(.049,.171),l(0,.147),l(-.319,0),l(-.172,.049),l(-.343,.196),l(-.221,.196),l(-.271,.049),l(-.221,.196),l(-.245,-.147),l(-.319,-.098),l(-.294,-.098),l(-.221,.025),l(-.246,.073),l(-.318,-.073),l(-.042,.098),l(-.345,-.005),l(-.409,.031),l(-.188,-.283),l(-.251,-.063),l(-.126,-.188),l(.251,-.126),l(.409,-.346),l(.188,-.22),l(-.252,-.251),l(-.439,-.377),l(-.221,.251),l(-.471,.346),l(-.692,.188),l(-.22,.157),l(-.252,-.22),l(-.22,-.157),l(-.346,0),l(.031,.22),l(-.283,.314),l(.189,.314),l(-.032,.346),l(-.062,.126),l(-.472,-.095),l(-.565,.095),l(-.503,.094),l(.251,.125),l(.534,-.031),l(.126,.094),l(-.251,.063),l(-.188,.063),l(-.032,.346),l(-.188,0),l(-.251,.157),l(-.063,.409),l(-.282,.188),l(-1.069,-.094),l(-.629,-.126),l(-.472,.283),l(-.125,.471),l(.251,.283),l(.346,.188),l(.157,.157),l(.44,.032),l(.346,0),l(.126,.22),l(-.126,.22),l(-.031,.472),l(.126,.409),l(.471,.314),l(.126,.283),l(-.157,.22),l(-.503,.346),l(-.283,.503),l(-.377,.377),l(.063,.377),l(-.375,.843),
+N(439.792,104.833),l(-.113,-.054),l(-.105,-.07),l(.284,-.083),l(-.067,-.212),l(-.245,-.123),l(-.146,-.112),l(-.189,-.167),l(-.201,-.078),l(-.078,-.279),l(0,-.112),l(-.101,-.056),l(-.223,-.067),l(-.224,-.123),l(-.379,-.357),l(-.213,-.078),l(-.078,-.044),l(-.021,-.167),l(-.369,-.357),l(-.167,-.033),l(-.167,-.045),l(-.112,-.111),l(-.056,-.123),l(-.089,-.111),l(-.168,.011),l(-.022,-.146),l(.012,-.1),l(.078,-.056),l(-.156,-.111),l(-.022,-.123),l(-.089,-.134),l(-.146,-.234),l(-.179,-.179),l(-.101,-.033),l(-.122,-.034),l(-.09,-.089),l(-.134,-.078),l(-.022,-.089),l(.123,-.067),l(-.033,-.212),l(0,-.078),l(-.045,-.134),l(.045,-.167),l(.089,-.179),l(.123,-.044),l(.312,.034),l(.101,.111),l(.156,.19),l(.256,.134),l(.201,.022),l(.123,-.201),l(.145,-.145),l(.101,-.111),l(.212,-.022),l(.168,.033),l(.223,.022),l(.112,.011),l(.134,-.123),l(.089,.011),l(.134,.045),l(.156,.056),l(.234,.078),l(.057,.056),l(.111,-.022),l(.089,-.078),l(.156,.089),l(.168,.011),l(.189,.056),l(.156,-.011),l(.179,-.011),l(.268,.089),l(.156,.067),l(.168,-.078),l(.122,-.089),l(.112,0),l(.111,.067),l(.134,.045),l(.134,-.045),l(.111,-.067),l(.156,.045),l(.146,0),l(.067,.022),l(.156,.078),l(.1,.011),l(.101,-.089),l(.146,.045),l(.145,.056),l(.022,.112),l(.123,.011),l(.045,.089),l(-.056,.101),l(.145,.123),l(.201,.022),l(.189,-.022),l(.078,-.011),l(.212,-.112),l(.156,0),l(.179,.011),l(.123,.022),l(.022,.067),l(-.056,.1),l(-.034,.167),l(-.156,.223),l(-.156,.156),l(-.066,.078),l(-.056,.056),l(.033,.1),l(0,.089),l(-.078,.067),l(.022,.134),l(.078,.1),l(.167,.056),l(.212,-.011),l(.045,.1),l(.033,.101),l(.156,.089),l(.123,.078),l(.089,.089),l(.135,.022),l(.089,.044),l(-.067,.078),l(-.089,.056),l(-.134,.078),l(-.224,-.044),l(-.145,-.056),l(-.101,-.011),l(-.101,-.022),l(.101,.145),l(.212,.134),l(.179,.19),l(.134,.145),l(-.011,.19),l(-.012,.134),l(-.045,.022),l(-.1,-.022),l(-.078,-.044),l(-.067,-.034),l(-.078,.011),l(-.089,.111),l(-.179,.101),l(-.056,-.033),l(-.156,.056),l(-.112,-.022),l(-.066,-.044),l(-.112,.033),l(-.078,.056),l(.012,.078),l(.089,.1),l(.123,.167),l(.056,.101),l(-.056,.101),l(-.111,0),l(-.09,-.056),l(-.056,-.089),l(-.056,-.044),l(-.123,-.011),l(-.122,.056),l(-.168,.078),l(-.045,.101),l(-.044,.089),l(-.112,.101),l(.034,.089),l(.011,.1),L(442,104.458),l(-.134,.011),l(-.111,.022),l(-.101,.089),l(-.012,.134),l(.012,.112),l(.011,.145),l(.012,.044),l(.066,.112),l(.078,.089),l(.045,.101),l(-.09,.089),l(-.183,.108),l(-.062,-.063),l(-.089,-.034),l(-.123,-.022),l(-.134,.033),l(-.045,-.067),l(-.312,-.257),l(-.212,-.011),l(-.179,-.078),l(-.146,-.089),l(-.066,-.089),l(-.045,-.078),l(-.123,-.034),l(-.134,-.011),l(-.132,.118),
+N(450.198,105.998),l(.013,-.208),l(.008,-.514),l(.246,-.301),l(.762,-.333),l(.302,-.302),l(-.028,-.2),l(-1.326,-1.122),l(-.056,-.271),l(.18,-.302),l(.383,-.374),l(.328,-.346),l(.392,.204),l(.352,.106),l(-.234,.318),l(.048,.189),l(.266,-.023),l(.771,-.189),l(.506,.002),l(1.151,.395),l(.34,-.031),l(.69,-.261),l(.3,.013),l(1.385,.35),l(.38,-.059),l(1.904,-.771),l(.618,-.218),l(.562,.04),l(.671,.369),l(.295,.042),l(.514,.226),l(1.346,.336),l(.671,.12),l(-.066,.335),l(-.077,.258),l(-.261,.086),l(-.313,-.028),l(-.339,.129),l(-.327,.73),l(-.039,.586),l(-.075,.143),l(-.404,.115),l(-.338,.372),l(-.017,.257),l(.252,-.036),l(.255,.224),l(.033,.154),l(.391,.375),l(.01,.223),l(-1.333,-.005),l(-.527,-.111),l(-.497,.045),l(-.629,.374),l(-.498,.445),l(-.363,-.026),l(-.344,.216),l(.097,.327),l(-.086,.257),l(-1.117,.277),l(-.388,.031),l(-.619,-.21),l(-1.473,-.505),l(-.584,.06),l(-.799,.261),l(-1.855,.195),l(-.09,.029),l(-.047,-.199),l(.104,-.3),l(.006,-.499),l(-.225,-.469),l(-.358,-.383),l(-.666,-.296),l(-.134,-.213),l(.007,-.106),
+N(381.009,107),l(-.121,-.278),l(.138,-.4),l(.343,-.5),l(-.358,-.471),l(-.304,-.428),l(-.514,-.07),l(-.164,-.1),l(-.053,-.329),l(.163,-.243),l(.409,-.272),l(.365,-.101),l(.563,-.03),l(.634,-.03),l(.133,-.172),l(.068,-.415),l(.535,-.273),l(.763,.042),l(1.078,.37),l(.763,.07),l(.756,-.087),l(.577,-.173),l(.508,-.144),l(.354,-.001),l(.629,.285),l(.694,.156),l(.939,.084),l(1.538,.04),l(.583,.027),l(.957,.141),l(.491,-.158),l(.419,-.229),l(.531,.027),l(.891,.47),l(.67,-.016),l(.335,.062),l(.472,.243),l(.469,-.03),l(.058,.122),l(-.205,.243),l(.094,.106),l(.15,.03),l(.112,-.106),l(1.088,.334),l(.15,-.061),l(.507,.395),l(.056,-.076),l(.262,.03),l(.131,-.076),l(.431,.152),l(.028,.038),l(.084,.114),l(.767,-.03),l(.037,.122),l(.337,-.061),l(.542,.015),l(-.017,-.319),l(.355,0),l(1.252,.304),l(.091,.213),l(.035,.289),l(.187,.076),l(.374,-.076),l(.206,-.03),l(.335,.091),l(.036,.152),l(.261,.015),l(.395,-.167),l(.427,.197),l(.485,.015),l(.039,-.136),l(.75,-.137),l(.334,.091),l(-.001,.088),l(-.001,.463),l(.156,.1),l(-.062,.485),l(-1.112,.528),l(-.95,.385),l(-.267,.328),l(-1.046,.198),l(-.664,.116),l(-.96,.301),l(-.323,.326),l(-.053,.2),l(.261,.128),l(-.088,.157),l(-.628,.143),l(-.594,.783),l(-.886,.787),l(-.096,.192),l(-.18,.361),l(-.245,.45),l(.353,.827),l(.072,.111),l(.084,.13),l(.648,.295),l(.103,.185),l(-.621,.327),l(-.215,.105),l(-.515,.252),l(-.286,.479),l(-.224,.085),l(-.461,.926),l(.155,.322),l(-.257,.099),l(-.992,.049),l(-.581,.242),l(-.425,.327),l(-.274,.757),l(-.663,.496),l(-.258,-.213),l(-.599,.028),l(-.305,.27),l(-.342,0),l(-.121,-.113),l(-3.282,.042),l(-.69,.524),l(-1.021,.17),l(-.35,.382),l(-.028,.283),l(-.083,.085),l(-.073,-.212),l(-.068,-.014),l(.005,.241),l(-.389,.127),l(-.421,-.142),l(-.788,-.467),l(-.224,-.382),l(.036,-.262),l(-.345,-.113),l(-.125,-.213),l(.175,-.163),l(-.468,-.51),l(-.702,-.284),L(385,117.498),l(-.484,-.135),l(-.586,.039),l(.008,-.018),l(.304,-.951),l(.242,-.37),l(.884,-.643),l(-.408,-.31),l(-.812,-.123),l(.17,-.455),l(.506,-.655),l(.347,-.371),l(-.163,-.198),l(-.455,-.551),l(-.488,-.494),l(.288,-.129),l(.482,-.045),l(.458,-.229),l(.043,-.199),l(-.057,-.938),l(.132,-.983),l(-.072,-.456),l(.051,-.442),l(.084,-.072),l(1.234,-.506),l(.288,-.216),l(-.062,-.242),l(-.842,-.495),l(-.15,-.242),l(-.272,-.227),l(-.335,-.055),l(-.531,.26),L(382.981,107),l(-.531,-.439),l(-.55,.188),L(381.009,107),
+N(489.685,103.693),l(.112,-.309),l(.26,-.166),l(.284,.047),l(.07,.047),l(.402,.023),l(.449,.023),l(.283,.095),l(.284,.142),l(.188,.094),l(.189,.047),l(.331,0),l(.213,0),l(.212,.166),l(.261,.095),l(.307,.071),l(.355,.047),l(.307,0),l(.426,-.095),l(.544,0),l(.401,.166),l(.189,0),l(.283,-.047),l(.354,.166),l(.095,.142),l(.284,.213),l(.52,.118),l(.354,.071),l(.236,.118),l(.308,.119),l(-.142,.118),l(-.048,.118),l(.261,.118),l(.212,.071),l(.261,-.118),l(.283,0),l(.166,-.166),l(.094,-.095),l(.213,-.071),l(.354,0),l(.261,.071),l(.188,.142),l(.142,-.166),l(.095,-.071),l(.118,0),l(.236,.118),l(.143,.094),l(.212,0),l(.189,.118),l(.213,.166),l(.378,0),l(.354,.024),l(.118,.142),l(-.118,.189),l(-.118,.307),l(.354,.284),l(.284,.166),l(.26,.094),l(.284,.047),l(.236,-.023),l(.236,.071),l(.126,.189),l(-.268,.189),l(-.143,.142),l(-.095,.071),l(.143,.26),l(.213,.307),l(.614,.166),l(.118,.213),l(-.095,.331),l(-.236,.095),l(-.236,.047),l(-.26,-.189),l(-.143,-.071),l(-.188,-.023),l(-.284,.047),l(-.638,-.189),l(-.189,-.213),l(-.331,-.189),l(-.473,-.024),l(-.236,0),l(-.418,.308),l(-.291,.094),l(-.378,.047),l(-.591,.095),l(-.592,-.047),l(-.401,.118),l(-.426,.023),l(-.308,.095),l(-.307,-.024),l(-.377,.108),l(-.031,-.028),l(-1.326,-1.018),l(-.41,-.041),l(-.761,.36),l(-.226,.072),l(-.491,-.068),l(-1.212,-.082),l(.083,-.065),l(.322,-.585),l(.032,-.143),l(-.064,-.728),l(-.331,-1.084),l(-.206,-.399),l(-.639,-.513),l(-.341,-.128),l(-.916,-.155),l(-.679,-.271),l(-.341,-.243),
+N(443.617,107.095),l(-.065,-.035),l(-.435,-.156),l(-.017,-.15),l(-.501,-.485),l(-.848,-.3),l(-.033,-.021),l(.002,-.1),l(-.029,-.092),l(-.082,-.072),l(-.033,-.078),l(.022,-.089),l(-.005,-.004),l(.183,-.108),l(.09,-.089),l(-.045,-.101),l(-.078,-.089),l(-.066,-.112),l(-.012,-.044),l(-.011,-.145),l(-.012,-.112),l(.012,-.134),l(.101,-.089),l(.111,-.022),l(.134,-.011),l(.056,-.056),l(-.011,-.1),l(-.034,-.089),l(.112,-.101),l(.044,-.089),l(.045,-.101),l(.168,-.078),l(.122,-.056),l(.123,.011),l(.056,.044),l(.056,.089),l(.09,.056),l(.111,0),l(.056,-.101),l(-.056,-.101),l(-.123,-.167),l(-.089,-.1),l(-.012,-.078),l(.078,-.056),l(.112,-.033),l(.066,.044),l(.112,.022),l(.156,-.056),l(.056,.033),l(0,.101),l(.223,.056),l(.156,.101),l(.168,.078),l(.111,.111),l(.101,.146),l(.167,.134),l(.056,.033),l(.078,.022),l(.279,.078),l(.101,.056),l(.078,.011),l(.067,-.044),l(.066,.022),l(.078,.1),l(.123,.101),l(.033,.067),l(.078,.045),l(.111,-.022),l(.057,.044),l(.134,.045),l(.167,.067),l(.101,.101),l(.011,.089),l(-.089,.011),l(-.134,.022),l(-.09,.044),l(-.021,.067),l(-.057,.033),l(-.167,-.033),l(-.146,-.011),l(-.111,.033),l(-.022,.089),l(.056,.056),l(.101,0),l(.078,.067),l(0,.067),l(-.109,.286),l(-.361,-.022),l(-.727,-.11),l(-.273,.273),l(-.279,.515),l(.133,.427),l(-.002,.342),
+N(558.52,110.652),l(.042,-.098),l(.318,.073),l(.246,-.073),l(.221,-.025),l(.294,.098),l(.319,.098),l(.245,.147),l(.221,-.196),l(.271,-.049),l(.221,-.196),l(.343,-.196),l(.172,-.049),l(.319,0),l(0,-.147),l(-.049,-.171),l(.245,.049),l(.49,.024),l(.221,-.147),l(.197,-.147),l(.49,-.221),l(-.172,-.123),l(-.466,-.024),l(-.516,-.074),l(-.245,-.196),l(-.319,-.147),l(-.343,.025),l(-.025,-.098),l(-.073,-.246),l(-.147,.024),l(-.393,-.024),l(-.049,-.147),l(-.172,-.294),l(-.196,-.172),l(-.196,-.098),l(-.024,.245),l(-.196,.196),l(-.221,-.049),l(-.049,.393),l(-.393,0),l(-.589,0),l(-.368,-.27),l(-.172,-.221),l(-.098,-.221),l(-.418,.147),l(-.146,-.123),l(-.295,-.074),l(-.294,-.049),l(.024,-.123),l(.172,-.098),l(.441,-.295),l(.196,-.147),l(.294,-.172),l(.295,-.049),l(.073,-.196),l(.221,-.049),l(.368,-.171),l(.368,-.172),l(-.441,-.246),l(-.349,.126),l(-.044,-.273),l(.393,-.442),l(.318,-.368),l(.736,-.123),l(.663,-.098),l(.883,.147),l(.883,.245),l(.688,.196),l(.81,.123),l(.344,.123),l(-.024,-.442),l(.245,-.736),l(.466,-.368),l(.688,-.123),l(.589,.074),l(.761,.27),l(.735,.246),l(.908,.196),l(.54,.098),l(.441,-.27),l(.858,-.024),l(.761,0),l(.785,-.147),l(.712,.221),l(.662,.098),l(1.35,.024),l(.662,-.074),l(.981,.246),l(.564,-.049),l(.147,.344),l(.27,.147),l(.196,.27),l(.663,0),l(.466,.098),l(.41,.375),l(.031,.194),l(-.051,.157),l(-.325,.187),l(-.97,.219),l(-1.338,.349),l(-.445,.145),l(-.405,.301),l(-.638,.701),l(-.646,.345),l(-.478,.102),l(-.459,.017),l(-1.248,-.235),l(-.238,.03),l(-.467,.472),l(-.463,.784),l(-.268,.243),l(-.885,.132),l(-.507,.145),l(-.344,-.055),l(-.183,-.567),l(-.06,-.071),l(-.359,.03),l(-1.737,.734),l(-1.422,.704),l(-.274,.186),l(-.129,.213),l(-.139,.739),l(-.196,-.073),l(-.344,.098),l(-.344,.171),l(-.539,0),l(-.663,-.073),l(-.834,.221),l(-.172,.147),l(-.196,0),l(-.172,-.319),l(-.368,.024),l(-.318,.172),l(-.074,-.221),l(-.049,-.172),l(-.122,.024),l(-.319,-.123),l(-.049,-.147),l(-.221,-.024),l(-.442,.123),l(-.343,.049),l(.024,.221),l(-.295,.049),l(-.393,-.074),l(-.073,-.196),l(-.147,-.123),l(-.368,-.098),l(-.49,.147),l(-.196,-.073),l(-.688,.024),l(-.564,0),l(-.589,.024),l(-.122,-.098),l(-.049,-.147),l(-.099,-.27),l(.099,-.245),l(.196,-.196),l(.098,.221),l(.196,-.074),l(-.049,-.196),l(.098,-.27),l(.123,0),l(.981,-.196),l(.515,.147),l(.516,.196),l(.099,.172),l(.196,0),l(.024,-.246),l(.441,-.196),l(.302,-.147),
+N(685.343,114.455),l(-.571,.678),l(-.309,.115),l(-.511,-.096),l(-.579,-.068),l(-.595,-.011),l(-.315,.157),l(-.633,.738),l(-.283,.256),l(-.235,.171),l(-.268,-.206),l(-.35,.34),l(-.319,.199),l(-.373,-.608),l(-.398,-.112),l(-.649,.78),l(-.195,-.382),l(-.232,-.254),l(-.683,-.367),l(-.169,-.453),l(.095,-.312),l(.429,-.411),l(.754,-.229),l(.056,-.269),l(-.591,-.282),l(.407,-.879),l(.189,-.34),l(-.199,-.269),l(-.632,-.296),l(-.139,0),l(-.381,.029),l(-.312,.143),l(-.234,-.07),l(-.52,-.368),l(-.167,-.233),l(.379,-.528),l(.415,-.442),l(.52,-.329),l(1.533,-.604),l(1.032,-.545),l(.636,-.543),l(.686,-1.027),l(.386,-.13),l(.448,-.017),l(.273,.396),l(.493,.253),l(.508,.153),l(.975,-.048),l(.527,-.159),l(-.046,-.113),l(-.508,-.765),l(.025,-.342),l(.273,-.243),l(.392,-.059),l(.333,.126),l(.452,.054),l(.538,-.017),l(.62,-.259),l(.955,-.532),l(.23,-.713),l(.383,-.358),l(.253,-.129),l(.247,-.001),l(.579,.68),l(.298,.439),l(.167,.393),l(-1.356,.923),l(-.408,.457),l(-.112,.414),l(.09,.427),l(-.154,.456),l(-.187,.868),l(-.668,.115),l(-.36,.229),l(-.497,.385),l(-.766,.641),l(-.468,.214),l(-.678,.03),l(-.577,.199),l(-.265,.228),l(-.248,.312),l(-.364,.893),l(.284,.326),l(1.225,.847),l(.419,.354),
+N(536.625,121.017),l(-.078,-.028),l(-.15,-.692),l(-.01,-.565),l(-.038,-.848),l(-.185,-.211),l(-.787,.075),l(-.696,-.01),l(-.655,-.506),l(-1.803,-1.362),l(-.597,-.336),l(-.66,-.167),l(-.5,-.054),l(-.788,-.066),l(-.822,-.335),l(-.708,-.251),l(-.402,-.437),l(-1.055,-.107),l(-.519,-.054),l(-.343,.129),l(-.517,.343),l(-.333,.03),l(-.78,-.038),l(-.609,.032),l(-.413,.144),l(-.476,.328),l(-.621,.654),l(-.466,.3),l(-.562,.13),l(-.441,-.025),l(-.066,-.376),l(-.128,-.681),l(-.106,-.447),l(.128,-.298),l(0,-.383),l(0,-.532),l(.106,-.191),l(.106,-.298),l(.085,-.234),l(-.085,-.212),l(-.256,-.128),l(-.319,-.191),l(-.213,-.255),l(-.042,-.149),l(-.171,0),l(-.191,-.042),l(-.361,-.106),l(-.191,.192),l(-.086,-.234),l(.086,-.106),l(.148,-.255),l(.128,.106),l(.383,-.042),l(.426,.085),l(.128,.021),l(.043,-.128),l(-.319,-.213),l(-.256,-.021),l(-.085,-.277),l(.17,-.255),l(.213,-.191),l(-.404,-.042),l(-.319,.085),l(-.383,0),l(-.319,-.085),l(-.128,.149),l(-.17,-.255),l(-.149,-.298),l(0,-.34),l(-.042,-.298),l(.17,-.213),l(.106,-.319),l(.043,-.255),l(.105,-.277),l(.086,-.234),l(.213,.34),l(.063,.128),l(.17,.17),l(.405,-.085),l(.383,.128),l(.106,-.149),l(-.021,-.149),l(.106,0),l(.148,.021),l(.064,.319),l(.106,.191),l(.298,-.021),l(.298,-.063),l(.256,-.106),l(.233,.085),l(.192,.064),l(.085,-.128),l(-.149,-.191),l(-.042,-.213),l(.191,-.042),l(.106,.149),l(.233,.085),l(.256,-.085),l(.213,-.064),l(.021,-.234),l(-.171,-.341),l(-.34,-.234),l(-.532,-.319),l(-.426,-.213),l(-.063,-.319),l(-.043,-.34),l(-.213,-.17),l(0,-.213),l(0,-.213),l(-.085,-.127),l(-.554,-.064),l(-.617,.085),l(-.426,.021),l(-.446,.127),l(-.192,.277),l(-.085,.298),l(.128,.192),l(-.063,.276),l(-.086,.405),l(.064,.234),l(.021,.298),l(-.256,-.553),l(-.361,-.319),l(.042,-.17),l(-.063,-.191),l(-.274,-.143),l(.529,-.453),l(.937,-.532),l(1.277,-.298),l(.979,-.085),l(.512,.234),l(.681,.383),l(.617,.383),l(.256,.511),l(.638,.703),l(.447,.255),l(.489,-.043),l(.341,-.106),l(.158,.014),l(1.03,.063),l(1.383,-.032),l(.126,-.314),l(-.314,-.409),l(.062,-.628),l(.503,-.409),l(.503,-.251),l(.629,0),l(.283,-.126),l(.22,-.346),l(.126,-.126),l(.283,0),l(.597,.283),l(-.188,-.377),l(-.251,-.251),l(.565,-.157),l(.188,-.063),l(.472,.095),l(.629,.377),l(.346,.032),l(.22,.314),l(.692,.095),l(.534,.094),l(.251,.22),l(.032,.22),l(0,.22),l(.439,.283),l(-.188,.157),l(-.031,.188),l(.188,.22),l(-.157,.188),l(-.094,.22),l(.691,.314),l(.314,.157),l(.503,-.094),l(.439,-.032),l(.535,.126),l(.439,-.063),l(.188,-.157),l(.881,.251),l(.22,.377),l(.126,.566),l(.408,.597),l(.283,.188),l(.188,.44),l(0,.283),l(1.384,.975),l(1.194,.597),l(.377,.44),l(1.006,.503),l(.314,-.063),l(.943,.597),l(.565,.22),l(1.132,.597),l(.378,.251),l(.471,-.063),l(.566,.189),l(.534,.283),l(.503,.094),l(-.062,.22),l(-.157,.251),l(.062,.503),l(-.091,.726),l(-1.454,-.244),l(-.565,-.294),l(-.445,.356),l(-.417,.2),l(-1.135,.205),l(-.432,.809),l(-.203,.991),l(-.103,.128),l(-.508,.243),l(-1.985,.689),l(-.568,.159),l(-.119,.199),l(-.001,.466),l(-.22,.199),l(-.636,.3),l(-.534,.031),l(-.573,-.082),l(-.999,-.348),l(-.937,-.193),l(-.193,-.112),
+N(445.294,112.196),l(-.07,-.115),l(-.138,-.469),l(-.5,-.452),l(-.966,-.541),l(.024,-.141),l(.23,.062),l(.023,-.237),l(-.345,-.414),l(.418,-.616),l(-.182,-.22),l(.188,-.563),l(-.251,-.282),l(.182,-.396),l(.268,-.079),l(-.027,-.45),l(-.331,-.081),l(-.2,-.107),l(.002,-.342),l(-.133,-.427),l(.279,-.515),l(.273,-.273),l(.727,.11),l(.361,.022),l(.058,.004),l(.382,.369),l(.733,.866),l(.018,.109),l(.035,.218),l(-.132,.429),l(.074,.641),l(.298,.668),l(.722,.608),l(-.09,.029),l(-.449,.842),l(-.402,.386),l(-.496,.472),l(-.583,.884),
+N(451.512,108.463),l(-.507,.16),l(-.532,.245),l(-.622,-.054),l(-.361,-.041),l(-.365,.159),l(-.395,.429),l(-.606,.146),l(-.809,.076),l(-.722,-.608),l(-.298,-.668),l(-.074,-.641),l(.132,-.429),l(-.035,-.218),l(.14,.049),l(.188,0),l(.072,-.087),l(.029,-.072),l(-.029,-.13),l(.058,-.167),l(.088,-.058),l(.225,-.058),l(.246,-.058),l(.116,-.058),l(.08,-.065),l(.087,.073),l(.131,.058),l(.094,.109),l(.116,0),l(.029,-.058),l(-.059,-.072),l(.109,-.058),l(.151,-.102),l(.152,-.087),l(.175,.043),l(.064,-.007),l(.196,.007),l(.152,.015),l(.087,-.08),l(.131,-.116),l(.064,.021),l(.102,.043),l(.088,-.021),l(.087,-.043),l(.102,.051),l(.058,-.015),l(.116,-.022),l(.145,-.021),l(.16,-.022),l(.072,-.029),l(.058,.017),l(-.007,.106),l(.134,.213),l(.666,.296),l(.358,.383),l(.225,.469),l(-.006,.499),l(-.104,.3),l(.047,.199),
+N(383.93,117.402),l(-.249,.101),l(-.517,.291),l(-.439,.052),l(-.548,-.178),l(-.58,0),l(-.28,-.073),l(-.719,.292),l(-.058,-.177),l(.389,-1.012),l(-.021,-.856),l(-.182,-.115),l(.244,-.542),l(-.054,-.397),l(.13,-.114),l(-.144,-.141),l(-.375,.085),l(-.476,.097),l(-.108,-.449),l(.48,-.052),l(.283,-.22),l(-.042,-.17),l(-.178,-.226),l(-.3,.417),l(-.413,.136),l(-.357,-.042),l(-.059,-.188),l(.198,-.397),l(.138,-.616),l(-.039,-.303),l(.258,-.114),l(.403,-.503),l(.45,-1.098),l(-.12,-.115),l(.612,-1.783),l(-.35,-.924),l(-.007,-.42),l(-.146,-.378),l(.255,-.271),l(.891,-.251),l(.55,-.188),l(.531,.439),l(1.822,.047),l(.531,-.26),l(.335,.055),l(.272,.227),l(.15,.242),l(.842,.495),l(.062,.242),l(-.288,.216),l(-1.234,.506),l(-.084,.072),l(-.051,.442),l(.072,.456),l(-.132,.983),l(.057,.938),l(-.043,.199),l(-.458,.229),l(-.482,.045),l(-.288,.129),l(.488,.494),l(.455,.551),l(.163,.198),l(-.347,.371),l(-.506,.655),l(-.17,.455),l(.812,.123),l(.408,.31),l(-.884,.643),l(-.242,.37),l(-.304,.951),l(-.008,.018),
+N(500.121,117.572),l(-.407,-.016),l(-.433,.388),l(-.164,.126),l(-.318,-.105),l(-.102,-.269),l(.03,-.259),l(-.274,-.151),l(-.366,-.082),l(-.244,.234),l(-.343,-.023),l(-.811,-.153),l(-.364,.032),l(-.304,-.16),l(-.437,.094),l(-.266,.143),l(-.23,.043),l(-.064,-.245),l(-.207,-.023),l(-.24,.292),l(-.693,.304),l(-1.185,.224),l(-.711,-.039),l(-.747,-.123),l(-.439,.073),l(-1.498,.673),l(-.567,.13),l(-1.104,.176),l(-.556,-.153),l(-1.532,-.444),l(-.278,.03),l(-.929,.373),l(-.746,.075),l(-.575,-.025),l(-.777,-.166),l(-.222,.001),l(-.142,-.035),l(-.055,.319),l(.102,.452),l(.243,.423),l(-.627,.127),l(-.156,.374),l(-.2,.169),l(-.171,-.041),l(-.114,.127),l(-.39,-.125),l(-.311,.001),l(-.245,-.459),l(-.119,-.093),l(.097,-.175),l(.242,-.197),l(.617,-.403),l(.021,-.175),l(-.049,-.134),l(-.279,-.28),l(-.146,-.053),l(-.487,.368),l(-.23,.041),l(-.137,.064),l(.092,.041),l(-.118,.216),l(-.172,.023),l(-.063,-.047),l(-.076,.088),l(-.297,.058),l(-.332,-.222),l(-.447,-.198),l(-.461,-.157),l(-.395,.046),l(-.849,.548),l(-.337,.286),l(.006,.204),l(-.141,.046),l(-.122,.07),l(-.005,.082),l(-.179,-.169),l(-.604,.206),l(-.689,.185),l(-.594,-.013),l(-.587,-.07),l(-.678,-.267),l(-.963,-.819),l(-1.181,-.479),l(-1.034,-.182),l(-.692,.072),l(-.119,.255),l(-.097,.609),l(-.053,.411),l(-.173,.156),l(-.256,0),l(-.253,-.155),l(-1.12,.243),l(-.423,-.027),l(-.386,-.183),l(-.657,-1.159),l(-.42,.354),l(-.764,-.451),l(-.451,.057),l(-.562,.412),l(-.227,-.382),l(.066,-.127),l(.242,-.17),l(-.116,-.17),l(-.989,-.012),l(-.545,-.013),l(-.088,-.269),l(.571,-.199),l(-.074,-.241),l(-.284,-.198),l(-.454,-.07),l(-.084,-.297),l(.041,-.34),l(.087,-.284),l(-.089,-.255),l(-.396,-.126),l(-.627,-.353),l(-.371,.086),l(-.265,-.084),l(-.004,-.255),l(.171,-.501),l(.131,.059),l(.478,.311),l(.567,-.271),l(-.396,-.283),l(.021,-.124),l(-.296,-.128),l(.03,-.128),l(.571,-.159),l(.152,-.113),l(-.068,-.142),l(-.149,-.088),l(-.337,-.035),l(.01,-.187),l(.18,-.07),l(-.163,-.164),l(-.198,-.117),l(-.009,-.152),l(-.227,-.012),l(.263,-.181),l(.296,-.275),l(.161,-.035),l(.07,-.16),l(-.341,-.042),l(-.573,.12),l(-.905,.164),l(-.166,-.035),l(.046,-.33),l(.127,-.125),l(-.003,-.199),l(-.029,-.286),l(.13,-.264),l(.299,.012),l(.184,-.41),l(.175,-.023),l(.63,-.422),l(.514,.012),l(.133,-.129),l(.479,-.047),l(.128,.211),l(.268,.102),l(.169,.028),l(.529,.022),l(.147,-.129),l(-.067,-.129),l(-.269,-.129),l(.286,-.094),l(.324,.036),l(.117,.082),l(-.219,.223),l(.213,-.026),l(1.053,-.073),l(.619,.042),l(.379,.046),l(.279,.047),l(.155,-.176),l(-.086,-.094),l(-.468,-.035),l(-.212,-.118),l(.275,-.212),l(1.386,-.151),l(.417,-.012),l(.377,-.117),l(-.442,-.012),l(-.592,.023),l(-.215,0),l(-.068,-.146),l(-.611,-.382),l(.325,-.528),l(.926,.14),l(1.244,.048),l(.264,-.117),l(1.086,.321),l(1.051,-.031),l(.414,-.243),l(-.041,-.27),l(.624,-.244),l(.455,-.214),l(1.218,-.573),l(.598,-.215),l(1.039,-.23),l(.889,-.073),l(.758,.07),l(.905,.126),l(.798,.041),l(.753,-.372),l(.216,.527),l(.416,.298),l(.278,.099),l(.592,.013),l(.622,-.144),l(.453,.74),l(.492,.255),l(.574,-.172),l(.391,.056),l(.968,.582),l(1.265,.04),l(1.094,.197),l(.749,-.001),l(1.084,-.272),l(.514,-.044),l(.651,.141),l(.764,.098),l(.787,-.016),l(.554,-.144),l(1.518,-.573),l(.424,-.335),l(1.212,.082),l(.491,.068),l(.226,-.072),l(.761,-.36),l(.41,.041),l(1.326,1.018),l(.031,.028),l(.795,.722),l(.026,.199),l(-.421,.813),l(.033,.412),l(.284,.211),l(1.413,.12),l(.492,.451),l(-.072,.211),l(-.409,-.023),l(-.231,.141),l(-.009,.433),l(-.584,.267),l(-.039,.27),l(.264,.67),l(-.122,.375),l(.224,.492),l(.09,.117),l(.106,-.105),l(.288,.203),l(.039,.207),l(-.229,.281),l(-.287,.535),l(-.06,.128),l(.213,.14),l(.424,.111),l(-.145,.245),l(.099,.421),l(.42,.374),l(.275,.035),l(.023,.308),M(462.617,106.804),l(.241,.211),l(-.019,.287),l(.115,.285),l(.077,.071),l(.593,.355),l(.819,.241),l(.605,.155),l(.152,.121),L(464.943,109),l(-.304,.166),l(-.515,-.072),l(-.94,-.246),l(-.326,.07),l(-.209,.152),l(-1.019,-.012),l(-.357,.384),l(-.109,.273),l(-.833,.316),l(-.612,.282),l(-.222,.258),l(-.307,.152),l(-.268,.293),l(-.255,.082),l(.164,-.258),l(.019,-.141),l(-.062,-.176),l(.584,-.293),l(.22,-.141),l(-.226,-.191),l(-.082,.015),l(-.653,-.056),l(-.229,-.148),l(.326,-.546),l(.387,-.558),l(.678,-.631),l(-.127,-.227),l(-.427,-.197),l(-.105,0),l(.498,-.445),l(.629,-.374),l(.497,-.045),l(.527,.111),l(1.333,.005),
+N(509.077,114.955),l(-.72,-.317),l(-.268,.016),l(-.356,-.433),l(-.374,-.105),l(-.13,-.363),l(.532,-.27),l(.095,-.222),l(-.43,-.176),l(-.027,-.188),l(.63,-.129),l(.094,-.155),l(-.061,-.113),l(-.487,-.21),l(-.351,-.281),l(-.306,-.166),l(-.456,.234),l(-1.058,.492),l(-.374,.445),l(-.642,.188),l(-.254,.255),l(-.014,-.027),l(.094,-.118),l(-.094,-.213),l(-.189,-.071),l(.26,-.095),l(.166,-.047),l(-.261,-.189),l(-.236,-.236),l(.236,-.118),l(.095,-.189),l(-.283,-.047),l(-.354,-.024),l(-.284,-.118),l(-.213,-.212),l(-.236,-.024),l(-.26,-.354),l(-.283,-.142),l(-.048,-.094),l(.166,0),l(.378,0),l(.165,-.236),l(0,-.236),l(-.213,-.024),l(-.188,-.142),l(-.544,-.331),l(-.283,-.354),l(.047,-.284),l(.402,-.142),l(-.119,-.236),l(-.212,-.166),l(-.426,-.071),l(-.284,-.095),l(.071,-.094),l(.071,-.118),l(-.284,-.095),l(-.087,-.212),l(.418,-.308),l(.236,0),l(.473,.024),l(.331,.189),l(.189,.213),l(.638,.189),l(.284,-.047),l(.188,.023),l(.143,.071),l(.26,.189),l(.236,-.047),l(.236,-.095),l(.095,-.331),l(-.118,-.213),l(-.614,-.166),l(-.213,-.307),l(-.143,-.26),l(.095,-.071),l(.143,-.142),l(.268,-.189),l(.229,-.023),l(.023,.166),l(.213,-.047),l(.189,0),l(.142,.189),l(.473,.284),l(.095,.118),l(.118,0),l(.283,.284),l(0,.308),l(.591,.094),l(.449,.142),l(.379,-.047),l(.165,-.213),l(.308,-.331),l(.283,-.094),l(.496,-.284),l(.292,-.449),l(.465,.331),l(.236,.378),l(.26,.189),l(.284,.307),l(.095,.52),l(.142,.236),l(.283,.26),l(.284,.165),l(0,.166),l(.449,.236),l(.473,-.047),l(.378,.071),l(.284,.166),l(.236,.189),l(.095,.189),l(0,.142),l(-.355,-.142),l(-.401,-.047),l(-.213,0),l(-.26,.047),l(-.142,.118),l(-.402,.071),l(-.213,.142),l(-.047,.189),l(-.023,.473),l(-.118,.26),l(-.095,.236),l(-.095,.378),l(.213,.236),l(-.023,.189),l(-.237,-.071),l(-.094,.095),l(-.071,.331),l(-.071,.26),l(-.118,-.047),l(-.094,-.236),l(-.143,-.095),l(-.165,.095),l(-.047,.307),l(.07,.166),l(-.118,.118),l(-.118,.095),l(.095,.26),l(-.363,.91),M(499.844,111.738),l(.709,.061),l(.142,-.047),l(.26,-.071),l(.236,.236),l(.071,.166),l(.378,.142),l(.213,.071),l(.308,-.118),l(.52,0),l(-.071,.213),l(.024,.236),l(.118,.023),l(.331,.166),l(-.071,.236),l(.421,.763),l(-.009,.001),l(-.253,-.133),l(-.416,.038),l(-.512,-.025),l(-.421,-.125),l(-.335,-.211),l(-.294,-.402),l(-.551,-.223),l(-.281,-.417),l(-.265,-.381),l(-.252,-.197),
+N(455.452,122.442),l(.049,-.209),l(-.057,-.128),l(-.812,-.256),l(-.691,-.006),l(-.506,-.116),l(-.484,.017),l(-.121,-.046),l(-.103,-.093),l(.139,-.56),l(.315,-.005),l(-.005,-.088),l(-.009,-.122),l(.069,-.07),l(.083,.157),l(.021,.146),l(.303,.021),l(.172,.055),l(.184,-.076),l(-.014,-.082),l(.108,-.029),l(.157,.105),l(-.037,.093),l(-.099,.006),l(-.04,.053),l(.088,.023),l(.144,.035),l(.094,.046),l(.021,.128),l(.353,.041),l(.846,-.122),l(.509,.016),l(.035,.13),l(.192,.035),l(.608,.064),l(.307,.051),l(.358,-.121),l(.09,.05),l(-.101,.312),l(.163,.11),l(.105,0),l(.325,-.169),l(.286,-.058),l(.078,.052),l(.154,-.07),l(.232,-.146),l(-.083,.187),l(.015,.186),l(-.183,.268),l(-.582,-.046),l(-.349,.081),l(-.335,-.017),l(-1.994,.169),M(445.294,112.196),l(.583,-.884),l(.496,-.472),l(.402,-.386),l(.449,-.842),l(.09,-.029),l(.809,-.076),l(.606,-.146),l(.395,-.429),l(.365,-.159),l(.361,.041),l(.622,.054),l(.532,-.245),l(.507,-.16),l(.09,-.029),l(1.855,-.195),l(.799,-.261),l(.584,-.06),l(1.473,.505),l(.619,.21),l(.388,-.031),l(1.117,-.277),l(.086,-.257),l(-.097,-.327),l(.344,-.216),l(.363,.026),l(.105,0),l(.427,.197),l(.127,.227),l(-.678,.631),l(-.387,.558),l(-.326,.546),l(-.062,-.407),l(-.794,-.056),l(-.743,-.041),l(-.566,-.125),l(-.062,-.144),l(-.459,.186),l(-.248,.123),l(-.403,.012),l(-.031,-.247),l(-.335,.029),l(-.301,.314),l(-.431,.186),l(-.31,.03),l(-.306,-.159),l(-.252,.07),l(-.004,.133),l(.169,.185),l(.169,.34),l(.308,.059),l(.826,.609),l(-.166,.07),l(-.369,-.258),l(-.015,-.105),l(-.276,-.082),l(-.331,-.105),l(-.116,.099),l(-.211,.007),l(.069,.129),l(-.016,.129),l(.338,.164),l(.145,-.012),l(.114,.234),l(-.03,.129),l(-.245,.023),l(-.445,-.457),l(-.341,-.141),l(-.207,-.059),l(-.128,-.012),l(.003,.094),l(-.075,.035),l(.138,.164),l(.102,.105),l(.154,.141),l(.193,.059),l(.153,.035),l(.103,.094),l(-.093,.058),l(-.494,-.046),l(-.253,-.035),l(.035,-.176),l(-.137,-.293),l(-.164,-.188),l(-.401,-.108),l(-.472,-.373),l(.258,-.118),l(.025,-.136),l(-.053,-.122),l(-.182,-.035),l(-.153,.199),l(-.465,.176),l(.245,.224),l(-.25,.371),l(-.05,.249),l(.13,.121),l(.065,.172),l(.311,.338),l(.133,.036),l(.131,.479),l(.579,.421),l(.359,.467),l(-.172,.14),l(-.237,.082),l(.106,-.187),l(-.121,-.187),l(-.142,-.128),l(-.139,-.035),l(-.151,-.047),l(-.29,.175),l(.102,.188),l(.153,.081),l(.08,.316),l(-.193,.187),l(-.652,.141),l(.248,.046),l(.27,.14),l(.391,.058),l(.188,.222),l(.257,-.012),l(.155,.012),l(.048,.126),l(.367,.269),l(.306,.014),l(.138,.292),l(.282,.012),l(.27,0),l(.348,.303),l(.015,.128),l(-.193,.082),l(.238,.782),l(-.153,.175),l(-.185,0),l(-.226,-.385),l(-.222,-.047),l(-.207,-.278),l(-.101,-.142),l(-.17,0),l(-.496,.14),l(-.479,.105),l(-.184,.128),l(.315,.093),l(.013,.188),l(.007,.291),l(.229,.117),l(.153,-.026),l(.225,-.079),l(-.021,.198),l(.235,.175),l(-.519,.093),l(.002,.117),l(-.169,.062),l(-.309,-.086),l(.121,-.21),l(-.186,-.086),l(-.508,-.056),l(-.158,-.092),l(-.008,.206),l(.194,.453),l(.193,.17),l(-.045,.163),l(.209,.204),l(.213,.96),l(-.688,-.31),l(-.331,.071),l(-.298,.439),l(-.442,-.735),l(-.46,-.367),l(-.452,.44),l(-.428,-.353),l(-.127,-.297),l(.212,-.425),l(-.028,-.241),l(-.215,-.269),l(-.491,-.424),l(-.167,-.226),l(.017,-.17),l(.471,-.61),l(.609,.098),l(.425,-.298),l(.202,.042),l(1.668,.663),l(.337,-.1),l(.483,-.355),l(-.266,-.049),l(-.27,-.056),l(-1.204,-.493),l(-1.127,-.083),l(-.367,.058),l(-.66,.058),l(-.427,.143),l(-.89,-1.118),l(.269,-.1),l(.253,.056),l(.218,-.114),l(.122,-.185),l(-.339,-.24),l(-.235,.114),l(-.496,-.042),l(-1.035,-.721),l(-.199,-.325),
+N(504.136,113.458),l(-.327,.328),l(-.377,.03),l(-.421,-.763),l(.071,-.236),l(-.331,-.166),l(-.118,-.023),l(-.024,-.236),l(.071,-.213),l(-.52,0),l(-.308,.118),l(-.213,-.071),l(-.378,-.142),l(-.071,-.166),l(-.236,-.236),l(-.26,.071),l(-.142,.047),l(-.709,-.061),l(-.492,-.451),l(-1.413,-.12),l(-.284,-.211),l(-.033,-.412),l(.421,-.813),l(-.026,-.199),l(-.795,-.722),l(.377,-.108),l(.307,.024),l(.308,-.095),l(.426,-.023),l(.401,-.118),l(.592,.047),l(.591,-.095),l(.378,-.047),l(.291,-.094),l(.087,.212),l(.284,.095),l(-.071,.118),l(-.071,.094),l(.284,.095),l(.426,.071),l(.212,.166),l(.119,.236),l(-.402,.142),l(-.047,.284),l(.283,.354),l(.544,.331),l(.188,.142),l(.213,.024),l(0,.236),l(-.165,.236),l(-.378,0),l(-.166,0),l(.048,.094),l(.283,.142),l(.26,.354),l(.236,.024),l(.213,.212),l(.284,.118),l(.354,.024),l(.283,.047),l(-.095,.189),l(-.236,.118),l(.236,.236),l(.261,.189),l(-.166,.047),l(-.26,.095),l(.189,.071),l(.094,.213),l(-.094,.118),l(.014,.027),
+N(566.651,117.4),l(-.565,-.153),l(-.496,-.054),l(-.264,-.151),l(-.564,.227),l(-.974,.147),l(-.137,-.059),l(.129,-.176),l(-.198,-.077),l(-.678,.03),l(-.739,.315),l(-.592,.486),l(-.589,.064),l(-.745,.495),l(-.351,.03),l(-.368,-.026),l(-.128,-.084),l(-.164,-.409),l(-.199,-.521),l(.185,-.444),l(.099,-.775),l(.029,-.255),l(-.17,-.187),l(-.484,.093),l(.156,-.597),l(-.576,-.45),l(-.153,-.056),l(-.384,.016),l(-.286,.162),l(-.134,.363),l(-.435,.428),l(-.049,.425),l(.006,.255),l(-.208,.228),l(-.442,.158),l(-.133,-.013),l(-.587,-.152),l(-.292,.058),l(-.073,.185),l(.007,.311),l(-.3,.313),l(-.21,.128),l(-.381,.016),l(-.63,-.237),l(-.325,.001),l(-.581,.286),l(-.58,.343),l(-.485,.144),l(-.245,-.041),l(-.129,-.141),l(-.04,-.055),l(.375,-.843),l(-.063,-.377),l(.377,-.377),l(.283,-.503),l(.503,-.346),l(.157,-.22),l(-.126,-.283),l(-.471,-.314),l(-.126,-.409),l(.031,-.472),l(.126,-.22),l(-.126,-.22),l(-.346,0),l(-.44,-.032),l(-.157,-.157),l(-.346,-.188),l(-.251,-.283),l(.125,-.471),l(.472,-.283),l(.629,.126),l(1.069,.094),l(.282,-.188),l(.063,-.409),l(.251,-.157),l(.188,0),l(.032,-.346),l(.188,-.063),l(.251,-.063),l(-.126,-.094),l(-.534,.031),l(-.251,-.125),l(.503,-.094),l(.565,-.095),l(.472,.095),l(.062,-.126),l(.032,-.346),l(-.189,-.314),l(.283,-.314),l(-.031,-.22),l(.346,0),l(.22,.157),l(.252,.22),l(.22,-.157),l(.692,-.188),l(.471,-.346),l(.221,-.251),l(.439,.377),l(.252,.251),l(-.188,.22),l(-.409,.346),l(-.251,.126),l(.126,.188),l(.251,.063),l(.188,.283),l(.409,-.031),l(.345,.005),l(-.302,.147),l(-.441,.196),l(-.024,.246),l(-.196,0),l(-.099,-.172),l(-.516,-.196),l(-.515,-.147),l(-.981,.196),l(-.123,0),l(-.098,.27),l(.049,.196),l(-.196,.074),l(-.098,-.221),l(-.196,.196),l(-.099,.245),l(.099,.27),l(.049,.147),l(.122,.098),l(.589,-.024),l(.564,0),l(.688,-.024),l(.196,.073),l(.49,-.147),l(.368,.098),l(.147,.123),l(.073,.196),l(.393,.074),l(.295,-.049),l(-.024,-.221),l(.343,-.049),l(.442,-.123),l(.221,.024),l(.049,.147),l(.319,.123),l(.122,-.024),l(.049,.172),l(.074,.221),l(.318,-.172),l(.368,-.024),l(.172,.319),l(.196,0),l(.172,-.147),l(.834,-.221),l(.663,.073),l(.539,0),l(.344,-.171),l(.344,-.098),l(.196,.073),l(-.011,.069),l(.023,1.031),l(-.207,.223),l(.077,.305),l(.325,.394),l(.463,-.045),l(.229,-.162),l(.22,.06),l(.692,.039),l(.273,.154),l(.295,.494),l(-.009,.284),l(.028,.246),l(.152,.012),l(.049,.123),l(-.126,.428),l(.245,.237),l(-.152,.36),l(.2,.163),l(-.181,.185),l(-.08,.249),l(-.354,.136),
+N(500.121,117.572),l(-.023,-.308),l(-.275,-.035),l(-.42,-.374),l(-.099,-.421),l(.145,-.245),l(-.424,-.111),l(-.213,-.14),l(.06,-.128),l(.287,-.535),l(.229,-.281),l(-.039,-.207),l(-.288,-.203),l(-.106,.105),l(-.09,-.117),l(-.224,-.492),l(.122,-.375),l(-.264,-.67),l(.039,-.27),l(.584,-.267),l(.009,-.433),l(.231,-.141),l(.409,.023),l(.072,-.211),l(.252,.197),l(.265,.381),l(.281,.417),l(.551,.223),l(.294,.402),l(.335,.211),l(.421,.125),l(.512,.025),l(.416,-.038),l(.253,.133),l(.009,-.001),l(.377,-.03),l(.327,-.328),l(.254,-.255),l(.642,-.188),l(.374,-.445),l(1.058,-.492),l(.456,-.234),l(.306,.166),l(.351,.281),l(.487,.21),l(.061,.113),l(-.094,.155),l(-.63,.129),l(.027,.188),l(.43,.176),l(-.095,.222),l(-.532,.27),l(.13,.363),l(.374,.105),l(.356,.433),l(.268,-.016),l(.72,.317),l(.015,.007),l(-.05,.707),l(-.143,.581),l(.205,.48),l(.494,.252),l(.925,.235),l(.827,.052),l(.424,.097),l(.162,.282),l(.312,.451),l(.687,.463),l(1.902,.513),l(.841,.052),l(.438,-.059),l(1.354,-.262),l(1.192,-.148),l(1.469,-.079),l(.41,-.229),l(.185,-.354),l(-.131,-.905),l(.015,0),l(.441,.025),l(.562,-.13),l(.466,-.3),l(.621,-.654),l(.476,-.328),l(.413,-.144),l(.609,-.032),l(.78,.038),l(.333,-.03),l(.517,-.343),l(.343,-.129),l(.519,.054),l(1.055,.107),l(.402,.437),l(.708,.251),l(.822,.335),l(.788,.066),l(.5,.054),l(.66,.167),l(.597,.336),l(1.803,1.362),l(.655,.506),l(.696,.01),l(.787,-.075),l(.185,.211),l(.038,.848),l(.01,.565),l(.15,.692),l(.078,.028),l(-.145,.241),l(-.084,.339),l(-.246,.807),l(-.49,1.272),l(-.222,.297),l(-.596,.384),l(-.016,.141),l(.119,.663),l(.096,.098),l(.738,.235),l(.026,.183),l(-.661,.935),l(-.034,.155),l(.254,1.085),l(.167,1.283),l(.143,.775),l(.191,.21),l(.209,.041),l(1.198,.275),l(.401,.167),l(.144,.366),l(.046,.437),l(-.425,.553),l(-.853,.795),l(-.853,1.034),l(.802,1.083),l(.71,1.068),l(.353,.464),l(.695,.391),l(1.144,.388),l(.409,.224),l(.168,.38),l(.111,1.34),l(.185,.394),l(.652,.053),l(.186,.281),l(-.036,.974),l(-.188,.255),l(-.209,.072),l(-1,.077),l(-.697,.258),l(-.794,.47),l(-.285,.383),l(-.31,.792),l(-.049,.354),l(-.182,.954),l(-.502,.028),l(-1.079,-.153),l(-.236,-.197),l(-.605,-.253),l(-.403,-.056),l(-1.43,.003),l(-.783,-.041),l(-.602,.072),l(-.475,-.38),l(-.163,-.126),l(-.835,-.026),l(-.576,.001),l(-.465,.014),l(-.212,-.239),l(-.756,-.125),l(-.305,-.183),l(-.162,-.014),l(-.021,-.5),l(-.295,-.128),l(-.103,-.514),l(-.292,-.349),l(-.013,-.639),l(-.309,-.493),l(-.237,.012),l(-.035,-.181),l(-.526,-.126),l(-.807,-.013),l(-.374,.017),l(-.209,.222),l(-.329,.018),l(-.517,.075),l(-.188,.364),l(-.538,.138),l(-.383,.443),l(-.368,.283),l(-.253,.043),l(-1.292,-.689),l(-.958,-.104),l(-.562,-.359),l(-1.088,-.317),l(-.247,-.301),l(-.324,-.282),l(-.497,-.592),l(-.997,-.436),l(-.584,-.083),l(-.194,-.028),l(-.58,-.465),l(-.596,-1.058),l(-.635,-1.114),l(-.209,-.268),l(.005,-.593),l(-.767,-.761),l(-.506,-.719),l(-.921,.143),l(-.46,-.042),l(-.13,-.126),l(-.291,-.056),l(-.191,-.268),l(-.029,-.565),l(-.448,.1),l(-.166,.099),l(-.32,.678),l(-.195,.184),l(-.355,.012),l(-.014,-.12),l(-.351,-.224),l(-.686,-.546),l(.064,-.212),l(-.007,-.395),l(-.164,-.465),l(-.215,-.013),l(-.551,.003),l(-.034,-.325),l(.055,-.579),l(.197,-.622),l(.014,-.508),l(-.112,-.239),l(-.29,-.28),l(-.774,-.603),l(-.436,-.209),l(-1.242,-.925),l(-.533,-.025),l(-.321,.115),L(503,127.106),l(.033,-.819),l(-1.02,-.954),l(-.312,-.351),l(-.002,-.184),l(.133,-.875),l(.235,-.763),l(1.142,-.98),l(-.422,-.761),l(.013,-.254),l(.468,-.596),l(-1.067,-.107),l(-.761,-.208),l(-.065,-.198),l(-.563,-1.086),l(-.69,-1.397),
+N(535.734,133.791),l(.853,-1.034),l(.853,-.795),l(.425,-.553),l(-.046,-.437),l(-.144,-.366),l(-.401,-.167),l(-1.198,-.275),l(-.209,-.041),l(-.191,-.21),l(-.143,-.775),l(-.167,-1.283),l(-.254,-1.085),l(.034,-.155),l(.661,-.935),l(-.026,-.183),l(-.738,-.235),l(-.096,-.098),l(-.119,-.663),l(.016,-.141),l(.596,-.384),l(.222,-.297),l(.49,-1.272),l(.246,-.807),l(.084,-.339),l(.145,-.241),l(.193,.112),l(.937,.193),l(.999,.348),l(.573,.082),l(.534,-.031),l(.636,-.3),l(.22,-.199),l(.001,-.466),l(.119,-.199),l(.568,-.159),l(1.985,-.689),l(.508,-.243),l(.103,-.128),l(.203,-.991),l(.432,-.809),l(1.135,-.205),l(.417,-.2),l(.445,-.356),l(.565,.294),l(1.454,.244),l(.096,.016),l(.646,.011),l(1.217,.05),l(.236,.126),l(.351,.48),l(.04,.055),l(.129,.141),l(.245,.041),l(.485,-.144),l(.58,-.343),l(.581,-.286),l(.325,-.001),l(.63,.237),l(.381,-.016),l(.21,-.128),l(.3,-.313),l(-.007,-.311),l(.073,-.185),l(.292,-.058),l(.587,.152),l(.133,.013),l(.442,-.158),l(.208,-.228),l(-.006,-.255),l(.049,-.425),l(.435,-.428),l(.134,-.363),l(.286,-.162),l(.384,-.016),l(.153,.056),l(.576,.45),l(-.156,.597),l(.484,-.093),l(.17,.187),l(-.029,.255),l(-.099,.775),l(-.185,.444),l(.199,.521),l(.164,.409),l(.128,.084),l(.368,.026),l(.351,-.03),l(.745,-.495),l(.589,-.064),l(.592,-.486),l(.739,-.315),l(.678,-.03),l(.198,.077),l(-.129,.176),l(.137,.059),l(.974,-.147),l(.564,-.227),l(.264,.151),l(.496,.054),l(.565,.153),l(-.192,.145),l(-.574,-.059),l(.01,.269),l(.236,.012),l(.048,.088),l(-.148,.142),l(-.358,.004),l(-.455,.297),l(-.332,-.005),l(-.338,.179),l(-.647,-.144),l(-1.345,.012),l(-1.148,.152),l(-.53,.292),l(-.272,.19),l(-.559,.395),l(-.246,-.023),l(-.258,.214),l(-.464,.413),l(.01,.32),l(.411,.271),l(.01,.336),l(.232,.171),l(-.119,.483),l(.198,.477),l(-.324,.426),l(-.524,.355),l(-.4,.341),l(-.13,.283),l(.223,.478),l(.033,.31),l(-.289,.255),l(-.513,.215),l(-.698,-.039),l(-.997,-.122),l(-.355,.129),l(.35,.336),l(.365,.407),l(.129,.281),l(.088,.437),l(-.199,.255),l(-.315,.115),l(-.513,.031),l(-.416,.115),l(-.292,.228),l(-.224,.424),l(-.288,.834),l(-.139,1.214),l(-.021,.084),l(-.34,.383),l(-.237,.086),l(-1.001,-.375),l(-.562,-.025),l(-.559,.243),l(-.362,.271),l(-.321,.693),l(-.254,.086),l(-.516,-.082),l(-.644,-.039),l(-.283,.072),l(-.597,.441),l(-.412,.369),l(-.188,.34),l(-.232,.876),l(-.099,.903),l(-.069,.184),l(-.247,.156),l(-1.066,.274),l(-1.183,.19),l(-.964,.175),l(-1.234,.12),l(-1.005,-.135),l(-.349,.002),l(-1.187,.218),l(-.742,-.024),l(-.541,-.039),l(-.854,-.235),l(-1.069,-.248),l(-.63,-.194),l(-.887,-.32),
+N(486.696,126.295),l(5.257,-2.711),l(.589,-2.701),l(-.024,-.467),l(-.187,-.508),l(.009,-.255),l(.23,-.355),l(.31,-.214),l(.866,-.174),l(.457,-.371),l(.944,-.883),l(-.059,-.24),l(.23,-.043),l(.266,-.143),l(.437,-.094),l(.304,.16),l(.364,-.032),l(.811,.153),l(.343,.023),l(.244,-.234),l(.366,.082),l(.274,.151),l(-.03,.259),l(.102,.269),l(.318,.105),l(.164,-.126),l(.433,-.388),l(.407,.016),l(.69,1.397),l(.563,1.086),l(.065,.198),l(.761,.208),l(1.067,.107),l(-.468,.596),l(-.013,.254),l(.422,.761),l(-1.142,.98),l(-.235,.763),l(-.133,.875),l(.002,.184),l(.312,.351),l(1.02,.954),L(503,127.106),l(.075,.155),l(.321,-.115),l(.533,.025),l(1.242,.925),l(.436,.209),l(.774,.603),l(.29,.28),l(.112,.239),l(-.014,.508),l(-.197,.622),l(-.055,.579),l(.034,.325),l(.551,-.003),l(.215,.013),l(.164,.465),l(.007,.395),l(-.064,.212),l(.686,.546),l(.351,.224),l(.014,.12),l(-.096,.003),l(-.664,.101),l(-.408,-.056),l(-.157,.057),l(-.103,.127),l(-1.271,.044),l(-.518,.13),l(-.343,.693),l(-.463,.609),l(-.521,.568),l(-4.048,-.132),l(-1.557,-.697),l(-.812,-.277),l(-.118,-.253),l(-.047,-.818),l(.118,-.396),l(-.135,-.366),l(-.973,.048),l(-.141,-.07),l(-.399,-.633),l(-.258,-.196),l(-2.44,-1.101),l(-1.14,-.473),l(-2.034,-.934),l(-.757,-.222),l(-1.129,-.459),l(-.093,-.056),l(-.093,-.056),l(-.311,-.69),l(-.87,-1.632),
+N(479.916,127.377),l(-.082,-.085),l(.047,-.122),l(-.021,-.183),l(-.201,-.128),l(-.183,-.346),l(.398,-.209),l(.041,-.099),l(.526,-.396),l(-.048,-.058),l(-.223,-.099),l(.077,-.151),l(.298,-.25),l(.599,-.006),l(-.14,-.146),l(-.035,-.046),l(.078,-.111),l(.177,-.163),l(.169,-.116),l(.299,-.239),l(-.068,-.058),l(.023,-.163),l(-.09,-.047),l(-.031,-.221),l(-.241,-.157),l(-.222,-.058),l(.204,-.204),l(-.125,-.052),l(-.053,-.116),l(-.12,.058),l(-.335,.052),l(-.388,-.023),l(-.225,-.564),l(.129,-.593),l(.072,-.064),l(-.1,-.507),l(-.42,-.326),l(.126,-.093),l(.036,-.152),l(.117,-.128),l(-.093,-.222),l(.107,-.012),l(.259,-.32),l(-.061,-.112),l(.311,-.001),l(.39,.125),l(.114,-.127),l(.171,.041),l(.2,-.169),l(.156,-.374),l(.627,-.127),l(-.243,-.423),l(-.102,-.452),l(.055,-.319),l(.142,.035),l(.222,-.001),l(.777,.166),l(.575,.025),l(.746,-.075),l(.929,-.373),l(.278,-.03),l(1.532,.444),l(.556,.153),l(1.104,-.176),l(.567,-.13),l(1.498,-.673),l(.439,-.073),l(.747,.123),l(.711,.039),l(1.185,-.224),l(.693,-.304),l(.24,-.292),l(.207,.023),l(.064,.245),l(.059,.24),l(-.944,.883),l(-.457,.371),l(-.866,.174),l(-.31,.214),l(-.23,.355),l(-.009,.255),l(.187,.508),l(.024,.467),l(-.589,2.701),l(-5.257,2.711),l(-.161,.071),l(-2.96,1.541),l(-1.139,.656),l(-.253,.016),l(-.365,-.167),l(-1.902,-1.034),
+N(426.068,126.434),l(-.093,.981),l(.064,.564),l(-.093,.269),l(-.802,.428),l(-.579,.314),l(-1.473,1.138),l(-.126,.354),l(.274,.973),l(-.147,.537),l(-.155,.227),l(-.864,.598),l(-.22,.143),l(-.564,-1.536),l(-.699,-2.242),l(-.323,-.464),l(-.363,-.252),l(-.432,-.181),l(-.484,-.831),l(-.225,-.465),l(-.363,-.28),l(-.452,-.097),l(-.336,-.774),l(-.301,-.888),l(.112,-.509),l(1,-.853),l(.414,-.355),l(.163,-.411),l(.048,-.537),l(-.052,-.594),l(-.026,-.892),l(-.012,-1.429),l(.114,-.439),l(.685,-.627),l(.012,-.184),l(.508,-.185),l(.633,-.455),l(.591,-.228),l(.703,-.016),l(.643,.183),l(.247,.212),l(.059,.241),l(.25,.538),l(.27,.084),l(.417,-.171),l(.584,-.44),l(.401,-.17),l(.034,.354),l(-.265,.567),l(-.638,.511),l(-.275,.468),l(.005,.283),l(.202,.438),l(.508,.466),l(.351,.127),l(.303,.848),l(-.094,.212),l(-.541,.764),l(-.59,.34),l(-1.017,.92),l(-.216,.339),l(.287,.451),l(.587,.55),l(.528,.295),l(.284,.056),l(.396,-.227),l(.316,.084),l(.244,.635),l(.582,.239),
+N(381.402,139.704),l(-.027,-.876),l(.069,-2.006),l(.037,-.382),l(.686,-.314),l(1.512,-.998),l(.963,-.542),l(1.265,.078),l(.397,-.059),l(.181,-.693),l(.864,-.033),l(.777,-.174),l(.527,-.229),l(.524,-.356),l(.484,-.652),l(1.109,-.332),l(1.52,-.701),l(.129,-.227),l(-.296,-.62),l(-.025,-.396),l(.079,-.227),l(.265,-.114),l(1.186,-.12),l(.381,-.186),l(.309,-.553),l(1.022,.022),l(.67,-.018),l(1.826,.004),l(.34,-1.033),l(-.07,-.211),l(-.507,-.322),L(397,126.646),l(-.158,-.465),l(.016,-1.271),l(.022,-.833),l(-.165,-.889),l(-.189,-.211),l(-.563,-.279),l(-.259,-.508),l(.351,0),l(.66,-.143),l(.541,-.256),l(.369,-.566),l(.405,-.312),l(.509,-.086),l(.407,-.157),l(.679,-.27),l(.324,.226),l(.176,.017),l(.249,.024),l(.238,-.142),l(.407,-.51),l(.613,-.426),l(.682,-.355),l(.614,-.171),l(1.16,-.116),l(1.587,-.06),l(.513,-.072),l(.634,-.312),l(.578,.211),l(.564,-.072),l(.585,-.313),l(.343,-.1),l(.939,.012),l(.513,-.015),l(.307,.056),l(.221,.042),l(.322,.113),l(.816,.168),l(.529,-.015),l(.772,-.171),l(.705,-.2),l(.612,-.554),l(.994,.508),l(.339,.099),l(.312,-.143),l(.314,-.241),l(.228,-.156),l(.528,.042),l(.388,.197),l(.162,.269),l(.269,.126),l(.516,-.086),l(1.093,-.158),l(-.012,.184),l(-.685,.627),l(-.114,.439),l(.012,1.429),l(.026,.892),l(.052,.594),l(-.048,.537),l(-.163,.411),l(-.414,.355),l(-1,.853),l(-.112,.509),l(.301,.888),l(.336,.774),l(.452,.097),l(.363,.28),l(.225,.465),l(.484,.831),l(.432,.181),l(.363,.252),l(.323,.464),l(.699,2.242),l(.564,1.536),l(-.204,.156),l(-.241,.383),l(.88,1.605),l(.147,.833),l(.052,.691),l(-.1,.862),l(.101,.748),l(-.16,.622),l(-.158,.495),l(.457,1.156),l(-.061,.664),l(-.086,.17),l(-.666,.47),l(-.249,.128),l(-.152,.283),l(1.272,1.702),l(.249,.917),l(.562,.873),l(.244,.154),l(.544,-.201),l(.702,.165),l(1.028,.347),l(.178,.168),l(.86,1.506),l(.098,.07),l(-.265,.186),l(-1.632,.843),l(-4.012,2.241),l(-1.607,.956),l(-2.308,1.454),l(-.834,.655),l(-3.084,2.617),l(-1.82,.364),l(-1.672,.321),l(-2.176,.408),l(-.146,-.564),l(.161,-.679),l(-.099,-.522),l(-.277,-.352),l(-.309,-.111),l(-.748,-.024),l(-.375,-.167),l(-.588,-.562),l(-.47,.314),l(-.229,-.027),l(-1.111,-1.039),l(-.393,-.28),l(-.082,-.183),l(.096,-.396),l(-.181,-.253),l(-2.472,-1.469),l(-.397,-.253),l(-1.292,-.824),l(-1.924,-1.26),l(-3.283,-2.241),l(-.811,-.575),l(-2.054,-1.344),l(-.895,-.531),l(-.1,-.084),l(-1.414,-.91),l(-4.12,-2.42),l(-2.829,-1.509),
+N(395.704,122.189),l(.259,.508),l(.563,.279),l(.189,.211),l(.165,.889),l(-.022,.833),l(-.016,1.271),l(.158,.465),l(.598,.788),l(.507,.322),l(.07,.211),l(-.34,1.033),l(-1.826,-.004),l(-.67,.018),l(-1.022,-.022),l(-.309,.553),l(-.381,.186),l(-1.186,.12),l(-.265,.114),l(-.079,.227),l(.025,.396),l(.296,.62),l(-.129,.227),l(-1.52,.701),l(-1.109,.332),l(-.484,.652),l(-.524,.356),l(-.527,.229),l(-.777,.174),l(-.864,.033),l(-.181,.693),l(-.397,.059),l(-1.265,-.078),l(-.963,.542),l(-1.512,.998),l(-.686,.314),l(-.037,.382),l(-.069,2.006),l(-1.42,.008),l(-1.718,-.004),l(-2.677,.001),l(-2.511,0),l(-1.635,.04),l(.141,-.28),l(.431,-.411),l(.427,-.085),l(1.296,-.285),l(1.143,-.455),l(.453,-.312),l(1.147,-.85),l(1.149,-.878),l(1.043,-1.104),l(.46,-.693),l(.133,-.509),l(-.05,-.494),l(-.427,-.776),l(-.09,-.678),l(.099,-.508),l(.396,-.636),l(.706,-.863),l(.211,-.65),l(-.063,-.367),l(.071,-.353),l(1.285,-1.203),l(.724,-.481),l(.916,-.327),l(1.266,-.469),l(.73,-.397),l(.558,-.552),l(.537,-.736),l(.466,-.905),l(.829,-1.925),l(.269,-.128),l(.54,-.171),l(.19,.127),l(.684,.848),l(.138,.099),l(1.148,.507),l(.661,-.001),l(.595,.042),l(1.304,-.074),l(.522,-.228),l(.437,-.27),l(.398,.551),l(.256,.099),l(.798,.097),l(.361,0),
+N(480.248,123.437),l(.388,.023),l(.335,-.052),l(.12,-.058),l(.053,.116),l(.125,.052),l(-.204,.204),l(.222,.058),l(.241,.157),l(.031,.221),l(.09,.047),l(-.023,.163),l(.068,.058),l(-.299,.239),l(-.169,.116),l(-.177,.163),l(-.078,.111),l(.035,.046),l(.14,.146),l(-.599,.006),l(-.298,.25),l(-.077,.151),l(.223,.099),l(.048,.058),l(-.526,.396),l(-.041,.099),l(-.398,.209),l(-.07,-.023),l(-.088,.041),l(-.067,.193),l(-.009,.167),l(-.355,.07),l(-.07,-.099),l(-.151,-.022),l(-.372,.051),l(.26,-.291),l(.097,-.361),l(.169,-.227),l(.328,-.681),l(-.017,-.232),l(.181,0),l(.138,-.192),l(.072,-.32),l(.018,-.32),l(.409,-.431),l(.232,-.07),l(.116,-.174),l(-.048,-.157),
+N(184.444,142.729),l(-.367,.82),l(-.518,.821),l(-.186,.763),l(-.179,1.159),l(.017,1.851),l(-.133,1.187),l(-.016,1.13),l(.564,1.737),l(.275,.805),l(.624,.945),l(.76,.903),l(.191,.452),l(.481,.521),l(.529,.974),l(.729,1.228),l(.375,.296),l(.677,.069),l(.436,-.015),l(.577,.154),l(.593,.451),l(.503,.508),l(.773,.069),l(1.016,-.242),l(1.55,-.456),l(1.396,-.3),l(.803,-.157),l(-.02,.542),l(.838,.223),l(.264,-.286),l(.293,-.199),l(-.104,-.247),l(-.393,-.175),l(1.073,-.62),l(.633,-.62),l(.086,-.827),l(.498,-.429),l(-.094,-.477),l(.092,-1.145),l(.254,-.699),l(.625,-.334),l(.164,-.043),l(.757,-.198),l(.701,-.1),l(1.088,-.229),l(1.016,-.37),l(.594,-.058),l(.499,.056),l(1.139,.181),l(.502,-.194),l(.378,.093),l(.62,.507),l(.047,.297),l(-.079,.424),l(-.298,.382),l(-.541,.496),l(-.433,.425),l(-.317,.445),l(-.02,.7),l(-.254,.297),l(-.188,.354),l(.155,.155),l(.337,-.138),l(-.101,.652),l(-.262,1.196),L(205.356,159),l(-.062,.24),l(-.34,-.534),l(-.167,-.452),l(-.072,-.155),l(-.386,.34),l(-.02,.549),l(-.437,.016),l(-.178,.447),l(-.599,.857),l(-.386,-.27),l(-.278,.095),l(.025,.329),l(-2.332,-.006),l(-1.792,-.005),l(-.04,1.24),l(-.999,.032),l(.396,.223),l(.495,.541),l(.624,.231),l(.359,.69),l(.532,.223),l(-.211,.683),l(-1.762,-.007),l(-1.06,.007),l(-1.076,1.812),l(.305,.397),l(-.207,.238),l(.054,.553),l(.044,.454),l(-.704,-.555),l(-.952,-.888),l(-.956,-.761),l(-1.069,-.859),l(-.534,-.352),l(-.053,-.071),l(-.639,-.252),l(-1.048,-.21),l(-.657,.044),l(-.817,.397),l(-1.1,.567),l(-.756,.256),l(-.931,-.069),l(-.724,-.21),l(-.48,-.197),l(-1.305,-.195),l(-.588,-.267),l(-.644,-.422),l(-.935,-.521),l(-.785,-.267),l(-1.711,-.392),l(-.963,-.365),l(-.722,-.366),l(-1.074,-.436),l(-.592,-.352),l(-1.123,-1),l(-.207,-.07),l(-.606,.058),l(-.689,-.14),l(-1.835,-.575),l(-.565,-.536),l(-.503,-.634),l(-.495,-.395),l(-1.049,-.577),l(-.619,-.267),l(-.5,-.494),l(-.742,-.987),l(-.363,-.55),l(-.038,-.113),l(.15,-.155),l(.504,-.086),l(.18,-.17),l(.047,-.184),l(-.331,-.367),l(.457,-.679),l(.041,-.381),l(-.172,-.466),l(-.744,-.959),l(.121,-.297),l(.146,-.17),l(-.07,-.268),l(-.665,-.62),l(-1.495,-1.777),l(-.546,-.493),l(-.963,-1.058),l(-.474,-.522),l(-.815,-.578),l(-.322,-.197),l(-.158,-.268),l(-.058,-.48),l(-.144,-.183),l(-.329,-.197),l(-.609,-.197),l(-.408,-.31),l(-.366,-.522),l(-.271,-.028),l(-.414,.114),l(-.238,-.155),l(-.163,-.367),l(-.005,-.325),l(.459,-.736),l(-.126,-.339),l(-.751,-.62),l(-.439,.255),l(-.375,-.621),l(-.118,-.353),l(-.359,-.211),l(-.61,-.168),l(-.319,-.296),l(-.125,-.254),l(.05,-.381),l(.084,-.269),l(-.185,-.226),l(-.561,-.21),l(-.46,-.098),l(-.46,-.253),l(-.935,-.86),l(-.478,-.706),l(-.281,-.551),l(-.646,-.832),l(-.736,-1.073),l(-.184,-.423),l(-.38,-.678),l(-.242,-.338),l(-.152,-.452),l(.042,-.509),l(.032,-.311),l(-.56,-.239),l(-.795,-.196),l(-.06,-.452),l(-.128,-.155),l(-.458,-.183),l(-.289,.326),l(-.251,.043),l(-1.43,-.647),l(-.285,1.004),l(-.045,.438),l(.033,.084),l(.265,.339),l(.264,.296),l(.028,1.046),l(.088,.509),l(.51,.677),l(.143,.169),l(.643,.267),l(.601,.536),l(.525,.663),l(.602,1.214),l(.44,.282),l(.328,.042),l(.237,.169),l(.325,1.398),l(.102,.169),l(.246,.155),l(.497,.056),l(.133,.056),l(.215,.438),l(.161,.65),l(.445,.79),l(.49,-.071),l(.223,-.142),l(.245,.452),l(.344,1.469),l(.531,1.059),l(.649,1.2),l(.069,.593),l(-.014,.522),l(.26,.353),l(.378,.154),l(.389,-.17),l(.234,-.198),l(.588,.804),l(.258,.579),l(.464,.253),l(.281,.014),l(.133,.311),l(-.196,.537),l(-.136,.127),l(-.691,.595),l(-.254,-.042),l(-.251,-.409),l(-.24,-.734),l(-.617,-.578),l(-.625,-.309),l(-.516,-.479),l(-.834,-.507),l(-1.143,-.986),l(-.416,-.451),l(-.162,-.269),l(.216,-.989),l(-.035,-.254),l(-.488,-1.002),l(-.238,-.381),l(-.327,-.282),l(-.44,-.098),l(-.5,-.31),l(-.675,-.677),l(-.305,.142),l(-.363,-.056),l(-1.262,-.746),l(-.722,-.31),l(-.896,-.973),l(-.139,-.127),l(-.246,-.254),l(.679,.15),l(.599,.013),l(.588,-.284),l(.244,-.326),l(.093,-.636),l(-.01,-.184),l(-.458,-.635),l(-.466,-.452),l(-1.1,-.888),l(-.986,-.493),l(-.402,-.338),l(-.203,-.522),l(-.272,-.649),l(-.091,-.155),l(-.447,-.126),l(-.15,-.353),l(-.026,-.594),l(-.203,-.395),l(-.623,-.734),l(-.434,-.706),l(-.003,-.254),l(.212,-.382),l(-.777,-.62),l(-.254,-.325),l(-.22,-.485),l(.34,-.017),l(2.367,-.155),l(2.381,-.084),l(.316,.31),l(.267,.154),l(1.186,.39),l(2.811,.933),l(3.516,1.112),l(.338,.055),l(1.662,.019),l(1.544,.02),l(.966,.037),l(1.867,-.011),l(.213,-.101),l(.096,-.892),l(1.858,.003),l(1.892,.046),l(.209,.112),l(.631,.662),l(.766,.632),l(.837,.519),l(.708,.491),l(.179,.226),l(.284,.678),l(.318,.847),l(.445,.549),l(1.092,.659),l(1.104,.503),l(.337,.069),l(.501,.011),l(.416,-.158),l(.283,-.37),l(.418,-.413),l(.576,-.541),l(.468,-.201),l(.643,-.018),l(.475,.082),l(.783,.321),l(.412,.252),l(.363,.366),l(.663,1.029),l(.744,1.227),l(.845,1.042),l(.657,.576),l(.268,.253),l(.078,.467),l(.332,.932),l(.336,.592),l(.375,.365),l(.921,.32),l(1.029,.56),l(.264,.069),l(.416,-.116),l(.296,-.001),l(.816,.377),l(.347,.119),
+N(507.047,133.665),l(.055,.197),l(.134,.691),l(-.336,-.028),l(-.513,.513),l(.421,.194),l(.418,-.206),l(.306,.021),l(.698,1.84),l(-.644,.044),l(-1.07,-.05),l(-.185,-.239),l(-.334,-.619),l(-.408,-.054),l(-1.657,-.259),l(.521,-.568),l(.463,-.609),l(.343,-.693),l(.518,-.13),l(1.271,-.044),
+N(606.155,150.953),l(.595,.152),l(.255,.14),l(.25,-.129),l(.273,-.368),l(.015,-.678),l(-.152,-.93),l(.228,-.185),l(.401,-.144),l(.191,-.354),l(-.146,-1.594),l(.133,-.283),l(.811,.32),l(.391,.11),l(.309,.013),l(.17,-.128),l(1.148,-2.25),l(0,-.324),l(-.192,-.408),l(.045,-.212),l(.938,-1.134),l(.136,-.382),l(-.057,-.761),l(.197,-.354),l(1.446,-.883),l(.719,-.512),l(.312,-.129),l(.558,.082),l(.853,.221),l(.295,-.058),l(-.184,-.718),l(.072,-.283),l(.596,-.582),l(.112,-.24),l(.018,-.508),l(.001,-.127),l(.306,-.34),l(.277,-.044),l(.504,.279),l(.397,.435),l(.243,.901),l(.217,.309),l(.287,.041),l(.504,-.031),l(.146,.14),l(.195,1.649),l(.02,.875),l(-.353,.862),l(-.429,.722),l(-.611,.525),l(-.487,.271),l(-.191,.198),l(-.617,.85),l(.075,.465),l(.163,.705),l(-.224,.58),l(0,.268),l(.216,.069),l(.312,-.086),l(.819,-.442),l(.771,-.089),l(.479,-.017),l(.156,.126),l(.277,1.762),l(.202,.324),l(.554,-.074),l(.521,.096),l(.033,.268),l(-.729,1.584),l(.117,.352),l(.228,.098),l(.507,-.003),l(.481,-.045),l(.291,.379),l(.341,.746),l(.378,.266),l(.246,.083),l(.647,-.159),l(.628,-.413),l(.111,.38),l(.153,.239),l(-.502,.355),l(-.53,.61),l(-.442,.581),l(-.582,.455),l(-.193,.185),l(-.08,.085),l(-.158,.071),l(-.645,.06),l(-.436,.172),l(-.528,.342),l(-.394,.595),l(-1.078,.316),l(-.62,.018),l(-.474,-.082),l(-.362,.411),l(-.143,.368),l(-.036,.819),l(-.114,.509),l(.064,.409),l(-.086,.24),l(-.163,.001),l(-.588,.131),l(.739,.884),l(.069,.183),l(.112,.875),l(.254,.14),l(1.091,.953),l(.148,.324),l(.646,1.041),l(.163,.338),l(-.194,.241),l(-.451,.229),l(-.128,.226),l(.231,1.185),l(-.171,.198),l(-.812,.428),l(.178,.38),l(.6,1.436),l(.54,.477),l(.606,.604),l(.203,.479),l(.088,.663),l(-.086,.636),l(.006,.254),l(.488,1.183),l(.586,1.225),l(-.077,.297),l(-1.011,1.559),l(-1.01,1.7),l(-.098,.374),l(-.359,-.181),l(-.075,-.805),l(.461,-.665),l(.174,-.495),l(.122,-.777),l(.287,-.466),l(-.512,-.027),l(-.104,-.084),l(-.004,-.282),l(.195,-.509),l(-.177,-1.524),l(-.246,-.832),l(-.639,-1.185),l(-.488,-1.312),l(-.347,-.846),l(-.179,-.875),l(-.174,-1.736),l(-.117,-.677),l(-.034,-.564),l(-.051,-.212),l(-.344,-.084),l(-.148,-.098),l(-.304,-.917),l(-.516,-.677),l(-.226,-.225),l(-.247,.029),l(-.081,.988),l(-.158,.424),l(-.43,.41),l(-.59,.284),l(-1.089,.511),l(-.359,.622),l(-.298,.297),l(-.196,.142),l(-.237,-.282),l(-.007,-.438),l(-.212,.015),l(-.338,.354),l(-.321,-.013),l(-.166,-.211),l(.147,-.495),l(-.001,-.113),l(-.621,.171),l(-.276,.127),l(-.247,.283),l(-.355,-.126),l(-.002,-.466),l(.553,-1.54),l(.162,-.791),l(.001,-.889),l(-.101,-1.059),l(-.384,-.973),l(-.431,-1.072),l(-.196,-.296),l(-.281,.537),l(-.32,-.126),l(-.526,-.366),l(.482,-.17),l(.312,-.015),l(-.149,-.479),l(-.054,-.268),l(-.684,-.775),l(-.182,-.183),l(-.19,-.028),l(-.407,.1),l(-.38,-.267),l(.086,-.438),l(-.026,-.141),l(-.209,-.112),l(-.365,.043),l(-.577,-.465),l(-.504,-.606),l(-.117,-.244),l(.252,-.341),l(.801,-.527),l(-.194,-1.607),
+N(605.297,153.429),l(-.126,-.264),l(-.269,-.55),l(-.223,-1.213),l(-.611,-1.41),l(-.357,-.395),l(-.73,.354),l(-.393,0),l(-.034,-.084),l(-.242,-.211),l(-.356,-.592),l(-.124,-.042),l(-.152,.127),l(-.026,.537),l(.374,.79),l(-.006,.424),l(-.143,.169),l(-.455,.086),l(-.235,.537),l(-.261,.1),l(-.255,-.437),l(-.311,-.395),l(-.073,-.057),l(-.163,.669),l(-.28,.249),l(-.203,.043),l(-.271,-.536),l(-.495,.636),l(-.359,-.265),l(-.147,-.532),l(-.402,-1.775),l(-.325,-1.409),l(-.352,-.45),l(-.04,-.254),l(.505,-.765),l(.029,-.269),l(-.193,-.21),l(-1.042,-.431),l(-.339,-.323),l(.548,-.61),l(.4,-.299),l(.502,-.13),l(.382,-.101),l(.047,-.155),l(-.126,-.112),l(-1.224,-.938),l(-.494,-.237),l(-.083,-.155),l(.124,-.283),l(.555,-.525),l(.234,-.171),l(1.252,.303),l(.339,.266),l(.372,.266),l(.489,-.06),l(.417,.054),l(.129,.324),l(.053,.479),l(.079,.719),l(.095,.099),l(.537,.109),l(.547,.053),l(.916,-.062),l(.559,-.003),l(2.473,.198),l(.111,.098),l(.057,.127),l(-.012,.79),l(-.159,.34),l(-.938,.767),l(-.498,.13),l(-.651,.356),l(-.131,.283),l(.009,.522),l(.001,.381),l(.23,.281),l(.249,.267),l(.529,.448),l(.224,-.354),l(.395,-1.159),l(.281,-.115),l(.4,-.044),l(.064,.578),l(.627,2.479),l(.037,.466),l(.194,1.607),l(-.801,.527),l(-.252,.341),
+N(627.408,186.411),l(-.086,.337),l(-.495,.35),l(-.11,.575),l(-.644,.089),l(-.05,-.478),l(-.309,-.163),l(-.279,.28),l(-.244,.394),l(-.204,-.083),l(-.118,-.239),l(.213,-.398),l(-.041,-.21),l(-.055,-.226),l(-.261,-.238),l(-.447,-.119),l(-.106,-.466),l(-.571,.013),l(-.448,.17),l(.013,-.104),l(.128,-.297),l(-.15,-.183),l(-.411,.212),l(-.301,-.07),l(-.38,-.38),l(-.116,-.508),l(.064,-.282),l(-.151,-.438),l(-.229,-.169),l(-.388,.043),l(-.39,-.719),l(-.209,-.508),l(-.3,-.324),l(-.311,-.155),l(-.456,-.395),l(-.343,.1),l(-.218,.142),l(-.216,-.381),l(-.04,-.607),l(.163,-.749),l(.559,-1.738),l(.29,-.848),l(-.087,-.044),l(.098,-.374),l(1.01,-1.7),l(1.011,-1.559),l(.077,-.297),l(-.586,-1.225),l(-.488,-1.183),l(-.006,-.254),l(.086,-.636),l(-.088,-.663),l(-.203,-.479),l(-.606,-.604),l(-.54,-.477),l(-.6,-1.436),l(-.178,-.38),l(.812,-.428),l(.171,-.198),l(-.231,-1.185),l(.128,-.226),l(.451,-.229),l(.194,-.241),l(-.163,-.338),l(-.646,-1.041),l(-.148,-.324),l(-1.091,-.953),l(-.254,-.14),l(-.112,-.875),l(-.069,-.183),l(-.739,-.884),l(.588,-.131),l(.163,-.001),l(.086,-.24),l(-.064,-.409),l(.114,-.509),l(.036,-.819),l(.143,-.368),l(.362,-.411),l(.474,.082),l(.62,-.018),l(1.078,-.316),l(.394,-.595),l(.528,-.342),l(.436,-.172),l(.645,-.06),l(.158,-.071),l(.08,-.085),l(.541,.166),l(.295,.182),l(.118,.168),l(.01,.423),l(-.106,.805),l(.066,.367),l(.186,.154),l(.423,-.003),l(.489,-.2),l(.414,-.045),l(.045,.113),l(.249,1.311),l(-.085,.41),l(-.528,1.569),l(-.117,.438),l(-.027,.494),l(.145,.324),l(.481,.138),l(.37,-.411),l(1.173,-1.178),l(.346,-.03),l(.835,.348),l(.59,.265),l(.223,-.072),l(.543,-.257),l(.2,-.538),l(.286,-.453),l(.403,.012),l(.893,.192),l(.266,.153),l(.052,.282),l(.285,.535),l(.688,.659),l(.435,.632),l(.058,1.524),l(.107,.366),l(.255,.464),l(.979,1.279),l(.419,.703),l(.157,.507),l(.002,.945),l(-.121,.438),l(-.808,.64),l(-.301,-.167),l(-.599,-.109),l(-.575,-.039),l(-.558,.074),l(-.798,-.066),l(-1.172,.091),l(-.383,.101),l(-.521,.441),l(-.92,1.233),l(-.146,.297),l(-.076,.382),l(.026,.635),l(.219,.648),l(.487,.717),l(.16,.479),l(-.146,.176),l(-.026,-.063),l(-.286,-.183),l(-.458,-.084),l(-.9,-.887),l(-.434,-.154),l(-.304,-.014),l(-.572,.227),l(-.391,-.112),l(-.29,-.141),l(-.337,-.014),l(0,-.282),l(.19,-1.243),l(-.107,-.184),l(-.719,-.055),l(-.248,-.084),l(-.521,.043),l(-.443,.212),l(-.244,.297),l(.207,.593),l(-.103,.339),l(-.318,.707),l(.083,.579),l(.054,.41),l(-.293,.664),l(-.583,1.187),l(-.7,1.682),l(-.255,1.314),l(.104,1.171),l(.172,.296),l(.229,.169),l(.55,-.072),l(.396,-.142),l(.252,.07),l(.135,.353),l(.009,.325),l(-.08,.466),l(.141,.282),l(.178,.211),l(.271,-.094),l(.17,.46),l(.209,.974),l(-.032,.254),l(.127,.737),l(.434,.871),l(.167,.155),l(.801,.281),l(.539,.112),l(.467,-.058),l(.294,.197),l(.266,.612),l(.664,.544),l(.212,.145),
+N(204.31,158.989),l(-.175,.412),l(.612,-.173),l(.026,.429),l(-.419,1.241),l(.178,.269),l(-.237,.795),l(.189,.318),l(-.092,.397),l(-.358,.875),l(-.3,.35),l(-.36,.032),l(-.054,.286),l(-.388,.238),l(0,.286),l(-.69,.016),l(.215,-4.297),l(-.025,-.329),l(.278,-.095),l(.386,.27),l(.599,-.857),l(.178,-.447),l(.437,-.016),
+N(200.276,169.481),l(-.151,-.056),l(-.928,-.342),l(-.614,.032),l(-.766,-.032),l(-.608,-.239),l(-.909,-.656),l(-.513,-.419),l(-.044,-.454),l(-.054,-.553),l(.207,-.238),l(-.305,-.397),l(1.076,-1.812),l(1.06,-.007),l(1.762,.007),l(.211,-.683),l(-.532,-.223),l(-.359,-.69),l(-.624,-.231),l(-.495,-.541),l(-.396,-.223),l(.999,-.032),l(.04,-1.24),l(1.792,.005),l(2.332,.006),l(-.215,4.297),l(.69,-.016),l(.303,.095),l(.311,.302),l(.14,-.191),l(-.066,-.381),l(.336,.157),l(.458,.367),l(-1.507,1.208),l(-.499,.238),l(-.177,.493),l(.162,.604),l(-.438,.302),l(-.467,.048),l(-.043,.254),l(.164,.159),l(-.351,.111),l(-.184,.302),l(-.22,-.016),l(-.565,.461),l(-.012,.223),
+N(204.413,165.093),l(.312,-.03),l(.612,-.27),l(.639,-.058),l(.743,.126),l(.478,.069),l(1.443,.04),l(.699,-.228),l(.379,-.199),l(.567,.267),l(.788,-.03),l(.763,-.101),l(.63,-.001),l(.5,.126),l(.564,.253),l(-.038,.353),l(-.102,.226),l(.228,.282),l(.787,.238),l(.557,.069),l(.244,.524),l(-1.425,.486),l(-.424,.229),l(-.248,.086),l(-.463,-.097),l(-.328,-.182),l(-.259,-.013),l(-.294,.242),l(-.503,.794),l(-1.207,.997),l(-.725,-.42),l(-.513,.583),l(-.882,.034),l(-.005,.961),l(-.293,.412),l(-.29,.143),l(-1.001,.125),l(-.311,-.661),l(-.025,-.085),l(-.478,-.3),l(.085,-.731),l(-.128,-.175),l(-.272,0),l(-.541,-.276),l(-.433,.34),l(-.365,-.016),l(-.066,-.271),l(-.565,-.35),l(-.409,-.08),l(-.208,-.418),l(-.677,-.17),l(.438,-.302),l(-.162,-.604),l(.177,-.493),l(.499,-.238),l(1.507,-1.208),
+N(205.532,170.085),l(.035,.076),l(-.203,.057),l(.01,.265),l(-.237,.334),l(-.68,-.046),l(-.853,-.139),l(-1.697,-.505),l(-1.305,-.435),l(-.325,-.21),l(.012,-.223),l(.565,-.461),l(.22,.016),l(.184,-.302),l(.351,-.111),l(-.164,-.159),l(.043,-.254),l(.467,-.048),l(.677,.17),l(.208,.418),l(.409,.08),l(.565,.35),l(.066,.271),l(.365,.016),l(.433,-.34),l(.541,.276),l(.272,0),l(.128,.175),l(-.085,.731),
+N(242.38,173.617),l(-.128,-.105),l(-.84,.171),l(-.534,.156),l(-.414,.2),l(-.056,.288),l(.048,.497),l(-.129,.396),l(-.227,-.027),l(-.381,.059),l(-.99,1.758),l(-.172,.722),l(-.241,.722),l(-.709,1.191),l(.402,.025),l(.234,-.1),l(.384,-.017),l(.31,.606),l(.855,1.45),l(.103,.395),l(-.226,1.132),l(.099,.353),l(.401,.309),l(.429,.548),l(.397,.252),l(.496,-.017),l(1.163,-.12),l(1.167,-.05),l(.521,.181),l(.64,.321),l(.188,.253),l(.847,.998),l(.554,.576),l(.144,0),l(.522,-.13),l(.76,-.174),l(1.99,-.224),l(.644,.081),l(-.409,.525),l(-.085,1.004),l(-.379,.511),l(-.147,.326),l(.026,.254),l(.035,.438),l(.048,.367),l(.162,.804),l(.447,.789),l(.256,.437),l(.486,.647),l(.121,.282),l(-.731,.612),l(-.479,.526),l(.51,.491),l(.797,1.182),l(-.52,.286),l(-.834,.57),l(-.412,.158),l(-.463,.017),l(-2.812,-.082),l(-.64,-.024),l(-.042,.325),l(-.013,1.031),l(.178,.154),l(.896,.122),l(.177,.084),l(.293,.408),l(.052,.367),l(-.384,-.04),l(-.417,-.11),l(-.687,.032),l(-.493,.187),l(-.111,.085),l(-.001,1.071),l(0,.554),l(.192,.197),l(.688,.363),l(.192,.183),l(-.031,.777),l(.399,.562),l(.031,.212),l(-.326,1.428),l(-.706,4.411),l(-.073,.382),l(-.133,.198),l(-.156,-.14),l(-.575,-.703),l(-.237,-.126),l(-.161,.058),l(-.448,.031),l(1.155,-1.956),l(.035,-.198),l(-.127,-.069),l(-1.299,-.84),l(-.509,-.209),l(-.708,.442),l(-.397,-.182),l(-.523,-.421),l(-.452,.427),l(-.337,.157),l(-.496,.031),l(-1.038,-.008),l(-.573,-.152),l(-.092,-.281),l(.004,-.396),l(-.173,-.296),l(-.479,-.039),l(-.366,-.14),l(-.078,-.282),l(-.251,-.761),l(-.988,-.672),l(-.526,-.364),l(-.208,-.62),l(-.208,-.324),l(-.513,-.435),l(-.897,-.418),l(-.927,-.107),l(-.081,-.112),l(-.269,-.162),l(-.197,-.118),l(-.709,-.631),l(-.128,-.056),l(-.89,.401),l(-.67,.061),l(-.977,-.277),l(-.355,-.309),l(-.166,-.493),l(-.227,-.225),l(-1.432,-.656),l(-1.496,-.803),l(.033,-.068),l(-.117,-.311),l(-.181,-.282),l(.108,-.212),l(.509,-.114),l(.465,.112),l(.186,-.325),l(-.348,-.564),l(.086,-.424),l(.314,-.227),l(.878,-.058),l(.193,.042),l(.41,-.227),l(.445,-.679),l(.45,-.961),l(.651,-1.061),l(-.122,-.268),l(-.56,-.408),l(-.071,-.184),l(.306,-.721),l(.089,-.523),l(-.149,-.861),l(-.371,-.86),l(.085,-.254),l(.49,-.143),l(.135,-.212),l(-.088,-.198),l(-.565,-.479),l(-.042,-.226),l(.103,-.198),l(.242,-.326),l(.036,-.254),l(-.173,-.282),l(-.739,-.719),l(-.364,-.396),l(.256,-.753),l(.228,-.082),l(-.045,-.45),l(.19,.082),l(.085,.307),l(.584,-.409),l(.094,-.43),l(.322,-.062),l(-.509,-1.038),l(-.228,-.149),l(-.084,-.327),l(.142,.075),l(.42,.338),l(.397,.507),l(.22,.508),l(.235,.197),l(.199,-.17),l(.147,-.241),l(-.332,-.776),l(.02,-.212),l(.176,-.297),l(.445,-.34),l(.399,-.297),l(.501,-.736),l(.327,-.156),l(.684,-.101),l(.217,-.382),l(-.104,-.381),l(.174,-.777),l(.067,-.65),l(.207,-.48),l(.498,-.439),l(.429,-.283),l(.592,-.242),l(.113,0),l(.374,.206),l(.22,.443),l(.281,-.34),l(.031,-.438),l(.199,-.551),l(.22,-.071),l(.267,.126),l(.855,.041),l(.562,-.001),l(.623,-.27),l(.237,-.254),l(.476,-.298),l(.848,-.256),l(.435,-.396),l(.278,-.551),l(.333,-.255),l(.469,-.17),l(.58,.013),l(.526,.338),l(.155,.126),l(.155,.339),l(-.285,.332),
+N(408.6,174.04),l(-.062,.777),l(.164,.762),l(.388,.718),l(-.316,.962),l(-.19,.566),l(-.223,.298),l(-.656,.414),l(-.095,.34),l(.116,.592),l(-.078,.368),l(-.433,.13),l(-.257,.03),l(-.237,.397),l(.002,.381),l(.003,.48),l(-.012,.876),l(-.09,.989),l(.217,1.439),l(-.121,1.188),l(.006,.234),l(-.333,.015),l(-1.232,.144),l(-.669,.052),l(-.106,-.205),l(-.295,-1.072),l(.188,-.708),l(.026,-.833),l(-.036,-.537),l(-.135,-.974),l(-.006,-.862),l(-.035,-.522),l(.013,-.579),l(-.442,-1.311),l(-.164,-.691),l(-.403,-.323),l(-.468,-.294),l(-.18,-.395),l(.141,-.594),l(.046,-.424),l(.062,-.142),l(.24,-.157),l(.366,-.553),l(.415,-.271),l(.418,-.045),l(.739,.095),l(.337,-.158),l(.415,-.468),l(.237,-.764),l(-.131,-.367),l(.577,-.3),l(.321,-.073),l(.339,.28),l(.727,.589),l(.902,.503),
+N(394.266,178.814),l(.191,-.17),l(-.02,-.41),l(-.261,-1.934),l(.125,-.34),l(.271,-.157),l(2.119,-.041),l(.867,.037),l(1.429,.006),l(.976,-.458),l(.161,.032),l(.595,.119),l(-.25,.849),l(.05,.254),l(.633,.915),l(.244,.451),l(-.188,.721),l(.02,.396),l(.265,1.114),l(.181,.592),l(.503,.788),l(.032,.155),l(-.286,.242),l(-.174,.382),l(.01,1.314),l(-.011,.72),l(.021,.551),l(.18,.479),l(.468,.577),l(.752,.608),l(-.503,.682),l(-.304,.099),l(-.593,-.013),l(-.992,.144),l(-.463,.185),l(-.188,.098),l(-.898,.469),l(-1.263,.398),l(-.942,.412),l(-.958,.567),l(-.578,-.324),l(-.945,-.436),l(-.074,-.124),l(-.066,-.741),l(-.554,-1.155),l(-.263,-.747),l(-.103,-.706),l(.327,-1.005),l(.437,-1.274),l(.521,-.851),l(.203,-.595),l(-.334,-1.438),l(-.189,-1.285),l(-.178,-.154),
+N(400.72,175.499),l(1.013,.163),l(.611,.166),l(.29,.041),l(-.046,.424),l(-.141,.594),l(.18,.395),l(.468,.294),l(.403,.323),l(.164,.691),l(.442,1.311),l(-.013,.579),l(.035,.522),l(.006,.862),l(.135,.974),l(.036,.537),l(-.026,.833),l(-.188,.708),l(.295,1.072),l(.106,.205),l(-.452,.035),l(-.479,.128),l(-.368,.212),l(-.023,.04),l(-.752,-.608),l(-.468,-.577),l(-.18,-.479),l(-.021,-.551),l(.011,-.72),l(-.01,-1.314),l(.174,-.382),l(.286,-.242),l(-.032,-.155),l(-.503,-.788),l(-.181,-.592),l(-.265,-1.114),l(-.02,-.396),l(.188,-.721),l(-.244,-.451),l(-.633,-.915),l(-.05,-.254),l(.25,-.849),
+N(383.772,190.418),l(.041,-.919),l(.105,-.565),l(.247,-.849),l(.059,-.452),l(-.131,-.282),l(-1.264,-1.334),l(-.592,.032),l(-.369,-.097),l(-.194,-.183),l(-.05,-.183),l(.501,-.865),l(.059,-.438),l(-.119,-.55),l(.062,-.17),l(.047,-.099),l(.602,-.554),l(.188,-.354),l(-.179,-.21),l(-.296,-.549),l(.03,-.127),l(.158,-.199),l(.304,-.03),l(.548,.223),l(.304,.012),l(.19,-.143),l(-.051,-.226),l(-.648,-.561),l(-.309,-.351),l(.332,-.37),l(.125,-.283),l(-.197,-.31),l(-.695,-.405),l(-.214,-.409),l(.264,-.623),l(.379,-.397),l(.522,-.54),l(.352,-.059),l(.566,.321),l(.455,.436),l(.35,-.228),l(.362,-.469),l(.571,-.483),l(.239,-.114),l(.257,.041),l(.147,.211),l(.004,.297),l(.117,.381),l(.148,.239),l(.352,-.059),l(.604,-.399),l(.159,-.1),l(.259,.168),l(.545,-.003),l(.308,.252),l(.15,.465),l(.52,.562),l(.452,.223),l(.354,.083),l(.368,-.115),l(.446,-.257),l(.608,-.159),l(.769,-.075),l(.644,.166),l(.63,.434),l(.178,.154),l(.189,1.285),l(.334,1.438),l(-.203,.595),l(-.521,.851),l(-.437,1.274),l(-.327,1.005),l(.103,.706),l(.263,.747),l(.554,1.155),l(.066,.741),l(-.119,-.201),l(-.289,-.197),l(-.208,.015),l(-.143,.24),l(-.096,.042),l(-.48,-.027),l(-.705,-.167),l(-.608,-.083),l(-2.352,.062),l(-1.279,.243),l(-.575,.199),l(-1.07,.369),l(-1.739,.781),l(-.701,.425),l(-.256,-.013),l(-.075,-.032),
+N(627.408,186.411),l(.035,.024),l(.523,.098),l(1.061,1.198),l(.693,.916),l(.506,.762),l(.081,.579),l(-.023,.805),l(-.134,.862),l(.07,1.073),l(-.07,.636),l(.097,.494),l(.196,.381),l(.53,.493),l(.338,.536),l(.705,1.608),l(.062,.127),l(-.366,-.069),l(-.895,-.055),l(-.401,.142),l(-.175,-.07),l(-.521,-.437),l(-.885,-.605),l(-.6,-.337),l(-1.231,-.675),l(-1.02,-.732),l(-.217,-.254),l(-.16,-.988),l(-.438,-.691),l(-.895,-.817),l(-.195,-.663),l(.039,-.494),l(.03,-.283),l(-.107,-.409),l(-.399,-.634),l(-.347,-1.849),l(-.188,-.72),l(.043,-.362),l(.448,-.17),l(.571,-.013),l(.106,.466),l(.447,.119),l(.261,.238),l(.055,.226),l(.041,.21),l(-.213,.398),l(.118,.239),l(.204,.083),l(.244,-.394),l(.279,-.28),l(.309,.163),l(.05,.478),l(.644,-.089),l(.11,-.575),l(.495,-.35),l(.086,-.337),M(643.95,196.042),l(.081,.044),l(.375,.408),l(.397,.141),l(.861,.083),l(.413,.168),l(.761,.436),l(.335,.042),l(.337,-.1),l(-.503,-.535),l(.169,-.551),l(.365,-.608),l(.528,-1.258),l(.584,-.27),l(1.481,-.342),l(1.018,-.299),l(.428,-.326),l(.524,-1.021),l(.523,-.323),l(.87,-1.117),l(.22,-.212),l(.244,-.147),l(.112,-.068),l(.084,.126),l(.062,.37),l(.234,-.012),l(.111,.259),l(.309,.271),l(.383,-.271),l(1.234,-.42),l(-.025,-.086),l(-.197,-.197),l(.013,-.247),l(.024,-.111),l(-.148,-.444),l(-.11,-.174),l(.167,-.042),l(.188,.143),l(.085,-.016),l(.449,-.084),l(.161,-.283),l(-.036,-.24),l(-.478,-.366),l(.197,-.396),l(.436,-.071),l(.511,.041),l(.154,-.605),l(.408,-.313),l(.284,-.48),l(.531,-.58),l(.498,-1.032),l(.075,-.17),l(.322,.465),l(.33,.098),l(.315,-.424),l(.305,.494),l(.351,.282),l(.402,.211),l(.075,.338),l(-.182,.354),l(-.234,.452),l(.037,.198),l(.631,-.128),l(.472,-.128),l(.115,.226),l(-.349,.495),l(.742,-.1),l(.357,-.085),l(.269,.056),l(1.035,.662),l(.345,.14),l(.1,.24),l(-.158,.269),l(-.201,.155),l(-.44,.143),l(-.594,.029),l(-.683,-.083),l(-.355,.311),l(.096,.254),l(.888,.69),l(-.161,.311),l(-.458,.199),l(-.646,.1),l(-.518,.114),l(-.067,.15),l(-.182,-.09),l(-.753,-.292),l(-.446,-.04),l(-.624,.018),l(-.651,-.081),l(-.748,-.081),l(-.464,.017),l(-.247,.157),l(-.381,.638),l(-.119,.565),l(.106,.988),l(-.22,.383),l(-.646,.244),l(-.218,.34),l(.076,.748),l(-.928,1.785),l(-.387,.299),l(-.864,.175),l(-.465,.172),l(-.467,.356),l(-.287,.03),l(-.559,-.152),l(-.524,-.223),l(-.557,-.251),l(-.431,-.11),l(-.479,.074),l(-.531,.441),l(-.403,.525),l(-.528,.342),l(-.399,.172),l(-.543,-.11),l(-.526,-.223),l(-.255,.001),l(-.928,.416),l(-.446,-.28),l(-.304,-.083),l(-1,-.983),l(-.253,-.295),l(-.288,-.792),
+N(274.556,195.884),l(.06,-.212),l(-.332,-.563),l(-.49,-1.127),l(-.246,-.832),l(-.185,-.295),l(-.561,-.067),l(-.532,-.675),l(-.571,-.831),l(.328,-.694),l(.095,-.467),l(-.078,-.777),l(.169,-.17),l(1.131,-.091),l(.183,-.27),l(.082,-.862),l(.142,-.496),l(.015,-.339),l(.326,-.312),l(.382,-.057),l(1.392,.463),l(.465,.042),l(.083,-.41),l(.141,-.085),l(.337,.027),l(.833,.012),l(.863,-.03),l(.723,.069),l(.63,.182),l(.999,.427),l(-.647,.876),l(-.391,.751),l(-.137,.594),l(.094,.381),l(.134,.635),l(.086,.664),l(.521,.844),l(.029,.438),l(-.424,1.472),l(-.489,.963),l(-1.05,-.488),l(-.319,.001),l(-.534,.385),l(-.398,.059),l(-.418,-.139),l(-.642,-.124),l(-.172,.156),l(-.2,.326),l(.611,1.014),l(-.528,-.054),l(-1.108,-.276),l(-.4,-.04),
+N(285.859,190.719),l(.015,.422),l(-1.102,1.646),l(-.427,.765),l(-.439,.992),l(-.464,.681),l(-.299,.214),l(-.56,-.025),l(-1.11,-.389),l(-.882,.613),l(-.225,-.069),l(-.649,-.505),l(.489,-.963),l(.424,-1.472),l(-.029,-.438),l(-.521,-.844),l(-.086,-.664),l(-.134,-.635),l(-.094,-.381),l(.137,-.594),l(.391,-.751),l(.647,-.876),l(.218,.093),l(1.033,.294),l(.55,.196),l(.799,.549),l(1.264,1.084),l(.545,.564),l(.317,.465),l(.193,.028),
+N(429.505,210.684),l(-.695,-.533),l(-.351,.13),l(-.68,.513),l(-.536,.414),l(-.112,-.177),l(-.392,-.86),l(-1.381,-1.344),l(.184,-.295),l(.413,-.229),l(.803,.307),l(.343,-.681),l(-.052,-.296),l(-.274,-.253),l(-.39,-.521),l(-.116,-.366),l(.058,-.495),l(.127,-.1),l(.909,-.146),l(.604,-.243),l(.125,-.213),l(.167,-.807),l(.174,-.185),l(.239,-.029),l(.193,.14),l(.341,.479),l(.405,.521),l(.386,.195),l(.287,-.016),l(.188,-.228),l(.362,-.482),l(.29,.253),l(.167,.578),l(.146,.112),l(.304,.068),l(.255,-.114),l(.184,-.68),l(.243,-1.089),l(.131,-1.229),l(.229,-1.047),l(-.114,-.338),l(-.161,-.127),l(-.384,-.082),l(-.528,-.208),l(-.242,-.31),l(-.132,-.437),l(.008,-.109),l(.021,-.3),l(.157,-.284),l(.492,-.398),l(.556,-.441),l(.094,-.354),l(-.389,-.902),l(-.369,-.322),l(-.592,-.067),l(-.939,.5),l(-.463,-.025),l(-.146,-.253),l(-.068,-.734),l(.077,-.679),l(.396,-.468),l(.56,.109),l(.863,.023),l(.878,-.076),l(.271,.055),l(.479,.04),l(.56,.124),l(.576,.194),l(.864,.334),l(.863,.292),l(.271,-.086),l(-.1,-.861),l(.159,-.185),l(.303,-.312),l(.365,-.497),l(.158,-.523),l(.222,-1.287),l(.255,-.228),l(.479,-.102),l(.975,-.175),l(.367,.054),l(.624,.138),l(.895,.093),l(.751,-.287),l(.128,.197),l(-.031,.156),l(-.398,1.203),l(-.558,.837),l(-.349,.821),l(-.094,.551),l(-.156,.764),l(-.196,2.021),l(-.254,1.342),l(-.115,.61),l(-.169,.708),l(-.139,.523),l(-.459,.427),l(-.525,.229),l(-.809,.599),l(-.41,.454),l(-.47,.836),l(-.343,.751),l(-.193,1.159),l(-.074,.396),l(.122,.72),l(-.086,.706),l(-1.025,.938),l(-.364,.229),l(-.931,.811),l(-.554,.399),l(-.57,.328),l(-.147,-.211),l(-.042,-.664),l(-.054,-.424),l(-1.495,.532),l(-.327,.581),l(-.443,.271),l(-.177,-.013),l(-.484,-.336),
+N(425.506,195.522),l(.045,-.495),l(.88,.093),l(1.773,.088),l(.831,.038),l(1.022,.149),l(-.396,.468),l(-.077,.679),l(.068,.734),l(.146,.253),l(.463,.025),l(.939,-.5),l(.592,.067),l(.369,.322),l(.389,.902),l(-.094,.354),l(-.556,.441),l(-.492,.398),l(-.157,.284),l(-.021,.3),l(-.008,.109),l(.132,.437),l(.242,.31),l(.528,.208),l(.384,.082),l(.161,.127),l(.114,.338),l(-.229,1.047),l(-.131,1.229),l(-.243,1.089),l(-.184,.68),l(-.255,.114),l(-.304,-.068),l(-.146,-.112),l(-.167,-.578),l(-.29,-.253),l(-.362,.482),l(-.188,.228),l(-.287,.016),l(-.386,-.195),l(-.405,-.521),l(-.341,-.479),l(-.193,-.14),l(-.239,.029),l(-.174,.185),l(-.167,.807),l(-.125,.213),l(-.604,.243),l(-.909,.146),l(-.127,.1),l(-.058,.495),l(.116,.366),l(.39,.521),l(.274,.253),l(.052,.296),l(-.343,.681),l(-.803,-.307),l(-.413,.229),l(-.184,.295),l(-.038,-.037),l(-.563,-.493),l(-.532,-.55),l(-.259,-.466),l(-1.318,-1.169),l(.286,-.312),l(-.369,-.281),l(-.528,-.056),l(-.918,-1.438),l(.382,-.297),l(.111,-.1),l(-.321,-.395),l(-.464,-.14),l(-.29,-.564),l(-.369,-.451),l(.319,-.17),l(.541,-.411),l(.223,-.396),l(.291,-.976),l(.089,-.295),l(.415,.07),l(.495,.013),l(-.064,-.211),l(-.56,-.352),l(-.561,-.451),l(.208,-.042),l(.271,-.128),l(.142,-.617),l(.83,-.043),l(.927,.008),l(1.245,.021),l(.575,-.032),l(-.001,-.155),l(-.02,-.551),l(-.026,-1.836),
+N(422.536,195.513),l(1.005,-.007),l(1.965,.017),l(.026,1.836),l(.02,.551),l(.001,.155),l(-.575,.032),l(-1.245,-.021),l(-.927,-.008),l(-.83,.043),l(.033,-.146),l(-.049,-.297),l(-.144,-.084),l(-.416,-.098),l(.287,-.467),l(.447,-.523),l(.335,-.58),l(.066,-.403),M(419.636,193.273),l(-.484,-.049),l(-.049,-.218),l(.049,-.291),l(.242,-.097),l(.024,-.146),l(0,-.291),l(.17,-.097),l(.169,-.121),l(.219,.048),l(.218,.048),l(.097,.194),l(-.193,.169),l(-.146,.243),l(-.17,.339),l(-.146,.267),
+N(222.291,207.801),l(.188,-.126),l(.264,-.312),l(.585,-1.061),l(.038,-.269),l(-.209,-.733),l(.058,-.391),l(-.478,.617),l(-.732,.623),l(-.333,-.098),l(-.601,-.324),l(-.408,-.408),l(.389,-.283),l(.1,-.24),l(-.167,-.579),l(.005,-.438),l(.133,-.466),l(-.109,-.282),l(-.362,-.649),l(.193,-.17),l(.48,-.198),l(.721,-.454),l(-.319,-.395),l(0,-.226),l(.175,-.354),l(.497,-.462),l(.157,-.146),l(.14,-.848),l(-.116,-.452),l(-.082,-.184),l(.11,-.226),l(.686,-.101),l(.891,-.341),l(.396,-.241),l(.474,-.382),l(.09,-.186),l(1.496,.803),l(1.432,.656),l(.227,.225),l(.166,.493),l(.355,.309),l(.977,.277),l(.67,-.061),l(.89,-.401),l(.128,.056),l(.709,.631),l(.197,.118),l(.269,.162),l(.081,.112),l(.387,.76),l(.321,.789),l(-.08,.255),l(-.352,.384),l(-.547,1.147),l(-.533,.779),l(-.71,.725),l(-1.001,.755),l(-.336,.087),l(-1.028,.303),l(-.901,.358),l(-.809,.499),l(-.59,.653),l(-.284,.51),l(-.987,2.506),l(-.267,.312),l(-.242,.072),l(-.398,-.068),l(-.398,-.491),l(-.191,-.479),l(-.312,-.252),l(-.815,.005),l(-.444,-.11),l(-.105,-.197),l(-.097,-.494),l(.233,-.34),l(.029,-.495),l(.005,-.569),
+N(245.934,224.314),l(1.109,1.843),l(.922,1.646),l(.08,.24),l(-.437,.736),l(-.15,.523),l(-.143,.848),l(.057,.522),l(.236,.902),l(-.156,.34),l(-.221,.34),l(-.532,.582),l(-.102,.452),l(.347,.845),l(-.21,.368),l(-.159,.34),l(.022,.48),l(.604,1.168),l(.015,.522),l(-.285,.439),l(-.723,.61),l(-.12,.227),l(.049,.48),l(.132,.295),l(.018,.142),l(-.586,.187),l(-.15,.199),l(-.228,.933),l(-.259,.298),l(-.852,.275),l(-.201,-.517),l(-.289,-.31),l(-1.422,-.618),l(-.313,-.239),l(-.233,-.678),l(-.315,-.38),l(-.559,-.225),l(-.504,-.281),l(-.489,-.254),l(-.616,-.309),l(-.873,-.464),l(-.673,-.366),l(-.866,-.379),l(-.838,-.337),l(-1.271,-.845),l(-1.354,-.985),l(-1.232,-1.099),l(-.594,-.705),l(-.42,-.677),l(.008,-.438),l(0,-.903),l(-.269,-.606),l(-.781,-1.043),l(-1.389,-2.778),l(-.285,-.268),l(-.467,-.211),l(-.074,-.085),l(.055,-.536),l(-.02,-.396),l(-.149,-.396),l(-1.208,-2.199),l(-.997,-1.961),l(-.688,-1.27),l(-1.365,-1.917),l(-.272,-.353),l(-.598,-.973),l(-.462,-.423),l(-.75,-.437),l(-.914,-.365),l(-.61,-.365),l(.119,-.17),l(.181,-.113),l(.609,-.029),l(.144,-.41),l(-.118,-.282),l(-.592,-.874),l(-.353,-.437),l(-.258,-.72),l(.01,-.24),l(.244,-.523),l(.582,-.622),l(1.02,-.878),l(.47,-.198),l(.317,-.214),l(-.005,.569),l(-.029,.495),l(-.233,.34),l(.097,.494),l(.105,.197),l(.444,.11),l(.815,-.005),l(.312,.252),l(.191,.479),l(.398,.491),l(.398,.068),l(.242,-.072),l(.267,-.312),l(.987,-2.506),l(.284,-.51),l(.59,-.653),l(.809,-.499),l(.901,-.358),l(1.028,-.303),l(.336,-.087),l(1.001,-.755),l(.71,-.725),l(.533,-.779),l(.547,-1.147),l(.352,-.384),l(.08,-.255),l(-.321,-.789),l(-.387,-.76),l(.927,.107),l(.897,.418),l(.513,.435),l(.208,.324),l(.208,.62),l(.526,.364),l(.988,.672),l(.251,.761),l(.078,.282),l(.366,.14),l(.479,.039),l(.173,.296),l(-.004,.396),l(.092,.281),l(.573,.152),l(1.038,.008),l(.496,-.031),l(.337,-.157),l(.452,-.427),l(.523,.421),l(.397,.182),l(.708,-.442),l(.509,.209),l(1.299,.84),l(.127,.069),l(-.035,.198),l(-1.155,1.956),l(.448,-.031),l(.161,-.058),l(.237,.126),l(.575,.703),l(.156,.14),l(.136,.324),l(-.341,.186),l(-.793,-.235),l(-.493,-.096),l(-.322,.072),l(-.604,.441),l(-.371,.115),l(-.669,-.066),l(-1.895,.773),l(-.701,.442),l(-.372,.172),l(-.599,1.415),l(-.319,.511),l(-.021,.155),l(.277,.86),l(.012,.112),l(-.625,.498),l(-.62,.328),l(-.349,.342),l(-.247,.622),l(.106,.564),l(.619,.957),l(1.333,2.166),l(.106,.127),l(-.247,.495),l(-.208,.312),l(1.085,.12),l(.415,.04),l(.312,.182),l(.241,.323),l(.096,.663),l(1.843,.032),l(.762,-.499),l(.648,-.484),l(.326,-.114),l(-.059,1.511),l(-.098,1.173),l(.108,.353),l(.317,.337),l(.285,.068),l(.358,-.072),l(.786,-.259),l(.42,-.045),
+N(279.288,257.295),l(-.063,-.423),l(.027,-.777),l(.222,-.819),l(.298,-.835),l(.321,-.75),l(-.019,-.141),l(-.491,-.336),l(-.234,-.112),l(-1.29,.445),l(-.224,-.055),l(-.254,-.563),l(-.487,-2.524),l(-.121,-.437),l(-.843,-.404),l(-.724,-.278),l(-.115,.001),l(-.709,.413),l(-.346,.03),l(-.925,-.221),l(-.818,-.15),l(-.372,-.04),l(.006,-.72),l(.086,-.721),l(.132,-.862),l(-.104,-.959),l(-.521,-.872),l(-.084,-.663),l(-.253,-.408),l(.421,-.666),l(.38,-.765),l(.238,-.834),l(.185,-1.173),l(-.022,-.494),l(-.307,-.774),l(-.316,-.479),l(-.73,-.362),l(-.436,-.407),l(-.067,-.563),l(.261,-.835),l(-.042,-.296),l(-.693,-.024),l(-1.615,-.019),l(-.5,-.012),l(-.967,-.036),l(-.153,-.112),l(-.249,-.944),l(-.138,-2.032),l(-.03,-.706),l(-.203,-1.029),l(.038,-.565),l(-.085,-.169),l(-.403,-.237),l(-.537,-.166),l(-.636,-.123),l(-1.148,.077),l(-.22,-.084),l(-.398,-.308),l(-.385,-.774),l(-.096,-.014),l(-.882,-.037),l(-.3,-.098),l(-.75,-.603),l(-.688,-.307),l(-.676,-.25),l(-.355,.03),l(-.787,-.023),l(-.316,-.097),l(-1.1,-.939),l(-.813,-.928),l(-.335,-.591),l(-.141,-.635),l(-.036,-.818),l(.064,-.622),l(.079,-.621),l(-.045,-.748),l(-1.283,-.021),l(-.934,.076),l(-.36,.158),l(-.874,.485),l(-1.471,1.109),l(-.415,.243),l(-.48,-.025),l(-.336,-.026),l(-.946,.824),l(-.355,.03),l(-1.122,-.304),l(-.939,-.136),l(-.42,.045),l(-.786,.259),l(-.358,.072),l(-.285,-.068),l(-.317,-.337),l(-.108,-.353),l(.098,-1.173),l(.059,-1.511),l(-.326,.114),l(-.648,.484),l(-.762,.499),l(-1.843,-.032),l(-.096,-.663),l(-.241,-.323),l(-.312,-.182),l(-.415,-.04),l(-1.085,-.12),l(.208,-.312),l(.247,-.495),l(-.106,-.127),l(-1.333,-2.166),l(-.619,-.957),l(-.106,-.564),l(.247,-.622),l(.349,-.342),l(.62,-.328),l(.625,-.498),l(-.012,-.112),l(-.277,-.86),l(.021,-.155),l(.319,-.511),l(.599,-1.415),l(.372,-.172),l(.701,-.442),l(1.895,-.773),l(.669,.066),l(.371,-.115),l(.604,-.441),l(.322,-.072),l(.493,.096),l(.793,.235),l(.341,-.186),l(-.136,-.324),l(.133,-.198),l(.073,-.382),l(.706,-4.411),l(.326,-1.428),l(-.031,-.212),l(-.399,-.562),l(.031,-.777),l(-.192,-.183),l(-.688,-.363),l(-.192,-.197),l(0,-.554),l(.001,-1.071),l(.111,-.085),l(.493,-.187),l(.687,-.032),l(.417,.11),l(.384,.04),l(-.052,-.367),l(-.293,-.408),l(-.177,-.084),l(-.896,-.122),l(-.178,-.154),l(.013,-1.031),l(.042,-.325),l(.64,.024),l(2.812,.082),l(.463,-.017),l(.412,-.158),l(.834,-.57),l(.52,-.286),l(.148,.168),l(.138,.437),l(.161,.861),l(.088,.452),l(.199,.437),l(.432,.054),l(.694,.546),l(.482,.223),l(.414,-.073),l(.757,-.697),l(.083,.183),l(.186,.776),l(.271,-.016),l(.645,-.739),l(.74,-.654),l(.554,-.286),l(.652,-.173),l(.235,-.213),l(.259,-.666),l(.203,-.199),l(.652,-.131),l(.569,-.272),l(.265,-.27),l(-.15,-.253),l(-.434,-.125),l(-.96,-.051),l(-.166,-.239),l(-.079,-.55),l(-.302,-1.524),l(-.391,-.69),l(-.616,-.688),l(.041,-.184),l(.159,-.029),l(.309,.125),l(.896,.461),l(.385,.04),l(1.149,-.035),l(.344,.224),l(.628,.618),l(.348,-.115),l(.232,-1.117),l(.284,-.115),l(.465,.054),l(.557,-.074),l(.807,-.23),l(1.74,-.9),l(.592,-.385),l(.163,-.326),l(-.103,-.169),l(.375,-.257),l(.302,-.058),l(.516,.124),l(.26,.111),l(.04,.212),l(-.452,1.09),l(.304,-.001),l(.533,.138),l(.435,.958),l(-.179,.368),l(-.547,1.416),l(-.213,.962),l(.156,.465),l(.281,.394),l(.167,.31),l(-.072,.354),l(.008,.396),l(.15,.296),l(1.103,.897),l(.613,.392),l(.686,-.075),l(.694,-.513),l(.505,-.314),l(1,-.331),l(.934,-.429),l(.943,.022),l(.4,.04),l(1.108,.276),l(.528,.054),l(-.611,-1.014),l(.2,-.326),l(.172,-.156),l(.642,.124),l(.418,.139),l(.398,-.059),l(.534,-.385),l(.319,-.001),l(1.05,.488),l(.649,.505),l(.225,.069),l(.882,-.613),l(1.11,.389),l(.56,.025),l(.299,-.214),l(.464,-.681),l(.439,-.992),l(.427,-.765),l(1.102,-1.646),l(-.015,-.422),l(.409,-.241),l(.276,.141),l(.235,.423),l(.164,.791),l(.414,1.34),l(.108,.607),l(.472,1.383),l(.296,.55),l(.324,.324),l(.978,.21),l(.196,.338),l(.038,.551),l(-.045,.254),l(-.524,.354),l(-.933,1.414),l(-.238,.325),l(-.51,.411),l(-.41,.231),l(-.244,.138),l(-.798,.778),l(-.544,.947),l(-.419,1.018),l(-.402,.453),l(.271,.112),l(.512,-.072),l(.497,-.198),l(1.202,-.709),l(.112,-.028),l(.299,.889),l(.442,.818),l(.255,.112),l(.287,.07),l(1.135,-.031),l(.592,-.058),l(.737,-.298),l(-.277,.679),l(-.017,.584),l(.448,-.557),l(1.254,-1.006),l(.289,-.156),l(.306,-.382),l(.499,-.933),l(.272,-.396),l(.256,-.099),l(1.006,-.003),l(.352,-.198),l(.175,0),l(.687,.549),l(.654,.267),l(.32,-.028),l(.814,.28),l(1.069,.45),l(.334,.281),l(.269,.593),l(.08,.028),l(.176,-.015),l(.402,-.354),l(.319,.042),l(.302,.254),l(.538,.789),l(.205,.396),l(-.13,.254),l(-.308,.41),l(-.116,.424),l(.091,.466),l(.171,.396),l(.825,-.821),l(.369,-.156),l(.609,-.156),l(.995,-.454),l(.224,-.028),l(.367,.069),l(.764,.38),l(.907,.436),l(.462,.098),l(1.117,.167),l(1.118,.083),l(.464,-.058),l(.48,-.029),l(.577,-.171),l(.416,-.016),l(1.131,.351),l(.731,.408),l(.762,.479),l(.586,.437),l(1.595,1.395),l(.742,.662),l(.821,.605),l(.41,.338),l(.477,.196),l(.734,.168),l(1.247,.097),l(.912,.04),l(.445,.197),l(.265,.465),l(.087,.607),l(.592,2.004),l(.268,1.143),l(.062,.988),l(-.076,.579),l(-.179,.904),l(-.243,.848),l(-.559,1.301),l(-.518,.849),l(-1.347,1.175),l(-1.388,1.288),l(-.44,.198),l(-.354,.043),l(-.39,.637),l(-.094,.396),l(-.012,.353),l(-.504,1.074),l(-.6,.976),l(-.8,1.104),l(-.266,.212),l(-.194,.015),l(-.079,-.423),l(-.278,-.296),l(-.367,-.084),l(-.064,.79),l(-.538,1.64),l(-.049,.734),l(-.077,.607),l(.318,2.738),l(.078,.988),l(-.577,2.543),l(-.039,.663),l(.14,.649),l(.043,.085),l(-.759,.495),l(-.404,.51),l(-.261,.791),l(-.04,.367),l(.245,1.468),l(-.106,.367),l(-.214,.283),l(-.676,.608),l(-.752,1.498),l(-1.01,1.4),l(-.201,.537),l(-.055,.311),l(.144,.945),l(-1.203,.37),l(-.544,.283),l(-.355,.368),l(-.084,.466),l(.021,.367),l(-.401,.114),l(-.864,-.041),l(-.979,.172),l(-.654,.001),l(-.764,-.055),l(-.465,-.112),l(-.922,.072),l(-.736,.002),l(-.214,.607),l(-.295,.198),l(-.795,.312),l(-.481,.453),l(-.21,.354),l(-.747,-.267),l(-.35,.071),l(-.563,.255),l(-1.864,1.232),l(-.528,.396),l(-.725,.383),l(-.616,.438),l(-.532,.679),l(-.486,.241),l(-.567,.072),l(-.278,-.014),l(.006,.24),l(.336,.225),l(.12,.536),l(-.111,.537),l(-.099,.282),l(-.282,.156),l(-.084,.155),l(.384,.648),l(.036,1.186),l(.05,1.045),l(-.057,.311),l(-.11,.594),l(-.198,.621),l(-.464,.735),l(-.566,.495),l(-.788,.524),l(-.866,1.046),l(-.208,.396),l(-.476,1.145),l(-.499,.933),l(-.75,1.004),l(-.69,.623),l(-.801,.78),l(.321,-.808),l(.436,-.594),l(.629,-.566),l(.605,-1.158),l(-.06,-.254),l(-.356,.142),l(-.397,-.014),l(-.633,-.21),l(-.075,.155),l(.002,.297),l(-.401,1.215),l(-.349,.48),l(-.661,.438),l(-.317,.283),l(-.225,.424),l(-.013,.354),l(-.448,1.709),l(-.481,.763),l(-.787,.934),l(-1.076,.979),l(-.101,-.313),l(-.258,-.746),l(.103,-.269),l(.634,-.766),l(.017,-.269),l(-.333,-.379),l(-.163,-.027),l(-.131,-.197),l(-.853,-1.109),l(-.474,-.435),l(-.999,-.587),l(-.55,-.194),l(-.617,-.194),l(-.432,-.576),l(-.365,-.393),l(-.693,.427),l(-.27,.016),l(-.232,-.38),l(-.59,-.814),l(-.379,-.407),l(-.47,-.364),l(-.266,-.098),l(-.25,.101),l(-1.042,.246),l(-.55,.003),l(.214,-.27),l(.833,-.88),l(1.805,-1.816),l(1.435,-1.363),l(1.633,-1.448),l(.348,-.242),l(1.754,-.744),l(.521,-.313),l(.161,-.467),l(.094,-1.638),l(-.517,-1.154),l(-.206,-.196),l(-.462,.017),l(-.957,.161),M(288.966,203.943),l(-.558,-.125),l(-.301,-.536),l(-.078,-.382),l(.16,-.197),l(-.094,-.636),l(.048,-.381),l(.208,-1.018),l(.176,-.099),l(.479,-.058),l(.879,.097),l(1.007,.11),l(.479,-.199),l(.368,.028),l(.479,.168),l(.479,.083),l(.319,.155),l(-.335,.538),l(-.193,.946),l(-.257,.494),l(-.289,.312),l(-.561,.326),l(-.464,.171),l(-.527,.015),l(-.783,.016),l(-.641,.171),
+N(732.92,214.323),l(-.164,-.24),l(-.225,-.197),l(-.379,-.126),l(-.416,.198),l(-.399,-.38),l(-.287,-.184),l(-.659,-.238),l(-.243,-.239),l(.156,-.255),l(.29,.027),l(.731,-.058),l(.538,.126),l(.743,.083),l(.523,-.058),l(.258,-.185),l(.232,-.509),l(.056,.099),l(.351,.395),l(.286,.184),l(.241,.014),l(.961,-.2),l(.366,-.227),l(.361,-.722),l(.241,-.212),l(.653,-.029),l(.263,-.128),l(.04,-.282),l(-.064,-.565),l(-.043,-.536),l(.495,.196),l(.404,.056),l(.324,-.354),l(.318,.578),l(.077,.353),l(-.196,.495),l(-.099,.184),l(-.509,.044),l(-.159,.226),l(.061,.17),l(.437,.479),l(-.308,.354),l(-.264,.113),l(-.742,-.083),l(-.416,.001),l(-.091,.269),l(-.408,.495),l(-.275,.156),l(-.973,.426),l(-.484,.143),l(-.798,.029),l(-.807,.115),M(713.795,220.696),l(.031,-3.438),l(-.046,-.805),l(-.431,-1.368),l(.44,-.822),l(-.169,-7.966),l(2.581,.802),l(.85,.337),l(1.04,.295),l(1.254,.378),l(.848,.507),l(.613,.323),l(.597,.084),l(.38,-.058),l(.26,.508),l(.274,.254),l(.635,.352),l(.687,.395),l(.779,.718),l(-.3,.947),l(.033,.226),l(.305,.226),l(.665,.111),l(1.887,.787),l(.354,.027),l(.72,.183),l(.181,.254),l(.413,.535),l(.211,.579),l(-.166,.113),l(-1.114,.073),l(-.563,.156),l(-.098,.155),l(.047,.339),l(.589,.987),l(.5,.521),l(1.464,1),l(.218,.847),l(.725,1.044),l(.288,.141),l(.304,-.015),l(.712,-.086),l(.338,.013),l(.087,.198),l(-.354,.551),l(.218,.212),l(.401,.141),l(.423,.041),l(.63,.168),l(.096,.127),l(-.031,.142),l(-.648,.213),l(.287,.24),l(1.064,.28),l(.538,.295),l(.235,.763),l(-.064,.226),l(-1.094,-.012),l(-.215,-.154),l(-.146,-.466),l(-.126,-.099),l(-1.011,-.111),l(-1.063,-.266),l(-.644,-.126),l(-.752,.016),l(-.774,-.026),l(-.573,-.211),l(-.494,-.352),l(-.223,-.522),l(-.24,-.268),l(-.726,-.31),l(-.472,-.338),l(-.135,-.197),l(-1.089,-1.608),l(-.613,-.817),l(-.454,-.056),l(-1.524,-.336),l(-.671,-.31),l(-.55,-.055),l(-.415,.185),l(-.394,.071),l(-.78,-.45),l(.282,.762),l(-.027,.212),l(-.249,.071),l(-.382,-.126),l(-.311,.1),l(.296,.395),l(-.116,.113),l(-1.037,.045),l(-1.125,-.182),l(-.478,.029),l(.237,.127),l(.24,.154),l(.642,.169),l(.662,.352),l(.404,.338),l(.219,.395),l(-.411,.199),l(-.739,.425),l(-.458,.213),l(-.668,-.097),l(-1.801,-.039),l(-1.219,-.092),
+N(726.605,297.247),l(-.479,-.229),l(-1.179,-.471),l(-.543,-.371),l(-.508,-.715),l(-.477,-.558),l(-.216,-.414),l(.264,-.044),l(.169,.1),l(.421,.171),l(.129,-.143),l(-.209,-.387),l(-.703,-.699),l(-.617,-.713),l(-.149,-.257),l(-.375,-.956),l(-.008,-.67),l(.177,-.044),l(1.004,.312),l(1.476,.354),l(1.089,.369),l(.797,-.03),l(.463,-.144),l(.382,-.115),l(.373,-.058),l(.684,.027),l(.306,-.301),l(.716,-.244),l(.475,.385),l(-.009,.1),l(.128,.8),l(-.009,.686),l(-.751,1.503),l(-.124,.758),l(-.245,.315),l(-.943,.26),l(-.553,.388),l(-.49,.689),l(-.189,.272),l(-.274,.072),M(716.883,224.344),l(.682,.394),l(.233,.509),l(.047,.649),l(.118,.678),l(.256,.197),l(.42,.013),l(.102,.24),l(-.45,.919),l(.715,.338),l(.175,.282),l(.147,.593),l(.08,1.3),l(.144,.621),l(.456,1.157),l(.293,.268),l(.422,.014),l(.328,-.312),l(.384,-.185),l(.32,.663),l(.363,.127),l(.563,.408),l(.385,.154),l(.144,.155),l(.002,.41),l(.083,.96),l(.275,.776),l(-.046,1.06),l(.279,.24),l(.93,1.325),l(.276,.706),l(.055,.777),l(-.209,.876),l(.403,.649),l(.275,.833),l(.204,.353),l(1.395,.803),l(.285,.154),l(.744,.111),l(.402,.649),l(.843,.535),l(.483,.169),l(1.141,.068),l(.117,.197),L(731,245.42),l(-.273,.565),l(.309,.424),l(.879,.479),l(.22,.564),l(.469,1.342),l(.211,.748),l(.112,.396),l(.252,.311),l(.771,.352),l(-.021,-.127),l(-.141,-.777),l(.913,.507),l(.567,.154),l(.184,.127),l(.136,.296),l(.106,1.752),l(.884,.762),l(.573,.323),l(.604,.154),l(.287,.296),l(.244,.579),l(.229,.424),l(.545,.168),l(.176,.297),l(.007,.269),l(.19,.536),l(.106,.113),l(.506,.126),l(.156,.112),l(.162,.354),l(.366,1.327),l(.026,.876),l(.013,.862),l(-.132,.41),l(-.181,.325),l(.778,1.271),l(.223,.48),l(.181,.833),l(.034,.749),l(-.293,.708),l(-.353,1.668),l(-.585,1.755),l(.137,.791),l(-.065,.806),l(-.306,.778),l(-.661,.977),l(-.203,.665),l(-.598,.821),l(-.45,.185),l(-.465,.1),l(-.423,.468),l(-.466,.68),l(-.328,1.527),l(-.477,.638),l(-.34,.495),l(.014,.65),l(-.208,.539),l(-.331,.312),l(-.601,.341),l(-.27,.326),l(-.3,1.021),l(-.169,1.049),l(-.079,.937),l(.021,.666),l(-.283,.64),l(-.322,.354),l(-.463,.3),l(-.754,.101),l(-1.158,.087),l(-.82,.03),l(-.527,.157),l(-.516,.299),l(-1.529,.94),l(-.443,.129),l(-.214,.284),l(-.346,.071),l(-.528,.059),l(-.286,-.014),l(.295,.368),l(.209,.27),l(-1.315,-.665),l(-.885,-.353),l(.003,-.469),l(-.073,-.156),l(-.56,-.467),l(-.628,-.368),l(-.421,-.014),l(-.485,.172),l(-.335,.242),l(.748,.467),l(-.97,.243),l(-.929,.428),l(-.953,.442),l(-.222,.028),l(-.604,-.226),l(-.886,-.438),l(-.67,-.226),l(-1.086,-.311),l(-.51,-.041),l(-.239,.156),l(-.044,.113),l(-.716,-.169),l(-.751,-.353),l(-.522,-.298),l(-.896,-.82),l(-.526,-.34),l(-.422,-.879),l(.09,-1.035),l(-.082,-.411),l(-.184,-.495),l(-.664,-.736),l(-.141,-.523),l(-.029,-.425),l(-.534,-.014),l(-.786,.398),l(-.597,.114),l(-.34,.058),l(-.178,-.07),l(-.167,-.17),l(.517,-.454),l(.233,-.567),l(.073,-.821),l(-.253,-.324),l(-.536,-.593),l(-.247,-.353),l(-.485,.735),l(-.443,1.431),l(-.19,.113),l(-.796,.002),l(-.199,.156),l(-.196,.015),l(-.255,.028),l(.198,-.396),l(.081,-.396),l(.079,-.1),l(.634,.041),l(.242,-.142),l(.126,-.255),l(-.105,-1.004),l(.454,-.835),l(.328,-.453),l(.091,-.396),l(.018,-.409),l(.151,-.128),l(.245,-.015),l(.218,-.354),l(-.052,-.227),l(-.323,-.494),l(-.338,-.494),l(-.107,.707),l(-.288,.255),l(-.518,.299),l(-.311,.467),l(-.086,.155),l(-.189,.467),l(-.281,.326),l(-.747,.242),l(-.735,.481),l(-.653,.567),l(-.36,.693),l(-.514,.808),l(-.41,-.339),l(-.38,-1.328),l(-.263,-.579),l(-.19,-.325),l(-.688,-.79),l(-.297,-.734),l(-.176,-.212),l(-.704,.072),l(-.235,-.099),l(-.139,-.24),l(-.085,-.269),l(.334,-.34),l(-.047,-.297),l(-.346,-.395),l(-.543,-.494),l(-.266,-.098),l(-.83,.157),l(-.486,-.07),l(-.95,-.549),l(-.274,-.014),l(-.438,.17),l(-.433,-.027),l(-.421,-.183),l(-.662,-.521),l(-.921,-.437),l(-.218,.001),l(-.723,.213),l(-1.282,.088),l(-.669,.001),l(-1.764,.061),l(-.611,.129),l(-.656,.213),l(-.989,.44),l(-.972,.256),l(-1.039,.257),l(-1.503,.088),l(-.794,-.013),l(-.383,.044),l(-.927,.284),l(-.993,.469),l(-.773,.397),l(-.538,.143),l(-.431,.085),l(-.361,.199),l(-.615,.693),l(-.774,1.02),l(-.588,.284),l(-.766,-.013),l(-.547,-.013),l(-.927,.143),l(-.4,.185),l(-.663,-.395),l(-.294,-.084),l(-.734,.016),l(-1.572,.173),l(-.938,.157),l(-.459,-.041),l(-.672,.044),l(-.398,.227),l(-.583,.793),l(-.344,.128),l(-.958,-.125),l(-.158,.057),l(-.57,.708),l(-.465,.368),l(-.919,.271),l(-.586,.086),l(-1.516,-.082),l(-.638,-.055),l(-.688,-.197),l(-.633,-.366),l(-.778,-.677),l(-.74,-.353),l(-.374,-.041),l(-.151,-.07),l(-.19,-1.229),l(.055,-.255),l(.489,.112),l(.45,-.086),l(.332,-.425),l(.197,-.467),l(.267,-1.357),l(-.043,-1.215),l(-.156,-.622),l(-.258,-.593),l(-1.117,-1.906),l(-.208,-.635),l(-.144,-.834),l(.027,-.989),l(-.16,-.692),l(-.467,-1.072),l(-.663,-.945),l(-.603,-.734),l(-.214,-.254),l(.128,-.904),l(-.215,-.536),l(-.733,-1.115),l(-.972,-1.018),l(-.273,-.583),l(.126,-.233),l(.188,.187),l(.152,.443),l(.183,.163),l(.235,.35),l(.327,.188),l(.354,.023),l(-.348,-1.144),l(-.437,-.396),l(-.226,-.326),l(.08,-.304),l(.748,.84),l(.495,.979),l(.477,.065),l(-.099,-.555),l(.289,-.039),l(.004,-.564),l(-.282,-.48),l(-1.03,-1.368),l(-.354,-.691),l(-.119,-.579),l(-.038,-.734),l(.355,-.595),l(.323,-.523),l(.21,-.664),l(-.083,-1.031),l(-.254,-.635),l(.033,-.368),l(.438,-.692),l(.109,-.325),l(.064,-.156),l(.271,.649),l(.011,.424),l(.105,.184),l(.35,.027),l(.171,-.113),l(.187,-.565),l(.141,-.48),l(.765,-.468),l(1.22,-.624),l(.484,-.326),l(.676,-.581),l(.585,-.467),l(.632,-.327),l(.79,-.114),l(.697,-.016),l(.7,-.002),l(.431,-.043),l(.352,-.185),l(.474,-.453),l(.494,-.128),l(.929,-.072),l(.279,-.143),l(.291,-.551),l(.158,-.1),l(.444,.027),l(.877,.224),l(.626,-.043),l(.911,-.299),l(1.084,-.469),l(.359,-.213),l(.716,-.665),l(.427,-.58),l(.29,-.622),l(.132,-.297),l(.41,-.369),l(.968,-.651),l(.079,-.17),l(-.067,-.409),l(-.242,-.805),l(-.016,-.495),l(1.063,-1.118),l(.387,-.692),l(.291,.169),l(.341,.437),l(.619,1.355),l(.262,.253),l(.177,-.579),l(.021,-.466),l(.436,.238),l(.272,.07),l(.189,-.607),l(-.06,-.142),l(-.563,-.238),l(-.175,-.24),l(.007,-.565),l(.044,-.112),l(.897,.04),l(.661,.253),l(.642,-.029),l(.334,-.029),l(.289,.074),l(-.699,-.455),l(-.431,-.141),l(.128,-.537),l(-.07,-.296),l(.135,-.509),l(.422,-.354),l(.165,-.07),l(.732,.394),l(.202,-.043),l(-.112,-.452),l(.11,-.48),l(.146,-.367),l(-.041,-.522),l(.358,-.171),l(.4,-.113),l(.813,.04),l(.529,-1.088),l(.371,-.298),l(.35,.169),l(.268,.451),l(.265,-.552),l(.222,-.227),l(.197,-.48),l(.695,.62),l(.513,.084),l(.293,.211),l(.331,.536),l(.632,.592),l(.122,.706),l(-.072,.594),l(.181,.197),l(.256,-.283),l(.462,-.679),l(.155,-.128),l(1.16,.082),l(.479,.155),l(.637,.492),l(.332,.141),l(.156,-.48),l(.302,-.297),l(.022,-.24),l(-.266,-.324),l(-.601,-.395),l(-.079,-.184),l(.008,-.24),l(.145,-.099),l(.604,-.538),l(.007,-.452),l(.191,-.213),l(.518,-.283),l(.268,-.241),l(.151,-.269),l(-.094,-.184),l(.003,-.296),l(.512,-.863),l(.121,-.057),l(.317,-.029),l(.397,-.029),l(.248,-.17),l(-.205,-.409),l(.377,-.396),l(.344,-.071),l(.793,.366),l(.616,-.072),l(1.291,-.088),l(.512,-.128),l(.232,-.17),l(.04,-.057),l(-.077,-.197),l(-.2,-.734),l(-.248,-.282),l(-.471,-.268),l(-.374,.086),l(-.244,-.141),l(.035,-.212),l(.367,-.143),l(.396,-.043),l(.617,.521),l(.255,.099),l(.34,-.1),l(.364,.31),l(.676,.465),l(.36,.154),l(1.297,.294),l(.591,.084),l(.413,-.143),l(.372,.014),l(.396,.606),l(.419,.112),l(.141,-.029),l(.562,-.438),l(.454,.027),l(.652,.38),l(.331,.479),l(.46,-.143),l(.122,-.791),l(.181,-.085),l(.455,.465),l(.337,.099),l(.152,.154),l(-.436,.411),l(-.126,.424),l(-.176,.212),l(-.011,.438),l(-.12,.255),l(-.513,.015),l(-.51,.228),l(-.396,.34),l(-.004,.551),l(.301,.353),l(-.187,.692),l(-.381,.58),l(-.559,.481),l(-.155,.48),l(.063,.17),l(.469,.437),l(1.038,.619),l(.81,.677),l(.508,.606),l(.246,.099),l(.349,-.114),l(.264,.027),l(.782,.352),l(.742,.465),l(.434,.451),l(.679,.535),l(.335,.127),l(.442,.027),l(.784,.182),l(.25,.184),l(.218,.621),l(.167,.169),l(.507,.31),l(.76,.423),l(.537,.154),l(.422,-.072),l(.414,-.17),l(.603,-.199),l(.208,-.156),l(.736,-1.344),l(.403,-1.131),l(.194,-1.314),l(.259,-.721),l(.43,-.679),l(-.131,-.424),l(-.409,-.621),l(.022,-.311),l(.236,-.637),l(.187,-.48),l(-.164,-.282),l(-.183,-.395),l(.088,-.692),l(.104,-1.004),l(.225,-.297),l(.329,-.156),l(.158,-.311),l(-.534,-.521),l(.033,-.198),l(.576,-.947),l(.312,-.934),l(.072,-.833),l(.191,-.241),l(.279,-.297),
+N(270.934,276.123),l(.054,-.338),l(-.194,-.577),l(.011,-.522),l(.24,-.495),l(.48,-.524),l(.118,-.325),l(-.168,-.986),l(-.049,-.847),l(.103,-.522),l(.595,-2.359),l(.065,-.452),l(.443,-.524),l(.55,-.003),l(1.042,-.246),l(.25,-.101),l(.266,.098),l(.47,.364),l(.379,.407),l(.59,.814),l(.232,.38),l(.27,-.016),l(.693,-.427),l(.365,.393),l(.432,.576),l(.617,.194),l(.55,.194),l(.999,.587),l(.474,.435),l(.853,1.109),l(.131,.197),l(.163,.027),l(.333,.379),l(-.017,.269),l(-.634,.766),l(-.103,.269),l(.258,.746),l(.101,.313),l(-.059,.053),l(-.803,1.103),l(-.625,.552),l(-.775,.454),l(-.441,.156),l(-.818,.1),l(-.874,-.337),l(-.551,.044),l(-.551,.114),l(-.64,.283),l(-.38,-.042),l(-.846,-.676),l(-.637,-.252),l(-.76,-.154),l(-.469,.128),l(-.507,.072),l(-.327,-.127),l(-.548,-.563),l(-.354,-.159),
+N(418.763,73.869),l(.391,-.499),l(.389,-.262),l(.283,.081),l(.663,-.062),l(.762,-.075),l(.324,-.137),l(.891,-.787),l(.979,-.2),l(.224,.025),l(.198,-.219),l(.175,.032),l(-.349,.531),l(.11,.106),l(.026,.481),l(-.288,.156),l(-.17,.368),l(-1.675,.069),l(-.376,-.106),l(-.428,.031),l(-.14,-.062),l(-.239,.081),l(-.275,.031),l(-.153,.417),l(-.249,.218),l(-.353,.118),l(-.08,.112),l(-.229,-.155),l(-.411,-.292),M(421.68,78.002),l(-1.839,.129),l(-.116,-.016),l(.064,-.404),l(-.19,-.638),l(-.174,.015),l(-.36,-.293),l(-.233,.184),l(-.398,-.172),l(.2,-.355),l(.434,-.293),l(-.007,-.181),l(-.445,-.541),l(.018,-.256),l(.131,-.498),l(.279,-.106),l(.318,.15),l(.495,.12),l(.43,-.574),l(.299,-.091),l(.296,.437),l(.391,-.046),l(.022,-.528),l(.107,-.363),l(1.032,-.017),l(.848,.089),l(.03,.544),l(.187,.301),l(1.138,.058),l(.14,.166),l(-.307,.604),l(-.565,.142),l(-.119,-.261),l(-.331,.031),l(-.263,.136),l(.121,.278),l(-.184,.414),l(-.312,.077),l(-.069,.244),l(-.635,.035),l(.157,.229),l(-.291,.094),l(-.191,.24),l(-.061,.285),l(.003,.21),l(-.215,.338),l(.166,.084),M(427.177,77.967),l(-.139,-.149),l(-.312,-.344),l(-.405,.031),l(-.665,-.059),l(-.123,-.329),l(-.285,-.66),l(.433,-.091),l(.603,-.467),l(.356,.495),l(.521,.179),l(.2,-.271),l(.078,-.586),l(.333,-.046),l(.459,.044),l(.056,.571),l(-.134,.36),l(-.144,.09),l(-.413,.286),l(.603,.299),l(-.285,.166),l(-.499,.39),l(-.238,.09),
+N(417.259,94.301),l(-.135,-.233),l(.216,-.701),l(-.091,-.076),l(.075,-.295),l(.321,-.372),l(.054,-.358),l(.054,-.213),l(.954,-.806),l(-.605,-.193),l(-.479,.014),l(-.456,-.09),l(-.095,-.186),l(-.233,-.007),l(-.072,.048),l(-.689,.062),l(-.045,-.145),l(-.395,.055),l(-.337,-.277),l(-.428,-.319),l(-.074,-.353),l(.248,-.144),l(.044,-.288),l(-.466,-.096),l(-.41,-.312),l(-.062,-.301),l(.422,-.376),l(-.038,-.468),l(-.282,-.365),l(-.344,-.177),l(.165,-.278),l(-.143,-.217),l(-.182,-.037),l(.08,-.097),l(.235,-.133),l(.236,-.133),l(-.193,-.121),l(.201,-.275),l(.095,-.148),l(-.152,-.292),l(-.082,-.265),l(-.376,-.182),l(.07,-.182),l(.341,-.078),l(.175,-.092),l(.378,.134),l(.664,-.151),l(.32,-.152),l(.128,-.376),l(.412,-.207),l(.003,-.328),l(-.16,-.17),l(-.188,.061),l(-.44,-.134),l(.029,-.184),l(.113,-.181),l(.571,.036),l(.084,-.401),l(.345,-.361),l(-.151,-.438),l(.066,-.147),l(-.465,-.192),l(.126,-.43),l(.285,-.181),l(.371,-.028),l(1.12,-.062),l(.293,-.012),l(.095,.16),l(.195,.271),l(-.155,.158),l(.324,.113),l(.148,-.068),l(.042,-.192),l(-.159,-.204),l(.259,.012),l(.437,.169),l(-.163,-.362),l(.247,-.419),l(.687,.113),l(.614,-.091),l(-.596,-.17),l(-.084,-.227),l(.282,-.102),l(-.252,-.114),l(-.12,-.147),l(.166,-.08),l(.068,-.136),l(-.675,.08),l(-.06,-.182),l(.664,-.125),l(.218,-.193),l(-.464,-.42),l(-.324,-.341),l(.045,-.28),l(.116,.016),l(1.839,-.129),l(.254,.13),l(.565,.341),l(.271,.297),l(-.02,.228),l(-.252,.175),l(.445,-.05),l(.171,.216),l(.157,-.078),l(.283,.021),l(.235,.156),l(.45,.073),l(.866,-.121),l(.077,.208),l(-.07,.149),l(-.057,.125),l(-.665,.295),l(.316,.193),l(.649,-.136),l(.113,.182),l(.457,-.006),l(.349,-.391),l(1.059,-.136),l(.602,-.234),l(.757,-.266),l(.249,.113),l(.396,-.17),l(.184,.182),l(.085,.159),l(.634,.29),l(.182,.074),l(.526,-.12),l(.156,.12),l(.059,.34),l(.036,.227),l(.154,.102),l(.354,.125),l(.372,-.022),l(.028,.13),l(.201,.025),l(.319,.835),l(-.219,.52),l(-.391,.147),l(-.051,.328),l(.391,.102),l(.683,.429),l(-.217,.395),l(.094,.18),l(.312,.146),l(-.021,.304),l(.131,.102),l(-.118,.292),l(-.214,.124),l(-.007,.191),l(.325,.27),l(-.114,.258),l(.468,.188),l(.281,.485),l(-.435,.698),l(-.142,.115),l(-.344,-.072),l(-.031,-.278),l(-.308,-.121),l(-.436,.072),l(.282,.218),l(-.254,.084),l(-.284,.097),l(-.524,.048),l(-.124,.169),l(-.735,.024),l(-.112,.217),l(-.176,-.072),l(-.358,.061),l(-.097,.229),l(-.382,.012),l(-.078,.181),l(-.442,0),l(-.422,.201),l(-.293,-.033),l(-.26,.181),l(-.243,.168),l(.037,.056),l(.258,.378),l(.413,.098),l(-.155,.25),l(-.08,.406),l(.159,.085),l(.036,.233),l(.244,.173),l(.478,.265),l(.188,-.072),l(.573,.515),l(.431,.17),l(.184,.201),l(.219,-.084),l(.165,.18),l(.207,.036),l(.447,.633),l(-.682,.26),l(-.437,-.151),l(-.132,.027),l(-.039,.337),l(-.162,.172),l(-.968,.295),l(-.364,.227),l(.538,.571),l(-.197,.295),l(.334,.014),l(.056,.158),l(-.098,.343),l(-.082,.055),l(-.441,-.178),l(.049,-.165),l(-.146,-.117),l(-.564,.062),l(-.195,-.138),l(-.433,.035),l(-.092,.178),l(-1.25,.035),l(-.125,.171),l(-.319,.014),l(-.044,.13),L(425.4,94.7),l(-.594,.021),l(-.059,-.144),l(-.093,-.145),l(-.493,-.089),l(-.262,.055),l(-.237,-.041),l(.032,.274),l(-.452,.343),l(-.215,0),l(.078,-.195),l(-.26,-.039),l(.011,-.165),l(-.194,-.048),l(-.104,-.137),l(-.249,-.007),l(-.096,-.11),l(-.164,.137),l(-.368,-.014),l(-.664,-.261),l(-.852,-.007),l(-.149,-.089),l(-.199,.014),l(-.017,-.151),l(-.709,.199),l(.179,.199),l(-.354,.021),l(-.278,-.11),l(-.567,.158),l(-.271,-.096),l(-.272,.117),l(-.271,-.089),M(432.012,80.473),l(-.171,-.038),l(-.088,-.049),l(-.447,.08),l(-.111,-.264),l(.002,-.213),l(.647,.281),l(.168,.203),
+N(450.734,91.05),l(-.831,-.33),l(-.424,-.101),l(-.104,-.216),l(-.703,-.186),l(-.129,-.13),l(-.561,.076),l(-.508,-.041),l(-.073,.137),l(-.373,.18),l(-.46,-.254),l(-.483,.046),l(-.168,-.056),l(-.558,.204),l(-.104,.177),l(-.149,.135),l(-.18,-.12),l(-.389,.083),l(-.003,-.204),l(.026,-.083),l(-.066,-.18),l(-.331,.025),l(-.073,-.169),l(-.194,-.24),l(-.261,.024),l(-.062,.112),l(-.193,-.016),l(-.183,.24),l(-.43,.048),l(-.03,-.228),l(-.285,-.041),l(-.105,-.331),l(-.382,-.096),l(-.188,-.208),l(.02,-.249),l(-.802,-.132),l(-.311,-.181),l(-.368,.161),l(-.293,-.013),l(-.013,-.221),l(.001,-.253),l(-.329,.084),l(.181,-.283),l(-.709,.005),l(-.258,-.121),l(-.509,-.193),l(-.286,-.024),l(-.003,.145),l(.282,.302),l(-.41,.12),l(-.22,.187),l(-.318,-.072),l(-.296,-.38),l(-.55,-.247),l(.224,-.211),l(.208,-.024),l(.099,-.121),l(-.199,-.181),l(-.257,-.024),l(-.061,.066),l(-.466,.066),l(.005,-.169),l(-.278,.037),l(-.132,-.181),l(-.568,-.054),l(-.201,-.025),l(-.363,-.163),l(-.093,-.26),l(-.211,-.091),l(-.361,.018),l(-.063,.084),l(.062,.073),l(-.024,.151),l(-.375,0),l(.435,-.698),l(-.281,-.485),l(-.468,-.188),l(.114,-.258),l(-.325,-.27),l(.007,-.191),l(.214,-.124),l(.118,-.292),l(-.131,-.102),l(.021,-.304),l(-.312,-.146),l(-.094,-.18),l(.217,-.395),l(-.683,-.429),l(-.391,-.102),l(.051,-.328),l(.391,-.147),l(.219,-.52),l(-.319,-.835),l(.34,.043),l(.23,.125),l(-.093,-.193),l(.156,-.153),l(-.019,-.147),l(-.166,-.13),l(-.021,-.129),l(-.012,-.075),l(.93,-.154),l(.431,-.051),l(.516,-.152),l(1.304,-.128),l(.48,-.158),l(.797,-.479),l(1.117,-.298),l(1.515,-.303),l(1.382,-.086),l(1.067,.417),l(-.882,-.294),l(-.054,.147),l(.256,.172),l(.132,.466),l(.355,.135),l(.648,.061),l(.645,-.11),l(1.063,-.409),l(.054,.004),l(-.978,.417),l(-.19,.11),l(-.004,.123),l(.218,.012),l(.286,-.123),l(.613,-.208),l(.543,-.253),l(.842,.006),l(2.246,.115),l(2.96,-.101),l(.641,-.021),l(.04,.014),l(.679,.243),l(.284,.487),l(.099,.186),l(.016,.029),l(.09,.171),l(.16,.302),l(.615,1.5),l(.172,.502),l(-.023,.236),l(-.263,.208),l(-.44,.03),l(-.571,.244),l(-.291,.328),l(-.128,.292),l(.289,.012),l(.421,.146),l(.149,.176),l(.218,.146),l(.013,.25),l(-.315,.752),l(.126,.137),l(.161,.175),l(-.074,.242),l(.816,.835),l(.316,.193),l(-.298,.048),l(-.085,.133),l(.328,.302),l(-.009,.314),l(-.255,.24),l(-.429,.013),l(-.286,.181),l(-.599,.398),l(-1.509,1.218),l(-.082,.306),l(.044,.552),l(.349,.383),l(-.653,-.018),M(431.844,80.27),l(.232,.024),l(.069,.208),l(-.134,-.029),l(-.168,-.203),M(432.739,80.361),l(-.03,.129),l(-.278,-.024),l(-.191,.03),l(-.046,-.208),l(.174,-.012),l(.266,0),l(.106,.085),
+N(416.488,81.945),l(.151,.438),l(-.345,.361),l(-.084,.401),l(-.571,-.036),l(-.113,.181),l(-.029,.184),l(.44,.134),l(.188,-.061),l(.16,.17),l(-.003,.328),l(-.412,.207),l(-.128,.376),l(-.32,.152),l(-.664,.151),l(-.378,-.134),l(-.175,.092),l(-.341,.078),l(-.07,.182),l(.376,.182),l(.082,.265),l(.152,.292),l(-.095,.148),l(-.201,.275),l(.193,.121),l(-.236,.133),l(-.235,.133),l(-.08,.097),l(.182,.037),l(.143,.217),l(-.165,.278),l(-.672,.024),l(-.001,-.338),l(.172,-.097),l(.11,-.423),l(-.754,-.375),l(-.535,.109),l(-.292,-.375),l(-.228,-.061),l(-.173,.121),l(-.313,-.242),l(-.267,.182),l(-.307,0),l(-.307,-.012),l(.025,.157),l(-.52,.034),l(-.407,-.042),l(-.477,-.098),l(-.07,-.162),l(-.496,-.264),l(.795,-.472),l(.858,-.621),l(.273,-.354),l(.563,-1.167),l(.173,-.104),l(.644,-.12),l(.367,.251),l(.136,.268),l(-.28,.203),l(-.228,-.056),l(.066,.496),l(-.191,.124),l(1.163,.225),l(.225,-.236),l(.406,-.113),l(.134,-.417),l(-.458,-.022),l(-.123,-.124),l(-.02,-.203),l(.236,-.158),l(-.171,-.023),l(-.558,-.067),l(.072,-.328),l(-.034,-.13),l(-.023,-.034),l(.128,-.283),l(.572,-.198),l(.709,-.238),l(.654,.03),l(1.089,-.151),l(.245,.275),l(.329,.045),l(.076,.181),l(.259,0),
+N(431.57,91.965),l(-.447,-.633),l(-.207,-.036),l(-.165,-.18),l(-.219,.084),L(430.349,91),l(-.431,-.17),l(-.573,-.515),l(-.188,.072),l(-.478,-.265),l(-.244,-.173),l(-.036,-.233),l(-.159,-.085),l(.08,-.406),l(.155,-.25),l(-.413,-.098),l(-.258,-.378),l(-.037,-.056),l(.243,-.168),l(.26,-.181),l(.293,.033),l(.422,-.201),l(.442,0),l(.078,-.181),l(.382,-.012),l(.097,-.229),l(.358,-.061),l(.176,.072),l(.112,-.217),l(.735,-.024),l(.124,-.169),l(.524,-.048),l(.284,-.097),l(.254,-.084),l(-.282,-.218),l(.436,-.072),l(.308,.121),l(.031,.278),l(.344,.072),l(.142,-.115),l(.375,0),l(.024,-.151),l(-.062,-.073),l(.063,-.084),l(.361,-.018),l(.211,.091),l(.093,.26),l(.363,.163),l(.201,.025),l(.568,.054),l(.132,.181),l(.278,-.037),l(-.005,.169),l(.466,-.066),l(.061,-.066),l(.257,.024),l(.199,.181),l(-.099,.121),l(-.208,.024),l(-.224,.211),l(.55,.247),l(.296,.38),l(.318,.072),l(.22,-.187),l(.41,-.12),l(-.282,-.302),l(.003,-.145),l(.286,.024),l(.509,.193),l(.258,.121),l(.709,-.005),l(-.181,.283),l(.329,-.084),l(-.001,.253),l(.013,.221),l(.293,.013),l(.368,-.161),l(.311,.181),l(.802,.132),l(-.02,.249),l(.188,.208),l(.382,.096),l(.105,.331),l(-.071,-.012),l(-.065,.066),l(-.066,.04),l(-.105,-.026),L(442,89.998),l(-.119,.013),l(-.065,.04),l(-.132,.066),l(-.053,.079),l(-.118,.092),l(-.119,.053),l(-.132,.079),l(-.105,.013),l(-.118,.013),l(-.053,.092),l(-.026,.092),l(-.026,.132),l(-.053,.079),l(-.053,.105),l(-.145,.066),l(-.105,.04),l(-.092,-.026),l(-.079,.079),l(-.04,.092),l(-.053,.079),l(-.039,0),l(-.105,.026),l(-.066,.026),l(-.079,.053),l(-.118,.053),l(-.132,0),l(-.145,.04),l(-.093,.026),l(-.065,-.026),l(-.065,-.026),l(-.093,0),l(-.065,.026),l(-.158,0),l(-.132,-.053),l(-.092,-.053),l(-.093,.026),l(-.171,.053),l(-.026,.04),l(-.146,.119),l(-.092,.066),l(-.014,.131),l(-.065,.105),l(-.039,.044),l(-.055,-.095),l(-.545,-.2),l(-1.332,.181),l(-1.019,-.227),l(-1.374,-.341),l(-.746,.773),l(-.598,.163),l(-.483,-.099),l(-.354,-.129),l(-.145,-.014),
+N(421.683,94.397),l(.368,.014),l(.164,-.137),l(.096,.11),l(.249,.007),l(.104,.137),l(.194,.048),l(-.011,.165),l(.26,.039),l(-.078,.195),l(.215,0),l(.452,-.343),l(-.032,-.274),l(.237,.041),l(.262,-.055),l(.493,.089),l(.093,.145),l(.059,.144),L(425.4,94.7),l(.377,-.103),l(.044,-.13),l(.319,-.014),l(.125,-.171),l(1.25,-.035),l(.092,-.178),l(.433,-.035),l(.195,.138),l(.564,-.062),l(.146,.117),l(-.049,.165),l(.441,.178),l(.082,-.055),l(.098,-.343),l(-.056,-.158),l(-.334,-.014),l(.197,-.295),l(-.538,-.571),l(.364,-.227),l(.968,-.295),l(.162,-.172),l(.039,-.337),l(.132,-.027),l(.437,.151),l(.682,-.26),l(.145,.014),l(.354,.129),l(.483,.099),l(.598,-.163),l(.746,-.773),l(1.374,.341),l(1.019,.227),l(1.332,-.181),l(.545,.2),l(.055,.095),l(.053,.093),l(-.097,.581),l(.155,.26),l(.761,.547),l(-.636,.291),l(-.019,.367),l(-.701,.054),l(-.164,-.134),l(-.325,0),l(-.223,.197),l(.452,.063),l(.136,.196),l(-.157,.206),l(-.409,.062),l(-.04,.375),l(.204,.188),l(-.154,.294),l(-.346,.299),l(-.693,.157),l(.082,.393),l(-.89,-.116),l(-.298,.196),l(-.85,-.08),l(-.566,.107),l(-.646,.5),l(-.287,-.152),l(-.612,.009),l(-.179,-.107),l(-1.04,-.065),l(-.478,-.112),l(-.894,-.082),l(-1.311,-.247),l(-.32,-.391),l(-.318,-.096),l(-.023,-.363),l(-.856,.227),l(-.301,-.103),l(-.445,.11),l(-.183,-.083),l(-.366,.096),l(-.329,.411),l(-.597,-.034),l(-.631,-.171),l(.029,-.171),l(-.215,-.117),l(-.579,.332),l(-.55,-.242),l(-.008,-.144),l(-.622,-.062),l(.09,-.192),l(-.214,-.288),l(.266,-.274),l(-.193,-.336),
+N(431.763,171.063),l(-.067,.354),l(.091,.72),l(.108,.508),l(.225,.168),l(.562,.11),l(.144,.183),l(.077,.353),l(-.089,1.116),l(-.146,.227),l(-.274,.171),l(-.885,.217),l(-.291,.256),l(-.664,1.275),l(-.503,1.203),l(-.243,1.004),l(-.354,.129),l(-.369,.03),l(-.129,.354),l(-.146,1.229),l(-.192,.312),l(-.385,.045),l(-.257,.284),l(-.417,.836),l(-.944,2.223),l(-.304,.624),l(-.352,.496),l(-.368,.355),l(-.239,.114),l(-.145,-.056),l(-.722,-.97),l(-.145,-.14),l(-1.104,-.05),l(-.272,.03),l(-1.31,1.265),l(-.941,.839),l(-.495,.526),l(.02,.974),l(-.189,.552),l(-.376,.686),l(-.188,-.119),l(-.224,-.042),l(-.176,-.127),l(-.145,.212),l(.144,.296),l(-.063,.127),l(-.353,.198),l(-.56,.03),l(-.977,.101),l(-.607,-.267),l(-.288,.043),l(-.271,.368),l(-.177,.113),l(-.432,-.07),l(-1.247,-.011),l(-.528,-.225),l(-.543,-.451),l(-.416,-.72),l(-.192,-.649),l(.048,-.254),l(.208,-.254),l(-.144,-.296),l(-.513,-.069),l(-.128,-.254),l(-.464,-.55),l(-.561,-.465),l(-.608,-.253),l(-.641,-.253),l(-.272,-.31),l(-.513,.072),l(-.24,.297),l(-.336,.071),l(-.881,.044),l(-.659,.03),l(-.006,-.234),l(.121,-1.188),l(-.217,-1.439),l(.09,-.989),l(.012,-.876),l(-.003,-.48),l(-.002,-.381),l(.237,-.397),l(.257,-.03),l(.433,-.13),l(.078,-.368),l(-.116,-.592),l(.095,-.34),l(.656,-.414),l(.223,-.298),l(.19,-.566),l(.316,-.962),l(-.388,-.718),l(-.164,-.762),l(.062,-.777),l(.092,-.961),l(.158,-.849),l(.353,-.482),l(.687,-1.614),l(.676,-.4),l(1.064,-.188),l(1.174,-.108),l(1.111,.121),l(.821,.277),l(1.095,1.223),l(.209,.098),l(.37,-.002),l(1.337,-.544),l(.467,-.116),l(.354,.083),l(1.173,.742),l(.965,.277),l(.934,-.005),l(.273,-.059),l(.629,-.399),l(.403,-.327),l(.774,-.287),l(.628,-.103),l(.709,-.047),l(.531,.04),l(.884,.221),l(.724,.193),l(.609,.208),l(.259,-.058),l(.761,-.697),l(.453,-.299),l(.437,-.2),l(.951,-.034),l(.173,.394),l(.575,.52),l(.351,.407),
+N(425.506,195.522),l(-1.965,-.017),l(-1.005,.007),l(.029,-.176),l(-.208,-.536),l(.271,-.989),l(-.159,-.72),l(-.191,-.522),l(-.399,-.649),l(.433,-.396),l(-.287,-.367),l(-.24,-.056),l(-.576,.058),l(-.559,-.268),l(-.176,-.324),l(-.063,-.537),l(-.111,-.211),l(-.527,-.14),l(-.101,-.064),l(.376,-.686),l(.189,-.552),l(-.02,-.974),l(.495,-.526),l(.941,-.839),l(1.31,-1.265),l(.272,-.03),l(1.104,.05),l(.145,.14),l(.722,.97),l(.145,.056),l(.239,-.114),l(.368,-.355),l(.352,-.496),l(.304,-.624),l(.944,-2.223),l(.417,-.836),l(.257,-.284),l(.385,-.045),l(.192,-.312),l(.146,-1.229),l(.129,-.354),l(.369,-.03),l(.354,-.129),l(.243,-1.004),l(.503,-1.203),l(.664,-1.275),l(.291,-.256),l(.885,-.217),l(.274,-.171),l(.146,-.227),l(.089,-1.116),l(-.077,-.353),l(-.144,-.183),l(-.562,-.11),l(-.225,-.168),l(-.108,-.508),l(-.091,-.72),l(.067,-.354),l(.627,.109),l(.208,.083),l(.923,1.238),l(.395,.887),l(.119,1.2),l(-.134,.651),l(.169,1.228),l(.205,.578),l(.381,.633),l(.208,.21),l(.047,.127),l(-.225,.143),l(-.674,.032),l(-1.187,-.191),l(-.481,-.082),l(-.24,.1),l(-.29,.312),l(-.323,.539),l(1.088,1.067),l(1.343,1.179),l(.638,1.07),l(.382,1.057),l(.208,.183),l(-.208,.114),l(-.354,.327),l(-.866,1.743),l(-.4,.511),l(-.528,.399),l(-.144,.198),l(-.064,.424),l(.112,.776),l(.273,1.185),l(.207,.592),l(.4,.661),l(.432,.605),l(.064,.791),l(.112,.324),l(1.217,1.236),l(.498,.802),l(.194,.804),l(.209,.451),l(-.159,.185),l(.1,.861),l(-.271,.086),l(-.863,-.292),l(-.864,-.334),l(-.576,-.194),l(-.56,-.124),l(-.479,-.04),l(-.271,-.055),l(-.878,.076),l(-.863,-.023),l(-.56,-.109),l(-1.022,-.149),l(-.831,-.038),l(-1.773,-.088),l(-.88,-.093),l(-.045,.495),
+N(551.793,147.278),l(-.788,-.111),l(-.458,-.253),l(-.379,-.367),l(-.248,-1.101),l(-.112,-.141),l(-.342,-.141),l(-.662,-.083),l(-.105,-.07),l(-.014,-.268),l(.116,-.466),l(-.178,-.451),l(-.418,-.38),l(-.294,-.013),l(-1.181,.412),l(-1.229,.087),l(-.81,.143),l(-.947,.073),l(-.415,-.083),l(-.263,-.226),l(-.135,-.197),l(-.909,.143),l(-.548,.382),l(-.856,.016),l(-1.264,-.011),l(-.691,.128),l(-.723,.115),l(-.541,.03),l(.182,-.954),l(.049,-.354),l(.31,-.792),l(.285,-.383),l(.794,-.47),l(.697,-.258),l(1,-.077),l(.209,-.072),l(.188,-.255),l(.036,-.974),l(-.186,-.281),l(-.652,-.053),l(-.185,-.394),l(-.111,-1.34),l(-.168,-.38),l(-.409,-.224),l(-1.144,-.388),l(-.695,-.391),l(-.353,-.464),l(-.71,-1.068),l(-.802,-1.083),l(.887,.32),l(.63,.194),l(1.069,.248),l(.854,.235),l(.541,.039),l(.742,.024),l(1.187,-.218),l(.349,-.002),l(1.005,.135),l(1.234,-.12),l(.964,-.175),l(1.183,-.19),l(1.066,-.274),l(.247,-.156),l(.069,-.184),l(.099,-.903),l(.232,-.876),l(.188,-.34),l(.412,-.369),l(.597,-.441),l(.283,-.072),l(.644,.039),l(.516,.082),l(.254,-.086),l(.321,-.693),l(.362,-.271),l(.559,-.243),l(.562,.025),l(1.001,.375),l(.237,-.086),l(.34,-.383),l(.021,-.084),l(.139,-1.214),l(.288,-.834),l(.224,-.424),l(.292,-.228),l(.416,-.115),l(.513,-.031),l(.315,-.115),l(.199,-.255),l(-.088,-.437),l(-.129,-.281),L(556.193,125),l(-.35,-.336),l(.355,-.129),l(.997,.122),l(.698,.039),l(.513,-.215),l(.289,-.255),l(-.033,-.31),l(-.223,-.478),l(.13,-.283),l(.4,-.341),l(.524,-.355),l(.324,-.426),l(-.198,-.477),l(.119,-.483),l(-.232,-.171),l(-.01,-.336),l(-.411,-.271),l(-.01,-.32),l(.464,-.413),l(.258,-.214),l(.246,.023),l(.559,-.395),l(.272,-.19),l(.53,-.292),l(1.148,-.152),l(1.345,-.012),l(.647,.144),l(.338,-.179),l(.332,.005),l(.455,-.297),l(.358,-.004),l(.148,-.142),l(.313,.14),l(.105,.112),l(.281,-.21),l(.296,.047),l(.554,.117),l(.004,.456),l(.282,-.128),l(.625,-.009),l(.373,.323),l(.213,.247),l(.007,.501),l(-.312,.516),l(.301,.07),l(.198,.218),l(.252,.281),l(.52,-.196),l(.239,0),l(.104,.199),l(.178,.09),l(.565,.436),l(1.337,.104),l(.305,.111),l(.147,.126),l(-.207,.084),l(-.688,.37),l(-.411,.232),l(-.155,.43),l(-.071,.479),l(-.234,.116),l(-.125,-.179),l(-.621,-.102),l(-.438,.132),l(-.271,.298),l(-.262,-.116),l(-.557,.265),l(-.928,-.149),l(-.568,-.138),l(-1.708,-.325),l(-.894,.265),l(-.38,.611),l(.09,.166),l(.336,0),l(.047,.314),l(-.202,.149),l(.101,.199),l(.598,-.017),l(-.002,.347),l(-.384,.099),l(-.139,.413),l(.389,.232),l(-.116,.479),l(-.215,.132),l(.089,.248),l(.606,.298),l(-.003,.532),l(.782,-.003),l(-.039,.43),l(.188,.248),l(.702,-.05),l(.668,.377),l(.012,.211),l(-.2,.255),l(-.504,.2),l(-.643,.196),l(-.517,.248),l(.032,.366),l(.188,.377),l(-.237,.977),l(-1.512,1.503),l(.261,.38),l(-.358,.281),l(-.609,.133),l(-.353,.467),l(-.509,1.216),l(-.478,.547),l(-1.193,.496),l(-.177,.364),l(-.277,.481),l(-.563,.61),l(-.076,.38),l(-.71,.259),l(-.734,-.011),l(-.895,.314),l(-.361,-.094),l(-.158,-.518),l(-.207,-.132),l(-.404,.083),l(-.535,.463),l(-.144,.446),l(-.979,.893),l(-.186,.543),l(-.079,.269),l(.116,.38),l(.541,.163),l(.504,.085),l(.425,.014),l(-.114,.689),l(-.154,.735),l(.412,.411),l(.33,.099),l(.537,-.099),l(.112,.441),l(.216,.507),l(.654,1.333),l(-.232,.149),l(.241,.463),l(-.317,.066),l(-.15,.248),l(-.55,.083),l(-.145,-.335),l(-.183,-.062),l(-.78,.215),l(-.2,.331),l(-.735,-.099),l(-.229,-.149),l(-.835,-.021),l(-.835,-.009),l(-.259,-.052),l(.015,.728),l(-.866,.017),l(-.296,.431),l(-.37,.036),
+N(606.155,150.953),l(-.037,-.466),l(-.627,-2.479),l(-.064,-.578),l(-.4,.044),l(-.281,.115),l(-.395,1.159),l(-.224,.354),l(-.529,-.448),l(-.249,-.267),l(-.23,-.281),l(-.001,-.381),l(-.009,-.522),l(.131,-.283),l(.651,-.356),l(.498,-.13),l(.938,-.767),l(.159,-.34),l(.012,-.79),l(-.057,-.127),l(-.111,-.098),l(-2.473,-.198),l(-.559,.003),l(-.916,.062),l(-.547,-.053),l(-.537,-.109),l(-.095,-.099),l(-.079,-.719),l(-.053,-.479),l(-.129,-.324),l(-.417,-.054),l(-.489,.06),l(-.372,-.266),l(-.339,-.266),l(-1.252,-.303),l(-.234,.171),l(-.555,.525),l(-.124,.283),l(.083,.155),l(.494,.237),l(1.224,.938),l(.126,.112),l(-.047,.155),l(-.382,.101),l(-.502,.13),l(-.4,.299),l(-.548,.61),l(.339,.323),l(1.042,.431),l(.193,.21),l(-.029,.269),l(-.505,.765),l(.04,.254),l(.352,.45),l(.325,1.409),l(.402,1.775),l(.147,.532),l(-.172,.322),l(.068,.183),l(-.321,.071),l(-.35,-.056),l(-.148,-.706),l(-.198,-.084),l(-.253,.354),l(-.171,.354),l(-.172,.057),l(-.115,-.099),l(-.28,-.592),l(-.152,-.169),l(-.412,.537),l(-.504,.298),l(-.992,.397),l(-.571,.213),l(-.264,.226),l(-.05,.113),l(.106,.579),l(.183,.409),l(-.336,.495),l(-.305,.332),l(-.013,.257),l(-.548,.415),l(-.593,.467),l(-.467,.185),l(-.432,.057),l(-.746,.206),l(-.591,.362),l(-.459,.58),l(-.551,.552),l(-.88,1.061),l(-.611,.552),l(-.673,.276),l(-.675,.829),l(-.676,.467),l(-.728,.368),l(-.452,.241),l(-.427,.312),l(-.192,.509),l(-.073,.466),l(-.341,.41),l(-.649,.298),l(-.428,.058),l(-.506,-.098),l(-.258,.269),l(-.366,.887),l(-.277,.23),l(-.505,-.253),l(-.614,.142),l(-.354,.283),l(-.304,.565),l(-.165,.48),l(-.005,.508),l(.166,1.539),l(.018,.805),l(-.208,.495),l(.227,.324),l(.315,.423),l(-.015,.508),l(-.199,.607),l(-.669,1.611),l(-.341,.834),l(-.13,.636),l(.047,.649),l(.21,1.764),l(-.081,.24),l(-.643,.001),l(-.433,-.013),l(-.186,.142),l(-.334,.961),l(-.324,.594),l(.025,.127),l(.493,.352),l(-.805,.327),l(-.306,.015),l(-.77,.284),l(-.295,.41),l(-.101,.692),l(-.157,.255),l(-.367,.283),l(-.526,.255),l(-.099,.057),l(-.066,.042),l(-.231,.156),l(-.145,0),l(-.174,-.056),l(-1.417,-1.451),l(-.092,-.395),l(-.276,-.522),l(-.34,-1.397),l(-.689,-1.792),l(-.94,-1.933),l(-.979,-1.508),l(-.554,-1.722),l(-.507,-1.792),l(-.738,-1.636),l(-.82,-1.42),l(-.424,-.737),l(-.598,-1.439),l(-.363,-1.157),l(-.767,-3.274),l(-.469,-2.695),l(-.157,-1.566),l(-.051,-.24),l(.232,-.565),l(.546,-.834),l(.007,-.311),l(-.404,-.281),l(-.367,-.437),l(-.113,-.635),l(-.108,-.917),l(.065,-.622),l(-.837,.03),l(-.19,.212),l(-.235,.424),l(.009,.183),l(.276,.254),l(.074,.183),l(-.073,.438),l(-.301,.438),l(-.503,.368),l(-.812,.369),l(-.504,.114),l(-.617,.255),l(-.377,.015),l(-.861,-.281),l(-.572,-.38),l(-1.018,-1),l(-1.391,-1.268),l(-.516,-.705),l(.267,-.043),l(1.062,.125),l(.615,-.086),l(.443,-.142),l(.461,-.283),l(.521,-.608),l(-.067,-.24),l(-.156,.071),l(-.587,.114),l(-.847,-.026),l(-.607,-.112),l(-.632,-.132),l(-.5,-.218),l(-.203,-.07),l(-.929,-.661),l(-.668,-.775),l(-.057,-.197),l(.37,-.036),l(.296,-.431),l(.866,-.017),l(-.015,-.728),l(.259,.052),l(.835,.009),l(.835,.021),l(.229,.149),l(.735,.099),l(.2,-.331),l(.78,-.215),l(.183,.062),l(.145,.335),l(.55,-.083),l(.15,-.248),l(.317,-.066),l(-.241,-.463),l(.232,-.149),l(-.654,-1.333),l(-.216,-.507),l(-.112,-.441),L(557,143.058),l(-.33,-.099),l(-.412,-.411),l(.154,-.735),l(.114,-.689),l(-.425,-.014),l(-.504,-.085),l(-.541,-.163),l(-.116,-.38),l(.079,-.269),l(.186,-.543),l(.979,-.893),l(.144,-.446),l(.535,-.463),l(.404,-.083),l(.207,.132),l(.158,.518),l(.361,.094),l(.895,-.314),l(.734,.011),l(.71,-.259),l(.076,-.38),l(.563,-.61),l(.277,-.481),l(.177,-.364),l(1.193,-.496),l(.478,-.547),l(.509,-1.216),l(.353,-.467),l(.609,-.133),l(.358,-.281),l(-.261,-.38),l(1.512,-1.503),l(.237,-.977),l(-.188,-.377),l(-.032,-.366),l(.517,-.248),l(.643,-.196),l(.504,-.2),l(.2,-.255),l(-.012,-.211),l(-.668,-.377),l(-.702,.05),l(-.188,-.248),l(.039,-.43),l(-.782,.003),l(.003,-.532),l(-.606,-.298),l(-.089,-.248),l(.215,-.132),l(.116,-.479),l(-.389,-.232),l(.139,-.413),l(.384,-.099),l(.002,-.347),l(-.598,.017),l(-.101,-.199),l(.202,-.149),l(-.047,-.314),l(-.336,0),l(-.09,-.166),l(.38,-.611),l(.894,-.265),l(1.708,.325),l(.568,.138),l(.928,.149),l(.557,-.265),l(.262,.116),l(.271,-.298),l(.438,-.132),l(.621,.102),l(.125,.179),l(.234,-.116),l(.071,-.479),l(.155,-.43),l(.411,-.232),l(.688,-.37),l(.207,-.084),l(.502,-.152),l(0,.515),l(.441,1.03),l(.221,.515),l(.81,.11),l(.441,.405),l(.221,.258),l(-.331,.221),l(-.184,.184),l(.073,.589),l(.037,.552),l(.294,.221),l(.718,.096),l(.092,.078),l(.044,.182),l(.68,.099),l(-.104,.414),l(.367,.694),l(-.578,.364),l(-.376,.281),l(-.368,.049),l(-.337,-.264),l(-.033,-.364),l(-.785,.198),l(.157,.694),l(.579,.595),l(-.104,.446),l(.272,.364),l(-.215,.215),l(.099,.43),l(.198,.066),l(.477,-.314),l(.383,.248),l(.772,.843),l(.363,-.116),l(1.078,.579),l(-.116,.364),l(.756,.248),l(.154,-.066),l(.859,.596),l(-1.07,.909),l(-.281,.116),l(.008,.364),l(-.221,.265),l(.04,.396),l(-.309,.397),l(-.218,.43),l(.039,.248),l(.63,.38),l(.466,-.083),l(.665,.479),l(.704,.149),l(.305,.529),l(1.242,.678),l(.231,-.166),l(1.027,.595),l(.538,-.066),l(.182,.397),l(.835,.166),l(.304,.198),l(.15,-.066),l(.087,-.215),l(.503,0),l(.482,.265),l(.582,-.347),l(.494,.298),l(.643,.066),l(.142,.231),l(-.101,.446),l(.663,.149),l(.286,.281),l(.618,.265),l(.584,-.281),l(.313,.066),l(-.034,.331),l(.312,.248),l(.379,-.231),l(.721,.148),l(.847,.364),l(.606,-.297),l(.268,.364),l(.399,.116),l(.357,-.132),l(.271,.083),l(.687,-.132),l(.336,.05),l(.311,-.694),l(-.429,-.827),l(.07,-.876),l(.318,-.761),l(-.037,-.022),l(.345,-.129),l(.471,-.102),l(.231,.069),l(.139,.183),l(-.052,.861),l(.212,.662),l(-.092,.113),l(.235,.493),l(.637,.18),l(.559,.081),l(.869,.214),l(.085,.021),l(.544,-.074),l(.531,-.313),l(.301,.055),l(.335,.125),l(1.238,.063),l(1.036,-.062),l(.624,-.06),l(.217,-.58),l(-.017,-.282),l(-.258,-.238),l(-.313,-.069),l(-.326,-.294),l(-.042,-.24),l(.07,-.156),l(.189,-.1),l(.784,-.033),l(.714,-.216),l(.397,-.27),l(1.123,-1.107),l(.543,-.37),l(.829,-.258),l(1.105,-.726),l(.546,-.299),l(.355,-.087),l(.286,.167),l(.975,.319),l(.377,-.044),l(1.377,-.84),l(.455,.392),l(.716,.715),l(.046,.38),l(-.354,.454),l(-.138,.269),l(.102,.056),l(.3,.055),l(.657,.081),l(.73,.179),l(.624,.081),l(-.018,.508),l(-.112,.24),l(-.596,.582),l(-.072,.283),l(.184,.718),l(-.295,.058),l(-.853,-.221),l(-.558,-.082),l(-.312,.129),l(-.719,.512),l(-1.446,.883),l(-.197,.354),l(.057,.761),l(-.136,.382),l(-.938,1.134),l(-.045,.212),l(.192,.408),l(0,.324),l(-1.148,2.25),l(-.17,.128),l(-.309,-.013),l(-.391,-.11),l(-.811,-.32),l(-.133,.283),l(.146,1.594),l(-.191,.354),l(-.401,.144),l(-.228,.185),l(.152,.93),l(-.015,.678),l(-.273,.368),l(-.25,.129),l(-.255,-.14),l(-.595,-.152),
+N(478.516,135.173),l(.052,-.292),l(-.049,-.169),l(-.197,-.107),l(.107,-.175),l(.292,-.919),l(-.015,-.818),l(.517,-1.09),l(.3,-.891),l(-.004,-.409),l(.133,-.283),l(.07,-.749),l(.033,-.847),l(.062,-.174),l(.026,-.278),l(-.065,-.411),l(.037,-.119),l(.102,-.063),l(1.902,1.034),l(.365,.167),l(.253,-.016),l(1.139,-.656),l(2.96,-1.541),l(.161,-.071),l(.87,1.632),l(.311,.69),l(.093,.056),l(-.46,.44),l(-.257,.171),l(-.97,.26),l(-2.586,.622),l(-.646,.229),l(1.146,1.32),l(.698,.885),l(-.765,.724),l(-.456,.37),l(-.24,.072),l(-1.249,.332),l(-.462,.61),l(-.975,.782),l(-2.079,-.299),l(-.154,-.017),
+N(456.028,151.46),l(-.021,1.003),l(.033,3.078),l(-.088,.17),l(-1.561,-.019),l(-.52,-.011),l(-.073,1.13),l(-1.981,-.963),l(-5.577,-2.792),l(-4.941,-2.372),l(-5.231,-2.554),l(-.41,.059),l(-1.818,.858),l(-1.519,.729),l(-.231,.157),l(-1.192,-.911),l(-.258,-.182),l(-1.008,-.333),l(-1.646,-.442),l(-.832,-.137),l(-.098,-.07),l(-.86,-1.506),l(-.178,-.168),l(-1.028,-.347),l(-.702,-.165),l(-.544,.201),l(-.244,-.154),l(-.562,-.873),l(-.249,-.917),l(-1.272,-1.702),l(.152,-.283),l(.249,-.128),l(.666,-.47),l(.086,-.17),l(.061,-.664),l(-.457,-1.156),l(.158,-.495),l(.16,-.622),l(-.101,-.748),l(.1,-.862),l(-.052,-.691),l(-.147,-.833),l(-.88,-1.605),l(.241,-.383),l(.204,-.156),l(.22,-.143),l(.864,-.598),l(.155,-.227),l(.147,-.537),l(-.274,-.973),l(.126,-.354),l(1.473,-1.138),l(.579,-.314),l(.802,-.428),l(.093,-.269),l(-.064,-.564),l(.093,-.981),l(1.436,.59),l(.995,.309),l(.521,.013),l(.863,-.129),l(.604,.027),l(1.552,.223),l(.614,.479),l(.794,.224),l(.486,-.001),l(.342,.197),l(.173,.226),l(.26,.819),l(.39,.564),l(.688,.591),l(.378,.126),l(.678,.14),l(1.031,.083),l(.447,.07),l(.973,.224),l(.639,.224),l(.552,.281),l(1.289,.788),l(.84,.464),l(.465,.013),l(.441,-.128),l(.758,-.411),l(.706,-.623),l(.3,-.523),l(0,-.254),l(-.276,-.621),l(-.112,-.579),l(.044,-.41),l(.237,-.537),l(.661,-.58),l(.333,-.198),l(.554,-.241),l(.644,-.298),l(1.376,-.413),l(1.205,.054),l(.728,.154),l(.844,.365),l(.188,.169),l(.043,.508),l(.281,.253),l(.301,.014),l(.945,.125),l(.712,.309),l(.45,.027),l(1.286,.04),l(.302,.197),l(.068,.381),l(.276,.296),l(-.099,.208),l(-.369,.228),l(-.21,.34),l(.354,1.283),l(-.231,.425),l(-.322,.553),l(-.056,.311),l(.185,.945),l(.388,.916),l(.042,.367),l(-.015,.988),l(-.019,3.869),l(-.016,1.468),l(-.008,2.048),l(-.001,.692),l(.045,2.81),l(-.021,1.342),l(.011,2.612),
+N(478.212,134.714),l(-.834,-1.746),l(-.796,-1.944),l(-.124,-.289),l(.109,-.066),l(.679,-.851),l(.219,-.438),l(.183,-.507),l(.171,-.566),l(.188,-.822),l(.116,.018),l(.137,-.093),l(.006,-.204),l(.028,-.315),l(.372,-.051),l(.151,.022),l(.07,.099),l(.355,-.07),l(.009,-.167),l(.067,-.193),l(.088,-.041),l(.07,.023),l(.183,.346),l(.201,.128),l(.021,.183),l(-.047,.122),l(.082,.085),l(-.102,.063),l(-.037,.119),l(.065,.411),l(-.026,.278),l(-.062,.174),l(-.033,.847),l(-.07,.749),l(-.133,.283),l(.004,.409),l(-.3,.891),l(-.517,1.09),l(.015,.818),l(-.292,.919),l(-.107,.175),l(-.067,.068),l(-.042,.042),
+N(495.751,163.817),l(-.03,-.184),l(-.112,-.537),l(-.508,-.945),l(-.73,-.987),l(-.438,-.493),l(-.846,-.69),l(-.51,-.875),l(-.814,-1.876),l(-.474,-.889),l(-.28,-.409),l(-.794,-.507),l(-1.271,-.689),l(-.527,-.634),l(-.8,-1.17),l(-.081,-.96),l(-.061,-.776),l(-.002,-.89),l(-.104,-.381),l(-.312,-.663),l(-.944,-1.65),l(-.357,-.494),l(-.758,-.62),l(-.459,-.225),l(-.499,-.126),l(-.308,-.282),l(-.483,-.578),l(.149,-.692),l(-.111,-.437),l(-.474,-.818),l(-.747,-1.072),l(-.793,-.902),l(-1.069,-1.494),l(-.525,-.931),l(-.352,-.479),l(-.722,-.761),l(-.524,-.056),l(-.248,0),l(.077,-.296),l(.193,-.283),l(.311,-1.314),l(.104,-.583),l(.154,.017),l(2.079,.299),l(.975,-.782),l(.462,-.61),l(1.249,-.332),l(.24,-.072),l(.456,-.37),l(.765,-.724),l(-.698,-.885),l(-1.146,-1.32),l(.646,-.229),l(2.586,-.622),l(.97,-.26),l(.257,-.171),l(.46,-.44),l(.093,.056),l(1.129,.459),l(.757,.222),l(2.034,.934),l(1.14,.473),l(2.44,1.101),l(.258,.196),l(.399,.633),l(.141,.07),l(.973,-.048),l(.135,.366),l(-.118,.396),l(.047,.818),l(.118,.253),l(.812,.277),l(1.557,.697),l(4.048,.132),l(1.657,.259),l(.408,.054),l(.334,.619),l(.185,.239),l(1.07,.05),l(.644,-.044),l(.043,.121),l(.277,.648),l(.365,.494),l(.178,.663),l(.052,.113),l(.615,-.029),l(.248,.126),l(-.204,.237),l(.194,.187),l(.184,.282),l(.234,.067),l(.234,.27),l(.886,.168),l(.423,.437),l(-.07,.28),l(-.235,.035),l(-.111,.25),l(.316,.381),l(-.188,.593),l(-.122,.198),l(.165,.268),l(.302,.666),l(.149,-.117),l(.372,.282),l(.105,.616),l(.422,.696),l(-.028,.423),l(.222,.268),l(.16,.197),l(.287,.084),l(.136,-.007),l(.336,.083),l(1.043,2.198),l(.548,1.154),l(.261,.902),l(3.362,.375),l(3.387,.446),l(.839,.108),l(.584,-.469),l(.152,-.057),l(.581,.914),l(.155,1.453),l(-5.346,2.982),l(-2.35,1.213),l(-1.691,.815),l(-.169,.085),l(-1.148,.346),l(-3.097,1.035),l(-4.618,1.566),l(-.484,.229),l(-.041,.127),l(-.383,.397),l(-1.907,2.271),l(-2.042,2.667),l(-1.302,-2.703),l(-1.211,-2.45),l(-.452,-.012),l(-.715,.047),l(-.446,-.125),l(-.671,-.151),l(-.216,.1),l(-.174,.185),l(-.11,.495),l(.041,.678),l(-.113,.565),l(-.692,.563),
+N(476.458,130.735),l(.124,.289),l(.796,1.944),l(.834,1.746),l(-.38,.381),l(-.479,1.145),l(-.334,1.413),l(-.171,.593),l(-.18,.156),l(-.407,-.07),l(-.448,-.521),l(-.782,-.676),l(-.386,-.494),l(-.338,-.988),l(-.521,-.45),l(-.289,-.621),l(-.17,-.479),l(-.198,-.353),l(-.466,.17),l(-.267,.523),l(.604,1.34),l(.131,.381),l(.634,.86),l(.933,1.042),l(.473,1.199),l(.526,.973),l(.277,.818),l(.391,.465),l(.912,1.735),l(1.072,1.904),l(.428,.705),l(.586,.549),l(.451,.352),l(.151,.183),l(-.241,.17),l(-.285,.1),l(-.043,.155),l(.238,1.087),l(.252,.467),l(.127,.238),l(.812,.591),l(.397,.168),l(.406,.521),l(.416,.38),l(.311,.56),l(-10.382,-.006),l(-2.138,-.001),l(-2.774,.002),l(-2.513,.015),l(-2.268,-.029),l(-1.664,.01),l(-1.241,.007),l(-1.614,-.019),l(-.914,.005),l(-.819,.089),l(-.011,-2.612),l(.021,-1.342),l(-.045,-2.81),l(.001,-.692),l(.008,-2.048),l(.016,-1.468),l(.019,-3.869),l(.015,-.988),l(-.042,-.367),l(-.388,-.916),l(-.185,-.945),l(.056,-.311),l(.322,-.553),l(.231,-.425),l(-.354,-1.283),l(.21,-.34),l(.369,-.228),l(.099,-.208),l(.337,.168),l(.553,-.015),l(.628,-.1),l(.786,-.001),l(1.513,.293),l(.587,.115),l(.779,.122),l(.721,.154),l(.348,.409),l(.4,0),l(.928,.083),l(.998,.394),l(.628,.069),l(.245,-.127),l(.72,-.538),l(.925,-.397),l(.837,-.186),l(.78,-.27),l(.375,-.072),l(.346,.056),l(.535,-.001),l(.723,.126),l(.202,.465),l(.596,.366),l(.518,-.156),l(.64,.267),l(.382,.027),l(.561,-.401),l(.241,.179),l(.277,.056),l(.674,.13),l(.338,-.062),l(.46,-.165),l(.51,-.308),
+N(580.625,132.873),l(.24,.43),l(.299,.05),l(.276,-.314),l(.134,-.513),l(.275,.066),l(.186,-.182),l(1.079,.199),l(.132,.612),l(.867,.264),l(.726,.595),l(.472,.215),l(.15,-.132),l(.454,.281),l(.657,.794),l(.158,-.166),l(.71,-.116),l(.289,.166),l(.178,.38),l(-.069,.281),l(1.451,.893),l(.333,-.248),l(.326,.017),l(-.175,.396),l(.014,.265),l(.935,.066),l(.365,-.066),l(.54,.496),l(.141,.463),l(.237,-.099),l(-.01,-.364),l(.685,.38),l(.271,-.083),l(.08,-.331),l(.407,0),l(.63,.281),l(.319,.364),l(.849,-.066),l(.341,.116),l(.339,-.281),l(.655,.159),l(.037,.022),l(-.318,.761),l(-.07,.876),l(.429,.827),l(-.311,.694),l(-.336,-.05),l(-.687,.132),l(-.271,-.083),l(-.357,.132),l(-.399,-.116),l(-.268,-.364),l(-.606,.297),l(-.847,-.364),l(-.721,-.148),l(-.379,.231),l(-.312,-.248),l(.034,-.331),l(-.313,-.066),l(-.584,.281),l(-.618,-.265),l(-.286,-.281),l(-.663,-.149),l(.101,-.446),l(-.142,-.231),l(-.643,-.066),l(-.494,-.298),l(-.582,.347),l(-.482,-.265),l(-.503,0),l(-.087,.215),l(-.15,.066),l(-.304,-.198),l(-.835,-.166),l(-.182,-.397),l(-.538,.066),l(-1.027,-.595),l(-.231,.166),l(-1.242,-.678),l(-.305,-.529),l(-.704,-.149),l(-.665,-.479),l(-.466,.083),l(-.63,-.38),l(-.039,-.248),l(.218,-.43),l(.309,-.397),l(-.04,-.396),l(.221,-.265),l(-.008,-.364),l(.281,-.116),l(1.07,-.909),
+N(374.125,167.166),l(-1.41,-.924),l(-.661,-.604),l(-.537,-.788),l(-.292,-.662),l(-.15,-.183),l(-.312,-.154),l(-.567,-.053),l(-.655,-.307),l(-.695,-.561),l(-.604,-.194),l(-.567,-.025),l(-1.209,.12),l(-1.821,.194),l(-.523,.417),l(.06,-.502),l(.563,-1.301),l(.188,-.819),l(.13,-1.13),l(-.162,-1.101),l(-.3,-.791),l(-.624,-.747),l(.267,-.283),l(.182,-.424),l(.067,-.975),l(-.065,-.537),l(-.174,-.353),l(-.808,-.817),l(-.297,-.112),l(-.393,.438),l(-.055,-.197),l(-.013,-.381),l(.08,-.689),l(.474,.013),l(5.055,.056),l(2.396,.014),l(.732,-.033),l(.212,.027),l(-.051,-1.102),l(-.164,-1.539),l(.01,-.707),l(.249,-.383),l(.575,-.427),l(.726,-.315),l(.809,-.287),l(.144,-.128),l(.099,-2.6),l(.07,-2.261),l(.047,-.509),l(.557,-.074),l(6.005,.022),l(.56,.011),l(.104,-.396),l(-.002,-.65),l(.03,-1.752),l(2.829,1.509),l(4.12,2.42),l(1.414,.91),l(-.523,.13),l(-3.231,.089),l(.049,.776),l(.908,7.962),l(.19,1.468),l(.178,1.736),l(.325,2.753),l(.202,2.386),l(.186,1.468),l(.724,.787),l(-.379,1.613),l(-6.457,.01),l(-1.953,.026),l(-.945,.344),l(-.387,.031),l(-.485,-.054),l(-.713,-.137),l(-.535,-.124),l(-.06,.212),l(.055,.296),l(-.107,.269),l(-.454,-.082),l(-.214,-.168),l(-.568,-.83),l(-.261,-.111),l(-.402,.073),l(-.252,.256),l(-.181,.552),l(-.018,.607),l(-.332,.284),
+N(476.114,191.139),l(-.563,.54),l(-.626,.314),l(-.367,.016),l(-.303,-.196),l(-.303,-.097),l(-.88,.132),l(-.881,.33),l(-.911,.048),l(-.942,-.234),l(-.495,-.011),l(-.464,.087),l(-.496,.229),l(-1.288,-1.32),l(-1.032,-.926),l(-.207,-.083),l(-.258,.157),l(-.436,.582),l(-.16,.1),l(-.684,-.631),l(-.384,-.012),l(-.529,.37),l(-.258,.242),l(-.352,.016),l(-.366,-.195),l(-.525,-.477),l(-.348,-.493),l(-.795,-.772),l(-.143,-.239),l(-.031,-.07),l(.083,-.679),l(-.157,-.352),l(-.558,-.477),l(-.814,-.165),l(-.367,-.224),l(-.285,-.436),l(-.059,-.72),l(-.206,-.366),l(-.207,-.168),l(-.717,-.434),l(-.558,-.378),l(-.445,-.351),l(-.173,-.395),l(.148,-.481),l(-.223,-.267),l(-.351,-.196),l(-.799,-.235),l(-.797,-.447),l(-.223,-.225),l(-.252,-.493),l(-.207,-.126),l(-.272,.001),l(-.802,.019),l(-.158,-.211),l(-.01,-.664),l(.416,-1.472),l(-.122,-.536),l(-.347,-.549),l(-1.342,-1.673),l(.15,-.425),l(-.026,-.367),l(-.266,-.465),l(-.444,-.407),l(-.155,-.295),l(-.137,-.522),l(-.221,-1.228),l(-.141,-.225),l(-.338,-.012),l(-.517,.088),l(-.219,-.352),l(.316,-.609),l(.542,-.596),l(.087,-.34),l(-.342,-.662),l(.053,-.24),l(.718,-.428),l(.149,-.212),l(-.131,-.677),l(.122,-.438),l(.583,-.809),l(.417,-1.245),l(.229,-.143),l(1.245,-.05),l(.839,.009),l(.04,-.283),l(-.031,-1.229),l(-.036,-1.002),l(.038,-.749),l(-.085,-2.5),l(.036,-.636),l(.016,-.946),l(.02,-1.045),l(.073,-1.13),l(.52,.011),l(1.561,.019),l(.088,-.17),l(-.033,-3.078),l(.021,-1.003),l(.819,-.089),l(.914,-.005),l(1.614,.019),l(1.241,-.007),l(1.664,-.01),l(2.268,.029),l(2.513,-.015),l(2.774,-.002),l(2.138,.001),l(10.382,.006),l(.229,.414),l(.499,1.115),l(.281,3.501),l(.319,1.637),l(.182,.211),l(.625,.394),l(.595,.366),l(.617,.507),l(.491,.197),l(.254,.223),l(-.238,.199),l(-.277,.327),l(-.306,.566),l(-.369,.242),l(-.511,.172),l(-.427,.116),l(-.153,.142),l(-.235,.496),l(-.348,.171),l(-.73,.033),l(-.204,.552),l(-.038,.452),l(-.202,.862),l(-.242,.722),l(-.541,1.302),l(-.068,.495),l(.092,.903),l(-.013,.679),l(-.001,.083),l(-.148,.947),l(-.398,1.231),l(-.214,1.061),l(-1.04,.331),l(-.414,.37),l(-.693,1.134),l(-.154,.34),l(-.191,1.088),l(-.141,1.074),l(-.197,.185),l(-.274,.044),l(-.431,-.096),l(-.259,.072),l(-.163,.114),l(-.187,1.103),l(-.115,.933),l(-.2,1.272),l(.039,.522),l(-.229,1.159),l(-.469,.299),l(-.288,-.012),l(-.928,-.122),l(-.321,.03),l(-.188,.806),l(-.054,.466),l(.094,.141),l(.479,.11),l(.734,.123),l(.334,.167),l(.805,.913),l(1.06,1.067),l(.751,1.464),l(.345,.732),l(.348,.421),l(.462,.28),l(.415,.097),l(.269,.309),l(.117,.988),l(-.034,.17),l(-.018,.141),l(-.079,-.028),l(-.464,.017),l(-1.009,.133),l(-.832,.005),l(-.671,-.052),l(-.291,.327),l(-.678,.795),
+N(518.402,163.079),l(-.282,.097),l(-.538,.27),l(-.656,.566),l(-.265,.297),l(-.166,.508),l(-.062,.41),l(-.41,.326),l(-1.418,.652),l(-2.27,.641),l(-1.285,.412),l(-.843,.312),l(-.356,.297),l(-.473,.622),l(-.436,.269),l(-.506,.114),l(-.593,-.069),l(-.521,.072),l(-.83,.439),l(-.65,.396),l(-.956,.397),l(-.752,.199),l(-1.16,.003),l(-.562,-.013),l(-.297,.128),l(-.386,.312),l(-.272,.297),l(-.45,.312),l(-.511,.241),l(-.389,.043),l(-.239,-.042),l(-.535,.411),l(-.839,.058),l(-.494,-.112),l(-.623,-.437),l(-.17,-.155),l(-.317,-.437),l(-.084,-.254),l(.18,-.721),l(-.081,-.635),l(-.312,-.507),l(-.341,-1.171),l(-.572,-1.919),l(-.072,-.438),l(.268,-.707),l(-.124,-.748),l(.692,-.563),l(.113,-.565),l(-.041,-.678),l(.11,-.495),l(.174,-.185),l(.216,-.1),l(.671,.151),l(.446,.125),l(.715,-.047),l(.452,.012),l(1.211,2.45),l(1.302,2.703),l(2.042,-2.667),l(1.907,-2.271),l(.383,-.397),l(.041,-.127),l(.484,-.229),l(4.618,-1.566),l(3.097,-1.035),l(1.148,-.346),l(.169,-.085),l(.092,.663),l(.226,.634),l(.604,.971),l(.933,1.364),l(.127,.253),l(-.006,.296),l(.483,.689),l(.241,.306),
+N(494.64,172.627),l(-.261,-.166),l(-.476,-.633),l(-.475,-.159),l(-.437,-.911),l(-1.267,-.792),l(-.277,-.594),l(-.673,-.673),l(-.594,-.119),l(-.871,-.554),l(-.555,.079),l(-.158,-.158),l(-.237,.04),l(-.277,-.198),l(-.356,.159),l(-.476,.079),l(-.277,-.436),l(-.158,.237),l(-.436,.198),l(-.792,.079),l(-.554,-.594),l(-.238,0),l(-.396,-.317),l(-.832,1.663),l(-.436,-.871),l(-.475,.079),l(-.277,.356),l(-.396,-.08),l(-.349,.041),l(.013,-.679),l(-.092,-.903),l(.068,-.495),l(.541,-1.302),l(.242,-.722),l(.202,-.862),l(.038,-.452),l(.204,-.552),l(.73,-.033),l(.348,-.171),l(.235,-.496),l(.153,-.142),l(.427,-.116),l(.511,-.172),l(.369,-.242),l(.306,-.566),l(.277,-.327),l(.238,-.199),l(.098,.129),l(.718,2.102),l(.442,1.623),l(.689,1.919),l(.618,.832),l(.205,-.212),l(-.072,-.479),l(.093,-.226),l(.31,.253),l(.4,.677),l(.384,.395),l(.534,-.043),l(.242,.014),l(1.338,1.282),l(.809,.916),l(.124,.099),l(.706,.055),l(.618,.874),l(.021,.24),l(-.025,.198),l(.118,.212),l(.507,.182),l(.915,.869),l(-.075,.094),l(-.529,.78),l(-.331,-.182),l(-.396,-.125),l(-.214,.114),l(-.055,.085),
+N(370.147,172.035),l(-2.301,-.043),l(-1.045,.006),l(-.505,.384),l(-.51,.187),l(-.74,-.024),l(-.819,.047),l(-.463,.139),l(-.009,-.003),l(-.278,-.226),l(-.169,-.409),l(.104,-.424),l(-.095,-.55),l(-.018,-.198),l(.001,-.046),l(1.695,.014),l(0,-.434),l(.155,0),l(.341,-.016),l(.186,-.077),l(.248,.062),l(.294,-.046),l(.124,-.093),l(.016,-.263),l(.077,-.078),l(.14,-.016),l(.155,.155),l(.232,.124),l(.356,.108),l(.046,.108),l(.139,.047),l(.217,-.031),l(.263,.108),l(.186,.17),l(.434,0),l(.186,-.108),l(.263,-.108),l(.279,0),l(.108,-.124),l(.016,-.124),l(-.155,-.217),l(-.202,-.077),l(-.201,.031),l(-.294,.093),l(-.108,.093),l(-.248,.062),l(-.279,-.186),l(-.031,-.186),l(-.186,-.108),l(-.17,-.015),l(-.186,.124),l(-.139,-.108),l(-.108,-.217),l(-.155,-.108),l(-.201,.031),l(-.17,-.062),l(-.387,.17),l(-.108,-.108),l(-.155,.046),l(-.202,.124),l(-.093,.294),l(-1.664,0),l(-.007,-.067),l(-.028,-.269),l(-.377,-.268),l(-.068,-.155),l(-.057,-.367),l(-.386,-.635),l(-.399,-.494),l(-.379,-.31),l(-.472,-.183),l(.54,-.34),l(.723,-.75),l(.588,-1.004),l(.334,-.82),l(.099,-.826),l(.523,-.417),l(1.821,-.194),l(1.209,-.12),l(.567,.025),l(.604,.194),l(.695,.561),l(.655,.307),l(.567,.053),l(.312,.154),l(.15,.183),l(.292,.662),l(.537,.788),l(.661,.604),l(1.41,.924),l(-.506,.399),l(-.057,.339),l(.251,1.1),l(.144,.663),l(.22,.479),l(.181,.197),l(.554,.322),l(.265,.337),l(.082,.833),l(-.004,.565),l(-.062,.142),l(-.146,-.042),l(-.963,.091),l(-.658,.074),l(-.725,-.081),l(-.503,-.209),l(-.795,-.32),l(-1.255,-.021),
+N(495.973,175.881),l(-.363,.807),l(.083,.409),l(.428,.732),l(.587,.844),l(.732,.801),l(.596,.547),l(.634,.321),l(.54,.209),l(4.26,1.443),l(1.447,.472),l(1.875,-.04),l(.236,.154),l(-4.087,4.205),l(-1.562,1.69),l(-.813,.909),l(-.8,.004),l(-1.693,-.046),l(-.626,.088),l(-.562,.215),l(-.388,.214),l(-.502,.497),l(-.294,.426),l(-.337,.115),l(-1.216,.078),l(-.305,.101),l(-.453,.511),l(-1.103,-.106),l(-.461,-.181),l(-.46,-.336),l(-.271,-.098),l(-.852,.358),l(-.675,.343),l(-.258,.199),l(-.71,.753),l(-.16,.114),l(-.847,-.094),l(-.67,-.193),l(-1.373,-.133),l(-.335,-.041),l(-2.353,-1.525),l(-.604,-.293),l(-.749,-.193),l(-1.054,-.149),l(-.319,-.069),l(.018,-.141),l(.034,-.17),l(-.117,-.988),l(-.269,-.309),l(-.415,-.097),l(-.462,-.28),l(-.348,-.421),l(-.345,-.732),l(-.751,-1.464),l(-1.06,-1.067),l(-.805,-.913),l(-.334,-.167),l(-.734,-.123),l(-.479,-.11),l(-.094,-.141),l(.054,-.466),l(.188,-.806),l(.321,-.03),l(.928,.122),l(.288,.012),l(.469,-.299),l(.229,-1.159),l(-.039,-.522),l(.2,-1.272),l(.115,-.933),l(.187,-1.103),l(.163,-.114),l(.259,-.072),l(.431,.096),l(.274,-.044),l(.197,-.185),l(.141,-1.074),l(.191,-1.088),l(.154,-.34),l(.693,-1.134),l(.414,-.37),l(1.04,-.331),l(.214,-1.061),l(.398,-1.231),l(.148,-.947),l(.001,-.083),l(.349,-.041),l(.396,.08),l(.277,-.356),l(.475,-.079),l(.436,.871),l(.832,-1.663),l(.396,.317),l(.238,0),l(.554,.594),l(.792,-.079),l(.436,-.198),l(.158,-.237),l(.277,.436),l(.476,-.079),l(.356,-.159),l(.277,.198),l(.237,-.04),l(.158,.158),l(.555,-.079),l(.871,.554),l(.594,.119),l(.673,.673),l(.277,.594),l(1.267,.792),l(.437,.911),l(.475,.159),l(.476,.633),l(.261,.166),l(-.594,.921),l(-.488,.61),l(-.105,.254),l(-.029,.396),l(.202,1.157),l(.5,-.074),l(.892,-.246),l(.4,.04),l(.556,.195),
+N(364.011,169.929),l(1.664,0),l(.093,-.294),l(.202,-.124),l(.155,-.046),l(.108,.108),l(.387,-.17),l(.17,.062),l(.201,-.031),l(.155,.108),l(.108,.217),l(.139,.108),l(.186,-.124),l(.17,.015),l(.186,.108),l(.031,.186),l(.279,.186),l(.248,-.062),l(.108,-.093),l(.294,-.093),l(.201,-.031),l(.202,.077),l(.155,.217),l(-.016,.124),l(-.108,.124),l(-.279,0),l(-.263,.108),l(-.186,.108),l(-.434,0),l(-.186,-.17),l(-.263,-.108),l(-.217,.031),l(-.139,-.047),l(-.046,-.108),l(-.356,-.108),l(-.232,-.124),l(-.155,-.155),l(-.14,.016),l(-.077,.078),l(-.016,.263),l(-.124,.093),l(-.294,.046),l(-.248,-.062),l(-.186,.077),l(-.341,.016),l(-.155,0),l(0,.434),l(-1.695,-.014),l(.019,-.477),l(.173,-.198),l(.434,-.058),l(.093,-.155),l(-.006,-.059),
+N(495.973,175.881),l(-.556,-.195),l(-.4,-.04),l(-.892,.246),l(-.5,.074),l(-.202,-1.157),l(.029,-.396),l(.105,-.254),l(.488,-.61),l(.594,-.921),l(.055,-.085),l(.214,-.114),l(.396,.125),l(.331,.182),l(.529,-.78),l(.075,-.094),l(.627,.596),l(.285,.465),l(.036,.282),l(-.118,.113),l(-.361,.185),l(-.438,.1),l(-.56,.312),l(-.566,.467),l(.253,.127),l(.645,-.058),l(.321,.013),l(.434,.124),l(-.114,.26),l(-.37,.468),l(-.34,.567),
+N(363.763,172.732),l(.463,-.139),l(.819,-.047),l(.74,.024),l(.51,-.187),l(.505,-.384),l(1.045,-.006),l(2.301,.043),l(-.111,.792),l(.115,.847),l(-.186,.312),l(-.333,.186),l(-.513,.031),l(-.401,.017),l(-.381,.186),l(-.789,.64),l(-.321,.372),l(-.047,-.126),l(-.192,0),l(-.501,-.14),l(-.165,-.254),l(.121,-.41),l(.33,-.438),l(.253,-.212),l(-.131,-.141),l(-.372,-.112),l(-.498,.015),l(-.415,.17),l(-.161,0),l(-.616,-.281),l(-.36,-.296),l(-.069,-.24),l(-.323,-.084),l(-.316,-.137),
+N(383.005,177.596),l(-.379,.397),l(-.264,.623),l(.214,.409),l(.695,.405),l(.197,.31),l(-.125,.283),l(-.332,.37),l(.309,.351),l(.648,.561),l(.051,.226),l(-.19,.143),l(-.304,-.012),l(-.548,-.223),l(-.304,.03),l(-.158,.199),l(-.03,.127),l(.296,.549),l(.179,.21),l(-.188,.354),l(-.602,.554),l(-.047,.099),l(-.339,-.224),l(-.337,-.097),l(-.143,.086),l(-.756,.922),l(-.321,-.026),l(-.404,-.322),l(-.277,-.323),l(.044,-.283),l(-.009,-.65),l(-.22,-.846),l(-.165,-.366),l(-.338,-.097),l(-.528,.046),l(-.367,.143),l(-.252,.242),l(-.193,.376),l(-.26,-.656),l(-.165,-1.369),l(-.252,-.761),l(-.475,-.619),l(-.456,-.421),l(-.42,-.224),l(-.466,-.04),l(-1.137,.148),l(-.594,-.039),l(-.238,.157),l(-.643,.866),l(-1.088,1.235),l(.051,-.392),l(-.312,-.55),l(-.764,-.775),l(-.122,-.649),l(-.84,-.366),l(-.908,-.563),l(-.277,-.296),l(.09,-.396),l(-.053,-.311),l(-.547,-.041),l(-.323,-.098),l(-.115,-.155),l(.059,-.311),l(-.038,-.1),l(.321,-.372),l(.789,-.64),l(.381,-.186),l(.401,-.017),l(.513,-.031),l(.333,-.186),l(.186,-.312),l(-.115,-.847),l(.111,-.792),l(1.255,.021),l(.795,.32),l(.503,.209),l(.725,.081),l(.658,-.074),l(.963,-.091),l(.146,.042),l(.113,.028),l(.18,.183),l(.882,.715),l(.536,.265),l(.252,-.256),l(.475,-.413),l(.338,.026),l(.356,.153),l(.565,.152),l(.43,-.243),l(.3,-.327),l(.684,-.428),l(.323,.055),l(.246,.281),l(.022,.339),l(.271,.832),l(.265,.451),l(.474,.478),l(.349,.718),l(.194,1.143),l(.559,.901),
+N(266.015,188.956),l(.103,.169),l(-.163,.326),l(-.592,.385),l(-1.74,.9),l(-.807,.23),l(-.557,.074),l(-.465,-.054),l(-.284,.115),l(-.232,1.117),l(-.348,.115),l(-.628,-.618),l(-.344,-.224),l(-1.149,.035),l(-.385,-.04),l(-.896,-.461),l(-.309,-.125),l(-.159,.029),l(-.041,.184),l(.616,.688),l(.391,.69),l(.302,1.524),l(.079,.55),l(.166,.239),l(.96,.051),l(.434,.125),l(.15,.253),l(-.265,.27),l(-.569,.272),l(-.652,.131),l(-.203,.199),l(-.259,.666),l(-.235,.213),l(-.652,.173),l(-.554,.286),l(-.74,.654),l(-.645,.739),l(-.271,.016),l(-.186,-.776),l(-.083,-.183),l(-.757,.697),l(-.414,.073),l(-.482,-.223),l(-.694,-.546),l(-.432,-.054),l(-.199,-.437),l(-.088,-.452),l(-.161,-.861),l(-.138,-.437),l(-.148,-.168),l(-.797,-1.182),l(-.51,-.491),l(.479,-.526),l(.731,-.612),l(-.121,-.282),l(-.486,-.647),l(-.256,-.437),l(-.447,-.789),l(-.162,-.804),l(-.048,-.367),l(-.035,-.438),l(-.026,-.254),l(.147,-.326),l(.379,-.511),l(.085,-1.004),l(.409,-.525),l(-.644,-.081),l(-1.99,.224),l(-.76,.174),l(-.522,.13),l(-.144,0),l(-.554,-.576),l(-.847,-.998),l(-.188,-.253),l(-.64,-.321),l(-.521,-.181),l(-1.167,.05),l(-1.163,.12),l(-.496,.017),l(-.397,-.252),l(-.429,-.548),l(-.401,-.309),l(-.099,-.353),l(.226,-1.132),l(-.103,-.395),l(-.855,-1.45),l(-.31,-.606),l(-.384,.017),l(-.234,.1),l(-.402,-.025),l(.709,-1.191),l(.241,-.722),l(.172,-.722),l(.99,-1.758),l(.381,-.059),l(.227,.027),l(.129,-.396),l(-.048,-.497),l(.056,-.288),l(.414,-.2),l(.534,-.156),l(.84,-.171),l(.128,.105),l(-.9,.151),l(-.731,.312),l(-.145,.212),l(.19,.607),l(.142,.407),l(.224,.126),l(-.043,.145),l(.153,.579),l(-.135,.367),l(-.327,.364),l(-.348,.824),l(-.137,.368),l(.253,.479),l(.288,.253),l(.25,.72),l(.341,.353),l(.523,-.114),l(.184,-.156),l(.419,-.255),l(.12,-.142),l(.066,-.523),l(-.167,-.649),l(-.21,-.282),l(-.438,-.804),l(-.136,-.135),l(-.118,-.395),l(-.247,-.18),l(.239,-.099),l(.095,-.251),l(-.204,-.144),l(1,-.379),l(1.085,-.327),l(.998,-.272),l(.086,-.225),l(.69,-.086),l(.143,-.008),l(-.042,-.157),l(-.055,-.198),l(-.125,-.036),l(-.039,-.108),l(-.128,-.072),l(-.226,.071),l(-.156,.027),l(-.229,-.012),l(-.315,-.55),l(.109,-.254),l(.337,-.213),l(.367,-.043),l(.09,.112),l(.14,.368),l(.186,.162),l(-.001,.148),l(.026,.193),l(.068,.09),l(.004,.198),l(.253,.258),l(.329,-.02),l(.699,.111),l(.455,.07),l(.593,.196),l(.323,.254),l(.393,.564),l(.156,.635),l(.358,.324),l(.359,.084),l(1.02,-.129),l(.928,-.059),l(.59,-.058),l(.799,-.059),l(.714,.125),l(.4,.479),l(.267,.169),l(.578,.253),l(.49,.14),l(1.094,.04),l(.382,-.057),l(.388,-.227),l(1.042,-.807),l(.47,-.185),l(.453,.042),l(.959,-.073),l(1.152,-.073),l(.919,.055),l(.248,.112),l(-.056,.141),l(-.294,.185),l(-.854,-.041),l(-.433,.015),l(-.083,.212),l(.059,.184),l(.593,.253),l(.609,.535),l(.195,.649),l(.246,-.523),l(.185,-.142),l(.415,.253),l(.483,.027),l(.374,.098),l(.258,.338),l(.918,.394),l(.464,.295),l(-.729,.496),l(-.161,.65),l(-.214,.226),l(-1.055,.417),l(.5,.064),l(.598,.098),l(.368,-.029),l(.33,-.142),l(.929,-.03),l(.725,.083),l(.84,.274),l(.095,.296),l(-.061,.41),l(-1.655,1.239),l(-.101,.255),l(.074,.212),l(.62,.604),l(.141,.282),l(-.308,.299),l(-.41,.144),l(-1.032,.19),l(-.061,.452),l(.008,.58),l(-.395,.539),l(-.071,.212),l(.324,.521),l(.732,.745),l(.503,.647),
+N(493.044,204.258),l(-1.223,-1.771),l(-.027,-.932),l(-.03,-1.43),l(-.042,-2.045),l(-.003,-1.017),l(0,-.438),l(.016,-.848),l(.004,-1.215),l(.002,-.508),l(1.649,-2.467),l(.453,-.511),l(.305,-.101),l(1.216,-.078),l(.337,-.115),l(.294,-.426),l(.502,-.497),l(.388,-.214),l(.562,-.215),l(.626,-.088),l(1.693,.046),l(.8,-.004),l(.813,-.909),l(1.562,-1.69),l(4.087,-4.205),l(-.236,-.154),l(-1.875,.04),l(-1.447,-.472),l(-4.26,-1.443),l(-.54,-.209),l(-.634,-.321),l(-.596,-.547),l(-.732,-.801),l(-.587,-.844),l(-.428,-.732),l(-.083,-.409),l(.363,-.807),l(.34,-.567),l(.37,-.468),l(.114,-.26),l(.154,.044),l(.935,1.142),l(.586,.62),l(.243,.381),l(.265,.211),l(.372,-.071),l(.417,-.001),l(.465,.027),l(.372,-.071),l(.572,-.27),l(.836,-.425),l(.585,-.157),l(.397,.098),l(.76,.267),l(.549,-.072),l(.56,-.326),l(.779,-.566),l(.247,-.127),l(.447,.041),l(.479,.098),l(.419,-.043),l(1.195,-.482),l(.288,.027),l(.682,.196),l(.74,-.03),l(.764,-.185),l(.964,-.327),l(.9,-.666),l(.47,-.382),l(.604,.154),l(.391,.211),l(.08,.014),l(.147,.268),l(-.414,.919),l(.021,.564),l(.132,.621),l(-.165,.452),l(-.375,.509),l(-.028,.678),l(-.047,.833),l(-.163,.509),l(-1.264,2.262),l(-.842,.792),l(-.122,.311),l(.102,.353),l(-.893,1.569),l(-.834,1.272),l(-.214,.947),l(-.351,.636),l(-.712,1.117),l(-.874,1.188),l(-1.159,1.498),l(-.384,.439),l(-2.274,2.504),l(-1.82,1.557),l(-2.164,1.121),l(-.593,.382),l(-1.28,1.09),l(-1.74,1.755),l(-.06,.061),l(-1.055,1.1),l(-1.235,1.569),l(-.615,.835),l(.1,.353),l(-.094,.276),
+N(264.768,176.039),l(-.128,.225),l(-.115,.067),l(-.029,.135),l(.039,.25),l(-.058,.086),l(.125,.376),l(-.039,.424),l(-.453,.154),l(-.135,-.01),l(-.144,.039),l(-.482,-.039),l(-.192,.039),l(-.087,-.048),l(-.356,0),l(-.02,-.058),l(.039,-.067),l(.202,-.029),l(.222,-.135),l(.019,-.087),l(.106,.02),l(.154,.01),l(.135,-.145),l(-.096,-.279),l(.048,-.125),l(.029,-.183),l(-.067,-.125),l(-.097,-.135),l(-.279,-.048),l(.116,-.096),l(.164,-.019),l(.231,-.029),l(.115,-.087),l(.385,.02),l(.106,-.039),l(.299,-.067),l(.244,.006),
+N(654.075,190.187),l(.206,-.125),l(.63,-.114),l(.656,-.938),l(.241,-.07),l(-.069,.268),l(.122,.087),l(.187,-.046),l(.11,.174),l(.148,.444),l(-.024,.111),l(-.013,.247),l(.197,.197),l(.025,.086),l(-1.234,.42),l(-.383,.271),l(-.309,-.271),l(-.111,-.259),l(-.234,.012),l(-.062,-.37),l(-.084,-.126),
+N(493.044,204.258),l(-.602,.389),l(-.557,.171),l(-.385,-.112),l(-.086,.777),l(-.282,.367),l(-.67,.115),l(-.394,.382),l(-.088,.48),l(.006,.353),l(-.356,.622),l(-.964,1.358),l(.092,.536),l(-.337,.65),l(-.25,.255),l(-.334,.1),l(-.084,.152),l(-1.067,-.807),l(-2.427,-1.752),l(-.07,-.239),l(.116,-.552),l(-.137,-.381),l(-3.553,-1.984),l(-4.663,-2.568),l(-1.448,.033),l(1.144,-2.479),l(.105,-.229),l(.656,-.835),l(.985,-.938),l(.477,-.525),l(.43,-.695),l(.143,-.566),l(-.048,-.664),l(-.223,-.606),l(-.224,-.352),l(-.732,-1.069),l(-.174,-.465),l(-.027,-.861),l(-.126,-.226),l(-.477,-.463),l(-.3,-.493),l(.678,-.795),l(.291,-.327),l(.671,.052),l(.832,-.005),l(1.009,-.133),l(.464,-.017),l(.079,.028),l(.319,.069),l(1.054,.149),l(.749,.193),l(.604,.293),l(2.353,1.525),l(.335,.041),l(1.373,.133),l(.67,.193),l(.847,.094),l(.16,-.114),l(.71,-.753),l(.258,-.199),l(.675,-.343),l(.852,-.358),l(.271,.098),l(.46,.336),l(.461,.181),l(1.103,.106),l(-1.649,2.467),l(-.002,.508),l(-.004,1.215),l(-.016,.848),l(0,.438),l(.003,1.017),l(.042,2.045),l(.03,1.43),l(.027,.932),l(1.223,1.771),
+N(466.196,203.275),l(.188,-.298),l(.076,-.27),l(-.057,-.748),l(.025,-.734),l(-.021,-.593),l(.107,-.507),l(.217,-1.02),l(.395,-.681),l(.255,-.284),l(1.241,-.996),l(1.195,-1.066),l(.191,-.453),l(-.111,-.31),l(-.271,-.182),l(-.479,-.04),l(-.191,-.027),l(-.128,-.253),l(.26,-1.88),l(.018,-.424),l(-.159,-.183),l(-.063,-.028),l(.496,-.229),l(.464,-.087),l(.495,.011),l(.942,.234),l(.911,-.048),l(.881,-.33),l(.88,-.132),l(.303,.097),l(.303,.196),l(.367,-.016),l(.626,-.314),l(.563,-.54),l(.3,.493),l(.477,.463),l(.126,.226),l(.027,.861),l(.174,.465),l(.732,1.069),l(.224,.352),l(.223,.606),l(.048,.664),l(-.143,.566),l(-.43,.695),l(-.477,.525),l(-.985,.938),l(-.656,.835),l(-.105,.229),l(-1.144,2.479),l(-4.659,0),l(-1.277,.05),l(-.319,.017),l(-.554,.398),l(-.458,.427),l(-.431,.045),l(-.546,-.223),l(-.064,-.042),
+N(713.621,206.298),l(.169,7.966),l(-.44,.822),l(.431,1.368),l(.046,.805),l(-.031,3.438),l(-.515,-.512),l(-.927,-.888),l(-.716,-.902),l(-.406,-.056),l(-.776,.101),l(-.739,.143),l(-.434,-.013),l(.091,-.382),l(.435,-.65),l(.006,-.283),l(-.561,-.521),l(-.565,-.775),l(.028,-.226),l(.442,.111),l(.236,-.042),l(.135,-.113),l(-.467,-.409),l(-.595,-.408),l(-.287,-.381),l(-.275,-.648),l(-1.053,-1.693),l(-.508,-.394),l(-.467,-.282),l(-.604,-.196),l(-1.983,-.603),l(-1.26,-.379),l(-.613,-.069),l(-.705,-.238),l(-.63,-.323),l(.072,-.34),l(-.098,-.268),l(-.193,-.028),l(-.617,.101),l(-.389,-.07),l(-.412,-.196),l(-.408,-.395),l(-.209,-.579),l(.133,-.494),l(-.155,-.226),l(-.187,.113),l(-.234,.396),l(-.122,.664),l(-.251,.608),l(-.334,.269),l(-.696,.354),l(-.155,-.169),l(-.331,-.677),l(.022,-.155),l(.384,-.27),l(-.152,-.424),l(-.173,-.239),l(-.564,-.395),l(-.707,-.394),l(-.338,-.056),l(-.059,-.212),l(.038,-.226),l(.413,-.044),l(.388,.084),l(.603,.239),l(.158,-.029),l(.368,-.34),l(.525,-.41),l(.146,.056),l(.3,.269),l(1.021,-.045),l(.139,-.128),l(.09,-.522),l(-.063,-.409),l(-.238,.028),l(-.345,.199),l(-.604,.071),l(-.656,-.041),l(-.766,.044),l(-1.026,-.082),l(-.411,-.31),l(-.135,-.197),l(-.148,-.664),l(-.202,-.338),l(-.42,-.155),l(-1.249,-.124),l(.265,-.297),l(.058,-.255),l(.004,-.593),l(.463,-.029),l(.92,-.411),l(.49,-.383),l(.444,-.283),l(.352,.027),l(.4,.069),l(1.494,.646),l(.515,.169),l(.913,.153),l(.382,.705),l(.138,.396),l(-.283,.749),l(-.067,.381),l(.221,.381),l(.115,.494),l(.115,.48),l(.215,.521),l(.186,.197),l(.197,.127),l(.226,-.65),l(.085,.113),l(.087,.141),l(.309,1.073),l(.169,.169),l(.234,.183),l(.294,.112),l(.354,.056),l(.58,-.198),l(.504,-.439),l(1.192,-1.853),l(.352,-.015),l(1.078,-.215),l(.378,-.142),l(.045,-.085),l(.014,-.509),l(.219,-.17),l(1.1,-.609),l(.335,-.043),l(1.732,.759),l(2.129,.941),l(1.54,.52),l(1.299,.404),M(691.208,208.707),l(-.388,-.069),l(-.693,-.38),l(-.852,-.647),l(-.295,-.141),l(-.414,.028),l(-.059,.1),l(.024,.452),l(-.206,.028),l(-1.014,-.407),l(-.258,-.353),l(-.582,.199),l(-.289,.269),l(-.326,.185),l(-.186,-.184),l(-.312,-.451),l(-.245,-.451),l(.246,-.198),l(.303,-.029),l(.274,.056),l(1.104,.04),l(.574,.31),l(.319,-.015),l(.544,-.326),l(.414,-.015),l(.534,.126),l(.857,.21),l(.499,.395),l(.293,.395),l(.179,.621),l(-.049,.254),M(682.045,208.699),l(-.419,-.056),l(-.715,-.493),l(-.232,-.451),l(.146,-.283),l(.603,-.1),l(.766,-.044),l(.246,.126),l(.256,.311),l(.313,.197),l(.108,.226),l(-.067,.226),l(-.125,.057),l(-.879,.285),M(707.635,219.095),l(-1.11,-.209),l(.589,-1.032),l(.56,-.708),l(.407,-.269),l(.427,-.072),l(.527,.338),l(.198,.24),l(-.11,.184),l(-.324,.637),l(-.256,.17),l(-.638,.693),l(-.27,.028),M(673.797,218.703),l(-.562,.257),l(.034,.233),l(-.886,.326),l(-.582,.274),l(-.339,-.041),l(-.453,.325),l(-.504,-.069),l(-.427,-.112),l(-.378,.255),l(-.3,.058),l(-.358,-.07),l(-.58,-.196),l(-1.046,-.04),l(-.316,.043),l(-.211,-.564),l(.027,-.24),l(.383,-.198),l(.672,-.199),l(.528,-.016),l(1.142,.407),l(.445,.324),l(.338,.013),l(.326,-.297),l(.464,-.016),l(.429,-.071),l(.414,.187),l(.467,-.116),l(-.072,-.222),l(.421,-.187),l(.404,-.233),l(.094,-.151),l(-.076,-.117),l(-.184,.023),l(.116,-.198),l(.16,.012),l(.22,.094),l(.177,.221),l(.013,.304),M(662.661,219.065),l(-.312,-.099),l(-.203,-.127),l(-.062,-.169),l(.03,-.212),l(.256,-.198),l(.315,-.036),l(.17,.092),l(.053,.212),l(.182,.098),l(.305,-.145),l(.34,.105),l(.104,.151),l(-.012,.451),l(.183,-.148),l(.163,-.304),l(.318,-.029),l(.229,.226),l(.021,.424),l(.181,-.036),l(.062,.104),l(-.025,.397),l(-.316,-.211),l(-.311,-.058),l(-.141,.058),l(.072,.155),l(-.852,.157),l(-.143,-.091),l(.097,-.268),l(-.085,-.059),l(-.308,.269),l(-.229,.256),l(-.296,-.046),l(-.63,.225),l(-.624,.199),l(-.357,-.051),l(-.31,.123),l(-.392,-.07),l(-.103,-.07),l(-.202,-.123),l(-.063,-.279),l(.143,-.261),l(-.08,-.253),l(.193,-.115),l(.23,-.113),l(.233,-.156),l(.224,.07),l(.61,.013),l(.4,.104),l(.089,.28),l(.291,.109),l(.294,.056),l(.189,-.259),l(.29,-.012),l(.051,-.187),l(-.263,-.15),M(656.294,219.602),l(-.393,-.282),l(-.855,-.449),l(-.118,-.269),l(.417,-.001),l(.514,-.185),l(.462,-.029),l(.925,.521),l(-.338,.17),l(-.232,.1),l(-.381,.425),M(631.053,200.125),l(-.061,.225),l(-.413,.439),l(-.204,.41),l(-.381,.354),l(.164,.353),l(.162,.169),l(.806,.493),l(.832,.055),l(.241,.112),l(.151,.381),l(.128,.763),l(-.007,.409),l(.267,.423),l(.212,.127),l(.544,.041),l(-.45,.933),l(.151,.212),l(.703,-.453),l(.824,.252),l(.177,.042),l(.265,.254),l(.144,.438),l(.698,.676),l(-.515,1.979),l(-.04,.452),l(.23,.946),l(-.021,.438),l(.021,.664),l(-.002,.268),l(-.149,1.06),l(-.087,.156),l(-.107,.07),l(-.367,-.253),l(-.381,-.522),l(-.261,-.084),l(-.262,.481),l(-.081,.268),l(-1.043,-.619),l(-.219,.086),l(.394,.747),l(-.163,.213),l(-.204,-.197),l(-1.343,-1.424),l(-.775,-.761),l(-1.011,-.859),l(-1.348,-.958),l(-.391,-.451),l(-.199,-.493),l(-.191,-.339),l(-1.003,-.633),l(-.697,-.677),l(-1.186,-1.509),l(-.074,-.353),l(.039,-.339),l(-.324,-.875),l(-.841,-1.467),l(-.667,-1.044),l(-.612,-.775),l(-.369,-.301),l(-.287,-.234),l(-.64,-.295),l(-.254,-.748),l(-.688,-1.806),l(.067,-.24),l(-.107,-.311),l(-.157,-.197),l(-.662,-.507),l(-.711,-.394),l(-.539,-.21),l(-.317,-.099),l(-.119,-.353),l(-.077,-.734),l(-.18,-.409),l(-.386,-.479),l(-.818,-.831),l(-.368,-.423),l(-.725,.128),l(-.613,-.676),l(-.646,-.606),l(-.593,-.69),l(-.562,-.945),l(-.229,-.635),l(-.032,-.367),l(.057,-.198),l(.149,-.113),l(.401,-.043),l(.364,.098),l(.25,.126),l(.632,.563),l(.361,.155),l(.922,.153),l(.335,.027),l(.548,-.1),l(.454,-.142),l(.4,-.015),l(.323,.31),l(.919,1.156),l(.513,.31),l(.058,.155),l(-.12,.537),l(1.066,.916),l(.749,.493),l(1.175,.689),l(.678,.323),l(.139,.169),l(.03,.593),l(-.02,.155),l(.573,.055),l(.745,.944),l(.612,.55),l(.271,-.015),l(.004,-.198),l(-.123,-.226),l(.069,-.24),l(.507,.21),l(.479,.804),l(.441,.38),l(.446,.056),l(.429,.197),l(.314,.366),l(.28,.734),l(.316,.437),l(.431,.268),l(.511,.126),l(.767,.083),l(.431,.154),l(.494,.38),l(.576,.606),l(-.019,.071),M(684.201,200.125),l(-.007,-.172),l(-.414,-1.058),l(.18,-.551),l(-.078,-.141),l(-.141,-.296),l(.036,-.325),l(.286,-.89),l(.514,-.82),l(.263,.367),l(.152,.353),l(-.054,.283),l(-.246,.396),l(-.361,.763),l(.061,.325),l(.19,.141),l(.097,-.141),l(.436,-.411),l(.135,-.522),l(.179,-.142),l(.806,-.412),l(.141,.141),l(-.052,.254),l(.104,.55),l(-.354,.212),l(-.467,.354),l(-.162,.311),l(.159,.099),l(.446,.126),l(.398,.211),l(-.016,.141),l(.159,.353),l(-.688,-.154),l(-.431,-.154),l(-.367,-.042),l(-.304,.156),l(-.08,.438),l(.049,.258),l(.131,.688),l(.341,.62),l(.405,.438),l(.196,.282),l(-.156,.212),l(-.26,-.211),l(-.664,-.648),l(-.55,-.733),l(-.002,-.396),l(-.011,-.251),M(637.361,207.144),l(-.863,-.394),l(-.377,-.239),l(-.205,-.367),l(-.045,-.367),l(-.156,-.395),l(-.507,-.395),l(-.291,-.099),l(-.446,.029),l(-.116,-.141),l(.271,-.65),l(.234,-.24),l(.509,.55),l(.148,-.467),l(.313,-.269),l(.072,.395),l(.312,.89),l(.648,.817),l(.698,.31),l(-.265,.184),l(-.118,.283),l(.183,.564),M(634.321,215.345),l(-.091,-.187),l(.316,-.023),l(.402,.093),l(.369,-.129),l(.068,-.524),l(.018,-.14),l(.309,.057),l(-.043,-.5),l(.222,-.235),l(.093,-.277),l(.202,.121),l(.631,.112),l(.474,-.047),l(.237,.443),l(.524,-.089),l(.158,-.297),l(.022,-.244),l(.259,.116),l(.618,.168),l(.411,.438),l(.338,-.046),l(.204,.271),l(.446,-.029),l(.453,-.185),l(.302,.211),l(.369,.522),l(.179,.521),l(.884,.041),l(.462,.188),l(.49,-.077),l(1.435,.124),l(.479,-.029),l(.34,-.17),l(.213,-.65),l(.271,-.269),l(.447,-.015),l(.223,.211),l(.289,.494),l(.633,.125),l(.53,.027),l(.774,.083),l(.796,.153),l(.289,.24),l(.293,.288),l(-.08,.445),l(.275,.466),l(.119,.099),l(.877,.352),l(.422,.069),l(.658,.013),l(.45,-.185),l(.415,-.015),l(.628,.238),l(.048,.197),l(-.255,.425),l(-.152,.494),l(.578,.776),l(-.499,-.211),l(-.802,-.196),l(-.599,-.253),l(-.891,-.309),l(-.528,.001),l(-.589,.256),l(-.348,.057),l(-.714,-.098),l(-1.454,-.138),l(-1.47,-.138),l(-.805,-.253),l(-.839,-.479),l(-1.099,-.336),l(-1.125,-.267),l(-.948,-.04),l(-.556,.298),l(-.445,.043),l(-.957,-.153),l(-.805,-.492),l(-.357,-.07),l(-1.606,-.066),l(-.363,-.155),l(.055,-.141),l(.448,-.468),l(-.402,-.267),l(-.551,-.099),l(-.506,-.14),l(-.307,-.027),l(-1.261,-.121),M(675.004,223.092),l(.249,-.494),l(.023,-.537),l(.113,-.312),l(.674,-.481),l(1.447,-.624),l(.662,-.454),l(.36,-.607),l(.466,-.157),l(1.578,-.102),l(.91,-.214),l(.541,-.044),l(.869,-.143),l(.118,.07),l(.099,.197),l(-.237,.212),l(-.36,.256),l(-1.609,.61),l(-1.369,.44),l(-.713,.256),l(-.606,.354),l(-1.09,.963),l(-.653,.481),l(-.439,.086),l(-.552,.228),l(-.48,.015),M(667.866,223.149),l(-.217,-.069),l(-.917,-.605),l(-.8,-.45),l(-.347,-.099),l(-.493,-.126),l(-.292,-.197),l(.108,-.212),l(.371,-.142),l(.992,-.03),l(.502,-.114),l(.35,.296),l(1.147,.746),l(.265,.381),l(-.125,.325),l(-.246,.24),l(-.299,.057),M(661.819,191.241),l(-.041,.09),l(.319,.691),l(-.23,.142),l(-.546,.043),l(-.579,.086),l(.198,.226),l(.115,.296),l(-.169,.226),l(.216,.211),l(.235,.112),l(.546,.832),l(.536,.747),l(.043,.198),l(-.338,.721),l(.075,.226),l(.406,.465),l(.743,.45),l(.6,.493),l(.551,.761),l(-.465,.17),l(-.75,-.026),l(-.797,-.238),l(-.337,.1),l(-.387,.467),l(-.354,.918),l(-.08,.476),l(-.046,.272),l(.132,.649),l(.116,.424),l(-.133,.848),l(-.256,0),l(-.466,-.154),l(-1.037,.963),l(-.433,.65),l(-.751,.608),l(.443,.381),l(.06,.396),l(.17,.296),l(-.685,.058),l(.452,.578),l(.009,.212),l(-.103,.227),l(-.547,.665),l(-.206,.396),l(-.127,.354),l(-.529,.594),l(-1.294,.61),l(-.607,.284),l(-.292,.198),l(-.194,-.311),l(.024,-.424),l(-.33,-.804),l(-.306,-.381),l(-.265,-.184),l(-.286,.029),l(-.503,.523),l(-.302,.029),l(-.328,-.508),l(-.313,-.197),l(-.437,-.112),l(-.387,-.451),l(-.342,-.154),l(-.35,.806),l(-.135,.198),l(-.381,.058),l(-.356,-.112),l(-.442,.128),l(-.318,.354),l(-.364,.071),l(-.059,-.551),l(.034,-.311),l(-.314,-1.03),l(-.336,.396),l(-1.42,.44),l(-.321,-.408),l(-.639,.015),l(-.281,.156),l(-.303,.029),l(-.058,-.649),l(-.022,-.65),l(-.267,-1.411),l(-.012,-.48),l(-.352,-.747),l(-.406,-.409),l(-.79,-.422),l(-.146,-.141),l(.555,-.354),l(-.531,-.38),l(-.258,-.296),l(.188,-.735),l(-.074,-.128),l(-.278,-.478),l(-.352,-.296),l(.065,-.466),l(-.125,-.593),l(.182,-.65),l(.133,-.353),l(.424,-.58),l(.303,-.806),l(.318,.028),l(.204,.11),l(.288,.792),l(.253,.295),l(1,.983),l(.304,.083),l(.446,.28),l(.928,-.416),l(.255,-.001),l(.526,.223),l(.543,.11),l(.399,-.172),l(.528,-.342),l(.403,-.525),l(.531,-.441),l(.479,-.074),l(.431,.11),l(.557,.251),l(.524,.223),l(.559,.152),l(.287,-.03),l(.467,-.356),l(.465,-.172),l(.864,-.175),l(.387,-.299),l(.928,-1.785),l(-.076,-.748),l(.218,-.34),l(.646,-.244),l(.22,-.383),l(-.106,-.988),l(.119,-.565),l(.381,-.638),l(.247,-.157),l(.464,-.017),l(.748,.081),l(.651,.081),l(.624,-.018),l(.446,.04),l(.753,.292),l(.182,.09),M(666.561,200.125),l(.012,-.049),l(.48,-1.188),l(.434,-.41),l(.289,-.142),l(.429,.338),l(.29,-.311),l(.162,-.325),l(.293,-.481),l(.496,-.058),l(.605,.14),l(.729,.535),l(.447,.027),l(.863,-.044),l(.478,.168),l(.749,.267),l(.577,-.227),l(1.853,.081),l(.72,-.128),l(.627,-.354),l(.211,-.283),l(-.156,-.268),l(.196,-.283),l(.388,-.241),l(.295,-.41),l(.289,-.057),l(.075,.24),l(-.073,.537),l(-.117,.311),l(-.081,.127),l(-.082,.127),l(-.969,1.259),l(-.416,.396),l(-.464,.1),l(-1.23,.229),l(-.495,-.069),l(-.591,-.422),l(-1.149,-.068),l(-1.151,.059),l(-.878,-.041),l(-1.039,.045),l(-.575,-.083),l(-.671,.029),l(-.415,.1),l(-.433,.368),l(-.259,.461),l(-.154,.274),l(-.187,.721),l(.068,.48),l(.263,.494),l(.194,.183),l(.403,.226),l(.259,.196),l(.221,.607),l(.179,.154),l(.226,.042),l(.815,.026),l(.249,-.269),l(.652,-.976),l(.385,.056),l(.307,.183),l(.496,.041),l(.363,-.227),l(.669,-.156),l(.62,-.143),l(.268,-.298),l(.271,-.057),l(.466,.196),l(.131,.212),l(-.083,.734),l(-.469,-.267),l(-.544,-.042),l(-.361,.298),l(-.389,.523),l(-.438,.425),l(-1.059,.439),l(-.214,.325),l(-.143,.029),l(-.241,-.042),l(-.468,-.126),l(-.03,.056),l(.025,.312),l(.212,.126),l(.676,.578),l(.467,.521),l(.854,1.24),l(-.097,.325),l(-.156,.679),l(.102,.409),l(.447,.535),l(.555,.438),l(.062,.226),l(-.045,.282),l(-.436,-.056),l(-.652,.059),l(-.412,.297),l(-.224,.692),l(-.498,-.026),l(-.461,-.183),l(-.107,-.17),l(.052,-.649),l(.204,-.58),l(-.978,-.845),l(-.417,-.31),l(-.174,-.269),l(.036,-.24),l(.284,-.396),l(.116,-.579),l(-.165,-.494),l(-.737,-.055),l(-.503,.213),l(-.494,.396),l(.16,.353),l(.143,.932),l(-.068,.509),l(-.236,1.145),l(.363,.903),l(-.01,.311),l(-.377,.636),l(-.019,.227),l(.275,.564),l(-.726,.171),l(-.513,.241),l(-.476,.071),l(-.245,-.324),l(-.16,-.522),l(.156,-.325),l(.181,-.466),l(.069,-.876),l(.06,-1.073),l(-.125,-.509),l(.029,-.339),l(-.213,-.395),l(-.311,-.127),l(-.391,.171),l(-.574,.029),l(.011,-.41),l(-.25,-1.284),l(.131,-.311),l(.491,-.524),l(.469,-.777),l(.161,-.48),l(-.089,-.918),l(-.006,-.254),l(.087,-.452),l(.339,-.721),l(.447,-.058),l(-.043,-.861),l(.254,-1.053),
+N(341.05,41.069),l(2.084,.272),l(.344,.361),l(-.869,.174),l(-.541,.139),l(-1.678,.106),l(-1.159,.037),l(-.689,.156),l(-.372,.224),l(-.308,.6),l(-.361,.376),l(1.05,.39),l(.971,.168),l(2.117,.064),l(.601,-.001),l(1.775,-.242),l(1.93,-.038),l(.866,.135),l(.933,.219),l(.417,.135),l(.284,-.018),l(1.001,-.002),l(1.277,.032),l(.615,.05),l(-1.277,.626),l(-1.583,.457),l(-1.976,.523),l(-.556,-.016),l(-.695,-.116),l(-.951,.671),l(-1.061,.503),l(-1.246,.452),l(-1.125,.296),l(-.211,.056),l(-2.212,.054),l(-.525,.134),l(-.502,.001),l(-.982,.201),l(-.665,.167),l(-.528,.051),l(-.946,-.413),l(-.375,.05),l(-.69,.913),l(-.958,.118),l(-.631,-.065),l(-.743,-.197),l(-.622,-.463),l(-.854,-.43),l(-.647,-.215),l(-.109,0),l(.008,.2),l(.707,1.043),l(-.192,.249),l(-.319,.017),l(-.69,.249),l(-.84,.249),l(-.573,.38),l(-1,.906),l(-.657,.657),l(-1.051,.851),l(-.776,.262),l(-1.034,.083),l(-1.023,-.275),l(-.148,.554),l(-.438,.569),l(-.783,.277),l(-.992,-.095),l(-.616,.05),l(-1.18,.439),l(.942,-1.723),l(-.121,.017),l(-.795,-.015),l(-1.055,-.177),l(.26,.423),l(-.026,.455),l(-.386,.407),l(-.794,.39),l(-1.16,.164),l(-.973,.002),l(-1.255,.083),l(.467,.403),l(.212,.403),l(-.09,.387),l(-.379,.097),l(-.321,-.063),l(-.47,.033),l(-1.792,-.157),l(.517,.32),l(.765,.462),l(.295,.351),l(-.01,.224),l(-.26,.176),l(-1.197,.034),l(-1.051,.129),l(.844,.413),l(.47,.126),l(.448,.222),l(.389,.333),l(-.554,.461),l(-.285,.111),l(-.599,-.094),l(-.478,.096),l(.345,.474),l(-.009,.127),L(308.501,60),l(-.486,-.125),l(-.583,-.062),l(.026,.158),l(.255,.457),l(-.101,.347),l(-.288,0),l(-.656,-.093),l(-.089,-.016),l(-.979,.112),l(-1.081,.018),l(.682,.487),l(1.108,.391),l(.331,.204),l(-.077,1.035),l(-.382,.938),l(-.427,.094),l(-.815,-.061),l(.489,.591),l(-.016,.498),l(.156,.233),l(-.068,.373),l(-.316,.062),l(-.495,-.03),l(-.771,.079),l(.762,.386),l(.427,.603),l(-.117,.447),l(-.287,.031),l(-.967,-.26),l(-1.052,-.508),l(-.498,.294),l(-.425,.602),l(-.635,-.599),l(.158,-.573),l(-.387,-.201),l(-1.124,-.184),l(-.577,-.309),l(.04,-.187),l(.253,-.249),l(.066,-.218),l(-.325,-.28),l(-.366,-.186),l(-.668,.359),l(-.276,.016),l(-.3,.141),l(-.444,-.046),l(-.98,.064),l(-.417,.017),l(-.571,.296),l(-.476,.28),l(-.426,-.403),l(-.104,-.357),l(-.222,-.217),l(-.513,-.233),l(-.817,-.232),l(-.772,-.389),l(-.517,-.781),l(.07,-.737),l(-.199,-.156),l(-.434,-.094),l(-.467,.048),l(-.97,-.266),l(-.108,-.094),l(-.138,-.236),l(.14,-.457),l(.459,-.395),l(.071,-.269),l(-.258,-.062),l(-.551,-.031),l(-.542,-.094),l(-.278,-.221),l(-.058,-.633),l(-.458,-.126),l(-.616,.049),l(-.589,-.57),l(.023,-.191),l(.198,-.254),l(.618,-.367),l(1.22,-.337),l(.405,-.304),l(.476,-.128),l(.051,-.383),l(-.277,-.287),l(-.473,.097),l(-.921,.082),l(-.493,.097),l(-.635,.416),l(-.538,.129),l(-.63,.304),l(-.339,-.318),l(.038,-.623),l(-.114,-.784),l(-.206,-.451),l(.015,-.355),l(-.243,-.323),l(-.504,.082),l(-.271,-.032),l(-.666,-.355),l(-.594,-.485),l(-.013,-.357),l(.842,-.538),l(.265,.019),l(-.556,-.189),l(-1.083,.064),l(-.103,-.284),l(.383,-.176),l(.677,-.03),l(.586,-.052),l(.456,-.087),l(.322,-.672),l(-1.208,-.047),l(-.572,.05),l(-.362,-.032),l(-.29,-.163),l(-.116,-.197),l(.223,-.28),l(.218,-.008),l(.18,-.16),l(.427,-.055),l(-.375,-.188),l(-.552,.073),l(-.22,-.248),l(.057,-.188),l(.073,-.132),l(.259,0),l(.309,-.099),l(.827,-.25),l(1.218,.081),l(.854,.163),l(.776,.032),l(.378,.131),l(.927,.146),l(1.027,.097),l(-.031,-.363),l(.299,-.545),l(-.298,-.182),l(-1.02,-.263),l(-1.356,-.312),l(-.903,-.164),l(-1.592,-.33),l(-.354,-.116),l(.336,-.35),l(.788,-.001),l(1.462,.363),l(1.034,.048),l(.463,-.067),l(.114,-.067),l(.088,-.6),l(.088,-.301),l(.595,-.034),l(.528,.116),l(.227,-.101),l(-.027,-.351),l(-.195,-.184),l(-.891,-.317),l(.162,-.437),l(.528,-.455),l(-.258,-.286),l(-1.21,-.167),l(-1.154,.002),l(-1.178,-.286),l(-1.649,-.49),l(-.78,-.101),l(-.903,-.05),l(-.76,-.34),l(-.811,-.477),l(.156,-.035),l(.323,-.155),l(.605,-.001),l(.572,-.019),l(2.085,.305),l(.716,.033),l(1.249,.306),l(1.451,.458),l(.729,.169),l(.056,-.307),l(-.311,-.426),l(-.86,-.546),l(-.172,-.446),l(-.603,-.446),l(-.485,-.051),l(-.677,-.24),l(.361,-.277),l(.542,-.139),L(285.644,41),l(-.704,-.241),l(-1.101,-.015),l(-.625,-.086),l(-1.132,-.327),l(-.88,.608),l(-.324,.156),l(-.274,.294),l(-.875,.243),l(-1.402,-.066),l(-1.031,-.343),l(-.306,-.242),l(-.027,-.294),l(.438,-.313),l(.293,-.645),l(-.152,-1.259),l(.582,-.054),l(.691,.192),l(.372,-.124),l(.151,-.334),l(-.383,-.369),l(-.933,-.545),l(-.452,-.141),l(-.946,-.796),l(-.895,-.925),l(-1.105,-1.289),l(-.578,-.485),l(-1.855,-.379),l(-.667,-.255),l(-.291,-.202),l(-.052,-.701),l(-.904,-.406),l(-.962,-.109),l(-1.589,-.165),l(-1.928,-.425),l(-1.903,-.333),l(-2.133,-.183),l(-.997,-.054),l(-1.632,-.035),l(-.785,.189),l(-1.043,.096),l(-.806,.188),l(-1.419,.152),l(-1.228,-.166),l(-1.46,-.296),l(.319,.747),l(-.051,.093),l(-1.051,-.017),l(-1.294,-.184),l(-3.168,-.611),l(1.538,-.566),l(.463,-.114),l(-.092,-.226),l(-.423,-.169),l(-1.067,-.017),l(-2.21,-.015),l(-.812,-.074),l(-.629,-.018),l(-1.238,-.434),l(-.87,-.208),l(.587,-.306),l(1.257,-.041),l(3.036,.147),l(2.025,.034),l(1.343,.017),l(2.117,-.157),l(1.055,-.212),l(.292,-.096),l(.054,-.345),l(-.627,-.287),l(-.82,-.21),l(-1.217,.156),l(-1.11,.252),l(-1.31,.021),l(-1.138,-.113),l(-.753,.078),l(-.879,.098),l(-.68,-.056),l(-.857,-.19),l(-.664,-.365),l(-.816,-.191),l(-.662,-.057),l(-.726,.059),l(-.486,-.076),l(-1.416,-.481),l(-.044,-.35),l(.36,-.45),l(.81,-.119),l(1.235,-.1),l(1.517,-.14),l(2.074,-.161),l(1.29,-.081),l(.951,-.396),l(1.089,-.259),l(.843,-.081),l(2.478,-.005),l(1.101,-.101),l(1.942,.036),l(.402,-.139),l(.31,-.199),l(.609,-.16),l(.202,-.658),l(.276,-.501),l(.116,-.101),l(-.89,-.228),l(-1.947,-.039),l(-1.155,.194),l(-.959,-.125),l(-1.243,-.383),l(.595,-.781),l(1.38,-.332),l(2.845,-.359),l(1.407,-.225),l(1.962,-.249),l(2.112,-.162),l(1.163,.087),l(1.213,-.07),l(1.319,-.07),l(.345,-.181),l(.011,-.226),l(-.357,-.753),l(-.022,-.208),l(.522,-.14),l(1.886,-.05),l(1.526,.205),l(2.141,.41),l(1.296,.226),l(.802,.181),l(.823,-.275),l(-1.657,-.525),l(-.697,-.509),l(.167,-.047),l(2.2,-.122),l(1.166,-.12),l(1.854,-.216),l(2.52,-.195),l(.73,.069),l(1.064,.116),l(.232,1.738),l(.913,-.162),l(.539,-.322),l(.432,-1),l(1.003,.021),l(2.004,.323),l(1.858,.414),l(1.529,.25),l(.205,-.3),l(-.644,-.3),l(-.816,-.537),l(-.894,-.4),l(.295,-.287),l(.742,.022),l(1.758,.02),l(1.136,.212),l(2.754,.373),l(1.284,.279),l(2.109,.322),l(1.878,.274),l(1.872,.204),l(.8,-.209),l(.816,-1.483),l(-.326,-.191),l(-1.292,-.334),l(-1.176,-.533),l(.708,-.247),l(2.404,-.005),l(2.962,-.328),l(1.329,-.077),l(1.527,.17),l(2.221,.488),l(1.567,.167),l(2.005,.142),l(.3,-.761),l(-.3,-.472),l(2.646,-.206),l(2.021,-.08),l(2.589,.095),l(1.989,.146),l(1.886,-.18),l(2.367,-.207),l(2.043,-.005),l(1.859,.223),l(1.825,-.055),l(1.315,.072),l(.619,.099),l(.55,-.102),l(1.946,.146),l(1.707,.046),l(1.673,.096),l(2.438,.761),l(1.368,.241),l(1.345,-.076),l(1.118,.168),l(2.594,.237),l(.445,.408),l(-.304,.12),l(-.492,.192),l(-1.683,.146),l(-2.303,.124),l(-1.152,.121),l(-1.233,.05),l(-1.469,-.068),l(-2.831,-.064),l(-2.22,-.066),l(-1.389,.168),l(-1.614,.027),l(-1.933,.027),l(-1.16,.026),l(-1.485,.168),l(-.444,.118),l(-1.322,.213),l(-.335,.464),l(.743,.251),l(2.551,-.281),l(1.367,-.072),l(3.912,.038),l(2.223,-.12),l(2.331,-.005),l(.997,-.025),l(.93,.067),l(1.77,.434),l(.671,.09),l(1.087,-.186),l(1.663,-.21),l(1.536,-.281),l(1.964,-.144),l(.59,.462),l(-.566,.482),l(-2.316,.639),l(-.973,.338),l(-1.281,.734),l(.12,.307),l(.319,.152),l(.796,-.089),l(.477,-.044),l(1.616,-.553),l(1.766,-.537),l(1.413,-.385),l(1.706,-.32),l(.775,-.207),l(1.662,-.163),l(1.618,.111),l(1.391,.065),l(1.497,-.3),l(.703,-.324),l(1.129,-.234),l(2.148,-.004),l(1.672,.112),l(1.097,.044),l(1.197,.136),l(1.135,.228),l(1.107,.112),l(.316,.25),l(-.181,.273),l(-1.97,.252),l(-1.491,.138),l(-1.245,.494),l(-.557,.289),l(-1.604,.355),l(-1.57,.548),l(-1.063,.089),l(-.918,-.042),l(-1.592,.047),l(-2.213,-.039),l(-1.491,.198),l(-.731,.217),l(-.495,.535),l(.166,.322),l(1.949,-.305),l(1.581,-.046),l(1.856,.101),l(.003,.42),l(-.743,.241),l(-2.388,.124),l(-.463,.14),l(-.213,.199),l(-.156,.595),l(-.471,.71),l(-.678,.158),l(-1.06,-.077),l(-.742,.041),l(-.837,.9),l(-.987,1.087),l(-.15,.347),l(.454,.307),l(.403,.095),l(.602,-.481),l(.743,-.368),l(.856,-.041),l(2.345,.266),l(.353,.096),l(.262,.288),l(-.059,.211),l(-1.234,-.074),l(-.673,-.018),l(-.512,.097),l(-.136,.191),l(.29,.286),l(1.756,.188),l(.557,.132),l(1.802,-.137),l(.526,.208),l(.214,.323),l(-.049,.436),l(-.256,.133),l(-1.835,-.186),l(-1.653,-.054),l(-.781,-.074),l(-1.295,.078),l(-1.382,.475),l(-.823,-.13),l(-.49,-.15),l(-1.06,.04),l(-.283,.377),l(1.393,.599),l(1.187,.222),l(1.298,.128),l(1.665,.072),l(.696,.148),l(.551,.482),l(.272,.444),l(.014,.554),l(-.434,.405),l(-.384,.074),l(-1.292,-.181),l(-.82,-.109),l(-.372,.111),l(.023,.55),l(.118,.236),l(.426,.162),l(.618,.089),l(.723,.215),l(.914,.142),l(.752,.16),l(.383,.376),l(-.338,.233),l(-.832,.145),l(-.647,.126),l(-1.747,-.032),l(-1.176,-.087),l(-1.624,-.086),l(-.592,.448),l(.551,.195),l(1.396,.051),l(1.052,.158),l(.724,.248),l(.088,.319),l(-.035,.549),l(-.13,-.005),l(-1.092,-.045),l(-1.247,.108),l(-.596,.266),l(-1.246,.02),l(-1.225,-.139),l(-1.497,-.404),l(-.922,-.478),l(-.373,-.07),l(-1.094,.286),L(345,36.811),l(-1.084,.09),l(-.589,.178),l(-1.451,-.033),l(-.913,-.087),l(-.969,.143),l(-.395,.125),l(-.174,.283),l(.006,.141),l(.354,.527),l(.71,.245),l(1.284,.05),l(1.515,.26),l(1.567,-.056),l(1.323,.54),l(.758,.226),l(.482,.226),l(1.196,.329),l(1.252,.38),l(.376,.276),l(.483,.898),l(.892,-.208),l(.278,-.139),l(.397,.207),l(.298,.43),l(.071,.344),l(.198,1.164),l(-.169,.205),l(-.371,.12),l(-.541,-.101),l(-.546,-.119),l(-.917,.002),l(-1.041,.036),l(-1.488,-.27),l(-.637,-.409),l(-.415,-.634),l(-.354,-.274),l(-1.17,-.566),l(-.84,-.292),l(-.748,-.137),l(-1.095,-.084),l(-.521,.14),l(-.962,.105),M(351.365,40.026),l(-1.527,-.537),l(-.96,-.225),l(-.712,-.156),l(-.159,-.069),l(-.314,-.419),l(1.483,-.038),l(.893,.139),l(1.064,.26),l(.819,.296),l(.162,.488),l(-.215,.209),l(-.533,.053),M(281.574,46.135),l(-.568,-.133),l(-.707,-.318),l(-.801,-.183),l(-.197,-.101),l(-.25,-.218),l(-.08,-.844),l(.287,-.34),l(.368,-.018),l(.646,.135),l(1.157,.066),l(1.287,.27),l(.748,.269),l(.595,.1),l(.777,.217),l(.603,.335),l(-.144,.202),l(-.112,.034),l(-.543,.051),l(-.774,.035),l(-.77,.186),l(-1.009,.153),l(-.511,.102),
+N(105.98,81.688),l(-.952,-.826),l(-.198,-.342),l(-.024,-.476),l(.095,-.104),l(.408,.044),l(.312,-.045),l(.781,.177),l(.658,-.076),l(.28,.119),l(.138,.163),l(-.234,.224),l(-.173,.565),l(-.028,.312),l(-.581,.075),l(-.483,.19),M(125.24,92.375),l(-1.312,-.288),l(-1.345,-.434),l(-.218,-.174),l(.061,-.189),l(.376,-.466),l(-1.023,.002),l(-.413,.248),l(-.299,-.072),l(-.416,-.188),l(.166,-.452),l(-.487,-.334),l(-.269,-.014),l(-.735,-.086),l(-.226,-.262),l(.317,-.292),l(-.976,-.524),l(-.556,.118),l(-.386,-.102),l(-.852,-.511),l(-1.277,-.863),l(-.219,-.235),l(.02,-.117),l(.962,-.12),l(.337,.043),l(1.979,.598),l(.981,.204),l(1.772,.202),l(.385,.263),l(.618,.526),l(.426,.642),l(.433,.422),l(.362,.189),l(1.587,.536),l(.316,.203),l(.48,.756),l(.116,.407),l(-.279,.349),l(-.407,.016),M(271.379,92.089),l(-1.202,-.23),l(.641,-.743),l(.358,-.161),l(.279,.058),l(.292,0),l(.355,-.263),l(-.697,-.653),l(.079,-.219),L(272,89.003),l(1.121,-1.35),l(1.454,-1.31),l(.725,-.442),l(.496,-.192),l(1.315,-.194),l(.198,.073),l(.11,.221),l(-.299,.221),l(-.582,.03),l(-.242,.133),l(.349,.44),l(-.755,.78),l(-1.226,1.438),l(-.271,.526),l(.113,.291),l(.11,0),l(.428,-.176),l(.483,-.555),l(.458,-.191),l(1.115,.305),l(-.896,.687),l(.261,.203),l(.229,.072),l(1.423,.565),l(.758,-.03),l(.325,-.408),l(.309,-.059),l(.718,.057),l(.826,.202),l(.616,.231),l(-.297,.292),l(-.373,.233),l(-.708,.467),l(.339,.333),l(.477,.362),l(.26,.014),l(.417,-.161),l(.464,-.132),l(.278,.116),l(.02,.16),l(-.254,.262),l(-.404,.248),l(-.892,.104),L(280.84,93),l(.273,.362),l(.124,.405),l(.28,.231),l(.183,-.203),l(.309,-.262),l(.527,.159),l(-.099,.449),l(.149,.275),l(.716,.028),l(.085,-.015),l(.015,.203),l(-.168,.304),l(-.25,.652),l(-.34,.651),l(-.222,-.072),l(-.71,-.128),l(-.301,-.144),l(-.042,-.651),l(-.601,.406),l(-.374,.015),l(-.095,-.274),l(.497,-.652),l(.011,-.333),l(-.421,-.478),l(-.279,-.072),l(-.388,.392),l(-.423,.291),l(-.365,.146),l(-.435,.204),l(-.552,.536),l(-.496,.334),l(-.881,-.042),l(-.222,-.217),l(.165,-.145),l(1.229,-.408),l(.466,-.522),l(.632,-.363),l(-.699,-.129),l(-.601,-.057),l(-.322,.464),l(-.412,.015),l(-.13,-.159),l(.04,-.493),l(-.757,.016),l(-.148,.29),l(-.41,.218),l(-1.052,.045),l(-.709,-.057),l(-1.139,-.186),l(-1.012,-.085),l(-1.355,.061),l(-1.014,.147),l(-.145,-.188),l(-.215,-.463),l(.187,-.175),l(.561,-.334),l(.734,-.408),l(.502,-.161),l(.636,-.335),M(265.435,98.655),l(-.469,-.057),l(-.497,-.273),l(-.356,-.562),l(.062,-.635),l(.742,-.738),l(.932,-1.043),l(.816,.432),l(-.375,.435),l(-.112,.462),l(-.233,.333),l(-.262,.116),l(-.58,.319),l(-.244,.448),l(.522,.244),l(.168,-.029),l(.279,-.26),l(.42,-.362),l(.617,-.319),l(.309,.057),l(.495,.461),l(-.177,.347),l(-.246,.159),l(-1.134,.42),l(-.682,.044),M(211.34,59.677),l(-.68,-.046),l(.068,-.872),l(-.375,-.333),l(-.958,.161),l(-2.375,.29),l(.107,-.461),l(.56,-.303),l(1.644,-.561),l(-.302,-.478),l(-.102,-.415),l(.106,-.417),l(.398,-.835),l(.434,-.566),l(.254,-.648),l(.331,-.471),l(1.11,.566),l(-.312,.518),l(.791,.386),l(.527,.047),l(.402,-.469),l(.67,.112),l(.806,.289),l(.917,.514),l(.582,.255),l(2.168,.492),l(.442,.271),l(.176,.255),l(-.09,.925),l(.539,.047),l(.57,-.065),l(.934,.046),l(.701,.142),l(1.019,.427),l(-.419,.096),l(-.269,.127),l(-.46,.271),l(-.949,-.046),l(-.623,-.125),l(-1.328,-.125),l(-.438,-.126),L(217.259,58),l(-.528,-.301),l(-1.017,-.237),l(-.528,.017),l(-.203,.271),l(.174,.588),l(-.126,.096),l(-1.314,.161),l(-.673,.493),l(-.588,.302),l(-1.116,.287),M(200.125,19.1),l(-.862,-.015),l(-1.085,-.195),l(-.308,-.664),l(.819,-.304),l(.77,-.072),l(.666,-.024),l(3.475,-.125),l(1.263,-.12),l(1.374,-.026),l(1.714,.324),l(.397,-.094),l(.397,-.377),l(1.303,-.287),l(1.759,-.099),l(1.975,.209),l(.746,-.001),l(2.562,.137),l(2.621,.324),l(1.424,.09),l(1.461,.161),l(.448,-.165),l(-1.433,-.42),l(-1.67,-.352),l(-.816,-.429),l(.293,-.242),l(1.361,-.148),l(1.101,-.246),l(1.431,-.101),l(2.382,-.201),l(1.666,.119),l(1.944,.191),l(1.009,.265),l(1.19,.456),l(.354,.047),l(.273,-.362),l(-.959,-.508),l(-.828,-.292),l(.499,-.248),l(1.45,.121),l(1.832,.168),l(1.653,.07),l(1.639,.46),l(.378,.023),l(.062,-.195),l(-.301,-.539),l(1.781,-.004),l(1.408,.046),l(.832,.269),l(.831,.34),l(.618,-.001),l(-.044,-.268),l(-.331,-.467),l(1.075,-.077),l(3.691,.386),l(2.726,.288),l(1.937,-.077),l(2.987,.018),l(.967,.047),l(.757,.12),l(.126,0),l(1.419,.094),l(1.089,.191),l(.744,.095),l(1.685,.044),l(1.357,.357),l(-.385,.358),l(-1.237,.121),l(-1.206,.356),l(-1.849,.191),l(-.978,-.045),l(-2.191,-.159),l(-2.284,.005),l(-.776,.142),l(-1.915,.168),l(-.597,.465),l(.75,.366),l(.761,.044),l(1.03,-.048),l(1.705,-.279),l(.79,.021),l(.808,.434),l(-.168,.114),l(-1.246,.139),l(-1.38,.207),l(-1.174,.295),l(-2.098,.518),l(-1.316,.224),l(-1.19,.355),l(-.924,.286),l(-2.252,.005),l(.65,.906),l(-.449,.193),l(-2.297,.455),l(-.768,-.019),l(-1.587,-.037),l(-1.462,-.218),l(-2.386,-.164),l(-.66,.33),l(2.591,.695),l(-.662,.141),l(-.967,-.038),l(-1.297,.022),l(-1.068,.022),l(-2.58,-.214),l(-2.009,.063),l(-.134,.66),l(1.257,-.105),l(1.065,.018),l(2.312,.292),l(.557,.157),l(.12,.67),l(-.33,.197),l(-1.031,.12),l(-.515,.705),l(-1.073,.021),l(-.448,-.058),l(-.402,.176),l(.297,.253),l(.759,.25),l(-.328,.136),l(-1.615,.08),l(-.867,-.037),l(-1.71,-.171),l(-.422,.078),l(.41,.791),l(-.08,.231),l(-.649,.289),l(-.767,.155),l(-1.52,-.112),l(-2.039,-.111),l(-1.43,-.227),l(-1.008,.079),l(-1.219,.5),l(1.031,.112),l(.368,.057),l(2.154,.11),l(1.759,.13),l(1.534,.168),l(2.001,.034),l(.66,.34),l(.045,.359),l(-.907,.398),l(-2.685,.268),l(-.927,.115),l(-1.054,.227),l(-1.115,.077),l(-.467,-.28),l(-.797,-.638),l(-.56,.039),l(-.631,.001),l(-1.453,-.318),l(-.001,.17),l(.331,.508),l(-1.477,-.016),l(-1.5,-.129),l(-.875,-.319),l(-1.033,-.471),l(-.388,.058),l(.527,.717),l(-.24,.17),l(-.821,.133),l(-1.72,-.109),l(-2.276,-.033),l(-.972,-.073),l(-1.382,-.394),l(-.642,-.131),l(-.282,.453),l(-.619,.152),l(-1.843,-.316),l(.161,-.586),l(.219,-.228),l(1.525,-.117),l(.61,-.249),l(.961,-.173),l(1.179,.036),l(.499,-.172),l(-1.073,-.4),l(-1.043,-.651),l(.052,-.154),l(.479,-.117),l(1.316,.036),l(1.743,.093),l(.888,.21),l(1.108,.517),l(1.35,.323),l(1.085,.093),l(1.667,-.022),l(.829,-.136),l(.086,-.268),l(.514,-.304),l(-3.019,.001),l(-1.025,-.171),l(-.156,-.85),l(.211,-.154),l(-1.74,-.153),l(-1.963,-.152),l(-.274,0),l(-.631,.114),l(.204,-.758),l(1.159,-.551),l(1.104,-.16),l(1.837,-.003),l(1.164,.037),l(1.37,.076),l(2.023,.311),l(1.342,.115),l(.486,-.158),l(1.132,-.041),l(-3.399,-.802),l(-1.742,-.313),l(-3.555,-1.27),l(-.406,.242),l(-1.398,-.878),l(.025,-.258),l(.313,-.108),l(1.747,.104),l(1.905,-.004),l(2.019,.06),l(1.6,.382),l(2.535,.784),l(1.448,-.043),l(.833,.095),l(-1.387,-.555),l(-2.015,-.317),l(1.208,-.743),l(1.456,-.329),l(1.731,-.025),l(1.529,-.222),l(2.042,-.07),l(1.157,-.112),l(1.414,-.051),l(-1.778,-.479),l(-1.425,-.153),l(-2.501,.027),l(-1.243,.248),l(-1.305,.158),l(-1.425,.202),l(-1.447,.047),l(-.586,.067),l(-1.532,-.438),l(-.214,.111),l(-.543,.156),l(-2.16,-.018),l(-1.58,.365),l(.311,-.828),l(.98,-.292),l(.007,-.202),l(-.606,-.247),l(-1.375,-.156),l(-1.39,.003),l(-4.189,.505),l(-2.031,.672),l(-.408,-.11),l(-.569,-.251),l(.395,-.133),l(.678,-.023),l(-.117,-.316),l(-.698,-.398),l(-1.216,-.056),l(-.216,-.003),M(200.125,20.844),l(.899,-.096),l(.832,.196),l(.339,.5),l(.511,.495),l(.427,.063),l(1.141,.041),l(-.081,-.236),l(.056,-.411),l(.438,-.109),l(.718,.194),l(.718,.322),l(.374,.3),l(-.066,.171),l(.056,.826),l(.764,.442),l(.953,-.017),l(1.276,-.074),l(1.646,.504),l(-1.123,-.264),l(-1.528,.34),l(-1.599,.221),l(-.83,.377),l(-.287,.197),l(-.265,.315),l(-.448,.021),l(-1.493,-.41),l(-.656,.335),l(.465,.43),l(-.029,.235),l(-.412,.196),l(-.392,.04),l(-1.086,-.31),l(-.944,-.311),l(-.26,.645),l(-.117,.068),l(-.083,.049),l(-.888,.041),l(-1.74,-.094),l(-1.458,-.153),l(-.888,-.154),l(-.494,.021),l(-3.115,-1.31),l(-.191,-.276),l(1.971,-.241),l(3.32,.093),l(.889,.097),l(1.573,.1),l(-2.485,-.693),l(-3.019,-.213),l(-1.103,.122),l(-1.43,-.017),l(-.597,.18),l(-1.008,.022),l(-.606,-.198),l(-1.066,-.517),l(-1.425,-.479),l(-.341,-.355),l(1.971,.124),l(2.278,-.175),l(-1.255,-.249),l(-.756,-.351),l(.38,-.305),l(.729,-.132),l(.769,-.023),l(.921,.042),l(.156,-.219),l(-1.799,-.793),l(1.053,-.114),l(1.213,-.182),l(1.13,.087),l(.584,-.046),l(.145,-.18),l(-.514,-.475),l(1.362,.134),l(.941,.066),l(.83,.202),l(1.589,.869),l(.653,.264),l(.772,.24),l(.674,-.001),l(.36,-.039),M(179.067,27.216),l(-1.156,-.056),l(-.604,-.173),l(-.926,-.638),l(-.621,-.193),l(-3.102,-.091),l(-1.487,.081),l(-.622,.001),l(-1.444,-.056),l(-.767,-.154),l(-1.019,-.37),l(-.161,-.234),l(.335,-.138),l(.802,-.001),l(1.687,.232),l(.867,-.021),l(-.031,-.235),l(-.252,-.275),l(-1.344,-.49),l(-.579,-.098),l(-1.075,-.077),l(-1.392,-.196),l(.065,-.397),l(2.246,-.124),l(2.392,.155),l(.77,.376),l(.999,.453),l(1.979,.193),l(2.189,.114),l(1.178,.233),l(.604,.254),l(1.123,.721),l(.581,.446),l(.168,.426),l(-.481,.194),l(-.919,.137),M(185.907,26.758),l(-1.078,-.037),L(184,26.529),l(-1.029,-.484),l(-1.144,-.76),l(-.03,-.216),l(.239,-.099),l(2.296,-.044),l(1.816,.311),l(3.101,.542),l(-.047,.351),l(-.254,.331),l(-.436,.04),l(-1.563,.177),l(-1.043,.08),M(156.886,26.865),l(-1.573,.646),l(-.558,.27),l(-1.85,.042),l(-1.019,.079),l(-1.898,-.15),l(-.577,-.114),l(-.302,-.423),l(.334,-.291),l(1.365,-.177),l(.899,.056),l(2.351,-.102),l(.496,0),l(2.331,.163),M(132.902,31.305),l(-.53,-.186),l(-.95,-.466),l(-.424,-.112),l(-.33,.057),l(-.56,.207),l(-1.269,.059),l(-.786,-.279),l(-.283,-.319),l(.23,-.264),l(1.13,-.097),l(.503,-.133),l(.771,-.134),l(.977,-.399),l(.848,-.211),l(.726,-.172),l(.548,-.344),l(1.083,-.231),l(1.277,-.079),l(2.532,-.158),l(1.68,.016),l(.888,-.29),l(1.038,-.079),l(1.503,.438),l(-.756,.097),l(-.852,.231),l(-.22,.268),l(.12,.266),l(.469,.474),l(-.777,.001),l(-.912,.115),l(-.918,.662),l(-1,-.017),l(-.867,-.981),l(-.694,-.15),l(-.379,.02),l(-.229,.285),l(-.588,.342),l(-.63,.623),l(-.595,.151),l(-.284,.375),l(-.705,.356),l(-.787,.058),M(191.827,30.313),l(-1.266,-.054),l(-2.278,-.165),l(-.426,.058),l(-.332,-.094),l(-.896,-.489),l(-1.185,-.414),l(.192,-.229),l(2.433,-.042),l(1.542,.263),l(1.472,.054),l(.171,0),l(.89,.358),l(-.179,.246),l(.123,.32),l(-.263,.188),M(144.688,31.739),l(-2.222,-.395),l(-.325,-.674),l(.503,-.057),l(.595,-.17),l(.945,-.096),l(.953,-.133),l(1.279,-.059),l(.522,.187),l(.65,.374),l(.659,.186),l(1.55,-.209),l(.617,.149),l(1.624,.762),l(1.016,.351),l(.897,.036),l(.96,-.058),l(1.418,.09),l(.591,-.02),l(1.116,-.169),l(.092,-.297),l(-.557,-.559),l(-.941,-.391),l(-1.347,-.354),l(.96,-.322),l(.524,-.379),l(.569,-.152),l(1.097,-.116),l(.507,.17),l(.773,.678),l(-.017,.413),l(.518,.654),l(.565,.111),l(.9,.036),l(1.805,.406),l(-.334,-.465),l(.151,-.28),l(.409,-.076),l(1.495,.24),l(.932,.39),l(-.292,.409),l(.039,.5),l(-.358,.461),l(-.573,.277),l(-.755,.111),l(-.782,.001),l(-1.682,.095),l(-1.156,-.071),l(-1.757,-.18),l(-.622,-.017),l(-1.129,.277),l(-1.132,.202),l(-.76,.182),l(-.977,.254),l(-1.625,.292),l(-1.338,.2),L(149.23,34.5),l(-.748,-.07),l(-1.445,-.286),l(-.276,-.378),l(.648,-.128),l(1.219,-.038),l(.738,-.146),l(.852,-.075),l(1.166,-.057),l(.622,.017),l(1.09,-.149),l(.483,-.553),l(-2.768,-.087),l(-.925,-.054),l(-1.564,.28),l(-1.625,.168),l(-1.292,.04),l(-.795,.093),l(-1.681,-.347),l(-.479,.167),l(-.92,.075),l(-.979,-.127),l(-.854,-.33),l(.023,-.111),l(.863,-.427),l(1.098,-.058),l(2.047,-.022),l(.96,-.159),M(178.479,33.234),l(-.984,-.219),l(-.193,-.294),l(.764,-.389),l(.433,-.112),l(.088,-.167),l(-.447,-.333),l(-1.161,-.054),l(-2.13,.227),l(-.939,.076),l(-.331,.019),l(-.854,-.276),l(.039,-.335),l(.739,-.02),l(.542,-.244),l(.587,-.057),l(-.466,-.598),l(.176,-.245),l(.132,-.226),l(.49,.018),l(.859,.224),l(1.942,.746),l(.426,.186),l(.46,-.094),l(-.101,-.243),l(-.959,-.486),l(-.371,-.131),l(-.407,-.357),l(.436,-.095),l(.956,-.059),l(.713,.131),l(1.033,.262),l(.572,.168),l(.27,.018),l(.162,-.452),l(.478,-.133),l(.73,.112),l(.717,.168),l(.327,.168),l(.367,.75),l(-.034,.616),l(-.247,.242),l(-.831,.335),l(-.017,.352),l(.35,.625),l(-.361,.147),l(-1.648,-.089),l(-.862,.112),l(-1.446,.003),M(200.125,30.572),l(-.895,.045),l(-.853,.17),l(-.37,.467),l(1.133,.054),l(.984,-.038),l(.046,-.001),l(.847,.11),l(.463,.129),l(.498,.463),l(.727,.424),l(.621,.091),l(.213,-.074),l(.043,-.314),l(.286,-.056),l(1.075,-.002),l(.883,-.187),l(.766,.11),l(.835,.239),l(.665,.257),l(.976,.053),l(.775,-.463),l(1.393,-.281),l(1.704,-.114),l(1.951,-.246),l(1.533,.053),l(2.59,.014),l(.381,.037),l(.79,.314),l(.911,.239),l(1.418,.146),l(.653,.128),l(.21,.037),l(.361,.166),l(.181,.257),l(-.4,.148),l(-1.833,.407),l(-.135,.255),l(.469,.666),l(-2.486,.076),l(-.592,.02),l(-.651,.091),l(-.768,-.053),l(-.846,-.16),l(-.405,-.125),l(-.306,-.667),l(-.833,-.218),l(-.366,.129),l(.072,.723),l(-.536,.127),l(-.747,-.053),l(-.91,.109),l(-.728,-.017),l(-.495,.001),l(-1.342,-.213),l(-.593,-.197),l(-.495,-.017),l(-.209,.433),l(-1.801,.111),l(-.831,.074),l(-1.453,-.069),l(-.404,-.251),l(-.144,-.686),l(-1.237,.129),l(-.389,.181),l(-.326,.325),l(-.955,.2),l(-1.011,-.034),l(-.112,-.027),l(-.704,-.169),l(-1.186,-.575),l(-.675,.489),l(-1.131,-.07),l(-.666,-.688),l(-.442,-.717),l(.587,-.481),l(.019,-.371),l(-.292,-.316),l(-1.249,-.651),l(-.617,-.299),l(-.047,-.338),l(.636,-.133),l(1.226,-.078),l(2.472,-.023),l(.763,.093),l(1.118,.261),l(.188,.04),l(.872,.184),l(-.613,.189),l(-.259,.013),M(128.19,41.985),l(-.926,-.016),l(-1.059,-.102),l(-.362,-.466),l(-.549,-.467),l(-.432,-.259),l(-1.123,-.363),l(-1.36,-.067),l(-.951,-.138),l(-.469,-.19),l(-.168,-.174),l(.537,-.106),l(.589,-.298),l(.481,-.211),l(.08,-.386),l(-.437,-.809),l(.552,-.001),l(.468,-.177),l(.307,-.372),l(1.104,-.533),l(.526,-.588),l(-.121,-.32),l(-.271,-.16),l(-1.229,-.677),l(-.375,-.448),l(.869,-.001),l(.823,-.056),l(1.455,.051),l(.97,.016),l(1.515,-.092),l(1.284,-.146),l(1.242,-.074),l(.495,.125),l(3.242,.801),l(.918,.088),l(.708,-.055),l(1.316,-.127),l(1.223,.016),l(.771,.07),l(1.35,.373),l(2.389,.815),l(-.242,.143),l(-.432,.036),l(-.26,.072),l(-1.609,.322),l(-1.073,.144),l(-1.829,.428),l(-1.069,.319),l(-1.604,.725),l(-1.025,.563),l(-.549,.089),l(-.974,.124),l(.066,.924),l(-.271,.504),l(-.662,.278),l(-1.215,.124),l(-1.213,-.067),l(-.521,.485),l(-.898,.312),M(190.483,39.666),l(-1.146,-.208),l(-.146,-.524),l(-.941,-.806),l(-.207,-.582),l(.058,-.389),l(.27,-.657),l(.377,-.321),l(1.256,.033),l(-.089,-.16),l(-.416,-.266),l(-.185,-.286),l(.211,-.09),l(.234,-.072),l(2.154,-.058),l(1.215,.087),l(1.464,.248),l(1.282,.051),l(1.316,-.146),l(1.051,.016),l(.694,.105),l(.639,.213),l(-.007,.089),l(-.224,.179),l(-.824,.428),l(-.874,.746),l(-1.513,.92),l(-1.386,.073),l(-2.379,-.154),l(-1.269,.055),l(1.392,.717),l(-.188,.315),l(-.855,.369),l(-.964,.072),M(181.204,41.523),l(-.491,-.085),l(-1.101,-.552),l(-.952,-.641),l(-1.014,-.468),l(-.978,-.225),l(-1.438,-.12),l(-.55,-.174),l(-2.255,-1.066),l(.866,-.654),l(.653,.14),l(1.032,.474),l(1.063,.227),l(.46,.052),l(.615,-.283),l(.908,-.619),l(.415,-.036),l(.018,-.212),l(-1.062,-.565),l(-1.068,-.424),l(-.177,-.231),l(.132,-.107),l(1.683,.086),l(.711,-.215),l(.42,0),l(.996,.39),l(.56,.035),l(.58,-.055),l(.435,-.25),l(1.232,-.127),l(1.354,.069),l(.912,.23),l(-.324,.268),l(-.58,.125),l(-.323,.338),l(-1.55,.375),l(-.392,.16),l(-.069,.194),l(.253,.247),l(.506,.105),l(.692,-.089),l(1.08,.174),l(.868,.245),l(.391,.017),l(.564,.262),l(.186,.438),l(-.681,.352),l(-.156,.35),l(-.271,.68),l(-.457,.366),l(-.508,.14),l(-.658,.019),l(-.582,-.103),l(-.773,-.346),l(-.653,-.103),l(.013,.208),l(1.054,.553),l(-.817,.399),l(-.77,.036),M(243.524,60.394),l(-.234,-.208),l(-1.199,-.235),l(-.673,-.331),l(-.154,-.269),l(.346,-.064),l(.616,-.461),l(-1.378,-.521),l(-1.132,-.125),l(-.76,-.349),l(-.929,-.731),l(-.035,-.511),l(-1.115,-.062),l(-1.311,-.366),l(-.675,-.031),l(.284,.767),l(-.155,.096),l(-.409,-.015),l(-1.704,-.332),l(-.309,.033),l(-.325,.304),l(-.441,.288),l(-1.312,.082),l(-1.349,-.173),l(-1.343,-.189),l(-.813,-.254),l(-.052,-.319),l(.196,-.4),l(.382,-.354),l(1.066,-.163),l(.192,-.178),l(-.128,-.516),l(.206,-.033),l(1.357,.11),l(1.408,.175),l(.517,.144),l(.962,.626),l(.051,-.386),l(-.154,-.193),l(.077,-.194),l(.585,-.033),l(.977,-.099),l(.652,-.163),l(.649,-.114),l(.515,.063),l(.785,.031),l(.166,-.275),l(-1.138,-.825),l(-.773,-.356),l(-.119,-.228),l(.167,-.163),l(.586,-.066),l(.72,-.246),l(1.409,-.591),l(.361,-.541),l(.771,-.46),l(.493,-.379),l(-.109,-.593),l(-.899,-.841),l(-.407,-.496),l(-.541,-.364),l(-.414,.001),l(-1.258,-.33),l(-1.041,-.481),l(-.244,-.467),l(-.527,-.384),l(-.442,.202),l(-.551,.202),l(-.825,-.015),l(-.293,.117),l(-.62,.018),l(-1.255,.169),l(-.214,-.667),l(1.032,-.052),l(1.23,-.103),l(.163,-.269),l(-1.604,-.618),l(-1.552,-.67),l(-.879,-.015),l(-.567,-.185),l(-.169,-.542),l(-.677,-.339),l(-.45,-.05),l(-.918,-.306),l(-.687,-.341),l(-.385,-.119),l(-.611,.155),l(-.81,-.187),l(-1.177,-.238),l(-.489,-.085),l(-.379,.138),l(.529,.307),l(.453,.05),l(2.838,.712),l(.438,.271),l(.069,.306),l(-.505,.221),l(-.669,.069),l(-.541,-.033),l(-.757,-.049),l(-.818,-.252),l(-1.153,-.27),l(-.667,-.066),l(-.323,.17),l(.044,.204),l(.426,.236),l(.259,.438),l(-1.703,-.553),l(-.47,-.05),l(-.396,.119),l(-.63,.153),l(-.767,-.218),l(-.693,-.117),l(-.859,.12),l(-1.474,-.184),l(-1.995,-.167),l(-1.321,.037),l(-1.146,-.032),l(-.862,-.186),l(-.003,-.597),l(-.363,-.153),l(-.904,-.049),l(-.396,.342),l(-.623,.086),l(-1.214,-.049),l(-1.076,-.168),l(-1.303,-.477),l(-.415,-.376),l(.123,-.275),l(.868,-.07),l(1.131,.067),l(1.212,.101),l(.879,-.019),l(.312,-.19),l(-.934,-.463),l(-.8,-.275),l(-.905,-.102),l(-1.106,-.119),l(-.752,.036),l(-.539,-.017),l(-1.249,-.223),l(.114,-.416),l(.292,-.557),l(.34,-.14),l(.646,-.054),l(.081,-.227),l(-1.082,-.4),l(-.044,-.175),l(.449,-.79),l(1.197,-.919),l(.565,-.284),l(.918,-.321),l(.74,-.374),l(.423,-.037),l(.37,-.178),l(.698,-.001),l(.481,-.125),l(.71,-.09),l(1.436,-.109),l(1.348,.033),l(.857,.194),l(-.92,.393),l(-.815,.48),l(-1.394,.639),l(-.43,.529),l(.169,.369),l(1.256,.541),l(-.444,.298),l(-.076,.402),l(.257,.313),l(.862,.554),l(1.559,.621),l(-.096,.121),l(-1.272,.331),l(-.072,.31),l(.959,.033),l(1.504,.101),l(.654,-.639),l(-.103,-.415),l(-.343,-.277),l(-.724,-.103),l(-.422,-.138),l(-.884,-.538),l(.101,-.157),l(.506,-.245),l(.473,-.193),l(1.001,.12),l(.837,-.071),l(-1.204,-.47),l(-.703,-.034),l(-.793,-.279),l(-.056,-.193),l(.053,-.545),l(.886,-.319),l(1.207,.086),l(1.509,-.056),l(-.939,-.281),l(-1.233,-.351),l(.793,-.303),l(1.288,-.198),l(1.044,-.126),l(1.688,-.323),l(1.114,.016),l(.642,.052),l(.833,.141),l(.782,.478),l(1.536,.97),l(.058,.141),l(-.583,.687),l(-.709,.632),l(.038,.733),l(.364,.086),l(.65,.033),l(1.088,-.315),l(.284,-.455),l(.595,-.088),l(.791,.034),l(.454,.174),l(-.006,.262),l(.16,.47),l(.875,.189),l(.196,-.122),l(-.204,-.854),l(.218,-.123),l(.456,-.474),l(1.038,-.265),l(.98,-.054),l(.748,.034),l(.98,.174),l(1.172,.138),l(1.151,-.09),l(.688,.139),l(.327,.262),l(.621,.331),l(.574,.191),L(235.438,40),l(0,.191),l(-.531,.088),l(-.484,.279),l(-.818,.262),l(-.148,.225),l(.45,.259),l(.427,.068),l(.897,-.417),l(.652,-.174),l(.502,.051),l(.476,.242),l(.365,.466),l(.516,.413),l(.342,-.242),l(1.304,-.798),l(1.935,.256),l(.915,.361),l(-.051,.069),l(-.638,.346),l(-.708,.517),l(1.167,-.054),l(.455,-.173),l(1.078,-.105),l(.033,.704),l(.797,.324),l(.523,-.069),l(.831,-.207),l(1.316,-.088),l(.816,.221),l(.566,.273),l(-.162,.154),l(-.461,.223),l(-1.87,.43),l(-.238,.272),l(.523,.253),l(.456,-.068),l(.747,-.171),l(1.235,-.122),l(.406,-.29),l(.361,-.103),l(.479,.067),l(.51,.187),l(.544,.339),l(.636,.522),l(-1.019,.002),l(-1.2,.053),l(-.424,.135),l(.059,.269),l(.372,.134),l(1.333,.065),l(.938,.183),l(.543,.217),l(.233,.301),l(-.37,.034),l(-.748,.001),l(-1.011,-.082),l(-.875,-.216),l(-.824,-.065),l(-.316,.185),l(1.23,.583),l(-.216,.201),l(-1.552,.12),l(.245,.283),l(.437,.166),l(.551,.032),l(1.331,.364),l(1.312,.347),l(.247,.182),l(.039,.282),l(.351,.38),l(.75,-.217),l(.536,.049),l(1.413,.295),l(.298,-.067),l(.649,-.15),l(.61,.032),l(.752,.379),l(.862,.477),l(.376,.346),l(-.685,.1),l(-.801,.117),l(-.027,.444),l(.795,-.001),l(1.405,-.052),l(.51,-.132),l(.895,.048),l(-.386,.559),l(.918,.179),l(.514,-.001),l(.943,-.379),l(.685,.343),l(1.089,.407),l(.194,.098),l(-.275,.229),l(-.254,.099),l(-.103,.326),l(-.819,.05),l(-.718,-.21),l(-.247,-.048),l(-.794,.213),l(.968,.454),l(.279,.162),l(.057,.276),l(-1.057,.197),l(-.356,.228),l(-.312,.292),l(-.372,-.113),l(-.819,-.583),l(-.29,1.103),l(.354,.903),l(-.419,.065),l(-.677,-.257),l(-.751,-.176),l(-.205,-.177),l(-.018,-.243),l(-.315,-.274),l(-.93,.276),l(-.743,-.613),l(.051,-.292),l(.27,-.374),l(-.304,-.129),l(-.224,-.016),l(-.992,-.08),l(-.718,-.292),l(-1.17,-.617),l(-.769,-.292),l(-.762,-.048),l(-.452,.23),l(-.645,.083),L(250,52.592),l(.281,.179),l(1.05,.682),l(-.321,.114),l(-.686,.05),l(-.359,-.259),L(249.277,53),l(-.646,-.21),l(.275,.488),l(.859,.972),l(.604,.015),l(.587,.08),l(.5,.581),l(.612,.805),l(.513,.432),l(.615,-.321),l(.285,.047),l(.592,.399),l(.585,.271),l(1.38,.396),l(-.634,.113),l(-.213,.208),l(.254,.19),l(.568,.286),l(.962,.444),l(.324,.237),l(.242,.682),l(-.112,.428),l(-1.302,-1.155),l(-.554,-.237),l(-.027,.238),l(.118,.27),l(1.055,1.281),l(-.01,.3),l(-.926,-.125),l(.053,.205),l(.432,.409),l(.378,.519),l(-.563,-.172),l(-.615,-.313),l(-.889,-.693),l(-.145,-.031),l(-.719,.064),l(-.91,-.188),l(-1.44,-.662),l(-.319,-.285),l(-1.062,-.665),l(.187,.777),l(-1.22,-.473),l(-.567,-.158),l(-.872,-.03),l(.095,.222),l(.799,.696),l(.853,.426),l(1.842,.645),l(1.296,.644),l(.774,.549),l(.442,.486),l(.429,.689),l(-1.833,-.341),l(-1.524,-.421),l(-1.251,-.28),l(-1.444,-.107),l(-1.092,-.25),l(-.898,-.644),l(-1.146,-.14),l(-.638,-.204),l(-.635,-.141),l(-.058,.145),M(146.194,38.698),l(.818,-.037),l(.78,-.125),l(1.138,-.548),l(.895,-.019),l(1.723,.243),l(.939,.262),l(-.188,.877),l(.515,-.071),l(.66,-.019),l(.792,-.229),l(.599,-.141),l(.758,.016),l(.334,-.071),l(-.989,-.965),l(.156,-.036),l(1.38,.138),l(1.208,.245),l(.675,.245),l(.259,.245),l(.194,.508),l(.965,1.063),l(.638,.346),l(1.045,-.315),l(.14,-.261),l(-1.243,-1.361),l(-.439,-1.321),l(.228,-.354),l(1.91,.262),l(1.775,.156),l(2.031,.719),l(.36,.175),l(.3,.316),l(.16,.701),l(.511,.645),l(.352,.26),l(.856,.606),l(.048,.19),l(-.178,.243),l(-.333,.605),l(.179,.31),l(.224,.12),l(1.4,.649),l(.509,.171),l(1.151,.254),l(1.513,.1),l(2.056,.576),l(1.012,.39),l(.364,.793),l(-.168,.101),l(-1.071,-.082),l(-.811,-.234),l(-.945,-.234),l(-.551,.169),l(-.665,.204),l(-1.021,.036),l(-.256,.118),l(.208,.689),l(1.087,-.069),l(.614,-.152),l(.676,-.119),l(.876,.536),l(-.01,.235),l(-.526,.151),l(-.517,.252),l(-.583,.102),l(-1.417,.12),l(-1.049,-.015),l(-1.194,-.082),l(-1.594,-.248),l(-2.278,-.499),l(-.553,-.217),l(-.436,-.335),l(-.482,-.033),l(-.581,.102),l(-.402,.37),l(-1.114,.505),l(-1.055,.019),l(-1.411,.103),l(-1.253,.42),l(-2.753,.356),l(-1.42,.019),l(-1.205,.086),l(-1.984,-.063),l(-.483,.101),l(-.916,-.015),l(-1,-.282),l(-.061,-.468),l(.198,-.101),l(.002,-.302),l(-.395,-.2),l(-.462,-.1),l(-3.146,-.112),l(-1.258,-.115),l(-.864,-.167),l(-.801,-.2),l(-.753,-.504),l(-1.274,-.554),l(.303,-.069),l(.531,-.222),l(1.572,-.054),l(.603,-.188),l(.558,.016),l(.91,-.019),l(.904,-.087),l(1.716,.031),l(.935,-.002),l(2.14,.047),l(1.09,-.002),l(1.711,-.038),l(.415,-.154),l(-.681,-.221),l(-2.312,-.492),l(-1.942,-.202),l(-4.059,-.061),l(-1.569,-.014),l(-1.694,.055),l(-.955,.053),l(-.604,.001),l(-1.651,-.529),l(.011,-.207),l(.28,-.069),l(.823,-.053),l(1.315,-.209),l(.996,.032),l(1.78,-.141),l(.931,-.105),l(.5,-.277),l(-.392,-.346),l(-2.023,.073),l(-1.578,.159),l(-.393,-.051),l(-.554,-.189),l(-.677,-.346),l(-.65,-.19),l(-.692,.054),l(-.709,-.242),l(-.039,-.279),l(1.304,-.981),l(2.62,-.848),l(.909,-.143),l(.373,-.177),l(1.297,-.267),l(1.324,-.162),l(.701,-.267),l(1.113,-.091),l(1.026,.246),l(-.07,.212),l(-.548,.354),l(-.021,.81),M(251.273,99.526),l(-.188,.153),l(-.16,-.291),l(.192,-.077),l(-.02,-.256),l(.143,-.069),l(-.042,-.154),l(-.224,0),l(-.606,-.231),l(.077,-.229),l(-.31,-2.819),l(-.174,-.274),l(-.488,-.288),l(-.771,-.025),l(-.41,.176),l(-.381,-.085),l(-.505,-.36),l(-.273,-.085),l(-.632,.526),l(-.514,.626),l(-1.139,2.22),l(-1.139,1.45),l(-1.161,-.124),l(-.402,.06),l(-.363,.435),l(-.174,.375),l(-1.093,-.095),l(-1.855,-.004),l(-2.508,-.029),l(-1.76,.009),l(-.968,.523),l(-.534,.305),l(-1.754,.814),l(-.545,.164),l(-.146,.434),l(-.163,.512),l(-.44,.275),l(-1.156,.197),l(-1.305,-.138),l(-1.123,-.01),l(-.93,.091),l(-.47,.203),l(-.162,.375),l(.018,.319),l(.16,.471),l(.047,.362),l(-.875,.427),l(-1.464,.452),l(-.944,.163),l(-.919,.062),l(-.88,.262),l(-.939,.478),l(-.925,.506),l(-.524,.117),l(-.573,-.068),l(-.497,-.169),l(-.371,-.427),l(-.012,-.33),l(.044,-.218),l(.707,-.525),l(.414,-.294),l(.264,-.433),l(.294,-.544),l(.377,-.576),l(.028,-.746),l(-.054,-.545),l(-.876,-3.16),l(-2.529,-1.05),l(-.26,-.33),l(.11,-.318),l(-.307,-.235),l(-.916,-.181),l(-.184,-.294),l(-.178,-.135),l(-.628,.024),l(-.46,-.465),l(-.101,-.429),l(-2.15,-1.061),l(-3.975,-1.576),l(-.977,-.386),l(-.797,-.227),l(-.805,.189),l(-1.469,.592),l(-.707,-.074),l(-.542,.049),l(-.196,-.144),l(-.156,-.115),l(-.474,-.041),l(-.855,.083),l(-.197,-.116),l(-.028,-.282),l(-.373,.075),l(-.412,.191),l(-.219,.06),l(-.573,.141),l(-.506,-.098),l(-.064,-.185),l(-.469,-.086),l(-.241,-.271),l(-.502,-.013),l(-.16,.247),l(-.338,-.48),l(-.271,.012),l(-.02,-.185),l(-1.425,-.208),l(-.518,.076),l(-.833,-.451),l(-.468,-.46),l(-.279,-.371),l(-.896,-.748),l(-.501,.036),l(.131,.347),l(.387,.588),l(-.68,-.003),l(-2.687,.029),l(-2.798,-.029),l(-1.348,.007),l(-2.105,-.003),l(-2.915,.016),l(-2.781,-.029),l(-2.131,.012),l(-2.935,-.014),l(-.601,.003),l(-4.84,-.018),l(-3.617,.004),l(-.875,.005),l(-3.821,-.023),l(-1.089,.035),l(-4.13,-.021),l(-.74,-.011),l(-5.117,.028),l(-1.687,-.006),l(-2.87,.001),l(-3.938,-.008),l(-4.588,.025),l(-1.335,-.022),l(-2.579,-.001),l(-.194,-.013),l(-.187,-.151),l(-.509,-.305),l(-.071,-.437),l(.074,-.346),l(-.708,.25),l(-.373,-.029),l(-.331,-.305),l(-.162,-.496),l(-.125,-.189),l(-.385,.088),l(-.23,.205),l(-.483,.059),l(-.721,-.495),l(-.119,-.425),l(-.201,-.821),l(-1.051,.104),l(-1.01,-.277),l(-.487,-.087),l(-.173,-.087),l(-.143,-.396),l(-.438,-.352),l(-.591,.222),l(-1.236,.046),l(-.461,-.117),l(-.383,-.249),l(-.106,-.25),l(.257,-.648),l(.458,-.222),l(-.227,-.294),l(-1.24,-.086),l(.617,-.518),l(.398,-.281),l(.547,-.149),l(.605,-.508),l(-.874,.006),l(-.621,.149),l(-.362,-.043),L(116,83.422),l(-.039,-.978),l(-.789,.002),l(-1.015,-1.066),l(.132,-.238),l(.034,-.53),l(-.547,.056),l(-.83,.492),l(-1.266,-.934),l(-.384,-.521),l(-.204,-.402),l(-.068,-.432),l(.419,-.404),l(.161,-.254),l(.436,-.3),l(-.358,-.689),l(-.393,-.777),l(.163,-.788),l(-.402,-.255),l(-2.025,-.763),l(-.98,-.314),l(-.189,-.029),l(-.512,-.393),l(-1.67,-1.882),l(-1.769,-1.768),l(-.814,-.58),l(-2.048,-1.175),l(-.35,-.322),l(-.371,-.492),l(-.316,-.199),l(-.832,-.073),l(-.495,.126),l(-.731,.498),l(-1.225,.67),l(-.848,.205),l(-.238,.325),l(-.945,-.673),l(-2.162,-1.318),l(-.348,-.292),l(-.173,-.387),l(-.332,-.388),l(-.739,-.059),l(-2.424,.122),l(-.84,-.074),l(-.196,-.279),l(-.089,-1.046),l(-.026,-2.167),l(.043,-4.334),l(.026,-2.183),l(-.129,-2.796),l(-.052,-2.335),l(-.039,-2.259),l(.003,-2.855),l(-.102,-.483),l(.71,.024),l(.9,-.086),l(.964,.116),l(2.012,.451),l(1.601,.518),l(1.214,.266),l(1.04,.115),l(.731,.032),l(1.619,.164),l(.888,.232),l(.429,.149),l(.254,-.034),l(-.452,-.601),l(-.357,-.2),l(-.345,-.15),l(-.064,-.419),l(1.344,-.423),l(.745,.066),l(.578,-.068),l(.15,-.102),l(1.736,.148),l(.771,-.001),l(1.125,-.373),l(.309,-.186),l(.442,0),l(.656,-.221),l(.759,-.069),l(.866,-.086),l(.734,-.086),l(.469,-.239),l(.392,-.188),l(.771,-.104),l(1.045,-.002),l(.872,.123),l(-.445,.404),l(-.352,.119),l(-1.101,.137),l(-1.092,.373),l(-1.244,.171),l(-1.22,.271),l(-.699,.522),l(-1.767,.255),l(-.681,.487),l(.846,.266),l(1.441,.748),l(-.219,-.55),l(.168,-.351),l(1.196,-.253),l(.39,-.235),l(.726,-.421),l(1.727,-.053),l(.96,-.372),l(1.158,-.389),l(2.066,-.173),l(.643,-.338),l(.921,-.272),l(.821,-.189),l(.476,-.239),l(-.178,-.272),l(-.702,-.392),l(-.655,-.444),l(.899,.101),l(.764,.272),l(.701,.306),l(.412,.324),l(.376,.476),l(.449,.523),l(.393,.235),l(1.246,.486),l(.186,.067),l(1.154,.216),l(.394,-.018),l(.199,-.151),l(-.511,-.639),l(.07,-.27),l(.548,-.035),l(.334,-.136),l(.627,-.103),l(.383,.354),l(.059,.421),l(-.205,.287),l(.833,.133),l(.938,-.069),l(1.136,-.457),l(.536,-.49),l(.479,-.069),l(1.131,.015),l(1.536,.267),l(1.745,.435),l(1.396,.334),l(2.095,.349),l(1.024,.216),l(.619,.066),l(1.572,.282),l(1.121,.065),l(1.144,.148),l(1.096,.032),l(1.4,-.086),l(.899,.099),l(1.008,.282),l(.982,.349),l(.434,.249),l(.191,.333),l(-.524,.134),l(-.935,-.032),l(-.566,.051),l(-.849,.135),l(-.354,.714),l(3.323,.358),l(1.726,.079),l(1.749,.014),l(.868,-.068),l(.738,-.051),l(.94,-.167),l(.895,-.118),l(.938,-.101),l(.886,.032),l(1.432,.477),l(1.452,.179),l(.42,.115),l(1.225,.443),l(-.013,.312),l(-.504,.083),l(-.35,.247),l(.305,.147),l(1.823,.979),l(-.162,-.392),l(-.024,-.312),l(.456,-.05),l(.617,-.132),l(-.062,-.181),l(-.972,-.656),l(-.128,-.198),l(-.145,-.445),l(.121,-.745),l(.35,-.034),l(1.944,-.136),l(.928,-.151),l(.207,-.299),l(.459,-.217),l(.613,-.035),l(1.098,.281),l(1.528,.279),l(.968,.064),l(.969,-.102),l(.612,.414),l(.248,.082),l(.962,.213),l(1.211,.13),l(.678,.081),l(1.146,-.002),l(.879,-.2),l(1.755,-.02),l(1.876,.029),l(1.07,-.052),l(1.18,-.267),l(.959,.478),l(.95,.296),l(.522,-.018),l(.243,-.314),l(-.017,-.513),l(-.666,-.231),l(-.732,-.131),l(-1.377,-.064),l(.089,-.449),l(1.193,-.085),l(.575,.065),l(.804,.214),l(.871,.198),l(.858,.048),l(.498,.198),l(.088,.183),l(-.095,.132),l(-.287,.86),l(.179,.51),l(.304,.164),l(.177,.065),l(.792,.097),l(.951,.311),l(-.071,-.559),l(-.466,-.989),l(.137,-.48),l(.258,-.266),l(.712,.015),l(.811,-.035),l(1.229,-.85),l(.492,-.051),l(.479,-.001),l(.517,-.151),l(.033,-.133),l(-.15,-.367),l(-.375,-.35),l(-.307,.001),l(-.81,.185),l(-.988,-.082),l(.535,-.52),l(.806,-.069),l(.435,-.168),l(.572,-.001),l(.739,.4),l(.464,.167),l(.065,-.268),l(-.081,-.956),l(-.744,.069),l(-.897,-.032),l(-.68,-.116),l(-.859,-.318),l(-.725,.085),l(-1.245,-.183),l(-.861,-.234),l(-.956,-.218),l(-.657,-.338),l(-.092,-.136),l(-.022,-.324),l(.33,-.137),l(.842,-.463),l(-.486,-.221),l(-1.188,-.375),l(.09,-.207),l(.58,-.604),l(.593,-.294),l(.387,-.035),l(1.032,.257),l(.139,-.035),l(.173,-.346),l(-.709,-.362),l(-.201,-.277),l(.23,-.035),l(.551,-.331),l(.367,-.035),l(1.841,-.021),l(.559,.086),l(.728,.189),l(1.26,.449),l(.432,.328),l(.195,.38),l(-.246,.603),l(1.261,.53),l(.809,.495),l(1.134,.493),l(-.377,.341),l(-.985,.036),l(-.474,.273),l(-.416,.557),l(.471,.067),l(1.071,.233),l(.805,.049),l(.387,-.136),l(.597,-.001),l(1.477,.351),l(-.335,.353),l(-.563,.219),l(.092,.151),l(.796,.467),l(.358,.601),l(.025,.833),l(.303,-.063),l(.021,-.004),l(.411,-.067),l(.608,-.367),l(.655,-1.137),l(.668,-.42),l(.523,.016),l(.731,.284),l(1.064,.55),l(.473,.383),l(.209,.45),l(-.159,.433),l(-.336,.034),l(-.796,-.098),l(-.202,.299),l(-.043,.415),l(.35,.396),l(1.146,.659),l(.61,.493),l(.463,.279),l(.595,-.166),l(.896,-.167),l(.369,-.132),l(.208,-.66),l(.764,-.398),l(.599,-.416),l(.249,-.664),l(.363,-.75),l(.237,-.184),l(1.394,.081),l(.329,-.067),l(.134,-.518),l(-.985,-.333),l(-.918,-.35),l(.088,-.891),l(.595,-.271),l(.787,.032),l(.42,-.068),l(.571,.016),l(3.459,.616),l(1.325,.669),l(.506,-.001),l(.553,-.068),l(.454,.201),l(.244,1.036),l(-.474,.268),l(-.431,.101),l(-.243,-.05),l(-.718,-.532),l(-.263,0),l(-.799,.269),l(.123,.316),l(.309,.515),l(.699,.729),l(.855,.528),l(1.108,.576),l(.024,.313),l(-.478,.642),l(-.439,.181),l(-1.407,.772),l(-.674,.083),l(-1.123,.509),l(-.763,-.276),l(-1.654,-.962),l(-.586,-.262),l(-.497,-.048),l(-.684,.281),l(1.364,.521),l(1.483,.896),l(-.708,.067),l(-.691,-.081),l(-1.288,.084),l(-.375,-.129),l(-.596,-.62),l(-1.147,-.014),l(-1.857,.118),l(-.253,.229),l(.614,.244),l(1.311,.421),l(-.159,.195),l(-.611,.327),l(-2.045,1.106),l(-.657,.179),l(-.527,.001),l(-.859,-.241),l(-.816,-.484),l(-.225,-.081),l(-1.189,-.225),l(-.736,-.259),l(-.598,-.112),l(-.947,.014),l(-.289,.004),l(-1.214,.174),l(1.503,.278),l(1.136,.21),l(1.751,.774),l(1.629,.433),l(1.233,.126),l(1.02,.031),l(-.618,1.091),l(-1.237,.705),l(-.856,.432),l(-.728,.161),l(-.829,.049),l(-.928,-.126),l(-1.062,-.38),l(-.048,.351),l(-.025,.287),l(.321,.572),l(-.02,.159),l(-.741,.031),l(-.058,.002),l(-1.365,-.108),l(-1.649,-.41),l(-.884,-.078),l(-2.962,-.322),l(2.146,.864),l(1.576,.156),l(1.367,.267),l(.562,.205),l(.33,.268),l(-.011,.19),l(-.642,.333),l(-1.106,.207),l(-1.429,-.076),l(-.511,-.062),l(-.367,.269),l(1.254,.423),l(-.469,.426),l(-1.06,.316),l(-1.454,.662),l(-.421,.252),l(.218,.704),l(-.313,.235),l(-.909,.205),l(-.31,.282),l(-.529,.64),l(-.276,.296),l(-.241,.669),l(-.274,.543),l(-.323,.666),l(.056,.416),l(-.161,.554),l(.123,.875),l(.136,.536),l(.598,.366),l(.25,.015),l(.257,.091),l(.664,.014),l(1.164,-.094),l(.276,.045),l(.367,.29),l(.413,.763),l(.813,1.157),l(.22,.668),l(-.132,.91),l(.673,.014),l(1.874,-.428),l(1.261,-.033),l(.723,.074),l(.535,.157),l(1.062,.311),l(2.129,.435),l(1.896,.903),l(.993,.933),l(3.5,.67),l(.644,.225),l(.982,.403),l(.986,.253),l(.553,.104),l(.702,-.091),l(.453,.044),l(.828,-.077),l(1.245,.163),l(1.407,.207),l(.401,.194),l(-.297,.702),l(-.142,.85),l(.154,.283),l(.307,.342),l(.07,.416),l(-.115,1.025),l(-.309,.593),l(.022,.208),l(.604,.266),l(.481,.339),l(.264,.354),l(.046,.488),l(-.076,.354),l(.97,.116),l(.963,.47),l(.676,.588),l(.392,.588),l(.078,.162),l(.64,.014),l(.726,.41),l(.907,.601),l(-.349,-.66),l(-.22,-.279),l(.134,-.338),l(.573,-.414),l(.365,.176),l(.381,.456),l(.262,.353),l(.165,-.354),l(.107,-.545),l(-.215,-.456),l(.541,-.532),l(.139,-.546),l(-.183,-.517),l(-.337,-.458),l(-.261,-.754),l(-.004,-.548),l(-.205,-.593),l(-.218,-.43),l(.615,-.016),l(-.097,-.476),l(-.296,-.252),l(-.657,-.163),l(-.375,-.282),l(-.326,-.923),l(1.252,-.271),l(.872,-.241),l(.625,-.271),l(1.758,-.949),l(.629,-.302),l(1.043,-.935),l(.434,-.544),l(.237,-.665),l(.054,-.529),l(-.257,-1.045),l(-.246,-.531),l(-.239,-.319),l(-.938,-.729),l(-.467,-.274),l(-1.105,-.532),l(-.363,-.122),l(-.453,-.274),l(-.151,-.046),l(-.293,-.351),l(.08,-.107),l(.868,-.522),l(.553,-.875),l(.293,-.416),l(.25,-.092),l(.447,-.017),l(.295,-.277),l(-.106,-.523),l(-.18,-.355),l(-.316,-.402),l(-.048,-.202),l(.258,-.357),l(.005,-.264),l(-1.751,-.105),l(1.084,-.92),l(.503,-.704),l(.007,-.125),l(-.316,-.203),l(-.609,-.265),l(-.244,-.266),l(-.043,-.533),l(.305,-.425),l(.554,-.315),l(.57,-.19),l(.827,.062),l(1.781,.374),l(1.097,.234),l(.753,.077),l(1.44,.013),l(1.08,-.144),l(.86,-.222),l(.21,-.047),l(.179,-.063),l(.589,.078),l(.991,.407),l(.254,.125),l(.754,.454),l(.918,.375),l(.796,.437),l(-.294,.391),l(.406,.233),l(1.698,.496),l(1.958,.354),l(.725,-.033),l(.368,-.203),l(.339,.295),l(-.013,.404),l(-.577,.343),l(-.123,.45),l(.438,1.327),l(.136,.722),l(.23,.414),l(-.049,.353),l(-.248,.169),l(-.445,-.014),l(-.347,-.015),l(-.138,.674),l(.375,.274),l(1.137,-.415),l(.366,-.047),l(.781,-.047),l(.286,.015),l(.677,.32),l(.378,.351),l(.004,.259),l(-.081,.123),l(.277,.32),l(.516,-.184),l(.306,-.046),l(1.173,-.079),l(.636,-.184),l(.436,-.383),l(.333,-.551),l(.326,.015),l(.194,.122),l(.693,.717),l(.042,-.062),l(.108,-.764),l(.317,-.583),l(.475,-.262),l(.539,-.385),l(-.651,-.505),l(.008,-.308),l(.272,-.139),l(.98,-.094),l(.193,-.139),l(.512,-.665),l(.667,.37),l(.607,.599),l(.785,.506),l(.596,.797),l(1.045,.764),l(.264,.352),l(-.344,.291),l(.095,.335),l(.573,-.031),l(.365,.777),l(.182,.183),l(.324,.121),l(.743,.136),l(.281,.258),l(.133,.38),l(-.379,.092),l(-.416,.168),l(.411,.318),l(.397,.227),l(.77,.196),l(.279,.227),l(.034,.424),l(-.056,.076),l(-.409,.106),l(-.676,-.029),l(-.745,-.12),l(-.316,.061),l(.091,.166),l(.273,.181),l(.189,.241),l(.333,.513),l(.411,.226),l(.634,.029),l(.462,.18),l(.838,.496),l(.899,.435),l(.246,.33),l(-.035,.195),l(-.447,.781),l(.508,.059),l(.663,-.166),l(.786,-.077),l(.79,.164),l(.574,.194),l(1.162,.49),l(.981,.132),l(1.517,.295),l(-.184,.253),l(-.718,.21),l(-.736,.21),l(-.663,.046),l(-.834,.24),l(-.583,.402),l(-.65,.225),l(-1.032,.061),l(-.286,.075),l(-.324,.268),l(.029,.371),l(-.271,.535),l(1.175,-.343),l(.542,-.09),l(.649,-.105),l(1.201,-.774),l(1.251,-.478),l(1.146,-.106),l(.662,.237),l(.35,.341),l(-.398,.446),l(.036,.119),l(.307,.296),l(.616,-.224),l(.455,-.164),l(.655,.192),l(1.051,.487),l(.226,.251),l(.022,.178),l(-.299,.43),l(-.05,.355),l(-.406,.444),l(1.001,.929),l(-.365,.37),l(-.795,.282),l(-1.078,.621),l(-.662,.281),l(-1.097,.046),l(-.823,.119),l(-1.548,.077),l(-.433,.413),l(-.916,.795),l(-.686,.427),l(-.612,.294),l(-.938,.222),l(-1.494,.172),l(-1.845,.127),l(-1.452,-.07),l(-2.031,-.084),l(-.355,.03),l(-1.073,.075),l(-1.058,-.012),l(-1.873,-.099),l(-.917,-.027),l(-1.758,.106),l(-.547,.206),l(-.523,.294),l(-.537,.585),l(-.205,.554),l(-.287,.335),l(-.591,.19),l(-1.07,.104),l(-.537,.147),l(-1.098,.555),l(-.774,.54),l(-.794,.612),l(-.325,.363),l(-.33,.233),l(-.868,.843),l(-.485,.566),l(-.418,.276),l(-.46,.58),l(-.518,.968),l(.749,-.737),l(.375,-.131),l(.688,-.479),l(1.059,-.944),l(1.097,-.785),l(2.028,-.948),l(1.245,-.395),l(1.797,-.512),l(1.065,-.235),l(1.03,-.235),l(1.473,-.148),l(.922,.056),l(.895,.289),l(.509,.29),l(.136,.189),l(.144,.464),l(-.125,.218),l(-.326,.219),l(-1.059,.292),l(-.753,.452),l(-.581,.044),l(-.845,-.23),l(-.726,.045),l(-.645,.19),l(.279,.08),l(1.13,.229),l(.17,.122),l(.256,.444),l(.074,.095),l(1.299,-.485),l(-.028,.216),l(.479,-.148),l(.372,.162),l(-.36,.229),l(-.231,.256),l(.112,.27),l(-.163,.014),l(-.074,.229),l(-.91,.444),l(1.216,.013),l(.077,.188),l(-.187,.282),l(.091,.43),l(.118,-.081),l(.239,.134),l(-.063,.134),l(.048,.202),l(.351,.457),l(.009,.147),l(.824,.054),l(.154,.094),l(.04,-.067),l(.727,.147),l(-.315,.134),l(-.373,-.013),l(-.047,.134),l(.525,.147),l(.211,.241),l(.569,-.081),l(.135,.134),l(.212,-.014),l(.134,.174),l(.418,-.04),l(-.075,-.107),l(.843,.067),l(.241,.107),l(-.207,.201),l(.242,-.107),l(.26,.027),l(.245,-.013),l(.696,-.362),l(.303,-.081),l(.104,.362),l(.377,.161),l(.538,-.121),l(.488,.416),l(-.405,.254),l(.089,.107),l(.825,.027),l(.164,.174),l(-.521,.121),l(-.161,-.121),l(-.3,.134),l(.118,.094),l(-.515,0),l(-.23,-.04),l(.109,.161),l(-.45,.04),l(.056,.107),l(-.443,.014),l(-.128,.147),l(-.45,.04),l(-.368,.253),l(-.09,-.106),l(-.706,.28),l(-.046,.053),l(-.529,.133),l(-.119,-.267),l(-.274,.106),l(-.163,.267),l(-.188,-.187),l(-.068,.253),l(-.218,.08),l(-.094,.187),l(-.513,0),l(-.081,-.08),l(-.169,-.053),l(.032,-.347),l(-.242,.36),l(-.202,.12),l(-.131,-.253),l(-.354,.027),l(.043,.24),l(-.233,.04),l(.312,.08),l(.033,.213),l(-.103,.12),l(-.174,-.067),l(-.768,.453),l(.127,.16),l(-.235,.12),l(-.194,.053),l(.015,.213),l(-.161,-.12),l(.083,.173),l(-.217,.08),l(-.14,-.107),l(.096,.253),l(-.222,.066),l(-.146,-.08),l(-.148,0),l(-.064,.133),l(-.156,-.106),l(-.243,.227),l(-.086,.292),l(-.201,-.226),l(-.344,.133),l(-.154,-.187),l(-.349,-.479),l(-.138,.24),l(-.419,-.866),l(.456,-.773),l(.284,-.16),l(.035,-.12),l(-.35,.12),l(-.357,.267),l(-.076,-.106),l(.924,-.507),l(.125,.146),l(.195,-.093),l(-.258,-.107),l(1.103,-.52),l(1.109,-.562),l(.658,-.361),l(.336,.094),l(-.067,.428),l(.179,-.054),l(.258,.281),l(-.044,-.201),l(-.017,-.174),l(.632,-.214),l(.73,-.134),l(.192,.067),l(.202,-.081),l(-.152,-.094),l(-.653,-.053),l(-.595,.053),l(-.42,-.053),l(-.804,-.014),l(-.306,.027),l(-.251,.081),l(-.246,.094),l(.033,-.214),l(1.128,-.563),l(.054,-.201),l(.252,-.08),l(-.052,-.174),l(-.523,.281),l(.097,-.134),l(-.502,-.51),l(.309,.443),l(-.36,.482),l(-.328,.013),l(-1.974,.817),l(-.284,.08),l(-.362,-.201),l(-.227,-.067),l(.23,.201),l(-.788,.401),l(-.219,-.174),l(-1.019,-.054),l(-.124,.147),l(-.38,-.241),M(186.193,47.833),l(-.713,-.032),l(-.922,-.181),l(-.882,-.065),l(-1.25,-.314),l(-.973,-.182),l(-.604,-.049),l(-1.083,-.199),l(.43,-.335),l(1.542,-.405),l(.385,-.186),l(.115,-.673),l(.305,-.236),l(.831,-.069),l(.743,.184),l(1.436,.603),l(1.287,.4),l(.074,.285),l(.315,.284),l(1.094,.516),l(-.288,.117),l(-1.263,.486),l(-.578,.051),M(231.09,50.646),l(-1.319,-.03),l(-.449,-.147),l(-.232,-.247),l(-.173,-.478),l(.3,-.43),l(.708,-.664),l(.662,-.267),l(1.359,-.168),l(.911,.197),l(.79,.314),l(-.021,.464),l(-.039,.727),l(-.362,.265),l(-1.025,.348),l(-1.108,.117),
+N(444.972,79.145),l(.47,-.218),l(.307,-.093),l(-.294,-.024),l(-.419,.061),l(-.15,-.135),l(-.125,.184),l(-.108,-.012),l(.066,-.491),l(.177,-.218),l(.41,.009),l(1.489,.062),l(.417,.014),l(.339,-.075),l(.121,-.253),l(-.175,-.288),l(.246,-.05),l(-.068,-.122),l(-.068,-.123),l(.353,-.096),l(.12,-.034),l(.051,.154),l(.086,.052),l(.24,0),l(.223,.12),l(.257,.069),l(.514,.068),l(.086,.103),l(.223,-.051),l(.445,0),l(.257,0),l(.223,-.017),l(.086,.137),l(.103,.103),l(.188,.034),l(.171,.069),l(.018,.137),l(.052,.12),l(-.224,.12),l(-.068,.154),l(-.068,.206),l(.018,.171),l(.034,.137),l(.029,.038),l(-2.96,.101),l(-2.246,-.115),l(-.842,-.006),M(717.633,81.109),l(.42,.443),l(.429,.62),l(.183,.457),l(.01,.767),l(-.244,.442),l(-.197,.78),l(-.002,.764),l(.29,.777),l(.592,.849),l(.65,1.446),l(.899,1.614),l(1.115,1.679),l(-1.26,-.677),l(-.832,-.39),l(-.99,-.056),l(-.268,.088),l(-.396,.204),l(-.462,1.045),l(-.266,1.101),l(-.082,.579),l(.277,.982),l(.183,.216),l(.659,.908),l(.54,.201),l(.463,.648),l(-.314,1.246),l(-.664,-1.258),l(-.866,-.301),l(-.224,.029),l(-.415,.303),l(-.311,.534),l(-.643,.907),l(-.422,-.5),l(-.19,-.929),l(.637,-1.146),l(-.395,-.884),l(.175,-.454),l(.502,-.63),l(-.131,-.723),l(-.196,-.376),l(-.27,-.55),l(-.062,-.235),l(.403,-.302),l(.284,-.915),l(.075,-.784),l(.005,-1.326),l(.15,-1.302),l(-.09,-.732),l(-.213,-.469),l(-.83,-.85),l(-.1,-.897),l(.114,-.192),l(.359,-.722),l(.065,-.738),l(-.336,-.457),l(.172,-.237),l(.374,-.03),l(.62,-.031),l(1.023,-.534),M(471.158,84.281),l(-.002,-.142),l(-.165,-.066),l(-.082,-.115),l(-.164,-.082),l(.033,-.099),l(-.033,-.23),l(-.033,-.164),l(.082,-.099),l(-.147,-.131),l(-.099,-.148),l(.132,-.066),l(0,-.165),l(-.296,-.164),l(-.279,-.263),l(-.017,-.164),l(.099,-.131),l(.131,-.165),l(.362,-.017),l(.328,.049),l(.197,.132),l(.51,.016),l(.525,-.099),l(.444,-.247),l(.049,-.065),l(.148,-.083),l(.296,0),l(.065,-.164),l(-.033,-.131),l(-.279,-.066),l(-.296,-.148),l(-.099,-.181),l(.082,-.017),l(.066,-.049),l(.032,-.065),l(-.263,-.066),l(-.361,-.099),l(-.378,-.066),l(-.361,.066),l(-.182,-.066),l(.066,-.181),l(.099,-.197),l(-.066,-.148),l(-.164,-.099),l(-.279,-.082),l(-.23,-.066),l(-.443,-.213),l(-.115,-.23),l(-.164,-.263),l(-.214,-.017),l(-.017,-.099),l(.066,-.131),l(.099,-.115),l(-.132,-.033),l(-.181,.049),l(-.082,-.115),l(-.132,-.181),l(-.345,-.049),l(.049,-.147),l(.033,-.165),l(.099,-.049),l(.115,-.082),l(0,-.083),l(.114,0),l(.066,-.131),l(-.05,-.164),l(-.147,-.099),l(-.197,-.247),l(.131,-.165),l(.033,-.164),l(0,-.083),l(.065,-.115),l(-.049,-.115),l(-.147,.033),l(-.165,-.033),l(-.147,-.099),l(-.099,-.099),l(-.279,-.099),l(-.132,-.131),l(-.542,-.115),l(-.247,.049),l(-.099,-.049),l(-.131,-.049),l(-.23,.083),l(-.147,.099),l(-.165,0),l(-.279,.016),l(-.214,.197),l(-.197,0),l(-.164,-.148),l(-.065,-.148),l(.017,-.099),l(.164,-.099),l(0,-.115),l(-.147,-.017),l(-.296,-.065),l(-.312,-.049),l(-.361,.049),l(-.214,.065),l(-.197,.033),l(-.082,-.148),l(-.132,-.148),l(-.312,-.033),l(-.181,-.016),l(-.197,.131),l(-.229,-.066),l(-.165,-.147),l(.061,-.042),l(.015,-.117),l(.044,-.087),l(-.088,-.233),l(.015,-.189),l(-.131,-.117),l(.059,-.087),l(-.16,-.043),l(-.146,-.102),l(-.029,-.16),l(-.131,-.058),l(-.116,-.102),l(.043,-.073),l(.059,-.087),l(-.073,-.044),l(-.087,-.014),l(-.131,-.073),l(-.146,.015),l(-.204,.059),l(.044,-.102),l(.102,-.073),l(.073,-.087),l(-.029,-.117),l(.072,-.131),l(-.131,-.087),l(.103,-.029),l(.087,-.015),l(.102,-.073),l(.015,-.087),l(.029,-.116),l(.015,-.087),l(-.204,-.058),l(-.087,-.073),l(-.204,-.087),l(-.232,-.073),l(0,-.117),l(.015,-.116),l(-.37,.004),l(-.081,-.106),l(.116,-.058),L(461.402,72),l(.029,-.117),l(.131,0),l(.087,-.116),l(.059,-.102),l(.16,-.058),l(.262,-.043),l(.175,-.073),l(-.059,-.059),l(-.175,-.043),l(-.043,-.146),l(-.015,-.087),l(0,-.073),l(-.088,-.073),l(-.203,-.087),l(-.175,-.233),l(0,-.175),l(.175,-.131),l(-.029,-.16),l(-.073,-.189),l(-.131,-.437),l(-.029,-.16),l(.088,-.16),l(.204,-.131),l(.319,-.131),l(.219,-.204),l(.175,-.277),l(.058,-.131),l(.088,-.043),l(.116,0),l(.189,0),l(.175,-.044),l(.043,-.174),l(-.16,-.131),l(-.145,-.053),l(-.089,-.13),l(-.17,-.038),l(.1,-.253),l(.339,-.038),l(.153,.165),l(.229,.063),l(.188,-.088),l(-.094,-.139),l(.301,-.154),l(.485,.199),l(.296,-.062),l(.312,-.338),l(.311,-.185),l(.75,.106),l(.781,.275),l(.439,0),l(.363,-.154),l(-.386,-.399),l(-.59,-.323),l(-.393,-.03),l(-1.204,.08),l(-.616,-.091),l(-.271,-.108),l(-.299,-.309),l(.258,-.434),l(-.065,-.201),l(-.199,.044),l(.174,-.285),l(1.946,-1.145),l(1.983,-1.195),l(1.385,-.758),l(.591,-.536),l(.43,-.536),l(.105,-.409),l(-.161,-.346),l(-.436,-.392),l(-.703,-.265),l(-1.357,-.499),l(-.439,-.33),l(.327,-.191),l(.542,-.415),l(.057,-.254),l(-.151,-.253),l(-1.286,-1.395),l(-.37,-.509),l(.029,-.37),l(.187,-.403),l(.44,-.535),l(.196,-.356),l(-.772,-1.195),l(-1.402,-1.394),l(.328,-.296),l(1.303,-.777),l(.421,-.364),l(-.543,-.392),l(-.964,-.506),l(-.872,-.194),l(-.563,-.212),l(-.116,-.529),l(.258,-.465),l(.024,-.283),l(.689,-.303),l(1.013,-.672),l(1.023,-.49),l(.77,-.121),l(.824,-.021),l(.514,-.204),l(.404,-.288),l(.617,-.051),l(1.002,-.254),l(.643,-.237),l(.01,.151),l(.255,.386),l(.358,.284),l(.543,.2),l(.919,.082),l(.602,.1),l(.078,.602),l(.695,-.319),l(.421,.049),l(1.083,.048),l(.875,.015),l(.522,.032),l(1.116,-.002),l(1.293,.281),l(2.728,.512),l(.984,.364),l(1.595,.86),l(.583,.214),l(1.48,.246),l(1.296,.212),l(2.018,.623),l(.328,.279),l(-.051,.444),l(.147,.295),l(.426,.294),l(.104,.294),l(-.24,.344),l(-.69,.491),l(-1.092,.54),l(-.816,.262),l(-1.75,.36),l(-.907,.083),l(-1.631,-.013),l(-1.391,-.192),l(-2.038,-.175),l(-1.63,-.192),l(-1.342,-.339),l(-2.256,-.485),l(-1.114,-.112),l(-.476,-.048),l(-.621,-.473),l(-.371,-.163),l(-.771,-.13),l(-.943,.117),l(.307,.163),l(.149,.065),l(.73,.538),l(.482,.146),l(1.109,.601),l(.832,.291),l(.921,.161),l(.634,.242),l(.405,.453),l(-.002,.405),l(-.276,.291),l(-.684,.195),l(.086,.113),l(.208,.531),l(.771,.943),l(.093,.494),l(.155,.207),l(.438,.174),l(1.203,.078),l(.872,.125),l(.499,.619),l(.401,.095),l(1.26,.077),l(.575,.126),l(.364,.079),l(.402,-.128),l(.785,-.097),l(.243,-.302),l(-.001,-.318),l(-.387,-.397),l(-.471,-.079),l(-.455,.096),l(-.447,-.031),l(-.589,-.206),l(-.952,-.795),l(.701,-.674),l(.484,-.001),l(1.116,.479),l(1.441,.333),l(2.09,.427),l(.952,.078),l(.834,-.146),l(.723,.174),l(.261,-.224),l(.05,-.415),l(-.214,-.239),l(-.858,-.656),l(-.348,-.628),l(.285,-.323),l(.19,-.049),l(1.432,-.423),l(1.495,-.359),l(.599,-.244),l(1.133,-.717),l(.172,-.049),l(.462,.064),l(1.829,.29),l(1.41,.533),l(.341,-.001),l(.052,-.065),l(.154,-.503),l(.581,-.767),l(-.048,-.653),l(-.317,-.408),l(-.847,-.163),l(-.3,-.229),l(1.139,-1.005),l(.101,-.247),l(-.205,-.594),l(-.771,-.512),l(.069,-.315),l(.353,-.051),l(1.458,.23),l(2.025,-.12),l(.631,.132),l(.664,.611),l(.616,.445),l(.433,.461),l(-1.045,.051),l(-1.559,.085),l(-.822,.215),l(-.492,.51),l(.191,.18),l(.952,.293),l(.732,.555),l(.804,.194),l(.723,.097),l(1.268,-.133),l(1.33,-.084),l(.301,-.164),l(.257,-.491),l(.291,-.591),l(.284,-.412),l(1.232,-.2),l(1.223,-.414),l(.988,-.216),l(1.924,-.483),l(1.429,-.251),l(1.537,-.318),l(.921,-.3),l(.205,.464),l(.278,.083),l(.571,-.117),l(.487,-.266),l(.148,-.465),l(.386,-.167),l(.718,-.135),l(.859,.065),l(-.18,.399),l(-.058,.597),l(-.858,.084),l(-.178,.149),l(.002,.215),l(.687,.197),l(.507,-.083),l(1.169,-.167),l(.436,-.001),l(.161,.198),l(.23,.049),l(.278,-.133),l(.264,-.216),l(.29,-.431),l(.464,-.183),l(.861,-.118),l(1.049,-.068),l(.768,.032),l(1.075,.23),l(.755,-.018),l(.36,-.083),l(.963,-.467),l(1,-.285),l(.803,-.052),l(.952,.182),l(.326,.166),l(-.631,.45),l(.129,.232),l(.217,.099),l(.632,.131),l(.579,-.018),l(.288,-.232),l(.074,-.398),l(.342,-.084),l(.962,.065),l(.543,-.184),l(.395,-.316),l(.115,-.417),l(-1.37,-1.033),l(.405,-.168),l(.66,-.37),l(.403,-.068),l(.609,.016),l(2.171,.063),l(1.272,.199),l(1.241,.149),l(1.135,.199),l(2.111,.515),l(1.071,.098),l(1.712,.414),l(1.02,.248),l(1.305,.53),l(1.455,.611),l(.864,.379),l(.376,.049),l(.229,-.1),l(1.145,-1.047),l(.236,-.3),l(-.927,.035),l(-.4,-.049),l(-.564,-.232),l(-.365,-.433),l(.027,-.652),l(-.727,-.283),l(-1.987,-.147),l(-.19,-.268),l(.064,-.168),l(.305,-.303),l(.693,-.255),l(.236,-.153),l(.085,-.187),l(-.052,-.833),l(-.251,-.238),l(-1.135,-.066),l(-.232,-.29),l(.328,-.532),l(.359,-.241),l(.391,-.035),l(1.482,-.416),l(1.098,-.485),l(.521,-.416),l(.581,-.608),l(.544,-1.22),l(.637,-.421),l(.374,.069),l(1.562,.155),l(1.613,-.125),l(1.605,-.091),l(.695,.069),l(1.066,-.055),l(.574,.122),l(.309,.279),l(-.018,.332),l(-.423,.836),l(-.348,.348),l(-1.334,.833),l(-.223,.345),l(.752,.342),l(.931,.667),l(.277,.342),l(.21,.818),l(-.174,.222),l(-.575,.375),l(.254,1.179),l(-.058,1.305),l(.263,.583),l(.45,.381),l(1.027,.264),l(.19,.166),l(-.001,.133),l(-.485,.481),l(-.417,.826),l(-.333,.33),l(-.784,.462),l(-1.232,.625),l(-.63,.198),l(-.55,.263),l(.321,.522),l(-.433,.115),L(558,52.195),l(-1.599,-.372),l(-.731,-.08),l(-.97,.034),l(-.601,.115),l(.195,.31),l(.583,.276),l(.738,.21),l(1.569,.208),l(1.133,.079),l(.613,-.05),l(1.188,.144),l(.922,-.034),l(.472,-.358),l(.303,-.358),l(1.352,-.328),l(1.166,-.492),l(.268,-.278),l(.386,-.606),l(.818,-.313),l(.864,-.626),l(.064,-.362),l(-.225,-.561),l(-.609,-.545),l(.244,-.548),l(.237,-.1),l(.677,-.151),l(1.38,-.152),l(1.757,-.003),l(.74,.231),l(.842,.463),l(.151,.778),l(-.34,1.023),l(.302,.279),l(.92,.212),l(1.298,.047),l(.864,-.149),l(.129,-.296),l(-.514,-.18),l(-.797,-.18),l(-.571,.034),l(-.457,-.098),l(.068,-.379),l(1.03,-.382),l(.065,-.249),l(-.218,-.148),l(-.166,-.331),l(-.441,-.763),l(-.511,-.266),l(-.836,-.098),l(-1.093,-.231),l(-.801,-.116),l(-1.288,-.165),l(-.91,.186),l(-.638,.101),l(-1.297,-.181),l(-.896,.019),l(-.015,-.267),l(-.564,-1.071),l(.305,-.657),l(.736,-.697),l(.282,-.46),l(-.134,-.221),l(-1.092,-1.042),l(-.949,-.514),l(-.12,-.189),l(.833,-.554),l(1.213,-.106),l(.998,-.262),l(.744,-.348),l(.172,-.226),l(.169,-.644),l(-.13,-.663),l(.23,.069),l(.64,.051),l(.466,.086),l(.108,.471),l(-.186,.54),l(-.636,.608),l(-.167,.554),l(.14,.448),l(.373,.274),l(.485,.274),l(1.384,.134),l(1.574,.169),l(1.632,.083),l(.819,.409),l(.321,.017),l(.799,-.036),l(-.527,-.89),l(-.521,-.274),l(-1.893,-.1),l(-.931,-.067),l(-.576,-.154),l(-.609,-.448),l(.275,-.329),l(1.374,-.054),l(.444,.172),l(1.145,.084),l(.747,-.157),l(-.09,-.728),l(.408,-.088),l(.84,-.105),l(1.278,-.02),l(1.067,.207),l(1.413,.379),l(1.088,.535),l(1.326,.343),l(.547,.085),l(1.822,.014),l(.727,-.174),l(.138,.345),l(-.781,.38),l(-.696,.259),l(-.248,.771),l(-.129,.972),l(.333,.136),l(.68,-.785),l(.387,-.292),l(.285,.051),l(.604,.528),l(-.088,.749),l(.743,-.205),l(.681,-.273),l(-.044,-.306),l(-.191,-.119),l(-.147,-.358),l(-.748,-.821),l(.188,-.223),l(.686,-.759),l(-.797,-.448),l(-.772,-.258),l(-.93,-.241),l(-.257,-.382),l(-.655,-.051),l(-.979,-.242),l(-1.34,-.207),l(-.307,-.296),l(.062,-.577),l(-.096,-.386),l(-.811,-.667),l(.081,-.247),l(.266,-.16),l(.484,-.125),l(2.31,-.11),l(2.54,-.022),l(2.125,-.128),l(1.421,-.162),l(.475,.317),l(.382,.052),l(.844,-.267),l(1.056,-.286),l(1.413,-.109),l(.589,.194),l(-.957,.338),l(-.451,.407),l(1.737,-.233),l(.521,-.107),l(.955,-.374),l(.27,-.284),l(-.334,-.444),l(-.326,-.177),l(-.925,-.266),l(-.365,-.303),l(-.002,-.232),l(.324,-.539),l(1.176,-.397),l(.966,-.22),l(3.028,-.903),l(.889,-.094),l(.248,-.036),l(.522,-.076),l(1.899,-.096),l(1.663,-.114),l(2.302,-.244),l(2.048,-.263),l(1.595,-.43),l(.855,-.243),l(1.763,.034),l(1.065,-.002),l(.383,.185),l(-.351,.409),l(1.504,.108),l(1.018,-.039),l(1.261,-.188),l(1.345,-.225),l(.95,-.039),l(.982,.166),l(.687,.073),l(.693,-.206),l(.12,-.335),l(-.133,-.167),l(.466,-.337),l(.942,-.077),l(.939,.036),l(1.243,-.377),l(-.618,-.506),l(.122,-.34),l(1.165,-.438),l(1.554,-.383),l(2.23,-.406),l(1.229,-.077),l(.993,.056),l(1.486,-.003),l(.86,.265),l(.045,.266),l(-1.528,.401),l(-.66,.342),l(.488,.15),l(1.83,-.117),l(1.588,.148),l(2.039,-.079),l(.177,.113),l(-.375,.283),l(-1.187,.453),l(-.296,.3),l(1.971,-.004),l(.833,-.02),l(.234,.093),l(1.052,-.545),l(1.366,-.002),l(1.771,-.097),l(.631,.13),l(1.35,-.021),l(1.954,.165),l(1.4,.259),l(1.181,.427),l(.52,.445),l(.726,-.001),l(.854,-.076),l(.422,.5),l(-1.354,.832),l(.241,.128),l(.896,.365),l(-2.329,.859),l(-1.035,.235),l(-1.166,.11),l(-3.404,1.061),l(-3.018,.965),l(-.793,.285),l(-2.388,.375),l(-2.35,.586),l(-2.065,1.126),l(.715,.017),l(1.081,-.247),l(.922,-.458),l(1.663,-.161),l(1.231,-.02),l(2.101,-.268),l(1.879,-.286),l(.879,-.107),l(1.004,-.284),l(-.094,-.389),l(-.492,-.123),l(-.034,-.071),l(.281,-.249),l(.581,-.214),l(.873,.211),l(.603,.389),l(.621,.052),l(.593,.193),l(.737,.052),l(.853,-.055),l(1.155,-.268),l(.499,.07),l(.192,.3),l(.009,.512),l(.522,.404),l(1.422,-.778),l(1.66,-.021),l(1.506,-.145),l(2.354,.014),l(1.919,.155),l(.854,.034),l(1.204,.033),l(-.271,.74),l(.354,.333),l(2.043,.154),l(.848,.121),l(.698,.069),l(1.035,.103),l(2.49,-.145),l(1.209,-.02),l(1.42,.348),l(1.405,-.932),l(-.161,-.352),l(-.038,-.229),l(-.034,-.105),l(1.806,-.48),l(.521,-.019),l(.802,.104),l(1.148,.298),l(.851,.281),l(1.711,.032),l(1.354,-.073),l(1.384,.033),l(1.323,.431),l(.409,.181),l(.058,.386),l(-.52,.088),l(-.268,.036),l(-1.905,.406),l(-2.438,.737),l(-.233,.227),l(.253,.069),l(1.033,.067),l(.957,.016),l(.659,.155),l(.659,.293),l(1.014,.396),l(.583,.172),l(.78,.481),l(1.27,.805),l(1.814,.801),l(1.351,.305),l(.612,-.018),l(.795,-.394),l(.843,-.72),l(1.093,-1.051),l(.601,-.329),l(.29,.017),l(.236,.465),l(.772,.308),l(1.346,.29),l(1.105,.135),l(.848,-.087),l(1.973,-.468),l(.778,-.07),l(.813,.067),l(1.196,.239),l(1.921,.734),l(.315,-.052),l(.186,-.069),l(.491,-.258),l(.221,-.258),l(-.137,-.051),l(.013,-.189),l(.726,-.312),l(.509,-.018),l(.5,.257),l(.575,.188),l(1.302,.032),l(.181,-.5),l(-.194,-.466),l(.15,-.363),l(.093,-.521),l(-1.131,.159),l(-.643,.001),l(-.179,-.104),l(.442,-.296),l(.318,-.087),l(.986,-.089),l(1.021,-.02),l(.832,-.141),l(1.566,-.648),l(.254,0),l(1.76,.241),l(1.561,.137),l(2.036,.188),l(.997,.068),l(.654,.103),l(2.307,.065),l(-1.732,.628),l(.865,.051),l(1.011,-.089),l(.335,.138),l(-.305,.381),l(-.926,.192),l(-.846,.261),l(-.177,.293),l(.664,.033),l(.52,-.122),l(.916,-.14),l(.978,-.33),l(1.62,-.799),l(2.766,.012),l(1.196,.067),l(.903,.172),l(.946,.137),l(.205,.19),l(.221,.104),l(-2.247,.59),l(.559,.137),l(1.674,.289),l(2.174,.202),l(.946,.204),l(.801,.375),l(.367,.427),l(.564,.357),l(.522,.152),l(1.459,-.037),l(1.364,-.105),l(1.138,-.139),l(2.518,-.329),l(2.208,-.107),l(3.008,.131),l(1.515,.134),l(.734,.118),l(1.168,.424),l(.655,.169),l(.525,.338),l(.361,.39),l(-.123,.491),l(-.286,.521),l(.509,.25),l(.764,.065),l(.835,.015),l(.643,.083),l(-.017,.685),l(.419,.416),l(.686,-.168),l(.545,-.435),l(1.211,-.387),l(.348,-.067),l(.35,.049),l(1.696,-.02),l(1.521,-.288),l(.736,.032),l(.588,.434),l(.707,.116),l(1.541,.014),l(2.176,.062),l(.869,-.118),l(1.378,-.504),l(.406,.2),l(.93,.533),l(.396,.216),l(1.095,.265),l(.875,.332),l(.282,.398),l(.612,.148),l(1.556,-.136),l(1.653,-.319),l(.16,-.25),l(-.248,-.333),l(-.805,-.833),l(-.833,-.115),l(.4,-.336),l(.479,-.571),l(1.89,.098),l(1.214,.082),l(1.135,.065),l(1.221,.166),l(.222,.318),l(1.396,-.153),l(2.084,-.054),l(2.304,.013),l(1.292,.148),l(.786,.199),l(1.185,.199),l(1.391,.098),l(.751,.182),l(1.302,.332),l(.747,.065),l(.703,.182),l(1.145,.505),l(0,2.126),l(0,2.222),l(0,2.222),l(0,1.292),l(0,.157),l(0,.576),l(0,.219),l(-1.083,.371),l(-.651,.03),l(-.645,.37),l(-.56,.065),l(-1.044,.065),l(-.355,-.079),l(-.928,-.052),l(-.118,-.343),l(-.271,-.211),l(-.501,.027),l(.241,-.185),l(-.938,.324),l(-.846,.02),l(-.337,-.211),l(-.478,-.079),l(.424,.501),l(-.569,.29),l(.32,.103),l(.942,.205),l(.634,-.36),l(.395,.041),l(.335,.079),l(0,.477),l(.248,.206),l(.76,.269),l(1.059,-.228),l(-.439,.322),l(.741,-.243),l(.065,.336),l(.247,.206),l(.187,.363),l(.068,.189),l(-.722,.522),l(.593,-.064),l(.349,.172),l(.473,.503),l(.501,.157),l(.145,.251),l(-.162,.456),l(.792,-.111),l(-.125,.393),l(.023,.25),l(-.43,.299),l(-.691,.205),l(-.635,-.046),l(-.448,-.14),l(-1.243,-.154),l(-.889,-.077),l(-.347,-.14),l(.123,-.267),l(-.493,-.046),l(-.304,.032),l(-.559,.55),l(-.069,.11),l(-3.06,.913),l(-1.155,.174),l(-.245,0),l(-.43,.203),l(-.219,.188),l(-.719,.22),l(-.991,.033),l(-.308,.11),l(-.48,.405),l(-.462,.203),l(-.946,.033),l(-.854,.622),l(-.24,.279),l(-1.67,.452),l(-.392,.449),l(-1.229,.25),l(-.406,.14),l(-.151,.293),l(.01,.292),l(-.473,.292),l(-.406,.016),l(-.586,-.306),l(-.183,-.262),l(-.169,-.37),l(-.67,-.308),l(-1.074,-.044),l(-1.021,.048),l(-1.159,.172),l(-1.301,.188),l(-.523,.217),l(-1.333,.756),l(-.536,.277),l(-.184,-.138),l(.575,-1.293),l(-.55,.094),l(-.392,-.097),l(-.811,.531),l(-.67,.186),l(-.401,.155),l(-.114,.506),l(-.66,.154),l(-.317,-.168),l(-.253,-.49),l(-.483,-.261),l(-1.024,.636),l(.261,-.204),l(-.557,.062),l(-.283,.092),l(-.628,.522),l(-.141,.261),l(.126,.229),l(.344,.152),l(-.932,.521),l(-.182,.199),l(.342,.167),l(-.647,.352),l(-.88,.55),l(-.626,.503),l(-.298,.35),l(-.01,.531),l(.421,.317),l(.477,.09),l(1.382,-.048),l(.562,.166),l(.11,.167),l(-.436,.394),l(-.752,.455),l(-.181,.302),l(.224,.512),l(.292,.452),l(.786,.089),l(.165,.391),l(-.014,.616),l(-.349,.361),l(-.528,.061),l(-.481,-.209),l(-.017,-.21),l(.388,-.527),l(-.444,-.014),l(-.454,.242),l(-1.014,.843),l(-.56,.675),l(-.237,.599),l(.321,.672),l(.647,.311),l(.154,.237),l(-.294,.387),l(-.688,.313),l(-.956,.031),l(-.664,-.088),l(-.344,.001),l(-.619,.179),l(-.837,.476),l(-.214,.149),l(-.525,.504),l(-.137,.341),l(.111,.281),l(.492,.398),l(.038,.221),l(-.056,.133),l(-.679,.238),l(-.604,.016),L(753.44,82.4),l(-.727,.296),l(-.065,.251),l(.067,.28),l(-.161,.854),l(-.293,.412),l(-.8,.78),l(-1.53,.971),l(-.854,.5),l(-.285,.103),l(-.295,-.614),l(-.198,-.821),l(-.25,-.69),l(-.064,-.794),l(-.351,-.75),l(-.305,-.383),l(-.214,-.915),l(-.514,-1.36),l(-.008,-.578),l(-.154,-.563),l(-.017,-.327),l(-.105,-.193),l(-.262,-.817),l(-.026,-.792),l(.116,-.808),l(.271,-1.396),l(.167,-.226),l(1.185,-.86),l(.716,-.424),l(.57,-.695),l(.14,-.227),l(-.085,-.318),l(-.139,-.166),l(1.632,-.367),l(1.001,-.305),l(.811,-.32),l(1.729,-.884),l(.641,-.412),l(.431,-.428),l(.14,-.335),l(1.784,-.889),l(.872,-.445),l(1.535,-.861),l(.368,-.293),l(.26,-.433),l(1.252,-.435),l(2.106,-.514),l(.257,-.434),l(.773,-.528),l(.086,-.233),l(-.568,-.216),l(.814,-.904),l(-.036,-.483),l(.183,-.391),l(.598,-.204),l(1.729,-.082),l(.513,-.063),l(-.485,-.328),l(-2.065,-.215),l(-.71,.095),l(-1.062,.174),l(-.777,.189),l(.042,.328),l(-.664,.923),l(.125,.202),l(-.04,.14),l(-.662,.11),l(-.479,.11),l(-1.555,.718),l(-1.979,1.042),l(-1.169,.342),l(-.249,-.062),l(.156,-.325),l(.352,-.465),l(-.933,-.076),l(-.16,-.263),l(.252,-.451),l(.442,-.467),l(.207,-.328),l(-.069,-.202),l(-.339,-.031),l(-1.136,.454),l(-.496,.032),l(-.277,-.358),l(-.589,-.17),l(-1.606,.144),l(-1.312,.111),l(-.956,.08),l(-.606,.157),l(-.894,.359),l(-.093,.436),l(.082,.186),l(-1.262,.53),l(-.408,.233),l(.149,.495),l(-1.294,.357),l(-1.019,.434),l(-.84,.479),l(-.496,.461),l(-.332,.46),l(.004,.383),l(.527,.213),l(1.269,.043),l(.278,.275),l(.062,.122),l(-1.152,.139),l(-1.028,.262),l(-1.045,-.059),l(-.698,-.136),l(-.382,.031),l(-.311,.107),l(-.721,.398),l(-.695,-.35),l(-1.112,.383),l(-.747,.139),l(-.487,-.09),l(-.284,-.137),l(.108,-.29),l(.748,-.124),l(.868,-.124),l(.164,-.046),l(-.741,-.64),L(736.444,68),l(-1.06,.017),l(-.854,.155),l(-.353,-.061),l(-.868,-.458),l(-.775,-.029),l(-.745,.047),l(-.704,.246),l(.042,.398),l(-.26,.229),l(-.477,.215),l(-.695,-.243),l(-.408,-.122),l(-1.26,.063),l(-.784,.093),l(-.651,-.075),l(-.887,-.136),l(-.563,.078),l(-.067,.122),l(-.147,.474),l(-.677,.017),l(-.311,-.137),l(-.038,-.382),l(-.203,-.259),l(-1.241,.094),l(-1.014,-.059),l(-1.257,.033),l(-1.158,.063),l(-.836,-.029),l(-.18,.016),l(-1.085,.292),l(-.964,.444),l(-.74,.474),l(-.536,.504),l(-.789,.245),l(-.904,.336),l(-.194,.152),l(-1.047,1.17),l(-1.634,.684),l(-.949,.471),l(-1.157,.711),l(-1.653,1.253),l(-.828,.572),l(-1.573,.873),l(-.893,.376),l(-1.889,.871),l(-.632,.388),l(-.203,.298),l(.018,.357),l(.428,.281),l(.485,.043),l(.918,.013),l(1.046,-.15),l(.656,.043),l(-.329,1.261),l(.016,.415),l(.303,.103),l(.63,-.09),l(.601,-.371),l(.761,.117),l(-.127,.148),L(705.293,81),l(-.112,.222),l(.373,.073),l(1.645,-.018),l(.566,-.238),l(.39,-.519),l(.017,-.638),l(.763,.014),l(.647,-.001),l(.726,.014),l(.951,.265),l(.658,.354),l(.486,.591),l(.847,.575),l(.426,.176),l(.506,.324),l(-.07,.148),l(-.581,.355),l(.453,.221),l(.13,.309),l(-.336,.723),l(.491,.117),l(.215,.235),l(-.291,.515),l(-.348,.397),l(-.532,.559),l(-.724,1.364),l(-.181,.688),l(.057,.219),l(.24,.701),l(-.188,.917),l(-.098,.741),l(-.403,1.408),l(-.146,.493),l(-1.928,1.538),l(-.371,.435),l(-.217,.65),l(-.587,.42),l(-.741,.579),l(-.241,.361),l(-.574,.981),l(-.587,.606),l(-.941,.778),l(-1.784,1.512),l(-.464,.474),l(-.235,.458),l(-.323,.33),l(-.758,.388),l(-.618,.416),l(-.574,.702),l(-.431,.458),l(-.875,.673),l(-.955,.487),l(-1.838,.475),l(-.798,.244),l(-.278,-.427),l(-.519,-.085),l(-.243,.043),l(-.337,-.185),l(-.337,-.513),l(-.663,.272),l(-.464,.101),l(.424,-.586),l(-.624,.173),l(-.506,.486),l(-.649,.543),l(-1.326,1.001),l(-.072,.049),l(-.167,-.393),l(-.298,-.439),l(.111,-.058),l(1.252,-.462),l(.511,-.43),l(.156,-.329),l(-.112,-.299),l(.097,-.128),l(.025,-.385),l(.006,-.613),l(-.062,-.656),l(-.332,-.897),l(.048,-.557),l(1.664,-.982),l(.396,.041),l(.685,.24),l(.679,.225),l(.547,.098),l(.47,-.347),l(.551,-.69),l(.329,-.432),l(.627,-1.08),l(.538,-1.066),l(.278,-.893),l(.291,-.707),l(.66,-.393),l(.566,-.407),l(-.017,-.504),l(-.116,-.389),l(-.064,-.259),l(-.246,-.114),l(-.902,.034),l(-1.181,.208),l(-1.357,.31),l(-.953,.308),l(-.604,.22),l(-1.657,.052),l(-.649,.018),l(-.68,.033),l(-.261,-.446),l(-.44,-1.283),l(-.297,-.866),l(-.205,-.144),l(-2.659,-.839),l(-1.523,-.253),l(-1.247,-.341),l(-.507,-.2),l(-.436,-.389),l(-.927,-1.903),l(-.94,-1.588),l(-1.087,-2.384),l(-.742,-.952),l(-.763,-.467),l(-.539,-.026),l(-1.386,.125),l(-.683,-.188),l(-1.037,-.481),l(-1.148,-.215),l(-.917,.049),l(-1.203,.109),l(-.836,.123),l(-1.854,.143),l(-.602,.136),l(-.478,.165),l(-1.193,.787),l(-.375,.282),l(-.119,.25),l(.822,-.078),l(.558,.026),l(.465,.306),l(.107,.249),l(-.167,.794),l(-1.371,.608),l(-.599,.545),l(-.667,.779),l(-.416,.543),l(-.422,.426),l(-.103,.249),l(.062,.146),l(.35,.348),l(.056,.306),l(-.123,.233),l(-.779,.339),l(-2.44,.752),l(-.438,.089),l(-.343,.016),l(-1.126,-.574),l(-.452,-.172),l(-.046,-.018),l(-.431,-.168),l(-.622,.018),l(-1.228,.297),l(-.86,-.169),l(-.34,-.129),l(-.751,-.506),l(-.74,-.156),l(-.609,.047),l(-.333,.002),l(-1.165,.487),l(-.755,.498),l(-.447,.394),l(-.604,.264),l(-.526,.163),l(-1.147,.136),l(-.867,.106),l(-.532,.075),l(-1.018,.063),l(-1.602,.11),l(-.739,-.039),l(-.984,-.082),l(-.905,-.241),l(-1.318,-.254),l(-.812,-.373),l(-1.132,-.313),l(-.623,-.331),l(-1.333,-.75),l(-.769,-.229),l(-1.423,-.022),l(-1.172,-.037),l(-.796,.077),l(-2.48,.538),l(-.673,-.113),l(-1.643,-.531),l(-.421,-.333),l(-.408,-.451),l(-.182,-.481),l(-.004,-.541),l(-.145,-.204),l(-.767,-.143),l(-.989,-.317),l(-.702,-.231),l(-1.748,-.328),l(-1.036,-.185),l(-1.028,-.098),l(-.84,-.201),l(-1.269,.96),l(-.905,.839),l(-.098,.293),l(.409,.86),l(.396,.304),l(.116,.204),l(-.163,.526),l(-.744,.572),l(-.31,.162),l(-.755,.28),l(-.562,.018),l(-.576,-.186),l(-.312,-.114),l(-.875,-.068),l(-.85,.004),l(-.719,-.083),l(-1.458,.008),l(-.699,-.113),l(-.393,-.406),l(-.694,-.755),l(-.831,-.083),l(-1.842,-.122),l(-.932,-.156),l(-.953,-.097),l(-.84,.18),l(-1.512,.476),l(-1.143,.341),l(-.787,.398),l(-.971,.692),l(-.017,.012),l(-.727,.381),l(-.603,.148),l(-1.387,.08),l(-.599,.207),l(-.799,.425),l(-.154,.044),l(-.244,.074),l(-.569,.003),l(-.228,.014),l(-.064,-.145),l(-.589,0),l(-.294,-.478),l(-.441,-.331),l(.073,-.331),l(-.625,.184),l(-.478,.368),l(-.993,-.074),l(-.882,-.073),l(-.662,-.699),l(-.588,-.552),l(-.956,-.147),l(-.331,-.625),l(-.772,-.588),l(-.698,-.441),l(-.993,.037),l(-.771,.221),l(-.993,.221),l(-.992,0),l(-.589,0),l(-.184,-.184),l(-.258,-.257),l(-.478,0),l(0,-.368),l(-.367,-.331),l(-.92,0),l(-.367,.515),l(-.331,.257),l(-.515,.257),l(-1.104,-1.434),l(-.846,-.882),l(-1.69,-2.133),l(-1.066,-1.029),l(-1.287,-.772),l(-.809,-.294),l(-1.104,-.588),l(.11,-.368),l(.515,-.11),l(0,-.441),l(-.809,.074),l(-.515,.22),l(-.772,.221),l(-.956,.515),l(-1.103,.257),l(-.772,.441),l(-.294,.294),l(-.552,-.184),l(-.441,-.037),l(-.771,.073),l(-.441,.11),l(-.331,-.184),l(.331,-.441),l(.441,-.184),l(.146,-.294),l(-.367,0),l(-.441,.221),l(-.478,-.11),l(-.405,-.294),l(-.478,-.037),l(-.184,.147),l(-.147,.147),l(-.367,-.221),l(-.295,-.368),l(-.294,-.11),l(-.331,.221),l(-.367,-.074),l(-.368,.147),l(-.44,-.11),l(-.295,.147),l(-.478,-.074),l(-.184,-.257),l(.33,-.037),l(-.073,-.331),l(.147,-.257),l(.11,-.331),l(-.515,-.22),l(-.147,-.221),l(.037,-.331),l(-.368,-.404),l(-.882,.037),l(-.625,.11),l(-.772,-.257),l(-.515,-.11),l(-.919,-.147),l(-.735,.037),l(-.11,.221),l(-.589,.184),l(-.515,.037),l(0,.294),l(-.367,.368),l(-.625,.074),l(-1.876,.073),l(-2.021,.405),l(-1.177,.037),l(-.625,.331),l(-.221,.331),l(-.331,-.073),l(-.588,.073),l(-1.545,.11),l(-.735,.11),l(-1.029,.037),l(-1.396,.405),l(-.368,.184),l(-.772,0),l(-.515,0),L(537.304,80),l(-.33,.074),l(-.185,.515),l(.074,.441),l(.294,.221),l(-.294,.074),l(0,.331),l(1.065,.11),l(.956,.294),l(-.11,.257),l(-.515,.073),l(-1.103,-.147),l(-.698,.184),l(-.662,.515),l(.146,.257),l(.441,.478),l(-.331,.294),l(-.588,.147),l(-.735,.368),l(-.467,.067),l(.164,.274),l(.239,0),l(.377,.137),l(-.068,.171),l(.377,.137),l(.651,.069),l(.274,.308),l(1.165,.171),l(.24,.24),l(-.138,.686),l(-.137,.309),l(-1.577,.411),l(-.959,-.034),l(-.343,-.343),l(-.24,0),l(-.171,.309),l(-.651,.343),l(-.411,-.171),l(-.754,-.137),l(-.926,-.309),l(-.274,-.548),l(-.754,-.103),l(-1.062,.103),l(-.137,.412),l(-.617,.068),l(-.651,-.411),l(-.65,-.035),l(-.823,-.068),l(-.514,.377),l(-.377,.343),l(-.274,.274),l(-.686,.171),l(-.411,-.24),l(-.686,-.137),l(-.582,-.548),l(-.72,-.068),l(.034,.24),l(.205,.48),l(-.239,.274),l(-.274,-.137),l(-.068,-.583),l(-.411,-.274),l(-.789,-.343),l(-.582,-.206),l(0,-.343),l(-.96,-.171),l(-.617,.069),l(-.788,-.035),l(-.411,-.514),l(-.411,-.069),l(-.617,.24),l(-.273,.137),l(-.651,.137),l(-.309,-.274),l(-.479,0),l(-.651,-.069),l(-.515,.309),l(-.548,.343),l(-.788,.377),l(-.549,.068),l(-.514,.171),l(-.309,.309),l(-.172,.24),L(509.58,87.5),l(-.479,.206),l(.068,.445),l(.171,.411),l(-.068,.446),l(-.411,.24),l(-.651,0),l(-.514,-.411),l(-.48,-.548),l(-.514,-.24),l(-.411,.069),l(-.103,.308),l(-.343,.549),l(-.823,.137),l(-.205,1.303),l(.343,.171),l(.239,.274),l(-.239,.206),l(-.446,.274),l(-.65,1.165),l(1.37,.343),l(.138,.377),l(-.068,.309),l(.514,.514),l(.103,-.343),l(.583,.206),l(.343,-.034),l(.514,.034),l(.515,.445),l(.479,.206),l(.343,.514),l(.96,1.131),l(-.138,.103),l(-.445,-.103),l(-.309,-.103),l(-.343,.103),l(.068,.308),l(.857,.377),l(.616,.19),l(-.167,.2),l(-.399,.28),l(-.38,.12),l(-.12,0),l(0,.14),l(0,.22),l(-.14,.08),l(-.3,-.2),l(-.34,.2),l(-.399,.26),l(-.64,.12),l(-.319,.08),l(-.26,-.08),l(-.181,-.08),l(-.1,.04),l(-.06,.16),l(.12,.26),l(-.221,.2),l(-.18,.3),l(-.18,.319),l(-.319,.42),l(-.18,.28),l(-.32,.16),l(-.34,.24),l(-.14,.32),l(.08,.2),l(.34,.2),l(.319,.04),l(.18,.3),l(.24,.04),l(.239,.34),l(.28,.419),l(-.06,.38),l(-.101,.2),l(.061,.16),l(.1,-.2),l(.1,-.34),l(.12,-.22),l(.16,-.02),l(-.141,.439),l(-.22,.36),l(.061,.4),l(.12,.1),l(-.16,.2),l(-.04,.4),l(.38,.24),l(.119,.1),l(.12,.52),l(.28,.06),l(.359,.42),l(.2,.28),l(.439,.419),l(.18,.36),l(.359,.06),l(.047,.146),l(-.292,.449),l(-.496,.284),l(-.283,.094),l(-.308,.331),l(-.165,.213),l(-.379,.047),l(-.449,-.142),l(-.591,-.094),l(0,-.308),l(-.283,-.284),l(-.118,0),l(-.095,-.118),l(-.473,-.284),l(-.142,-.189),H(504.5,0),l(-.213,.047),L(504.264,107),l(-.229,.023),l(-.126,-.189),l(-.236,-.071),l(-.236,.023),l(-.284,-.047),l(-.26,-.094),l(-.284,-.166),l(-.354,-.284),l(.118,-.307),l(.118,-.189),l(-.118,-.142),l(-.354,-.024),l(-.378,0),l(-.213,-.166),l(-.189,-.118),l(-.212,0),l(-.143,-.094),l(-.236,-.118),l(-.118,0),l(-.095,.071),l(-.142,.166),l(-.188,-.142),l(-.261,-.071),l(-.354,0),l(-.213,.071),l(-.094,.095),l(-.166,.166),l(-.283,0),l(-.261,.118),l(-.212,-.071),l(-.261,-.118),l(.048,-.118),l(.142,-.118),l(-.308,-.119),l(-.236,-.118),l(-.354,-.071),l(-.52,-.118),l(-.284,-.213),l(-.095,-.142),l(-.354,-.166),l(-.283,.047),l(-.189,0),l(-.401,-.166),l(-.544,0),l(-.426,.095),l(-.307,0),l(-.355,-.047),l(-.307,-.071),l(-.261,-.095),l(-.212,-.166),l(-.213,0),l(-.331,0),l(-.189,-.047),l(-.188,-.094),l(-.284,-.142),l(-.283,-.095),l(-.449,-.023),l(-.402,-.023),l(-.07,-.047),l(-.284,-.047),l(-.26,.166),l(-.112,.309),l(-1.421,-1.012),l(-1.188,-.842),l(-.817,-.385),l(-.62,-.084),l(-.373,-.157),l(-.509,-.5),l(-.236,-.057),l(-.338,.159),l(-.329,-.042),l(-.347,-.515),l(-.795,-.674),l(.595,-.188),l(.519,-.001),l(.445,-.073),l(.1,-.519),l(.359,-.476),L(485,97.85),l(.526,-.03),l(.516,-.131),l(-.432,-.532),l(-.654,-.273),l(-.474,-.36),l(.243,-.116),l(.367,-.03),l(.82,-.117),l(.715,-.348),l(1.244,-.436),l(-.196,-.375),l(-.076,-.058),l(-.799,.088),l(-1.312,.175),l(.012,-.126),l(.021,-.112),l(0,-.224),l(.09,-.514),l(.224,-.089),l(.357,-.022),l(.38,-.134),l(.223,-.179),l(.022,-.179),l(.269,0),l(.736,-.045),l(.805,.157),l(.335,-.134),l(0,-.156),l(.156,-.291),l(.156,-.491),l(-.066,-.067),l(-.09,-.201),l(-.045,-.179),l(-.044,-.134),l(-.179,-.067),l(-.156,-.067),l(.044,-.224),l(.045,-.179),l(.246,0),l(.312,0),l(.134,-.156),l(-.29,-.044),l(-.269,-.089),l(-.223,-.246),l(.312,-.044),l(.357,-.201),l(.156,-.067),l(.201,-.089),l(.045,-.224),l(-.09,-.179),l(-.111,-.089),l(.022,-.179),l(.089,-.111),l(-.156,-.089),l(-.201,.089),l(-.134,.044),l(-.269,0),l(-.357,-.156),l(-.111,-.224),l(-.469,-.022),l(-.291,-.089),l(-.201,-.179),l(-.357,.067),l(-.268,-.112),l(-.469,-.112),l(-.425,-.067),l(-.29,-.268),l(-.224,.067),l(-.045,.157),l(-.29,.134),l(-.224,-.112),l(-.179,-.179),l(-.402,-.044),l(-.022,-.134),l(-.201,-.179),l(0,-.134),l(-.268,-.179),l(-.111,-.156),l(-.357,.067),l(-.536,.134),l(-.269,0),l(-.312,.112),l(-.425,.112),l(-.179,-.156),l(-.179,-.022),l(-.201,.067),l(-.469,-.291),l(-.268,-.067),l(-.47,.067),l(-.268,.179),l(-.291,-.29),l(-.179,-.089),l(-.089,-.224),l(.089,-.179),l(-.089,-.246),l(-.224,-.268),l(0,-.357),l(-.402,0),l(0,-.312),l(-.38,-.022),l(-.514,.067),l(-.156,-.112),l(-.647,0),l(-.269,-.29),l(-.111,-.357),l(-.134,-.291),l(.312,-.134),l(.312,.022),l(0,-.268),l(-.312,-.156),l(-.246,-.156),l(-.134,-.201),l(-.246,-.335),l(-.312,-.134),l(-.201,-.268),l(-.514,.134),l(-.692,-.112),l(-.67,.223),l(-.536,.022),l(-.536,-.246),l(-.134,.268),l(-.179,.291),l(-.313,.156),l(-.469,-.022),l(-.357,-.089),M(527.156,37.071),l(-.59,-.14),l(-.955,-.21),l(-1.128,-.139),l(-.739,.054),l(-.959,.037),l(-.732,.143),l(-.411,-.105),l(-1.025,-.476),l(.737,-.303),l(1.21,-.429),l(.654,-.09),l(1.549,-.002),l(.135,-.357),l(-.728,-.338),l(.351,-.269),l(.419,-.144),l(.676,-.02),l(.852,-.181),l(-.25,-.161),l(-.526,-.233),l(1.539,-.895),l(.513,-.129),l(.226,.073),l(.892,.017),l(.981,-.535),l(.863,-.333),l(1.02,-.261),l(.975,-.15),l(1.305,-.113),l(1.274,.053),l(.738,-.15),l(.786,-.615),l(.273,.018),l(.873,-.132),l(1.722,.276),l(.471,-.038),l(1.93,-.321),l(.958,-.039),l(2.184,-.247),l(1.864,-.116),l(.771,-.564),l(.376,-.152),l(2.511,-.137),l(1.965,-.079),l(.471,.131),l(.442,.225),l(.154,.357),l(-.812,.47),l(-.541,.169),l(-1.346,.319),l(-2.609,.433),l(-4.329,.49),l(-2.187,.281),l(-2.122,.299),l(-1.842,.316),l(-2.102,.242),l(-.941,.203),l(-.288,.274),l(.644,.216),l(-.422,.217),l(-.943,.361),l(-.527,.037),l(-2.047,-.068),l(-.432,.18),l(-.119,.233),l(.621,.249),l(-.331,.233),l(-1.206,.448),l(-.402,-.142),l(-.752,-.087),l(-.592,.304),l(.877,.283),l(.052,.319),l(-1.094,.657),l(-.822,.284),M(517.491,38.443),l(1.041,-.37),l(.512,-.299),l(.428,-.212),l(1.426,-.021),l(1.316,-.249),l(.987,-.02),l(1.412,-.091),l(.551,.069),l(.988,.227),l(-.063,.176),l(-.463,.528),l(-.302,.158),l(-.697,.071),l(-.443,.228),l(-.233,.385),l(.006,.818),l(.445,1.039),l(.957,.826),l(.505,.308),l(.775,.307),l(1.162,.392),l(-.028,.282),l(-2.62,-.089),l(-.844,.053),l(-.917,.326),l(-.596,.086),l(-.676,-.494),l(-.382,-.034),l(-1.091,.088),l(-.673,-.102),l(-.031,-.359),l(1.347,-.362),l(.073,-.24),l(-.102,-.017),l(-.944,-.273),l(-1.442,-.411),l(-1.519,-.17),l(-.33,.156),l(-.624,.122),l(-.681,-.033),l(-.625,-.396),l(-.114,-.415),l(.229,-.312),l(.39,-.209),l(.344,-.036),l(.318,.104),l(.65,.016),l(.518,-.192),l(1.121,-.768),l(.243,-.35),l(-.322,0),l(-.981,-.243),M(.125,56.089),l(0,-.562),l(0,-.576),l(0,-.157),l(0,-1.292),V(0,51.28),l(0,-2.222),l(0,-2.12),l(2.917,.61),l(1.693,.598),l(.933,.181),l(3.819,.99),l(1.059,.395),l(.204,.149),l(.074,.214),l(.445,.429),l(.406,.789),l(-.209,.428),l(1.034,.8),l(.982,.26),l(-.02,-.18),l(.297,-.148),l(-.051,-.196),l(-.638,-.392),l(.016,-.542),l(.077,-.296),l(1.026,-.183),l(.984,.278),l(.573,.098),l(.767,.064),l(1.003,-.117),l(.933,.13),l(.93,.425),l(.694,.359),l(1.676,.684),l(.32,.081),l(.584,.179),l(.43,.211),l(.09,.13),l(-.343,.033),l(-1.026,-.096),l(-.364,.034),l(-.808,.798),l(-1.539,-.468),l(.138,.178),l(.438,.227),l(.465,.324),l(-1.177,-.111),l(-1.008,.05),l(-1.202,-.387),l(-.708,-.096),l(.135,.129),l(.289,.259),l(.084,0),l(.561,.259),l(.205,.307),l(.131,.453),l(-.056,.339),l(-.814,.05),l(-.805,.065),l(-.189,.161),l(.5,.865),l(-.115,.272),l(-1.04,-.078),l(-.397,.081),l(-1.193,-.174),l(-.264,-.352),l(-1.795,-.51),l(-.253,.273),L(9.85,55.79),l(-.276,-.161),l(-.265,-.403),l(.174,-.178),l(-.321,-.42),l(-.87,-.339),l(-.565,-.063),l(-.762,.034),l(-.858,.229),l(-1.343,.084),L(3.57,54.443),l(.059,-.325),l(-.155,-.325),l(-.618,-.487),L(1.958,53.43),l(-.45,.409),l(-.655,1.228),l(-.166,.715),l(-.562,.307),
+N(449.401,22.792),l(-1.596,-.014),l(-2.019,-.016),l(-1.739,.063),l(-.764,.061),l(-1.972,-.316),l(.812,-.456),l(.826,-.172),l(.735,-.453),l(1.148,-.588),l(1.354,.691),l(.998,.105),l(1.116,-.088),l(.9,.084),l(.704,.341),l(.865,-.323),l(.53,-.454),l(.868,-.306),l(-.311,.823),l(.086,.32),l(.851,-.001),l(.991,-.495),l(1.061,-.261),l(.803,.128),l(.559,.472),l(.705,.041),l(2.007,-.111),l(1.543,.189),l(.551,.376),l(-.507,.201),l(-2.004,.622),l(-1.623,.38),l(-1.069,.062),l(-.976,.14),l(-1.324,.366),l(-6.271,-.097),l(-.033,-.377),l(1.492,-.26),l(.702,-.677),M(430.027,22.752),l(.068,.697),l(.252,.119),l(1.694,.155),l(.221,-.377),l(.13,-.418),l(.573,-.141),l(.523,-.041),l(.949,.477),l(1.192,.771),l(.798,.235),l(.568,-.218),l(-.222,-.296),l(-.46,-.356),l(-.327,-.477),l(-.04,-.22),l(.91,-.407),l(1.001,.103),l(.485,.18),l(1.278,.018),l(.586,.179),l(-.08,.419),l(-.169,.298),l(.104,.159),l(.549,.118),l(.743,-.338),l(.44,-.1),l(.662,.396),l(.678,.335),l(.785,.156),l(.948,.117),l(1.672,.429),l(.498,.234),l(-1.305,.277),l(-1.981,.218),l(-.696,.293),l(-.731,1.144),l(-.885,.885),l(-1.243,.117),l(-.766,.535),l(-.455,.589),l(-.029,.378),l(-.786,.209),l(-.556,-.018),l(-1.593,-.355),l(-1.883,-.507),l(-1.365,-.568),l(.651,-.364),l(1.565,-.041),l(1.788,-.137),l(.944,-.386),l(-.652,-.249),l(-1.822,.139),l(-1.566,.118),l(-1.694,.042),l(-.502,-.519),l(.959,-.06),l(1.371,-.215),l(2.149,-.314),l(1.547,-.45),l(-2.525,-.405),l(-.881,-.292),l(-.589,.138),l(-1.036,.646),l(-1.069,.293),l(-.562,.059),l(-1.236,-.172),l(-.338,-.174),l(-1.05,-.368),l(-.807,-.233),l(-.698,-.41),l(1.698,-.396),l(-.817,-.571),l(-1.359,.319),l(-.467,-.078),l(-.924,-.751),l(.812,-.36),l(.516,-.021),l(1.075,-.122),l(1.017,.038),l(.577,-.061),l(1.188,-.042),M(425.42,68.82),l(-.148,-.21),l(-.629,.132),l(-.134,-.026),l(-.249,-.237),l(-.239,-.343),l(.188,-.158),l(-.143,-.291),l(-.361,.185),l(-.062,.29),l(.113,.237),l(-.147,.105),l(-.339,.316),l(-.086,.185),l(-.521,.105),l(-.533,-.131),l(-.781,.342),l(.057,.131),l(-.502,.289),l(-.498,.263),l(-1.658,.813),L(418.697,71),l(-.274,.052),l(-.271,0),l(-2.111,.209),l(-.188,-.236),l(-.33,-.131),l(-.183,.209),L(414.976,71),l(.384,-.184),l(-.935,-.236),l(-.794,-.341),l(-.574,-.052),l(-.479,-.578),l(.292,-.368),l(-.126,-.21),l(.34,.026),l(.085,.316),l(.284,-.21),l(.667,.263),l(-.244,-.197),l(.127,-.145),l(.5,-.092),l(-.479,.026),l(-.245,.105),l(-.263,-.184),l(-.111,-.132),l(.579,-.276),l(.455,-.185),l(-.482,.066),l(-.317,-.132),l(.441,-.237),l(.083,-.237),l(-.661,.475),l(-.036,-.264),l(-.265,-.171),l(.005,.211),l(.076,.171),l(-.419,.158),l(-.41,0),l(.014,-.277),l(-.15,.264),l(-.235,-.079),l(-.2,-.448),l(.658,-.449),l(.08,.436),l(.068,-.317),l(.438,.158),l(.211,-.092),l(-.166,-.106),l(.674,-.079),l(.479,-.251),l(-.611,.159),l(-.48,0),l(-.335,-.159),l(.196,-.132),l(.387,-.146),l(.196,-.471),l(-.821,-.014),l(-.546,.094),l(-.406,-.309),l(.368,-.14),l(.643,-.404),l(-.212,-.186),l(-.445,.063),l(-.399,-.062),l(-.211,-.217),l(.284,-.482),l(.619,-.265),l(-.595,-.248),l(-.225,-.374),l(.272,-.188),l(-.089,-.687),l(.106,-.094),l(.594,-.063),l(.392,-.045),l(.455,.044),l(.082,-.11),l(-.255,-.134),l(-.425,-.118),l(-.228,-.17),l(.098,-.188),l(.211,-.125),l(.575,-.064),l(.511,-.346),l(.567,.109),l(.498,-.111),l(.179,-.55),l(.835,.093),l(.668,.125),l(.571,-.252),l(-.989,-.234),l(-.266,-.314),l(.236,-.143),l(1.151,.014),l(.851,-.08),l(.538,.219),l(.06,-.473),l(.477,-.27),l(.297,-.333),l(.78,-.175),l(.438,.189),l(.138,.079),l(.5,-.476),l(.869,-.413),l(.084,-.429),l(.437,-.398),l(1.497,-.735),l(.335,-.192),l(.235,-.048),l(.481,.175),l(.335,-.001),l(.319,-.224),l(-.225,-.223),l(.141,-.128),l(.488,-.224),l(.457,-.305),l(.481,-.483),l(.245,.064),l(.676,-.017),l(-.067,-.612),l(-.471,-.112),l(.434,-.405),l(.511,-.648),l(.841,.063),l(.047,-.341),l(.096,-.228),l(.954,-.116),l(-.396,-.357),l(-.327,-.097),l(-.193,-.146),l(.164,-.245),l(.313,-.246),l(.464,.048),l(.888,-.067),l(-.543,-.522),l(.249,-.082),l(.435,-.034),l(.707,-.263),l(.592,-.067),l(.308,-.208),l(-.358,-.064),l(-.105,-.187),l(.289,-.1),l(.616,-.33),l(.557,.032),l(.351,.098),l(.434,-.264),l(.369,-.031),l(.035,-.176),l(-.421,-.321),l(.627,-.232),l(.146,-.275),l(.336,.01),l(.359,.297),l(.397,.082),l(.526,-.381),l(-.281,-.656),l(.438,.065),l(.698,-.058),l(.207,-.174),l(-.604,-.315),l(.411,-.224),l(.816,-.251),l(.093,-.257),l(.261,-.194),l(.73,-.135),l(.074,.067),l(.274,-.051),l(.102,-.067),l(.492,-.67),l(.521,.25),l(.96,-.153),l(.411,.133),l(.265,.1),l(.646,.065),l(.536,-.419),l(.747,-.84),l(.286,-.085),l(.16,.168),l(-.178,.286),l(-.182,.587),l(.791,-.454),l(.403,-.572),l(.819,-.085),l(.688,-.439),l(.437,.117),l(.665,.353),l(.567,-.643),l(.318,-.272),l(.976,.1),l(1.019,.506),l(.197,-.152),l(.479,-.595),l(.709,-.444),l(.283,-.052),l(.562,.067),l(.338,-.308),l(.469,-.291),l(.619,-.189),l(1.2,-.173),l(.984,.015),l(-1.337,.755),l(-.24,.273),l(-.107,.646),l(.42,.084),l(.691,-.579),l(1.53,-.941),l(.764,-.275),l(.028,.257),l(-.004,.905),l(.704,-.172),l(.438,-.239),l(.855,-.874),l(.959,-.104),l(.586,-.001),l(.216,.154),l(-.383,.257),l(-.335,.257),l(.074,.239),l(-.319,.41),l(.921,.118),l(.221,-.443),l(.902,-.651),l(.421,.017),l(1.682,.561),l(.667,.135),l(.935,.015),l(.649,.237),l(.1,.272),l(-1.216,.393),l(-1.148,.036),l(-2.233,-.047),l(.193,.084),l(.585,.304),l(1.058,.284),l(.383,.302),l(.728,-.052),l(.679,-.102),l(.831,.032),l(.708,.25),l(-.404,.288),l(-.514,.204),l(-.824,.021),l(-.77,.121),l(-1.023,.49),l(-1.013,.672),l(-.689,.303),l(-.001,-.2),l(.096,-.367),l(.874,-.923),l(-.044,-.268),l(-.88,-.348),l(-.672,-.114),l(-1.125,-.431),l(-.556,.003),l(-.555,.07),l(-2.004,.095),l(-.865,.374),l(-.567,.422),l(-.26,.437),l(-.079,.501),l(-.188,.5),l(-1.25,.472),l(-.582,.368),l(-.31,-.148),l(-1.288,-.492),l(-1.657,.225),l(-1.411,-.042),l(-.536,-.13),l(-.861,-.595),l(-.994,-.612),l(-.486,-.082),l(-.355,.103),l(-1.012,.656),l(-.114,-.063),l(-.41,-.031),l(-.865,.038),l(-.281,1.066),l(-.164,.449),l(-.271,.25),l(-.323,-.015),l(-.67,-.129),l(-.972,-.144),l(-1.731,-.389),l(-.168,.183),l(-.021,.663),l(-.083,.199),l(-.318,.299),l(-.695,.004),l(-.871,-.095),l(-.279,-.015),l(-.465,.167),l(-1.484,1.176),l(.05,.213),l(.171,.507),l(-.188,.345),l(-.615,.346),l(-1.325,.919),l(-.687,.442),l(-.981,.021),l(-.121,.244),l(-.06,.971),l(-.32,.598),l(-.383,.469),l(-.574,.499),l(-.495,.545),l(.339,.125),l(.381,.269),l(.252,.587),l(-.17,.176),l(-.44,.177),l(-.733,.036),l(-.932,-.027),l(-.618,.114),l(-.613,.257),l(-.617,.479),l(-.389,.492),l(-.077,.664),l(-.349,1.093),l(.65,.729),l(-.335,1.171),l(.582,.39),l(.715,.186),l(.297,.243),l(-.432,.67),l(-.941,.16),l(.571,.735),l(.26,.427),l(-.249,.346),l(-.029,.532),l(-.769,.384),l(-.414,.013),l(-.117,.423),l(-.506,.224),l(.22,.831),l(-.324,.737),l(-.41,.013),l(-.15,-.342),l(-.293,-.132),
+N(464.349,47.431),l(-.024,.283),l(-.258,.465),l(.116,.529),l(.563,.212),l(.872,.194),l(.964,.506),l(.543,.392),l(-.421,.364),l(-1.303,.777),l(-.328,.296),l(1.402,1.394),l(.772,1.195),l(-.196,.356),l(-.44,.535),l(-.187,.403),l(-.029,.37),l(.37,.509),l(1.286,1.395),l(.151,.253),l(-.057,.254),l(-.542,.415),l(-.327,.191),l(.439,.33),l(1.357,.499),l(.703,.265),l(.436,.392),l(.161,.346),l(-.105,.409),l(-.43,.536),l(-.591,.536),l(-1.385,.758),l(-1.983,1.195),l(-1.946,1.145),l(-.174,.285),l(-.935,.206),l(-1.195,.188),l(-.149,.181),l(-.506,-.217),l(-.518,.146),l(-.052,.134),l(-.688,-.07),l(-.193,-.108),l(.057,-.387),l(-.241,.527),l(-.268,.14),l(-.633,.047),l(-.328,.03),l(-.1,.267),l(-.589,-.326),l(-.29,.275),l(-.676,.064),l(-.34,.178),l(-.052,-.127),l(-.504,.108),l(-.504,.108),l(-.211,.266),l(-.311,-.152),l(-.672,.126),l(-.04,-.114),l(-.658,.215),l(-.547,.013),l(-.482,.025),l(-.487,-.253),l(-.223,-.274),l(.273,-.34),l(-.846,.017),l(-.517,.177),l(.065,-.382),l(-.446,.076),l(-.644,-.271),l(-.409,-.061),l(-.415,-.231),l(-.26,-.403),l(-.036,-.714),l(.158,-.374),l(.05,-.436),l(.09,-.234),l(-.347,-.483),l(-.431,-.375),l(.3,-.658),l(-.02,-.392),l(-.305,-.204),l(-.37,-.093),l(.062,-.299),l(.295,-.331),l(.637,-.285),l(.144,-.189),l(-.113,-.331),l(.382,-.143),l(.633,.125),l(.274,.063),l(.483,-.222),l(.303,-.427),l(.104,-.459),l(.207,-.27),l(.301,.316),l(.27,0),l(1.678,-1.114),l(1.142,-.654),l(.835,-.576),l(1.026,-.401),l(.874,.03),l(.06,-.304),l(-.13,-.144),l(-.357,-.16),l(.119,-.371),l(.146,-.387),l(-.329,-.274),l(-1.139,-.095),l(-.836,-.451),l(-.594,-.024),l(-.113,-.234),l(-.327,-.307),l(-.208,-.535),l(.5,-.8),l(-.028,-.392),l(-.405,-.685),l(-.507,-.637),l(.004,-.526),l(.062,-.413),l(-.77,-.558),l(-.508,-.229),l(-1.583,-.207),l(-1.085,-.292),l(-1.286,-.658),l(-.616,-.463),l(-.146,-.033),l(1.012,-.656),l(.355,-.103),l(.486,.082),l(.994,.612),l(.861,.595),l(.536,.13),l(1.411,.042),l(1.657,-.225),l(1.288,.492),l(.31,.148),l(.582,-.368),l(1.25,-.472),l(.188,-.5),l(.079,-.501),l(.26,-.437),l(.567,-.422),l(.865,-.374),l(2.004,-.095),l(.555,-.07),l(.556,-.003),l(1.125,.431),l(.672,.114),l(.88,.348),l(.044,.268),l(-.874,.923),l(-.096,.367),l(.001,.2),
+N(453.795,53.873),l(-.23,-.004),l(-.975,.164),l(-.447,.098),l(-.902,-.387),l(-.339,-.178),l(-.347,.114),l(-.641,.374),l(-.542,.599),l(-.309,.146),l(-.658,.018),l(-.545,.355),l(.325,.241),l(.146,.192),l(-.355,.273),l(-.907,.401),l(.317,.271),l(.385,.159),l(.33,.302),l(-.749,.367),l(-.405,.43),l(-.368,.461),l(-1.591,.669),l(-.699,.286),l(-.456,-.205),l(-.326,.08),l(-.538,.539),l(-.646,.144),l(-.663,.348),l(-.217,.284),l(-.191,.142),l(-.746,.033),l(-.221,-.031),l(.151,.535),l(-.484,.425),L(439,61.33),l(.216,.533),l(.243,.25),l(-.244,.955),l(-.498,.063),l(-.217,.204),l(.189,.757),l(-.009,.544),l(.381,.444),l(-.287,.175),l(.926,.082),l(.225,.187),l(.499,-.082),l(.652,.594),l(.428,.314),l(.86,.163),l(.198,.406),l(.32,.139),l(.014,.335),l(-.391,0),l(-.508,.323),l(-.861,.3),l(-.492,.162),l(-.521,-.012),l(-.146,-.115),l(-.805,-.115),l(-.66,-.173),l(-.634,-.069),l(-.137,.104),l(-.446,-.115),l(-.257,.3),l(.819,-.069),l(.497,.265),l(.591,.046),l(.205,.208),l(.481,.069),l(.021,-.127),l(.616,.069),l(.258,-.196),l(.297,.011),l(.481,-.104),l(.226,.081),l(.026,-.15),l(.164,0),l(.214,.15),l(-.029,.081),l(-.722,.023),l(.219,.288),l(-.792,.265),l(-.168,.288),l(-.386,.08),l(-.089,-.092),l(.042,-.161),l(-.014,-.276),l(-.258,.023),l(-.091,.46),l(-.501,.333),l(-.248,.058),l(-.36,-.069),l(-.005,.218),l(-1.418,.035),l(.156,.458),l(.513,.258),l(.066,.609),l(-.109,.33),l(-.281,.051),l(-.308,-.064),l(.038,.429),l(.29,.081),l(-.126,.268),l(.24,.228),l(-.365,.364),l(-.222,.455),l(-.041,.56),l(-.766,1.043),l(-.58,.71),l(-.165,-.062),l(-.442,-.236),l(-.614,.193),l(-1.262,-.089),l(-.106,.311),l(-.252,-.083),l(-.518,.264),l(-.295,.517),l(.356,.336),l(-.376,.374),l(-.48,-.129),l(-.173,-.037),l(-1.158,.269),l(-1.11,-.116),l(-.001,-.142),l(.19,-.078),l(-.062,-.181),l(.281,-.297),l(-.638,-.53),l(-.413,-.479),l(-.22,-.272),l(.529,-.116),l(-.082,-.312),l(.421,.077),l(.147,-.245),l(-.19,-.234),l(-.285,.013),l(-.292,-.377),l(-.527,-.221),l(-.63,-.99),l(-.286,0),l(.011,-.408),l(-.256,-.364),l(-.348,-.298),l(.221,-.484),l(.206,-.341),l(-.418,-.154),l(-.405,.102),l(-.055,-.288),l(-.412,.051),l(-.18,-1.207),l(-.225,-.131),l(.311,-.355),l(.293,.132),l(.15,.342),l(.41,-.013),l(.324,-.737),l(-.22,-.831),l(.506,-.224),l(.117,-.423),l(.414,-.013),l(.769,-.384),l(.029,-.532),l(.249,-.346),l(-.26,-.427),l(-.571,-.735),l(.941,-.16),l(.432,-.67),l(-.297,-.243),l(-.715,-.186),l(-.582,-.39),l(.335,-1.171),l(-.65,-.729),l(.349,-1.093),l(.077,-.664),l(.389,-.492),l(.617,-.479),l(.613,-.257),l(.618,-.114),l(.932,.027),l(.733,-.036),l(.44,-.177),l(.17,-.176),l(-.252,-.587),l(-.381,-.269),l(-.339,-.125),l(.495,-.545),l(.574,-.499),l(.383,-.469),l(.32,-.598),l(.06,-.971),l(.121,-.244),l(.981,-.021),l(.687,-.442),l(1.325,-.919),l(.615,-.346),l(.188,-.345),l(-.171,-.507),l(-.05,-.213),l(1.484,-1.176),l(.465,-.167),l(.279,.015),l(.871,.095),l(.695,-.004),l(.318,-.299),l(.083,-.199),l(.021,-.663),l(.168,-.183),l(1.731,.389),l(.972,.144),l(.67,.129),l(.323,.015),l(.271,-.25),l(.164,-.449),l(.281,-1.066),l(.865,-.038),l(.41,.031),l(.114,.063),l(.146,.033),l(.616,.463),l(1.286,.658),l(1.085,.292),l(1.583,.207),l(.508,.229),l(.77,.558),l(-.062,.413),l(-.004,.526),l(.507,.637),l(.405,.685),l(.028,.392),l(-.5,.8),l(.208,.535),l(.327,.307),l(.113,.234),
+N(238.107,361.753),l(.515,-.688),l(.859,0),l(1.202,-.515),l(-.171,-.858),l(-3.091,.344),l(-2.061,.515),l(-1.545,-.344),l(-.515,-.858),l(1.374,-.515),l(1.202,-.172),l(3.091,-.172),l(1.374,-.515),l(-.172,-1.03),l(.859,-.516),l(0,-1.374),l(-1.326,-1.054),l(.982,-.491),l(1.202,-.344),l(1.545,-.172),l(.858,.344),l(.858,.858),l(1.03,1.374),l(1.374,1.202),l(1.03,1.373),l(-.515,2.061),l(-1.545,.516),l(-1.545,.858),l(-3.434,.172),l(-3.435,0),M(172.686,360.379),l(3.091,-.687),l(3.262,0),l(5.495,0),l(2.919,1.202),l(-2.748,.687),l(-3.759,-.337),l(-5.169,-.35),l(-3.091,-.516),M(65.13,371.085),l(1.194,-.265),l(1.393,.133),l(.796,.464),l(-1.99,0),l(-1.393,-.332),M(277.944,379.954),l(.515,-1.202),l(2.404,-1.03),l(2.748,-.172),l(2.061,-.171),l(1.545,-.859),l(.343,-1.545),l(1.374,-.687),l(3.091,-.688),l(4.464,0),l(2.404,0),l(3.434,.688),l(.687,1.889),l(0,1.202),l(-2.061,1.201),l(-11.848,1.03),l(-11.161,.344),M(36.004,376.434),l(1.202,-.858),l(2.061,-.343),l(4.121,.515),l(4.293,1.374),l(-.887,.482),l(-2.719,.204),l(-4.293,-.515),l(-3.777,-.859),M(777.843,385.46),l(3.823,.391),l(.899,.029),l(.322,.234),l(1.202,.294),l(2.329,.088),l(2.126,-.003),l(5.218,.329),l(3.046,.348),l(1.144,-.059),l(.762,.029),l(1.411,.37),l(0,2.175),l(0,2.223),l(0,2.222),l(0,2.223),l(0,2.222),l(0,1.551),l(-.041,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.182,0),l(-.041,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.182,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),H(9.014,0),H(6.792,0),H(4.569,0),H(2.347,0),H(.125,0),l(0,-1.544),l(0,-2.222),l(0,-2.223),l(0,-2.222),l(0,-2.223),l(0,-2.223),l(3.941,.297),l(10.131,0),l(7.898,.516),l(6.868,.172),l(8.586,.858),l(6.353,.515),l(8.757,.172),l(4.808,-.172),l(6.525,-.172),l(2.404,.344),l(3.606,-.858),l(16.312,.344),l(4.638,-.013),l(1.143,-.467),l(-14.366,-.551),l(-9.616,-.688),l(-2.232,0),l(-3.091,-.172),l(-.858,-.171),l(-1.545,-.344),l(-.498,-.455),l(.155,-1.434),l(.687,-.858),l(1.889,-.688),l(-3.263,-.687),l(-2.232,-.344),l(-.953,-.503),l(1.589,-.521),l(-2.891,-.6),l(-3.074,-.208),l(-.104,-.261),l(2.857,-.312),l(2.748,0),l(6.525,.343),l(3.434,0),l(2.576,.344),l(4.636,-1.202),l(2.404,-.687),l(-2.748,-1.03),l(-3.778,-.858),l(-4.391,-.405),l(-2.649,-.625),l(-3.262,-.172),l(-2.919,0),l(-2.919,-.858),l(2.576,-.516),l(1.28,-.342),l(-1.28,-.517),l(-2.919,.516),H(52.66,0),l(-1.717,-.344),L(49.054,373),l(.343,-1.202),l(1.956,-.07),l(1.65,-.273),l(4.98,0),l(4.626,.493),l(3.272,.709),l(1.374,.172),l(4.465,-.858),l(4.089,.243),l(0,-.729),l(.066,-.597),l(-2.255,-.73),l(-2.93,-.075),l(-2.576,-.344),l(2.404,-.687),l(4.121,.171),l(1.717,-.343),l(3.091,-.516),l(3.09,-.343),l(5.667,-.688),l(4.465,-.343),l(3.949,-.516),l(4.464,-.515),l(2.748,.343),l(5.667,0),l(4.464,.258),l(4.121,-.515),l(7.898,.343),l(7.555,-.687),l(3.091,.172),l(1.545,-.688),l(1.545,.516),l(5.323,-.172),l(.121,-.87),l(1.758,-.293),l(1.433,.325),l(.586,.554),l(-.423,.813),l(2.364,.329),l(1.38,-.264),l(-.006,-.938),l(1.438,-.267),l(1.48,.267),l(.212,1.036),l(-1.009,.423),l(.521,.781),l(3.024,-.351),l(1.889,.343),l(3.434,.172),l(2.576,-.687),l(3.778,.086),l(4.868,.33),l(2.344,.013),l(1.449,.109),l(.693,-.53),l(-.611,-.285),l(-2.202,-.122),l(1.305,-.285),l(.041,-.408),l(-.367,-.245),l(-1.386,.123),l(-.693,.04),l(-.856,-.979),l(-2.178,-.337),l(-.676,-.193),l(-.182,-.665),l(4.056,.462),l(3.874,-.245),l(-1.224,-.489),l(-6.035,-.163),l(-.774,-.082),l(-.653,-.489),l(0,-.815),l(2.301,.105),l(.839,.67),l(5.301,-.204),l(7.253,.736),l(2.404,-.172),l(3.262,-.172),l(4.464,.172),l(3.263,.172),l(2.061,-.516),l(.687,-.687),l(1.202,0),l(.344,1.03),l(2.576,.343),l(2.404,-.343),l(.858,.515),l(3.435,.688),l(3.777,.343),l(2.061,.172),l(1.03,-.515),l(.687,-1.202),l(2.747,-.172),l(1.03,.515),l(1.717,.688),l(3.091,-.172),l(3.434,.258),l(4.121,-.344),l(4.979,-.516),l(5.495,-.858),l(3.091,-.515),l(1.202,-.688),l(0,-1.373),l(-1.374,-1.202),l(-.859,-1.545),l(-1.202,-.688),l(-.344,-1.03),l(1.202,-1.03),l(1.717,-.343),l(.858,-1.202),l(-.343,-.858),l(-.172,-1.03),l(-1.202,-.516),l(.344,-1.545),l(1.889,-.172),l(2.404,-1.374),l(1.545,-.515),l(.859,-.859),l(1.03,-.687),l(2.919,-1.202),l(3.606,-.515),l(2.404,-1.202),l(2.404,-.172),l(2.232,-1.03),l(1.545,-.343),l(.343,1.03),l(-1.374,.515),l(-1.03,.344),l(-1.202,.515),l(-.858,0),l(-1.202,0),l(-2.919,1.202),l(-1.03,.687),l(-1.717,1.374),l(-.172,1.202),l(-.687,.687),l(-1.374,-.344),l(-1.03,0),l(-1.202,.516),l(-1.202,.687),l(-1.03,.688),l(-.687,1.201),l(.515,.688),l(.687,.687),l(1.374,.344),l(1.545,0),l(.858,.515),l(.687,1.202),l(1.545,1.546),l(.687,.858),l(1.03,.687),l(.687,1.374),l(.858,1.373),l(.344,1.546),l(-.344,.858),l(-.858,1.717),l(-2.919,1.889),l(-1.202,.172),l(-1.03,.858),l(-1.717,1.03),l(-3.263,.344),l(-4.979,.858),l(-2.404,.344),l(-2.919,.687),l(-7.377,-.141),l(-4.556,-.316),l(-.773,-.401),l(-1.717,-.344),l(-1.717,-.343),l(-1.374,0),l(-.859,.515),l(.515,.858),l(2.061,.688),l(4.121,1.202),l(4.995,.04),l(1.202,.759),l(-3.278,.574),l(-5.667,-.343),l(-4.979,-.516),l(-3.097,-.285),l(-2.151,.253),l(.823,.696),l(1.164,.538),l(3.262,.172),l(2.232,0),l(1.374,.343),l(-.249,.402),l(-.782,.113),l(-2.232,.515),l(-5.151,-.171),l(-1.374,-.172),l(-2.232,-1.202),l(-2.404,0),l(.128,.917),l(2.62,1.144),l(1.03,.172),l(1.202,-.172),l(1.374,.344),l(2.404,0),l(4.98,-.516),l(3.09,.687),l(.385,.431),l(-.041,.6),l(-2.061,.172),l(-3.263,.172),l(-2.608,-.185),l(3.811,.871),l(3.656,-.238),l(1.324,.41),l(2.576,.687),l(6.869,.859),l(8.585,.858),l(10.646,1.373),l(6.525,.516),l(2.061,.858),l(.687,1.03),l(3.091,-.344),l(5.323,-1.545),l(6.525,-.687),l(2.455,-.066),l(6.474,-.105),l(9.788,-.344),l(1.889,.344),l(2.576,-1.202),l(8.414,-1.03),l(11.333,-.172),l(8.929,-.858),l(1.202,-.858),l(-1.202,-.516),l(-.271,-1.07),l(-1.184,-.338),l(-5.071,.035),l(-2.232,.172),l(-4.808,-.344),l(-3.091,0),l(0,-1.202),l(3.263,-1.545),l(3.949,-1.202),l(2.404,-.172),l(4.121,-1.03),l(9.444,-1.202),l(5.323,-.858),l(6.525,0),l(4.979,-.516),l(2.747,-2.061),l(6.039,-1.541),l(-1.23,-.52),l(-2.576,.516),l(-2.061,-.344),l(1.545,-1.03),l(1.545,-.343),l(2.48,-.249),l(.696,-.742),l(3.85,-.742),l(1.577,-.418),l(.603,-.557),l(-1.716,-.881),l(-.093,-.604),l(1.252,-.231),l(.464,.742),l(1.017,-.097),l(2.061,-.858),l(1.03,-.343),l(.858,.171),l(1.203,1.544),l(2.403,-.342),l(.242,-.91),l(.96,-.807),l(1.889,-.516),l(1.374,.516),l(-1.162,.574),l(-.139,.604),l(3.189,-.319),l(4.808,.258),l(3.263,-.172),l(1.03,.858),l(1.545,-.344),l(1.717,-.515),l(5.839,-.858),l(7.212,-1.03),l(4.292,-.858),l(2.061,0),l(2.061,1.373),l(1.718,.344),l(1.889,-.344),l(1.545,-.858),l(7.556,-.344),l(3.434,-.171),l(4.293,.171),l(6.697,.945),l(2.575,0),l(4.293,-.516),l(6.01,-.687),l(5.151,-.688),l(2.404,-.515),l(2.231,-.344),l(.615,-.959),l(1.148,-.835),l(1.671,-.095),l(.938,.965),l(1.294,.409),l(1.732,1.052),l(1.009,.139),l(1.009,-.661),l(1.322,-.174),l(.209,.522),l(-.835,.312),l(.661,.244),l(.8,.278),l(.383,-.383),l(.313,-.383),l(.312,.209),l(-.174,.348),l(0,.521),l(.592,-.174),l(1.913,-.557),l(.418,-1.704),l(5.102,-1.137),l(1.374,-.687),l(3.605,-.172),l(1.363,-.646),l(2.758,-.126),l(.21,-.27),l(-.165,-.297),l(.692,-.23),l(1.221,.165),l(.197,.593),l(.66,.231),l(2.165,-.536),l(1.265,-.223),l(-.726,-.297),l(-.561,-.264),l(.099,-.362),l(1.451,-.1),l(1.562,.215),l(.647,.28),l(.692,-.132),l(-.066,-.33),l(-.527,-.33),l(0,-.561),l(.297,-.23),l(.846,-.242),l(4.292,-.858),l(2.92,-.172),l(3.058,.115),l(2.296,1.076),l(1.316,.023),l(.741,.144),l(.023,.383),l(-.908,.096),l(-.168,.454),l(1.723,.216),l(4.892,.646),l(5.571,.453),l(1.546,.172),l(1.717,-.172),l(7.556,.688),l(5.323,0),l(.687,.687),l(.2,1.075),l(-.73,1.232),l(-.672,.439),l(-.425,1.113),l(-.73,.319),l(-.905,-.402),l(-.83,.402),l(-.73,.959),l(1.507,.457),l(.594,.045),l(.959,-.593),l(.411,.365),l(-.867,1.05),l(-1.215,.062),l(-1.717,1.374),l(0,.858),l(1.889,.688),l(2.919,0),l(1.718,-.859),l(1.889,-1.545),l(1.717,-1.717),l(1.374,-.688),l(4.121,-1.03),l(2.231,-.687),l(3.263,-.344),l(2.061,-1.03),l(1.03,-1.03),l(3.435,-1.03),l(2.403,-.687),l(2.061,-.687),l(2.061,-.172),l(6.182,-.687),l(3.778,.171),l(1.204,-.542),l(.169,-.659),l(.54,-.759),l(.49,-.101),l(.471,.776),l(.305,.775),l(4.892,-.35),l(5.838,0),l(4.979,-.172),l(3.091,.344),l(2.112,-.214),l(1.328,.085),l(1.018,.594),l(1.188,-.764),l(2.714,-1.046),l(1.866,-.226),l(.664,-.028),l(.594,-.028),l(.777,-.057),l(1.512,.311),l(1.166,-.172),l(3.606,.687),l(3.777,.344),l(1.03,0),l(1.064,.573),l(.745,.088),l(2.149,-.088),l(1.271,-.263),l(1.315,-.438),l(-.192,-.216),l(.28,-.53),l(1.447,-.131),l(.921,-.088),l(.786,.062),l(2.195,-.501),l(2.097,.501),l(.688,.687),l(.373,.435),l(1.842,-.131),l(.921,-.044),l(.985,.255),l(1.889,.688),l(2.919,.515),l(2.919,-.343),l(4.121,-.258),l(2.232,-.344),l(2.404,0),l(3.521,.315),l(.702,-.755),l(2.158,-.162),l(.863,1.133),l(4.909,.323),l(.917,-1.133),l(1.889,-.593),l(5.988,.013),l(.756,-.283),l(1.996,.108),l(-.432,-1.403),l(.054,-.809),l(1.232,-.36),l(.872,1.277),l(-.647,1.079),l(.805,.219),l(2.81,.482),l(5.262,.376),l(5.494,.344),l(2.278,.583),l(1.283,.14),l(1.396,-.167),l(2.204,.446),l(1.961,-.167),l(.719,.278),l(-.435,.431),l(-.151,.351),l(.293,.123),l(2.081,.331),l(1.078,.484),l(2.706,-.134),l(.377,.595),l(1.038,.227),l(4.808,.515),l(-.123,.536),l(.567,.236),l(2.695,-.095),l(3.214,.181),l(-.329,-.984),l(.71,-.095),l(.804,.426),l(.331,.709),l(1.135,.284),l(2.672,.519),l(2.576,-.343),l(4.017,.999),l(1.626,.495),l(1.672,.871),l(1.626,.023),l(.504,.187),l(.532,-.021),l(.754,-.047),l(.777,.047),l(.896,.589),l(.541,.118),l(.542,.07),l(.142,-.259),l(.471,0),l(1.484,-.048),l(1.416,.237),l(5.951,.535),l(.729,.516),l(1.733,.323),l(2.979,.944),l(.43,-.335),l(.551,.191),l(.454,.67),l(-.741,.215),l(-1.124,.167),l(-.216,.359),l(.168,.263),l(.55,-.048),l(-.024,.67),l(-.43,-.048),l(-.096,.287),l(-.312,.071),l(-.598,.622),l(-.526,.12),l(-.55,.287),l(-.168,.669),l(-1.603,.096),l(-1.339,-.239),l(-1.794,.359),l(-2.153,.425),l(-2.629,.764),l(1.271,1.103),l(-1.907,.212),l(-1.23,-.255),l(-.763,.212),l(-.043,.637),l(-2.374,.169),l(.136,.514),l(.924,.632),l(-.127,.763),l(-.382,.085),l(-.466,.467),l(.551,.466),l(.085,.764),l(-.764,.424),l(.637,.254),l(.763,0),l(.764,.255),l(.339,.509),l(.17,.721),l(1.284,-.187),l(.836,.907),l(2.035,.297),l(.339,.339),l(2.672,.552),l(-4.113,0),l(-2.799,.138),l(-2.332,.541),l(-1.823,-.085),l(-.806,.212),l(-1.611,.339),l(-1.326,-.32),l(-.546,.426),l(1.116,.529),l(-.503,.447),l(-.669,.195),l(-1.117,.753),l(-2.623,.642),l(.475,.391),l(2.205,-.111),l(.53,0),l(.809,.307),l(.168,.363),l(.499,.18),l(.422,.183),l(.111,.894),l(.209,.502),l(3.195,.279),l(2.874,.948),l(3.662,.801),l(5.151,1.202),l(1.456,.062),l(1.015,.17),l(-.146,.234),l(-1.67,.264),l(-.47,.264),l(1.173,.088),l(2.198,-.293),l(1.988,.166),
+E(0,0)
+};
+
+#undef N
+#undef M
+#undef L
+#undef l
+#undef E
diff --git a/perf/micro/zrusin-another.h b/perf/micro/zrusin-another.h
new file mode 100644 (file)
index 0000000..f6e72ae
--- /dev/null
@@ -0,0 +1,668 @@
+/* The data in this file was created by Zack Rusin and downloaded from:
+
+   http://ktown.kde.org/~zrusin/examples/cairorender.tar.bz2
+
+   It is included in the cairo performance test suite by his
+   permission, (given in email to Carl Worth on 2006-10-23)
+*/
+point_t zrusin_another[] = {
+{107.893088734820,103.841923410053}, {106.908713734820,104.920048410053},
+{106.605078419636,105.201721390261}, {102.286079757931,107.612435141518},
+{95.848592904540,109.855345077872}, {95.820873452616,109.863161445001},
+{90.517184462013,111.170008329713}, {87.125000000000,111.625000000000},
+{82.393245921775,111.316572641568}, {77.855183975790,110.402282596173},
+{73.577390672683,108.898617908665}, {69.626442523093,106.822066623894},
+{69.373711344187,106.641023992165}, {57.717461344187,97.141023992165},
+{60.861283165549,97.153157587308}, {52.861026127481,102.679312400770},
+{43.297199733607,107.707604120505}, {38.061787185467,109.824994465236},
+{32.824736230810,111.354170438973}, {27.636335844151,112.281599723349},
+{22.546875000000,112.593750000000}, {19.478437557032,111.949288129016},
+{16.158848619890,110.212087077375}, {13.224557514711,107.109984243309},
+{12.401022392395,105.215121771751}, {12.109375000000,103.234375000000},
+{12.857968117079,98.340329228563}, {15.050447208669,93.495802782054},
+{18.606861885297,88.792446786896}, {23.447261757492,84.321912369510},
+{23.462751811761,84.309735859104}, {29.835792474114,80.170241641945},
+{36.826339837257,77.183700966953}, {44.355342734713,75.374150023260},
+{52.343750000000,74.765625000000}, {54.172214431835,74.493658379815},
+{55.005326273741,73.240819884599}, {54.891695624471,73.637732784350},
+{55.835419478567,69.288398667240}, {56.941029555972,65.553969608663},
+{57.993585771605,68.524034756036}, {53.681468264740,66.410473849442},
+{48.062136369578,64.586219029579}, {42.258612309644,63.317877950046},
+{37.437500000000,62.906250000000}, {32.573685233275,63.044800246717},
+{26.989159126193,63.632907811096}, {20.752015377001,64.279295952574},
+{16.562500000000,64.484375000000}, {16.408072340842,64.479600861137},
+{11.611197340842,64.182725861137}, {11.453516511116,64.167941148499},
+{6.725716011250,62.845442977500}, {4.607850691978,60.769517471259},
+{3.781250000000,57.859375000000}, {4.373461857036,55.042556375256},
+{5.951936001924,52.702230099626}, {6.143541831166,52.540844514692},
+{9.884650718045,51.301737345381}, {10.233131146503,51.254965794601},
+{14.936256146503,50.958090794601}, {15.093750000000,50.953125000000},
+{19.896489079886,51.056672881330}, {25.533897731570,51.448761318515},
+{25.296875000000,51.437500000000}, {30.909892241200,52.067729129895},
+{30.484375000000,52.031250000000}, {39.960841830525,52.410121686268},
+{48.752300459876,53.538988916016}, {48.757228534847,53.539874692156},
+{54.123571896675,54.731960341587}, {58.970980406852,56.270657465230},
+{63.248918806945,58.141504425356}, {66.906851838519,60.330039584234},
+{66.896289983187,60.322652661852}, {70.654761046586,63.581911162241},
+{73.423041490057,67.400928399521}, {75.133561184297,71.670457102504},
+{75.718750000000,76.281250000000}, {74.721151272876,79.892293861068},
+{72.068677652167,83.513297493231}, {70.325052633091,85.868624092784},
+{69.828125000000,88.250000000000}, {70.186304443223,90.552747673203},
+{71.326302526678,92.735137555366}, {73.346308881046,94.894011912335},
+{76.344513137007,97.126213009954}, {79.989627194854,99.164217236556},
+{83.789048154438,100.596188457783}, {87.811323321056,101.441306202608},
+{92.125000000000,101.718750000000}, {97.462586180660,101.070932456387},
+{97.557831952069,101.055648080491}, {103.015964519671,99.851433056816},
+{103.984375000000,99.656250000000}, {106.046875000000,99.656250000000},
+{107.893088734820,103.841923410053}, {106.046875000000,104.656250000000},
+{103.984375000000,104.656250000000}, {104.952785480329,104.461066943184},
+{98.254668047931,106.006851919509}, {98.349913819340,105.991567543613},
+{92.125000000000,106.718750000000}, {87.170121991444,106.371193797392},
+{82.386733095562,105.341311542217}, {77.843380617646,103.648282763444},
+{73.608611862993,101.311286990046}, {69.914433306454,98.422394337665},
+{67.154166223322,95.217987444634}, {65.426000244277,91.794908576797},
+{64.828125000000,88.250000000000}, {65.079958470471,86.099459712769},
+{65.803853616909,83.955594657216}, {68.478197347833,80.033577506769},
+{70.243692477124,77.869424888932}, {70.718750000000,76.281250000000},
+{70.335188815703,72.767042897496}, {69.139458509943,69.692821600479},
+{67.063988953414,66.949338837759}, {64.041210016813,64.427347338148},
+{64.030648161481,64.419960415766}, {57.122769593148,60.885592534770},
+{47.867771465153,58.460125307844}, {47.872699540124,58.461011083984},
+{39.531345669475,57.386753313732}, {30.484375000000,57.031250000000},
+{30.058857758800,56.994770870105}, {25.296875000000,56.437500000000},
+{25.059852268430,56.426238681485}, {19.697260920114,56.052702118670},
+{15.093750000000,55.953125000000}, {15.251243853497,55.948159205399},
+{10.548118853497,56.245034205399}, {10.896599281955,56.198262654619},
+{9.168958168834,56.521655485308}, {9.360563998076,56.360269900374},
+{8.781250000000,57.859375000000}, {8.758601803360,58.189255218076},
+{8.735899308021,58.292982528741}, {8.961783988750,58.373307022500},
+{12.077733488884,59.207058851501}, {11.920052659158,59.192274138863},
+{16.716927659158,59.489149138863}, {16.562500000000,59.484375000000},
+{20.310484622999,59.298829047426}, {26.432715873807,58.663967188904},
+{32.129439766725,58.064574753283}, {37.437500000000,57.906250000000},
+{42.956231440356,58.377434549954}, {49.359738630422,59.757530970421},
+{55.603687985260,61.808276150558}, {60.600164228395,64.257215243964},
+{61.652720444028,67.227280391337}, {60.695830521433,70.461601332760},
+{59.795804375529,74.612267215650}, {59.682173726259,75.009180115401},
+{58.534870256371,76.967235761415}, {56.827785568165,78.467279120185},
+{54.713289027441,79.427384197713}, {52.343750000000,79.765625000000},
+{45.078251015287,80.284053101740}, {38.408035162743,81.855361533047},
+{32.254051275886,84.503586483055}, {26.537248188239,88.252764140896},
+{26.552738242508,88.240587630490}, {22.301341239703,92.064975088104},
+{19.363615291331,95.777634717946}, {17.659610007921,99.470217646437},
+{17.109375000000,103.234375000000}, {17.462942485289,104.745484506691},
+{18.934901380110,106.053537922625}, {21.052812442968,107.257743120984},
+{22.546875000000,107.593750000000}, {27.127336030849,107.323869026651},
+{31.729951269190,106.505204561027}, {36.405009689533,105.124224284764},
+{41.202800266393,103.167395879495}, {50.256161372519,98.422250099230},
+{57.732466834451,93.253092412692}, {60.876288655813,93.265226007835},
+{72.532538655813,102.765226007835}, {72.279807476907,102.584183376106},
+{75.647218702317,104.376772716335}, {79.199503524210,105.636779903827},
+{83.003238453225,106.380692983432}, {87.125000000000,106.625000000000},
+{94.491626547384,105.043088554999}, {94.463907095460,105.050904922128},
+{100.135795242069,103.121939858482}, {103.519921580364,101.267028609739},
+{103.216286265180,101.548701589947}, {104.200661265180,100.470576589947},
+{106.046875000000,104.656250000000}, {107.893088734820,103.841923410053},
+{55.872490683787,89.763134548637}, {54.997490683787,85.841259548637},
+{55.036476188094,85.993355189713}, {52.343750000000,85.750000000000},
+{46.702141892082,86.180801752650}, {41.285068752391,87.490050907175},
+{36.021375201732,89.703013308437}, {30.839905860908,92.844954801298},
+{30.840393168266,92.844613599752}, {24.841774707763,97.553563991314},
+{23.093750000000,100.109375000000}, {23.668337709691,101.518994849695},
+{23.371174656526,101.224323797050}, {23.620948599883,101.397798979282},
+{24.023420115060,101.552272092059}, {25.484375000000,101.718750000000},
+{29.298199671143,101.348354407082}, {33.545396502623,100.218839979757},
+{38.283160693967,98.302715245167}, {43.568687444697,95.572488730454},
+{43.462444284634,95.635929361188}, {56.978069284634,87.104679361188},
+{55.872490683787,89.763134548637}, {107.893088734820,103.841923410053},
+{59.646930715366,91.332820638812}, {46.131305715366,99.864070638812},
+{46.025062555303,99.927511269546}, {40.294964306033,102.857441004833},
+{34.923353497377,104.984285020243}, {29.967425328857,106.280551842918},
+{25.484375000000,106.718750000000}, {22.429704884940,106.291477907941},
+{20.160075343474,105.056926202950}, {19.862912290309,104.762255150305},
+{18.581942192719,102.559691058502}, {18.093750000000,100.109375000000},
+{18.781885833597,97.630167914546}, {20.751975292237,94.868311008686},
+{27.972106831734,88.749136400248}, {27.972594139092,88.748795198702},
+{33.744249798268,85.287221066563}, {39.777431247609,82.783386592825},
+{46.000983107918,81.262557622350}, {52.343750000000,80.750000000000},
+{54.881053553302,80.995299290410}, {57.121823397686,81.724997675285},
+{58.847249996203,82.929795925088}, {59.838523811906,84.600394810287},
+{59.877509316213,84.752490451363}, {60.752509316213,88.674365451363},
+{59.646930715366,91.332820638812}, {107.893088734820,103.841923410053},
+{155.484375000000,101.859375000000}, {154.564834615754,105.515207938449},
+{152.115670325806,108.366761454651}, {148.955163295626,109.891294781728},
+{145.437500000000,110.437500000000}, {142.960397099416,109.918703000637},
+{140.390467597390,108.438024620128}, {137.806753458193,106.109033784838},
+{135.288296646094,103.045299421136}, {135.165845369106,102.859481271949},
+{127.322095369106,89.718856271949}, {127.315939322101,89.708486304067},
+{123.037165252408,83.240466071649}, {119.422893480005,79.457711309224},
+{123.333764624626,78.181432151290}, {119.923748586047,88.267600564284},
+{116.288451493331,96.371224650234}, {116.310270123205,96.328970526240},
+{111.013619195867,105.085481651574}, {104.498474377631,112.833853093888},
+{102.718750000000,113.578125000000}, {98.109375000000,113.578125000000},
+{95.790533815800,112.012456612681}, {92.715536961613,101.759617602767},
+{90.624767168816,88.808240839597}, {90.625938562396,88.818553777181},
+{88.018863793847,68.357428789163}, {88.022375537533,68.374401202625},
+{86.904794319936,64.234191072834}, {85.466909526601,60.964474099786},
+{83.670654326939,58.467433589062}, {81.477961890361,56.645252846242},
+{80.463845731714,53.674615230649}, {82.823220731714,47.205865230649},
+{82.883587418943,47.055653464335}, {83.966380643129,44.754922491868},
+{85.291281927099,42.535861182972}, {88.514402164445,41.595109434025},
+{91.785870247761,43.817111177877}, {94.599270325866,47.029404143594},
+{96.906293571887,51.130222797294}, {98.658631158949,56.017801605095},
+{98.664492355983,56.039251079522}, {100.006630671722,63.228131068770},
+{101.762541190731,76.272848032637}, {101.765778670251,76.300425005941},
+{103.467098832178,87.353480411923}, {105.697048617918,94.795754781373},
+{102.319980907406,93.545204629259}, {104.354680083199,92.248641487426},
+{106.296883956259,90.334282557687}, {110.081726403105,84.371439341729},
+{110.069085073875,84.396782220394}, {112.413266990492,78.560599894318},
+{114.080111889746,71.934817013191}, {115.075734612596,64.464258680566},
+{115.406250000000,56.093750000000}, {117.906250000000,53.593750000000},
+{122.515625000000,53.593750000000}, {124.596624760838,54.708311442938},
+{155.065374760838,100.473936442938}, {155.484375000000,101.859375000000},
+{107.893088734820,103.841923410053}, {150.903375239162,103.244813557062},
+{120.434625239162,57.479188557062}, {122.515625000000,58.593750000000},
+{117.906250000000,58.593750000000}, {120.406250000000,56.093750000000},
+{120.037546637404,64.922460069434}, {118.935513110254,72.987057986809},
+{117.106264259508,80.232368855682}, {114.555914926125,86.603217779606},
+{114.543273596895,86.628560658271}, {112.308927321184,90.508291033149},
+{109.859366043741,93.751654942313}, {107.239069916801,96.288467887574},
+{104.492519092594,98.048545370741}, {101.115451382082,96.797995218627},
+{98.626651167822,88.552769588077}, {96.796721329749,76.855824994059},
+{96.799958809269,76.883401967363}, {95.087119328278,64.107806431230},
+{93.835507644017,57.335748920478}, {93.841368841051,57.357198394905},
+{92.427690803113,53.221339702706}, {90.736667174134,50.033095856406},
+{88.719989127239,47.690701322123}, {86.329347835555,46.092390565975},
+{89.552468072901,45.151638817028}, {88.346119356871,47.166952508132},
+{87.460162581057,49.069346535665}, {87.520529268286,48.919134769351},
+{85.161154268286,55.387884769351}, {84.147038109639,52.417247153758},
+{87.106689423061,54.909519535937}, {89.579965473399,58.293338400214},
+{91.528799430064,62.470887052166}, {92.915124462467,67.344348797375},
+{92.918636206153,67.361321210837}, {95.592811437604,88.243946222819},
+{95.593982831184,88.254259160403}, {97.565713038387,100.591944897233},
+{100.428216184200,110.143793387319}, {98.109375000000,108.578125000000},
+{102.718750000000,108.578125000000}, {100.939025622369,109.322396906112},
+{106.962943304133,102.180143348427}, {111.845979876795,94.077279473760},
+{111.867798506669,94.035025349766}, {115.287188913953,86.412086935716},
+{118.541235375374,76.756067848710}, {122.452106519995,75.479788690776},
+{126.837834747592,80.025158928351}, {131.621560677899,87.166513695933},
+{131.615404630894,87.156143728051}, {139.459154630894,100.296768728051},
+{139.336703353906,100.110950578864}, {142.906407402610,104.257287879872},
+{145.437500000000,105.437500000000}, {147.478430454374,105.175111468272},
+{149.243704674194,104.273863545349}, {150.251571634246,103.277760811551},
+{150.484375000000,101.859375000000}, {150.903375239162,103.244813557062},
+{107.893088734820,103.841923410053}, {231.343750000000,91.875000000000},
+{230.760913847107,95.964970754466}, {229.068047862611,99.735912943733},
+{226.348615757792,103.075786207286}, {222.686081243924,105.872550184609},
+{222.648873373705,105.894655312846}, {215.093162016193,109.098726800095},
+{205.138412990776,111.389009633945}, {205.099511514392,111.395066324151},
+{196.531910121565,112.292873746127}, {187.093750000000,112.593750000000},
+{180.736641615184,112.156201510495}, {174.578395035074,110.855968371846},
+{168.685194057574,108.711669078852}, {163.123222480588,105.741922126310},
+{163.102786019362,105.728945017668}, {158.065383006950,101.833715472335},
+{154.350564084671,97.489870440862}, {152.052565874896,92.798170193375},
+{151.265625000000,87.859375000000}, {151.531470468770,85.013668760581},
+{152.287360086651,82.060400295498}, {152.321821893194,81.964698849566},
+{155.704155919825,73.828381186220}, {158.270837388345,69.249451319292},
+{158.258242073026,69.265568807584}, {162.161895999987,65.281962610977},
+{167.278829839386,61.794937132641}, {173.532669176810,58.834467171163},
+{180.847039597849,56.430527525129}, {179.144548452656,58.005986305326},
+{180.222673452656,54.974736305326}, {180.222279555972,54.975844608663},
+{182.650011718926,50.400876966176}, {185.678714551395,49.466466465483},
+{192.833058049629,52.996232862321}, {201.831199616962,58.692914409530},
+{211.602112156950,65.789874557518}, {220.243790039283,73.085988470431},
+{220.250361498213,73.092084369790}, {227.553434625305,81.089470457139},
+{229.783449199558,84.667905408782}, {230.987849850362,87.897461316292},
+{231.037603485703,88.222391630780}, {231.334478485703,91.659891630780},
+{231.343750000000,91.875000000000}, {107.893088734820,103.841923410053},
+{226.353021514297,92.090108369220}, {226.056146514297,88.652608369220},
+{226.105900149638,88.977538683708}, {225.181394550443,86.544985216218},
+{223.368440374695,83.730842042861}, {216.843388501787,76.751665630210},
+{216.849959960717,76.757761529569}, {208.538512843050,69.741375442482},
+{199.012550383038,62.822710590470}, {190.370066950371,57.347517137679},
+{183.790035448605,54.096033534517}, {186.818738281074,53.161623033824},
+{184.933970444028,56.649155391337}, {184.933576547344,56.650263694674},
+{183.855451547344,59.681513694674}, {182.152960402151,61.256972474871},
+{175.418502698190,63.429204703837}, {169.900857660614,65.978500367359},
+{165.523650875013,68.934834264023}, {162.210507926974,72.328181192416},
+{162.197912611655,72.344298680708}, {160.116156580175,76.155993813780},
+{156.990678106806,83.754051150434}, {157.025139913349,83.658349704502},
+{156.265625000000,87.859375000000}, {156.814621625104,91.570970431625},
+{158.524435915329,95.017942059138}, {161.489304493050,98.301050152665},
+{165.803463980638,101.521054982332}, {165.783027519412,101.508077873690},
+{170.773790317426,104.198487171148}, {175.945042464926,106.097156628154},
+{181.362967759816,107.222704739505}, {187.093750000000,107.593750000000},
+{196.179027378435,107.308688753873}, {204.369238485608,106.448683675849},
+{204.330337009224,106.454740366055}, {213.484962983807,104.385648199905},
+{220.132376626295,101.574094687154}, {220.095168756076,101.596199815391},
+{222.954118617208,99.523823167714}, {224.892889637389,97.287524556267},
+{225.994945527893,94.775263620534}, {226.343750000000,91.875000000000},
+{226.353021514297,92.090108369220}, {107.893088734820,103.841923410053},
+{219.781250000000,95.890625000000}, {219.378166433508,93.853798730227},
+{218.111752188172,91.489941446073}, {212.645950385807,85.475693519170},
+{212.685981945341,85.511936058310}, {206.545320157918,80.643069684901},
+{198.180220478697,74.962876659750}, {198.191932106006,74.970352872652},
+{191.582535336704,71.037624006410}, {191.504903114611,70.993386859602},
+{190.358726480976,70.329891393482}, {194.093750000000,68.156250000000},
+{193.486799914207,72.326466575586}, {191.750022838432,75.648693620488},
+{191.727672642897,75.674671879974}, {189.796209835616,77.308949639626},
+{187.307360975056,78.497446987452}, {184.374093537693,79.223076311545},
+{181.109375000000,79.468750000000}, {178.539517280969,79.274893727068},
+{176.586499975916,78.676142180991}, {173.086052480141,74.877460550577},
+{176.026873378540,75.790765650642}, {173.637934677835,76.966105971818},
+{171.454775584042,78.628861257931}, {167.479150931130,83.659399177979},
+{167.492959438562,83.636441477986}, {164.707555748674,89.579684053440},
+{164.890625000000,88.640625000000}, {165.429801946489,91.718738201013},
+{167.140367049889,94.413281660069}, {170.161871706101,96.828214751252},
+{174.633867311024,99.067496848641}, {174.595883286589,99.052025896332},
+{183.311717611230,101.570655775842}, {188.036065935963,102.270678900324},
+{192.781250000000,102.500000000000}, {204.722829878151,102.082467436594},
+{212.458857303872,100.833616072622}, {214.905535432122,99.897801735668},
+{216.940228691241,98.604487224303}, {220.087216056537,94.692203098109},
+{219.781250000000,95.890625000000}, {107.893088734820,103.841923410053},
+{224.475283943463,97.089046901891}, {222.495392805158,99.992428606532},
+{220.012896308759,102.426762775697}, {217.106183317878,104.328760764332},
+{213.853642696128,105.635133927378}, {205.324045121849,107.034720063406},
+{192.781250000000,107.500000000000}, {187.549871564037,107.241039849676},
+{182.344532388770,106.476219224158}, {172.747866713411,103.697974103668},
+{172.709882688976,103.682503151359}, {167.310784543899,100.802644623748},
+{163.281507950111,97.266405839931}, {160.761604303511,93.177746173987},
+{160.111277146626,90.958745814619}, {159.890625000000,88.640625000000},
+{160.073694251326,87.701565946560}, {163.194540561438,81.082308522014},
+{163.208349068870,81.059350822021}, {165.573405849355,77.703241627766},
+{168.240536915958,74.886763742069}, {171.153080947165,72.670612778182},
+{174.254376621460,71.115484349358}, {177.195197519859,72.028789449423},
+{178.991625024084,74.292607819009}, {181.109375000000,74.468750000000},
+{185.450451524944,73.971303012548}, {187.959827357103,72.387828120026},
+{187.937477161568,72.413806379512}, {188.825700085793,70.736033424413},
+{189.093750000000,68.156250000000}, {192.828773519024,65.982608606518},
+{194.057596885389,66.694113140398}, {193.979964663296,66.649875993590},
+{200.870567893994,70.748397127348}, {200.882279521303,70.755873340250},
+{209.532804842082,76.638180315099}, {216.001518054659,81.769313941690},
+{216.041549614192,81.805556480830}, {219.736550554799,85.597619431325},
+{222.481997811828,89.260058553927}, {224.192146066492,92.716513769773},
+{224.781250000000,95.890625000000}, {224.475283943463,97.089046901891},
+{107.893088734820,103.841923410053}, {274.375000000000,74.921875000000},
+{274.375000000000,76.000000000000}, {273.941340954992,77.877573018672},
+{272.878932097861,79.515803527315}, {273.201644884750,79.109789854043},
+{272.240878131027,80.634530144823}, {272.178064435979,80.731878545843},
+{270.366672394243,82.164949472839}, {268.046875000000,82.703125000000},
+{266.183148747233,82.484226579792}, {265.676843902484,82.347511014208},
+{261.919943241226,81.056638412991}, {261.845529920936,81.035610157726},
+{257.771892015055,80.000491652766}, {254.812500000000,79.671875000000},
+{253.064641952966,80.017766952966}, {253.029646790629,80.052082662255},
+{253.183319188544,79.921363850038}, {253.251518993920,79.984611568497},
+{253.185946392942,81.106825858762}, {253.000000000000,86.578125000000},
+{253.496255380785,102.128843883245}, {253.498940253820,102.192840332413},
+{254.068093288267,110.651263719025}, {251.578125000000,113.375000000000},
+{246.875000000000,113.375000000000}, {246.340326851360,113.317155732979},
+{244.239667843345,112.409341061474}, {241.449240399376,110.489665586259},
+{233.966932708158,103.861287459859}, {233.964253154933,103.858697290509},
+{230.739570245917,99.429986748935}, {227.863465886694,92.900734302334},
+{227.865566738278,92.906614054351}, {224.904456445713,83.297192679644},
+{223.890625000000,76.390625000000}, {224.718172891760,73.652424627265},
+{226.991950628026,71.671512863124}, {230.398659300280,70.468125917422},
+{234.625000000000,70.062500000000}, {239.436000436064,69.828681464349},
+{240.453125000000,69.140625000000}, {239.621272369169,63.470278877568},
+{237.983036171748,55.833595299706}, {237.978023315197,55.813489579321},
+{235.265625000000,42.968750000000}, {235.414374888933,43.818233911095},
+{223.164374888933,9.911983911095}, {223.454623060848,7.647483743269},
+{225.610873060848,4.506858743269}, {225.803351703291,4.260965402925},
+{227.719534150475,2.932735689909}, {230.078782966130,2.347671229322},
+{230.218750000000,2.343750000000}, {231.580832848366,2.789728316683},
+{232.871346377781,3.980786060276}, {235.131457239225,7.716876589460},
+{240.051188613236,19.745668022304}, {244.970104457946,33.805503176211},
+{247.762510801176,44.143184476801}, {247.777764624530,44.225329525617},
+{252.184014624530,70.381579525617}, {251.352799675865,68.904817480577},
+{254.575226795957,69.673348314939}, {260.406250000000,70.359375000000},
+{270.523910039912,70.690857394210}, {273.186407177161,72.174765791999},
+{274.049624252688,73.457293811137}, {274.375000000000,74.921875000000},
+{107.893088734820,103.841923410053}, {269.375000000000,74.921875000000},
+{269.694839960088,75.621642605790}, {260.406250000000,75.359375000000},
+{253.596648204043,74.576651685061}, {250.197596782742,73.718304003256},
+{248.084700324135,72.688932519423}, {247.253485375470,71.212170474383},
+{242.847235375470,45.055920474383}, {242.862489198824,45.138065523199},
+{240.182239292054,35.233559323790}, {235.370686386764,21.504331977696},
+{230.614636510775,9.861248410540}, {229.674552059719,7.679370189724},
+{229.774391761009,7.390935745817}, {230.218750000000,7.343750000000},
+{230.358717033870,7.339828770678}, {229.540398296709,7.582784597075},
+{229.732876939152,7.336891256731}, {227.576626939152,10.477516256731},
+{227.866875111067,8.213016088905}, {240.116875111067,42.119266088905},
+{240.265625000000,42.968750000000}, {242.834476684803,54.624010420679},
+{242.829463828252,54.603904700294}, {244.534977630831,62.545346122432},
+{245.453125000000,69.140625000000}, {244.611402180226,71.735213821357},
+{242.306187063936,73.585381035651}, {238.867409665678,74.693638982119},
+{234.625000000000,75.062500000000}, {229.906486871974,75.367549636876},
+{229.092373983240,75.779215997735}, {228.934535185216,76.058687905577},
+{228.890625000000,76.390625000000}, {229.790856054287,82.319994820356},
+{232.571933261722,91.218385945649}, {232.574034113306,91.224265697666},
+{234.986992254083,96.851263251065}, {237.441996845067,100.266302709491},
+{237.439317291842,100.263712540141}, {244.074197100624,106.275959413741},
+{247.409673148640,108.432844267021}, {246.875000000000,108.375000000000},
+{251.578125000000,108.375000000000}, {249.088156711733,111.098736280975},
+{248.501059746180,102.338409667587}, {248.503744619215,102.402406116755},
+{248.000000000000,86.578125000000}, {248.204678607058,80.674424141238},
+{248.635199756080,78.226325931503}, {249.564103209371,76.447917337745},
+{249.529108047034,76.482233047034}, {251.855421271850,75.142541341903},
+{254.812500000000,74.671875000000}, {258.571857984945,75.077633347234},
+{263.279470079064,76.245639842274}, {263.205056758774,76.224611587009},
+{267.479406097516,77.683738985792}, {266.973101252767,77.547023420208},
+{268.046875000000,77.703125000000}, {268.247054721136,77.733964370993},
+{268.227077605757,77.803800527161}, {268.040685564021,77.924371454157},
+{267.977871868973,78.021719855177}, {268.985855115250,76.421460145957},
+{269.308567902139,76.015446472685}, {269.418034045008,75.973989481328},
+{269.375000000000,76.000000000000}, {269.375000000000,74.921875000000},
+{107.893088734820,103.841923410053}, {243.911209450369,105.662056821622},
+{240.832004992147,102.845101845416}, {236.815516984936,97.742848726260},
+{236.813922800164,97.740633958664}, {232.773597278734,91.287437871638},
+{230.710552894044,85.924483215760}, {230.745327869205,86.067653416413},
+{229.840275149638,82.415038683708}, {229.855893749637,82.481339062591},
+{229.093750000000,78.546875000000}, {229.510257350732,77.264688549054},
+{230.557731326090,76.286764971873}, {233.333290689663,75.138571514324},
+{238.843750000000,74.468750000000}, {241.980836948934,74.687319511759},
+{245.189547085049,75.561169162745}, {247.167986856352,77.549353232692},
+{248.217702574415,80.638507879225}, {248.687500000000,88.140625000000},
+{248.194671228610,98.736670034806}, {248.200295344311,98.650162873539},
+{247.698246960594,103.850772804200}, {243.911209450369,105.662056821622},
+{107.893088734820,103.841923410053}, {242.739253039406,103.211727195800},
+{243.205954655689,98.412337126461}, {243.211578771390,98.325829965194},
+{243.687500000000,88.140625000000}, {243.282297425585,81.439617120775},
+{243.185452914951,80.141955837255}, {241.300413051066,79.640805488241},
+{238.843750000000,79.468750000000}, {234.557334310337,79.986428485676},
+{234.323615830224,80.043375869145}, {234.139295619945,80.082650511165},
+{233.899908001681,80.106538837663}, {233.805549923910,79.924172528127},
+{234.093750000000,78.546875000000}, {234.706606250363,81.268660937409},
+{234.722224850362,81.334961316292}, {235.567172130795,84.744846583587},
+{235.601947105956,84.888016784240}, {237.273277721266,89.165687128362},
+{240.873577199836,94.821866041336}, {240.871983015064,94.819651273740},
+{244.355495007853,99.326773154584}, {246.526290549631,101.400443178378},
+{242.739253039406,103.211727195800}, {107.893088734820,103.841923410053},
+{304.579027849556,115.887559254375}, {299.647333003501,118.498603127587},
+{299.601234810263,118.526555579345}, {296.960609810263,120.089055579345},
+{295.687500000000,120.437500000000}, {292.434178516679,119.763451801101},
+{289.591538470777,117.850158805700}, {287.263626957927,114.860898415742},
+{285.554491073762,110.958948033170}, {285.546168760441,110.932189402305},
+{283.409425227272,100.413450166388}, {283.408069329655,100.405281467271},
+{280.968147929764,87.644193704042}, {284.218299056292,87.914012723915},
+{279.519746335556,93.766171451312}, {279.798545332977,93.224824860858},
+{274.937093489612,105.343910553875}, {272.454127837569,109.762553039486},
+{269.772550129691,112.314814767144}, {266.294988321147,110.999865652887},
+{264.699764075525,105.167535274279}, {264.676753039406,105.022647804200},
+{264.221109604112,100.702343060691}, {264.171875000000,98.828125000000},
+{263.607902205199,91.869081828698}, {263.613915250645,91.062865437610},
+{263.781250000000,89.718750000000}, {263.644962649638,88.985351183708},
+{262.873500431741,86.703985451358}, {262.739182179139,86.219942105230},
+{260.786057179139,74.844942105230}, {260.786249814804,74.846062487964},
+{256.958124814804,52.611687487964}, {256.955473276820,52.595988114755},
+{254.892973276820,40.142863114755}, {254.909488793847,40.232428789163},
+{254.331363793847,37.388678789163}, {254.339079247810,37.425229542768},
+{253.651579247810,34.284604542768}, {253.593750000000,33.750000000000},
+{254.239654341050,30.319069019177}, {254.154681406546,30.589185956325},
+{256.319133024351,26.771372253672}, {258.818704827809,25.924919245868},
+{261.217531024880,26.988383072536}, {263.253153025566,28.888095452080},
+{264.856582863559,31.480336604342}, {265.958832572550,34.621386749163},
+{266.013105788729,34.918097636045}, {267.200714432551,44.731495841479},
+{267.198350954149,44.712535224402}, {269.443623734153,61.037071609407},
+{269.421396014347,60.906344576785}, {270.652021489737,67.736566992690},
+{271.992261513603,76.440263813345}, {271.992747064473,76.443809223415},
+{273.038636247453,85.686082548185}, {273.390625000000,92.953125000000},
+{273.238407027301,96.898129287691}, {272.771685367998,99.590064317759},
+{267.989237947004,98.132898258783}, {271.223612947004,86.961023258783},
+{271.391159168632,86.533772766328}, {274.526090303798,81.379570646076},
+{274.584318175231,81.310286998919}, {276.982682022500,78.600716011250},
+{277.142029400154,78.326905342657}, {279.265919333198,75.301911531816},
+{279.298622345235,75.258362091268}, {281.876153531632,73.009942282517},
+{284.906250000000,72.125000000000}, {287.318073492888,72.767987310135},
+{289.339116026344,74.544184270464}, {290.903803647217,77.224443425871},
+{291.946562402359,80.579617321243}, {291.963278527291,80.666832095730},
+{293.477578855570,88.491377103079}, {294.532438780850,92.026218792971},
+{294.690170210143,92.371639700252}, {298.832669585147,102.687317191341},
+{298.821447919079,102.663322107333}, {301.483679194839,107.248880594573},
+{302.541048238599,108.202972500861}, {303.515625000000,108.484375000000},
+{306.015625000000,110.984375000000}, {306.015625000000,113.625000000000},
+{304.579027849556,115.887559254375}, {107.893088734820,103.841923410053},
+{301.015625000000,113.625000000000}, {301.015625000000,110.984375000000},
+{303.515625000000,113.484375000000}, {300.984342386401,112.859527499139},
+{298.555383305161,111.094869405427}, {296.303526841571,108.355227332168},
+{294.303552080921,104.805427892667}, {294.292330414853,104.781432808659},
+{289.997329789857,94.097110299748}, {290.155061219150,94.442531207029},
+{288.709921144430,89.953935396921}, {287.036721472709,81.520667904270},
+{287.053437597641,81.607882678757}, {286.004633973656,77.940190729536},
+{284.906250000000,77.125000000000}, {284.500063101353,77.160595459408},
+{284.155096468369,77.318182717483}, {283.263877654765,78.304137908732},
+{283.296580666802,78.260588468184}, {281.295470599846,81.110594657343},
+{281.454817977500,80.836783988750}, {278.353181824769,84.595963001081},
+{278.411409696202,84.526679353924}, {275.858840831368,88.778727233672},
+{276.026387052996,88.351476741217}, {272.792012052996,99.523351741217},
+{268.009564632002,98.066185682241}, {268.390625000000,92.953125000000},
+{268.055113752547,86.056104951815}, {267.038502935527,77.118690776585},
+{267.038988486397,77.122236186655}, {265.722978510263,68.575933007310},
+{264.516103985653,61.874905423215}, {264.493876265847,61.744178390593},
+{262.239149045851,45.349964775598}, {262.236785567449,45.331004158521},
+{261.049394211271,35.519402363955}, {261.103667427450,35.816113250837},
+{259.699971974434,32.150967047920}, {257.681295172191,30.793830754132},
+{260.180866975649,29.947377746328}, {259.001568593454,31.817064043675},
+{258.916595658950,32.087180980823}, {258.593750000000,33.750000000000},
+{258.535920752190,33.215395457232}, {259.223420752190,36.356020457232},
+{259.231136206153,36.392571210837}, {259.809261206153,39.236321210837},
+{259.825776723180,39.325886885245}, {261.888276723180,51.779011885245},
+{261.885625185196,51.763312512036}, {265.713750185196,73.997687512036},
+{265.713942820861,73.998807894770}, {267.667067820861,85.373807894770},
+{267.532749568259,84.889764548642}, {268.526912350362,87.905273816292},
+{268.781250000000,89.718750000000}, {268.542334749355,91.905884562390},
+{268.548347794801,91.099668171302}, {269.171875000000,98.828125000000},
+{269.216390395888,100.485156939309}, {269.635746960594,104.383602195800},
+{269.612735924475,104.238714725721}, {270.955011678853,109.187634347113},
+{267.477449870309,107.872685232856}, {268.686497162431,106.569478210514},
+{270.375406510388,103.296714446125}, {275.107704667023,91.493925139142},
+{275.386503664444,90.952578548688}, {278.700840527029,86.470174071108},
+{281.281700943708,83.867237276085}, {284.531852070236,84.137056295958},
+{285.408419686884,85.805066273498}, {286.324716189650,88.997159604327},
+{288.341930670345,99.594718532729}, {288.340574772728,99.586549833612},
+{290.328831239559,109.474060597695}, {290.320508926238,109.447301966830},
+{291.419966792073,112.312929709258}, {292.611586529223,114.157653694300},
+{293.999415233321,115.144751323899}, {295.687500000000,115.437500000000},
+{294.414390189737,115.785944420655}, {297.055015189737,114.223444420655},
+{297.008916996499,114.251396872413}, {302.452222150444,111.362440745625},
+{301.015625000000,113.625000000000}, {107.893088734820,103.841923410053},
+{374.437500000000,106.765625000000}, {373.775309600600,109.463093710969},
+{372.064906958700,112.117295697199}, {372.041004786215,112.144602045402},
+{368.522704503395,114.706400007155}, {366.442611342446,115.399541345014},
+{364.281250000000,115.640625000000}, {355.911205219123,114.726652651698},
+{341.052591424867,111.966274658224}, {341.038040421249,111.963294783992},
+{326.905309277070,108.757693347762}, {319.727026481732,106.439595965247},
+{319.807037817648,106.479160754059}, {310.484453831953,101.696158824691},
+{304.482826587959,97.721718623918}, {304.310358047034,97.564641952966},
+{302.095819571999,95.254869206705}, {298.220153609739,90.667578419636},
+{300.367638895226,91.618501549714}, {296.258263895226,91.915376549714},
+{296.078125000000,91.921875000000}, {293.025957236218,91.709450913344},
+{290.151865257421,91.083888117023}, {285.254658796565,88.663603177256},
+{285.219422230119,88.636374171395}, {281.529900243089,84.536170132203},
+{280.513144708967,82.282593311151}, {280.156250000000,80.015625000000},
+{280.896516775664,77.119922391977}, {282.806537684315,74.726645730656},
+{285.448073147177,73.263018105533}, {288.686079158715,72.443690273534},
+{290.750793112766,73.122099563886}, {293.797668112766,76.059599563886},
+{293.736465601749,76.002539206463}, {295.798965601749,77.861914206463},
+{294.222063773555,77.220634985862}, {301.862688773555,77.517509985862},
+{299.265625000000,80.015625000000}, {299.934685624955,74.754308742711},
+{301.898213943218,69.792099871114}, {305.090729619888,65.230982029954},
+{309.446752320064,61.172938863978}, {309.482902906976,61.145713152252},
+{318.065904047846,56.213131631660}, {329.455658353400,51.953267244508},
+{329.496351456999,51.941186017188}, {336.615241964605,50.572965088062},
+{336.666137267666,50.568817238037}, {342.150512267666,50.178192238037},
+{342.328125000000,50.171875000000}, {347.088673798588,50.461178987176},
+{351.635436696472,51.319590054257}, {355.904450990616,52.732856859568},
+{359.831753977986,54.686728061438}, {363.292951645549,57.250115143707},
+{365.880587968213,60.272932122861}, {367.501493801268,63.647371305944},
+{368.062500000000,67.265625000000}, {367.659428864193,69.524372763002},
+{366.507997055207,71.901249483478}, {362.306741008310,76.690232523947},
+{362.309449580203,76.687848273793}, {355.067416613929,81.592460070084},
+{345.579434368324,85.619156329617}, {345.543296906352,85.631025936969},
+{338.856518222681,87.434049611292}, {331.674077585810,88.726763309129},
+{324.032609882622,89.505258086643}, {315.968750000000,89.765625000000},
+{315.697255981477,89.750839477245}, {313.837880981477,89.547714477245},
+{314.109375000000,89.562500000000}, {315.203325765838,88.142787621838},
+{315.234375000000,87.750000000000}, {315.261368553241,87.459370280378},
+{315.416652198286,87.453346686481}, {316.557907675735,88.522399326740},
+{316.429841220906,88.403882310796}, {319.273591220906,90.857007310796},
+{318.935343465338,90.611377991437}, {331.091593465338,97.970752991437},
+{330.612118984109,97.746034726917}, {334.956642245028,98.975789357391},
+{339.851927078584,99.850564497063}, {351.453125000000,100.546875000000},
+{354.203872313810,100.381670729150}, {355.159915151904,99.886038066767},
+{355.055436157189,100.055596900919}, {357.900435727416,97.290434866877},
+{362.379803562479,95.425955323112}, {363.015625000000,95.343750000000},
+{366.828125000000,95.343750000000}, {368.997562855593,96.601357392681},
+{374.106937855593,105.523232392681}, {374.437500000000,106.765625000000},
+{107.893088734820,103.841923410053}, {369.768062144407,108.008017607319},
+{364.658687144407,99.086142607319}, {366.828125000000,100.343750000000},
+{363.015625000000,100.343750000000}, {363.651446437521,100.261544676888},
+{360.724564272584,101.303315133123}, {359.413313842811,102.506903099081},
+{359.308834848096,102.676461933233}, {358.021570604282,103.932262067178},
+{356.186752686190,104.829266770850}, {351.453125000000,105.546875000000},
+{345.006190631125,105.353277680068}, {339.085572921416,104.774435502937},
+{333.730857754972,103.813273142609}, {328.981631015891,102.472715273083},
+{328.502156534662,102.247997008563}, {316.345906534662,94.888622008563},
+{316.007658779094,94.642992689204}, {313.163908779094,92.189867689204},
+{313.035842324265,92.071350673260}, {311.083347801714,89.820090813519},
+{310.234375000000,87.750000000000}, {310.265424234162,87.357212378162},
+{310.780311953087,86.191927459145}, {311.752715552710,85.312201869144},
+{314.109375000000,84.562500000000}, {314.380869018523,84.577285522755},
+{316.240244018523,84.780410522755}, {315.968750000000,84.765625000000},
+{323.705671367377,84.524038788357}, {330.935297414190,83.796674190871},
+{337.694263027319,82.579622263708}, {344.019203093648,80.868974063031},
+{343.983065631676,80.880843670383}, {352.635708386071,77.251289929916},
+{359.003050419797,72.937151726207}, {359.005758991690,72.934767476053},
+{362.163877944793,69.637813016522}, {363.062500000000,67.265625000000},
+{362.747529636232,64.880949006556}, {361.740505781787,62.777849127139},
+{359.948259291951,60.848517668793}, {357.277621022014,58.985146938562},
+{353.920744321884,57.295463452932}, {350.352844553528,56.106191195743},
+{346.509959013911,55.403078825324}, {342.328125000000,55.171875000000},
+{342.505737732334,55.165557761963}, {337.021362732334,55.556182761963},
+{337.072258035395,55.552034911938}, {330.878648543001,56.746313982812},
+{330.919341646600,56.734232755492}, {320.246595952154,60.693118368340},
+{312.454597093024,65.166786847748}, {312.490747679936,65.139561136022},
+{308.794036005112,68.485814845046}, {306.234598556782,72.012587628886},
+{304.746955000045,75.821863132289}, {304.265625000000,80.015625000000},
+{301.668561226445,82.513740014138}, {294.027936226445,82.216865014138},
+{292.451034398251,81.575585793537}, {290.388534398251,79.716210793537},
+{290.327331887234,79.659150436114}, {287.280456887234,76.721650436114},
+{289.345170841285,77.400059726466}, {287.321458102823,77.850263144467},
+{285.927837315685,78.632729269344}, {285.271451974336,79.274608858023},
+{285.175338636761,79.581613557521}, {285.156250000000,80.015625000000},
+{285.837287256911,82.245079867797}, {288.311827769881,84.707375828605},
+{288.276591203435,84.680146822744}, {291.660634742579,86.384861882977},
+{296.078125000000,86.921875000000}, {295.897986104774,86.928373450286},
+{300.007361104774,86.631498450286}, {302.154846390261,87.582421580364},
+{305.732305428001,91.823255793295}, {307.845891952966,94.029108047034},
+{307.673423412041,93.872031376082}, {313.085858668047,97.436653675309},
+{321.942962182352,101.958339245941}, {322.022973518268,101.997904034753},
+{328.415003222930,104.000119152238}, {342.055709578751,107.067955216008},
+{342.041158575133,107.064975341776}, {356.698169780877,109.788972348302},
+{364.281250000000,110.640625000000}, {366.516357996605,110.246724992845},
+{368.302745213785,108.824147954598}, {368.278843041300,108.851454302801},
+{369.437500000000,106.765625000000}, {369.768062144407,108.008017607319},
+{107.893088734820,103.841923410053}, {359.034454681034,69.861443602839},
+{357.173548986071,72.638610976591}, {353.435826646756,75.215386191513},
+{347.992418306797,77.582163291606}, {341.014454609897,79.729336320871},
+{326.426284670840,82.761098429090}, {319.640717587115,83.525315463381},
+{313.234375000000,83.781250000000}, {312.706760041223,83.724940335389},
+{311.331760041223,83.428065335389}, {311.389194323030,83.439762979730},
+{309.920444323030,83.158512979730}, {310.390625000000,83.203125000000},
+{309.032298883605,83.610801187634}, {305.860259182280,80.222501196670},
+{308.487654632717,75.272571710106}, {311.451975717975,70.927537861140},
+{314.718319876723,67.239682598354}, {318.251784547635,64.261288870324},
+{318.235984828743,64.272646821685}, {322.460311501394,61.735717754517},
+{327.134482463160,59.899177687483}, {332.183220900533,58.782772982128},
+{337.531250000000,58.406250000000}, {345.475041381297,58.786142249068},
+{351.020038715603,59.929523334009}, {351.130689531816,59.972424607386},
+{353.699762084814,61.379594420291}, {355.915109055748,63.288105475323},
+{357.703808635445,65.619192847673}, {358.992939014732,68.294091612533},
+{359.034454681034,69.861443602839}, {107.893088734820,103.841923410053},
+{354.288310985268,69.987158387467}, {352.366140944252,66.657207024677},
+{349.213060468184,64.590075392614}, {349.323711284397,64.632976665991},
+{344.735896118703,63.713857750932}, {337.531250000000,63.406250000000},
+{332.857794724467,63.703555142872}, {328.623330036840,64.608634812517},
+{324.752579123606,66.141235370483}, {321.170265171257,68.321103178315},
+{321.154465452365,68.332461129676}, {318.096133248277,70.895083026646},
+{315.305836782025,74.049024638860}, {312.748673492282,77.846568914895},
+{310.389740817720,82.339998803330}, {307.217701116395,78.951698812366},
+{310.390625000000,78.203125000000}, {310.860805676970,78.247737020270},
+{312.329555676970,78.528987020270}, {312.386989958777,78.540684664611},
+{313.761989958777,78.837559664611}, {313.234375000000,78.781250000000},
+{319.326079287885,78.541090786619}, {325.753402829160,77.817026570910},
+{339.735545390103,74.895663679129}, {350.446985853244,71.354926308487},
+{353.125279138929,69.816467148409}, {354.246795318966,68.419806397161},
+{354.288310985268,69.987158387467}, {107.893088734820,103.841923410053},
+{412.906150029990,61.293260146951}, {409.761470127352,62.134684085464},
+{407.280172710472,63.259435125605}, {407.285792255081,63.256184676520},
+{404.793374271599,64.364965077547}, {402.406250000000,64.781250000000},
+{400.799037804796,64.455489160260}, {399.281565012266,63.565560595750},
+{396.896451926356,60.617324020679}, {395.843750000000,59.187500000000},
+{394.132759007664,59.586356110398}, {392.227223295325,60.862510968959},
+{387.514233459670,66.524236094764}, {387.523930329881,66.510005317299},
+{384.972470502350,70.813073903396}, {383.182220364063,75.072391356081},
+{382.127155126215,79.356608210051}, {381.781250000000,83.734375000000},
+{382.036891605602,86.926476407739}, {382.837535469594,90.232985146136},
+{382.953125000000,90.984375000000}, {382.953125000000,110.781250000000},
+{380.453125000000,113.281250000000}, {374.875000000000,113.281250000000},
+{372.797350977108,112.469290573921}, {370.771127450416,110.197059087430},
+{366.940746762890,102.253660683699}, {366.931766441279,102.229357657717},
+{364.988344509233,95.969248633492}, {363.592635453443,89.400212341223},
+{362.750737781251,82.563409292771}, {362.468750000000,75.500000000000},
+{362.469585639102,75.564633561058}, {362.375835639102,71.939633561058},
+{362.376194806642,71.952282634846}, {362.282444806642,68.921032634846},
+{362.281250000000,68.843750000000}, {362.557974048920,65.558176179264},
+{363.371209408870,62.925233614273}, {365.078767647837,60.585701488502},
+{367.830147985199,58.062445160283}, {369.390625000000,57.515625000000},
+{372.609375000000,57.515625000000}, {375.044088155839,59.448023159988},
+{375.637838155839,61.994898159988}, {375.701153736333,62.463240580013},
+{375.794903736333,64.822615580013}, {375.796875000000,64.921875000000},
+{375.796875000000,66.390625000000}, {375.727843254951,66.974057381188},
+{375.364882971473,68.609156073692}, {375.377354243352,68.535253718305},
+{374.986729243352,71.082128718305}, {375.015625000000,70.703125000000},
+{375.946084438562,73.051058522014}, {371.472688855144,73.407169646001},
+{374.338903268299,66.961197216653}, {377.510670879106,61.308691210684},
+{380.962283852915,56.499192859154}, {384.668034355078,52.582243393122},
+{384.668120639869,52.582165462116}, {388.032154904598,50.002880989291},
+{391.626628158728,48.110057708285}, {395.367786243461,46.944465688666},
+{399.171875000000,46.546875000000}, {400.072873198005,46.714879663986},
+{404.885373198005,48.574254663986}, {404.871260445955,48.568850974212},
+{412.883679720929,50.905386234424}, {414.906250000000,53.359375000000},
+{414.906250000000,58.843750000000}, {412.906150029990,61.293260146951},
+{107.893088734820,103.841923410053}, {409.906250000000,58.843750000000},
+{409.906250000000,53.359375000000}, {411.928820279071,55.813363765576},
+{403.097489554045,53.243649025788}, {403.083376801995,53.238245336014},
+{398.270876801995,51.378870336014}, {399.171875000000,51.546875000000},
+{396.227916881538,51.823112436334}, {393.443684341272,52.679004791715},
+{390.735423220402,54.155322135709}, {388.019379360131,56.292834537884},
+{388.019465644922,56.292756606878}, {384.707638022085,59.784010265846},
+{381.637766620894,64.074121289316}, {378.784143606701,69.212630908347},
+{376.121061144856,75.249080353999}, {371.647665561438,75.605191477986},
+{370.464152438185,73.063686657060}, {370.015625000000,70.703125000000},
+{370.044520756648,70.324121281695}, {370.435145756648,67.777246281695},
+{370.447617028527,67.703343926308}, {370.865906745049,65.807192618812},
+{370.796875000000,66.390625000000}, {370.796875000000,64.921875000000},
+{370.798846263667,65.021134419987}, {370.705096263667,62.661759419987},
+{370.768411844161,63.130101840012}, {370.174661844161,60.583226840012},
+{372.609375000000,62.515625000000}, {369.390625000000,62.515625000000},
+{370.951102014801,61.968804839717}, {367.847540591130,65.152891385727},
+{367.281250000000,68.843750000000}, {367.280055193358,68.766467365154},
+{367.373805193358,71.797717365154}, {367.374164360898,71.810366438942},
+{367.467914360898,75.435366438942}, {367.468750000000,75.500000000000},
+{367.725824718749,82.182684457229}, {368.501114546557,88.552912658777},
+{369.800717990767,94.651845116508}, {371.630733558721,100.520642342283},
+{371.621753237110,100.496339316301}, {374.875000000000,108.281250000000},
+{380.453125000000,108.281250000000}, {377.953125000000,110.781250000000},
+{377.953125000000,90.984375000000}, {378.068714530406,91.735764853864},
+{377.111545894398,87.698523592261}, {376.781250000000,83.734375000000},
+{377.206829248785,78.659016789949}, {378.466217135937,73.583858643919},
+{380.533388872650,68.577551096604}, {383.382319670119,63.708744682701},
+{383.392016540330,63.694513905236}, {386.493443824758,59.714265008269},
+{389.655589204675,56.723426531041}, {392.798881617336,54.841378264602},
+{395.843750000000,54.187500000000}, {397.450962195204,54.513260839740},
+{398.968434987734,55.403189404250}, {401.353548073644,58.351425979321},
+{402.406250000000,59.781250000000}, {404.776707744919,58.931315323480},
+{404.782327289528,58.928064874395}, {408.035404872648,57.459065914536},
+{411.906349970010,56.394239853049}, {409.906250000000,58.843750000000},
+{107.893088734820, 103.841923410053}
+};
diff --git a/perf/micro/zrusin.c b/perf/micro/zrusin.c
new file mode 100644 (file)
index 0000000..7d8b004
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * Copyright © 2006 Red Hat, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ */
+
+#include "cairo-perf.h"
+
+typedef struct {
+    double x;
+    double y;
+} point_t;
+
+#include "zrusin-another.h"
+
+#define ARRAY_SIZE(arr) sizeof(arr)/sizeof(arr[0])
+
+static void
+zrusin_another_path (cairo_t *cr)
+{
+    unsigned int i;
+
+    for (i=0; i < ARRAY_SIZE (zrusin_another); i++)
+       cairo_line_to (cr, zrusin_another[i].x, zrusin_another[i].y);
+}
+
+static cairo_time_t
+zrusin_another_tessellate (cairo_t *cr, int width, int height, int loops)
+{
+    zrusin_another_path (cr);
+
+    cairo_perf_timer_start ();
+
+    /* We'd like to measure just tessellation without
+     * rasterization. For now, we can do that with cairo_in_fill. But
+     * we'll have to be careful since cairo_in_fill might eventually
+     * be optimized to have an implementation that doesn't necessarily
+     * include tessellation. */
+    while (loops--)
+       cairo_in_fill (cr, 50, 50);
+
+    cairo_perf_timer_stop ();
+
+    cairo_new_path (cr);
+
+    return cairo_perf_timer_elapsed ();
+}
+
+static cairo_time_t
+zrusin_another_fill (cairo_t *cr, int width, int height, int loops)
+{
+    zrusin_another_path (cr);
+    cairo_set_source_rgb (cr, 0.0, 0.0, 0.8); /* blue */
+
+    cairo_perf_timer_start ();
+
+    while (loops--)
+       cairo_fill_preserve (cr);
+
+    cairo_perf_timer_stop ();
+
+    cairo_new_path (cr);
+
+    return cairo_perf_timer_elapsed ();
+}
+
+cairo_bool_t
+zrusin_enabled (cairo_perf_t *perf)
+{
+    return cairo_perf_can_run (perf, "zrusin", NULL);
+}
+
+void
+zrusin (cairo_perf_t *perf, cairo_t *cr, int width, int height)
+{
+
+    cairo_perf_run (perf, "zrusin-another-tessellate", zrusin_another_tessellate, NULL);
+    cairo_perf_run (perf, "zrusin-another-fill", zrusin_another_fill, NULL);
+}
diff --git a/src/.gitignore b/src/.gitignore
new file mode 100644 (file)
index 0000000..fd53c86
--- /dev/null
@@ -0,0 +1,33 @@
+.deps
+.libs
+Makefile
+Makefile.in
+Makefile.am.features
+#Makefile.win32.features
+*.gcda
+*.gcno
+*.la
+*.lo
+*.loT
+*.pc
+cairo-features.h
+cairo-supported-features.h
+cairo.def
+*.i
+*.s
+*.o
+*.obj
+*.pdb
+*.dll
+*.manifest
+*.ilk
+*.exp
+*.lib
+*~
+.*.sw?
+TAGS
+tags
+check-has-hidden-symbols.i
+check-link
+check-skiplist
+headers-standalone
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644 (file)
index 0000000..ef01abb
--- /dev/null
@@ -0,0 +1,119 @@
+# Note: All source files are listed in Makefile.sources.
+
+include $(top_srcdir)/build/Makefile.am.common
+include $(srcdir)/Makefile.am.features
+
+EXTRA_DIST += Makefile.win32      Makefile.win32.features
+#MAINTAINERCLEANFILES += $(srcdir)/Makefile.win32.features
+
+AM_CPPFLAGS = -I$(srcdir) $(CAIRO_CFLAGS)
+AM_LDFLAGS = $(CAIRO_LDFLAGS)
+
+if OS_WIN32
+export_symbols = -export-symbols cairo.def
+cairo_def_dependency = cairo.def
+endif
+
+$(top_builddir)/config.h: $(top_srcdir)/config.h.in
+       cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) config.h
+
+cairoincludedir = $(includedir)/cairo
+cairoinclude_HEADERS = $(enabled_cairo_headers)
+
+lib_LTLIBRARIES = libcairo.la
+
+if BUILD_CXX
+cairo_cxx_lib = libcairo_cxx.la
+else
+cairo_cxx_lib =
+endif
+
+noinst_LTLIBRARIES = $(cairo_cxx_lib)
+libcairo_cxx_la_SOURCES = \
+       $(enabled_cairo_headers) \
+       $(enabled_cairo_private) \
+       $(enabled_cairo_cxx_sources) \
+       $(NULL)
+libcairo_cxx_la_LDFLAGS = $(AM_LDFLAGS) $(export_symbols)
+libcairo_cxx_la_LIBADD = $(CAIRO_LIBS)
+libcairo_cxx_la_DEPENDENCIES = $(cairo_def_dependency)
+
+
+libcairo_la_SOURCES = \
+       $(enabled_cairo_headers) \
+       $(enabled_cairo_private) \
+       $(enabled_cairo_sources) \
+       $(NULL)
+libcairo_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(CAIRO_LIBTOOL_VERSION_INFO) -no-undefined $(export_symbols)
+libcairo_la_LIBADD = $(CAIRO_LIBS) \
+       $(cairo_cxx_lib)
+libcairo_la_DEPENDENCIES = $(cairo_def_dependency) $(cairo_cxx_lib)
+
+# Special headers
+cairoinclude_HEADERS += $(top_srcdir)/cairo-version.h
+libcairo_la_SOURCES += cairo-version.h
+nodist_cairoinclude_HEADERS = cairo-features.h
+nodist_libcairo_la_SOURCES  = cairo-features.h
+BUILT_SOURCES  += cairo-features.h cairo-supported-features.h
+DISTCLEANFILES += cairo-features.h cairo-supported-features.h
+cairo-features.h cairo-supported-features.h:
+       cd $(top_builddir) && ./config.status src/$@
+
+pkgconfigdir = $(libdir)/pkgconfig
+pkgconfig_DATA = $(enabled_cairo_pkgconf)
+
+CLEANFILES += cairo.def
+cairo.def: cairo-features.h $(enabled_cairo_headers)
+       @echo Generating $@
+       @(echo EXPORTS; \
+       (cd $(srcdir); cat $(enabled_cairo_headers) || echo 'cairo_ERROR ()' ) | \
+       $(EGREP) -v '^# *include' | \
+       ( cat cairo-features.h - | $(CPP) -D__cplusplus - || echo 'cairo_ERROR ()' ) | \
+       $(EGREP) '^cairo_.* \(' | \
+       sed -e 's/[     ].*//' | \
+       sort; \
+       echo LIBRARY libcairo-$(CAIRO_VERSION_SONUM).dll; \
+       ) >$@
+       @ ! grep -q cairo_ERROR $@ || ($(RM) $@; false)
+
+TESTS_ENVIRONMENT = \
+       srcdir="$(srcdir)" \
+       MAKE="$(MAKE) $(AM_MAKEFLAGS)" \
+       all_cairo_files="$(all_cairo_files)" \
+       all_cairo_headers="$(all_cairo_headers)" \
+       all_cairo_private="$(all_cairo_private)" \
+       all_cairo_sources="$(all_cairo_sources)" \
+       enabled_cairo_headers="$(enabled_cairo_headers)" \
+       enabled_cairo_private="$(enabled_cairo_private)" \
+       enabled_cairo_sources="$(enabled_cairo_sources)" \
+       $(NULL)
+TESTS_SH = \
+       check-def.sh \
+       check-doc-syntax.sh\
+       check-headers.sh \
+       check-plt.sh \
+       check-preprocessor-syntax.sh \
+       $(NULL)
+TESTS += $(TESTS_SH)
+if CROSS_COMPILING
+else
+TESTS += check-link$(EXEEXT)
+endif
+
+EXTRA_DIST += $(TESTS_SH) check-has-hidden-symbols.c
+check_PROGRAMS += check-link
+check_link_LDADD = libcairo.la
+
+check: headers-standalone
+
+PREPROCESS_ARGS = $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS)
+COMPILE_ARGS = $(PREPROCESS_ARGS) $(AM_CFLAGS) $(CFLAGS)
+
+# The pre-processed result is used by check-{def,plt}.sh to determine whether
+# cairo has been compiled with symbol hiding.
+.c.i: $(cairoinclude_HEADERS) $(nodist_cairoinclude_HEADERS) cairoint.h $(top_builddir)/config.h
+       $(CPP) $(PREPROCESS_ARGS) $< -o $@
+.c.s: $(cairoinclude_HEADERS) $(nodist_cairoinclude_HEADERS) cairoint.h $(top_builddir)/config.h
+       $(CC) $(COMPILE_ARGS) $< -S -o $@
+
+include $(srcdir)/Makefile.am.analysis
diff --git a/src/Makefile.am.analysis b/src/Makefile.am.analysis
new file mode 100644 (file)
index 0000000..fab4cf7
--- /dev/null
@@ -0,0 +1,35 @@
+
+SPARSE = sparse
+sparse:
+       @echo Checking enabled sources with sparse checker
+       @status=true; for f in $(enabled_cairo_sources) $(enabled_cairo_cxx_sources); do \
+               echo $(SPARSE) $(PREPROCESS_ARGS) $(srcdir)/$$f; \
+               $(SPARSE) $(PREPROCESS_ARGS) $(srcdir)/$$f || status=false; \
+       done; $$status
+
+SPLINT = splint -badflag
+splint:
+       @echo Checking enabled sources with splint checker
+       @status=true; for f in $(enabled_cairo_sources) $(enabled_cairo_cxx_sources); do \
+               echo $(SPLINT) $(PREPROCESS_ARGS) $(srcdir)/$$f; \
+               $(SPLINT) $(PREPROCESS_ARGS) $(srcdir)/$$f || status=false; \
+       done; $$status
+
+UNO = uno
+uno:
+       @echo Checking enabled sources with uno checker
+       cd $(srcdir); $(UNO) $(PREPROCESS_ARGS) -DHAVE_CONFIG_H -U__GNUC__ $(enabled_cairo_sources)
+
+headers-standalone: $(enabled_cairo_headers) $(enabled_cairo_private)
+       @echo Checking that enabled public/private headers can be compiled standalone
+       @status=true; for f in $(enabled_cairo_headers) $(enabled_cairo_private); do \
+               echo "  CHECK $$f"; \
+               echo "#include \"$(srcdir)/$$f\"" > headers-standalone-tmp.c; \
+               echo "int main(int argc, char * argv[]) { return 0; }" >> headers-standalone-tmp.c; \
+               $(COMPILE) -o headers-standalone-tmp headers-standalone-tmp.c || status=false; \
+               $(RM) headers-standalone-tmp headers-standalone-tmp.c; \
+       done; $$status
+       @touch $@
+CLEANFILES += headers-standalone
+
+analysis: all headers-standalone sparse splint uno
diff --git a/src/Makefile.sources b/src/Makefile.sources
new file mode 100644 (file)
index 0000000..2320f29
--- /dev/null
@@ -0,0 +1,429 @@
+# Makefile.sources
+#
+# This file is the canonical location listing all the source files used
+# to build the cairo library.  Every source file is categorized as one of:
+#
+#   * public header file
+#   * private header file (must end in -private.h except for cairoint.h)
+#   * source code file
+#
+# Every source file should be specified exactly once, grouped with the
+# feature that uses the source file.  If more than one feature use the
+# file (like pdf_operators or font_subset files), the files should be
+# appended to to the base cairo files, and the code inside them
+# enabled/disabled using C preprocessor macros defined in cairoint.h.
+# See how pdf_operators or font_subset are handled.
+#
+# The sources are picked up according to the configured features
+# by the generated file Makefile.am.features or Makefile.win32.features.
+#
+# These are a few special source files.  Those are not included in this
+# file to not confuse build systems.  Each build system must handle them
+# separately.  These files include:
+#
+#   * cairo-features.h:
+#     This file is generated by configure and includes macros signifying
+#     which features are enabled.  This file should be installed like
+#     other public headers, but should NOT be distributed in the cairo
+#     distribution.
+#
+#   * cairo-version.h:
+#     This is a dummy header file used during the build, but it should
+#     NOT be installed.  Its sole purpose is to make sure changes in the
+#     cairo version do not trigger a full rebuild of the library, but
+#     just the functions actually using the version information.
+#
+#   * $(top_srcdir)/cairo-version.h:
+#     This is the real file holding the cairo version number.  This file
+#     should be installed like other public headers.  This is used during
+#     the build by cairo-version.c only.
+#
+#   * cairo-supported-features.h:
+#     This file is generated by configure and includes macros signifying
+#     all supported features.  This is used by gtk-doc to generate
+#     documentation for all those macros, enabled or not.
+#     This file is NOT used during the build of the library and should
+#     NOT be installed or distributed.
+#
+# Please follow the strict syntax of this file, including keeping file
+# lists sorted.
+#
+
+cairo_headers = cairo.h cairo-deprecated.h
+cairo_private = \
+       cairoint.h \
+       cairo-analysis-surface-private.h \
+       cairo-arc-private.h \
+       cairo-array-private.h \
+       cairo-atomic-private.h \
+       cairo-backend-private.h \
+       cairo-box-private.h \
+       cairo-boxes-private.h \
+       cairo-cache-private.h \
+       cairo-clip-private.h \
+       cairo-combsort-private.h \
+       cairo-compiler-private.h \
+       cairo-compositor-private.h \
+       cairo-contour-private.h \
+       cairo-composite-rectangles-private.h \
+       cairo-default-context-private.h \
+       cairo-device-private.h \
+       cairo-error-private.h \
+       cairo-fixed-private.h \
+       cairo-fixed-type-private.h \
+       cairo-freelist-private.h \
+       cairo-freelist-type-private.h \
+       cairo-freed-pool-private.h \
+       cairo-fontconfig-private.h \
+       cairo-gstate-private.h \
+       cairo-hash-private.h \
+       cairo-image-info-private.h \
+       cairo-image-surface-private.h \
+       cairo-list-private.h \
+       cairo-malloc-private.h \
+       cairo-mutex-impl-private.h \
+       cairo-mutex-list-private.h \
+       cairo-mutex-private.h \
+       cairo-mutex-type-private.h \
+       cairo-output-stream-private.h \
+       cairo-paginated-private.h \
+       cairo-paginated-surface-private.h \
+       cairo-path-fixed-private.h \
+       cairo-path-private.h \
+       cairo-pattern-private.h \
+       cairo-private.h \
+       cairo-recording-surface-private.h \
+       cairo-reference-count-private.h \
+       cairo-region-private.h \
+       cairo-rtree-private.h \
+       cairo-scaled-font-private.h \
+       cairo-slope-private.h \
+       cairo-spans-private.h \
+       cairo-spans-compositor-private.h \
+       cairo-stroke-dash-private.h \
+       cairo-surface-private.h \
+       cairo-surface-backend-private.h \
+       cairo-surface-clipper-private.h \
+       cairo-surface-fallback-private.h \
+       cairo-surface-observer-private.h \
+       cairo-surface-offset-private.h \
+       cairo-surface-subsurface-private.h \
+       cairo-surface-snapshot-private.h \
+       cairo-surface-wrapper-private.h \
+       cairo-time-private.h \
+       cairo-types-private.h \
+       cairo-traps-private.h \
+       cairo-tristrip-private.h \
+       cairo-user-font-private.h \
+       cairo-wideint-private.h \
+       cairo-wideint-type-private.h \
+       $(NULL)
+cairo_sources = \
+       cairo-analysis-surface.c \
+       cairo-arc.c \
+       cairo-array.c \
+       cairo-atomic.c \
+       cairo-base64-stream.c \
+       cairo-base85-stream.c \
+       cairo-bentley-ottmann.c \
+       cairo-bentley-ottmann-rectangular.c \
+       cairo-bentley-ottmann-rectilinear.c \
+       cairo-botor-scan-converter.c \
+       cairo-boxes.c \
+       cairo-boxes-intersect.c \
+       cairo.c \
+       cairo-cache.c \
+       cairo-clip.c \
+       cairo-clip-boxes.c \
+       cairo-clip-polygon.c \
+       cairo-clip-region.c \
+       cairo-clip-surface.c \
+       cairo-color.c \
+       cairo-composite-rectangles.c \
+       cairo-compositor.c \
+       cairo-contour.c \
+       cairo-debug.c \
+       cairo-default-context.c \
+       cairo-device.c \
+       cairo-error.c \
+       cairo-fallback-compositor.c \
+       cairo-fixed.c \
+       cairo-font-face.c \
+       cairo-font-face-twin.c \
+       cairo-font-face-twin-data.c \
+       cairo-font-options.c \
+       cairo-freelist.c \
+       cairo-freed-pool.c \
+       cairo-gstate.c \
+       cairo-hash.c \
+       cairo-hull.c \
+       cairo-image-compositor.c \
+       cairo-image-info.c \
+       cairo-image-source.c \
+       cairo-image-surface.c \
+       cairo-lzw.c \
+       cairo-matrix.c \
+       cairo-mask-compositor.c \
+       cairo-mesh-pattern-rasterizer.c \
+       cairo-mime-surface.c \
+       cairo-misc.c \
+       cairo-mono-scan-converter.c \
+       cairo-mutex.c \
+       cairo-no-compositor.c \
+       cairo-observer.c \
+       cairo-output-stream.c \
+       cairo-paginated-surface.c \
+       cairo-path-bounds.c \
+       cairo-path.c \
+       cairo-path-fill.c \
+       cairo-path-fixed.c \
+       cairo-path-in-fill.c \
+       cairo-path-stroke.c \
+       cairo-path-stroke-boxes.c \
+       cairo-path-stroke-polygon.c \
+       cairo-path-stroke-tristrip.c \
+       cairo-pattern.c \
+       cairo-pen.c \
+       cairo-polygon.c \
+       cairo-polygon-intersect.c \
+       cairo-polygon-reduce.c \
+       cairo-recording-surface.c \
+       cairo-rectangle.c \
+       cairo-rectangular-scan-converter.c \
+       cairo-region.c \
+       cairo-rtree.c \
+       cairo-scaled-font.c \
+       cairo-slope.c \
+       cairo-spans.c \
+       cairo-spans-compositor.c \
+       cairo-spline.c \
+       cairo-stroke-dash.c \
+       cairo-stroke-style.c \
+       cairo-surface.c \
+       cairo-surface-clipper.c \
+       cairo-surface-fallback.c \
+       cairo-surface-observer.c \
+       cairo-surface-offset.c \
+       cairo-surface-snapshot.c \
+       cairo-surface-subsurface.c \
+       cairo-surface-wrapper.c \
+       cairo-system.c \
+       cairo-time.c \
+       cairo-tor-scan-converter.c \
+       cairo-tor22-scan-converter.c \
+       cairo-clip-tor-scan-converter.c \
+       cairo-toy-font-face.c \
+       cairo-traps.c \
+       cairo-tristrip.c \
+       cairo-traps-compositor.c \
+       cairo-unicode.c \
+       cairo-user-font.c \
+       cairo-version.c \
+       cairo-wideint.c \
+       $(NULL)
+
+_cairo_font_subset_private = \
+       cairo-scaled-font-subsets-private.h \
+       cairo-truetype-subset-private.h \
+       cairo-type1-private.h \
+        cairo-type3-glyph-surface-private.h \
+       $(NULL)
+_cairo_font_subset_sources = \
+       cairo-cff-subset.c \
+       cairo-scaled-font-subsets.c \
+       cairo-truetype-subset.c \
+       cairo-type1-fallback.c \
+       cairo-type1-glyph-names.c \
+       cairo-type1-subset.c \
+        cairo-type3-glyph-surface.c \
+       $(NULL)
+cairo_private += $(_cairo_font_subset_private)
+cairo_sources += $(_cairo_font_subset_sources)
+
+cairo_egl_sources =
+cairo_glx_sources =
+cairo_wgl_sources =
+
+_cairo_pdf_operators_private = cairo-pdf-operators-private.h cairo-pdf-shading-private.h
+_cairo_pdf_operators_sources = cairo-pdf-operators.c cairo-pdf-shading.c
+cairo_private += $(_cairo_pdf_operators_private)
+cairo_sources += $(_cairo_pdf_operators_sources)
+
+cairo_png_sources = cairo-png.c
+
+cairo_ps_headers = cairo-ps.h
+cairo_ps_private = cairo-ps-surface-private.h
+cairo_ps_sources = cairo-ps-surface.c
+
+_cairo_deflate_stream_sources = cairo-deflate-stream.c
+cairo_sources += $(_cairo_deflate_stream_sources)
+
+cairo_pdf_headers = cairo-pdf.h
+cairo_pdf_private = cairo-pdf-surface-private.h
+cairo_pdf_sources = cairo-pdf-surface.c
+
+cairo_svg_headers = cairo-svg.h
+cairo_svg_private = cairo-svg-surface-private.h
+cairo_svg_sources = cairo-svg-surface.c
+
+cairo_ft_headers = cairo-ft.h
+cairo_ft_private = cairo-ft-private.h
+cairo_ft_sources = cairo-ft-font.c
+
+# These are private, even though they look like public headers
+cairo_test_surfaces_private = \
+       test-compositor-surface.h \
+       test-compositor-surface-private.h \
+       test-null-compositor-surface.h \
+       test-paginated-surface.h \
+       $(NULL)
+cairo_test_surfaces_sources = \
+       test-compositor-surface.c \
+       test-null-compositor-surface.c \
+       test-base-compositor-surface.c \
+       test-paginated-surface.c \
+       $(NULL)
+
+cairo_xlib_headers = cairo-xlib.h
+cairo_xlib_private = \
+       cairo-xlib-private.h \
+       cairo-xlib-surface-private.h \
+       cairo-xlib-xrender-private.h \
+       $(NULL)
+cairo_xlib_sources = \
+       cairo-xlib-display.c \
+       cairo-xlib-core-compositor.c \
+       cairo-xlib-fallback-compositor.c \
+       cairo-xlib-render-compositor.c \
+       cairo-xlib-screen.c \
+       cairo-xlib-source.c \
+       cairo-xlib-surface.c \
+       cairo-xlib-visual.c \
+       cairo-xlib-xcb-surface.c \
+       $(NULL)
+
+cairo_xlib_xrender_headers = cairo-xlib-xrender.h
+
+cairo_xcb_headers = cairo-xcb.h
+cairo_xcb_private = cairo-xcb-private.h
+cairo_xcb_sources = \
+                   cairo-xcb-connection.c \
+                   cairo-xcb-connection-core.c \
+                   cairo-xcb-connection-render.c \
+                   cairo-xcb-connection-shm.c \
+                   cairo-xcb-screen.c \
+                   cairo-xcb-shm.c \
+                   cairo-xcb-surface.c \
+                   cairo-xcb-surface-cairo.c \
+                   cairo-xcb-surface-core.c \
+                   cairo-xcb-surface-render.c \
+                   $(NULL)
+
+cairo_qt_headers = cairo-qt.h
+cairo_qt_cxx_sources = cairo-qt-surface.cpp
+
+cairo_quartz_headers = cairo-quartz.h
+cairo_quartz_private = cairo-quartz-private.h
+cairo_quartz_sources = cairo-quartz-surface.c
+
+cairo_quartz_image_headers = cairo-quartz-image.h
+cairo_quartz_image_sources = cairo-quartz-image-surface.c
+
+cairo_quartz_font_sources = cairo-quartz-font.c
+
+cairo_win32_headers = cairo-win32.h
+cairo_win32_private = cairo-win32-private.h
+cairo_win32_sources = cairo-win32-surface.c cairo-win32-printing-surface.c
+
+cairo_win32_font_sources = cairo-win32-font.c
+
+cairo_skia_headers = cairo-skia.h
+cairo_skia_cxx_sources = \
+       skia/cairo-skia-context.cpp \
+       skia/cairo-skia-surface.cpp \
+       $(NULL)
+
+cairo_os2_headers = cairo-os2.h
+cairo_os2_private = cairo-os2-private.h
+cairo_os2_sources = cairo-os2-surface.c
+
+# automake is stupid enough to always use c++ linker if we enable the
+# following lines, even if beos surface is not enabled.  Disable it for now.
+cairo_beos_headers = cairo-beos.h
+cairo_beos_cxx_sources = cairo-beos-surface.cpp
+
+cairo_gl_headers = cairo-gl.h
+cairo_gl_private = cairo-gl-private.h \
+                  cairo-gl-dispatch-private.h \
+                  cairo-gl-ext-def-private.h \
+                  cairo-gl-gradient-private.h
+
+cairo_gl_sources = cairo-gl-composite.c \
+                  cairo-gl-device.c \
+                  cairo-gl-dispatch.c \
+                  cairo-gl-glyphs.c \
+                  cairo-gl-gradient.c \
+                  cairo-gl-info.c \
+                  cairo-gl-operand.c \
+                  cairo-gl-shaders.c \
+                  cairo-gl-spans-compositor.c \
+                  cairo-gl-traps-compositor.c \
+                  cairo-gl-surface.c
+
+cairo_glesv2_headers = $(cairo_gl_headers)
+cairo_glesv2_private = $(cairo_gl_private)
+cairo_glesv2_sources = $(cairo_gl_sources)
+
+cairo_egl_sources += cairo-egl-context.c
+cairo_glx_sources += cairo-glx-context.c
+cairo_wgl_sources += cairo-wgl-context.c
+
+cairo_directfb_headers = cairo-directfb.h
+cairo_directfb_sources = cairo-directfb-surface.c
+
+cairo_drm_headers = cairo-drm.h
+cairo_drm_private = drm/cairo-drm-private.h \
+                   drm/cairo-drm-ioctl-private.h \
+                   drm/cairo-drm-intel-private.h \
+                   drm/cairo-drm-intel-brw-defines.h \
+                   drm/cairo-drm-intel-brw-structs.h \
+                   drm/cairo-drm-intel-brw-eu.h \
+                   drm/cairo-drm-intel-command-private.h \
+                   drm/cairo-drm-intel-ioctl-private.h \
+                   drm/cairo-drm-i915-private.h \
+                   drm/cairo-drm-i965-private.h \
+                   drm/cairo-drm-radeon-private.h
+cairo_drm_sources = drm/cairo-drm.c \
+                   drm/cairo-drm-bo.c \
+                   drm/cairo-drm-surface.c \
+                   drm/cairo-drm-intel.c \
+                   drm/cairo-drm-intel-debug.c \
+                   drm/cairo-drm-intel-surface.c \
+                   drm/cairo-drm-i915-surface.c \
+                   drm/cairo-drm-i915-glyphs.c \
+                   drm/cairo-drm-i915-shader.c \
+                   drm/cairo-drm-i915-spans.c \
+                   drm/cairo-drm-i965-surface.c \
+                   drm/cairo-drm-i965-glyphs.c \
+                   drm/cairo-drm-i965-shader.c \
+                   drm/cairo-drm-i965-spans.c \
+                   drm/cairo-drm-intel-brw-eu.c \
+                   drm/cairo-drm-intel-brw-eu-emit.c \
+                   drm/cairo-drm-intel-brw-eu-util.c \
+                   drm/cairo-drm-radeon.c \
+                   drm/cairo-drm-radeon-surface.c
+cairo_gallium_sources = drm/cairo-drm-gallium-surface.c
+
+cairo_script_headers = cairo-script.h
+cairo_script_private = cairo-script-private.h
+cairo_script_sources = cairo-script-surface.c
+
+cairo_tee_headers = cairo-tee.h
+cairo_tee_private = cairo-tee-surface-private.h
+cairo_tee_sources = cairo-tee-surface.c
+
+cairo_xml_headers = cairo-xml.h
+cairo_xml_sources = cairo-xml-surface.c
+
+cairo_vg_headers = cairo-vg.h
+cairo_vg_sources = cairo-vg-surface.c
diff --git a/src/Makefile.win32 b/src/Makefile.win32
new file mode 100644 (file)
index 0000000..864791f
--- /dev/null
@@ -0,0 +1,29 @@
+top_srcdir = ..
+include $(top_srcdir)/build/Makefile.win32.common
+include Makefile.win32.features
+
+SOURCES = $(enabled_cairo_sources)
+
+STATIC_SOURCES = cairo-system.c
+
+OBJECTS = $(patsubst %.c, $(CFG)/%.obj, $(SOURCES))
+OBJECTS_STATIC = $(patsubst %cairo-system.obj, %cairo-system-static.obj, $(OBJECTS))
+
+static: inform $(CFG)/cairo-static.lib
+dynamic: inform $(CFG)/cairo.dll
+
+$(CFG)/cairo.dll: $(OBJECTS)
+       @$(LD) $(CAIRO_LDFLAGS) -DLL -OUT:$@ $(CAIRO_LIBS) $(PIXMAN_LIBS) $(OBJECTS)
+
+$(CFG)/cairo-static.lib: $(OBJECTS_STATIC)
+       @$(AR) $(CAIRO_ARFLAGS) -OUT:$@ $(PIXMAN_LIBS) $(OBJECTS_STATIC)
+
+all: inform $(CFG)/cairo.dll $(CFG)/cairo-static.lib
+       @echo "Built successfully!"
+       @echo "You should copy the following files to a proper place now:"
+       @echo ""
+       @echo " cairo-version.h (NOTE: toplevel, not the src/cairo-version.h one!)"
+       @echo " src/cairo-features.h"
+       @for x in $(enabled_cairo_headers); do echo "   src/$$x"; done
+       @echo " src/$(CFG)/cairo.dll"
+       @echo " src/$(CFG)/cairo-static.lib"
diff --git a/src/Makefile.win32.features b/src/Makefile.win32.features
new file mode 100644 (file)
index 0000000..4b90d64
--- /dev/null
@@ -0,0 +1,645 @@
+# Generated by configure.  Do not edit.
+
+ifeq ($(top_srcdir),)
+include Makefile.sources
+else
+include $(top_srcdir)/src/Makefile.sources
+endif
+
+supported_cairo_headers = $(cairo_headers)
+unsupported_cairo_headers =
+all_cairo_headers = $(cairo_headers)
+all_cairo_private = $(cairo_private)
+all_cairo_cxx_sources = $(cairo_cxx_sources)
+all_cairo_sources = $(cairo_sources)
+
+enabled_cairo_headers = $(cairo_headers)
+enabled_cairo_private = $(cairo_private)
+enabled_cairo_cxx_sources = $(cairo_cxx_sources)
+enabled_cairo_sources = $(cairo_sources)
+
+all_cairo_pkgconf = cairo.pc
+enabled_cairo_pkgconf = cairo.pc
+
+supported_cairo_headers += $(cairo_xlib_headers)
+all_cairo_headers += $(cairo_xlib_headers)
+all_cairo_private += $(cairo_xlib_private)
+all_cairo_cxx_sources += $(cairo_xlib_cxx_sources)
+all_cairo_sources += $(cairo_xlib_sources)
+ifeq ($(CAIRO_HAS_XLIB_SURFACE),1)
+enabled_cairo_headers += $(cairo_xlib_headers)
+enabled_cairo_private += $(cairo_xlib_private)
+enabled_cairo_cxx_sources += $(cairo_xlib_cxx_sources)
+enabled_cairo_sources += $(cairo_xlib_sources)
+endif
+all_cairo_pkgconf += cairo-xlib.pc
+ifeq ($(CAIRO_HAS_XLIB_SURFACE),1)
+enabled_cairo_pkgconf += cairo-xlib.pc
+endif
+
+supported_cairo_headers += $(cairo_xlib_xrender_headers)
+all_cairo_headers += $(cairo_xlib_xrender_headers)
+all_cairo_private += $(cairo_xlib_xrender_private)
+all_cairo_cxx_sources += $(cairo_xlib_xrender_cxx_sources)
+all_cairo_sources += $(cairo_xlib_xrender_sources)
+ifeq ($(CAIRO_HAS_XLIB_XRENDER_SURFACE),1)
+enabled_cairo_headers += $(cairo_xlib_xrender_headers)
+enabled_cairo_private += $(cairo_xlib_xrender_private)
+enabled_cairo_cxx_sources += $(cairo_xlib_xrender_cxx_sources)
+enabled_cairo_sources += $(cairo_xlib_xrender_sources)
+endif
+all_cairo_pkgconf += cairo-xlib-xrender.pc
+ifeq ($(CAIRO_HAS_XLIB_XRENDER_SURFACE),1)
+enabled_cairo_pkgconf += cairo-xlib-xrender.pc
+endif
+
+supported_cairo_headers += $(cairo_xcb_headers)
+all_cairo_headers += $(cairo_xcb_headers)
+all_cairo_private += $(cairo_xcb_private)
+all_cairo_cxx_sources += $(cairo_xcb_cxx_sources)
+all_cairo_sources += $(cairo_xcb_sources)
+ifeq ($(CAIRO_HAS_XCB_SURFACE),1)
+enabled_cairo_headers += $(cairo_xcb_headers)
+enabled_cairo_private += $(cairo_xcb_private)
+enabled_cairo_cxx_sources += $(cairo_xcb_cxx_sources)
+enabled_cairo_sources += $(cairo_xcb_sources)
+endif
+all_cairo_pkgconf += cairo-xcb.pc
+ifeq ($(CAIRO_HAS_XCB_SURFACE),1)
+enabled_cairo_pkgconf += cairo-xcb.pc
+endif
+
+unsupported_cairo_headers += $(cairo_xlib_xcb_headers)
+all_cairo_headers += $(cairo_xlib_xcb_headers)
+all_cairo_private += $(cairo_xlib_xcb_private)
+all_cairo_cxx_sources += $(cairo_xlib_xcb_cxx_sources)
+all_cairo_sources += $(cairo_xlib_xcb_sources)
+ifeq ($(CAIRO_HAS_XLIB_XCB_FUNCTIONS),1)
+enabled_cairo_headers += $(cairo_xlib_xcb_headers)
+enabled_cairo_private += $(cairo_xlib_xcb_private)
+enabled_cairo_cxx_sources += $(cairo_xlib_xcb_cxx_sources)
+enabled_cairo_sources += $(cairo_xlib_xcb_sources)
+endif
+all_cairo_pkgconf += cairo-xlib-xcb.pc
+ifeq ($(CAIRO_HAS_XLIB_XCB_FUNCTIONS),1)
+enabled_cairo_pkgconf += cairo-xlib-xcb.pc
+endif
+
+supported_cairo_headers += $(cairo_xcb_shm_headers)
+all_cairo_headers += $(cairo_xcb_shm_headers)
+all_cairo_private += $(cairo_xcb_shm_private)
+all_cairo_cxx_sources += $(cairo_xcb_shm_cxx_sources)
+all_cairo_sources += $(cairo_xcb_shm_sources)
+ifeq ($(CAIRO_HAS_XCB_SHM_FUNCTIONS),1)
+enabled_cairo_headers += $(cairo_xcb_shm_headers)
+enabled_cairo_private += $(cairo_xcb_shm_private)
+enabled_cairo_cxx_sources += $(cairo_xcb_shm_cxx_sources)
+enabled_cairo_sources += $(cairo_xcb_shm_sources)
+endif
+all_cairo_pkgconf += cairo-xcb-shm.pc
+ifeq ($(CAIRO_HAS_XCB_SHM_FUNCTIONS),1)
+enabled_cairo_pkgconf += cairo-xcb-shm.pc
+endif
+
+unsupported_cairo_headers += $(cairo_qt_headers)
+all_cairo_headers += $(cairo_qt_headers)
+all_cairo_private += $(cairo_qt_private)
+all_cairo_cxx_sources += $(cairo_qt_cxx_sources)
+all_cairo_sources += $(cairo_qt_sources)
+ifeq ($(CAIRO_HAS_QT_SURFACE),1)
+enabled_cairo_headers += $(cairo_qt_headers)
+enabled_cairo_private += $(cairo_qt_private)
+enabled_cairo_cxx_sources += $(cairo_qt_cxx_sources)
+enabled_cairo_sources += $(cairo_qt_sources)
+endif
+all_cairo_pkgconf += cairo-qt.pc
+ifeq ($(CAIRO_HAS_QT_SURFACE),1)
+enabled_cairo_pkgconf += cairo-qt.pc
+endif
+
+supported_cairo_headers += $(cairo_quartz_headers)
+all_cairo_headers += $(cairo_quartz_headers)
+all_cairo_private += $(cairo_quartz_private)
+all_cairo_cxx_sources += $(cairo_quartz_cxx_sources)
+all_cairo_sources += $(cairo_quartz_sources)
+ifeq ($(CAIRO_HAS_QUARTZ_SURFACE),1)
+enabled_cairo_headers += $(cairo_quartz_headers)
+enabled_cairo_private += $(cairo_quartz_private)
+enabled_cairo_cxx_sources += $(cairo_quartz_cxx_sources)
+enabled_cairo_sources += $(cairo_quartz_sources)
+endif
+all_cairo_pkgconf += cairo-quartz.pc
+ifeq ($(CAIRO_HAS_QUARTZ_SURFACE),1)
+enabled_cairo_pkgconf += cairo-quartz.pc
+endif
+
+supported_cairo_headers += $(cairo_quartz_font_headers)
+all_cairo_headers += $(cairo_quartz_font_headers)
+all_cairo_private += $(cairo_quartz_font_private)
+all_cairo_cxx_sources += $(cairo_quartz_font_cxx_sources)
+all_cairo_sources += $(cairo_quartz_font_sources)
+ifeq ($(CAIRO_HAS_QUARTZ_FONT),1)
+enabled_cairo_headers += $(cairo_quartz_font_headers)
+enabled_cairo_private += $(cairo_quartz_font_private)
+enabled_cairo_cxx_sources += $(cairo_quartz_font_cxx_sources)
+enabled_cairo_sources += $(cairo_quartz_font_sources)
+endif
+all_cairo_pkgconf += cairo-quartz-font.pc
+ifeq ($(CAIRO_HAS_QUARTZ_FONT),1)
+enabled_cairo_pkgconf += cairo-quartz-font.pc
+endif
+
+unsupported_cairo_headers += $(cairo_quartz_image_headers)
+all_cairo_headers += $(cairo_quartz_image_headers)
+all_cairo_private += $(cairo_quartz_image_private)
+all_cairo_cxx_sources += $(cairo_quartz_image_cxx_sources)
+all_cairo_sources += $(cairo_quartz_image_sources)
+ifeq ($(CAIRO_HAS_QUARTZ_IMAGE_SURFACE),1)
+enabled_cairo_headers += $(cairo_quartz_image_headers)
+enabled_cairo_private += $(cairo_quartz_image_private)
+enabled_cairo_cxx_sources += $(cairo_quartz_image_cxx_sources)
+enabled_cairo_sources += $(cairo_quartz_image_sources)
+endif
+all_cairo_pkgconf += cairo-quartz-image.pc
+ifeq ($(CAIRO_HAS_QUARTZ_IMAGE_SURFACE),1)
+enabled_cairo_pkgconf += cairo-quartz-image.pc
+endif
+
+supported_cairo_headers += $(cairo_win32_headers)
+all_cairo_headers += $(cairo_win32_headers)
+all_cairo_private += $(cairo_win32_private)
+all_cairo_cxx_sources += $(cairo_win32_cxx_sources)
+all_cairo_sources += $(cairo_win32_sources)
+ifeq ($(CAIRO_HAS_WIN32_SURFACE),1)
+enabled_cairo_headers += $(cairo_win32_headers)
+enabled_cairo_private += $(cairo_win32_private)
+enabled_cairo_cxx_sources += $(cairo_win32_cxx_sources)
+enabled_cairo_sources += $(cairo_win32_sources)
+endif
+all_cairo_pkgconf += cairo-win32.pc
+ifeq ($(CAIRO_HAS_WIN32_SURFACE),1)
+enabled_cairo_pkgconf += cairo-win32.pc
+endif
+
+supported_cairo_headers += $(cairo_win32_font_headers)
+all_cairo_headers += $(cairo_win32_font_headers)
+all_cairo_private += $(cairo_win32_font_private)
+all_cairo_cxx_sources += $(cairo_win32_font_cxx_sources)
+all_cairo_sources += $(cairo_win32_font_sources)
+ifeq ($(CAIRO_HAS_WIN32_FONT),1)
+enabled_cairo_headers += $(cairo_win32_font_headers)
+enabled_cairo_private += $(cairo_win32_font_private)
+enabled_cairo_cxx_sources += $(cairo_win32_font_cxx_sources)
+enabled_cairo_sources += $(cairo_win32_font_sources)
+endif
+all_cairo_pkgconf += cairo-win32-font.pc
+ifeq ($(CAIRO_HAS_WIN32_FONT),1)
+enabled_cairo_pkgconf += cairo-win32-font.pc
+endif
+
+unsupported_cairo_headers += $(cairo_skia_headers)
+all_cairo_headers += $(cairo_skia_headers)
+all_cairo_private += $(cairo_skia_private)
+all_cairo_cxx_sources += $(cairo_skia_cxx_sources)
+all_cairo_sources += $(cairo_skia_sources)
+ifeq ($(CAIRO_HAS_SKIA_SURFACE),1)
+enabled_cairo_headers += $(cairo_skia_headers)
+enabled_cairo_private += $(cairo_skia_private)
+enabled_cairo_cxx_sources += $(cairo_skia_cxx_sources)
+enabled_cairo_sources += $(cairo_skia_sources)
+endif
+all_cairo_pkgconf += cairo-skia.pc
+ifeq ($(CAIRO_HAS_SKIA_SURFACE),1)
+enabled_cairo_pkgconf += cairo-skia.pc
+endif
+
+unsupported_cairo_headers += $(cairo_os2_headers)
+all_cairo_headers += $(cairo_os2_headers)
+all_cairo_private += $(cairo_os2_private)
+all_cairo_cxx_sources += $(cairo_os2_cxx_sources)
+all_cairo_sources += $(cairo_os2_sources)
+ifeq ($(CAIRO_HAS_OS2_SURFACE),1)
+enabled_cairo_headers += $(cairo_os2_headers)
+enabled_cairo_private += $(cairo_os2_private)
+enabled_cairo_cxx_sources += $(cairo_os2_cxx_sources)
+enabled_cairo_sources += $(cairo_os2_sources)
+endif
+all_cairo_pkgconf += cairo-os2.pc
+ifeq ($(CAIRO_HAS_OS2_SURFACE),1)
+enabled_cairo_pkgconf += cairo-os2.pc
+endif
+
+unsupported_cairo_headers += $(cairo_beos_headers)
+all_cairo_headers += $(cairo_beos_headers)
+all_cairo_private += $(cairo_beos_private)
+all_cairo_cxx_sources += $(cairo_beos_cxx_sources)
+all_cairo_sources += $(cairo_beos_sources)
+ifeq ($(CAIRO_HAS_BEOS_SURFACE),1)
+enabled_cairo_headers += $(cairo_beos_headers)
+enabled_cairo_private += $(cairo_beos_private)
+enabled_cairo_cxx_sources += $(cairo_beos_cxx_sources)
+enabled_cairo_sources += $(cairo_beos_sources)
+endif
+all_cairo_pkgconf += cairo-beos.pc
+ifeq ($(CAIRO_HAS_BEOS_SURFACE),1)
+enabled_cairo_pkgconf += cairo-beos.pc
+endif
+
+unsupported_cairo_headers += $(cairo_drm_headers)
+all_cairo_headers += $(cairo_drm_headers)
+all_cairo_private += $(cairo_drm_private)
+all_cairo_cxx_sources += $(cairo_drm_cxx_sources)
+all_cairo_sources += $(cairo_drm_sources)
+ifeq ($(CAIRO_HAS_DRM_SURFACE),1)
+enabled_cairo_headers += $(cairo_drm_headers)
+enabled_cairo_private += $(cairo_drm_private)
+enabled_cairo_cxx_sources += $(cairo_drm_cxx_sources)
+enabled_cairo_sources += $(cairo_drm_sources)
+endif
+all_cairo_pkgconf += cairo-drm.pc
+ifeq ($(CAIRO_HAS_DRM_SURFACE),1)
+enabled_cairo_pkgconf += cairo-drm.pc
+endif
+
+unsupported_cairo_headers += $(cairo_gallium_headers)
+all_cairo_headers += $(cairo_gallium_headers)
+all_cairo_private += $(cairo_gallium_private)
+all_cairo_cxx_sources += $(cairo_gallium_cxx_sources)
+all_cairo_sources += $(cairo_gallium_sources)
+ifeq ($(CAIRO_HAS_GALLIUM_SURFACE),1)
+enabled_cairo_headers += $(cairo_gallium_headers)
+enabled_cairo_private += $(cairo_gallium_private)
+enabled_cairo_cxx_sources += $(cairo_gallium_cxx_sources)
+enabled_cairo_sources += $(cairo_gallium_sources)
+endif
+all_cairo_pkgconf += cairo-gallium.pc
+ifeq ($(CAIRO_HAS_GALLIUM_SURFACE),1)
+enabled_cairo_pkgconf += cairo-gallium.pc
+endif
+
+supported_cairo_headers += $(cairo_png_headers)
+all_cairo_headers += $(cairo_png_headers)
+all_cairo_private += $(cairo_png_private)
+all_cairo_cxx_sources += $(cairo_png_cxx_sources)
+all_cairo_sources += $(cairo_png_sources)
+ifeq ($(CAIRO_HAS_PNG_FUNCTIONS),1)
+enabled_cairo_headers += $(cairo_png_headers)
+enabled_cairo_private += $(cairo_png_private)
+enabled_cairo_cxx_sources += $(cairo_png_cxx_sources)
+enabled_cairo_sources += $(cairo_png_sources)
+endif
+all_cairo_pkgconf += cairo-png.pc
+ifeq ($(CAIRO_HAS_PNG_FUNCTIONS),1)
+enabled_cairo_pkgconf += cairo-png.pc
+endif
+
+unsupported_cairo_headers += $(cairo_gl_headers)
+all_cairo_headers += $(cairo_gl_headers)
+all_cairo_private += $(cairo_gl_private)
+all_cairo_cxx_sources += $(cairo_gl_cxx_sources)
+all_cairo_sources += $(cairo_gl_sources)
+ifeq ($(CAIRO_HAS_GL_SURFACE),1)
+enabled_cairo_headers += $(cairo_gl_headers)
+enabled_cairo_private += $(cairo_gl_private)
+enabled_cairo_cxx_sources += $(cairo_gl_cxx_sources)
+enabled_cairo_sources += $(cairo_gl_sources)
+endif
+all_cairo_pkgconf += cairo-gl.pc
+ifeq ($(CAIRO_HAS_GL_SURFACE),1)
+enabled_cairo_pkgconf += cairo-gl.pc
+endif
+
+unsupported_cairo_headers += $(cairo_glesv2_headers)
+all_cairo_headers += $(cairo_glesv2_headers)
+all_cairo_private += $(cairo_glesv2_private)
+all_cairo_cxx_sources += $(cairo_glesv2_cxx_sources)
+all_cairo_sources += $(cairo_glesv2_sources)
+ifeq ($(CAIRO_HAS_GLESV2_SURFACE),1)
+enabled_cairo_headers += $(cairo_glesv2_headers)
+enabled_cairo_private += $(cairo_glesv2_private)
+enabled_cairo_cxx_sources += $(cairo_glesv2_cxx_sources)
+enabled_cairo_sources += $(cairo_glesv2_sources)
+endif
+all_cairo_pkgconf += cairo-glesv2.pc
+ifeq ($(CAIRO_HAS_GLESV2_SURFACE),1)
+enabled_cairo_pkgconf += cairo-glesv2.pc
+endif
+
+unsupported_cairo_headers += $(cairo_directfb_headers)
+all_cairo_headers += $(cairo_directfb_headers)
+all_cairo_private += $(cairo_directfb_private)
+all_cairo_cxx_sources += $(cairo_directfb_cxx_sources)
+all_cairo_sources += $(cairo_directfb_sources)
+ifeq ($(CAIRO_HAS_DIRECTFB_SURFACE),1)
+enabled_cairo_headers += $(cairo_directfb_headers)
+enabled_cairo_private += $(cairo_directfb_private)
+enabled_cairo_cxx_sources += $(cairo_directfb_cxx_sources)
+enabled_cairo_sources += $(cairo_directfb_sources)
+endif
+all_cairo_pkgconf += cairo-directfb.pc
+ifeq ($(CAIRO_HAS_DIRECTFB_SURFACE),1)
+enabled_cairo_pkgconf += cairo-directfb.pc
+endif
+
+unsupported_cairo_headers += $(cairo_vg_headers)
+all_cairo_headers += $(cairo_vg_headers)
+all_cairo_private += $(cairo_vg_private)
+all_cairo_cxx_sources += $(cairo_vg_cxx_sources)
+all_cairo_sources += $(cairo_vg_sources)
+ifeq ($(CAIRO_HAS_VG_SURFACE),1)
+enabled_cairo_headers += $(cairo_vg_headers)
+enabled_cairo_private += $(cairo_vg_private)
+enabled_cairo_cxx_sources += $(cairo_vg_cxx_sources)
+enabled_cairo_sources += $(cairo_vg_sources)
+endif
+all_cairo_pkgconf += cairo-vg.pc
+ifeq ($(CAIRO_HAS_VG_SURFACE),1)
+enabled_cairo_pkgconf += cairo-vg.pc
+endif
+
+supported_cairo_headers += $(cairo_egl_headers)
+all_cairo_headers += $(cairo_egl_headers)
+all_cairo_private += $(cairo_egl_private)
+all_cairo_cxx_sources += $(cairo_egl_cxx_sources)
+all_cairo_sources += $(cairo_egl_sources)
+ifeq ($(CAIRO_HAS_EGL_FUNCTIONS),1)
+enabled_cairo_headers += $(cairo_egl_headers)
+enabled_cairo_private += $(cairo_egl_private)
+enabled_cairo_cxx_sources += $(cairo_egl_cxx_sources)
+enabled_cairo_sources += $(cairo_egl_sources)
+endif
+all_cairo_pkgconf += cairo-egl.pc
+ifeq ($(CAIRO_HAS_EGL_FUNCTIONS),1)
+enabled_cairo_pkgconf += cairo-egl.pc
+endif
+
+supported_cairo_headers += $(cairo_glx_headers)
+all_cairo_headers += $(cairo_glx_headers)
+all_cairo_private += $(cairo_glx_private)
+all_cairo_cxx_sources += $(cairo_glx_cxx_sources)
+all_cairo_sources += $(cairo_glx_sources)
+ifeq ($(CAIRO_HAS_GLX_FUNCTIONS),1)
+enabled_cairo_headers += $(cairo_glx_headers)
+enabled_cairo_private += $(cairo_glx_private)
+enabled_cairo_cxx_sources += $(cairo_glx_cxx_sources)
+enabled_cairo_sources += $(cairo_glx_sources)
+endif
+all_cairo_pkgconf += cairo-glx.pc
+ifeq ($(CAIRO_HAS_GLX_FUNCTIONS),1)
+enabled_cairo_pkgconf += cairo-glx.pc
+endif
+
+supported_cairo_headers += $(cairo_wgl_headers)
+all_cairo_headers += $(cairo_wgl_headers)
+all_cairo_private += $(cairo_wgl_private)
+all_cairo_cxx_sources += $(cairo_wgl_cxx_sources)
+all_cairo_sources += $(cairo_wgl_sources)
+ifeq ($(CAIRO_HAS_WGL_FUNCTIONS),1)
+enabled_cairo_headers += $(cairo_wgl_headers)
+enabled_cairo_private += $(cairo_wgl_private)
+enabled_cairo_cxx_sources += $(cairo_wgl_cxx_sources)
+enabled_cairo_sources += $(cairo_wgl_sources)
+endif
+all_cairo_pkgconf += cairo-wgl.pc
+ifeq ($(CAIRO_HAS_WGL_FUNCTIONS),1)
+enabled_cairo_pkgconf += cairo-wgl.pc
+endif
+
+supported_cairo_headers += $(cairo_script_headers)
+all_cairo_headers += $(cairo_script_headers)
+all_cairo_private += $(cairo_script_private)
+all_cairo_cxx_sources += $(cairo_script_cxx_sources)
+all_cairo_sources += $(cairo_script_sources)
+ifeq ($(CAIRO_HAS_SCRIPT_SURFACE),1)
+enabled_cairo_headers += $(cairo_script_headers)
+enabled_cairo_private += $(cairo_script_private)
+enabled_cairo_cxx_sources += $(cairo_script_cxx_sources)
+enabled_cairo_sources += $(cairo_script_sources)
+endif
+all_cairo_pkgconf += cairo-script.pc
+ifeq ($(CAIRO_HAS_SCRIPT_SURFACE),1)
+enabled_cairo_pkgconf += cairo-script.pc
+endif
+
+supported_cairo_headers += $(cairo_ft_headers)
+all_cairo_headers += $(cairo_ft_headers)
+all_cairo_private += $(cairo_ft_private)
+all_cairo_cxx_sources += $(cairo_ft_cxx_sources)
+all_cairo_sources += $(cairo_ft_sources)
+ifeq ($(CAIRO_HAS_FT_FONT),1)
+enabled_cairo_headers += $(cairo_ft_headers)
+enabled_cairo_private += $(cairo_ft_private)
+enabled_cairo_cxx_sources += $(cairo_ft_cxx_sources)
+enabled_cairo_sources += $(cairo_ft_sources)
+endif
+all_cairo_pkgconf += cairo-ft.pc
+ifeq ($(CAIRO_HAS_FT_FONT),1)
+enabled_cairo_pkgconf += cairo-ft.pc
+endif
+
+supported_cairo_headers += $(cairo_fc_headers)
+all_cairo_headers += $(cairo_fc_headers)
+all_cairo_private += $(cairo_fc_private)
+all_cairo_cxx_sources += $(cairo_fc_cxx_sources)
+all_cairo_sources += $(cairo_fc_sources)
+ifeq ($(CAIRO_HAS_FC_FONT),1)
+enabled_cairo_headers += $(cairo_fc_headers)
+enabled_cairo_private += $(cairo_fc_private)
+enabled_cairo_cxx_sources += $(cairo_fc_cxx_sources)
+enabled_cairo_sources += $(cairo_fc_sources)
+endif
+all_cairo_pkgconf += cairo-fc.pc
+ifeq ($(CAIRO_HAS_FC_FONT),1)
+enabled_cairo_pkgconf += cairo-fc.pc
+endif
+
+supported_cairo_headers += $(cairo_ps_headers)
+all_cairo_headers += $(cairo_ps_headers)
+all_cairo_private += $(cairo_ps_private)
+all_cairo_cxx_sources += $(cairo_ps_cxx_sources)
+all_cairo_sources += $(cairo_ps_sources)
+ifeq ($(CAIRO_HAS_PS_SURFACE),1)
+enabled_cairo_headers += $(cairo_ps_headers)
+enabled_cairo_private += $(cairo_ps_private)
+enabled_cairo_cxx_sources += $(cairo_ps_cxx_sources)
+enabled_cairo_sources += $(cairo_ps_sources)
+endif
+all_cairo_pkgconf += cairo-ps.pc
+ifeq ($(CAIRO_HAS_PS_SURFACE),1)
+enabled_cairo_pkgconf += cairo-ps.pc
+endif
+
+supported_cairo_headers += $(cairo_pdf_headers)
+all_cairo_headers += $(cairo_pdf_headers)
+all_cairo_private += $(cairo_pdf_private)
+all_cairo_cxx_sources += $(cairo_pdf_cxx_sources)
+all_cairo_sources += $(cairo_pdf_sources)
+ifeq ($(CAIRO_HAS_PDF_SURFACE),1)
+enabled_cairo_headers += $(cairo_pdf_headers)
+enabled_cairo_private += $(cairo_pdf_private)
+enabled_cairo_cxx_sources += $(cairo_pdf_cxx_sources)
+enabled_cairo_sources += $(cairo_pdf_sources)
+endif
+all_cairo_pkgconf += cairo-pdf.pc
+ifeq ($(CAIRO_HAS_PDF_SURFACE),1)
+enabled_cairo_pkgconf += cairo-pdf.pc
+endif
+
+supported_cairo_headers += $(cairo_svg_headers)
+all_cairo_headers += $(cairo_svg_headers)
+all_cairo_private += $(cairo_svg_private)
+all_cairo_cxx_sources += $(cairo_svg_cxx_sources)
+all_cairo_sources += $(cairo_svg_sources)
+ifeq ($(CAIRO_HAS_SVG_SURFACE),1)
+enabled_cairo_headers += $(cairo_svg_headers)
+enabled_cairo_private += $(cairo_svg_private)
+enabled_cairo_cxx_sources += $(cairo_svg_cxx_sources)
+enabled_cairo_sources += $(cairo_svg_sources)
+endif
+all_cairo_pkgconf += cairo-svg.pc
+ifeq ($(CAIRO_HAS_SVG_SURFACE),1)
+enabled_cairo_pkgconf += cairo-svg.pc
+endif
+
+all_cairo_private += $(cairo_test_surfaces_private) $(cairo_test_surfaces_headers)
+all_cairo_cxx_sources += $(cairo_test_surfaces_cxx_sources)
+all_cairo_sources += $(cairo_test_surfaces_sources)
+ifeq ($(CAIRO_HAS_TEST_SURFACES),1)
+enabled_cairo_private += $(cairo_test_surfaces_private) $(cairo_test_surfaces_headers)
+enabled_cairo_cxx_sources += $(cairo_test_surfaces_cxx_sources)
+enabled_cairo_sources += $(cairo_test_surfaces_sources)
+endif
+
+supported_cairo_headers += $(cairo_image_headers)
+all_cairo_headers += $(cairo_image_headers)
+all_cairo_private += $(cairo_image_private)
+all_cairo_cxx_sources += $(cairo_image_cxx_sources)
+all_cairo_sources += $(cairo_image_sources)
+enabled_cairo_headers += $(cairo_image_headers)
+enabled_cairo_private += $(cairo_image_private)
+enabled_cairo_cxx_sources += $(cairo_image_cxx_sources)
+enabled_cairo_sources += $(cairo_image_sources)
+
+supported_cairo_headers += $(cairo_mime_headers)
+all_cairo_headers += $(cairo_mime_headers)
+all_cairo_private += $(cairo_mime_private)
+all_cairo_cxx_sources += $(cairo_mime_cxx_sources)
+all_cairo_sources += $(cairo_mime_sources)
+enabled_cairo_headers += $(cairo_mime_headers)
+enabled_cairo_private += $(cairo_mime_private)
+enabled_cairo_cxx_sources += $(cairo_mime_cxx_sources)
+enabled_cairo_sources += $(cairo_mime_sources)
+
+supported_cairo_headers += $(cairo_recording_headers)
+all_cairo_headers += $(cairo_recording_headers)
+all_cairo_private += $(cairo_recording_private)
+all_cairo_cxx_sources += $(cairo_recording_cxx_sources)
+all_cairo_sources += $(cairo_recording_sources)
+enabled_cairo_headers += $(cairo_recording_headers)
+enabled_cairo_private += $(cairo_recording_private)
+enabled_cairo_cxx_sources += $(cairo_recording_cxx_sources)
+enabled_cairo_sources += $(cairo_recording_sources)
+
+supported_cairo_headers += $(cairo_observer_headers)
+all_cairo_headers += $(cairo_observer_headers)
+all_cairo_private += $(cairo_observer_private)
+all_cairo_cxx_sources += $(cairo_observer_cxx_sources)
+all_cairo_sources += $(cairo_observer_sources)
+enabled_cairo_headers += $(cairo_observer_headers)
+enabled_cairo_private += $(cairo_observer_private)
+enabled_cairo_cxx_sources += $(cairo_observer_cxx_sources)
+enabled_cairo_sources += $(cairo_observer_sources)
+
+unsupported_cairo_headers += $(cairo_tee_headers)
+all_cairo_headers += $(cairo_tee_headers)
+all_cairo_private += $(cairo_tee_private)
+all_cairo_cxx_sources += $(cairo_tee_cxx_sources)
+all_cairo_sources += $(cairo_tee_sources)
+ifeq ($(CAIRO_HAS_TEE_SURFACE),1)
+enabled_cairo_headers += $(cairo_tee_headers)
+enabled_cairo_private += $(cairo_tee_private)
+enabled_cairo_cxx_sources += $(cairo_tee_cxx_sources)
+enabled_cairo_sources += $(cairo_tee_sources)
+endif
+all_cairo_pkgconf += cairo-tee.pc
+ifeq ($(CAIRO_HAS_TEE_SURFACE),1)
+enabled_cairo_pkgconf += cairo-tee.pc
+endif
+
+unsupported_cairo_headers += $(cairo_xml_headers)
+all_cairo_headers += $(cairo_xml_headers)
+all_cairo_private += $(cairo_xml_private)
+all_cairo_cxx_sources += $(cairo_xml_cxx_sources)
+all_cairo_sources += $(cairo_xml_sources)
+ifeq ($(CAIRO_HAS_XML_SURFACE),1)
+enabled_cairo_headers += $(cairo_xml_headers)
+enabled_cairo_private += $(cairo_xml_private)
+enabled_cairo_cxx_sources += $(cairo_xml_cxx_sources)
+enabled_cairo_sources += $(cairo_xml_sources)
+endif
+all_cairo_pkgconf += cairo-xml.pc
+ifeq ($(CAIRO_HAS_XML_SURFACE),1)
+enabled_cairo_pkgconf += cairo-xml.pc
+endif
+
+supported_cairo_headers += $(cairo_user_headers)
+all_cairo_headers += $(cairo_user_headers)
+all_cairo_private += $(cairo_user_private)
+all_cairo_cxx_sources += $(cairo_user_cxx_sources)
+all_cairo_sources += $(cairo_user_sources)
+enabled_cairo_headers += $(cairo_user_headers)
+enabled_cairo_private += $(cairo_user_private)
+enabled_cairo_cxx_sources += $(cairo_user_cxx_sources)
+enabled_cairo_sources += $(cairo_user_sources)
+
+all_cairo_private += $(cairo_pthread_private) $(cairo_pthread_headers)
+all_cairo_cxx_sources += $(cairo_pthread_cxx_sources)
+all_cairo_sources += $(cairo_pthread_sources)
+ifeq ($(CAIRO_HAS_PTHREAD),1)
+enabled_cairo_private += $(cairo_pthread_private) $(cairo_pthread_headers)
+enabled_cairo_cxx_sources += $(cairo_pthread_cxx_sources)
+enabled_cairo_sources += $(cairo_pthread_sources)
+endif
+
+supported_cairo_headers += $(cairo_gobject_headers)
+all_cairo_headers += $(cairo_gobject_headers)
+all_cairo_private += $(cairo_gobject_private)
+all_cairo_cxx_sources += $(cairo_gobject_cxx_sources)
+all_cairo_sources += $(cairo_gobject_sources)
+ifeq ($(CAIRO_HAS_GOBJECT_FUNCTIONS),1)
+enabled_cairo_headers += $(cairo_gobject_headers)
+enabled_cairo_private += $(cairo_gobject_private)
+enabled_cairo_cxx_sources += $(cairo_gobject_cxx_sources)
+enabled_cairo_sources += $(cairo_gobject_sources)
+endif
+all_cairo_pkgconf += cairo-gobject.pc
+ifeq ($(CAIRO_HAS_GOBJECT_FUNCTIONS),1)
+enabled_cairo_pkgconf += cairo-gobject.pc
+endif
+
+all_cairo_private += $(cairo_trace_private) $(cairo_trace_headers)
+all_cairo_cxx_sources += $(cairo_trace_cxx_sources)
+all_cairo_sources += $(cairo_trace_sources)
+ifeq ($(CAIRO_HAS_TRACE),1)
+enabled_cairo_private += $(cairo_trace_private) $(cairo_trace_headers)
+enabled_cairo_cxx_sources += $(cairo_trace_cxx_sources)
+enabled_cairo_sources += $(cairo_trace_sources)
+endif
+
+all_cairo_private += $(cairo_interpreter_private) $(cairo_interpreter_headers)
+all_cairo_cxx_sources += $(cairo_interpreter_cxx_sources)
+all_cairo_sources += $(cairo_interpreter_sources)
+ifeq ($(CAIRO_HAS_INTERPRETER),1)
+enabled_cairo_private += $(cairo_interpreter_private) $(cairo_interpreter_headers)
+enabled_cairo_cxx_sources += $(cairo_interpreter_cxx_sources)
+enabled_cairo_sources += $(cairo_interpreter_sources)
+endif
+
+all_cairo_private += $(cairo_symbol_lookup_private) $(cairo_symbol_lookup_headers)
+all_cairo_cxx_sources += $(cairo_symbol_lookup_cxx_sources)
+all_cairo_sources += $(cairo_symbol_lookup_sources)
+ifeq ($(CAIRO_HAS_SYMBOL_LOOKUP),1)
+enabled_cairo_private += $(cairo_symbol_lookup_private) $(cairo_symbol_lookup_headers)
+enabled_cairo_cxx_sources += $(cairo_symbol_lookup_cxx_sources)
+enabled_cairo_sources += $(cairo_symbol_lookup_sources)
+endif
diff --git a/src/README b/src/README
new file mode 100644 (file)
index 0000000..03e4455
--- /dev/null
@@ -0,0 +1,69 @@
+Cairo Library Source Code
+=========================
+
+This directory contains the source code of the cairo library.
+
+
+Source Code Listing
+-------------------
+
+The canonical list of source files is the file Makefile.sources.  See that
+file for how it works.
+
+
+New Backends
+------------
+
+The rule of the thumb for adding new backends is to see how other
+backends are integrated.  Pick one of the simpler, unsupported, backends
+and search the entire tree for it, and go from there.
+
+To add new backends you need to basically:
+
+  * Modify $(top_srcdir)/configure.in to add checks for your backend.
+
+  * Modify Makefile.sources to add source files for your backend,
+
+  * Modify $(top_srcdir)/boilerplate/ to add boilerplate code for
+    testing your new backend.
+
+
+New API
+-------
+
+After adding new API, run "make check" in this directory and fix any
+reported issues.  Also add new API to the right location in
+$(top_srcdir)/doc/public/cairo-sections.txt and run "make check"
+in $(top_builddir)/doc/public to make sure that any newly added
+documentation is correctly hooked up.
+
+Do not forget to add tests for the new API.  See next section.
+
+
+Tests
+-----
+
+There are some tests in this directory that check the source code and
+the build for various issues.  The tests are very quick to run, and
+particularly should be run after any documentation or API changes.  It
+does not hurt to run them after any source modification either.  Run
+them simply by calling:
+
+       make check
+
+There are also extensive regression tests in $(top_srcdir)/test.  It is
+a good idea to run that test suite for any changes made to the source
+code.  Moreover, for any new feature, API, or bug fix, new tests should
+be added to the regression test suite to test the new code.
+
+
+Bibliography
+------------
+
+A detailed list of academic publications used in cairo code is available
+in the file $(top_srcdir)/BIBLIOGRAPHY.  Feel free to update as you
+implement more papers.
+
+For more technical publications (eg. Adobe technical reports) just
+point them out in a comment in the header of the file implementing them.
+
diff --git a/src/cairo-analysis-surface-private.h b/src/cairo-analysis-surface-private.h
new file mode 100644 (file)
index 0000000..1e054c2
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * Copyright © 2005 Keith Packard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Keith Packard
+ *
+ * Contributor(s):
+ *      Keith Packard <keithp@keithp.com>
+ */
+
+#ifndef CAIRO_ANALYSIS_SURFACE_H
+#define CAIRO_ANALYSIS_SURFACE_H
+
+#include "cairoint.h"
+
+cairo_private cairo_surface_t *
+_cairo_analysis_surface_create (cairo_surface_t                *target);
+
+cairo_private void
+_cairo_analysis_surface_set_ctm (cairo_surface_t *surface,
+                                const cairo_matrix_t  *ctm);
+
+cairo_private void
+_cairo_analysis_surface_get_ctm (cairo_surface_t *surface,
+                                cairo_matrix_t  *ctm);
+
+cairo_private cairo_region_t *
+_cairo_analysis_surface_get_supported (cairo_surface_t *surface);
+
+cairo_private cairo_region_t *
+_cairo_analysis_surface_get_unsupported (cairo_surface_t *surface);
+
+cairo_private cairo_bool_t
+_cairo_analysis_surface_has_supported (cairo_surface_t *surface);
+
+cairo_private cairo_bool_t
+_cairo_analysis_surface_has_unsupported (cairo_surface_t *surface);
+
+cairo_private void
+_cairo_analysis_surface_get_bounding_box (cairo_surface_t *surface,
+                                         cairo_box_t     *bbox);
+
+cairo_private cairo_int_status_t
+_cairo_analysis_surface_merge_status (cairo_int_status_t status_a,
+                                     cairo_int_status_t status_b);
+
+cairo_private cairo_surface_t *
+_cairo_null_surface_create (cairo_content_t content);
+
+#endif /* CAIRO_ANALYSIS_SURFACE_H */
diff --git a/src/cairo-analysis-surface.c b/src/cairo-analysis-surface.c
new file mode 100644 (file)
index 0000000..d9edcd7
--- /dev/null
@@ -0,0 +1,937 @@
+/*
+ * Copyright © 2006 Keith Packard
+ * Copyright © 2007 Adrian Johnson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Keith Packard
+ *
+ * Contributor(s):
+ *      Keith Packard <keithp@keithp.com>
+ *      Adrian Johnson <ajohnson@redneon.com>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-analysis-surface-private.h"
+#include "cairo-default-context-private.h"
+#include "cairo-error-private.h"
+#include "cairo-paginated-private.h"
+#include "cairo-recording-surface-private.h"
+#include "cairo-surface-snapshot-private.h"
+#include "cairo-surface-subsurface-private.h"
+#include "cairo-region-private.h"
+
+typedef struct {
+    cairo_surface_t base;
+
+    cairo_surface_t *target;
+
+    cairo_bool_t first_op;
+    cairo_bool_t has_supported;
+    cairo_bool_t has_unsupported;
+
+    cairo_region_t supported_region;
+    cairo_region_t fallback_region;
+    cairo_box_t page_bbox;
+
+    cairo_bool_t has_ctm;
+    cairo_matrix_t ctm;
+
+} cairo_analysis_surface_t;
+
+cairo_int_status_t
+_cairo_analysis_surface_merge_status (cairo_int_status_t status_a,
+                                     cairo_int_status_t status_b)
+{
+    /* fatal errors should be checked and propagated at source */
+    assert (! _cairo_int_status_is_error (status_a));
+    assert (! _cairo_int_status_is_error (status_b));
+
+    /* return the most important status */
+    if (status_a == CAIRO_INT_STATUS_UNSUPPORTED ||
+       status_b == CAIRO_INT_STATUS_UNSUPPORTED)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    if (status_a == CAIRO_INT_STATUS_IMAGE_FALLBACK ||
+       status_b == CAIRO_INT_STATUS_IMAGE_FALLBACK)
+       return CAIRO_INT_STATUS_IMAGE_FALLBACK;
+
+    if (status_a == CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN ||
+       status_b == CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN)
+       return CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN;
+
+    if (status_a == CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY ||
+       status_b == CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY)
+       return CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY;
+
+    /* at this point we have checked all the valid internal codes, so... */
+    assert (status_a == CAIRO_INT_STATUS_SUCCESS &&
+           status_b == CAIRO_INT_STATUS_SUCCESS);
+
+    return CAIRO_INT_STATUS_SUCCESS;
+}
+
+struct proxy {
+    cairo_surface_t base;
+    cairo_surface_t *target;
+};
+
+static cairo_status_t
+proxy_finish (void *abstract_surface)
+{
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static const cairo_surface_backend_t proxy_backend  = {
+    CAIRO_INTERNAL_SURFACE_TYPE_NULL,
+    proxy_finish,
+};
+
+static cairo_surface_t *
+attach_proxy (cairo_surface_t *source,
+             cairo_surface_t *target)
+{
+    struct proxy *proxy;
+
+    proxy = malloc (sizeof (*proxy));
+    if (unlikely (proxy == NULL))
+       return _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY);
+
+    _cairo_surface_init (&proxy->base, &proxy_backend, NULL, target->content);
+
+    proxy->target = target;
+    _cairo_surface_attach_snapshot (source, &proxy->base, NULL);
+
+    return &proxy->base;
+}
+
+static void
+detach_proxy (cairo_surface_t *proxy)
+{
+    cairo_surface_finish (proxy);
+    cairo_surface_destroy (proxy);
+}
+
+static cairo_int_status_t
+_analyze_recording_surface_pattern (cairo_analysis_surface_t *surface,
+                                   const cairo_pattern_t    *pattern)
+{
+    const cairo_surface_pattern_t *surface_pattern;
+    cairo_analysis_surface_t *tmp;
+    cairo_surface_t *source, *proxy;
+    cairo_matrix_t p2d;
+    cairo_status_t status, analysis_status;
+
+    assert (pattern->type == CAIRO_PATTERN_TYPE_SURFACE);
+    surface_pattern = (const cairo_surface_pattern_t *) pattern;
+    assert (surface_pattern->surface->type == CAIRO_SURFACE_TYPE_RECORDING);
+    source = surface_pattern->surface;
+
+    proxy = _cairo_surface_has_snapshot (source, &proxy_backend);
+    if (proxy != NULL) {
+       /* nothing untoward found so far */
+       return CAIRO_STATUS_SUCCESS;
+    }
+
+    tmp = (cairo_analysis_surface_t *)
+       _cairo_analysis_surface_create (surface->target);
+    if (unlikely (tmp->base.status))
+       return tmp->base.status;
+    proxy = attach_proxy (source, &tmp->base);
+
+    p2d = pattern->matrix;
+    status = cairo_matrix_invert (&p2d);
+    assert (status == CAIRO_STATUS_SUCCESS);
+
+    cairo_matrix_multiply (&tmp->ctm, &p2d, &surface->ctm);
+    tmp->has_ctm = ! _cairo_matrix_is_identity (&tmp->ctm);
+
+    if (_cairo_surface_is_snapshot (source))
+       source = _cairo_surface_snapshot_get_target (source);
+    if (_cairo_surface_is_subsurface (source))
+       source = _cairo_surface_subsurface_get_target (source);
+
+    status = _cairo_recording_surface_replay_and_create_regions (source,
+                                                                &tmp->base);
+    analysis_status = tmp->has_unsupported ? CAIRO_INT_STATUS_IMAGE_FALLBACK : CAIRO_INT_STATUS_SUCCESS;
+    detach_proxy (proxy);
+    cairo_surface_destroy (&tmp->base);
+
+    if (unlikely (status))
+       return status;
+
+    return analysis_status;
+}
+
+static cairo_int_status_t
+_add_operation (cairo_analysis_surface_t *surface,
+               cairo_rectangle_int_t    *rect,
+               cairo_int_status_t        backend_status)
+{
+    cairo_int_status_t status;
+    cairo_box_t bbox;
+
+    if (rect->width == 0 || rect->height == 0) {
+       /* Even though the operation is not visible we must be careful
+        * to not allow unsupported operations to be replayed to the
+        * backend during CAIRO_PAGINATED_MODE_RENDER */
+       if (backend_status == CAIRO_INT_STATUS_SUCCESS ||
+           backend_status == CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY ||
+           backend_status == CAIRO_INT_STATUS_NOTHING_TO_DO)
+       {
+           return CAIRO_INT_STATUS_SUCCESS;
+       }
+       else
+       {
+           return CAIRO_INT_STATUS_IMAGE_FALLBACK;
+       }
+    }
+
+    _cairo_box_from_rectangle (&bbox, rect);
+
+    if (surface->has_ctm) {
+       int tx, ty;
+
+       if (_cairo_matrix_is_integer_translation (&surface->ctm, &tx, &ty)) {
+           rect->x += tx;
+           rect->y += ty;
+       } else {
+           _cairo_matrix_transform_bounding_box_fixed (&surface->ctm,
+                                                       &bbox, NULL);
+
+           if (bbox.p1.x == bbox.p2.x || bbox.p1.y == bbox.p2.y) {
+               /* Even though the operation is not visible we must be
+                * careful to not allow unsupported operations to be
+                * replayed to the backend during
+                * CAIRO_PAGINATED_MODE_RENDER */
+               if (backend_status == CAIRO_INT_STATUS_SUCCESS ||
+                   backend_status == CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY ||
+                   backend_status == CAIRO_INT_STATUS_NOTHING_TO_DO)
+               {
+                   return CAIRO_INT_STATUS_SUCCESS;
+               }
+               else
+               {
+                   return CAIRO_INT_STATUS_IMAGE_FALLBACK;
+               }
+           }
+
+           _cairo_box_round_to_rectangle (&bbox, rect);
+       }
+    }
+
+    if (surface->first_op) {
+       surface->first_op = FALSE;
+       surface->page_bbox = bbox;
+    } else {
+       if (bbox.p1.x < surface->page_bbox.p1.x)
+           surface->page_bbox.p1.x = bbox.p1.x;
+       if (bbox.p1.y < surface->page_bbox.p1.y)
+           surface->page_bbox.p1.y = bbox.p1.y;
+       if (bbox.p2.x > surface->page_bbox.p2.x)
+           surface->page_bbox.p2.x = bbox.p2.x;
+       if (bbox.p2.y > surface->page_bbox.p2.y)
+           surface->page_bbox.p2.y = bbox.p2.y;
+    }
+
+    /* If the operation is completely enclosed within the fallback
+     * region there is no benefit in emitting a native operation as
+     * the fallback image will be painted on top.
+     */
+    if (cairo_region_contains_rectangle (&surface->fallback_region, rect) == CAIRO_REGION_OVERLAP_IN)
+       return CAIRO_INT_STATUS_IMAGE_FALLBACK;
+
+    if (backend_status == CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY) {
+       /* A status of CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY indicates
+        * that the backend only supports this operation if the
+        * transparency removed. If the extents of this operation does
+        * not intersect any other native operation, the operation is
+        * natively supported and the backend will blend the
+        * transparency into the white background.
+        */
+       if (cairo_region_contains_rectangle (&surface->supported_region, rect) == CAIRO_REGION_OVERLAP_OUT)
+           backend_status = CAIRO_INT_STATUS_SUCCESS;
+    }
+
+    if (backend_status == CAIRO_INT_STATUS_SUCCESS) {
+       /* Add the operation to the supported region. Operations in
+        * this region will be emitted as native operations.
+        */
+       surface->has_supported = TRUE;
+       return cairo_region_union_rectangle (&surface->supported_region, rect);
+    }
+
+    /* Add the operation to the unsupported region. This region will
+     * be painted as an image after all native operations have been
+     * emitted.
+     */
+    surface->has_unsupported = TRUE;
+    status = cairo_region_union_rectangle (&surface->fallback_region, rect);
+
+    /* The status CAIRO_INT_STATUS_IMAGE_FALLBACK is used to indicate
+     * unsupported operations to the recording surface as using
+     * CAIRO_INT_STATUS_UNSUPPORTED would cause cairo-surface to
+     * invoke the cairo-surface-fallback path then return
+     * CAIRO_STATUS_SUCCESS.
+     */
+    if (status == CAIRO_INT_STATUS_SUCCESS)
+       return CAIRO_INT_STATUS_IMAGE_FALLBACK;
+    else
+       return status;
+}
+
+static cairo_status_t
+_cairo_analysis_surface_finish (void *abstract_surface)
+{
+    cairo_analysis_surface_t   *surface = (cairo_analysis_surface_t *) abstract_surface;
+
+    _cairo_region_fini (&surface->supported_region);
+    _cairo_region_fini (&surface->fallback_region);
+
+    cairo_surface_destroy (surface->target);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_bool_t
+_cairo_analysis_surface_get_extents (void                      *abstract_surface,
+                                    cairo_rectangle_int_t      *rectangle)
+{
+    cairo_analysis_surface_t *surface = abstract_surface;
+
+    return _cairo_surface_get_extents (surface->target, rectangle);
+}
+
+static void
+_rectangle_intersect_clip (cairo_rectangle_int_t *extents, const cairo_clip_t *clip)
+{
+    if (clip != NULL)
+       _cairo_rectangle_intersect (extents, _cairo_clip_get_extents (clip));
+}
+
+static void
+_cairo_analysis_surface_operation_extents (cairo_analysis_surface_t *surface,
+                                          cairo_operator_t op,
+                                          const cairo_pattern_t *source,
+                                          const cairo_clip_t *clip,
+                                          cairo_rectangle_int_t *extents)
+{
+    cairo_bool_t is_empty;
+
+    is_empty = _cairo_surface_get_extents (&surface->base, extents);
+
+    if (_cairo_operator_bounded_by_source (op)) {
+       cairo_rectangle_int_t source_extents;
+
+       _cairo_pattern_get_extents (source, &source_extents);
+       _cairo_rectangle_intersect (extents, &source_extents);
+    }
+
+    _rectangle_intersect_clip (extents, clip);
+}
+
+static cairo_int_status_t
+_cairo_analysis_surface_paint (void                    *abstract_surface,
+                              cairo_operator_t         op,
+                              const cairo_pattern_t    *source,
+                              const cairo_clip_t               *clip)
+{
+    cairo_analysis_surface_t *surface = abstract_surface;
+    cairo_int_status_t      backend_status;
+    cairo_rectangle_int_t  extents;
+
+    if (surface->target->backend->paint == NULL) {
+       backend_status = CAIRO_INT_STATUS_UNSUPPORTED;
+    } else {
+       backend_status =
+           surface->target->backend->paint (surface->target,
+                                            op, source, clip);
+       if (_cairo_int_status_is_error (backend_status))
+           return backend_status;
+    }
+
+    if (backend_status == CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN)
+       backend_status = _analyze_recording_surface_pattern (surface, source);
+
+    _cairo_analysis_surface_operation_extents (surface,
+                                              op, source, clip,
+                                              &extents);
+
+    return _add_operation (surface, &extents, backend_status);
+}
+
+static cairo_int_status_t
+_cairo_analysis_surface_mask (void                     *abstract_surface,
+                             cairo_operator_t           op,
+                             const cairo_pattern_t     *source,
+                             const cairo_pattern_t     *mask,
+                             const cairo_clip_t                *clip)
+{
+    cairo_analysis_surface_t *surface = abstract_surface;
+    cairo_int_status_t       backend_status;
+    cairo_rectangle_int_t   extents;
+
+    if (surface->target->backend->mask == NULL) {
+       backend_status = CAIRO_INT_STATUS_UNSUPPORTED;
+    } else {
+       backend_status =
+           surface->target->backend->mask (surface->target,
+                                           op, source, mask, clip);
+       if (_cairo_int_status_is_error (backend_status))
+           return backend_status;
+    }
+
+    if (backend_status == CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN) {
+       cairo_int_status_t backend_source_status = CAIRO_STATUS_SUCCESS;
+       cairo_int_status_t backend_mask_status = CAIRO_STATUS_SUCCESS;
+
+       if (source->type == CAIRO_PATTERN_TYPE_SURFACE) {
+           cairo_surface_t *src_surface = ((cairo_surface_pattern_t *)source)->surface;
+           if (_cairo_surface_is_snapshot (src_surface))
+               src_surface = _cairo_surface_snapshot_get_target (src_surface);
+           if (_cairo_surface_is_recording (src_surface)) {
+               backend_source_status =
+                   _analyze_recording_surface_pattern (surface, source);
+               if (_cairo_int_status_is_error (backend_source_status))
+                   return backend_source_status;
+           }
+       }
+
+       if (mask->type == CAIRO_PATTERN_TYPE_SURFACE) {
+           cairo_surface_t *mask_surface = ((cairo_surface_pattern_t *)mask)->surface;
+           if (_cairo_surface_is_snapshot (mask_surface))
+               mask_surface = _cairo_surface_snapshot_get_target (mask_surface);
+           if (_cairo_surface_is_recording (mask_surface)) {
+               backend_mask_status =
+                   _analyze_recording_surface_pattern (surface, mask);
+               if (_cairo_int_status_is_error (backend_mask_status))
+                   return backend_mask_status;
+           }
+       }
+
+       backend_status =
+           _cairo_analysis_surface_merge_status (backend_source_status,
+                                                 backend_mask_status);
+    }
+
+    _cairo_analysis_surface_operation_extents (surface,
+                                              op, source, clip,
+                                              &extents);
+
+    if (_cairo_operator_bounded_by_mask (op)) {
+       cairo_rectangle_int_t mask_extents;
+
+       _cairo_pattern_get_extents (mask, &mask_extents);
+       _cairo_rectangle_intersect (&extents, &mask_extents);
+    }
+
+    return _add_operation (surface, &extents, backend_status);
+}
+
+static cairo_int_status_t
+_cairo_analysis_surface_stroke (void                   *abstract_surface,
+                               cairo_operator_t         op,
+                               const cairo_pattern_t   *source,
+                               const cairo_path_fixed_t        *path,
+                               const cairo_stroke_style_t      *style,
+                               const cairo_matrix_t            *ctm,
+                               const cairo_matrix_t            *ctm_inverse,
+                               double                   tolerance,
+                               cairo_antialias_t        antialias,
+                               const cairo_clip_t              *clip)
+{
+    cairo_analysis_surface_t *surface = abstract_surface;
+    cairo_int_status_t      backend_status;
+    cairo_rectangle_int_t    extents;
+
+    if (surface->target->backend->stroke == NULL) {
+       backend_status = CAIRO_INT_STATUS_UNSUPPORTED;
+    } else {
+       backend_status =
+           surface->target->backend->stroke (surface->target, op,
+                                             source, path, style,
+                                             ctm, ctm_inverse,
+                                             tolerance, antialias,
+                                             clip);
+       if (_cairo_int_status_is_error (backend_status))
+           return backend_status;
+    }
+
+    if (backend_status == CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN)
+       backend_status = _analyze_recording_surface_pattern (surface, source);
+
+    _cairo_analysis_surface_operation_extents (surface,
+                                              op, source, clip,
+                                              &extents);
+
+    if (_cairo_operator_bounded_by_mask (op)) {
+       cairo_rectangle_int_t mask_extents;
+       cairo_int_status_t status;
+
+       status = _cairo_path_fixed_stroke_extents (path, style,
+                                                  ctm, ctm_inverse,
+                                                  tolerance,
+                                                  &mask_extents);
+       if (unlikely (status))
+           return status;
+
+       _cairo_rectangle_intersect (&extents, &mask_extents);
+    }
+
+    return _add_operation (surface, &extents, backend_status);
+}
+
+static cairo_int_status_t
+_cairo_analysis_surface_fill (void                     *abstract_surface,
+                             cairo_operator_t           op,
+                             const cairo_pattern_t     *source,
+                             const cairo_path_fixed_t  *path,
+                             cairo_fill_rule_t          fill_rule,
+                             double                     tolerance,
+                             cairo_antialias_t          antialias,
+                             const cairo_clip_t                *clip)
+{
+    cairo_analysis_surface_t *surface = abstract_surface;
+    cairo_int_status_t      backend_status;
+    cairo_rectangle_int_t    extents;
+
+    if (surface->target->backend->fill == NULL) {
+       backend_status = CAIRO_INT_STATUS_UNSUPPORTED;
+    } else {
+       backend_status =
+           surface->target->backend->fill (surface->target, op,
+                                           source, path, fill_rule,
+                                           tolerance, antialias,
+                                           clip);
+       if (_cairo_int_status_is_error (backend_status))
+           return backend_status;
+    }
+
+    if (backend_status == CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN)
+       backend_status = _analyze_recording_surface_pattern (surface, source);
+
+    _cairo_analysis_surface_operation_extents (surface,
+                                              op, source, clip,
+                                              &extents);
+
+    if (_cairo_operator_bounded_by_mask (op)) {
+       cairo_rectangle_int_t mask_extents;
+
+       _cairo_path_fixed_fill_extents (path, fill_rule, tolerance,
+                                       &mask_extents);
+
+       _cairo_rectangle_intersect (&extents, &mask_extents);
+    }
+
+    return _add_operation (surface, &extents, backend_status);
+}
+
+static cairo_int_status_t
+_cairo_analysis_surface_show_glyphs (void                *abstract_surface,
+                                    cairo_operator_t      op,
+                                    const cairo_pattern_t *source,
+                                    cairo_glyph_t        *glyphs,
+                                    int                   num_glyphs,
+                                    cairo_scaled_font_t  *scaled_font,
+                                    const cairo_clip_t         *clip)
+{
+    cairo_analysis_surface_t *surface = abstract_surface;
+    cairo_int_status_t      status, backend_status;
+    cairo_rectangle_int_t    extents, glyph_extents;
+
+    /* Adapted from _cairo_surface_show_glyphs */
+    if (surface->target->backend->show_glyphs != NULL) {
+       backend_status =
+           surface->target->backend->show_glyphs (surface->target, op,
+                                                  source,
+                                                  glyphs, num_glyphs,
+                                                  scaled_font,
+                                                  clip);
+       if (_cairo_int_status_is_error (backend_status))
+           return backend_status;
+    }
+    else if (surface->target->backend->show_text_glyphs != NULL)
+    {
+       backend_status =
+           surface->target->backend->show_text_glyphs (surface->target, op,
+                                                       source,
+                                                       NULL, 0,
+                                                       glyphs, num_glyphs,
+                                                       NULL, 0,
+                                                       FALSE,
+                                                       scaled_font,
+                                                       clip);
+       if (_cairo_int_status_is_error (backend_status))
+           return backend_status;
+    }
+    else
+    {
+       backend_status = CAIRO_INT_STATUS_UNSUPPORTED;
+    }
+
+    if (backend_status == CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN)
+       backend_status = _analyze_recording_surface_pattern (surface, source);
+
+    _cairo_analysis_surface_operation_extents (surface,
+                                              op, source, clip,
+                                              &extents);
+
+    if (_cairo_operator_bounded_by_mask (op)) {
+       status = _cairo_scaled_font_glyph_device_extents (scaled_font,
+                                                         glyphs,
+                                                         num_glyphs,
+                                                         &glyph_extents,
+                                                         NULL);
+       if (unlikely (status))
+           return status;
+
+       _cairo_rectangle_intersect (&extents, &glyph_extents);
+    }
+
+    return _add_operation (surface, &extents, backend_status);
+}
+
+static cairo_bool_t
+_cairo_analysis_surface_has_show_text_glyphs (void *abstract_surface)
+{
+    cairo_analysis_surface_t *surface = abstract_surface;
+
+    return cairo_surface_has_show_text_glyphs (surface->target);
+}
+
+static cairo_int_status_t
+_cairo_analysis_surface_show_text_glyphs (void                     *abstract_surface,
+                                         cairo_operator_t           op,
+                                         const cairo_pattern_t     *source,
+                                         const char                *utf8,
+                                         int                        utf8_len,
+                                         cairo_glyph_t             *glyphs,
+                                         int                        num_glyphs,
+                                         const cairo_text_cluster_t *clusters,
+                                         int                        num_clusters,
+                                         cairo_text_cluster_flags_t cluster_flags,
+                                         cairo_scaled_font_t       *scaled_font,
+                                         const cairo_clip_t                *clip)
+{
+    cairo_analysis_surface_t *surface = abstract_surface;
+    cairo_int_status_t      status, backend_status;
+    cairo_rectangle_int_t    extents, glyph_extents;
+
+    /* Adapted from _cairo_surface_show_glyphs */
+    backend_status = CAIRO_INT_STATUS_UNSUPPORTED;
+    if (surface->target->backend->show_text_glyphs != NULL) {
+       backend_status =
+           surface->target->backend->show_text_glyphs (surface->target, op,
+                                                       source,
+                                                       utf8, utf8_len,
+                                                       glyphs, num_glyphs,
+                                                       clusters, num_clusters,
+                                                       cluster_flags,
+                                                       scaled_font,
+                                                       clip);
+       if (_cairo_int_status_is_error (backend_status))
+           return backend_status;
+    }
+    if (backend_status == CAIRO_INT_STATUS_UNSUPPORTED &&
+       surface->target->backend->show_glyphs != NULL)
+    {
+       backend_status =
+           surface->target->backend->show_glyphs (surface->target, op,
+                                                  source,
+                                                  glyphs, num_glyphs,
+                                                  scaled_font,
+                                                  clip);
+       if (_cairo_int_status_is_error (backend_status))
+           return backend_status;
+    }
+
+    if (backend_status == CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN)
+       backend_status = _analyze_recording_surface_pattern (surface, source);
+
+    _cairo_analysis_surface_operation_extents (surface,
+                                              op, source, clip,
+                                              &extents);
+
+    if (_cairo_operator_bounded_by_mask (op)) {
+       status = _cairo_scaled_font_glyph_device_extents (scaled_font,
+                                                         glyphs,
+                                                         num_glyphs,
+                                                         &glyph_extents,
+                                                         NULL);
+       if (unlikely (status))
+           return status;
+
+       _cairo_rectangle_intersect (&extents, &glyph_extents);
+    }
+
+    return _add_operation (surface, &extents, backend_status);
+}
+
+static const cairo_surface_backend_t cairo_analysis_surface_backend = {
+    CAIRO_INTERNAL_SURFACE_TYPE_ANALYSIS,
+
+    _cairo_analysis_surface_finish,
+    NULL,
+
+    NULL, /* create_similar */
+    NULL, /* create_similar_image */
+    NULL, /* map_to_image */
+    NULL, /* unmap */
+
+    NULL, /* acquire_source_image */
+    NULL, /* release_source_image */
+    NULL, /* snapshot */
+
+    NULL, /* copy_page */
+    NULL, /* show_page */
+
+    _cairo_analysis_surface_get_extents,
+    NULL, /* get_font_options */
+
+    NULL, /* flush */
+    NULL, /* mark_dirty_rectangle */
+
+    _cairo_analysis_surface_paint,
+    _cairo_analysis_surface_mask,
+    _cairo_analysis_surface_stroke,
+    _cairo_analysis_surface_fill,
+    NULL, /* fill_stroke */
+    _cairo_analysis_surface_show_glyphs,
+    _cairo_analysis_surface_has_show_text_glyphs,
+    _cairo_analysis_surface_show_text_glyphs
+};
+
+cairo_surface_t *
+_cairo_analysis_surface_create (cairo_surface_t                *target)
+{
+    cairo_analysis_surface_t *surface;
+    cairo_status_t status;
+
+    status = target->status;
+    if (unlikely (status))
+       return _cairo_surface_create_in_error (status);
+
+    surface = malloc (sizeof (cairo_analysis_surface_t));
+    if (unlikely (surface == NULL))
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+    /* I believe the content type here is truly arbitrary. I'm quite
+     * sure nothing will ever use this value. */
+    _cairo_surface_init (&surface->base,
+                        &cairo_analysis_surface_backend,
+                        NULL, /* device */
+                        CAIRO_CONTENT_COLOR_ALPHA);
+
+    cairo_matrix_init_identity (&surface->ctm);
+    surface->has_ctm = FALSE;
+
+    surface->target = cairo_surface_reference (target);
+    surface->first_op  = TRUE;
+    surface->has_supported = FALSE;
+    surface->has_unsupported = FALSE;
+
+    _cairo_region_init (&surface->supported_region);
+    _cairo_region_init (&surface->fallback_region);
+
+    surface->page_bbox.p1.x = 0;
+    surface->page_bbox.p1.y = 0;
+    surface->page_bbox.p2.x = 0;
+    surface->page_bbox.p2.y = 0;
+
+    return &surface->base;
+}
+
+void
+_cairo_analysis_surface_set_ctm (cairo_surface_t *abstract_surface,
+                                const cairo_matrix_t  *ctm)
+{
+    cairo_analysis_surface_t   *surface;
+
+    if (abstract_surface->status)
+       return;
+
+    surface = (cairo_analysis_surface_t *) abstract_surface;
+
+    surface->ctm = *ctm;
+    surface->has_ctm = ! _cairo_matrix_is_identity (&surface->ctm);
+}
+
+void
+_cairo_analysis_surface_get_ctm (cairo_surface_t *abstract_surface,
+                                cairo_matrix_t  *ctm)
+{
+    cairo_analysis_surface_t   *surface = (cairo_analysis_surface_t *) abstract_surface;
+
+    *ctm = surface->ctm;
+}
+
+
+cairo_region_t *
+_cairo_analysis_surface_get_supported (cairo_surface_t *abstract_surface)
+{
+    cairo_analysis_surface_t   *surface = (cairo_analysis_surface_t *) abstract_surface;
+
+    return &surface->supported_region;
+}
+
+cairo_region_t *
+_cairo_analysis_surface_get_unsupported (cairo_surface_t *abstract_surface)
+{
+    cairo_analysis_surface_t   *surface = (cairo_analysis_surface_t *) abstract_surface;
+
+    return &surface->fallback_region;
+}
+
+cairo_bool_t
+_cairo_analysis_surface_has_supported (cairo_surface_t *abstract_surface)
+{
+    cairo_analysis_surface_t   *surface = (cairo_analysis_surface_t *) abstract_surface;
+
+    return surface->has_supported;
+}
+
+cairo_bool_t
+_cairo_analysis_surface_has_unsupported (cairo_surface_t *abstract_surface)
+{
+    cairo_analysis_surface_t   *surface = (cairo_analysis_surface_t *) abstract_surface;
+
+    return surface->has_unsupported;
+}
+
+void
+_cairo_analysis_surface_get_bounding_box (cairo_surface_t *abstract_surface,
+                                         cairo_box_t     *bbox)
+{
+    cairo_analysis_surface_t   *surface = (cairo_analysis_surface_t *) abstract_surface;
+
+    *bbox = surface->page_bbox;
+}
+
+/* null surface type: a surface that does nothing (has no side effects, yay!) */
+
+static cairo_int_status_t
+_return_success (void)
+{
+    return CAIRO_STATUS_SUCCESS;
+}
+
+/* These typedefs are just to silence the compiler... */
+typedef cairo_int_status_t
+(*_paint_func)                 (void                   *surface,
+                                cairo_operator_t        op,
+                                const cairo_pattern_t  *source,
+                                const cairo_clip_t             *clip);
+
+typedef cairo_int_status_t
+(*_mask_func)                  (void                   *surface,
+                                cairo_operator_t        op,
+                                const cairo_pattern_t  *source,
+                                const cairo_pattern_t  *mask,
+                                const cairo_clip_t             *clip);
+
+typedef cairo_int_status_t
+(*_stroke_func)                        (void                   *surface,
+                                cairo_operator_t        op,
+                                const cairo_pattern_t  *source,
+                                const cairo_path_fixed_t       *path,
+                                const cairo_stroke_style_t     *style,
+                                const cairo_matrix_t           *ctm,
+                                const cairo_matrix_t           *ctm_inverse,
+                                double                  tolerance,
+                                cairo_antialias_t       antialias,
+                                const cairo_clip_t             *clip);
+
+typedef cairo_int_status_t
+(*_fill_func)                  (void                   *surface,
+                                cairo_operator_t        op,
+                                const cairo_pattern_t  *source,
+                                const cairo_path_fixed_t       *path,
+                                cairo_fill_rule_t       fill_rule,
+                                double                  tolerance,
+                                cairo_antialias_t       antialias,
+                                const cairo_clip_t             *clip);
+
+typedef cairo_int_status_t
+(*_show_glyphs_func)           (void                   *surface,
+                                cairo_operator_t        op,
+                                const cairo_pattern_t  *source,
+                                cairo_glyph_t          *glyphs,
+                                int                     num_glyphs,
+                                cairo_scaled_font_t    *scaled_font,
+                                const cairo_clip_t             *clip);
+
+static const cairo_surface_backend_t cairo_null_surface_backend = {
+    CAIRO_INTERNAL_SURFACE_TYPE_NULL,
+    NULL, /* finish */
+
+    NULL, /* only accessed through the surface functions */
+
+    NULL, /* create_similar */
+    NULL, /* create similar image */
+    NULL, /* map to image */
+    NULL, /* unmap image*/
+
+    NULL, /* acquire_source_image */
+    NULL, /* release_source_image */
+    NULL, /* snapshot */
+
+    NULL, /* copy_page */
+    NULL, /* show_page */
+
+    NULL, /* get_extents */
+    NULL, /* get_font_options */
+
+    NULL, /* flush */
+    NULL, /* mark_dirty_rectangle */
+
+    (_paint_func) _return_success,         /* paint */
+    (_mask_func) _return_success,          /* mask */
+    (_stroke_func) _return_success,        /* stroke */
+    (_fill_func) _return_success,          /* fill */
+    NULL, /* fill_stroke */
+    (_show_glyphs_func) _return_success,    /* show_glyphs */
+    NULL, /* has_show_text_glyphs */
+    NULL  /* show_text_glyphs */
+};
+
+cairo_surface_t *
+_cairo_null_surface_create (cairo_content_t content)
+{
+    cairo_surface_t *surface;
+
+    surface = malloc (sizeof (cairo_surface_t));
+    if (unlikely (surface == NULL)) {
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+    }
+
+    _cairo_surface_init (surface,
+                        &cairo_null_surface_backend,
+                        NULL, /* device */
+                        content);
+
+    return surface;
+}
diff --git a/src/cairo-arc-private.h b/src/cairo-arc-private.h
new file mode 100644 (file)
index 0000000..a22c01a
--- /dev/null
@@ -0,0 +1,61 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ *     Carl D. Worth <cworth@redhat.com>
+ */
+
+#ifndef CAIRO_ARC_PRIVATE_H
+#define CAIRO_ARC_PRIVATE_H
+
+#include "cairoint.h"
+
+CAIRO_BEGIN_DECLS
+
+cairo_private void
+_cairo_arc_path (cairo_t *cr,
+                double   xc,
+                double   yc,
+                double   radius,
+                double   angle1,
+                double   angle2);
+
+cairo_private void
+_cairo_arc_path_negative (cairo_t *cr,
+                         double   xc,
+                         double   yc,
+                         double   radius,
+                         double   angle1,
+                         double   angle2);
+
+CAIRO_END_DECLS
+
+#endif /* CAIRO_ARC_PRIVATE_H */
diff --git a/src/cairo-arc.c b/src/cairo-arc.c
new file mode 100644 (file)
index 0000000..c997e5c
--- /dev/null
@@ -0,0 +1,303 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *     Carl D. Worth <cworth@cworth.org>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-arc-private.h"
+
+#define MAX_FULL_CIRCLES 65536
+
+/* Spline deviation from the circle in radius would be given by:
+
+       error = sqrt (x**2 + y**2) - 1
+
+   A simpler error function to work with is:
+
+       e = x**2 + y**2 - 1
+
+   From "Good approximation of circles by curvature-continuous Bezier
+   curves", Tor Dokken and Morten Daehlen, Computer Aided Geometric
+   Design 8 (1990) 22-41, we learn:
+
+       abs (max(e)) = 4/27 * sin**6(angle/4) / cos**2(angle/4)
+
+   and
+       abs (error) =~ 1/2 * e
+
+   Of course, this error value applies only for the particular spline
+   approximation that is used in _cairo_gstate_arc_segment.
+*/
+static double
+_arc_error_normalized (double angle)
+{
+    return 2.0/27.0 * pow (sin (angle / 4), 6) / pow (cos (angle / 4), 2);
+}
+
+static double
+_arc_max_angle_for_tolerance_normalized (double tolerance)
+{
+    double angle, error;
+    int i;
+
+    /* Use table lookup to reduce search time in most cases. */
+    struct {
+       double angle;
+       double error;
+    } table[] = {
+       { M_PI / 1.0,   0.0185185185185185036127 },
+       { M_PI / 2.0,   0.000272567143730179811158 },
+       { M_PI / 3.0,   2.38647043651461047433e-05 },
+       { M_PI / 4.0,   4.2455377443222443279e-06 },
+       { M_PI / 5.0,   1.11281001494389081528e-06 },
+       { M_PI / 6.0,   3.72662000942734705475e-07 },
+       { M_PI / 7.0,   1.47783685574284411325e-07 },
+       { M_PI / 8.0,   6.63240432022601149057e-08 },
+       { M_PI / 9.0,   3.2715520137536980553e-08 },
+       { M_PI / 10.0,  1.73863223499021216974e-08 },
+       { M_PI / 11.0,  9.81410988043554039085e-09 },
+    };
+    int table_size = ARRAY_LENGTH (table);
+
+    for (i = 0; i < table_size; i++)
+       if (table[i].error < tolerance)
+           return table[i].angle;
+
+    ++i;
+    do {
+       angle = M_PI / i++;
+       error = _arc_error_normalized (angle);
+    } while (error > tolerance);
+
+    return angle;
+}
+
+static int
+_arc_segments_needed (double         angle,
+                     double          radius,
+                     cairo_matrix_t *ctm,
+                     double          tolerance)
+{
+    double major_axis, max_angle;
+
+    /* the error is amplified by at most the length of the
+     * major axis of the circle; see cairo-pen.c for a more detailed analysis
+     * of this. */
+    major_axis = _cairo_matrix_transformed_circle_major_axis (ctm, radius);
+    max_angle = _arc_max_angle_for_tolerance_normalized (tolerance / major_axis);
+
+    return ceil (fabs (angle) / max_angle);
+}
+
+/* We want to draw a single spline approximating a circular arc radius
+   R from angle A to angle B. Since we want a symmetric spline that
+   matches the endpoints of the arc in position and slope, we know
+   that the spline control points must be:
+
+       (R * cos(A), R * sin(A))
+       (R * cos(A) - h * sin(A), R * sin(A) + h * cos (A))
+       (R * cos(B) + h * sin(B), R * sin(B) - h * cos (B))
+       (R * cos(B), R * sin(B))
+
+   for some value of h.
+
+   "Approximation of circular arcs by cubic poynomials", Michael
+   Goldapp, Computer Aided Geometric Design 8 (1991) 227-238, provides
+   various values of h along with error analysis for each.
+
+   From that paper, a very practical value of h is:
+
+       h = 4/3 * tan(angle/4)
+
+   This value does not give the spline with minimal error, but it does
+   provide a very good approximation, (6th-order convergence), and the
+   error expression is quite simple, (see the comment for
+   _arc_error_normalized).
+*/
+static void
+_cairo_arc_segment (cairo_t *cr,
+                   double   xc,
+                   double   yc,
+                   double   radius,
+                   double   angle_A,
+                   double   angle_B)
+{
+    double r_sin_A, r_cos_A;
+    double r_sin_B, r_cos_B;
+    double h;
+
+    r_sin_A = radius * sin (angle_A);
+    r_cos_A = radius * cos (angle_A);
+    r_sin_B = radius * sin (angle_B);
+    r_cos_B = radius * cos (angle_B);
+
+    h = 4.0/3.0 * tan ((angle_B - angle_A) / 4.0);
+
+    cairo_curve_to (cr,
+                   xc + r_cos_A - h * r_sin_A,
+                   yc + r_sin_A + h * r_cos_A,
+                   xc + r_cos_B + h * r_sin_B,
+                   yc + r_sin_B - h * r_cos_B,
+                   xc + r_cos_B,
+                   yc + r_sin_B);
+}
+
+static void
+_cairo_arc_in_direction (cairo_t         *cr,
+                        double            xc,
+                        double            yc,
+                        double            radius,
+                        double            angle_min,
+                        double            angle_max,
+                        cairo_direction_t dir)
+{
+    if (cairo_status (cr))
+        return;
+
+    assert (angle_max >= angle_min);
+
+    if (angle_max - angle_min > 2 * M_PI * MAX_FULL_CIRCLES) {
+       angle_max = fmod (angle_max - angle_min, 2 * M_PI);
+       angle_min = fmod (angle_min, 2 * M_PI);
+       angle_max += angle_min + 2 * M_PI * MAX_FULL_CIRCLES;
+    }
+
+    /* Recurse if drawing arc larger than pi */
+    if (angle_max - angle_min > M_PI) {
+       double angle_mid = angle_min + (angle_max - angle_min) / 2.0;
+       if (dir == CAIRO_DIRECTION_FORWARD) {
+           _cairo_arc_in_direction (cr, xc, yc, radius,
+                                    angle_min, angle_mid,
+                                    dir);
+
+           _cairo_arc_in_direction (cr, xc, yc, radius,
+                                    angle_mid, angle_max,
+                                    dir);
+       } else {
+           _cairo_arc_in_direction (cr, xc, yc, radius,
+                                    angle_mid, angle_max,
+                                    dir);
+
+           _cairo_arc_in_direction (cr, xc, yc, radius,
+                                    angle_min, angle_mid,
+                                    dir);
+       }
+    } else if (angle_max != angle_min) {
+       cairo_matrix_t ctm;
+       int i, segments;
+       double angle, angle_step;
+
+       cairo_get_matrix (cr, &ctm);
+       segments = _arc_segments_needed (angle_max - angle_min,
+                                        radius, &ctm,
+                                        cairo_get_tolerance (cr));
+       angle_step = (angle_max - angle_min) / (double) segments;
+
+       if (dir == CAIRO_DIRECTION_FORWARD) {
+           angle = angle_min;
+       } else {
+           angle = angle_max;
+           angle_step = - angle_step;
+       }
+
+       for (i = 0; i < segments; i++, angle += angle_step) {
+           _cairo_arc_segment (cr, xc, yc,
+                               radius,
+                               angle,
+                               angle + angle_step);
+       }
+    } else {
+       cairo_line_to (cr,
+                      xc + radius * cos (angle_min),
+                      yc + radius * sin (angle_min));
+    }
+}
+
+/**
+ * _cairo_arc_path
+ * @cr: a cairo context
+ * @xc: X position of the center of the arc
+ * @yc: Y position of the center of the arc
+ * @radius: the radius of the arc
+ * @angle1: the start angle, in radians
+ * @angle2: the end angle, in radians
+ *
+ * Compute a path for the given arc and append it onto the current
+ * path within @cr. The arc will be accurate within the current
+ * tolerance and given the current transformation.
+ **/
+void
+_cairo_arc_path (cairo_t *cr,
+                double   xc,
+                double   yc,
+                double   radius,
+                double   angle1,
+                double   angle2)
+{
+    _cairo_arc_in_direction (cr, xc, yc,
+                            radius,
+                            angle1, angle2,
+                            CAIRO_DIRECTION_FORWARD);
+}
+
+/**
+ * _cairo_arc_path_negative:
+ * @xc: X position of the center of the arc
+ * @yc: Y position of the center of the arc
+ * @radius: the radius of the arc
+ * @angle1: the start angle, in radians
+ * @angle2: the end angle, in radians
+ * @ctm: the current transformation matrix
+ * @tolerance: the current tolerance value
+ * @path: the path onto which the arc will be appended
+ *
+ * Compute a path for the given arc (defined in the negative
+ * direction) and append it onto the current path within @cr. The arc
+ * will be accurate within the current tolerance and given the current
+ * transformation.
+ **/
+void
+_cairo_arc_path_negative (cairo_t *cr,
+                         double   xc,
+                         double   yc,
+                         double   radius,
+                         double   angle1,
+                         double   angle2)
+{
+    _cairo_arc_in_direction (cr, xc, yc,
+                            radius,
+                            angle2, angle1,
+                            CAIRO_DIRECTION_REVERSE);
+}
diff --git a/src/cairo-array-private.h b/src/cairo-array-private.h
new file mode 100644 (file)
index 0000000..35b29e5
--- /dev/null
@@ -0,0 +1,90 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *     Carl D. Worth <cworth@cworth.org>
+ */
+
+#ifndef CAIRO_ARRAY_PRIVATE_H
+#define CAIRO_ARRAY_PRIVATE_H
+
+#include "cairo-compiler-private.h"
+#include "cairo-types-private.h"
+
+CAIRO_BEGIN_DECLS
+
+/* cairo-array.c structures and functions */
+
+cairo_private void
+_cairo_array_init (cairo_array_t *array, unsigned int element_size);
+
+cairo_private void
+_cairo_array_fini (cairo_array_t *array);
+
+cairo_private cairo_status_t
+_cairo_array_grow_by (cairo_array_t *array, unsigned int additional);
+
+cairo_private void
+_cairo_array_truncate (cairo_array_t *array, unsigned int num_elements);
+
+cairo_private cairo_status_t
+_cairo_array_append (cairo_array_t *array, const void *element);
+
+cairo_private cairo_status_t
+_cairo_array_append_multiple (cairo_array_t    *array,
+                             const void        *elements,
+                             unsigned int       num_elements);
+
+cairo_private cairo_status_t
+_cairo_array_allocate (cairo_array_t    *array,
+                      unsigned int       num_elements,
+                      void             **elements);
+
+cairo_private void *
+_cairo_array_index (cairo_array_t *array, unsigned int index);
+
+cairo_private const void *
+_cairo_array_index_const (const cairo_array_t *array, unsigned int index);
+
+cairo_private void
+_cairo_array_copy_element (const cairo_array_t *array, unsigned int index, void *dst);
+
+cairo_private unsigned int
+_cairo_array_num_elements (const cairo_array_t *array);
+
+cairo_private unsigned int
+_cairo_array_size (const cairo_array_t *array);
+
+CAIRO_END_DECLS
+
+#endif /* CAIRO_ARRAY_PRIVATE_H */
diff --git a/src/cairo-array.c b/src/cairo-array.c
new file mode 100644 (file)
index 0000000..4f3c082
--- /dev/null
@@ -0,0 +1,526 @@
+/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2004 Red Hat, Inc
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *     Kristian Høgsberg <krh@redhat.com>
+ *     Carl Worth <cworth@cworth.org>
+ */
+
+#include "cairoint.h"
+#include "cairo-array-private.h"
+#include "cairo-error-private.h"
+
+/**
+ * _cairo_array_init:
+ *
+ * Initialize a new #cairo_array_t object to store objects each of size
+ * @element_size.
+ *
+ * The #cairo_array_t object provides grow-by-doubling storage. It
+ * never interprets the data passed to it, nor does it provide any
+ * sort of callback mechanism for freeing resources held onto by
+ * stored objects.
+ *
+ * When finished using the array, _cairo_array_fini() should be
+ * called to free resources allocated during use of the array.
+ **/
+void
+_cairo_array_init (cairo_array_t *array, unsigned int element_size)
+{
+    array->size = 0;
+    array->num_elements = 0;
+    array->element_size = element_size;
+    array->elements = NULL;
+}
+
+/**
+ * _cairo_array_fini:
+ * @array: A #cairo_array_t
+ *
+ * Free all resources associated with @array. After this call, @array
+ * should not be used again without a subsequent call to
+ * _cairo_array_init() again first.
+ **/
+void
+_cairo_array_fini (cairo_array_t *array)
+{
+    free (array->elements);
+}
+
+/**
+ * _cairo_array_grow_by:
+ * @array: a #cairo_array_t
+ *
+ * Increase the size of @array (if needed) so that there are at least
+ * @additional free spaces in the array. The actual size of the array
+ * is always increased by doubling as many times as necessary.
+ **/
+cairo_status_t
+_cairo_array_grow_by (cairo_array_t *array, unsigned int additional)
+{
+    char *new_elements;
+    unsigned int old_size = array->size;
+    unsigned int required_size = array->num_elements + additional;
+    unsigned int new_size;
+
+    /* check for integer overflow */
+    if (required_size > INT_MAX || required_size < array->num_elements)
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    if (CAIRO_INJECT_FAULT ())
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    if (required_size <= old_size)
+       return CAIRO_STATUS_SUCCESS;
+
+    if (old_size == 0)
+       new_size = 1;
+    else
+       new_size = old_size * 2;
+
+    while (new_size < required_size)
+       new_size = new_size * 2;
+
+    array->size = new_size;
+    new_elements = _cairo_realloc_ab (array->elements,
+                                     array->size, array->element_size);
+
+    if (unlikely (new_elements == NULL)) {
+       array->size = old_size;
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+    }
+
+    array->elements = new_elements;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+/**
+ * _cairo_array_truncate:
+ * @array: a #cairo_array_t
+ *
+ * Truncate size of the array to @num_elements if less than the
+ * current size. No memory is actually freed. The stored objects
+ * beyond @num_elements are simply "forgotten".
+ **/
+void
+_cairo_array_truncate (cairo_array_t *array, unsigned int num_elements)
+{
+    if (num_elements < array->num_elements)
+       array->num_elements = num_elements;
+}
+
+/**
+ * _cairo_array_index:
+ * @array: a #cairo_array_t
+ * Returns: A pointer to the object stored at @index.
+ *
+ * If the resulting value is assigned to a pointer to an object of the same
+ * element_size as initially passed to _cairo_array_init() then that
+ * pointer may be used for further direct indexing with []. For
+ * example:
+ *
+ * <informalexample><programlisting>
+ *     cairo_array_t array;
+ *     double *values;
+ *
+ *     _cairo_array_init (&array, sizeof(double));
+ *     ... calls to _cairo_array_append() here ...
+ *
+ *     values = _cairo_array_index (&array, 0);
+ *      for (i = 0; i < _cairo_array_num_elements (&array); i++)
+ *         ... use values[i] here ...
+ * </programlisting></informalexample>
+ **/
+void *
+_cairo_array_index (cairo_array_t *array, unsigned int index)
+{
+    /* We allow an index of 0 for the no-elements case.
+     * This makes for cleaner calling code which will often look like:
+     *
+     *    elements = _cairo_array_index (array, 0);
+     *   for (i=0; i < num_elements; i++) {
+     *        ... use elements[i] here ...
+     *    }
+     *
+     * which in the num_elements==0 case gets the NULL pointer here,
+     * but never dereferences it.
+     */
+    if (index == 0 && array->num_elements == 0)
+       return NULL;
+
+    assert (index < array->num_elements);
+
+    return array->elements + index * array->element_size;
+}
+
+/**
+ * _cairo_array_index_const:
+ * @array: a #cairo_array_t
+ * Returns: A pointer to the object stored at @index.
+ *
+ * If the resulting value is assigned to a pointer to an object of the same
+ * element_size as initially passed to _cairo_array_init() then that
+ * pointer may be used for further direct indexing with []. For
+ * example:
+ *
+ * <informalexample><programlisting>
+ *     cairo_array_t array;
+ *     const double *values;
+ *
+ *     _cairo_array_init (&array, sizeof(double));
+ *     ... calls to _cairo_array_append() here ...
+ *
+ *     values = _cairo_array_index_const (&array, 0);
+ *      for (i = 0; i < _cairo_array_num_elements (&array); i++)
+ *         ... read values[i] here ...
+ * </programlisting></informalexample>
+ **/
+const void *
+_cairo_array_index_const (const cairo_array_t *array, unsigned int index)
+{
+    /* We allow an index of 0 for the no-elements case.
+     * This makes for cleaner calling code which will often look like:
+     *
+     *    elements = _cairo_array_index_const (array, 0);
+     *   for (i=0; i < num_elements; i++) {
+     *        ... read elements[i] here ...
+     *    }
+     *
+     * which in the num_elements==0 case gets the NULL pointer here,
+     * but never dereferences it.
+     */
+    if (index == 0 && array->num_elements == 0)
+       return NULL;
+
+    assert (index < array->num_elements);
+
+    return array->elements + index * array->element_size;
+}
+
+/**
+ * _cairo_array_copy_element:
+ * @array: a #cairo_array_t
+ *
+ * Copy a single element out of the array from index @index into the
+ * location pointed to by @dst.
+ **/
+void
+_cairo_array_copy_element (const cairo_array_t *array,
+                          unsigned int         index,
+                          void                *dst)
+{
+    memcpy (dst, _cairo_array_index_const (array, index), array->element_size);
+}
+
+/**
+ * _cairo_array_append:
+ * @array: a #cairo_array_t
+ *
+ * Append a single item onto the array by growing the array by at
+ * least one element, then copying element_size bytes from @element
+ * into the array. The address of the resulting object within the
+ * array can be determined with:
+ *
+ * _cairo_array_index (array, _cairo_array_num_elements (array) - 1);
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS if successful or
+ * %CAIRO_STATUS_NO_MEMORY if insufficient memory is available for the
+ * operation.
+ **/
+cairo_status_t
+_cairo_array_append (cairo_array_t     *array,
+                    const void         *element)
+{
+    return _cairo_array_append_multiple (array, element, 1);
+}
+
+/**
+ * _cairo_array_append_multiple:
+ * @array: a #cairo_array_t
+ *
+ * Append one or more items onto the array by growing the array by
+ * @num_elements, then copying @num_elements * element_size bytes from
+ * @elements into the array.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS if successful or
+ * %CAIRO_STATUS_NO_MEMORY if insufficient memory is available for the
+ * operation.
+ **/
+cairo_status_t
+_cairo_array_append_multiple (cairo_array_t    *array,
+                             const void        *elements,
+                             unsigned int       num_elements)
+{
+    cairo_status_t status;
+    void *dest;
+
+    status = _cairo_array_allocate (array, num_elements, &dest);
+    if (unlikely (status))
+       return status;
+
+    memcpy (dest, elements, num_elements * array->element_size);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+/**
+ * _cairo_array_allocate:
+ * @array: a #cairo_array_t
+ *
+ * Allocate space at the end of the array for @num_elements additional
+ * elements, providing the address of the new memory chunk in
+ * @elements. This memory will be unitialized, but will be accounted
+ * for in the return value of _cairo_array_num_elements().
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS if successful or
+ * %CAIRO_STATUS_NO_MEMORY if insufficient memory is available for the
+ * operation.
+ **/
+cairo_status_t
+_cairo_array_allocate (cairo_array_t    *array,
+                      unsigned int       num_elements,
+                      void             **elements)
+{
+    cairo_status_t status;
+
+    status = _cairo_array_grow_by (array, num_elements);
+    if (unlikely (status))
+       return status;
+
+    assert (array->num_elements + num_elements <= array->size);
+
+    *elements = array->elements + array->num_elements * array->element_size;
+
+    array->num_elements += num_elements;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+/**
+ * _cairo_array_num_elements:
+ * @array: a #cairo_array_t
+ * Returns: The number of elements stored in @array.
+ *
+ * This space was left intentionally blank, but gtk-doc filled it.
+ **/
+unsigned int
+_cairo_array_num_elements (const cairo_array_t *array)
+{
+    return array->num_elements;
+}
+
+/**
+ * _cairo_array_size:
+ * @array: a #cairo_array_t
+ * Returns: The number of elements for which there is currently space
+ * allocated in @array.
+ *
+ * This space was left intentionally blank, but gtk-doc filled it.
+ **/
+unsigned int
+_cairo_array_size (const cairo_array_t *array)
+{
+    return array->size;
+}
+
+/**
+ * _cairo_user_data_array_init:
+ * @array: a #cairo_user_data_array_t
+ *
+ * Initializes a #cairo_user_data_array_t structure for future
+ * use. After initialization, the array has no keys. Call
+ * _cairo_user_data_array_fini() to free any allocated memory
+ * when done using the array.
+ **/
+void
+_cairo_user_data_array_init (cairo_user_data_array_t *array)
+{
+    _cairo_array_init (array, sizeof (cairo_user_data_slot_t));
+}
+
+/**
+ * _cairo_user_data_array_fini:
+ * @array: a #cairo_user_data_array_t
+ *
+ * Destroys all current keys in the user data array and deallocates
+ * any memory allocated for the array itself.
+ **/
+void
+_cairo_user_data_array_fini (cairo_user_data_array_t *array)
+{
+    unsigned int num_slots;
+
+    num_slots = array->num_elements;
+    if (num_slots) {
+       cairo_user_data_slot_t *slots;
+
+       slots = _cairo_array_index (array, 0);
+       while (num_slots--) {
+           cairo_user_data_slot_t *s = &slots[num_slots];
+           if (s->user_data != NULL && s->destroy != NULL)
+               s->destroy (s->user_data);
+       }
+    }
+
+    _cairo_array_fini (array);
+}
+
+/**
+ * _cairo_user_data_array_get_data:
+ * @array: a #cairo_user_data_array_t
+ * @key: the address of the #cairo_user_data_key_t the user data was
+ * attached to
+ *
+ * Returns user data previously attached using the specified
+ * key.  If no user data has been attached with the given key this
+ * function returns %NULL.
+ *
+ * Return value: the user data previously attached or %NULL.
+ **/
+void *
+_cairo_user_data_array_get_data (cairo_user_data_array_t     *array,
+                                const cairo_user_data_key_t *key)
+{
+    int i, num_slots;
+    cairo_user_data_slot_t *slots;
+
+    /* We allow this to support degenerate objects such as cairo_surface_nil. */
+    if (array == NULL)
+       return NULL;
+
+    num_slots = array->num_elements;
+    slots = _cairo_array_index (array, 0);
+    for (i = 0; i < num_slots; i++) {
+       if (slots[i].key == key)
+           return slots[i].user_data;
+    }
+
+    return NULL;
+}
+
+/**
+ * _cairo_user_data_array_set_data:
+ * @array: a #cairo_user_data_array_t
+ * @key: the address of a #cairo_user_data_key_t to attach the user data to
+ * @user_data: the user data to attach
+ * @destroy: a #cairo_destroy_func_t which will be called when the
+ * user data array is destroyed or when new user data is attached using the
+ * same key.
+ *
+ * Attaches user data to a user data array.  To remove user data,
+ * call this function with the key that was used to set it and %NULL
+ * for @data.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY if a
+ * slot could not be allocated for the user data.
+ **/
+cairo_status_t
+_cairo_user_data_array_set_data (cairo_user_data_array_t     *array,
+                                const cairo_user_data_key_t *key,
+                                void                        *user_data,
+                                cairo_destroy_func_t         destroy)
+{
+    cairo_status_t status;
+    int i, num_slots;
+    cairo_user_data_slot_t *slots, *slot, new_slot;
+
+    if (user_data) {
+       new_slot.key = key;
+       new_slot.user_data = user_data;
+       new_slot.destroy = destroy;
+    } else {
+       new_slot.key = NULL;
+       new_slot.user_data = NULL;
+       new_slot.destroy = NULL;
+    }
+
+    slot = NULL;
+    num_slots = array->num_elements;
+    slots = _cairo_array_index (array, 0);
+    for (i = 0; i < num_slots; i++) {
+       if (slots[i].key == key) {
+           slot = &slots[i];
+           if (slot->destroy && slot->user_data)
+               slot->destroy (slot->user_data);
+           break;
+       }
+       if (user_data && slots[i].user_data == NULL) {
+           slot = &slots[i];   /* Have to keep searching for an exact match */
+       }
+    }
+
+    if (slot) {
+       *slot = new_slot;
+       return CAIRO_STATUS_SUCCESS;
+    }
+
+    status = _cairo_array_append (array, &new_slot);
+    if (unlikely (status))
+       return status;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_status_t
+_cairo_user_data_array_copy (cairo_user_data_array_t   *dst,
+                            const cairo_user_data_array_t      *src)
+{
+    /* discard any existing user-data */
+    if (dst->num_elements != 0) {
+       _cairo_user_data_array_fini (dst);
+       _cairo_user_data_array_init (dst);
+    }
+
+    return _cairo_array_append_multiple (dst,
+                                        _cairo_array_index_const (src, 0),
+                                        src->num_elements);
+}
+
+void
+_cairo_user_data_array_foreach (cairo_user_data_array_t     *array,
+                               void (*func) (const void *key,
+                                             void *elt,
+                                             void *closure),
+                               void *closure)
+{
+    cairo_user_data_slot_t *slots;
+    int i, num_slots;
+
+    num_slots = array->num_elements;
+    slots = _cairo_array_index (array, 0);
+    for (i = 0; i < num_slots; i++) {
+       if (slots[i].user_data != NULL)
+           func (slots[i].key, slots[i].user_data, closure);
+    }
+}
diff --git a/src/cairo-atomic-private.h b/src/cairo-atomic-private.h
new file mode 100644 (file)
index 0000000..327fed1
--- /dev/null
@@ -0,0 +1,272 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2007 Chris Wilson
+ * Copyright © 2010 Andrea Canciani
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ *     Andrea Canciani <ranma42@gmail.com>
+ */
+
+#ifndef CAIRO_ATOMIC_PRIVATE_H
+#define CAIRO_ATOMIC_PRIVATE_H
+
+# include "cairo-compiler-private.h"
+
+#if 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
+
+CAIRO_BEGIN_DECLS
+
+#if HAVE_INTEL_ATOMIC_PRIMITIVES
+
+#define HAS_ATOMIC_OPS 1
+
+typedef int cairo_atomic_int_t;
+
+#ifdef ATOMIC_OP_NEEDS_MEMORY_BARRIER
+static cairo_always_inline cairo_atomic_int_t
+_cairo_atomic_int_get (cairo_atomic_int_t *x)
+{
+    __sync_synchronize ();
+    return *x;
+}
+
+static cairo_always_inline void *
+_cairo_atomic_ptr_get (void **x)
+{
+    __sync_synchronize ();
+    return *x;
+}
+#else
+# define _cairo_atomic_int_get(x) (*x)
+# define _cairo_atomic_ptr_get(x) (*x)
+#endif
+
+# define _cairo_atomic_int_inc(x) ((void) __sync_fetch_and_add(x, 1))
+# define _cairo_atomic_int_dec(x) ((void) __sync_fetch_and_add(x, -1))
+# define _cairo_atomic_int_dec_and_test(x) (__sync_fetch_and_add(x, -1) == 1)
+# define _cairo_atomic_int_cmpxchg(x, oldv, newv) __sync_bool_compare_and_swap (x, oldv, newv)
+# define _cairo_atomic_int_cmpxchg_return_old(x, oldv, newv) __sync_val_compare_and_swap (x, oldv, newv)
+
+#if SIZEOF_VOID_P==SIZEOF_INT
+typedef int cairo_atomic_intptr_t;
+#elif SIZEOF_VOID_P==SIZEOF_LONG
+typedef long cairo_atomic_intptr_t;
+#elif SIZEOF_VOID_P==SIZEOF_LONG_LONG
+typedef long long cairo_atomic_intptr_t;
+#else
+#error No matching integer pointer type
+#endif
+
+# define _cairo_atomic_ptr_cmpxchg(x, oldv, newv) \
+    __sync_bool_compare_and_swap ((cairo_atomic_intptr_t*)x, (cairo_atomic_intptr_t)oldv, (cairo_atomic_intptr_t)newv)
+
+# define _cairo_atomic_ptr_cmpxchg_return_old(x, oldv, newv) \
+    _cairo_atomic_intptr_to_voidptr (__sync_val_compare_and_swap ((cairo_atomic_intptr_t*)x, (cairo_atomic_intptr_t)oldv, (cairo_atomic_intptr_t)newv))
+
+#endif
+
+#if HAVE_LIB_ATOMIC_OPS
+#include <atomic_ops.h>
+
+#define HAS_ATOMIC_OPS 1
+
+typedef  AO_t cairo_atomic_int_t;
+
+# define _cairo_atomic_int_get(x) (AO_load_full (x))
+
+# define _cairo_atomic_int_inc(x) ((void) AO_fetch_and_add1_full(x))
+# define _cairo_atomic_int_dec(x) ((void) AO_fetch_and_sub1_full(x))
+# define _cairo_atomic_int_dec_and_test(x) (AO_fetch_and_sub1_full(x) == 1)
+# define _cairo_atomic_int_cmpxchg(x, oldv, newv) AO_compare_and_swap_full(x, oldv, newv)
+
+#if SIZEOF_VOID_P==SIZEOF_INT
+typedef unsigned int cairo_atomic_intptr_t;
+#elif SIZEOF_VOID_P==SIZEOF_LONG
+typedef unsigned long cairo_atomic_intptr_t;
+#elif SIZEOF_VOID_P==SIZEOF_LONG_LONG
+typedef unsigned long long cairo_atomic_intptr_t;
+#else
+#error No matching integer pointer type
+#endif
+
+# define _cairo_atomic_ptr_get(x) _cairo_atomic_intptr_to_voidptr (AO_load_full (x))
+# define _cairo_atomic_ptr_cmpxchg(x, oldv, newv) \
+    _cairo_atomic_int_cmpxchg ((cairo_atomic_intptr_t*)(x), (cairo_atomic_intptr_t)oldv, (cairo_atomic_intptr_t)newv)
+
+#endif
+
+#if HAVE_OS_ATOMIC_OPS
+#include <libkern/OSAtomic.h>
+
+#define HAS_ATOMIC_OPS 1
+
+typedef int32_t cairo_atomic_int_t;
+
+# define _cairo_atomic_int_get(x) (OSMemoryBarrier(), *(x))
+
+# define _cairo_atomic_int_inc(x) ((void) OSAtomicIncrement32Barrier (x))
+# define _cairo_atomic_int_dec(x) ((void) OSAtomicDecrement32Barrier (x))
+# define _cairo_atomic_int_dec_and_test(x) (OSAtomicDecrement32Barrier (x) == 0)
+# define _cairo_atomic_int_cmpxchg(x, oldv, newv) OSAtomicCompareAndSwap32Barrier(oldv, newv, x)
+
+#if SIZEOF_VOID_P==4
+typedef int32_t cairo_atomic_intptr_t;
+# define _cairo_atomic_ptr_cmpxchg(x, oldv, newv) \
+    OSAtomicCompareAndSwap32Barrier((cairo_atomic_intptr_t)oldv, (cairo_atomic_intptr_t)newv, (cairo_atomic_intptr_t *)x)
+
+#elif SIZEOF_VOID_P==8
+typedef int64_t cairo_atomic_intptr_t;
+# define _cairo_atomic_ptr_cmpxchg(x, oldv, newv) \
+    OSAtomicCompareAndSwap64Barrier((cairo_atomic_intptr_t)oldv, (cairo_atomic_intptr_t)newv, (cairo_atomic_intptr_t *)x)
+
+#else
+#error No matching integer pointer type
+#endif
+
+# define _cairo_atomic_ptr_get(x) (OSMemoryBarrier(), *(x))
+
+#endif
+
+#ifndef HAS_ATOMIC_OPS
+
+#if SIZEOF_VOID_P==SIZEOF_INT
+typedef unsigned int cairo_atomic_intptr_t;
+#elif SIZEOF_VOID_P==SIZEOF_LONG
+typedef unsigned long cairo_atomic_intptr_t;
+#elif SIZEOF_VOID_P==SIZEOF_LONG_LONG
+typedef unsigned long long cairo_atomic_intptr_t;
+#else
+#error No matching integer pointer type
+#endif
+
+typedef cairo_atomic_intptr_t cairo_atomic_int_t;
+
+cairo_private void
+_cairo_atomic_int_inc (cairo_atomic_int_t *x);
+
+#define _cairo_atomic_int_dec(x) _cairo_atomic_int_dec_and_test(x)
+
+cairo_private cairo_bool_t
+_cairo_atomic_int_dec_and_test (cairo_atomic_int_t *x);
+
+cairo_private cairo_atomic_int_t
+_cairo_atomic_int_cmpxchg_return_old_impl (cairo_atomic_int_t *x, cairo_atomic_int_t oldv, cairo_atomic_int_t newv);
+
+cairo_private void *
+_cairo_atomic_ptr_cmpxchg_return_old_impl (void **x, void *oldv, void *newv);
+
+#define _cairo_atomic_int_cmpxchg_return_old(x, oldv, newv) _cairo_atomic_int_cmpxchg_return_old_impl (x, oldv, newv)
+#define _cairo_atomic_ptr_cmpxchg_return_old(x, oldv, newv) _cairo_atomic_ptr_cmpxchg_return_old_impl (x, oldv, newv)
+
+#ifdef ATOMIC_OP_NEEDS_MEMORY_BARRIER
+cairo_private cairo_atomic_int_t
+_cairo_atomic_int_get (cairo_atomic_int_t *x);
+# define _cairo_atomic_ptr_get(x) (void *) _cairo_atomic_int_get((cairo_atomic_int_t *) x)
+#else
+# define _cairo_atomic_int_get(x) (*x)
+# define _cairo_atomic_ptr_get(x) (*x)
+#endif
+
+#else
+
+/* Workaround GCC complaining about casts */
+static cairo_always_inline void *
+_cairo_atomic_intptr_to_voidptr (cairo_atomic_intptr_t x)
+{
+  return (void *) x;
+}
+
+static cairo_always_inline cairo_atomic_int_t
+_cairo_atomic_int_cmpxchg_return_old_fallback(cairo_atomic_int_t *x, cairo_atomic_int_t oldv, cairo_atomic_int_t newv)
+{
+    cairo_atomic_int_t curr;
+
+    do {
+        curr = _cairo_atomic_int_get (x);
+    } while (curr == oldv && !_cairo_atomic_int_cmpxchg (x, oldv, newv));
+
+    return curr;
+}
+
+static cairo_always_inline void *
+_cairo_atomic_ptr_cmpxchg_return_old_fallback(void **x, void *oldv, void *newv)
+{
+    void *curr;
+
+    do {
+        curr = _cairo_atomic_ptr_get (x);
+    } while (curr == oldv && !_cairo_atomic_ptr_cmpxchg (x, oldv, newv));
+
+    return curr;
+}
+#endif
+
+#ifndef _cairo_atomic_int_cmpxchg_return_old
+#define _cairo_atomic_int_cmpxchg_return_old(x, oldv, newv) _cairo_atomic_int_cmpxchg_return_old_fallback (x, oldv, newv)
+#endif
+
+#ifndef _cairo_atomic_ptr_cmpxchg_return_old
+#define _cairo_atomic_ptr_cmpxchg_return_old(x, oldv, newv) _cairo_atomic_ptr_cmpxchg_return_old_fallback (x, oldv, newv)
+#endif
+
+#ifndef _cairo_atomic_int_cmpxchg
+#define _cairo_atomic_int_cmpxchg(x, oldv, newv) (_cairo_atomic_int_cmpxchg_return_old (x, oldv, newv) == oldv)
+#endif
+
+#ifndef _cairo_atomic_ptr_cmpxchg
+#define _cairo_atomic_ptr_cmpxchg(x, oldv, newv) (_cairo_atomic_ptr_cmpxchg_return_old (x, oldv, newv) == oldv)
+#endif
+
+#define _cairo_atomic_uint_get(x) _cairo_atomic_int_get(x)
+#define _cairo_atomic_uint_cmpxchg(x, oldv, newv) \
+    _cairo_atomic_int_cmpxchg((cairo_atomic_int_t *)x, oldv, newv)
+
+#define _cairo_status_set_error(status, err) do { \
+    int ret__; \
+    assert (err < CAIRO_STATUS_LAST_STATUS); \
+    /* hide compiler warnings about cairo_status_t != int (gcc treats its as \
+     * an unsigned integer instead, and about ignoring the return value. */  \
+    ret__ = _cairo_atomic_int_cmpxchg ((cairo_atomic_int_t *) status, CAIRO_STATUS_SUCCESS, err); \
+    (void) ret__; \
+} while (0)
+
+CAIRO_END_DECLS
+
+#endif
diff --git a/src/cairo-atomic.c b/src/cairo-atomic.c
new file mode 100644 (file)
index 0000000..909cfea
--- /dev/null
@@ -0,0 +1,106 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2007 Chris Wilson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * Contributor(s):
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-atomic-private.h"
+#include "cairo-mutex-private.h"
+
+#ifdef HAS_ATOMIC_OPS
+COMPILE_TIME_ASSERT(sizeof(void*) == sizeof(int) ||
+                   sizeof(void*) == sizeof(long) ||
+                   sizeof(void*) == sizeof(long long));
+#else
+void
+_cairo_atomic_int_inc (cairo_atomic_intptr_t *x)
+{
+    CAIRO_MUTEX_LOCK (_cairo_atomic_mutex);
+    *x += 1;
+    CAIRO_MUTEX_UNLOCK (_cairo_atomic_mutex);
+}
+
+cairo_bool_t
+_cairo_atomic_int_dec_and_test (cairo_atomic_intptr_t *x)
+{
+    cairo_bool_t ret;
+
+    CAIRO_MUTEX_LOCK (_cairo_atomic_mutex);
+    ret = --*x == 0;
+    CAIRO_MUTEX_UNLOCK (_cairo_atomic_mutex);
+
+    return ret;
+}
+
+cairo_atomic_intptr_t
+_cairo_atomic_int_cmpxchg_return_old_impl (cairo_atomic_intptr_t *x, cairo_atomic_intptr_t oldv, cairo_atomic_intptr_t newv)
+{
+    cairo_atomic_intptr_t ret;
+
+    CAIRO_MUTEX_LOCK (_cairo_atomic_mutex);
+    ret = *x;
+    if (ret == oldv)
+       *x = newv;
+    CAIRO_MUTEX_UNLOCK (_cairo_atomic_mutex);
+
+    return ret;
+}
+
+void *
+_cairo_atomic_ptr_cmpxchg_return_old_impl (void **x, void *oldv, void *newv)
+{
+    void *ret;
+
+    CAIRO_MUTEX_LOCK (_cairo_atomic_mutex);
+    ret = *x;
+    if (ret == oldv)
+       *x = newv;
+    CAIRO_MUTEX_UNLOCK (_cairo_atomic_mutex);
+
+    return ret;
+}
+
+#ifdef ATOMIC_OP_NEEDS_MEMORY_BARRIER
+cairo_atomic_intptr_t
+_cairo_atomic_int_get (cairo_atomic_intptr_t *x)
+{
+    cairo_atomic_intptr_t ret;
+
+    CAIRO_MUTEX_LOCK (_cairo_atomic_mutex);
+    ret = *x;
+    CAIRO_MUTEX_UNLOCK (_cairo_atomic_mutex);
+
+    return ret;
+}
+#endif
+
+#endif
diff --git a/src/cairo-backend-private.h b/src/cairo-backend-private.h
new file mode 100644 (file)
index 0000000..1dd5ea0
--- /dev/null
@@ -0,0 +1,171 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2010 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Intel Corporation
+ *
+ * Contributor(s):
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#ifndef CAIRO_BACKEND_PRIVATE_H
+#define CAIRO_BACKEND_PRIVATE_H
+
+#include "cairo-types-private.h"
+
+typedef enum _cairo_backend_type {
+    CAIRO_TYPE_DEFAULT,
+    CAIRO_TYPE_SKIA,
+} cairo_backend_type_t;
+
+struct _cairo_backend {
+    cairo_backend_type_t type;
+    void (*destroy) (void *cr);
+
+    cairo_surface_t *(*get_original_target) (void *cr);
+    cairo_surface_t *(*get_current_target) (void *cr);
+
+    cairo_status_t (*save) (void *cr);
+    cairo_status_t (*restore) (void *cr);
+
+    cairo_status_t (*push_group) (void *cr, cairo_content_t content);
+    cairo_pattern_t *(*pop_group) (void *cr);
+
+    cairo_status_t (*set_source_rgba) (void *cr, double red, double green, double blue, double alpha);
+    cairo_status_t (*set_source_surface) (void *cr, cairo_surface_t *surface, double x, double y);
+    cairo_status_t (*set_source) (void *cr, cairo_pattern_t *source);
+    cairo_pattern_t *(*get_source) (void *cr);
+
+    cairo_status_t (*set_antialias) (void *cr, cairo_antialias_t antialias);
+    cairo_status_t (*set_dash) (void *cr, const double *dashes, int num_dashes, double offset);
+    cairo_status_t (*set_fill_rule) (void *cr, cairo_fill_rule_t fill_rule);
+    cairo_status_t (*set_line_cap) (void *cr, cairo_line_cap_t line_cap);
+    cairo_status_t (*set_line_join) (void *cr, cairo_line_join_t line_join);
+    cairo_status_t (*set_line_width) (void *cr, double line_width);
+    cairo_status_t (*set_miter_limit) (void *cr, double limit);
+    cairo_status_t (*set_opacity) (void *cr, double opacity);
+    cairo_status_t (*set_operator) (void *cr, cairo_operator_t op);
+    cairo_status_t (*set_tolerance) (void *cr, double tolerance);
+
+    cairo_antialias_t (*get_antialias) (void *cr);
+    void (*get_dash) (void *cr, double *dashes, int *num_dashes, double *offset);
+    cairo_fill_rule_t (*get_fill_rule) (void *cr);
+    cairo_line_cap_t (*get_line_cap) (void *cr);
+    cairo_line_join_t (*get_line_join) (void *cr);
+    double (*get_line_width) (void *cr);
+    double (*get_miter_limit) (void *cr);
+    double (*get_opacity) (void *cr);
+    cairo_operator_t (*get_operator) (void *cr);
+    double (*get_tolerance) (void *cr);
+
+    cairo_status_t (*translate) (void *cr, double tx, double ty);
+    cairo_status_t (*scale) (void *cr, double sx, double sy);
+    cairo_status_t (*rotate) (void *cr, double theta);
+    cairo_status_t (*transform) (void *cr, const cairo_matrix_t *matrix);
+    cairo_status_t (*set_matrix) (void *cr, const cairo_matrix_t *matrix);
+    cairo_status_t (*set_identity_matrix) (void *cr);
+    void (*get_matrix) (void *cr, cairo_matrix_t *matrix);
+
+    void (*user_to_device) (void *cr, double *x, double *y);
+    void (*user_to_device_distance) (void *cr, double *x, double *y);
+    void (*device_to_user) (void *cr, double *x, double *y);
+    void (*device_to_user_distance) (void *cr, double *x, double *y);
+
+    cairo_status_t (*new_path) (void *cr);
+    cairo_status_t (*new_sub_path) (void *cr);
+    cairo_status_t (*move_to) (void *cr, double x, double y);
+    cairo_status_t (*rel_move_to) (void *cr, double dx, double dy);
+    cairo_status_t (*line_to) (void *cr, double x, double y);
+    cairo_status_t (*rel_line_to) (void *cr, double dx, double dy);
+    cairo_status_t (*curve_to) (void *cr, double x1, double y1, double x2, double y2, double x3, double y3);
+    cairo_status_t (*rel_curve_to) (void *cr, double dx1, double dy1, double dx2, double dy2, double dx3, double dy3);
+    cairo_status_t (*arc_to) (void *cr, double x1, double y1, double x2, double y2, double radius);
+    cairo_status_t (*rel_arc_to) (void *cr, double dx1, double dy1, double dx2, double dy2, double radius);
+    cairo_status_t (*close_path) (void *cr);
+
+    cairo_status_t (*arc) (void *cr, double xc, double yc, double radius, double angle1, double angle2, cairo_bool_t forward);
+    cairo_status_t (*rectangle) (void *cr, double x, double y, double width, double height);
+
+    void (*path_extents) (void *cr, double *x1, double *y1, double *x2, double *y2);
+    cairo_bool_t (*has_current_point) (void *cr);
+    cairo_bool_t (*get_current_point) (void *cr, double *x, double *y);
+
+    cairo_path_t *(*copy_path) (void *cr);
+    cairo_path_t *(*copy_path_flat) (void *cr);
+    cairo_status_t (*append_path) (void *cr, const cairo_path_t *path);
+
+    cairo_status_t (*stroke_to_path) (void *cr);
+
+    cairo_status_t (*clip) (void *cr);
+    cairo_status_t (*clip_preserve) (void *cr);
+    cairo_status_t (*in_clip) (void *cr, double x, double y, cairo_bool_t *inside);
+    cairo_status_t (*clip_extents) (void *cr, double *x1, double *y1, double *x2, double *y2);
+    cairo_status_t (*reset_clip) (void *cr);
+    cairo_rectangle_list_t *(*clip_copy_rectangle_list) (void *cr);
+
+    cairo_status_t (*paint) (void *cr);
+    cairo_status_t (*paint_with_alpha) (void *cr, double opacity);
+    cairo_status_t (*mask) (void *cr, cairo_pattern_t *pattern);
+
+    cairo_status_t (*stroke) (void *cr);
+    cairo_status_t (*stroke_preserve) (void *cr);
+    cairo_status_t (*in_stroke) (void *cr, double x, double y, cairo_bool_t *inside);
+    cairo_status_t (*stroke_extents) (void *cr, double *x1, double *y1, double *x2, double *y2);
+
+    cairo_status_t (*fill) (void *cr);
+    cairo_status_t (*fill_preserve) (void *cr);
+    cairo_status_t (*in_fill) (void *cr, double x, double y, cairo_bool_t *inside);
+    cairo_status_t (*fill_extents) (void *cr, double *x1, double *y1, double *x2, double *y2);
+
+    cairo_status_t (*set_font_face) (void *cr, cairo_font_face_t *font_face);
+    cairo_font_face_t *(*get_font_face) (void *cr);
+    cairo_status_t (*set_font_size) (void *cr, double size);
+    cairo_status_t (*set_font_matrix) (void *cr, const cairo_matrix_t *matrix);
+    void (*get_font_matrix) (void *cr, cairo_matrix_t *matrix);
+    cairo_status_t (*set_font_options) (void *cr, const cairo_font_options_t *options);
+    void (*get_font_options) (void *cr, cairo_font_options_t *options);
+    cairo_status_t (*set_scaled_font) (void *cr, cairo_scaled_font_t *scaled_font);
+    cairo_scaled_font_t *(*get_scaled_font) (void *cr);
+    cairo_status_t (*font_extents) (void *cr, cairo_font_extents_t *extents);
+
+    cairo_status_t (*glyphs) (void *cr,
+                             const cairo_glyph_t *glyphs, int num_glyphs,
+                             cairo_glyph_text_info_t *info);
+    cairo_status_t (*glyph_path) (void *cr,
+                                 const cairo_glyph_t *glyphs, int num_glyphs);
+
+    cairo_status_t (*glyph_extents) (void *cr,
+                                    const cairo_glyph_t *glyphs,
+                                    int num_glyphs,
+                                    cairo_text_extents_t *extents);
+
+    cairo_status_t (*copy_page) (void *cr);
+    cairo_status_t (*show_page) (void *cr);
+};
+
+#endif /* CAIRO_BACKEND_PRIVATE_H */
diff --git a/src/cairo-base64-stream.c b/src/cairo-base64-stream.c
new file mode 100644 (file)
index 0000000..6364313
--- /dev/null
@@ -0,0 +1,144 @@
+/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2005-2007 Emmanuel Pacaud <emmanuel.pacaud@free.fr>
+ * Copyright © 2009 Chris Wilson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Author(s):
+ *     Kristian Høgsberg <krh@redhat.com>
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+#include "cairo-error-private.h"
+#include "cairo-output-stream-private.h"
+
+typedef struct _cairo_base64_stream {
+    cairo_output_stream_t base;
+    cairo_output_stream_t *output;
+    unsigned int in_mem;
+    unsigned int trailing;
+    unsigned char src[3];
+} cairo_base64_stream_t;
+
+static char const base64_table[64] =
+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+static cairo_status_t
+_cairo_base64_stream_write (cairo_output_stream_t *base,
+                           const unsigned char   *data,
+                           unsigned int           length)
+{
+    cairo_base64_stream_t * stream = (cairo_base64_stream_t *) base;
+    unsigned char *src = stream->src;
+    unsigned int i;
+
+    if (stream->in_mem + length < 3) {
+       for (i = 0; i < length; i++) {
+           src[i + stream->in_mem] = *data++;
+       }
+       stream->in_mem += length;
+       return CAIRO_STATUS_SUCCESS;
+    }
+
+    do {
+       unsigned char dst[4];
+
+       for (i = stream->in_mem; i < 3; i++) {
+           src[i] = *data++;
+           length--;
+       }
+       stream->in_mem = 0;
+
+       dst[0] = base64_table[src[0] >> 2];
+       dst[1] = base64_table[(src[0] & 0x03) << 4 | src[1] >> 4];
+       dst[2] = base64_table[(src[1] & 0x0f) << 2 | src[2] >> 6];
+       dst[3] = base64_table[src[2] & 0xfc >> 2];
+       /* Special case for the last missing bits */
+       switch (stream->trailing) {
+           case 2:
+               dst[2] = '=';
+           case 1:
+               dst[3] = '=';
+           default:
+               break;
+       }
+       _cairo_output_stream_write (stream->output, dst, 4);
+    } while (length >= 3);
+
+    for (i = 0; i < length; i++) {
+       src[i] = *data++;
+    }
+    stream->in_mem = length;
+
+    return _cairo_output_stream_get_status (stream->output);
+}
+
+static cairo_status_t
+_cairo_base64_stream_close (cairo_output_stream_t *base)
+{
+    cairo_base64_stream_t *stream = (cairo_base64_stream_t *) base;
+    cairo_status_t status = CAIRO_STATUS_SUCCESS;
+
+    if (stream->in_mem > 0) {
+       memset (stream->src + stream->in_mem, 0, 3 - stream->in_mem);
+       stream->trailing = 3 - stream->in_mem;
+       stream->in_mem = 3;
+       status = _cairo_base64_stream_write (base, NULL, 0);
+    }
+
+    return status;
+}
+
+cairo_output_stream_t *
+_cairo_base64_stream_create (cairo_output_stream_t *output)
+{
+    cairo_base64_stream_t *stream;
+
+    if (output->status)
+       return _cairo_output_stream_create_in_error (output->status);
+
+    stream = malloc (sizeof (cairo_base64_stream_t));
+    if (unlikely (stream == NULL)) {
+       _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+       return (cairo_output_stream_t *) &_cairo_output_stream_nil;
+    }
+
+    _cairo_output_stream_init (&stream->base,
+                              _cairo_base64_stream_write,
+                              NULL,
+                              _cairo_base64_stream_close);
+
+    stream->output = output;
+    stream->in_mem = 0;
+    stream->trailing = 0;
+
+    return &stream->base;
+}
diff --git a/src/cairo-base85-stream.c b/src/cairo-base85-stream.c
new file mode 100644 (file)
index 0000000..f81affb
--- /dev/null
@@ -0,0 +1,131 @@
+/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2005 Red Hat, Inc
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Author(s):
+ *     Kristian Høgsberg <krh@redhat.com>
+ */
+
+#include "cairoint.h"
+#include "cairo-error-private.h"
+#include "cairo-output-stream-private.h"
+
+typedef struct _cairo_base85_stream {
+    cairo_output_stream_t base;
+    cairo_output_stream_t *output;
+    unsigned char four_tuple[4];
+    int pending;
+} cairo_base85_stream_t;
+
+static void
+_expand_four_tuple_to_five (unsigned char four_tuple[4],
+                           unsigned char five_tuple[5],
+                           cairo_bool_t *all_zero)
+{
+    uint32_t value;
+    int digit, i;
+
+    value = four_tuple[0] << 24 | four_tuple[1] << 16 | four_tuple[2] << 8 | four_tuple[3];
+    if (all_zero)
+       *all_zero = TRUE;
+    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;
+    }
+}
+
+static cairo_status_t
+_cairo_base85_stream_write (cairo_output_stream_t *base,
+                           const unsigned char   *data,
+                           unsigned int           length)
+{
+    cairo_base85_stream_t *stream = (cairo_base85_stream_t *) base;
+    const unsigned char *ptr = data;
+    unsigned char five_tuple[5];
+    cairo_bool_t is_zero;
+
+    while (length) {
+       stream->four_tuple[stream->pending++] = *ptr++;
+       length--;
+       if (stream->pending == 4) {
+           _expand_four_tuple_to_five (stream->four_tuple, five_tuple, &is_zero);
+           if (is_zero)
+               _cairo_output_stream_write (stream->output, "z", 1);
+           else
+               _cairo_output_stream_write (stream->output, five_tuple, 5);
+           stream->pending = 0;
+       }
+    }
+
+    return _cairo_output_stream_get_status (stream->output);
+}
+
+static cairo_status_t
+_cairo_base85_stream_close (cairo_output_stream_t *base)
+{
+    cairo_base85_stream_t *stream = (cairo_base85_stream_t *) base;
+    unsigned char five_tuple[5];
+
+    if (stream->pending) {
+       memset (stream->four_tuple + stream->pending, 0, 4 - stream->pending);
+       _expand_four_tuple_to_five (stream->four_tuple, five_tuple, NULL);
+       _cairo_output_stream_write (stream->output, five_tuple, stream->pending + 1);
+    }
+
+    return _cairo_output_stream_get_status (stream->output);
+}
+
+cairo_output_stream_t *
+_cairo_base85_stream_create (cairo_output_stream_t *output)
+{
+    cairo_base85_stream_t *stream;
+
+    if (output->status)
+       return _cairo_output_stream_create_in_error (output->status);
+
+    stream = malloc (sizeof (cairo_base85_stream_t));
+    if (unlikely (stream == NULL)) {
+       _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+       return (cairo_output_stream_t *) &_cairo_output_stream_nil;
+    }
+
+    _cairo_output_stream_init (&stream->base,
+                              _cairo_base85_stream_write,
+                              NULL,
+                              _cairo_base85_stream_close);
+    stream->output = output;
+    stream->pending = 0;
+
+    return &stream->base;
+}
diff --git a/src/cairo-bentley-ottmann-rectangular.c b/src/cairo-bentley-ottmann-rectangular.c
new file mode 100644 (file)
index 0000000..3585bee
--- /dev/null
@@ -0,0 +1,885 @@
+/*
+ * Copyright © 2004 Carl Worth
+ * Copyright © 2006 Red Hat, Inc.
+ * Copyright © 2009 Chris Wilson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Carl Worth
+ *
+ * Contributor(s):
+ *     Carl D. Worth <cworth@cworth.org>
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+/* Provide definitions for standalone compilation */
+#include "cairoint.h"
+
+#include "cairo-boxes-private.h"
+#include "cairo-error-private.h"
+#include "cairo-combsort-private.h"
+#include "cairo-list-private.h"
+#include "cairo-traps-private.h"
+
+#include <setjmp.h>
+
+typedef struct _rectangle rectangle_t;
+typedef struct _edge edge_t;
+
+struct _edge {
+    edge_t *next, *prev;
+    edge_t *right;
+    cairo_fixed_t x, top;
+    int dir;
+};
+
+struct _rectangle {
+    edge_t left, right;
+    int32_t top, bottom;
+};
+
+#define UNROLL3(x) x x x
+
+/* the parent is always given by index/2 */
+#define PQ_PARENT_INDEX(i) ((i) >> 1)
+#define PQ_FIRST_ENTRY 1
+
+/* left and right children are index * 2 and (index * 2) +1 respectively */
+#define PQ_LEFT_CHILD_INDEX(i) ((i) << 1)
+
+typedef struct _sweep_line {
+    rectangle_t **rectangles;
+    rectangle_t **stop;
+    edge_t head, tail, *insert, *cursor;
+    int32_t current_y;
+    int32_t last_y;
+    int stop_size;
+
+    int32_t insert_x;
+    cairo_fill_rule_t fill_rule;
+
+    cairo_bool_t do_traps;
+    void *container;
+
+    jmp_buf unwind;
+} sweep_line_t;
+
+#define DEBUG_TRAPS 0
+
+#if DEBUG_TRAPS
+static void
+dump_traps (cairo_traps_t *traps, const char *filename)
+{
+    FILE *file;
+    int n;
+
+    if (getenv ("CAIRO_DEBUG_TRAPS") == NULL)
+       return;
+
+    file = fopen (filename, "a");
+    if (file != NULL) {
+       for (n = 0; n < traps->num_traps; n++) {
+           fprintf (file, "%d %d L:(%d, %d), (%d, %d) R:(%d, %d), (%d, %d)\n",
+                    traps->traps[n].top,
+                    traps->traps[n].bottom,
+                    traps->traps[n].left.p1.x,
+                    traps->traps[n].left.p1.y,
+                    traps->traps[n].left.p2.x,
+                    traps->traps[n].left.p2.y,
+                    traps->traps[n].right.p1.x,
+                    traps->traps[n].right.p1.y,
+                    traps->traps[n].right.p2.x,
+                    traps->traps[n].right.p2.y);
+       }
+       fprintf (file, "\n");
+       fclose (file);
+    }
+}
+#else
+#define dump_traps(traps, filename)
+#endif
+
+static inline int
+rectangle_compare_start (const rectangle_t *a,
+                        const rectangle_t *b)
+{
+    return a->top - b->top;
+}
+
+static inline int
+rectangle_compare_stop (const rectangle_t *a,
+                        const rectangle_t *b)
+{
+    return a->bottom - b->bottom;
+}
+
+static inline void
+pqueue_push (sweep_line_t *sweep, rectangle_t *rectangle)
+{
+    rectangle_t **elements;
+    int i, parent;
+
+    elements = sweep->stop;
+    for (i = ++sweep->stop_size;
+        i != PQ_FIRST_ENTRY &&
+        rectangle_compare_stop (rectangle,
+                                elements[parent = PQ_PARENT_INDEX (i)]) < 0;
+        i = parent)
+    {
+       elements[i] = elements[parent];
+    }
+
+    elements[i] = rectangle;
+}
+
+static inline void
+rectangle_pop_stop (sweep_line_t *sweep)
+{
+    rectangle_t **elements = sweep->stop;
+    rectangle_t *tail;
+    int child, i;
+
+    tail = elements[sweep->stop_size--];
+    if (sweep->stop_size == 0) {
+       elements[PQ_FIRST_ENTRY] = NULL;
+       return;
+    }
+
+    for (i = PQ_FIRST_ENTRY;
+        (child = PQ_LEFT_CHILD_INDEX (i)) <= sweep->stop_size;
+        i = child)
+    {
+       if (child != sweep->stop_size &&
+           rectangle_compare_stop (elements[child+1],
+                                   elements[child]) < 0)
+       {
+           child++;
+       }
+
+       if (rectangle_compare_stop (elements[child], tail) >= 0)
+           break;
+
+       elements[i] = elements[child];
+    }
+    elements[i] = tail;
+}
+
+static inline rectangle_t *
+rectangle_pop_start (sweep_line_t *sweep_line)
+{
+    return *sweep_line->rectangles++;
+}
+
+static inline rectangle_t *
+rectangle_peek_stop (sweep_line_t *sweep_line)
+{
+    return sweep_line->stop[PQ_FIRST_ENTRY];
+}
+
+CAIRO_COMBSORT_DECLARE (_rectangle_sort,
+                       rectangle_t *,
+                       rectangle_compare_start)
+
+static void
+sweep_line_init (sweep_line_t   *sweep_line,
+                rectangle_t    **rectangles,
+                int              num_rectangles,
+                cairo_fill_rule_t fill_rule,
+                cairo_bool_t    do_traps,
+                void           *container)
+{
+    rectangles[-2] = NULL;
+    rectangles[-1] = NULL;
+    rectangles[num_rectangles] = NULL;
+    sweep_line->rectangles = rectangles;
+    sweep_line->stop = rectangles - 2;
+    sweep_line->stop_size = 0;
+
+    sweep_line->insert = NULL;
+    sweep_line->insert_x = INT_MAX;
+    sweep_line->cursor = &sweep_line->tail;
+
+    sweep_line->head.dir = 0;
+    sweep_line->head.x = INT32_MIN;
+    sweep_line->head.right = NULL;
+    sweep_line->head.prev = NULL;
+    sweep_line->head.next = &sweep_line->tail;
+    sweep_line->tail.prev = &sweep_line->head;
+    sweep_line->tail.next = NULL;
+    sweep_line->tail.right = NULL;
+    sweep_line->tail.x = INT32_MAX;
+    sweep_line->tail.dir = 0;
+
+    sweep_line->current_y = INT32_MIN;
+    sweep_line->last_y = INT32_MIN;
+
+    sweep_line->fill_rule = fill_rule;
+    sweep_line->container = container;
+    sweep_line->do_traps = do_traps;
+}
+
+static void
+edge_end_box (sweep_line_t *sweep_line, edge_t *left, int32_t bot)
+{
+    cairo_status_t status = CAIRO_STATUS_SUCCESS;
+
+    /* Only emit (trivial) non-degenerate trapezoids with positive height. */
+    if (likely (left->top < bot)) {
+       if (sweep_line->do_traps) {
+           cairo_line_t _left = {
+               { left->x, left->top },
+               { left->x, bot },
+           }, _right = {
+               { left->right->x, left->top },
+               { left->right->x, bot },
+           };
+           _cairo_traps_add_trap (sweep_line->container, left->top, bot, &_left, &_right);
+           status = _cairo_traps_status ((cairo_traps_t *) sweep_line->container);
+       } else {
+           cairo_box_t box;
+
+           box.p1.x = left->x;
+           box.p1.y = left->top;
+           box.p2.x = left->right->x;
+           box.p2.y = bot;
+
+           status = _cairo_boxes_add (sweep_line->container,
+                                      CAIRO_ANTIALIAS_DEFAULT,
+                                      &box);
+       }
+    }
+    if (unlikely (status))
+       longjmp (sweep_line->unwind, status);
+
+    left->right = NULL;
+}
+
+/* Start a new trapezoid at the given top y coordinate, whose edges
+ * are `edge' and `edge->next'. If `edge' already has a trapezoid,
+ * then either add it to the traps in `traps', if the trapezoid's
+ * right edge differs from `edge->next', or do nothing if the new
+ * trapezoid would be a continuation of the existing one. */
+static inline void
+edge_start_or_continue_box (sweep_line_t *sweep_line,
+                           edge_t      *left,
+                           edge_t      *right,
+                           int          top)
+{
+    if (left->right == right)
+       return;
+
+    if (left->right != NULL) {
+       if (left->right->x == right->x) {
+           /* continuation on right, so just swap edges */
+           left->right = right;
+           return;
+       }
+
+       edge_end_box (sweep_line, left, top);
+    }
+
+    if (left->x != right->x) {
+       left->top = top;
+       left->right = right;
+    }
+}
+/*
+ * Merge two sorted edge lists.
+ * Input:
+ *  - head_a: The head of the first list.
+ *  - head_b: The head of the second list; head_b cannot be NULL.
+ * Output:
+ * Returns the head of the merged list.
+ *
+ * Implementation notes:
+ * To make it fast (in particular, to reduce to an insertion sort whenever
+ * one of the two input lists only has a single element) we iterate through
+ * a list until its head becomes greater than the head of the other list,
+ * then we switch their roles. As soon as one of the two lists is empty, we
+ * just attach the other one to the current list and exit.
+ * Writes to memory are only needed to "switch" lists (as it also requires
+ * attaching to the output list the list which we will be iterating next) and
+ * to attach the last non-empty list.
+ */
+static edge_t *
+merge_sorted_edges (edge_t *head_a, edge_t *head_b)
+{
+    edge_t *head, *prev;
+    int32_t x;
+
+    prev = head_a->prev;
+    if (head_a->x <= head_b->x) {
+       head = head_a;
+    } else {
+       head_b->prev = prev;
+       head = head_b;
+       goto start_with_b;
+    }
+
+    do {
+       x = head_b->x;
+       while (head_a != NULL && head_a->x <= x) {
+           prev = head_a;
+           head_a = head_a->next;
+       }
+
+       head_b->prev = prev;
+       prev->next = head_b;
+       if (head_a == NULL)
+           return head;
+
+start_with_b:
+       x = head_a->x;
+       while (head_b != NULL && head_b->x <= x) {
+           prev = head_b;
+           head_b = head_b->next;
+       }
+
+       head_a->prev = prev;
+       prev->next = head_a;
+       if (head_b == NULL)
+           return head;
+    } while (1);
+}
+
+/*
+ * Sort (part of) a list.
+ * Input:
+ *  - list: The list to be sorted; list cannot be NULL.
+ *  - limit: Recursion limit.
+ * Output:
+ *  - head_out: The head of the sorted list containing the first 2^(level+1) elements of the
+ *              input list; if the input list has fewer elements, head_out be a sorted list
+ *              containing all the elements of the input list.
+ * Returns the head of the list of unprocessed elements (NULL if the sorted list contains
+ * all the elements of the input list).
+ *
+ * Implementation notes:
+ * Special case single element list, unroll/inline the sorting of the first two elements.
+ * Some tail recursion is used since we iterate on the bottom-up solution of the problem
+ * (we start with a small sorted list and keep merging other lists of the same size to it).
+ */
+static edge_t *
+sort_edges (edge_t  *list,
+           unsigned int  level,
+           edge_t **head_out)
+{
+    edge_t *head_other, *remaining;
+    unsigned int i;
+
+    head_other = list->next;
+
+    if (head_other == NULL) {
+       *head_out = list;
+       return NULL;
+    }
+
+    remaining = head_other->next;
+    if (list->x <= head_other->x) {
+       *head_out = list;
+       head_other->next = NULL;
+    } else {
+       *head_out = head_other;
+       head_other->prev = list->prev;
+       head_other->next = list;
+       list->prev = head_other;
+       list->next = NULL;
+    }
+
+    for (i = 0; i < level && remaining; i++) {
+       remaining = sort_edges (remaining, i, &head_other);
+       *head_out = merge_sorted_edges (*head_out, head_other);
+    }
+
+    return remaining;
+}
+
+static edge_t *
+merge_unsorted_edges (edge_t *head, edge_t *unsorted)
+{
+    sort_edges (unsorted, UINT_MAX, &unsorted);
+    return merge_sorted_edges (head, unsorted);
+}
+
+static void
+active_edges_insert (sweep_line_t *sweep)
+{
+    edge_t *prev;
+    int x;
+
+    x = sweep->insert_x;
+    prev = sweep->cursor;
+    if (prev->x > x) {
+       do {
+           prev = prev->prev;
+       } while (prev->x > x);
+    } else {
+       while (prev->next->x < x)
+           prev = prev->next;
+    }
+
+    prev->next = merge_unsorted_edges (prev->next, sweep->insert);
+    sweep->cursor = sweep->insert;
+    sweep->insert = NULL;
+    sweep->insert_x = INT_MAX;
+}
+
+static inline void
+active_edges_to_traps (sweep_line_t *sweep)
+{
+    int top = sweep->current_y;
+    edge_t *pos;
+
+    if (sweep->last_y == sweep->current_y)
+       return;
+
+    if (sweep->insert)
+       active_edges_insert (sweep);
+
+    pos = sweep->head.next;
+    if (pos == &sweep->tail)
+       return;
+
+    if (sweep->fill_rule == CAIRO_FILL_RULE_WINDING) {
+       do {
+           edge_t *left, *right;
+           int winding;
+
+           left = pos;
+           winding = left->dir;
+
+           right = left->next;
+
+           /* Check if there is a co-linear edge with an existing trap */
+           while (right->x == left->x) {
+               if (right->right != NULL) {
+                   assert (left->right == NULL);
+                   /* continuation on left */
+                   left->top = right->top;
+                   left->right = right->right;
+                   right->right = NULL;
+               }
+               winding += right->dir;
+               right = right->next;
+           }
+
+           if (winding == 0) {
+               pos = right;
+               continue;
+           }
+
+           do {
+               /* End all subsumed traps */
+               if (unlikely (right->right != NULL))
+                   edge_end_box (sweep, right, top);
+
+               /* Greedily search for the closing edge, so that we generate
+                * the * maximal span width with the minimal number of
+                * boxes.
+                */
+               winding += right->dir;
+               if (winding == 0 && right->x != right->next->x)
+                   break;
+
+               right = right->next;
+           } while (TRUE);
+
+           edge_start_or_continue_box (sweep, left, right, top);
+
+           pos = right->next;
+       } while (pos != &sweep->tail);
+    } else {
+       do {
+           edge_t *right = pos->next;
+           int count = 0;
+
+           do {
+               /* End all subsumed traps */
+               if (unlikely (right->right != NULL))
+                   edge_end_box (sweep, right, top);
+
+                   /* skip co-linear edges */
+               if (++count & 1 && right->x != right->next->x)
+                   break;
+
+               right = right->next;
+           } while (TRUE);
+
+           edge_start_or_continue_box (sweep, pos, right, top);
+
+           pos = right->next;
+       } while (pos != &sweep->tail);
+    }
+
+    sweep->last_y = sweep->current_y;
+}
+
+static inline void
+sweep_line_delete_edge (sweep_line_t *sweep, edge_t *edge)
+{
+    if (edge->right != NULL) {
+       edge_t *next = edge->next;
+       if (next->x == edge->x) {
+           next->top = edge->top;
+           next->right = edge->right;
+       } else
+           edge_end_box (sweep, edge, sweep->current_y);
+    }
+
+    if (sweep->cursor == edge)
+       sweep->cursor = edge->prev;
+
+    edge->prev->next = edge->next;
+    edge->next->prev = edge->prev;
+}
+
+static inline cairo_bool_t
+sweep_line_delete (sweep_line_t        *sweep, rectangle_t     *rectangle)
+{
+    cairo_bool_t update;
+
+    update = TRUE;
+    if (sweep->fill_rule == CAIRO_FILL_RULE_WINDING &&
+       rectangle->left.prev->dir == rectangle->left.dir)
+    {
+       update = rectangle->left.next != &rectangle->right;
+    }
+
+    sweep_line_delete_edge (sweep, &rectangle->left);
+    sweep_line_delete_edge (sweep, &rectangle->right);
+
+    rectangle_pop_stop (sweep);
+    return update;
+}
+
+static inline void
+sweep_line_insert (sweep_line_t        *sweep, rectangle_t *rectangle)
+{
+    if (sweep->insert)
+       sweep->insert->prev = &rectangle->right;
+    rectangle->right.next = sweep->insert;
+    rectangle->right.prev = &rectangle->left;
+    rectangle->left.next = &rectangle->right;
+    rectangle->left.prev = NULL;
+    sweep->insert = &rectangle->left;
+    if (rectangle->left.x < sweep->insert_x)
+       sweep->insert_x = rectangle->left.x;
+
+    pqueue_push (sweep, rectangle);
+}
+
+static cairo_status_t
+_cairo_bentley_ottmann_tessellate_rectangular (rectangle_t     **rectangles,
+                                              int                        num_rectangles,
+                                              cairo_fill_rule_t          fill_rule,
+                                              cairo_bool_t              do_traps,
+                                              void                     *container)
+{
+    sweep_line_t sweep_line;
+    rectangle_t *rectangle;
+    cairo_status_t status;
+    cairo_bool_t update = FALSE;
+
+    sweep_line_init (&sweep_line,
+                    rectangles, num_rectangles,
+                    fill_rule,
+                    do_traps, container);
+    if ((status = setjmp (sweep_line.unwind)))
+       return status;
+
+    rectangle = rectangle_pop_start (&sweep_line);
+    do {
+       if (rectangle->top != sweep_line.current_y) {
+           rectangle_t *stop;
+
+           stop = rectangle_peek_stop (&sweep_line);
+           while (stop != NULL && stop->bottom < rectangle->top) {
+               if (stop->bottom != sweep_line.current_y) {
+                   if (update) {
+                       active_edges_to_traps (&sweep_line);
+                       update = FALSE;
+                   }
+
+                   sweep_line.current_y = stop->bottom;
+               }
+
+               update |= sweep_line_delete (&sweep_line, stop);
+               stop = rectangle_peek_stop (&sweep_line);
+           }
+
+           if (update) {
+               active_edges_to_traps (&sweep_line);
+               update = FALSE;
+           }
+
+           sweep_line.current_y = rectangle->top;
+       }
+
+       do {
+           sweep_line_insert (&sweep_line, rectangle);
+       } while ((rectangle = rectangle_pop_start (&sweep_line)) != NULL &&
+                sweep_line.current_y == rectangle->top);
+       update = TRUE;
+    } while (rectangle);
+
+    while ((rectangle = rectangle_peek_stop (&sweep_line)) != NULL) {
+       if (rectangle->bottom != sweep_line.current_y) {
+           if (update) {
+               active_edges_to_traps (&sweep_line);
+               update = FALSE;
+           }
+
+           sweep_line.current_y = rectangle->bottom;
+       }
+
+       update |= sweep_line_delete (&sweep_line, rectangle);
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_status_t
+_cairo_bentley_ottmann_tessellate_rectangular_traps (cairo_traps_t *traps,
+                                                    cairo_fill_rule_t fill_rule)
+{
+    rectangle_t stack_rectangles[CAIRO_STACK_ARRAY_LENGTH (rectangle_t)];
+    rectangle_t *stack_rectangles_ptrs[ARRAY_LENGTH (stack_rectangles) + 3];
+    rectangle_t *rectangles, **rectangles_ptrs;
+    cairo_status_t status;
+    int i;
+
+    if (unlikely (traps->num_traps <= 1))
+       return CAIRO_STATUS_SUCCESS;
+
+    assert (traps->is_rectangular);
+
+    dump_traps (traps, "bo-rects-traps-in.txt");
+
+    rectangles = stack_rectangles;
+    rectangles_ptrs = stack_rectangles_ptrs;
+    if (traps->num_traps > ARRAY_LENGTH (stack_rectangles)) {
+       rectangles = _cairo_malloc_ab_plus_c (traps->num_traps,
+                                             sizeof (rectangle_t) +
+                                             sizeof (rectangle_t *),
+                                             3*sizeof (rectangle_t *));
+       if (unlikely (rectangles == NULL))
+           return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+       rectangles_ptrs = (rectangle_t **) (rectangles + traps->num_traps);
+    }
+
+    for (i = 0; i < traps->num_traps; i++) {
+       if (traps->traps[i].left.p1.x < traps->traps[i].right.p1.x) {
+           rectangles[i].left.x = traps->traps[i].left.p1.x;
+           rectangles[i].left.dir = 1;
+
+           rectangles[i].right.x = traps->traps[i].right.p1.x;
+           rectangles[i].right.dir = -1;
+       } else {
+           rectangles[i].right.x = traps->traps[i].left.p1.x;
+           rectangles[i].right.dir = 1;
+
+           rectangles[i].left.x = traps->traps[i].right.p1.x;
+           rectangles[i].left.dir = -1;
+       }
+
+       rectangles[i].left.right = NULL;
+       rectangles[i].right.right = NULL;
+
+       rectangles[i].top = traps->traps[i].top;
+       rectangles[i].bottom = traps->traps[i].bottom;
+
+       rectangles_ptrs[i+2] = &rectangles[i];
+    }
+    /* XXX incremental sort */
+    _rectangle_sort (rectangles_ptrs+2, i);
+
+    _cairo_traps_clear (traps);
+    status = _cairo_bentley_ottmann_tessellate_rectangular (rectangles_ptrs+2, i,
+                                                           fill_rule,
+                                                           TRUE, traps);
+    traps->is_rectilinear = TRUE;
+    traps->is_rectangular = TRUE;
+
+    if (rectangles != stack_rectangles)
+       free (rectangles);
+
+    dump_traps (traps, "bo-rects-traps-out.txt");
+
+    return status;
+}
+
+cairo_status_t
+_cairo_bentley_ottmann_tessellate_boxes (const cairo_boxes_t *in,
+                                        cairo_fill_rule_t fill_rule,
+                                        cairo_boxes_t *out)
+{
+    rectangle_t stack_rectangles[CAIRO_STACK_ARRAY_LENGTH (rectangle_t)];
+    rectangle_t *stack_rectangles_ptrs[ARRAY_LENGTH (stack_rectangles) + 3];
+    rectangle_t *rectangles, **rectangles_ptrs;
+    rectangle_t *stack_rectangles_chain[CAIRO_STACK_ARRAY_LENGTH (rectangle_t *) ];
+    rectangle_t **rectangles_chain = NULL;
+    const struct _cairo_boxes_chunk *chunk;
+    cairo_status_t status;
+    int i, j, y_min, y_max;
+
+    if (unlikely (in->num_boxes == 0)) {
+       _cairo_boxes_clear (out);
+       return CAIRO_STATUS_SUCCESS;
+    }
+
+    if (in->num_boxes == 1) {
+       if (in == out) {
+           cairo_box_t *box = &in->chunks.base[0];
+
+           if (box->p1.x > box->p2.x) {
+               cairo_fixed_t tmp = box->p1.x;
+               box->p1.x = box->p2.x;
+               box->p2.x = tmp;
+           }
+       } else {
+           cairo_box_t box = in->chunks.base[0];
+
+           if (box.p1.x > box.p2.x) {
+               cairo_fixed_t tmp = box.p1.x;
+               box.p1.x = box.p2.x;
+               box.p2.x = tmp;
+           }
+
+           _cairo_boxes_clear (out);
+           status = _cairo_boxes_add (out, CAIRO_ANTIALIAS_DEFAULT, &box);
+           assert (status == CAIRO_STATUS_SUCCESS);
+       }
+       return CAIRO_STATUS_SUCCESS;
+    }
+
+    y_min = INT_MAX; y_max = INT_MIN;
+    for (chunk = &in->chunks; chunk != NULL; chunk = chunk->next) {
+       const cairo_box_t *box = chunk->base;
+       for (i = 0; i < chunk->count; i++) {
+           if (box[i].p1.y < y_min)
+               y_min = box[i].p1.y;
+           if (box[i].p1.y > y_max)
+               y_max = box[i].p1.y;
+       }
+    }
+    y_min = _cairo_fixed_integer_floor (y_min);
+    y_max = _cairo_fixed_integer_floor (y_max) + 1;
+    y_max -= y_min;
+
+    if (y_max < in->num_boxes) {
+       rectangles_chain = stack_rectangles_chain;
+       if (y_max > ARRAY_LENGTH (stack_rectangles_chain)) {
+           rectangles_chain = _cairo_malloc_ab (y_max, sizeof (rectangle_t *));
+           if (unlikely (rectangles_chain == NULL))
+               return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+       }
+       memset (rectangles_chain, 0, y_max * sizeof (rectangle_t*));
+    }
+
+    rectangles = stack_rectangles;
+    rectangles_ptrs = stack_rectangles_ptrs;
+    if (in->num_boxes > ARRAY_LENGTH (stack_rectangles)) {
+       rectangles = _cairo_malloc_ab_plus_c (in->num_boxes,
+                                             sizeof (rectangle_t) +
+                                             sizeof (rectangle_t *),
+                                             3*sizeof (rectangle_t *));
+       if (unlikely (rectangles == NULL)) {
+           if (rectangles_chain != stack_rectangles_chain)
+               free (rectangles_chain);
+           return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+       }
+
+       rectangles_ptrs = (rectangle_t **) (rectangles + in->num_boxes);
+    }
+
+    j = 0;
+    for (chunk = &in->chunks; chunk != NULL; chunk = chunk->next) {
+       const cairo_box_t *box = chunk->base;
+       for (i = 0; i < chunk->count; i++) {
+           int h;
+
+           if (box[i].p1.x < box[i].p2.x) {
+               rectangles[j].left.x = box[i].p1.x;
+               rectangles[j].left.dir = 1;
+
+               rectangles[j].right.x = box[i].p2.x;
+               rectangles[j].right.dir = -1;
+           } else if (box[i].p1.x > box[i].p2.x) {
+               rectangles[j].right.x = box[i].p1.x;
+               rectangles[j].right.dir = 1;
+
+               rectangles[j].left.x = box[i].p2.x;
+               rectangles[j].left.dir = -1;
+           } else {
+               continue;
+           }
+
+           rectangles[j].left.right = NULL;
+           rectangles[j].right.right = NULL;
+
+           rectangles[j].top = box[i].p1.y;
+           rectangles[j].bottom = box[i].p2.y;
+
+           if (rectangles_chain) {
+               h = _cairo_fixed_integer_floor (box[i].p1.y) - y_min;
+               rectangles[j].left.next = (edge_t *)rectangles_chain[h];
+               rectangles_chain[h] = &rectangles[j];
+           } else {
+               rectangles_ptrs[j+2] = &rectangles[j];
+           }
+           j++;
+       }
+    }
+
+    if (rectangles_chain) {
+       j = 2;
+       for (y_min = 0; y_min < y_max; y_min++) {
+           rectangle_t *r;
+           int start = j;
+           for (r = rectangles_chain[y_min]; r; r = (rectangle_t *)r->left.next)
+               rectangles_ptrs[j++] = r;
+           if (j > start + 1)
+               _rectangle_sort (rectangles_ptrs + start, j - start);
+       }
+
+       if (rectangles_chain != stack_rectangles_chain)
+           free (rectangles_chain);
+
+       j -= 2;
+    } else {
+       _rectangle_sort (rectangles_ptrs + 2, j);
+    }
+
+    _cairo_boxes_clear (out);
+    status = _cairo_bentley_ottmann_tessellate_rectangular (rectangles_ptrs+2, j,
+                                                           fill_rule,
+                                                           FALSE, out);
+    if (rectangles != stack_rectangles)
+       free (rectangles);
+
+    return status;
+}
diff --git a/src/cairo-bentley-ottmann-rectilinear.c b/src/cairo-bentley-ottmann-rectilinear.c
new file mode 100644 (file)
index 0000000..1edeeb5
--- /dev/null
@@ -0,0 +1,600 @@
+/*
+ * Copyright © 2004 Carl Worth
+ * Copyright © 2006 Red Hat, Inc.
+ * Copyright © 2008 Chris Wilson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Carl Worth
+ *
+ * Contributor(s):
+ *     Carl D. Worth <cworth@cworth.org>
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+/* Provide definitions for standalone compilation */
+#include "cairoint.h"
+
+#include "cairo-boxes-private.h"
+#include "cairo-combsort-private.h"
+#include "cairo-error-private.h"
+#include "cairo-traps-private.h"
+
+typedef struct _cairo_bo_edge cairo_bo_edge_t;
+typedef struct _cairo_bo_trap cairo_bo_trap_t;
+
+/* A deferred trapezoid of an edge */
+struct _cairo_bo_trap {
+    cairo_bo_edge_t *right;
+    int32_t top;
+};
+
+struct _cairo_bo_edge {
+    cairo_edge_t edge;
+    cairo_bo_edge_t *prev;
+    cairo_bo_edge_t *next;
+    cairo_bo_trap_t deferred_trap;
+};
+
+typedef enum {
+    CAIRO_BO_EVENT_TYPE_START,
+    CAIRO_BO_EVENT_TYPE_STOP
+} cairo_bo_event_type_t;
+
+typedef struct _cairo_bo_event {
+    cairo_bo_event_type_t type;
+    cairo_point_t point;
+    cairo_bo_edge_t *edge;
+} cairo_bo_event_t;
+
+typedef struct _cairo_bo_sweep_line {
+    cairo_bo_event_t **events;
+    cairo_bo_edge_t *head;
+    cairo_bo_edge_t *stopped;
+    int32_t current_y;
+    cairo_bo_edge_t *current_edge;
+} cairo_bo_sweep_line_t;
+
+static inline int
+_cairo_point_compare (const cairo_point_t *a,
+                     const cairo_point_t *b)
+{
+    int cmp;
+
+    cmp = a->y - b->y;
+    if (likely (cmp))
+       return cmp;
+
+    return a->x - b->x;
+}
+
+static inline int
+_cairo_bo_edge_compare (const cairo_bo_edge_t  *a,
+                       const cairo_bo_edge_t   *b)
+{
+    int cmp;
+
+    cmp = a->edge.line.p1.x - b->edge.line.p1.x;
+    if (likely (cmp))
+       return cmp;
+
+    return b->edge.bottom - a->edge.bottom;
+}
+
+static inline int
+cairo_bo_event_compare (const cairo_bo_event_t *a,
+                       const cairo_bo_event_t *b)
+{
+    int cmp;
+
+    cmp = _cairo_point_compare (&a->point, &b->point);
+    if (likely (cmp))
+       return cmp;
+
+    cmp = a->type - b->type;
+    if (cmp)
+       return cmp;
+
+    return a - b;
+}
+
+static inline cairo_bo_event_t *
+_cairo_bo_event_dequeue (cairo_bo_sweep_line_t *sweep_line)
+{
+    return *sweep_line->events++;
+}
+
+CAIRO_COMBSORT_DECLARE (_cairo_bo_event_queue_sort,
+                       cairo_bo_event_t *,
+                       cairo_bo_event_compare)
+
+static void
+_cairo_bo_sweep_line_init (cairo_bo_sweep_line_t *sweep_line,
+                          cairo_bo_event_t     **events,
+                          int                    num_events)
+{
+    _cairo_bo_event_queue_sort (events, num_events);
+    events[num_events] = NULL;
+    sweep_line->events = events;
+
+    sweep_line->head = NULL;
+    sweep_line->current_y = INT32_MIN;
+    sweep_line->current_edge = NULL;
+}
+
+static void
+_cairo_bo_sweep_line_insert (cairo_bo_sweep_line_t     *sweep_line,
+                            cairo_bo_edge_t            *edge)
+{
+    if (sweep_line->current_edge != NULL) {
+       cairo_bo_edge_t *prev, *next;
+       int cmp;
+
+       cmp = _cairo_bo_edge_compare (sweep_line->current_edge, edge);
+       if (cmp < 0) {
+           prev = sweep_line->current_edge;
+           next = prev->next;
+           while (next != NULL && _cairo_bo_edge_compare (next, edge) < 0)
+               prev = next, next = prev->next;
+
+           prev->next = edge;
+           edge->prev = prev;
+           edge->next = next;
+           if (next != NULL)
+               next->prev = edge;
+       } else if (cmp > 0) {
+           next = sweep_line->current_edge;
+           prev = next->prev;
+           while (prev != NULL && _cairo_bo_edge_compare (prev, edge) > 0)
+               next = prev, prev = next->prev;
+
+           next->prev = edge;
+           edge->next = next;
+           edge->prev = prev;
+           if (prev != NULL)
+               prev->next = edge;
+           else
+               sweep_line->head = edge;
+       } else {
+           prev = sweep_line->current_edge;
+           edge->prev = prev;
+           edge->next = prev->next;
+           if (prev->next != NULL)
+               prev->next->prev = edge;
+           prev->next = edge;
+       }
+    } else {
+       sweep_line->head = edge;
+    }
+
+    sweep_line->current_edge = edge;
+}
+
+static void
+_cairo_bo_sweep_line_delete (cairo_bo_sweep_line_t     *sweep_line,
+                            cairo_bo_edge_t    *edge)
+{
+    if (edge->prev != NULL)
+       edge->prev->next = edge->next;
+    else
+       sweep_line->head = edge->next;
+
+    if (edge->next != NULL)
+       edge->next->prev = edge->prev;
+
+    if (sweep_line->current_edge == edge)
+       sweep_line->current_edge = edge->prev ? edge->prev : edge->next;
+}
+
+static inline cairo_bool_t
+edges_collinear (const cairo_bo_edge_t *a, const cairo_bo_edge_t *b)
+{
+    return a->edge.line.p1.x == b->edge.line.p1.x;
+}
+
+static cairo_status_t
+_cairo_bo_edge_end_trap (cairo_bo_edge_t       *left,
+                        int32_t                 bot,
+                        cairo_bool_t            do_traps,
+                        void                   *container)
+{
+    cairo_bo_trap_t *trap = &left->deferred_trap;
+    cairo_status_t status = CAIRO_STATUS_SUCCESS;
+
+    /* Only emit (trivial) non-degenerate trapezoids with positive height. */
+    if (likely (trap->top < bot)) {
+       if (do_traps) {
+           _cairo_traps_add_trap (container,
+                                  trap->top, bot,
+                                  &left->edge.line, &trap->right->edge.line);
+           status =  _cairo_traps_status ((cairo_traps_t *) container);
+       } else {
+           cairo_box_t box;
+
+           box.p1.x = left->edge.line.p1.x;
+           box.p1.y = trap->top;
+           box.p2.x = trap->right->edge.line.p1.x;
+           box.p2.y = bot;
+           status = _cairo_boxes_add (container, CAIRO_ANTIALIAS_DEFAULT, &box);
+       }
+    }
+
+    trap->right = NULL;
+
+    return status;
+}
+
+/* Start a new trapezoid at the given top y coordinate, whose edges
+ * are `edge' and `edge->next'. If `edge' already has a trapezoid,
+ * then either add it to the traps in `traps', if the trapezoid's
+ * right edge differs from `edge->next', or do nothing if the new
+ * trapezoid would be a continuation of the existing one. */
+static inline cairo_status_t
+_cairo_bo_edge_start_or_continue_trap (cairo_bo_edge_t *left,
+                                      cairo_bo_edge_t  *right,
+                                      int               top,
+                                      cairo_bool_t      do_traps,
+                                      void             *container)
+{
+    cairo_status_t status;
+
+    if (left->deferred_trap.right == right)
+       return CAIRO_STATUS_SUCCESS;
+
+    if (left->deferred_trap.right != NULL) {
+       if (right != NULL && edges_collinear (left->deferred_trap.right, right))
+       {
+           /* continuation on right, so just swap edges */
+           left->deferred_trap.right = right;
+           return CAIRO_STATUS_SUCCESS;
+       }
+
+       status = _cairo_bo_edge_end_trap (left, top, do_traps, container);
+       if (unlikely (status))
+           return status;
+    }
+
+    if (right != NULL && ! edges_collinear (left, right)) {
+       left->deferred_trap.top = top;
+       left->deferred_trap.right = right;
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static inline cairo_status_t
+_active_edges_to_traps (cairo_bo_edge_t                *left,
+                       int32_t                  top,
+                       cairo_fill_rule_t        fill_rule,
+                       cairo_bool_t             do_traps,
+                       void                    *container)
+{
+    cairo_bo_edge_t *right;
+    cairo_status_t status;
+
+    if (fill_rule == CAIRO_FILL_RULE_WINDING) {
+       while (left != NULL) {
+           int in_out;
+
+           /* Greedily search for the closing edge, so that we generate the
+            * maximal span width with the minimal number of trapezoids.
+            */
+           in_out = left->edge.dir;
+
+           /* Check if there is a co-linear edge with an existing trap */
+           right = left->next;
+           if (left->deferred_trap.right == NULL) {
+               while (right != NULL && right->deferred_trap.right == NULL)
+                   right = right->next;
+
+               if (right != NULL && edges_collinear (left, right)) {
+                   /* continuation on left */
+                   left->deferred_trap = right->deferred_trap;
+                   right->deferred_trap.right = NULL;
+               }
+           }
+
+           /* End all subsumed traps */
+           right = left->next;
+           while (right != NULL) {
+               if (right->deferred_trap.right != NULL) {
+                   status = _cairo_bo_edge_end_trap (right, top, do_traps, container);
+                   if (unlikely (status))
+                       return status;
+               }
+
+               in_out += right->edge.dir;
+               if (in_out == 0) {
+                   /* skip co-linear edges */
+                   if (right->next == NULL ||
+                       ! edges_collinear (right, right->next))
+                   {
+                       break;
+                   }
+               }
+
+               right = right->next;
+           }
+
+           status = _cairo_bo_edge_start_or_continue_trap (left, right, top,
+                                                           do_traps, container);
+           if (unlikely (status))
+               return status;
+
+           left = right;
+           if (left != NULL)
+               left = left->next;
+       }
+    } else {
+       while (left != NULL) {
+           int in_out = 0;
+
+           right = left->next;
+           while (right != NULL) {
+               if (right->deferred_trap.right != NULL) {
+                   status = _cairo_bo_edge_end_trap (right, top, do_traps, container);
+                   if (unlikely (status))
+                       return status;
+               }
+
+               if ((in_out++ & 1) == 0) {
+                   cairo_bo_edge_t *next;
+                   cairo_bool_t skip = FALSE;
+
+                   /* skip co-linear edges */
+                   next = right->next;
+                   if (next != NULL)
+                       skip = edges_collinear (right, next);
+
+                   if (! skip)
+                       break;
+               }
+
+               right = right->next;
+           }
+
+           status = _cairo_bo_edge_start_or_continue_trap (left, right, top,
+                                                           do_traps, container);
+           if (unlikely (status))
+               return status;
+
+           left = right;
+           if (left != NULL)
+               left = left->next;
+       }
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_bentley_ottmann_tessellate_rectilinear (cairo_bo_event_t   **start_events,
+                                              int                       num_events,
+                                              cairo_fill_rule_t         fill_rule,
+                                              cairo_bool_t              do_traps,
+                                              void                     *container)
+{
+    cairo_bo_sweep_line_t sweep_line;
+    cairo_bo_event_t *event;
+    cairo_status_t status;
+
+    _cairo_bo_sweep_line_init (&sweep_line, start_events, num_events);
+
+    while ((event = _cairo_bo_event_dequeue (&sweep_line))) {
+       if (event->point.y != sweep_line.current_y) {
+           status = _active_edges_to_traps (sweep_line.head,
+                                            sweep_line.current_y,
+                                            fill_rule, do_traps, container);
+           if (unlikely (status))
+               return status;
+
+           sweep_line.current_y = event->point.y;
+       }
+
+       switch (event->type) {
+       case CAIRO_BO_EVENT_TYPE_START:
+           _cairo_bo_sweep_line_insert (&sweep_line, event->edge);
+           break;
+
+       case CAIRO_BO_EVENT_TYPE_STOP:
+           _cairo_bo_sweep_line_delete (&sweep_line, event->edge);
+
+           if (event->edge->deferred_trap.right != NULL) {
+               status = _cairo_bo_edge_end_trap (event->edge,
+                                                 sweep_line.current_y,
+                                                 do_traps, container);
+               if (unlikely (status))
+                   return status;
+           }
+
+           break;
+       }
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_status_t
+_cairo_bentley_ottmann_tessellate_rectilinear_polygon_to_boxes (const cairo_polygon_t *polygon,
+                                                               cairo_fill_rule_t         fill_rule,
+                                                               cairo_boxes_t *boxes)
+{
+    cairo_status_t status;
+    cairo_bo_event_t stack_events[CAIRO_STACK_ARRAY_LENGTH (cairo_bo_event_t)];
+    cairo_bo_event_t *events;
+    cairo_bo_event_t *stack_event_ptrs[ARRAY_LENGTH (stack_events) + 1];
+    cairo_bo_event_t **event_ptrs;
+    cairo_bo_edge_t stack_edges[ARRAY_LENGTH (stack_events)];
+    cairo_bo_edge_t *edges;
+    int num_events;
+    int i, j;
+
+    if (unlikely (polygon->num_edges == 0))
+       return CAIRO_STATUS_SUCCESS;
+
+    num_events = 2 * polygon->num_edges;
+
+    events = stack_events;
+    event_ptrs = stack_event_ptrs;
+    edges = stack_edges;
+    if (num_events > ARRAY_LENGTH (stack_events)) {
+       events = _cairo_malloc_ab_plus_c (num_events,
+                                         sizeof (cairo_bo_event_t) +
+                                         sizeof (cairo_bo_edge_t) +
+                                         sizeof (cairo_bo_event_t *),
+                                         sizeof (cairo_bo_event_t *));
+       if (unlikely (events == NULL))
+           return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+       event_ptrs = (cairo_bo_event_t **) (events + num_events);
+       edges = (cairo_bo_edge_t *) (event_ptrs + num_events + 1);
+    }
+
+    for (i = j = 0; i < polygon->num_edges; i++) {
+       edges[i].edge = polygon->edges[i];
+       edges[i].deferred_trap.right = NULL;
+       edges[i].prev = NULL;
+       edges[i].next = NULL;
+
+       event_ptrs[j] = &events[j];
+       events[j].type = CAIRO_BO_EVENT_TYPE_START;
+       events[j].point.y = polygon->edges[i].top;
+       events[j].point.x = polygon->edges[i].line.p1.x;
+       events[j].edge = &edges[i];
+       j++;
+
+       event_ptrs[j] = &events[j];
+       events[j].type = CAIRO_BO_EVENT_TYPE_STOP;
+       events[j].point.y = polygon->edges[i].bottom;
+       events[j].point.x = polygon->edges[i].line.p1.x;
+       events[j].edge = &edges[i];
+       j++;
+    }
+
+    status = _cairo_bentley_ottmann_tessellate_rectilinear (event_ptrs, j,
+                                                           fill_rule,
+                                                           FALSE, boxes);
+    if (events != stack_events)
+       free (events);
+
+    return status;
+}
+
+cairo_status_t
+_cairo_bentley_ottmann_tessellate_rectilinear_traps (cairo_traps_t *traps,
+                                                    cairo_fill_rule_t fill_rule)
+{
+    cairo_bo_event_t stack_events[CAIRO_STACK_ARRAY_LENGTH (cairo_bo_event_t)];
+    cairo_bo_event_t *events;
+    cairo_bo_event_t *stack_event_ptrs[ARRAY_LENGTH (stack_events) + 1];
+    cairo_bo_event_t **event_ptrs;
+    cairo_bo_edge_t stack_edges[ARRAY_LENGTH (stack_events)];
+    cairo_bo_edge_t *edges;
+    cairo_status_t status;
+    int i, j, k;
+
+    if (unlikely (traps->num_traps == 0))
+       return CAIRO_STATUS_SUCCESS;
+
+    assert (traps->is_rectilinear);
+
+    i = 4 * traps->num_traps;
+
+    events = stack_events;
+    event_ptrs = stack_event_ptrs;
+    edges = stack_edges;
+    if (i > ARRAY_LENGTH (stack_events)) {
+       events = _cairo_malloc_ab_plus_c (i,
+                                         sizeof (cairo_bo_event_t) +
+                                         sizeof (cairo_bo_edge_t) +
+                                         sizeof (cairo_bo_event_t *),
+                                         sizeof (cairo_bo_event_t *));
+       if (unlikely (events == NULL))
+           return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+       event_ptrs = (cairo_bo_event_t **) (events + i);
+       edges = (cairo_bo_edge_t *) (event_ptrs + i + 1);
+    }
+
+    for (i = j = k = 0; i < traps->num_traps; i++) {
+       edges[k].edge.top = traps->traps[i].top;
+       edges[k].edge.bottom = traps->traps[i].bottom;
+       edges[k].edge.line = traps->traps[i].left;
+       edges[k].edge.dir = 1;
+       edges[k].deferred_trap.right = NULL;
+       edges[k].prev = NULL;
+       edges[k].next = NULL;
+
+       event_ptrs[j] = &events[j];
+       events[j].type = CAIRO_BO_EVENT_TYPE_START;
+       events[j].point.y = traps->traps[i].top;
+       events[j].point.x = traps->traps[i].left.p1.x;
+       events[j].edge = &edges[k];
+       j++;
+
+       event_ptrs[j] = &events[j];
+       events[j].type = CAIRO_BO_EVENT_TYPE_STOP;
+       events[j].point.y = traps->traps[i].bottom;
+       events[j].point.x = traps->traps[i].left.p1.x;
+       events[j].edge = &edges[k];
+       j++;
+       k++;
+
+       edges[k].edge.top = traps->traps[i].top;
+       edges[k].edge.bottom = traps->traps[i].bottom;
+       edges[k].edge.line = traps->traps[i].right;
+       edges[k].edge.dir = -1;
+       edges[k].deferred_trap.right = NULL;
+       edges[k].prev = NULL;
+       edges[k].next = NULL;
+
+       event_ptrs[j] = &events[j];
+       events[j].type = CAIRO_BO_EVENT_TYPE_START;
+       events[j].point.y = traps->traps[i].top;
+       events[j].point.x = traps->traps[i].right.p1.x;
+       events[j].edge = &edges[k];
+       j++;
+
+       event_ptrs[j] = &events[j];
+       events[j].type = CAIRO_BO_EVENT_TYPE_STOP;
+       events[j].point.y = traps->traps[i].bottom;
+       events[j].point.x = traps->traps[i].right.p1.x;
+       events[j].edge = &edges[k];
+       j++;
+       k++;
+    }
+
+    _cairo_traps_clear (traps);
+    status = _cairo_bentley_ottmann_tessellate_rectilinear (event_ptrs, j,
+                                                           fill_rule,
+                                                           TRUE, traps);
+    traps->is_rectilinear = TRUE;
+
+    if (events != stack_events)
+       free (events);
+
+    return status;
+}
diff --git a/src/cairo-bentley-ottmann.c b/src/cairo-bentley-ottmann.c
new file mode 100644 (file)
index 0000000..f30449e
--- /dev/null
@@ -0,0 +1,2121 @@
+/*
+ * Copyright © 2004 Carl Worth
+ * Copyright © 2006 Red Hat, Inc.
+ * Copyright © 2008 Chris Wilson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Carl Worth
+ *
+ * Contributor(s):
+ *     Carl D. Worth <cworth@cworth.org>
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+/* Provide definitions for standalone compilation */
+#include "cairoint.h"
+
+#include "cairo-error-private.h"
+#include "cairo-freelist-private.h"
+#include "cairo-combsort-private.h"
+#include "cairo-traps-private.h"
+
+#define DEBUG_PRINT_STATE 0
+#define DEBUG_EVENTS 0
+#define DEBUG_TRAPS 0
+
+typedef cairo_point_t cairo_bo_point32_t;
+
+typedef struct _cairo_bo_intersect_ordinate {
+    int32_t ordinate;
+    enum { EXACT, INEXACT } exactness;
+} cairo_bo_intersect_ordinate_t;
+
+typedef struct _cairo_bo_intersect_point {
+    cairo_bo_intersect_ordinate_t x;
+    cairo_bo_intersect_ordinate_t y;
+} cairo_bo_intersect_point_t;
+
+typedef struct _cairo_bo_edge cairo_bo_edge_t;
+typedef struct _cairo_bo_trap cairo_bo_trap_t;
+
+/* A deferred trapezoid of an edge */
+struct _cairo_bo_trap {
+    cairo_bo_edge_t *right;
+    int32_t top;
+};
+
+struct _cairo_bo_edge {
+    cairo_edge_t edge;
+    cairo_bo_edge_t *prev;
+    cairo_bo_edge_t *next;
+    cairo_bo_trap_t deferred_trap;
+};
+
+/* the parent is always given by index/2 */
+#define PQ_PARENT_INDEX(i) ((i) >> 1)
+#define PQ_FIRST_ENTRY 1
+
+/* left and right children are index * 2 and (index * 2) +1 respectively */
+#define PQ_LEFT_CHILD_INDEX(i) ((i) << 1)
+
+typedef enum {
+    CAIRO_BO_EVENT_TYPE_STOP,
+    CAIRO_BO_EVENT_TYPE_INTERSECTION,
+    CAIRO_BO_EVENT_TYPE_START
+} cairo_bo_event_type_t;
+
+typedef struct _cairo_bo_event {
+    cairo_bo_event_type_t type;
+    cairo_point_t point;
+} cairo_bo_event_t;
+
+typedef struct _cairo_bo_start_event {
+    cairo_bo_event_type_t type;
+    cairo_point_t point;
+    cairo_bo_edge_t edge;
+} cairo_bo_start_event_t;
+
+typedef struct _cairo_bo_queue_event {
+    cairo_bo_event_type_t type;
+    cairo_point_t point;
+    cairo_bo_edge_t *e1;
+    cairo_bo_edge_t *e2;
+} cairo_bo_queue_event_t;
+
+typedef struct _pqueue {
+    int size, max_size;
+
+    cairo_bo_event_t **elements;
+    cairo_bo_event_t *elements_embedded[1024];
+} pqueue_t;
+
+typedef struct _cairo_bo_event_queue {
+    cairo_freepool_t pool;
+    pqueue_t pqueue;
+    cairo_bo_event_t **start_events;
+} cairo_bo_event_queue_t;
+
+typedef struct _cairo_bo_sweep_line {
+    cairo_bo_edge_t *head;
+    cairo_bo_edge_t *stopped;
+    int32_t current_y;
+    cairo_bo_edge_t *current_edge;
+} cairo_bo_sweep_line_t;
+
+#if DEBUG_TRAPS
+static void
+dump_traps (cairo_traps_t *traps, const char *filename)
+{
+    FILE *file;
+    cairo_box_t extents;
+    int n;
+
+    if (getenv ("CAIRO_DEBUG_TRAPS") == NULL)
+       return;
+
+#if 0
+    if (traps->has_limits) {
+       printf ("%s: limits=(%d, %d, %d, %d)\n",
+               filename,
+               traps->limits.p1.x, traps->limits.p1.y,
+               traps->limits.p2.x, traps->limits.p2.y);
+    }
+#endif
+    _cairo_traps_extents (traps, &extents);
+    printf ("%s: extents=(%d, %d, %d, %d)\n",
+           filename,
+           extents.p1.x, extents.p1.y,
+           extents.p2.x, extents.p2.y);
+
+    file = fopen (filename, "a");
+    if (file != NULL) {
+       for (n = 0; n < traps->num_traps; n++) {
+           fprintf (file, "%d %d L:(%d, %d), (%d, %d) R:(%d, %d), (%d, %d)\n",
+                    traps->traps[n].top,
+                    traps->traps[n].bottom,
+                    traps->traps[n].left.p1.x,
+                    traps->traps[n].left.p1.y,
+                    traps->traps[n].left.p2.x,
+                    traps->traps[n].left.p2.y,
+                    traps->traps[n].right.p1.x,
+                    traps->traps[n].right.p1.y,
+                    traps->traps[n].right.p2.x,
+                    traps->traps[n].right.p2.y);
+       }
+       fprintf (file, "\n");
+       fclose (file);
+    }
+}
+
+static void
+dump_edges (cairo_bo_start_event_t *events,
+           int num_edges,
+           const char *filename)
+{
+    FILE *file;
+    int n;
+
+    if (getenv ("CAIRO_DEBUG_TRAPS") == NULL)
+       return;
+
+    file = fopen (filename, "a");
+    if (file != NULL) {
+       for (n = 0; n < num_edges; n++) {
+           fprintf (file, "(%d, %d), (%d, %d) %d %d %d\n",
+                    events[n].edge.edge.line.p1.x,
+                    events[n].edge.edge.line.p1.y,
+                    events[n].edge.edge.line.p2.x,
+                    events[n].edge.edge.line.p2.y,
+                    events[n].edge.edge.top,
+                    events[n].edge.edge.bottom,
+                    events[n].edge.edge.dir);
+       }
+       fprintf (file, "\n");
+       fclose (file);
+    }
+}
+#endif
+
+static cairo_fixed_t
+_line_compute_intersection_x_for_y (const cairo_line_t *line,
+                                   cairo_fixed_t y)
+{
+    cairo_fixed_t x, dy;
+
+    if (y == line->p1.y)
+       return line->p1.x;
+    if (y == line->p2.y)
+       return line->p2.x;
+
+    x = line->p1.x;
+    dy = line->p2.y - line->p1.y;
+    if (dy != 0) {
+       x += _cairo_fixed_mul_div_floor (y - line->p1.y,
+                                        line->p2.x - line->p1.x,
+                                        dy);
+    }
+
+    return x;
+}
+
+static inline int
+_cairo_bo_point32_compare (cairo_bo_point32_t const *a,
+                          cairo_bo_point32_t const *b)
+{
+    int cmp;
+
+    cmp = a->y - b->y;
+    if (cmp)
+       return cmp;
+
+    return a->x - b->x;
+}
+
+/* Compare the slope of a to the slope of b, returning 1, 0, -1 if the
+ * slope a is respectively greater than, equal to, or less than the
+ * slope of b.
+ *
+ * For each edge, consider the direction vector formed from:
+ *
+ *     top -> bottom
+ *
+ * which is:
+ *
+ *     (dx, dy) = (line.p2.x - line.p1.x, line.p2.y - line.p1.y)
+ *
+ * We then define the slope of each edge as dx/dy, (which is the
+ * inverse of the slope typically used in math instruction). We never
+ * compute a slope directly as the value approaches infinity, but we
+ * can derive a slope comparison without division as follows, (where
+ * the ? represents our compare operator).
+ *
+ * 1.     slope(a) ? slope(b)
+ * 2.      adx/ady ? bdx/bdy
+ * 3.  (adx * bdy) ? (bdx * ady)
+ *
+ * Note that from step 2 to step 3 there is no change needed in the
+ * sign of the result since both ady and bdy are guaranteed to be
+ * greater than or equal to 0.
+ *
+ * When using this slope comparison to sort edges, some care is needed
+ * when interpreting the results. Since the slope compare operates on
+ * distance vectors from top to bottom it gives a correct left to
+ * right sort for edges that have a common top point, (such as two
+ * edges with start events at the same location). On the other hand,
+ * the sense of the result will be exactly reversed for two edges that
+ * have a common stop point.
+ */
+static inline int
+_slope_compare (const cairo_bo_edge_t *a,
+               const cairo_bo_edge_t *b)
+{
+    /* XXX: We're assuming here that dx and dy will still fit in 32
+     * bits. That's not true in general as there could be overflow. We
+     * should prevent that before the tessellation algorithm
+     * begins.
+     */
+    int32_t adx = a->edge.line.p2.x - a->edge.line.p1.x;
+    int32_t bdx = b->edge.line.p2.x - b->edge.line.p1.x;
+
+    /* Since the dy's are all positive by construction we can fast
+     * path several common cases.
+     */
+
+    /* First check for vertical lines. */
+    if (adx == 0)
+       return -bdx;
+    if (bdx == 0)
+       return adx;
+
+    /* Then where the two edges point in different directions wrt x. */
+    if ((adx ^ bdx) < 0)
+       return adx;
+
+    /* Finally we actually need to do the general comparison. */
+    {
+       int32_t ady = a->edge.line.p2.y - a->edge.line.p1.y;
+       int32_t bdy = b->edge.line.p2.y - b->edge.line.p1.y;
+       cairo_int64_t adx_bdy = _cairo_int32x32_64_mul (adx, bdy);
+       cairo_int64_t bdx_ady = _cairo_int32x32_64_mul (bdx, ady);
+
+       return _cairo_int64_cmp (adx_bdy, bdx_ady);
+    }
+}
+
+/*
+ * We need to compare the x-coordinates of a pair of lines for a particular y,
+ * without loss of precision.
+ *
+ * The x-coordinate along an edge for a given y is:
+ *   X = A_x + (Y - A_y) * A_dx / A_dy
+ *
+ * So the inequality we wish to test is:
+ *   A_x + (Y - A_y) * A_dx / A_dy ∘ B_x + (Y - B_y) * B_dx / B_dy,
+ * where ∘ is our inequality operator.
+ *
+ * By construction, we know that A_dy and B_dy (and (Y - A_y), (Y - B_y)) are
+ * all positive, so we can rearrange it thus without causing a sign change:
+ *   A_dy * B_dy * (A_x - B_x) ∘ (Y - B_y) * B_dx * A_dy
+ *                                 - (Y - A_y) * A_dx * B_dy
+ *
+ * Given the assumption that all the deltas fit within 32 bits, we can compute
+ * this comparison directly using 128 bit arithmetic. For certain, but common,
+ * input we can reduce this down to a single 32 bit compare by inspecting the
+ * deltas.
+ *
+ * (And put the burden of the work on developing fast 128 bit ops, which are
+ * required throughout the tessellator.)
+ *
+ * See the similar discussion for _slope_compare().
+ */
+static int
+edges_compare_x_for_y_general (const cairo_bo_edge_t *a,
+                              const cairo_bo_edge_t *b,
+                              int32_t y)
+{
+    /* XXX: We're assuming here that dx and dy will still fit in 32
+     * bits. That's not true in general as there could be overflow. We
+     * should prevent that before the tessellation algorithm
+     * begins.
+     */
+    int32_t dx;
+    int32_t adx, ady;
+    int32_t bdx, bdy;
+    enum {
+       HAVE_NONE    = 0x0,
+       HAVE_DX      = 0x1,
+       HAVE_ADX     = 0x2,
+       HAVE_DX_ADX  = HAVE_DX | HAVE_ADX,
+       HAVE_BDX     = 0x4,
+       HAVE_DX_BDX  = HAVE_DX | HAVE_BDX,
+       HAVE_ADX_BDX = HAVE_ADX | HAVE_BDX,
+       HAVE_ALL     = HAVE_DX | HAVE_ADX | HAVE_BDX
+    } have_dx_adx_bdx = HAVE_ALL;
+
+    /* don't bother solving for abscissa if the edges' bounding boxes
+     * can be used to order them. */
+    {
+           int32_t amin, amax;
+           int32_t bmin, bmax;
+           if (a->edge.line.p1.x < a->edge.line.p2.x) {
+                   amin = a->edge.line.p1.x;
+                   amax = a->edge.line.p2.x;
+           } else {
+                   amin = a->edge.line.p2.x;
+                   amax = a->edge.line.p1.x;
+           }
+           if (b->edge.line.p1.x < b->edge.line.p2.x) {
+                   bmin = b->edge.line.p1.x;
+                   bmax = b->edge.line.p2.x;
+           } else {
+                   bmin = b->edge.line.p2.x;
+                   bmax = b->edge.line.p1.x;
+           }
+           if (amax < bmin) return -1;
+           if (amin > bmax) return +1;
+    }
+
+    ady = a->edge.line.p2.y - a->edge.line.p1.y;
+    adx = a->edge.line.p2.x - a->edge.line.p1.x;
+    if (adx == 0)
+       have_dx_adx_bdx &= ~HAVE_ADX;
+
+    bdy = b->edge.line.p2.y - b->edge.line.p1.y;
+    bdx = b->edge.line.p2.x - b->edge.line.p1.x;
+    if (bdx == 0)
+       have_dx_adx_bdx &= ~HAVE_BDX;
+
+    dx = a->edge.line.p1.x - b->edge.line.p1.x;
+    if (dx == 0)
+       have_dx_adx_bdx &= ~HAVE_DX;
+
+#define L _cairo_int64x32_128_mul (_cairo_int32x32_64_mul (ady, bdy), dx)
+#define A _cairo_int64x32_128_mul (_cairo_int32x32_64_mul (adx, bdy), y - a->edge.line.p1.y)
+#define B _cairo_int64x32_128_mul (_cairo_int32x32_64_mul (bdx, ady), y - b->edge.line.p1.y)
+    switch (have_dx_adx_bdx) {
+    default:
+    case HAVE_NONE:
+       return 0;
+    case HAVE_DX:
+       /* A_dy * B_dy * (A_x - B_x) ∘ 0 */
+       return dx; /* ady * bdy is positive definite */
+    case HAVE_ADX:
+       /* 0 ∘  - (Y - A_y) * A_dx * B_dy */
+       return adx; /* bdy * (y - a->top.y) is positive definite */
+    case HAVE_BDX:
+       /* 0 ∘ (Y - B_y) * B_dx * A_dy */
+       return -bdx; /* ady * (y - b->top.y) is positive definite */
+    case HAVE_ADX_BDX:
+       /*  0 ∘ (Y - B_y) * B_dx * A_dy - (Y - A_y) * A_dx * B_dy */
+       if ((adx ^ bdx) < 0) {
+           return adx;
+       } else if (a->edge.line.p1.y == b->edge.line.p1.y) { /* common origin */
+           cairo_int64_t adx_bdy, bdx_ady;
+
+           /* ∴ A_dx * B_dy ∘ B_dx * A_dy */
+
+           adx_bdy = _cairo_int32x32_64_mul (adx, bdy);
+           bdx_ady = _cairo_int32x32_64_mul (bdx, ady);
+
+           return _cairo_int64_cmp (adx_bdy, bdx_ady);
+       } else
+           return _cairo_int128_cmp (A, B);
+    case HAVE_DX_ADX:
+       /* A_dy * (A_x - B_x) ∘ - (Y - A_y) * A_dx */
+       if ((-adx ^ dx) < 0) {
+           return dx;
+       } else {
+           cairo_int64_t ady_dx, dy_adx;
+
+           ady_dx = _cairo_int32x32_64_mul (ady, dx);
+           dy_adx = _cairo_int32x32_64_mul (a->edge.line.p1.y - y, adx);
+
+           return _cairo_int64_cmp (ady_dx, dy_adx);
+       }
+    case HAVE_DX_BDX:
+       /* B_dy * (A_x - B_x) ∘ (Y - B_y) * B_dx */
+       if ((bdx ^ dx) < 0) {
+           return dx;
+       } else {
+           cairo_int64_t bdy_dx, dy_bdx;
+
+           bdy_dx = _cairo_int32x32_64_mul (bdy, dx);
+           dy_bdx = _cairo_int32x32_64_mul (y - b->edge.line.p1.y, bdx);
+
+           return _cairo_int64_cmp (bdy_dx, dy_bdx);
+       }
+    case HAVE_ALL:
+       /* XXX try comparing (a->edge.line.p2.x - b->edge.line.p2.x) et al */
+       return _cairo_int128_cmp (L, _cairo_int128_sub (B, A));
+    }
+#undef B
+#undef A
+#undef L
+}
+
+/*
+ * We need to compare the x-coordinate of a line for a particular y wrt to a
+ * given x, without loss of precision.
+ *
+ * The x-coordinate along an edge for a given y is:
+ *   X = A_x + (Y - A_y) * A_dx / A_dy
+ *
+ * So the inequality we wish to test is:
+ *   A_x + (Y - A_y) * A_dx / A_dy ∘ X
+ * where ∘ is our inequality operator.
+ *
+ * By construction, we know that A_dy (and (Y - A_y)) are
+ * all positive, so we can rearrange it thus without causing a sign change:
+ *   (Y - A_y) * A_dx ∘ (X - A_x) * A_dy
+ *
+ * Given the assumption that all the deltas fit within 32 bits, we can compute
+ * this comparison directly using 64 bit arithmetic.
+ *
+ * See the similar discussion for _slope_compare() and
+ * edges_compare_x_for_y_general().
+ */
+static int
+edge_compare_for_y_against_x (const cairo_bo_edge_t *a,
+                             int32_t y,
+                             int32_t x)
+{
+    int32_t adx, ady;
+    int32_t dx, dy;
+    cairo_int64_t L, R;
+
+    if (x < a->edge.line.p1.x && x < a->edge.line.p2.x)
+       return 1;
+    if (x > a->edge.line.p1.x && x > a->edge.line.p2.x)
+       return -1;
+
+    adx = a->edge.line.p2.x - a->edge.line.p1.x;
+    dx = x - a->edge.line.p1.x;
+
+    if (adx == 0)
+       return -dx;
+    if (dx == 0 || (adx ^ dx) < 0)
+       return adx;
+
+    dy = y - a->edge.line.p1.y;
+    ady = a->edge.line.p2.y - a->edge.line.p1.y;
+
+    L = _cairo_int32x32_64_mul (dy, adx);
+    R = _cairo_int32x32_64_mul (dx, ady);
+
+    return _cairo_int64_cmp (L, R);
+}
+
+static int
+edges_compare_x_for_y (const cairo_bo_edge_t *a,
+                      const cairo_bo_edge_t *b,
+                      int32_t y)
+{
+    /* If the sweep-line is currently on an end-point of a line,
+     * then we know its precise x value (and considering that we often need to
+     * compare events at end-points, this happens frequently enough to warrant
+     * special casing).
+     */
+    enum {
+       HAVE_NEITHER = 0x0,
+       HAVE_AX      = 0x1,
+       HAVE_BX      = 0x2,
+       HAVE_BOTH    = HAVE_AX | HAVE_BX
+    } have_ax_bx = HAVE_BOTH;
+    int32_t ax, bx;
+
+    if (y == a->edge.line.p1.y)
+       ax = a->edge.line.p1.x;
+    else if (y == a->edge.line.p2.y)
+       ax = a->edge.line.p2.x;
+    else
+       have_ax_bx &= ~HAVE_AX;
+
+    if (y == b->edge.line.p1.y)
+       bx = b->edge.line.p1.x;
+    else if (y == b->edge.line.p2.y)
+       bx = b->edge.line.p2.x;
+    else
+       have_ax_bx &= ~HAVE_BX;
+
+    switch (have_ax_bx) {
+    default:
+    case HAVE_NEITHER:
+       return edges_compare_x_for_y_general (a, b, y);
+    case HAVE_AX:
+       return -edge_compare_for_y_against_x (b, y, ax);
+    case HAVE_BX:
+       return edge_compare_for_y_against_x (a, y, bx);
+    case HAVE_BOTH:
+       return ax - bx;
+    }
+}
+
+static inline int
+_line_equal (const cairo_line_t *a, const cairo_line_t *b)
+{
+    return a->p1.x == b->p1.x && a->p1.y == b->p1.y &&
+           a->p2.x == b->p2.x && a->p2.y == b->p2.y;
+}
+
+static int
+_cairo_bo_sweep_line_compare_edges (cairo_bo_sweep_line_t      *sweep_line,
+                                   const cairo_bo_edge_t       *a,
+                                   const cairo_bo_edge_t       *b)
+{
+    int cmp;
+
+    /* compare the edges if not identical */
+    if (! _line_equal (&a->edge.line, &b->edge.line)) {
+       cmp = edges_compare_x_for_y (a, b, sweep_line->current_y);
+       if (cmp)
+           return cmp;
+
+       /* The two edges intersect exactly at y, so fall back on slope
+        * comparison. We know that this compare_edges function will be
+        * called only when starting a new edge, (not when stopping an
+        * edge), so we don't have to worry about conditionally inverting
+        * the sense of _slope_compare. */
+       cmp = _slope_compare (a, b);
+       if (cmp)
+           return cmp;
+    }
+
+    /* We've got two collinear edges now. */
+    return b->edge.bottom - a->edge.bottom;
+}
+
+static inline cairo_int64_t
+det32_64 (int32_t a, int32_t b,
+         int32_t c, int32_t d)
+{
+    /* det = a * d - b * c */
+    return _cairo_int64_sub (_cairo_int32x32_64_mul (a, d),
+                            _cairo_int32x32_64_mul (b, c));
+}
+
+static inline cairo_int128_t
+det64x32_128 (cairo_int64_t a, int32_t       b,
+             cairo_int64_t c, int32_t       d)
+{
+    /* det = a * d - b * c */
+    return _cairo_int128_sub (_cairo_int64x32_128_mul (a, d),
+                             _cairo_int64x32_128_mul (c, b));
+}
+
+/* Compute the intersection of two lines as defined by two edges. The
+ * result is provided as a coordinate pair of 128-bit integers.
+ *
+ * Returns %CAIRO_BO_STATUS_INTERSECTION if there is an intersection or
+ * %CAIRO_BO_STATUS_PARALLEL if the two lines are exactly parallel.
+ */
+static cairo_bool_t
+intersect_lines (cairo_bo_edge_t               *a,
+                cairo_bo_edge_t                *b,
+                cairo_bo_intersect_point_t     *intersection)
+{
+    cairo_int64_t a_det, b_det;
+
+    /* XXX: We're assuming here that dx and dy will still fit in 32
+     * bits. That's not true in general as there could be overflow. We
+     * should prevent that before the tessellation algorithm begins.
+     * What we're doing to mitigate this is to perform clamping in
+     * cairo_bo_tessellate_polygon().
+     */
+    int32_t dx1 = a->edge.line.p1.x - a->edge.line.p2.x;
+    int32_t dy1 = a->edge.line.p1.y - a->edge.line.p2.y;
+
+    int32_t dx2 = b->edge.line.p1.x - b->edge.line.p2.x;
+    int32_t dy2 = b->edge.line.p1.y - b->edge.line.p2.y;
+
+    cairo_int64_t den_det;
+    cairo_int64_t R;
+    cairo_quorem64_t qr;
+
+    den_det = det32_64 (dx1, dy1, dx2, dy2);
+
+     /* Q: Can we determine that the lines do not intersect (within range)
+      * much more cheaply than computing the intersection point i.e. by
+      * avoiding the division?
+      *
+      *   X = ax + t * adx = bx + s * bdx;
+      *   Y = ay + t * ady = by + s * bdy;
+      *   ∴ t * (ady*bdx - bdy*adx) = bdx * (by - ay) + bdy * (ax - bx)
+      *   => t * L = R
+      *
+      * Therefore we can reject any intersection (under the criteria for
+      * valid intersection events) if:
+      *   L^R < 0 => t < 0, or
+      *   L<R => t > 1
+      *
+      * (where top/bottom must at least extend to the line endpoints).
+      *
+      * A similar substitution can be performed for s, yielding:
+      *   s * (ady*bdx - bdy*adx) = ady * (ax - bx) - adx * (ay - by)
+      */
+    R = det32_64 (dx2, dy2,
+                 b->edge.line.p1.x - a->edge.line.p1.x,
+                 b->edge.line.p1.y - a->edge.line.p1.y);
+    if (_cairo_int64_negative (den_det)) {
+       if (_cairo_int64_ge (den_det, R))
+           return FALSE;
+    } else {
+       if (_cairo_int64_le (den_det, R))
+           return FALSE;
+    }
+
+    R = det32_64 (dy1, dx1,
+                 a->edge.line.p1.y - b->edge.line.p1.y,
+                 a->edge.line.p1.x - b->edge.line.p1.x);
+    if (_cairo_int64_negative (den_det)) {
+       if (_cairo_int64_ge (den_det, R))
+           return FALSE;
+    } else {
+       if (_cairo_int64_le (den_det, R))
+           return FALSE;
+    }
+
+    /* We now know that the two lines should intersect within range. */
+
+    a_det = det32_64 (a->edge.line.p1.x, a->edge.line.p1.y,
+                     a->edge.line.p2.x, a->edge.line.p2.y);
+    b_det = det32_64 (b->edge.line.p1.x, b->edge.line.p1.y,
+                     b->edge.line.p2.x, b->edge.line.p2.y);
+
+    /* x = det (a_det, dx1, b_det, dx2) / den_det */
+    qr = _cairo_int_96by64_32x64_divrem (det64x32_128 (a_det, dx1,
+                                                      b_det, dx2),
+                                        den_det);
+    if (_cairo_int64_eq (qr.rem, den_det))
+       return FALSE;
+#if 0
+    intersection->x.exactness = _cairo_int64_is_zero (qr.rem) ? EXACT : INEXACT;
+#else
+    intersection->x.exactness = EXACT;
+    if (! _cairo_int64_is_zero (qr.rem)) {
+       if (_cairo_int64_negative (den_det) ^ _cairo_int64_negative (qr.rem))
+           qr.rem = _cairo_int64_negate (qr.rem);
+       qr.rem = _cairo_int64_mul (qr.rem, _cairo_int32_to_int64 (2));
+       if (_cairo_int64_ge (qr.rem, den_det)) {
+           qr.quo = _cairo_int64_add (qr.quo,
+                                      _cairo_int32_to_int64 (_cairo_int64_negative (qr.quo) ? -1 : 1));
+       } else
+           intersection->x.exactness = INEXACT;
+    }
+#endif
+    intersection->x.ordinate = _cairo_int64_to_int32 (qr.quo);
+
+    /* y = det (a_det, dy1, b_det, dy2) / den_det */
+    qr = _cairo_int_96by64_32x64_divrem (det64x32_128 (a_det, dy1,
+                                                      b_det, dy2),
+                                        den_det);
+    if (_cairo_int64_eq (qr.rem, den_det))
+       return FALSE;
+#if 0
+    intersection->y.exactness = _cairo_int64_is_zero (qr.rem) ? EXACT : INEXACT;
+#else
+    intersection->y.exactness = EXACT;
+    if (! _cairo_int64_is_zero (qr.rem)) {
+       if (_cairo_int64_negative (den_det) ^ _cairo_int64_negative (qr.rem))
+           qr.rem = _cairo_int64_negate (qr.rem);
+       qr.rem = _cairo_int64_mul (qr.rem, _cairo_int32_to_int64 (2));
+       if (_cairo_int64_ge (qr.rem, den_det)) {
+           qr.quo = _cairo_int64_add (qr.quo,
+                                      _cairo_int32_to_int64 (_cairo_int64_negative (qr.quo) ? -1 : 1));
+       } else
+           intersection->y.exactness = INEXACT;
+    }
+#endif
+    intersection->y.ordinate = _cairo_int64_to_int32 (qr.quo);
+
+    return TRUE;
+}
+
+static int
+_cairo_bo_intersect_ordinate_32_compare (cairo_bo_intersect_ordinate_t a,
+                                        int32_t                        b)
+{
+    /* First compare the quotient */
+    if (a.ordinate > b)
+       return +1;
+    if (a.ordinate < b)
+       return -1;
+    /* With quotient identical, if remainder is 0 then compare equal */
+    /* Otherwise, the non-zero remainder makes a > b */
+    return INEXACT == a.exactness;
+}
+
+/* Does the given edge contain the given point. The point must already
+ * be known to be contained within the line determined by the edge,
+ * (most likely the point results from an intersection of this edge
+ * with another).
+ *
+ * If we had exact arithmetic, then this function would simply be a
+ * matter of examining whether the y value of the point lies within
+ * the range of y values of the edge. But since intersection points
+ * are not exact due to being rounded to the nearest integer within
+ * the available precision, we must also examine the x value of the
+ * point.
+ *
+ * The definition of "contains" here is that the given intersection
+ * point will be seen by the sweep line after the start event for the
+ * given edge and before the stop event for the edge. See the comments
+ * in the implementation for more details.
+ */
+static cairo_bool_t
+_cairo_bo_edge_contains_intersect_point (cairo_bo_edge_t               *edge,
+                                        cairo_bo_intersect_point_t     *point)
+{
+    int cmp_top, cmp_bottom;
+
+    /* XXX: When running the actual algorithm, we don't actually need to
+     * compare against edge->top at all here, since any intersection above
+     * top is eliminated early via a slope comparison. We're leaving these
+     * here for now only for the sake of the quadratic-time intersection
+     * finder which needs them.
+     */
+
+    cmp_top = _cairo_bo_intersect_ordinate_32_compare (point->y,
+                                                      edge->edge.top);
+    cmp_bottom = _cairo_bo_intersect_ordinate_32_compare (point->y,
+                                                         edge->edge.bottom);
+
+    if (cmp_top < 0 || cmp_bottom > 0)
+    {
+       return FALSE;
+    }
+
+    if (cmp_top > 0 && cmp_bottom < 0)
+    {
+       return TRUE;
+    }
+
+    /* At this stage, the point lies on the same y value as either
+     * edge->top or edge->bottom, so we have to examine the x value in
+     * order to properly determine containment. */
+
+    /* If the y value of the point is the same as the y value of the
+     * top of the edge, then the x value of the point must be greater
+     * to be considered as inside the edge. Similarly, if the y value
+     * of the point is the same as the y value of the bottom of the
+     * edge, then the x value of the point must be less to be
+     * considered as inside. */
+
+    if (cmp_top == 0) {
+       cairo_fixed_t top_x;
+
+       top_x = _line_compute_intersection_x_for_y (&edge->edge.line,
+                                                   edge->edge.top);
+       return _cairo_bo_intersect_ordinate_32_compare (point->x, top_x) > 0;
+    } else { /* cmp_bottom == 0 */
+       cairo_fixed_t bot_x;
+
+       bot_x = _line_compute_intersection_x_for_y (&edge->edge.line,
+                                                   edge->edge.bottom);
+       return _cairo_bo_intersect_ordinate_32_compare (point->x, bot_x) < 0;
+    }
+}
+
+/* Compute the intersection of two edges. The result is provided as a
+ * coordinate pair of 128-bit integers.
+ *
+ * Returns %CAIRO_BO_STATUS_INTERSECTION if there is an intersection
+ * that is within both edges, %CAIRO_BO_STATUS_NO_INTERSECTION if the
+ * intersection of the lines defined by the edges occurs outside of
+ * one or both edges, and %CAIRO_BO_STATUS_PARALLEL if the two edges
+ * are exactly parallel.
+ *
+ * Note that when determining if a candidate intersection is "inside"
+ * an edge, we consider both the infinitesimal shortening and the
+ * infinitesimal tilt rules described by John Hobby. Specifically, if
+ * the intersection is exactly the same as an edge point, it is
+ * effectively outside (no intersection is returned). Also, if the
+ * intersection point has the same
+ */
+static cairo_bool_t
+_cairo_bo_edge_intersect (cairo_bo_edge_t      *a,
+                         cairo_bo_edge_t       *b,
+                         cairo_bo_point32_t    *intersection)
+{
+    cairo_bo_intersect_point_t quorem;
+
+    if (! intersect_lines (a, b, &quorem))
+       return FALSE;
+
+    if (! _cairo_bo_edge_contains_intersect_point (a, &quorem))
+       return FALSE;
+
+    if (! _cairo_bo_edge_contains_intersect_point (b, &quorem))
+       return FALSE;
+
+    /* Now that we've correctly compared the intersection point and
+     * determined that it lies within the edge, then we know that we
+     * no longer need any more bits of storage for the intersection
+     * than we do for our edge coordinates. We also no longer need the
+     * remainder from the division. */
+    intersection->x = quorem.x.ordinate;
+    intersection->y = quorem.y.ordinate;
+
+    return TRUE;
+}
+
+static inline int
+cairo_bo_event_compare (const cairo_bo_event_t *a,
+                       const cairo_bo_event_t *b)
+{
+    int cmp;
+
+    cmp = _cairo_bo_point32_compare (&a->point, &b->point);
+    if (cmp)
+       return cmp;
+
+    cmp = a->type - b->type;
+    if (cmp)
+       return cmp;
+
+    return a - b;
+}
+
+static inline void
+_pqueue_init (pqueue_t *pq)
+{
+    pq->max_size = ARRAY_LENGTH (pq->elements_embedded);
+    pq->size = 0;
+
+    pq->elements = pq->elements_embedded;
+}
+
+static inline void
+_pqueue_fini (pqueue_t *pq)
+{
+    if (pq->elements != pq->elements_embedded)
+       free (pq->elements);
+}
+
+static cairo_status_t
+_pqueue_grow (pqueue_t *pq)
+{
+    cairo_bo_event_t **new_elements;
+    pq->max_size *= 2;
+
+    if (pq->elements == pq->elements_embedded) {
+       new_elements = _cairo_malloc_ab (pq->max_size,
+                                        sizeof (cairo_bo_event_t *));
+       if (unlikely (new_elements == NULL))
+           return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+       memcpy (new_elements, pq->elements_embedded,
+               sizeof (pq->elements_embedded));
+    } else {
+       new_elements = _cairo_realloc_ab (pq->elements,
+                                         pq->max_size,
+                                         sizeof (cairo_bo_event_t *));
+       if (unlikely (new_elements == NULL))
+           return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+    }
+
+    pq->elements = new_elements;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static inline cairo_status_t
+_pqueue_push (pqueue_t *pq, cairo_bo_event_t *event)
+{
+    cairo_bo_event_t **elements;
+    int i, parent;
+
+    if (unlikely (pq->size + 1 == pq->max_size)) {
+       cairo_status_t status;
+
+       status = _pqueue_grow (pq);
+       if (unlikely (status))
+           return status;
+    }
+
+    elements = pq->elements;
+
+    for (i = ++pq->size;
+        i != PQ_FIRST_ENTRY &&
+        cairo_bo_event_compare (event,
+                                elements[parent = PQ_PARENT_INDEX (i)]) < 0;
+        i = parent)
+    {
+       elements[i] = elements[parent];
+    }
+
+    elements[i] = event;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static inline void
+_pqueue_pop (pqueue_t *pq)
+{
+    cairo_bo_event_t **elements = pq->elements;
+    cairo_bo_event_t *tail;
+    int child, i;
+
+    tail = elements[pq->size--];
+    if (pq->size == 0) {
+       elements[PQ_FIRST_ENTRY] = NULL;
+       return;
+    }
+
+    for (i = PQ_FIRST_ENTRY;
+        (child = PQ_LEFT_CHILD_INDEX (i)) <= pq->size;
+        i = child)
+    {
+       if (child != pq->size &&
+           cairo_bo_event_compare (elements[child+1],
+                                   elements[child]) < 0)
+       {
+           child++;
+       }
+
+       if (cairo_bo_event_compare (elements[child], tail) >= 0)
+           break;
+
+       elements[i] = elements[child];
+    }
+    elements[i] = tail;
+}
+
+static inline cairo_status_t
+_cairo_bo_event_queue_insert (cairo_bo_event_queue_t   *queue,
+                             cairo_bo_event_type_t      type,
+                             cairo_bo_edge_t           *e1,
+                             cairo_bo_edge_t           *e2,
+                             const cairo_point_t        *point)
+{
+    cairo_bo_queue_event_t *event;
+
+    event = _cairo_freepool_alloc (&queue->pool);
+    if (unlikely (event == NULL))
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    event->type = type;
+    event->e1 = e1;
+    event->e2 = e2;
+    event->point = *point;
+
+    return _pqueue_push (&queue->pqueue, (cairo_bo_event_t *) event);
+}
+
+static void
+_cairo_bo_event_queue_delete (cairo_bo_event_queue_t *queue,
+                             cairo_bo_event_t       *event)
+{
+    _cairo_freepool_free (&queue->pool, event);
+}
+
+static cairo_bo_event_t *
+_cairo_bo_event_dequeue (cairo_bo_event_queue_t *event_queue)
+{
+    cairo_bo_event_t *event, *cmp;
+
+    event = event_queue->pqueue.elements[PQ_FIRST_ENTRY];
+    cmp = *event_queue->start_events;
+    if (event == NULL ||
+       (cmp != NULL && cairo_bo_event_compare (cmp, event) < 0))
+    {
+       event = cmp;
+       event_queue->start_events++;
+    }
+    else
+    {
+       _pqueue_pop (&event_queue->pqueue);
+    }
+
+    return event;
+}
+
+CAIRO_COMBSORT_DECLARE (_cairo_bo_event_queue_sort,
+                       cairo_bo_event_t *,
+                       cairo_bo_event_compare)
+
+static void
+_cairo_bo_event_queue_init (cairo_bo_event_queue_t      *event_queue,
+                           cairo_bo_event_t            **start_events,
+                           int                           num_events)
+{
+    event_queue->start_events = start_events;
+
+    _cairo_freepool_init (&event_queue->pool,
+                         sizeof (cairo_bo_queue_event_t));
+    _pqueue_init (&event_queue->pqueue);
+    event_queue->pqueue.elements[PQ_FIRST_ENTRY] = NULL;
+}
+
+static cairo_status_t
+_cairo_bo_event_queue_insert_stop (cairo_bo_event_queue_t      *event_queue,
+                                  cairo_bo_edge_t              *edge)
+{
+    cairo_bo_point32_t point;
+
+    point.y = edge->edge.bottom;
+    point.x = _line_compute_intersection_x_for_y (&edge->edge.line,
+                                                 point.y);
+    return _cairo_bo_event_queue_insert (event_queue,
+                                        CAIRO_BO_EVENT_TYPE_STOP,
+                                        edge, NULL,
+                                        &point);
+}
+
+static void
+_cairo_bo_event_queue_fini (cairo_bo_event_queue_t *event_queue)
+{
+    _pqueue_fini (&event_queue->pqueue);
+    _cairo_freepool_fini (&event_queue->pool);
+}
+
+static inline cairo_status_t
+_cairo_bo_event_queue_insert_if_intersect_below_current_y (cairo_bo_event_queue_t      *event_queue,
+                                                          cairo_bo_edge_t      *left,
+                                                          cairo_bo_edge_t *right)
+{
+    cairo_bo_point32_t intersection;
+
+    if (_line_equal (&left->edge.line, &right->edge.line))
+       return CAIRO_STATUS_SUCCESS;
+
+    /* The names "left" and "right" here are correct descriptions of
+     * the order of the two edges within the active edge list. So if a
+     * slope comparison also puts left less than right, then we know
+     * that the intersection of these two segments has already
+     * occurred before the current sweep line position. */
+    if (_slope_compare (left, right) <= 0)
+       return CAIRO_STATUS_SUCCESS;
+
+    if (! _cairo_bo_edge_intersect (left, right, &intersection))
+       return CAIRO_STATUS_SUCCESS;
+
+    return _cairo_bo_event_queue_insert (event_queue,
+                                        CAIRO_BO_EVENT_TYPE_INTERSECTION,
+                                        left, right,
+                                        &intersection);
+}
+
+static void
+_cairo_bo_sweep_line_init (cairo_bo_sweep_line_t *sweep_line)
+{
+    sweep_line->head = NULL;
+    sweep_line->stopped = NULL;
+    sweep_line->current_y = INT32_MIN;
+    sweep_line->current_edge = NULL;
+}
+
+static cairo_status_t
+_cairo_bo_sweep_line_insert (cairo_bo_sweep_line_t     *sweep_line,
+                            cairo_bo_edge_t            *edge)
+{
+    if (sweep_line->current_edge != NULL) {
+       cairo_bo_edge_t *prev, *next;
+       int cmp;
+
+       cmp = _cairo_bo_sweep_line_compare_edges (sweep_line,
+                                                 sweep_line->current_edge,
+                                                 edge);
+       if (cmp < 0) {
+           prev = sweep_line->current_edge;
+           next = prev->next;
+           while (next != NULL &&
+                  _cairo_bo_sweep_line_compare_edges (sweep_line,
+                                                      next, edge) < 0)
+           {
+               prev = next, next = prev->next;
+           }
+
+           prev->next = edge;
+           edge->prev = prev;
+           edge->next = next;
+           if (next != NULL)
+               next->prev = edge;
+       } else if (cmp > 0) {
+           next = sweep_line->current_edge;
+           prev = next->prev;
+           while (prev != NULL &&
+                  _cairo_bo_sweep_line_compare_edges (sweep_line,
+                                                      prev, edge) > 0)
+           {
+               next = prev, prev = next->prev;
+           }
+
+           next->prev = edge;
+           edge->next = next;
+           edge->prev = prev;
+           if (prev != NULL)
+               prev->next = edge;
+           else
+               sweep_line->head = edge;
+       } else {
+           prev = sweep_line->current_edge;
+           edge->prev = prev;
+           edge->next = prev->next;
+           if (prev->next != NULL)
+               prev->next->prev = edge;
+           prev->next = edge;
+       }
+    } else {
+       sweep_line->head = edge;
+       edge->next = NULL;
+    }
+
+    sweep_line->current_edge = edge;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_bo_sweep_line_delete (cairo_bo_sweep_line_t     *sweep_line,
+                            cairo_bo_edge_t    *edge)
+{
+    if (edge->prev != NULL)
+       edge->prev->next = edge->next;
+    else
+       sweep_line->head = edge->next;
+
+    if (edge->next != NULL)
+       edge->next->prev = edge->prev;
+
+    if (sweep_line->current_edge == edge)
+       sweep_line->current_edge = edge->prev ? edge->prev : edge->next;
+}
+
+static void
+_cairo_bo_sweep_line_swap (cairo_bo_sweep_line_t       *sweep_line,
+                          cairo_bo_edge_t              *left,
+                          cairo_bo_edge_t              *right)
+{
+    if (left->prev != NULL)
+       left->prev->next = right;
+    else
+       sweep_line->head = right;
+
+    if (right->next != NULL)
+       right->next->prev = left;
+
+    left->next = right->next;
+    right->next = left;
+
+    right->prev = left->prev;
+    left->prev = right;
+}
+
+#if DEBUG_PRINT_STATE
+static void
+_cairo_bo_edge_print (cairo_bo_edge_t *edge)
+{
+    printf ("(0x%x, 0x%x)-(0x%x, 0x%x)",
+           edge->edge.line.p1.x, edge->edge.line.p1.y,
+           edge->edge.line.p2.x, edge->edge.line.p2.y);
+}
+
+static void
+_cairo_bo_event_print (cairo_bo_event_t *event)
+{
+    switch (event->type) {
+    case CAIRO_BO_EVENT_TYPE_START:
+       printf ("Start: ");
+       break;
+    case CAIRO_BO_EVENT_TYPE_STOP:
+       printf ("Stop: ");
+       break;
+    case CAIRO_BO_EVENT_TYPE_INTERSECTION:
+       printf ("Intersection: ");
+       break;
+    }
+    printf ("(%d, %d)\t", event->point.x, event->point.y);
+    _cairo_bo_edge_print (event->e1);
+    if (event->type == CAIRO_BO_EVENT_TYPE_INTERSECTION) {
+       printf (" X ");
+       _cairo_bo_edge_print (event->e2);
+    }
+    printf ("\n");
+}
+
+static void
+_cairo_bo_event_queue_print (cairo_bo_event_queue_t *event_queue)
+{
+    /* XXX: fixme to print the start/stop array too. */
+    printf ("Event queue:\n");
+}
+
+static void
+_cairo_bo_sweep_line_print (cairo_bo_sweep_line_t *sweep_line)
+{
+    cairo_bool_t first = TRUE;
+    cairo_bo_edge_t *edge;
+
+    printf ("Sweep line from edge list: ");
+    first = TRUE;
+    for (edge = sweep_line->head;
+        edge;
+        edge = edge->next)
+    {
+       if (!first)
+           printf (", ");
+       _cairo_bo_edge_print (edge);
+       first = FALSE;
+    }
+    printf ("\n");
+}
+
+static void
+print_state (const char                        *msg,
+            cairo_bo_event_t           *event,
+            cairo_bo_event_queue_t     *event_queue,
+            cairo_bo_sweep_line_t      *sweep_line)
+{
+    printf ("%s ", msg);
+    _cairo_bo_event_print (event);
+    _cairo_bo_event_queue_print (event_queue);
+    _cairo_bo_sweep_line_print (sweep_line);
+    printf ("\n");
+}
+#endif
+
+#if DEBUG_EVENTS
+static void CAIRO_PRINTF_FORMAT (1, 2)
+event_log (const char *fmt, ...)
+{
+    FILE *file;
+
+    if (getenv ("CAIRO_DEBUG_EVENTS") == NULL)
+       return;
+
+    file = fopen ("bo-events.txt", "a");
+    if (file != NULL) {
+       va_list ap;
+
+       va_start (ap, fmt);
+       vfprintf (file, fmt, ap);
+       va_end (ap);
+
+       fclose (file);
+    }
+}
+#endif
+
+static inline cairo_bool_t
+edges_colinear (const cairo_bo_edge_t *a, const cairo_bo_edge_t *b)
+{
+    unsigned p;
+
+    p = 0;
+    p |= (a->edge.line.p1.x == b->edge.line.p1.x) << 0;
+    p |= (a->edge.line.p1.y == b->edge.line.p1.y) << 1;
+    p |= (a->edge.line.p2.x == b->edge.line.p2.x) << 3;
+    p |= (a->edge.line.p2.y == b->edge.line.p2.y) << 4;
+    if (p == ((1 << 0) | (1 << 1) | (1 << 3) | (1 << 4)))
+       return TRUE;
+
+    if (_slope_compare (a, b))
+       return FALSE;
+
+    /* The choice of y is not truly arbitrary since we must guarantee that it
+     * is greater than the start of either line.
+     */
+    if (p != 0) {
+       /* colinear if either end-point are coincident */
+       return ((p >> 1) & p) != 0;
+    } else if (a->edge.line.p1.y < b->edge.line.p1.y) {
+       return edge_compare_for_y_against_x (b,
+                                            a->edge.line.p1.y,
+                                            a->edge.line.p1.x) == 0;
+    } else {
+       return edge_compare_for_y_against_x (a,
+                                            b->edge.line.p1.y,
+                                            b->edge.line.p1.x) == 0;
+    }
+}
+
+/* Adds the trapezoid, if any, of the left edge to the #cairo_traps_t */
+static cairo_status_t
+_cairo_bo_edge_end_trap (cairo_bo_edge_t       *left,
+                        int32_t                 bot,
+                        cairo_traps_t          *traps)
+{
+    cairo_bo_trap_t *trap = &left->deferred_trap;
+
+    /* Only emit (trivial) non-degenerate trapezoids with positive height. */
+    if (likely (trap->top < bot)) {
+       _cairo_traps_add_trap (traps,
+                              trap->top, bot,
+                              &left->edge.line, &trap->right->edge.line);
+
+#if DEBUG_PRINT_STATE
+       printf ("Deferred trap: left=(%x, %x)-(%x,%x) "
+               "right=(%x,%x)-(%x,%x) top=%x, bot=%x\n",
+               left->edge.line.p1.x, left->edge.line.p1.y,
+               left->edge.line.p2.x, left->edge.line.p2.y,
+               trap->right->edge.line.p1.x, trap->right->edge.line.p1.y,
+               trap->right->edge.line.p2.x, trap->right->edge.line.p2.y,
+               trap->top, bot);
+#endif
+#if DEBUG_EVENTS
+       event_log ("end trap: %lu %lu %d %d\n",
+                  (long) left,
+                  (long) trap->right,
+                  trap->top,
+                  bot);
+#endif
+    }
+
+    trap->right = NULL;
+
+    return _cairo_traps_status (traps);
+}
+
+
+/* Start a new trapezoid at the given top y coordinate, whose edges
+ * are `edge' and `edge->next'. If `edge' already has a trapezoid,
+ * then either add it to the traps in `traps', if the trapezoid's
+ * right edge differs from `edge->next', or do nothing if the new
+ * trapezoid would be a continuation of the existing one. */
+static inline cairo_status_t
+_cairo_bo_edge_start_or_continue_trap (cairo_bo_edge_t *left,
+                                      cairo_bo_edge_t  *right,
+                                      int               top,
+                                      cairo_traps_t    *traps)
+{
+    cairo_status_t status;
+
+    if (left->deferred_trap.right == right)
+       return CAIRO_STATUS_SUCCESS;
+
+    assert (right);
+    if (left->deferred_trap.right != NULL) {
+       if (edges_colinear (left->deferred_trap.right, right))
+       {
+           /* continuation on right, so just swap edges */
+           left->deferred_trap.right = right;
+           return CAIRO_STATUS_SUCCESS;
+       }
+
+       status = _cairo_bo_edge_end_trap (left, top, traps);
+       if (unlikely (status))
+           return status;
+    }
+
+    if (! edges_colinear (left, right)) {
+       left->deferred_trap.top = top;
+       left->deferred_trap.right = right;
+
+#if DEBUG_EVENTS
+       event_log ("begin trap: %lu %lu %d\n",
+                  (long) left,
+                  (long) right,
+                  top);
+#endif
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static inline cairo_status_t
+_active_edges_to_traps (cairo_bo_edge_t        *pos,
+                       int32_t          top,
+                       unsigned         mask,
+                       cairo_traps_t        *traps)
+{
+    cairo_bo_edge_t *left;
+    cairo_status_t status;
+    int in_out;
+
+
+#if DEBUG_PRINT_STATE
+    printf ("Processing active edges for %x\n", top);
+#endif
+
+    in_out = 0;
+    left = pos;
+    while (pos != NULL) {
+       if (pos != left && pos->deferred_trap.right) {
+           /* XXX It shouldn't be possible to here with 2 deferred traps
+            * on colinear edges... See bug-bo-rictoz.
+            */
+           if (left->deferred_trap.right == NULL &&
+               edges_colinear (left, pos))
+           {
+               /* continuation on left */
+               left->deferred_trap = pos->deferred_trap;
+               pos->deferred_trap.right = NULL;
+           }
+           else
+           {
+               status = _cairo_bo_edge_end_trap (pos, top, traps);
+               if (unlikely (status))
+                   return status;
+           }
+       }
+
+       in_out += pos->edge.dir;
+       if ((in_out & mask) == 0) {
+           /* skip co-linear edges */
+           if (pos->next == NULL || ! edges_colinear (pos, pos->next)) {
+               status = _cairo_bo_edge_start_or_continue_trap (left, pos,
+                                                               top, traps);
+               if (unlikely (status))
+                   return status;
+
+               left = pos->next;
+           }
+       }
+
+       pos = pos->next;
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+
+/* Execute a single pass of the Bentley-Ottmann algorithm on edges,
+ * generating trapezoids according to the fill_rule and appending them
+ * to traps. */
+static cairo_status_t
+_cairo_bentley_ottmann_tessellate_bo_edges (cairo_bo_event_t   **start_events,
+                                           int                  num_events,
+                                           unsigned             fill_rule,
+                                           cairo_traps_t       *traps,
+                                           int                 *num_intersections)
+{
+    cairo_status_t status = CAIRO_STATUS_SUCCESS; /* silence compiler */
+    int intersection_count = 0;
+    cairo_bo_event_queue_t event_queue;
+    cairo_bo_sweep_line_t sweep_line;
+    cairo_bo_event_t *event;
+    cairo_bo_edge_t *left, *right;
+    cairo_bo_edge_t *e1, *e2;
+
+    /* convert the fill_rule into a winding mask */
+    if (fill_rule == CAIRO_FILL_RULE_WINDING)
+       fill_rule = (unsigned) -1;
+    else
+       fill_rule = 1;
+
+#if DEBUG_EVENTS
+    {
+       int i;
+
+       for (i = 0; i < num_events; i++) {
+           cairo_bo_start_event_t *event =
+               ((cairo_bo_start_event_t **) start_events)[i];
+           event_log ("edge: %lu (%d, %d) (%d, %d) (%d, %d) %d\n",
+                      (long) &events[i].edge,
+                      event->edge.edge.line.p1.x,
+                      event->edge.edge.line.p1.y,
+                      event->edge.edge.line.p2.x,
+                      event->edge.edge.line.p2.y,
+                      event->edge.top,
+                      event->edge.bottom,
+                      event->edge.edge.dir);
+       }
+    }
+#endif
+
+    _cairo_bo_event_queue_init (&event_queue, start_events, num_events);
+    _cairo_bo_sweep_line_init (&sweep_line);
+
+    while ((event = _cairo_bo_event_dequeue (&event_queue))) {
+       if (event->point.y != sweep_line.current_y) {
+           for (e1 = sweep_line.stopped; e1; e1 = e1->next) {
+               if (e1->deferred_trap.right != NULL) {
+                   status = _cairo_bo_edge_end_trap (e1,
+                                                     e1->edge.bottom,
+                                                     traps);
+                   if (unlikely (status))
+                       goto unwind;
+               }
+           }
+           sweep_line.stopped = NULL;
+
+           status = _active_edges_to_traps (sweep_line.head,
+                                            sweep_line.current_y,
+                                            fill_rule, traps);
+           if (unlikely (status))
+               goto unwind;
+
+           sweep_line.current_y = event->point.y;
+       }
+
+#if DEBUG_EVENTS
+       event_log ("event: %d (%ld, %ld) %lu, %lu\n",
+                  event->type,
+                  (long) event->point.x,
+                  (long) event->point.y,
+                  (long) event->e1,
+                  (long) event->e2);
+#endif
+
+       switch (event->type) {
+       case CAIRO_BO_EVENT_TYPE_START:
+           e1 = &((cairo_bo_start_event_t *) event)->edge;
+
+           status = _cairo_bo_sweep_line_insert (&sweep_line, e1);
+           if (unlikely (status))
+               goto unwind;
+
+           status = _cairo_bo_event_queue_insert_stop (&event_queue, e1);
+           if (unlikely (status))
+               goto unwind;
+
+           /* check to see if this is a continuation of a stopped edge */
+           /* XXX change to an infinitesimal lengthening rule */
+           for (left = sweep_line.stopped; left; left = left->next) {
+               if (e1->edge.top <= left->edge.bottom &&
+                   edges_colinear (e1, left))
+               {
+                   e1->deferred_trap = left->deferred_trap;
+                   if (left->prev != NULL)
+                       left->prev = left->next;
+                   else
+                       sweep_line.stopped = left->next;
+                   if (left->next != NULL)
+                       left->next->prev = left->prev;
+                   break;
+               }
+           }
+
+           left = e1->prev;
+           right = e1->next;
+
+           if (left != NULL) {
+               status = _cairo_bo_event_queue_insert_if_intersect_below_current_y (&event_queue, left, e1);
+               if (unlikely (status))
+                   goto unwind;
+           }
+
+           if (right != NULL) {
+               status = _cairo_bo_event_queue_insert_if_intersect_below_current_y (&event_queue, e1, right);
+               if (unlikely (status))
+                   goto unwind;
+           }
+
+           break;
+
+       case CAIRO_BO_EVENT_TYPE_STOP:
+           e1 = ((cairo_bo_queue_event_t *) event)->e1;
+           _cairo_bo_event_queue_delete (&event_queue, event);
+
+           left = e1->prev;
+           right = e1->next;
+
+           _cairo_bo_sweep_line_delete (&sweep_line, e1);
+
+           /* first, check to see if we have a continuation via a fresh edge */
+           if (e1->deferred_trap.right != NULL) {
+               e1->next = sweep_line.stopped;
+               if (sweep_line.stopped != NULL)
+                   sweep_line.stopped->prev = e1;
+               sweep_line.stopped = e1;
+               e1->prev = NULL;
+           }
+
+           if (left != NULL && right != NULL) {
+               status = _cairo_bo_event_queue_insert_if_intersect_below_current_y (&event_queue, left, right);
+               if (unlikely (status))
+                   goto unwind;
+           }
+
+           break;
+
+       case CAIRO_BO_EVENT_TYPE_INTERSECTION:
+           e1 = ((cairo_bo_queue_event_t *) event)->e1;
+           e2 = ((cairo_bo_queue_event_t *) event)->e2;
+           _cairo_bo_event_queue_delete (&event_queue, event);
+
+           /* skip this intersection if its edges are not adjacent */
+           if (e2 != e1->next)
+               break;
+
+           intersection_count++;
+
+           left = e1->prev;
+           right = e2->next;
+
+           _cairo_bo_sweep_line_swap (&sweep_line, e1, e2);
+
+           /* after the swap e2 is left of e1 */
+
+           if (left != NULL) {
+               status = _cairo_bo_event_queue_insert_if_intersect_below_current_y (&event_queue, left, e2);
+               if (unlikely (status))
+                   goto unwind;
+           }
+
+           if (right != NULL) {
+               status = _cairo_bo_event_queue_insert_if_intersect_below_current_y (&event_queue, e1, right);
+               if (unlikely (status))
+                   goto unwind;
+           }
+
+           break;
+       }
+    }
+
+    *num_intersections = intersection_count;
+    for (e1 = sweep_line.stopped; e1; e1 = e1->next) {
+       if (e1->deferred_trap.right != NULL) {
+           status = _cairo_bo_edge_end_trap (e1, e1->edge.bottom, traps);
+           if (unlikely (status))
+               break;
+       }
+    }
+ unwind:
+    _cairo_bo_event_queue_fini (&event_queue);
+
+#if DEBUG_EVENTS
+    event_log ("\n");
+#endif
+
+    return status;
+}
+
+cairo_status_t
+_cairo_bentley_ottmann_tessellate_polygon (cairo_traps_t        *traps,
+                                          const cairo_polygon_t *polygon,
+                                          cairo_fill_rule_t      fill_rule)
+{
+    int intersections;
+    cairo_status_t status;
+    cairo_bo_start_event_t stack_events[CAIRO_STACK_ARRAY_LENGTH (cairo_bo_start_event_t)];
+    cairo_bo_start_event_t *events;
+    cairo_bo_event_t *stack_event_ptrs[ARRAY_LENGTH (stack_events) + 1];
+    cairo_bo_event_t **event_ptrs;
+    cairo_bo_start_event_t *stack_event_y[64];
+    cairo_bo_start_event_t **event_y = NULL;
+    int i, num_events, y, ymin, ymax;
+
+    num_events = polygon->num_edges;
+    if (unlikely (0 == num_events))
+       return CAIRO_STATUS_SUCCESS;
+
+    if (polygon->num_limits) {
+       ymin = _cairo_fixed_integer_floor (polygon->limit.p1.y);
+       ymax = _cairo_fixed_integer_ceil (polygon->limit.p2.y) - ymin;
+
+       if (ymax > 64)
+           event_y = _cairo_malloc_ab(sizeof (cairo_bo_event_t*), ymax);
+       else
+           event_y = stack_event_y;
+       memset (event_y, 0, ymax * sizeof(cairo_bo_event_t *));
+    }
+
+    events = stack_events;
+    event_ptrs = stack_event_ptrs;
+    if (num_events > ARRAY_LENGTH (stack_events)) {
+       events = _cairo_malloc_ab_plus_c (num_events,
+                                         sizeof (cairo_bo_start_event_t) +
+                                         sizeof (cairo_bo_event_t *),
+                                         sizeof (cairo_bo_event_t *));
+       if (unlikely (events == NULL))
+           return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+       event_ptrs = (cairo_bo_event_t **) (events + num_events);
+    }
+
+    for (i = 0; i < num_events; i++) {
+       events[i].type = CAIRO_BO_EVENT_TYPE_START;
+       events[i].point.y = polygon->edges[i].top;
+       events[i].point.x =
+           _line_compute_intersection_x_for_y (&polygon->edges[i].line,
+                                               events[i].point.y);
+
+       events[i].edge.edge = polygon->edges[i];
+       events[i].edge.deferred_trap.right = NULL;
+       events[i].edge.prev = NULL;
+       events[i].edge.next = NULL;
+
+       if (event_y) {
+           y = _cairo_fixed_integer_floor (events[i].point.y) - ymin;
+           events[i].edge.next = (cairo_bo_edge_t *) event_y[y];
+           event_y[y] = (cairo_bo_start_event_t *) &events[i];
+       } else
+           event_ptrs[i] = (cairo_bo_event_t *) &events[i];
+    }
+
+    if (event_y) {
+       for (y = i = 0; y < ymax && i < num_events; y++) {
+           cairo_bo_start_event_t *e;
+           int j = i;
+           for (e = event_y[y]; e; e = (cairo_bo_start_event_t *)e->edge.next)
+               event_ptrs[i++] = (cairo_bo_event_t *) e;
+           if (i > j + 1)
+               _cairo_bo_event_queue_sort (event_ptrs+j, i-j);
+       }
+       if (event_y != stack_event_y)
+           free (event_y);
+    } else
+       _cairo_bo_event_queue_sort (event_ptrs, i);
+    event_ptrs[i] = NULL;
+
+#if DEBUG_TRAPS
+    dump_edges (events, num_events, "bo-polygon-edges.txt");
+#endif
+
+    /* XXX: This would be the convenient place to throw in multiple
+     * passes of the Bentley-Ottmann algorithm. It would merely
+     * require storing the results of each pass into a temporary
+     * cairo_traps_t. */
+    status = _cairo_bentley_ottmann_tessellate_bo_edges (event_ptrs, num_events,
+                                                        fill_rule, traps,
+                                                        &intersections);
+#if DEBUG_TRAPS
+    dump_traps (traps, "bo-polygon-out.txt");
+#endif
+
+    if (events != stack_events)
+       free (events);
+
+    return status;
+}
+
+cairo_status_t
+_cairo_bentley_ottmann_tessellate_traps (cairo_traps_t *traps,
+                                        cairo_fill_rule_t fill_rule)
+{
+    cairo_status_t status;
+    cairo_polygon_t polygon;
+    int i;
+
+    if (unlikely (0 == traps->num_traps))
+       return CAIRO_STATUS_SUCCESS;
+
+#if DEBUG_TRAPS
+    dump_traps (traps, "bo-traps-in.txt");
+#endif
+
+    _cairo_polygon_init (&polygon, traps->limits, traps->num_limits);
+
+    for (i = 0; i < traps->num_traps; i++) {
+       status = _cairo_polygon_add_line (&polygon,
+                                         &traps->traps[i].left,
+                                         traps->traps[i].top,
+                                         traps->traps[i].bottom,
+                                         1);
+       if (unlikely (status))
+           goto CLEANUP;
+
+       status = _cairo_polygon_add_line (&polygon,
+                                         &traps->traps[i].right,
+                                         traps->traps[i].top,
+                                         traps->traps[i].bottom,
+                                         -1);
+       if (unlikely (status))
+           goto CLEANUP;
+    }
+
+    _cairo_traps_clear (traps);
+    status = _cairo_bentley_ottmann_tessellate_polygon (traps,
+                                                       &polygon,
+                                                       fill_rule);
+
+#if DEBUG_TRAPS
+    dump_traps (traps, "bo-traps-out.txt");
+#endif
+
+  CLEANUP:
+    _cairo_polygon_fini (&polygon);
+
+    return status;
+}
+
+#if 0
+static cairo_bool_t
+edges_have_an_intersection_quadratic (cairo_bo_edge_t  *edges,
+                                     int                num_edges)
+
+{
+    int i, j;
+    cairo_bo_edge_t *a, *b;
+    cairo_bo_point32_t intersection;
+
+    /* We must not be given any upside-down edges. */
+    for (i = 0; i < num_edges; i++) {
+       assert (_cairo_bo_point32_compare (&edges[i].top, &edges[i].bottom) < 0);
+       edges[i].line.p1.x <<= CAIRO_BO_GUARD_BITS;
+       edges[i].line.p1.y <<= CAIRO_BO_GUARD_BITS;
+       edges[i].line.p2.x <<= CAIRO_BO_GUARD_BITS;
+       edges[i].line.p2.y <<= CAIRO_BO_GUARD_BITS;
+    }
+
+    for (i = 0; i < num_edges; i++) {
+       for (j = 0; j < num_edges; j++) {
+           if (i == j)
+               continue;
+
+           a = &edges[i];
+           b = &edges[j];
+
+           if (! _cairo_bo_edge_intersect (a, b, &intersection))
+               continue;
+
+           printf ("Found intersection (%d,%d) between (%d,%d)-(%d,%d) and (%d,%d)-(%d,%d)\n",
+                   intersection.x,
+                   intersection.y,
+                   a->line.p1.x, a->line.p1.y,
+                   a->line.p2.x, a->line.p2.y,
+                   b->line.p1.x, b->line.p1.y,
+                   b->line.p2.x, b->line.p2.y);
+
+           return TRUE;
+       }
+    }
+    return FALSE;
+}
+
+#define TEST_MAX_EDGES 10
+
+typedef struct test {
+    const char *name;
+    const char *description;
+    int num_edges;
+    cairo_bo_edge_t edges[TEST_MAX_EDGES];
+} test_t;
+
+static test_t
+tests[] = {
+    {
+       "3 near misses",
+       "3 edges all intersecting very close to each other",
+       3,
+       {
+           { { 4, 2}, {0, 0}, { 9, 9}, NULL, NULL },
+           { { 7, 2}, {0, 0}, { 2, 3}, NULL, NULL },
+           { { 5, 2}, {0, 0}, { 1, 7}, NULL, NULL }
+       }
+    },
+    {
+       "inconsistent data",
+       "Derived from random testing---was leading to skip list and edge list disagreeing.",
+       2,
+       {
+           { { 2, 3}, {0, 0}, { 8, 9}, NULL, NULL },
+           { { 2, 3}, {0, 0}, { 6, 7}, NULL, NULL }
+       }
+    },
+    {
+       "failed sort",
+       "A test derived from random testing that leads to an inconsistent sort --- looks like we just can't attempt to validate the sweep line with edge_compare?",
+       3,
+       {
+           { { 6, 2}, {0, 0}, { 6, 5}, NULL, NULL },
+           { { 3, 5}, {0, 0}, { 5, 6}, NULL, NULL },
+           { { 9, 2}, {0, 0}, { 5, 6}, NULL, NULL },
+       }
+    },
+    {
+       "minimal-intersection",
+       "Intersection of a two from among the smallest possible edges.",
+       2,
+       {
+           { { 0, 0}, {0, 0}, { 1, 1}, NULL, NULL },
+           { { 1, 0}, {0, 0}, { 0, 1}, NULL, NULL }
+       }
+    },
+    {
+       "simple",
+       "A simple intersection of two edges at an integer (2,2).",
+       2,
+       {
+           { { 1, 1}, {0, 0}, { 3, 3}, NULL, NULL },
+           { { 2, 1}, {0, 0}, { 2, 3}, NULL, NULL }
+       }
+    },
+    {
+       "bend-to-horizontal",
+       "With intersection truncation one edge bends to horizontal",
+       2,
+       {
+           { { 9, 1}, {0, 0}, {3, 7}, NULL, NULL },
+           { { 3, 5}, {0, 0}, {9, 9}, NULL, NULL }
+       }
+    }
+};
+
+/*
+    {
+       "endpoint",
+       "An intersection that occurs at the endpoint of a segment.",
+       {
+           { { 4, 6}, { 5, 6}, NULL, { { NULL }} },
+           { { 4, 5}, { 5, 7}, NULL, { { NULL }} },
+           { { 0, 0}, { 0, 0}, NULL, { { NULL }} },
+       }
+    }
+    {
+       name = "overlapping",
+       desc = "Parallel segments that share an endpoint, with different slopes.",
+       edges = {
+           { top = { x = 2, y = 0}, bottom = { x = 1, y = 1}},
+           { top = { x = 2, y = 0}, bottom = { x = 0, y = 2}},
+           { top = { x = 0, y = 3}, bottom = { x = 1, y = 3}},
+           { top = { x = 0, y = 3}, bottom = { x = 2, y = 3}},
+           { top = { x = 0, y = 4}, bottom = { x = 0, y = 6}},
+           { top = { x = 0, y = 5}, bottom = { x = 0, y = 6}}
+       }
+    },
+    {
+       name = "hobby_stage_3",
+       desc = "A particularly tricky part of the 3rd stage of the 'hobby' test below.",
+       edges = {
+           { top = { x = -1, y = -2}, bottom = { x =  4, y = 2}},
+           { top = { x =  5, y =  3}, bottom = { x =  9, y = 5}},
+           { top = { x =  5, y =  3}, bottom = { x =  6, y = 3}},
+       }
+    },
+    {
+       name = "hobby",
+       desc = "Example from John Hobby's paper. Requires 3 passes of the iterative algorithm.",
+       edges = {
+           { top = { x =   0, y =   0}, bottom = { x =   9, y =   5}},
+           { top = { x =   0, y =   0}, bottom = { x =  13, y =   6}},
+           { top = { x =  -1, y =  -2}, bottom = { x =   9, y =   5}}
+       }
+    },
+    {
+       name = "slope",
+       desc = "Edges with same start/stop points but different slopes",
+       edges = {
+           { top = { x = 4, y = 1}, bottom = { x = 6, y = 3}},
+           { top = { x = 4, y = 1}, bottom = { x = 2, y = 3}},
+           { top = { x = 2, y = 4}, bottom = { x = 4, y = 6}},
+           { top = { x = 6, y = 4}, bottom = { x = 4, y = 6}}
+       }
+    },
+    {
+       name = "horizontal",
+       desc = "Test of a horizontal edge",
+       edges = {
+           { top = { x = 1, y = 1}, bottom = { x = 6, y = 6}},
+           { top = { x = 2, y = 3}, bottom = { x = 5, y = 3}}
+       }
+    },
+    {
+       name = "vertical",
+       desc = "Test of a vertical edge",
+       edges = {
+           { top = { x = 5, y = 1}, bottom = { x = 5, y = 7}},
+           { top = { x = 2, y = 4}, bottom = { x = 8, y = 5}}
+       }
+    },
+    {
+       name = "congruent",
+       desc = "Two overlapping edges with the same slope",
+       edges = {
+           { top = { x = 5, y = 1}, bottom = { x = 5, y = 7}},
+           { top = { x = 5, y = 2}, bottom = { x = 5, y = 6}},
+           { top = { x = 2, y = 4}, bottom = { x = 8, y = 5}}
+       }
+    },
+    {
+       name = "multi",
+       desc = "Several segments with a common intersection point",
+       edges = {
+           { top = { x = 1, y = 2}, bottom = { x = 5, y = 4} },
+           { top = { x = 1, y = 1}, bottom = { x = 5, y = 5} },
+           { top = { x = 2, y = 1}, bottom = { x = 4, y = 5} },
+           { top = { x = 4, y = 1}, bottom = { x = 2, y = 5} },
+           { top = { x = 5, y = 1}, bottom = { x = 1, y = 5} },
+           { top = { x = 5, y = 2}, bottom = { x = 1, y = 4} }
+       }
+    }
+};
+*/
+
+static int
+run_test (const char           *test_name,
+          cairo_bo_edge_t      *test_edges,
+          int                   num_edges)
+{
+    int i, intersections, passes;
+    cairo_bo_edge_t *edges;
+    cairo_array_t intersected_edges;
+
+    printf ("Testing: %s\n", test_name);
+
+    _cairo_array_init (&intersected_edges, sizeof (cairo_bo_edge_t));
+
+    intersections = _cairo_bentley_ottmann_intersect_edges (test_edges, num_edges, &intersected_edges);
+    if (intersections)
+       printf ("Pass 1 found %d intersections:\n", intersections);
+
+
+    /* XXX: Multi-pass Bentley-Ottmmann. Preferable would be to add a
+     * pass of Hobby's tolerance-square algorithm instead. */
+    passes = 1;
+    while (intersections) {
+       int num_edges = _cairo_array_num_elements (&intersected_edges);
+       passes++;
+       edges = _cairo_malloc_ab (num_edges, sizeof (cairo_bo_edge_t));
+       assert (edges != NULL);
+       memcpy (edges, _cairo_array_index (&intersected_edges, 0), num_edges * sizeof (cairo_bo_edge_t));
+       _cairo_array_fini (&intersected_edges);
+       _cairo_array_init (&intersected_edges, sizeof (cairo_bo_edge_t));
+       intersections = _cairo_bentley_ottmann_intersect_edges (edges, num_edges, &intersected_edges);
+       free (edges);
+
+       if (intersections){
+           printf ("Pass %d found %d remaining intersections:\n", passes, intersections);
+       } else {
+           if (passes > 3)
+               for (i = 0; i < passes; i++)
+                   printf ("*");
+           printf ("No remainining intersections found after pass %d\n", passes);
+       }
+    }
+
+    if (edges_have_an_intersection_quadratic (_cairo_array_index (&intersected_edges, 0),
+                                             _cairo_array_num_elements (&intersected_edges)))
+       printf ("*** FAIL ***\n");
+    else
+       printf ("PASS\n");
+
+    _cairo_array_fini (&intersected_edges);
+
+    return 0;
+}
+
+#define MAX_RANDOM 300
+
+int
+main (void)
+{
+    char random_name[] = "random-XX";
+    cairo_bo_edge_t random_edges[MAX_RANDOM], *edge;
+    unsigned int i, num_random;
+    test_t *test;
+
+    for (i = 0; i < ARRAY_LENGTH (tests); i++) {
+       test = &tests[i];
+       run_test (test->name, test->edges, test->num_edges);
+    }
+
+    for (num_random = 0; num_random < MAX_RANDOM; num_random++) {
+       srand (0);
+       for (i = 0; i < num_random; i++) {
+           do {
+               edge = &random_edges[i];
+               edge->line.p1.x = (int32_t) (10.0 * (rand() / (RAND_MAX + 1.0)));
+               edge->line.p1.y = (int32_t) (10.0 * (rand() / (RAND_MAX + 1.0)));
+               edge->line.p2.x = (int32_t) (10.0 * (rand() / (RAND_MAX + 1.0)));
+               edge->line.p2.y = (int32_t) (10.0 * (rand() / (RAND_MAX + 1.0)));
+               if (edge->line.p1.y > edge->line.p2.y) {
+                   int32_t tmp = edge->line.p1.y;
+                   edge->line.p1.y = edge->line.p2.y;
+                   edge->line.p2.y = tmp;
+               }
+           } while (edge->line.p1.y == edge->line.p2.y);
+       }
+
+       sprintf (random_name, "random-%02d", num_random);
+
+       run_test (random_name, random_edges, num_random);
+    }
+
+    return 0;
+}
+#endif
diff --git a/src/cairo-beos-surface.cpp b/src/cairo-beos-surface.cpp
new file mode 100644 (file)
index 0000000..2e0f42c
--- /dev/null
@@ -0,0 +1,979 @@
+/* vim:set ts=8 sw=4 noet cin: */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2005 Christian Biesinger <cbiesinger@web.de>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Christian Biesinger
+ * <cbiesinger@web.de>
+ *
+ * Contributor(s):
+ */
+
+// This is a C++ file in order to use the C++ BeOS API
+
+#include "cairoint.h"
+
+#include "cairo-beos.h"
+
+#include "cairo-error-private.h"
+
+#include <new>
+
+#include <Bitmap.h>
+#include <Region.h>
+#if 0
+#include <DirectWindow.h>
+#endif
+#include <Screen.h>
+#include <Window.h>
+#include <Locker.h>
+
+/**
+ * SECTION:beos-surface
+ * @Title: BeOS Surfaces
+ * @Short_Description: BeOS surface support
+ * @See_Also: #cairo_surface_t
+ *
+ * The BeOS surface is used to render cairo graphics to BeOS views 
+ * and bitmaps.
+ */
+
+#define CAIRO_INT_STATUS_SUCCESS (cairo_int_status_t)(CAIRO_STATUS_SUCCESS)
+
+struct cairo_beos_surface_t {
+    cairo_surface_t base;
+
+    cairo_region_t *clip_region;
+
+    BView* view;
+
+    /*
+     * A view is either attached to a bitmap, a window, or unattached.
+     * If it is attached to a window, we can copy data out of it using BScreen.
+     * If it is attached to a bitmap, we can read the bitmap data.
+     * If it is not attached, it doesn't draw anything, we need not bother.
+     *
+     * Since there doesn't seem to be a way to get the bitmap from a view if it
+     * is attached to one, we have to use a special surface creation function.
+     */
+
+    BBitmap* bitmap;
+
+    // If true, surface and view should be deleted when this surface is
+    // destroyed
+    bool owns_bitmap_view;
+};
+
+class AutoLockView {
+    public:
+       AutoLockView(BView* view) : mView(view) {
+           mOK = mView->LockLooper();
+       }
+
+       ~AutoLockView() {
+           if (mOK)
+               mView->UnlockLooper();
+       }
+
+       operator bool() {
+           return mOK;
+       }
+
+    private:
+       BView* mView;
+       bool   mOK;
+};
+
+static cairo_surface_t *
+_cairo_beos_surface_create_internal (BView*   view,
+                                    BBitmap* bmp,
+                                    bool     owns_bitmap_view = false);
+
+static inline BRect
+_cairo_rectangle_to_brect (const cairo_rectangle_int_t* rect)
+{
+    // A BRect is one pixel wider than you'd think
+    return BRect (rect->x, rect->y,
+                 rect->x + rect->width - 1,
+                 rect->y + rect->height - 1);
+}
+
+static inline cairo_rectangle_int_t
+_brect_to_cairo_rectangle (const BRect &rect)
+{
+    cairo_rectangle_int_t retval;
+    retval.x = floor (rect.left);
+    retval.y = floor (rect.top);
+    retval.width = ceil (rect.right) - retval.x + 1;
+    retval.height = ceil (rect.bottom) - rectval.y + 1;
+    return retval;
+}
+
+static inline rgb_color
+_cairo_color_to_be_color (const cairo_color_t *color)
+{
+    // This factor ensures a uniform distribution of numbers
+    const float factor = 256 - 1e-5;
+    // Using doubles to have non-premultiplied colors
+    rgb_color be_color = { uint8(color->red * factor),
+                          uint8(color->green * factor),
+                          uint8(color->blue * factor),
+                          uint8(color->alpha * factor) };
+
+    return be_color;
+}
+
+enum ViewCopyStatus {
+    OK,
+    NOT_VISIBLE, // The view or the interest rect is not visible on screen
+    ERROR        // The view was visible, but the rect could not be copied. Probably OOM
+};
+
+/**
+ * _cairo_beos_view_to_bitmap:
+ * @bitmap: [out] The resulting bitmap.
+ * @rect: [out] The rectangle that was copied, in the view's coordinate system
+ * @interestRect: If non-null, only this part of the view will be copied (view's coord system).
+ *
+ * Gets the contents of the view as a BBitmap*. Caller must delete the bitmap.
+ **/
+static ViewCopyStatus
+_cairo_beos_view_to_bitmap (BView*       view,
+                           BBitmap**    bitmap,
+                           BRect*       rect = NULL,
+                           const BRect* interestRect = NULL)
+{
+    *bitmap = NULL;
+
+    BWindow* wnd = view->Window();
+    // If we have no window, can't do anything
+    if (!wnd)
+       return NOT_VISIBLE;
+
+    view->Sync();
+    wnd->Sync();
+
+#if 0
+    // Is it a direct window?
+    BDirectWindow* directWnd = dynamic_cast<BDirectWindow*>(wnd);
+    if (directWnd) {
+       // WRITEME
+    }
+#endif
+
+    // Is it visible? If so, we can copy the content off the screen
+    if (wnd->IsHidden())
+       return NOT_VISIBLE;
+
+    BRect rectToCopy(view->Bounds());
+    if (interestRect)
+       rectToCopy = rectToCopy & *interestRect;
+
+    if (!rectToCopy.IsValid())
+       return NOT_VISIBLE;
+
+    BScreen screen(wnd);
+    BRect screenRect(view->ConvertToScreen(rectToCopy));
+    screenRect = screenRect & screen.Frame();
+
+    if (!screen.IsValid())
+       return NOT_VISIBLE;
+
+    if (rect)
+       *rect = view->ConvertFromScreen(screenRect);
+
+    if (screen.GetBitmap(bitmap, false, &screenRect) == B_OK)
+       return OK;
+
+    return ERROR;
+}
+
+static void
+unpremultiply_bgra (unsigned char* data,
+                   int            width,
+                   int            height,
+                   int            stride,
+                   unsigned char* retdata)
+{
+    unsigned char* end = data + stride * height;
+    for (unsigned char* in = data, *out = retdata;
+        in < end;
+        in += stride, out += stride)
+    {
+       for (int i = 0; i < width; i ++) {
+           uint8_t *b = &out[4*i];
+           uint32_t pixel;
+           uint8_t  alpha;
+
+           memcpy (&pixel, &data[4*i], sizeof (uint32_t));
+           alpha = pixel & 0xff;
+           if (alpha == 0) {
+               b[0] = b[1] = b[2] = b[3] = 0;
+           } else {
+               b[0] = (((pixel >> 24) & 0xff) * 255 + alpha / 2) / alpha;
+               b[1] = (((pixel >> 16) & 0xff) * 255 + alpha / 2) / alpha;
+               b[2] = (((pixel >>  8) & 0xff) * 255 + alpha / 2) / alpha;
+               b[3] = alpha;
+           }
+       }
+    }
+}
+
+static inline int
+multiply_alpha (int alpha, int color)
+{
+    int temp = (alpha * color) + 0x80;
+    return ((temp + (temp >> 8)) >> 8);
+}
+
+static unsigned char*
+premultiply_bgra (unsigned char* data,
+                 int            width,
+                 int            height,
+                 int            stride)
+{
+    uint8_t * retdata = reinterpret_cast<unsigned char*>(_cairo_malloc_ab(height, stride));
+    if (!retdata)
+       return NULL;
+
+    uint8_t * end = data + stride * height;
+    for (uint8_t * in = data, *out = retdata;
+        in < end;
+        in += stride, out += stride)
+    {
+       for (int i = 0; i < width; i ++) {
+           uint8_t *base  = &in[4*i];
+           uint8_t  alpha = base[3];
+           uint32_t p;
+
+           if (alpha == 0) {
+               p = 0;
+           } else {
+               uint8_t  blue  = base[0];
+               uint8_t  green = base[1];
+               uint8_t  red   = base[2];
+
+               if (alpha != 0xff) {
+                   blue  = multiply_alpha (alpha, blue);
+                   green = multiply_alpha (alpha, green);
+                   red   = multiply_alpha (alpha, red);
+               }
+               p = (alpha << 0) | (red << 8) | (green << 16) | (blue << 24);
+           }
+           memcpy (&out[4*i], &p, sizeof (uint32_t));
+       }
+    }
+    return retdata;
+}
+
+static cairo_int_status_t
+_cairo_beos_surface_set_clip_region (cairo_beos_surface_t *surface,
+                                    cairo_region_t     *region)
+{
+    cairo_beos_surface_t *surface = reinterpret_cast<cairo_beos_surface_t*>(
+                                                       abstract_surface);
+    AutoLockView locker(surface->view);
+    assert (locker);
+
+    if (region == surface->clip_region)
+       return CAIRO_INT_STATUS_SUCCESS;
+
+    cairo_region_destroy (surface->clip_region);
+    surface->clip_region = cairo_region_reference (region);
+
+    if (region == NULL) {
+       // No clipping
+       surface->view->ConstrainClippingRegion(NULL);
+       return CAIRO_INT_STATUS_SUCCESS;
+    }
+
+    int count = cairo_region_num_rectangles (region);
+    BRegion bregion;
+    for (int i = 0; i < count; ++i) {
+       cairo_rectangle_int_t rect;
+
+       cairo_region_get_rectangle (region, i, &rect);
+       // Have to substract one, because for pixman, the second coordinate
+       // lies outside the rectangle.
+       bregion.Include (_cairo_rectangle_to_brect (&rect));
+    }
+    surface->view->ConstrainClippingRegion(&bregion);
+    return CAIRO_INT_STATUS_SUCCESS;
+}
+
+
+/**
+ * _cairo_beos_bitmap_to_surface:
+ *
+ * Returns an addrefed image surface for a BBitmap. The bitmap need not outlive
+ * the surface.
+ **/
+static cairo_image_surface_t*
+_cairo_beos_bitmap_to_surface (BBitmap* bitmap)
+{
+    color_space format = bitmap->ColorSpace();
+    if (format != B_RGB32 && format != B_RGBA32) {
+       BBitmap bmp(bitmap->Bounds(), B_RGB32, true);
+       BView view(bitmap->Bounds(), "Cairo bitmap drawing view",
+                  B_FOLLOW_ALL_SIDES, 0);
+       bmp.AddChild(&view);
+
+       view.LockLooper();
+
+       view.DrawBitmap(bitmap, BPoint(0.0, 0.0));
+       view.Sync();
+
+       cairo_image_surface_t* imgsurf = _cairo_beos_bitmap_to_surface(&bmp);
+
+       view.UnlockLooper();
+       bmp.RemoveChild(&view);
+       return imgsurf;
+    }
+
+    cairo_format_t cformat = format == B_RGB32 ?
+                            CAIRO_FORMAT_RGB24 : CAIRO_FORMAT_ARGB32;
+
+    BRect bounds(bitmap->Bounds());
+    unsigned char* bits = reinterpret_cast<unsigned char*>(bitmap->Bits());
+    int width = bounds.IntegerWidth() + 1;
+    int height = bounds.IntegerHeight() + 1;
+    unsigned char* premultiplied;
+    if (cformat == CAIRO_FORMAT_ARGB32) {
+       premultiplied = premultiply_bgra (bits, width, height,
+                                        bitmap->BytesPerRow());
+    } else {
+       premultiplied = reinterpret_cast<unsigned char*>(
+                                       _cairo_malloc_ab(bitmap->BytesPerRow(), height));
+       if (premultiplied)
+           memcpy(premultiplied, bits, bitmap->BytesPerRow() * height);
+    }
+    if (!premultiplied)
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+    cairo_image_surface_t* surf = reinterpret_cast<cairo_image_surface_t*>
+       (cairo_image_surface_create_for_data(premultiplied,
+                                            cformat,
+                                            width,
+                                            height,
+                                            bitmap->BytesPerRow()));
+    if (surf->base.status)
+       free(premultiplied);
+    else
+       _cairo_image_surface_assume_ownership_of_data(surf);
+    return surf;
+}
+
+/**
+ * _cairo_image_surface_to_bitmap:
+ *
+ * Converts an image surface to a BBitmap. The return value must be freed with
+ * delete.
+ **/
+static BBitmap*
+_cairo_image_surface_to_bitmap (cairo_image_surface_t* surface)
+{
+    BRect size(0.0, 0.0, surface->width - 1, surface->height - 1);
+    switch (surface->format) {
+       case CAIRO_FORMAT_ARGB32: {
+           BBitmap* data = new BBitmap(size, B_RGBA32);
+           unpremultiply_bgra (surface->data,
+                               surface->width,
+                               surface->height,
+                               surface->stride,
+                               reinterpret_cast<unsigned char*>(data->Bits()));
+           return data;
+        }
+       case CAIRO_FORMAT_RGB24: {
+           BBitmap* data = new BBitmap(size, B_RGB32);
+           memcpy(data->Bits(), surface->data, surface->height * surface->stride);
+           return data;
+       }
+       default:
+           assert(0);
+           return NULL;
+    }
+}
+
+/**
+ * _cairo_op_to_be_op:
+ *
+ * Converts a cairo drawing operator to a beos drawing_mode. Returns true if
+ * the operator could be converted, false otherwise.
+ **/
+static bool
+_cairo_op_to_be_op (cairo_operator_t cairo_op,
+                   drawing_mode*    beos_op)
+{
+    switch (cairo_op) {
+    case CAIRO_OPERATOR_SOURCE:
+       *beos_op = B_OP_COPY;
+       return true;
+    case CAIRO_OPERATOR_OVER:
+       *beos_op = B_OP_ALPHA;
+       return true;
+
+    case CAIRO_OPERATOR_ADD:
+       // Does not actually work
+       // XXX This is a fundamental compositing operator, it has to work!
+#if 1
+       return false;
+#else
+       *beos_op = B_OP_ADD;
+       return true;
+#endif
+
+    case CAIRO_OPERATOR_CLEAR:
+       // Does not map to B_OP_ERASE - it replaces the dest with the low
+       // color, instead of transparency; could be done by setting low
+       // color appropriately.
+
+    case CAIRO_OPERATOR_IN:
+    case CAIRO_OPERATOR_OUT:
+    case CAIRO_OPERATOR_ATOP:
+
+    case CAIRO_OPERATOR_DEST:
+    case CAIRO_OPERATOR_DEST_OVER:
+    case CAIRO_OPERATOR_DEST_IN:
+    case CAIRO_OPERATOR_DEST_OUT:
+    case CAIRO_OPERATOR_DEST_ATOP:
+
+    case CAIRO_OPERATOR_XOR:
+    case CAIRO_OPERATOR_SATURATE:
+
+    default:
+       return false;
+    }
+}
+
+static cairo_surface_t *
+_cairo_beos_surface_create_similar (void            *abstract_surface,
+                                   cairo_content_t  content,
+                                   int              width,
+                                   int              height)
+{
+    cairo_beos_surface_t *surface = reinterpret_cast<cairo_beos_surface_t*>(
+                                                       abstract_surface);
+
+    if (width <= 0)
+       width = 1;
+    if (height <= 0)
+       height = 1;
+
+    BRect rect(0.0, 0.0, width - 1, height - 1);
+    BBitmap* bmp;
+    switch (content) {
+       case CAIRO_CONTENT_ALPHA:
+           return NULL;
+       case CAIRO_CONTENT_COLOR_ALPHA:
+           bmp = new BBitmap(rect, B_RGBA32, true);
+           break;
+       case CAIRO_CONTENT_COLOR:
+           // Match the color depth
+           if (surface->bitmap) {
+               color_space space = surface->bitmap->ColorSpace();
+               // No alpha was requested -> make sure not to return
+               // a surface with alpha
+               if (space == B_RGBA32)
+                   space = B_RGB32;
+               if (space == B_RGBA15)
+                   space = B_RGB15;
+               bmp = new BBitmap(rect, space, true);
+           } else {
+               BScreen scr(surface->view->Window());
+               color_space space = B_RGB32;
+               if (scr.IsValid())
+                   space = scr.ColorSpace();
+               bmp = new BBitmap(rect, space, true);
+           }
+           break;
+       default:
+           ASSERT_NOT_REACHED;
+           return NULL;
+    }
+    BView* view = new BView(rect, "Cairo bitmap view", B_FOLLOW_ALL_SIDES, 0);
+    bmp->AddChild(view);
+    return _cairo_beos_surface_create_internal(view, bmp, true);
+}
+
+static cairo_status_t
+_cairo_beos_surface_finish (void *abstract_surface)
+{
+    cairo_beos_surface_t *surface = reinterpret_cast<cairo_beos_surface_t*>(
+                                                       abstract_surface);
+    if (surface->owns_bitmap_view) {
+       if (surface->bitmap)
+           surface->bitmap->RemoveChild(surface->view);
+
+       delete surface->view;
+       delete surface->bitmap;
+
+       surface->view = NULL;
+       surface->bitmap = NULL;
+    }
+
+    cairo_region_destroy (surface->clip_region);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_beos_surface_acquire_source_image (void                   *abstract_surface,
+                                         cairo_image_surface_t **image_out,
+                                         void                  **image_extra)
+{
+    cairo_beos_surface_t *surface = reinterpret_cast<cairo_beos_surface_t*>(
+                                                       abstract_surface);
+    AutoLockView locker(surface->view);
+    if (!locker)
+       return CAIRO_STATUS_NO_MEMORY; /// XXX not exactly right, but what can we do?
+
+
+    surface->view->Sync();
+
+    if (surface->bitmap) {
+       *image_out = _cairo_beos_bitmap_to_surface (surface->bitmap);
+       if (unlikely ((*image_out)->base.status))
+           return (*image_out)->base.status;
+
+       *image_extra = NULL;
+       return CAIRO_STATUS_SUCCESS;
+    }
+
+    BBitmap* bmp;
+    if (_cairo_beos_view_to_bitmap(surface->view, &bmp) != OK)
+       return CAIRO_STATUS_NO_MEMORY; /// XXX incorrect if the error was NOT_VISIBLE
+
+    *image_out = _cairo_beos_bitmap_to_surface (bmp);
+    if (unlikely ((*image_out)->base.status)) {
+       delete bmp;
+       return (*image_out)->base.status;
+    }
+    *image_extra = bmp;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_beos_surface_release_source_image (void                  *abstract_surface,
+                                         cairo_image_surface_t *image,
+                                         void                  *image_extra)
+{
+    cairo_surface_destroy (&image->base);
+
+    if (image_extra != NULL) {
+       BBitmap* bmp = static_cast<BBitmap*>(image_extra);
+       delete bmp;
+    }
+}
+
+static cairo_status_t
+_cairo_beos_surface_acquire_dest_image (void                    *abstract_surface,
+                                        cairo_rectangle_int_t   *interest_rect,
+                                        cairo_image_surface_t  **image_out,
+                                        cairo_rectangle_int_t   *image_rect,
+                                        void                   **image_extra)
+{
+    cairo_beos_surface_t *surface = reinterpret_cast<cairo_beos_surface_t*>(
+                                                       abstract_surface);
+
+    AutoLockView locker(surface->view);
+    if (!locker) {
+       *image_out = NULL;
+       *image_extra = NULL;
+       return (cairo_status_t) CAIRO_INT_STATUS_NOTHING_TO_DO;
+    }
+
+    if (surface->bitmap) {
+       surface->view->Sync();
+       *image_out = _cairo_beos_bitmap_to_surface(surface->bitmap);
+       if (unlikely ((*image_out)->base.status))
+           return (*image_out)->base.status;
+
+       image_rect->x = 0;
+       image_rect->y = 0;
+       image_rect->width = (*image_out)->width;
+       image_rect->height = (*image_out)->height;
+
+       *image_extra = NULL;
+       return CAIRO_STATUS_SUCCESS;
+    }
+
+    BRect b_interest_rect (_cairo_rectangle_to_brect (interest_rect));
+
+    BRect rect;
+    BBitmap* bitmap;
+    ViewCopyStatus status = _cairo_beos_view_to_bitmap(surface->view, &bitmap,
+                                                      &rect, &b_interest_rect);
+    if (status == NOT_VISIBLE) {
+       *image_out = NULL;
+       *image_extra = NULL;
+       return CAIRO_STATUS_SUCCESS;
+    }
+    if (status == ERROR)
+       return CAIRO_STATUS_NO_MEMORY;
+
+    *image_rect = _brect_to_cairo_rectangle(rect);
+    *image_out = _cairo_beos_bitmap_to_surface(bitmap);
+    delete bitmap;
+    if (unlikely ((*image_out)->base.status))
+       return (*image_out)->base.status;
+
+    *image_extra = NULL;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+
+static void
+_cairo_beos_surface_release_dest_image (void                   *abstract_surface,
+                                        cairo_rectangle_int_t  *intersect_rect,
+                                        cairo_image_surface_t  *image,
+                                        cairo_rectangle_int_t  *image_rect,
+                                        void                   *image_extra)
+{
+    cairo_beos_surface_t *surface = reinterpret_cast<cairo_beos_surface_t*>(
+                                                       abstract_surface);
+
+    AutoLockView locker(surface->view);
+    if (!locker)
+       return;
+
+    BBitmap* bitmap_to_draw = _cairo_image_surface_to_bitmap(image);
+    surface->view->PushState();
+
+       surface->view->SetDrawingMode(B_OP_COPY);
+
+       surface->view->DrawBitmap (bitmap_to_draw,
+                                  _cairo_rectangle_to_brect (image_rect));
+
+    surface->view->PopState();
+
+    delete bitmap_to_draw;
+    cairo_surface_destroy(&image->base);
+}
+
+static cairo_int_status_t
+_cairo_beos_surface_composite (cairo_operator_t                op,
+                              cairo_pattern_t         *src,
+                              cairo_pattern_t         *mask,
+                              void                    *dst,
+                              int                      src_x,
+                              int                      src_y,
+                              int                      mask_x,
+                              int                      mask_y,
+                              int                      dst_x,
+                              int                      dst_y,
+                              unsigned int             width,
+                              unsigned int             height,
+                              cairo_region_t           *clip_region)
+{
+    cairo_beos_surface_t *surface = reinterpret_cast<cairo_beos_surface_t*>(
+                                                       dst);
+    cairo_int_status_t status;
+    AutoLockView locker(surface->view);
+    if (!locker)
+       return CAIRO_INT_STATUS_SUCCESS;
+
+    drawing_mode mode;
+    if (!_cairo_op_to_be_op(op, &mode))
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    // XXX Masks are not yet supported
+    if (mask)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    // XXX should eventually support the others
+    if (src->type != CAIRO_PATTERN_TYPE_SURFACE ||
+       src->extend != CAIRO_EXTEND_NONE)
+    {
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+    }
+
+    // Can we maybe support other matrices as well? (scale? if the filter is right)
+    int itx, ity;
+    if (!_cairo_matrix_is_integer_translation(&src->matrix, &itx, &ity))
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    status = _cairo_beos_surface_set_clip_region (surface, clip_region);
+    if (unlikely (status))
+       return status;
+
+    BRect srcRect(src_x + itx,
+                 src_y + ity,
+                 src_x + itx + width - 1,
+                 src_y + ity + height - 1);
+    BRect dstRect(dst_x, dst_y, dst_x + width - 1, dst_y + height - 1);
+
+    cairo_surface_t* src_surface = reinterpret_cast<cairo_surface_pattern_t*>(src)->
+                                       surface;
+
+    // Get a bitmap
+    BBitmap* bmp = NULL;
+    bool free_bmp = false;
+    if (_cairo_surface_is_image(src_surface)) {
+       cairo_image_surface_t* img_surface =
+           reinterpret_cast<cairo_image_surface_t*>(src_surface);
+
+       bmp = _cairo_image_surface_to_bitmap(img_surface);
+       free_bmp = true;
+    } else if (src_surface->backend == surface->base.backend) {
+       cairo_beos_surface_t *beos_surface =
+           reinterpret_cast<cairo_beos_surface_t*>(src_surface);
+       if (beos_surface->bitmap) {
+           AutoLockView locker(beos_surface->view);
+           if (locker)
+               beos_surface->view->Sync();
+           bmp = beos_surface->bitmap;
+       } else {
+           _cairo_beos_view_to_bitmap(surface->view, &bmp);
+           free_bmp = true;
+       }
+    }
+
+    if (!bmp)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    // So, BeOS seems to screw up painting an opaque bitmap onto a
+    // translucent one (it makes them partly transparent). Just return
+    // unsupported.
+    if (bmp->ColorSpace() == B_RGB32 && surface->bitmap &&
+       surface->bitmap->ColorSpace() == B_RGBA32 &&
+       (mode == B_OP_COPY || mode == B_OP_ALPHA))
+    {
+       if (free_bmp)
+           delete bmp;
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+    }
+
+    // Draw it on screen.
+    surface->view->PushState();
+
+       // If our image rect is only a subrect of the desired size, and we
+       // aren't using B_OP_ALPHA, then we need to fill the rect first.
+       if (mode == B_OP_COPY && !bmp->Bounds().Contains(srcRect)) {
+           rgb_color black = { 0, 0, 0, 0 };
+
+           surface->view->SetDrawingMode(mode);
+           surface->view->SetHighColor(black);
+           surface->view->FillRect(dstRect);
+       }
+
+       if (mode == B_OP_ALPHA && bmp->ColorSpace() == B_RGB32) {
+           mode = B_OP_COPY;
+       }
+       surface->view->SetDrawingMode(mode);
+
+       if (surface->bitmap && surface->bitmap->ColorSpace() == B_RGBA32)
+           surface->view->SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_COMPOSITE);
+       else
+           surface->view->SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY);
+
+       surface->view->DrawBitmap(bmp, srcRect, dstRect);
+
+    surface->view->PopState();
+
+    if (free_bmp)
+       delete bmp;
+
+    return CAIRO_INT_STATUS_SUCCESS;
+}
+
+
+static cairo_int_status_t
+_cairo_beos_surface_fill_rectangles (void                      *abstract_surface,
+                                    cairo_operator_t            op,
+                                    const cairo_color_t        *color,
+                                    cairo_rectangle_int_t      *rects,
+                                    int                         num_rects,
+                                    cairo_region_t             *clip_region)
+{
+    cairo_beos_surface_t *surface = reinterpret_cast<cairo_beos_surface_t*>(
+                                                       abstract_surface);
+    cairo_int_status_t status;
+
+    if (num_rects <= 0)
+       return CAIRO_INT_STATUS_SUCCESS;
+
+    AutoLockView locker(surface->view);
+    if (!locker)
+       return CAIRO_INT_STATUS_SUCCESS;
+
+    drawing_mode mode;
+    if (!_cairo_op_to_be_op(op, &mode))
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    status = _cairo_beos_surface_set_clip_region (surface, clip_region);
+    if (unlikely (status))
+       return status;
+
+    rgb_color be_color = _cairo_color_to_be_color(color);
+
+    if (mode == B_OP_ALPHA && be_color.alpha == 0xFF)
+       mode = B_OP_COPY;
+
+    // For CAIRO_OPERATOR_SOURCE, cairo expects us to use the premultiplied
+    // color info. This is only relevant when drawing into an rgb24 buffer
+    // (as for others, we can convert when asked for the image)
+    if (mode == B_OP_COPY && be_color.alpha != 0xFF &&
+       (!surface->bitmap || surface->bitmap->ColorSpace() != B_RGBA32))
+    {
+       be_color.red   = color->red_short  >> 8;
+       be_color.green = color->green_short >> 8;
+       be_color.blue  = color->blue_short  >> 8;
+    }
+
+    surface->view->PushState();
+
+       surface->view->SetDrawingMode(mode);
+       surface->view->SetHighColor(be_color);
+       if (surface->bitmap && surface->bitmap->ColorSpace() == B_RGBA32)
+           surface->view->SetBlendingMode(B_CONSTANT_ALPHA, B_ALPHA_COMPOSITE);
+       else
+           surface->view->SetBlendingMode(B_CONSTANT_ALPHA, B_ALPHA_OVERLAY);
+
+       for (int i = 0; i < num_rects; ++i)
+           surface->view->FillRect (_cairo_rectangle_to_brect (&rects[i]));
+
+    surface->view->PopState();
+
+    return CAIRO_INT_STATUS_SUCCESS;
+}
+
+static cairo_bool_t
+_cairo_beos_surface_get_extents (void                          *abstract_surface,
+                                cairo_rectangle_int_t  *rectangle)
+{
+    cairo_beos_surface_t *surface = reinterpret_cast<cairo_beos_surface_t*>(
+                                                       abstract_surface);
+    AutoLockView locker(surface->view);
+    if (!locker)
+       return FALSE;
+
+    *rectangle = _brect_to_cairo_rectangle (surface->view->Bounds());
+    return TRUE;
+}
+
+static const struct _cairo_surface_backend cairo_beos_surface_backend = {
+    CAIRO_SURFACE_TYPE_BEOS,
+    _cairo_beos_surface_create_similar,
+    _cairo_beos_surface_finish,
+    _cairo_beos_surface_acquire_source_image,
+    _cairo_beos_surface_release_source_image,
+    _cairo_beos_surface_acquire_dest_image,
+    _cairo_beos_surface_release_dest_image,
+    NULL, /* clone_similar */
+    _cairo_beos_surface_composite, /* composite */
+    _cairo_beos_surface_fill_rectangles,
+    NULL, /* composite_trapezoids */
+    NULL, /* create_span_renderer */
+    NULL, /* check_span_renderer */
+    NULL, /* copy_page */
+    NULL, /* show_page */
+    _cairo_beos_surface_get_extents,
+    NULL,  /* old_show_glyphs */
+    NULL, /* get_font_options */
+    NULL, /* flush */
+    NULL, /* mark_dirty_rectangle */
+    NULL, /* scaled_font_fini */
+    NULL, /* scaled_glyph_fini */
+
+    NULL, /* paint */
+    NULL, /* mask */
+    NULL, /* stroke */
+    NULL, /* fill */
+    NULL  /* show_glyphs */
+};
+
+static cairo_surface_t *
+_cairo_beos_surface_create_internal (BView*   view,
+                                    BBitmap* bmp,
+                                    bool     owns_bitmap_view)
+{
+    // Must use malloc, because cairo code will use free() on the surface
+    cairo_beos_surface_t *surface = static_cast<cairo_beos_surface_t*>(
+                                       malloc(sizeof(cairo_beos_surface_t)));
+    if (surface == NULL) {
+       _cairo_error (CAIRO_STATUS_NO_MEMORY);
+        return const_cast<cairo_surface_t*>(&_cairo_surface_nil);
+    }
+
+    cairo_content_t content = CAIRO_CONTENT_COLOR;
+    if (bmp && (bmp->ColorSpace() == B_RGBA32 || bmp->ColorSpace() == B_RGBA15))
+       content = CAIRO_CONTENT_COLOR_ALPHA;
+    _cairo_surface_init (&surface->base,
+                        &cairo_beos_surface_backend,
+                        NULL, /* device */
+                        content);
+
+    surface->view = view;
+    surface->bitmap = bmp;
+    surface->owns_bitmap_view = owns_bitmap_view;
+
+    surface->clip_region = NULL;
+
+    return &surface->base;
+}
+
+/**
+ * cairo_beos_surface_create:
+ * @view: The view to draw on
+ *
+ * Creates a Cairo surface that draws onto a BeOS BView.
+ * The caller must ensure that the view does not get deleted before the surface.
+ * If the view is attached to a bitmap rather than an on-screen window, use
+ * cairo_beos_surface_create_for_bitmap() instead of this function.
+ **/
+cairo_surface_t *
+cairo_beos_surface_create (BView* view)
+{
+    return cairo_beos_surface_create_for_bitmap(view, NULL);
+}
+
+/**
+ * cairo_beos_surface_create_for_bitmap:
+ * @view: The view to draw on
+ * @bmp: The bitmap to which the view is attached
+ *
+ * Creates a Cairo surface that draws onto a BeOS BView which is attached to a
+ * BBitmap.
+ * The caller must ensure that the view and the bitmap do not get deleted
+ * before the surface.
+ *
+ * For views that draw to a bitmap (as opposed to a screen), use this function
+ * rather than cairo_beos_surface_create(). Not using this function WILL lead to
+ * incorrect behaviour.
+ *
+ * For now, only views that draw to the entire area of bmp are supported.
+ * The view must already be attached to the bitmap.
+ **/
+cairo_surface_t *
+cairo_beos_surface_create_for_bitmap (BView*   view,
+                                     BBitmap* bmp)
+{
+    return _cairo_beos_surface_create_internal(view, bmp);
+}
diff --git a/src/cairo-beos.h b/src/cairo-beos.h
new file mode 100644 (file)
index 0000000..fdb89a6
--- /dev/null
@@ -0,0 +1,60 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2005 Christian Biesinger <cbiesinger@web.de>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Christian Biesinger
+ * <cbiesinger@web.de>
+ *
+ * Contributor(s):
+ */
+
+#ifndef CAIRO_BEOS_H
+#define CAIRO_BEOS_H
+
+#include "cairo.h"
+
+#if CAIRO_HAS_BEOS_SURFACE
+
+#include <View.h>
+
+CAIRO_BEGIN_DECLS
+
+cairo_public cairo_surface_t *
+cairo_beos_surface_create (BView* view);
+
+cairo_public cairo_surface_t *
+cairo_beos_surface_create_for_bitmap (BView*   view,
+                                     BBitmap* bmp);
+
+CAIRO_END_DECLS
+
+#else  /* CAIRO_HAS_BEOS_SURFACE */
+# error Cairo was not compiled with support for the beos backend
+#endif /* CAIRO_HAS_BEOS_SURFACE */
+
+#endif /* CAIRO_BEOS_H */
diff --git a/src/cairo-botor-scan-converter.c b/src/cairo-botor-scan-converter.c
new file mode 100644 (file)
index 0000000..bcebcfb
--- /dev/null
@@ -0,0 +1,2200 @@
+/*
+ * Copyright © 2004 Carl Worth
+ * Copyright © 2006 Red Hat, Inc.
+ * Copyright © 2007 David Turner
+ * Copyright © 2008 M Joonas Pihlaja
+ * Copyright © 2008 Chris Wilson
+ * Copyright © 2009 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Carl Worth
+ *
+ * Contributor(s):
+ *     Carl D. Worth <cworth@cworth.org>
+ *      M Joonas Pihlaja <jpihlaja@cc.helsinki.fi>
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+/* Provide definitions for standalone compilation */
+#include "cairoint.h"
+
+#include "cairo-error-private.h"
+#include "cairo-list-private.h"
+#include "cairo-freelist-private.h"
+#include "cairo-combsort-private.h"
+
+#include <setjmp.h>
+
+#define STEP_X CAIRO_FIXED_ONE
+#define STEP_Y CAIRO_FIXED_ONE
+#define UNROLL3(x) x x x
+
+#define STEP_XY (2*STEP_X*STEP_Y) /* Unit area in the step. */
+#define AREA_TO_ALPHA(c)  (((c)*255 + STEP_XY/2) / STEP_XY)
+
+typedef struct _cairo_bo_intersect_ordinate {
+    int32_t ordinate;
+    enum { EXACT, INEXACT } exactness;
+} cairo_bo_intersect_ordinate_t;
+
+typedef struct _cairo_bo_intersect_point {
+    cairo_bo_intersect_ordinate_t x;
+    cairo_bo_intersect_ordinate_t y;
+} cairo_bo_intersect_point_t;
+
+struct quorem {
+    cairo_fixed_t quo;
+    cairo_fixed_t rem;
+};
+
+struct run {
+    struct run *next;
+    int sign;
+    cairo_fixed_t y;
+};
+
+typedef struct edge {
+    cairo_list_t link;
+
+    cairo_edge_t edge;
+
+    /* Current x coordinate and advancement.
+     * Initialised to the x coordinate of the top of the
+     * edge. The quotient is in cairo_fixed_t units and the
+     * remainder is mod dy in cairo_fixed_t units.
+     */
+    cairo_fixed_t dy;
+    struct quorem x;
+    struct quorem dxdy;
+    struct quorem dxdy_full;
+
+    cairo_bool_t vertical;
+    unsigned int flags;
+
+    int current_sign;
+    struct run *runs;
+} edge_t;
+
+enum {
+    START = 0x1,
+    STOP = 0x2,
+};
+
+/* the parent is always given by index/2 */
+#define PQ_PARENT_INDEX(i) ((i) >> 1)
+#define PQ_FIRST_ENTRY 1
+
+/* left and right children are index * 2 and (index * 2) +1 respectively */
+#define PQ_LEFT_CHILD_INDEX(i) ((i) << 1)
+
+typedef enum {
+    EVENT_TYPE_STOP,
+    EVENT_TYPE_INTERSECTION,
+    EVENT_TYPE_START
+} event_type_t;
+
+typedef struct _event {
+    cairo_fixed_t y;
+    event_type_t type;
+} event_t;
+
+typedef struct _start_event {
+    cairo_fixed_t y;
+    event_type_t type;
+    edge_t *edge;
+} start_event_t;
+
+typedef struct _queue_event {
+    cairo_fixed_t y;
+    event_type_t type;
+    edge_t *e1;
+    edge_t *e2;
+} queue_event_t;
+
+typedef struct _pqueue {
+    int size, max_size;
+
+    event_t **elements;
+    event_t *elements_embedded[1024];
+} pqueue_t;
+
+struct cell {
+    struct cell        *prev;
+    struct cell        *next;
+    int                 x;
+    int                 uncovered_area;
+    int                 covered_height;
+};
+
+typedef struct _sweep_line {
+    cairo_list_t active;
+    cairo_list_t stopped;
+    cairo_list_t *insert_cursor;
+    cairo_bool_t is_vertical;
+
+    cairo_fixed_t current_row;
+    cairo_fixed_t current_subrow;
+
+    struct coverage {
+       struct cell head;
+       struct cell tail;
+
+       struct cell *cursor;
+       int count;
+
+       cairo_freepool_t pool;
+    } coverage;
+
+    struct event_queue {
+       pqueue_t pq;
+       event_t **start_events;
+
+       cairo_freepool_t pool;
+    } queue;
+
+    cairo_freepool_t runs;
+
+    jmp_buf unwind;
+} sweep_line_t;
+
+cairo_always_inline static struct quorem
+floored_divrem (int a, int b)
+{
+    struct quorem qr;
+    qr.quo = a/b;
+    qr.rem = a%b;
+    if ((a^b)<0 && qr.rem) {
+       qr.quo--;
+       qr.rem += b;
+    }
+    return qr;
+}
+
+static struct quorem
+floored_muldivrem(int x, int a, int b)
+{
+    struct quorem qr;
+    long long xa = (long long)x*a;
+    qr.quo = xa/b;
+    qr.rem = xa%b;
+    if ((xa>=0) != (b>=0) && qr.rem) {
+       qr.quo--;
+       qr.rem += b;
+    }
+    return qr;
+}
+
+static cairo_fixed_t
+line_compute_intersection_x_for_y (const cairo_line_t *line,
+                                  cairo_fixed_t y)
+{
+    cairo_fixed_t x, dy;
+
+    if (y == line->p1.y)
+       return line->p1.x;
+    if (y == line->p2.y)
+       return line->p2.x;
+
+    x = line->p1.x;
+    dy = line->p2.y - line->p1.y;
+    if (dy != 0) {
+       x += _cairo_fixed_mul_div_floor (y - line->p1.y,
+                                        line->p2.x - line->p1.x,
+                                        dy);
+    }
+
+    return x;
+}
+
+/*
+ * We need to compare the x-coordinates of a pair of lines for a particular y,
+ * without loss of precision.
+ *
+ * The x-coordinate along an edge for a given y is:
+ *   X = A_x + (Y - A_y) * A_dx / A_dy
+ *
+ * So the inequality we wish to test is:
+ *   A_x + (Y - A_y) * A_dx / A_dy ??B_x + (Y - B_y) * B_dx / B_dy,
+ * where ??is our inequality operator.
+ *
+ * By construction, we know that A_dy and B_dy (and (Y - A_y), (Y - B_y)) are
+ * all positive, so we can rearrange it thus without causing a sign change:
+ *   A_dy * B_dy * (A_x - B_x) ??(Y - B_y) * B_dx * A_dy
+ *                                 - (Y - A_y) * A_dx * B_dy
+ *
+ * Given the assumption that all the deltas fit within 32 bits, we can compute
+ * this comparison directly using 128 bit arithmetic. For certain, but common,
+ * input we can reduce this down to a single 32 bit compare by inspecting the
+ * deltas.
+ *
+ * (And put the burden of the work on developing fast 128 bit ops, which are
+ * required throughout the tessellator.)
+ *
+ * See the similar discussion for _slope_compare().
+ */
+static int
+edges_compare_x_for_y_general (const cairo_edge_t *a,
+                              const cairo_edge_t *b,
+                              int32_t y)
+{
+    /* XXX: We're assuming here that dx and dy will still fit in 32
+     * bits. That's not true in general as there could be overflow. We
+     * should prevent that before the tessellation algorithm
+     * begins.
+     */
+    int32_t dx;
+    int32_t adx, ady;
+    int32_t bdx, bdy;
+    enum {
+       HAVE_NONE    = 0x0,
+       HAVE_DX      = 0x1,
+       HAVE_ADX     = 0x2,
+       HAVE_DX_ADX  = HAVE_DX | HAVE_ADX,
+       HAVE_BDX     = 0x4,
+       HAVE_DX_BDX  = HAVE_DX | HAVE_BDX,
+       HAVE_ADX_BDX = HAVE_ADX | HAVE_BDX,
+       HAVE_ALL     = HAVE_DX | HAVE_ADX | HAVE_BDX
+    } have_dx_adx_bdx = HAVE_ALL;
+
+    /* don't bother solving for abscissa if the edges' bounding boxes
+     * can be used to order them. */
+    {
+           int32_t amin, amax;
+           int32_t bmin, bmax;
+           if (a->line.p1.x < a->line.p2.x) {
+                   amin = a->line.p1.x;
+                   amax = a->line.p2.x;
+           } else {
+                   amin = a->line.p2.x;
+                   amax = a->line.p1.x;
+           }
+           if (b->line.p1.x < b->line.p2.x) {
+                   bmin = b->line.p1.x;
+                   bmax = b->line.p2.x;
+           } else {
+                   bmin = b->line.p2.x;
+                   bmax = b->line.p1.x;
+           }
+           if (amax < bmin) return -1;
+           if (amin > bmax) return +1;
+    }
+
+    ady = a->line.p2.y - a->line.p1.y;
+    adx = a->line.p2.x - a->line.p1.x;
+    if (adx == 0)
+       have_dx_adx_bdx &= ~HAVE_ADX;
+
+    bdy = b->line.p2.y - b->line.p1.y;
+    bdx = b->line.p2.x - b->line.p1.x;
+    if (bdx == 0)
+       have_dx_adx_bdx &= ~HAVE_BDX;
+
+    dx = a->line.p1.x - b->line.p1.x;
+    if (dx == 0)
+       have_dx_adx_bdx &= ~HAVE_DX;
+
+#define L _cairo_int64x32_128_mul (_cairo_int32x32_64_mul (ady, bdy), dx)
+#define A _cairo_int64x32_128_mul (_cairo_int32x32_64_mul (adx, bdy), y - a->line.p1.y)
+#define B _cairo_int64x32_128_mul (_cairo_int32x32_64_mul (bdx, ady), y - b->line.p1.y)
+    switch (have_dx_adx_bdx) {
+    default:
+    case HAVE_NONE:
+       return 0;
+    case HAVE_DX:
+       /* A_dy * B_dy * (A_x - B_x) ??0 */
+       return dx; /* ady * bdy is positive definite */
+    case HAVE_ADX:
+       /* 0 ?? - (Y - A_y) * A_dx * B_dy */
+       return adx; /* bdy * (y - a->top.y) is positive definite */
+    case HAVE_BDX:
+       /* 0 ??(Y - B_y) * B_dx * A_dy */
+       return -bdx; /* ady * (y - b->top.y) is positive definite */
+    case HAVE_ADX_BDX:
+       /*  0 ??(Y - B_y) * B_dx * A_dy - (Y - A_y) * A_dx * B_dy */
+       if ((adx ^ bdx) < 0) {
+           return adx;
+       } else if (a->line.p1.y == b->line.p1.y) { /* common origin */
+           cairo_int64_t adx_bdy, bdx_ady;
+
+           /* ??A_dx * B_dy ??B_dx * A_dy */
+
+           adx_bdy = _cairo_int32x32_64_mul (adx, bdy);
+           bdx_ady = _cairo_int32x32_64_mul (bdx, ady);
+
+           return _cairo_int64_cmp (adx_bdy, bdx_ady);
+       } else
+           return _cairo_int128_cmp (A, B);
+    case HAVE_DX_ADX:
+       /* A_dy * (A_x - B_x) ??- (Y - A_y) * A_dx */
+       if ((-adx ^ dx) < 0) {
+           return dx;
+       } else {
+           cairo_int64_t ady_dx, dy_adx;
+
+           ady_dx = _cairo_int32x32_64_mul (ady, dx);
+           dy_adx = _cairo_int32x32_64_mul (a->line.p1.y - y, adx);
+
+           return _cairo_int64_cmp (ady_dx, dy_adx);
+       }
+    case HAVE_DX_BDX:
+       /* B_dy * (A_x - B_x) ??(Y - B_y) * B_dx */
+       if ((bdx ^ dx) < 0) {
+           return dx;
+       } else {
+           cairo_int64_t bdy_dx, dy_bdx;
+
+           bdy_dx = _cairo_int32x32_64_mul (bdy, dx);
+           dy_bdx = _cairo_int32x32_64_mul (y - b->line.p1.y, bdx);
+
+           return _cairo_int64_cmp (bdy_dx, dy_bdx);
+       }
+    case HAVE_ALL:
+       /* XXX try comparing (a->line.p2.x - b->line.p2.x) et al */
+       return _cairo_int128_cmp (L, _cairo_int128_sub (B, A));
+    }
+#undef B
+#undef A
+#undef L
+}
+
+/*
+ * We need to compare the x-coordinate of a line for a particular y wrt to a
+ * given x, without loss of precision.
+ *
+ * The x-coordinate along an edge for a given y is:
+ *   X = A_x + (Y - A_y) * A_dx / A_dy
+ *
+ * So the inequality we wish to test is:
+ *   A_x + (Y - A_y) * A_dx / A_dy ??X
+ * where ??is our inequality operator.
+ *
+ * By construction, we know that A_dy (and (Y - A_y)) are
+ * all positive, so we can rearrange it thus without causing a sign change:
+ *   (Y - A_y) * A_dx ??(X - A_x) * A_dy
+ *
+ * Given the assumption that all the deltas fit within 32 bits, we can compute
+ * this comparison directly using 64 bit arithmetic.
+ *
+ * See the similar discussion for _slope_compare() and
+ * edges_compare_x_for_y_general().
+ */
+static int
+edge_compare_for_y_against_x (const cairo_edge_t *a,
+                             int32_t y,
+                             int32_t x)
+{
+    int32_t adx, ady;
+    int32_t dx, dy;
+    cairo_int64_t L, R;
+
+    if (a->line.p1.x <= a->line.p2.x) {
+       if (x < a->line.p1.x)
+           return 1;
+       if (x > a->line.p2.x)
+           return -1;
+    } else {
+       if (x < a->line.p2.x)
+           return 1;
+       if (x > a->line.p1.x)
+           return -1;
+    }
+
+    adx = a->line.p2.x - a->line.p1.x;
+    dx = x - a->line.p1.x;
+
+    if (adx == 0)
+       return -dx;
+    if (dx == 0 || (adx ^ dx) < 0)
+       return adx;
+
+    dy = y - a->line.p1.y;
+    ady = a->line.p2.y - a->line.p1.y;
+
+    L = _cairo_int32x32_64_mul (dy, adx);
+    R = _cairo_int32x32_64_mul (dx, ady);
+
+    return _cairo_int64_cmp (L, R);
+}
+
+static int
+edges_compare_x_for_y (const cairo_edge_t *a,
+                      const cairo_edge_t *b,
+                      int32_t y)
+{
+    /* If the sweep-line is currently on an end-point of a line,
+     * then we know its precise x value (and considering that we often need to
+     * compare events at end-points, this happens frequently enough to warrant
+     * special casing).
+     */
+    enum {
+       HAVE_NEITHER = 0x0,
+       HAVE_AX      = 0x1,
+       HAVE_BX      = 0x2,
+       HAVE_BOTH    = HAVE_AX | HAVE_BX
+    } have_ax_bx = HAVE_BOTH;
+    int32_t ax, bx;
+
+    /* XXX given we have x and dx? */
+
+    if (y == a->line.p1.y)
+       ax = a->line.p1.x;
+    else if (y == a->line.p2.y)
+       ax = a->line.p2.x;
+    else
+       have_ax_bx &= ~HAVE_AX;
+
+    if (y == b->line.p1.y)
+       bx = b->line.p1.x;
+    else if (y == b->line.p2.y)
+       bx = b->line.p2.x;
+    else
+       have_ax_bx &= ~HAVE_BX;
+
+    switch (have_ax_bx) {
+    default:
+    case HAVE_NEITHER:
+       return edges_compare_x_for_y_general (a, b, y);
+    case HAVE_AX:
+       return -edge_compare_for_y_against_x (b, y, ax);
+    case HAVE_BX:
+       return edge_compare_for_y_against_x (a, y, bx);
+    case HAVE_BOTH:
+       return ax - bx;
+    }
+}
+
+static inline int
+slope_compare (const edge_t *a,
+              const edge_t *b)
+{
+    cairo_int64_t L, R;
+    int cmp;
+
+    cmp = a->dxdy.quo - b->dxdy.quo;
+    if (cmp)
+       return cmp;
+
+    if (a->dxdy.rem == 0)
+       return -b->dxdy.rem;
+    if (b->dxdy.rem == 0)
+       return a->dxdy.rem;
+
+    L = _cairo_int32x32_64_mul (b->dy, a->dxdy.rem);
+    R = _cairo_int32x32_64_mul (a->dy, b->dxdy.rem);
+    return _cairo_int64_cmp (L, R);
+}
+
+static inline int
+line_equal (const cairo_line_t *a, const cairo_line_t *b)
+{
+    return a->p1.x == b->p1.x && a->p1.y == b->p1.y &&
+           a->p2.x == b->p2.x && a->p2.y == b->p2.y;
+}
+
+static inline int
+sweep_line_compare_edges (const edge_t *a,
+                         const edge_t  *b,
+                         cairo_fixed_t y)
+{
+    int cmp;
+
+    if (line_equal (&a->edge.line, &b->edge.line))
+       return 0;
+
+    cmp = edges_compare_x_for_y (&a->edge, &b->edge, y);
+    if (cmp)
+       return cmp;
+
+    return slope_compare (a, b);
+}
+
+static inline cairo_int64_t
+det32_64 (int32_t a, int32_t b,
+         int32_t c, int32_t d)
+{
+    /* det = a * d - b * c */
+    return _cairo_int64_sub (_cairo_int32x32_64_mul (a, d),
+                            _cairo_int32x32_64_mul (b, c));
+}
+
+static inline cairo_int128_t
+det64x32_128 (cairo_int64_t a, int32_t       b,
+             cairo_int64_t c, int32_t       d)
+{
+    /* det = a * d - b * c */
+    return _cairo_int128_sub (_cairo_int64x32_128_mul (a, d),
+                             _cairo_int64x32_128_mul (c, b));
+}
+
+/* Compute the intersection of two lines as defined by two edges. The
+ * result is provided as a coordinate pair of 128-bit integers.
+ *
+ * Returns %CAIRO_BO_STATUS_INTERSECTION if there is an intersection or
+ * %CAIRO_BO_STATUS_PARALLEL if the two lines are exactly parallel.
+ */
+static cairo_bool_t
+intersect_lines (const edge_t *a, const edge_t *b,
+                cairo_bo_intersect_point_t     *intersection)
+{
+    cairo_int64_t a_det, b_det;
+
+    /* XXX: We're assuming here that dx and dy will still fit in 32
+     * bits. That's not true in general as there could be overflow. We
+     * should prevent that before the tessellation algorithm begins.
+     * What we're doing to mitigate this is to perform clamping in
+     * cairo_bo_tessellate_polygon().
+     */
+    int32_t dx1 = a->edge.line.p1.x - a->edge.line.p2.x;
+    int32_t dy1 = a->edge.line.p1.y - a->edge.line.p2.y;
+
+    int32_t dx2 = b->edge.line.p1.x - b->edge.line.p2.x;
+    int32_t dy2 = b->edge.line.p1.y - b->edge.line.p2.y;
+
+    cairo_int64_t den_det;
+    cairo_int64_t R;
+    cairo_quorem64_t qr;
+
+    den_det = det32_64 (dx1, dy1, dx2, dy2);
+
+     /* Q: Can we determine that the lines do not intersect (within range)
+      * much more cheaply than computing the intersection point i.e. by
+      * avoiding the division?
+      *
+      *   X = ax + t * adx = bx + s * bdx;
+      *   Y = ay + t * ady = by + s * bdy;
+      *   ??t * (ady*bdx - bdy*adx) = bdx * (by - ay) + bdy * (ax - bx)
+      *   => t * L = R
+      *
+      * Therefore we can reject any intersection (under the criteria for
+      * valid intersection events) if:
+      *   L^R < 0 => t < 0, or
+      *   L<R => t > 1
+      *
+      * (where top/bottom must at least extend to the line endpoints).
+      *
+      * A similar substitution can be performed for s, yielding:
+      *   s * (ady*bdx - bdy*adx) = ady * (ax - bx) - adx * (ay - by)
+      */
+    R = det32_64 (dx2, dy2,
+                 b->edge.line.p1.x - a->edge.line.p1.x,
+                 b->edge.line.p1.y - a->edge.line.p1.y);
+    if (_cairo_int64_negative (den_det)) {
+       if (_cairo_int64_ge (den_det, R))
+           return FALSE;
+    } else {
+       if (_cairo_int64_le (den_det, R))
+           return FALSE;
+    }
+
+    R = det32_64 (dy1, dx1,
+                 a->edge.line.p1.y - b->edge.line.p1.y,
+                 a->edge.line.p1.x - b->edge.line.p1.x);
+    if (_cairo_int64_negative (den_det)) {
+       if (_cairo_int64_ge (den_det, R))
+           return FALSE;
+    } else {
+       if (_cairo_int64_le (den_det, R))
+           return FALSE;
+    }
+
+    /* We now know that the two lines should intersect within range. */
+
+    a_det = det32_64 (a->edge.line.p1.x, a->edge.line.p1.y,
+                     a->edge.line.p2.x, a->edge.line.p2.y);
+    b_det = det32_64 (b->edge.line.p1.x, b->edge.line.p1.y,
+                     b->edge.line.p2.x, b->edge.line.p2.y);
+
+    /* x = det (a_det, dx1, b_det, dx2) / den_det */
+    qr = _cairo_int_96by64_32x64_divrem (det64x32_128 (a_det, dx1,
+                                                      b_det, dx2),
+                                        den_det);
+    if (_cairo_int64_eq (qr.rem, den_det))
+       return FALSE;
+#if 0
+    intersection->x.exactness = _cairo_int64_is_zero (qr.rem) ? EXACT : INEXACT;
+#else
+    intersection->x.exactness = EXACT;
+    if (! _cairo_int64_is_zero (qr.rem)) {
+       if (_cairo_int64_negative (den_det) ^ _cairo_int64_negative (qr.rem))
+           qr.rem = _cairo_int64_negate (qr.rem);
+       qr.rem = _cairo_int64_mul (qr.rem, _cairo_int32_to_int64 (2));
+       if (_cairo_int64_ge (qr.rem, den_det)) {
+           qr.quo = _cairo_int64_add (qr.quo,
+                                      _cairo_int32_to_int64 (_cairo_int64_negative (qr.quo) ? -1 : 1));
+       } else
+           intersection->x.exactness = INEXACT;
+    }
+#endif
+    intersection->x.ordinate = _cairo_int64_to_int32 (qr.quo);
+
+    /* y = det (a_det, dy1, b_det, dy2) / den_det */
+    qr = _cairo_int_96by64_32x64_divrem (det64x32_128 (a_det, dy1,
+                                                      b_det, dy2),
+                                        den_det);
+    if (_cairo_int64_eq (qr.rem, den_det))
+       return FALSE;
+#if 0
+    intersection->y.exactness = _cairo_int64_is_zero (qr.rem) ? EXACT : INEXACT;
+#else
+    intersection->y.exactness = EXACT;
+    if (! _cairo_int64_is_zero (qr.rem)) {
+       /* compute ceiling away from zero */
+       qr.quo = _cairo_int64_add (qr.quo,
+                                  _cairo_int32_to_int64 (_cairo_int64_negative (qr.quo) ? -1 : 1));
+       intersection->y.exactness = INEXACT;
+    }
+#endif
+    intersection->y.ordinate = _cairo_int64_to_int32 (qr.quo);
+
+    return TRUE;
+}
+
+static int
+bo_intersect_ordinate_32_compare (int32_t a, int32_t b, int exactness)
+{
+    int cmp;
+
+    /* First compare the quotient */
+    cmp = a - b;
+    if (cmp)
+       return cmp;
+
+    /* With quotient identical, if remainder is 0 then compare equal */
+    /* Otherwise, the non-zero remainder makes a > b */
+    return -(INEXACT == exactness);
+}
+
+/* Does the given edge contain the given point. The point must already
+ * be known to be contained within the line determined by the edge,
+ * (most likely the point results from an intersection of this edge
+ * with another).
+ *
+ * If we had exact arithmetic, then this function would simply be a
+ * matter of examining whether the y value of the point lies within
+ * the range of y values of the edge. But since intersection points
+ * are not exact due to being rounded to the nearest integer within
+ * the available precision, we must also examine the x value of the
+ * point.
+ *
+ * The definition of "contains" here is that the given intersection
+ * point will be seen by the sweep line after the start event for the
+ * given edge and before the stop event for the edge. See the comments
+ * in the implementation for more details.
+ */
+static cairo_bool_t
+bo_edge_contains_intersect_point (const edge_t                 *edge,
+                                 cairo_bo_intersect_point_t    *point)
+{
+    int cmp_top, cmp_bottom;
+
+    /* XXX: When running the actual algorithm, we don't actually need to
+     * compare against edge->top at all here, since any intersection above
+     * top is eliminated early via a slope comparison. We're leaving these
+     * here for now only for the sake of the quadratic-time intersection
+     * finder which needs them.
+     */
+
+    cmp_top = bo_intersect_ordinate_32_compare (point->y.ordinate,
+                                               edge->edge.top,
+                                               point->y.exactness);
+    if (cmp_top < 0)
+       return FALSE;
+
+    cmp_bottom = bo_intersect_ordinate_32_compare (point->y.ordinate,
+                                                  edge->edge.bottom,
+                                                  point->y.exactness);
+    if (cmp_bottom > 0)
+       return FALSE;
+
+    if (cmp_top > 0 && cmp_bottom < 0)
+       return TRUE;
+
+    /* At this stage, the point lies on the same y value as either
+     * edge->top or edge->bottom, so we have to examine the x value in
+     * order to properly determine containment. */
+
+    /* If the y value of the point is the same as the y value of the
+     * top of the edge, then the x value of the point must be greater
+     * to be considered as inside the edge. Similarly, if the y value
+     * of the point is the same as the y value of the bottom of the
+     * edge, then the x value of the point must be less to be
+     * considered as inside. */
+
+    if (cmp_top == 0) {
+       cairo_fixed_t top_x;
+
+       top_x = line_compute_intersection_x_for_y (&edge->edge.line,
+                                                  edge->edge.top);
+       return bo_intersect_ordinate_32_compare (top_x, point->x.ordinate, point->x.exactness) < 0;
+    } else { /* cmp_bottom == 0 */
+       cairo_fixed_t bot_x;
+
+       bot_x = line_compute_intersection_x_for_y (&edge->edge.line,
+                                                  edge->edge.bottom);
+       return bo_intersect_ordinate_32_compare (point->x.ordinate, bot_x, point->x.exactness) < 0;
+    }
+}
+
+static cairo_bool_t
+edge_intersect (const edge_t           *a,
+               const edge_t            *b,
+               cairo_point_t   *intersection)
+{
+    cairo_bo_intersect_point_t quorem;
+
+    if (! intersect_lines (a, b, &quorem))
+       return FALSE;
+
+    if (a->edge.top != a->edge.line.p1.y || a->edge.bottom != a->edge.line.p2.y) {
+       if (! bo_edge_contains_intersect_point (a, &quorem))
+           return FALSE;
+    }
+
+    if (b->edge.top != b->edge.line.p1.y || b->edge.bottom != b->edge.line.p2.y) {
+       if (! bo_edge_contains_intersect_point (b, &quorem))
+           return FALSE;
+    }
+
+    /* Now that we've correctly compared the intersection point and
+     * determined that it lies within the edge, then we know that we
+     * no longer need any more bits of storage for the intersection
+     * than we do for our edge coordinates. We also no longer need the
+     * remainder from the division. */
+    intersection->x = quorem.x.ordinate;
+    intersection->y = quorem.y.ordinate;
+
+    return TRUE;
+}
+
+static inline int
+event_compare (const event_t *a, const event_t *b)
+{
+    return a->y - b->y;
+}
+
+static void
+pqueue_init (pqueue_t *pq)
+{
+    pq->max_size = ARRAY_LENGTH (pq->elements_embedded);
+    pq->size = 0;
+
+    pq->elements = pq->elements_embedded;
+}
+
+static void
+pqueue_fini (pqueue_t *pq)
+{
+    if (pq->elements != pq->elements_embedded)
+       free (pq->elements);
+}
+
+static cairo_bool_t
+pqueue_grow (pqueue_t *pq)
+{
+    event_t **new_elements;
+    pq->max_size *= 2;
+
+    if (pq->elements == pq->elements_embedded) {
+       new_elements = _cairo_malloc_ab (pq->max_size,
+                                        sizeof (event_t *));
+       if (unlikely (new_elements == NULL))
+           return FALSE;
+
+       memcpy (new_elements, pq->elements_embedded,
+               sizeof (pq->elements_embedded));
+    } else {
+       new_elements = _cairo_realloc_ab (pq->elements,
+                                         pq->max_size,
+                                         sizeof (event_t *));
+       if (unlikely (new_elements == NULL))
+           return FALSE;
+    }
+
+    pq->elements = new_elements;
+    return TRUE;
+}
+
+static inline void
+pqueue_push (sweep_line_t *sweep_line, event_t *event)
+{
+    event_t **elements;
+    int i, parent;
+
+    if (unlikely (sweep_line->queue.pq.size + 1 == sweep_line->queue.pq.max_size)) {
+       if (unlikely (! pqueue_grow (&sweep_line->queue.pq))) {
+           longjmp (sweep_line->unwind,
+                    _cairo_error (CAIRO_STATUS_NO_MEMORY));
+       }
+    }
+
+    elements = sweep_line->queue.pq.elements;
+    for (i = ++sweep_line->queue.pq.size;
+        i != PQ_FIRST_ENTRY &&
+        event_compare (event,
+                       elements[parent = PQ_PARENT_INDEX (i)]) < 0;
+        i = parent)
+    {
+       elements[i] = elements[parent];
+    }
+
+    elements[i] = event;
+}
+
+static inline void
+pqueue_pop (pqueue_t *pq)
+{
+    event_t **elements = pq->elements;
+    event_t *tail;
+    int child, i;
+
+    tail = elements[pq->size--];
+    if (pq->size == 0) {
+       elements[PQ_FIRST_ENTRY] = NULL;
+       return;
+    }
+
+    for (i = PQ_FIRST_ENTRY;
+        (child = PQ_LEFT_CHILD_INDEX (i)) <= pq->size;
+        i = child)
+    {
+       if (child != pq->size &&
+           event_compare (elements[child+1],
+                          elements[child]) < 0)
+       {
+           child++;
+       }
+
+       if (event_compare (elements[child], tail) >= 0)
+           break;
+
+       elements[i] = elements[child];
+    }
+    elements[i] = tail;
+}
+
+static inline void
+event_insert (sweep_line_t     *sweep_line,
+             event_type_t       type,
+             edge_t            *e1,
+             edge_t            *e2,
+             cairo_fixed_t      y)
+{
+    queue_event_t *event;
+
+    event = _cairo_freepool_alloc (&sweep_line->queue.pool);
+    if (unlikely (event == NULL)) {
+       longjmp (sweep_line->unwind,
+                _cairo_error (CAIRO_STATUS_NO_MEMORY));
+    }
+
+    event->y = y;
+    event->type = type;
+    event->e1 = e1;
+    event->e2 = e2;
+
+    pqueue_push (sweep_line, (event_t *) event);
+}
+
+static void
+event_delete (sweep_line_t     *sweep_line,
+             event_t           *event)
+{
+    _cairo_freepool_free (&sweep_line->queue.pool, event);
+}
+
+static inline event_t *
+event_next (sweep_line_t *sweep_line)
+{
+    event_t *event, *cmp;
+
+    event = sweep_line->queue.pq.elements[PQ_FIRST_ENTRY];
+    cmp = *sweep_line->queue.start_events;
+    if (event == NULL ||
+       (cmp != NULL && event_compare (cmp, event) < 0))
+    {
+       event = cmp;
+       sweep_line->queue.start_events++;
+    }
+    else
+    {
+       pqueue_pop (&sweep_line->queue.pq);
+    }
+
+    return event;
+}
+
+CAIRO_COMBSORT_DECLARE (start_event_sort, event_t *, event_compare)
+
+static inline void
+event_insert_stop (sweep_line_t        *sweep_line,
+                  edge_t       *edge)
+{
+    event_insert (sweep_line,
+                 EVENT_TYPE_STOP,
+                 edge, NULL,
+                 edge->edge.bottom);
+}
+
+static inline void
+event_insert_if_intersect_below_current_y (sweep_line_t        *sweep_line,
+                                          edge_t       *left,
+                                          edge_t       *right)
+{
+    cairo_point_t intersection;
+
+    /* start points intersect */
+    if (left->edge.line.p1.x == right->edge.line.p1.x &&
+       left->edge.line.p1.y == right->edge.line.p1.y)
+    {
+       return;
+    }
+
+    /* end points intersect, process DELETE events first */
+    if (left->edge.line.p2.x == right->edge.line.p2.x &&
+       left->edge.line.p2.y == right->edge.line.p2.y)
+    {
+       return;
+    }
+
+    if (slope_compare (left, right) <= 0)
+       return;
+
+    if (! edge_intersect (left, right, &intersection))
+       return;
+
+    event_insert (sweep_line,
+                 EVENT_TYPE_INTERSECTION,
+                 left, right,
+                 intersection.y);
+}
+
+static inline edge_t *
+link_to_edge (cairo_list_t *link)
+{
+    return (edge_t *) link;
+}
+
+static void
+sweep_line_insert (sweep_line_t        *sweep_line,
+                  edge_t       *edge)
+{
+    cairo_list_t *pos;
+    cairo_fixed_t y = sweep_line->current_subrow;
+
+    pos = sweep_line->insert_cursor;
+    if (pos == &sweep_line->active)
+       pos = sweep_line->active.next;
+    if (pos != &sweep_line->active) {
+       int cmp;
+
+       cmp = sweep_line_compare_edges (link_to_edge (pos),
+                                       edge,
+                                       y);
+       if (cmp < 0) {
+           while (pos->next != &sweep_line->active &&
+                  sweep_line_compare_edges (link_to_edge (pos->next),
+                                            edge,
+                                            y) < 0)
+           {
+               pos = pos->next;
+           }
+       } else if (cmp > 0) {
+           do {
+               pos = pos->prev;
+           } while (pos != &sweep_line->active &&
+                    sweep_line_compare_edges (link_to_edge (pos),
+                                              edge,
+                                              y) > 0);
+       }
+    }
+    cairo_list_add (&edge->link, pos);
+    sweep_line->insert_cursor = &edge->link;
+}
+
+inline static void
+coverage_rewind (struct coverage *cells)
+{
+    cells->cursor = &cells->head;
+}
+
+static void
+coverage_init (struct coverage *cells)
+{
+    _cairo_freepool_init (&cells->pool,
+                         sizeof (struct cell));
+    cells->head.prev = NULL;
+    cells->head.next = &cells->tail;
+    cells->head.x = INT_MIN;
+    cells->tail.prev = &cells->head;
+    cells->tail.next = NULL;
+    cells->tail.x = INT_MAX;
+    cells->count = 0;
+    coverage_rewind (cells);
+}
+
+static void
+coverage_fini (struct coverage *cells)
+{
+    _cairo_freepool_fini (&cells->pool);
+}
+
+inline static void
+coverage_reset (struct coverage *cells)
+{
+    cells->head.next = &cells->tail;
+    cells->tail.prev = &cells->head;
+    cells->count = 0;
+    _cairo_freepool_reset (&cells->pool);
+    coverage_rewind (cells);
+}
+
+static struct cell *
+coverage_alloc (sweep_line_t *sweep_line,
+               struct cell *tail,
+               int x)
+{
+    struct cell *cell;
+
+    cell = _cairo_freepool_alloc (&sweep_line->coverage.pool);
+    if (unlikely (NULL == cell)) {
+       longjmp (sweep_line->unwind,
+                _cairo_error (CAIRO_STATUS_NO_MEMORY));
+    }
+
+    tail->prev->next = cell;
+    cell->prev = tail->prev;
+    cell->next = tail;
+    tail->prev = cell;
+    cell->x = x;
+    cell->uncovered_area = 0;
+    cell->covered_height = 0;
+    sweep_line->coverage.count++;
+    return cell;
+}
+
+inline static struct cell *
+coverage_find (sweep_line_t *sweep_line, int x)
+{
+    struct cell *cell;
+
+    cell = sweep_line->coverage.cursor;
+    if (unlikely (cell->x > x)) {
+       do {
+           if (cell->prev->x < x)
+               break;
+           cell = cell->prev;
+       } while (TRUE);
+    } else {
+       if (cell->x == x)
+           return cell;
+
+       do {
+           UNROLL3({
+                   cell = cell->next;
+                   if (cell->x >= x)
+                       break;
+                   });
+       } while (TRUE);
+    }
+
+    if (cell->x != x)
+       cell = coverage_alloc (sweep_line, cell, x);
+
+    return sweep_line->coverage.cursor = cell;
+}
+
+static void
+coverage_render_cells (sweep_line_t *sweep_line,
+                      cairo_fixed_t left, cairo_fixed_t right,
+                      cairo_fixed_t y1, cairo_fixed_t y2,
+                      int sign)
+{
+    int fx1, fx2;
+    int ix1, ix2;
+    int dx, dy;
+
+    /* Orient the edge left-to-right. */
+    dx = right - left;
+    if (dx >= 0) {
+       ix1 = _cairo_fixed_integer_part (left);
+       fx1 = _cairo_fixed_fractional_part (left);
+
+       ix2 = _cairo_fixed_integer_part (right);
+       fx2 = _cairo_fixed_fractional_part (right);
+
+       dy = y2 - y1;
+    } else {
+       ix1 = _cairo_fixed_integer_part (right);
+       fx1 = _cairo_fixed_fractional_part (right);
+
+       ix2 = _cairo_fixed_integer_part (left);
+       fx2 = _cairo_fixed_fractional_part (left);
+
+       dx = -dx;
+       sign = -sign;
+       dy = y1 - y2;
+       y1 = y2 - dy;
+       y2 = y1 + dy;
+    }
+
+    /* Add coverage for all pixels [ix1,ix2] on this row crossed
+     * by the edge. */
+    {
+       struct quorem y = floored_divrem ((STEP_X - fx1)*dy, dx);
+       struct cell *cell;
+
+       cell = sweep_line->coverage.cursor;
+       if (cell->x != ix1) {
+           if (unlikely (cell->x > ix1)) {
+               do {
+                   if (cell->prev->x < ix1)
+                       break;
+                   cell = cell->prev;
+               } while (TRUE);
+           } else do {
+               UNROLL3({
+                       if (cell->x >= ix1)
+                           break;
+                       cell = cell->next;
+                       });
+           } while (TRUE);
+
+           if (cell->x != ix1)
+               cell = coverage_alloc (sweep_line, cell, ix1);
+       }
+
+       cell->uncovered_area += sign * y.quo * (STEP_X + fx1);
+       cell->covered_height += sign * y.quo;
+       y.quo += y1;
+
+       cell = cell->next;
+       if (cell->x != ++ix1)
+           cell = coverage_alloc (sweep_line, cell, ix1);
+       if (ix1 < ix2) {
+           struct quorem dydx_full = floored_divrem (STEP_X*dy, dx);
+
+           do {
+               cairo_fixed_t y_skip = dydx_full.quo;
+               y.rem += dydx_full.rem;
+               if (y.rem >= dx) {
+                   ++y_skip;
+                   y.rem -= dx;
+               }
+
+               y.quo += y_skip;
+
+               y_skip *= sign;
+               cell->covered_height += y_skip;
+               cell->uncovered_area += y_skip*STEP_X;
+
+               cell = cell->next;
+               if (cell->x != ++ix1)
+                   cell = coverage_alloc (sweep_line, cell, ix1);
+           } while (ix1 != ix2);
+       }
+       cell->uncovered_area += sign*(y2 - y.quo)*fx2;
+       cell->covered_height += sign*(y2 - y.quo);
+       sweep_line->coverage.cursor = cell;
+    }
+}
+
+inline static void
+full_inc_edge (edge_t *edge)
+{
+    edge->x.quo += edge->dxdy_full.quo;
+    edge->x.rem += edge->dxdy_full.rem;
+    if (edge->x.rem >= 0) {
+       ++edge->x.quo;
+       edge->x.rem -= edge->dy;
+    }
+}
+
+static void
+full_add_edge (sweep_line_t *sweep_line, edge_t *edge, int sign)
+{
+    struct cell *cell;
+    cairo_fixed_t x1, x2;
+    int ix1, ix2;
+    int frac;
+
+    edge->current_sign = sign;
+
+    ix1 = _cairo_fixed_integer_part (edge->x.quo);
+
+    if (edge->vertical) {
+       frac = _cairo_fixed_fractional_part (edge->x.quo);
+       cell = coverage_find (sweep_line, ix1);
+       cell->covered_height += sign * STEP_Y;
+       cell->uncovered_area += sign * 2 * frac * STEP_Y;
+       return;
+    }
+
+    x1 = edge->x.quo;
+    full_inc_edge (edge);
+    x2 = edge->x.quo;
+
+    ix2 = _cairo_fixed_integer_part (edge->x.quo);
+
+    /* Edge is entirely within a column? */
+    if (likely (ix1 == ix2)) {
+       frac = _cairo_fixed_fractional_part (x1) +
+              _cairo_fixed_fractional_part (x2);
+       cell = coverage_find (sweep_line, ix1);
+       cell->covered_height += sign * STEP_Y;
+       cell->uncovered_area += sign * frac * STEP_Y;
+       return;
+    }
+
+    coverage_render_cells (sweep_line, x1, x2, 0, STEP_Y, sign);
+}
+
+static void
+full_nonzero (sweep_line_t *sweep_line)
+{
+    cairo_list_t *pos;
+
+    sweep_line->is_vertical = TRUE;
+    pos = sweep_line->active.next;
+    do {
+       edge_t *left = link_to_edge (pos), *right;
+       int winding = left->edge.dir;
+
+       sweep_line->is_vertical &= left->vertical;
+
+       pos = left->link.next;
+       do {
+           if (unlikely (pos == &sweep_line->active)) {
+               full_add_edge (sweep_line, left, +1);
+               return;
+           }
+
+           right = link_to_edge (pos);
+           pos = pos->next;
+           sweep_line->is_vertical &= right->vertical;
+
+           winding += right->edge.dir;
+           if (0 == winding) {
+               if (pos == &sweep_line->active ||
+                   link_to_edge (pos)->x.quo != right->x.quo)
+               {
+                   break;
+               }
+           }
+
+           if (! right->vertical)
+               full_inc_edge (right);
+       } while (TRUE);
+
+       full_add_edge (sweep_line, left,  +1);
+       full_add_edge (sweep_line, right, -1);
+    } while (pos != &sweep_line->active);
+}
+
+static void
+full_evenodd (sweep_line_t *sweep_line)
+{
+    cairo_list_t *pos;
+
+    sweep_line->is_vertical = TRUE;
+    pos = sweep_line->active.next;
+    do {
+       edge_t *left = link_to_edge (pos), *right;
+       int winding = 0;
+
+       sweep_line->is_vertical &= left->vertical;
+
+       pos = left->link.next;
+       do {
+           if (pos == &sweep_line->active) {
+               full_add_edge (sweep_line, left, +1);
+               return;
+           }
+
+           right = link_to_edge (pos);
+           pos = pos->next;
+           sweep_line->is_vertical &= right->vertical;
+
+           if (++winding & 1) {
+               if (pos == &sweep_line->active ||
+                   link_to_edge (pos)->x.quo != right->x.quo)
+               {
+                   break;
+               }
+           }
+
+           if (! right->vertical)
+               full_inc_edge (right);
+       } while (TRUE);
+
+       full_add_edge (sweep_line, left,  +1);
+       full_add_edge (sweep_line, right, -1);
+    } while (pos != &sweep_line->active);
+}
+
+static void
+render_rows (cairo_botor_scan_converter_t *self,
+            sweep_line_t *sweep_line,
+            int y, int height,
+            cairo_span_renderer_t *renderer)
+{
+    cairo_half_open_span_t spans_stack[CAIRO_STACK_ARRAY_LENGTH (cairo_half_open_span_t)];
+    cairo_half_open_span_t *spans = spans_stack;
+    struct cell *cell;
+    int prev_x, cover;
+    int num_spans;
+    cairo_status_t status;
+
+    if (unlikely (sweep_line->coverage.count == 0)) {
+       status = renderer->render_rows (renderer, y, height, NULL, 0);
+       if (unlikely (status))
+           longjmp (sweep_line->unwind, status);
+       return;
+    }
+
+    /* Allocate enough spans for the row. */
+
+    num_spans = 2*sweep_line->coverage.count+2;
+    if (unlikely (num_spans > ARRAY_LENGTH (spans_stack))) {
+       spans = _cairo_malloc_ab (num_spans, sizeof (cairo_half_open_span_t));
+       if (unlikely (spans == NULL)) {
+           longjmp (sweep_line->unwind,
+                    _cairo_error (CAIRO_STATUS_NO_MEMORY));
+       }
+    }
+
+    /* Form the spans from the coverage and areas. */
+    num_spans = 0;
+    prev_x = self->xmin;
+    cover = 0;
+    cell = sweep_line->coverage.head.next;
+    do {
+       int x = cell->x;
+       int area;
+
+       if (x > prev_x) {
+           spans[num_spans].x = prev_x;
+           spans[num_spans].inverse = 0;
+           spans[num_spans].coverage = AREA_TO_ALPHA (cover);
+           ++num_spans;
+       }
+
+       cover += cell->covered_height*STEP_X*2;
+       area = cover - cell->uncovered_area;
+
+       spans[num_spans].x = x;
+       spans[num_spans].coverage = AREA_TO_ALPHA (area);
+       ++num_spans;
+
+       prev_x = x + 1;
+    } while ((cell = cell->next) != &sweep_line->coverage.tail);
+
+    if (prev_x <= self->xmax) {
+       spans[num_spans].x = prev_x;
+       spans[num_spans].inverse = 0;
+       spans[num_spans].coverage = AREA_TO_ALPHA (cover);
+       ++num_spans;
+    }
+
+    if (cover && prev_x < self->xmax) {
+       spans[num_spans].x = self->xmax;
+       spans[num_spans].inverse = 1;
+       spans[num_spans].coverage = 0;
+       ++num_spans;
+    }
+
+    status = renderer->render_rows (renderer, y, height, spans, num_spans);
+
+    if (unlikely (spans != spans_stack))
+       free (spans);
+
+    coverage_reset (&sweep_line->coverage);
+
+    if (unlikely (status))
+       longjmp (sweep_line->unwind, status);
+}
+
+static void
+full_repeat (sweep_line_t *sweep)
+{
+    edge_t *edge;
+
+    cairo_list_foreach_entry (edge, edge_t, &sweep->active, link) {
+       if (edge->current_sign)
+           full_add_edge (sweep, edge, edge->current_sign);
+       else if (! edge->vertical)
+           full_inc_edge (edge);
+    }
+}
+
+static void
+full_reset (sweep_line_t *sweep)
+{
+    edge_t *edge;
+
+    cairo_list_foreach_entry (edge, edge_t, &sweep->active, link)
+       edge->current_sign = 0;
+}
+
+static void
+full_step (cairo_botor_scan_converter_t *self,
+          sweep_line_t *sweep_line,
+          cairo_fixed_t row,
+          cairo_span_renderer_t *renderer)
+{
+    int top, bottom;
+
+    top = _cairo_fixed_integer_part (sweep_line->current_row);
+    bottom = _cairo_fixed_integer_part (row);
+    if (cairo_list_is_empty (&sweep_line->active)) {
+       cairo_status_t  status;
+
+       status = renderer->render_rows (renderer, top, bottom - top, NULL, 0);
+       if (unlikely (status))
+           longjmp (sweep_line->unwind, status);
+
+       return;
+    }
+
+    if (self->fill_rule == CAIRO_FILL_RULE_WINDING)
+       full_nonzero (sweep_line);
+    else
+       full_evenodd (sweep_line);
+
+    if (sweep_line->is_vertical || bottom == top + 1) {
+       render_rows (self, sweep_line, top, bottom - top, renderer);
+       full_reset (sweep_line);
+       return;
+    }
+
+    render_rows (self, sweep_line, top++, 1, renderer);
+    do {
+       full_repeat (sweep_line);
+       render_rows (self, sweep_line, top, 1, renderer);
+    } while (++top != bottom);
+
+    full_reset (sweep_line);
+}
+
+cairo_always_inline static void
+sub_inc_edge (edge_t *edge,
+             cairo_fixed_t height)
+{
+    if (height == 1) {
+       edge->x.quo += edge->dxdy.quo;
+       edge->x.rem += edge->dxdy.rem;
+       if (edge->x.rem >= 0) {
+           ++edge->x.quo;
+           edge->x.rem -= edge->dy;
+       }
+    } else {
+       edge->x.quo += height * edge->dxdy.quo;
+       edge->x.rem += height * edge->dxdy.rem;
+       if (edge->x.rem >= 0) {
+           int carry = edge->x.rem / edge->dy + 1;
+           edge->x.quo += carry;
+           edge->x.rem -= carry * edge->dy;
+       }
+    }
+}
+
+static void
+sub_add_run (sweep_line_t *sweep_line, edge_t *edge, int y, int sign)
+{
+    struct run *run;
+
+    run = _cairo_freepool_alloc (&sweep_line->runs);
+    if (unlikely (run == NULL))
+       longjmp (sweep_line->unwind, _cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+    run->y = y;
+    run->sign = sign;
+    run->next = edge->runs;
+    edge->runs = run;
+
+    edge->current_sign = sign;
+}
+
+inline static cairo_bool_t
+edges_coincident (edge_t *left, edge_t *right, cairo_fixed_t y)
+{
+    /* XXX is compare_x_for_y() worth executing during sub steps? */
+    return line_equal (&left->edge.line, &right->edge.line);
+    //edges_compare_x_for_y (&left->edge, &right->edge, y) >= 0;
+}
+
+static void
+sub_nonzero (sweep_line_t *sweep_line)
+{
+    cairo_fixed_t y = sweep_line->current_subrow;
+    cairo_fixed_t fy = _cairo_fixed_fractional_part (y);
+    cairo_list_t *pos;
+
+    pos = sweep_line->active.next;
+    do {
+       edge_t *left = link_to_edge (pos), *right;
+       int winding = left->edge.dir;
+
+       pos = left->link.next;
+       do {
+           if (unlikely (pos == &sweep_line->active)) {
+               if (left->current_sign != +1)
+                   sub_add_run (sweep_line, left, fy, +1);
+               return;
+           }
+
+           right = link_to_edge (pos);
+           pos = pos->next;
+
+           winding += right->edge.dir;
+           if (0 == winding) {
+               if (pos == &sweep_line->active ||
+                   ! edges_coincident (right, link_to_edge (pos), y))
+               {
+                   break;
+               }
+           }
+
+           if (right->current_sign)
+               sub_add_run (sweep_line, right, fy, 0);
+       } while (TRUE);
+
+       if (left->current_sign != +1)
+           sub_add_run (sweep_line, left, fy, +1);
+       if (right->current_sign != -1)
+           sub_add_run (sweep_line, right, fy, -1);
+    } while (pos != &sweep_line->active);
+}
+
+static void
+sub_evenodd (sweep_line_t *sweep_line)
+{
+    cairo_fixed_t y = sweep_line->current_subrow;
+    cairo_fixed_t fy = _cairo_fixed_fractional_part (y);
+    cairo_list_t *pos;
+
+    pos = sweep_line->active.next;
+    do {
+       edge_t *left = link_to_edge (pos), *right;
+       int winding = 0;
+
+       pos = left->link.next;
+       do {
+           if (unlikely (pos == &sweep_line->active)) {
+               if (left->current_sign != +1)
+                   sub_add_run (sweep_line, left, fy, +1);
+               return;
+           }
+
+           right = link_to_edge (pos);
+           pos = pos->next;
+
+           if (++winding & 1) {
+               if (pos == &sweep_line->active ||
+                   ! edges_coincident (right, link_to_edge (pos), y))
+               {
+                   break;
+               }
+           }
+
+           if (right->current_sign)
+               sub_add_run (sweep_line, right, fy, 0);
+       } while (TRUE);
+
+       if (left->current_sign != +1)
+           sub_add_run (sweep_line, left, fy, +1);
+       if (right->current_sign != -1)
+           sub_add_run (sweep_line, right, fy, -1);
+    } while (pos != &sweep_line->active);
+}
+
+cairo_always_inline static void
+sub_step (cairo_botor_scan_converter_t *self,
+         sweep_line_t *sweep_line)
+{
+    if (cairo_list_is_empty (&sweep_line->active))
+       return;
+
+    if (self->fill_rule == CAIRO_FILL_RULE_WINDING)
+       sub_nonzero (sweep_line);
+    else
+       sub_evenodd (sweep_line);
+}
+
+static void
+coverage_render_runs (sweep_line_t *sweep, edge_t *edge,
+                     cairo_fixed_t y1, cairo_fixed_t y2)
+{
+    struct run tail;
+    struct run *run = &tail;
+
+    tail.next = NULL;
+    tail.y = y2;
+
+    /* Order the runs top->bottom */
+    while (edge->runs) {
+       struct run *r;
+
+       r = edge->runs;
+       edge->runs = r->next;
+       r->next = run;
+       run = r;
+    }
+
+    if (run->y > y1)
+       sub_inc_edge (edge, run->y - y1);
+
+    do {
+       cairo_fixed_t x1, x2;
+
+       y1 = run->y;
+       y2 = run->next->y;
+
+       x1 = edge->x.quo;
+       if (y2 - y1 == STEP_Y)
+           full_inc_edge (edge);
+       else
+           sub_inc_edge (edge, y2 - y1);
+       x2 = edge->x.quo;
+
+       if (run->sign) {
+           int ix1, ix2;
+
+           ix1 = _cairo_fixed_integer_part (x1);
+           ix2 = _cairo_fixed_integer_part (x2);
+
+           /* Edge is entirely within a column? */
+           if (likely (ix1 == ix2)) {
+               struct cell *cell;
+               int frac;
+
+               frac = _cairo_fixed_fractional_part (x1) +
+                      _cairo_fixed_fractional_part (x2);
+               cell = coverage_find (sweep, ix1);
+               cell->covered_height += run->sign * (y2 - y1);
+               cell->uncovered_area += run->sign * (y2 - y1) * frac;
+           } else {
+               coverage_render_cells (sweep, x1, x2, y1, y2, run->sign);
+           }
+       }
+
+       run = run->next;
+    } while (run->next != NULL);
+}
+
+static void
+coverage_render_vertical_runs (sweep_line_t *sweep, edge_t *edge, cairo_fixed_t y2)
+{
+    struct cell *cell;
+    struct run *run;
+    int height = 0;
+
+    for (run = edge->runs; run != NULL; run = run->next) {
+       if (run->sign)
+           height += run->sign * (y2 - run->y);
+       y2 = run->y;
+    }
+
+    cell = coverage_find (sweep, _cairo_fixed_integer_part (edge->x.quo));
+    cell->covered_height += height;
+    cell->uncovered_area += 2 * _cairo_fixed_fractional_part (edge->x.quo) * height;
+}
+
+cairo_always_inline static void
+sub_emit (cairo_botor_scan_converter_t *self,
+         sweep_line_t *sweep,
+         cairo_span_renderer_t *renderer)
+{
+    edge_t *edge;
+
+    sub_step (self, sweep);
+
+    /* convert the runs into coverages */
+
+    cairo_list_foreach_entry (edge, edge_t, &sweep->active, link) {
+       if (edge->runs == NULL) {
+           if (! edge->vertical) {
+               if (edge->flags & START) {
+                   sub_inc_edge (edge,
+                                 STEP_Y - _cairo_fixed_fractional_part (edge->edge.top));
+                   edge->flags &= ~START;
+               } else
+                   full_inc_edge (edge);
+           }
+       } else {
+           if (edge->vertical) {
+               coverage_render_vertical_runs (sweep, edge, STEP_Y);
+           } else {
+               int y1 = 0;
+               if (edge->flags & START) {
+                   y1 = _cairo_fixed_fractional_part (edge->edge.top);
+                   edge->flags &= ~START;
+               }
+               coverage_render_runs (sweep, edge, y1, STEP_Y);
+           }
+       }
+       edge->current_sign = 0;
+       edge->runs = NULL;
+    }
+
+    cairo_list_foreach_entry (edge, edge_t, &sweep->stopped, link) {
+       int y2 = _cairo_fixed_fractional_part (edge->edge.bottom);
+       if (edge->vertical) {
+           coverage_render_vertical_runs (sweep, edge, y2);
+       } else {
+           int y1 = 0;
+           if (edge->flags & START)
+               y1 = _cairo_fixed_fractional_part (edge->edge.top);
+           coverage_render_runs (sweep, edge, y1, y2);
+       }
+    }
+    cairo_list_init (&sweep->stopped);
+
+    _cairo_freepool_reset (&sweep->runs);
+
+    render_rows (self, sweep,
+                _cairo_fixed_integer_part (sweep->current_row), 1,
+                renderer);
+}
+
+static void
+sweep_line_init (sweep_line_t   *sweep_line,
+                event_t        **start_events,
+                int              num_events)
+{
+    cairo_list_init (&sweep_line->active);
+    cairo_list_init (&sweep_line->stopped);
+    sweep_line->insert_cursor = &sweep_line->active;
+
+    sweep_line->current_row = INT32_MIN;
+    sweep_line->current_subrow = INT32_MIN;
+
+    coverage_init (&sweep_line->coverage);
+    _cairo_freepool_init (&sweep_line->runs, sizeof (struct run));
+
+    start_event_sort (start_events, num_events);
+    start_events[num_events] = NULL;
+
+    sweep_line->queue.start_events = start_events;
+
+    _cairo_freepool_init (&sweep_line->queue.pool,
+                         sizeof (queue_event_t));
+    pqueue_init (&sweep_line->queue.pq);
+    sweep_line->queue.pq.elements[PQ_FIRST_ENTRY] = NULL;
+}
+
+static void
+sweep_line_delete (sweep_line_t        *sweep_line,
+                  edge_t       *edge)
+{
+    if (sweep_line->insert_cursor == &edge->link)
+       sweep_line->insert_cursor = edge->link.prev;
+
+    cairo_list_del (&edge->link);
+    if (edge->runs)
+       cairo_list_add_tail (&edge->link, &sweep_line->stopped);
+    edge->flags |= STOP;
+}
+
+static void
+sweep_line_swap (sweep_line_t  *sweep_line,
+                edge_t *left,
+                edge_t *right)
+{
+    right->link.prev = left->link.prev;
+    left->link.next = right->link.next;
+    right->link.next = &left->link;
+    left->link.prev = &right->link;
+    left->link.next->prev = &left->link;
+    right->link.prev->next = &right->link;
+}
+
+static void
+sweep_line_fini (sweep_line_t *sweep_line)
+{
+    pqueue_fini (&sweep_line->queue.pq);
+    _cairo_freepool_fini (&sweep_line->queue.pool);
+    coverage_fini (&sweep_line->coverage);
+    _cairo_freepool_fini (&sweep_line->runs);
+}
+
+static cairo_status_t
+botor_generate (cairo_botor_scan_converter_t    *self,
+               event_t                         **start_events,
+               cairo_span_renderer_t            *renderer)
+{
+    cairo_status_t status;
+    sweep_line_t sweep_line;
+    cairo_fixed_t ybot;
+    event_t *event;
+    cairo_list_t *left, *right;
+    edge_t *e1, *e2;
+    int bottom;
+
+    sweep_line_init (&sweep_line, start_events, self->num_edges);
+    if ((status = setjmp (sweep_line.unwind)))
+       goto unwind;
+
+    ybot = self->extents.p2.y;
+    sweep_line.current_subrow = self->extents.p1.y;
+    sweep_line.current_row = _cairo_fixed_floor (self->extents.p1.y);
+    event = *sweep_line.queue.start_events++;
+    do {
+       /* Can we process a full step in one go? */
+       if (event->y >= sweep_line.current_row + STEP_Y) {
+           bottom = _cairo_fixed_floor (event->y);
+           full_step (self, &sweep_line, bottom, renderer);
+           sweep_line.current_row = bottom;
+           sweep_line.current_subrow = bottom;
+       }
+
+       do {
+           if (event->y > sweep_line.current_subrow) {
+               sub_step (self, &sweep_line);
+               sweep_line.current_subrow = event->y;
+           }
+
+           do {
+               /* Update the active list using Bentley-Ottmann */
+               switch (event->type) {
+               case EVENT_TYPE_START:
+                   e1 = ((start_event_t *) event)->edge;
+
+                   sweep_line_insert (&sweep_line, e1);
+                   event_insert_stop (&sweep_line, e1);
+
+                   left = e1->link.prev;
+                   right = e1->link.next;
+
+                   if (left != &sweep_line.active) {
+                       event_insert_if_intersect_below_current_y (&sweep_line,
+                                                                  link_to_edge (left), e1);
+                   }
+
+                   if (right != &sweep_line.active) {
+                       event_insert_if_intersect_below_current_y (&sweep_line,
+                                                                  e1, link_to_edge (right));
+                   }
+
+                   break;
+
+               case EVENT_TYPE_STOP:
+                   e1 = ((queue_event_t *) event)->e1;
+                   event_delete (&sweep_line, event);
+
+                   left = e1->link.prev;
+                   right = e1->link.next;
+
+                   sweep_line_delete (&sweep_line, e1);
+
+                   if (left != &sweep_line.active &&
+                       right != &sweep_line.active)
+                   {
+                        event_insert_if_intersect_below_current_y (&sweep_line,
+                                                                   link_to_edge (left),
+                                                                   link_to_edge (right));
+                   }
+
+                   break;
+
+               case EVENT_TYPE_INTERSECTION:
+                   e1 = ((queue_event_t *) event)->e1;
+                   e2 = ((queue_event_t *) event)->e2;
+
+                   event_delete (&sweep_line, event);
+                   if (e1->flags & STOP)
+                       break;
+                   if (e2->flags & STOP)
+                       break;
+
+                   /* skip this intersection if its edges are not adjacent */
+                   if (&e2->link != e1->link.next)
+                       break;
+
+                   left = e1->link.prev;
+                   right = e2->link.next;
+
+                   sweep_line_swap (&sweep_line, e1, e2);
+
+                   /* after the swap e2 is left of e1 */
+                   if (left != &sweep_line.active) {
+                       event_insert_if_intersect_below_current_y (&sweep_line,
+                                                                  link_to_edge (left), e2);
+                   }
+
+                   if (right != &sweep_line.active) {
+                       event_insert_if_intersect_below_current_y (&sweep_line,
+                                                                  e1, link_to_edge (right));
+                   }
+
+                   break;
+               }
+
+               event = event_next (&sweep_line);
+               if (event == NULL)
+                   goto end;
+           } while (event->y == sweep_line.current_subrow);
+       } while (event->y < sweep_line.current_row + STEP_Y);
+
+       bottom = sweep_line.current_row + STEP_Y;
+       sub_emit (self, &sweep_line, renderer);
+       sweep_line.current_subrow = bottom;
+       sweep_line.current_row = sweep_line.current_subrow;
+    } while (TRUE);
+
+  end:
+    /* flush any partial spans */
+    if (sweep_line.current_subrow != sweep_line.current_row) {
+       sub_emit (self, &sweep_line, renderer);
+       sweep_line.current_row += STEP_Y;
+       sweep_line.current_subrow = sweep_line.current_row;
+    }
+    /* clear the rest */
+    if (sweep_line.current_subrow < ybot) {
+       bottom = _cairo_fixed_integer_part (sweep_line.current_row);
+       status = renderer->render_rows (renderer,
+                                       bottom, _cairo_fixed_integer_ceil (ybot) - bottom,
+                                       NULL, 0);
+    }
+
+ unwind:
+    sweep_line_fini (&sweep_line);
+
+    return status;
+}
+
+static cairo_status_t
+_cairo_botor_scan_converter_generate (void                     *converter,
+                                     cairo_span_renderer_t     *renderer)
+{
+    cairo_botor_scan_converter_t *self = converter;
+    start_event_t stack_events[CAIRO_STACK_ARRAY_LENGTH (start_event_t)];
+    start_event_t *events;
+    event_t *stack_event_ptrs[ARRAY_LENGTH (stack_events) + 1];
+    event_t **event_ptrs;
+    struct _cairo_botor_scan_converter_chunk *chunk;
+    cairo_status_t status;
+    int num_events;
+    int i, j;
+
+    num_events = self->num_edges;
+    if (unlikely (0 == num_events)) {
+       return renderer->render_rows (renderer,
+                                     _cairo_fixed_integer_floor (self->extents.p1.y),
+                                     _cairo_fixed_integer_ceil (self->extents.p2.y) -
+                                     _cairo_fixed_integer_floor (self->extents.p1.y),
+                                     NULL, 0);
+    }
+
+    events = stack_events;
+    event_ptrs = stack_event_ptrs;
+    if (unlikely (num_events >= ARRAY_LENGTH (stack_events))) {
+       events = _cairo_malloc_ab_plus_c (num_events,
+                                         sizeof (start_event_t) + sizeof (event_t *),
+                                         sizeof (event_t *));
+       if (unlikely (events == NULL))
+           return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+       event_ptrs = (event_t **) (events + num_events);
+    }
+
+    j = 0;
+    for (chunk = &self->chunks; chunk != NULL; chunk = chunk->next) {
+       edge_t *edge;
+
+       edge = chunk->base;
+       for (i = 0; i < chunk->count; i++) {
+           event_ptrs[j] = (event_t *) &events[j];
+
+           events[j].y = edge->edge.top;
+           events[j].type = EVENT_TYPE_START;
+           events[j].edge = edge;
+
+           edge++, j++;
+       }
+    }
+
+    status = botor_generate (self, event_ptrs, renderer);
+
+    if (events != stack_events)
+       free (events);
+
+    return status;
+}
+
+static edge_t *
+botor_allocate_edge (cairo_botor_scan_converter_t *self)
+{
+    struct _cairo_botor_scan_converter_chunk *chunk;
+
+    chunk = self->tail;
+    if (chunk->count == chunk->size) {
+       int size;
+
+       size = chunk->size * 2;
+       chunk->next = _cairo_malloc_ab_plus_c (size,
+                                              sizeof (edge_t),
+                                              sizeof (struct _cairo_botor_scan_converter_chunk));
+       if (unlikely (chunk->next == NULL))
+           return NULL;
+
+       chunk = chunk->next;
+       chunk->next = NULL;
+       chunk->count = 0;
+       chunk->size = size;
+       chunk->base = chunk + 1;
+       self->tail = chunk;
+    }
+
+    return (edge_t *) chunk->base + chunk->count++;
+}
+
+static cairo_status_t
+botor_add_edge (cairo_botor_scan_converter_t *self,
+               const cairo_edge_t *edge)
+{
+    edge_t *e;
+    cairo_fixed_t dx, dy;
+
+    e = botor_allocate_edge (self);
+    if (unlikely (e == NULL))
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    cairo_list_init (&e->link);
+    e->edge = *edge;
+
+    dx = edge->line.p2.x - edge->line.p1.x;
+    dy = edge->line.p2.y - edge->line.p1.y;
+    e->dy = dy;
+
+    if (dx == 0) {
+       e->vertical = TRUE;
+       e->x.quo = edge->line.p1.x;
+       e->x.rem = 0;
+       e->dxdy.quo = 0;
+       e->dxdy.rem = 0;
+       e->dxdy_full.quo = 0;
+       e->dxdy_full.rem = 0;
+    } else {
+       e->vertical = FALSE;
+       e->dxdy = floored_divrem (dx, dy);
+       if (edge->top == edge->line.p1.y) {
+           e->x.quo = edge->line.p1.x;
+           e->x.rem = 0;
+       } else {
+           e->x = floored_muldivrem (edge->top - edge->line.p1.y,
+                                     dx, dy);
+           e->x.quo += edge->line.p1.x;
+       }
+
+       if (_cairo_fixed_integer_part (edge->bottom) - _cairo_fixed_integer_part (edge->top) > 1) {
+           e->dxdy_full = floored_muldivrem (STEP_Y, dx, dy);
+       } else {
+           e->dxdy_full.quo = 0;
+           e->dxdy_full.rem = 0;
+       }
+    }
+
+    e->x.rem = -e->dy;
+    e->current_sign = 0;
+    e->runs = NULL;
+    e->flags = START;
+
+    self->num_edges++;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_botor_scan_converter_add_edge (void             *converter,
+                                     const cairo_point_t *p1,
+                                     const cairo_point_t *p2,
+                                     int top, int bottom,
+                                     int dir)
+{
+    cairo_botor_scan_converter_t *self = converter;
+    cairo_edge_t edge;
+
+    edge.line.p1 = *p1;
+    edge.line.p2 = *p2;
+    edge.top = top;
+    edge.bottom = bottom;
+    edge.dir = dir;
+
+    return botor_add_edge (self, &edge);
+}
+
+static cairo_status_t
+_cairo_botor_scan_converter_add_polygon (void          *converter,
+                                        const cairo_polygon_t *polygon)
+{
+    cairo_botor_scan_converter_t *self = converter;
+    cairo_status_t status;
+    int i;
+
+    for (i = 0; i < polygon->num_edges; i++) {
+       status = botor_add_edge (self, &polygon->edges[i]);
+       if (unlikely (status))
+           return status;
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_botor_scan_converter_destroy (void *converter)
+{
+    cairo_botor_scan_converter_t *self = converter;
+    struct _cairo_botor_scan_converter_chunk *chunk, *next;
+
+    for (chunk = self->chunks.next; chunk != NULL; chunk = next) {
+       next = chunk->next;
+       free (chunk);
+    }
+}
+
+void
+_cairo_botor_scan_converter_init (cairo_botor_scan_converter_t *self,
+                                 const cairo_box_t *extents,
+                                 cairo_fill_rule_t fill_rule)
+{
+    self->base.destroy     = _cairo_botor_scan_converter_destroy;
+    self->base.generate    = _cairo_botor_scan_converter_generate;
+
+    self->extents   = *extents;
+    self->fill_rule = fill_rule;
+
+    self->xmin = _cairo_fixed_integer_floor (extents->p1.x);
+    self->xmax = _cairo_fixed_integer_ceil (extents->p2.x);
+
+    self->chunks.base = self->buf;
+    self->chunks.next = NULL;
+    self->chunks.count = 0;
+    self->chunks.size = sizeof (self->buf) / sizeof (edge_t);
+    self->tail = &self->chunks;
+
+    self->num_edges = 0;
+}
diff --git a/src/cairo-box-private.h b/src/cairo-box-private.h
new file mode 100644 (file)
index 0000000..d6b9941
--- /dev/null
@@ -0,0 +1,121 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2010 Andrea Canciani
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * Contributor(s):
+ *     Andrea Canciani <ranma42@gmail.com>
+ */
+
+#ifndef CAIRO_BOX_H
+#define CAIRO_BOX_H
+
+#include "cairo-types-private.h"
+#include "cairo-compiler-private.h"
+#include "cairo-fixed-private.h"
+
+static inline void
+_cairo_box_set (cairo_box_t *box,
+               const cairo_point_t *p1,
+               const cairo_point_t *p2)
+{
+    box->p1 = *p1;
+    box->p2 = *p2;
+}
+
+static inline void
+_cairo_box_from_integers (cairo_box_t *box, int x, int y, int w, int h)
+{
+    box->p1.x = _cairo_fixed_from_int (x);
+    box->p1.y = _cairo_fixed_from_int (y);
+    box->p2.x = _cairo_fixed_from_int (x + w);
+    box->p2.y = _cairo_fixed_from_int (y + h);
+}
+
+/* assumes box->p1 is top-left, p2 bottom-right */
+static inline void
+_cairo_box_add_point (cairo_box_t *box,
+                     const cairo_point_t *point)
+{
+    if (point->x < box->p1.x)
+       box->p1.x = point->x;
+    else if (point->x > box->p2.x)
+       box->p2.x = point->x;
+
+    if (point->y < box->p1.y)
+       box->p1.y = point->y;
+    else if (point->y > box->p2.y)
+       box->p2.y = point->y;
+}
+
+static inline void
+_cairo_box_add_box (cairo_box_t *box,
+                   const cairo_box_t *add)
+{
+    if (add->p1.x < box->p1.x)
+       box->p1.x = add->p1.x;
+    if (add->p2.x > box->p2.x)
+       box->p2.x = add->p2.x;
+
+    if (add->p1.y < box->p1.y)
+       box->p1.y = add->p1.y;
+    if (add->p2.y > box->p2.y)
+       box->p2.y = add->p2.y;
+}
+
+/* assumes box->p1 is top-left, p2 bottom-right */
+static inline cairo_bool_t
+_cairo_box_contains_point (const cairo_box_t *box,
+                          const cairo_point_t *point)
+{
+    return box->p1.x <= point->x  && point->x <= box->p2.x &&
+       box->p1.y <= point->y  && point->y <= box->p2.y;
+}
+
+static inline cairo_bool_t
+_cairo_box_is_pixel_aligned (const cairo_box_t *box)
+{
+#if CAIRO_FIXED_FRAC_BITS <= 8 && 0
+    return ((box->p1.x & CAIRO_FIXED_FRAC_MASK) << 24 |
+           (box->p1.y & CAIRO_FIXED_FRAC_MASK) << 16 |
+           (box->p2.x & CAIRO_FIXED_FRAC_MASK) << 8 |
+           (box->p2.y & CAIRO_FIXED_FRAC_MASK) << 0) == 0;
+#else /* GCC on i7 prefers this variant (bizarrely according to the profiler) */
+    cairo_fixed_t f;
+
+    f = 0;
+    f |= box->p1.x & CAIRO_FIXED_FRAC_MASK;
+    f |= box->p1.y & CAIRO_FIXED_FRAC_MASK;
+    f |= box->p2.x & CAIRO_FIXED_FRAC_MASK;
+    f |= box->p2.y & CAIRO_FIXED_FRAC_MASK;
+
+    return f == 0;
+#endif
+}
+
+#endif /* CAIRO_BOX_H */
diff --git a/src/cairo-boxes-intersect.c b/src/cairo-boxes-intersect.c
new file mode 100644 (file)
index 0000000..dd4c241
--- /dev/null
@@ -0,0 +1,690 @@
+/*
+ * Copyright © 2004 Carl Worth
+ * Copyright © 2006 Red Hat, Inc.
+ * Copyright © 2009 Chris Wilson
+ * Copyright © 2011 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Carl Worth
+ *
+ * Contributor(s):
+ *     Carl D. Worth <cworth@cworth.org>
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+/* Provide definitions for standalone compilation */
+#include "cairoint.h"
+
+#include "cairo-boxes-private.h"
+#include "cairo-error-private.h"
+#include "cairo-combsort-private.h"
+#include "cairo-list-private.h"
+
+#include <setjmp.h>
+
+typedef struct _rectangle rectangle_t;
+typedef struct _edge edge_t;
+
+struct _edge {
+    edge_t *next, *prev;
+    edge_t *right;
+    cairo_fixed_t x, top;
+    int a_or_b;
+    int dir;
+};
+
+struct _rectangle {
+    edge_t left, right;
+    int32_t top, bottom;
+};
+
+#define UNROLL3(x) x x x
+
+/* the parent is always given by index/2 */
+#define PQ_PARENT_INDEX(i) ((i) >> 1)
+#define PQ_FIRST_ENTRY 1
+
+/* left and right children are index * 2 and (index * 2) +1 respectively */
+#define PQ_LEFT_CHILD_INDEX(i) ((i) << 1)
+
+typedef struct _pqueue {
+    int size, max_size;
+
+    rectangle_t **elements;
+    rectangle_t *elements_embedded[1024];
+} pqueue_t;
+
+typedef struct _sweep_line {
+    rectangle_t **rectangles;
+    pqueue_t pq;
+    edge_t head, tail;
+    edge_t *insert_left, *insert_right;
+    int32_t current_y;
+    int32_t last_y;
+
+    jmp_buf unwind;
+} sweep_line_t;
+
+#define DEBUG_TRAPS 0
+
+#if DEBUG_TRAPS
+static void
+dump_traps (cairo_traps_t *traps, const char *filename)
+{
+    FILE *file;
+    int n;
+
+    if (getenv ("CAIRO_DEBUG_TRAPS") == NULL)
+       return;
+
+    file = fopen (filename, "a");
+    if (file != NULL) {
+       for (n = 0; n < traps->num_traps; n++) {
+           fprintf (file, "%d %d L:(%d, %d), (%d, %d) R:(%d, %d), (%d, %d)\n",
+                    traps->traps[n].top,
+                    traps->traps[n].bottom,
+                    traps->traps[n].left.p1.x,
+                    traps->traps[n].left.p1.y,
+                    traps->traps[n].left.p2.x,
+                    traps->traps[n].left.p2.y,
+                    traps->traps[n].right.p1.x,
+                    traps->traps[n].right.p1.y,
+                    traps->traps[n].right.p2.x,
+                    traps->traps[n].right.p2.y);
+       }
+       fprintf (file, "\n");
+       fclose (file);
+    }
+}
+#else
+#define dump_traps(traps, filename)
+#endif
+
+static inline int
+rectangle_compare_start (const rectangle_t *a,
+                        const rectangle_t *b)
+{
+    return a->top - b->top;
+}
+
+static inline int
+rectangle_compare_stop (const rectangle_t *a,
+                        const rectangle_t *b)
+{
+    return a->bottom - b->bottom;
+}
+
+static inline void
+pqueue_init (pqueue_t *pq)
+{
+    pq->max_size = ARRAY_LENGTH (pq->elements_embedded);
+    pq->size = 0;
+
+    pq->elements = pq->elements_embedded;
+    pq->elements[PQ_FIRST_ENTRY] = NULL;
+}
+
+static inline void
+pqueue_fini (pqueue_t *pq)
+{
+    if (pq->elements != pq->elements_embedded)
+       free (pq->elements);
+}
+
+static cairo_bool_t
+pqueue_grow (pqueue_t *pq)
+{
+    rectangle_t **new_elements;
+    pq->max_size *= 2;
+
+    if (pq->elements == pq->elements_embedded) {
+       new_elements = _cairo_malloc_ab (pq->max_size,
+                                        sizeof (rectangle_t *));
+       if (unlikely (new_elements == NULL))
+           return FALSE;
+
+       memcpy (new_elements, pq->elements_embedded,
+               sizeof (pq->elements_embedded));
+    } else {
+       new_elements = _cairo_realloc_ab (pq->elements,
+                                         pq->max_size,
+                                         sizeof (rectangle_t *));
+       if (unlikely (new_elements == NULL))
+           return FALSE;
+    }
+
+    pq->elements = new_elements;
+    return TRUE;
+}
+
+static inline void
+pqueue_push (sweep_line_t *sweep, rectangle_t *rectangle)
+{
+    rectangle_t **elements;
+    int i, parent;
+
+    if (unlikely (sweep->pq.size + 1 == sweep->pq.max_size)) {
+       if (unlikely (! pqueue_grow (&sweep->pq))) {
+           longjmp (sweep->unwind,
+                    _cairo_error (CAIRO_STATUS_NO_MEMORY));
+       }
+    }
+
+    elements = sweep->pq.elements;
+    for (i = ++sweep->pq.size;
+        i != PQ_FIRST_ENTRY &&
+        rectangle_compare_stop (rectangle,
+                                elements[parent = PQ_PARENT_INDEX (i)]) < 0;
+        i = parent)
+    {
+       elements[i] = elements[parent];
+    }
+
+    elements[i] = rectangle;
+}
+
+static inline void
+pqueue_pop (pqueue_t *pq)
+{
+    rectangle_t **elements = pq->elements;
+    rectangle_t *tail;
+    int child, i;
+
+    tail = elements[pq->size--];
+    if (pq->size == 0) {
+       elements[PQ_FIRST_ENTRY] = NULL;
+       return;
+    }
+
+    for (i = PQ_FIRST_ENTRY;
+        (child = PQ_LEFT_CHILD_INDEX (i)) <= pq->size;
+        i = child)
+    {
+       if (child != pq->size &&
+           rectangle_compare_stop (elements[child+1],
+                                   elements[child]) < 0)
+       {
+           child++;
+       }
+
+       if (rectangle_compare_stop (elements[child], tail) >= 0)
+           break;
+
+       elements[i] = elements[child];
+    }
+    elements[i] = tail;
+}
+
+static inline rectangle_t *
+rectangle_pop_start (sweep_line_t *sweep_line)
+{
+    return *sweep_line->rectangles++;
+}
+
+static inline rectangle_t *
+rectangle_peek_stop (sweep_line_t *sweep_line)
+{
+    return sweep_line->pq.elements[PQ_FIRST_ENTRY];
+}
+
+CAIRO_COMBSORT_DECLARE (_rectangle_sort,
+                       rectangle_t *,
+                       rectangle_compare_start)
+
+static void
+sweep_line_init (sweep_line_t   *sweep_line,
+                rectangle_t    **rectangles,
+                int              num_rectangles)
+{
+    _rectangle_sort (rectangles, num_rectangles);
+    rectangles[num_rectangles] = NULL;
+    sweep_line->rectangles = rectangles;
+
+    sweep_line->head.x = INT32_MIN;
+    sweep_line->head.right = NULL;
+    sweep_line->head.dir = 0;
+    sweep_line->head.next = &sweep_line->tail;
+    sweep_line->tail.x = INT32_MAX;
+    sweep_line->tail.right = NULL;
+    sweep_line->tail.dir = 0;
+    sweep_line->tail.prev = &sweep_line->head;
+
+    sweep_line->insert_left = &sweep_line->tail;
+    sweep_line->insert_right = &sweep_line->tail;
+
+    sweep_line->current_y = INT32_MIN;
+    sweep_line->last_y = INT32_MIN;
+
+    pqueue_init (&sweep_line->pq);
+}
+
+static void
+sweep_line_fini (sweep_line_t *sweep_line)
+{
+    pqueue_fini (&sweep_line->pq);
+}
+
+static void
+end_box (sweep_line_t *sweep_line, edge_t *left, int32_t bot, cairo_boxes_t *out)
+{
+    if (likely (left->top < bot)) {
+       cairo_status_t status;
+       cairo_box_t box;
+
+       box.p1.x = left->x;
+       box.p1.y = left->top;
+       box.p2.x = left->right->x;
+       box.p2.y = bot;
+
+       status = _cairo_boxes_add (out, CAIRO_ANTIALIAS_DEFAULT, &box);
+       if (unlikely (status))
+           longjmp (sweep_line->unwind, status);
+    }
+
+    left->right = NULL;
+}
+
+/* Start a new trapezoid at the given top y coordinate, whose edges
+ * are `edge' and `edge->next'. If `edge' already has a trapezoid,
+ * then either add it to the traps in `traps', if the trapezoid's
+ * right edge differs from `edge->next', or do nothing if the new
+ * trapezoid would be a continuation of the existing one. */
+static inline void
+start_or_continue_box (sweep_line_t *sweep_line,
+                      edge_t   *left,
+                      edge_t   *right,
+                      int               top,
+                      cairo_boxes_t *out)
+{
+    if (left->right == right)
+       return;
+
+    if (left->right != NULL) {
+       if (right != NULL && left->right->x == right->x) {
+           /* continuation on right, so just swap edges */
+           left->right = right;
+           return;
+       }
+
+       end_box (sweep_line, left, top, out);
+    }
+
+    if (right != NULL && left->x != right->x) {
+       left->top = top;
+       left->right = right;
+    }
+}
+
+static inline int is_zero(const int *winding)
+{
+    return winding[0] == 0 || winding[1] == 0;
+}
+
+static inline void
+active_edges (sweep_line_t *sweep, cairo_boxes_t *out)
+{
+    int top = sweep->current_y;
+    int winding[2] = { 0 };
+    edge_t *pos;
+
+    if (sweep->last_y == sweep->current_y)
+       return;
+
+    pos = sweep->head.next;
+    if (pos == &sweep->tail)
+       return;
+
+    do {
+       edge_t *left, *right;
+
+       left = pos;
+       do {
+           winding[left->a_or_b] += left->dir;
+           if (!is_zero (winding))
+               break;
+           if (left->next == &sweep->tail)
+               goto out;
+
+           if (unlikely (left->right != NULL))
+               end_box (sweep, left, top, out);
+
+           left = left->next;
+       } while (1);
+
+       right = left->next;
+       do {
+           if (unlikely (right->right != NULL))
+               end_box (sweep, right, top, out);
+
+           winding[right->a_or_b] += right->dir;
+           if (is_zero (winding)) {
+               /* skip co-linear edges */
+               if (likely (right->x != right->next->x))
+                   break;
+           }
+
+           right = right->next;
+       } while (TRUE);
+
+       start_or_continue_box (sweep, left, right, top, out);
+
+       pos = right->next;
+    } while (pos != &sweep->tail);
+
+out:
+    sweep->last_y = sweep->current_y;
+}
+
+static inline void
+sweep_line_delete_edge (sweep_line_t *sweep_line, edge_t *edge, cairo_boxes_t *out)
+{
+    if (edge->right != NULL) {
+       edge_t *next = edge->next;
+       if (next->x == edge->x) {
+           next->top = edge->top;
+           next->right = edge->right;
+       } else {
+           end_box (sweep_line, edge, sweep_line->current_y, out);
+       }
+    }
+
+    if (sweep_line->insert_left == edge)
+       sweep_line->insert_left = edge->next;
+    if (sweep_line->insert_right == edge)
+       sweep_line->insert_right = edge->next;
+
+    edge->prev->next = edge->next;
+    edge->next->prev = edge->prev;
+}
+
+static inline void
+sweep_line_delete (sweep_line_t        *sweep,
+                  rectangle_t  *rectangle,
+                  cairo_boxes_t *out)
+{
+    sweep_line_delete_edge (sweep, &rectangle->left, out);
+    sweep_line_delete_edge (sweep, &rectangle->right, out);
+
+    pqueue_pop (&sweep->pq);
+}
+
+static inline void
+insert_edge (edge_t *edge, edge_t *pos)
+{
+    if (pos->x != edge->x) {
+       if (pos->x > edge->x) {
+           do {
+               UNROLL3({
+                   if (pos->prev->x <= edge->x)
+                       break;
+                   pos = pos->prev;
+               })
+           } while (TRUE);
+       } else {
+           do {
+               UNROLL3({
+                   pos = pos->next;
+                   if (pos->x >= edge->x)
+                       break;
+               })
+           } while (TRUE);
+       }
+    }
+
+    pos->prev->next = edge;
+    edge->prev = pos->prev;
+    edge->next = pos;
+    pos->prev = edge;
+}
+
+static inline void
+sweep_line_insert (sweep_line_t        *sweep, rectangle_t     *rectangle)
+{
+    edge_t *pos;
+
+    /* right edge */
+    pos = sweep->insert_right;
+    insert_edge (&rectangle->right, pos);
+    sweep->insert_right = &rectangle->right;
+
+    /* left edge */
+    pos = sweep->insert_left;
+    if (pos->x > sweep->insert_right->x)
+       pos = sweep->insert_right->prev;
+    insert_edge (&rectangle->left, pos);
+    sweep->insert_left = &rectangle->left;
+
+    pqueue_push (sweep, rectangle);
+}
+
+static cairo_status_t
+intersect (rectangle_t **rectangles, int num_rectangles, cairo_boxes_t *out)
+{
+    sweep_line_t sweep_line;
+    rectangle_t *rectangle;
+    cairo_status_t status;
+
+    sweep_line_init (&sweep_line, rectangles, num_rectangles);
+    if ((status = setjmp (sweep_line.unwind)))
+       goto unwind;
+
+    rectangle = rectangle_pop_start (&sweep_line);
+    do {
+       if (rectangle->top != sweep_line.current_y) {
+           rectangle_t *stop;
+
+           stop = rectangle_peek_stop (&sweep_line);
+           while (stop != NULL && stop->bottom < rectangle->top) {
+               if (stop->bottom != sweep_line.current_y) {
+                   active_edges (&sweep_line, out);
+                   sweep_line.current_y = stop->bottom;
+               }
+
+               sweep_line_delete (&sweep_line, stop, out);
+
+               stop = rectangle_peek_stop (&sweep_line);
+           }
+
+           active_edges (&sweep_line, out);
+           sweep_line.current_y = rectangle->top;
+       }
+
+       sweep_line_insert (&sweep_line, rectangle);
+    } while ((rectangle = rectangle_pop_start (&sweep_line)) != NULL);
+
+    while ((rectangle = rectangle_peek_stop (&sweep_line)) != NULL) {
+       if (rectangle->bottom != sweep_line.current_y) {
+           active_edges (&sweep_line, out);
+           sweep_line.current_y = rectangle->bottom;
+       }
+
+       sweep_line_delete (&sweep_line, rectangle, out);
+    }
+
+unwind:
+    sweep_line_fini (&sweep_line);
+    return status;
+}
+
+static cairo_status_t
+_cairo_boxes_intersect_with_box (const cairo_boxes_t *boxes,
+                                const cairo_box_t *box,
+                                cairo_boxes_t *out)
+{
+    cairo_status_t status;
+    int i, j;
+
+    if (out == boxes) { /* inplace update */
+       struct _cairo_boxes_chunk *chunk;
+
+       out->num_boxes = 0;
+       for (chunk = &out->chunks; chunk != NULL; chunk = chunk->next) {
+           for (i = j = 0; i < chunk->count; i++) {
+               cairo_box_t *b = &chunk->base[i];
+
+               b->p1.x = MAX (b->p1.x, box->p1.x);
+               b->p1.y = MAX (b->p1.y, box->p1.y);
+               b->p2.x = MIN (b->p2.x, box->p2.x);
+               b->p2.y = MIN (b->p2.y, box->p2.y);
+               if (b->p1.x < b->p2.x && b->p1.y < b->p2.y) {
+                   if (i != j)
+                       chunk->base[j] = *b;
+                   j++;
+               }
+           }
+           /* XXX unlink empty chains? */
+           chunk->count = j;
+           out->num_boxes += j;
+       }
+    } else {
+       const struct _cairo_boxes_chunk *chunk;
+
+       _cairo_boxes_clear (out);
+       _cairo_boxes_limit (out, box, 1);
+       for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
+           for (i = 0; i < chunk->count; i++) {
+               status = _cairo_boxes_add (out,
+                                          CAIRO_ANTIALIAS_DEFAULT,
+                                          &chunk->base[i]);
+               if (unlikely (status))
+                   return status;
+           }
+       }
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_status_t
+_cairo_boxes_intersect (const cairo_boxes_t *a,
+                       const cairo_boxes_t *b,
+                       cairo_boxes_t *out)
+{
+    rectangle_t stack_rectangles[CAIRO_STACK_ARRAY_LENGTH (rectangle_t)];
+    rectangle_t *rectangles;
+    rectangle_t *stack_rectangles_ptrs[ARRAY_LENGTH (stack_rectangles) + 1];
+    rectangle_t **rectangles_ptrs;
+    const struct _cairo_boxes_chunk *chunk;
+    cairo_status_t status;
+    int i, j, count;
+
+    if (unlikely (a->num_boxes == 0 || b->num_boxes == 0)) {
+       _cairo_boxes_clear (out);
+       return CAIRO_STATUS_SUCCESS;
+    }
+
+    if (a->num_boxes == 1) {
+       cairo_box_t box = a->chunks.base[0];
+       return _cairo_boxes_intersect_with_box (b, &box, out);
+    }
+    if (b->num_boxes == 1) {
+       cairo_box_t box = b->chunks.base[0];
+       return _cairo_boxes_intersect_with_box (a, &box, out);
+    }
+
+    rectangles = stack_rectangles;
+    rectangles_ptrs = stack_rectangles_ptrs;
+    count = a->num_boxes + b->num_boxes;
+    if (count > ARRAY_LENGTH (stack_rectangles)) {
+       rectangles = _cairo_malloc_ab_plus_c (count,
+                                             sizeof (rectangle_t) +
+                                             sizeof (rectangle_t *),
+                                             sizeof (rectangle_t *));
+       if (unlikely (rectangles == NULL))
+           return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+       rectangles_ptrs = (rectangle_t **) (rectangles + count);
+    }
+
+    j = 0;
+    for (chunk = &a->chunks; chunk != NULL; chunk = chunk->next) {
+       const cairo_box_t *box = chunk->base;
+       for (i = 0; i < chunk->count; i++) {
+           if (box[i].p1.x < box[i].p2.x) {
+               rectangles[j].left.x = box[i].p1.x;
+               rectangles[j].left.dir = 1;
+
+               rectangles[j].right.x = box[i].p2.x;
+               rectangles[j].right.dir = -1;
+           } else {
+               rectangles[j].right.x = box[i].p1.x;
+               rectangles[j].right.dir = 1;
+
+               rectangles[j].left.x = box[i].p2.x;
+               rectangles[j].left.dir = -1;
+           }
+
+           rectangles[j].left.a_or_b = 0;
+           rectangles[j].left.right = NULL;
+           rectangles[j].right.a_or_b = 0;
+           rectangles[j].right.right = NULL;
+
+           rectangles[j].top = box[i].p1.y;
+           rectangles[j].bottom = box[i].p2.y;
+
+           rectangles_ptrs[j] = &rectangles[j];
+           j++;
+       }
+    }
+    for (chunk = &b->chunks; chunk != NULL; chunk = chunk->next) {
+       const cairo_box_t *box = chunk->base;
+       for (i = 0; i < chunk->count; i++) {
+           if (box[i].p1.x < box[i].p2.x) {
+               rectangles[j].left.x = box[i].p1.x;
+               rectangles[j].left.dir = 1;
+
+               rectangles[j].right.x = box[i].p2.x;
+               rectangles[j].right.dir = -1;
+           } else {
+               rectangles[j].right.x = box[i].p1.x;
+               rectangles[j].right.dir = 1;
+
+               rectangles[j].left.x = box[i].p2.x;
+               rectangles[j].left.dir = -1;
+           }
+
+           rectangles[j].left.a_or_b = 1;
+           rectangles[j].left.right = NULL;
+           rectangles[j].right.a_or_b = 1;
+           rectangles[j].right.right = NULL;
+
+           rectangles[j].top = box[i].p1.y;
+           rectangles[j].bottom = box[i].p2.y;
+
+           rectangles_ptrs[j] = &rectangles[j];
+           j++;
+       }
+    }
+    assert (j == count);
+
+    _cairo_boxes_clear (out);
+    status = intersect (rectangles_ptrs, j, out);
+    if (rectangles != stack_rectangles)
+       free (rectangles);
+
+    return status;
+}
diff --git a/src/cairo-boxes-private.h b/src/cairo-boxes-private.h
new file mode 100644 (file)
index 0000000..57d1228
--- /dev/null
@@ -0,0 +1,126 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * Contributor(s):
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#ifndef CAIRO_BOXES_H
+#define CAIRO_BOXES_H
+
+#include "cairo-types-private.h"
+#include "cairo-compiler-private.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+struct _cairo_boxes_t {
+    cairo_status_t status;
+
+    cairo_box_t limit;
+    const cairo_box_t *limits;
+    int num_limits;
+
+    int num_boxes;
+
+    unsigned int is_pixel_aligned;
+
+    struct _cairo_boxes_chunk {
+       struct _cairo_boxes_chunk *next;
+       cairo_box_t *base;
+       int count;
+       int size;
+    } chunks, *tail;
+    cairo_box_t boxes_embedded[32];
+};
+
+cairo_private void
+_cairo_boxes_init (cairo_boxes_t *boxes);
+
+cairo_private void
+_cairo_boxes_init_with_clip (cairo_boxes_t *boxes,
+                            cairo_clip_t *clip);
+
+cairo_private void
+_cairo_boxes_init_for_array (cairo_boxes_t *boxes,
+                            cairo_box_t *array,
+                            int num_boxes);
+
+cairo_private void
+_cairo_boxes_init_from_rectangle (cairo_boxes_t *boxes,
+                                 int x, int y, int w, int h);
+
+cairo_private void
+_cairo_boxes_limit (cairo_boxes_t      *boxes,
+                   const cairo_box_t   *limits,
+                   int                  num_limits);
+
+cairo_private cairo_status_t
+_cairo_boxes_add (cairo_boxes_t *boxes,
+                 cairo_antialias_t antialias,
+                 const cairo_box_t *box);
+
+cairo_private void
+_cairo_boxes_extents (const cairo_boxes_t *boxes,
+                     cairo_box_t *box);
+
+cairo_private cairo_box_t *
+_cairo_boxes_to_array (const cairo_boxes_t *boxes,
+                      int *num_boxes,
+                      cairo_bool_t force_allocation);
+
+static inline void
+_cairo_boxes_free_array (const cairo_boxes_t *boxes,
+                        cairo_box_t *box)
+{
+    if (box != boxes->chunks.base)
+       free(box);
+}
+
+cairo_private cairo_status_t
+_cairo_boxes_intersect (const cairo_boxes_t *a,
+                       const cairo_boxes_t *b,
+                       cairo_boxes_t *out);
+
+cairo_private void
+_cairo_boxes_clear (cairo_boxes_t *boxes);
+
+cairo_private_no_warn cairo_bool_t
+_cairo_boxes_for_each_box (cairo_boxes_t *boxes,
+                          cairo_bool_t (*func) (cairo_box_t *box, void *data),
+                          void *data);
+
+cairo_private void
+_cairo_boxes_fini (cairo_boxes_t *boxes);
+
+cairo_private void
+_cairo_debug_print_boxes (FILE *stream,
+                         const cairo_boxes_t *boxes);
+
+#endif /* CAIRO_BOXES_H */
diff --git a/src/cairo-boxes.c b/src/cairo-boxes.c
new file mode 100644 (file)
index 0000000..182601a
--- /dev/null
@@ -0,0 +1,401 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * Contributor(s):
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-box-private.h"
+#include "cairo-boxes-private.h"
+#include "cairo-error-private.h"
+
+void
+_cairo_boxes_init (cairo_boxes_t *boxes)
+{
+    boxes->status = CAIRO_STATUS_SUCCESS;
+    boxes->num_limits = 0;
+    boxes->num_boxes = 0;
+
+    boxes->tail = &boxes->chunks;
+    boxes->chunks.next = NULL;
+    boxes->chunks.base = boxes->boxes_embedded;
+    boxes->chunks.size = ARRAY_LENGTH (boxes->boxes_embedded);
+    boxes->chunks.count = 0;
+
+    boxes->is_pixel_aligned = TRUE;
+}
+
+void
+_cairo_boxes_init_from_rectangle (cairo_boxes_t *boxes,
+                                 int x, int y, int w, int h)
+{
+    _cairo_boxes_init (boxes);
+
+    _cairo_box_from_integers (&boxes->chunks.base[0], x, y, w, h);
+    boxes->num_boxes = 1;
+}
+
+void
+_cairo_boxes_init_with_clip (cairo_boxes_t *boxes,
+                            cairo_clip_t *clip)
+{
+    _cairo_boxes_init (boxes);
+    if (clip)
+       _cairo_boxes_limit (boxes, clip->boxes, clip->num_boxes);
+}
+
+void
+_cairo_boxes_init_for_array (cairo_boxes_t *boxes,
+                            cairo_box_t *array,
+                            int num_boxes)
+{
+    int n;
+
+    boxes->status = CAIRO_STATUS_SUCCESS;
+    boxes->num_limits = 0;
+    boxes->num_boxes = num_boxes;
+
+    boxes->tail = &boxes->chunks;
+    boxes->chunks.next = NULL;
+    boxes->chunks.base = array;
+    boxes->chunks.size = num_boxes;
+    boxes->chunks.count = num_boxes;
+
+    for (n = 0; n < num_boxes; n++) {
+       if (! _cairo_fixed_is_integer (array[n].p1.x) ||
+           ! _cairo_fixed_is_integer (array[n].p1.y) ||
+           ! _cairo_fixed_is_integer (array[n].p2.x) ||
+           ! _cairo_fixed_is_integer (array[n].p2.y))
+       {
+           break;
+       }
+    }
+
+    boxes->is_pixel_aligned = n == num_boxes;
+}
+
+void
+_cairo_boxes_limit (cairo_boxes_t      *boxes,
+                   const cairo_box_t   *limits,
+                   int                  num_limits)
+{
+    int n;
+
+    boxes->limits = limits;
+    boxes->num_limits = num_limits;
+
+    if (boxes->num_limits) {
+       boxes->limit = limits[0];
+       for (n = 1; n < num_limits; n++) {
+           if (limits[n].p1.x < boxes->limit.p1.x)
+               boxes->limit.p1.x = limits[n].p1.x;
+
+           if (limits[n].p1.y < boxes->limit.p1.y)
+               boxes->limit.p1.y = limits[n].p1.y;
+
+           if (limits[n].p2.x > boxes->limit.p2.x)
+               boxes->limit.p2.x = limits[n].p2.x;
+
+           if (limits[n].p2.y > boxes->limit.p2.y)
+               boxes->limit.p2.y = limits[n].p2.y;
+       }
+    }
+}
+
+static void
+_cairo_boxes_add_internal (cairo_boxes_t *boxes,
+                          const cairo_box_t *box)
+{
+    struct _cairo_boxes_chunk *chunk;
+
+    if (unlikely (boxes->status))
+       return;
+
+    chunk = boxes->tail;
+    if (unlikely (chunk->count == chunk->size)) {
+       int size;
+
+       size = chunk->size * 2;
+       chunk->next = _cairo_malloc_ab_plus_c (size,
+                                              sizeof (cairo_box_t),
+                                              sizeof (struct _cairo_boxes_chunk));
+
+       if (unlikely (chunk->next == NULL)) {
+           boxes->status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+           return;
+       }
+
+       chunk = chunk->next;
+       boxes->tail = chunk;
+
+       chunk->next = NULL;
+       chunk->count = 0;
+       chunk->size = size;
+       chunk->base = (cairo_box_t *) (chunk + 1);
+    }
+
+    chunk->base[chunk->count++] = *box;
+    boxes->num_boxes++;
+
+    if (boxes->is_pixel_aligned)
+       boxes->is_pixel_aligned = _cairo_box_is_pixel_aligned (box);
+}
+
+cairo_status_t
+_cairo_boxes_add (cairo_boxes_t *boxes,
+                 cairo_antialias_t antialias,
+                 const cairo_box_t *box)
+{
+    cairo_box_t b;
+
+    if (antialias == CAIRO_ANTIALIAS_NONE) {
+       b.p1.x = _cairo_fixed_round_down (box->p1.x);
+       b.p1.y = _cairo_fixed_round_down (box->p1.y);
+       b.p2.x = _cairo_fixed_round_down (box->p2.x);
+       b.p2.y = _cairo_fixed_round_down (box->p2.y);
+       box = &b;
+    }
+
+    if (box->p1.y == box->p2.y)
+       return CAIRO_STATUS_SUCCESS;
+
+    if (box->p1.x == box->p2.x)
+       return CAIRO_STATUS_SUCCESS;
+
+    if (boxes->num_limits) {
+       cairo_point_t p1, p2;
+       cairo_bool_t reversed = FALSE;
+       int n;
+
+       /* support counter-clockwise winding for rectangular tessellation */
+       if (box->p1.x < box->p2.x) {
+           p1.x = box->p1.x;
+           p2.x = box->p2.x;
+       } else {
+           p2.x = box->p1.x;
+           p1.x = box->p2.x;
+           reversed = ! reversed;
+       }
+
+       if (p1.x >= boxes->limit.p2.x || p2.x <= boxes->limit.p1.x)
+           return CAIRO_STATUS_SUCCESS;
+
+       if (box->p1.y < box->p2.y) {
+           p1.y = box->p1.y;
+           p2.y = box->p2.y;
+       } else {
+           p2.y = box->p1.y;
+           p1.y = box->p2.y;
+           reversed = ! reversed;
+       }
+
+       if (p1.y >= boxes->limit.p2.y || p2.y <= boxes->limit.p1.y)
+           return CAIRO_STATUS_SUCCESS;
+
+       for (n = 0; n < boxes->num_limits; n++) {
+           const cairo_box_t *limits = &boxes->limits[n];
+           cairo_box_t _box;
+           cairo_point_t _p1, _p2;
+
+           if (p1.x >= limits->p2.x || p2.x <= limits->p1.x)
+               continue;
+           if (p1.y >= limits->p2.y || p2.y <= limits->p1.y)
+               continue;
+
+           /* Otherwise, clip the box to the limits. */
+           _p1 = p1;
+           if (_p1.x < limits->p1.x)
+               _p1.x = limits->p1.x;
+           if (_p1.y < limits->p1.y)
+               _p1.y = limits->p1.y;
+
+           _p2 = p2;
+           if (_p2.x > limits->p2.x)
+               _p2.x = limits->p2.x;
+           if (_p2.y > limits->p2.y)
+               _p2.y = limits->p2.y;
+
+           if (_p2.y <= _p1.y || _p2.x <= _p1.x)
+               continue;
+
+           _box.p1.y = _p1.y;
+           _box.p2.y = _p2.y;
+           if (reversed) {
+               _box.p1.x = _p2.x;
+               _box.p2.x = _p1.x;
+           } else {
+               _box.p1.x = _p1.x;
+               _box.p2.x = _p2.x;
+           }
+
+           _cairo_boxes_add_internal (boxes, &_box);
+       }
+    } else {
+       _cairo_boxes_add_internal (boxes, box);
+    }
+
+    return boxes->status;
+}
+
+void
+_cairo_boxes_extents (const cairo_boxes_t *boxes,
+                     cairo_box_t *box)
+{
+    const struct _cairo_boxes_chunk *chunk;
+    cairo_box_t b;
+    int i;
+
+    if (boxes->num_boxes == 0) {
+       box->p1.x = box->p1.y = box->p2.x = box->p2.y = 0;
+       return;
+    }
+
+    b = boxes->chunks.base[0];
+    for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
+       for (i = 0; i < chunk->count; i++) {
+           if (chunk->base[i].p1.x < b.p1.x)
+               b.p1.x = chunk->base[i].p1.x;
+
+           if (chunk->base[i].p1.y < b.p1.y)
+               b.p1.y = chunk->base[i].p1.y;
+
+           if (chunk->base[i].p2.x > b.p2.x)
+               b.p2.x = chunk->base[i].p2.x;
+
+           if (chunk->base[i].p2.y > b.p2.y)
+               b.p2.y = chunk->base[i].p2.y;
+       }
+    }
+    *box = b;
+}
+
+void
+_cairo_boxes_clear (cairo_boxes_t *boxes)
+{
+    struct _cairo_boxes_chunk *chunk, *next;
+
+    for (chunk = boxes->chunks.next; chunk != NULL; chunk = next) {
+       next = chunk->next;
+       free (chunk);
+    }
+
+    boxes->tail = &boxes->chunks;
+    boxes->chunks.next = 0;
+    boxes->chunks.count = 0;
+    boxes->chunks.base = boxes->boxes_embedded;
+    boxes->chunks.size = ARRAY_LENGTH (boxes->boxes_embedded);
+    boxes->num_boxes = 0;
+
+    boxes->is_pixel_aligned = TRUE;
+}
+
+cairo_box_t *
+_cairo_boxes_to_array (const cairo_boxes_t *boxes,
+                      int *num_boxes,
+                      cairo_bool_t force_allocation)
+{
+    const struct _cairo_boxes_chunk *chunk;
+    cairo_box_t *box;
+    int i, j;
+
+    *num_boxes = boxes->num_boxes;
+    if (boxes->chunks.next == NULL && ! force_allocation)
+           return boxes->chunks.base;
+
+    box = _cairo_malloc_ab (boxes->num_boxes, sizeof (cairo_box_t));
+    if (box == NULL) {
+       _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+       return NULL;
+    }
+
+    j = 0;
+    for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
+       for (i = 0; i < chunk->count; i++)
+           box[j++] = chunk->base[i];
+    }
+
+    return box;
+}
+
+void
+_cairo_boxes_fini (cairo_boxes_t *boxes)
+{
+    struct _cairo_boxes_chunk *chunk, *next;
+
+    for (chunk = boxes->chunks.next; chunk != NULL; chunk = next) {
+       next = chunk->next;
+       free (chunk);
+    }
+}
+
+cairo_bool_t
+_cairo_boxes_for_each_box (cairo_boxes_t *boxes,
+                          cairo_bool_t (*func) (cairo_box_t *box, void *data),
+                          void *data)
+{
+    struct _cairo_boxes_chunk *chunk;
+    int i;
+
+    for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
+       for (i = 0; i < chunk->count; i++)
+           if (! func (&chunk->base[i], data))
+               return FALSE;
+    }
+
+    return TRUE;
+}
+
+
+void
+_cairo_debug_print_boxes (FILE *stream, const cairo_boxes_t *boxes)
+{
+    const struct _cairo_boxes_chunk *chunk;
+    cairo_box_t extents;
+    int i;
+
+    _cairo_boxes_extents (boxes, &extents);
+    fprintf (stream, "boxes x %d: (%f, %f) x (%f, %f)\n",
+            boxes->num_boxes,
+            _cairo_fixed_to_double (extents.p1.x),
+            _cairo_fixed_to_double (extents.p1.y),
+            _cairo_fixed_to_double (extents.p2.x),
+            _cairo_fixed_to_double (extents.p2.y));
+
+    for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
+       for (i = 0; i < chunk->count; i++) {
+           fprintf (stderr, "  box[%d]: (%f, %f), (%f, %f)\n", i,
+                    _cairo_fixed_to_double (chunk->base[i].p1.x),
+                    _cairo_fixed_to_double (chunk->base[i].p1.y),
+                    _cairo_fixed_to_double (chunk->base[i].p2.x),
+                    _cairo_fixed_to_double (chunk->base[i].p2.y));
+       }
+    }
+}
diff --git a/src/cairo-cache-private.h b/src/cairo-cache-private.h
new file mode 100644 (file)
index 0000000..76b5561
--- /dev/null
@@ -0,0 +1,145 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2004 Red Hat, Inc.
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ *      Keith Packard <keithp@keithp.com>
+ *     Graydon Hoare <graydon@redhat.com>
+ *     Carl Worth <cworth@cworth.org>
+ */
+
+#ifndef CAIRO_CACHE_PRIVATE_H
+#define CAIRO_CACHE_PRIVATE_H
+
+#include "cairo-compiler-private.h"
+#include "cairo-types-private.h"
+
+/**
+ * cairo_cache_entry_t:
+ *
+ * A #cairo_cache_entry_t contains both a key and a value for
+ * #cairo_cache_t. User-derived types for #cairo_cache_entry_t must
+ * have a #cairo_cache_entry_t as their first field. For example:
+ *
+ *     typedef _my_entry {
+ *         cairo_cache_entry_t base;
+ *         ... Remainder of key and value fields here ..
+ *     } my_entry_t;
+ *
+ * which then allows a pointer to my_entry_t to be passed to any of
+ * the #cairo_cache_t functions as follows without requiring a cast:
+ *
+ *     _cairo_cache_insert (cache, &my_entry->base, size);
+ *
+ * IMPORTANT: The caller is responsible for initializing
+ * my_entry->base.hash with a hash code derived from the key.  The
+ * essential property of the hash code is that keys_equal must never
+ * return %TRUE for two keys that have different hashes. The best hash
+ * code will reduce the frequency of two keys with the same code for
+ * which keys_equal returns %FALSE.
+ *
+ * The user must also initialize my_entry->base.size to indicate
+ * the size of the current entry. What units to use for size is
+ * entirely up to the caller, (though the same units must be used for
+ * the max_size parameter passed to _cairo_cache_create()). If all
+ * entries are close to the same size, the simplest thing to do is to
+ * just use units of "entries", (eg. set size==1 in all entries and
+ * set max_size to the number of entries which you want to be saved
+ * in the cache).
+ *
+ * Which parts of the entry make up the "key" and which part make up
+ * the value are entirely up to the caller, (as determined by the
+ * computation going into base.hash as well as the keys_equal
+ * function). A few of the #cairo_cache_t functions accept an entry which
+ * will be used exclusively as a "key", (indicated by a parameter name
+ * of key). In these cases, the value-related fields of the entry need
+ * not be initialized if so desired.
+ **/
+typedef struct _cairo_cache_entry {
+    unsigned long hash;
+    unsigned long size;
+} cairo_cache_entry_t;
+
+typedef cairo_bool_t (*cairo_cache_predicate_func_t) (const void *entry);
+
+struct _cairo_cache {
+    cairo_hash_table_t *hash_table;
+
+    cairo_cache_predicate_func_t predicate;
+    cairo_destroy_func_t entry_destroy;
+
+    unsigned long max_size;
+    unsigned long size;
+
+    int freeze_count;
+};
+
+typedef cairo_bool_t
+(*cairo_cache_keys_equal_func_t) (const void *key_a, const void *key_b);
+
+typedef void
+(*cairo_cache_callback_func_t) (void *entry,
+                               void *closure);
+
+cairo_private cairo_status_t
+_cairo_cache_init (cairo_cache_t *cache,
+                  cairo_cache_keys_equal_func_t keys_equal,
+                  cairo_cache_predicate_func_t  predicate,
+                  cairo_destroy_func_t    entry_destroy,
+                  unsigned long                   max_size);
+
+cairo_private void
+_cairo_cache_fini (cairo_cache_t *cache);
+
+cairo_private void
+_cairo_cache_freeze (cairo_cache_t *cache);
+
+cairo_private void
+_cairo_cache_thaw (cairo_cache_t *cache);
+
+cairo_private void *
+_cairo_cache_lookup (cairo_cache_t       *cache,
+                    cairo_cache_entry_t  *key);
+
+cairo_private cairo_status_t
+_cairo_cache_insert (cairo_cache_t      *cache,
+                    cairo_cache_entry_t *entry);
+
+cairo_private void
+_cairo_cache_remove (cairo_cache_t      *cache,
+                    cairo_cache_entry_t *entry);
+
+cairo_private void
+_cairo_cache_foreach (cairo_cache_t             *cache,
+                     cairo_cache_callback_func_t cache_callback,
+                     void                       *closure);
+
+#endif
diff --git a/src/cairo-cache.c b/src/cairo-cache.c
new file mode 100644 (file)
index 0000000..5c4e4ca
--- /dev/null
@@ -0,0 +1,338 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2004 Red Hat, Inc.
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ *      Keith Packard <keithp@keithp.com>
+ *     Graydon Hoare <graydon@redhat.com>
+ *     Carl Worth <cworth@cworth.org>
+ */
+
+#include "cairoint.h"
+#include "cairo-error-private.h"
+
+static void
+_cairo_cache_shrink_to_accommodate (cairo_cache_t *cache,
+                                   unsigned long  additional);
+
+static cairo_bool_t
+_cairo_cache_entry_is_non_zero (const void *entry)
+{
+    return ((const cairo_cache_entry_t *) entry)->size;
+}
+
+
+/**
+ * _cairo_cache_init:
+ * @cache: the #cairo_cache_t to initialise
+ * @keys_equal: a function to return %TRUE if two keys are equal
+ * @entry_destroy: destroy notifier for cache entries
+ * @max_size: the maximum size for this cache
+ * Returns: the newly created #cairo_cache_t
+ *
+ * Creates a new cache using the keys_equal() function to determine
+ * the equality of entries.
+ *
+ * Data is provided to the cache in the form of user-derived version
+ * of #cairo_cache_entry_t. A cache entry must be able to hold hash
+ * code, a size, and the key/value pair being stored in the
+ * cache. Sometimes only the key will be necessary, (as in
+ * _cairo_cache_lookup()), and in these cases the value portion of the
+ * entry need not be initialized.
+ *
+ * The units for max_size can be chosen by the caller, but should be
+ * consistent with the units of the size field of cache entries. When
+ * adding an entry with _cairo_cache_insert() if the total size of
+ * entries in the cache would exceed max_size then entries will be
+ * removed at random until the new entry would fit or the cache is
+ * empty. Then the new entry is inserted.
+ *
+ * There are cases in which the automatic removal of entries is
+ * undesired. If the cache entries have reference counts, then it is a
+ * simple matter to use the reference counts to ensure that entries
+ * continue to live even after being ejected from the cache. However,
+ * in some cases the memory overhead of adding a reference count to
+ * the entry would be objectionable. In such cases, the
+ * _cairo_cache_freeze() and _cairo_cache_thaw() calls can be
+ * used to establish a window during which no automatic removal of
+ * entries will occur.
+ **/
+cairo_status_t
+_cairo_cache_init (cairo_cache_t               *cache,
+                  cairo_cache_keys_equal_func_t keys_equal,
+                  cairo_cache_predicate_func_t  predicate,
+                  cairo_destroy_func_t          entry_destroy,
+                  unsigned long                 max_size)
+{
+    cache->hash_table = _cairo_hash_table_create (keys_equal);
+    if (unlikely (cache->hash_table == NULL))
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    if (predicate == NULL)
+       predicate = _cairo_cache_entry_is_non_zero;
+    cache->predicate = predicate;
+    cache->entry_destroy = entry_destroy;
+
+    cache->max_size = max_size;
+    cache->size = 0;
+
+    cache->freeze_count = 0;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_cache_pluck (void *entry, void *closure)
+{
+    _cairo_cache_remove (closure, entry);
+}
+
+/**
+ * _cairo_cache_fini:
+ * @cache: a cache to destroy
+ *
+ * Immediately destroys the given cache, freeing all resources
+ * associated with it. As part of this process, the entry_destroy()
+ * function, (as passed to _cairo_cache_init()), will be called for
+ * each entry in the cache.
+ **/
+void
+_cairo_cache_fini (cairo_cache_t *cache)
+{
+    _cairo_hash_table_foreach (cache->hash_table,
+                              _cairo_cache_pluck,
+                              cache);
+    assert (cache->size == 0);
+    _cairo_hash_table_destroy (cache->hash_table);
+}
+
+/**
+ * _cairo_cache_freeze:
+ * @cache: a cache with some precious entries in it (or about to be
+ * added)
+ *
+ * Disable the automatic ejection of entries from the cache. For as
+ * long as the cache is "frozen", calls to _cairo_cache_insert() will
+ * add new entries to the cache regardless of how large the cache
+ * grows. See _cairo_cache_thaw().
+ *
+ * Note: Multiple calls to _cairo_cache_freeze() will stack, in that
+ * the cache will remain "frozen" until a corresponding number of
+ * calls are made to _cairo_cache_thaw().
+ **/
+void
+_cairo_cache_freeze (cairo_cache_t *cache)
+{
+    assert (cache->freeze_count >= 0);
+
+    cache->freeze_count++;
+}
+
+/**
+ * _cairo_cache_thaw:
+ * @cache: a cache, just after the entries in it have become less
+ * precious
+ *
+ * Cancels the effects of _cairo_cache_freeze().
+ *
+ * When a number of calls to _cairo_cache_thaw() is made corresponding
+ * to the number of calls to _cairo_cache_freeze() the cache will no
+ * longer be "frozen". If the cache had grown larger than max_size
+ * while frozen, entries will immediately be ejected (by random) from
+ * the cache until the cache is smaller than max_size. Also, the
+ * automatic ejection of entries on _cairo_cache_insert() will resume.
+ **/
+void
+_cairo_cache_thaw (cairo_cache_t *cache)
+{
+    assert (cache->freeze_count > 0);
+
+    if (--cache->freeze_count == 0)
+       _cairo_cache_shrink_to_accommodate (cache, 0);
+}
+
+/**
+ * _cairo_cache_lookup:
+ * @cache: a cache
+ * @key: the key of interest
+ * @entry_return: pointer for return value
+ *
+ * Performs a lookup in @cache looking for an entry which has a key
+ * that matches @key, (as determined by the keys_equal() function
+ * passed to _cairo_cache_init()).
+ *
+ * Return value: %TRUE if there is an entry in the cache that matches
+ * @key, (which will now be in *entry_return). %FALSE otherwise, (in
+ * which case *entry_return will be %NULL).
+ **/
+void *
+_cairo_cache_lookup (cairo_cache_t       *cache,
+                    cairo_cache_entry_t  *key)
+{
+    return _cairo_hash_table_lookup (cache->hash_table,
+                                    (cairo_hash_entry_t *) key);
+}
+
+/**
+ * _cairo_cache_remove_random:
+ * @cache: a cache
+ *
+ * Remove a random entry from the cache.
+ *
+ * Return value: %TRUE if an entry was successfully removed.
+ * %FALSE if there are no entries that can be removed.
+ **/
+static cairo_bool_t
+_cairo_cache_remove_random (cairo_cache_t *cache)
+{
+    cairo_cache_entry_t *entry;
+
+    entry = _cairo_hash_table_random_entry (cache->hash_table,
+                                           cache->predicate);
+    if (unlikely (entry == NULL))
+       return FALSE;
+
+    _cairo_cache_remove (cache, entry);
+
+    return TRUE;
+}
+
+/**
+ * _cairo_cache_shrink_to_accommodate:
+ * @cache: a cache
+ * @additional: additional size requested in bytes
+ *
+ * If cache is not frozen, eject entries randomly until the size of
+ * the cache is at least @additional bytes less than
+ * cache->max_size. That is, make enough room to accommodate a new
+ * entry of size @additional.
+ **/
+static void
+_cairo_cache_shrink_to_accommodate (cairo_cache_t *cache,
+                                   unsigned long  additional)
+{
+    while (cache->size + additional > cache->max_size) {
+       if (! _cairo_cache_remove_random (cache))
+           return;
+    }
+}
+
+/**
+ * _cairo_cache_insert:
+ * @cache: a cache
+ * @entry: an entry to be inserted
+ *
+ * Insert @entry into the cache. If an entry exists in the cache with
+ * a matching key, then the old entry will be removed first, (and the
+ * entry_destroy() callback will be called on it).
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS if successful or
+ * %CAIRO_STATUS_NO_MEMORY if insufficient memory is available.
+ **/
+cairo_status_t
+_cairo_cache_insert (cairo_cache_t      *cache,
+                    cairo_cache_entry_t *entry)
+{
+    cairo_status_t status;
+
+    if (entry->size && ! cache->freeze_count)
+       _cairo_cache_shrink_to_accommodate (cache, entry->size);
+
+    status = _cairo_hash_table_insert (cache->hash_table,
+                                      (cairo_hash_entry_t *) entry);
+    if (unlikely (status))
+       return status;
+
+    cache->size += entry->size;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+/**
+ * _cairo_cache_remove:
+ * @cache: a cache
+ * @entry: an entry that exists in the cache
+ *
+ * Remove an existing entry from the cache.
+ **/
+void
+_cairo_cache_remove (cairo_cache_t      *cache,
+                    cairo_cache_entry_t *entry)
+{
+    cache->size -= entry->size;
+
+    _cairo_hash_table_remove (cache->hash_table,
+                             (cairo_hash_entry_t *) entry);
+
+    if (cache->entry_destroy)
+       cache->entry_destroy (entry);
+}
+
+/**
+ * _cairo_cache_foreach:
+ * @cache: a cache
+ * @cache_callback: function to be called for each entry
+ * @closure: additional argument to be passed to @cache_callback
+ *
+ * Call @cache_callback for each entry in the cache, in a
+ * non-specified order.
+ **/
+void
+_cairo_cache_foreach (cairo_cache_t                  *cache,
+                     cairo_cache_callback_func_t      cache_callback,
+                     void                            *closure)
+{
+    _cairo_hash_table_foreach (cache->hash_table,
+                              cache_callback,
+                              closure);
+}
+
+unsigned long
+_cairo_hash_string (const char *c)
+{
+    /* This is the djb2 hash. */
+    unsigned long hash = _CAIRO_HASH_INIT_VALUE;
+    while (c && *c)
+       hash = ((hash << 5) + hash) + *c++;
+    return hash;
+}
+
+unsigned long
+_cairo_hash_bytes (unsigned long hash,
+                  const void *ptr,
+                  unsigned int length)
+{
+    const uint8_t *bytes = ptr;
+    /* This is the djb2 hash. */
+    while (length--)
+       hash = ((hash << 5) + hash) + *bytes++;
+    return hash;
+}
diff --git a/src/cairo-cff-subset.c b/src/cairo-cff-subset.c
new file mode 100644 (file)
index 0000000..db6fdf7
--- /dev/null
@@ -0,0 +1,3408 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2006 Adrian Johnson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Adrian Johnson.
+ *
+ * Contributor(s):
+ *     Adrian Johnson <ajohnson@redneon.com>
+ *      Eugeniy Meshcheryakov <eugen@debian.org>
+ */
+
+/*
+ * Useful links:
+ * http://www.adobe.com/content/dam/Adobe/en/devnet/font/pdfs/5176.CFF.pdf
+ * http://www.adobe.com/content/dam/Adobe/en/devnet/font/pdfs/5177.Type2.pdf
+ */
+
+#define _BSD_SOURCE /* for snprintf(), strdup() */
+#include "cairoint.h"
+
+#include "cairo-array-private.h"
+#include "cairo-error-private.h"
+
+#if CAIRO_HAS_FONT_SUBSET
+
+#include "cairo-scaled-font-subsets-private.h"
+#include "cairo-truetype-subset-private.h"
+#include <string.h>
+
+/* CFF Dict Operators. If the high byte is 0 the command is encoded
+ * with a single byte. */
+#define BASEFONTNAME_OP  0x0c16
+#define CIDCOUNT_OP      0x0c22
+#define CHARSET_OP       0x000f
+#define CHARSTRINGS_OP   0x0011
+#define COPYRIGHT_OP     0x0c00
+#define DEFAULTWIDTH_OP  0x0014
+#define ENCODING_OP      0x0010
+#define FAMILYNAME_OP    0x0003
+#define FDARRAY_OP       0x0c24
+#define FDSELECT_OP      0x0c25
+#define FONTBBOX_OP      0x0005
+#define FONTMATRIX_OP    0x0c07
+#define FONTNAME_OP      0x0c26
+#define FULLNAME_OP      0x0002
+#define LOCAL_SUB_OP     0x0013
+#define NOMINALWIDTH_OP  0x0015
+#define NOTICE_OP        0x0001
+#define POSTSCRIPT_OP    0x0c15
+#define PRIVATE_OP       0x0012
+#define ROS_OP           0x0c1e
+#define UNIQUEID_OP      0x000d
+#define VERSION_OP       0x0000
+#define WEIGHT_OP        0x0004
+#define XUID_OP          0x000e
+
+#define NUM_STD_STRINGS 391
+
+/* Type 2 Charstring operators */
+#define TYPE2_hstem     0x0001
+#define TYPE2_vstem     0x0003
+#define TYPE2_callsubr  0x000a
+
+#define TYPE2_return    0x000b
+#define TYPE2_endchar   0x000e
+
+#define TYPE2_hstemhm   0x0012
+#define TYPE2_hintmask  0x0013
+#define TYPE2_cntrmask  0x0014
+#define TYPE2_vstemhm   0x0017
+#define TYPE2_callgsubr 0x001d
+
+#define TYPE2_rmoveto   0x0015
+#define TYPE2_hmoveto   0x0016
+#define TYPE2_vmoveto   0x0004
+
+
+#define MAX_SUBROUTINE_NESTING 10 /* From Type2 Charstring spec */
+
+
+typedef struct _cff_header {
+    uint8_t major;
+    uint8_t minor;
+    uint8_t header_size;
+    uint8_t offset_size;
+} cff_header_t;
+
+typedef struct _cff_index_element {
+    cairo_bool_t   is_copy;
+    unsigned char *data;
+    int            length;
+} cff_index_element_t;
+
+typedef struct _cff_dict_operator {
+    cairo_hash_entry_t base;
+
+    unsigned short operator;
+    unsigned char *operand;
+    int            operand_length;
+    int            operand_offset;
+} cff_dict_operator_t;
+
+typedef struct _cairo_cff_font {
+
+    cairo_scaled_font_subset_t *scaled_font_subset;
+    const cairo_scaled_font_backend_t *backend;
+
+    /* Font Data */
+    unsigned char       *data;
+    unsigned long        data_length;
+    unsigned char       *current_ptr;
+    unsigned char       *data_end;
+    cff_header_t        *header;
+    char                *font_name;
+    char                *ps_name;
+    cairo_hash_table_t  *top_dict;
+    cairo_hash_table_t  *private_dict;
+    cairo_array_t        strings_index;
+    cairo_array_t        charstrings_index;
+    cairo_array_t        global_sub_index;
+    cairo_array_t        local_sub_index;
+    unsigned char       *charset;
+    int                  num_glyphs;
+    cairo_bool_t         is_cid;
+    cairo_bool_t         is_opentype;
+    int                 units_per_em;
+    int                 global_sub_bias;
+    int                         local_sub_bias;
+    int                  default_width;
+    int                  nominal_width;
+
+    /* CID Font Data */
+    int                 *fdselect;
+    unsigned int         num_fontdicts;
+    cairo_hash_table_t **fd_dict;
+    cairo_hash_table_t **fd_private_dict;
+    cairo_array_t       *fd_local_sub_index;
+    int                        *fd_local_sub_bias;
+    int                 *fd_default_width;
+    int                 *fd_nominal_width;
+
+    /* Subsetted Font Data */
+    char                *subset_font_name;
+    cairo_array_t        charstrings_subset_index;
+    cairo_array_t        strings_subset_index;
+    int                         euro_sid;
+    int                 *fdselect_subset;
+    unsigned int         num_subset_fontdicts;
+    int                 *fd_subset_map;
+    int                 *private_dict_offset;
+    cairo_bool_t         subset_subroutines;
+    cairo_bool_t       *global_subs_used;
+    cairo_bool_t       *local_subs_used;
+    cairo_bool_t       **fd_local_subs_used;
+    cairo_array_t        output;
+
+    /* Subset Metrics */
+    int                 *widths;
+    int                  x_min, y_min, x_max, y_max;
+    int                  ascent, descent;
+
+    /* Type 2 charstring data */
+    int                 type2_stack_size;
+    int                 type2_stack_top_value;
+    cairo_bool_t        type2_stack_top_is_int;
+    int                 type2_num_hints;
+    int                 type2_hintmask_bytes;
+    int                  type2_nesting_level;
+    cairo_bool_t         type2_seen_first_int;
+    cairo_bool_t         type2_find_width;
+    cairo_bool_t         type2_found_width;
+    int                  type2_width;
+    cairo_bool_t         type2_has_path;
+
+} cairo_cff_font_t;
+
+/* Encoded integer using maximum sized encoding. This is required for
+ * operands that are later modified after encoding. */
+static unsigned char *
+encode_integer_max (unsigned char *p, int i)
+{
+    *p++ = 29;
+    *p++ = i >> 24;
+    *p++ = (i >> 16) & 0xff;
+    *p++ = (i >> 8)  & 0xff;
+    *p++ = i & 0xff;
+    return p;
+}
+
+static unsigned char *
+encode_integer (unsigned char *p, int i)
+{
+    if (i >= -107 && i <= 107) {
+        *p++ = i + 139;
+    } else if (i >= 108 && i <= 1131) {
+        i -= 108;
+        *p++ = (i >> 8)+ 247;
+        *p++ = i & 0xff;
+    } else if (i >= -1131 && i <= -108) {
+        i = -i - 108;
+        *p++ = (i >> 8)+ 251;
+        *p++ = i & 0xff;
+    } else if (i >= -32768 && i <= 32767) {
+        *p++ = 28;
+        *p++ = (i >> 8)  & 0xff;
+        *p++ = i & 0xff;
+    } else {
+        p = encode_integer_max (p, i);
+    }
+    return p;
+}
+
+static unsigned char *
+decode_integer (unsigned char *p, int *integer)
+{
+    if (*p == 28) {
+        *integer = (int)(p[1]<<8 | p[2]);
+        p += 3;
+    } else if (*p == 29) {
+        *integer = (int)((p[1] << 24) | (p[2] << 16) | (p[3] << 8) | p[4]);
+        p += 5;
+    } else if (*p >= 32 && *p <= 246) {
+        *integer = *p++ - 139;
+    } else if (*p <= 250) {
+        *integer = (p[0] - 247) * 256 + p[1] + 108;
+        p += 2;
+    } else if (*p <= 254) {
+        *integer = -(p[0] - 251) * 256 - p[1] - 108;
+        p += 2;
+    } else {
+        *integer = 0;
+        p += 1;
+    }
+    return p;
+}
+
+static char *
+decode_nibble (int n, char *buf)
+{
+    switch (n)
+    {
+    case 0xa:
+       *buf++ = '.';
+       break;
+    case 0xb:
+       *buf++ = 'E';
+       break;
+    case 0xc:
+       *buf++ = 'E';
+       *buf++ = '-';
+       break;
+    case 0xd:
+       *buf++ = '-';
+       break;
+    case 0xe:
+       *buf++ = '-';
+       break;
+    case 0xf:
+       break;
+    default:
+       *buf++ = '0' + n;
+       break;
+    }
+
+    return buf;
+}
+
+static unsigned char *
+decode_real (unsigned char *p, double *real)
+{
+    int n;
+    char buffer[100];
+    char *buf = buffer;
+    char *buf_end = buffer + sizeof (buf);
+
+    p++;
+    while (buf + 2 < buf_end) {
+       n = *p >> 4;
+       buf = decode_nibble (n, buf);
+       n = *p & 0x0f;
+       buf = decode_nibble (n, buf);
+        if ((*p & 0x0f) == 0x0f) {
+           p++;
+            break;
+       }
+       p++;
+    };
+    *buf = 0;
+
+    if (sscanf(buffer, "%lf", real) != 1)
+        *real = 0.0;
+
+    return p;
+}
+
+static unsigned char *
+decode_number (unsigned char *p, double *number)
+{
+    if (*p == 30) {
+        p = decode_real (p, number);
+    } else {
+        int i;
+        p = decode_integer (p, &i);
+        *number = i;
+    }
+    return p;
+}
+
+static unsigned char *
+decode_operator (unsigned char *p, unsigned short *operator)
+{
+    unsigned short op = 0;
+
+    op = *p++;
+    if (op == 12) {
+        op <<= 8;
+        op |= *p++;
+    }
+    *operator = op;
+    return p;
+}
+
+/* return 0 if not an operand */
+static int
+operand_length (unsigned char *p)
+{
+    unsigned char *begin = p;
+
+    if (*p == 28)
+        return 3;
+
+    if (*p == 29)
+        return 5;
+
+    if (*p >= 32 && *p <= 246)
+        return 1;
+
+    if (*p >= 247 && *p <= 254)
+        return 2;
+
+    if (*p == 30) {
+        while ((*p & 0x0f) != 0x0f)
+            p++;
+        return p - begin + 1;
+    }
+
+    return 0;
+}
+
+static unsigned char *
+encode_index_offset (unsigned char *p, int offset_size, unsigned long offset)
+{
+    while (--offset_size >= 0) {
+        p[offset_size] = (unsigned char) (offset & 0xff);
+        offset >>= 8;
+    }
+    return p + offset_size;
+}
+
+static unsigned long
+decode_index_offset(unsigned char *p, int off_size)
+{
+    unsigned long offset = 0;
+
+    while (off_size-- > 0)
+        offset = offset*256 + *p++;
+    return offset;
+}
+
+static void
+cff_index_init (cairo_array_t *index)
+{
+    _cairo_array_init (index, sizeof (cff_index_element_t));
+}
+
+static cairo_int_status_t
+cff_index_read (cairo_array_t *index, unsigned char **ptr, unsigned char *end_ptr)
+{
+    cff_index_element_t element;
+    unsigned char *data, *p;
+    cairo_status_t status;
+    int offset_size, count, start, i;
+    int end = 0;
+
+    p = *ptr;
+    if (p + 2 > end_ptr)
+        return CAIRO_INT_STATUS_UNSUPPORTED;
+    count = be16_to_cpu( *((uint16_t *)p) );
+    p += 2;
+    if (count > 0) {
+        offset_size = *p++;
+        if (p + (count + 1)*offset_size > end_ptr)
+            return CAIRO_INT_STATUS_UNSUPPORTED;
+        data = p + offset_size*(count + 1) - 1;
+        start = decode_index_offset (p, offset_size);
+        p += offset_size;
+        for (i = 0; i < count; i++) {
+            end = decode_index_offset (p, offset_size);
+            p += offset_size;
+            if (p > end_ptr)
+                return CAIRO_INT_STATUS_UNSUPPORTED;
+            element.length = end - start;
+            element.is_copy = FALSE;
+            element.data = data + start;
+            status = _cairo_array_append (index, &element);
+            if (unlikely (status))
+                return status;
+            start = end;
+        }
+        p = data + end;
+    }
+    *ptr = p;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+cff_index_write (cairo_array_t *index, cairo_array_t *output)
+{
+    int offset_size;
+    int offset;
+    int num_elem;
+    int i;
+    cff_index_element_t *element;
+    uint16_t count;
+    unsigned char buf[5];
+    cairo_status_t status;
+
+    num_elem = _cairo_array_num_elements (index);
+    count = cpu_to_be16 ((uint16_t) num_elem);
+    status = _cairo_array_append_multiple (output, &count, 2);
+    if (unlikely (status))
+        return status;
+
+    if (num_elem == 0)
+        return CAIRO_STATUS_SUCCESS;
+
+    /* Find maximum offset to determine offset size */
+    offset = 1;
+    for (i = 0; i < num_elem; i++) {
+        element = _cairo_array_index (index, i);
+        offset += element->length;
+    }
+    if (offset < 0x100)
+        offset_size = 1;
+    else if (offset < 0x10000)
+        offset_size = 2;
+    else if (offset < 0x1000000)
+        offset_size = 3;
+    else
+        offset_size = 4;
+
+    buf[0] = (unsigned char) offset_size;
+    status = _cairo_array_append (output, buf);
+    if (unlikely (status))
+        return status;
+
+    offset = 1;
+    encode_index_offset (buf, offset_size, offset);
+    status = _cairo_array_append_multiple (output, buf, offset_size);
+    if (unlikely (status))
+        return status;
+
+    for (i = 0; i < num_elem; i++) {
+        element = _cairo_array_index (index, i);
+        offset += element->length;
+        encode_index_offset (buf, offset_size, offset);
+        status = _cairo_array_append_multiple (output, buf, offset_size);
+        if (unlikely (status))
+            return status;
+    }
+
+    for (i = 0; i < num_elem; i++) {
+        element = _cairo_array_index (index, i);
+        if (element->length > 0) {
+            status = _cairo_array_append_multiple (output,
+                                                   element->data,
+                                                   element->length);
+        }
+        if (unlikely (status))
+            return status;
+    }
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+cff_index_set_object (cairo_array_t *index, int obj_index,
+                      unsigned char *object , int length)
+{
+    cff_index_element_t *element;
+
+    element = _cairo_array_index (index, obj_index);
+    if (element->is_copy)
+        free (element->data);
+
+    element->data = object;
+    element->length = length;
+    element->is_copy = FALSE;
+}
+
+static cairo_status_t
+cff_index_append (cairo_array_t *index, unsigned char *object , int length)
+{
+    cff_index_element_t element;
+
+    element.length = length;
+    element.is_copy = FALSE;
+    element.data = object;
+
+    return _cairo_array_append (index, &element);
+}
+
+static cairo_status_t
+cff_index_append_copy (cairo_array_t *index,
+                       const unsigned char *object,
+                       unsigned int length)
+{
+    cff_index_element_t element;
+    cairo_status_t status;
+
+    element.length = length;
+    element.is_copy = TRUE;
+    element.data = malloc (element.length);
+    if (unlikely (element.data == NULL))
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    memcpy (element.data, object, element.length);
+
+    status = _cairo_array_append (index, &element);
+    if (unlikely (status)) {
+       free (element.data);
+       return status;
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+cff_index_fini (cairo_array_t *index)
+{
+    cff_index_element_t *element;
+    unsigned int i;
+
+    for (i = 0; i < _cairo_array_num_elements (index); i++) {
+        element = _cairo_array_index (index, i);
+        if (element->is_copy && element->data)
+            free (element->data);
+    }
+    _cairo_array_fini (index);
+}
+
+static cairo_bool_t
+_cairo_cff_dict_equal (const void *key_a, const void *key_b)
+{
+    const cff_dict_operator_t *op_a = key_a;
+    const cff_dict_operator_t *op_b = key_b;
+
+    return op_a->operator == op_b->operator;
+}
+
+static cairo_status_t
+cff_dict_init (cairo_hash_table_t **dict)
+{
+    *dict = _cairo_hash_table_create (_cairo_cff_dict_equal);
+    if (unlikely (*dict == NULL))
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_dict_init_key (cff_dict_operator_t *key, int operator)
+{
+    key->base.hash = (unsigned long) operator;
+    key->operator = operator;
+}
+
+static cairo_status_t
+cff_dict_create_operator (int            operator,
+                          unsigned char *operand,
+                          int            size,
+                         cff_dict_operator_t **out)
+{
+    cff_dict_operator_t *op;
+
+    op = malloc (sizeof (cff_dict_operator_t));
+    if (unlikely (op == NULL))
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    _cairo_dict_init_key (op, operator);
+    op->operand = malloc (size);
+    if (unlikely (op->operand == NULL)) {
+        free (op);
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+    }
+
+    memcpy (op->operand, operand, size);
+    op->operand_length = size;
+    op->operand_offset = -1;
+
+    *out = op;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+cff_dict_read (cairo_hash_table_t *dict, unsigned char *p, int dict_size)
+{
+    unsigned char *end;
+    cairo_array_t operands;
+    cff_dict_operator_t *op;
+    unsigned short operator;
+    cairo_status_t status = CAIRO_STATUS_SUCCESS;
+    int size;
+
+    end = p + dict_size;
+    _cairo_array_init (&operands, 1);
+    while (p < end) {
+        size = operand_length (p);
+        if (size != 0) {
+            status = _cairo_array_append_multiple (&operands, p, size);
+            if (unlikely (status))
+                goto fail;
+
+            p += size;
+        } else {
+            p = decode_operator (p, &operator);
+            status = cff_dict_create_operator (operator,
+                                          _cairo_array_index (&operands, 0),
+                                          _cairo_array_num_elements (&operands),
+                                         &op);
+            if (unlikely (status))
+                goto fail;
+
+            status = _cairo_hash_table_insert (dict, &op->base);
+            if (unlikely (status))
+                goto fail;
+
+            _cairo_array_truncate (&operands, 0);
+        }
+    }
+
+fail:
+    _cairo_array_fini (&operands);
+
+    return status;
+}
+
+static void
+cff_dict_remove (cairo_hash_table_t *dict, unsigned short operator)
+{
+    cff_dict_operator_t key, *op;
+
+    _cairo_dict_init_key (&key, operator);
+    op = _cairo_hash_table_lookup (dict, &key.base);
+    if (op != NULL) {
+        free (op->operand);
+        _cairo_hash_table_remove (dict, (cairo_hash_entry_t *) op);
+        free (op);
+    }
+}
+
+static unsigned char *
+cff_dict_get_operands (cairo_hash_table_t *dict,
+                       unsigned short      operator,
+                       int                *size)
+{
+    cff_dict_operator_t key, *op;
+
+    _cairo_dict_init_key (&key, operator);
+    op = _cairo_hash_table_lookup (dict, &key.base);
+    if (op != NULL) {
+        *size = op->operand_length;
+        return op->operand;
+    }
+
+    return NULL;
+}
+
+static cairo_status_t
+cff_dict_set_operands (cairo_hash_table_t *dict,
+                       unsigned short      operator,
+                       unsigned char      *operand,
+                       int                 size)
+{
+    cff_dict_operator_t key, *op;
+    cairo_status_t status;
+
+    _cairo_dict_init_key (&key, operator);
+    op = _cairo_hash_table_lookup (dict, &key.base);
+    if (op != NULL) {
+        free (op->operand);
+        op->operand = malloc (size);
+       if (unlikely (op->operand == NULL))
+           return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+        memcpy (op->operand, operand, size);
+        op->operand_length = size;
+    }
+    else
+    {
+        status = cff_dict_create_operator (operator, operand, size, &op);
+        if (unlikely (status))
+           return status;
+
+       status = _cairo_hash_table_insert (dict, &op->base);
+       if (unlikely (status))
+           return status;
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static int
+cff_dict_get_location (cairo_hash_table_t *dict,
+                       unsigned short      operator,
+                       int                *size)
+{
+    cff_dict_operator_t key, *op;
+
+    _cairo_dict_init_key (&key, operator);
+    op = _cairo_hash_table_lookup (dict, &key.base);
+    if (op != NULL) {
+        *size = op->operand_length;
+        return op->operand_offset;
+    }
+
+    return -1;
+}
+
+typedef struct _dict_write_info {
+    cairo_array_t *output;
+    cairo_status_t status;
+} dict_write_info_t;
+
+static void
+cairo_dict_write_operator (cff_dict_operator_t *op, dict_write_info_t *write_info)
+{
+    unsigned char data;
+
+    op->operand_offset = _cairo_array_num_elements (write_info->output);
+    write_info->status = _cairo_array_append_multiple (write_info->output, op->operand, op->operand_length);
+    if (write_info->status)
+        return;
+
+    if (op->operator & 0xff00) {
+        data = op->operator >> 8;
+        write_info->status = _cairo_array_append (write_info->output, &data);
+        if (write_info->status)
+            return;
+    }
+    data = op->operator & 0xff;
+    write_info->status = _cairo_array_append (write_info->output, &data);
+}
+
+static void
+_cairo_dict_collect (void *entry, void *closure)
+{
+    dict_write_info_t   *write_info = closure;
+    cff_dict_operator_t *op = entry;
+
+    if (write_info->status)
+        return;
+
+    /* The ROS operator is handled separately in cff_dict_write() */
+    if (op->operator != ROS_OP)
+        cairo_dict_write_operator (op, write_info);
+}
+
+static cairo_status_t
+cff_dict_write (cairo_hash_table_t *dict, cairo_array_t *output)
+{
+    dict_write_info_t write_info;
+    cff_dict_operator_t key, *op;
+
+    write_info.output = output;
+    write_info.status = CAIRO_STATUS_SUCCESS;
+
+    /* The CFF specification requires that the Top Dict of CID fonts
+     * begin with the ROS operator. */
+    _cairo_dict_init_key (&key, ROS_OP);
+    op = _cairo_hash_table_lookup (dict, &key.base);
+    if (op != NULL)
+        cairo_dict_write_operator (op, &write_info);
+
+    _cairo_hash_table_foreach (dict, _cairo_dict_collect, &write_info);
+
+    return write_info.status;
+}
+
+static void
+_cff_dict_entry_pluck (void *_entry, void *dict)
+{
+    cff_dict_operator_t *entry = _entry;
+
+    _cairo_hash_table_remove (dict, &entry->base);
+    free (entry->operand);
+    free (entry);
+}
+
+static void
+cff_dict_fini (cairo_hash_table_t *dict)
+{
+    _cairo_hash_table_foreach (dict, _cff_dict_entry_pluck, dict);
+    _cairo_hash_table_destroy (dict);
+}
+
+static cairo_int_status_t
+cairo_cff_font_read_header (cairo_cff_font_t *font)
+{
+    if (font->data_length < sizeof (cff_header_t))
+        return CAIRO_INT_STATUS_UNSUPPORTED;
+
+
+    font->header = (cff_header_t *) font->data;
+    font->current_ptr = font->data + font->header->header_size;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+cairo_cff_font_read_name (cairo_cff_font_t *font)
+{
+    cairo_array_t index;
+    cairo_int_status_t status;
+    cff_index_element_t *element;
+    unsigned char *p;
+    int i, len;
+
+    cff_index_init (&index);
+    status = cff_index_read (&index, &font->current_ptr, font->data_end);
+    if (!font->is_opentype) {
+        element = _cairo_array_index (&index, 0);
+       p = element->data;
+       len = element->length;
+
+       /* If font name is prefixed with a subset tag, strip it off. */
+       if (len > 7 && p[6] == '+') {
+           for (i = 0; i < 6; i++)
+               if (p[i] < 'A' || p[i] > 'Z')
+                   break;
+           if (i == 6) {
+               p += 7;
+               len -= 7;
+           }
+       }
+        font->ps_name = malloc (len + 1);
+        if (unlikely (font->ps_name == NULL))
+            return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+        memcpy (font->ps_name, p, len);
+        font->ps_name[len] = 0;
+    }
+    cff_index_fini (&index);
+
+    return status;
+}
+
+static cairo_int_status_t
+cairo_cff_font_read_private_dict (cairo_cff_font_t   *font,
+                                  cairo_hash_table_t *private_dict,
+                                  cairo_array_t      *local_sub_index,
+                                  int                *local_sub_bias,
+                                  cairo_bool_t      **local_subs_used,
+                                  int                *default_width,
+                                  int                *nominal_width,
+                                  unsigned char      *ptr,
+                                  int                 size)
+{
+    cairo_int_status_t status;
+    unsigned char buf[10];
+    unsigned char *end_buf;
+    int offset;
+    int i;
+    unsigned char *operand;
+    unsigned char *p;
+    int num_subs;
+
+    status = cff_dict_read (private_dict, ptr, size);
+    if (unlikely (status))
+       return status;
+
+    operand = cff_dict_get_operands (private_dict, LOCAL_SUB_OP, &i);
+    if (operand) {
+        decode_integer (operand, &offset);
+        p = ptr + offset;
+        status = cff_index_read (local_sub_index, &p, font->data_end);
+       if (unlikely (status))
+           return status;
+
+       /* Use maximum sized encoding to reserve space for later modification. */
+       end_buf = encode_integer_max (buf, 0);
+       status = cff_dict_set_operands (private_dict, LOCAL_SUB_OP, buf, end_buf - buf);
+       if (unlikely (status))
+           return status;
+    }
+
+    *default_width = 0;
+    operand = cff_dict_get_operands (private_dict, DEFAULTWIDTH_OP, &i);
+    if (operand)
+        decode_integer (operand, default_width);
+
+    *nominal_width = 0;
+    operand = cff_dict_get_operands (private_dict, NOMINALWIDTH_OP, &i);
+    if (operand)
+        decode_integer (operand, nominal_width);
+
+    num_subs = _cairo_array_num_elements (local_sub_index);
+    *local_subs_used = calloc (num_subs, sizeof (cairo_bool_t));
+    if (unlikely (*local_subs_used == NULL))
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    if (num_subs < 1240)
+       *local_sub_bias = 107;
+    else if (num_subs < 33900)
+       *local_sub_bias = 1131;
+    else
+       *local_sub_bias = 32768;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+cairo_cff_font_read_fdselect (cairo_cff_font_t *font, unsigned char *p)
+{
+    int type, num_ranges, first, last, fd, i, j;
+
+    font->fdselect = calloc (font->num_glyphs, sizeof (int));
+    if (unlikely (font->fdselect == NULL))
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    type = *p++;
+    if (type == 0)
+    {
+        for (i = 0; i < font->num_glyphs; i++)
+            font->fdselect[i] = *p++;
+    } else if (type == 3) {
+        num_ranges = be16_to_cpu( *((uint16_t *)p) );
+        p += 2;
+        for  (i = 0; i < num_ranges; i++)
+        {
+            first = be16_to_cpu( *((uint16_t *)p) );
+            p += 2;
+            fd = *p++;
+            last = be16_to_cpu( *((uint16_t *)p) );
+            for (j = first; j < last; j++)
+                font->fdselect[j] = fd;
+        }
+    } else {
+        return CAIRO_INT_STATUS_UNSUPPORTED;
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+cairo_cff_font_read_cid_fontdict (cairo_cff_font_t *font, unsigned char *ptr)
+{
+    cairo_array_t index;
+    cff_index_element_t *element;
+    unsigned int i;
+    int size;
+    unsigned char *operand;
+    int offset;
+    cairo_int_status_t status;
+    unsigned char buf[100];
+    unsigned char *end_buf;
+
+    cff_index_init (&index);
+    status = cff_index_read (&index, &ptr, font->data_end);
+    if (unlikely (status))
+        goto fail;
+
+    font->num_fontdicts = _cairo_array_num_elements (&index);
+
+    font->fd_dict = calloc (sizeof (cairo_hash_table_t *), font->num_fontdicts);
+    if (unlikely (font->fd_dict == NULL)) {
+        status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+        goto fail;
+    }
+
+    font->fd_private_dict = calloc (sizeof (cairo_hash_table_t *), font->num_fontdicts);
+    if (unlikely (font->fd_private_dict == NULL)) {
+        status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+        goto fail;
+    }
+
+    font->fd_local_sub_index = calloc (sizeof (cairo_array_t), font->num_fontdicts);
+    if (unlikely (font->fd_local_sub_index == NULL)) {
+        status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+        goto fail;
+    }
+
+    font->fd_local_sub_bias = calloc (sizeof (int), font->num_fontdicts);
+    if (unlikely (font->fd_local_sub_bias == NULL)) {
+        status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+        goto fail;
+    }
+
+    font->fd_local_subs_used = calloc (sizeof (cairo_bool_t *), font->num_fontdicts);
+    if (unlikely (font->fd_local_subs_used == NULL)) {
+        status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+        goto fail;
+    }
+
+    font->fd_default_width = calloc (sizeof (int), font->num_fontdicts);
+    if (unlikely (font->fd_default_width == NULL)) {
+        status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+        goto fail;
+    }
+
+    font->fd_nominal_width = calloc (sizeof (int), font->num_fontdicts);
+    if (unlikely (font->fd_nominal_width == NULL)) {
+        status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+        goto fail;
+    }
+
+    for (i = 0; i < font->num_fontdicts; i++) {
+        status = cff_dict_init (&font->fd_dict[i]);
+        if (unlikely (status))
+            goto fail;
+
+        element = _cairo_array_index (&index, i);
+        status = cff_dict_read (font->fd_dict[i], element->data, element->length);
+        if (unlikely (status))
+            goto fail;
+
+        operand = cff_dict_get_operands (font->fd_dict[i], PRIVATE_OP, &size);
+        if (operand == NULL) {
+            status = CAIRO_INT_STATUS_UNSUPPORTED;
+            goto fail;
+        }
+        operand = decode_integer (operand, &size);
+        decode_integer (operand, &offset);
+        status = cff_dict_init (&font->fd_private_dict[i]);
+       if (unlikely (status))
+            goto fail;
+
+        cff_index_init (&font->fd_local_sub_index[i]);
+        status = cairo_cff_font_read_private_dict (font,
+                                                   font->fd_private_dict[i],
+                                                   &font->fd_local_sub_index[i],
+                                                   &font->fd_local_sub_bias[i],
+                                                   &font->fd_local_subs_used[i],
+                                                   &font->fd_default_width[i],
+                                                   &font->fd_nominal_width[i],
+                                                   font->data + offset,
+                                                   size);
+        if (unlikely (status))
+            goto fail;
+
+       /* Set integer operand to max value to use max size encoding to reserve
+         * space for any value later */
+        end_buf = encode_integer_max (buf, 0);
+        end_buf = encode_integer_max (end_buf, 0);
+        status = cff_dict_set_operands (font->fd_dict[i], PRIVATE_OP, buf, end_buf - buf);
+        if (unlikely (status))
+            goto fail;
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+
+fail:
+    cff_index_fini (&index);
+
+    return status;
+}
+
+static void
+cairo_cff_font_read_font_metrics (cairo_cff_font_t *font, cairo_hash_table_t  *top_dict)
+{
+    unsigned char *p;
+    unsigned char *end;
+    int size;
+    double x_min, y_min, x_max, y_max;
+    double xx, yx, xy, yy;
+
+    x_min = 0.0;
+    y_min = 0.0;
+    x_max = 0.0;
+    y_max = 0.0;
+    p = cff_dict_get_operands (font->top_dict, FONTBBOX_OP, &size);
+    if (p) {
+        end = p + size;
+        if (p < end)
+            p = decode_number (p, &x_min);
+        if (p < end)
+            p = decode_number (p, &y_min);
+        if (p < end)
+            p = decode_number (p, &x_max);
+        if (p < end)
+            p = decode_number (p, &y_max);
+    }
+    font->x_min = floor (x_min);
+    font->y_min = floor (y_min);
+    font->x_max = floor (x_max);
+    font->y_max = floor (y_max);
+    font->ascent = font->y_max;
+    font->descent = font->y_min;
+
+    xx = 0.001;
+    yx = 0.0;
+    xy = 0.0;
+    yy = 0.001;
+    p = cff_dict_get_operands (font->top_dict, FONTMATRIX_OP, &size);
+    if (p) {
+        end = p + size;
+        if (p < end)
+            p = decode_number (p, &xx);
+        if (p < end)
+            p = decode_number (p, &yx);
+        if (p < end)
+            p = decode_number (p, &xy);
+        if (p < end)
+            p = decode_number (p, &yy);
+    }
+    /* Freetype uses 1/yy to get units per EM */
+    font->units_per_em = _cairo_round(1.0/yy);
+}
+
+static cairo_int_status_t
+cairo_cff_font_read_top_dict (cairo_cff_font_t *font)
+{
+    cairo_array_t index;
+    cff_index_element_t *element;
+    unsigned char buf[20];
+    unsigned char *end_buf;
+    unsigned char *operand;
+    cairo_int_status_t status;
+    unsigned char *p;
+    int size;
+    int offset;
+
+    cff_index_init (&index);
+    status = cff_index_read (&index, &font->current_ptr, font->data_end);
+    if (unlikely (status))
+        goto fail;
+
+    element = _cairo_array_index (&index, 0);
+    status = cff_dict_read (font->top_dict, element->data, element->length);
+    if (unlikely (status))
+        goto fail;
+
+    if (cff_dict_get_operands (font->top_dict, ROS_OP, &size) != NULL)
+        font->is_cid = TRUE;
+    else
+        font->is_cid = FALSE;
+
+    operand = cff_dict_get_operands (font->top_dict, CHARSTRINGS_OP, &size);
+    decode_integer (operand, &offset);
+    p = font->data + offset;
+    status = cff_index_read (&font->charstrings_index, &p, font->data_end);
+    if (unlikely (status))
+        goto fail;
+    font->num_glyphs = _cairo_array_num_elements (&font->charstrings_index);
+
+    operand = cff_dict_get_operands (font->top_dict, CHARSET_OP, &size);
+    if (font->is_cid && !operand)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    decode_integer (operand, &offset);
+    font->charset = font->data + offset;
+    if (font->charset >= font->data_end)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    if (!font->is_opentype)
+        cairo_cff_font_read_font_metrics (font, font->top_dict);
+
+    if (font->is_cid) {
+        operand = cff_dict_get_operands (font->top_dict, FDSELECT_OP, &size);
+        decode_integer (operand, &offset);
+        status = cairo_cff_font_read_fdselect (font, font->data + offset);
+       if (unlikely (status))
+           goto fail;
+
+        operand = cff_dict_get_operands (font->top_dict, FDARRAY_OP, &size);
+        decode_integer (operand, &offset);
+        status = cairo_cff_font_read_cid_fontdict (font, font->data + offset);
+       if (unlikely (status))
+           goto fail;
+    } else {
+        operand = cff_dict_get_operands (font->top_dict, PRIVATE_OP, &size);
+        operand = decode_integer (operand, &size);
+        decode_integer (operand, &offset);
+       status = cairo_cff_font_read_private_dict (font,
+                                                   font->private_dict,
+                                                  &font->local_sub_index,
+                                                  &font->local_sub_bias,
+                                                  &font->local_subs_used,
+                                                   &font->default_width,
+                                                   &font->nominal_width,
+                                                  font->data + offset,
+                                                  size);
+       if (unlikely (status))
+           goto fail;
+    }
+
+    /* Use maximum sized encoding to reserve space for later modification. */
+    end_buf = encode_integer_max (buf, 0);
+    status = cff_dict_set_operands (font->top_dict,
+                                   CHARSTRINGS_OP, buf, end_buf - buf);
+    if (unlikely (status))
+       goto fail;
+
+    status = cff_dict_set_operands (font->top_dict,
+                                   CHARSET_OP, buf, end_buf - buf);
+    if (unlikely (status))
+       goto fail;
+
+    if (font->scaled_font_subset->is_latin) {
+        status = cff_dict_set_operands (font->top_dict,
+                                        ENCODING_OP, buf, end_buf - buf);
+        if (unlikely (status))
+            goto fail;
+
+       /* Private has two operands - size and offset */
+       end_buf = encode_integer_max (end_buf, 0);
+       cff_dict_set_operands (font->top_dict, PRIVATE_OP, buf, end_buf - buf);
+        if (unlikely (status))
+            goto fail;
+
+    } else {
+        status = cff_dict_set_operands (font->top_dict,
+                                        FDSELECT_OP, buf, end_buf - buf);
+        if (unlikely (status))
+            goto fail;
+
+        status = cff_dict_set_operands (font->top_dict,
+                                        FDARRAY_OP, buf, end_buf - buf);
+        if (unlikely (status))
+            goto fail;
+
+        cff_dict_remove (font->top_dict, ENCODING_OP);
+        cff_dict_remove (font->top_dict, PRIVATE_OP);
+    }
+
+    /* Remove the unique identifier operators as the subsetted font is
+     * not the same is the original font. */
+    cff_dict_remove (font->top_dict, UNIQUEID_OP);
+    cff_dict_remove (font->top_dict, XUID_OP);
+
+fail:
+    cff_index_fini (&index);
+
+    return status;
+}
+
+static cairo_int_status_t
+cairo_cff_font_read_strings (cairo_cff_font_t *font)
+{
+    return cff_index_read (&font->strings_index, &font->current_ptr, font->data_end);
+}
+
+static cairo_int_status_t
+cairo_cff_font_read_global_subroutines (cairo_cff_font_t *font)
+{
+    cairo_int_status_t status;
+    int num_subs;
+
+    status = cff_index_read (&font->global_sub_index, &font->current_ptr, font->data_end);
+    if (unlikely (status))
+       return status;
+
+    num_subs = _cairo_array_num_elements (&font->global_sub_index);
+    font->global_subs_used = calloc (num_subs, sizeof(cairo_bool_t));
+    if (unlikely (font->global_subs_used == NULL))
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    if (num_subs < 1240)
+        font->global_sub_bias = 107;
+    else if (num_subs < 33900)
+        font->global_sub_bias = 1131;
+    else
+        font->global_sub_bias = 32768;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+typedef cairo_int_status_t
+(*font_read_t) (cairo_cff_font_t *font);
+
+static const font_read_t font_read_funcs[] = {
+    cairo_cff_font_read_header,
+    cairo_cff_font_read_name,
+    cairo_cff_font_read_top_dict,
+    cairo_cff_font_read_strings,
+    cairo_cff_font_read_global_subroutines,
+};
+
+static cairo_int_status_t
+cairo_cff_font_read_font (cairo_cff_font_t *font)
+{
+    cairo_int_status_t status;
+    unsigned int i;
+
+    for (i = 0; i < ARRAY_LENGTH (font_read_funcs); i++) {
+        status = font_read_funcs[i] (font);
+        if (unlikely (status))
+            return status;
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+cairo_cff_font_set_ros_strings (cairo_cff_font_t *font)
+{
+    cairo_status_t status;
+    unsigned char buf[30];
+    unsigned char *p;
+    int sid1, sid2;
+    const char *registry = "Adobe";
+    const char *ordering = "Identity";
+
+    sid1 = NUM_STD_STRINGS + _cairo_array_num_elements (&font->strings_subset_index);
+    status = cff_index_append_copy (&font->strings_subset_index,
+                                    (unsigned char *)registry,
+                                    strlen(registry));
+    if (unlikely (status))
+       return status;
+
+    sid2 = NUM_STD_STRINGS + _cairo_array_num_elements (&font->strings_subset_index);
+    status = cff_index_append_copy (&font->strings_subset_index,
+                                    (unsigned char *)ordering,
+                                   strlen(ordering));
+    if (unlikely (status))
+       return status;
+
+    p = encode_integer (buf, sid1);
+    p = encode_integer (p, sid2);
+    p = encode_integer (p, 0);
+    status = cff_dict_set_operands (font->top_dict, ROS_OP, buf, p - buf);
+    if (unlikely (status))
+       return status;
+
+    p = encode_integer (buf, font->scaled_font_subset->num_glyphs);
+    status = cff_dict_set_operands (font->top_dict, CIDCOUNT_OP, buf, p - buf);
+    if (unlikely (status))
+       return status;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+cairo_cff_font_subset_dict_string(cairo_cff_font_t   *font,
+                                  cairo_hash_table_t *dict,
+                                  int                 operator)
+{
+    int size;
+    unsigned char *p;
+    int sid;
+    unsigned char buf[100];
+    cff_index_element_t *element;
+    cairo_status_t status;
+
+    p = cff_dict_get_operands (dict, operator, &size);
+    if (!p)
+        return CAIRO_STATUS_SUCCESS;
+
+    decode_integer (p, &sid);
+    if (sid < NUM_STD_STRINGS)
+        return CAIRO_STATUS_SUCCESS;
+
+    element = _cairo_array_index (&font->strings_index, sid - NUM_STD_STRINGS);
+    sid = NUM_STD_STRINGS + _cairo_array_num_elements (&font->strings_subset_index);
+    status = cff_index_append (&font->strings_subset_index, element->data, element->length);
+    if (unlikely (status))
+        return status;
+
+    p = encode_integer (buf, sid);
+    status = cff_dict_set_operands (dict, operator, buf, p - buf);
+    if (unlikely (status))
+       return status;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static const int dict_strings[] = {
+    VERSION_OP,
+    NOTICE_OP,
+    COPYRIGHT_OP,
+    FULLNAME_OP,
+    FAMILYNAME_OP,
+    WEIGHT_OP,
+    POSTSCRIPT_OP,
+    BASEFONTNAME_OP,
+    FONTNAME_OP,
+};
+
+static cairo_status_t
+cairo_cff_font_subset_dict_strings (cairo_cff_font_t   *font,
+                                    cairo_hash_table_t *dict)
+{
+    cairo_status_t status;
+    unsigned int i;
+
+    for (i = 0; i < ARRAY_LENGTH (dict_strings); i++) {
+        status = cairo_cff_font_subset_dict_string (font, dict, dict_strings[i]);
+        if (unlikely (status))
+            return status;
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static unsigned char *
+type2_decode_integer (unsigned char *p, int *integer)
+{
+    if (*p == 28) {
+       *integer = p[1] << 8 | p[2];
+       p += 3;
+    } else if (*p <= 246) {
+        *integer = *p++ - 139;
+    } else if (*p <= 250) {
+        *integer = (p[0] - 247) * 256 + p[1] + 108;
+        p += 2;
+    } else if (*p <= 254) {
+        *integer = -(p[0] - 251) * 256 - p[1] - 108;
+        p += 2;
+    } else { /* *p == 255 */
+    /* This actually a 16.16 fixed-point number however we are not interested in
+     * the value of fixed-point numbers. */
+        *integer = (p[1] << 24) | (p[2] << 16) | (p[3] << 8) | p[4];
+        p += 5;
+    }
+    return p;
+}
+
+/* Type 2 charstring parser for finding calls to local or global
+ * subroutines. For non Opentype CFF fonts it also gets the glyph
+ * widths.
+ *
+ * When we find a subroutine operator, the subroutine is marked as in
+ * use and recursively followed. The subroutine number is the value on
+ * the top of the stack when the subroutine operator is executed. In
+ * most fonts the subroutine number is encoded in an integer
+ * immediately preceding the subroutine operator. However it is
+ * possible for the subroutine number on the stack to be the result of
+ * a computation (in which case there will be an operator preceding
+ * the subroutine operator). If this occurs, subroutine subsetting is
+ * disabled since we can't easily determine which subroutines are
+ * used.
+ *
+ * The width, if present, is the first integer in the charstring. The
+ * only way to confirm if the integer at the start of the charstring is
+ * the width is when the first stack clearing operator is parsed,
+ * check if there is an extra integer left over on the stack.
+ *
+ * When the first stack clearing operator is encountered
+ * type2_find_width is set to FALSE and type2_found_width is set to
+ * TRUE if an extra argument is found, otherwise FALSE.
+ */
+static cairo_status_t
+cairo_cff_parse_charstring (cairo_cff_font_t *font,
+                            unsigned char *charstring, int length,
+                            int glyph_id,
+                           cairo_bool_t need_width)
+{
+    unsigned char *p = charstring;
+    unsigned char *end = charstring + length;
+    int integer;
+    int hint_bytes;
+    int sub_num;
+    cff_index_element_t *element;
+    int fd;
+
+    while (p < end) {
+        if (*p == 28 || *p >= 32) {
+            /* Integer value */
+            p = type2_decode_integer (p, &integer);
+            font->type2_stack_size++;
+            font->type2_stack_top_value = integer;
+            font->type2_stack_top_is_int = TRUE;
+           if (!font->type2_seen_first_int) {
+               font->type2_width = integer;
+               font->type2_seen_first_int = TRUE;
+           }
+       } else if (*p == TYPE2_hstem || *p == TYPE2_vstem ||
+                  *p == TYPE2_hstemhm || *p == TYPE2_vstemhm) {
+            /* Hint operator. The number of hints declared by the
+             * operator depends on the size of the stack. */
+           font->type2_stack_top_is_int = FALSE;
+           font->type2_num_hints += font->type2_stack_size/2;
+           if (font->type2_find_width && font->type2_stack_size % 2)
+               font->type2_found_width = TRUE;
+
+           font->type2_stack_size = 0;
+           font->type2_find_width = FALSE;
+           p++;
+       } else if (*p == TYPE2_hintmask || *p == TYPE2_cntrmask) {
+           /* Hintmask operator. These operators are followed by a
+            * variable length mask where the length depends on the
+            * number of hints declared. The first time this is called
+            * it is also an implicit vstem if there are arguments on
+            * the stack. */
+           if (font->type2_hintmask_bytes == 0) {
+               font->type2_stack_top_is_int = FALSE;
+               font->type2_num_hints += font->type2_stack_size/2;
+               if (font->type2_find_width && font->type2_stack_size % 2)
+                   font->type2_found_width = TRUE;
+
+               font->type2_stack_size = 0;
+               font->type2_find_width = FALSE;
+               font->type2_hintmask_bytes = (font->type2_num_hints+7)/8;
+           }
+
+           hint_bytes = font->type2_hintmask_bytes;
+           p++;
+           p += hint_bytes;
+       } else if (*p == TYPE2_rmoveto) {
+           if (font->type2_find_width && font->type2_stack_size > 2)
+               font->type2_found_width = TRUE;
+
+           font->type2_stack_size = 0;
+           font->type2_find_width = FALSE;
+           font->type2_has_path = TRUE;
+           p++;
+       } else if (*p == TYPE2_hmoveto || *p == TYPE2_vmoveto) {
+           if (font->type2_find_width && font->type2_stack_size > 1)
+               font->type2_found_width = TRUE;
+
+           font->type2_stack_size = 0;
+           font->type2_find_width = FALSE;
+           font->type2_has_path = TRUE;
+           p++;
+       } else if (*p == TYPE2_endchar) {
+           if (!font->type2_has_path && font->type2_stack_size > 3)
+               return CAIRO_INT_STATUS_UNSUPPORTED; /* seac (Ref Appendix C of Type 2 Charstring Format */
+
+           if (font->type2_find_width && font->type2_stack_size > 0)
+               font->type2_found_width = TRUE;
+
+           return CAIRO_STATUS_SUCCESS;
+        } else if (*p == TYPE2_callsubr) {
+            /* call to local subroutine */
+           if (! font->type2_stack_top_is_int)
+               return CAIRO_INT_STATUS_UNSUPPORTED;
+
+            if (++font->type2_nesting_level > MAX_SUBROUTINE_NESTING)
+               return CAIRO_INT_STATUS_UNSUPPORTED;
+
+            p++;
+           font->type2_stack_top_is_int = FALSE;
+            font->type2_stack_size--;
+           if (font->type2_find_width && font->type2_stack_size == 0)
+               font->type2_seen_first_int = FALSE;
+
+            if (font->is_cid) {
+                fd = font->fdselect[glyph_id];
+                sub_num = font->type2_stack_top_value + font->fd_local_sub_bias[fd];
+                element = _cairo_array_index (&font->fd_local_sub_index[fd], sub_num);
+                if (! font->fd_local_subs_used[fd][sub_num]) {
+                   font->fd_local_subs_used[fd][sub_num] = TRUE;
+                   cairo_cff_parse_charstring (font, element->data, element->length, glyph_id, need_width);
+               }
+            } else {
+                sub_num = font->type2_stack_top_value + font->local_sub_bias;
+                element = _cairo_array_index (&font->local_sub_index, sub_num);
+                if (! font->local_subs_used[sub_num] ||
+                   (need_width && !font->type2_found_width))
+               {
+                   font->local_subs_used[sub_num] = TRUE;
+                   cairo_cff_parse_charstring (font, element->data, element->length, glyph_id, need_width);
+               }
+            }
+            font->type2_nesting_level--;
+        } else if (*p == TYPE2_callgsubr) {
+            /* call to global subroutine */
+           if (! font->type2_stack_top_is_int)
+               return CAIRO_INT_STATUS_UNSUPPORTED;
+
+            if (++font->type2_nesting_level > MAX_SUBROUTINE_NESTING)
+               return CAIRO_INT_STATUS_UNSUPPORTED;
+
+            p++;
+            font->type2_stack_size--;
+           font->type2_stack_top_is_int = FALSE;
+           if (font->type2_find_width && font->type2_stack_size == 0)
+               font->type2_seen_first_int = FALSE;
+
+           sub_num = font->type2_stack_top_value + font->global_sub_bias;
+           element = _cairo_array_index (&font->global_sub_index, sub_num);
+            if (! font->global_subs_used[sub_num] ||
+               (need_width && !font->type2_found_width))
+           {
+                font->global_subs_used[sub_num] = TRUE;
+                cairo_cff_parse_charstring (font, element->data, element->length, glyph_id, need_width);
+            }
+            font->type2_nesting_level--;
+        } else if (*p == 12) {
+            /* 2 byte instruction */
+
+           /* All the 2 byte operators are either not valid before a
+            * stack clearing operator or they are one of the
+            * arithmetic, storage, or conditional operators. */
+           if (need_width && font->type2_find_width)
+               return CAIRO_INT_STATUS_UNSUPPORTED;
+
+            p += 2;
+           font->type2_stack_top_is_int = FALSE;
+       } else {
+            /* 1 byte instruction */
+            p++;
+           font->type2_stack_top_is_int = FALSE;
+        }
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+cairo_cff_find_width_and_subroutines_used (cairo_cff_font_t  *font,
+                                          unsigned char *charstring, int length,
+                                          int glyph_id, int subset_id)
+{
+    cairo_status_t status;
+    int width;
+    int fd;
+
+    font->type2_stack_size = 0;
+    font->type2_stack_top_value = 0;;
+    font->type2_stack_top_is_int = FALSE;
+    font->type2_num_hints = 0;
+    font->type2_hintmask_bytes = 0;
+    font->type2_nesting_level = 0;
+    font->type2_seen_first_int = FALSE;
+    font->type2_find_width = TRUE;
+    font->type2_found_width = FALSE;
+    font->type2_width = 0;
+    font->type2_has_path = FALSE;
+
+    status = cairo_cff_parse_charstring (font, charstring, length, glyph_id, TRUE);
+    if (status)
+       return status;
+
+    if (!font->is_opentype) {
+        if (font->is_cid) {
+            fd = font->fdselect[glyph_id];
+            if (font->type2_found_width)
+                width = font->fd_nominal_width[fd] + font->type2_width;
+            else
+                width = font->fd_default_width[fd];
+        } else {
+            if (font->type2_found_width)
+                width = font->nominal_width + font->type2_width;
+            else
+                width = font->default_width;
+        }
+        font->widths[subset_id] = width;
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+cairo_cff_font_get_gid_for_cid (cairo_cff_font_t  *font, unsigned long cid, unsigned long *gid)
+{
+    unsigned char *p;
+    unsigned long first_gid;
+    unsigned long first_cid;
+    int num_left;
+    unsigned long c, g;
+
+    if (cid == 0) {
+       *gid = 0;
+       return CAIRO_STATUS_SUCCESS;
+    }
+
+    switch (font->charset[0]) {
+       /* Format 0 */
+       case 0:
+           p = font->charset + 1;
+           g = 1;
+           while (g <= (unsigned)font->num_glyphs && p < font->data_end) {
+               c = be16_to_cpu( *((uint16_t *)p) );
+               if (c == cid) {
+                   *gid = g;
+                   return CAIRO_STATUS_SUCCESS;
+               }
+               g++;
+               p += 2;
+           }
+           break;
+
+       /* Format 1 */
+       case 1:
+           first_gid = 1;
+           p = font->charset + 1;
+           while (first_gid <= (unsigned)font->num_glyphs && p + 2 < font->data_end) {
+               first_cid = be16_to_cpu( *((uint16_t *)p) );
+               num_left = p[2];
+               if (cid >= first_cid && cid <= first_cid + num_left) {
+                   *gid = first_gid + cid - first_cid;
+                   return CAIRO_STATUS_SUCCESS;
+               }
+               first_gid += num_left + 1;
+               p += 3;
+           }
+           break;
+
+       /* Format 2 */
+       case 2:
+           first_gid = 1;
+           p = font->charset + 1;
+           while (first_gid <= (unsigned)font->num_glyphs && p + 3 < font->data_end) {
+               first_cid = be16_to_cpu( *((uint16_t *)p) );
+               num_left = be16_to_cpu( *((uint16_t *)(p+2)) );
+               if (cid >= first_cid && cid <= first_cid + num_left) {
+                   *gid = first_gid + cid - first_cid;
+                   return CAIRO_STATUS_SUCCESS;
+               }
+               first_gid += num_left + 1;
+               p += 4;
+           }
+           break;
+
+       default:
+           break;
+    }
+    return CAIRO_INT_STATUS_UNSUPPORTED;
+}
+
+static cairo_int_status_t
+cairo_cff_font_subset_charstrings_and_subroutines (cairo_cff_font_t  *font)
+{
+    cff_index_element_t *element;
+    unsigned int i;
+    cairo_int_status_t status;
+    unsigned long glyph, cid;
+
+    font->subset_subroutines = TRUE;
+    for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) {
+       if (font->is_cid) {
+           cid = font->scaled_font_subset->glyphs[i];
+           status = cairo_cff_font_get_gid_for_cid (font, cid, &glyph);
+           if (unlikely (status))
+               return status;
+       } else {
+           glyph = font->scaled_font_subset->glyphs[i];
+       }
+        element = _cairo_array_index (&font->charstrings_index, glyph);
+        status = cff_index_append (&font->charstrings_subset_index,
+                                   element->data,
+                                   element->length);
+        if (unlikely (status))
+            return status;
+
+       if (font->subset_subroutines) {
+           status = cairo_cff_find_width_and_subroutines_used (font,
+                                                               element->data, element->length,
+                                                               glyph, i);
+           if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
+               /* If parsing the charstrings fails we embed all the
+                * subroutines. But if the font is not opentype we
+                * need to successfully parse all charstrings to get
+                * the widths. */
+               font->subset_subroutines = FALSE;
+               if (!font->is_opentype)
+                   return status;
+           } else if (unlikely (status)) {
+                return status;
+           }
+        }
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+cairo_cff_font_subset_fontdict (cairo_cff_font_t  *font)
+{
+    unsigned int i;
+    int fd;
+    int *reverse_map;
+    unsigned long cid, gid;
+    cairo_int_status_t status;
+
+    font->fdselect_subset = calloc (font->scaled_font_subset->num_glyphs,
+                                     sizeof (int));
+    if (unlikely (font->fdselect_subset == NULL))
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    font->fd_subset_map = calloc (font->num_fontdicts, sizeof (int));
+    if (unlikely (font->fd_subset_map == NULL))
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    font->private_dict_offset = calloc (font->num_fontdicts, sizeof (int));
+    if (unlikely (font->private_dict_offset == NULL))
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    reverse_map = calloc (font->num_fontdicts, sizeof (int));
+    if (unlikely (reverse_map == NULL))
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    for (i = 0; i < font->num_fontdicts; i++)
+        reverse_map[i] = -1;
+
+    font->num_subset_fontdicts = 0;
+    for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) {
+       cid = font->scaled_font_subset->glyphs[i];
+       status = cairo_cff_font_get_gid_for_cid (font, cid, &gid);
+       if (unlikely (status))
+           return status;
+
+        fd = font->fdselect[gid];
+        if (reverse_map[fd] < 0) {
+            font->fd_subset_map[font->num_subset_fontdicts] = fd;
+            reverse_map[fd] = font->num_subset_fontdicts++;
+        }
+        font->fdselect_subset[i] = reverse_map[fd];
+    }
+
+    free (reverse_map);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+cairo_cff_font_create_cid_fontdict (cairo_cff_font_t *font)
+{
+    unsigned char buf[100];
+    unsigned char *end_buf;
+    cairo_status_t status;
+
+    font->num_fontdicts = 1;
+    font->fd_dict = malloc (sizeof (cairo_hash_table_t *));
+    if (unlikely (font->fd_dict == NULL))
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    if (cff_dict_init (&font->fd_dict[0])) {
+       free (font->fd_dict);
+       font->fd_dict = NULL;
+       font->num_fontdicts = 0;
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+    }
+
+    font->fd_subset_map = malloc (sizeof (int));
+    if (unlikely (font->fd_subset_map == NULL))
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    font->private_dict_offset = malloc (sizeof (int));
+    if (unlikely (font->private_dict_offset == NULL))
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    font->fd_subset_map[0] = 0;
+    font->num_subset_fontdicts = 1;
+
+    /* Set integer operand to max value to use max size encoding to reserve
+     * space for any value later */
+    end_buf = encode_integer_max (buf, 0);
+    end_buf = encode_integer_max (end_buf, 0);
+    status = cff_dict_set_operands (font->fd_dict[0], PRIVATE_OP, buf, end_buf - buf);
+    if (unlikely (status))
+       return status;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+cairo_cff_font_subset_strings (cairo_cff_font_t *font)
+{
+    cairo_status_t status;
+    unsigned int i;
+
+    status = cairo_cff_font_subset_dict_strings (font, font->top_dict);
+    if (unlikely (status))
+        return status;
+
+    if (font->is_cid) {
+        for (i = 0; i < font->num_subset_fontdicts; i++) {
+            status = cairo_cff_font_subset_dict_strings (font, font->fd_dict[font->fd_subset_map[i]]);
+            if (unlikely (status))
+                return status;
+
+            status = cairo_cff_font_subset_dict_strings (font, font->fd_private_dict[font->fd_subset_map[i]]);
+            if (unlikely (status))
+                return status;
+        }
+    } else {
+        status = cairo_cff_font_subset_dict_strings (font, font->private_dict);
+    }
+
+    return status;
+}
+
+/* The Euro is the only the only character in the winansi encoding
+ * with a glyph name that is not a CFF standard string. As the strings
+ * are written before the charset, we need to check during the
+ * subsetting phase if the Euro glyph is required and add the
+ * glyphname to the list of strings to write out.
+ */
+static cairo_status_t
+cairo_cff_font_add_euro_charset_string (cairo_cff_font_t *font)
+{
+    cairo_status_t status;
+    unsigned int i;
+    int ch;
+    const char *euro = "Euro";
+
+    for (i = 1; i < font->scaled_font_subset->num_glyphs; i++) {
+       ch = font->scaled_font_subset->to_latin_char[i];
+       if (ch == 128) {
+           font->euro_sid = NUM_STD_STRINGS + _cairo_array_num_elements (&font->strings_subset_index);
+           status = cff_index_append_copy (&font->strings_subset_index,
+                                           (unsigned char *)euro, strlen(euro));
+           return status;
+       }
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+cairo_cff_font_subset_font (cairo_cff_font_t  *font)
+{
+    cairo_status_t status;
+
+    if (!font->scaled_font_subset->is_latin) {
+       status = cairo_cff_font_set_ros_strings (font);
+       if (unlikely (status))
+           return status;
+    }
+
+    status = cairo_cff_font_subset_charstrings_and_subroutines (font);
+    if (unlikely (status))
+        return status;
+
+    if (!font->scaled_font_subset->is_latin) {
+       if (font->is_cid)
+           status = cairo_cff_font_subset_fontdict (font);
+       else
+           status = cairo_cff_font_create_cid_fontdict (font);
+       if (unlikely (status))
+           return status;
+    }  else {
+       font->private_dict_offset = malloc (sizeof (int));
+       if (unlikely (font->private_dict_offset == NULL))
+           return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+    }
+
+    status = cairo_cff_font_subset_strings (font);
+    if (unlikely (status))
+        return status;
+
+    if (font->scaled_font_subset->is_latin)
+       status = cairo_cff_font_add_euro_charset_string (font);
+
+    return status;
+}
+
+/* Set the operand of the specified operator in the (already written)
+ * top dict to point to the current position in the output
+ * array. Operands updated with this function must have previously
+ * been encoded with the 5-byte (max) integer encoding. */
+static void
+cairo_cff_font_set_topdict_operator_to_cur_pos (cairo_cff_font_t  *font,
+                                                int                operator)
+{
+    int cur_pos;
+    int offset;
+    int size;
+    unsigned char buf[10];
+    unsigned char *buf_end;
+    unsigned char *op_ptr;
+
+    cur_pos = _cairo_array_num_elements (&font->output);
+    buf_end = encode_integer_max (buf, cur_pos);
+    offset = cff_dict_get_location (font->top_dict, operator, &size);
+    assert (offset > 0);
+    op_ptr = _cairo_array_index (&font->output, offset);
+    memcpy (op_ptr, buf, buf_end - buf);
+}
+
+static cairo_status_t
+cairo_cff_font_write_header (cairo_cff_font_t *font)
+{
+    return _cairo_array_append_multiple (&font->output,
+                                         font->header,
+                                         font->header->header_size);
+}
+
+static cairo_status_t
+cairo_cff_font_write_name (cairo_cff_font_t *font)
+{
+    cairo_status_t status = CAIRO_STATUS_SUCCESS;
+    cairo_array_t index;
+
+    cff_index_init (&index);
+
+    status = cff_index_append_copy (&index,
+                                    (unsigned char *) font->ps_name,
+                                    strlen(font->ps_name));
+    if (unlikely (status))
+       goto FAIL;
+
+    status = cff_index_write (&index, &font->output);
+    if (unlikely (status))
+        goto FAIL;
+
+FAIL:
+    cff_index_fini (&index);
+
+    return status;
+}
+
+static cairo_status_t
+cairo_cff_font_write_top_dict (cairo_cff_font_t *font)
+{
+    uint16_t count;
+    unsigned char buf[10];
+    unsigned char *p;
+    int offset_index;
+    int dict_start, dict_size;
+    int offset_size = 4;
+    cairo_status_t status;
+
+    /* Write an index containing the top dict */
+
+    count = cpu_to_be16 (1);
+    status = _cairo_array_append_multiple (&font->output, &count, 2);
+    if (unlikely (status))
+        return status;
+    buf[0] = offset_size;
+    status = _cairo_array_append (&font->output, buf);
+    if (unlikely (status))
+        return status;
+    encode_index_offset (buf, offset_size, 1);
+    status = _cairo_array_append_multiple (&font->output, buf, offset_size);
+    if (unlikely (status))
+        return status;
+
+    /* Reserve space for last element of offset array and update after
+     * dict is written */
+    offset_index = _cairo_array_num_elements (&font->output);
+    status = _cairo_array_append_multiple (&font->output, buf, offset_size);
+    if (unlikely (status))
+        return status;
+
+    dict_start = _cairo_array_num_elements (&font->output);
+    status = cff_dict_write (font->top_dict, &font->output);
+    if (unlikely (status))
+        return status;
+    dict_size = _cairo_array_num_elements (&font->output) - dict_start;
+
+    encode_index_offset (buf, offset_size, dict_size + 1);
+    p = _cairo_array_index (&font->output, offset_index);
+    memcpy (p, buf, offset_size);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+cairo_cff_font_write_strings (cairo_cff_font_t  *font)
+{
+    return cff_index_write (&font->strings_subset_index, &font->output);
+}
+
+static cairo_status_t
+cairo_cff_font_write_global_subrs (cairo_cff_font_t  *font)
+{
+    unsigned int i;
+    unsigned char return_op = TYPE2_return;
+
+    /* poppler and fontforge don't like zero length subroutines so we
+     * replace unused subroutines with a 'return' instruction. */
+    if (font->subset_subroutines) {
+        for (i = 0; i < _cairo_array_num_elements (&font->global_sub_index); i++) {
+            if (! font->global_subs_used[i])
+                cff_index_set_object (&font->global_sub_index, i, &return_op, 1);
+        }
+    }
+
+    return cff_index_write (&font->global_sub_index, &font->output);
+}
+
+static cairo_status_t
+cairo_cff_font_write_encoding (cairo_cff_font_t  *font)
+{
+    unsigned char buf[2];
+    cairo_status_t status;
+    unsigned int i;
+
+    cairo_cff_font_set_topdict_operator_to_cur_pos (font, ENCODING_OP);
+    buf[0] = 0; /* Format 0 */
+    buf[1] = font->scaled_font_subset->num_glyphs - 1;
+    status = _cairo_array_append_multiple (&font->output, buf, 2);
+    if (unlikely (status))
+       return status;
+
+    for (i = 1; i < font->scaled_font_subset->num_glyphs; i++) {
+       unsigned char ch = font->scaled_font_subset->to_latin_char[i];
+        status = _cairo_array_append (&font->output, &ch);
+        if (unlikely (status))
+            return status;
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+cairo_cff_font_write_fdselect (cairo_cff_font_t  *font)
+{
+    unsigned char data;
+    unsigned int i;
+    cairo_int_status_t status;
+
+    cairo_cff_font_set_topdict_operator_to_cur_pos (font, FDSELECT_OP);
+
+    if (font->is_cid) {
+        data = 0;
+        status = _cairo_array_append (&font->output, &data);
+        if (unlikely (status))
+            return status;
+
+        for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) {
+            data = font->fdselect_subset[i];
+            status = _cairo_array_append (&font->output, &data);
+            if (unlikely (status))
+                return status;
+        }
+    } else {
+        unsigned char byte;
+        uint16_t word;
+
+        status = _cairo_array_grow_by (&font->output, 9);
+        if (unlikely (status))
+            return status;
+
+        byte = 3;
+        status = _cairo_array_append (&font->output, &byte);
+        assert (status == CAIRO_INT_STATUS_SUCCESS);
+
+        word = cpu_to_be16 (1);
+        status = _cairo_array_append_multiple (&font->output, &word, 2);
+        assert (status == CAIRO_INT_STATUS_SUCCESS);
+
+        word = cpu_to_be16 (0);
+        status = _cairo_array_append_multiple (&font->output, &word, 2);
+        assert (status == CAIRO_INT_STATUS_SUCCESS);
+
+        byte = 0;
+        status = _cairo_array_append (&font->output, &byte);
+        assert (status == CAIRO_INT_STATUS_SUCCESS);
+
+        word = cpu_to_be16 (font->scaled_font_subset->num_glyphs);
+        status = _cairo_array_append_multiple (&font->output, &word, 2);
+        assert (status == CAIRO_INT_STATUS_SUCCESS);
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+/* Winansi to CFF standard strings mapping for characters 128 to 255 */
+static const int winansi_to_cff_std_string[] = {
+       /* 128 */
+      0,   0, 117, 101, 118, 121, 112, 113,
+    126, 122, 192, 107, 142,   0, 199,   0,
+       /* 144 */
+      0,  65,   8, 105, 119, 116, 111, 137,
+    127, 153, 221, 108, 148,   0, 228, 198,
+       /* 160 */
+      0,  96,  97,  98, 103, 100, 160, 102,
+    131, 170, 139, 106, 151,   0, 165, 128,
+       /* 176 */
+    161, 156, 164, 169, 125, 152, 115, 114,
+    133, 150, 143, 120, 158, 155, 163, 123,
+       /* 192 */
+    174, 171, 172, 176, 173, 175, 138, 177,
+    181, 178, 179, 180, 185, 182, 183, 184,
+       /* 208 */
+    154, 186, 190, 187, 188, 191, 189, 168,
+    141, 196, 193, 194, 195, 197, 157, 149,
+       /* 224 */
+    203, 200, 201, 205, 202, 204, 144, 206,
+    210, 207, 208, 209, 214, 211, 212, 213,
+       /* 240 */
+    167, 215, 219, 216, 217, 220, 218, 159,
+    147, 225, 222, 223, 224, 226, 162, 227,
+};
+
+static int
+cairo_cff_font_get_sid_for_winansi_char (cairo_cff_font_t  *font, int ch)
+{
+    int sid;
+
+    if (ch == 39) {
+       sid = 104;
+
+    } else if (ch == 96) {
+       sid = 124;
+
+    } else if (ch >= 32 && ch <= 126) {
+       sid = ch - 31;
+
+    } else if (ch == 128) {
+       assert (font->euro_sid >= NUM_STD_STRINGS);
+       sid = font->euro_sid;
+
+    } else if (ch >= 128 && ch <= 255) {
+       sid = winansi_to_cff_std_string[ch - 128];
+
+    } else {
+       sid = 0;
+    }
+
+    return sid;
+}
+
+static cairo_status_t
+cairo_cff_font_write_type1_charset (cairo_cff_font_t  *font)
+{
+    unsigned char format = 0;
+    unsigned int i;
+    int ch, sid;
+    cairo_status_t status;
+    uint16_t sid_be16;
+
+    cairo_cff_font_set_topdict_operator_to_cur_pos (font, CHARSET_OP);
+    status = _cairo_array_append (&font->output, &format);
+    if (unlikely (status))
+       return status;
+
+    for (i = 1; i < font->scaled_font_subset->num_glyphs; i++) {
+       ch = font->scaled_font_subset->to_latin_char[i];
+       sid = cairo_cff_font_get_sid_for_winansi_char (font, ch);
+        if (unlikely (status))
+           return status;
+
+       sid_be16 = cpu_to_be16(sid);
+       status = _cairo_array_append_multiple (&font->output, &sid_be16, sizeof(sid_be16));
+        if (unlikely (status))
+            return status;
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+cairo_cff_font_write_cid_charset (cairo_cff_font_t  *font)
+{
+    unsigned char byte;
+    uint16_t word;
+    cairo_status_t status;
+
+    cairo_cff_font_set_topdict_operator_to_cur_pos (font, CHARSET_OP);
+    status = _cairo_array_grow_by (&font->output, 5);
+    if (unlikely (status))
+        return status;
+
+    byte = 2;
+    status = _cairo_array_append (&font->output, &byte);
+    assert (status == CAIRO_STATUS_SUCCESS);
+
+    word = cpu_to_be16 (1);
+    status = _cairo_array_append_multiple (&font->output, &word, 2);
+    assert (status == CAIRO_STATUS_SUCCESS);
+
+    word = cpu_to_be16 (font->scaled_font_subset->num_glyphs - 2);
+    status = _cairo_array_append_multiple (&font->output, &word, 2);
+    assert (status == CAIRO_STATUS_SUCCESS);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+cairo_cff_font_write_charstrings (cairo_cff_font_t  *font)
+{
+    cairo_cff_font_set_topdict_operator_to_cur_pos (font, CHARSTRINGS_OP);
+
+    return cff_index_write (&font->charstrings_subset_index, &font->output);
+}
+
+static cairo_status_t
+cairo_cff_font_write_cid_fontdict (cairo_cff_font_t *font)
+{
+    unsigned int i;
+    cairo_int_status_t status;
+    unsigned int offset_array;
+    uint32_t *offset_array_ptr;
+    int offset_base;
+    uint16_t count;
+    uint8_t offset_size = 4;
+
+    cairo_cff_font_set_topdict_operator_to_cur_pos (font, FDARRAY_OP);
+    count = cpu_to_be16 (font->num_subset_fontdicts);
+    status = _cairo_array_append_multiple (&font->output, &count, sizeof (uint16_t));
+    if (unlikely (status))
+        return status;
+    status = _cairo_array_append (&font->output, &offset_size);
+    if (unlikely (status))
+        return status;
+
+    offset_array = _cairo_array_num_elements (&font->output);
+    status = _cairo_array_allocate (&font->output,
+                                    (font->num_subset_fontdicts + 1)*offset_size,
+                                    (void **) &offset_array_ptr);
+    if (unlikely (status))
+        return status;
+    offset_base = _cairo_array_num_elements (&font->output) - 1;
+    *offset_array_ptr = cpu_to_be32(1);
+    offset_array += sizeof(uint32_t);
+    for (i = 0; i < font->num_subset_fontdicts; i++) {
+        status = cff_dict_write (font->fd_dict[font->fd_subset_map[i]],
+                                 &font->output);
+        if (unlikely (status))
+            return status;
+
+       offset_array_ptr = (uint32_t *) _cairo_array_index (&font->output, offset_array);
+        *offset_array_ptr = cpu_to_be32(_cairo_array_num_elements (&font->output) - offset_base);
+       offset_array += sizeof(uint32_t);
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+cairo_cff_font_write_private_dict (cairo_cff_font_t   *font,
+                                  int                 dict_num,
+                                  cairo_hash_table_t *parent_dict,
+                                  cairo_hash_table_t *private_dict)
+{
+    int offset;
+    int size;
+    unsigned char buf[10];
+    unsigned char *buf_end;
+    unsigned char *p;
+    cairo_status_t status;
+
+    /* Write private dict and update offset and size in top dict */
+    font->private_dict_offset[dict_num] = _cairo_array_num_elements (&font->output);
+    status = cff_dict_write (private_dict, &font->output);
+    if (unlikely (status))
+        return status;
+
+    size = _cairo_array_num_elements (&font->output) - font->private_dict_offset[dict_num];
+    /* private entry has two operands - size and offset */
+    buf_end = encode_integer_max (buf, size);
+    buf_end = encode_integer_max (buf_end, font->private_dict_offset[dict_num]);
+    offset = cff_dict_get_location (parent_dict, PRIVATE_OP, &size);
+    assert (offset > 0);
+    p = _cairo_array_index (&font->output, offset);
+    memcpy (p, buf, buf_end - buf);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+cairo_cff_font_write_local_sub (cairo_cff_font_t   *font,
+                                int                 dict_num,
+                                cairo_hash_table_t *private_dict,
+                                cairo_array_t      *local_sub_index,
+                                cairo_bool_t       *local_subs_used)
+{
+    int offset;
+    int size;
+    unsigned char buf[10];
+    unsigned char *buf_end;
+    unsigned char *p;
+    cairo_status_t status;
+    unsigned int i;
+    unsigned char return_op = TYPE2_return;
+
+    if (_cairo_array_num_elements (local_sub_index) > 0) {
+        /* Write local subroutines and update offset in private
+         * dict. Local subroutines offset is relative to start of
+         * private dict */
+        offset = _cairo_array_num_elements (&font->output) - font->private_dict_offset[dict_num];
+        buf_end = encode_integer_max (buf, offset);
+        offset = cff_dict_get_location (private_dict, LOCAL_SUB_OP, &size);
+        assert (offset > 0);
+        p = _cairo_array_index (&font->output, offset);
+        memcpy (p, buf, buf_end - buf);
+
+       /* poppler and fontforge don't like zero length subroutines so
+        * we replace unused subroutines with a 'return' instruction.
+        */
+        if (font->subset_subroutines) {
+            for (i = 0; i < _cairo_array_num_elements (local_sub_index); i++) {
+                if (! local_subs_used[i])
+                    cff_index_set_object (local_sub_index, i, &return_op, 1);
+            }
+        }
+        status = cff_index_write (local_sub_index, &font->output);
+        if (unlikely (status))
+            return status;
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+
+static cairo_status_t
+cairo_cff_font_write_cid_private_dict_and_local_sub (cairo_cff_font_t  *font)
+{
+    unsigned int i;
+    cairo_int_status_t status;
+
+    if (font->is_cid) {
+        for (i = 0; i < font->num_subset_fontdicts; i++) {
+            status = cairo_cff_font_write_private_dict (
+                            font,
+                            i,
+                            font->fd_dict[font->fd_subset_map[i]],
+                            font->fd_private_dict[font->fd_subset_map[i]]);
+            if (unlikely (status))
+                return status;
+        }
+
+        for (i = 0; i < font->num_subset_fontdicts; i++) {
+            status = cairo_cff_font_write_local_sub (
+                            font,
+                            i,
+                            font->fd_private_dict[font->fd_subset_map[i]],
+                            &font->fd_local_sub_index[font->fd_subset_map[i]],
+                            font->fd_local_subs_used[font->fd_subset_map[i]]);
+            if (unlikely (status))
+                return status;
+        }
+    } else {
+        status = cairo_cff_font_write_private_dict (font,
+                                                    0,
+                                                    font->fd_dict[0],
+                                                    font->private_dict);
+       if (unlikely (status))
+           return status;
+
+        status = cairo_cff_font_write_local_sub (font,
+                                                 0,
+                                                 font->private_dict,
+                                                 &font->local_sub_index,
+                                                 font->local_subs_used);
+       if (unlikely (status))
+           return status;
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+cairo_cff_font_write_type1_private_dict_and_local_sub (cairo_cff_font_t  *font)
+{
+    cairo_int_status_t status;
+
+    status = cairo_cff_font_write_private_dict (font,
+                                               0,
+                                               font->top_dict,
+                                               font->private_dict);
+    if (unlikely (status))
+       return status;
+
+    status = cairo_cff_font_write_local_sub (font,
+                                            0,
+                                            font->private_dict,
+                                            &font->local_sub_index,
+                                            font->local_subs_used);
+    if (unlikely (status))
+       return status;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+
+typedef cairo_status_t
+(*font_write_t) (cairo_cff_font_t *font);
+
+static const font_write_t font_write_cid_funcs[] = {
+    cairo_cff_font_write_header,
+    cairo_cff_font_write_name,
+    cairo_cff_font_write_top_dict,
+    cairo_cff_font_write_strings,
+    cairo_cff_font_write_global_subrs,
+    cairo_cff_font_write_cid_charset,
+    cairo_cff_font_write_fdselect,
+    cairo_cff_font_write_charstrings,
+    cairo_cff_font_write_cid_fontdict,
+    cairo_cff_font_write_cid_private_dict_and_local_sub,
+};
+
+static const font_write_t font_write_type1_funcs[] = {
+    cairo_cff_font_write_header,
+    cairo_cff_font_write_name,
+    cairo_cff_font_write_top_dict,
+    cairo_cff_font_write_strings,
+    cairo_cff_font_write_global_subrs,
+    cairo_cff_font_write_encoding,
+    cairo_cff_font_write_type1_charset,
+    cairo_cff_font_write_charstrings,
+    cairo_cff_font_write_type1_private_dict_and_local_sub,
+};
+
+static cairo_status_t
+cairo_cff_font_write_subset (cairo_cff_font_t *font)
+{
+    cairo_int_status_t status;
+    unsigned int i;
+
+    if (font->scaled_font_subset->is_latin) {
+        for (i = 0; i < ARRAY_LENGTH (font_write_type1_funcs); i++) {
+            status = font_write_type1_funcs[i] (font);
+            if (unlikely (status))
+                return status;
+        }
+    } else {
+        for (i = 0; i < ARRAY_LENGTH (font_write_cid_funcs); i++) {
+            status = font_write_cid_funcs[i] (font);
+            if (unlikely (status))
+                return status;
+        }
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+cairo_cff_font_generate (cairo_cff_font_t  *font,
+                         const char       **data,
+                         unsigned long     *length)
+{
+    cairo_int_status_t status;
+
+    status = cairo_cff_font_read_font (font);
+    if (unlikely (status))
+        return status;
+
+    /* If the PS name is not found, create a CairoFont-x-y name. */
+    if (font->ps_name == NULL) {
+        font->ps_name = malloc (30);
+        if (unlikely (font->ps_name == NULL))
+           return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+        snprintf(font->ps_name, 30, "CairoFont-%u-%u",
+                 font->scaled_font_subset->font_id,
+                 font->scaled_font_subset->subset_id);
+    }
+
+    status = cairo_cff_font_subset_font (font);
+    if (unlikely (status))
+        return status;
+
+    status = cairo_cff_font_write_subset (font);
+    if (unlikely (status))
+        return status;
+
+
+    *data = _cairo_array_index (&font->output, 0);
+    *length = _cairo_array_num_elements (&font->output);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+cairo_cff_font_create_set_widths (cairo_cff_font_t *font)
+{
+    unsigned long size;
+    unsigned long long_entry_size;
+    unsigned long short_entry_size;
+    unsigned int i;
+    tt_hhea_t hhea;
+    int num_hmetrics;
+    unsigned char buf[10];
+    int glyph_index;
+    cairo_int_status_t status;
+
+    size = sizeof (tt_hhea_t);
+    status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font,
+                                                 TT_TAG_hhea, 0,
+                                                 (unsigned char*) &hhea, &size);
+    if (unlikely (status))
+        return status;
+    num_hmetrics = be16_to_cpu (hhea.num_hmetrics);
+
+    for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) {
+        glyph_index = font->scaled_font_subset->glyphs[i];
+        long_entry_size = 2 * sizeof (int16_t);
+        short_entry_size = sizeof (int16_t);
+        if (glyph_index < num_hmetrics) {
+            status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font,
+                                                         TT_TAG_hmtx,
+                                                         glyph_index * long_entry_size,
+                                                         buf, &short_entry_size);
+            if (unlikely (status))
+                return status;
+        }
+        else
+        {
+            status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font,
+                                                         TT_TAG_hmtx,
+                                                         (num_hmetrics - 1) * long_entry_size,
+                                                         buf, &short_entry_size);
+            if (unlikely (status))
+                return status;
+        }
+        font->widths[i] = be16_to_cpu (*((int16_t*)buf));
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_bool_t
+check_fontdata_is_cff (const unsigned char *data, long length)
+{
+    cff_header_t *header;
+
+    if (length < (long)sizeof (cff_header_t))
+        return FALSE;
+
+    header = (cff_header_t *) data;
+    if (header->major == 1 &&
+       header->minor == 0 &&
+       header->header_size == 4)
+    {
+       return TRUE;
+    }
+
+    return FALSE;
+}
+
+static cairo_int_status_t
+_cairo_cff_font_load_opentype_cff (cairo_cff_font_t  *font)
+{
+    const cairo_scaled_font_backend_t *backend = font->backend;
+    cairo_status_t status;
+    tt_head_t head;
+    tt_hhea_t hhea;
+    unsigned long size, data_length;
+
+    if (!backend->load_truetype_table)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    data_length = 0;
+    status = backend->load_truetype_table (font->scaled_font_subset->scaled_font,
+                                           TT_TAG_CFF, 0, NULL, &data_length);
+    if (status)
+        return status;
+
+    size = sizeof (tt_head_t);
+    status = backend->load_truetype_table (font->scaled_font_subset->scaled_font,
+                                           TT_TAG_head, 0,
+                                           (unsigned char *) &head, &size);
+    if (unlikely (status))
+        return status;
+
+    size = sizeof (tt_hhea_t);
+    status = backend->load_truetype_table (font->scaled_font_subset->scaled_font,
+                                           TT_TAG_hhea, 0,
+                                           (unsigned char *) &hhea, &size);
+    if (unlikely (status))
+        return status;
+
+    size = 0;
+    status = backend->load_truetype_table (font->scaled_font_subset->scaled_font,
+                                           TT_TAG_hmtx, 0, NULL, &size);
+    if (unlikely (status))
+        return status;
+
+    font->x_min = (int16_t) be16_to_cpu (head.x_min);
+    font->y_min = (int16_t) be16_to_cpu (head.y_min);
+    font->x_max = (int16_t) be16_to_cpu (head.x_max);
+    font->y_max = (int16_t) be16_to_cpu (head.y_max);
+    font->ascent = (int16_t) be16_to_cpu (hhea.ascender);
+    font->descent = (int16_t) be16_to_cpu (hhea.descender);
+    font->units_per_em = (int16_t) be16_to_cpu (head.units_per_em);
+    if (font->units_per_em == 0)
+        font->units_per_em = 1000;
+
+    font->font_name = NULL;
+    status = _cairo_truetype_read_font_name (font->scaled_font_subset->scaled_font,
+                                            &font->ps_name,
+                                            &font->font_name);
+    if (_cairo_status_is_error (status))
+       return status;
+
+    font->is_opentype = TRUE;
+    font->data_length = data_length;
+    font->data = malloc (data_length);
+    if (unlikely (font->data == NULL))
+        return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font,
+                                                TT_TAG_CFF, 0, font->data,
+                                                &font->data_length);
+    if (unlikely (status))
+        return status;
+
+    if (!check_fontdata_is_cff (font->data, data_length))
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_cff_font_load_cff (cairo_cff_font_t  *font)
+{
+    const cairo_scaled_font_backend_t *backend = font->backend;
+    cairo_status_t status;
+    unsigned long data_length;
+
+    if (!backend->load_type1_data)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    data_length = 0;
+    status = backend->load_type1_data (font->scaled_font_subset->scaled_font,
+                                     0, NULL, &data_length);
+    if (unlikely (status))
+        return status;
+
+    font->font_name = NULL;
+    font->is_opentype = FALSE;
+    font->data_length = data_length;
+    font->data = malloc (data_length);
+    if (unlikely (font->data == NULL))
+        return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    status = font->backend->load_type1_data (font->scaled_font_subset->scaled_font,
+                                            0, font->data, &font->data_length);
+    if (unlikely (status))
+        return status;
+
+    if (!check_fontdata_is_cff (font->data, data_length))
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_cff_font_create (cairo_scaled_font_subset_t  *scaled_font_subset,
+                        cairo_cff_font_t           **font_return,
+                        const char                  *subset_name)
+{
+    const cairo_scaled_font_backend_t *backend;
+    cairo_int_status_t status;
+    cairo_cff_font_t *font;
+
+    backend = scaled_font_subset->scaled_font->backend;
+
+    /* We need to use a fallback font generated from the synthesized outlines. */
+    if (backend->is_synthetic && backend->is_synthetic (scaled_font_subset->scaled_font))
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    font = malloc (sizeof (cairo_cff_font_t));
+    if (unlikely (font == NULL))
+        return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    font->backend = backend;
+    font->scaled_font_subset = scaled_font_subset;
+
+    status = _cairo_cff_font_load_opentype_cff (font);
+    if (status == CAIRO_INT_STATUS_UNSUPPORTED)
+       status = _cairo_cff_font_load_cff (font);
+    if (status)
+       goto fail1;
+
+    font->data_end = font->data + font->data_length;
+    _cairo_array_init (&font->output, sizeof (char));
+    status = _cairo_array_grow_by (&font->output, 4096);
+    if (unlikely (status))
+       goto fail2;
+
+    font->subset_font_name = strdup (subset_name);
+    if (unlikely (font->subset_font_name == NULL)) {
+        status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+       goto fail2;
+    }
+
+    font->widths = calloc (font->scaled_font_subset->num_glyphs, sizeof (int));
+    if (unlikely (font->widths == NULL)) {
+        status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+        goto fail3;
+    }
+
+    if (font->is_opentype) {
+       status = cairo_cff_font_create_set_widths (font);
+       if (unlikely (status))
+           goto fail4;
+    }
+
+    status = cff_dict_init (&font->top_dict);
+    if (unlikely (status))
+       goto fail4;
+
+    status = cff_dict_init (&font->private_dict);
+    if (unlikely (status))
+       goto fail5;
+
+    cff_index_init (&font->strings_index);
+    cff_index_init (&font->charstrings_index);
+    cff_index_init (&font->global_sub_index);
+    cff_index_init (&font->local_sub_index);
+    cff_index_init (&font->charstrings_subset_index);
+    cff_index_init (&font->strings_subset_index);
+    font->euro_sid = 0;
+    font->fdselect = NULL;
+    font->fd_dict = NULL;
+    font->fd_private_dict = NULL;
+    font->fd_local_sub_index = NULL;
+    font->fd_local_sub_bias = NULL;
+    font->fdselect_subset = NULL;
+    font->fd_subset_map = NULL;
+    font->private_dict_offset = NULL;
+    font->global_subs_used = NULL;
+    font->local_subs_used = NULL;
+    font->fd_local_subs_used = NULL;
+
+    *font_return = font;
+
+    return CAIRO_STATUS_SUCCESS;
+
+fail5:
+    _cairo_hash_table_destroy (font->top_dict);
+fail4:
+    free (font->widths);
+fail3:
+    free (font->subset_font_name);
+fail2:
+    free (font->data);
+    free (font->font_name);
+    free (font->ps_name);
+    _cairo_array_fini (&font->output);
+fail1:
+    free (font);
+
+    return status;
+}
+
+static void
+cairo_cff_font_destroy (cairo_cff_font_t *font)
+{
+    unsigned int i;
+
+    free (font->widths);
+    free (font->font_name);
+    free (font->ps_name);
+    free (font->subset_font_name);
+    _cairo_array_fini (&font->output);
+    cff_dict_fini (font->top_dict);
+    cff_dict_fini (font->private_dict);
+    cff_index_fini (&font->strings_index);
+    cff_index_fini (&font->charstrings_index);
+    cff_index_fini (&font->global_sub_index);
+    cff_index_fini (&font->local_sub_index);
+    cff_index_fini (&font->charstrings_subset_index);
+    cff_index_fini (&font->strings_subset_index);
+
+    /* If we bailed out early as a result of an error some of the
+     * following cairo_cff_font_t members may still be NULL */
+    if (font->fd_dict) {
+        for (i = 0; i < font->num_fontdicts; i++) {
+            if (font->fd_dict[i])
+                cff_dict_fini (font->fd_dict[i]);
+        }
+        free (font->fd_dict);
+    }
+    free (font->global_subs_used);
+    free (font->local_subs_used);
+    free (font->fd_subset_map);
+    free (font->private_dict_offset);
+
+    if (font->is_cid) {
+       free (font->fdselect);
+       free (font->fdselect_subset);
+        if (font->fd_private_dict) {
+            for (i = 0; i < font->num_fontdicts; i++) {
+                if (font->fd_private_dict[i])
+                    cff_dict_fini (font->fd_private_dict[i]);
+            }
+            free (font->fd_private_dict);
+        }
+        if (font->fd_local_sub_index) {
+            for (i = 0; i < font->num_fontdicts; i++)
+                cff_index_fini (&font->fd_local_sub_index[i]);
+            free (font->fd_local_sub_index);
+        }
+       free (font->fd_local_sub_bias);
+        if (font->fd_local_subs_used) {
+            for (i = 0; i < font->num_fontdicts; i++) {
+               free (font->fd_local_subs_used[i]);
+            }
+            free (font->fd_local_subs_used);
+        }
+       free (font->fd_default_width);
+       free (font->fd_nominal_width);
+    }
+
+    free (font->data);
+
+    free (font);
+}
+
+cairo_status_t
+_cairo_cff_subset_init (cairo_cff_subset_t          *cff_subset,
+                        const char                 *subset_name,
+                        cairo_scaled_font_subset_t  *font_subset)
+{
+    cairo_cff_font_t *font = NULL; /* squelch bogus compiler warning */
+    cairo_status_t status;
+    const char *data = NULL; /* squelch bogus compiler warning */
+    unsigned long length = 0; /* squelch bogus compiler warning */
+    unsigned int i;
+
+    status = _cairo_cff_font_create (font_subset, &font, subset_name);
+    if (unlikely (status))
+       return status;
+
+    status = cairo_cff_font_generate (font, &data, &length);
+    if (unlikely (status))
+       goto fail1;
+
+    cff_subset->ps_name = strdup (font->ps_name);
+    if (unlikely (cff_subset->ps_name == NULL)) {
+       status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+       goto fail1;
+    }
+
+    if (font->font_name) {
+       cff_subset->family_name_utf8 = strdup (font->font_name);
+       if (cff_subset->family_name_utf8 == NULL) {
+           status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+           goto fail2;
+       }
+    } else {
+       cff_subset->family_name_utf8 = NULL;
+    }
+
+    cff_subset->widths = calloc (sizeof (double), font->scaled_font_subset->num_glyphs);
+    if (unlikely (cff_subset->widths == NULL)) {
+       status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+       goto fail3;
+    }
+    for (i = 0; i < font->scaled_font_subset->num_glyphs; i++)
+        cff_subset->widths[i] = (double)font->widths[i]/font->units_per_em;
+
+    cff_subset->x_min = (double)font->x_min/font->units_per_em;
+    cff_subset->y_min = (double)font->y_min/font->units_per_em;
+    cff_subset->x_max = (double)font->x_max/font->units_per_em;
+    cff_subset->y_max = (double)font->y_max/font->units_per_em;
+    cff_subset->ascent = (double)font->ascent/font->units_per_em;
+    cff_subset->descent = (double)font->descent/font->units_per_em;
+
+    cff_subset->data = malloc (length);
+    if (unlikely (cff_subset->data == NULL)) {
+       status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+       goto fail4;
+    }
+
+    memcpy (cff_subset->data, data, length);
+    cff_subset->data_length = length;
+
+    cairo_cff_font_destroy (font);
+
+    return CAIRO_STATUS_SUCCESS;
+
+ fail4:
+    free (cff_subset->widths);
+ fail3:
+    free (cff_subset->family_name_utf8);
+ fail2:
+    free (cff_subset->ps_name);
+ fail1:
+    cairo_cff_font_destroy (font);
+
+    return status;
+}
+
+void
+_cairo_cff_subset_fini (cairo_cff_subset_t *subset)
+{
+    free (subset->ps_name);
+    free (subset->family_name_utf8);
+    free (subset->widths);
+    free (subset->data);
+}
+
+cairo_bool_t
+_cairo_cff_scaled_font_is_cid_cff (cairo_scaled_font_t *scaled_font)
+{
+    const cairo_scaled_font_backend_t *backend;
+    cairo_int_status_t status;
+    unsigned char *data;
+    unsigned long data_length;
+    unsigned char *current_ptr;
+    unsigned char *data_end;
+    cff_header_t  *header;
+    cff_index_element_t *element;
+    cairo_hash_table_t  *top_dict;
+    cairo_array_t index;
+    int size;
+    cairo_bool_t is_cid = FALSE;
+
+    backend = scaled_font->backend;
+    data = NULL;
+    data_length = 0;
+    status = CAIRO_INT_STATUS_UNSUPPORTED;
+    /* Try to load an OpenType/CFF font */
+    if (backend->load_truetype_table &&
+       (status = backend->load_truetype_table (scaled_font, TT_TAG_CFF,
+                                               0, NULL, &data_length)) == CAIRO_INT_STATUS_SUCCESS)
+    {
+       data = malloc (data_length);
+       if (unlikely (data == NULL)) {
+           status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+           return FALSE;
+       }
+
+       status = backend->load_truetype_table (scaled_font, TT_TAG_CFF,
+                                          0, data, &data_length);
+       if (unlikely (status))
+           goto fail1;
+    }
+    /* Try to load a CFF font */
+    if (status == CAIRO_INT_STATUS_UNSUPPORTED &&
+       backend->load_type1_data &&
+       (status = backend->load_type1_data (scaled_font,
+                                           0, NULL, &data_length)) == CAIRO_INT_STATUS_SUCCESS)
+    {
+       data = malloc (data_length);
+       if (unlikely (data == NULL)) {
+           status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+           return FALSE;
+       }
+
+       status = backend->load_type1_data (scaled_font, 0, data, &data_length);
+       if (unlikely (status))
+           goto fail1;
+    }
+    if (status)
+       goto fail1;
+
+    /* Check if it looks like a CFF font */
+    if (!check_fontdata_is_cff (data, data_length))
+       goto fail1;
+
+    data_end = data + data_length;
+
+    /* skip header */
+    if (data_length < sizeof (cff_header_t))
+        goto fail1;
+
+    header = (cff_header_t *) data;
+    current_ptr = data + header->header_size;
+
+    /* skip name */
+    cff_index_init (&index);
+    status = cff_index_read (&index, &current_ptr, data_end);
+    cff_index_fini (&index);
+
+    if (status)
+        goto fail1;
+
+    /* read top dict */
+    cff_index_init (&index);
+    status = cff_index_read (&index, &current_ptr, data_end);
+    if (unlikely (status))
+        goto fail2;
+
+    status = cff_dict_init (&top_dict);
+    if (unlikely (status))
+       goto fail2;
+
+    element = _cairo_array_index (&index, 0);
+    status = cff_dict_read (top_dict, element->data, element->length);
+    if (unlikely (status))
+        goto fail3;
+
+    /* check for ROS operator indicating a CID font */
+    if (cff_dict_get_operands (top_dict, ROS_OP, &size) != NULL)
+        is_cid = TRUE;
+
+fail3:
+    cff_dict_fini (top_dict);
+
+fail2:
+    cff_index_fini (&index);
+
+fail1:
+    free (data);
+
+    return is_cid;
+}
+
+static cairo_int_status_t
+_cairo_cff_font_fallback_create (cairo_scaled_font_subset_t  *scaled_font_subset,
+                                 cairo_cff_font_t           **font_return,
+                                 const char                  *subset_name)
+{
+    cairo_status_t status;
+    cairo_cff_font_t *font;
+
+    font = malloc (sizeof (cairo_cff_font_t));
+    if (unlikely (font == NULL))
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    font->backend = NULL;
+    font->scaled_font_subset = scaled_font_subset;
+
+    _cairo_array_init (&font->output, sizeof (char));
+    status = _cairo_array_grow_by (&font->output, 4096);
+    if (unlikely (status))
+       goto fail1;
+
+    font->subset_font_name = strdup (subset_name);
+    if (unlikely (font->subset_font_name == NULL)) {
+        status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+       goto fail1;
+    }
+
+    font->ps_name = strdup (subset_name);
+    if (unlikely (font->ps_name == NULL)) {
+        status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+       goto fail2;
+    }
+    font->font_name = NULL;
+
+    font->x_min = 0;
+    font->y_min = 0;
+    font->x_max = 0;
+    font->y_max = 0;
+    font->ascent = 0;
+    font->descent = 0;
+
+    font->widths = calloc (font->scaled_font_subset->num_glyphs, sizeof (int));
+    if (unlikely (font->widths == NULL)) {
+        status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+        goto fail3;
+    }
+
+    font->data_length = 0;
+    font->data = NULL;
+    font->data_end = NULL;
+
+    status = cff_dict_init (&font->top_dict);
+    if (unlikely (status))
+       goto fail4;
+
+    status = cff_dict_init (&font->private_dict);
+    if (unlikely (status))
+       goto fail5;
+
+    cff_index_init (&font->strings_index);
+    cff_index_init (&font->charstrings_index);
+    cff_index_init (&font->global_sub_index);
+    cff_index_init (&font->local_sub_index);
+    cff_index_init (&font->charstrings_subset_index);
+    cff_index_init (&font->strings_subset_index);
+    font->global_subs_used = NULL;
+    font->local_subs_used = NULL;
+    font->fdselect = NULL;
+    font->fd_dict = NULL;
+    font->fd_private_dict = NULL;
+    font->fd_local_sub_index = NULL;
+    font->fdselect_subset = NULL;
+    font->fd_subset_map = NULL;
+    font->private_dict_offset = NULL;
+
+    *font_return = font;
+
+    return CAIRO_STATUS_SUCCESS;
+
+fail5:
+    _cairo_hash_table_destroy (font->top_dict);
+fail4:
+    free (font->widths);
+fail3:
+    free (font->font_name);
+    free (font->ps_name);
+fail2:
+    free (font->subset_font_name);
+fail1:
+    _cairo_array_fini (&font->output);
+    free (font);
+    return status;
+}
+
+static cairo_int_status_t
+cairo_cff_font_fallback_generate (cairo_cff_font_t           *font,
+                                  cairo_type2_charstrings_t  *type2_subset,
+                                  const char                **data,
+                                  unsigned long              *length)
+{
+    cairo_int_status_t status;
+    cff_header_t header;
+    cairo_array_t *charstring;
+    unsigned char buf[40];
+    unsigned char *end_buf, *end_buf2;
+    unsigned int i;
+    int sid;
+
+    /* Create header */
+    header.major = 1;
+    header.minor = 0;
+    header.header_size = 4;
+    header.offset_size = 4;
+    font->header = &header;
+
+    /* Create Top Dict */
+    font->is_cid = FALSE;
+
+    snprintf((char*)buf, sizeof(buf), "CairoFont-%u-%u",
+            font->scaled_font_subset->font_id,
+            font->scaled_font_subset->subset_id);
+    sid = NUM_STD_STRINGS + _cairo_array_num_elements (&font->strings_subset_index);
+    status = cff_index_append_copy (&font->strings_subset_index,
+                                    (unsigned char *)buf,
+                                    strlen((char*)buf));
+    if (unlikely (status))
+       return status;
+
+    end_buf = encode_integer (buf, sid);
+    status = cff_dict_set_operands (font->top_dict, FULLNAME_OP,
+                                   buf, end_buf - buf);
+    if (unlikely (status))
+       return status;
+
+    status = cff_dict_set_operands (font->top_dict, FAMILYNAME_OP,
+                                   buf, end_buf - buf);
+    if (unlikely (status))
+       return status;
+
+    end_buf = encode_integer (buf, type2_subset->x_min);
+    end_buf = encode_integer (end_buf, type2_subset->y_min);
+    end_buf = encode_integer (end_buf, type2_subset->x_max);
+    end_buf = encode_integer (end_buf, type2_subset->y_max);
+    status = cff_dict_set_operands (font->top_dict,
+                                   FONTBBOX_OP, buf, end_buf - buf);
+    if (unlikely (status))
+       return status;
+
+    end_buf = encode_integer_max (buf, 0);
+    status = cff_dict_set_operands (font->top_dict,
+                                   CHARSTRINGS_OP, buf, end_buf - buf);
+    if (unlikely (status))
+       return status;
+
+
+    if (font->scaled_font_subset->is_latin) {
+       status = cff_dict_set_operands (font->top_dict,
+                                        ENCODING_OP, buf, end_buf - buf);
+        if (unlikely (status))
+           return status;
+
+       /* Private has two operands - size and offset */
+       end_buf2 = encode_integer_max (end_buf, 0);
+       cff_dict_set_operands (font->top_dict, PRIVATE_OP, buf, end_buf2 - buf);
+        if (unlikely (status))
+           return status;
+
+    } else {
+       status = cff_dict_set_operands (font->top_dict,
+                                       FDSELECT_OP, buf, end_buf - buf);
+       if (unlikely (status))
+           return status;
+
+       status = cff_dict_set_operands (font->top_dict,
+                                       FDARRAY_OP, buf, end_buf - buf);
+       if (unlikely (status))
+           return status;
+    }
+
+    status = cff_dict_set_operands (font->top_dict,
+                                   CHARSET_OP, buf, end_buf - buf);
+    if (unlikely (status))
+       return status;
+
+    if (!font->scaled_font_subset->is_latin) {
+       status = cairo_cff_font_set_ros_strings (font);
+       if (unlikely (status))
+           return status;
+
+       /* Create CID FD dictionary */
+       status = cairo_cff_font_create_cid_fontdict (font);
+       if (unlikely (status))
+           return status;
+    } else {
+       font->private_dict_offset = malloc (sizeof (int));
+       if (unlikely (font->private_dict_offset == NULL))
+           return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+    }
+
+    /* Create charstrings */
+    for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) {
+        charstring = _cairo_array_index(&type2_subset->charstrings, i);
+
+        status = cff_index_append (&font->charstrings_subset_index,
+                                   _cairo_array_index (charstring, 0),
+                                   _cairo_array_num_elements (charstring));
+
+        if (unlikely (status))
+            return status;
+    }
+
+    if (font->scaled_font_subset->is_latin)
+       status = cairo_cff_font_add_euro_charset_string (font);
+
+    status = cairo_cff_font_write_subset (font);
+    if (unlikely (status))
+        return status;
+
+    *data = _cairo_array_index (&font->output, 0);
+    *length = _cairo_array_num_elements (&font->output);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_status_t
+_cairo_cff_fallback_init (cairo_cff_subset_t          *cff_subset,
+                          const char                 *subset_name,
+                          cairo_scaled_font_subset_t  *font_subset)
+{
+    cairo_cff_font_t *font = NULL; /* squelch bogus compiler warning */
+    cairo_status_t status;
+    const char *data = NULL; /* squelch bogus compiler warning */
+    unsigned long length = 0; /* squelch bogus compiler warning */
+    unsigned int i;
+    cairo_type2_charstrings_t type2_subset;
+
+    status = _cairo_cff_font_fallback_create (font_subset, &font, subset_name);
+    if (unlikely (status))
+       return status;
+
+    status = _cairo_type2_charstrings_init (&type2_subset, font_subset);
+    if (unlikely (status))
+       goto fail1;
+
+    status = cairo_cff_font_fallback_generate (font, &type2_subset, &data, &length);
+    if (unlikely (status))
+       goto fail2;
+
+    cff_subset->family_name_utf8 = NULL;
+    cff_subset->ps_name = strdup (font->ps_name);
+    if (unlikely (cff_subset->ps_name == NULL)) {
+       status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+       goto fail2;
+    }
+
+    cff_subset->widths = calloc (sizeof (double), font->scaled_font_subset->num_glyphs);
+    if (unlikely (cff_subset->widths == NULL)) {
+       status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+       goto fail3;
+    }
+
+    for (i = 0; i < font->scaled_font_subset->num_glyphs; i++)
+        cff_subset->widths[i] = (double)type2_subset.widths[i]/1000;
+
+    cff_subset->x_min = (double)type2_subset.x_min/1000;
+    cff_subset->y_min = (double)type2_subset.y_min/1000;
+    cff_subset->x_max = (double)type2_subset.x_max/1000;
+    cff_subset->y_max = (double)type2_subset.y_max/1000;
+    cff_subset->ascent = (double)type2_subset.y_max/1000;
+    cff_subset->descent = (double)type2_subset.y_min/1000;
+
+    cff_subset->data = malloc (length);
+    if (unlikely (cff_subset->data == NULL)) {
+       status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+       goto fail4;
+    }
+
+    memcpy (cff_subset->data, data, length);
+    cff_subset->data_length = length;
+
+    _cairo_type2_charstrings_fini (&type2_subset);
+    cairo_cff_font_destroy (font);
+
+    return CAIRO_STATUS_SUCCESS;
+
+ fail4:
+    free (cff_subset->widths);
+ fail3:
+    free (cff_subset->ps_name);
+ fail2:
+    _cairo_type2_charstrings_fini (&type2_subset);
+ fail1:
+    cairo_cff_font_destroy (font);
+
+    return status;
+}
+
+void
+_cairo_cff_fallback_fini (cairo_cff_subset_t *subset)
+{
+    free (subset->ps_name);
+    free (subset->widths);
+    free (subset->data);
+}
+
+#endif /* CAIRO_HAS_FONT_SUBSET */
diff --git a/src/cairo-clip-boxes.c b/src/cairo-clip-boxes.c
new file mode 100644 (file)
index 0000000..f42b53d
--- /dev/null
@@ -0,0 +1,595 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ * Copyright © 2005 Red Hat, Inc.
+ * Copyright © 2009 Chris Wilson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *     Carl D. Worth <cworth@cworth.org>
+ *     Kristian Høgsberg <krh@redhat.com>
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-box-private.h"
+#include "cairo-clip-private.h"
+#include "cairo-error-private.h"
+#include "cairo-freed-pool-private.h"
+#include "cairo-gstate-private.h"
+#include "cairo-path-fixed-private.h"
+#include "cairo-pattern-private.h"
+#include "cairo-composite-rectangles-private.h"
+#include "cairo-region-private.h"
+
+static inline int
+pot (int v)
+{
+    v--;
+    v |= v >> 1;
+    v |= v >> 2;
+    v |= v >> 4;
+    v |= v >> 8;
+    v |= v >> 16;
+    v++;
+    return v;
+}
+
+static cairo_bool_t
+_cairo_clip_contains_rectangle_box (const cairo_clip_t *clip,
+                                   const cairo_rectangle_int_t *rect,
+                                   const cairo_box_t *box)
+{
+    int i;
+
+    /* clip == NULL means no clip, so the clip contains everything */
+    if (clip == NULL)
+       return TRUE;
+
+    if (_cairo_clip_is_all_clipped (clip))
+       return FALSE;
+
+    /* If we have a non-trivial path, just say no */
+    if (clip->path)
+       return FALSE;
+
+    if (clip->extents.x > rect->x ||
+       clip->extents.y > rect->y ||
+       clip->extents.x + clip->extents.width  < rect->x + rect->width ||
+       clip->extents.y + clip->extents.height < rect->y + rect->height)
+    {
+       return FALSE;
+    }
+
+    /* Check for a clip-box that wholly contains the rectangle */
+    for (i = 0; i < clip->num_boxes; i++) {
+       if (box->p1.x >= clip->boxes[i].p1.x &&
+           box->p1.y >= clip->boxes[i].p1.y &&
+           box->p2.x <= clip->boxes[i].p2.x &&
+           box->p2.y <= clip->boxes[i].p2.y)
+       {
+           return TRUE;
+       }
+    }
+
+    return FALSE;
+}
+
+cairo_bool_t
+_cairo_clip_contains_box (const cairo_clip_t *clip,
+                         const cairo_box_t *box)
+{
+    cairo_rectangle_int_t rect;
+
+    _cairo_box_round_to_rectangle (box, &rect);
+    return _cairo_clip_contains_rectangle_box(clip, &rect, box);
+}
+
+cairo_bool_t
+_cairo_clip_contains_rectangle (const cairo_clip_t *clip,
+                               const cairo_rectangle_int_t *rect)
+{
+    cairo_box_t box;
+
+    box.p1.x = _cairo_fixed_from_int (rect->x);
+    box.p1.y = _cairo_fixed_from_int (rect->y);
+    box.p2.x = _cairo_fixed_from_int (rect->x + rect->width);
+    box.p2.y = _cairo_fixed_from_int (rect->y + rect->height);
+
+    return _cairo_clip_contains_rectangle_box (clip, rect, &box);
+}
+
+cairo_clip_t *
+_cairo_clip_intersect_rectilinear_path (cairo_clip_t *clip,
+                                       const cairo_path_fixed_t *path,
+                                       cairo_fill_rule_t fill_rule,
+                                       cairo_antialias_t antialias)
+{
+    cairo_status_t status;
+    cairo_boxes_t boxes;
+
+    _cairo_boxes_init (&boxes);
+    status = _cairo_path_fixed_fill_rectilinear_to_boxes (path,
+                                                         fill_rule,
+                                                         antialias,
+                                                         &boxes);
+    if (likely (status == CAIRO_STATUS_SUCCESS && boxes.num_boxes))
+       clip = _cairo_clip_intersect_boxes (clip, &boxes);
+    else
+       clip = _cairo_clip_set_all_clipped (clip);
+    _cairo_boxes_fini (&boxes);
+
+    return clip;
+}
+
+static cairo_clip_t *
+_cairo_clip_intersect_rectangle_box (cairo_clip_t *clip,
+                                    const cairo_rectangle_int_t *r,
+                                    const cairo_box_t *box)
+{
+    cairo_box_t extents_box;
+    cairo_bool_t changed = FALSE;
+    int i, j;
+
+    if (clip == NULL) {
+       clip = _cairo_clip_create ();
+       if (clip == NULL)
+           return _cairo_clip_set_all_clipped (clip);
+    }
+
+    if (clip->num_boxes == 0) {
+       clip->boxes = &clip->embedded_box;
+       clip->boxes[0] = *box;
+       clip->num_boxes = 1;
+       if (clip->path == NULL) {
+           clip->extents = *r;
+       } else {
+           if (! _cairo_rectangle_intersect (&clip->extents, r))
+               clip = _cairo_clip_set_all_clipped (clip);
+       }
+       if (clip->path == NULL)
+           clip->is_region = _cairo_box_is_pixel_aligned (box);
+       return clip;
+    }
+
+    /* Does the new box wholly subsume the clip? Perform a cheap check
+     * for the common condition of a single clip rectangle.
+     */
+    if (clip->num_boxes == 1 &&
+       clip->boxes[0].p1.x >= box->p1.x &&
+       clip->boxes[0].p1.y >= box->p1.y &&
+       clip->boxes[0].p2.x <= box->p2.x &&
+       clip->boxes[0].p2.y <= box->p2.y)
+    {
+       return clip;
+    }
+
+    for (i = j = 0; i < clip->num_boxes; i++) {
+       cairo_box_t *b = &clip->boxes[j];
+
+       if (j != i)
+           *b = clip->boxes[i];
+
+       if (box->p1.x > b->p1.x)
+           b->p1.x = box->p1.x, changed = TRUE;
+       if (box->p2.x < b->p2.x)
+           b->p2.x = box->p2.x, changed = TRUE;
+
+       if (box->p1.y > b->p1.y)
+           b->p1.y = box->p1.y, changed = TRUE;
+       if (box->p2.y < b->p2.y)
+           b->p2.y = box->p2.y, changed = TRUE;
+
+       j += b->p2.x > b->p1.x && b->p2.y > b->p1.y;
+    }
+    clip->num_boxes = j;
+
+    if (clip->num_boxes == 0)
+       return _cairo_clip_set_all_clipped (clip);
+
+    if (! changed)
+       return clip;
+
+    extents_box = clip->boxes[0];
+    for (i = 1; i < clip->num_boxes; i++) {
+           if (clip->boxes[i].p1.x < extents_box.p1.x)
+               extents_box.p1.x = clip->boxes[i].p1.x;
+
+           if (clip->boxes[i].p1.y < extents_box.p1.y)
+               extents_box.p1.y = clip->boxes[i].p1.y;
+
+           if (clip->boxes[i].p2.x > extents_box.p2.x)
+               extents_box.p2.x = clip->boxes[i].p2.x;
+
+           if (clip->boxes[i].p2.y > extents_box.p2.y)
+               extents_box.p2.y = clip->boxes[i].p2.y;
+    }
+
+    if (clip->path == NULL) {
+       _cairo_box_round_to_rectangle (&extents_box, &clip->extents);
+    } else {
+       cairo_rectangle_int_t extents_rect;
+
+       _cairo_box_round_to_rectangle (&extents_box, &extents_rect);
+       if (! _cairo_rectangle_intersect (&clip->extents, &extents_rect))
+           return _cairo_clip_set_all_clipped (clip);
+    }
+
+    if (clip->region) {
+       cairo_region_destroy (clip->region);
+       clip->region = NULL;
+    }
+
+    clip->is_region = FALSE;
+    return clip;
+}
+
+cairo_clip_t *
+_cairo_clip_intersect_box (cairo_clip_t *clip,
+                          const cairo_box_t *box)
+{
+    cairo_rectangle_int_t r;
+
+    _cairo_box_round_to_rectangle (box, &r);
+    if (r.width == 0 || r.height == 0)
+       return _cairo_clip_set_all_clipped (clip);
+
+    return _cairo_clip_intersect_rectangle_box (clip, &r, box);
+}
+
+cairo_clip_t *
+_cairo_clip_intersect_boxes (cairo_clip_t *clip,
+                            const cairo_boxes_t *boxes)
+{
+    cairo_boxes_t clip_boxes;
+    cairo_box_t limits;
+    cairo_rectangle_int_t extents;
+
+    if (_cairo_clip_is_all_clipped (clip))
+       return clip;
+
+    if (boxes->num_boxes == 0)
+       return _cairo_clip_set_all_clipped (clip);
+
+    if (boxes->num_boxes == 1)
+       return _cairo_clip_intersect_box (clip, boxes->chunks.base);
+
+    if (clip == NULL)
+       clip = _cairo_clip_create ();
+
+    if (clip->num_boxes) {
+       _cairo_boxes_init_for_array (&clip_boxes, clip->boxes, clip->num_boxes);
+       if (unlikely (_cairo_boxes_intersect (&clip_boxes, boxes, &clip_boxes))) {
+           clip = _cairo_clip_set_all_clipped (clip);
+           goto out;
+       }
+
+       if (clip->boxes != &clip->embedded_box)
+           free (clip->boxes);
+
+       clip->boxes = NULL;
+       boxes = &clip_boxes;
+    }
+
+    if (boxes->num_boxes == 0) {
+       clip = _cairo_clip_set_all_clipped (clip);
+       goto out;
+    } else if (boxes->num_boxes == 1) {
+       clip->boxes = &clip->embedded_box;
+       clip->boxes[0] = boxes->chunks.base[0];
+       clip->num_boxes = 1;
+    } else {
+       clip->boxes = _cairo_boxes_to_array (boxes, &clip->num_boxes, TRUE);
+    }
+    _cairo_boxes_extents (boxes, &limits);
+
+    _cairo_box_round_to_rectangle (&limits, &extents);
+    if (clip->path == NULL)
+       clip->extents = extents;
+    else if (! _cairo_rectangle_intersect (&clip->extents, &extents))
+       clip = _cairo_clip_set_all_clipped (clip);
+
+    if (clip->region) {
+       cairo_region_destroy (clip->region);
+       clip->region = NULL;
+    }
+    clip->is_region = FALSE;
+
+out:
+    if (boxes == &clip_boxes)
+       _cairo_boxes_fini (&clip_boxes);
+
+    return clip;
+}
+
+cairo_clip_t *
+_cairo_clip_intersect_rectangle (cairo_clip_t       *clip,
+                                const cairo_rectangle_int_t *r)
+{
+    cairo_box_t box;
+
+    if (_cairo_clip_is_all_clipped (clip))
+       return clip;
+
+    if (r->width == 0 || r->height == 0)
+       return _cairo_clip_set_all_clipped (clip);
+
+    box.p1.x = _cairo_fixed_from_int (r->x);
+    box.p1.y = _cairo_fixed_from_int (r->y);
+    box.p2.x = _cairo_fixed_from_int (r->x + r->width);
+    box.p2.y = _cairo_fixed_from_int (r->y + r->height);
+
+    return _cairo_clip_intersect_rectangle_box (clip, r, &box);
+}
+
+struct reduce {
+    cairo_clip_t *clip;
+    cairo_box_t limit;
+    cairo_box_t extents;
+    cairo_bool_t inside;
+
+    cairo_point_t current_point;
+    cairo_point_t last_move_to;
+};
+
+static void
+_add_clipped_edge (struct reduce *r,
+                  const cairo_point_t *p1,
+                  const cairo_point_t *p2,
+                  int y1, int y2)
+{
+    cairo_fixed_t x;
+
+    x = _cairo_edge_compute_intersection_x_for_y (p1, p2, y1);
+    if (x < r->extents.p1.x)
+       r->extents.p1.x = x;
+
+    x = _cairo_edge_compute_intersection_x_for_y (p1, p2, y2);
+    if (x > r->extents.p2.x)
+       r->extents.p2.x = x;
+
+    if (y1 < r->extents.p1.y)
+       r->extents.p1.y = y1;
+
+    if (y2 > r->extents.p2.y)
+       r->extents.p2.y = y2;
+
+    r->inside = TRUE;
+}
+
+static void
+_add_edge (struct reduce *r,
+          const cairo_point_t *p1,
+          const cairo_point_t *p2)
+{
+    int top, bottom;
+    int top_y, bot_y;
+    int n;
+
+    if (p1->y < p2->y) {
+       top = p1->y;
+       bottom = p2->y;
+    } else {
+       top = p2->y;
+       bottom = p1->y;
+    }
+
+    if (bottom < r->limit.p1.y || top > r->limit.p2.y)
+       return;
+
+    if (p1->x > p2->x) {
+       const cairo_point_t *t = p1;
+       p1 = p2;
+       p2 = t;
+    }
+
+    if (p2->x <= r->limit.p1.x || p1->x >= r->limit.p2.x)
+       return;
+
+    for (n = 0; n < r->clip->num_boxes; n++) {
+       const cairo_box_t *limits = &r->clip->boxes[n];
+
+       if (bottom < limits->p1.y || top > limits->p2.y)
+           continue;
+
+       if (p2->x <= limits->p1.x || p1->x >= limits->p2.x)
+           continue;
+
+       if (p1->x >= limits->p1.x && p2->x <= limits->p1.x) {
+           top_y = top;
+           bot_y = bottom;
+       } else {
+           int p1_y, p2_y;
+
+           p1_y = _cairo_edge_compute_intersection_y_for_x (p1, p2,
+                                                            limits->p1.x);
+           p2_y = _cairo_edge_compute_intersection_y_for_x (p1, p2,
+                                                            limits->p2.x);
+           if (p1_y < p2_y) {
+               top_y = p1_y;
+               bot_y = p2_y;
+           } else {
+               top_y = p2_y;
+               bot_y = p1_y;
+           }
+
+           if (top_y < top)
+               top_y = top;
+           if (bot_y > bottom)
+               bot_y = bottom;
+       }
+
+       if (top_y < limits->p1.y)
+           top_y = limits->p1.y;
+
+       if (bot_y > limits->p2.y)
+           bot_y = limits->p2.y;
+       if (bot_y > top_y)
+           _add_clipped_edge (r, p1, p2, top_y, bot_y);
+    }
+}
+
+static cairo_status_t
+_reduce_line_to (void *closure,
+                      const cairo_point_t *point)
+{
+    struct reduce *r = closure;
+
+    _add_edge (r, &r->current_point, point);
+    r->current_point = *point;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_reduce_close (void *closure)
+{
+    struct reduce *r = closure;
+
+    return _reduce_line_to (r, &r->last_move_to);
+}
+
+static cairo_status_t
+_reduce_move_to (void *closure,
+                const cairo_point_t *point)
+{
+    struct reduce *r = closure;
+    cairo_status_t status;
+
+    /* close current subpath */
+    status = _reduce_close (closure);
+
+    /* make sure that the closure represents a degenerate path */
+    r->current_point = *point;
+    r->last_move_to = *point;
+
+    return status;
+}
+
+static cairo_clip_t *
+_cairo_clip_reduce_to_boxes (cairo_clip_t *clip)
+{
+    struct reduce r;
+    cairo_clip_path_t *clip_path;
+    cairo_status_t status;
+
+       return clip;
+    if (clip->path == NULL)
+       return clip;
+
+    r.clip = clip;
+    r.extents.p1.x = r.extents.p1.y = INT_MAX;
+    r.extents.p2.x = r.extents.p2.y = INT_MIN;
+    r.inside = FALSE;
+
+    r.limit.p1.x = _cairo_fixed_from_int (clip->extents.x);
+    r.limit.p1.y = _cairo_fixed_from_int (clip->extents.y);
+    r.limit.p2.x = _cairo_fixed_from_int (clip->extents.x + clip->extents.width);
+    r.limit.p2.y = _cairo_fixed_from_int (clip->extents.y + clip->extents.height);
+
+    clip_path = clip->path;
+    do {
+       r.current_point.x = 0;
+       r.current_point.y = 0;
+       r.last_move_to = r.current_point;
+
+       status = _cairo_path_fixed_interpret_flat (&clip_path->path,
+                                                  _reduce_move_to,
+                                                  _reduce_line_to,
+                                                  _reduce_close,
+                                                  &r,
+                                                  clip_path->tolerance);
+       assert (status == CAIRO_STATUS_SUCCESS);
+       _reduce_close (&r);
+    } while ((clip_path = clip_path->prev));
+
+    if (! r.inside) {
+       _cairo_clip_path_destroy (clip->path);
+       clip->path = NULL;
+    }
+
+    return _cairo_clip_intersect_box (clip, &r.extents);
+}
+
+cairo_clip_t *
+_cairo_clip_reduce_to_rectangle (const cairo_clip_t *clip,
+                                const cairo_rectangle_int_t *r)
+{
+    cairo_clip_t *copy;
+
+    if (_cairo_clip_is_all_clipped (clip))
+       return (cairo_clip_t *) clip;
+
+    if (_cairo_clip_contains_rectangle (clip, r))
+       return _cairo_clip_intersect_rectangle (NULL, r);
+
+    copy = _cairo_clip_copy_intersect_rectangle (clip, r);
+    if (_cairo_clip_is_all_clipped (copy))
+       return copy;
+
+    return _cairo_clip_reduce_to_boxes (copy);
+}
+
+cairo_clip_t *
+_cairo_clip_reduce_for_composite (const cairo_clip_t *clip,
+                                 cairo_composite_rectangles_t *extents)
+{
+    const cairo_rectangle_int_t *r;
+
+    r = extents->is_bounded ? &extents->bounded : &extents->unbounded;
+    return _cairo_clip_reduce_to_rectangle (clip, r);
+}
+
+cairo_clip_t *
+_cairo_clip_from_boxes (const cairo_boxes_t *boxes)
+{
+    cairo_box_t extents;
+    cairo_clip_t *clip = _cairo_clip_create ();
+    if (clip == NULL)
+       return _cairo_clip_set_all_clipped (clip);
+
+    /* XXX cow-boxes? */
+    if(boxes->num_boxes == 1) {
+       clip->boxes = &clip->embedded_box;
+       clip->boxes[0] = boxes->chunks.base[0];
+       clip->num_boxes = 1;
+    } else {
+       clip->boxes = _cairo_boxes_to_array (boxes, &clip->num_boxes, TRUE);
+       if (clip->boxes == NULL)
+           return _cairo_clip_set_all_clipped (clip);
+    }
+
+    _cairo_boxes_extents (boxes, &extents);
+    _cairo_box_round_to_rectangle (&extents, &clip->extents);
+
+    return clip;
+}
diff --git a/src/cairo-clip-polygon.c b/src/cairo-clip-polygon.c
new file mode 100644 (file)
index 0000000..0e1968d
--- /dev/null
@@ -0,0 +1,144 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2011 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+#include "cairo-clip-private.h"
+#include "cairo-error-private.h"
+#include "cairo-freed-pool-private.h"
+#include "cairo-gstate-private.h"
+#include "cairo-path-fixed-private.h"
+#include "cairo-pattern-private.h"
+#include "cairo-composite-rectangles-private.h"
+#include "cairo-region-private.h"
+
+static cairo_bool_t
+can_convert_to_polygon (const cairo_clip_t *clip)
+{
+    cairo_clip_path_t *clip_path = clip->path;
+    cairo_antialias_t antialias = clip_path->antialias;
+
+    while ((clip_path = clip_path->prev) != NULL) {
+       if (clip_path->antialias != antialias)
+           return FALSE;
+    }
+
+    return TRUE;
+}
+
+cairo_int_status_t
+_cairo_clip_get_polygon (const cairo_clip_t *clip,
+                        cairo_polygon_t *polygon,
+                        cairo_fill_rule_t *fill_rule,
+                        cairo_antialias_t *antialias)
+{
+    cairo_status_t status;
+    cairo_clip_path_t *clip_path;
+
+    if (_cairo_clip_is_all_clipped (clip)) {
+       _cairo_polygon_init (polygon, NULL, 0);
+       return CAIRO_INT_STATUS_SUCCESS;
+    }
+
+    /* If there is no clip, we need an infinite polygon */
+    assert (clip && (clip->path || clip->num_boxes));
+
+    if (clip->path == NULL) {
+       *fill_rule = CAIRO_FILL_RULE_WINDING;
+       *antialias = CAIRO_ANTIALIAS_DEFAULT;
+       return _cairo_polygon_init_box_array (polygon,
+                                             clip->boxes,
+                                             clip->num_boxes);
+    }
+
+    /* check that residual is all of the same type/tolerance */
+    if (! can_convert_to_polygon (clip))
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    _cairo_polygon_init_with_clip (polygon, clip);
+
+    clip_path = clip->path;
+    status = _cairo_path_fixed_fill_to_polygon (&clip_path->path,
+                                               clip_path->tolerance,
+                                               polygon);
+    if (unlikely (status)) {
+       _cairo_polygon_fini (polygon);
+       return status;
+    }
+
+    polygon->limits = NULL;
+    polygon->num_limits = 0;
+
+    *fill_rule = clip_path->fill_rule;
+    *antialias = clip_path->antialias;
+    while ((clip_path = clip_path->prev) != NULL) {
+       cairo_polygon_t next;
+
+       _cairo_polygon_init (&next, NULL, 0);
+       status = _cairo_path_fixed_fill_to_polygon (&clip_path->path,
+                                                   clip_path->tolerance,
+                                                   &next);
+       if (likely (status == CAIRO_STATUS_SUCCESS))
+               status = _cairo_polygon_intersect (polygon, *fill_rule,
+                                                  &next, clip_path->fill_rule);
+       _cairo_polygon_fini (&next);
+       if (unlikely (status)) {
+           _cairo_polygon_fini (polygon);
+           return status;
+       }
+
+       *fill_rule = CAIRO_FILL_RULE_WINDING;
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_bool_t
+_cairo_clip_is_polygon (const cairo_clip_t *clip)
+{
+    if (_cairo_clip_is_all_clipped (clip))
+       return TRUE;
+
+    /* If there is no clip, we need an infinite polygon */
+    if (clip == NULL)
+       return FALSE;
+
+    if (clip->path == NULL)
+       return TRUE;
+
+    /* check that residual is all of the same type/tolerance */
+    return can_convert_to_polygon (clip);
+}
diff --git a/src/cairo-clip-private.h b/src/cairo-clip-private.h
new file mode 100644 (file)
index 0000000..aaf16f3
--- /dev/null
@@ -0,0 +1,234 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ *     Kristian Høgsberg <krh@redhat.com>
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#ifndef CAIRO_CLIP_PRIVATE_H
+#define CAIRO_CLIP_PRIVATE_H
+
+#include "cairo-types-private.h"
+
+#include "cairo-boxes-private.h"
+#include "cairo-error-private.h"
+#include "cairo-compiler-private.h"
+#include "cairo-error-private.h"
+#include "cairo-path-fixed-private.h"
+#include "cairo-reference-count-private.h"
+
+extern const cairo_private cairo_rectangle_list_t _cairo_rectangles_nil;
+
+struct _cairo_clip_path {
+    cairo_reference_count_t     ref_count;
+    cairo_path_fixed_t          path;
+    cairo_fill_rule_t           fill_rule;
+    double                      tolerance;
+    cairo_antialias_t           antialias;
+    cairo_clip_path_t          *prev;
+};
+
+struct _cairo_clip {
+    cairo_rectangle_int_t extents;
+    cairo_clip_path_t *path;
+
+    cairo_box_t *boxes;
+    int num_boxes;
+
+    cairo_region_t *region;
+    cairo_bool_t is_region;
+
+    cairo_box_t embedded_box;
+};
+
+cairo_private cairo_clip_t *
+_cairo_clip_create (void);
+
+cairo_private void
+_cairo_clip_path_destroy (cairo_clip_path_t *clip_path);
+
+cairo_private void
+_cairo_clip_destroy (cairo_clip_t *clip);
+
+cairo_private extern const cairo_clip_t __cairo_clip_all;
+
+static inline cairo_bool_t _cairo_clip_is_all_clipped(const cairo_clip_t *clip)
+{
+    return clip == &__cairo_clip_all;
+}
+
+static inline cairo_clip_t *
+_cairo_clip_set_all_clipped (cairo_clip_t *clip)
+{
+    _cairo_clip_destroy (clip);
+    return (cairo_clip_t *) &__cairo_clip_all;
+}
+
+cairo_private cairo_clip_t *
+_cairo_clip_copy (const cairo_clip_t *clip);
+
+cairo_private cairo_clip_t *
+_cairo_clip_copy_region (const cairo_clip_t *clip);
+
+cairo_private cairo_clip_t *
+_cairo_clip_translate (cairo_clip_t *clip, int tx, int ty);
+
+cairo_private cairo_clip_t *
+_cairo_clip_transform (cairo_clip_t *clip, const cairo_matrix_t *m);
+
+cairo_private cairo_clip_t *
+_cairo_clip_copy_with_translation (const cairo_clip_t *clip, int tx, int ty);
+
+cairo_private cairo_bool_t
+_cairo_clip_equal (const cairo_clip_t *clip_a,
+                  const cairo_clip_t *clip_b);
+
+cairo_private cairo_clip_t *
+_cairo_clip_intersect_rectangle (cairo_clip_t       *clip,
+                                const cairo_rectangle_int_t *rectangle);
+
+static inline cairo_clip_t *
+_cairo_clip_copy_intersect_rectangle (const cairo_clip_t       *clip,
+                                     const cairo_rectangle_int_t *r)
+{
+    return _cairo_clip_intersect_rectangle (_cairo_clip_copy (clip), r);
+}
+
+cairo_private cairo_clip_t *
+_cairo_clip_intersect_clip (cairo_clip_t *clip,
+                           const cairo_clip_t *other);
+
+static inline cairo_clip_t *
+_cairo_clip_copy_intersect_clip (const cairo_clip_t *clip,
+                                const cairo_clip_t *other)
+{
+    return _cairo_clip_intersect_clip (_cairo_clip_copy (clip), other);
+}
+
+
+cairo_private cairo_clip_t *
+_cairo_clip_intersect_box (cairo_clip_t       *clip,
+                          const cairo_box_t *box);
+
+cairo_private cairo_clip_t *
+_cairo_clip_intersect_boxes (cairo_clip_t *clip,
+                            const cairo_boxes_t *boxes);
+
+cairo_private cairo_clip_t *
+_cairo_clip_intersect_rectilinear_path (cairo_clip_t       *clip,
+                                       const cairo_path_fixed_t *path,
+                                       cairo_fill_rule_t   fill_rule,
+                                       cairo_antialias_t   antialias);
+
+cairo_private cairo_clip_t *
+_cairo_clip_intersect_path (cairo_clip_t       *clip,
+                           const cairo_path_fixed_t *path,
+                           cairo_fill_rule_t   fill_rule,
+                           double              tolerance,
+                           cairo_antialias_t   antialias);
+
+cairo_private const cairo_rectangle_int_t *
+_cairo_clip_get_extents (const cairo_clip_t *clip);
+
+cairo_private cairo_surface_t *
+_cairo_clip_get_surface (const cairo_clip_t *clip, cairo_surface_t *dst, int *tx, int *ty);
+
+cairo_private cairo_surface_t *
+_cairo_clip_get_image (const cairo_clip_t *clip,
+                      cairo_surface_t *target,
+                      const cairo_rectangle_int_t *extents);
+
+cairo_private cairo_status_t
+_cairo_clip_combine_with_surface (const cairo_clip_t *clip,
+                                 cairo_surface_t *dst,
+                                 int dst_x, int dst_y);
+
+static inline void
+_cairo_clip_steal_boxes (cairo_clip_t *clip, cairo_boxes_t *boxes)
+{
+    _cairo_boxes_init_for_array (boxes, clip->boxes, clip->num_boxes);
+    clip->boxes = NULL;
+    clip->num_boxes = 0;
+}
+
+static inline void
+_cairo_clip_unsteal_boxes (cairo_clip_t *clip, cairo_boxes_t *boxes)
+{
+    clip->boxes = boxes->chunks.base;
+    clip->num_boxes = boxes->num_boxes;
+}
+
+cairo_private cairo_clip_t *
+_cairo_clip_from_boxes (const cairo_boxes_t *boxes);
+
+cairo_private cairo_region_t *
+_cairo_clip_get_region (const cairo_clip_t *clip);
+
+cairo_private cairo_bool_t
+_cairo_clip_is_region (const cairo_clip_t *clip);
+
+cairo_private cairo_clip_t *
+_cairo_clip_reduce_to_rectangle (const cairo_clip_t *clip,
+                                const cairo_rectangle_int_t *r);
+
+cairo_private cairo_clip_t *
+_cairo_clip_reduce_for_composite (const cairo_clip_t *clip,
+                                 cairo_composite_rectangles_t *extents);
+
+cairo_private cairo_bool_t
+_cairo_clip_contains_rectangle (const cairo_clip_t *clip,
+                               const cairo_rectangle_int_t *rect);
+
+cairo_private cairo_bool_t
+_cairo_clip_contains_box (const cairo_clip_t *clip,
+                         const cairo_box_t *box);
+
+cairo_private cairo_bool_t
+_cairo_clip_contains_extents (const cairo_clip_t *clip,
+                             const cairo_composite_rectangles_t *extents);
+
+cairo_private cairo_rectangle_list_t*
+_cairo_clip_copy_rectangle_list (cairo_clip_t *clip, cairo_gstate_t *gstate);
+
+cairo_private cairo_rectangle_list_t *
+_cairo_rectangle_list_create_in_error (cairo_status_t status);
+
+cairo_private cairo_bool_t
+_cairo_clip_is_polygon (const cairo_clip_t *clip);
+
+cairo_private cairo_int_status_t
+_cairo_clip_get_polygon (const cairo_clip_t *clip,
+                        cairo_polygon_t *polygon,
+                        cairo_fill_rule_t *fill_rule,
+                        cairo_antialias_t *antialias);
+
+#endif /* CAIRO_CLIP_PRIVATE_H */
diff --git a/src/cairo-clip-region.c b/src/cairo-clip-region.c
new file mode 100644 (file)
index 0000000..e3f4891
--- /dev/null
@@ -0,0 +1,123 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ * Copyright © 2005 Red Hat, Inc.
+ * Copyright © 2009 Chris Wilson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *     Carl D. Worth <cworth@cworth.org>
+ *     Kristian Høgsberg <krh@redhat.com>
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+#include "cairo-clip-private.h"
+#include "cairo-error-private.h"
+#include "cairo-freed-pool-private.h"
+#include "cairo-gstate-private.h"
+#include "cairo-path-fixed-private.h"
+#include "cairo-pattern-private.h"
+#include "cairo-composite-rectangles-private.h"
+#include "cairo-region-private.h"
+
+static void
+_cairo_clip_extract_region (cairo_clip_t *clip)
+{
+    cairo_rectangle_int_t stack_rects[CAIRO_STACK_ARRAY_LENGTH (cairo_rectangle_int_t)];
+    cairo_rectangle_int_t *r = stack_rects;
+    cairo_bool_t is_region;
+    int i;
+
+    if (clip->num_boxes == 0)
+       return;
+
+    if (clip->num_boxes > ARRAY_LENGTH (stack_rects)) {
+       r = _cairo_malloc_ab (clip->num_boxes, sizeof (cairo_rectangle_int_t));
+       if (r == NULL){
+           _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+           return;
+       }
+    }
+
+    is_region = clip->path == NULL;
+    for (i = 0; i < clip->num_boxes; i++) {
+       cairo_box_t *b = &clip->boxes[i];
+       if (is_region)
+           is_region =
+               _cairo_fixed_is_integer (b->p1.x | b->p1.y |  b->p2.x | b->p2.y);
+       r[i].x = _cairo_fixed_integer_floor (b->p1.x);
+       r[i].y = _cairo_fixed_integer_floor (b->p1.y);
+       r[i].width  = _cairo_fixed_integer_ceil (b->p2.x) - r[i].x;
+       r[i].height = _cairo_fixed_integer_ceil (b->p2.y) - r[i].y;
+    }
+    clip->is_region = is_region;
+
+    clip->region = cairo_region_create_rectangles (r, i);
+
+    if (r != stack_rects)
+       free (r);
+}
+
+cairo_region_t *
+_cairo_clip_get_region (const cairo_clip_t *clip)
+{
+    if (clip == NULL)
+       return NULL;
+
+    if (clip->region == NULL)
+       _cairo_clip_extract_region ((cairo_clip_t *) clip);
+
+    return clip->region;
+}
+
+cairo_bool_t
+_cairo_clip_is_region (const cairo_clip_t *clip)
+{
+    if (clip == NULL)
+       return TRUE;
+
+    if (clip->is_region)
+       return TRUE;
+
+    /* XXX Geometric reduction? */
+
+    if (clip->path)
+       return FALSE;
+
+    if (clip->num_boxes == 0)
+       return TRUE;
+
+    if (clip->region == NULL)
+       _cairo_clip_extract_region ((cairo_clip_t *) clip);
+
+    return clip->is_region;
+}
diff --git a/src/cairo-clip-surface.c b/src/cairo-clip-surface.c
new file mode 100644 (file)
index 0000000..244e7f2
--- /dev/null
@@ -0,0 +1,170 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ * Copyright © 2005 Red Hat, Inc.
+ * Copyright © 2009 Chris Wilson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *     Carl D. Worth <cworth@cworth.org>
+ *     Kristian Høgsberg <krh@redhat.com>
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+#include "cairo-clip-private.h"
+#include "cairo-error-private.h"
+#include "cairo-freed-pool-private.h"
+#include "cairo-gstate-private.h"
+#include "cairo-path-fixed-private.h"
+#include "cairo-pattern-private.h"
+#include "cairo-composite-rectangles-private.h"
+#include "cairo-region-private.h"
+
+cairo_status_t
+_cairo_clip_combine_with_surface (const cairo_clip_t *clip,
+                                 cairo_surface_t *dst,
+                                 int dst_x, int dst_y)
+{
+    cairo_clip_path_t *clip_path;
+    cairo_clip_path_t *copy_path;
+    cairo_clip_t *copy;
+    cairo_status_t status = CAIRO_STATUS_SUCCESS;
+
+    copy = _cairo_clip_copy_with_translation (clip, -dst_x, -dst_y);
+    copy_path = copy->path;
+    copy->path = NULL;
+
+    if (copy_path == NULL) {
+       assert (copy->num_boxes);
+       status = _cairo_surface_paint (dst,
+                                      CAIRO_OPERATOR_IN,
+                                      &_cairo_pattern_white.base,
+                                      copy);
+    }
+
+    clip_path = copy_path;
+    while (status == CAIRO_STATUS_SUCCESS && clip_path) {
+       status = _cairo_surface_fill (dst,
+                                     CAIRO_OPERATOR_IN,
+                                     &_cairo_pattern_white.base,
+                                     &clip_path->path,
+                                     clip_path->fill_rule,
+                                     clip_path->tolerance,
+                                     clip_path->antialias,
+                                     copy);
+       clip_path = clip_path->prev;
+    }
+
+    copy->path = copy_path;
+    _cairo_clip_destroy (copy);
+    return status;
+}
+
+cairo_surface_t *
+_cairo_clip_get_surface (const cairo_clip_t *clip,
+                        cairo_surface_t *target,
+                        int *tx, int *ty)
+{
+    cairo_surface_t *surface;
+    cairo_status_t status;
+    cairo_clip_t *copy;
+    cairo_clip_path_t *copy_path, *clip_path;
+
+    surface = _cairo_surface_create_similar_solid (target,
+                                                  CAIRO_CONTENT_ALPHA,
+                                                  clip->extents.width,
+                                                  clip->extents.height,
+                                                  CAIRO_COLOR_WHITE);
+    if (unlikely (surface->status))
+       return surface;
+
+    copy = _cairo_clip_copy_with_translation (clip,
+                                             -clip->extents.x,
+                                             -clip->extents.y);
+    copy_path = copy->path;
+    copy->path = NULL;
+
+    status = CAIRO_STATUS_SUCCESS;
+    clip_path = copy_path;
+    while (status == CAIRO_STATUS_SUCCESS && clip_path) {
+       status = _cairo_surface_fill (surface,
+                                     CAIRO_OPERATOR_IN,
+                                     &_cairo_pattern_white.base,
+                                     &clip_path->path,
+                                     clip_path->fill_rule,
+                                     clip_path->tolerance,
+                                     clip_path->antialias,
+                                     copy);
+       clip_path = clip_path->prev;
+    }
+
+    copy->path = copy_path;
+    _cairo_clip_destroy (copy);
+
+    if (unlikely (status)) {
+       cairo_surface_destroy (surface);
+       return _cairo_surface_create_in_error (status);
+    }
+
+    *tx = clip->extents.x;
+    *ty = clip->extents.y;
+    return surface;
+}
+
+cairo_surface_t *
+_cairo_clip_get_image (const cairo_clip_t *clip,
+                      cairo_surface_t *target,
+                      const cairo_rectangle_int_t *extents)
+{
+    cairo_surface_t *surface;
+    cairo_status_t status;
+
+    surface = cairo_surface_create_similar_image (target,
+                                                 CAIRO_FORMAT_A8,
+                                                 extents->width,
+                                                 extents->height);
+    if (unlikely (surface->status))
+       return surface;
+
+    status = _cairo_surface_paint (surface, CAIRO_OPERATOR_SOURCE,
+                                  &_cairo_pattern_white.base, NULL);
+    if (likely (status == CAIRO_STATUS_SUCCESS))
+       status = _cairo_clip_combine_with_surface (clip, surface,
+                                                  extents->x, extents->y);
+
+    if (unlikely (status)) {
+       cairo_surface_destroy (surface);
+       surface = _cairo_surface_create_in_error (status);
+    }
+
+    return surface;
+}
diff --git a/src/cairo-clip-tor-scan-converter.c b/src/cairo-clip-tor-scan-converter.c
new file mode 100644 (file)
index 0000000..e32a5a9
--- /dev/null
@@ -0,0 +1,1845 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* glitter-paths - polygon scan converter
+ *
+ * Copyright (c) 2008  M Joonas Pihlaja
+ * Copyright (c) 2007  David Turner
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+/* This is the Glitter paths scan converter incorporated into cairo.
+ * The source is from commit 734c53237a867a773640bd5b64816249fa1730f8
+ * of
+ *
+ *   http://gitweb.freedesktop.org/?p=users/joonas/glitter-paths
+ */
+/* Glitter-paths is a stand alone polygon rasteriser derived from
+ * David Turner's reimplementation of Tor Anderssons's 15x17
+ * supersampling rasteriser from the Apparition graphics library.  The
+ * main new feature here is cheaply choosing per-scan line between
+ * doing fully analytical coverage computation for an entire row at a
+ * time vs. using a supersampling approach.
+ *
+ * David Turner's code can be found at
+ *
+ *   http://david.freetype.org/rasterizer-shootout/raster-comparison-20070813.tar.bz2
+ *
+ * In particular this file incorporates large parts of ftgrays_tor10.h
+ * from raster-comparison-20070813.tar.bz2
+ */
+/* Overview
+ *
+ * A scan converter's basic purpose to take polygon edges and convert
+ * them into an RLE compressed A8 mask.  This one works in two phases:
+ * gathering edges and generating spans.
+ *
+ * 1) As the user feeds the scan converter edges they are vertically
+ * clipped and bucketted into a _polygon_ data structure.  The edges
+ * are also snapped from the user's coordinates to the subpixel grid
+ * coordinates used during scan conversion.
+ *
+ *     user
+ *      |
+ *      | edges
+ *      V
+ *    polygon buckets
+ *
+ * 2) Generating spans works by performing a vertical sweep of pixel
+ * rows from top to bottom and maintaining an _active_list_ of edges
+ * that intersect the row.  From the active list the fill rule
+ * determines which edges are the left and right edges of the start of
+ * each span, and their contribution is then accumulated into a pixel
+ * coverage list (_cell_list_) as coverage deltas.  Once the coverage
+ * deltas of all edges are known we can form spans of constant pixel
+ * coverage by summing the deltas during a traversal of the cell list.
+ * At the end of a pixel row the cell list is sent to a coverage
+ * blitter for rendering to some target surface.
+ *
+ * The pixel coverages are computed by either supersampling the row
+ * and box filtering a mono rasterisation, or by computing the exact
+ * coverages of edges in the active list.  The supersampling method is
+ * used whenever some edge starts or stops within the row or there are
+ * edge intersections in the row.
+ *
+ *   polygon bucket for       \
+ *   current pixel row        |
+ *      |                     |
+ *      | activate new edges  |  Repeat GRID_Y times if we
+ *      V                     \  are supersampling this row,
+ *   active list              /  or just once if we're computing
+ *      |                     |  analytical coverage.
+ *      | coverage deltas     |
+ *      V                     |
+ *   pixel coverage list     /
+ *      |
+ *      V
+ *   coverage blitter
+ */
+#include "cairoint.h"
+#include "cairo-spans-private.h"
+#include "cairo-error-private.h"
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <setjmp.h>
+
+/* The input coordinate scale and the rasterisation grid scales. */
+#define GLITTER_INPUT_BITS CAIRO_FIXED_FRAC_BITS
+#define GRID_X_BITS CAIRO_FIXED_FRAC_BITS
+#define GRID_Y 15
+
+/* Set glitter up to use a cairo span renderer to do the coverage
+ * blitting. */
+struct pool;
+struct cell_list;
+
+/*-------------------------------------------------------------------------
+ * glitter-paths.h
+ */
+
+/* "Input scaled" numbers are fixed precision reals with multiplier
+ * 2**GLITTER_INPUT_BITS.  Input coordinates are given to glitter as
+ * pixel scaled numbers.  These get converted to the internal grid
+ * scaled numbers as soon as possible. Internal overflow is possible
+ * if GRID_X/Y inside glitter-paths.c is larger than
+ * 1<<GLITTER_INPUT_BITS. */
+#ifndef GLITTER_INPUT_BITS
+#  define GLITTER_INPUT_BITS 8
+#endif
+#define GLITTER_INPUT_SCALE (1<<GLITTER_INPUT_BITS)
+typedef int glitter_input_scaled_t;
+
+/* Opaque type for scan converting. */
+typedef struct glitter_scan_converter glitter_scan_converter_t;
+
+/*-------------------------------------------------------------------------
+ * glitter-paths.c: Implementation internal types
+ */
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+
+/* All polygon coordinates are snapped onto a subsample grid. "Grid
+ * scaled" numbers are fixed precision reals with multiplier GRID_X or
+ * GRID_Y. */
+typedef int grid_scaled_t;
+typedef int grid_scaled_x_t;
+typedef int grid_scaled_y_t;
+
+/* Default x/y scale factors.
+ *  You can either define GRID_X/Y_BITS to get a power-of-two scale
+ *  or define GRID_X/Y separately. */
+#if !defined(GRID_X) && !defined(GRID_X_BITS)
+#  define GRID_X_BITS 8
+#endif
+#if !defined(GRID_Y) && !defined(GRID_Y_BITS)
+#  define GRID_Y 15
+#endif
+
+/* Use GRID_X/Y_BITS to define GRID_X/Y if they're available. */
+#ifdef GRID_X_BITS
+#  define GRID_X (1 << GRID_X_BITS)
+#endif
+#ifdef GRID_Y_BITS
+#  define GRID_Y (1 << GRID_Y_BITS)
+#endif
+
+/* The GRID_X_TO_INT_FRAC macro splits a grid scaled coordinate into
+ * integer and fractional parts. The integer part is floored. */
+#if defined(GRID_X_TO_INT_FRAC)
+  /* do nothing */
+#elif defined(GRID_X_BITS)
+#  define GRID_X_TO_INT_FRAC(x, i, f) \
+       _GRID_TO_INT_FRAC_shift(x, i, f, GRID_X_BITS)
+#else
+#  define GRID_X_TO_INT_FRAC(x, i, f) \
+       _GRID_TO_INT_FRAC_general(x, i, f, GRID_X)
+#endif
+
+#define _GRID_TO_INT_FRAC_general(t, i, f, m) do {     \
+    (i) = (t) / (m);                                   \
+    (f) = (t) % (m);                                   \
+    if ((f) < 0) {                                     \
+       --(i);                                          \
+       (f) += (m);                                     \
+    }                                                  \
+} while (0)
+
+#define _GRID_TO_INT_FRAC_shift(t, i, f, b) do {       \
+    (f) = (t) & ((1 << (b)) - 1);                      \
+    (i) = (t) >> (b);                                  \
+} while (0)
+
+/* A grid area is a real in [0,1] scaled by 2*GRID_X*GRID_Y.  We want
+ * to be able to represent exactly areas of subpixel trapezoids whose
+ * vertices are given in grid scaled coordinates.  The scale factor
+ * comes from needing to accurately represent the area 0.5*dx*dy of a
+ * triangle with base dx and height dy in grid scaled numbers. */
+typedef int grid_area_t;
+#define GRID_XY (2*GRID_X*GRID_Y) /* Unit area on the grid. */
+
+/* GRID_AREA_TO_ALPHA(area): map [0,GRID_XY] to [0,255]. */
+#if GRID_XY == 510
+#  define GRID_AREA_TO_ALPHA(c)          (((c)+1) >> 1)
+#elif GRID_XY == 255
+#  define  GRID_AREA_TO_ALPHA(c)  (c)
+#elif GRID_XY == 64
+#  define  GRID_AREA_TO_ALPHA(c)  (((c) << 2) | -(((c) & 0x40) >> 6))
+#elif GRID_XY == 128
+#  define  GRID_AREA_TO_ALPHA(c)  ((((c) << 1) | -((c) >> 7)) & 255)
+#elif GRID_XY == 256
+#  define  GRID_AREA_TO_ALPHA(c)  (((c) | -((c) >> 8)) & 255)
+#elif GRID_XY == 15
+#  define  GRID_AREA_TO_ALPHA(c)  (((c) << 4) + (c))
+#elif GRID_XY == 2*256*15
+#  define  GRID_AREA_TO_ALPHA(c)  (((c) + ((c)<<4) + 256) >> 9)
+#else
+#  define  GRID_AREA_TO_ALPHA(c)  (((c)*255 + GRID_XY/2) / GRID_XY)
+#endif
+
+#define UNROLL3(x) x x x
+
+struct quorem {
+    int32_t quo;
+    int32_t rem;
+};
+
+/* Header for a chunk of memory in a memory pool. */
+struct _pool_chunk {
+    /* # bytes used in this chunk. */
+    size_t size;
+
+    /* # bytes total in this chunk */
+    size_t capacity;
+
+    /* Pointer to the previous chunk or %NULL if this is the sentinel
+     * chunk in the pool header. */
+    struct _pool_chunk *prev_chunk;
+
+    /* Actual data starts here.         Well aligned for pointers. */
+};
+
+/* A memory pool.  This is supposed to be embedded on the stack or
+ * within some other structure.         It may optionally be followed by an
+ * embedded array from which requests are fulfilled until
+ * malloc needs to be called to allocate a first real chunk. */
+struct pool {
+    /* Chunk we're allocating from. */
+    struct _pool_chunk *current;
+
+    jmp_buf *jmp;
+
+    /* Free list of previously allocated chunks.  All have >= default
+     * capacity. */
+    struct _pool_chunk *first_free;
+
+    /* The default capacity of a chunk. */
+    size_t default_capacity;
+
+    /* Header for the sentinel chunk.  Directly following the pool
+     * struct should be some space for embedded elements from which
+     * the sentinel chunk allocates from. */
+    struct _pool_chunk sentinel[1];
+};
+
+/* A polygon edge. */
+struct edge {
+    /* Next in y-bucket or active list. */
+    struct edge *next;
+
+    /* Current x coordinate while the edge is on the active
+     * list. Initialised to the x coordinate of the top of the
+     * edge. The quotient is in grid_scaled_x_t units and the
+     * remainder is mod dy in grid_scaled_y_t units.*/
+    struct quorem x;
+
+    /* Advance of the current x when moving down a subsample line. */
+    struct quorem dxdy;
+
+    /* Advance of the current x when moving down a full pixel
+     * row. Only initialised when the height of the edge is large
+     * enough that there's a chance the edge could be stepped by a
+     * full row's worth of subsample rows at a time. */
+    struct quorem dxdy_full;
+
+    /* The clipped y of the top of the edge. */
+    grid_scaled_y_t ytop;
+
+    /* y2-y1 after orienting the edge downwards.  */
+    grid_scaled_y_t dy;
+
+    /* Number of subsample rows remaining to scan convert of this
+     * edge. */
+    grid_scaled_y_t height_left;
+
+    /* Original sign of the edge: +1 for downwards, -1 for upwards
+     * edges.  */
+    int dir;
+    int vertical;
+    int clip;
+};
+
+/* Number of subsample rows per y-bucket. Must be GRID_Y. */
+#define EDGE_Y_BUCKET_HEIGHT GRID_Y
+
+#define EDGE_Y_BUCKET_INDEX(y, ymin) (((y) - (ymin))/EDGE_Y_BUCKET_HEIGHT)
+
+/* A collection of sorted and vertically clipped edges of the polygon.
+ * Edges are moved from the polygon to an active list while scan
+ * converting. */
+struct polygon {
+    /* The vertical clip extents. */
+    grid_scaled_y_t ymin, ymax;
+
+    /* Array of edges all starting in the same bucket. An edge is put
+     * into bucket EDGE_BUCKET_INDEX(edge->ytop, polygon->ymin) when
+     * it is added to the polygon. */
+    struct edge **y_buckets;
+    struct edge *y_buckets_embedded[64];
+
+    struct {
+       struct pool base[1];
+       struct edge embedded[32];
+    } edge_pool;
+};
+
+/* A cell records the effect on pixel coverage of polygon edges
+ * passing through a pixel.  It contains two accumulators of pixel
+ * coverage.
+ *
+ * Consider the effects of a polygon edge on the coverage of a pixel
+ * it intersects and that of the following one.  The coverage of the
+ * following pixel is the height of the edge multiplied by the width
+ * of the pixel, and the coverage of the pixel itself is the area of
+ * the trapezoid formed by the edge and the right side of the pixel.
+ *
+ * +-----------------------+-----------------------+
+ * |                       |                       |
+ * |                       |                       |
+ * |_______________________|_______________________|
+ * |   \...................|.......................|\
+ * |    \..................|.......................| |
+ * |     \.................|.......................| |
+ * |      \....covered.....|.......................| |
+ * |       \....area.......|.......................| } covered height
+ * |        \..............|.......................| |
+ * |uncovered\.............|.......................| |
+ * |  area    \............|.......................| |
+ * |___________\...........|.......................|/
+ * |                       |                       |
+ * |                       |                       |
+ * |                       |                       |
+ * +-----------------------+-----------------------+
+ *
+ * Since the coverage of the following pixel will always be a multiple
+ * of the width of the pixel, we can store the height of the covered
+ * area instead.  The coverage of the pixel itself is the total
+ * coverage minus the area of the uncovered area to the left of the
+ * edge.  As it's faster to compute the uncovered area we only store
+ * that and subtract it from the total coverage later when forming
+ * spans to blit.
+ *
+ * The heights and areas are signed, with left edges of the polygon
+ * having positive sign and right edges having negative sign.  When
+ * two edges intersect they swap their left/rightness so their
+ * contribution above and below the intersection point must be
+ * computed separately. */
+struct cell {
+    struct cell                *next;
+    int                         x;
+    grid_area_t                 uncovered_area;
+    grid_scaled_y_t     covered_height;
+    grid_scaled_y_t     clipped_height;
+};
+
+/* A cell list represents the scan line sparsely as cells ordered by
+ * ascending x.  It is geared towards scanning the cells in order
+ * using an internal cursor. */
+struct cell_list {
+    /* Sentinel nodes */
+    struct cell head, tail;
+
+    /* Cursor state for iterating through the cell list. */
+    struct cell *cursor;
+
+    /* Cells in the cell list are owned by the cell list and are
+     * allocated from this pool.  */
+    struct {
+       struct pool base[1];
+       struct cell embedded[32];
+    } cell_pool;
+};
+
+struct cell_pair {
+    struct cell *cell1;
+    struct cell *cell2;
+};
+
+/* The active list contains edges in the current scan line ordered by
+ * the x-coordinate of the intercept of the edge and the scan line. */
+struct active_list {
+    /* Leftmost edge on the current scan line. */
+    struct edge *head;
+
+    /* A lower bound on the height of the active edges is used to
+     * estimate how soon some active edge ends.         We can't advance the
+     * scan conversion by a full pixel row if an edge ends somewhere
+     * within it. */
+    grid_scaled_y_t min_height;
+};
+
+struct glitter_scan_converter {
+    struct polygon     polygon[1];
+    struct active_list active[1];
+    struct cell_list   coverages[1];
+
+    /* Clip box. */
+    grid_scaled_y_t ymin, ymax;
+};
+
+/* Compute the floored division a/b. Assumes / and % perform symmetric
+ * division. */
+inline static struct quorem
+floored_divrem(int a, int b)
+{
+    struct quorem qr;
+    qr.quo = a/b;
+    qr.rem = a%b;
+    if ((a^b)<0 && qr.rem) {
+       qr.quo -= 1;
+       qr.rem += b;
+    }
+    return qr;
+}
+
+/* Compute the floored division (x*a)/b. Assumes / and % perform symmetric
+ * division. */
+static struct quorem
+floored_muldivrem(int x, int a, int b)
+{
+    struct quorem qr;
+    long long xa = (long long)x*a;
+    qr.quo = xa/b;
+    qr.rem = xa%b;
+    if ((xa>=0) != (b>=0) && qr.rem) {
+       qr.quo -= 1;
+       qr.rem += b;
+    }
+    return qr;
+}
+
+static struct _pool_chunk *
+_pool_chunk_init(
+    struct _pool_chunk *p,
+    struct _pool_chunk *prev_chunk,
+    size_t capacity)
+{
+    p->prev_chunk = prev_chunk;
+    p->size = 0;
+    p->capacity = capacity;
+    return p;
+}
+
+static struct _pool_chunk *
+_pool_chunk_create(struct pool *pool, size_t size)
+{
+    struct _pool_chunk *p;
+
+    p = malloc(size + sizeof(struct _pool_chunk));
+    if (unlikely (NULL == p))
+       longjmp (*pool->jmp, _cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+    return _pool_chunk_init(p, pool->current, size);
+}
+
+static void
+pool_init(struct pool *pool,
+         jmp_buf *jmp,
+         size_t default_capacity,
+         size_t embedded_capacity)
+{
+    pool->jmp = jmp;
+    pool->current = pool->sentinel;
+    pool->first_free = NULL;
+    pool->default_capacity = default_capacity;
+    _pool_chunk_init(pool->sentinel, NULL, embedded_capacity);
+}
+
+static void
+pool_fini(struct pool *pool)
+{
+    struct _pool_chunk *p = pool->current;
+    do {
+       while (NULL != p) {
+           struct _pool_chunk *prev = p->prev_chunk;
+           if (p != pool->sentinel)
+               free(p);
+           p = prev;
+       }
+       p = pool->first_free;
+       pool->first_free = NULL;
+    } while (NULL != p);
+}
+
+/* Satisfy an allocation by first allocating a new large enough chunk
+ * and adding it to the head of the pool's chunk list. This function
+ * is called as a fallback if pool_alloc() couldn't do a quick
+ * allocation from the current chunk in the pool. */
+static void *
+_pool_alloc_from_new_chunk(
+    struct pool *pool,
+    size_t size)
+{
+    struct _pool_chunk *chunk;
+    void *obj;
+    size_t capacity;
+
+    /* If the allocation is smaller than the default chunk size then
+     * try getting a chunk off the free list.  Force alloc of a new
+     * chunk for large requests. */
+    capacity = size;
+    chunk = NULL;
+    if (size < pool->default_capacity) {
+       capacity = pool->default_capacity;
+       chunk = pool->first_free;
+       if (chunk) {
+           pool->first_free = chunk->prev_chunk;
+           _pool_chunk_init(chunk, pool->current, chunk->capacity);
+       }
+    }
+
+    if (NULL == chunk)
+       chunk = _pool_chunk_create (pool, capacity);
+    pool->current = chunk;
+
+    obj = ((unsigned char*)chunk + sizeof(*chunk) + chunk->size);
+    chunk->size += size;
+    return obj;
+}
+
+/* Allocate size bytes from the pool.  The first allocated address
+ * returned from a pool is aligned to sizeof(void*).  Subsequent
+ * addresses will maintain alignment as long as multiples of void* are
+ * allocated.  Returns the address of a new memory area or %NULL on
+ * allocation failures.         The pool retains ownership of the returned
+ * memory. */
+inline static void *
+pool_alloc (struct pool *pool, size_t size)
+{
+    struct _pool_chunk *chunk = pool->current;
+
+    if (size <= chunk->capacity - chunk->size) {
+       void *obj = ((unsigned char*)chunk + sizeof(*chunk) + chunk->size);
+       chunk->size += size;
+       return obj;
+    } else {
+       return _pool_alloc_from_new_chunk(pool, size);
+    }
+}
+
+/* Relinquish all pool_alloced memory back to the pool. */
+static void
+pool_reset (struct pool *pool)
+{
+    /* Transfer all used chunks to the chunk free list. */
+    struct _pool_chunk *chunk = pool->current;
+    if (chunk != pool->sentinel) {
+       while (chunk->prev_chunk != pool->sentinel) {
+           chunk = chunk->prev_chunk;
+       }
+       chunk->prev_chunk = pool->first_free;
+       pool->first_free = pool->current;
+    }
+    /* Reset the sentinel as the current chunk. */
+    pool->current = pool->sentinel;
+    pool->sentinel->size = 0;
+}
+
+/* Rewinds the cell list's cursor to the beginning.  After rewinding
+ * we're good to cell_list_find() the cell any x coordinate. */
+inline static void
+cell_list_rewind (struct cell_list *cells)
+{
+    cells->cursor = &cells->head;
+}
+
+/* Rewind the cell list if its cursor has been advanced past x. */
+inline static void
+cell_list_maybe_rewind (struct cell_list *cells, int x)
+{
+    struct cell *tail = cells->cursor;
+    if (tail->x > x)
+       cell_list_rewind (cells);
+}
+
+static void
+cell_list_init(struct cell_list *cells, jmp_buf *jmp)
+{
+    pool_init(cells->cell_pool.base, jmp,
+             256*sizeof(struct cell),
+             sizeof(cells->cell_pool.embedded));
+    cells->tail.next = NULL;
+    cells->tail.x = INT_MAX;
+    cells->head.x = INT_MIN;
+    cells->head.next = &cells->tail;
+    cell_list_rewind (cells);
+}
+
+static void
+cell_list_fini(struct cell_list *cells)
+{
+    pool_fini (cells->cell_pool.base);
+}
+
+/* Empty the cell list.  This is called at the start of every pixel
+ * row. */
+inline static void
+cell_list_reset (struct cell_list *cells)
+{
+    cell_list_rewind (cells);
+    cells->head.next = &cells->tail;
+    pool_reset (cells->cell_pool.base);
+}
+
+static struct cell *
+cell_list_alloc (struct cell_list *cells,
+                struct cell *tail,
+                int x)
+{
+    struct cell *cell;
+
+    cell = pool_alloc (cells->cell_pool.base, sizeof (struct cell));
+    cell->next = tail->next;
+    tail->next = cell;
+    cell->x = x;
+    cell->uncovered_area = 0;
+    cell->covered_height = 0;
+    cell->clipped_height = 0;
+    return cell;
+}
+
+/* Find a cell at the given x-coordinate.  Returns %NULL if a new cell
+ * needed to be allocated but couldn't be.  Cells must be found with
+ * non-decreasing x-coordinate until the cell list is rewound using
+ * cell_list_rewind(). Ownership of the returned cell is retained by
+ * the cell list. */
+inline static struct cell *
+cell_list_find (struct cell_list *cells, int x)
+{
+    struct cell *tail = cells->cursor;
+
+    while (1) {
+       UNROLL3({
+           if (tail->next->x > x)
+               break;
+           tail = tail->next;
+       });
+    }
+
+    if (tail->x != x)
+       tail = cell_list_alloc (cells, tail, x);
+    return cells->cursor = tail;
+
+}
+
+/* Find two cells at x1 and x2.         This is exactly equivalent
+ * to
+ *
+ *   pair.cell1 = cell_list_find(cells, x1);
+ *   pair.cell2 = cell_list_find(cells, x2);
+ *
+ * except with less function call overhead. */
+inline static struct cell_pair
+cell_list_find_pair(struct cell_list *cells, int x1, int x2)
+{
+    struct cell_pair pair;
+
+    pair.cell1 = cells->cursor;
+    while (1) {
+       UNROLL3({
+           if (pair.cell1->next->x > x1)
+               break;
+           pair.cell1 = pair.cell1->next;
+       });
+    }
+    if (pair.cell1->x != x1) {
+       struct cell *cell = pool_alloc (cells->cell_pool.base,
+                                       sizeof (struct cell));
+       cell->x = x1;
+       cell->uncovered_area = 0;
+       cell->covered_height = 0;
+       cell->clipped_height = 0;
+       cell->next = pair.cell1->next;
+       pair.cell1->next = cell;
+       pair.cell1 = cell;
+    }
+
+    pair.cell2 = pair.cell1;
+    while (1) {
+       UNROLL3({
+           if (pair.cell2->next->x > x2)
+               break;
+           pair.cell2 = pair.cell2->next;
+       });
+    }
+    if (pair.cell2->x != x2) {
+       struct cell *cell = pool_alloc (cells->cell_pool.base,
+                                       sizeof (struct cell));
+       cell->uncovered_area = 0;
+       cell->covered_height = 0;
+       cell->clipped_height = 0;
+       cell->x = x2;
+       cell->next = pair.cell2->next;
+       pair.cell2->next = cell;
+       pair.cell2 = cell;
+    }
+
+    cells->cursor = pair.cell2;
+    return pair;
+}
+
+/* Add a subpixel span covering [x1, x2) to the coverage cells. */
+inline static void
+cell_list_add_subspan(struct cell_list *cells,
+                     grid_scaled_x_t x1,
+                     grid_scaled_x_t x2)
+{
+    int ix1, fx1;
+    int ix2, fx2;
+
+    GRID_X_TO_INT_FRAC(x1, ix1, fx1);
+    GRID_X_TO_INT_FRAC(x2, ix2, fx2);
+
+    if (ix1 != ix2) {
+       struct cell_pair p;
+       p = cell_list_find_pair(cells, ix1, ix2);
+       p.cell1->uncovered_area += 2*fx1;
+       ++p.cell1->covered_height;
+       p.cell2->uncovered_area -= 2*fx2;
+       --p.cell2->covered_height;
+    } else {
+       struct cell *cell = cell_list_find(cells, ix1);
+       cell->uncovered_area += 2*(fx1-fx2);
+    }
+}
+
+/* Adds the analytical coverage of an edge crossing the current pixel
+ * row to the coverage cells and advances the edge's x position to the
+ * following row.
+ *
+ * This function is only called when we know that during this pixel row:
+ *
+ * 1) The relative order of all edges on the active list doesn't
+ * change.  In particular, no edges intersect within this row to pixel
+ * precision.
+ *
+ * 2) No new edges start in this row.
+ *
+ * 3) No existing edges end mid-row.
+ *
+ * This function depends on being called with all edges from the
+ * active list in the order they appear on the list (i.e. with
+ * non-decreasing x-coordinate.)  */
+static void
+cell_list_render_edge(
+    struct cell_list *cells,
+    struct edge *edge,
+    int sign)
+{
+    grid_scaled_y_t y1, y2, dy;
+    grid_scaled_x_t dx;
+    int ix1, ix2;
+    grid_scaled_x_t fx1, fx2;
+
+    struct quorem x1 = edge->x;
+    struct quorem x2 = x1;
+
+    if (! edge->vertical) {
+       x2.quo += edge->dxdy_full.quo;
+       x2.rem += edge->dxdy_full.rem;
+       if (x2.rem >= 0) {
+           ++x2.quo;
+           x2.rem -= edge->dy;
+       }
+
+       edge->x = x2;
+    }
+
+    GRID_X_TO_INT_FRAC(x1.quo, ix1, fx1);
+    GRID_X_TO_INT_FRAC(x2.quo, ix2, fx2);
+
+    /* Edge is entirely within a column? */
+    if (ix1 == ix2) {
+       /* We always know that ix1 is >= the cell list cursor in this
+        * case due to the no-intersections precondition.  */
+       struct cell *cell = cell_list_find(cells, ix1);
+       cell->covered_height += sign*GRID_Y;
+       cell->uncovered_area += sign*(fx1 + fx2)*GRID_Y;
+       return;
+    }
+
+    /* Orient the edge left-to-right. */
+    dx = x2.quo - x1.quo;
+    if (dx >= 0) {
+       y1 = 0;
+       y2 = GRID_Y;
+    } else {
+       int tmp;
+       tmp = ix1; ix1 = ix2; ix2 = tmp;
+       tmp = fx1; fx1 = fx2; fx2 = tmp;
+       dx = -dx;
+       sign = -sign;
+       y1 = GRID_Y;
+       y2 = 0;
+    }
+    dy = y2 - y1;
+
+    /* Add coverage for all pixels [ix1,ix2] on this row crossed
+     * by the edge. */
+    {
+       struct cell_pair pair;
+       struct quorem y = floored_divrem((GRID_X - fx1)*dy, dx);
+
+       /* When rendering a previous edge on the active list we may
+        * advance the cell list cursor past the leftmost pixel of the
+        * current edge even though the two edges don't intersect.
+        * e.g. consider two edges going down and rightwards:
+        *
+        *  --\_+---\_+-----+-----+----
+        *      \_    \_    |     |
+        *      | \_  | \_  |     |
+        *      |   \_|   \_|     |
+        *      |     \_    \_    |
+        *  ----+-----+-\---+-\---+----
+        *
+        * The left edge touches cells past the starting cell of the
+        * right edge.  Fortunately such cases are rare.
+        *
+        * The rewinding is never necessary if the current edge stays
+        * within a single column because we've checked before calling
+        * this function that the active list order won't change. */
+       cell_list_maybe_rewind(cells, ix1);
+
+       pair = cell_list_find_pair(cells, ix1, ix1+1);
+       pair.cell1->uncovered_area += sign*y.quo*(GRID_X + fx1);
+       pair.cell1->covered_height += sign*y.quo;
+       y.quo += y1;
+
+       if (ix1+1 < ix2) {
+           struct quorem dydx_full = floored_divrem(GRID_X*dy, dx);
+           struct cell *cell = pair.cell2;
+
+           ++ix1;
+           do {
+               grid_scaled_y_t y_skip = dydx_full.quo;
+               y.rem += dydx_full.rem;
+               if (y.rem >= dx) {
+                   ++y_skip;
+                   y.rem -= dx;
+               }
+
+               y.quo += y_skip;
+
+               y_skip *= sign;
+               cell->uncovered_area += y_skip*GRID_X;
+               cell->covered_height += y_skip;
+
+               ++ix1;
+               cell = cell_list_find(cells, ix1);
+           } while (ix1 != ix2);
+
+           pair.cell2 = cell;
+       }
+       pair.cell2->uncovered_area += sign*(y2 - y.quo)*fx2;
+       pair.cell2->covered_height += sign*(y2 - y.quo);
+    }
+}
+
+static void
+polygon_init (struct polygon *polygon, jmp_buf *jmp)
+{
+    polygon->ymin = polygon->ymax = 0;
+    polygon->y_buckets = polygon->y_buckets_embedded;
+    pool_init (polygon->edge_pool.base, jmp,
+              8192 - sizeof (struct _pool_chunk),
+              sizeof (polygon->edge_pool.embedded));
+}
+
+static void
+polygon_fini (struct polygon *polygon)
+{
+    if (polygon->y_buckets != polygon->y_buckets_embedded)
+       free (polygon->y_buckets);
+
+    pool_fini (polygon->edge_pool.base);
+}
+
+/* Empties the polygon of all edges. The polygon is then prepared to
+ * receive new edges and clip them to the vertical range
+ * [ymin,ymax). */
+static cairo_status_t
+polygon_reset (struct polygon *polygon,
+              grid_scaled_y_t ymin,
+              grid_scaled_y_t ymax)
+{
+    unsigned h = ymax - ymin;
+    unsigned num_buckets = EDGE_Y_BUCKET_INDEX(ymax + EDGE_Y_BUCKET_HEIGHT-1,
+                                              ymin);
+
+    pool_reset(polygon->edge_pool.base);
+
+    if (unlikely (h > 0x7FFFFFFFU - EDGE_Y_BUCKET_HEIGHT))
+       goto bail_no_mem; /* even if you could, you wouldn't want to. */
+
+    if (polygon->y_buckets != polygon->y_buckets_embedded)
+       free (polygon->y_buckets);
+
+    polygon->y_buckets =  polygon->y_buckets_embedded;
+    if (num_buckets > ARRAY_LENGTH (polygon->y_buckets_embedded)) {
+       polygon->y_buckets = _cairo_malloc_ab (num_buckets,
+                                              sizeof (struct edge *));
+       if (unlikely (NULL == polygon->y_buckets))
+           goto bail_no_mem;
+    }
+    memset (polygon->y_buckets, 0, num_buckets * sizeof (struct edge *));
+
+    polygon->ymin = ymin;
+    polygon->ymax = ymax;
+    return CAIRO_STATUS_SUCCESS;
+
+ bail_no_mem:
+    polygon->ymin = 0;
+    polygon->ymax = 0;
+    return CAIRO_STATUS_NO_MEMORY;
+}
+
+static void
+_polygon_insert_edge_into_its_y_bucket(
+    struct polygon *polygon,
+    struct edge *e)
+{
+    unsigned ix = EDGE_Y_BUCKET_INDEX(e->ytop, polygon->ymin);
+    struct edge **ptail = &polygon->y_buckets[ix];
+    e->next = *ptail;
+    *ptail = e;
+}
+
+inline static void
+polygon_add_edge (struct polygon *polygon,
+                 const cairo_edge_t *edge,
+                 int clip)
+{
+    struct edge *e;
+    grid_scaled_x_t dx;
+    grid_scaled_y_t dy;
+    grid_scaled_y_t ytop, ybot;
+    grid_scaled_y_t ymin = polygon->ymin;
+    grid_scaled_y_t ymax = polygon->ymax;
+
+    assert (edge->bottom > edge->top);
+
+    if (unlikely (edge->top >= ymax || edge->bottom <= ymin))
+       return;
+
+    e = pool_alloc (polygon->edge_pool.base, sizeof (struct edge));
+
+    dx = edge->line.p2.x - edge->line.p1.x;
+    dy = edge->line.p2.y - edge->line.p1.y;
+    e->dy = dy;
+    e->dir = edge->dir;
+    e->clip = clip;
+
+    ytop = edge->top >= ymin ? edge->top : ymin;
+    ybot = edge->bottom <= ymax ? edge->bottom : ymax;
+    e->ytop = ytop;
+    e->height_left = ybot - ytop;
+
+    if (dx == 0) {
+       e->vertical = TRUE;
+       e->x.quo = edge->line.p1.x;
+       e->x.rem = 0;
+       e->dxdy.quo = 0;
+       e->dxdy.rem = 0;
+       e->dxdy_full.quo = 0;
+       e->dxdy_full.rem = 0;
+    } else {
+       e->vertical = FALSE;
+       e->dxdy = floored_divrem (dx, dy);
+       if (ytop == edge->line.p1.y) {
+           e->x.quo = edge->line.p1.x;
+           e->x.rem = 0;
+       } else {
+           e->x = floored_muldivrem (ytop - edge->line.p1.y, dx, dy);
+           e->x.quo += edge->line.p1.x;
+       }
+
+       if (e->height_left >= GRID_Y) {
+           e->dxdy_full = floored_muldivrem (GRID_Y, dx, dy);
+       } else {
+           e->dxdy_full.quo = 0;
+           e->dxdy_full.rem = 0;
+       }
+    }
+
+    _polygon_insert_edge_into_its_y_bucket (polygon, e);
+
+    e->x.rem -= dy;            /* Bias the remainder for faster
+                                * edge advancement. */
+}
+
+static void
+active_list_reset (struct active_list *active)
+{
+    active->head = NULL;
+    active->min_height = 0;
+}
+
+static void
+active_list_init(struct active_list *active)
+{
+    active_list_reset(active);
+}
+
+/*
+ * Merge two sorted edge lists.
+ * Input:
+ *  - head_a: The head of the first list.
+ *  - head_b: The head of the second list; head_b cannot be NULL.
+ * Output:
+ * Returns the head of the merged list.
+ *
+ * Implementation notes:
+ * To make it fast (in particular, to reduce to an insertion sort whenever
+ * one of the two input lists only has a single element) we iterate through
+ * a list until its head becomes greater than the head of the other list,
+ * then we switch their roles. As soon as one of the two lists is empty, we
+ * just attach the other one to the current list and exit.
+ * Writes to memory are only needed to "switch" lists (as it also requires
+ * attaching to the output list the list which we will be iterating next) and
+ * to attach the last non-empty list.
+ */
+static struct edge *
+merge_sorted_edges (struct edge *head_a, struct edge *head_b)
+{
+    struct edge *head, **next;
+    int32_t x;
+
+    if (head_a == NULL)
+       return head_b;
+
+    next = &head;
+    if (head_a->x.quo <= head_b->x.quo) {
+       head = head_a;
+    } else {
+       head = head_b;
+       goto start_with_b;
+    }
+
+    do {
+       x = head_b->x.quo;
+       while (head_a != NULL && head_a->x.quo <= x) {
+           next = &head_a->next;
+           head_a = head_a->next;
+       }
+
+       *next = head_b;
+       if (head_a == NULL)
+           return head;
+
+start_with_b:
+       x = head_a->x.quo;
+       while (head_b != NULL && head_b->x.quo <= x) {
+           next = &head_b->next;
+           head_b = head_b->next;
+       }
+
+       *next = head_a;
+       if (head_b == NULL)
+           return head;
+    } while (1);
+}
+
+/*
+ * Sort (part of) a list.
+ * Input:
+ *  - list: The list to be sorted; list cannot be NULL.
+ *  - limit: Recursion limit.
+ * Output:
+ *  - head_out: The head of the sorted list containing the first 2^(level+1) elements of the
+ *              input list; if the input list has fewer elements, head_out be a sorted list
+ *              containing all the elements of the input list.
+ * Returns the head of the list of unprocessed elements (NULL if the sorted list contains
+ * all the elements of the input list).
+ *
+ * Implementation notes:
+ * Special case single element list, unroll/inline the sorting of the first two elements.
+ * Some tail recursion is used since we iterate on the bottom-up solution of the problem
+ * (we start with a small sorted list and keep merging other lists of the same size to it).
+ */
+static struct edge *
+sort_edges (struct edge  *list,
+           unsigned int  level,
+           struct edge **head_out)
+{
+    struct edge *head_other, *remaining;
+    unsigned int i;
+
+    head_other = list->next;
+
+    /* Single element list -> return */
+    if (head_other == NULL) {
+       *head_out = list;
+       return NULL;
+    }
+
+    /* Unroll the first iteration of the following loop (halves the number of calls to merge_sorted_edges):
+     *  - Initialize remaining to be the list containing the elements after the second in the input list.
+     *  - Initialize *head_out to be the sorted list containing the first two element.
+     */
+    remaining = head_other->next;
+    if (list->x.quo <= head_other->x.quo) {
+       *head_out = list;
+       /* list->next = head_other; */ /* The input list is already like this. */
+       head_other->next = NULL;
+    } else {
+       *head_out = head_other;
+       head_other->next = list;
+       list->next = NULL;
+    }
+
+    for (i = 0; i < level && remaining; i++) {
+       /* Extract a sorted list of the same size as *head_out
+        * (2^(i+1) elements) from the list of remaining elements. */
+       remaining = sort_edges (remaining, i, &head_other);
+       *head_out = merge_sorted_edges (*head_out, head_other);
+    }
+
+    /* *head_out now contains (at most) 2^(level+1) elements. */
+
+    return remaining;
+}
+
+/* Test if the edges on the active list can be safely advanced by a
+ * full row without intersections or any edges ending. */
+inline static int
+active_list_can_step_full_row (struct active_list *active)
+{
+    const struct edge *e;
+    int prev_x = INT_MIN;
+
+    /* Recomputes the minimum height of all edges on the active
+     * list if we have been dropping edges. */
+    if (active->min_height <= 0) {
+       int min_height = INT_MAX;
+
+       e = active->head;
+       while (NULL != e) {
+           if (e->height_left < min_height)
+               min_height = e->height_left;
+           e = e->next;
+       }
+
+       active->min_height = min_height;
+    }
+
+    if (active->min_height < GRID_Y)
+       return 0;
+
+    /* Check for intersections as no edges end during the next row. */
+    e = active->head;
+    while (NULL != e) {
+       struct quorem x = e->x;
+
+       if (! e->vertical) {
+           x.quo += e->dxdy_full.quo;
+           x.rem += e->dxdy_full.rem;
+           if (x.rem >= 0)
+               ++x.quo;
+       }
+
+       if (x.quo <= prev_x)
+           return 0;
+
+       prev_x = x.quo;
+       e = e->next;
+    }
+
+    return 1;
+}
+
+/* Merges edges on the given subpixel row from the polygon to the
+ * active_list. */
+inline static void
+active_list_merge_edges_from_polygon(struct active_list *active,
+                                    struct edge **ptail,
+                                    grid_scaled_y_t y,
+                                    struct polygon *polygon)
+{
+    /* Split off the edges on the current subrow and merge them into
+     * the active list. */
+    int min_height = active->min_height;
+    struct edge *subrow_edges = NULL;
+    struct edge *tail = *ptail;
+
+    do {
+       struct edge *next = tail->next;
+
+       if (y == tail->ytop) {
+           tail->next = subrow_edges;
+           subrow_edges = tail;
+
+           if (tail->height_left < min_height)
+               min_height = tail->height_left;
+
+           *ptail = next;
+       } else
+           ptail = &tail->next;
+
+       tail = next;
+    } while (tail);
+
+    if (subrow_edges) {
+       sort_edges (subrow_edges, UINT_MAX, &subrow_edges);
+       active->head = merge_sorted_edges (active->head, subrow_edges);
+       active->min_height = min_height;
+    }
+}
+
+/* Advance the edges on the active list by one subsample row by
+ * updating their x positions.  Drop edges from the list that end. */
+inline static void
+active_list_substep_edges(struct active_list *active)
+{
+    struct edge **cursor = &active->head;
+    grid_scaled_x_t prev_x = INT_MIN;
+    struct edge *unsorted = NULL;
+    struct edge *edge = *cursor;
+
+    do {
+       UNROLL3({
+           struct edge *next;
+
+           if (NULL == edge)
+               break;
+
+           next = edge->next;
+           if (--edge->height_left) {
+               edge->x.quo += edge->dxdy.quo;
+               edge->x.rem += edge->dxdy.rem;
+               if (edge->x.rem >= 0) {
+                   ++edge->x.quo;
+                   edge->x.rem -= edge->dy;
+               }
+
+               if (edge->x.quo < prev_x) {
+                   *cursor = next;
+                   edge->next = unsorted;
+                   unsorted = edge;
+               } else {
+                   prev_x = edge->x.quo;
+                   cursor = &edge->next;
+               }
+           } else {
+                *cursor = next;
+           }
+           edge = next;
+       })
+    } while (1);
+
+    if (unsorted) {
+       sort_edges (unsorted, UINT_MAX, &unsorted);
+       active->head = merge_sorted_edges (active->head, unsorted);
+    }
+}
+
+inline static void
+apply_nonzero_fill_rule_for_subrow (struct active_list *active,
+                                   struct cell_list *coverages)
+{
+    struct edge *edge = active->head;
+    int winding = 0;
+    int xstart;
+    int xend;
+
+    cell_list_rewind (coverages);
+
+    while (NULL != edge) {
+       xstart = edge->x.quo;
+       winding = edge->dir;
+       while (1) {
+           edge = edge->next;
+           if (NULL == edge) {
+               ASSERT_NOT_REACHED;
+               return;
+           }
+
+           winding += edge->dir;
+           if (0 == winding) {
+               if (edge->next == NULL || edge->next->x.quo != edge->x.quo)
+                   break;
+           }
+       }
+
+       xend = edge->x.quo;
+       cell_list_add_subspan (coverages, xstart, xend);
+
+       edge = edge->next;
+    }
+}
+
+static void
+apply_evenodd_fill_rule_for_subrow (struct active_list *active,
+                                   struct cell_list *coverages)
+{
+    struct edge *edge = active->head;
+    int xstart;
+    int xend;
+
+    cell_list_rewind (coverages);
+
+    while (NULL != edge) {
+       xstart = edge->x.quo;
+
+       while (1) {
+           edge = edge->next;
+           if (NULL == edge) {
+               ASSERT_NOT_REACHED;
+               return;
+           }
+
+           if (edge->next == NULL || edge->next->x.quo != edge->x.quo)
+               break;
+
+           edge = edge->next;
+       }
+
+       xend = edge->x.quo;
+       cell_list_add_subspan (coverages, xstart, xend);
+
+       edge = edge->next;
+    }
+}
+
+static void
+apply_nonzero_fill_rule_and_step_edges (struct active_list *active,
+                                       struct cell_list *coverages)
+{
+    struct edge **cursor = &active->head;
+    struct edge *left_edge;
+
+    left_edge = *cursor;
+    while (NULL != left_edge) {
+       struct edge *right_edge;
+       int winding = left_edge->dir;
+
+       left_edge->height_left -= GRID_Y;
+       if (left_edge->height_left)
+           cursor = &left_edge->next;
+       else
+           *cursor = left_edge->next;
+
+       while (1) {
+           right_edge = *cursor;
+           if (NULL == right_edge) {
+               cell_list_render_edge (coverages, left_edge, +1);
+               return;
+           }
+
+           right_edge->height_left -= GRID_Y;
+           if (right_edge->height_left)
+               cursor = &right_edge->next;
+           else
+               *cursor = right_edge->next;
+
+           winding += right_edge->dir;
+           if (0 == winding) {
+               if (right_edge->next == NULL ||
+                   right_edge->next->x.quo != right_edge->x.quo)
+               {
+                   break;
+               }
+           }
+
+           if (! right_edge->vertical) {
+               right_edge->x.quo += right_edge->dxdy_full.quo;
+               right_edge->x.rem += right_edge->dxdy_full.rem;
+               if (right_edge->x.rem >= 0) {
+                   ++right_edge->x.quo;
+                   right_edge->x.rem -= right_edge->dy;
+               }
+           }
+       }
+
+       cell_list_render_edge (coverages, left_edge, +1);
+       cell_list_render_edge (coverages, right_edge, -1);
+
+       left_edge = *cursor;
+    }
+}
+
+static void
+apply_evenodd_fill_rule_and_step_edges (struct active_list *active,
+                                       struct cell_list *coverages)
+{
+    struct edge **cursor = &active->head;
+    struct edge *left_edge;
+
+    left_edge = *cursor;
+    while (NULL != left_edge) {
+       struct edge *right_edge;
+
+       left_edge->height_left -= GRID_Y;
+       if (left_edge->height_left)
+           cursor = &left_edge->next;
+       else
+           *cursor = left_edge->next;
+
+       while (1) {
+           right_edge = *cursor;
+           if (NULL == right_edge) {
+               cell_list_render_edge (coverages, left_edge, +1);
+               return;
+           }
+
+           right_edge->height_left -= GRID_Y;
+           if (right_edge->height_left)
+               cursor = &right_edge->next;
+           else
+               *cursor = right_edge->next;
+
+           if (right_edge->next == NULL ||
+               right_edge->next->x.quo != right_edge->x.quo)
+           {
+               break;
+           }
+
+           if (! right_edge->vertical) {
+               right_edge->x.quo += right_edge->dxdy_full.quo;
+               right_edge->x.rem += right_edge->dxdy_full.rem;
+               if (right_edge->x.rem >= 0) {
+                   ++right_edge->x.quo;
+                   right_edge->x.rem -= right_edge->dy;
+               }
+           }
+       }
+
+       cell_list_render_edge (coverages, left_edge, +1);
+       cell_list_render_edge (coverages, right_edge, -1);
+
+       left_edge = *cursor;
+    }
+}
+
+static void
+_glitter_scan_converter_init(glitter_scan_converter_t *converter, jmp_buf *jmp)
+{
+    polygon_init(converter->polygon, jmp);
+    active_list_init(converter->active);
+    cell_list_init(converter->coverages, jmp);
+    converter->ymin=0;
+    converter->ymax=0;
+}
+
+static void
+_glitter_scan_converter_fini(glitter_scan_converter_t *converter)
+{
+    polygon_fini(converter->polygon);
+    cell_list_fini(converter->coverages);
+    converter->ymin=0;
+    converter->ymax=0;
+}
+
+static grid_scaled_t
+int_to_grid_scaled(int i, int scale)
+{
+    /* Clamp to max/min representable scaled number. */
+    if (i >= 0) {
+       if (i >= INT_MAX/scale)
+           i = INT_MAX/scale;
+    }
+    else {
+       if (i <= INT_MIN/scale)
+           i = INT_MIN/scale;
+    }
+    return i*scale;
+}
+
+#define int_to_grid_scaled_x(x) int_to_grid_scaled((x), GRID_X)
+#define int_to_grid_scaled_y(x) int_to_grid_scaled((x), GRID_Y)
+
+static cairo_status_t
+glitter_scan_converter_reset(glitter_scan_converter_t *converter,
+                            int ymin, int ymax)
+{
+    cairo_status_t status;
+
+    converter->ymin = 0;
+    converter->ymax = 0;
+
+    ymin = int_to_grid_scaled_y(ymin);
+    ymax = int_to_grid_scaled_y(ymax);
+
+    active_list_reset(converter->active);
+    cell_list_reset(converter->coverages);
+    status = polygon_reset(converter->polygon, ymin, ymax);
+    if (status)
+       return status;
+
+    converter->ymin = ymin;
+    converter->ymax = ymax;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+/* INPUT_TO_GRID_X/Y (in_coord, out_grid_scaled, grid_scale)
+ *   These macros convert an input coordinate in the client's
+ *   device space to the rasterisation grid.
+ */
+/* Gah.. this bit of ugly defines INPUT_TO_GRID_X/Y so as to use
+ * shifts if possible, and something saneish if not.
+ */
+#if !defined(INPUT_TO_GRID_Y) && defined(GRID_Y_BITS) && GRID_Y_BITS <= GLITTER_INPUT_BITS
+#  define INPUT_TO_GRID_Y(in, out) (out) = (in) >> (GLITTER_INPUT_BITS - GRID_Y_BITS)
+#else
+#  define INPUT_TO_GRID_Y(in, out) INPUT_TO_GRID_general(in, out, GRID_Y)
+#endif
+
+#if !defined(INPUT_TO_GRID_X) && defined(GRID_X_BITS) && GRID_X_BITS <= GLITTER_INPUT_BITS
+#  define INPUT_TO_GRID_X(in, out) (out) = (in) >> (GLITTER_INPUT_BITS - GRID_X_BITS)
+#else
+#  define INPUT_TO_GRID_X(in, out) INPUT_TO_GRID_general(in, out, GRID_X)
+#endif
+
+#define INPUT_TO_GRID_general(in, out, grid_scale) do {                \
+       long long tmp__ = (long long)(grid_scale) * (in);       \
+       tmp__ >>= GLITTER_INPUT_BITS;                           \
+       (out) = tmp__;                                          \
+} while (0)
+
+static void
+glitter_scan_converter_add_edge (glitter_scan_converter_t *converter,
+                                const cairo_edge_t *edge,
+                                int clip)
+{
+    cairo_edge_t e;
+
+    INPUT_TO_GRID_Y (edge->top, e.top);
+    INPUT_TO_GRID_Y (edge->bottom, e.bottom);
+    if (e.top >= e.bottom)
+       return;
+
+    /* XXX: possible overflows if GRID_X/Y > 2**GLITTER_INPUT_BITS */
+    INPUT_TO_GRID_Y (edge->line.p1.y, e.line.p1.y);
+    INPUT_TO_GRID_Y (edge->line.p2.y, e.line.p2.y);
+    if (e.line.p1.y == e.line.p2.y)
+       return;
+
+    INPUT_TO_GRID_X (edge->line.p1.x, e.line.p1.x);
+    INPUT_TO_GRID_X (edge->line.p2.x, e.line.p2.x);
+
+    e.dir = edge->dir;
+
+    polygon_add_edge (converter->polygon, &e, clip);
+}
+
+static cairo_bool_t
+active_list_is_vertical (struct active_list *active)
+{
+    struct edge *e;
+
+    for (e = active->head; e != NULL; e = e->next) {
+       if (! e->vertical)
+           return FALSE;
+    }
+
+    return TRUE;
+}
+
+static void
+step_edges (struct active_list *active, int count)
+{
+    struct edge **cursor = &active->head;
+    struct edge *edge;
+
+    for (edge = *cursor; edge != NULL; edge = *cursor) {
+       edge->height_left -= GRID_Y * count;
+       if (edge->height_left)
+           cursor = &edge->next;
+       else
+           *cursor = edge->next;
+    }
+}
+
+static cairo_status_t
+blit_coverages (struct cell_list *cells,
+               cairo_span_renderer_t *renderer,
+               struct pool *span_pool,
+               int y, int height)
+{
+    struct cell *cell = cells->head.next;
+    int prev_x = -1;
+    int cover = 0, last_cover = 0;
+    int clip = 0;
+    cairo_half_open_span_t *spans;
+    unsigned num_spans;
+
+    assert (cell != &cells->tail);
+
+    /* Count number of cells remaining. */
+    {
+       struct cell *next = cell;
+       num_spans = 2;
+       while (next->next) {
+           next = next->next;
+           ++num_spans;
+       }
+       num_spans = 2*num_spans;
+    }
+
+    /* Allocate enough spans for the row. */
+    pool_reset (span_pool);
+    spans = pool_alloc (span_pool, sizeof(spans[0])*num_spans);
+    num_spans = 0;
+
+    /* Form the spans from the coverages and areas. */
+    for (; cell->next; cell = cell->next) {
+       int x = cell->x;
+       int area;
+
+       if (x > prev_x && cover != last_cover) {
+           spans[num_spans].x = prev_x;
+           spans[num_spans].coverage = GRID_AREA_TO_ALPHA (cover);
+           spans[num_spans].inverse = 0;
+           last_cover = cover;
+           ++num_spans;
+       }
+
+       cover += cell->covered_height*GRID_X*2;
+       clip += cell->covered_height*GRID_X*2;
+       area = cover - cell->uncovered_area;
+
+       if (area != last_cover) {
+           spans[num_spans].x = x;
+           spans[num_spans].coverage = GRID_AREA_TO_ALPHA (area);
+           spans[num_spans].inverse = 0;
+           last_cover = area;
+           ++num_spans;
+       }
+
+       prev_x = x+1;
+    }
+
+    /* Dump them into the renderer. */
+    return renderer->render_rows (renderer, y, height, spans, num_spans);
+}
+
+static void
+glitter_scan_converter_render(glitter_scan_converter_t *converter,
+                             int nonzero_fill,
+                             cairo_span_renderer_t *span_renderer,
+                             struct pool *span_pool)
+{
+    int i, j;
+    int ymax_i = converter->ymax / GRID_Y;
+    int ymin_i = converter->ymin / GRID_Y;
+    int h = ymax_i - ymin_i;
+    struct polygon *polygon = converter->polygon;
+    struct cell_list *coverages = converter->coverages;
+    struct active_list *active = converter->active;
+
+    /* Render each pixel row. */
+    for (i = 0; i < h; i = j) {
+       int do_full_step = 0;
+
+       j = i + 1;
+
+       /* Determine if we can ignore this row or use the full pixel
+        * stepper. */
+       if (GRID_Y == EDGE_Y_BUCKET_HEIGHT && ! polygon->y_buckets[i]) {
+           if (! active->head) {
+               for (; j < h && ! polygon->y_buckets[j]; j++)
+                   ;
+               continue;
+           }
+
+           do_full_step = active_list_can_step_full_row (active);
+       }
+
+       if (do_full_step) {
+           /* Step by a full pixel row's worth. */
+           if (nonzero_fill)
+               apply_nonzero_fill_rule_and_step_edges (active, coverages);
+           else
+               apply_evenodd_fill_rule_and_step_edges (active, coverages);
+
+           if (active_list_is_vertical (active)) {
+               while (j < h &&
+                      polygon->y_buckets[j] == NULL &&
+                      active->min_height >= 2*GRID_Y)
+               {
+                   active->min_height -= GRID_Y;
+                   j++;
+               }
+               if (j != i + 1)
+                   step_edges (active, j - (i + 1));
+           }
+       } else {
+           grid_scaled_y_t suby;
+
+           /* Subsample this row. */
+           for (suby = 0; suby < GRID_Y; suby++) {
+               grid_scaled_y_t y = (i+ymin_i)*GRID_Y + suby;
+
+               if (polygon->y_buckets[i]) {
+                   active_list_merge_edges_from_polygon (active,
+                                                         &polygon->y_buckets[i], y,
+                                                         polygon);
+               }
+
+               if (nonzero_fill)
+                   apply_nonzero_fill_rule_for_subrow (active, coverages);
+               else
+                   apply_evenodd_fill_rule_for_subrow (active, coverages);
+
+               active_list_substep_edges(active);
+           }
+       }
+
+       blit_coverages (coverages, span_renderer, span_pool, i+ymin_i, j -i);
+       cell_list_reset (coverages);
+
+       if (! active->head)
+           active->min_height = INT_MAX;
+       else
+           active->min_height -= GRID_Y;
+    }
+}
+
+struct _cairo_clip_tor_scan_converter {
+    cairo_scan_converter_t base;
+
+    glitter_scan_converter_t converter[1];
+    cairo_fill_rule_t fill_rule;
+    cairo_antialias_t antialias;
+
+    cairo_fill_rule_t clip_fill_rule;
+    cairo_antialias_t clip_antialias;
+
+    jmp_buf jmp;
+
+    struct {
+       struct pool base[1];
+       cairo_half_open_span_t embedded[32];
+    } span_pool;
+};
+
+typedef struct _cairo_clip_tor_scan_converter cairo_clip_tor_scan_converter_t;
+
+static void
+_cairo_clip_tor_scan_converter_destroy (void *converter)
+{
+    cairo_clip_tor_scan_converter_t *self = converter;
+    if (self == NULL) {
+       return;
+    }
+    _glitter_scan_converter_fini (self->converter);
+    pool_fini (self->span_pool.base);
+    free(self);
+}
+
+static cairo_status_t
+_cairo_clip_tor_scan_converter_generate (void                  *converter,
+                                   cairo_span_renderer_t       *renderer)
+{
+    cairo_clip_tor_scan_converter_t *self = converter;
+    cairo_status_t status;
+
+    if ((status = setjmp (self->jmp)))
+       return _cairo_scan_converter_set_error (self, _cairo_error (status));
+
+    glitter_scan_converter_render (self->converter,
+                                  self->fill_rule == CAIRO_FILL_RULE_WINDING,
+                                  renderer,
+                                  self->span_pool.base);
+    return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_scan_converter_t *
+_cairo_clip_tor_scan_converter_create (cairo_clip_t *clip,
+                                      cairo_polygon_t *polygon,
+                                      cairo_fill_rule_t fill_rule,
+                                      cairo_antialias_t antialias)
+{
+    cairo_clip_tor_scan_converter_t *self;
+    cairo_polygon_t clipper;
+    cairo_status_t status;
+    int i;
+
+    self = calloc (1, sizeof(struct _cairo_clip_tor_scan_converter));
+    if (unlikely (self == NULL)) {
+       status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+       goto bail_nomem;
+    }
+
+    self->base.destroy = _cairo_clip_tor_scan_converter_destroy;
+    self->base.generate = _cairo_clip_tor_scan_converter_generate;
+
+    pool_init (self->span_pool.base, &self->jmp,
+              250 * sizeof(self->span_pool.embedded[0]),
+              sizeof(self->span_pool.embedded));
+
+    _glitter_scan_converter_init (self->converter, &self->jmp);
+    status = glitter_scan_converter_reset (self->converter,
+                                          clip->extents.y,
+                                          clip->extents.y + clip->extents.height);
+    if (unlikely (status))
+       goto bail;
+
+    self->fill_rule = fill_rule;
+    self->antialias = antialias;
+
+    for (i = 0; i < polygon->num_edges; i++)
+        glitter_scan_converter_add_edge (self->converter,
+                                         &polygon->edges[i],
+                                         FALSE);
+
+    status = _cairo_clip_get_polygon (clip,
+                                     &clipper,
+                                     &self->clip_fill_rule,
+                                     &self->clip_antialias);
+    if (unlikely (status))
+       goto bail;
+
+    for (i = 0; i < clipper.num_edges; i++)
+        glitter_scan_converter_add_edge (self->converter,
+                                         &clipper.edges[i],
+                                         TRUE);
+    _cairo_polygon_fini (&clipper);
+
+    return &self->base;
+
+ bail:
+    self->base.destroy(&self->base);
+ bail_nomem:
+    return _cairo_scan_converter_create_in_error (status);
+}
+
diff --git a/src/cairo-clip.c b/src/cairo-clip.c
new file mode 100644 (file)
index 0000000..c89e8be
--- /dev/null
@@ -0,0 +1,817 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ * Copyright © 2005 Red Hat, Inc.
+ * Copyright © 2009 Chris Wilson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *     Carl D. Worth <cworth@cworth.org>
+ *     Kristian Høgsberg <krh@redhat.com>
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+#include "cairo-clip-private.h"
+#include "cairo-error-private.h"
+#include "cairo-freed-pool-private.h"
+#include "cairo-gstate-private.h"
+#include "cairo-path-fixed-private.h"
+#include "cairo-pattern-private.h"
+#include "cairo-composite-rectangles-private.h"
+#include "cairo-region-private.h"
+
+static freed_pool_t clip_path_pool;
+static freed_pool_t clip_pool;
+
+const cairo_clip_t __cairo_clip_all;
+
+static cairo_clip_path_t *
+_cairo_clip_path_create (cairo_clip_t *clip)
+{
+    cairo_clip_path_t *clip_path;
+
+    clip_path = _freed_pool_get (&clip_path_pool);
+    if (unlikely (clip_path == NULL)) {
+       clip_path = malloc (sizeof (cairo_clip_path_t));
+       if (unlikely (clip_path == NULL))
+           return NULL;
+    }
+
+    CAIRO_REFERENCE_COUNT_INIT (&clip_path->ref_count, 1);
+
+    clip_path->prev = clip->path;
+    clip->path = clip_path;
+
+    return clip_path;
+}
+
+static cairo_clip_path_t *
+_cairo_clip_path_reference (cairo_clip_path_t *clip_path)
+{
+    assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&clip_path->ref_count));
+
+    _cairo_reference_count_inc (&clip_path->ref_count);
+
+    return clip_path;
+}
+
+void
+_cairo_clip_path_destroy (cairo_clip_path_t *clip_path)
+{
+    assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&clip_path->ref_count));
+
+    if (! _cairo_reference_count_dec_and_test (&clip_path->ref_count))
+       return;
+
+    _cairo_path_fixed_fini (&clip_path->path);
+
+    if (clip_path->prev != NULL)
+       _cairo_clip_path_destroy (clip_path->prev);
+
+    _freed_pool_put (&clip_path_pool, clip_path);
+}
+
+cairo_clip_t *
+_cairo_clip_create (void)
+{
+    cairo_clip_t *clip;
+
+    clip = _freed_pool_get (&clip_pool);
+    if (unlikely (clip == NULL)) {
+       clip = malloc (sizeof (cairo_clip_t));
+       if (unlikely (clip == NULL))
+           return NULL;
+    }
+
+    clip->extents = _cairo_unbounded_rectangle;
+
+    clip->path = NULL;
+    clip->boxes = NULL;
+    clip->num_boxes = 0;
+    clip->region = NULL;
+    clip->is_region = FALSE;
+
+    return clip;
+}
+
+void
+_cairo_clip_destroy (cairo_clip_t *clip)
+{
+    if (clip == NULL || _cairo_clip_is_all_clipped (clip))
+       return;
+
+    if (clip->path != NULL)
+       _cairo_clip_path_destroy (clip->path);
+
+    if (clip->boxes != &clip->embedded_box)
+       free (clip->boxes);
+    cairo_region_destroy (clip->region);
+
+    _freed_pool_put (&clip_pool, clip);
+}
+
+cairo_clip_t *
+_cairo_clip_copy (const cairo_clip_t *clip)
+{
+    cairo_clip_t *copy;
+
+    if (clip == NULL || _cairo_clip_is_all_clipped (clip))
+       return (cairo_clip_t *) clip;
+
+    copy = _cairo_clip_create ();
+
+    if (clip->path)
+       copy->path = _cairo_clip_path_reference (clip->path);
+
+    if (clip->num_boxes) {
+       if (clip->num_boxes == 1) {
+           copy->boxes = &copy->embedded_box;
+       } else {
+           copy->boxes = _cairo_malloc_ab (clip->num_boxes, sizeof (cairo_box_t));
+           if (unlikely (copy->boxes == NULL))
+               return _cairo_clip_set_all_clipped (copy);
+       }
+
+       memcpy (copy->boxes, clip->boxes,
+               clip->num_boxes * sizeof (cairo_box_t));
+       copy->num_boxes = clip->num_boxes;
+    }
+
+    copy->extents = clip->extents;
+    copy->region = cairo_region_reference (clip->region);
+    copy->is_region = clip->is_region;
+
+    return copy;
+}
+
+cairo_clip_t *
+_cairo_clip_copy_region (const cairo_clip_t *clip)
+{
+    cairo_clip_t *copy;
+    int i;
+
+    if (clip == NULL || _cairo_clip_is_all_clipped (clip))
+       return (cairo_clip_t *) clip;
+
+    assert (clip->num_boxes);
+
+    copy = _cairo_clip_create ();
+    copy->extents = clip->extents;
+
+    if (clip->num_boxes == 1) {
+       copy->boxes = &copy->embedded_box;
+    } else {
+       copy->boxes = _cairo_malloc_ab (clip->num_boxes, sizeof (cairo_box_t));
+       if (unlikely (copy->boxes == NULL))
+           return _cairo_clip_set_all_clipped (copy);
+    }
+
+    for (i = 0; i < clip->num_boxes; i++) {
+       copy->boxes[i].p1.x = _cairo_fixed_floor (clip->boxes[i].p1.x);
+       copy->boxes[i].p1.y = _cairo_fixed_floor (clip->boxes[i].p1.y);
+       copy->boxes[i].p2.x = _cairo_fixed_ceil (clip->boxes[i].p2.x);
+       copy->boxes[i].p2.y = _cairo_fixed_ceil (clip->boxes[i].p2.y);
+    }
+    copy->num_boxes = clip->num_boxes;
+
+    copy->region = cairo_region_reference (clip->region);
+    copy->is_region = TRUE;
+
+    return copy;
+}
+
+cairo_clip_t *
+_cairo_clip_intersect_path (cairo_clip_t       *clip,
+                           const cairo_path_fixed_t *path,
+                           cairo_fill_rule_t   fill_rule,
+                           double              tolerance,
+                           cairo_antialias_t   antialias)
+{
+    cairo_clip_path_t *clip_path;
+    cairo_status_t status;
+    cairo_rectangle_int_t extents;
+    cairo_box_t box;
+
+    if (_cairo_clip_is_all_clipped (clip))
+       return clip;
+
+    /* catch the empty clip path */
+    if (_cairo_path_fixed_fill_is_empty (path))
+       return _cairo_clip_set_all_clipped (clip);
+
+    if (_cairo_path_fixed_is_box (path, &box)) {
+       if (antialias == CAIRO_ANTIALIAS_NONE) {
+           box.p1.x = _cairo_fixed_round_down (box.p1.x);
+           box.p1.y = _cairo_fixed_round_down (box.p1.y);
+           box.p2.x = _cairo_fixed_round_down (box.p2.x);
+           box.p2.y = _cairo_fixed_round_down (box.p2.y);
+       }
+
+       return _cairo_clip_intersect_box (clip, &box);
+    }
+    if (_cairo_path_fixed_fill_is_rectilinear (path))
+       return _cairo_clip_intersect_rectilinear_path (clip, path,
+                                                      fill_rule, antialias);
+
+    _cairo_path_fixed_approximate_clip_extents (path, &extents);
+    if (extents.width == 0 || extents.height == 0)
+       return _cairo_clip_set_all_clipped (clip);
+
+    clip = _cairo_clip_intersect_rectangle (clip, &extents);
+    if (_cairo_clip_is_all_clipped (clip))
+       return clip;
+
+    clip_path = _cairo_clip_path_create (clip);
+    if (unlikely (clip_path == NULL))
+       return _cairo_clip_set_all_clipped (clip);
+
+    status = _cairo_path_fixed_init_copy (&clip_path->path, path);
+    if (unlikely (status))
+       return _cairo_clip_set_all_clipped (clip);
+
+    clip_path->fill_rule = fill_rule;
+    clip_path->tolerance = tolerance;
+    clip_path->antialias = antialias;
+
+    if (clip->region) {
+       cairo_region_destroy (clip->region);
+       clip->region = NULL;
+    }
+
+    clip->is_region = FALSE;
+    return clip;
+}
+
+static cairo_clip_t *
+_cairo_clip_intersect_clip_path (cairo_clip_t *clip,
+                                const cairo_clip_path_t *clip_path)
+{
+    if (clip_path->prev)
+       clip = _cairo_clip_intersect_clip_path (clip, clip_path->prev);
+
+    return _cairo_clip_intersect_path (clip,
+                                      &clip_path->path,
+                                      clip_path->fill_rule,
+                                      clip_path->tolerance,
+                                      clip_path->antialias);
+}
+
+cairo_clip_t *
+_cairo_clip_intersect_clip (cairo_clip_t *clip,
+                           const cairo_clip_t *other)
+{
+    if (_cairo_clip_is_all_clipped (clip))
+       return clip;
+
+    if (other == NULL)
+       return clip;
+
+    if (clip == NULL)
+       return _cairo_clip_copy (other);
+
+    if (_cairo_clip_is_all_clipped (other))
+       return _cairo_clip_set_all_clipped (clip);
+
+    if (! _cairo_rectangle_intersect (&clip->extents, &other->extents))
+       return _cairo_clip_set_all_clipped (clip);
+
+    if (other->num_boxes) {
+       cairo_boxes_t boxes;
+
+       _cairo_boxes_init_for_array (&boxes, other->boxes, other->num_boxes);
+       clip = _cairo_clip_intersect_boxes (clip, &boxes);
+    }
+
+    if (! _cairo_clip_is_all_clipped (clip)) {
+       if (other->path) {
+           if (clip->path == NULL)
+               clip->path = _cairo_clip_path_reference (other->path);
+           else
+               clip = _cairo_clip_intersect_clip_path (clip, other->path);
+       }
+    }
+
+    if (clip->region) {
+       cairo_region_destroy (clip->region);
+       clip->region = NULL;
+    }
+    clip->is_region = FALSE;
+
+    return clip;
+}
+
+cairo_bool_t
+_cairo_clip_equal (const cairo_clip_t *clip_a,
+                  const cairo_clip_t *clip_b)
+{
+    const cairo_clip_path_t *cp_a, *cp_b;
+
+    /* are both all-clipped or no-clip? */
+    if (clip_a == clip_b)
+       return TRUE;
+
+    /* or just one of them? */
+    if (clip_a == NULL || clip_b == NULL ||
+       _cairo_clip_is_all_clipped (clip_a) ||
+       _cairo_clip_is_all_clipped (clip_b))
+    {
+       return FALSE;
+    }
+
+    /* We have a pair of normal clips, check their contents */
+
+    if (clip_a->num_boxes != clip_b->num_boxes)
+       return FALSE;
+
+    if (memcmp (clip_a->boxes, clip_b->boxes,
+               sizeof (cairo_box_t) * clip_a->num_boxes))
+       return FALSE;
+
+    cp_a = clip_a->path;
+    cp_b = clip_b->path;
+    while (cp_a && cp_b) {
+       if (cp_a == cp_b)
+           return TRUE;
+
+       /* XXX compare reduced polygons? */
+
+       if (cp_a->antialias != cp_b->antialias)
+           return FALSE;
+
+       if (cp_a->tolerance != cp_b->tolerance)
+           return FALSE;
+
+       if (cp_a->fill_rule != cp_b->fill_rule)
+           return FALSE;
+
+       if (! _cairo_path_fixed_equal (&cp_a->path,
+                                      &cp_b->path))
+           return FALSE;
+
+       cp_a = cp_a->prev;
+       cp_b = cp_b->prev;
+    }
+
+    return cp_a == NULL && cp_b == NULL;
+}
+
+static cairo_clip_t *
+_cairo_clip_path_copy_with_translation (cairo_clip_t      *clip,
+                                       cairo_clip_path_t *other_path,
+                                       int fx, int fy)
+{
+    cairo_status_t status;
+    cairo_clip_path_t *clip_path;
+
+    if (other_path->prev != NULL)
+       clip = _cairo_clip_path_copy_with_translation (clip, other_path->prev,
+                                                      fx, fy);
+    if (_cairo_clip_is_all_clipped (clip))
+       return clip;
+
+    clip_path = _cairo_clip_path_create (clip);
+    if (unlikely (clip_path == NULL))
+       return _cairo_clip_set_all_clipped (clip);
+
+    status = _cairo_path_fixed_init_copy (&clip_path->path,
+                                         &other_path->path);
+    if (unlikely (status))
+       return _cairo_clip_set_all_clipped (clip);
+
+    _cairo_path_fixed_translate (&clip_path->path, fx, fy);
+
+    clip_path->fill_rule = other_path->fill_rule;
+    clip_path->tolerance = other_path->tolerance;
+    clip_path->antialias = other_path->antialias;
+
+    return clip;
+}
+
+cairo_clip_t *
+_cairo_clip_translate (cairo_clip_t *clip, int tx, int ty)
+{
+    int fx, fy, i;
+    cairo_clip_path_t *clip_path;
+
+    if (clip == NULL || _cairo_clip_is_all_clipped (clip))
+       return clip;
+
+    if (tx == 0 && ty == 0)
+       return clip;
+
+    fx = _cairo_fixed_from_int (tx);
+    fy = _cairo_fixed_from_int (ty);
+
+    for (i = 0; i < clip->num_boxes; i++) {
+       clip->boxes[i].p1.x += fx;
+       clip->boxes[i].p2.x += fx;
+       clip->boxes[i].p1.y += fy;
+       clip->boxes[i].p2.y += fy;
+    }
+
+    clip->extents.x += tx;
+    clip->extents.y += ty;
+
+    if (clip->path == NULL)
+       return clip;
+
+    clip_path = clip->path;
+    clip->path = NULL;
+    clip = _cairo_clip_path_copy_with_translation (clip, clip_path, fx, fy);
+    _cairo_clip_path_destroy (clip_path);
+
+    return clip;
+}
+
+static cairo_status_t
+_cairo_path_fixed_add_box (cairo_path_fixed_t *path,
+                          const cairo_box_t *box)
+{
+    cairo_status_t status;
+
+    status = _cairo_path_fixed_move_to (path, box->p1.x, box->p1.y);
+    if (unlikely (status))
+       return status;
+
+    status = _cairo_path_fixed_line_to (path, box->p2.x, box->p1.y);
+    if (unlikely (status))
+       return status;
+
+    status = _cairo_path_fixed_line_to (path, box->p2.x, box->p2.y);
+    if (unlikely (status))
+       return status;
+
+    status = _cairo_path_fixed_line_to (path, box->p1.x, box->p2.y);
+    if (unlikely (status))
+       return status;
+
+    return _cairo_path_fixed_close_path (path);
+}
+
+static cairo_status_t
+_cairo_path_fixed_init_from_boxes (cairo_path_fixed_t *path,
+                                  const cairo_boxes_t *boxes)
+{
+    cairo_status_t status;
+    const struct _cairo_boxes_chunk *chunk;
+    int i;
+
+    _cairo_path_fixed_init (path);
+    if (boxes->num_boxes == 0)
+       return CAIRO_STATUS_SUCCESS;
+
+    for (chunk = &boxes->chunks; chunk; chunk = chunk->next) {
+       for (i = 0; i < chunk->count; i++) {
+           status = _cairo_path_fixed_add_box (path, &chunk->base[i]);
+           if (unlikely (status)) {
+               _cairo_path_fixed_fini (path);
+               return status;
+           }
+       }
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_clip_t *
+_cairo_clip_intersect_clip_path_transformed (cairo_clip_t *clip,
+                                            const cairo_clip_path_t *clip_path,
+                                            const cairo_matrix_t *m)
+{
+    cairo_path_fixed_t path;
+
+    if (clip_path->prev)
+       clip = _cairo_clip_intersect_clip_path_transformed (clip,
+                                                           clip_path->prev,
+                                                           m);
+
+    if (_cairo_path_fixed_init_copy (&path, &clip_path->path))
+       return _cairo_clip_set_all_clipped (clip);
+
+    clip =  _cairo_clip_intersect_path (clip,
+                                      &path,
+                                      clip_path->fill_rule,
+                                      clip_path->tolerance,
+                                      clip_path->antialias);
+    _cairo_path_fixed_fini (&path);
+
+    return clip;
+}
+
+cairo_clip_t *
+_cairo_clip_transform (cairo_clip_t *clip, const cairo_matrix_t *m)
+{
+    cairo_clip_t *copy;
+
+    if (clip == NULL || _cairo_clip_is_all_clipped (clip))
+       return clip;
+
+    if (_cairo_matrix_is_translation (m))
+       return _cairo_clip_translate (clip, m->x0, m->y0);
+
+    copy = _cairo_clip_create ();
+
+    if (clip->num_boxes) {
+       cairo_path_fixed_t path;
+       cairo_boxes_t boxes;
+
+       _cairo_boxes_init_for_array (&boxes, clip->boxes, clip->num_boxes);
+       _cairo_path_fixed_init_from_boxes (&path, &boxes);
+       _cairo_path_fixed_transform (&path, m);
+
+       copy = _cairo_clip_intersect_path (copy, &path,
+                                          CAIRO_FILL_RULE_WINDING,
+                                          0.1,
+                                          CAIRO_ANTIALIAS_DEFAULT);
+
+       _cairo_path_fixed_fini (&path);
+    }
+
+    if (clip->path)
+       copy = _cairo_clip_intersect_clip_path_transformed (copy, clip->path,m);
+
+    _cairo_clip_destroy (clip);
+    return copy;
+}
+
+cairo_clip_t *
+_cairo_clip_copy_with_translation (const cairo_clip_t *clip, int tx, int ty)
+{
+    cairo_clip_t *copy;
+    int fx, fy, i;
+
+    if (clip == NULL || _cairo_clip_is_all_clipped (clip))
+       return (cairo_clip_t *)clip;
+
+    if (tx == 0 && ty == 0)
+       return _cairo_clip_copy (clip);
+
+    copy = _cairo_clip_create ();
+    if (copy == NULL)
+           return _cairo_clip_set_all_clipped (copy);
+
+    fx = _cairo_fixed_from_int (tx);
+    fy = _cairo_fixed_from_int (ty);
+
+    if (clip->num_boxes) {
+       if (clip->num_boxes == 1) {
+           copy->boxes = &copy->embedded_box;
+       } else {
+           copy->boxes = _cairo_malloc_ab (clip->num_boxes, sizeof (cairo_box_t));
+           if (unlikely (copy->boxes == NULL))
+               return _cairo_clip_set_all_clipped (copy);
+       }
+
+       for (i = 0; i < clip->num_boxes; i++) {
+           copy->boxes[i].p1.x = clip->boxes[i].p1.x + fx;
+           copy->boxes[i].p2.x = clip->boxes[i].p2.x + fx;
+           copy->boxes[i].p1.y = clip->boxes[i].p1.y + fy;
+           copy->boxes[i].p2.y = clip->boxes[i].p2.y + fy;
+       }
+       copy->num_boxes = clip->num_boxes;
+    }
+
+    copy->extents = clip->extents;
+    copy->extents.x += tx;
+    copy->extents.y += ty;
+
+    if (clip->path == NULL)
+       return copy;
+
+    return _cairo_clip_path_copy_with_translation (copy, clip->path, fx, fy);
+}
+
+cairo_bool_t
+_cairo_clip_contains_extents (const cairo_clip_t *clip,
+                             const cairo_composite_rectangles_t *extents)
+{
+    const cairo_rectangle_int_t *rect;
+
+    rect = extents->is_bounded ? &extents->bounded : &extents->unbounded;
+    return _cairo_clip_contains_rectangle (clip, rect);
+}
+
+void
+_cairo_debug_print_clip (FILE *stream, const cairo_clip_t *clip)
+{
+    int i;
+
+    if (clip == NULL) {
+       fprintf (stream, "no clip\n");
+       return;
+    }
+
+    if (_cairo_clip_is_all_clipped (clip)) {
+       fprintf (stream, "clip: all-clipped\n");
+       return;
+    }
+
+    fprintf (stream, "clip:\n");
+    fprintf (stream, "  extents: (%d, %d) x (%d, %d), is-region? %d",
+            clip->extents.x, clip->extents.y,
+            clip->extents.width, clip->extents.height,
+            clip->is_region);
+
+    fprintf (stream, "  num_boxes = %d\n", clip->num_boxes);
+    for (i = 0; i < clip->num_boxes; i++) {
+       fprintf (stream, "  [%d] = (%f, %f), (%f, %f)\n", i,
+                _cairo_fixed_to_double (clip->boxes[i].p1.x),
+                _cairo_fixed_to_double (clip->boxes[i].p1.y),
+                _cairo_fixed_to_double (clip->boxes[i].p2.x),
+                _cairo_fixed_to_double (clip->boxes[i].p2.y));
+    }
+
+    if (clip->path) {
+       cairo_clip_path_t *clip_path = clip->path;
+       do {
+           fprintf (stream, "path: aa=%d, tolerance=%f, rule=%d: ",
+                    clip_path->antialias,
+                    clip_path->tolerance,
+                    clip_path->fill_rule);
+           _cairo_debug_print_path (stream, &clip_path->path);
+           fprintf (stream, "\n");
+       } while ((clip_path = clip_path->prev) != NULL);
+    }
+}
+
+const cairo_rectangle_int_t *
+_cairo_clip_get_extents (const cairo_clip_t *clip)
+{
+    if (clip == NULL)
+       return &_cairo_unbounded_rectangle;
+
+    if (_cairo_clip_is_all_clipped (clip))
+       return &_cairo_empty_rectangle;
+
+    return &clip->extents;
+}
+
+const cairo_rectangle_list_t _cairo_rectangles_nil =
+  { CAIRO_STATUS_NO_MEMORY, NULL, 0 };
+static const cairo_rectangle_list_t _cairo_rectangles_not_representable =
+  { CAIRO_STATUS_CLIP_NOT_REPRESENTABLE, NULL, 0 };
+
+static cairo_bool_t
+_cairo_clip_int_rect_to_user (cairo_gstate_t *gstate,
+                             cairo_rectangle_int_t *clip_rect,
+                             cairo_rectangle_t *user_rect)
+{
+    cairo_bool_t is_tight;
+
+    double x1 = clip_rect->x;
+    double y1 = clip_rect->y;
+    double x2 = clip_rect->x + (int) clip_rect->width;
+    double y2 = clip_rect->y + (int) clip_rect->height;
+
+    _cairo_gstate_backend_to_user_rectangle (gstate,
+                                            &x1, &y1, &x2, &y2,
+                                            &is_tight);
+
+    user_rect->x = x1;
+    user_rect->y = y1;
+    user_rect->width  = x2 - x1;
+    user_rect->height = y2 - y1;
+
+    return is_tight;
+}
+
+cairo_rectangle_list_t *
+_cairo_rectangle_list_create_in_error (cairo_status_t status)
+{
+    cairo_rectangle_list_t *list;
+
+    if (status == CAIRO_STATUS_NO_MEMORY)
+       return (cairo_rectangle_list_t*) &_cairo_rectangles_nil;
+    if (status == CAIRO_STATUS_CLIP_NOT_REPRESENTABLE)
+       return (cairo_rectangle_list_t*) &_cairo_rectangles_not_representable;
+
+    list = malloc (sizeof (*list));
+    if (unlikely (list == NULL)) {
+       status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+       return (cairo_rectangle_list_t*) &_cairo_rectangles_nil;
+    }
+
+    list->status = status;
+    list->rectangles = NULL;
+    list->num_rectangles = 0;
+
+    return list;
+}
+
+cairo_rectangle_list_t *
+_cairo_clip_copy_rectangle_list (cairo_clip_t *clip, cairo_gstate_t *gstate)
+{
+#define ERROR_LIST(S) _cairo_rectangle_list_create_in_error (_cairo_error (S))
+
+    cairo_rectangle_list_t *list;
+    cairo_rectangle_t *rectangles = NULL;
+    cairo_region_t *region = NULL;
+    int n_rects = 0;
+    int i;
+
+    if (clip == NULL)
+       return ERROR_LIST (CAIRO_STATUS_CLIP_NOT_REPRESENTABLE);
+
+    if (_cairo_clip_is_all_clipped (clip))
+       goto DONE;
+
+    if (! _cairo_clip_is_region (clip))
+       return ERROR_LIST (CAIRO_STATUS_CLIP_NOT_REPRESENTABLE);
+
+    region = _cairo_clip_get_region (clip);
+    if (region == NULL)
+       return ERROR_LIST (CAIRO_STATUS_NO_MEMORY);
+
+    n_rects = cairo_region_num_rectangles (region);
+    if (n_rects) {
+       rectangles = _cairo_malloc_ab (n_rects, sizeof (cairo_rectangle_t));
+       if (unlikely (rectangles == NULL)) {
+           return ERROR_LIST (CAIRO_STATUS_NO_MEMORY);
+       }
+
+       for (i = 0; i < n_rects; ++i) {
+           cairo_rectangle_int_t clip_rect;
+
+           cairo_region_get_rectangle (region, i, &clip_rect);
+
+           if (! _cairo_clip_int_rect_to_user (gstate,
+                                               &clip_rect,
+                                               &rectangles[i]))
+           {
+               free (rectangles);
+               return ERROR_LIST (CAIRO_STATUS_CLIP_NOT_REPRESENTABLE);
+           }
+       }
+    }
+
+ DONE:
+    list = malloc (sizeof (cairo_rectangle_list_t));
+    if (unlikely (list == NULL)) {
+        free (rectangles);
+       return ERROR_LIST (CAIRO_STATUS_NO_MEMORY);
+    }
+
+    list->status = CAIRO_STATUS_SUCCESS;
+    list->rectangles = rectangles;
+    list->num_rectangles = n_rects;
+    return list;
+
+#undef ERROR_LIST
+}
+
+/**
+ * cairo_rectangle_list_destroy:
+ * @rectangle_list: a rectangle list, as obtained from cairo_copy_clip_rectangle_list()
+ *
+ * Unconditionally frees @rectangle_list and all associated
+ * references. After this call, the @rectangle_list pointer must not
+ * be dereferenced.
+ *
+ * Since: 1.4
+ **/
+void
+cairo_rectangle_list_destroy (cairo_rectangle_list_t *rectangle_list)
+{
+    if (rectangle_list == NULL || rectangle_list == &_cairo_rectangles_nil ||
+        rectangle_list == &_cairo_rectangles_not_representable)
+        return;
+
+    free (rectangle_list->rectangles);
+    free (rectangle_list);
+}
+
+void
+_cairo_clip_reset_static_data (void)
+{
+    _freed_pool_reset (&clip_path_pool);
+    _freed_pool_reset (&clip_pool);
+}
diff --git a/src/cairo-color.c b/src/cairo-color.c
new file mode 100644 (file)
index 0000000..9c85255
--- /dev/null
@@ -0,0 +1,198 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *     Carl D. Worth <cworth@cworth.org>
+ */
+
+#include "cairoint.h"
+
+static cairo_color_t const cairo_color_white = {
+    1.0,    1.0,    1.0,    1.0,
+    0xffff, 0xffff, 0xffff, 0xffff
+};
+
+static cairo_color_t const cairo_color_black = {
+    0.0, 0.0, 0.0, 1.0,
+    0x0, 0x0, 0x0, 0xffff
+};
+
+static cairo_color_t const cairo_color_transparent = {
+    0.0, 0.0, 0.0, 0.0,
+    0x0, 0x0, 0x0, 0x0
+};
+
+static cairo_color_t const cairo_color_magenta = {
+    1.0,    0.0, 1.0,    1.0,
+    0xffff, 0x0, 0xffff, 0xffff
+};
+
+const cairo_color_t *
+_cairo_stock_color (cairo_stock_t stock)
+{
+    switch (stock) {
+    case CAIRO_STOCK_WHITE:
+       return &cairo_color_white;
+    case CAIRO_STOCK_BLACK:
+       return &cairo_color_black;
+    case CAIRO_STOCK_TRANSPARENT:
+       return &cairo_color_transparent;
+
+    case CAIRO_STOCK_NUM_COLORS:
+    default:
+       ASSERT_NOT_REACHED;
+       /* If the user can get here somehow, give a color that indicates a
+        * problem. */
+       return &cairo_color_magenta;
+    }
+}
+
+/* Convert a double in [0.0, 1.0] to an integer in [0, 65535]
+ * The conversion is designed to divide the input range into 65536
+ * equally-sized regions. This is achieved by multiplying by 65536 and
+ * then special-casing the result of an input value of 1.0 so that it
+ * maps to 65535 instead of 65536.
+ */
+uint16_t
+_cairo_color_double_to_short (double d)
+{
+    uint32_t i;
+    i = (uint32_t) (d * 65536);
+    i -= (i >> 16);
+    return i;
+}
+
+static void
+_cairo_color_compute_shorts (cairo_color_t *color)
+{
+    color->red_short   = _cairo_color_double_to_short (color->red   * color->alpha);
+    color->green_short = _cairo_color_double_to_short (color->green * color->alpha);
+    color->blue_short  = _cairo_color_double_to_short (color->blue  * color->alpha);
+    color->alpha_short = _cairo_color_double_to_short (color->alpha);
+}
+
+void
+_cairo_color_init_rgba (cairo_color_t *color,
+                       double red, double green, double blue,
+                       double alpha)
+{
+    color->red   = red;
+    color->green = green;
+    color->blue  = blue;
+    color->alpha = alpha;
+
+    _cairo_color_compute_shorts (color);
+}
+
+void
+_cairo_color_multiply_alpha (cairo_color_t *color,
+                            double         alpha)
+{
+    color->alpha *= alpha;
+
+    _cairo_color_compute_shorts (color);
+}
+
+void
+_cairo_color_get_rgba (cairo_color_t *color,
+                      double        *red,
+                      double        *green,
+                      double        *blue,
+                      double        *alpha)
+{
+    *red   = color->red;
+    *green = color->green;
+    *blue  = color->blue;
+    *alpha = color->alpha;
+}
+
+void
+_cairo_color_get_rgba_premultiplied (cairo_color_t *color,
+                                    double        *red,
+                                    double        *green,
+                                    double        *blue,
+                                    double        *alpha)
+{
+    *red   = color->red   * color->alpha;
+    *green = color->green * color->alpha;
+    *blue  = color->blue  * color->alpha;
+    *alpha = color->alpha;
+}
+
+/* NB: This function works both for unmultiplied and premultiplied colors */
+cairo_bool_t
+_cairo_color_equal (const cairo_color_t *color_a,
+                   const cairo_color_t *color_b)
+{
+    if (color_a == color_b)
+       return TRUE;
+
+    if (color_a->alpha_short != color_b->alpha_short)
+        return FALSE;
+
+    if (color_a->alpha_short == 0)
+        return TRUE;
+
+    return color_a->red_short   == color_b->red_short   &&
+           color_a->green_short == color_b->green_short &&
+           color_a->blue_short  == color_b->blue_short;
+}
+
+cairo_bool_t
+_cairo_color_stop_equal (const cairo_color_stop_t *color_a,
+                        const cairo_color_stop_t *color_b)
+{
+    if (color_a == color_b)
+       return TRUE;
+
+    return color_a->alpha_short == color_b->alpha_short &&
+           color_a->red_short   == color_b->red_short   &&
+           color_a->green_short == color_b->green_short &&
+           color_a->blue_short  == color_b->blue_short;
+}
+
+cairo_content_t
+_cairo_color_get_content (const cairo_color_t *color)
+{
+    if (CAIRO_COLOR_IS_OPAQUE (color))
+        return CAIRO_CONTENT_COLOR;
+
+    if (color->red_short == 0 &&
+       color->green_short == 0 &&
+       color->blue_short == 0)
+    {
+        return CAIRO_CONTENT_ALPHA;
+    }
+
+    return CAIRO_CONTENT_COLOR_ALPHA;
+}
diff --git a/src/cairo-combsort-private.h b/src/cairo-combsort-private.h
new file mode 100644 (file)
index 0000000..d359fae
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * Copyright © 2008 Chris Wilson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Chris Wilson
+ *
+ * Contributor(s):
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+/* This fragment implements a comb sort (specifically combsort11) */
+#ifndef _HAVE_CAIRO_COMBSORT_NEWGAP
+#define _HAVE_CAIRO_COMBSORT_NEWGAP
+static inline unsigned int
+_cairo_combsort_newgap (unsigned int gap)
+{
+  gap = 10 * gap / 13;
+  if (gap == 9 || gap == 10)
+    gap = 11;
+  if (gap < 1)
+    gap = 1;
+  return gap;
+}
+#endif
+
+#define CAIRO_COMBSORT_DECLARE(NAME, TYPE, CMP) \
+static void \
+NAME (TYPE *base, unsigned int nmemb) \
+{ \
+  unsigned int gap = nmemb; \
+  unsigned int i, j; \
+  int swapped; \
+  do { \
+      gap = _cairo_combsort_newgap (gap); \
+      swapped = gap > 1; \
+      for (i = 0; i < nmemb-gap ; i++) { \
+         j = i + gap; \
+         if (CMP (base[i], base[j]) > 0 ) { \
+             TYPE tmp; \
+             tmp = base[i]; \
+             base[i] = base[j]; \
+             base[j] = tmp; \
+             swapped = 1; \
+         } \
+      } \
+  } while (swapped); \
+}
+
+#define CAIRO_COMBSORT_DECLARE_WITH_DATA(NAME, TYPE, CMP) \
+static void \
+NAME (TYPE *base, unsigned int nmemb, void *data) \
+{ \
+  unsigned int gap = nmemb; \
+  unsigned int i, j; \
+  int swapped; \
+  do { \
+      gap = _cairo_combsort_newgap (gap); \
+      swapped = gap > 1; \
+      for (i = 0; i < nmemb-gap ; i++) { \
+         j = i + gap; \
+         if (CMP (base[i], base[j], data) > 0 ) { \
+             TYPE tmp; \
+             tmp = base[i]; \
+             base[i] = base[j]; \
+             base[j] = tmp; \
+             swapped = 1; \
+         } \
+      } \
+  } while (swapped); \
+}
diff --git a/src/cairo-compiler-private.h b/src/cairo-compiler-private.h
new file mode 100644 (file)
index 0000000..dfe114b
--- /dev/null
@@ -0,0 +1,244 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *     Carl D. Worth <cworth@cworth.org>
+ */
+
+#ifndef CAIRO_COMPILER_PRIVATE_H
+#define CAIRO_COMPILER_PRIVATE_H
+
+#include "cairo.h"
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+/* Size in bytes of buffer to use off the stack per functions.
+ * Mostly used by text functions.  For larger allocations, they'll
+ * malloc(). */
+#ifndef CAIRO_STACK_BUFFER_SIZE
+#define CAIRO_STACK_BUFFER_SIZE (512 * sizeof (int))
+#endif
+
+#define CAIRO_STACK_ARRAY_LENGTH(T) (CAIRO_STACK_BUFFER_SIZE / sizeof(T))
+
+/*
+ * The goal of this block is to define the following macros for
+ * providing faster linkage to functions in the public API for calls
+ * from within cairo.
+ *
+ * slim_hidden_proto(f)
+ * slim_hidden_proto_no_warn(f)
+ *
+ *   Declares `f' as a library internal function and hides the
+ *   function from the global symbol table.  This macro must be
+ *   expanded after `f' has been declared with a prototype but before
+ *   any calls to the function are seen by the compiler.  The no_warn
+ *   variant inhibits warnings about the return value being unused at
+ *   call sites.  The macro works by renaming `f' to an internal name
+ *   in the symbol table and hiding that.  As far as cairo internal
+ *   calls are concerned they're calling a library internal function
+ *   and thus don't need to bounce via the PLT.
+ *
+ * slim_hidden_def(f)
+ *
+ *   Exports `f' back to the global symbol table.  This macro must be
+ *   expanded right after the function definition and only for symbols
+ *   hidden previously with slim_hidden_proto().  The macro works by
+ *   adding a global entry to the symbol table which points at the
+ *   internal name of `f' created by slim_hidden_proto().
+ *
+ * Functions in the public API which aren't called by the library
+ * don't need to be hidden and re-exported using the slim hidden
+ * macros.
+ */
+#if __GNUC__ >= 3 && defined(__ELF__) && !defined(__sun)
+# define slim_hidden_proto(name)               slim_hidden_proto1(name, slim_hidden_int_name(name)) cairo_private
+# define slim_hidden_proto_no_warn(name)       slim_hidden_proto1(name, slim_hidden_int_name(name)) cairo_private_no_warn
+# define slim_hidden_def(name)                 slim_hidden_def1(name, slim_hidden_int_name(name))
+# define slim_hidden_int_name(name) INT_##name
+# define slim_hidden_proto1(name, internal)                            \
+  extern __typeof (name) name                                          \
+       __asm__ (slim_hidden_asmname (internal))
+# define slim_hidden_def1(name, internal)                              \
+  extern __typeof (name) EXT_##name __asm__(slim_hidden_asmname(name)) \
+       __attribute__((__alias__(slim_hidden_asmname(internal))))
+# define slim_hidden_ulp               slim_hidden_ulp1(__USER_LABEL_PREFIX__)
+# define slim_hidden_ulp1(x)           slim_hidden_ulp2(x)
+# define slim_hidden_ulp2(x)           #x
+# define slim_hidden_asmname(name)     slim_hidden_asmname1(name)
+# define slim_hidden_asmname1(name)    slim_hidden_ulp #name
+#else
+# define slim_hidden_proto(name)               int _cairo_dummy_prototype(void)
+# define slim_hidden_proto_no_warn(name)       int _cairo_dummy_prototype(void)
+# define slim_hidden_def(name)                 int _cairo_dummy_prototype(void)
+#endif
+
+#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
+
+/* slim_internal.h */
+#define CAIRO_HAS_HIDDEN_SYMBOLS 1
+#if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 3)) && \
+    (defined(__ELF__) || defined(__APPLE__)) &&                        \
+    !defined(__sun)
+#define cairo_private_no_warn  __attribute__((__visibility__("hidden")))
+#elif defined(__SUNPRO_C) && (__SUNPRO_C >= 0x550)
+#define cairo_private_no_warn  __hidden
+#else /* not gcc >= 3.3 and not Sun Studio >= 8 */
+#define cairo_private_no_warn
+#undef CAIRO_HAS_HIDDEN_SYMBOLS
+#endif
+
+#ifndef WARN_UNUSED_RESULT
+#define WARN_UNUSED_RESULT
+#endif
+/* Add attribute(warn_unused_result) if supported */
+#define cairo_warn         WARN_UNUSED_RESULT
+#define cairo_private      cairo_private_no_warn cairo_warn
+
+/* This macro allow us to deprecate a function by providing an alias
+   for the old function name to the new function name. With this
+   macro, binary compatibility is preserved. The macro only works on
+   some platforms --- tough.
+
+   Meanwhile, new definitions in the public header file break the
+   source code so that it will no longer link against the old
+   symbols. Instead it will give a descriptive error message
+   indicating that the old function has been deprecated by the new
+   function.
+*/
+#if __GNUC__ >= 2 && defined(__ELF__)
+# define CAIRO_FUNCTION_ALIAS(old, new)                \
+       extern __typeof (new) old               \
+       __asm__ ("" #old)                       \
+       __attribute__((__alias__("" #new)))
+#else
+# define CAIRO_FUNCTION_ALIAS(old, new)
+#endif
+
+/*
+ * Cairo uses the following function attributes in order to improve the
+ * generated code (effectively by manual inter-procedural analysis).
+ *
+ *   'cairo_pure': The function is only allowed to read from its arguments
+ *                 and global memory (i.e. following a pointer argument or
+ *                 accessing a shared variable). The return value should
+ *                 only depend on its arguments, and for an identical set of
+ *                 arguments should return the same value.
+ *
+ *   'cairo_const': The function is only allowed to read from its arguments.
+ *                  It is not allowed to access global memory. The return
+ *                  value should only depend its arguments, and for an
+ *                  identical set of arguments should return the same value.
+ *                  This is currently the most strict function attribute.
+ *
+ * Both these function attributes allow gcc to perform CSE and
+ * constant-folding, with 'cairo_const 'also guaranteeing that pointer contents
+ * do not change across the function call.
+ */
+#if __GNUC__ >= 3
+#define cairo_pure __attribute__((pure))
+#define cairo_const __attribute__((const))
+#define cairo_always_inline inline __attribute__((always_inline))
+#else
+#define cairo_pure
+#define cairo_const
+#define cairo_always_inline inline
+#endif
+
+#if defined(__GNUC__) && (__GNUC__ > 2) && defined(__OPTIMIZE__)
+#define likely(expr) (__builtin_expect (!!(expr), 1))
+#define unlikely(expr) (__builtin_expect (!!(expr), 0))
+#else
+#define likely(expr) (expr)
+#define unlikely(expr) (expr)
+#endif
+
+#ifndef __GNUC__
+#undef __attribute__
+#define __attribute__(x)
+#endif
+
+#if (defined(__WIN32__) && !defined(__WINE__)) || defined(_MSC_VER)
+#define access _access
+#define fdopen _fdopen
+#define hypot _hypot
+#define pclose _pclose
+#define popen _popen
+#define snprintf _snprintf
+#define strdup _strdup
+#define unlink _unlink
+#define vsnprintf _vsnprintf
+#endif
+
+#ifdef _MSC_VER
+#undef inline
+#define inline __inline
+#endif
+
+#if defined(_MSC_VER) && defined(_M_IX86)
+/* When compiling with /Gy and /OPT:ICF identical functions will be folded in together.
+   The CAIRO_ENSURE_UNIQUE macro ensures that a function is always unique and
+   will never be folded into another one. Something like this might eventually
+   be needed for GCC but it seems fine for now. */
+#define CAIRO_ENSURE_UNIQUE                       \
+    do {                                          \
+       char func[] = __FUNCTION__;               \
+       char file[] = __FILE__;                   \
+       __asm {                                   \
+           __asm jmp __internal_skip_line_no     \
+           __asm _emit (__LINE__ & 0xff)         \
+           __asm _emit ((__LINE__>>8) & 0xff)    \
+           __asm _emit ((__LINE__>>16) & 0xff)   \
+           __asm _emit ((__LINE__>>24) & 0xff)   \
+           __asm lea eax, func                   \
+           __asm lea eax, file                   \
+           __asm __internal_skip_line_no:        \
+       };                                        \
+    } while (0)
+#else
+#define CAIRO_ENSURE_UNIQUE    do { } while (0)
+#endif
+
+#ifdef __STRICT_ANSI__
+#undef inline
+#define inline __inline__
+#endif
+
+#endif
diff --git a/src/cairo-composite-rectangles-private.h b/src/cairo-composite-rectangles-private.h
new file mode 100644 (file)
index 0000000..fd77289
--- /dev/null
@@ -0,0 +1,159 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *     Chris Wilson <chris@chris-wilson.co.u>
+ */
+
+#ifndef CAIRO_COMPOSITE_RECTANGLES_PRIVATE_H
+#define CAIRO_COMPOSITE_RECTANGLES_PRIVATE_H
+
+#include "cairo-types-private.h"
+#include "cairo-error-private.h"
+#include "cairo-pattern-private.h"
+
+CAIRO_BEGIN_DECLS
+
+/* Rectangles that take part in a composite operation.
+ *
+ * The source and mask track the extents of the respective patterns in device
+ * space. The unbounded rectangle is essentially the clip rectangle. And the
+ * intersection of all is the bounded rectangle, which is the minimum extents
+ * the operation may require. Whether or not the operation is actually bounded
+ * is tracked in the is_bounded boolean.
+ *
+ */
+struct _cairo_composite_rectangles {
+    cairo_surface_t *surface;
+    cairo_operator_t op;
+
+    cairo_rectangle_int_t source;
+    cairo_rectangle_int_t mask;
+    cairo_rectangle_int_t destination;
+
+    cairo_rectangle_int_t bounded; /* source? IN mask? IN unbounded */
+    cairo_rectangle_int_t unbounded; /* destination IN clip */
+    uint32_t is_bounded;
+
+    cairo_rectangle_int_t source_sample_area;
+    cairo_rectangle_int_t mask_sample_area;
+
+    cairo_pattern_union_t source_pattern;
+    cairo_pattern_union_t mask_pattern;
+    const cairo_pattern_t *original_source_pattern;
+    const cairo_pattern_t *original_mask_pattern;
+
+    cairo_clip_t *clip; /* clip will be reduced to the minimal container */
+};
+
+cairo_private cairo_int_status_t
+_cairo_composite_rectangles_init_for_paint (cairo_composite_rectangles_t *extents,
+                                           cairo_surface_t *surface,
+                                           cairo_operator_t     op,
+                                           const cairo_pattern_t       *source,
+                                           const cairo_clip_t          *clip);
+
+cairo_private cairo_int_status_t
+_cairo_composite_rectangles_init_for_mask (cairo_composite_rectangles_t *extents,
+                                          cairo_surface_t *surface,
+                                          cairo_operator_t      op,
+                                          const cairo_pattern_t        *source,
+                                          const cairo_pattern_t        *mask,
+                                          const cairo_clip_t           *clip);
+
+cairo_private cairo_int_status_t
+_cairo_composite_rectangles_init_for_stroke (cairo_composite_rectangles_t *extents,
+                                            cairo_surface_t *surface,
+                                            cairo_operator_t    op,
+                                            const cairo_pattern_t      *source,
+                                            const cairo_path_fixed_t   *path,
+                                            const cairo_stroke_style_t *style,
+                                            const cairo_matrix_t       *ctm,
+                                            const cairo_clip_t         *clip);
+
+cairo_private cairo_int_status_t
+_cairo_composite_rectangles_init_for_fill (cairo_composite_rectangles_t *extents,
+                                          cairo_surface_t *surface,
+                                          cairo_operator_t      op,
+                                          const cairo_pattern_t        *source,
+                                          const cairo_path_fixed_t     *path,
+                                          const cairo_clip_t           *clip);
+
+cairo_private cairo_int_status_t
+_cairo_composite_rectangles_init_for_boxes (cairo_composite_rectangles_t *extents,
+                                             cairo_surface_t           *surface,
+                                             cairo_operator_t           op,
+                                             const cairo_pattern_t     *source,
+                                             const cairo_boxes_t       *boxes,
+                                             const cairo_clip_t                *clip);
+
+cairo_private cairo_int_status_t
+_cairo_composite_rectangles_init_for_polygon (cairo_composite_rectangles_t *extents,
+                                             cairo_surface_t           *surface,
+                                             cairo_operator_t           op,
+                                             const cairo_pattern_t     *source,
+                                             const cairo_polygon_t     *polygon,
+                                             const cairo_clip_t                *clip);
+
+cairo_private cairo_int_status_t
+_cairo_composite_rectangles_init_for_glyphs (cairo_composite_rectangles_t *extents,
+                                            cairo_surface_t *surface,
+                                            cairo_operator_t            op,
+                                            const cairo_pattern_t      *source,
+                                            cairo_scaled_font_t        *scaled_font,
+                                            cairo_glyph_t              *glyphs,
+                                            int                         num_glyphs,
+                                            const cairo_clip_t         *clip,
+                                            cairo_bool_t               *overlap);
+
+cairo_private cairo_int_status_t
+_cairo_composite_rectangles_intersect_source_extents (cairo_composite_rectangles_t *extents,
+                                                     const cairo_box_t *box);
+
+cairo_private cairo_int_status_t
+_cairo_composite_rectangles_intersect_mask_extents (cairo_composite_rectangles_t *extents,
+                                                   const cairo_box_t *box);
+
+cairo_private cairo_bool_t
+_cairo_composite_rectangles_can_reduce_clip (cairo_composite_rectangles_t *composite,
+                                            cairo_clip_t *clip);
+
+cairo_private cairo_int_status_t
+_cairo_composite_rectangles_add_to_damage (cairo_composite_rectangles_t *composite,
+                                          cairo_boxes_t *damage);
+
+cairo_private void
+_cairo_composite_rectangles_fini (cairo_composite_rectangles_t *extents);
+
+CAIRO_END_DECLS
+
+#endif /* CAIRO_COMPOSITE_RECTANGLES_PRIVATE_H */
diff --git a/src/cairo-composite-rectangles.c b/src/cairo-composite-rectangles.c
new file mode 100644 (file)
index 0000000..7fc0f5f
--- /dev/null
@@ -0,0 +1,456 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ *      Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-error-private.h"
+#include "cairo-composite-rectangles-private.h"
+#include "cairo-pattern-private.h"
+
+/* A collection of routines to facilitate writing compositors. */
+
+void _cairo_composite_rectangles_fini (cairo_composite_rectangles_t *extents)
+{
+    _cairo_clip_destroy (extents->clip);
+}
+
+static void
+_cairo_composite_reduce_pattern (const cairo_pattern_t *src,
+                                cairo_pattern_union_t *dst)
+{
+    int tx, ty;
+
+    _cairo_pattern_init_static_copy (&dst->base, src);
+    if (dst->base.type == CAIRO_PATTERN_TYPE_SOLID)
+       return;
+
+    dst->base.filter = _cairo_pattern_analyze_filter (&dst->base, NULL),
+
+    tx = ty = 0;
+    if (_cairo_matrix_is_pixman_translation (&dst->base.matrix,
+                                            dst->base.filter,
+                                            &tx, &ty))
+    {
+       dst->base.matrix.x0 = tx;
+       dst->base.matrix.y0 = ty;
+    }
+}
+
+static inline cairo_bool_t
+_cairo_composite_rectangles_init (cairo_composite_rectangles_t *extents,
+                                 cairo_surface_t *surface,
+                                 cairo_operator_t op,
+                                 const cairo_pattern_t *source,
+                                 const cairo_clip_t *clip)
+{
+    if (_cairo_clip_is_all_clipped (clip))
+       return FALSE;
+
+    extents->surface = surface;
+    extents->op = op;
+
+    _cairo_surface_get_extents (surface, &extents->destination);
+    extents->clip = NULL;
+
+    extents->unbounded = extents->destination;
+    if (clip && ! _cairo_rectangle_intersect (&extents->unbounded,
+                                             _cairo_clip_get_extents (clip)))
+       return FALSE;
+
+    extents->bounded = extents->unbounded;
+    extents->is_bounded = _cairo_operator_bounded_by_either (op);
+
+    extents->original_source_pattern = source;
+    _cairo_composite_reduce_pattern (source, &extents->source_pattern);
+
+    _cairo_pattern_get_extents (&extents->source_pattern.base,
+                               &extents->source);
+    if (extents->is_bounded & CAIRO_OPERATOR_BOUND_BY_SOURCE) {
+       if (! _cairo_rectangle_intersect (&extents->bounded, &extents->source))
+           return FALSE;
+    }
+
+    extents->original_mask_pattern = NULL;
+    extents->mask_pattern.base.type = CAIRO_PATTERN_TYPE_SOLID;
+    extents->mask_pattern.solid.color.alpha = 1.; /* XXX full initialisation? */
+
+    return TRUE;
+}
+
+cairo_int_status_t
+_cairo_composite_rectangles_init_for_paint (cairo_composite_rectangles_t *extents,
+                                           cairo_surface_t *surface,
+                                           cairo_operator_t             op,
+                                           const cairo_pattern_t       *source,
+                                           const cairo_clip_t          *clip)
+{
+    if (! _cairo_composite_rectangles_init (extents,
+                                           surface, op, source, clip))
+    {
+       return CAIRO_INT_STATUS_NOTHING_TO_DO;
+    }
+
+    extents->mask = extents->destination;
+
+    extents->clip = _cairo_clip_reduce_for_composite (clip, extents);
+    if (_cairo_clip_is_all_clipped (extents->clip))
+       return CAIRO_INT_STATUS_NOTHING_TO_DO;
+
+    if (extents->source_pattern.base.type != CAIRO_PATTERN_TYPE_SOLID)
+       _cairo_pattern_sampled_area (&extents->source_pattern.base,
+                                    &extents->bounded,
+                                    &extents->source_sample_area);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_composite_rectangles_intersect (cairo_composite_rectangles_t *extents,
+                                      const cairo_clip_t *clip)
+{
+    cairo_bool_t ret;
+
+    ret = _cairo_rectangle_intersect (&extents->bounded, &extents->mask);
+    if (! ret && extents->is_bounded & CAIRO_OPERATOR_BOUND_BY_MASK)
+       return CAIRO_INT_STATUS_NOTHING_TO_DO;
+
+    if (extents->is_bounded == (CAIRO_OPERATOR_BOUND_BY_MASK | CAIRO_OPERATOR_BOUND_BY_SOURCE))
+       extents->unbounded = extents->bounded;
+
+    extents->clip = _cairo_clip_reduce_for_composite (clip, extents);
+    if (_cairo_clip_is_all_clipped (extents->clip))
+       return CAIRO_INT_STATUS_NOTHING_TO_DO;
+
+    if (extents->source_pattern.base.type != CAIRO_PATTERN_TYPE_SOLID)
+       _cairo_pattern_sampled_area (&extents->source_pattern.base,
+                                    &extents->bounded,
+                                    &extents->source_sample_area);
+    if (extents->mask_pattern.base.type != CAIRO_PATTERN_TYPE_SOLID) {
+       _cairo_pattern_sampled_area (&extents->mask_pattern.base,
+                                    &extents->bounded,
+                                    &extents->mask_sample_area);
+       if (extents->mask_sample_area.width == 0 ||
+           extents->mask_sample_area.height == 0) {
+           _cairo_composite_rectangles_fini (extents);
+           return CAIRO_INT_STATUS_NOTHING_TO_DO;
+       }
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_int_status_t
+_cairo_composite_rectangles_intersect_source_extents (cairo_composite_rectangles_t *extents,
+                                                     const cairo_box_t *box)
+{
+    cairo_rectangle_int_t rect;
+    cairo_clip_t *clip;
+
+    _cairo_box_round_to_rectangle (box, &rect);
+    if (rect.x == extents->source.x &&
+       rect.y == extents->source.y &&
+       rect.width  == extents->source.width &&
+       rect.height == extents->source.height)
+    {
+       return CAIRO_INT_STATUS_SUCCESS;
+    }
+
+    _cairo_rectangle_intersect (&extents->source, &rect);
+
+    rect = extents->bounded;
+    if (! _cairo_rectangle_intersect (&extents->bounded, &extents->source) &&
+       extents->is_bounded & CAIRO_OPERATOR_BOUND_BY_SOURCE)
+       return CAIRO_INT_STATUS_NOTHING_TO_DO;
+
+    if (rect.width  == extents->bounded.width &&
+       rect.height == extents->bounded.height)
+       return CAIRO_INT_STATUS_SUCCESS;
+
+    if (extents->is_bounded == (CAIRO_OPERATOR_BOUND_BY_MASK | CAIRO_OPERATOR_BOUND_BY_SOURCE))
+       extents->unbounded = extents->bounded;
+
+    clip = extents->clip;
+    extents->clip = _cairo_clip_reduce_for_composite (clip, extents);
+    if (clip != extents->clip)
+       _cairo_clip_destroy (clip);
+
+    if (_cairo_clip_is_all_clipped (extents->clip))
+       return CAIRO_INT_STATUS_NOTHING_TO_DO;
+
+    if (extents->source_pattern.base.type != CAIRO_PATTERN_TYPE_SOLID)
+       _cairo_pattern_sampled_area (&extents->source_pattern.base,
+                                    &extents->bounded,
+                                    &extents->source_sample_area);
+    if (extents->mask_pattern.base.type != CAIRO_PATTERN_TYPE_SOLID) {
+       _cairo_pattern_sampled_area (&extents->mask_pattern.base,
+                                    &extents->bounded,
+                                    &extents->mask_sample_area);
+       if (extents->mask_sample_area.width == 0 ||
+           extents->mask_sample_area.height == 0)
+           return CAIRO_INT_STATUS_NOTHING_TO_DO;
+    }
+
+    return CAIRO_INT_STATUS_SUCCESS;
+}
+
+cairo_int_status_t
+_cairo_composite_rectangles_intersect_mask_extents (cairo_composite_rectangles_t *extents,
+                                                   const cairo_box_t *box)
+{
+    cairo_rectangle_int_t mask;
+    cairo_clip_t *clip;
+
+    _cairo_box_round_to_rectangle (box, &mask);
+    if (mask.x == extents->mask.x &&
+       mask.y == extents->mask.y &&
+       mask.width  == extents->mask.width &&
+       mask.height == extents->mask.height)
+    {
+       return CAIRO_INT_STATUS_SUCCESS;
+    }
+
+    _cairo_rectangle_intersect (&extents->mask, &mask);
+
+    mask = extents->bounded;
+    if (! _cairo_rectangle_intersect (&extents->bounded, &extents->mask) &&
+       extents->is_bounded & CAIRO_OPERATOR_BOUND_BY_MASK)
+       return CAIRO_INT_STATUS_NOTHING_TO_DO;
+
+    if (mask.width  == extents->bounded.width &&
+       mask.height == extents->bounded.height)
+       return CAIRO_INT_STATUS_SUCCESS;
+
+    if (extents->is_bounded == (CAIRO_OPERATOR_BOUND_BY_MASK | CAIRO_OPERATOR_BOUND_BY_SOURCE))
+       extents->unbounded = extents->bounded;
+
+    clip = extents->clip;
+    extents->clip = _cairo_clip_reduce_for_composite (clip, extents);
+    if (clip != extents->clip)
+       _cairo_clip_destroy (clip);
+
+    if (_cairo_clip_is_all_clipped (extents->clip))
+       return CAIRO_INT_STATUS_NOTHING_TO_DO;
+
+    if (extents->source_pattern.base.type != CAIRO_PATTERN_TYPE_SOLID)
+       _cairo_pattern_sampled_area (&extents->source_pattern.base,
+                                    &extents->bounded,
+                                    &extents->source_sample_area);
+    if (extents->mask_pattern.base.type != CAIRO_PATTERN_TYPE_SOLID) {
+       _cairo_pattern_sampled_area (&extents->mask_pattern.base,
+                                    &extents->bounded,
+                                    &extents->mask_sample_area);
+       if (extents->mask_sample_area.width == 0 ||
+           extents->mask_sample_area.height == 0)
+           return CAIRO_INT_STATUS_NOTHING_TO_DO;
+    }
+
+    return CAIRO_INT_STATUS_SUCCESS;
+}
+
+cairo_int_status_t
+_cairo_composite_rectangles_init_for_mask (cairo_composite_rectangles_t *extents,
+                                          cairo_surface_t*surface,
+                                          cairo_operator_t              op,
+                                          const cairo_pattern_t        *source,
+                                          const cairo_pattern_t        *mask,
+                                          const cairo_clip_t           *clip)
+{
+    if (! _cairo_composite_rectangles_init (extents,
+                                           surface, op, source, clip))
+    {
+       return CAIRO_INT_STATUS_NOTHING_TO_DO;
+    }
+
+
+    extents->original_mask_pattern = mask;
+    _cairo_composite_reduce_pattern (mask, &extents->mask_pattern);
+    _cairo_pattern_get_extents (&extents->mask_pattern.base, &extents->mask);
+
+    return _cairo_composite_rectangles_intersect (extents, clip);
+}
+
+cairo_int_status_t
+_cairo_composite_rectangles_init_for_stroke (cairo_composite_rectangles_t *extents,
+                                            cairo_surface_t *surface,
+                                            cairo_operator_t            op,
+                                            const cairo_pattern_t      *source,
+                                            const cairo_path_fixed_t           *path,
+                                            const cairo_stroke_style_t *style,
+                                            const cairo_matrix_t       *ctm,
+                                            const cairo_clip_t         *clip)
+{
+    if (! _cairo_composite_rectangles_init (extents,
+                                           surface, op, source, clip))
+    {
+       return CAIRO_INT_STATUS_NOTHING_TO_DO;
+    }
+
+    _cairo_path_fixed_approximate_stroke_extents (path, style, ctm, &extents->mask);
+
+    return _cairo_composite_rectangles_intersect (extents, clip);
+}
+
+cairo_int_status_t
+_cairo_composite_rectangles_init_for_fill (cairo_composite_rectangles_t *extents,
+                                          cairo_surface_t *surface,
+                                          cairo_operator_t              op,
+                                          const cairo_pattern_t        *source,
+                                          const cairo_path_fixed_t             *path,
+                                          const cairo_clip_t           *clip)
+{
+    if (! _cairo_composite_rectangles_init (extents,
+                                           surface, op, source, clip))
+    {
+       return CAIRO_INT_STATUS_NOTHING_TO_DO;
+    }
+
+    _cairo_path_fixed_approximate_fill_extents (path, &extents->mask);
+
+    return _cairo_composite_rectangles_intersect (extents, clip);
+}
+
+cairo_int_status_t
+_cairo_composite_rectangles_init_for_polygon (cairo_composite_rectangles_t *extents,
+                                             cairo_surface_t           *surface,
+                                             cairo_operator_t           op,
+                                             const cairo_pattern_t     *source,
+                                             const cairo_polygon_t     *polygon,
+                                             const cairo_clip_t                *clip)
+{
+    if (! _cairo_composite_rectangles_init (extents,
+                                           surface, op, source, clip))
+    {
+       return CAIRO_INT_STATUS_NOTHING_TO_DO;
+    }
+
+    _cairo_box_round_to_rectangle (&polygon->extents, &extents->mask);
+    return _cairo_composite_rectangles_intersect (extents, clip);
+}
+
+cairo_int_status_t
+_cairo_composite_rectangles_init_for_boxes (cairo_composite_rectangles_t *extents,
+                                             cairo_surface_t           *surface,
+                                             cairo_operator_t           op,
+                                             const cairo_pattern_t     *source,
+                                             const cairo_boxes_t       *boxes,
+                                             const cairo_clip_t                *clip)
+{
+    cairo_box_t box;
+
+    if (! _cairo_composite_rectangles_init (extents,
+                                           surface, op, source, clip))
+    {
+       return CAIRO_INT_STATUS_NOTHING_TO_DO;
+    }
+
+    _cairo_boxes_extents (boxes, &box);
+    _cairo_box_round_to_rectangle (&box, &extents->mask);
+    return _cairo_composite_rectangles_intersect (extents, clip);
+}
+
+cairo_int_status_t
+_cairo_composite_rectangles_init_for_glyphs (cairo_composite_rectangles_t *extents,
+                                            cairo_surface_t *surface,
+                                            cairo_operator_t            op,
+                                            const cairo_pattern_t      *source,
+                                            cairo_scaled_font_t        *scaled_font,
+                                            cairo_glyph_t              *glyphs,
+                                            int                         num_glyphs,
+                                            const cairo_clip_t         *clip,
+                                            cairo_bool_t               *overlap)
+{
+    cairo_status_t status;
+
+    if (! _cairo_composite_rectangles_init (extents, surface, op, source, clip))
+       return CAIRO_INT_STATUS_NOTHING_TO_DO;
+
+    /* Computing the exact bbox and the overlap is expensive.
+     * First perform a cheap test to see if the glyphs are all clipped out.
+     */
+    if (extents->is_bounded & CAIRO_OPERATOR_BOUND_BY_MASK) {
+       _cairo_scaled_font_glyph_approximate_extents (scaled_font,
+                                                     glyphs, num_glyphs,
+                                                     &extents->mask);
+       if (! _cairo_rectangle_intersect (&extents->bounded, &extents->mask))
+           return CAIRO_INT_STATUS_NOTHING_TO_DO;
+    }
+
+    status = _cairo_scaled_font_glyph_device_extents (scaled_font,
+                                                     glyphs, num_glyphs,
+                                                     &extents->mask,
+                                                     overlap);
+    if (unlikely (status))
+       return status;
+
+    return _cairo_composite_rectangles_intersect (extents, clip);
+}
+
+cairo_bool_t
+_cairo_composite_rectangles_can_reduce_clip (cairo_composite_rectangles_t *composite,
+                                            cairo_clip_t *clip)
+{
+    cairo_rectangle_int_t extents;
+    cairo_box_t box;
+
+    if (clip == NULL)
+       return TRUE;
+
+    extents = composite->destination;
+    if (composite->is_bounded & CAIRO_OPERATOR_BOUND_BY_SOURCE)
+       _cairo_rectangle_intersect (&extents, &composite->source);
+    if (composite->is_bounded & CAIRO_OPERATOR_BOUND_BY_MASK)
+       _cairo_rectangle_intersect (&extents, &composite->mask);
+
+    _cairo_box_from_rectangle (&box, &extents);
+    return _cairo_clip_contains_box (clip, &box);
+}
+
+cairo_int_status_t
+_cairo_composite_rectangles_add_to_damage (cairo_composite_rectangles_t *composite,
+                                          cairo_boxes_t *damage)
+{
+    cairo_int_status_t status;
+    int n;
+
+    for (n = 0; n < composite->clip->num_boxes; n++) {
+       status = _cairo_boxes_add (damage,
+                         CAIRO_ANTIALIAS_NONE,
+                         &composite->clip->boxes[n]);
+       if (unlikely (status))
+           return status;
+    }
+
+    return CAIRO_INT_STATUS_SUCCESS;
+}
diff --git a/src/cairo-compositor-private.h b/src/cairo-compositor-private.h
new file mode 100644 (file)
index 0000000..72507b2
--- /dev/null
@@ -0,0 +1,355 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2011 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#ifndef CAIRO_COMPOSITOR_PRIVATE_H
+#define CAIRO_COMPOSITOR_PRIVATE_H
+
+#include "cairo-composite-rectangles-private.h"
+
+CAIRO_BEGIN_DECLS
+
+typedef struct {
+    cairo_scaled_font_t *font;
+    cairo_glyph_t *glyphs;
+    int num_glyphs;
+    cairo_bool_t use_mask;
+    cairo_rectangle_int_t extents;
+} cairo_composite_glyphs_info_t;
+
+struct cairo_compositor {
+    const cairo_compositor_t *delegate;
+
+    cairo_warn cairo_int_status_t
+    (*paint)                   (const cairo_compositor_t       *compositor,
+                                cairo_composite_rectangles_t   *extents);
+
+    cairo_warn cairo_int_status_t
+    (*mask)                    (const cairo_compositor_t       *compositor,
+                                cairo_composite_rectangles_t   *extents);
+
+    cairo_warn cairo_int_status_t
+    (*stroke)                  (const cairo_compositor_t       *compositor,
+                                cairo_composite_rectangles_t   *extents,
+                                const cairo_path_fixed_t       *path,
+                                const cairo_stroke_style_t     *style,
+                                const cairo_matrix_t           *ctm,
+                                const cairo_matrix_t           *ctm_inverse,
+                                double                          tolerance,
+                                cairo_antialias_t               antialias);
+
+    cairo_warn cairo_int_status_t
+    (*fill)                    (const cairo_compositor_t       *compositor,
+                                cairo_composite_rectangles_t   *extents,
+                                const cairo_path_fixed_t       *path,
+                                cairo_fill_rule_t               fill_rule,
+                                double                          tolerance,
+                                cairo_antialias_t               antialias);
+
+    cairo_warn cairo_int_status_t
+    (*glyphs)                  (const cairo_compositor_t        *compositor,
+                                cairo_composite_rectangles_t   *extents,
+                                cairo_scaled_font_t            *scaled_font,
+                                cairo_glyph_t                  *glyphs,
+                                int                             num_glyphs,
+                                cairo_bool_t                    overlap);
+};
+
+struct cairo_mask_compositor {
+    cairo_compositor_t base;
+
+    cairo_int_status_t (*acquire) (void *surface);
+    cairo_int_status_t (*release) (void *surface);
+
+    cairo_int_status_t (*set_clip_region) (void                 *surface,
+                                          cairo_region_t       *clip_region);
+
+    cairo_surface_t * (*pattern_to_surface) (cairo_surface_t *dst,
+                                            const cairo_pattern_t *pattern,
+                                            cairo_bool_t is_mask,
+                                            const cairo_rectangle_int_t *extents,
+                                            const cairo_rectangle_int_t *sample,
+                                            int *src_x, int *src_y);
+
+    cairo_int_status_t (*draw_image_boxes) (void *surface,
+                                           cairo_image_surface_t *image,
+                                           cairo_boxes_t *boxes,
+                                           int dx, int dy);
+
+    cairo_int_status_t (*copy_boxes) (void *surface,
+                                     cairo_surface_t *src,
+                                     cairo_boxes_t *boxes,
+                                     const cairo_rectangle_int_t *extents,
+                                     int dx, int dy);
+
+    cairo_int_status_t
+       (*fill_rectangles)      (void                    *surface,
+                                cairo_operator_t         op,
+                                const cairo_color_t     *color,
+                                cairo_rectangle_int_t   *rectangles,
+                                int                      num_rects);
+
+    cairo_int_status_t
+       (*fill_boxes)           (void                   *surface,
+                                cairo_operator_t        op,
+                                const cairo_color_t    *color,
+                                cairo_boxes_t          *boxes);
+
+    cairo_int_status_t
+       (*composite)            (void                   *dst,
+                                cairo_operator_t        op,
+                                cairo_surface_t        *src,
+                                cairo_surface_t        *mask,
+                                int                     src_x,
+                                int                     src_y,
+                                int                     mask_x,
+                                int                     mask_y,
+                                int                     dst_x,
+                                int                     dst_y,
+                                unsigned int            width,
+                                unsigned int            height);
+
+    cairo_int_status_t
+       (*composite_boxes)      (void                   *surface,
+                                cairo_operator_t        op,
+                                cairo_surface_t        *source,
+                                cairo_surface_t        *mask,
+                                int                     src_x,
+                                int                     src_y,
+                                int                     mask_x,
+                                int                     mask_y,
+                                int                     dst_x,
+                                int                     dst_y,
+                                cairo_boxes_t          *boxes,
+                                const cairo_rectangle_int_t  *extents);
+
+    cairo_int_status_t
+       (*check_composite_glyphs) (const cairo_composite_rectangles_t *extents,
+                                  cairo_scaled_font_t *scaled_font,
+                                  cairo_glyph_t *glyphs,
+                                  int *num_glyphs);
+    cairo_int_status_t
+       (*composite_glyphs)     (void                           *surface,
+                                cairo_operator_t                op,
+                                cairo_surface_t                *src,
+                                int                             src_x,
+                                int                             src_y,
+                                int                             dst_x,
+                                int                             dst_y,
+                                cairo_composite_glyphs_info_t  *info);
+};
+
+struct cairo_traps_compositor {
+    cairo_compositor_t base;
+
+    cairo_int_status_t
+       (*acquire) (void *surface);
+
+    cairo_int_status_t
+       (*release) (void *surface);
+
+    cairo_int_status_t
+       (*set_clip_region) (void                 *surface,
+                           cairo_region_t      *clip_region);
+
+    cairo_surface_t *
+       (*pattern_to_surface) (cairo_surface_t *dst,
+                              const cairo_pattern_t *pattern,
+                              cairo_bool_t is_mask,
+                              const cairo_rectangle_int_t *extents,
+                              const cairo_rectangle_int_t *sample,
+                              int *src_x, int *src_y);
+
+    cairo_int_status_t (*draw_image_boxes) (void *surface,
+                                           cairo_image_surface_t *image,
+                                           cairo_boxes_t *boxes,
+                                           int dx, int dy);
+
+    cairo_int_status_t (*copy_boxes) (void *surface,
+                                     cairo_surface_t *src,
+                                     cairo_boxes_t *boxes,
+                                     const cairo_rectangle_int_t *extents,
+                                     int dx, int dy);
+
+    cairo_int_status_t
+       (*fill_boxes)           (void                   *surface,
+                                cairo_operator_t        op,
+                                const cairo_color_t    *color,
+                                cairo_boxes_t          *boxes);
+
+    cairo_int_status_t
+       (*composite)            (void                   *dst,
+                                cairo_operator_t        op,
+                                cairo_surface_t        *src,
+                                cairo_surface_t        *mask,
+                                int                     src_x,
+                                int                     src_y,
+                                int                     mask_x,
+                                int                     mask_y,
+                                int                     dst_x,
+                                int                     dst_y,
+                                unsigned int            width,
+                                unsigned int            height);
+    cairo_int_status_t
+           (*lerp)             (void                   *_dst,
+                                cairo_surface_t        *abstract_src,
+                                cairo_surface_t        *abstract_mask,
+                                int                    src_x,
+                                int                    src_y,
+                                int                    mask_x,
+                                int                    mask_y,
+                                int                    dst_x,
+                                int                    dst_y,
+                                unsigned int           width,
+                                unsigned int           height);
+
+    cairo_int_status_t
+       (*composite_boxes)      (void                   *surface,
+                                cairo_operator_t        op,
+                                cairo_surface_t        *source,
+                                cairo_surface_t        *mask,
+                                int                     src_x,
+                                int                     src_y,
+                                int                     mask_x,
+                                int                     mask_y,
+                                int                     dst_x,
+                                int                     dst_y,
+                                cairo_boxes_t          *boxes,
+                                const cairo_rectangle_int_t  *extents);
+
+    cairo_int_status_t
+       (*composite_traps)      (void                   *dst,
+                                cairo_operator_t        op,
+                                cairo_surface_t        *source,
+                                int                     src_x,
+                                int                     src_y,
+                                int                     dst_x,
+                                int                     dst_y,
+                                const cairo_rectangle_int_t *extents,
+                                cairo_antialias_t       antialias,
+                                cairo_traps_t          *traps);
+
+    cairo_int_status_t
+       (*composite_tristrip)   (void                   *dst,
+                                cairo_operator_t        op,
+                                cairo_surface_t        *source,
+                                int                     src_x,
+                                int                     src_y,
+                                int                     dst_x,
+                                int                     dst_y,
+                                const cairo_rectangle_int_t *extents,
+                                cairo_antialias_t       antialias,
+                                cairo_tristrip_t       *tristrip);
+
+    cairo_int_status_t
+       (*check_composite_glyphs) (const cairo_composite_rectangles_t *extents,
+                                  cairo_scaled_font_t *scaled_font,
+                                  cairo_glyph_t *glyphs,
+                                  int *num_glyphs);
+    cairo_int_status_t
+       (*composite_glyphs)     (void                           *surface,
+                                cairo_operator_t                op,
+                                cairo_surface_t                *src,
+                                int                             src_x,
+                                int                             src_y,
+                                int                             dst_x,
+                                int                             dst_y,
+                                cairo_composite_glyphs_info_t  *info);
+};
+
+cairo_private extern const cairo_compositor_t __cairo_no_compositor;
+cairo_private extern const cairo_compositor_t _cairo_fallback_compositor;
+
+cairo_private void
+_cairo_mask_compositor_init (cairo_mask_compositor_t *compositor,
+                            const cairo_compositor_t *delegate);
+
+cairo_private void
+_cairo_traps_compositor_init (cairo_traps_compositor_t *compositor,
+                             const cairo_compositor_t *delegate);
+
+cairo_private cairo_int_status_t
+_cairo_compositor_paint (const cairo_compositor_t      *compositor,
+                        cairo_surface_t                *surface,
+                        cairo_operator_t                op,
+                        const cairo_pattern_t          *source,
+                        const cairo_clip_t             *clip);
+
+cairo_private cairo_int_status_t
+_cairo_compositor_mask (const cairo_compositor_t       *compositor,
+                       cairo_surface_t                 *surface,
+                       cairo_operator_t                 op,
+                       const cairo_pattern_t           *source,
+                       const cairo_pattern_t           *mask,
+                       const cairo_clip_t              *clip);
+
+cairo_private cairo_int_status_t
+_cairo_compositor_stroke (const cairo_compositor_t     *compositor,
+                         cairo_surface_t               *surface,
+                         cairo_operator_t               op,
+                         const cairo_pattern_t         *source,
+                         const cairo_path_fixed_t      *path,
+                         const cairo_stroke_style_t    *style,
+                         const cairo_matrix_t          *ctm,
+                         const cairo_matrix_t          *ctm_inverse,
+                         double                         tolerance,
+                         cairo_antialias_t              antialias,
+                         const cairo_clip_t            *clip);
+
+cairo_private cairo_int_status_t
+_cairo_compositor_fill (const cairo_compositor_t       *compositor,
+                       cairo_surface_t                 *surface,
+                       cairo_operator_t                 op,
+                       const cairo_pattern_t           *source,
+                       const cairo_path_fixed_t        *path,
+                       cairo_fill_rule_t                fill_rule,
+                       double                           tolerance,
+                       cairo_antialias_t                antialias,
+                       const cairo_clip_t              *clip);
+
+cairo_private cairo_int_status_t
+_cairo_compositor_glyphs (const cairo_compositor_t             *compositor,
+                         cairo_surface_t                       *surface,
+                         cairo_operator_t                       op,
+                         const cairo_pattern_t                 *source,
+                         cairo_glyph_t                         *glyphs,
+                         int                                    num_glyphs,
+                         cairo_scaled_font_t                   *scaled_font,
+                         const cairo_clip_t                    *clip);
+
+CAIRO_END_DECLS
+
+#endif /* CAIRO_COMPOSITOR_PRIVATE_H */
diff --git a/src/cairo-compositor.c b/src/cairo-compositor.c
new file mode 100644 (file)
index 0000000..cf943e7
--- /dev/null
@@ -0,0 +1,213 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2011 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-compositor-private.h"
+#include "cairo-error-private.h"
+
+cairo_int_status_t
+_cairo_compositor_paint (const cairo_compositor_t      *compositor,
+                        cairo_surface_t                *surface,
+                        cairo_operator_t                op,
+                        const cairo_pattern_t          *source,
+                        const cairo_clip_t             *clip)
+{
+    cairo_composite_rectangles_t extents;
+    cairo_int_status_t status;
+
+    status = _cairo_composite_rectangles_init_for_paint (&extents, surface,
+                                                        op, source,
+                                                        clip);
+    if (unlikely (status))
+       return status;
+
+    do {
+       while (compositor->paint == NULL)
+           compositor = compositor->delegate;
+
+       status = compositor->paint (compositor, &extents);
+
+       compositor = compositor->delegate;
+    } while (status == CAIRO_INT_STATUS_UNSUPPORTED);
+
+    _cairo_composite_rectangles_fini (&extents);
+
+    return status;
+}
+
+cairo_int_status_t
+_cairo_compositor_mask (const cairo_compositor_t       *compositor,
+                       cairo_surface_t                 *surface,
+                       cairo_operator_t                 op,
+                       const cairo_pattern_t           *source,
+                       const cairo_pattern_t           *mask,
+                       const cairo_clip_t              *clip)
+{
+    cairo_composite_rectangles_t extents;
+    cairo_int_status_t status;
+
+    status = _cairo_composite_rectangles_init_for_mask (&extents, surface,
+                                                       op, source, mask,
+                                                       clip);
+    if (unlikely (status))
+       return status;
+
+    do {
+       while (compositor->mask == NULL)
+           compositor = compositor->delegate;
+
+       status = compositor->mask (compositor, &extents);
+
+       compositor = compositor->delegate;
+    } while (status == CAIRO_INT_STATUS_UNSUPPORTED);
+
+    _cairo_composite_rectangles_fini (&extents);
+
+    return status;
+}
+
+cairo_int_status_t
+_cairo_compositor_stroke (const cairo_compositor_t     *compositor,
+                         cairo_surface_t               *surface,
+                         cairo_operator_t               op,
+                         const cairo_pattern_t         *source,
+                         const cairo_path_fixed_t      *path,
+                         const cairo_stroke_style_t    *style,
+                         const cairo_matrix_t          *ctm,
+                         const cairo_matrix_t          *ctm_inverse,
+                         double                         tolerance,
+                         cairo_antialias_t              antialias,
+                         const cairo_clip_t            *clip)
+{
+    cairo_composite_rectangles_t extents;
+    cairo_int_status_t status;
+
+    status = _cairo_composite_rectangles_init_for_stroke (&extents, surface,
+                                                         op, source,
+                                                         path, style, ctm,
+                                                         clip);
+    if (unlikely (status))
+       return status;
+
+    do {
+       while (compositor->stroke == NULL)
+           compositor = compositor->delegate;
+
+       status = compositor->stroke (compositor, &extents,
+                                    path, style, ctm, ctm_inverse,
+                                    tolerance, antialias);
+
+       compositor = compositor->delegate;
+    } while (status == CAIRO_INT_STATUS_UNSUPPORTED);
+
+    _cairo_composite_rectangles_fini (&extents);
+
+    return status;
+}
+
+cairo_int_status_t
+_cairo_compositor_fill (const cairo_compositor_t       *compositor,
+                       cairo_surface_t                 *surface,
+                       cairo_operator_t                 op,
+                       const cairo_pattern_t           *source,
+                       const cairo_path_fixed_t        *path,
+                       cairo_fill_rule_t                fill_rule,
+                       double                           tolerance,
+                       cairo_antialias_t                antialias,
+                       const cairo_clip_t              *clip)
+{
+    cairo_composite_rectangles_t extents;
+    cairo_int_status_t status;
+
+    status = _cairo_composite_rectangles_init_for_fill (&extents, surface,
+                                                       op, source, path,
+                                                       clip);
+    if (unlikely (status))
+       return status;
+
+    do {
+       while (compositor->fill == NULL)
+           compositor = compositor->delegate;
+
+       status = compositor->fill (compositor, &extents,
+                                  path, fill_rule, tolerance, antialias);
+
+       compositor = compositor->delegate;
+    } while (status == CAIRO_INT_STATUS_UNSUPPORTED);
+
+    _cairo_composite_rectangles_fini (&extents);
+
+    return status;
+}
+
+cairo_int_status_t
+_cairo_compositor_glyphs (const cairo_compositor_t             *compositor,
+                         cairo_surface_t                       *surface,
+                         cairo_operator_t                       op,
+                         const cairo_pattern_t                 *source,
+                         cairo_glyph_t                         *glyphs,
+                         int                                    num_glyphs,
+                         cairo_scaled_font_t                   *scaled_font,
+                         const cairo_clip_t                    *clip)
+{
+    cairo_composite_rectangles_t extents;
+    cairo_bool_t overlap;
+    cairo_int_status_t status;
+
+    status = _cairo_composite_rectangles_init_for_glyphs (&extents, surface,
+                                                         op, source,
+                                                         scaled_font,
+                                                         glyphs, num_glyphs,
+                                                         clip, &overlap);
+    if (unlikely (status))
+       return status;
+
+    do {
+       while (compositor->glyphs == NULL)
+           compositor = compositor->delegate;
+
+       status = compositor->glyphs (compositor, &extents,
+                                    scaled_font, glyphs, num_glyphs, overlap);
+
+       compositor = compositor->delegate;
+    } while (status == CAIRO_INT_STATUS_UNSUPPORTED);
+
+    _cairo_composite_rectangles_fini (&extents);
+
+    return status;
+}
diff --git a/src/cairo-contour-private.h b/src/cairo-contour-private.h
new file mode 100644 (file)
index 0000000..2eb1f60
--- /dev/null
@@ -0,0 +1,159 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2011 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Intel Corporation
+ *
+ * Contributor(s):
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#ifndef CAIRO_CONTOUR_PRIVATE_H
+#define CAIRO_CONTOUR_PRIVATE_H
+
+#include "cairo-types-private.h"
+#include "cairo-compiler-private.h"
+#include "cairo-error-private.h"
+#include "cairo-list-private.h"
+
+#include <stdio.h>
+
+CAIRO_BEGIN_DECLS
+
+/* A contour is simply a closed chain of points that divide the infinite plane
+ * into inside and outside. Each contour is a simple polygon, that is it
+ * contains no holes or self-intersections, but maybe either concave or convex.
+ */
+
+struct _cairo_contour_chain {
+    cairo_point_t *points;
+    int num_points, size_points;
+    struct _cairo_contour_chain *next;
+};
+
+struct _cairo_contour_iter {
+    cairo_point_t *point;
+    cairo_contour_chain_t *chain;
+};
+
+struct _cairo_contour {
+    cairo_list_t next;
+    int direction;
+    cairo_contour_chain_t chain, *tail;
+
+    cairo_point_t embedded_points[64];
+};
+
+/* Initial definition of a shape is a set of contours (some representing holes) */
+struct _cairo_shape {
+    cairo_list_t contours;
+};
+
+typedef struct _cairo_shape cairo_shape_t;
+
+#if 0
+cairo_private cairo_status_t
+_cairo_shape_init_from_polygon (cairo_shape_t *shape,
+                               const cairo_polygon_t *polygon);
+
+cairo_private cairo_status_t
+_cairo_shape_reduce (cairo_shape_t *shape, double tolerance);
+#endif
+
+cairo_private void
+_cairo_contour_init (cairo_contour_t *contour,
+                    int direction);
+
+cairo_private cairo_int_status_t
+__cairo_contour_add_point (cairo_contour_t *contour,
+                          const cairo_point_t *point);
+
+static inline cairo_int_status_t
+_cairo_contour_add_point (cairo_contour_t *contour,
+                         const cairo_point_t *point)
+{
+    struct _cairo_contour_chain *tail = contour->tail;
+
+    if (unlikely (tail->num_points == tail->size_points))
+       return __cairo_contour_add_point (contour, point);
+
+    tail->points[tail->num_points++] = *point;
+    return CAIRO_INT_STATUS_SUCCESS;
+}
+
+static inline cairo_point_t *
+_cairo_contour_first_point (cairo_contour_t *c)
+{
+    return &c->chain.points[0];
+}
+
+static inline cairo_point_t *
+_cairo_contour_last_point (cairo_contour_t *c)
+{
+    return &c->tail->points[c->tail->num_points-1];
+}
+
+cairo_private void
+_cairo_contour_simplify (cairo_contour_t *contour, double tolerance);
+
+cairo_private void
+_cairo_contour_reverse (cairo_contour_t *contour);
+
+cairo_private cairo_int_status_t
+_cairo_contour_add (cairo_contour_t *dst,
+                   const cairo_contour_t *src);
+
+cairo_private cairo_int_status_t
+_cairo_contour_add_reversed (cairo_contour_t *dst,
+                            const cairo_contour_t *src);
+
+cairo_private void
+__cairo_contour_remove_last_chain (cairo_contour_t *contour);
+
+static inline void
+_cairo_contour_remove_last_point (cairo_contour_t *contour)
+{
+    if (contour->chain.num_points == 0)
+       return;
+
+    if (--contour->tail->num_points == 0)
+       __cairo_contour_remove_last_chain (contour);
+}
+
+cairo_private void
+_cairo_contour_reset (cairo_contour_t *contour);
+
+cairo_private void
+_cairo_contour_fini (cairo_contour_t *contour);
+
+cairo_private void
+_cairo_debug_print_contour (FILE *file, cairo_contour_t *contour);
+
+CAIRO_END_DECLS
+
+#endif /* CAIRO_CONTOUR_PRIVATE_H */
diff --git a/src/cairo-contour.c b/src/cairo-contour.c
new file mode 100644 (file)
index 0000000..ac3998b
--- /dev/null
@@ -0,0 +1,453 @@
+/*
+ * Copyright © 2004 Carl Worth
+ * Copyright © 2006 Red Hat, Inc.
+ * Copyright © 2008 Chris Wilson
+ * Copyright © 2011 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Carl Worth
+ *
+ * Contributor(s):
+ *     Carl D. Worth <cworth@cworth.org>
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-error-private.h"
+#include "cairo-freelist-private.h"
+#include "cairo-combsort-private.h"
+#include "cairo-contour-private.h"
+
+void
+_cairo_contour_init (cairo_contour_t *contour,
+                    int direction)
+{
+    contour->direction = direction;
+    contour->chain.points = contour->embedded_points;
+    contour->chain.next = NULL;
+    contour->chain.num_points = 0;
+    contour->chain.size_points = ARRAY_LENGTH (contour->embedded_points);
+    contour->tail = &contour->chain;
+}
+
+cairo_int_status_t
+__cairo_contour_add_point (cairo_contour_t *contour,
+                         const cairo_point_t *point)
+{
+    cairo_contour_chain_t *tail = contour->tail;
+    cairo_contour_chain_t *next;
+
+    assert (tail->next == NULL);
+
+    next = _cairo_malloc_ab_plus_c (tail->size_points*2,
+                                   sizeof (cairo_point_t),
+                                   sizeof (cairo_contour_chain_t));
+    if (unlikely (next == NULL))
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    next->size_points = tail->size_points*2;
+    next->num_points = 1;
+    next->points = (cairo_point_t *)(next+1);
+    next->next = NULL;
+    tail->next = next;
+    contour->tail = next;
+
+    next->points[0] = *point;
+    return CAIRO_INT_STATUS_SUCCESS;
+}
+
+static void
+first_inc (cairo_contour_t *contour,
+          cairo_point_t **p,
+          cairo_contour_chain_t **chain)
+{
+    if (*p == (*chain)->points + (*chain)->num_points) {
+       assert ((*chain)->next);
+       *chain = (*chain)->next;
+       *p = &(*chain)->points[0];
+    } else
+       ++*p;
+}
+
+static void
+last_dec (cairo_contour_t *contour,
+         cairo_point_t **p,
+         cairo_contour_chain_t **chain)
+{
+    if (*p == (*chain)->points) {
+       cairo_contour_chain_t *prev;
+       assert (*chain != &contour->chain);
+       for (prev = &contour->chain; prev->next != *chain; prev = prev->next)
+           ;
+       *chain = prev;
+       *p = &(*chain)->points[(*chain)->num_points-1];
+    } else
+       --*p;
+}
+
+void
+_cairo_contour_reverse (cairo_contour_t *contour)
+{
+    cairo_contour_chain_t *first_chain, *last_chain;
+    cairo_point_t *first, *last;
+
+    contour->direction = -contour->direction;
+
+    if (contour->chain.num_points <= 1)
+       return;
+
+    first_chain = &contour->chain;
+    last_chain = contour->tail;
+
+    first = &first_chain->points[0];
+    last = &last_chain->points[last_chain->num_points-1];
+
+    while (first != last) {
+       cairo_point_t p;
+
+       p = *first;
+       *first = *last;
+       *last = p;
+
+       first_inc (contour, &first, &first_chain);
+       last_dec (contour, &last, &last_chain);
+    }
+}
+
+cairo_int_status_t
+_cairo_contour_add (cairo_contour_t *dst,
+                   const cairo_contour_t *src)
+{
+    const cairo_contour_chain_t *chain;
+    cairo_int_status_t status;
+    int i;
+
+    for (chain = &src->chain; chain; chain = chain->next) {
+       for (i = 0; i < chain->num_points; i++) {
+           status = _cairo_contour_add_point (dst, &chain->points[i]);
+           if (unlikely (status))
+               return status;
+       }
+    }
+
+    return CAIRO_INT_STATUS_SUCCESS;
+}
+
+static inline cairo_bool_t
+iter_next (cairo_contour_iter_t *iter)
+{
+    if (iter->point == &iter->chain->points[iter->chain->size_points-1]) {
+       iter->chain = iter->chain->next;
+       if (iter->chain == NULL)
+           return FALSE;
+
+       iter->point = &iter->chain->points[0];
+       return TRUE;
+    } else {
+       iter->point++;
+       return TRUE;
+    }
+}
+
+static cairo_bool_t
+iter_equal (const cairo_contour_iter_t *i1,
+           const cairo_contour_iter_t *i2)
+{
+    return i1->chain == i2->chain && i1->point == i2->point;
+}
+
+static void
+iter_init (cairo_contour_iter_t *iter, cairo_contour_t *contour)
+{
+    iter->chain = &contour->chain;
+    iter->point = &contour->chain.points[0];
+}
+
+static void
+iter_init_last (cairo_contour_iter_t *iter, cairo_contour_t *contour)
+{
+    iter->chain = contour->tail;
+    iter->point = &contour->tail->points[contour->tail->num_points-1];
+}
+
+static const cairo_contour_chain_t *prev_const_chain(const cairo_contour_t *contour,
+                                                    const cairo_contour_chain_t *chain)
+{
+    const cairo_contour_chain_t *prev;
+
+    if (chain == &contour->chain)
+       return NULL;
+
+    for (prev = &contour->chain; prev->next != chain; prev = prev->next)
+       ;
+
+    return prev;
+}
+
+cairo_int_status_t
+_cairo_contour_add_reversed (cairo_contour_t *dst,
+                            const cairo_contour_t *src)
+{
+    const cairo_contour_chain_t *last;
+    cairo_int_status_t status;
+    int i;
+
+    if (src->chain.num_points == 0)
+       return CAIRO_INT_STATUS_SUCCESS;
+
+    for (last = src->tail; last; last = prev_const_chain (src, last)) {
+       for (i = last->num_points-1; i >= 0; i--) {
+           status = _cairo_contour_add_point (dst, &last->points[i]);
+           if (unlikely (status))
+               return status;
+       }
+    }
+
+    return CAIRO_INT_STATUS_SUCCESS;
+}
+
+static cairo_uint64_t
+point_distance_sq (const cairo_point_t *p1,
+                  const cairo_point_t *p2)
+{
+    int32_t dx = p1->x - p2->x;
+    int32_t dy = p1->y - p2->y;
+    return _cairo_int32x32_64_mul (dx, dx) + _cairo_int32x32_64_mul (dy, dy);
+}
+
+#define DELETED(p) ((p)->x == INT_MIN && (p)->y == INT_MAX)
+#define MARK_DELETED(p) ((p)->x = INT_MIN, (p)->y = INT_MAX)
+
+static cairo_bool_t
+_cairo_contour_simplify_chain (cairo_contour_t *contour, const double tolerance,
+                              const cairo_contour_iter_t *first,
+                              const cairo_contour_iter_t *last)
+{
+    cairo_contour_iter_t iter, furthest;
+    uint64_t max_error;
+    int x0, y0;
+    int nx, ny;
+    int count;
+
+    iter = *first;
+    iter_next (&iter);
+    if (iter_equal (&iter, last))
+       return FALSE;
+
+    x0 = first->point->x;
+    y0 = first->point->y;
+    nx = last->point->y - y0;
+    ny = x0 - last->point->x;
+
+    count = 0;
+    max_error = 0;
+    do {
+       cairo_point_t *p = iter.point;
+       if (! DELETED(p)) {
+           uint64_t d = (uint64_t)nx * (x0 - p->x) + (uint64_t)ny * (y0 - p->y);
+           if (d * d > max_error) {
+               max_error = d * d;
+               furthest = iter;
+           }
+           count++;
+       }
+       iter_next (&iter);
+    } while (! iter_equal (&iter, last));
+    if (count == 0)
+       return FALSE;
+
+    if (max_error > tolerance * ((uint64_t)nx * nx + (uint64_t)ny * ny)) {
+       cairo_bool_t simplified;
+
+       simplified = FALSE;
+       simplified |= _cairo_contour_simplify_chain (contour, tolerance,
+                                                    first, &furthest);
+       simplified |= _cairo_contour_simplify_chain (contour, tolerance,
+                                                    &furthest, last);
+       return simplified;
+    } else {
+       iter = *first;
+       iter_next (&iter);
+       do {
+           MARK_DELETED (iter.point);
+           iter_next (&iter);
+       } while (! iter_equal (&iter, last));
+
+       return TRUE;
+    }
+}
+
+void
+_cairo_contour_simplify (cairo_contour_t *contour, double tolerance)
+{
+    cairo_contour_chain_t *chain;
+    cairo_point_t *last = NULL;
+    cairo_contour_iter_t iter, furthest;
+    cairo_bool_t simplified;
+    uint64_t max = 0;
+    int i;
+
+    if (contour->chain.num_points <= 2)
+       return;
+
+    tolerance = tolerance * CAIRO_FIXED_ONE;
+    tolerance *= tolerance;
+
+    /* stage 1: vertex reduction */
+    for (chain = &contour->chain; chain; chain = chain->next) {
+       for (i = 0; i < chain->num_points; i++) {
+           if (last == NULL ||
+               point_distance_sq (last, &chain->points[i]) > tolerance) {
+               last = &chain->points[i];
+           } else {
+               MARK_DELETED (&chain->points[i]);
+           }
+       }
+    }
+
+    /* stage2: polygon simplification using Douglas-Peucker */
+    simplified = FALSE;
+    do {
+       last = &contour->chain.points[0];
+       iter_init (&furthest, contour);
+       max = 0;
+       for (chain = &contour->chain; chain; chain = chain->next) {
+           for (i = 0; i < chain->num_points; i++) {
+               uint64_t d;
+
+               if (DELETED (&chain->points[i]))
+                   continue;
+
+               d = point_distance_sq (last, &chain->points[i]);
+               if (d > max) {
+                   furthest.chain = chain;
+                   furthest.point = &chain->points[i];
+                   max = d;
+               }
+           }
+       }
+       assert (max);
+
+       simplified = FALSE;
+       iter_init (&iter, contour);
+       simplified |= _cairo_contour_simplify_chain (contour, tolerance,
+                                                    &iter, &furthest);
+
+       iter_init_last (&iter, contour);
+       if (! iter_equal (&furthest, &iter))
+           simplified |= _cairo_contour_simplify_chain (contour, tolerance,
+                                                        &furthest, &iter);
+    } while (simplified);
+
+    iter_init (&iter, contour);
+    for (chain = &contour->chain; chain; chain = chain->next) {
+       int num_points = chain->num_points;
+       chain->num_points = 0;
+       for (i = 0; i < num_points; i++) {
+           if (! DELETED(&chain->points[i])) {
+               if (iter.point != &chain->points[i])
+                   *iter.point = chain->points[i];
+               iter.chain->num_points++;
+               iter_next (&iter);
+           }
+       }
+    }
+
+    if (iter.chain) {
+       cairo_contour_chain_t *next;
+
+       for (chain = iter.chain->next; chain; chain = next) {
+           next = chain->next;
+           free (chain);
+       }
+
+       iter.chain->next = NULL;
+       contour->tail = iter.chain;
+    }
+}
+
+void
+_cairo_contour_reset (cairo_contour_t *contour)
+{
+    _cairo_contour_fini (contour);
+    _cairo_contour_init (contour, contour->direction);
+}
+
+void
+_cairo_contour_fini (cairo_contour_t *contour)
+{
+    cairo_contour_chain_t *chain, *next;
+
+    for (chain = contour->chain.next; chain; chain = next) {
+       next = chain->next;
+       free (chain);
+    }
+}
+
+void
+_cairo_debug_print_contour (FILE *file, cairo_contour_t *contour)
+{
+    cairo_contour_chain_t *chain;
+    int num_points, size_points;
+    int i;
+
+    num_points = 0;
+    size_points = 0;
+    for (chain = &contour->chain; chain; chain = chain->next) {
+       num_points += chain->num_points;
+       size_points += chain->size_points;
+    }
+
+    fprintf (file, "contour: direction=%d, num_points=%d / %d\n",
+            contour->direction, num_points, size_points);
+
+    num_points = 0;
+    for (chain = &contour->chain; chain; chain = chain->next) {
+       for (i = 0; i < chain->num_points; i++) {
+           fprintf (file, "  [%d] = (%f, %f)\n",
+                    num_points++,
+                    _cairo_fixed_to_double (chain->points[i].x),
+                    _cairo_fixed_to_double (chain->points[i].y));
+       }
+    }
+}
+
+void
+__cairo_contour_remove_last_chain (cairo_contour_t *contour)
+{
+    cairo_contour_chain_t *chain;
+
+    if (contour->tail == &contour->chain)
+       return;
+
+    for (chain = &contour->chain; chain->next != contour->tail; chain = chain->next)
+       ;
+    free (contour->tail);
+    contour->tail = chain;
+    chain->next = NULL;
+}
diff --git a/src/cairo-debug.c b/src/cairo-debug.c
new file mode 100644 (file)
index 0000000..b4cde7d
--- /dev/null
@@ -0,0 +1,298 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ *     Carl D. Worth <cworth@cworth.org>
+ */
+
+#include "cairoint.h"
+#include "cairo-image-surface-private.h"
+
+/**
+ * cairo_debug_reset_static_data:
+ *
+ * Resets all static data within cairo to its original state,
+ * (ie. identical to the state at the time of program invocation). For
+ * example, all caches within cairo will be flushed empty.
+ *
+ * This function is intended to be useful when using memory-checking
+ * tools such as valgrind. When valgrind's memcheck analyzes a
+ * cairo-using program without a call to cairo_debug_reset_static_data(),
+ * it will report all data reachable via cairo's static objects as
+ * "still reachable". Calling cairo_debug_reset_static_data() just prior
+ * to program termination will make it easier to get squeaky clean
+ * reports from valgrind.
+ *
+ * WARNING: It is only safe to call this function when there are no
+ * active cairo objects remaining, (ie. the appropriate destroy
+ * functions have been called as necessary). If there are active cairo
+ * objects, this call is likely to cause a crash, (eg. an assertion
+ * failure due to a hash table being destroyed when non-empty).
+ **/
+void
+cairo_debug_reset_static_data (void)
+{
+    CAIRO_MUTEX_INITIALIZE ();
+
+    _cairo_scaled_font_map_destroy ();
+
+    _cairo_toy_font_face_reset_static_data ();
+
+#if CAIRO_HAS_FT_FONT
+    _cairo_ft_font_reset_static_data ();
+#endif
+
+#if CAIRO_HAS_WIN32_FONT
+    _cairo_win32_font_reset_static_data ();
+#endif
+
+    _cairo_intern_string_reset_static_data ();
+
+    _cairo_scaled_font_reset_static_data ();
+
+    _cairo_pattern_reset_static_data ();
+
+    _cairo_clip_reset_static_data ();
+
+    _cairo_image_reset_static_data ();
+
+#if CAIRO_HAS_DRM_SURFACE
+    _cairo_drm_device_reset_static_data ();
+#endif
+
+    _cairo_default_context_reset_static_data ();
+
+    CAIRO_MUTEX_FINALIZE ();
+}
+
+#if HAVE_VALGRIND
+void
+_cairo_debug_check_image_surface_is_defined (const cairo_surface_t *surface)
+{
+    const cairo_image_surface_t *image = (cairo_image_surface_t *) surface;
+    const uint8_t *bits;
+    int row, width;
+
+    if (surface == NULL)
+       return;
+
+    if (! RUNNING_ON_VALGRIND)
+       return;
+
+    bits = image->data;
+    switch (image->format) {
+    case CAIRO_FORMAT_A1:
+       width = (image->width + 7)/8;
+       break;
+    case CAIRO_FORMAT_A8:
+       width = image->width;
+       break;
+    case CAIRO_FORMAT_RGB16_565:
+       width = image->width*2;
+       break;
+    case CAIRO_FORMAT_RGB24:
+    case CAIRO_FORMAT_RGB30:
+    case CAIRO_FORMAT_ARGB32:
+       width = image->width*4;
+       break;
+    case CAIRO_FORMAT_INVALID:
+    default:
+       /* XXX compute width from pixman bpp */
+       return;
+    }
+
+    for (row = 0; row < image->height; row++) {
+       VALGRIND_CHECK_MEM_IS_DEFINED (bits, width);
+       /* and then silence any future valgrind warnings */
+       VALGRIND_MAKE_MEM_DEFINED (bits, width);
+       bits += image->stride;
+    }
+}
+#endif
+
+
+#if 0
+void
+_cairo_image_surface_write_to_ppm (cairo_image_surface_t *isurf, const char *fn)
+{
+    char *fmt;
+    if (isurf->format == CAIRO_FORMAT_ARGB32 || isurf->format == CAIRO_FORMAT_RGB24)
+        fmt = "P6";
+    else if (isurf->format == CAIRO_FORMAT_A8)
+        fmt = "P5";
+    else
+        return;
+
+    FILE *fp = fopen(fn, "wb");
+    if (!fp)
+        return;
+
+    fprintf (fp, "%s %d %d 255\n", fmt,isurf->width, isurf->height);
+    for (int j = 0; j < isurf->height; j++) {
+        unsigned char *row = isurf->data + isurf->stride * j;
+        for (int i = 0; i < isurf->width; i++) {
+            if (isurf->format == CAIRO_FORMAT_ARGB32 || isurf->format == CAIRO_FORMAT_RGB24) {
+                unsigned char r = *row++;
+                unsigned char g = *row++;
+                unsigned char b = *row++;
+                *row++;
+                putc(r, fp);
+                putc(g, fp);
+                putc(b, fp);
+            } else {
+                unsigned char a = *row++;
+                putc(a, fp);
+            }
+        }
+    }
+
+    fclose (fp);
+
+    fprintf (stderr, "Wrote %s\n", fn);
+}
+#endif
+
+static cairo_status_t
+_print_move_to (void *closure,
+               const cairo_point_t *point)
+{
+    fprintf (closure,
+            " %f %f m",
+            _cairo_fixed_to_double (point->x),
+            _cairo_fixed_to_double (point->y));
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_print_line_to (void *closure,
+               const cairo_point_t *point)
+{
+    fprintf (closure,
+            " %f %f l",
+            _cairo_fixed_to_double (point->x),
+            _cairo_fixed_to_double (point->y));
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_print_curve_to (void *closure,
+                const cairo_point_t *p1,
+                const cairo_point_t *p2,
+                const cairo_point_t *p3)
+{
+    fprintf (closure,
+            " %f %f %f %f %f %f c",
+            _cairo_fixed_to_double (p1->x),
+            _cairo_fixed_to_double (p1->y),
+            _cairo_fixed_to_double (p2->x),
+            _cairo_fixed_to_double (p2->y),
+            _cairo_fixed_to_double (p3->x),
+            _cairo_fixed_to_double (p3->y));
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_print_close (void *closure)
+{
+    fprintf (closure, " h");
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+void
+_cairo_debug_print_path (FILE *stream, cairo_path_fixed_t *path)
+{
+    cairo_status_t status;
+    cairo_box_t box;
+
+    fprintf (stream,
+            "path: extents=(%f, %f), (%f, %f)\n",
+           _cairo_fixed_to_double (path->extents.p1.x),
+           _cairo_fixed_to_double (path->extents.p1.y),
+           _cairo_fixed_to_double (path->extents.p2.x),
+           _cairo_fixed_to_double (path->extents.p2.y));
+
+    status = _cairo_path_fixed_interpret (path,
+                                         _print_move_to,
+                                         _print_line_to,
+                                         _print_curve_to,
+                                         _print_close,
+                                         stream);
+    assert (status == CAIRO_STATUS_SUCCESS);
+
+    if (_cairo_path_fixed_is_box (path, &box)) {
+       fprintf (stream, "[box (%d, %d), (%d, %d)]",
+                box.p1.x, box.p1.y, box.p2.x, box.p2.y);
+    }
+
+    printf ("\n");
+}
+
+void
+_cairo_debug_print_polygon (FILE *stream, cairo_polygon_t *polygon)
+{
+    int n;
+
+    fprintf (stream,
+            "polygon: extents=(%f, %f), (%f, %f)\n",
+           _cairo_fixed_to_double (polygon->extents.p1.x),
+           _cairo_fixed_to_double (polygon->extents.p1.y),
+           _cairo_fixed_to_double (polygon->extents.p2.x),
+           _cairo_fixed_to_double (polygon->extents.p2.y));
+    if (polygon->num_limits) {
+       fprintf (stream,
+                "       : limit=(%f, %f), (%f, %f) x %d\n",
+                _cairo_fixed_to_double (polygon->limit.p1.x),
+                _cairo_fixed_to_double (polygon->limit.p1.y),
+                _cairo_fixed_to_double (polygon->limit.p2.x),
+                _cairo_fixed_to_double (polygon->limit.p2.y),
+                polygon->num_limits);
+    }
+
+    for (n = 0; n < polygon->num_edges; n++) {
+       cairo_edge_t *edge = &polygon->edges[n];
+
+       fprintf (stream,
+                "  [%d] = [(%f, %f), (%f, %f)], top=%f, bottom=%f, dir=%d\n",
+                n,
+                _cairo_fixed_to_double (edge->line.p1.x),
+                _cairo_fixed_to_double (edge->line.p1.y),
+                _cairo_fixed_to_double (edge->line.p2.x),
+                _cairo_fixed_to_double (edge->line.p2.y),
+                _cairo_fixed_to_double (edge->top),
+                _cairo_fixed_to_double (edge->bottom),
+                edge->dir);
+
+    }
+}
diff --git a/src/cairo-default-context-private.h b/src/cairo-default-context-private.h
new file mode 100644 (file)
index 0000000..32fd12d
--- /dev/null
@@ -0,0 +1,58 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ *     Carl D. Worth <cworth@redhat.com>
+ */
+
+#ifndef CAIRO_DEFAULT_CONTEXT_PRIVATE_H
+#define CAIRO_DEFAULT_CONTEXT_PRIVATE_H
+
+#include "cairo-private.h"
+#include "cairo-gstate-private.h"
+#include "cairo-path-fixed-private.h"
+
+typedef struct _cairo_default_context cairo_default_context_t;
+
+struct _cairo_default_context {
+    cairo_t base;
+
+    cairo_gstate_t *gstate;
+    cairo_gstate_t  gstate_tail[2];
+    cairo_gstate_t *gstate_freelist;
+
+    cairo_path_fixed_t path[1];
+};
+
+cairo_private cairo_t *
+_cairo_default_context_create (void *target);
+
+#endif /* CAIRO_DEFAULT_CONTEXT_PRIVATE_H */
diff --git a/src/cairo-default-context.c b/src/cairo-default-context.c
new file mode 100644 (file)
index 0000000..98cc668
--- /dev/null
@@ -0,0 +1,1411 @@
+/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ * Copyright © 2005 Red Hat, Inc.
+ * Copyright © 2011 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *     Carl D. Worth <cworth@cworth.org>
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-private.h"
+#include "cairo-arc-private.h"
+#include "cairo-backend-private.h"
+#include "cairo-default-context-private.h"
+#include "cairo-error-private.h"
+#include "cairo-freed-pool-private.h"
+#include "cairo-path-private.h"
+#include "cairo-pattern-private.h"
+
+#define CAIRO_TOLERANCE_MINIMUM        _cairo_fixed_to_double(1)
+
+#if !defined(INFINITY)
+#define INFINITY HUGE_VAL
+#endif
+
+static freed_pool_t context_pool;
+
+void
+_cairo_default_context_reset_static_data (void)
+{
+    _freed_pool_reset (&context_pool);
+}
+
+static void
+_cairo_default_context_destroy (void *abstract_cr)
+{
+    cairo_default_context_t *cr = abstract_cr;
+
+    while (cr->gstate != &cr->gstate_tail[0]) {
+       if (_cairo_gstate_restore (&cr->gstate, &cr->gstate_freelist))
+           break;
+    }
+
+    _cairo_gstate_fini (cr->gstate);
+    cr->gstate_freelist = cr->gstate_freelist->next; /* skip over tail[1] */
+    while (cr->gstate_freelist != NULL) {
+       cairo_gstate_t *gstate = cr->gstate_freelist;
+       cr->gstate_freelist = gstate->next;
+       free (gstate);
+    }
+
+    _cairo_path_fixed_fini (cr->path);
+
+    _cairo_fini (&cr->base);
+
+    /* mark the context as invalid to protect against misuse */
+    cr->base.status = CAIRO_STATUS_NULL_POINTER;
+    _freed_pool_put (&context_pool, cr);
+}
+
+static cairo_surface_t *
+_cairo_default_context_get_original_target (void *abstract_cr)
+{
+    cairo_default_context_t *cr = abstract_cr;
+
+    return _cairo_gstate_get_original_target (cr->gstate);
+}
+
+static cairo_surface_t *
+_cairo_default_context_get_current_target (void *abstract_cr)
+{
+    cairo_default_context_t *cr = abstract_cr;
+
+    return _cairo_gstate_get_target (cr->gstate);
+}
+
+static cairo_status_t
+_cairo_default_context_save (void *abstract_cr)
+{
+    cairo_default_context_t *cr = abstract_cr;
+
+    return _cairo_gstate_save (&cr->gstate, &cr->gstate_freelist);
+}
+
+static cairo_status_t
+_cairo_default_context_restore (void *abstract_cr)
+{
+    cairo_default_context_t *cr = abstract_cr;
+
+    if (unlikely (_cairo_gstate_is_group (cr->gstate)))
+       return _cairo_error (CAIRO_STATUS_INVALID_RESTORE);
+
+    return _cairo_gstate_restore (&cr->gstate, &cr->gstate_freelist);
+}
+
+static cairo_status_t
+_cairo_default_context_push_group (void *abstract_cr, cairo_content_t content)
+{
+    cairo_default_context_t *cr = abstract_cr;
+    cairo_surface_t *group_surface;
+    cairo_clip_t *clip;
+    cairo_status_t status;
+
+    clip = _cairo_gstate_get_clip (cr->gstate);
+    if (_cairo_clip_is_all_clipped (clip)) {
+       group_surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 0, 0);
+       status = group_surface->status;
+       if (unlikely (status))
+           goto bail;
+    } else {
+       cairo_surface_t *parent_surface;
+       cairo_rectangle_int_t extents;
+       cairo_bool_t is_empty;
+
+       parent_surface = _cairo_gstate_get_target (cr->gstate);
+
+       /* Get the extents that we'll use in creating our new group surface */
+       is_empty = _cairo_surface_get_extents (parent_surface, &extents);
+       if (clip)
+           is_empty = _cairo_rectangle_intersect (&extents,
+                                                  _cairo_clip_get_extents (clip));
+
+       /* XXX unbounded surface creation */
+
+       group_surface = _cairo_surface_create_similar_solid (parent_surface,
+                                                            content,
+                                                            extents.width,
+                                                            extents.height,
+                                                            CAIRO_COLOR_TRANSPARENT);
+       status = group_surface->status;
+       if (unlikely (status))
+           goto bail;
+
+       /* Set device offsets on the new surface so that logically it appears at
+        * the same location on the parent surface -- when we pop_group this,
+        * the source pattern will get fixed up for the appropriate target surface
+        * device offsets, so we want to set our own surface offsets from /that/,
+        * and not from the device origin. */
+       cairo_surface_set_device_offset (group_surface,
+                                        parent_surface->device_transform.x0 - extents.x,
+                                        parent_surface->device_transform.y0 - extents.y);
+
+       /* If we have a current path, we need to adjust it to compensate for
+        * the device offset just applied. */
+       _cairo_path_fixed_translate (cr->path,
+                                    _cairo_fixed_from_int (-extents.x),
+                                    _cairo_fixed_from_int (-extents.y));
+    }
+
+    /* create a new gstate for the redirect */
+    status = _cairo_gstate_save (&cr->gstate, &cr->gstate_freelist);
+    if (unlikely (status))
+       goto bail;
+
+    status = _cairo_gstate_redirect_target (cr->gstate, group_surface);
+
+bail:
+    cairo_surface_destroy (group_surface);
+    return status;
+}
+
+static cairo_pattern_t *
+_cairo_default_context_pop_group (void *abstract_cr)
+{
+    cairo_default_context_t *cr = abstract_cr;
+    cairo_surface_t *group_surface;
+    cairo_pattern_t *group_pattern;
+    cairo_matrix_t group_matrix, device_transform_matrix;
+    cairo_status_t status;
+
+    /* Verify that we are at the right nesting level */
+    if (unlikely (! _cairo_gstate_is_group (cr->gstate)))
+       return _cairo_pattern_create_in_error (CAIRO_STATUS_INVALID_POP_GROUP);
+
+    /* Get a reference to the active surface before restoring */
+    group_surface = _cairo_gstate_get_target (cr->gstate);
+    group_surface = cairo_surface_reference (group_surface);
+
+    status = _cairo_gstate_restore (&cr->gstate, &cr->gstate_freelist);
+    assert (status == CAIRO_STATUS_SUCCESS);
+
+    group_pattern = cairo_pattern_create_for_surface (group_surface);
+    status = group_pattern->status;
+    if (unlikely (status))
+        goto done;
+
+    _cairo_gstate_get_matrix (cr->gstate, &group_matrix);
+    /* Transform by group_matrix centered around device_transform so that when
+     * we call _cairo_gstate_copy_transformed_pattern the result is a pattern
+     * with a matrix equivalent to the device_transform of group_surface. */
+    if (_cairo_surface_has_device_transform (group_surface)) {
+       cairo_pattern_set_matrix (group_pattern, &group_surface->device_transform);
+       _cairo_pattern_transform (group_pattern, &group_matrix);
+       _cairo_pattern_transform (group_pattern, &group_surface->device_transform_inverse);
+    } else {
+       cairo_pattern_set_matrix (group_pattern, &group_matrix);
+    }
+
+    /* If we have a current path, we need to adjust it to compensate for
+     * the device offset just removed. */
+    cairo_matrix_multiply (&device_transform_matrix,
+                           &_cairo_gstate_get_target (cr->gstate)->device_transform,
+                          &group_surface->device_transform_inverse);
+    _cairo_path_fixed_transform (cr->path, &device_transform_matrix);
+
+done:
+    cairo_surface_destroy (group_surface);
+
+    return group_pattern;
+}
+
+static cairo_status_t
+_cairo_default_context_set_source (void *abstract_cr,
+                                  cairo_pattern_t *source)
+{
+    cairo_default_context_t *cr = abstract_cr;
+
+    return _cairo_gstate_set_source (cr->gstate, source);
+}
+
+static cairo_bool_t
+_current_source_matches_solid (const cairo_pattern_t *pattern,
+                              double red,
+                              double green,
+                              double blue,
+                              double alpha)
+{
+    cairo_color_t color;
+
+    if (pattern->type != CAIRO_PATTERN_TYPE_SOLID)
+       return FALSE;
+
+    red   = _cairo_restrict_value (red,   0.0, 1.0);
+    green = _cairo_restrict_value (green, 0.0, 1.0);
+    blue  = _cairo_restrict_value (blue,  0.0, 1.0);
+    alpha = _cairo_restrict_value (alpha, 0.0, 1.0);
+
+    _cairo_color_init_rgba (&color, red, green, blue, alpha);
+    return _cairo_color_equal (&color,
+                              &((cairo_solid_pattern_t *) pattern)->color);
+}
+
+static cairo_status_t
+_cairo_default_context_set_source_rgba (void *abstract_cr, double red, double green, double blue, double alpha)
+{
+    cairo_default_context_t *cr = abstract_cr;
+    cairo_pattern_t *pattern;
+    cairo_status_t status;
+
+    if (_current_source_matches_solid (cr->gstate->source,
+                                      red, green, blue, alpha))
+       return CAIRO_STATUS_SUCCESS;
+
+    /* push the current pattern to the freed lists */
+    _cairo_default_context_set_source (cr, (cairo_pattern_t *) &_cairo_pattern_black);
+
+    pattern = cairo_pattern_create_rgba (red, green, blue, alpha);
+    if (unlikely (pattern->status))
+       return pattern->status;
+
+    status = _cairo_default_context_set_source (cr, pattern);
+    cairo_pattern_destroy (pattern);
+
+    return status;
+}
+
+static cairo_status_t
+_cairo_default_context_set_source_surface (void *abstract_cr,
+                                          cairo_surface_t *surface,
+                                          double          x,
+                                          double          y)
+{
+    cairo_default_context_t *cr = abstract_cr;
+    cairo_pattern_t *pattern;
+    cairo_matrix_t matrix;
+    cairo_status_t status;
+
+    /* push the current pattern to the freed lists */
+    _cairo_default_context_set_source (cr, (cairo_pattern_t *) &_cairo_pattern_black);
+
+    pattern = cairo_pattern_create_for_surface (surface);
+    if (unlikely (pattern->status))
+       return pattern->status;
+
+    cairo_matrix_init_translate (&matrix, -x, -y);
+    cairo_pattern_set_matrix (pattern, &matrix);
+
+    status = _cairo_default_context_set_source (cr, pattern);
+    cairo_pattern_destroy (pattern);
+
+    return status;
+}
+
+static cairo_pattern_t *
+_cairo_default_context_get_source (void *abstract_cr)
+{
+    cairo_default_context_t *cr = abstract_cr;
+
+    return _cairo_gstate_get_source (cr->gstate);
+}
+
+static cairo_status_t
+_cairo_default_context_set_tolerance (void *abstract_cr,
+                                     double tolerance)
+{
+    cairo_default_context_t *cr = abstract_cr;
+
+    if (tolerance < CAIRO_TOLERANCE_MINIMUM)
+       tolerance = CAIRO_TOLERANCE_MINIMUM;
+
+    return _cairo_gstate_set_tolerance (cr->gstate, tolerance);
+}
+
+static cairo_status_t
+_cairo_default_context_set_operator (void *abstract_cr, cairo_operator_t op)
+{
+    cairo_default_context_t *cr = abstract_cr;
+
+    return _cairo_gstate_set_operator (cr->gstate, op);
+}
+
+static cairo_status_t
+_cairo_default_context_set_opacity (void *abstract_cr, double opacity)
+{
+    cairo_default_context_t *cr = abstract_cr;
+
+    return _cairo_gstate_set_opacity (cr->gstate, opacity);
+}
+
+static cairo_status_t
+_cairo_default_context_set_antialias (void *abstract_cr,
+                                     cairo_antialias_t antialias)
+{
+    cairo_default_context_t *cr = abstract_cr;
+
+    return _cairo_gstate_set_antialias (cr->gstate, antialias);
+}
+
+static cairo_status_t
+_cairo_default_context_set_fill_rule (void *abstract_cr,
+                                     cairo_fill_rule_t fill_rule)
+{
+    cairo_default_context_t *cr = abstract_cr;
+
+    return _cairo_gstate_set_fill_rule (cr->gstate, fill_rule);
+}
+
+static cairo_status_t
+_cairo_default_context_set_line_width (void *abstract_cr,
+                                      double line_width)
+{
+    cairo_default_context_t *cr = abstract_cr;
+
+    return _cairo_gstate_set_line_width (cr->gstate, line_width);
+}
+
+static cairo_status_t
+_cairo_default_context_set_line_cap (void *abstract_cr,
+                                    cairo_line_cap_t line_cap)
+{
+    cairo_default_context_t *cr = abstract_cr;
+
+    return _cairo_gstate_set_line_cap (cr->gstate, line_cap);
+}
+
+static cairo_status_t
+_cairo_default_context_set_line_join (void *abstract_cr,
+                                     cairo_line_join_t line_join)
+{
+    cairo_default_context_t *cr = abstract_cr;
+
+    return _cairo_gstate_set_line_join (cr->gstate, line_join);
+}
+
+static cairo_status_t
+_cairo_default_context_set_dash (void *abstract_cr,
+                                const double *dashes,
+                                int          num_dashes,
+                                double       offset)
+{
+    cairo_default_context_t *cr = abstract_cr;
+
+    return _cairo_gstate_set_dash (cr->gstate,
+                                  dashes, num_dashes, offset);
+}
+
+static cairo_status_t
+_cairo_default_context_set_miter_limit (void *abstract_cr,
+                                       double limit)
+{
+    cairo_default_context_t *cr = abstract_cr;
+
+    return _cairo_gstate_set_miter_limit (cr->gstate, limit);
+}
+
+static cairo_antialias_t
+_cairo_default_context_get_antialias (void *abstract_cr)
+{
+    cairo_default_context_t *cr = abstract_cr;
+
+    return _cairo_gstate_get_antialias (cr->gstate);
+}
+
+static void
+_cairo_default_context_get_dash (void *abstract_cr,
+                                double *dashes,
+                                int *num_dashes,
+                                double *offset)
+{
+    cairo_default_context_t *cr = abstract_cr;
+
+    _cairo_gstate_get_dash (cr->gstate, dashes, num_dashes, offset);
+}
+
+static cairo_fill_rule_t
+_cairo_default_context_get_fill_rule (void *abstract_cr)
+{
+    cairo_default_context_t *cr = abstract_cr;
+
+    return _cairo_gstate_get_fill_rule (cr->gstate);
+}
+
+static double
+_cairo_default_context_get_line_width (void *abstract_cr)
+{
+    cairo_default_context_t *cr = abstract_cr;
+
+    return _cairo_gstate_get_line_width (cr->gstate);
+}
+
+static cairo_line_cap_t
+_cairo_default_context_get_line_cap (void *abstract_cr)
+{
+    cairo_default_context_t *cr = abstract_cr;
+
+    return _cairo_gstate_get_line_cap (cr->gstate);
+}
+
+static cairo_line_join_t
+_cairo_default_context_get_line_join (void *abstract_cr)
+{
+    cairo_default_context_t *cr = abstract_cr;
+
+    return _cairo_gstate_get_line_join (cr->gstate);
+}
+
+static double
+_cairo_default_context_get_miter_limit (void *abstract_cr)
+{
+    cairo_default_context_t *cr = abstract_cr;
+
+    return _cairo_gstate_get_miter_limit (cr->gstate);
+}
+
+static cairo_operator_t
+_cairo_default_context_get_operator (void *abstract_cr)
+{
+    cairo_default_context_t *cr = abstract_cr;
+
+    return _cairo_gstate_get_operator (cr->gstate);
+}
+
+static double
+_cairo_default_context_get_opacity (void *abstract_cr)
+{
+    cairo_default_context_t *cr = abstract_cr;
+
+    return _cairo_gstate_get_opacity (cr->gstate);
+}
+
+static double
+_cairo_default_context_get_tolerance (void *abstract_cr)
+{
+    cairo_default_context_t *cr = abstract_cr;
+
+    return _cairo_gstate_get_tolerance (cr->gstate);
+}
+
+
+/* Current tranformation matrix */
+
+static cairo_status_t
+_cairo_default_context_translate (void *abstract_cr,
+                                 double tx,
+                                 double ty)
+{
+    cairo_default_context_t *cr = abstract_cr;
+
+    return _cairo_gstate_translate (cr->gstate, tx, ty);
+}
+
+static cairo_status_t
+_cairo_default_context_scale (void *abstract_cr,
+                             double sx,
+                             double sy)
+{
+    cairo_default_context_t *cr = abstract_cr;
+
+    return _cairo_gstate_scale (cr->gstate, sx, sy);
+}
+
+static cairo_status_t
+_cairo_default_context_rotate (void *abstract_cr,
+                              double theta)
+{
+    cairo_default_context_t *cr = abstract_cr;
+
+    return _cairo_gstate_rotate (cr->gstate, theta);
+}
+
+static cairo_status_t
+_cairo_default_context_transform (void *abstract_cr,
+                                 const cairo_matrix_t *matrix)
+{
+    cairo_default_context_t *cr = abstract_cr;
+
+    return _cairo_gstate_transform (cr->gstate, matrix);
+}
+
+static cairo_status_t
+_cairo_default_context_set_matrix (void *abstract_cr,
+                                  const cairo_matrix_t *matrix)
+{
+    cairo_default_context_t *cr = abstract_cr;
+
+    return _cairo_gstate_set_matrix (cr->gstate, matrix);
+}
+
+static cairo_status_t
+_cairo_default_context_set_identity_matrix (void *abstract_cr)
+{
+    cairo_default_context_t *cr = abstract_cr;
+
+    _cairo_gstate_identity_matrix (cr->gstate);
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_default_context_get_matrix (void *abstract_cr,
+                                  cairo_matrix_t *matrix)
+{
+    cairo_default_context_t *cr = abstract_cr;
+
+    _cairo_gstate_get_matrix (cr->gstate, matrix);
+}
+
+static void
+_cairo_default_context_user_to_device (void *abstract_cr,
+                                      double *x,
+                                      double *y)
+{
+    cairo_default_context_t *cr = abstract_cr;
+
+    _cairo_gstate_user_to_device (cr->gstate, x, y);
+}
+
+static void
+_cairo_default_context_user_to_device_distance (void *abstract_cr, double *dx, double *dy)
+{
+    cairo_default_context_t *cr = abstract_cr;
+
+    _cairo_gstate_user_to_device_distance (cr->gstate, dx, dy);
+}
+
+static void
+_cairo_default_context_device_to_user (void *abstract_cr,
+                                      double *x,
+                                      double *y)
+{
+    cairo_default_context_t *cr = abstract_cr;
+
+    _cairo_gstate_device_to_user (cr->gstate, x, y);
+}
+
+static void
+_cairo_default_context_device_to_user_distance (void *abstract_cr,
+                                               double *dx,
+                                               double *dy)
+{
+    cairo_default_context_t *cr = abstract_cr;
+
+    _cairo_gstate_device_to_user_distance (cr->gstate, dx, dy);
+}
+
+/* Path constructor */
+
+static cairo_status_t
+_cairo_default_context_new_path (void *abstract_cr)
+{
+    cairo_default_context_t *cr = abstract_cr;
+
+    _cairo_path_fixed_fini (cr->path);
+    _cairo_path_fixed_init (cr->path);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_default_context_new_sub_path (void *abstract_cr)
+{
+    cairo_default_context_t *cr = abstract_cr;
+
+    _cairo_path_fixed_new_sub_path (cr->path);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_default_context_move_to (void *abstract_cr, double x, double y)
+{
+    cairo_default_context_t *cr = abstract_cr;
+    cairo_fixed_t x_fixed, y_fixed;
+
+    _cairo_gstate_user_to_backend (cr->gstate, &x, &y);
+    x_fixed = _cairo_fixed_from_double (x);
+    y_fixed = _cairo_fixed_from_double (y);
+
+    return _cairo_path_fixed_move_to (cr->path, x_fixed, y_fixed);
+}
+
+static cairo_status_t
+_cairo_default_context_line_to (void *abstract_cr, double x, double y)
+{
+    cairo_default_context_t *cr = abstract_cr;
+    cairo_fixed_t x_fixed, y_fixed;
+
+    _cairo_gstate_user_to_backend (cr->gstate, &x, &y);
+    x_fixed = _cairo_fixed_from_double (x);
+    y_fixed = _cairo_fixed_from_double (y);
+
+    return _cairo_path_fixed_line_to (cr->path, x_fixed, y_fixed);
+}
+
+static cairo_status_t
+_cairo_default_context_curve_to (void *abstract_cr,
+                                double x1, double y1,
+                                double x2, double y2,
+                                double x3, double y3)
+{
+    cairo_default_context_t *cr = abstract_cr;
+    cairo_fixed_t x1_fixed, y1_fixed;
+    cairo_fixed_t x2_fixed, y2_fixed;
+    cairo_fixed_t x3_fixed, y3_fixed;
+
+    _cairo_gstate_user_to_backend (cr->gstate, &x1, &y1);
+    _cairo_gstate_user_to_backend (cr->gstate, &x2, &y2);
+    _cairo_gstate_user_to_backend (cr->gstate, &x3, &y3);
+
+    x1_fixed = _cairo_fixed_from_double (x1);
+    y1_fixed = _cairo_fixed_from_double (y1);
+
+    x2_fixed = _cairo_fixed_from_double (x2);
+    y2_fixed = _cairo_fixed_from_double (y2);
+
+    x3_fixed = _cairo_fixed_from_double (x3);
+    y3_fixed = _cairo_fixed_from_double (y3);
+
+    return _cairo_path_fixed_curve_to (cr->path,
+                                      x1_fixed, y1_fixed,
+                                      x2_fixed, y2_fixed,
+                                      x3_fixed, y3_fixed);
+}
+
+static cairo_status_t
+_cairo_default_context_arc (void *abstract_cr,
+                           double xc, double yc, double radius,
+                           double angle1, double angle2,
+                           cairo_bool_t forward)
+{
+    cairo_default_context_t *cr = abstract_cr;
+    cairo_status_t status;
+
+    /* Do nothing, successfully, if radius is <= 0 */
+    if (radius <= 0.0) {
+       cairo_fixed_t x_fixed, y_fixed;
+
+       _cairo_gstate_user_to_backend (cr->gstate, &xc, &yc);
+       x_fixed = _cairo_fixed_from_double (xc);
+       y_fixed = _cairo_fixed_from_double (yc);
+       status = _cairo_path_fixed_line_to (cr->path, x_fixed, y_fixed);
+       if (unlikely (status))
+           return status;
+
+       status = _cairo_path_fixed_line_to (cr->path, x_fixed, y_fixed);
+       if (unlikely (status))
+           return status;
+
+       return CAIRO_STATUS_SUCCESS;
+    }
+
+    status = _cairo_default_context_line_to (cr,
+                                            xc + radius * cos (angle1),
+                                            yc + radius * sin (angle1));
+
+    if (unlikely (status))
+       return status;
+
+    if (forward)
+       _cairo_arc_path (&cr->base, xc, yc, radius, angle1, angle2);
+    else
+       _cairo_arc_path_negative (&cr->base, xc, yc, radius, angle1, angle2);
+
+    return CAIRO_STATUS_SUCCESS; /* any error will have already been set on cr */
+}
+
+static cairo_status_t
+_cairo_default_context_rel_move_to (void *abstract_cr, double dx, double dy)
+{
+    cairo_default_context_t *cr = abstract_cr;
+    cairo_fixed_t dx_fixed, dy_fixed;
+
+    _cairo_gstate_user_to_device_distance (cr->gstate, &dx, &dy);
+
+    dx_fixed = _cairo_fixed_from_double (dx);
+    dy_fixed = _cairo_fixed_from_double (dy);
+
+    return _cairo_path_fixed_rel_move_to (cr->path, dx_fixed, dy_fixed);
+}
+
+static cairo_status_t
+_cairo_default_context_rel_line_to (void *abstract_cr, double dx, double dy)
+{
+    cairo_default_context_t *cr = abstract_cr;
+    cairo_fixed_t dx_fixed, dy_fixed;
+
+    _cairo_gstate_user_to_device_distance (cr->gstate, &dx, &dy);
+
+    dx_fixed = _cairo_fixed_from_double (dx);
+    dy_fixed = _cairo_fixed_from_double (dy);
+
+    return _cairo_path_fixed_rel_line_to (cr->path, dx_fixed, dy_fixed);
+}
+
+
+static cairo_status_t
+_cairo_default_context_rel_curve_to (void *abstract_cr,
+                                    double dx1, double dy1,
+                                    double dx2, double dy2,
+                                    double dx3, double dy3)
+{
+    cairo_default_context_t *cr = abstract_cr;
+    cairo_fixed_t dx1_fixed, dy1_fixed;
+    cairo_fixed_t dx2_fixed, dy2_fixed;
+    cairo_fixed_t dx3_fixed, dy3_fixed;
+
+    _cairo_gstate_user_to_device_distance (cr->gstate, &dx1, &dy1);
+    _cairo_gstate_user_to_device_distance (cr->gstate, &dx2, &dy2);
+    _cairo_gstate_user_to_device_distance (cr->gstate, &dx3, &dy3);
+
+    dx1_fixed = _cairo_fixed_from_double (dx1);
+    dy1_fixed = _cairo_fixed_from_double (dy1);
+
+    dx2_fixed = _cairo_fixed_from_double (dx2);
+    dy2_fixed = _cairo_fixed_from_double (dy2);
+
+    dx3_fixed = _cairo_fixed_from_double (dx3);
+    dy3_fixed = _cairo_fixed_from_double (dy3);
+
+    return _cairo_path_fixed_rel_curve_to (cr->path,
+                                          dx1_fixed, dy1_fixed,
+                                          dx2_fixed, dy2_fixed,
+                                          dx3_fixed, dy3_fixed);
+}
+
+static cairo_status_t
+_cairo_default_context_close_path (void *abstract_cr)
+{
+    cairo_default_context_t *cr = abstract_cr;
+
+    return _cairo_path_fixed_close_path (cr->path);
+}
+
+static cairo_status_t
+_cairo_default_context_rectangle (void *abstract_cr,
+                                 double x, double y,
+                                 double width, double height)
+{
+    cairo_default_context_t *cr = abstract_cr;
+    cairo_status_t status;
+
+    status = _cairo_default_context_move_to (cr, x, y);
+    if (unlikely (status))
+       return status;
+
+    status = _cairo_default_context_rel_line_to (cr, width, 0);
+    if (unlikely (status))
+       return status;
+
+    status = _cairo_default_context_rel_line_to (cr, 0, height);
+    if (unlikely (status))
+       return status;
+
+    status = _cairo_default_context_rel_line_to (cr, -width, 0);
+    if (unlikely (status))
+       return status;
+
+    return _cairo_default_context_close_path (cr);
+}
+
+static void
+_cairo_default_context_path_extents (void *abstract_cr,
+                                    double *x1,
+                                    double *y1,
+                                    double *x2,
+                                    double *y2)
+{
+    cairo_default_context_t *cr = abstract_cr;
+
+    _cairo_gstate_path_extents (cr->gstate,
+                               cr->path,
+                               x1, y1, x2, y2);
+}
+
+static cairo_bool_t
+_cairo_default_context_has_current_point (void *abstract_cr)
+{
+    cairo_default_context_t *cr = abstract_cr;
+
+    return cr->path->has_current_point;
+}
+
+static cairo_bool_t
+_cairo_default_context_get_current_point (void *abstract_cr,
+                                         double *x,
+                                         double *y)
+{
+    cairo_default_context_t *cr = abstract_cr;
+    cairo_fixed_t x_fixed, y_fixed;
+
+    if (_cairo_path_fixed_get_current_point (cr->path, &x_fixed, &y_fixed))
+    {
+       *x = _cairo_fixed_to_double (x_fixed);
+       *y = _cairo_fixed_to_double (y_fixed);
+       _cairo_gstate_backend_to_user (cr->gstate, x, y);
+
+       return TRUE;
+    }
+    else
+    {
+       return FALSE;
+    }
+}
+
+static cairo_path_t *
+_cairo_default_context_copy_path (void *abstract_cr)
+{
+    cairo_default_context_t *cr = abstract_cr;
+
+    return _cairo_path_create (cr->path, &cr->base);
+}
+
+static cairo_path_t *
+_cairo_default_context_copy_path_flat (void *abstract_cr)
+{
+    cairo_default_context_t *cr = abstract_cr;
+
+    return _cairo_path_create_flat (cr->path, &cr->base);
+}
+
+static cairo_status_t
+_cairo_default_context_append_path (void *abstract_cr,
+                                   const cairo_path_t *path)
+{
+    cairo_default_context_t *cr = abstract_cr;
+
+    return _cairo_path_append_to_context (path, &cr->base);
+}
+
+static cairo_status_t
+_cairo_default_context_paint (void *abstract_cr)
+{
+    cairo_default_context_t *cr = abstract_cr;
+
+    return _cairo_gstate_paint (cr->gstate);
+}
+
+static cairo_status_t
+_cairo_default_context_paint_with_alpha (void *abstract_cr,
+                                        double alpha)
+{
+    cairo_default_context_t *cr = abstract_cr;
+    cairo_solid_pattern_t pattern;
+    cairo_status_t status;
+    cairo_color_t color;
+
+    if (CAIRO_ALPHA_IS_OPAQUE (alpha))
+       return _cairo_gstate_paint (cr->gstate);
+
+    if (CAIRO_ALPHA_IS_ZERO (alpha) &&
+        _cairo_operator_bounded_by_mask (cr->gstate->op)) {
+       return CAIRO_STATUS_SUCCESS;
+    }
+
+    _cairo_color_init_rgba (&color, 0., 0., 0., alpha);
+    _cairo_pattern_init_solid (&pattern, &color);
+
+    status = _cairo_gstate_mask (cr->gstate, &pattern.base);
+    _cairo_pattern_fini (&pattern.base);
+
+    return status;
+}
+
+static cairo_status_t
+_cairo_default_context_mask (void *abstract_cr,
+                            cairo_pattern_t *mask)
+{
+    cairo_default_context_t *cr = abstract_cr;
+
+    return _cairo_gstate_mask (cr->gstate, mask);
+}
+
+static cairo_status_t
+_cairo_default_context_stroke_preserve (void *abstract_cr)
+{
+    cairo_default_context_t *cr = abstract_cr;
+
+    return _cairo_gstate_stroke (cr->gstate, cr->path);
+}
+
+static cairo_status_t
+_cairo_default_context_stroke (void *abstract_cr)
+{
+    cairo_default_context_t *cr = abstract_cr;
+    cairo_status_t status;
+
+    status = _cairo_gstate_stroke (cr->gstate, cr->path);
+    if (unlikely (status))
+       return status;
+
+    return _cairo_default_context_new_path (cr);
+}
+
+static cairo_status_t
+_cairo_default_context_in_stroke (void *abstract_cr,
+                                 double x, double y,
+                                 cairo_bool_t *inside)
+{
+    cairo_default_context_t *cr = abstract_cr;
+
+    return _cairo_gstate_in_stroke (cr->gstate,
+                                   cr->path,
+                                   x, y,
+                                   inside);
+}
+
+static cairo_status_t
+_cairo_default_context_stroke_extents (void *abstract_cr,
+                                      double *x1, double *y1, double *x2, double *y2)
+{
+    cairo_default_context_t *cr = abstract_cr;
+
+    return _cairo_gstate_stroke_extents (cr->gstate,
+                                        cr->path,
+                                        x1, y1, x2, y2);
+}
+
+static cairo_status_t
+_cairo_default_context_fill_preserve (void *abstract_cr)
+{
+    cairo_default_context_t *cr = abstract_cr;
+
+    return _cairo_gstate_fill (cr->gstate, cr->path);
+}
+
+static cairo_status_t
+_cairo_default_context_fill (void *abstract_cr)
+{
+    cairo_default_context_t *cr = abstract_cr;
+    cairo_status_t status;
+
+    status = _cairo_gstate_fill (cr->gstate, cr->path);
+    if (unlikely (status))
+       return status;
+
+    return _cairo_default_context_new_path (cr);
+}
+
+static cairo_status_t
+_cairo_default_context_in_fill (void *abstract_cr,
+                               double x, double y,
+                               cairo_bool_t *inside)
+{
+    cairo_default_context_t *cr = abstract_cr;
+
+    *inside = _cairo_gstate_in_fill (cr->gstate,
+                                    cr->path,
+                                    x, y);
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_default_context_fill_extents (void *abstract_cr,
+                                    double *x1, double *y1, double *x2, double *y2)
+{
+    cairo_default_context_t *cr = abstract_cr;
+
+    return _cairo_gstate_fill_extents (cr->gstate,
+                                      cr->path,
+                                      x1, y1, x2, y2);
+}
+
+static cairo_status_t
+_cairo_default_context_clip_preserve (void *abstract_cr)
+{
+    cairo_default_context_t *cr = abstract_cr;
+
+    return _cairo_gstate_clip (cr->gstate, cr->path);
+}
+
+static cairo_status_t
+_cairo_default_context_clip (void *abstract_cr)
+{
+    cairo_default_context_t *cr = abstract_cr;
+    cairo_status_t status;
+
+    status = _cairo_gstate_clip (cr->gstate, cr->path);
+    if (unlikely (status))
+       return status;
+
+    return _cairo_default_context_new_path (cr);
+}
+
+static cairo_status_t
+_cairo_default_context_in_clip (void *abstract_cr,
+                               double x, double y,
+                               cairo_bool_t *inside)
+{
+    cairo_default_context_t *cr = abstract_cr;
+
+    *inside = _cairo_gstate_in_clip (cr->gstate, x, y);
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_default_context_reset_clip (void *abstract_cr)
+{
+    cairo_default_context_t *cr = abstract_cr;
+
+    return _cairo_gstate_reset_clip (cr->gstate);
+}
+
+static cairo_status_t
+_cairo_default_context_clip_extents (void *abstract_cr,
+                                    double *x1, double *y1, double *x2, double *y2)
+{
+    cairo_default_context_t *cr = abstract_cr;
+
+    if (! _cairo_gstate_clip_extents (cr->gstate, x1, y1, x2, y2)) {
+       *x1 = -INFINITY;
+       *y1 = -INFINITY;
+       *x2 = +INFINITY;
+       *y2 = +INFINITY;
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_rectangle_list_t *
+_cairo_default_context_copy_clip_rectangle_list (void *abstract_cr)
+{
+    cairo_default_context_t *cr = abstract_cr;
+
+    return _cairo_gstate_copy_clip_rectangle_list (cr->gstate);
+}
+
+static cairo_status_t
+_cairo_default_context_copy_page (void *abstract_cr)
+{
+    cairo_default_context_t *cr = abstract_cr;
+
+    return _cairo_gstate_copy_page (cr->gstate);
+}
+
+static cairo_status_t
+_cairo_default_context_show_page (void *abstract_cr)
+{
+    cairo_default_context_t *cr = abstract_cr;
+
+    return _cairo_gstate_show_page (cr->gstate);
+}
+
+static cairo_status_t
+_cairo_default_context_set_font_face (void *abstract_cr,
+                                     cairo_font_face_t *font_face)
+{
+    cairo_default_context_t *cr = abstract_cr;
+
+    return _cairo_gstate_set_font_face (cr->gstate, font_face);
+}
+
+static cairo_font_face_t *
+_cairo_default_context_get_font_face (void *abstract_cr)
+{
+    cairo_default_context_t *cr = abstract_cr;
+    cairo_font_face_t *font_face;
+    cairo_status_t status;
+
+    status = _cairo_gstate_get_font_face (cr->gstate, &font_face);
+    if (unlikely (status)) {
+       _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+       return (cairo_font_face_t *) &_cairo_font_face_nil;
+    }
+
+    return font_face;
+}
+
+static cairo_status_t
+_cairo_default_context_font_extents (void *abstract_cr,
+                                    cairo_font_extents_t *extents)
+{
+    cairo_default_context_t *cr = abstract_cr;
+
+    return _cairo_gstate_get_font_extents (cr->gstate, extents);
+}
+
+static cairo_status_t
+_cairo_default_context_set_font_size (void *abstract_cr,
+                                     double size)
+{
+    cairo_default_context_t *cr = abstract_cr;
+
+    return _cairo_gstate_set_font_size (cr->gstate, size);
+}
+
+static cairo_status_t
+_cairo_default_context_set_font_matrix (void *abstract_cr,
+                                       const cairo_matrix_t *matrix)
+{
+    cairo_default_context_t *cr = abstract_cr;
+
+    return _cairo_gstate_set_font_matrix (cr->gstate, matrix);
+}
+
+static void
+_cairo_default_context_get_font_matrix (void *abstract_cr,
+                                       cairo_matrix_t *matrix)
+{
+    cairo_default_context_t *cr = abstract_cr;
+
+    _cairo_gstate_get_font_matrix (cr->gstate, matrix);
+}
+
+static cairo_status_t
+_cairo_default_context_set_font_options (void *abstract_cr,
+                                        const cairo_font_options_t *options)
+{
+    cairo_default_context_t *cr = abstract_cr;
+
+    _cairo_gstate_set_font_options (cr->gstate, options);
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_default_context_get_font_options (void *abstract_cr,
+                                        cairo_font_options_t *options)
+{
+    cairo_default_context_t *cr = abstract_cr;
+
+    _cairo_gstate_get_font_options (cr->gstate, options);
+}
+
+static cairo_status_t
+_cairo_default_context_set_scaled_font (void *abstract_cr,
+                                       cairo_scaled_font_t *scaled_font)
+{
+    cairo_default_context_t *cr = abstract_cr;
+    cairo_bool_t was_previous;
+    cairo_status_t status;
+
+    if (scaled_font == cr->gstate->scaled_font)
+       return CAIRO_STATUS_SUCCESS;
+
+    was_previous = scaled_font == cr->gstate->previous_scaled_font;
+
+    status = _cairo_gstate_set_font_face (cr->gstate, scaled_font->font_face);
+    if (unlikely (status))
+       return status;
+
+    status = _cairo_gstate_set_font_matrix (cr->gstate, &scaled_font->font_matrix);
+    if (unlikely (status))
+       return status;
+
+    _cairo_gstate_set_font_options (cr->gstate, &scaled_font->options);
+
+    if (was_previous)
+       cr->gstate->scaled_font = cairo_scaled_font_reference (scaled_font);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_scaled_font_t *
+_cairo_default_context_get_scaled_font (void *abstract_cr)
+{
+    cairo_default_context_t *cr = abstract_cr;
+    cairo_scaled_font_t *scaled_font;
+    cairo_status_t status;
+
+    status = _cairo_gstate_get_scaled_font (cr->gstate, &scaled_font);
+    if (unlikely (status))
+       return _cairo_scaled_font_create_in_error (status);
+
+    return scaled_font;
+}
+
+static cairo_status_t
+_cairo_default_context_glyphs (void *abstract_cr,
+                              const cairo_glyph_t *glyphs,
+                              int num_glyphs,
+                              cairo_glyph_text_info_t *info)
+{
+    cairo_default_context_t *cr = abstract_cr;
+
+    return _cairo_gstate_show_text_glyphs (cr->gstate, glyphs, num_glyphs, info);
+}
+
+static cairo_status_t
+_cairo_default_context_glyph_path (void *abstract_cr,
+                                  const cairo_glyph_t *glyphs,
+                                  int num_glyphs)
+{
+    cairo_default_context_t *cr = abstract_cr;
+
+    return _cairo_gstate_glyph_path (cr->gstate,
+                                    glyphs, num_glyphs,
+                                    cr->path);
+}
+
+static cairo_status_t
+_cairo_default_context_glyph_extents (void                *abstract_cr,
+                                     const cairo_glyph_t    *glyphs,
+                                     int                    num_glyphs,
+                                     cairo_text_extents_t   *extents)
+{
+    cairo_default_context_t *cr = abstract_cr;
+
+    return _cairo_gstate_glyph_extents (cr->gstate, glyphs, num_glyphs, extents);
+}
+
+static const cairo_backend_t _cairo_default_context_backend = {
+    CAIRO_TYPE_DEFAULT,
+    _cairo_default_context_destroy,
+
+    _cairo_default_context_get_original_target,
+    _cairo_default_context_get_current_target,
+
+    _cairo_default_context_save,
+    _cairo_default_context_restore,
+
+    _cairo_default_context_push_group,
+    _cairo_default_context_pop_group,
+
+    _cairo_default_context_set_source_rgba,
+    _cairo_default_context_set_source_surface,
+    _cairo_default_context_set_source,
+    _cairo_default_context_get_source,
+
+    _cairo_default_context_set_antialias,
+    _cairo_default_context_set_dash,
+    _cairo_default_context_set_fill_rule,
+    _cairo_default_context_set_line_cap,
+    _cairo_default_context_set_line_join,
+    _cairo_default_context_set_line_width,
+    _cairo_default_context_set_miter_limit,
+    _cairo_default_context_set_opacity,
+    _cairo_default_context_set_operator,
+    _cairo_default_context_set_tolerance,
+    _cairo_default_context_get_antialias,
+    _cairo_default_context_get_dash,
+    _cairo_default_context_get_fill_rule,
+    _cairo_default_context_get_line_cap,
+    _cairo_default_context_get_line_join,
+    _cairo_default_context_get_line_width,
+    _cairo_default_context_get_miter_limit,
+    _cairo_default_context_get_opacity,
+    _cairo_default_context_get_operator,
+    _cairo_default_context_get_tolerance,
+
+    _cairo_default_context_translate,
+    _cairo_default_context_scale,
+    _cairo_default_context_rotate,
+    _cairo_default_context_transform,
+    _cairo_default_context_set_matrix,
+    _cairo_default_context_set_identity_matrix,
+    _cairo_default_context_get_matrix,
+    _cairo_default_context_user_to_device,
+    _cairo_default_context_user_to_device_distance,
+    _cairo_default_context_device_to_user,
+    _cairo_default_context_device_to_user_distance,
+
+    _cairo_default_context_new_path,
+    _cairo_default_context_new_sub_path,
+    _cairo_default_context_move_to,
+    _cairo_default_context_rel_move_to,
+    _cairo_default_context_line_to,
+    _cairo_default_context_rel_line_to,
+    _cairo_default_context_curve_to,
+    _cairo_default_context_rel_curve_to,
+    NULL, /* arc-to */
+    NULL, /* rel-arc-to */
+    _cairo_default_context_close_path,
+    _cairo_default_context_arc,
+    _cairo_default_context_rectangle,
+    _cairo_default_context_path_extents,
+    _cairo_default_context_has_current_point,
+    _cairo_default_context_get_current_point,
+    _cairo_default_context_copy_path,
+    _cairo_default_context_copy_path_flat,
+    _cairo_default_context_append_path,
+
+    NULL, /* stroke-to-path */
+
+    _cairo_default_context_clip,
+    _cairo_default_context_clip_preserve,
+    _cairo_default_context_in_clip,
+    _cairo_default_context_clip_extents,
+    _cairo_default_context_reset_clip,
+    _cairo_default_context_copy_clip_rectangle_list,
+
+    _cairo_default_context_paint,
+    _cairo_default_context_paint_with_alpha,
+    _cairo_default_context_mask,
+
+    _cairo_default_context_stroke,
+    _cairo_default_context_stroke_preserve,
+    _cairo_default_context_in_stroke,
+    _cairo_default_context_stroke_extents,
+
+    _cairo_default_context_fill,
+    _cairo_default_context_fill_preserve,
+    _cairo_default_context_in_fill,
+    _cairo_default_context_fill_extents,
+
+    _cairo_default_context_set_font_face,
+    _cairo_default_context_get_font_face,
+    _cairo_default_context_set_font_size,
+    _cairo_default_context_set_font_matrix,
+    _cairo_default_context_get_font_matrix,
+    _cairo_default_context_set_font_options,
+    _cairo_default_context_get_font_options,
+    _cairo_default_context_set_scaled_font,
+    _cairo_default_context_get_scaled_font,
+    _cairo_default_context_font_extents,
+
+    _cairo_default_context_glyphs,
+    _cairo_default_context_glyph_path,
+    _cairo_default_context_glyph_extents,
+
+    _cairo_default_context_copy_page,
+    _cairo_default_context_show_page,
+};
+
+cairo_t *
+_cairo_default_context_create (void *target)
+{
+    cairo_default_context_t *cr;
+    cairo_status_t status;
+
+    cr = _freed_pool_get (&context_pool);
+    if (unlikely (cr == NULL)) {
+       cr = malloc (sizeof (cairo_default_context_t));
+       if (unlikely (cr == NULL))
+           return _cairo_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+    }
+
+    _cairo_init (&cr->base, &_cairo_default_context_backend);
+    _cairo_path_fixed_init (cr->path);
+
+    cr->gstate = &cr->gstate_tail[0];
+    cr->gstate_freelist = &cr->gstate_tail[1];
+    cr->gstate_tail[1].next = NULL;
+
+    status = _cairo_gstate_init (cr->gstate, target);
+    if (unlikely (status)) {
+       _freed_pool_put (&context_pool, cr);
+       return _cairo_create_in_error (status);
+    }
+
+    return &cr->base;
+}
diff --git a/src/cairo-deflate-stream.c b/src/cairo-deflate-stream.c
new file mode 100644 (file)
index 0000000..ae23bda
--- /dev/null
@@ -0,0 +1,156 @@
+/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2006 Adrian Johnson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Adrian Johnson.
+ *
+ * Author(s):
+ *     Adrian Johnson <ajohnson@redneon.com>
+ */
+
+#include "cairoint.h"
+
+#if CAIRO_HAS_DEFLATE_STREAM
+
+#include "cairo-error-private.h"
+#include "cairo-output-stream-private.h"
+#include <zlib.h>
+
+#define BUFFER_SIZE 16384
+
+typedef struct _cairo_deflate_stream {
+    cairo_output_stream_t  base;
+    cairo_output_stream_t *output;
+    z_stream               zlib_stream;
+    unsigned char          input_buf[BUFFER_SIZE];
+    unsigned char          output_buf[BUFFER_SIZE];
+} cairo_deflate_stream_t;
+
+static void
+cairo_deflate_stream_deflate (cairo_deflate_stream_t *stream, cairo_bool_t flush)
+{
+    int ret;
+    cairo_bool_t finished;
+
+    do {
+        ret = deflate (&stream->zlib_stream, flush ? Z_FINISH : Z_NO_FLUSH);
+        if (flush || stream->zlib_stream.avail_out == 0)
+        {
+            _cairo_output_stream_write (stream->output,
+                                        stream->output_buf,
+                                        BUFFER_SIZE - stream->zlib_stream.avail_out);
+            stream->zlib_stream.next_out = stream->output_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->input_buf;
+}
+
+static cairo_status_t
+_cairo_deflate_stream_write (cairo_output_stream_t *base,
+                             const unsigned char   *data,
+                             unsigned int          length)
+{
+    cairo_deflate_stream_t *stream = (cairo_deflate_stream_t *) base;
+    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->input_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)
+            cairo_deflate_stream_deflate (stream, FALSE);
+    }
+
+    return _cairo_output_stream_get_status (stream->output);
+}
+
+static cairo_status_t
+_cairo_deflate_stream_close (cairo_output_stream_t *base)
+{
+    cairo_deflate_stream_t *stream = (cairo_deflate_stream_t *) base;
+
+    cairo_deflate_stream_deflate (stream, TRUE);
+    deflateEnd (&stream->zlib_stream);
+
+    return _cairo_output_stream_get_status (stream->output);
+}
+
+cairo_output_stream_t *
+_cairo_deflate_stream_create (cairo_output_stream_t *output)
+{
+    cairo_deflate_stream_t *stream;
+
+    if (output->status)
+       return _cairo_output_stream_create_in_error (output->status);
+
+    stream = malloc (sizeof (cairo_deflate_stream_t));
+    if (unlikely (stream == NULL)) {
+       _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+       return (cairo_output_stream_t *) &_cairo_output_stream_nil;
+    }
+
+    _cairo_output_stream_init (&stream->base,
+                              _cairo_deflate_stream_write,
+                              NULL,
+                              _cairo_deflate_stream_close);
+    stream->output = output;
+
+    stream->zlib_stream.zalloc = Z_NULL;
+    stream->zlib_stream.zfree  = Z_NULL;
+    stream->zlib_stream.opaque  = Z_NULL;
+
+    if (deflateInit (&stream->zlib_stream, Z_DEFAULT_COMPRESSION) != Z_OK) {
+       free (stream);
+       return (cairo_output_stream_t *) &_cairo_output_stream_nil;
+    }
+
+    stream->zlib_stream.next_in = stream->input_buf;
+    stream->zlib_stream.avail_in = 0;
+    stream->zlib_stream.next_out = stream->output_buf;
+    stream->zlib_stream.avail_out = BUFFER_SIZE;
+
+    return &stream->base;
+}
+
+#endif /* CAIRO_HAS_DEFLATE_STREAM */
diff --git a/src/cairo-deprecated.h b/src/cairo-deprecated.h
new file mode 100644 (file)
index 0000000..7a56aad
--- /dev/null
@@ -0,0 +1,123 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2006 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ *     Carl D. Worth <cworth@cworth.org>
+ */
+
+#ifndef CAIRO_DEPRECATED_H
+#define CAIRO_DEPRECATED_H
+
+#define CAIRO_FONT_TYPE_ATSUI CAIRO_FONT_TYPE_QUARTZ
+
+/* Obsolete functions. These definitions exist to coerce the compiler
+ * into providing a little bit of guidance with its error
+ * messages. The idea is to help users port their old code without
+ * having to dig through lots of documentation.
+ *
+ * The first set of REPLACED_BY functions is for functions whose names
+ * have just been changed. So fixing these up is mechanical, (and
+ * automated by means of the cairo/util/cairo-api-update script.
+ *
+ * The second set of DEPRECATED_BY functions is for functions where
+ * the replacement is used in a different way, (ie. different
+ * arguments, multiple functions instead of one, etc). Fixing these up
+ * will require a bit more work on the user's part, (and hopefully we
+ * can get cairo-api-update to find these and print some guiding
+ * information).
+ */
+#define cairo_current_font_extents   cairo_current_font_extents_REPLACED_BY_cairo_font_extents
+#define cairo_get_font_extents       cairo_get_font_extents_REPLACED_BY_cairo_font_extents
+#define cairo_current_operator       cairo_current_operator_REPLACED_BY_cairo_get_operator
+#define cairo_current_tolerance             cairo_current_tolerance_REPLACED_BY_cairo_get_tolerance
+#define cairo_current_point         cairo_current_point_REPLACED_BY_cairo_get_current_point
+#define cairo_current_fill_rule             cairo_current_fill_rule_REPLACED_BY_cairo_get_fill_rule
+#define cairo_current_line_width     cairo_current_line_width_REPLACED_BY_cairo_get_line_width
+#define cairo_current_line_cap       cairo_current_line_cap_REPLACED_BY_cairo_get_line_cap
+#define cairo_current_line_join      cairo_current_line_join_REPLACED_BY_cairo_get_line_join
+#define cairo_current_miter_limit    cairo_current_miter_limit_REPLACED_BY_cairo_get_miter_limit
+#define cairo_current_matrix         cairo_current_matrix_REPLACED_BY_cairo_get_matrix
+#define cairo_current_target_surface cairo_current_target_surface_REPLACED_BY_cairo_get_target
+#define cairo_get_status             cairo_get_status_REPLACED_BY_cairo_status
+#define cairo_concat_matrix             cairo_concat_matrix_REPLACED_BY_cairo_transform
+#define cairo_scale_font                 cairo_scale_font_REPLACED_BY_cairo_set_font_size
+#define cairo_select_font                cairo_select_font_REPLACED_BY_cairo_select_font_face
+#define cairo_transform_font             cairo_transform_font_REPLACED_BY_cairo_set_font_matrix
+#define cairo_transform_point           cairo_transform_point_REPLACED_BY_cairo_user_to_device
+#define cairo_transform_distance        cairo_transform_distance_REPLACED_BY_cairo_user_to_device_distance
+#define cairo_inverse_transform_point   cairo_inverse_transform_point_REPLACED_BY_cairo_device_to_user
+#define cairo_inverse_transform_distance cairo_inverse_transform_distance_REPLACED_BY_cairo_device_to_user_distance
+#define cairo_init_clip                         cairo_init_clip_REPLACED_BY_cairo_reset_clip
+#define cairo_surface_create_for_image  cairo_surface_create_for_image_REPLACED_BY_cairo_image_surface_create_for_data
+#define cairo_default_matrix            cairo_default_matrix_REPLACED_BY_cairo_identity_matrix
+#define cairo_matrix_set_affine                 cairo_matrix_set_affine_REPLACED_BY_cairo_matrix_init
+#define cairo_matrix_set_identity       cairo_matrix_set_identity_REPLACED_BY_cairo_matrix_init_identity
+#define cairo_pattern_add_color_stop    cairo_pattern_add_color_stop_REPLACED_BY_cairo_pattern_add_color_stop_rgba
+#define cairo_set_rgb_color             cairo_set_rgb_color_REPLACED_BY_cairo_set_source_rgb
+#define cairo_set_pattern               cairo_set_pattern_REPLACED_BY_cairo_set_source
+#define cairo_xlib_surface_create_for_pixmap_with_visual       cairo_xlib_surface_create_for_pixmap_with_visual_REPLACED_BY_cairo_xlib_surface_create
+#define cairo_xlib_surface_create_for_window_with_visual       cairo_xlib_surface_create_for_window_with_visual_REPLACED_BY_cairo_xlib_surface_create
+#define cairo_xcb_surface_create_for_pixmap_with_visual        cairo_xcb_surface_create_for_pixmap_with_visual_REPLACED_BY_cairo_xcb_surface_create
+#define cairo_xcb_surface_create_for_window_with_visual        cairo_xcb_surface_create_for_window_with_visual_REPLACED_BY_cairo_xcb_surface_create
+#define cairo_ps_surface_set_dpi       cairo_ps_surface_set_dpi_REPLACED_BY_cairo_surface_set_fallback_resolution
+#define cairo_pdf_surface_set_dpi      cairo_pdf_surface_set_dpi_REPLACED_BY_cairo_surface_set_fallback_resolution
+#define cairo_svg_surface_set_dpi      cairo_svg_surface_set_dpi_REPLACED_BY_cairo_surface_set_fallback_resolution
+#define cairo_atsui_font_face_create_for_atsu_font_id  cairo_atsui_font_face_create_for_atsu_font_id_REPLACED_BY_cairo_quartz_font_face_create_for_atsu_font_id
+
+#define cairo_current_path          cairo_current_path_DEPRECATED_BY_cairo_copy_path
+#define cairo_current_path_flat             cairo_current_path_flat_DEPRECATED_BY_cairo_copy_path_flat
+#define cairo_get_path              cairo_get_path_DEPRECATED_BY_cairo_copy_path
+#define cairo_get_path_flat         cairo_get_path_flat_DEPRECATED_BY_cairo_get_path_flat
+#define cairo_set_alpha                     cairo_set_alpha_DEPRECATED_BY_cairo_set_source_rgba_OR_cairo_paint_with_alpha
+#define cairo_show_surface          cairo_show_surface_DEPRECATED_BY_cairo_set_source_surface_AND_cairo_paint
+#define cairo_copy                  cairo_copy_DEPRECATED_BY_cairo_create_AND_MANY_INDIVIDUAL_FUNCTIONS
+#define cairo_surface_set_repeat       cairo_surface_set_repeat_DEPRECATED_BY_cairo_pattern_set_extend
+#define cairo_surface_set_matrix       cairo_surface_set_matrix_DEPRECATED_BY_cairo_pattern_set_matrix
+#define cairo_surface_get_matrix       cairo_surface_get_matrix_DEPRECATED_BY_cairo_pattern_get_matrix
+#define cairo_surface_set_filter       cairo_surface_set_filter_DEPRECATED_BY_cairo_pattern_set_filter
+#define cairo_surface_get_filter       cairo_surface_get_filter_DEPRECATED_BY_cairo_pattern_get_filter
+#define cairo_matrix_create            cairo_matrix_create_DEPRECATED_BY_cairo_matrix_t
+#define cairo_matrix_destroy           cairo_matrix_destroy_DEPRECATED_BY_cairo_matrix_t
+#define cairo_matrix_copy              cairo_matrix_copy_DEPRECATED_BY_cairo_matrix_t
+#define cairo_matrix_get_affine                cairo_matrix_get_affine_DEPRECATED_BY_cairo_matrix_t
+#define cairo_set_target_surface       cairo_set_target_surface_DEPRECATED_BY_cairo_create
+#define cairo_set_target_image         cairo_set_target_image_DEPRECATED_BY_cairo_image_surface_create_for_data
+#define cairo_set_target_pdf           cairo_set_target_pdf_DEPRECATED_BY_cairo_pdf_surface_create
+#define cairo_set_target_png           cairo_set_target_png_DEPRECATED_BY_cairo_surface_write_to_png
+#define cairo_set_target_ps            cairo_set_target_ps_DEPRECATED_BY_cairo_ps_surface_create
+#define cairo_set_target_quartz                cairo_set_target_quartz_DEPRECATED_BY_cairo_quartz_surface_create
+#define cairo_set_target_win32         cairo_set_target_win32_DEPRECATED_BY_cairo_win32_surface_create
+#define cairo_set_target_xcb           cairo_set_target_xcb_DEPRECATED_BY_cairo_xcb_surface_create
+#define cairo_set_target_drawable      cairo_set_target_drawable_DEPRECATED_BY_cairo_xlib_surface_create
+#define cairo_get_status_string                cairo_get_status_string_DEPRECATED_BY_cairo_status_AND_cairo_status_to_string
+#define cairo_status_string            cairo_status_string_DEPRECATED_BY_cairo_status_AND_cairo_status_to_string
+
+#endif /* CAIRO_DEPRECATED_H */
diff --git a/src/cairo-device-private.h b/src/cairo-device-private.h
new file mode 100644 (file)
index 0000000..6eb44f3
--- /dev/null
@@ -0,0 +1,86 @@
+/* Cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Intel Corporation.
+ *
+ * Contributors(s):
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#ifndef _CAIRO_DEVICE_PRIVATE_H_
+#define _CAIRO_DEVICE_PRIVATE_H_
+
+#include "cairo-compiler-private.h"
+#include "cairo-mutex-private.h"
+#include "cairo-reference-count-private.h"
+#include "cairo-types-private.h"
+
+struct _cairo_device {
+    cairo_reference_count_t ref_count;
+    cairo_status_t status;
+    cairo_user_data_array_t user_data;
+
+    const cairo_device_backend_t *backend;
+
+    cairo_recursive_mutex_t mutex;
+    unsigned mutex_depth;
+
+    cairo_bool_t finished;
+};
+
+struct _cairo_device_backend {
+    cairo_device_type_t type;
+
+    void (*lock) (void *device);
+    void (*unlock) (void *device);
+
+    cairo_warn cairo_status_t (*flush) (void *device);
+    void (*finish) (void *device);
+    void (*destroy) (void *device);
+};
+
+cairo_private cairo_device_t *
+_cairo_device_create_in_error (cairo_status_t status);
+
+cairo_private void
+_cairo_device_init (cairo_device_t *device,
+                   const cairo_device_backend_t *backend);
+
+cairo_private cairo_status_t
+_cairo_device_set_error (cairo_device_t *device,
+                        cairo_status_t error);
+
+slim_hidden_proto_no_warn (cairo_device_reference);
+slim_hidden_proto (cairo_device_acquire);
+slim_hidden_proto (cairo_device_release);
+slim_hidden_proto (cairo_device_flush);
+slim_hidden_proto (cairo_device_finish);
+slim_hidden_proto (cairo_device_destroy);
+
+#endif /* _CAIRO_DEVICE_PRIVATE_H_ */
diff --git a/src/cairo-device.c b/src/cairo-device.c
new file mode 100644 (file)
index 0000000..fd1ec71
--- /dev/null
@@ -0,0 +1,540 @@
+/* Cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Intel Corporation.
+ *
+ * Contributors(s):
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+#include "cairo-device-private.h"
+#include "cairo-error-private.h"
+
+/**
+ * SECTION:cairo-device
+ * @Title: cairo_device_t
+ * @Short_Description: interface to underlying rendering system
+ * @See_Also: #cairo_surface_t
+ *
+ * Devices are the abstraction Cairo employs for the rendering system
+ * used by a #cairo_surface_t. You can get the device of a surface using
+ * cairo_surface_get_device().
+ *
+ * Devices are created using custom functions specific to the rendering
+ * system you want to use. See the documentation for the surface types
+ * for those functions.
+ *
+ * An important function that devices fulfill is sharing access to the
+ * rendering system between Cairo and your application. If you want to
+ * access a device directly that you used to draw to with Cairo, you must
+ * first call cairo_device_flush() to ensure that Cairo finishes all
+ * operations on the device and resets it to a clean state.
+ *
+ * Cairo also provides the functions cairo_device_acquire() and
+ * cairo_device_release() to synchronize access to the rendering system
+ * in a multithreaded environment. This is done internally, but can also
+ * be used by applications.
+ *
+ * Putting this all together, a function that works with devices should
+ * look something like this:
+ * <informalexample><programlisting>
+ * void
+ * my_device_modifying_function (cairo_device_t *device)
+ * {
+ *   cairo_status_t status;
+ *
+ *   // Ensure the device is properly reset
+ *   cairo_device_flush (device);
+ *   // Try to acquire the device
+ *   status = cairo_device_acquire (device);
+ *   if (status != CAIRO_STATUS_SUCCESS) {
+ *     printf ("Failed to acquire the device: %s\n", cairo_status_to_string (status));
+ *     return;
+ *   }
+ *
+ *   // Do the custom operations on the device here.
+ *   // But do not call any Cairo functions that might acquire devices.
+ *   
+ *   // Release the device when done.
+ *   cairo_device_release (device);
+ * }
+ * </programlisting></informalexample>
+ *
+ * <note><para>Please refer to the documentation of each backend for
+ * additional usage requirements, guarantees provided, and
+ * interactions with existing surface API of the device functions for
+ * surfaces of that type.
+ * </para></note>
+ */
+
+static const cairo_device_t _nil_device = {
+    CAIRO_REFERENCE_COUNT_INVALID,
+    CAIRO_STATUS_NO_MEMORY,
+};
+
+static const cairo_device_t _mismatch_device = {
+    CAIRO_REFERENCE_COUNT_INVALID,
+    CAIRO_STATUS_DEVICE_TYPE_MISMATCH,
+};
+
+static const cairo_device_t _invalid_device = {
+    CAIRO_REFERENCE_COUNT_INVALID,
+    CAIRO_STATUS_DEVICE_ERROR,
+};
+
+cairo_device_t *
+_cairo_device_create_in_error (cairo_status_t status)
+{
+    switch (status) {
+    case CAIRO_STATUS_NO_MEMORY:
+       return (cairo_device_t *) &_nil_device;
+    case CAIRO_STATUS_DEVICE_ERROR:
+       return (cairo_device_t *) &_invalid_device;
+    case CAIRO_STATUS_DEVICE_TYPE_MISMATCH:
+       return (cairo_device_t *) &_mismatch_device;
+
+    case CAIRO_STATUS_SUCCESS:
+    case CAIRO_STATUS_LAST_STATUS:
+       ASSERT_NOT_REACHED;
+       /* fall-through */
+    case CAIRO_STATUS_SURFACE_TYPE_MISMATCH:
+    case CAIRO_STATUS_INVALID_STATUS:
+    case CAIRO_STATUS_INVALID_FORMAT:
+    case CAIRO_STATUS_INVALID_VISUAL:
+    case CAIRO_STATUS_READ_ERROR:
+    case CAIRO_STATUS_WRITE_ERROR:
+    case CAIRO_STATUS_FILE_NOT_FOUND:
+    case CAIRO_STATUS_TEMP_FILE_ERROR:
+    case CAIRO_STATUS_INVALID_STRIDE:
+    case CAIRO_STATUS_INVALID_SIZE:
+    case CAIRO_STATUS_INVALID_RESTORE:
+    case CAIRO_STATUS_INVALID_POP_GROUP:
+    case CAIRO_STATUS_NO_CURRENT_POINT:
+    case CAIRO_STATUS_INVALID_MATRIX:
+    case CAIRO_STATUS_NULL_POINTER:
+    case CAIRO_STATUS_INVALID_STRING:
+    case CAIRO_STATUS_INVALID_PATH_DATA:
+    case CAIRO_STATUS_SURFACE_FINISHED:
+    case CAIRO_STATUS_PATTERN_TYPE_MISMATCH:
+    case CAIRO_STATUS_INVALID_DASH:
+    case CAIRO_STATUS_INVALID_DSC_COMMENT:
+    case CAIRO_STATUS_INVALID_INDEX:
+    case CAIRO_STATUS_CLIP_NOT_REPRESENTABLE:
+    case CAIRO_STATUS_FONT_TYPE_MISMATCH:
+    case CAIRO_STATUS_USER_FONT_IMMUTABLE:
+    case CAIRO_STATUS_USER_FONT_ERROR:
+    case CAIRO_STATUS_NEGATIVE_COUNT:
+    case CAIRO_STATUS_INVALID_CLUSTERS:
+    case CAIRO_STATUS_INVALID_SLANT:
+    case CAIRO_STATUS_INVALID_WEIGHT:
+    case CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED:
+    case CAIRO_STATUS_INVALID_CONTENT:
+    case CAIRO_STATUS_INVALID_MESH_CONSTRUCTION:
+    case CAIRO_STATUS_DEVICE_FINISHED:
+    default:
+       _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+       return (cairo_device_t *) &_nil_device;
+    }
+}
+
+void
+_cairo_device_init (cairo_device_t *device,
+                   const cairo_device_backend_t *backend)
+{
+    CAIRO_REFERENCE_COUNT_INIT (&device->ref_count, 1);
+    device->status = CAIRO_STATUS_SUCCESS;
+
+    device->backend = backend;
+
+    CAIRO_RECURSIVE_MUTEX_INIT (device->mutex);
+    device->mutex_depth = 0;
+
+    device->finished = FALSE;
+
+    _cairo_user_data_array_init (&device->user_data);
+}
+
+/**
+ * cairo_device_reference:
+ * @device: a #cairo_device_t
+ *
+ * Increases the reference count on @device by one. This prevents
+ * @device from being destroyed until a matching call to
+ * cairo_device_destroy() is made.
+ *
+ * The number of references to a #cairo_device_t can be get using
+ * cairo_device_get_reference_count().
+ *
+ * Return value: the referenced #cairo_device_t.
+ *
+ * Since: 1.10
+ **/
+cairo_device_t *
+cairo_device_reference (cairo_device_t *device)
+{
+    if (device == NULL ||
+       CAIRO_REFERENCE_COUNT_IS_INVALID (&device->ref_count))
+    {
+       return device;
+    }
+
+    assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&device->ref_count));
+    _cairo_reference_count_inc (&device->ref_count);
+
+    return device;
+}
+slim_hidden_def (cairo_device_reference);
+
+/**
+ * cairo_device_status:
+ * @device: a #cairo_device_t
+ *
+ * Checks whether an error has previously occurred for this
+ * device.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS on success or an error code if
+ *               the device is in an error state.
+ *
+ * Since: 1.10
+ **/
+cairo_status_t
+cairo_device_status (cairo_device_t *device)
+{
+    if (device == NULL)
+       return CAIRO_STATUS_NULL_POINTER;
+
+    return device->status;
+}
+
+/**
+ * cairo_device_flush:
+ * @device: a #cairo_device_t
+ *
+ * Finish any pending operations for the device and also restore any
+ * temporary modifications cairo has made to the device's state.
+ * This function must be called before switching from using the 
+ * device with Cairo to operating on it directly with native APIs.
+ * If the device doesn't support direct access, then this function
+ * does nothing.
+ *
+ * This function may acquire devices.
+ *
+ * Since: 1.10
+ **/
+void
+cairo_device_flush (cairo_device_t *device)
+{
+    cairo_status_t status;
+
+    if (device == NULL || device->status)
+       return;
+
+    if (device->finished)
+       return;
+
+    if (device->backend->flush != NULL) {
+       status = device->backend->flush (device);
+       if (unlikely (status))
+           status = _cairo_device_set_error (device, status);
+    }
+}
+slim_hidden_def (cairo_device_flush);
+
+/**
+ * cairo_device_finish:
+ * @device: the #cairo_device_t to finish
+ *
+ * This function finishes the device and drops all references to
+ * external resources. All surfaces, fonts and other objects created
+ * for this @device will be finished, too.
+ * Further operations on the @device will not affect the @device but
+ * will instead trigger a %CAIRO_STATUS_DEVICE_FINISHED error.
+ *
+ * When the last call to cairo_device_destroy() decreases the
+ * reference count to zero, cairo will call cairo_device_finish() if
+ * it hasn't been called already, before freeing the resources
+ * associated with the device.
+ *
+ * This function may acquire devices.
+ *
+ * Since: 1.10
+ **/
+void
+cairo_device_finish (cairo_device_t *device)
+{
+    if (device == NULL ||
+       CAIRO_REFERENCE_COUNT_IS_INVALID (&device->ref_count))
+    {
+       return;
+    }
+
+    if (device->finished)
+       return;
+
+    cairo_device_flush (device);
+
+    if (device->backend->finish != NULL)
+       device->backend->finish (device);
+
+    /* We only finish the device after the backend's callback returns because
+     * the device might still be needed during the callback
+     * (e.g. for cairo_device_acquire ()).
+     */
+    device->finished = TRUE;
+}
+slim_hidden_def (cairo_device_finish);
+
+/**
+ * cairo_device_destroy:
+ * @device: a #cairo_device_t
+ *
+ * Decreases the reference count on @device by one. If the result is
+ * zero, then @device and all associated resources are freed.  See
+ * cairo_device_reference().
+ *
+ * This function may acquire devices if the last reference was dropped.
+ *
+ * Since: 1.10
+ **/
+void
+cairo_device_destroy (cairo_device_t *device)
+{
+    cairo_user_data_array_t user_data;
+
+    if (device == NULL ||
+       CAIRO_REFERENCE_COUNT_IS_INVALID (&device->ref_count))
+    {
+       return;
+    }
+
+    assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&device->ref_count));
+    if (! _cairo_reference_count_dec_and_test (&device->ref_count))
+       return;
+
+    cairo_device_finish (device);
+
+    assert (device->mutex_depth == 0);
+    CAIRO_MUTEX_FINI (device->mutex);
+
+    user_data = device->user_data;
+
+    device->backend->destroy (device);
+
+    _cairo_user_data_array_fini (&user_data);
+
+}
+slim_hidden_def (cairo_device_destroy);
+
+/**
+ * cairo_device_get_type:
+ * @device: a #cairo_device_t
+ *
+ * This function returns the type of the device. See #cairo_device_type_t
+ * for available types.
+ *
+ * Return value: The type of @device.
+ *
+ * Since: 1.10
+ **/
+cairo_device_type_t
+cairo_device_get_type (cairo_device_t *device)
+{
+    if (device == NULL ||
+       CAIRO_REFERENCE_COUNT_IS_INVALID (&device->ref_count))
+    {
+       return CAIRO_DEVICE_TYPE_INVALID;
+    }
+
+    return device->backend->type;
+}
+
+/**
+ * cairo_device_acquire:
+ * @device: a #cairo_device_t
+ *
+ * Acquires the @device for the current thread. This function will block
+ * until no other thread has acquired the device.
+ *
+ * If the return value is %CAIRO_STATUS_SUCCESS, you successfully acquired the
+ * device. From now on your thread owns the device and no other thread will be
+ * able to acquire it until a matching call to cairo_device_release(). It is
+ * allowed to recursively acquire the device multiple times from the same
+ * thread.
+ *
+ * <note><para>You must never acquire two different devices at the same time
+ * unless this is explicitly allowed. Otherwise the possibility of deadlocks
+ * exist.
+ *
+ * As various Cairo functions can acquire devices when called, these functions
+ * may also cause deadlocks when you call them with an acquired device. So you
+ * must not have a device acquired when calling them. These functions are
+ * marked in the documentation.
+ * </para></note>
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS on success or an error code if
+ *               the device is in an error state and could not be
+ *               acquired. After a successful call to cairo_device_acquire(),
+ *               a matching call to cairo_device_release() is required.
+ *
+ * Since: 1.10
+ **/
+cairo_status_t
+cairo_device_acquire (cairo_device_t *device)
+{
+    if (device == NULL)
+       return CAIRO_STATUS_SUCCESS;
+
+    if (unlikely (device->status))
+       return device->status;
+
+    if (unlikely (device->finished))
+       return _cairo_device_set_error (device, CAIRO_STATUS_DEVICE_FINISHED);
+
+    CAIRO_MUTEX_LOCK (device->mutex);
+    if (device->mutex_depth++ == 0) {
+       if (device->backend->lock != NULL)
+           device->backend->lock (device);
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+slim_hidden_def (cairo_device_acquire);
+
+/**
+ * cairo_device_release:
+ * @device: a #cairo_device_t
+ *
+ * Releases a @device previously acquired using cairo_device_acquire(). See
+ * that function for details.
+ *
+ * Since: 1.10
+ **/
+void
+cairo_device_release (cairo_device_t *device)
+{
+    if (device == NULL)
+       return;
+
+    assert (device->mutex_depth > 0);
+
+    if (--device->mutex_depth == 0) {
+       if (device->backend->unlock != NULL)
+           device->backend->unlock (device);
+    }
+
+    CAIRO_MUTEX_UNLOCK (device->mutex);
+}
+slim_hidden_def (cairo_device_release);
+
+cairo_status_t
+_cairo_device_set_error (cairo_device_t *device,
+                        cairo_status_t  status)
+{
+    if (status == CAIRO_STATUS_SUCCESS)
+        return CAIRO_STATUS_SUCCESS;
+
+    _cairo_status_set_error (&device->status, status);
+
+    return _cairo_error (status);
+}
+
+/**
+ * cairo_device_get_reference_count:
+ * @device: a #cairo_device_t
+ *
+ * Returns the current reference count of @device.
+ *
+ * Return value: the current reference count of @device.  If the
+ * object is a nil object, 0 will be returned.
+ *
+ * Since: 1.10
+ **/
+unsigned int
+cairo_device_get_reference_count (cairo_device_t *device)
+{
+    if (device == NULL ||
+       CAIRO_REFERENCE_COUNT_IS_INVALID (&device->ref_count))
+       return 0;
+
+    return CAIRO_REFERENCE_COUNT_GET_VALUE (&device->ref_count);
+}
+
+/**
+ * cairo_device_get_user_data:
+ * @device: a #cairo_device_t
+ * @key: the address of the #cairo_user_data_key_t the user data was
+ * attached to
+ *
+ * Return user data previously attached to @device using the
+ * specified key.  If no user data has been attached with the given
+ * key this function returns %NULL.
+ *
+ * Return value: the user data previously attached or %NULL.
+ *
+ * Since: 1.10
+ **/
+void *
+cairo_device_get_user_data (cairo_device_t              *device,
+                           const cairo_user_data_key_t *key)
+{
+    return _cairo_user_data_array_get_data (&device->user_data,
+                                           key);
+}
+
+/**
+ * cairo_device_set_user_data:
+ * @device: a #cairo_device_t
+ * @key: the address of a #cairo_user_data_key_t to attach the user data to
+ * @user_data: the user data to attach to the #cairo_device_t
+ * @destroy: a #cairo_destroy_func_t which will be called when the
+ * #cairo_t is destroyed or when new user data is attached using the
+ * same key.
+ *
+ * Attach user data to @device.  To remove user data from a surface,
+ * call this function with the key that was used to set it and %NULL
+ * for @data.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY if a
+ * slot could not be allocated for the user data.
+ *
+ * Since: 1.10
+ **/
+cairo_status_t
+cairo_device_set_user_data (cairo_device_t              *device,
+                           const cairo_user_data_key_t *key,
+                           void                         *user_data,
+                           cairo_destroy_func_t          destroy)
+{
+    if (CAIRO_REFERENCE_COUNT_IS_INVALID (&device->ref_count))
+       return device->status;
+
+    return _cairo_user_data_array_set_data (&device->user_data,
+                                           key, user_data, destroy);
+}
diff --git a/src/cairo-directfb-surface.c b/src/cairo-directfb-surface.c
new file mode 100644 (file)
index 0000000..46c0960
--- /dev/null
@@ -0,0 +1,1970 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2003 University of Southern California
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *    Michael Emmel <mike.emmel@gmail.com>
+ *    Claudio Ciccani <klan@users.sf.net>
+ */
+
+#include "cairoint.h"
+#include "cairo-directfb.h"
+
+#include "cairo-clip-private.h"
+#include "cairo-default-context-private.h"
+#include "cairo-error-private.h"
+#include "cairo-pattern-private.h"
+
+#include <pixman.h>
+
+#include <directfb.h>
+#include <direct/types.h>
+#include <direct/debug.h>
+#include <direct/memcpy.h>
+#include <direct/util.h>
+
+/*
+ * Rectangle works fine.
+ * Bugs 361377, 359553, 359243 in Gnome BTS are caused
+ * by GDK/DirectFB, not by Cairo/DirectFB.
+ */
+#define DFB_RECTANGLES 1
+
+/*
+ * Composite works fine.
+ */
+#define DFB_COMPOSITE 1
+
+/*
+ * CompositeTrapezoids works (without antialiasing).
+ */
+#define DFB_COMPOSITE_TRAPEZOIDS 1
+
+/*
+ * ShowGlyphs works fine.
+ */
+#define DFB_SHOW_GLYPHS 1
+
+#define PIXMAN_invalid (pixman_format_code_t) 0
+
+
+D_DEBUG_DOMAIN (CairoDFB_Acquire, "CairoDFB/Acquire", "Cairo DirectFB Acquire");
+D_DEBUG_DOMAIN (CairoDFB_Clip,    "CairoDFB/Clip",    "Cairo DirectFB Clipping");
+D_DEBUG_DOMAIN (CairoDFB_Font,    "CairoDFB/Font",    "Cairo DirectFB Font Rendering");
+D_DEBUG_DOMAIN (CairoDFB_Render,  "CairoDFB/Render",  "Cairo DirectFB Rendering");
+D_DEBUG_DOMAIN (CairoDFB_Surface, "CairoDFB/Surface", "Cairo DirectFB Surface");
+
+/*****************************************************************************/
+
+typedef struct _cairo_directfb_surface {
+    cairo_surface_t    base;
+
+    pixman_format_code_t       pixman_format;
+    cairo_bool_t               supported_destination;
+
+    IDirectFB                  *dfb;
+    IDirectFBSurface           *dfbsurface;
+    IDirectFBSurface           *tmpsurface;
+    pixman_format_code_t        tmpformat;
+
+    int                  width;
+    int                  height;
+
+    unsigned             local : 1;
+    unsigned             blit_premultiplied : 1;
+} cairo_directfb_surface_t;
+
+
+typedef struct _cairo_directfb_font_cache {
+    IDirectFB           *dfb;
+    IDirectFBSurface    *dfbsurface;
+
+    int                  width;
+    int                  height;
+
+    /* coordinates within the surface
+     * of the last loaded glyph */
+    int                  x;
+    int                  y;
+} cairo_directfb_font_cache_t;
+
+static cairo_surface_backend_t _cairo_directfb_surface_backend;
+
+/*****************************************************************************/
+
+static int _directfb_argb_font = 0;
+
+/*****************************************************************************/
+
+#define RUN_CLIPPED(surface, clip_region, clip, func) {\
+    if ((clip_region) != NULL) {\
+       int n_clips = cairo_region_num_rectangles (clip_region), n; \
+        for (n = 0; n < n_clips; n++) {\
+            if (clip) {\
+                DFBRegion  reg, *cli = (clip); \
+               cairo_rectangle_int_t rect; \
+               cairo_region_get_rectangle (clip_region, n, &rect); \
+               reg.x1 = rect.x; \
+               reg.y1 = rect.y; \
+               reg.x2 = rect.x + rect.width - 1; \
+               reg.y2 = rect.y + rect.height - 1; \
+                if (reg.x2 < cli->x1 || reg.y2 < cli->y1 ||\
+                    reg.x1 > cli->x2 || reg.y1 > cli->y2)\
+                    continue;\
+                if (reg.x1 < cli->x1)\
+                    reg.x1 = cli->x1;\
+                if (reg.y1 < cli->y1)\
+                    reg.y1 = cli->y1;\
+                if (reg.x2 > cli->x2)\
+                    reg.x2 = cli->x2;\
+                if (reg.y2 > cli->y2)\
+                    reg.y2 = cli->y2;\
+                (surface)->dfbsurface->SetClip ((surface)->dfbsurface, &reg);\
+            } else {\
+               DFBRegion reg; \
+               cairo_rectangle_int_t rect; \
+               cairo_region_get_rectangle (clip_region, n, &rect); \
+               reg.x1 = rect.x; \
+               reg.y1 = rect.y; \
+               reg.x2 = rect.x + rect.width - 1; \
+               reg.y2 = rect.y + rect.height - 1; \
+                (surface)->dfbsurface->SetClip ((surface)->dfbsurface, &reg); \
+            }\
+            func;\
+        }\
+    } else {\
+        (surface)->dfbsurface->SetClip ((surface)->dfbsurface, clip);\
+        func;\
+    }\
+}
+
+#define TRANSFORM_POINT2X(m, x, y, ret_x, ret_y) do { \
+    double _x = (x); \
+    double _y = (y); \
+    (ret_x) = (_x * (m).xx + (m).x0); \
+    (ret_y) = (_y * (m).yy + (m).y0); \
+} while (0)
+
+#define TRANSFORM_POINT3X(m, x, y, ret_x, ret_y) do { \
+    double _x = (x); \
+    double _y = (y); \
+    (ret_x) = (_x * (m).xx + _y * (m).xy + (m).x0); \
+    (ret_y) = (_x * (m).yx + _y * (m).yy + (m).y0); \
+} while (0)
+
+/* XXX: A1 has a different bits ordering in cairo.
+ *      Probably we should drop it.
+ */
+
+static cairo_content_t
+_directfb_format_to_content (DFBSurfacePixelFormat format)
+{
+    if (DFB_PIXELFORMAT_HAS_ALPHA (format)) {
+        if (DFB_COLOR_BITS_PER_PIXEL (format))
+            return CAIRO_CONTENT_COLOR_ALPHA;
+
+        return CAIRO_CONTENT_ALPHA;
+    }
+
+    return CAIRO_CONTENT_COLOR;
+}
+
+static inline DFBSurfacePixelFormat
+_cairo_to_directfb_format (cairo_format_t format)
+{
+    switch (format) {
+    case CAIRO_FORMAT_RGB24:
+       return DSPF_RGB32;
+    case CAIRO_FORMAT_ARGB32:
+       return DSPF_ARGB;
+    case CAIRO_FORMAT_A8:
+       return DSPF_A8;
+    case CAIRO_FORMAT_A1:
+       return DSPF_A1;
+    default:
+       break;
+    }
+
+    return -1;
+}
+
+static inline pixman_format_code_t
+_directfb_to_pixman_format (DFBSurfacePixelFormat format)
+{
+    switch (format) {
+    case DSPF_UNKNOWN: return PIXMAN_invalid;
+    case DSPF_ARGB1555: return PIXMAN_a1r5g5b5;
+    case DSPF_RGB16: return PIXMAN_r5g6b5;
+    case DSPF_RGB24: return PIXMAN_r8g8b8;
+    case DSPF_RGB32: return PIXMAN_x8r8g8b8;
+    case DSPF_ARGB: return PIXMAN_a8r8g8b8;
+    case DSPF_A8: return PIXMAN_a8;
+    case DSPF_YUY2: return PIXMAN_yuy2;
+    case DSPF_RGB332: return PIXMAN_r3g3b2;
+    case DSPF_UYVY: return PIXMAN_invalid;
+    case DSPF_I420: return PIXMAN_invalid;
+    case DSPF_YV12: return PIXMAN_yv12;
+    case DSPF_LUT8: return PIXMAN_invalid;
+    case DSPF_ALUT44: return PIXMAN_invalid;
+    case DSPF_AiRGB: return PIXMAN_invalid;
+    case DSPF_A1: return PIXMAN_a1; /* bit reversed, oops */
+    case DSPF_NV12: return PIXMAN_invalid;
+    case DSPF_NV16: return PIXMAN_invalid;
+    case DSPF_ARGB2554: return PIXMAN_invalid;
+    case DSPF_ARGB4444: return PIXMAN_a4r4g4b4;
+    case DSPF_NV21: return PIXMAN_invalid;
+    case DSPF_AYUV: return PIXMAN_invalid;
+    case DSPF_A4: return PIXMAN_a4;
+    case DSPF_ARGB1666: return PIXMAN_invalid;
+    case DSPF_ARGB6666: return PIXMAN_invalid;
+    case DSPF_RGB18: return PIXMAN_invalid;
+    case DSPF_LUT2: return PIXMAN_invalid;
+    case DSPF_RGB444: return PIXMAN_x4r4g4b4;
+    case DSPF_RGB555: return PIXMAN_x1r5g5b5;
+#if DFB_NUM_PIXELFORMATS >= 29
+    case DSPF_BGR555: return PIXMAN_x1b5g5r5;
+#endif
+    }
+    return PIXMAN_invalid;
+}
+
+static inline DFBSurfacePixelFormat
+_directfb_from_pixman_format (pixman_format_code_t format)
+{
+    switch ((int) format) {
+    case PIXMAN_a1r5g5b5: return DSPF_ARGB1555;
+    case PIXMAN_r5g6b5: return DSPF_RGB16;
+    case PIXMAN_r8g8b8: return DSPF_RGB24;
+    case PIXMAN_x8r8g8b8: return DSPF_RGB32;
+    case PIXMAN_a8r8g8b8: return DSPF_ARGB;
+    case PIXMAN_a8: return DSPF_A8;
+    case PIXMAN_yuy2: return DSPF_YUY2;
+    case PIXMAN_r3g3b2: return DSPF_RGB332;
+    case PIXMAN_yv12: return DSPF_YV12;
+    case PIXMAN_a1: return DSPF_A1; /* bit reversed, oops */
+    case PIXMAN_a4r4g4b4: return DSPF_ARGB4444;
+    case PIXMAN_a4: return DSPF_A4;
+    case PIXMAN_x4r4g4b4: return DSPF_RGB444;
+    case PIXMAN_x1r5g5b5: return DSPF_RGB555;
+#if DFB_NUM_PIXELFORMATS >= 29
+    case PIXMAN_x1b5g5r5: return DSPF_BGR555;
+#endif
+    default: return 0;
+    }
+}
+
+static cairo_bool_t
+_directfb_get_operator (cairo_operator_t         operator,
+                        DFBSurfaceBlendFunction *ret_srcblend,
+                        DFBSurfaceBlendFunction *ret_dstblend)
+{
+    DFBSurfaceBlendFunction srcblend = DSBF_ONE;
+    DFBSurfaceBlendFunction dstblend = DSBF_ZERO;
+
+    switch (operator) {
+    case CAIRO_OPERATOR_CLEAR:
+       srcblend = DSBF_ZERO;
+       dstblend = DSBF_ZERO;
+       break;
+    case CAIRO_OPERATOR_SOURCE:
+       srcblend = DSBF_ONE;
+       dstblend = DSBF_ZERO;
+       break;
+    case CAIRO_OPERATOR_OVER:
+       srcblend = DSBF_ONE;
+       dstblend = DSBF_INVSRCALPHA;
+       break;
+    case CAIRO_OPERATOR_IN:
+       srcblend = DSBF_DESTALPHA;
+       dstblend = DSBF_ZERO;
+       break;
+    case CAIRO_OPERATOR_OUT:
+       srcblend = DSBF_INVDESTALPHA;
+       dstblend = DSBF_ZERO;
+       break;
+    case CAIRO_OPERATOR_ATOP:
+       srcblend = DSBF_DESTALPHA;
+       dstblend = DSBF_INVSRCALPHA;
+       break;
+    case CAIRO_OPERATOR_DEST:
+       srcblend = DSBF_ZERO;
+       dstblend = DSBF_ONE;
+       break;
+    case CAIRO_OPERATOR_DEST_OVER:
+       srcblend = DSBF_INVDESTALPHA;
+       dstblend = DSBF_ONE;
+       break;
+    case CAIRO_OPERATOR_DEST_IN:
+       srcblend = DSBF_ZERO;
+       dstblend = DSBF_SRCALPHA;
+       break;
+    case CAIRO_OPERATOR_DEST_OUT:
+       srcblend = DSBF_ZERO;
+       dstblend = DSBF_INVSRCALPHA;
+       break;
+    case CAIRO_OPERATOR_DEST_ATOP:
+       srcblend = DSBF_INVDESTALPHA;
+       dstblend = DSBF_SRCALPHA;
+       break;
+    case CAIRO_OPERATOR_XOR:
+       srcblend = DSBF_INVDESTALPHA;
+       dstblend = DSBF_INVSRCALPHA;
+       break;
+    case CAIRO_OPERATOR_ADD:
+       srcblend = DSBF_ONE;
+       dstblend = DSBF_ONE;
+       break;
+    case CAIRO_OPERATOR_SATURATE:
+       /* XXX This does not work. */
+#if 0
+       srcblend = DSBF_SRCALPHASAT;
+       dstblend = DSBF_ONE;
+       break;
+#endif
+    case CAIRO_OPERATOR_MULTIPLY:
+    case CAIRO_OPERATOR_SCREEN:
+    case CAIRO_OPERATOR_OVERLAY:
+    case CAIRO_OPERATOR_DARKEN:
+    case CAIRO_OPERATOR_LIGHTEN:
+    case CAIRO_OPERATOR_COLOR_DODGE:
+    case CAIRO_OPERATOR_COLOR_BURN:
+    case CAIRO_OPERATOR_HARD_LIGHT:
+    case CAIRO_OPERATOR_SOFT_LIGHT:
+    case CAIRO_OPERATOR_DIFFERENCE:
+    case CAIRO_OPERATOR_EXCLUSION:
+    case CAIRO_OPERATOR_HSL_HUE:
+    case CAIRO_OPERATOR_HSL_SATURATION:
+    case CAIRO_OPERATOR_HSL_COLOR:
+    case CAIRO_OPERATOR_HSL_LUMINOSITY:
+    default:
+       return FALSE;
+    }
+
+    *ret_srcblend = srcblend;
+    *ret_dstblend = dstblend;
+
+    return TRUE;
+}
+
+static cairo_status_t
+_directfb_buffer_surface_create (IDirectFB             *dfb,
+                                 DFBSurfacePixelFormat  format,
+                                 int                    width,
+                                 int                    height,
+                                IDirectFBSurface     **out)
+{
+    IDirectFBSurface      *buffer;
+    DFBSurfaceDescription  dsc;
+    DFBResult              ret;
+
+    dsc.flags       = DSDESC_WIDTH | DSDESC_HEIGHT | DSDESC_PIXELFORMAT;
+    dsc.caps        = DSCAPS_PREMULTIPLIED;
+    dsc.width       = width;
+    dsc.height      = height;
+    dsc.pixelformat = format;
+
+    ret = dfb->CreateSurface (dfb, &dsc, &buffer);
+    if (ret) {
+        DirectFBError ("IDirectFB::CreateSurface()", ret);
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+    }
+
+    *out = buffer;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_directfb_acquire_surface (cairo_directfb_surface_t  *surface,
+                           cairo_rectangle_int_t     *intrest_rec,
+                           cairo_image_surface_t    **image_out,
+                           cairo_rectangle_int_t     *image_rect_out,
+                           void                     **image_extra,
+                           DFBSurfaceLockFlags        lock_flags)
+{
+    IDirectFBSurface *buffer = NULL;
+    DFBRectangle source_rect;
+    cairo_surface_t *image;
+    pixman_image_t *pixman_image;
+    pixman_format_code_t pixman_format;
+    cairo_status_t status;
+    void *data;
+    int   pitch;
+
+    if (surface->pixman_format == PIXMAN_invalid) {
+        if (intrest_rec != NULL) {
+            source_rect.x = intrest_rec->x;
+            source_rect.y = intrest_rec->y;
+            source_rect.w = intrest_rec->width;
+            source_rect.h = intrest_rec->height;
+        } else {
+            source_rect.x = 0;
+            source_rect.y = 0;
+            surface->dfbsurface->GetSize (surface->dfbsurface,
+                                          &source_rect.w, &source_rect.h);
+        }
+
+        if (surface->tmpsurface != NULL) {
+            int w, h;
+
+            surface->tmpsurface->GetSize (surface->tmpsurface, &w, &h);
+            if (w < source_rect.w || h < source_rect.h) {
+                surface->tmpsurface->Release (surface->tmpsurface);
+                surface->tmpsurface = NULL;
+                surface->tmpformat = PIXMAN_invalid;
+            }
+        }
+
+        if (surface->tmpsurface == NULL) {
+           DFBSurfacePixelFormat format;
+
+            D_DEBUG_AT (CairoDFB_Acquire, "Allocating buffer for surface %p.\n", surface);
+
+           format = _cairo_to_directfb_format (_cairo_format_from_content (surface->base.content));
+            status =
+                _directfb_buffer_surface_create (surface->dfb, format,
+                                                 source_rect.w, source_rect.h,
+                                                &surface->tmpsurface);
+            if (unlikely (status))
+                goto ERROR;
+
+           surface->tmpformat = _directfb_to_pixman_format (format);
+        }
+        buffer = surface->tmpsurface;
+       pixman_format = surface->tmpformat;
+
+
+/*        surface->dfbsurface->GetCapabilities (surface->dfbsurface, &caps);
+        DFBSurfaceCapabilities caps;
+        if (caps & DSCAPS_FLIPPING) {
+            DFBRegion region = { .x1 = source_rect.x, .y1 = source_rect.y,
+                                 .x2 = source_rect.x + source_rect.w - 1,
+                                 .y2 = source_rect.y + source_rect.h - 1 };
+            surface->dfbsurface->Flip (surface->dfbsurface, &region, DSFLIP_BLIT);
+        } */
+        buffer->Blit (buffer, surface->dfbsurface, &source_rect, 0, 0);
+    } else {
+        /*might be a subsurface get the offset*/
+        surface->dfbsurface->GetVisibleRectangle (surface->dfbsurface, &source_rect);
+        pixman_format = surface->pixman_format;
+        buffer = surface->dfbsurface;
+    }
+
+    if (buffer->Lock (buffer, lock_flags, &data, &pitch)) {
+        D_DEBUG_AT (CairoDFB_Acquire, "Couldn't lock surface!\n");
+       status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+        goto ERROR;
+    }
+
+    pixman_image = pixman_image_create_bits (pixman_format,
+                                            source_rect.w, source_rect.h,
+                                            data, pitch);
+    if (pixman_image == NULL) {
+       status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+       goto ERROR;
+    }
+
+    image = _cairo_image_surface_create_for_pixman_image (pixman_image,
+                                                         pixman_format);
+    status = image->status;
+    if (status)
+        goto ERROR;
+
+    if (image_rect_out) {
+        image_rect_out->x = source_rect.x;
+        image_rect_out->y = source_rect.y;
+        image_rect_out->width = source_rect.w;
+        image_rect_out->height = source_rect.h;
+    } else {
+        /* lock for read */
+        /* might be a subsurface */
+        if (buffer == surface->dfbsurface) {
+            cairo_surface_set_device_offset (image,
+                                            source_rect.x, source_rect.y);
+       }
+    }
+
+    *image_extra = buffer;
+    *image_out = (cairo_image_surface_t *) image;
+    return CAIRO_STATUS_SUCCESS;
+
+ERROR:
+    if (buffer) {
+        buffer->Unlock (buffer);
+        if (buffer != surface->dfbsurface)
+            buffer->Release (buffer);
+    }
+    return status;
+}
+
+static cairo_surface_t *
+_cairo_directfb_surface_create_internal (IDirectFB *dfb,
+                                        DFBSurfacePixelFormat format,
+                                        cairo_content_t content,
+                                        int              width,
+                                        int              height)
+{
+    cairo_directfb_surface_t *surface;
+    cairo_status_t           status;
+
+    surface = calloc (1, sizeof (cairo_directfb_surface_t));
+    if (unlikely (surface == NULL))
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+    surface->dfb = dfb;
+
+    if (width < 8 || height < 8) {
+        IDirectFBSurface *tmp;
+       DFBRectangle rect = { .x=0, .y=0, .w=width, .h=height };
+
+        /* Some cards (e.g. Matrox) don't support surfaces smaller than 8x8 */
+        status =  _directfb_buffer_surface_create (dfb, format,
+                                        MAX (width, 8), MAX (height, 8),
+                                       &tmp);
+       if (status) {
+           free (surface);
+           return _cairo_surface_create_in_error (status);
+       }
+
+       tmp->GetSubSurface (tmp, &rect, &surface->dfbsurface);
+       tmp->Release (tmp);
+    } else {
+       status = _directfb_buffer_surface_create (dfb, format,
+                                                 width, height,
+                                                 &surface->dfbsurface);
+       if (status) {
+           free (surface);
+           return _cairo_surface_create_in_error (status);
+       }
+    }
+
+    _cairo_surface_init (&surface->base,
+                        &_cairo_directfb_surface_backend,
+                        NULL, /* device */
+                        content);
+    surface->pixman_format = _directfb_to_pixman_format (format);
+    surface->supported_destination = pixman_format_supported_destination (surface->pixman_format);
+
+    surface->width   = width;
+    surface->height  = height;
+    surface->local   = TRUE;
+    surface->blit_premultiplied = TRUE;
+
+    return &surface->base;
+}
+
+static cairo_surface_t *
+_cairo_directfb_surface_create_similar (void            *abstract_src,
+                                        cairo_content_t  content,
+                                        int              width,
+                                        int              height)
+{
+    cairo_directfb_surface_t *other  = abstract_src;
+    DFBSurfacePixelFormat     format;
+
+    D_DEBUG_AT (CairoDFB_Surface,
+                "%s( src=%p, content=0x%x, width=%d, height=%d).\n",
+                __FUNCTION__, other, content, width, height);
+
+    width = (width <= 0) ? 1 : width;
+    height = (height<= 0) ? 1 : height;
+
+    format = _cairo_to_directfb_format (_cairo_format_from_content (content));
+    return _cairo_directfb_surface_create_internal (other->dfb, format,
+                                                   content, width, height);
+}
+
+static cairo_status_t
+_cairo_directfb_surface_finish (void *data)
+{
+    cairo_directfb_surface_t *surface = (cairo_directfb_surface_t *)data;
+
+    D_DEBUG_AT (CairoDFB_Surface, "%s( surface=%p ).\n", __FUNCTION__, surface);
+
+    if (surface->tmpsurface) {
+        surface->tmpsurface->Release (surface->tmpsurface);
+        surface->tmpsurface = NULL;
+    }
+
+    if (surface->dfbsurface) {
+        surface->dfbsurface->Release (surface->dfbsurface);
+        surface->dfbsurface = NULL;
+    }
+
+    if (surface->dfb)
+        surface->dfb = NULL;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_directfb_surface_acquire_source_image (void                   *abstract_surface,
+                                              cairo_image_surface_t **image_out,
+                                              void                  **image_extra)
+{
+    cairo_directfb_surface_t *surface = abstract_surface;
+
+    D_DEBUG_AT (CairoDFB_Acquire,
+               "%s( surface=%p ).\n", __FUNCTION__, surface);
+
+    return _directfb_acquire_surface (surface, NULL, image_out,
+                                     NULL, image_extra, DSLF_READ);
+}
+
+static void
+_cairo_directfb_surface_release_source_image (void                  *abstract_surface,
+                                              cairo_image_surface_t *image,
+                                              void                  *image_extra)
+{
+    IDirectFBSurface *buffer = image_extra;
+
+    D_DEBUG_AT (CairoDFB_Acquire,
+               "%s( release=%p ).\n", __FUNCTION__, abstract_surface);
+
+    buffer->Unlock (buffer);
+
+    cairo_surface_destroy (&image->base);
+}
+
+static cairo_status_t
+_cairo_directfb_surface_acquire_dest_image (void                     *abstract_surface,
+                                            cairo_rectangle_int_t    *interest_rect,
+                                            cairo_image_surface_t   **image_out,
+                                            cairo_rectangle_int_t    *image_rect_out,
+                                            void                    **image_extra)
+{
+    cairo_directfb_surface_t *surface = abstract_surface;
+
+    D_DEBUG_AT (CairoDFB_Acquire,
+               "%s( surface=%p (%dx%d), interest_rect={ %u %u %u %u } ).\n",
+               __FUNCTION__, surface, surface->width, surface->height,
+               interest_rect ? interest_rect->x : 0,
+               interest_rect ? interest_rect->y : 0,
+               interest_rect ? interest_rect->width  : (unsigned) surface->width,
+               interest_rect ? interest_rect->height : (unsigned) surface->height);
+
+    return _directfb_acquire_surface (surface, interest_rect, image_out,
+                                     image_rect_out, image_extra,
+                                     DSLF_READ | DSLF_WRITE);
+}
+
+static void
+_cairo_directfb_surface_release_dest_image (void                  *abstract_surface,
+                                            cairo_rectangle_int_t *interest_rect,
+                                            cairo_image_surface_t *image,
+                                            cairo_rectangle_int_t *image_rect,
+                                            void                  *image_extra)
+{
+    cairo_directfb_surface_t *surface = abstract_surface;
+    IDirectFBSurface *buffer = image_extra;
+
+    D_DEBUG_AT (CairoDFB_Acquire,
+               "%s( surface=%p ).\n", __FUNCTION__, surface);
+
+    buffer->Unlock (buffer);
+
+    if (surface->dfbsurface != buffer) {
+       DFBRegion region = {
+           .x1 = interest_rect->x,
+           .y1 = interest_rect->y,
+           .x2 = interest_rect->x + interest_rect->width - 1,
+           .y2 = interest_rect->y + interest_rect->height - 1
+       };
+       surface->dfbsurface->SetBlittingFlags (surface->dfbsurface, DSBLIT_NOFX);
+        surface->dfbsurface->SetClip (surface->dfbsurface, &region);
+       surface->dfbsurface->Blit (surface->dfbsurface,
+                                  buffer, NULL,
+                                  image_rect->x, image_rect->y);
+    }
+
+    cairo_surface_destroy (&image->base);
+}
+
+static cairo_status_t
+_cairo_directfb_surface_clone_similar (void             *abstract_surface,
+                                       cairo_surface_t  *src,
+                                       int               src_x,
+                                       int               src_y,
+                                       int               width,
+                                       int               height,
+                                       int              *clone_offset_x,
+                                       int              *clone_offset_y,
+                                       cairo_surface_t **clone_out)
+{
+    cairo_directfb_surface_t *surface = abstract_surface;
+    cairo_directfb_surface_t *clone;
+
+    D_DEBUG_AT (CairoDFB_Surface,
+               "%s( surface=%p, src=%p ).\n", __FUNCTION__, surface, src);
+
+    if (src->backend == surface->base.backend) {
+       *clone_offset_x = 0;
+       *clone_offset_y = 0;
+       *clone_out = cairo_surface_reference (src);
+
+       return CAIRO_STATUS_SUCCESS;
+    } else if (_cairo_surface_is_image (src)) {
+       cairo_image_surface_t *image_src = (cairo_image_surface_t *) src;
+       DFBSurfacePixelFormat format;
+       DFBResult              ret;
+       pixman_image_t        *pixman_image;
+       void *data;
+       int pitch;
+
+       format = _directfb_from_pixman_format (image_src->pixman_format);
+       if (format == 0)
+           return CAIRO_INT_STATUS_UNSUPPORTED;
+
+       clone = (cairo_directfb_surface_t *)
+           _cairo_directfb_surface_create_internal (surface->dfb, format,
+                                                    image_src->base.content,
+                                                    width, height);
+       if (unlikely (clone->base.status))
+           return clone->base.status;
+
+       ret = clone->dfbsurface->Lock (clone->dfbsurface,
+                                      DSLF_WRITE, (void *)&data, &pitch);
+       if (ret) {
+           DirectFBError ("IDirectFBSurface::Lock()", ret);
+           cairo_surface_destroy (&clone->base);
+           return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+       }
+
+       pixman_image = pixman_image_create_bits (clone->pixman_format,
+                                                width, height,
+                                                data, pitch);
+       if (unlikely (pixman_image == NULL)) {
+           DirectFBError ("IDirectFBSurface::Lock()", ret);
+           cairo_surface_destroy (&clone->base);
+           return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+       }
+
+       pixman_image_composite32 (PIXMAN_OP_SRC,
+                                  image_src->pixman_image,
+                                  NULL,
+                                  pixman_image,
+                                  src_x, src_y,
+                                  0, 0,
+                                  0, 0,
+                                  width, height);
+
+       pixman_image_unref (pixman_image);
+
+       clone->dfbsurface->Unlock (clone->dfbsurface);
+
+       *clone_offset_x = src_x;
+       *clone_offset_y = src_y;
+       *clone_out = &clone->base;
+       return CAIRO_STATUS_SUCCESS;
+    }
+
+    return CAIRO_INT_STATUS_UNSUPPORTED;
+}
+
+#if DFB_COMPOSITE || DFB_COMPOSITE_TRAPEZOIDS
+static cairo_int_status_t
+_directfb_prepare_composite (cairo_directfb_surface_t    *dst,
+                             const cairo_pattern_t       *src_pattern,
+                             const cairo_pattern_t       *mask_pattern,
+                             cairo_operator_t             op,
+                             int *src_x,             int *src_y,
+                             int *mask_x,            int *mask_y,
+                             unsigned int                 width,
+                             unsigned int                 height,
+                             cairo_directfb_surface_t   **ret_src,
+                             cairo_surface_attributes_t  *ret_src_attr)
+{
+    cairo_directfb_surface_t   *src;
+    cairo_surface_attributes_t  src_attr;
+    cairo_status_t              status;
+    DFBSurfaceBlittingFlags     flags;
+    DFBSurfaceBlendFunction     sblend;
+    DFBSurfaceBlendFunction     dblend;
+    const cairo_color_t        *color;
+
+    /* XXX Unbounded operators are not handled correctly */
+    if (! _cairo_operator_bounded_by_source (op))
+        return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    if (! _directfb_get_operator (op, &sblend, &dblend))
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    if (mask_pattern) {
+               return CAIRO_INT_STATUS_UNSUPPORTED;
+       if (mask_pattern->type != CAIRO_PATTERN_TYPE_SOLID) {
+           const cairo_pattern_t *tmp;
+           int              tmp_x, tmp_y;
+
+           if (src_pattern->type != CAIRO_PATTERN_TYPE_SOLID ||
+               sblend == DSBF_INVDESTALPHA) /* Doesn't work correctly */
+               return CAIRO_INT_STATUS_UNSUPPORTED;
+
+           D_DEBUG_AT (CairoDFB_Render, "Replacing src pattern by mask pattern.\n");
+
+           tmp = src_pattern;
+           tmp_x = *src_x; tmp_y = *src_y;
+
+           src_pattern = mask_pattern;
+           *src_x = *mask_x; *src_y = *mask_y;
+
+           mask_pattern = tmp;
+           *mask_x = tmp_x; *mask_y = tmp_y;
+
+           if (sblend == DSBF_ONE) {
+               sblend = DSBF_SRCALPHA;
+               /*dblend = DSBF_INVSRCALPHA;*/
+           }
+       }
+
+       color = &((cairo_solid_pattern_t *) mask_pattern)->color;
+    } else {
+       color = _cairo_stock_color (CAIRO_STOCK_WHITE);
+    }
+
+    status = _cairo_pattern_acquire_surface (src_pattern, &dst->base,
+                                            *src_x, *src_y, width, height,
+                                            CAIRO_PATTERN_ACQUIRE_NO_REFLECT,
+                                            (cairo_surface_t **) &src,
+                                            &src_attr);
+    if (status)
+       return status;
+
+    if (src->base.backend != &_cairo_directfb_surface_backend ||
+       src->dfb != dst->dfb)
+    {
+       _cairo_pattern_release_surface (src_pattern, &src->base, &src_attr);
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+    }
+
+    if ((src->base.content & CAIRO_CONTENT_ALPHA) == 0) {
+       if (sblend == DSBF_SRCALPHA)
+           sblend = DSBF_ONE;
+       else if (sblend == DSBF_INVSRCALPHA)
+           sblend = DSBF_ZERO;
+
+       if (dblend == DSBF_SRCALPHA)
+           dblend = DSBF_ONE;
+       else if (dblend == DSBF_INVSRCALPHA)
+           dblend = DSBF_ZERO;
+    }
+
+    if ((dst->base.content & CAIRO_CONTENT_ALPHA) == 0) {
+       if (sblend == DSBF_DESTALPHA)
+           sblend = DSBF_ONE;
+       else if (sblend == DSBF_INVDESTALPHA)
+           sblend = DSBF_ZERO;
+
+       if (dblend == DSBF_DESTALPHA)
+           dblend = DSBF_ONE;
+       else if (dblend == DSBF_INVDESTALPHA)
+           dblend = DSBF_ZERO;
+    }
+
+    flags = (sblend == DSBF_ONE && dblend == DSBF_ZERO)
+       ? DSBLIT_NOFX : DSBLIT_BLEND_ALPHACHANNEL;
+    if (! CAIRO_COLOR_IS_OPAQUE (color))
+       flags |= DSBLIT_BLEND_COLORALPHA;
+    if (! _cairo_color_equal (color, _cairo_stock_color (CAIRO_STOCK_WHITE)))
+       flags |= DSBLIT_COLORIZE;
+
+    dst->dfbsurface->SetBlittingFlags (dst->dfbsurface, flags);
+
+    if (flags & (DSBLIT_BLEND_ALPHACHANNEL | DSBLIT_BLEND_COLORALPHA)) {
+       dst->dfbsurface->SetSrcBlendFunction (dst->dfbsurface, sblend);
+       dst->dfbsurface->SetDstBlendFunction (dst->dfbsurface, dblend);
+    }
+
+    if (flags & (DSBLIT_BLEND_COLORALPHA | DSBLIT_COLORIZE)) {
+       if (dst->blit_premultiplied) {
+           dst->dfbsurface->SetColor (dst->dfbsurface,
+                                      color->red_short >> 8,
+                                      color->green_short >> 8,
+                                      color->blue_short >> 8,
+                                      color->alpha_short >> 8);
+       } else {
+           dst->dfbsurface->SetColor (dst->dfbsurface,
+                                      color->red * 0xff,
+                                      color->green * 0xff,
+                                      color->blue * 0xff,
+                                      color->alpha * 0xff);
+       }
+    }
+
+    *ret_src = src;
+    *ret_src_attr = src_attr;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_directfb_finish_composite (cairo_directfb_surface_t   *dst,
+                            const cairo_pattern_t      *src_pattern,
+                            cairo_surface_t            *src,
+                            cairo_surface_attributes_t *src_attr)
+{
+    _cairo_pattern_release_surface (src_pattern, src, src_attr);
+}
+#endif /* DFB_COMPOSITE || DFB_COMPOSITE_TRAPEZOIDS */
+
+#if DFB_COMPOSITE
+static DFBAccelerationMask
+_directfb_categorize_operation (cairo_surface_attributes_t *src_attr)
+{
+    cairo_matrix_t *m = &src_attr->matrix;
+
+    if (m->xy != 0 || m->yx != 0 || m->xx < 0 || m->yy < 0) {
+       if (src_attr->extend != CAIRO_EXTEND_NONE)
+           return DFXL_NONE;
+
+       return DFXL_TEXTRIANGLES;
+    }
+
+    if (m->xx != 1 || m->yy != 1) {
+       if (src_attr->extend != CAIRO_EXTEND_NONE)
+           return DFXL_NONE;
+
+       return DFXL_STRETCHBLIT;
+    }
+
+    switch (src_attr->extend) {
+    case CAIRO_EXTEND_NONE:
+    case CAIRO_EXTEND_REPEAT:
+       if (_cairo_matrix_is_integer_translation (&src_attr->matrix,
+                                                 NULL, NULL))
+       {
+           return DFXL_BLIT;
+       }
+       else
+       {
+           return DFXL_STRETCHBLIT;
+       }
+
+    default:
+    case CAIRO_EXTEND_REFLECT:
+    case CAIRO_EXTEND_PAD:
+       return DFXL_NONE;
+    }
+}
+
+static cairo_int_status_t
+_cairo_directfb_surface_composite (cairo_operator_t  op,
+                                   const cairo_pattern_t  *src_pattern,
+                                   const cairo_pattern_t  *mask_pattern,
+                                   void             *abstract_dst,
+                                   int  src_x,  int  src_y,
+                                   int  mask_x, int  mask_y,
+                                   int  dst_x,  int  dst_y,
+                                   unsigned int      width,
+                                   unsigned int      height,
+                                  cairo_region_t   *clip_region)
+{
+    cairo_directfb_surface_t   *dst = abstract_dst;
+    cairo_directfb_surface_t   *src;
+    cairo_surface_attributes_t  src_attr;
+    cairo_bool_t                is_integer_translation;
+    DFBAccelerationMask         accel, mask;
+    cairo_int_status_t          status;
+    int                         tx, ty;
+
+    D_DEBUG_AT (CairoDFB_Render,
+               "%s( op=%d, src_pattern=%p, mask_pattern=%p, dst=%p,"
+               " src_x=%d, src_y=%d, mask_x=%d, mask_y=%d, dst_x=%d,"
+               " dst_y=%d, width=%u, height=%u ).\n",
+               __FUNCTION__, op, src_pattern, mask_pattern, dst,
+               src_x, src_y, mask_x, mask_y, dst_x, dst_y, width, height);
+
+    if (! dst->supported_destination)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    status = _directfb_prepare_composite (dst, src_pattern, mask_pattern, op,
+                                         &src_x, &src_y, &mask_x, &mask_y,
+                                         width, height, &src, &src_attr);
+    if (status)
+       return status;
+
+    accel = _directfb_categorize_operation (&src_attr);
+    if (accel == DFXL_NONE) {
+       _directfb_finish_composite (dst, src_pattern, &src->base, &src_attr);
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+    }
+
+    dst->dfbsurface->GetAccelerationMask (dst->dfbsurface,
+                                         src->dfbsurface,
+                                         &mask);
+    if ((mask & accel) == 0) {
+       D_DEBUG_AT (CairoDFB_Render, "No acceleration (%08x)!\n", accel);
+       if (accel != DFXL_BLIT) {
+           _directfb_finish_composite (dst, src_pattern, &src->base, &src_attr);
+           return CAIRO_INT_STATUS_UNSUPPORTED;
+       }
+    }
+
+    src_x += src_attr.x_offset;
+    src_y += src_attr.y_offset;
+
+    switch ((int) accel) {
+    case DFXL_BLIT:
+       {
+           DFBRectangle sr;
+
+           is_integer_translation =
+               _cairo_matrix_is_integer_translation (&src_attr.matrix,
+                                                     &tx, &ty);
+           assert (is_integer_translation);
+
+           sr.x = src_x + tx;
+           sr.y = src_y + ty;
+           sr.w = width;
+           sr.h = height;
+
+           if (src_attr.extend == CAIRO_EXTEND_NONE) {
+               D_DEBUG_AT (CairoDFB_Render, "Running Blit().\n");
+
+               RUN_CLIPPED (dst, clip_region, NULL,
+                            dst->dfbsurface->Blit (dst->dfbsurface,
+                                                   src->dfbsurface,
+                                                   &sr, dst_x, dst_y));
+           } else if (src_attr.extend == CAIRO_EXTEND_REPEAT) {
+               DFBRegion clip;
+
+               clip.x1 = dst_x;
+               clip.y1 = dst_y;
+               clip.x2 = dst_x + width  - 1;
+               clip.y2 = dst_y + height - 1;
+
+               D_DEBUG_AT (CairoDFB_Render, "Running TileBlit().\n");
+
+               RUN_CLIPPED (dst, clip_region, &clip,
+                            dst->dfbsurface->TileBlit (dst->dfbsurface,
+                                                       src->dfbsurface,
+                                                       &sr, dst_x, dst_y));
+           }
+           break;
+       }
+
+    case DFXL_STRETCHBLIT:
+       {
+           DFBRectangle sr, dr;
+           double       x1, y1, x2, y2;
+
+           TRANSFORM_POINT2X (src_attr.matrix,
+                              src_x, src_y, x1, y1);
+           TRANSFORM_POINT2X (src_attr.matrix,
+                              src_x+width, src_y+height, x2, y2);
+
+           sr.x = floor (x1);
+           sr.y = floor (y1);
+           sr.w = ceil (x2) - sr.x;
+           sr.h = ceil (y2) - sr.y;
+
+           dr.x = dst_x;
+           dr.y = dst_y;
+           dr.w = width;
+           dr.h = height;
+
+           D_DEBUG_AT (CairoDFB_Render, "Running StretchBlit().\n");
+
+           RUN_CLIPPED (dst, clip_region, NULL,
+                        dst->dfbsurface->StretchBlit (dst->dfbsurface,
+                                                      src->dfbsurface,
+                                                      &sr, &dr));
+           break;
+       }
+
+    case DFXL_TEXTRIANGLES:
+       {
+           DFBRegion clip;
+           DFBVertex v[4];
+           float     x1, y1, x2, y2;
+           int       w, h;
+
+           status = cairo_matrix_invert (&src_attr.matrix);
+           /* guaranteed by cairo_pattern_set_matrix (); */
+           assert (status == CAIRO_STATUS_SUCCESS);
+
+           x1 = src_x;
+           y1 = src_y;
+           x2 = width  + x1;
+           y2 = height + y1;
+
+           src->dfbsurface->GetSize (src->dfbsurface, &w, &h);
+
+           TRANSFORM_POINT3X (src_attr.matrix, x1, y1, v[0].x, v[0].y);
+           v[0].z = 0;
+           v[0].w = 1;
+           v[0].s = x1 / w;
+           v[0].t = y1 / h;
+
+           TRANSFORM_POINT3X (src_attr.matrix, x2, y1, v[1].x, v[1].y);
+           v[1].z = 0;
+           v[1].w = 1;
+           v[1].s = x2 / w;
+           v[1].t = y1 / h;
+
+           TRANSFORM_POINT3X (src_attr.matrix, x2, y2, v[2].x, v[2].y);
+           v[2].z = 0;
+           v[2].w = 1;
+           v[2].s = x2 / w;
+           v[2].t = y2 / h;
+
+           TRANSFORM_POINT3X (src_attr.matrix, x1, y2, v[3].x, v[3].y);
+           v[3].z = 0;
+           v[3].w = 1;
+           v[3].s = x1 / w;
+           v[3].t = y2 / h;
+
+           clip.x1 = dst_x;
+           clip.y1 = dst_y;
+           clip.x2 = dst_x + width  - 1;
+           clip.y2 = dst_y + height - 1;
+
+           D_DEBUG_AT (CairoDFB_Render, "Running TextureTriangles().\n");
+
+           RUN_CLIPPED (dst, clip_region, &clip,
+                        dst->dfbsurface->TextureTriangles (dst->dfbsurface,
+                                                           src->dfbsurface,
+                                                           v, NULL,
+                                                           4, DTTF_FAN));
+           break;
+       }
+
+    default:
+       D_BUG ("Unexpected operation");
+       break;
+    }
+
+    _directfb_finish_composite (dst, src_pattern, &src->base, &src_attr);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+#endif /* DFB_COMPOSITE */
+
+#if DFB_RECTANGLES
+static cairo_int_status_t
+_cairo_directfb_surface_fill_rectangles (void                  *abstract_surface,
+                                         cairo_operator_t       op,
+                                         const cairo_color_t   *color,
+                                         cairo_rectangle_int_t *rects,
+                                         int                    n_rects)
+{
+    cairo_directfb_surface_t *dst   = abstract_surface;
+    DFBSurfaceDrawingFlags    flags;
+    DFBSurfaceBlendFunction   sblend;
+    DFBSurfaceBlendFunction   dblend;
+    DFBRectangle              r[n_rects];
+    int                       i;
+
+    D_DEBUG_AT (CairoDFB_Render,
+               "%s( dst=%p, op=%d, color=%p, rects=%p, n_rects=%d ).\n",
+               __FUNCTION__, dst, op, color, rects, n_rects);
+
+    if (! dst->supported_destination)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    if (! _directfb_get_operator (op, &sblend, &dblend))
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    if (CAIRO_COLOR_IS_OPAQUE (color)) {
+       if (sblend == DSBF_SRCALPHA)
+           sblend = DSBF_ONE;
+       else if (sblend == DSBF_INVSRCALPHA)
+           sblend = DSBF_ZERO;
+
+       if (dblend == DSBF_SRCALPHA)
+           dblend = DSBF_ONE;
+       else if (dblend == DSBF_INVSRCALPHA)
+           dblend = DSBF_ZERO;
+    }
+    if ((dst->base.content & CAIRO_CONTENT_ALPHA) == 0) {
+       if (sblend == DSBF_DESTALPHA)
+           sblend = DSBF_ONE;
+       else if (sblend == DSBF_INVDESTALPHA)
+           sblend = DSBF_ZERO;
+
+       if (dblend == DSBF_DESTALPHA)
+           dblend = DSBF_ONE;
+       else if (dblend == DSBF_INVDESTALPHA)
+           dblend = DSBF_ZERO;
+    }
+
+    flags = (sblend == DSBF_ONE && dblend == DSBF_ZERO) ? DSDRAW_NOFX : DSDRAW_BLEND;
+    dst->dfbsurface->SetDrawingFlags (dst->dfbsurface, flags);
+    if (flags & DSDRAW_BLEND) {
+       dst->dfbsurface->SetSrcBlendFunction (dst->dfbsurface, sblend);
+       dst->dfbsurface->SetDstBlendFunction (dst->dfbsurface, dblend);
+    }
+
+    dst->dfbsurface->SetColor (dst->dfbsurface,
+                              color->red_short >> 8,
+                              color->green_short >> 8,
+                              color->blue_short >> 8,
+                              color->alpha_short >> 8);
+
+    for (i = 0; i < n_rects; i++) {
+       r[i].x = rects[i].x;
+       r[i].y = rects[i].y;
+       r[i].w = rects[i].width;
+       r[i].h = rects[i].height;
+    }
+
+    RUN_CLIPPED (dst, NULL, NULL,
+                dst->dfbsurface->FillRectangles (dst->dfbsurface, r, n_rects));
+
+    return CAIRO_STATUS_SUCCESS;
+}
+#endif
+
+#if DFB_COMPOSITE_TRAPEZOIDS
+static cairo_int_status_t
+_cairo_directfb_surface_composite_trapezoids (cairo_operator_t   op,
+                                              const cairo_pattern_t   *pattern,
+                                              void              *abstract_dst,
+                                              cairo_antialias_t  antialias,
+                                              int  src_x,   int  src_y,
+                                              int  dst_x,   int  dst_y,
+                                              unsigned int       width,
+                                              unsigned int       height,
+                                              cairo_trapezoid_t *traps,
+                                              int                num_traps,
+                                             cairo_region_t    *clip_region)
+{
+    cairo_directfb_surface_t   *dst = abstract_dst;
+    cairo_directfb_surface_t   *src;
+    cairo_surface_attributes_t  src_attr;
+    cairo_status_t              status;
+    DFBAccelerationMask         accel;
+
+    D_DEBUG_AT (CairoDFB_Render,
+                "%s( op=%d, pattern=%p, dst=%p, antialias=%d,"
+                   " src_x=%d, src_y=%d, dst_x=%d, dst_y=%d,"
+                   " width=%u, height=%u, traps=%p, num_traps=%d ).\n",
+                __FUNCTION__, op, pattern, dst, antialias,
+                src_x, src_y, dst_x, dst_y, width, height, traps, num_traps);
+
+    if (! dst->supported_destination)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    if (antialias != CAIRO_ANTIALIAS_NONE)
+        return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    /* Textures are not supported yet. */
+    if (pattern->type != CAIRO_PATTERN_TYPE_SOLID)
+        return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    status = _directfb_prepare_composite (dst, pattern, NULL, op,
+                                         &src_x, &src_y, NULL, NULL,
+                                         width, height, &src, &src_attr);
+    if (status)
+        return status;
+
+    dst->dfbsurface->GetAccelerationMask (dst->dfbsurface,
+                                         src->dfbsurface,
+                                         &accel);
+
+    status = CAIRO_INT_STATUS_UNSUPPORTED;
+
+    if (accel & DFXL_TEXTRIANGLES) {
+        DFBVertex  vertex[6*num_traps];
+        DFBVertex *v = &vertex[0];
+        int        n = 0;
+
+#define ADD_TRI_V(V, X, Y) do { \
+    (V)->x = (X); (V)->y = (Y); (V)->w = 1; (V)->z = (V)->s = (V)->t = 0; \
+} while (0)
+#define ADD_TRI(id, x1, y1, x2, y2, x3, y3) do {\
+    const int p = (id)*3;\
+    ADD_TRI_V (v+p+0, x1, y1); \
+    ADD_TRI_V (v+p+1, x2, y2); \
+    ADD_TRI_V (v+p+2, x3, y3); \
+} while (0)
+       while (num_traps--) {
+            double lx1, ly1, lx2, ly2;
+            double rx1, ry1, rx2, ry2;
+
+            lx1 = _cairo_fixed_to_double (traps->left.p1.x);
+            ly1 = _cairo_fixed_to_double (traps->left.p1.y);
+            lx2 = _cairo_fixed_to_double (traps->left.p2.x);
+            ly2 = _cairo_fixed_to_double (traps->left.p2.y);
+            rx1 = _cairo_fixed_to_double (traps->right.p1.x);
+            ry1 = _cairo_fixed_to_double (traps->right.p1.y);
+            rx2 = _cairo_fixed_to_double (traps->right.p2.x);
+            ry2 = _cairo_fixed_to_double (traps->right.p2.y);
+
+            if (traps->left.p1.y < traps->top) {
+                double y = _cairo_fixed_to_double (traps->top);
+                if (lx2 != lx1)
+                    lx1 = (y - ly1) * (lx2 - lx1) / (ly2 - ly1) + lx1;
+                ly1 = y;
+            }
+            if (traps->left.p2.y > traps->bottom) {
+                double y = _cairo_fixed_to_double (traps->bottom);
+                if (lx2 != lx1)
+                    lx2 = (y - ly1) * (lx2 - lx1) / (ly2 - ly1) + lx1;
+                ly2 = y;
+            }
+
+            if (traps->right.p1.y < traps->top) {
+                double y = _cairo_fixed_to_double (traps->top);
+                if (rx2 != rx1)
+                    rx1 = (y - ry1) * (rx2 - rx1) / (ry2 - ry1) + rx1;
+                ry1 = y;
+            }
+            if (traps->right.p2.y > traps->bottom) {
+                double y = _cairo_fixed_to_double (traps->bottom);
+                if (rx2 != rx1)
+                    rx2 = (y - ry1) * (rx2 - rx1) / (ry2 - ry1) + rx1;
+                ry2 = y;
+            }
+
+            if (lx1 == rx1 && ly1 == ry1) {
+                ADD_TRI (0, lx2, ly2, lx1, ly1, rx2, ry2);
+                v += 3;
+                n += 3;
+            } else if (lx2 == rx2 && ly2 == ry2) {
+                ADD_TRI (0, lx1, ly1, lx2, ly2, rx1, ry1);
+                v += 3;
+                n += 3;
+            } else {
+                ADD_TRI (0, lx1, ly1, rx1, ry1, lx2, ly2);
+                ADD_TRI (1, lx2, ly2, rx1, ry1, rx2, ry2);
+                v += 6;
+                n += 6;
+            }
+
+            traps++;
+        }
+#undef ADD_TRI
+#undef ADD_TRI_V
+
+        D_DEBUG_AT (CairoDFB_Render, "Running TextureTriangles().\n");
+
+        RUN_CLIPPED (dst, clip_region, NULL,
+                     dst->dfbsurface->TextureTriangles (dst->dfbsurface,
+                                                       src->dfbsurface,
+                                                       vertex, NULL, n,
+                                                       DTTF_LIST));
+
+        status = CAIRO_STATUS_SUCCESS;
+    }
+
+    _directfb_finish_composite (dst, pattern, &src->base, &src_attr);
+
+    return status;
+}
+#endif /* DFB_COMPOSITE_TRAPEZOIDS */
+
+static cairo_bool_t
+_cairo_directfb_abstract_surface_get_extents (void                  *abstract_surface,
+                                              cairo_rectangle_int_t *rectangle)
+{
+    cairo_directfb_surface_t *surface = abstract_surface;
+
+    D_DEBUG_AT (CairoDFB_Surface,
+               "%s( surface=%p, rectangle=%p ).\n",
+               __FUNCTION__, surface, rectangle);
+
+    if (!surface->local) {
+       surface->dfbsurface->GetSize (surface->dfbsurface,
+                                     &surface->width, &surface->height);
+    }
+
+    rectangle->x = 0;
+    rectangle->y = 0;
+    rectangle->width  = surface->width;
+    rectangle->height = surface->height;
+
+    return TRUE;
+}
+
+#if DFB_SHOW_GLYPHS
+static cairo_status_t
+_directfb_allocate_font_cache (IDirectFB *dfb,
+                              int width, int height,
+                              cairo_directfb_font_cache_t **out)
+{
+    cairo_directfb_font_cache_t *cache;
+    cairo_status_t status;
+
+    cache = calloc (1, sizeof (cairo_directfb_font_cache_t));
+    if (cache == NULL)
+        return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    cache->dfb = dfb;
+    status = _directfb_buffer_surface_create (dfb,
+                            _directfb_argb_font ? DSPF_ARGB : DSPF_A8,
+                           width, height,
+                           &cache->dfbsurface);
+    if (status) {
+        free (cache);
+       return status;
+    }
+
+    cache->width  = width;
+    cache->height = height;
+    *out = cache;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_directfb_destroy_font_cache (cairo_directfb_font_cache_t *cache)
+{
+    cache->dfbsurface->Release (cache->dfbsurface);
+    free (cache);
+}
+
+/* XXX hook into rtree font cache from drm */
+static cairo_status_t
+_directfb_acquire_font_cache (cairo_directfb_surface_t     *surface,
+                              cairo_scaled_font_t          *scaled_font,
+                              const cairo_glyph_t          *glyphs,
+                              int                           num_glyphs,
+                              cairo_directfb_font_cache_t **ret_cache,
+                              DFBRectangle                 *rects,
+                              DFBPoint                     *points,
+                             int                          *ret_num)
+{
+    cairo_status_t               status;
+    cairo_scaled_glyph_t        *chars[num_glyphs];
+    int                          num_chars = 0;
+    cairo_directfb_font_cache_t *cache     = NULL;
+    int                          n         = 0;
+    int                          x         = 0;
+    int                          y         = 0;
+    int                          w         = 8;
+    int                          h         = 8;
+    int                          i;
+
+    D_DEBUG_AT (CairoDFB_Font, "%s( %p [%d] )\n", __FUNCTION__, glyphs, num_glyphs );
+
+    _cairo_scaled_font_freeze_cache (scaled_font);
+
+    if (scaled_font->surface_private) {
+       cache = scaled_font->surface_private;
+       x = cache->x;
+       y = cache->y;
+    }
+
+    for (i = 0; i < num_glyphs; i++) {
+       cairo_scaled_glyph_t  *scaled_glyph;
+       cairo_image_surface_t *img;
+
+       D_DEBUG_AT (CairoDFB_Font, "  -> [%2d] = %4lu\n", i, glyphs[i].index );
+
+       status = _cairo_scaled_glyph_lookup (scaled_font, glyphs[i].index,
+                                         CAIRO_SCALED_GLYPH_INFO_SURFACE,
+                                         &scaled_glyph);
+       if (status) {
+           _cairo_scaled_font_thaw_cache (scaled_font);
+           return status;
+       }
+
+       img = scaled_glyph->surface;
+       switch (img->format) {
+       case CAIRO_FORMAT_A1:
+       case CAIRO_FORMAT_A8:
+       case CAIRO_FORMAT_ARGB32:
+           break;
+       case CAIRO_FORMAT_RGB24:
+       default:
+           D_DEBUG_AT (CairoDFB_Font,
+                       "  -> Unsupported font format %d!\n", img->format);
+           _cairo_scaled_font_thaw_cache (scaled_font);
+           return CAIRO_INT_STATUS_UNSUPPORTED;
+       }
+
+       points[n].x = _cairo_lround (glyphs[i].x - img->base.device_transform.x0);
+       points[n].y = _cairo_lround (glyphs[i].y - img->base.device_transform.y0);
+
+       //        D_DEBUG_AT (CairoDFB_Font, "            (%4d,%4d) [%2d]\n", points[n].x, points[n].y, n );
+
+       if (points[n].x >= surface->width  ||
+           points[n].y >= surface->height ||
+           points[n].x+img->width  <= 0   ||
+           points[n].y+img->height <= 0)
+       {
+           continue;
+       }
+
+       if (scaled_glyph->surface_private == NULL) {
+           DFBRectangle *rect;
+
+           if (x+img->width > 2048) {
+               x = 0;
+               y = h;
+               h = 0;
+           }
+
+           rects[n].x = x;
+           rects[n].y = y;
+           rects[n].w = img->width;
+           rects[n].h = img->height;
+
+           x += img->width;
+           h  = MAX (h, img->height);
+           w  = MAX (w, x);
+
+           /* Remember glyph location */
+           rect = malloc (sizeof (DFBRectangle));
+           if (rect == NULL) {
+               _cairo_scaled_font_thaw_cache (scaled_font);
+               return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+           }
+           *rect = rects[n];
+
+           scaled_glyph->surface_private = rect;
+           chars[num_chars++] = scaled_glyph;
+
+           D_DEBUG_AT (CairoDFB_Font, "  -> loading at %4d,%2d <- rect %p, img %p, entry %p\n",
+                       rects[n].x, rects[n].y, rect, scaled_glyph->surface, scaled_glyph);
+       } else {
+           rects[n] = *(DFBRectangle *) scaled_glyph->surface_private;
+
+           D_DEBUG_AT (CairoDFB_Font, "  -> exists at  %4d,%2d\n", rects[n].x, rects[n].y);
+       }
+
+       n++;
+    }
+
+    if (n == 0) {
+       _cairo_scaled_font_thaw_cache (scaled_font);
+       return CAIRO_INT_STATUS_NOTHING_TO_DO;
+    }
+
+    h += y;
+    w = MAX (w, 8);
+    h = MAX (h, 8);
+
+    /* XXX query maximum surface size */
+    if (w > 2048 || h > 2048) {
+       _cairo_scaled_font_thaw_cache (scaled_font);
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+    }
+
+    if (cache) {
+       if (cache->width < w || cache->height < h) {
+           cairo_directfb_font_cache_t *new_cache;
+
+           w = MAX (w, cache->width);
+           h = MAX (h, cache->height);
+
+           D_DEBUG_AT (CairoDFB_Font, "  -> Reallocating font cache (%dx%d).\n", w, h);
+
+           status = _directfb_allocate_font_cache (surface->dfb,
+                                                   w, h,
+                                                   &new_cache);
+           if (status) {
+               _cairo_scaled_font_thaw_cache (scaled_font);
+               return status;
+           }
+
+           new_cache->dfbsurface->Blit (new_cache->dfbsurface,
+                                        cache->dfbsurface, NULL, 0, 0);
+
+           _directfb_destroy_font_cache (cache);
+           scaled_font->surface_private = cache = new_cache;
+       }
+    } else {
+       D_DEBUG_AT (CairoDFB_Font, "  -> Allocating font cache (%dx%d).\n", w, h);
+
+       status = _directfb_allocate_font_cache (surface->dfb, w, h, &cache);
+       if (status) {
+           _cairo_scaled_font_thaw_cache (scaled_font);
+           return status;
+       }
+
+       scaled_font->surface_backend = &_cairo_directfb_surface_backend;
+       scaled_font->surface_private = cache;
+    }
+
+    if (num_chars) {
+       unsigned char *data;
+       int            pitch;
+
+       if (cache->dfbsurface->Lock (cache->dfbsurface,
+                                    DSLF_WRITE, (void *)&data, &pitch))
+       {
+           _cairo_scaled_font_thaw_cache (scaled_font);
+           return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+       }
+
+       D_DEBUG_AT (CairoDFB_Font, "  => %d chars to load, cache %dx%d\n", num_chars, cache->width, cache->height);
+
+       for (i = 0; i < num_chars; i++) {
+           cairo_image_surface_t *img  = chars[i]->surface;
+           DFBRectangle          *rect = chars[i]->surface_private;
+           unsigned char         *dst  = data;
+           unsigned char         *src;
+           int                    j;
+
+           D_DEBUG_AT (CairoDFB_Font, "  -> loading [%2d] <- rect %p, img %p, entry %p\n", i, rect, img, chars[i]);
+
+           src = img->data;
+
+           D_DEBUG_AT (CairoDFB_Font, "     from %p\n", src);
+
+           dst += rect->y * pitch + (_directfb_argb_font ? (rect->x<<2) : rect->x);
+
+           D_DEBUG_AT (CairoDFB_Font, "     to %4d,%2d (%p)\n", rect->x, rect->y, dst);
+
+           if (img->format == CAIRO_FORMAT_A1) {
+               for (h = rect->h; h; h--) {
+                   if (_directfb_argb_font) {
+                       for (j = 0; j < rect->w; j++)
+                           ((uint32_t *) dst)[j] = (src[j>>3] & (1 << (j&7))) ? 0xffffffff : 0;
+                   } else {
+                       for (j = 0; j < rect->w; j++)
+                           dst[j] = (src[j>>3] & (1 << (j&7))) ? 0xff : 0;
+                   }
+
+                   dst += pitch;
+                   src += img->stride;
+               }
+           } else if (img->format == CAIRO_FORMAT_A8) {
+               for (h = rect->h; h; h--) {
+                   if (_directfb_argb_font) {
+                       for (j = 0; j < rect->w; j++)
+                           ((uint32_t *) dst)[j] = src[j] * 0x01010101;
+                   } else {
+                       direct_memcpy (dst, src, rect->w);
+                   }
+
+                   dst += pitch;
+                   src += img->stride;
+               }
+           } else { /* ARGB32 */
+               for (h = rect->h; h; h--) {
+                   if (_directfb_argb_font) {
+                       direct_memcpy (dst, src, rect->w<<2);
+                   } else {
+                       for (j = 0; j < rect->w; j++)
+                           dst[j] = ((uint32_t *) src)[j] >> 24;
+                   }
+
+                   dst += pitch;
+                   src += img->stride;
+               }
+           }
+       }
+
+       cache->dfbsurface->Unlock (cache->dfbsurface);
+    }
+
+    _cairo_scaled_font_thaw_cache (scaled_font);
+
+    cache->x = x;
+    cache->y = y;
+
+    D_DEBUG_AT (CairoDFB_Font, "  => cache %d,%d, %p [%d]\n", x, y, cache, n);
+
+    *ret_cache = cache;
+    *ret_num   = n;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_directfb_surface_scaled_font_fini (cairo_scaled_font_t *scaled_font)
+{
+    cairo_directfb_font_cache_t *cache = scaled_font->surface_private;
+
+    D_DEBUG_AT (CairoDFB_Font,
+               "%s( scaled_font=%p ).\n", __FUNCTION__, scaled_font);
+
+    if (cache != NULL) {
+       _directfb_destroy_font_cache (cache);
+       scaled_font->surface_private = NULL;
+    }
+}
+
+static void
+_cairo_directfb_surface_scaled_glyph_fini (cairo_scaled_glyph_t *scaled_glyph,
+                                           cairo_scaled_font_t  *scaled_font)
+{
+     D_DEBUG_AT (CairoDFB_Font,
+                 "%s( scaled_glyph=%p, scaled_font=%p ).\n",
+                 __FUNCTION__, scaled_glyph, scaled_font);
+
+     free (scaled_glyph->surface_private);
+     scaled_glyph->surface_private = NULL;
+}
+
+static cairo_int_status_t
+_cairo_directfb_surface_show_glyphs (void                  *abstract_dst,
+                                     cairo_operator_t       op,
+                                     const cairo_pattern_t  *pattern,
+                                     cairo_glyph_t         *glyphs,
+                                     int                    num_glyphs,
+                                     cairo_scaled_font_t    *scaled_font,
+                                    const cairo_clip_t     *clip,
+                                    int                    *remaining_glyphs)
+{
+    cairo_directfb_surface_t    *dst = abstract_dst;
+    cairo_directfb_font_cache_t *cache;
+    cairo_status_t               status;
+    DFBSurfaceBlittingFlags      flags;
+    DFBSurfaceBlendFunction      sblend;
+    DFBSurfaceBlendFunction      dblend;
+    DFBRectangle                 rects[num_glyphs];
+    DFBPoint                     points[num_glyphs];
+    int                          num;
+    const cairo_color_t         *color;
+    cairo_region_t             *clip_region = NULL;
+
+    D_DEBUG_AT (CairoDFB_Font,
+               "%s( dst=%p, op=%d, pattern=%p, glyphs=%p, num_glyphs=%d, scaled_font=%p ).\n",
+               __FUNCTION__, dst, op, pattern, glyphs, num_glyphs, scaled_font);
+
+    if (! dst->supported_destination)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    if (pattern->type != CAIRO_PATTERN_TYPE_SOLID)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    /* Fallback if we need to emulate clip regions */
+    if (clip != NULL) {
+       clip_region = _cairo_clip_get_region (clip);
+       if (unlikely (clip_region == NULL))
+           return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+    }
+
+    /* XXX Unbounded operators are not handled correctly */
+    if (! _cairo_operator_bounded_by_mask (op))
+        return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    if (! _directfb_get_operator (op, &sblend, &dblend) ||
+       sblend == DSBF_DESTALPHA || sblend == DSBF_INVDESTALPHA)
+    {
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+    }
+
+    status = _directfb_acquire_font_cache (dst, scaled_font, glyphs, num_glyphs,
+                                          &cache, &rects[0], &points[0], &num);
+    if (status) {
+       if (status == CAIRO_INT_STATUS_NOTHING_TO_DO)
+           status = CAIRO_STATUS_SUCCESS;
+       return status;
+    }
+
+    color = &((cairo_solid_pattern_t *) pattern)->color;
+
+    flags = DSBLIT_BLEND_ALPHACHANNEL | DSBLIT_COLORIZE;
+    if (! CAIRO_COLOR_IS_OPAQUE (color))
+       flags |= DSBLIT_BLEND_COLORALPHA;
+
+    if (!_directfb_argb_font) {
+       if (sblend == DSBF_ONE) {
+           sblend = DSBF_SRCALPHA;
+           if (dblend == DSBF_ZERO)
+               dblend = DSBF_INVSRCALPHA;
+       }
+    }
+
+    dst->dfbsurface->SetBlittingFlags (dst->dfbsurface, flags);
+    dst->dfbsurface->SetSrcBlendFunction (dst->dfbsurface, sblend);
+    dst->dfbsurface->SetDstBlendFunction (dst->dfbsurface, dblend);
+    if (dst->blit_premultiplied) {
+       dst->dfbsurface->SetColor (dst->dfbsurface,
+                                  color->red_short >> 8,
+                                  color->green_short >> 8,
+                                  color->blue_short >> 8,
+                                  color->alpha_short >> 8);
+    } else {
+       dst->dfbsurface->SetColor (dst->dfbsurface,
+                                  color->red * 0xff,
+                                  color->green * 0xff,
+                                  color->blue * 0xff,
+                                  color->alpha * 0xff);
+    }
+
+    D_DEBUG_AT (CairoDFB_Font, "Running BatchBlit().\n");
+
+    RUN_CLIPPED (dst, clip_region, NULL,
+                dst->dfbsurface->BatchBlit (dst->dfbsurface,
+                                            cache->dfbsurface, rects, points, num));
+
+    return CAIRO_STATUS_SUCCESS;
+}
+#endif /* DFB_SHOW_GLYPHS */
+
+
+static cairo_bool_t
+_cairo_directfb_surface_is_similar (void *surface_a, void *surface_b)
+{
+    cairo_directfb_surface_t *a = (cairo_directfb_surface_t *) surface_a;
+    cairo_directfb_surface_t *b = (cairo_directfb_surface_t *) surface_b;
+
+    return a->dfb == b->dfb;
+}
+
+static cairo_surface_backend_t
+_cairo_directfb_surface_backend = {
+         CAIRO_SURFACE_TYPE_DIRECTFB, /*type*/
+        _cairo_directfb_surface_finish, /*finish*/
+
+        _cairo_default_context_create,
+
+        _cairo_directfb_surface_create_similar,/*create_similar*/
+       NULL, /* create similar image */
+       NULL, /* map to image */
+       NULL, /* unmap image */
+
+        _cairo_directfb_surface_acquire_source_image,/*acquire_source_image*/
+        _cairo_directfb_surface_release_source_image,/*release_source_image*/
+        _cairo_directfb_surface_acquire_dest_image,/*acquire_dest_image*/
+        _cairo_directfb_surface_release_dest_image,/*release_dest_image*/
+        _cairo_directfb_surface_clone_similar,/*clone_similar*/
+#if DFB_COMPOSITE
+        _cairo_directfb_surface_composite,/*composite*/
+#else
+        NULL,/*composite*/
+#endif
+#if DFB_RECTANGLES
+        _cairo_directfb_surface_fill_rectangles,/*fill_rectangles*/
+#else
+        NULL,/*fill_rectangles*/
+#endif
+#if DFB_COMPOSITE_TRAPEZOIDS
+        _cairo_directfb_surface_composite_trapezoids,/*composite_trapezoids*/
+#else
+        NULL,/*composite_trapezoids*/
+#endif
+        NULL, /* create_span_renderer */
+        NULL, /* check_span_renderer */
+        NULL, /* copy_page */
+        NULL, /* show_page */
+        _cairo_directfb_abstract_surface_get_extents,/* get_extents */
+        NULL, /* old_show_glyphs */
+        NULL, /* get_font_options */
+        NULL, /* flush */
+        NULL, /* mark_dirty_rectangle */
+#if DFB_SHOW_GLYPHS
+        _cairo_directfb_surface_scaled_font_fini,/* scaled_font_fini */
+        _cairo_directfb_surface_scaled_glyph_fini,/* scaled_glyph_fini */
+#else
+        NULL,
+        NULL,
+#endif
+        NULL, /* paint */
+        NULL, /* mask */
+        NULL, /* stroke */
+        NULL, /* fill */
+#if DFB_SHOW_GLYPHS
+        _cairo_directfb_surface_show_glyphs,/* show_glyphs */
+#else
+        NULL, /* show_glyphs */
+#endif
+        NULL, /* snapshot */
+        _cairo_directfb_surface_is_similar,
+};
+
+
+static void
+cairo_directfb_surface_backend_init (IDirectFB *dfb)
+{
+    static int done = 0;
+
+    if (done)
+       return;
+
+    if (getenv ("CAIRO_DIRECTFB_NO_ACCEL")) {
+#if DFB_RECTANGLES
+       _cairo_directfb_surface_backend.fill_rectangles = NULL;
+#endif
+#if DFB_COMPOSITE
+       _cairo_directfb_surface_backend.composite = NULL;
+#endif
+#if DFB_COMPOSITE_TRAPEZOIDS
+       _cairo_directfb_surface_backend.composite_trapezoids = NULL;
+#endif
+#if DFB_SHOW_GLYPHS
+       _cairo_directfb_surface_backend.scaled_font_fini = NULL;
+       _cairo_directfb_surface_backend.scaled_glyph_fini = NULL;
+       _cairo_directfb_surface_backend.show_glyphs = NULL;
+#endif
+       D_DEBUG_AT (CairoDFB_Surface, "Acceleration disabled.\n");
+    } else {
+       DFBGraphicsDeviceDescription dsc;
+
+       dfb->GetDeviceDescription (dfb, &dsc);
+
+#if DFB_COMPOSITE
+       //        if (!(dsc.acceleration_mask & DFXL_BLIT))
+       //            _cairo_directfb_surface_backend.composite = NULL;
+#endif
+
+#if DFB_COMPOSITE_TRAPEZOIDS
+       //        if (!(dsc.acceleration_mask & DFXL_TEXTRIANGLES))
+       //            _cairo_directfb_surface_backend.composite_trapezoids = NULL;
+#endif
+    }
+
+    if (getenv ("CAIRO_DIRECTFB_ARGB_FONT")) {
+       _directfb_argb_font = 1;
+       D_DEBUG_AT (CairoDFB_Surface, "Using ARGB fonts.\n");
+    }
+
+    done = 1;
+}
+
+cairo_surface_t *
+cairo_directfb_surface_create (IDirectFB *dfb, IDirectFBSurface *dfbsurface)
+{
+    cairo_directfb_surface_t *surface;
+    DFBSurfacePixelFormat     format;
+    DFBSurfaceCapabilities caps;
+
+    D_ASSERT (dfb != NULL);
+    D_ASSERT (dfbsurface != NULL);
+
+    cairo_directfb_surface_backend_init (dfb);
+
+    surface = calloc (1, sizeof (cairo_directfb_surface_t));
+    if (surface == NULL)
+        return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+    dfbsurface->AddRef (dfbsurface);
+    dfbsurface->GetPixelFormat (dfbsurface, &format);
+    dfbsurface->GetSize (dfbsurface, &surface->width, &surface->height);
+    surface->dfb = dfb;
+    surface->dfbsurface = dfbsurface;
+    surface->pixman_format = _directfb_to_pixman_format (format);
+    surface->supported_destination = pixman_format_supported_destination (surface->pixman_format);
+
+    dfbsurface->GetCapabilities (dfbsurface, &caps);
+    if (caps & DSCAPS_PREMULTIPLIED)
+       surface->blit_premultiplied = TRUE;
+
+    _cairo_surface_init (&surface->base,
+                         &_cairo_directfb_surface_backend,
+                        NULL, /* device */
+                        _directfb_format_to_content (format));
+
+    return &surface->base;
+}
diff --git a/src/cairo-directfb.h b/src/cairo-directfb.h
new file mode 100644 (file)
index 0000000..e3d818c
--- /dev/null
@@ -0,0 +1,67 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2003 University of Southern California
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *     Carl D. Worth <cworth@isi.edu>
+ */
+
+/*
+ * Environment variables affecting the backend:
+ *
+ *  %CAIRO_DIRECTFB_NO_ACCEL (boolean)
+ *      if found, disables acceleration at all
+ *
+ *  %CAIRO_DIRECTFB_ARGB_FONT (boolean)
+ *      if found, enables using ARGB fonts instead of A8
+ */
+
+#ifndef CAIRO_DIRECTFB_H
+#define CAIRO_DIRECTFB_H
+
+#include "cairo.h"
+
+#if  CAIRO_HAS_DIRECTFB_SURFACE
+
+#include <directfb.h>
+
+CAIRO_BEGIN_DECLS
+
+cairo_public cairo_surface_t *
+cairo_directfb_surface_create (IDirectFB *dfb, IDirectFBSurface *surface);
+
+CAIRO_END_DECLS
+
+#else  /*CAIRO_HAS_DIRECTFB_SURFACE*/
+# error Cairo was not compiled with support for the directfb backend
+#endif /*CAIRO_HAS_DIRECTFB_SURFACE*/
+
+#endif /*CAIRO_DIRECTFB_H*/
diff --git a/src/cairo-drm.h b/src/cairo-drm.h
new file mode 100644 (file)
index 0000000..907610d
--- /dev/null
@@ -0,0 +1,120 @@
+/* Cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Chris Wilson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Chris Wilson.
+ */
+
+#ifndef CAIRO_DRM_H
+#define CAIRO_DRM_H
+
+#include "cairo.h"
+
+#if CAIRO_HAS_DRM_SURFACE
+
+CAIRO_BEGIN_DECLS
+
+struct udev_device;
+
+cairo_public cairo_device_t *
+cairo_drm_device_get (struct udev_device *device);
+
+cairo_public cairo_device_t *
+cairo_drm_device_get_for_fd (int fd);
+
+cairo_public cairo_device_t *
+cairo_drm_device_default (void);
+
+cairo_public int
+cairo_drm_device_get_fd (cairo_device_t *device);
+
+cairo_public void
+cairo_drm_device_throttle (cairo_device_t *device);
+
+cairo_public cairo_surface_t *
+cairo_drm_surface_create (cairo_device_t *device,
+                         cairo_format_t format,
+                         int width, int height);
+
+cairo_public cairo_surface_t *
+cairo_drm_surface_create_for_name (cairo_device_t *device,
+                                  unsigned int name,
+                                  cairo_format_t format,
+                                  int width, int height, int stride);
+
+cairo_public cairo_surface_t *
+cairo_drm_surface_create_from_cacheable_image (cairo_device_t *device,
+                                              cairo_surface_t *surface);
+
+cairo_public cairo_status_t
+cairo_drm_surface_enable_scan_out (cairo_surface_t *surface);
+
+cairo_public unsigned int
+cairo_drm_surface_get_handle (cairo_surface_t *surface);
+
+cairo_public unsigned int
+cairo_drm_surface_get_name (cairo_surface_t *surface);
+
+cairo_public cairo_format_t
+cairo_drm_surface_get_format (cairo_surface_t *surface);
+
+cairo_public int
+cairo_drm_surface_get_width (cairo_surface_t *surface);
+
+cairo_public int
+cairo_drm_surface_get_height (cairo_surface_t *surface);
+
+cairo_public int
+cairo_drm_surface_get_stride (cairo_surface_t *surface);
+
+/* XXX map/unmap, general surface layer? */
+
+/* Rough outline, culled from a conversation on IRC:
+ *   map() returns an image-surface representation of the drm-surface,
+ *   which you unmap() when you are finished, i.e. map() pulls the buffer back
+ *   from the GPU, maps it into the CPU domain and gives you direct access to
+ *   the pixels.  With the unmap(), the buffer is ready to be used again by the
+ *   GPU and *until* the unmap(), all operations will be done in software.
+ *
+ *  (Technically calling cairo_surface_flush() on the underlying drm-surface
+ *  will also disassociate the mapping.)
+*/
+cairo_public cairo_surface_t *
+cairo_drm_surface_map_to_image (cairo_surface_t *surface);
+
+cairo_public void
+cairo_drm_surface_unmap (cairo_surface_t *drm_surface,
+                        cairo_surface_t *image_surface);
+
+CAIRO_END_DECLS
+
+#else  /* CAIRO_HAS_DRM_SURFACE */
+# error Cairo was not compiled with support for the DRM backend
+#endif /* CAIRO_HAS_DRM_SURFACE */
+
+#endif /* CAIRO_DRM_H */
diff --git a/src/cairo-egl-context.c b/src/cairo-egl-context.c
new file mode 100644 (file)
index 0000000..ec23852
--- /dev/null
@@ -0,0 +1,230 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Eric Anholt
+ * Copyright © 2009 Chris Wilson
+ * Copyright © 2005 Red Hat, Inc
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ *     Carl Worth <cworth@cworth.org>
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-gl-private.h"
+
+#include "cairo-error-private.h"
+
+typedef struct _cairo_egl_context {
+    cairo_gl_context_t base;
+
+    EGLDisplay display;
+    EGLContext context;
+
+    EGLSurface dummy_surface;
+} cairo_egl_context_t;
+
+typedef struct _cairo_egl_surface {
+    cairo_gl_surface_t base;
+
+    EGLSurface egl;
+} cairo_egl_surface_t;
+
+
+static void
+_egl_acquire (void *abstract_ctx)
+{
+    cairo_egl_context_t *ctx = abstract_ctx;
+    EGLSurface current_surface;
+
+    if (ctx->base.current_target == NULL ||
+        _cairo_gl_surface_is_texture (ctx->base.current_target)) {
+        current_surface = ctx->dummy_surface;
+    } else {
+        cairo_egl_surface_t *surface = (cairo_egl_surface_t *) ctx->base.current_target;
+        current_surface = surface->egl ;
+    }
+
+    eglMakeCurrent (ctx->display,
+                   current_surface, current_surface, ctx->context);
+}
+
+static void
+_egl_release (void *abstract_ctx)
+{
+    cairo_egl_context_t *ctx = abstract_ctx;
+
+    eglMakeCurrent (ctx->display,
+                   EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+}
+
+static void
+_egl_make_current (void *abstract_ctx,
+                  cairo_gl_surface_t *abstract_surface)
+{
+    cairo_egl_context_t *ctx = abstract_ctx;
+    cairo_egl_surface_t *surface = (cairo_egl_surface_t *) abstract_surface;
+
+    eglMakeCurrent(ctx->display, surface->egl, surface->egl, ctx->context);
+}
+
+static void
+_egl_swap_buffers (void *abstract_ctx,
+                  cairo_gl_surface_t *abstract_surface)
+{
+    cairo_egl_context_t *ctx = abstract_ctx;
+    cairo_egl_surface_t *surface = (cairo_egl_surface_t *) abstract_surface;
+
+    eglSwapBuffers (ctx->display, surface->egl);
+}
+
+static void
+_egl_destroy (void *abstract_ctx)
+{
+    cairo_egl_context_t *ctx = abstract_ctx;
+
+    eglMakeCurrent (ctx->display,
+                   EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+    eglDestroySurface (ctx->display, ctx->dummy_surface);
+}
+
+static cairo_bool_t
+_egl_make_current_surfaceless(cairo_egl_context_t *ctx)
+{
+    const char *extensions;
+
+    extensions = eglQueryString(ctx->display, EGL_EXTENSIONS);
+    if (!strstr(extensions, "EGL_KHR_surfaceless_opengl"))
+       return FALSE;
+    if (!eglMakeCurrent(ctx->display,
+                       EGL_NO_SURFACE, EGL_NO_SURFACE, ctx->context))
+       return FALSE;
+
+    return TRUE;
+}
+
+cairo_device_t *
+cairo_egl_device_create (EGLDisplay dpy, EGLContext egl)
+{
+    cairo_egl_context_t *ctx;
+    cairo_status_t status;
+    int attribs[] = {
+       EGL_WIDTH, 1,
+       EGL_HEIGHT, 1,
+       EGL_NONE,
+    };
+    EGLConfig config;
+    EGLint numConfigs;
+
+    ctx = calloc (1, sizeof (cairo_egl_context_t));
+    if (unlikely (ctx == NULL))
+       return _cairo_gl_context_create_in_error (CAIRO_STATUS_NO_MEMORY);
+
+    ctx->display = dpy;
+    ctx->context = egl;
+
+    ctx->base.acquire = _egl_acquire;
+    ctx->base.release = _egl_release;
+    ctx->base.make_current = _egl_make_current;
+    ctx->base.swap_buffers = _egl_swap_buffers;
+    ctx->base.destroy = _egl_destroy;
+
+    if (!_egl_make_current_surfaceless (ctx)) {
+       /* Fall back to dummy surface, meh. */
+       EGLint config_attribs[] = {
+           EGL_CONFIG_ID, 0,
+           EGL_NONE
+       };
+
+       /*
+        * In order to be able to make an egl context current when using a
+        * pbuffer surface, that surface must have been created with a config
+        * that is compatible with the context config. For Mesa, this means
+        * that the configs must be the same.
+        */
+       eglQueryContext (dpy, egl, EGL_CONFIG_ID, &config_attribs[1]);
+       eglChooseConfig (dpy, config_attribs, &config, 1, &numConfigs);
+
+       ctx->dummy_surface = eglCreatePbufferSurface (dpy, config, attribs);
+
+       if (ctx->dummy_surface == NULL) {
+           free (ctx);
+           return _cairo_gl_context_create_in_error (CAIRO_STATUS_NO_MEMORY);
+       }
+
+       if (!eglMakeCurrent (dpy, ctx->dummy_surface, ctx->dummy_surface, egl)) {
+           free (ctx);
+           return _cairo_gl_context_create_in_error (CAIRO_STATUS_NO_MEMORY);
+       }
+    }
+
+    status = _cairo_gl_dispatch_init (&ctx->base.dispatch, eglGetProcAddress);
+    if (unlikely (status)) {
+       free (ctx);
+       return _cairo_gl_context_create_in_error (status);
+    }
+
+    status = _cairo_gl_context_init (&ctx->base);
+    if (unlikely (status)) {
+       if (ctx->dummy_surface != EGL_NO_SURFACE)
+           eglDestroySurface (dpy, ctx->dummy_surface);
+       free (ctx);
+       return _cairo_gl_context_create_in_error (status);
+    }
+
+    eglMakeCurrent (dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+
+    return &ctx->base.base;
+}
+
+cairo_surface_t *
+cairo_gl_surface_create_for_egl (cairo_device_t        *device,
+                                EGLSurface      egl,
+                                int             width,
+                                int             height)
+{
+    cairo_egl_surface_t *surface;
+
+    if (unlikely (device->status))
+       return _cairo_surface_create_in_error (device->status);
+
+    if (device->backend->type != CAIRO_DEVICE_TYPE_GL)
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH));
+
+    surface = calloc (1, sizeof (cairo_egl_surface_t));
+    if (unlikely (surface == NULL))
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+    _cairo_gl_surface_init (device, &surface->base,
+                           CAIRO_CONTENT_COLOR_ALPHA, width, height);
+    surface->egl = egl;
+
+    return &surface->base.base;
+}
diff --git a/src/cairo-error-private.h b/src/cairo-error-private.h
new file mode 100644 (file)
index 0000000..a548a35
--- /dev/null
@@ -0,0 +1,130 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *     Carl D. Worth <cworth@cworth.org>
+ */
+
+#ifndef _CAIRO_ERROR_PRIVATE_H_
+#define _CAIRO_ERROR_PRIVATE_H_
+
+#include "cairo.h"
+#include "cairo-compiler-private.h"
+#include "cairo-types-private.h"
+
+#include <assert.h>
+
+CAIRO_BEGIN_DECLS
+
+/* Sure wish C had a real enum type so that this would be distinct
+ * from #cairo_status_t. Oh well, without that, I'll use this bogus 100
+ * offset.  We want to keep it fit in int8_t as the compiler may choose
+ * that for #cairo_status_t */
+enum _cairo_int_status {
+    CAIRO_INT_STATUS_SUCCESS = 0,
+
+    CAIRO_INT_STATUS_NO_MEMORY,
+    CAIRO_INT_STATUS_INVALID_RESTORE,
+    CAIRO_INT_STATUS_INVALID_POP_GROUP,
+    CAIRO_INT_STATUS_NO_CURRENT_POINT,
+    CAIRO_INT_STATUS_INVALID_MATRIX,
+    CAIRO_INT_STATUS_INVALID_STATUS,
+    CAIRO_INT_STATUS_NULL_POINTER,
+    CAIRO_INT_STATUS_INVALID_STRING,
+    CAIRO_INT_STATUS_INVALID_PATH_DATA,
+    CAIRO_INT_STATUS_READ_ERROR,
+    CAIRO_INT_STATUS_WRITE_ERROR,
+    CAIRO_INT_STATUS_SURFACE_FINISHED,
+    CAIRO_INT_STATUS_SURFACE_TYPE_MISMATCH,
+    CAIRO_INT_STATUS_PATTERN_TYPE_MISMATCH,
+    CAIRO_INT_STATUS_INVALID_CONTENT,
+    CAIRO_INT_STATUS_INVALID_FORMAT,
+    CAIRO_INT_STATUS_INVALID_VISUAL,
+    CAIRO_INT_STATUS_FILE_NOT_FOUND,
+    CAIRO_INT_STATUS_INVALID_DASH,
+    CAIRO_INT_STATUS_INVALID_DSC_COMMENT,
+    CAIRO_INT_STATUS_INVALID_INDEX,
+    CAIRO_INT_STATUS_CLIP_NOT_REPRESENTABLE,
+    CAIRO_INT_STATUS_TEMP_FILE_ERROR,
+    CAIRO_INT_STATUS_INVALID_STRIDE,
+    CAIRO_INT_STATUS_FONT_TYPE_MISMATCH,
+    CAIRO_INT_STATUS_USER_FONT_IMMUTABLE,
+    CAIRO_INT_STATUS_USER_FONT_ERROR,
+    CAIRO_INT_STATUS_NEGATIVE_COUNT,
+    CAIRO_INT_STATUS_INVALID_CLUSTERS,
+    CAIRO_INT_STATUS_INVALID_SLANT,
+    CAIRO_INT_STATUS_INVALID_WEIGHT,
+    CAIRO_INT_STATUS_INVALID_SIZE,
+    CAIRO_INT_STATUS_USER_FONT_NOT_IMPLEMENTED,
+    CAIRO_INT_STATUS_DEVICE_TYPE_MISMATCH,
+    CAIRO_INT_STATUS_DEVICE_ERROR,
+    CAIRO_INT_STATUS_INVALID_MESH_CONSTRUCTION,
+    CAIRO_INT_STATUS_DEVICE_FINISHED,
+
+    CAIRO_INT_STATUS_LAST_STATUS,
+
+    CAIRO_INT_STATUS_UNSUPPORTED = 100,
+    CAIRO_INT_STATUS_DEGENERATE,
+    CAIRO_INT_STATUS_NOTHING_TO_DO,
+    CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY,
+    CAIRO_INT_STATUS_IMAGE_FALLBACK,
+    CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN,
+};
+
+typedef enum _cairo_int_status cairo_int_status_t;
+
+#define _cairo_status_is_error(status) \
+    (status != CAIRO_STATUS_SUCCESS && status < CAIRO_STATUS_LAST_STATUS)
+
+#define _cairo_int_status_is_error(status) \
+    (status != CAIRO_INT_STATUS_SUCCESS && status < CAIRO_INT_STATUS_LAST_STATUS)
+
+static inline cairo_status_t
+_cairo_public_status (cairo_int_status_t status)
+{
+    assert (status <= CAIRO_INT_STATUS_LAST_STATUS);
+    return (cairo_status_t) status;
+}
+
+cairo_private cairo_status_t
+_cairo_error (cairo_status_t status);
+
+/* hide compiler warnings when discarding the return value */
+#define _cairo_error_throw(status) do { \
+    cairo_status_t status__ = _cairo_error (status); \
+    (void) status__; \
+} while (0)
+
+CAIRO_END_DECLS
+
+#endif /* _CAIRO_ERROR_PRIVATE_H_ */
diff --git a/src/cairo-error.c b/src/cairo-error.c
new file mode 100644 (file)
index 0000000..1b9bd76
--- /dev/null
@@ -0,0 +1,73 @@
+/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *     Carl D. Worth <cworth@cworth.org>
+ */
+
+#include "cairoint.h"
+#include "cairo-private.h"
+
+#include "cairo-compiler-private.h"
+#include "cairo-error-private.h"
+
+#include <assert.h>
+
+/**
+ * _cairo_error:
+ * @status: a status value indicating an error, (eg. not
+ * %CAIRO_STATUS_SUCCESS)
+ *
+ * Checks that status is an error status, but does nothing else.
+ *
+ * All assignments of an error status to any user-visible object
+ * within the cairo application should result in a call to
+ * _cairo_error().
+ *
+ * The purpose of this function is to allow the user to set a
+ * breakpoint in _cairo_error() to generate a stack trace for when the
+ * user causes cairo to detect an error.
+ *
+ * Return value: the error status.
+ **/
+cairo_status_t
+_cairo_error (cairo_status_t status)
+{
+    CAIRO_ENSURE_UNIQUE;
+    assert (_cairo_status_is_error (status));
+
+    return status;
+}
+
+COMPILE_TIME_ASSERT ((int)CAIRO_INT_STATUS_LAST_STATUS == (int)CAIRO_STATUS_LAST_STATUS);
diff --git a/src/cairo-fallback-compositor.c b/src/cairo-fallback-compositor.c
new file mode 100644 (file)
index 0000000..105859d
--- /dev/null
@@ -0,0 +1,174 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ * Copyright © 2005 Red Hat, Inc.
+ * Copyright © 2011 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *     Carl D. Worth <cworth@cworth.org>
+ *      Joonas Pihlaja <jpihlaja@cc.helsinki.fi>
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-compositor-private.h"
+#include "cairo-surface-offset-private.h"
+
+/* high-level compositor interface */
+
+static cairo_int_status_t
+_cairo_fallback_compositor_paint (const cairo_compositor_t     *_compositor,
+                                 cairo_composite_rectangles_t  *extents)
+{
+    cairo_surface_t *image;
+    cairo_int_status_t status;
+
+    image = cairo_surface_map_to_image (extents->surface, &extents->unbounded);
+    status = _cairo_surface_offset_paint (image,
+                                         -extents->unbounded.x,
+                                         -extents->unbounded.y,
+                                         extents->op,
+                                         &extents->source_pattern.base,
+                                         extents->clip);
+    cairo_surface_unmap_image (extents->surface, image);
+
+    return status;
+}
+
+static cairo_int_status_t
+_cairo_fallback_compositor_mask (const cairo_compositor_t      *_compositor,
+                                cairo_composite_rectangles_t   *extents)
+{
+    cairo_surface_t *image;
+    cairo_int_status_t status;
+
+    image = cairo_surface_map_to_image (extents->surface, &extents->unbounded);
+    status = _cairo_surface_offset_mask (image,
+                                        extents->unbounded.x,
+                                        extents->unbounded.y,
+                                        extents->op,
+                                        &extents->source_pattern.base,
+                                        &extents->mask_pattern.base,
+                                        extents->clip);
+    cairo_surface_unmap_image (extents->surface, image);
+
+    return status;
+}
+
+static cairo_int_status_t
+_cairo_fallback_compositor_stroke (const cairo_compositor_t    *_compositor,
+                                  cairo_composite_rectangles_t *extents,
+                                  const cairo_path_fixed_t     *path,
+                                  const cairo_stroke_style_t   *style,
+                                  const cairo_matrix_t         *ctm,
+                                  const cairo_matrix_t         *ctm_inverse,
+                                  double                        tolerance,
+                                  cairo_antialias_t             antialias)
+{
+    cairo_surface_t *image;
+    cairo_int_status_t status;
+
+    image = cairo_surface_map_to_image (extents->surface, &extents->unbounded);
+    status = _cairo_surface_offset_stroke (image,
+                                          extents->unbounded.x,
+                                          extents->unbounded.y,
+                                          extents->op,
+                                          &extents->source_pattern.base,
+                                          path, style,
+                                          ctm, ctm_inverse,
+                                          tolerance,
+                                          antialias,
+                                          extents->clip);
+    cairo_surface_unmap_image (extents->surface, image);
+
+    return status;
+}
+
+static cairo_int_status_t
+_cairo_fallback_compositor_fill (const cairo_compositor_t      *_compositor,
+                                cairo_composite_rectangles_t *extents,
+                                const cairo_path_fixed_t       *path,
+                                cairo_fill_rule_t               fill_rule,
+                                double                          tolerance,
+                                cairo_antialias_t               antialias)
+{
+    cairo_surface_t *image;
+    cairo_int_status_t status;
+
+    image = cairo_surface_map_to_image (extents->surface, &extents->unbounded);
+    status = _cairo_surface_offset_fill (image,
+                                        extents->unbounded.x,
+                                        extents->unbounded.y,
+                                        extents->op,
+                                        &extents->source_pattern.base,
+                                        path,
+                                        fill_rule, tolerance, antialias,
+                                        extents->clip);
+    cairo_surface_unmap_image (extents->surface, image);
+
+    return status;
+}
+
+static cairo_int_status_t
+_cairo_fallback_compositor_glyphs (const cairo_compositor_t    *_compositor,
+                                  cairo_composite_rectangles_t *extents,
+                                  cairo_scaled_font_t          *scaled_font,
+                                  cairo_glyph_t                *glyphs,
+                                  int                           num_glyphs,
+                                  cairo_bool_t                  overlap)
+{
+    cairo_surface_t *image;
+    cairo_int_status_t status;
+
+    image = cairo_surface_map_to_image (extents->surface, &extents->unbounded);
+    status = _cairo_surface_offset_glyphs (image,
+                                          extents->unbounded.x,
+                                          extents->unbounded.y,
+                                          extents->op,
+                                          &extents->source_pattern.base,
+                                          scaled_font, glyphs, num_glyphs,
+                                          extents->clip);
+    cairo_surface_unmap_image (extents->surface, image);
+
+    return status;
+}
+
+const cairo_compositor_t _cairo_fallback_compositor = {
+     &__cairo_no_compositor,
+
+     _cairo_fallback_compositor_paint,
+     _cairo_fallback_compositor_mask,
+     _cairo_fallback_compositor_stroke,
+     _cairo_fallback_compositor_fill,
+     _cairo_fallback_compositor_glyphs,
+};
diff --git a/src/cairo-features-uninstalled.pc.in b/src/cairo-features-uninstalled.pc.in
new file mode 100644 (file)
index 0000000..b9cd9d3
--- /dev/null
@@ -0,0 +1,7 @@
+Name: @FEATURE_PC@
+Description: @FEATURE_NAME@ for cairo graphics library
+Version: @VERSION@
+
+Requires: @FEATURE_BASE@ @FEATURE_REQUIRES@
+Libs: @FEATURE_NONPKGCONFIG_LIBS@ @FEATURE_NONPKGCONFIG_EXTRA_LIBS@
+Cflags: -I${pc_top_builddir}/${pcfiledir}/@srcdir@/src @FEATURE_NONPKGCONFIG_CFLAGS@
diff --git a/src/cairo-features.pc.in b/src/cairo-features.pc.in
new file mode 100644 (file)
index 0000000..9a4b657
--- /dev/null
@@ -0,0 +1,12 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: @FEATURE_PC@
+Description: @FEATURE_NAME@ for cairo graphics library
+Version: @VERSION@
+
+Requires: @FEATURE_BASE@ @FEATURE_REQUIRES@
+Libs: @FEATURE_NONPKGCONFIG_LIBS@ @FEATURE_NONPKGCONFIG_EXTRA_LIBS@
+Cflags: -I${includedir}/cairo @FEATURE_NONPKGCONFIG_CFLAGS@
diff --git a/src/cairo-fixed-private.h b/src/cairo-fixed-private.h
new file mode 100644 (file)
index 0000000..9a16a03
--- /dev/null
@@ -0,0 +1,357 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* Cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2007 Mozilla Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation
+ *
+ * Contributor(s):
+ *     Vladimir Vukicevic <vladimir@pobox.com>
+ */
+
+#ifndef CAIRO_FIXED_PRIVATE_H
+#define CAIRO_FIXED_PRIVATE_H
+
+#include "cairo-fixed-type-private.h"
+
+#include "cairo-wideint-private.h"
+
+/* Implementation */
+
+#if (CAIRO_FIXED_BITS != 32)
+# error CAIRO_FIXED_BITS must be 32, and the type must be a 32-bit type.
+# error To remove this limitation, you will have to fix the tesselator.
+#endif
+
+#define CAIRO_FIXED_ONE        ((cairo_fixed_t)(1 << CAIRO_FIXED_FRAC_BITS))
+#define CAIRO_FIXED_ONE_DOUBLE ((double)(1 << CAIRO_FIXED_FRAC_BITS))
+#define CAIRO_FIXED_EPSILON    ((cairo_fixed_t)(1))
+
+#define CAIRO_FIXED_FRAC_MASK  ((cairo_fixed_t)(((cairo_fixed_unsigned_t)(-1)) >> (CAIRO_FIXED_BITS - CAIRO_FIXED_FRAC_BITS)))
+#define CAIRO_FIXED_WHOLE_MASK (~CAIRO_FIXED_FRAC_MASK)
+
+static inline cairo_fixed_t
+_cairo_fixed_from_int (int i)
+{
+    return i << CAIRO_FIXED_FRAC_BITS;
+}
+
+/* This is the "magic number" approach to converting a double into fixed
+ * point as described here:
+ *
+ * http://www.stereopsis.com/sree/fpu2006.html (an overview)
+ * http://www.d6.com/users/checker/pdfs/gdmfp.pdf (in detail)
+ *
+ * The basic idea is to add a large enough number to the double that the
+ * literal floating point is moved up to the extent that it forces the
+ * double's value to be shifted down to the bottom of the mantissa (to make
+ * room for the large number being added in). Since the mantissa is, at a
+ * given moment in time, a fixed point integer itself, one can convert a
+ * float to various fixed point representations by moving around the point
+ * of a floating point number through arithmetic operations. This behavior
+ * is reliable on most modern platforms as it is mandated by the IEEE-754
+ * standard for floating point arithmetic.
+ *
+ * For our purposes, a "magic number" must be carefully selected that is
+ * both large enough to produce the desired point-shifting effect, and also
+ * has no lower bits in its representation that would interfere with our
+ * value at the bottom of the mantissa. The magic number is calculated as
+ * follows:
+ *
+ *          (2 ^ (MANTISSA_SIZE - FRACTIONAL_SIZE)) * 1.5
+ *
+ * where in our case:
+ *  - MANTISSA_SIZE for 64-bit doubles is 52
+ *  - FRACTIONAL_SIZE for 16.16 fixed point is 16
+ *
+ * Although this approach provides a very large speedup of this function
+ * on a wide-array of systems, it does come with two caveats:
+ *
+ * 1) It uses banker's rounding as opposed to arithmetic rounding.
+ * 2) It doesn't function properly if the FPU is in single-precision
+ *    mode.
+ */
+
+/* The 16.16 number must always be available */
+#define CAIRO_MAGIC_NUMBER_FIXED_16_16 (103079215104.0)
+
+#if CAIRO_FIXED_BITS <= 32
+#define CAIRO_MAGIC_NUMBER_FIXED ((1LL << (52 - CAIRO_FIXED_FRAC_BITS)) * 1.5)
+
+/* For 32-bit fixed point numbers */
+static inline cairo_fixed_t
+_cairo_fixed_from_double (double d)
+{
+    union {
+        double d;
+        int32_t i[2];
+    } u;
+
+    u.d = d + CAIRO_MAGIC_NUMBER_FIXED;
+#ifdef FLOAT_WORDS_BIGENDIAN
+    return u.i[1];
+#else
+    return u.i[0];
+#endif
+}
+
+#else
+# error Please define a magic number for your fixed point type!
+# error See cairo-fixed-private.h for details.
+#endif
+
+static inline cairo_fixed_t
+_cairo_fixed_from_26_6 (uint32_t i)
+{
+#if CAIRO_FIXED_FRAC_BITS > 6
+    return i << (CAIRO_FIXED_FRAC_BITS - 6);
+#else
+    return i >> (6 - CAIRO_FIXED_FRAC_BITS);
+#endif
+}
+
+static inline cairo_fixed_t
+_cairo_fixed_from_16_16 (uint32_t i)
+{
+#if CAIRO_FIXED_FRAC_BITS > 16
+    return i << (CAIRO_FIXED_FRAC_BITS - 16);
+#else
+    return i >> (16 - CAIRO_FIXED_FRAC_BITS);
+#endif
+}
+
+static inline double
+_cairo_fixed_to_double (cairo_fixed_t f)
+{
+    return ((double) f) / CAIRO_FIXED_ONE_DOUBLE;
+}
+
+static inline int
+_cairo_fixed_is_integer (cairo_fixed_t f)
+{
+    return (f & CAIRO_FIXED_FRAC_MASK) == 0;
+}
+
+static inline cairo_fixed_t
+_cairo_fixed_floor (cairo_fixed_t f)
+{
+    return f & ~CAIRO_FIXED_FRAC_MASK;
+}
+
+static inline cairo_fixed_t
+_cairo_fixed_ceil (cairo_fixed_t f)
+{
+    return _cairo_fixed_floor (f + CAIRO_FIXED_FRAC_MASK);
+}
+
+static inline cairo_fixed_t
+_cairo_fixed_round (cairo_fixed_t f)
+{
+    return _cairo_fixed_floor (f + (CAIRO_FIXED_FRAC_MASK+1)/2);
+}
+
+static inline cairo_fixed_t
+_cairo_fixed_round_down (cairo_fixed_t f)
+{
+    return _cairo_fixed_floor (f + CAIRO_FIXED_FRAC_MASK/2);
+}
+
+static inline int
+_cairo_fixed_integer_part (cairo_fixed_t f)
+{
+    return f >> CAIRO_FIXED_FRAC_BITS;
+}
+
+static inline int
+_cairo_fixed_integer_round (cairo_fixed_t f)
+{
+    return _cairo_fixed_integer_part (f + (CAIRO_FIXED_FRAC_MASK+1)/2);
+}
+
+static inline int
+_cairo_fixed_integer_round_down (cairo_fixed_t f)
+{
+    return _cairo_fixed_integer_part (f + CAIRO_FIXED_FRAC_MASK/2);
+}
+
+static inline int
+_cairo_fixed_fractional_part (cairo_fixed_t f)
+{
+    return f & CAIRO_FIXED_FRAC_MASK;
+}
+
+static inline int
+_cairo_fixed_integer_floor (cairo_fixed_t f)
+{
+    if (f >= 0)
+        return f >> CAIRO_FIXED_FRAC_BITS;
+    else
+        return -((-f - 1) >> CAIRO_FIXED_FRAC_BITS) - 1;
+}
+
+static inline int
+_cairo_fixed_integer_ceil (cairo_fixed_t f)
+{
+    if (f > 0)
+       return ((f - 1)>>CAIRO_FIXED_FRAC_BITS) + 1;
+    else
+       return - (-f >> CAIRO_FIXED_FRAC_BITS);
+}
+
+/* A bunch of explicit 16.16 operators; we need these
+ * to interface with pixman and other backends that require
+ * 16.16 fixed point types.
+ */
+static inline cairo_fixed_16_16_t
+_cairo_fixed_to_16_16 (cairo_fixed_t f)
+{
+#if (CAIRO_FIXED_FRAC_BITS == 16) && (CAIRO_FIXED_BITS == 32)
+    return f;
+#elif CAIRO_FIXED_FRAC_BITS > 16
+    /* We're just dropping the low bits, so we won't ever got over/underflow here */
+    return f >> (CAIRO_FIXED_FRAC_BITS - 16);
+#else
+    cairo_fixed_16_16_t x;
+
+    /* Handle overflow/underflow by clamping to the lowest/highest
+     * value representable as 16.16
+     */
+    if ((f >> CAIRO_FIXED_FRAC_BITS) < INT16_MIN) {
+       x = INT32_MIN;
+    } else if ((f >> CAIRO_FIXED_FRAC_BITS) > INT16_MAX) {
+       x = INT32_MAX;
+    } else {
+       x = f << (16 - CAIRO_FIXED_FRAC_BITS);
+    }
+
+    return x;
+#endif
+}
+
+static inline cairo_fixed_16_16_t
+_cairo_fixed_16_16_from_double (double d)
+{
+    union {
+        double d;
+        int32_t i[2];
+    } u;
+
+    u.d = d + CAIRO_MAGIC_NUMBER_FIXED_16_16;
+#ifdef FLOAT_WORDS_BIGENDIAN
+    return u.i[1];
+#else
+    return u.i[0];
+#endif
+}
+
+static inline int
+_cairo_fixed_16_16_floor (cairo_fixed_16_16_t f)
+{
+    if (f >= 0)
+       return f >> 16;
+    else
+       return -((-f - 1) >> 16) - 1;
+}
+
+static inline double
+_cairo_fixed_16_16_to_double (cairo_fixed_16_16_t f)
+{
+    return ((double) f) / (double) (1 << 16);
+}
+
+#if CAIRO_FIXED_BITS == 32
+
+static inline cairo_fixed_t
+_cairo_fixed_mul (cairo_fixed_t a, cairo_fixed_t b)
+{
+    cairo_int64_t temp = _cairo_int32x32_64_mul (a, b);
+    return _cairo_int64_to_int32(_cairo_int64_rsl (temp, CAIRO_FIXED_FRAC_BITS));
+}
+
+/* computes round (a * b / c) */
+static inline cairo_fixed_t
+_cairo_fixed_mul_div (cairo_fixed_t a, cairo_fixed_t b, cairo_fixed_t c)
+{
+    cairo_int64_t ab  = _cairo_int32x32_64_mul (a, b);
+    cairo_int64_t c64 = _cairo_int32_to_int64 (c);
+    return _cairo_int64_to_int32 (_cairo_int64_divrem (ab, c64).quo);
+}
+
+/* computes floor (a * b / c) */
+static inline cairo_fixed_t
+_cairo_fixed_mul_div_floor (cairo_fixed_t a, cairo_fixed_t b, cairo_fixed_t c)
+{
+    return _cairo_int64_32_div (_cairo_int32x32_64_mul (a, b), c);
+}
+
+
+static inline cairo_fixed_t
+_cairo_edge_compute_intersection_y_for_x (const cairo_point_t *p1,
+                                         const cairo_point_t *p2,
+                                         cairo_fixed_t x)
+{
+    cairo_fixed_t y, dx;
+
+    if (x == p1->x)
+       return p1->y;
+    if (x == p2->x)
+       return p2->y;
+
+    y = p1->y;
+    dx = p2->x - p1->x;
+    if (dx != 0)
+       y += _cairo_fixed_mul_div_floor (x - p1->x, p2->y - p1->y, dx);
+
+    return y;
+}
+
+static inline cairo_fixed_t
+_cairo_edge_compute_intersection_x_for_y (const cairo_point_t *p1,
+                                         const cairo_point_t *p2,
+                                         cairo_fixed_t y)
+{
+    cairo_fixed_t x, dy;
+
+    if (y == p1->y)
+       return p1->x;
+    if (y == p2->y)
+       return p2->x;
+
+    x = p1->x;
+    dy = p2->y - p1->y;
+    if (dy != 0)
+       x += _cairo_fixed_mul_div_floor (y - p1->y, p2->x - p1->x, dy);
+
+    return x;
+}
+
+#else
+# error Please define multiplication and other operands for your fixed-point type size
+#endif
+
+#endif /* CAIRO_FIXED_PRIVATE_H */
diff --git a/src/cairo-fixed-type-private.h b/src/cairo-fixed-type-private.h
new file mode 100644 (file)
index 0000000..2bbd5f7
--- /dev/null
@@ -0,0 +1,75 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* Cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2007 Mozilla Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation
+ *
+ * Contributor(s):
+ *     Vladimir Vukicevic <vladimir@pobox.com>
+ */
+
+#ifndef CAIRO_FIXED_TYPE_PRIVATE_H
+#define CAIRO_FIXED_TYPE_PRIVATE_H
+
+#include "cairo-wideint-type-private.h"
+
+/*
+ * Fixed-point configuration
+ */
+
+typedef int32_t                cairo_fixed_16_16_t;
+typedef cairo_int64_t  cairo_fixed_32_32_t;
+typedef cairo_int64_t  cairo_fixed_48_16_t;
+typedef cairo_int128_t cairo_fixed_64_64_t;
+typedef cairo_int128_t cairo_fixed_96_32_t;
+
+/* Eventually, we should allow changing this, but I think
+ * there are some assumptions in the tesselator about the
+ * size of a fixed type.  For now, it must be 32.
+ */
+#define CAIRO_FIXED_BITS       32
+
+/* The number of fractional bits.  Changing this involves
+ * making sure that you compute a double-to-fixed magic number.
+ * (see below).
+ */
+#define CAIRO_FIXED_FRAC_BITS  8
+
+/* A signed type %CAIRO_FIXED_BITS in size; the main fixed point type */
+typedef int32_t cairo_fixed_t;
+
+/* An unsigned type of the same size as #cairo_fixed_t */
+typedef uint32_t cairo_fixed_unsigned_t;
+
+typedef struct _cairo_point {
+    cairo_fixed_t x;
+    cairo_fixed_t y;
+} cairo_point_t;
+
+#endif /* CAIRO_FIXED_TYPE_PRIVATE_H */
diff --git a/src/cairo-fixed.c b/src/cairo-fixed.c
new file mode 100644 (file)
index 0000000..03e0559
--- /dev/null
@@ -0,0 +1,39 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2003 University of Southern California
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *     Carl D. Worth <cworth@cworth.org>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-fixed-private.h"
diff --git a/src/cairo-font-face-twin-data.c b/src/cairo-font-face-twin-data.c
new file mode 100644 (file)
index 0000000..ff09cb2
--- /dev/null
@@ -0,0 +1,1072 @@
+/* See cairo-font-face-twin.c for copyright info */
+
+#include "cairoint.h"
+
+const int8_t _cairo_twin_outlines[] = {
+/* 0x0 '\0'  offset 0 */
+    0, 24, 42, 0, 2, 2,
+    0, 24, /* snap_x */
+    -42, 0, /* snap_y */
+    'm', 0, 0,
+    'l', 0, -42,
+    'l', 24, -42,
+    'l', 24, 0,
+    'l', 0, 0,
+    'e',
+    'X', 'X',
+/* 0x20 ' '  offset 28 */
+    0, 4, 0, 0, 0, 0,
+    /* snap_x */
+    /* snap_y */
+    'e',
+    'X', 'X', 'X',
+    'X', 'X',
+/* 0x21 '!'  offset 40 */
+    0, 0, 42, 0, 1, 3,
+    0, /* snap_x */
+    -42, -14, 0, /* snap_y */
+    'm', 0, -42,
+    'l', 0, -14,
+    'm', 0, 0,
+    'l', 0, 0,
+    'e',
+    'X', 'X', 'X', 'X', 'X', 'X',
+    'X', 'X', 'X', 'X', 'X', 'X',
+    'X', 'X', 'X', 'X', 'X', 'X',
+    'X', 'X', 'X', 'X', 'X', 'X',
+    'X', 'X', 'X',
+/* 0x22 '"'  offset 90 */
+    0, 16, 42, -28, 2, 2,
+    0, 16, /* snap_x */
+    -42, -28, /* snap_y */
+    'm', 0, -42,
+    'l', 0, -28,
+    'm', 16, -42,
+    'l', 16, -28,
+    'e',
+    'X',
+/* 0x23 '#'  offset 114 */
+    0, 30, 50, 14, 2, 5,
+    0, 30, /* snap_x */
+    -24, -21, -15, -12, 0, /* snap_y */
+    'm', 16, -50,
+    'l', 2, 14,
+    'm', 28, -50,
+    'l', 14, 14,
+    'm', 2, -24,
+    'l', 30, -24,
+    'm', 0, -12,
+    'l', 28, -12,
+    'e',
+/* 0x24 '$'  offset 152 */
+    0, 28, 50, 8, 4, 4,
+    0, 10, 18, 28, /* snap_x */
+    -42, -21, -15, 0, /* snap_y */
+    'm', 10, -50,
+    'l', 10, 8,
+    'm', 18, -50,
+    'l', 18, 8,
+    'm', 28, -36,
+    'c', 24, -42, 18, -42, 14, -42,
+    'c', 10, -42, 0, -42, 0, -34,
+    'c', 0, -25, 8, -24, 14, -22,
+    'c', 20, -20, 28, -19, 28, -9,
+    'c', 28, 0, 18, 0, 14, 0,
+    'c', 10, 0, 4, 0, 0, -6,
+    'e',
+/* 0x25 '%'  offset 224 */
+    0, 36, 42, 0, 4, 7,
+    0, 14, 22, 36, /* snap_x */
+    -42, -38, -28, -21, -15, -14, 0, /* snap_y */
+    'm', 10, -42,
+    'c', 12, -41, 14, -40, 14, -36,
+    'c', 14, -30, 11, -28, 6, -28,
+    'c', 2, -28, 0, -30, 0, -34,
+    'c', 0, -39, 3, -42, 8, -42,
+    'l', 10, -42,
+    'c', 18, -37, 28, -37, 36, -42,
+    'l', 0, 0,
+    'm', 28, -14,
+    'c', 24, -14, 22, -11, 22, -6,
+    'c', 22, -2, 24, 0, 28, 0,
+    'c', 33, 0, 36, -2, 36, -8,
+    'c', 36, -12, 34, -14, 30, -14,
+    'l', 28, -14,
+    'e',
+    'X', 'X', 'X',
+/* 0x26 '&'  offset 323 */
+    0, 40, 42, 0, 4, 4,
+    0, 10, 22, 40, /* snap_x */
+    -28, -21, -15, 0, /* snap_y */
+    'm', 40, -24,
+    'c', 40, -27, 39, -28, 37, -28,
+    'c', 29, -28, 32, 0, 12, 0,
+    'c', 0, 0, 0, -8, 0, -10,
+    'c', 0, -24, 22, -20, 22, -34,
+    'c', 22, -45, 10, -45, 10, -34,
+    'c', 10, -27, 25, 0, 36, 0,
+    'c', 39, 0, 40, -1, 40, -4,
+    'e',
+/* 0x27 '''  offset 390 */
+    0, 4, 42, -30, 2, 2,
+    0, 4, /* snap_x */
+    -42, -28, /* snap_y */
+    'm', 2, -38,
+    'c', -1, -38, -1, -42, 2, -42,
+    'c', 6, -42, 5, -33, 0, -30,
+    'e',
+    'X',
+/* 0x28 '('  offset 419 */
+    0, 14, 50, 14, 2, 2,
+    0, 14, /* snap_x */
+    -50, 14, /* snap_y */
+    'm', 14, -50,
+    'c', -5, -32, -5, -5, 14, 14,
+    'e',
+    'X',
+/* 0x29 ')'  offset 441 */
+    0, 14, 50, 14, 2, 2,
+    0, 14, /* snap_x */
+    -15, 14, /* snap_y */
+    'm', 0, -50,
+    'c', 19, -34, 19, -2, 0, 14,
+    'e',
+    'X',
+/* 0x2a '*'  offset 463 */
+    0, 20, 30, -6, 3, 3,
+    0, 10, 20, /* snap_x */
+    -21, -15, 0, /* snap_y */
+    'm', 10, -30,
+    'l', 10, -6,
+    'm', 0, -24,
+    'l', 20, -12,
+    'm', 20, -24,
+    'l', 0, -12,
+    'e',
+/* 0x2b '+'  offset 494 */
+    0, 36, 36, 0, 3, 4,
+    0, 18, 36, /* snap_x */
+    -21, -18, -15, 0, /* snap_y */
+    'm', 18, -36,
+    'l', 18, 0,
+    'm', 0, -18,
+    'l', 36, -18,
+    'e',
+/* 0x2c ','  offset 520 */
+    0, 4, 4, 8, 2, 3,
+    0, 4, /* snap_x */
+    -21, -15, 0, /* snap_y */
+    'm', 4, -2,
+    'c', 4, 1, 0, 1, 0, -2,
+    'c', 0, -5, 4, -5, 4, -2,
+    'c', 4, 4, 2, 6, 0, 8,
+    'e',
+/* 0x2d '-'  offset 556 */
+    0, 36, 18, -18, 2, 4,
+    0, 36, /* snap_x */
+    -21, -18, -15, 0, /* snap_y */
+    'm', 0, -18,
+    'l', 36, -18,
+    'e',
+/* 0x2e '.'  offset 575 */
+    0, 4, 4, 0, 2, 3,
+    0, 4, /* snap_x */
+    -21, -15, 0, /* snap_y */
+    'm', 2, -4,
+    'c', -1, -4, -1, 0, 2, 0,
+    'c', 5, 0, 5, -4, 2, -4,
+    'e',
+/* 0x2f '/'  offset 604 */
+    0, 36, 50, 14, 2, 3,
+    0, 36, /* snap_x */
+    -21, -15, 0, /* snap_y */
+    'm', 36, -50,
+    'l', 0, 14,
+    'e',
+/* 0x30 '0'  offset 622 */
+    0, 28, 42, 0, 2, 4,
+    0, 28, /* snap_x */
+    -42, -21, -15, 0, /* snap_y */
+    'm', 14, -42,
+    'c', 9, -42, 0, -42, 0, -21,
+    'c', 0, 0, 9, 0, 14, 0,
+    'c', 19, 0, 28, 0, 28, -21,
+    'c', 28, -42, 19, -42, 14, -42,
+    'E',
+/* 0x31 '1'  offset 666 */
+    0, 28, 42, 0, 2, 3,
+    0, 17, 28 /* snap_x */
+    -42, -34, 0, /* snap_y */
+    'm', 7, -34,
+    'c', 11, -35, 15, -38, 17, -42,
+    'l', 17, 0,
+    'e',
+/* 0x32 '2'  offset 691 */
+    0, 28, 42, 0, 4, 4,
+    0, 2, 26, 28, /* snap_x */
+    -42, -21, -15, 0, /* snap_y */
+    'm', 2, -32,
+    'c', 2, -34, 2, -42, 14, -42,
+    'c', 26, -42, 26, -34, 26, -32,
+    'c', 26, -30, 25, -25, 10, -10,
+    'l', 0, 0,
+    'l', 28, 0,
+    'e',
+/* 0x33 '3'  offset 736 */
+    0, 28, 42, 0, 2, 5,
+    0, 28, /* snap_x */
+    -42, -26, -21, -15, 0, /* snap_y */
+    'm', 4, -42,
+    'l', 26, -42,
+    'l', 14, -26,
+    'c', 21, -26, 28, -26, 28, -14,
+    'c', 28, 0, 17, 0, 13, 0,
+    'c', 8, 0, 3, -1, 0, -8,
+    'e',
+/* 0x34 '4'  offset 780 */
+    0, 28, 42, 0, 3, 3,
+    0, 20, 30, /* snap_x */
+    -42, -14, 0, /* snap_y */
+    'm', 20, 0,
+    'l', 20, -42,
+    'l', 0, -14,
+    'l', 30, -14,
+    'e',
+    'X', 'X', 'X',
+    'X',
+/* 0x35 '5'  offset 809 */
+    0, 28, 42, 0, 2, 5,
+    0, 28, /* snap_x */
+    -42, -28, -21, -15, 0, /* snap_y */
+    'm', 24, -42,
+    'l', 4, -42,
+    'l', 2, -24,
+    'c', 5, -27, 10, -28, 13, -28,
+    'c', 16, -28, 28, -28, 28, -14,
+    'c', 28, 0, 16, 0, 13, 0,
+    'c', 10, 0, 3, 0, 0, -8,
+    'e',
+/* 0x36 '6'  offset 860 */
+    0, 28, 42, 0, 2, 5,
+    0, 26, /* snap_x */
+    -42, -26, -21, -15, 0, /* snap_y */
+    'm', 24, -36,
+    'c', 22, -41, 19, -42, 14, -42,
+    'c', 9, -42, 0, -41, 0, -19,
+    'c', 0, -1, 9, 0, 13, 0,
+    'c', 18, 0, 26, -3, 26, -13,
+    'c', 26, -18, 23, -26, 13, -26,
+    'c', 10, -26, 1, -24, 0, -14,
+    'e',
+/* 0x37 '7'  offset 919 */
+    0, 28, 42, 0, 2, 4,
+    0, 28, /* snap_x */
+    -42, -21, -15, 0, /* snap_y */
+    'm', 0, -42,
+    'l', 28, -42,
+    'l', 8, 0,
+    'e',
+    'X', 'X', 'X',
+/* 0x38 '8'  offset 944 */
+    0, 28, 42, 0, 4, 4,
+    0, 2, 26, 28, /* snap_x */
+    -42, -21, -15, 0, /* snap_y */
+    'm', 14, -42,
+    'c', 5, -42, 2, -40, 2, -34,
+    'c', 2, -18, 28, -32, 28, -11,
+    'c', 28, 0, 18, 0, 14, 0,
+    'c', 10, 0, 0, 0, 0, -11,
+    'c', 0, -32, 26, -18, 26, -34,
+    'c', 26, -40, 23, -42, 14, -42,
+    'E',
+/* 0x39 '9'  offset 1004 */
+    0, 28, 42, 0, 2, 5,
+    0, 26, /* snap_x */
+    -42, -21, -16, -15, 0, /* snap_y */
+    'm', 26, -28,
+    'c', 25, -16, 13, -16, 13, -16,
+    'c', 8, -16, 0, -19, 0, -29,
+    'c', 0, -34, 3, -42, 13, -42,
+    'c', 24, -42, 26, -32, 26, -23,
+    'c', 26, -14, 24, 0, 12, 0,
+    'c', 7, 0, 4, -2, 2, -6,
+    'e',
+/* 0x3a ':'  offset 1063 */
+    0, 4, 28, 0, 2, 3,
+    0, 4, /* snap_x */
+    -21, -15, 0, /* snap_y */
+    'm', 2, -28,
+    'c', -1, -28, -1, -24, 2, -24,
+    'c', 5, -24, 5, -28, 2, -28,
+    'm', 2, -4,
+    'c', -1, -4, -1, 0, 2, 0,
+    'c', 5, 0, 5, -4, 2, -4,
+    'e',
+/* 0x3b ';'  offset 1109 */
+    0, 4, 28, 8, 2, 3,
+    0, 4, /* snap_x */
+    -21, -15, 0, /* snap_y */
+    'm', 2, -28,
+    'c', -1, -28, -1, -24, 2, -24,
+    'c', 5, -24, 5, -28, 2, -28,
+    'm', 4, -2,
+    'c', 4, 1, 0, 1, 0, -2,
+    'c', 0, -5, 4, -5, 4, -2,
+    'c', 4, 3, 2, 6, 0, 8,
+    'e',
+/* 0x3c '<'  offset 1162 */
+    0, 32, 36, 0, 2, 3,
+    0, 32, /* snap_x */
+    -36, -18, 0, /* snap_y */
+    'm', 32, -36,
+    'l', 0, -18,
+    'l', 32, 0,
+    'e',
+/* 0x3d '='  offset 1183 */
+    0, 36, 24, -12, 2, 2,
+    0, 36, /* snap_x */
+    -24, -15, /* snap_y */
+    'm', 0, -24,
+    'l', 36, -24,
+    'm', 0, -12,
+    'l', 36, -12,
+    'e',
+    'X', 'X', 'X',
+/* 0x3e '>'  offset 1209 */
+    0, 32, 36, 0, 2, 3,
+    0, 32, /* snap_x */
+    -36, -18, 0, /* snap_y */
+    'm', 0, -36,
+    'l', 32, -18,
+    'l', 0, 0,
+    'e',
+/* 0x3f '?'  offset 1230 */
+    0, 24, 42, 0, 3, 4,
+    0, 12, 24, /* snap_x */
+    -42, -21, -15, 0, /* snap_y */
+    'm', 0, -32,
+    'c', 0, -34, 0, -42, 12, -42,
+    'c', 24, -42, 24, -34, 24, -32,
+    'c', 24, -29, 24, -24, 12, -20,
+    'l', 12, -14,
+    'm', 12, 0,
+    'l', 12, 0,
+    'e',
+    'X', 'X', 'X',
+    'X', 'X', 'X',
+    'X', 'X', 'X',
+    'X', 'X',
+/* 0x40 '@'  offset 1288 */
+    0, 42, 42, 0, 1, 6,
+    30, /* snap_x */
+    -42, -32, -21, -15, -10, 0, /* snap_y */
+    'm', 30, -26,
+    'c', 28, -31, 24, -32, 21, -32,
+    'c', 10, -32, 10, -23, 10, -19,
+    'c', 10, -13, 11, -10, 19, -10,
+    'c', 30, -10, 28, -21, 30, -32,
+    'c', 27, -10, 30, -10, 34, -10,
+    'c', 41, -10, 42, -19, 42, -22,
+    'c', 42, -34, 34, -42, 21, -42,
+    'c', 9, -42, 0, -34, 0, -21,
+    'c', 0, -9, 8, 0, 21, 0,
+    'c', 30, 0, 34, -3, 36, -6,
+    'e',
+/* 0x41 'A'  offset 1375 */
+    0, 32, 42, 0, 2, 3,
+    0, 32, /* snap_x */
+    -42, -14, 0, /* snap_y */
+    'm', 0, 0,
+    'l', 16, -42,
+    'l', 32, 0,
+    'm', 6, -14,
+    'l', 26, -14,
+    'e',
+    'X', 'X', 'X',
+    'X',
+/* 0x42 'B'  offset 1406 */
+    0, 28, 42, 0, 2, 3,
+    0, 28, /* snap_x */
+    -42, -22, 0, /* snap_y */
+    'm', 0, 0,
+    'l', 0, -42,
+    'l', 18, -42,
+    'c', 32, -42, 32, -22, 18, -22,
+    'l', 0, -22,
+    'l', 18, -22,
+    'c', 32, -22, 32, 0, 18, 0,
+    'E',
+    'X', 'X', 'X',
+    'X', 'X', 'X',
+    'X', 'X',
+/* 0x43 'C'  offset 1455 */
+    0, 30, 42, 0, 2, 4,
+    0, 30, /* snap_x */
+    -42, -21, -15, 0, /* snap_y */
+    'm', 30, -32,
+    'c', 26, -42, 21, -42, 16, -42,
+    'c', 2, -42, 0, -29, 0, -21,
+    'c', 0, -13, 2, 0, 16, 0,
+    'c', 21, 0, 26, 0, 30, -10,
+    'e',
+/* 0x44 'D'  offset 1499 */
+    0, 28, 42, 0, 2, 2,
+    0, 28, /* snap_x */
+    -42, 0, /* snap_y */
+    'm', 0, 0,
+    'l', 0, -42,
+    'l', 14, -42,
+    'c', 33, -42, 33, 0, 14, 0,
+    'E',
+    'X', 'X', 'X',
+    'X', 'X', 'X',
+    'X', 'X',
+/* 0x45 'E'  offset 1534 */
+    0, 26, 42, 0, 2, 3,
+    0, 26, /* snap_x */
+    -42, -22, 0, /* snap_y */
+    'm', 26, -42,
+    'l', 0, -42,
+    'l', 0, 0,
+    'l', 26, 0,
+    'm', 0, -22,
+    'l', 16, -22,
+    'e',
+    'X', 'X', 'X',
+    'X', 'X', 'X',
+    'X', 'X',
+/* 0x46 'F'  offset 1572 */
+    0, 26, 42, 0, 2, 3,
+    0, 26, /* snap_x */
+    -42, -22, 0, /* snap_y */
+    'm', 0, 0,
+    'l', 0, -42,
+    'l', 26, -42,
+    'm', 0, -22,
+    'l', 16, -22,
+    'e',
+    'X', 'X', 'X',
+    'X', 'X',
+/* 0x47 'G'  offset 1604 */
+    0, 30, 42, 0, 2, 5,
+    0, 30, /* snap_x */
+    -42, -21, -16, -15, 0, /* snap_y */
+    'm', 30, -32,
+    'c', 26, -42, 21, -42, 16, -42,
+    'c', 2, -42, 0, -29, 0, -21,
+    'c', 0, -13, 2, 0, 16, 0,
+    'c', 28, 0, 30, -7, 30, -16,
+    'l', 20, -16,
+    'e',
+    'X', 'X', 'X',
+/* 0x48 'H'  offset 1655 */
+    0, 28, 42, 0, 2, 3,
+    0, 28, /* snap_x */
+    -42, -22, 0, /* snap_y */
+    'm', 0, -42,
+    'l', 0, 0,
+    'm', 28, -42,
+    'l', 28, 0,
+    'm', 0, -22,
+    'l', 28, -22,
+    'e',
+    'X',
+/* 0x49 'I'  offset 1686 */
+    0, 0, 42, 0, 1, 2,
+    0, /* snap_x */
+    -42, 0, /* snap_y */
+    'm', 0, -42,
+    'l', 0, 0,
+    'e',
+    'X',
+/* 0x4a 'J'  offset 1703 */
+    0, 20, 42, 0, 2, 3,
+    0, 20, /* snap_x */
+    -42, -15, 0, /* snap_y */
+    'm', 20, -42,
+    'l', 20, -10,
+    'c', 20, 3, 0, 3, 0, -10,
+    'l', 0, -14,
+    'e',
+/* 0x4b 'K'  offset 1731 */
+    0, 28, 42, 0, 2, 3,
+    0, 28, /* snap_x */
+    -42, -15, 0, /* snap_y */
+    'm', 0, -42,
+    'l', 0, 0,
+    'm', 28, -42,
+    'l', 0, -14,
+    'm', 10, -24,
+    'l', 28, 0,
+    'e',
+/* 0x4c 'L'  offset 1761 */
+    0, 24, 42, 0, 2, 2,
+    0, 24, /* snap_x */
+    -42, 0, /* snap_y */
+    'm', 0, -42,
+    'l', 0, 0,
+    'l', 24, 0,
+    'e',
+    'X', 'X', 'X',
+    'X',
+/* 0x4d 'M'  offset 1785 */
+    0, 32, 42, 0, 2, 2,
+    0, 32, /* snap_x */
+    -42, 0, /* snap_y */
+    'm', 0, 0,
+    'l', 0, -42,
+    'l', 16, 0,
+    'l', 32, -42,
+    'l', 32, 0,
+    'e',
+    'X', 'X', 'X',
+    'X', 'X', 'X',
+    'X', 'X', 'X',
+    'X',
+/* 0x4e 'N'  offset 1821 */
+    0, 28, 42, 0, 2, 2,
+    0, 28, /* snap_x */
+    -42, 0, /* snap_y */
+    'm', 0, 0,
+    'l', 0, -42,
+    'l', 28, 0,
+    'l', 28, -42,
+    'e',
+    'X', 'X', 'X',
+    'X', 'X', 'X',
+    'X',
+/* 0x4f 'O'  offset 1851 */
+    0, 32, 42, 0, 2, 4,
+    0, 32, /* snap_x */
+    -42, -21, -15, 0, /* snap_y */
+    'm', 16, -42,
+    'c', 2, -42, 0, -29, 0, -21,
+    'c', 0, -13, 2, 0, 16, 0,
+    'c', 30, 0, 32, -13, 32, -21,
+    'c', 32, -29, 30, -42, 16, -42,
+    'E',
+/* 0x50 'P'  offset 1895 */
+    0, 28, 42, 0, 2, 5,
+    0, 28, /* snap_x */
+    -42, -21, -20, -15, 0, /* snap_y */
+    'm', 0, 0,
+    'l', 0, -42,
+    'l', 18, -42,
+    'c', 32, -42, 32, -20, 18, -20,
+    'l', 0, -20,
+    'e',
+    'X', 'X', 'X',
+/* 0x51 'Q'  offset 1931 */
+    0, 32, 42, 4, 2, 4,
+    0, 32, /* snap_x */
+    -42, -21, -15, 0, /* snap_y */
+    'm', 16, -42,
+    'c', 2, -42, 0, -29, 0, -21,
+    'c', 0, -13, 2, 0, 16, 0,
+    'c', 30, 0, 32, -13, 32, -21,
+    'c', 32, -29, 30, -42, 16, -42,
+    'M', 18, -8,
+    'l', 30, 4,
+    'e',
+/* 0x52 'R'  offset 1981 */
+    0, 28, 42, 0, 2, 5,
+    0, 28, /* snap_x */
+    -42, -22, -21, -15, 0, /* snap_y */
+    'm', 0, 0,
+    'l', 0, -42,
+    'l', 18, -42,
+    'c', 32, -42, 31, -22, 18, -22,
+    'l', 0, -22,
+    'm', 14, -22,
+    'l', 28, 0,
+    'e',
+    'X', 'X', 'X',
+/* 0x53 'S'  offset 2023 */
+    0, 28, 42, 0, 2, 4,
+    0, 28, /* snap_x */
+    -42, -21, -15, 0, /* snap_y */
+    'm', 28, -36,
+    'c', 25, -41, 21, -42, 14, -42,
+    'c', 10, -42, 0, -42, 0, -34,
+    'c', 0, -17, 28, -28, 28, -9,
+    'c', 28, 0, 19, 0, 14, 0,
+    'c', 7, 0, 3, -1, 0, -6,
+    'e',
+/* 0x54 'T'  offset 2074 */
+    0, 28, 42, 0, 3, 4,
+    0, 14, 28, /* snap_x */
+    -42, -21, -15, 0, /* snap_y */
+    'm', 14, -42,
+    'l', 14, 0,
+    'm', 0, -42,
+    'l', 28, -42,
+    'e',
+/* 0x55 'U'  offset 2100 */
+    0, 28, 42, 0, 2, 2,
+    0, 28, /* snap_x */
+    -42, 0, /* snap_y */
+    'm', 0, -42,
+    'l', 0, -12,
+    'c', 0, 4, 28, 4, 28, -12,
+    'l', 28, -42,
+    'e',
+    'X',
+/* 0x56 'V'  offset 2128 */
+    0, 32, 42, 0, 2, 2,
+    0, 32, /* snap_x */
+    -42, 0, /* snap_y */
+    'm', 0, -42,
+    'l', 16, 0,
+    'l', 32, -42,
+    'e',
+    'X', 'X', 'X',
+    'X',
+/* 0x57 'W'  offset 2152 */
+    0, 40, 42, 0, 2, 2,
+    0, 40, /* snap_x */
+    -42, 0, /* snap_y */
+    'm', 0, -42,
+    'l', 10, 0,
+    'l', 20, -42,
+    'l', 30, 0,
+    'l', 40, -42,
+    'e',
+    'X', 'X', 'X',
+    'X', 'X', 'X',
+    'X', 'X', 'X',
+    'X',
+/* 0x58 'X'  offset 2188 */
+    0, 28, 42, 0, 2, 2,
+    0, 28, /* snap_x */
+    -42, 0, /* snap_y */
+    'm', 0, -42,
+    'l', 28, 0,
+    'm', 28, -42,
+    'l', 0, 0,
+    'e',
+    'X',
+/* 0x59 'Y'  offset 2212 */
+    0, 32, 42, 0, 3, 3,
+    0, 16, 32, /* snap_x */
+    -42, -21, 0, /* snap_y */
+    'm', 0, -42,
+    'l', 16, -22,
+    'l', 16, 0,
+    'm', 32, -42,
+    'l', 16, -22,
+    'e',
+/* 0x5a 'Z'  offset 2240 */
+    0, 28, 42, 0, 2, 4,
+    0, 28, /* snap_x */
+    -42, -21, -15, 0, /* snap_y */
+    'm', 28, 0,
+    'l', 0, 0,
+    'l', 28, -42,
+    'l', 0, -42,
+    'e',
+    'X', 'X', 'X',
+    'X', 'X', 'X',
+/* 0x5b '['  offset 2271 */
+    0, 14, 44, 0, 2, 4,
+    0, 14, /* snap_x */
+    -44, -21, -15, 0, /* snap_y */
+    'm', 14, -44,
+    'l', 0, -44,
+    'l', 0, 0,
+    'l', 14, 0,
+    'e',
+/* 0x5c '\'  offset 2296 */
+    0, 36, 50, 14, 2, 3,
+    0, 36, /* snap_x */
+    -21, -15, 0, /* snap_y */
+    'm', 0, -50,
+    'l', 36, 14,
+    'e',
+/* 0x5d ']'  offset 2314 */
+    0, 14, 44, 0, 2, 4,
+    0, 14, /* snap_x */
+    -44, -21, -15, 0, /* snap_y */
+    'm', 0, -44,
+    'l', 14, -44,
+    'l', 14, 0,
+    'l', 0, 0,
+    'e',
+/* 0x5e '^'  offset 2339 */
+    0, 32, 46, -18, 2, 3,
+    0, 32, /* snap_x */
+    -21, -15, 0, /* snap_y */
+    'm', 0, -18,
+    'l', 16, -46,
+    'l', 32, -18,
+    'e',
+    'X', 'X', 'X',
+/* 0x5f '_'  offset 2363 */
+    0, 36, 0, 0, 2, 1,
+    0, 36, /* snap_x */
+    0, /* snap_y */
+    'm', 0, 0,
+    'l', 36, 0,
+    'e',
+    'X', 'X',
+/* 0x60 '`'  offset 2381 */
+    0, 4, 42, -30, 2, 2,
+    0, 4, /* snap_x */
+    -42, 0, /* snap_y */
+    'm', 4, -42,
+    'c', 2, -40, 0, -39, 0, -32,
+    'c', 0, -31, 1, -30, 2, -30,
+    'c', 5, -30, 5, -34, 2, -34,
+    'e',
+    'X',
+/* 0x61 'a'  offset 2417 */
+    0, 24, 28, 0, 2, 4,
+    0, 24, /* snap_x */
+    -28, -21, -15, 0, /* snap_y */
+    'm', 24, -28,
+    'l', 24, 0,
+    'm', 24, -22,
+    'c', 21, -27, 18, -28, 13, -28,
+    'c', 2, -28, 0, -19, 0, -14,
+    'c', 0, -9, 2, 0, 13, 0,
+    'c', 18, 0, 21, -1, 24, -6,
+    'e',
+/* 0x62 'b'  offset 2467 */
+    0, 24, 42, 0, 2, 4,
+    0, 24, /* snap_x */
+    -42, -28, -15, 0, /* snap_y */
+    'm', 0, -42,
+    'l', 0, 0,
+    'm', 0, -22,
+    'c', 3, -26, 6, -28, 11, -28,
+    'c', 22, -28, 24, -19, 24, -14,
+    'c', 24, -9, 22, 0, 11, 0,
+    'c', 6, 0, 3, -2, 0, -6,
+    'e',
+/* 0x63 'c'  offset 2517 */
+    0, 24, 28, 0, 2, 4,
+    0, 24, /* snap_x */
+    -28, -21, -15, 0, /* snap_y */
+    'm', 24, -22,
+    'c', 21, -26, 18, -28, 13, -28,
+    'c', 2, -28, 0, -19, 0, -14,
+    'c', 0, -9, 2, 0, 13, 0,
+    'c', 18, 0, 21, -2, 24, -6,
+    'e',
+/* 0x64 'd'  offset 2561 */
+    0, 24, 42, 0, 2, 4,
+    0, 24, /* snap_x */
+    -42, -28, -15, 0, /* snap_y */
+    'm', 24, -42,
+    'l', 24, 0,
+    'm', 24, -22,
+    'c', 21, -26, 18, -28, 13, -28,
+    'c', 2, -28, 0, -19, 0, -14,
+    'c', 0, -9, 2, 0, 13, 0,
+    'c', 18, 0, 21, -2, 24, -6,
+    'e',
+/* 0x65 'e'  offset 2611 */
+    0, 24, 28, 0, 2, 5,
+    0, 24, /* snap_x */
+    -28, -21, -16, -15, 0, /* snap_y */
+    'm', 0, -16,
+    'l', 24, -16,
+    'c', 24, -20, 24, -28, 13, -28,
+    'c', 2, -28, 0, -19, 0, -14,
+    'c', 0, -9, 2, 0, 13, 0,
+    'c', 18, 0, 21, -2, 24, -6,
+    'e',
+/* 0x66 'f'  offset 2659 */
+    0, 16, 42, 0, 3, 5,
+    0, 6, 16, /* snap_x */
+    -42, -28, -21, -15, 0, /* snap_y */
+    'm', 16, -42,
+    'c', 8, -42, 6, -40, 6, -34,
+    'l', 6, 0,
+    'm', 0, -28,
+    'l', 14, -28,
+    'e',
+/* 0x67 'g'  offset 2693 */
+    0, 24, 28, 14, 2, 5,
+    0, 24, /* snap_x */
+    -28, -21, -15, 0, 14, /* snap_y */
+    'm', 24, -28,
+    'l', 24, 4,
+    'c', 23, 14, 16, 14, 13, 14,
+    'c', 10, 14, 8, 14, 6, 12,
+    'm', 24, -22,
+    'c', 21, -26, 18, -28, 13, -28,
+    'c', 2, -28, 0, -19, 0, -14,
+    'c', 0, -9, 2, 0, 13, 0,
+    'c', 18, 0, 21, -2, 24, -6,
+    'e',
+/* 0x68 'h'  offset 2758 */
+    0, 22, 42, 0, 2, 4,
+    0, 22, /* snap_x */
+    -42, -28, -15, 0, /* snap_y */
+    'm', 0, -42,
+    'l', 0, 0,
+    'm', 0, -20,
+    'c', 8, -32, 22, -31, 22, -20,
+    'l', 22, 0,
+    'e',
+/* 0x69 'i'  offset 2790 */
+    0, 0, 44, 0, 1, 3,
+    0, /* snap_x */
+    -42, -28, 0, /* snap_y */
+    'm', 0, -42,
+    'l', 0, -42,
+    'm', 0, -28,
+    'l', 0, 0,
+    'e',
+    'X', 'X', 'X',
+    'X', 'X', 'X',
+    'X', 'X', 'X',
+    'X', 'X',
+    'X', 'X',
+/* 0x6a 'j'  offset 2826 */
+    -8, 4, 44, 14, 3, 5,
+    -8, 2, 4, /* snap_x */
+    -42, -21, -15, 0, 14, /* snap_y */
+    'm', 2, -42,
+    'l', 2, -42,
+    'm', 2, -28,
+    'l', 2, 6,
+    'c', 2, 13, -1, 14, -8, 14,
+    'e',
+    'X', 'X', 'X',
+    'X', 'X', 'X',
+    'X', 'X', 'X',
+    'X',
+/* 0x6b 'k'  offset 2870 */
+    0, 22, 42, 0, 2, 3,
+    0, 22, /* snap_x */
+    -42, -28, 0, /* snap_y */
+    'm', 0, -42,
+    'l', 0, 0,
+    'm', 20, -28,
+    'l', 0, -8,
+    'm', 8, -16,
+    'l', 22, 0,
+    'e',
+/* 0x6c 'l'  offset 2900 */
+    0, 0, 42, 0, 1, 2,
+    0, /* snap_x */
+    -42, 0, /* snap_y */
+    'm', 0, -42,
+    'l', 0, 0,
+    'e',
+    'X',
+/* 0x6d 'm'  offset 2917 */
+    0, 44, 28, 0, 3, 3,
+    0, 22, 44, /* snap_x */
+    -28, -21, 0, /* snap_y */
+    'm', 0, -28,
+    'l', 0, 0,
+    'm', 0, -20,
+    'c', 5, -29, 22, -33, 22, -20,
+    'l', 22, 0,
+    'm', 22, -20,
+    'c', 27, -29, 44, -33, 44, -20,
+    'l', 44, 0,
+    'e',
+    'X',
+/* 0x6e 'n'  offset 2963 */
+    0, 22, 28, 0, 2, 3,
+    0, 22, /* snap_x */
+    -28, -21, 0, /* snap_y */
+    'm', 0, -28,
+    'l', 0, 0,
+    'm', 0, -20,
+    'c', 4, -28, 22, -34, 22, -20,
+    'l', 22, 0,
+    'e',
+    'X',
+/* 0x6f 'o'  offset 2995 */
+    0, 26, 28, 0, 2, 4,
+    0, 26, /* snap_x */
+    -28, -21, -15, 0, /* snap_y */
+    'm', 13, -28,
+    'c', 2, -28, 0, -19, 0, -14,
+    'c', 0, -9, 2, 0, 13, 0,
+    'c', 24, 0, 26, -9, 26, -14,
+    'c', 26, -19, 24, -28, 13, -28,
+    'E',
+/* 0x70 'p'  offset 3039 */
+    0, 24, 28, 14, 2, 4,
+    0, 24, /* snap_x */
+    -28, -21, 0, 14, /* snap_y */
+    'm', 0, -28,
+    'l', 0, 14,
+    'm', 0, -22,
+    'c', 3, -26, 6, -28, 11, -28,
+    'c', 22, -28, 24, -19, 24, -14,
+    'c', 24, -9, 22, 0, 11, 0,
+    'c', 6, 0, 3, -2, 0, -6,
+    'e',
+/* 0x71 'q'  offset 3089 */
+    0, 24, 28, 14, 2, 4,
+    0, 24, /* snap_x */
+    -28, -21, 0, 14, /* snap_y */
+    'm', 24, -28,
+    'l', 24, 14,
+    'm', 24, -22,
+    'c', 21, -26, 18, -28, 13, -28,
+    'c', 2, -28, 0, -19, 0, -14,
+    'c', 0, -9, 2, 0, 13, 0,
+    'c', 18, 0, 21, -2, 24, -6,
+    'e',
+/* 0x72 'r'  offset 3139 */
+    0, 16, 28, 0, 2, 4,
+    0, 16, /* snap_x */
+    -28, -21, -15, 0, /* snap_y */
+    'm', 0, -28,
+    'l', 0, 0,
+    'm', 0, -16,
+    'c', 2, -27, 7, -28, 16, -28,
+    'e',
+/* 0x73 's'  offset 3168 */
+    0, 22, 28, 0, 2, 4,
+    0, 22, /* snap_x */
+    -28, -21, -15, 0, /* snap_y */
+    'm', 22, -22,
+    'c', 22, -27, 16, -28, 11, -28,
+    'c', 4, -28, 0, -26, 0, -22,
+    'c', 0, -11, 22, -20, 22, -7,
+    'c', 22, 0, 17, 0, 11, 0,
+    'c', 6, 0, 0, -1, 0, -6,
+    'e',
+/* 0x74 't'  offset 3219 */
+    0, 16, 42, 0, 3, 4,
+    0, 6, 16, /* snap_x */
+    -42, -28, -21, 0, /* snap_y */
+    'm', 6, -42,
+    'l', 6, -8,
+    'c', 6, -2, 8, 0, 16, 0,
+    'm', 0, -28,
+    'l', 14, -28,
+    'e',
+/* 0x75 'u'  offset 3252 */
+    0, 22, 28, 0, 2, 3,
+    0, 22, /* snap_x */
+    -28, -15, 0, /* snap_y */
+    'm', 0, -28,
+    'l', 0, -8,
+    'c', 0, 6, 18, 0, 22, -8,
+    'm', 22, -28,
+    'l', 22, 0,
+    'e',
+/* 0x76 'v'  offset 3283 */
+    0, 24, 28, 0, 2, 3,
+    0, 24, /* snap_x */
+    -28, -15, 0, /* snap_y */
+    'm', 0, -28,
+    'l', 12, 0,
+    'l', 24, -28,
+    'e',
+    'X', 'X', 'X',
+/* 0x77 'w'  offset 3307 */
+    0, 32, 28, 0, 2, 3,
+    0, 32, /* snap_x */
+    -28, -15, 0, /* snap_y */
+    'm', 0, -28,
+    'l', 8, 0,
+    'l', 16, -28,
+    'l', 24, 0,
+    'l', 32, -28,
+    'e',
+    'X', 'X', 'X',
+    'X', 'X', 'X',
+    'X', 'X', 'X',
+/* 0x78 'x'  offset 3343 */
+    0, 22, 28, 0, 2, 2,
+    0, 22, /* snap_x */
+    -28, 0, /* snap_y */
+    'm', 0, -28,
+    'l', 22, 0,
+    'm', 22, -28,
+    'l', 0, 0,
+    'e',
+    'X',
+/* 0x79 'y'  offset 3367 */
+    -2, 24, 28, 14, 2, 4,
+    0, 24, /* snap_x */
+    -28, -15, 0, 14, /* snap_y */
+    'm', 0, -28,
+    'l', 12, 0,
+    'm', 24, -28,
+    'l', 12, 0,
+    'c', 6, 13, 0, 14, -2, 14,
+    'e',
+/* 0x7a 'z'  offset 3399 */
+    0, 22, 28, 0, 2, 4,
+    0, 22, /* snap_x */
+    -28, -21, -15, 0, /* snap_y */
+    'm', 22, 0,
+    'l', 0, 0,
+    'l', 22, -28,
+    'l', 0, -28,
+    'e',
+    'X', 'X', 'X',
+    'X', 'X', 'X',
+/* 0x7b '{'  offset 3430 */
+    0, 16, 44, 0, 3, 5,
+    0, 6, 16, /* snap_x */
+    -44, -24, -21, -15, 0, /* snap_y */
+    'm', 16, -44,
+    'c', 10, -44, 6, -42, 6, -36,
+    'l', 6, -24,
+    'l', 0, -24,
+    'l', 6, -24,
+    'l', 6, -8,
+    'c', 6, -2, 10, 0, 16, 0,
+    'e',
+/* 0x7c '|'  offset 3474 */
+    0, 0, 50, 14, 1, 2,
+    0, /* snap_x */
+    -50, 14, /* snap_y */
+    'm', 0, -50,
+    'l', 0, 14,
+    'e',
+    'X',
+/* 0x7d '}'  offset 3491 */
+    0, 16, 44, 0, 3, 5,
+    0, 10, 16, /* snap_x */
+    -44, -24, -21, -15, 0, /* snap_y */
+    'm', 0, -44,
+    'c', 6, -44, 10, -42, 10, -36,
+    'l', 10, -24,
+    'l', 16, -24,
+    'l', 10, -24,
+    'l', 10, -8,
+    'c', 10, -2, 6, 0, 0, 0,
+    'e',
+/* 0x7e '~'  offset 3535 */
+    0, 36, 24, -12, 2, 5,
+    0, 36, /* snap_x */
+    -24, -21, -15, -12, 0, /* snap_y */
+    'm', 0, -14,
+    'c', 1, -21, 4, -24, 8, -24,
+    'c', 18, -24, 18, -12, 28, -12,
+    'c', 32, -12, 35, -15, 36, -22,
+    'e',
+};
+
+const uint16_t _cairo_twin_charmap[128] = {
+    0,    0,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,    0,    0,    0,    0,
+    28,   40,   90,  114,  152,  224,  323,  390,
+    419,  441,  463,  494,  520,  556,  575,  604,
+    622,  666,  691,  736,  780,  809,  860,  919,
+    944, 1004, 1063, 1109, 1162, 1183, 1209, 1230,
+    1288, 1375, 1406, 1455, 1499, 1534, 1572, 1604,
+    1655, 1686, 1703, 1731, 1761, 1785, 1821, 1851,
+    1895, 1931, 1981, 2023, 2074, 2100, 2128, 2152,
+    2188, 2212, 2240, 2271, 2296, 2314, 2339, 2363,
+    2381, 2417, 2467, 2517, 2561, 2611, 2659, 2693,
+    2758, 2790, 2826, 2870, 2900, 2917, 2963, 2995,
+    3039, 3089, 3139, 3168, 3219, 3252, 3283, 3307,
+    3343, 3367, 3399, 3430, 3474, 3491, 3535,    0,
+};
+
diff --git a/src/cairo-font-face-twin.c b/src/cairo-font-face-twin.c
new file mode 100644 (file)
index 0000000..2ad2630
--- /dev/null
@@ -0,0 +1,752 @@
+/*
+ * Copyright © 2004 Keith Packard
+ * Copyright © 2008 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Keith Packard
+ *
+ * Contributor(s):
+ *      Keith Packard <keithp@keithp.com>
+ *      Behdad Esfahbod <behdad@behdad.org>
+ */
+
+#include "cairoint.h"
+#include "cairo-error-private.h"
+
+#include <math.h>
+
+/*
+ * This file implements a user-font rendering the descendant of the Hershey
+ * font coded by Keith Packard for use in the Twin window system.
+ * The actual font data is in cairo-font-face-twin-data.c
+ *
+ * Ported to cairo user font and extended by Behdad Esfahbod.
+ */
+
+
+
+static cairo_user_data_key_t twin_properties_key;
+
+
+/*
+ * Face properties
+ */
+
+/* We synthesize multiple faces from the twin data.  Here is the parameters. */
+
+/* The following tables and matching code are copied from Pango */
+
+/* CSS weight */
+typedef enum {
+  TWIN_WEIGHT_THIN = 100,
+  TWIN_WEIGHT_ULTRALIGHT = 200,
+  TWIN_WEIGHT_LIGHT = 300,
+  TWIN_WEIGHT_BOOK = 380,
+  TWIN_WEIGHT_NORMAL = 400,
+  TWIN_WEIGHT_MEDIUM = 500,
+  TWIN_WEIGHT_SEMIBOLD = 600,
+  TWIN_WEIGHT_BOLD = 700,
+  TWIN_WEIGHT_ULTRABOLD = 800,
+  TWIN_WEIGHT_HEAVY = 900,
+  TWIN_WEIGHT_ULTRAHEAVY = 1000
+} twin_face_weight_t;
+
+/* CSS stretch */
+typedef enum {
+  TWIN_STRETCH_ULTRA_CONDENSED,
+  TWIN_STRETCH_EXTRA_CONDENSED,
+  TWIN_STRETCH_CONDENSED,
+  TWIN_STRETCH_SEMI_CONDENSED,
+  TWIN_STRETCH_NORMAL,
+  TWIN_STRETCH_SEMI_EXPANDED,
+  TWIN_STRETCH_EXPANDED,
+  TWIN_STRETCH_EXTRA_EXPANDED,
+  TWIN_STRETCH_ULTRA_EXPANDED
+} twin_face_stretch_t;
+
+typedef struct
+{
+  int value;
+  const char str[16];
+} FieldMap;
+
+static const FieldMap slant_map[] = {
+  { CAIRO_FONT_SLANT_NORMAL, "" },
+  { CAIRO_FONT_SLANT_NORMAL, "Roman" },
+  { CAIRO_FONT_SLANT_OBLIQUE, "Oblique" },
+  { CAIRO_FONT_SLANT_ITALIC, "Italic" }
+};
+
+static const FieldMap smallcaps_map[] = {
+  { FALSE, "" },
+  { TRUE, "Small-Caps" }
+};
+
+static const FieldMap weight_map[] = {
+  { TWIN_WEIGHT_THIN, "Thin" },
+  { TWIN_WEIGHT_ULTRALIGHT, "Ultra-Light" },
+  { TWIN_WEIGHT_ULTRALIGHT, "Extra-Light" },
+  { TWIN_WEIGHT_LIGHT, "Light" },
+  { TWIN_WEIGHT_BOOK, "Book" },
+  { TWIN_WEIGHT_NORMAL, "" },
+  { TWIN_WEIGHT_NORMAL, "Regular" },
+  { TWIN_WEIGHT_MEDIUM, "Medium" },
+  { TWIN_WEIGHT_SEMIBOLD, "Semi-Bold" },
+  { TWIN_WEIGHT_SEMIBOLD, "Demi-Bold" },
+  { TWIN_WEIGHT_BOLD, "Bold" },
+  { TWIN_WEIGHT_ULTRABOLD, "Ultra-Bold" },
+  { TWIN_WEIGHT_ULTRABOLD, "Extra-Bold" },
+  { TWIN_WEIGHT_HEAVY, "Heavy" },
+  { TWIN_WEIGHT_HEAVY, "Black" },
+  { TWIN_WEIGHT_ULTRAHEAVY, "Ultra-Heavy" },
+  { TWIN_WEIGHT_ULTRAHEAVY, "Extra-Heavy" },
+  { TWIN_WEIGHT_ULTRAHEAVY, "Ultra-Black" },
+  { TWIN_WEIGHT_ULTRAHEAVY, "Extra-Black" }
+};
+
+static const FieldMap stretch_map[] = {
+  { TWIN_STRETCH_ULTRA_CONDENSED, "Ultra-Condensed" },
+  { TWIN_STRETCH_EXTRA_CONDENSED, "Extra-Condensed" },
+  { TWIN_STRETCH_CONDENSED,       "Condensed" },
+  { TWIN_STRETCH_SEMI_CONDENSED,  "Semi-Condensed" },
+  { TWIN_STRETCH_NORMAL,          "" },
+  { TWIN_STRETCH_SEMI_EXPANDED,   "Semi-Expanded" },
+  { TWIN_STRETCH_EXPANDED,        "Expanded" },
+  { TWIN_STRETCH_EXTRA_EXPANDED,  "Extra-Expanded" },
+  { TWIN_STRETCH_ULTRA_EXPANDED,  "Ultra-Expanded" }
+};
+
+static const FieldMap monospace_map[] = {
+  { FALSE, "" },
+  { TRUE, "Mono" },
+  { TRUE, "Monospace" }
+};
+
+
+typedef struct _twin_face_properties {
+    cairo_font_slant_t  slant;
+    twin_face_weight_t  weight;
+    twin_face_stretch_t stretch;
+
+    /* lets have some fun */
+    cairo_bool_t monospace;
+    cairo_bool_t smallcaps;
+} twin_face_properties_t;
+
+static cairo_bool_t
+field_matches (const char *s1,
+               const char *s2,
+               int len)
+{
+  int c1, c2;
+
+  while (len && *s1 && *s2)
+    {
+#define TOLOWER(c) \
+   (((c) >= 'A' && (c) <= 'Z') ? (c) - 'A' + 'a' : (c))
+
+      c1 = TOLOWER (*s1);
+      c2 = TOLOWER (*s2);
+      if (c1 != c2) {
+        if (c1 == '-') {
+          s1++;
+          continue;
+        }
+        return FALSE;
+      }
+      s1++; s2++;
+      len--;
+    }
+
+  return len == 0 && *s1 == '\0';
+}
+
+static cairo_bool_t
+parse_int (const char *word,
+          size_t      wordlen,
+          int        *out)
+{
+  char *end;
+  long val = strtol (word, &end, 10);
+  int i = val;
+
+  if (end != word && (end == word + wordlen) && val >= 0 && val == i)
+    {
+      if (out)
+        *out = i;
+
+      return TRUE;
+    }
+
+  return FALSE;
+}
+
+static cairo_bool_t
+find_field (const char *what,
+           const FieldMap *map,
+           int n_elements,
+           const char *str,
+           int len,
+           int *val)
+{
+  int i;
+  cairo_bool_t had_prefix = FALSE;
+
+  if (what)
+    {
+      i = strlen (what);
+      if (len > i && 0 == strncmp (what, str, i) && str[i] == '=')
+       {
+         str += i + 1;
+         len -= i + 1;
+         had_prefix = TRUE;
+       }
+    }
+
+  for (i=0; i<n_elements; i++)
+    {
+      if (map[i].str[0] && field_matches (map[i].str, str, len))
+       {
+         if (val)
+           *val = map[i].value;
+         return TRUE;
+       }
+    }
+
+  if (!what || had_prefix)
+    return parse_int (str, len, val);
+
+  return FALSE;
+}
+
+static void
+parse_field (twin_face_properties_t *props,
+            const char *str,
+            int len)
+{
+  if (field_matches ("Normal", str, len))
+    return;
+
+#define FIELD(NAME) \
+  if (find_field (STRINGIFY (NAME), NAME##_map, ARRAY_LENGTH (NAME##_map), str, len, \
+                 (int *)(void *)&props->NAME)) \
+      return; \
+
+  FIELD (weight);
+  FIELD (slant);
+  FIELD (stretch);
+  FIELD (smallcaps);
+  FIELD (monospace);
+
+#undef FIELD
+}
+
+static void
+face_props_parse (twin_face_properties_t *props,
+            const char *s)
+{
+    const char *start, *end;
+
+    for (start = end = s; *end; end++) {
+       if (*end != ' ' && *end != ':')
+           continue;
+
+       if (start < end)
+               parse_field (props, start, end - start);
+       start = end + 1;
+    }
+    if (start < end)
+           parse_field (props, start, end - start);
+}
+
+static twin_face_properties_t *
+twin_font_face_create_properties (cairo_font_face_t *twin_face)
+{
+    twin_face_properties_t *props;
+
+    props = malloc (sizeof (twin_face_properties_t));
+    if (unlikely (props == NULL))
+       return NULL;
+
+    props->stretch  = TWIN_STRETCH_NORMAL;
+    props->slant = CAIRO_FONT_SLANT_NORMAL;
+    props->weight = TWIN_WEIGHT_NORMAL;
+    props->monospace = FALSE;
+    props->smallcaps = FALSE;
+
+    if (unlikely (cairo_font_face_set_user_data (twin_face,
+                                           &twin_properties_key,
+                                           props, free))) {
+       free (props);
+       return NULL;
+    }
+
+    return props;
+}
+
+static cairo_status_t
+twin_font_face_set_properties_from_toy (cairo_font_face_t *twin_face,
+                                       cairo_toy_font_face_t *toy_face)
+{
+    twin_face_properties_t *props;
+
+    props = twin_font_face_create_properties (twin_face);
+    if (unlikely (props == NULL))
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    props->slant = toy_face->slant;
+    props->weight = toy_face->weight == CAIRO_FONT_WEIGHT_NORMAL ?
+                   TWIN_WEIGHT_NORMAL : TWIN_WEIGHT_BOLD;
+    face_props_parse (props, toy_face->family);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+
+/*
+ * Scaled properties
+ */
+
+typedef struct _twin_scaled_properties {
+       twin_face_properties_t *face_props;
+
+       cairo_bool_t snap; /* hint outlines */
+
+       double weight; /* unhinted pen width */
+       double penx, peny; /* hinted pen width */
+       double marginl, marginr; /* hinted side margins */
+
+       double stretch; /* stretch factor */
+} twin_scaled_properties_t;
+
+static void
+compute_hinting_scale (cairo_t *cr,
+                      double x, double y,
+                      double *scale, double *inv)
+{
+    cairo_user_to_device_distance (cr, &x, &y);
+    *scale = x == 0 ? y : y == 0 ? x :sqrt (x*x + y*y);
+    *inv = 1 / *scale;
+}
+
+static void
+compute_hinting_scales (cairo_t *cr,
+                       double *x_scale, double *x_scale_inv,
+                       double *y_scale, double *y_scale_inv)
+{
+    double x, y;
+
+    x = 1; y = 0;
+    compute_hinting_scale (cr, x, y, x_scale, x_scale_inv);
+
+    x = 0; y = 1;
+    compute_hinting_scale (cr, x, y, y_scale, y_scale_inv);
+}
+
+#define SNAPXI(p)      (_cairo_round ((p) * x_scale) * x_scale_inv)
+#define SNAPYI(p)      (_cairo_round ((p) * y_scale) * y_scale_inv)
+
+/* This controls the global font size */
+#define F(g)           ((g) / 72.)
+
+static void
+twin_hint_pen_and_margins(cairo_t *cr,
+                         double *penx, double *peny,
+                         double *marginl, double *marginr)
+{
+    double x_scale, x_scale_inv;
+    double y_scale, y_scale_inv;
+    double margin;
+
+    compute_hinting_scales (cr,
+                           &x_scale, &x_scale_inv,
+                           &y_scale, &y_scale_inv);
+
+    *penx = SNAPXI (*penx);
+    if (*penx < x_scale_inv)
+       *penx = x_scale_inv;
+
+    *peny = SNAPYI (*peny);
+    if (*peny < y_scale_inv)
+       *peny = y_scale_inv;
+
+    margin = *marginl + *marginr;
+    *marginl = SNAPXI (*marginl);
+    if (*marginl < x_scale_inv)
+       *marginl = x_scale_inv;
+
+    *marginr = margin - *marginl;
+    if (*marginr < 0)
+       *marginr = 0;
+    *marginr = SNAPXI (*marginr);
+}
+
+static cairo_status_t
+twin_scaled_font_compute_properties (cairo_scaled_font_t *scaled_font,
+                                    cairo_t           *cr)
+{
+    cairo_status_t status;
+    twin_scaled_properties_t *props;
+
+    props = malloc (sizeof (twin_scaled_properties_t));
+    if (unlikely (props == NULL))
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+
+    props->face_props = cairo_font_face_get_user_data (cairo_scaled_font_get_font_face (scaled_font),
+                                                      &twin_properties_key);
+
+    props->snap = scaled_font->options.hint_style > CAIRO_HINT_STYLE_NONE;
+
+    /* weight */
+    props->weight = props->face_props->weight * (F (4) / TWIN_WEIGHT_NORMAL);
+
+    /* pen & margins */
+    props->penx = props->peny = props->weight;
+    props->marginl = props->marginr = F (4);
+    if (scaled_font->options.hint_style > CAIRO_HINT_STYLE_SLIGHT)
+       twin_hint_pen_and_margins(cr,
+                                 &props->penx, &props->peny,
+                                 &props->marginl, &props->marginr);
+
+    /* stretch */
+    props->stretch = 1 + .1 * ((int) props->face_props->stretch - (int) TWIN_STRETCH_NORMAL);
+
+
+    /* Save it */
+    status = cairo_scaled_font_set_user_data (scaled_font,
+                                             &twin_properties_key,
+                                             props, free);
+    if (unlikely (status))
+       goto FREE_PROPS;
+
+    return CAIRO_STATUS_SUCCESS;
+
+FREE_PROPS:
+    free (props);
+    return status;
+}
+
+
+/*
+ * User-font implementation
+ */
+
+static cairo_status_t
+twin_scaled_font_init (cairo_scaled_font_t  *scaled_font,
+                      cairo_t              *cr,
+                      cairo_font_extents_t *metrics)
+{
+  metrics->ascent  = F (54);
+  metrics->descent = 1 - metrics->ascent;
+
+  return twin_scaled_font_compute_properties (scaled_font, cr);
+}
+
+#define TWIN_GLYPH_MAX_SNAP_X 4
+#define TWIN_GLYPH_MAX_SNAP_Y 7
+
+typedef struct {
+    int n_snap_x;
+    int8_t snap_x[TWIN_GLYPH_MAX_SNAP_X];
+    double snapped_x[TWIN_GLYPH_MAX_SNAP_X];
+    int n_snap_y;
+    int8_t snap_y[TWIN_GLYPH_MAX_SNAP_Y];
+    double snapped_y[TWIN_GLYPH_MAX_SNAP_Y];
+} twin_snap_info_t;
+
+#define twin_glyph_left(g)      ((g)[0])
+#define twin_glyph_right(g)     ((g)[1])
+#define twin_glyph_ascent(g)    ((g)[2])
+#define twin_glyph_descent(g)   ((g)[3])
+
+#define twin_glyph_n_snap_x(g)  ((g)[4])
+#define twin_glyph_n_snap_y(g)  ((g)[5])
+#define twin_glyph_snap_x(g)    (&g[6])
+#define twin_glyph_snap_y(g)    (twin_glyph_snap_x(g) + twin_glyph_n_snap_x(g))
+#define twin_glyph_draw(g)      (twin_glyph_snap_y(g) + twin_glyph_n_snap_y(g))
+
+static void
+twin_compute_snap (cairo_t             *cr,
+                  twin_snap_info_t    *info,
+                  const signed char   *b)
+{
+    int                        s, n;
+    const signed char  *snap;
+    double x_scale, x_scale_inv;
+    double y_scale, y_scale_inv;
+
+    compute_hinting_scales (cr,
+                           &x_scale, &x_scale_inv,
+                           &y_scale, &y_scale_inv);
+
+    snap = twin_glyph_snap_x (b);
+    n = twin_glyph_n_snap_x (b);
+    info->n_snap_x = n;
+    assert (n <= TWIN_GLYPH_MAX_SNAP_X);
+    for (s = 0; s < n; s++) {
+       info->snap_x[s] = snap[s];
+       info->snapped_x[s] = SNAPXI (F (snap[s]));
+    }
+
+    snap = twin_glyph_snap_y (b);
+    n = twin_glyph_n_snap_y (b);
+    info->n_snap_y = n;
+    assert (n <= TWIN_GLYPH_MAX_SNAP_Y);
+    for (s = 0; s < n; s++) {
+       info->snap_y[s] = snap[s];
+       info->snapped_y[s] = SNAPYI (F (snap[s]));
+    }
+}
+
+static double
+twin_snap (int8_t v, int n, int8_t *snap, double *snapped)
+{
+    int        s;
+
+    if (!n)
+       return F(v);
+
+    if (snap[0] == v)
+       return snapped[0];
+
+    for (s = 0; s < n - 1; s++)
+    {
+       if (snap[s+1] == v)
+           return snapped[s+1];
+
+       if (snap[s] <= v && v <= snap[s+1])
+       {
+           int before = snap[s];
+           int after = snap[s+1];
+           int dist = after - before;
+           double snap_before = snapped[s];
+           double snap_after = snapped[s+1];
+           double dist_before = v - before;
+           return snap_before + (snap_after - snap_before) * dist_before / dist;
+       }
+    }
+    return F(v);
+}
+
+#define SNAPX(p)       twin_snap (p, info.n_snap_x, info.snap_x, info.snapped_x)
+#define SNAPY(p)       twin_snap (p, info.n_snap_y, info.snap_y, info.snapped_y)
+
+static cairo_status_t
+twin_scaled_font_render_glyph (cairo_scaled_font_t  *scaled_font,
+                              unsigned long         glyph,
+                              cairo_t              *cr,
+                              cairo_text_extents_t *metrics)
+{
+    double x1, y1, x2, y2, x3, y3;
+    double marginl;
+    twin_scaled_properties_t *props;
+    twin_snap_info_t info;
+    const int8_t *b;
+    const int8_t *g;
+    int8_t w;
+    double gw;
+
+    props = cairo_scaled_font_get_user_data (scaled_font, &twin_properties_key);
+
+    /* Save glyph space, we need it when stroking */
+    cairo_save (cr);
+
+    /* center the pen */
+    cairo_translate (cr, props->penx * .5, -props->peny * .5);
+
+    /* small-caps */
+    if (props->face_props->smallcaps && glyph >= 'a' && glyph <= 'z') {
+       glyph += 'A' - 'a';
+       /* 28 and 42 are small and capital letter heights of the glyph data */
+       cairo_scale (cr, 1, 28. / 42);
+    }
+
+    /* slant */
+    if (props->face_props->slant != CAIRO_FONT_SLANT_NORMAL) {
+       cairo_matrix_t shear = { 1, 0, -.2, 1, 0, 0};
+       cairo_transform (cr, &shear);
+    }
+
+    b = _cairo_twin_outlines +
+       _cairo_twin_charmap[unlikely (glyph >= ARRAY_LENGTH (_cairo_twin_charmap)) ? 0 : glyph];
+    g = twin_glyph_draw(b);
+    w = twin_glyph_right(b);
+    gw = F(w);
+
+    marginl = props->marginl;
+
+    /* monospace */
+    if (props->face_props->monospace) {
+       double monow = F(24);
+       double extra =  props->penx + props->marginl + props->marginr;
+       cairo_scale (cr, (monow + extra) / (gw + extra), 1);
+       gw = monow;
+
+       /* resnap margin for new transform */
+       {
+           double x, y, x_scale, x_scale_inv;
+           x = 1; y = 0;
+           compute_hinting_scale (cr, x, y, &x_scale, &x_scale_inv);
+           marginl = SNAPXI (marginl);
+       }
+    }
+
+    cairo_translate (cr, marginl, 0);
+
+    /* stretch */
+    cairo_scale (cr, props->stretch, 1);
+
+    if (props->snap)
+       twin_compute_snap (cr, &info, b);
+    else
+       info.n_snap_x = info.n_snap_y = 0;
+
+    /* advance width */
+    metrics->x_advance = gw * props->stretch + props->penx + props->marginl + props->marginr;
+
+    /* glyph shape */
+    for (;;) {
+       switch (*g++) {
+       case 'M':
+           cairo_close_path (cr);
+           /* fall through */
+       case 'm':
+           x1 = SNAPX(*g++);
+           y1 = SNAPY(*g++);
+           cairo_move_to (cr, x1, y1);
+           continue;
+       case 'L':
+           cairo_close_path (cr);
+           /* fall through */
+       case 'l':
+           x1 = SNAPX(*g++);
+           y1 = SNAPY(*g++);
+           cairo_line_to (cr, x1, y1);
+           continue;
+       case 'C':
+           cairo_close_path (cr);
+           /* fall through */
+       case 'c':
+           x1 = SNAPX(*g++);
+           y1 = SNAPY(*g++);
+           x2 = SNAPX(*g++);
+           y2 = SNAPY(*g++);
+           x3 = SNAPX(*g++);
+           y3 = SNAPY(*g++);
+           cairo_curve_to (cr, x1, y1, x2, y2, x3, y3);
+           continue;
+       case 'E':
+           cairo_close_path (cr);
+           /* fall through */
+       case 'e':
+           cairo_restore (cr); /* restore glyph space */
+           cairo_set_tolerance (cr, 0.01);
+           cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND);
+           cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
+           cairo_set_line_width (cr, 1);
+           cairo_scale (cr, props->penx, props->peny);
+           cairo_stroke (cr);
+           break;
+       case 'X':
+           /* filler */
+           continue;
+       }
+       break;
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+twin_scaled_font_unicode_to_glyph (cairo_scaled_font_t *scaled_font,
+                                  unsigned long        unicode,
+                                  unsigned long       *glyph)
+{
+    /* We use an identity charmap.  Which means we could live
+     * with no unicode_to_glyph method too.  But we define this
+     * to map all unknown chars to a single unknown glyph to
+     * reduce pressure on cache. */
+
+    if (likely (unicode < ARRAY_LENGTH (_cairo_twin_charmap)))
+       *glyph = unicode;
+    else
+       *glyph = 0;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+
+/*
+ * Face constructor
+ */
+
+static cairo_font_face_t *
+_cairo_font_face_twin_create_internal (void)
+{
+    cairo_font_face_t *twin_font_face;
+
+    twin_font_face = cairo_user_font_face_create ();
+    cairo_user_font_face_set_init_func             (twin_font_face, twin_scaled_font_init);
+    cairo_user_font_face_set_render_glyph_func     (twin_font_face, twin_scaled_font_render_glyph);
+    cairo_user_font_face_set_unicode_to_glyph_func (twin_font_face, twin_scaled_font_unicode_to_glyph);
+
+    return twin_font_face;
+}
+
+cairo_font_face_t *
+_cairo_font_face_twin_create_fallback (void)
+{
+    cairo_font_face_t *twin_font_face;
+
+    twin_font_face = _cairo_font_face_twin_create_internal ();
+    if (! twin_font_face_create_properties (twin_font_face)) {
+       cairo_font_face_destroy (twin_font_face);
+       return (cairo_font_face_t *) &_cairo_font_face_nil;
+    }
+
+    return twin_font_face;
+}
+
+cairo_status_t
+_cairo_font_face_twin_create_for_toy (cairo_toy_font_face_t   *toy_face,
+                                     cairo_font_face_t      **font_face)
+{
+    cairo_status_t status;
+    cairo_font_face_t *twin_font_face;
+
+    twin_font_face = _cairo_font_face_twin_create_internal ();
+    status = twin_font_face_set_properties_from_toy (twin_font_face, toy_face);
+    if (status) {
+       cairo_font_face_destroy (twin_font_face);
+       return status;
+    }
+
+    *font_face = twin_font_face;
+
+    return CAIRO_STATUS_SUCCESS;
+}
diff --git a/src/cairo-font-face.c b/src/cairo-font-face.c
new file mode 100644 (file)
index 0000000..e379b25
--- /dev/null
@@ -0,0 +1,308 @@
+/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ * Copyright © 2005 Red Hat Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *     Carl D. Worth <cworth@cworth.org>
+ *      Graydon Hoare <graydon@redhat.com>
+ *      Owen Taylor <otaylor@redhat.com>
+ */
+
+#include "cairoint.h"
+#include "cairo-error-private.h"
+
+/**
+ * SECTION:cairo-font-face
+ * @Title: cairo_font_face_t
+ * @Short_Description: Base class for font faces
+ * @See_Also: #cairo_scaled_font_t
+ *
+ * #cairo_font_face_t represents a particular font at a particular weight,
+ * slant, and other characteristic but no size, transformation, or size.
+ *
+ * Font faces are created using <firstterm>font-backend</firstterm>-specific
+ * constructors, typically of the form
+ * <function>cairo_<emphasis>backend</emphasis>_font_face_create(<!-- -->)</function>,
+ * or implicitly using the <firstterm>toy</firstterm> text API by way of
+ * cairo_select_font_face().  The resulting face can be accessed using
+ * cairo_get_font_face().
+ */
+
+/* #cairo_font_face_t */
+
+const cairo_font_face_t _cairo_font_face_nil = {
+    { 0 },                             /* hash_entry */
+    CAIRO_STATUS_NO_MEMORY,            /* status */
+    CAIRO_REFERENCE_COUNT_INVALID,     /* ref_count */
+    { 0, 0, 0, NULL },                 /* user_data */
+    NULL
+};
+
+cairo_status_t
+_cairo_font_face_set_error (cairo_font_face_t *font_face,
+                           cairo_status_t     status)
+{
+    if (status == CAIRO_STATUS_SUCCESS)
+       return status;
+
+    /* Don't overwrite an existing error. This preserves the first
+     * error, which is the most significant. */
+    _cairo_status_set_error (&font_face->status, status);
+
+    return _cairo_error (status);
+}
+
+void
+_cairo_font_face_init (cairo_font_face_t               *font_face,
+                      const cairo_font_face_backend_t *backend)
+{
+    CAIRO_MUTEX_INITIALIZE ();
+
+    font_face->status = CAIRO_STATUS_SUCCESS;
+    CAIRO_REFERENCE_COUNT_INIT (&font_face->ref_count, 1);
+    font_face->backend = backend;
+
+    _cairo_user_data_array_init (&font_face->user_data);
+}
+
+/**
+ * cairo_font_face_reference:
+ * @font_face: a #cairo_font_face_t, (may be %NULL in which case this
+ * function does nothing).
+ *
+ * Increases the reference count on @font_face by one. This prevents
+ * @font_face from being destroyed until a matching call to
+ * cairo_font_face_destroy() is made.
+ *
+ * The number of references to a #cairo_font_face_t can be get using
+ * cairo_font_face_get_reference_count().
+ *
+ * Return value: the referenced #cairo_font_face_t.
+ **/
+cairo_font_face_t *
+cairo_font_face_reference (cairo_font_face_t *font_face)
+{
+    if (font_face == NULL ||
+           CAIRO_REFERENCE_COUNT_IS_INVALID (&font_face->ref_count))
+       return font_face;
+
+    /* We would normally assert that we have a reference here but we
+     * can't get away with that due to the zombie case as documented
+     * in _cairo_ft_font_face_destroy. */
+
+    _cairo_reference_count_inc (&font_face->ref_count);
+
+    return font_face;
+}
+slim_hidden_def (cairo_font_face_reference);
+
+/**
+ * cairo_font_face_destroy:
+ * @font_face: a #cairo_font_face_t
+ *
+ * Decreases the reference count on @font_face by one. If the result
+ * is zero, then @font_face and all associated resources are freed.
+ * See cairo_font_face_reference().
+ **/
+void
+cairo_font_face_destroy (cairo_font_face_t *font_face)
+{
+    if (font_face == NULL ||
+           CAIRO_REFERENCE_COUNT_IS_INVALID (&font_face->ref_count))
+       return;
+
+    assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&font_face->ref_count));
+
+    if (! _cairo_reference_count_dec_and_test (&font_face->ref_count))
+       return;
+
+    if (font_face->backend->destroy)
+       font_face->backend->destroy (font_face);
+
+    /* We allow resurrection to deal with some memory management for the
+     * FreeType backend where cairo_ft_font_face_t and cairo_ft_unscaled_font_t
+     * need to effectively mutually reference each other
+     */
+    if (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&font_face->ref_count))
+       return;
+
+    _cairo_user_data_array_fini (&font_face->user_data);
+
+    free (font_face);
+}
+slim_hidden_def (cairo_font_face_destroy);
+
+/**
+ * cairo_font_face_get_type:
+ * @font_face: a font face
+ *
+ * This function returns the type of the backend used to create
+ * a font face. See #cairo_font_type_t for available types.
+ *
+ * Return value: The type of @font_face.
+ *
+ * Since: 1.2
+ **/
+cairo_font_type_t
+cairo_font_face_get_type (cairo_font_face_t *font_face)
+{
+    if (CAIRO_REFERENCE_COUNT_IS_INVALID (&font_face->ref_count))
+       return CAIRO_FONT_TYPE_TOY;
+
+    return font_face->backend->type;
+}
+
+/**
+ * cairo_font_face_get_reference_count:
+ * @font_face: a #cairo_font_face_t
+ *
+ * Returns the current reference count of @font_face.
+ *
+ * Return value: the current reference count of @font_face.  If the
+ * object is a nil object, 0 will be returned.
+ *
+ * Since: 1.4
+ **/
+unsigned int
+cairo_font_face_get_reference_count (cairo_font_face_t *font_face)
+{
+    if (font_face == NULL ||
+           CAIRO_REFERENCE_COUNT_IS_INVALID (&font_face->ref_count))
+       return 0;
+
+    return CAIRO_REFERENCE_COUNT_GET_VALUE (&font_face->ref_count);
+}
+
+/**
+ * cairo_font_face_status:
+ * @font_face: a #cairo_font_face_t
+ *
+ * Checks whether an error has previously occurred for this
+ * font face
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS or another error such as
+ *   %CAIRO_STATUS_NO_MEMORY.
+ **/
+cairo_status_t
+cairo_font_face_status (cairo_font_face_t *font_face)
+{
+    return font_face->status;
+}
+
+/**
+ * cairo_font_face_get_user_data:
+ * @font_face: a #cairo_font_face_t
+ * @key: the address of the #cairo_user_data_key_t the user data was
+ * attached to
+ *
+ * Return user data previously attached to @font_face using the specified
+ * key.  If no user data has been attached with the given key this
+ * function returns %NULL.
+ *
+ * Return value: the user data previously attached or %NULL.
+ **/
+void *
+cairo_font_face_get_user_data (cairo_font_face_t          *font_face,
+                              const cairo_user_data_key_t *key)
+{
+    return _cairo_user_data_array_get_data (&font_face->user_data,
+                                           key);
+}
+slim_hidden_def (cairo_font_face_get_user_data);
+
+/**
+ * cairo_font_face_set_user_data:
+ * @font_face: a #cairo_font_face_t
+ * @key: the address of a #cairo_user_data_key_t to attach the user data to
+ * @user_data: the user data to attach to the font face
+ * @destroy: a #cairo_destroy_func_t which will be called when the
+ * font face is destroyed or when new user data is attached using the
+ * same key.
+ *
+ * Attach user data to @font_face.  To remove user data from a font face,
+ * call this function with the key that was used to set it and %NULL
+ * for @data.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY if a
+ * slot could not be allocated for the user data.
+ **/
+cairo_status_t
+cairo_font_face_set_user_data (cairo_font_face_t          *font_face,
+                              const cairo_user_data_key_t *key,
+                              void                        *user_data,
+                              cairo_destroy_func_t         destroy)
+{
+    if (CAIRO_REFERENCE_COUNT_IS_INVALID (&font_face->ref_count))
+       return font_face->status;
+
+    return _cairo_user_data_array_set_data (&font_face->user_data,
+                                           key, user_data, destroy);
+}
+slim_hidden_def (cairo_font_face_set_user_data);
+
+void
+_cairo_unscaled_font_init (cairo_unscaled_font_t               *unscaled_font,
+                          const cairo_unscaled_font_backend_t *backend)
+{
+    CAIRO_REFERENCE_COUNT_INIT (&unscaled_font->ref_count, 1);
+    unscaled_font->backend = backend;
+}
+
+cairo_unscaled_font_t *
+_cairo_unscaled_font_reference (cairo_unscaled_font_t *unscaled_font)
+{
+    if (unscaled_font == NULL)
+       return NULL;
+
+    assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&unscaled_font->ref_count));
+
+    _cairo_reference_count_inc (&unscaled_font->ref_count);
+
+    return unscaled_font;
+}
+
+void
+_cairo_unscaled_font_destroy (cairo_unscaled_font_t *unscaled_font)
+{
+    if (unscaled_font == NULL)
+       return;
+
+    assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&unscaled_font->ref_count));
+
+    if (! _cairo_reference_count_dec_and_test (&unscaled_font->ref_count))
+       return;
+
+    unscaled_font->backend->destroy (unscaled_font);
+
+    free (unscaled_font);
+}
diff --git a/src/cairo-font-options.c b/src/cairo-font-options.c
new file mode 100644 (file)
index 0000000..521d371
--- /dev/null
@@ -0,0 +1,513 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2005 Red Hat Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *      Owen Taylor <otaylor@redhat.com>
+ */
+
+#include "cairoint.h"
+#include "cairo-error-private.h"
+
+/**
+ * SECTION:cairo-font-options
+ * @Title: cairo_font_options_t
+ * @Short_Description: How a font should be rendered
+ * @See_Also: #cairo_scaled_font_t
+ *
+ * The font options specify how fonts should be rendered.  Most of the 
+ * time the font options implied by a surface are just right and do not 
+ * need any changes, but for pixel-based targets tweaking font options 
+ * may result in superior output on a particular display.
+ */
+
+static const cairo_font_options_t _cairo_font_options_nil = {
+    CAIRO_ANTIALIAS_DEFAULT,
+    CAIRO_SUBPIXEL_ORDER_DEFAULT,
+    CAIRO_LCD_FILTER_DEFAULT,
+    CAIRO_HINT_STYLE_DEFAULT,
+    CAIRO_HINT_METRICS_DEFAULT,
+    CAIRO_ROUND_GLYPH_POS_DEFAULT
+};
+
+/**
+ * _cairo_font_options_init_default:
+ * @options: a #cairo_font_options_t
+ *
+ * Initializes all fields of the font options object to default values.
+ **/
+void
+_cairo_font_options_init_default (cairo_font_options_t *options)
+{
+    options->antialias = CAIRO_ANTIALIAS_DEFAULT;
+    options->subpixel_order = CAIRO_SUBPIXEL_ORDER_DEFAULT;
+    options->lcd_filter = CAIRO_LCD_FILTER_DEFAULT;
+    options->hint_style = CAIRO_HINT_STYLE_DEFAULT;
+    options->hint_metrics = CAIRO_HINT_METRICS_DEFAULT;
+    options->round_glyph_positions = CAIRO_ROUND_GLYPH_POS_DEFAULT;
+}
+
+void
+_cairo_font_options_init_copy (cairo_font_options_t            *options,
+                              const cairo_font_options_t       *other)
+{
+    options->antialias = other->antialias;
+    options->subpixel_order = other->subpixel_order;
+    options->lcd_filter = other->lcd_filter;
+    options->hint_style = other->hint_style;
+    options->hint_metrics = other->hint_metrics;
+    options->round_glyph_positions = other->round_glyph_positions;
+}
+
+/**
+ * cairo_font_options_create:
+ *
+ * Allocates a new font options object with all options initialized
+ *  to default values.
+ *
+ * Return value: a newly allocated #cairo_font_options_t. Free with
+ *   cairo_font_options_destroy(). This function always returns a
+ *   valid pointer; if memory cannot be allocated, then a special
+ *   error object is returned where all operations on the object do nothing.
+ *   You can check for this with cairo_font_options_status().
+ **/
+cairo_font_options_t *
+cairo_font_options_create (void)
+{
+    cairo_font_options_t *options;
+
+    options = malloc (sizeof (cairo_font_options_t));
+    if (!options) {
+       _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+       return (cairo_font_options_t *) &_cairo_font_options_nil;
+    }
+
+    _cairo_font_options_init_default (options);
+
+    return options;
+}
+
+/**
+ * cairo_font_options_copy:
+ * @original: a #cairo_font_options_t
+ *
+ * Allocates a new font options object copying the option values from
+ *  @original.
+ *
+ * Return value: a newly allocated #cairo_font_options_t. Free with
+ *   cairo_font_options_destroy(). This function always returns a
+ *   valid pointer; if memory cannot be allocated, then a special
+ *   error object is returned where all operations on the object do nothing.
+ *   You can check for this with cairo_font_options_status().
+ **/
+cairo_font_options_t *
+cairo_font_options_copy (const cairo_font_options_t *original)
+{
+    cairo_font_options_t *options;
+
+    if (cairo_font_options_status ((cairo_font_options_t *) original))
+       return (cairo_font_options_t *) &_cairo_font_options_nil;
+
+    options = malloc (sizeof (cairo_font_options_t));
+    if (!options) {
+       _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+       return (cairo_font_options_t *) &_cairo_font_options_nil;
+    }
+
+    _cairo_font_options_init_copy (options, original);
+
+    return options;
+}
+
+/**
+ * cairo_font_options_destroy:
+ * @options: a #cairo_font_options_t
+ *
+ * Destroys a #cairo_font_options_t object created with
+ * cairo_font_options_create() or cairo_font_options_copy().
+ **/
+void
+cairo_font_options_destroy (cairo_font_options_t *options)
+{
+    if (cairo_font_options_status (options))
+       return;
+
+    free (options);
+}
+
+/**
+ * cairo_font_options_status:
+ * @options: a #cairo_font_options_t
+ *
+ * Checks whether an error has previously occurred for this
+ * font options object
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY
+ **/
+cairo_status_t
+cairo_font_options_status (cairo_font_options_t *options)
+{
+    if (options == NULL)
+       return CAIRO_STATUS_NULL_POINTER;
+    else if (options == (cairo_font_options_t *) &_cairo_font_options_nil)
+       return CAIRO_STATUS_NO_MEMORY;
+    else
+       return CAIRO_STATUS_SUCCESS;
+}
+slim_hidden_def (cairo_font_options_status);
+
+/**
+ * cairo_font_options_merge:
+ * @options: a #cairo_font_options_t
+ * @other: another #cairo_font_options_t
+ *
+ * Merges non-default options from @other into @options, replacing
+ * existing values. This operation can be thought of as somewhat
+ * similar to compositing @other onto @options with the operation
+ * of %CAIRO_OPERATOR_OVER.
+ **/
+void
+cairo_font_options_merge (cairo_font_options_t       *options,
+                         const cairo_font_options_t *other)
+{
+    if (cairo_font_options_status (options))
+       return;
+
+    if (cairo_font_options_status ((cairo_font_options_t *) other))
+       return;
+
+    if (other->antialias != CAIRO_ANTIALIAS_DEFAULT)
+       options->antialias = other->antialias;
+    if (other->subpixel_order != CAIRO_SUBPIXEL_ORDER_DEFAULT)
+       options->subpixel_order = other->subpixel_order;
+    if (other->lcd_filter != CAIRO_LCD_FILTER_DEFAULT)
+       options->lcd_filter = other->lcd_filter;
+    if (other->hint_style != CAIRO_HINT_STYLE_DEFAULT)
+       options->hint_style = other->hint_style;
+    if (other->hint_metrics != CAIRO_HINT_METRICS_DEFAULT)
+       options->hint_metrics = other->hint_metrics;
+    if (other->round_glyph_positions != CAIRO_ROUND_GLYPH_POS_DEFAULT)
+       options->round_glyph_positions = other->round_glyph_positions;
+}
+slim_hidden_def (cairo_font_options_merge);
+
+/**
+ * cairo_font_options_equal:
+ * @options: a #cairo_font_options_t
+ * @other: another #cairo_font_options_t
+ *
+ * Compares two font options objects for equality.
+ *
+ * Return value: %TRUE if all fields of the two font options objects match.
+ *     Note that this function will return %FALSE if either object is in
+ *     error.
+ **/
+cairo_bool_t
+cairo_font_options_equal (const cairo_font_options_t *options,
+                         const cairo_font_options_t *other)
+{
+    if (cairo_font_options_status ((cairo_font_options_t *) options))
+       return FALSE;
+    if (cairo_font_options_status ((cairo_font_options_t *) other))
+       return FALSE;
+
+    if (options == other)
+       return TRUE;
+
+    return (options->antialias == other->antialias &&
+           options->subpixel_order == other->subpixel_order &&
+           options->lcd_filter == other->lcd_filter &&
+           options->hint_style == other->hint_style &&
+           options->hint_metrics == other->hint_metrics &&
+           options->round_glyph_positions == other->round_glyph_positions);
+}
+slim_hidden_def (cairo_font_options_equal);
+
+/**
+ * cairo_font_options_hash:
+ * @options: a #cairo_font_options_t
+ *
+ * Compute a hash for the font options object; this value will
+ * be useful when storing an object containing a #cairo_font_options_t
+ * in a hash table.
+ *
+ * Return value: the hash value for the font options object.
+ *   The return value can be cast to a 32-bit type if a
+ *   32-bit hash value is needed.
+ **/
+unsigned long
+cairo_font_options_hash (const cairo_font_options_t *options)
+{
+    if (cairo_font_options_status ((cairo_font_options_t *) options))
+       options = &_cairo_font_options_nil; /* force default values */
+
+    return ((options->antialias) |
+           (options->subpixel_order << 4) |
+           (options->lcd_filter << 8) |
+           (options->hint_style << 12) |
+           (options->hint_metrics << 16));
+}
+slim_hidden_def (cairo_font_options_hash);
+
+/**
+ * cairo_font_options_set_antialias:
+ * @options: a #cairo_font_options_t
+ * @antialias: the new antialiasing mode
+ *
+ * Sets the antialiasing mode for the font options object. This
+ * specifies the type of antialiasing to do when rendering text.
+ **/
+void
+cairo_font_options_set_antialias (cairo_font_options_t *options,
+                                 cairo_antialias_t     antialias)
+{
+    if (cairo_font_options_status (options))
+       return;
+
+    options->antialias = antialias;
+}
+slim_hidden_def (cairo_font_options_set_antialias);
+
+/**
+ * cairo_font_options_get_antialias:
+ * @options: a #cairo_font_options_t
+ *
+ * Gets the antialiasing mode for the font options object.
+ *
+ * Return value: the antialiasing mode
+ **/
+cairo_antialias_t
+cairo_font_options_get_antialias (const cairo_font_options_t *options)
+{
+    if (cairo_font_options_status ((cairo_font_options_t *) options))
+       return CAIRO_ANTIALIAS_DEFAULT;
+
+    return options->antialias;
+}
+
+/**
+ * cairo_font_options_set_subpixel_order:
+ * @options: a #cairo_font_options_t
+ * @subpixel_order: the new subpixel order
+ *
+ * Sets the subpixel order for the font options object. The subpixel
+ * order specifies the order of color elements within each pixel on
+ * the display device when rendering with an antialiasing mode of
+ * %CAIRO_ANTIALIAS_SUBPIXEL. See the documentation for
+ * #cairo_subpixel_order_t for full details.
+ **/
+void
+cairo_font_options_set_subpixel_order (cairo_font_options_t   *options,
+                                      cairo_subpixel_order_t  subpixel_order)
+{
+    if (cairo_font_options_status (options))
+       return;
+
+    options->subpixel_order = subpixel_order;
+}
+slim_hidden_def (cairo_font_options_set_subpixel_order);
+
+/**
+ * cairo_font_options_get_subpixel_order:
+ * @options: a #cairo_font_options_t
+ *
+ * Gets the subpixel order for the font options object.
+ * See the documentation for #cairo_subpixel_order_t for full details.
+ *
+ * Return value: the subpixel order for the font options object
+ **/
+cairo_subpixel_order_t
+cairo_font_options_get_subpixel_order (const cairo_font_options_t *options)
+{
+    if (cairo_font_options_status ((cairo_font_options_t *) options))
+       return CAIRO_SUBPIXEL_ORDER_DEFAULT;
+
+    return options->subpixel_order;
+}
+
+/**
+ * _cairo_font_options_set_lcd_filter:
+ * @options: a #cairo_font_options_t
+ * @lcd_filter: the new LCD filter
+ *
+ * Sets the LCD filter for the font options object. The LCD filter
+ * specifies how pixels are filtered when rendered with an antialiasing
+ * mode of %CAIRO_ANTIALIAS_SUBPIXEL. See the documentation for
+ * #cairo_lcd_filter_t for full details.
+ *
+ * Since: 1.8
+ **/
+void
+_cairo_font_options_set_lcd_filter (cairo_font_options_t *options,
+                                   cairo_lcd_filter_t    lcd_filter)
+{
+    if (cairo_font_options_status (options))
+       return;
+
+    options->lcd_filter = lcd_filter;
+}
+
+/**
+ * _cairo_font_options_get_lcd_filter:
+ * @options: a #cairo_font_options_t
+ *
+ * Gets the LCD filter for the font options object.
+ * See the documentation for #cairo_lcd_filter_t for full details.
+ *
+ * Return value: the LCD filter for the font options object
+ *
+ * Since: 1.8
+ **/
+cairo_lcd_filter_t
+_cairo_font_options_get_lcd_filter (const cairo_font_options_t *options)
+{
+    if (cairo_font_options_status ((cairo_font_options_t *) options))
+       return CAIRO_LCD_FILTER_DEFAULT;
+
+    return options->lcd_filter;
+}
+
+/**
+ * _cairo_font_options_set_round_glyph_positions:
+ * @options: a #cairo_font_options_t
+ * @round: the new rounding value
+ *
+ * Sets the rounding options for the font options object. If rounding is set, a
+ * glyph's position will be rounded to integer values.
+ *
+ * Since: 1.12
+ **/
+void
+_cairo_font_options_set_round_glyph_positions (cairo_font_options_t *options,
+                                              cairo_round_glyph_positions_t  round)
+{
+    if (cairo_font_options_status (options))
+       return;
+
+    options->round_glyph_positions = round;
+}
+
+/**
+ * _cairo_font_options_get_round_glyph_positions:
+ * @options: a #cairo_font_options_t
+ *
+ * Gets the glyph position rounding option for the font options object.
+ *
+ * Return value: The round glyph posistions flag for the font options object.
+ *
+ * Since: 1.12
+ **/
+cairo_round_glyph_positions_t
+_cairo_font_options_get_round_glyph_positions (const cairo_font_options_t *options)
+{
+    if (cairo_font_options_status ((cairo_font_options_t *) options))
+       return CAIRO_ROUND_GLYPH_POS_DEFAULT;
+
+    return options->round_glyph_positions;
+}
+
+/**
+ * cairo_font_options_set_hint_style:
+ * @options: a #cairo_font_options_t
+ * @hint_style: the new hint style
+ *
+ * Sets the hint style for font outlines for the font options object.
+ * This controls whether to fit font outlines to the pixel grid,
+ * and if so, whether to optimize for fidelity or contrast.
+ * See the documentation for #cairo_hint_style_t for full details.
+ **/
+void
+cairo_font_options_set_hint_style (cairo_font_options_t *options,
+                                  cairo_hint_style_t    hint_style)
+{
+    if (cairo_font_options_status (options))
+       return;
+
+    options->hint_style = hint_style;
+}
+slim_hidden_def (cairo_font_options_set_hint_style);
+
+/**
+ * cairo_font_options_get_hint_style:
+ * @options: a #cairo_font_options_t
+ *
+ * Gets the hint style for font outlines for the font options object.
+ * See the documentation for #cairo_hint_style_t for full details.
+ *
+ * Return value: the hint style for the font options object
+ **/
+cairo_hint_style_t
+cairo_font_options_get_hint_style (const cairo_font_options_t *options)
+{
+    if (cairo_font_options_status ((cairo_font_options_t *) options))
+       return CAIRO_HINT_STYLE_DEFAULT;
+
+    return options->hint_style;
+}
+
+/**
+ * cairo_font_options_set_hint_metrics:
+ * @options: a #cairo_font_options_t
+ * @hint_metrics: the new metrics hinting mode
+ *
+ * Sets the metrics hinting mode for the font options object. This
+ * controls whether metrics are quantized to integer values in
+ * device units.
+ * See the documentation for #cairo_hint_metrics_t for full details.
+ **/
+void
+cairo_font_options_set_hint_metrics (cairo_font_options_t *options,
+                                    cairo_hint_metrics_t  hint_metrics)
+{
+    if (cairo_font_options_status (options))
+       return;
+
+    options->hint_metrics = hint_metrics;
+}
+slim_hidden_def (cairo_font_options_set_hint_metrics);
+
+/**
+ * cairo_font_options_get_hint_metrics:
+ * @options: a #cairo_font_options_t
+ *
+ * Gets the metrics hinting mode for the font options object.
+ * See the documentation for #cairo_hint_metrics_t for full details.
+ *
+ * Return value: the metrics hinting mode for the font options object
+ **/
+cairo_hint_metrics_t
+cairo_font_options_get_hint_metrics (const cairo_font_options_t *options)
+{
+    if (cairo_font_options_status ((cairo_font_options_t *) options))
+       return CAIRO_HINT_METRICS_DEFAULT;
+
+    return options->hint_metrics;
+}
diff --git a/src/cairo-fontconfig-private.h b/src/cairo-fontconfig-private.h
new file mode 100644 (file)
index 0000000..ea873ab
--- /dev/null
@@ -0,0 +1,78 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2000 Keith Packard
+ * Copyright © 2005 Red Hat, Inc
+ * Copyright © 2010 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ *      Graydon Hoare <graydon@redhat.com>
+ *     Owen Taylor <otaylor@redhat.com>
+ *      Keith Packard <keithp@keithp.com>
+ *      Carl Worth <cworth@cworth.org>
+ *      Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#ifndef _CAIRO_FONTCONFIG_PRIVATE_H
+#define _CAIRO_FONTCONFIG_PRIVATE_H
+
+#include "cairo.h"
+
+#if CAIRO_HAS_FC_FONT
+#include <fontconfig/fontconfig.h>
+#include <fontconfig/fcfreetype.h>
+#endif
+
+/* sub-pixel order */
+#ifndef FC_RGBA_UNKNOWN
+#define FC_RGBA_UNKNOWN            0
+#define FC_RGBA_RGB        1
+#define FC_RGBA_BGR        2
+#define FC_RGBA_VRGB       3
+#define FC_RGBA_VBGR       4
+#define FC_RGBA_NONE       5
+#endif
+
+/* hinting style */
+#ifndef FC_HINT_NONE
+#define FC_HINT_NONE        0
+#define FC_HINT_SLIGHT      1
+#define FC_HINT_MEDIUM      2
+#define FC_HINT_FULL        3
+#endif
+
+/* LCD filter */
+#ifndef FC_LCD_NONE
+#define FC_LCD_NONE        0
+#define FC_LCD_DEFAULT     1
+#define FC_LCD_LIGHT       2
+#define FC_LCD_LEGACY      3
+#endif
+
+#endif /* _CAIRO_FONTCONFIG_PRIVATE_H */
diff --git a/src/cairo-freed-pool-private.h b/src/cairo-freed-pool-private.h
new file mode 100644 (file)
index 0000000..0ec6de3
--- /dev/null
@@ -0,0 +1,139 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Chris Wilson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#ifndef CAIRO_FREED_POOL_H
+#define CAIRO_FREED_POOL_H
+
+#include "cairoint.h"
+#include "cairo-atomic-private.h"
+
+CAIRO_BEGIN_DECLS
+
+#define DISABLE_FREED_POOLS 0
+
+#if HAS_ATOMIC_OPS && ! DISABLE_FREED_POOLS
+/* Keep a stash of recently freed clip_paths, since we need to
+ * reallocate them frequently.
+ */
+#define MAX_FREED_POOL_SIZE 16
+typedef struct {
+    void *pool[MAX_FREED_POOL_SIZE];
+    int top;
+} freed_pool_t;
+
+static cairo_always_inline void *
+_atomic_fetch (void **slot)
+{
+    void *ptr;
+
+    do {
+        ptr = _cairo_atomic_ptr_get (slot);
+    } while (! _cairo_atomic_ptr_cmpxchg (slot, ptr, NULL));
+
+    return ptr;
+}
+
+static cairo_always_inline cairo_bool_t
+_atomic_store (void **slot, void *ptr)
+{
+    return _cairo_atomic_ptr_cmpxchg (slot, NULL, ptr);
+}
+
+cairo_private void *
+_freed_pool_get_search (freed_pool_t *pool);
+
+static inline void *
+_freed_pool_get (freed_pool_t *pool)
+{
+    void *ptr;
+    int i;
+
+    i = pool->top - 1;
+    if (i < 0)
+       i = 0;
+
+    ptr = _atomic_fetch (&pool->pool[i]);
+    if (likely (ptr != NULL)) {
+       pool->top = i;
+       return ptr;
+    }
+
+    /* either empty or contended */
+    return _freed_pool_get_search (pool);
+}
+
+cairo_private void
+_freed_pool_put_search (freed_pool_t *pool, void *ptr);
+
+static inline void
+_freed_pool_put (freed_pool_t *pool, void *ptr)
+{
+    int i;
+
+    i = pool->top;
+    if (likely (i < ARRAY_LENGTH (pool->pool) &&
+               _atomic_store (&pool->pool[i], ptr)))
+    {
+       pool->top = i + 1;
+       return;
+    }
+
+    /* either full or contended */
+    _freed_pool_put_search (pool, ptr);
+}
+
+cairo_private void
+_freed_pool_reset (freed_pool_t *pool);
+
+#define HAS_FREED_POOL 1
+
+#else
+
+/* A warning about an unused freed-pool in a build without atomics
+ * enabled usually indicates a missing _freed_pool_reset() in the
+ * static reset function */
+
+typedef int freed_pool_t;
+
+#define _freed_pool_get(pool) NULL
+#define _freed_pool_put(pool, ptr) free(ptr)
+#define _freed_pool_reset(ptr)
+
+#endif
+
+CAIRO_END_DECLS
+
+#endif /* CAIRO_FREED_POOL_PRIVATE_H */
diff --git a/src/cairo-freed-pool.c b/src/cairo-freed-pool.c
new file mode 100644 (file)
index 0000000..cfdc8e9
--- /dev/null
@@ -0,0 +1,93 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Chris Wilson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-freed-pool-private.h"
+
+#if HAS_FREED_POOL
+
+void *
+_freed_pool_get_search (freed_pool_t *pool)
+{
+    void *ptr;
+    int i;
+
+    for (i = ARRAY_LENGTH (pool->pool); i--;) {
+       ptr = _atomic_fetch (&pool->pool[i]);
+       if (ptr != NULL) {
+           pool->top = i;
+           return ptr;
+       }
+    }
+
+    /* empty */
+    pool->top = 0;
+    return NULL;
+}
+
+void
+_freed_pool_put_search (freed_pool_t *pool, void *ptr)
+{
+    int i;
+
+    for (i = 0; i < ARRAY_LENGTH (pool->pool); i++) {
+       if (_atomic_store (&pool->pool[i], ptr)) {
+           pool->top = i + 1;
+           return;
+       }
+    }
+
+    /* full */
+    pool->top = i;
+    free (ptr);
+}
+
+void
+_freed_pool_reset (freed_pool_t *pool)
+{
+    int i;
+
+    for (i = 0; i < ARRAY_LENGTH (pool->pool); i++) {
+       free (pool->pool[i]);
+       pool->pool[i] = NULL;
+    }
+
+    pool->top = 0;
+}
+
+#endif
diff --git a/src/cairo-freelist-private.h b/src/cairo-freelist-private.h
new file mode 100644 (file)
index 0000000..8fe6516
--- /dev/null
@@ -0,0 +1,139 @@
+/*
+ * Copyright © 2006 Joonas Pihlaja
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting documentation, and
+ * that the name of the copyright holders not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission.  The copyright holders make no representations
+ * about the suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ */
+#ifndef CAIRO_FREELIST_H
+#define CAIRO_FREELIST_H
+
+#include "cairo-types-private.h"
+#include "cairo-compiler-private.h"
+#include "cairo-freelist-type-private.h"
+
+/* for stand-alone compilation*/
+#ifndef VG
+#define VG(x)
+#endif
+
+#ifndef NULL
+#define NULL (void *) 0
+#endif
+
+/* Initialise a freelist that will be responsible for allocating
+ * nodes of size nodesize. */
+cairo_private void
+_cairo_freelist_init (cairo_freelist_t *freelist, unsigned nodesize);
+
+/* Deallocate any nodes in the freelist. */
+cairo_private void
+_cairo_freelist_fini (cairo_freelist_t *freelist);
+
+/* Allocate a new node from the freelist.  If the freelist contains no
+ * nodes, a new one will be allocated using malloc().  The caller is
+ * responsible for calling _cairo_freelist_free() or free() on the
+ * returned node.  Returns %NULL on memory allocation error. */
+cairo_private void *
+_cairo_freelist_alloc (cairo_freelist_t *freelist);
+
+/* Allocate a new node from the freelist.  If the freelist contains no
+ * nodes, a new one will be allocated using calloc().  The caller is
+ * responsible for calling _cairo_freelist_free() or free() on the
+ * returned node.  Returns %NULL on memory allocation error. */
+cairo_private void *
+_cairo_freelist_calloc (cairo_freelist_t *freelist);
+
+/* Return a node to the freelist. This does not deallocate the memory,
+ * but makes it available for later reuse by
+ * _cairo_freelist_alloc(). */
+cairo_private void
+_cairo_freelist_free (cairo_freelist_t *freelist, void *node);
+
+
+cairo_private void
+_cairo_freepool_init (cairo_freepool_t *freepool, unsigned nodesize);
+
+cairo_private void
+_cairo_freepool_fini (cairo_freepool_t *freepool);
+
+static inline void
+_cairo_freepool_reset (cairo_freepool_t *freepool)
+{
+    while (freepool->pools != &freepool->embedded_pool) {
+       cairo_freelist_pool_t *pool = freepool->pools;
+       freepool->pools = pool->next;
+       pool->next = freepool->freepools;
+       freepool->freepools = pool;
+    }
+
+    freepool->embedded_pool.rem = sizeof (freepool->embedded_data);
+    freepool->embedded_pool.data = freepool->embedded_data;
+}
+
+cairo_private void *
+_cairo_freepool_alloc_from_new_pool (cairo_freepool_t *freepool);
+
+static inline void *
+_cairo_freepool_alloc_from_pool (cairo_freepool_t *freepool)
+{
+    cairo_freelist_pool_t *pool;
+    uint8_t *ptr;
+
+    pool = freepool->pools;
+    if (unlikely (freepool->nodesize > pool->rem))
+       return _cairo_freepool_alloc_from_new_pool (freepool);
+
+    ptr = pool->data;
+    pool->data += freepool->nodesize;
+    pool->rem -= freepool->nodesize;
+    VG (VALGRIND_MAKE_MEM_UNDEFINED (ptr, freepool->nodesize));
+    return ptr;
+}
+
+static inline void *
+_cairo_freepool_alloc (cairo_freepool_t *freepool)
+{
+    cairo_freelist_node_t *node;
+
+    node = freepool->first_free_node;
+    if (node == NULL)
+       return _cairo_freepool_alloc_from_pool (freepool);
+
+    VG (VALGRIND_MAKE_MEM_DEFINED (node, sizeof (node->next)));
+    freepool->first_free_node = node->next;
+    VG (VALGRIND_MAKE_MEM_UNDEFINED (node, freepool->nodesize));
+
+    return node;
+}
+
+cairo_private cairo_status_t
+_cairo_freepool_alloc_array (cairo_freepool_t *freepool,
+                            int count,
+                            void **array);
+
+static inline void
+_cairo_freepool_free (cairo_freepool_t *freepool, void *ptr)
+{
+    cairo_freelist_node_t *node = ptr;
+
+    node->next = freepool->first_free_node;
+    freepool->first_free_node = node;
+    VG (VALGRIND_MAKE_MEM_NOACCESS (node, freepool->nodesize));
+}
+
+#endif /* CAIRO_FREELIST_H */
diff --git a/src/cairo-freelist-type-private.h b/src/cairo-freelist-type-private.h
new file mode 100644 (file)
index 0000000..4dd0564
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright © 2010 Joonas Pihlaja
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting documentation, and
+ * that the name of the copyright holders not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission.  The copyright holders make no representations
+ * about the suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ */
+#ifndef CAIRO_FREELIST_TYPE_H
+#define CAIRO_FREELIST_TYPE_H
+
+#include "cairo-types-private.h"
+#include "cairo-compiler-private.h"
+
+typedef struct _cairo_freelist_node cairo_freelist_node_t;
+struct _cairo_freelist_node {
+    cairo_freelist_node_t *next;
+};
+
+typedef struct _cairo_freelist {
+    cairo_freelist_node_t *first_free_node;
+    unsigned nodesize;
+} cairo_freelist_t;
+
+typedef struct _cairo_freelist_pool cairo_freelist_pool_t;
+struct _cairo_freelist_pool {
+    cairo_freelist_pool_t *next;
+    unsigned size, rem;
+    uint8_t *data;
+};
+
+typedef struct _cairo_freepool {
+    cairo_freelist_node_t *first_free_node;
+    cairo_freelist_pool_t *pools;
+    cairo_freelist_pool_t *freepools;
+    unsigned nodesize;
+    cairo_freelist_pool_t embedded_pool;
+    uint8_t embedded_data[1000];
+} cairo_freepool_t;
+
+#endif /* CAIRO_FREELIST_TYPE_H */
diff --git a/src/cairo-freelist.c b/src/cairo-freelist.c
new file mode 100644 (file)
index 0000000..d596eab
--- /dev/null
@@ -0,0 +1,191 @@
+/*
+ * Copyright © 2006 Joonas Pihlaja
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting documentation, and
+ * that the name of the copyright holders not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission.  The copyright holders make no representations
+ * about the suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ */
+
+#include "cairoint.h"
+
+#include "cairo-error-private.h"
+#include "cairo-freelist-private.h"
+
+void
+_cairo_freelist_init (cairo_freelist_t *freelist, unsigned nodesize)
+{
+    memset (freelist, 0, sizeof (cairo_freelist_t));
+    freelist->nodesize = nodesize;
+}
+
+void
+_cairo_freelist_fini (cairo_freelist_t *freelist)
+{
+    cairo_freelist_node_t *node = freelist->first_free_node;
+    while (node) {
+       cairo_freelist_node_t *next;
+
+       VG (VALGRIND_MAKE_MEM_DEFINED (node, sizeof (node->next)));
+       next = node->next;
+
+       free (node);
+       node = next;
+    }
+}
+
+void *
+_cairo_freelist_alloc (cairo_freelist_t *freelist)
+{
+    if (freelist->first_free_node) {
+       cairo_freelist_node_t *node;
+
+       node = freelist->first_free_node;
+       VG (VALGRIND_MAKE_MEM_DEFINED (node, sizeof (node->next)));
+       freelist->first_free_node = node->next;
+       VG (VALGRIND_MAKE_MEM_UNDEFINED (node, freelist->nodesize));
+
+       return node;
+    }
+
+    return malloc (freelist->nodesize);
+}
+
+void *
+_cairo_freelist_calloc (cairo_freelist_t *freelist)
+{
+    void *node = _cairo_freelist_alloc (freelist);
+    if (node)
+       memset (node, 0, freelist->nodesize);
+    return node;
+}
+
+void
+_cairo_freelist_free (cairo_freelist_t *freelist, void *voidnode)
+{
+    cairo_freelist_node_t *node = voidnode;
+    if (node) {
+       node->next = freelist->first_free_node;
+       freelist->first_free_node = node;
+       VG (VALGRIND_MAKE_MEM_NOACCESS (node, freelist->nodesize));
+    }
+}
+
+void
+_cairo_freepool_init (cairo_freepool_t *freepool, unsigned nodesize)
+{
+    freepool->first_free_node = NULL;
+    freepool->pools = &freepool->embedded_pool;
+    freepool->freepools = NULL;
+    freepool->nodesize = nodesize;
+
+    freepool->embedded_pool.next = NULL;
+    freepool->embedded_pool.size = sizeof (freepool->embedded_data);
+    freepool->embedded_pool.rem = sizeof (freepool->embedded_data);
+    freepool->embedded_pool.data = freepool->embedded_data;
+
+    VG (VALGRIND_MAKE_MEM_NOACCESS (freepool->embedded_data, sizeof (freepool->embedded_data)));
+}
+
+void
+_cairo_freepool_fini (cairo_freepool_t *freepool)
+{
+    cairo_freelist_pool_t *pool;
+
+    pool = freepool->pools;
+    while (pool != &freepool->embedded_pool) {
+       cairo_freelist_pool_t *next = pool->next;
+       free (pool);
+       pool = next;
+    }
+
+    pool = freepool->freepools;
+    while (pool != NULL) {
+       cairo_freelist_pool_t *next = pool->next;
+       free (pool);
+       pool = next;
+    }
+
+    VG (VALGRIND_MAKE_MEM_NOACCESS (freepool, sizeof (freepool)));
+}
+
+void *
+_cairo_freepool_alloc_from_new_pool (cairo_freepool_t *freepool)
+{
+    cairo_freelist_pool_t *pool;
+    int poolsize;
+
+    if (freepool->freepools != NULL) {
+       pool = freepool->freepools;
+       freepool->freepools = pool->next;
+
+       poolsize = pool->size;
+    } else {
+       if (freepool->pools != &freepool->embedded_pool)
+           poolsize = 2 * freepool->pools->size;
+       else
+           poolsize = (128 * freepool->nodesize + 8191) & -8192;
+
+       pool = malloc (sizeof (cairo_freelist_pool_t) + poolsize);
+       if (unlikely (pool == NULL))
+           return pool;
+
+       pool->size = poolsize;
+    }
+
+    pool->next = freepool->pools;
+    freepool->pools = pool;
+
+    pool->rem = poolsize - freepool->nodesize;
+    pool->data = (uint8_t *) (pool + 1) + freepool->nodesize;
+
+    VG (VALGRIND_MAKE_MEM_NOACCESS (pool->data, pool->rem));
+
+    return pool + 1;
+}
+
+cairo_status_t
+_cairo_freepool_alloc_array (cairo_freepool_t *freepool,
+                            int count,
+                            void **array)
+{
+    int i;
+
+    for (i = 0; i < count; i++) {
+       cairo_freelist_node_t *node;
+
+       node = freepool->first_free_node;
+       if (likely (node != NULL)) {
+           VG (VALGRIND_MAKE_MEM_DEFINED (node, sizeof (node->next)));
+           freepool->first_free_node = node->next;
+           VG (VALGRIND_MAKE_MEM_UNDEFINED (node, freepool->nodesize));
+       } else {
+           node = _cairo_freepool_alloc_from_pool (freepool);
+           if (unlikely (node == NULL))
+               goto CLEANUP;
+       }
+
+       array[i] = node;
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+
+  CLEANUP:
+    while (i--)
+       _cairo_freepool_free (freepool, array[i]);
+
+    return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+}
diff --git a/src/cairo-ft-font.c b/src/cairo-ft-font.c
new file mode 100644 (file)
index 0000000..2edcd0d
--- /dev/null
@@ -0,0 +1,3417 @@
+/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2000 Keith Packard
+ * Copyright © 2005 Red Hat, Inc
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ *      Graydon Hoare <graydon@redhat.com>
+ *     Owen Taylor <otaylor@redhat.com>
+ *      Keith Packard <keithp@keithp.com>
+ *      Carl Worth <cworth@cworth.org>
+ */
+
+#define _BSD_SOURCE /* for strdup() */
+#include "cairoint.h"
+
+#include "cairo-error-private.h"
+#include "cairo-image-surface-private.h"
+#include "cairo-ft-private.h"
+#include "cairo-pattern-private.h"
+
+#include <float.h>
+
+#include "cairo-fontconfig-private.h"
+
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#include FT_OUTLINE_H
+#include FT_IMAGE_H
+#include FT_TRUETYPE_TABLES_H
+#include FT_XFREE86_H
+#if HAVE_FT_GLYPHSLOT_EMBOLDEN
+#include FT_SYNTHESIS_H
+#endif
+
+#if HAVE_FT_LIBRARY_SETLCDFILTER
+#include FT_LCD_FILTER_H
+#endif
+
+/* Fontconfig version older than 2.6 didn't have these options */
+#ifndef FC_LCD_FILTER
+#define FC_LCD_FILTER  "lcdfilter"
+#endif
+/* Some Ubuntu versions defined FC_LCD_FILTER without defining the following */
+#ifndef FC_LCD_NONE
+#define FC_LCD_NONE    0
+#define FC_LCD_DEFAULT 1
+#define FC_LCD_LIGHT   2
+#define FC_LCD_LEGACY  3
+#endif
+
+/* FreeType version older than 2.3.5(?) didn't have these options */
+#ifndef FT_LCD_FILTER_NONE
+#define FT_LCD_FILTER_NONE     0
+#define FT_LCD_FILTER_DEFAULT  1
+#define FT_LCD_FILTER_LIGHT    2
+#define FT_LCD_FILTER_LEGACY   16
+#endif
+
+#define DOUBLE_TO_26_6(d) ((FT_F26Dot6)((d) * 64.0))
+#define DOUBLE_FROM_26_6(t) ((double)(t) / 64.0)
+#define DOUBLE_TO_16_16(d) ((FT_Fixed)((d) * 65536.0))
+#define DOUBLE_FROM_16_16(t) ((double)(t) / 65536.0)
+
+/* This is the max number of FT_face objects we keep open at once
+ */
+#define MAX_OPEN_FACES 10
+
+/**
+ * SECTION:cairo-ft
+ * @Title: FreeType Fonts
+ * @Short_Description: Font support for FreeType
+ * @See_Also: #cairo_font_face_t
+ *
+ * The FreeType font backend is primarily used to render text on GNU/Linux
+ * systems, but can be used on other platforms too.
+ */
+
+/**
+ * CAIRO_HAS_FT_FONT:
+ *
+ * Defined if the FreeType font backend is available.
+ * This macro can be used to conditionally compile backend-specific code.
+ */
+
+/**
+ * CAIRO_HAS_FC_FONT:
+ *
+ * Defined if the Fontconfig-specific functions of the FreeType font backend
+ * are available.
+ * This macro can be used to conditionally compile backend-specific code.
+ */
+
+/*
+ * The simple 2x2 matrix is converted into separate scale and shape
+ * factors so that hinting works right
+ */
+
+typedef struct _cairo_ft_font_transform {
+    double  x_scale, y_scale;
+    double  shape[2][2];
+} cairo_ft_font_transform_t;
+
+/*
+ * We create an object that corresponds to a single font on the disk;
+ * (identified by a filename/id pair) these are shared between all
+ * fonts using that file.  For cairo_ft_font_face_create_for_ft_face(), we
+ * just create a one-off version with a permanent face value.
+ */
+
+typedef struct _cairo_ft_font_face cairo_ft_font_face_t;
+
+struct _cairo_ft_unscaled_font {
+    cairo_unscaled_font_t base;
+
+    cairo_bool_t from_face; /* was the FT_Face provided by user? */
+    FT_Face face;          /* provided or cached face */
+
+    /* only set if from_face is false */
+    char *filename;
+    int id;
+
+    /* We temporarily scale the unscaled font as needed */
+    cairo_bool_t have_scale;
+    cairo_matrix_t current_scale;
+    double x_scale;            /* Extracted X scale factor */
+    double y_scale;             /* Extracted Y scale factor */
+    cairo_bool_t have_shape;   /* true if the current scale has a non-scale component*/
+    cairo_matrix_t current_shape;
+    FT_Matrix Current_Shape;
+
+    cairo_mutex_t mutex;
+    int lock_count;
+
+    cairo_ft_font_face_t *faces;       /* Linked list of faces for this font */
+};
+
+static int
+_cairo_ft_unscaled_font_keys_equal (const void *key_a,
+                                   const void *key_b);
+
+static void
+_cairo_ft_unscaled_font_fini (cairo_ft_unscaled_font_t *unscaled);
+
+#ifdef TIZEN_ENABLE_SET_EXTRA_FLAGS
+/* This enum is moved to cairo-ft.h for support to set extra_flags from application */
+#if 0
+typedef enum _cairo_ft_extra_flags {
+    CAIRO_FT_OPTIONS_HINT_METRICS = (1 << 0),
+    CAIRO_FT_OPTIONS_EMBOLDEN = (1 << 1)
+} cairo_ft_extra_flags_t;
+#endif
+#endif
+
+typedef struct _cairo_ft_options {
+    cairo_font_options_t    base;
+    int                            load_flags;  /* flags for FT_Load_Glyph */
+    cairo_ft_extra_flags_t  extra_flags; /* other flags that affect results */
+} cairo_ft_options_t;
+
+struct _cairo_ft_font_face {
+    cairo_font_face_t base;
+
+    cairo_ft_unscaled_font_t *unscaled;
+    cairo_ft_options_t ft_options;
+    cairo_ft_font_face_t *next;
+
+#if CAIRO_HAS_FC_FONT
+    FcPattern *pattern; /* if pattern is set, the above fields will be NULL */
+    cairo_font_face_t *resolved_font_face;
+    FcConfig *resolved_config;
+#endif
+};
+
+static const cairo_unscaled_font_backend_t cairo_ft_unscaled_font_backend;
+
+#if CAIRO_HAS_FC_FONT
+static cairo_status_t
+_cairo_ft_font_options_substitute (const cairo_font_options_t *options,
+                                  FcPattern                  *pattern);
+
+static cairo_font_face_t *
+_cairo_ft_resolve_pattern (FcPattern                 *pattern,
+                          const cairo_matrix_t       *font_matrix,
+                          const cairo_matrix_t       *ctm,
+                          const cairo_font_options_t *options);
+
+#endif
+
+/*
+ * We maintain a hash table to map file/id => #cairo_ft_unscaled_font_t.
+ * The hash table itself isn't limited in size. However, we limit the
+ * number of FT_Face objects we keep around; when we've exceeded that
+ * limit and need to create a new FT_Face, we dump the FT_Face from a
+ * random #cairo_ft_unscaled_font_t which has an unlocked FT_Face, (if
+ * there are any).
+ */
+
+typedef struct _cairo_ft_unscaled_font_map {
+    cairo_hash_table_t *hash_table;
+    FT_Library ft_library;
+    int num_open_faces;
+} cairo_ft_unscaled_font_map_t;
+
+static cairo_ft_unscaled_font_map_t *cairo_ft_unscaled_font_map = NULL;
+
+
+static FT_Face
+_cairo_ft_unscaled_font_lock_face (cairo_ft_unscaled_font_t *unscaled);
+
+static void
+_cairo_ft_unscaled_font_unlock_face (cairo_ft_unscaled_font_t *unscaled);
+
+static cairo_bool_t
+_cairo_ft_scaled_font_is_vertical (cairo_scaled_font_t *scaled_font);
+
+
+static void
+_font_map_release_face_lock_held (cairo_ft_unscaled_font_map_t *font_map,
+                                 cairo_ft_unscaled_font_t *unscaled)
+{
+    if (unscaled->face) {
+       FT_Done_Face (unscaled->face);
+       unscaled->face = NULL;
+       unscaled->have_scale = FALSE;
+
+       font_map->num_open_faces--;
+    }
+}
+
+static cairo_status_t
+_cairo_ft_unscaled_font_map_create (void)
+{
+    cairo_ft_unscaled_font_map_t *font_map;
+
+    /* This function is only intended to be called from
+     * _cairo_ft_unscaled_font_map_lock. So we'll crash if we can
+     * detect some other call path. */
+    assert (cairo_ft_unscaled_font_map == NULL);
+
+    font_map = malloc (sizeof (cairo_ft_unscaled_font_map_t));
+    if (unlikely (font_map == NULL))
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    font_map->hash_table =
+       _cairo_hash_table_create (_cairo_ft_unscaled_font_keys_equal);
+
+    if (unlikely (font_map->hash_table == NULL))
+       goto FAIL;
+
+    if (unlikely (FT_Init_FreeType (&font_map->ft_library)))
+       goto FAIL;
+
+    font_map->num_open_faces = 0;
+
+    cairo_ft_unscaled_font_map = font_map;
+    return CAIRO_STATUS_SUCCESS;
+
+FAIL:
+    if (font_map->hash_table)
+       _cairo_hash_table_destroy (font_map->hash_table);
+    free (font_map);
+
+    return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+}
+
+
+static void
+_cairo_ft_unscaled_font_map_pluck_entry (void *entry, void *closure)
+{
+    cairo_ft_unscaled_font_t *unscaled = entry;
+    cairo_ft_unscaled_font_map_t *font_map = closure;
+
+    _cairo_hash_table_remove (font_map->hash_table,
+                             &unscaled->base.hash_entry);
+
+    if (! unscaled->from_face)
+       _font_map_release_face_lock_held (font_map, unscaled);
+
+    _cairo_ft_unscaled_font_fini (unscaled);
+    free (unscaled);
+}
+
+static void
+_cairo_ft_unscaled_font_map_destroy (void)
+{
+    cairo_ft_unscaled_font_map_t *font_map;
+
+    CAIRO_MUTEX_LOCK (_cairo_ft_unscaled_font_map_mutex);
+    font_map = cairo_ft_unscaled_font_map;
+    cairo_ft_unscaled_font_map = NULL;
+    CAIRO_MUTEX_UNLOCK (_cairo_ft_unscaled_font_map_mutex);
+
+    if (font_map != NULL) {
+       _cairo_hash_table_foreach (font_map->hash_table,
+                                  _cairo_ft_unscaled_font_map_pluck_entry,
+                                  font_map);
+       assert (font_map->num_open_faces == 0);
+
+       FT_Done_FreeType (font_map->ft_library);
+
+       _cairo_hash_table_destroy (font_map->hash_table);
+
+       free (font_map);
+    }
+}
+
+static cairo_ft_unscaled_font_map_t *
+_cairo_ft_unscaled_font_map_lock (void)
+{
+    CAIRO_MUTEX_LOCK (_cairo_ft_unscaled_font_map_mutex);
+
+    if (unlikely (cairo_ft_unscaled_font_map == NULL)) {
+       if (unlikely (_cairo_ft_unscaled_font_map_create ())) {
+           CAIRO_MUTEX_UNLOCK (_cairo_ft_unscaled_font_map_mutex);
+           return NULL;
+       }
+    }
+
+    return cairo_ft_unscaled_font_map;
+}
+
+static void
+_cairo_ft_unscaled_font_map_unlock (void)
+{
+    CAIRO_MUTEX_UNLOCK (_cairo_ft_unscaled_font_map_mutex);
+}
+
+static void
+_cairo_ft_unscaled_font_init_key (cairo_ft_unscaled_font_t *key,
+                                 cairo_bool_t              from_face,
+                                 char                     *filename,
+                                 int                       id,
+                                 FT_Face                   face)
+{
+    unsigned long hash;
+
+    key->from_face = from_face;
+    key->filename = filename;
+    key->id = id;
+    key->face = face;
+
+    hash = _cairo_hash_string (filename);
+    /* the constants are just arbitrary primes */
+    hash += ((unsigned long) id) * 1607;
+    hash += ((unsigned long) face) * 2137;
+
+    key->base.hash_entry.hash = hash;
+}
+
+/**
+ * _cairo_ft_unscaled_font_init:
+ *
+ * Initialize a #cairo_ft_unscaled_font_t.
+ *
+ * There are two basic flavors of #cairo_ft_unscaled_font_t, one
+ * created from an FT_Face and the other created from a filename/id
+ * pair. These two flavors are identified as from_face and !from_face.
+ *
+ * To initialize a from_face font, pass filename==%NULL, id=0 and the
+ * desired face.
+ *
+ * To initialize a !from_face font, pass the filename/id as desired
+ * and face==%NULL.
+ *
+ * Note that the code handles these two flavors in very distinct
+ * ways. For example there is a hash_table mapping
+ * filename/id->#cairo_unscaled_font_t in the !from_face case, but no
+ * parallel in the from_face case, (where the calling code would have
+ * to do its own mapping to ensure similar sharing).
+ **/
+static cairo_status_t
+_cairo_ft_unscaled_font_init (cairo_ft_unscaled_font_t *unscaled,
+                             cairo_bool_t              from_face,
+                             const char               *filename,
+                             int                       id,
+                             FT_Face                   face)
+{
+    _cairo_unscaled_font_init (&unscaled->base,
+                              &cairo_ft_unscaled_font_backend);
+
+    if (from_face) {
+       unscaled->from_face = TRUE;
+       _cairo_ft_unscaled_font_init_key (unscaled, TRUE, NULL, 0, face);
+    } else {
+       char *filename_copy;
+
+       unscaled->from_face = FALSE;
+       unscaled->face = NULL;
+
+       filename_copy = strdup (filename);
+       if (unlikely (filename_copy == NULL))
+           return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+       _cairo_ft_unscaled_font_init_key (unscaled, FALSE, filename_copy, id, NULL);
+    }
+
+    unscaled->have_scale = FALSE;
+    CAIRO_MUTEX_INIT (unscaled->mutex);
+    unscaled->lock_count = 0;
+
+    unscaled->faces = NULL;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+/**
+ * _cairo_ft_unscaled_font_fini:
+ *
+ * Free all data associated with a #cairo_ft_unscaled_font_t.
+ *
+ * CAUTION: The unscaled->face field must be %NULL before calling this
+ * function. This is because the #cairo_ft_unscaled_font_t_map keeps a
+ * count of these faces (font_map->num_open_faces) so it maintains the
+ * unscaled->face field while it has its lock held. See
+ * _font_map_release_face_lock_held().
+ **/
+static void
+_cairo_ft_unscaled_font_fini (cairo_ft_unscaled_font_t *unscaled)
+{
+    assert (unscaled->face == NULL);
+
+    free (unscaled->filename);
+    unscaled->filename = NULL;
+
+    CAIRO_MUTEX_FINI (unscaled->mutex);
+}
+
+static int
+_cairo_ft_unscaled_font_keys_equal (const void *key_a,
+                                   const void *key_b)
+{
+    const cairo_ft_unscaled_font_t *unscaled_a = key_a;
+    const cairo_ft_unscaled_font_t *unscaled_b = key_b;
+
+    if (unscaled_a->id == unscaled_b->id &&
+       unscaled_a->from_face == unscaled_b->from_face)
+    {
+        if (unscaled_a->from_face)
+           return unscaled_a->face == unscaled_b->face;
+
+       if (unscaled_a->filename == NULL && unscaled_b->filename == NULL)
+           return TRUE;
+       else if (unscaled_a->filename == NULL || unscaled_b->filename == NULL)
+           return FALSE;
+       else
+           return (strcmp (unscaled_a->filename, unscaled_b->filename) == 0);
+    }
+
+    return FALSE;
+}
+
+/* Finds or creates a #cairo_ft_unscaled_font_t for the filename/id from
+ * pattern.  Returns a new reference to the unscaled font.
+ */
+static cairo_status_t
+_cairo_ft_unscaled_font_create_internal (cairo_bool_t from_face,
+                                        char *filename,
+                                        int id,
+                                        FT_Face font_face,
+                                        cairo_ft_unscaled_font_t **out)
+{
+    cairo_ft_unscaled_font_t key, *unscaled;
+    cairo_ft_unscaled_font_map_t *font_map;
+    cairo_status_t status;
+
+    font_map = _cairo_ft_unscaled_font_map_lock ();
+    if (unlikely (font_map == NULL))
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    _cairo_ft_unscaled_font_init_key (&key, from_face, filename, id, font_face);
+
+    /* Return existing unscaled font if it exists in the hash table. */
+    unscaled = _cairo_hash_table_lookup (font_map->hash_table,
+                                        &key.base.hash_entry);
+    if (unscaled != NULL) {
+       _cairo_unscaled_font_reference (&unscaled->base);
+       goto DONE;
+    }
+
+    /* Otherwise create it and insert into hash table. */
+    unscaled = malloc (sizeof (cairo_ft_unscaled_font_t));
+    if (unlikely (unscaled == NULL)) {
+       status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+       goto UNWIND_FONT_MAP_LOCK;
+    }
+
+    status = _cairo_ft_unscaled_font_init (unscaled, from_face, filename, id, font_face);
+    if (unlikely (status))
+       goto UNWIND_UNSCALED_MALLOC;
+
+    assert (unscaled->base.hash_entry.hash == key.base.hash_entry.hash);
+    status = _cairo_hash_table_insert (font_map->hash_table,
+                                      &unscaled->base.hash_entry);
+    if (unlikely (status))
+       goto UNWIND_UNSCALED_FONT_INIT;
+
+DONE:
+    _cairo_ft_unscaled_font_map_unlock ();
+    *out = unscaled;
+    return CAIRO_STATUS_SUCCESS;
+
+UNWIND_UNSCALED_FONT_INIT:
+    _cairo_ft_unscaled_font_fini (unscaled);
+UNWIND_UNSCALED_MALLOC:
+    free (unscaled);
+UNWIND_FONT_MAP_LOCK:
+    _cairo_ft_unscaled_font_map_unlock ();
+    return status;
+}
+
+
+#if CAIRO_HAS_FC_FONT
+static cairo_status_t
+_cairo_ft_unscaled_font_create_for_pattern (FcPattern *pattern,
+                                           cairo_ft_unscaled_font_t **out)
+{
+    FT_Face font_face = NULL;
+    char *filename = NULL;
+    int id = 0;
+    FcResult ret;
+
+    ret = FcPatternGetFTFace (pattern, FC_FT_FACE, 0, &font_face);
+    if (ret == FcResultMatch)
+       goto DONE;
+    if (ret == FcResultOutOfMemory)
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    ret = FcPatternGetString (pattern, FC_FILE, 0, (FcChar8 **) &filename);
+    if (ret == FcResultOutOfMemory)
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+    if (ret == FcResultMatch) {
+       /* If FC_INDEX is not set, we just use 0 */
+       ret = FcPatternGetInteger (pattern, FC_INDEX, 0, &id);
+       if (ret == FcResultOutOfMemory)
+           return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+       goto DONE;
+    }
+
+    /* The pattern contains neither a face nor a filename, resolve it later. */
+    *out = NULL;
+    return CAIRO_STATUS_SUCCESS;
+
+DONE:
+    return _cairo_ft_unscaled_font_create_internal (font_face != NULL,
+                                                   filename, id, font_face,
+                                                   out);
+}
+#endif
+
+static cairo_status_t
+_cairo_ft_unscaled_font_create_from_face (FT_Face face,
+                                         cairo_ft_unscaled_font_t **out)
+{
+    return _cairo_ft_unscaled_font_create_internal (TRUE, NULL, 0, face, out);
+}
+
+static void
+_cairo_ft_unscaled_font_destroy (void *abstract_font)
+{
+    cairo_ft_unscaled_font_t *unscaled  = abstract_font;
+    cairo_ft_unscaled_font_map_t *font_map;
+
+    if (unscaled == NULL)
+       return;
+
+    font_map = _cairo_ft_unscaled_font_map_lock ();
+    /* All created objects must have been mapped in the font map. */
+    assert (font_map != NULL);
+
+    if (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&unscaled->base.ref_count)) {
+       /* somebody recreated the font whilst we waited for the lock */
+       _cairo_ft_unscaled_font_map_unlock ();
+       return;
+    }
+
+    _cairo_hash_table_remove (font_map->hash_table,
+                             &unscaled->base.hash_entry);
+
+    if (unscaled->from_face) {
+       /* See comments in _ft_font_face_destroy about the "zombie" state
+        * for a _ft_font_face.
+        */
+       if (unscaled->faces && unscaled->faces->unscaled == NULL) {
+           assert (unscaled->faces->next == NULL);
+           cairo_font_face_destroy (&unscaled->faces->base);
+       }
+    } else {
+       _font_map_release_face_lock_held (font_map, unscaled);
+    }
+    unscaled->face = NULL;
+
+    _cairo_ft_unscaled_font_map_unlock ();
+
+    _cairo_ft_unscaled_font_fini (unscaled);
+}
+
+static cairo_bool_t
+_has_unlocked_face (const void *entry)
+{
+    const cairo_ft_unscaled_font_t *unscaled = entry;
+
+    return (!unscaled->from_face && unscaled->lock_count == 0 && unscaled->face);
+}
+
+/* Ensures that an unscaled font has a face object. If we exceed
+ * MAX_OPEN_FACES, try to close some.
+ *
+ * This differs from _cairo_ft_scaled_font_lock_face in that it doesn't
+ * set the scale on the face, but just returns it at the last scale.
+ */
+static cairo_warn FT_Face
+_cairo_ft_unscaled_font_lock_face (cairo_ft_unscaled_font_t *unscaled)
+{
+    cairo_ft_unscaled_font_map_t *font_map;
+    FT_Face face = NULL;
+
+    CAIRO_MUTEX_LOCK (unscaled->mutex);
+    unscaled->lock_count++;
+
+    if (unscaled->face)
+       return unscaled->face;
+
+    /* If this unscaled font was created from an FT_Face then we just
+     * returned it above. */
+    assert (!unscaled->from_face);
+
+    font_map = _cairo_ft_unscaled_font_map_lock ();
+    {
+       assert (font_map != NULL);
+
+       while (font_map->num_open_faces >= MAX_OPEN_FACES)
+       {
+           cairo_ft_unscaled_font_t *entry;
+
+           entry = _cairo_hash_table_random_entry (font_map->hash_table,
+                                                   _has_unlocked_face);
+           if (entry == NULL)
+               break;
+
+           _font_map_release_face_lock_held (font_map, entry);
+       }
+    }
+    _cairo_ft_unscaled_font_map_unlock ();
+
+    if (FT_New_Face (font_map->ft_library,
+                    unscaled->filename,
+                    unscaled->id,
+                    &face) != FT_Err_Ok)
+    {
+       unscaled->lock_count--;
+       CAIRO_MUTEX_UNLOCK (unscaled->mutex);
+       _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+       return NULL;
+    }
+
+    unscaled->face = face;
+
+    font_map->num_open_faces++;
+
+    return face;
+}
+
+
+/* Unlock unscaled font locked with _cairo_ft_unscaled_font_lock_face
+ */
+static void
+_cairo_ft_unscaled_font_unlock_face (cairo_ft_unscaled_font_t *unscaled)
+{
+    assert (unscaled->lock_count > 0);
+
+    unscaled->lock_count--;
+
+    CAIRO_MUTEX_UNLOCK (unscaled->mutex);
+}
+
+
+static cairo_status_t
+_compute_transform (cairo_ft_font_transform_t *sf,
+                   cairo_matrix_t      *scale)
+{
+    cairo_status_t status;
+    double x_scale, y_scale;
+    cairo_matrix_t normalized = *scale;
+
+    /* The font matrix has x and y "scale" components which we extract and
+     * use as character scale values. These influence the way freetype
+     * chooses hints, as well as selecting different bitmaps in
+     * hand-rendered fonts. We also copy the normalized matrix to
+     * freetype's transformation.
+     */
+
+    status = _cairo_matrix_compute_basis_scale_factors (scale,
+                                                 &x_scale, &y_scale,
+                                                 1);
+    if (unlikely (status))
+       return status;
+
+    /* FreeType docs say this about x_scale and y_scale:
+     * "A character width or height smaller than 1pt is set to 1pt;"
+     * So, we cap them from below at 1.0 and let the FT transform
+     * take care of sub-1.0 scaling. */
+    if (x_scale < 1.0)
+      x_scale = 1.0;
+    if (y_scale < 1.0)
+      y_scale = 1.0;
+
+    sf->x_scale = x_scale;
+    sf->y_scale = y_scale;
+
+    cairo_matrix_scale (&normalized, 1.0 / x_scale, 1.0 / y_scale);
+
+    _cairo_matrix_get_affine (&normalized,
+                             &sf->shape[0][0], &sf->shape[0][1],
+                             &sf->shape[1][0], &sf->shape[1][1],
+                             NULL, NULL);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+/* Temporarily scales an unscaled font to the give scale. We catch
+ * scaling to the same size, since changing a FT_Face is expensive.
+ */
+static cairo_status_t
+_cairo_ft_unscaled_font_set_scale (cairo_ft_unscaled_font_t *unscaled,
+                                  cairo_matrix_t             *scale)
+{
+    cairo_status_t status;
+    cairo_ft_font_transform_t sf;
+    FT_Matrix mat;
+    FT_Error error;
+
+    assert (unscaled->face != NULL);
+
+    if (unscaled->have_scale &&
+       scale->xx == unscaled->current_scale.xx &&
+       scale->yx == unscaled->current_scale.yx &&
+       scale->xy == unscaled->current_scale.xy &&
+       scale->yy == unscaled->current_scale.yy)
+       return CAIRO_STATUS_SUCCESS;
+
+    unscaled->have_scale = TRUE;
+    unscaled->current_scale = *scale;
+
+    status = _compute_transform (&sf, scale);
+    if (unlikely (status))
+       return status;
+
+    unscaled->x_scale = sf.x_scale;
+    unscaled->y_scale = sf.y_scale;
+
+    mat.xx = DOUBLE_TO_16_16(sf.shape[0][0]);
+    mat.yx = - DOUBLE_TO_16_16(sf.shape[0][1]);
+    mat.xy = - DOUBLE_TO_16_16(sf.shape[1][0]);
+    mat.yy = DOUBLE_TO_16_16(sf.shape[1][1]);
+
+    unscaled->have_shape = (mat.xx != 0x10000 ||
+                           mat.yx != 0x00000 ||
+                           mat.xy != 0x00000 ||
+                           mat.yy != 0x10000);
+
+    unscaled->Current_Shape = mat;
+    cairo_matrix_init (&unscaled->current_shape,
+                      sf.shape[0][0], sf.shape[0][1],
+                      sf.shape[1][0], sf.shape[1][1],
+                      0.0, 0.0);
+
+    FT_Set_Transform(unscaled->face, &mat, NULL);
+
+    if ((unscaled->face->face_flags & FT_FACE_FLAG_SCALABLE) != 0) {
+       error = FT_Set_Char_Size (unscaled->face,
+                                 sf.x_scale * 64.0 + .5,
+                                 sf.y_scale * 64.0 + .5,
+                                 0, 0);
+       if (error)
+           return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+    } else {
+       double min_distance = DBL_MAX;
+       int i;
+       int best_i = 0;
+
+       for (i = 0; i < unscaled->face->num_fixed_sizes; i++) {
+#if HAVE_FT_BITMAP_SIZE_Y_PPEM
+           double size = unscaled->face->available_sizes[i].y_ppem / 64.;
+#else
+           double size = unscaled->face->available_sizes[i].height;
+#endif
+           double distance = fabs (size - sf.y_scale);
+
+           if (distance <= min_distance) {
+               min_distance = distance;
+               best_i = i;
+           }
+       }
+#if HAVE_FT_BITMAP_SIZE_Y_PPEM
+       error = FT_Set_Char_Size (unscaled->face,
+                                 unscaled->face->available_sizes[best_i].x_ppem,
+                                 unscaled->face->available_sizes[best_i].y_ppem,
+                                 0, 0);
+       if (error)
+#endif
+           error = FT_Set_Pixel_Sizes (unscaled->face,
+                                       unscaled->face->available_sizes[best_i].width,
+                                       unscaled->face->available_sizes[best_i].height);
+       if (error)
+           return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+/* we sometimes need to convert the glyph bitmap in a FT_GlyphSlot
+ * into a different format. For example, we want to convert a
+ * FT_PIXEL_MODE_LCD or FT_PIXEL_MODE_LCD_V bitmap into a 32-bit
+ * ARGB or ABGR bitmap.
+ *
+ * this function prepares a target descriptor for this operation.
+ *
+ * input :: target bitmap descriptor. The function will set its
+ *          'width', 'rows' and 'pitch' fields, and only these
+ *
+ * slot  :: the glyph slot containing the source bitmap. this
+ *          function assumes that slot->format == FT_GLYPH_FORMAT_BITMAP
+ *
+ * mode  :: the requested final rendering mode. supported values are
+ *          MONO, NORMAL (i.e. gray), LCD and LCD_V
+ *
+ * the function returns the size in bytes of the corresponding buffer,
+ * it's up to the caller to allocate the corresponding memory block
+ * before calling _fill_xrender_bitmap
+ *
+ * it also returns -1 in case of error (e.g. incompatible arguments,
+ * like trying to convert a gray bitmap into a monochrome one)
+ */
+static int
+_compute_xrender_bitmap_size(FT_Bitmap      *target,
+                            FT_GlyphSlot    slot,
+                            FT_Render_Mode  mode)
+{
+    FT_Bitmap *ftbit;
+    int width, height, pitch;
+
+    if (slot->format != FT_GLYPH_FORMAT_BITMAP)
+       return -1;
+
+    /* compute the size of the final bitmap */
+    ftbit = &slot->bitmap;
+
+    width = ftbit->width;
+    height = ftbit->rows;
+    pitch = (width + 3) & ~3;
+
+    switch (ftbit->pixel_mode) {
+    case FT_PIXEL_MODE_MONO:
+       if (mode == FT_RENDER_MODE_MONO) {
+           pitch = (((width + 31) & ~31) >> 3);
+           break;
+       }
+       /* fall-through */
+
+    case FT_PIXEL_MODE_GRAY:
+       if (mode == FT_RENDER_MODE_LCD ||
+           mode == FT_RENDER_MODE_LCD_V)
+       {
+           /* each pixel is replicated into a 32-bit ARGB value */
+           pitch = width * 4;
+       }
+       break;
+
+    case FT_PIXEL_MODE_LCD:
+       if (mode != FT_RENDER_MODE_LCD)
+           return -1;
+
+       /* horz pixel triplets are packed into 32-bit ARGB values */
+       width /= 3;
+       pitch = width * 4;
+       break;
+
+    case FT_PIXEL_MODE_LCD_V:
+       if (mode != FT_RENDER_MODE_LCD_V)
+           return -1;
+
+       /* vert pixel triplets are packed into 32-bit ARGB values */
+       height /= 3;
+       pitch = width * 4;
+       break;
+
+    default:  /* unsupported source format */
+       return -1;
+    }
+
+    target->width = width;
+    target->rows = height;
+    target->pitch = pitch;
+    target->buffer = NULL;
+
+    return pitch * height;
+}
+
+/* this functions converts the glyph bitmap found in a FT_GlyphSlot
+ * into a different format (see _compute_xrender_bitmap_size)
+ *
+ * you should call this function after _compute_xrender_bitmap_size
+ *
+ * target :: target bitmap descriptor. Note that its 'buffer' pointer
+ *           must point to memory allocated by the caller
+ *
+ * slot   :: the glyph slot containing the source bitmap
+ *
+ * mode   :: the requested final rendering mode
+ *
+ * bgr    :: boolean, set if BGR or VBGR pixel ordering is needed
+ */
+static void
+_fill_xrender_bitmap(FT_Bitmap      *target,
+                    FT_GlyphSlot    slot,
+                    FT_Render_Mode  mode,
+                    int             bgr)
+{
+    FT_Bitmap *ftbit = &slot->bitmap;
+    unsigned char *srcLine = ftbit->buffer;
+    unsigned char *dstLine = target->buffer;
+    int src_pitch = ftbit->pitch;
+    int width = target->width;
+    int height = target->rows;
+    int pitch = target->pitch;
+    int subpixel;
+    int h;
+
+    subpixel = (mode == FT_RENDER_MODE_LCD ||
+               mode == FT_RENDER_MODE_LCD_V);
+
+    if (src_pitch < 0)
+       srcLine -= src_pitch * (ftbit->rows - 1);
+
+    target->pixel_mode = ftbit->pixel_mode;
+
+    switch (ftbit->pixel_mode) {
+    case FT_PIXEL_MODE_MONO:
+       if (subpixel) {
+           /* convert mono to ARGB32 values */
+
+           for (h = height; h > 0; h--, srcLine += src_pitch, dstLine += pitch) {
+               int x;
+
+               for (x = 0; x < width; x++) {
+                   if (srcLine[(x >> 3)] & (0x80 >> (x & 7)))
+                       ((unsigned int *) dstLine)[x] = 0xffffffffU;
+               }
+           }
+           target->pixel_mode = FT_PIXEL_MODE_LCD;
+
+       } else if (mode == FT_RENDER_MODE_NORMAL) {
+           /* convert mono to 8-bit gray */
+
+           for (h = height; h > 0; h--, srcLine += src_pitch, dstLine += pitch) {
+               int x;
+
+               for (x = 0; x < width; x++) {
+                   if (srcLine[(x >> 3)] & (0x80 >> (x & 7)))
+                       dstLine[x] = 0xff;
+               }
+           }
+           target->pixel_mode = FT_PIXEL_MODE_GRAY;
+
+       } else {
+           /* copy mono to mono */
+
+           int  bytes = (width + 7) >> 3;
+
+           for (h = height; h > 0; h--, srcLine += src_pitch, dstLine += pitch)
+               memcpy (dstLine, srcLine, bytes);
+       }
+       break;
+
+    case FT_PIXEL_MODE_GRAY:
+       if (subpixel) {
+           /* convert gray to ARGB32 values */
+
+           for (h = height; h > 0; h--, srcLine += src_pitch, dstLine += pitch) {
+               int x;
+               unsigned int *dst = (unsigned int *) dstLine;
+
+               for (x = 0; x < width; x++) {
+                   unsigned int pix = srcLine[x];
+
+                   pix |= (pix << 8);
+                   pix |= (pix << 16);
+
+                   dst[x] = pix;
+               }
+           }
+           target->pixel_mode = FT_PIXEL_MODE_LCD;
+        } else {
+            /* copy gray into gray */
+
+            for (h = height; h > 0; h--, srcLine += src_pitch, dstLine += pitch)
+                memcpy (dstLine, srcLine, width);
+        }
+        break;
+
+    case FT_PIXEL_MODE_LCD:
+       if (!bgr) {
+           /* convert horizontal RGB into ARGB32 */
+
+           for (h = height; h > 0; h--, srcLine += src_pitch, dstLine += pitch) {
+               int x;
+               unsigned char *src = srcLine;
+               unsigned int *dst = (unsigned int *) dstLine;
+
+               for (x = 0; x < width; x++, src += 3) {
+                   unsigned int  pix;
+
+                   pix = ((unsigned int)src[0] << 16) |
+                         ((unsigned int)src[1] <<  8) |
+                         ((unsigned int)src[2]      ) |
+                         ((unsigned int)src[1] << 24) ;
+
+                   dst[x] = pix;
+               }
+           }
+       } else {
+           /* convert horizontal BGR into ARGB32 */
+
+           for (h = height; h > 0; h--, srcLine += src_pitch, dstLine += pitch) {
+
+               int x;
+               unsigned char *src = srcLine;
+               unsigned int *dst = (unsigned int *) dstLine;
+
+               for (x = 0; x < width; x++, src += 3) {
+                   unsigned int  pix;
+
+                   pix = ((unsigned int)src[2] << 16) |
+                         ((unsigned int)src[1] <<  8) |
+                         ((unsigned int)src[0]      ) |
+                         ((unsigned int)src[1] << 24) ;
+
+                   dst[x] = pix;
+               }
+           }
+       }
+       break;
+
+    default:  /* FT_PIXEL_MODE_LCD_V */
+       /* convert vertical RGB into ARGB32 */
+       if (!bgr) {
+
+           for (h = height; h > 0; h--, srcLine += 3 * src_pitch, dstLine += pitch) {
+               int x;
+               unsigned char* src = srcLine;
+               unsigned int*  dst = (unsigned int *) dstLine;
+
+               for (x = 0; x < width; x++, src += 1) {
+                   unsigned int pix;
+                   pix = ((unsigned int)src[0]           << 16) |
+                         ((unsigned int)src[src_pitch]   <<  8) |
+                         ((unsigned int)src[src_pitch*2]      ) |
+                         ((unsigned int)src[src_pitch]   << 24) ;
+                   dst[x] = pix;
+               }
+           }
+       } else {
+
+           for (h = height; h > 0; h--, srcLine += 3*src_pitch, dstLine += pitch) {
+               int x;
+               unsigned char *src = srcLine;
+               unsigned int *dst = (unsigned int *) dstLine;
+
+               for (x = 0; x < width; x++, src += 1) {
+                   unsigned int  pix;
+
+                   pix = ((unsigned int)src[src_pitch * 2] << 16) |
+                         ((unsigned int)src[src_pitch]     <<  8) |
+                         ((unsigned int)src[0]                  ) |
+                         ((unsigned int)src[src_pitch]     << 24) ;
+
+                   dst[x] = pix;
+               }
+           }
+       }
+    }
+}
+
+
+/* Fills in val->image with an image surface created from @bitmap
+ */
+static cairo_status_t
+_get_bitmap_surface (FT_Bitmap              *bitmap,
+                    cairo_bool_t             own_buffer,
+                    cairo_font_options_t    *font_options,
+                    cairo_image_surface_t  **surface)
+{
+    int width, height, stride;
+    unsigned char *data;
+    int format = CAIRO_FORMAT_A8;
+    cairo_image_surface_t *image;
+
+    width = bitmap->width;
+    height = bitmap->rows;
+
+    if (width == 0 || height == 0) {
+       *surface = (cairo_image_surface_t *)
+           cairo_image_surface_create_for_data (NULL, format, 0, 0, 0);
+       return (*surface)->base.status;
+    }
+
+    switch (bitmap->pixel_mode) {
+    case FT_PIXEL_MODE_MONO:
+       stride = (((width + 31) & ~31) >> 3);
+       if (own_buffer) {
+           data = bitmap->buffer;
+           assert (stride == bitmap->pitch);
+       } else {
+           data = _cairo_malloc_ab (height, stride);
+           if (!data)
+               return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+           if (stride == bitmap->pitch) {
+               memcpy (data, bitmap->buffer, stride * height);
+           } else {
+               int i;
+               unsigned char *source, *dest;
+
+               source = bitmap->buffer;
+               dest = data;
+               for (i = height; i; i--) {
+                   memcpy (dest, source, bitmap->pitch);
+                   memset (dest + bitmap->pitch, '\0', stride - bitmap->pitch);
+
+                   source += bitmap->pitch;
+                   dest += stride;
+               }
+           }
+       }
+
+#ifndef WORDS_BIGENDIAN
+       {
+           uint8_t   *d = data;
+           int         count = stride * height;
+
+           while (count--) {
+               *d = CAIRO_BITSWAP8 (*d);
+               d++;
+           }
+       }
+#endif
+       format = CAIRO_FORMAT_A1;
+       break;
+
+    case FT_PIXEL_MODE_LCD:
+    case FT_PIXEL_MODE_LCD_V:
+    case FT_PIXEL_MODE_GRAY:
+        if (font_options->antialias != CAIRO_ANTIALIAS_SUBPIXEL) {
+           stride = bitmap->pitch;
+           if (own_buffer) {
+               data = bitmap->buffer;
+           } else {
+               data = _cairo_malloc_ab (height, stride);
+               if (!data)
+                   return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+               memcpy (data, bitmap->buffer, stride * height);
+           }
+
+       format = CAIRO_FORMAT_A8;
+       } else {
+           /* if we get there, the  data from the source bitmap
+            * really comes from _fill_xrender_bitmap, and is
+            * made of 32-bit ARGB or ABGR values */
+           assert (own_buffer != 0);
+           assert (bitmap->pixel_mode != FT_PIXEL_MODE_GRAY);
+
+               data = bitmap->buffer;
+               stride = bitmap->pitch;
+               format = CAIRO_FORMAT_ARGB32;
+       }
+       break;
+    case FT_PIXEL_MODE_GRAY2:
+    case FT_PIXEL_MODE_GRAY4:
+       /* These could be triggered by very rare types of TrueType fonts */
+    default:
+       if (own_buffer)
+           free (bitmap->buffer);
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+    }
+
+    /* XXX */
+    *surface = image = (cairo_image_surface_t *)
+       cairo_image_surface_create_for_data (data,
+                                            format,
+                                            width, height, stride);
+    if (image->base.status) {
+       free (data);
+       return (*surface)->base.status;
+    }
+
+    if (format == CAIRO_FORMAT_ARGB32)
+       pixman_image_set_component_alpha (image->pixman_image, TRUE);
+
+    _cairo_image_surface_assume_ownership_of_data (image);
+
+    _cairo_debug_check_image_surface_is_defined (&image->base);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+/* Converts an outline FT_GlyphSlot into an image
+ *
+ * This could go through _render_glyph_bitmap as well, letting
+ * FreeType convert the outline to a bitmap, but doing it ourselves
+ * has two minor advantages: first, we save a copy of the bitmap
+ * buffer: we can directly use the buffer that FreeType renders
+ * into.
+ *
+ * Second, it may help when we add support for subpixel
+ * rendering: the Xft code does it this way. (Keith thinks that
+ * it may also be possible to get the subpixel rendering with
+ * FT_Render_Glyph: something worth looking into in more detail
+ * when we add subpixel support. If so, we may want to eliminate
+ * this version of the code path entirely.
+ */
+static cairo_status_t
+_render_glyph_outline (FT_Face                    face,
+                      cairo_font_options_t      *font_options,
+                      cairo_image_surface_t    **surface)
+{
+    int rgba = FC_RGBA_UNKNOWN;
+    int lcd_filter = FT_LCD_FILTER_LEGACY;
+    FT_GlyphSlot glyphslot = face->glyph;
+    FT_Outline *outline = &glyphslot->outline;
+    FT_Bitmap bitmap;
+    FT_BBox cbox;
+    unsigned int width, height;
+    cairo_status_t status;
+    FT_Error fterror;
+    FT_Library library = glyphslot->library;
+    FT_Render_Mode render_mode = FT_RENDER_MODE_NORMAL;
+
+    switch (font_options->antialias) {
+    case CAIRO_ANTIALIAS_NONE:
+       render_mode = FT_RENDER_MODE_MONO;
+       break;
+
+    case CAIRO_ANTIALIAS_SUBPIXEL:
+    case CAIRO_ANTIALIAS_BEST:
+       switch (font_options->subpixel_order) {
+           case CAIRO_SUBPIXEL_ORDER_DEFAULT:
+           case CAIRO_SUBPIXEL_ORDER_RGB:
+           case CAIRO_SUBPIXEL_ORDER_BGR:
+               render_mode = FT_RENDER_MODE_LCD;
+               break;
+
+           case CAIRO_SUBPIXEL_ORDER_VRGB:
+           case CAIRO_SUBPIXEL_ORDER_VBGR:
+               render_mode = FT_RENDER_MODE_LCD_V;
+               break;
+       }
+
+       switch (font_options->lcd_filter) {
+       case CAIRO_LCD_FILTER_NONE:
+           lcd_filter = FT_LCD_FILTER_NONE;
+           break;
+       case CAIRO_LCD_FILTER_DEFAULT:
+       case CAIRO_LCD_FILTER_INTRA_PIXEL:
+           lcd_filter = FT_LCD_FILTER_LEGACY;
+           break;
+       case CAIRO_LCD_FILTER_FIR3:
+           lcd_filter = FT_LCD_FILTER_LIGHT;
+           break;
+       case CAIRO_LCD_FILTER_FIR5:
+           lcd_filter = FT_LCD_FILTER_DEFAULT;
+           break;
+       }
+
+       break;
+
+    case CAIRO_ANTIALIAS_DEFAULT:
+    case CAIRO_ANTIALIAS_GRAY:
+    case CAIRO_ANTIALIAS_GOOD:
+    case CAIRO_ANTIALIAS_FAST:
+       render_mode = FT_RENDER_MODE_NORMAL;
+    }
+
+    FT_Outline_Get_CBox (outline, &cbox);
+
+    cbox.xMin &= -64;
+    cbox.yMin &= -64;
+    cbox.xMax = (cbox.xMax + 63) & -64;
+    cbox.yMax = (cbox.yMax + 63) & -64;
+
+    width = (unsigned int) ((cbox.xMax - cbox.xMin) >> 6);
+    height = (unsigned int) ((cbox.yMax - cbox.yMin) >> 6);
+
+    if (width * height == 0) {
+       cairo_format_t format;
+       /* Looks like fb handles zero-sized images just fine */
+       switch (render_mode) {
+       case FT_RENDER_MODE_MONO:
+           format = CAIRO_FORMAT_A1;
+           break;
+       case FT_RENDER_MODE_LCD:
+       case FT_RENDER_MODE_LCD_V:
+           format= CAIRO_FORMAT_ARGB32;
+           break;
+       case FT_RENDER_MODE_LIGHT:
+       case FT_RENDER_MODE_NORMAL:
+       case FT_RENDER_MODE_MAX:
+       default:
+           format = CAIRO_FORMAT_A8;
+           break;
+       }
+
+       (*surface) = (cairo_image_surface_t *)
+           cairo_image_surface_create_for_data (NULL, format, 0, 0, 0);
+       if ((*surface)->base.status)
+           return (*surface)->base.status;
+    } else {
+
+       int bitmap_size;
+
+       switch (render_mode) {
+       case FT_RENDER_MODE_LCD:
+           if (font_options->subpixel_order == CAIRO_SUBPIXEL_ORDER_BGR) {
+               rgba = FC_RGBA_BGR;
+           } else {
+               rgba = FC_RGBA_RGB;
+           }
+       case FT_RENDER_MODE_LCD_V:
+           if (font_options->subpixel_order == CAIRO_SUBPIXEL_ORDER_VBGR) {
+               rgba = FC_RGBA_VBGR;
+           } else {
+               rgba = FC_RGBA_VRGB;
+               }
+           break;
+       case FT_RENDER_MODE_MONO:
+       case FT_RENDER_MODE_LIGHT:
+       case FT_RENDER_MODE_NORMAL:
+       case FT_RENDER_MODE_MAX:
+       default:
+           break;
+           }
+
+#if HAVE_FT_LIBRARY_SETLCDFILTER
+       FT_Library_SetLcdFilter (library, lcd_filter);
+#endif
+
+       fterror = FT_Render_Glyph (face->glyph, render_mode);
+
+#if HAVE_FT_LIBRARY_SETLCDFILTER
+       FT_Library_SetLcdFilter (library, FT_LCD_FILTER_NONE);
+#endif
+
+       if (fterror != 0)
+               return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+       bitmap_size = _compute_xrender_bitmap_size (&bitmap,
+                                                   face->glyph,
+                                                   render_mode);
+       if (bitmap_size < 0)
+           return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+       bitmap.buffer = calloc (1, bitmap_size);
+       if (bitmap.buffer == NULL)
+               return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+       _fill_xrender_bitmap (&bitmap, face->glyph, render_mode,
+                             (rgba == FC_RGBA_BGR || rgba == FC_RGBA_VBGR));
+
+       /* Note:
+        * _get_bitmap_surface will free bitmap.buffer if there is an error
+        */
+       status = _get_bitmap_surface (&bitmap, TRUE, font_options, surface);
+       if (unlikely (status))
+           return status;
+
+       /* Note: the font's coordinate system is upside down from ours, so the
+        * Y coordinate of the control box needs to be negated.  Moreover, device
+        * offsets are position of glyph origin relative to top left while xMin
+        * and yMax are offsets of top left relative to origin.  Another negation.
+        */
+       cairo_surface_set_device_offset (&(*surface)->base,
+                                        (double)-glyphslot->bitmap_left,
+                                        (double)+glyphslot->bitmap_top);
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+/* Converts a bitmap (or other) FT_GlyphSlot into an image */
+static cairo_status_t
+_render_glyph_bitmap (FT_Face                face,
+                     cairo_font_options_t   *font_options,
+                     cairo_image_surface_t **surface)
+{
+    FT_GlyphSlot glyphslot = face->glyph;
+    cairo_status_t status;
+    FT_Error error;
+
+    /* According to the FreeType docs, glyphslot->format could be
+     * something other than FT_GLYPH_FORMAT_OUTLINE or
+     * FT_GLYPH_FORMAT_BITMAP. Calling FT_Render_Glyph gives FreeType
+     * the opportunity to convert such to
+     * bitmap. FT_GLYPH_FORMAT_COMPOSITE will not be encountered since
+     * we avoid the FT_LOAD_NO_RECURSE flag.
+     */
+    error = FT_Render_Glyph (glyphslot, FT_RENDER_MODE_NORMAL);
+    /* XXX ignoring all other errors for now.  They are not fatal, typically
+     * just a glyph-not-found. */
+    if (error == FT_Err_Out_Of_Memory)
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    status = _get_bitmap_surface (&glyphslot->bitmap,
+                                 FALSE, font_options,
+                                 surface);
+    if (unlikely (status))
+       return status;
+
+    /*
+     * Note: the font's coordinate system is upside down from ours, so the
+     * Y coordinate of the control box needs to be negated.  Moreover, device
+     * offsets are position of glyph origin relative to top left while
+     * bitmap_left and bitmap_top are offsets of top left relative to origin.
+     * Another negation.
+     */
+    cairo_surface_set_device_offset (&(*surface)->base,
+                                    -glyphslot->bitmap_left,
+                                    +glyphslot->bitmap_top);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_transform_glyph_bitmap (cairo_matrix_t         * shape,
+                        cairo_image_surface_t ** surface)
+{
+    cairo_matrix_t original_to_transformed;
+    cairo_matrix_t transformed_to_original;
+    cairo_image_surface_t *old_image;
+    cairo_surface_t *image;
+    double x[4], y[4];
+    double origin_x, origin_y;
+    int orig_width, orig_height;
+    int i;
+    int x_min, y_min, x_max, y_max;
+    int width, height;
+    cairo_status_t status;
+    cairo_surface_pattern_t pattern;
+
+    /* We want to compute a transform that takes the origin
+     * (device_x_offset, device_y_offset) to 0,0, then applies
+     * the "shape" portion of the font transform
+     */
+    original_to_transformed = *shape;
+
+    cairo_surface_get_device_offset (&(*surface)->base, &origin_x, &origin_y);
+    orig_width = (*surface)->width;
+    orig_height = (*surface)->height;
+
+    cairo_matrix_translate (&original_to_transformed,
+                           -origin_x, -origin_y);
+
+    /* Find the bounding box of the original bitmap under that
+     * transform
+     */
+    x[0] = 0;          y[0] = 0;
+    x[1] = orig_width; y[1] = 0;
+    x[2] = orig_width; y[2] = orig_height;
+    x[3] = 0;          y[3] = orig_height;
+
+    for (i = 0; i < 4; i++)
+      cairo_matrix_transform_point (&original_to_transformed,
+                                   &x[i], &y[i]);
+
+    x_min = floor (x[0]);   y_min = floor (y[0]);
+    x_max =  ceil (x[0]);   y_max =  ceil (y[0]);
+
+    for (i = 1; i < 4; i++) {
+       if (x[i] < x_min)
+           x_min = floor (x[i]);
+       else if (x[i] > x_max)
+           x_max = ceil (x[i]);
+       if (y[i] < y_min)
+           y_min = floor (y[i]);
+       else if (y[i] > y_max)
+           y_max = ceil (y[i]);
+    }
+
+    /* Adjust the transform so that the bounding box starts at 0,0 ...
+     * this gives our final transform from original bitmap to transformed
+     * bitmap.
+     */
+    original_to_transformed.x0 -= x_min;
+    original_to_transformed.y0 -= y_min;
+
+    /* Create the transformed bitmap */
+    width  = x_max - x_min;
+    height = y_max - y_min;
+
+    transformed_to_original = original_to_transformed;
+    status = cairo_matrix_invert (&transformed_to_original);
+    if (unlikely (status))
+       return status;
+
+    image = cairo_image_surface_create (CAIRO_FORMAT_A8, width, height);
+    if (unlikely (image->status))
+       return image->status;
+
+    /* Draw the original bitmap transformed into the new bitmap
+     */
+    _cairo_pattern_init_for_surface (&pattern, &(*surface)->base);
+    cairo_pattern_set_matrix (&pattern.base, &transformed_to_original);
+
+    status = _cairo_surface_paint (image,
+                                  CAIRO_OPERATOR_SOURCE,
+                                  &pattern.base,
+                                  NULL);
+
+    _cairo_pattern_fini (&pattern.base);
+
+    if (unlikely (status)) {
+       cairo_surface_destroy (image);
+       return status;
+    }
+
+    /* Now update the cache entry for the new bitmap, recomputing
+     * the origin based on the final transform.
+     */
+    cairo_matrix_transform_point (&original_to_transformed,
+                                 &origin_x, &origin_y);
+
+    old_image = (*surface);
+    (*surface) = (cairo_image_surface_t *)image;
+    cairo_surface_destroy (&old_image->base);
+
+    cairo_surface_set_device_offset (&(*surface)->base,
+                                    _cairo_lround (origin_x),
+                                    _cairo_lround (origin_y));
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static const cairo_unscaled_font_backend_t cairo_ft_unscaled_font_backend = {
+    _cairo_ft_unscaled_font_destroy,
+#if 0
+    _cairo_ft_unscaled_font_create_glyph
+#endif
+};
+
+/* #cairo_ft_scaled_font_t */
+
+typedef struct _cairo_ft_scaled_font {
+    cairo_scaled_font_t base;
+    cairo_ft_unscaled_font_t *unscaled;
+    cairo_ft_options_t ft_options;
+} cairo_ft_scaled_font_t;
+
+static const cairo_scaled_font_backend_t _cairo_ft_scaled_font_backend;
+
+#if CAIRO_HAS_FC_FONT
+/* The load flags passed to FT_Load_Glyph control aspects like hinting and
+ * antialiasing. Here we compute them from the fields of a FcPattern.
+ */
+static void
+_get_pattern_ft_options (FcPattern *pattern, cairo_ft_options_t *ret)
+{
+    FcBool antialias, vertical_layout, hinting, autohint, bitmap, embolden;
+    cairo_ft_options_t ft_options;
+    int rgba;
+#ifdef FC_HINT_STYLE
+    int hintstyle;
+#endif
+
+    _cairo_font_options_init_default (&ft_options.base);
+    ft_options.load_flags = FT_LOAD_DEFAULT;
+    ft_options.extra_flags = 0;
+
+#ifndef FC_EMBEDDED_BITMAP
+#define FC_EMBEDDED_BITMAP "embeddedbitmap"
+#endif
+
+    /* Check whether to force use of embedded bitmaps */
+    if (FcPatternGetBool (pattern,
+                         FC_EMBEDDED_BITMAP, 0, &bitmap) != FcResultMatch)
+       bitmap = FcFalse;
+
+    /* disable antialiasing if requested */
+    if (FcPatternGetBool (pattern,
+                         FC_ANTIALIAS, 0, &antialias) != FcResultMatch)
+       antialias = FcTrue;
+
+    if (antialias) {
+       cairo_subpixel_order_t subpixel_order;
+       int lcd_filter;
+
+       /* disable hinting if requested */
+       if (FcPatternGetBool (pattern,
+                             FC_HINTING, 0, &hinting) != FcResultMatch)
+           hinting = FcTrue;
+
+       if (FcPatternGetInteger (pattern,
+                                FC_RGBA, 0, &rgba) != FcResultMatch)
+           rgba = FC_RGBA_UNKNOWN;
+
+       switch (rgba) {
+       case FC_RGBA_RGB:
+           subpixel_order = CAIRO_SUBPIXEL_ORDER_RGB;
+           break;
+       case FC_RGBA_BGR:
+           subpixel_order = CAIRO_SUBPIXEL_ORDER_BGR;
+           break;
+       case FC_RGBA_VRGB:
+           subpixel_order = CAIRO_SUBPIXEL_ORDER_VRGB;
+           break;
+       case FC_RGBA_VBGR:
+           subpixel_order = CAIRO_SUBPIXEL_ORDER_VBGR;
+           break;
+       case FC_RGBA_UNKNOWN:
+       case FC_RGBA_NONE:
+       default:
+           subpixel_order = CAIRO_SUBPIXEL_ORDER_DEFAULT;
+           break;
+       }
+
+       if (subpixel_order != CAIRO_SUBPIXEL_ORDER_DEFAULT) {
+           ft_options.base.subpixel_order = subpixel_order;
+           ft_options.base.antialias = CAIRO_ANTIALIAS_SUBPIXEL;
+       }
+
+       if (FcPatternGetInteger (pattern,
+                                FC_LCD_FILTER, 0, &lcd_filter) == FcResultMatch)
+       {
+           switch (lcd_filter) {
+           case FC_LCD_NONE:
+               ft_options.base.lcd_filter = CAIRO_LCD_FILTER_NONE;
+               break;
+           case FC_LCD_DEFAULT:
+               ft_options.base.lcd_filter = CAIRO_LCD_FILTER_FIR5;
+               break;
+           case FC_LCD_LIGHT:
+               ft_options.base.lcd_filter = CAIRO_LCD_FILTER_FIR3;
+               break;
+           case FC_LCD_LEGACY:
+               ft_options.base.lcd_filter = CAIRO_LCD_FILTER_INTRA_PIXEL;
+               break;
+           }
+       }
+
+#ifdef FC_HINT_STYLE
+       if (FcPatternGetInteger (pattern,
+                                FC_HINT_STYLE, 0, &hintstyle) != FcResultMatch)
+           hintstyle = FC_HINT_FULL;
+
+       if (!hinting)
+           hintstyle = FC_HINT_NONE;
+
+       switch (hintstyle) {
+       case FC_HINT_NONE:
+           ft_options.base.hint_style = CAIRO_HINT_STYLE_NONE;
+           break;
+       case FC_HINT_SLIGHT:
+           ft_options.base.hint_style = CAIRO_HINT_STYLE_SLIGHT;
+           break;
+       case FC_HINT_MEDIUM:
+       default:
+           ft_options.base.hint_style = CAIRO_HINT_STYLE_MEDIUM;
+           break;
+       case FC_HINT_FULL:
+           ft_options.base.hint_style = CAIRO_HINT_STYLE_FULL;
+           break;
+       }
+#else /* !FC_HINT_STYLE */
+       if (!hinting) {
+           ft_options.base.hint_style = CAIRO_HINT_STYLE_NONE;
+       }
+#endif /* FC_HINT_STYLE */
+
+       /* Force embedded bitmaps off if no hinting requested */
+       if (ft_options.base.hint_style == CAIRO_HINT_STYLE_NONE)
+         bitmap = FcFalse;
+
+       if (!bitmap)
+           ft_options.load_flags |= FT_LOAD_NO_BITMAP;
+
+    } else {
+       ft_options.base.antialias = CAIRO_ANTIALIAS_NONE;
+    }
+
+    /* force autohinting if requested */
+    if (FcPatternGetBool (pattern,
+                         FC_AUTOHINT, 0, &autohint) != FcResultMatch)
+       autohint = FcFalse;
+
+    if (autohint)
+       ft_options.load_flags |= FT_LOAD_FORCE_AUTOHINT;
+
+    if (FcPatternGetBool (pattern,
+                         FC_VERTICAL_LAYOUT, 0, &vertical_layout) != FcResultMatch)
+       vertical_layout = FcFalse;
+
+    if (vertical_layout)
+       ft_options.load_flags |= FT_LOAD_VERTICAL_LAYOUT;
+
+#ifndef FC_EMBOLDEN
+#define FC_EMBOLDEN "embolden"
+#endif
+    if (FcPatternGetBool (pattern,
+                         FC_EMBOLDEN, 0, &embolden) != FcResultMatch)
+       embolden = FcFalse;
+
+    if (embolden)
+       ft_options.extra_flags |= CAIRO_FT_OPTIONS_EMBOLDEN;
+
+    *ret = ft_options;
+}
+#endif
+
+static void
+_cairo_ft_options_merge (cairo_ft_options_t *options,
+                        cairo_ft_options_t *other)
+{
+    int load_flags = other->load_flags;
+    int load_target = FT_LOAD_TARGET_NORMAL;
+
+    /* clear load target mode */
+    load_flags &= ~(FT_LOAD_TARGET_(FT_LOAD_TARGET_MODE(other->load_flags)));
+
+    if (load_flags & FT_LOAD_NO_HINTING)
+       other->base.hint_style = CAIRO_HINT_STYLE_NONE;
+
+    if (other->base.antialias == CAIRO_ANTIALIAS_NONE ||
+       options->base.antialias == CAIRO_ANTIALIAS_NONE) {
+       options->base.antialias = CAIRO_ANTIALIAS_NONE;
+       options->base.subpixel_order = CAIRO_SUBPIXEL_ORDER_DEFAULT;
+    }
+
+    if (other->base.antialias == CAIRO_ANTIALIAS_SUBPIXEL &&
+       (options->base.antialias == CAIRO_ANTIALIAS_DEFAULT ||
+        options->base.antialias == CAIRO_ANTIALIAS_GRAY)) {
+       options->base.antialias = CAIRO_ANTIALIAS_SUBPIXEL;
+       options->base.subpixel_order = other->base.subpixel_order;
+    }
+
+    if (options->base.hint_style == CAIRO_HINT_STYLE_DEFAULT)
+       options->base.hint_style = other->base.hint_style;
+
+    if (other->base.hint_style == CAIRO_HINT_STYLE_NONE)
+       options->base.hint_style = CAIRO_HINT_STYLE_NONE;
+
+    if (options->base.lcd_filter == CAIRO_LCD_FILTER_DEFAULT)
+       options->base.lcd_filter = other->base.lcd_filter;
+
+    if (other->base.lcd_filter == CAIRO_LCD_FILTER_NONE)
+       options->base.lcd_filter = CAIRO_LCD_FILTER_NONE;
+
+    if (options->base.antialias == CAIRO_ANTIALIAS_NONE) {
+       if (options->base.hint_style == CAIRO_HINT_STYLE_NONE)
+           load_flags |= FT_LOAD_NO_HINTING;
+       else
+           load_target = FT_LOAD_TARGET_MONO;
+       load_flags |= FT_LOAD_MONOCHROME;
+    } else {
+       switch (options->base.hint_style) {
+       case CAIRO_HINT_STYLE_NONE:
+           load_flags |= FT_LOAD_NO_HINTING;
+           break;
+       case CAIRO_HINT_STYLE_SLIGHT:
+           load_target = FT_LOAD_TARGET_LIGHT;
+           break;
+       case CAIRO_HINT_STYLE_MEDIUM:
+           break;
+       case CAIRO_HINT_STYLE_FULL:
+       case CAIRO_HINT_STYLE_DEFAULT:
+           if (options->base.antialias == CAIRO_ANTIALIAS_SUBPIXEL) {
+               switch (options->base.subpixel_order) {
+               case CAIRO_SUBPIXEL_ORDER_DEFAULT:
+               case CAIRO_SUBPIXEL_ORDER_RGB:
+               case CAIRO_SUBPIXEL_ORDER_BGR:
+                   load_target = FT_LOAD_TARGET_LCD;
+                   break;
+               case CAIRO_SUBPIXEL_ORDER_VRGB:
+               case CAIRO_SUBPIXEL_ORDER_VBGR:
+                   load_target = FT_LOAD_TARGET_LCD_V;
+               break;
+               }
+           }
+           break;
+       }
+    }
+
+    options->load_flags = load_flags | load_target;
+    options->extra_flags = other->extra_flags;
+    if (options->base.hint_metrics != CAIRO_HINT_METRICS_OFF)
+       options->extra_flags |= CAIRO_FT_OPTIONS_HINT_METRICS;
+}
+
+static cairo_status_t
+_cairo_ft_font_face_scaled_font_create (void               *abstract_font_face,
+                                       const cairo_matrix_t     *font_matrix,
+                                       const cairo_matrix_t     *ctm,
+                                       const cairo_font_options_t *options,
+                                       cairo_scaled_font_t       **font_out)
+{
+    cairo_ft_font_face_t *font_face = abstract_font_face;
+    cairo_ft_scaled_font_t *scaled_font;
+    FT_Face face;
+    FT_Size_Metrics *metrics;
+    cairo_font_extents_t fs_metrics;
+    cairo_status_t status;
+    cairo_ft_unscaled_font_t *unscaled;
+
+    assert (font_face->unscaled);
+
+    face = _cairo_ft_unscaled_font_lock_face (font_face->unscaled);
+    if (unlikely (face == NULL)) /* backend error */
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    scaled_font = malloc (sizeof (cairo_ft_scaled_font_t));
+    if (unlikely (scaled_font == NULL)) {
+       status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+       goto FAIL;
+    }
+
+    scaled_font->unscaled = unscaled = font_face->unscaled;
+    _cairo_unscaled_font_reference (&unscaled->base);
+
+    _cairo_font_options_init_copy (&scaled_font->ft_options.base, options);
+    _cairo_ft_options_merge (&scaled_font->ft_options, &font_face->ft_options);
+
+    status = _cairo_scaled_font_init (&scaled_font->base,
+                                     &font_face->base,
+                                     font_matrix, ctm, options,
+                                     &_cairo_ft_scaled_font_backend);
+    if (unlikely (status))
+       goto CLEANUP_SCALED_FONT;
+
+    status = _cairo_ft_unscaled_font_set_scale (unscaled,
+                                               &scaled_font->base.scale);
+    if (unlikely (status)) {
+       /* This can only fail if we encounter an error with the underlying
+        * font, so propagate the error back to the font-face. */
+       _cairo_ft_unscaled_font_unlock_face (unscaled);
+       _cairo_unscaled_font_destroy (&unscaled->base);
+       free (scaled_font);
+       return status;
+    }
+
+
+    metrics = &face->size->metrics;
+
+    /*
+     * Get to unscaled metrics so that the upper level can get back to
+     * user space
+     *
+     * Also use this path for bitmap-only fonts.  The other branch uses
+     * face members that are only relevant for scalable fonts.  This is
+     * detected by simply checking for units_per_EM==0.
+     */
+    if (scaled_font->base.options.hint_metrics != CAIRO_HINT_METRICS_OFF ||
+       face->units_per_EM == 0) {
+       double x_factor, y_factor;
+
+       if (unscaled->x_scale == 0)
+           x_factor = 0;
+       else
+           x_factor = 1 / unscaled->x_scale;
+
+       if (unscaled->y_scale == 0)
+           y_factor = 0;
+       else
+           y_factor = 1 / unscaled->y_scale;
+
+       fs_metrics.ascent =        DOUBLE_FROM_26_6(metrics->ascender) * y_factor;
+       fs_metrics.descent =       DOUBLE_FROM_26_6(- metrics->descender) * y_factor;
+       fs_metrics.height =        DOUBLE_FROM_26_6(metrics->height) * y_factor;
+       if (!_cairo_ft_scaled_font_is_vertical (&scaled_font->base)) {
+           fs_metrics.max_x_advance = DOUBLE_FROM_26_6(metrics->max_advance) * x_factor;
+           fs_metrics.max_y_advance = 0;
+       } else {
+           fs_metrics.max_x_advance = 0;
+           fs_metrics.max_y_advance = DOUBLE_FROM_26_6(metrics->max_advance) * y_factor;
+       }
+    } else {
+       double scale = face->units_per_EM;
+
+       fs_metrics.ascent =        face->ascender / scale;
+       fs_metrics.descent =       - face->descender / scale;
+       fs_metrics.height =        face->height / scale;
+       if (!_cairo_ft_scaled_font_is_vertical (&scaled_font->base)) {
+           fs_metrics.max_x_advance = face->max_advance_width / scale;
+           fs_metrics.max_y_advance = 0;
+       } else {
+           fs_metrics.max_x_advance = 0;
+           fs_metrics.max_y_advance = face->max_advance_height / scale;
+       }
+    }
+
+    status = _cairo_scaled_font_set_metrics (&scaled_font->base, &fs_metrics);
+    if (unlikely (status))
+       goto CLEANUP_SCALED_FONT;
+
+    _cairo_ft_unscaled_font_unlock_face (unscaled);
+
+    *font_out = &scaled_font->base;
+    return CAIRO_STATUS_SUCCESS;
+
+  CLEANUP_SCALED_FONT:
+    _cairo_unscaled_font_destroy (&unscaled->base);
+    free (scaled_font);
+  FAIL:
+    _cairo_ft_unscaled_font_unlock_face (font_face->unscaled);
+    *font_out = _cairo_scaled_font_create_in_error (status);
+    return CAIRO_STATUS_SUCCESS; /* non-backend error */
+}
+
+cairo_bool_t
+_cairo_scaled_font_is_ft (cairo_scaled_font_t *scaled_font)
+{
+    return scaled_font->backend == &_cairo_ft_scaled_font_backend;
+}
+
+static void
+_cairo_ft_scaled_font_fini (void *abstract_font)
+{
+    cairo_ft_scaled_font_t *scaled_font = abstract_font;
+
+    if (scaled_font == NULL)
+        return;
+
+    _cairo_unscaled_font_destroy (&scaled_font->unscaled->base);
+}
+
+static int
+_move_to (FT_Vector *to, void *closure)
+{
+    cairo_path_fixed_t *path = closure;
+    cairo_fixed_t x, y;
+
+    x = _cairo_fixed_from_26_6 (to->x);
+    y = _cairo_fixed_from_26_6 (to->y);
+
+    if (_cairo_path_fixed_close_path (path) != CAIRO_STATUS_SUCCESS)
+       return 1;
+    if (_cairo_path_fixed_move_to (path, x, y) != CAIRO_STATUS_SUCCESS)
+       return 1;
+
+    return 0;
+}
+
+static int
+_line_to (FT_Vector *to, void *closure)
+{
+    cairo_path_fixed_t *path = closure;
+    cairo_fixed_t x, y;
+
+    x = _cairo_fixed_from_26_6 (to->x);
+    y = _cairo_fixed_from_26_6 (to->y);
+
+    if (_cairo_path_fixed_line_to (path, x, y) != CAIRO_STATUS_SUCCESS)
+       return 1;
+
+    return 0;
+}
+
+static int
+_conic_to (FT_Vector *control, FT_Vector *to, void *closure)
+{
+    cairo_path_fixed_t *path = closure;
+
+    cairo_fixed_t x0, y0;
+    cairo_fixed_t x1, y1;
+    cairo_fixed_t x2, y2;
+    cairo_fixed_t x3, y3;
+    cairo_point_t conic;
+
+    if (! _cairo_path_fixed_get_current_point (path, &x0, &y0))
+       return 1;
+
+    conic.x = _cairo_fixed_from_26_6 (control->x);
+    conic.y = _cairo_fixed_from_26_6 (control->y);
+
+    x3 = _cairo_fixed_from_26_6 (to->x);
+    y3 = _cairo_fixed_from_26_6 (to->y);
+
+    x1 = x0 + 2.0/3.0 * (conic.x - x0);
+    y1 = y0 + 2.0/3.0 * (conic.y - y0);
+
+    x2 = x3 + 2.0/3.0 * (conic.x - x3);
+    y2 = y3 + 2.0/3.0 * (conic.y - y3);
+
+    if (_cairo_path_fixed_curve_to (path,
+                                   x1, y1,
+                                   x2, y2,
+                                   x3, y3) != CAIRO_STATUS_SUCCESS)
+       return 1;
+
+    return 0;
+}
+
+static int
+_cubic_to (FT_Vector *control1, FT_Vector *control2,
+          FT_Vector *to, void *closure)
+{
+    cairo_path_fixed_t *path = closure;
+    cairo_fixed_t x0, y0;
+    cairo_fixed_t x1, y1;
+    cairo_fixed_t x2, y2;
+
+    x0 = _cairo_fixed_from_26_6 (control1->x);
+    y0 = _cairo_fixed_from_26_6 (control1->y);
+
+    x1 = _cairo_fixed_from_26_6 (control2->x);
+    y1 = _cairo_fixed_from_26_6 (control2->y);
+
+    x2 = _cairo_fixed_from_26_6 (to->x);
+    y2 = _cairo_fixed_from_26_6 (to->y);
+
+    if (_cairo_path_fixed_curve_to (path,
+                                   x0, y0,
+                                   x1, y1,
+                                   x2, y2) != CAIRO_STATUS_SUCCESS)
+       return 1;
+
+    return 0;
+}
+
+static cairo_status_t
+_decompose_glyph_outline (FT_Face                face,
+                         cairo_font_options_t   *options,
+                         cairo_path_fixed_t    **pathp)
+{
+    static const FT_Outline_Funcs outline_funcs = {
+       (FT_Outline_MoveToFunc)_move_to,
+       (FT_Outline_LineToFunc)_line_to,
+       (FT_Outline_ConicToFunc)_conic_to,
+       (FT_Outline_CubicToFunc)_cubic_to,
+       0, /* shift */
+       0, /* delta */
+    };
+    static const FT_Matrix invert_y = {
+       DOUBLE_TO_16_16 (1.0), 0,
+       0, DOUBLE_TO_16_16 (-1.0),
+    };
+
+    FT_GlyphSlot glyph;
+    cairo_path_fixed_t *path;
+    cairo_status_t status;
+
+    path = _cairo_path_fixed_create ();
+    if (!path)
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    glyph = face->glyph;
+
+    /* Font glyphs have an inverted Y axis compared to cairo. */
+    FT_Outline_Transform (&glyph->outline, &invert_y);
+    if (FT_Outline_Decompose (&glyph->outline, &outline_funcs, path)) {
+       _cairo_path_fixed_destroy (path);
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+    }
+
+    status = _cairo_path_fixed_close_path (path);
+    if (unlikely (status)) {
+       _cairo_path_fixed_destroy (path);
+       return status;
+    }
+
+    *pathp = path;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+/*
+ * Translate glyph to match its metrics.
+ */
+static void
+_cairo_ft_scaled_glyph_vertical_layout_bearing_fix (void        *abstract_font,
+                                                   FT_GlyphSlot glyph)
+{
+    cairo_ft_scaled_font_t *scaled_font = abstract_font;
+    FT_Vector vector;
+
+    vector.x = glyph->metrics.vertBearingX - glyph->metrics.horiBearingX;
+    vector.y = -glyph->metrics.vertBearingY - glyph->metrics.horiBearingY;
+
+    if (glyph->format == FT_GLYPH_FORMAT_OUTLINE) {
+       FT_Vector_Transform (&vector, &scaled_font->unscaled->Current_Shape);
+       FT_Outline_Translate(&glyph->outline, vector.x, vector.y);
+    } else if (glyph->format == FT_GLYPH_FORMAT_BITMAP) {
+       glyph->bitmap_left += vector.x / 64;
+       glyph->bitmap_top  += vector.y / 64;
+    }
+}
+
+static cairo_int_status_t
+_cairo_ft_scaled_glyph_init (void                      *abstract_font,
+                            cairo_scaled_glyph_t       *scaled_glyph,
+                            cairo_scaled_glyph_info_t   info)
+{
+    cairo_text_extents_t    fs_metrics;
+    cairo_ft_scaled_font_t *scaled_font = abstract_font;
+    cairo_ft_unscaled_font_t *unscaled = scaled_font->unscaled;
+    FT_GlyphSlot glyph;
+    FT_Face face;
+    FT_Error error;
+    int load_flags = scaled_font->ft_options.load_flags;
+    FT_Glyph_Metrics *metrics;
+    double x_factor, y_factor;
+    cairo_bool_t vertical_layout = FALSE;
+    cairo_status_t status;
+
+    face = _cairo_ft_unscaled_font_lock_face (unscaled);
+    if (!face)
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    status = _cairo_ft_unscaled_font_set_scale (scaled_font->unscaled,
+                                               &scaled_font->base.scale);
+    if (unlikely (status))
+       goto FAIL;
+
+    /* Ignore global advance unconditionally */
+    load_flags |= FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH;
+
+    if ((info & CAIRO_SCALED_GLYPH_INFO_PATH) != 0 &&
+       (info & CAIRO_SCALED_GLYPH_INFO_SURFACE) == 0)
+       load_flags |= FT_LOAD_NO_BITMAP;
+
+    /*
+     * Don't pass FT_LOAD_VERTICAL_LAYOUT to FT_Load_Glyph here as
+     * suggested by freetype people.
+     */
+    if (load_flags & FT_LOAD_VERTICAL_LAYOUT) {
+       load_flags &= ~FT_LOAD_VERTICAL_LAYOUT;
+       vertical_layout = TRUE;
+    }
+
+    error = FT_Load_Glyph (face,
+                          _cairo_scaled_glyph_index(scaled_glyph),
+                          load_flags);
+    /* XXX ignoring all other errors for now.  They are not fatal, typically
+     * just a glyph-not-found. */
+    if (error == FT_Err_Out_Of_Memory) {
+       status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+       goto FAIL;
+    }
+
+    glyph = face->glyph;
+
+#if HAVE_FT_GLYPHSLOT_EMBOLDEN
+    /*
+     * embolden glyphs if requested
+     */
+    if (scaled_font->ft_options.extra_flags & CAIRO_FT_OPTIONS_EMBOLDEN)
+       FT_GlyphSlot_Embolden (glyph);
+#endif
+
+    if (vertical_layout)
+       _cairo_ft_scaled_glyph_vertical_layout_bearing_fix (scaled_font, glyph);
+
+    if (info & CAIRO_SCALED_GLYPH_INFO_METRICS) {
+
+       cairo_bool_t hint_metrics = scaled_font->base.options.hint_metrics != CAIRO_HINT_METRICS_OFF;
+       /*
+        * Compute font-space metrics
+        */
+       metrics = &glyph->metrics;
+
+       if (unscaled->x_scale == 0)
+           x_factor = 0;
+       else
+           x_factor = 1 / unscaled->x_scale;
+
+       if (unscaled->y_scale == 0)
+           y_factor = 0;
+       else
+           y_factor = 1 / unscaled->y_scale;
+
+       /*
+        * Note: Y coordinates of the horizontal bearing need to be negated.
+        *
+        * Scale metrics back to glyph space from the scaled glyph space returned
+        * by FreeType
+        *
+        * If we want hinted metrics but aren't asking for hinted glyphs from
+        * FreeType, then we need to do the metric hinting ourselves.
+        */
+
+       if (hint_metrics && (load_flags & FT_LOAD_NO_HINTING))
+       {
+           FT_Pos x1, x2;
+           FT_Pos y1, y2;
+           FT_Pos advance;
+
+           if (!vertical_layout) {
+               x1 = (metrics->horiBearingX) & -64;
+               x2 = (metrics->horiBearingX + metrics->width + 63) & -64;
+               y1 = (-metrics->horiBearingY) & -64;
+               y2 = (-metrics->horiBearingY + metrics->height + 63) & -64;
+
+               advance = ((metrics->horiAdvance + 32) & -64);
+
+               fs_metrics.x_bearing = DOUBLE_FROM_26_6 (x1) * x_factor;
+               fs_metrics.y_bearing = DOUBLE_FROM_26_6 (y1) * y_factor;
+
+               fs_metrics.width  = DOUBLE_FROM_26_6 (x2 - x1) * x_factor;
+               fs_metrics.height  = DOUBLE_FROM_26_6 (y2 - y1) * y_factor;
+
+               fs_metrics.x_advance = DOUBLE_FROM_26_6 (advance) * x_factor;
+               fs_metrics.y_advance = 0;
+           } else {
+               x1 = (metrics->vertBearingX) & -64;
+               x2 = (metrics->vertBearingX + metrics->width + 63) & -64;
+               y1 = (metrics->vertBearingY) & -64;
+               y2 = (metrics->vertBearingY + metrics->height + 63) & -64;
+
+               advance = ((metrics->vertAdvance + 32) & -64);
+
+               fs_metrics.x_bearing = DOUBLE_FROM_26_6 (x1) * x_factor;
+               fs_metrics.y_bearing = DOUBLE_FROM_26_6 (y1) * y_factor;
+
+               fs_metrics.width  = DOUBLE_FROM_26_6 (x2 - x1) * x_factor;
+               fs_metrics.height  = DOUBLE_FROM_26_6 (y2 - y1) * y_factor;
+
+               fs_metrics.x_advance = 0;
+               fs_metrics.y_advance = DOUBLE_FROM_26_6 (advance) * y_factor;
+           }
+        } else {
+           fs_metrics.width  = DOUBLE_FROM_26_6 (metrics->width) * x_factor;
+           fs_metrics.height = DOUBLE_FROM_26_6 (metrics->height) * y_factor;
+
+           if (!vertical_layout) {
+               fs_metrics.x_bearing = DOUBLE_FROM_26_6 (metrics->horiBearingX) * x_factor;
+               fs_metrics.y_bearing = DOUBLE_FROM_26_6 (-metrics->horiBearingY) * y_factor;
+
+               if (hint_metrics || glyph->format != FT_GLYPH_FORMAT_OUTLINE)
+                   fs_metrics.x_advance = DOUBLE_FROM_26_6 (metrics->horiAdvance) * x_factor;
+               else
+                   fs_metrics.x_advance = DOUBLE_FROM_16_16 (glyph->linearHoriAdvance) * x_factor;
+               fs_metrics.y_advance = 0 * y_factor;
+           } else {
+               fs_metrics.x_bearing = DOUBLE_FROM_26_6 (metrics->vertBearingX) * x_factor;
+               fs_metrics.y_bearing = DOUBLE_FROM_26_6 (metrics->vertBearingY) * y_factor;
+
+               fs_metrics.x_advance = 0 * x_factor;
+               if (hint_metrics || glyph->format != FT_GLYPH_FORMAT_OUTLINE)
+                   fs_metrics.y_advance = DOUBLE_FROM_26_6 (metrics->vertAdvance) * y_factor;
+               else
+                   fs_metrics.y_advance = DOUBLE_FROM_16_16 (glyph->linearVertAdvance) * y_factor;
+           }
+        }
+
+       _cairo_scaled_glyph_set_metrics (scaled_glyph,
+                                        &scaled_font->base,
+                                        &fs_metrics);
+    }
+
+    if ((info & CAIRO_SCALED_GLYPH_INFO_SURFACE) != 0) {
+       cairo_image_surface_t   *surface;
+
+       if (glyph->format == FT_GLYPH_FORMAT_OUTLINE) {
+           status = _render_glyph_outline (face, &scaled_font->ft_options.base,
+                                           &surface);
+       } else {
+           status = _render_glyph_bitmap (face, &scaled_font->ft_options.base,
+                                          &surface);
+           if (likely (status == CAIRO_STATUS_SUCCESS) &&
+               unscaled->have_shape)
+           {
+               status = _transform_glyph_bitmap (&unscaled->current_shape,
+                                                 &surface);
+               if (unlikely (status))
+                   cairo_surface_destroy (&surface->base);
+           }
+       }
+       if (unlikely (status))
+           goto FAIL;
+
+       _cairo_scaled_glyph_set_surface (scaled_glyph,
+                                        &scaled_font->base,
+                                        surface);
+    }
+
+    if (info & CAIRO_SCALED_GLYPH_INFO_PATH) {
+       cairo_path_fixed_t *path = NULL; /* hide compiler warning */
+
+       /*
+        * A kludge -- the above code will trash the outline,
+        * so reload it. This will probably never occur though
+        */
+       if ((info & CAIRO_SCALED_GLYPH_INFO_SURFACE) != 0) {
+           error = FT_Load_Glyph (face,
+                                  _cairo_scaled_glyph_index(scaled_glyph),
+                                  load_flags | FT_LOAD_NO_BITMAP);
+           /* XXX ignoring all other errors for now.  They are not fatal, typically
+            * just a glyph-not-found. */
+           if (error == FT_Err_Out_Of_Memory) {
+               status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+               goto FAIL;
+           }
+#if HAVE_FT_GLYPHSLOT_EMBOLDEN
+           /*
+            * embolden glyphs if requested
+            */
+           if (scaled_font->ft_options.extra_flags & CAIRO_FT_OPTIONS_EMBOLDEN)
+               FT_GlyphSlot_Embolden (glyph);
+#endif
+           if (vertical_layout)
+               _cairo_ft_scaled_glyph_vertical_layout_bearing_fix (scaled_font, glyph);
+
+       }
+       if (glyph->format == FT_GLYPH_FORMAT_OUTLINE)
+           status = _decompose_glyph_outline (face, &scaled_font->ft_options.base,
+                                              &path);
+       else
+           status = CAIRO_INT_STATUS_UNSUPPORTED;
+
+       if (unlikely (status))
+           goto FAIL;
+
+       _cairo_scaled_glyph_set_path (scaled_glyph,
+                                     &scaled_font->base,
+                                     path);
+    }
+ FAIL:
+    _cairo_ft_unscaled_font_unlock_face (unscaled);
+
+    return status;
+}
+
+static unsigned long
+_cairo_ft_ucs4_to_index (void      *abstract_font,
+                        uint32_t    ucs4)
+{
+    cairo_ft_scaled_font_t *scaled_font = abstract_font;
+    cairo_ft_unscaled_font_t *unscaled = scaled_font->unscaled;
+    FT_Face face;
+    FT_UInt index;
+
+    face = _cairo_ft_unscaled_font_lock_face (unscaled);
+    if (!face)
+       return 0;
+
+#if CAIRO_HAS_FC_FONT
+    index = FcFreeTypeCharIndex (face, ucs4);
+#else
+    index = FT_Get_Char_Index (face, ucs4);
+#endif
+
+    _cairo_ft_unscaled_font_unlock_face (unscaled);
+    return index;
+}
+
+static cairo_int_status_t
+_cairo_ft_load_truetype_table (void           *abstract_font,
+                              unsigned long     tag,
+                              long              offset,
+                              unsigned char    *buffer,
+                              unsigned long    *length)
+{
+    cairo_ft_scaled_font_t *scaled_font = abstract_font;
+    cairo_ft_unscaled_font_t *unscaled = scaled_font->unscaled;
+    FT_Face face;
+    cairo_status_t status = CAIRO_INT_STATUS_UNSUPPORTED;
+
+    /* We don't support the FreeType feature of loading a table
+     * without specifying the size since this may overflow our
+     * buffer. */
+    assert (length != NULL);
+
+    if (_cairo_ft_scaled_font_is_vertical (&scaled_font->base))
+        return CAIRO_INT_STATUS_UNSUPPORTED;
+
+#if HAVE_FT_LOAD_SFNT_TABLE
+    face = _cairo_ft_unscaled_font_lock_face (unscaled);
+    if (!face)
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    if (FT_IS_SFNT (face)) {
+       if (buffer == NULL)
+           *length = 0;
+
+       if (FT_Load_Sfnt_Table (face, tag, offset, buffer, length) == 0)
+           status = CAIRO_STATUS_SUCCESS;
+    }
+
+    _cairo_ft_unscaled_font_unlock_face (unscaled);
+#endif
+
+    return status;
+}
+
+static cairo_int_status_t
+_cairo_ft_index_to_ucs4(void           *abstract_font,
+                       unsigned long    index,
+                       uint32_t        *ucs4)
+{
+    cairo_ft_scaled_font_t *scaled_font = abstract_font;
+    cairo_ft_unscaled_font_t *unscaled = scaled_font->unscaled;
+    FT_Face face;
+    FT_ULong  charcode;
+    FT_UInt   gindex;
+
+    face = _cairo_ft_unscaled_font_lock_face (unscaled);
+    if (!face)
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    *ucs4 = (uint32_t) -1;
+    charcode = FT_Get_First_Char(face, &gindex);
+    while (gindex != 0) {
+       if (gindex == index) {
+           *ucs4 = charcode;
+           break;
+       }
+       charcode = FT_Get_Next_Char (face, charcode, &gindex);
+    }
+
+    _cairo_ft_unscaled_font_unlock_face (unscaled);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_bool_t
+_cairo_ft_is_synthetic (void           *abstract_font)
+{
+    cairo_ft_scaled_font_t *scaled_font = abstract_font;
+
+    if (scaled_font->ft_options.extra_flags & CAIRO_FT_OPTIONS_EMBOLDEN)
+       return TRUE;
+    else
+       return FALSE;
+}
+
+static cairo_int_status_t
+_cairo_index_to_glyph_name (void                *abstract_font,
+                           char                **glyph_names,
+                           int                   num_glyph_names,
+                           unsigned long         glyph_index,
+                           unsigned long        *glyph_array_index)
+{
+    cairo_ft_scaled_font_t *scaled_font = abstract_font;
+    cairo_ft_unscaled_font_t *unscaled = scaled_font->unscaled;
+    FT_Face face;
+    char buffer[256]; /* PLRM spcifies max name length of 127 */
+    FT_Error error;
+    int i;
+
+    face = _cairo_ft_unscaled_font_lock_face (unscaled);
+    if (!face)
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    error = FT_Get_Glyph_Name (face, glyph_index, buffer, sizeof buffer);
+
+    _cairo_ft_unscaled_font_unlock_face (unscaled);
+
+    if (error != FT_Err_Ok) {
+       /* propagate fatal errors from FreeType */
+       if (error == FT_Err_Out_Of_Memory)
+           return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+    }
+
+    /* FT first numbers the glyphs in the order they are read from the
+     * Type 1 font. Then if .notdef is not the first glyph, the first
+     * glyph is swapped with .notdef to ensure that .notdef is at
+     * glyph index 0.
+     *
+     * As all but two glyphs in glyph_names already have the same
+     * index as the FT glyph index, we first check if
+     * glyph_names[glyph_index] is the name we are looking for. If not
+     * we fall back to searching the entire array.
+     */
+
+    if ((long)glyph_index < num_glyph_names &&
+       strcmp (glyph_names[glyph_index], buffer) == 0)
+    {
+       *glyph_array_index = glyph_index;
+
+       return CAIRO_STATUS_SUCCESS;
+    }
+
+    for (i = 0; i < num_glyph_names; i++) {
+       if (strcmp (glyph_names[i], buffer) == 0) {
+           *glyph_array_index = i;
+
+           return CAIRO_STATUS_SUCCESS;
+       }
+    }
+
+    return CAIRO_INT_STATUS_UNSUPPORTED;
+}
+
+static cairo_int_status_t
+_cairo_ft_load_type1_data (void                    *abstract_font,
+                          long              offset,
+                          unsigned char    *buffer,
+                          unsigned long    *length)
+{
+    cairo_ft_scaled_font_t *scaled_font = abstract_font;
+    cairo_ft_unscaled_font_t *unscaled = scaled_font->unscaled;
+    FT_Face face;
+    cairo_status_t status = CAIRO_STATUS_SUCCESS;
+    unsigned long available_length;
+    unsigned long ret;
+    const char *font_format;
+
+    assert (length != NULL);
+
+    if (_cairo_ft_scaled_font_is_vertical (&scaled_font->base))
+        return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    face = _cairo_ft_unscaled_font_lock_face (unscaled);
+    if (!face)
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+#if HAVE_FT_LOAD_SFNT_TABLE
+    if (FT_IS_SFNT (face)) {
+       status = CAIRO_INT_STATUS_UNSUPPORTED;
+       goto unlock;
+    }
+#endif
+
+    font_format = FT_Get_X11_Font_Format (face);
+    if (!font_format ||
+       !(strcmp (font_format, "Type 1") == 0 ||
+         strcmp (font_format, "CFF") == 0))
+    {
+        status = CAIRO_INT_STATUS_UNSUPPORTED;
+       goto unlock;
+    }
+
+    available_length = MAX (face->stream->size - offset, 0);
+    if (!buffer) {
+       *length = available_length;
+    } else {
+       if (*length > available_length) {
+           status = CAIRO_INT_STATUS_UNSUPPORTED;
+       } else if (face->stream->read != NULL) {
+           /* Note that read() may be implemented as a macro, thanks POSIX!, so we
+            * need to wrap the following usage in parentheses in order to
+            * disambiguate it for the pre-processor - using the verbose function
+            * pointer dereference for clarity.
+            */
+           ret = (* face->stream->read) (face->stream,
+                                         offset,
+                                         buffer,
+                                         *length);
+           if (ret != *length)
+               status = _cairo_error (CAIRO_STATUS_READ_ERROR);
+       } else {
+           memcpy (buffer, face->stream->base + offset, *length);
+       }
+    }
+
+  unlock:
+    _cairo_ft_unscaled_font_unlock_face (unscaled);
+
+    return status;
+}
+
+static const cairo_scaled_font_backend_t _cairo_ft_scaled_font_backend = {
+    CAIRO_FONT_TYPE_FT,
+    _cairo_ft_scaled_font_fini,
+    _cairo_ft_scaled_glyph_init,
+    NULL,                      /* text_to_glyphs */
+    _cairo_ft_ucs4_to_index,
+    _cairo_ft_load_truetype_table,
+    _cairo_ft_index_to_ucs4,
+    _cairo_ft_is_synthetic,
+    _cairo_index_to_glyph_name,
+    _cairo_ft_load_type1_data
+};
+
+/* #cairo_ft_font_face_t */
+
+#if CAIRO_HAS_FC_FONT
+static cairo_font_face_t *
+_cairo_ft_font_face_create_for_pattern (FcPattern *pattern);
+
+static cairo_status_t
+_cairo_ft_font_face_create_for_toy (cairo_toy_font_face_t *toy_face,
+                                   cairo_font_face_t **font_face_out)
+{
+    cairo_font_face_t *font_face = (cairo_font_face_t *) &_cairo_font_face_nil;
+    FcPattern *pattern;
+    int fcslant;
+    int fcweight;
+
+    pattern = FcPatternCreate ();
+    if (!pattern) {
+       _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+       return font_face->status;
+    }
+
+    if (!FcPatternAddString (pattern,
+                            FC_FAMILY, (unsigned char *) toy_face->family))
+    {
+       _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+       goto FREE_PATTERN;
+    }
+
+    switch (toy_face->slant)
+    {
+    case CAIRO_FONT_SLANT_ITALIC:
+        fcslant = FC_SLANT_ITALIC;
+        break;
+    case CAIRO_FONT_SLANT_OBLIQUE:
+       fcslant = FC_SLANT_OBLIQUE;
+        break;
+    case CAIRO_FONT_SLANT_NORMAL:
+    default:
+        fcslant = FC_SLANT_ROMAN;
+        break;
+    }
+
+    if (!FcPatternAddInteger (pattern, FC_SLANT, fcslant)) {
+       _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+       goto FREE_PATTERN;
+    }
+
+    switch (toy_face->weight)
+    {
+    case CAIRO_FONT_WEIGHT_BOLD:
+        fcweight = FC_WEIGHT_BOLD;
+        break;
+    case CAIRO_FONT_WEIGHT_NORMAL:
+    default:
+        fcweight = FC_WEIGHT_MEDIUM;
+        break;
+    }
+
+    if (!FcPatternAddInteger (pattern, FC_WEIGHT, fcweight)) {
+       _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+       goto FREE_PATTERN;
+    }
+
+    font_face = _cairo_ft_font_face_create_for_pattern (pattern);
+
+ FREE_PATTERN:
+    FcPatternDestroy (pattern);
+
+    *font_face_out = font_face;
+    return font_face->status;
+}
+#endif
+
+static void
+_cairo_ft_font_face_destroy (void *abstract_face)
+{
+    cairo_ft_font_face_t *font_face = abstract_face;
+
+    /* When destroying a face created by cairo_ft_font_face_create_for_ft_face,
+     * we have a special "zombie" state for the face when the unscaled font
+     * is still alive but there are no other references to a font face with
+     * the same FT_Face.
+     *
+     * We go from:
+     *
+     *   font_face ------> unscaled
+     *        <-....weak....../
+     *
+     * To:
+     *
+     *    font_face <------- unscaled
+     */
+
+    if (font_face->unscaled &&
+       font_face->unscaled->from_face &&
+       font_face->next == NULL &&
+       font_face->unscaled->faces == font_face &&
+       CAIRO_REFERENCE_COUNT_GET_VALUE (&font_face->unscaled->base.ref_count) > 1)
+    {
+       cairo_font_face_reference (&font_face->base);
+
+       _cairo_unscaled_font_destroy (&font_face->unscaled->base);
+       font_face->unscaled = NULL;
+
+       return;
+    }
+
+    if (font_face->unscaled) {
+       cairo_ft_font_face_t *tmp_face = NULL;
+       cairo_ft_font_face_t *last_face = NULL;
+
+       /* Remove face from linked list */
+       for (tmp_face = font_face->unscaled->faces;
+            tmp_face;
+            tmp_face = tmp_face->next)
+       {
+           if (tmp_face == font_face) {
+               if (last_face)
+                   last_face->next = tmp_face->next;
+               else
+                   font_face->unscaled->faces = tmp_face->next;
+           }
+
+           last_face = tmp_face;
+       }
+
+       _cairo_unscaled_font_destroy (&font_face->unscaled->base);
+       font_face->unscaled = NULL;
+    }
+
+#if CAIRO_HAS_FC_FONT
+    if (font_face->pattern) {
+       FcPatternDestroy (font_face->pattern);
+       cairo_font_face_destroy (font_face->resolved_font_face);
+    }
+#endif
+}
+
+static cairo_font_face_t *
+_cairo_ft_font_face_get_implementation (void                     *abstract_face,
+                                       const cairo_matrix_t       *font_matrix,
+                                       const cairo_matrix_t       *ctm,
+                                       const cairo_font_options_t *options)
+{
+    cairo_ft_font_face_t      *font_face = abstract_face;
+
+    /* The handling of font options is different depending on how the
+     * font face was created. When the user creates a font face with
+     * cairo_ft_font_face_create_for_ft_face(), then the load flags
+     * passed in augment the load flags for the options.  But for
+     * cairo_ft_font_face_create_for_pattern(), the load flags are
+     * derived from a pattern where the user has called
+     * cairo_ft_font_options_substitute(), so *just* use those load
+     * flags and ignore the options.
+     */
+
+#if CAIRO_HAS_FC_FONT
+    /* If we have an unresolved pattern, resolve it and create
+     * unscaled font.  Otherwise, use the ones stored in font_face.
+     */
+    if (font_face->pattern) {
+       cairo_font_face_t *resolved;
+
+       /* Cache the resolved font whilst the FcConfig remains consistent. */
+       resolved = font_face->resolved_font_face;
+       if (resolved != NULL) {
+           if (! FcInitBringUptoDate ()) {
+               _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+               return (cairo_font_face_t *) &_cairo_font_face_nil;
+           }
+
+           if (font_face->resolved_config == FcConfigGetCurrent ())
+               return cairo_font_face_reference (resolved);
+
+           cairo_font_face_destroy (resolved);
+           font_face->resolved_font_face = NULL;
+       }
+
+       resolved = _cairo_ft_resolve_pattern (font_face->pattern,
+                                             font_matrix,
+                                             ctm,
+                                             options);
+       if (unlikely (resolved->status))
+           return resolved;
+
+       font_face->resolved_font_face = cairo_font_face_reference (resolved);
+       font_face->resolved_config = FcConfigGetCurrent ();
+
+       return resolved;
+    }
+#endif
+
+    return abstract_face;
+}
+
+const cairo_font_face_backend_t _cairo_ft_font_face_backend = {
+    CAIRO_FONT_TYPE_FT,
+#if CAIRO_HAS_FC_FONT
+    _cairo_ft_font_face_create_for_toy,
+#else
+    NULL,
+#endif
+    _cairo_ft_font_face_destroy,
+    _cairo_ft_font_face_scaled_font_create,
+    _cairo_ft_font_face_get_implementation
+};
+
+#if CAIRO_HAS_FC_FONT
+static cairo_font_face_t *
+_cairo_ft_font_face_create_for_pattern (FcPattern *pattern)
+{
+    cairo_ft_font_face_t *font_face;
+
+    font_face = malloc (sizeof (cairo_ft_font_face_t));
+    if (unlikely (font_face == NULL)) {
+       _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+       return (cairo_font_face_t *) &_cairo_font_face_nil;
+    }
+
+    font_face->unscaled = NULL;
+    font_face->next = NULL;
+
+    font_face->pattern = FcPatternDuplicate (pattern);
+    if (unlikely (font_face->pattern == NULL)) {
+       free (font_face);
+       _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+       return (cairo_font_face_t *) &_cairo_font_face_nil;
+    }
+
+    font_face->resolved_font_face = NULL;
+    font_face->resolved_config = NULL;
+
+    _cairo_font_face_init (&font_face->base, &_cairo_ft_font_face_backend);
+
+    return &font_face->base;
+}
+#endif
+
+static cairo_font_face_t *
+_cairo_ft_font_face_create (cairo_ft_unscaled_font_t *unscaled,
+                           cairo_ft_options_t       *ft_options)
+{
+    cairo_ft_font_face_t *font_face, **prev_font_face;
+
+    /* Looked for an existing matching font face */
+    for (font_face = unscaled->faces, prev_font_face = &unscaled->faces;
+        font_face;
+        prev_font_face = &font_face->next, font_face = font_face->next)
+    {
+       if (font_face->ft_options.load_flags == ft_options->load_flags &&
+           font_face->ft_options.extra_flags == ft_options->extra_flags &&
+           cairo_font_options_equal (&font_face->ft_options.base, &ft_options->base))
+       {
+           if (font_face->base.status) {
+               /* The font_face has been left in an error state, abandon it. */
+               *prev_font_face = font_face->next;
+               break;
+           }
+
+           if (font_face->unscaled == NULL) {
+               /* Resurrect this "zombie" font_face (from
+                * _cairo_ft_font_face_destroy), switching its unscaled_font
+                * from owner to ownee. */
+               font_face->unscaled = unscaled;
+               _cairo_unscaled_font_reference (&unscaled->base);
+               return &font_face->base;
+           } else
+               return cairo_font_face_reference (&font_face->base);
+       }
+    }
+
+    /* No match found, create a new one */
+    font_face = malloc (sizeof (cairo_ft_font_face_t));
+    if (unlikely (!font_face)) {
+       _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+       return (cairo_font_face_t *)&_cairo_font_face_nil;
+    }
+
+    font_face->unscaled = unscaled;
+    _cairo_unscaled_font_reference (&unscaled->base);
+
+    font_face->ft_options = *ft_options;
+
+    if (unscaled->faces && unscaled->faces->unscaled == NULL) {
+       /* This "zombie" font_face (from _cairo_ft_font_face_destroy)
+        * is no longer needed. */
+       assert (unscaled->from_face && unscaled->faces->next == NULL);
+       cairo_font_face_destroy (&unscaled->faces->base);
+       unscaled->faces = NULL;
+    }
+
+    font_face->next = unscaled->faces;
+    unscaled->faces = font_face;
+
+#if CAIRO_HAS_FC_FONT
+    font_face->pattern = NULL;
+#endif
+
+    _cairo_font_face_init (&font_face->base, &_cairo_ft_font_face_backend);
+
+    return &font_face->base;
+}
+
+/* implement the platform-specific interface */
+
+#if CAIRO_HAS_FC_FONT
+static cairo_status_t
+_cairo_ft_font_options_substitute (const cairo_font_options_t *options,
+                                  FcPattern                  *pattern)
+{
+    FcValue v;
+
+    if (options->antialias != CAIRO_ANTIALIAS_DEFAULT)
+    {
+       if (FcPatternGet (pattern, FC_ANTIALIAS, 0, &v) == FcResultNoMatch)
+       {
+           if (! FcPatternAddBool (pattern,
+                                   FC_ANTIALIAS,
+                                   options->antialias != CAIRO_ANTIALIAS_NONE))
+               return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+           if (options->antialias != CAIRO_ANTIALIAS_SUBPIXEL) {
+               FcPatternDel (pattern, FC_RGBA);
+               if (! FcPatternAddInteger (pattern, FC_RGBA, FC_RGBA_NONE))
+                   return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+           }
+       }
+    }
+
+    if (options->antialias != CAIRO_ANTIALIAS_DEFAULT)
+    {
+       if (FcPatternGet (pattern, FC_RGBA, 0, &v) == FcResultNoMatch)
+       {
+           int rgba;
+
+           if (options->antialias == CAIRO_ANTIALIAS_SUBPIXEL) {
+               switch (options->subpixel_order) {
+               case CAIRO_SUBPIXEL_ORDER_DEFAULT:
+               case CAIRO_SUBPIXEL_ORDER_RGB:
+               default:
+                   rgba = FC_RGBA_RGB;
+                   break;
+               case CAIRO_SUBPIXEL_ORDER_BGR:
+                   rgba = FC_RGBA_BGR;
+                   break;
+               case CAIRO_SUBPIXEL_ORDER_VRGB:
+                   rgba = FC_RGBA_VRGB;
+                   break;
+               case CAIRO_SUBPIXEL_ORDER_VBGR:
+                   rgba = FC_RGBA_VBGR;
+                   break;
+               }
+           } else {
+               rgba = FC_RGBA_NONE;
+           }
+
+           if (! FcPatternAddInteger (pattern, FC_RGBA, rgba))
+               return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+       }
+    }
+
+    if (options->lcd_filter != CAIRO_LCD_FILTER_DEFAULT)
+    {
+       if (FcPatternGet (pattern, FC_LCD_FILTER, 0, &v) == FcResultNoMatch)
+       {
+           int lcd_filter;
+
+           switch (options->lcd_filter) {
+           case CAIRO_LCD_FILTER_NONE:
+               lcd_filter = FT_LCD_FILTER_NONE;
+               break;
+           case CAIRO_LCD_FILTER_DEFAULT:
+           case CAIRO_LCD_FILTER_INTRA_PIXEL:
+               lcd_filter = FT_LCD_FILTER_LEGACY;
+               break;
+           case CAIRO_LCD_FILTER_FIR3:
+               lcd_filter = FT_LCD_FILTER_LIGHT;
+               break;
+           default:
+           case CAIRO_LCD_FILTER_FIR5:
+               lcd_filter = FT_LCD_FILTER_DEFAULT;
+               break;
+           }
+
+           if (! FcPatternAddInteger (pattern, FC_LCD_FILTER, lcd_filter))
+               return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+       }
+    }
+
+    if (options->hint_style != CAIRO_HINT_STYLE_DEFAULT)
+    {
+       if (FcPatternGet (pattern, FC_HINTING, 0, &v) == FcResultNoMatch)
+       {
+           if (! FcPatternAddBool (pattern,
+                                   FC_HINTING,
+                                   options->hint_style != CAIRO_HINT_STYLE_NONE))
+               return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+       }
+
+#ifdef FC_HINT_STYLE
+       if (FcPatternGet (pattern, FC_HINT_STYLE, 0, &v) == FcResultNoMatch)
+       {
+           int hint_style;
+
+           switch (options->hint_style) {
+           case CAIRO_HINT_STYLE_NONE:
+               hint_style = FC_HINT_NONE;
+               break;
+           case CAIRO_HINT_STYLE_SLIGHT:
+               hint_style = FC_HINT_SLIGHT;
+               break;
+           case CAIRO_HINT_STYLE_MEDIUM:
+               hint_style = FC_HINT_MEDIUM;
+               break;
+           case CAIRO_HINT_STYLE_FULL:
+           case CAIRO_HINT_STYLE_DEFAULT:
+           default:
+               hint_style = FC_HINT_FULL;
+               break;
+           }
+
+           if (! FcPatternAddInteger (pattern, FC_HINT_STYLE, hint_style))
+               return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+       }
+#endif
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+/**
+ * cairo_ft_font_options_substitute:
+ * @options: a #cairo_font_options_t object
+ * @pattern: an existing #FcPattern
+ *
+ * Add options to a #FcPattern based on a #cairo_font_options_t font
+ * options object. Options that are already in the pattern, are not overridden,
+ * so you should call this function after calling FcConfigSubstitute() (the
+ * user's settings should override options based on the surface type), but
+ * before calling FcDefaultSubstitute().
+ **/
+void
+cairo_ft_font_options_substitute (const cairo_font_options_t *options,
+                                 FcPattern                  *pattern)
+{
+    if (cairo_font_options_status ((cairo_font_options_t *) options))
+       return;
+
+    _cairo_ft_font_options_substitute (options, pattern);
+}
+
+static cairo_font_face_t *
+_cairo_ft_resolve_pattern (FcPattern                 *pattern,
+                          const cairo_matrix_t       *font_matrix,
+                          const cairo_matrix_t       *ctm,
+                          const cairo_font_options_t *font_options)
+{
+    cairo_status_t status;
+
+    cairo_matrix_t scale;
+    FcPattern *resolved;
+    cairo_ft_font_transform_t sf;
+    FcResult result;
+    cairo_ft_unscaled_font_t *unscaled;
+    cairo_ft_options_t ft_options;
+    cairo_font_face_t *font_face;
+
+    scale = *ctm;
+    scale.x0 = scale.y0 = 0;
+    cairo_matrix_multiply (&scale,
+                           font_matrix,
+                           &scale);
+
+    status = _compute_transform (&sf, &scale);
+    if (unlikely (status))
+       return (cairo_font_face_t *)&_cairo_font_face_nil;
+
+    pattern = FcPatternDuplicate (pattern);
+    if (pattern == NULL)
+       return (cairo_font_face_t *)&_cairo_font_face_nil;
+
+    if (! FcPatternAddDouble (pattern, FC_PIXEL_SIZE, sf.y_scale)) {
+       font_face = (cairo_font_face_t *)&_cairo_font_face_nil;
+       goto FREE_PATTERN;
+    }
+
+    if (! FcConfigSubstitute (NULL, pattern, FcMatchPattern)) {
+       font_face = (cairo_font_face_t *)&_cairo_font_face_nil;
+       goto FREE_PATTERN;
+    }
+
+    status = _cairo_ft_font_options_substitute (font_options, pattern);
+    if (status) {
+       font_face = (cairo_font_face_t *)&_cairo_font_face_nil;
+       goto FREE_PATTERN;
+    }
+
+    FcDefaultSubstitute (pattern);
+
+    status = _cairo_ft_unscaled_font_create_for_pattern (pattern, &unscaled);
+    if (unlikely (status)) {
+       font_face = (cairo_font_face_t *)&_cairo_font_face_nil;
+       goto FREE_PATTERN;
+    }
+
+    if (unscaled == NULL) {
+       resolved = FcFontMatch (NULL, pattern, &result);
+       if (!resolved) {
+           /* We failed to find any font. Substitute twin so that the user can
+            * see something (and hopefully recognise that the font is missing)
+            * and not just receive a NO_MEMORY error during rendering.
+            */
+           font_face = _cairo_font_face_twin_create_fallback ();
+           goto FREE_PATTERN;
+       }
+
+       status = _cairo_ft_unscaled_font_create_for_pattern (resolved, &unscaled);
+       if (unlikely (status || unscaled == NULL)) {
+           font_face = (cairo_font_face_t *)&_cairo_font_face_nil;
+           goto FREE_RESOLVED;
+       }
+    } else
+       resolved = pattern;
+
+    _get_pattern_ft_options (resolved, &ft_options);
+    font_face = _cairo_ft_font_face_create (unscaled, &ft_options);
+    _cairo_unscaled_font_destroy (&unscaled->base);
+
+FREE_RESOLVED:
+    if (resolved != pattern)
+       FcPatternDestroy (resolved);
+
+FREE_PATTERN:
+    FcPatternDestroy (pattern);
+
+    return font_face;
+}
+
+/**
+ * cairo_ft_font_face_create_for_pattern:
+ * @pattern: A fontconfig pattern.  Cairo makes a copy of the pattern
+ * if it needs to.  You are free to modify or free @pattern after this call.
+ *
+ * Creates a new font face for the FreeType font backend based on a
+ * fontconfig pattern. This font can then be used with
+ * cairo_set_font_face() or cairo_scaled_font_create(). The
+ * #cairo_scaled_font_t returned from cairo_scaled_font_create() is
+ * also for the FreeType backend and can be used with functions such
+ * as cairo_ft_scaled_font_lock_face().
+ *
+ * Font rendering options are represented both here and when you
+ * call cairo_scaled_font_create(). Font options that have a representation
+ * in a #FcPattern must be passed in here; to modify #FcPattern
+ * appropriately to reflect the options in a #cairo_font_options_t, call
+ * cairo_ft_font_options_substitute().
+ *
+ * The pattern's FC_FT_FACE element is inspected first and if that is set,
+ * that will be the FreeType font face associated with the returned cairo
+ * font face.  Otherwise the FC_FILE element is checked.  If it's set,
+ * that and the value of the FC_INDEX element (defaults to zero) of @pattern
+ * are used to load a font face from file.
+ *
+ * If both steps from the previous paragraph fails, @pattern will be passed
+ * to FcConfigSubstitute, FcDefaultSubstitute, and finally FcFontMatch,
+ * and the resulting font pattern is used.
+ *
+ * If the FC_FT_FACE element of @pattern is set, the user is responsible
+ * for making sure that the referenced FT_Face remains valid for the life
+ * time of the returned #cairo_font_face_t.  See
+ * cairo_ft_font_face_create_for_ft_face() for an exmaple of how to couple
+ * the life time of the FT_Face to that of the cairo font-face.
+ *
+ * Return value: a newly created #cairo_font_face_t. Free with
+ *  cairo_font_face_destroy() when you are done using it.
+ **/
+cairo_font_face_t *
+cairo_ft_font_face_create_for_pattern (FcPattern *pattern)
+{
+    cairo_ft_unscaled_font_t *unscaled;
+    cairo_font_face_t *font_face;
+    cairo_ft_options_t ft_options;
+    cairo_status_t status;
+
+    status = _cairo_ft_unscaled_font_create_for_pattern (pattern, &unscaled);
+    if (unlikely (status))
+       return (cairo_font_face_t *) &_cairo_font_face_nil;
+    if (unlikely (unscaled == NULL)) {
+       /* Store the pattern.  We will resolve it and create unscaled
+        * font when creating scaled fonts */
+       return _cairo_ft_font_face_create_for_pattern (pattern);
+    }
+
+    _get_pattern_ft_options (pattern, &ft_options);
+    font_face = _cairo_ft_font_face_create (unscaled, &ft_options);
+    _cairo_unscaled_font_destroy (&unscaled->base);
+
+    return font_face;
+}
+#endif
+
+/**
+ * cairo_ft_font_face_create_for_ft_face:
+ * @face: A FreeType face object, already opened. This must
+ *   be kept around until the face's ref_count drops to
+ *   zero and it is freed. Since the face may be referenced
+ *   internally to Cairo, the best way to determine when it
+ *   is safe to free the face is to pass a
+ *   #cairo_destroy_func_t to cairo_font_face_set_user_data()
+ * @load_flags: flags to pass to FT_Load_Glyph when loading
+ *   glyphs from the font. These flags are OR'ed together with
+ *   the flags derived from the #cairo_font_options_t passed
+ *   to cairo_scaled_font_create(), so only a few values such
+ *   as %FT_LOAD_VERTICAL_LAYOUT, and %FT_LOAD_FORCE_AUTOHINT
+ *   are useful. You should not pass any of the flags affecting
+ *   the load target, such as %FT_LOAD_TARGET_LIGHT.
+ *
+ * Creates a new font face for the FreeType font backend from a
+ * pre-opened FreeType face. This font can then be used with
+ * cairo_set_font_face() or cairo_scaled_font_create(). The
+ * #cairo_scaled_font_t returned from cairo_scaled_font_create() is
+ * also for the FreeType backend and can be used with functions such
+ * as cairo_ft_scaled_font_lock_face(). Note that Cairo may keep a reference
+ * to the FT_Face alive in a font-cache and the exact lifetime of the reference
+ * depends highly upon the exact usage pattern and is subject to external
+ * factors. You must not call FT_Done_Face() before the last reference to the
+ * #cairo_font_face_t has been dropped.
+ *
+ * As an example, below is how one might correctly couple the lifetime of
+ * the FreeType face object to the #cairo_font_face_t.
+ *
+ * <informalexample><programlisting>
+ * static const cairo_user_data_key_t key;
+ *
+ * font_face = cairo_ft_font_face_create_for_ft_face (ft_face, 0);
+ * status = cairo_font_face_set_user_data (font_face, &key,
+ *                                ft_face, (cairo_destroy_func_t) FT_Done_Face);
+ * if (status) {
+ *    cairo_font_face_destroy (font_face);
+ *    FT_Done_Face (ft_face);
+ *    return ERROR;
+ * }
+ * </programlisting></informalexample>
+ *
+ * Return value: a newly created #cairo_font_face_t. Free with
+ *  cairo_font_face_destroy() when you are done using it.
+ **/
+cairo_font_face_t *
+cairo_ft_font_face_create_for_ft_face (FT_Face         face,
+                                      int             load_flags)
+{
+    cairo_ft_unscaled_font_t *unscaled;
+    cairo_font_face_t *font_face;
+    cairo_ft_options_t ft_options;
+    cairo_status_t status;
+
+    status = _cairo_ft_unscaled_font_create_from_face (face, &unscaled);
+    if (unlikely (status))
+       return (cairo_font_face_t *)&_cairo_font_face_nil;
+
+    ft_options.load_flags = load_flags;
+    ft_options.extra_flags = 0;
+    _cairo_font_options_init_default (&ft_options.base);
+
+    font_face = _cairo_ft_font_face_create (unscaled, &ft_options);
+    _cairo_unscaled_font_destroy (&unscaled->base);
+
+    return font_face;
+}
+
+/**
+ * cairo_ft_scaled_font_lock_face:
+ * @scaled_font: A #cairo_scaled_font_t from the FreeType font backend. Such an
+ *   object can be created by calling cairo_scaled_font_create() on a
+ *   FreeType backend font face (see cairo_ft_font_face_create_for_pattern(),
+ *   cairo_ft_font_face_create_for_ft_face()).
+ *
+ * cairo_ft_scaled_font_lock_face() gets the #FT_Face object from a FreeType
+ * backend font and scales it appropriately for the font. You must
+ * release the face with cairo_ft_scaled_font_unlock_face()
+ * when you are done using it.  Since the #FT_Face object can be
+ * shared between multiple #cairo_scaled_font_t objects, you must not
+ * lock any other font objects until you unlock this one. A count is
+ * kept of the number of times cairo_ft_scaled_font_lock_face() is
+ * called. cairo_ft_scaled_font_unlock_face() must be called the same number
+ * of times.
+ *
+ * You must be careful when using this function in a library or in a
+ * threaded application, because freetype's design makes it unsafe to
+ * call freetype functions simultaneously from multiple threads, (even
+ * if using distinct FT_Face objects). Because of this, application
+ * code that acquires an FT_Face object with this call must add its
+ * own locking to protect any use of that object, (and which also must
+ * protect any other calls into cairo as almost any cairo function
+ * might result in a call into the freetype library).
+ *
+ * Return value: The #FT_Face object for @font, scaled appropriately,
+ * or %NULL if @scaled_font is in an error state (see
+ * cairo_scaled_font_status()) or there is insufficient memory.
+ **/
+FT_Face
+cairo_ft_scaled_font_lock_face (cairo_scaled_font_t *abstract_font)
+{
+    cairo_ft_scaled_font_t *scaled_font = (cairo_ft_scaled_font_t *) abstract_font;
+    FT_Face face;
+    cairo_status_t status;
+
+    if (! _cairo_scaled_font_is_ft (abstract_font)) {
+       _cairo_error_throw (CAIRO_STATUS_FONT_TYPE_MISMATCH);
+       return NULL;
+    }
+
+    if (scaled_font->base.status)
+       return NULL;
+
+    face = _cairo_ft_unscaled_font_lock_face (scaled_font->unscaled);
+    if (unlikely (face == NULL)) {
+       status = _cairo_scaled_font_set_error (&scaled_font->base, CAIRO_STATUS_NO_MEMORY);
+       return NULL;
+    }
+
+    status = _cairo_ft_unscaled_font_set_scale (scaled_font->unscaled,
+                                               &scaled_font->base.scale);
+    if (unlikely (status)) {
+       _cairo_ft_unscaled_font_unlock_face (scaled_font->unscaled);
+       status = _cairo_scaled_font_set_error (&scaled_font->base, status);
+       return NULL;
+    }
+
+    /* Note: We deliberately release the unscaled font's mutex here,
+     * so that we are not holding a lock across two separate calls to
+     * cairo function, (which would give the application some
+     * opportunity for creating deadlock. This is obviously unsafe,
+     * but as documented, the user must add manual locking when using
+     * this function. */
+     CAIRO_MUTEX_UNLOCK (scaled_font->unscaled->mutex);
+
+    return face;
+}
+
+/**
+ * cairo_ft_scaled_font_unlock_face:
+ * @scaled_font: A #cairo_scaled_font_t from the FreeType font backend. Such an
+ *   object can be created by calling cairo_scaled_font_create() on a
+ *   FreeType backend font face (see cairo_ft_font_face_create_for_pattern(),
+ *   cairo_ft_font_face_create_for_ft_face()).
+ *
+ * Releases a face obtained with cairo_ft_scaled_font_lock_face().
+ **/
+void
+cairo_ft_scaled_font_unlock_face (cairo_scaled_font_t *abstract_font)
+{
+    cairo_ft_scaled_font_t *scaled_font = (cairo_ft_scaled_font_t *) abstract_font;
+
+    if (! _cairo_scaled_font_is_ft (abstract_font)) {
+       _cairo_error_throw (CAIRO_STATUS_FONT_TYPE_MISMATCH);
+       return;
+    }
+
+    if (scaled_font->base.status)
+       return;
+
+    /* Note: We released the unscaled font's mutex at the end of
+     * cairo_ft_scaled_font_lock_face, so we have to acquire it again
+     * as _cairo_ft_unscaled_font_unlock_face expects it to be held
+     * when we call into it. */
+    CAIRO_MUTEX_LOCK (scaled_font->unscaled->mutex);
+
+    _cairo_ft_unscaled_font_unlock_face (scaled_font->unscaled);
+}
+
+static cairo_bool_t
+_cairo_ft_scaled_font_is_vertical (cairo_scaled_font_t *scaled_font)
+{
+    cairo_ft_scaled_font_t *ft_scaled_font;
+
+    if (!_cairo_scaled_font_is_ft (scaled_font))
+       return FALSE;
+
+    ft_scaled_font = (cairo_ft_scaled_font_t *) scaled_font;
+    if (ft_scaled_font->ft_options.load_flags & FT_LOAD_VERTICAL_LAYOUT)
+       return TRUE;
+    return FALSE;
+}
+
+unsigned int
+_cairo_ft_scaled_font_get_load_flags (cairo_scaled_font_t *scaled_font)
+{
+    cairo_ft_scaled_font_t *ft_scaled_font;
+
+    if (! _cairo_scaled_font_is_ft (scaled_font))
+       return 0;
+
+    ft_scaled_font = (cairo_ft_scaled_font_t *) scaled_font;
+    return ft_scaled_font->ft_options.load_flags;
+}
+
+void
+_cairo_ft_font_reset_static_data (void)
+{
+    _cairo_ft_unscaled_font_map_destroy ();
+}
+
+#ifdef TIZEN_ENABLE_SET_EXTRA_FLAGS
+/* Set extra_flags to cairo_ft_font_face_t's ft_options from application */
+void
+cairo_ft_font_option_set_extra_flags (cairo_font_face_t * font_face,
+                               cairo_ft_extra_flags_t extra_flags)
+{
+    cairo_ft_font_face_t *ft_font_face =(cairo_ft_font_face_t *) font_face;
+
+    if (extra_flags & CAIRO_FT_OPTIONS_HINT_METRICS)
+        ft_font_face->ft_options.extra_flags |= CAIRO_FT_OPTIONS_HINT_METRICS;
+    if (extra_flags & CAIRO_FT_OPTIONS_EMBOLDEN)
+        ft_font_face->ft_options.extra_flags |= CAIRO_FT_OPTIONS_EMBOLDEN;
+}
+#endif
+
diff --git a/src/cairo-ft-private.h b/src/cairo-ft-private.h
new file mode 100644 (file)
index 0000000..3921518
--- /dev/null
@@ -0,0 +1,61 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2005 Red Hat, Inc
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ *      Graydon Hoare <graydon@redhat.com>
+ *     Owen Taylor <otaylor@redhat.com>
+ */
+
+#ifndef CAIRO_FT_PRIVATE_H
+#define CAIRO_FT_PRIVATE_H
+
+#include "cairo-ft.h"
+#include "cairoint.h"
+
+#if CAIRO_HAS_FT_FONT
+
+CAIRO_BEGIN_DECLS
+
+typedef struct _cairo_ft_unscaled_font cairo_ft_unscaled_font_t;
+
+cairo_private cairo_bool_t
+_cairo_scaled_font_is_ft (cairo_scaled_font_t *scaled_font);
+
+/* These functions are needed by the PDF backend, which needs to keep track of the
+ * the different fonts-on-disk used by a document, so it can embed them
+ */
+cairo_private unsigned int
+_cairo_ft_scaled_font_get_load_flags (cairo_scaled_font_t *scaled_font);
+
+CAIRO_END_DECLS
+
+#endif /* CAIRO_HAS_FT_FONT */
+#endif /* CAIRO_FT_PRIVATE_H */
diff --git a/src/cairo-ft.h b/src/cairo-ft.h
new file mode 100644 (file)
index 0000000..a0f09e5
--- /dev/null
@@ -0,0 +1,99 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2005 Red Hat, Inc
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ *      Graydon Hoare <graydon@redhat.com>
+ *     Owen Taylor <otaylor@redhat.com>
+ */
+
+#ifndef CAIRO_FT_H
+#define CAIRO_FT_H
+
+#include "cairo.h"
+
+#if CAIRO_HAS_FT_FONT
+
+/* Fontconfig/Freetype platform-specific font interface */
+
+#include <ft2build.h>
+#include FT_FREETYPE_H
+
+#if CAIRO_HAS_FC_FONT
+#include <fontconfig/fontconfig.h>
+#endif
+
+CAIRO_BEGIN_DECLS
+
+cairo_public cairo_font_face_t *
+cairo_ft_font_face_create_for_ft_face (FT_Face         face,
+                                      int             load_flags);
+
+cairo_public FT_Face
+cairo_ft_scaled_font_lock_face (cairo_scaled_font_t *scaled_font);
+
+cairo_public void
+cairo_ft_scaled_font_unlock_face (cairo_scaled_font_t *scaled_font);
+
+#if CAIRO_HAS_FC_FONT
+
+cairo_public cairo_font_face_t *
+cairo_ft_font_face_create_for_pattern (FcPattern *pattern);
+
+cairo_public void
+cairo_ft_font_options_substitute (const cairo_font_options_t *options,
+                                 FcPattern                  *pattern);
+
+#endif
+
+#ifndef TIZEN_ENABLE_SET_EXTRA_FLAGS
+/* For support to set extra_flags from application */
+#define TIZEN_ENABLE_SET_EXTRA_FLAGS
+#endif
+
+#ifdef TIZEN_ENABLE_SET_EXTRA_FLAGS
+/* This enum is moved from cairo-ft-font.c to cairo-ft.h for support to set extra_flags from application */
+typedef enum _cairo_ft_extra_flags {
+    CAIRO_FT_OPTIONS_HINT_METRICS = (1 << 0),
+    CAIRO_FT_OPTIONS_EMBOLDEN = (1 << 1)
+} cairo_ft_extra_flags_t;
+
+/* Set extra_flags to cairo_ft_font_face_t's ft_options from application */
+cairo_public void cairo_ft_font_option_set_extra_flags (cairo_font_face_t * font_face,
+                               cairo_ft_extra_flags_t extra_flags);
+#endif
+
+CAIRO_END_DECLS
+
+#else  /* CAIRO_HAS_FT_FONT */
+# error Cairo was not compiled with support for the freetype font backend
+#endif /* CAIRO_HAS_FT_FONT */
+
+#endif /* CAIRO_FT_H */
diff --git a/src/cairo-gl-composite.c b/src/cairo-gl-composite.c
new file mode 100644 (file)
index 0000000..5bce04d
--- /dev/null
@@ -0,0 +1,769 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Eric Anholt
+ * Copyright © 2009 Chris Wilson
+ * Copyright © 2005,2010 Red Hat, Inc
+ * Copyright © 2011 Linaro Limited
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ *     Benjamin Otte <otte@gnome.org>
+ *     Carl Worth <cworth@cworth.org>
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ *     Eric Anholt <eric@anholt.net>
+ *     Alexandros Frantzis <alexandros.frantzis@linaro.org>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-gl-private.h"
+
+#include "cairo-composite-rectangles-private.h"
+#include "cairo-clip-private.h"
+#include "cairo-error-private.h"
+#include "cairo-image-surface-private.h"
+
+cairo_int_status_t
+_cairo_gl_composite_set_source (cairo_gl_composite_t *setup,
+                               const cairo_pattern_t *pattern,
+                                int src_x, int src_y,
+                                int dst_x, int dst_y,
+                                int width, int height)
+{
+    _cairo_gl_operand_destroy (&setup->src);
+    return _cairo_gl_operand_init (&setup->src, pattern,
+                                   setup->dst,
+                                   src_x, src_y,
+                                   dst_x, dst_y,
+                                   width, height);
+}
+
+void
+_cairo_gl_composite_set_source_operand (cairo_gl_composite_t *setup,
+                                       const cairo_gl_operand_t *source)
+{
+    _cairo_gl_operand_destroy (&setup->src);
+    _cairo_gl_operand_copy (&setup->src, source);
+}
+
+void
+_cairo_gl_composite_set_solid_source (cairo_gl_composite_t *setup,
+                                     const cairo_color_t *color)
+{
+    _cairo_gl_operand_destroy (&setup->src);
+    _cairo_gl_solid_operand_init (&setup->src, color);
+}
+
+cairo_int_status_t
+_cairo_gl_composite_set_mask (cairo_gl_composite_t *setup,
+                             const cairo_pattern_t *pattern,
+                              int src_x, int src_y,
+                              int dst_x, int dst_y,
+                              int width, int height)
+{
+    _cairo_gl_operand_destroy (&setup->mask);
+    if (pattern == NULL)
+        return CAIRO_STATUS_SUCCESS;
+
+    return _cairo_gl_operand_init (&setup->mask, pattern,
+                                   setup->dst,
+                                   src_x, src_y,
+                                   dst_x, dst_y,
+                                   width, height);
+}
+
+void
+_cairo_gl_composite_set_mask_operand (cairo_gl_composite_t *setup,
+                                     const cairo_gl_operand_t *mask)
+{
+    _cairo_gl_operand_destroy (&setup->mask);
+    if (mask)
+       _cairo_gl_operand_copy (&setup->mask, mask);
+}
+
+void
+_cairo_gl_composite_set_spans (cairo_gl_composite_t *setup)
+{
+    setup->spans = TRUE;
+}
+
+void
+_cairo_gl_composite_set_clip_region (cairo_gl_composite_t *setup,
+                                     cairo_region_t *clip_region)
+{
+    setup->clip_region = clip_region;
+}
+
+static void
+_cairo_gl_composite_bind_to_shader (cairo_gl_context_t   *ctx,
+                                   cairo_gl_composite_t *setup)
+{
+    _cairo_gl_shader_bind_matrix4f(ctx, "ModelViewProjectionMatrix",
+                                  ctx->modelviewprojection_matrix);
+    _cairo_gl_operand_bind_to_shader (ctx, &setup->src,  CAIRO_GL_TEX_SOURCE);
+    _cairo_gl_operand_bind_to_shader (ctx, &setup->mask, CAIRO_GL_TEX_MASK);
+}
+
+static void
+_cairo_gl_texture_set_filter (cairo_gl_context_t *ctx,
+                              GLuint              target,
+                              cairo_filter_t      filter)
+{
+    switch (filter) {
+    case CAIRO_FILTER_FAST:
+    case CAIRO_FILTER_NEAREST:
+       glTexParameteri (target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+       glTexParameteri (target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+       break;
+    case CAIRO_FILTER_GOOD:
+    case CAIRO_FILTER_BEST:
+    case CAIRO_FILTER_BILINEAR:
+       glTexParameteri (target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+       glTexParameteri (target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+       break;
+    default:
+    case CAIRO_FILTER_GAUSSIAN:
+       ASSERT_NOT_REACHED;
+    }
+}
+
+static void
+_cairo_gl_texture_set_extend (cairo_gl_context_t *ctx,
+                              GLuint              target,
+                              cairo_extend_t      extend)
+{
+    assert (! _cairo_gl_device_requires_power_of_two_textures (&ctx->base) ||
+            (extend != CAIRO_EXTEND_REPEAT && extend != CAIRO_EXTEND_REFLECT));
+
+    switch (extend) {
+    case CAIRO_EXTEND_NONE:
+       if (ctx->gl_flavor == CAIRO_GL_FLAVOR_ES) {
+           glTexParameteri (target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+           glTexParameteri (target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+       }
+       else {
+           glTexParameteri (target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
+           glTexParameteri (target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
+       }
+       break;
+    case CAIRO_EXTEND_PAD:
+       glTexParameteri (target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+       glTexParameteri (target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+       break;
+    case CAIRO_EXTEND_REPEAT:
+       glTexParameteri (target, GL_TEXTURE_WRAP_S, GL_REPEAT);
+       glTexParameteri (target, GL_TEXTURE_WRAP_T, GL_REPEAT);
+       break;
+    case CAIRO_EXTEND_REFLECT:
+       glTexParameteri (target, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT);
+       glTexParameteri (target, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);
+       break;
+    }
+}
+
+
+static void
+_cairo_gl_context_setup_operand (cairo_gl_context_t *ctx,
+                                 cairo_gl_tex_t      tex_unit,
+                                 cairo_gl_operand_t *operand,
+                                 unsigned int        vertex_size,
+                                 unsigned int        vertex_offset)
+{
+    cairo_gl_dispatch_t *dispatch = &ctx->dispatch;
+    cairo_bool_t needs_setup;
+
+    /* XXX: we need to do setup when switching from shaders
+     * to no shaders (or back) */
+    needs_setup = ctx->vertex_size != vertex_size;
+    needs_setup |= _cairo_gl_operand_needs_setup (&ctx->operands[tex_unit],
+                                                 operand,
+                                                 vertex_offset);
+
+    if (needs_setup) {
+        _cairo_gl_composite_flush (ctx);
+        _cairo_gl_context_destroy_operand (ctx, tex_unit);
+    }
+
+    memcpy (&ctx->operands[tex_unit], operand, sizeof (cairo_gl_operand_t));
+    ctx->operands[tex_unit].vertex_offset = vertex_offset;
+
+    if (! needs_setup)
+        return;
+
+    switch (operand->type) {
+    default:
+    case CAIRO_GL_OPERAND_COUNT:
+        ASSERT_NOT_REACHED;
+    case CAIRO_GL_OPERAND_NONE:
+        break;
+        /* fall through */
+    case CAIRO_GL_OPERAND_CONSTANT:
+        break;
+    case CAIRO_GL_OPERAND_TEXTURE:
+        glActiveTexture (GL_TEXTURE0 + tex_unit);
+        glBindTexture (ctx->tex_target, operand->texture.tex);
+        _cairo_gl_texture_set_extend (ctx, ctx->tex_target,
+                                      operand->texture.attributes.extend);
+        _cairo_gl_texture_set_filter (ctx, ctx->tex_target,
+                                      operand->texture.attributes.filter);
+
+       dispatch->VertexAttribPointer (CAIRO_GL_TEXCOORD0_ATTRIB_INDEX + tex_unit, 2,
+                                       GL_FLOAT, GL_FALSE, vertex_size,
+                                       (void *) (uintptr_t) vertex_offset);
+       dispatch->EnableVertexAttribArray (CAIRO_GL_TEXCOORD0_ATTRIB_INDEX + tex_unit);
+        break;
+    case CAIRO_GL_OPERAND_LINEAR_GRADIENT:
+    case CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0:
+    case CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE:
+    case CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT:
+        glActiveTexture (GL_TEXTURE0 + tex_unit);
+        glBindTexture (ctx->tex_target, operand->gradient.gradient->tex);
+        _cairo_gl_texture_set_extend (ctx, ctx->tex_target, operand->gradient.extend);
+        _cairo_gl_texture_set_filter (ctx, ctx->tex_target, CAIRO_FILTER_BILINEAR);
+
+       dispatch->VertexAttribPointer (CAIRO_GL_TEXCOORD0_ATTRIB_INDEX + tex_unit, 2,
+                                      GL_FLOAT, GL_FALSE, vertex_size,
+                                      (void *) (uintptr_t) vertex_offset);
+       dispatch->EnableVertexAttribArray (CAIRO_GL_TEXCOORD0_ATTRIB_INDEX + tex_unit);
+       break;
+    }
+}
+
+static void
+_cairo_gl_context_setup_spans (cairo_gl_context_t *ctx,
+                              unsigned int        vertex_size,
+                              unsigned int        vertex_offset)
+{
+    cairo_gl_dispatch_t *dispatch = &ctx->dispatch;
+
+    dispatch->VertexAttribPointer (CAIRO_GL_COLOR_ATTRIB_INDEX, 4,
+                                  GL_UNSIGNED_BYTE, GL_TRUE, vertex_size,
+                                  (void *) (uintptr_t) vertex_offset);
+    dispatch->EnableVertexAttribArray (CAIRO_GL_COLOR_ATTRIB_INDEX);
+}
+
+void
+_cairo_gl_context_destroy_operand (cairo_gl_context_t *ctx,
+                                   cairo_gl_tex_t tex_unit)
+{
+    cairo_gl_dispatch_t *dispatch = &ctx->dispatch;
+    assert (_cairo_gl_context_is_flushed (ctx));
+
+    switch (ctx->operands[tex_unit].type) {
+    default:
+    case CAIRO_GL_OPERAND_COUNT:
+        ASSERT_NOT_REACHED;
+    case CAIRO_GL_OPERAND_NONE:
+        break;
+        /* fall through */
+    case CAIRO_GL_OPERAND_CONSTANT:
+        break;
+    case CAIRO_GL_OPERAND_TEXTURE:
+        dispatch->DisableVertexAttribArray (CAIRO_GL_TEXCOORD0_ATTRIB_INDEX + tex_unit);
+        break;
+    case CAIRO_GL_OPERAND_LINEAR_GRADIENT:
+    case CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0:
+    case CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE:
+    case CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT:
+        dispatch->DisableVertexAttribArray (CAIRO_GL_TEXCOORD0_ATTRIB_INDEX + tex_unit);
+        break;
+    }
+
+    memset (&ctx->operands[tex_unit], 0, sizeof (cairo_gl_operand_t));
+}
+
+static void
+_cairo_gl_set_operator (cairo_gl_context_t *ctx,
+                        cairo_operator_t    op,
+                       cairo_bool_t        component_alpha)
+{
+    struct {
+       GLenum src;
+       GLenum dst;
+    } blend_factors[] = {
+       { GL_ZERO, GL_ZERO }, /* Clear */
+       { GL_ONE, GL_ZERO }, /* Source */
+       { GL_ONE, GL_ONE_MINUS_SRC_ALPHA }, /* Over */
+       { GL_DST_ALPHA, GL_ZERO }, /* In */
+       { GL_ONE_MINUS_DST_ALPHA, GL_ZERO }, /* Out */
+       { GL_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA }, /* Atop */
+
+       { GL_ZERO, GL_ONE }, /* Dest */
+       { GL_ONE_MINUS_DST_ALPHA, GL_ONE }, /* DestOver */
+       { GL_ZERO, GL_SRC_ALPHA }, /* DestIn */
+       { GL_ZERO, GL_ONE_MINUS_SRC_ALPHA }, /* DestOut */
+       { GL_ONE_MINUS_DST_ALPHA, GL_SRC_ALPHA }, /* DestAtop */
+
+       { GL_ONE_MINUS_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA }, /* Xor */
+       { GL_ONE, GL_ONE }, /* Add */
+    };
+    GLenum src_factor, dst_factor;
+
+    assert (op < ARRAY_LENGTH (blend_factors));
+    /* different dst and component_alpha changes cause flushes elsewhere */
+    if (ctx->current_operator != op)
+        _cairo_gl_composite_flush (ctx);
+    ctx->current_operator = op;
+
+    src_factor = blend_factors[op].src;
+    dst_factor = blend_factors[op].dst;
+
+    /* Even when the user requests CAIRO_CONTENT_COLOR, we use GL_RGBA
+     * due to texture filtering of GL_CLAMP_TO_BORDER.  So fix those
+     * bits in that case.
+     */
+    if (ctx->current_target->base.content == CAIRO_CONTENT_COLOR) {
+       if (src_factor == GL_ONE_MINUS_DST_ALPHA)
+           src_factor = GL_ZERO;
+       if (src_factor == GL_DST_ALPHA)
+           src_factor = GL_ONE;
+    }
+
+    if (component_alpha) {
+       if (dst_factor == GL_ONE_MINUS_SRC_ALPHA)
+           dst_factor = GL_ONE_MINUS_SRC_COLOR;
+       if (dst_factor == GL_SRC_ALPHA)
+           dst_factor = GL_SRC_COLOR;
+    }
+
+    if (ctx->current_target->base.content == CAIRO_CONTENT_ALPHA) {
+        glBlendFuncSeparate (GL_ZERO, GL_ZERO, src_factor, dst_factor);
+    } else if (ctx->current_target->base.content == CAIRO_CONTENT_COLOR) {
+        glBlendFuncSeparate (src_factor, dst_factor, GL_ONE, GL_ONE);
+    } else {
+        glBlendFunc (src_factor, dst_factor);
+    }
+}
+
+static cairo_status_t
+_cairo_gl_composite_begin_component_alpha  (cairo_gl_context_t *ctx,
+                                            cairo_gl_composite_t *setup)
+{
+    cairo_gl_shader_t *pre_shader = NULL;
+    cairo_status_t status;
+
+    /* For CLEAR, cairo's rendering equation (quoting Owen's description in:
+     * http://lists.cairographics.org/archives/cairo/2005-August/004992.html)
+     * is:
+     *     mask IN clip ? src OP dest : dest
+     * or more simply:
+     *     mask IN CLIP ? 0 : dest
+     *
+     * where the ternary operator A ? B : C is (A * B) + ((1 - A) * C).
+     *
+     * The model we use in _cairo_gl_set_operator() is Render's:
+     *     src IN mask IN clip OP dest
+     * which would boil down to:
+     *     0 (bounded by the extents of the drawing).
+     *
+     * However, we can do a Render operation using an opaque source
+     * and DEST_OUT to produce:
+     *    1 IN mask IN clip DEST_OUT dest
+     * which is
+     *    mask IN clip ? 0 : dest
+     */
+    if (setup->op == CAIRO_OPERATOR_CLEAR) {
+        _cairo_gl_solid_operand_init (&setup->src, CAIRO_COLOR_WHITE);
+       setup->op = CAIRO_OPERATOR_DEST_OUT;
+    }
+
+    /*
+     * implements component-alpha %CAIRO_OPERATOR_OVER using two passes of
+     * the simpler operations %CAIRO_OPERATOR_DEST_OUT and %CAIRO_OPERATOR_ADD.
+     *
+     * From http://anholt.livejournal.com/32058.html:
+     *
+     * The trouble is that component-alpha rendering requires two different sources
+     * for blending: one for the source value to the blender, which is the
+     * per-channel multiplication of source and mask, and one for the source alpha
+     * for multiplying with the destination channels, which is the multiplication
+     * of the source channels by the mask alpha. So the equation for Over is:
+     *
+     * dst.A = src.A * mask.A + (1 - (src.A * mask.A)) * dst.A
+     * dst.R = src.R * mask.R + (1 - (src.A * mask.R)) * dst.R
+     * dst.G = src.G * mask.G + (1 - (src.A * mask.G)) * dst.G
+     * dst.B = src.B * mask.B + (1 - (src.A * mask.B)) * dst.B
+     *
+     * But we can do some simpler operations, right? How about PictOpOutReverse,
+     * which has a source factor of 0 and dest factor of (1 - source alpha). We
+     * can get the source alpha value (srca.X = src.A * mask.X) out of the texture
+     * blenders pretty easily. So we can do a component-alpha OutReverse, which
+     * gets us:
+     *
+     * dst.A = 0 + (1 - (src.A * mask.A)) * dst.A
+     * dst.R = 0 + (1 - (src.A * mask.R)) * dst.R
+     * dst.G = 0 + (1 - (src.A * mask.G)) * dst.G
+     * dst.B = 0 + (1 - (src.A * mask.B)) * dst.B
+     *
+     * OK. And if an op doesn't use the source alpha value for the destination
+     * factor, then we can do the channel multiplication in the texture blenders
+     * to get the source value, and ignore the source alpha that we wouldn't use.
+     * We've supported this in the Radeon driver for a long time. An example would
+     * be PictOpAdd, which does:
+     *
+     * dst.A = src.A * mask.A + dst.A
+     * dst.R = src.R * mask.R + dst.R
+     * dst.G = src.G * mask.G + dst.G
+     * dst.B = src.B * mask.B + dst.B
+     *
+     * Hey, this looks good! If we do a PictOpOutReverse and then a PictOpAdd right
+     * after it, we get:
+     *
+     * dst.A = src.A * mask.A + ((1 - (src.A * mask.A)) * dst.A)
+     * dst.R = src.R * mask.R + ((1 - (src.A * mask.R)) * dst.R)
+     * dst.G = src.G * mask.G + ((1 - (src.A * mask.G)) * dst.G)
+     * dst.B = src.B * mask.B + ((1 - (src.A * mask.B)) * dst.B)
+     *
+     * This two-pass trickery could be avoided using a new GL extension that
+     * lets two values come out of the shader and into the blend unit.
+     */
+    if (setup->op == CAIRO_OPERATOR_OVER) {
+       setup->op = CAIRO_OPERATOR_ADD;
+       status = _cairo_gl_get_shader_by_type (ctx,
+                                               &setup->src,
+                                               &setup->mask,
+                                              setup->spans,
+                                               CAIRO_GL_SHADER_IN_CA_SOURCE_ALPHA,
+                                               &pre_shader);
+        if (unlikely (status))
+            return status;
+    }
+
+    if (ctx->pre_shader != pre_shader)
+        _cairo_gl_composite_flush (ctx);
+    ctx->pre_shader = pre_shader;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_status_t
+_cairo_gl_composite_begin (cairo_gl_composite_t *setup,
+                           cairo_gl_context_t **ctx_out)
+{
+    unsigned int dst_size, src_size, mask_size, vertex_size;
+    cairo_gl_context_t *ctx;
+    cairo_status_t status;
+    cairo_bool_t component_alpha;
+    cairo_gl_shader_t *shader;
+
+    assert (setup->dst);
+
+    status = _cairo_gl_context_acquire (setup->dst->base.device, &ctx);
+    if (unlikely (status))
+       return status;
+
+    glEnable (GL_BLEND);
+
+    component_alpha = ((setup->mask.type == CAIRO_GL_OPERAND_TEXTURE) &&
+                       setup->mask.texture.attributes.has_component_alpha);
+
+    /* Do various magic for component alpha */
+    if (component_alpha) {
+        status = _cairo_gl_composite_begin_component_alpha (ctx, setup);
+        if (unlikely (status))
+            goto FAIL;
+    } else {
+        if (ctx->pre_shader) {
+            _cairo_gl_composite_flush (ctx);
+            ctx->pre_shader = NULL;
+        }
+    }
+
+    status = _cairo_gl_get_shader_by_type (ctx,
+                                          &setup->src,
+                                          &setup->mask,
+                                          setup->spans,
+                                           component_alpha ?
+                                          CAIRO_GL_SHADER_IN_CA_SOURCE :
+                                          CAIRO_GL_SHADER_IN_NORMAL,
+                                           &shader);
+    if (unlikely (status)) {
+        ctx->pre_shader = NULL;
+        goto FAIL;
+    }
+    if (ctx->current_shader != shader)
+        _cairo_gl_composite_flush (ctx);
+
+    status = CAIRO_STATUS_SUCCESS;
+
+    dst_size  = 2 * sizeof (GLfloat);
+    src_size  = _cairo_gl_operand_get_vertex_size (setup->src.type);
+    mask_size = _cairo_gl_operand_get_vertex_size (setup->mask.type);
+
+    vertex_size = dst_size + src_size + mask_size;
+    if (ctx->vertex_size != vertex_size) {
+        _cairo_gl_composite_flush (ctx);
+    }
+
+    _cairo_gl_context_set_destination (ctx, setup->dst);
+
+    if (_cairo_gl_context_is_flushed (ctx)) {
+        ctx->dispatch.BindBuffer (GL_ARRAY_BUFFER, ctx->vbo);
+
+       ctx->dispatch.VertexAttribPointer (CAIRO_GL_VERTEX_ATTRIB_INDEX, 2,
+                                          GL_FLOAT, GL_FALSE, vertex_size, NULL);
+       ctx->dispatch.EnableVertexAttribArray (CAIRO_GL_VERTEX_ATTRIB_INDEX);
+    }
+
+    _cairo_gl_context_setup_operand (ctx, CAIRO_GL_TEX_SOURCE, &setup->src, vertex_size, dst_size);
+    _cairo_gl_context_setup_operand (ctx, CAIRO_GL_TEX_MASK, &setup->mask, vertex_size, dst_size + src_size);
+    if (setup->spans)
+       _cairo_gl_context_setup_spans (ctx, vertex_size, dst_size + src_size + mask_size);
+    else
+       ctx->dispatch.DisableVertexAttribArray (CAIRO_GL_COLOR_ATTRIB_INDEX);
+
+    _cairo_gl_set_operator (ctx, setup->op, component_alpha);
+
+    ctx->vertex_size = vertex_size;
+
+    if (_cairo_gl_context_is_flushed (ctx)) {
+        if (ctx->pre_shader) {
+            _cairo_gl_set_shader (ctx, ctx->pre_shader);
+            _cairo_gl_composite_bind_to_shader (ctx, setup);
+        }
+        _cairo_gl_set_shader (ctx, shader);
+        _cairo_gl_composite_bind_to_shader (ctx, setup);
+    }
+
+    if (! _cairo_gl_context_is_flushed (ctx) &&
+        ! cairo_region_equal (ctx->clip_region, setup->clip_region))
+        _cairo_gl_composite_flush (ctx);
+    cairo_region_destroy (ctx->clip_region);
+    ctx->clip_region = cairo_region_reference (setup->clip_region);
+    if (ctx->clip_region)
+       glEnable (GL_SCISSOR_TEST);
+    else
+       glDisable (GL_SCISSOR_TEST);
+
+    *ctx_out = ctx;
+
+FAIL:
+    if (unlikely (status))
+        status = _cairo_gl_context_release (ctx, status);
+
+    return status;
+}
+
+static inline void
+_cairo_gl_composite_draw (cairo_gl_context_t *ctx,
+                         unsigned int count)
+{
+    if (! ctx->pre_shader) {
+        glDrawArrays (GL_TRIANGLES, 0, count);
+    } else {
+        cairo_gl_shader_t *prev_shader = ctx->current_shader;
+
+        _cairo_gl_set_shader (ctx, ctx->pre_shader);
+        _cairo_gl_set_operator (ctx, CAIRO_OPERATOR_DEST_OUT, TRUE);
+        glDrawArrays (GL_TRIANGLES, 0, count);
+
+        _cairo_gl_set_shader (ctx, prev_shader);
+        _cairo_gl_set_operator (ctx, CAIRO_OPERATOR_ADD, TRUE);
+        glDrawArrays (GL_TRIANGLES, 0, count);
+    }
+}
+
+void
+_cairo_gl_composite_flush (cairo_gl_context_t *ctx)
+{
+    unsigned int count;
+
+    if (_cairo_gl_context_is_flushed (ctx))
+        return;
+
+    count = ctx->vb_offset / ctx->vertex_size;
+
+    if (ctx->has_map_buffer)
+       ctx->dispatch.UnmapBuffer (GL_ARRAY_BUFFER);
+    else
+       ctx->dispatch.BufferData (GL_ARRAY_BUFFER, ctx->vb_offset,
+                                 ctx->vb, GL_DYNAMIC_DRAW);
+
+    ctx->vb = NULL;
+    ctx->vb_offset = 0;
+
+    if (ctx->clip_region) {
+       int i, num_rectangles = cairo_region_num_rectangles (ctx->clip_region);
+
+       for (i = 0; i < num_rectangles; i++) {
+           cairo_rectangle_int_t rect;
+
+           cairo_region_get_rectangle (ctx->clip_region, i, &rect);
+
+           glScissor (rect.x, rect.y, rect.width, rect.height);
+            _cairo_gl_composite_draw (ctx, count);
+       }
+    } else {
+        _cairo_gl_composite_draw (ctx, count);
+    }
+}
+
+static void
+_cairo_gl_composite_prepare_buffer (cairo_gl_context_t *ctx,
+                                    unsigned int n_vertices)
+{
+    cairo_gl_dispatch_t *dispatch = &ctx->dispatch;
+
+    if (ctx->vb_offset + n_vertices * ctx->vertex_size > CAIRO_GL_VBO_SIZE)
+       _cairo_gl_composite_flush (ctx);
+
+    if (ctx->vb == NULL) {
+       if (ctx->has_map_buffer) {
+           dispatch->BufferData (GL_ARRAY_BUFFER, CAIRO_GL_VBO_SIZE,
+                                 NULL, GL_DYNAMIC_DRAW);
+           ctx->vb = dispatch->MapBuffer (GL_ARRAY_BUFFER, GL_WRITE_ONLY);
+       }
+       else {
+           ctx->vb = ctx->vb_mem;
+       }
+    }
+}
+
+static inline void
+_cairo_gl_composite_emit_vertex (cairo_gl_context_t *ctx,
+                                 GLfloat x,
+                                 GLfloat y,
+                                 uint8_t alpha)
+{
+    GLfloat *vb = (GLfloat *) (void *) &ctx->vb[ctx->vb_offset];
+
+    *vb++ = x;
+    *vb++ = y;
+
+    _cairo_gl_operand_emit (&ctx->operands[CAIRO_GL_TEX_SOURCE], &vb, x, y);
+    _cairo_gl_operand_emit (&ctx->operands[CAIRO_GL_TEX_MASK  ], &vb, x, y);
+
+    if (ctx->spans) {
+       union fi {
+           float f;
+           GLbyte bytes[4];
+       } fi;
+
+       fi.bytes[0] = 0;
+       fi.bytes[1] = 0;
+       fi.bytes[2] = 0;
+       fi.bytes[3] = alpha;
+       *vb++ = fi.f;
+    }
+
+    ctx->vb_offset += ctx->vertex_size;
+}
+
+void
+_cairo_gl_composite_emit_rect (cairo_gl_context_t *ctx,
+                               GLfloat x1,
+                               GLfloat y1,
+                               GLfloat x2,
+                               GLfloat y2,
+                               uint8_t alpha)
+{
+    _cairo_gl_composite_prepare_buffer (ctx, 6);
+
+    _cairo_gl_composite_emit_vertex (ctx, x1, y1, alpha);
+    _cairo_gl_composite_emit_vertex (ctx, x2, y1, alpha);
+    _cairo_gl_composite_emit_vertex (ctx, x1, y2, alpha);
+
+    _cairo_gl_composite_emit_vertex (ctx, x2, y1, alpha);
+    _cairo_gl_composite_emit_vertex (ctx, x2, y2, alpha);
+    _cairo_gl_composite_emit_vertex (ctx, x1, y2, alpha);
+}
+
+static inline void
+_cairo_gl_composite_emit_glyph_vertex (cairo_gl_context_t *ctx,
+                                       GLfloat x,
+                                       GLfloat y,
+                                       GLfloat glyph_x,
+                                       GLfloat glyph_y)
+{
+    GLfloat *vb = (GLfloat *) (void *) &ctx->vb[ctx->vb_offset];
+
+    *vb++ = x;
+    *vb++ = y;
+
+    _cairo_gl_operand_emit (&ctx->operands[CAIRO_GL_TEX_SOURCE], &vb, x, y);
+
+    *vb++ = glyph_x;
+    *vb++ = glyph_y;
+
+    ctx->vb_offset += ctx->vertex_size;
+}
+
+void
+_cairo_gl_composite_emit_glyph (cairo_gl_context_t *ctx,
+                                GLfloat x1,
+                                GLfloat y1,
+                                GLfloat x2,
+                                GLfloat y2,
+                                GLfloat glyph_x1,
+                                GLfloat glyph_y1,
+                                GLfloat glyph_x2,
+                                GLfloat glyph_y2)
+{
+    _cairo_gl_composite_prepare_buffer (ctx, 6);
+
+    _cairo_gl_composite_emit_glyph_vertex (ctx, x1, y1, glyph_x1, glyph_y1);
+    _cairo_gl_composite_emit_glyph_vertex (ctx, x2, y1, glyph_x2, glyph_y1);
+    _cairo_gl_composite_emit_glyph_vertex (ctx, x1, y2, glyph_x1, glyph_y2);
+
+    _cairo_gl_composite_emit_glyph_vertex (ctx, x2, y1, glyph_x2, glyph_y1);
+    _cairo_gl_composite_emit_glyph_vertex (ctx, x2, y2, glyph_x2, glyph_y2);
+    _cairo_gl_composite_emit_glyph_vertex (ctx, x1, y2, glyph_x1, glyph_y2);
+}
+
+void
+_cairo_gl_composite_fini (cairo_gl_composite_t *setup)
+{
+    _cairo_gl_operand_destroy (&setup->src);
+    _cairo_gl_operand_destroy (&setup->mask);
+}
+
+cairo_status_t
+_cairo_gl_composite_init (cairo_gl_composite_t *setup,
+                          cairo_operator_t op,
+                          cairo_gl_surface_t *dst,
+                          cairo_bool_t assume_component_alpha,
+                          const cairo_rectangle_int_t *rect)
+{
+    memset (setup, 0, sizeof (cairo_gl_composite_t));
+
+    if (assume_component_alpha) {
+        if (op != CAIRO_OPERATOR_CLEAR &&
+            op != CAIRO_OPERATOR_OVER &&
+            op != CAIRO_OPERATOR_ADD)
+            return UNSUPPORTED ("unsupported component alpha operator");
+    } else {
+        if (! _cairo_gl_operator_is_supported (op))
+            return UNSUPPORTED ("unsupported operator");
+    }
+
+    setup->dst = dst;
+    setup->op = op;
+
+    return CAIRO_STATUS_SUCCESS;
+}
diff --git a/src/cairo-gl-device.c b/src/cairo-gl-device.c
new file mode 100644 (file)
index 0000000..f20850e
--- /dev/null
@@ -0,0 +1,379 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Eric Anholt
+ * Copyright © 2009 Chris Wilson
+ * Copyright © 2005,2010 Red Hat, Inc
+ * Copyright © 2010 Linaro Limited
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ *     Benjamin Otte <otte@gnome.org>
+ *     Carl Worth <cworth@cworth.org>
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ *     Eric Anholt <eric@anholt.net>
+ *     Alexandros Frantzis <alexandros.frantzis@linaro.org>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-error-private.h"
+#include "cairo-gl-private.h"
+
+static void
+_gl_lock (void *device)
+{
+    cairo_gl_context_t *ctx = (cairo_gl_context_t *) device;
+
+    ctx->acquire (ctx);
+}
+
+static void
+_gl_unlock (void *device)
+{
+    cairo_gl_context_t *ctx = (cairo_gl_context_t *) device;
+
+    ctx->release (ctx);
+}
+
+static cairo_status_t
+_gl_flush (void *device)
+{
+    cairo_gl_context_t *ctx;
+    cairo_status_t status;
+
+    status = _cairo_gl_context_acquire (device, &ctx);
+    if (unlikely (status))
+        return status;
+
+    _cairo_gl_composite_flush (ctx);
+
+    _cairo_gl_context_destroy_operand (ctx, CAIRO_GL_TEX_SOURCE);
+    _cairo_gl_context_destroy_operand (ctx, CAIRO_GL_TEX_MASK);
+
+    if (ctx->clip_region) {
+        cairo_region_destroy (ctx->clip_region);
+        ctx->clip_region = NULL;
+    }
+
+    ctx->current_target = NULL;
+    ctx->current_operator = -1;
+    ctx->vertex_size = 0;
+    ctx->pre_shader = NULL;
+    _cairo_gl_set_shader (ctx, NULL);
+
+    ctx->dispatch.BindBuffer (GL_ARRAY_BUFFER, 0);
+
+    glDisable (GL_SCISSOR_TEST);
+    glDisable (GL_BLEND);
+
+    return _cairo_gl_context_release (ctx, status);
+}
+
+static void
+_gl_finish (void *device)
+{
+    cairo_gl_context_t *ctx = device;
+
+    _gl_lock (device);
+
+    _cairo_cache_fini (&ctx->gradients);
+
+    _cairo_gl_context_fini_shaders (ctx);
+
+    _gl_unlock (device);
+}
+
+static void
+_gl_destroy (void *device)
+{
+    cairo_gl_context_t *ctx = device;
+    int n;
+
+    ctx->acquire (ctx);
+
+    while (! cairo_list_is_empty (&ctx->fonts)) {
+       cairo_gl_font_t *font;
+
+       font = cairo_list_first_entry (&ctx->fonts,
+                                      cairo_gl_font_t,
+                                      link);
+
+       cairo_list_del (&font->base.link);
+       cairo_list_del (&font->link);
+       free (font);
+    }
+
+    for (n = 0; n < ARRAY_LENGTH (ctx->glyph_cache); n++)
+       _cairo_gl_glyph_cache_fini (ctx, &ctx->glyph_cache[n]);
+
+    cairo_region_destroy (ctx->clip_region);
+
+    free (ctx->vb_mem);
+
+    ctx->destroy (ctx);
+
+    free (ctx);
+}
+
+static const cairo_device_backend_t _cairo_gl_device_backend = {
+    CAIRO_DEVICE_TYPE_GL,
+
+    _gl_lock,
+    _gl_unlock,
+
+    _gl_flush, /* flush */
+    _gl_finish,
+    _gl_destroy,
+};
+
+cairo_status_t
+_cairo_gl_context_init (cairo_gl_context_t *ctx)
+{
+    cairo_status_t status;
+    cairo_gl_dispatch_t *dispatch = &ctx->dispatch;
+    int gl_version = _cairo_gl_get_version ();
+    cairo_gl_flavor_t gl_flavor = _cairo_gl_get_flavor ();
+    int n;
+
+    _cairo_device_init (&ctx->base, &_cairo_gl_device_backend);
+
+    ctx->compositor = _cairo_gl_span_compositor_get ();
+
+    memset (ctx->glyph_cache, 0, sizeof (ctx->glyph_cache));
+    cairo_list_init (&ctx->fonts);
+
+    /* Support only GL version >= 1.3 */
+    if (gl_version < CAIRO_GL_VERSION_ENCODE (1, 3))
+       return _cairo_error (CAIRO_STATUS_DEVICE_ERROR);
+
+    /* Check for required extensions */
+    if (gl_flavor == CAIRO_GL_FLAVOR_DESKTOP) {
+       if (_cairo_gl_has_extension ("GL_ARB_texture_non_power_of_two"))
+           ctx->tex_target = GL_TEXTURE_2D;
+       else if (_cairo_gl_has_extension ("GL_ARB_texture_rectangle"))
+           ctx->tex_target = GL_TEXTURE_RECTANGLE;
+       else
+           return _cairo_error (CAIRO_STATUS_DEVICE_ERROR);
+    }
+    else {
+       if (_cairo_gl_has_extension ("GL_OES_texture_npot"))
+           ctx->tex_target = GL_TEXTURE_2D;
+       else
+           return _cairo_error (CAIRO_STATUS_DEVICE_ERROR);
+    }
+
+    if (gl_flavor == CAIRO_GL_FLAVOR_DESKTOP &&
+       gl_version < CAIRO_GL_VERSION_ENCODE (2, 1) &&
+       ! _cairo_gl_has_extension ("GL_ARB_pixel_buffer_object"))
+       return _cairo_error (CAIRO_STATUS_DEVICE_ERROR);
+
+    if (gl_flavor == CAIRO_GL_FLAVOR_ES &&
+       ! _cairo_gl_has_extension ("GL_EXT_texture_format_BGRA8888"))
+       return _cairo_error (CAIRO_STATUS_DEVICE_ERROR);
+
+    ctx->has_map_buffer = (gl_flavor == CAIRO_GL_FLAVOR_DESKTOP ||
+                          (gl_flavor == CAIRO_GL_FLAVOR_ES &&
+                           _cairo_gl_has_extension ("GL_OES_mapbuffer")));
+
+    ctx->has_mesa_pack_invert =
+       _cairo_gl_has_extension ("GL_MESA_pack_invert");
+
+    ctx->current_operator = -1;
+    ctx->gl_flavor = gl_flavor;
+
+    status = _cairo_gl_context_init_shaders (ctx);
+    if (unlikely (status))
+        return status;
+
+    status = _cairo_cache_init (&ctx->gradients,
+                                _cairo_gl_gradient_equal,
+                                NULL,
+                                (cairo_destroy_func_t) _cairo_gl_gradient_destroy,
+                                CAIRO_GL_GRADIENT_CACHE_SIZE);
+    if (unlikely (status))
+        return status;
+
+    if (! ctx->has_map_buffer) {
+       ctx->vb_mem = _cairo_malloc_ab (CAIRO_GL_VBO_SIZE, 1);
+       if (unlikely (ctx->vb_mem == NULL)) {
+           _cairo_cache_fini (&ctx->gradients);
+           return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+       }
+    }
+
+    /* PBO for any sort of texture upload */
+    dispatch->GenBuffers (1, &ctx->texture_load_pbo);
+    dispatch->GenBuffers (1, &ctx->vbo);
+
+    ctx->max_framebuffer_size = 0;
+    glGetIntegerv (GL_MAX_RENDERBUFFER_SIZE, &ctx->max_framebuffer_size);
+    ctx->max_texture_size = 0;
+    glGetIntegerv (GL_MAX_TEXTURE_SIZE, &ctx->max_texture_size);
+    ctx->max_textures = 0;
+    glGetIntegerv (GL_MAX_TEXTURE_IMAGE_UNITS, &ctx->max_textures);
+
+    for (n = 0; n < ARRAY_LENGTH (ctx->glyph_cache); n++)
+       _cairo_gl_glyph_cache_init (&ctx->glyph_cache[n]);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+void
+_cairo_gl_context_activate (cairo_gl_context_t *ctx,
+                            cairo_gl_tex_t      tex_unit)
+{
+    if (ctx->max_textures <= (GLint) tex_unit) {
+        if (tex_unit < 2) {
+            _cairo_gl_composite_flush (ctx);
+            _cairo_gl_context_destroy_operand (ctx, ctx->max_textures - 1);   
+        }
+        glActiveTexture (ctx->max_textures - 1);
+    } else {
+        glActiveTexture (GL_TEXTURE0 + tex_unit);
+    }
+}
+
+static void
+_cairo_gl_ensure_framebuffer (cairo_gl_context_t *ctx,
+                              cairo_gl_surface_t *surface)
+{
+    GLenum status;
+    cairo_gl_dispatch_t *dispatch = &ctx->dispatch;
+
+    if (likely (surface->fb))
+        return;
+
+    /* Create a framebuffer object wrapping the texture so that we can render
+     * to it.
+     */
+    dispatch->GenFramebuffers (1, &surface->fb);
+    dispatch->BindFramebuffer (GL_FRAMEBUFFER, surface->fb);
+    dispatch->FramebufferTexture2D (GL_FRAMEBUFFER,
+                                   GL_COLOR_ATTACHMENT0,
+                                   ctx->tex_target,
+                                   surface->tex,
+                                   0);
+#if CAIRO_HAS_GL_SURFACE
+    glDrawBuffer (GL_COLOR_ATTACHMENT0);
+    glReadBuffer (GL_COLOR_ATTACHMENT0);
+#endif
+
+    status = dispatch->CheckFramebufferStatus (GL_FRAMEBUFFER);
+    if (status != GL_FRAMEBUFFER_COMPLETE) {
+       const char *str;
+       switch (status) {
+       //case GL_FRAMEBUFFER_UNDEFINED: str= "undefined"; break;
+       case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: str= "incomplete attachment"; break;
+       case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: str= "incomplete/missing attachment"; break;
+       case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER: str= "incomplete draw buffer"; break;
+       case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER: str= "incomplete read buffer"; break;
+       case GL_FRAMEBUFFER_UNSUPPORTED: str= "unsupported"; break;
+       case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE: str= "incomplete multiple"; break;
+       default: str = "unknown error"; break;
+       }
+
+       fprintf (stderr,
+                "destination is framebuffer incomplete: %s [%#x]\n",
+                str, status);
+    }
+}
+
+/*
+ * Stores a parallel projection transformation in matrix 'm',
+ * using column-major order.
+ *
+ * This is equivalent to:
+ *
+ * glLoadIdentity()
+ * gluOrtho2D()
+ *
+ * The calculation for the ortho tranformation was taken from the
+ * mesa source code.
+ */
+static void
+_gl_identity_ortho (GLfloat *m,
+                   GLfloat left, GLfloat right,
+                   GLfloat bottom, GLfloat top)
+{
+#define M(row,col)  m[col*4+row]
+    M(0,0) = 2.f / (right - left);
+    M(0,1) = 0.f;
+    M(0,2) = 0.f;
+    M(0,3) = -(right + left) / (right - left);
+
+    M(1,0) = 0.f;
+    M(1,1) = 2.f / (top - bottom);
+    M(1,2) = 0.f;
+    M(1,3) = -(top + bottom) / (top - bottom);
+
+    M(2,0) = 0.f;
+    M(2,1) = 0.f;
+    M(2,2) = -1.f;
+    M(2,3) = 0.f;
+
+    M(3,0) = 0.f;
+    M(3,1) = 0.f;
+    M(3,2) = 0.f;
+    M(3,3) = 1.f;
+#undef M
+}
+
+void
+_cairo_gl_context_set_destination (cairo_gl_context_t *ctx,
+                                   cairo_gl_surface_t *surface)
+{
+    if (ctx->current_target == surface && ! surface->needs_update)
+        return;
+
+    _cairo_gl_composite_flush (ctx);
+
+    ctx->current_target = surface;
+    surface->needs_update = FALSE;
+
+    if (_cairo_gl_surface_is_texture (surface)) {
+        _cairo_gl_ensure_framebuffer (ctx, surface);
+        ctx->dispatch.BindFramebuffer (GL_FRAMEBUFFER, surface->fb);
+    } else {
+        ctx->make_current (ctx, surface);
+        ctx->dispatch.BindFramebuffer (GL_FRAMEBUFFER, 0);
+#if CAIRO_HAS_GL_SURFACE
+        glDrawBuffer (GL_BACK_LEFT);
+        glReadBuffer (GL_BACK_LEFT);
+#endif
+    }
+
+    glViewport (0, 0, surface->width, surface->height);
+
+    if (_cairo_gl_surface_is_texture (surface))
+       _gl_identity_ortho (ctx->modelviewprojection_matrix,
+                           0, surface->width, 0, surface->height);
+    else
+       _gl_identity_ortho (ctx->modelviewprojection_matrix,
+                           0, surface->width, surface->height, 0);
+}
diff --git a/src/cairo-gl-dispatch-private.h b/src/cairo-gl-dispatch-private.h
new file mode 100644 (file)
index 0000000..751913c
--- /dev/null
@@ -0,0 +1,115 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2010 Linaro Limited
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * Contributor(s):
+ *      Alexandros Frantzis <alexandros.frantzis@linaro.org>
+ */
+
+#ifndef CAIRO_GL_DISPATCH_PRIVATE_H
+#define CAIRO_GL_DISPATCH_PRIVATE_H
+
+#include "cairo-gl-private.h"
+#include <stddef.h>
+
+typedef enum _cairo_gl_dispatch_name {
+    CAIRO_GL_DISPATCH_NAME_CORE,
+    CAIRO_GL_DISPATCH_NAME_EXT,
+    CAIRO_GL_DISPATCH_NAME_ES,
+    CAIRO_GL_DISPATCH_NAME_COUNT
+} cairo_gl_dispatch_name_t;
+
+typedef struct _cairo_gl_dispatch_entry {
+    const char *name[CAIRO_GL_DISPATCH_NAME_COUNT];
+    size_t offset;
+} cairo_gl_dispatch_entry_t;
+
+#define DISPATCH_ENTRY_ARB(name) { { "gl"#name, "gl"#name"ARB", "gl"#name }, \
+                                  offsetof(cairo_gl_dispatch_t, name) }
+#define DISPATCH_ENTRY_EXT(name) { { "gl"#name, "gl"#name"EXT", "gl"#name }, \
+                                  offsetof(cairo_gl_dispatch_t, name) }
+#define DISPATCH_ENTRY_ARB_OES(name) { { "gl"#name, "gl"#name"ARB", "gl"#name"OES" }, \
+                                      offsetof(cairo_gl_dispatch_t, name) }
+#define DISPATCH_ENTRY_CUSTOM(name, name2) { { "gl"#name, "gl"#name2, "gl"#name }, \
+                                            offsetof(cairo_gl_dispatch_t, name)}
+#define DISPATCH_ENTRY_LAST { { NULL, NULL, NULL }, 0 }
+
+cairo_private cairo_gl_dispatch_entry_t dispatch_buffers_entries[] = {
+    DISPATCH_ENTRY_ARB     (GenBuffers),
+    DISPATCH_ENTRY_ARB     (BindBuffer),
+    DISPATCH_ENTRY_ARB     (BufferData),
+    DISPATCH_ENTRY_ARB_OES (MapBuffer),
+    DISPATCH_ENTRY_ARB_OES (UnmapBuffer),
+    DISPATCH_ENTRY_LAST
+};
+
+cairo_private cairo_gl_dispatch_entry_t dispatch_shaders_entries[] = {
+    /* Shaders */
+    DISPATCH_ENTRY_CUSTOM (CreateShader, CreateShaderObjectARB),
+    DISPATCH_ENTRY_ARB    (ShaderSource),
+    DISPATCH_ENTRY_ARB    (CompileShader),
+    DISPATCH_ENTRY_CUSTOM (GetShaderiv, GetObjectParameterivARB),
+    DISPATCH_ENTRY_CUSTOM (GetShaderInfoLog, GetInfoLogARB),
+    DISPATCH_ENTRY_CUSTOM (DeleteShader, DeleteObjectARB),
+
+    /* Programs */
+    DISPATCH_ENTRY_CUSTOM (CreateProgram, CreateProgramObjectARB),
+    DISPATCH_ENTRY_CUSTOM (AttachShader, AttachObjectARB),
+    DISPATCH_ENTRY_CUSTOM (DeleteProgram, DeleteObjectARB),
+    DISPATCH_ENTRY_ARB    (LinkProgram),
+    DISPATCH_ENTRY_CUSTOM (UseProgram, UseProgramObjectARB),
+    DISPATCH_ENTRY_CUSTOM (GetProgramiv, GetObjectParameterivARB),
+    DISPATCH_ENTRY_CUSTOM (GetProgramInfoLog, GetInfoLogARB),
+
+    /* Uniforms */
+    DISPATCH_ENTRY_ARB (GetUniformLocation),
+    DISPATCH_ENTRY_ARB (Uniform1f),
+    DISPATCH_ENTRY_ARB (Uniform2f),
+    DISPATCH_ENTRY_ARB (Uniform3f),
+    DISPATCH_ENTRY_ARB (Uniform4f),
+    DISPATCH_ENTRY_ARB (UniformMatrix3fv),
+    DISPATCH_ENTRY_ARB (UniformMatrix4fv),
+    DISPATCH_ENTRY_ARB (Uniform1i),
+
+    /* Attributes */
+    DISPATCH_ENTRY_ARB (BindAttribLocation),
+    DISPATCH_ENTRY_ARB (VertexAttribPointer),
+    DISPATCH_ENTRY_ARB (EnableVertexAttribArray),
+    DISPATCH_ENTRY_ARB (DisableVertexAttribArray),
+
+    DISPATCH_ENTRY_LAST
+};
+
+cairo_private cairo_gl_dispatch_entry_t dispatch_fbo_entries[] = {
+    DISPATCH_ENTRY_EXT (GenFramebuffers),
+    DISPATCH_ENTRY_EXT (BindFramebuffer),
+    DISPATCH_ENTRY_EXT (FramebufferTexture2D),
+    DISPATCH_ENTRY_EXT (CheckFramebufferStatus),
+    DISPATCH_ENTRY_EXT (DeleteFramebuffers),
+    DISPATCH_ENTRY_LAST
+};
+
+#endif /* CAIRO_GL_DISPATCH_PRIVATE_H */
diff --git a/src/cairo-gl-dispatch.c b/src/cairo-gl-dispatch.c
new file mode 100644 (file)
index 0000000..5bffddd
--- /dev/null
@@ -0,0 +1,235 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2010 Linaro Limited
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * Contributor(s):
+ *      Alexandros Frantzis <alexandros.frantzis@linaro.org>
+ */
+
+#include "cairoint.h"
+#include "cairo-gl-private.h"
+#include "cairo-gl-dispatch-private.h"
+#if CAIRO_HAS_DLSYM
+#include <dlfcn.h>
+#endif
+
+#if CAIRO_HAS_DLSYM
+static void *
+_cairo_gl_dispatch_open_lib (void)
+{
+    return dlopen (NULL, RTLD_LAZY);
+}
+
+static void
+_cairo_gl_dispatch_close_lib (void *handle)
+{
+    dlclose (handle);
+}
+
+static cairo_gl_generic_func_t
+_cairo_gl_dispatch_get_proc_addr (void *handle, const char *name)
+{
+    return (cairo_gl_generic_func_t) dlsym (handle, name);
+}
+#else
+static void *
+_cairo_gl_dispatch_open_lib (void)
+{
+    return NULL;
+}
+
+static void
+_cairo_gl_dispatch_close_lib (void *handle)
+{
+    return;
+}
+
+static cairo_gl_generic_func_t
+_cairo_gl_dispatch_get_proc_addr (void *handle, const char *name)
+{
+    return NULL;
+}
+#endif /* CAIRO_HAS_DLSYM */
+
+
+static void
+_cairo_gl_dispatch_init_entries (cairo_gl_dispatch_t *dispatch,
+                                cairo_gl_get_proc_addr_func_t get_proc_addr,
+                                cairo_gl_dispatch_entry_t *entries,
+                                cairo_gl_dispatch_name_t dispatch_name)
+{
+    cairo_gl_dispatch_entry_t *entry = entries;
+    void *handle = _cairo_gl_dispatch_open_lib ();
+
+    while (entry->name[CAIRO_GL_DISPATCH_NAME_CORE] != NULL) {
+       void *dispatch_ptr = &((char *) dispatch)[entry->offset];
+       const char *name = entry->name[dispatch_name];
+
+       /*
+        * In strictly conforming EGL implementations, eglGetProcAddress() can
+        * be used only to get extension functions, but some of the functions
+        * we want belong to core GL(ES). If the *GetProcAddress function
+        * provided by the context fails, try to get the address of the wanted
+        * GL function using standard system facilities (eg dlsym() in *nix
+        * systems).
+        */
+       cairo_gl_generic_func_t func = get_proc_addr (name);
+       if (func == NULL)
+           func = _cairo_gl_dispatch_get_proc_addr (handle, name);
+
+       *((cairo_gl_generic_func_t *) dispatch_ptr) = func;
+
+       ++entry;
+    }
+
+    _cairo_gl_dispatch_close_lib (handle);
+}
+
+static cairo_status_t
+_cairo_gl_dispatch_init_buffers (cairo_gl_dispatch_t *dispatch,
+                                cairo_gl_get_proc_addr_func_t get_proc_addr,
+                                int gl_version, cairo_gl_flavor_t gl_flavor)
+{
+    cairo_gl_dispatch_name_t dispatch_name;
+
+    if (gl_flavor == CAIRO_GL_FLAVOR_DESKTOP)
+    {
+       if (gl_version >= CAIRO_GL_VERSION_ENCODE (1, 5))
+           dispatch_name = CAIRO_GL_DISPATCH_NAME_CORE;
+       else if (_cairo_gl_has_extension ("GL_ARB_vertex_buffer_object"))
+           dispatch_name = CAIRO_GL_DISPATCH_NAME_EXT;
+       else
+           return CAIRO_STATUS_DEVICE_ERROR;
+    }
+    else if (gl_flavor == CAIRO_GL_FLAVOR_ES &&
+            gl_version >= CAIRO_GL_VERSION_ENCODE (2, 0))
+    {
+       dispatch_name = CAIRO_GL_DISPATCH_NAME_ES;
+    }
+    else
+    {
+       return CAIRO_STATUS_DEVICE_ERROR;
+    }
+
+    _cairo_gl_dispatch_init_entries (dispatch, get_proc_addr,
+                                    dispatch_buffers_entries, dispatch_name);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_gl_dispatch_init_shaders (cairo_gl_dispatch_t *dispatch,
+                                cairo_gl_get_proc_addr_func_t get_proc_addr,
+                                int gl_version, cairo_gl_flavor_t gl_flavor)
+{
+    cairo_gl_dispatch_name_t dispatch_name;
+
+    if (gl_flavor == CAIRO_GL_FLAVOR_DESKTOP)
+    {
+       if (gl_version >= CAIRO_GL_VERSION_ENCODE (2, 0))
+           dispatch_name = CAIRO_GL_DISPATCH_NAME_CORE;
+       else if (_cairo_gl_has_extension ("GL_ARB_shader_objects"))
+           dispatch_name = CAIRO_GL_DISPATCH_NAME_EXT;
+       else
+           return CAIRO_STATUS_DEVICE_ERROR;
+    }
+    else if (gl_flavor == CAIRO_GL_FLAVOR_ES &&
+            gl_version >= CAIRO_GL_VERSION_ENCODE (2, 0))
+    {
+       dispatch_name = CAIRO_GL_DISPATCH_NAME_ES;
+    }
+    else
+    {
+       return CAIRO_STATUS_DEVICE_ERROR;
+    }
+
+    _cairo_gl_dispatch_init_entries (dispatch, get_proc_addr,
+                                    dispatch_shaders_entries, dispatch_name);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_gl_dispatch_init_fbo (cairo_gl_dispatch_t *dispatch,
+                            cairo_gl_get_proc_addr_func_t get_proc_addr,
+                            int gl_version, cairo_gl_flavor_t gl_flavor)
+{
+    cairo_gl_dispatch_name_t dispatch_name;
+
+    if (gl_flavor == CAIRO_GL_FLAVOR_DESKTOP)
+    {
+       if (gl_version >= CAIRO_GL_VERSION_ENCODE (3, 0) ||
+           _cairo_gl_has_extension ("GL_ARB_framebuffer_object"))
+           dispatch_name = CAIRO_GL_DISPATCH_NAME_CORE;
+       else if (_cairo_gl_has_extension ("GL_EXT_framebuffer_object"))
+           dispatch_name = CAIRO_GL_DISPATCH_NAME_EXT;
+       else
+           return CAIRO_STATUS_DEVICE_ERROR;
+    }
+    else if (gl_flavor == CAIRO_GL_FLAVOR_ES &&
+            gl_version >= CAIRO_GL_VERSION_ENCODE (2, 0))
+    {
+       dispatch_name = CAIRO_GL_DISPATCH_NAME_ES;
+    }
+    else
+    {
+       return CAIRO_STATUS_DEVICE_ERROR;
+    }
+
+    _cairo_gl_dispatch_init_entries (dispatch, get_proc_addr,
+                                    dispatch_fbo_entries, dispatch_name);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_status_t
+_cairo_gl_dispatch_init (cairo_gl_dispatch_t *dispatch,
+                        cairo_gl_get_proc_addr_func_t get_proc_addr)
+{
+    cairo_status_t status;
+    int gl_version;
+    cairo_gl_flavor_t gl_flavor;
+
+    gl_version = _cairo_gl_get_version ();
+    gl_flavor = _cairo_gl_get_flavor ();
+
+    status = _cairo_gl_dispatch_init_buffers (dispatch, get_proc_addr,
+                                             gl_version, gl_flavor);
+    if (status != CAIRO_STATUS_SUCCESS)
+       return status;
+
+    status = _cairo_gl_dispatch_init_shaders (dispatch, get_proc_addr,
+                                             gl_version, gl_flavor);
+    if (status != CAIRO_STATUS_SUCCESS)
+       return status;
+
+    status = _cairo_gl_dispatch_init_fbo (dispatch, get_proc_addr,
+                                         gl_version, gl_flavor);
+    if (status != CAIRO_STATUS_SUCCESS)
+       return status;
+
+    return CAIRO_STATUS_SUCCESS;
+}
diff --git a/src/cairo-gl-ext-def-private.h b/src/cairo-gl-ext-def-private.h
new file mode 100644 (file)
index 0000000..a261947
--- /dev/null
@@ -0,0 +1,143 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2010 Linaro Limited
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * Contributor(s):
+ *      Alexandros Frantzis <alexandros.frantzis@linaro.org>
+ */
+
+#ifndef CAIRO_GL_EXT_DEF_PRIVATE_H
+#define CAIRO_GL_EXT_DEF_PRIVATE_H
+
+#ifndef GL_TEXTURE_RECTANGLE
+#define GL_TEXTURE_RECTANGLE 0x84F5
+#endif
+
+#ifndef GL_ARRAY_BUFFER
+#define GL_ARRAY_BUFFER 0x8892
+#endif
+
+#ifndef GL_STREAM_DRAW
+#define GL_STREAM_DRAW 0x88E0
+#endif
+
+#ifndef GL_WRITE_ONLY
+#define GL_WRITE_ONLY 0x88B9
+#endif
+
+#ifndef GL_PIXEL_UNPACK_BUFFER
+#define GL_PIXEL_UNPACK_BUFFER 0x88EC
+#endif
+
+#ifndef GL_FRAMEBUFFER
+#define GL_FRAMEBUFFER 0x8D40
+#endif
+
+#ifndef GL_COLOR_ATTACHMENT0
+#define GL_COLOR_ATTACHMENT0 0x8CE0
+#endif
+
+#ifndef GL_FRAMEBUFFER_COMPLETE
+#define GL_FRAMEBUFFER_COMPLETE 0x8CD5
+#endif
+
+#ifndef GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT
+#define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT 0x8CD6
+#endif
+
+#ifndef GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT
+#define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT 0x8CD7
+#endif
+
+#ifndef GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS
+#define GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS 0x8CD9
+#endif
+
+#ifndef GL_FRAMEBUFFER_INCOMPLETE_FORMATS
+#define GL_FRAMEBUFFER_INCOMPLETE_FORMATS 0x8CDA
+#endif
+
+#ifndef GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER
+#define GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER 0x8CDB
+#endif
+
+#ifndef GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER
+#define GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER 0x8CDC
+#endif
+
+#ifndef GL_FRAMEBUFFER_UNSUPPORTED
+#define GL_FRAMEBUFFER_UNSUPPORTED 0x8CDD
+#endif
+
+#ifndef GL_PACK_INVERT_MESA
+#define GL_PACK_INVERT_MESA 0x8758
+#endif
+
+#ifndef GL_CLAMP_TO_BORDER
+#define GL_CLAMP_TO_BORDER 0x812D
+#endif
+
+#ifndef GL_BGR
+#define GL_BGR 0x80E0
+#endif
+
+#ifndef GL_BGRA
+#define GL_BGRA 0x80E1
+#endif
+
+#ifndef GL_RGBA8
+#define GL_RGBA8 0x8058
+#endif
+
+#ifndef GL_UNSIGNED_INT_8_8_8_8
+#define GL_UNSIGNED_INT_8_8_8_8 0x8035
+#endif
+
+#ifndef GL_UNSIGNED_SHORT_5_6_5_REV
+#define GL_UNSIGNED_SHORT_5_6_5_REV 0x8364
+#endif
+
+#ifndef GL_UNSIGNED_SHORT_1_5_5_5_REV
+#define GL_UNSIGNED_SHORT_1_5_5_5_REV 0x8366
+#endif
+
+#ifndef GL_UNSIGNED_INT_8_8_8_8_REV
+#define GL_UNSIGNED_INT_8_8_8_8_REV 0x8367
+#endif
+
+#ifndef GL_PACK_ROW_LENGTH
+#define GL_PACK_ROW_LENGTH 0x0D02
+#endif
+
+#ifndef GL_UNPACK_ROW_LENGTH
+#define GL_UNPACK_ROW_LENGTH 0x0CF2
+#endif
+
+#ifndef GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE
+#define GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE 0x8D56
+#endif
+
+#endif /* CAIRO_GL_EXT_DEF_PRIVATE_H */
diff --git a/src/cairo-gl-glyphs.c b/src/cairo-gl-glyphs.c
new file mode 100644 (file)
index 0000000..ee5c543
--- /dev/null
@@ -0,0 +1,477 @@
+/* Cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Chris Wilson
+ * Copyright © 2010 Intel Corporation
+ * Copyright © 2010 Red Hat, Inc
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Chris Wilson.
+ *
+ * Contributors:
+ *      Benjamin Otte <otte@gnome.org>
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-gl-private.h"
+
+#include "cairo-compositor-private.h"
+#include "cairo-composite-rectangles-private.h"
+#include "cairo-error-private.h"
+#include "cairo-image-surface-private.h"
+#include "cairo-rtree-private.h"
+
+#define GLYPH_CACHE_WIDTH 1024
+#define GLYPH_CACHE_HEIGHT 1024
+#define GLYPH_CACHE_MIN_SIZE 4
+#define GLYPH_CACHE_MAX_SIZE 128
+
+typedef struct _cairo_gl_glyph {
+    cairo_rtree_node_t node;
+    cairo_scaled_glyph_private_t base;
+    cairo_gl_glyph_cache_t *cache;
+    struct { float x, y; } p1, p2;
+} cairo_gl_glyph_t;
+
+static void
+_cairo_gl_glyph_fini (cairo_scaled_glyph_private_t *_priv,
+                     cairo_scaled_glyph_t *scaled_glyph,
+                     cairo_scaled_font_t  *scaled_font)
+{
+    cairo_gl_glyph_t *priv = cairo_container_of (_priv, cairo_gl_glyph_t, base);
+
+    priv->node.owner = NULL;
+    if (! priv->node.pinned) {
+       /* XXX thread-safety? Probably ok due to the frozen scaled-font. */
+       _cairo_rtree_node_remove (&priv->cache->rtree, &priv->node);
+    }
+}
+
+static cairo_int_status_t
+_cairo_gl_glyph_cache_add_glyph (cairo_gl_context_t *ctx,
+                                cairo_gl_glyph_cache_t *cache,
+                                cairo_scaled_glyph_t  *scaled_glyph)
+{
+    cairo_image_surface_t *glyph_surface = scaled_glyph->surface;
+    cairo_gl_surface_t *cache_surface;
+    cairo_gl_glyph_t *glyph_private;
+    cairo_rtree_node_t *node = NULL;
+    cairo_int_status_t status;
+    int width, height;
+
+    width = glyph_surface->width;
+    if (width < GLYPH_CACHE_MIN_SIZE)
+       width = GLYPH_CACHE_MIN_SIZE;
+    height = glyph_surface->height;
+    if (height < GLYPH_CACHE_MIN_SIZE)
+       height = GLYPH_CACHE_MIN_SIZE;
+
+    /* search for an available slot */
+    status = _cairo_rtree_insert (&cache->rtree, width, height, &node);
+    /* search for an unlocked slot */
+    if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
+       status = _cairo_rtree_evict_random (&cache->rtree,
+                                           width, height, &node);
+       if (status == CAIRO_INT_STATUS_SUCCESS) {
+           status = _cairo_rtree_node_insert (&cache->rtree,
+                                              node, width, height, &node);
+       }
+    }
+    if (status)
+       return status;
+
+    cache_surface = (cairo_gl_surface_t *) cache->pattern.surface;
+
+    /* XXX: Make sure we use the mask texture. This should work automagically somehow */
+    glActiveTexture (GL_TEXTURE1);
+    status = _cairo_gl_surface_draw_image (cache_surface,
+                                           glyph_surface,
+                                           0, 0,
+                                           glyph_surface->width, glyph_surface->height,
+                                           node->x, node->y);
+    if (unlikely (status))
+       return status;
+
+    glyph_private = (cairo_gl_glyph_t *) node;
+    glyph_private->cache = cache;
+    _cairo_scaled_glyph_attach_private (scaled_glyph,
+                                       &glyph_private->base,
+                                       cache,
+                                       _cairo_gl_glyph_fini);
+
+    scaled_glyph->dev_private = glyph_private;
+    scaled_glyph->dev_private_key = cache;
+
+    /* compute tex coords */
+    glyph_private->p1.x = node->x;
+    glyph_private->p1.y = node->y;
+    glyph_private->p2.x = node->x + glyph_surface->width;
+    glyph_private->p2.y = node->y + glyph_surface->height;
+    if (! _cairo_gl_device_requires_power_of_two_textures (&ctx->base)) {
+       glyph_private->p1.x /= cache_surface->width;
+       glyph_private->p1.y /= cache_surface->height;
+       glyph_private->p2.x /= cache_surface->width;
+       glyph_private->p2.y /= cache_surface->height;
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_gl_glyph_t *
+_cairo_gl_glyph_cache_lock (cairo_gl_glyph_cache_t *cache,
+                           cairo_scaled_glyph_t *scaled_glyph)
+{
+    return _cairo_rtree_pin (&cache->rtree, scaled_glyph->dev_private);
+}
+
+static cairo_status_t
+cairo_gl_context_get_glyph_cache (cairo_gl_context_t *ctx,
+                                 cairo_format_t format,
+                                  cairo_gl_glyph_cache_t **cache_out)
+{
+    cairo_gl_glyph_cache_t *cache;
+    cairo_content_t content;
+
+    switch (format) {
+    case CAIRO_FORMAT_RGB30:
+    case CAIRO_FORMAT_RGB16_565:
+    case CAIRO_FORMAT_ARGB32:
+    case CAIRO_FORMAT_RGB24:
+       cache = &ctx->glyph_cache[0];
+        content = CAIRO_CONTENT_COLOR_ALPHA;
+       break;
+    case CAIRO_FORMAT_A8:
+    case CAIRO_FORMAT_A1:
+       cache = &ctx->glyph_cache[1];
+        content = CAIRO_CONTENT_ALPHA;
+       break;
+    default:
+    case CAIRO_FORMAT_INVALID:
+       ASSERT_NOT_REACHED;
+       return _cairo_error (CAIRO_STATUS_INVALID_FORMAT);
+    }
+
+    if (unlikely (cache->pattern.surface == NULL)) {
+        cairo_surface_t *surface;
+        surface = cairo_gl_surface_create (&ctx->base,
+                                           content,
+                                           GLYPH_CACHE_WIDTH,
+                                           GLYPH_CACHE_HEIGHT);
+        if (unlikely (surface->status)) {
+            cairo_status_t status = surface->status;
+            cairo_surface_destroy (surface);
+            return status;
+        }
+        _cairo_surface_release_device_reference (surface);
+        _cairo_pattern_init_for_surface (&cache->pattern, surface);
+        cairo_surface_destroy (surface);
+        cache->pattern.base.has_component_alpha = (content == CAIRO_CONTENT_COLOR_ALPHA);
+    }
+
+    *cache_out = cache;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_gl_glyph_cache_unlock (cairo_gl_glyph_cache_t *cache)
+{
+    _cairo_rtree_unpin (&cache->rtree);
+}
+
+static void
+_cairo_gl_font_fini (cairo_scaled_font_private_t *_priv,
+                    cairo_scaled_font_t  *scaled_font)
+{
+    cairo_gl_font_t *priv = (cairo_gl_font_t *)_priv;
+
+    cairo_list_del (&priv->link);
+    free (priv);
+}
+
+static cairo_status_t
+render_glyphs (cairo_gl_surface_t      *dst,
+              int dst_x, int dst_y,
+              cairo_operator_t  op,
+              const cairo_pattern_t    *source,
+              cairo_composite_glyphs_info_t *info,
+              cairo_bool_t             *has_component_alpha)
+{
+    cairo_format_t last_format = CAIRO_FORMAT_INVALID;
+    cairo_gl_glyph_cache_t *cache = NULL;
+    cairo_gl_context_t *ctx;
+    cairo_gl_composite_t setup;
+    cairo_int_status_t status;
+    int i = 0;
+
+    *has_component_alpha = FALSE;
+
+    status = _cairo_gl_context_acquire (dst->base.device, &ctx);
+    if (unlikely (status))
+       return status;
+
+    status = _cairo_gl_composite_init (&setup, op, dst, TRUE, &info->extents);
+    if (unlikely (status))
+       goto FINISH;
+
+    status = _cairo_gl_composite_set_source (&setup, source,
+                                             info->extents.x,
+                                            info->extents.y,
+                                             dst_x, dst_y,
+                                             info->extents.width,
+                                             info->extents.height);
+    if (unlikely (status))
+       goto FINISH;
+
+
+    //_cairo_gl_composite_set_clip_region (&setup, _cairo_clip_get_region (extents->clip));
+
+    for (i = 0; i < info->num_glyphs; i++) {
+       cairo_scaled_glyph_t *scaled_glyph;
+       cairo_gl_glyph_t *glyph;
+       double x_offset, y_offset;
+       double x1, x2, y1, y2;
+
+       status = _cairo_scaled_glyph_lookup (info->font,
+                                            info->glyphs[i].index,
+                                            CAIRO_SCALED_GLYPH_INFO_SURFACE,
+                                            &scaled_glyph);
+       if (unlikely (status))
+           goto FINISH;
+
+       if (scaled_glyph->surface->width  == 0 ||
+           scaled_glyph->surface->height == 0)
+       {
+           continue;
+       }
+       if (scaled_glyph->surface->format != last_format) {
+           status = cairo_gl_context_get_glyph_cache (ctx,
+                                                      scaled_glyph->surface->format,
+                                                       &cache);
+            if (unlikely (status))
+                goto FINISH;
+
+           last_format = scaled_glyph->surface->format;
+
+            status = _cairo_gl_composite_set_mask (&setup,
+                                                   &cache->pattern.base,
+                                                   0, 0, 0, 0, 0, 0);
+            if (unlikely (status))
+                goto FINISH;
+
+           *has_component_alpha |= cache->pattern.base.has_component_alpha;
+
+            /* XXX: _cairo_gl_composite_begin() acquires the context a
+             * second time. Need to refactor this loop so this doesn't happen.
+             */
+            status = _cairo_gl_composite_begin (&setup, &ctx);
+
+            status = _cairo_gl_context_release (ctx, status);
+           if (unlikely (status))
+               goto FINISH;
+       }
+
+       if (scaled_glyph->dev_private_key != cache) {
+           cairo_scaled_glyph_private_t *priv;
+
+           priv = _cairo_scaled_glyph_find_private (scaled_glyph, cache);
+           if (priv) {
+               scaled_glyph->dev_private_key = cache;
+               scaled_glyph->dev_private = cairo_container_of (priv,
+                                                               cairo_gl_glyph_t,
+                                                               base);;
+           } else {
+               status = _cairo_gl_glyph_cache_add_glyph (ctx, cache, scaled_glyph);
+
+               if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
+                   /* Cache is full, so flush existing prims and try again. */
+                   _cairo_gl_composite_flush (ctx);
+                   _cairo_gl_glyph_cache_unlock (cache);
+                   status = _cairo_gl_glyph_cache_add_glyph (ctx, cache, scaled_glyph);
+               }
+
+               if (unlikely (_cairo_int_status_is_error (status)))
+                   goto FINISH;
+           }
+       }
+
+       x_offset = scaled_glyph->surface->base.device_transform.x0;
+       y_offset = scaled_glyph->surface->base.device_transform.y0;
+
+       x1 = _cairo_lround (info->glyphs[i].x - x_offset);
+       y1 = _cairo_lround (info->glyphs[i].y - y_offset);
+       x2 = x1 + scaled_glyph->surface->width;
+       y2 = y1 + scaled_glyph->surface->height;
+
+       glyph = _cairo_gl_glyph_cache_lock (cache, scaled_glyph);
+       _cairo_gl_composite_emit_glyph (ctx,
+                                       x1, y1, x2, y2,
+                                        glyph->p1.x, glyph->p1.y,
+                                        glyph->p2.x, glyph->p2.y);
+    }
+
+    status = CAIRO_STATUS_SUCCESS;
+  FINISH:
+    status = _cairo_gl_context_release (ctx, status);
+
+    _cairo_gl_composite_fini (&setup);
+    return status;
+}
+
+static cairo_int_status_t
+render_glyphs_via_mask (cairo_gl_surface_t     *dst,
+                          cairo_operator_t      op,
+                          const cairo_surface_t        *source,
+                          cairo_composite_glyphs_info_t *info)
+{
+    cairo_surface_t *mask;
+    cairo_status_t status;
+    cairo_bool_t has_component_alpha;
+    int i;
+
+    /* XXX: For non-CA, this should be CAIRO_CONTENT_ALPHA to save memory */
+    mask = cairo_gl_surface_create (dst->base.device,
+                                    CAIRO_CONTENT_COLOR_ALPHA,
+                                    info->extents.width,
+                                    info->extents.height);
+    if (unlikely (mask->status))
+        return mask->status;
+
+    for (i = 0; i < info->num_glyphs; i++) {
+       info->glyphs[i].x -= info->extents.x;
+       info->glyphs[i].y -= info->extents.y;
+    }
+
+    status = render_glyphs ((cairo_gl_surface_t *) mask, 0, 0,
+                           CAIRO_OPERATOR_ADD,
+                           &_cairo_pattern_white.base,
+                           info, &has_component_alpha);
+    if (likely (status == CAIRO_STATUS_SUCCESS)) {
+       /* XXX composite */
+#if 0
+       cairo_surface_pattern_t mask_pattern;
+        mask->is_clear = FALSE;
+       _cairo_pattern_init_for_surface (&mask_pattern, mask);
+       mask_pattern.base.has_component_alpha = has_component_alpha;
+       cairo_matrix_init_translate (&mask_pattern.base.matrix,
+                                    -info->extents.x, -info->extents.y);
+       status = _cairo_surface_mask (&dst->base, op,
+                                     source, &mask_pattern.base,
+                                     NULL);
+       _cairo_pattern_fini (&mask_pattern.base);
+#endif
+    } else {
+       for (i = 0; i < info->num_glyphs; i++) {
+           info->glyphs[i].x += info->extents.x;
+           info->glyphs[i].y += info->extents.y;
+       }
+    }
+
+    cairo_surface_destroy (mask);
+
+    return status;
+}
+
+cairo_int_status_t
+_cairo_gl_check_composite_glyphs (const cairo_composite_rectangles_t *extents,
+                                 cairo_scaled_font_t *scaled_font,
+                                 cairo_glyph_t *glyphs,
+                                 int *num_glyphs)
+{
+    if (! _cairo_gl_operator_is_supported (extents->op))
+       return UNSUPPORTED ("unsupported operator");
+
+    /* XXX use individual masks for large glyphs? */
+    if (ceil (scaled_font->max_scale) >= GLYPH_CACHE_MAX_SIZE)
+       return UNSUPPORTED ("glyphs too large");
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_int_status_t
+_cairo_gl_composite_glyphs (void                       *_dst,
+                           cairo_operator_t             op,
+                           cairo_surface_t             *_src,
+                           int                          src_x,
+                           int                          src_y,
+                           int                          dst_x,
+                           int                          dst_y,
+                           cairo_composite_glyphs_info_t *info)
+{
+    cairo_gl_surface_t *dst = _dst;
+    cairo_bool_t has_component_alpha;
+    int i;
+
+    /* If any of the glyphs are component alpha, we have to go through a mask,
+     * since only _cairo_gl_surface_composite() currently supports component
+     * alpha.
+     */
+    if (!dst->base.is_clear && ! info->use_mask && op != CAIRO_OPERATOR_OVER) {
+       for (i = 0; i < info->num_glyphs; i++) {
+           cairo_scaled_glyph_t *scaled_glyph;
+
+           if (_cairo_scaled_glyph_lookup (info->font, info->glyphs[i].index,
+                                           CAIRO_SCALED_GLYPH_INFO_SURFACE,
+                                           &scaled_glyph) == CAIRO_INT_STATUS_SUCCESS &&
+               scaled_glyph->surface->format == CAIRO_FORMAT_ARGB32)
+           {
+               info->use_mask = TRUE;
+               break;
+           }
+       }
+    }
+
+    if (info->use_mask) {
+       return render_glyphs_via_mask (dst, op, _src, info);
+    } else {
+       return render_glyphs (dst, dst_x, dst_y,
+                             op, _src, info,
+                             &has_component_alpha);
+    }
+}
+
+void
+_cairo_gl_glyph_cache_init (cairo_gl_glyph_cache_t *cache)
+{
+    _cairo_rtree_init (&cache->rtree,
+                      GLYPH_CACHE_WIDTH,
+                      GLYPH_CACHE_HEIGHT,
+                      GLYPH_CACHE_MIN_SIZE,
+                      sizeof (cairo_gl_glyph_t));
+}
+
+void
+_cairo_gl_glyph_cache_fini (cairo_gl_context_t *ctx,
+                           cairo_gl_glyph_cache_t *cache)
+{
+    _cairo_rtree_fini (&cache->rtree);
+
+    if (cache->pattern.surface) {
+        _cairo_pattern_fini (&cache->pattern.base);
+        cache->pattern.surface = NULL;
+    }
+}
diff --git a/src/cairo-gl-gradient-private.h b/src/cairo-gl-gradient-private.h
new file mode 100644 (file)
index 0000000..77f9bbd
--- /dev/null
@@ -0,0 +1,93 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Eric Anholt
+ * Copyright © 2009 Chris Wilson
+ * Copyright © 2005,2010 Red Hat, Inc
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ *     Benjamin Otte <otte@gnome.org>
+ *     Carl Worth <cworth@cworth.org>
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ *     Eric Anholt <eric@anholt.net>
+ */
+
+#ifndef CAIRO_GL_GRADIENT_PRIVATE_H
+#define CAIRO_GL_GRADIENT_PRIVATE_H
+
+#define GL_GLEXT_PROTOTYPES
+
+#include "cairo-cache-private.h"
+#include "cairo-device-private.h"
+#include "cairo-reference-count-private.h"
+#include "cairo-pattern-private.h"
+#include "cairo-types-private.h"
+
+#include "cairo-gl.h"
+
+#if CAIRO_HAS_GL_SURFACE
+#include <GL/gl.h>
+#include <GL/glext.h>
+#elif CAIRO_HAS_GLESV2_SURFACE
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#endif
+
+#define CAIRO_GL_GRADIENT_CACHE_SIZE 4096
+
+/* XXX: Declare in a better place */
+typedef struct _cairo_gl_context cairo_gl_context_t;
+
+typedef struct _cairo_gl_gradient {
+    cairo_cache_entry_t           cache_entry;
+    cairo_reference_count_t       ref_count;
+    cairo_device_t               *device; /* NB: we don't hold a reference */
+    GLuint                        tex;
+    unsigned int                 n_stops;
+    const cairo_gradient_stop_t  *stops;
+    cairo_gradient_stop_t         stops_embedded[1];
+} cairo_gl_gradient_t;
+
+cairo_private cairo_int_status_t
+_cairo_gl_gradient_create (cairo_gl_context_t           *ctx,
+                           unsigned int                  n_stops,
+                           const cairo_gradient_stop_t  *stops,
+                           cairo_gl_gradient_t         **gradient_out);
+
+cairo_private_no_warn cairo_gl_gradient_t *
+_cairo_gl_gradient_reference (cairo_gl_gradient_t *gradient);
+
+cairo_private void
+_cairo_gl_gradient_destroy (cairo_gl_gradient_t *gradient);
+
+cairo_private cairo_bool_t
+_cairo_gl_gradient_equal (const void *key_a, const void *key_b);
+
+
+#endif /* CAIRO_GL_GRADIENT_PRIVATE_H */
diff --git a/src/cairo-gl-gradient.c b/src/cairo-gl-gradient.c
new file mode 100644 (file)
index 0000000..aad2773
--- /dev/null
@@ -0,0 +1,309 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Eric Anholt
+ * Copyright © 2009 Chris Wilson
+ * Copyright © 2005,2010 Red Hat, Inc
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ *     Benjamin Otte <otte@gnome.org>
+ *     Carl Worth <cworth@cworth.org>
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ *     Eric Anholt <eric@anholt.net>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-error-private.h"
+#include "cairo-gl-gradient-private.h"
+#include "cairo-gl-private.h"
+
+
+static int
+_cairo_gl_gradient_sample_width (unsigned int                 n_stops,
+                                 const cairo_gradient_stop_t *stops)
+{
+    unsigned int n;
+    int width;
+
+    width = 8;
+    for (n = 1; n < n_stops; n++) {
+       double dx = stops[n].offset - stops[n-1].offset;
+       double delta, max;
+       int ramp;
+
+       if (dx == 0)
+           continue;
+
+       max = stops[n].color.red - stops[n-1].color.red;
+
+       delta = stops[n].color.green - stops[n-1].color.green;
+       if (delta > max)
+           max = delta;
+
+       delta = stops[n].color.blue - stops[n-1].color.blue;
+       if (delta > max)
+           max = delta;
+
+       delta = stops[n].color.alpha - stops[n-1].color.alpha;
+       if (delta > max)
+           max = delta;
+
+       ramp = 128 * max / dx;
+       if (ramp > width)
+           width = ramp;
+    }
+
+    width = (width + 7) & -8;
+    return MIN (width, 1024);
+}
+
+static cairo_status_t
+_cairo_gl_gradient_render (const cairo_gl_context_t    *ctx,
+                           unsigned int                 n_stops,
+                           const cairo_gradient_stop_t *stops,
+                           void                        *bytes,
+                           int                          width)
+{
+    pixman_image_t *gradient, *image;
+    pixman_gradient_stop_t pixman_stops_stack[32];
+    pixman_gradient_stop_t *pixman_stops;
+    pixman_point_fixed_t p1, p2;
+    unsigned int i;
+    pixman_format_code_t gradient_pixman_format;
+
+    /*
+     * Ensure that the order of the gradient's components in memory is BGRA.
+     * This is done so that the gradient's pixel data is always suitable for
+     * texture upload using format=GL_BGRA and type=GL_UNSIGNED_BYTE.
+     */
+    if (_cairo_is_little_endian ())
+       gradient_pixman_format = PIXMAN_a8r8g8b8;
+    else
+       gradient_pixman_format = PIXMAN_b8g8r8a8;
+
+    pixman_stops = pixman_stops_stack;
+    if (unlikely (n_stops > ARRAY_LENGTH (pixman_stops_stack))) {
+       pixman_stops = _cairo_malloc_ab (n_stops,
+                                        sizeof (pixman_gradient_stop_t));
+       if (unlikely (pixman_stops == NULL))
+           return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+    }
+
+    for (i = 0; i < n_stops; i++) {
+       pixman_stops[i].x = _cairo_fixed_16_16_from_double (stops[i].offset);
+       pixman_stops[i].color.red   = stops[i].color.red_short;
+       pixman_stops[i].color.green = stops[i].color.green_short;
+       pixman_stops[i].color.blue  = stops[i].color.blue_short;
+       pixman_stops[i].color.alpha = stops[i].color.alpha_short;
+    }
+
+    p1.x = 0;
+    p1.y = 0;
+    p2.x = width << 16;
+    p2.y = 0;
+
+    gradient = pixman_image_create_linear_gradient (&p1, &p2,
+                                                   pixman_stops,
+                                                   n_stops);
+    if (pixman_stops != pixman_stops_stack)
+       free (pixman_stops);
+
+    if (unlikely (gradient == NULL))
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    pixman_image_set_filter (gradient, PIXMAN_FILTER_BILINEAR, NULL, 0);
+    pixman_image_set_repeat (gradient, PIXMAN_REPEAT_PAD);
+
+    image = pixman_image_create_bits (gradient_pixman_format, width, 1,
+                                     bytes, sizeof(uint32_t)*width);
+    if (unlikely (image == NULL)) {
+       pixman_image_unref (gradient);
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+    }
+
+    pixman_image_composite32 (PIXMAN_OP_SRC,
+                              gradient, NULL, image,
+                              0, 0,
+                              0, 0,
+                              0, 0,
+                              width, 1);
+
+    pixman_image_unref (gradient);
+    pixman_image_unref (image);
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static unsigned long
+_cairo_gl_gradient_hash (unsigned int                  n_stops,
+                         const cairo_gradient_stop_t  *stops)
+{
+    return _cairo_hash_bytes (n_stops,
+                              stops,
+                              sizeof (cairo_gradient_stop_t) * n_stops);
+}
+
+static cairo_gl_gradient_t *
+_cairo_gl_gradient_lookup (cairo_gl_context_t           *ctx,
+                           unsigned long                 hash,
+                           unsigned int                  n_stops,
+                           const cairo_gradient_stop_t  *stops)
+{
+    cairo_gl_gradient_t lookup;
+
+    lookup.cache_entry.hash = hash,
+    lookup.n_stops = n_stops;
+    lookup.stops = stops;
+
+    return _cairo_cache_lookup (&ctx->gradients, &lookup.cache_entry);
+}
+
+cairo_bool_t
+_cairo_gl_gradient_equal (const void *key_a, const void *key_b)
+{
+    const cairo_gl_gradient_t *a = key_a;
+    const cairo_gl_gradient_t *b = key_b;
+
+    if (a->n_stops != b->n_stops)
+        return FALSE;
+
+    return memcmp (a->stops, b->stops, a->n_stops * sizeof (cairo_gradient_stop_t)) == 0;
+}
+
+cairo_int_status_t
+_cairo_gl_gradient_create (cairo_gl_context_t           *ctx,
+                           unsigned int                  n_stops,
+                           const cairo_gradient_stop_t  *stops,
+                           cairo_gl_gradient_t         **gradient_out)
+{
+    unsigned long hash;
+    cairo_gl_gradient_t *gradient;
+    cairo_status_t status;
+    int tex_width;
+    void *data;
+    cairo_gl_dispatch_t *dispatch = &ctx->dispatch;
+
+    if ((unsigned int) ctx->max_texture_size / 2 <= n_stops)
+        return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    hash = _cairo_gl_gradient_hash (n_stops, stops);
+
+    gradient = _cairo_gl_gradient_lookup (ctx, hash, n_stops, stops);
+    if (gradient) {
+        *gradient_out = _cairo_gl_gradient_reference (gradient);
+        return CAIRO_STATUS_SUCCESS;
+    }
+
+    gradient = malloc (sizeof (cairo_gl_gradient_t) + sizeof (cairo_gradient_stop_t) * (n_stops - 1));
+    if (gradient == NULL)
+        return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    tex_width = _cairo_gl_gradient_sample_width (n_stops, stops);
+
+    CAIRO_REFERENCE_COUNT_INIT (&gradient->ref_count, 2);
+    gradient->cache_entry.hash = hash;
+    gradient->cache_entry.size = tex_width;
+    gradient->device = &ctx->base;
+    gradient->n_stops = n_stops;
+    gradient->stops = gradient->stops_embedded;
+    memcpy (gradient->stops_embedded, stops, n_stops * sizeof (cairo_gradient_stop_t));
+
+    glGenTextures (1, &gradient->tex);
+    _cairo_gl_context_activate (ctx, CAIRO_GL_TEX_TEMP);
+    glBindTexture (ctx->tex_target, gradient->tex);
+
+    /* GL_PIXEL_UNPACK_BUFFER is only available in Desktop GL */
+    if (ctx->gl_flavor == CAIRO_GL_FLAVOR_DESKTOP) {
+       dispatch->BindBuffer (GL_PIXEL_UNPACK_BUFFER, ctx->texture_load_pbo);
+       dispatch->BufferData (GL_PIXEL_UNPACK_BUFFER,
+                             tex_width * sizeof (uint32_t), 0, GL_STREAM_DRAW);
+       data = dispatch->MapBuffer (GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY);
+
+       status = _cairo_gl_gradient_render (ctx, n_stops, stops, data, tex_width);
+
+       dispatch->UnmapBuffer (GL_PIXEL_UNPACK_BUFFER);
+
+       if (unlikely (status)) {
+           dispatch->BindBuffer (GL_PIXEL_UNPACK_BUFFER, 0);
+           free (gradient);
+           return status;
+       }
+
+       glTexImage2D (ctx->tex_target, 0, GL_RGBA8, tex_width, 1, 0,
+                     GL_BGRA, GL_UNSIGNED_BYTE, 0);
+
+       dispatch->BindBuffer (GL_PIXEL_UNPACK_BUFFER, 0);
+    }
+    else {
+       data = _cairo_malloc_ab (tex_width, sizeof (uint32_t));
+
+       status = _cairo_gl_gradient_render (ctx, n_stops, stops, data, tex_width);
+
+       glTexImage2D (ctx->tex_target, 0, GL_BGRA, tex_width, 1, 0,
+                     GL_BGRA, GL_UNSIGNED_BYTE, data);
+
+       free (data);
+    }
+
+    /* we ignore errors here and just return an uncached gradient */
+    if (unlikely (_cairo_cache_insert (&ctx->gradients, &gradient->cache_entry)))
+       CAIRO_REFERENCE_COUNT_INIT (&gradient->ref_count, 1);
+
+    *gradient_out = gradient;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_gl_gradient_t *
+_cairo_gl_gradient_reference (cairo_gl_gradient_t *gradient)
+{
+    assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&gradient->ref_count));
+
+    _cairo_reference_count_inc (&gradient->ref_count);
+
+    return gradient;
+}
+
+void
+_cairo_gl_gradient_destroy (cairo_gl_gradient_t *gradient)
+{
+    cairo_gl_context_t *ctx;
+    cairo_status_t ignore;
+
+    assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&gradient->ref_count));
+
+    if (! _cairo_reference_count_dec_and_test (&gradient->ref_count))
+       return;
+
+    if (_cairo_gl_context_acquire (gradient->device, &ctx) == CAIRO_STATUS_SUCCESS) {
+        glDeleteTextures (1, &gradient->tex);
+        ignore = _cairo_gl_context_release (ctx, CAIRO_STATUS_SUCCESS);
+    }
+
+    free (gradient);
+}
diff --git a/src/cairo-gl-info.c b/src/cairo-gl-info.c
new file mode 100644 (file)
index 0000000..12a618d
--- /dev/null
@@ -0,0 +1,91 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2010 Linaro Limited
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * Contributor(s):
+ *      Alexandros Frantzis <alexandros.frantzis@linaro.org>
+ */
+
+#include "cairoint.h"
+#include "cairo-gl-private.h"
+
+int
+_cairo_gl_get_version (void)
+{
+    int major, minor;
+    const char *version = (const char *) glGetString (GL_VERSION);
+    const char *dot = version == NULL ? NULL : strchr (version, '.');
+    const char *major_start = dot;
+
+    /* Sanity check */
+    if (dot == NULL || dot == version || *(dot + 1) == '\0') {
+       major = 0;
+       minor = 0;
+    } else {
+       /* Find the start of the major version in the string */
+       while (major_start > version && *major_start != ' ')
+           --major_start;
+       major = strtol (major_start, NULL, 10);
+       minor = strtol (dot + 1, NULL, 10);
+    }
+
+    return CAIRO_GL_VERSION_ENCODE (major, minor);
+}
+
+cairo_gl_flavor_t
+_cairo_gl_get_flavor (void)
+{
+    const char *version = (const char *) glGetString (GL_VERSION);
+    cairo_gl_flavor_t flavor;
+
+    if (version == NULL)
+       flavor = CAIRO_GL_FLAVOR_NONE;
+    else if (strstr (version, "OpenGL ES") != NULL)
+       flavor = CAIRO_GL_FLAVOR_ES;
+    else
+       flavor = CAIRO_GL_FLAVOR_DESKTOP;
+
+    return flavor;
+}
+
+cairo_bool_t
+_cairo_gl_has_extension (const char *ext)
+{
+    const char *extensions = (const char *) glGetString (GL_EXTENSIONS);
+    size_t len = strlen (ext);
+    const char *ext_ptr = extensions;
+
+    if (unlikely (ext_ptr == NULL))
+       return 0;
+
+    while ((ext_ptr = strstr (ext_ptr, ext)) != NULL) {
+       if (ext_ptr[len] == ' ' || ext_ptr[len] == '\0')
+           break;
+       ext_ptr += len;
+    }
+
+    return (ext_ptr != NULL);
+}
diff --git a/src/cairo-gl-operand.c b/src/cairo-gl-operand.c
new file mode 100644 (file)
index 0000000..a7e187e
--- /dev/null
@@ -0,0 +1,638 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Eric Anholt
+ * Copyright © 2009 Chris Wilson
+ * Copyright © 2005,2010 Red Hat, Inc
+ * Copyright © 2011 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ *     Benjamin Otte <otte@gnome.org>
+ *     Carl Worth <cworth@cworth.org>
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ *     Eric Anholt <eric@anholt.net>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-gl-private.h"
+
+#include "cairo-composite-rectangles-private.h"
+#include "cairo-compositor-private.h"
+#include "cairo-default-context-private.h"
+#include "cairo-error-private.h"
+#include "cairo-image-surface-private.h"
+#include "cairo-surface-backend-private.h"
+#include "cairo-surface-offset-private.h"
+#include "cairo-surface-subsurface-private.h"
+
+static cairo_int_status_t
+_cairo_gl_create_gradient_texture (cairo_gl_surface_t *dst,
+                                  const cairo_gradient_pattern_t *pattern,
+                                   cairo_gl_gradient_t **gradient)
+{
+    cairo_gl_context_t *ctx;
+    cairo_status_t status;
+
+    status = _cairo_gl_context_acquire (dst->base.device, &ctx);
+    if (unlikely (status))
+       return status;
+
+    status = _cairo_gl_gradient_create (ctx, pattern->n_stops, pattern->stops, gradient);
+
+    return _cairo_gl_context_release (ctx, status);
+}
+
+static cairo_status_t
+_cairo_gl_surface_operand_init (cairo_gl_operand_t *operand,
+                               const cairo_pattern_t *_src,
+                               cairo_gl_surface_t *dst,
+                               int src_x, int src_y,
+                               int dst_x, int dst_y,
+                               int width, int height)
+{
+    const cairo_surface_pattern_t *src = (cairo_surface_pattern_t *)_src;
+    cairo_gl_surface_t *surface;
+    cairo_surface_attributes_t *attributes;
+    cairo_matrix_t m;
+
+    surface = (cairo_gl_surface_t *) src->surface;
+    if (surface->base.type != CAIRO_SURFACE_TYPE_GL)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+    if (surface->base.backend->type != CAIRO_SURFACE_TYPE_GL) {
+       if (_cairo_surface_is_subsurface (&surface->base)) {
+           surface = (cairo_gl_surface_t *)
+               _cairo_surface_subsurface_get_target_with_offset (&surface->base, &src_x, &src_y);
+       }
+    }
+
+    attributes = &operand->texture.attributes;
+
+    operand->type = CAIRO_GL_OPERAND_TEXTURE;
+    operand->texture.surface =
+       (cairo_gl_surface_t *) cairo_surface_reference (&surface->base);
+    operand->texture.tex = surface->tex;
+
+    /* Translate the matrix from
+     * (unnormalized src -> unnormalized src) to
+     * (unnormalized dst -> unnormalized src)
+     */
+    cairo_matrix_init_translate (&m, src_x - dst_x, src_y - dst_y);
+    cairo_matrix_multiply (&attributes->matrix, &m, &src->base.matrix);
+
+    /* Translate the matrix from
+     * (unnormalized dst -> unnormalized src) to
+     * (unnormalized dst -> normalized src)
+     */
+    if (_cairo_gl_device_requires_power_of_two_textures (dst->base.device)) {
+       cairo_matrix_init_scale (&m,
+                                1.0,
+                                1.0);
+    } else {
+       cairo_matrix_init_scale (&m,
+                                1.0 / surface->width,
+                                1.0 / surface->height);
+    }
+    cairo_matrix_multiply (&attributes->matrix,
+                          &attributes->matrix,
+                          &m);
+
+    attributes->extend = src->base.extend;
+    attributes->filter = src->base.filter;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_gl_pattern_texture_setup (cairo_gl_operand_t *operand,
+                                const cairo_pattern_t *_src,
+                                cairo_gl_surface_t *dst,
+                                int src_x, int src_y,
+                                int dst_x, int dst_y,
+                                int width, int height)
+{
+    const cairo_surface_pattern_t *src = (cairo_surface_pattern_t *)_src;
+    cairo_status_t status;
+    cairo_matrix_t m;
+    cairo_gl_surface_t *surface;
+    cairo_gl_context_t *ctx;
+    cairo_surface_attributes_t *attributes;
+
+    attributes = &operand->texture.attributes;
+
+    status = _cairo_gl_context_acquire (dst->base.device, &ctx);
+    if (unlikely (status))
+       return status;
+
+    surface = (cairo_gl_surface_t *)
+       _cairo_gl_surface_create_scratch (ctx,
+                                         src->surface->content,
+                                         width, height);
+    if (src->surface->backend->type == CAIRO_SURFACE_TYPE_IMAGE) {
+       status = _cairo_gl_surface_draw_image (surface,
+                                     (cairo_image_surface_t *)src->surface,
+                                     src_x, src_y,
+                                     width, height,
+                                     0, 0);
+
+       attributes->extend = src->base.extend;
+       attributes->filter = src->base.filter;
+    } else {
+       cairo_surface_t *image;
+
+       image = cairo_surface_map_to_image (&surface->base, NULL);
+       status = _cairo_surface_offset_paint (image, src_x, src_y,
+                                             CAIRO_OPERATOR_SOURCE, _src,
+                                             NULL);
+       cairo_surface_unmap_image (&surface->base, image);
+
+       attributes->extend = CAIRO_EXTEND_NONE;
+       attributes->filter = CAIRO_FILTER_NEAREST;
+    }
+
+    status = _cairo_gl_context_release (ctx, status);
+
+    operand->type = CAIRO_GL_OPERAND_TEXTURE;
+    operand->texture.surface = surface;
+    operand->texture.tex = surface->tex;
+
+    /* Translate the matrix from
+     * (unnormalized src -> unnormalized src) to
+     * (unnormalized dst -> unnormalized src)
+     */
+    cairo_matrix_init_translate (&m, -dst_x, -dst_y);
+    cairo_matrix_multiply (&attributes->matrix, &m, &src->base.matrix);
+
+    /* Translate the matrix from
+     * (unnormalized dst -> unnormalized src) to
+     * (unnormalized dst -> normalized src)
+     */
+    if (_cairo_gl_device_requires_power_of_two_textures (dst->base.device)) {
+       cairo_matrix_init_scale (&m,
+                                1.0,
+                                1.0);
+    } else {
+       cairo_matrix_init_scale (&m,
+                                1.0 / surface->width,
+                                1.0 / surface->height);
+    }
+    cairo_matrix_multiply (&attributes->matrix,
+                          &attributes->matrix,
+                          &m);
+    return CAIRO_STATUS_SUCCESS;
+}
+
+void
+_cairo_gl_solid_operand_init (cairo_gl_operand_t *operand,
+                             const cairo_color_t *color)
+{
+    operand->type = CAIRO_GL_OPERAND_CONSTANT;
+    operand->constant.color[0] = color->red   * color->alpha;
+    operand->constant.color[1] = color->green * color->alpha;
+    operand->constant.color[2] = color->blue  * color->alpha;
+    operand->constant.color[3] = color->alpha;
+}
+
+static cairo_status_t
+_cairo_gl_gradient_operand_init (cairo_gl_operand_t *operand,
+                                 const cairo_pattern_t *pattern,
+                                cairo_gl_surface_t *dst,
+                                int src_x, int src_y,
+                                int dst_x, int dst_y)
+{
+    const cairo_gradient_pattern_t *gradient = (const cairo_gradient_pattern_t *)pattern;
+    cairo_status_t status;
+
+    assert (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR ||
+           gradient->base.type == CAIRO_PATTERN_TYPE_RADIAL);
+
+    if (! _cairo_gl_device_has_glsl (dst->base.device))
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    status = _cairo_gl_create_gradient_texture (dst,
+                                               gradient,
+                                               &operand->gradient.gradient);
+    if (unlikely (status))
+       return status;
+
+    if (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR) {
+       cairo_linear_pattern_t *linear = (cairo_linear_pattern_t *) gradient;
+       double x0, y0, dx, dy, sf, offset;
+
+       dx = linear->pd2.x - linear->pd1.x;
+       dy = linear->pd2.y - linear->pd1.y;
+       sf = 1.0 / (dx * dx + dy * dy);
+       dx *= sf;
+       dy *= sf;
+
+       x0 = linear->pd1.x;
+       y0 = linear->pd1.y;
+       offset = dx * x0 + dy * y0;
+
+       operand->type = CAIRO_GL_OPERAND_LINEAR_GRADIENT;
+
+       cairo_matrix_init (&operand->gradient.m, dx, 0, dy, 1, -offset, 0);
+       if (! _cairo_matrix_is_identity (&pattern->matrix)) {
+           cairo_matrix_multiply (&operand->gradient.m,
+                                  &pattern->matrix,
+                                  &operand->gradient.m);
+       }
+    } else {
+       cairo_matrix_t m;
+       cairo_circle_double_t circles[2];
+       double x0, y0, r0, dx, dy, dr;
+
+       /*
+        * Some fragment shader implementations use half-floats to
+        * represent numbers, so the maximum number they can represent
+        * is about 2^14. Some intermediate computations used in the
+        * radial gradient shaders can produce results of up to 2*k^4.
+        * Setting k=8 makes the maximum result about 8192 (assuming
+        * that the extreme circles are not much smaller than the
+        * destination image).
+        */
+       _cairo_gradient_pattern_fit_to_range (gradient, 8.,
+                                             &operand->gradient.m, circles);
+
+       x0 = circles[0].center.x;
+       y0 = circles[0].center.y;
+       r0 = circles[0].radius;
+       dx = circles[1].center.x - x0;
+       dy = circles[1].center.y - y0;
+       dr = circles[1].radius   - r0;
+
+       operand->gradient.a = dx * dx + dy * dy - dr * dr;
+       operand->gradient.radius_0 = r0;
+       operand->gradient.circle_d.center.x = dx;
+       operand->gradient.circle_d.center.y = dy;
+       operand->gradient.circle_d.radius   = dr;
+
+       if (operand->gradient.a == 0)
+           operand->type = CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0;
+       else if (pattern->extend == CAIRO_EXTEND_NONE)
+           operand->type = CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE;
+       else
+           operand->type = CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT;
+
+       cairo_matrix_init_translate (&m, -x0, -y0);
+       cairo_matrix_multiply (&operand->gradient.m,
+                              &operand->gradient.m,
+                              &m);
+    }
+
+    cairo_matrix_translate (&operand->gradient.m, src_x - dst_x, src_y - dst_y);
+
+    operand->gradient.extend = pattern->extend;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+void
+_cairo_gl_operand_copy (cairo_gl_operand_t *dst,
+                       const cairo_gl_operand_t *src)
+{
+    *dst = *src;
+    switch (dst->type) {
+    case CAIRO_GL_OPERAND_CONSTANT:
+       break;
+    case CAIRO_GL_OPERAND_LINEAR_GRADIENT:
+    case CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0:
+    case CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE:
+    case CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT:
+       _cairo_gl_gradient_reference (dst->gradient.gradient);
+       break;
+    case CAIRO_GL_OPERAND_TEXTURE:
+       break;
+    default:
+    case CAIRO_GL_OPERAND_COUNT:
+        ASSERT_NOT_REACHED;
+    case CAIRO_GL_OPERAND_NONE:
+        break;
+    }
+}
+
+void
+_cairo_gl_operand_destroy (cairo_gl_operand_t *operand)
+{
+    switch (operand->type) {
+    case CAIRO_GL_OPERAND_CONSTANT:
+       break;
+    case CAIRO_GL_OPERAND_LINEAR_GRADIENT:
+    case CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0:
+    case CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE:
+    case CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT:
+       _cairo_gl_gradient_destroy (operand->gradient.gradient);
+       break;
+    case CAIRO_GL_OPERAND_TEXTURE:
+       break;
+    default:
+    case CAIRO_GL_OPERAND_COUNT:
+        ASSERT_NOT_REACHED;
+    case CAIRO_GL_OPERAND_NONE:
+        break;
+    }
+
+    operand->type = CAIRO_GL_OPERAND_NONE;
+}
+
+cairo_int_status_t
+_cairo_gl_operand_init (cairo_gl_operand_t *operand,
+                       const cairo_pattern_t *pattern,
+                       cairo_gl_surface_t *dst,
+                       int src_x, int src_y,
+                       int dst_x, int dst_y,
+                       int width, int height)
+{
+    cairo_int_status_t status;
+
+    switch (pattern->type) {
+    case CAIRO_PATTERN_TYPE_SOLID:
+       _cairo_gl_solid_operand_init (operand,
+                                     &((cairo_solid_pattern_t *) pattern)->color);
+       return CAIRO_STATUS_SUCCESS;
+    case CAIRO_PATTERN_TYPE_SURFACE:
+       status = _cairo_gl_surface_operand_init (operand,
+                                                pattern, dst,
+                                                src_x, src_y,
+                                                dst_x, dst_y,
+                                                width, height);
+       if (status == CAIRO_INT_STATUS_UNSUPPORTED)
+           break;
+
+       return status;
+
+    case CAIRO_PATTERN_TYPE_LINEAR:
+    case CAIRO_PATTERN_TYPE_RADIAL:
+       status = _cairo_gl_gradient_operand_init (operand,
+                                                 pattern, dst,
+                                                 src_x, src_y,
+                                                 dst_x, dst_y);
+       if (status == CAIRO_INT_STATUS_UNSUPPORTED)
+           break;
+
+       return status;
+
+    default:
+    case CAIRO_PATTERN_TYPE_MESH:
+       break;
+    }
+
+    return _cairo_gl_pattern_texture_setup (operand,
+                                           pattern, dst,
+                                           src_x, src_y,
+                                           dst_x, dst_y,
+                                           width, height);
+}
+
+cairo_filter_t
+_cairo_gl_operand_get_filter (cairo_gl_operand_t *operand)
+{
+    cairo_filter_t filter;
+
+    switch ((int) operand->type) {
+    case CAIRO_GL_OPERAND_TEXTURE:
+       filter = operand->texture.attributes.filter;
+       break;
+    case CAIRO_GL_OPERAND_LINEAR_GRADIENT:
+    case CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0:
+    case CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE:
+    case CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT:
+       filter = CAIRO_FILTER_BILINEAR;
+       break;
+    default:
+       filter = CAIRO_FILTER_DEFAULT;
+       break;
+    }
+
+    return filter;
+}
+
+GLint
+_cairo_gl_operand_get_gl_filter (cairo_gl_operand_t *operand)
+{
+    cairo_filter_t filter = _cairo_gl_operand_get_filter (operand);
+
+    return filter != CAIRO_FILTER_FAST && filter != CAIRO_FILTER_NEAREST ?
+          GL_LINEAR :
+          GL_NEAREST;
+}
+
+cairo_extend_t
+_cairo_gl_operand_get_extend (cairo_gl_operand_t *operand)
+{
+    cairo_extend_t extend;
+
+    switch ((int) operand->type) {
+    case CAIRO_GL_OPERAND_TEXTURE:
+       extend = operand->texture.attributes.extend;
+       break;
+    case CAIRO_GL_OPERAND_LINEAR_GRADIENT:
+    case CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0:
+    case CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE:
+    case CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT:
+       extend = operand->gradient.extend;
+       break;
+    default:
+       extend = CAIRO_EXTEND_NONE;
+       break;
+    }
+
+    return extend;
+}
+
+
+void
+_cairo_gl_operand_bind_to_shader (cairo_gl_context_t *ctx,
+                                  cairo_gl_operand_t *operand,
+                                  cairo_gl_tex_t      tex_unit)
+{
+    char uniform_name[50];
+    char *custom_part;
+    static const char *names[] = { "source", "mask" };
+
+    strcpy (uniform_name, names[tex_unit]);
+    custom_part = uniform_name + strlen (names[tex_unit]);
+
+    switch (operand->type) {
+    default:
+    case CAIRO_GL_OPERAND_COUNT:
+        ASSERT_NOT_REACHED;
+    case CAIRO_GL_OPERAND_NONE:
+        break;
+    case CAIRO_GL_OPERAND_CONSTANT:
+        strcpy (custom_part, "_constant");
+       _cairo_gl_shader_bind_vec4 (ctx,
+                                    uniform_name,
+                                    operand->constant.color[0],
+                                    operand->constant.color[1],
+                                    operand->constant.color[2],
+                                    operand->constant.color[3]);
+        break;
+    case CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE:
+    case CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT:
+       strcpy (custom_part, "_a");
+       _cairo_gl_shader_bind_float  (ctx,
+                                     uniform_name,
+                                     operand->gradient.a);
+       /* fall through */
+    case CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0:
+       strcpy (custom_part, "_circle_d");
+       _cairo_gl_shader_bind_vec3   (ctx,
+                                     uniform_name,
+                                     operand->gradient.circle_d.center.x,
+                                     operand->gradient.circle_d.center.y,
+                                     operand->gradient.circle_d.radius);
+       strcpy (custom_part, "_radius_0");
+       _cairo_gl_shader_bind_float  (ctx,
+                                     uniform_name,
+                                     operand->gradient.radius_0);
+        /* fall through */
+    case CAIRO_GL_OPERAND_LINEAR_GRADIENT:
+    case CAIRO_GL_OPERAND_TEXTURE:
+       /*
+        * For GLES2 we use shaders to implement GL_CLAMP_TO_BORDER (used
+        * with CAIRO_EXTEND_NONE). When bilinear filtering is enabled,
+        * these shaders need the texture dimensions for their calculations.
+        */
+       if (ctx->gl_flavor == CAIRO_GL_FLAVOR_ES &&
+           _cairo_gl_operand_get_extend (operand) == CAIRO_EXTEND_NONE &&
+           _cairo_gl_operand_get_gl_filter (operand) == GL_LINEAR)
+       {
+           float width, height;
+           if (operand->type == CAIRO_GL_OPERAND_TEXTURE) {
+               width = operand->texture.surface->width;
+               height = operand->texture.surface->height;
+           }
+           else {
+               width = operand->gradient.gradient->cache_entry.size,
+               height = 1;
+           }
+           strcpy (custom_part, "_texdims");
+           _cairo_gl_shader_bind_vec2 (ctx, uniform_name, width, height);
+       }
+        break;
+    }
+}
+
+
+cairo_bool_t
+_cairo_gl_operand_needs_setup (cairo_gl_operand_t *dest,
+                               cairo_gl_operand_t *source,
+                               unsigned int        vertex_offset)
+{
+    if (dest->type != source->type)
+        return TRUE;
+    if (dest->vertex_offset != vertex_offset)
+        return TRUE;
+
+    switch (source->type) {
+    case CAIRO_GL_OPERAND_NONE:
+        return FALSE;
+    case CAIRO_GL_OPERAND_CONSTANT:
+        return dest->constant.color[0] != source->constant.color[0] ||
+               dest->constant.color[1] != source->constant.color[1] ||
+               dest->constant.color[2] != source->constant.color[2] ||
+               dest->constant.color[3] != source->constant.color[3];
+    case CAIRO_GL_OPERAND_TEXTURE:
+        return dest->texture.surface != source->texture.surface ||
+               dest->texture.attributes.extend != source->texture.attributes.extend ||
+               dest->texture.attributes.filter != source->texture.attributes.filter ||
+               dest->texture.attributes.has_component_alpha != source->texture.attributes.has_component_alpha;
+    case CAIRO_GL_OPERAND_LINEAR_GRADIENT:
+    case CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0:
+    case CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE:
+    case CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT:
+        /* XXX: improve this */
+        return TRUE;
+    default:
+    case CAIRO_GL_OPERAND_COUNT:
+        ASSERT_NOT_REACHED;
+        break;
+    }
+    return TRUE;
+}
+
+unsigned int
+_cairo_gl_operand_get_vertex_size (cairo_gl_operand_type_t type)
+{
+    switch (type) {
+    default:
+    case CAIRO_GL_OPERAND_COUNT:
+        ASSERT_NOT_REACHED;
+    case CAIRO_GL_OPERAND_NONE:
+    case CAIRO_GL_OPERAND_CONSTANT:
+        return 0;
+    case CAIRO_GL_OPERAND_TEXTURE:
+    case CAIRO_GL_OPERAND_LINEAR_GRADIENT:
+    case CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0:
+    case CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE:
+    case CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT:
+        return 2 * sizeof (GLfloat);
+    }
+}
+
+void
+_cairo_gl_operand_emit (cairo_gl_operand_t *operand,
+                        GLfloat ** vb,
+                        GLfloat x,
+                        GLfloat y)
+{
+    switch (operand->type) {
+    default:
+    case CAIRO_GL_OPERAND_COUNT:
+        ASSERT_NOT_REACHED;
+    case CAIRO_GL_OPERAND_NONE:
+    case CAIRO_GL_OPERAND_CONSTANT:
+        break;
+    case CAIRO_GL_OPERAND_LINEAR_GRADIENT:
+    case CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0:
+    case CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE:
+    case CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT:
+        {
+           double s = x;
+           double t = y;
+
+           cairo_matrix_transform_point (&operand->gradient.m, &s, &t);
+
+           *(*vb)++ = s;
+           *(*vb)++ = t;
+        }
+       break;
+    case CAIRO_GL_OPERAND_TEXTURE:
+        {
+            cairo_surface_attributes_t *src_attributes = &operand->texture.attributes;
+            double s = x;
+            double t = y;
+
+            cairo_matrix_transform_point (&src_attributes->matrix, &s, &t);
+            *(*vb)++ = s;
+            *(*vb)++ = t;
+        }
+        break;
+    }
+}
diff --git a/src/cairo-gl-private.h b/src/cairo-gl-private.h
new file mode 100644 (file)
index 0000000..9027178
--- /dev/null
@@ -0,0 +1,679 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Eric Anholt
+ * Copyright © 2009 Chris Wilson
+ * Copyright © 2005,2010 Red Hat, Inc
+ * Copyright © 2011 Linaro Limited
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ *     Benjamin Otte <otte@gnome.org>
+ *     Carl Worth <cworth@cworth.org>
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ *     Eric Anholt <eric@anholt.net>
+ *     T. Zachary Laine <whatwasthataddress@gmail.com>
+ *     Alexandros Frantzis <alexandros.frantzis@linaro.org>
+ */
+
+#ifndef CAIRO_GL_PRIVATE_H
+#define CAIRO_GL_PRIVATE_H
+
+#define GL_GLEXT_PROTOTYPES
+
+#include "cairoint.h"
+
+#include "cairo-gl.h"
+#include "cairo-gl-gradient-private.h"
+
+#include "cairo-device-private.h"
+#include "cairo-error-private.h"
+#include "cairo-rtree-private.h"
+#include "cairo-scaled-font-private.h"
+#include "cairo-spans-compositor-private.h"
+
+#include <assert.h>
+
+#if CAIRO_HAS_GL_SURFACE
+#include <GL/gl.h>
+#include <GL/glext.h>
+#elif CAIRO_HAS_GLESV2_SURFACE
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#endif
+
+#include "cairo-gl-ext-def-private.h"
+
+#define DEBUG_GL 0
+
+#if DEBUG_GL && __GNUC__
+#define UNSUPPORTED(reason) ({ \
+    fprintf (stderr, \
+            "cairo-gl: hit unsupported operation in %s(), line %d: %s\n", \
+            __FUNCTION__, __LINE__, reason); \
+    CAIRO_INT_STATUS_UNSUPPORTED; \
+})
+#else
+#define UNSUPPORTED(reason) CAIRO_INT_STATUS_UNSUPPORTED
+#endif
+
+#define CAIRO_GL_VERSION_ENCODE(major, minor) (        \
+         ((major) * 256)                       \
+       + ((minor) *   1))
+
+/* maximal number of shaders we keep in the cache.
+ * Random number that is hopefully big enough to not cause many cache evictions. */
+#define CAIRO_GL_MAX_SHADERS_PER_CONTEXT 64
+
+/* VBO size that we allocate, smaller size means we gotta flush more often */
+#define CAIRO_GL_VBO_SIZE 16384
+
+/* GL flavor */
+typedef enum cairo_gl_flavor {
+    CAIRO_GL_FLAVOR_NONE = 0,
+    CAIRO_GL_FLAVOR_DESKTOP = 1,
+    CAIRO_GL_FLAVOR_ES = 2
+} cairo_gl_flavor_t;
+
+/* Indices for vertex attributes used by BindAttribLocation etc */
+enum {
+    CAIRO_GL_VERTEX_ATTRIB_INDEX = 0,
+    CAIRO_GL_COLOR_ATTRIB_INDEX  = 1,
+    CAIRO_GL_TEXCOORD0_ATTRIB_INDEX = 2,
+    CAIRO_GL_TEXCOORD1_ATTRIB_INDEX = CAIRO_GL_TEXCOORD0_ATTRIB_INDEX + 1
+};
+
+typedef struct _cairo_gl_surface {
+    cairo_surface_t base;
+
+    int width, height;
+
+    GLuint tex; /* GL texture object containing our data. */
+    GLuint fb; /* GL framebuffer object wrapping our data. */
+    GLuint depth; /* GL framebuffer object holding depth */
+    int owns_tex;
+    cairo_bool_t needs_update;
+} cairo_gl_surface_t;
+
+typedef struct cairo_gl_glyph_cache {
+    cairo_rtree_t rtree;
+    cairo_surface_pattern_t pattern;
+} cairo_gl_glyph_cache_t;
+
+typedef enum cairo_gl_tex {
+    CAIRO_GL_TEX_SOURCE = 0,
+    CAIRO_GL_TEX_MASK = 1,
+    CAIRO_GL_TEX_TEMP = 2
+} cairo_gl_tex_t;
+
+typedef enum cairo_gl_operand_type {
+    CAIRO_GL_OPERAND_NONE,
+    CAIRO_GL_OPERAND_CONSTANT,
+    CAIRO_GL_OPERAND_TEXTURE,
+    CAIRO_GL_OPERAND_LINEAR_GRADIENT,
+    CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0,
+    CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE,
+    CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT,
+
+    CAIRO_GL_OPERAND_COUNT
+} cairo_gl_operand_type_t;
+
+typedef struct cairo_gl_shader_impl cairo_gl_shader_impl_t;
+
+typedef struct cairo_gl_shader {
+    GLuint fragment_shader;
+    GLuint program;
+} cairo_gl_shader_t;
+
+typedef enum cairo_gl_shader_in {
+    CAIRO_GL_SHADER_IN_NORMAL,
+    CAIRO_GL_SHADER_IN_CA_SOURCE,
+    CAIRO_GL_SHADER_IN_CA_SOURCE_ALPHA,
+
+    CAIRO_GL_SHADER_IN_COUNT
+} cairo_gl_shader_in_t;
+
+typedef enum cairo_gl_var_type {
+  CAIRO_GL_VAR_NONE,
+  CAIRO_GL_VAR_TEXCOORDS,
+} cairo_gl_var_type_t;
+
+#define cairo_gl_var_type_hash(src,mask,spans,dest) ((spans) << 3) | ((mask) << 2 | (src << 1) | (dest))
+#define CAIRO_GL_VAR_TYPE_MAX ((CAIRO_GL_VAR_TEXCOORDS << 3) | (CAIRO_GL_VAR_TEXCOORDS << 2) | (CAIRO_GL_VAR_TEXCOORDS << 1) | CAIRO_GL_VAR_TEXCOORDS)
+
+/* This union structure describes a potential source or mask operand to the
+ * compositing equation.
+ */
+typedef struct cairo_gl_operand {
+    cairo_gl_operand_type_t type;
+    union {
+       struct {
+           GLuint tex;
+           cairo_gl_surface_t *surface;
+           cairo_surface_attributes_t attributes;
+       } texture;
+       struct {
+           GLfloat color[4];
+       } constant;
+       struct {
+           cairo_gl_gradient_t *gradient;
+           cairo_matrix_t m;
+           cairo_circle_double_t circle_d;
+           double radius_0, a;
+           cairo_extend_t extend;
+       } gradient;
+    };
+    unsigned int vertex_offset;
+} cairo_gl_operand_t;
+
+typedef void (*cairo_gl_generic_func_t)(void);
+typedef cairo_gl_generic_func_t (*cairo_gl_get_proc_addr_func_t)(const char *procname);
+
+typedef struct _cairo_gl_dispatch {
+    /* Buffers */
+    void (*GenBuffers) (GLsizei n, GLuint *buffers);
+    void (*BindBuffer) (GLenum target, GLuint buffer);
+    void (*BufferData) (GLenum target, GLsizeiptr size,
+                         const GLvoid* data, GLenum usage);
+    GLvoid *(*MapBuffer) (GLenum target, GLenum access);
+    GLboolean (*UnmapBuffer) (GLenum target);
+
+    /* Shaders */
+    GLuint (*CreateShader) (GLenum type);
+    void (*ShaderSource) (GLuint shader, GLsizei count,
+                           const GLchar** string, const GLint* length);
+    void (*CompileShader) (GLuint shader);
+    void (*GetShaderiv) (GLuint shader, GLenum pname, GLint *params);
+    void (*GetShaderInfoLog) (GLuint shader, GLsizei bufSize,
+                               GLsizei *length, GLchar *infoLog);
+    void (*DeleteShader) (GLuint shader);
+
+    /* Programs */
+    GLuint (*CreateProgram) (void);
+    void (*AttachShader) (GLuint program, GLuint shader);
+    void (*DeleteProgram) (GLuint program);
+    void (*LinkProgram) (GLuint program);
+    void (*UseProgram) (GLuint program);
+    void (*GetProgramiv) (GLuint program, GLenum pname, GLint *params);
+    void (*GetProgramInfoLog) (GLuint program, GLsizei bufSize,
+                                GLsizei *length, GLchar *infoLog);
+
+    /* Uniforms */
+    GLint (*GetUniformLocation) (GLuint program, const GLchar* name);
+    void (*Uniform1f) (GLint location, GLfloat x);
+    void (*Uniform2f) (GLint location, GLfloat x, GLfloat y);
+    void (*Uniform3f) (GLint location, GLfloat x, GLfloat y, GLfloat z);
+    void (*Uniform4f) (GLint location, GLfloat x, GLfloat y, GLfloat z,
+                        GLfloat w);
+    void (*UniformMatrix3fv) (GLint location, GLsizei count,
+                               GLboolean transpose, const GLfloat *value);
+    void (*UniformMatrix4fv) (GLint location, GLsizei count,
+                             GLboolean transpose, const GLfloat *value);
+    void (*Uniform1i) (GLint location, GLint x);
+
+    /* Attributes */
+    void (*BindAttribLocation) (GLuint program, GLuint index,
+                               const GLchar *name);
+    void (*VertexAttribPointer) (GLuint index, GLint size, GLenum type,
+                                GLboolean normalized, GLsizei stride,
+                                const GLvoid *pointer);
+    void (*EnableVertexAttribArray) (GLuint index);
+    void (*DisableVertexAttribArray) (GLuint index);
+
+    /* Framebuffer objects */
+    void (*GenFramebuffers) (GLsizei n, GLuint* framebuffers);
+    void (*BindFramebuffer) (GLenum target, GLuint framebuffer);
+    void (*FramebufferTexture2D) (GLenum target, GLenum attachment,
+                                   GLenum textarget, GLuint texture,
+                                   GLint level);
+    GLenum (*CheckFramebufferStatus) (GLenum target);
+    void (*DeleteFramebuffers) (GLsizei n, const GLuint* framebuffers);
+} cairo_gl_dispatch_t;
+
+struct _cairo_gl_context {
+    cairo_device_t base;
+
+    const cairo_compositor_t *compositor;
+
+    GLuint texture_load_pbo;
+    GLuint vbo;
+    GLint max_framebuffer_size;
+    GLint max_texture_size;
+    GLint max_textures;
+    GLenum tex_target;
+
+    const cairo_gl_shader_impl_t *shader_impl;
+
+    GLuint vertex_shaders[CAIRO_GL_VAR_TYPE_MAX + 1];
+    cairo_gl_shader_t fill_rectangles_shader;
+    cairo_cache_t shaders;
+
+    cairo_cache_t gradients;
+
+    cairo_gl_glyph_cache_t glyph_cache[2];
+    cairo_list_t fonts;
+
+    cairo_gl_surface_t *current_target;
+    cairo_operator_t current_operator;
+    cairo_gl_shader_t *pre_shader; /* for component alpha */
+    cairo_gl_shader_t *current_shader;
+
+    cairo_gl_operand_t operands[2];
+    cairo_bool_t spans;
+
+    char *vb;
+    char *vb_mem;
+    unsigned int vb_offset;
+    unsigned int vertex_size;
+    cairo_region_t *clip_region;
+
+    cairo_bool_t has_mesa_pack_invert;
+    cairo_gl_dispatch_t dispatch;
+    GLfloat modelviewprojection_matrix[16];
+    cairo_gl_flavor_t gl_flavor;
+    cairo_bool_t has_map_buffer;
+
+    void (*acquire) (void *ctx);
+    void (*release) (void *ctx);
+
+    void (*make_current) (void *ctx, cairo_gl_surface_t *surface);
+    void (*swap_buffers)(void *ctx, cairo_gl_surface_t *surface);
+    void (*destroy) (void *ctx);
+};
+
+typedef struct _cairo_gl_composite {
+    cairo_gl_surface_t *dst;
+    cairo_operator_t op;
+    cairo_region_t *clip_region;
+
+    cairo_gl_operand_t src;
+    cairo_gl_operand_t mask;
+    cairo_bool_t spans;
+} cairo_gl_composite_t;
+
+typedef struct _cairo_gl_font {
+    cairo_scaled_font_private_t                base;
+    cairo_device_t                     *device;
+    cairo_list_t                       link;
+} cairo_gl_font_t;
+
+cairo_private extern const cairo_surface_backend_t _cairo_gl_surface_backend;
+
+static cairo_always_inline GLenum
+_cairo_gl_get_error (void)
+{
+    GLenum err = glGetError();
+
+    if (unlikely (err))
+        while (glGetError ());
+
+    return err;
+}
+
+static inline cairo_device_t *
+_cairo_gl_context_create_in_error (cairo_status_t status)
+{
+    return (cairo_device_t *) _cairo_device_create_in_error (status);
+}
+
+cairo_private cairo_status_t
+_cairo_gl_context_init (cairo_gl_context_t *ctx);
+
+cairo_private void
+_cairo_gl_surface_init (cairo_device_t *device,
+                       cairo_gl_surface_t *surface,
+                       cairo_content_t content,
+                       int width, int height);
+
+static cairo_always_inline cairo_bool_t cairo_warn
+_cairo_gl_surface_is_texture (cairo_gl_surface_t *surface)
+{
+    return surface->tex != 0;
+}
+
+cairo_private cairo_status_t
+_cairo_gl_surface_draw_image (cairo_gl_surface_t *dst,
+                             cairo_image_surface_t *src,
+                             int src_x, int src_y,
+                             int width, int height,
+                             int dst_x, int dst_y);
+
+static cairo_always_inline cairo_bool_t
+_cairo_gl_device_has_glsl (cairo_device_t *device)
+{
+    return ((cairo_gl_context_t *) device)->shader_impl != NULL;
+}
+
+static cairo_always_inline cairo_bool_t
+_cairo_gl_device_requires_power_of_two_textures (cairo_device_t *device)
+{
+    return ((cairo_gl_context_t *) device)->tex_target == GL_TEXTURE_RECTANGLE;
+}
+
+static cairo_always_inline cairo_status_t cairo_warn
+_cairo_gl_context_acquire (cairo_device_t *device,
+                          cairo_gl_context_t **ctx)
+{
+    cairo_status_t status;
+
+    status = cairo_device_acquire (device);
+    if (unlikely (status))
+       return status;
+
+    /* clear potential previous GL errors */
+    _cairo_gl_get_error ();
+
+    *ctx = (cairo_gl_context_t *) device;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_always_inline cairo_warn cairo_status_t
+_cairo_gl_context_release (cairo_gl_context_t *ctx, cairo_status_t status)
+{
+    GLenum err;
+
+    err = _cairo_gl_get_error ();
+
+    if (unlikely (err)) {
+        cairo_status_t new_status;
+       new_status = _cairo_error (CAIRO_STATUS_DEVICE_ERROR);
+        if (status == CAIRO_STATUS_SUCCESS)
+            status = new_status;
+    }
+
+    cairo_device_release (&(ctx)->base);
+
+    return status;
+}
+
+cairo_private void
+_cairo_gl_context_set_destination (cairo_gl_context_t *ctx, cairo_gl_surface_t *surface);
+
+cairo_private void
+_cairo_gl_context_activate (cairo_gl_context_t *ctx,
+                            cairo_gl_tex_t      tex_unit);
+
+cairo_private cairo_bool_t
+_cairo_gl_operator_is_supported (cairo_operator_t op);
+
+cairo_private cairo_status_t
+_cairo_gl_composite_init (cairo_gl_composite_t *setup,
+                          cairo_operator_t op,
+                          cairo_gl_surface_t *dst,
+                          cairo_bool_t has_component_alpha,
+                          const cairo_rectangle_int_t *rect);
+
+cairo_private void
+_cairo_gl_composite_fini (cairo_gl_composite_t *setup);
+
+cairo_private void
+_cairo_gl_composite_set_clip_region (cairo_gl_composite_t *setup,
+                                     cairo_region_t *clip_region);
+
+cairo_private cairo_int_status_t
+_cairo_gl_composite_set_source (cairo_gl_composite_t *setup,
+                               const cairo_pattern_t *pattern,
+                                int src_x, int src_y,
+                                int dst_x, int dst_y,
+                                int width, int height);
+cairo_private void
+_cairo_gl_composite_set_solid_source (cairo_gl_composite_t *setup,
+                                     const cairo_color_t *color);
+
+cairo_private void
+_cairo_gl_composite_set_source_operand (cairo_gl_composite_t *setup,
+                                       const cairo_gl_operand_t *source);
+
+cairo_private cairo_int_status_t
+_cairo_gl_composite_set_mask (cairo_gl_composite_t *setup,
+                             const cairo_pattern_t *pattern,
+                              int src_x, int src_y,
+                              int dst_x, int dst_y,
+                              int width, int height);
+
+cairo_private void
+_cairo_gl_composite_set_mask_operand (cairo_gl_composite_t *setup,
+                                     const cairo_gl_operand_t *mask);
+
+cairo_private void
+_cairo_gl_composite_set_spans (cairo_gl_composite_t *setup);
+
+cairo_private cairo_status_t
+_cairo_gl_composite_begin (cairo_gl_composite_t *setup,
+                           cairo_gl_context_t **ctx);
+
+cairo_private void
+_cairo_gl_composite_emit_rect (cairo_gl_context_t *ctx,
+                               GLfloat x1,
+                               GLfloat y1,
+                               GLfloat x2,
+                               GLfloat y2,
+                               uint8_t alpha);
+
+cairo_private void
+_cairo_gl_composite_emit_glyph (cairo_gl_context_t *ctx,
+                                GLfloat x1,
+                                GLfloat y1,
+                                GLfloat x2,
+                                GLfloat y2,
+                                GLfloat glyph_x1,
+                                GLfloat glyph_y1,
+                                GLfloat glyph_x2,
+                                GLfloat glyph_y2);
+
+cairo_private void
+_cairo_gl_composite_flush (cairo_gl_context_t *ctx);
+
+cairo_private void
+_cairo_gl_context_destroy_operand (cairo_gl_context_t *ctx,
+                                   cairo_gl_tex_t tex_unit);
+
+cairo_private cairo_bool_t
+_cairo_gl_get_image_format_and_type (cairo_gl_flavor_t flavor,
+                                    pixman_format_code_t pixman_format,
+                                    GLenum *internal_format, GLenum *format,
+                                    GLenum *type, cairo_bool_t *has_alpha,
+                                    cairo_bool_t *needs_swap);
+
+cairo_private void
+_cairo_gl_glyph_cache_init (cairo_gl_glyph_cache_t *cache);
+
+cairo_private void
+_cairo_gl_glyph_cache_fini (cairo_gl_context_t *ctx,
+                           cairo_gl_glyph_cache_t *cache);
+
+cairo_private cairo_int_status_t
+_cairo_gl_surface_show_glyphs (void                    *abstract_dst,
+                              cairo_operator_t          op,
+                              const cairo_pattern_t    *source,
+                              cairo_glyph_t            *glyphs,
+                              int                       num_glyphs,
+                              cairo_scaled_font_t      *scaled_font,
+                              const cairo_clip_t       *clip,
+                              int                      *remaining_glyphs);
+
+static inline int
+_cairo_gl_y_flip (cairo_gl_surface_t *surface, int y)
+{
+    if (surface->fb)
+       return y;
+    else
+       return (surface->height - 1) - y;
+}
+
+cairo_private cairo_status_t
+_cairo_gl_context_init_shaders (cairo_gl_context_t *ctx);
+
+cairo_private void
+_cairo_gl_context_fini_shaders (cairo_gl_context_t *ctx);
+
+static cairo_always_inline cairo_bool_t
+_cairo_gl_context_is_flushed (cairo_gl_context_t *ctx)
+{
+    return ctx->vb == NULL;
+}
+
+cairo_private cairo_status_t
+_cairo_gl_get_shader_by_type (cairo_gl_context_t *ctx,
+                              cairo_gl_operand_t *source,
+                              cairo_gl_operand_t *mask,
+                             cairo_bool_t use_coverage,
+                              cairo_gl_shader_in_t in,
+                              cairo_gl_shader_t **shader);
+
+cairo_private void
+_cairo_gl_shader_bind_float (cairo_gl_context_t *ctx,
+                            const char *name,
+                            float value);
+
+cairo_private void
+_cairo_gl_shader_bind_vec2 (cairo_gl_context_t *ctx,
+                           const char *name,
+                           float value0, float value1);
+
+cairo_private void
+_cairo_gl_shader_bind_vec3 (cairo_gl_context_t *ctx,
+                           const char *name,
+                           float value0,
+                           float value1,
+                           float value2);
+
+cairo_private void
+_cairo_gl_shader_bind_vec4 (cairo_gl_context_t *ctx,
+                           const char *name,
+                           float value0, float value1,
+                           float value2, float value3);
+
+cairo_private void
+_cairo_gl_shader_bind_matrix (cairo_gl_context_t *ctx,
+                             const char *name,
+                             cairo_matrix_t* m);
+
+cairo_private void
+_cairo_gl_shader_bind_matrix4f (cairo_gl_context_t *ctx,
+                               const char *name,
+                               GLfloat* gl_m);
+
+cairo_private void
+_cairo_gl_set_shader (cairo_gl_context_t *ctx,
+                     cairo_gl_shader_t *shader);
+
+cairo_private void
+_cairo_gl_shader_fini (cairo_gl_context_t *ctx, cairo_gl_shader_t *shader);
+
+cairo_private int
+_cairo_gl_get_version (void);
+
+cairo_private cairo_gl_flavor_t
+_cairo_gl_get_flavor (void);
+
+cairo_private cairo_bool_t
+_cairo_gl_has_extension (const char *ext);
+
+cairo_private cairo_status_t
+_cairo_gl_dispatch_init(cairo_gl_dispatch_t *dispatch,
+                       cairo_gl_get_proc_addr_func_t get_proc_addr);
+
+cairo_private cairo_int_status_t
+_cairo_gl_operand_init (cairo_gl_operand_t *operand,
+                       const cairo_pattern_t *pattern,
+                       cairo_gl_surface_t *dst,
+                       int src_x, int src_y,
+                       int dst_x, int dst_y,
+                       int width, int height);
+cairo_private void
+_cairo_gl_solid_operand_init (cairo_gl_operand_t *operand,
+                             const cairo_color_t *color);
+
+cairo_private cairo_filter_t
+_cairo_gl_operand_get_filter (cairo_gl_operand_t *operand);
+
+cairo_private GLint
+_cairo_gl_operand_get_gl_filter (cairo_gl_operand_t *operand);
+
+cairo_private cairo_extend_t
+_cairo_gl_operand_get_extend (cairo_gl_operand_t *operand);
+
+cairo_private unsigned int
+_cairo_gl_operand_get_vertex_size (cairo_gl_operand_type_t type);
+
+cairo_private cairo_bool_t
+_cairo_gl_operand_needs_setup (cairo_gl_operand_t *dest,
+                               cairo_gl_operand_t *source,
+                               unsigned int        vertex_offset);
+
+cairo_private void
+_cairo_gl_operand_bind_to_shader (cairo_gl_context_t *ctx,
+                                  cairo_gl_operand_t *operand,
+                                  cairo_gl_tex_t      tex_unit);
+
+cairo_private void
+_cairo_gl_operand_emit (cairo_gl_operand_t *operand,
+                        GLfloat ** vb,
+                        GLfloat x,
+                        GLfloat y);
+
+cairo_private void
+_cairo_gl_operand_copy (cairo_gl_operand_t *dst,
+                       const cairo_gl_operand_t *src);
+
+cairo_private void
+_cairo_gl_operand_destroy (cairo_gl_operand_t *operand);
+
+cairo_private const cairo_compositor_t *
+_cairo_gl_span_compositor_get (void);
+
+cairo_private const cairo_compositor_t *
+_cairo_gl_traps_compositor_get (void);
+
+cairo_private cairo_int_status_t
+_cairo_gl_check_composite_glyphs (const cairo_composite_rectangles_t *extents,
+                                 cairo_scaled_font_t *scaled_font,
+                                 cairo_glyph_t *glyphs,
+                                 int *num_glyphs);
+
+cairo_private cairo_int_status_t
+_cairo_gl_composite_glyphs (void                       *_dst,
+                           cairo_operator_t             op,
+                           cairo_surface_t             *_src,
+                           int                          src_x,
+                           int                          src_y,
+                           int                          dst_x,
+                           int                          dst_y,
+                           cairo_composite_glyphs_info_t *info);
+
+cairo_private cairo_surface_t *
+_cairo_gl_surface_create_scratch (cairo_gl_context_t   *ctx,
+                                 cairo_content_t       content,
+                                 int                   width,
+                                 int                   height);
+
+slim_hidden_proto (cairo_gl_surface_create);
+slim_hidden_proto (cairo_gl_surface_create_for_texture);
+
+#endif /* CAIRO_GL_PRIVATE_H */
diff --git a/src/cairo-gl-shaders.c b/src/cairo-gl-shaders.c
new file mode 100644 (file)
index 0000000..b3af6f7
--- /dev/null
@@ -0,0 +1,1132 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 T. Zachary Laine
+ * Copyright © 2010 Eric Anholt
+ * Copyright © 2010 Red Hat, Inc
+ * Copyright © 2010 Linaro Limited
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is T. Zachary Laine.
+ *
+ * Contributor(s):
+ *     Benjamin Otte <otte@gnome.org>
+ *     Eric Anholt <eric@anholt.net>
+ *     T. Zachary Laine <whatwasthataddress@gmail.com>
+ *     Alexandros Frantzis <alexandros.frantzis@linaro.org>
+ */
+
+#include "cairoint.h"
+#include "cairo-gl-private.h"
+#include "cairo-error-private.h"
+#include "cairo-output-stream-private.h"
+
+typedef struct cairo_gl_shader_impl {
+    void
+    (*compile_shader) (cairo_gl_context_t *ctx, GLuint *shader, GLenum type,
+                      const char *text);
+
+    void
+    (*link_shader) (cairo_gl_context_t *ctx, GLuint *program, GLuint vert, GLuint frag);
+
+    void
+    (*destroy_shader) (cairo_gl_context_t *ctx, GLuint shader);
+
+    void
+    (*destroy_program) (cairo_gl_context_t *ctx, GLuint program);
+
+    void
+    (*bind_float) (cairo_gl_context_t *ctx,
+                  cairo_gl_shader_t *shader,
+                  const char *name,
+                  float value);
+
+    void
+    (*bind_vec2) (cairo_gl_context_t *ctx,
+                 cairo_gl_shader_t *shader,
+                 const char *name,
+                 float value0,
+                 float value1);
+
+    void
+    (*bind_vec3) (cairo_gl_context_t *ctx,
+                 cairo_gl_shader_t *shader,
+                 const char *name,
+                 float value0,
+                 float value1,
+                 float value2);
+
+    void
+    (*bind_vec4) (cairo_gl_context_t *ctx,
+                 cairo_gl_shader_t *shader,
+                 const char *name,
+                 float value0, float value1,
+                 float value2, float value3);
+
+    void
+    (*bind_matrix) (cairo_gl_context_t *ctx,
+                   cairo_gl_shader_t *shader,
+                   const char *name,
+                   cairo_matrix_t* m);
+
+    void
+    (*bind_matrix4f) (cairo_gl_context_t *ctx,
+                     cairo_gl_shader_t *shader,
+                     const char *name,
+                     GLfloat* gl_m);
+
+    void
+    (*use) (cairo_gl_context_t *ctx,
+           cairo_gl_shader_t *shader);
+} shader_impl_t;
+
+static cairo_status_t
+_cairo_gl_shader_compile (cairo_gl_context_t *ctx,
+                         cairo_gl_shader_t *shader,
+                         cairo_gl_var_type_t src,
+                         cairo_gl_var_type_t mask,
+                         cairo_bool_t use_coverage,
+                         const char *fragment_text);
+
+/* OpenGL Core 2.0 API. */
+static void
+compile_shader_core_2_0 (cairo_gl_context_t *ctx, GLuint *shader,
+                        GLenum type, const char *text)
+{
+    const char* strings[1] = { text };
+    GLint gl_status;
+    cairo_gl_dispatch_t *dispatch = &ctx->dispatch;
+
+    *shader = dispatch->CreateShader (type);
+    dispatch->ShaderSource (*shader, 1, strings, 0);
+    dispatch->CompileShader (*shader);
+    dispatch->GetShaderiv (*shader, GL_COMPILE_STATUS, &gl_status);
+    if (gl_status == GL_FALSE) {
+        GLint log_size;
+        dispatch->GetShaderiv (*shader, GL_INFO_LOG_LENGTH, &log_size);
+        if (0 < log_size) {
+            char *log = _cairo_malloc (log_size);
+            GLint chars;
+
+            log[log_size - 1] = '\0';
+            dispatch->GetShaderInfoLog (*shader, log_size, &chars, log);
+            printf ("OpenGL shader compilation failed.  Shader:\n"
+                    "%s\n"
+                    "OpenGL compilation log:\n"
+                    "%s\n",
+                    text, log);
+
+            free (log);
+        } else {
+            printf ("OpenGL shader compilation failed.\n");
+        }
+
+       ASSERT_NOT_REACHED;
+    }
+}
+
+static void
+link_shader_core_2_0 (cairo_gl_context_t *ctx, GLuint *program,
+                     GLuint vert, GLuint frag)
+{
+    GLint gl_status;
+    cairo_gl_dispatch_t *dispatch = &ctx->dispatch;
+
+    *program = dispatch->CreateProgram ();
+    dispatch->AttachShader (*program, vert);
+    dispatch->AttachShader (*program, frag);
+
+    dispatch->BindAttribLocation (*program, CAIRO_GL_VERTEX_ATTRIB_INDEX,
+                                 "Vertex");
+    dispatch->BindAttribLocation (*program, CAIRO_GL_COLOR_ATTRIB_INDEX,
+                                 "Color");
+    dispatch->BindAttribLocation (*program, CAIRO_GL_TEXCOORD0_ATTRIB_INDEX,
+                                 "MultiTexCoord0");
+    dispatch->BindAttribLocation (*program, CAIRO_GL_TEXCOORD1_ATTRIB_INDEX,
+                                 "MultiTexCoord1");
+
+    dispatch->LinkProgram (*program);
+    dispatch->GetProgramiv (*program, GL_LINK_STATUS, &gl_status);
+    if (gl_status == GL_FALSE) {
+        GLint log_size;
+        dispatch->GetProgramiv (*program, GL_INFO_LOG_LENGTH, &log_size);
+        if (0 < log_size) {
+            char *log = _cairo_malloc (log_size);
+            GLint chars;
+
+            log[log_size - 1] = '\0';
+            dispatch->GetProgramInfoLog (*program, log_size, &chars, log);
+            printf ("OpenGL shader link failed:\n%s\n", log);
+
+            free (log);
+        } else {
+            printf ("OpenGL shader link failed.\n");
+        }
+
+       ASSERT_NOT_REACHED;
+    }
+}
+
+static void
+destroy_shader_core_2_0 (cairo_gl_context_t *ctx, GLuint shader)
+{
+    ctx->dispatch.DeleteShader (shader);
+}
+
+static void
+destroy_program_core_2_0 (cairo_gl_context_t *ctx, GLuint shader)
+{
+    ctx->dispatch.DeleteProgram (shader);
+}
+
+static void
+bind_float_core_2_0 (cairo_gl_context_t *ctx,
+                    cairo_gl_shader_t *shader,
+                    const char *name,
+                    float value)
+{
+    cairo_gl_dispatch_t *dispatch = &ctx->dispatch;
+    GLint location = dispatch->GetUniformLocation (shader->program, name);
+    assert (location != -1);
+    dispatch->Uniform1f (location, value);
+}
+
+static void
+bind_vec2_core_2_0 (cairo_gl_context_t *ctx,
+                   cairo_gl_shader_t *shader,
+                   const char *name,
+                   float value0,
+                   float value1)
+{
+    cairo_gl_dispatch_t *dispatch = &ctx->dispatch;
+    GLint location = dispatch->GetUniformLocation (shader->program, name);
+    assert (location != -1);
+    dispatch->Uniform2f (location, value0, value1);
+}
+
+static void
+bind_vec3_core_2_0 (cairo_gl_context_t *ctx,
+                   cairo_gl_shader_t *shader,
+                   const char *name,
+                   float value0,
+                   float value1,
+                   float value2)
+{
+    cairo_gl_dispatch_t *dispatch = &ctx->dispatch;
+    GLint location = dispatch->GetUniformLocation (shader->program, name);
+    assert (location != -1);
+    dispatch->Uniform3f (location, value0, value1, value2);
+}
+
+static void
+bind_vec4_core_2_0 (cairo_gl_context_t *ctx,
+                   cairo_gl_shader_t *shader,
+                   const char *name,
+                   float value0,
+                   float value1,
+                   float value2,
+                   float value3)
+{
+    cairo_gl_dispatch_t *dispatch = &ctx->dispatch;
+    GLint location = dispatch->GetUniformLocation (shader->program, name);
+    assert (location != -1);
+    dispatch->Uniform4f (location, value0, value1, value2, value3);
+}
+
+static void
+bind_matrix_core_2_0 (cairo_gl_context_t *ctx,
+                     cairo_gl_shader_t *shader,
+                     const char *name,
+                     cairo_matrix_t* m)
+{
+    cairo_gl_dispatch_t *dispatch = &ctx->dispatch;
+    GLint location = dispatch->GetUniformLocation (shader->program, name);
+    float gl_m[16] = {
+        m->xx, m->xy, m->x0,
+        m->yx, m->yy, m->y0,
+        0,     0,     1
+    };
+    assert (location != -1);
+    dispatch->UniformMatrix3fv (location, 1, GL_TRUE, gl_m);
+}
+
+static void
+bind_matrix4f_core_2_0 (cairo_gl_context_t *ctx,
+                       cairo_gl_shader_t *shader,
+                       const char *name,
+                       GLfloat* gl_m)
+{
+    cairo_gl_dispatch_t *dispatch = &ctx->dispatch;
+    GLint location = dispatch->GetUniformLocation (shader->program, name);
+    assert (location != -1);
+    dispatch->UniformMatrix4fv (location, 1, GL_FALSE, gl_m);
+}
+
+static void
+use_program_core_2_0 (cairo_gl_context_t *ctx,
+                     cairo_gl_shader_t *shader)
+{
+    if (shader)
+       ctx->dispatch.UseProgram (shader->program);
+    else
+       ctx->dispatch.UseProgram (0);
+}
+
+static const cairo_gl_shader_impl_t shader_impl_core_2_0 = {
+    compile_shader_core_2_0,
+    link_shader_core_2_0,
+    destroy_shader_core_2_0,
+    destroy_program_core_2_0,
+    bind_float_core_2_0,
+    bind_vec2_core_2_0,
+    bind_vec3_core_2_0,
+    bind_vec4_core_2_0,
+    bind_matrix_core_2_0,
+    bind_matrix4f_core_2_0,
+    use_program_core_2_0,
+};
+
+typedef struct _cairo_shader_cache_entry {
+    cairo_cache_entry_t base;
+
+    cairo_gl_operand_type_t src;
+    cairo_gl_operand_type_t mask;
+    cairo_gl_operand_type_t dest;
+    cairo_bool_t use_coverage;
+    cairo_gl_shader_in_t in;
+    GLint src_gl_filter;
+    cairo_bool_t src_border_fade;
+    GLint mask_gl_filter;
+    cairo_bool_t mask_border_fade;
+
+    cairo_gl_context_t *ctx; /* XXX: needed to destroy the program */
+    cairo_gl_shader_t shader;
+} cairo_shader_cache_entry_t;
+
+static cairo_bool_t
+_cairo_gl_shader_cache_equal_desktop (const void *key_a, const void *key_b)
+{
+    const cairo_shader_cache_entry_t *a = key_a;
+    const cairo_shader_cache_entry_t *b = key_b;
+
+    return a->src  == b->src  &&
+           a->mask == b->mask &&
+           a->dest == b->dest &&
+          a->use_coverage == b->use_coverage &&
+           a->in   == b->in;
+}
+
+/*
+ * For GLES2 we use more complicated shaders to implement missing GL
+ * features. In this case we need more parameters to uniquely identify
+ * a shader (vs _cairo_gl_shader_cache_equal_desktop()).
+ */
+static cairo_bool_t
+_cairo_gl_shader_cache_equal_gles2 (const void *key_a, const void *key_b)
+{
+    const cairo_shader_cache_entry_t *a = key_a;
+    const cairo_shader_cache_entry_t *b = key_b;
+
+    return a->src  == b->src  &&
+          a->mask == b->mask &&
+          a->dest == b->dest &&
+          a->use_coverage == b->use_coverage &&
+          a->in   == b->in   &&
+          a->src_gl_filter == b->src_gl_filter &&
+          a->src_border_fade == b->src_border_fade &&
+          a->mask_gl_filter == b->mask_gl_filter &&
+          a->mask_border_fade == b->mask_border_fade;
+}
+
+static unsigned long
+_cairo_gl_shader_cache_hash (const cairo_shader_cache_entry_t *entry)
+{
+    return (entry->src << 24) | (entry->mask << 16) | (entry->dest << 8) | (entry->in << 1) | entry->use_coverage;
+}
+
+static void
+_cairo_gl_shader_cache_destroy (void *data)
+{
+    cairo_shader_cache_entry_t *entry = data;
+
+    _cairo_gl_shader_fini (entry->ctx, &entry->shader);
+    if (entry->ctx->current_shader == &entry->shader)
+        entry->ctx->current_shader = NULL;
+    free (entry);
+}
+
+static void
+_cairo_gl_shader_init (cairo_gl_shader_t *shader)
+{
+    shader->fragment_shader = 0;
+    shader->program = 0;
+}
+
+cairo_status_t
+_cairo_gl_context_init_shaders (cairo_gl_context_t *ctx)
+{
+    static const char *fill_fs_source =
+       "#ifdef GL_ES\n"
+       "precision mediump float;\n"
+       "#endif\n"
+       "uniform vec4 color;\n"
+       "void main()\n"
+       "{\n"
+       "       gl_FragColor = color;\n"
+       "}\n";
+    cairo_status_t status;
+
+    if (_cairo_gl_get_version () >= CAIRO_GL_VERSION_ENCODE (2, 0) ||
+       (_cairo_gl_has_extension ("GL_ARB_shader_objects") &&
+        _cairo_gl_has_extension ("GL_ARB_fragment_shader") &&
+        _cairo_gl_has_extension ("GL_ARB_vertex_shader")))
+    {
+       ctx->shader_impl = &shader_impl_core_2_0;
+    }
+    else
+    {
+       ctx->shader_impl = NULL;
+       fprintf (stderr, "Error: The cairo gl backend requires shader support!\n");
+       return CAIRO_STATUS_DEVICE_ERROR;
+    }
+
+    memset (ctx->vertex_shaders, 0, sizeof (ctx->vertex_shaders));
+
+    status = _cairo_cache_init (&ctx->shaders,
+                                ctx->gl_flavor == CAIRO_GL_FLAVOR_DESKTOP ?
+                                   _cairo_gl_shader_cache_equal_desktop :
+                                   _cairo_gl_shader_cache_equal_gles2,
+                                NULL,
+                                _cairo_gl_shader_cache_destroy,
+                                CAIRO_GL_MAX_SHADERS_PER_CONTEXT);
+    if (unlikely (status))
+       return status;
+
+    _cairo_gl_shader_init (&ctx->fill_rectangles_shader);
+    status = _cairo_gl_shader_compile (ctx,
+                                      &ctx->fill_rectangles_shader,
+                                      CAIRO_GL_VAR_NONE,
+                                      CAIRO_GL_VAR_NONE,
+                                      FALSE,
+                                      fill_fs_source);
+    if (unlikely (status))
+       return status;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+void
+_cairo_gl_context_fini_shaders (cairo_gl_context_t *ctx)
+{
+    int i;
+
+    for (i = 0; i <= CAIRO_GL_VAR_TYPE_MAX; i++) {
+       if (ctx->vertex_shaders[i])
+           ctx->shader_impl->destroy_shader (ctx, ctx->vertex_shaders[i]);
+    }
+
+    _cairo_cache_fini (&ctx->shaders);
+}
+
+void
+_cairo_gl_shader_fini (cairo_gl_context_t *ctx,
+                      cairo_gl_shader_t *shader)
+{
+    if (shader->fragment_shader)
+        ctx->shader_impl->destroy_shader (ctx, shader->fragment_shader);
+
+    if (shader->program)
+        ctx->shader_impl->destroy_program (ctx, shader->program);
+}
+
+static const char *operand_names[] = { "source", "mask", "dest" };
+
+static cairo_gl_var_type_t
+cairo_gl_operand_get_var_type (cairo_gl_operand_type_t type)
+{
+    switch (type) {
+    default:
+    case CAIRO_GL_OPERAND_COUNT:
+        ASSERT_NOT_REACHED;
+    case CAIRO_GL_OPERAND_NONE:
+    case CAIRO_GL_OPERAND_CONSTANT:
+        return CAIRO_GL_VAR_NONE;
+    case CAIRO_GL_OPERAND_LINEAR_GRADIENT:
+    case CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0:
+    case CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE:
+    case CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT:
+    case CAIRO_GL_OPERAND_TEXTURE:
+        return CAIRO_GL_VAR_TEXCOORDS;
+    }
+}
+
+static void
+cairo_gl_shader_emit_variable (cairo_output_stream_t *stream,
+                               cairo_gl_var_type_t type,
+                               cairo_gl_tex_t name)
+{
+    switch (type) {
+    default:
+        ASSERT_NOT_REACHED;
+    case CAIRO_GL_VAR_NONE:
+        break;
+    case CAIRO_GL_VAR_TEXCOORDS:
+        _cairo_output_stream_printf (stream,
+                                     "varying vec2 %s_texcoords;\n",
+                                     operand_names[name]);
+        break;
+    }
+}
+
+static void
+cairo_gl_shader_emit_vertex (cairo_output_stream_t *stream,
+                             cairo_gl_var_type_t type,
+                             cairo_gl_tex_t name)
+{
+    switch (type) {
+    default:
+        ASSERT_NOT_REACHED;
+    case CAIRO_GL_VAR_NONE:
+        break;
+    case CAIRO_GL_VAR_TEXCOORDS:
+        _cairo_output_stream_printf (stream,
+                                     "    %s_texcoords = MultiTexCoord%d.xy;\n",
+                                     operand_names[name], name);
+        break;
+    }
+}
+
+static void
+cairo_gl_shader_dcl_coverage (cairo_output_stream_t *stream)
+{
+    _cairo_output_stream_printf (stream, "varying float coverage;\n");
+}
+
+static void
+cairo_gl_shader_def_coverage (cairo_output_stream_t *stream)
+{
+    _cairo_output_stream_printf (stream, "    coverage = Color.a;\n");
+}
+
+static cairo_status_t
+cairo_gl_shader_get_vertex_source (cairo_gl_var_type_t src,
+                                   cairo_gl_var_type_t mask,
+                                  cairo_bool_t use_coverage,
+                                   cairo_gl_var_type_t dest,
+                                  char **out)
+{
+    cairo_output_stream_t *stream = _cairo_memory_stream_create ();
+    unsigned char *source;
+    unsigned long length;
+    cairo_status_t status;
+
+    cairo_gl_shader_emit_variable (stream, src, CAIRO_GL_TEX_SOURCE);
+    cairo_gl_shader_emit_variable (stream, mask, CAIRO_GL_TEX_MASK);
+    if (use_coverage)
+       cairo_gl_shader_dcl_coverage (stream);
+
+    _cairo_output_stream_printf (stream,
+                                "attribute vec4 Vertex;\n"
+                                "attribute vec4 Color;\n"
+                                "attribute vec4 MultiTexCoord0;\n"
+                                "attribute vec4 MultiTexCoord1;\n"
+                                "uniform mat4 ModelViewProjectionMatrix;\n"
+                                "void main()\n"
+                                "{\n"
+                                "    gl_Position = ModelViewProjectionMatrix * Vertex;\n");
+
+    cairo_gl_shader_emit_vertex (stream, src, CAIRO_GL_TEX_SOURCE);
+    cairo_gl_shader_emit_vertex (stream, mask, CAIRO_GL_TEX_MASK);
+    if (use_coverage)
+       cairo_gl_shader_def_coverage (stream);
+
+    _cairo_output_stream_write (stream,
+                               "}\n\0", 3);
+
+    status = _cairo_memory_stream_destroy (stream, &source, &length);
+    if (unlikely (status))
+       return status;
+
+    *out = (char *) source;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+/*
+ * Returns whether an operand needs a special border fade fragment shader
+ * to simulate the GL_CLAMP_TO_BORDER wrapping method that is missing in GLES2.
+ */
+static cairo_bool_t
+_cairo_gl_shader_needs_border_fade (cairo_gl_operand_t *operand)
+{
+    cairo_extend_t extend =_cairo_gl_operand_get_extend (operand);
+
+    return extend == CAIRO_EXTEND_NONE &&
+          (operand->type == CAIRO_GL_OPERAND_TEXTURE ||
+           operand->type == CAIRO_GL_OPERAND_LINEAR_GRADIENT ||
+           operand->type == CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE ||
+           operand->type == CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0);
+}
+
+static void
+cairo_gl_shader_emit_color (cairo_output_stream_t *stream,
+                            cairo_gl_context_t *ctx,
+                            cairo_gl_operand_t *op,
+                            cairo_gl_tex_t name)
+{
+    const char *namestr = operand_names[name];
+    const char *rectstr = (ctx->tex_target == GL_TEXTURE_RECTANGLE ? "Rect" : "");
+
+    switch (op->type) {
+    case CAIRO_GL_OPERAND_COUNT:
+    default:
+        ASSERT_NOT_REACHED;
+        break;
+    case CAIRO_GL_OPERAND_NONE:
+        _cairo_output_stream_printf (stream, 
+            "vec4 get_%s()\n"
+            "{\n"
+            "    return vec4 (0, 0, 0, 1);\n"
+            "}\n",
+            namestr);
+        break;
+    case CAIRO_GL_OPERAND_CONSTANT:
+        _cairo_output_stream_printf (stream, 
+            "uniform vec4 %s_constant;\n"
+            "vec4 get_%s()\n"
+            "{\n"
+            "    return %s_constant;\n"
+            "}\n",
+            namestr, namestr, namestr);
+        break;
+    case CAIRO_GL_OPERAND_TEXTURE:
+       _cairo_output_stream_printf (stream,
+            "uniform sampler2D%s %s_sampler;\n"
+            "uniform vec2 %s_texdims;\n"
+            "varying vec2 %s_texcoords;\n"
+            "vec4 get_%s()\n"
+            "{\n",
+            rectstr, namestr, namestr, namestr, namestr);
+       if (ctx->gl_flavor == CAIRO_GL_FLAVOR_ES &&
+           _cairo_gl_shader_needs_border_fade (op))
+       {
+           _cairo_output_stream_printf (stream,
+               "    vec2 border_fade = %s_border_fade (%s_texcoords, %s_texdims);\n"
+               "    vec4 texel = texture2D%s (%s_sampler, %s_texcoords);\n"
+               "    return texel * border_fade.x * border_fade.y;\n"
+               "}\n",
+               namestr, namestr, namestr, rectstr, namestr, namestr);
+       }
+       else
+       {
+           _cairo_output_stream_printf (stream,
+               "    return texture2D%s (%s_sampler, %s_texcoords);\n"
+               "}\n",
+               rectstr, namestr, namestr);
+       }
+        break;
+    case CAIRO_GL_OPERAND_LINEAR_GRADIENT:
+       _cairo_output_stream_printf (stream,
+           "varying vec2 %s_texcoords;\n"
+           "uniform vec2 %s_texdims;\n"
+           "uniform sampler2D%s %s_sampler;\n"
+           "\n"
+           "vec4 get_%s()\n"
+           "{\n",
+           namestr, namestr, rectstr, namestr, namestr);
+       if (ctx->gl_flavor == CAIRO_GL_FLAVOR_ES &&
+           _cairo_gl_shader_needs_border_fade (op))
+       {
+           _cairo_output_stream_printf (stream,
+               "    float border_fade = %s_border_fade (%s_texcoords.x, %s_texdims.x);\n"
+               "    vec4 texel = texture2D%s (%s_sampler, vec2 (%s_texcoords.x, 0.5));\n"
+               "    return texel * border_fade;\n"
+               "}\n",
+               namestr, namestr, namestr, rectstr, namestr, namestr);
+       }
+       else
+       {
+           _cairo_output_stream_printf (stream,
+               "    return texture2D%s (%s_sampler, vec2 (%s_texcoords.x, 0.5));\n"
+               "}\n",
+               rectstr, namestr, namestr);
+       }
+       break;
+    case CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0:
+       _cairo_output_stream_printf (stream,
+           "varying vec2 %s_texcoords;\n"
+           "uniform vec2 %s_texdims;\n"
+           "uniform sampler2D%s %s_sampler;\n"
+           "uniform vec3 %s_circle_d;\n"
+           "uniform float %s_radius_0;\n"
+           "\n"
+           "vec4 get_%s()\n"
+           "{\n"
+           "    vec3 pos = vec3 (%s_texcoords, %s_radius_0);\n"
+           "    \n"
+           "    float B = dot (pos, %s_circle_d);\n"
+           "    float C = dot (pos, vec3 (pos.xy, -pos.z));\n"
+           "    \n"
+           "    float t = 0.5 * C / B;\n"
+           "    float is_valid = step (-%s_radius_0, t * %s_circle_d.z);\n",
+           namestr, namestr, rectstr, namestr, namestr, namestr, namestr,
+           namestr, namestr, namestr, namestr, namestr);
+       if (ctx->gl_flavor == CAIRO_GL_FLAVOR_ES &&
+           _cairo_gl_shader_needs_border_fade (op))
+       {
+           _cairo_output_stream_printf (stream,
+               "    float border_fade = %s_border_fade (t, %s_texdims.x);\n"
+               "    vec4 texel = texture2D%s (%s_sampler, vec2 (t, 0.5));\n"
+               "    return mix (vec4 (0.0), texel * border_fade, is_valid);\n"
+               "}\n",
+               namestr, namestr, rectstr, namestr);
+       }
+       else
+       {
+           _cairo_output_stream_printf (stream,
+               "    return mix (vec4 (0.0), texture2D%s (%s_sampler, vec2(t, 0.5)), is_valid);\n"
+               "}\n",
+               rectstr, namestr);
+       }
+       break;
+    case CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE:
+       _cairo_output_stream_printf (stream,
+           "varying vec2 %s_texcoords;\n"
+           "uniform vec2 %s_texdims;\n"
+           "uniform sampler2D%s %s_sampler;\n"
+           "uniform vec3 %s_circle_d;\n"
+           "uniform float %s_a;\n"
+           "uniform float %s_radius_0;\n"
+           "\n"
+           "vec4 get_%s()\n"
+           "{\n"
+           "    vec3 pos = vec3 (%s_texcoords, %s_radius_0);\n"
+           "    \n"
+           "    float B = dot (pos, %s_circle_d);\n"
+           "    float C = dot (pos, vec3 (pos.xy, -pos.z));\n"
+           "    \n"
+           "    float det = dot (vec2 (B, %s_a), vec2 (B, -C));\n"
+           "    float sqrtdet = sqrt (abs (det));\n"
+           "    vec2 t = (B + vec2 (sqrtdet, -sqrtdet)) / %s_a;\n"
+           "    \n"
+           "    vec2 is_valid = step (vec2 (0.0), t) * step (t, vec2(1.0));\n"
+           "    float has_color = step (0., det) * max (is_valid.x, is_valid.y);\n"
+           "    \n"
+           "    float upper_t = mix (t.y, t.x, is_valid.x);\n",
+           namestr, namestr, rectstr, namestr, namestr, namestr, namestr,
+           namestr, namestr, namestr, namestr, namestr, namestr);
+       if (ctx->gl_flavor == CAIRO_GL_FLAVOR_ES &&
+           _cairo_gl_shader_needs_border_fade (op))
+       {
+           _cairo_output_stream_printf (stream,
+               "    float border_fade = %s_border_fade (upper_t, %s_texdims.x);\n"
+               "    vec4 texel = texture2D%s (%s_sampler, vec2 (upper_t, 0.5));\n"
+               "    return mix (vec4 (0.0), texel * border_fade, has_color);\n"
+               "}\n",
+               namestr, namestr, rectstr, namestr);
+       }
+       else
+       {
+           _cairo_output_stream_printf (stream,
+               "    return mix (vec4 (0.0), texture2D%s (%s_sampler, vec2 (upper_t, 0.5)), has_color);\n"
+               "}\n",
+               rectstr, namestr);
+       }
+       break;
+    case CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT:
+       _cairo_output_stream_printf (stream,
+           "varying vec2 %s_texcoords;\n"
+           "uniform sampler2D%s %s_sampler;\n"
+           "uniform vec3 %s_circle_d;\n"
+           "uniform float %s_a;\n"
+           "uniform float %s_radius_0;\n"
+           "\n"
+           "vec4 get_%s()\n"
+           "{\n"
+           "    vec3 pos = vec3 (%s_texcoords, %s_radius_0);\n"
+           "    \n"
+           "    float B = dot (pos, %s_circle_d);\n"
+           "    float C = dot (pos, vec3 (pos.xy, -pos.z));\n"
+           "    \n"
+           "    float det = dot (vec2 (B, %s_a), vec2 (B, -C));\n"
+           "    float sqrtdet = sqrt (abs (det));\n"
+           "    vec2 t = (B + vec2 (sqrtdet, -sqrtdet)) / %s_a;\n"
+           "    \n"
+           "    vec2 is_valid = step (vec2 (-%s_radius_0), t * %s_circle_d.z);\n"
+           "    float has_color = step (0., det) * max (is_valid.x, is_valid.y);\n"
+           "    \n"
+           "    float upper_t = mix (t.y, t.x, is_valid.x);\n"
+           "    return mix (vec4 (0.0), texture2D%s (%s_sampler, vec2 (upper_t, 0.5)), has_color);\n"
+           "}\n",
+           namestr, rectstr, namestr, namestr, namestr, namestr,
+           namestr, namestr, namestr, namestr, namestr,
+           namestr, namestr, namestr, rectstr, namestr);
+       break;
+    }
+}
+
+/*
+ * Emits the border fade functions used by an operand.
+ *
+ * If bilinear filtering is used, the emitted function performs a linear
+ * fade to transparency effect in the intervals [-1/2n, 1/2n] and
+ * [1 - 1/2n, 1 + 1/2n] (n: texture size).
+ *
+ * If nearest filtering is used, the emitted function just returns
+ * 0.0 for all values outside [0, 1).
+ */
+static void
+_cairo_gl_shader_emit_border_fade (cairo_output_stream_t *stream,
+                                  cairo_gl_operand_t *operand,
+                                  cairo_gl_tex_t name)
+{
+    const char *namestr = operand_names[name];
+    GLint gl_filter = _cairo_gl_operand_get_gl_filter (operand);
+
+    /* 2D version */
+    _cairo_output_stream_printf (stream,
+       "vec2 %s_border_fade (vec2 coords, vec2 dims)\n"
+       "{\n",
+       namestr);
+
+    if (gl_filter == GL_LINEAR)
+       _cairo_output_stream_printf (stream,
+           "    return clamp(-abs(dims * (coords - 0.5)) + (dims + vec2(1.0)) * 0.5, 0.0, 1.0);\n");
+    else
+       _cairo_output_stream_printf (stream,
+           "    bvec2 in_tex1 = greaterThanEqual (coords, vec2 (0.0));\n"
+           "    bvec2 in_tex2 = lessThan (coords, vec2 (1.0));\n"
+           "    return vec2 (float (all (in_tex1) && all (in_tex2)));\n");
+
+    _cairo_output_stream_printf (stream, "}\n");
+
+    /* 1D version */
+    _cairo_output_stream_printf (stream,
+       "float %s_border_fade (float x, float dim)\n"
+       "{\n",
+       namestr);
+    if (gl_filter == GL_LINEAR)
+       _cairo_output_stream_printf (stream,
+           "    return clamp(-abs(dim * (x - 0.5)) + (dim + 1.0) * 0.5, 0.0, 1.0);\n");
+    else
+       _cairo_output_stream_printf (stream,
+           "    bool in_tex = x >= 0.0 && x < 1.0;\n"
+           "    return float (in_tex);\n");
+
+    _cairo_output_stream_printf (stream, "}\n");
+}
+
+static cairo_status_t
+cairo_gl_shader_get_fragment_source (cairo_gl_context_t *ctx,
+                                     cairo_gl_shader_in_t in,
+                                     cairo_gl_operand_t *src,
+                                     cairo_gl_operand_t *mask,
+                                    cairo_bool_t use_coverage,
+                                     cairo_gl_operand_type_t dest_type,
+                                    char **out)
+{
+    cairo_output_stream_t *stream = _cairo_memory_stream_create ();
+    unsigned char *source;
+    unsigned long length;
+    cairo_status_t status;
+    const char *coverage_str;
+
+    _cairo_output_stream_printf (stream,
+       "#ifdef GL_ES\n"
+       "precision mediump float;\n"
+       "#endif\n");
+
+    if (ctx->gl_flavor == CAIRO_GL_FLAVOR_ES) {
+       if (_cairo_gl_shader_needs_border_fade (src))
+           _cairo_gl_shader_emit_border_fade (stream, src, CAIRO_GL_TEX_SOURCE);
+       if (_cairo_gl_shader_needs_border_fade (mask))
+           _cairo_gl_shader_emit_border_fade (stream, mask, CAIRO_GL_TEX_MASK);
+    }
+
+    cairo_gl_shader_emit_color (stream, ctx, src, CAIRO_GL_TEX_SOURCE);
+    cairo_gl_shader_emit_color (stream, ctx, mask, CAIRO_GL_TEX_MASK);
+
+    coverage_str = "";
+    if (use_coverage) {
+       _cairo_output_stream_printf (stream, "varying float coverage;\n");
+       coverage_str = " * coverage";
+    }
+
+    _cairo_output_stream_printf (stream,
+        "void main()\n"
+        "{\n");
+    switch (in) {
+    case CAIRO_GL_SHADER_IN_COUNT:
+    default:
+        ASSERT_NOT_REACHED;
+    case CAIRO_GL_SHADER_IN_NORMAL:
+        _cairo_output_stream_printf (stream,
+            "    gl_FragColor = get_source() * get_mask().a%s;\n",
+           coverage_str);
+        break;
+    case CAIRO_GL_SHADER_IN_CA_SOURCE:
+        _cairo_output_stream_printf (stream,
+            "    gl_FragColor = get_source() * get_mask()%s;\n",
+           coverage_str);
+        break;
+    case CAIRO_GL_SHADER_IN_CA_SOURCE_ALPHA:
+        _cairo_output_stream_printf (stream,
+            "    gl_FragColor = get_source().a * get_mask()%s;\n",
+           coverage_str);
+        break;
+    }
+
+    _cairo_output_stream_write (stream,
+                                "}\n\0", 3);
+
+    status = _cairo_memory_stream_destroy (stream, &source, &length);
+    if (unlikely (status))
+        return status;
+
+    *out = (char *) source;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_gl_shader_compile (cairo_gl_context_t *ctx,
+                         cairo_gl_shader_t *shader,
+                         cairo_gl_var_type_t src,
+                         cairo_gl_var_type_t mask,
+                         cairo_bool_t use_coverage,
+                         const char *fragment_text)
+{
+    unsigned int vertex_shader;
+    cairo_status_t status;
+
+    assert (shader->program == 0);
+
+    vertex_shader = cairo_gl_var_type_hash (src, mask, use_coverage,
+                                           CAIRO_GL_VAR_NONE);
+    if (ctx->vertex_shaders[vertex_shader] == 0) {
+       char *source;
+
+       status = cairo_gl_shader_get_vertex_source (src,
+                                                   mask,
+                                                   use_coverage,
+                                                   CAIRO_GL_VAR_NONE,
+                                                   &source);
+        if (unlikely (status))
+            goto FAILURE;
+
+       ctx->shader_impl->compile_shader (ctx, &ctx->vertex_shaders[vertex_shader],
+                                         GL_VERTEX_SHADER,
+                                         source);
+        free (source);
+    }
+
+    ctx->shader_impl->compile_shader (ctx, &shader->fragment_shader,
+                                     GL_FRAGMENT_SHADER,
+                                     fragment_text);
+
+    ctx->shader_impl->link_shader (ctx, &shader->program,
+                                  ctx->vertex_shaders[vertex_shader],
+                                  shader->fragment_shader);
+
+    return CAIRO_STATUS_SUCCESS;
+
+ FAILURE:
+    _cairo_gl_shader_fini (ctx, shader);
+    shader->fragment_shader = 0;
+    shader->program = 0;
+
+    return status;
+}
+
+/* We always bind the source to texture unit 0 if present, and mask to
+ * texture unit 1 if present, so we can just initialize these once at
+ * compile time.
+ */
+static void
+_cairo_gl_shader_set_samplers (cairo_gl_context_t *ctx,
+                              cairo_gl_shader_t *shader)
+{
+    cairo_gl_dispatch_t *dispatch = &ctx->dispatch;
+    GLint location;
+    GLint saved_program;
+
+    /* We have to save/restore the current program because we might be
+     * asked for a different program while a shader is bound.  This shouldn't
+     * be a performance issue, since this is only called once per compile.
+     */
+    glGetIntegerv (GL_CURRENT_PROGRAM, &saved_program);
+    dispatch->UseProgram (shader->program);
+
+    location = dispatch->GetUniformLocation (shader->program, "source_sampler");
+    if (location != -1) {
+       dispatch->Uniform1i (location, CAIRO_GL_TEX_SOURCE);
+    }
+
+    location = dispatch->GetUniformLocation (shader->program, "mask_sampler");
+    if (location != -1) {
+       dispatch->Uniform1i (location, CAIRO_GL_TEX_MASK);
+    }
+
+    dispatch->UseProgram (saved_program);
+}
+
+void
+_cairo_gl_shader_bind_float (cairo_gl_context_t *ctx,
+                            const char *name,
+                            float value)
+{
+    ctx->shader_impl->bind_float (ctx, ctx->current_shader, name, value);
+}
+
+void
+_cairo_gl_shader_bind_vec2 (cairo_gl_context_t *ctx,
+                           const char *name,
+                           float value0,
+                           float value1)
+{
+    ctx->shader_impl->bind_vec2 (ctx, ctx->current_shader, name, value0, value1);
+}
+
+void
+_cairo_gl_shader_bind_vec3 (cairo_gl_context_t *ctx,
+                           const char *name,
+                           float value0,
+                           float value1,
+                           float value2)
+{
+    ctx->shader_impl->bind_vec3 (ctx, ctx->current_shader, name, value0, value1, value2);
+}
+
+void
+_cairo_gl_shader_bind_vec4 (cairo_gl_context_t *ctx,
+                           const char *name,
+                           float value0, float value1,
+                           float value2, float value3)
+{
+    ctx->shader_impl->bind_vec4 (ctx, ctx->current_shader, name, value0, value1, value2, value3);
+}
+
+void
+_cairo_gl_shader_bind_matrix (cairo_gl_context_t *ctx,
+                             const char *name, cairo_matrix_t* m)
+{
+    ctx->shader_impl->bind_matrix (ctx, ctx->current_shader, name, m);
+}
+
+void
+_cairo_gl_shader_bind_matrix4f (cairo_gl_context_t *ctx,
+                               const char *name, GLfloat* gl_m)
+{
+    ctx->shader_impl->bind_matrix4f (ctx, ctx->current_shader, name, gl_m);
+}
+
+void
+_cairo_gl_set_shader (cairo_gl_context_t *ctx,
+                     cairo_gl_shader_t *shader)
+{
+    if (ctx->current_shader == shader)
+        return;
+
+    ctx->shader_impl->use (ctx, shader);
+
+    ctx->current_shader = shader;
+}
+
+cairo_status_t
+_cairo_gl_get_shader_by_type (cairo_gl_context_t *ctx,
+                              cairo_gl_operand_t *source,
+                              cairo_gl_operand_t *mask,
+                             cairo_bool_t use_coverage,
+                              cairo_gl_shader_in_t in,
+                              cairo_gl_shader_t **shader)
+{
+    cairo_shader_cache_entry_t lookup, *entry;
+    char *fs_source;
+    cairo_status_t status;
+
+    lookup.src = source->type;
+    lookup.mask = mask->type;
+    lookup.dest = CAIRO_GL_OPERAND_NONE;
+    lookup.use_coverage = use_coverage;
+    lookup.in = in;
+    lookup.src_gl_filter = _cairo_gl_operand_get_gl_filter (source);
+    lookup.src_border_fade = _cairo_gl_shader_needs_border_fade (source);
+    lookup.mask_gl_filter = _cairo_gl_operand_get_gl_filter (mask);
+    lookup.mask_border_fade = _cairo_gl_shader_needs_border_fade (mask);
+    lookup.base.hash = _cairo_gl_shader_cache_hash (&lookup);
+    lookup.base.size = 1;
+
+    entry = _cairo_cache_lookup (&ctx->shaders, &lookup.base);
+    if (entry) {
+        assert (entry->shader.program);
+        *shader = &entry->shader;
+       return CAIRO_STATUS_SUCCESS;
+    }
+
+    status = cairo_gl_shader_get_fragment_source (ctx,
+                                                 in,
+                                                 source,
+                                                 mask,
+                                                 use_coverage,
+                                                 CAIRO_GL_OPERAND_NONE,
+                                                 &fs_source);
+    if (unlikely (status))
+       return status;
+
+    entry = malloc (sizeof (cairo_shader_cache_entry_t));
+    if (unlikely (entry == NULL)) {
+        free (fs_source);
+        return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+    }
+
+    memcpy (entry, &lookup, sizeof (cairo_shader_cache_entry_t));
+
+    entry->ctx = ctx;
+    _cairo_gl_shader_init (&entry->shader);
+    status = _cairo_gl_shader_compile (ctx,
+                                      &entry->shader,
+                                      cairo_gl_operand_get_var_type (source->type),
+                                      cairo_gl_operand_get_var_type (mask->type),
+                                      use_coverage,
+                                      fs_source);
+    free (fs_source);
+
+    if (unlikely (status)) {
+       free (entry);
+       return status;
+    }
+
+    _cairo_gl_shader_set_samplers (ctx, &entry->shader);
+
+    status = _cairo_cache_insert (&ctx->shaders, &entry->base);
+    if (unlikely (status)) {
+       _cairo_gl_shader_fini (ctx, &entry->shader);
+       free (entry);
+       return status;
+    }
+
+    *shader = &entry->shader;
+
+    return CAIRO_STATUS_SUCCESS;
+}
diff --git a/src/cairo-gl-spans-compositor.c b/src/cairo-gl-spans-compositor.c
new file mode 100644 (file)
index 0000000..3e52f28
--- /dev/null
@@ -0,0 +1,502 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Eric Anholt
+ * Copyright © 2009 Chris Wilson
+ * Copyright © 2005,2010 Red Hat, Inc
+ * Copyright © 2011 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ *     Benjamin Otte <otte@gnome.org>
+ *     Carl Worth <cworth@cworth.org>
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ *     Eric Anholt <eric@anholt.net>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-gl-private.h"
+
+#include "cairo-composite-rectangles-private.h"
+#include "cairo-compositor-private.h"
+#include "cairo-default-context-private.h"
+#include "cairo-error-private.h"
+#include "cairo-image-surface-private.h"
+#include "cairo-spans-compositor-private.h"
+#include "cairo-surface-backend-private.h"
+
+typedef struct _cairo_gl_span_renderer {
+    cairo_span_renderer_t base;
+
+    cairo_gl_composite_t setup;
+    double opacity;
+
+    int xmin, xmax;
+    int ymin, ymax;
+
+    cairo_gl_context_t *ctx;
+} cairo_gl_span_renderer_t;
+
+static cairo_status_t
+_cairo_gl_bounded_opaque_spans (void *abstract_renderer,
+                               int y, int height,
+                               const cairo_half_open_span_t *spans,
+                               unsigned num_spans)
+{
+    cairo_gl_span_renderer_t *r = abstract_renderer;
+
+    if (num_spans == 0)
+       return CAIRO_STATUS_SUCCESS;
+
+    do {
+       if (spans[0].coverage) {
+            _cairo_gl_composite_emit_rect (r->ctx,
+                                           spans[0].x, y,
+                                           spans[1].x, y + height,
+                                           spans[0].coverage);
+       }
+
+       spans++;
+    } while (--num_spans > 1);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_gl_bounded_spans (void *abstract_renderer,
+                        int y, int height,
+                        const cairo_half_open_span_t *spans,
+                        unsigned num_spans)
+{
+    cairo_gl_span_renderer_t *r = abstract_renderer;
+
+    if (num_spans == 0)
+       return CAIRO_STATUS_SUCCESS;
+
+    do {
+       if (spans[0].coverage) {
+            _cairo_gl_composite_emit_rect (r->ctx,
+                                           spans[0].x, y,
+                                           spans[1].x, y + height,
+                                           r->opacity * spans[0].coverage);
+       }
+
+       spans++;
+    } while (--num_spans > 1);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_gl_unbounded_spans (void *abstract_renderer,
+                          int y, int height,
+                          const cairo_half_open_span_t *spans,
+                          unsigned num_spans)
+{
+    cairo_gl_span_renderer_t *r = abstract_renderer;
+
+    if (y > r->ymin) {
+        _cairo_gl_composite_emit_rect (r->ctx,
+                                       r->xmin, r->ymin,
+                                       r->xmax, y,
+                                       0);
+    }
+
+    if (num_spans == 0) {
+        _cairo_gl_composite_emit_rect (r->ctx,
+                                       r->xmin, y,
+                                       r->xmax, y + height,
+                                       0);
+    } else {
+        if (spans[0].x != r->xmin) {
+            _cairo_gl_composite_emit_rect (r->ctx,
+                                           r->xmin, y,
+                                           spans[0].x,     y + height,
+                                           0);
+        }
+
+        do {
+            _cairo_gl_composite_emit_rect (r->ctx,
+                                           spans[0].x, y,
+                                           spans[1].x, y + height,
+                                           r->opacity * spans[0].coverage);
+            spans++;
+        } while (--num_spans > 1);
+
+        if (spans[0].x != r->xmax) {
+            _cairo_gl_composite_emit_rect (r->ctx,
+                                           spans[0].x,     y,
+                                           r->xmax, y + height,
+                                           0);
+        }
+    }
+
+    r->ymin = y + height;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+/* XXX */
+static cairo_status_t
+_cairo_gl_clipped_spans (void *abstract_renderer,
+                          int y, int height,
+                          const cairo_half_open_span_t *spans,
+                          unsigned num_spans)
+{
+    cairo_gl_span_renderer_t *r = abstract_renderer;
+
+    if (y > r->ymin) {
+        _cairo_gl_composite_emit_rect (r->ctx,
+                                       r->xmin, r->ymin,
+                                       r->xmax, y,
+                                       0);
+    }
+
+    if (num_spans == 0) {
+        _cairo_gl_composite_emit_rect (r->ctx,
+                                       r->xmin, y,
+                                       r->xmax, y + height,
+                                       0);
+    } else {
+        if (spans[0].x != r->xmin) {
+            _cairo_gl_composite_emit_rect (r->ctx,
+                                           r->xmin, y,
+                                           spans[0].x,     y + height,
+                                           0);
+        }
+
+        do {
+            _cairo_gl_composite_emit_rect (r->ctx,
+                                           spans[0].x, y,
+                                           spans[1].x, y + height,
+                                           r->opacity * spans[0].coverage);
+            spans++;
+        } while (--num_spans > 1);
+
+        if (spans[0].x != r->xmax) {
+            _cairo_gl_composite_emit_rect (r->ctx,
+                                           spans[0].x,     y,
+                                           r->xmax, y + height,
+                                           0);
+        }
+    }
+
+    r->ymin = y + height;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_gl_finish_unbounded_spans (void *abstract_renderer)
+{
+    cairo_gl_span_renderer_t *r = abstract_renderer;
+
+    if (r->ymax > r->ymin) {
+        _cairo_gl_composite_emit_rect (r->ctx,
+                                       r->xmin, r->ymin,
+                                       r->xmax, r->ymax,
+                                       0);
+    }
+
+    return _cairo_gl_context_release (r->ctx, CAIRO_STATUS_SUCCESS);
+}
+
+static cairo_status_t
+_cairo_gl_finish_bounded_spans (void *abstract_renderer)
+{
+    cairo_gl_span_renderer_t *r = abstract_renderer;
+
+    return _cairo_gl_context_release (r->ctx, CAIRO_STATUS_SUCCESS);
+}
+
+static void
+emit_aligned_boxes (cairo_gl_context_t *ctx,
+                   const cairo_boxes_t *boxes)
+{
+    const struct _cairo_boxes_chunk *chunk;
+    int i;
+
+    for (chunk = &boxes->chunks; chunk; chunk = chunk->next) {
+       for (i = 0; i < chunk->count; i++) {
+           int x1 = _cairo_fixed_integer_part (chunk->base[i].p1.x);
+           int y1 = _cairo_fixed_integer_part (chunk->base[i].p1.y);
+           int x2 = _cairo_fixed_integer_part (chunk->base[i].p2.x);
+           int y2 = _cairo_fixed_integer_part (chunk->base[i].p2.y);
+           _cairo_gl_composite_emit_rect (ctx, x1, y1, x2, y2, 0);
+       }
+    }
+}
+
+static cairo_int_status_t
+fill_boxes (void               *_dst,
+           cairo_operator_t     op,
+           const cairo_color_t *color,
+           cairo_boxes_t       *boxes)
+{
+    cairo_gl_composite_t setup;
+    cairo_gl_context_t *ctx;
+    cairo_int_status_t status;
+
+    status = _cairo_gl_composite_init (&setup, op, _dst, FALSE, NULL);
+    if (unlikely (status))
+        goto FAIL;
+
+   _cairo_gl_composite_set_solid_source (&setup, color);
+
+    status = _cairo_gl_composite_begin (&setup, &ctx);
+    if (unlikely (status))
+        goto FAIL;
+
+    emit_aligned_boxes (ctx, boxes);
+    status = _cairo_gl_context_release (ctx, CAIRO_STATUS_SUCCESS);
+
+FAIL:
+    _cairo_gl_composite_fini (&setup);
+    return status;
+}
+
+typedef struct cairo_gl_source {
+    cairo_surface_t base;
+
+    cairo_gl_operand_t operand;
+} cairo_gl_source_t;
+
+static cairo_status_t
+_cairo_gl_source_finish (void *abstract_surface)
+{
+    cairo_gl_source_t *source = abstract_surface;
+
+    _cairo_gl_operand_destroy (&source->operand);
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static const cairo_surface_backend_t cairo_gl_source_backend = {
+    CAIRO_SURFACE_TYPE_GL,
+    _cairo_gl_source_finish,
+    NULL, /* read-only wrapper */
+};
+
+static cairo_surface_t *
+pattern_to_surface (cairo_surface_t *dst,
+                   const cairo_pattern_t *pattern,
+                   cairo_bool_t is_mask,
+                   const cairo_rectangle_int_t *extents,
+                   const cairo_rectangle_int_t *sample,
+                   int *src_x, int *src_y)
+{
+    cairo_gl_source_t *source;
+    cairo_int_status_t status;
+
+    source = malloc (sizeof (*source));
+    if (unlikely (source == NULL))
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+    _cairo_surface_init (&source->base,
+                        &cairo_gl_source_backend,
+                        NULL, /* device */
+                        CAIRO_CONTENT_COLOR_ALPHA);
+
+    *src_x = *src_y = 0;
+    status = _cairo_gl_operand_init (&source->operand, pattern, (cairo_gl_surface_t *)dst,
+                                    extents->x, extents->y,
+                                    extents->x, extents->y,
+                                    extents->width, extents->height);
+    if (unlikely (status)) {
+       cairo_surface_destroy (&source->base);
+       return _cairo_surface_create_in_error (status);
+    }
+
+    return &source->base;
+}
+
+static inline cairo_gl_operand_t *
+source_to_operand (cairo_surface_t *surface)
+{
+    cairo_gl_source_t *source = (cairo_gl_source_t *)surface;
+    return source ? &source->operand : NULL;
+}
+
+static cairo_int_status_t
+composite_boxes (void                  *_dst,
+                cairo_operator_t       op,
+                cairo_surface_t        *abstract_src,
+                cairo_surface_t        *abstract_mask,
+                int                    src_x,
+                int                    src_y,
+                int                    mask_x,
+                int                    mask_y,
+                int                    dst_x,
+                int                    dst_y,
+                cairo_boxes_t          *boxes,
+                const cairo_rectangle_int_t  *extents)
+{
+    cairo_gl_composite_t setup;
+    cairo_gl_context_t *ctx;
+    cairo_int_status_t status;
+
+    status = _cairo_gl_composite_init (&setup, op, _dst, FALSE, extents);
+    if (unlikely (status))
+        goto FAIL;
+
+    _cairo_gl_composite_set_source_operand (&setup,
+                                           source_to_operand (abstract_src));
+
+    _cairo_gl_composite_set_mask_operand (&setup,
+                                         source_to_operand (abstract_mask));
+
+    status = _cairo_gl_composite_begin (&setup, &ctx);
+    if (unlikely (status))
+        goto FAIL;
+
+    emit_aligned_boxes (ctx, boxes);
+    status = _cairo_gl_context_release (ctx, CAIRO_STATUS_SUCCESS);
+
+FAIL:
+    _cairo_gl_composite_fini (&setup);
+    return status;
+}
+
+static cairo_int_status_t
+_cairo_gl_span_renderer_init (cairo_abstract_span_renderer_t   *_r,
+                             const cairo_composite_rectangles_t *composite,
+                             cairo_bool_t                       needs_clip)
+{
+    cairo_gl_span_renderer_t *r = (cairo_gl_span_renderer_t *)_r;
+    const cairo_pattern_t *source = &composite->source_pattern.base;
+    cairo_operator_t op = composite->op;
+    cairo_int_status_t status;
+
+    /* XXX earlier! */
+    if (op == CAIRO_OPERATOR_CLEAR) {
+       source = &_cairo_pattern_white.base;
+       op = CAIRO_OPERATOR_DEST_OUT;
+    } else if (composite->surface->is_clear &&
+              (op == CAIRO_OPERATOR_SOURCE ||
+               op == CAIRO_OPERATOR_OVER ||
+               op == CAIRO_OPERATOR_ADD)) {
+       op = CAIRO_OPERATOR_SOURCE;
+    } else if (op == CAIRO_OPERATOR_SOURCE) {
+       /* no lerp equivalent without some major PITA */
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+    } else if (! _cairo_gl_operator_is_supported (op))
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    status = _cairo_gl_composite_init (&r->setup,
+                                       op, (cairo_gl_surface_t *)composite->surface,
+                                       FALSE, &composite->unbounded);
+    if (unlikely (status))
+        goto FAIL;
+
+    status = _cairo_gl_composite_set_source (&r->setup, source,
+                                            composite->unbounded.x,
+                                            composite->unbounded.y,
+                                            composite->unbounded.x,
+                                            composite->unbounded.y,
+                                            composite->unbounded.width,
+                                            composite->unbounded.height);
+    if (unlikely (status))
+        goto FAIL;
+
+    r->opacity = 1.0;
+    if (composite->mask_pattern.base.type == CAIRO_PATTERN_TYPE_SOLID) {
+       r->opacity = composite->mask_pattern.solid.color.alpha;
+    } else {
+       status = _cairo_gl_composite_set_mask (&r->setup,
+                                              &composite->mask_pattern.base,
+                                              composite->unbounded.x,
+                                              composite->unbounded.y,
+                                              composite->unbounded.x,
+                                              composite->unbounded.y,
+                                              composite->unbounded.width,
+                                              composite->unbounded.height);
+       if (unlikely (status))
+           goto FAIL;
+    }
+
+    _cairo_gl_composite_set_spans (&r->setup);
+
+    status = _cairo_gl_composite_begin (&r->setup, &r->ctx);
+    if (unlikely (status))
+        goto FAIL;
+
+    if (composite->is_bounded) {
+       if (r->opacity == 1.)
+           r->base.render_rows = _cairo_gl_bounded_opaque_spans;
+       else
+           r->base.render_rows = _cairo_gl_bounded_spans;
+        r->base.finish = _cairo_gl_finish_bounded_spans;
+    } else {
+       if (needs_clip)
+           r->base.render_rows = _cairo_gl_clipped_spans;
+       else
+           r->base.render_rows = _cairo_gl_unbounded_spans;
+        r->base.finish = _cairo_gl_finish_unbounded_spans;
+       r->xmin = composite->unbounded.x;
+       r->xmax = composite->unbounded.x + composite->unbounded.width;
+       r->ymin = composite->unbounded.y;
+       r->ymax = composite->unbounded.y + composite->unbounded.height;
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+
+FAIL:
+    return status;
+}
+
+static void
+_cairo_gl_span_renderer_fini (cairo_abstract_span_renderer_t *_r,
+                             cairo_int_status_t status)
+{
+    cairo_gl_span_renderer_t *r = (cairo_gl_span_renderer_t *) _r;
+
+    if (status == CAIRO_INT_STATUS_SUCCESS)
+       r->base.finish (r);
+
+    _cairo_gl_composite_fini (&r->setup);
+}
+
+const cairo_compositor_t *
+_cairo_gl_span_compositor_get (void)
+{
+    static cairo_spans_compositor_t compositor;
+
+    if (compositor.base.delegate == NULL) {
+       /* The fallback to traps here is essentially just for glyphs... */
+       _cairo_spans_compositor_init (&compositor,
+                                     _cairo_gl_traps_compositor_get());
+
+       compositor.fill_boxes = fill_boxes;
+       //compositor.check_composite_boxes = check_composite_boxes;
+       compositor.pattern_to_surface = pattern_to_surface;
+       compositor.composite_boxes = composite_boxes;
+       //compositor.check_span_renderer = check_span_renderer;
+       compositor.renderer_init = _cairo_gl_span_renderer_init;
+       compositor.renderer_fini = _cairo_gl_span_renderer_fini;
+    }
+
+    return &compositor.base;
+}
diff --git a/src/cairo-gl-surface-legacy.c b/src/cairo-gl-surface-legacy.c
new file mode 100644 (file)
index 0000000..0ba356e
--- /dev/null
@@ -0,0 +1,601 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Eric Anholt
+ * Copyright © 2009 Chris Wilson
+ * Copyright © 2005,2010 Red Hat, Inc
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ *     Benjamin Otte <otte@gnome.org>
+ *     Carl Worth <cworth@cworth.org>
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ *     Eric Anholt <eric@anholt.net>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-composite-rectangles-private.h"
+#include "cairo-default-context-private.h"
+#include "cairo-error-private.h"
+#include "cairo-gl-private.h"
+
+cairo_status_t
+_cairo_gl_surface_acquire_dest_image (void                   *abstract_surface,
+                                     cairo_rectangle_int_t   *interest_rect,
+                                     cairo_image_surface_t  **image_out,
+                                     cairo_rectangle_int_t   *image_rect_out,
+                                     void                   **image_extra)
+{
+    cairo_gl_surface_t *surface = abstract_surface;
+    cairo_int_status_t status;
+
+    status = _cairo_gl_surface_deferred_clear (surface);
+    if (unlikely (status))
+       return status;
+
+    *image_extra = NULL;
+    return _cairo_gl_surface_get_image (surface, interest_rect, image_out,
+                                       image_rect_out);
+}
+
+void
+_cairo_gl_surface_release_dest_image (void                   *abstract_surface,
+                                     cairo_rectangle_int_t   *interest_rect,
+                                     cairo_image_surface_t   *image,
+                                     cairo_rectangle_int_t   *image_rect,
+                                     void                    *image_extra)
+{
+    cairo_status_t status;
+
+    status = _cairo_gl_surface_draw_image (abstract_surface, image,
+                                          0, 0,
+                                          image->width, image->height,
+                                          image_rect->x, image_rect->y);
+    /* as we created the image, its format should be directly applicable */
+    assert (status == CAIRO_STATUS_SUCCESS);
+
+    cairo_surface_destroy (&image->base);
+}
+
+cairo_status_t
+_cairo_gl_surface_clone_similar (void               *abstract_surface,
+                                cairo_surface_t     *src,
+                                int                  src_x,
+                                int                  src_y,
+                                int                  width,
+                                int                  height,
+                                int                 *clone_offset_x,
+                                int                 *clone_offset_y,
+                                cairo_surface_t    **clone_out)
+{
+    cairo_gl_surface_t *surface = abstract_surface;
+    cairo_int_status_t status;
+
+    /* XXX: Use GLCopyTexImage2D to clone non-texture-surfaces */
+    if (src->device == surface->base.device &&
+        _cairo_gl_surface_is_texture ((cairo_gl_surface_t *) src)) {
+       status = _cairo_gl_surface_deferred_clear ((cairo_gl_surface_t *)src);
+       if (unlikely (status))
+           return status;
+
+       *clone_offset_x = 0;
+       *clone_offset_y = 0;
+       *clone_out = cairo_surface_reference (src);
+
+       return CAIRO_STATUS_SUCCESS;
+    } else if (_cairo_surface_is_image (src)) {
+       cairo_image_surface_t *image_src = (cairo_image_surface_t *)src;
+       cairo_gl_surface_t *clone;
+
+       clone = (cairo_gl_surface_t *)
+               _cairo_gl_surface_create_similar (&surface->base,
+                                                 src->content,
+                                                 width, height);
+       if (clone == NULL)
+           return UNSUPPORTED ("create_similar failed");
+       if (clone->base.status)
+           return clone->base.status;
+
+       status = _cairo_gl_surface_draw_image (clone, image_src,
+                                              src_x, src_y,
+                                              width, height,
+                                              0, 0);
+       if (status) {
+           cairo_surface_destroy (&clone->base);
+           return status;
+       }
+
+       *clone_out = &clone->base;
+       *clone_offset_x = src_x;
+       *clone_offset_y = src_y;
+
+       return CAIRO_STATUS_SUCCESS;
+    }
+
+    return UNSUPPORTED ("unknown src surface type in clone_similar");
+}
+
+/** Creates a cairo-gl pattern surface for the given trapezoids */
+static cairo_status_t
+_cairo_gl_get_traps_pattern (cairo_gl_surface_t *dst,
+                            int dst_x, int dst_y,
+                            int width, int height,
+                            cairo_trapezoid_t *traps,
+                            int num_traps,
+                            cairo_antialias_t antialias,
+                            cairo_surface_pattern_t *pattern)
+{
+    pixman_format_code_t pixman_format;
+    pixman_image_t *image;
+    cairo_surface_t *surface;
+    int i;
+
+    pixman_format = antialias != CAIRO_ANTIALIAS_NONE ? PIXMAN_a8 : PIXMAN_a1,
+    image = pixman_image_create_bits (pixman_format, width, height, NULL, 0);
+    if (unlikely (image == NULL))
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    for (i = 0; i < num_traps; i++) {
+       pixman_trapezoid_t trap;
+
+       trap.top = _cairo_fixed_to_16_16 (traps[i].top);
+       trap.bottom = _cairo_fixed_to_16_16 (traps[i].bottom);
+
+       trap.left.p1.x = _cairo_fixed_to_16_16 (traps[i].left.p1.x);
+       trap.left.p1.y = _cairo_fixed_to_16_16 (traps[i].left.p1.y);
+       trap.left.p2.x = _cairo_fixed_to_16_16 (traps[i].left.p2.x);
+       trap.left.p2.y = _cairo_fixed_to_16_16 (traps[i].left.p2.y);
+
+       trap.right.p1.x = _cairo_fixed_to_16_16 (traps[i].right.p1.x);
+       trap.right.p1.y = _cairo_fixed_to_16_16 (traps[i].right.p1.y);
+       trap.right.p2.x = _cairo_fixed_to_16_16 (traps[i].right.p2.x);
+       trap.right.p2.y = _cairo_fixed_to_16_16 (traps[i].right.p2.y);
+
+       pixman_rasterize_trapezoid (image, &trap, -dst_x, -dst_y);
+    }
+
+    surface = _cairo_image_surface_create_for_pixman_image (image,
+                                                           pixman_format);
+    if (unlikely (surface->status)) {
+       pixman_image_unref (image);
+       return surface->status;
+    }
+
+    _cairo_pattern_init_for_surface (pattern, surface);
+    cairo_surface_destroy (surface);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_int_status_t
+_cairo_gl_surface_composite (cairo_operator_t            op,
+                            const cairo_pattern_t       *src,
+                            const cairo_pattern_t       *mask,
+                            void                        *abstract_dst,
+                            int                          src_x,
+                            int                          src_y,
+                            int                          mask_x,
+                            int                          mask_y,
+                            int                          dst_x,
+                            int                          dst_y,
+                            unsigned int                 width,
+                            unsigned int                 height,
+                            cairo_region_t              *clip_region)
+{
+    cairo_gl_surface_t *dst = abstract_dst;
+    cairo_gl_context_t *ctx;
+    cairo_status_t status;
+    cairo_gl_composite_t setup;
+    cairo_rectangle_int_t rect = { dst_x, dst_y, width, height };
+    int dx, dy;
+
+    status = _cairo_gl_surface_deferred_clear (dst);
+    if (unlikely (status))
+           return status;
+
+    if (op == CAIRO_OPERATOR_SOURCE &&
+        mask == NULL &&
+        src->type == CAIRO_PATTERN_TYPE_SURFACE &&
+        _cairo_surface_is_image (((cairo_surface_pattern_t *) src)->surface) &&
+        _cairo_matrix_is_integer_translation (&src->matrix, &dx, &dy)) {
+        cairo_image_surface_t *image = (cairo_image_surface_t *)
+            ((cairo_surface_pattern_t *) src)->surface;
+        dx += src_x;
+        dy += src_y;
+        if (dx >= 0 &&
+            dy >= 0 &&
+            dx + width <= (unsigned int) image->width &&
+            dy + height <= (unsigned int) image->height) {
+            status = _cairo_gl_surface_draw_image (dst, image,
+                                                   dx, dy,
+                                                   width, height,
+                                                   dst_x, dst_y);
+            if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+                return status;
+        }
+    }
+
+    status = _cairo_gl_composite_init (&setup, op, dst,
+                                       mask && mask->has_component_alpha,
+                                       &rect);
+    if (unlikely (status))
+        goto CLEANUP;
+
+    status = _cairo_gl_composite_set_source (&setup, src,
+                                             src_x, src_y,
+                                             dst_x, dst_y,
+                                             width, height);
+    if (unlikely (status))
+        goto CLEANUP;
+
+    status = _cairo_gl_composite_set_mask (&setup, mask,
+                                           mask_x, mask_y,
+                                           dst_x, dst_y,
+                                           width, height);
+    if (unlikely (status))
+        goto CLEANUP;
+
+    status = _cairo_gl_composite_begin (&setup, &ctx);
+    if (unlikely (status))
+       goto CLEANUP;
+
+    if (clip_region != NULL) {
+        int i, num_rectangles;
+
+        num_rectangles = cairo_region_num_rectangles (clip_region);
+
+       for (i = 0; i < num_rectangles; i++) {
+           cairo_rectangle_int_t rect;
+
+           cairo_region_get_rectangle (clip_region, i, &rect);
+            _cairo_gl_composite_emit_rect (ctx,
+                                           rect.x,              rect.y,
+                                           rect.x + rect.width, rect.y + rect.height,
+                                           0);
+       }
+    } else {
+        _cairo_gl_composite_emit_rect (ctx,
+                                       dst_x,         dst_y,
+                                       dst_x + width, dst_y + height,
+                                       0);
+    }
+
+    status = _cairo_gl_context_release (ctx, status);
+
+  CLEANUP:
+    _cairo_gl_composite_fini (&setup);
+
+    return status;
+}
+
+cairo_int_status_t
+_cairo_gl_surface_composite_trapezoids (cairo_operator_t op,
+                                       const cairo_pattern_t *pattern,
+                                       void *abstract_dst,
+                                       cairo_antialias_t antialias,
+                                       int src_x, int src_y,
+                                       int dst_x, int dst_y,
+                                       unsigned int width,
+                                       unsigned int height,
+                                       cairo_trapezoid_t *traps,
+                                       int num_traps,
+                                       cairo_region_t *clip_region)
+{
+    cairo_gl_surface_t *dst = abstract_dst;
+    cairo_surface_pattern_t traps_pattern;
+    cairo_int_status_t status;
+
+    if (! _cairo_gl_operator_is_supported (op))
+       return UNSUPPORTED ("unsupported operator");
+
+    status = _cairo_gl_surface_deferred_clear (dst);
+    if (unlikely (status))
+           return status;
+
+    status = _cairo_gl_get_traps_pattern (dst,
+                                         dst_x, dst_y, width, height,
+                                         traps, num_traps, antialias,
+                                         &traps_pattern);
+    if (unlikely (status))
+       return status;
+
+    status = _cairo_gl_surface_composite (op,
+                                         pattern, &traps_pattern.base, dst,
+                                         src_x, src_y,
+                                         0, 0,
+                                         dst_x, dst_y,
+                                         width, height,
+                                         clip_region);
+
+    _cairo_pattern_fini (&traps_pattern.base);
+
+    assert (status != CAIRO_INT_STATUS_UNSUPPORTED);
+    return status;
+}
+
+cairo_int_status_t
+_cairo_gl_surface_fill_rectangles (void                           *abstract_dst,
+                                  cairo_operator_t         op,
+                                  const cairo_color_t     *color,
+                                  cairo_rectangle_int_t   *rects,
+                                  int                      num_rects)
+{
+    cairo_gl_surface_t *dst = abstract_dst;
+    cairo_solid_pattern_t solid;
+    cairo_gl_context_t *ctx;
+    cairo_status_t status;
+    cairo_gl_composite_t setup;
+    int i;
+
+    status = _cairo_gl_surface_deferred_clear (dst);
+    if (unlikely (status))
+           return status;
+
+    status = _cairo_gl_composite_init (&setup, op, dst,
+                                       FALSE,
+                                       /* XXX */ NULL);
+    if (unlikely (status))
+        goto CLEANUP;
+
+    _cairo_pattern_init_solid (&solid, color);
+    status = _cairo_gl_composite_set_source (&setup, &solid.base,
+                                             0, 0,
+                                             0, 0,
+                                             0, 0);
+    if (unlikely (status))
+        goto CLEANUP;
+
+    status = _cairo_gl_composite_set_mask (&setup, NULL,
+                                           0, 0,
+                                           0, 0,
+                                           0, 0);
+    if (unlikely (status))
+        goto CLEANUP;
+
+    status = _cairo_gl_composite_begin (&setup, &ctx);
+    if (unlikely (status))
+        goto CLEANUP;
+
+    for (i = 0; i < num_rects; i++) {
+        _cairo_gl_composite_emit_rect (ctx,
+                                       rects[i].x,
+                                       rects[i].y,
+                                       rects[i].x + rects[i].width,
+                                       rects[i].y + rects[i].height,
+                                       0);
+    }
+
+    status = _cairo_gl_context_release (ctx, status);
+
+  CLEANUP:
+    _cairo_gl_composite_fini (&setup);
+
+    return status;
+}
+
+typedef struct _cairo_gl_surface_span_renderer {
+    cairo_span_renderer_t base;
+
+    cairo_gl_composite_t setup;
+
+    int xmin, xmax;
+    int ymin, ymax;
+
+    cairo_gl_context_t *ctx;
+} cairo_gl_surface_span_renderer_t;
+
+static cairo_status_t
+_cairo_gl_render_bounded_spans (void *abstract_renderer,
+                               int y, int height,
+                               const cairo_half_open_span_t *spans,
+                               unsigned num_spans)
+{
+    cairo_gl_surface_span_renderer_t *renderer = abstract_renderer;
+
+    if (num_spans == 0)
+       return CAIRO_STATUS_SUCCESS;
+
+    do {
+       if (spans[0].coverage) {
+            _cairo_gl_composite_emit_rect (renderer->ctx,
+                                           spans[0].x, y,
+                                           spans[1].x, y + height,
+                                           spans[0].coverage);
+       }
+
+       spans++;
+    } while (--num_spans > 1);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_gl_render_unbounded_spans (void *abstract_renderer,
+                                 int y, int height,
+                                 const cairo_half_open_span_t *spans,
+                                 unsigned num_spans)
+{
+    cairo_gl_surface_span_renderer_t *renderer = abstract_renderer;
+
+    if (y > renderer->ymin) {
+        _cairo_gl_composite_emit_rect (renderer->ctx,
+                                       renderer->xmin, renderer->ymin,
+                                       renderer->xmax, y,
+                                       0);
+    }
+
+    if (num_spans == 0) {
+        _cairo_gl_composite_emit_rect (renderer->ctx,
+                                       renderer->xmin, y,
+                                       renderer->xmax, y + height,
+                                       0);
+    } else {
+        if (spans[0].x != renderer->xmin) {
+            _cairo_gl_composite_emit_rect (renderer->ctx,
+                                           renderer->xmin, y,
+                                           spans[0].x,     y + height,
+                                           0);
+        }
+
+        do {
+            _cairo_gl_composite_emit_rect (renderer->ctx,
+                                           spans[0].x, y,
+                                           spans[1].x, y + height,
+                                           spans[0].coverage);
+            spans++;
+        } while (--num_spans > 1);
+
+        if (spans[0].x != renderer->xmax) {
+            _cairo_gl_composite_emit_rect (renderer->ctx,
+                                           spans[0].x,     y,
+                                           renderer->xmax, y + height,
+                                           0);
+        }
+    }
+
+    renderer->ymin = y + height;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_gl_finish_unbounded_spans (void *abstract_renderer)
+{
+    cairo_gl_surface_span_renderer_t *renderer = abstract_renderer;
+
+    if (renderer->ymax > renderer->ymin) {
+        _cairo_gl_composite_emit_rect (renderer->ctx,
+                                       renderer->xmin, renderer->ymin,
+                                       renderer->xmax, renderer->ymax,
+                                       0);
+    }
+
+    return _cairo_gl_context_release (renderer->ctx, CAIRO_STATUS_SUCCESS);
+}
+
+static cairo_status_t
+_cairo_gl_finish_bounded_spans (void *abstract_renderer)
+{
+    cairo_gl_surface_span_renderer_t *renderer = abstract_renderer;
+
+    return _cairo_gl_context_release (renderer->ctx, CAIRO_STATUS_SUCCESS);
+}
+
+static void
+_cairo_gl_surface_span_renderer_destroy (void *abstract_renderer)
+{
+    cairo_gl_surface_span_renderer_t *renderer = abstract_renderer;
+
+    if (!renderer)
+       return;
+
+    _cairo_gl_composite_fini (&renderer->setup);
+
+    free (renderer);
+}
+
+cairo_bool_t
+_cairo_gl_surface_check_span_renderer (cairo_operator_t               op,
+                                      const cairo_pattern_t  *pattern,
+                                      void                   *abstract_dst,
+                                      cairo_antialias_t       antialias)
+{
+    if (! _cairo_gl_operator_is_supported (op))
+       return FALSE;
+
+    return TRUE;
+
+    (void) pattern;
+    (void) abstract_dst;
+    (void) antialias;
+}
+
+cairo_span_renderer_t *
+_cairo_gl_surface_create_span_renderer (cairo_operator_t        op,
+                                       const cairo_pattern_t   *src,
+                                       void                    *abstract_dst,
+                                       cairo_antialias_t        antialias,
+                                       const cairo_composite_rectangles_t *rects)
+{
+    cairo_gl_surface_t *dst = abstract_dst;
+    cairo_gl_surface_span_renderer_t *renderer;
+    cairo_status_t status;
+    const cairo_rectangle_int_t *extents;
+
+    status = _cairo_gl_surface_deferred_clear (dst);
+    if (unlikely (status))
+       return _cairo_span_renderer_create_in_error (status);
+
+    renderer = calloc (1, sizeof (*renderer));
+    if (unlikely (renderer == NULL))
+       return _cairo_span_renderer_create_in_error (CAIRO_STATUS_NO_MEMORY);
+
+    renderer->base.destroy = _cairo_gl_surface_span_renderer_destroy;
+    if (rects->is_bounded) {
+       renderer->base.render_rows = _cairo_gl_render_bounded_spans;
+        renderer->base.finish =      _cairo_gl_finish_bounded_spans;
+       extents = &rects->bounded;
+    } else {
+       renderer->base.render_rows = _cairo_gl_render_unbounded_spans;
+        renderer->base.finish =      _cairo_gl_finish_unbounded_spans;
+       extents = &rects->unbounded;
+    }
+    renderer->xmin = extents->x;
+    renderer->xmax = extents->x + extents->width;
+    renderer->ymin = extents->y;
+    renderer->ymax = extents->y + extents->height;
+
+    status = _cairo_gl_composite_init (&renderer->setup,
+                                       op, dst,
+                                       FALSE, extents);
+    if (unlikely (status))
+        goto FAIL;
+
+    status = _cairo_gl_composite_set_source (&renderer->setup, src,
+                                             extents->x, extents->y,
+                                             extents->x, extents->y,
+                                             extents->width, extents->height);
+    if (unlikely (status))
+        goto FAIL;
+
+    _cairo_gl_composite_set_spans (&renderer->setup);
+    _cairo_gl_composite_set_clip_region (&renderer->setup,
+                                        _cairo_clip_get_region (rects->clip));
+
+    status = _cairo_gl_composite_begin (&renderer->setup, &renderer->ctx);
+    if (unlikely (status))
+        goto FAIL;
+
+    return &renderer->base;
+
+FAIL:
+    _cairo_gl_composite_fini (&renderer->setup);
+    free (renderer);
+    return _cairo_span_renderer_create_in_error (status);
+}
+
diff --git a/src/cairo-gl-surface.c b/src/cairo-gl-surface.c
new file mode 100644 (file)
index 0000000..9fcbc3e
--- /dev/null
@@ -0,0 +1,1230 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Eric Anholt
+ * Copyright © 2009 Chris Wilson
+ * Copyright © 2005,2010 Red Hat, Inc
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ *     Benjamin Otte <otte@gnome.org>
+ *     Carl Worth <cworth@cworth.org>
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ *     Eric Anholt <eric@anholt.net>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-gl-private.h"
+
+#include "cairo-composite-rectangles-private.h"
+#include "cairo-compositor-private.h"
+#include "cairo-default-context-private.h"
+#include "cairo-error-private.h"
+#include "cairo-image-surface-private.h"
+#include "cairo-surface-backend-private.h"
+
+static cairo_status_t
+_cairo_gl_surface_flush (void *abstract_surface);
+
+static cairo_bool_t _cairo_surface_is_gl (cairo_surface_t *surface)
+{
+    return surface->backend == &_cairo_gl_surface_backend;
+}
+
+static cairo_bool_t
+_cairo_gl_get_image_format_and_type_gles2 (pixman_format_code_t pixman_format,
+                                          GLenum *internal_format, GLenum *format,
+                                          GLenum *type, cairo_bool_t *has_alpha,
+                                          cairo_bool_t *needs_swap)
+{
+    cairo_bool_t is_little_endian = _cairo_is_little_endian ();
+
+    *has_alpha = TRUE;
+
+    switch ((int) pixman_format) {
+    case PIXMAN_a8r8g8b8:
+       *internal_format = GL_BGRA;
+       *format = GL_BGRA;
+       *type = GL_UNSIGNED_BYTE;
+       *needs_swap = !is_little_endian;
+       return TRUE;
+
+    case PIXMAN_x8r8g8b8:
+       *internal_format = GL_BGRA;
+       *format = GL_BGRA;
+       *type = GL_UNSIGNED_BYTE;
+       *has_alpha = FALSE;
+       *needs_swap = !is_little_endian;
+       return TRUE;
+
+    case PIXMAN_a8b8g8r8:
+       *internal_format = GL_RGBA;
+       *format = GL_RGBA;
+       *type = GL_UNSIGNED_BYTE;
+       *needs_swap = !is_little_endian;
+       return TRUE;
+
+    case PIXMAN_x8b8g8r8:
+       *internal_format = GL_RGBA;
+       *format = GL_RGBA;
+       *type = GL_UNSIGNED_BYTE;
+       *has_alpha = FALSE;
+       *needs_swap = !is_little_endian;
+       return TRUE;
+
+    case PIXMAN_b8g8r8a8:
+       *internal_format = GL_BGRA;
+       *format = GL_BGRA;
+       *type = GL_UNSIGNED_BYTE;
+       *needs_swap = is_little_endian;
+       return TRUE;
+
+    case PIXMAN_b8g8r8x8:
+       *internal_format = GL_BGRA;
+       *format = GL_BGRA;
+       *type = GL_UNSIGNED_BYTE;
+       *has_alpha = FALSE;
+       *needs_swap = is_little_endian;
+       return TRUE;
+
+    case PIXMAN_r8g8b8:
+       *internal_format = GL_RGB;
+       *format = GL_RGB;
+       *type = GL_UNSIGNED_BYTE;
+       *needs_swap = is_little_endian;
+       return TRUE;
+
+    case PIXMAN_b8g8r8:
+       *internal_format = GL_RGB;
+       *format = GL_RGB;
+       *type = GL_UNSIGNED_BYTE;
+       *needs_swap = !is_little_endian;
+       return TRUE;
+
+    case PIXMAN_r5g6b5:
+       *internal_format = GL_RGB;
+       *format = GL_RGB;
+       *type = GL_UNSIGNED_SHORT_5_6_5;
+       *needs_swap = FALSE;
+       return TRUE;
+
+    case PIXMAN_b5g6r5:
+       *internal_format = GL_RGB;
+       *format = GL_RGB;
+       *type = GL_UNSIGNED_SHORT_5_6_5;
+       *needs_swap = TRUE;
+       return TRUE;
+
+    case PIXMAN_a1b5g5r5:
+       *internal_format = GL_RGBA;
+       *format = GL_RGBA;
+       *type = GL_UNSIGNED_SHORT_5_5_5_1;
+       *needs_swap = TRUE;
+       return TRUE;
+
+    case PIXMAN_x1b5g5r5:
+       *internal_format = GL_RGBA;
+       *format = GL_RGBA;
+       *type = GL_UNSIGNED_SHORT_5_5_5_1;
+       *has_alpha = FALSE;
+       *needs_swap = TRUE;
+       return TRUE;
+
+    case PIXMAN_a8:
+       *internal_format = GL_ALPHA;
+       *format = GL_ALPHA;
+       *type = GL_UNSIGNED_BYTE;
+       *needs_swap = FALSE;
+       return TRUE;
+
+    default:
+       return FALSE;
+    }
+}
+
+static cairo_bool_t
+_cairo_gl_get_image_format_and_type_gl (pixman_format_code_t pixman_format,
+                                       GLenum *internal_format, GLenum *format,
+                                       GLenum *type, cairo_bool_t *has_alpha,
+                                       cairo_bool_t *needs_swap)
+{
+    *has_alpha = TRUE;
+    *needs_swap = FALSE;
+
+    switch (pixman_format) {
+    case PIXMAN_a8r8g8b8:
+       *internal_format = GL_RGBA;
+       *format = GL_BGRA;
+       *type = GL_UNSIGNED_INT_8_8_8_8_REV;
+       return TRUE;
+    case PIXMAN_x8r8g8b8:
+       *internal_format = GL_RGB;
+       *format = GL_BGRA;
+       *type = GL_UNSIGNED_INT_8_8_8_8_REV;
+       *has_alpha = FALSE;
+       return TRUE;
+    case PIXMAN_a8b8g8r8:
+       *internal_format = GL_RGBA;
+       *format = GL_RGBA;
+       *type = GL_UNSIGNED_INT_8_8_8_8_REV;
+       return TRUE;
+    case PIXMAN_x8b8g8r8:
+       *internal_format = GL_RGB;
+       *format = GL_RGBA;
+       *type = GL_UNSIGNED_INT_8_8_8_8_REV;
+       *has_alpha = FALSE;
+       return TRUE;
+    case PIXMAN_b8g8r8a8:
+       *internal_format = GL_RGBA;
+       *format = GL_BGRA;
+       *type = GL_UNSIGNED_INT_8_8_8_8;
+       return TRUE;
+    case PIXMAN_b8g8r8x8:
+       *internal_format = GL_RGB;
+       *format = GL_BGRA;
+       *type = GL_UNSIGNED_INT_8_8_8_8;
+       *has_alpha = FALSE;
+       return TRUE;
+    case PIXMAN_r8g8b8:
+       *internal_format = GL_RGB;
+       *format = GL_RGB;
+       *type = GL_UNSIGNED_BYTE;
+       return TRUE;
+    case PIXMAN_b8g8r8:
+       *internal_format = GL_RGB;
+       *format = GL_BGR;
+       *type = GL_UNSIGNED_BYTE;
+       return TRUE;
+    case PIXMAN_r5g6b5:
+       *internal_format = GL_RGB;
+       *format = GL_RGB;
+       *type = GL_UNSIGNED_SHORT_5_6_5;
+       return TRUE;
+    case PIXMAN_b5g6r5:
+       *internal_format = GL_RGB;
+       *format = GL_RGB;
+       *type = GL_UNSIGNED_SHORT_5_6_5_REV;
+       return TRUE;
+    case PIXMAN_a1r5g5b5:
+       *internal_format = GL_RGBA;
+       *format = GL_BGRA;
+       *type = GL_UNSIGNED_SHORT_1_5_5_5_REV;
+       return TRUE;
+    case PIXMAN_x1r5g5b5:
+       *internal_format = GL_RGB;
+       *format = GL_BGRA;
+       *type = GL_UNSIGNED_SHORT_1_5_5_5_REV;
+       *has_alpha = FALSE;
+       return TRUE;
+    case PIXMAN_a1b5g5r5:
+       *internal_format = GL_RGBA;
+       *format = GL_RGBA;
+       *type = GL_UNSIGNED_SHORT_1_5_5_5_REV;
+       return TRUE;
+    case PIXMAN_x1b5g5r5:
+       *internal_format = GL_RGB;
+       *format = GL_RGBA;
+       *type = GL_UNSIGNED_SHORT_1_5_5_5_REV;
+       *has_alpha = FALSE;
+       return TRUE;
+    case PIXMAN_a8:
+       *internal_format = GL_ALPHA;
+       *format = GL_ALPHA;
+       *type = GL_UNSIGNED_BYTE;
+       return TRUE;
+
+    case PIXMAN_a2b10g10r10:
+    case PIXMAN_x2b10g10r10:
+    case PIXMAN_a4r4g4b4:
+    case PIXMAN_x4r4g4b4:
+    case PIXMAN_a4b4g4r4:
+    case PIXMAN_x4b4g4r4:
+    case PIXMAN_r3g3b2:
+    case PIXMAN_b2g3r3:
+    case PIXMAN_a2r2g2b2:
+    case PIXMAN_a2b2g2r2:
+    case PIXMAN_c8:
+    case PIXMAN_x4a4:
+    /* case PIXMAN_x4c4: */
+    case PIXMAN_x4g4:
+    case PIXMAN_a4:
+    case PIXMAN_r1g2b1:
+    case PIXMAN_b1g2r1:
+    case PIXMAN_a1r1g1b1:
+    case PIXMAN_a1b1g1r1:
+    case PIXMAN_c4:
+    case PIXMAN_g4:
+    case PIXMAN_a1:
+    case PIXMAN_g1:
+    case PIXMAN_yuy2:
+    case PIXMAN_yv12:
+    case PIXMAN_x2r10g10b10:
+    case PIXMAN_a2r10g10b10:
+    case PIXMAN_r8g8b8x8:
+    case PIXMAN_r8g8b8a8:
+    case PIXMAN_x14r6g6b6:
+    default:
+       return FALSE;
+    }
+}
+
+/*
+ * Extracts pixel data from an image surface.
+ */
+static cairo_status_t
+_cairo_gl_surface_extract_image_data (cairo_image_surface_t *image,
+                                     int x, int y,
+                                     int width, int height,
+                                     void **output)
+{
+    int cpp = PIXMAN_FORMAT_BPP (image->pixman_format) / 8;
+    char *data = _cairo_malloc_ab (width * height, cpp);
+    char *dst = data;
+    unsigned char *src = image->data + y * image->stride + x * cpp;
+    int i;
+
+    if (unlikely (data == NULL))
+       return CAIRO_STATUS_NO_MEMORY;
+
+    for (i = 0; i < height; i++) {
+       memcpy (dst, src, width * cpp);
+       src += image->stride;
+       dst += width * cpp;
+    }
+
+    *output = data;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_bool_t
+_cairo_gl_get_image_format_and_type (cairo_gl_flavor_t flavor,
+                                    pixman_format_code_t pixman_format,
+                                    GLenum *internal_format, GLenum *format,
+                                    GLenum *type, cairo_bool_t *has_alpha,
+                                    cairo_bool_t *needs_swap)
+{
+    if (flavor == CAIRO_GL_FLAVOR_DESKTOP)
+       return _cairo_gl_get_image_format_and_type_gl (pixman_format,
+                                                      internal_format, format,
+                                                      type, has_alpha,
+                                                      needs_swap);
+    else
+       return _cairo_gl_get_image_format_and_type_gles2 (pixman_format,
+                                                         internal_format, format,
+                                                         type, has_alpha,
+                                                         needs_swap);
+
+}
+
+cairo_bool_t
+_cairo_gl_operator_is_supported (cairo_operator_t op)
+{
+    return op < CAIRO_OPERATOR_SATURATE;
+}
+
+void
+_cairo_gl_surface_init (cairo_device_t *device,
+                       cairo_gl_surface_t *surface,
+                       cairo_content_t content,
+                       int width, int height)
+{
+    _cairo_surface_init (&surface->base,
+                        &_cairo_gl_surface_backend,
+                        device,
+                        content);
+
+    surface->width = width;
+    surface->height = height;
+    surface->needs_update = FALSE;
+}
+
+static cairo_surface_t *
+_cairo_gl_surface_create_scratch_for_texture (cairo_gl_context_t   *ctx,
+                                             cairo_content_t       content,
+                                             GLuint                tex,
+                                             int                   width,
+                                             int                   height)
+{
+    cairo_gl_surface_t *surface;
+
+    assert (width <= ctx->max_framebuffer_size && height <= ctx->max_framebuffer_size);
+
+    surface = calloc (1, sizeof (cairo_gl_surface_t));
+    if (unlikely (surface == NULL))
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+    _cairo_gl_surface_init (&ctx->base, surface, content, width, height);
+    surface->tex = tex;
+
+    /* Create the texture used to store the surface's data. */
+    _cairo_gl_context_activate (ctx, CAIRO_GL_TEX_TEMP);
+    glBindTexture (ctx->tex_target, surface->tex);
+    glTexParameteri (ctx->tex_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+    glTexParameteri (ctx->tex_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+
+    return &surface->base;
+}
+
+cairo_surface_t *
+_cairo_gl_surface_create_scratch (cairo_gl_context_t   *ctx,
+                                 cairo_content_t       content,
+                                 int                   width,
+                                 int                   height)
+{
+    cairo_gl_surface_t *surface;
+    GLenum format;
+    GLuint tex;
+
+    glGenTextures (1, &tex);
+    surface = (cairo_gl_surface_t *)
+       _cairo_gl_surface_create_scratch_for_texture (ctx, content,
+                                                     tex, width, height);
+    if (unlikely (surface->base.status))
+       return &surface->base;
+
+    surface->owns_tex = TRUE;
+
+    /* adjust the texture size after setting our real extents */
+    if (width < 1)
+       width = 1;
+    if (height < 1)
+       height = 1;
+
+    switch (content) {
+    default:
+       ASSERT_NOT_REACHED;
+    case CAIRO_CONTENT_COLOR_ALPHA:
+       format = GL_RGBA;
+       break;
+    case CAIRO_CONTENT_ALPHA:
+       /* We want to be trying GL_ALPHA framebuffer objects here. */
+       format = GL_RGBA;
+       break;
+    case CAIRO_CONTENT_COLOR:
+       /* GL_RGB is almost what we want here -- sampling 1 alpha when
+        * texturing, using 1 as destination alpha factor in blending,
+        * etc.  However, when filtering with GL_CLAMP_TO_BORDER, the
+        * alpha channel of the border color will also be clamped to
+        * 1, when we actually want the border color we explicitly
+        * specified.  So, we have to store RGBA, and fill the alpha
+        * channel with 1 when blending.
+        */
+       format = GL_RGBA;
+       break;
+    }
+
+    glTexImage2D (ctx->tex_target, 0, format, width, height, 0,
+                 format, GL_UNSIGNED_BYTE, NULL);
+
+    return &surface->base;
+}
+
+static cairo_status_t
+_cairo_gl_surface_clear (cairo_gl_surface_t  *surface,
+                         const cairo_color_t *color)
+{
+    cairo_gl_context_t *ctx;
+    cairo_status_t status;
+    double r, g, b, a;
+
+    status = _cairo_gl_context_acquire (surface->base.device, &ctx);
+    if (unlikely (status))
+       return status;
+
+    _cairo_gl_context_set_destination (ctx, surface);
+    if (surface->base.content & CAIRO_CONTENT_COLOR) {
+        r = color->red   * color->alpha;
+        g = color->green * color->alpha;
+        b = color->blue  * color->alpha;
+    } else {
+        r = g = b = 0;
+    }
+    if (surface->base.content & CAIRO_CONTENT_ALPHA) {
+        a = color->alpha;
+    } else {
+        a = 1.0;
+    }
+
+    glDisable (GL_SCISSOR_TEST);
+    glClearColor (r, g, b, a);
+    glClear (GL_COLOR_BUFFER_BIT);
+
+    return _cairo_gl_context_release (ctx, status);
+}
+
+cairo_surface_t *
+cairo_gl_surface_create (cairo_device_t                *abstract_device,
+                        cairo_content_t         content,
+                        int                     width,
+                        int                     height)
+{
+    cairo_gl_context_t *ctx;
+    cairo_gl_surface_t *surface;
+    cairo_status_t status;
+
+    if (! CAIRO_CONTENT_VALID (content))
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_CONTENT));
+
+    if (abstract_device == NULL)
+       return _cairo_image_surface_create_with_content (content, width, height);
+
+    if (abstract_device->status)
+       return _cairo_surface_create_in_error (abstract_device->status);
+
+    if (abstract_device->backend->type != CAIRO_DEVICE_TYPE_GL)
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH));
+
+    status = _cairo_gl_context_acquire (abstract_device, &ctx);
+    if (unlikely (status))
+       return _cairo_surface_create_in_error (status);
+
+    surface = (cairo_gl_surface_t *)
+       _cairo_gl_surface_create_scratch (ctx, content, width, height);
+    if (unlikely (surface->base.status)) {
+       status = _cairo_gl_context_release (ctx, surface->base.status);
+       cairo_surface_destroy (&surface->base);
+       return _cairo_surface_create_in_error (status);
+    }
+
+    /* Cairo surfaces start out initialized to transparent (black) */
+    status = _cairo_gl_surface_clear (surface, CAIRO_COLOR_TRANSPARENT);
+
+    status = _cairo_gl_context_release (ctx, status);
+    if (unlikely (status)) {
+       cairo_surface_destroy (&surface->base);
+       return _cairo_surface_create_in_error (status);
+    }
+
+    return &surface->base;
+}
+slim_hidden_def (cairo_gl_surface_create);
+
+/**
+ * cairo_gl_surface_create_for_texture:
+ * @content: type of content in the surface
+ * @tex: name of texture to use for storage of surface pixels
+ * @width: width of the surface, in pixels
+ * @height: height of the surface, in pixels
+ *
+ * Creates a GL surface for the specified texture with the specified
+ * content and dimensions.  The texture must be kept around until the
+ * #cairo_surface_t is destroyed or cairo_surface_finish() is called
+ * on the surface.  The initial contents of @tex will be used as the
+ * initial image contents; you must explicitly clear the buffer,
+ * using, for example, cairo_rectangle() and cairo_fill() if you want
+ * it cleared.  The format of @tex should be compatible with @content,
+ * in the sense that it must have the color components required by
+ * @content.
+ *
+ * Return value: a pointer to the newly created surface. The caller
+ * owns the surface and should call cairo_surface_destroy() when done
+ * with it.
+ *
+ * This function always returns a valid pointer, but it will return a
+ * pointer to a "nil" surface if an error such as out of memory
+ * occurs. You can use cairo_surface_status() to check for this.
+ **/
+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_gl_context_t *ctx;
+    cairo_gl_surface_t *surface;
+    cairo_status_t status;
+
+    if (! CAIRO_CONTENT_VALID (content))
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_CONTENT));
+
+    if (abstract_device == NULL)
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NULL_POINTER));
+
+    if (abstract_device->status)
+       return _cairo_surface_create_in_error (abstract_device->status);
+
+    if (abstract_device->backend->type != CAIRO_DEVICE_TYPE_GL)
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_DEVICE_TYPE_MISMATCH));
+
+    status = _cairo_gl_context_acquire (abstract_device, &ctx);
+    if (unlikely (status))
+       return _cairo_surface_create_in_error (status);
+
+    surface = (cairo_gl_surface_t *)
+       _cairo_gl_surface_create_scratch_for_texture (ctx, content,
+                                                     tex, width, height);
+    status = _cairo_gl_context_release (ctx, status);
+
+    return &surface->base;
+}
+slim_hidden_def (cairo_gl_surface_create_for_texture);
+
+
+void
+cairo_gl_surface_set_size (cairo_surface_t *abstract_surface,
+                          int              width,
+                          int              height)
+{
+    cairo_gl_surface_t *surface = (cairo_gl_surface_t *) abstract_surface;
+
+    if (unlikely (abstract_surface->status))
+       return;
+    if (unlikely (abstract_surface->finished)) {
+       _cairo_surface_set_error (abstract_surface,
+                                 _cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
+        return;
+    }
+
+    if (! _cairo_surface_is_gl (abstract_surface) ||
+        _cairo_gl_surface_is_texture (surface)) {
+       _cairo_surface_set_error (abstract_surface,
+                                 _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH));
+       return;
+    }
+
+    if (surface->width != width || surface->height != height) {
+       surface->needs_update = TRUE;
+       surface->width = width;
+       surface->height = height;
+    }
+}
+
+int
+cairo_gl_surface_get_width (cairo_surface_t *abstract_surface)
+{
+    cairo_gl_surface_t *surface = (cairo_gl_surface_t *) abstract_surface;
+
+    if (! _cairo_surface_is_gl (abstract_surface))
+       return 0;
+
+    return surface->width;
+}
+
+int
+cairo_gl_surface_get_height (cairo_surface_t *abstract_surface)
+{
+    cairo_gl_surface_t *surface = (cairo_gl_surface_t *) abstract_surface;
+
+    if (! _cairo_surface_is_gl (abstract_surface))
+       return 0;
+
+    return surface->height;
+}
+
+void
+cairo_gl_surface_swapbuffers (cairo_surface_t *abstract_surface)
+{
+    cairo_gl_surface_t *surface = (cairo_gl_surface_t *) abstract_surface;
+
+    if (unlikely (abstract_surface->status))
+       return;
+    if (unlikely (abstract_surface->finished)) {
+       _cairo_surface_set_error (abstract_surface,
+                                 _cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
+        return;
+    }
+
+    if (! _cairo_surface_is_gl (abstract_surface)) {
+       _cairo_surface_set_error (abstract_surface,
+                                 CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+       return;
+    }
+
+    if (! _cairo_gl_surface_is_texture (surface)) {
+       cairo_gl_context_t *ctx;
+        cairo_status_t status;
+
+        status = _cairo_gl_context_acquire (surface->base.device, &ctx);
+        if (unlikely (status))
+            return;
+
+        cairo_surface_flush (abstract_surface);
+
+       ctx->swap_buffers (ctx, surface);
+
+        status = _cairo_gl_context_release (ctx, status);
+        if (status)
+            status = _cairo_surface_set_error (abstract_surface, status);
+    }
+}
+
+static cairo_bool_t
+_cairo_gl_surface_size_valid (cairo_gl_surface_t *surface,
+                             int width, int height)
+{
+    cairo_gl_context_t *ctx = (cairo_gl_context_t *)surface->base.device;
+    return width > 0 && height > 0 &&
+       width <= ctx->max_framebuffer_size &&
+       height <= ctx->max_framebuffer_size;
+}
+
+static cairo_surface_t *
+_cairo_gl_surface_create_similar (void          *abstract_surface,
+                                 cairo_content_t  content,
+                                 int             width,
+                                 int             height)
+{
+    cairo_surface_t *surface = abstract_surface;
+    cairo_gl_context_t *ctx;
+    cairo_status_t status;
+
+    if (! _cairo_gl_surface_size_valid (abstract_surface, width, height))
+        return _cairo_image_surface_create_with_content (content, width, height);
+
+    status = _cairo_gl_context_acquire (surface->device, &ctx);
+    if (unlikely (status))
+       return _cairo_surface_create_in_error (status);
+
+    surface = _cairo_gl_surface_create_scratch (ctx, content, width, height);
+
+    status = _cairo_gl_context_release (ctx, status);
+    if (unlikely (status)) {
+        cairo_surface_destroy (surface);
+        return _cairo_surface_create_in_error (status);
+    }
+
+    return surface;
+}
+
+static cairo_int_status_t
+_cairo_gl_surface_fill_alpha_channel (cairo_gl_surface_t *dst,
+                                     cairo_gl_context_t *ctx,
+                                     int x, int y,
+                                     int width, int height)
+{
+    cairo_gl_composite_t setup;
+    cairo_status_t status;
+
+    _cairo_gl_composite_flush (ctx);
+    glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE);
+
+    status = _cairo_gl_composite_init (&setup, CAIRO_OPERATOR_SOURCE, dst,
+                                       FALSE, NULL);
+    if (unlikely (status))
+        goto CLEANUP;
+
+    _cairo_gl_composite_set_solid_source (&setup, CAIRO_COLOR_BLACK);
+
+    status = _cairo_gl_composite_begin (&setup, &ctx);
+    if (unlikely (status))
+        goto CLEANUP;
+
+    _cairo_gl_composite_emit_rect (ctx, x, y, x + width, y + height, 0);
+
+    status = _cairo_gl_context_release (ctx, status);
+
+  CLEANUP:
+    _cairo_gl_composite_fini (&setup);
+
+    _cairo_gl_composite_flush (ctx);
+    glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
+
+    return status;
+}
+
+cairo_status_t
+_cairo_gl_surface_draw_image (cairo_gl_surface_t *dst,
+                             cairo_image_surface_t *src,
+                             int src_x, int src_y,
+                             int width, int height,
+                             int dst_x, int dst_y)
+{
+    GLenum internal_format, format, type;
+    cairo_bool_t has_alpha, needs_swap;
+    cairo_image_surface_t *clone = NULL;
+    cairo_gl_context_t *ctx;
+    int cpp;
+    cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS;
+
+    status = _cairo_gl_context_acquire (dst->base.device, &ctx);
+    if (unlikely (status))
+       return status;
+
+    if (! _cairo_gl_get_image_format_and_type (ctx->gl_flavor,
+                                              src->pixman_format,
+                                              &internal_format,
+                                              &format,
+                                              &type,
+                                              &has_alpha,
+                                              &needs_swap))
+    {
+       cairo_bool_t is_supported;
+
+       clone = _cairo_image_surface_coerce (src);
+       if (unlikely (status = clone->base.status))
+           goto FAIL;
+
+       is_supported =
+           _cairo_gl_get_image_format_and_type (ctx->gl_flavor,
+                                                clone->pixman_format,
+                                                &internal_format,
+                                                &format,
+                                                &type,
+                                                &has_alpha,
+                                                &needs_swap);
+       assert (is_supported);
+       assert (!needs_swap);
+       src = clone;
+    }
+
+    cpp = PIXMAN_FORMAT_BPP (src->pixman_format) / 8;
+
+    status = _cairo_gl_surface_flush (&dst->base);
+    if (unlikely (status))
+       goto FAIL;
+
+    if (ctx->gl_flavor == CAIRO_GL_FLAVOR_DESKTOP)
+       glPixelStorei (GL_UNPACK_ROW_LENGTH, src->stride / cpp);
+    if (_cairo_gl_surface_is_texture (dst)) {
+       void *data_start = src->data + src_y * src->stride + src_x * cpp;
+       void *data_start_gles2 = NULL;
+
+       /*
+        * Due to GL_UNPACK_ROW_LENGTH missing in GLES2 we have to extract the
+        * image data ourselves in some cases. In particular, we must extract
+        * the pixels if:
+        * a. we don't want full-length lines or
+        * b. the row stride cannot be handled by GL itself using a 4 byte
+        *     alignment constraint
+        */
+       if (ctx->gl_flavor == CAIRO_GL_FLAVOR_ES &&
+           (src->width * cpp < src->stride - 3 ||
+            width != src->width))
+       {
+           glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
+           status = _cairo_gl_surface_extract_image_data (src, src_x, src_y,
+                                                          width, height,
+                                                          &data_start_gles2);
+           if (unlikely (status))
+               goto FAIL;
+       }
+       else
+       {
+           glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
+       }
+
+        _cairo_gl_context_activate (ctx, CAIRO_GL_TEX_TEMP);
+       glBindTexture (ctx->tex_target, dst->tex);
+       glTexParameteri (ctx->tex_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+       glTexParameteri (ctx->tex_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+       glTexSubImage2D (ctx->tex_target, 0,
+                        dst_x, dst_y, width, height,
+                        format, type,
+                        data_start_gles2 != NULL ? data_start_gles2 :
+                                                   data_start);
+
+       free (data_start_gles2);
+
+       /* If we just treated some rgb-only data as rgba, then we have to
+        * go back and fix up the alpha channel where we filled in this
+        * texture data.
+        */
+       if (!has_alpha) {
+           _cairo_gl_surface_fill_alpha_channel (dst, ctx,
+                                                 dst_x, dst_y,
+                                                 width, height);
+       }
+    } else {
+        cairo_surface_t *tmp;
+
+        tmp = _cairo_gl_surface_create_scratch (ctx,
+                                                dst->base.content,
+                                                width, height);
+        if (unlikely (tmp->status)) {
+            cairo_surface_destroy (tmp);
+            goto FAIL;
+        }
+        status = _cairo_gl_surface_draw_image ((cairo_gl_surface_t *) tmp,
+                                               src,
+                                               src_x, src_y,
+                                               width, height,
+                                               0, 0);
+        if (status == CAIRO_INT_STATUS_SUCCESS) {
+            cairo_surface_pattern_t tmp_pattern;
+
+            _cairo_pattern_init_for_surface (&tmp_pattern, tmp);
+           status = _cairo_surface_paint (&dst->base,
+                                          CAIRO_OPERATOR_SOURCE,
+                                          &tmp_pattern.base,
+                                          NULL);
+            _cairo_pattern_fini (&tmp_pattern.base);
+        }
+
+        cairo_surface_destroy (tmp);
+    }
+
+FAIL:
+    if (ctx->gl_flavor == CAIRO_GL_FLAVOR_DESKTOP)
+       glPixelStorei (GL_UNPACK_ROW_LENGTH, 0);
+
+    status = _cairo_gl_context_release (ctx, status);
+
+    if (clone)
+        cairo_surface_destroy (&clone->base);
+
+    return status;
+}
+
+static int _cairo_gl_surface_flavor (cairo_gl_surface_t *surface)
+{
+    cairo_gl_context_t *ctx = (cairo_gl_context_t *)surface->base.device;
+    return ctx->gl_flavor;
+}
+
+static cairo_status_t
+_cairo_gl_surface_finish (void *abstract_surface)
+{
+    cairo_gl_surface_t *surface = abstract_surface;
+    cairo_status_t status;
+    cairo_gl_context_t *ctx;
+
+    status = _cairo_gl_context_acquire (surface->base.device, &ctx);
+    if (unlikely (status))
+        return status;
+
+    if (ctx->operands[CAIRO_GL_TEX_SOURCE].type == CAIRO_GL_OPERAND_TEXTURE &&
+        ctx->operands[CAIRO_GL_TEX_SOURCE].texture.surface == surface)
+        _cairo_gl_context_destroy_operand (ctx, CAIRO_GL_TEX_SOURCE);
+    if (ctx->operands[CAIRO_GL_TEX_MASK].type == CAIRO_GL_OPERAND_TEXTURE &&
+        ctx->operands[CAIRO_GL_TEX_MASK].texture.surface == surface)
+        _cairo_gl_context_destroy_operand (ctx, CAIRO_GL_TEX_MASK);
+    if (ctx->current_target == surface)
+       ctx->current_target = NULL;
+
+    if (surface->depth)
+        ctx->dispatch.DeleteFramebuffers (1, &surface->depth);
+    if (surface->fb)
+        ctx->dispatch.DeleteFramebuffers (1, &surface->fb);
+    if (surface->owns_tex)
+       glDeleteTextures (1, &surface->tex);
+
+    return _cairo_gl_context_release (ctx, status);
+}
+
+static cairo_surface_t *
+_cairo_gl_surface_map_to_image (void      *abstract_surface,
+                               const cairo_rectangle_int_t   *extents)
+{
+    cairo_gl_surface_t *surface = abstract_surface;
+    cairo_image_surface_t *image;
+    cairo_gl_context_t *ctx;
+    GLenum format, type;
+    pixman_format_code_t pixman_format;
+    unsigned int cpp;
+    cairo_bool_t invert;
+    cairo_status_t status;
+
+    /* Want to use a switch statement here but the compiler gets whiny. */
+    if (surface->base.content == CAIRO_CONTENT_COLOR_ALPHA) {
+       format = GL_BGRA;
+       pixman_format = PIXMAN_a8r8g8b8;
+       type = GL_UNSIGNED_INT_8_8_8_8_REV;
+       cpp = 4;
+    } else if (surface->base.content == CAIRO_CONTENT_COLOR) {
+       format = GL_BGRA;
+       pixman_format = PIXMAN_x8r8g8b8;
+       type = GL_UNSIGNED_INT_8_8_8_8_REV;
+       cpp = 4;
+    } else if (surface->base.content == CAIRO_CONTENT_ALPHA) {
+       format = GL_ALPHA;
+       pixman_format = PIXMAN_a8;
+       type = GL_UNSIGNED_BYTE;
+       cpp = 1;
+    } else {
+       ASSERT_NOT_REACHED;
+       return NULL;
+    }
+
+    /*
+     * GLES2 supports only RGBA, UNSIGNED_BYTE so use that.
+     * We are also using this format for ALPHA as GLES2 does not
+     * support GL_PACK_ROW_LENGTH anyway, and this makes sure that the
+     * pixman image that is created has row_stride = row_width * bpp.
+     */
+    if (_cairo_gl_surface_flavor (surface) == CAIRO_GL_FLAVOR_ES) {
+       format = GL_RGBA;
+       if (! _cairo_is_little_endian ()) {
+           if (surface->base.content == CAIRO_CONTENT_COLOR)
+               pixman_format = PIXMAN_r8g8b8x8;
+           else
+               pixman_format = PIXMAN_r8g8b8a8;
+       } else {
+           if (surface->base.content == CAIRO_CONTENT_COLOR)
+               pixman_format = PIXMAN_x8b8g8r8;
+           else
+               pixman_format = PIXMAN_a8b8g8r8;
+       }
+       type = GL_UNSIGNED_BYTE;
+       cpp = 4;
+    }
+
+    image = (cairo_image_surface_t*)
+       _cairo_image_surface_create_with_pixman_format (NULL,
+                                                       pixman_format,
+                                                       extents->width,
+                                                       extents->height,
+                                                       -1);
+    if (unlikely (image->base.status))
+       return &image->base;
+
+    if (surface->base.serial == 0)
+       return &image->base;
+
+    status = _cairo_gl_context_acquire (surface->base.device, &ctx);
+    if (unlikely (status)) {
+       cairo_surface_destroy (&image->base);
+       return _cairo_surface_create_in_error (status);
+    }
+
+    /* This is inefficient, as we'd rather just read the thing without making
+     * it the destination.  But then, this is the fallback path, so let's not
+     * fall back instead.
+     */
+    _cairo_gl_composite_flush (ctx);
+    _cairo_gl_context_set_destination (ctx, surface);
+
+    invert = ! _cairo_gl_surface_is_texture (surface) &&
+           ctx->has_mesa_pack_invert;
+
+    glPixelStorei (GL_PACK_ALIGNMENT, 4);
+    if (ctx->gl_flavor == CAIRO_GL_FLAVOR_DESKTOP)
+       glPixelStorei (GL_PACK_ROW_LENGTH, image->stride / cpp);
+    if (invert)
+       glPixelStorei (GL_PACK_INVERT_MESA, 1);
+    glReadPixels (extents->x, extents->y,
+                 extents->width, extents->height,
+                 format, type, image->data);
+    if (invert)
+       glPixelStorei (GL_PACK_INVERT_MESA, 0);
+
+    status = _cairo_gl_context_release (ctx, status);
+    if (unlikely (status)) {
+       cairo_surface_destroy (&image->base);
+       image = (cairo_image_surface_t *)
+           _cairo_surface_create_in_error (status);
+    }
+
+    return &image->base;
+}
+
+static cairo_status_t
+_cairo_gl_surface_acquire_source_image (void                  *abstract_surface,
+                                       cairo_image_surface_t **image_out,
+                                       void                  **image_extra)
+{
+    cairo_gl_surface_t *surface = abstract_surface;
+    cairo_rectangle_int_t extents;
+
+    *image_extra = NULL;
+
+    extents.x = extents.y = 0;
+    extents.width = surface->width;
+    extents.height = surface->height;
+
+    *image_out = (cairo_image_surface_t *)
+       _cairo_gl_surface_map_to_image (surface, &extents);
+    return (*image_out)->base.status;
+}
+
+static void
+_cairo_gl_surface_release_source_image (void                 *abstract_surface,
+                                       cairo_image_surface_t *image,
+                                       void                  *image_extra)
+{
+    cairo_surface_destroy (&image->base);
+}
+
+static cairo_int_status_t
+_cairo_gl_surface_unmap_image (void                  *abstract_surface,
+                              cairo_image_surface_t *image)
+{
+    return _cairo_gl_surface_draw_image (abstract_surface, image,
+                                        0, 0,
+                                        image->width, image->height,
+                                        image->base.device_transform_inverse.x0,
+                                        image->base.device_transform_inverse.y0);
+}
+
+static cairo_bool_t
+_cairo_gl_surface_get_extents (void                 *abstract_surface,
+                              cairo_rectangle_int_t *rectangle)
+{
+    cairo_gl_surface_t *surface = abstract_surface;
+
+    rectangle->x = 0;
+    rectangle->y = 0;
+    rectangle->width  = surface->width;
+    rectangle->height = surface->height;
+
+    return TRUE;
+}
+
+static cairo_status_t
+_cairo_gl_surface_flush (void *abstract_surface)
+{
+    cairo_gl_surface_t *surface = abstract_surface;
+    cairo_status_t status;
+    cairo_gl_context_t *ctx;
+
+    status = _cairo_gl_context_acquire (surface->base.device, &ctx);
+    if (unlikely (status))
+        return status;
+
+    if ((ctx->operands[CAIRO_GL_TEX_SOURCE].type == CAIRO_GL_OPERAND_TEXTURE &&
+         ctx->operands[CAIRO_GL_TEX_SOURCE].texture.surface == surface) ||
+        (ctx->operands[CAIRO_GL_TEX_MASK].type == CAIRO_GL_OPERAND_TEXTURE &&
+         ctx->operands[CAIRO_GL_TEX_MASK].texture.surface == surface) ||
+        (ctx->current_target == surface))
+      _cairo_gl_composite_flush (ctx);
+
+    return _cairo_gl_context_release (ctx, status);
+}
+
+static const cairo_compositor_t *
+get_compositor (cairo_gl_surface_t *surface)
+{
+    cairo_gl_context_t *ctx = (cairo_gl_context_t *)surface->base.device;
+    return ctx->compositor;
+}
+
+static cairo_int_status_t
+_cairo_gl_surface_paint (void                  *surface,
+                        cairo_operator_t        op,
+                        const cairo_pattern_t  *source,
+                        const cairo_clip_t     *clip)
+{
+#if 0
+    /* simplify the common case of clearing the surface */
+    if (clip == NULL) {
+        if (op == CAIRO_OPERATOR_CLEAR)
+            return _cairo_gl_surface_clear (abstract_surface, CAIRO_COLOR_TRANSPARENT);
+       else if (source->type == CAIRO_PATTERN_TYPE_SOLID &&
+                (op == CAIRO_OPERATOR_SOURCE ||
+                 (op == CAIRO_OPERATOR_OVER && _cairo_pattern_is_opaque_solid (source)))) {
+            return _cairo_gl_surface_clear (abstract_surface,
+                                            &((cairo_solid_pattern_t *) source)->color);
+        }
+    }
+#endif
+
+    return _cairo_compositor_paint (get_compositor (surface), surface,
+                                   op, source, clip);
+}
+
+static cairo_int_status_t
+_cairo_gl_surface_mask (void                    *surface,
+                       cairo_operator_t          op,
+                       const cairo_pattern_t   *source,
+                       const cairo_pattern_t   *mask,
+                       const cairo_clip_t      *clip)
+{
+    return _cairo_compositor_mask (get_compositor (surface), surface,
+                                  op, source, mask, clip);
+}
+
+static cairo_int_status_t
+_cairo_gl_surface_stroke (void                         *surface,
+                          cairo_operator_t              op,
+                          const cairo_pattern_t                *source,
+                          const cairo_path_fixed_t     *path,
+                          const cairo_stroke_style_t   *style,
+                          const cairo_matrix_t         *ctm,
+                          const cairo_matrix_t         *ctm_inverse,
+                          double                        tolerance,
+                          cairo_antialias_t             antialias,
+                          const cairo_clip_t           *clip)
+{
+    return _cairo_compositor_stroke (get_compositor (surface), surface,
+                                    op, source, path, style,
+                                    ctm, ctm_inverse, tolerance, antialias,
+                                    clip);
+}
+
+static cairo_int_status_t
+_cairo_gl_surface_fill (void                   *surface,
+                        cairo_operator_t        op,
+                        const cairo_pattern_t  *source,
+                        const cairo_path_fixed_t*path,
+                        cairo_fill_rule_t       fill_rule,
+                        double                  tolerance,
+                        cairo_antialias_t       antialias,
+                        const cairo_clip_t     *clip)
+{
+    return _cairo_compositor_fill (get_compositor (surface), surface,
+                                  op, source, path,
+                                  fill_rule, tolerance, antialias,
+                                  clip);
+}
+
+static cairo_int_status_t
+_cairo_gl_surface_glyphs (void                 *surface,
+                         cairo_operator_t       op,
+                         const cairo_pattern_t *source,
+                         cairo_glyph_t         *glyphs,
+                         int                    num_glyphs,
+                         cairo_scaled_font_t   *font,
+                         const cairo_clip_t    *clip)
+{
+    return _cairo_compositor_glyphs (get_compositor (surface), surface,
+                                    op, source, glyphs, num_glyphs, font,
+                                    clip);
+}
+
+const cairo_surface_backend_t _cairo_gl_surface_backend = {
+    CAIRO_SURFACE_TYPE_GL,
+    _cairo_gl_surface_finish,
+    _cairo_default_context_create,
+
+    _cairo_gl_surface_create_similar,
+    NULL, /* similar image */
+    _cairo_gl_surface_map_to_image,
+    _cairo_gl_surface_unmap_image,
+
+    _cairo_gl_surface_acquire_source_image,
+    _cairo_gl_surface_release_source_image,
+    NULL, /* snapshot */
+
+    NULL, /* copy_page */
+    NULL, /* show_page */
+
+    _cairo_gl_surface_get_extents,
+    _cairo_image_surface_get_font_options,
+
+    _cairo_gl_surface_flush,
+    NULL, /* mark_dirty_rectangle */
+
+    _cairo_gl_surface_paint,
+    _cairo_gl_surface_mask,
+    _cairo_gl_surface_stroke,
+    _cairo_gl_surface_fill,
+    NULL, /* fill/stroke */
+    _cairo_gl_surface_glyphs,
+};
diff --git a/src/cairo-gl-traps-compositor.c b/src/cairo-gl-traps-compositor.c
new file mode 100644 (file)
index 0000000..afc74d0
--- /dev/null
@@ -0,0 +1,550 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Eric Anholt
+ * Copyright © 2009 Chris Wilson
+ * Copyright © 2005,2010 Red Hat, Inc
+ * Copyright © 2011 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ *     Benjamin Otte <otte@gnome.org>
+ *     Carl Worth <cworth@cworth.org>
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ *     Eric Anholt <eric@anholt.net>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-gl-private.h"
+
+#include "cairo-composite-rectangles-private.h"
+#include "cairo-compositor-private.h"
+#include "cairo-default-context-private.h"
+#include "cairo-error-private.h"
+#include "cairo-image-surface-private.h"
+#include "cairo-spans-compositor-private.h"
+#include "cairo-surface-backend-private.h"
+
+static cairo_int_status_t
+acquire (void *abstract_dst)
+{
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+release (void *abstract_dst)
+{
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+set_clip_region (void *_surface,
+                cairo_region_t *region)
+{
+    cairo_gl_surface_t *surface = _surface;
+
+    //surface->clip_region = region;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+draw_image_boxes (void *_dst,
+                 cairo_image_surface_t *image,
+                 cairo_boxes_t *boxes,
+                 int dx, int dy)
+{
+    cairo_gl_surface_t *dst = _dst;
+    struct _cairo_boxes_chunk *chunk;
+    int i;
+
+    for (chunk = &boxes->chunks; chunk; chunk = chunk->next) {
+       for (i = 0; i < chunk->count; i++) {
+           cairo_box_t *b = &chunk->base[i];
+           int x = _cairo_fixed_integer_part (b->p1.x);
+           int y = _cairo_fixed_integer_part (b->p1.y);
+           int w = _cairo_fixed_integer_part (b->p2.x) - x;
+           int h = _cairo_fixed_integer_part (b->p2.y) - y;
+           cairo_status_t status;
+
+           status = _cairo_gl_surface_draw_image (dst, image,
+                                                  x + dx, y + dy,
+                                                  w, h,
+                                                  x, y);
+           if (unlikely (status))
+               return status;
+       }
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+emit_aligned_boxes (cairo_gl_context_t *ctx,
+                   const cairo_boxes_t *boxes)
+{
+    const struct _cairo_boxes_chunk *chunk;
+    int i;
+
+    for (chunk = &boxes->chunks; chunk; chunk = chunk->next) {
+       for (i = 0; i < chunk->count; i++) {
+           int x1 = _cairo_fixed_integer_part (chunk->base[i].p1.x);
+           int y1 = _cairo_fixed_integer_part (chunk->base[i].p1.y);
+           int x2 = _cairo_fixed_integer_part (chunk->base[i].p2.x);
+           int y2 = _cairo_fixed_integer_part (chunk->base[i].p2.y);
+           _cairo_gl_composite_emit_rect (ctx, x1, y1, x2, y2, 0);
+       }
+    }
+}
+
+static cairo_int_status_t
+fill_boxes (void               *_dst,
+           cairo_operator_t     op,
+           const cairo_color_t *color,
+           cairo_boxes_t       *boxes)
+{
+    cairo_gl_composite_t setup;
+    cairo_gl_context_t *ctx;
+    cairo_int_status_t status;
+
+    status = _cairo_gl_composite_init (&setup, op, _dst, FALSE, NULL);
+    if (unlikely (status))
+        goto FAIL;
+
+   _cairo_gl_composite_set_solid_source (&setup, color);
+
+    status = _cairo_gl_composite_begin (&setup, &ctx);
+    if (unlikely (status))
+        goto FAIL;
+
+    emit_aligned_boxes (ctx, boxes);
+    status = _cairo_gl_context_release (ctx, CAIRO_STATUS_SUCCESS);
+
+FAIL:
+    _cairo_gl_composite_fini (&setup);
+    return status;
+}
+
+typedef struct cairo_gl_source {
+    cairo_surface_t base;
+
+    cairo_gl_operand_t operand;
+} cairo_gl_source_t;
+
+static cairo_status_t
+_cairo_gl_source_finish (void *abstract_surface)
+{
+    cairo_gl_source_t *source = abstract_surface;
+
+    _cairo_gl_operand_destroy (&source->operand);
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static const cairo_surface_backend_t cairo_gl_source_backend = {
+    CAIRO_SURFACE_TYPE_GL,
+    _cairo_gl_source_finish,
+    NULL, /* read-only wrapper */
+};
+
+static cairo_surface_t *
+pattern_to_surface (cairo_surface_t *dst,
+                   const cairo_pattern_t *pattern,
+                   cairo_bool_t is_mask,
+                   const cairo_rectangle_int_t *extents,
+                   const cairo_rectangle_int_t *sample,
+                   int *src_x, int *src_y)
+{
+    cairo_gl_source_t *source;
+    cairo_int_status_t status;
+
+    source = malloc (sizeof (*source));
+    if (unlikely (source == NULL))
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+    _cairo_surface_init (&source->base,
+                        &cairo_gl_source_backend,
+                        NULL, /* device */
+                        CAIRO_CONTENT_COLOR_ALPHA);
+
+    *src_x = *src_y = 0;
+    status = _cairo_gl_operand_init (&source->operand, pattern, (cairo_gl_surface_t *)dst,
+                                    extents->x, extents->y,
+                                    extents->x, extents->y,
+                                    extents->width, extents->height);
+    if (unlikely (status)) {
+       cairo_surface_destroy (&source->base);
+       return _cairo_surface_create_in_error (status);
+    }
+
+    return &source->base;
+}
+
+static inline cairo_gl_operand_t *
+source_to_operand (cairo_surface_t *surface)
+{
+    cairo_gl_source_t *source = (cairo_gl_source_t *)surface;
+    return &source->operand;
+}
+
+static cairo_int_status_t
+composite_boxes (void                  *_dst,
+                cairo_operator_t       op,
+                cairo_surface_t        *abstract_src,
+                cairo_surface_t        *abstract_mask,
+                int                    src_x,
+                int                    src_y,
+                int                    mask_x,
+                int                    mask_y,
+                int                    dst_x,
+                int                    dst_y,
+                cairo_boxes_t          *boxes,
+                const cairo_rectangle_int_t  *extents)
+{
+    cairo_gl_composite_t setup;
+    cairo_gl_context_t *ctx;
+    cairo_int_status_t status;
+
+    status = _cairo_gl_composite_init (&setup, op, _dst, FALSE, extents);
+    if (unlikely (status))
+        goto FAIL;
+
+    _cairo_gl_composite_set_source_operand (&setup,
+                                           source_to_operand (abstract_src));
+
+    _cairo_gl_composite_set_mask_operand (&setup,
+                                         source_to_operand (abstract_mask));
+
+    status = _cairo_gl_composite_begin (&setup, &ctx);
+    if (unlikely (status))
+        goto FAIL;
+
+    emit_aligned_boxes (ctx, boxes);
+    status = _cairo_gl_context_release (ctx, CAIRO_STATUS_SUCCESS);
+
+FAIL:
+    _cairo_gl_composite_fini (&setup);
+    return status;
+}
+
+static cairo_int_status_t
+composite (void                        *_dst,
+          cairo_operator_t     op,
+          cairo_surface_t      *abstract_src,
+          cairo_surface_t      *abstract_mask,
+          int                  src_x,
+          int                  src_y,
+          int                  mask_x,
+          int                  mask_y,
+          int                  dst_x,
+          int                  dst_y,
+          unsigned int         width,
+          unsigned int         height)
+{
+    cairo_gl_composite_t setup;
+    cairo_gl_context_t *ctx;
+    cairo_int_status_t status;
+
+    status = _cairo_gl_composite_init (&setup, op, _dst, FALSE, NULL);
+    if (unlikely (status))
+        goto FAIL;
+
+    _cairo_gl_composite_set_source_operand (&setup,
+                                           source_to_operand (abstract_src));
+
+    _cairo_gl_composite_set_mask_operand (&setup,
+                                         source_to_operand (abstract_mask));
+
+    status = _cairo_gl_composite_begin (&setup, &ctx);
+    if (unlikely (status))
+        goto FAIL;
+
+    /* XXX clip */
+    _cairo_gl_composite_emit_rect (ctx, dst_x, dst_y, dst_x+width, dst_y+height, 0);
+    status = _cairo_gl_context_release (ctx, CAIRO_STATUS_SUCCESS);
+
+FAIL:
+    _cairo_gl_composite_fini (&setup);
+    return status;
+}
+
+static cairo_int_status_t
+lerp (void                     *dst,
+      cairo_surface_t          *src,
+      cairo_surface_t          *mask,
+      int                      src_x,
+      int                      src_y,
+      int                      mask_x,
+      int                      mask_y,
+      int                      dst_x,
+      int                      dst_y,
+      unsigned int             width,
+      unsigned int             height)
+{
+    cairo_int_status_t status;
+
+    /* we could avoid some repetition... */
+    status = composite (dst, CAIRO_OPERATOR_DEST_OUT, src, mask,
+                       src_x, src_y,
+                       mask_x, mask_y,
+                       dst_x, dst_y,
+                       width, height);
+    if (unlikely (status))
+       return status;
+
+    status = composite (dst, CAIRO_OPERATOR_ADD, src, mask,
+                       src_x, src_y,
+                       mask_x, mask_y,
+                       dst_x, dst_y,
+                       width, height);
+    if (unlikely (status))
+       return status;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_gl_surface_t *
+traps_to_surface (void *_dst,
+                 const cairo_rectangle_int_t *extents,
+                 cairo_antialias_t     antialias,
+                 cairo_traps_t         *traps)
+{
+    pixman_format_code_t pixman_format;
+    pixman_image_t *pixman_image;
+    cairo_surface_t *image, *mask;
+    cairo_status_t status;
+
+    pixman_format = antialias != CAIRO_ANTIALIAS_NONE ? PIXMAN_a8 : PIXMAN_a1,
+    pixman_image = pixman_image_create_bits (pixman_format,
+                                            extents->width,
+                                            extents->height,
+                                            NULL, 0);
+    if (unlikely (pixman_image == NULL))
+       return (cairo_gl_surface_t *)_cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+    _pixman_image_add_traps (pixman_image, extents->x, extents->y, traps);
+    image = _cairo_image_surface_create_for_pixman_image (pixman_image,
+                                                         pixman_format);
+    if (unlikely (image->status)) {
+       pixman_image_unref (pixman_image);
+       return (cairo_gl_surface_t *)image;
+    }
+
+    mask = _cairo_surface_create_similar_scratch (_dst, CAIRO_CONTENT_ALPHA,
+                                                 extents->width,
+                                                 extents->height);
+    if (unlikely (mask->status)) {
+       cairo_surface_destroy (image);
+       return (cairo_gl_surface_t *)mask;
+    }
+
+    status = _cairo_gl_surface_draw_image ((cairo_gl_surface_t *)mask,
+                                          (cairo_image_surface_t *)image,
+                                          0, 0,
+                                          extents->width, extents->height,
+                                          0, 0);
+    cairo_surface_destroy (image);
+    if (unlikely (status)) {
+       cairo_surface_destroy (mask);
+       return (cairo_gl_surface_t*)_cairo_surface_create_in_error (status);
+    }
+
+    return (cairo_gl_surface_t*)mask;
+}
+
+static cairo_int_status_t
+composite_traps (void                  *_dst,
+                cairo_operator_t       op,
+                cairo_surface_t        *abstract_src,
+                int                    src_x,
+                int                    src_y,
+                int                    dst_x,
+                int                    dst_y,
+                const cairo_rectangle_int_t *extents,
+                cairo_antialias_t      antialias,
+                cairo_traps_t          *traps)
+{
+    cairo_gl_composite_t setup;
+    cairo_gl_context_t *ctx;
+    cairo_gl_surface_t *mask;
+    cairo_int_status_t status;
+
+    mask = traps_to_surface (_dst, extents, antialias, traps);
+    if (unlikely (mask->base.status))
+       return mask->base.status;
+
+    status = _cairo_gl_composite_init (&setup, op, _dst, FALSE, NULL);
+    if (unlikely (status))
+        goto FAIL;
+
+    _cairo_gl_composite_set_source_operand (&setup,
+                                           source_to_operand (abstract_src));
+
+    //_cairo_gl_composite_set_mask_surface (&setup, mask, 0, 0);
+
+    status = _cairo_gl_composite_begin (&setup, &ctx);
+    if (unlikely (status))
+        goto FAIL;
+
+    /* XXX clip */
+    _cairo_gl_composite_emit_rect (ctx,
+                                  dst_x, dst_y,
+                                  dst_x+extents->width,
+                                  dst_y+extents->height, 0);
+    status = _cairo_gl_context_release (ctx, CAIRO_STATUS_SUCCESS);
+
+FAIL:
+    _cairo_gl_composite_fini (&setup);
+    cairo_surface_destroy (&mask->base);
+    return status;
+}
+
+static cairo_gl_surface_t *
+tristrip_to_surface (void *_dst,
+                 const cairo_rectangle_int_t *extents,
+                 cairo_antialias_t     antialias,
+                 cairo_tristrip_t      *strip)
+{
+    pixman_format_code_t pixman_format;
+    pixman_image_t *pixman_image;
+    cairo_surface_t *image, *mask;
+    cairo_status_t status;
+
+    pixman_format = antialias != CAIRO_ANTIALIAS_NONE ? PIXMAN_a8 : PIXMAN_a1,
+    pixman_image = pixman_image_create_bits (pixman_format,
+                                            extents->width,
+                                            extents->height,
+                                            NULL, 0);
+    if (unlikely (pixman_image == NULL))
+       return (cairo_gl_surface_t *)_cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+    _pixman_image_add_tristrip (pixman_image, extents->x, extents->y, strip);
+    image = _cairo_image_surface_create_for_pixman_image (pixman_image,
+                                                         pixman_format);
+    if (unlikely (image->status)) {
+       pixman_image_unref (pixman_image);
+       return (cairo_gl_surface_t *)image;
+    }
+
+    mask = _cairo_surface_create_similar_scratch (_dst, CAIRO_CONTENT_ALPHA,
+                                                 extents->width,
+                                                 extents->height);
+    if (unlikely (mask->status)) {
+       cairo_surface_destroy (image);
+       return (cairo_gl_surface_t *)mask;
+    }
+
+    status = _cairo_gl_surface_draw_image ((cairo_gl_surface_t *)mask,
+                                          (cairo_image_surface_t *)image,
+                                          0, 0,
+                                          extents->width, extents->height,
+                                          0, 0);
+    cairo_surface_destroy (image);
+    if (unlikely (status)) {
+       cairo_surface_destroy (mask);
+       return (cairo_gl_surface_t*)_cairo_surface_create_in_error (status);
+    }
+
+    return (cairo_gl_surface_t*)mask;
+}
+
+static cairo_int_status_t
+composite_tristrip (void               *_dst,
+                   cairo_operator_t    op,
+                   cairo_surface_t     *abstract_src,
+                   int                 src_x,
+                   int                 src_y,
+                   int                 dst_x,
+                   int                 dst_y,
+                   const cairo_rectangle_int_t *extents,
+                   cairo_antialias_t   antialias,
+                   cairo_tristrip_t    *strip)
+{
+    cairo_gl_composite_t setup;
+    cairo_gl_context_t *ctx;
+    cairo_gl_surface_t *mask;
+    cairo_int_status_t status;
+
+    mask = tristrip_to_surface (_dst, extents, antialias, strip);
+    if (unlikely (mask->base.status))
+       return mask->base.status;
+
+    status = _cairo_gl_composite_init (&setup, op, _dst, FALSE, NULL);
+    if (unlikely (status))
+        goto FAIL;
+
+    _cairo_gl_composite_set_source_operand (&setup,
+                                           source_to_operand (abstract_src));
+
+    //_cairo_gl_composite_set_mask_surface (&setup, mask, 0, 0);
+
+    status = _cairo_gl_composite_begin (&setup, &ctx);
+    if (unlikely (status))
+        goto FAIL;
+
+    /* XXX clip */
+    _cairo_gl_composite_emit_rect (ctx,
+                                  dst_x, dst_y,
+                                  dst_x+extents->width,
+                                  dst_y+extents->height, 0);
+    status = _cairo_gl_context_release (ctx, CAIRO_STATUS_SUCCESS);
+
+FAIL:
+    _cairo_gl_composite_fini (&setup);
+    cairo_surface_destroy (&mask->base);
+    return status;
+}
+
+const cairo_compositor_t *
+_cairo_gl_traps_compositor_get (void)
+{
+    static cairo_traps_compositor_t compositor;
+
+    if (compositor.base.delegate == NULL) {
+       _cairo_traps_compositor_init (&compositor, &_cairo_fallback_compositor);
+       compositor.acquire = acquire;
+       compositor.release = release;
+       compositor.set_clip_region = set_clip_region;
+       compositor.pattern_to_surface = pattern_to_surface;
+       compositor.draw_image_boxes = draw_image_boxes;
+       //compositor.copy_boxes = copy_boxes;
+       compositor.fill_boxes = fill_boxes;
+       //compositor.check_composite = check_composite;
+       compositor.composite = composite;
+       compositor.lerp = lerp;
+       //compositor.check_composite_boxes = check_composite_boxes;
+       compositor.composite_boxes = composite_boxes;
+       //compositor.check_composite_traps = check_composite_traps;
+       compositor.composite_traps = composite_traps;
+       //compositor.check_composite_tristrip = check_composite_traps;
+       compositor.composite_tristrip = composite_tristrip;
+       compositor.check_composite_glyphs = _cairo_gl_check_composite_glyphs;
+       compositor.composite_glyphs = _cairo_gl_composite_glyphs;
+    }
+
+    return &compositor.base;
+}
diff --git a/src/cairo-gl.h b/src/cairo-gl.h
new file mode 100644 (file)
index 0000000..d23b8a5
--- /dev/null
@@ -0,0 +1,145 @@
+/* Cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Eric Anholt
+ * Copyright © 2009 Chris Wilson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Eric Anholt.
+ */
+
+/**
+ * @file cairo-gl.h
+ *
+ * The cairo-gl backend provides an implementation of possibly
+ * hardware-accelerated cairo rendering by targeting the OpenGL API.
+ * The goal of the cairo-gl backend is to provide better performance
+ * with equal functionality to cairo-image where possible.  It does
+ * not directly provide for applying additional OpenGL effects to
+ * cairo surfaces.
+ *
+ * Cairo-gl allows interoperability with other GL rendering through GL
+ * context sharing.  Cairo-gl surfaces are created in reference to a
+ * #cairo_device_t, which represents an GL context created by the user.
+ * When that GL context is created with its sharePtr set to another
+ * context (or vice versa), its objects (textures backing cairo-gl
+ * surfaces) can be accessed in the other OpenGL context.  This allows
+ * cairo-gl to maintain its drawing state in one context while the
+ * user's 3D rendering occurs in the user's other context.
+ *
+ * However, as only one context can be current to a thread at a time,
+ * cairo-gl may make its context current to the thread on any cairo
+ * call which interacts with a cairo-gl surface or the cairo-gl
+ * device.  As a result, the user must make their own context current
+ * between any cairo calls and their own OpenGL rendering.
+ */
+
+#ifndef CAIRO_GL_H
+#define CAIRO_GL_H
+
+#include "cairo.h"
+
+#if CAIRO_HAS_GL_SURFACE || CAIRO_HAS_GLESV2_SURFACE
+
+CAIRO_BEGIN_DECLS
+
+cairo_public cairo_surface_t *
+cairo_gl_surface_create (cairo_device_t *device,
+                        cairo_content_t content,
+                        int width, int height);
+
+cairo_public 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_public void
+cairo_gl_surface_set_size (cairo_surface_t *surface, int width, int height);
+
+cairo_public int
+cairo_gl_surface_get_width (cairo_surface_t *abstract_surface);
+
+cairo_public int
+cairo_gl_surface_get_height (cairo_surface_t *abstract_surface);
+
+cairo_public void
+cairo_gl_surface_swapbuffers (cairo_surface_t *surface);
+
+#if CAIRO_HAS_GLX_FUNCTIONS
+#include <GL/glx.h>
+
+cairo_public cairo_device_t *
+cairo_glx_device_create (Display *dpy, GLXContext gl_ctx);
+
+cairo_public Display *
+cairo_glx_device_get_display (cairo_device_t *device);
+
+cairo_public GLXContext
+cairo_glx_device_get_context (cairo_device_t *device);
+
+cairo_public cairo_surface_t *
+cairo_gl_surface_create_for_window (cairo_device_t *device,
+                                   Window win,
+                                   int width, int height);
+#endif
+
+#if CAIRO_HAS_WGL_FUNCTIONS
+#include <windows.h>
+
+cairo_public cairo_device_t *
+cairo_wgl_device_create (HGLRC rc);
+
+cairo_public HGLRC
+cairo_wgl_device_get_context (cairo_device_t *device);
+
+cairo_public cairo_surface_t *
+cairo_gl_surface_create_for_dc (cairo_device_t         *device,
+                               HDC                      dc,
+                               int                      width,
+                               int                      height);
+#endif
+
+#if CAIRO_HAS_EGL_FUNCTIONS
+#include <EGL/egl.h>
+
+cairo_public cairo_device_t *
+cairo_egl_device_create (EGLDisplay dpy, EGLContext egl);
+
+cairo_public cairo_surface_t *
+cairo_gl_surface_create_for_egl (cairo_device_t        *device,
+                                EGLSurface      egl,
+                                int             width,
+                                int             height);
+
+#endif
+
+CAIRO_END_DECLS
+
+#else  /* CAIRO_HAS_GL_SURFACE */
+# error Cairo was not compiled with support for the GL backend
+#endif /* CAIRO_HAS_GL_SURFACE */
+
+#endif /* CAIRO_GL_H */
diff --git a/src/cairo-glx-context.c b/src/cairo-glx-context.c
new file mode 100644 (file)
index 0000000..f89fa32
--- /dev/null
@@ -0,0 +1,277 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Eric Anholt
+ * Copyright © 2009 Chris Wilson
+ * Copyright © 2005 Red Hat, Inc
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ *     Carl Worth <cworth@cworth.org>
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-gl-private.h"
+
+#include "cairo-error-private.h"
+
+#include <X11/Xutil.h>
+
+/* XXX needs hooking into XCloseDisplay() */
+
+typedef struct _cairo_glx_context {
+    cairo_gl_context_t base;
+
+    Display *display;
+    Window dummy_window;
+    GLXContext context;
+
+    cairo_bool_t has_multithread_makecurrent;
+} cairo_glx_context_t;
+
+typedef struct _cairo_glx_surface {
+    cairo_gl_surface_t base;
+
+    Window win;
+} cairo_glx_surface_t;
+
+static void
+_glx_acquire (void *abstract_ctx)
+{
+    cairo_glx_context_t *ctx = abstract_ctx;
+    GLXDrawable current_drawable;
+
+    if (ctx->base.current_target == NULL ||
+        _cairo_gl_surface_is_texture (ctx->base.current_target)) {
+        current_drawable = ctx->dummy_window;
+    } else {
+        cairo_glx_surface_t *surface = (cairo_glx_surface_t *) ctx->base.current_target;
+        current_drawable = surface->win;
+    }
+
+    glXMakeCurrent (ctx->display, current_drawable, ctx->context);
+}
+
+static void
+_glx_make_current (void *abstract_ctx, cairo_gl_surface_t *abstract_surface)
+{
+    cairo_glx_context_t *ctx = abstract_ctx;
+    cairo_glx_surface_t *surface = (cairo_glx_surface_t *) abstract_surface;
+
+    /* Set the window as the target of our context. */
+    glXMakeCurrent (ctx->display, surface->win, ctx->context);
+}
+
+static void
+_glx_release (void *abstract_ctx)
+{
+    cairo_glx_context_t *ctx = abstract_ctx;
+
+    if (!ctx->has_multithread_makecurrent) {
+       glXMakeCurrent (ctx->display, None, None);
+    }
+}
+
+static void
+_glx_swap_buffers (void *abstract_ctx,
+                  cairo_gl_surface_t *abstract_surface)
+{
+    cairo_glx_context_t *ctx = abstract_ctx;
+    cairo_glx_surface_t *surface = (cairo_glx_surface_t *) abstract_surface;
+
+    glXSwapBuffers (ctx->display, surface->win);
+}
+
+static void
+_glx_destroy (void *abstract_ctx)
+{
+    cairo_glx_context_t *ctx = abstract_ctx;
+
+    if (ctx->dummy_window != None)
+       XDestroyWindow (ctx->display, ctx->dummy_window);
+
+    glXMakeCurrent (ctx->display, 0, 0);
+}
+
+static cairo_status_t
+_glx_dummy_ctx (Display *dpy, GLXContext gl_ctx, Window *dummy)
+{
+    int attr[3] = { GLX_FBCONFIG_ID, 0, None };
+    GLXFBConfig *config;
+    XVisualInfo *vi;
+    Colormap cmap;
+    XSetWindowAttributes swa;
+    Window win = None;
+    int cnt;
+
+    /* Create a dummy window created for the target GLX context that we can
+     * use to query the available GL/GLX extensions.
+     */
+    glXQueryContext (dpy, gl_ctx, GLX_FBCONFIG_ID, &attr[1]);
+
+    cnt = 0;
+    config = glXChooseFBConfig (dpy, DefaultScreen (dpy), attr, &cnt);
+    if (unlikely (cnt == 0))
+       return _cairo_error (CAIRO_STATUS_INVALID_FORMAT);
+
+    vi = glXGetVisualFromFBConfig (dpy, config[0]);
+    XFree (config);
+
+    if (unlikely (vi == NULL))
+       return _cairo_error (CAIRO_STATUS_INVALID_FORMAT);
+
+    cmap = XCreateColormap (dpy,
+                           RootWindow (dpy, vi->screen),
+                           vi->visual,
+                           AllocNone);
+    swa.colormap = cmap;
+    swa.border_pixel = 0;
+    win = XCreateWindow (dpy, RootWindow (dpy, vi->screen),
+                        -1, -1, 1, 1, 0,
+                        vi->depth,
+                        InputOutput,
+                        vi->visual,
+                        CWBorderPixel | CWColormap, &swa);
+    XFreeColormap (dpy, cmap);
+    XFree (vi);
+
+    XFlush (dpy);
+    if (unlikely (! glXMakeCurrent (dpy, win, gl_ctx))) {
+       XDestroyWindow (dpy, win);
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+    }
+
+    *dummy = win;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_device_t *
+cairo_glx_device_create (Display *dpy, GLXContext gl_ctx)
+{
+    cairo_glx_context_t *ctx;
+    cairo_status_t status;
+    Window dummy = None;
+    const char *glx_extensions;
+
+    status = _glx_dummy_ctx (dpy, gl_ctx, &dummy);
+    if (unlikely (status))
+       return _cairo_gl_context_create_in_error (status);
+
+    ctx = calloc (1, sizeof (cairo_glx_context_t));
+    if (unlikely (ctx == NULL))
+       return _cairo_gl_context_create_in_error (CAIRO_STATUS_NO_MEMORY);
+
+    ctx->display = dpy;
+    ctx->dummy_window = dummy;
+    ctx->context = gl_ctx;
+
+    ctx->base.acquire = _glx_acquire;
+    ctx->base.release = _glx_release;
+    ctx->base.make_current = _glx_make_current;
+    ctx->base.swap_buffers = _glx_swap_buffers;
+    ctx->base.destroy = _glx_destroy;
+
+    status = _cairo_gl_dispatch_init (&ctx->base.dispatch,
+                                     (cairo_gl_get_proc_addr_func_t) glXGetProcAddress);
+    if (unlikely (status)) {
+       free (ctx);
+       return _cairo_gl_context_create_in_error (status);
+    }
+
+    status = _cairo_gl_context_init (&ctx->base);
+    if (unlikely (status)) {
+       free (ctx);
+       return _cairo_gl_context_create_in_error (status);
+    }
+
+    glx_extensions = glXQueryExtensionsString (dpy, DefaultScreen (dpy));
+    if (strstr(glx_extensions, "GLX_MESA_multithread_makecurrent")) {
+       ctx->has_multithread_makecurrent = TRUE;
+    }
+
+    ctx->base.release (ctx);
+
+    return &ctx->base.base;
+}
+
+Display *
+cairo_glx_device_get_display (cairo_device_t *device)
+{
+    cairo_glx_context_t *ctx;
+
+    if (device->backend->type != CAIRO_DEVICE_TYPE_GL) {
+       _cairo_error_throw (CAIRO_STATUS_DEVICE_TYPE_MISMATCH);
+       return NULL;
+    }
+
+    ctx = (cairo_glx_context_t *) device;
+
+    return ctx->display;
+}
+
+GLXContext
+cairo_glx_device_get_context (cairo_device_t *device)
+{
+    cairo_glx_context_t *ctx;
+
+    if (device->backend->type != CAIRO_DEVICE_TYPE_GL) {
+       _cairo_error_throw (CAIRO_STATUS_DEVICE_TYPE_MISMATCH);
+       return NULL;
+    }
+
+    ctx = (cairo_glx_context_t *) device;
+
+    return ctx->context;
+}
+
+cairo_surface_t *
+cairo_gl_surface_create_for_window (cairo_device_t     *device,
+                                   Window               win,
+                                   int                  width,
+                                   int                  height)
+{
+    cairo_glx_surface_t *surface;
+
+    if (unlikely (device->status))
+       return _cairo_surface_create_in_error (device->status);
+
+    if (device->backend->type != CAIRO_DEVICE_TYPE_GL)
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH));
+
+    surface = calloc (1, sizeof (cairo_glx_surface_t));
+    if (unlikely (surface == NULL))
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+    _cairo_gl_surface_init (device, &surface->base,
+                           CAIRO_CONTENT_COLOR_ALPHA, width, height);
+    surface->win = win;
+
+    return &surface->base.base;
+}
diff --git a/src/cairo-gstate-private.h b/src/cairo-gstate-private.h
new file mode 100644 (file)
index 0000000..38f11c7
--- /dev/null
@@ -0,0 +1,367 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ *     Carl D. Worth <cworth@redhat.com>
+ */
+
+#ifndef CAIRO_GSTATE_PRIVATE_H
+#define CAIRO_GSTATE_PRIVATE_H
+
+#include "cairo-clip-private.h"
+
+struct _cairo_gstate {
+    cairo_operator_t op;
+
+    double opacity;
+    double tolerance;
+    cairo_antialias_t antialias;
+
+    cairo_stroke_style_t stroke_style;
+
+    cairo_fill_rule_t fill_rule;
+
+    cairo_font_face_t *font_face;
+    cairo_scaled_font_t *scaled_font;  /* Specific to the current CTM */
+    cairo_scaled_font_t *previous_scaled_font; /* holdover */
+    cairo_matrix_t font_matrix;
+    cairo_font_options_t font_options;
+
+    cairo_clip_t *clip;
+
+    cairo_surface_t *target;           /* The target to which all rendering is directed */
+    cairo_surface_t *parent_target;    /* The previous target which was receiving rendering */
+    cairo_surface_t *original_target;  /* The original target the initial gstate was created with */
+
+    /* the user is allowed to update the device after we have cached the matrices... */
+    cairo_observer_t device_transform_observer;
+
+    cairo_matrix_t ctm;
+    cairo_matrix_t ctm_inverse;
+    cairo_matrix_t source_ctm_inverse; /* At the time ->source was set */
+    cairo_bool_t is_identity;
+
+    cairo_pattern_t *source;
+
+    struct _cairo_gstate *next;
+};
+
+/* cairo-gstate.c */
+cairo_private cairo_status_t
+_cairo_gstate_init (cairo_gstate_t  *gstate,
+                   cairo_surface_t *target);
+
+cairo_private void
+_cairo_gstate_fini (cairo_gstate_t *gstate);
+
+cairo_private cairo_status_t
+_cairo_gstate_save (cairo_gstate_t **gstate, cairo_gstate_t **freelist);
+
+cairo_private cairo_status_t
+_cairo_gstate_restore (cairo_gstate_t **gstate, cairo_gstate_t **freelist);
+
+cairo_private cairo_bool_t
+_cairo_gstate_is_group (cairo_gstate_t *gstate);
+
+cairo_private cairo_status_t
+_cairo_gstate_redirect_target (cairo_gstate_t *gstate, cairo_surface_t *child);
+
+cairo_private cairo_surface_t *
+_cairo_gstate_get_target (cairo_gstate_t *gstate);
+
+cairo_private cairo_surface_t *
+_cairo_gstate_get_original_target (cairo_gstate_t *gstate);
+
+cairo_private cairo_clip_t *
+_cairo_gstate_get_clip (cairo_gstate_t *gstate);
+
+cairo_private cairo_status_t
+_cairo_gstate_set_source (cairo_gstate_t *gstate, cairo_pattern_t *source);
+
+cairo_private cairo_pattern_t *
+_cairo_gstate_get_source (cairo_gstate_t *gstate);
+
+cairo_private cairo_status_t
+_cairo_gstate_set_operator (cairo_gstate_t *gstate, cairo_operator_t op);
+
+cairo_private cairo_operator_t
+_cairo_gstate_get_operator (cairo_gstate_t *gstate);
+
+cairo_private cairo_status_t
+_cairo_gstate_set_opacity (cairo_gstate_t *gstate, double opacity);
+
+cairo_private double
+_cairo_gstate_get_opacity (cairo_gstate_t *gstate);
+
+cairo_private cairo_status_t
+_cairo_gstate_set_tolerance (cairo_gstate_t *gstate, double tolerance);
+
+cairo_private double
+_cairo_gstate_get_tolerance (cairo_gstate_t *gstate);
+
+cairo_private cairo_status_t
+_cairo_gstate_set_fill_rule (cairo_gstate_t *gstate, cairo_fill_rule_t fill_rule);
+
+cairo_private cairo_fill_rule_t
+_cairo_gstate_get_fill_rule (cairo_gstate_t *gstate);
+
+cairo_private cairo_status_t
+_cairo_gstate_set_line_width (cairo_gstate_t *gstate, double width);
+
+cairo_private double
+_cairo_gstate_get_line_width (cairo_gstate_t *gstate);
+
+cairo_private cairo_status_t
+_cairo_gstate_set_line_cap (cairo_gstate_t *gstate, cairo_line_cap_t line_cap);
+
+cairo_private cairo_line_cap_t
+_cairo_gstate_get_line_cap (cairo_gstate_t *gstate);
+
+cairo_private cairo_status_t
+_cairo_gstate_set_line_join (cairo_gstate_t *gstate, cairo_line_join_t line_join);
+
+cairo_private cairo_line_join_t
+_cairo_gstate_get_line_join (cairo_gstate_t *gstate);
+
+cairo_private cairo_status_t
+_cairo_gstate_set_dash (cairo_gstate_t *gstate, const double *dash, int num_dashes, double offset);
+
+cairo_private void
+_cairo_gstate_get_dash (cairo_gstate_t *gstate, double *dash, int *num_dashes, double *offset);
+
+cairo_private cairo_status_t
+_cairo_gstate_set_miter_limit (cairo_gstate_t *gstate, double limit);
+
+cairo_private double
+_cairo_gstate_get_miter_limit (cairo_gstate_t *gstate);
+
+cairo_private void
+_cairo_gstate_get_matrix (cairo_gstate_t *gstate, cairo_matrix_t *matrix);
+
+cairo_private cairo_status_t
+_cairo_gstate_translate (cairo_gstate_t *gstate, double tx, double ty);
+
+cairo_private cairo_status_t
+_cairo_gstate_scale (cairo_gstate_t *gstate, double sx, double sy);
+
+cairo_private cairo_status_t
+_cairo_gstate_rotate (cairo_gstate_t *gstate, double angle);
+
+cairo_private cairo_status_t
+_cairo_gstate_transform (cairo_gstate_t              *gstate,
+                        const cairo_matrix_t *matrix);
+
+cairo_private cairo_status_t
+_cairo_gstate_set_matrix (cairo_gstate_t       *gstate,
+                         const cairo_matrix_t *matrix);
+
+cairo_private void
+_cairo_gstate_identity_matrix (cairo_gstate_t *gstate);
+
+cairo_private void
+_cairo_gstate_user_to_device (cairo_gstate_t *gstate, double *x, double *y);
+
+cairo_private void
+_cairo_gstate_user_to_device_distance (cairo_gstate_t *gstate, double *dx, double *dy);
+
+cairo_private void
+_cairo_gstate_device_to_user (cairo_gstate_t *gstate, double *x, double *y);
+
+cairo_private void
+_cairo_gstate_device_to_user_distance (cairo_gstate_t *gstate, double *dx, double *dy);
+
+cairo_private void
+_do_cairo_gstate_user_to_backend (cairo_gstate_t *gstate, double *x, double *y);
+
+static inline void
+_cairo_gstate_user_to_backend (cairo_gstate_t *gstate, double *x, double *y)
+{
+    if (! gstate->is_identity)
+       _do_cairo_gstate_user_to_backend (gstate, x, y);
+}
+
+cairo_private void
+_do_cairo_gstate_backend_to_user (cairo_gstate_t *gstate, double *x, double *y);
+
+static inline void
+_cairo_gstate_backend_to_user (cairo_gstate_t *gstate, double *x, double *y)
+{
+    if (! gstate->is_identity)
+       _do_cairo_gstate_backend_to_user (gstate, x, y);
+}
+
+cairo_private void
+_cairo_gstate_backend_to_user_rectangle (cairo_gstate_t *gstate,
+                                         double *x1, double *y1,
+                                         double *x2, double *y2,
+                                         cairo_bool_t *is_tight);
+
+cairo_private void
+_cairo_gstate_path_extents (cairo_gstate_t     *gstate,
+                           cairo_path_fixed_t *path,
+                           double *x1, double *y1,
+                           double *x2, double *y2);
+
+cairo_private cairo_status_t
+_cairo_gstate_paint (cairo_gstate_t *gstate);
+
+cairo_private cairo_status_t
+_cairo_gstate_mask (cairo_gstate_t  *gstate,
+                   cairo_pattern_t *mask);
+
+cairo_private cairo_status_t
+_cairo_gstate_stroke (cairo_gstate_t *gstate, cairo_path_fixed_t *path);
+
+cairo_private cairo_status_t
+_cairo_gstate_fill (cairo_gstate_t *gstate, cairo_path_fixed_t *path);
+
+cairo_private cairo_status_t
+_cairo_gstate_copy_page (cairo_gstate_t *gstate);
+
+cairo_private cairo_status_t
+_cairo_gstate_show_page (cairo_gstate_t *gstate);
+
+cairo_private cairo_status_t
+_cairo_gstate_stroke_extents (cairo_gstate_t    *gstate,
+                             cairo_path_fixed_t *path,
+                              double *x1, double *y1,
+                             double *x2, double *y2);
+
+cairo_private cairo_status_t
+_cairo_gstate_fill_extents (cairo_gstate_t     *gstate,
+                           cairo_path_fixed_t *path,
+                            double *x1, double *y1,
+                           double *x2, double *y2);
+
+cairo_private cairo_status_t
+_cairo_gstate_in_stroke (cairo_gstate_t            *gstate,
+                        cairo_path_fixed_t *path,
+                        double              x,
+                        double              y,
+                        cairo_bool_t       *inside_ret);
+
+cairo_private cairo_bool_t
+_cairo_gstate_in_fill (cairo_gstate_t    *gstate,
+                      cairo_path_fixed_t *path,
+                      double              x,
+                      double              y);
+
+cairo_private cairo_bool_t
+_cairo_gstate_in_clip (cairo_gstate_t    *gstate,
+                      double              x,
+                      double              y);
+
+cairo_private cairo_status_t
+_cairo_gstate_clip (cairo_gstate_t *gstate, cairo_path_fixed_t *path);
+
+cairo_private cairo_status_t
+_cairo_gstate_reset_clip (cairo_gstate_t *gstate);
+
+cairo_private cairo_bool_t
+_cairo_gstate_clip_extents (cairo_gstate_t *gstate,
+                           double         *x1,
+                           double         *y1,
+                           double         *x2,
+                           double         *y2);
+
+cairo_private cairo_rectangle_list_t*
+_cairo_gstate_copy_clip_rectangle_list (cairo_gstate_t *gstate);
+
+cairo_private cairo_status_t
+_cairo_gstate_show_surface (cairo_gstate_t     *gstate,
+                           cairo_surface_t     *surface,
+                           double               x,
+                           double               y,
+                           double              width,
+                           double              height);
+
+cairo_private cairo_status_t
+_cairo_gstate_set_font_size (cairo_gstate_t *gstate,
+                            double          size);
+
+cairo_private void
+_cairo_gstate_get_font_matrix (cairo_gstate_t *gstate,
+                              cairo_matrix_t *matrix);
+
+cairo_private cairo_status_t
+_cairo_gstate_set_font_matrix (cairo_gstate_t      *gstate,
+                              const cairo_matrix_t *matrix);
+
+cairo_private void
+_cairo_gstate_get_font_options (cairo_gstate_t       *gstate,
+                               cairo_font_options_t *options);
+
+cairo_private void
+_cairo_gstate_set_font_options (cairo_gstate_t            *gstate,
+                               const cairo_font_options_t *options);
+
+cairo_private cairo_status_t
+_cairo_gstate_get_font_face (cairo_gstate_t     *gstate,
+                            cairo_font_face_t **font_face);
+
+cairo_private cairo_status_t
+_cairo_gstate_get_scaled_font (cairo_gstate_t       *gstate,
+                              cairo_scaled_font_t **scaled_font);
+
+cairo_private cairo_status_t
+_cairo_gstate_get_font_extents (cairo_gstate_t *gstate,
+                               cairo_font_extents_t *extents);
+
+cairo_private cairo_status_t
+_cairo_gstate_set_font_face (cairo_gstate_t    *gstate,
+                            cairo_font_face_t *font_face);
+
+cairo_private cairo_status_t
+_cairo_gstate_glyph_extents (cairo_gstate_t *gstate,
+                            const cairo_glyph_t *glyphs,
+                            int num_glyphs,
+                            cairo_text_extents_t *extents);
+
+cairo_private cairo_status_t
+_cairo_gstate_show_text_glyphs (cairo_gstate_t            *gstate,
+                               const cairo_glyph_t        *glyphs,
+                               int                         num_glyphs,
+                               cairo_glyph_text_info_t    *info);
+
+cairo_private cairo_status_t
+_cairo_gstate_glyph_path (cairo_gstate_t      *gstate,
+                         const cairo_glyph_t *glyphs,
+                         int                  num_glyphs,
+                         cairo_path_fixed_t  *path);
+
+cairo_private cairo_status_t
+_cairo_gstate_set_antialias (cairo_gstate_t *gstate,
+                            cairo_antialias_t antialias);
+
+cairo_private cairo_antialias_t
+_cairo_gstate_get_antialias (cairo_gstate_t *gstate);
+
+#endif /* CAIRO_GSTATE_PRIVATE_H */
diff --git a/src/cairo-gstate.c b/src/cairo-gstate.c
new file mode 100644 (file)
index 0000000..d232f9b
--- /dev/null
@@ -0,0 +1,2291 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *     Carl D. Worth <cworth@cworth.org>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-clip-private.h"
+#include "cairo-error-private.h"
+#include "cairo-gstate-private.h"
+#include "cairo-pattern-private.h"
+#include "cairo-traps-private.h"
+
+#if _XOPEN_SOURCE >= 600 || defined (_ISOC99_SOURCE)
+#define ISFINITE(x) isfinite (x)
+#else
+#define ISFINITE(x) ((x) * (x) >= 0.) /* check for NaNs */
+#endif
+
+static cairo_status_t
+_cairo_gstate_init_copy (cairo_gstate_t *gstate, cairo_gstate_t *other);
+
+static cairo_status_t
+_cairo_gstate_ensure_font_face (cairo_gstate_t *gstate);
+
+static cairo_status_t
+_cairo_gstate_ensure_scaled_font (cairo_gstate_t *gstate);
+
+static void
+_cairo_gstate_unset_scaled_font (cairo_gstate_t *gstate);
+
+static void
+_cairo_gstate_transform_glyphs_to_backend (cairo_gstate_t      *gstate,
+                                           const cairo_glyph_t *glyphs,
+                                           int                  num_glyphs,
+                                          const cairo_text_cluster_t   *clusters,
+                                          int                   num_clusters,
+                                          cairo_text_cluster_flags_t cluster_flags,
+                                           cairo_glyph_t       *transformed_glyphs,
+                                          int                  *num_transformed_glyphs,
+                                          cairo_text_cluster_t *transformed_clusters);
+
+static void
+_cairo_gstate_update_device_transform (cairo_observer_t *observer,
+                                      void *arg)
+{
+    cairo_gstate_t *gstate = cairo_container_of (observer,
+                                                cairo_gstate_t,
+                                                device_transform_observer);
+
+    gstate->is_identity = (_cairo_matrix_is_identity (&gstate->ctm) &&
+                          _cairo_matrix_is_identity (&gstate->target->device_transform));
+}
+
+cairo_status_t
+_cairo_gstate_init (cairo_gstate_t  *gstate,
+                   cairo_surface_t *target)
+{
+    VG (VALGRIND_MAKE_MEM_UNDEFINED (gstate, sizeof (cairo_gstate_t)));
+
+    gstate->next = NULL;
+
+    gstate->op = CAIRO_GSTATE_OPERATOR_DEFAULT;
+    gstate->opacity = 1.;
+
+    gstate->tolerance = CAIRO_GSTATE_TOLERANCE_DEFAULT;
+    gstate->antialias = CAIRO_ANTIALIAS_DEFAULT;
+
+    _cairo_stroke_style_init (&gstate->stroke_style);
+
+    gstate->fill_rule = CAIRO_GSTATE_FILL_RULE_DEFAULT;
+
+    gstate->font_face = NULL;
+    gstate->scaled_font = NULL;
+    gstate->previous_scaled_font = NULL;
+
+    cairo_matrix_init_scale (&gstate->font_matrix,
+                            CAIRO_GSTATE_DEFAULT_FONT_SIZE,
+                            CAIRO_GSTATE_DEFAULT_FONT_SIZE);
+
+    _cairo_font_options_init_default (&gstate->font_options);
+
+    gstate->clip = NULL;
+
+    gstate->target = cairo_surface_reference (target);
+    gstate->parent_target = NULL;
+    gstate->original_target = cairo_surface_reference (target);
+
+    gstate->device_transform_observer.callback = _cairo_gstate_update_device_transform;
+    cairo_list_add (&gstate->device_transform_observer.link,
+                   &gstate->target->device_transform_observers);
+
+    gstate->is_identity = _cairo_matrix_is_identity (&gstate->target->device_transform);
+    cairo_matrix_init_identity (&gstate->ctm);
+    gstate->ctm_inverse = gstate->ctm;
+    gstate->source_ctm_inverse = gstate->ctm;
+
+    gstate->source = (cairo_pattern_t *) &_cairo_pattern_black.base;
+
+    /* Now that the gstate is fully initialized and ready for the eventual
+     * _cairo_gstate_fini(), we can check for errors (and not worry about
+     * the resource deallocation). */
+    return target->status;
+}
+
+/**
+ * _cairo_gstate_init_copy:
+ *
+ * Initialize @gstate by performing a deep copy of state fields from
+ * @other. Note that gstate->next is not copied but is set to %NULL by
+ * this function.
+ **/
+static cairo_status_t
+_cairo_gstate_init_copy (cairo_gstate_t *gstate, cairo_gstate_t *other)
+{
+    cairo_status_t status;
+
+    VG (VALGRIND_MAKE_MEM_UNDEFINED (gstate, sizeof (cairo_gstate_t)));
+
+    gstate->op = other->op;
+    gstate->opacity = other->opacity;
+
+    gstate->tolerance = other->tolerance;
+    gstate->antialias = other->antialias;
+
+    status = _cairo_stroke_style_init_copy (&gstate->stroke_style,
+                                           &other->stroke_style);
+    if (unlikely (status))
+       return status;
+
+    gstate->fill_rule = other->fill_rule;
+
+    gstate->font_face = cairo_font_face_reference (other->font_face);
+    gstate->scaled_font = cairo_scaled_font_reference (other->scaled_font);
+    gstate->previous_scaled_font = cairo_scaled_font_reference (other->previous_scaled_font);
+
+    gstate->font_matrix = other->font_matrix;
+
+    _cairo_font_options_init_copy (&gstate->font_options , &other->font_options);
+
+    gstate->clip = _cairo_clip_copy (other->clip);
+
+    gstate->target = cairo_surface_reference (other->target);
+    /* parent_target is always set to NULL; it's only ever set by redirect_target */
+    gstate->parent_target = NULL;
+    gstate->original_target = cairo_surface_reference (other->original_target);
+
+    gstate->device_transform_observer.callback = _cairo_gstate_update_device_transform;
+    cairo_list_add (&gstate->device_transform_observer.link,
+                   &gstate->target->device_transform_observers);
+
+    gstate->is_identity = other->is_identity;
+    gstate->ctm = other->ctm;
+    gstate->ctm_inverse = other->ctm_inverse;
+    gstate->source_ctm_inverse = other->source_ctm_inverse;
+
+    gstate->source = cairo_pattern_reference (other->source);
+
+    gstate->next = NULL;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+void
+_cairo_gstate_fini (cairo_gstate_t *gstate)
+{
+    _cairo_stroke_style_fini (&gstate->stroke_style);
+
+    cairo_font_face_destroy (gstate->font_face);
+    gstate->font_face = NULL;
+
+    cairo_scaled_font_destroy (gstate->previous_scaled_font);
+    gstate->previous_scaled_font = NULL;
+
+    cairo_scaled_font_destroy (gstate->scaled_font);
+    gstate->scaled_font = NULL;
+
+    _cairo_clip_destroy (gstate->clip);
+
+    cairo_list_del (&gstate->device_transform_observer.link);
+
+    cairo_surface_destroy (gstate->target);
+    gstate->target = NULL;
+
+    cairo_surface_destroy (gstate->parent_target);
+    gstate->parent_target = NULL;
+
+    cairo_surface_destroy (gstate->original_target);
+    gstate->original_target = NULL;
+
+    cairo_pattern_destroy (gstate->source);
+    gstate->source = NULL;
+
+    VG (VALGRIND_MAKE_MEM_NOACCESS (gstate, sizeof (cairo_gstate_t)));
+}
+
+/**
+ * _cairo_gstate_save:
+ * @gstate: input/output gstate pointer
+ *
+ * Makes a copy of the current state of @gstate and saves it
+ * to @gstate->next, then put the address of the newly allcated
+ * copy into @gstate.  _cairo_gstate_restore() reverses this.
+ **/
+cairo_status_t
+_cairo_gstate_save (cairo_gstate_t **gstate, cairo_gstate_t **freelist)
+{
+    cairo_gstate_t *top;
+    cairo_status_t status;
+
+    if (CAIRO_INJECT_FAULT ())
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    top = *freelist;
+    if (top == NULL) {
+       top = malloc (sizeof (cairo_gstate_t));
+       if (unlikely (top == NULL))
+           return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+    } else
+       *freelist = top->next;
+
+    status = _cairo_gstate_init_copy (top, *gstate);
+    if (unlikely (status)) {
+       top->next = *freelist;
+       *freelist = top;
+       return status;
+    }
+
+    top->next = *gstate;
+    *gstate = top;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+/**
+ * _cairo_gstate_restore:
+ * @gstate: input/output gstate pointer
+ *
+ * Reverses the effects of one _cairo_gstate_save() call.
+ **/
+cairo_status_t
+_cairo_gstate_restore (cairo_gstate_t **gstate, cairo_gstate_t **freelist)
+{
+    cairo_gstate_t *top;
+
+    top = *gstate;
+    if (top->next == NULL)
+       return _cairo_error (CAIRO_STATUS_INVALID_RESTORE);
+
+    *gstate = top->next;
+
+    _cairo_gstate_fini (top);
+    VG (VALGRIND_MAKE_MEM_UNDEFINED (&top->next, sizeof (cairo_gstate_t *)));
+    top->next = *freelist;
+    *freelist = top;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+/**
+ * _cairo_gstate_redirect_target:
+ * @gstate: a #cairo_gstate_t
+ * @child: the new child target
+ *
+ * Redirect @gstate rendering to a "child" target. The original
+ * "parent" target with which the gstate was created will not be
+ * affected. See _cairo_gstate_get_target().
+ **/
+cairo_status_t
+_cairo_gstate_redirect_target (cairo_gstate_t *gstate, cairo_surface_t *child)
+{
+    /* If this gstate is already redirected, this is an error; we need a
+     * new gstate to be able to redirect */
+    assert (gstate->parent_target == NULL);
+
+    /* Set up our new parent_target based on our current target;
+     * gstate->parent_target will take the ref that is held by gstate->target
+     */
+    gstate->parent_target = gstate->target;
+
+    /* Now set up our new target; we overwrite gstate->target directly,
+     * since its ref is now owned by gstate->parent_target */
+    gstate->target = cairo_surface_reference (child);
+    gstate->is_identity &= _cairo_matrix_is_identity (&child->device_transform);
+    cairo_list_move (&gstate->device_transform_observer.link,
+                    &gstate->target->device_transform_observers);
+
+    /* The clip is in surface backend coordinates for the previous target;
+     * translate it into the child's backend coordinates. */
+    _cairo_clip_destroy (gstate->clip);
+    gstate->clip = _cairo_clip_copy_with_translation (gstate->next->clip,
+                                                     child->device_transform.x0 - gstate->parent_target->device_transform.x0,
+                                                     child->device_transform.y0 - gstate->parent_target->device_transform.y0);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+/**
+ * _cairo_gstate_is_group
+ * @gstate: a #cairo_gstate_t
+ *
+ * Check if _cairo_gstate_redirect_target has been called on the head
+ * of the stack.
+ *
+ * Return value: %TRUE if @gstate is redirected to a target different
+ * than the previous state in the stack, %FALSE otherwise.
+ **/
+cairo_bool_t
+_cairo_gstate_is_group (cairo_gstate_t *gstate)
+{
+    return gstate->parent_target != NULL;
+}
+
+/**
+ * _cairo_gstate_get_target:
+ * @gstate: a #cairo_gstate_t
+ *
+ * Return the current drawing target; if drawing is not redirected,
+ * this will be the same as _cairo_gstate_get_original_target().
+ *
+ * Return value: the current target surface
+ **/
+cairo_surface_t *
+_cairo_gstate_get_target (cairo_gstate_t *gstate)
+{
+    return gstate->target;
+}
+
+/**
+ * _cairo_gstate_get_original_target:
+ * @gstate: a #cairo_gstate_t
+ *
+ * Return the original target with which @gstate was created. This
+ * function always returns the original target independent of any
+ * child target that may have been set with
+ * _cairo_gstate_redirect_target.
+ *
+ * Return value: the original target surface
+ **/
+cairo_surface_t *
+_cairo_gstate_get_original_target (cairo_gstate_t *gstate)
+{
+    return gstate->original_target;
+}
+
+/**
+ * _cairo_gstate_get_clip:
+ * @gstate: a #cairo_gstate_t
+ *
+ * This space left intentionally blank.
+ *
+ * Return value: a pointer to the gstate's #cairo_clip_t structure.
+ */
+cairo_clip_t *
+_cairo_gstate_get_clip (cairo_gstate_t *gstate)
+{
+    return gstate->clip;
+}
+
+cairo_status_t
+_cairo_gstate_set_source (cairo_gstate_t  *gstate,
+                         cairo_pattern_t *source)
+{
+    if (source->status)
+       return source->status;
+
+    source = cairo_pattern_reference (source);
+    cairo_pattern_destroy (gstate->source);
+    gstate->source = source;
+    gstate->source_ctm_inverse = gstate->ctm_inverse;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_pattern_t *
+_cairo_gstate_get_source (cairo_gstate_t *gstate)
+{
+    if (gstate->source == &_cairo_pattern_black.base) {
+       /* do not expose the static object to the user */
+        gstate->source = _cairo_pattern_create_solid (CAIRO_COLOR_BLACK);
+    }
+
+    return gstate->source;
+}
+
+cairo_status_t
+_cairo_gstate_set_operator (cairo_gstate_t *gstate, cairo_operator_t op)
+{
+    gstate->op = op;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_operator_t
+_cairo_gstate_get_operator (cairo_gstate_t *gstate)
+{
+    return gstate->op;
+}
+
+cairo_status_t
+_cairo_gstate_set_opacity (cairo_gstate_t *gstate, double op)
+{
+    gstate->opacity = op;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+double
+_cairo_gstate_get_opacity (cairo_gstate_t *gstate)
+{
+    return gstate->opacity;
+}
+
+cairo_status_t
+_cairo_gstate_set_tolerance (cairo_gstate_t *gstate, double tolerance)
+{
+    gstate->tolerance = tolerance;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+double
+_cairo_gstate_get_tolerance (cairo_gstate_t *gstate)
+{
+    return gstate->tolerance;
+}
+
+cairo_status_t
+_cairo_gstate_set_fill_rule (cairo_gstate_t *gstate, cairo_fill_rule_t fill_rule)
+{
+    gstate->fill_rule = fill_rule;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_fill_rule_t
+_cairo_gstate_get_fill_rule (cairo_gstate_t *gstate)
+{
+    return gstate->fill_rule;
+}
+
+cairo_status_t
+_cairo_gstate_set_line_width (cairo_gstate_t *gstate, double width)
+{
+    gstate->stroke_style.line_width = width;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+double
+_cairo_gstate_get_line_width (cairo_gstate_t *gstate)
+{
+    return gstate->stroke_style.line_width;
+}
+
+cairo_status_t
+_cairo_gstate_set_line_cap (cairo_gstate_t *gstate, cairo_line_cap_t line_cap)
+{
+    gstate->stroke_style.line_cap = line_cap;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_line_cap_t
+_cairo_gstate_get_line_cap (cairo_gstate_t *gstate)
+{
+    return gstate->stroke_style.line_cap;
+}
+
+cairo_status_t
+_cairo_gstate_set_line_join (cairo_gstate_t *gstate, cairo_line_join_t line_join)
+{
+    gstate->stroke_style.line_join = line_join;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_line_join_t
+_cairo_gstate_get_line_join (cairo_gstate_t *gstate)
+{
+    return gstate->stroke_style.line_join;
+}
+
+cairo_status_t
+_cairo_gstate_set_dash (cairo_gstate_t *gstate, const double *dash, int num_dashes, double offset)
+{
+    unsigned int i;
+    double dash_total;
+
+    free (gstate->stroke_style.dash);
+
+    gstate->stroke_style.num_dashes = num_dashes;
+
+    if (gstate->stroke_style.num_dashes == 0) {
+       gstate->stroke_style.dash = NULL;
+       gstate->stroke_style.dash_offset = 0.0;
+       return CAIRO_STATUS_SUCCESS;
+    }
+
+    gstate->stroke_style.dash = _cairo_malloc_ab (gstate->stroke_style.num_dashes, sizeof (double));
+    if (unlikely (gstate->stroke_style.dash == NULL)) {
+       gstate->stroke_style.num_dashes = 0;
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+    }
+
+    memcpy (gstate->stroke_style.dash, dash, gstate->stroke_style.num_dashes * sizeof (double));
+
+    dash_total = 0.0;
+    for (i = 0; i < gstate->stroke_style.num_dashes; i++) {
+       if (gstate->stroke_style.dash[i] < 0)
+           return _cairo_error (CAIRO_STATUS_INVALID_DASH);
+
+       dash_total += gstate->stroke_style.dash[i];
+    }
+
+    if (dash_total == 0.0)
+       return _cairo_error (CAIRO_STATUS_INVALID_DASH);
+
+    /* An odd dash value indicate symmetric repeating, so the total
+     * is twice as long. */
+    if (gstate->stroke_style.num_dashes & 1)
+       dash_total *= 2;
+
+    /* The dashing code doesn't like a negative offset or a big positive
+     * offset, so we compute an equivalent offset which is guaranteed to be
+     * positive and less than twice the pattern length. */
+    offset = fmod (offset, dash_total);
+    if (offset < 0.0)
+       offset += dash_total;
+    if (offset <= 0.0)         /* Take care of -0 */
+       offset = 0.0;
+    gstate->stroke_style.dash_offset = offset;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+void
+_cairo_gstate_get_dash (cairo_gstate_t *gstate,
+                       double         *dashes,
+                       int            *num_dashes,
+                       double         *offset)
+{
+    if (dashes) {
+       memcpy (dashes,
+               gstate->stroke_style.dash,
+               sizeof (double) * gstate->stroke_style.num_dashes);
+    }
+
+    if (num_dashes)
+       *num_dashes = gstate->stroke_style.num_dashes;
+
+    if (offset)
+       *offset = gstate->stroke_style.dash_offset;
+}
+
+cairo_status_t
+_cairo_gstate_set_miter_limit (cairo_gstate_t *gstate, double limit)
+{
+    gstate->stroke_style.miter_limit = limit;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+double
+_cairo_gstate_get_miter_limit (cairo_gstate_t *gstate)
+{
+    return gstate->stroke_style.miter_limit;
+}
+
+void
+_cairo_gstate_get_matrix (cairo_gstate_t *gstate, cairo_matrix_t *matrix)
+{
+    *matrix = gstate->ctm;
+}
+
+cairo_status_t
+_cairo_gstate_translate (cairo_gstate_t *gstate, double tx, double ty)
+{
+    cairo_matrix_t tmp;
+
+    if (! ISFINITE (tx) || ! ISFINITE (ty))
+       return _cairo_error (CAIRO_STATUS_INVALID_MATRIX);
+
+    _cairo_gstate_unset_scaled_font (gstate);
+
+    cairo_matrix_init_translate (&tmp, tx, ty);
+    cairo_matrix_multiply (&gstate->ctm, &tmp, &gstate->ctm);
+    gstate->is_identity = FALSE;
+
+    /* paranoid check against gradual numerical instability */
+    if (! _cairo_matrix_is_invertible (&gstate->ctm))
+       return _cairo_error (CAIRO_STATUS_INVALID_MATRIX);
+
+    cairo_matrix_init_translate (&tmp, -tx, -ty);
+    cairo_matrix_multiply (&gstate->ctm_inverse, &gstate->ctm_inverse, &tmp);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_status_t
+_cairo_gstate_scale (cairo_gstate_t *gstate, double sx, double sy)
+{
+    cairo_matrix_t tmp;
+
+    if (sx * sy == 0.) /* either sx or sy is 0, or det == 0 due to underflow */
+       return _cairo_error (CAIRO_STATUS_INVALID_MATRIX);
+    if (! ISFINITE (sx) || ! ISFINITE (sy))
+       return _cairo_error (CAIRO_STATUS_INVALID_MATRIX);
+
+    _cairo_gstate_unset_scaled_font (gstate);
+
+    cairo_matrix_init_scale (&tmp, sx, sy);
+    cairo_matrix_multiply (&gstate->ctm, &tmp, &gstate->ctm);
+    gstate->is_identity = FALSE;
+
+    /* paranoid check against gradual numerical instability */
+    if (! _cairo_matrix_is_invertible (&gstate->ctm))
+       return _cairo_error (CAIRO_STATUS_INVALID_MATRIX);
+
+    cairo_matrix_init_scale (&tmp, 1/sx, 1/sy);
+    cairo_matrix_multiply (&gstate->ctm_inverse, &gstate->ctm_inverse, &tmp);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_status_t
+_cairo_gstate_rotate (cairo_gstate_t *gstate, double angle)
+{
+    cairo_matrix_t tmp;
+
+    if (angle == 0.)
+       return CAIRO_STATUS_SUCCESS;
+
+    if (! ISFINITE (angle))
+       return _cairo_error (CAIRO_STATUS_INVALID_MATRIX);
+
+    _cairo_gstate_unset_scaled_font (gstate);
+
+    cairo_matrix_init_rotate (&tmp, angle);
+    cairo_matrix_multiply (&gstate->ctm, &tmp, &gstate->ctm);
+    gstate->is_identity = FALSE;
+
+    /* paranoid check against gradual numerical instability */
+    if (! _cairo_matrix_is_invertible (&gstate->ctm))
+       return _cairo_error (CAIRO_STATUS_INVALID_MATRIX);
+
+    cairo_matrix_init_rotate (&tmp, -angle);
+    cairo_matrix_multiply (&gstate->ctm_inverse, &gstate->ctm_inverse, &tmp);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_status_t
+_cairo_gstate_transform (cairo_gstate_t              *gstate,
+                        const cairo_matrix_t *matrix)
+{
+    cairo_matrix_t tmp;
+    cairo_status_t status;
+
+    if (! _cairo_matrix_is_invertible (matrix))
+       return _cairo_error (CAIRO_STATUS_INVALID_MATRIX);
+
+    if (_cairo_matrix_is_identity (matrix))
+       return CAIRO_STATUS_SUCCESS;
+
+    tmp = *matrix;
+    status = cairo_matrix_invert (&tmp);
+    if (unlikely (status))
+       return status;
+
+    _cairo_gstate_unset_scaled_font (gstate);
+
+    cairo_matrix_multiply (&gstate->ctm, matrix, &gstate->ctm);
+    cairo_matrix_multiply (&gstate->ctm_inverse, &gstate->ctm_inverse, &tmp);
+    gstate->is_identity = FALSE;
+
+    /* paranoid check against gradual numerical instability */
+    if (! _cairo_matrix_is_invertible (&gstate->ctm))
+       return _cairo_error (CAIRO_STATUS_INVALID_MATRIX);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_status_t
+_cairo_gstate_set_matrix (cairo_gstate_t       *gstate,
+                         const cairo_matrix_t *matrix)
+{
+    cairo_status_t status;
+
+    if (memcmp (matrix, &gstate->ctm, sizeof (cairo_matrix_t)) == 0)
+       return CAIRO_STATUS_SUCCESS;
+
+    if (! _cairo_matrix_is_invertible (matrix))
+       return _cairo_error (CAIRO_STATUS_INVALID_MATRIX);
+
+    if (_cairo_matrix_is_identity (matrix)) {
+       _cairo_gstate_identity_matrix (gstate);
+       return CAIRO_STATUS_SUCCESS;
+    }
+
+    _cairo_gstate_unset_scaled_font (gstate);
+
+    gstate->ctm = *matrix;
+    gstate->ctm_inverse = *matrix;
+    status = cairo_matrix_invert (&gstate->ctm_inverse);
+    assert (status == CAIRO_STATUS_SUCCESS);
+    gstate->is_identity = FALSE;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+void
+_cairo_gstate_identity_matrix (cairo_gstate_t *gstate)
+{
+    if (_cairo_matrix_is_identity (&gstate->ctm))
+       return;
+
+    _cairo_gstate_unset_scaled_font (gstate);
+
+    cairo_matrix_init_identity (&gstate->ctm);
+    cairo_matrix_init_identity (&gstate->ctm_inverse);
+    gstate->is_identity = _cairo_matrix_is_identity (&gstate->target->device_transform);
+}
+
+void
+_cairo_gstate_user_to_device (cairo_gstate_t *gstate, double *x, double *y)
+{
+    cairo_matrix_transform_point (&gstate->ctm, x, y);
+}
+
+void
+_cairo_gstate_user_to_device_distance (cairo_gstate_t *gstate,
+                                      double *dx, double *dy)
+{
+    cairo_matrix_transform_distance (&gstate->ctm, dx, dy);
+}
+
+void
+_cairo_gstate_device_to_user (cairo_gstate_t *gstate, double *x, double *y)
+{
+    cairo_matrix_transform_point (&gstate->ctm_inverse, x, y);
+}
+
+void
+_cairo_gstate_device_to_user_distance (cairo_gstate_t *gstate,
+                                      double *dx, double *dy)
+{
+    cairo_matrix_transform_distance (&gstate->ctm_inverse, dx, dy);
+}
+
+void
+_do_cairo_gstate_user_to_backend (cairo_gstate_t *gstate, double *x, double *y)
+{
+    cairo_matrix_transform_point (&gstate->ctm, x, y);
+    cairo_matrix_transform_point (&gstate->target->device_transform, x, y);
+}
+
+void
+_do_cairo_gstate_backend_to_user (cairo_gstate_t *gstate, double *x, double *y)
+{
+    cairo_matrix_transform_point (&gstate->target->device_transform_inverse, x, y);
+    cairo_matrix_transform_point (&gstate->ctm_inverse, x, y);
+}
+
+void
+_cairo_gstate_backend_to_user_rectangle (cairo_gstate_t *gstate,
+                                         double *x1, double *y1,
+                                         double *x2, double *y2,
+                                         cairo_bool_t *is_tight)
+{
+    cairo_matrix_t matrix_inverse;
+
+    if (! _cairo_matrix_is_identity (&gstate->target->device_transform_inverse) ||
+    ! _cairo_matrix_is_identity (&gstate->ctm_inverse))
+    {
+       cairo_matrix_multiply (&matrix_inverse,
+                              &gstate->target->device_transform_inverse,
+                              &gstate->ctm_inverse);
+       _cairo_matrix_transform_bounding_box (&matrix_inverse,
+                                             x1, y1, x2, y2, is_tight);
+    }
+
+    else
+    {
+       if (is_tight)
+           *is_tight = TRUE;
+    }
+}
+
+/* XXX: NYI
+cairo_status_t
+_cairo_gstate_stroke_to_path (cairo_gstate_t *gstate)
+{
+    cairo_status_t status;
+
+    _cairo_pen_init (&gstate);
+    return CAIRO_STATUS_SUCCESS;
+}
+*/
+
+void
+_cairo_gstate_path_extents (cairo_gstate_t     *gstate,
+                           cairo_path_fixed_t *path,
+                           double *x1, double *y1,
+                           double *x2, double *y2)
+{
+    cairo_box_t box;
+    double px1, py1, px2, py2;
+
+    if (_cairo_path_fixed_extents (path, &box)) {
+       px1 = _cairo_fixed_to_double (box.p1.x);
+       py1 = _cairo_fixed_to_double (box.p1.y);
+       px2 = _cairo_fixed_to_double (box.p2.x);
+       py2 = _cairo_fixed_to_double (box.p2.y);
+
+       _cairo_gstate_backend_to_user_rectangle (gstate,
+                                                &px1, &py1, &px2, &py2,
+                                                NULL);
+    } else {
+       px1 = 0.0;
+       py1 = 0.0;
+       px2 = 0.0;
+       py2 = 0.0;
+    }
+
+    if (x1)
+       *x1 = px1;
+    if (y1)
+       *y1 = py1;
+    if (x2)
+       *x2 = px2;
+    if (y2)
+       *y2 = py2;
+}
+
+static void
+_cairo_gstate_copy_pattern (cairo_pattern_t *pattern,
+                           const cairo_pattern_t *original)
+{
+    /* First check if the we can replace the original with a much simpler
+     * pattern. For example, gradients that are uniform or just have a single
+     * stop can sometimes be replaced with a solid.
+     */
+
+    if (_cairo_pattern_is_clear (original)) {
+        _cairo_pattern_init_solid ((cairo_solid_pattern_t *) pattern,
+                                  CAIRO_COLOR_TRANSPARENT);
+       return;
+    }
+
+    if (original->type == CAIRO_PATTERN_TYPE_LINEAR ||
+       original->type == CAIRO_PATTERN_TYPE_RADIAL)
+    {
+        cairo_color_t color;
+       if (_cairo_gradient_pattern_is_solid ((cairo_gradient_pattern_t *) original,
+                                             NULL,
+                                             &color))
+       {
+           _cairo_pattern_init_solid ((cairo_solid_pattern_t *) pattern,
+                                      &color);
+           return;
+       }
+    }
+
+    _cairo_pattern_init_static_copy (pattern, original);
+}
+
+static void
+_cairo_gstate_copy_transformed_pattern (cairo_gstate_t  *gstate,
+                                       cairo_pattern_t *pattern,
+                                       const cairo_pattern_t *original,
+                                       const cairo_matrix_t  *ctm_inverse)
+{
+    _cairo_gstate_copy_pattern (pattern, original);
+
+    /* apply device_transform first so that it is transformed by ctm_inverse */
+    if (original->type == CAIRO_PATTERN_TYPE_SURFACE) {
+       cairo_surface_pattern_t *surface_pattern;
+       cairo_surface_t *surface;
+
+        surface_pattern = (cairo_surface_pattern_t *) original;
+        surface = surface_pattern->surface;
+
+       if (_cairo_surface_has_device_transform (surface))
+           _cairo_pattern_transform (pattern, &surface->device_transform);
+    }
+
+    if (! _cairo_matrix_is_identity (ctm_inverse))
+       _cairo_pattern_transform (pattern, ctm_inverse);
+
+    if (_cairo_surface_has_device_transform (gstate->target)) {
+        _cairo_pattern_transform (pattern,
+                                  &gstate->target->device_transform_inverse);
+    }
+}
+
+static void
+_cairo_gstate_copy_transformed_source (cairo_gstate_t   *gstate,
+                                      cairo_pattern_t  *pattern)
+{
+    _cairo_gstate_copy_transformed_pattern (gstate, pattern,
+                                           gstate->source,
+                                           &gstate->source_ctm_inverse);
+}
+
+static void
+_cairo_gstate_copy_transformed_mask (cairo_gstate_t   *gstate,
+                                    cairo_pattern_t  *pattern,
+                                    cairo_pattern_t  *mask)
+{
+    _cairo_gstate_copy_transformed_pattern (gstate, pattern,
+                                           mask,
+                                           &gstate->ctm_inverse);
+}
+
+static cairo_operator_t
+_reduce_op (cairo_gstate_t *gstate)
+{
+    cairo_operator_t op;
+    const cairo_pattern_t *pattern;
+
+    op = gstate->op;
+    if (op != CAIRO_OPERATOR_SOURCE)
+       return op;
+
+    pattern = gstate->source;
+    if (pattern->type == CAIRO_PATTERN_TYPE_SOLID) {
+       const cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) pattern;
+       if (solid->color.alpha_short <= 0x00ff) {
+           op = CAIRO_OPERATOR_CLEAR;
+       } else if ((gstate->target->content & CAIRO_CONTENT_ALPHA) == 0) {
+           if ((solid->color.red_short |
+                solid->color.green_short |
+                solid->color.blue_short) <= 0x00ff)
+           {
+               op = CAIRO_OPERATOR_CLEAR;
+           }
+       }
+    } else if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) {
+       const cairo_surface_pattern_t *surface = (cairo_surface_pattern_t *) pattern;
+       if (surface->surface->is_clear &&
+           surface->surface->content & CAIRO_CONTENT_ALPHA)
+       {
+           op = CAIRO_OPERATOR_CLEAR;
+       }
+    } else {
+       const cairo_gradient_pattern_t *gradient = (cairo_gradient_pattern_t *) pattern;
+       if (gradient->n_stops == 0)
+           op = CAIRO_OPERATOR_CLEAR;
+    }
+
+    return op;
+}
+
+static cairo_status_t
+_cairo_gstate_get_pattern_status (const cairo_pattern_t *pattern)
+{
+    if (unlikely (pattern->type == CAIRO_PATTERN_TYPE_MESH &&
+                 ((const cairo_mesh_pattern_t *) pattern)->current_patch))
+    {
+       /* If current patch != NULL, the pattern is under construction
+        * and cannot be used as a source */
+       return CAIRO_STATUS_INVALID_MESH_CONSTRUCTION;
+    }
+
+    return pattern->status;
+}
+
+cairo_status_t
+_cairo_gstate_paint (cairo_gstate_t *gstate)
+{
+    cairo_pattern_union_t source_pattern;
+    const cairo_pattern_t *pattern;
+    cairo_status_t status;
+    cairo_operator_t op;
+
+    status = _cairo_gstate_get_pattern_status (gstate->source);
+    if (unlikely (status))
+       return status;
+
+    if (gstate->op == CAIRO_OPERATOR_DEST)
+       return CAIRO_STATUS_SUCCESS;
+
+    if (_cairo_clip_is_all_clipped (gstate->clip))
+       return CAIRO_STATUS_SUCCESS;
+
+    op = _reduce_op (gstate);
+    if (op == CAIRO_OPERATOR_CLEAR) {
+       pattern = &_cairo_pattern_clear.base;
+    } else {
+       _cairo_gstate_copy_transformed_source (gstate, &source_pattern.base);
+       pattern = &source_pattern.base;
+    }
+
+    return _cairo_surface_paint (gstate->target,
+                                op, pattern,
+                                gstate->clip);
+}
+
+cairo_status_t
+_cairo_gstate_mask (cairo_gstate_t  *gstate,
+                   cairo_pattern_t *mask)
+{
+    cairo_pattern_union_t source_pattern, mask_pattern;
+    const cairo_pattern_t *source;
+    cairo_operator_t op;
+    cairo_status_t status;
+
+    status = _cairo_gstate_get_pattern_status (mask);
+    if (unlikely (status))
+       return status;
+
+    status = _cairo_gstate_get_pattern_status (gstate->source);
+    if (unlikely (status))
+       return status;
+
+    if (gstate->op == CAIRO_OPERATOR_DEST)
+       return CAIRO_STATUS_SUCCESS;
+
+    if (_cairo_clip_is_all_clipped (gstate->clip))
+       return CAIRO_STATUS_SUCCESS;
+
+    assert (gstate->opacity == 1.0);
+
+    if (_cairo_pattern_is_opaque (mask, NULL))
+       return _cairo_gstate_paint (gstate);
+
+    if (_cairo_pattern_is_clear (mask) &&
+       _cairo_operator_bounded_by_mask (gstate->op))
+    {
+       return CAIRO_STATUS_SUCCESS;
+    }
+
+    op = _reduce_op (gstate);
+    if (op == CAIRO_OPERATOR_CLEAR) {
+       source = &_cairo_pattern_clear.base;
+    } else {
+       _cairo_gstate_copy_transformed_source (gstate, &source_pattern.base);
+       source = &source_pattern.base;
+    }
+    _cairo_gstate_copy_transformed_mask (gstate, &mask_pattern.base, mask);
+
+    if (source->type == CAIRO_PATTERN_TYPE_SOLID &&
+       mask_pattern.base.type == CAIRO_PATTERN_TYPE_SOLID &&
+       _cairo_operator_bounded_by_source (op))
+    {
+       const cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) source;
+       cairo_color_t combined;
+
+       if (mask_pattern.base.has_component_alpha) {
+#define M(R, A, B, c) R.c = A.c * B.c
+           M(combined, solid->color, mask_pattern.solid.color, red);
+           M(combined, solid->color, mask_pattern.solid.color, green);
+           M(combined, solid->color, mask_pattern.solid.color, blue);
+           M(combined, solid->color, mask_pattern.solid.color, alpha);
+#undef M
+       } else {
+           combined = solid->color;
+           _cairo_color_multiply_alpha (&combined, mask_pattern.solid.color.alpha);
+       }
+
+       _cairo_pattern_init_solid (&source_pattern.solid, &combined);
+
+       status = _cairo_surface_paint (gstate->target, op,
+                                      &source_pattern.base,
+                                      gstate->clip);
+    }
+    else
+    {
+       status = _cairo_surface_mask (gstate->target, op,
+                                     source,
+                                     &mask_pattern.base,
+                                     gstate->clip);
+    }
+
+    return status;
+}
+
+cairo_status_t
+_cairo_gstate_stroke (cairo_gstate_t *gstate, cairo_path_fixed_t *path)
+{
+    cairo_pattern_union_t source_pattern;
+    cairo_stroke_style_t style;
+    double dash[2];
+    cairo_status_t status;
+
+    status = _cairo_gstate_get_pattern_status (gstate->source);
+    if (unlikely (status))
+       return status;
+
+    if (gstate->op == CAIRO_OPERATOR_DEST)
+       return CAIRO_STATUS_SUCCESS;
+
+    if (gstate->stroke_style.line_width <= 0.0)
+       return CAIRO_STATUS_SUCCESS;
+
+    if (_cairo_clip_is_all_clipped (gstate->clip))
+       return CAIRO_STATUS_SUCCESS;
+
+    assert (gstate->opacity == 1.0);
+
+    memcpy (&style, &gstate->stroke_style, sizeof (gstate->stroke_style));
+    if (_cairo_stroke_style_dash_can_approximate (&gstate->stroke_style, &gstate->ctm, gstate->tolerance)) {
+        style.dash = dash;
+        _cairo_stroke_style_dash_approximate (&gstate->stroke_style, &gstate->ctm, gstate->tolerance,
+                                             &style.dash_offset,
+                                             style.dash,
+                                             &style.num_dashes);
+    }
+
+    _cairo_gstate_copy_transformed_source (gstate, &source_pattern.base);
+
+    return _cairo_surface_stroke (gstate->target,
+                                 gstate->op,
+                                 &source_pattern.base,
+                                 path,
+                                 &style,
+                                 &gstate->ctm,
+                                 &gstate->ctm_inverse,
+                                 gstate->tolerance,
+                                 gstate->antialias,
+                                 gstate->clip);
+}
+
+cairo_status_t
+_cairo_gstate_in_stroke (cairo_gstate_t            *gstate,
+                        cairo_path_fixed_t *path,
+                        double              x,
+                        double              y,
+                        cairo_bool_t       *inside_ret)
+{
+    cairo_status_t status;
+    cairo_rectangle_int_t extents;
+    cairo_box_t limit;
+    cairo_traps_t traps;
+
+    if (gstate->stroke_style.line_width <= 0.0) {
+       *inside_ret = FALSE;
+       return CAIRO_STATUS_SUCCESS;
+    }
+
+    _cairo_gstate_user_to_backend (gstate, &x, &y);
+
+    /* Before we perform the expensive stroke analysis,
+     * check whether the point is within the extents of the path.
+     */
+    _cairo_path_fixed_approximate_stroke_extents (path,
+                                                 &gstate->stroke_style,
+                                                 &gstate->ctm,
+                                                 &extents);
+    if (x < extents.x || x > extents.x + extents.width ||
+       y < extents.y || y > extents.y + extents.height)
+    {
+       *inside_ret = FALSE;
+       return CAIRO_STATUS_SUCCESS;
+    }
+
+    limit.p1.x = _cairo_fixed_from_double (x) - 1;
+    limit.p1.y = _cairo_fixed_from_double (y) - 1;
+    limit.p2.x = limit.p1.x + 2;
+    limit.p2.y = limit.p1.y + 2;
+
+    _cairo_traps_init (&traps);
+    _cairo_traps_limit (&traps, &limit, 1);
+
+    status = _cairo_path_fixed_stroke_to_traps (path,
+                                               &gstate->stroke_style,
+                                               &gstate->ctm,
+                                               &gstate->ctm_inverse,
+                                               gstate->tolerance,
+                                               &traps);
+    if (unlikely (status))
+       goto BAIL;
+
+    *inside_ret = _cairo_traps_contain (&traps, x, y);
+
+BAIL:
+    _cairo_traps_fini (&traps);
+
+    return status;
+}
+
+cairo_status_t
+_cairo_gstate_fill (cairo_gstate_t *gstate, cairo_path_fixed_t *path)
+{
+    cairo_status_t status;
+
+    status = _cairo_gstate_get_pattern_status (gstate->source);
+    if (unlikely (status))
+       return status;
+
+    if (gstate->op == CAIRO_OPERATOR_DEST)
+       return CAIRO_STATUS_SUCCESS;
+
+    if (_cairo_clip_is_all_clipped (gstate->clip))
+       return CAIRO_STATUS_SUCCESS;
+
+    assert (gstate->opacity == 1.0);
+
+    if (_cairo_path_fixed_fill_is_empty (path)) {
+       if (_cairo_operator_bounded_by_mask (gstate->op))
+           return CAIRO_STATUS_SUCCESS;
+
+       status = _cairo_surface_paint (gstate->target,
+                                      CAIRO_OPERATOR_CLEAR,
+                                      &_cairo_pattern_clear.base,
+                                      gstate->clip);
+    } else {
+       cairo_pattern_union_t source_pattern;
+       const cairo_pattern_t *pattern;
+       cairo_operator_t op;
+       cairo_rectangle_int_t extents;
+       cairo_box_t box;
+
+       op = _reduce_op (gstate);
+       if (op == CAIRO_OPERATOR_CLEAR) {
+           pattern = &_cairo_pattern_clear.base;
+       } else {
+           _cairo_gstate_copy_transformed_source (gstate, &source_pattern.base);
+           pattern = &source_pattern.base;
+       }
+
+       /* Toolkits often paint the entire background with a fill */
+       if (_cairo_surface_get_extents (gstate->target, &extents) &&
+           _cairo_path_fixed_is_box (path, &box) &&
+           box.p1.x <= _cairo_fixed_from_int (extents.x) &&
+           box.p1.y <= _cairo_fixed_from_int (extents.y) &&
+           box.p2.x >= _cairo_fixed_from_int (extents.x + extents.width) &&
+           box.p2.y >= _cairo_fixed_from_int (extents.y + extents.height))
+       {
+           status = _cairo_surface_paint (gstate->target, op, pattern,
+                                          gstate->clip);
+       }
+       else
+       {
+           status = _cairo_surface_fill (gstate->target, op, pattern,
+                                         path,
+                                         gstate->fill_rule,
+                                         gstate->tolerance,
+                                         gstate->antialias,
+                                         gstate->clip);
+       }
+    }
+
+    return status;
+}
+
+cairo_bool_t
+_cairo_gstate_in_fill (cairo_gstate_t    *gstate,
+                      cairo_path_fixed_t *path,
+                      double              x,
+                      double              y)
+{
+    _cairo_gstate_user_to_backend (gstate, &x, &y);
+
+    return _cairo_path_fixed_in_fill (path,
+                                     gstate->fill_rule,
+                                     gstate->tolerance,
+                                     x, y);
+}
+
+cairo_bool_t
+_cairo_gstate_in_clip (cairo_gstate_t    *gstate,
+                      double              x,
+                      double              y)
+{
+    cairo_clip_t *clip = gstate->clip;
+    int i;
+
+    if (_cairo_clip_is_all_clipped (clip))
+       return FALSE;
+
+    if (clip == NULL)
+       return TRUE;
+
+    _cairo_gstate_user_to_backend (gstate, &x, &y);
+
+    if (x <  clip->extents.x ||
+       x >= clip->extents.x + clip->extents.width ||
+       y <  clip->extents.y ||
+       y >= clip->extents.y + clip->extents.height)
+    {
+       return FALSE;
+    }
+
+    if (clip->num_boxes) {
+       int fx, fy;
+
+       fx = _cairo_fixed_from_double (x);
+       fy = _cairo_fixed_from_double (y);
+       for (i = 0; i < clip->num_boxes; i++) {
+           if (fx >= clip->boxes[i].p1.x && fx <= clip->boxes[i].p2.x &&
+               fy >= clip->boxes[i].p1.y && fy <= clip->boxes[i].p2.y)
+               break;
+       }
+       if (i == clip->num_boxes)
+           return FALSE;
+    }
+
+    if (clip->path) {
+       cairo_clip_path_t *clip_path = clip->path;
+       do {
+           if (! _cairo_path_fixed_in_fill (&clip_path->path,
+                                            clip_path->fill_rule,
+                                            clip_path->tolerance,
+                                            x, y))
+               return FALSE;
+       } while ((clip_path = clip_path->prev) != NULL);
+    }
+
+    return TRUE;
+}
+
+cairo_status_t
+_cairo_gstate_copy_page (cairo_gstate_t *gstate)
+{
+    cairo_surface_copy_page (gstate->target);
+    return cairo_surface_status (gstate->target);
+}
+
+cairo_status_t
+_cairo_gstate_show_page (cairo_gstate_t *gstate)
+{
+    cairo_surface_show_page (gstate->target);
+    return cairo_surface_status (gstate->target);
+}
+
+static void
+_cairo_gstate_extents_to_user_rectangle (cairo_gstate_t          *gstate,
+                                        const cairo_box_t *extents,
+                                        double *x1, double *y1,
+                                        double *x2, double *y2)
+{
+    double px1, py1, px2, py2;
+
+    px1 = _cairo_fixed_to_double (extents->p1.x);
+    py1 = _cairo_fixed_to_double (extents->p1.y);
+    px2 = _cairo_fixed_to_double (extents->p2.x);
+    py2 = _cairo_fixed_to_double (extents->p2.y);
+
+    _cairo_gstate_backend_to_user_rectangle (gstate,
+                                            &px1, &py1, &px2, &py2,
+                                            NULL);
+    if (x1)
+       *x1 = px1;
+    if (y1)
+       *y1 = py1;
+    if (x2)
+       *x2 = px2;
+    if (y2)
+       *y2 = py2;
+}
+
+cairo_status_t
+_cairo_gstate_stroke_extents (cairo_gstate_t    *gstate,
+                             cairo_path_fixed_t *path,
+                              double *x1, double *y1,
+                             double *x2, double *y2)
+{
+    cairo_int_status_t status;
+    cairo_box_t extents;
+    cairo_bool_t empty;
+
+    if (x1)
+       *x1 = 0.0;
+    if (y1)
+       *y1 = 0.0;
+    if (x2)
+       *x2 = 0.0;
+    if (y2)
+       *y2 = 0.0;
+
+    if (gstate->stroke_style.line_width <= 0.0)
+       return CAIRO_STATUS_SUCCESS;
+
+    status = CAIRO_INT_STATUS_UNSUPPORTED;
+    if (_cairo_path_fixed_stroke_is_rectilinear (path)) {
+       cairo_boxes_t boxes;
+
+       _cairo_boxes_init (&boxes);
+       status = _cairo_path_fixed_stroke_rectilinear_to_boxes (path,
+                                                               &gstate->stroke_style,
+                                                               &gstate->ctm,
+                                                               gstate->antialias,
+                                                               &boxes);
+       empty = boxes.num_boxes == 0;
+       if (! empty)
+           _cairo_boxes_extents (&boxes, &extents);
+       _cairo_boxes_fini (&boxes);
+    }
+
+    if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
+       cairo_traps_t traps;
+
+       _cairo_traps_init (&traps);
+       status = _cairo_path_fixed_stroke_to_traps (path,
+                                                   &gstate->stroke_style,
+                                                   &gstate->ctm,
+                                                   &gstate->ctm_inverse,
+                                                   gstate->tolerance,
+                                                   &traps);
+       empty = traps.num_traps == 0;
+       if (! empty)
+           _cairo_traps_extents (&traps, &extents);
+       _cairo_traps_fini (&traps);
+    }
+    if (! empty) {
+       _cairo_gstate_extents_to_user_rectangle (gstate, &extents,
+                                                x1, y1, x2, y2);
+    }
+
+    return status;
+}
+
+cairo_status_t
+_cairo_gstate_fill_extents (cairo_gstate_t     *gstate,
+                           cairo_path_fixed_t *path,
+                            double *x1, double *y1,
+                           double *x2, double *y2)
+{
+    cairo_status_t status;
+    cairo_box_t extents;
+    cairo_bool_t empty;
+
+    if (x1)
+       *x1 = 0.0;
+    if (y1)
+       *y1 = 0.0;
+    if (x2)
+       *x2 = 0.0;
+    if (y2)
+       *y2 = 0.0;
+
+    if (_cairo_path_fixed_fill_is_empty (path))
+       return CAIRO_STATUS_SUCCESS;
+
+    if (_cairo_path_fixed_fill_is_rectilinear (path)) {
+       cairo_boxes_t boxes;
+
+       _cairo_boxes_init (&boxes);
+       status = _cairo_path_fixed_fill_rectilinear_to_boxes (path,
+                                                             gstate->fill_rule,
+                                                             gstate->antialias,
+                                                             &boxes);
+       empty = boxes.num_boxes == 0;
+       if (! empty)
+           _cairo_boxes_extents (&boxes, &extents);
+
+       _cairo_boxes_fini (&boxes);
+    } else {
+       cairo_traps_t traps;
+
+       _cairo_traps_init (&traps);
+
+       status = _cairo_path_fixed_fill_to_traps (path,
+                                                 gstate->fill_rule,
+                                                 gstate->tolerance,
+                                                 &traps);
+       empty = traps.num_traps == 0;
+       if (! empty)
+           _cairo_traps_extents (&traps, &extents);
+
+       _cairo_traps_fini (&traps);
+    }
+
+    if (! empty) {
+       _cairo_gstate_extents_to_user_rectangle (gstate, &extents,
+                                                x1, y1, x2, y2);
+    }
+
+    return status;
+}
+
+cairo_status_t
+_cairo_gstate_reset_clip (cairo_gstate_t *gstate)
+{
+    _cairo_clip_destroy (gstate->clip);
+    gstate->clip = NULL;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_status_t
+_cairo_gstate_clip (cairo_gstate_t *gstate, cairo_path_fixed_t *path)
+{
+    gstate->clip =
+       _cairo_clip_intersect_path (gstate->clip,
+                                   path,
+                                   gstate->fill_rule,
+                                   gstate->tolerance,
+                                   gstate->antialias);
+    /* XXX */
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_bool_t
+_cairo_gstate_int_clip_extents (cairo_gstate_t        *gstate,
+                               cairo_rectangle_int_t *extents)
+{
+    cairo_bool_t is_bounded;
+
+    is_bounded = _cairo_surface_get_extents (gstate->target, extents);
+
+    if (gstate->clip) {
+       _cairo_rectangle_intersect (extents,
+                                   _cairo_clip_get_extents (gstate->clip));
+       is_bounded = TRUE;
+    }
+
+    return is_bounded;
+}
+
+cairo_bool_t
+_cairo_gstate_clip_extents (cairo_gstate_t *gstate,
+                           double         *x1,
+                           double         *y1,
+                           double         *x2,
+                           double         *y2)
+{
+    cairo_rectangle_int_t extents;
+    double px1, py1, px2, py2;
+
+    if (! _cairo_gstate_int_clip_extents (gstate, &extents))
+       return FALSE;
+
+    px1 = extents.x;
+    py1 = extents.y;
+    px2 = extents.x + (int) extents.width;
+    py2 = extents.y + (int) extents.height;
+
+    _cairo_gstate_backend_to_user_rectangle (gstate,
+                                            &px1, &py1, &px2, &py2,
+                                            NULL);
+
+    if (x1)
+       *x1 = px1;
+    if (y1)
+       *y1 = py1;
+    if (x2)
+       *x2 = px2;
+    if (y2)
+       *y2 = py2;
+
+    return TRUE;
+}
+
+cairo_rectangle_list_t*
+_cairo_gstate_copy_clip_rectangle_list (cairo_gstate_t *gstate)
+{
+    cairo_rectangle_int_t extents;
+    cairo_rectangle_list_t *list;
+    cairo_clip_t *clip;
+
+    if (_cairo_surface_get_extents (gstate->target, &extents))
+       clip = _cairo_clip_copy_intersect_rectangle (gstate->clip, &extents);
+    else
+       clip = gstate->clip;
+
+    list = _cairo_clip_copy_rectangle_list (clip, gstate);
+
+    if (clip != gstate->clip)
+       _cairo_clip_destroy (clip);
+
+    return list;
+}
+
+static void
+_cairo_gstate_unset_scaled_font (cairo_gstate_t *gstate)
+{
+    if (gstate->scaled_font == NULL)
+       return;
+
+    if (gstate->previous_scaled_font != NULL)
+       cairo_scaled_font_destroy (gstate->previous_scaled_font);
+
+    gstate->previous_scaled_font = gstate->scaled_font;
+    gstate->scaled_font = NULL;
+}
+
+cairo_status_t
+_cairo_gstate_set_font_size (cairo_gstate_t *gstate,
+                            double          size)
+{
+    _cairo_gstate_unset_scaled_font (gstate);
+
+    cairo_matrix_init_scale (&gstate->font_matrix, size, size);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_status_t
+_cairo_gstate_set_font_matrix (cairo_gstate_t      *gstate,
+                              const cairo_matrix_t *matrix)
+{
+    if (memcmp (matrix, &gstate->font_matrix, sizeof (cairo_matrix_t)) == 0)
+       return CAIRO_STATUS_SUCCESS;
+
+    _cairo_gstate_unset_scaled_font (gstate);
+
+    gstate->font_matrix = *matrix;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+void
+_cairo_gstate_get_font_matrix (cairo_gstate_t *gstate,
+                              cairo_matrix_t *matrix)
+{
+    *matrix = gstate->font_matrix;
+}
+
+void
+_cairo_gstate_set_font_options (cairo_gstate_t             *gstate,
+                               const cairo_font_options_t *options)
+{
+    if (memcmp (options, &gstate->font_options, sizeof (cairo_font_options_t)) == 0)
+       return;
+
+    _cairo_gstate_unset_scaled_font (gstate);
+
+    _cairo_font_options_init_copy (&gstate->font_options, options);
+}
+
+void
+_cairo_gstate_get_font_options (cairo_gstate_t       *gstate,
+                               cairo_font_options_t *options)
+{
+    *options = gstate->font_options;
+}
+
+cairo_status_t
+_cairo_gstate_get_font_face (cairo_gstate_t     *gstate,
+                            cairo_font_face_t **font_face)
+{
+    cairo_status_t status;
+
+    status = _cairo_gstate_ensure_font_face (gstate);
+    if (unlikely (status))
+       return status;
+
+    *font_face = gstate->font_face;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_status_t
+_cairo_gstate_get_scaled_font (cairo_gstate_t       *gstate,
+                              cairo_scaled_font_t **scaled_font)
+{
+    cairo_status_t status;
+
+    status = _cairo_gstate_ensure_scaled_font (gstate);
+    if (unlikely (status))
+       return status;
+
+    *scaled_font = gstate->scaled_font;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+/*
+ * Like everything else in this file, fonts involve Too Many Coordinate Spaces;
+ * it is easy to get confused about what's going on.
+ *
+ * The user's view
+ * ---------------
+ *
+ * Users ask for things in user space. When cairo starts, a user space unit
+ * is about 1/96 inch, which is similar to (but importantly different from)
+ * the normal "point" units most users think in terms of. When a user
+ * selects a font, its scale is set to "one user unit". The user can then
+ * independently scale the user coordinate system *or* the font matrix, in
+ * order to adjust the rendered size of the font.
+ *
+ * Metrics are returned in user space, whether they are obtained from
+ * the currently selected font in a  #cairo_t or from a #cairo_scaled_font_t
+ * which is a font specialized to a particular scale matrix, CTM, and target
+ * surface.
+ *
+ * The font's view
+ * ---------------
+ *
+ * Fonts are designed and stored (in say .ttf files) in "font space", which
+ * describes an "EM Square" (a design tile) and has some abstract number
+ * such as 1000, 1024, or 2048 units per "EM". This is basically an
+ * uninteresting space for us, but we need to remember that it exists.
+ *
+ * Font resources (from libraries or operating systems) render themselves
+ * to a particular device. Since they do not want to make most programmers
+ * worry about the font design space, the scaling API is simplified to
+ * involve just telling the font the required pixel size of the EM square
+ * (that is, in device space).
+ *
+ *
+ * Cairo's gstate view
+ * -------------------
+ *
+ * In addition to the CTM and CTM inverse, we keep a matrix in the gstate
+ * called the "font matrix" which describes the user's most recent
+ * font-scaling or font-transforming request. This is kept in terms of an
+ * abstract scale factor, composed with the CTM and used to set the font's
+ * pixel size. So if the user asks to "scale the font by 12", the matrix
+ * is:
+ *
+ *   [ 12.0, 0.0, 0.0, 12.0, 0.0, 0.0 ]
+ *
+ * It is an affine matrix, like all cairo matrices, where its tx and ty
+ * components are used to "nudging" fonts around and are handled in gstate
+ * and then ignored by the "scaled-font" layer.
+ *
+ * In order to perform any action on a font, we must build an object
+ * called a #cairo_font_scale_t; this contains the central 2x2 matrix
+ * resulting from "font matrix * CTM" (sans the font matrix translation
+ * components as stated in the previous paragraph).
+ *
+ * We pass this to the font when making requests of it, which causes it to
+ * reply for a particular [user request, device] combination, under the CTM
+ * (to accommodate the "zoom in" == "bigger fonts" issue above).
+ *
+ * The other terms in our communication with the font are therefore in
+ * device space. When we ask it to perform text->glyph conversion, it will
+ * produce a glyph string in device space. Glyph vectors we pass to it for
+ * measuring or rendering should be in device space. The metrics which we
+ * get back from the font will be in device space. The contents of the
+ * global glyph image cache will be in device space.
+ *
+ *
+ * Cairo's public view
+ * -------------------
+ *
+ * Since the values entering and leaving via public API calls are in user
+ * space, the gstate functions typically need to multiply arguments by the
+ * CTM (for user-input glyph vectors), and return values by the CTM inverse
+ * (for font responses such as metrics or glyph vectors).
+ *
+ */
+
+static cairo_status_t
+_cairo_gstate_ensure_font_face (cairo_gstate_t *gstate)
+{
+    cairo_font_face_t *font_face;
+
+    if (gstate->font_face != NULL)
+       return gstate->font_face->status;
+
+
+    font_face = cairo_toy_font_face_create (CAIRO_FONT_FAMILY_DEFAULT,
+                                           CAIRO_FONT_SLANT_DEFAULT,
+                                           CAIRO_FONT_WEIGHT_DEFAULT);
+    if (font_face->status)
+       return font_face->status;
+
+    gstate->font_face = font_face;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_gstate_ensure_scaled_font (cairo_gstate_t *gstate)
+{
+    cairo_status_t status;
+    cairo_font_options_t options;
+    cairo_scaled_font_t *scaled_font;
+
+    if (gstate->scaled_font != NULL)
+       return gstate->scaled_font->status;
+
+    status = _cairo_gstate_ensure_font_face (gstate);
+    if (unlikely (status))
+       return status;
+
+    cairo_surface_get_font_options (gstate->target, &options);
+    cairo_font_options_merge (&options, &gstate->font_options);
+
+    scaled_font = cairo_scaled_font_create (gstate->font_face,
+                                           &gstate->font_matrix,
+                                           &gstate->ctm,
+                                           &options);
+
+    status = cairo_scaled_font_status (scaled_font);
+    if (unlikely (status))
+       return status;
+
+    gstate->scaled_font = scaled_font;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_status_t
+_cairo_gstate_get_font_extents (cairo_gstate_t *gstate,
+                               cairo_font_extents_t *extents)
+{
+    cairo_status_t status = _cairo_gstate_ensure_scaled_font (gstate);
+    if (unlikely (status))
+       return status;
+
+    cairo_scaled_font_extents (gstate->scaled_font, extents);
+
+    return cairo_scaled_font_status (gstate->scaled_font);
+}
+
+cairo_status_t
+_cairo_gstate_set_font_face (cairo_gstate_t    *gstate,
+                            cairo_font_face_t *font_face)
+{
+    if (font_face && font_face->status)
+       return _cairo_error (font_face->status);
+
+    if (font_face == gstate->font_face)
+       return CAIRO_STATUS_SUCCESS;
+
+    cairo_font_face_destroy (gstate->font_face);
+    gstate->font_face = cairo_font_face_reference (font_face);
+
+    _cairo_gstate_unset_scaled_font (gstate);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_status_t
+_cairo_gstate_glyph_extents (cairo_gstate_t *gstate,
+                            const cairo_glyph_t *glyphs,
+                            int num_glyphs,
+                            cairo_text_extents_t *extents)
+{
+    cairo_status_t status;
+
+    status = _cairo_gstate_ensure_scaled_font (gstate);
+    if (unlikely (status))
+       return status;
+
+    cairo_scaled_font_glyph_extents (gstate->scaled_font,
+                                    glyphs, num_glyphs,
+                                    extents);
+
+    return cairo_scaled_font_status (gstate->scaled_font);
+}
+
+cairo_status_t
+_cairo_gstate_show_text_glyphs (cairo_gstate_t            *gstate,
+                               const cairo_glyph_t        *glyphs,
+                               int                         num_glyphs,
+                               cairo_glyph_text_info_t    *info)
+{
+    cairo_glyph_t stack_transformed_glyphs[CAIRO_STACK_ARRAY_LENGTH (cairo_glyph_t)];
+    cairo_text_cluster_t stack_transformed_clusters[CAIRO_STACK_ARRAY_LENGTH (cairo_text_cluster_t)];
+    cairo_pattern_union_t source_pattern;
+    cairo_glyph_t *transformed_glyphs;
+    const cairo_pattern_t *pattern;
+    cairo_text_cluster_t *transformed_clusters;
+    cairo_operator_t op;
+    cairo_status_t status;
+
+    status = _cairo_gstate_get_pattern_status (gstate->source);
+    if (unlikely (status))
+       return status;
+
+    if (gstate->op == CAIRO_OPERATOR_DEST)
+       return CAIRO_STATUS_SUCCESS;
+
+    if (_cairo_clip_is_all_clipped (gstate->clip))
+       return CAIRO_STATUS_SUCCESS;
+
+    status = _cairo_gstate_ensure_scaled_font (gstate);
+    if (unlikely (status))
+       return status;
+
+    transformed_glyphs = stack_transformed_glyphs;
+    transformed_clusters = stack_transformed_clusters;
+
+    if (num_glyphs > ARRAY_LENGTH (stack_transformed_glyphs)) {
+       transformed_glyphs = cairo_glyph_allocate (num_glyphs);
+       if (unlikely (transformed_glyphs == NULL))
+           return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+    }
+
+    if (info != NULL) {
+       if (info->num_clusters > ARRAY_LENGTH (stack_transformed_clusters)) {
+           transformed_clusters = cairo_text_cluster_allocate (info->num_clusters);
+           if (unlikely (transformed_clusters == NULL)) {
+               status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+               goto CLEANUP_GLYPHS;
+           }
+       }
+
+       _cairo_gstate_transform_glyphs_to_backend (gstate,
+                                                  glyphs, num_glyphs,
+                                                  info->clusters,
+                                                  info->num_clusters,
+                                                  info->cluster_flags,
+                                                  transformed_glyphs,
+                                                  &num_glyphs,
+                                                  transformed_clusters);
+    } else {
+       _cairo_gstate_transform_glyphs_to_backend (gstate,
+                                                  glyphs, num_glyphs,
+                                                  NULL, 0, 0,
+                                                  transformed_glyphs,
+                                                  &num_glyphs,
+                                                  NULL);
+    }
+
+    if (num_glyphs == 0)
+       goto CLEANUP_GLYPHS;
+
+    op = _reduce_op (gstate);
+    if (op == CAIRO_OPERATOR_CLEAR) {
+       pattern = &_cairo_pattern_clear.base;
+    } else {
+       _cairo_gstate_copy_transformed_source (gstate, &source_pattern.base);
+       pattern = &source_pattern.base;
+    }
+
+    /* For really huge font sizes, we can just do path;fill instead of
+     * show_glyphs, as show_glyphs would put excess pressure on the cache,
+     * and moreover, not all components below us correctly handle huge font
+     * sizes.  I wanted to set the limit at 256.  But alas, seems like cairo's
+     * rasterizer is something like ten times slower than freetype's for huge
+     * sizes.  So, no win just yet.  For now, do it for insanely-huge sizes,
+     * just to make sure we don't make anyone unhappy.  When we get a really
+     * fast rasterizer in cairo, we may want to readjust this.
+     *
+     * Needless to say, do this only if show_text_glyphs is not available. */
+    if (cairo_surface_has_show_text_glyphs (gstate->target) ||
+       _cairo_scaled_font_get_max_scale (gstate->scaled_font) <= 10240)
+    {
+       if (info != NULL) {
+           status = _cairo_surface_show_text_glyphs (gstate->target, op, pattern,
+                                                     info->utf8, info->utf8_len,
+                                                     transformed_glyphs, num_glyphs,
+                                                     transformed_clusters, info->num_clusters,
+                                                     info->cluster_flags,
+                                                     gstate->scaled_font,
+                                                     gstate->clip);
+       } else {
+           status = _cairo_surface_show_text_glyphs (gstate->target, op, pattern,
+                                                     NULL, 0,
+                                                     transformed_glyphs, num_glyphs,
+                                                     NULL, 0, 0,
+                                                     gstate->scaled_font,
+                                                     gstate->clip);
+       }
+    }
+    else
+    {
+       cairo_path_fixed_t path;
+
+       _cairo_path_fixed_init (&path);
+
+       status = _cairo_scaled_font_glyph_path (gstate->scaled_font,
+                                               transformed_glyphs, num_glyphs,
+                                               &path);
+
+       if (status == CAIRO_STATUS_SUCCESS) {
+           status = _cairo_surface_fill (gstate->target, op, pattern,
+                                         &path,
+                                         CAIRO_FILL_RULE_WINDING,
+                                         gstate->tolerance,
+                                         gstate->scaled_font->options.antialias,
+                                         gstate->clip);
+       }
+
+       _cairo_path_fixed_fini (&path);
+    }
+
+CLEANUP_GLYPHS:
+    if (transformed_glyphs != stack_transformed_glyphs)
+      cairo_glyph_free (transformed_glyphs);
+    if (transformed_clusters != stack_transformed_clusters)
+      cairo_text_cluster_free (transformed_clusters);
+
+    return status;
+}
+
+cairo_status_t
+_cairo_gstate_glyph_path (cairo_gstate_t      *gstate,
+                         const cairo_glyph_t *glyphs,
+                         int                  num_glyphs,
+                         cairo_path_fixed_t  *path)
+{
+    cairo_glyph_t stack_transformed_glyphs[CAIRO_STACK_ARRAY_LENGTH (cairo_glyph_t)];
+    cairo_glyph_t *transformed_glyphs;
+    cairo_status_t status;
+
+    status = _cairo_gstate_ensure_scaled_font (gstate);
+    if (unlikely (status))
+       return status;
+
+    if (num_glyphs < ARRAY_LENGTH (stack_transformed_glyphs)) {
+       transformed_glyphs = stack_transformed_glyphs;
+    } else {
+       transformed_glyphs = cairo_glyph_allocate (num_glyphs);
+       if (unlikely (transformed_glyphs == NULL))
+           return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+    }
+
+    _cairo_gstate_transform_glyphs_to_backend (gstate,
+                                              glyphs, num_glyphs,
+                                              NULL, 0, 0,
+                                              transformed_glyphs,
+                                              &num_glyphs, NULL);
+
+    status = _cairo_scaled_font_glyph_path (gstate->scaled_font,
+                                           transformed_glyphs, num_glyphs,
+                                           path);
+
+    if (transformed_glyphs != stack_transformed_glyphs)
+      cairo_glyph_free (transformed_glyphs);
+
+    return status;
+}
+
+cairo_status_t
+_cairo_gstate_set_antialias (cairo_gstate_t *gstate,
+                            cairo_antialias_t antialias)
+{
+    gstate->antialias = antialias;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_antialias_t
+_cairo_gstate_get_antialias (cairo_gstate_t *gstate)
+{
+    return gstate->antialias;
+}
+
+/**
+ * _cairo_gstate_transform_glyphs_to_backend:
+ * @gstate: a #cairo_gstate_t
+ * @glyphs: the array of #cairo_glyph_t objects to be transformed
+ * @num_glyphs: the number of elements in @glyphs
+ * @transformed_glyphs: a pre-allocated array of at least @num_glyphs
+ * #cairo_glyph_t objects
+ * @num_transformed_glyphs: the number of elements in @transformed_glyphs
+ * after dropping out of bounds glyphs, or %NULL if glyphs shouldn't be
+ * dropped
+ *
+ * Transform an array of glyphs to backend space by first adding the offset
+ * of the font matrix, then transforming from user space to backend space.
+ * The result of the transformation is placed in @transformed_glyphs.
+ *
+ * This also uses information from the scaled font and the surface to
+ * cull/drop glyphs that will not be visible.
+ **/
+static void
+_cairo_gstate_transform_glyphs_to_backend (cairo_gstate_t      *gstate,
+                                           const cairo_glyph_t *glyphs,
+                                           int                  num_glyphs,
+                                          const cairo_text_cluster_t   *clusters,
+                                          int                   num_clusters,
+                                          cairo_text_cluster_flags_t cluster_flags,
+                                           cairo_glyph_t       *transformed_glyphs,
+                                          int                  *num_transformed_glyphs,
+                                          cairo_text_cluster_t *transformed_clusters)
+{
+    cairo_rectangle_int_t surface_extents;
+    cairo_matrix_t *ctm = &gstate->ctm;
+    cairo_matrix_t *font_matrix = &gstate->font_matrix;
+    cairo_matrix_t *device_transform = &gstate->target->device_transform;
+    cairo_bool_t drop = FALSE;
+    double x1 = 0, x2 = 0, y1 = 0, y2 = 0;
+    int i, j, k;
+
+    drop = TRUE;
+    if (! _cairo_gstate_int_clip_extents (gstate, &surface_extents)) {
+       drop = FALSE; /* unbounded surface */
+    } else {
+       double scale10 = 10 * _cairo_scaled_font_get_max_scale (gstate->scaled_font);
+       if (surface_extents.width == 0 || surface_extents.height == 0) {
+         /* No visible area.  Don't draw anything */
+         *num_transformed_glyphs = 0;
+         return;
+       }
+       /* XXX We currently drop any glyphs that has its position outside
+        * of the surface boundaries by a safety margin depending on the
+        * font scale.  This however can fail in extreme cases where the
+        * font has really long swashes for example...  We can correctly
+        * handle that by looking the glyph up and using its device bbox
+        * to device if it's going to be visible, but I'm not inclined to
+        * do that now.
+        */
+       x1 = surface_extents.x - scale10;
+       y1 = surface_extents.y - scale10;
+       x2 = surface_extents.x + (int) surface_extents.width  + scale10;
+       y2 = surface_extents.y + (int) surface_extents.height + scale10;
+    }
+
+    if (!drop)
+       *num_transformed_glyphs = num_glyphs;
+
+#define KEEP_GLYPH(glyph) (x1 <= glyph.x && glyph.x <= x2 && y1 <= glyph.y && glyph.y <= y2)
+
+    j = 0;
+    if (_cairo_matrix_is_identity (ctm) &&
+        _cairo_matrix_is_identity (device_transform) &&
+       font_matrix->x0 == 0 && font_matrix->y0 == 0)
+    {
+       if (! drop) {
+           memcpy (transformed_glyphs, glyphs,
+                   num_glyphs * sizeof (cairo_glyph_t));
+           memcpy (transformed_clusters, clusters,
+                   num_clusters * sizeof (cairo_text_cluster_t));
+           j = num_glyphs;
+       } else if (num_clusters == 0) {
+           for (i = 0; i < num_glyphs; i++) {
+               transformed_glyphs[j].index = glyphs[i].index;
+               transformed_glyphs[j].x = glyphs[i].x;
+               transformed_glyphs[j].y = glyphs[i].y;
+               if (KEEP_GLYPH (transformed_glyphs[j]))
+                   j++;
+           }
+       } else {
+           const cairo_glyph_t *cur_glyph;
+
+           if (cluster_flags & CAIRO_TEXT_CLUSTER_FLAG_BACKWARD)
+               cur_glyph = glyphs + num_glyphs - 1;
+           else
+               cur_glyph = glyphs;
+
+           for (i = 0; i < num_clusters; i++) {
+               cairo_bool_t cluster_visible = FALSE;
+
+               for (k = 0; k < clusters[i].num_glyphs; k++) {
+                   transformed_glyphs[j+k].index = cur_glyph->index;
+                   transformed_glyphs[j+k].x = cur_glyph->x;
+                   transformed_glyphs[j+k].y = cur_glyph->y;
+                   if (KEEP_GLYPH (transformed_glyphs[j+k]))
+                       cluster_visible = TRUE;
+
+                   if (cluster_flags & CAIRO_TEXT_CLUSTER_FLAG_BACKWARD)
+                       cur_glyph--;
+                   else
+                       cur_glyph++;
+               }
+
+               transformed_clusters[i] = clusters[i];
+               if (cluster_visible)
+                   j += k;
+               else
+                   transformed_clusters[i].num_glyphs = 0;
+           }
+       }
+    }
+    else if (_cairo_matrix_is_translation (ctm) &&
+             _cairo_matrix_is_translation (device_transform))
+    {
+        double tx = font_matrix->x0 + ctm->x0 + device_transform->x0;
+        double ty = font_matrix->y0 + ctm->y0 + device_transform->y0;
+
+       if (! drop || num_clusters == 0) {
+           for (i = 0; i < num_glyphs; i++) {
+               transformed_glyphs[j].index = glyphs[i].index;
+               transformed_glyphs[j].x = glyphs[i].x + tx;
+               transformed_glyphs[j].y = glyphs[i].y + ty;
+               if (!drop || KEEP_GLYPH (transformed_glyphs[j]))
+                   j++;
+           }
+           memcpy (transformed_clusters, clusters,
+                   num_clusters * sizeof (cairo_text_cluster_t));
+       } else {
+           const cairo_glyph_t *cur_glyph;
+
+           if (cluster_flags & CAIRO_TEXT_CLUSTER_FLAG_BACKWARD)
+               cur_glyph = glyphs + num_glyphs - 1;
+           else
+               cur_glyph = glyphs;
+
+           for (i = 0; i < num_clusters; i++) {
+               cairo_bool_t cluster_visible = FALSE;
+
+               for (k = 0; k < clusters[i].num_glyphs; k++) {
+                   transformed_glyphs[j+k].index = cur_glyph->index;
+                   transformed_glyphs[j+k].x = cur_glyph->x + tx;
+                   transformed_glyphs[j+k].y = cur_glyph->y + ty;
+                   if (KEEP_GLYPH (transformed_glyphs[j+k]))
+                       cluster_visible = TRUE;
+
+                   if (cluster_flags & CAIRO_TEXT_CLUSTER_FLAG_BACKWARD)
+                       cur_glyph--;
+                   else
+                       cur_glyph++;
+               }
+
+               transformed_clusters[i] = clusters[i];
+               if (cluster_visible)
+                   j += k;
+               else
+                   transformed_clusters[i].num_glyphs = 0;
+           }
+       }
+    }
+    else
+    {
+        cairo_matrix_t aggregate_transform;
+
+        cairo_matrix_init_translate (&aggregate_transform,
+                                     gstate->font_matrix.x0,
+                                     gstate->font_matrix.y0);
+        cairo_matrix_multiply (&aggregate_transform,
+                               &aggregate_transform, ctm);
+        cairo_matrix_multiply (&aggregate_transform,
+                               &aggregate_transform, device_transform);
+
+       if (! drop || num_clusters == 0) {
+           for (i = 0; i < num_glyphs; i++) {
+               transformed_glyphs[j] = glyphs[i];
+               cairo_matrix_transform_point (&aggregate_transform,
+                                             &transformed_glyphs[j].x,
+                                             &transformed_glyphs[j].y);
+               if (! drop || KEEP_GLYPH (transformed_glyphs[j]))
+                   j++;
+           }
+           memcpy (transformed_clusters, clusters,
+                   num_clusters * sizeof (cairo_text_cluster_t));
+       } else {
+           const cairo_glyph_t *cur_glyph;
+
+           if (cluster_flags & CAIRO_TEXT_CLUSTER_FLAG_BACKWARD)
+               cur_glyph = glyphs + num_glyphs - 1;
+           else
+               cur_glyph = glyphs;
+
+           for (i = 0; i < num_clusters; i++) {
+               cairo_bool_t cluster_visible = FALSE;
+               for (k = 0; k < clusters[i].num_glyphs; k++) {
+                   transformed_glyphs[j+k] = *cur_glyph;
+                   cairo_matrix_transform_point (&aggregate_transform,
+                                                 &transformed_glyphs[j+k].x,
+                                                 &transformed_glyphs[j+k].y);
+                   if (KEEP_GLYPH (transformed_glyphs[j+k]))
+                       cluster_visible = TRUE;
+
+                   if (cluster_flags & CAIRO_TEXT_CLUSTER_FLAG_BACKWARD)
+                       cur_glyph--;
+                   else
+                       cur_glyph++;
+               }
+
+               transformed_clusters[i] = clusters[i];
+               if (cluster_visible)
+                   j += k;
+               else
+                   transformed_clusters[i].num_glyphs = 0;
+           }
+       }
+    }
+    *num_transformed_glyphs = j;
+
+    if (num_clusters != 0 && cluster_flags & CAIRO_TEXT_CLUSTER_FLAG_BACKWARD) {
+       for (i = 0; i < --j; i++) {
+           cairo_glyph_t tmp;
+
+           tmp = transformed_glyphs[i];
+           transformed_glyphs[i] = transformed_glyphs[j];
+           transformed_glyphs[j] = tmp;
+       }
+    }
+}
diff --git a/src/cairo-hash-private.h b/src/cairo-hash-private.h
new file mode 100644 (file)
index 0000000..30e51ff
--- /dev/null
@@ -0,0 +1,87 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2004 Red Hat, Inc.
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ *      Keith Packard <keithp@keithp.com>
+ *     Graydon Hoare <graydon@redhat.com>
+ *     Carl Worth <cworth@cworth.org>
+ */
+
+#ifndef CAIRO_HASH_PRIVATE_H
+#define CAIRO_HASH_PRIVATE_H
+
+#include "cairo-compiler-private.h"
+#include "cairo-types-private.h"
+
+/* XXX: I'd like this file to be self-contained in terms of
+ * includeability, but that's not really possible with the current
+ * monolithic cairoint.h. So, for now, just include cairoint.h instead
+ * if you want to include this file. */
+
+typedef cairo_bool_t
+(*cairo_hash_keys_equal_func_t) (const void *key_a, const void *key_b);
+
+typedef cairo_bool_t
+(*cairo_hash_predicate_func_t) (const void *entry);
+
+typedef void
+(*cairo_hash_callback_func_t) (void *entry,
+                              void *closure);
+
+cairo_private cairo_hash_table_t *
+_cairo_hash_table_create (cairo_hash_keys_equal_func_t keys_equal);
+
+cairo_private void
+_cairo_hash_table_destroy (cairo_hash_table_t *hash_table);
+
+cairo_private void *
+_cairo_hash_table_lookup (cairo_hash_table_t  *hash_table,
+                         cairo_hash_entry_t  *key);
+
+cairo_private void *
+_cairo_hash_table_random_entry (cairo_hash_table_t        *hash_table,
+                               cairo_hash_predicate_func_t predicate);
+
+cairo_private cairo_status_t
+_cairo_hash_table_insert (cairo_hash_table_t *hash_table,
+                         cairo_hash_entry_t *entry);
+
+cairo_private void
+_cairo_hash_table_remove (cairo_hash_table_t *hash_table,
+                         cairo_hash_entry_t *key);
+
+cairo_private void
+_cairo_hash_table_foreach (cairo_hash_table_t        *hash_table,
+                          cairo_hash_callback_func_t  hash_callback,
+                          void                       *closure);
+
+#endif
diff --git a/src/cairo-hash.c b/src/cairo-hash.c
new file mode 100644 (file)
index 0000000..f4fb7cd
--- /dev/null
@@ -0,0 +1,564 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2004 Red Hat, Inc.
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ *      Keith Packard <keithp@keithp.com>
+ *     Graydon Hoare <graydon@redhat.com>
+ *     Carl Worth <cworth@cworth.org>
+ */
+
+#include "cairoint.h"
+#include "cairo-error-private.h"
+
+/*
+ * An entry can be in one of three states:
+ *
+ * FREE: Entry has never been used, terminates all searches.
+ *       Appears in the table as a %NULL pointer.
+ *
+ * DEAD: Entry had been live in the past. A dead entry can be reused
+ *       but does not terminate a search for an exact entry.
+ *       Appears in the table as a pointer to DEAD_ENTRY.
+ *
+ * LIVE: Entry is currently being used.
+ *       Appears in the table as any non-%NULL, non-DEAD_ENTRY pointer.
+ */
+
+#define DEAD_ENTRY ((cairo_hash_entry_t *) 0x1)
+
+#define ENTRY_IS_FREE(entry) ((entry) == NULL)
+#define ENTRY_IS_DEAD(entry) ((entry) == DEAD_ENTRY)
+#define ENTRY_IS_LIVE(entry) ((entry) >  DEAD_ENTRY)
+
+/*
+ * This table is open-addressed with double hashing. Each table size
+ * is a prime and it makes for the "first" hash modulus; a second
+ * prime (2 less than the first prime) serves as the "second" hash
+ * modulus, which is smaller and thus guarantees a complete
+ * permutation of table indices.
+ *
+ * Hash tables are rehashed in order to keep between 12.5% and 50%
+ * entries in the hash table alive and at least 25% free. When table
+ * size is changed, the new table has about 25% live elements.
+ *
+ * The free entries guarantee an expected constant-time lookup.
+ * Doubling/halving the table in the described fashion guarantees
+ * amortized O(1) insertion/removal.
+ *
+ * This structure, and accompanying table, is borrowed/modified from the
+ * file xserver/render/glyph.c in the freedesktop.org x server, with
+ * permission (and suggested modification of doubling sizes) by Keith
+ * Packard.
+ */
+
+static const unsigned long hash_table_sizes[] = {
+    43,
+    73,
+    151,
+    283,
+    571,
+    1153,
+    2269,
+    4519,
+    9013,
+    18043,
+    36109,
+    72091,
+    144409,
+    288361,
+    576883,
+    1153459,
+    2307163,
+    4613893,
+    9227641,
+    18455029,
+    36911011,
+    73819861,
+    147639589,
+    295279081,
+    590559793
+};
+
+struct _cairo_hash_table {
+    cairo_hash_keys_equal_func_t keys_equal;
+
+    const unsigned long *table_size;
+    cairo_hash_entry_t **entries;
+
+    unsigned long live_entries;
+    unsigned long free_entries;
+    unsigned long iterating;   /* Iterating, no insert, no resize */
+};
+
+/**
+ * _cairo_hash_table_uid_keys_equal:
+ * @key_a: the first key to be compared
+ * @key_b: the second key to be compared
+ *
+ * Provides a #cairo_hash_keys_equal_func_t which always returns
+ * %TRUE. This is useful to create hash tables using keys whose hash
+ * completely describes the key, because in this special case
+ * comparing the hashes is sufficient to guarantee that the keys are
+ * equal.
+ *
+ * Return value: %TRUE.
+ **/
+static cairo_bool_t
+_cairo_hash_table_uid_keys_equal (const void *key_a, const void *key_b)
+{
+    return TRUE;
+}
+
+/**
+ * _cairo_hash_table_create:
+ * @keys_equal: a function to return %TRUE if two keys are equal
+ *
+ * Creates a new hash table which will use the keys_equal() function
+ * to compare hash keys. Data is provided to the hash table in the
+ * form of user-derived versions of #cairo_hash_entry_t. A hash entry
+ * must be able to hold both a key (including a hash code) and a
+ * value. Sometimes only the key will be necessary, (as in
+ * _cairo_hash_table_remove), and other times both a key and a value
+ * will be necessary, (as in _cairo_hash_table_insert).
+ *
+ * If @keys_equal is %NULL, two keys will be considered equal if and
+ * only if their hashes are equal.
+ *
+ * See #cairo_hash_entry_t for more details.
+ *
+ * Return value: the new hash table or %NULL if out of memory.
+ **/
+cairo_hash_table_t *
+_cairo_hash_table_create (cairo_hash_keys_equal_func_t keys_equal)
+{
+    cairo_hash_table_t *hash_table;
+
+    hash_table = malloc (sizeof (cairo_hash_table_t));
+    if (unlikely (hash_table == NULL)) {
+       _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+       return NULL;
+    }
+
+    if (keys_equal == NULL)
+       hash_table->keys_equal = _cairo_hash_table_uid_keys_equal;
+    else
+       hash_table->keys_equal = keys_equal;
+
+    hash_table->table_size = &hash_table_sizes[0];
+
+    hash_table->entries = calloc (*hash_table->table_size,
+                                 sizeof (cairo_hash_entry_t *));
+    if (unlikely (hash_table->entries == NULL)) {
+       _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+       free (hash_table);
+       return NULL;
+    }
+
+    hash_table->live_entries = 0;
+    hash_table->free_entries = *hash_table->table_size;
+    hash_table->iterating = 0;
+
+    return hash_table;
+}
+
+/**
+ * _cairo_hash_table_destroy:
+ * @hash_table: an empty hash table to destroy
+ *
+ * Immediately destroys the given hash table, freeing all resources
+ * associated with it.
+ *
+ * WARNING: The hash_table must have no live entries in it before
+ * _cairo_hash_table_destroy is called. It is a fatal error otherwise,
+ * and this function will halt. The rationale for this behavior is to
+ * avoid memory leaks and to avoid needless complication of the API
+ * with destroy notifiy callbacks.
+ *
+ * WARNING: The hash_table must have no running iterators in it when
+ * _cairo_hash_table_destroy is called. It is a fatal error otherwise,
+ * and this function will halt.
+ **/
+void
+_cairo_hash_table_destroy (cairo_hash_table_t *hash_table)
+{
+    /* The hash table must be empty. Otherwise, halt. */
+    assert (hash_table->live_entries == 0);
+    /* No iterators can be running. Otherwise, halt. */
+    assert (hash_table->iterating == 0);
+
+    free (hash_table->entries);
+    free (hash_table);
+}
+
+static cairo_hash_entry_t **
+_cairo_hash_table_lookup_unique_key (cairo_hash_table_t *hash_table,
+                                    cairo_hash_entry_t *key)
+{
+    unsigned long table_size, i, idx, step;
+    cairo_hash_entry_t **entry;
+
+    table_size = *hash_table->table_size;
+    idx = key->hash % table_size;
+
+    entry = &hash_table->entries[idx];
+    if (! ENTRY_IS_LIVE (*entry))
+       return entry;
+
+    i = 1;
+    step = 1 + key->hash % (table_size - 2);
+    do {
+       idx += step;
+       if (idx >= table_size)
+           idx -= table_size;
+
+       entry = &hash_table->entries[idx];
+       if (! ENTRY_IS_LIVE (*entry))
+           return entry;
+    } while (++i < table_size);
+
+    ASSERT_NOT_REACHED;
+    return NULL;
+}
+
+/**
+ * _cairo_hash_table_manage:
+ * @hash_table: a hash table
+ *
+ * Resize the hash table if the number of entries has gotten much
+ * bigger or smaller than the ideal number of entries for the current
+ * size and guarantee some free entries to be used as lookup
+ * termination points.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS if successful or
+ * %CAIRO_STATUS_NO_MEMORY if out of memory.
+ **/
+static cairo_status_t
+_cairo_hash_table_manage (cairo_hash_table_t *hash_table)
+{
+    cairo_hash_table_t tmp;
+    unsigned long new_size, i;
+
+    /* Keep between 12.5% and 50% entries in the hash table alive and
+     * at least 25% free. */
+    unsigned long live_high = *hash_table->table_size >> 1;
+    unsigned long live_low = live_high >> 2;
+    unsigned long free_low = live_high >> 1;
+
+    tmp = *hash_table;
+
+    if (hash_table->live_entries > live_high)
+    {
+       tmp.table_size = hash_table->table_size + 1;
+       /* This code is being abused if we can't make a table big enough. */
+       assert (tmp.table_size - hash_table_sizes <
+               ARRAY_LENGTH (hash_table_sizes));
+    }
+    else if (hash_table->live_entries < live_low)
+    {
+       /* Can't shrink if we're at the smallest size */
+       if (hash_table->table_size == &hash_table_sizes[0])
+           tmp.table_size = hash_table->table_size;
+       else
+           tmp.table_size = hash_table->table_size - 1;
+    }
+
+    if (tmp.table_size == hash_table->table_size &&
+       hash_table->free_entries > free_low)
+    {
+       /* The number of live entries is within the desired bounds
+        * (we're not going to resize the table) and we have enough
+        * free entries. Do nothing. */
+       return CAIRO_STATUS_SUCCESS;
+    }
+
+    new_size = *tmp.table_size;
+    tmp.entries = calloc (new_size, sizeof (cairo_hash_entry_t*));
+    if (unlikely (tmp.entries == NULL))
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    for (i = 0; i < *hash_table->table_size; ++i) {
+       if (ENTRY_IS_LIVE (hash_table->entries[i])) {
+           *_cairo_hash_table_lookup_unique_key (&tmp, hash_table->entries[i])
+               = hash_table->entries[i];
+       }
+    }
+
+    free (hash_table->entries);
+    hash_table->entries = tmp.entries;
+    hash_table->table_size = tmp.table_size;
+    hash_table->free_entries = new_size - hash_table->live_entries;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+/**
+ * _cairo_hash_table_lookup:
+ * @hash_table: a hash table
+ * @key: the key of interest
+ *
+ * Performs a lookup in @hash_table looking for an entry which has a
+ * key that matches @key, (as determined by the keys_equal() function
+ * passed to _cairo_hash_table_create).
+ *
+ * Return value: the matching entry, of %NULL if no match was found.
+ **/
+void *
+_cairo_hash_table_lookup (cairo_hash_table_t *hash_table,
+                         cairo_hash_entry_t *key)
+{
+    cairo_hash_entry_t *entry;
+    unsigned long table_size, i, idx, step;
+
+    table_size = *hash_table->table_size;
+    idx = key->hash % table_size;
+
+    entry = hash_table->entries[idx];
+    if (ENTRY_IS_LIVE (entry)) {
+       if (entry->hash == key->hash && hash_table->keys_equal (key, entry))
+           return entry;
+    } else if (ENTRY_IS_FREE (entry))
+       return NULL;
+
+    i = 1;
+    step = 1 + key->hash % (table_size - 2);
+    do {
+       idx += step;
+       if (idx >= table_size)
+           idx -= table_size;
+
+       entry = hash_table->entries[idx];
+       if (ENTRY_IS_LIVE (entry)) {
+           if (entry->hash == key->hash && hash_table->keys_equal (key, entry))
+               return entry;
+       } else if (ENTRY_IS_FREE (entry))
+           return NULL;
+    } while (++i < table_size);
+
+    ASSERT_NOT_REACHED;
+    return NULL;
+}
+
+/**
+ * _cairo_hash_table_random_entry:
+ * @hash_table: a hash table
+ * @predicate: a predicate function.
+ *
+ * Find a random entry in the hash table satisfying the given
+ * @predicate.
+ *
+ * We use the same algorithm as the lookup algorithm to walk over the
+ * entries in the hash table in a pseudo-random order. Walking
+ * linearly would favor entries following gaps in the hash table. We
+ * could also call rand() repeatedly, which works well for almost-full
+ * tables, but degrades when the table is almost empty, or predicate
+ * returns %TRUE for most entries.
+ *
+ * Return value: a random live entry or %NULL if there are no entries
+ * that match the given predicate. In particular, if predicate is
+ * %NULL, a %NULL return value indicates that the table is empty.
+ **/
+void *
+_cairo_hash_table_random_entry (cairo_hash_table_t        *hash_table,
+                               cairo_hash_predicate_func_t predicate)
+{
+    cairo_hash_entry_t *entry;
+    unsigned long hash;
+    unsigned long table_size, i, idx, step;
+
+    assert (predicate != NULL);
+
+    table_size = *hash_table->table_size;
+    hash = rand ();
+    idx = hash % table_size;
+
+    entry = hash_table->entries[idx];
+    if (ENTRY_IS_LIVE (entry) && predicate (entry))
+       return entry;
+
+    i = 1;
+    step = 1 + hash % (table_size - 2);
+    do {
+       idx += step;
+       if (idx >= table_size)
+           idx -= table_size;
+
+       entry = hash_table->entries[idx];
+       if (ENTRY_IS_LIVE (entry) && predicate (entry))
+           return entry;
+    } while (++i < table_size);
+
+    return NULL;
+}
+
+/**
+ * _cairo_hash_table_insert:
+ * @hash_table: a hash table
+ * @key_and_value: an entry to be inserted
+ *
+ * Insert the entry #key_and_value into the hash table.
+ *
+ * WARNING: There must not be an existing entry in the hash table
+ * with a matching key.
+ *
+ * WARNING: It is a fatal error to insert an element while
+ * an iterator is running
+ *
+ * Instead of using insert to replace an entry, consider just editing
+ * the entry obtained with _cairo_hash_table_lookup. Or if absolutely
+ * necessary, use _cairo_hash_table_remove first.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS if successful or
+ * %CAIRO_STATUS_NO_MEMORY if insufficient memory is available.
+ **/
+cairo_status_t
+_cairo_hash_table_insert (cairo_hash_table_t *hash_table,
+                         cairo_hash_entry_t *key_and_value)
+{
+    cairo_hash_entry_t **entry;
+    cairo_status_t status;
+
+    /* Insert is illegal while an iterator is running. */
+    assert (hash_table->iterating == 0);
+
+    status = _cairo_hash_table_manage (hash_table);
+    if (unlikely (status))
+       return status;
+
+    entry = _cairo_hash_table_lookup_unique_key (hash_table, key_and_value);
+
+    if (ENTRY_IS_FREE (*entry))
+       hash_table->free_entries--;
+
+    *entry = key_and_value;
+    hash_table->live_entries++;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_hash_entry_t **
+_cairo_hash_table_lookup_exact_key (cairo_hash_table_t *hash_table,
+                                   cairo_hash_entry_t *key)
+{
+    unsigned long table_size, i, idx, step;
+    cairo_hash_entry_t **entry;
+
+    table_size = *hash_table->table_size;
+    idx = key->hash % table_size;
+
+    entry = &hash_table->entries[idx];
+    if (*entry == key)
+       return entry;
+
+    i = 1;
+    step = 1 + key->hash % (table_size - 2);
+    do {
+       idx += step;
+       if (idx >= table_size)
+           idx -= table_size;
+
+       entry = &hash_table->entries[idx];
+       if (*entry == key)
+           return entry;
+    } while (++i < table_size);
+
+    ASSERT_NOT_REACHED;
+    return NULL;
+}
+/**
+ * _cairo_hash_table_remove:
+ * @hash_table: a hash table
+ * @key: key of entry to be removed
+ *
+ * Remove an entry from the hash table which points to @key.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS if successful or
+ * %CAIRO_STATUS_NO_MEMORY if out of memory.
+ **/
+void
+_cairo_hash_table_remove (cairo_hash_table_t *hash_table,
+                         cairo_hash_entry_t *key)
+{
+    *_cairo_hash_table_lookup_exact_key (hash_table, key) = DEAD_ENTRY;
+    hash_table->live_entries--;
+
+    /* Check for table resize. Don't do this when iterating as this will
+     * reorder elements of the table and cause the iteration to potentially
+     * skip some elements. */
+    if (hash_table->iterating == 0) {
+       /* This call _can_ fail, but only in failing to allocate new
+        * memory to shrink the hash table. It does leave the table in a
+        * consistent state, and we've already succeeded in removing the
+        * entry, so we don't examine the failure status of this call. */
+       _cairo_hash_table_manage (hash_table);
+    }
+}
+
+/**
+ * _cairo_hash_table_foreach:
+ * @hash_table: a hash table
+ * @hash_callback: function to be called for each live entry
+ * @closure: additional argument to be passed to @hash_callback
+ *
+ * Call @hash_callback for each live entry in the hash table, in a
+ * non-specified order.
+ *
+ * Entries in @hash_table may be removed by code executed from @hash_callback.
+ *
+ * Entries may not be inserted to @hash_table, nor may @hash_table
+ * be destroyed by code executed from @hash_callback. The relevant
+ * functions will halt in these cases.
+ **/
+void
+_cairo_hash_table_foreach (cairo_hash_table_t        *hash_table,
+                          cairo_hash_callback_func_t  hash_callback,
+                          void                       *closure)
+{
+    unsigned long i;
+    cairo_hash_entry_t *entry;
+
+    /* Mark the table for iteration */
+    ++hash_table->iterating;
+    for (i = 0; i < *hash_table->table_size; i++) {
+       entry = hash_table->entries[i];
+       if (ENTRY_IS_LIVE(entry))
+           hash_callback (entry, closure);
+    }
+    /* If some elements were deleted during the iteration,
+     * the table may need resizing. Just do this every time
+     * as the check is inexpensive.
+     */
+    if (--hash_table->iterating == 0) {
+       /* Should we fail to shrink the hash table, it is left unaltered,
+        * and we don't need to propagate the error status. */
+       _cairo_hash_table_manage (hash_table);
+    }
+}
diff --git a/src/cairo-hull.c b/src/cairo-hull.c
new file mode 100644 (file)
index 0000000..c655933
--- /dev/null
@@ -0,0 +1,235 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2003 University of Southern California
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *     Carl D. Worth <cworth@cworth.org>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-error-private.h"
+#include "cairo-slope-private.h"
+
+typedef struct cairo_hull {
+    cairo_point_t point;
+    cairo_slope_t slope;
+    int discard;
+    int id;
+} cairo_hull_t;
+
+static void
+_cairo_hull_init (cairo_hull_t                 *hull,
+                 cairo_pen_vertex_t            *vertices,
+                 int                            num_vertices)
+{
+    cairo_point_t *p, *extremum, tmp;
+    int i;
+
+    extremum = &vertices[0].point;
+    for (i = 1; i < num_vertices; i++) {
+       p = &vertices[i].point;
+       if (p->y < extremum->y || (p->y == extremum->y && p->x < extremum->x))
+           extremum = p;
+    }
+    /* Put the extremal point at the beginning of the array */
+    tmp = *extremum;
+    *extremum = vertices[0].point;
+    vertices[0].point = tmp;
+
+    for (i = 0; i < num_vertices; i++) {
+       hull[i].point = vertices[i].point;
+       _cairo_slope_init (&hull[i].slope, &hull[0].point, &hull[i].point);
+
+        /* give each point a unique id for later comparison */
+        hull[i].id = i;
+
+        /* Don't discard by default */
+        hull[i].discard = 0;
+
+       /* Discard all points coincident with the extremal point */
+       if (i != 0 && hull[i].slope.dx == 0 && hull[i].slope.dy == 0)
+           hull[i].discard = 1;
+    }
+}
+
+static inline cairo_int64_t
+_slope_length (cairo_slope_t *slope)
+{
+    return _cairo_int64_add (_cairo_int32x32_64_mul (slope->dx, slope->dx),
+                            _cairo_int32x32_64_mul (slope->dy, slope->dy));
+}
+
+static int
+_cairo_hull_vertex_compare (const void *av, const void *bv)
+{
+    cairo_hull_t *a = (cairo_hull_t *) av;
+    cairo_hull_t *b = (cairo_hull_t *) bv;
+    int ret;
+
+    /* Some libraries are reported to actually compare identical
+     * pointers and require the result to be 0. This is the crazy world we
+     * have to live in.
+     */
+    if (a == b)
+       return 0;
+
+    ret = _cairo_slope_compare (&a->slope, &b->slope);
+
+    /*
+     * In the case of two vertices with identical slope from the
+     * extremal point discard the nearer point.
+     */
+    if (ret == 0) {
+       int cmp;
+
+       cmp = _cairo_int64_cmp (_slope_length (&a->slope),
+                               _slope_length (&b->slope));
+
+       /*
+        * Use the points' ids to ensure a well-defined ordering,
+        * and avoid setting discard on both points.
+        */
+       if (cmp < 0 || (cmp == 0 && a->id < b->id)) {
+           a->discard = 1;
+           ret = -1;
+       } else {
+           b->discard = 1;
+           ret = 1;
+       }
+    }
+
+    return ret;
+}
+
+static int
+_cairo_hull_prev_valid (cairo_hull_t *hull, int num_hull, int index)
+{
+    /* hull[0] is always valid, and we never need to wraparound, (if
+     * we are passed an index of 0 here, then the calling loop is just
+     * about to terminate). */
+    if (index == 0)
+       return 0;
+
+    do {
+       index--;
+    } while (hull[index].discard);
+
+    return index;
+}
+
+static int
+_cairo_hull_next_valid (cairo_hull_t *hull, int num_hull, int index)
+{
+    do {
+       index = (index + 1) % num_hull;
+    } while (hull[index].discard);
+
+    return index;
+}
+
+static void
+_cairo_hull_eliminate_concave (cairo_hull_t *hull, int num_hull)
+{
+    int i, j, k;
+    cairo_slope_t slope_ij, slope_jk;
+
+    i = 0;
+    j = _cairo_hull_next_valid (hull, num_hull, i);
+    k = _cairo_hull_next_valid (hull, num_hull, j);
+
+    do {
+       _cairo_slope_init (&slope_ij, &hull[i].point, &hull[j].point);
+       _cairo_slope_init (&slope_jk, &hull[j].point, &hull[k].point);
+
+       /* Is the angle formed by ij and jk concave? */
+       if (_cairo_slope_compare (&slope_ij, &slope_jk) >= 0) {
+           if (i == k)
+               return;
+           hull[j].discard = 1;
+           j = i;
+           i = _cairo_hull_prev_valid (hull, num_hull, j);
+       } else {
+           i = j;
+           j = k;
+           k = _cairo_hull_next_valid (hull, num_hull, j);
+       }
+    } while (j != 0);
+}
+
+static void
+_cairo_hull_to_pen (cairo_hull_t *hull, cairo_pen_vertex_t *vertices, int *num_vertices)
+{
+    int i, j = 0;
+
+    for (i = 0; i < *num_vertices; i++) {
+       if (hull[i].discard)
+           continue;
+       vertices[j++].point = hull[i].point;
+    }
+
+    *num_vertices = j;
+}
+
+/* Given a set of vertices, compute the convex hull using the Graham
+   scan algorithm. */
+cairo_status_t
+_cairo_hull_compute (cairo_pen_vertex_t *vertices, int *num_vertices)
+{
+    cairo_hull_t hull_stack[CAIRO_STACK_ARRAY_LENGTH (cairo_hull_t)];
+    cairo_hull_t *hull;
+    int num_hull = *num_vertices;
+
+    if (CAIRO_INJECT_FAULT ())
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    if (num_hull > ARRAY_LENGTH (hull_stack)) {
+       hull = _cairo_malloc_ab (num_hull, sizeof (cairo_hull_t));
+       if (unlikely (hull == NULL))
+           return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+    } else {
+       hull = hull_stack;
+    }
+
+    _cairo_hull_init (hull, vertices, num_hull);
+
+    qsort (hull + 1, num_hull - 1,
+          sizeof (cairo_hull_t), _cairo_hull_vertex_compare);
+
+    _cairo_hull_eliminate_concave (hull, num_hull);
+
+    _cairo_hull_to_pen (hull, vertices, num_vertices);
+
+    if (hull != hull_stack)
+       free (hull);
+
+    return CAIRO_STATUS_SUCCESS;
+}
diff --git a/src/cairo-image-compositor.c b/src/cairo-image-compositor.c
new file mode 100644 (file)
index 0000000..d299c9f
--- /dev/null
@@ -0,0 +1,1578 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2003 University of Southern California
+ * Copyright © 2009,2010,2011 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *     Carl D. Worth <cworth@cworth.org>
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+/* The primarily reason for keeping a traps-compositor around is
+ * for validating cairo-xlib (which currently also uses traps).
+ */
+
+#include "cairoint.h"
+
+#include "cairo-image-surface-private.h"
+
+#include "cairo-compositor-private.h"
+#include "cairo-spans-compositor-private.h"
+
+#include "cairo-region-private.h"
+#include "cairo-traps-private.h"
+#include "cairo-tristrip-private.h"
+
+static pixman_image_t *
+to_pixman_image (cairo_surface_t *s)
+{
+    return ((cairo_image_surface_t *)s)->pixman_image;
+}
+
+static cairo_int_status_t
+acquire (void *abstract_dst)
+{
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+release (void *abstract_dst)
+{
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+set_clip_region (void *_surface,
+                cairo_region_t *region)
+{
+    cairo_image_surface_t *surface = _surface;
+    pixman_region32_t *rgn = region ? &region->rgn : NULL;
+
+    if (! pixman_image_set_clip_region32 (surface->pixman_image, rgn))
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+draw_image_boxes (void *_dst,
+                 cairo_image_surface_t *image,
+                 cairo_boxes_t *boxes,
+                 int dx, int dy)
+{
+    cairo_image_surface_t *dst = _dst;
+    struct _cairo_boxes_chunk *chunk;
+    int i;
+
+    for (chunk = &boxes->chunks; chunk; chunk = chunk->next) {
+       for (i = 0; i < chunk->count; i++) {
+           cairo_box_t *b = &chunk->base[i];
+           int x = _cairo_fixed_integer_part (b->p1.x);
+           int y = _cairo_fixed_integer_part (b->p1.y);
+           int w = _cairo_fixed_integer_part (b->p2.x) - x;
+           int h = _cairo_fixed_integer_part (b->p2.y) - y;
+           if (dst->pixman_format != image->pixman_format ||
+               ! pixman_blt ((uint32_t *)image->data, (uint32_t *)dst->data,
+                             image->stride / sizeof (uint32_t),
+                             dst->stride / sizeof (uint32_t),
+                             PIXMAN_FORMAT_BPP (image->pixman_format),
+                             PIXMAN_FORMAT_BPP (dst->pixman_format),
+                             x + dx, y + dy,
+                             x, y,
+                             w, h))
+           {
+               pixman_image_composite32 (PIXMAN_OP_SRC,
+                                         image->pixman_image, NULL, dst->pixman_image,
+                                         x + dx, y + dy,
+                                         0, 0,
+                                         x, y,
+                                         w, h);
+           }
+       }
+    }
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static inline uint32_t
+color_to_uint32 (const cairo_color_t *color)
+{
+    return
+        (color->alpha_short >> 8 << 24) |
+        (color->red_short >> 8 << 16)   |
+        (color->green_short & 0xff00)   |
+        (color->blue_short >> 8);
+}
+
+static inline cairo_bool_t
+color_to_pixel (const cairo_color_t    *color,
+                pixman_format_code_t    format,
+                uint32_t               *pixel)
+{
+    uint32_t c;
+
+    if (!(format == PIXMAN_a8r8g8b8     ||
+          format == PIXMAN_x8r8g8b8     ||
+          format == PIXMAN_a8b8g8r8     ||
+          format == PIXMAN_x8b8g8r8     ||
+          format == PIXMAN_b8g8r8a8     ||
+          format == PIXMAN_b8g8r8x8     ||
+          format == PIXMAN_r5g6b5       ||
+          format == PIXMAN_b5g6r5       ||
+          format == PIXMAN_a8))
+    {
+       return FALSE;
+    }
+
+    c = color_to_uint32 (color);
+
+    if (PIXMAN_FORMAT_TYPE (format) == PIXMAN_TYPE_ABGR) {
+       c = ((c & 0xff000000) >>  0) |
+           ((c & 0x00ff0000) >> 16) |
+           ((c & 0x0000ff00) >>  0) |
+           ((c & 0x000000ff) << 16);
+    }
+
+    if (PIXMAN_FORMAT_TYPE (format) == PIXMAN_TYPE_BGRA) {
+       c = ((c & 0xff000000) >> 24) |
+           ((c & 0x00ff0000) >>  8) |
+           ((c & 0x0000ff00) <<  8) |
+           ((c & 0x000000ff) << 24);
+    }
+
+    if (format == PIXMAN_a8) {
+       c = c >> 24;
+    } else if (format == PIXMAN_r5g6b5 || format == PIXMAN_b5g6r5) {
+       c = ((((c) >> 3) & 0x001f) |
+            (((c) >> 5) & 0x07e0) |
+            (((c) >> 8) & 0xf800));
+    }
+
+    *pixel = c;
+    return TRUE;
+}
+
+static pixman_op_t
+_pixman_operator (cairo_operator_t op)
+{
+    switch ((int) op) {
+    case CAIRO_OPERATOR_CLEAR:
+       return PIXMAN_OP_CLEAR;
+
+    case CAIRO_OPERATOR_SOURCE:
+       return PIXMAN_OP_SRC;
+    case CAIRO_OPERATOR_OVER:
+       return PIXMAN_OP_OVER;
+    case CAIRO_OPERATOR_IN:
+       return PIXMAN_OP_IN;
+    case CAIRO_OPERATOR_OUT:
+       return PIXMAN_OP_OUT;
+    case CAIRO_OPERATOR_ATOP:
+       return PIXMAN_OP_ATOP;
+
+    case CAIRO_OPERATOR_DEST:
+       return PIXMAN_OP_DST;
+    case CAIRO_OPERATOR_DEST_OVER:
+       return PIXMAN_OP_OVER_REVERSE;
+    case CAIRO_OPERATOR_DEST_IN:
+       return PIXMAN_OP_IN_REVERSE;
+    case CAIRO_OPERATOR_DEST_OUT:
+       return PIXMAN_OP_OUT_REVERSE;
+    case CAIRO_OPERATOR_DEST_ATOP:
+       return PIXMAN_OP_ATOP_REVERSE;
+
+    case CAIRO_OPERATOR_XOR:
+       return PIXMAN_OP_XOR;
+    case CAIRO_OPERATOR_ADD:
+       return PIXMAN_OP_ADD;
+    case CAIRO_OPERATOR_SATURATE:
+       return PIXMAN_OP_SATURATE;
+
+    case CAIRO_OPERATOR_MULTIPLY:
+       return PIXMAN_OP_MULTIPLY;
+    case CAIRO_OPERATOR_SCREEN:
+       return PIXMAN_OP_SCREEN;
+    case CAIRO_OPERATOR_OVERLAY:
+       return PIXMAN_OP_OVERLAY;
+    case CAIRO_OPERATOR_DARKEN:
+       return PIXMAN_OP_DARKEN;
+    case CAIRO_OPERATOR_LIGHTEN:
+       return PIXMAN_OP_LIGHTEN;
+    case CAIRO_OPERATOR_COLOR_DODGE:
+       return PIXMAN_OP_COLOR_DODGE;
+    case CAIRO_OPERATOR_COLOR_BURN:
+       return PIXMAN_OP_COLOR_BURN;
+    case CAIRO_OPERATOR_HARD_LIGHT:
+       return PIXMAN_OP_HARD_LIGHT;
+    case CAIRO_OPERATOR_SOFT_LIGHT:
+       return PIXMAN_OP_SOFT_LIGHT;
+    case CAIRO_OPERATOR_DIFFERENCE:
+       return PIXMAN_OP_DIFFERENCE;
+    case CAIRO_OPERATOR_EXCLUSION:
+       return PIXMAN_OP_EXCLUSION;
+    case CAIRO_OPERATOR_HSL_HUE:
+       return PIXMAN_OP_HSL_HUE;
+    case CAIRO_OPERATOR_HSL_SATURATION:
+       return PIXMAN_OP_HSL_SATURATION;
+    case CAIRO_OPERATOR_HSL_COLOR:
+       return PIXMAN_OP_HSL_COLOR;
+    case CAIRO_OPERATOR_HSL_LUMINOSITY:
+       return PIXMAN_OP_HSL_LUMINOSITY;
+
+    default:
+       ASSERT_NOT_REACHED;
+       return PIXMAN_OP_OVER;
+    }
+}
+
+static cairo_bool_t
+fill_reduces_to_source (cairo_operator_t op,
+                       const cairo_color_t *color,
+                       cairo_image_surface_t *dst)
+{
+    if (op == CAIRO_OPERATOR_SOURCE || op == CAIRO_OPERATOR_CLEAR)
+       return TRUE;
+    if (op == CAIRO_OPERATOR_OVER && CAIRO_COLOR_IS_OPAQUE (color))
+       return TRUE;
+    if (dst->base.is_clear)
+       return op == CAIRO_OPERATOR_OVER || op == CAIRO_OPERATOR_ADD;
+
+    return FALSE;
+}
+
+static cairo_int_status_t
+fill_rectangles (void                  *_dst,
+                cairo_operator_t        op,
+                const cairo_color_t    *color,
+                cairo_rectangle_int_t  *rects,
+                int                     num_rects)
+{
+    cairo_image_surface_t *dst = _dst;
+    uint32_t pixel;
+    int i;
+
+    if (fill_reduces_to_source (op, color, dst) &&
+       color_to_pixel (color, dst->pixman_format, &pixel))
+    {
+       for (i = 0; i < num_rects; i++) {
+           pixman_fill ((uint32_t *) dst->data, dst->stride / sizeof (uint32_t),
+                        PIXMAN_FORMAT_BPP (dst->pixman_format),
+                        rects[i].x, rects[i].y,
+                        rects[i].width, rects[i].height,
+                        pixel);
+       }
+    }
+    else
+    {
+       pixman_image_t *src = _pixman_image_for_color (color);
+
+       op = _pixman_operator (op);
+       for (i = 0; i < num_rects; i++) {
+           pixman_image_composite32 (op,
+                                     src, NULL, dst->pixman_image,
+                                     0, 0,
+                                     0, 0,
+                                     rects[i].x, rects[i].y,
+                                     rects[i].width, rects[i].height);
+       }
+
+       pixman_image_unref (src);
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+fill_boxes (void               *_dst,
+           cairo_operator_t     op,
+           const cairo_color_t *color,
+           cairo_boxes_t       *boxes)
+{
+    cairo_image_surface_t *dst = _dst;
+    struct _cairo_boxes_chunk *chunk;
+    uint32_t pixel;
+    int i;
+
+    if (fill_reduces_to_source (op, color, dst) &&
+       color_to_pixel (color, dst->pixman_format, &pixel))
+    {
+       for (chunk = &boxes->chunks; chunk; chunk = chunk->next) {
+           for (i = 0; i < chunk->count; i++) {
+               int x = _cairo_fixed_integer_part (chunk->base[i].p1.x);
+               int y = _cairo_fixed_integer_part (chunk->base[i].p1.y);
+               int w = _cairo_fixed_integer_part (chunk->base[i].p2.x) - x;
+               int h = _cairo_fixed_integer_part (chunk->base[i].p2.y) - y;
+               pixman_fill ((uint32_t *) dst->data,
+                            dst->stride / sizeof (uint32_t),
+                            PIXMAN_FORMAT_BPP (dst->pixman_format),
+                            x, y, w, h, pixel);
+           }
+       }
+    }
+    else
+    {
+       pixman_image_t *src = _pixman_image_for_color (color);
+
+       op = _pixman_operator (op);
+       for (chunk = &boxes->chunks; chunk; chunk = chunk->next) {
+           for (i = 0; i < chunk->count; i++) {
+               int x1 = _cairo_fixed_integer_part (chunk->base[i].p1.x);
+               int y1 = _cairo_fixed_integer_part (chunk->base[i].p1.y);
+               int x2 = _cairo_fixed_integer_part (chunk->base[i].p2.x);
+               int y2 = _cairo_fixed_integer_part (chunk->base[i].p2.y);
+               pixman_image_composite32 (op,
+                                         src, NULL, dst->pixman_image,
+                                         0, 0,
+                                         0, 0,
+                                         x1, y1,
+                                         x2-x1, y2-y1);
+           }
+       }
+
+       pixman_image_unref (src);
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+composite (void                        *_dst,
+          cairo_operator_t     op,
+          cairo_surface_t      *abstract_src,
+          cairo_surface_t      *abstract_mask,
+          int                  src_x,
+          int                  src_y,
+          int                  mask_x,
+          int                  mask_y,
+          int                  dst_x,
+          int                  dst_y,
+          unsigned int         width,
+          unsigned int         height)
+{
+    cairo_image_source_t *src = (cairo_image_source_t *)abstract_src;
+    cairo_image_source_t *mask = (cairo_image_source_t *)abstract_mask;
+    if (mask) {
+       pixman_image_composite32 (_pixman_operator (op),
+                                 src->pixman_image, mask->pixman_image, to_pixman_image (_dst),
+                                 src_x, src_y,
+                                 mask_x, mask_y,
+                                 dst_x, dst_y,
+                                 width, height);
+    } else {
+       pixman_image_composite32 (_pixman_operator (op),
+                                 src->pixman_image, NULL, to_pixman_image (_dst),
+                                 src_x, src_y,
+                                 0, 0,
+                                 dst_x, dst_y,
+                                 width, height);
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+lerp (void                     *_dst,
+      cairo_surface_t          *abstract_src,
+      cairo_surface_t          *abstract_mask,
+      int                      src_x,
+      int                      src_y,
+      int                      mask_x,
+      int                      mask_y,
+      int                      dst_x,
+      int                      dst_y,
+      unsigned int             width,
+      unsigned int             height)
+{
+    cairo_image_surface_t *dst = _dst;
+    cairo_image_source_t *src = (cairo_image_source_t *)abstract_src;
+    cairo_image_source_t *mask = (cairo_image_source_t *)abstract_mask;
+
+#if PIXMAN_HAS_OP_LERP
+    pixman_image_composite32 (PIXMAN_OP_LERP_SRC,
+                             src->pixman_image, mask->pixman_image, dst->pixman_image,
+                             src_x,  src_y,
+                             mask_x, mask_y,
+                             dst_x,  dst_y,
+                             width,  height);
+#else
+    /* Punch the clip out of the destination */
+    pixman_image_composite32 (PIXMAN_OP_OUT_REVERSE,
+                             mask->pixman_image, NULL, dst->pixman_image,
+                             mask_x, mask_y,
+                             0,      0,
+                             dst_x,  dst_y,
+                             width,  height);
+
+    /* Now add the two results together */
+    pixman_image_composite32 (PIXMAN_OP_ADD,
+                             src->pixman_image, mask->pixman_image, dst->pixman_image,
+                             src_x,  src_y,
+                             mask_x, mask_y,
+                             dst_x,  dst_y,
+                             width,  height);
+#endif
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+composite_boxes (void                  *_dst,
+                cairo_operator_t       op,
+                cairo_surface_t        *abstract_src,
+                cairo_surface_t        *abstract_mask,
+                int                    src_x,
+                int                    src_y,
+                int                    mask_x,
+                int                    mask_y,
+                int                    dst_x,
+                int                    dst_y,
+                cairo_boxes_t          *boxes,
+                const cairo_rectangle_int_t  *extents)
+{
+    pixman_image_t *dst = to_pixman_image (_dst);
+    pixman_image_t *src = ((cairo_image_source_t *)abstract_src)->pixman_image;
+    pixman_image_t *mask = abstract_mask ? ((cairo_image_source_t *)abstract_mask)->pixman_image : NULL;
+    struct _cairo_boxes_chunk *chunk;
+    int i;
+
+    /* XXX consider using a region? saves multiple prepare-composite */
+
+    if (((cairo_surface_t *)_dst)->is_clear &&
+       (op == CAIRO_OPERATOR_SOURCE ||
+        op == CAIRO_OPERATOR_OVER ||
+        op == CAIRO_OPERATOR_ADD)) {
+       op = PIXMAN_OP_SRC;
+    } else if (mask) {
+       if (op == CAIRO_OPERATOR_CLEAR) {
+#if PIXMAN_HAS_OP_LERP
+           op = PIXMAN_OP_LERP_CLEAR;
+#else
+           src = _pixman_image_for_color (CAIRO_COLOR_WHITE);
+           op = PIXMAN_OP_OUT_REVERSE;
+#endif
+       } else if (op == CAIRO_OPERATOR_SOURCE) {
+#if PIXMAN_HAS_OP_LERP
+           op = PIXMAN_OP_LERP_SRC;
+#else
+           return CAIRO_INT_STATUS_UNSUPPORTED;
+#endif
+       } else {
+           op = _pixman_operator (op);
+       }
+    } else {
+       op = _pixman_operator (op);
+    }
+
+    for (chunk = &boxes->chunks; chunk; chunk = chunk->next) {
+       for (i = 0; i < chunk->count; i++) {
+           int x1 = _cairo_fixed_integer_part (chunk->base[i].p1.x);
+           int y1 = _cairo_fixed_integer_part (chunk->base[i].p1.y);
+           int x2 = _cairo_fixed_integer_part (chunk->base[i].p2.x);
+           int y2 = _cairo_fixed_integer_part (chunk->base[i].p2.y);
+
+           pixman_image_composite32 (op, src, mask, dst,
+                                     x1 + src_x, y1 + src_y,
+                                     x1 + mask_x, y1 + mask_y,
+                                     x1 + dst_x, y1 + dst_y,
+                                     x2 - x1, y2 - y1);
+       }
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+#define CAIRO_FIXED_16_16_MIN _cairo_fixed_from_int (-32768)
+#define CAIRO_FIXED_16_16_MAX _cairo_fixed_from_int (32767)
+
+static cairo_bool_t
+line_exceeds_16_16 (const cairo_line_t *line)
+{
+    return
+       line->p1.x <= CAIRO_FIXED_16_16_MIN ||
+       line->p1.x >= CAIRO_FIXED_16_16_MAX ||
+
+       line->p2.x <= CAIRO_FIXED_16_16_MIN ||
+       line->p2.x >= CAIRO_FIXED_16_16_MAX ||
+
+       line->p1.y <= CAIRO_FIXED_16_16_MIN ||
+       line->p1.y >= CAIRO_FIXED_16_16_MAX ||
+
+       line->p2.y <= CAIRO_FIXED_16_16_MIN ||
+       line->p2.y >= CAIRO_FIXED_16_16_MAX;
+}
+
+static void
+project_line_x_onto_16_16 (const cairo_line_t *line,
+                          cairo_fixed_t top,
+                          cairo_fixed_t bottom,
+                          pixman_line_fixed_t *out)
+{
+    /* XXX use fixed-point arithmetic? */
+    cairo_point_double_t p1, p2;
+    double m;
+
+    p1.x = _cairo_fixed_to_double (line->p1.x);
+    p1.y = _cairo_fixed_to_double (line->p1.y);
+
+    p2.x = _cairo_fixed_to_double (line->p2.x);
+    p2.y = _cairo_fixed_to_double (line->p2.y);
+
+    m = (p2.x - p1.x) / (p2.y - p1.y);
+    out->p1.x = _cairo_fixed_16_16_from_double (p1.x + m * _cairo_fixed_to_double (top - line->p1.y));
+    out->p2.x = _cairo_fixed_16_16_from_double (p1.x + m * _cairo_fixed_to_double (bottom - line->p1.y));
+}
+
+void
+_pixman_image_add_traps (pixman_image_t *image,
+                        int dst_x, int dst_y,
+                        cairo_traps_t *traps)
+{
+    cairo_trapezoid_t *t = traps->traps;
+    int num_traps = traps->num_traps;
+    while (num_traps--) {
+       pixman_trapezoid_t trap;
+
+       /* top/bottom will be clamped to surface bounds */
+       trap.top = _cairo_fixed_to_16_16 (t->top);
+       trap.bottom = _cairo_fixed_to_16_16 (t->bottom);
+
+       /* However, all the other coordinates will have been left untouched so
+        * as not to introduce numerical error. Recompute them if they
+        * exceed the 16.16 limits.
+        */
+       if (unlikely (line_exceeds_16_16 (&t->left))) {
+           project_line_x_onto_16_16 (&t->left, t->top, t->bottom, &trap.left);
+           trap.left.p1.y = trap.top;
+           trap.left.p2.y = trap.bottom;
+       } else {
+           trap.left.p1.x = _cairo_fixed_to_16_16 (t->left.p1.x);
+           trap.left.p1.y = _cairo_fixed_to_16_16 (t->left.p1.y);
+           trap.left.p2.x = _cairo_fixed_to_16_16 (t->left.p2.x);
+           trap.left.p2.y = _cairo_fixed_to_16_16 (t->left.p2.y);
+       }
+
+       if (unlikely (line_exceeds_16_16 (&t->right))) {
+           project_line_x_onto_16_16 (&t->right, t->top, t->bottom, &trap.right);
+           trap.right.p1.y = trap.top;
+           trap.right.p2.y = trap.bottom;
+       } else {
+           trap.right.p1.x = _cairo_fixed_to_16_16 (t->right.p1.x);
+           trap.right.p1.y = _cairo_fixed_to_16_16 (t->right.p1.y);
+           trap.right.p2.x = _cairo_fixed_to_16_16 (t->right.p2.x);
+           trap.right.p2.y = _cairo_fixed_to_16_16 (t->right.p2.y);
+       }
+
+       pixman_rasterize_trapezoid (image, &trap, -dst_x, -dst_y);
+       t++;
+    }
+}
+
+static cairo_int_status_t
+composite_traps (void                  *_dst,
+                cairo_operator_t       op,
+                cairo_surface_t        *abstract_src,
+                int                    src_x,
+                int                    src_y,
+                int                    dst_x,
+                int                    dst_y,
+                const cairo_rectangle_int_t *extents,
+                cairo_antialias_t      antialias,
+                cairo_traps_t          *traps)
+{
+    cairo_image_surface_t *dst = (cairo_image_surface_t *) _dst;
+    cairo_image_source_t *src = (cairo_image_source_t *) abstract_src;
+    pixman_image_t *mask;
+    pixman_format_code_t format;
+
+    /* Special case adding trapezoids onto a mask surface; we want to avoid
+     * creating an intermediate temporary mask unnecessarily.
+     *
+     * We make the assumption here that the portion of the trapezoids
+     * contained within the surface is bounded by [dst_x,dst_y,width,height];
+     * the Cairo core code passes bounds based on the trapezoid extents.
+     */
+    format = antialias == CAIRO_ANTIALIAS_NONE ? PIXMAN_a1 : PIXMAN_a8;
+    if (dst->pixman_format == format &&
+       (abstract_src == NULL ||
+        (op == CAIRO_OPERATOR_ADD && src->is_opaque_solid)))
+    {
+       _pixman_image_add_traps (dst->pixman_image, dst_x, dst_y, traps);
+       return CAIRO_STATUS_SUCCESS;
+    }
+
+    mask = pixman_image_create_bits (format,
+                                    extents->width, extents->height,
+                                    NULL, 0);
+    if (unlikely (mask == NULL))
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    _pixman_image_add_traps (mask, extents->x, extents->y, traps);
+    pixman_image_composite32 (_pixman_operator (op),
+                              src->pixman_image, mask, dst->pixman_image,
+                              extents->x + src_x, extents->y + src_y,
+                              0, 0,
+                              extents->x - dst_x, extents->y - dst_y,
+                              extents->width, extents->height);
+
+    pixman_image_unref (mask);
+
+    return  CAIRO_STATUS_SUCCESS;
+}
+
+static void
+set_point (pixman_point_fixed_t *p, cairo_point_t *c)
+{
+    p->x = _cairo_fixed_to_16_16 (c->x);
+    p->y = _cairo_fixed_to_16_16 (c->y);
+}
+
+void
+_pixman_image_add_tristrip (pixman_image_t *image,
+                           int dst_x, int dst_y,
+                           cairo_tristrip_t *strip)
+{
+    pixman_triangle_t tri;
+    pixman_point_fixed_t *p[3] = {&tri.p1, &tri.p2, &tri.p3 };
+    int n;
+
+    set_point (p[0], &strip->points[0]);
+    set_point (p[1], &strip->points[1]);
+    set_point (p[2], &strip->points[2]);
+    pixman_add_triangles (image, -dst_x, -dst_y, 1, &tri);
+    for (n = 3; n < strip->num_points; n++) {
+       set_point (p[n%3], &strip->points[n]);
+       pixman_add_triangles (image, -dst_x, -dst_y, 1, &tri);
+    }
+}
+
+static cairo_int_status_t
+composite_tristrip (void                       *_dst,
+                   cairo_operator_t    op,
+                   cairo_surface_t     *abstract_src,
+                   int                 src_x,
+                   int                 src_y,
+                   int                 dst_x,
+                   int                 dst_y,
+                   const cairo_rectangle_int_t *extents,
+                   cairo_antialias_t   antialias,
+                   cairo_tristrip_t    *strip)
+{
+    cairo_image_surface_t *dst = (cairo_image_surface_t *) _dst;
+    cairo_image_source_t *src = (cairo_image_source_t *) abstract_src;
+    pixman_image_t *mask;
+    pixman_format_code_t format;
+
+    if (strip->num_points < 3)
+       return CAIRO_STATUS_SUCCESS;
+
+    format = antialias == CAIRO_ANTIALIAS_NONE ? PIXMAN_a1 : PIXMAN_a8;
+    if (dst->pixman_format == format &&
+       (abstract_src == NULL ||
+        (op == CAIRO_OPERATOR_ADD && src->is_opaque_solid)))
+    {
+       _pixman_image_add_tristrip (dst->pixman_image, dst_x, dst_y, strip);
+       return CAIRO_STATUS_SUCCESS;
+    }
+
+    mask = pixman_image_create_bits (format,
+                                    extents->width, extents->height,
+                                    NULL, 0);
+    if (unlikely (mask == NULL))
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    _pixman_image_add_tristrip (mask, extents->x, extents->y, strip);
+    pixman_image_composite32 (_pixman_operator (op),
+                              src->pixman_image, mask, dst->pixman_image,
+                              extents->x + src_x, extents->y + src_y,
+                              0, 0,
+                              extents->x - dst_x, extents->y - dst_y,
+                              extents->width, extents->height);
+
+    pixman_image_unref (mask);
+
+    return  CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+check_composite_glyphs (const cairo_composite_rectangles_t *extents,
+                       cairo_scaled_font_t *scaled_font,
+                       cairo_glyph_t *glyphs,
+                       int *num_glyphs)
+{
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+composite_one_glyph (void                              *_dst,
+                    cairo_operator_t                    op,
+                    cairo_surface_t                    *_src,
+                    int                                 src_x,
+                    int                                 src_y,
+                    int                                 dst_x,
+                    int                                 dst_y,
+                    cairo_composite_glyphs_info_t       *info)
+{
+    cairo_image_surface_t *glyph_surface;
+    cairo_scaled_glyph_t *scaled_glyph;
+    cairo_status_t status;
+    int x, y;
+
+    status = _cairo_scaled_glyph_lookup (info->font,
+                                        info->glyphs[0].index,
+                                        CAIRO_SCALED_GLYPH_INFO_SURFACE,
+                                        &scaled_glyph);
+
+    if (unlikely (status))
+       return status;
+
+    glyph_surface = scaled_glyph->surface;
+    if (glyph_surface->width == 0 || glyph_surface->height == 0)
+       return CAIRO_INT_STATUS_NOTHING_TO_DO;
+
+    /* round glyph locations to the nearest pixel */
+    /* XXX: FRAGILE: We're ignoring device_transform scaling here. A bug? */
+    x = _cairo_lround (info->glyphs[0].x -
+                      glyph_surface->base.device_transform.x0);
+    y = _cairo_lround (info->glyphs[0].y -
+                      glyph_surface->base.device_transform.y0);
+
+    pixman_image_composite32 (_pixman_operator (op),
+                             ((cairo_image_source_t *)_src)->pixman_image,
+                             glyph_surface->pixman_image,
+                             to_pixman_image (_dst),
+                             x + src_x,  y + src_y,
+                             0, 0,
+                             x - dst_x, y - dst_y,
+                             glyph_surface->width,
+                             glyph_surface->height);
+
+    return CAIRO_INT_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+composite_glyphs_via_mask (void                                *_dst,
+                          cairo_operator_t              op,
+                          cairo_surface_t              *_src,
+                          int                           src_x,
+                          int                           src_y,
+                          int                           dst_x,
+                          int                           dst_y,
+                          cairo_composite_glyphs_info_t *info)
+{
+    cairo_scaled_glyph_t *glyph_cache[64];
+    cairo_bool_t component_alpha = FALSE;
+    uint8_t buf[2048];
+    pixman_image_t *mask;
+    cairo_status_t status;
+    int i;
+
+    /* XXX convert the glyphs to common formats a8/a8r8g8b8 to hit
+     * optimised paths through pixman. Should we increase the bit
+     * depth of the target surface, we should reconsider the appropriate
+     * mask formats.
+     */
+    i = (info->extents.width + 3) & ~3;
+    if (i * info->extents.height > (int) sizeof (buf)) {
+       mask = pixman_image_create_bits (PIXMAN_a8,
+                                       info->extents.width,
+                                       info->extents.height,
+                                       NULL, 0);
+    } else {
+       memset (buf, 0, i * info->extents.height);
+       mask = pixman_image_create_bits (PIXMAN_a8,
+                                       info->extents.width,
+                                       info->extents.height,
+                                       (uint32_t *)buf, i);
+    }
+    if (unlikely (mask == NULL))
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    memset (glyph_cache, 0, sizeof (glyph_cache));
+    status = CAIRO_STATUS_SUCCESS;
+
+    for (i = 0; i < info->num_glyphs; i++) {
+       cairo_image_surface_t *glyph_surface;
+       cairo_scaled_glyph_t *scaled_glyph;
+       unsigned long glyph_index = info->glyphs[i].index;
+       int cache_index = glyph_index % ARRAY_LENGTH (glyph_cache);
+       int x, y;
+
+       scaled_glyph = glyph_cache[cache_index];
+       if (scaled_glyph == NULL ||
+           _cairo_scaled_glyph_index (scaled_glyph) != glyph_index)
+       {
+           status = _cairo_scaled_glyph_lookup (info->font, glyph_index,
+                                                CAIRO_SCALED_GLYPH_INFO_SURFACE,
+                                                &scaled_glyph);
+
+           if (unlikely (status)) {
+               pixman_image_unref (mask);
+               return status;
+           }
+
+           glyph_cache[cache_index] = scaled_glyph;
+       }
+
+       glyph_surface = scaled_glyph->surface;
+       if (glyph_surface->width && glyph_surface->height) {
+           if (glyph_surface->base.content & CAIRO_CONTENT_COLOR &&
+               ! component_alpha) {
+               pixman_image_t *ca_mask;
+
+               ca_mask = pixman_image_create_bits (PIXMAN_a8r8g8b8,
+                                                   info->extents.width,
+                                                   info->extents.height,
+                                                   NULL, 0);
+               if (unlikely (ca_mask == NULL)) {
+                   pixman_image_unref (mask);
+                   return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+               }
+
+               pixman_image_composite32 (PIXMAN_OP_SRC,
+                                         mask, 0, ca_mask,
+                                         0, 0,
+                                         0, 0,
+                                         0, 0,
+                                         info->extents.width,
+                                         info->extents.height);
+               pixman_image_unref (mask);
+               mask = ca_mask;
+               component_alpha = TRUE;
+           }
+
+           /* round glyph locations to the nearest pixel */
+           /* XXX: FRAGILE: We're ignoring device_transform scaling here. A bug? */
+           x = _cairo_lround (info->glyphs[i].x -
+                              glyph_surface->base.device_transform.x0);
+           y = _cairo_lround (info->glyphs[i].y -
+                              glyph_surface->base.device_transform.y0);
+
+           pixman_image_composite32 (PIXMAN_OP_ADD,
+                                     glyph_surface->pixman_image, NULL, mask,
+                                      0, 0,
+                                     0, 0,
+                                      x - info->extents.x, y - info->extents.y,
+                                     glyph_surface->width,
+                                     glyph_surface->height);
+       }
+    }
+
+    if (component_alpha)
+       pixman_image_set_component_alpha (mask, TRUE);
+
+    pixman_image_composite32 (_pixman_operator (op),
+                             ((cairo_image_source_t *)_src)->pixman_image,
+                             mask,
+                             to_pixman_image (_dst),
+                             info->extents.x + src_x, info->extents.y + src_y,
+                             0, 0,
+                             info->extents.x - dst_x, info->extents.y - dst_y,
+                             info->extents.width, info->extents.height);
+    pixman_image_unref (mask);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+composite_glyphs (void                         *_dst,
+                 cairo_operator_t               op,
+                 cairo_surface_t               *_src,
+                 int                            src_x,
+                 int                            src_y,
+                 int                            dst_x,
+                 int                            dst_y,
+                 cairo_composite_glyphs_info_t *info)
+{
+    cairo_scaled_glyph_t *glyph_cache[64];
+    pixman_image_t *dst, *src;
+    cairo_status_t status;
+    int i;
+
+    if (info->num_glyphs == 1)
+       return composite_one_glyph(_dst, op, _src, src_x, src_y, dst_x, dst_y, info);
+
+    if (info->use_mask)
+       return composite_glyphs_via_mask(_dst, op, _src, src_x, src_y, dst_x, dst_y, info);
+
+    op = _pixman_operator (op);
+    dst = to_pixman_image (_dst);
+    src = ((cairo_image_source_t *)_src)->pixman_image;
+
+    memset (glyph_cache, 0, sizeof (glyph_cache));
+    status = CAIRO_STATUS_SUCCESS;
+
+    for (i = 0; i < info->num_glyphs; i++) {
+       int x, y;
+       cairo_image_surface_t *glyph_surface;
+       cairo_scaled_glyph_t *scaled_glyph;
+       unsigned long glyph_index = info->glyphs[i].index;
+       int cache_index = glyph_index % ARRAY_LENGTH (glyph_cache);
+
+       scaled_glyph = glyph_cache[cache_index];
+       if (scaled_glyph == NULL ||
+           _cairo_scaled_glyph_index (scaled_glyph) != glyph_index)
+       {
+           status = _cairo_scaled_glyph_lookup (info->font, glyph_index,
+                                                CAIRO_SCALED_GLYPH_INFO_SURFACE,
+                                                &scaled_glyph);
+
+           if (unlikely (status))
+               break;
+
+           glyph_cache[cache_index] = scaled_glyph;
+       }
+
+       glyph_surface = scaled_glyph->surface;
+       if (glyph_surface->width && glyph_surface->height) {
+           /* round glyph locations to the nearest pixel */
+           /* XXX: FRAGILE: We're ignoring device_transform scaling here. A bug? */
+           x = _cairo_lround (info->glyphs[i].x -
+                              glyph_surface->base.device_transform.x0);
+           y = _cairo_lround (info->glyphs[i].y -
+                              glyph_surface->base.device_transform.y0);
+
+           pixman_image_composite32 (op, src, glyph_surface->pixman_image, dst,
+                                      x + src_x,  y + src_y,
+                                      0, 0,
+                                      x - dst_x, y - dst_y,
+                                     glyph_surface->width,
+                                     glyph_surface->height);
+       }
+    }
+
+    return status;
+}
+
+const cairo_compositor_t *
+_cairo_image_traps_compositor_get (void)
+{
+    static cairo_traps_compositor_t compositor;
+
+    if (compositor.base.delegate == NULL) {
+       _cairo_traps_compositor_init (&compositor,
+                                     &__cairo_no_compositor);
+       compositor.acquire = acquire;
+       compositor.release = release;
+       compositor.set_clip_region = set_clip_region;
+       compositor.pattern_to_surface = _cairo_image_source_create_for_pattern;
+       compositor.draw_image_boxes = draw_image_boxes;
+       //compositor.copy_boxes = copy_boxes;
+       compositor.fill_boxes = fill_boxes;
+       //compositor.check_composite = check_composite;
+       compositor.composite = composite;
+       compositor.lerp = lerp;
+       //compositor.check_composite_boxes = check_composite_boxes;
+       compositor.composite_boxes = composite_boxes;
+       //compositor.check_composite_traps = check_composite_traps;
+       compositor.composite_traps = composite_traps;
+       //compositor.check_composite_tristrip = check_composite_traps;
+       compositor.composite_tristrip = composite_tristrip;
+       compositor.check_composite_glyphs = check_composite_glyphs;
+       compositor.composite_glyphs = composite_glyphs;
+    }
+
+    return &compositor.base;
+}
+
+const cairo_compositor_t *
+_cairo_image_mask_compositor_get (void)
+{
+    static cairo_mask_compositor_t compositor;
+
+    if (compositor.base.delegate == NULL) {
+       _cairo_mask_compositor_init (&compositor,
+                                    _cairo_image_traps_compositor_get ());
+       compositor.acquire = acquire;
+       compositor.release = release;
+       compositor.set_clip_region = set_clip_region;
+       compositor.pattern_to_surface = _cairo_image_source_create_for_pattern;
+       compositor.draw_image_boxes = draw_image_boxes;
+       compositor.fill_rectangles = fill_rectangles;
+       compositor.fill_boxes = fill_boxes;
+       //compositor.check_composite = check_composite;
+       compositor.composite = composite;
+       //compositor.lerp = lerp;
+       //compositor.check_composite_boxes = check_composite_boxes;
+       compositor.composite_boxes = composite_boxes;
+       compositor.check_composite_glyphs = check_composite_glyphs;
+       compositor.composite_glyphs = composite_glyphs;
+    }
+
+    return &compositor.base;
+}
+
+#if PIXMAN_HAS_COMPOSITOR
+typedef struct _cairo_image_span_renderer {
+    cairo_span_renderer_t base;
+
+    pixman_image_compositor_t *compositor;
+    pixman_image_t *src, *mask;
+    float opacity;
+    cairo_rectangle_int_t extents;
+} cairo_image_span_renderer_t;
+COMPILE_TIME_ASSERT (sizeof (cairo_image_span_renderer_t) <= sizeof (cairo_abstract_span_renderer_t));
+
+static cairo_status_t
+_cairo_image_bounded_opaque_spans (void *abstract_renderer,
+                                  int y, int height,
+                                  const cairo_half_open_span_t *spans,
+                                  unsigned num_spans)
+{
+    cairo_image_span_renderer_t *r = abstract_renderer;
+
+    if (num_spans == 0)
+       return CAIRO_STATUS_SUCCESS;
+
+    do {
+       if (spans[0].coverage)
+           pixman_image_compositor_blt (r->compositor,
+                                        spans[0].x, y,
+                                        spans[1].x - spans[0].x, height,
+                                        spans[0].coverage);
+       spans++;
+    } while (--num_spans > 1);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_image_bounded_spans (void *abstract_renderer,
+                           int y, int height,
+                           const cairo_half_open_span_t *spans,
+                           unsigned num_spans)
+{
+    cairo_image_span_renderer_t *r = abstract_renderer;
+
+    if (num_spans == 0)
+       return CAIRO_STATUS_SUCCESS;
+
+    do {
+       if (spans[0].coverage) {
+           pixman_image_compositor_blt (r->compositor,
+                                        spans[0].x, y,
+                                        spans[1].x - spans[0].x, height,
+                                        r->opacity * spans[0].coverage);
+       }
+       spans++;
+    } while (--num_spans > 1);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_image_unbounded_spans (void *abstract_renderer,
+                             int y, int height,
+                             const cairo_half_open_span_t *spans,
+                             unsigned num_spans)
+{
+    cairo_image_span_renderer_t *r = abstract_renderer;
+
+    assert (y + height <= r->extents.height);
+    if (y > r->extents.y) {
+       pixman_image_compositor_blt (r->compositor,
+                                    r->extents.x, r->extents.y,
+                                    r->extents.width, y - r->extents.y,
+                                    0);
+    }
+
+    if (num_spans == 0) {
+       pixman_image_compositor_blt (r->compositor,
+                                    r->extents.x, y,
+                                    r->extents.width,  height,
+                                    0);
+    } else {
+       if (spans[0].x != r->extents.x) {
+           pixman_image_compositor_blt (r->compositor,
+                                        r->extents.x, y,
+                                        spans[0].x - r->extents.x,
+                                        height,
+                                        0);
+       }
+
+       do {
+           assert (spans[0].x < r->extents.x + r->extents.width);
+           pixman_image_compositor_blt (r->compositor,
+                                        spans[0].x, y,
+                                        spans[1].x - spans[0].x, height,
+                                        r->opacity * spans[0].coverage);
+           spans++;
+       } while (--num_spans > 1);
+
+       if (spans[0].x != r->extents.x + r->extents.width) {
+           assert (spans[0].x < r->extents.x + r->extents.width);
+           pixman_image_compositor_blt (r->compositor,
+                                        spans[0].x,     y,
+                                        r->extents.x + r->extents.width - spans[0].x, height,
+                                        0);
+       }
+    }
+
+    r->extents.y = y + height;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_image_clipped_spans (void *abstract_renderer,
+                           int y, int height,
+                           const cairo_half_open_span_t *spans,
+                           unsigned num_spans)
+{
+    cairo_image_span_renderer_t *r = abstract_renderer;
+
+    assert (num_spans);
+
+    do {
+       if (! spans[0].inverse)
+           pixman_image_compositor_blt (r->compositor,
+                                        spans[0].x, y,
+                                        spans[1].x - spans[0].x, height,
+                                        r->opacity * spans[0].coverage);
+       spans++;
+    } while (--num_spans > 1);
+
+    r->extents.y = y + height;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_image_finish_unbounded_spans (void *abstract_renderer)
+{
+    cairo_image_span_renderer_t *r = abstract_renderer;
+
+    if (r->extents.y < r->extents.height) {
+       pixman_image_compositor_blt (r->compositor,
+                                    r->extents.x, r->extents.y,
+                                    r->extents.width,
+                                    r->extents.height - r->extents.y,
+                                    0);
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+span_renderer_init (cairo_abstract_span_renderer_t     *_r,
+                   const cairo_composite_rectangles_t *composite,
+                   cairo_bool_t                         needs_clip)
+{
+    cairo_image_span_renderer_t *r = (cairo_image_span_renderer_t *)_r;
+    cairo_image_surface_t *dst = (cairo_image_surface_t *)composite->surface;
+    const cairo_pattern_t *source = &composite->source_pattern.base;
+    cairo_operator_t op = composite->op;
+    int src_x, src_y;
+    int mask_x, mask_y;
+
+    if (op == CAIRO_OPERATOR_CLEAR) {
+       op = PIXMAN_OP_LERP_CLEAR;
+    } else if (dst->base.is_clear &&
+              (op == CAIRO_OPERATOR_SOURCE ||
+               op == CAIRO_OPERATOR_OVER ||
+               op == CAIRO_OPERATOR_ADD)) {
+       op = PIXMAN_OP_SRC;
+    } else if (op == CAIRO_OPERATOR_SOURCE) {
+       op = PIXMAN_OP_LERP_SRC;
+    } else {
+       op = _pixman_operator (op);
+    }
+
+    r->compositor = NULL;
+    r->mask = NULL;
+    r->src = _pixman_image_for_pattern (dst, source, FALSE,
+                                       &composite->unbounded,
+                                       &composite->source_sample_area,
+                                       &src_x, &src_y);
+    if (unlikely (r->src == NULL))
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    r->opacity = 1.0;
+    if (composite->mask_pattern.base.type == CAIRO_PATTERN_TYPE_SOLID) {
+       r->opacity = composite->mask_pattern.solid.color.alpha;
+    } else {
+       r->mask = _pixman_image_for_pattern (dst,
+                                            &composite->mask_pattern.base,
+                                            TRUE,
+                                            &composite->unbounded,
+                                            &composite->mask_sample_area,
+                                            &mask_x, &mask_y);
+       if (unlikely (r->mask == NULL))
+           return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+       /* XXX Component-alpha? */
+       if ((dst->base.content & CAIRO_CONTENT_COLOR) == 0 &&
+           _cairo_pattern_is_opaque (source, &composite->source_sample_area))
+       {
+           pixman_image_unref (r->src);
+           r->src = r->mask;
+           src_x = mask_x;
+           src_y = mask_y;
+           r->mask = NULL;
+       }
+    }
+
+    if (composite->is_bounded) {
+       if (r->opacity == 1.)
+           r->base.render_rows = _cairo_image_bounded_opaque_spans;
+       else
+           r->base.render_rows = _cairo_image_bounded_spans;
+       r->base.finish = NULL;
+    } else {
+       if (needs_clip)
+           r->base.render_rows = _cairo_image_clipped_spans;
+       else
+           r->base.render_rows = _cairo_image_unbounded_spans;
+        r->base.finish = _cairo_image_finish_unbounded_spans;
+       r->extents = composite->unbounded;
+       r->extents.height += r->extents.y;
+    }
+
+    r->compositor =
+       pixman_image_create_compositor (op, r->src, r->mask, dst->pixman_image,
+                                       composite->unbounded.x + src_x,
+                                       composite->unbounded.y + src_y,
+                                       composite->unbounded.x + mask_x,
+                                       composite->unbounded.y + mask_y,
+                                       composite->unbounded.x,
+                                       composite->unbounded.y,
+                                       composite->unbounded.width,
+                                       composite->unbounded.height);
+    if (unlikely (r->compositor == NULL))
+       return CAIRO_INT_STATUS_NOTHING_TO_DO;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+span_renderer_fini (cairo_abstract_span_renderer_t *_r,
+                   cairo_int_status_t status)
+{
+    cairo_image_span_renderer_t *r = (cairo_image_span_renderer_t *) _r;
+
+    if (status == CAIRO_INT_STATUS_SUCCESS && r->base.finish)
+       r->base.finish (r);
+
+    if (r->compositor)
+       pixman_image_compositor_destroy (r->compositor);
+
+    if (r->src)
+       pixman_image_unref (r->src);
+    if (r->mask)
+       pixman_image_unref (r->mask);
+}
+#else
+typedef struct _cairo_image_span_renderer {
+    cairo_span_renderer_t base;
+
+    cairo_rectangle_int_t extents;
+
+    float opacity;
+    int stride;
+    uint8_t *data;
+
+    const cairo_composite_rectangles_t *composite;
+    pixman_image_t *src, *mask;
+    int src_x, src_y;
+
+    uint8_t op;
+
+    uint8_t buf[sizeof(cairo_abstract_span_renderer_t)-128];
+} cairo_image_span_renderer_t;
+COMPILE_TIME_ASSERT (sizeof (cairo_image_span_renderer_t) <= sizeof (cairo_abstract_span_renderer_t));
+
+static cairo_status_t
+_cairo_image_spans (void *abstract_renderer,
+                   int y, int height,
+                   const cairo_half_open_span_t *spans,
+                   unsigned num_spans)
+{
+    cairo_image_span_renderer_t *r = abstract_renderer;
+    uint8_t *mask, *row;
+    int len;
+
+    if (num_spans == 0)
+       return CAIRO_STATUS_SUCCESS;
+
+    mask = r->data + (y - r->extents.y) * r->stride;
+    mask += spans[0].x - r->extents.x;
+    row = mask;
+
+    do {
+       len = spans[1].x - spans[0].x;
+       if (spans[0].coverage) {
+           *row++ = r->opacity * spans[0].coverage;
+           if (--len)
+               memset (row, row[-1], len);
+       }
+       row += len;
+       spans++;
+    } while (--num_spans > 1);
+
+    len = row - mask;
+    row = mask;
+    while (--height) {
+       mask += r->stride;
+       memcpy (mask, row, len);
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_image_spans_and_zero (void *abstract_renderer,
+                            int y, int height,
+                            const cairo_half_open_span_t *spans,
+                            unsigned num_spans)
+{
+    cairo_image_span_renderer_t *r = abstract_renderer;
+    uint8_t *mask;
+    int len;
+
+    mask = r->data;
+    if (y > r->extents.y) {
+       len = (y - r->extents.y) * r->stride;
+       memset (mask, 0, len);
+       mask += len;
+    }
+
+    r->extents.y = y + height;
+    r->data = mask + height * r->stride;
+    if (num_spans == 0) {
+       memset (mask, 0, height * r->stride);
+    } else {
+       uint8_t *row = mask;
+
+       if (spans[0].x != r->extents.x) {
+           len = spans[0].x - r->extents.x;
+           memset (row, 0, len);
+           row += len;
+       }
+
+       do {
+           len = spans[1].x - spans[0].x;
+           *row++ = r->opacity * spans[0].coverage;
+           if (len > 1) {
+               memset (row, row[-1], --len);
+               row += len;
+           }
+           spans++;
+       } while (--num_spans > 1);
+
+       if (spans[0].x != r->extents.x + r->extents.width) {
+           len = r->extents.x + r->extents.width - spans[0].x;
+           memset (row, 0, len);
+       }
+
+       row = mask;
+       while (--height) {
+           mask += r->stride;
+           memcpy (mask, row, r->extents.width);
+       }
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_image_finish_spans_and_zero (void *abstract_renderer)
+{
+    cairo_image_span_renderer_t *r = abstract_renderer;
+
+    if (r->extents.y < r->extents.height)
+       memset (r->data, 0, (r->extents.height - r->extents.y) * r->stride);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+span_renderer_init (cairo_abstract_span_renderer_t     *_r,
+                   const cairo_composite_rectangles_t *composite,
+                   cairo_bool_t                         needs_clip)
+{
+    cairo_image_span_renderer_t *r = (cairo_image_span_renderer_t *)_r;
+    cairo_image_surface_t *dst = (cairo_image_surface_t *)composite->surface;
+    const cairo_pattern_t *source = &composite->source_pattern.base;
+    cairo_operator_t op = composite->op;
+
+    r->composite = composite;
+    r->mask = NULL;
+    r->src = NULL;
+
+    if (needs_clip)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    if (op == CAIRO_OPERATOR_CLEAR) {
+#if PIXMAN_HAS_OP_LERP
+       op = PIXMAN_OP_LERP_CLEAR;
+#else
+       source = &_cairo_pattern_white.base;
+       op = PIXMAN_OP_OUT_REVERSE;
+#endif
+    } else if (dst->base.is_clear &&
+              (op == CAIRO_OPERATOR_SOURCE ||
+               op == CAIRO_OPERATOR_OVER ||
+               op == CAIRO_OPERATOR_ADD)) {
+       op = PIXMAN_OP_SRC;
+    } else if (op == CAIRO_OPERATOR_SOURCE) {
+#if PIXMAN_HAS_OP_LERP
+       op = PIXMAN_OP_LERP_SRC;
+#else
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+#endif
+    } else {
+       op = _pixman_operator (op);
+    }
+    r->op = op;
+
+    r->src = _pixman_image_for_pattern (dst, source, FALSE,
+                                       &composite->unbounded,
+                                       &composite->source_sample_area,
+                                       &r->src_x, &r->src_y);
+    if (unlikely (r->src == NULL))
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    r->opacity = 1.0;
+    if (composite->mask_pattern.base.type == CAIRO_PATTERN_TYPE_SOLID) {
+       r->opacity = composite->mask_pattern.solid.color.alpha;
+    } else {
+       pixman_image_t *mask;
+       int mask_x, mask_y;
+
+       mask = _pixman_image_for_pattern (dst,
+                                         &composite->mask_pattern.base,
+                                         TRUE,
+                                         &composite->unbounded,
+                                         &composite->mask_sample_area,
+                                         &mask_x, &mask_y);
+       if (unlikely (mask == NULL))
+           return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+       /* XXX Component-alpha? */
+       if ((dst->base.content & CAIRO_CONTENT_COLOR) == 0 &&
+           _cairo_pattern_is_opaque (source, &composite->source_sample_area))
+       {
+           pixman_image_unref (r->src);
+           r->src = mask;
+           r->src_x = mask_x;
+           r->src_y = mask_y;
+           mask = NULL;
+       }
+
+       if (mask) {
+           pixman_image_unref (mask);
+           return CAIRO_INT_STATUS_UNSUPPORTED;
+       }
+    }
+
+    r->extents = composite->unbounded;
+    r->stride = (r->extents.width + 3) & ~3;
+    if (r->extents.height * r->stride > (int)sizeof (r->buf)) {
+       r->mask = pixman_image_create_bits (PIXMAN_a8,
+                                           r->extents.width,
+                                           r->extents.height,
+                                           NULL, 0);
+
+       r->base.render_rows = _cairo_image_spans;
+       r->base.finish = NULL;
+    } else {
+       r->mask = pixman_image_create_bits (PIXMAN_a8,
+                                           r->extents.width,
+                                           r->extents.height,
+                                           (uint32_t *)r->buf, r->stride);
+
+       r->base.render_rows = _cairo_image_spans_and_zero;
+       r->base.finish = _cairo_image_finish_spans_and_zero;
+    }
+    if (unlikely (r->mask == NULL))
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    r->data = (uint8_t *) pixman_image_get_data (r->mask);
+    r->stride = pixman_image_get_stride (r->mask);
+
+    r->extents.height += r->extents.y;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+span_renderer_fini (cairo_abstract_span_renderer_t *_r,
+                   cairo_int_status_t status)
+{
+    cairo_image_span_renderer_t *r = (cairo_image_span_renderer_t *) _r;
+
+    if (likely (status == CAIRO_INT_STATUS_SUCCESS)) {
+       const cairo_composite_rectangles_t *composite = r->composite;
+
+       if (r->base.finish)
+           r->base.finish (r);
+
+       pixman_image_composite32 (r->op, r->src, r->mask,
+                                 to_pixman_image (composite->surface),
+                                 composite->unbounded.x + r->src_x,
+                                 composite->unbounded.y + r->src_y,
+                                 0, 0,
+                                 composite->unbounded.x,
+                                 composite->unbounded.y,
+                                 composite->unbounded.width,
+                                 composite->unbounded.height);
+    }
+
+    if (r->src)
+       pixman_image_unref (r->src);
+    if (r->mask)
+       pixman_image_unref (r->mask);
+}
+#endif
+
+const cairo_compositor_t *
+_cairo_image_spans_compositor_get (void)
+{
+    static cairo_spans_compositor_t compositor;
+
+    if (compositor.base.delegate == NULL) {
+       _cairo_spans_compositor_init (&compositor,
+                                     _cairo_image_traps_compositor_get());
+
+       compositor.flags = 0;
+#if PIXMAN_HAS_OP_LERP
+       compositor.flags |= CAIRO_SPANS_COMPOSITOR_HAS_LERP;
+#endif
+
+       //compositor.acquire = acquire;
+       //compositor.release = release;
+       compositor.fill_boxes = fill_boxes;
+       compositor.pattern_to_surface = _cairo_image_source_create_for_pattern;
+       //compositor.check_composite_boxes = check_composite_boxes;
+       compositor.composite_boxes = composite_boxes;
+       //compositor.check_span_renderer = check_span_renderer;
+       compositor.renderer_init = span_renderer_init;
+       compositor.renderer_fini = span_renderer_fini;
+    }
+
+    return &compositor.base;
+}
diff --git a/src/cairo-image-info-private.h b/src/cairo-image-info-private.h
new file mode 100644 (file)
index 0000000..0d9ef84
--- /dev/null
@@ -0,0 +1,63 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2008 Adrian Johnson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Adrian Johnson.
+ *
+ * Contributor(s):
+ *     Adrian Johnson <ajohnson@redneon.com>
+ */
+
+#ifndef CAIRO_IMAGE_INFO_PRIVATE_H
+#define CAIRO_IMAGE_INFO_PRIVATE_H
+
+#include "cairoint.h"
+
+typedef struct _cairo_image_info {
+    int                 width;
+    int                 height;
+    int                 num_components;
+    int                 bits_per_component;
+} cairo_image_info_t;
+
+cairo_private cairo_int_status_t
+_cairo_image_info_get_jpeg_info (cairo_image_info_t    *info,
+                                const unsigned char    *data,
+                                long                    length);
+
+cairo_private cairo_int_status_t
+_cairo_image_info_get_jpx_info (cairo_image_info_t     *info,
+                               const unsigned char     *data,
+                               unsigned long            length);
+
+cairo_private cairo_int_status_t
+_cairo_image_info_get_png_info (cairo_image_info_t     *info,
+                               const unsigned char     *data,
+                               unsigned long            length);
+
+#endif /* CAIRO_IMAGE_INFO_PRIVATE_H */
diff --git a/src/cairo-image-info.c b/src/cairo-image-info.c
new file mode 100644 (file)
index 0000000..4489698
--- /dev/null
@@ -0,0 +1,292 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2008 Adrian Johnson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Adrian Johnson.
+ *
+ * Contributor(s):
+ *     Adrian Johnson <ajohnson@redneon.com>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-error-private.h"
+#include "cairo-image-info-private.h"
+
+static uint32_t
+_get_be32 (const unsigned char *p)
+{
+    return p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3];
+}
+
+/* JPEG (image/jpeg)
+ *
+ * http://www.w3.org/Graphics/JPEG/itu-t81.pdf
+ */
+
+/* Markers with no parameters. All other markers are followed by a two
+ * byte length of the parameters. */
+#define TEM       0x01
+#define RST_begin 0xd0
+#define RST_end   0xd7
+#define SOI       0xd8
+#define EOI       0xd9
+
+/* Start of frame markers. */
+#define SOF0  0xc0
+#define SOF1  0xc1
+#define SOF2  0xc2
+#define SOF3  0xc3
+#define SOF5  0xc5
+#define SOF6  0xc6
+#define SOF7  0xc7
+#define SOF9  0xc9
+#define SOF10 0xca
+#define SOF11 0xcb
+#define SOF13 0xcd
+#define SOF14 0xce
+#define SOF15 0xcf
+
+static const unsigned char *
+_jpeg_skip_segment (const unsigned char *p)
+{
+    int len;
+
+    p++;
+    len = (p[0] << 8) | p[1];
+
+    return p + len;
+}
+
+static void
+_jpeg_extract_info (cairo_image_info_t *info, const unsigned char *p)
+{
+    info->width = (p[6] << 8) + p[7];
+    info->height = (p[4] << 8) + p[5];
+    info->num_components = p[8];
+    info->bits_per_component = p[3];
+}
+
+cairo_int_status_t
+_cairo_image_info_get_jpeg_info (cairo_image_info_t    *info,
+                                const unsigned char    *data,
+                                long                    length)
+{
+    const unsigned char *p = data;
+
+    while (p + 1 < data + length) {
+       if (*p != 0xff)
+           return CAIRO_INT_STATUS_UNSUPPORTED;
+       p++;
+
+       switch (*p) {
+           /* skip fill bytes */
+       case 0xff:
+           p++;
+           break;
+
+       case TEM:
+       case SOI:
+       case EOI:
+           p++;
+           break;
+
+       case SOF0:
+       case SOF1:
+       case SOF2:
+       case SOF3:
+       case SOF5:
+       case SOF6:
+       case SOF7:
+       case SOF9:
+       case SOF10:
+       case SOF11:
+       case SOF13:
+       case SOF14:
+       case SOF15:
+           /* Start of frame found. Extract the image parameters. */
+           if (p + 8 > data + length)
+               return CAIRO_INT_STATUS_UNSUPPORTED;
+
+           _jpeg_extract_info (info, p);
+           return CAIRO_STATUS_SUCCESS;
+
+       default:
+           if (*p >= RST_begin && *p <= RST_end) {
+               p++;
+               break;
+           }
+
+           if (p + 2 > data + length)
+               return CAIRO_INT_STATUS_UNSUPPORTED;
+
+           p = _jpeg_skip_segment (p);
+           break;
+       }
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+/* JPEG 2000 (image/jp2)
+ *
+ * http://www.jpeg.org/public/15444-1annexi.pdf
+ */
+
+#define JPX_FILETYPE 0x66747970
+#define JPX_JP2_HEADER 0x6A703268
+#define JPX_IMAGE_HEADER 0x69686472
+
+static const unsigned char _jpx_signature[] = {
+    0x00, 0x00, 0x00, 0x0c, 0x6a, 0x50, 0x20, 0x20, 0x0d, 0x0a, 0x87, 0x0a
+};
+
+static const unsigned char *
+_jpx_next_box (const unsigned char *p)
+{
+    return p + _get_be32 (p);
+}
+
+static const unsigned char *
+_jpx_get_box_contents (const unsigned char *p)
+{
+    return p + 8;
+}
+
+static cairo_bool_t
+_jpx_match_box (const unsigned char *p, const unsigned char *end, uint32_t type)
+{
+    uint32_t length;
+
+    if (p + 8 < end) {
+       length = _get_be32 (p);
+       if (_get_be32 (p + 4) == type &&  p + length < end)
+           return TRUE;
+    }
+
+    return FALSE;
+}
+
+static const unsigned char *
+_jpx_find_box (const unsigned char *p, const unsigned char *end, uint32_t type)
+{
+    while (p < end) {
+       if (_jpx_match_box (p, end, type))
+           return p;
+       p = _jpx_next_box (p);
+    }
+
+    return NULL;
+}
+
+static void
+_jpx_extract_info (const unsigned char *p, cairo_image_info_t *info)
+{
+    info->height = _get_be32 (p);
+    info->width = _get_be32 (p + 4);
+    info->num_components = (p[8] << 8) + p[9];
+    info->bits_per_component = p[10];
+}
+
+cairo_int_status_t
+_cairo_image_info_get_jpx_info (cairo_image_info_t     *info,
+                               const unsigned char     *data,
+                               unsigned long            length)
+{
+    const unsigned char *p = data;
+    const unsigned char *end = data + length;
+
+    /* First 12 bytes must be the JPEG 2000 signature box. */
+    if (length < ARRAY_LENGTH(_jpx_signature) ||
+       memcmp(p, _jpx_signature, ARRAY_LENGTH(_jpx_signature)) != 0)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    p += ARRAY_LENGTH(_jpx_signature);
+
+    /* Next box must be a File Type Box */
+    if (! _jpx_match_box (p, end, JPX_FILETYPE))
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    p = _jpx_next_box (p);
+
+    /* Locate the JP2 header box. */
+    p = _jpx_find_box (p, end, JPX_JP2_HEADER);
+    if (!p)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    /* Step into the JP2 header box. First box must be the Image
+     * Header */
+    p = _jpx_get_box_contents (p);
+    if (! _jpx_match_box (p, end, JPX_IMAGE_HEADER))
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    /* Get the image info */
+    p = _jpx_get_box_contents (p);
+    _jpx_extract_info (p, info);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+/* PNG (image/png)
+ *
+ * http://www.w3.org/TR/2003/REC-PNG-20031110/
+ */
+
+#define PNG_IHDR 0x49484452
+
+static const unsigned char _png_magic[8] = { 137, 80, 78, 71, 13, 10, 26, 10 };
+
+cairo_int_status_t
+_cairo_image_info_get_png_info (cairo_image_info_t     *info,
+                               const unsigned char     *data,
+                               unsigned long            length)
+{
+    const unsigned char *p = data;
+    const unsigned char *end = data + length;
+
+    if (length < 8 || memcmp (data, _png_magic, 8) != 0)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    p += 8;
+
+    /* The first chunk must be IDHR. IDHR has 13 bytes of data plus
+     * the 12 bytes of overhead for the chunk. */
+    if (p + 13 + 12 > end)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    p += 4;
+    if (_get_be32 (p) != PNG_IHDR)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    p += 4;
+    info->width = _get_be32 (p);
+    p += 4;
+    info->height = _get_be32 (p);
+
+    return CAIRO_STATUS_SUCCESS;
+}
diff --git a/src/cairo-image-mask-compositor.c b/src/cairo-image-mask-compositor.c
new file mode 100644 (file)
index 0000000..33fd6dd
--- /dev/null
@@ -0,0 +1,408 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2003 University of Southern California
+ * Copyright © 2009,2010,2011 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *     Carl D. Worth <cworth@cworth.org>
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+/* This compositor is slightly pointless. Just exists for testing
+ * and as skeleton code.
+ */
+
+#include "cairoint.h"
+
+#include "cairo-image-surface-private.h"
+
+#include "cairo-compositor-private.h"
+#include "cairo-region-private.h"
+
+static cairo_int_status_t
+acquire (void *abstract_dst)
+{
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+release (void *abstract_dst)
+{
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+set_clip_region (void *_surface,
+                cairo_region_t *region)
+{
+    cairo_image_surface_t *surface = _surface;
+    pixman_region32_t *rgn = region ? &region->rgn : NULL;
+
+    if (! pixman_image_set_clip_region32 (surface->pixman_image, rgn))
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_bool_t
+has_snapshot (void *_dst,
+             const cairo_pattern_t *pattern)
+{
+    return FALSE;
+}
+
+static cairo_int_status_t
+draw_image (void *_dst,
+           cairo_image_surface_t *image,
+           int src_x, int src_y,
+           int width, int height,
+           int dst_x, int dst_y)
+{
+    cairo_image_surface_t *dst = (cairo_image_surface_t *)_dst;
+
+    pixman_image_composite32 (PIXMAN_OP_SRC,
+                             image->pixman_image, NULL, dst->pixman_image,
+                             src_x, src_y,
+                             0, 0,
+                             dst_x, dst_y,
+                             width, height);
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static inline uint32_t
+color_to_uint32 (const cairo_color_t *color)
+{
+    return
+        (color->alpha_short >> 8 << 24) |
+        (color->red_short >> 8 << 16)   |
+        (color->green_short & 0xff00)   |
+        (color->blue_short >> 8);
+}
+
+static inline cairo_bool_t
+color_to_pixel (const cairo_color_t    *color,
+               double opacity,
+                pixman_format_code_t    format,
+                uint32_t               *pixel)
+{
+    cairo_color_t opacity_color;
+    uint32_t c;
+
+    if (!(format == PIXMAN_a8r8g8b8     ||
+          format == PIXMAN_x8r8g8b8     ||
+          format == PIXMAN_a8b8g8r8     ||
+          format == PIXMAN_x8b8g8r8     ||
+          format == PIXMAN_b8g8r8a8     ||
+          format == PIXMAN_b8g8r8x8     ||
+          format == PIXMAN_r5g6b5       ||
+          format == PIXMAN_b5g6r5       ||
+          format == PIXMAN_a8))
+    {
+       return FALSE;
+    }
+
+    if (opacity != 1.0) {
+       _cairo_color_init_rgba (&opacity_color,
+                               color->red,
+                               color->green,
+                               color->blue,
+                               color->alpha * opacity);
+       color = &opacity_color;
+    }
+    c = color_to_uint32 (color);
+
+    if (PIXMAN_FORMAT_TYPE (format) == PIXMAN_TYPE_ABGR) {
+       c = ((c & 0xff000000) >>  0) |
+           ((c & 0x00ff0000) >> 16) |
+           ((c & 0x0000ff00) >>  0) |
+           ((c & 0x000000ff) << 16);
+    }
+
+    if (PIXMAN_FORMAT_TYPE (format) == PIXMAN_TYPE_BGRA) {
+       c = ((c & 0xff000000) >> 24) |
+           ((c & 0x00ff0000) >>  8) |
+           ((c & 0x0000ff00) <<  8) |
+           ((c & 0x000000ff) << 24);
+    }
+
+    if (format == PIXMAN_a8) {
+       c = c >> 24;
+    } else if (format == PIXMAN_r5g6b5 || format == PIXMAN_b5g6r5) {
+       c = ((((c) >> 3) & 0x001f) |
+            (((c) >> 5) & 0x07e0) |
+            (((c) >> 8) & 0xf800));
+    }
+
+    *pixel = c;
+    return TRUE;
+}
+
+static cairo_int_status_t
+fill_rectangles (void                  *_dst,
+                cairo_operator_t        op,
+                const cairo_color_t    *color,
+                cairo_rectangle_int_t  *rects,
+                int                     num_rects)
+{
+    cairo_image_surface_t *dst = _dst;
+    uint32_t pixel;
+    int i;
+
+    if (! color_to_pixel (color, 1.0, dst->pixman_format, &pixel))
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    for (i = 0; i < num_rects; i++) {
+       pixman_fill ((uint32_t *) dst->data, dst->stride / sizeof (uint32_t),
+                    PIXMAN_FORMAT_BPP (dst->pixman_format),
+                    rects[i].x, rects[i].y,
+                    rects[i].width, rects[i].height,
+                    pixel);
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+fill_boxes (void               *_dst,
+           cairo_operator_t     op,
+           const cairo_color_t *color,
+           cairo_boxes_t       *boxes)
+{
+    cairo_image_surface_t *dst = _dst;
+    struct _cairo_boxes_chunk *chunk;
+    uint32_t pixel;
+    int i;
+
+    assert (boxes->is_pixel_aligned);
+
+    if (! color_to_pixel (color, 1.0, dst->pixman_format, &pixel))
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    for (chunk = &boxes->chunks; chunk; chunk = chunk->next) {
+       for (i = 0; i < chunk->count; i++) {
+           int x1 = _cairo_fixed_integer_part (chunk->base[i].p1.x);
+           int y1 = _cairo_fixed_integer_part (chunk->base[i].p1.y);
+           int x2 = _cairo_fixed_integer_part (chunk->base[i].p2.x);
+           int y2 = _cairo_fixed_integer_part (chunk->base[i].p2.y);
+           pixman_fill ((uint32_t *) dst->data, dst->stride / sizeof (uint32_t),
+                        PIXMAN_FORMAT_BPP (dst->pixman_format),
+                        x1, y1, x2 - x1, y2 - y1,
+                        pixel);
+       }
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static pixman_op_t
+_pixman_operator (cairo_operator_t op)
+{
+    switch ((int) op) {
+    case CAIRO_OPERATOR_CLEAR:
+       return PIXMAN_OP_CLEAR;
+
+    case CAIRO_OPERATOR_SOURCE:
+       return PIXMAN_OP_SRC;
+    case CAIRO_OPERATOR_OVER:
+       return PIXMAN_OP_OVER;
+    case CAIRO_OPERATOR_IN:
+       return PIXMAN_OP_IN;
+    case CAIRO_OPERATOR_OUT:
+       return PIXMAN_OP_OUT;
+    case CAIRO_OPERATOR_ATOP:
+       return PIXMAN_OP_ATOP;
+
+    case CAIRO_OPERATOR_DEST:
+       return PIXMAN_OP_DST;
+    case CAIRO_OPERATOR_DEST_OVER:
+       return PIXMAN_OP_OVER_REVERSE;
+    case CAIRO_OPERATOR_DEST_IN:
+       return PIXMAN_OP_IN_REVERSE;
+    case CAIRO_OPERATOR_DEST_OUT:
+       return PIXMAN_OP_OUT_REVERSE;
+    case CAIRO_OPERATOR_DEST_ATOP:
+       return PIXMAN_OP_ATOP_REVERSE;
+
+    case CAIRO_OPERATOR_XOR:
+       return PIXMAN_OP_XOR;
+    case CAIRO_OPERATOR_ADD:
+       return PIXMAN_OP_ADD;
+    case CAIRO_OPERATOR_SATURATE:
+       return PIXMAN_OP_SATURATE;
+
+    case CAIRO_OPERATOR_MULTIPLY:
+       return PIXMAN_OP_MULTIPLY;
+    case CAIRO_OPERATOR_SCREEN:
+       return PIXMAN_OP_SCREEN;
+    case CAIRO_OPERATOR_OVERLAY:
+       return PIXMAN_OP_OVERLAY;
+    case CAIRO_OPERATOR_DARKEN:
+       return PIXMAN_OP_DARKEN;
+    case CAIRO_OPERATOR_LIGHTEN:
+       return PIXMAN_OP_LIGHTEN;
+    case CAIRO_OPERATOR_COLOR_DODGE:
+       return PIXMAN_OP_COLOR_DODGE;
+    case CAIRO_OPERATOR_COLOR_BURN:
+       return PIXMAN_OP_COLOR_BURN;
+    case CAIRO_OPERATOR_HARD_LIGHT:
+       return PIXMAN_OP_HARD_LIGHT;
+    case CAIRO_OPERATOR_SOFT_LIGHT:
+       return PIXMAN_OP_SOFT_LIGHT;
+    case CAIRO_OPERATOR_DIFFERENCE:
+       return PIXMAN_OP_DIFFERENCE;
+    case CAIRO_OPERATOR_EXCLUSION:
+       return PIXMAN_OP_EXCLUSION;
+    case CAIRO_OPERATOR_HSL_HUE:
+       return PIXMAN_OP_HSL_HUE;
+    case CAIRO_OPERATOR_HSL_SATURATION:
+       return PIXMAN_OP_HSL_SATURATION;
+    case CAIRO_OPERATOR_HSL_COLOR:
+       return PIXMAN_OP_HSL_COLOR;
+    case CAIRO_OPERATOR_HSL_LUMINOSITY:
+       return PIXMAN_OP_HSL_LUMINOSITY;
+
+    default:
+       ASSERT_NOT_REACHED;
+       return PIXMAN_OP_OVER;
+    }
+}
+
+static cairo_int_status_t
+composite (void                        *_dst,
+          cairo_operator_t     op,
+          cairo_surface_t      *abstract_src,
+          cairo_surface_t      *abstract_mask,
+          int                  src_x,
+          int                  src_y,
+          int                  mask_x,
+          int                  mask_y,
+          int                  dst_x,
+          int                  dst_y,
+          unsigned int         width,
+          unsigned int         height)
+{
+    cairo_image_surface_t *dst = _dst;
+    cairo_pixman_source_t *src = (cairo_pixman_source_t *)abstract_src;
+    cairo_pixman_source_t *mask = (cairo_pixman_source_t *)abstract_mask;
+    if (mask) {
+       pixman_image_composite32 (_pixman_operator (op),
+                                 src->pixman_image, mask->pixman_image, dst->pixman_image,
+                                 src_x, src_y,
+                                 mask_x, mask_y,
+                                 dst_x, dst_y,
+                                 width, height);
+    } else {
+       pixman_image_composite32 (_pixman_operator (op),
+                                 src->pixman_image, NULL, dst->pixman_image,
+                                 src_x, src_y,
+                                 0, 0,
+                                 dst_x, dst_y,
+                                 width, height);
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+composite_boxes (void                  *_dst,
+                cairo_operator_t       op,
+                cairo_surface_t        *abstract_src,
+                cairo_surface_t        *abstract_mask,
+                int                    src_x,
+                int                    src_y,
+                int                    mask_x,
+                int                    mask_y,
+                int                    dst_x,
+                int                    dst_y,
+                cairo_boxes_t          *boxes)
+{
+    cairo_image_surface_t *dst = _dst;
+    cairo_pixman_source_t *src = (cairo_pixman_source_t *)abstract_src;
+    cairo_pixman_source_t *mask = (cairo_pixman_source_t *)abstract_mask;
+    struct _cairo_boxes_chunk *chunk;
+    int i;
+
+    assert (boxes->is_pixel_aligned);
+
+    op = _pixman_operator (op);
+    for (chunk = &boxes->chunks; chunk; chunk = chunk->next) {
+       for (i = 0; i < chunk->count; i++) {
+           int x1 = _cairo_fixed_integer_part (chunk->base[i].p1.x);
+           int y1 = _cairo_fixed_integer_part (chunk->base[i].p1.y);
+           int x2 = _cairo_fixed_integer_part (chunk->base[i].p2.x);
+           int y2 = _cairo_fixed_integer_part (chunk->base[i].p2.y);
+
+           if (mask) {
+               pixman_image_composite32 (op,
+                                         src->pixman_image, mask->pixman_image, dst->pixman_image,
+                                         x1 + src_x, y1 + src_y,
+                                         x1 + mask_x, y1 + mask_y,
+                                         x1 + dst_x, y1 + dst_y,
+                                         x2 - x1, y2 - y1);
+           } else {
+               pixman_image_composite32 (op,
+                                         src->pixman_image, NULL, dst->pixman_image,
+                                         x1 + src_x, y1 + src_y,
+                                         0, 0,
+                                         x1 + dst_x, y1 + dst_y,
+                                         x2 - x1, y2 - y1);
+           }
+       }
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+const cairo_compositor_t *
+_cairo_image_mask_compositor_get (void)
+{
+    static cairo_mask_compositor_t compositor;
+
+    if (compositor.base.delegate == NULL) {
+       _cairo_mask_compositor_init (&compositor,
+                                    _cairo_image_traps_compositor_get ());
+       compositor.acquire = acquire;
+       compositor.release = release;
+       compositor.set_clip_region = set_clip_region;
+       compositor.pattern_to_surface = _cairo_pixman_source_create_for_pattern;
+       compositor.has_snapshot = has_snapshot;
+       compositor.draw_image = draw_image;
+       compositor.fill_rectangles = fill_rectangles;
+       compositor.fill_boxes = fill_boxes;
+       //compositor.check_composite = check_composite;
+       compositor.composite = composite;
+       //compositor.check_composite_boxes = check_composite_boxes;
+       compositor.composite_boxes = composite_boxes;
+    }
+
+    return &compositor.base;
+}
diff --git a/src/cairo-image-source.c b/src/cairo-image-source.c
new file mode 100644 (file)
index 0000000..943790d
--- /dev/null
@@ -0,0 +1,1004 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2003 University of Southern California
+ * Copyright © 2009,2010,2011 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *     Carl D. Worth <cworth@cworth.org>
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+/* The purpose of this file/surface is to simply translate a pattern
+ * to a pixman_image_t and thence to feed it back to the general
+ * compositor interface.
+ */
+
+#include "cairoint.h"
+
+#include "cairo-image-surface-private.h"
+
+#include "cairo-compositor-private.h"
+#include "cairo-error-private.h"
+#include "cairo-pattern-private.h"
+#include "cairo-paginated-private.h"
+#include "cairo-recording-surface-private.h"
+#include "cairo-surface-observer-private.h"
+#include "cairo-surface-snapshot-private.h"
+#include "cairo-surface-subsurface-private.h"
+
+#define PIXMAN_MAX_INT ((pixman_fixed_1 >> 1) - pixman_fixed_e) /* need to ensure deltas also fit */
+
+#if CAIRO_NO_MUTEX
+#define PIXMAN_HAS_ATOMIC_OPS 1
+#endif
+
+#if PIXMAN_HAS_ATOMIC_OPS
+static pixman_image_t *__pixman_transparent_image;
+static pixman_image_t *__pixman_black_image;
+static pixman_image_t *__pixman_white_image;
+
+static pixman_image_t *
+_pixman_transparent_image (void)
+{
+    pixman_image_t *image;
+
+    image = __pixman_transparent_image;
+    if (unlikely (image == NULL)) {
+       pixman_color_t color;
+
+       color.red   = 0x00;
+       color.green = 0x00;
+       color.blue  = 0x00;
+       color.alpha = 0x00;
+
+       image = pixman_image_create_solid_fill (&color);
+       if (unlikely (image == NULL))
+           return NULL;
+
+       if (_cairo_atomic_ptr_cmpxchg (&__pixman_transparent_image,
+                                      NULL, image))
+       {
+           pixman_image_ref (image);
+       }
+    } else {
+       pixman_image_ref (image);
+    }
+
+    return image;
+}
+
+static pixman_image_t *
+_pixman_black_image (void)
+{
+    pixman_image_t *image;
+
+    image = __pixman_black_image;
+    if (unlikely (image == NULL)) {
+       pixman_color_t color;
+
+       color.red   = 0x00;
+       color.green = 0x00;
+       color.blue  = 0x00;
+       color.alpha = 0xffff;
+
+       image = pixman_image_create_solid_fill (&color);
+       if (unlikely (image == NULL))
+           return NULL;
+
+       if (_cairo_atomic_ptr_cmpxchg (&__pixman_black_image,
+                                      NULL, image))
+       {
+           pixman_image_ref (image);
+       }
+    } else {
+       pixman_image_ref (image);
+    }
+
+    return image;
+}
+
+static pixman_image_t *
+_pixman_white_image (void)
+{
+    pixman_image_t *image;
+
+    image = __pixman_white_image;
+    if (unlikely (image == NULL)) {
+       pixman_color_t color;
+
+       color.red   = 0xffff;
+       color.green = 0xffff;
+       color.blue  = 0xffff;
+       color.alpha = 0xffff;
+
+       image = pixman_image_create_solid_fill (&color);
+       if (unlikely (image == NULL))
+           return NULL;
+
+       if (_cairo_atomic_ptr_cmpxchg (&__pixman_white_image,
+                                      NULL, image))
+       {
+           pixman_image_ref (image);
+       }
+    } else {
+       pixman_image_ref (image);
+    }
+
+    return image;
+}
+
+static uint32_t
+hars_petruska_f54_1_random (void)
+{
+#define rol(x,k) ((x << k) | (x >> (32-k)))
+    static uint32_t x;
+    return x = (x ^ rol (x, 5) ^ rol (x, 24)) + 0x37798849;
+#undef rol
+}
+
+static struct {
+    cairo_color_t color;
+    pixman_image_t *image;
+} cache[16];
+static int n_cached;
+
+#else  /* !PIXMAN_HAS_ATOMIC_OPS */
+static pixman_image_t *
+_pixman_transparent_image (void)
+{
+    return _pixman_image_for_color (CAIRO_COLOR_TRANSPARENT);
+}
+
+static pixman_image_t *
+_pixman_black_image (void)
+{
+    return _pixman_image_for_color (CAIRO_COLOR_BLACK);
+}
+
+static pixman_image_t *
+_pixman_white_image (void)
+{
+    return _pixman_image_for_color (CAIRO_COLOR_WHITE);
+}
+#endif /* !PIXMAN_HAS_ATOMIC_OPS */
+
+
+pixman_image_t *
+_pixman_image_for_color (const cairo_color_t *cairo_color)
+{
+    pixman_color_t color;
+    pixman_image_t *image;
+
+#if PIXMAN_HAS_ATOMIC_OPS
+    int i;
+
+    if (CAIRO_COLOR_IS_CLEAR (cairo_color))
+       return _pixman_transparent_image ();
+
+    if (CAIRO_COLOR_IS_OPAQUE (cairo_color)) {
+       if (cairo_color->red_short <= 0x00ff &&
+           cairo_color->green_short <= 0x00ff &&
+           cairo_color->blue_short <= 0x00ff)
+       {
+           return _pixman_black_image ();
+       }
+
+       if (cairo_color->red_short >= 0xff00 &&
+           cairo_color->green_short >= 0xff00 &&
+           cairo_color->blue_short >= 0xff00)
+       {
+           return _pixman_white_image ();
+       }
+    }
+
+    CAIRO_MUTEX_LOCK (_cairo_image_solid_cache_mutex);
+    for (i = 0; i < n_cached; i++) {
+       if (_cairo_color_equal (&cache[i].color, cairo_color)) {
+           image = pixman_image_ref (cache[i].image);
+           goto UNLOCK;
+       }
+    }
+#endif
+
+    color.red   = cairo_color->red_short;
+    color.green = cairo_color->green_short;
+    color.blue  = cairo_color->blue_short;
+    color.alpha = cairo_color->alpha_short;
+
+    image = pixman_image_create_solid_fill (&color);
+#if PIXMAN_HAS_ATOMIC_OPS
+    if (image == NULL)
+       goto UNLOCK;
+
+    if (n_cached < ARRAY_LENGTH (cache)) {
+       i = n_cached++;
+    } else {
+       i = hars_petruska_f54_1_random () % ARRAY_LENGTH (cache);
+       pixman_image_unref (cache[i].image);
+    }
+    cache[i].image = pixman_image_ref (image);
+    cache[i].color = *cairo_color;
+
+UNLOCK:
+    CAIRO_MUTEX_UNLOCK (_cairo_image_solid_cache_mutex);
+#endif
+    return image;
+}
+
+
+void
+_cairo_image_reset_static_data (void)
+{
+#if PIXMAN_HAS_ATOMIC_OPS
+    while (n_cached)
+       pixman_image_unref (cache[--n_cached].image);
+
+    if (__pixman_transparent_image) {
+       pixman_image_unref (__pixman_transparent_image);
+       __pixman_transparent_image = NULL;
+    }
+
+    if (__pixman_black_image) {
+       pixman_image_unref (__pixman_black_image);
+       __pixman_black_image = NULL;
+    }
+
+    if (__pixman_white_image) {
+       pixman_image_unref (__pixman_white_image);
+       __pixman_white_image = NULL;
+    }
+#endif
+}
+
+static pixman_image_t *
+_pixman_image_for_gradient (const cairo_gradient_pattern_t *pattern,
+                           const cairo_rectangle_int_t *extents,
+                           int *ix, int *iy)
+{
+    pixman_image_t       *pixman_image;
+    pixman_gradient_stop_t pixman_stops_static[2];
+    pixman_gradient_stop_t *pixman_stops = pixman_stops_static;
+    pixman_transform_t      pixman_transform;
+    cairo_matrix_t matrix;
+    cairo_circle_double_t extremes[2];
+    pixman_point_fixed_t p1, p2;
+    unsigned int i;
+    cairo_int_status_t status;
+
+    if (pattern->n_stops > ARRAY_LENGTH(pixman_stops_static)) {
+       pixman_stops = _cairo_malloc_ab (pattern->n_stops,
+                                        sizeof(pixman_gradient_stop_t));
+       if (unlikely (pixman_stops == NULL))
+           return NULL;
+    }
+
+    for (i = 0; i < pattern->n_stops; i++) {
+       pixman_stops[i].x = _cairo_fixed_16_16_from_double (pattern->stops[i].offset);
+       pixman_stops[i].color.red   = pattern->stops[i].color.red_short;
+       pixman_stops[i].color.green = pattern->stops[i].color.green_short;
+       pixman_stops[i].color.blue  = pattern->stops[i].color.blue_short;
+       pixman_stops[i].color.alpha = pattern->stops[i].color.alpha_short;
+    }
+
+    _cairo_gradient_pattern_fit_to_range (pattern, PIXMAN_MAX_INT >> 1, &matrix, extremes);
+
+    p1.x = _cairo_fixed_16_16_from_double (extremes[0].center.x);
+    p1.y = _cairo_fixed_16_16_from_double (extremes[0].center.y);
+    p2.x = _cairo_fixed_16_16_from_double (extremes[1].center.x);
+    p2.y = _cairo_fixed_16_16_from_double (extremes[1].center.y);
+
+    if (pattern->base.type == CAIRO_PATTERN_TYPE_LINEAR) {
+       pixman_image = pixman_image_create_linear_gradient (&p1, &p2,
+                                                           pixman_stops,
+                                                           pattern->n_stops);
+    } else {
+       pixman_fixed_t r1, r2;
+
+       r1   = _cairo_fixed_16_16_from_double (extremes[0].radius);
+       r2   = _cairo_fixed_16_16_from_double (extremes[1].radius);
+
+       pixman_image = pixman_image_create_radial_gradient (&p1, &p2, r1, r2,
+                                                           pixman_stops,
+                                                           pattern->n_stops);
+    }
+
+    if (pixman_stops != pixman_stops_static)
+       free (pixman_stops);
+
+    if (unlikely (pixman_image == NULL))
+       return NULL;
+
+    *ix = *iy = 0;
+    status = _cairo_matrix_to_pixman_matrix_offset (&matrix, pattern->base.filter,
+                                                   extents->x + extents->width/2.,
+                                                   extents->y + extents->height/2.,
+                                                   &pixman_transform, ix, iy);
+    if (status != CAIRO_INT_STATUS_NOTHING_TO_DO) {
+       if (unlikely (status != CAIRO_INT_STATUS_SUCCESS) ||
+           ! pixman_image_set_transform (pixman_image, &pixman_transform))
+       {
+           pixman_image_unref (pixman_image);
+           return NULL;
+       }
+    }
+
+    {
+       pixman_repeat_t pixman_repeat;
+
+       switch (pattern->base.extend) {
+       default:
+       case CAIRO_EXTEND_NONE:
+           pixman_repeat = PIXMAN_REPEAT_NONE;
+           break;
+       case CAIRO_EXTEND_REPEAT:
+           pixman_repeat = PIXMAN_REPEAT_NORMAL;
+           break;
+       case CAIRO_EXTEND_REFLECT:
+           pixman_repeat = PIXMAN_REPEAT_REFLECT;
+           break;
+       case CAIRO_EXTEND_PAD:
+           pixman_repeat = PIXMAN_REPEAT_PAD;
+           break;
+       }
+
+       pixman_image_set_repeat (pixman_image, pixman_repeat);
+    }
+
+    return pixman_image;
+}
+
+static pixman_image_t *
+_pixman_image_for_mesh (const cairo_mesh_pattern_t *pattern,
+                       const cairo_rectangle_int_t *extents,
+                       int *tx, int *ty)
+{
+    pixman_image_t *image;
+    int width, height;
+
+    *tx = -extents->x;
+    *ty = -extents->y;
+    width = extents->width;
+    height = extents->height;
+
+    image = pixman_image_create_bits (PIXMAN_a8r8g8b8, width, height, NULL, 0);
+    if (unlikely (image == NULL))
+       return NULL;
+
+    _cairo_mesh_pattern_rasterize (pattern,
+                                  pixman_image_get_data (image),
+                                  width, height,
+                                  pixman_image_get_stride (image),
+                                  *tx, *ty);
+    return image;
+}
+
+struct acquire_source_cleanup {
+    cairo_surface_t *surface;
+    cairo_image_surface_t *image;
+    void *image_extra;
+};
+
+static void
+_acquire_source_cleanup (pixman_image_t *pixman_image,
+                        void *closure)
+{
+    struct acquire_source_cleanup *data = closure;
+
+    _cairo_surface_release_source_image (data->surface,
+                                        data->image,
+                                        data->image_extra);
+    free (data);
+}
+
+static uint16_t
+expand_channel (uint16_t v, uint32_t bits)
+{
+    int offset = 16 - bits;
+    while (offset > 0) {
+       v |= v >> bits;
+       offset -= bits;
+       bits += bits;
+    }
+    return v;
+}
+
+static pixman_image_t *
+_pixel_to_solid (cairo_image_surface_t *image, int x, int y)
+{
+    uint32_t pixel;
+    pixman_color_t color;
+
+    switch (image->format) {
+    default:
+    case CAIRO_FORMAT_INVALID:
+       ASSERT_NOT_REACHED;
+       return NULL;
+
+    case CAIRO_FORMAT_A1:
+       pixel = *(uint8_t *) (image->data + y * image->stride + x/8);
+       return pixel & (1 << (x&7)) ? _pixman_black_image () : _pixman_transparent_image ();
+
+    case CAIRO_FORMAT_A8:
+       color.alpha = *(uint8_t *) (image->data + y * image->stride + x);
+       color.alpha |= color.alpha << 8;
+       if (color.alpha == 0)
+           return _pixman_transparent_image ();
+       if (color.alpha == 0xffff)
+           return _pixman_black_image ();
+
+       color.red = color.green = color.blue = 0;
+       return pixman_image_create_solid_fill (&color);
+
+    case CAIRO_FORMAT_RGB16_565:
+       pixel = *(uint16_t *) (image->data + y * image->stride + 2 * x);
+       if (pixel == 0)
+           return _pixman_black_image ();
+       if (pixel == 0xffff)
+           return _pixman_white_image ();
+
+       color.alpha = 0xffff;
+       color.red = expand_channel ((pixel >> 11 & 0x1f) << 11, 5);
+       color.green = expand_channel ((pixel >> 5 & 0x3f) << 10, 6);
+       color.blue = expand_channel ((pixel & 0x1f) << 11, 5);
+       return pixman_image_create_solid_fill (&color);
+
+    case CAIRO_FORMAT_RGB30:
+       pixel = *(uint32_t *) (image->data + y * image->stride + 4 * x);
+       pixel &= 0x3fffffff; /* ignore alpha bits */
+       if (pixel == 0)
+           return _pixman_black_image ();
+       if (pixel == 0x3fffffff)
+           return _pixman_white_image ();
+
+       /* convert 10bpc to 16bpc */
+       color.alpha = 0xffff;
+       color.red = expand_channel((pixel >> 20) & 0x3fff, 10);
+       color.green = expand_channel((pixel >> 10) & 0x3fff, 10);
+       color.blue = expand_channel(pixel & 0x3fff, 10);
+       return pixman_image_create_solid_fill (&color);
+
+    case CAIRO_FORMAT_ARGB32:
+    case CAIRO_FORMAT_RGB24:
+       pixel = *(uint32_t *) (image->data + y * image->stride + 4 * x);
+       color.alpha = image->format == CAIRO_FORMAT_ARGB32 ? (pixel >> 24) | (pixel >> 16 & 0xff00) : 0xffff;
+       if (color.alpha == 0)
+           return _pixman_transparent_image ();
+       if (pixel == 0xffffffff)
+           return _pixman_white_image ();
+       if (color.alpha == 0xffff && (pixel & 0xffffff) == 0)
+           return _pixman_black_image ();
+
+       color.red = (pixel >> 16 & 0xff) | (pixel >> 8 & 0xff00);
+       color.green = (pixel >> 8 & 0xff) | (pixel & 0xff00);
+       color.blue = (pixel & 0xff) | (pixel << 8 & 0xff00);
+       return pixman_image_create_solid_fill (&color);
+    }
+}
+
+static cairo_bool_t
+_pixman_image_set_properties (pixman_image_t *pixman_image,
+                             const cairo_pattern_t *pattern,
+                             const cairo_rectangle_int_t *extents,
+                             int *ix,int *iy)
+{
+    pixman_transform_t pixman_transform;
+    cairo_int_status_t status;
+
+    status = _cairo_matrix_to_pixman_matrix_offset (&pattern->matrix,
+                                                   pattern->filter,
+                                                   extents->x + extents->width/2.,
+                                                   extents->y + extents->height/2.,
+                                                   &pixman_transform, ix, iy);
+    if (status == CAIRO_INT_STATUS_NOTHING_TO_DO)
+    {
+       /* If the transform is an identity, we don't need to set it
+        * and we can use any filtering, so choose the fastest one. */
+       pixman_image_set_filter (pixman_image, PIXMAN_FILTER_NEAREST, NULL, 0);
+    }
+    else if (unlikely (status != CAIRO_INT_STATUS_SUCCESS ||
+                      ! pixman_image_set_transform (pixman_image,
+                                                    &pixman_transform)))
+    {
+       return FALSE;
+    }
+    else
+    {
+       pixman_filter_t pixman_filter;
+
+       switch (pattern->filter) {
+       case CAIRO_FILTER_FAST:
+           pixman_filter = PIXMAN_FILTER_FAST;
+           break;
+       case CAIRO_FILTER_GOOD:
+           pixman_filter = PIXMAN_FILTER_GOOD;
+           break;
+       case CAIRO_FILTER_BEST:
+           pixman_filter = PIXMAN_FILTER_BEST;
+           break;
+       case CAIRO_FILTER_NEAREST:
+           pixman_filter = PIXMAN_FILTER_NEAREST;
+           break;
+       case CAIRO_FILTER_BILINEAR:
+           pixman_filter = PIXMAN_FILTER_BILINEAR;
+           break;
+       case CAIRO_FILTER_GAUSSIAN:
+           /* XXX: The GAUSSIAN value has no implementation in cairo
+            * whatsoever, so it was really a mistake to have it in the
+            * API. We could fix this by officially deprecating it, or
+            * else inventing semantics and providing an actual
+            * implementation for it. */
+       default:
+           pixman_filter = PIXMAN_FILTER_BEST;
+       }
+
+       pixman_image_set_filter (pixman_image, pixman_filter, NULL, 0);
+    }
+
+    {
+       pixman_repeat_t pixman_repeat;
+
+       switch (pattern->extend) {
+       default:
+       case CAIRO_EXTEND_NONE:
+           pixman_repeat = PIXMAN_REPEAT_NONE;
+           break;
+       case CAIRO_EXTEND_REPEAT:
+           pixman_repeat = PIXMAN_REPEAT_NORMAL;
+           break;
+       case CAIRO_EXTEND_REFLECT:
+           pixman_repeat = PIXMAN_REPEAT_REFLECT;
+           break;
+       case CAIRO_EXTEND_PAD:
+           pixman_repeat = PIXMAN_REPEAT_PAD;
+           break;
+       }
+
+       pixman_image_set_repeat (pixman_image, pixman_repeat);
+    }
+
+    if (pattern->has_component_alpha)
+       pixman_image_set_component_alpha (pixman_image, TRUE);
+
+    return TRUE;
+}
+
+static pixman_image_t *
+_pixman_image_for_recording (cairo_image_surface_t *dst,
+                            const cairo_surface_pattern_t *pattern,
+                            cairo_bool_t is_mask,
+                            const cairo_rectangle_int_t *extents,
+                            const cairo_rectangle_int_t *sample,
+                            int *ix, int *iy)
+{
+    cairo_surface_t *source, *clone;
+    cairo_rectangle_int_t limit;
+    pixman_image_t *pixman_image;
+    cairo_status_t status;
+    cairo_extend_t extend;
+    cairo_matrix_t *m, matrix;
+    int tx = 0, ty = 0;
+
+    *ix = *iy = 0;
+
+    source = pattern->surface;
+    if (_cairo_surface_is_subsurface (source))
+       source = _cairo_surface_subsurface_get_target_with_offset (source, &tx, &ty);
+    if (_cairo_surface_is_snapshot (source))
+       source = _cairo_surface_snapshot_get_target (source);
+    if (_cairo_surface_is_observer (source))
+       source = _cairo_surface_observer_get_target (source);
+    if (_cairo_surface_is_paginated (source))
+       source = _cairo_paginated_surface_get_target (source);
+
+    extend = pattern->base.extend;
+    if (_cairo_surface_get_extents (source, &limit)) {
+       if (sample->x >= limit.x &&
+           sample->y >= limit.y &&
+           sample->x + sample->width  <= limit.x + limit.width &&
+           sample->y + sample->height <= limit.y + limit.height)
+       {
+           extend = CAIRO_EXTEND_NONE;
+       }
+       else if (extend == CAIRO_EXTEND_NONE &&
+                (sample->x + sample->width <= limit.x ||
+                 sample->x >= limit.x + limit.width ||
+                 sample->y + sample->height <= limit.y ||
+                 sample->y >= limit.y + limit.height))
+       {
+           return _pixman_transparent_image ();
+       }
+    } else
+       extend = CAIRO_EXTEND_NONE;
+
+    if (extend == CAIRO_EXTEND_NONE) {
+       if (! _cairo_rectangle_intersect (&limit, sample))
+           return _pixman_transparent_image ();
+
+       if (! _cairo_matrix_is_identity (&pattern->base.matrix)) {
+           double x1, y1, x2, y2;
+
+           matrix = pattern->base.matrix;
+           status = cairo_matrix_invert (&matrix);
+           assert (status == CAIRO_STATUS_SUCCESS);
+
+           x1 = limit.x;
+           y1 = limit.y;
+           x2 = limit.x + limit.width;
+           y2 = limit.y + limit.height;
+
+           _cairo_matrix_transform_bounding_box (&matrix,
+                                                 &x1, &y1, &x2, &y2, NULL);
+
+           limit.x = floor (x1);
+           limit.y = floor (y1);
+           limit.width  = ceil (x2) - limit.x;
+           limit.height = ceil (y2) - limit.y;
+       }
+    }
+
+    if (dst->base.content == source->content)
+       clone = cairo_image_surface_create (dst->format,
+                                           limit.width, limit.height);
+    else
+       clone = _cairo_image_surface_create_with_content (source->content,
+                                                         limit.width,
+                                                         limit.height);
+    cairo_surface_set_device_offset (clone, -limit.x, -limit.y);
+
+    m = NULL;
+    if (extend == CAIRO_EXTEND_NONE) {
+       matrix = pattern->base.matrix;
+       if (tx | ty)
+           cairo_matrix_translate (&matrix, tx, ty);
+       m = &matrix;
+    } else {
+       /* XXX extract scale factor for repeating patterns */
+    }
+
+    status = _cairo_recording_surface_replay_with_clip (source, m, clone, NULL);
+    if (unlikely (status)) {
+       cairo_surface_destroy (clone);
+       return NULL;
+    }
+
+    pixman_image = pixman_image_ref (((cairo_image_surface_t *)clone)->pixman_image);
+    cairo_surface_destroy (clone);
+
+    if (extend != CAIRO_EXTEND_NONE) {
+       if (! _pixman_image_set_properties (pixman_image,
+                                           &pattern->base, extents,
+                                           ix, iy)) {
+           pixman_image_unref (pixman_image);
+           pixman_image= NULL;
+       }
+    } else {
+       *ix = -limit.x;
+       *iy = -limit.y;
+    }
+
+    return pixman_image;
+}
+
+static pixman_image_t *
+_pixman_image_for_surface (cairo_image_surface_t *dst,
+                          const cairo_surface_pattern_t *pattern,
+                          cairo_bool_t is_mask,
+                          const cairo_rectangle_int_t *extents,
+                          const cairo_rectangle_int_t *sample,
+                          int *ix, int *iy)
+{
+    cairo_extend_t extend = pattern->base.extend;
+    pixman_image_t *pixman_image;
+
+    *ix = *iy = 0;
+    pixman_image = NULL;
+    if (pattern->surface->type == CAIRO_SURFACE_TYPE_RECORDING)
+       return _pixman_image_for_recording(dst, pattern,
+                                          is_mask, extents, sample,
+                                          ix, iy);
+
+    if (pattern->surface->type == CAIRO_SURFACE_TYPE_IMAGE &&
+       (! is_mask || ! pattern->base.has_component_alpha ||
+        (pattern->surface->content & CAIRO_CONTENT_COLOR) == 0))
+    {
+       cairo_image_surface_t *source = (cairo_image_surface_t *) pattern->surface;
+       cairo_surface_type_t type;
+
+       if (_cairo_surface_is_snapshot (&source->base))
+           source = (cairo_image_surface_t *) _cairo_surface_snapshot_get_target (&source->base);
+
+       type = source->base.backend->type;
+       if (type == CAIRO_SURFACE_TYPE_IMAGE) {
+           if (extend != CAIRO_EXTEND_NONE &&
+               sample->x >= 0 &&
+               sample->y >= 0 &&
+               sample->x + sample->width  <= source->width &&
+               sample->y + sample->height <= source->height)
+           {
+               extend = CAIRO_EXTEND_NONE;
+           }
+
+           if (sample->width == 1 && sample->height == 1) {
+               if (sample->x < 0 ||
+                   sample->y < 0 ||
+                   sample->x >= source->width ||
+                   sample->y >= source->height)
+               {
+                   if (extend == CAIRO_EXTEND_NONE)
+                       return _pixman_transparent_image ();
+               }
+               else
+               {
+                   pixman_image = _pixel_to_solid (source,
+                                                   sample->x, sample->y);
+                    if (pixman_image)
+                        return pixman_image;
+               }
+           }
+
+#if PIXMAN_HAS_ATOMIC_OPS
+           /* avoid allocating a 'pattern' image if we can reuse the original */
+           *ix = *iy = 0;
+           if (extend == CAIRO_EXTEND_NONE &&
+               _cairo_matrix_is_pixman_translation (&pattern->base.matrix,
+                                                    pattern->base.filter,
+                                                    ix, iy))
+           {
+               return pixman_image_ref (source->pixman_image);
+           }
+#endif
+
+           pixman_image = pixman_image_create_bits (source->pixman_format,
+                                                    source->width,
+                                                    source->height,
+                                                    (uint32_t *) source->data,
+                                                    source->stride);
+           if (unlikely (pixman_image == NULL))
+               return NULL;
+       } else if (type == CAIRO_SURFACE_TYPE_SUBSURFACE) {
+           cairo_surface_subsurface_t *sub;
+           cairo_bool_t is_contained = FALSE;
+
+           sub = (cairo_surface_subsurface_t *) source;
+           source = (cairo_image_surface_t *) sub->target;
+
+           if (sample->x >= 0 &&
+               sample->y >= 0 &&
+               sample->x + sample->width  <= sub->extents.width &&
+               sample->y + sample->height <= sub->extents.height)
+           {
+               is_contained = TRUE;
+           }
+
+           if (sample->width == 1 && sample->height == 1) {
+               if (is_contained) {
+                   pixman_image = _pixel_to_solid (source,
+                                                    sub->extents.x + sample->x,
+                                                    sub->extents.y + sample->y);
+                    if (pixman_image)
+                        return pixman_image;
+               } else {
+                   if (extend == CAIRO_EXTEND_NONE)
+                       return _pixman_transparent_image ();
+               }
+           }
+
+#if PIXMAN_HAS_ATOMIC_OPS
+           *ix = sub->extents.x;
+           *iy = sub->extents.y;
+           if (is_contained &&
+               _cairo_matrix_is_pixman_translation (&pattern->base.matrix,
+                                                    pattern->base.filter,
+                                                    ix, iy))
+           {
+               return pixman_image_ref (source->pixman_image);
+           }
+#endif
+
+           /* Avoid sub-byte offsets, force a copy in that case. */
+           if (PIXMAN_FORMAT_BPP (source->pixman_format) >= 8) {
+               void *data = source->data
+                   + sub->extents.x * PIXMAN_FORMAT_BPP(source->pixman_format)/8
+                   + sub->extents.y * source->stride;
+               pixman_image = pixman_image_create_bits (source->pixman_format,
+                                                        sub->extents.width,
+                                                        sub->extents.height,
+                                                        data,
+                                                        source->stride);
+               if (unlikely (pixman_image == NULL))
+                   return NULL;
+           }
+       }
+    }
+
+#if PIXMAN_HAS_ATOMIC_OPS
+    *ix = *iy = 0;
+#endif
+    if (pixman_image == NULL) {
+       struct acquire_source_cleanup *cleanup;
+       cairo_image_surface_t *image;
+       void *extra;
+       cairo_status_t status;
+
+       status = _cairo_surface_acquire_source_image (pattern->surface, &image, &extra);
+       if (unlikely (status))
+           return NULL;
+
+       if (sample->x >= 0 && sample->y >= 0 &&
+           sample->x + sample->width  <= image->width &&
+           sample->y + sample->height <= image->height)
+       {
+           extend = CAIRO_EXTEND_NONE;
+       }
+
+       if (sample->width == 1 && sample->height == 1) {
+           if (sample->x < 0 ||
+               sample->y < 0 ||
+               sample->x >= image->width ||
+               sample->y >= image->height)
+           {
+               if (extend == CAIRO_EXTEND_NONE) {
+                   pixman_image = _pixman_transparent_image ();
+                   _cairo_surface_release_source_image (pattern->surface, image, extra);
+                   return pixman_image;
+               }
+           }
+           else
+           {
+               pixman_image = _pixel_to_solid (image, sample->x, sample->y);
+                if (pixman_image) {
+                    _cairo_surface_release_source_image (pattern->surface, image, extra);
+                    return pixman_image;
+                }
+           }
+       }
+
+       pixman_image = pixman_image_create_bits (image->pixman_format,
+                                                image->width,
+                                                image->height,
+                                                (uint32_t *) image->data,
+                                                image->stride);
+       if (unlikely (pixman_image == NULL)) {
+           _cairo_surface_release_source_image (pattern->surface, image, extra);
+           return NULL;
+       }
+
+       cleanup = malloc (sizeof (*cleanup));
+       if (unlikely (cleanup == NULL)) {
+           _cairo_surface_release_source_image (pattern->surface, image, extra);
+           pixman_image_unref (pixman_image);
+           return NULL;
+       }
+
+       cleanup->surface = pattern->surface;
+       cleanup->image = image;
+       cleanup->image_extra = extra;
+       pixman_image_set_destroy_function (pixman_image,
+                                          _acquire_source_cleanup, cleanup);
+    }
+
+    if (! _pixman_image_set_properties (pixman_image,
+                                       &pattern->base, extents,
+                                       ix, iy)) {
+       pixman_image_unref (pixman_image);
+       pixman_image= NULL;
+    }
+
+    return pixman_image;
+}
+
+pixman_image_t *
+_pixman_image_for_pattern (cairo_image_surface_t *dst,
+                          const cairo_pattern_t *pattern,
+                          cairo_bool_t is_mask,
+                          const cairo_rectangle_int_t *extents,
+                          const cairo_rectangle_int_t *sample,
+                          int *tx, int *ty)
+{
+    *tx = *ty = 0;
+
+    if (pattern == NULL)
+       return _pixman_white_image ();
+
+    switch (pattern->type) {
+    default:
+       ASSERT_NOT_REACHED;
+    case CAIRO_PATTERN_TYPE_SOLID:
+       return _pixman_image_for_color (&((const cairo_solid_pattern_t *) pattern)->color);
+
+    case CAIRO_PATTERN_TYPE_RADIAL:
+    case CAIRO_PATTERN_TYPE_LINEAR:
+       return _pixman_image_for_gradient ((const cairo_gradient_pattern_t *) pattern,
+                                          extents, tx, ty);
+
+    case CAIRO_PATTERN_TYPE_MESH:
+       return _pixman_image_for_mesh ((const cairo_mesh_pattern_t *) pattern,
+                                          extents, tx, ty);
+
+    case CAIRO_PATTERN_TYPE_SURFACE:
+       return _pixman_image_for_surface (dst,
+                                         (const cairo_surface_pattern_t *) pattern,
+                                         is_mask, extents, sample,
+                                         tx, ty);
+
+    }
+}
+
+static cairo_status_t
+_cairo_image_source_finish (void *abstract_surface)
+{
+    cairo_image_source_t *source = abstract_surface;
+
+    pixman_image_unref (source->pixman_image);
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static const cairo_surface_backend_t cairo_image_source_backend = {
+    CAIRO_SURFACE_TYPE_IMAGE,
+    _cairo_image_source_finish,
+    NULL, /* read-only wrapper */
+};
+
+cairo_surface_t *
+_cairo_image_source_create_for_pattern (cairo_surface_t *dst,
+                                        const cairo_pattern_t *pattern,
+                                        cairo_bool_t is_mask,
+                                        const cairo_rectangle_int_t *extents,
+                                        const cairo_rectangle_int_t *sample,
+                                        int *src_x, int *src_y)
+{
+    cairo_image_source_t *source;
+
+    source = malloc (sizeof (cairo_image_source_t));
+    if (unlikely (source == NULL))
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+    source->pixman_image =
+       _pixman_image_for_pattern ((cairo_image_surface_t *)dst,
+                                  pattern, is_mask,
+                                  extents, sample,
+                                  src_x, src_y);
+    if (unlikely (source->pixman_image == NULL)) {
+       free (source);
+       return _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY);
+    }
+
+    _cairo_surface_init (&source->base,
+                        &cairo_image_source_backend,
+                        NULL, /* device */
+                        CAIRO_CONTENT_COLOR_ALPHA);
+
+    source->is_opaque_solid =
+       pattern == NULL || _cairo_pattern_is_opaque_solid (pattern);
+
+    return &source->base;
+}
diff --git a/src/cairo-image-spans-compositor.c b/src/cairo-image-spans-compositor.c
new file mode 100644 (file)
index 0000000..5718b55
--- /dev/null
@@ -0,0 +1,131 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2003 University of Southern California
+ * Copyright © 2009,2010,2011 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *     Carl D. Worth <cworth@cworth.org>
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-compositor-private.h"
+#include "cairo-image-surface-private.h"
+#include "cairo-spans-compositor-private.h"
+
+typedef struct _cairo_image_span_renderer {
+    cairo_span_renderer_t base;
+
+    pixman_image_compositor_t *compositor;
+    pixman_image_t *src;
+    float opacity;
+    cairo_rectangle_int_t extents;
+} cairo_image_span_renderer_t;
+
+static cairo_status_t
+_cairo_image_span_renderer_init (cairo_abstract_span_renderer_t *_r,
+                                cairo_surface_t *dst,
+                                cairo_operator_t op,
+                                cairo_surface_t *src,
+                                int src_x, int src_y;
+                                float opacity,
+                                const cairo_composite_rectangles_t *composite,
+                                cairo_bool_t needs_clip)
+{
+    cairo_image_span_renderer_t *r = (cairo_image_span_renderer_t *)_r;
+    cairo_pixman_source_t *src = (cairo_pixman_source_t *)_src;
+    int src_x, src_y;
+
+    if (op == CAIRO_OPERATOR_CLEAR) {
+       op = CAIRO_OPERATOR_DEST_OUT;
+       pattern = NULL;
+    }
+
+    r->src = ((cairo_pixman_source_t *) src)->pixman_image;
+    r->opacity = opacity;
+
+    if (composite->is_bounded) {
+       if (opacity == 1.)
+           r->base.render_rows = _cairo_image_bounded_opaque_spans;
+       else
+           r->base.render_rows = _cairo_image_bounded_spans;
+       r->base.finish = NULL;
+    } else {
+       if (needs_clip)
+           r->base.render_rows = _cairo_image_clipped_spans;
+       else
+           r->base.render_rows = _cairo_image_unbounded_spans;
+        r->base.finish =      _cairo_image_finish_unbounded_spans;
+       r->extents = composite->unbounded;
+       r->extents.height += r->extents.y;
+
+    }
+    r->compositor =
+       pixman_image_create_compositor (_pixman_operator (op),
+                                       r->src, NULL, dst->pixman_image,
+                                       composite->bounded.x + src_x,
+                                       composite->bounded.y + src_y,
+                                       0, 0,
+                                       composite->bounded.x,
+                                       composite->bounded.y,
+                                       composite->bounded.width,
+                                       composite->bounded.height);
+    if (unlikely (r->compositor == NULL))
+       return CAIRO_INT_STATUS_NOTHING_TO_DO;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_image_span_renderer_fini (cairo_abstract_span_renderer_t *_r)
+{
+    cairo_image_span_renderer_t *r = (cairo_image_span_renderer_t *) r;
+    pixman_image_compositor_destroy (r->compositor);
+}
+
+const cairo_compositor_t *
+_cairo_image_spans_compositor_get (void)
+{
+    static cairo_spans_compositor_t compositor;
+
+    if (compositor.base.delegate == NULL) {
+       /* Can't fallback to the mask compositor as that will simply
+        * call the spans-compositor again to render the mask!
+        */
+       _cairo_spans_compositor_init (&compositor,
+                                     _cairo_image_traps_compositor_get());
+
+    }
+
+    return &compositor.base;
+}
diff --git a/src/cairo-image-surface-private.h b/src/cairo-image-surface-private.h
new file mode 100644 (file)
index 0000000..227a447
--- /dev/null
@@ -0,0 +1,168 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *     Carl D. Worth <cworth@cworth.org>
+ */
+
+#ifndef CAIRO_IMAGE_SURFACE_PRIVATE_H
+#define CAIRO_IMAGE_SURFACE_PRIVATE_H
+
+#include "cairo-surface-private.h"
+
+#include <pixman.h>
+
+CAIRO_BEGIN_DECLS
+
+/* The canonical image backend */
+struct _cairo_image_surface {
+    cairo_surface_t base;
+
+    pixman_image_t *pixman_image;
+    const cairo_compositor_t *compositor;
+
+    pixman_format_code_t pixman_format;
+    cairo_format_t format;
+    unsigned char *data;
+
+    int width;
+    int height;
+    int stride;
+    int depth;
+
+    unsigned owns_data : 1;
+    unsigned transparency : 2;
+    unsigned color : 2;
+};
+
+/* A wrapper for holding pixman images returned by create_for_pattern */
+typedef struct _cairo_image_source {
+    cairo_surface_t base;
+
+    pixman_image_t *pixman_image;
+    unsigned is_opaque_solid : 1;
+} cairo_image_source_t;
+
+cairo_private extern const cairo_surface_backend_t _cairo_image_surface_backend;
+
+cairo_private const cairo_compositor_t *
+_cairo_image_mask_compositor_get (void);
+
+cairo_private const cairo_compositor_t *
+_cairo_image_traps_compositor_get (void);
+
+cairo_private const cairo_compositor_t *
+_cairo_image_spans_compositor_get (void);
+
+cairo_private void
+_cairo_image_surface_init (cairo_image_surface_t *surface,
+                          pixman_image_t       *pixman_image,
+                          pixman_format_code_t  pixman_format);
+
+cairo_private cairo_surface_t *
+_cairo_image_surface_map_to_image (void *abstract_other,
+                                  const cairo_rectangle_int_t *extents);
+
+cairo_private cairo_int_status_t
+_cairo_image_surface_unmap_image (void *abstract_surface,
+                                 cairo_image_surface_t *image);
+cairo_private cairo_status_t
+_cairo_image_surface_acquire_source_image (void                    *abstract_surface,
+                                          cairo_image_surface_t  **image_out,
+                                          void                   **image_extra);
+
+cairo_private void
+_cairo_image_surface_release_source_image (void                   *abstract_surface,
+                                          cairo_image_surface_t  *image,
+                                          void                   *image_extra);
+
+cairo_private cairo_surface_t *
+_cairo_image_surface_snapshot (void *abstract_surface);
+
+cairo_private_no_warn cairo_bool_t
+_cairo_image_surface_get_extents (void                   *abstract_surface,
+                                 cairo_rectangle_int_t   *rectangle);
+
+cairo_private void
+_cairo_image_surface_get_font_options (void                  *abstract_surface,
+                                      cairo_font_options_t  *options);
+
+cairo_private cairo_surface_t *
+_cairo_image_source_create_for_pattern (cairo_surface_t *dst,
+                                       const cairo_pattern_t *pattern,
+                                       cairo_bool_t is_mask,
+                                       const cairo_rectangle_int_t *extents,
+                                       const cairo_rectangle_int_t *sample,
+                                       int *src_x, int *src_y);
+
+cairo_private cairo_status_t
+_cairo_image_surface_finish (void *abstract_surface);
+
+cairo_private pixman_image_t *
+_pixman_image_for_color (const cairo_color_t *cairo_color);
+
+cairo_private pixman_image_t *
+_pixman_image_for_pattern (cairo_image_surface_t *dst,
+                          const cairo_pattern_t *pattern,
+                          cairo_bool_t is_mask,
+                          const cairo_rectangle_int_t *extents,
+                          const cairo_rectangle_int_t *sample,
+                          int *tx, int *ty);
+
+cairo_private void
+_pixman_image_add_traps (pixman_image_t *image,
+                        int dst_x, int dst_y,
+                        cairo_traps_t *traps);
+
+cairo_private void
+_pixman_image_add_tristrip (pixman_image_t *image,
+                           int dst_x, int dst_y,
+                           cairo_tristrip_t *strip);
+
+/**
+ * _cairo_surface_is_image:
+ * @surface: a #cairo_surface_t
+ *
+ * Checks if a surface is an #cairo_image_surface_t
+ *
+ * Return value: %TRUE if the surface is an image surface
+ **/
+static inline cairo_bool_t
+_cairo_surface_is_image (const cairo_surface_t *surface)
+{
+    return surface->backend == &_cairo_image_surface_backend;
+}
+
+CAIRO_END_DECLS
+
+#endif /* CAIRO_IMAGE_SURFACE_PRIVATE_H */
diff --git a/src/cairo-image-surface.c b/src/cairo-image-surface.c
new file mode 100644 (file)
index 0000000..6adbdd6
--- /dev/null
@@ -0,0 +1,1131 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2003 University of Southern California
+ * Copyright © 2009,2010,2011 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *     Carl D. Worth <cworth@cworth.org>
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-boxes-private.h"
+#include "cairo-clip-private.h"
+#include "cairo-composite-rectangles-private.h"
+#include "cairo-compositor-private.h"
+#include "cairo-default-context-private.h"
+#include "cairo-error-private.h"
+#include "cairo-image-surface-private.h"
+#include "cairo-paginated-private.h"
+#include "cairo-pattern-private.h"
+#include "cairo-recording-surface-private.h"
+#include "cairo-region-private.h"
+#include "cairo-scaled-font-private.h"
+#include "cairo-surface-snapshot-private.h"
+#include "cairo-surface-subsurface-private.h"
+
+/* Limit on the width / height of an image surface in pixels.  This is
+ * mainly determined by coordinates of things sent to pixman at the
+ * moment being in 16.16 format. */
+#define MAX_IMAGE_SIZE 32767
+
+/**
+ * SECTION:cairo-image
+ * @Title: Image Surfaces
+ * @Short_Description: Rendering to memory buffers
+ * @See_Also: #cairo_surface_t
+ *
+ * Image surfaces provide the ability to render to memory buffers
+ * either allocated by cairo or by the calling code.  The supported
+ * image formats are those defined in #cairo_format_t.
+ */
+
+/**
+ * CAIRO_HAS_IMAGE_SURFACE:
+ *
+ * Defined if the image surface backend is available.
+ * The image surface backend is always built in.
+ * This macro was added for completeness in cairo 1.8.
+ *
+ * @Since: 1.8
+ */
+
+static cairo_bool_t
+_cairo_image_surface_is_size_valid (int width, int height)
+{
+    return 0 <= width  &&  width <= MAX_IMAGE_SIZE &&
+          0 <= height && height <= MAX_IMAGE_SIZE;
+}
+
+cairo_format_t
+_cairo_format_from_pixman_format (pixman_format_code_t pixman_format)
+{
+    switch (pixman_format) {
+    case PIXMAN_a8r8g8b8:
+       return CAIRO_FORMAT_ARGB32;
+    case PIXMAN_x2r10g10b10:
+       return CAIRO_FORMAT_RGB30;
+    case PIXMAN_x8r8g8b8:
+       return CAIRO_FORMAT_RGB24;
+    case PIXMAN_a8:
+       return CAIRO_FORMAT_A8;
+    case PIXMAN_a1:
+       return CAIRO_FORMAT_A1;
+    case PIXMAN_r5g6b5:
+       return CAIRO_FORMAT_RGB16_565;
+    case PIXMAN_r8g8b8a8: case PIXMAN_r8g8b8x8:
+    case PIXMAN_a8b8g8r8: case PIXMAN_x8b8g8r8: case PIXMAN_r8g8b8:
+    case PIXMAN_b8g8r8:   case PIXMAN_b5g6r5:
+    case PIXMAN_a1r5g5b5: case PIXMAN_x1r5g5b5: case PIXMAN_a1b5g5r5:
+    case PIXMAN_x1b5g5r5: case PIXMAN_a4r4g4b4: case PIXMAN_x4r4g4b4:
+    case PIXMAN_a4b4g4r4: case PIXMAN_x4b4g4r4: case PIXMAN_r3g3b2:
+    case PIXMAN_b2g3r3:   case PIXMAN_a2r2g2b2: case PIXMAN_a2b2g2r2:
+    case PIXMAN_c8:       case PIXMAN_g8:       case PIXMAN_x4a4:
+    case PIXMAN_a4:       case PIXMAN_r1g2b1:   case PIXMAN_b1g2r1:
+    case PIXMAN_a1r1g1b1: case PIXMAN_a1b1g1r1: case PIXMAN_c4:
+    case PIXMAN_g4:       case PIXMAN_g1:
+    case PIXMAN_yuy2:     case PIXMAN_yv12:
+    case PIXMAN_b8g8r8x8:
+    case PIXMAN_b8g8r8a8:
+    case PIXMAN_a2b10g10r10:
+    case PIXMAN_x2b10g10r10:
+    case PIXMAN_a2r10g10b10:
+    case PIXMAN_x14r6g6b6:
+    default:
+       return CAIRO_FORMAT_INVALID;
+    }
+
+    return CAIRO_FORMAT_INVALID;
+}
+
+cairo_content_t
+_cairo_content_from_pixman_format (pixman_format_code_t pixman_format)
+{
+    cairo_content_t content;
+
+    content = 0;
+    if (PIXMAN_FORMAT_RGB (pixman_format))
+       content |= CAIRO_CONTENT_COLOR;
+    if (PIXMAN_FORMAT_A (pixman_format))
+       content |= CAIRO_CONTENT_ALPHA;
+
+    return content;
+}
+
+void
+_cairo_image_surface_init (cairo_image_surface_t *surface,
+                          pixman_image_t       *pixman_image,
+                          pixman_format_code_t  pixman_format)
+{
+    surface->pixman_image = pixman_image;
+
+    surface->pixman_format = pixman_format;
+    surface->format = _cairo_format_from_pixman_format (pixman_format);
+    surface->data = (uint8_t *) pixman_image_get_data (pixman_image);
+    surface->owns_data = FALSE;
+    surface->transparency = CAIRO_IMAGE_UNKNOWN;
+    surface->color = CAIRO_IMAGE_UNKNOWN_COLOR;
+
+    surface->width = pixman_image_get_width (pixman_image);
+    surface->height = pixman_image_get_height (pixman_image);
+    surface->stride = pixman_image_get_stride (pixman_image);
+    surface->depth = pixman_image_get_depth (pixman_image);
+
+    surface->base.is_clear = surface->width == 0 || surface->height == 0;
+
+    surface->compositor = _cairo_image_spans_compositor_get ();
+}
+
+cairo_surface_t *
+_cairo_image_surface_create_for_pixman_image (pixman_image_t           *pixman_image,
+                                             pixman_format_code_t       pixman_format)
+{
+    cairo_image_surface_t *surface;
+
+    surface = malloc (sizeof (cairo_image_surface_t));
+    if (unlikely (surface == NULL))
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+    _cairo_surface_init (&surface->base,
+                        &_cairo_image_surface_backend,
+                        NULL, /* device */
+                        _cairo_content_from_pixman_format (pixman_format));
+
+    _cairo_image_surface_init (surface, pixman_image, pixman_format);
+
+    return &surface->base;
+}
+
+cairo_bool_t
+_pixman_format_from_masks (cairo_format_masks_t *masks,
+                          pixman_format_code_t *format_ret)
+{
+    pixman_format_code_t format;
+    int format_type;
+    int a, r, g, b;
+    cairo_format_masks_t format_masks;
+
+    a = _cairo_popcount (masks->alpha_mask);
+    r = _cairo_popcount (masks->red_mask);
+    g = _cairo_popcount (masks->green_mask);
+    b = _cairo_popcount (masks->blue_mask);
+
+    if (masks->red_mask) {
+       if (masks->red_mask > masks->blue_mask)
+           format_type = PIXMAN_TYPE_ARGB;
+       else
+           format_type = PIXMAN_TYPE_ABGR;
+    } else if (masks->alpha_mask) {
+       format_type = PIXMAN_TYPE_A;
+    } else {
+       return FALSE;
+    }
+
+    format = PIXMAN_FORMAT (masks->bpp, format_type, a, r, g, b);
+
+    if (! pixman_format_supported_destination (format))
+       return FALSE;
+
+    /* Sanity check that we got out of PIXMAN_FORMAT exactly what we
+     * expected. This avoid any problems from something bizarre like
+     * alpha in the least-significant bits, or insane channel order,
+     * or whatever. */
+     if (!_pixman_format_to_masks (format, &format_masks) ||
+         masks->bpp        != format_masks.bpp            ||
+        masks->red_mask   != format_masks.red_mask       ||
+        masks->green_mask != format_masks.green_mask     ||
+        masks->blue_mask  != format_masks.blue_mask)
+     {
+        return FALSE;
+     }
+
+    *format_ret = format;
+    return TRUE;
+}
+
+/* A mask consisting of N bits set to 1. */
+#define MASK(N) ((1UL << (N))-1)
+
+cairo_bool_t
+_pixman_format_to_masks (pixman_format_code_t   format,
+                        cairo_format_masks_t   *masks)
+{
+    int a, r, g, b;
+
+    masks->bpp = PIXMAN_FORMAT_BPP (format);
+
+    /* Number of bits in each channel */
+    a = PIXMAN_FORMAT_A (format);
+    r = PIXMAN_FORMAT_R (format);
+    g = PIXMAN_FORMAT_G (format);
+    b = PIXMAN_FORMAT_B (format);
+
+    switch (PIXMAN_FORMAT_TYPE (format)) {
+    case PIXMAN_TYPE_ARGB:
+        masks->alpha_mask = MASK (a) << (r + g + b);
+        masks->red_mask   = MASK (r) << (g + b);
+        masks->green_mask = MASK (g) << (b);
+        masks->blue_mask  = MASK (b);
+        return TRUE;
+    case PIXMAN_TYPE_ABGR:
+        masks->alpha_mask = MASK (a) << (b + g + r);
+        masks->blue_mask  = MASK (b) << (g + r);
+        masks->green_mask = MASK (g) << (r);
+        masks->red_mask   = MASK (r);
+        return TRUE;
+#ifdef PIXMAN_TYPE_BGRA
+    case PIXMAN_TYPE_BGRA:
+        masks->blue_mask  = MASK (b) << (masks->bpp - b);
+        masks->green_mask = MASK (g) << (masks->bpp - b - g);
+        masks->red_mask   = MASK (r) << (masks->bpp - b - g - r);
+        masks->alpha_mask = MASK (a);
+        return TRUE;
+#endif
+    case PIXMAN_TYPE_A:
+        masks->alpha_mask = MASK (a);
+        masks->red_mask   = 0;
+        masks->green_mask = 0;
+        masks->blue_mask  = 0;
+        return TRUE;
+    case PIXMAN_TYPE_OTHER:
+    case PIXMAN_TYPE_COLOR:
+    case PIXMAN_TYPE_GRAY:
+    case PIXMAN_TYPE_YUY2:
+    case PIXMAN_TYPE_YV12:
+    default:
+        masks->alpha_mask = 0;
+        masks->red_mask   = 0;
+        masks->green_mask = 0;
+        masks->blue_mask  = 0;
+        return FALSE;
+    }
+}
+
+pixman_format_code_t
+_cairo_format_to_pixman_format_code (cairo_format_t format)
+{
+    pixman_format_code_t ret;
+    switch (format) {
+    case CAIRO_FORMAT_A1:
+       ret = PIXMAN_a1;
+       break;
+    case CAIRO_FORMAT_A8:
+       ret = PIXMAN_a8;
+       break;
+    case CAIRO_FORMAT_RGB24:
+       ret = PIXMAN_x8r8g8b8;
+       break;
+    case CAIRO_FORMAT_RGB30:
+       ret = PIXMAN_x2r10g10b10;
+       break;
+    case CAIRO_FORMAT_RGB16_565:
+       ret = PIXMAN_r5g6b5;
+       break;
+    case CAIRO_FORMAT_ARGB32:
+    case CAIRO_FORMAT_INVALID:
+    default:
+       ret = PIXMAN_a8r8g8b8;
+       break;
+    }
+    return ret;
+}
+
+cairo_surface_t *
+_cairo_image_surface_create_with_pixman_format (unsigned char          *data,
+                                               pixman_format_code_t     pixman_format,
+                                               int                      width,
+                                               int                      height,
+                                               int                      stride)
+{
+    cairo_surface_t *surface;
+    pixman_image_t *pixman_image;
+
+    if (! _cairo_image_surface_is_size_valid (width, height))
+    {
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE));
+    }
+
+    pixman_image = pixman_image_create_bits (pixman_format, width, height,
+                                            (uint32_t *) data, stride);
+
+    if (unlikely (pixman_image == NULL))
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+    surface = _cairo_image_surface_create_for_pixman_image (pixman_image,
+                                                           pixman_format);
+    if (unlikely (surface->status)) {
+       pixman_image_unref (pixman_image);
+       return surface;
+    }
+
+    /* we can not make any assumptions about the initial state of user data */
+    surface->is_clear = data == NULL;
+    return surface;
+}
+
+/**
+ * cairo_image_surface_create:
+ * @format: format of pixels in the surface to create
+ * @width: width of the surface, in pixels
+ * @height: height of the surface, in pixels
+ *
+ * Creates an image surface of the specified format and
+ * dimensions. Initially the surface contents are all
+ * 0. (Specifically, within each pixel, each color or alpha channel
+ * belonging to format will be 0. The contents of bits within a pixel,
+ * but not belonging to the given format are undefined).
+ *
+ * Return value: a pointer to the newly created surface. The caller
+ * owns the surface and should call cairo_surface_destroy() when done
+ * with it.
+ *
+ * This function always returns a valid pointer, but it will return a
+ * pointer to a "nil" surface if an error such as out of memory
+ * occurs. You can use cairo_surface_status() to check for this.
+ **/
+cairo_surface_t *
+cairo_image_surface_create (cairo_format_t     format,
+                           int                 width,
+                           int                 height)
+{
+    pixman_format_code_t pixman_format;
+
+    if (! CAIRO_FORMAT_VALID (format))
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
+
+    pixman_format = _cairo_format_to_pixman_format_code (format);
+
+    return _cairo_image_surface_create_with_pixman_format (NULL, pixman_format,
+                                                          width, height, -1);
+}
+slim_hidden_def (cairo_image_surface_create);
+
+    cairo_surface_t *
+_cairo_image_surface_create_with_content (cairo_content_t      content,
+                                         int                   width,
+                                         int                   height)
+{
+    return cairo_image_surface_create (_cairo_format_from_content (content),
+                                      width, height);
+}
+
+/**
+ * cairo_format_stride_for_width:
+ * @format: A #cairo_format_t value
+ * @width: The desired width of an image surface to be created.
+ *
+ * This function provides a stride value that will respect all
+ * alignment requirements of the accelerated image-rendering code
+ * within cairo. Typical usage will be of the form:
+ *
+ * <informalexample><programlisting>
+ * int stride;
+ * unsigned char *data;
+ * #cairo_surface_t *surface;
+ *
+ * stride = cairo_format_stride_for_width (format, width);
+ * data = malloc (stride * height);
+ * surface = cairo_image_surface_create_for_data (data, format,
+ *                                               width, height,
+ *                                               stride);
+ * </programlisting></informalexample>
+ *
+ * Return value: the appropriate stride to use given the desired
+ * format and width, or -1 if either the format is invalid or the width
+ * too large.
+ *
+ * Since: 1.6
+ **/
+    int
+cairo_format_stride_for_width (cairo_format_t  format,
+                              int              width)
+{
+    int bpp;
+
+    if (! CAIRO_FORMAT_VALID (format)) {
+       _cairo_error_throw (CAIRO_STATUS_INVALID_FORMAT);
+       return -1;
+    }
+
+    bpp = _cairo_format_bits_per_pixel (format);
+    if ((unsigned) (width) >= (INT32_MAX - 7) / (unsigned) (bpp))
+       return -1;
+
+    return CAIRO_STRIDE_FOR_WIDTH_BPP (width, bpp);
+}
+slim_hidden_def (cairo_format_stride_for_width);
+
+/**
+ * cairo_image_surface_create_for_data:
+ * @data: a pointer to a buffer supplied by the application in which
+ *     to write contents. This pointer must be suitably aligned for any
+ *     kind of variable, (for example, a pointer returned by malloc).
+ * @format: the format of pixels in the buffer
+ * @width: the width of the image to be stored in the buffer
+ * @height: the height of the image to be stored in the buffer
+ * @stride: the number of bytes between the start of rows in the
+ *     buffer as allocated. This value should always be computed by
+ *     cairo_format_stride_for_width() before allocating the data
+ *     buffer.
+ *
+ * Creates an image surface for the provided pixel data. The output
+ * buffer must be kept around until the #cairo_surface_t is destroyed
+ * or cairo_surface_finish() is called on the surface.  The initial
+ * contents of @data will be used as the initial image contents; you
+ * must explicitly clear the buffer, using, for example,
+ * cairo_rectangle() and cairo_fill() if you want it cleared.
+ *
+ * Note that the stride may be larger than
+ * width*bytes_per_pixel to provide proper alignment for each pixel
+ * and row. This alignment is required to allow high-performance rendering
+ * within cairo. The correct way to obtain a legal stride value is to
+ * call cairo_format_stride_for_width() with the desired format and
+ * maximum image width value, and then use the resulting stride value
+ * to allocate the data and to create the image surface. See
+ * cairo_format_stride_for_width() for example code.
+ *
+ * Return value: a pointer to the newly created surface. The caller
+ * owns the surface and should call cairo_surface_destroy() when done
+ * with it.
+ *
+ * This function always returns a valid pointer, but it will return a
+ * pointer to a "nil" surface in the case of an error such as out of
+ * memory or an invalid stride value. In case of invalid stride value
+ * the error status of the returned surface will be
+ * %CAIRO_STATUS_INVALID_STRIDE.  You can use
+ * cairo_surface_status() to check for this.
+ *
+ * See cairo_surface_set_user_data() for a means of attaching a
+ * destroy-notification fallback to the surface if necessary.
+ **/
+    cairo_surface_t *
+cairo_image_surface_create_for_data (unsigned char     *data,
+                                    cairo_format_t     format,
+                                    int                width,
+                                    int                height,
+                                    int                stride)
+{
+    pixman_format_code_t pixman_format;
+    int minstride;
+
+    if (! CAIRO_FORMAT_VALID (format))
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
+
+    if ((stride & (CAIRO_STRIDE_ALIGNMENT-1)) != 0)
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_STRIDE));
+
+    if (! _cairo_image_surface_is_size_valid (width, height))
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE));
+
+    minstride = cairo_format_stride_for_width (format, width);
+    if (stride < 0) {
+       if (stride > -minstride) {
+           return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_STRIDE));
+       }
+    } else {
+       if (stride < minstride) {
+           return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_STRIDE));
+       }
+    }
+
+    pixman_format = _cairo_format_to_pixman_format_code (format);
+    return _cairo_image_surface_create_with_pixman_format (data,
+                                                          pixman_format,
+                                                          width, height,
+                                                          stride);
+}
+slim_hidden_def (cairo_image_surface_create_for_data);
+
+/**
+ * cairo_image_surface_get_data:
+ * @surface: a #cairo_image_surface_t
+ *
+ * Get a pointer to the data of the image surface, for direct
+ * inspection or modification.
+ *
+ * A call to cairo_surface_flush() is required before accessing the
+ * pixel data to ensure that all pending drawing operations are
+ * finished. A call to cairo_surface_mark_dirty() is required after
+ * the data is modified.
+ *
+ * Return value: a pointer to the image data of this surface or %NULL
+ * if @surface is not an image surface, or if cairo_surface_finish()
+ * has been called.
+ *
+ * Since: 1.2
+ **/
+    unsigned char *
+cairo_image_surface_get_data (cairo_surface_t *surface)
+{
+    cairo_image_surface_t *image_surface = (cairo_image_surface_t *) surface;
+
+    if (! _cairo_surface_is_image (surface)) {
+       _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+       return NULL;
+    }
+
+    return image_surface->data;
+}
+slim_hidden_def (cairo_image_surface_get_data);
+
+/**
+ * cairo_image_surface_get_format:
+ * @surface: a #cairo_image_surface_t
+ *
+ * Get the format of the surface.
+ *
+ * Return value: the format of the surface
+ *
+ * Since: 1.2
+ **/
+    cairo_format_t
+cairo_image_surface_get_format (cairo_surface_t *surface)
+{
+    cairo_image_surface_t *image_surface = (cairo_image_surface_t *) surface;
+
+    if (! _cairo_surface_is_image (surface)) {
+       _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+       return CAIRO_FORMAT_INVALID;
+    }
+
+    return image_surface->format;
+}
+slim_hidden_def (cairo_image_surface_get_format);
+
+/**
+ * cairo_image_surface_get_width:
+ * @surface: a #cairo_image_surface_t
+ *
+ * Get the width of the image surface in pixels.
+ *
+ * Return value: the width of the surface in pixels.
+ **/
+    int
+cairo_image_surface_get_width (cairo_surface_t *surface)
+{
+    cairo_image_surface_t *image_surface = (cairo_image_surface_t *) surface;
+
+    if (! _cairo_surface_is_image (surface)) {
+       _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+       return 0;
+    }
+
+    return image_surface->width;
+}
+slim_hidden_def (cairo_image_surface_get_width);
+
+/**
+ * cairo_image_surface_get_height:
+ * @surface: a #cairo_image_surface_t
+ *
+ * Get the height of the image surface in pixels.
+ *
+ * Return value: the height of the surface in pixels.
+ **/
+    int
+cairo_image_surface_get_height (cairo_surface_t *surface)
+{
+    cairo_image_surface_t *image_surface = (cairo_image_surface_t *) surface;
+
+    if (! _cairo_surface_is_image (surface)) {
+       _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+       return 0;
+    }
+
+    return image_surface->height;
+}
+slim_hidden_def (cairo_image_surface_get_height);
+
+/**
+ * cairo_image_surface_get_stride:
+ * @surface: a #cairo_image_surface_t
+ *
+ * Get the stride of the image surface in bytes
+ *
+ * Return value: the stride of the image surface in bytes (or 0 if
+ * @surface is not an image surface). The stride is the distance in
+ * bytes from the beginning of one row of the image data to the
+ * beginning of the next row.
+ *
+ * Since: 1.2
+ **/
+    int
+cairo_image_surface_get_stride (cairo_surface_t *surface)
+{
+
+    cairo_image_surface_t *image_surface = (cairo_image_surface_t *) surface;
+
+    if (! _cairo_surface_is_image (surface)) {
+       _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+       return 0;
+    }
+
+    return image_surface->stride;
+}
+slim_hidden_def (cairo_image_surface_get_stride);
+
+    cairo_format_t
+_cairo_format_from_content (cairo_content_t content)
+{
+    switch (content) {
+    case CAIRO_CONTENT_COLOR:
+       return CAIRO_FORMAT_RGB24;
+    case CAIRO_CONTENT_ALPHA:
+       return CAIRO_FORMAT_A8;
+    case CAIRO_CONTENT_COLOR_ALPHA:
+       return CAIRO_FORMAT_ARGB32;
+    }
+
+    ASSERT_NOT_REACHED;
+    return CAIRO_FORMAT_INVALID;
+}
+
+    cairo_content_t
+_cairo_content_from_format (cairo_format_t format)
+{
+    switch (format) {
+    case CAIRO_FORMAT_ARGB32:
+       return CAIRO_CONTENT_COLOR_ALPHA;
+    case CAIRO_FORMAT_RGB30:
+       return CAIRO_CONTENT_COLOR;
+    case CAIRO_FORMAT_RGB24:
+       return CAIRO_CONTENT_COLOR;
+    case CAIRO_FORMAT_RGB16_565:
+       return CAIRO_CONTENT_COLOR;
+    case CAIRO_FORMAT_A8:
+    case CAIRO_FORMAT_A1:
+       return CAIRO_CONTENT_ALPHA;
+    case CAIRO_FORMAT_INVALID:
+       break;
+    }
+
+    ASSERT_NOT_REACHED;
+    return CAIRO_CONTENT_COLOR_ALPHA;
+}
+
+    int
+_cairo_format_bits_per_pixel (cairo_format_t format)
+{
+    switch (format) {
+    case CAIRO_FORMAT_ARGB32:
+    case CAIRO_FORMAT_RGB30:
+    case CAIRO_FORMAT_RGB24:
+       return 32;
+    case CAIRO_FORMAT_RGB16_565:
+       return 16;
+    case CAIRO_FORMAT_A8:
+       return 8;
+    case CAIRO_FORMAT_A1:
+       return 1;
+    case CAIRO_FORMAT_INVALID:
+    default:
+       ASSERT_NOT_REACHED;
+       return 0;
+    }
+}
+
+    static cairo_surface_t *
+_cairo_image_surface_create_similar (void             *abstract_other,
+                                    cairo_content_t    content,
+                                    int                width,
+                                    int                height)
+{
+    cairo_image_surface_t *other = abstract_other;
+
+    if (! _cairo_image_surface_is_size_valid (width, height))
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE));
+
+    if (content == other->base.content) {
+       return _cairo_image_surface_create_with_pixman_format (NULL,
+                                                              other->pixman_format,
+                                                              width, height,
+                                                              0);
+    }
+
+    return _cairo_image_surface_create_with_content (content,
+                                                    width, height);
+}
+
+cairo_surface_t *
+_cairo_image_surface_snapshot (void *abstract_surface)
+{
+    cairo_image_surface_t *image = abstract_surface;
+    cairo_image_surface_t *clone;
+
+    clone = (cairo_image_surface_t *)
+       _cairo_image_surface_create_with_pixman_format (NULL,
+                                                       image->pixman_format,
+                                                       image->width,
+                                                       image->height,
+                                                       0);
+    if (unlikely (clone->base.status))
+       return &clone->base;
+
+    if (clone->stride == image->stride) {
+       memcpy (clone->data, image->data, clone->stride * clone->height);
+    } else {
+       pixman_image_composite32 (PIXMAN_OP_SRC,
+                                 image->pixman_image, NULL, clone->pixman_image,
+                                 0, 0,
+                                 0, 0,
+                                 0, 0,
+                                 image->width, image->height);
+    }
+    clone->base.is_clear = FALSE;
+    return &clone->base;
+}
+
+cairo_surface_t *
+_cairo_image_surface_map_to_image (void *abstract_other,
+                                  const cairo_rectangle_int_t *extents)
+{
+    cairo_image_surface_t *other = abstract_other;
+    cairo_surface_t *surface;
+    uint8_t *data;
+
+    data = other->data;
+    data += extents->y * other->stride;
+    data += extents->x * PIXMAN_FORMAT_BPP (other->pixman_format)/ 8;
+
+    surface =
+       _cairo_image_surface_create_with_pixman_format (data,
+                                                       other->pixman_format,
+                                                       extents->width,
+                                                       extents->height,
+                                                       other->stride);
+
+    cairo_surface_set_device_offset (surface, -extents->x, -extents->y);
+    return surface;
+}
+
+cairo_int_status_t
+_cairo_image_surface_unmap_image (void *abstract_surface,
+                                 cairo_image_surface_t *image)
+{
+    return CAIRO_INT_STATUS_SUCCESS;
+}
+
+cairo_status_t
+_cairo_image_surface_finish (void *abstract_surface)
+{
+    cairo_image_surface_t *surface = abstract_surface;
+
+    if (surface->pixman_image) {
+       pixman_image_unref (surface->pixman_image);
+       surface->pixman_image = NULL;
+    }
+
+    if (surface->owns_data) {
+       free (surface->data);
+       surface->data = NULL;
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+void
+_cairo_image_surface_assume_ownership_of_data (cairo_image_surface_t *surface)
+{
+    surface->owns_data = TRUE;
+}
+
+cairo_status_t
+_cairo_image_surface_acquire_source_image (void                    *abstract_surface,
+                                          cairo_image_surface_t  **image_out,
+                                          void                   **image_extra)
+{
+    *image_out = abstract_surface;
+    *image_extra = NULL;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+void
+_cairo_image_surface_release_source_image (void                   *abstract_surface,
+                                          cairo_image_surface_t  *image,
+                                          void                   *image_extra)
+{
+}
+
+/* high level image interface */
+cairo_bool_t
+_cairo_image_surface_get_extents (void                   *abstract_surface,
+                                 cairo_rectangle_int_t   *rectangle)
+{
+    cairo_image_surface_t *surface = abstract_surface;
+
+    rectangle->x = 0;
+    rectangle->y = 0;
+    rectangle->width  = surface->width;
+    rectangle->height = surface->height;
+
+    return TRUE;
+}
+
+static cairo_int_status_t
+_cairo_image_surface_paint (void                       *abstract_surface,
+                           cairo_operator_t             op,
+                           const cairo_pattern_t       *source,
+                           const cairo_clip_t          *clip)
+{
+    cairo_image_surface_t *surface = abstract_surface;
+    return _cairo_compositor_paint (surface->compositor,
+                                   &surface->base, op, source, clip);
+}
+
+static cairo_int_status_t
+_cairo_image_surface_mask (void                                *abstract_surface,
+                          cairo_operator_t              op,
+                          const cairo_pattern_t        *source,
+                          const cairo_pattern_t        *mask,
+                          const cairo_clip_t           *clip)
+{
+    cairo_image_surface_t *surface = abstract_surface;
+    return _cairo_compositor_mask (surface->compositor,
+                                  &surface->base, op, source, mask, clip);
+}
+
+static cairo_int_status_t
+_cairo_image_surface_stroke (void                      *abstract_surface,
+                            cairo_operator_t            op,
+                            const cairo_pattern_t      *source,
+                            const cairo_path_fixed_t   *path,
+                            const cairo_stroke_style_t *style,
+                            const cairo_matrix_t       *ctm,
+                            const cairo_matrix_t       *ctm_inverse,
+                            double                      tolerance,
+                            cairo_antialias_t           antialias,
+                            const cairo_clip_t         *clip)
+{
+    cairo_image_surface_t *surface = abstract_surface;
+    return _cairo_compositor_stroke (surface->compositor, &surface->base,
+                                    op, source, path,
+                                    style, ctm, ctm_inverse,
+                                    tolerance, antialias, clip);
+}
+
+static cairo_int_status_t
+_cairo_image_surface_fill (void                                *abstract_surface,
+                          cairo_operator_t              op,
+                          const cairo_pattern_t        *source,
+                          const cairo_path_fixed_t     *path,
+                          cairo_fill_rule_t             fill_rule,
+                          double                        tolerance,
+                          cairo_antialias_t             antialias,
+                          const cairo_clip_t           *clip)
+{
+    cairo_image_surface_t *surface = abstract_surface;
+    return _cairo_compositor_fill (surface->compositor, &surface->base,
+                                  op, source, path,
+                                  fill_rule, tolerance, antialias,
+                                  clip);
+}
+
+static cairo_int_status_t
+_cairo_image_surface_glyphs (void                      *abstract_surface,
+                            cairo_operator_t            op,
+                            const cairo_pattern_t      *source,
+                            cairo_glyph_t              *glyphs,
+                            int                         num_glyphs,
+                            cairo_scaled_font_t        *scaled_font,
+                            const cairo_clip_t         *clip)
+{
+    cairo_image_surface_t *surface = abstract_surface;
+    return _cairo_compositor_glyphs (surface->compositor, &surface->base,
+                                    op, source,
+                                    glyphs, num_glyphs, scaled_font,
+                                    clip);
+}
+
+void
+_cairo_image_surface_get_font_options (void                  *abstract_surface,
+                                      cairo_font_options_t  *options)
+{
+    _cairo_font_options_init_default (options);
+
+    cairo_font_options_set_hint_metrics (options, CAIRO_HINT_METRICS_ON);
+    _cairo_font_options_set_round_glyph_positions (options, CAIRO_ROUND_GLYPH_POS_ON);
+}
+
+const cairo_surface_backend_t _cairo_image_surface_backend = {
+    CAIRO_SURFACE_TYPE_IMAGE,
+    _cairo_image_surface_finish,
+
+    _cairo_default_context_create,
+
+    _cairo_image_surface_create_similar,
+    NULL, /* create similar image */
+    _cairo_image_surface_map_to_image,
+    _cairo_image_surface_unmap_image,
+
+    _cairo_image_surface_acquire_source_image,
+    _cairo_image_surface_release_source_image,
+    _cairo_image_surface_snapshot,
+
+    NULL, /* copy_page */
+    NULL, /* show_page */
+
+    _cairo_image_surface_get_extents,
+    _cairo_image_surface_get_font_options,
+
+    NULL, /* flush */
+    NULL, /* mark dirty */
+
+    _cairo_image_surface_paint,
+    _cairo_image_surface_mask,
+    _cairo_image_surface_stroke,
+    _cairo_image_surface_fill,
+    NULL, /* fill-stroke */
+    _cairo_image_surface_glyphs,
+};
+
+/* A convenience function for when one needs to coerce an image
+ * surface to an alternate format. */
+cairo_image_surface_t *
+_cairo_image_surface_coerce (cairo_image_surface_t *surface)
+{
+    return _cairo_image_surface_coerce_to_format (surface,
+                                                 _cairo_format_from_content (surface->base.content));
+}
+
+/* A convenience function for when one needs to coerce an image
+ * surface to an alternate format. */
+cairo_image_surface_t *
+_cairo_image_surface_coerce_to_format (cairo_image_surface_t *surface,
+                                      cairo_format_t         format)
+{
+    cairo_image_surface_t *clone;
+    cairo_status_t status;
+
+    status = surface->base.status;
+    if (unlikely (status))
+       return (cairo_image_surface_t *)_cairo_surface_create_in_error (status);
+
+    if (surface->format == format)
+       return (cairo_image_surface_t *)cairo_surface_reference(&surface->base);
+
+    clone = (cairo_image_surface_t *)
+       cairo_image_surface_create (format, surface->width, surface->height);
+    if (unlikely (clone->base.status))
+       return clone;
+
+    pixman_image_composite32 (PIXMAN_OP_SRC,
+                              surface->pixman_image, NULL, clone->pixman_image,
+                              0, 0,
+                              0, 0,
+                              0, 0,
+                              surface->width, surface->height);
+    clone->base.is_clear = FALSE;
+
+    clone->base.device_transform =
+       surface->base.device_transform;
+    clone->base.device_transform_inverse =
+       surface->base.device_transform_inverse;
+
+    return clone;
+}
+
+cairo_image_transparency_t
+_cairo_image_analyze_transparency (cairo_image_surface_t *image)
+{
+    int x, y;
+
+    if (image->transparency != CAIRO_IMAGE_UNKNOWN)
+       return image->transparency;
+
+    if ((image->base.content & CAIRO_CONTENT_ALPHA) == 0)
+       return image->transparency = CAIRO_IMAGE_IS_OPAQUE;
+
+    if (image->base.is_clear)
+       return image->transparency = CAIRO_IMAGE_HAS_BILEVEL_ALPHA;
+
+    if ((image->base.content & CAIRO_CONTENT_COLOR) == 0) {
+       if (image->format == CAIRO_FORMAT_A1) {
+           return image->transparency = CAIRO_IMAGE_HAS_BILEVEL_ALPHA;
+       } else if (image->format == CAIRO_FORMAT_A8) {
+           for (y = 0; y < image->height; y++) {
+               uint8_t *alpha = (uint8_t *) (image->data + y * image->stride);
+
+               for (x = 0; x < image->width; x++, alpha++) {
+                   if (*alpha > 0 && *alpha < 255)
+                       return image->transparency = CAIRO_IMAGE_HAS_ALPHA;
+               }
+           }
+           return image->transparency = CAIRO_IMAGE_HAS_BILEVEL_ALPHA;
+       } else {
+           return image->transparency = CAIRO_IMAGE_HAS_ALPHA;
+       }
+    }
+
+    if (image->format == CAIRO_FORMAT_RGB16_565) {
+       image->transparency = CAIRO_IMAGE_IS_OPAQUE;
+       return CAIRO_IMAGE_IS_OPAQUE;
+    }
+
+    if (image->format != CAIRO_FORMAT_ARGB32)
+       return image->transparency = CAIRO_IMAGE_HAS_ALPHA;
+
+    image->transparency = CAIRO_IMAGE_IS_OPAQUE;
+    for (y = 0; y < image->height; y++) {
+       uint32_t *pixel = (uint32_t *) (image->data + y * image->stride);
+
+       for (x = 0; x < image->width; x++, pixel++) {
+           int a = (*pixel & 0xff000000) >> 24;
+           if (a > 0 && a < 255) {
+               return image->transparency = CAIRO_IMAGE_HAS_ALPHA;
+           } else if (a == 0) {
+               image->transparency = CAIRO_IMAGE_HAS_BILEVEL_ALPHA;
+           }
+       }
+    }
+
+    return image->transparency;
+}
+
+cairo_image_color_t
+_cairo_image_analyze_color (cairo_image_surface_t      *image)
+{
+    int x, y;
+
+    if (image->color != CAIRO_IMAGE_UNKNOWN_COLOR)
+       return image->color;
+
+    if (image->format == CAIRO_FORMAT_A1 || image->format == CAIRO_FORMAT_A8)
+       return image->color = CAIRO_IMAGE_IS_MONOCHROME;
+
+    if (image->format == CAIRO_FORMAT_ARGB32) {
+       image->color = CAIRO_IMAGE_IS_MONOCHROME;
+       for (y = 0; y < image->height; y++) {
+           uint32_t *pixel = (uint32_t *) (image->data + y * image->stride);
+
+           for (x = 0; x < image->width; x++, pixel++) {
+               int a = (*pixel & 0xff000000) >> 24;
+               int r = (*pixel & 0x00ff0000) >> 16;
+               int g = (*pixel & 0x0000ff00) >> 8;
+               int b = (*pixel & 0x000000ff);
+               if (a == 0) {
+                   r = g = b = 0;
+               } else {
+                   r = (r * 255 + a / 2) / a;
+                   g = (g * 255 + a / 2) / a;
+                   b = (b * 255 + a / 2) / a;
+               }
+               if (!(r == g && g == b))
+                   return image->color = CAIRO_IMAGE_IS_COLOR;
+               else if (r > 0 && r < 255)
+                   image->color = CAIRO_IMAGE_IS_GRAYSCALE;
+           }
+       }
+       return image->color;
+    }
+
+    if (image->format == CAIRO_FORMAT_RGB24) {
+       image->color = CAIRO_IMAGE_IS_MONOCHROME;
+       for (y = 0; y < image->height; y++) {
+           uint32_t *pixel = (uint32_t *) (image->data + y * image->stride);
+
+           for (x = 0; x < image->width; x++, pixel++) {
+               int r = (*pixel & 0x00ff0000) >> 16;
+               int g = (*pixel & 0x0000ff00) >>  8;
+               int b = (*pixel & 0x000000ff);
+               if (!(r == g && g == b))
+                   return image->color = CAIRO_IMAGE_IS_COLOR;
+               else if (r > 0 && r < 255)
+                   image->color = CAIRO_IMAGE_IS_GRAYSCALE;
+           }
+       }
+       return image->color;
+    }
+
+    return image->color = CAIRO_IMAGE_IS_COLOR;
+}
diff --git a/src/cairo-list-private.h b/src/cairo-list-private.h
new file mode 100644 (file)
index 0000000..ddfd0a4
--- /dev/null
@@ -0,0 +1,215 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Chris Wilson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Chris Wilson.
+ *
+ * Contributor(s):
+ *      Chris Wilson <chris@chris-wilson.co.uk>
+ *
+ */
+
+#ifndef CAIRO_LIST_PRIVATE_H
+#define CAIRO_LIST_PRIVATE_H
+
+#include "cairo-compiler-private.h"
+
+/* Basic circular, doubly linked list implementation */
+
+typedef struct _cairo_list {
+    struct _cairo_list *next, *prev;
+} cairo_list_t;
+
+#define cairo_list_entry(ptr, type, member) \
+       cairo_container_of(ptr, type, member)
+
+#define cairo_list_first_entry(ptr, type, member) \
+       cairo_list_entry((ptr)->next, type, member)
+
+#define cairo_list_last_entry(ptr, type, member) \
+       cairo_list_entry((ptr)->prev, type, member)
+
+#define cairo_list_foreach(pos, head)                  \
+       for (pos = (head)->next; pos != (head); pos = pos->next)
+
+#define cairo_list_foreach_entry(pos, type, head, member)              \
+       for (pos = cairo_list_entry((head)->next, type, member);\
+            &pos->member != (head);                                    \
+            pos = cairo_list_entry(pos->member.next, type, member))
+
+#define cairo_list_foreach_entry_safe(pos, n, type, head, member)      \
+       for (pos = cairo_list_entry ((head)->next, type, member),\
+            n = cairo_list_entry (pos->member.next, type, member);\
+            &pos->member != (head);                                    \
+            pos = n, n = cairo_list_entry (n->member.next, type, member))
+
+#define cairo_list_foreach_entry_reverse(pos, type, head, member)      \
+       for (pos = cairo_list_entry((head)->prev, type, member);\
+            &pos->member != (head);                                    \
+            pos = cairo_list_entry(pos->member.prev, type, member))
+
+#define cairo_list_foreach_entry_reverse_safe(pos, n, type, head, member)      \
+       for (pos = cairo_list_entry((head)->prev, type, member),\
+            n = cairo_list_entry (pos->member.prev, type, member);\
+            &pos->member != (head);                                    \
+            pos = n, n = cairo_list_entry (n->member.prev, type, member))
+
+#ifdef CAIRO_LIST_DEBUG
+static inline void
+_cairo_list_validate (const cairo_list_t *link)
+{
+    assert (link->next->prev == link);
+    assert (link->prev->next == link);
+}
+static inline void
+cairo_list_validate (const cairo_list_t *head)
+{
+    cairo_list_t *link;
+
+    cairo_list_foreach (link, head)
+       _cairo_list_validate (link);
+}
+static inline cairo_bool_t
+cairo_list_is_empty (const cairo_list_t *head);
+static inline void
+cairo_list_validate_is_empty (const cairo_list_t *head)
+{
+    assert (head->next == NULL || (cairo_list_is_empty (head) && head->next == head->prev));
+}
+#else
+#define _cairo_list_validate(link)
+#define cairo_list_validate(head)
+#define cairo_list_validate_is_empty(head)
+#endif
+
+static inline void
+cairo_list_init (cairo_list_t *entry)
+{
+    entry->next = entry;
+    entry->prev = entry;
+}
+
+static inline void
+__cairo_list_add (cairo_list_t *entry,
+                 cairo_list_t *prev,
+                 cairo_list_t *next)
+{
+    next->prev = entry;
+    entry->next = next;
+    entry->prev = prev;
+    prev->next = entry;
+}
+
+static inline void
+cairo_list_add (cairo_list_t *entry, cairo_list_t *head)
+{
+    cairo_list_validate (head);
+    cairo_list_validate_is_empty (entry);
+    __cairo_list_add (entry, head, head->next);
+    cairo_list_validate (head);
+}
+
+static inline void
+cairo_list_add_tail (cairo_list_t *entry, cairo_list_t *head)
+{
+    cairo_list_validate (head);
+    cairo_list_validate_is_empty (entry);
+    __cairo_list_add (entry, head->prev, head);
+    cairo_list_validate (head);
+}
+
+static inline void
+__cairo_list_del (cairo_list_t *prev, cairo_list_t *next)
+{
+    next->prev = prev;
+    prev->next = next;
+}
+
+static inline void
+cairo_list_del (cairo_list_t *entry)
+{
+    __cairo_list_del (entry->prev, entry->next);
+    cairo_list_init (entry);
+}
+
+static inline void
+cairo_list_move (cairo_list_t *entry, cairo_list_t *head)
+{
+    cairo_list_validate (head);
+    __cairo_list_del (entry->prev, entry->next);
+    __cairo_list_add (entry, head, head->next);
+    cairo_list_validate (head);
+}
+
+static inline void
+cairo_list_move_tail (cairo_list_t *entry, cairo_list_t *head)
+{
+    cairo_list_validate (head);
+    __cairo_list_del (entry->prev, entry->next);
+    __cairo_list_add (entry, head->prev, head);
+    cairo_list_validate (head);
+}
+
+static inline void
+cairo_list_swap (cairo_list_t *entry, cairo_list_t *other)
+{
+    __cairo_list_add (entry, other->prev, other->next);
+    cairo_list_init (other);
+}
+
+static inline cairo_bool_t
+cairo_list_is_first (const cairo_list_t *entry,
+                    const cairo_list_t *head)
+{
+    cairo_list_validate (head);
+    return entry->prev == head;
+}
+
+static inline cairo_bool_t
+cairo_list_is_last (const cairo_list_t *entry,
+                   const cairo_list_t *head)
+{
+    cairo_list_validate (head);
+    return entry->next == head;
+}
+
+static inline cairo_bool_t
+cairo_list_is_empty (const cairo_list_t *head)
+{
+    cairo_list_validate (head);
+    return head->next == head;
+}
+
+static inline cairo_bool_t
+cairo_list_is_singular (const cairo_list_t *head)
+{
+    cairo_list_validate (head);
+    return head->next == head || head->next == head->prev;
+}
+
+#endif /* CAIRO_LIST_PRIVATE_H */
diff --git a/src/cairo-lzw.c b/src/cairo-lzw.c
new file mode 100644 (file)
index 0000000..de7f999
--- /dev/null
@@ -0,0 +1,404 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2006 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *     Carl D. Worth <cworth@cworth.org>
+ */
+
+#include "cairoint.h"
+#include "cairo-error-private.h"
+
+typedef struct _lzw_buf {
+    cairo_status_t status;
+
+    unsigned char *data;
+    int data_size;
+    int num_data;
+    uint32_t pending;
+    unsigned int pending_bits;
+} lzw_buf_t;
+
+/* An lzw_buf_t is a simple, growable chunk of memory for holding
+ * variable-size objects of up to 16 bits each.
+ *
+ * Initialize an lzw_buf_t to the given size in bytes.
+ *
+ * To store objects into the lzw_buf_t, call _lzw_buf_store_bits and
+ * when finished, call _lzw_buf_store_pending, (which flushes out the
+ * last few bits that hadn't yet made a complete byte yet).
+ *
+ * Instead of returning failure from any functions, lzw_buf_t provides
+ * a status value that the caller can query, (and should query at
+ * least once when done with the object). The status value will be
+ * either %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY;
+ */
+static void
+_lzw_buf_init (lzw_buf_t *buf, int size)
+{
+    if (size == 0)
+       size = 16;
+
+    buf->status = CAIRO_STATUS_SUCCESS;
+    buf->data_size = size;
+    buf->num_data = 0;
+    buf->pending = 0;
+    buf->pending_bits = 0;
+
+    buf->data = malloc (size);
+    if (unlikely (buf->data == NULL)) {
+       buf->data_size = 0;
+       buf->status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+       return;
+    }
+}
+
+/* Increase the buffer size by doubling.
+ *
+ * Returns %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY
+ */
+static cairo_status_t
+_lzw_buf_grow (lzw_buf_t *buf)
+{
+    int new_size = buf->data_size * 2;
+    unsigned char *new_data;
+
+    if (buf->status)
+       return buf->status;
+
+    new_data = NULL;
+    /* check for integer overflow */
+    if (new_size / 2 == buf->data_size)
+       new_data = realloc (buf->data, new_size);
+
+    if (unlikely (new_data == NULL)) {
+       free (buf->data);
+       buf->data_size = 0;
+       buf->status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+       return buf->status;
+    }
+
+    buf->data = new_data;
+    buf->data_size = new_size;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+/* Store the lowest num_bits bits of values into buf.
+ *
+ * Note: The bits of value above size_in_bits must be 0, (so don't lie
+ * about the size).
+ *
+ * See also _lzw_buf_store_pending which must be called after the last
+ * call to _lzw_buf_store_bits.
+ *
+ * Sets buf->status to either %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY.
+ */
+static void
+_lzw_buf_store_bits (lzw_buf_t *buf, uint16_t value, int num_bits)
+{
+    cairo_status_t status;
+
+    assert (value <= (1 << num_bits) - 1);
+
+    if (buf->status)
+       return;
+
+    buf->pending = (buf->pending << num_bits) | value;
+    buf->pending_bits += num_bits;
+
+    while (buf->pending_bits >= 8) {
+       if (buf->num_data >= buf->data_size) {
+           status = _lzw_buf_grow (buf);
+           if (unlikely (status))
+               return;
+       }
+       buf->data[buf->num_data++] = buf->pending >> (buf->pending_bits - 8);
+       buf->pending_bits -= 8;
+    }
+}
+
+/* Store the last remaining pending bits into the buffer.
+ *
+ * Note: This function must be called after the last call to
+ * _lzw_buf_store_bits.
+ *
+ * Sets buf->status to either %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY.
+ */
+static void
+_lzw_buf_store_pending  (lzw_buf_t *buf)
+{
+    cairo_status_t status;
+
+    if (buf->status)
+       return;
+
+    if (buf->pending_bits == 0)
+       return;
+
+    assert (buf->pending_bits < 8);
+
+    if (buf->num_data >= buf->data_size) {
+       status = _lzw_buf_grow (buf);
+       if (unlikely (status))
+           return;
+    }
+
+    buf->data[buf->num_data++] = buf->pending << (8 - buf->pending_bits);
+    buf->pending_bits = 0;
+}
+
+/* LZW defines a few magic code values */
+#define LZW_CODE_CLEAR_TABLE   256
+#define LZW_CODE_EOD           257
+#define LZW_CODE_FIRST         258
+
+/* We pack three separate values into a symbol as follows:
+ *
+ * 12 bits (31 down to 20):    CODE: code value used to represent this symbol
+ * 12 bits (19 down to  8):    PREV: previous code value in chain
+ *  8 bits ( 7 down to  0):    NEXT: next byte value in chain
+ */
+typedef uint32_t lzw_symbol_t;
+
+#define LZW_SYMBOL_SET(sym, prev, next)                        ((sym) = ((prev) << 8)|(next))
+#define LZW_SYMBOL_SET_CODE(sym, code, prev, next)     ((sym) = ((code << 20)|(prev) << 8)|(next))
+#define LZW_SYMBOL_GET_CODE(sym)                       (((sym) >> 20))
+#define LZW_SYMBOL_GET_PREV(sym)                       (((sym) >>  8) & 0x7ff)
+#define LZW_SYMBOL_GET_BYTE(sym)                       (((sym) >>  0) & 0x0ff)
+
+/* The PREV+NEXT fields can be seen as the key used to fetch values
+ * from the hash table, while the code is the value fetched.
+ */
+#define LZW_SYMBOL_KEY_MASK    0x000fffff
+
+/* Since code values are only stored starting with 258 we can safely
+ * use a zero value to represent free slots in the hash table. */
+#define LZW_SYMBOL_FREE                0x00000000
+
+/* These really aren't very free for modifying. First, the PostScript
+ * specification sets the 9-12 bit range. Second, the encoding of
+ * lzw_symbol_t above also relies on 2 of LZW_BITS_MAX plus one byte
+ * fitting within 32 bits.
+ *
+ * But other than that, the LZW compression scheme could function with
+ * more bits per code.
+ */
+#define LZW_BITS_MIN           9
+#define LZW_BITS_MAX           12
+#define LZW_BITS_BOUNDARY(bits)        ((1<<(bits))-1)
+#define LZW_MAX_SYMBOLS                (1<<LZW_BITS_MAX)
+
+#define LZW_SYMBOL_TABLE_SIZE  9013
+#define LZW_SYMBOL_MOD1                LZW_SYMBOL_TABLE_SIZE
+#define LZW_SYMBOL_MOD2                9011
+
+typedef struct _lzw_symbol_table {
+    lzw_symbol_t table[LZW_SYMBOL_TABLE_SIZE];
+} lzw_symbol_table_t;
+
+/* Initialize the hash table to entirely empty */
+static void
+_lzw_symbol_table_init (lzw_symbol_table_t *table)
+{
+    memset (table->table, 0, LZW_SYMBOL_TABLE_SIZE * sizeof (lzw_symbol_t));
+}
+
+/* Lookup a symbol in the symbol table. The PREV and NEXT fields of
+ * symbol form the key for the lookup.
+ *
+ * If successful, then this function returns %TRUE and slot_ret will be
+ * left pointing at the result that will have the CODE field of
+ * interest.
+ *
+ * If the lookup fails, then this function returns %FALSE and slot_ret
+ * will be pointing at the location in the table to which a new CODE
+ * value should be stored along with PREV and NEXT.
+ */
+static cairo_bool_t
+_lzw_symbol_table_lookup (lzw_symbol_table_t    *table,
+                         lzw_symbol_t            symbol,
+                         lzw_symbol_t          **slot_ret)
+{
+    /* The algorithm here is identical to that in cairo-hash.c. We
+     * copy it here to allow for a rather more efficient
+     * implementation due to several circumstances that do not apply
+     * to the more general case:
+     *
+     * 1) We have a known bound on the total number of symbols, so we
+     *    have a fixed-size table without any copying when growing
+     *
+     * 2) We never delete any entries, so we don't need to
+     *    support/check for DEAD entries during lookup.
+     *
+     * 3) The object fits in 32 bits so we store each object in its
+     *    entirety within the table rather than storing objects
+     *    externally and putting pointers in the table, (which here
+     *    would just double the storage requirements and have negative
+     *    impacts on memory locality).
+     */
+    int i, idx, step, hash = symbol & LZW_SYMBOL_KEY_MASK;
+    lzw_symbol_t candidate;
+
+    idx = hash % LZW_SYMBOL_MOD1;
+    step = 0;
+
+    *slot_ret = NULL;
+    for (i = 0; i < LZW_SYMBOL_TABLE_SIZE; i++)
+    {
+       candidate = table->table[idx];
+       if (candidate == LZW_SYMBOL_FREE)
+       {
+           *slot_ret = &table->table[idx];
+           return FALSE;
+       }
+       else /* candidate is LIVE */
+       {
+           if ((candidate & LZW_SYMBOL_KEY_MASK) ==
+               (symbol & LZW_SYMBOL_KEY_MASK))
+           {
+               *slot_ret = &table->table[idx];
+               return TRUE;
+           }
+       }
+
+       if (step == 0) {
+           step = hash % LZW_SYMBOL_MOD2;
+           if (step == 0)
+               step = 1;
+       }
+
+       idx += step;
+       if (idx >= LZW_SYMBOL_TABLE_SIZE)
+           idx -= LZW_SYMBOL_TABLE_SIZE;
+    }
+
+    return FALSE;
+}
+
+/* Compress a bytestream using the LZW algorithm.
+ *
+ * This is an original implementation based on reading the
+ * specification of the LZWDecode filter in the PostScript Language
+ * Reference. The free parameters in the LZW algorithm are set to the
+ * values mandated by PostScript, (symbols encoded with widths from 9
+ * to 12 bits).
+ *
+ * This function returns a pointer to a newly allocated buffer holding
+ * the compressed data, or %NULL if an out-of-memory situation
+ * occurs.
+ *
+ * Notice that any one of the _lzw_buf functions called here could
+ * trigger an out-of-memory condition. But lzw_buf_t uses cairo's
+ * shutdown-on-error idiom, so it's safe to continue to call into
+ * lzw_buf without having to check for errors, (until a final check at
+ * the end).
+ */
+unsigned char *
+_cairo_lzw_compress (unsigned char *data, unsigned long *size_in_out)
+{
+    int bytes_remaining = *size_in_out;
+    lzw_buf_t buf;
+    lzw_symbol_table_t table;
+    lzw_symbol_t symbol, *slot = NULL; /* just to squelch a warning */
+    int code_next = LZW_CODE_FIRST;
+    int code_bits = LZW_BITS_MIN;
+    int prev, next = 0; /* just to squelch a warning */
+
+    if (*size_in_out == 0)
+       return NULL;
+
+    _lzw_buf_init (&buf, *size_in_out);
+
+    _lzw_symbol_table_init (&table);
+
+    /* The LZW header is a clear table code. */
+    _lzw_buf_store_bits (&buf, LZW_CODE_CLEAR_TABLE, code_bits);
+
+    while (1) {
+
+       /* Find the longest existing code in the symbol table that
+        * matches the current input, if any. */
+       prev = *data++;
+       bytes_remaining--;
+       if (bytes_remaining) {
+           do
+           {
+               next = *data++;
+               bytes_remaining--;
+               LZW_SYMBOL_SET (symbol, prev, next);
+               if (_lzw_symbol_table_lookup (&table, symbol, &slot))
+                   prev = LZW_SYMBOL_GET_CODE (*slot);
+           } while (bytes_remaining && *slot != LZW_SYMBOL_FREE);
+           if (*slot == LZW_SYMBOL_FREE) {
+               data--;
+               bytes_remaining++;
+           }
+       }
+
+       /* Write the code into the output. This is either a byte read
+        * directly from the input, or a code from the last successful
+        * lookup. */
+       _lzw_buf_store_bits (&buf, prev, code_bits);
+
+       if (bytes_remaining == 0)
+           break;
+
+       LZW_SYMBOL_SET_CODE (*slot, code_next++, prev, next);
+
+       if (code_next > LZW_BITS_BOUNDARY(code_bits))
+       {
+           code_bits++;
+           if (code_bits > LZW_BITS_MAX) {
+               _lzw_symbol_table_init (&table);
+               _lzw_buf_store_bits (&buf, LZW_CODE_CLEAR_TABLE, code_bits - 1);
+               code_bits = LZW_BITS_MIN;
+               code_next = LZW_CODE_FIRST;
+           }
+       }
+    }
+
+    /* The LZW footer is an end-of-data code. */
+    _lzw_buf_store_bits (&buf, LZW_CODE_EOD, code_bits);
+
+    _lzw_buf_store_pending (&buf);
+
+    /* See if we ever ran out of memory while writing to buf. */
+    if (buf.status == CAIRO_STATUS_NO_MEMORY) {
+       *size_in_out = 0;
+       return NULL;
+    }
+
+    assert (buf.status == CAIRO_STATUS_SUCCESS);
+
+    *size_in_out = buf.num_data;
+    return buf.data;
+}
diff --git a/src/cairo-malloc-private.h b/src/cairo-malloc-private.h
new file mode 100644 (file)
index 0000000..e5acf16
--- /dev/null
@@ -0,0 +1,149 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* Cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2007 Mozilla Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation
+ *
+ * Contributor(s):
+ *     Vladimir Vukicevic <vladimir@pobox.com>
+ */
+
+#ifndef CAIRO_MALLOC_PRIVATE_H
+#define CAIRO_MALLOC_PRIVATE_H
+
+#include "cairo-wideint-private.h"
+#include <stdlib.h>
+
+#if HAVE_MEMFAULT
+#include <memfault.h>
+#define CAIRO_INJECT_FAULT() MEMFAULT_INJECT_FAULT()
+#else
+#define CAIRO_INJECT_FAULT() 0
+#endif
+
+/**
+ * _cairo_malloc:
+ * @size: size in bytes
+ *
+ * Allocate @size memory using malloc().
+ * The memory should be freed using free().
+ * malloc is skipped, if 0 bytes are requested, and %NULL will be returned.
+ *
+ * Return value: A pointer to the newly allocated memory, or %NULL in
+ * case of malloc() failure or size is 0.
+ */
+
+#define _cairo_malloc(size) \
+   ((size) ? malloc((unsigned) (size)) : NULL)
+
+/**
+ * _cairo_malloc_ab:
+ * @n: number of elements to allocate
+ * @size: size of each element
+ *
+ * Allocates @n*@size memory using _cairo_malloc(), taking care to not
+ * overflow when doing the multiplication.  Behaves much like
+ * calloc(), except that the returned memory is not set to zero.
+ * The memory should be freed using free().
+ *
+ * @size should be a constant so that the compiler can optimize
+ * out a constant division.
+ *
+ * Return value: A pointer to the newly allocated memory, or %NULL in
+ * case of malloc() failure or overflow.
+ */
+
+#define _cairo_malloc_ab(a, size) \
+  ((size) && (unsigned) (a) >= INT32_MAX / (unsigned) (size) ? NULL : \
+   _cairo_malloc((unsigned) (a) * (unsigned) (size)))
+
+/**
+ * _cairo_realloc_ab:
+ * @ptr: original pointer to block of memory to be resized
+ * @n: number of elements to allocate
+ * @size: size of each element
+ *
+ * Reallocates @ptr a block of @n*@size memory using realloc(), taking
+ * care to not overflow when doing the multiplication.  The memory
+ * should be freed using free().
+ *
+ * @size should be a constant so that the compiler can optimize
+ * out a constant division.
+ *
+ * Return value: A pointer to the newly allocated memory, or %NULL in
+ * case of realloc() failure or overflow (whereupon the original block
+ * of memory * is left untouched).
+ */
+
+#define _cairo_realloc_ab(ptr, a, size) \
+  ((size) && (unsigned) (a) >= INT32_MAX / (unsigned) (size) ? NULL : \
+   realloc(ptr, (unsigned) (a) * (unsigned) (size)))
+
+/**
+ * _cairo_malloc_abc:
+ * @n: first factor of number of elements to allocate
+ * @b: second factor of number of elements to allocate
+ * @size: size of each element
+ *
+ * Allocates @n*@b*@size memory using _cairo_malloc(), taking care to not
+ * overflow when doing the multiplication.  Behaves like
+ * _cairo_malloc_ab().  The memory should be freed using free().
+ *
+ * @size should be a constant so that the compiler can optimize
+ * out a constant division.
+ *
+ * Return value: A pointer to the newly allocated memory, or %NULL in
+ * case of malloc() failure or overflow.
+ */
+
+#define _cairo_malloc_abc(a, b, size) \
+  ((b) && (unsigned) (a) >= INT32_MAX / (unsigned) (b) ? NULL : \
+   (size) && (unsigned) ((a)*(b)) >= INT32_MAX / (unsigned) (size) ? NULL : \
+   _cairo_malloc((unsigned) (a) * (unsigned) (b) * (unsigned) (size)))
+
+/**
+ * _cairo_malloc_ab_plus_c:
+ * @n: number of elements to allocate
+ * @size: size of each element
+ * @k: additional size to allocate
+ *
+ * Allocates @n*@ksize+@k memory using _cairo_malloc(), taking care to not
+ * overflow when doing the arithmetic.  Behaves like
+ * _cairo_malloc_ab().  The memory should be freed using free().
+ *
+ * Return value: A pointer to the newly allocated memory, or %NULL in
+ * case of malloc() failure or overflow.
+ */
+
+#define _cairo_malloc_ab_plus_c(n, size, k) \
+  ((size) && (unsigned) (n) >= INT32_MAX / (unsigned) (size) ? NULL : \
+   (unsigned) (k) >= INT32_MAX - (unsigned) (n) * (unsigned) (size) ? NULL : \
+   _cairo_malloc((unsigned) (n) * (unsigned) (size) + (unsigned) (k)))
+
+#endif /* CAIRO_MALLOC_PRIVATE_H */
diff --git a/src/cairo-mask-compositor.c b/src/cairo-mask-compositor.c
new file mode 100644 (file)
index 0000000..b34ffa2
--- /dev/null
@@ -0,0 +1,1412 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ * Copyright © 2005 Red Hat, Inc.
+ * Copyright © 2011 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *     Carl D. Worth <cworth@cworth.org>
+ *      Joonas Pihlaja <jpihlaja@cc.helsinki.fi>
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+/* This compositor renders the shape to a mask using an image surface
+ * then calls composite.
+ */
+
+#include "cairoint.h"
+
+#include "cairo-compositor-private.h"
+#include "cairo-image-surface-private.h"
+#include "cairo-pattern-private.h"
+#include "cairo-region-private.h"
+#include "cairo-surface-observer-private.h"
+#include "cairo-surface-offset-private.h"
+#include "cairo-surface-snapshot-private.h"
+#include "cairo-surface-subsurface-private.h"
+
+typedef cairo_int_status_t
+(*draw_func_t) (const cairo_mask_compositor_t *compositor,
+               cairo_surface_t                 *dst,
+               void                            *closure,
+               cairo_operator_t                 op,
+               const cairo_pattern_t           *src,
+               const cairo_rectangle_int_t     *src_sample,
+               int                              dst_x,
+               int                              dst_y,
+               const cairo_rectangle_int_t     *extents,
+               cairo_clip_t                    *clip);
+
+static void do_unaligned_row(void (*blt)(void *closure,
+                                        int16_t x, int16_t y,
+                                        int16_t w, int16_t h,
+                                        uint16_t coverage),
+                            void *closure,
+                            const cairo_box_t *b,
+                            int tx, int y, int h,
+                            uint16_t coverage)
+{
+    int x1 = _cairo_fixed_integer_part (b->p1.x) - tx;
+    int x2 = _cairo_fixed_integer_part (b->p2.x) - tx;
+    if (x2 > x1) {
+       if (! _cairo_fixed_is_integer (b->p1.x)) {
+           blt(closure, x1, y, 1, h,
+               coverage * (256 - _cairo_fixed_fractional_part (b->p1.x)));
+           x1++;
+       }
+
+       if (x2 > x1)
+           blt(closure, x1, y, x2-x1, h, (coverage << 8) - (coverage >> 8));
+
+       if (! _cairo_fixed_is_integer (b->p2.x))
+           blt(closure, x2, y, 1, h,
+               coverage * _cairo_fixed_fractional_part (b->p2.x));
+    } else
+       blt(closure, x1, y, 1, h,
+           coverage * (b->p2.x - b->p1.x));
+}
+
+static void do_unaligned_box(void (*blt)(void *closure,
+                                        int16_t x, int16_t y,
+                                        int16_t w, int16_t h,
+                                        uint16_t coverage),
+                            void *closure,
+                            const cairo_box_t *b, int tx, int ty)
+{
+    int y1 = _cairo_fixed_integer_part (b->p1.y) - ty;
+    int y2 = _cairo_fixed_integer_part (b->p2.y) - ty;
+    if (y2 > y1) {
+       if (! _cairo_fixed_is_integer (b->p1.y)) {
+           do_unaligned_row(blt, closure, b, tx, y1, 1,
+                            256 - _cairo_fixed_fractional_part (b->p1.y));
+           y1++;
+       }
+
+       if (y2 > y1)
+           do_unaligned_row(blt, closure, b, tx, y1, y2-y1, 256);
+
+       if (! _cairo_fixed_is_integer (b->p2.y))
+           do_unaligned_row(blt, closure, b, tx, y2, 1,
+                            _cairo_fixed_fractional_part (b->p2.y));
+    } else
+       do_unaligned_row(blt, closure, b, tx, y1, 1,
+                        b->p2.y - b->p1.y);
+}
+
+struct blt_in {
+    const cairo_mask_compositor_t *compositor;
+    cairo_surface_t *dst;
+};
+
+static void blt_in(void *closure,
+                  int16_t x, int16_t y,
+                  int16_t w, int16_t h,
+                  uint16_t coverage)
+{
+    struct blt_in *info = closure;
+    cairo_color_t color;
+    cairo_rectangle_int_t rect;
+
+    if (coverage == 0xffff)
+       return;
+
+    rect.x = x;
+    rect.y = y;
+    rect.width  = w;
+    rect.height = h;
+
+    _cairo_color_init_rgba (&color, 0, 0, 0, coverage / (double) 0xffff);
+    info->compositor->fill_rectangles (info->dst, CAIRO_OPERATOR_IN,
+                                      &color, &rect, 1);
+}
+
+static cairo_surface_t *
+create_composite_mask (const cairo_mask_compositor_t *compositor,
+                      cairo_surface_t          *dst,
+                      void                     *draw_closure,
+                      draw_func_t               draw_func,
+                      draw_func_t               mask_func,
+                      const cairo_composite_rectangles_t *extents)
+{
+    cairo_surface_t *surface;
+    cairo_int_status_t status;
+    struct blt_in info;
+    int i;
+
+    surface = _cairo_surface_create_similar_solid (dst,
+                                                  CAIRO_CONTENT_ALPHA,
+                                                  extents->bounded.width,
+                                                  extents->bounded.height,
+                                                  CAIRO_COLOR_TRANSPARENT);
+    if (unlikely (surface->status))
+       return surface;
+
+    if (mask_func) {
+       status = mask_func (compositor, surface, draw_closure,
+                           CAIRO_OPERATOR_SOURCE, NULL, NULL,
+                           extents->bounded.x, extents->bounded.y,
+                           &extents->bounded, extents->clip);
+       if (likely (status != CAIRO_INT_STATUS_UNSUPPORTED))
+           return surface;
+    }
+
+    /* Is it worth setting the clip region here? */
+    status = draw_func (compositor, surface, draw_closure,
+                       CAIRO_OPERATOR_ADD, NULL, NULL,
+                       extents->bounded.x, extents->bounded.y,
+                       &extents->bounded, NULL);
+    if (unlikely (status)) {
+       cairo_surface_destroy (surface);
+       return _cairo_surface_create_in_error (status);
+    }
+
+    info.compositor = compositor;
+    info.dst = surface;
+    for (i = 0; i < extents->clip->num_boxes; i++) {
+       cairo_box_t *b = &extents->clip->boxes[i];
+
+       if (! _cairo_fixed_is_integer (b->p1.x) ||
+           ! _cairo_fixed_is_integer (b->p1.y) ||
+           ! _cairo_fixed_is_integer (b->p2.x) ||
+           ! _cairo_fixed_is_integer (b->p2.y))
+       {
+           do_unaligned_box(blt_in, &info, b,
+                            extents->bounded.x,
+                            extents->bounded.y);
+       }
+    }
+
+    if (extents->clip->path != NULL) {
+       status = _cairo_clip_combine_with_surface (extents->clip, surface,
+                                                  extents->bounded.x,
+                                                  extents->bounded.y);
+       if (unlikely (status)) {
+           cairo_surface_destroy (surface);
+           return _cairo_surface_create_in_error (status);
+       }
+    }
+
+    return surface;
+}
+
+/* Handles compositing with a clip surface when the operator allows
+ * us to combine the clip with the mask
+ */
+static cairo_status_t
+clip_and_composite_with_mask (const cairo_mask_compositor_t *compositor,
+                             void                      *draw_closure,
+                             draw_func_t                draw_func,
+                             draw_func_t                mask_func,
+                             cairo_operator_t           op,
+                             cairo_pattern_t           *pattern,
+                             const cairo_composite_rectangles_t*extents)
+{
+    cairo_surface_t *dst = extents->surface;
+    cairo_surface_t *mask, *src;
+    int src_x, src_y;
+
+    mask = create_composite_mask (compositor, dst, draw_closure,
+                                 draw_func, mask_func,
+                                 extents);
+    if (unlikely (mask->status))
+       return mask->status;
+
+    if (pattern != NULL || dst->content != CAIRO_CONTENT_ALPHA) {
+       src = compositor->pattern_to_surface (dst,
+                                             &extents->source_pattern.base,
+                                             FALSE,
+                                             &extents->bounded,
+                                             &extents->source_sample_area,
+                                             &src_x, &src_y);
+       if (unlikely (src->status)) {
+           cairo_surface_destroy (mask);
+           return src->status;
+       }
+
+       compositor->composite (dst, op, src, mask,
+                              extents->bounded.x + src_x,
+                              extents->bounded.y + src_y,
+                              0, 0,
+                              extents->bounded.x,      extents->bounded.y,
+                              extents->bounded.width,  extents->bounded.height);
+
+       cairo_surface_destroy (src);
+    } else {
+       compositor->composite (dst, op, mask, NULL,
+                              0, 0,
+                              0, 0,
+                              extents->bounded.x,      extents->bounded.y,
+                              extents->bounded.width,  extents->bounded.height);
+    }
+    cairo_surface_destroy (mask);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+/* Handles compositing with a clip surface when we have to do the operation
+ * in two pieces and combine them together.
+ */
+static cairo_status_t
+clip_and_composite_combine (const cairo_mask_compositor_t *compositor,
+                           void                        *draw_closure,
+                           draw_func_t          draw_func,
+                           cairo_operator_t             op,
+                           const cairo_pattern_t       *pattern,
+                           const cairo_composite_rectangles_t*extents)
+{
+    cairo_surface_t *dst = extents->surface;
+    cairo_surface_t *tmp, *clip;
+    cairo_status_t status;
+
+    tmp = _cairo_surface_create_similar_scratch (dst, dst->content,
+                                                extents->bounded.width,
+                                                extents->bounded.height);
+    if (unlikely (tmp->status))
+       return tmp->status;
+
+    compositor->composite (tmp, CAIRO_OPERATOR_SOURCE, dst, NULL,
+                          extents->bounded.x,      extents->bounded.y,
+                          0, 0,
+                          0, 0,
+                          extents->bounded.width,  extents->bounded.height);
+
+    status = draw_func (compositor, tmp, draw_closure, op,
+                       pattern, &extents->source_sample_area,
+                       extents->bounded.x, extents->bounded.y,
+                       &extents->bounded, NULL);
+    if (unlikely (status))
+       goto cleanup;
+
+    clip = _cairo_clip_get_image (extents->clip, dst, &extents->bounded);
+    if (unlikely ((status = clip->status)))
+       goto cleanup;
+
+    if (dst->is_clear) {
+       compositor->composite (dst, CAIRO_OPERATOR_SOURCE, tmp, clip,
+                              0, 0,
+                              0, 0,
+                              extents->bounded.x,      extents->bounded.y,
+                              extents->bounded.width,  extents->bounded.height);
+    } else {
+       /* Punch the clip out of the destination */
+       compositor->composite (dst, CAIRO_OPERATOR_DEST_OUT, clip, NULL,
+                              0, 0,
+                              0, 0,
+                              extents->bounded.x,     extents->bounded.y,
+                              extents->bounded.width, extents->bounded.height);
+
+       /* Now add the two results together */
+       compositor->composite (dst, CAIRO_OPERATOR_ADD, tmp, clip,
+                              0, 0,
+                              0, 0,
+                              extents->bounded.x,     extents->bounded.y,
+                              extents->bounded.width, extents->bounded.height);
+    }
+    cairo_surface_destroy (clip);
+
+cleanup:
+    cairo_surface_destroy (tmp);
+    return status;
+}
+
+/* Handles compositing for %CAIRO_OPERATOR_SOURCE, which is special; it's
+ * defined as (src IN mask IN clip) ADD (dst OUT (mask IN clip))
+ */
+static cairo_status_t
+clip_and_composite_source (const cairo_mask_compositor_t       *compositor,
+                          void                         *draw_closure,
+                          draw_func_t                   draw_func,
+                          draw_func_t                   mask_func,
+                          cairo_pattern_t              *pattern,
+                          const cairo_composite_rectangles_t   *extents)
+{
+    cairo_surface_t *dst = extents->surface;
+    cairo_surface_t *mask, *src;
+    int src_x, src_y;
+
+    /* Create a surface that is mask IN clip */
+    mask = create_composite_mask (compositor, dst, draw_closure,
+                                 draw_func, mask_func,
+                                 extents);
+    if (unlikely (mask->status))
+       return mask->status;
+
+    src = compositor->pattern_to_surface (dst,
+                                         pattern,
+                                         FALSE,
+                                         &extents->bounded,
+                                         &extents->source_sample_area,
+                                         &src_x, &src_y);
+    if (unlikely (src->status)) {
+       cairo_surface_destroy (mask);
+       return src->status;
+    }
+
+    if (dst->is_clear) {
+       compositor->composite (dst, CAIRO_OPERATOR_SOURCE, src, mask,
+                              extents->bounded.x + src_x, extents->bounded.y + src_y,
+                              0, 0,
+                              extents->bounded.x,      extents->bounded.y,
+                              extents->bounded.width,  extents->bounded.height);
+    } else {
+       /* Compute dest' = dest OUT (mask IN clip) */
+       compositor->composite (dst, CAIRO_OPERATOR_DEST_OUT, mask, NULL,
+                              0, 0, 0, 0,
+                              extents->bounded.x,     extents->bounded.y,
+                              extents->bounded.width, extents->bounded.height);
+
+       /* Now compute (src IN (mask IN clip)) ADD dest' */
+       compositor->composite (dst, CAIRO_OPERATOR_ADD, src, mask,
+                              extents->bounded.x + src_x, extents->bounded.y + src_y,
+                              0, 0,
+                              extents->bounded.x,     extents->bounded.y,
+                              extents->bounded.width, extents->bounded.height);
+    }
+
+    cairo_surface_destroy (src);
+    cairo_surface_destroy (mask);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_bool_t
+can_reduce_alpha_op (cairo_operator_t op)
+{
+    int iop = op;
+    switch (iop) {
+    case CAIRO_OPERATOR_OVER:
+    case CAIRO_OPERATOR_SOURCE:
+    case CAIRO_OPERATOR_ADD:
+       return TRUE;
+    default:
+       return FALSE;
+    }
+}
+
+static cairo_bool_t
+reduce_alpha_op (cairo_surface_t *dst,
+                cairo_operator_t op,
+                const cairo_pattern_t *pattern)
+{
+    return dst->is_clear &&
+          dst->content == CAIRO_CONTENT_ALPHA &&
+          _cairo_pattern_is_opaque_solid (pattern) &&
+          can_reduce_alpha_op (op);
+}
+
+static cairo_status_t
+fixup_unbounded (const cairo_mask_compositor_t *compositor,
+                cairo_surface_t *dst,
+                const cairo_composite_rectangles_t *extents)
+{
+    cairo_rectangle_int_t rects[4];
+    int n;
+
+    if (extents->bounded.width  == extents->unbounded.width &&
+       extents->bounded.height == extents->unbounded.height)
+    {
+       return CAIRO_STATUS_SUCCESS;
+    }
+
+    n = 0;
+    if (extents->bounded.width == 0 || extents->bounded.height == 0) {
+       rects[n].x = extents->unbounded.x;
+       rects[n].width = extents->unbounded.width;
+       rects[n].y = extents->unbounded.y;
+       rects[n].height = extents->unbounded.height;
+       n++;
+    } else {
+       /* top */
+       if (extents->bounded.y != extents->unbounded.y) {
+           rects[n].x = extents->unbounded.x;
+           rects[n].width = extents->unbounded.width;
+           rects[n].y = extents->unbounded.y;
+           rects[n].height = extents->bounded.y - extents->unbounded.y;
+           n++;
+       }
+       /* left */
+       if (extents->bounded.x != extents->unbounded.x) {
+           rects[n].x = extents->unbounded.x;
+           rects[n].width = extents->bounded.x - extents->unbounded.x;
+           rects[n].y = extents->bounded.y;
+           rects[n].height = extents->bounded.height;
+           n++;
+       }
+       /* right */
+       if (extents->bounded.x + extents->bounded.width != extents->unbounded.x + extents->unbounded.width) {
+           rects[n].x = extents->bounded.x + extents->bounded.width;
+           rects[n].width = extents->unbounded.x + extents->unbounded.width - rects[n].x;
+           rects[n].y = extents->bounded.y;
+           rects[n].height = extents->bounded.height;
+           n++;
+       }
+       /* bottom */
+       if (extents->bounded.y + extents->bounded.height != extents->unbounded.y + extents->unbounded.height) {
+           rects[n].x = extents->unbounded.x;
+           rects[n].width = extents->unbounded.width;
+           rects[n].y = extents->bounded.y + extents->bounded.height;
+           rects[n].height = extents->unbounded.y + extents->unbounded.height - rects[n].y;
+           n++;
+       }
+    }
+
+    return compositor->fill_rectangles (dst, CAIRO_OPERATOR_CLEAR,
+                                       CAIRO_COLOR_TRANSPARENT,
+                                       rects, n);
+}
+
+static cairo_status_t
+fixup_unbounded_with_mask (const cairo_mask_compositor_t *compositor,
+                          cairo_surface_t *dst,
+                          const cairo_composite_rectangles_t *extents)
+{
+    cairo_clip_t *clip = extents->clip;
+    cairo_surface_t *mask;
+    int mask_x, mask_y;
+
+    mask = _cairo_clip_get_image (clip, dst, &extents->unbounded);
+    if (unlikely (mask->status))
+       return mask->status;
+
+    mask_x = -extents->unbounded.x;
+    mask_y = -extents->unbounded.y;
+
+    /* top */
+    if (extents->bounded.y != extents->unbounded.y) {
+       int x = extents->unbounded.x;
+       int y = extents->unbounded.y;
+       int width = extents->unbounded.width;
+       int height = extents->bounded.y - y;
+
+       compositor->composite (dst, CAIRO_OPERATOR_DEST_OUT, mask, NULL,
+                              x + mask_x, y + mask_y,
+                              0, 0,
+                              x, y,
+                              width, height);
+    }
+
+    /* left */
+    if (extents->bounded.x != extents->unbounded.x) {
+       int x = extents->unbounded.x;
+       int y = extents->bounded.y;
+       int width = extents->bounded.x - x;
+       int height = extents->bounded.height;
+
+       compositor->composite (dst, CAIRO_OPERATOR_DEST_OUT, mask, NULL,
+                              x + mask_x, y + mask_y,
+                              0, 0,
+                              x, y,
+                              width, height);
+    }
+
+    /* right */
+    if (extents->bounded.x + extents->bounded.width != extents->unbounded.x + extents->unbounded.width) {
+       int x = extents->bounded.x + extents->bounded.width;
+       int y = extents->bounded.y;
+       int width = extents->unbounded.x + extents->unbounded.width - x;
+       int height = extents->bounded.height;
+
+       compositor->composite (dst, CAIRO_OPERATOR_DEST_OUT, mask, NULL,
+                              x + mask_x, y + mask_y,
+                              0, 0,
+                              x, y,
+                              width, height);
+    }
+
+    /* bottom */
+    if (extents->bounded.y + extents->bounded.height != extents->unbounded.y + extents->unbounded.height) {
+       int x = extents->unbounded.x;
+       int y = extents->bounded.y + extents->bounded.height;
+       int width = extents->unbounded.width;
+       int height = extents->unbounded.y + extents->unbounded.height - y;
+
+       compositor->composite (dst, CAIRO_OPERATOR_DEST_OUT, mask, NULL,
+                              x + mask_x, y + mask_y,
+                              0, 0,
+                              x, y,
+                              width, height);
+    }
+
+    cairo_surface_destroy (mask);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+fixup_unbounded_boxes (const cairo_mask_compositor_t *compositor,
+                      const cairo_composite_rectangles_t *extents,
+                      cairo_boxes_t *boxes)
+{
+    cairo_surface_t *dst = extents->surface;
+    cairo_boxes_t clear;
+    cairo_region_t *clip_region;
+    cairo_box_t box;
+    cairo_status_t status;
+    struct _cairo_boxes_chunk *chunk;
+    int i;
+
+    assert (boxes->is_pixel_aligned);
+
+    clip_region = NULL;
+    if (_cairo_clip_is_region (extents->clip) &&
+       (clip_region = _cairo_clip_get_region (extents->clip)) &&
+       cairo_region_contains_rectangle (clip_region,
+                                        &extents->bounded) == CAIRO_REGION_OVERLAP_IN)
+       clip_region = NULL;
+
+
+    if (boxes->num_boxes <= 1 && clip_region == NULL)
+       return fixup_unbounded (compositor, dst, extents);
+
+    _cairo_boxes_init (&clear);
+
+    box.p1.x = _cairo_fixed_from_int (extents->unbounded.x + extents->unbounded.width);
+    box.p1.y = _cairo_fixed_from_int (extents->unbounded.y);
+    box.p2.x = _cairo_fixed_from_int (extents->unbounded.x);
+    box.p2.y = _cairo_fixed_from_int (extents->unbounded.y + extents->unbounded.height);
+
+    if (clip_region == NULL) {
+       cairo_boxes_t tmp;
+
+       _cairo_boxes_init (&tmp);
+
+       status = _cairo_boxes_add (&tmp, CAIRO_ANTIALIAS_DEFAULT, &box);
+       assert (status == CAIRO_STATUS_SUCCESS);
+
+       tmp.chunks.next = &boxes->chunks;
+       tmp.num_boxes += boxes->num_boxes;
+
+       status = _cairo_bentley_ottmann_tessellate_boxes (&tmp,
+                                                         CAIRO_FILL_RULE_WINDING,
+                                                         &clear);
+
+       tmp.chunks.next = NULL;
+    } else {
+       pixman_box32_t *pbox;
+
+       pbox = pixman_region32_rectangles (&clip_region->rgn, &i);
+       _cairo_boxes_limit (&clear, (cairo_box_t *) pbox, i);
+
+       status = _cairo_boxes_add (&clear, CAIRO_ANTIALIAS_DEFAULT, &box);
+       assert (status == CAIRO_STATUS_SUCCESS);
+
+       for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
+           for (i = 0; i < chunk->count; i++) {
+               status = _cairo_boxes_add (&clear,
+                                          CAIRO_ANTIALIAS_DEFAULT,
+                                          &chunk->base[i]);
+               if (unlikely (status)) {
+                   _cairo_boxes_fini (&clear);
+                   return status;
+               }
+           }
+       }
+
+       status = _cairo_bentley_ottmann_tessellate_boxes (&clear,
+                                                         CAIRO_FILL_RULE_WINDING,
+                                                         &clear);
+    }
+
+    if (likely (status == CAIRO_STATUS_SUCCESS)) {
+       status = compositor->fill_boxes (dst,
+                                        CAIRO_OPERATOR_CLEAR,
+                                        CAIRO_COLOR_TRANSPARENT,
+                                        &clear);
+    }
+
+    _cairo_boxes_fini (&clear);
+
+    return status;
+}
+
+enum {
+    NEED_CLIP_REGION = 0x1,
+    NEED_CLIP_SURFACE = 0x2,
+    FORCE_CLIP_REGION = 0x4,
+};
+
+static cairo_bool_t
+need_bounded_clip (cairo_composite_rectangles_t *extents)
+{
+    unsigned int flags = NEED_CLIP_REGION;
+    if (! _cairo_clip_is_region (extents->clip))
+       flags |= NEED_CLIP_SURFACE;
+    return flags;
+}
+
+static cairo_bool_t
+need_unbounded_clip (cairo_composite_rectangles_t *extents)
+{
+    unsigned int flags = 0;
+    if (! extents->is_bounded) {
+       flags |= NEED_CLIP_REGION;
+       if (! _cairo_clip_is_region (extents->clip))
+           flags |= NEED_CLIP_SURFACE;
+    }
+    if (extents->clip->path != NULL)
+       flags |= NEED_CLIP_SURFACE;
+    return flags;
+}
+
+static cairo_status_t
+clip_and_composite (const cairo_mask_compositor_t *compositor,
+                   draw_func_t                  draw_func,
+                   draw_func_t                  mask_func,
+                   void                        *draw_closure,
+                   cairo_composite_rectangles_t*extents,
+                   unsigned int need_clip)
+{
+    cairo_surface_t *dst = extents->surface;
+    cairo_operator_t op = extents->op;
+    cairo_pattern_t *src = &extents->source_pattern.base;
+    cairo_region_t *clip_region = NULL;
+    cairo_status_t status;
+
+    compositor->acquire (dst);
+
+    if (need_clip & NEED_CLIP_REGION) {
+       clip_region = _cairo_clip_get_region (extents->clip);
+       if ((need_clip & FORCE_CLIP_REGION) == 0 &&
+           _cairo_composite_rectangles_can_reduce_clip (extents,
+                                                        extents->clip))
+           clip_region = NULL;
+       if (clip_region != NULL) {
+           status = compositor->set_clip_region (dst, clip_region);
+           if (unlikely (status)) {
+               compositor->release (dst);
+               return status;
+           }
+       }
+    }
+
+    if (reduce_alpha_op (dst, op, &extents->source_pattern.base)) {
+       op = CAIRO_OPERATOR_ADD;
+       src = NULL;
+    }
+
+    if (op == CAIRO_OPERATOR_SOURCE) {
+       status = clip_and_composite_source (compositor,
+                                           draw_closure, draw_func, mask_func,
+                                           src, extents);
+    } else {
+       if (op == CAIRO_OPERATOR_CLEAR) {
+           op = CAIRO_OPERATOR_DEST_OUT;
+           src = NULL;
+       }
+
+       if (need_clip & NEED_CLIP_SURFACE) {
+           if (extents->is_bounded) {
+               status = clip_and_composite_with_mask (compositor,
+                                                      draw_closure,
+                                                      draw_func,
+                                                      mask_func,
+                                                      op, src, extents);
+           } else {
+               status = clip_and_composite_combine (compositor,
+                                                    draw_closure,
+                                                    draw_func,
+                                                    op, src, extents);
+           }
+       } else {
+           status = draw_func (compositor,
+                               dst, draw_closure,
+                               op, src, &extents->source_sample_area,
+                               0, 0,
+                               &extents->bounded,
+                               extents->clip);
+       }
+    }
+
+    if (status == CAIRO_STATUS_SUCCESS && ! extents->is_bounded) {
+       if (need_clip & NEED_CLIP_SURFACE)
+           status = fixup_unbounded_with_mask (compositor, dst, extents);
+       else
+           status = fixup_unbounded (compositor, dst, extents);
+    }
+
+    if (clip_region)
+       compositor->set_clip_region (dst, NULL);
+
+    compositor->release (dst);
+
+    return status;
+}
+
+static cairo_int_status_t
+trim_extents_to_boxes (cairo_composite_rectangles_t *extents,
+                      cairo_boxes_t *boxes)
+{
+    cairo_box_t box;
+
+    _cairo_boxes_extents (boxes, &box);
+    return _cairo_composite_rectangles_intersect_mask_extents (extents, &box);
+}
+
+static cairo_status_t
+upload_boxes (const cairo_mask_compositor_t *compositor,
+             cairo_composite_rectangles_t *extents,
+             cairo_boxes_t *boxes)
+{
+    cairo_surface_t *dst = extents->surface;
+    const cairo_pattern_t *source = &extents->source_pattern.base;
+    const cairo_surface_pattern_t *pattern;
+    cairo_surface_t *src;
+    cairo_rectangle_int_t limit;
+    cairo_int_status_t status;
+    int tx, ty;
+
+    pattern = (const cairo_surface_pattern_t *) source;
+    src = pattern->surface;
+    if (!(src->type == CAIRO_SURFACE_TYPE_IMAGE || src->type == dst->type))
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    if (! _cairo_matrix_is_integer_translation (&source->matrix, &tx, &ty))
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    if (_cairo_surface_is_snapshot (src))
+       src = _cairo_surface_snapshot_get_target (src);
+    if (_cairo_surface_is_observer (src))
+       src = _cairo_surface_observer_get_target (src);
+    if (_cairo_surface_is_subsurface (src)) {
+       _cairo_surface_subsurface_offset (src, &tx, &ty);
+       src = _cairo_surface_subsurface_get_target (src);
+    }
+
+    /* Check that the data is entirely within the image */
+    if (extents->bounded.x + tx < 0 || extents->bounded.y + ty < 0)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    _cairo_surface_get_extents (pattern->surface, &limit);
+    if (extents->bounded.x + extents->bounded.width  + tx > limit.width ||
+       extents->bounded.y + extents->bounded.height + ty > limit.height)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    if (pattern->surface->type == CAIRO_SURFACE_TYPE_IMAGE)
+       status = compositor->draw_image_boxes (dst,
+                                              (cairo_image_surface_t *)src,
+                                              boxes, tx, ty);
+    else
+       status = compositor->copy_boxes (dst, src, boxes, &extents->bounded,
+                                        tx, ty);
+
+    return status;
+}
+
+static cairo_status_t
+composite_boxes (const cairo_mask_compositor_t *compositor,
+                const cairo_composite_rectangles_t *extents,
+                cairo_boxes_t *boxes)
+{
+    cairo_surface_t *dst = extents->surface;
+    cairo_operator_t op = extents->op;
+    const cairo_pattern_t *source = &extents->source_pattern.base;
+    cairo_bool_t need_clip_mask = extents->clip->path != NULL;
+    cairo_status_t status;
+
+    if (need_clip_mask &&
+       (! extents->is_bounded || op == CAIRO_OPERATOR_SOURCE))
+    {
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+    }
+
+    status = compositor->acquire (dst);
+    if (unlikely (status))
+       return status;
+
+    if (! need_clip_mask && source->type == CAIRO_PATTERN_TYPE_SOLID) {
+       const cairo_color_t *color;
+
+       color = &((cairo_solid_pattern_t *) source)->color;
+       status = compositor->fill_boxes (dst, op, color, boxes);
+    } else {
+       cairo_surface_t *src, *mask = NULL;
+       int src_x, src_y;
+       int mask_x = 0, mask_y = 0;
+
+       if (need_clip_mask) {
+           mask = _cairo_clip_get_image (extents->clip, dst,
+                                         &extents->bounded);
+           if (unlikely (mask->status))
+               return mask->status;
+
+           if (op == CAIRO_OPERATOR_CLEAR) {
+               source = NULL;
+               op = CAIRO_OPERATOR_DEST_OUT;
+           }
+
+           mask_x = -extents->bounded.x;
+           mask_y = -extents->bounded.y;
+       }
+
+       if (source || mask == NULL) {
+           src = compositor->pattern_to_surface (dst, source, FALSE,
+                                                 &extents->bounded,
+                                                 &extents->source_sample_area,
+                                                 &src_x, &src_y);
+       } else {
+           src = mask;
+           src_x = mask_x;
+           src_y = mask_y;
+           mask = NULL;
+       }
+
+       status = compositor->composite_boxes (dst, op, src, mask,
+                                             src_x, src_y,
+                                             mask_x, mask_y,
+                                             0, 0,
+                                             boxes, &extents->bounded);
+
+       cairo_surface_destroy (src);
+       cairo_surface_destroy (mask);
+    }
+
+    if (status == CAIRO_STATUS_SUCCESS && ! extents->is_bounded)
+       status = fixup_unbounded_boxes (compositor, extents, boxes);
+
+    compositor->release (dst);
+
+    return status;
+}
+
+static cairo_status_t
+clip_and_composite_boxes (const cairo_mask_compositor_t *compositor,
+                         cairo_composite_rectangles_t *extents,
+                         cairo_boxes_t *boxes)
+{
+    cairo_surface_t *dst = extents->surface;
+    cairo_int_status_t status;
+
+    if (boxes->num_boxes == 0) {
+       if (extents->is_bounded)
+           return CAIRO_STATUS_SUCCESS;
+
+       return fixup_unbounded_boxes (compositor, extents, boxes);
+    }
+
+    if (! boxes->is_pixel_aligned)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    status = trim_extents_to_boxes (extents, boxes);
+    if (unlikely (status))
+       return status;
+
+    if (extents->source_pattern.base.type == CAIRO_PATTERN_TYPE_SURFACE &&
+       extents->clip->path == NULL &&
+       (extents->op == CAIRO_OPERATOR_SOURCE ||
+        (dst->is_clear && (extents->op == CAIRO_OPERATOR_OVER ||
+                           extents->op == CAIRO_OPERATOR_ADD))))
+    {
+       status = upload_boxes (compositor, extents, boxes);
+       if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+           return status;
+    }
+
+    return composite_boxes (compositor, extents, boxes);
+}
+
+/* high-level compositor interface */
+
+static cairo_int_status_t
+_cairo_mask_compositor_paint (const cairo_compositor_t *_compositor,
+                             cairo_composite_rectangles_t *extents)
+{
+    cairo_mask_compositor_t *compositor = (cairo_mask_compositor_t*)_compositor;
+    cairo_boxes_t boxes;
+    cairo_int_status_t status;
+
+    _cairo_clip_steal_boxes (extents->clip, &boxes);
+    status = clip_and_composite_boxes (compositor, extents, &boxes);
+    _cairo_clip_unsteal_boxes (extents->clip, &boxes);
+
+    return status;
+}
+
+struct composite_opacity_info {
+    const cairo_mask_compositor_t *compositor;
+    uint8_t op;
+    cairo_surface_t *dst;
+    cairo_surface_t *src;
+    int src_x, src_y;
+    double opacity;
+};
+
+static void composite_opacity(void *closure,
+                             int16_t x, int16_t y,
+                             int16_t w, int16_t h,
+                             uint16_t coverage)
+{
+    struct composite_opacity_info *info = closure;
+    const cairo_mask_compositor_t *compositor = info->compositor;
+    cairo_surface_t *mask;
+    int mask_x, mask_y;
+    cairo_color_t color;
+    cairo_solid_pattern_t solid;
+
+    _cairo_color_init_rgba (&color, 0, 0, 0, info->opacity * coverage);
+    _cairo_pattern_init_solid (&solid, &color);
+    mask = compositor->pattern_to_surface (info->dst, &solid.base, TRUE,
+                                          &_cairo_unbounded_rectangle,
+                                          &_cairo_unbounded_rectangle,
+                                          &mask_x, &mask_y);
+    if (likely (mask->status == CAIRO_STATUS_SUCCESS)) {
+       if (info->src) {
+           compositor->composite (info->dst, info->op, info->src, mask,
+                                  x + info->src_x,  y + info->src_y,
+                                  mask_x,           mask_y,
+                                  x,                y,
+                                  w,                h);
+       } else {
+           compositor->composite (info->dst, info->op, mask, NULL,
+                                  mask_x,            mask_y,
+                                  0,                 0,
+                                  x,                 y,
+                                  w,                 h);
+       }
+    }
+
+    cairo_surface_destroy (mask);
+}
+
+static cairo_int_status_t
+composite_opacity_boxes (const cairo_mask_compositor_t *compositor,
+                        cairo_surface_t                *dst,
+                        void                           *closure,
+                        cairo_operator_t                op,
+                        const cairo_pattern_t          *src_pattern,
+                        const cairo_rectangle_int_t    *src_sample,
+                        int                             dst_x,
+                        int                             dst_y,
+                        const cairo_rectangle_int_t    *extents,
+                        cairo_clip_t                   *clip)
+{
+    const cairo_solid_pattern_t *mask_pattern = closure;
+    struct composite_opacity_info info;
+    int i;
+
+    assert (clip);
+
+    info.compositor = compositor;
+    info.op = op;
+    info.dst = dst;
+
+    if (src_pattern != NULL) {
+       info.src = compositor->pattern_to_surface (dst, src_pattern, FALSE,
+                                                  extents, src_sample,
+                                                  &info.src_x, &info.src_y);
+       if (unlikely (info.src->status))
+           return info.src->status;
+    } else
+       info.src = NULL;
+
+    info.opacity = mask_pattern->color.alpha / (double) 0xffff;
+
+    /* XXX for lots of boxes create a clip region for the fully opaque areas */
+    for (i = 0; i < clip->num_boxes; i++)
+       do_unaligned_box(composite_opacity, &info,
+                        &clip->boxes[i], dst_x, dst_y);
+    cairo_surface_destroy (info.src);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+struct composite_box_info {
+    const cairo_mask_compositor_t *compositor;
+    cairo_surface_t *dst;
+    cairo_surface_t *src;
+    int src_x, src_y;
+    uint8_t op;
+};
+
+static void composite_box(void *closure,
+                         int16_t x, int16_t y,
+                         int16_t w, int16_t h,
+                         uint16_t coverage)
+{
+    struct composite_box_info *info = closure;
+    const cairo_mask_compositor_t *compositor = info->compositor;
+
+    if (! CAIRO_ALPHA_SHORT_IS_OPAQUE (coverage)) {
+       cairo_surface_t *mask;
+       cairo_color_t color;
+       cairo_solid_pattern_t solid;
+       int mask_x, mask_y;
+
+       _cairo_color_init_rgba (&color, 0, 0, 0, coverage / (double)0xffff);
+       _cairo_pattern_init_solid (&solid, &color);
+
+       mask = compositor->pattern_to_surface (info->dst, &solid.base, FALSE,
+                                              &_cairo_unbounded_rectangle,
+                                              &_cairo_unbounded_rectangle,
+                                              &mask_x, &mask_y);
+
+       if (likely (mask->status == CAIRO_STATUS_SUCCESS)) {
+           compositor->composite (info->dst, info->op, info->src, mask,
+                                  x + info->src_x,  y + info->src_y,
+                                  mask_x,           mask_y,
+                                  x,                y,
+                                  w,                h);
+       }
+
+       cairo_surface_destroy (mask);
+    } else {
+       compositor->composite (info->dst, info->op, info->src, NULL,
+                              x + info->src_x,  y + info->src_y,
+                              0,                0,
+                              x,                y,
+                              w,                h);
+    }
+}
+
+static cairo_int_status_t
+composite_mask_clip_boxes (const cairo_mask_compositor_t *compositor,
+                          cairo_surface_t              *dst,
+                          void                         *closure,
+                          cairo_operator_t              op,
+                          const cairo_pattern_t        *src_pattern,
+                          const cairo_rectangle_int_t  *src_sample,
+                          int                           dst_x,
+                          int                           dst_y,
+                          const cairo_rectangle_int_t  *extents,
+                          cairo_clip_t                 *clip)
+{
+    cairo_composite_rectangles_t *composite = closure;
+    struct composite_box_info info;
+    int i;
+
+    assert (src_pattern == NULL);
+    assert (op == CAIRO_OPERATOR_SOURCE);
+
+    info.compositor = compositor;
+    info.op = CAIRO_OPERATOR_SOURCE;
+    info.dst = dst;
+    info.src = compositor->pattern_to_surface (dst,
+                                              &composite->mask_pattern.base,
+                                              FALSE, extents,
+                                              &composite->mask_sample_area,
+                                              &info.src_x, &info.src_y);
+    if (unlikely (info.src->status))
+       return info.src->status;
+
+    info.src_x += dst_x;
+    info.src_y += dst_y;
+
+    for (i = 0; i < clip->num_boxes; i++)
+       do_unaligned_box(composite_box, &info, &clip->boxes[i], dst_x, dst_y);
+
+    cairo_surface_destroy (info.src);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+composite_mask (const cairo_mask_compositor_t *compositor,
+               cairo_surface_t                 *dst,
+               void                            *closure,
+               cairo_operator_t                 op,
+               const cairo_pattern_t           *src_pattern,
+               const cairo_rectangle_int_t     *src_sample,
+               int                              dst_x,
+               int                              dst_y,
+               const cairo_rectangle_int_t     *extents,
+               cairo_clip_t                    *clip)
+{
+    cairo_composite_rectangles_t *composite = closure;
+    cairo_surface_t *src, *mask;
+    int src_x, src_y;
+    int mask_x, mask_y;
+
+    if (src_pattern != NULL) {
+       src = compositor->pattern_to_surface (dst, src_pattern, FALSE,
+                                             extents, src_sample,
+                                             &src_x, &src_y);
+       if (unlikely (src->status))
+           return src->status;
+
+       mask = compositor->pattern_to_surface (dst, &composite->mask_pattern.base, TRUE,
+                                              extents, &composite->mask_sample_area,
+                                              &mask_x, &mask_y);
+       if (unlikely (mask->status)) {
+           cairo_surface_destroy (src);
+           return mask->status;
+       }
+
+       compositor->composite (dst, op, src, mask,
+                              extents->x + src_x,  extents->y + src_y,
+                              extents->x + mask_x, extents->y + mask_y,
+                              extents->x - dst_x,  extents->y - dst_y,
+                              extents->width,      extents->height);
+
+       cairo_surface_destroy (mask);
+       cairo_surface_destroy (src);
+    } else {
+       src = compositor->pattern_to_surface (dst, &composite->mask_pattern.base, FALSE,
+                                             extents, &composite->mask_sample_area,
+                                             &src_x, &src_y);
+       if (unlikely (src->status))
+           return src->status;
+
+       compositor->composite (dst, op, src, NULL,
+                              extents->x + src_x,  extents->y + src_y,
+                              0, 0,
+                              extents->x - dst_x,  extents->y - dst_y,
+                              extents->width,      extents->height);
+
+       cairo_surface_destroy (src);
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_mask_compositor_mask (const cairo_compositor_t *_compositor,
+                            cairo_composite_rectangles_t *extents)
+{
+    const cairo_mask_compositor_t *compositor = (cairo_mask_compositor_t*)_compositor;
+    cairo_int_status_t status = CAIRO_INT_STATUS_UNSUPPORTED;
+
+    if (extents->mask_pattern.base.type == CAIRO_PATTERN_TYPE_SOLID &&
+       extents->clip->path == NULL &&
+       ! _cairo_clip_is_region (extents->clip)) {
+       status = clip_and_composite (compositor,
+                                    composite_opacity_boxes,
+                                    composite_opacity_boxes,
+                                    &extents->mask_pattern.solid,
+                                    extents, need_unbounded_clip (extents));
+    } else {
+       status = clip_and_composite (compositor,
+                                    composite_mask,
+                                    extents->clip->path == NULL ? composite_mask_clip_boxes : NULL,
+                                    extents,
+                                    extents, need_bounded_clip (extents));
+    }
+
+    return status;
+}
+
+static cairo_int_status_t
+_cairo_mask_compositor_stroke (const cairo_compositor_t *_compositor,
+                              cairo_composite_rectangles_t *extents,
+                              const cairo_path_fixed_t *path,
+                              const cairo_stroke_style_t       *style,
+                              const cairo_matrix_t     *ctm,
+                              const cairo_matrix_t     *ctm_inverse,
+                              double            tolerance,
+                              cairo_antialias_t         antialias)
+{
+    const cairo_mask_compositor_t *compositor = (cairo_mask_compositor_t*)_compositor;
+    cairo_surface_t *mask;
+    cairo_surface_pattern_t pattern;
+    cairo_int_status_t status;
+
+    status = CAIRO_INT_STATUS_UNSUPPORTED;
+    if (_cairo_path_fixed_stroke_is_rectilinear (path)) {
+       cairo_boxes_t boxes;
+
+       _cairo_boxes_init_with_clip (&boxes, extents->clip);
+       status = _cairo_path_fixed_stroke_rectilinear_to_boxes (path,
+                                                               style,
+                                                               ctm,
+                                                               antialias,
+                                                               &boxes);
+       if (likely (status == CAIRO_INT_STATUS_SUCCESS))
+           status = clip_and_composite_boxes (compositor, extents, &boxes);
+       _cairo_boxes_fini (&boxes);
+    }
+
+
+    if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
+       mask = cairo_surface_create_similar_image (extents->surface,
+                                                  CAIRO_FORMAT_A8,
+                                                  extents->bounded.width,
+                                                  extents->bounded.height);
+       if (unlikely (mask->status))
+           return mask->status;
+
+       status = _cairo_surface_offset_stroke (mask,
+                                              extents->bounded.x,
+                                              extents->bounded.y,
+                                              CAIRO_OPERATOR_ADD,
+                                              &_cairo_pattern_white.base,
+                                              path, style, ctm, ctm_inverse,
+                                              tolerance, antialias,
+                                              extents->clip);
+       if (unlikely (status)) {
+           cairo_surface_destroy (mask);
+           return status;
+       }
+
+       _cairo_pattern_init_for_surface (&pattern, mask);
+       cairo_surface_destroy (mask);
+
+       cairo_matrix_init_translate (&pattern.base.matrix,
+                                    -extents->bounded.x,
+                                    -extents->bounded.y);
+       pattern.base.filter = CAIRO_FILTER_NEAREST;
+       pattern.base.extend = CAIRO_EXTEND_NONE;
+       status = _cairo_surface_mask (extents->surface,
+                                     extents->op,
+                                     &extents->source_pattern.base,
+                                     &pattern.base,
+                                     extents->clip);
+       _cairo_pattern_fini (&pattern.base);
+    }
+
+    return status;
+}
+
+static cairo_int_status_t
+_cairo_mask_compositor_fill (const cairo_compositor_t *_compositor,
+                            cairo_composite_rectangles_t *extents,
+                            const cairo_path_fixed_t   *path,
+                            cairo_fill_rule_t   fill_rule,
+                            double                      tolerance,
+                            cairo_antialias_t   antialias)
+{
+    const cairo_mask_compositor_t *compositor = (cairo_mask_compositor_t*)_compositor;
+    cairo_surface_t *mask;
+    cairo_surface_pattern_t pattern;
+    cairo_int_status_t status;
+
+    status = CAIRO_INT_STATUS_UNSUPPORTED;
+    if (_cairo_path_fixed_fill_is_rectilinear (path)) {
+       cairo_boxes_t boxes;
+
+       _cairo_boxes_init_with_clip (&boxes, extents->clip);
+       status = _cairo_path_fixed_fill_rectilinear_to_boxes (path,
+                                                             fill_rule,
+                                                             antialias,
+                                                             &boxes);
+       if (likely (status == CAIRO_INT_STATUS_SUCCESS))
+           status = clip_and_composite_boxes (compositor, extents, &boxes);
+       _cairo_boxes_fini (&boxes);
+    }
+
+    if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
+       mask = cairo_surface_create_similar_image (extents->surface,
+                                                  CAIRO_FORMAT_A8,
+                                                  extents->bounded.width,
+                                                  extents->bounded.height);
+       if (unlikely (mask->status))
+           return mask->status;
+
+       status = _cairo_surface_offset_fill (mask,
+                                            extents->bounded.x,
+                                            extents->bounded.y,
+                                            CAIRO_OPERATOR_ADD,
+                                            &_cairo_pattern_white.base,
+                                            path, fill_rule, tolerance, antialias,
+                                            extents->clip);
+       if (unlikely (status)) {
+           cairo_surface_destroy (mask);
+           return status;
+       }
+
+       _cairo_pattern_init_for_surface (&pattern, mask);
+       cairo_surface_destroy (mask);
+
+       cairo_matrix_init_translate (&pattern.base.matrix,
+                                    -extents->bounded.x,
+                                    -extents->bounded.y);
+       pattern.base.filter = CAIRO_FILTER_NEAREST;
+       pattern.base.extend = CAIRO_EXTEND_NONE;
+       status = _cairo_surface_mask (extents->surface,
+                                     extents->op,
+                                     &extents->source_pattern.base,
+                                     &pattern.base,
+                                     extents->clip);
+       _cairo_pattern_fini (&pattern.base);
+    }
+
+    return status;
+}
+
+static cairo_int_status_t
+_cairo_mask_compositor_glyphs (const cairo_compositor_t *_compositor,
+                              cairo_composite_rectangles_t *extents,
+                              cairo_scaled_font_t      *scaled_font,
+                              cairo_glyph_t            *glyphs,
+                              int                       num_glyphs,
+                              cairo_bool_t              overlap)
+{
+    cairo_surface_t *mask;
+    cairo_surface_pattern_t pattern;
+    cairo_int_status_t status;
+
+    mask = cairo_surface_create_similar_image (extents->surface,
+                                              CAIRO_FORMAT_A8,
+                                              extents->bounded.width,
+                                              extents->bounded.height);
+    if (unlikely (mask->status))
+       return mask->status;
+
+    status = _cairo_surface_offset_glyphs (mask,
+                                          extents->bounded.x,
+                                          extents->bounded.y,
+                                          CAIRO_OPERATOR_ADD,
+                                          &_cairo_pattern_white.base,
+                                          scaled_font, glyphs, num_glyphs,
+                                          extents->clip);
+    if (unlikely (status)) {
+       cairo_surface_destroy (mask);
+       return status;
+    }
+
+    _cairo_pattern_init_for_surface (&pattern, mask);
+    cairo_surface_destroy (mask);
+
+    cairo_matrix_init_translate (&pattern.base.matrix,
+                                -extents->bounded.x,
+                                -extents->bounded.y);
+    pattern.base.filter = CAIRO_FILTER_NEAREST;
+    pattern.base.extend = CAIRO_EXTEND_NONE;
+    status = _cairo_surface_mask (extents->surface,
+                                 extents->op,
+                                 &extents->source_pattern.base,
+                                 &pattern.base,
+                                 extents->clip);
+    _cairo_pattern_fini (&pattern.base);
+
+    return status;
+}
+
+void
+_cairo_mask_compositor_init (cairo_mask_compositor_t *compositor,
+                            const cairo_compositor_t *delegate)
+{
+    compositor->base.delegate = delegate;
+
+    compositor->base.paint = _cairo_mask_compositor_paint;
+    compositor->base.mask  = _cairo_mask_compositor_mask;
+    compositor->base.fill  = _cairo_mask_compositor_fill;
+    compositor->base.stroke = _cairo_mask_compositor_stroke;
+    compositor->base.glyphs = _cairo_mask_compositor_glyphs;
+}
diff --git a/src/cairo-matrix.c b/src/cairo-matrix.c
new file mode 100644 (file)
index 0000000..e5e5888
--- /dev/null
@@ -0,0 +1,1192 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *     Carl D. Worth <cworth@cworth.org>
+ */
+
+#include "cairoint.h"
+#include "cairo-error-private.h"
+#include <float.h>
+
+#define PIXMAN_MAX_INT ((pixman_fixed_1 >> 1) - pixman_fixed_e) /* need to ensure deltas also fit */
+
+#if _XOPEN_SOURCE >= 600 || defined (_ISOC99_SOURCE)
+#define ISFINITE(x) isfinite (x)
+#else
+#define ISFINITE(x) ((x) * (x) >= 0.) /* check for NaNs */
+#endif
+
+/**
+ * SECTION:cairo-matrix
+ * @Title: cairo_matrix_t
+ * @Short_Description: Generic matrix operations
+ * @See_Also: #cairo_t
+ *
+ * #cairo_matrix_t is used throughout cairo to convert between different
+ * coordinate spaces.  A #cairo_matrix_t holds an affine transformation,
+ * such as a scale, rotation, shear, or a combination of these.
+ * The transformation of a point (<literal>x</literal>,<literal>y</literal>)
+ * is given by:
+ *
+ * <programlisting>
+ * x_new = xx * x + xy * y + x0;
+ * y_new = yx * x + yy * y + y0;
+ * </programlisting>
+ *
+ * The current transformation matrix of a #cairo_t, represented as a
+ * #cairo_matrix_t, defines the transformation from user-space
+ * coordinates to device-space coordinates. See cairo_get_matrix() and
+ * cairo_set_matrix().
+ */
+
+static void
+_cairo_matrix_scalar_multiply (cairo_matrix_t *matrix, double scalar);
+
+static void
+_cairo_matrix_compute_adjoint (cairo_matrix_t *matrix);
+
+/**
+ * cairo_matrix_init_identity:
+ * @matrix: a #cairo_matrix_t
+ *
+ * Modifies @matrix to be an identity transformation.
+ **/
+void
+cairo_matrix_init_identity (cairo_matrix_t *matrix)
+{
+    cairo_matrix_init (matrix,
+                      1, 0,
+                      0, 1,
+                      0, 0);
+}
+slim_hidden_def(cairo_matrix_init_identity);
+
+/**
+ * cairo_matrix_init:
+ * @matrix: a #cairo_matrix_t
+ * @xx: xx component of the affine transformation
+ * @yx: yx component of the affine transformation
+ * @xy: xy component of the affine transformation
+ * @yy: yy component of the affine transformation
+ * @x0: X translation component of the affine transformation
+ * @y0: Y translation component of the affine transformation
+ *
+ * Sets @matrix to be the affine transformation given by
+ * @xx, @yx, @xy, @yy, @x0, @y0. The transformation is given
+ * by:
+ * <programlisting>
+ *  x_new = xx * x + xy * y + x0;
+ *  y_new = yx * x + yy * y + y0;
+ * </programlisting>
+ **/
+void
+cairo_matrix_init (cairo_matrix_t *matrix,
+                  double xx, double yx,
+
+                  double xy, double yy,
+                  double x0, double y0)
+{
+    matrix->xx = xx; matrix->yx = yx;
+    matrix->xy = xy; matrix->yy = yy;
+    matrix->x0 = x0; matrix->y0 = y0;
+}
+slim_hidden_def(cairo_matrix_init);
+
+/**
+ * _cairo_matrix_get_affine:
+ * @matrix: a #cairo_matrix_t
+ * @xx: location to store xx component of matrix
+ * @yx: location to store yx component of matrix
+ * @xy: location to store xy component of matrix
+ * @yy: location to store yy component of matrix
+ * @x0: location to store x0 (X-translation component) of matrix, or %NULL
+ * @y0: location to store y0 (Y-translation component) of matrix, or %NULL
+ *
+ * Gets the matrix values for the affine transformation that @matrix represents.
+ * See cairo_matrix_init().
+ *
+ *
+ * This function is a leftover from the old public API, but is still
+ * mildly useful as an internal means for getting at the matrix
+ * members in a positional way. For example, when reassigning to some
+ * external matrix type, or when renaming members to more meaningful
+ * names (such as a,b,c,d,e,f) for particular manipulations.
+ **/
+void
+_cairo_matrix_get_affine (const cairo_matrix_t *matrix,
+                         double *xx, double *yx,
+                         double *xy, double *yy,
+                         double *x0, double *y0)
+{
+    *xx  = matrix->xx;
+    *yx  = matrix->yx;
+
+    *xy  = matrix->xy;
+    *yy  = matrix->yy;
+
+    if (x0)
+       *x0 = matrix->x0;
+    if (y0)
+       *y0 = matrix->y0;
+}
+
+/**
+ * cairo_matrix_init_translate:
+ * @matrix: a #cairo_matrix_t
+ * @tx: amount to translate in the X direction
+ * @ty: amount to translate in the Y direction
+ *
+ * Initializes @matrix to a transformation that translates by @tx and
+ * @ty in the X and Y dimensions, respectively.
+ **/
+void
+cairo_matrix_init_translate (cairo_matrix_t *matrix,
+                            double tx, double ty)
+{
+    cairo_matrix_init (matrix,
+                      1, 0,
+                      0, 1,
+                      tx, ty);
+}
+slim_hidden_def(cairo_matrix_init_translate);
+
+/**
+ * cairo_matrix_translate:
+ * @matrix: a #cairo_matrix_t
+ * @tx: amount to translate in the X direction
+ * @ty: amount to translate in the Y direction
+ *
+ * Applies a translation by @tx, @ty to the transformation in
+ * @matrix. The effect of the new transformation is to first translate
+ * the coordinates by @tx and @ty, then apply the original transformation
+ * to the coordinates.
+ **/
+void
+cairo_matrix_translate (cairo_matrix_t *matrix, double tx, double ty)
+{
+    cairo_matrix_t tmp;
+
+    cairo_matrix_init_translate (&tmp, tx, ty);
+
+    cairo_matrix_multiply (matrix, &tmp, matrix);
+}
+slim_hidden_def (cairo_matrix_translate);
+
+/**
+ * cairo_matrix_init_scale:
+ * @matrix: a #cairo_matrix_t
+ * @sx: scale factor in the X direction
+ * @sy: scale factor in the Y direction
+ *
+ * Initializes @matrix to a transformation that scales by @sx and @sy
+ * in the X and Y dimensions, respectively.
+ **/
+void
+cairo_matrix_init_scale (cairo_matrix_t *matrix,
+                        double sx, double sy)
+{
+    cairo_matrix_init (matrix,
+                      sx,  0,
+                      0, sy,
+                      0, 0);
+}
+slim_hidden_def(cairo_matrix_init_scale);
+
+/**
+ * cairo_matrix_scale:
+ * @matrix: a #cairo_matrix_t
+ * @sx: scale factor in the X direction
+ * @sy: scale factor in the Y direction
+ *
+ * Applies scaling by @sx, @sy to the transformation in @matrix. The
+ * effect of the new transformation is to first scale the coordinates
+ * by @sx and @sy, then apply the original transformation to the coordinates.
+ **/
+void
+cairo_matrix_scale (cairo_matrix_t *matrix, double sx, double sy)
+{
+    cairo_matrix_t tmp;
+
+    cairo_matrix_init_scale (&tmp, sx, sy);
+
+    cairo_matrix_multiply (matrix, &tmp, matrix);
+}
+slim_hidden_def(cairo_matrix_scale);
+
+/**
+ * cairo_matrix_init_rotate:
+ * @matrix: a #cairo_matrix_t
+ * @radians: angle of rotation, in radians. The direction of rotation
+ * is defined such that positive angles rotate in the direction from
+ * the positive X axis toward the positive Y axis. With the default
+ * axis orientation of cairo, positive angles rotate in a clockwise
+ * direction.
+ *
+ * Initialized @matrix to a transformation that rotates by @radians.
+ **/
+void
+cairo_matrix_init_rotate (cairo_matrix_t *matrix,
+                         double radians)
+{
+    double  s;
+    double  c;
+
+    s = sin (radians);
+    c = cos (radians);
+
+    cairo_matrix_init (matrix,
+                      c, s,
+                      -s, c,
+                      0, 0);
+}
+slim_hidden_def(cairo_matrix_init_rotate);
+
+/**
+ * cairo_matrix_rotate:
+ * @matrix: a #cairo_matrix_t
+ * @radians: angle of rotation, in radians. The direction of rotation
+ * is defined such that positive angles rotate in the direction from
+ * the positive X axis toward the positive Y axis. With the default
+ * axis orientation of cairo, positive angles rotate in a clockwise
+ * direction.
+ *
+ * Applies rotation by @radians to the transformation in
+ * @matrix. The effect of the new transformation is to first rotate the
+ * coordinates by @radians, then apply the original transformation
+ * to the coordinates.
+ **/
+void
+cairo_matrix_rotate (cairo_matrix_t *matrix, double radians)
+{
+    cairo_matrix_t tmp;
+
+    cairo_matrix_init_rotate (&tmp, radians);
+
+    cairo_matrix_multiply (matrix, &tmp, matrix);
+}
+
+/**
+ * cairo_matrix_multiply:
+ * @result: a #cairo_matrix_t in which to store the result
+ * @a: a #cairo_matrix_t
+ * @b: a #cairo_matrix_t
+ *
+ * Multiplies the affine transformations in @a and @b together
+ * and stores the result in @result. The effect of the resulting
+ * transformation is to first apply the transformation in @a to the
+ * coordinates and then apply the transformation in @b to the
+ * coordinates.
+ *
+ * It is allowable for @result to be identical to either @a or @b.
+ **/
+/*
+ * XXX: The ordering of the arguments to this function corresponds
+ *      to [row_vector]*A*B. If we want to use column vectors instead,
+ *      then we need to switch the two arguments and fix up all
+ *      uses.
+ */
+void
+cairo_matrix_multiply (cairo_matrix_t *result, const cairo_matrix_t *a, const cairo_matrix_t *b)
+{
+    cairo_matrix_t r;
+
+    r.xx = a->xx * b->xx + a->yx * b->xy;
+    r.yx = a->xx * b->yx + a->yx * b->yy;
+
+    r.xy = a->xy * b->xx + a->yy * b->xy;
+    r.yy = a->xy * b->yx + a->yy * b->yy;
+
+    r.x0 = a->x0 * b->xx + a->y0 * b->xy + b->x0;
+    r.y0 = a->x0 * b->yx + a->y0 * b->yy + b->y0;
+
+    *result = r;
+}
+slim_hidden_def(cairo_matrix_multiply);
+
+/**
+ * cairo_matrix_transform_distance:
+ * @matrix: a #cairo_matrix_t
+ * @dx: X component of a distance vector. An in/out parameter
+ * @dy: Y component of a distance vector. An in/out parameter
+ *
+ * Transforms the distance vector (@dx,@dy) by @matrix. This is
+ * similar to cairo_matrix_transform_point() except that the translation
+ * components of the transformation are ignored. The calculation of
+ * the returned vector is as follows:
+ *
+ * <programlisting>
+ * dx2 = dx1 * a + dy1 * c;
+ * dy2 = dx1 * b + dy1 * d;
+ * </programlisting>
+ *
+ * Affine transformations are position invariant, so the same vector
+ * always transforms to the same vector. If (@x1,@y1) transforms
+ * to (@x2,@y2) then (@x1+@dx1,@y1+@dy1) will transform to
+ * (@x1+@dx2,@y1+@dy2) for all values of @x1 and @x2.
+ **/
+void
+cairo_matrix_transform_distance (const cairo_matrix_t *matrix, double *dx, double *dy)
+{
+    double new_x, new_y;
+
+    new_x = (matrix->xx * *dx + matrix->xy * *dy);
+    new_y = (matrix->yx * *dx + matrix->yy * *dy);
+
+    *dx = new_x;
+    *dy = new_y;
+}
+slim_hidden_def(cairo_matrix_transform_distance);
+
+/**
+ * cairo_matrix_transform_point:
+ * @matrix: a #cairo_matrix_t
+ * @x: X position. An in/out parameter
+ * @y: Y position. An in/out parameter
+ *
+ * Transforms the point (@x, @y) by @matrix.
+ **/
+void
+cairo_matrix_transform_point (const cairo_matrix_t *matrix, double *x, double *y)
+{
+    cairo_matrix_transform_distance (matrix, x, y);
+
+    *x += matrix->x0;
+    *y += matrix->y0;
+}
+slim_hidden_def(cairo_matrix_transform_point);
+
+void
+_cairo_matrix_transform_bounding_box (const cairo_matrix_t *matrix,
+                                     double *x1, double *y1,
+                                     double *x2, double *y2,
+                                     cairo_bool_t *is_tight)
+{
+    int i;
+    double quad_x[4], quad_y[4];
+    double min_x, max_x;
+    double min_y, max_y;
+
+    if (matrix->xy == 0. && matrix->yx == 0.) {
+       /* non-rotation/skew matrix, just map the two extreme points */
+
+       if (matrix->xx != 1.) {
+           quad_x[0] = *x1 * matrix->xx;
+           quad_x[1] = *x2 * matrix->xx;
+           if (quad_x[0] < quad_x[1]) {
+               *x1 = quad_x[0];
+               *x2 = quad_x[1];
+           } else {
+               *x1 = quad_x[1];
+               *x2 = quad_x[0];
+           }
+       }
+       if (matrix->x0 != 0.) {
+           *x1 += matrix->x0;
+           *x2 += matrix->x0;
+       }
+
+       if (matrix->yy != 1.) {
+           quad_y[0] = *y1 * matrix->yy;
+           quad_y[1] = *y2 * matrix->yy;
+           if (quad_y[0] < quad_y[1]) {
+               *y1 = quad_y[0];
+               *y2 = quad_y[1];
+           } else {
+               *y1 = quad_y[1];
+               *y2 = quad_y[0];
+           }
+       }
+       if (matrix->y0 != 0.) {
+           *y1 += matrix->y0;
+           *y2 += matrix->y0;
+       }
+
+       if (is_tight)
+           *is_tight = TRUE;
+
+       return;
+    }
+
+    /* general matrix */
+    quad_x[0] = *x1;
+    quad_y[0] = *y1;
+    cairo_matrix_transform_point (matrix, &quad_x[0], &quad_y[0]);
+
+    quad_x[1] = *x2;
+    quad_y[1] = *y1;
+    cairo_matrix_transform_point (matrix, &quad_x[1], &quad_y[1]);
+
+    quad_x[2] = *x1;
+    quad_y[2] = *y2;
+    cairo_matrix_transform_point (matrix, &quad_x[2], &quad_y[2]);
+
+    quad_x[3] = *x2;
+    quad_y[3] = *y2;
+    cairo_matrix_transform_point (matrix, &quad_x[3], &quad_y[3]);
+
+    min_x = max_x = quad_x[0];
+    min_y = max_y = quad_y[0];
+
+    for (i=1; i < 4; i++) {
+       if (quad_x[i] < min_x)
+           min_x = quad_x[i];
+       if (quad_x[i] > max_x)
+           max_x = quad_x[i];
+
+       if (quad_y[i] < min_y)
+           min_y = quad_y[i];
+       if (quad_y[i] > max_y)
+           max_y = quad_y[i];
+    }
+
+    *x1 = min_x;
+    *y1 = min_y;
+    *x2 = max_x;
+    *y2 = max_y;
+
+    if (is_tight) {
+        /* it's tight if and only if the four corner points form an axis-aligned
+           rectangle.
+           And that's true if and only if we can derive corners 0 and 3 from
+           corners 1 and 2 in one of two straightforward ways...
+           We could use a tolerance here but for now we'll fall back to FALSE in the case
+           of floating point error.
+        */
+        *is_tight =
+            (quad_x[1] == quad_x[0] && quad_y[1] == quad_y[3] &&
+             quad_x[2] == quad_x[3] && quad_y[2] == quad_y[0]) ||
+            (quad_x[1] == quad_x[3] && quad_y[1] == quad_y[0] &&
+             quad_x[2] == quad_x[0] && quad_y[2] == quad_y[3]);
+    }
+}
+
+cairo_private void
+_cairo_matrix_transform_bounding_box_fixed (const cairo_matrix_t *matrix,
+                                           cairo_box_t          *bbox,
+                                           cairo_bool_t *is_tight)
+{
+    double x1, y1, x2, y2;
+
+    _cairo_box_to_doubles (bbox, &x1, &y1, &x2, &y2);
+    _cairo_matrix_transform_bounding_box (matrix, &x1, &y1, &x2, &y2, is_tight);
+    _cairo_box_from_doubles (bbox, &x1, &y1, &x2, &y2);
+}
+
+static void
+_cairo_matrix_scalar_multiply (cairo_matrix_t *matrix, double scalar)
+{
+    matrix->xx *= scalar;
+    matrix->yx *= scalar;
+
+    matrix->xy *= scalar;
+    matrix->yy *= scalar;
+
+    matrix->x0 *= scalar;
+    matrix->y0 *= scalar;
+}
+
+/* This function isn't a correct adjoint in that the implicit 1 in the
+   homogeneous result should actually be ad-bc instead. But, since this
+   adjoint is only used in the computation of the inverse, which
+   divides by det (A)=ad-bc anyway, everything works out in the end. */
+static void
+_cairo_matrix_compute_adjoint (cairo_matrix_t *matrix)
+{
+    /* adj (A) = transpose (C:cofactor (A,i,j)) */
+    double a, b, c, d, tx, ty;
+
+    _cairo_matrix_get_affine (matrix,
+                             &a,  &b,
+                             &c,  &d,
+                             &tx, &ty);
+
+    cairo_matrix_init (matrix,
+                      d, -b,
+                      -c, a,
+                      c*ty - d*tx, b*tx - a*ty);
+}
+
+/**
+ * cairo_matrix_invert:
+ * @matrix: a #cairo_matrix_t
+ *
+ * Changes @matrix to be the inverse of its original value. Not
+ * all transformation matrices have inverses; if the matrix
+ * collapses points together (it is <firstterm>degenerate</firstterm>),
+ * then it has no inverse and this function will fail.
+ *
+ * Returns: If @matrix has an inverse, modifies @matrix to
+ *  be the inverse matrix and returns %CAIRO_STATUS_SUCCESS. Otherwise,
+ *  returns %CAIRO_STATUS_INVALID_MATRIX.
+ **/
+cairo_status_t
+cairo_matrix_invert (cairo_matrix_t *matrix)
+{
+    double det;
+
+    /* Simple scaling|translation matrices are quite common... */
+    if (matrix->xy == 0. && matrix->yx == 0.) {
+       matrix->x0 = -matrix->x0;
+       matrix->y0 = -matrix->y0;
+
+       if (matrix->xx != 1.) {
+           if (matrix->xx == 0.)
+               return _cairo_error (CAIRO_STATUS_INVALID_MATRIX);
+
+           matrix->xx = 1. / matrix->xx;
+           matrix->x0 *= matrix->xx;
+       }
+
+       if (matrix->yy != 1.) {
+           if (matrix->yy == 0.)
+               return _cairo_error (CAIRO_STATUS_INVALID_MATRIX);
+
+           matrix->yy = 1. / matrix->yy;
+           matrix->y0 *= matrix->yy;
+       }
+
+       return CAIRO_STATUS_SUCCESS;
+    }
+
+    /* inv (A) = 1/det (A) * adj (A) */
+    det = _cairo_matrix_compute_determinant (matrix);
+
+    if (! ISFINITE (det))
+       return _cairo_error (CAIRO_STATUS_INVALID_MATRIX);
+
+    if (det == 0)
+       return _cairo_error (CAIRO_STATUS_INVALID_MATRIX);
+
+    _cairo_matrix_compute_adjoint (matrix);
+    _cairo_matrix_scalar_multiply (matrix, 1 / det);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+slim_hidden_def(cairo_matrix_invert);
+
+cairo_bool_t
+_cairo_matrix_is_invertible (const cairo_matrix_t *matrix)
+{
+    double det;
+
+    det = _cairo_matrix_compute_determinant (matrix);
+
+    return ISFINITE (det) && det != 0.;
+}
+
+cairo_bool_t
+_cairo_matrix_is_scale_0 (const cairo_matrix_t *matrix)
+{
+    return matrix->xx == 0. &&
+           matrix->xy == 0. &&
+           matrix->yx == 0. &&
+           matrix->yy == 0.;
+}
+
+double
+_cairo_matrix_compute_determinant (const cairo_matrix_t *matrix)
+{
+    double a, b, c, d;
+
+    a = matrix->xx; b = matrix->yx;
+    c = matrix->xy; d = matrix->yy;
+
+    return a*d - b*c;
+}
+
+/**
+ * _cairo_matrix_compute_basis_scale_factors:
+ * @matrix: a matrix
+ * @basis_scale: the scale factor in the direction of basis
+ * @normal_scale: the scale factor in the direction normal to the basis
+ * @x_basis: basis to use.  X basis if true, Y basis otherwise.
+ *
+ * Computes |Mv| and det(M)/|Mv| for v=[1,0] if x_basis is true, and v=[0,1]
+ * otherwise, and M is @matrix.
+ *
+ * Return value: the scale factor of @matrix on the height of the font,
+ * or 1.0 if @matrix is %NULL.
+ **/
+cairo_status_t
+_cairo_matrix_compute_basis_scale_factors (const cairo_matrix_t *matrix,
+                                          double *basis_scale, double *normal_scale,
+                                          cairo_bool_t x_basis)
+{
+    double det;
+
+    det = _cairo_matrix_compute_determinant (matrix);
+
+    if (! ISFINITE (det))
+       return _cairo_error (CAIRO_STATUS_INVALID_MATRIX);
+
+    if (det == 0)
+    {
+       *basis_scale = *normal_scale = 0;
+    }
+    else
+    {
+       double x = x_basis != 0;
+       double y = x == 0;
+       double major, minor;
+
+       cairo_matrix_transform_distance (matrix, &x, &y);
+       major = hypot (x, y);
+       /*
+        * ignore mirroring
+        */
+       if (det < 0)
+           det = -det;
+       if (major)
+           minor = det / major;
+       else
+           minor = 0.0;
+       if (x_basis)
+       {
+           *basis_scale = major;
+           *normal_scale = minor;
+       }
+       else
+       {
+           *basis_scale = minor;
+           *normal_scale = major;
+       }
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_bool_t
+_cairo_matrix_is_identity (const cairo_matrix_t *matrix)
+{
+    return (matrix->xx == 1.0 && matrix->yx == 0.0 &&
+           matrix->xy == 0.0 && matrix->yy == 1.0 &&
+           matrix->x0 == 0.0 && matrix->y0 == 0.0);
+}
+
+cairo_bool_t
+_cairo_matrix_is_translation (const cairo_matrix_t *matrix)
+{
+    return (matrix->xx == 1.0 && matrix->yx == 0.0 &&
+           matrix->xy == 0.0 && matrix->yy == 1.0);
+}
+
+cairo_bool_t
+_cairo_matrix_is_integer_translation (const cairo_matrix_t *matrix,
+                                     int *itx, int *ity)
+{
+    if (_cairo_matrix_is_translation (matrix))
+    {
+        cairo_fixed_t x0_fixed = _cairo_fixed_from_double (matrix->x0);
+        cairo_fixed_t y0_fixed = _cairo_fixed_from_double (matrix->y0);
+
+        if (_cairo_fixed_is_integer (x0_fixed) &&
+            _cairo_fixed_is_integer (y0_fixed))
+        {
+            if (itx)
+                *itx = _cairo_fixed_integer_part (x0_fixed);
+            if (ity)
+                *ity = _cairo_fixed_integer_part (y0_fixed);
+
+            return TRUE;
+        }
+    }
+
+    return FALSE;
+}
+
+cairo_bool_t
+_cairo_matrix_has_unity_scale (const cairo_matrix_t *matrix)
+{
+    if (matrix->xy == 0.0 && matrix->yx == 0.0) {
+       if (! (matrix->xx == 1.0 || matrix->xx == -1.0))
+           return FALSE;
+       if (! (matrix->yy == 1.0 || matrix->yy == -1.0))
+           return FALSE;
+    } else if (matrix->xx == 0.0 && matrix->yy == 0.0) {
+       if (! (matrix->xy == 1.0 || matrix->xy == -1.0))
+           return FALSE;
+       if (! (matrix->yx == 1.0 || matrix->yx == -1.0))
+           return FALSE;
+    } else
+       return FALSE;
+
+    return TRUE;
+}
+
+#ifdef TIZEN_FIX_DASHED_LINE_STROKE
+/* Check whether matrix is uniform scale or not */
+cairo_bool_t
+_cairo_matrix_has_uniform_scale (const cairo_matrix_t *matrix)
+{
+    if (matrix->xy == 0.0 && matrix->yx == 0.0) {
+       if (matrix->xx == matrix->yy)
+           return TRUE;
+    }
+    return FALSE;
+}
+#endif
+
+/* By pixel exact here, we mean a matrix that is composed only of
+ * 90 degree rotations, flips, and integer translations and produces a 1:1
+ * mapping between source and destination pixels. If we transform an image
+ * with a pixel-exact matrix, filtering is not useful.
+ */
+cairo_bool_t
+_cairo_matrix_is_pixel_exact (const cairo_matrix_t *matrix)
+{
+    cairo_fixed_t x0_fixed, y0_fixed;
+
+    if (! _cairo_matrix_has_unity_scale (matrix))
+       return FALSE;
+
+    x0_fixed = _cairo_fixed_from_double (matrix->x0);
+    y0_fixed = _cairo_fixed_from_double (matrix->y0);
+
+    return _cairo_fixed_is_integer (x0_fixed) && _cairo_fixed_is_integer (y0_fixed);
+}
+
+/*
+  A circle in user space is transformed into an ellipse in device space.
+
+  The following is a derivation of a formula to calculate the length of the
+  major axis for this ellipse; this is useful for error bounds calculations.
+
+  Thanks to Walter Brisken <wbrisken@aoc.nrao.edu> for this derivation:
+
+  1.  First some notation:
+
+  All capital letters represent vectors in two dimensions.  A prime '
+  represents a transformed coordinate.  Matrices are written in underlined
+  form, ie _R_.  Lowercase letters represent scalar real values.
+
+  2.  The question has been posed:  What is the maximum expansion factor
+  achieved by the linear transformation
+
+  X' = X _R_
+
+  where _R_ is a real-valued 2x2 matrix with entries:
+
+  _R_ = [a b]
+        [c d]  .
+
+  In other words, what is the maximum radius, MAX[ |X'| ], reached for any
+  X on the unit circle ( |X| = 1 ) ?
+
+  3.  Some useful formulae
+
+  (A) through (C) below are standard double-angle formulae.  (D) is a lesser
+  known result and is derived below:
+
+  (A)  sin²(θ) = (1 - cos(2*θ))/2
+  (B)  cos²(θ) = (1 + cos(2*θ))/2
+  (C)  sin(θ)*cos(θ) = sin(2*θ)/2
+  (D)  MAX[a*cos(θ) + b*sin(θ)] = sqrt(a² + b²)
+
+  Proof of (D):
+
+  find the maximum of the function by setting the derivative to zero:
+
+       -a*sin(θ)+b*cos(θ) = 0
+
+  From this it follows that
+
+       tan(θ) = b/a
+
+  and hence
+
+       sin(θ) = b/sqrt(a² + b²)
+
+  and
+
+       cos(θ) = a/sqrt(a² + b²)
+
+  Thus the maximum value is
+
+       MAX[a*cos(θ) + b*sin(θ)] = (a² + b²)/sqrt(a² + b²)
+                                   = sqrt(a² + b²)
+
+  4.  Derivation of maximum expansion
+
+  To find MAX[ |X'| ] we search brute force method using calculus.  The unit
+  circle on which X is constrained is to be parameterized by t:
+
+       X(θ) = (cos(θ), sin(θ))
+
+  Thus
+
+       X'(θ) = X(θ) * _R_ = (cos(θ), sin(θ)) * [a b]
+                                               [c d]
+             = (a*cos(θ) + c*sin(θ), b*cos(θ) + d*sin(θ)).
+
+  Define
+
+       r(θ) = |X'(θ)|
+
+  Thus
+
+       r²(θ) = (a*cos(θ) + c*sin(θ))² + (b*cos(θ) + d*sin(θ))²
+             = (a² + b²)*cos²(θ) + (c² + d²)*sin²(θ)
+                 + 2*(a*c + b*d)*cos(θ)*sin(θ)
+
+  Now apply the double angle formulae (A) to (C) from above:
+
+       r²(θ) = (a² + b² + c² + d²)/2
+            + (a² + b² - c² - d²)*cos(2*θ)/2
+            + (a*c + b*d)*sin(2*θ)
+             = f + g*cos(φ) + h*sin(φ)
+
+  Where
+
+       f = (a² + b² + c² + d²)/2
+       g = (a² + b² - c² - d²)/2
+       h = (a*c + d*d)
+       φ = 2*θ
+
+  It is clear that MAX[ |X'| ] = sqrt(MAX[ r² ]).  Here we determine MAX[ r² ]
+  using (D) from above:
+
+       MAX[ r² ] = f + sqrt(g² + h²)
+
+  And finally
+
+       MAX[ |X'| ] = sqrt( f + sqrt(g² + h²) )
+
+  Which is the solution to this problem.
+
+  Walter Brisken
+  2004/10/08
+
+  (Note that the minor axis length is at the minimum of the above solution,
+  which is just sqrt ( f - sqrt(g² + h²) ) given the symmetry of (D)).
+
+
+  For another derivation of the same result, using Singular Value Decomposition,
+  see doc/tutorial/src/singular.c.
+*/
+
+/* determine the length of the major axis of a circle of the given radius
+   after applying the transformation matrix. */
+double
+_cairo_matrix_transformed_circle_major_axis (const cairo_matrix_t *matrix,
+                                            double radius)
+{
+    double  a, b, c, d, f, g, h, i, j;
+
+    if (_cairo_matrix_has_unity_scale (matrix))
+       return radius;
+
+    _cairo_matrix_get_affine (matrix,
+                              &a, &b,
+                              &c, &d,
+                              NULL, NULL);
+
+    i = a*a + b*b;
+    j = c*c + d*d;
+
+    f = 0.5 * (i + j);
+    g = 0.5 * (i - j);
+    h = a*c + b*d;
+
+    return radius * sqrt (f + hypot (g, h));
+
+    /*
+     * we don't need the minor axis length, which is
+     * double min = radius * sqrt (f - sqrt (g*g+h*h));
+     */
+}
+
+static const pixman_transform_t pixman_identity_transform = {{
+        {1 << 16,        0,       0},
+        {       0, 1 << 16,       0},
+        {       0,       0, 1 << 16}
+    }};
+
+static cairo_status_t
+_cairo_matrix_to_pixman_matrix (const cairo_matrix_t   *matrix,
+                               pixman_transform_t      *pixman_transform,
+                               double xc,
+                               double yc)
+{
+    cairo_matrix_t inv;
+    unsigned max_iterations;
+
+    pixman_transform->matrix[0][0] = _cairo_fixed_16_16_from_double (matrix->xx);
+    pixman_transform->matrix[0][1] = _cairo_fixed_16_16_from_double (matrix->xy);
+    pixman_transform->matrix[0][2] = _cairo_fixed_16_16_from_double (matrix->x0);
+
+    pixman_transform->matrix[1][0] = _cairo_fixed_16_16_from_double (matrix->yx);
+    pixman_transform->matrix[1][1] = _cairo_fixed_16_16_from_double (matrix->yy);
+    pixman_transform->matrix[1][2] = _cairo_fixed_16_16_from_double (matrix->y0);
+
+    pixman_transform->matrix[2][0] = 0;
+    pixman_transform->matrix[2][1] = 0;
+    pixman_transform->matrix[2][2] = 1 << 16;
+
+    /* The conversion above breaks cairo's translation invariance:
+     * a translation of (a, b) in device space translates to
+     * a translation of (xx * a + xy * b, yx * a + yy * b)
+     * for cairo, while pixman uses rounded versions of xx ... yy.
+     * This error increases as a and b get larger.
+     *
+     * To compensate for this, we fix the point (xc, yc) in pattern
+     * space and adjust pixman's transform to agree with cairo's at
+     * that point.
+     */
+
+    if (_cairo_matrix_has_unity_scale (matrix))
+       return CAIRO_STATUS_SUCCESS;
+
+    if (unlikely (fabs (matrix->xx) > PIXMAN_MAX_INT ||
+                 fabs (matrix->xy) > PIXMAN_MAX_INT ||
+                 fabs (matrix->x0) > PIXMAN_MAX_INT ||
+                 fabs (matrix->yx) > PIXMAN_MAX_INT ||
+                 fabs (matrix->yy) > PIXMAN_MAX_INT ||
+                 fabs (matrix->y0) > PIXMAN_MAX_INT))
+    {
+       return _cairo_error (CAIRO_STATUS_INVALID_MATRIX);
+    }
+
+    /* Note: If we can't invert the transformation, skip the adjustment. */
+    inv = *matrix;
+    if (cairo_matrix_invert (&inv) != CAIRO_STATUS_SUCCESS)
+       return CAIRO_STATUS_SUCCESS;
+
+    /* find the pattern space coordinate that maps to (xc, yc) */
+    max_iterations = 5;
+    do {
+       double x,y;
+       pixman_vector_t vector;
+       cairo_fixed_16_16_t dx, dy;
+
+       vector.vector[0] = _cairo_fixed_16_16_from_double (xc);
+       vector.vector[1] = _cairo_fixed_16_16_from_double (yc);
+       vector.vector[2] = 1 << 16;
+
+       /* If we can't transform the reference point, skip the adjustment. */
+       if (! pixman_transform_point_3d (pixman_transform, &vector))
+           return CAIRO_STATUS_SUCCESS;
+
+       x = pixman_fixed_to_double (vector.vector[0]);
+       y = pixman_fixed_to_double (vector.vector[1]);
+       cairo_matrix_transform_point (&inv, &x, &y);
+
+       /* Ideally, the vector should now be (xc, yc).
+        * We can now compensate for the resulting error.
+        */
+       x -= xc;
+       y -= yc;
+       cairo_matrix_transform_distance (matrix, &x, &y);
+       dx = _cairo_fixed_16_16_from_double (x);
+       dy = _cairo_fixed_16_16_from_double (y);
+       pixman_transform->matrix[0][2] -= dx;
+       pixman_transform->matrix[1][2] -= dy;
+
+       if (dx == 0 && dy == 0)
+           return CAIRO_STATUS_SUCCESS;
+    } while (--max_iterations);
+
+    /* We didn't find an exact match between cairo and pixman, but
+     * the matrix should be mostly correct */
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static inline double
+_pixman_nearest_sample (double d)
+{
+    return ceil (d - .5);
+}
+
+/**
+ * _cairo_matrix_is_pixman_translation:
+ * @matrix: a matrix
+ * @filter: the filter to be used on the pattern transformed by @matrix
+ * @x_offset: the translation in the X direction
+ * @y_offset: the translation in the Y direction
+ *
+ * Checks if @matrix translated by (x_offset, y_offset) can be
+ * represented using just an offset (within the range pixman can
+ * accept) and an identity matrix.
+ *
+ * Passing a non-zero value in x_offset/y_offset has the same effect
+ * as applying cairo_matrix_translate(matrix, x_offset, y_offset) and
+ * setting x_offset and y_offset to 0.
+ *
+ * Upon return x_offset and y_offset contain the translation vector if
+ * the return value is %TRUE. If the return value is %FALSE, they will
+ * not be modified.
+ *
+ * Return value: %TRUE if @matrix can be represented as a pixman
+ * translation, %FALSE otherwise.
+ **/
+cairo_bool_t
+_cairo_matrix_is_pixman_translation (const cairo_matrix_t     *matrix,
+                                    cairo_filter_t            filter,
+                                    int                      *x_offset,
+                                    int                      *y_offset)
+{
+    double tx, ty;
+
+    if (!_cairo_matrix_is_translation (matrix))
+       return FALSE;
+
+    if (matrix->x0 == 0. && matrix->y0 == 0.)
+       return TRUE;
+
+    tx = matrix->x0 + *x_offset;
+    ty = matrix->y0 + *y_offset;
+
+    if (filter == CAIRO_FILTER_FAST || filter == CAIRO_FILTER_NEAREST) {
+       tx = _pixman_nearest_sample (tx);
+       ty = _pixman_nearest_sample (ty);
+    } else if (tx != floor (tx) || ty != floor (ty)) {
+       return FALSE;
+    }
+
+    if (fabs (tx) > PIXMAN_MAX_INT || fabs (ty) > PIXMAN_MAX_INT)
+       return FALSE;
+
+    *x_offset = _cairo_lround (tx);
+    *y_offset = _cairo_lround (ty);
+    return TRUE;
+}
+
+/**
+ * _cairo_matrix_to_pixman_matrix_offset:
+ * @matrix: a matrix
+ * @filter: the filter to be used on the pattern transformed by @matrix
+ * @xc: the X coordinate of the point to fix in pattern space
+ * @yc: the Y coordinate of the point to fix in pattern space
+ * @out_transform: the transformation which best approximates @matrix
+ * @x_offset: the translation in the X direction
+ * @y_offset: the translation in the Y direction
+ *
+ * This function tries to represent @matrix translated by (x_offset,
+ * y_offset) as a %pixman_transform_t and an translation.
+ *
+ * Passing a non-zero value in x_offset/y_offset has the same effect
+ * as applying cairo_matrix_translate(matrix, x_offset, y_offset) and
+ * setting x_offset and y_offset to 0.
+ *
+ * If it is possible to represent the matrix with an identity
+ * %pixman_transform_t and a translation within the valid range for
+ * pixman, this function will set @out_transform to be the identity,
+ * @x_offset and @y_offset to be the translation vector and will
+ * return %CAIRO_INT_STATUS_NOTHING_TO_DO. Otherwise it will try to
+ * evenly divide the translational component of @matrix between
+ * @out_transform and (@x_offset, @y_offset).
+ *
+ * Upon return x_offset and y_offset contain the translation vector.
+ *
+ * Return value: %CAIRO_INT_STATUS_NOTHING_TO_DO if the out_transform
+ * is the identity, %CAIRO_STATUS_INVALID_MATRIX if it was not
+ * possible to represent @matrix as a pixman_transform_t without
+ * overflows, %CAIRO_STATUS_SUCCESS otherwise.
+ **/
+cairo_status_t
+_cairo_matrix_to_pixman_matrix_offset (const cairo_matrix_t    *matrix,
+                                      cairo_filter_t            filter,
+                                      double                    xc,
+                                      double                    yc,
+                                      pixman_transform_t       *out_transform,
+                                      int                      *x_offset,
+                                      int                      *y_offset)
+{
+    cairo_bool_t is_pixman_translation;
+
+    is_pixman_translation = _cairo_matrix_is_pixman_translation (matrix,
+                                                                filter,
+                                                                x_offset,
+                                                                y_offset);
+
+    if (is_pixman_translation) {
+       *out_transform = pixman_identity_transform;
+       return CAIRO_INT_STATUS_NOTHING_TO_DO;
+    } else {
+       cairo_matrix_t m;
+
+       m = *matrix;
+       cairo_matrix_translate (&m, *x_offset, *y_offset);
+       if (m.x0 != 0.0 || m.y0 != 0.0) {
+           double tx, ty, norm;
+           int i, j;
+
+           /* pixman also limits the [xy]_offset to 16 bits so evenly
+            * spread the bits between the two.
+            *
+            * To do this, find the solutions of:
+            *   |x| = |x*m.xx + y*m.xy + m.x0|
+            *   |y| = |x*m.yx + y*m.yy + m.y0|
+            *
+            * and select the one whose maximum norm is smallest.
+            */
+           tx = m.x0;
+           ty = m.y0;
+           norm = MAX (fabs (tx), fabs (ty));
+
+           for (i = -1; i < 2; i+=2) {
+               for (j = -1; j < 2; j+=2) {
+                   double x, y, den, new_norm;
+
+                   den = (m.xx + i) * (m.yy + j) - m.xy * m.yx;
+                   if (fabs (den) < DBL_EPSILON)
+                       continue;
+
+                   x = m.y0 * m.xy - m.x0 * (m.yy + j);
+                   y = m.x0 * m.yx - m.y0 * (m.xx + i);
+
+                   den = 1 / den;
+                   x *= den;
+                   y *= den;
+
+                   new_norm = MAX (fabs (x), fabs (y));
+                   if (norm > new_norm) {
+                       norm = new_norm;
+                       tx = x;
+                       ty = y;
+                   }
+               }
+           }
+
+           tx = floor (tx);
+           ty = floor (ty);
+           *x_offset = -tx;
+           *y_offset = -ty;
+           cairo_matrix_translate (&m, tx, ty);
+       } else {
+           *x_offset = 0;
+           *y_offset = 0;
+       }
+
+       return _cairo_matrix_to_pixman_matrix (&m, out_transform, xc, yc);
+    }
+}
diff --git a/src/cairo-mesh-pattern-rasterizer.c b/src/cairo-mesh-pattern-rasterizer.c
new file mode 100644 (file)
index 0000000..82b16e7
--- /dev/null
@@ -0,0 +1,940 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright 2009 Andrea Canciani
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Andrea Canciani.
+ *
+ * Contributor(s):
+ *     Andrea Canciani <ranma42@gmail.com>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-array-private.h"
+#include "cairo-pattern-private.h"
+
+/*
+ * Rasterizer for mesh patterns.
+ *
+ * This implementation is based on techniques derived from several
+ * papers (available from ACM):
+ *
+ * - Lien, Shantz and Pratt "Adaptive Forward Differencing for
+ *   Rendering Curves and Surfaces" (discussion of the AFD technique,
+ *   bound of 1/sqrt(2) on step length without proof)
+ *
+ * - Popescu and Rosen, "Forward rasterization" (description of
+ *   forward rasterization, proof of the previous bound)
+ *
+ * - Klassen, "Integer Forward Differencing of Cubic Polynomials:
+ *   Analysis and Algorithms"
+ *
+ * - Klassen, "Exact Integer Hybrid Subdivision and Forward
+ *   Differencing of Cubics" (improving the bound on the minimum
+ *   number of steps)
+ *
+ * - Chang, Shantz and Rocchetti, "Rendering Cubic Curves and Surfaces
+ *   with Integer Adaptive Forward Differencing" (analysis of forward
+ *   differencing applied to Bezier patches)
+ *
+ * Notes:
+ * - Poor performance expected in degenerate cases
+ *
+ * - Patches mostly outside the drawing area are drawn completely (and
+ *   clipped), wasting time
+ *
+ * - Both previous problems are greatly reduced by splitting until a
+ *   reasonably small size and clipping the new tiles: execution time
+ *   is quadratic in the convex-hull diameter instead than linear to
+ *   the painted area. Splitting the tiles doesn't change the painted
+ *   area but (usually) reduces the bounding box area (bbox area can
+ *   remain the same after splitting, but cannot grow)
+ *
+ * - The initial implementation used adaptive forward differencing,
+ *   but simple forward differencing scored better in benchmarks
+ *
+ * Idea:
+ *
+ * We do a sampling over the cubic patch with step du and dv (in the
+ * two parameters) that guarantees that any point of our sampling will
+ * be at most at 1/sqrt(2) from its adjacent points. In formulae
+ * (assuming B is the patch):
+ *
+ *   |B(u,v) - B(u+du,v)| < 1/sqrt(2)
+ *   |B(u,v) - B(u,v+dv)| < 1/sqrt(2)
+ *
+ * This means that every pixel covered by the patch will contain at
+ * least one of the samples, thus forward rasterization can be
+ * performed. Sketch of proof (from Popescu and Rosen):
+ *
+ * Let's take the P pixel we're interested into. If we assume it to be
+ * square, its boundaries define 9 regions on the plane:
+ *
+ * 1|2|3
+ * -+-+-
+ * 8|P|4
+ * -+-+-
+ * 7|6|5
+ *
+ * Let's check that the pixel P will contain at least one point
+ * assuming that it is covered by the patch.
+ *
+ * Since the pixel is covered by the patch, its center will belong to
+ * (at least) one of the quads:
+ *
+ *   {(B(u,v), B(u+du,v), B(u,v+dv), B(u+du,v+dv)) for u,v in [0,1]}
+ *
+ * If P doesn't contain any of the corners of the quad:
+ *
+ * - if one of the corners is in 1,3,5 or 7, other two of them have to
+ *   be in 2,4,6 or 8, thus if the last corner is not in P, the length
+ *   of one of the edges will be > 1/sqrt(2)
+ *
+ * - if none of the corners is in 1,3,5 or 7, all of them are in 2,4,6
+ *   and/or 8. If they are all in different regions, they can't
+ *   satisfy the distance constraint. If two of them are in the same
+ *   region (let's say 2), no point is in 6 and again it is impossible
+ *   to have the center of P in the quad respecting the distance
+ *   constraint (both these assertions can be checked by continuity
+ *   considering the length of the edges of a quad with the vertices
+ *   on the edges of P)
+ *
+ * Each of the cases led to a contradiction, so P contains at least
+ * one of the corners of the quad.
+ */
+
+/*
+ * Make sure that errors are less than 1 in fixed point math if you
+ * change these values.
+ *
+ * The error is amplified by about steps^3/4 times.
+ * The rasterizer always uses a number of steps that is a power of 2.
+ *
+ * 256 is the maximum allowed number of steps (to have error < 1)
+ * using 8.24 for the differences.
+ */
+#define STEPS_MAX_V 256.0
+#define STEPS_MAX_U 256.0
+
+/*
+ * If the patch/curve is only partially visible, split it to a finer
+ * resolution to get higher chances to clip (part of) it.
+ *
+ * These values have not been computed, but simply obtained
+ * empirically (by benchmarking some patches). They should never be
+ * greater than STEPS_MAX_V (or STEPS_MAX_U), but they can be as small
+ * as 1 (depending on how much you want to spend time in splitting the
+ * patch/curve when trying to save some rasterization time).
+ */
+#define STEPS_CLIP_V 64.0
+#define STEPS_CLIP_U 64.0
+
+
+/* Utils */
+static inline double
+sqlen (cairo_point_double_t p0, cairo_point_double_t p1)
+{
+    cairo_point_double_t delta;
+
+    delta.x = p0.x - p1.x;
+    delta.y = p0.y - p1.y;
+
+    return delta.x * delta.x + delta.y * delta.y;
+}
+
+static inline int16_t
+_color_delta_to_shifted_short (int32_t from, int32_t to, int shift)
+{
+    int32_t delta = to - from;
+
+    /* We need to round toward zero, because otherwise adding the
+     * delta 2^shift times can overflow */
+    if (delta >= 0)
+       return delta >> shift;
+    else
+       return -((-delta) >> shift);
+}
+
+/*
+ * Convert a number of steps to the equivalent shift.
+ *
+ * Input: the square of the minimum number of steps
+ *
+ * Output: the smallest integer x such that 2^x > steps
+ */
+static inline int
+sqsteps2shift (double steps_sq)
+{
+    int r;
+    frexp (MAX (1.0, steps_sq), &r);
+    return (r + 1) >> 1;
+}
+
+/*
+ * FD functions
+ *
+ * A Bezier curve is defined (with respect to a parameter t in
+ * [0,1]) from its nodes (x,y,z,w) like this:
+ *
+ *   B(t) = x(1-t)^3 + 3yt(1-t)^2 + 3zt^2(1-t) + wt^3
+ *
+ * To efficiently evaluate a Bezier curve, the rasterizer uses forward
+ * differences. Given x, y, z, w (the 4 nodes of the Bezier curve), it
+ * is possible to convert them to forward differences form and walk
+ * over the curve using fd_init (), fd_down () and fd_fwd ().
+ *
+ * f[0] is always the value of the Bezier curve for "current" t.
+ */
+
+/*
+ * Initialize the coefficient for forward differences.
+ *
+ * Input: x,y,z,w are the 4 nodes of the Bezier curve
+ *
+ * Output: f[i] is the i-th difference of the curve
+ *
+ * f[0] is the value of the curve for t==0, i.e. f[0]==x.
+ *
+ * The initial step is 1; this means that each step increases t by 1
+ * (so fd_init () immediately followed by fd_fwd (f) n times makes
+ * f[0] be the value of the curve for t==n).
+ */
+static inline void
+fd_init (double x, double y, double z, double w, double f[4])
+{
+    f[0] = x;
+    f[1] = w - x;
+    f[2] = 6. * (w - 2. * z + y);
+    f[3] = 6. * (w - 3. * z + 3. * y - x);
+}
+
+/*
+ * Halve the step of the coefficients for forward differences.
+ *
+ * Input: f[i] is the i-th difference of the curve
+ *
+ * Output: f[i] is the i-th difference of the curve with half the
+ *         original step
+ *
+ * f[0] is not affected, so the current t is not changed.
+ *
+ * The other coefficients are changed so that the step is half the
+ * original step. This means that doing fd_fwd (f) n times with the
+ * input f results in the same f[0] as doing fd_fwd (f) 2n times with
+ * the output f.
+ */
+static inline void
+fd_down (double f[4])
+{
+    f[3] *= 0.125;
+    f[2] = f[2] * 0.25 - f[3];
+    f[1] = (f[1] - f[2]) * 0.5;
+}
+
+/*
+ * Perform one step of forward differences along the curve.
+ *
+ * Input: f[i] is the i-th difference of the curve
+ *
+ * Output: f[i] is the i-th difference of the curve after one step
+ */
+static inline void
+fd_fwd (double f[4])
+{
+    f[0] += f[1];
+    f[1] += f[2];
+    f[2] += f[3];
+}
+
+/*
+ * Transform to integer forward differences.
+ *
+ * Input: d[n] is the n-th difference (in double precision)
+ *
+ * Output: i[n] is the n-th difference (in fixed point precision)
+ *
+ * i[0] is 9.23 fixed point, other differences are 4.28 fixed point.
+ */
+static inline void
+fd_fixed (double d[4], int32_t i[4])
+{
+    i[0] = _cairo_fixed_16_16_from_double (256 *  2 * d[0]);
+    i[1] = _cairo_fixed_16_16_from_double (256 * 16 * d[1]);
+    i[2] = _cairo_fixed_16_16_from_double (256 * 16 * d[2]);
+    i[3] = _cairo_fixed_16_16_from_double (256 * 16 * d[3]);
+}
+
+/*
+ * Perform one step of integer forward differences along the curve.
+ *
+ * Input: f[n] is the n-th difference
+ *
+ * Output: f[n] is the n-th difference
+ *
+ * f[0] is 9.23 fixed point, other differences are 4.28 fixed point.
+ */
+static inline void
+fd_fixed_fwd (int32_t f[4])
+{
+    f[0] += (f[1] >> 5) + ((f[1] >> 4) & 1);
+    f[1] += f[2];
+    f[2] += f[3];
+}
+
+/*
+ * Compute the minimum number of steps that guarantee that walking
+ * over a curve will leave no holes.
+ *
+ * Input: p[0..3] the nodes of the Bezier curve
+ *
+ * Returns: the square of the number of steps
+ *
+ * Idea:
+ *
+ * We want to make sure that at every step we move by less than
+ * 1/sqrt(2).
+ *
+ * The derivative of the cubic Bezier with nodes (p0, p1, p2, p3) is
+ * the quadratic Bezier with nodes (p1-p0, p2-p1, p3-p2) scaled by 3,
+ * so (since a Bezier curve is always bounded by its convex hull), we
+ * can say that:
+ *
+ *  max(|B'(t)|) <= 3 max (|p1-p0|, |p2-p1|, |p3-p2|)
+ *
+ * We can improve this by noticing that a quadratic Bezier (a,b,c) is
+ * bounded by the quad (a,lerp(a,b,t),lerp(b,c,t),c) for any t, so
+ * (substituting the previous values, using t=0.5 and simplifying):
+ *
+ *  max(|B'(t)|) <= 3 max (|p1-p0|, |p2-p0|/2, |p3-p1|/2, |p3-p2|)
+ *
+ * So, to guarantee a maximum step lenght of 1/sqrt(2) we must do:
+ *
+ *   3 max (|p1-p0|, |p2-p0|/2, |p3-p1|/2, |p3-p2|) sqrt(2) steps
+ */
+static inline double
+bezier_steps_sq (cairo_point_double_t p[4])
+{
+    double tmp = sqlen (p[0], p[1]);
+    tmp = MAX (tmp, sqlen (p[2], p[3]));
+    tmp = MAX (tmp, sqlen (p[0], p[2]) * .25);
+    tmp = MAX (tmp, sqlen (p[1], p[3]) * .25);
+    return 18.0 * tmp;
+}
+
+/*
+ * Split a 1D Bezier cubic using de Casteljau's algorithm.
+ *
+ * Input: x,y,z,w the nodes of the Bezier curve
+ *
+ * Output: x0,y0,z0,w0 and x1,y1,z1,w1 are respectively the nodes of
+ *         the first half and of the second half of the curve
+ *
+ * The output control nodes have to be distinct.
+ */
+static inline void
+split_bezier_1D (double  x,  double  y,  double  z,  double  w,
+                double *x0, double *y0, double *z0, double *w0,
+                double *x1, double *y1, double *z1, double *w1)
+{
+    double tmp;
+
+    *x0 = x;
+    *w1 = w;
+
+    tmp = 0.5 * (y + z);
+    *y0 = 0.5 * (x + y);
+    *z1 = 0.5 * (z + w);
+
+    *z0 = 0.5 * (*y0 + tmp);
+    *y1 = 0.5 * (tmp + *z1);
+
+    *w0 = *x1 = 0.5 * (*z0 + *y1);
+}
+
+/*
+ * Split a Bezier curve using de Casteljau's algorithm.
+ *
+ * Input: p[0..3] the nodes of the Bezier curve
+ *
+ * Output: fst_half[0..3] and snd_half[0..3] are respectively the
+ *         nodes of the first and of the second half of the curve
+ *
+ * fst_half and snd_half must be different, but they can be the same as
+ * nodes.
+ */
+static void
+split_bezier (cairo_point_double_t p[4],
+             cairo_point_double_t fst_half[4],
+             cairo_point_double_t snd_half[4])
+{
+    split_bezier_1D (p[0].x, p[1].x, p[2].x, p[3].x,
+                    &fst_half[0].x, &fst_half[1].x, &fst_half[2].x, &fst_half[3].x,
+                    &snd_half[0].x, &snd_half[1].x, &snd_half[2].x, &snd_half[3].x);
+
+    split_bezier_1D (p[0].y, p[1].y, p[2].y, p[3].y,
+                    &fst_half[0].y, &fst_half[1].y, &fst_half[2].y, &fst_half[3].y,
+                    &snd_half[0].y, &snd_half[1].y, &snd_half[2].y, &snd_half[3].y);
+}
+
+
+typedef enum _intersection {
+    INSIDE = -1, /* the interval is entirely contained in the reference interval */
+    OUTSIDE = 0, /* the interval has no intersection with the reference interval */
+    PARTIAL = 1  /* the interval intersects the reference interval (but is not fully inside it) */
+} intersection_t;
+
+/*
+ * Check if an interval if inside another.
+ *
+ * Input: a,b are the extrema of the first interval
+ *        c,d are the extrema of the second interval
+ *
+ * Returns: INSIDE  iff [a,b) intersection [c,d) = [a,b)
+ *          OUTSIDE iff [a,b) intersection [c,d) = {}
+ *          PARTIAL otherwise
+ *
+ * The function assumes a < b and c < d
+ *
+ * Note: Bitwise-anding the results along each component gives the
+ *       expected result for [a,b) x [A,B) intersection [c,d) x [C,D).
+ */
+static inline int
+intersect_interval (double a, double b, double c, double d)
+{
+    if (c <= a && b <= d)
+       return INSIDE;
+    else if (a >= d || b <= c)
+       return OUTSIDE;
+    else
+       return PARTIAL;
+}
+
+/*
+ * Set the color of a pixel.
+ *
+ * Input: data is the base pointer of the image
+ *        width, height are the dimensions of the image
+ *        stride is the stride in bytes between adjacent rows
+ *        x, y are the coordinates of the pixel to be colored
+ *        r,g,b,a are the color components of the color to be set
+ *
+ * Output: the (x,y) pixel in data has the (r,g,b,a) color
+ *
+ * The input color components are not premultiplied, but the data
+ * stored in the image is assumed to be in CAIRO_FORMAT_ARGB32 (8 bpc,
+ * premultiplied).
+ *
+ * If the pixel to be set is outside the image, this function does
+ * nothing.
+ */
+static inline void
+draw_pixel (unsigned char *data, int width, int height, int stride,
+           int x, int y, uint16_t r, uint16_t g, uint16_t b, uint16_t a)
+{
+    if (likely (0 <= x && 0 <= y && x < width && y < height)) {
+       uint32_t tr, tg, tb, ta;
+
+       /* Premultiply and round */
+       ta = a;
+       tr = r * ta + 0x8000;
+       tg = g * ta + 0x8000;
+       tb = b * ta + 0x8000;
+
+       tr += tr >> 16;
+       tg += tg >> 16;
+       tb += tb >> 16;
+
+       *((uint32_t*) (data + y*stride + 4*x)) = ((ta << 16) & 0xff000000) |
+           ((tr >> 8) & 0xff0000) | ((tg >> 16) & 0xff00) | (tb >> 24);
+    }
+}
+
+/*
+ * Forward-rasterize a cubic curve using forward differences.
+ *
+ * Input: data is the base pointer of the image
+ *        width, height are the dimensions of the image
+ *        stride is the stride in bytes between adjacent rows
+ *        ushift is log2(n) if n is the number of desired steps
+ *        dxu[i], dyu[i] are the x,y forward differences of the curve
+ *        r0,g0,b0,a0 are the color components of the start point
+ *        r3,g3,b3,a3 are the color components of the end point
+ *
+ * Output: data will be changed to have the requested curve drawn in
+ *         the specified colors
+ *
+ * The input color components are not premultiplied, but the data
+ * stored in the image is assumed to be in CAIRO_FORMAT_ARGB32 (8 bpc,
+ * premultiplied).
+ *
+ * The function draws n+1 pixels, that is from the point at step 0 to
+ * the point at step n, both included. This is the discrete equivalent
+ * to drawing the curve for values of the interpolation parameter in
+ * [0,1] (including both extremes).
+ */
+static inline void
+rasterize_bezier_curve (unsigned char *data, int width, int height, int stride,
+                       int ushift, double dxu[4], double dyu[4],
+                       uint16_t r0, uint16_t g0, uint16_t b0, uint16_t a0,
+                       uint16_t r3, uint16_t g3, uint16_t b3, uint16_t a3)
+{
+    int32_t xu[4], yu[4];
+    int x0, y0, u, usteps = 1 << ushift;
+
+    uint16_t r = r0, g = g0, b = b0, a = a0;
+    int16_t dr = _color_delta_to_shifted_short (r0, r3, ushift);
+    int16_t dg = _color_delta_to_shifted_short (g0, g3, ushift);
+    int16_t db = _color_delta_to_shifted_short (b0, b3, ushift);
+    int16_t da = _color_delta_to_shifted_short (a0, a3, ushift);
+
+    fd_fixed (dxu, xu);
+    fd_fixed (dyu, yu);
+
+    /*
+     * Use (dxu[0],dyu[0]) as origin for the forward differences.
+     *
+     * This makes it possible to handle much larger coordinates (the
+     * ones that can be represented as cairo_fixed_t)
+     */
+    x0 = _cairo_fixed_from_double (dxu[0]);
+    y0 = _cairo_fixed_from_double (dyu[0]);
+    xu[0] = 0;
+    yu[0] = 0;
+
+    for (u = 0; u <= usteps; ++u) {
+       /*
+        * This rasterizer assumes that pixels are integer aligned
+        * squares, so a generic (x,y) point belongs to the pixel with
+        * top-left coordinates (floor(x), floor(y))
+        */
+
+       int x = _cairo_fixed_integer_floor (x0 + (xu[0] >> 15) + ((xu[0] >> 14) & 1));
+       int y = _cairo_fixed_integer_floor (y0 + (yu[0] >> 15) + ((yu[0] >> 14) & 1));
+
+       draw_pixel (data, width, height, stride, x, y, r, g, b, a);
+
+       fd_fixed_fwd (xu);
+       fd_fixed_fwd (yu);
+       r += dr;
+       g += dg;
+       b += db;
+       a += da;
+    }
+}
+
+/*
+ * Clip, split and rasterize a Bezier curve.
+ *
+ * Input: data is the base pointer of the image
+ *        width, height are the dimensions of the image
+ *        stride is the stride in bytes between adjacent rows
+ *        p[i] is the i-th node of the Bezier curve
+ *        c0[i] is the i-th color component at the start point
+ *        c3[i] is the i-th color component at the end point
+ *
+ * Output: data will be changed to have the requested curve drawn in
+ *         the specified colors
+ *
+ * The input color components are not premultiplied, but the data
+ * stored in the image is assumed to be in CAIRO_FORMAT_ARGB32 (8 bpc,
+ * premultiplied).
+ *
+ * The color components are red, green, blue and alpha, in this order.
+ *
+ * The function guarantees that it will draw the curve with a step
+ * small enough to never have a distance above 1/sqrt(2) between two
+ * consecutive points (which is needed to ensure that no hole can
+ * appear when using this function to rasterize a patch).
+ */
+static void
+draw_bezier_curve (unsigned char *data, int width, int height, int stride,
+                  cairo_point_double_t p[4], double c0[4], double c3[4])
+{
+    double top, bottom, left, right, steps_sq;
+    int i, v;
+
+    top = bottom = p[0].y;
+    for (i = 1; i < 4; ++i) {
+       top    = MIN (top,    p[i].y);
+       bottom = MAX (bottom, p[i].y);
+    }
+
+    /* Check visibility */
+    v = intersect_interval (top, bottom, 0, height);
+    if (v == OUTSIDE)
+       return;
+
+    left = right = p[0].x;
+    for (i = 1; i < 4; ++i) {
+       left  = MIN (left,  p[i].x);
+       right = MAX (right, p[i].x);
+    }
+
+    v &= intersect_interval (left, right, 0, width);
+    if (v == OUTSIDE)
+       return;
+
+    steps_sq = bezier_steps_sq (p);
+    if (steps_sq >= (v == INSIDE ? STEPS_MAX_U * STEPS_MAX_U : STEPS_CLIP_U * STEPS_CLIP_U)) {
+       /*
+        * The number of steps is greater than the threshold. This
+        * means that either the error would become too big if we
+        * directly rasterized it or that we can probably save some
+        * time by splitting the curve and clipping part of it
+        */
+       cairo_point_double_t first[4], second[4];
+       double midc[4];
+       split_bezier (p, first, second);
+       midc[0] = (c0[0] + c3[0]) * 0.5;
+       midc[1] = (c0[1] + c3[1]) * 0.5;
+       midc[2] = (c0[2] + c3[2]) * 0.5;
+       midc[3] = (c0[3] + c3[3]) * 0.5;
+       draw_bezier_curve (data, width, height, stride, first, c0, midc);
+       draw_bezier_curve (data, width, height, stride, second, midc, c3);
+    } else {
+       double xu[4], yu[4];
+       int ushift = sqsteps2shift (steps_sq), k;
+
+       fd_init (p[0].x, p[1].x, p[2].x, p[3].x, xu);
+       fd_init (p[0].y, p[1].y, p[2].y, p[3].y, yu);
+
+       for (k = 0; k < ushift; ++k) {
+           fd_down (xu);
+           fd_down (yu);
+       }
+
+       rasterize_bezier_curve (data, width, height, stride, ushift,
+                               xu, yu,
+                               _cairo_color_double_to_short (c0[0]),
+                               _cairo_color_double_to_short (c0[1]),
+                               _cairo_color_double_to_short (c0[2]),
+                               _cairo_color_double_to_short (c0[3]),
+                               _cairo_color_double_to_short (c3[0]),
+                               _cairo_color_double_to_short (c3[1]),
+                               _cairo_color_double_to_short (c3[2]),
+                               _cairo_color_double_to_short (c3[3]));
+
+       /* Draw the end point, to make sure that we didn't leave it
+        * out because of rounding */
+       draw_pixel (data, width, height, stride,
+                   _cairo_fixed_integer_floor (_cairo_fixed_from_double (p[3].x)),
+                   _cairo_fixed_integer_floor (_cairo_fixed_from_double (p[3].y)),
+                   _cairo_color_double_to_short (c3[0]),
+                   _cairo_color_double_to_short (c3[1]),
+                   _cairo_color_double_to_short (c3[2]),
+                   _cairo_color_double_to_short (c3[3]));
+    }
+}
+
+/*
+ * Forward-rasterize a cubic Bezier patch using forward differences.
+ *
+ * Input: data is the base pointer of the image
+ *        width, height are the dimensions of the image
+ *        stride is the stride in bytes between adjacent rows
+ *        vshift is log2(n) if n is the number of desired steps
+ *        p[i][j], p[i][j] are the the nodes of the Bezier patch
+ *        col[i][j] is the j-th color component of the i-th corner
+ *
+ * Output: data will be changed to have the requested patch drawn in
+ *         the specified colors
+ *
+ * The nodes of the patch are as follows:
+ *
+ * u\v 0    - >    1
+ * 0  p00 p01 p02 p03
+ * |  p10 p11 p12 p13
+ * v  p20 p21 p22 p23
+ * 1  p30 p31 p32 p33
+ *
+ * i.e. u varies along the first component (rows), v varies along the
+ * second one (columns).
+ *
+ * The color components are red, green, blue and alpha, in this order.
+ * c[0..3] are the colors in p00, p30, p03, p33 respectively
+ *
+ * The input color components are not premultiplied, but the data
+ * stored in the image is assumed to be in CAIRO_FORMAT_ARGB32 (8 bpc,
+ * premultiplied).
+ *
+ * If the patch folds over itself, the part with the highest v
+ * parameter is considered above. If both have the same v, the one
+ * with the highest u parameter is above.
+ *
+ * The function draws n+1 curves, that is from the curve at step 0 to
+ * the curve at step n, both included. This is the discrete equivalent
+ * to drawing the patch for values of the interpolation parameter in
+ * [0,1] (including both extremes).
+ */
+static inline void
+rasterize_bezier_patch (unsigned char *data, int width, int height, int stride, int vshift,
+                       cairo_point_double_t p[4][4], double col[4][4])
+{
+    double pv[4][2][4], cstart[4], cend[4], dcstart[4], dcend[4];
+    int vsteps, v, i, k;
+
+    vsteps = 1 << vshift;
+
+    /*
+     * pv[i][0] is the function (represented using forward
+     * differences) mapping v to the x coordinate of the i-th node of
+     * the Bezier curve with parameter u.
+     * (Likewise p[i][0] gives the y coordinate).
+     *
+     * This means that (pv[0][0][0],pv[0][1][0]),
+     * (pv[1][0][0],pv[1][1][0]), (pv[2][0][0],pv[2][1][0]) and
+     * (pv[3][0][0],pv[3][1][0]) are the nodes of the Bezier curve for
+     * the "current" v value (see the FD comments for more details).
+     */
+    for (i = 0; i < 4; ++i) {
+       fd_init (p[i][0].x, p[i][1].x, p[i][2].x, p[i][3].x, pv[i][0]);
+       fd_init (p[i][0].y, p[i][1].y, p[i][2].y, p[i][3].y, pv[i][1]);
+       for (k = 0; k < vshift; ++k) {
+           fd_down (pv[i][0]);
+           fd_down (pv[i][1]);
+       }
+    }
+
+    for (i = 0; i < 4; ++i) {
+       cstart[i]  = col[0][i];
+       cend[i]    = col[1][i];
+       dcstart[i] = (col[2][i] - col[0][i]) / vsteps;
+       dcend[i]   = (col[3][i] - col[1][i]) / vsteps;
+    }
+
+    for (v = 0; v <= vsteps; ++v) {
+       cairo_point_double_t nodes[4];
+       for (i = 0; i < 4; ++i) {
+           nodes[i].x = pv[i][0][0];
+           nodes[i].y = pv[i][1][0];
+       }
+
+       draw_bezier_curve (data, width, height, stride, nodes, cstart, cend);
+
+       for (i = 0; i < 4; ++i) {
+           fd_fwd (pv[i][0]);
+           fd_fwd (pv[i][1]);
+           cstart[i] += dcstart[i];
+           cend[i] += dcend[i];
+       }
+    }
+}
+
+/*
+ * Clip, split and rasterize a Bezier cubic patch.
+ *
+ * Input: data is the base pointer of the image
+ *        width, height are the dimensions of the image
+ *        stride is the stride in bytes between adjacent rows
+ *        p[i][j], p[i][j] are the nodes of the patch
+ *        col[i][j] is the j-th color component of the i-th corner
+ *
+ * Output: data will be changed to have the requested patch drawn in
+ *         the specified colors
+ *
+ * The nodes of the patch are as follows:
+ *
+ * u\v 0    - >    1
+ * 0  p00 p01 p02 p03
+ * |  p10 p11 p12 p13
+ * v  p20 p21 p22 p23
+ * 1  p30 p31 p32 p33
+ *
+ * i.e. u varies along the first component (rows), v varies along the
+ * second one (columns).
+ *
+ * The color components are red, green, blue and alpha, in this order.
+ * c[0..3] are the colors in p00, p30, p03, p33 respectively
+ *
+ * The input color components are not premultiplied, but the data
+ * stored in the image is assumed to be in CAIRO_FORMAT_ARGB32 (8 bpc,
+ * premultiplied).
+ *
+ * If the patch folds over itself, the part with the highest v
+ * parameter is considered above. If both have the same v, the one
+ * with the highest u parameter is above.
+ *
+ * The function guarantees that it will draw the patch with a step
+ * small enough to never have a distance above 1/sqrt(2) between two
+ * adjacent points (which guarantees that no hole can appear).
+ *
+ * This function can be used to rasterize a tile of PDF type 7
+ * shadings (see http://www.adobe.com/devnet/pdf/pdf_reference.html).
+ */
+static void
+draw_bezier_patch (unsigned char *data, int width, int height, int stride,
+                    cairo_point_double_t p[4][4], double c[4][4])
+{
+    double top, bottom, left, right, steps_sq;
+    int i, j, v;
+
+    top = bottom = p[0][0].y;
+    for (i = 0; i < 4; ++i) {
+       for (j= 0; j < 4; ++j) {
+           top    = MIN (top,    p[i][j].y);
+           bottom = MAX (bottom, p[i][j].y);
+       }
+    }
+
+    v = intersect_interval (top, bottom, 0, height);
+    if (v == OUTSIDE)
+       return;
+
+    left = right = p[0][0].x;
+    for (i = 0; i < 4; ++i) {
+       for (j= 0; j < 4; ++j) {
+           left  = MIN (left,  p[i][j].x);
+           right = MAX (right, p[i][j].x);
+       }
+    }
+
+    v &= intersect_interval (left, right, 0, width);
+    if (v == OUTSIDE)
+       return;
+
+    steps_sq = 0;
+    for (i = 0; i < 4; ++i)
+       steps_sq = MAX (steps_sq, bezier_steps_sq (p[i]));
+
+    if (steps_sq >= (v == INSIDE ? STEPS_MAX_V * STEPS_MAX_V : STEPS_CLIP_V * STEPS_CLIP_V)) {
+       /* The number of steps is greater than the threshold. This
+        * means that either the error would become too big if we
+        * directly rasterized it or that we can probably save some
+        * time by splitting the curve and clipping part of it. The
+        * patch is only split in the v direction to guarantee that
+        * rasterizing each part will overwrite parts with low v with
+        * overlapping parts with higher v. */
+
+       cairo_point_double_t first[4][4], second[4][4];
+       double subc[4][4];
+
+       for (i = 0; i < 4; ++i)
+           split_bezier (p[i], first[i], second[i]);
+
+       for (i = 0; i < 4; ++i) {
+           subc[0][i] = c[0][i];
+           subc[1][i] = c[1][i];
+           subc[2][i] = 0.5 * (c[0][i] + c[2][i]);
+           subc[3][i] = 0.5 * (c[1][i] + c[3][i]);
+       }
+
+       draw_bezier_patch (data, width, height, stride, first, subc);
+
+       for (i = 0; i < 4; ++i) {
+           subc[0][i] = subc[2][i];
+           subc[1][i] = subc[3][i];
+           subc[2][i] = c[2][i];
+           subc[3][i] = c[3][i];
+       }
+       draw_bezier_patch (data, width, height, stride, second, subc);
+    } else {
+       rasterize_bezier_patch (data, width, height, stride, sqsteps2shift (steps_sq), p, c);
+    }
+}
+
+/*
+ * Draw a tensor product shading pattern.
+ *
+ * Input: mesh is the mesh pattern
+ *        data is the base pointer of the image
+ *        width, height are the dimensions of the image
+ *        stride is the stride in bytes between adjacent rows
+ *
+ * Output: data will be changed to have the pattern drawn on it
+ *
+ * data is assumed to be clear and its content is assumed to be in
+ * CAIRO_FORMAT_ARGB32 (8 bpc, premultiplied).
+ *
+ * This function can be used to rasterize a PDF type 7 shading (see
+ * http://www.adobe.com/devnet/pdf/pdf_reference.html).
+ */
+void
+_cairo_mesh_pattern_rasterize (const cairo_mesh_pattern_t *mesh,
+                              void                       *data,
+                              int                         width,
+                              int                         height,
+                              int                         stride,
+                              double                      x_offset,
+                              double                      y_offset)
+{
+    cairo_point_double_t nodes[4][4];
+    double colors[4][4];
+    cairo_matrix_t p2u;
+    unsigned int i, j, k, n;
+    cairo_status_t status;
+    const cairo_mesh_patch_t *patch;
+    const cairo_color_t *c;
+
+    assert (mesh->base.status == CAIRO_STATUS_SUCCESS);
+    assert (mesh->current_patch == NULL);
+
+    p2u = mesh->base.matrix;
+    status = cairo_matrix_invert (&p2u);
+    assert (status == CAIRO_STATUS_SUCCESS);
+
+    n = _cairo_array_num_elements (&mesh->patches);
+    patch = _cairo_array_index_const (&mesh->patches, 0);
+    for (i = 0; i < n; i++) {
+       for (j = 0; j < 4; j++) {
+           for (k = 0; k < 4; k++) {
+               nodes[j][k] = patch->points[j][k];
+               cairo_matrix_transform_point (&p2u, &nodes[j][k].x, &nodes[j][k].y);
+               nodes[j][k].x += x_offset;
+               nodes[j][k].y += y_offset;
+           }
+       }
+
+       c = &patch->colors[0];
+       colors[0][0] = c->red;
+       colors[0][1] = c->green;
+       colors[0][2] = c->blue;
+       colors[0][3] = c->alpha;
+
+       c = &patch->colors[3];
+       colors[1][0] = c->red;
+       colors[1][1] = c->green;
+       colors[1][2] = c->blue;
+       colors[1][3] = c->alpha;
+
+       c = &patch->colors[1];
+       colors[2][0] = c->red;
+       colors[2][1] = c->green;
+       colors[2][2] = c->blue;
+       colors[2][3] = c->alpha;
+
+       c = &patch->colors[2];
+       colors[3][0] = c->red;
+       colors[3][1] = c->green;
+       colors[3][2] = c->blue;
+       colors[3][3] = c->alpha;
+
+       draw_bezier_patch (data, width, height, stride, nodes, colors);
+       patch++;
+    }
+}
diff --git a/src/cairo-mime-surface.c b/src/cairo-mime-surface.c
new file mode 100644 (file)
index 0000000..e19852f
--- /dev/null
@@ -0,0 +1,405 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2011 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+/**
+ * SECTION:cairo-mime-surface
+ * @Title: Callback Surfaces
+ * @Short_Description: Allows the user to provide a callback to supply image
+ * data upon demand
+ * @See_Also: #cairo_surface_t
+ *
+ * The mime surfaces provide the ability to render from arbitrary sources
+ * not necessarily resident nor immediately usable by Cairo. The user is
+ * given the ability to insert a placeholder surface which can be used
+ * with a pattern and then later supply the actual pixel data upon demand.
+ * This deferred source is given both the sample region for the operation
+ * along with the destination surface such that they may be taken into
+ * account when creating the actual surface to use as the source of pixel
+ * data.
+ *
+ * The reason why it is called the mime surface is two-fold. First it came
+ * about as an extension of the mime-data property to handle deferred
+ * image decoding when rendering to pixel buffers (as opposed to the pass-
+ * through support in PDF and friends.) And then to further rationalise
+ * the name, it is a surface that mimics a real source without ever
+ * holding onto to any pixels of its own - a mime surface.
+ *
+ * The mime-surface callback interface consists of 4 functions. The principal
+ * pair are the acquire/release callbacks which are called when pixel data
+ * is required for an operation (along with the target surface and the sample
+ * extents). The callee must supply a surface that covers the sample area and
+ * set the actual extents of the returned surface in the output rectangle
+ * parameter. The surface does not necessarily have to be an image surface,
+ * but it is expected that an image surface is likely to be the most
+ * convenient for uploading pixel data. (Use
+ * cairo_surface_create_similar_image() to create an image surface that is
+ * optimised for uploading to the target.) The release callback is
+ * subsequently called when the returned surface is no longer needed (before
+ * the operation completes, within the lifetime of the source).
+ *
+ * The other pair of functions are to aide with lifetime management of the
+ * surface with respect to patterns and other users. The destroy callback
+ * allows for the caller to cleanup the associated data when the last
+ * reference to surface is destroyed. The snapshot callback is triggered
+ * when there is an immutable surface pattern referencing the mime-surface
+ * and the mime-surface will be be invalidated. (Since the mime-surface is
+ * read-only and a reference will be held by the pattern, this can only be
+ * triggered through an explicit cairo_surface_finish().) In this
+ * circumstance, we need to clone the source in order to preserve the pixel
+ * data for later use (i.e. we have recorded the pattern). The snapshot
+ * callback provides an interface for the caller to clone the mime-surface
+ * in an efficient manner.  The returned surface may be of any type so long
+ * as it holds all pixel data and remains accessible.
+ */
+
+/**
+ * CAIRO_HAS_MIME_SURFACE:
+ *
+ * Defined if the mime surface backend is available.
+ * The mime surface backend is always built in.
+ *
+ * @Since: 1.12
+ */
+
+#include "cairoint.h"
+#include "cairo-error-private.h"
+#include "cairo-image-surface-private.h"
+#include "cairo-surface-backend-private.h"
+
+typedef struct _cairo_mime_surface {
+    cairo_surface_t base;
+
+    cairo_rectangle_int_t extents;
+
+    cairo_mime_surface_acquire_t acquire;
+    cairo_mime_surface_release_t release;
+    cairo_mime_surface_snapshot_t snapshot;
+    cairo_mime_surface_destroy_t destroy;
+
+    /* an explicit pre-allocated member in preference to the general user-data */
+    void *user_data;
+} cairo_mime_surface_t;
+
+static cairo_status_t
+_cairo_mime_surface_finish (void *abstract_surface)
+{
+    cairo_mime_surface_t *surface = abstract_surface;
+
+    if (surface->destroy)
+       surface->destroy (&surface->base, surface->user_data);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_bool_t
+_cairo_mime_surface_get_extents (void                    *abstract_surface,
+                                cairo_rectangle_int_t   *rectangle)
+{
+    cairo_mime_surface_t *surface = abstract_surface;
+
+    *rectangle = surface->extents;
+    return TRUE;
+}
+
+static cairo_status_t
+_cairo_mime_surface_acquire_source_image (void                    *abstract_surface,
+                                         //cairo_surface_t       *target,
+                                         cairo_image_surface_t  **image_out,
+                                         void                   **image_extra)
+{
+    cairo_mime_surface_t *mime = abstract_surface;
+    cairo_surface_t *acquired;
+    cairo_surface_t *dummy_target;
+    cairo_rectangle_int_t extents;
+
+    if (mime->acquire == NULL)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    /* Force the callee to populate the extents rectangle */
+    memset (&extents, 0, sizeof (extents));
+
+    /* Masquerade for a flexible user-interface */
+    dummy_target = _cairo_image_surface_create_with_content (mime->base.content, 0, 0);
+    acquired = mime->acquire (&mime->base, mime->user_data,
+                             dummy_target, &mime->extents, &extents);
+    cairo_surface_destroy (dummy_target);
+
+    if (acquired == NULL)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    /* The callee must have supplied us with all the image data */
+    assert (extents.width == mime->extents.width && extents.height == mime->extents.height);
+
+    if (! _cairo_surface_is_image (acquired)) {
+       cairo_status_t status;
+       void *extra = NULL;
+
+       status = _cairo_surface_acquire_source_image (acquired, image_out, &extra);
+       if (unlikely (status)) {
+           cairo_surface_destroy (acquired);
+           return status;
+       }
+
+       assert (extra == NULL);
+       *image_extra = acquired;
+    } else {
+       *image_out = (cairo_image_surface_t *) acquired;
+       *image_extra = NULL;
+    }
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_mime_surface_release_source_image (void                   *abstract_surface,
+                                         cairo_image_surface_t  *image,
+                                         void                   *image_extra)
+{
+    cairo_mime_surface_t *mime = abstract_surface;
+
+    if (image_extra) {
+       cairo_surface_destroy (&image->base);
+       image = image_extra;
+    }
+
+    if (mime->release)
+       mime->release (&mime->base, mime->user_data, &image->base);
+}
+
+static cairo_surface_t *
+_cairo_mime_surface_snapshot (void *abstract_surface)
+{
+    cairo_mime_surface_t *mime = abstract_surface;
+
+    if (mime->snapshot == NULL)
+       return NULL;
+
+    return mime->snapshot (&mime->base, mime->user_data);
+}
+
+static const cairo_surface_backend_t cairo_mime_surface_backend = {
+    CAIRO_SURFACE_TYPE_MIME,
+    _cairo_mime_surface_finish,
+
+    NULL, /* Read-only */
+
+    NULL, /* create similar */
+    NULL, /* create similar image */
+    NULL, /* map to image */
+    NULL, /* unmap image */
+
+    _cairo_mime_surface_acquire_source_image,
+    _cairo_mime_surface_release_source_image,
+    _cairo_mime_surface_snapshot,
+
+    NULL, /* copy_page */
+    NULL, /* show_page */
+
+    _cairo_mime_surface_get_extents,
+    NULL, /* get_font_options */
+
+    NULL, /* flush */
+    NULL, /* mark_dirty_rectangle */
+
+    NULL, /* scaled_font_fini */
+    NULL, /* scaled_glyph_fini */
+
+    NULL, /* paint */
+    NULL, /* mask */
+    NULL, /* stroke */
+    NULL, /* fill */
+    NULL, /* glyphs */
+};
+
+cairo_surface_t *
+cairo_mime_surface_create (void *data, cairo_content_t content, int width, int height)
+{
+    cairo_mime_surface_t *surface;
+
+    if (width < 0 || height < 0)
+       return _cairo_surface_create_in_error (CAIRO_STATUS_INVALID_SIZE);
+
+    if (! CAIRO_CONTENT_VALID (content))
+       return _cairo_surface_create_in_error (CAIRO_STATUS_INVALID_CONTENT);
+
+    surface = calloc (1, sizeof (*surface));
+    if (unlikely (surface == NULL))
+       return _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY);
+
+    _cairo_surface_init (&surface->base,
+                        &cairo_mime_surface_backend,
+                        NULL, /* device */
+                        content);
+
+    surface->extents.x = 0;
+    surface->extents.y = 0;
+    surface->extents.width  = width;
+    surface->extents.height = height;
+
+    surface->user_data = data;
+
+    return &surface->base;
+}
+
+void
+cairo_mime_surface_set_callback_data (cairo_surface_t *surface,
+                                     void *data)
+{
+    cairo_mime_surface_t *mime;
+
+    if (CAIRO_REFERENCE_COUNT_IS_INVALID (&surface->ref_count))
+       return;
+
+    if (surface->backend != &cairo_mime_surface_backend)
+       return;
+
+    mime = (cairo_mime_surface_t *)surface;
+    mime->user_data = data;
+}
+
+void *
+cairo_mime_surface_get_callback_data (cairo_surface_t *surface)
+{
+    cairo_mime_surface_t *mime;
+
+    if (CAIRO_REFERENCE_COUNT_IS_INVALID (&surface->ref_count))
+       return NULL;
+
+    if (surface->backend != &cairo_mime_surface_backend)
+       return NULL;
+
+    mime = (cairo_mime_surface_t *)surface;
+    return mime->user_data;
+}
+
+void
+cairo_mime_surface_set_acquire (cairo_surface_t *surface,
+                               cairo_mime_surface_acquire_t acquire,
+                               cairo_mime_surface_release_t release)
+{
+    cairo_mime_surface_t *mime;
+
+    if (CAIRO_REFERENCE_COUNT_IS_INVALID (&surface->ref_count))
+       return;
+
+    if (surface->backend != &cairo_mime_surface_backend)
+       return;
+
+    mime = (cairo_mime_surface_t *)surface;
+    mime->acquire = acquire;
+    mime->release = release;
+}
+
+void
+cairo_mime_surface_get_acquire (cairo_surface_t *surface,
+                               cairo_mime_surface_acquire_t *acquire,
+                               cairo_mime_surface_release_t *release)
+{
+    cairo_mime_surface_t *mime;
+
+    if (CAIRO_REFERENCE_COUNT_IS_INVALID (&surface->ref_count))
+       return;
+
+    if (surface->backend != &cairo_mime_surface_backend)
+       return;
+
+    mime = (cairo_mime_surface_t *)surface;
+    if (acquire)
+       *acquire = mime->acquire;
+    if (release)
+       *release = mime->release;
+}
+
+void
+cairo_mime_surface_set_snapshot (cairo_surface_t *surface,
+                                cairo_mime_surface_snapshot_t snapshot)
+{
+    cairo_mime_surface_t *mime;
+
+    if (CAIRO_REFERENCE_COUNT_IS_INVALID (&surface->ref_count))
+       return;
+
+    if (surface->backend != &cairo_mime_surface_backend)
+       return;
+
+    mime = (cairo_mime_surface_t *)surface;
+    mime->snapshot = snapshot;
+}
+
+cairo_mime_surface_snapshot_t
+cairo_mime_surface_get_snapshot (cairo_surface_t *surface)
+{
+    cairo_mime_surface_t *mime;
+
+    if (CAIRO_REFERENCE_COUNT_IS_INVALID (&surface->ref_count))
+       return NULL;
+
+    if (surface->backend != &cairo_mime_surface_backend)
+       return NULL;
+
+    mime = (cairo_mime_surface_t *)surface;
+    return mime->snapshot;
+}
+
+void
+cairo_mime_surface_set_destroy (cairo_surface_t *surface,
+                               cairo_mime_surface_destroy_t destroy)
+{
+    cairo_mime_surface_t *mime;
+
+    if (CAIRO_REFERENCE_COUNT_IS_INVALID (&surface->ref_count))
+       return;
+
+    if (surface->backend != &cairo_mime_surface_backend)
+       return;
+
+    mime = (cairo_mime_surface_t *)surface;
+    mime->destroy = destroy;
+}
+
+cairo_mime_surface_destroy_t
+cairo_mime_surface_get_destroy (cairo_surface_t *surface)
+{
+    cairo_mime_surface_t *mime;
+
+    if (CAIRO_REFERENCE_COUNT_IS_INVALID (&surface->ref_count))
+       return NULL;
+
+    if (surface->backend != &cairo_mime_surface_backend)
+       return NULL;
+
+    mime = (cairo_mime_surface_t *)surface;
+    return mime->destroy;
+}
diff --git a/src/cairo-misc.c b/src/cairo-misc.c
new file mode 100644 (file)
index 0000000..27c264b
--- /dev/null
@@ -0,0 +1,933 @@
+/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ * Copyright © 2005 Red Hat, Inc.
+ * Copyright © 2007 Adrian Johnson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *     Carl D. Worth <cworth@cworth.org>
+ *      Adrian Johnson <ajohnson@redneon.com>
+ */
+
+#include "cairoint.h"
+#include "cairo-error-private.h"
+
+COMPILE_TIME_ASSERT ((int)CAIRO_STATUS_LAST_STATUS < (int)CAIRO_INT_STATUS_UNSUPPORTED);
+COMPILE_TIME_ASSERT (CAIRO_INT_STATUS_LAST_STATUS <= 127);
+
+/**
+ * SECTION:cairo-status
+ * @Title: Error handling
+ * @Short_Description: Decoding cairo's status
+ * @See_Also: cairo_status(), cairo_surface_status(), cairo_pattern_status(),
+ *            cairo_font_face_status(), cairo_scaled_font_status(), 
+ *            cairo_region_status()
+ *
+ * Cairo uses a single status type to represent all kinds of errors.  A status
+ * value of %CAIRO_STATUS_SUCCESS represents no error and has an integer value
+ * of zero.  All other status values represent an error.
+ *
+ * Cairo's error handling is designed to be easy to use and safe.  All major
+ * cairo objects <firstterm>retain</firstterm> an error status internally which
+ * can be queried anytime by the users using cairo*_status() calls.  In
+ * the mean time, it is safe to call all cairo functions normally even if the
+ * underlying object is in an error status.  This means that no error handling
+ * code is required before or after each individual cairo function call.
+ */
+
+/* Public stuff */
+
+/**
+ * cairo_status_to_string:
+ * @status: a cairo status
+ *
+ * Provides a human-readable description of a #cairo_status_t.
+ *
+ * Returns: a string representation of the status
+ */
+const char *
+cairo_status_to_string (cairo_status_t status)
+{
+    switch (status) {
+    case CAIRO_STATUS_SUCCESS:
+       return "no error has occurred";
+    case CAIRO_STATUS_NO_MEMORY:
+       return "out of memory";
+    case CAIRO_STATUS_INVALID_RESTORE:
+       return "cairo_restore() without matching cairo_save()";
+    case CAIRO_STATUS_INVALID_POP_GROUP:
+       return "no saved group to pop, i.e. cairo_pop_group() without matching cairo_push_group()";
+    case CAIRO_STATUS_NO_CURRENT_POINT:
+       return "no current point defined";
+    case CAIRO_STATUS_INVALID_MATRIX:
+       return "invalid matrix (not invertible)";
+    case CAIRO_STATUS_INVALID_STATUS:
+       return "invalid value for an input cairo_status_t";
+    case CAIRO_STATUS_NULL_POINTER:
+       return "NULL pointer";
+    case CAIRO_STATUS_INVALID_STRING:
+       return "input string not valid UTF-8";
+    case CAIRO_STATUS_INVALID_PATH_DATA:
+       return "input path data not valid";
+    case CAIRO_STATUS_READ_ERROR:
+       return "error while reading from input stream";
+    case CAIRO_STATUS_WRITE_ERROR:
+       return "error while writing to output stream";
+    case CAIRO_STATUS_SURFACE_FINISHED:
+       return "the target surface has been finished";
+    case CAIRO_STATUS_SURFACE_TYPE_MISMATCH:
+       return "the surface type is not appropriate for the operation";
+    case CAIRO_STATUS_PATTERN_TYPE_MISMATCH:
+       return "the pattern type is not appropriate for the operation";
+    case CAIRO_STATUS_INVALID_CONTENT:
+       return "invalid value for an input cairo_content_t";
+    case CAIRO_STATUS_INVALID_FORMAT:
+       return "invalid value for an input cairo_format_t";
+    case CAIRO_STATUS_INVALID_VISUAL:
+       return "invalid value for an input Visual*";
+    case CAIRO_STATUS_FILE_NOT_FOUND:
+       return "file not found";
+    case CAIRO_STATUS_INVALID_DASH:
+       return "invalid value for a dash setting";
+    case CAIRO_STATUS_INVALID_DSC_COMMENT:
+       return "invalid value for a DSC comment";
+    case CAIRO_STATUS_INVALID_INDEX:
+       return "invalid index passed to getter";
+    case CAIRO_STATUS_CLIP_NOT_REPRESENTABLE:
+        return "clip region not representable in desired format";
+    case CAIRO_STATUS_TEMP_FILE_ERROR:
+       return "error creating or writing to a temporary file";
+    case CAIRO_STATUS_INVALID_STRIDE:
+       return "invalid value for stride";
+    case CAIRO_STATUS_FONT_TYPE_MISMATCH:
+       return "the font type is not appropriate for the operation";
+    case CAIRO_STATUS_USER_FONT_IMMUTABLE:
+       return "the user-font is immutable";
+    case CAIRO_STATUS_USER_FONT_ERROR:
+       return "error occurred in a user-font callback function";
+    case CAIRO_STATUS_NEGATIVE_COUNT:
+       return "negative number used where it is not allowed";
+    case CAIRO_STATUS_INVALID_CLUSTERS:
+       return "input clusters do not represent the accompanying text and glyph arrays";
+    case CAIRO_STATUS_INVALID_SLANT:
+       return "invalid value for an input cairo_font_slant_t";
+    case CAIRO_STATUS_INVALID_WEIGHT:
+       return "invalid value for an input cairo_font_weight_t";
+    case CAIRO_STATUS_INVALID_SIZE:
+       return "invalid value (typically too big) for the size of the input (surface, pattern, etc.)";
+    case CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED:
+       return "user-font method not implemented";
+    case CAIRO_STATUS_DEVICE_TYPE_MISMATCH:
+       return "the device type is not appropriate for the operation";
+    case CAIRO_STATUS_DEVICE_ERROR:
+       return "an operation to the device caused an unspecified error";
+    case CAIRO_STATUS_INVALID_MESH_CONSTRUCTION:
+       return "invalid operation during mesh pattern construction";
+    case CAIRO_STATUS_DEVICE_FINISHED:
+       return "the target device has been finished";
+    default:
+    case CAIRO_STATUS_LAST_STATUS:
+       return "<unknown error status>";
+    }
+}
+
+
+/**
+ * cairo_glyph_allocate:
+ * @num_glyphs: number of glyphs to allocate
+ *
+ * Allocates an array of #cairo_glyph_t's.
+ * This function is only useful in implementations of
+ * #cairo_user_scaled_font_text_to_glyphs_func_t where the user
+ * needs to allocate an array of glyphs that cairo will free.
+ * For all other uses, user can use their own allocation method
+ * for glyphs.
+ *
+ * This function returns %NULL if @num_glyphs is not positive,
+ * or if out of memory.  That means, the %NULL return value
+ * signals out-of-memory only if @num_glyphs was positive.
+ *
+ * Returns: the newly allocated array of glyphs that should be
+ *          freed using cairo_glyph_free()
+ *
+ * Since: 1.8
+ */
+cairo_glyph_t *
+cairo_glyph_allocate (int num_glyphs)
+{
+    if (num_glyphs <= 0)
+       return NULL;
+
+    return _cairo_malloc_ab (num_glyphs, sizeof (cairo_glyph_t));
+}
+slim_hidden_def (cairo_glyph_allocate);
+
+/**
+ * cairo_glyph_free:
+ * @glyphs: array of glyphs to free, or %NULL
+ *
+ * Frees an array of #cairo_glyph_t's allocated using cairo_glyph_allocate().
+ * This function is only useful to free glyph array returned
+ * by cairo_scaled_font_text_to_glyphs() where cairo returns
+ * an array of glyphs that the user will free.
+ * For all other uses, user can use their own allocation method
+ * for glyphs.
+ *
+ * Since: 1.8
+ */
+void
+cairo_glyph_free (cairo_glyph_t *glyphs)
+{
+    free (glyphs);
+}
+slim_hidden_def (cairo_glyph_free);
+
+/**
+ * cairo_text_cluster_allocate:
+ * @num_clusters: number of text_clusters to allocate
+ *
+ * Allocates an array of #cairo_text_cluster_t's.
+ * This function is only useful in implementations of
+ * #cairo_user_scaled_font_text_to_glyphs_func_t where the user
+ * needs to allocate an array of text clusters that cairo will free.
+ * For all other uses, user can use their own allocation method
+ * for text clusters.
+ *
+ * This function returns %NULL if @num_clusters is not positive,
+ * or if out of memory.  That means, the %NULL return value
+ * signals out-of-memory only if @num_clusters was positive.
+ *
+ * Returns: the newly allocated array of text clusters that should be
+ *          freed using cairo_text_cluster_free()
+ *
+ * Since: 1.8
+ */
+cairo_text_cluster_t *
+cairo_text_cluster_allocate (int num_clusters)
+{
+    if (num_clusters <= 0)
+       return NULL;
+
+    return _cairo_malloc_ab (num_clusters, sizeof (cairo_text_cluster_t));
+}
+slim_hidden_def (cairo_text_cluster_allocate);
+
+/**
+ * cairo_text_cluster_free:
+ * @clusters: array of text clusters to free, or %NULL
+ *
+ * Frees an array of #cairo_text_cluster's allocated using cairo_text_cluster_allocate().
+ * This function is only useful to free text cluster array returned
+ * by cairo_scaled_font_text_to_glyphs() where cairo returns
+ * an array of text clusters that the user will free.
+ * For all other uses, user can use their own allocation method
+ * for text clusters.
+ *
+ * Since: 1.8
+ */
+void
+cairo_text_cluster_free (cairo_text_cluster_t *clusters)
+{
+    free (clusters);
+}
+slim_hidden_def (cairo_text_cluster_free);
+
+
+/* Private stuff */
+
+/**
+ * _cairo_validate_text_clusters:
+ * @utf8: UTF-8 text
+ * @utf8_len: length of @utf8 in bytes
+ * @glyphs: array of glyphs
+ * @num_glyphs: number of glyphs
+ * @clusters: array of cluster mapping information
+ * @num_clusters: number of clusters in the mapping
+ * @cluster_flags: cluster flags
+ *
+ * Check that clusters cover the entire glyphs and utf8 arrays,
+ * and that cluster boundaries are UTF-8 boundaries.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS upon success, or
+ *               %CAIRO_STATUS_INVALID_CLUSTERS on error.
+ *               The error is either invalid UTF-8 input,
+ *               or bad cluster mapping.
+ */
+cairo_status_t
+_cairo_validate_text_clusters (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   cluster_flags)
+{
+    cairo_status_t status;
+    unsigned int n_bytes  = 0;
+    unsigned int n_glyphs = 0;
+    int i;
+
+    for (i = 0; i < num_clusters; i++) {
+       int cluster_bytes  = clusters[i].num_bytes;
+       int cluster_glyphs = clusters[i].num_glyphs;
+
+       if (cluster_bytes < 0 || cluster_glyphs < 0)
+           goto BAD;
+
+       /* A cluster should cover at least one character or glyph.
+        * I can't see any use for a 0,0 cluster.
+        * I can't see an immediate use for a zero-text cluster
+        * right now either, but they don't harm.
+        * Zero-glyph clusters on the other hand are useful for
+        * things like U+200C ZERO WIDTH NON-JOINER */
+       if (cluster_bytes == 0 && cluster_glyphs == 0)
+           goto BAD;
+
+       /* Since n_bytes and n_glyphs are unsigned, but the rest of
+        * values involved are signed, we can detect overflow easily */
+       if (n_bytes+cluster_bytes > (unsigned int)utf8_len || n_glyphs+cluster_glyphs > (unsigned int)num_glyphs)
+           goto BAD;
+
+       /* Make sure we've got valid UTF-8 for the cluster */
+       status = _cairo_utf8_to_ucs4 (utf8+n_bytes, cluster_bytes, NULL, NULL);
+       if (unlikely (status))
+           return _cairo_error (CAIRO_STATUS_INVALID_CLUSTERS);
+
+       n_bytes  += cluster_bytes ;
+       n_glyphs += cluster_glyphs;
+    }
+
+    if (n_bytes != (unsigned int) utf8_len || n_glyphs != (unsigned int) num_glyphs) {
+      BAD:
+       return _cairo_error (CAIRO_STATUS_INVALID_CLUSTERS);
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+/**
+ * _cairo_operator_bounded_by_mask:
+ * @op: a #cairo_operator_t
+ *
+ * A bounded operator is one where mask pixel
+ * of zero results in no effect on the destination image.
+ *
+ * Unbounded operators often require special handling; if you, for
+ * example, draw trapezoids with an unbounded operator, the effect
+ * extends past the bounding box of the trapezoids.
+ *
+ * Return value: %TRUE if the operator is bounded by the mask operand
+ **/
+cairo_bool_t
+_cairo_operator_bounded_by_mask (cairo_operator_t op)
+{
+    switch (op) {
+    case CAIRO_OPERATOR_CLEAR:
+    case CAIRO_OPERATOR_SOURCE:
+    case CAIRO_OPERATOR_OVER:
+    case CAIRO_OPERATOR_ATOP:
+    case CAIRO_OPERATOR_DEST:
+    case CAIRO_OPERATOR_DEST_OVER:
+    case CAIRO_OPERATOR_DEST_OUT:
+    case CAIRO_OPERATOR_XOR:
+    case CAIRO_OPERATOR_ADD:
+    case CAIRO_OPERATOR_SATURATE:
+    case CAIRO_OPERATOR_MULTIPLY:
+    case CAIRO_OPERATOR_SCREEN:
+    case CAIRO_OPERATOR_OVERLAY:
+    case CAIRO_OPERATOR_DARKEN:
+    case CAIRO_OPERATOR_LIGHTEN:
+    case CAIRO_OPERATOR_COLOR_DODGE:
+    case CAIRO_OPERATOR_COLOR_BURN:
+    case CAIRO_OPERATOR_HARD_LIGHT:
+    case CAIRO_OPERATOR_SOFT_LIGHT:
+    case CAIRO_OPERATOR_DIFFERENCE:
+    case CAIRO_OPERATOR_EXCLUSION:
+    case CAIRO_OPERATOR_HSL_HUE:
+    case CAIRO_OPERATOR_HSL_SATURATION:
+    case CAIRO_OPERATOR_HSL_COLOR:
+    case CAIRO_OPERATOR_HSL_LUMINOSITY:
+       return TRUE;
+    case CAIRO_OPERATOR_OUT:
+    case CAIRO_OPERATOR_IN:
+    case CAIRO_OPERATOR_DEST_IN:
+    case CAIRO_OPERATOR_DEST_ATOP:
+       return FALSE;
+    }
+
+    ASSERT_NOT_REACHED;
+    return FALSE;
+}
+
+/**
+ * _cairo_operator_bounded_by_source:
+ * @op: a #cairo_operator_t
+ *
+ * A bounded operator is one where source pixels of zero
+ * (in all four components, r, g, b and a) effect no change
+ * in the resulting destination image.
+ *
+ * Unbounded operators often require special handling; if you, for
+ * example, copy a surface with the SOURCE operator, the effect
+ * extends past the bounding box of the source surface.
+ *
+ * Return value: %TRUE if the operator is bounded by the source operand
+ **/
+cairo_bool_t
+_cairo_operator_bounded_by_source (cairo_operator_t op)
+{
+    switch (op) {
+    case CAIRO_OPERATOR_OVER:
+    case CAIRO_OPERATOR_ATOP:
+    case CAIRO_OPERATOR_DEST:
+    case CAIRO_OPERATOR_DEST_OVER:
+    case CAIRO_OPERATOR_DEST_OUT:
+    case CAIRO_OPERATOR_XOR:
+    case CAIRO_OPERATOR_ADD:
+    case CAIRO_OPERATOR_SATURATE:
+    case CAIRO_OPERATOR_MULTIPLY:
+    case CAIRO_OPERATOR_SCREEN:
+    case CAIRO_OPERATOR_OVERLAY:
+    case CAIRO_OPERATOR_DARKEN:
+    case CAIRO_OPERATOR_LIGHTEN:
+    case CAIRO_OPERATOR_COLOR_DODGE:
+    case CAIRO_OPERATOR_COLOR_BURN:
+    case CAIRO_OPERATOR_HARD_LIGHT:
+    case CAIRO_OPERATOR_SOFT_LIGHT:
+    case CAIRO_OPERATOR_DIFFERENCE:
+    case CAIRO_OPERATOR_EXCLUSION:
+    case CAIRO_OPERATOR_HSL_HUE:
+    case CAIRO_OPERATOR_HSL_SATURATION:
+    case CAIRO_OPERATOR_HSL_COLOR:
+    case CAIRO_OPERATOR_HSL_LUMINOSITY:
+       return TRUE;
+    case CAIRO_OPERATOR_CLEAR:
+    case CAIRO_OPERATOR_SOURCE:
+    case CAIRO_OPERATOR_OUT:
+    case CAIRO_OPERATOR_IN:
+    case CAIRO_OPERATOR_DEST_IN:
+    case CAIRO_OPERATOR_DEST_ATOP:
+       return FALSE;
+    }
+
+    ASSERT_NOT_REACHED;
+    return FALSE;
+}
+
+uint32_t
+_cairo_operator_bounded_by_either (cairo_operator_t op)
+{
+    switch (op) {
+    default:
+       ASSERT_NOT_REACHED;
+    case CAIRO_OPERATOR_OVER:
+    case CAIRO_OPERATOR_ATOP:
+    case CAIRO_OPERATOR_DEST:
+    case CAIRO_OPERATOR_DEST_OVER:
+    case CAIRO_OPERATOR_DEST_OUT:
+    case CAIRO_OPERATOR_XOR:
+    case CAIRO_OPERATOR_ADD:
+    case CAIRO_OPERATOR_SATURATE:
+    case CAIRO_OPERATOR_MULTIPLY:
+    case CAIRO_OPERATOR_SCREEN:
+    case CAIRO_OPERATOR_OVERLAY:
+    case CAIRO_OPERATOR_DARKEN:
+    case CAIRO_OPERATOR_LIGHTEN:
+    case CAIRO_OPERATOR_COLOR_DODGE:
+    case CAIRO_OPERATOR_COLOR_BURN:
+    case CAIRO_OPERATOR_HARD_LIGHT:
+    case CAIRO_OPERATOR_SOFT_LIGHT:
+    case CAIRO_OPERATOR_DIFFERENCE:
+    case CAIRO_OPERATOR_EXCLUSION:
+    case CAIRO_OPERATOR_HSL_HUE:
+    case CAIRO_OPERATOR_HSL_SATURATION:
+    case CAIRO_OPERATOR_HSL_COLOR:
+    case CAIRO_OPERATOR_HSL_LUMINOSITY:
+       return CAIRO_OPERATOR_BOUND_BY_MASK | CAIRO_OPERATOR_BOUND_BY_SOURCE;
+    case CAIRO_OPERATOR_CLEAR:
+    case CAIRO_OPERATOR_SOURCE:
+       return CAIRO_OPERATOR_BOUND_BY_MASK;
+    case CAIRO_OPERATOR_OUT:
+    case CAIRO_OPERATOR_IN:
+    case CAIRO_OPERATOR_DEST_IN:
+    case CAIRO_OPERATOR_DEST_ATOP:
+       return 0;
+    }
+
+}
+
+#if DISABLE_SOME_FLOATING_POINT
+/* This function is identical to the C99 function lround(), except that it
+ * performs arithmetic rounding (floor(d + .5) instead of away-from-zero rounding) and
+ * has a valid input range of (INT_MIN, INT_MAX] instead of
+ * [INT_MIN, INT_MAX]. It is much faster on both x86 and FPU-less systems
+ * than other commonly used methods for rounding (lround, round, rint, lrint
+ * or float (d + 0.5)).
+ *
+ * The reason why this function is much faster on x86 than other
+ * methods is due to the fact that it avoids the fldcw instruction.
+ * This instruction incurs a large performance penalty on modern Intel
+ * processors due to how it prevents efficient instruction pipelining.
+ *
+ * The reason why this function is much faster on FPU-less systems is for
+ * an entirely different reason. All common rounding methods involve multiple
+ * floating-point operations. Each one of these operations has to be
+ * emulated in software, which adds up to be a large performance penalty.
+ * This function doesn't perform any floating-point calculations, and thus
+ * avoids this penalty.
+  */
+int
+_cairo_lround (double d)
+{
+    uint32_t top, shift_amount, output;
+    union {
+        double d;
+        uint64_t ui64;
+        uint32_t ui32[2];
+    } u;
+
+    u.d = d;
+
+    /* If the integer word order doesn't match the float word order, we swap
+     * the words of the input double. This is needed because we will be
+     * treating the whole double as a 64-bit unsigned integer. Notice that we
+     * use WORDS_BIGENDIAN to detect the integer word order, which isn't
+     * exactly correct because WORDS_BIGENDIAN refers to byte order, not word
+     * order. Thus, we are making the assumption that the byte order is the
+     * same as the integer word order which, on the modern machines that we
+     * care about, is OK.
+     */
+#if ( defined(FLOAT_WORDS_BIGENDIAN) && !defined(WORDS_BIGENDIAN)) || \
+    (!defined(FLOAT_WORDS_BIGENDIAN) &&  defined(WORDS_BIGENDIAN))
+    {
+        uint32_t temp = u.ui32[0];
+        u.ui32[0] = u.ui32[1];
+        u.ui32[1] = temp;
+    }
+#endif
+
+#ifdef WORDS_BIGENDIAN
+    #define MSW (0) /* Most Significant Word */
+    #define LSW (1) /* Least Significant Word */
+#else
+    #define MSW (1)
+    #define LSW (0)
+#endif
+
+    /* By shifting the most significant word of the input double to the
+     * right 20 places, we get the very "top" of the double where the exponent
+     * and sign bit lie.
+     */
+    top = u.ui32[MSW] >> 20;
+
+    /* Here, we calculate how much we have to shift the mantissa to normalize
+     * it to an integer value. We extract the exponent "top" by masking out the
+     * sign bit, then we calculate the shift amount by subtracting the exponent
+     * from the bias. Notice that the correct bias for 64-bit doubles is
+     * actually 1075, but we use 1053 instead for two reasons:
+     *
+     *  1) To perform rounding later on, we will first need the target
+     *     value in a 31.1 fixed-point format. Thus, the bias needs to be one
+     *     less: (1075 - 1: 1074).
+     *
+     *  2) To avoid shifting the mantissa as a full 64-bit integer (which is
+     *     costly on certain architectures), we break the shift into two parts.
+     *     First, the upper and lower parts of the mantissa are shifted
+     *     individually by a constant amount that all valid inputs will require
+     *     at the very least. This amount is chosen to be 21, because this will
+     *     allow the two parts of the mantissa to later be combined into a
+     *     single 32-bit representation, on which the remainder of the shift
+     *     will be performed. Thus, we decrease the bias by an additional 21:
+     *     (1074 - 21: 1053).
+     */
+    shift_amount = 1053 - (top & 0x7FF);
+
+    /* We are done with the exponent portion in "top", so here we shift it off
+     * the end.
+     */
+    top >>= 11;
+
+    /* Before we perform any operations on the mantissa, we need to OR in
+     * the implicit 1 at the top (see the IEEE-754 spec). We needn't mask
+     * off the sign bit nor the exponent bits because these higher bits won't
+     * make a bit of difference in the rest of our calculations.
+     */
+    u.ui32[MSW] |= 0x100000;
+
+    /* If the input double is negative, we have to decrease the mantissa
+     * by a hair. This is an important part of performing arithmetic rounding,
+     * as negative numbers must round towards positive infinity in the
+     * halfwase case of -x.5. Since "top" contains only the sign bit at this
+     * point, we can just decrease the mantissa by the value of "top".
+     */
+    u.ui64 -= top;
+
+    /* By decrementing "top", we create a bitmask with a value of either
+     * 0x0 (if the input was negative) or 0xFFFFFFFF (if the input was positive
+     * and thus the unsigned subtraction underflowed) that we'll use later.
+     */
+    top--;
+
+    /* Here, we shift the mantissa by the constant value as described above.
+     * We can emulate a 64-bit shift right by 21 through shifting the top 32
+     * bits left 11 places and ORing in the bottom 32 bits shifted 21 places
+     * to the right. Both parts of the mantissa are now packed into a single
+     * 32-bit integer. Although we severely truncate the lower part in the
+     * process, we still have enough significant bits to perform the conversion
+     * without error (for all valid inputs).
+     */
+    output = (u.ui32[MSW] << 11) | (u.ui32[LSW] >> 21);
+
+    /* Next, we perform the shift that converts the X.Y fixed-point number
+     * currently found in "output" to the desired 31.1 fixed-point format
+     * needed for the following rounding step. It is important to consider
+     * all possible values for "shift_amount" at this point:
+     *
+     * - {shift_amount < 0} Since shift_amount is an unsigned integer, it
+     *   really can't have a value less than zero. But, if the shift_amount
+     *   calculation above caused underflow (which would happen with
+     *   input > INT_MAX or input <= INT_MIN) then shift_amount will now be
+     *   a very large number, and so this shift will result in complete
+     *   garbage. But that's OK, as the input was out of our range, so our
+     *   output is undefined.
+     *
+     * - {shift_amount > 31} If the magnitude of the input was very small
+     *   (i.e. |input| << 1.0), shift_amount will have a value greater than
+     *   31. Thus, this shift will also result in garbage. After performing
+     *   the shift, we will zero-out "output" if this is the case.
+     *
+     * - {0 <= shift_amount < 32} In this case, the shift will properly convert
+     *   the mantissa into a 31.1 fixed-point number.
+     */
+    output >>= shift_amount;
+
+    /* This is where we perform rounding with the 31.1 fixed-point number.
+     * Since what we're after is arithmetic rounding, we simply add the single
+     * fractional bit into the integer part of "output", and just keep the
+     * integer part.
+     */
+    output = (output >> 1) + (output & 1);
+
+    /* Here, we zero-out the result if the magnitude if the input was very small
+     * (as explained in the section above). Notice that all input out of the
+     * valid range is also caught by this condition, which means we produce 0
+     * for all invalid input, which is a nice side effect.
+     *
+     * The most straightforward way to do this would be:
+     *
+     *      if (shift_amount > 31)
+     *          output = 0;
+     *
+     * But we can use a little trick to avoid the potential branch. The
+     * expression (shift_amount > 31) will be either 1 or 0, which when
+     * decremented will be either 0x0 or 0xFFFFFFFF (unsigned underflow),
+     * which can be used to conditionally mask away all the bits in "output"
+     * (in the 0x0 case), effectively zeroing it out. Certain, compilers would
+     * have done this for us automatically.
+     */
+    output &= ((shift_amount > 31) - 1);
+
+    /* If the input double was a negative number, then we have to negate our
+     * output. The most straightforward way to do this would be:
+     *
+     *      if (!top)
+     *          output = -output;
+     *
+     * as "top" at this point is either 0x0 (if the input was negative) or
+     * 0xFFFFFFFF (if the input was positive). But, we can use a trick to
+     * avoid the branch. Observe that the following snippet of code has the
+     * same effect as the reference snippet above:
+     *
+     *      if (!top)
+     *          output = 0 - output;
+     *      else
+     *          output = output - 0;
+     *
+     * Armed with the bitmask found in "top", we can condense the two statements
+     * into the following:
+     *
+     *      output = (output & top) - (output & ~top);
+     *
+     * where, in the case that the input double was negative, "top" will be 0,
+     * and the statement will be equivalent to:
+     *
+     *      output = (0) - (output);
+     *
+     * and if the input double was positive, "top" will be 0xFFFFFFFF, and the
+     * statement will be equivalent to:
+     *
+     *      output = (output) - (0);
+     *
+     * Which, as pointed out earlier, is equivalent to the original reference
+     * snippet.
+     */
+    output = (output & top) - (output & ~top);
+
+    return output;
+#undef MSW
+#undef LSW
+}
+#endif
+
+/* Convert a 32-bit IEEE single precision floating point number to a
+ * 'half' representation (s10.5)
+ */
+uint16_t
+_cairo_half_from_float (float f)
+{
+    union {
+       uint32_t ui;
+       float f;
+    } u;
+    int s, e, m;
+
+    u.f = f;
+    s =  (u.ui >> 16) & 0x00008000;
+    e = ((u.ui >> 23) & 0x000000ff) - (127 - 15);
+    m =   u.ui        & 0x007fffff;
+    if (e <= 0) {
+       if (e < -10) {
+           /* underflow */
+           return 0;
+       }
+
+       m = (m | 0x00800000) >> (1 - e);
+
+       /* round to nearest, round 0.5 up. */
+       if (m &  0x00001000)
+           m += 0x00002000;
+       return s | (m >> 13);
+    } else if (e == 0xff - (127 - 15)) {
+       if (m == 0) {
+           /* infinity */
+           return s | 0x7c00;
+       } else {
+           /* nan */
+           m >>= 13;
+           return s | 0x7c00 | m | (m == 0);
+       }
+    } else {
+       /* round to nearest, round 0.5 up. */
+       if (m &  0x00001000) {
+           m += 0x00002000;
+
+           if (m & 0x00800000) {
+               m =  0;
+               e += 1;
+           }
+       }
+
+       if (e > 30) {
+           /* overflow -> infinity */
+           return s | 0x7c00;
+       }
+
+       return s | (e << 10) | (m >> 13);
+    }
+}
+
+
+#ifdef _WIN32
+
+#define WIN32_LEAN_AND_MEAN
+/* We require Windows 2000 features such as ETO_PDY */
+#if !defined(WINVER) || (WINVER < 0x0500)
+# define WINVER 0x0500
+#endif
+#if !defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x0500)
+# define _WIN32_WINNT 0x0500
+#endif
+
+#include <windows.h>
+#include <io.h>
+
+#if !_WIN32_WCE
+/* tmpfile() replacement for Windows.
+ *
+ * On Windows tmpfile() creates the file in the root directory. This
+ * may fail due to unsufficient privileges. However, this isn't a
+ * problem on Windows CE so we don't use it there.
+ */
+FILE *
+_cairo_win32_tmpfile (void)
+{
+    DWORD path_len;
+    WCHAR path_name[MAX_PATH + 1];
+    WCHAR file_name[MAX_PATH + 1];
+    HANDLE handle;
+    int fd;
+    FILE *fp;
+
+    path_len = GetTempPathW (MAX_PATH, path_name);
+    if (path_len <= 0 || path_len >= MAX_PATH)
+       return NULL;
+
+    if (GetTempFileNameW (path_name, L"ps_", 0, file_name) == 0)
+       return NULL;
+
+    handle = CreateFileW (file_name,
+                        GENERIC_READ | GENERIC_WRITE,
+                        0,
+                        NULL,
+                        CREATE_ALWAYS,
+                        FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE,
+                        NULL);
+    if (handle == INVALID_HANDLE_VALUE) {
+       DeleteFileW (file_name);
+       return NULL;
+    }
+
+    fd = _open_osfhandle((intptr_t) handle, 0);
+    if (fd < 0) {
+       CloseHandle (handle);
+       return NULL;
+    }
+
+    fp = fdopen(fd, "w+b");
+    if (fp == NULL) {
+       _close(fd);
+       return NULL;
+    }
+
+    return fp;
+}
+#endif /* !_WIN32_WCE */
+
+#endif /* _WIN32 */
+
+typedef struct _cairo_intern_string {
+    cairo_hash_entry_t hash_entry;
+    int len;
+    char *string;
+} cairo_intern_string_t;
+
+static cairo_hash_table_t *_cairo_intern_string_ht;
+
+static unsigned long
+_intern_string_hash (const char *str, int len)
+{
+    const signed char *p = (const signed char *) str;
+    unsigned int h = *p;
+
+    for (p += 1; --len; p++)
+       h = (h << 5) - h + *p;
+
+    return h;
+}
+
+static cairo_bool_t
+_intern_string_equal (const void *_a, const void *_b)
+{
+    const cairo_intern_string_t *a = _a;
+    const cairo_intern_string_t *b = _b;
+
+    if (a->len != b->len)
+       return FALSE;
+
+    return memcmp (a->string, b->string, a->len) == 0;
+}
+
+cairo_status_t
+_cairo_intern_string (const char **str_inout, int len)
+{
+    char *str = (char *) *str_inout;
+    cairo_intern_string_t tmpl, *istring;
+    cairo_status_t status = CAIRO_STATUS_SUCCESS;
+
+    if (CAIRO_INJECT_FAULT ())
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    if (len < 0)
+       len = strlen (str);
+    tmpl.hash_entry.hash = _intern_string_hash (str, len);
+    tmpl.len = len;
+    tmpl.string = (char *) str;
+
+    CAIRO_MUTEX_LOCK (_cairo_intern_string_mutex);
+    if (_cairo_intern_string_ht == NULL) {
+       _cairo_intern_string_ht = _cairo_hash_table_create (_intern_string_equal);
+       if (unlikely (_cairo_intern_string_ht == NULL)) {
+           status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+           goto BAIL;
+       }
+    }
+
+    istring = _cairo_hash_table_lookup (_cairo_intern_string_ht,
+                                       &tmpl.hash_entry);
+    if (istring == NULL) {
+       istring = malloc (sizeof (cairo_intern_string_t) + len + 1);
+       if (likely (istring != NULL)) {
+           istring->hash_entry.hash = tmpl.hash_entry.hash;
+           istring->len = tmpl.len;
+           istring->string = (char *) (istring + 1);
+           memcpy (istring->string, str, len);
+           istring->string[len] = '\0';
+
+           status = _cairo_hash_table_insert (_cairo_intern_string_ht,
+                                              &istring->hash_entry);
+           if (unlikely (status)) {
+               free (istring);
+               goto BAIL;
+           }
+       } else {
+           status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+           goto BAIL;
+       }
+    }
+
+    *str_inout = istring->string;
+
+  BAIL:
+    CAIRO_MUTEX_UNLOCK (_cairo_intern_string_mutex);
+    return status;
+}
+
+static void
+_intern_string_pluck (void *entry, void *closure)
+{
+    _cairo_hash_table_remove (closure, entry);
+    free (entry);
+}
+
+void
+_cairo_intern_string_reset_static_data (void)
+{
+    CAIRO_MUTEX_LOCK (_cairo_intern_string_mutex);
+    if (_cairo_intern_string_ht != NULL) {
+       _cairo_hash_table_foreach (_cairo_intern_string_ht,
+                                  _intern_string_pluck,
+                                  _cairo_intern_string_ht);
+       _cairo_hash_table_destroy(_cairo_intern_string_ht);
+       _cairo_intern_string_ht = NULL;
+    }
+    CAIRO_MUTEX_UNLOCK (_cairo_intern_string_mutex);
+}
diff --git a/src/cairo-mono-scan-converter.c b/src/cairo-mono-scan-converter.c
new file mode 100644 (file)
index 0000000..a617f48
--- /dev/null
@@ -0,0 +1,608 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/*
+ * Copyright (c) 2011  Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "cairoint.h"
+#include "cairo-spans-private.h"
+#include "cairo-error-private.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+
+struct quorem {
+    int32_t quo;
+    int32_t rem;
+};
+
+struct edge {
+    struct edge *next, *prev;
+
+    int32_t height_left;
+    int32_t dir;
+    int32_t vertical;
+
+    int32_t dy;
+    struct quorem x;
+    struct quorem dxdy;
+};
+
+/* A collection of sorted and vertically clipped edges of the polygon.
+ * Edges are moved from the polygon to an active list while scan
+ * converting. */
+struct polygon {
+    /* The vertical clip extents. */
+    int32_t ymin, ymax;
+
+    int num_edges;
+    struct edge *edges;
+
+    /* Array of edges all starting in the same bucket. An edge is put
+     * into bucket EDGE_BUCKET_INDEX(edge->ytop, polygon->ymin) when
+     * it is added to the polygon. */
+    struct edge **y_buckets;
+
+    struct edge *y_buckets_embedded[64];
+    struct edge edges_embedded[32];
+};
+
+struct mono_scan_converter {
+    struct polygon polygon[1];
+
+    /* Leftmost edge on the current scan line. */
+    struct edge head, tail;
+    int is_vertical;
+
+    cairo_half_open_span_t *spans;
+    cairo_half_open_span_t spans_embedded[64];
+    int num_spans;
+
+    /* Clip box. */
+    int32_t xmin, xmax;
+    int32_t ymin, ymax;
+};
+
+#define I(x) _cairo_fixed_integer_round_down(x)
+
+/* Compute the floored division a/b. Assumes / and % perform symmetric
+ * division. */
+inline static struct quorem
+floored_divrem(int a, int b)
+{
+    struct quorem qr;
+    qr.quo = a/b;
+    qr.rem = a%b;
+    if ((a^b)<0 && qr.rem) {
+       qr.quo -= 1;
+       qr.rem += b;
+    }
+    return qr;
+}
+
+/* Compute the floored division (x*a)/b. Assumes / and % perform symmetric
+ * division. */
+static struct quorem
+floored_muldivrem(int x, int a, int b)
+{
+    struct quorem qr;
+    long long xa = (long long)x*a;
+    qr.quo = xa/b;
+    qr.rem = xa%b;
+    if ((xa>=0) != (b>=0) && qr.rem) {
+       qr.quo -= 1;
+       qr.rem += b;
+    }
+    return qr;
+}
+
+static cairo_status_t
+polygon_init (struct polygon *polygon, int ymin, int ymax)
+{
+    unsigned h = ymax - ymin + 1;
+
+    polygon->y_buckets = polygon->y_buckets_embedded;
+    if (h > ARRAY_LENGTH (polygon->y_buckets_embedded)) {
+       polygon->y_buckets = _cairo_malloc_ab (h, sizeof (struct edge *));
+       if (unlikely (NULL == polygon->y_buckets))
+           return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+    }
+    memset (polygon->y_buckets, 0, h * sizeof (struct edge *));
+    polygon->y_buckets[h-1] = (void *)-1;
+
+    polygon->ymin = ymin;
+    polygon->ymax = ymax;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+polygon_fini (struct polygon *polygon)
+{
+    if (polygon->y_buckets != polygon->y_buckets_embedded)
+       free (polygon->y_buckets);
+
+    if (polygon->edges != polygon->edges_embedded)
+       free (polygon->edges);
+}
+
+static void
+_polygon_insert_edge_into_its_y_bucket(struct polygon *polygon,
+                                      struct edge *e,
+                                      int y)
+{
+    struct edge **ptail = &polygon->y_buckets[y - polygon->ymin];
+    if (*ptail)
+       (*ptail)->prev = e;
+    e->next = *ptail;
+    e->prev = NULL;
+    *ptail = e;
+}
+
+inline static void
+polygon_add_edge (struct polygon *polygon,
+                 const cairo_edge_t *edge)
+{
+    struct edge *e;
+    cairo_fixed_t dx;
+    cairo_fixed_t dy;
+    int y, ytop, ybot;
+    int ymin = polygon->ymin;
+    int ymax = polygon->ymax;
+
+    y = I(edge->top);
+    ytop = MAX(y, ymin);
+
+    y = I(edge->bottom);
+    ybot = MIN(y, ymax);
+
+    if (ybot <= ytop)
+       return;
+
+    e = polygon->edges + polygon->num_edges++;
+    e->height_left = ybot - ytop;
+    e->dir = edge->dir;
+
+    dx = edge->line.p2.x - edge->line.p1.x;
+    dy = edge->line.p2.y - edge->line.p1.y;
+
+    if (dx == 0) {
+       e->vertical = TRUE;
+       e->x.quo = edge->line.p1.x;
+       e->x.rem = 0;
+       e->dxdy.quo = 0;
+       e->dxdy.rem = 0;
+       e->dy = 0;
+    } else {
+       e->vertical = FALSE;
+       e->dxdy = floored_muldivrem (dx, CAIRO_FIXED_ONE, dy);
+       e->dy = dy;
+
+       e->x = floored_muldivrem (ytop * CAIRO_FIXED_ONE + CAIRO_FIXED_FRAC_MASK/2 - edge->line.p1.y,
+                                 dx, dy);
+       e->x.quo += edge->line.p1.x;
+       e->x.rem -= dy;
+    }
+
+    _polygon_insert_edge_into_its_y_bucket (polygon, e, ytop);
+}
+
+static struct edge *
+merge_sorted_edges (struct edge *head_a, struct edge *head_b)
+{
+    struct edge *head, **next, *prev;
+    int32_t x;
+
+    prev = head_a->prev;
+    next = &head;
+    if (head_a->x.quo <= head_b->x.quo) {
+       head = head_a;
+    } else {
+       head = head_b;
+       head_b->prev = prev;
+       goto start_with_b;
+    }
+
+    do {
+       x = head_b->x.quo;
+       while (head_a != NULL && head_a->x.quo <= x) {
+           prev = head_a;
+           next = &head_a->next;
+           head_a = head_a->next;
+       }
+
+       head_b->prev = prev;
+       *next = head_b;
+       if (head_a == NULL)
+           return head;
+
+start_with_b:
+       x = head_a->x.quo;
+       while (head_b != NULL && head_b->x.quo <= x) {
+           prev = head_b;
+           next = &head_b->next;
+           head_b = head_b->next;
+       }
+
+       head_a->prev = prev;
+       *next = head_a;
+       if (head_b == NULL)
+           return head;
+    } while (1);
+}
+
+static struct edge *
+sort_edges (struct edge *list,
+           unsigned int level,
+           struct edge **head_out)
+{
+    struct edge *head_other, *remaining;
+    unsigned int i;
+
+    head_other = list->next;
+
+    if (head_other == NULL) {
+       *head_out = list;
+       return NULL;
+    }
+
+    remaining = head_other->next;
+    if (list->x.quo <= head_other->x.quo) {
+       *head_out = list;
+       head_other->next = NULL;
+    } else {
+       *head_out = head_other;
+       head_other->prev = list->prev;
+       head_other->next = list;
+       list->prev = head_other;
+       list->next = NULL;
+    }
+
+    for (i = 0; i < level && remaining; i++) {
+       remaining = sort_edges (remaining, i, &head_other);
+       *head_out = merge_sorted_edges (*head_out, head_other);
+    }
+
+    return remaining;
+}
+
+static struct edge *
+merge_unsorted_edges (struct edge *head, struct edge *unsorted)
+{
+    sort_edges (unsorted, UINT_MAX, &unsorted);
+    return merge_sorted_edges (head, unsorted);
+}
+
+inline static void
+active_list_merge_edges (struct mono_scan_converter *c, struct edge *edges)
+{
+    struct edge *e;
+
+    for (e = edges; c->is_vertical && e; e = e->next)
+       c->is_vertical = e->vertical;
+
+    c->head.next = merge_unsorted_edges (c->head.next, edges);
+}
+
+inline static void
+add_span (struct mono_scan_converter *c, int x1, int x2)
+{
+    int n;
+
+    if (x1 < c->xmin)
+       x1 = c->xmin;
+    if (x2 > c->xmax)
+       x2 = c->xmax;
+    if (x2 <= x1)
+       return;
+
+    n = c->num_spans++;
+    c->spans[n].x = x1;
+    c->spans[n].coverage = 255;
+
+    n = c->num_spans++;
+    c->spans[n].x = x2;
+    c->spans[n].coverage = 0;
+}
+
+inline static void
+row (struct mono_scan_converter *c, unsigned int mask)
+{
+    struct edge *edge = c->head.next;
+    int xstart = INT_MIN, prev_x = INT_MIN;
+    int winding = 0;
+
+    c->num_spans = 0;
+    while (&c->tail != edge) {
+       struct edge *next = edge->next;
+       int xend = I(edge->x.quo);
+
+       if (--edge->height_left) {
+           edge->x.quo += edge->dxdy.quo;
+           edge->x.rem += edge->dxdy.rem;
+           if (edge->x.rem >= 0) {
+               ++edge->x.quo;
+               edge->x.rem -= edge->dy;
+           }
+
+           if (edge->x.quo < prev_x) {
+               struct edge *pos = edge->prev;
+               pos->next = next;
+               next->prev = pos;
+               do {
+                   pos = pos->prev;
+               } while (edge->x.quo < pos->x.quo);
+               pos->next->prev = edge;
+               edge->next = pos->next;
+               edge->prev = pos;
+               pos->next = edge;
+           } else
+               prev_x = edge->x.quo;
+       } else {
+           edge->prev->next = next;
+           next->prev = edge->prev;
+       }
+
+       winding += edge->dir;
+       if ((winding & mask) == 0) {
+           if (I(next->x.quo) > xend + 1) {
+               add_span (c, xstart, xend);
+               xstart = INT_MIN;
+           }
+       } else if (xstart == INT_MIN)
+           xstart = xend;
+
+       edge = next;
+    }
+}
+
+inline static void dec (struct edge *e, int h)
+{
+    e->height_left -= h;
+    if (e->height_left == 0) {
+       e->prev->next = e->next;
+       e->next->prev = e->prev;
+    }
+}
+
+static cairo_status_t
+_mono_scan_converter_init(struct mono_scan_converter *c,
+                         int xmin, int ymin,
+                         int xmax, int ymax)
+{
+    cairo_status_t status;
+
+    status = polygon_init (c->polygon, ymin, ymax);
+    if  (unlikely (status))
+       return status;
+
+    if (xmax - xmin > ARRAY_LENGTH(c->spans_embedded)) {
+       c->spans = _cairo_malloc_ab (xmax - xmin,
+                                    sizeof (cairo_half_open_span_t));
+       if (unlikely (c->spans == NULL)) {
+           polygon_fini (c->polygon);
+           return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+       }
+    } else
+       c->spans = c->spans_embedded;
+
+    c->xmin = xmin;
+    c->xmax = xmax;
+    c->ymin = ymin;
+    c->ymax = ymax;
+
+    c->head.vertical = 1;
+    c->head.height_left = INT_MAX;
+    c->head.x.quo = _cairo_fixed_from_int (_cairo_fixed_integer_part (INT_MIN));
+    c->head.prev = NULL;
+    c->head.next = &c->tail;
+    c->tail.prev = &c->head;
+    c->tail.next = NULL;
+    c->tail.x.quo = _cairo_fixed_from_int (_cairo_fixed_integer_part (INT_MAX));
+    c->tail.height_left = INT_MAX;
+    c->tail.vertical = 1;
+
+    c->is_vertical = 1;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_mono_scan_converter_fini(struct mono_scan_converter *self)
+{
+    if (self->spans != self->spans_embedded)
+       free (self->spans);
+
+    polygon_fini(self->polygon);
+}
+
+static cairo_status_t
+mono_scan_converter_allocate_edges(struct mono_scan_converter *c,
+                                  int num_edges)
+
+{
+    c->polygon->num_edges = 0;
+    c->polygon->edges = c->polygon->edges_embedded;
+    if (num_edges > ARRAY_LENGTH (c->polygon->edges_embedded)) {
+       c->polygon->edges = _cairo_malloc_ab (num_edges, sizeof (struct edge));
+       if (unlikely (c->polygon->edges == NULL))
+           return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+mono_scan_converter_add_edge (struct mono_scan_converter *c,
+                             const cairo_edge_t *edge)
+{
+    polygon_add_edge (c->polygon, edge);
+}
+
+static void
+step_edges (struct mono_scan_converter *c, int count)
+{
+    struct edge *edge;
+
+    for (edge = c->head.next; edge != &c->tail; edge = edge->next) {
+       edge->height_left -= count;
+       if (! edge->height_left) {
+           edge->prev->next = edge->next;
+           edge->next->prev = edge->prev;
+       }
+    }
+}
+
+static cairo_status_t
+mono_scan_converter_render(struct mono_scan_converter *c,
+                          unsigned int winding_mask,
+                          cairo_span_renderer_t *renderer)
+{
+    struct polygon *polygon = c->polygon;
+    int i, j, h = c->ymax - c->ymin;
+    cairo_status_t status;
+
+    for (i = 0; i < h; i = j) {
+       j = i + 1;
+
+       if (polygon->y_buckets[i])
+           active_list_merge_edges (c, polygon->y_buckets[i]);
+
+       if (c->is_vertical) {
+           int min_height;
+           struct edge *e;
+
+           e = c->head.next;
+           min_height = e->height_left;
+           while (e != &c->tail) {
+               if (e->height_left < min_height)
+                   min_height = e->height_left;
+               e = e->next;
+           }
+
+           while (--min_height >= 1 && polygon->y_buckets[j] == NULL)
+               j++;
+           if (j != i + 1)
+               step_edges (c, j - (i + 1));
+       }
+
+       row (c, winding_mask);
+       if (c->num_spans) {
+           status = renderer->render_rows (renderer, c->ymin+i, j-i,
+                                           c->spans, c->num_spans);
+           if (unlikely (status))
+               return status;
+       }
+
+       /* XXX recompute after dropping edges? */
+       if (c->head.next == &c->tail)
+           c->is_vertical = 1;
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+struct _cairo_mono_scan_converter {
+    cairo_scan_converter_t base;
+
+    struct mono_scan_converter converter[1];
+    cairo_fill_rule_t fill_rule;
+};
+
+typedef struct _cairo_mono_scan_converter cairo_mono_scan_converter_t;
+
+static void
+_cairo_mono_scan_converter_destroy (void *converter)
+{
+    cairo_mono_scan_converter_t *self = converter;
+    _mono_scan_converter_fini (self->converter);
+    free(self);
+}
+
+cairo_status_t
+_cairo_mono_scan_converter_add_polygon (void           *converter,
+                                      const cairo_polygon_t *polygon)
+{
+    cairo_mono_scan_converter_t *self = converter;
+    cairo_status_t status;
+    int i;
+
+#if 0
+    FILE *file = fopen ("polygon.txt", "w");
+    _cairo_debug_print_polygon (file, polygon);
+    fclose (file);
+#endif
+
+    status = mono_scan_converter_allocate_edges (self->converter,
+                                                polygon->num_edges);
+    if (unlikely (status))
+       return status;
+
+    for (i = 0; i < polygon->num_edges; i++)
+        mono_scan_converter_add_edge (self->converter, &polygon->edges[i]);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_mono_scan_converter_generate (void                      *converter,
+                                   cairo_span_renderer_t       *renderer)
+{
+    cairo_mono_scan_converter_t *self = converter;
+
+    return mono_scan_converter_render (self->converter,
+                                      self->fill_rule == CAIRO_FILL_RULE_WINDING ? ~0 : 1,
+                                      renderer);
+}
+
+cairo_scan_converter_t *
+_cairo_mono_scan_converter_create (int                 xmin,
+                                 int                   ymin,
+                                 int                   xmax,
+                                 int                   ymax,
+                                 cairo_fill_rule_t     fill_rule)
+{
+    cairo_mono_scan_converter_t *self;
+    cairo_status_t status;
+
+    self = malloc (sizeof(struct _cairo_mono_scan_converter));
+    if (unlikely (self == NULL)) {
+       status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+       goto bail_nomem;
+    }
+
+    self->base.destroy = _cairo_mono_scan_converter_destroy;
+    self->base.generate = _cairo_mono_scan_converter_generate;
+
+    status = _mono_scan_converter_init (self->converter,
+                                       xmin, ymin, xmax, ymax);
+    if (unlikely (status))
+       goto bail;
+
+    self->fill_rule = fill_rule;
+
+    return &self->base;
+
+ bail:
+    self->base.destroy(&self->base);
+ bail_nomem:
+    return _cairo_scan_converter_create_in_error (status);
+}
diff --git a/src/cairo-mutex-impl-private.h b/src/cairo-mutex-impl-private.h
new file mode 100644 (file)
index 0000000..25223f3
--- /dev/null
@@ -0,0 +1,278 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ * Copyright © 2005,2007 Red Hat, Inc.
+ * Copyright © 2007 Mathias Hasselmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *     Carl D. Worth <cworth@cworth.org>
+ *     Mathias Hasselmann <mathias.hasselmann@gmx.de>
+ *     Behdad Esfahbod <behdad@behdad.org>
+ */
+
+#ifndef CAIRO_MUTEX_IMPL_PRIVATE_H
+#define CAIRO_MUTEX_IMPL_PRIVATE_H
+
+#include "cairo.h"
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#if HAVE_LOCKDEP
+#include <lockdep.h>
+#endif
+
+/* A fully qualified no-operation statement */
+#define CAIRO_MUTEX_IMPL_NOOP  do {/*no-op*/} while (0)
+/* And one that evaluates its argument once */
+#define CAIRO_MUTEX_IMPL_NOOP1(expr)        do { (void)(expr); } while (0)
+/* Note: 'if (expr) {}' is an alternative to '(void)(expr);' that will 'use' the
+ * result of __attribute__((warn_used_result)) functions. */
+
+/* Cairo mutex implementation:
+ *
+ * Any new mutex implementation needs to do the following:
+ *
+ * - Condition on the right header or feature.  Headers are
+ *   preferred as eg. you still can use win32 mutex implementation
+ *   on a win32 system even if you do not compile the win32
+ *   surface/backend.
+ *
+ * - typedef #cairo_mutex_impl_t to the proper mutex type on your target
+ *   system.  Note that you may or may not need to use a pointer,
+ *   depending on what kinds of initialization your mutex
+ *   implementation supports.  No trailing semicolon needed.
+ *   You should be able to compile the following snippet (don't try
+ *   running it):
+ *
+ *   <programlisting>
+ *     cairo_mutex_impl_t _cairo_some_mutex;
+ *   </programlisting>
+ *
+ * - #define %CAIRO_MUTEX_IMPL_<NAME> 1 with suitable name for your platform.  You
+ *   can later use this symbol in cairo-system.c.
+ *
+ * - #define CAIRO_MUTEX_IMPL_LOCK(mutex) and CAIRO_MUTEX_IMPL_UNLOCK(mutex) to
+ *   proper statement to lock/unlock the mutex object passed in.
+ *   You can (and should) assume that the mutex is already
+ *   initialized, and is-not-already-locked/is-locked,
+ *   respectively.  Use the "do { ... } while (0)" idiom if necessary.
+ *   No trailing semicolons are needed (in any macro you define here).
+ *   You should be able to compile the following snippet:
+ *
+ *   <programlisting>
+ *     cairo_mutex_impl_t _cairo_some_mutex;
+ *
+ *      if (1)
+ *          CAIRO_MUTEX_IMPL_LOCK (_cairo_some_mutex);
+ *      else
+ *          CAIRO_MUTEX_IMPL_UNLOCK (_cairo_some_mutex);
+ *   </programlisting>
+ *
+ * - #define %CAIRO_MUTEX_IMPL_NIL_INITIALIZER to something that can
+ *   initialize the #cairo_mutex_impl_t type you defined.  Most of the
+ *   time one of 0, %NULL, or {} works.  At this point
+ *   you should be able to compile the following snippet:
+ *
+ *   <programlisting>
+ *     cairo_mutex_impl_t _cairo_some_mutex = CAIRO_MUTEX_IMPL_NIL_INITIALIZER;
+ *
+ *      if (1)
+ *          CAIRO_MUTEX_IMPL_LOCK (_cairo_some_mutex);
+ *      else
+ *          CAIRO_MUTEX_IMPL_UNLOCK (_cairo_some_mutex);
+ *   </programlisting>
+ *
+ * - If the above code is not enough to initialize a mutex on
+ *   your platform, #define CAIRO_MUTEX_IMPL_INIT(mutex) to statement
+ *   to initialize the mutex (allocate resources, etc).  Such that
+ *   you should be able to compile AND RUN the following snippet:
+ *
+ *   <programlisting>
+ *     cairo_mutex_impl_t _cairo_some_mutex = CAIRO_MUTEX_IMPL_NIL_INITIALIZER;
+ *
+ *      CAIRO_MUTEX_IMPL_INIT (_cairo_some_mutex);
+ *
+ *      if (1)
+ *          CAIRO_MUTEX_IMPL_LOCK (_cairo_some_mutex);
+ *      else
+ *          CAIRO_MUTEX_IMPL_UNLOCK (_cairo_some_mutex);
+ *   </programlisting>
+ *
+ * - If you define CAIRO_MUTEX_IMPL_INIT(mutex), cairo will use it to
+ *   initialize all static mutex'es.  If for any reason that should
+ *   not happen (eg. %CAIRO_MUTEX_IMPL_INIT is just a faster way than
+ *   what cairo does using %CAIRO_MUTEX_IMPL_NIL_INITIALIZER), then
+ *   <programlisting>
+ *      #define CAIRO_MUTEX_IMPL_INITIALIZE() CAIRO_MUTEX_IMPL_NOOP
+ *   </programlisting>
+ *
+ * - If your system supports freeing a mutex object (deallocating
+ *   resources, etc), then #define CAIRO_MUTEX_IMPL_FINI(mutex) to do
+ *   that.
+ *
+ * - If you define CAIRO_MUTEX_IMPL_FINI(mutex), cairo will use it to
+ *   define a finalizer function to finalize all static mutex'es.
+ *   However, it's up to you to call CAIRO_MUTEX_IMPL_FINALIZE() at
+ *   proper places, eg. when the system is unloading the cairo library.
+ *   So, if for any reason finalizing static mutex'es is not needed
+ *   (eg. you never call CAIRO_MUTEX_IMPL_FINALIZE()), then
+ *   <programlisting>
+ *      #define CAIRO_MUTEX_IMPL_FINALIZE() CAIRO_MUTEX_IMPL_NOOP
+ *   </programlisting>
+ *
+ * - That is all.  If for any reason you think the above API is
+ *   not enough to implement #cairo_mutex_impl_t on your system, please
+ *   stop and write to the cairo mailing list about it.  DO NOT
+ *   poke around cairo-mutex-private.h for possible solutions.
+ */
+
+#if CAIRO_NO_MUTEX
+
+/* No mutexes */
+
+  typedef int cairo_mutex_impl_t;
+
+# define CAIRO_MUTEX_IMPL_NO 1
+# define CAIRO_MUTEX_IMPL_INITIALIZE() CAIRO_MUTEX_IMPL_NOOP
+# define CAIRO_MUTEX_IMPL_LOCK(mutex) CAIRO_MUTEX_IMPL_NOOP1(mutex)
+# define CAIRO_MUTEX_IMPL_UNLOCK(mutex) CAIRO_MUTEX_IMPL_NOOP1(mutex)
+# define CAIRO_MUTEX_IMPL_NIL_INITIALIZER 0
+
+# define CAIRO_MUTEX_HAS_RECURSIVE_IMPL 1
+
+  typedef int cairo_recursive_mutex_impl_t;
+
+# define CAIRO_RECURSIVE_MUTEX_IMPL_INIT(mutex)
+# define CAIRO_RECURSIVE_MUTEX_IMPL_NIL_INITIALIZER 0
+
+#elif defined(_WIN32) /******************************************************/
+
+#define WIN32_LEAN_AND_MEAN
+/* We require Windows 2000 features such as ETO_PDY */
+#if !defined(WINVER) || (WINVER < 0x0500)
+# define WINVER 0x0500
+#endif
+#if !defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x0500)
+# define _WIN32_WINNT 0x0500
+#endif
+
+# include <windows.h>
+
+  typedef CRITICAL_SECTION cairo_mutex_impl_t;
+
+# define CAIRO_MUTEX_IMPL_WIN32 1
+# define CAIRO_MUTEX_IMPL_LOCK(mutex) EnterCriticalSection (&(mutex))
+# define CAIRO_MUTEX_IMPL_UNLOCK(mutex) LeaveCriticalSection (&(mutex))
+# define CAIRO_MUTEX_IMPL_INIT(mutex) InitializeCriticalSection (&(mutex))
+# define CAIRO_MUTEX_IMPL_FINI(mutex) DeleteCriticalSection (&(mutex))
+# define CAIRO_MUTEX_IMPL_NIL_INITIALIZER { NULL, 0, 0, NULL, NULL, 0 }
+
+#elif defined __OS2__ /******************************************************/
+
+# define INCL_BASE
+# define INCL_PM
+# include <os2.h>
+
+  typedef HMTX cairo_mutex_impl_t;
+
+# define CAIRO_MUTEX_IMPL_OS2 1
+# define CAIRO_MUTEX_IMPL_LOCK(mutex) DosRequestMutexSem(mutex, SEM_INDEFINITE_WAIT)
+# define CAIRO_MUTEX_IMPL_UNLOCK(mutex) DosReleaseMutexSem(mutex)
+# define CAIRO_MUTEX_IMPL_INIT(mutex) DosCreateMutexSem (NULL, &(mutex), 0L, FALSE)
+# define CAIRO_MUTEX_IMPL_FINI(mutex) DosCloseMutexSem (mutex)
+# define CAIRO_MUTEX_IMPL_NIL_INITIALIZER 0
+
+#elif CAIRO_HAS_BEOS_SURFACE /***********************************************/
+
+  typedef BLocker* cairo_mutex_impl_t;
+
+# define CAIRO_MUTEX_IMPL_BEOS 1
+# define CAIRO_MUTEX_IMPL_LOCK(mutex) (mutex)->Lock()
+# define CAIRO_MUTEX_IMPL_UNLOCK(mutex) (mutex)->Unlock()
+# define CAIRO_MUTEX_IMPL_INIT(mutex) (mutex) = new BLocker()
+# define CAIRO_MUTEX_IMPL_FINI(mutex) delete (mutex)
+# define CAIRO_MUTEX_IMPL_NIL_INITIALIZER NULL
+
+#elif CAIRO_HAS_PTHREAD /* and finally if there are no native mutexes ********/
+
+# include <pthread.h>
+
+  typedef pthread_mutex_t cairo_mutex_impl_t;
+  typedef pthread_mutex_t cairo_recursive_mutex_impl_t;
+
+# define CAIRO_MUTEX_IMPL_PTHREAD 1
+#if HAVE_LOCKDEP
+/* expose all mutexes to the validator */
+# define CAIRO_MUTEX_IMPL_INIT(mutex) pthread_mutex_init (&(mutex), NULL)
+#endif
+# define CAIRO_MUTEX_IMPL_LOCK(mutex) pthread_mutex_lock (&(mutex))
+# define CAIRO_MUTEX_IMPL_UNLOCK(mutex) pthread_mutex_unlock (&(mutex))
+#if HAVE_LOCKDEP
+# define CAIRO_MUTEX_IS_LOCKED(mutex) LOCKDEP_IS_LOCKED (&(mutex))
+# define CAIRO_MUTEX_IS_UNLOCKED(mutex) LOCKDEP_IS_UNLOCKED (&(mutex))
+#endif
+# define CAIRO_MUTEX_IMPL_FINI(mutex) pthread_mutex_destroy (&(mutex))
+#if ! HAVE_LOCKDEP
+# define CAIRO_MUTEX_IMPL_FINALIZE() CAIRO_MUTEX_IMPL_NOOP
+#endif
+# define CAIRO_MUTEX_IMPL_NIL_INITIALIZER PTHREAD_MUTEX_INITIALIZER
+
+# define CAIRO_MUTEX_HAS_RECURSIVE_IMPL 1
+# define CAIRO_RECURSIVE_MUTEX_IMPL_INIT(mutex) do { \
+    pthread_mutexattr_t attr; \
+    pthread_mutexattr_init (&attr); \
+    pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_RECURSIVE); \
+    pthread_mutex_init (&(mutex), &attr); \
+    pthread_mutexattr_destroy (&attr); \
+} while (0)
+# define CAIRO_RECURSIVE_MUTEX_IMPL_NIL_INITIALIZER PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP
+
+#else /**********************************************************************/
+
+# error "XXX: No mutex implementation found.  Cairo will not work with multiple threads.  Define CAIRO_NO_MUTEX to 1 to acknowledge and accept this limitation and compile cairo without thread-safety support."
+
+#endif
+
+/* By default mutex implementations are assumed to be recursive */
+#if ! CAIRO_MUTEX_HAS_RECURSIVE_IMPL
+
+# define CAIRO_MUTEX_HAS_RECURSIVE_IMPL 1
+
+  typedef cairo_mutex_impl_t cairo_recursive_mutex_impl_t;
+
+# define CAIRO_RECURSIVE_MUTEX_IMPL_INIT(mutex) CAIRO_MUTEX_IMPL_INIT(mutex)
+# define CAIRO_RECURSIVE_MUTEX_IMPL_NIL_INITIALIZER CAIRO_MUTEX_IMPL_NIL_INITIALIZER
+
+#endif
+
+#endif
diff --git a/src/cairo-mutex-list-private.h b/src/cairo-mutex-list-private.h
new file mode 100644 (file)
index 0000000..4016f8e
--- /dev/null
@@ -0,0 +1,77 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2007 Mathias Hasselmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * Contributor(s):
+ *     Mathias Hasselmann <mathias.hasselmann@gmx.de>
+ */
+
+#ifndef CAIRO_FEATURES_H
+/* This block is to just make this header file standalone */
+#define CAIRO_MUTEX_DECLARE(mutex)
+#endif
+
+CAIRO_MUTEX_DECLARE (_cairo_pattern_solid_surface_cache_lock)
+
+CAIRO_MUTEX_DECLARE (_cairo_image_solid_cache_mutex)
+
+CAIRO_MUTEX_DECLARE (_cairo_toy_font_face_mutex)
+CAIRO_MUTEX_DECLARE (_cairo_intern_string_mutex)
+CAIRO_MUTEX_DECLARE (_cairo_scaled_font_map_mutex)
+CAIRO_MUTEX_DECLARE (_cairo_scaled_glyph_page_cache_mutex)
+CAIRO_MUTEX_DECLARE (_cairo_scaled_font_error_mutex)
+
+#if CAIRO_HAS_FT_FONT
+CAIRO_MUTEX_DECLARE (_cairo_ft_unscaled_font_map_mutex)
+#endif
+
+#if CAIRO_HAS_WIN32_FONT
+CAIRO_MUTEX_DECLARE (_cairo_win32_font_face_mutex)
+#endif
+
+#if CAIRO_HAS_XLIB_SURFACE
+CAIRO_MUTEX_DECLARE (_cairo_xlib_display_mutex)
+#endif
+
+#if CAIRO_HAS_XCB_SURFACE
+CAIRO_MUTEX_DECLARE (_cairo_xcb_connections_mutex)
+#endif
+
+#if CAIRO_HAS_GL_SURFACE
+CAIRO_MUTEX_DECLARE (_cairo_gl_context_mutex)
+#endif
+
+#if !defined (HAS_ATOMIC_OPS) || defined (ATOMIC_OP_NEEDS_MEMORY_BARRIER)
+CAIRO_MUTEX_DECLARE (_cairo_atomic_mutex)
+#endif
+
+#if CAIRO_HAS_DRM_SURFACE
+CAIRO_MUTEX_DECLARE (_cairo_drm_device_mutex)
+#endif
+/* Undefine, to err on unintended inclusion */
+#undef   CAIRO_MUTEX_DECLARE
diff --git a/src/cairo-mutex-private.h b/src/cairo-mutex-private.h
new file mode 100644 (file)
index 0000000..61a7160
--- /dev/null
@@ -0,0 +1,67 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ * Copyright © 2005,2007 Red Hat, Inc.
+ * Copyright © 2007 Mathias Hasselmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *     Carl D. Worth <cworth@cworth.org>
+ *     Mathias Hasselmann <mathias.hasselmann@gmx.de>
+ *     Behdad Esfahbod <behdad@behdad.org>
+ */
+
+#ifndef CAIRO_MUTEX_PRIVATE_H
+#define CAIRO_MUTEX_PRIVATE_H
+
+#include "cairo-mutex-type-private.h"
+
+CAIRO_BEGIN_DECLS
+
+#if _CAIRO_MUTEX_IMPL_USE_STATIC_INITIALIZER
+cairo_private void _cairo_mutex_initialize (void);
+#endif
+#if _CAIRO_MUTEX_IMPL_USE_STATIC_FINALIZER
+cairo_private void _cairo_mutex_finalize (void);
+#endif
+/* only if using static initializer and/or finalizer define the boolean */
+#if _CAIRO_MUTEX_IMPL_USE_STATIC_INITIALIZER || _CAIRO_MUTEX_IMPL_USE_STATIC_FINALIZER
+  cairo_private extern cairo_bool_t _cairo_mutex_initialized;
+#endif
+
+/* Finally, extern the static mutexes and undef */
+
+#define CAIRO_MUTEX_DECLARE(mutex) cairo_private extern cairo_mutex_t mutex;
+#include "cairo-mutex-list-private.h"
+#undef CAIRO_MUTEX_DECLARE
+
+CAIRO_END_DECLS
+
+#endif
diff --git a/src/cairo-mutex-type-private.h b/src/cairo-mutex-type-private.h
new file mode 100644 (file)
index 0000000..e8c4939
--- /dev/null
@@ -0,0 +1,194 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ * Copyright © 2005,2007 Red Hat, Inc.
+ * Copyright © 2007 Mathias Hasselmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *     Carl D. Worth <cworth@cworth.org>
+ *     Mathias Hasselmann <mathias.hasselmann@gmx.de>
+ *     Behdad Esfahbod <behdad@behdad.org>
+ */
+
+#ifndef CAIRO_MUTEX_TYPE_PRIVATE_H
+#define CAIRO_MUTEX_TYPE_PRIVATE_H
+
+#include "cairo-compiler-private.h"
+#include "cairo-mutex-impl-private.h"
+
+/* Only the following four are mandatory at this point */
+#ifndef CAIRO_MUTEX_IMPL_LOCK
+# error "CAIRO_MUTEX_IMPL_LOCK not defined.  Check cairo-mutex-impl-private.h."
+#endif
+#ifndef CAIRO_MUTEX_IMPL_UNLOCK
+# error "CAIRO_MUTEX_IMPL_UNLOCK not defined.  Check cairo-mutex-impl-private.h."
+#endif
+#ifndef CAIRO_MUTEX_IMPL_NIL_INITIALIZER
+# error "CAIRO_MUTEX_IMPL_NIL_INITIALIZER not defined.  Check cairo-mutex-impl-private.h."
+#endif
+#ifndef CAIRO_RECURSIVE_MUTEX_IMPL_INIT
+# error "CAIRO_RECURSIVE_MUTEX_IMPL_INIT not defined.  Check cairo-mutex-impl-private.h."
+#endif
+
+
+/* make sure implementations don't fool us: we decide these ourself */
+#undef _CAIRO_MUTEX_IMPL_USE_STATIC_INITIALIZER
+#undef _CAIRO_MUTEX_IMPL_USE_STATIC_FINALIZER
+
+
+#ifdef CAIRO_MUTEX_IMPL_INIT
+
+/* If %CAIRO_MUTEX_IMPL_INIT is defined, we may need to initialize all
+ * static mutex'es. */
+# ifndef CAIRO_MUTEX_IMPL_INITIALIZE
+#  define CAIRO_MUTEX_IMPL_INITIALIZE() do {   \
+       if (!_cairo_mutex_initialized)  \
+           _cairo_mutex_initialize (); \
+    } while(0)
+
+/* and make sure we implement the above */
+#  define _CAIRO_MUTEX_IMPL_USE_STATIC_INITIALIZER 1
+# endif /* CAIRO_MUTEX_IMPL_INITIALIZE */
+
+#else /* no CAIRO_MUTEX_IMPL_INIT */
+
+/* Otherwise we probably don't need to initialize static mutex'es, */
+# ifndef CAIRO_MUTEX_IMPL_INITIALIZE
+#  define CAIRO_MUTEX_IMPL_INITIALIZE() CAIRO_MUTEX_IMPL_NOOP
+# endif /* CAIRO_MUTEX_IMPL_INITIALIZE */
+
+/* and dynamic ones can be initialized using the static initializer. */
+# define CAIRO_MUTEX_IMPL_INIT(mutex) do {                             \
+      cairo_mutex_t _tmp_mutex = CAIRO_MUTEX_IMPL_NIL_INITIALIZER;     \
+      memcpy (&(mutex), &_tmp_mutex, sizeof (_tmp_mutex));     \
+  } while (0)
+
+#endif /* CAIRO_MUTEX_IMPL_INIT */
+
+#ifdef CAIRO_MUTEX_IMPL_FINI
+
+/* If %CAIRO_MUTEX_IMPL_FINI is defined, we may need to finalize all
+ * static mutex'es. */
+# ifndef CAIRO_MUTEX_IMPL_FINALIZE
+#  define CAIRO_MUTEX_IMPL_FINALIZE() do {     \
+       if (_cairo_mutex_initialized)   \
+           _cairo_mutex_finalize ();   \
+    } while(0)
+
+/* and make sure we implement the above */
+#  define _CAIRO_MUTEX_IMPL_USE_STATIC_FINALIZER 1
+# endif /* CAIRO_MUTEX_IMPL_FINALIZE */
+
+#else /* no CAIRO_MUTEX_IMPL_FINI */
+
+/* Otherwise we probably don't need to finalize static mutex'es, */
+# ifndef CAIRO_MUTEX_IMPL_FINALIZE
+#  define CAIRO_MUTEX_IMPL_FINALIZE() CAIRO_MUTEX_IMPL_NOOP
+# endif /* CAIRO_MUTEX_IMPL_FINALIZE */
+
+/* neither do the dynamic ones. */
+# define CAIRO_MUTEX_IMPL_FINI(mutex)  CAIRO_MUTEX_IMPL_NOOP1(mutex)
+
+#endif /* CAIRO_MUTEX_IMPL_FINI */
+
+
+#ifndef _CAIRO_MUTEX_IMPL_USE_STATIC_INITIALIZER
+#define _CAIRO_MUTEX_IMPL_USE_STATIC_INITIALIZER 0
+#endif
+#ifndef _CAIRO_MUTEX_IMPL_USE_STATIC_FINALIZER
+#define _CAIRO_MUTEX_IMPL_USE_STATIC_FINALIZER 0
+#endif
+
+
+/* Make sure everything we want is defined */
+#ifndef CAIRO_MUTEX_IMPL_INITIALIZE
+# error "CAIRO_MUTEX_IMPL_INITIALIZE not defined"
+#endif
+#ifndef CAIRO_MUTEX_IMPL_FINALIZE
+# error "CAIRO_MUTEX_IMPL_FINALIZE not defined"
+#endif
+#ifndef CAIRO_MUTEX_IMPL_LOCK
+# error "CAIRO_MUTEX_IMPL_LOCK not defined"
+#endif
+#ifndef CAIRO_MUTEX_IMPL_UNLOCK
+# error "CAIRO_MUTEX_IMPL_UNLOCK not defined"
+#endif
+#ifndef CAIRO_MUTEX_IMPL_INIT
+# error "CAIRO_MUTEX_IMPL_INIT not defined"
+#endif
+#ifndef CAIRO_MUTEX_IMPL_FINI
+# error "CAIRO_MUTEX_IMPL_FINI not defined"
+#endif
+#ifndef CAIRO_MUTEX_IMPL_NIL_INITIALIZER
+# error "CAIRO_MUTEX_IMPL_NIL_INITIALIZER not defined"
+#endif
+
+
+/* Public interface. */
+
+/* By default it simply uses the implementation provided.
+ * But we can provide for debugging features by overriding them */
+
+#ifndef CAIRO_MUTEX_DEBUG
+typedef cairo_mutex_impl_t cairo_mutex_t;
+typedef cairo_recursive_mutex_impl_t cairo_recursive_mutex_t;
+#else
+# define cairo_mutex_t                 cairo_mutex_impl_t
+#endif
+
+#define CAIRO_MUTEX_INITIALIZE         CAIRO_MUTEX_IMPL_INITIALIZE
+#define CAIRO_MUTEX_FINALIZE           CAIRO_MUTEX_IMPL_FINALIZE
+#define CAIRO_MUTEX_LOCK               CAIRO_MUTEX_IMPL_LOCK
+#define CAIRO_MUTEX_UNLOCK             CAIRO_MUTEX_IMPL_UNLOCK
+#define CAIRO_MUTEX_INIT               CAIRO_MUTEX_IMPL_INIT
+#define CAIRO_MUTEX_FINI               CAIRO_MUTEX_IMPL_FINI
+#define CAIRO_MUTEX_NIL_INITIALIZER    CAIRO_MUTEX_IMPL_NIL_INITIALIZER
+
+#define CAIRO_RECURSIVE_MUTEX_INIT             CAIRO_RECURSIVE_MUTEX_IMPL_INIT
+#define CAIRO_RECURSIVE_MUTEX_NIL_INITIALIZER  CAIRO_RECURSIVE_MUTEX_IMPL_NIL_INITIALIZER
+
+#ifndef CAIRO_MUTEX_IS_LOCKED
+# define CAIRO_MUTEX_IS_LOCKED(name) 1
+#endif
+#ifndef CAIRO_MUTEX_IS_UNLOCKED
+# define CAIRO_MUTEX_IS_UNLOCKED(name) 1
+#endif
+
+
+/* Debugging support */
+
+#ifdef CAIRO_MUTEX_DEBUG
+
+/* TODO add mutex debugging facilities here (eg deadlock detection) */
+
+#endif /* CAIRO_MUTEX_DEBUG */
+
+#endif
diff --git a/src/cairo-mutex.c b/src/cairo-mutex.c
new file mode 100644 (file)
index 0000000..0a31dce
--- /dev/null
@@ -0,0 +1,82 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2007 Mathias Hasselmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * Contributor(s):
+ *     Mathias Hasselmann <mathias.hasselmann@gmx.de>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-mutex-private.h"
+
+#define CAIRO_MUTEX_DECLARE(mutex) cairo_mutex_t mutex = CAIRO_MUTEX_NIL_INITIALIZER;
+#include "cairo-mutex-list-private.h"
+#undef   CAIRO_MUTEX_DECLARE
+
+#if _CAIRO_MUTEX_IMPL_USE_STATIC_INITIALIZER || _CAIRO_MUTEX_IMPL_USE_STATIC_FINALIZER
+
+# if _CAIRO_MUTEX_IMPL_USE_STATIC_INITIALIZER
+#  define _CAIRO_MUTEX_IMPL_INITIALIZED_DEFAULT_VALUE FALSE
+# else
+#  define _CAIRO_MUTEX_IMPL_INITIALIZED_DEFAULT_VALUE TRUE
+# endif
+
+cairo_bool_t _cairo_mutex_initialized = _CAIRO_MUTEX_IMPL_INITIALIZED_DEFAULT_VALUE;
+
+# undef _CAIRO_MUTEX_IMPL_INITIALIZED_DEFAULT_VALUE
+
+#endif
+
+#if _CAIRO_MUTEX_IMPL_USE_STATIC_INITIALIZER
+void _cairo_mutex_initialize (void)
+{
+    if (_cairo_mutex_initialized)
+        return;
+
+    _cairo_mutex_initialized = TRUE;
+
+#define  CAIRO_MUTEX_DECLARE(mutex) CAIRO_MUTEX_INIT (mutex);
+#include "cairo-mutex-list-private.h"
+#undef   CAIRO_MUTEX_DECLARE
+}
+#endif
+
+#if _CAIRO_MUTEX_IMPL_USE_STATIC_FINALIZER
+void _cairo_mutex_finalize (void)
+{
+    if (!_cairo_mutex_initialized)
+        return;
+
+    _cairo_mutex_initialized = FALSE;
+
+#define  CAIRO_MUTEX_DECLARE(mutex) CAIRO_MUTEX_FINI (mutex);
+#include "cairo-mutex-list-private.h"
+#undef   CAIRO_MUTEX_DECLARE
+}
+#endif
diff --git a/src/cairo-no-compositor.c b/src/cairo-no-compositor.c
new file mode 100644 (file)
index 0000000..1602a12
--- /dev/null
@@ -0,0 +1,107 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ * Copyright © 2005 Red Hat, Inc.
+ * Copyright © 2011 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *     Carl D. Worth <cworth@cworth.org>
+ *      Joonas Pihlaja <jpihlaja@cc.helsinki.fi>
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-compositor-private.h"
+
+static cairo_int_status_t
+_cairo_no_compositor_paint (const cairo_compositor_t *_compositor,
+                           cairo_composite_rectangles_t *extents)
+{
+    ASSERT_NOT_REACHED;
+    return CAIRO_INT_STATUS_NOTHING_TO_DO;
+}
+
+static cairo_int_status_t
+_cairo_no_compositor_mask (const cairo_compositor_t *compositor,
+                          cairo_composite_rectangles_t *extents)
+{
+    ASSERT_NOT_REACHED;
+    return CAIRO_INT_STATUS_NOTHING_TO_DO;
+}
+
+static cairo_int_status_t
+_cairo_no_compositor_stroke (const cairo_compositor_t *_compositor,
+                            cairo_composite_rectangles_t *extents,
+                            const cairo_path_fixed_t   *path,
+                            const cairo_stroke_style_t *style,
+                            const cairo_matrix_t       *ctm,
+                            const cairo_matrix_t       *ctm_inverse,
+                            double              tolerance,
+                            cairo_antialias_t   antialias)
+{
+    ASSERT_NOT_REACHED;
+    return CAIRO_INT_STATUS_NOTHING_TO_DO;
+}
+
+static cairo_int_status_t
+_cairo_no_compositor_fill (const cairo_compositor_t *_compositor,
+                          cairo_composite_rectangles_t *extents,
+                          const cairo_path_fixed_t     *path,
+                          cairo_fill_rule_t     fill_rule,
+                          double                        tolerance,
+                          cairo_antialias_t     antialias)
+{
+    ASSERT_NOT_REACHED;
+    return CAIRO_INT_STATUS_NOTHING_TO_DO;
+}
+
+static cairo_int_status_t
+_cairo_no_compositor_glyphs (const cairo_compositor_t *compositor,
+                            cairo_composite_rectangles_t *extents,
+                            cairo_scaled_font_t        *scaled_font,
+                            cairo_glyph_t              *glyphs,
+                            int                         num_glyphs,
+                            cairo_bool_t overlap)
+{
+    ASSERT_NOT_REACHED;
+    return CAIRO_INT_STATUS_NOTHING_TO_DO;
+}
+
+const cairo_compositor_t __cairo_no_compositor = {
+    NULL,
+    _cairo_no_compositor_paint,
+    _cairo_no_compositor_mask,
+    _cairo_no_compositor_stroke,
+    _cairo_no_compositor_fill,
+    _cairo_no_compositor_glyphs,
+};
diff --git a/src/cairo-observer.c b/src/cairo-observer.c
new file mode 100644 (file)
index 0000000..7c7b69c
--- /dev/null
@@ -0,0 +1,50 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2010 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Intel Corporation
+ *
+ * Contributor(s):
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+
+void
+_cairo_observers_notify (cairo_list_t *observers, void *arg)
+{
+    cairo_observer_t *obs, *next;
+
+    cairo_list_foreach_entry_safe (obs, next,
+                                  cairo_observer_t,
+                                  observers, link)
+    {
+       obs->callback (obs, arg);
+    }
+}
diff --git a/src/cairo-os2-private.h b/src/cairo-os2-private.h
new file mode 100644 (file)
index 0000000..829dd3c
--- /dev/null
@@ -0,0 +1,67 @@
+/* vim: set sw=4 sts=4 et cin: */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright (c) 2005-2006 netlabs.org
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is
+ *     Doodle <doodle@scenergy.dfmk.hu>
+ *
+ * Contributor(s):
+ *     Peter Weilbacher <mozilla@Weilbacher.org>
+ */
+
+#ifndef CAIRO_OS2_PRIVATE_H
+#define CAIRO_OS2_PRIVATE_H
+
+#include "cairo-os2.h"
+#include "cairoint.h"
+
+typedef struct _cairo_os2_surface
+{
+    cairo_surface_t        base;
+
+    /* Mutex semaphore to protect private fields from concurrent access */
+    HMTX                   hmtx_use_private_fields;
+    /* Private fields: */
+    HPS                    hps_client_window;
+    HWND                   hwnd_client_window;
+    BITMAPINFO2            bitmap_info;
+    unsigned char         *pixels;
+    cairo_image_surface_t *image_surface;
+    int                    pixel_array_lend_count;
+    HEV                    hev_pixel_array_came_back;
+
+    RECTL                  rcl_dirty_area;
+    cairo_bool_t           dirty_area_present;
+
+    /* General flags: */
+    cairo_bool_t           blit_as_changes;
+    cairo_bool_t           use_24bpp;
+} cairo_os2_surface_t;
+
+#endif /* CAIRO_OS2_PRIVATE_H */
diff --git a/src/cairo-os2-surface.c b/src/cairo-os2-surface.c
new file mode 100644 (file)
index 0000000..51171fa
--- /dev/null
@@ -0,0 +1,1413 @@
+/* vim: set sw=4 sts=4 et cin: */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright (c) 2005-2006 netlabs.org
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is
+ *     Doodle <doodle@scenergy.dfmk.hu>
+ *
+ * Contributor(s):
+ *     Peter Weilbacher <mozilla@Weilbacher.org>
+ *     Rich Walsh <dragtext@e-vertise.com>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-os2-private.h"
+#include "cairo-default-context-private.h"
+#include "cairo-error-private.h"
+#include "cairo-surface-fallback-private.h"
+#include "cairo-image-surface-private.h"
+
+#if CAIRO_HAS_FC_FONT
+#include <fontconfig/fontconfig.h>
+#endif
+
+#include <float.h>
+#ifdef BUILD_CAIRO_DLL
+# include "cairo-os2.h"
+# ifndef __WATCOMC__
+#  include <emx/startup.h>
+# endif
+#endif
+
+/*
+ * Here comes the extra API for the OS/2 platform. Currently it consists
+ * of two extra functions, the cairo_os2_init() and the
+ * cairo_os2_fini(). Both of them are called automatically if
+ * Cairo is compiled to be a DLL file, but you have to call them before
+ * using the Cairo API if you link to Cairo statically!
+ *
+ * You'll also find the code in here which deals with DLL initialization
+ * and termination, if the code is built to be a DLL.
+ * (if BUILD_CAIRO_DLL is defined)
+ */
+
+/* Initialization counter: */
+static int cairo_os2_initialization_count = 0;
+
+static inline void
+DisableFPUException (void)
+{
+    unsigned short usCW;
+
+    /* Some OS/2 PM API calls modify the FPU Control Word,
+     * but forget to restore it.
+     *
+     * This can result in XCPT_FLOAT_INVALID_OPCODE exceptions,
+     * so to be sure, we disable Invalid Opcode FPU exception
+     * before using FPU stuffs.
+     */
+    usCW = _control87 (0, 0);
+    usCW = usCW | EM_INVALID | 0x80;
+    _control87 (usCW, MCW_EM | 0x80);
+}
+
+/**
+ * cairo_os2_init:
+ *
+ * Initializes the Cairo library. This function is automatically called if
+ * Cairo was compiled to be a DLL (however it's not a problem if it's called
+ * multiple times). But if you link to Cairo statically, you have to call it
+ * once to set up Cairo's internal structures and mutexes.
+ *
+ * Since: 1.4
+ **/
+cairo_public void
+cairo_os2_init (void)
+{
+    /* This may initialize some stuffs, like create mutex semaphores etc.. */
+
+    cairo_os2_initialization_count++;
+    if (cairo_os2_initialization_count > 1) return;
+
+    DisableFPUException ();
+
+#if CAIRO_HAS_FC_FONT
+    /* Initialize FontConfig */
+    FcInit ();
+#endif
+
+    CAIRO_MUTEX_INITIALIZE ();
+}
+
+/**
+ * cairo_os2_fini:
+ *
+ * Uninitializes the Cairo library. This function is automatically called if
+ * Cairo was compiled to be a DLL (however it's not a problem if it's called
+ * multiple times). But if you link to Cairo statically, you have to call it
+ * once to shut down Cairo, to let it free all the resources it has allocated.
+ *
+ * Since: 1.4
+ **/
+cairo_public void
+cairo_os2_fini (void)
+{
+    /* This has to uninitialize some stuffs, like destroy mutex semaphores etc.. */
+
+    if (cairo_os2_initialization_count <= 0) return;
+    cairo_os2_initialization_count--;
+    if (cairo_os2_initialization_count > 0) return;
+
+    DisableFPUException ();
+
+    cairo_debug_reset_static_data ();
+
+#if CAIRO_HAS_FC_FONT
+# if HAVE_FCFINI
+    /* Uninitialize FontConfig */
+    FcFini ();
+# endif
+#endif
+
+#ifdef __WATCOMC__
+    /* It can happen that the libraries we use have memory leaks,
+     * so there are still memory chunks allocated at this point.
+     * In these cases, Watcom might still have a bigger memory chunk,
+     * called "the heap" allocated from the OS.
+     * As we want to minimize the memory we lose from the point of
+     * view of the OS, we call this function to shrink that heap
+     * as much as possible.
+     */
+    _heapshrink ();
+#else
+    /* GCC has a heapmin function that approximately corresponds to
+     * what the Watcom function does
+     */
+    _heapmin ();
+#endif
+}
+
+/*
+ * This function calls the allocation function depending on which
+ * method was compiled into the library: it can be native allocation
+ * (DosAllocMem/DosFreeMem) or C-Library based allocation (malloc/free).
+ * Actually, for pixel buffers that we use this function for, cairo
+ * uses _cairo_malloc_abc, so we use that here, too. And use the
+ * change to check the size argument
+ */
+void *_buffer_alloc (size_t a, size_t b, const unsigned int size)
+{
+    size_t nbytes;
+    void  *buffer = NULL;
+
+    if (!a || !b || !size ||
+        a >= INT32_MAX / b || a*b >= INT32_MAX / size) {
+        return NULL;
+    }
+    nbytes = a * b * size;
+
+#ifdef OS2_USE_PLATFORM_ALLOC
+    /* Using OBJ_ANY on a machine that isn't configured for hi-mem
+     * will cause ERROR_INVALID_PARAMETER.  If this occurs, or this
+     * build doesn't have hi-mem enabled, fall back to using lo-mem.
+     */
+#ifdef OS2_HIGH_MEMORY
+    if (!DosAllocMem (&buffer, nbytes,
+                      OBJ_ANY | PAG_READ | PAG_WRITE | PAG_COMMIT))
+        return buffer;
+#endif
+    if (DosAllocMem (&buffer, nbytes,
+                     PAG_READ | PAG_WRITE | PAG_COMMIT))
+        return NULL;
+#else
+    /* Clear the malloc'd buffer the way DosAllocMem() does. */
+    buffer = malloc (nbytes);
+    if (buffer) {
+        memset (buffer, 0, nbytes);
+    }
+#endif
+    return buffer;
+}
+
+/*
+ * This function selects the free function depending on which
+ * allocation method was compiled into the library
+ */
+void _buffer_free (void *buffer)
+{
+#ifdef OS2_USE_PLATFORM_ALLOC
+    DosFreeMem (buffer);
+#else
+    free (buffer);
+#endif
+}
+
+/* XXX
+ * The cairo_os2_ini() and cairo_os2_fini() functions should be removed and
+ * the LibMain code moved to cairo-system.c.  It should also call
+ * cairo_debug_reset_static_data() instead of duplicating its logic...
+ */
+
+#ifdef BUILD_CAIRO_DLL
+/* The main DLL entry for DLL initialization and uninitialization */
+/* Only include this code if we're about to build a DLL.          */
+
+#ifdef __WATCOMC__
+unsigned _System
+LibMain (unsigned hmod,
+         unsigned termination)
+#else
+unsigned long _System
+_DLL_InitTerm (unsigned long hModule,
+               unsigned long termination)
+#endif
+{
+    if (termination) {
+        /* Unloading the DLL */
+        cairo_os2_fini ();
+
+#ifndef __WATCOMC__
+        /* Uninitialize RTL of GCC */
+        __ctordtorTerm ();
+        _CRT_term ();
+#endif
+        return 1;
+    } else {
+        /* Loading the DLL */
+#ifndef __WATCOMC__
+        /* Initialize RTL of GCC */
+        if (_CRT_init () != 0)
+            return 0;
+        __ctordtorInit ();
+#endif
+
+        cairo_os2_init ();
+        return 1;
+    }
+}
+
+#endif /* BUILD_CAIRO_DLL */
+
+/*
+ * The following part of the source file contains the code which might
+ * be called the "core" of the OS/2 backend support. This contains the
+ * OS/2 surface support functions and structures.
+ */
+
+/* Forward declaration */
+static const cairo_surface_backend_t cairo_os2_surface_backend;
+
+/* Unpublished API:
+ *   GpiEnableYInversion = PMGPI.723
+ *   GpiQueryYInversion = PMGPI.726
+ *   BOOL APIENTRY GpiEnableYInversion (HPS hps, LONG lHeight);
+ *   LONG APIENTRY GpiQueryYInversion (HPS hps);
+ */
+BOOL APIENTRY GpiEnableYInversion (HPS hps, LONG lHeight);
+LONG APIENTRY GpiQueryYInversion (HPS hps);
+
+#ifdef __WATCOMC__
+/* Function declaration for GpiDrawBits () (missing from OpenWatcom headers) */
+LONG APIENTRY GpiDrawBits (HPS hps,
+                           PVOID pBits,
+                           PBITMAPINFO2 pbmiInfoTable,
+                           LONG lCount,
+                           PPOINTL aptlPoints,
+                           LONG lRop,
+                           ULONG flOptions);
+#endif
+
+static void
+_cairo_os2_surface_blit_pixels (cairo_os2_surface_t *surface,
+                                HPS                  hps_begin_paint,
+                                PRECTL               prcl_begin_paint_rect)
+{
+    POINTL aptlPoints[4];
+    LONG   lOldYInversion;
+    LONG   rc = GPI_OK;
+
+    /* Check the limits (may not be necessary) */
+    if (prcl_begin_paint_rect->xLeft < 0)
+        prcl_begin_paint_rect->xLeft = 0;
+    if (prcl_begin_paint_rect->yBottom < 0)
+        prcl_begin_paint_rect->yBottom = 0;
+    if (prcl_begin_paint_rect->xRight > (LONG) surface->bitmap_info.cx)
+        prcl_begin_paint_rect->xRight = (LONG) surface->bitmap_info.cx;
+    if (prcl_begin_paint_rect->yTop > (LONG) surface->bitmap_info.cy)
+        prcl_begin_paint_rect->yTop = (LONG) surface->bitmap_info.cy;
+
+    /* Exit if the rectangle is empty */
+    if (prcl_begin_paint_rect->xLeft   >= prcl_begin_paint_rect->xRight ||
+        prcl_begin_paint_rect->yBottom >= prcl_begin_paint_rect->yTop)
+        return;
+
+    /* Set the Target & Source coordinates */
+    *((PRECTL)&aptlPoints[0]) = *prcl_begin_paint_rect;
+    *((PRECTL)&aptlPoints[2]) = *prcl_begin_paint_rect;
+
+    /* Make the Target coordinates non-inclusive */
+    aptlPoints[1].x -= 1;
+    aptlPoints[1].y -= 1;
+
+    /* Enable Y Inversion for the HPS, so  GpiDrawBits will
+     * work with upside-top image, not with upside-down image!
+     */
+    lOldYInversion = GpiQueryYInversion (hps_begin_paint);
+    GpiEnableYInversion (hps_begin_paint, surface->bitmap_info.cy-1);
+
+    /* Debug code to draw rectangle limits */
+#if 0
+    {
+        int x, y;
+        unsigned char *pixels;
+
+        pixels = surface->pixels;
+        for (x = 0; x < surface->bitmap_info.cx; x++) {
+            for (y = 0; y < surface->bitmap_info.cy; y++) {
+                if ((x == 0) ||
+                    (y == 0) ||
+                    (x == y) ||
+                    (x >= surface->bitmap_info.cx-1) ||
+                    (y >= surface->bitmap_info.cy-1))
+                {
+                    pixels[y*surface->bitmap_info.cx*4+x*4] = 255;
+                }
+            }
+        }
+    }
+#endif
+    if (!surface->use_24bpp) {
+        rc = GpiDrawBits (hps_begin_paint,
+                          surface->pixels,
+                          &(surface->bitmap_info),
+                          4,
+                          aptlPoints,
+                          ROP_SRCCOPY,
+                          BBO_IGNORE);
+        if (rc != GPI_OK)
+            surface->use_24bpp = TRUE;
+    }
+
+    if (surface->use_24bpp) {
+        /* If GpiDrawBits () failed then this is most likely because the
+         * display driver could not handle a 32bit bitmap. So we need to
+         * - create a buffer that only contains 3 bytes per pixel
+         * - change the bitmap info header to contain 24bit
+         * - pass the new buffer to GpiDrawBits () again
+         * - clean up the new buffer
+         */
+        BITMAPINFO2       bmpinfo;
+        unsigned char    *pchPixBuf;
+        unsigned char    *pchTarget;
+        ULONG            *pulSource;
+        ULONG             ulX;
+        ULONG             ulY;
+        ULONG             ulPad;
+
+        /* Set up the bitmap header, but this time for 24bit depth. */
+        bmpinfo = surface->bitmap_info;
+        bmpinfo.cBitCount = 24;
+
+        /* The start of each row has to be DWORD aligned.  Calculate the
+         * of number aligned bytes per row, the total size of the bitmap,
+         * and the number of padding bytes at the end of each row.
+         */
+        ulX = (((bmpinfo.cx * bmpinfo.cBitCount) + 31) / 32) * 4;
+        bmpinfo.cbImage = ulX * bmpinfo.cy;
+        ulPad = ulX - bmpinfo.cx * 3;
+
+        /* Allocate temporary pixel buffer.  If the rows don't need
+         * padding, it has to be 1 byte larger than the size of the
+         * bitmap  or else the high-order byte from the last source
+         * row will end up in unallocated memory.
+         */
+        pchPixBuf = (unsigned char *)_buffer_alloc (1, 1,
+                                        bmpinfo.cbImage + (ulPad ? 0 : 1));
+
+        if (pchPixBuf) {
+            /* Copy 4 bytes from the source but advance the target ptr only
+             * 3 bytes, so the high-order alpha byte will be overwritten by
+             * the next copy. At the end of each row, skip over the padding.
+             */
+            pchTarget = pchPixBuf;
+            pulSource = (ULONG*)surface->pixels;
+            for (ulY = bmpinfo.cy; ulY; ulY--) {
+                for (ulX = bmpinfo.cx; ulX; ulX--) {
+                    *((ULONG*)pchTarget) = *pulSource++;
+                    pchTarget += 3;
+                }
+                pchTarget += ulPad;
+            }
+
+            rc = GpiDrawBits (hps_begin_paint,
+                              pchPixBuf,
+                              &bmpinfo,
+                              4,
+                              aptlPoints,
+                              ROP_SRCCOPY,
+                              BBO_IGNORE);
+            if (rc != GPI_OK)
+                surface->use_24bpp = FALSE;
+
+            _buffer_free (pchPixBuf);
+        }
+    }
+
+    /* Restore Y inversion */
+    GpiEnableYInversion (hps_begin_paint, lOldYInversion);
+}
+
+static void
+_cairo_os2_surface_get_pixels_from_screen (cairo_os2_surface_t *surface,
+                                           HPS                  hps_begin_paint,
+                                           PRECTL               prcl_begin_paint_rect)
+{
+    HPS hps;
+    HDC hdc;
+    SIZEL sizlTemp;
+    HBITMAP hbmpTemp;
+    BITMAPINFO2 bmi2Temp;
+    POINTL aptlPoints[4];
+    int y;
+    unsigned char *pchTemp;
+
+    /* To copy pixels from screen to our buffer, we do the following steps:
+     *
+     * - Blit pixels from screen to a HBITMAP:
+     *   -- Create Memory Device Context
+     *   -- Create a PS into it
+     *   -- Create a HBITMAP
+     *   -- Select HBITMAP into memory PS
+     *   -- Blit dirty pixels from screen to HBITMAP
+     * - Copy HBITMAP lines (pixels) into our buffer
+     * - Free resources
+     */
+
+    /* Create a memory device context */
+    hdc = DevOpenDC (0, OD_MEMORY,"*",0L, NULL, NULLHANDLE);
+    if (!hdc) {
+        return;
+    }
+
+    /* Create a memory PS */
+    sizlTemp.cx = prcl_begin_paint_rect->xRight - prcl_begin_paint_rect->xLeft;
+    sizlTemp.cy = prcl_begin_paint_rect->yTop - prcl_begin_paint_rect->yBottom;
+    hps = GpiCreatePS (0,
+                       hdc,
+                       &sizlTemp,
+                       PU_PELS | GPIT_NORMAL | GPIA_ASSOC);
+    if (!hps) {
+        DevCloseDC (hdc);
+        return;
+    }
+
+    /* Create an uninitialized bitmap. */
+    /* Prepare BITMAPINFO2 structure for our buffer */
+    memset (&bmi2Temp, 0, sizeof (bmi2Temp));
+    bmi2Temp.cbFix = sizeof (BITMAPINFOHEADER2);
+    bmi2Temp.cx = sizlTemp.cx;
+    bmi2Temp.cy = sizlTemp.cy;
+    bmi2Temp.cPlanes = 1;
+    bmi2Temp.cBitCount = 32;
+
+    hbmpTemp = GpiCreateBitmap (hps,
+                                (PBITMAPINFOHEADER2) &bmi2Temp,
+                                0,
+                                NULL,
+                                NULL);
+
+    if (!hbmpTemp) {
+        GpiDestroyPS (hps);
+        DevCloseDC (hdc);
+        return;
+    }
+
+    /* Select the bitmap into the memory device context. */
+    GpiSetBitmap (hps, hbmpTemp);
+
+    /* Target coordinates (Noninclusive) */
+    aptlPoints[0].x = 0;
+    aptlPoints[0].y = 0;
+
+    aptlPoints[1].x = sizlTemp.cx;
+    aptlPoints[1].y = sizlTemp.cy;
+
+    /* Source coordinates (Inclusive) */
+    aptlPoints[2].x = prcl_begin_paint_rect->xLeft;
+    aptlPoints[2].y = surface->bitmap_info.cy - prcl_begin_paint_rect->yBottom;
+
+    aptlPoints[3].x = prcl_begin_paint_rect->xRight;
+    aptlPoints[3].y = surface->bitmap_info.cy - prcl_begin_paint_rect->yTop;
+
+    /* Blit pixels from screen to bitmap */
+    GpiBitBlt (hps,
+               hps_begin_paint,
+               4,
+               aptlPoints,
+               ROP_SRCCOPY,
+               BBO_IGNORE);
+
+    /* Now we have to extract the pixels from the bitmap. */
+    pchTemp =
+        surface->pixels +
+        (prcl_begin_paint_rect->yBottom)*surface->bitmap_info.cx*4 +
+        prcl_begin_paint_rect->xLeft*4;
+    for (y = 0; y < sizlTemp.cy; y++) {
+        /* Get one line of pixels */
+        GpiQueryBitmapBits (hps,
+                            sizlTemp.cy - y - 1, /* lScanStart */
+                            1,                   /* lScans */
+                            (PBYTE)pchTemp,
+                            &bmi2Temp);
+
+        /* Go for next line */
+        pchTemp += surface->bitmap_info.cx*4;
+    }
+
+    /* Clean up resources */
+    GpiSetBitmap (hps, (HBITMAP) NULL);
+    GpiDeleteBitmap (hbmpTemp);
+    GpiDestroyPS (hps);
+    DevCloseDC (hdc);
+}
+
+static cairo_status_t
+_cairo_os2_surface_acquire_source_image (void                   *abstract_surface,
+                                         cairo_image_surface_t **image_out,
+                                         void                  **image_extra)
+{
+    cairo_os2_surface_t *surface = (cairo_os2_surface_t *) abstract_surface;
+
+    DosRequestMutexSem (surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT);
+
+    /* Increase lend counter */
+    surface->pixel_array_lend_count++;
+
+    *image_out = surface->image_surface;
+    *image_extra = NULL;
+
+    DosReleaseMutexSem (surface->hmtx_use_private_fields);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_os2_surface_release_source_image (void                  *abstract_surface,
+                                         cairo_image_surface_t *image,
+                                         void                  *image_extra)
+{
+    cairo_os2_surface_t *surface = (cairo_os2_surface_t *) abstract_surface;
+
+    /* Decrease Lend counter! */
+    DosRequestMutexSem (surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT);
+
+    if (surface->pixel_array_lend_count > 0)
+        surface->pixel_array_lend_count--;
+    DosPostEventSem (surface->hev_pixel_array_came_back);
+
+    DosReleaseMutexSem (surface->hmtx_use_private_fields);
+}
+
+static cairo_surface_t *
+_cairo_os2_surface_map_to_image (void *abstract_surface,
+                                const cairo_rectangle_int_t *extents)
+{
+    cairo_os2_surface_t *surface = (cairo_os2_surface_t *) abstract_surface;
+
+    DosRequestMutexSem (surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT);
+    /* Increase lend counter */
+    surface->pixel_array_lend_count++;
+    DosReleaseMutexSem (local_os2_surface->hmtx_use_private_fields);
+
+    *image_out = _cairo_surface_create_for_rectangle_int (surface->image_surface,
+                                                         extents);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_os2_surface_unmap_image (void *abstract_surface,
+                               cairo_image_surface_t *image)
+{
+    cairo_os2_surface_t *surface = (cairo_os2_surface_t *) abstract_surface;
+
+    /* So, we got back the image, and if all goes well, then
+     * something has been changed inside the interest_rect.
+     * So, we blit it to the screen!
+     */
+    if (surface->blit_as_changes) {
+       RECTL rclToBlit;
+
+        /* Get mutex, we'll work with the pixel array! */
+        if (DosRequestMutexSem (surface->hmtx_use_private_fields,
+                               SEM_INDEFINITE_WAIT) != NO_ERROR)
+       {
+            /* Could not get mutex! */
+            return;
+        }
+
+       rclToBlit.xLeft = image->base.device_transform_inverse.x0;
+       rclToBlit.xRight = rclToBlit.xLeft + image->width; /* Noninclusive */
+       rclToBlit.yTop = image->base.device_transform_inverse.y0;
+       rclToBlit.yBottom = rclToBlit.yTop + image->height; /* Noninclusive */
+
+        if (surface->hwnd_client_window) {
+            /* We know the HWND, so let's invalidate the window region,
+             * so the application will redraw itself, using the
+             * cairo_os2_surface_refresh_window () API from its own PM thread.
+             *
+             * This is the safe method, which should be preferred every time.
+             */
+           rclToBlit.yTop = surface->bitmap_info.cy - rclToBlit.yTop;
+           rclToBlit.yBottom = surface->bitmap_info.cy - rclToBlit.yTop;
+            WinInvalidateRect (surface->hwnd_client_window,
+                               &rclToBlit,
+                               FALSE);
+        } else {
+            /* We don't know the HWND, so try to blit the pixels from here!
+             * Please note that it can be problematic if this is not the PM thread!
+             *
+             * It can cause internal PM stuffs to be screwed up, for some reason.
+             * Please always tell the HWND to the surface using the
+             * cairo_os2_surface_set_hwnd () API, and call cairo_os2_surface_refresh_window ()
+             * from your WM_PAINT, if it's possible!
+             */
+            _cairo_os2_surface_blit_pixels (surface,
+                                            surface->hps_client_window,
+                                            &rclToBlit);
+        }
+
+        DosReleaseMutexSem (surface->hmtx_use_private_fields);
+    }
+    /* Also decrease Lend counter! */
+    DosRequestMutexSem (surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT);
+
+    if (surface->pixel_array_lend_count > 0)
+        surface->pixel_array_lend_count--;
+    DosPostEventSem (surface->hev_pixel_array_came_back);
+
+    DosReleaseMutexSem (surface->hmtx_use_private_fields);
+}
+
+static cairo_bool_t
+_cairo_os2_surface_get_extents (void                    *abstract_surface,
+                                cairo_rectangle_int_t   *rectangle)
+{
+    cairo_os2_surface_t *surface = (cairo_os2_surface_t *) abstract_surface;
+
+    rectangle->x = 0;
+    rectangle->y = 0;
+    rectangle->width  = surface->bitmap_info.cx;
+    rectangle->height = surface->bitmap_info.cy;
+
+    return TRUE;
+}
+
+/**
+ * cairo_os2_surface_create:
+ * @hps_client_window: the presentation handle to bind the surface to
+ * @width: the width of the surface
+ * @height: the height of the surface
+ *
+ * Create a Cairo surface which is bound to a given presentation space (HPS).
+ * The caller retains ownership of the HPS and must dispose of it after the
+ * the surface has been destroyed.  The surface will be created to have the
+ * given size. By default every change to the surface will be made visible
+ * immediately by blitting it into the window. This can be changed with
+ * cairo_os2_surface_set_manual_window_refresh().
+ * Note that the surface will contain garbage when created, so the pixels
+ * have to be initialized by hand first. You can use the Cairo functions to
+ * fill it with black, or use cairo_surface_mark_dirty() to fill the surface
+ * with pixels from the window/HPS.
+ *
+ * Return value: the newly created surface
+ *
+ * Since: 1.4
+ **/
+cairo_surface_t *
+cairo_os2_surface_create (HPS hps_client_window,
+                          int width,
+                          int height)
+{
+    cairo_os2_surface_t *local_os2_surface = 0;
+    cairo_status_t status;
+    int rc;
+
+    /* Check the size of the window */
+    if ((width <= 0) || (height <= 0)) {
+        status = _cairo_error (CAIRO_STATUS_INVALID_SIZE);
+        goto error_exit;
+    }
+
+    /* Allocate an OS/2 surface structure. */
+    local_os2_surface = (cairo_os2_surface_t *) malloc (sizeof (cairo_os2_surface_t));
+    if (!local_os2_surface) {
+        status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+        goto error_exit;
+    }
+
+    memset(local_os2_surface, 0, sizeof(cairo_os2_surface_t));
+
+    /* Allocate resources:  mutex & event semaphores and the pixel buffer */
+    if (DosCreateMutexSem (NULL,
+                           &(local_os2_surface->hmtx_use_private_fields),
+                           0,
+                           FALSE))
+    {
+        status = _cairo_error (CAIRO_STATUS_DEVICE_ERROR);
+        goto error_exit;
+    }
+
+    if (DosCreateEventSem (NULL,
+                           &(local_os2_surface->hev_pixel_array_came_back),
+                           0,
+                           FALSE))
+    {
+        status = _cairo_error (CAIRO_STATUS_DEVICE_ERROR);
+        goto error_exit;
+    }
+
+    local_os2_surface->pixels = (unsigned char *) _buffer_alloc (height, width, 4);
+    if (!local_os2_surface->pixels) {
+        status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+        goto error_exit;
+    }
+
+    /* Create image surface from pixel array */
+    local_os2_surface->image_surface = (cairo_image_surface_t *)
+        cairo_image_surface_create_for_data (local_os2_surface->pixels,
+                                             CAIRO_FORMAT_ARGB32,
+                                             width,      /* Width */
+                                             height,     /* Height */
+                                             width * 4); /* Rowstride */
+    status = local_os2_surface->image_surface->base.status;
+    if (status)
+        goto error_exit;
+
+    /* Set values for OS/2-specific data that aren't zero/NULL/FALSE.
+     * Note: hps_client_window may be null if this was called by
+     * cairo_os2_surface_create_for_window().
+     */
+    local_os2_surface->hps_client_window = hps_client_window;
+    local_os2_surface->blit_as_changes = TRUE;
+
+    /* Prepare BITMAPINFO2 structure for our buffer */
+    local_os2_surface->bitmap_info.cbFix = sizeof (BITMAPINFOHEADER2);
+    local_os2_surface->bitmap_info.cx = width;
+    local_os2_surface->bitmap_info.cy = height;
+    local_os2_surface->bitmap_info.cPlanes = 1;
+    local_os2_surface->bitmap_info.cBitCount = 32;
+
+    /* Initialize base surface */
+    _cairo_surface_init (&local_os2_surface->base,
+                         &cairo_os2_surface_backend,
+                         NULL, /* device */
+                         _cairo_content_from_format (CAIRO_FORMAT_ARGB32));
+
+    /* Successful exit */
+    return (cairo_surface_t *)local_os2_surface;
+
+ error_exit:
+
+    /* This point will only be reached if an error occured */
+
+    if (local_os2_surface) {
+        if (local_os2_surface->pixels)
+            _buffer_free (local_os2_surface->pixels);
+        if (local_os2_surface->hev_pixel_array_came_back)
+            DosCloseEventSem (local_os2_surface->hev_pixel_array_came_back);
+        if (local_os2_surface->hmtx_use_private_fields)
+            DosCloseMutexSem (local_os2_surface->hmtx_use_private_fields);
+        free (local_os2_surface);
+    }
+
+    return _cairo_surface_create_in_error (status);
+}
+
+/**
+ * cairo_os2_surface_create_for_window:
+ * @hwnd_client_window: the window handle to bind the surface to
+ * @width: the width of the surface
+ * @height: the height of the surface
+ *
+ * Create a Cairo surface which is bound to a given window; the caller retains
+ * ownership of the window.  This is a convenience function for use with
+ * windows that will only be updated when cairo_os2_surface_refresh_window()
+ * is called (usually in response to a WM_PAINT message).  It avoids the need
+ * to create a persistent HPS for every window and assumes that one will be
+ * supplied by the caller when a cairo function needs one.  If it isn't, an
+ * HPS will be created on-the-fly and released before the function which needs
+ * it returns.
+ *
+ * Return value: the newly created surface
+ *
+ * Since: 1.10
+ **/
+cairo_surface_t *
+cairo_os2_surface_create_for_window (HWND hwnd_client_window,
+                                     int width,
+                                     int height)
+{
+    cairo_os2_surface_t *local_os2_surface;
+
+    /* A window handle must be provided. */
+    if (!hwnd_client_window) {
+        return _cairo_surface_create_in_error (
+                                _cairo_error (CAIRO_STATUS_NO_MEMORY));
+    }
+
+    /* Create the surface. */
+    local_os2_surface = (cairo_os2_surface_t *)
+        cairo_os2_surface_create (0, width, height);
+
+    /* If successful, save the hwnd & turn off automatic repainting. */
+    if (!local_os2_surface->image_surface->base.status) {
+        local_os2_surface->hwnd_client_window = hwnd_client_window;
+        local_os2_surface->blit_as_changes = FALSE;
+    }
+
+    return (cairo_surface_t *)local_os2_surface;
+}
+
+/**
+ * cairo_os2_surface_set_size:
+ * @surface: the cairo surface to resize
+ * @new_width: the new width of the surface
+ * @new_height: the new height of the surface
+ * @timeout: timeout value in milliseconds
+ *
+ * When the client window is resized, call this API to set the new size in the
+ * underlying surface accordingly. This function will reallocate everything,
+ * so you'll have to redraw everything in the surface after this call.
+ * The surface will contain garbage after the resizing. So the notes of
+ * cairo_os2_surface_create() apply here, too.
+ *
+ * The timeout value specifies how long the function should wait on other parts
+ * of the program to release the buffers. It is necessary, because it can happen
+ * that Cairo is just drawing something into the surface while we want to
+ * destroy and recreate it.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS if the surface could be resized,
+ * %CAIRO_STATUS_SURFACE_TYPE_MISMATCH if the surface is not an OS/2 surface,
+ * %CAIRO_STATUS_INVALID_SIZE for invalid sizes
+ * %CAIRO_STATUS_NO_MEMORY if the new size could not be allocated, or if the
+ * timeout happened before all the buffers were released
+ *
+ * Since: 1.4
+ **/
+int
+cairo_os2_surface_set_size (cairo_surface_t *surface,
+                            int              new_width,
+                            int              new_height,
+                            int              timeout)
+{
+    cairo_os2_surface_t *local_os2_surface;
+    unsigned char *pchNewPixels;
+    int rc;
+    cairo_image_surface_t *pNewImageSurface;
+
+    local_os2_surface = (cairo_os2_surface_t *) surface;
+    if ((!local_os2_surface) ||
+        (local_os2_surface->base.backend != &cairo_os2_surface_backend))
+    {
+        /* Invalid parameter (wrong surface)! */
+        return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+    }
+
+    if ((new_width <= 0) ||
+        (new_height <= 0))
+    {
+        /* Invalid size! */
+        return _cairo_error (CAIRO_STATUS_INVALID_SIZE);
+    }
+
+    /* Allocate memory for new stuffs */
+    pchNewPixels = (unsigned char *) _buffer_alloc (new_height, new_width, 4);
+    if (!pchNewPixels) {
+        /* Not enough memory for the pixels!
+         * Everything remains the same!
+         */
+        return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+    }
+
+    /* Create image surface from new pixel array */
+    pNewImageSurface = (cairo_image_surface_t *)
+        cairo_image_surface_create_for_data (pchNewPixels,
+                                             CAIRO_FORMAT_ARGB32,
+                                             new_width,      /* Width */
+                                             new_height,     /* Height */
+                                             new_width * 4); /* Rowstride */
+
+    if (pNewImageSurface->base.status) {
+        /* Could not create image surface!
+         * Everything remains the same!
+         */
+        _buffer_free (pchNewPixels);
+        return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+    }
+
+    /* Okay, new memory allocated, so it's time to swap old buffers
+     * to new ones!
+     */
+    if (DosRequestMutexSem (local_os2_surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT)!=NO_ERROR) {
+        /* Could not get mutex!
+         * Everything remains the same!
+         */
+        cairo_surface_destroy ((cairo_surface_t *) pNewImageSurface);
+        _buffer_free (pchNewPixels);
+        return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+    }
+
+    /* We have to make sure that we won't destroy a surface which
+     * is lent to some other code (Cairo is drawing into it)!
+     */
+    while (local_os2_surface->pixel_array_lend_count > 0) {
+        ULONG ulPostCount;
+        DosResetEventSem (local_os2_surface->hev_pixel_array_came_back, &ulPostCount);
+        DosReleaseMutexSem (local_os2_surface->hmtx_use_private_fields);
+        /* Wait for somebody to return the pixels! */
+        rc = DosWaitEventSem (local_os2_surface->hev_pixel_array_came_back, timeout);
+        if (rc != NO_ERROR) {
+            /* Either timeout or something wrong... Exit. */
+            cairo_surface_destroy ((cairo_surface_t *) pNewImageSurface);
+            _buffer_free (pchNewPixels);
+            return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+        }
+        /* Okay, grab mutex and check counter again! */
+        if (DosRequestMutexSem (local_os2_surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT)
+            != NO_ERROR)
+        {
+            /* Could not get mutex!
+             * Everything remains the same!
+             */
+            cairo_surface_destroy ((cairo_surface_t *) pNewImageSurface);
+            _buffer_free (pchNewPixels);
+            return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+        }
+    }
+
+    /* Destroy old image surface */
+    cairo_surface_destroy ((cairo_surface_t *) (local_os2_surface->image_surface));
+    /* Destroy old pixel buffer */
+    _buffer_free (local_os2_surface->pixels);
+    /* Set new image surface */
+    local_os2_surface->image_surface = pNewImageSurface;
+    /* Set new pixel buffer */
+    local_os2_surface->pixels = pchNewPixels;
+    /* Change bitmap2 structure */
+    local_os2_surface->bitmap_info.cx = new_width;
+    local_os2_surface->bitmap_info.cy = new_height;
+
+    DosReleaseMutexSem (local_os2_surface->hmtx_use_private_fields);
+    return CAIRO_STATUS_SUCCESS;
+}
+
+/**
+ * cairo_os2_surface_refresh_window:
+ * @surface: the cairo surface to refresh
+ * @hps_begin_paint: the presentation handle of the window to refresh
+ * @prcl_begin_paint_rect: the rectangle to redraw
+ *
+ * This function can be used to force a repaint of a given area of the client
+ * window. It should usually be called from the WM_PAINT processing of the
+ * window procedure. However, it can be called any time a given part of the
+ * window has to be updated.
+ *
+ * The HPS and RECTL to be passed can be taken from the usual WinBeginPaint call
+ * of the window procedure, but you can also get the HPS using WinGetPS, and you
+ * can assemble your own update rectangle by hand.
+ * If hps_begin_paint is %NULL, the function will use the HPS passed into
+ * cairo_os2_surface_create(). If @prcl_begin_paint_rect is %NULL, the function
+ * will query the current window size and repaint the whole window.
+ *
+ * Cairo assumes that if you set the HWND to the surface using
+ * cairo_os2_surface_set_hwnd(), this function will be called by the application
+ * every time it gets a WM_PAINT for that HWND. If the HWND is set in the
+ * surface, Cairo uses this function to handle dirty areas too.
+ *
+ * Since: 1.4
+ **/
+void
+cairo_os2_surface_refresh_window (cairo_surface_t *surface,
+                                  HPS              hps_begin_paint,
+                                  PRECTL           prcl_begin_paint_rect)
+{
+    cairo_os2_surface_t *local_os2_surface;
+    RECTL rclTemp;
+    HPS hpsTemp = 0;
+
+    local_os2_surface = (cairo_os2_surface_t *) surface;
+    if ((!local_os2_surface) ||
+        (local_os2_surface->base.backend != &cairo_os2_surface_backend))
+    {
+        /* Invalid parameter (wrong surface)! */
+        return;
+    }
+
+    /* If an HPS wasn't provided, see if we can get one. */
+    if (!hps_begin_paint) {
+        hps_begin_paint = local_os2_surface->hps_client_window;
+        if (!hps_begin_paint) {
+            if (local_os2_surface->hwnd_client_window) {
+                hpsTemp = WinGetPS(local_os2_surface->hwnd_client_window);
+                hps_begin_paint = hpsTemp;
+            }
+            /* No HPS & no way to get one, so exit */
+            if (!hps_begin_paint)
+                return;
+        }
+    }
+
+    if (prcl_begin_paint_rect == NULL) {
+        /* Update the whole window! */
+        rclTemp.xLeft = 0;
+        rclTemp.xRight = local_os2_surface->bitmap_info.cx;
+        rclTemp.yTop = local_os2_surface->bitmap_info.cy;
+        rclTemp.yBottom = 0;
+    } else {
+        /* Use the rectangle we got passed as parameter! */
+        rclTemp.xLeft = prcl_begin_paint_rect->xLeft;
+        rclTemp.xRight = prcl_begin_paint_rect->xRight;
+        rclTemp.yTop = local_os2_surface->bitmap_info.cy - prcl_begin_paint_rect->yBottom;
+        rclTemp.yBottom = local_os2_surface->bitmap_info.cy - prcl_begin_paint_rect->yTop ;
+    }
+
+    /* Get mutex, we'll work with the pixel array! */
+    if (DosRequestMutexSem (local_os2_surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT)
+        != NO_ERROR)
+    {
+        /* Could not get mutex! */
+        if (hpsTemp)
+            WinReleasePS(hpsTemp);
+        return;
+    }
+
+    if ((local_os2_surface->dirty_area_present) &&
+        (local_os2_surface->rcl_dirty_area.xLeft == rclTemp.xLeft) &&
+        (local_os2_surface->rcl_dirty_area.xRight == rclTemp.xRight) &&
+        (local_os2_surface->rcl_dirty_area.yTop == rclTemp.yTop) &&
+        (local_os2_surface->rcl_dirty_area.yBottom == rclTemp.yBottom))
+    {
+        /* Aha, this call was because of a dirty area, so in this case we
+         * have to blit the pixels from the screen to the surface!
+         */
+        local_os2_surface->dirty_area_present = FALSE;
+        _cairo_os2_surface_get_pixels_from_screen (local_os2_surface,
+                                                   hps_begin_paint,
+                                                   &rclTemp);
+    } else {
+        /* Okay, we have the surface, have the HPS
+         * (might be from WinBeginPaint () or from WinGetPS () )
+         * Now blit there the stuffs!
+         */
+        _cairo_os2_surface_blit_pixels (local_os2_surface,
+                                        hps_begin_paint,
+                                        &rclTemp);
+    }
+
+    DosReleaseMutexSem (local_os2_surface->hmtx_use_private_fields);
+
+    if (hpsTemp)
+        WinReleasePS(hpsTemp);
+}
+
+static cairo_status_t
+_cairo_os2_surface_finish (void *abstract_surface)
+{
+    cairo_os2_surface_t *local_os2_surface;
+
+    local_os2_surface = (cairo_os2_surface_t *) abstract_surface;
+    if ((!local_os2_surface) ||
+        (local_os2_surface->base.backend != &cairo_os2_surface_backend))
+    {
+        /* Invalid parameter (wrong surface)! */
+        return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+    }
+
+    DosRequestMutexSem (local_os2_surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT);
+
+    /* Destroy old image surface */
+    cairo_surface_destroy ((cairo_surface_t *) (local_os2_surface->image_surface));
+    /* Destroy old pixel buffer */
+    _buffer_free (local_os2_surface->pixels);
+    DosCloseMutexSem (local_os2_surface->hmtx_use_private_fields);
+    DosCloseEventSem (local_os2_surface->hev_pixel_array_came_back);
+
+    /* The memory itself will be free'd by the cairo_surface_destroy ()
+     * who called us.
+     */
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+/**
+ * cairo_os2_surface_set_hwnd:
+ * @surface: the cairo surface to associate with the window handle
+ * @hwnd_client_window: the window handle of the client window
+ *
+ * Sets window handle for surface; the caller retains ownership of the window.
+ * If Cairo wants to blit into the window because it is set to blit as the
+ * surface changes (see cairo_os2_surface_set_manual_window_refresh()), then
+ * there are two ways it can choose:
+ * If it knows the HWND of the surface, then it invalidates that area, so the
+ * application will get a WM_PAINT message and it can call
+ * cairo_os2_surface_refresh_window() to redraw that area. Otherwise cairo itself
+ * will use the HPS it got at surface creation time, and blit the pixels itself.
+ * It's also a solution, but experience shows that if this happens from a non-PM
+ * thread, then it can screw up PM internals.
+ *
+ * So, best solution is to set the HWND for the surface after the surface
+ * creation, so every blit will be done from application's message processing
+ * loop, which is the safest way to do.
+ *
+ * Since: 1.4
+ **/
+void
+cairo_os2_surface_set_hwnd (cairo_surface_t *surface,
+                            HWND             hwnd_client_window)
+{
+    cairo_os2_surface_t *local_os2_surface;
+
+    local_os2_surface = (cairo_os2_surface_t *) surface;
+    if ((!local_os2_surface) ||
+        (local_os2_surface->base.backend != &cairo_os2_surface_backend))
+    {
+        /* Invalid parameter (wrong surface)! */
+        return;
+    }
+
+    if (DosRequestMutexSem (local_os2_surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT)
+        != NO_ERROR)
+    {
+        /* Could not get mutex! */
+        return;
+    }
+
+    local_os2_surface->hwnd_client_window = hwnd_client_window;
+
+    DosReleaseMutexSem (local_os2_surface->hmtx_use_private_fields);
+}
+
+/**
+ * cairo_os2_surface_set_manual_window_refresh:
+ * @surface: the cairo surface to set the refresh mode for
+ * @manual_refresh: the switch for manual surface refresh
+ *
+ * This API can tell Cairo if it should show every change to this surface
+ * immediately in the window or if it should be cached and will only be visible
+ * once the user calls cairo_os2_surface_refresh_window() explicitly. If the
+ * HWND was not set in the cairo surface, then the HPS will be used to blit the
+ * graphics. Otherwise it will invalidate the given window region so the user
+ * will get the WM_PAINT message to redraw that area of the window.
+ *
+ * So, if you're only interested in displaying the final result after several
+ * drawing operations, you might get better performance if you put the surface
+ * into manual refresh mode by passing a true value to this function. Then call
+ * cairo_os2_surface_refresh() whenever desired.
+ *
+ * Since: 1.4
+ **/
+void
+cairo_os2_surface_set_manual_window_refresh (cairo_surface_t *surface,
+                                             cairo_bool_t     manual_refresh)
+{
+    cairo_os2_surface_t *local_os2_surface;
+
+    local_os2_surface = (cairo_os2_surface_t *) surface;
+    if ((!local_os2_surface) ||
+        (local_os2_surface->base.backend != &cairo_os2_surface_backend))
+    {
+        /* Invalid parameter (wrong surface)! */
+        return;
+    }
+
+    local_os2_surface->blit_as_changes = !manual_refresh;
+}
+
+/**
+ * cairo_os2_surface_get_manual_window_refresh:
+ * @surface: the cairo surface to query the refresh mode from
+ *
+ * This space left intentionally blank.
+ *
+ * Return value: current refresh mode of the surface (true by default)
+ *
+ * Since: 1.4
+ **/
+cairo_bool_t
+cairo_os2_surface_get_manual_window_refresh (cairo_surface_t *surface)
+{
+    cairo_os2_surface_t *local_os2_surface;
+
+    local_os2_surface = (cairo_os2_surface_t *) surface;
+    if ((!local_os2_surface) ||
+        (local_os2_surface->base.backend != &cairo_os2_surface_backend))
+    {
+        /* Invalid parameter (wrong surface)! */
+        return FALSE;
+    }
+
+    return !(local_os2_surface->blit_as_changes);
+}
+
+/**
+ * cairo_os2_surface_get_hps:
+ * @surface: the cairo surface to be querued
+ * @hps: HPS currently associated with the surface (if any)
+ *
+ * This API retrieves the HPS associated with the surface.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS if the hps could be retrieved,
+ * %CAIRO_STATUS_SURFACE_TYPE_MISMATCH if the surface is not an OS/2 surface,
+ * %CAIRO_STATUS_NULL_POINTER if the hps argument is null
+ *
+ * Since: 1.10
+ **/
+cairo_status_t
+cairo_os2_surface_get_hps (cairo_surface_t *surface,
+                           HPS             *hps)
+{
+    cairo_os2_surface_t *local_os2_surface;
+
+    local_os2_surface = (cairo_os2_surface_t *) surface;
+    if ((!local_os2_surface) ||
+        (local_os2_surface->base.backend != &cairo_os2_surface_backend))
+    {
+        /* Invalid parameter (wrong surface)! */
+        return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+    }
+    if (!hps)
+    {
+        return _cairo_error (CAIRO_STATUS_NULL_POINTER);
+    }
+    *hps = local_os2_surface->hps_client_window;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+/**
+ * cairo_os2_surface_set_hps:
+ * @surface: the cairo surface to associate with the HPS
+ * @hps: new HPS to be associated with the surface (the HPS may be null)
+ *
+ * This API replaces the HPS associated with the surface with a new one.
+ * The caller retains ownership of the HPS and must dispose of it after
+ * the surface has been destroyed or it has been replaced by another
+ * call to this function.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS if the hps could be replaced,
+ * %CAIRO_STATUS_SURFACE_TYPE_MISMATCH if the surface is not an OS/2 surface,
+ *
+ * Since: 1.10
+ **/
+cairo_status_t
+cairo_os2_surface_set_hps (cairo_surface_t *surface,
+                           HPS              hps)
+{
+    cairo_os2_surface_t *local_os2_surface;
+
+    local_os2_surface = (cairo_os2_surface_t *) surface;
+    if ((!local_os2_surface) ||
+        (local_os2_surface->base.backend != &cairo_os2_surface_backend))
+    {
+        /* Invalid parameter (wrong surface)! */
+        return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+    }
+    local_os2_surface->hps_client_window = hps;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_os2_surface_mark_dirty_rectangle (void *surface,
+                                         int   x,
+                                         int   y,
+                                         int   width,
+                                         int   height)
+{
+    cairo_os2_surface_t *local_os2_surface;
+    RECTL rclToBlit;
+
+    local_os2_surface = (cairo_os2_surface_t *) surface;
+    if ((!local_os2_surface) ||
+        (local_os2_surface->base.backend != &cairo_os2_surface_backend))
+    {
+        /* Invalid parameter (wrong surface)! */
+        return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+    }
+
+    /* Get mutex, we'll work with the pixel array! */
+    if (DosRequestMutexSem (local_os2_surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT)
+        != NO_ERROR)
+    {
+        /* Could not get mutex! */
+        return CAIRO_STATUS_NO_MEMORY;
+    }
+
+    /* Check for defaults */
+    if (width < 0)
+        width = local_os2_surface->bitmap_info.cx;
+    if (height < 0)
+        height = local_os2_surface->bitmap_info.cy;
+
+    if (local_os2_surface->hwnd_client_window) {
+        /* We know the HWND, so let's invalidate the window region,
+         * so the application will redraw itself, using the
+         * cairo_os2_surface_refresh_window () API from its own PM thread.
+         * From that function we'll note that it's not a redraw but a
+         * dirty-rectangle deal stuff, so we'll handle the things from
+         * there.
+         *
+         * This is the safe method, which should be preferred every time.
+         */
+        rclToBlit.xLeft = x;
+        rclToBlit.xRight = x + width; /* Noninclusive */
+        rclToBlit.yTop = local_os2_surface->bitmap_info.cy - (y);
+        rclToBlit.yBottom = local_os2_surface->bitmap_info.cy - (y + height); /* Noninclusive */
+
+#if 0
+        if (local_os2_surface->dirty_area_present) {
+            /* Yikes, there is already a dirty area which should be
+             * cleaned up, but we'll overwrite it. Sorry.
+             * TODO: Something clever should be done here.
+             */
+        }
+#endif
+
+        /* Set up dirty area reminder stuff */
+        memcpy (&(local_os2_surface->rcl_dirty_area), &rclToBlit, sizeof (RECTL));
+        local_os2_surface->dirty_area_present = TRUE;
+
+        /* Invalidate window area */
+        WinInvalidateRect (local_os2_surface->hwnd_client_window,
+                           &rclToBlit,
+                           FALSE);
+    } else {
+        /* We don't know the HWND, so try to blit the pixels from here!
+         * Please note that it can be problematic if this is not the PM thread!
+         *
+         * It can cause internal PM stuffs to be scewed up, for some reason.
+         * Please always tell the HWND to the surface using the
+         * cairo_os2_surface_set_hwnd () API, and call cairo_os2_surface_refresh_window ()
+         * from your WM_PAINT, if it's possible!
+         */
+
+        rclToBlit.xLeft = x;
+        rclToBlit.xRight = x + width; /* Noninclusive */
+        rclToBlit.yBottom = y;
+        rclToBlit.yTop = y + height; /* Noninclusive */
+        /* Now get the pixels from the screen! */
+        _cairo_os2_surface_get_pixels_from_screen (local_os2_surface,
+                                                   local_os2_surface->hps_client_window,
+                                                   &rclToBlit);
+    }
+
+    DosReleaseMutexSem (local_os2_surface->hmtx_use_private_fields);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static const cairo_surface_backend_t cairo_os2_surface_backend = {
+    CAIRO_SURFACE_TYPE_OS2,
+    _cairo_os2_surface_finish,
+    _cairo_default_context_create,
+
+    NULL, /* create_similar */
+    NULL, /* create_similar_image */
+    _cairo_os2_surface_map_to_image,
+    _cairo_os2_surface_unmap_image,
+
+    _cairo_os2_surface_acquire_source_image,
+    _cairo_os2_surface_release_source_image,
+    NULL, /* snapshot */
+
+    _cairo_os2_surface_get_extents,
+    NULL, /* get_font_options */
+
+    NULL, /* flush */
+    _cairo_os2_surface_mark_dirty_rectangle,
+
+    _cairo_surface_fallback_paint,
+    _cairo_surface_fallback_mask,
+    _cairo_surface_fallback_fill,
+    _cairo_surface_fallback_stroke,
+    NULL, /* fill/stroke */
+    _cairo_surface_fallback_glyphs,
+};
diff --git a/src/cairo-os2.h b/src/cairo-os2.h
new file mode 100644 (file)
index 0000000..d23f2de
--- /dev/null
@@ -0,0 +1,110 @@
+/* vim: set sw=4 sts=4 et cin: */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright (c) 2005-2006 netlabs.org
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is
+ *     Doodle <doodle@scenergy.dfmk.hu>
+ *
+ * Contributor(s):
+ *     Peter Weilbacher <mozilla@Weilbacher.org>
+ *     Rich Walsh <dragtext@e-vertise.com>
+ */
+
+#ifndef _CAIRO_OS2_H_
+#define _CAIRO_OS2_H_
+
+#define INCL_DOS
+#define INCL_DOSSEMAPHORES
+#define INCL_DOSERRORS
+#define INCL_WIN
+#define INCL_GPI
+
+#include "cairo.h"
+
+#include <os2.h>
+
+CAIRO_BEGIN_DECLS
+
+/* The OS/2 Specific Cairo API */
+
+cairo_public void
+cairo_os2_init (void);
+
+cairo_public void
+cairo_os2_fini (void);
+
+#if CAIRO_HAS_OS2_SURFACE
+
+cairo_public cairo_surface_t *
+cairo_os2_surface_create (HPS hps_client_window,
+                          int width,
+                          int height);
+
+cairo_public cairo_surface_t *
+cairo_os2_surface_create_for_window (HWND hwnd_client_window,
+                                     int  width,
+                                     int  height);
+
+cairo_public void
+cairo_os2_surface_set_hwnd (cairo_surface_t *surface,
+                            HWND             hwnd_client_window);
+
+cairo_public int
+cairo_os2_surface_set_size (cairo_surface_t *surface,
+                            int              new_width,
+                            int              new_height,
+                            int              timeout);
+
+cairo_public void
+cairo_os2_surface_refresh_window (cairo_surface_t *surface,
+                                  HPS              hps_begin_paint,
+                                  PRECTL           prcl_begin_paint_rect);
+
+cairo_public void
+cairo_os2_surface_set_manual_window_refresh (cairo_surface_t *surface,
+                                             cairo_bool_t     manual_refresh);
+
+cairo_public cairo_bool_t
+cairo_os2_surface_get_manual_window_refresh (cairo_surface_t *surface);
+
+cairo_public cairo_status_t
+cairo_os2_surface_get_hps (cairo_surface_t *surface,
+                           HPS             *hps);
+
+cairo_public cairo_status_t
+cairo_os2_surface_set_hps (cairo_surface_t *surface,
+                           HPS              hps);
+
+#else  /* CAIRO_HAS_OS2_SURFACE */
+# error Cairo was not compiled with support for the OS/2 backend
+#endif /* CAIRO_HAS_OS2_SURFACE */
+
+CAIRO_END_DECLS
+
+#endif /* _CAIRO_OS2_H_ */
diff --git a/src/cairo-output-stream-private.h b/src/cairo-output-stream-private.h
new file mode 100644 (file)
index 0000000..edaabbe
--- /dev/null
@@ -0,0 +1,196 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2006 Red Hat, Inc
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Author(s):
+ *     Kristian Høgsberg <krh@redhat.com>
+ */
+
+#ifndef CAIRO_OUTPUT_STREAM_PRIVATE_H
+#define CAIRO_OUTPUT_STREAM_PRIVATE_H
+
+#include "cairo-compiler-private.h"
+#include "cairo-types-private.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+typedef cairo_status_t
+(*cairo_output_stream_write_func_t) (cairo_output_stream_t *output_stream,
+                                    const unsigned char   *data,
+                                    unsigned int           length);
+
+typedef cairo_status_t
+(*cairo_output_stream_flush_func_t) (cairo_output_stream_t *output_stream);
+
+typedef cairo_status_t
+(*cairo_output_stream_close_func_t) (cairo_output_stream_t *output_stream);
+
+struct _cairo_output_stream {
+    cairo_output_stream_write_func_t write_func;
+    cairo_output_stream_flush_func_t flush_func;
+    cairo_output_stream_close_func_t close_func;
+    unsigned long                   position;
+    cairo_status_t                  status;
+    cairo_bool_t                    closed;
+};
+
+extern const cairo_private cairo_output_stream_t _cairo_output_stream_nil;
+
+cairo_private void
+_cairo_output_stream_init (cairo_output_stream_t            *stream,
+                          cairo_output_stream_write_func_t  write_func,
+                          cairo_output_stream_flush_func_t  flush_func,
+                          cairo_output_stream_close_func_t  close_func);
+
+cairo_private cairo_status_t
+_cairo_output_stream_fini (cairo_output_stream_t *stream);
+
+
+/* We already have the following declared in cairo.h:
+
+typedef cairo_status_t (*cairo_write_func_t) (void               *closure,
+                                             const unsigned char *data,
+                                             unsigned int         length);
+*/
+typedef cairo_status_t (*cairo_close_func_t) (void *closure);
+
+
+/* This function never returns %NULL. If an error occurs (NO_MEMORY)
+ * while trying to create the output stream this function returns a
+ * valid pointer to a nil output stream.
+ *
+ * Note that even with a nil surface, the close_func callback will be
+ * called by a call to _cairo_output_stream_close or
+ * _cairo_output_stream_destroy.
+ */
+cairo_private cairo_output_stream_t *
+_cairo_output_stream_create (cairo_write_func_t                write_func,
+                            cairo_close_func_t         close_func,
+                            void                       *closure);
+
+cairo_private cairo_output_stream_t *
+_cairo_output_stream_create_in_error (cairo_status_t status);
+
+/* Tries to flush any buffer maintained by the stream or its delegates. */
+cairo_private cairo_status_t
+_cairo_output_stream_flush (cairo_output_stream_t *stream);
+
+/* Returns the final status value associated with this object, just
+ * before its last gasp. This final status value will capture any
+ * status failure returned by the stream's close_func as well. */
+cairo_private cairo_status_t
+_cairo_output_stream_close (cairo_output_stream_t *stream);
+
+/* Returns the final status value associated with this object, just
+ * before its last gasp. This final status value will capture any
+ * status failure returned by the stream's close_func as well. */
+cairo_private cairo_status_t
+_cairo_output_stream_destroy (cairo_output_stream_t *stream);
+
+cairo_private void
+_cairo_output_stream_write (cairo_output_stream_t *stream,
+                           const void *data, size_t length);
+
+cairo_private void
+_cairo_output_stream_write_hex_string (cairo_output_stream_t *stream,
+                                      const unsigned char *data,
+                                      size_t length);
+
+cairo_private void
+_cairo_output_stream_vprintf (cairo_output_stream_t *stream,
+                             const char *fmt,
+                             va_list ap) CAIRO_PRINTF_FORMAT ( 2, 0);
+
+cairo_private void
+_cairo_output_stream_printf (cairo_output_stream_t *stream,
+                            const char *fmt,
+                            ...) CAIRO_PRINTF_FORMAT (2, 3);
+
+cairo_private long
+_cairo_output_stream_get_position (cairo_output_stream_t *stream);
+
+cairo_private cairo_status_t
+_cairo_output_stream_get_status (cairo_output_stream_t *stream);
+
+/* This function never returns %NULL. If an error occurs (NO_MEMORY or
+ * WRITE_ERROR) while trying to create the output stream this function
+ * returns a valid pointer to a nil output stream.
+ *
+ * Note: Even if a nil surface is returned, the caller should still
+ * call _cairo_output_stream_destroy (or _cairo_output_stream_close at
+ * least) in order to ensure that everything is properly cleaned up.
+ */
+cairo_private cairo_output_stream_t *
+_cairo_output_stream_create_for_filename (const char *filename);
+
+/* This function never returns %NULL. If an error occurs (NO_MEMORY or
+ * WRITE_ERROR) while trying to create the output stream this function
+ * returns a valid pointer to a nil output stream.
+ *
+ * The caller still "owns" file and is responsible for calling fclose
+ * on it when finished. The stream will not do this itself.
+ */
+cairo_private cairo_output_stream_t *
+_cairo_output_stream_create_for_file (FILE *file);
+
+cairo_private cairo_output_stream_t *
+_cairo_memory_stream_create (void);
+
+cairo_private void
+_cairo_memory_stream_copy (cairo_output_stream_t *base,
+                          cairo_output_stream_t *dest);
+
+cairo_private int
+_cairo_memory_stream_length (cairo_output_stream_t *stream);
+
+cairo_private cairo_status_t
+_cairo_memory_stream_destroy (cairo_output_stream_t *abstract_stream,
+                             unsigned char **data_out,
+                             unsigned long *length_out);
+
+cairo_private cairo_output_stream_t *
+_cairo_null_stream_create (void);
+
+/* cairo-base85-stream.c */
+cairo_private cairo_output_stream_t *
+_cairo_base85_stream_create (cairo_output_stream_t *output);
+
+/* cairo-base64-stream.c */
+cairo_private cairo_output_stream_t *
+_cairo_base64_stream_create (cairo_output_stream_t *output);
+
+/* cairo-deflate-stream.c */
+cairo_private cairo_output_stream_t *
+_cairo_deflate_stream_create (cairo_output_stream_t *output);
+
+
+#endif /* CAIRO_OUTPUT_STREAM_PRIVATE_H */
diff --git a/src/cairo-output-stream.c b/src/cairo-output-stream.c
new file mode 100644 (file)
index 0000000..cc7e300
--- /dev/null
@@ -0,0 +1,766 @@
+/* cairo-output-stream.c: Output stream abstraction
+ *
+ * Copyright © 2005 Red Hat, Inc
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Author(s):
+ *     Kristian Høgsberg <krh@redhat.com>
+ */
+
+#define _BSD_SOURCE /* for snprintf() */
+#include "cairoint.h"
+
+#include "cairo-output-stream-private.h"
+
+#include "cairo-array-private.h"
+#include "cairo-error-private.h"
+#include "cairo-compiler-private.h"
+
+#include <stdio.h>
+#include <locale.h>
+#include <errno.h>
+
+/* Numbers printed with %f are printed with this number of significant
+ * digits after the decimal.
+ */
+#define SIGNIFICANT_DIGITS_AFTER_DECIMAL 6
+
+/* Numbers printed with %g are assumed to only have %CAIRO_FIXED_FRAC_BITS
+ * bits of precision available after the decimal point.
+ *
+ * FIXED_POINT_DECIMAL_DIGITS specifies the minimum number of decimal
+ * digits after the decimal point required to preserve the available
+ * precision.
+ *
+ * The conversion is:
+ *
+ * <programlisting>
+ * FIXED_POINT_DECIMAL_DIGITS = ceil( CAIRO_FIXED_FRAC_BITS * ln(2)/ln(10) )
+ * </programlisting>
+ *
+ * We can replace ceil(x) with (int)(x+1) since x will never be an
+ * integer for any likely value of %CAIRO_FIXED_FRAC_BITS.
+ */
+#define FIXED_POINT_DECIMAL_DIGITS ((int)(CAIRO_FIXED_FRAC_BITS*0.301029996 + 1))
+
+void
+_cairo_output_stream_init (cairo_output_stream_t            *stream,
+                          cairo_output_stream_write_func_t  write_func,
+                          cairo_output_stream_flush_func_t  flush_func,
+                          cairo_output_stream_close_func_t  close_func)
+{
+    stream->write_func = write_func;
+    stream->flush_func = flush_func;
+    stream->close_func = close_func;
+    stream->position = 0;
+    stream->status = CAIRO_STATUS_SUCCESS;
+    stream->closed = FALSE;
+}
+
+cairo_status_t
+_cairo_output_stream_fini (cairo_output_stream_t *stream)
+{
+    return _cairo_output_stream_close (stream);
+}
+
+const cairo_output_stream_t _cairo_output_stream_nil = {
+    NULL, /* write_func */
+    NULL, /* flush_func */
+    NULL, /* close_func */
+    0,    /* position */
+    CAIRO_STATUS_NO_MEMORY,
+    FALSE /* closed */
+};
+
+static const cairo_output_stream_t _cairo_output_stream_nil_write_error = {
+    NULL, /* write_func */
+    NULL, /* flush_func */
+    NULL, /* close_func */
+    0,    /* position */
+    CAIRO_STATUS_WRITE_ERROR,
+    FALSE /* closed */
+};
+
+typedef struct _cairo_output_stream_with_closure {
+    cairo_output_stream_t       base;
+    cairo_write_func_t          write_func;
+    cairo_close_func_t          close_func;
+    void                       *closure;
+} cairo_output_stream_with_closure_t;
+
+
+static cairo_status_t
+closure_write (cairo_output_stream_t *stream,
+              const unsigned char *data, unsigned int length)
+{
+    cairo_output_stream_with_closure_t *stream_with_closure =
+       (cairo_output_stream_with_closure_t *) stream;
+
+    if (stream_with_closure->write_func == NULL)
+       return CAIRO_STATUS_SUCCESS;
+
+    return stream_with_closure->write_func (stream_with_closure->closure,
+                                           data, length);
+}
+
+static cairo_status_t
+closure_close (cairo_output_stream_t *stream)
+{
+    cairo_output_stream_with_closure_t *stream_with_closure =
+       (cairo_output_stream_with_closure_t *) stream;
+
+    if (stream_with_closure->close_func != NULL)
+       return stream_with_closure->close_func (stream_with_closure->closure);
+    else
+       return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_output_stream_t *
+_cairo_output_stream_create (cairo_write_func_t                write_func,
+                            cairo_close_func_t         close_func,
+                            void                       *closure)
+{
+    cairo_output_stream_with_closure_t *stream;
+
+    stream = malloc (sizeof (cairo_output_stream_with_closure_t));
+    if (unlikely (stream == NULL)) {
+       _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+       return (cairo_output_stream_t *) &_cairo_output_stream_nil;
+    }
+
+    _cairo_output_stream_init (&stream->base,
+                              closure_write, NULL, closure_close);
+    stream->write_func = write_func;
+    stream->close_func = close_func;
+    stream->closure = closure;
+
+    return &stream->base;
+}
+
+cairo_output_stream_t *
+_cairo_output_stream_create_in_error (cairo_status_t status)
+{
+    cairo_output_stream_t *stream;
+
+    /* check for the common ones */
+    if (status == CAIRO_STATUS_NO_MEMORY)
+       return (cairo_output_stream_t *) &_cairo_output_stream_nil;
+    if (status == CAIRO_STATUS_WRITE_ERROR)
+       return (cairo_output_stream_t *) &_cairo_output_stream_nil_write_error;
+
+    stream = malloc (sizeof (cairo_output_stream_t));
+    if (unlikely (stream == NULL)) {
+       _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+       return (cairo_output_stream_t *) &_cairo_output_stream_nil;
+    }
+
+    _cairo_output_stream_init (stream, NULL, NULL, NULL);
+    stream->status = status;
+
+    return stream;
+}
+
+cairo_status_t
+_cairo_output_stream_flush (cairo_output_stream_t *stream)
+{
+    cairo_status_t status;
+
+    if (stream->closed)
+       return stream->status;
+
+    if (stream == &_cairo_output_stream_nil ||
+       stream == &_cairo_output_stream_nil_write_error)
+    {
+       return stream->status;
+    }
+
+    if (stream->flush_func) {
+       status = stream->flush_func (stream);
+       /* Don't overwrite a pre-existing status failure. */
+       if (stream->status == CAIRO_STATUS_SUCCESS)
+           stream->status = status;
+    }
+
+    return stream->status;
+}
+
+cairo_status_t
+_cairo_output_stream_close (cairo_output_stream_t *stream)
+{
+    cairo_status_t status;
+
+    if (stream->closed)
+       return stream->status;
+
+    if (stream == &_cairo_output_stream_nil ||
+       stream == &_cairo_output_stream_nil_write_error)
+    {
+       return stream->status;
+    }
+
+    if (stream->close_func) {
+       status = stream->close_func (stream);
+       /* Don't overwrite a pre-existing status failure. */
+       if (stream->status == CAIRO_STATUS_SUCCESS)
+           stream->status = status;
+    }
+
+    stream->closed = TRUE;
+
+    return stream->status;
+}
+
+cairo_status_t
+_cairo_output_stream_destroy (cairo_output_stream_t *stream)
+{
+    cairo_status_t status;
+
+    assert (stream != NULL);
+
+    if (stream == &_cairo_output_stream_nil ||
+       stream == &_cairo_output_stream_nil_write_error)
+    {
+       return stream->status;
+    }
+
+    status = _cairo_output_stream_fini (stream);
+    free (stream);
+
+    return status;
+}
+
+void
+_cairo_output_stream_write (cairo_output_stream_t *stream,
+                           const void *data, size_t length)
+{
+    if (length == 0)
+       return;
+
+    if (stream->status)
+       return;
+
+    stream->status = stream->write_func (stream, data, length);
+    stream->position += length;
+}
+
+void
+_cairo_output_stream_write_hex_string (cairo_output_stream_t *stream,
+                                      const unsigned char *data,
+                                      size_t length)
+{
+    const char hex_chars[] = "0123456789abcdef";
+    char buffer[2];
+    unsigned int i, column;
+
+    if (stream->status)
+       return;
+
+    for (i = 0, column = 0; i < length; i++, column++) {
+       if (column == 38) {
+           _cairo_output_stream_write (stream, "\n", 1);
+           column = 0;
+       }
+       buffer[0] = hex_chars[(data[i] >> 4) & 0x0f];
+       buffer[1] = hex_chars[data[i] & 0x0f];
+       _cairo_output_stream_write (stream, buffer, 2);
+    }
+}
+
+/* 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, but
+ * has been relicensed under the LGPL/MPL dual license for inclusion
+ * into cairo (see COPYING). -- Kristian Høgsberg <krh@redhat.com>
+ */
+static void
+_cairo_dtostr (char *buffer, size_t size, double d, cairo_bool_t limited_precision)
+{
+    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);
+
+    assert (decimal_point_len != 0);
+
+    if (limited_precision) {
+       snprintf (buffer, size, "%.*f", FIXED_POINT_DECIMAL_DIGITS, d);
+    } else {
+       /* 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 (_cairo_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 (_cairo_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.
+ */
+void
+_cairo_output_stream_vprintf (cairo_output_stream_t *stream,
+                             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;
+
+    if (stream->status)
+       return;
+
+    f = fmt;
+    p = buffer;
+    while (*f != '\0') {
+       if (p == buffer + sizeof (buffer)) {
+           _cairo_output_stream_write (stream, buffer, sizeof (buffer));
+           p = buffer;
+       }
+
+       if (*f != '%') {
+           *p++ = *f++;
+           continue;
+       }
+
+       start = f;
+       f++;
+
+       if (*f == '0')
+           f++;
+
+        var_width = FALSE;
+        if (*f == '*') {
+            var_width = TRUE;
+           f++;
+        }
+
+       while (_cairo_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;
+       assert (single_fmt_length + 1 <= SINGLE_FMT_BUFFER_SIZE);
+
+       /* 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. */
+       _cairo_output_stream_write (stream, buffer, p - buffer);
+
+       /* 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':
+           _cairo_dtostr (buffer, sizeof buffer, va_arg (ap, double), FALSE);
+           break;
+       case 'g':
+           _cairo_dtostr (buffer, sizeof buffer, va_arg (ap, double), TRUE);
+           break;
+       case 'c':
+           buffer[0] = va_arg (ap, int);
+           buffer[1] = 0;
+           break;
+       default:
+           ASSERT_NOT_REACHED;
+       }
+       p = buffer + strlen (buffer);
+       f++;
+    }
+
+    _cairo_output_stream_write (stream, buffer, p - buffer);
+}
+
+void
+_cairo_output_stream_printf (cairo_output_stream_t *stream,
+                            const char *fmt, ...)
+{
+    va_list ap;
+
+    va_start (ap, fmt);
+
+    _cairo_output_stream_vprintf (stream, fmt, ap);
+
+    va_end (ap);
+}
+
+long
+_cairo_output_stream_get_position (cairo_output_stream_t *stream)
+{
+    return stream->position;
+}
+
+cairo_status_t
+_cairo_output_stream_get_status (cairo_output_stream_t *stream)
+{
+    return stream->status;
+}
+
+/* Maybe this should be a configure time option, so embedded targets
+ * don't have to pull in stdio. */
+
+
+typedef struct _stdio_stream {
+    cairo_output_stream_t       base;
+    FILE                       *file;
+} stdio_stream_t;
+
+static cairo_status_t
+stdio_write (cairo_output_stream_t *base,
+            const unsigned char *data, unsigned int length)
+{
+    stdio_stream_t *stream = (stdio_stream_t *) base;
+
+    if (fwrite (data, 1, length, stream->file) != length)
+       return _cairo_error (CAIRO_STATUS_WRITE_ERROR);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+stdio_flush (cairo_output_stream_t *base)
+{
+    stdio_stream_t *stream = (stdio_stream_t *) base;
+
+    fflush (stream->file);
+
+    if (ferror (stream->file))
+       return _cairo_error (CAIRO_STATUS_WRITE_ERROR);
+    else
+       return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+stdio_close (cairo_output_stream_t *base)
+{
+    cairo_status_t status;
+    stdio_stream_t *stream = (stdio_stream_t *) base;
+
+    status = stdio_flush (base);
+
+    fclose (stream->file);
+
+    return status;
+}
+
+cairo_output_stream_t *
+_cairo_output_stream_create_for_file (FILE *file)
+{
+    stdio_stream_t *stream;
+
+    if (file == NULL) {
+       _cairo_error_throw (CAIRO_STATUS_WRITE_ERROR);
+       return (cairo_output_stream_t *) &_cairo_output_stream_nil_write_error;
+    }
+
+    stream = malloc (sizeof *stream);
+    if (unlikely (stream == NULL)) {
+       _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+       return (cairo_output_stream_t *) &_cairo_output_stream_nil;
+    }
+
+    _cairo_output_stream_init (&stream->base,
+                              stdio_write, stdio_flush, stdio_flush);
+    stream->file = file;
+
+    return &stream->base;
+}
+
+cairo_output_stream_t *
+_cairo_output_stream_create_for_filename (const char *filename)
+{
+    stdio_stream_t *stream;
+    FILE *file;
+
+    if (filename == NULL)
+       return _cairo_null_stream_create ();
+
+    file = fopen (filename, "wb");
+    if (file == NULL) {
+       switch (errno) {
+       case ENOMEM:
+           _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+           return (cairo_output_stream_t *) &_cairo_output_stream_nil;
+       default:
+           _cairo_error_throw (CAIRO_STATUS_WRITE_ERROR);
+           return (cairo_output_stream_t *) &_cairo_output_stream_nil_write_error;
+       }
+    }
+
+    stream = malloc (sizeof *stream);
+    if (unlikely (stream == NULL)) {
+       fclose (file);
+       _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+       return (cairo_output_stream_t *) &_cairo_output_stream_nil;
+    }
+
+    _cairo_output_stream_init (&stream->base,
+                              stdio_write, stdio_flush, stdio_close);
+    stream->file = file;
+
+    return &stream->base;
+}
+
+
+typedef struct _memory_stream {
+    cairo_output_stream_t      base;
+    cairo_array_t              array;
+} memory_stream_t;
+
+static cairo_status_t
+memory_write (cairo_output_stream_t *base,
+             const unsigned char *data, unsigned int length)
+{
+    memory_stream_t *stream = (memory_stream_t *) base;
+
+    return _cairo_array_append_multiple (&stream->array, data, length);
+}
+
+static cairo_status_t
+memory_close (cairo_output_stream_t *base)
+{
+    memory_stream_t *stream = (memory_stream_t *) base;
+
+    _cairo_array_fini (&stream->array);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_output_stream_t *
+_cairo_memory_stream_create (void)
+{
+    memory_stream_t *stream;
+
+    stream = malloc (sizeof *stream);
+    if (unlikely (stream == NULL)) {
+       _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+       return (cairo_output_stream_t *) &_cairo_output_stream_nil;
+    }
+
+    _cairo_output_stream_init (&stream->base, memory_write, NULL, memory_close);
+    _cairo_array_init (&stream->array, 1);
+
+    return &stream->base;
+}
+
+cairo_status_t
+_cairo_memory_stream_destroy (cairo_output_stream_t *abstract_stream,
+                             unsigned char **data_out,
+                             unsigned long *length_out)
+{
+    memory_stream_t *stream;
+    cairo_status_t status;
+
+    status = abstract_stream->status;
+    if (unlikely (status))
+       return _cairo_output_stream_destroy (abstract_stream);
+
+    stream = (memory_stream_t *) abstract_stream;
+
+    *length_out = _cairo_array_num_elements (&stream->array);
+    *data_out = malloc (*length_out);
+    if (unlikely (*data_out == NULL)) {
+       status = _cairo_output_stream_destroy (abstract_stream);
+       assert (status == CAIRO_STATUS_SUCCESS);
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+    }
+    memcpy (*data_out, _cairo_array_index (&stream->array, 0), *length_out);
+
+    return _cairo_output_stream_destroy (abstract_stream);
+}
+
+void
+_cairo_memory_stream_copy (cairo_output_stream_t *base,
+                          cairo_output_stream_t *dest)
+{
+    memory_stream_t *stream = (memory_stream_t *) base;
+
+    if (dest->status)
+       return;
+
+    if (base->status) {
+       dest->status = base->status;
+       return;
+    }
+
+    _cairo_output_stream_write (dest,
+                               _cairo_array_index (&stream->array, 0),
+                               _cairo_array_num_elements (&stream->array));
+}
+
+int
+_cairo_memory_stream_length (cairo_output_stream_t *base)
+{
+    memory_stream_t *stream = (memory_stream_t *) base;
+
+    return _cairo_array_num_elements (&stream->array);
+}
+
+static cairo_status_t
+null_write (cairo_output_stream_t *base,
+           const unsigned char *data, unsigned int length)
+{
+    return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_output_stream_t *
+_cairo_null_stream_create (void)
+{
+    cairo_output_stream_t *stream;
+
+    stream = malloc (sizeof *stream);
+    if (unlikely (stream == NULL)) {
+       _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+       return (cairo_output_stream_t *) &_cairo_output_stream_nil;
+    }
+
+    _cairo_output_stream_init (stream, null_write, NULL, NULL);
+
+    return stream;
+}
diff --git a/src/cairo-paginated-private.h b/src/cairo-paginated-private.h
new file mode 100644 (file)
index 0000000..b827fab
--- /dev/null
@@ -0,0 +1,168 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2005 Red Hat, Inc
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ *     Carl Worth <cworth@cworth.org>
+ */
+
+#ifndef CAIRO_PAGINATED_H
+#define CAIRO_PAGINATED_H
+
+#include "cairoint.h"
+
+struct _cairo_paginated_surface_backend {
+    /* Optional. Will be called once for each page.
+     *
+     * Note: With respect to the order of drawing operations as seen
+     * by the target, this call will occur before any drawing
+     * operations for the relevant page. However, with respect to the
+     * function calls as made by the user, this call will be *after*
+     * any drawing operations for the page, (that is, it will occur
+     * during the user's call to cairo_show_page or cairo_copy_page).
+     */
+    cairo_warn cairo_int_status_t
+    (*start_page)              (void                   *surface);
+
+    /* Required. Will be called twice for each page, once with an
+     * argument of CAIRO_PAGINATED_MODE_ANALYZE and once with
+     * CAIRO_PAGINATED_MODE_RENDER. See more details in the
+     * documentation for _cairo_paginated_surface_create below.
+     */
+    void
+    (*set_paginated_mode)      (void                   *surface,
+                                cairo_paginated_mode_t  mode);
+
+    /* Optional. Specifies the smallest box that encloses all objects
+     * on the page. Will be called at the end of the ANALYZE phase but
+     * before the mode is changed to RENDER.
+     */
+    cairo_warn cairo_int_status_t
+    (*set_bounding_box)        (void           *surface,
+                        cairo_box_t    *bbox);
+
+    /* Optional. Indicates whether the page requires fallback images.
+     * Will be called at the end of the ANALYZE phase but before the
+     * mode is changed to RENDER.
+     */
+    cairo_warn cairo_int_status_t
+    (*set_fallback_images_required) (void          *surface,
+                                    cairo_bool_t    fallbacks_required);
+
+    cairo_bool_t
+    (*supports_fine_grained_fallbacks) (void               *surface);
+};
+
+/* A #cairo_paginated_surface_t provides a very convenient wrapper that
+ * is well-suited for doing the analysis common to most surfaces that
+ * have paginated output, (that is, things directed at printers, or
+ * for saving content in files such as PostScript or PDF files).
+ *
+ * To use the paginated surface, you'll first need to create your
+ * 'real' surface using _cairo_surface_init() and the standard
+ * #cairo_surface_backend_t. Then you also call
+ * _cairo_paginated_surface_create which takes its own, much simpler,
+ * #cairo_paginated_surface_backend_t. You are free to return the result
+ * of _cairo_paginated_surface_create() from your public
+ * cairo_<foo>_surface_create(). The paginated backend will be careful
+ * to not let the user see that they really got a "wrapped"
+ * surface. See test-paginated-surface.c for a fairly minimal example
+ * of a paginated-using surface. That should be a reasonable example
+ * to follow.
+ *
+ * What the paginated surface does is first save all drawing
+ * operations for a page into a recording-surface. Then when the user calls
+ * cairo_show_page(), the paginated surface performs the following
+ * sequence of operations (using the backend functions passed to
+ * cairo_paginated_surface_create()):
+ *
+ * 1. Calls start_page() (if not %NULL). At this point, it is appropriate
+ *    for the target to emit any page-specific header information into
+ *    its output.
+ *
+ * 2. Calls set_paginated_mode() with an argument of %CAIRO_PAGINATED_MODE_ANALYZE
+ *
+ * 3. Replays the recording-surface to the target surface, (with an
+ *    analysis surface inserted between which watches the return value
+ *    from each operation). This analysis stage is used to decide which
+ *    operations will require fallbacks.
+ *
+ * 4. Calls set_bounding_box() to provide the target surface with the
+ *    tight bounding box of the page.
+ *
+ * 5. Calls set_paginated_mode() with an argument of %CAIRO_PAGINATED_MODE_RENDER
+ *
+ * 6. Replays a subset of the recording-surface operations to the target surface
+ *
+ * 7. Calls set_paginated_mode() with an argument of %CAIRO_PAGINATED_MODE_FALLBACK
+ *
+ * 8. Replays the remaining operations to an image surface, sets an
+ *    appropriate clip on the target, then paints the resulting image
+ *    surface to the target.
+ *
+ * So, the target will see drawing operations during three separate
+ * stages, (ANALYZE, RENDER and FALLBACK). During the ANALYZE phase
+ * the target should not actually perform any rendering, (for example,
+ * if performing output to a file, no output should be generated
+ * during this stage). Instead the drawing functions simply need to
+ * return %CAIRO_STATUS_SUCCESS or %CAIRO_INT_STATUS_UNSUPPORTED to
+ * indicate whether rendering would be supported. And it should do
+ * this as quickly as possible. The FALLBACK phase allows the surface
+ * to distinguish fallback images from native rendering in case they
+ * need to be handled as a special case.
+ *
+ * Note: The paginated surface layer assumes that the target surface
+ * is "blank" by default at the beginning of each page, without any
+ * need for an explicit erase operation, (as opposed to an image
+ * surface, for example, which might have uninitialized content
+ * originally). As such, it optimizes away CLEAR operations that
+ * happen at the beginning of each page---the target surface will not
+ * even see these operations.
+ */
+cairo_private cairo_surface_t *
+_cairo_paginated_surface_create (cairo_surface_t                               *target,
+                                cairo_content_t                                 content,
+                                const cairo_paginated_surface_backend_t        *backend);
+
+cairo_private cairo_surface_t *
+_cairo_paginated_surface_get_target (cairo_surface_t *surface);
+
+cairo_private cairo_surface_t *
+_cairo_paginated_surface_get_recording (cairo_surface_t *surface);
+
+cairo_private cairo_bool_t
+_cairo_surface_is_paginated (cairo_surface_t *surface);
+
+cairo_private cairo_status_t
+_cairo_paginated_surface_set_size (cairo_surface_t     *surface,
+                                  int                   width,
+                                  int                   height);
+
+#endif /* CAIRO_PAGINATED_H */
diff --git a/src/cairo-paginated-surface-private.h b/src/cairo-paginated-surface-private.h
new file mode 100644 (file)
index 0000000..ebf4b34
--- /dev/null
@@ -0,0 +1,62 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2005 Red Hat, Inc
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ *     Carl Worth <cworth@cworth.org>
+ */
+
+#ifndef CAIRO_PAGINATED_SURFACE_H
+#define CAIRO_PAGINATED_SURFACE_H
+
+#include "cairo.h"
+
+#include "cairo-surface-private.h"
+
+typedef struct _cairo_paginated_surface {
+    cairo_surface_t base;
+
+    /* The target surface to hold the final result. */
+    cairo_surface_t *target;
+
+    cairo_content_t content;
+
+    /* Paginated-surface specific functions for the target */
+    const cairo_paginated_surface_backend_t *backend;
+
+    /* A cairo_recording_surface to record all operations. To be replayed
+     * against target, and also against image surface as necessary for
+     * fallbacks. */
+    cairo_surface_t *recording_surface;
+
+    int page_num;
+} cairo_paginated_surface_t;
+
+#endif /* CAIRO_PAGINATED_SURFACE_H */
diff --git a/src/cairo-paginated-surface.c b/src/cairo-paginated-surface.c
new file mode 100644 (file)
index 0000000..0418e67
--- /dev/null
@@ -0,0 +1,695 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2005 Red Hat, Inc
+ * Copyright © 2007 Adrian Johnson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ *     Carl Worth <cworth@cworth.org>
+ *     Keith Packard <keithp@keithp.com>
+ *     Adrian Johnson <ajohnson@redneon.com>
+ */
+
+/* The paginated surface layer exists to provide as much code sharing
+ * as possible for the various paginated surface backends in cairo
+ * (PostScript, PDF, etc.). See cairo-paginated-private.h for
+ * more details on how it works and how to use it.
+ */
+
+#include "cairoint.h"
+
+#include "cairo-paginated-private.h"
+#include "cairo-paginated-surface-private.h"
+#include "cairo-recording-surface-private.h"
+#include "cairo-analysis-surface-private.h"
+#include "cairo-error-private.h"
+#include "cairo-image-surface-private.h"
+#include "cairo-surface-subsurface-private.h"
+
+static const cairo_surface_backend_t cairo_paginated_surface_backend;
+
+static cairo_int_status_t
+_cairo_paginated_surface_show_page (void *abstract_surface);
+
+static cairo_surface_t *
+_cairo_paginated_surface_create_similar (void                  *abstract_surface,
+                                        cairo_content_t         content,
+                                        int                     width,
+                                        int                     height)
+{
+    cairo_rectangle_t rect;
+    rect.x = rect.y = 0.;
+    rect.width = width;
+    rect.height = height;
+    return cairo_recording_surface_create (content, &rect);
+}
+
+static cairo_surface_t *
+_create_recording_surface_for_target (cairo_surface_t *target,
+                                     cairo_content_t content)
+{
+    cairo_rectangle_int_t rect;
+
+    if (_cairo_surface_get_extents (target, &rect)) {
+       cairo_rectangle_t recording_extents;
+
+       recording_extents.x = rect.x;
+       recording_extents.y = rect.y;
+       recording_extents.width = rect.width;
+       recording_extents.height = rect.height;
+
+       return cairo_recording_surface_create (content, &recording_extents);
+    } else {
+       return cairo_recording_surface_create (content, NULL);
+    }
+}
+
+cairo_surface_t *
+_cairo_paginated_surface_create (cairo_surface_t                               *target,
+                                cairo_content_t                                 content,
+                                const cairo_paginated_surface_backend_t        *backend)
+{
+    cairo_paginated_surface_t *surface;
+    cairo_status_t status;
+
+    surface = malloc (sizeof (cairo_paginated_surface_t));
+    if (unlikely (surface == NULL)) {
+       status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+       goto FAIL;
+    }
+
+    _cairo_surface_init (&surface->base,
+                        &cairo_paginated_surface_backend,
+                        NULL, /* device */
+                        content);
+
+    /* Override surface->base.type with target's type so we don't leak
+     * evidence of the paginated wrapper out to the user. */
+    surface->base.type = target->type;
+
+    surface->target = cairo_surface_reference (target);
+
+    surface->content = content;
+    surface->backend = backend;
+
+    surface->recording_surface = _create_recording_surface_for_target (target, content);
+    status = surface->recording_surface->status;
+    if (unlikely (status))
+       goto FAIL_CLEANUP_SURFACE;
+
+    surface->page_num = 1;
+    surface->base.is_clear = TRUE;
+
+    return &surface->base;
+
+  FAIL_CLEANUP_SURFACE:
+    cairo_surface_destroy (target);
+    free (surface);
+  FAIL:
+    return _cairo_surface_create_in_error (status);
+}
+
+cairo_bool_t
+_cairo_surface_is_paginated (cairo_surface_t *surface)
+{
+    return surface->backend == &cairo_paginated_surface_backend;
+}
+
+cairo_surface_t *
+_cairo_paginated_surface_get_target (cairo_surface_t *surface)
+{
+    cairo_paginated_surface_t *paginated_surface;
+
+    assert (_cairo_surface_is_paginated (surface));
+
+    paginated_surface = (cairo_paginated_surface_t *) surface;
+    return paginated_surface->target;
+}
+
+cairo_surface_t *
+_cairo_paginated_surface_get_recording (cairo_surface_t *surface)
+{
+    cairo_paginated_surface_t *paginated_surface;
+
+    assert (_cairo_surface_is_paginated (surface));
+
+    paginated_surface = (cairo_paginated_surface_t *) surface;
+    return paginated_surface->recording_surface;
+}
+
+cairo_status_t
+_cairo_paginated_surface_set_size (cairo_surface_t     *surface,
+                                  int                   width,
+                                  int                   height)
+{
+    cairo_paginated_surface_t *paginated_surface;
+    cairo_status_t status;
+    cairo_rectangle_t recording_extents;
+
+    assert (_cairo_surface_is_paginated (surface));
+
+    paginated_surface = (cairo_paginated_surface_t *) surface;
+
+    recording_extents.x = 0;
+    recording_extents.y = 0;
+    recording_extents.width = width;
+    recording_extents.height = height;
+
+    cairo_surface_destroy (paginated_surface->recording_surface);
+    paginated_surface->recording_surface = cairo_recording_surface_create (paginated_surface->content,
+                                                                          &recording_extents);
+    status = paginated_surface->recording_surface->status;
+    if (unlikely (status))
+       return _cairo_surface_set_error (surface, status);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_paginated_surface_finish (void *abstract_surface)
+{
+    cairo_paginated_surface_t *surface = abstract_surface;
+    cairo_status_t status = CAIRO_STATUS_SUCCESS;
+
+    if (! surface->base.is_clear || surface->page_num == 1) {
+       /* Bypass some of the sanity checking in cairo-surface.c, as we
+        * know that the surface is finished...
+        */
+       status = _cairo_paginated_surface_show_page (surface);
+    }
+
+     /* XXX We want to propagate any errors from destroy(), but those are not
+      * returned via the api. So we need to explicitly finish the target,
+      * and check the status afterwards. However, we can only call finish()
+      * on the target, if we own it.
+      */
+    if (CAIRO_REFERENCE_COUNT_GET_VALUE (&surface->target->ref_count) == 1)
+       cairo_surface_finish (surface->target);
+    if (status == CAIRO_STATUS_SUCCESS)
+       status = cairo_surface_status (surface->target);
+    cairo_surface_destroy (surface->target);
+
+    cairo_surface_finish (surface->recording_surface);
+    if (status == CAIRO_STATUS_SUCCESS)
+       status = cairo_surface_status (surface->recording_surface);
+    cairo_surface_destroy (surface->recording_surface);
+
+    return status;
+}
+
+static cairo_surface_t *
+_cairo_paginated_surface_create_image_surface (void           *abstract_surface,
+                                              int              width,
+                                              int              height)
+{
+    cairo_paginated_surface_t *surface = abstract_surface;
+    cairo_surface_t *image;
+    cairo_font_options_t options;
+
+    image = _cairo_image_surface_create_with_content (surface->content,
+                                                     width,
+                                                     height);
+
+    cairo_surface_get_font_options (&surface->base, &options);
+    _cairo_surface_set_font_options (image, &options);
+
+    return image;
+}
+
+static cairo_status_t
+_cairo_paginated_surface_acquire_source_image (void           *abstract_surface,
+                                              cairo_image_surface_t **image_out,
+                                              void                **image_extra)
+{
+    cairo_paginated_surface_t *surface = abstract_surface;
+    cairo_bool_t is_bounded;
+    cairo_surface_t *image;
+    cairo_status_t status;
+    cairo_rectangle_int_t extents;
+
+    is_bounded = _cairo_surface_get_extents (surface->target, &extents);
+    if (! is_bounded)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    image = _cairo_paginated_surface_create_image_surface (surface,
+                                                          extents.width,
+                                                          extents.height);
+
+    status = _cairo_recording_surface_replay (surface->recording_surface, image);
+    if (unlikely (status)) {
+       cairo_surface_destroy (image);
+       return status;
+    }
+
+    *image_out = (cairo_image_surface_t*) image;
+    *image_extra = NULL;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_paginated_surface_release_source_image (void      *abstract_surface,
+                                              cairo_image_surface_t *image,
+                                              void            *image_extra)
+{
+    cairo_surface_destroy (&image->base);
+}
+
+static cairo_int_status_t
+_paint_fallback_image (cairo_paginated_surface_t *surface,
+                      cairo_rectangle_int_t     *rect)
+{
+    double x_scale = surface->base.x_fallback_resolution / surface->target->x_resolution;
+    double y_scale = surface->base.y_fallback_resolution / surface->target->y_resolution;
+    int x, y, width, height;
+    cairo_status_t status;
+    cairo_surface_t *image;
+    cairo_surface_pattern_t pattern;
+    cairo_clip_t *clip;
+
+    x = rect->x;
+    y = rect->y;
+    width = rect->width;
+    height = rect->height;
+    image = _cairo_paginated_surface_create_image_surface (surface,
+                                                          ceil (width  * x_scale),
+                                                          ceil (height * y_scale));
+    _cairo_surface_set_device_scale (image, x_scale, y_scale);
+    /* set_device_offset just sets the x0/y0 components of the matrix;
+     * so we have to do the scaling manually. */
+    cairo_surface_set_device_offset (image, -x*x_scale, -y*y_scale);
+
+    status = _cairo_recording_surface_replay (surface->recording_surface, image);
+    if (unlikely (status))
+       goto CLEANUP_IMAGE;
+
+    _cairo_pattern_init_for_surface (&pattern, image);
+    cairo_matrix_init (&pattern.base.matrix,
+                      x_scale, 0, 0, y_scale, -x*x_scale, -y*y_scale);
+    /* the fallback should be rendered at native resolution, so disable
+     * filtering (if possible) to avoid introducing potential artifacts. */
+    pattern.base.filter = CAIRO_FILTER_NEAREST;
+
+    clip = _cairo_clip_intersect_rectangle (NULL, rect);
+    status = _cairo_surface_paint (surface->target,
+                                  CAIRO_OPERATOR_SOURCE,
+                                  &pattern.base, clip);
+    _cairo_clip_destroy (clip);
+    _cairo_pattern_fini (&pattern.base);
+
+CLEANUP_IMAGE:
+    cairo_surface_destroy (image);
+
+    return status;
+}
+
+static cairo_int_status_t
+_paint_page (cairo_paginated_surface_t *surface)
+{
+    cairo_surface_t *analysis;
+    cairo_int_status_t status;
+    cairo_bool_t has_supported, has_page_fallback, has_finegrained_fallback;
+
+    if (unlikely (surface->target->status))
+       return surface->target->status;
+
+    analysis = _cairo_analysis_surface_create (surface->target);
+    if (unlikely (analysis->status))
+       return _cairo_surface_set_error (surface->target, analysis->status);
+
+    surface->backend->set_paginated_mode (surface->target,
+                                         CAIRO_PAGINATED_MODE_ANALYZE);
+    status = _cairo_recording_surface_replay_and_create_regions (surface->recording_surface,
+                                                                analysis);
+    if (status)
+       goto FAIL;
+
+    assert (analysis->status == CAIRO_STATUS_SUCCESS);
+
+     if (surface->backend->set_bounding_box) {
+        cairo_box_t bbox;
+
+        _cairo_analysis_surface_get_bounding_box (analysis, &bbox);
+        status = surface->backend->set_bounding_box (surface->target, &bbox);
+        if (unlikely (status))
+            goto FAIL;
+     }
+
+    if (surface->backend->set_fallback_images_required) {
+       cairo_bool_t has_fallbacks = _cairo_analysis_surface_has_unsupported (analysis);
+
+       status = surface->backend->set_fallback_images_required (surface->target,
+                                                                has_fallbacks);
+       if (unlikely (status))
+           goto FAIL;
+    }
+
+    /* Finer grained fallbacks are currently only supported for some
+     * surface types */
+    if (surface->backend->supports_fine_grained_fallbacks != NULL &&
+       surface->backend->supports_fine_grained_fallbacks (surface->target))
+    {
+       has_supported = _cairo_analysis_surface_has_supported (analysis);
+       has_page_fallback = FALSE;
+       has_finegrained_fallback = _cairo_analysis_surface_has_unsupported (analysis);
+    }
+    else
+    {
+       if (_cairo_analysis_surface_has_unsupported (analysis)) {
+           has_supported = FALSE;
+           has_page_fallback = TRUE;
+       } else {
+           has_supported = TRUE;
+           has_page_fallback = FALSE;
+       }
+       has_finegrained_fallback = FALSE;
+    }
+
+    if (has_supported) {
+       surface->backend->set_paginated_mode (surface->target,
+                                             CAIRO_PAGINATED_MODE_RENDER);
+
+       status = _cairo_recording_surface_replay_region (surface->recording_surface,
+                                                        NULL,
+                                                        surface->target,
+                                                        CAIRO_RECORDING_REGION_NATIVE);
+       assert (status != CAIRO_INT_STATUS_UNSUPPORTED);
+       if (unlikely (status))
+           goto FAIL;
+    }
+
+    if (has_page_fallback) {
+       cairo_rectangle_int_t extents;
+       cairo_bool_t is_bounded;
+
+       surface->backend->set_paginated_mode (surface->target,
+                                             CAIRO_PAGINATED_MODE_FALLBACK);
+
+       is_bounded = _cairo_surface_get_extents (surface->target, &extents);
+       if (! is_bounded) {
+           status = CAIRO_INT_STATUS_UNSUPPORTED;
+           goto FAIL;
+       }
+
+       status = _paint_fallback_image (surface, &extents);
+       if (unlikely (status))
+           goto FAIL;
+    }
+
+    if (has_finegrained_fallback) {
+        cairo_region_t *region;
+        int num_rects, i;
+
+       surface->backend->set_paginated_mode (surface->target,
+                                             CAIRO_PAGINATED_MODE_FALLBACK);
+
+       region = _cairo_analysis_surface_get_unsupported (analysis);
+
+       num_rects = cairo_region_num_rectangles (region);
+       for (i = 0; i < num_rects; i++) {
+           cairo_rectangle_int_t rect;
+
+           cairo_region_get_rectangle (region, i, &rect);
+           status = _paint_fallback_image (surface, &rect);
+           if (unlikely (status))
+               goto FAIL;
+       }
+    }
+
+  FAIL:
+    cairo_surface_destroy (analysis);
+
+    return _cairo_surface_set_error (surface->target, status);
+}
+
+static cairo_status_t
+_start_page (cairo_paginated_surface_t *surface)
+{
+    if (surface->target->status)
+       return surface->target->status;
+
+    if (! surface->backend->start_page)
+       return CAIRO_STATUS_SUCCESS;
+
+    return _cairo_surface_set_error (surface->target,
+                               surface->backend->start_page (surface->target));
+}
+
+static cairo_int_status_t
+_cairo_paginated_surface_copy_page (void *abstract_surface)
+{
+    cairo_status_t status;
+    cairo_paginated_surface_t *surface = abstract_surface;
+
+    status = _start_page (surface);
+    if (unlikely (status))
+       return status;
+
+    status = _paint_page (surface);
+    if (unlikely (status))
+       return status;
+
+    surface->page_num++;
+
+    /* XXX: It might make sense to add some support here for calling
+     * cairo_surface_copy_page on the target surface. It would be an
+     * optimization for the output, but the interaction with image
+     * fallbacks gets tricky. For now, we just let the target see a
+     * show_page and we implement the copying by simply not destroying
+     * the recording-surface. */
+
+    cairo_surface_show_page (surface->target);
+    return cairo_surface_status (surface->target);
+}
+
+static cairo_int_status_t
+_cairo_paginated_surface_show_page (void *abstract_surface)
+{
+    cairo_status_t status;
+    cairo_paginated_surface_t *surface = abstract_surface;
+
+    status = _start_page (surface);
+    if (unlikely (status))
+       return status;
+
+    status = _paint_page (surface);
+    if (unlikely (status))
+       return status;
+
+    cairo_surface_show_page (surface->target);
+    status = surface->target->status;
+    if (unlikely (status))
+       return status;
+
+    status = surface->recording_surface->status;
+    if (unlikely (status))
+       return status;
+
+    if (! surface->base.finished) {
+       cairo_surface_destroy (surface->recording_surface);
+
+       surface->recording_surface = _create_recording_surface_for_target (surface->target,
+                                                                          surface->content);
+       status = surface->recording_surface->status;
+       if (unlikely (status))
+           return status;
+
+       surface->page_num++;
+       surface->base.is_clear = TRUE;
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_bool_t
+_cairo_paginated_surface_get_extents (void                   *abstract_surface,
+                                     cairo_rectangle_int_t   *rectangle)
+{
+    cairo_paginated_surface_t *surface = abstract_surface;
+
+    return _cairo_surface_get_extents (surface->target, rectangle);
+}
+
+static void
+_cairo_paginated_surface_get_font_options (void                  *abstract_surface,
+                                          cairo_font_options_t  *options)
+{
+    cairo_paginated_surface_t *surface = abstract_surface;
+
+    cairo_surface_get_font_options (surface->target, options);
+}
+
+static cairo_int_status_t
+_cairo_paginated_surface_paint (void                   *abstract_surface,
+                               cairo_operator_t         op,
+                               const cairo_pattern_t   *source,
+                               const cairo_clip_t      *clip)
+{
+    cairo_paginated_surface_t *surface = abstract_surface;
+
+    return _cairo_surface_paint (surface->recording_surface, op, source, clip);
+}
+
+static cairo_int_status_t
+_cairo_paginated_surface_mask (void            *abstract_surface,
+                              cairo_operator_t  op,
+                              const cairo_pattern_t    *source,
+                              const cairo_pattern_t    *mask,
+                              const cairo_clip_t               *clip)
+{
+    cairo_paginated_surface_t *surface = abstract_surface;
+
+    return _cairo_surface_mask (surface->recording_surface, op, source, mask, clip);
+}
+
+static cairo_int_status_t
+_cairo_paginated_surface_stroke (void                  *abstract_surface,
+                                cairo_operator_t        op,
+                                const cairo_pattern_t  *source,
+                                const cairo_path_fixed_t       *path,
+                                const cairo_stroke_style_t     *style,
+                                const cairo_matrix_t           *ctm,
+                                const cairo_matrix_t           *ctm_inverse,
+                                double                  tolerance,
+                                cairo_antialias_t       antialias,
+                                const cairo_clip_t             *clip)
+{
+    cairo_paginated_surface_t *surface = abstract_surface;
+
+    return _cairo_surface_stroke (surface->recording_surface, op, source,
+                                 path, style,
+                                 ctm, ctm_inverse,
+                                 tolerance, antialias,
+                                 clip);
+}
+
+static cairo_int_status_t
+_cairo_paginated_surface_fill (void                    *abstract_surface,
+                              cairo_operator_t          op,
+                              const cairo_pattern_t    *source,
+                              const cairo_path_fixed_t *path,
+                              cairo_fill_rule_t         fill_rule,
+                              double                    tolerance,
+                              cairo_antialias_t         antialias,
+                              const cairo_clip_t               *clip)
+{
+    cairo_paginated_surface_t *surface = abstract_surface;
+
+    return _cairo_surface_fill (surface->recording_surface, op, source,
+                               path, fill_rule,
+                               tolerance, antialias,
+                               clip);
+}
+
+static cairo_bool_t
+_cairo_paginated_surface_has_show_text_glyphs (void *abstract_surface)
+{
+    cairo_paginated_surface_t *surface = abstract_surface;
+
+    return cairo_surface_has_show_text_glyphs (surface->target);
+}
+
+static cairo_int_status_t
+_cairo_paginated_surface_show_text_glyphs (void                              *abstract_surface,
+                                          cairo_operator_t            op,
+                                          const cairo_pattern_t      *source,
+                                          const char                 *utf8,
+                                          int                         utf8_len,
+                                          cairo_glyph_t              *glyphs,
+                                          int                         num_glyphs,
+                                          const cairo_text_cluster_t *clusters,
+                                          int                         num_clusters,
+                                          cairo_text_cluster_flags_t  cluster_flags,
+                                          cairo_scaled_font_t        *scaled_font,
+                                          const cairo_clip_t                 *clip)
+{
+    cairo_paginated_surface_t *surface = abstract_surface;
+
+    return _cairo_surface_show_text_glyphs (surface->recording_surface, op, source,
+                                           utf8, utf8_len,
+                                           glyphs, num_glyphs,
+                                           clusters, num_clusters,
+                                           cluster_flags,
+                                           scaled_font,
+                                           clip);
+}
+
+static cairo_surface_t *
+_cairo_paginated_surface_snapshot (void *abstract_other)
+{
+    cairo_paginated_surface_t *other = abstract_other;
+
+    return other->recording_surface->backend->snapshot (other->recording_surface);
+}
+
+static cairo_t *
+_cairo_paginated_context_create (void *target)
+{
+    cairo_paginated_surface_t *surface = target;
+
+    if (_cairo_surface_is_subsurface (&surface->base))
+       surface = (cairo_paginated_surface_t *)
+           _cairo_surface_subsurface_get_target (&surface->base);
+
+    return surface->recording_surface->backend->create_context (target);
+}
+
+static const cairo_surface_backend_t cairo_paginated_surface_backend = {
+    CAIRO_INTERNAL_SURFACE_TYPE_PAGINATED,
+    _cairo_paginated_surface_finish,
+
+    _cairo_paginated_context_create,
+
+    _cairo_paginated_surface_create_similar,
+    NULL, /* create simlar image */
+    NULL, /* map to image */
+    NULL, /* unmap image */
+
+    _cairo_paginated_surface_acquire_source_image,
+    _cairo_paginated_surface_release_source_image,
+    _cairo_paginated_surface_snapshot,
+
+    _cairo_paginated_surface_copy_page,
+    _cairo_paginated_surface_show_page,
+
+    _cairo_paginated_surface_get_extents,
+    _cairo_paginated_surface_get_font_options,
+
+    NULL, /* flush */
+    NULL, /* mark_dirty_rectangle */
+
+    _cairo_paginated_surface_paint,
+    _cairo_paginated_surface_mask,
+    _cairo_paginated_surface_stroke,
+    _cairo_paginated_surface_fill,
+    NULL, /* fill_stroke */
+    NULL, /* show_glyphs */
+    _cairo_paginated_surface_has_show_text_glyphs,
+    _cairo_paginated_surface_show_text_glyphs
+};
diff --git a/src/cairo-path-bounds.c b/src/cairo-path-bounds.c
new file mode 100644 (file)
index 0000000..087a7d0
--- /dev/null
@@ -0,0 +1,207 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2003 University of Southern California
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *     Carl D. Worth <cworth@cworth.org>
+ */
+
+#include "cairoint.h"
+#include "cairo-box-private.h"
+#include "cairo-error-private.h"
+#include "cairo-path-fixed-private.h"
+
+typedef struct _cairo_path_bounder {
+    cairo_point_t current_point;
+    cairo_bool_t has_extents;
+    cairo_box_t extents;
+} cairo_path_bounder_t;
+
+static cairo_status_t
+_cairo_path_bounder_move_to (void *closure,
+                            const cairo_point_t *point)
+{
+    cairo_path_bounder_t *bounder = closure;
+
+    bounder->current_point = *point;
+
+    if (likely (bounder->has_extents)) {
+       _cairo_box_add_point (&bounder->extents, point);
+    } else {
+       bounder->has_extents = TRUE;
+       _cairo_box_set (&bounder->extents, point, point);
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_path_bounder_line_to (void *closure,
+                            const cairo_point_t *point)
+{
+    cairo_path_bounder_t *bounder = closure;
+
+    bounder->current_point = *point;
+    _cairo_box_add_point (&bounder->extents, point);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_path_bounder_curve_to (void *closure,
+                             const cairo_point_t *b,
+                             const cairo_point_t *c,
+                             const cairo_point_t *d)
+{
+    cairo_path_bounder_t *bounder = closure;
+
+    _cairo_box_add_curve_to (&bounder->extents,
+                            &bounder->current_point,
+                            b, c, d);
+    bounder->current_point = *d;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_path_bounder_close_path (void *closure)
+{
+    return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_bool_t
+_cairo_path_bounder_extents (const cairo_path_fixed_t *path,
+                            cairo_box_t *extents)
+{
+    cairo_path_bounder_t bounder;
+    cairo_status_t status;
+
+    bounder.has_extents = FALSE;
+    status = _cairo_path_fixed_interpret (path,
+                                         _cairo_path_bounder_move_to,
+                                         _cairo_path_bounder_line_to,
+                                         _cairo_path_bounder_curve_to,
+                                         _cairo_path_bounder_close_path,
+                                         &bounder);
+    assert (!status);
+
+    if (bounder.has_extents)
+       *extents = bounder.extents;
+
+    return bounder.has_extents;
+}
+
+void
+_cairo_path_fixed_approximate_clip_extents (const cairo_path_fixed_t *path,
+                                           cairo_rectangle_int_t *extents)
+{
+    _cairo_path_fixed_approximate_fill_extents (path, extents);
+}
+
+void
+_cairo_path_fixed_approximate_fill_extents (const cairo_path_fixed_t *path,
+                                           cairo_rectangle_int_t *extents)
+{
+    _cairo_path_fixed_fill_extents (path, CAIRO_FILL_RULE_WINDING, 0, extents);
+}
+
+void
+_cairo_path_fixed_fill_extents (const cairo_path_fixed_t       *path,
+                               cairo_fill_rule_t        fill_rule,
+                               double                   tolerance,
+                               cairo_rectangle_int_t   *extents)
+{
+    if (path->extents.p1.x < path->extents.p2.x &&
+       path->extents.p1.y < path->extents.p2.y) {
+       _cairo_box_round_to_rectangle (&path->extents, extents);
+    } else {
+       extents->x = extents->y = 0;
+       extents->width = extents->height = 0;
+    }
+}
+
+/* Adjusts the fill extents (above) by the device-space pen.  */
+void
+_cairo_path_fixed_approximate_stroke_extents (const cairo_path_fixed_t *path,
+                                             const cairo_stroke_style_t *style,
+                                             const cairo_matrix_t *ctm,
+                                             cairo_rectangle_int_t *extents)
+{
+    if (path->has_extents) {
+       cairo_box_t box_extents;
+       double dx, dy;
+
+       _cairo_stroke_style_max_distance_from_path (style, path, ctm, &dx, &dy);
+
+       box_extents = path->extents;
+       box_extents.p1.x -= _cairo_fixed_from_double (dx);
+       box_extents.p1.y -= _cairo_fixed_from_double (dy);
+       box_extents.p2.x += _cairo_fixed_from_double (dx);
+       box_extents.p2.y += _cairo_fixed_from_double (dy);
+
+       _cairo_box_round_to_rectangle (&box_extents, extents);
+    } else {
+       extents->x = extents->y = 0;
+       extents->width = extents->height = 0;
+    }
+}
+
+cairo_status_t
+_cairo_path_fixed_stroke_extents (const cairo_path_fixed_t     *path,
+                                 const cairo_stroke_style_t    *stroke_style,
+                                 const cairo_matrix_t          *ctm,
+                                 const cairo_matrix_t          *ctm_inverse,
+                                 double                         tolerance,
+                                 cairo_rectangle_int_t         *extents)
+{
+    cairo_polygon_t polygon;
+    cairo_status_t status;
+
+    _cairo_polygon_init (&polygon, NULL, 0);
+    status = _cairo_path_fixed_stroke_to_polygon (path,
+                                                 stroke_style,
+                                                 ctm, ctm_inverse,
+                                                 tolerance,
+                                                 &polygon);
+    _cairo_box_round_to_rectangle (&polygon.extents, extents);
+    _cairo_polygon_fini (&polygon);
+
+    return status;
+}
+
+cairo_bool_t
+_cairo_path_fixed_extents (const cairo_path_fixed_t *path,
+                          cairo_box_t *box)
+{
+    *box = path->extents;
+    return path->has_extents;
+}
diff --git a/src/cairo-path-fill.c b/src/cairo-path-fill.c
new file mode 100644 (file)
index 0000000..4000c9c
--- /dev/null
@@ -0,0 +1,341 @@
+/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *     Carl D. Worth <cworth@cworth.org>
+ */
+
+#include "cairoint.h"
+#include "cairo-boxes-private.h"
+#include "cairo-error-private.h"
+#include "cairo-path-fixed-private.h"
+#include "cairo-region-private.h"
+#include "cairo-traps-private.h"
+
+typedef struct cairo_filler {
+    cairo_polygon_t *polygon;
+    double tolerance;
+
+    cairo_box_t limit;
+    cairo_bool_t has_limits;
+
+    cairo_point_t current_point;
+    cairo_point_t last_move_to;
+} cairo_filler_t;
+
+static cairo_status_t
+_cairo_filler_line_to (void *closure,
+                      const cairo_point_t *point)
+{
+    cairo_filler_t *filler = closure;
+    cairo_status_t status;
+
+    status = _cairo_polygon_add_external_edge (filler->polygon,
+                                              &filler->current_point,
+                                              point);
+
+    filler->current_point = *point;
+
+    return status;
+}
+
+static cairo_status_t
+_cairo_filler_close (void *closure)
+{
+    cairo_filler_t *filler = closure;
+
+    /* close the subpath */
+    return _cairo_filler_line_to (closure, &filler->last_move_to);
+}
+
+static cairo_status_t
+_cairo_filler_move_to (void *closure,
+                      const cairo_point_t *point)
+{
+    cairo_filler_t *filler = closure;
+    cairo_status_t status;
+
+    /* close current subpath */
+    status = _cairo_filler_close (closure);
+    if (unlikely (status))
+       return status;
+
+        /* make sure that the closure represents a degenerate path */
+    filler->current_point = *point;
+    filler->last_move_to = *point;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_filler_curve_to (void           *closure,
+                       const cairo_point_t     *p1,
+                       const cairo_point_t     *p2,
+                       const cairo_point_t     *p3)
+{
+    cairo_filler_t *filler = closure;
+    cairo_spline_t spline;
+
+    if (filler->has_limits) {
+       if (! _cairo_spline_intersects (&filler->current_point, p1, p2, p3,
+                                       &filler->limit))
+           return _cairo_filler_line_to (filler, p3);
+    }
+
+    if (! _cairo_spline_init (&spline,
+                             (cairo_spline_add_point_func_t)_cairo_filler_line_to, filler,
+                             &filler->current_point, p1, p2, p3))
+    {
+       return _cairo_filler_line_to (closure, p3);
+    }
+
+    return _cairo_spline_decompose (&spline, filler->tolerance);
+}
+
+cairo_status_t
+_cairo_path_fixed_fill_to_polygon (const cairo_path_fixed_t *path,
+                                  double tolerance,
+                                  cairo_polygon_t *polygon)
+{
+    cairo_filler_t filler;
+    cairo_status_t status;
+
+    filler.polygon = polygon;
+    filler.tolerance = tolerance;
+
+    filler.has_limits = FALSE;
+    if (polygon->num_limits) {
+       filler.has_limits = TRUE;
+       filler.limit = polygon->limit;
+    }
+
+    /* make sure that the closure represents a degenerate path */
+    filler.current_point.x = 0;
+    filler.current_point.y = 0;
+    filler.last_move_to = filler.current_point;
+
+    status = _cairo_path_fixed_interpret (path,
+                                         _cairo_filler_move_to,
+                                         _cairo_filler_line_to,
+                                         _cairo_filler_curve_to,
+                                         _cairo_filler_close,
+                                         &filler);
+    if (unlikely (status))
+       return status;
+
+    return _cairo_filler_close (&filler);
+}
+
+typedef struct cairo_filler_rectilinear_aligned {
+    cairo_polygon_t *polygon;
+
+    cairo_point_t current_point;
+    cairo_point_t last_move_to;
+} cairo_filler_ra_t;
+
+static cairo_status_t
+_cairo_filler_ra_line_to (void *closure,
+                         const cairo_point_t *point)
+{
+    cairo_filler_ra_t *filler = closure;
+    cairo_status_t status;
+    cairo_point_t p;
+
+    p.x = _cairo_fixed_round_down (point->x);
+    p.y = _cairo_fixed_round_down (point->y);
+
+    status = _cairo_polygon_add_external_edge (filler->polygon,
+                                              &filler->current_point,
+                                              &p);
+
+    filler->current_point = p;
+
+    return status;
+}
+
+static cairo_status_t
+_cairo_filler_ra_close (void *closure)
+{
+    cairo_filler_ra_t *filler = closure;
+    return _cairo_filler_ra_line_to (closure, &filler->last_move_to);
+}
+
+static cairo_status_t
+_cairo_filler_ra_move_to (void *closure,
+                         const cairo_point_t *point)
+{
+    cairo_filler_ra_t *filler = closure;
+    cairo_status_t status;
+    cairo_point_t p;
+
+    /* close current subpath */
+    status = _cairo_filler_ra_close (closure);
+    if (unlikely (status))
+       return status;
+
+    p.x = _cairo_fixed_round_down (point->x);
+    p.y = _cairo_fixed_round_down (point->y);
+
+    /* make sure that the closure represents a degenerate path */
+    filler->current_point = p;
+    filler->last_move_to = p;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_status_t
+_cairo_path_fixed_fill_rectilinear_to_polygon (const cairo_path_fixed_t *path,
+                                              cairo_antialias_t antialias,
+                                              cairo_polygon_t *polygon)
+{
+    cairo_filler_ra_t filler;
+    cairo_status_t status;
+
+    if (antialias != CAIRO_ANTIALIAS_NONE)
+       return _cairo_path_fixed_fill_to_polygon (path, 0., polygon);
+
+    filler.polygon = polygon;
+
+    /* make sure that the closure represents a degenerate path */
+    filler.current_point.x = 0;
+    filler.current_point.y = 0;
+    filler.last_move_to = filler.current_point;
+
+    status = _cairo_path_fixed_interpret_flat (path,
+                                              _cairo_filler_ra_move_to,
+                                              _cairo_filler_ra_line_to,
+                                              _cairo_filler_ra_close,
+                                              &filler,
+                                              0.);
+    if (unlikely (status))
+       return status;
+
+    return _cairo_filler_ra_close (&filler);
+}
+
+cairo_status_t
+_cairo_path_fixed_fill_to_traps (const cairo_path_fixed_t *path,
+                                cairo_fill_rule_t fill_rule,
+                                double tolerance,
+                                cairo_traps_t *traps)
+{
+    cairo_polygon_t polygon;
+    cairo_status_t status;
+
+    if (_cairo_path_fixed_fill_is_empty (path))
+       return CAIRO_STATUS_SUCCESS;
+
+    _cairo_polygon_init (&polygon, traps->limits, traps->num_limits);
+    status = _cairo_path_fixed_fill_to_polygon (path, tolerance, &polygon);
+    if (unlikely (status || polygon.num_edges == 0))
+       goto CLEANUP;
+
+    status = _cairo_bentley_ottmann_tessellate_polygon (traps,
+                                                       &polygon, fill_rule);
+
+  CLEANUP:
+    _cairo_polygon_fini (&polygon);
+    return status;
+}
+
+static cairo_status_t
+_cairo_path_fixed_fill_rectilinear_tessellate_to_boxes (const cairo_path_fixed_t *path,
+                                                       cairo_fill_rule_t fill_rule,
+                                                       cairo_antialias_t antialias,
+                                                       cairo_boxes_t *boxes)
+{
+    cairo_polygon_t polygon;
+    cairo_status_t status;
+
+    _cairo_polygon_init (&polygon, boxes->limits, boxes->num_limits);
+    boxes->num_limits = 0;
+
+    /* tolerance will be ignored as the path is rectilinear */
+    status = _cairo_path_fixed_fill_rectilinear_to_polygon (path, antialias, &polygon);
+    if (likely (status == CAIRO_STATUS_SUCCESS)) {
+       status =
+           _cairo_bentley_ottmann_tessellate_rectilinear_polygon_to_boxes (&polygon,
+                                                                           fill_rule,
+                                                                           boxes);
+    }
+
+    _cairo_polygon_fini (&polygon);
+
+    return status;
+}
+
+cairo_status_t
+_cairo_path_fixed_fill_rectilinear_to_boxes (const cairo_path_fixed_t *path,
+                                            cairo_fill_rule_t fill_rule,
+                                            cairo_antialias_t antialias,
+                                            cairo_boxes_t *boxes)
+{
+    cairo_path_fixed_iter_t iter;
+    cairo_status_t status;
+    cairo_box_t box;
+
+    if (_cairo_path_fixed_is_box (path, &box))
+       return _cairo_boxes_add (boxes, antialias, &box);
+
+    _cairo_path_fixed_iter_init (&iter, path);
+    while (_cairo_path_fixed_iter_is_fill_box (&iter, &box)) {
+       if (box.p1.y == box.p2.y || box.p1.x == box.p2.x)
+           continue;
+
+       if (box.p1.y > box.p2.y) {
+           cairo_fixed_t t;
+
+           t = box.p1.y;
+           box.p1.y = box.p2.y;
+           box.p2.y = t;
+
+           t = box.p1.x;
+           box.p1.x = box.p2.x;
+           box.p2.x = t;
+       }
+
+       status = _cairo_boxes_add (boxes, antialias, &box);
+       if (unlikely (status))
+           return status;
+    }
+
+    if (_cairo_path_fixed_iter_at_end (&iter))
+       return _cairo_bentley_ottmann_tessellate_boxes (boxes, fill_rule, boxes);
+
+    /* path is not rectangular, try extracting clipped rectilinear edges */
+    _cairo_boxes_clear (boxes);
+    return _cairo_path_fixed_fill_rectilinear_tessellate_to_boxes (path,
+                                                                  fill_rule,
+                                                                  antialias,
+                                                                  boxes);
+}
diff --git a/src/cairo-path-fixed-private.h b/src/cairo-path-fixed-private.h
new file mode 100644 (file)
index 0000000..9b7b403
--- /dev/null
@@ -0,0 +1,189 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ *     Carl D. Worth <cworth@redhat.com>
+ */
+
+#ifndef CAIRO_PATH_FIXED_PRIVATE_H
+#define CAIRO_PATH_FIXED_PRIVATE_H
+
+#include "cairo-types-private.h"
+#include "cairo-compiler-private.h"
+#include "cairo-list-private.h"
+
+#define WATCH_PATH 0
+#if WATCH_PATH
+#include <stdio.h>
+#endif
+
+enum cairo_path_op {
+    CAIRO_PATH_OP_MOVE_TO = 0,
+    CAIRO_PATH_OP_LINE_TO = 1,
+    CAIRO_PATH_OP_CURVE_TO = 2,
+    CAIRO_PATH_OP_CLOSE_PATH = 3
+};
+
+/* we want to make sure a single byte is used for the enum */
+typedef char cairo_path_op_t;
+
+/* make _cairo_path_fixed fit into ~512 bytes -- about 50 items */
+#define CAIRO_PATH_BUF_SIZE ((512 - sizeof (cairo_path_buf_t)) \
+                          / (2 * sizeof (cairo_point_t) + sizeof (cairo_path_op_t)))
+
+typedef struct _cairo_path_buf {
+    cairo_list_t link;
+    unsigned int num_ops;
+    unsigned int size_ops;
+    unsigned int num_points;
+    unsigned int size_points;
+
+    cairo_path_op_t *op;
+    cairo_point_t *points;
+} cairo_path_buf_t;
+
+typedef struct _cairo_path_buf_fixed {
+    cairo_path_buf_t base;
+
+    cairo_path_op_t op[CAIRO_PATH_BUF_SIZE];
+    cairo_point_t points[2 * CAIRO_PATH_BUF_SIZE];
+} cairo_path_buf_fixed_t;
+
+/*
+  NOTES:
+  has_curve_to => !stroke_is_rectilinear
+  fill_is_rectilinear => stroke_is_rectilinear
+  fill_is_empty => fill_is_rectilinear
+  fill_maybe_region => fill_is_rectilinear
+*/
+struct _cairo_path_fixed {
+    cairo_point_t last_move_point;
+    cairo_point_t current_point;
+    unsigned int has_current_point     : 1;
+    unsigned int needs_move_to         : 1;
+    unsigned int has_extents           : 1;
+    unsigned int has_curve_to          : 1;
+    unsigned int stroke_is_rectilinear : 1;
+    unsigned int fill_is_rectilinear   : 1;
+    unsigned int fill_maybe_region     : 1;
+    unsigned int fill_is_empty         : 1;
+
+    cairo_box_t extents;
+
+    cairo_path_buf_fixed_t  buf;
+};
+
+cairo_private void
+_cairo_path_fixed_translate (cairo_path_fixed_t *path,
+                            cairo_fixed_t offx,
+                            cairo_fixed_t offy);
+
+cairo_private cairo_status_t
+_cairo_path_fixed_append (cairo_path_fixed_t               *path,
+                         const cairo_path_fixed_t          *other,
+                         cairo_fixed_t                      tx,
+                         cairo_fixed_t                      ty);
+
+cairo_private unsigned long
+_cairo_path_fixed_hash (const cairo_path_fixed_t *path);
+
+cairo_private unsigned long
+_cairo_path_fixed_size (const cairo_path_fixed_t *path);
+
+cairo_private cairo_bool_t
+_cairo_path_fixed_equal (const cairo_path_fixed_t *a,
+                        const cairo_path_fixed_t *b);
+
+typedef struct _cairo_path_fixed_iter {
+    const cairo_path_buf_t *first;
+    const cairo_path_buf_t *buf;
+    unsigned int n_op;
+    unsigned int n_point;
+} cairo_path_fixed_iter_t;
+
+cairo_private void
+_cairo_path_fixed_iter_init (cairo_path_fixed_iter_t *iter,
+                            const cairo_path_fixed_t *path);
+
+cairo_private cairo_bool_t
+_cairo_path_fixed_iter_is_fill_box (cairo_path_fixed_iter_t *_iter,
+                                   cairo_box_t *box);
+
+cairo_private cairo_bool_t
+_cairo_path_fixed_iter_at_end (const cairo_path_fixed_iter_t *iter);
+
+static inline cairo_bool_t
+_cairo_path_fixed_fill_is_empty (const cairo_path_fixed_t *path)
+{
+    return path->fill_is_empty;
+}
+
+static inline cairo_bool_t
+_cairo_path_fixed_fill_is_rectilinear (const cairo_path_fixed_t *path)
+{
+    if (! path->fill_is_rectilinear)
+       return 0;
+
+    if (! path->has_current_point || path->needs_move_to)
+       return 1;
+
+    /* check whether the implicit close preserves the rectilinear property */
+    return path->current_point.x == path->last_move_point.x ||
+          path->current_point.y == path->last_move_point.y;
+}
+
+static inline cairo_bool_t
+_cairo_path_fixed_stroke_is_rectilinear (const cairo_path_fixed_t *path)
+{
+    return path->stroke_is_rectilinear;
+}
+
+static inline cairo_bool_t
+_cairo_path_fixed_fill_maybe_region (const cairo_path_fixed_t *path)
+{
+    if (! path->fill_maybe_region)
+       return 0;
+
+    if (! path->has_current_point || path->needs_move_to)
+       return 1;
+
+    /* check whether the implicit close preserves the rectilinear property
+     * (the integer point property is automatically preserved)
+     */
+    return path->current_point.x == path->last_move_point.x ||
+          path->current_point.y == path->last_move_point.y;
+}
+
+cairo_private cairo_bool_t
+_cairo_path_fixed_is_stroke_box (const cairo_path_fixed_t *path,
+                                cairo_box_t *box);
+
+#endif /* CAIRO_PATH_FIXED_PRIVATE_H */
diff --git a/src/cairo-path-fixed.c b/src/cairo-path-fixed.c
new file mode 100644 (file)
index 0000000..1e9a759
--- /dev/null
@@ -0,0 +1,1490 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ * Copyright © 2005 Red Hat, Inc.
+  *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *     Carl D. Worth <cworth@cworth.org>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-box-private.h"
+#include "cairo-error-private.h"
+#include "cairo-path-fixed-private.h"
+#include "cairo-slope-private.h"
+
+static cairo_status_t
+_cairo_path_fixed_add (cairo_path_fixed_t  *path,
+                      cairo_path_op_t      op,
+                      const cairo_point_t *points,
+                      int                  num_points);
+
+static void
+_cairo_path_fixed_add_buf (cairo_path_fixed_t *path,
+                          cairo_path_buf_t   *buf);
+
+static cairo_path_buf_t *
+_cairo_path_buf_create (int size_ops, int size_points);
+
+static void
+_cairo_path_buf_destroy (cairo_path_buf_t *buf);
+
+static void
+_cairo_path_buf_add_op (cairo_path_buf_t *buf,
+                       cairo_path_op_t   op);
+
+static void
+_cairo_path_buf_add_points (cairo_path_buf_t       *buf,
+                           const cairo_point_t    *points,
+                           int                     num_points);
+
+#define cairo_path_head(path__) (&(path__)->buf.base)
+#define cairo_path_tail(path__) cairo_path_buf_prev (cairo_path_head (path__))
+
+#define cairo_path_buf_next(pos__) \
+    cairo_list_entry ((pos__)->link.next, cairo_path_buf_t, link)
+#define cairo_path_buf_prev(pos__) \
+    cairo_list_entry ((pos__)->link.prev, cairo_path_buf_t, link)
+
+#define cairo_path_foreach_buf_start(pos__, path__) \
+    pos__ = cairo_path_head (path__); do
+#define cairo_path_foreach_buf_end(pos__, path__) \
+    while ((pos__ = cairo_path_buf_next (pos__)) !=  cairo_path_head (path__))
+
+void
+_cairo_path_fixed_init (cairo_path_fixed_t *path)
+{
+    VG (VALGRIND_MAKE_MEM_UNDEFINED (path, sizeof (cairo_path_fixed_t)));
+
+    cairo_list_init (&path->buf.base.link);
+
+    path->buf.base.num_ops = 0;
+    path->buf.base.num_points = 0;
+    path->buf.base.size_ops = ARRAY_LENGTH (path->buf.op);
+    path->buf.base.size_points = ARRAY_LENGTH (path->buf.points);
+    path->buf.base.op = path->buf.op;
+    path->buf.base.points = path->buf.points;
+
+    path->current_point.x = 0;
+    path->current_point.y = 0;
+    path->last_move_point = path->current_point;
+
+    path->has_current_point = FALSE;
+    path->needs_move_to = TRUE;
+    path->has_extents = FALSE;
+    path->has_curve_to = FALSE;
+    path->stroke_is_rectilinear = TRUE;
+    path->fill_is_rectilinear = TRUE;
+    path->fill_maybe_region = TRUE;
+    path->fill_is_empty = TRUE;
+
+    path->extents.p1.x = path->extents.p1.y = 0;
+    path->extents.p2.x = path->extents.p2.y = 0;
+}
+
+cairo_status_t
+_cairo_path_fixed_init_copy (cairo_path_fixed_t *path,
+                            const cairo_path_fixed_t *other)
+{
+    cairo_path_buf_t *buf, *other_buf;
+    unsigned int num_points, num_ops;
+
+    VG (VALGRIND_MAKE_MEM_UNDEFINED (path, sizeof (cairo_path_fixed_t)));
+
+    cairo_list_init (&path->buf.base.link);
+
+    path->buf.base.op = path->buf.op;
+    path->buf.base.points = path->buf.points;
+    path->buf.base.size_ops = ARRAY_LENGTH (path->buf.op);
+    path->buf.base.size_points = ARRAY_LENGTH (path->buf.points);
+
+    path->current_point = other->current_point;
+    path->last_move_point = other->last_move_point;
+
+    path->has_current_point = other->has_current_point;
+    path->needs_move_to = other->needs_move_to;
+    path->has_extents = other->has_extents;
+    path->has_curve_to = other->has_curve_to;
+    path->stroke_is_rectilinear = other->stroke_is_rectilinear;
+    path->fill_is_rectilinear = other->fill_is_rectilinear;
+    path->fill_maybe_region = other->fill_maybe_region;
+    path->fill_is_empty = other->fill_is_empty;
+
+    path->extents = other->extents;
+
+    path->buf.base.num_ops = other->buf.base.num_ops;
+    path->buf.base.num_points = other->buf.base.num_points;
+    memcpy (path->buf.op, other->buf.base.op,
+           other->buf.base.num_ops * sizeof (other->buf.op[0]));
+    memcpy (path->buf.points, other->buf.points,
+           other->buf.base.num_points * sizeof (other->buf.points[0]));
+
+    num_points = num_ops = 0;
+    for (other_buf = cairo_path_buf_next (cairo_path_head (other));
+        other_buf != cairo_path_head (other);
+        other_buf = cairo_path_buf_next (other_buf))
+    {
+       num_ops    += other_buf->num_ops;
+       num_points += other_buf->num_points;
+    }
+
+    if (num_ops) {
+       buf = _cairo_path_buf_create (num_ops, num_points);
+       if (unlikely (buf == NULL)) {
+           _cairo_path_fixed_fini (path);
+           return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+       }
+
+       for (other_buf = cairo_path_buf_next (cairo_path_head (other));
+            other_buf != cairo_path_head (other);
+            other_buf = cairo_path_buf_next (other_buf))
+       {
+           memcpy (buf->op + buf->num_ops, other_buf->op,
+                   other_buf->num_ops * sizeof (buf->op[0]));
+           buf->num_ops += other_buf->num_ops;
+
+           memcpy (buf->points + buf->num_points, other_buf->points,
+                   other_buf->num_points * sizeof (buf->points[0]));
+           buf->num_points += other_buf->num_points;
+       }
+
+       _cairo_path_fixed_add_buf (path, buf);
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+unsigned long
+_cairo_path_fixed_hash (const cairo_path_fixed_t *path)
+{
+    unsigned long hash = _CAIRO_HASH_INIT_VALUE;
+    const cairo_path_buf_t *buf;
+    unsigned int count;
+
+    count = 0;
+    cairo_path_foreach_buf_start (buf, path) {
+       hash = _cairo_hash_bytes (hash, buf->op,
+                                 buf->num_ops * sizeof (buf->op[0]));
+       count += buf->num_ops;
+    } cairo_path_foreach_buf_end (buf, path);
+    hash = _cairo_hash_bytes (hash, &count, sizeof (count));
+
+    count = 0;
+    cairo_path_foreach_buf_start (buf, path) {
+       hash = _cairo_hash_bytes (hash, buf->points,
+                                 buf->num_points * sizeof (buf->points[0]));
+       count += buf->num_points;
+    } cairo_path_foreach_buf_end (buf, path);
+    hash = _cairo_hash_bytes (hash, &count, sizeof (count));
+
+    return hash;
+}
+
+unsigned long
+_cairo_path_fixed_size (const cairo_path_fixed_t *path)
+{
+    const cairo_path_buf_t *buf;
+    int num_points, num_ops;
+
+    num_ops = num_points = 0;
+    cairo_path_foreach_buf_start (buf, path) {
+       num_ops    += buf->num_ops;
+       num_points += buf->num_points;
+    } cairo_path_foreach_buf_end (buf, path);
+
+    return num_ops * sizeof (buf->op[0]) +
+          num_points * sizeof (buf->points[0]);
+}
+
+cairo_bool_t
+_cairo_path_fixed_equal (const cairo_path_fixed_t *a,
+                        const cairo_path_fixed_t *b)
+{
+    const cairo_path_buf_t *buf_a, *buf_b;
+    const cairo_path_op_t *ops_a, *ops_b;
+    const cairo_point_t *points_a, *points_b;
+    int num_points_a, num_ops_a;
+    int num_points_b, num_ops_b;
+
+    if (a == b)
+       return TRUE;
+
+    /* use the flags to quickly differentiate based on contents */
+    if (a->has_curve_to != b->has_curve_to)
+    {
+       return FALSE;
+    }
+
+    if (a->extents.p1.x != b->extents.p1.x ||
+       a->extents.p1.y != b->extents.p1.y ||
+       a->extents.p2.x != b->extents.p2.x ||
+       a->extents.p2.y != b->extents.p2.y)
+    {
+       return FALSE;
+    }
+
+    num_ops_a = num_points_a = 0;
+    cairo_path_foreach_buf_start (buf_a, a) {
+       num_ops_a    += buf_a->num_ops;
+       num_points_a += buf_a->num_points;
+    } cairo_path_foreach_buf_end (buf_a, a);
+
+    num_ops_b = num_points_b = 0;
+    cairo_path_foreach_buf_start (buf_b, b) {
+       num_ops_b    += buf_b->num_ops;
+       num_points_b += buf_b->num_points;
+    } cairo_path_foreach_buf_end (buf_b, b);
+
+    if (num_ops_a == 0 && num_ops_b == 0)
+       return TRUE;
+
+    if (num_ops_a != num_ops_b || num_points_a != num_points_b)
+       return FALSE;
+
+    buf_a = cairo_path_head (a);
+    num_points_a = buf_a->num_points;
+    num_ops_a = buf_a->num_ops;
+    ops_a = buf_a->op;
+    points_a = buf_a->points;
+
+    buf_b = cairo_path_head (b);
+    num_points_b = buf_b->num_points;
+    num_ops_b = buf_b->num_ops;
+    ops_b = buf_b->op;
+    points_b = buf_b->points;
+
+    while (TRUE) {
+       int num_ops = MIN (num_ops_a, num_ops_b);
+       int num_points = MIN (num_points_a, num_points_b);
+
+       if (memcmp (ops_a, ops_b, num_ops * sizeof (cairo_path_op_t)))
+           return FALSE;
+       if (memcmp (points_a, points_b, num_points * sizeof (cairo_point_t)))
+           return FALSE;
+
+       num_ops_a -= num_ops;
+       ops_a += num_ops;
+       num_points_a -= num_points;
+       points_a += num_points;
+       if (num_ops_a == 0 || num_points_a == 0) {
+           if (num_ops_a || num_points_a)
+               return FALSE;
+
+           buf_a = cairo_path_buf_next (buf_a);
+           if (buf_a == cairo_path_head (a))
+               break;
+
+           num_points_a = buf_a->num_points;
+           num_ops_a = buf_a->num_ops;
+           ops_a = buf_a->op;
+           points_a = buf_a->points;
+       }
+
+       num_ops_b -= num_ops;
+       ops_b += num_ops;
+       num_points_b -= num_points;
+       points_b += num_points;
+       if (num_ops_b == 0 || num_points_b == 0) {
+           if (num_ops_b || num_points_b)
+               return FALSE;
+
+           buf_b = cairo_path_buf_next (buf_b);
+           if (buf_b == cairo_path_head (b))
+               break;
+
+           num_points_b = buf_b->num_points;
+           num_ops_b = buf_b->num_ops;
+           ops_b = buf_b->op;
+           points_b = buf_b->points;
+       }
+    }
+
+    return TRUE;
+}
+
+cairo_path_fixed_t *
+_cairo_path_fixed_create (void)
+{
+    cairo_path_fixed_t *path;
+
+    path = malloc (sizeof (cairo_path_fixed_t));
+    if (!path) {
+       _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+       return NULL;
+    }
+
+    _cairo_path_fixed_init (path);
+    return path;
+}
+
+void
+_cairo_path_fixed_fini (cairo_path_fixed_t *path)
+{
+    cairo_path_buf_t *buf;
+
+    buf = cairo_path_buf_next (cairo_path_head (path));
+    while (buf != cairo_path_head (path)) {
+       cairo_path_buf_t *this = buf;
+       buf = cairo_path_buf_next (buf);
+       _cairo_path_buf_destroy (this);
+    }
+
+    VG (VALGRIND_MAKE_MEM_NOACCESS (path, sizeof (cairo_path_fixed_t)));
+}
+
+void
+_cairo_path_fixed_destroy (cairo_path_fixed_t *path)
+{
+    _cairo_path_fixed_fini (path);
+    free (path);
+}
+
+static inline cairo_path_op_t
+_cairo_path_fixed_last_op (cairo_path_fixed_t *path)
+{
+    cairo_path_buf_t *buf;
+
+    buf = cairo_path_tail (path);
+    assert (buf->num_ops != 0);
+
+    return buf->op[buf->num_ops - 1];
+}
+
+static inline const cairo_point_t *
+_cairo_path_fixed_penultimate_point (cairo_path_fixed_t *path)
+{
+    cairo_path_buf_t *buf;
+
+    buf = cairo_path_tail (path);
+    if (likely (buf->num_points >= 2)) {
+       return &buf->points[buf->num_points - 2];
+    } else {
+       cairo_path_buf_t *prev_buf = cairo_path_buf_prev (buf);
+
+       assert (prev_buf->num_points >= 2 - buf->num_points);
+       return &prev_buf->points[prev_buf->num_points - (2 - buf->num_points)];
+    }
+}
+
+static void
+_cairo_path_fixed_drop_line_to (cairo_path_fixed_t *path)
+{
+    cairo_path_buf_t *buf;
+
+    assert (_cairo_path_fixed_last_op (path) == CAIRO_PATH_OP_LINE_TO);
+
+    buf = cairo_path_tail (path);
+    buf->num_points--;
+    buf->num_ops--;
+}
+
+cairo_status_t
+_cairo_path_fixed_move_to (cairo_path_fixed_t  *path,
+                          cairo_fixed_t        x,
+                          cairo_fixed_t        y)
+{
+    _cairo_path_fixed_new_sub_path (path);
+
+    path->has_current_point = TRUE;
+    path->current_point.x = x;
+    path->current_point.y = y;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_path_fixed_move_to_apply (cairo_path_fixed_t  *path)
+{
+    if (likely (! path->needs_move_to))
+       return CAIRO_STATUS_SUCCESS;
+
+    path->needs_move_to = FALSE;
+
+    if (path->has_extents) {
+       _cairo_box_add_point (&path->extents, &path->current_point);
+    } else {
+       _cairo_box_set (&path->extents, &path->current_point, &path->current_point);
+       path->has_extents = TRUE;
+    }
+
+    if (path->fill_maybe_region) {
+       path->fill_maybe_region = _cairo_fixed_is_integer (path->current_point.x) &&
+                                 _cairo_fixed_is_integer (path->current_point.y);
+    }
+
+    path->last_move_point = path->current_point;
+
+    return _cairo_path_fixed_add (path, CAIRO_PATH_OP_MOVE_TO, &path->current_point, 1);
+}
+
+void
+_cairo_path_fixed_new_sub_path (cairo_path_fixed_t *path)
+{
+    if (! path->needs_move_to) {
+       /* If the current subpath doesn't need_move_to, it contains at least one command */
+       if (path->fill_is_rectilinear) {
+           /* Implicitly close for fill */
+           path->fill_is_rectilinear = path->current_point.x == path->last_move_point.x ||
+                                       path->current_point.y == path->last_move_point.y;
+           path->fill_maybe_region &= path->fill_is_rectilinear;
+       }
+       path->needs_move_to = TRUE;
+    }
+
+    path->has_current_point = FALSE;
+}
+
+cairo_status_t
+_cairo_path_fixed_rel_move_to (cairo_path_fixed_t *path,
+                              cairo_fixed_t       dx,
+                              cairo_fixed_t       dy)
+{
+    if (unlikely (! path->has_current_point))
+       return _cairo_error (CAIRO_STATUS_NO_CURRENT_POINT);
+
+    return _cairo_path_fixed_move_to (path,
+                                     path->current_point.x + dx,
+                                     path->current_point.y + dy);
+
+}
+
+cairo_status_t
+_cairo_path_fixed_line_to (cairo_path_fixed_t *path,
+                          cairo_fixed_t        x,
+                          cairo_fixed_t        y)
+{
+    cairo_status_t status;
+    cairo_point_t point;
+
+    point.x = x;
+    point.y = y;
+
+    /* When there is not yet a current point, the line_to operation
+     * becomes a move_to instead. Note: We have to do this by
+     * explicitly calling into _cairo_path_fixed_move_to to ensure
+     * that the last_move_point state is updated properly.
+     */
+    if (! path->has_current_point)
+       return _cairo_path_fixed_move_to (path, point.x, point.y);
+
+    status = _cairo_path_fixed_move_to_apply (path);
+    if (unlikely (status))
+       return status;
+
+    /* If the previous op was but the initial MOVE_TO and this segment
+     * is degenerate, then we can simply skip this point. Note that
+     * a move-to followed by a degenerate line-to is a valid path for
+     * stroking, but at all other times is simply a degenerate segment.
+     */
+    if (_cairo_path_fixed_last_op (path) != CAIRO_PATH_OP_MOVE_TO) {
+       if (x == path->current_point.x && y == path->current_point.y)
+           return CAIRO_STATUS_SUCCESS;
+    }
+
+    /* If the previous op was also a LINE_TO with the same gradient,
+     * then just change its end-point rather than adding a new op.
+     */
+    if (_cairo_path_fixed_last_op (path) == CAIRO_PATH_OP_LINE_TO) {
+       const cairo_point_t *p;
+
+       p = _cairo_path_fixed_penultimate_point (path);
+       if (p->x == path->current_point.x && p->y == path->current_point.y) {
+           /* previous line element was degenerate, replace */
+           _cairo_path_fixed_drop_line_to (path);
+       } else {
+           cairo_slope_t prev, self;
+
+           _cairo_slope_init (&prev, p, &path->current_point);
+           _cairo_slope_init (&self, &path->current_point, &point);
+           if (_cairo_slope_equal (&prev, &self) &&
+               /* cannot trim anti-parallel segments whilst stroking */
+               ! _cairo_slope_backwards (&prev, &self))
+           {
+               _cairo_path_fixed_drop_line_to (path);
+               /* In this case the flags might be more restrictive than
+                * what we actually need.
+                * When changing the flags definition we should check if
+                * changing the line_to point can affect them.
+               */
+           }
+       }
+    }
+
+    if (path->stroke_is_rectilinear) {
+       path->stroke_is_rectilinear = path->current_point.x == x ||
+                                     path->current_point.y == y;
+       path->fill_is_rectilinear &= path->stroke_is_rectilinear;
+       path->fill_maybe_region &= path->fill_is_rectilinear;
+       if (path->fill_maybe_region) {
+           path->fill_maybe_region = _cairo_fixed_is_integer (x) &&
+                                     _cairo_fixed_is_integer (y);
+       }
+       if (path->fill_is_empty) {
+           path->fill_is_empty = path->current_point.x == x &&
+                                 path->current_point.y == y;
+       }
+    }
+
+    path->current_point = point;
+
+    _cairo_box_add_point (&path->extents, &point);
+
+    return _cairo_path_fixed_add (path, CAIRO_PATH_OP_LINE_TO, &point, 1);
+}
+
+cairo_status_t
+_cairo_path_fixed_rel_line_to (cairo_path_fixed_t *path,
+                              cairo_fixed_t       dx,
+                              cairo_fixed_t       dy)
+{
+    if (unlikely (! path->has_current_point))
+       return _cairo_error (CAIRO_STATUS_NO_CURRENT_POINT);
+
+    return _cairo_path_fixed_line_to (path,
+                                     path->current_point.x + dx,
+                                     path->current_point.y + dy);
+}
+
+cairo_status_t
+_cairo_path_fixed_curve_to (cairo_path_fixed_t *path,
+                           cairo_fixed_t x0, cairo_fixed_t y0,
+                           cairo_fixed_t x1, cairo_fixed_t y1,
+                           cairo_fixed_t x2, cairo_fixed_t y2)
+{
+    cairo_status_t status;
+    cairo_point_t point[3];
+
+    /* If this curves does not move, replace it with a line-to.
+     * This frequently happens with rounded-rectangles and r==0.
+    */
+    if (path->current_point.x == x2 && path->current_point.y == y2) {
+       if (x1 == x2 && x0 == x2 && y1 == y2 && y0 == y2)
+           return _cairo_path_fixed_line_to (path, x2, y2);
+
+       /* We may want to check for the absence of a cusp, in which case
+        * we can also replace the curve-to with a line-to.
+        */
+    }
+
+    /* make sure subpaths are started properly */
+    if (! path->has_current_point) {
+       status = _cairo_path_fixed_move_to (path, x0, y0);
+       assert (status == CAIRO_STATUS_SUCCESS);
+    }
+
+    status = _cairo_path_fixed_move_to_apply (path);
+    if (unlikely (status))
+       return status;
+
+    /* If the previous op was a degenerate LINE_TO, drop it. */
+    if (_cairo_path_fixed_last_op (path) == CAIRO_PATH_OP_LINE_TO) {
+       const cairo_point_t *p;
+
+       p = _cairo_path_fixed_penultimate_point (path);
+       if (p->x == path->current_point.x && p->y == path->current_point.y) {
+           /* previous line element was degenerate, replace */
+           _cairo_path_fixed_drop_line_to (path);
+       }
+    }
+
+    point[0].x = x0; point[0].y = y0;
+    point[1].x = x1; point[1].y = y1;
+    point[2].x = x2; point[2].y = y2;
+
+    _cairo_box_add_curve_to (&path->extents, &path->current_point,
+                            &point[0], &point[1], &point[2]);
+
+    path->current_point = point[2];
+    path->has_curve_to = TRUE;
+    path->stroke_is_rectilinear = FALSE;
+    path->fill_is_rectilinear = FALSE;
+    path->fill_maybe_region = FALSE;
+    path->fill_is_empty = FALSE;
+
+    return _cairo_path_fixed_add (path, CAIRO_PATH_OP_CURVE_TO, point, 3);
+}
+
+cairo_status_t
+_cairo_path_fixed_rel_curve_to (cairo_path_fixed_t *path,
+                               cairo_fixed_t dx0, cairo_fixed_t dy0,
+                               cairo_fixed_t dx1, cairo_fixed_t dy1,
+                               cairo_fixed_t dx2, cairo_fixed_t dy2)
+{
+    if (unlikely (! path->has_current_point))
+       return _cairo_error (CAIRO_STATUS_NO_CURRENT_POINT);
+
+    return _cairo_path_fixed_curve_to (path,
+                                      path->current_point.x + dx0,
+                                      path->current_point.y + dy0,
+
+                                      path->current_point.x + dx1,
+                                      path->current_point.y + dy1,
+
+                                      path->current_point.x + dx2,
+                                      path->current_point.y + dy2);
+}
+
+cairo_status_t
+_cairo_path_fixed_close_path (cairo_path_fixed_t *path)
+{
+    cairo_status_t status;
+
+    if (! path->has_current_point)
+       return CAIRO_STATUS_SUCCESS;
+
+    /*
+     * Add a line_to, to compute flags and solve any degeneracy.
+     * It will be removed later (if it was actually added).
+     */
+    status = _cairo_path_fixed_line_to (path,
+                                       path->last_move_point.x,
+                                       path->last_move_point.y);
+    if (unlikely (status))
+       return status;
+
+    /*
+     * If the command used to close the path is a line_to, drop it.
+     * We must check that last command is actually a line_to,
+     * because the path could have been closed with a curve_to (and
+     * the previous line_to not added as it would be degenerate).
+     */
+    if (_cairo_path_fixed_last_op (path) == CAIRO_PATH_OP_LINE_TO)
+           _cairo_path_fixed_drop_line_to (path);
+
+    path->needs_move_to = TRUE; /* After close_path, add an implicit move_to */
+
+    return _cairo_path_fixed_add (path, CAIRO_PATH_OP_CLOSE_PATH, NULL, 0);
+}
+
+cairo_bool_t
+_cairo_path_fixed_get_current_point (cairo_path_fixed_t *path,
+                                    cairo_fixed_t      *x,
+                                    cairo_fixed_t      *y)
+{
+    if (! path->has_current_point)
+       return FALSE;
+
+    *x = path->current_point.x;
+    *y = path->current_point.y;
+
+    return TRUE;
+}
+
+static cairo_status_t
+_cairo_path_fixed_add (cairo_path_fixed_t   *path,
+                      cairo_path_op_t       op,
+                      const cairo_point_t  *points,
+                      int                   num_points)
+{
+    cairo_path_buf_t *buf = cairo_path_tail (path);
+
+    if (buf->num_ops + 1 > buf->size_ops ||
+       buf->num_points + num_points > buf->size_points)
+    {
+       buf = _cairo_path_buf_create (buf->num_ops * 2, buf->num_points * 2);
+       if (unlikely (buf == NULL))
+           return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+       _cairo_path_fixed_add_buf (path, buf);
+    }
+
+    if (WATCH_PATH) {
+       const char *op_str[] = {
+           "move-to",
+           "line-to",
+           "curve-to",
+           "close-path",
+       };
+       char buf[1024];
+       int len = 0;
+       int i;
+
+       len += snprintf (buf + len, sizeof (buf), "[");
+       for (i = 0; i < num_points; i++) {
+           if (i != 0)
+               len += snprintf (buf + len, sizeof (buf), " ");
+           len += snprintf (buf + len, sizeof (buf), "(%f, %f)",
+                            _cairo_fixed_to_double (points[i].x),
+                            _cairo_fixed_to_double (points[i].y));
+       }
+       len += snprintf (buf + len, sizeof (buf), "]");
+
+#define STRINGIFYFLAG(x)  (path->x ? #x " " : "")
+       fprintf (stderr,
+                "_cairo_path_fixed_add (%s, %s) [%s%s%s%s%s%s%s%s]\n",
+                op_str[(int) op], buf,
+                STRINGIFYFLAG(has_current_point),
+                STRINGIFYFLAG(needs_move_to),
+                STRINGIFYFLAG(has_extents),
+                STRINGIFYFLAG(has_curve_to),
+                STRINGIFYFLAG(stroke_is_rectilinear),
+                STRINGIFYFLAG(fill_is_rectilinear),
+                STRINGIFYFLAG(fill_is_empty),
+                STRINGIFYFLAG(fill_maybe_region)
+                );
+#undef STRINGIFYFLAG
+    }
+
+    _cairo_path_buf_add_op (buf, op);
+    _cairo_path_buf_add_points (buf, points, num_points);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_path_fixed_add_buf (cairo_path_fixed_t *path,
+                          cairo_path_buf_t   *buf)
+{
+    cairo_list_add_tail (&buf->link, &cairo_path_head (path)->link);
+}
+
+COMPILE_TIME_ASSERT (sizeof (cairo_path_op_t) == 1);
+static cairo_path_buf_t *
+_cairo_path_buf_create (int size_ops, int size_points)
+{
+    cairo_path_buf_t *buf;
+
+    /* adjust size_ops to ensure that buf->points is naturally aligned */
+    size_ops += sizeof (double) - ((sizeof (cairo_path_buf_t) + size_ops) % sizeof (double));
+    buf = _cairo_malloc_ab_plus_c (size_points, sizeof (cairo_point_t), size_ops + sizeof (cairo_path_buf_t));
+    if (buf) {
+       buf->num_ops = 0;
+       buf->num_points = 0;
+       buf->size_ops = size_ops;
+       buf->size_points = size_points;
+
+       buf->op = (cairo_path_op_t *) (buf + 1);
+       buf->points = (cairo_point_t *) (buf->op + size_ops);
+    }
+
+    return buf;
+}
+
+static void
+_cairo_path_buf_destroy (cairo_path_buf_t *buf)
+{
+    free (buf);
+}
+
+static void
+_cairo_path_buf_add_op (cairo_path_buf_t *buf,
+                       cairo_path_op_t   op)
+{
+    buf->op[buf->num_ops++] = op;
+}
+
+static void
+_cairo_path_buf_add_points (cairo_path_buf_t       *buf,
+                           const cairo_point_t    *points,
+                           int                     num_points)
+{
+    if (num_points == 0)
+       return;
+
+    memcpy (buf->points + buf->num_points,
+           points,
+           sizeof (points[0]) * num_points);
+    buf->num_points += num_points;
+}
+
+cairo_status_t
+_cairo_path_fixed_interpret (const cairo_path_fixed_t          *path,
+                            cairo_path_fixed_move_to_func_t    *move_to,
+                            cairo_path_fixed_line_to_func_t    *line_to,
+                            cairo_path_fixed_curve_to_func_t   *curve_to,
+                            cairo_path_fixed_close_path_func_t *close_path,
+                            void                               *closure)
+{
+    const cairo_path_buf_t *buf;
+    cairo_status_t status;
+
+    cairo_path_foreach_buf_start (buf, path) {
+       const cairo_point_t *points = buf->points;
+       unsigned int i;
+
+       for (i = 0; i < buf->num_ops; i++) {
+           switch (buf->op[i]) {
+           case CAIRO_PATH_OP_MOVE_TO:
+               status = (*move_to) (closure, &points[0]);
+               points += 1;
+               break;
+           case CAIRO_PATH_OP_LINE_TO:
+               status = (*line_to) (closure, &points[0]);
+               points += 1;
+               break;
+           case CAIRO_PATH_OP_CURVE_TO:
+               status = (*curve_to) (closure, &points[0], &points[1], &points[2]);
+               points += 3;
+               break;
+           default:
+               ASSERT_NOT_REACHED;
+           case CAIRO_PATH_OP_CLOSE_PATH:
+               status = (*close_path) (closure);
+               break;
+           }
+
+           if (unlikely (status))
+               return status;
+       }
+    } cairo_path_foreach_buf_end (buf, path);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+typedef struct _cairo_path_fixed_append_closure {
+    cairo_point_t          offset;
+    cairo_path_fixed_t     *path;
+} cairo_path_fixed_append_closure_t;
+
+static cairo_status_t
+_append_move_to (void           *abstract_closure,
+                const cairo_point_t  *point)
+{
+    cairo_path_fixed_append_closure_t  *closure = abstract_closure;
+
+    return _cairo_path_fixed_move_to (closure->path,
+                                     point->x + closure->offset.x,
+                                     point->y + closure->offset.y);
+}
+
+static cairo_status_t
+_append_line_to (void           *abstract_closure,
+                const cairo_point_t *point)
+{
+    cairo_path_fixed_append_closure_t  *closure = abstract_closure;
+
+    return _cairo_path_fixed_line_to (closure->path,
+                                     point->x + closure->offset.x,
+                                     point->y + closure->offset.y);
+}
+
+static cairo_status_t
+_append_curve_to (void   *abstract_closure,
+                 const cairo_point_t *p0,
+                 const cairo_point_t *p1,
+                 const cairo_point_t *p2)
+{
+    cairo_path_fixed_append_closure_t  *closure = abstract_closure;
+
+    return _cairo_path_fixed_curve_to (closure->path,
+                                      p0->x + closure->offset.x,
+                                      p0->y + closure->offset.y,
+                                      p1->x + closure->offset.x,
+                                      p1->y + closure->offset.y,
+                                      p2->x + closure->offset.x,
+                                      p2->y + closure->offset.y);
+}
+
+static cairo_status_t
+_append_close_path (void *abstract_closure)
+{
+    cairo_path_fixed_append_closure_t  *closure = abstract_closure;
+
+    return _cairo_path_fixed_close_path (closure->path);
+}
+
+cairo_status_t
+_cairo_path_fixed_append (cairo_path_fixed_t               *path,
+                         const cairo_path_fixed_t          *other,
+                         cairo_fixed_t                      tx,
+                         cairo_fixed_t                      ty)
+{
+    cairo_path_fixed_append_closure_t closure;
+
+    closure.path = path;
+    closure.offset.x = tx;
+    closure.offset.y = ty;
+
+    return _cairo_path_fixed_interpret (other,
+                                       _append_move_to,
+                                       _append_line_to,
+                                       _append_curve_to,
+                                       _append_close_path,
+                                       &closure);
+}
+
+static void
+_cairo_path_fixed_offset_and_scale (cairo_path_fixed_t *path,
+                                   cairo_fixed_t offx,
+                                   cairo_fixed_t offy,
+                                   cairo_fixed_t scalex,
+                                   cairo_fixed_t scaley)
+{
+    cairo_path_buf_t *buf;
+    unsigned int i;
+
+    if (scalex == CAIRO_FIXED_ONE && scaley == CAIRO_FIXED_ONE) {
+       _cairo_path_fixed_translate (path, offx, offy);
+       return;
+    }
+
+    path->last_move_point.x = _cairo_fixed_mul (scalex, path->last_move_point.x) + offx;
+    path->last_move_point.y = _cairo_fixed_mul (scaley, path->last_move_point.y) + offy;
+    path->current_point.x   = _cairo_fixed_mul (scalex, path->current_point.x) + offx;
+    path->current_point.y   = _cairo_fixed_mul (scaley, path->current_point.y) + offy;
+
+    path->fill_maybe_region = TRUE;
+
+    cairo_path_foreach_buf_start (buf, path) {
+        for (i = 0; i < buf->num_points; i++) {
+            if (scalex != CAIRO_FIXED_ONE)
+                buf->points[i].x = _cairo_fixed_mul (buf->points[i].x, scalex);
+            buf->points[i].x += offx;
+
+            if (scaley != CAIRO_FIXED_ONE)
+                buf->points[i].y = _cairo_fixed_mul (buf->points[i].y, scaley);
+            buf->points[i].y += offy;
+
+           if (path->fill_maybe_region) {
+               path->fill_maybe_region = _cairo_fixed_is_integer (buf->points[i].x) &&
+                                         _cairo_fixed_is_integer (buf->points[i].y);
+           }
+        }
+    } cairo_path_foreach_buf_end (buf, path);
+
+    path->fill_maybe_region &= path->fill_is_rectilinear;
+
+    path->extents.p1.x = _cairo_fixed_mul (scalex, path->extents.p1.x) + offx;
+    path->extents.p2.x = _cairo_fixed_mul (scalex, path->extents.p2.x) + offx;
+    path->extents.p1.y = _cairo_fixed_mul (scaley, path->extents.p1.y) + offy;
+    path->extents.p2.y = _cairo_fixed_mul (scaley, path->extents.p2.y) + offy;
+}
+
+void
+_cairo_path_fixed_translate (cairo_path_fixed_t *path,
+                            cairo_fixed_t offx,
+                            cairo_fixed_t offy)
+{
+    cairo_path_buf_t *buf;
+    unsigned int i;
+
+    if (offx == 0 && offy == 0)
+       return;
+
+    path->last_move_point.x += offx;
+    path->last_move_point.y += offy;
+    path->current_point.x += offx;
+    path->current_point.y += offy;
+
+    path->fill_maybe_region = TRUE;
+
+    cairo_path_foreach_buf_start (buf, path) {
+       for (i = 0; i < buf->num_points; i++) {
+           buf->points[i].x += offx;
+           buf->points[i].y += offy;
+
+           if (path->fill_maybe_region) {
+               path->fill_maybe_region = _cairo_fixed_is_integer (buf->points[i].x) &&
+                                         _cairo_fixed_is_integer (buf->points[i].y);
+           }
+        }
+    } cairo_path_foreach_buf_end (buf, path);
+
+    path->fill_maybe_region &= path->fill_is_rectilinear;
+
+    path->extents.p1.x += offx;
+    path->extents.p1.y += offy;
+    path->extents.p2.x += offx;
+    path->extents.p2.y += offy;
+}
+
+
+static inline void
+_cairo_path_fixed_transform_point (cairo_point_t *p,
+                                  const cairo_matrix_t *matrix)
+{
+    double dx, dy;
+
+    dx = _cairo_fixed_to_double (p->x);
+    dy = _cairo_fixed_to_double (p->y);
+    cairo_matrix_transform_point (matrix, &dx, &dy);
+    p->x = _cairo_fixed_from_double (dx);
+    p->y = _cairo_fixed_from_double (dy);
+}
+
+/**
+ * _cairo_path_fixed_transform:
+ * @path: a #cairo_path_fixed_t to be transformed
+ * @matrix: a #cairo_matrix_t
+ *
+ * Transform the fixed-point path according to the given matrix.
+ * There is a fast path for the case where @matrix has no rotation
+ * or shear.
+ **/
+void
+_cairo_path_fixed_transform (cairo_path_fixed_t        *path,
+                            const cairo_matrix_t     *matrix)
+{
+    cairo_box_t extents;
+    cairo_point_t point;
+    cairo_path_buf_t *buf;
+    unsigned int i;
+
+    if (matrix->yx == 0.0 && matrix->xy == 0.0) {
+       /* Fast path for the common case of scale+transform */
+       _cairo_path_fixed_offset_and_scale (path,
+                                           _cairo_fixed_from_double (matrix->x0),
+                                           _cairo_fixed_from_double (matrix->y0),
+                                           _cairo_fixed_from_double (matrix->xx),
+                                           _cairo_fixed_from_double (matrix->yy));
+       return;
+    }
+
+    _cairo_path_fixed_transform_point (&path->last_move_point, matrix);
+    _cairo_path_fixed_transform_point (&path->current_point, matrix);
+
+    buf = cairo_path_head (path);
+    if (buf->num_points == 0)
+       return;
+
+    extents = path->extents;
+    point = buf->points[0];
+    _cairo_path_fixed_transform_point (&point, matrix);
+    _cairo_box_set (&path->extents, &point, &point);
+
+    cairo_path_foreach_buf_start (buf, path) {
+       for (i = 0; i < buf->num_points; i++) {
+           _cairo_path_fixed_transform_point (&buf->points[i], matrix);
+           _cairo_box_add_point (&path->extents, &buf->points[i]);
+       }
+    } cairo_path_foreach_buf_end (buf, path);
+
+    if (path->has_curve_to) {
+       cairo_bool_t is_tight;
+
+       _cairo_matrix_transform_bounding_box_fixed (matrix, &extents, &is_tight);
+       if (!is_tight) {
+           cairo_bool_t has_extents;
+
+           has_extents = _cairo_path_bounder_extents (path, &extents);
+           assert (has_extents);
+       }
+       path->extents = extents;
+    }
+
+    /* flags might become more strict than needed */
+    path->stroke_is_rectilinear = FALSE;
+    path->fill_is_rectilinear = FALSE;
+    path->fill_is_empty = FALSE;
+    path->fill_maybe_region = FALSE;
+}
+
+/* Closure for path flattening */
+typedef struct cairo_path_flattener {
+    double tolerance;
+    cairo_point_t current_point;
+    cairo_path_fixed_move_to_func_t    *move_to;
+    cairo_path_fixed_line_to_func_t    *line_to;
+    cairo_path_fixed_close_path_func_t *close_path;
+    void *closure;
+} cpf_t;
+
+static cairo_status_t
+_cpf_move_to (void *closure,
+             const cairo_point_t *point)
+{
+    cpf_t *cpf = closure;
+
+    cpf->current_point = *point;
+
+    return cpf->move_to (cpf->closure, point);
+}
+
+static cairo_status_t
+_cpf_line_to (void *closure,
+             const cairo_point_t *point)
+{
+    cpf_t *cpf = closure;
+
+    cpf->current_point = *point;
+
+    return cpf->line_to (cpf->closure, point);
+}
+
+static cairo_status_t
+_cpf_curve_to (void            *closure,
+              const cairo_point_t      *p1,
+              const cairo_point_t      *p2,
+              const cairo_point_t      *p3)
+{
+    cpf_t *cpf = closure;
+    cairo_spline_t spline;
+
+    cairo_point_t *p0 = &cpf->current_point;
+
+    if (! _cairo_spline_init (&spline,
+                             (cairo_spline_add_point_func_t)cpf->line_to,
+                             cpf->closure,
+                             p0, p1, p2, p3))
+    {
+       return _cpf_line_to (closure, p3);
+    }
+
+    cpf->current_point = *p3;
+
+    return _cairo_spline_decompose (&spline, cpf->tolerance);
+}
+
+static cairo_status_t
+_cpf_close_path (void *closure)
+{
+    cpf_t *cpf = closure;
+
+    return cpf->close_path (cpf->closure);
+}
+
+cairo_status_t
+_cairo_path_fixed_interpret_flat (const cairo_path_fixed_t             *path,
+                                 cairo_path_fixed_move_to_func_t       *move_to,
+                                 cairo_path_fixed_line_to_func_t       *line_to,
+                                 cairo_path_fixed_close_path_func_t    *close_path,
+                                 void                                  *closure,
+                                 double                                tolerance)
+{
+    cpf_t flattener;
+
+    if (! path->has_curve_to) {
+       return _cairo_path_fixed_interpret (path,
+                                           move_to,
+                                           line_to,
+                                           NULL,
+                                           close_path,
+                                           closure);
+    }
+
+    flattener.tolerance = tolerance;
+    flattener.move_to = move_to;
+    flattener.line_to = line_to;
+    flattener.close_path = close_path;
+    flattener.closure = closure;
+    return _cairo_path_fixed_interpret (path,
+                                       _cpf_move_to,
+                                       _cpf_line_to,
+                                       _cpf_curve_to,
+                                       _cpf_close_path,
+                                       &flattener);
+}
+
+static inline void
+_canonical_box (cairo_box_t *box,
+               const cairo_point_t *p1,
+               const cairo_point_t *p2)
+{
+    if (p1->x <= p2->x) {
+       box->p1.x = p1->x;
+       box->p2.x = p2->x;
+    } else {
+       box->p1.x = p2->x;
+       box->p2.x = p1->x;
+    }
+
+    if (p1->y <= p2->y) {
+       box->p1.y = p1->y;
+       box->p2.y = p2->y;
+    } else {
+       box->p1.y = p2->y;
+       box->p2.y = p1->y;
+    }
+}
+
+/*
+ * Check whether the given path contains a single rectangle.
+ */
+cairo_bool_t
+_cairo_path_fixed_is_box (const cairo_path_fixed_t *path,
+                         cairo_box_t *box)
+{
+    const cairo_path_buf_t *buf = cairo_path_head (path);
+
+    if (! path->fill_is_rectilinear)
+       return FALSE;
+
+    /* Do we have the right number of ops? */
+    if (buf->num_ops < 4 || buf->num_ops > 6)
+       return FALSE;
+
+    /* Check whether the ops are those that would be used for a rectangle */
+    if (buf->op[0] != CAIRO_PATH_OP_MOVE_TO ||
+       buf->op[1] != CAIRO_PATH_OP_LINE_TO ||
+       buf->op[2] != CAIRO_PATH_OP_LINE_TO ||
+       buf->op[3] != CAIRO_PATH_OP_LINE_TO)
+    {
+       return FALSE;
+    }
+
+    /* we accept an implicit close for filled paths */
+    if (buf->num_ops > 4) {
+       /* Now, there are choices. The rectangle might end with a LINE_TO
+        * (to the original point), but this isn't required. If it
+        * doesn't, then it must end with a CLOSE_PATH. */
+       if (buf->op[4] == CAIRO_PATH_OP_LINE_TO) {
+           if (buf->points[4].x != buf->points[0].x ||
+               buf->points[4].y != buf->points[0].y)
+               return FALSE;
+       } else if (buf->op[4] != CAIRO_PATH_OP_CLOSE_PATH) {
+           return FALSE;
+       }
+
+       if (buf->num_ops == 6) {
+           /* A trailing CLOSE_PATH or MOVE_TO is ok */
+           if (buf->op[5] != CAIRO_PATH_OP_MOVE_TO &&
+               buf->op[5] != CAIRO_PATH_OP_CLOSE_PATH)
+               return FALSE;
+       }
+    }
+
+    /* Ok, we may have a box, if the points line up */
+    if (buf->points[0].y == buf->points[1].y &&
+       buf->points[1].x == buf->points[2].x &&
+       buf->points[2].y == buf->points[3].y &&
+       buf->points[3].x == buf->points[0].x)
+    {
+       _canonical_box (box, &buf->points[0], &buf->points[2]);
+       return TRUE;
+    }
+
+    if (buf->points[0].x == buf->points[1].x &&
+       buf->points[1].y == buf->points[2].y &&
+       buf->points[2].x == buf->points[3].x &&
+       buf->points[3].y == buf->points[0].y)
+    {
+       _canonical_box (box, &buf->points[0], &buf->points[2]);
+       return TRUE;
+    }
+
+    return FALSE;
+}
+
+cairo_bool_t
+_cairo_path_fixed_is_stroke_box (const cairo_path_fixed_t *path,
+                                cairo_box_t *box)
+{
+    const cairo_path_buf_t *buf = cairo_path_head (path);
+
+    if (! path->fill_is_rectilinear)
+       return FALSE;
+
+    /* Do we have the right number of ops? */
+    if (buf->num_ops != 5)
+       return FALSE;
+
+    /* Check whether the ops are those that would be used for a rectangle */
+    if (buf->op[0] != CAIRO_PATH_OP_MOVE_TO ||
+       buf->op[1] != CAIRO_PATH_OP_LINE_TO ||
+       buf->op[2] != CAIRO_PATH_OP_LINE_TO ||
+       buf->op[3] != CAIRO_PATH_OP_LINE_TO ||
+       buf->op[4] != CAIRO_PATH_OP_CLOSE_PATH)
+    {
+       return FALSE;
+    }
+
+    /* Ok, we may have a box, if the points line up */
+    if (buf->points[0].y == buf->points[1].y &&
+       buf->points[1].x == buf->points[2].x &&
+       buf->points[2].y == buf->points[3].y &&
+       buf->points[3].x == buf->points[0].x)
+    {
+       _canonical_box (box, &buf->points[0], &buf->points[2]);
+       return TRUE;
+    }
+
+    if (buf->points[0].x == buf->points[1].x &&
+       buf->points[1].y == buf->points[2].y &&
+       buf->points[2].x == buf->points[3].x &&
+       buf->points[3].y == buf->points[0].y)
+    {
+       _canonical_box (box, &buf->points[0], &buf->points[2]);
+       return TRUE;
+    }
+
+    return FALSE;
+}
+
+/*
+ * Check whether the given path contains a single rectangle
+ * that is logically equivalent to:
+ * <informalexample><programlisting>
+ *   cairo_move_to (cr, x, y);
+ *   cairo_rel_line_to (cr, width, 0);
+ *   cairo_rel_line_to (cr, 0, height);
+ *   cairo_rel_line_to (cr, -width, 0);
+ *   cairo_close_path (cr);
+ * </programlisting></informalexample>
+ */
+cairo_bool_t
+_cairo_path_fixed_is_rectangle (const cairo_path_fixed_t *path,
+                               cairo_box_t        *box)
+{
+    const cairo_path_buf_t *buf;
+
+    if (! _cairo_path_fixed_is_box (path, box))
+       return FALSE;
+
+    /* This check is valid because the current implementation of
+     * _cairo_path_fixed_is_box () only accepts rectangles like:
+     * move,line,line,line[,line|close[,close|move]]. */
+    buf = cairo_path_head (path);
+    if (buf->num_ops > 4)
+       return TRUE;
+
+    return FALSE;
+}
+
+void
+_cairo_path_fixed_iter_init (cairo_path_fixed_iter_t *iter,
+                            const cairo_path_fixed_t *path)
+{
+    iter->first = iter->buf = cairo_path_head (path);
+    iter->n_op = 0;
+    iter->n_point = 0;
+}
+
+static cairo_bool_t
+_cairo_path_fixed_iter_next_op (cairo_path_fixed_iter_t *iter)
+{
+    if (++iter->n_op >= iter->buf->num_ops) {
+       iter->buf = cairo_path_buf_next (iter->buf);
+       if (iter->buf == iter->first) {
+           iter->buf = NULL;
+           return FALSE;
+       }
+
+       iter->n_op = 0;
+       iter->n_point = 0;
+    }
+
+    return TRUE;
+}
+
+cairo_bool_t
+_cairo_path_fixed_iter_is_fill_box (cairo_path_fixed_iter_t *_iter,
+                                   cairo_box_t *box)
+{
+    cairo_point_t points[5];
+    cairo_path_fixed_iter_t iter;
+
+    if (_iter->buf == NULL)
+       return FALSE;
+
+    iter = *_iter;
+
+    if (iter.n_op == iter.buf->num_ops && ! _cairo_path_fixed_iter_next_op (&iter))
+       return FALSE;
+
+    /* Check whether the ops are those that would be used for a rectangle */
+    if (iter.buf->op[iter.n_op] != CAIRO_PATH_OP_MOVE_TO)
+       return FALSE;
+    points[0] = iter.buf->points[iter.n_point++];
+    if (! _cairo_path_fixed_iter_next_op (&iter))
+       return FALSE;
+
+    if (iter.buf->op[iter.n_op] != CAIRO_PATH_OP_LINE_TO)
+       return FALSE;
+    points[1] = iter.buf->points[iter.n_point++];
+    if (! _cairo_path_fixed_iter_next_op (&iter))
+       return FALSE;
+
+    /* a horizontal/vertical closed line is also a degenerate rectangle */
+    switch (iter.buf->op[iter.n_op]) {
+    case CAIRO_PATH_OP_CLOSE_PATH:
+       _cairo_path_fixed_iter_next_op (&iter);
+    case CAIRO_PATH_OP_MOVE_TO: /* implicit close */
+       box->p1 = box->p2 = points[0];
+       *_iter = iter;
+       return TRUE;
+    default:
+       return FALSE;
+    case CAIRO_PATH_OP_LINE_TO:
+       break;
+    }
+
+    points[2] = iter.buf->points[iter.n_point++];
+    if (! _cairo_path_fixed_iter_next_op (&iter))
+       return FALSE;
+
+    if (iter.buf->op[iter.n_op] != CAIRO_PATH_OP_LINE_TO)
+       return FALSE;
+    points[3] = iter.buf->points[iter.n_point++];
+
+    /* Now, there are choices. The rectangle might end with a LINE_TO
+     * (to the original point), but this isn't required. If it
+     * doesn't, then it must end with a CLOSE_PATH (which may be implicit). */
+    if (! _cairo_path_fixed_iter_next_op (&iter)) {
+       /* implicit close due to fill */
+    } else if (iter.buf->op[iter.n_op] == CAIRO_PATH_OP_LINE_TO) {
+       points[4] = iter.buf->points[iter.n_point++];
+       if (points[4].x != points[0].x || points[4].y != points[0].y)
+           return FALSE;
+       _cairo_path_fixed_iter_next_op (&iter);
+    } else if (iter.buf->op[iter.n_op] == CAIRO_PATH_OP_CLOSE_PATH) {
+       _cairo_path_fixed_iter_next_op (&iter);
+    } else if (iter.buf->op[iter.n_op] == CAIRO_PATH_OP_MOVE_TO) {
+       /* implicit close-path due to new-sub-path */
+    } else {
+       return FALSE;
+    }
+
+    /* Ok, we may have a box, if the points line up */
+    if (points[0].y == points[1].y &&
+       points[1].x == points[2].x &&
+       points[2].y == points[3].y &&
+       points[3].x == points[0].x)
+    {
+       box->p1 = points[0];
+       box->p2 = points[2];
+       *_iter = iter;
+       return TRUE;
+    }
+
+    if (points[0].x == points[1].x &&
+       points[1].y == points[2].y &&
+       points[2].x == points[3].x &&
+       points[3].y == points[0].y)
+    {
+       box->p1 = points[1];
+       box->p2 = points[3];
+       *_iter = iter;
+       return TRUE;
+    }
+
+    return FALSE;
+}
+
+cairo_bool_t
+_cairo_path_fixed_iter_at_end (const cairo_path_fixed_iter_t *iter)
+{
+    if (iter->buf == NULL)
+       return TRUE;
+
+    return iter->n_op == iter->buf->num_ops;
+}
diff --git a/src/cairo-path-in-fill.c b/src/cairo-path-in-fill.c
new file mode 100644 (file)
index 0000000..1787fb1
--- /dev/null
@@ -0,0 +1,290 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2008 Chris Wilson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Chris Wilson.
+ *
+ * Contributor(s):
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+#include "cairo-path-fixed-private.h"
+
+typedef struct cairo_in_fill {
+    double tolerance;
+    cairo_bool_t on_edge;
+    int winding;
+
+    cairo_fixed_t x, y;
+
+    cairo_bool_t has_current_point;
+    cairo_point_t current_point;
+    cairo_point_t first_point;
+} cairo_in_fill_t;
+
+static void
+_cairo_in_fill_init (cairo_in_fill_t   *in_fill,
+                    double              tolerance,
+                    double              x,
+                    double              y)
+{
+    in_fill->on_edge = FALSE;
+    in_fill->winding = 0;
+    in_fill->tolerance = tolerance;
+
+    in_fill->x = _cairo_fixed_from_double (x);
+    in_fill->y = _cairo_fixed_from_double (y);
+
+    in_fill->has_current_point = FALSE;
+    in_fill->current_point.x = 0;
+    in_fill->current_point.y = 0;
+}
+
+static void
+_cairo_in_fill_fini (cairo_in_fill_t *in_fill)
+{
+}
+
+static int
+edge_compare_for_y_against_x (const cairo_point_t *p1,
+                             const cairo_point_t *p2,
+                             cairo_fixed_t y,
+                             cairo_fixed_t x)
+{
+    cairo_fixed_t adx, ady;
+    cairo_fixed_t dx, dy;
+    cairo_int64_t L, R;
+
+    adx = p2->x - p1->x;
+    dx = x - p1->x;
+
+    if (adx == 0)
+       return -dx;
+    if ((adx ^ dx) < 0)
+       return adx;
+
+    dy = y - p1->y;
+    ady = p2->y - p1->y;
+
+    L = _cairo_int32x32_64_mul (dy, adx);
+    R = _cairo_int32x32_64_mul (dx, ady);
+
+    return _cairo_int64_cmp (L, R);
+}
+
+static void
+_cairo_in_fill_add_edge (cairo_in_fill_t *in_fill,
+                        const cairo_point_t *p1,
+                        const cairo_point_t *p2)
+{
+    int dir;
+
+    if (in_fill->on_edge)
+       return;
+
+    /* count the number of edge crossing to -∞ */
+
+    dir = 1;
+    if (p2->y < p1->y) {
+       const cairo_point_t *tmp;
+
+       tmp = p1;
+       p1 = p2;
+       p2 = tmp;
+
+       dir = -1;
+    }
+
+    /* First check whether the query is on an edge */
+    if ((p1->x == in_fill->x && p1->y == in_fill->y) ||
+       (p2->x == in_fill->x && p2->y == in_fill->y) ||
+       (! (p2->y < in_fill->y || p1->y > in_fill->y ||
+          (p1->x > in_fill->x && p2->x > in_fill->x) ||
+          (p1->x < in_fill->x && p2->x < in_fill->x)) &&
+        edge_compare_for_y_against_x (p1, p2, in_fill->y, in_fill->x) == 0))
+    {
+       in_fill->on_edge = TRUE;
+       return;
+    }
+
+    /* edge is entirely above or below, note the shortening rule */
+    if (p2->y <= in_fill->y || p1->y > in_fill->y)
+       return;
+
+    /* edge lies wholly to the right */
+    if (p1->x >= in_fill->x && p2->x >= in_fill->x)
+       return;
+
+    if ((p1->x <= in_fill->x && p2->x <= in_fill->x) ||
+       edge_compare_for_y_against_x (p1, p2, in_fill->y, in_fill->x) < 0)
+    {
+       in_fill->winding += dir;
+    }
+}
+
+static cairo_status_t
+_cairo_in_fill_move_to (void *closure,
+                       const cairo_point_t *point)
+{
+    cairo_in_fill_t *in_fill = closure;
+
+    /* implicit close path */
+    if (in_fill->has_current_point) {
+       _cairo_in_fill_add_edge (in_fill,
+                                &in_fill->current_point,
+                                &in_fill->first_point);
+    }
+
+    in_fill->first_point = *point;
+    in_fill->current_point = *point;
+    in_fill->has_current_point = TRUE;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_in_fill_line_to (void *closure,
+                       const cairo_point_t *point)
+{
+    cairo_in_fill_t *in_fill = closure;
+
+    if (in_fill->has_current_point)
+       _cairo_in_fill_add_edge (in_fill, &in_fill->current_point, point);
+
+    in_fill->current_point = *point;
+    in_fill->has_current_point = TRUE;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_in_fill_curve_to (void *closure,
+                        const cairo_point_t *b,
+                        const cairo_point_t *c,
+                        const cairo_point_t *d)
+{
+    cairo_in_fill_t *in_fill = closure;
+    cairo_spline_t spline;
+    cairo_fixed_t top, bot, left;
+
+    /* first reject based on bbox */
+    bot = top = in_fill->current_point.y;
+    if (b->y < top) top = b->y;
+    if (b->y > bot) bot = b->y;
+    if (c->y < top) top = c->y;
+    if (c->y > bot) bot = c->y;
+    if (d->y < top) top = d->y;
+    if (d->y > bot) bot = d->y;
+    if (bot < in_fill->y || top > in_fill->y) {
+       in_fill->current_point = *d;
+       return CAIRO_STATUS_SUCCESS;
+    }
+
+    left = in_fill->current_point.x;
+    if (b->x < left) left = b->x;
+    if (c->x < left) left = c->x;
+    if (d->x < left) left = d->x;
+    if (left > in_fill->x) {
+       in_fill->current_point = *d;
+       return CAIRO_STATUS_SUCCESS;
+    }
+
+    /* XXX Investigate direct inspection of the inflections? */
+    if (! _cairo_spline_init (&spline,
+                             (cairo_spline_add_point_func_t)_cairo_in_fill_line_to,
+                             in_fill,
+                             &in_fill->current_point, b, c, d))
+    {
+       return CAIRO_STATUS_SUCCESS;
+    }
+
+    return _cairo_spline_decompose (&spline, in_fill->tolerance);
+}
+
+static cairo_status_t
+_cairo_in_fill_close_path (void *closure)
+{
+    cairo_in_fill_t *in_fill = closure;
+
+    if (in_fill->has_current_point) {
+       _cairo_in_fill_add_edge (in_fill,
+                                &in_fill->current_point,
+                                &in_fill->first_point);
+
+       in_fill->has_current_point = FALSE;
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_bool_t
+_cairo_path_fixed_in_fill (const cairo_path_fixed_t    *path,
+                          cairo_fill_rule_t     fill_rule,
+                          double                tolerance,
+                          double                x,
+                          double                y)
+{
+    cairo_in_fill_t in_fill;
+    cairo_status_t status;
+    cairo_bool_t is_inside;
+
+    if (_cairo_path_fixed_fill_is_empty (path))
+       return FALSE;
+
+    _cairo_in_fill_init (&in_fill, tolerance, x, y);
+
+    status = _cairo_path_fixed_interpret (path,
+                                         _cairo_in_fill_move_to,
+                                         _cairo_in_fill_line_to,
+                                         _cairo_in_fill_curve_to,
+                                         _cairo_in_fill_close_path,
+                                         &in_fill);
+    assert (status == CAIRO_STATUS_SUCCESS);
+
+    _cairo_in_fill_close_path (&in_fill);
+
+    if (in_fill.on_edge) {
+       is_inside = TRUE;
+    } else switch (fill_rule) {
+    case CAIRO_FILL_RULE_EVEN_ODD:
+       is_inside = in_fill.winding & 1;
+       break;
+    case CAIRO_FILL_RULE_WINDING:
+       is_inside = in_fill.winding != 0;
+       break;
+    default:
+       ASSERT_NOT_REACHED;
+       is_inside = FALSE;
+       break;
+    }
+
+    _cairo_in_fill_fini (&in_fill);
+
+    return is_inside;
+}
diff --git a/src/cairo-path-private.h b/src/cairo-path-private.h
new file mode 100644 (file)
index 0000000..7b54317
--- /dev/null
@@ -0,0 +1,57 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2005 Red Hat, Inc.
+ * Copyright © 2006 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ *     Carl D. Worth <cworth@redhat.com>
+ */
+
+#ifndef CAIRO_PATH_PRIVATE_H
+#define CAIRO_PATH_PRIVATE_H
+
+#include "cairoint.h"
+
+cairo_private cairo_path_t *
+_cairo_path_create (cairo_path_fixed_t *path,
+                   cairo_t             *cr);
+
+cairo_private cairo_path_t *
+_cairo_path_create_flat (cairo_path_fixed_t *path,
+                        cairo_t            *cr);
+
+cairo_private cairo_path_t *
+_cairo_path_create_in_error (cairo_status_t status);
+
+cairo_private cairo_status_t
+_cairo_path_append_to_context (const cairo_path_t      *path,
+                              cairo_t                  *cr);
+
+#endif /* CAIRO_PATH_DATA_PRIVATE_H */
diff --git a/src/cairo-path-stroke-boxes.c b/src/cairo-path-stroke-boxes.c
new file mode 100644 (file)
index 0000000..cd09bb6
--- /dev/null
@@ -0,0 +1,737 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *     Carl D. Worth <cworth@cworth.org>
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#define _BSD_SOURCE /* for hypot() */
+#include "cairoint.h"
+
+#include "cairo-box-private.h"
+#include "cairo-boxes-private.h"
+#include "cairo-error-private.h"
+#include "cairo-path-fixed-private.h"
+#include "cairo-slope-private.h"
+#include "cairo-stroke-dash-private.h"
+
+typedef struct _segment_t {
+    cairo_point_t p1, p2;
+    cairo_bool_t is_horizontal;
+    cairo_bool_t has_join;
+} segment_t;
+
+typedef struct _cairo_rectilinear_stroker {
+    const cairo_stroke_style_t *stroke_style;
+    const cairo_matrix_t *ctm;
+    cairo_antialias_t antialias;
+
+    cairo_fixed_t half_line_width;
+    cairo_boxes_t *boxes;
+    cairo_point_t current_point;
+    cairo_point_t first_point;
+    cairo_bool_t open_sub_path;
+
+    cairo_stroker_dash_t dash;
+
+    cairo_bool_t has_bounds;
+    cairo_box_t bounds;
+
+    int num_segments;
+    int segments_size;
+    segment_t *segments;
+    segment_t segments_embedded[8]; /* common case is a single rectangle */
+} cairo_rectilinear_stroker_t;
+
+static void
+_cairo_rectilinear_stroker_limit (cairo_rectilinear_stroker_t *stroker,
+                                 const cairo_box_t *boxes,
+                                 int num_boxes)
+{
+    stroker->has_bounds = TRUE;
+    _cairo_boxes_get_extents (boxes, num_boxes, &stroker->bounds);
+
+    stroker->bounds.p1.x -= stroker->half_line_width;
+    stroker->bounds.p2.x += stroker->half_line_width;
+
+    stroker->bounds.p1.y -= stroker->half_line_width;
+    stroker->bounds.p2.y += stroker->half_line_width;
+}
+
+static cairo_bool_t
+_cairo_rectilinear_stroker_init (cairo_rectilinear_stroker_t   *stroker,
+                                const cairo_stroke_style_t     *stroke_style,
+                                const cairo_matrix_t           *ctm,
+                                cairo_antialias_t               antialias,
+                                cairo_boxes_t                  *boxes)
+{
+    /* This special-case rectilinear stroker only supports
+     * miter-joined lines (not curves) and a translation-only matrix
+     * (though it could probably be extended to support a matrix with
+     * uniform, integer scaling).
+     *
+     * It also only supports horizontal and vertical line_to
+     * elements. But we don't catch that here, but instead return
+     * UNSUPPORTED from _cairo_rectilinear_stroker_line_to if any
+     * non-rectilinear line_to is encountered.
+     */
+    if (stroke_style->line_join        != CAIRO_LINE_JOIN_MITER)
+       return FALSE;
+
+    /* If the miter limit turns right angles into bevels, then we
+     * can't use this optimization. Remember, the ratio is
+     * 1/sin(ɸ/2). So the cutoff is 1/sin(π/4.0) or ⎷2,
+     * which we round for safety. */
+    if (stroke_style->miter_limit < M_SQRT2)
+       return FALSE;
+
+    if (! (stroke_style->line_cap == CAIRO_LINE_CAP_BUTT ||
+          stroke_style->line_cap == CAIRO_LINE_CAP_SQUARE))
+    {
+       return FALSE;
+    }
+
+#ifdef TIZEN_FIX_DASHED_LINE_STROKE
+/* Use uniform scale instead of unity scale */
+    if (! _cairo_matrix_has_uniform_scale (ctm))
+       return FALSE;
+#else
+    if (! _cairo_matrix_has_unity_scale (ctm))
+       return FALSE;
+#endif
+
+    stroker->stroke_style = stroke_style;
+    stroker->ctm = ctm;
+    stroker->antialias = antialias;
+
+    stroker->half_line_width =
+#ifdef TIZEN_FIX_DASHED_LINE_STROKE
+/* Scale line width using ctm matrix's xx value */
+       _cairo_fixed_from_double (stroke_style->line_width*ctm->xx / 2.0);
+#else
+       _cairo_fixed_from_double (stroke_style->line_width / 2.0);
+#endif
+
+    stroker->open_sub_path = FALSE;
+    stroker->segments = stroker->segments_embedded;
+    stroker->segments_size = ARRAY_LENGTH (stroker->segments_embedded);
+    stroker->num_segments = 0;
+
+#ifdef TIZEN_FIX_DASHED_LINE_STROKE
+/* Scale stroke style's dash using ctm matrix's xx value */
+    int i;
+    for(i =0;i < stroke_style->num_dashes;i++)
+       stroke_style->dash[i] *= ctm->xx;
+#endif
+
+    _cairo_stroker_dash_init (&stroker->dash, stroke_style);
+
+    stroker->has_bounds = FALSE;
+
+    stroker->boxes = boxes;
+
+    return TRUE;
+}
+
+static void
+_cairo_rectilinear_stroker_fini (cairo_rectilinear_stroker_t   *stroker)
+{
+    if (stroker->segments != stroker->segments_embedded)
+       free (stroker->segments);
+}
+
+static cairo_status_t
+_cairo_rectilinear_stroker_add_segment (cairo_rectilinear_stroker_t *stroker,
+                                       const cairo_point_t     *p1,
+                                       const cairo_point_t     *p2,
+                                       cairo_bool_t             is_horizontal,
+                                       cairo_bool_t             has_join)
+{
+    if (CAIRO_INJECT_FAULT ())
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    if (stroker->num_segments == stroker->segments_size) {
+       int new_size = stroker->segments_size * 2;
+       segment_t *new_segments;
+
+       if (stroker->segments == stroker->segments_embedded) {
+           new_segments = _cairo_malloc_ab (new_size, sizeof (segment_t));
+           if (unlikely (new_segments == NULL))
+               return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+           memcpy (new_segments, stroker->segments,
+                   stroker->num_segments * sizeof (segment_t));
+       } else {
+           new_segments = _cairo_realloc_ab (stroker->segments,
+                                             new_size, sizeof (segment_t));
+           if (unlikely (new_segments == NULL))
+               return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+       }
+
+       stroker->segments_size = new_size;
+       stroker->segments = new_segments;
+    }
+
+    stroker->segments[stroker->num_segments].p1 = *p1;
+    stroker->segments[stroker->num_segments].p2 = *p2;
+    stroker->segments[stroker->num_segments].has_join = has_join;
+    stroker->segments[stroker->num_segments].is_horizontal = is_horizontal;
+    stroker->num_segments++;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_rectilinear_stroker_emit_segments (cairo_rectilinear_stroker_t *stroker)
+{
+    cairo_line_cap_t line_cap = stroker->stroke_style->line_cap;
+    cairo_fixed_t half_line_width = stroker->half_line_width;
+    cairo_status_t status;
+    int i;
+
+    /* For each segment we generate a single rectangle.
+     * This rectangle is based on a perpendicular extension (by half the
+     * line width) of the segment endpoints * after some adjustments of the
+     * endpoints to account for caps and joins.
+     */
+    for (i = 0; i < stroker->num_segments; i++) {
+       cairo_bool_t lengthen_initial, lengthen_final;
+       cairo_point_t *a, *b;
+       cairo_box_t box;
+
+       a = &stroker->segments[i].p1;
+       b = &stroker->segments[i].p2;
+
+       /* We adjust the initial point of the segment to extend the
+        * rectangle to include the previous cap or join, (this
+        * adjustment applies to all segments except for the first
+        * segment of open, butt-capped paths).
+        *
+        * Overlapping segments will be eliminated by the tessellation.
+        * Ideally, we would not emit these self-intersections at all,
+        * but that is tricky with segments shorter than half_line_width.
+        */
+       lengthen_initial = TRUE;
+       lengthen_final = TRUE;
+       if (stroker->open_sub_path && line_cap == CAIRO_LINE_CAP_BUTT) {
+           if (i == 0)
+               lengthen_initial = FALSE;
+
+           if (i == stroker->num_segments - 1)
+               lengthen_final = FALSE;
+       }
+
+       /* Perform the adjustments of the endpoints. */
+       if (lengthen_initial | lengthen_final) {
+           if (a->y == b->y) {
+               if (a->x < b->x) {
+                   if (lengthen_initial)
+                       a->x -= half_line_width;
+                   if (lengthen_final)
+                       b->x += half_line_width;
+               } else {
+                   if (lengthen_initial)
+                       a->x += half_line_width;
+                   if (lengthen_final)
+                       b->x -= half_line_width;
+               }
+           } else {
+               if (a->y < b->y) {
+                   if (lengthen_initial)
+                       a->y -= half_line_width;
+                   if (lengthen_final)
+                       b->y += half_line_width;
+               } else {
+                   if (lengthen_initial)
+                       a->y += half_line_width;
+                   if (lengthen_final)
+                       b->y -= half_line_width;
+               }
+           }
+       }
+
+       /* Form the rectangle by expanding by half the line width in
+        * either perpendicular direction. */
+       if (a->y == b->y) {
+           a->y -= half_line_width;
+           b->y += half_line_width;
+       } else {
+           a->x -= half_line_width;
+           b->x += half_line_width;
+       }
+
+       if (a->x < b->x) {
+           box.p1.x = a->x;
+           box.p2.x = b->x;
+       } else {
+           box.p1.x = b->x;
+           box.p2.x = a->x;
+       }
+       if (a->y < b->y) {
+           box.p1.y = a->y;
+           box.p2.y = b->y;
+       } else {
+           box.p1.y = b->y;
+           box.p2.y = a->y;
+       }
+
+       status = _cairo_boxes_add (stroker->boxes, stroker->antialias, &box);
+       if (unlikely (status))
+           return status;
+    }
+
+    stroker->num_segments = 0;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_rectilinear_stroker_emit_segments_dashed (cairo_rectilinear_stroker_t *stroker)
+{
+    cairo_status_t status;
+    cairo_line_cap_t line_cap = stroker->stroke_style->line_cap;
+    cairo_fixed_t half_line_width = stroker->half_line_width;
+    int i;
+
+    for (i = 0; i < stroker->num_segments; i++) {
+       cairo_point_t *a, *b;
+       cairo_bool_t is_horizontal;
+       cairo_box_t box;
+
+       a = &stroker->segments[i].p1;
+       b = &stroker->segments[i].p2;
+
+       is_horizontal = stroker->segments[i].is_horizontal;
+
+       /* Handle the joins for a potentially degenerate segment. */
+       if (line_cap == CAIRO_LINE_CAP_BUTT &&
+           stroker->segments[i].has_join &&
+           (i != stroker->num_segments - 1 ||
+            (! stroker->open_sub_path && stroker->dash.dash_starts_on)))
+       {
+           cairo_slope_t out_slope;
+           int j = (i + 1) % stroker->num_segments;
+
+           box.p1 = stroker->segments[i].p1;
+           box.p2 = stroker->segments[i].p2;
+           _cairo_slope_init (&out_slope,
+                              &stroker->segments[j].p1,
+                              &stroker->segments[j].p2);
+
+           if (is_horizontal) {
+               if (box.p1.x <= box.p2.x) {
+                   box.p1.x = box.p2.x;
+                   box.p2.x += half_line_width;
+               } else {
+                   box.p1.x = box.p2.x - half_line_width;
+               }
+               if (out_slope.dy >= 0)
+                   box.p1.y -= half_line_width;
+               if (out_slope.dy <= 0)
+                   box.p2.y += half_line_width;
+           } else {
+               if (box.p1.y <= box.p2.y) {
+                   box.p1.y = box.p2.y;
+                   box.p2.y += half_line_width;
+               } else {
+                   box.p1.y = box.p2.y - half_line_width;
+               }
+               if (out_slope.dx >= 0)
+                   box.p1.x -= half_line_width;
+               if (out_slope.dx <= 0)
+                   box.p2.x += half_line_width;
+           }
+
+           status = _cairo_boxes_add (stroker->boxes, stroker->antialias, &box);
+           if (unlikely (status))
+               return status;
+       }
+
+       /* Perform the adjustments of the endpoints. */
+       if (is_horizontal) {
+           if (line_cap == CAIRO_LINE_CAP_SQUARE) {
+               if (a->x <= b->x) {
+                   a->x -= half_line_width;
+                   b->x += half_line_width;
+               } else {
+                   a->x += half_line_width;
+                   b->x -= half_line_width;
+               }
+           }
+
+           a->y += half_line_width;
+           b->y -= half_line_width;
+       } else {
+           if (line_cap == CAIRO_LINE_CAP_SQUARE) {
+               if (a->y <= b->y) {
+                   a->y -= half_line_width;
+                   b->y += half_line_width;
+               } else {
+                   a->y += half_line_width;
+                   b->y -= half_line_width;
+               }
+           }
+
+           a->x += half_line_width;
+           b->x -= half_line_width;
+       }
+
+       if (a->x == b->x && a->y == b->y)
+           continue;
+
+       if (a->x < b->x) {
+           box.p1.x = a->x;
+           box.p2.x = b->x;
+       } else {
+           box.p1.x = b->x;
+           box.p2.x = a->x;
+       }
+       if (a->y < b->y) {
+           box.p1.y = a->y;
+           box.p2.y = b->y;
+       } else {
+           box.p1.y = b->y;
+           box.p2.y = a->y;
+       }
+
+       status = _cairo_boxes_add (stroker->boxes, stroker->antialias, &box);
+       if (unlikely (status))
+           return status;
+    }
+
+    stroker->num_segments = 0;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_rectilinear_stroker_move_to (void               *closure,
+                                   const cairo_point_t *point)
+{
+    cairo_rectilinear_stroker_t *stroker = closure;
+    cairo_status_t status;
+
+    if (stroker->dash.dashed)
+       status = _cairo_rectilinear_stroker_emit_segments_dashed (stroker);
+    else
+       status = _cairo_rectilinear_stroker_emit_segments (stroker);
+    if (unlikely (status))
+       return status;
+
+    /* reset the dash pattern for new sub paths */
+    _cairo_stroker_dash_start (&stroker->dash);
+
+    stroker->current_point = *point;
+    stroker->first_point = *point;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_rectilinear_stroker_line_to (void               *closure,
+                                   const cairo_point_t *b)
+{
+    cairo_rectilinear_stroker_t *stroker = closure;
+    cairo_point_t *a = &stroker->current_point;
+    cairo_status_t status;
+
+    /* We only support horizontal or vertical elements. */
+    assert (a->x == b->x || a->y == b->y);
+
+    /* We don't draw anything for degenerate paths. */
+    if (a->x == b->x && a->y == b->y)
+       return CAIRO_STATUS_SUCCESS;
+
+    status = _cairo_rectilinear_stroker_add_segment (stroker, a, b,
+                                                    a->y == b->y,
+                                                    TRUE);
+
+    stroker->current_point = *b;
+    stroker->open_sub_path = TRUE;
+
+    return status;
+}
+
+static cairo_status_t
+_cairo_rectilinear_stroker_line_to_dashed (void                *closure,
+                                          const cairo_point_t  *point)
+{
+    cairo_rectilinear_stroker_t *stroker = closure;
+    const cairo_point_t *a = &stroker->current_point;
+    const cairo_point_t *b = point;
+    cairo_bool_t fully_in_bounds;
+    double sign, remain;
+    cairo_fixed_t mag;
+    cairo_status_t status;
+    cairo_line_t segment;
+    cairo_bool_t dash_on = FALSE;
+    cairo_bool_t is_horizontal;
+
+    /* We don't draw anything for degenerate paths. */
+    if (a->x == b->x && a->y == b->y)
+       return CAIRO_STATUS_SUCCESS;
+
+    /* We only support horizontal or vertical elements. */
+    assert (a->x == b->x || a->y == b->y);
+
+    fully_in_bounds = TRUE;
+    if (stroker->has_bounds &&
+       (! _cairo_box_contains_point (&stroker->bounds, a) ||
+        ! _cairo_box_contains_point (&stroker->bounds, b)))
+    {
+       fully_in_bounds = FALSE;
+    }
+
+    is_horizontal = a->y == b->y;
+    if (is_horizontal)
+       mag = b->x - a->x;
+    else
+       mag = b->y - a->y;
+    if (mag < 0) {
+       remain = _cairo_fixed_to_double (-mag);
+       sign = 1.;
+    } else {
+       remain = _cairo_fixed_to_double (mag);
+       sign = -1.;
+    }
+
+    segment.p2 = segment.p1 = *a;
+    while (remain > 0.) {
+       double step_length;
+
+       step_length = MIN (stroker->dash.dash_remain, remain);
+       remain -= step_length;
+
+       mag = _cairo_fixed_from_double (sign*remain);
+       if (is_horizontal)
+           segment.p2.x = b->x + mag;
+       else
+           segment.p2.y = b->y + mag;
+
+       if (stroker->dash.dash_on &&
+           (fully_in_bounds ||
+            _cairo_box_intersects_line_segment (&stroker->bounds, &segment)))
+       {
+           status = _cairo_rectilinear_stroker_add_segment (stroker,
+                                                            &segment.p1,
+                                                            &segment.p2,
+                                                            is_horizontal,
+                                                            remain <= 0.);
+           if (unlikely (status))
+               return status;
+
+           dash_on = TRUE;
+       }
+       else
+       {
+           dash_on = FALSE;
+       }
+
+       _cairo_stroker_dash_step (&stroker->dash, step_length);
+       segment.p1 = segment.p2;
+    }
+
+    if (stroker->dash.dash_on && ! dash_on &&
+       (fully_in_bounds ||
+        _cairo_box_intersects_line_segment (&stroker->bounds, &segment)))
+    {
+
+       /* This segment ends on a transition to dash_on, compute a new face
+        * and add cap for the beginning of the next dash_on step.
+        */
+
+       status = _cairo_rectilinear_stroker_add_segment (stroker,
+                                                        &segment.p1,
+                                                        &segment.p1,
+                                                        is_horizontal,
+                                                        TRUE);
+       if (unlikely (status))
+           return status;
+    }
+
+    stroker->current_point = *point;
+    stroker->open_sub_path = TRUE;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_rectilinear_stroker_close_path (void *closure)
+{
+    cairo_rectilinear_stroker_t *stroker = closure;
+    cairo_status_t status;
+
+    /* We don't draw anything for degenerate paths. */
+    if (! stroker->open_sub_path)
+       return CAIRO_STATUS_SUCCESS;
+
+    if (stroker->dash.dashed) {
+       status = _cairo_rectilinear_stroker_line_to_dashed (stroker,
+                                                           &stroker->first_point);
+    } else {
+       status = _cairo_rectilinear_stroker_line_to (stroker,
+                                                    &stroker->first_point);
+    }
+    if (unlikely (status))
+       return status;
+
+    stroker->open_sub_path = FALSE;
+
+    if (stroker->dash.dashed)
+       status = _cairo_rectilinear_stroker_emit_segments_dashed (stroker);
+    else
+       status = _cairo_rectilinear_stroker_emit_segments (stroker);
+    if (unlikely (status))
+       return status;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_int_status_t
+_cairo_path_fixed_stroke_rectilinear_to_boxes (const cairo_path_fixed_t        *path,
+                                              const cairo_stroke_style_t       *stroke_style,
+                                              const cairo_matrix_t     *ctm,
+                                              cairo_antialias_t         antialias,
+                                              cairo_boxes_t            *boxes)
+{
+    cairo_rectilinear_stroker_t rectilinear_stroker;
+    cairo_int_status_t status;
+    cairo_box_t box;
+#ifdef TIZEN_FIX_DASHED_LINE_STROKE
+    int i;
+#endif
+
+    assert (_cairo_path_fixed_stroke_is_rectilinear (path));
+
+    if (! _cairo_rectilinear_stroker_init (&rectilinear_stroker,
+                                          stroke_style, ctm, antialias,
+                                          boxes))
+    {
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+    }
+
+    if (! rectilinear_stroker.dash.dashed &&
+       _cairo_path_fixed_is_stroke_box (path, &box) &&
+       /* if the segments overlap we need to feed them into the tessellator */
+       box.p2.x - box.p1.x > 2* rectilinear_stroker.half_line_width &&
+       box.p2.y - box.p1.y > 2* rectilinear_stroker.half_line_width)
+    {
+       cairo_box_t b;
+
+       /* top */
+       b.p1.x = box.p1.x - rectilinear_stroker.half_line_width;
+       b.p2.x = box.p2.x + rectilinear_stroker.half_line_width;
+       b.p1.y = box.p1.y - rectilinear_stroker.half_line_width;
+       b.p2.y = box.p1.y + rectilinear_stroker.half_line_width;
+       status = _cairo_boxes_add (boxes, antialias, &b);
+       assert (status == CAIRO_INT_STATUS_SUCCESS);
+
+       /* left  (excluding top/bottom) */
+       b.p1.x = box.p1.x - rectilinear_stroker.half_line_width;
+       b.p2.x = box.p1.x + rectilinear_stroker.half_line_width;
+       b.p1.y = box.p1.y + rectilinear_stroker.half_line_width;
+       b.p2.y = box.p2.y - rectilinear_stroker.half_line_width;
+       status = _cairo_boxes_add (boxes, antialias, &b);
+       assert (status == CAIRO_INT_STATUS_SUCCESS);
+
+       /* right  (excluding top/bottom) */
+       b.p1.x = box.p2.x - rectilinear_stroker.half_line_width;
+       b.p2.x = box.p2.x + rectilinear_stroker.half_line_width;
+       b.p1.y = box.p1.y + rectilinear_stroker.half_line_width;
+       b.p2.y = box.p2.y - rectilinear_stroker.half_line_width;
+       status = _cairo_boxes_add (boxes, antialias, &b);
+       assert (status == CAIRO_INT_STATUS_SUCCESS);
+
+       /* bottom */
+       b.p1.x = box.p1.x - rectilinear_stroker.half_line_width;
+       b.p2.x = box.p2.x + rectilinear_stroker.half_line_width;
+       b.p1.y = box.p2.y - rectilinear_stroker.half_line_width;
+       b.p2.y = box.p2.y + rectilinear_stroker.half_line_width;
+       status = _cairo_boxes_add (boxes, antialias, &b);
+       assert (status == CAIRO_INT_STATUS_SUCCESS);
+
+       goto done;
+    }
+
+    if (boxes->num_limits) {
+       _cairo_rectilinear_stroker_limit (&rectilinear_stroker,
+                                         boxes->limits,
+                                         boxes->num_limits);
+    }
+
+    status = _cairo_path_fixed_interpret (path,
+                                         _cairo_rectilinear_stroker_move_to,
+                                         rectilinear_stroker.dash.dashed ?
+                                         _cairo_rectilinear_stroker_line_to_dashed :
+                                         _cairo_rectilinear_stroker_line_to,
+                                         NULL,
+                                         _cairo_rectilinear_stroker_close_path,
+                                         &rectilinear_stroker);
+    if (unlikely (status))
+       goto BAIL;
+
+    if (rectilinear_stroker.dash.dashed)
+       status = _cairo_rectilinear_stroker_emit_segments_dashed (&rectilinear_stroker);
+    else
+       status = _cairo_rectilinear_stroker_emit_segments (&rectilinear_stroker);
+    if (unlikely (status))
+       goto BAIL;
+
+    /* As we incrementally tessellate, we do not eliminate self-intersections */
+    status = _cairo_bentley_ottmann_tessellate_boxes (boxes,
+                                                     CAIRO_FILL_RULE_WINDING,
+                                                     boxes);
+    if (unlikely (status))
+       goto BAIL;
+
+done:
+#ifdef TIZEN_FIX_DASHED_LINE_STROKE
+/* Restore stroke style's dash value */
+    for(i =0;i < stroke_style->num_dashes;i++)
+       stroke_style->dash[i] /= ctm->xx;
+#endif
+
+    _cairo_rectilinear_stroker_fini (&rectilinear_stroker);
+    return CAIRO_STATUS_SUCCESS;
+
+BAIL:
+#ifdef TIZEN_FIX_DASHED_LINE_STROKE
+/* Restore stroke style's dash value */
+    for(i =0;i < stroke_style->num_dashes;i++)
+       stroke_style->dash[i] /= ctm->xx;
+#endif
+
+    _cairo_rectilinear_stroker_fini (&rectilinear_stroker);
+    _cairo_boxes_clear (boxes);
+    return status;
+}
diff --git a/src/cairo-path-stroke-polygon.c b/src/cairo-path-stroke-polygon.c
new file mode 100644 (file)
index 0000000..9128a00
--- /dev/null
@@ -0,0 +1,1410 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ * Copyright © 2011 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *     Carl D. Worth <cworth@cworth.org>
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#define _BSD_SOURCE /* for hypot() */
+#include "cairoint.h"
+
+#include "cairo-box-private.h"
+#include "cairo-boxes-private.h"
+#include "cairo-contour-private.h"
+#include "cairo-error-private.h"
+#include "cairo-path-fixed-private.h"
+#include "cairo-slope-private.h"
+
+#define DEBUG 0
+
+struct stroker {
+    cairo_stroke_style_t style;
+
+#if DEBUG
+    cairo_contour_t path;
+#endif
+
+    struct stroke_contour {
+       /* Note that these are not strictly contours as they may intersect */
+       cairo_contour_t contour;
+    } cw, ccw;
+    cairo_uint64_t contour_tolerance;
+    cairo_polygon_t *polygon;
+
+    const cairo_matrix_t *ctm;
+    const cairo_matrix_t *ctm_inverse;
+    double tolerance;
+    cairo_bool_t ctm_det_positive;
+
+    cairo_pen_t pen;
+
+    cairo_point_t first_point;
+
+    cairo_bool_t has_initial_sub_path;
+
+    cairo_bool_t has_current_face;
+    cairo_stroke_face_t current_face;
+
+    cairo_bool_t has_first_face;
+    cairo_stroke_face_t first_face;
+};
+
+static inline double
+normalize_slope (double *dx, double *dy);
+
+static void
+compute_face (const cairo_point_t *point,
+             const cairo_slope_t *dev_slope,
+             struct stroker *stroker,
+             cairo_stroke_face_t *face);
+
+static cairo_uint64_t
+point_distance_sq (const cairo_point_t *p1,
+                       const cairo_point_t *p2)
+{
+    int32_t dx = p1->x - p2->x;
+    int32_t dy = p1->y - p2->y;
+    return _cairo_int32x32_64_mul (dx, dx) + _cairo_int32x32_64_mul (dy, dy);
+}
+
+static cairo_bool_t
+within_tolerance (const cairo_point_t *p1,
+             const cairo_point_t *p2,
+             cairo_uint64_t tolerance)
+{
+    return FALSE;
+    return _cairo_int64_lt (point_distance_sq (p1, p2), tolerance);
+}
+
+static void
+contour_add_point (struct stroker *stroker,
+                  struct stroke_contour *c,
+                  const cairo_point_t *point)
+{
+    if (! within_tolerance (point, _cairo_contour_last_point (&c->contour),
+                       stroker->contour_tolerance))
+       _cairo_contour_add_point (&c->contour, point);
+    //*_cairo_contour_last_point (&c->contour) = *point;
+}
+
+static void
+translate_point (cairo_point_t *point, const cairo_point_t *offset)
+{
+    point->x += offset->x;
+    point->y += offset->y;
+}
+
+static int
+slope_compare_sgn (double dx1, double dy1, double dx2, double dy2)
+{
+    double  c = (dx1 * dy2 - dx2 * dy1);
+
+    if (c > 0) return 1;
+    if (c < 0) return -1;
+    return 0;
+}
+
+static inline int
+range_step (int i, int step, int max)
+{
+    i += step;
+    if (i < 0)
+       i = max - 1;
+    if (i >= max)
+       i = 0;
+    return i;
+}
+
+/*
+ * Construct a fan around the midpoint using the vertices from pen between
+ * inpt and outpt.
+ */
+static void
+add_fan (struct stroker *stroker,
+        const cairo_slope_t *in_vector,
+        const cairo_slope_t *out_vector,
+        const cairo_point_t *midpt,
+        const cairo_point_t *inpt,
+        const cairo_point_t *outpt,
+        cairo_bool_t clockwise,
+        struct stroke_contour *c)
+{
+    int start, stop, step, i, npoints;
+
+    if (clockwise) {
+       step  = 1;
+
+       start = _cairo_pen_find_active_cw_vertex_index (&stroker->pen,
+                                                       in_vector);
+       if (_cairo_slope_compare (&stroker->pen.vertices[start].slope_cw,
+                                 in_vector) < 0)
+           start = range_step (start, 1, stroker->pen.num_vertices);
+
+       stop  = _cairo_pen_find_active_cw_vertex_index (&stroker->pen,
+                                                       out_vector);
+       if (_cairo_slope_compare (&stroker->pen.vertices[stop].slope_ccw,
+                                 out_vector) > 0)
+       {
+           stop = range_step (stop, -1, stroker->pen.num_vertices);
+           if (_cairo_slope_compare (&stroker->pen.vertices[stop].slope_cw,
+                                     in_vector) < 0)
+               return;
+       }
+
+       npoints = stop - start;
+    } else {
+       step  = -1;
+
+       start = _cairo_pen_find_active_ccw_vertex_index (&stroker->pen,
+                                                        in_vector);
+       if (_cairo_slope_compare (&stroker->pen.vertices[start].slope_ccw,
+                                 in_vector) < 0)
+           start = range_step (start, -1, stroker->pen.num_vertices);
+
+       stop  = _cairo_pen_find_active_ccw_vertex_index (&stroker->pen,
+                                                        out_vector);
+       if (_cairo_slope_compare (&stroker->pen.vertices[stop].slope_cw,
+                                 out_vector) > 0)
+       {
+           stop = range_step (stop, 1, stroker->pen.num_vertices);
+           if (_cairo_slope_compare (&stroker->pen.vertices[stop].slope_ccw,
+                                     in_vector) < 0)
+               return;
+       }
+
+       npoints = start - stop;
+    }
+    stop = range_step (stop, step, stroker->pen.num_vertices);
+    if (npoints < 0)
+       npoints += stroker->pen.num_vertices;
+    if (npoints <= 1)
+       return;
+
+    for (i = start;
+        i != stop;
+       i = range_step (i, step, stroker->pen.num_vertices))
+    {
+       cairo_point_t p = *midpt;
+       translate_point (&p, &stroker->pen.vertices[i].point);
+       contour_add_point (stroker, c, &p);
+    }
+}
+
+static int
+join_is_clockwise (const cairo_stroke_face_t *in,
+                  const cairo_stroke_face_t *out)
+{
+    return _cairo_slope_compare (&in->dev_vector, &out->dev_vector) < 0;
+}
+
+static cairo_int64_t
+distance_from_face (const cairo_stroke_face_t *face,
+                   const cairo_point_t *p,
+                   cairo_bool_t negate)
+{
+    int32_t dx = (p->x - face->point.x);
+    int32_t dy = (p->y - face->point.y);
+    cairo_int64_t d;
+
+    d = _cairo_int64_sub (_cairo_int32x32_64_mul (dx, face->dev_vector.dy),
+                         _cairo_int32x32_64_mul (dy, face->dev_vector.dx));
+    if (negate)
+       d = _cairo_int64_negate (d);
+    return d;
+}
+
+static cairo_int64_t
+distance_along_face (const cairo_stroke_face_t *face,
+                   const cairo_point_t *p)
+{
+    int32_t dx = (p->x - face->point.x);
+    int32_t dy = (p->y - face->point.y);
+    return _cairo_int64_add (_cairo_int32x32_64_mul (dx, face->dev_vector.dx),
+                            _cairo_int32x32_64_mul (dy, face->dev_vector.dy));
+}
+
+static void
+compute_inner_joint (cairo_point_t *p1, cairo_int64_t d_p1,
+                    const cairo_point_t *p2, cairo_int64_t d_p2,
+                    cairo_int64_t half_line_width)
+{
+    int32_t dx = p2->x - p1->x;
+    int32_t dy = p2->y - p1->y;
+
+    half_line_width = _cairo_int64_sub (half_line_width, d_p1);
+    d_p2 = _cairo_int64_sub (d_p2, d_p1);
+
+    p1->x += _cairo_int_96by64_32x64_divrem (_cairo_int64x32_128_mul (half_line_width, dx),
+                                             d_p2).quo;
+
+    p1->y += _cairo_int_96by64_32x64_divrem (_cairo_int64x32_128_mul (half_line_width, dy),
+                                             d_p2).quo;
+}
+
+static void
+inner_join (struct stroker *stroker,
+           const cairo_stroke_face_t *in,
+           const cairo_stroke_face_t *out,
+           int clockwise)
+{
+#if 0
+    cairo_point_t last;
+    const cairo_point_t *p, *outpt;
+    struct stroke_contour *inner;
+    cairo_int64_t d_p, d_last;
+    cairo_int64_t half_line_width;
+    cairo_bool_t negate;
+
+    /* XXX line segments shorter than line-width */
+
+    if (clockwise) {
+       inner = &stroker->ccw;
+       outpt = &out->ccw;
+       negate = 1;
+    } else {
+       inner = &stroker->cw;
+       outpt = &out->cw;
+       negate = 0;
+    }
+
+    half_line_width = CAIRO_FIXED_ONE*CAIRO_FIXED_ONE/2 * stroker->style.line_width * out->length + .5;
+
+    /* On the inside, the previous end-point is always
+     * closer to the new face by definition.
+     */
+    last = *_cairo_contour_last_point (&inner->contour);
+    d_last = distance_from_face (out, &last, negate);
+    _cairo_contour_remove_last_point (&inner->contour);
+
+prev:
+    if (inner->contour.chain.num_points == 0) {
+       contour_add_point (stroker, inner, outpt);
+       return;
+    }
+    p = _cairo_contour_last_point (&inner->contour);
+    d_p = distance_from_face (out, p, negate);
+    if (_cairo_int64_lt (d_p, half_line_width) &&
+       !_cairo_int64_negative (distance_along_face (out, p)))
+    {
+       last = *p;
+       d_last = d_p;
+       _cairo_contour_remove_last_point (&inner->contour);
+       goto prev;
+    }
+
+    compute_inner_joint (&last, d_last, p, d_p, half_line_width);
+    contour_add_point (stroker, inner, &last);
+#else
+    const cairo_point_t *outpt;
+    struct stroke_contour *inner;
+
+    if (clockwise) {
+       inner = &stroker->ccw;
+       outpt = &out->ccw;
+    } else {
+       inner = &stroker->cw;
+       outpt = &out->cw;
+    }
+    contour_add_point (stroker, inner, &in->point);
+    contour_add_point (stroker, inner, outpt);
+#endif
+}
+
+static void
+inner_close (struct stroker *stroker,
+            const cairo_stroke_face_t *in,
+            cairo_stroke_face_t *out)
+{
+#if 0
+    cairo_point_t last;
+    const cairo_point_t *p, *outpt, *inpt;
+    struct stroke_contour *inner;
+    struct _cairo_contour_chain *chain;
+
+    /* XXX line segments shorter than line-width */
+
+    if (join_is_clockwise (in, out)) {
+       inner = &stroker->ccw;
+       outpt = &in->ccw;
+       inpt = &out->ccw;
+    } else {
+       inner = &stroker->cw;
+       outpt = &in->cw;
+       inpt = &out->cw;
+    }
+
+    if (inner->contour.chain.num_points == 0) {
+       contour_add_point (stroker, inner, &in->point);
+       contour_add_point (stroker, inner, inpt);
+       *_cairo_contour_first_point (&inner->contour) =
+           *_cairo_contour_last_point (&inner->contour);
+       return;
+    }
+
+    line_width = stroker->style.line_width/2;
+    line_width *= CAIRO_FIXED_ONE;
+
+    d_last = sign * distance_from_face (out, outpt);
+    last = *outpt;
+
+    for (chain = &inner->contour.chain; chain; chain = chain->next) {
+       for (i = 0; i < chain->num_points; i++) {
+           p = &chain->points[i];
+           if ((d_p = sign * distance_from_face (in, p)) >= line_width &&
+               distance_from_edge (stroker, inpt, &last, p) < line_width)
+           {
+               goto out;
+           }
+
+           if (p->x != last.x || p->y != last.y) {
+               last = *p;
+               d_last = d_p;
+           }
+       }
+    }
+out:
+
+    if (d_p != d_last) {
+       double dot = (line_width - d_last) / (d_p - d_last);
+       last.x += dot * (p->x - last.x);
+       last.y += dot * (p->y - last.y);
+    }
+    *_cairo_contour_last_point (&inner->contour) = last;
+
+    for (chain = &inner->contour.chain; chain; chain = chain->next) {
+       for (i = 0; i < chain->num_points; i++) {
+           cairo_point_t *pp = &chain->points[i];
+           if (pp == p)
+               return;
+           *pp = last;
+       }
+    }
+#else
+    const cairo_point_t *inpt;
+    struct stroke_contour *inner;
+
+    if (join_is_clockwise (in, out)) {
+       inner = &stroker->ccw;
+       inpt = &out->ccw;
+    } else {
+       inner = &stroker->cw;
+       inpt = &out->cw;
+    }
+
+    contour_add_point (stroker, inner, &in->point);
+    contour_add_point (stroker, inner, inpt);
+    *_cairo_contour_first_point (&inner->contour) =
+       *_cairo_contour_last_point (&inner->contour);
+#endif
+}
+
+static void
+outer_close (struct stroker *stroker,
+            const cairo_stroke_face_t *in,
+            const cairo_stroke_face_t *out)
+{
+    const cairo_point_t        *inpt, *outpt;
+    struct stroke_contour *outer;
+    int        clockwise;
+
+    if (in->cw.x == out->cw.x && in->cw.y == out->cw.y &&
+       in->ccw.x == out->ccw.x && out->ccw.y == out->ccw.y)
+    {
+       return;
+    }
+    clockwise = join_is_clockwise (in, out);
+    if (clockwise) {
+       inpt = &in->cw;
+       outpt = &out->cw;
+       outer = &stroker->cw;
+    } else {
+       inpt = &in->ccw;
+       outpt = &out->ccw;
+       outer = &stroker->ccw;
+    }
+
+    if (within_tolerance (inpt, outpt, stroker->contour_tolerance)) {
+       *_cairo_contour_first_point (&outer->contour) =
+           *_cairo_contour_last_point (&outer->contour);
+       return;
+    }
+
+    switch (stroker->style.line_join) {
+    case CAIRO_LINE_JOIN_ROUND:
+       /* construct a fan around the common midpoint */
+       add_fan (stroker,
+                &in->dev_vector,
+                &out->dev_vector,
+                &in->point, inpt, outpt,
+                clockwise, outer);
+       break;
+
+    case CAIRO_LINE_JOIN_MITER:
+    default: {
+       /* dot product of incoming slope vector with outgoing slope vector */
+       double  in_dot_out = -in->usr_vector.x * out->usr_vector.x +
+                            -in->usr_vector.y * out->usr_vector.y;
+       double  ml = stroker->style.miter_limit;
+
+       /* Check the miter limit -- lines meeting at an acute angle
+        * can generate long miters, the limit converts them to bevel
+        *
+        * Consider the miter join formed when two line segments
+        * meet at an angle psi:
+        *
+        *         /.\
+        *        /. .\
+        *       /./ \.\
+        *      /./psi\.\
+        *
+        * We can zoom in on the right half of that to see:
+        *
+        *          |\
+        *          | \ psi/2
+        *          |  \
+        *          |   \
+        *          |    \
+        *          |     \
+        *        miter    \
+        *       length     \
+        *          |        \
+        *          |        .\
+        *          |    .     \
+        *          |.   line   \
+        *           \    width  \
+        *            \           \
+        *
+        *
+        * The right triangle in that figure, (the line-width side is
+        * shown faintly with three '.' characters), gives us the
+        * following expression relating miter length, angle and line
+        * width:
+        *
+        *      1 /sin (psi/2) = miter_length / line_width
+        *
+        * The right-hand side of this relationship is the same ratio
+        * in which the miter limit (ml) is expressed. We want to know
+        * when the miter length is within the miter limit. That is
+        * when the following condition holds:
+        *
+        *      1/sin(psi/2) <= ml
+        *      1 <= ml sin(psi/2)
+        *      1 <= ml² sin²(psi/2)
+        *      2 <= ml² 2 sin²(psi/2)
+        *                              2·sin²(psi/2) = 1-cos(psi)
+        *      2 <= ml² (1-cos(psi))
+        *
+        *                              in · out = |in| |out| cos (psi)
+        *
+        * in and out are both unit vectors, so:
+        *
+        *                              in · out = cos (psi)
+        *
+        *      2 <= ml² (1 - in · out)
+        *
+        */
+       if (2 <= ml * ml * (1 - in_dot_out)) {
+           double              x1, y1, x2, y2;
+           double              mx, my;
+           double              dx1, dx2, dy1, dy2;
+           double              ix, iy;
+           double              fdx1, fdy1, fdx2, fdy2;
+           double              mdx, mdy;
+
+           /*
+            * we've got the points already transformed to device
+            * space, but need to do some computation with them and
+            * also need to transform the slope from user space to
+            * device space
+            */
+           /* outer point of incoming line face */
+           x1 = _cairo_fixed_to_double (inpt->x);
+           y1 = _cairo_fixed_to_double (inpt->y);
+           dx1 = in->usr_vector.x;
+           dy1 = in->usr_vector.y;
+           cairo_matrix_transform_distance (stroker->ctm, &dx1, &dy1);
+
+           /* outer point of outgoing line face */
+           x2 = _cairo_fixed_to_double (outpt->x);
+           y2 = _cairo_fixed_to_double (outpt->y);
+           dx2 = out->usr_vector.x;
+           dy2 = out->usr_vector.y;
+           cairo_matrix_transform_distance (stroker->ctm, &dx2, &dy2);
+
+           /*
+            * Compute the location of the outer corner of the miter.
+            * That's pretty easy -- just the intersection of the two
+            * outer edges.  We've got slopes and points on each
+            * of those edges.  Compute my directly, then compute
+            * mx by using the edge with the larger dy; that avoids
+            * dividing by values close to zero.
+            */
+           my = (((x2 - x1) * dy1 * dy2 - y2 * dx2 * dy1 + y1 * dx1 * dy2) /
+                 (dx1 * dy2 - dx2 * dy1));
+           if (fabs (dy1) >= fabs (dy2))
+               mx = (my - y1) * dx1 / dy1 + x1;
+           else
+               mx = (my - y2) * dx2 / dy2 + x2;
+
+           /*
+            * When the two outer edges are nearly parallel, slight
+            * perturbations in the position of the outer points of the lines
+            * caused by representing them in fixed point form can cause the
+            * intersection point of the miter to move a large amount. If
+            * that moves the miter intersection from between the two faces,
+            * then draw a bevel instead.
+            */
+
+           ix = _cairo_fixed_to_double (in->point.x);
+           iy = _cairo_fixed_to_double (in->point.y);
+
+           /* slope of one face */
+           fdx1 = x1 - ix; fdy1 = y1 - iy;
+
+           /* slope of the other face */
+           fdx2 = x2 - ix; fdy2 = y2 - iy;
+
+           /* slope from the intersection to the miter point */
+           mdx = mx - ix; mdy = my - iy;
+
+           /*
+            * Make sure the miter point line lies between the two
+            * faces by comparing the slopes
+            */
+           if (slope_compare_sgn (fdx1, fdy1, mdx, mdy) !=
+               slope_compare_sgn (fdx2, fdy2, mdx, mdy))
+           {
+               cairo_point_t p;
+
+               p.x = _cairo_fixed_from_double (mx);
+               p.y = _cairo_fixed_from_double (my);
+
+               *_cairo_contour_last_point (&outer->contour) = p;
+               *_cairo_contour_first_point (&outer->contour) = p;
+               return;
+           }
+       }
+       break;
+    }
+
+    case CAIRO_LINE_JOIN_BEVEL:
+       break;
+    }
+    contour_add_point (stroker, outer, outpt);
+}
+
+static void
+outer_join (struct stroker *stroker,
+           const cairo_stroke_face_t *in,
+           const cairo_stroke_face_t *out,
+           int clockwise)
+{
+    const cairo_point_t        *inpt, *outpt;
+    struct stroke_contour *outer;
+
+    if (in->cw.x == out->cw.x && in->cw.y == out->cw.y &&
+       in->ccw.x == out->ccw.x && out->ccw.y == out->ccw.y)
+    {
+       return;
+    }
+    if (clockwise) {
+       inpt = &in->cw;
+       outpt = &out->cw;
+       outer = &stroker->cw;
+    } else {
+       inpt = &in->ccw;
+       outpt = &out->ccw;
+       outer = &stroker->ccw;
+    }
+
+    switch (stroker->style.line_join) {
+    case CAIRO_LINE_JOIN_ROUND:
+       /* construct a fan around the common midpoint */
+       add_fan (stroker,
+                &in->dev_vector,
+                &out->dev_vector,
+                &in->point, inpt, outpt,
+                clockwise, outer);
+       break;
+
+    case CAIRO_LINE_JOIN_MITER:
+    default: {
+       /* dot product of incoming slope vector with outgoing slope vector */
+       double  in_dot_out = -in->usr_vector.x * out->usr_vector.x +
+                            -in->usr_vector.y * out->usr_vector.y;
+       double  ml = stroker->style.miter_limit;
+
+       /* Check the miter limit -- lines meeting at an acute angle
+        * can generate long miters, the limit converts them to bevel
+        *
+        * Consider the miter join formed when two line segments
+        * meet at an angle psi:
+        *
+        *         /.\
+        *        /. .\
+        *       /./ \.\
+        *      /./psi\.\
+        *
+        * We can zoom in on the right half of that to see:
+        *
+        *          |\
+        *          | \ psi/2
+        *          |  \
+        *          |   \
+        *          |    \
+        *          |     \
+        *        miter    \
+        *       length     \
+        *          |        \
+        *          |        .\
+        *          |    .     \
+        *          |.   line   \
+        *           \    width  \
+        *            \           \
+        *
+        *
+        * The right triangle in that figure, (the line-width side is
+        * shown faintly with three '.' characters), gives us the
+        * following expression relating miter length, angle and line
+        * width:
+        *
+        *      1 /sin (psi/2) = miter_length / line_width
+        *
+        * The right-hand side of this relationship is the same ratio
+        * in which the miter limit (ml) is expressed. We want to know
+        * when the miter length is within the miter limit. That is
+        * when the following condition holds:
+        *
+        *      1/sin(psi/2) <= ml
+        *      1 <= ml sin(psi/2)
+        *      1 <= ml² sin²(psi/2)
+        *      2 <= ml² 2 sin²(psi/2)
+        *                              2·sin²(psi/2) = 1-cos(psi)
+        *      2 <= ml² (1-cos(psi))
+        *
+        *                              in · out = |in| |out| cos (psi)
+        *
+        * in and out are both unit vectors, so:
+        *
+        *                              in · out = cos (psi)
+        *
+        *      2 <= ml² (1 - in · out)
+        *
+        */
+       if (2 <= ml * ml * (1 - in_dot_out)) {
+           double              x1, y1, x2, y2;
+           double              mx, my;
+           double              dx1, dx2, dy1, dy2;
+           double              ix, iy;
+           double              fdx1, fdy1, fdx2, fdy2;
+           double              mdx, mdy;
+
+           /*
+            * we've got the points already transformed to device
+            * space, but need to do some computation with them and
+            * also need to transform the slope from user space to
+            * device space
+            */
+           /* outer point of incoming line face */
+           x1 = _cairo_fixed_to_double (inpt->x);
+           y1 = _cairo_fixed_to_double (inpt->y);
+           dx1 = in->usr_vector.x;
+           dy1 = in->usr_vector.y;
+           cairo_matrix_transform_distance (stroker->ctm, &dx1, &dy1);
+
+           /* outer point of outgoing line face */
+           x2 = _cairo_fixed_to_double (outpt->x);
+           y2 = _cairo_fixed_to_double (outpt->y);
+           dx2 = out->usr_vector.x;
+           dy2 = out->usr_vector.y;
+           cairo_matrix_transform_distance (stroker->ctm, &dx2, &dy2);
+
+           /*
+            * Compute the location of the outer corner of the miter.
+            * That's pretty easy -- just the intersection of the two
+            * outer edges.  We've got slopes and points on each
+            * of those edges.  Compute my directly, then compute
+            * mx by using the edge with the larger dy; that avoids
+            * dividing by values close to zero.
+            */
+           my = (((x2 - x1) * dy1 * dy2 - y2 * dx2 * dy1 + y1 * dx1 * dy2) /
+                 (dx1 * dy2 - dx2 * dy1));
+           if (fabs (dy1) >= fabs (dy2))
+               mx = (my - y1) * dx1 / dy1 + x1;
+           else
+               mx = (my - y2) * dx2 / dy2 + x2;
+
+           /*
+            * When the two outer edges are nearly parallel, slight
+            * perturbations in the position of the outer points of the lines
+            * caused by representing them in fixed point form can cause the
+            * intersection point of the miter to move a large amount. If
+            * that moves the miter intersection from between the two faces,
+            * then draw a bevel instead.
+            */
+
+           ix = _cairo_fixed_to_double (in->point.x);
+           iy = _cairo_fixed_to_double (in->point.y);
+
+           /* slope of one face */
+           fdx1 = x1 - ix; fdy1 = y1 - iy;
+
+           /* slope of the other face */
+           fdx2 = x2 - ix; fdy2 = y2 - iy;
+
+           /* slope from the intersection to the miter point */
+           mdx = mx - ix; mdy = my - iy;
+
+           /*
+            * Make sure the miter point line lies between the two
+            * faces by comparing the slopes
+            */
+           if (slope_compare_sgn (fdx1, fdy1, mdx, mdy) !=
+               slope_compare_sgn (fdx2, fdy2, mdx, mdy))
+           {
+               cairo_point_t p;
+
+               p.x = _cairo_fixed_from_double (mx);
+               p.y = _cairo_fixed_from_double (my);
+
+               *_cairo_contour_last_point (&outer->contour) = p;
+               return;
+           }
+       }
+       break;
+    }
+
+    case CAIRO_LINE_JOIN_BEVEL:
+       break;
+    }
+    contour_add_point (stroker,outer, outpt);
+}
+
+static void
+add_cap (struct stroker *stroker,
+        const cairo_stroke_face_t *f,
+        struct stroke_contour *c)
+{
+    switch (stroker->style.line_cap) {
+    case CAIRO_LINE_CAP_ROUND: {
+       cairo_slope_t slope;
+
+       slope.dx = -f->dev_vector.dx;
+       slope.dy = -f->dev_vector.dy;
+
+       add_fan (stroker, &f->dev_vector, &slope,
+                &f->point, &f->ccw, &f->cw,
+                FALSE, c);
+       break;
+    }
+
+    case CAIRO_LINE_CAP_SQUARE: {
+       double dx, dy;
+       cairo_slope_t   fvector;
+       cairo_point_t   quad[4];
+
+       dx = f->usr_vector.x;
+       dy = f->usr_vector.y;
+       dx *= stroker->style.line_width / 2.0;
+       dy *= stroker->style.line_width / 2.0;
+       cairo_matrix_transform_distance (stroker->ctm, &dx, &dy);
+       fvector.dx = _cairo_fixed_from_double (dx);
+       fvector.dy = _cairo_fixed_from_double (dy);
+
+       quad[0] = f->ccw;
+       quad[1].x = f->ccw.x + fvector.dx;
+       quad[1].y = f->ccw.y + fvector.dy;
+       quad[2].x = f->cw.x + fvector.dx;
+       quad[2].y = f->cw.y + fvector.dy;
+       quad[3] = f->cw;
+
+       contour_add_point (stroker, c, &quad[1]);
+       contour_add_point (stroker, c, &quad[2]);
+    }
+
+    case CAIRO_LINE_CAP_BUTT:
+    default:
+       break;
+    }
+    contour_add_point (stroker, c, &f->cw);
+}
+
+static void
+add_leading_cap (struct stroker *stroker,
+                const cairo_stroke_face_t *face,
+                struct stroke_contour *c)
+{
+    cairo_stroke_face_t reversed;
+    cairo_point_t t;
+
+    reversed = *face;
+
+    /* The initial cap needs an outward facing vector. Reverse everything */
+    reversed.usr_vector.x = -reversed.usr_vector.x;
+    reversed.usr_vector.y = -reversed.usr_vector.y;
+    reversed.dev_vector.dx = -reversed.dev_vector.dx;
+    reversed.dev_vector.dy = -reversed.dev_vector.dy;
+
+    t = reversed.cw;
+    reversed.cw = reversed.ccw;
+    reversed.ccw = t;
+
+    add_cap (stroker, &reversed, c);
+}
+
+static void
+add_trailing_cap (struct stroker *stroker,
+                 const cairo_stroke_face_t *face,
+                 struct stroke_contour *c)
+{
+    add_cap (stroker, face, c);
+}
+
+static inline double
+normalize_slope (double *dx, double *dy)
+{
+    double dx0 = *dx, dy0 = *dy;
+    double mag;
+
+    assert (dx0 != 0.0 || dy0 != 0.0);
+
+    if (dx0 == 0.0) {
+       *dx = 0.0;
+       if (dy0 > 0.0) {
+           mag = dy0;
+           *dy = 1.0;
+       } else {
+           mag = -dy0;
+           *dy = -1.0;
+       }
+    } else if (dy0 == 0.0) {
+       *dy = 0.0;
+       if (dx0 > 0.0) {
+           mag = dx0;
+           *dx = 1.0;
+       } else {
+           mag = -dx0;
+           *dx = -1.0;
+       }
+    } else {
+       mag = hypot (dx0, dy0);
+       *dx = dx0 / mag;
+       *dy = dy0 / mag;
+    }
+
+    return mag;
+}
+
+static void
+compute_face (const cairo_point_t *point,
+             const cairo_slope_t *dev_slope,
+             struct stroker *stroker,
+             cairo_stroke_face_t *face)
+{
+    double face_dx, face_dy;
+    cairo_point_t offset_ccw, offset_cw;
+    double slope_dx, slope_dy;
+
+    slope_dx = _cairo_fixed_to_double (dev_slope->dx);
+    slope_dy = _cairo_fixed_to_double (dev_slope->dy);
+    face->length = normalize_slope (&slope_dx, &slope_dy);
+    face->dev_slope.x = slope_dx;
+    face->dev_slope.y = slope_dy;
+
+    /*
+     * rotate to get a line_width/2 vector along the face, note that
+     * the vector must be rotated the right direction in device space,
+     * but by 90° in user space. So, the rotation depends on
+     * whether the ctm reflects or not, and that can be determined
+     * by looking at the determinant of the matrix.
+     */
+    if (! _cairo_matrix_is_identity (stroker->ctm_inverse)) {
+       /* Normalize the matrix! */
+       cairo_matrix_transform_distance (stroker->ctm_inverse,
+                                        &slope_dx, &slope_dy);
+       normalize_slope (&slope_dx, &slope_dy);
+
+       if (stroker->ctm_det_positive)
+       {
+           face_dx = - slope_dy * (stroker->style.line_width / 2.0);
+           face_dy = slope_dx * (stroker->style.line_width / 2.0);
+       }
+       else
+       {
+           face_dx = slope_dy * (stroker->style.line_width / 2.0);
+           face_dy = - slope_dx * (stroker->style.line_width / 2.0);
+       }
+
+       /* back to device space */
+       cairo_matrix_transform_distance (stroker->ctm, &face_dx, &face_dy);
+    } else {
+       face_dx = - slope_dy * (stroker->style.line_width / 2.0);
+       face_dy = slope_dx * (stroker->style.line_width / 2.0);
+    }
+
+    offset_ccw.x = _cairo_fixed_from_double (face_dx);
+    offset_ccw.y = _cairo_fixed_from_double (face_dy);
+    offset_cw.x = -offset_ccw.x;
+    offset_cw.y = -offset_ccw.y;
+
+    face->ccw = *point;
+    translate_point (&face->ccw, &offset_ccw);
+
+    face->point = *point;
+
+    face->cw = *point;
+    translate_point (&face->cw, &offset_cw);
+
+    face->usr_vector.x = slope_dx;
+    face->usr_vector.y = slope_dy;
+
+    face->dev_vector = *dev_slope;
+}
+
+static void
+add_caps (struct stroker *stroker)
+{
+    /* check for a degenerative sub_path */
+    if (stroker->has_initial_sub_path &&
+       ! stroker->has_first_face &&
+       ! stroker->has_current_face &&
+       stroker->style.line_cap == CAIRO_LINE_CAP_ROUND)
+    {
+       /* pick an arbitrary slope to use */
+       cairo_slope_t slope = { CAIRO_FIXED_ONE, 0 };
+       cairo_stroke_face_t face;
+
+       /* arbitrarily choose first_point */
+       compute_face (&stroker->first_point, &slope, stroker, &face);
+
+       add_leading_cap (stroker, &face, &stroker->ccw);
+       add_trailing_cap (stroker, &face, &stroker->ccw);
+
+       /* ensure the circle is complete */
+       _cairo_contour_add_point (&stroker->ccw.contour,
+                                 _cairo_contour_first_point (&stroker->ccw.contour));
+
+       _cairo_polygon_add_contour (stroker->polygon, &stroker->ccw.contour);
+       _cairo_contour_reset (&stroker->ccw.contour);
+    } else {
+       if (stroker->has_current_face)
+           add_trailing_cap (stroker, &stroker->current_face, &stroker->ccw);
+
+#if DEBUG
+       {
+           FILE *file = fopen ("contours.txt", "a");
+           _cairo_debug_print_contour (file, &stroker->path);
+           _cairo_debug_print_contour (file, &stroker->cw.contour);
+           _cairo_debug_print_contour (file, &stroker->ccw.contour);
+           fclose (file);
+           _cairo_contour_reset (&stroker->path);
+       }
+#endif
+
+       _cairo_polygon_add_contour (stroker->polygon, &stroker->ccw.contour);
+       _cairo_contour_reset (&stroker->ccw.contour);
+
+       if (stroker->has_first_face) {
+           _cairo_contour_add_point (&stroker->ccw.contour,
+                                     &stroker->first_face.cw);
+           add_leading_cap (stroker, &stroker->first_face, &stroker->ccw);
+#if DEBUG
+           {
+               FILE *file = fopen ("contours.txt", "a");
+               _cairo_debug_print_contour (file, &stroker->ccw.contour);
+               fclose (file);
+           }
+#endif
+
+           _cairo_polygon_add_contour (stroker->polygon,
+                                       &stroker->ccw.contour);
+           _cairo_contour_reset (&stroker->ccw.contour);
+       }
+
+       _cairo_polygon_add_contour (stroker->polygon, &stroker->cw.contour);
+       _cairo_contour_reset (&stroker->cw.contour);
+    }
+}
+
+static cairo_status_t
+close_path (void *closure);
+
+static cairo_status_t
+move_to (void *closure,
+        const cairo_point_t *point)
+{
+    struct stroker *stroker = closure;
+
+    /* Cap the start and end of the previous sub path as needed */
+    add_caps (stroker);
+
+    stroker->has_first_face = FALSE;
+    stroker->has_current_face = FALSE;
+    stroker->has_initial_sub_path = FALSE;
+
+    stroker->first_point = *point;
+
+#if DEBUG
+    _cairo_contour_add_point (&stroker->path, point);
+#endif
+
+    stroker->current_face.point = *point;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+line_to (void *closure,
+        const cairo_point_t *point)
+{
+    struct stroker *stroker = closure;
+    cairo_stroke_face_t start;
+    cairo_point_t *p1 = &stroker->current_face.point;
+    cairo_slope_t dev_slope;
+    int move_last = 0;
+
+    stroker->has_initial_sub_path = TRUE;
+
+    if (p1->x == point->x && p1->y == point->y)
+       return CAIRO_STATUS_SUCCESS;
+
+#if DEBUG
+    _cairo_contour_add_point (&stroker->path, point);
+#endif
+
+    _cairo_slope_init (&dev_slope, p1, point);
+    compute_face (p1, &dev_slope, stroker, &start);
+
+    if (stroker->has_current_face) {
+       int clockwise = _cairo_slope_compare (&stroker->current_face.dev_vector,
+                                             &start.dev_vector);
+       if (clockwise == 0) {
+           move_last = 1;
+       } else {
+           clockwise = clockwise < 0;
+           /* Join with final face from previous segment */
+           if (! within_tolerance (&stroker->current_face.ccw, &start.ccw,
+                                   stroker->contour_tolerance) ||
+               ! within_tolerance (&stroker->current_face.cw, &start.cw,
+                                   stroker->contour_tolerance))
+           {
+               outer_join (stroker, &stroker->current_face, &start, clockwise);
+               inner_join (stroker, &stroker->current_face, &start, clockwise);
+           }
+       }
+    } else {
+       if (! stroker->has_first_face) {
+           /* Save sub path's first face in case needed for closing join */
+           stroker->first_face = start;
+           stroker->has_first_face = TRUE;
+       }
+       stroker->has_current_face = TRUE;
+
+       contour_add_point (stroker, &stroker->cw, &start.cw);
+       contour_add_point (stroker, &stroker->ccw, &start.ccw);
+    }
+
+    stroker->current_face = start;
+    stroker->current_face.point = *point;
+    stroker->current_face.ccw.x += dev_slope.dx;
+    stroker->current_face.ccw.y += dev_slope.dy;
+    stroker->current_face.cw.x += dev_slope.dx;
+    stroker->current_face.cw.y += dev_slope.dy;
+
+    if (move_last) {
+       *_cairo_contour_last_point (&stroker->cw.contour) = stroker->current_face.cw;
+       *_cairo_contour_last_point (&stroker->ccw.contour) = stroker->current_face.ccw;
+    } else {
+       contour_add_point (stroker, &stroker->cw, &stroker->current_face.cw);
+       contour_add_point (stroker, &stroker->ccw, &stroker->current_face.ccw);
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+spline_to (void *closure,
+          const cairo_point_t *point,
+          const cairo_slope_t *tangent)
+{
+    struct stroker *stroker = closure;
+    cairo_stroke_face_t face;
+
+#if DEBUG
+    _cairo_contour_add_point (&stroker->path, point);
+#endif
+    if (tangent->dx == 0 && tangent->dy == 0) {
+       const cairo_point_t *inpt, *outpt;
+       struct stroke_contour *outer;
+       cairo_point_t t;
+       int clockwise;
+
+       face = stroker->current_face;
+
+       face.usr_vector.x = -face.usr_vector.x;
+       face.usr_vector.y = -face.usr_vector.y;
+       face.dev_vector.dx = -face.dev_vector.dx;
+       face.dev_vector.dy = -face.dev_vector.dy;
+
+       t = face.cw;
+       face.cw = face.ccw;
+       face.ccw = t;
+
+       clockwise = join_is_clockwise (&stroker->current_face, &face);
+       if (clockwise) {
+           inpt = &stroker->current_face.cw;
+           outpt = &face.cw;
+           outer = &stroker->cw;
+       } else {
+           inpt = &stroker->current_face.ccw;
+           outpt = &face.ccw;
+           outer = &stroker->ccw;
+       }
+
+       add_fan (stroker,
+                &stroker->current_face.dev_vector,
+                &face.dev_vector,
+                &stroker->current_face.point, inpt, outpt,
+                clockwise, outer);
+    } else {
+       compute_face (point, tangent, stroker, &face);
+
+       if (face.dev_slope.x * stroker->current_face.dev_slope.x +
+           face.dev_slope.y * stroker->current_face.dev_slope.y < 0)
+       {
+           const cairo_point_t *inpt, *outpt;
+           struct stroke_contour *outer;
+           int clockwise = join_is_clockwise (&stroker->current_face, &face);
+
+           stroker->current_face.cw.x += face.point.x - stroker->current_face.point.x;
+           stroker->current_face.cw.y += face.point.y - stroker->current_face.point.y;
+           contour_add_point (stroker, &stroker->cw, &stroker->current_face.cw);
+
+           stroker->current_face.ccw.x += face.point.x - stroker->current_face.point.x;
+           stroker->current_face.ccw.y += face.point.y - stroker->current_face.point.y;
+           contour_add_point (stroker, &stroker->ccw, &stroker->current_face.ccw);
+
+           if (clockwise) {
+               inpt = &stroker->current_face.cw;
+               outpt = &face.cw;
+               outer = &stroker->cw;
+           } else {
+               inpt = &stroker->current_face.ccw;
+               outpt = &face.ccw;
+               outer = &stroker->ccw;
+           }
+           add_fan (stroker,
+                    &stroker->current_face.dev_vector,
+                    &face.dev_vector,
+                    &stroker->current_face.point, inpt, outpt,
+                    clockwise, outer);
+       }
+
+       contour_add_point (stroker, &stroker->cw, &face.cw);
+       contour_add_point (stroker, &stroker->ccw, &face.ccw);
+    }
+
+    stroker->current_face = face;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+curve_to (void *closure,
+         const cairo_point_t *b,
+         const cairo_point_t *c,
+         const cairo_point_t *d)
+{
+    struct stroker *stroker = closure;
+    cairo_spline_t spline;
+    cairo_stroke_face_t face;
+
+    if (! _cairo_spline_init (&spline, spline_to, stroker,
+                             &stroker->current_face.point, b, c, d))
+       return line_to (closure, d);
+
+    compute_face (&stroker->current_face.point, &spline.initial_slope,
+                 stroker, &face);
+
+    if (stroker->has_current_face) {
+       int clockwise = join_is_clockwise (&stroker->current_face, &face);
+       /* Join with final face from previous segment */
+       outer_join (stroker, &stroker->current_face, &face, clockwise);
+       inner_join (stroker, &stroker->current_face, &face, clockwise);
+    } else {
+       if (! stroker->has_first_face) {
+           /* Save sub path's first face in case needed for closing join */
+           stroker->first_face = face;
+           stroker->has_first_face = TRUE;
+       }
+       stroker->has_current_face = TRUE;
+
+       contour_add_point (stroker, &stroker->cw, &face.cw);
+       contour_add_point (stroker, &stroker->ccw, &face.ccw);
+    }
+    stroker->current_face = face;
+
+    return _cairo_spline_decompose (&spline, stroker->tolerance);
+}
+
+static cairo_status_t
+close_path (void *closure)
+{
+    struct stroker *stroker = closure;
+    cairo_status_t status;
+
+    status = line_to (stroker, &stroker->first_point);
+    if (unlikely (status))
+       return status;
+
+    if (stroker->has_first_face && stroker->has_current_face) {
+       /* Join first and final faces of sub path */
+       outer_close (stroker, &stroker->current_face, &stroker->first_face);
+       inner_close (stroker, &stroker->current_face, &stroker->first_face);
+#if 0
+       *_cairo_contour_first_point (&stroker->ccw.contour) =
+           *_cairo_contour_last_point (&stroker->ccw.contour);
+
+       *_cairo_contour_first_point (&stroker->cw.contour) =
+           *_cairo_contour_last_point (&stroker->cw.contour);
+#endif
+
+       _cairo_polygon_add_contour (stroker->polygon, &stroker->cw.contour);
+       _cairo_polygon_add_contour (stroker->polygon, &stroker->ccw.contour);
+
+#if DEBUG
+       {
+           FILE *file = fopen ("contours.txt", "a");
+           _cairo_debug_print_contour (file, &stroker->path);
+           _cairo_debug_print_contour (file, &stroker->cw.contour);
+           _cairo_debug_print_contour (file, &stroker->ccw.contour);
+           fclose (file);
+
+           _cairo_contour_reset (&stroker->path);
+       }
+#endif
+       _cairo_contour_reset (&stroker->cw.contour);
+       _cairo_contour_reset (&stroker->ccw.contour);
+    } else {
+       /* Cap the start and end of the sub path as needed */
+       add_caps (stroker);
+    }
+
+    stroker->has_initial_sub_path = FALSE;
+    stroker->has_first_face = FALSE;
+    stroker->has_current_face = FALSE;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_status_t
+_cairo_path_fixed_stroke_to_polygon (const cairo_path_fixed_t  *path,
+                                    const cairo_stroke_style_t *style,
+                                    const cairo_matrix_t       *ctm,
+                                    const cairo_matrix_t       *ctm_inverse,
+                                    double              tolerance,
+                                    cairo_polygon_t *polygon)
+{
+    struct stroker stroker;
+    cairo_status_t status;
+
+    if (style->num_dashes) {
+       return _cairo_path_fixed_stroke_dashed_to_polygon (path,
+                                                          style,
+                                                          ctm,
+                                                          ctm_inverse,
+                                                          tolerance,
+                                                          polygon);
+    }
+
+    stroker.style = *style;
+    stroker.ctm = ctm;
+    stroker.ctm_inverse = ctm_inverse;
+    stroker.tolerance = tolerance;
+
+    stroker.ctm_det_positive =
+       _cairo_matrix_compute_determinant (ctm) >= 0.0;
+
+    status = _cairo_pen_init (&stroker.pen,
+                             style->line_width / 2.0,
+                             tolerance, ctm);
+    if (unlikely (status))
+       return status;
+
+    /* If the line width is so small that the pen is reduced to a
+       single point, then we have nothing to do. */
+    if (stroker.pen.num_vertices <= 1)
+       return CAIRO_STATUS_SUCCESS;
+
+    stroker.has_current_face = FALSE;
+    stroker.has_first_face = FALSE;
+    stroker.has_initial_sub_path = FALSE;
+
+#if DEBUG
+    remove ("contours.txt");
+    remove ("polygons.txt");
+    _cairo_contour_init (&stroker.path, 0);
+#endif
+    _cairo_contour_init (&stroker.cw.contour, 1);
+    _cairo_contour_init (&stroker.ccw.contour, -1);
+    tolerance *= CAIRO_FIXED_ONE;
+    tolerance *= tolerance;
+    stroker.contour_tolerance = tolerance;
+    stroker.polygon = polygon;
+
+    status = _cairo_path_fixed_interpret (path,
+                                         move_to,
+                                         line_to,
+                                         curve_to,
+                                         close_path,
+                                         &stroker);
+    /* Cap the start and end of the final sub path as needed */
+    if (likely (status == CAIRO_STATUS_SUCCESS))
+       add_caps (&stroker);
+
+    _cairo_contour_fini (&stroker.cw.contour);
+    _cairo_contour_fini (&stroker.ccw.contour);
+    _cairo_pen_fini (&stroker.pen);
+
+#if DEBUG
+    {
+       FILE *file = fopen ("polygons.txt", "a");
+       _cairo_debug_print_polygon (file, polygon);
+       fclose (file);
+    }
+#endif
+
+    return status;
+}
diff --git a/src/cairo-path-stroke-tristrip.c b/src/cairo-path-stroke-tristrip.c
new file mode 100644 (file)
index 0000000..337d814
--- /dev/null
@@ -0,0 +1,1088 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ * Copyright © 2011 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *     Carl D. Worth <cworth@cworth.org>
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#define _BSD_SOURCE /* for hypot() */
+#include "cairoint.h"
+
+#include "cairo-box-private.h"
+#include "cairo-boxes-private.h"
+#include "cairo-error-private.h"
+#include "cairo-path-fixed-private.h"
+#include "cairo-slope-private.h"
+#include "cairo-tristrip-private.h"
+
+struct stroker {
+    cairo_stroke_style_t style;
+
+    cairo_tristrip_t *strip;
+
+    const cairo_matrix_t *ctm;
+    const cairo_matrix_t *ctm_inverse;
+    double tolerance;
+    cairo_bool_t ctm_det_positive;
+
+    cairo_pen_t pen;
+
+    cairo_bool_t has_sub_path;
+
+    cairo_point_t first_point;
+
+    cairo_bool_t has_current_face;
+    cairo_stroke_face_t current_face;
+
+    cairo_bool_t has_first_face;
+    cairo_stroke_face_t first_face;
+
+    cairo_box_t limit;
+    cairo_bool_t has_limits;
+};
+
+static inline double
+normalize_slope (double *dx, double *dy);
+
+static void
+compute_face (const cairo_point_t *point,
+             const cairo_slope_t *dev_slope,
+             struct stroker *stroker,
+             cairo_stroke_face_t *face);
+
+static void
+translate_point (cairo_point_t *point, const cairo_point_t *offset)
+{
+    point->x += offset->x;
+    point->y += offset->y;
+}
+
+static int
+slope_compare_sgn (double dx1, double dy1, double dx2, double dy2)
+{
+    double  c = (dx1 * dy2 - dx2 * dy1);
+
+    if (c > 0) return 1;
+    if (c < 0) return -1;
+    return 0;
+}
+
+static inline int
+range_step (int i, int step, int max)
+{
+    i += step;
+    if (i < 0)
+       i = max - 1;
+    if (i >= max)
+       i = 0;
+    return i;
+}
+
+/*
+ * Construct a fan around the midpoint using the vertices from pen between
+ * inpt and outpt.
+ */
+static void
+add_fan (struct stroker *stroker,
+        const cairo_slope_t *in_vector,
+        const cairo_slope_t *out_vector,
+        const cairo_point_t *midpt,
+        const cairo_point_t *inpt,
+        const cairo_point_t *outpt,
+        cairo_bool_t clockwise)
+{
+    int start, stop, step, i, npoints;
+
+    if (clockwise) {
+       step  = 1;
+
+       start = _cairo_pen_find_active_cw_vertex_index (&stroker->pen,
+                                                       in_vector);
+       if (_cairo_slope_compare (&stroker->pen.vertices[start].slope_cw,
+                                 in_vector) < 0)
+           start = range_step (start, 1, stroker->pen.num_vertices);
+
+       stop  = _cairo_pen_find_active_cw_vertex_index (&stroker->pen,
+                                                       out_vector);
+       if (_cairo_slope_compare (&stroker->pen.vertices[stop].slope_ccw,
+                                 out_vector) > 0)
+       {
+           stop = range_step (stop, -1, stroker->pen.num_vertices);
+           if (_cairo_slope_compare (&stroker->pen.vertices[stop].slope_cw,
+                                     in_vector) < 0)
+               return;
+       }
+
+       npoints = stop - start;
+    } else {
+       step  = -1;
+
+       start = _cairo_pen_find_active_ccw_vertex_index (&stroker->pen,
+                                                        in_vector);
+       if (_cairo_slope_compare (&stroker->pen.vertices[start].slope_ccw,
+                                 in_vector) < 0)
+           start = range_step (start, -1, stroker->pen.num_vertices);
+
+       stop  = _cairo_pen_find_active_ccw_vertex_index (&stroker->pen,
+                                                        out_vector);
+       if (_cairo_slope_compare (&stroker->pen.vertices[stop].slope_cw,
+                                 out_vector) > 0)
+       {
+           stop = range_step (stop, 1, stroker->pen.num_vertices);
+           if (_cairo_slope_compare (&stroker->pen.vertices[stop].slope_ccw,
+                                     in_vector) < 0)
+               return;
+       }
+
+       npoints = start - stop;
+    }
+    stop = range_step (stop, step, stroker->pen.num_vertices);
+    if (npoints < 0)
+       npoints += stroker->pen.num_vertices;
+    if (npoints <= 1)
+       return;
+
+    for (i = start;
+        i != stop;
+       i = range_step (i, step, stroker->pen.num_vertices))
+    {
+       cairo_point_t p = *midpt;
+       translate_point (&p, &stroker->pen.vertices[i].point);
+       //contour_add_point (stroker, c, &p);
+    }
+}
+
+static int
+join_is_clockwise (const cairo_stroke_face_t *in,
+                  const cairo_stroke_face_t *out)
+{
+    return _cairo_slope_compare (&in->dev_vector, &out->dev_vector) < 0;
+}
+
+static void
+inner_join (struct stroker *stroker,
+           const cairo_stroke_face_t *in,
+           const cairo_stroke_face_t *out,
+           int clockwise)
+{
+    const cairo_point_t *outpt;
+
+    if (clockwise) {
+       outpt = &out->ccw;
+    } else {
+       outpt = &out->cw;
+    }
+    //contour_add_point (stroker, inner, &in->point);
+    //contour_add_point (stroker, inner, outpt);
+}
+
+static void
+inner_close (struct stroker *stroker,
+            const cairo_stroke_face_t *in,
+            cairo_stroke_face_t *out)
+{
+    const cairo_point_t *inpt;
+
+    if (join_is_clockwise (in, out)) {
+       inpt = &out->ccw;
+    } else {
+       inpt = &out->cw;
+    }
+
+    //contour_add_point (stroker, inner, &in->point);
+    //contour_add_point (stroker, inner, inpt);
+    //*_cairo_contour_first_point (&inner->contour) =
+       //*_cairo_contour_last_point (&inner->contour);
+}
+
+static void
+outer_close (struct stroker *stroker,
+            const cairo_stroke_face_t *in,
+            const cairo_stroke_face_t *out)
+{
+    const cairo_point_t        *inpt, *outpt;
+    int        clockwise;
+
+    if (in->cw.x == out->cw.x && in->cw.y == out->cw.y &&
+       in->ccw.x == out->ccw.x && out->ccw.y == out->ccw.y)
+    {
+       return;
+    }
+    clockwise = join_is_clockwise (in, out);
+    if (clockwise) {
+       inpt = &in->cw;
+       outpt = &out->cw;
+    } else {
+       inpt = &in->ccw;
+       outpt = &out->ccw;
+    }
+
+    switch (stroker->style.line_join) {
+    case CAIRO_LINE_JOIN_ROUND:
+       /* construct a fan around the common midpoint */
+       add_fan (stroker,
+                &in->dev_vector,
+                &out->dev_vector,
+                &in->point, inpt, outpt,
+                clockwise);
+       break;
+
+    case CAIRO_LINE_JOIN_MITER:
+    default: {
+       /* dot product of incoming slope vector with outgoing slope vector */
+       double  in_dot_out = -in->usr_vector.x * out->usr_vector.x +
+                            -in->usr_vector.y * out->usr_vector.y;
+       double  ml = stroker->style.miter_limit;
+
+       /* Check the miter limit -- lines meeting at an acute angle
+        * can generate long miters, the limit converts them to bevel
+        *
+        * Consider the miter join formed when two line segments
+        * meet at an angle psi:
+        *
+        *         /.\
+        *        /. .\
+        *       /./ \.\
+        *      /./psi\.\
+        *
+        * We can zoom in on the right half of that to see:
+        *
+        *          |\
+        *          | \ psi/2
+        *          |  \
+        *          |   \
+        *          |    \
+        *          |     \
+        *        miter    \
+        *       length     \
+        *          |        \
+        *          |        .\
+        *          |    .     \
+        *          |.   line   \
+        *           \    width  \
+        *            \           \
+        *
+        *
+        * The right triangle in that figure, (the line-width side is
+        * shown faintly with three '.' characters), gives us the
+        * following expression relating miter length, angle and line
+        * width:
+        *
+        *      1 /sin (psi/2) = miter_length / line_width
+        *
+        * The right-hand side of this relationship is the same ratio
+        * in which the miter limit (ml) is expressed. We want to know
+        * when the miter length is within the miter limit. That is
+        * when the following condition holds:
+        *
+        *      1/sin(psi/2) <= ml
+        *      1 <= ml sin(psi/2)
+        *      1 <= ml² sin²(psi/2)
+        *      2 <= ml² 2 sin²(psi/2)
+        *                              2·sin²(psi/2) = 1-cos(psi)
+        *      2 <= ml² (1-cos(psi))
+        *
+        *                              in · out = |in| |out| cos (psi)
+        *
+        * in and out are both unit vectors, so:
+        *
+        *                              in · out = cos (psi)
+        *
+        *      2 <= ml² (1 - in · out)
+        *
+        */
+       if (2 <= ml * ml * (1 - in_dot_out)) {
+           double              x1, y1, x2, y2;
+           double              mx, my;
+           double              dx1, dx2, dy1, dy2;
+           double              ix, iy;
+           double              fdx1, fdy1, fdx2, fdy2;
+           double              mdx, mdy;
+
+           /*
+            * we've got the points already transformed to device
+            * space, but need to do some computation with them and
+            * also need to transform the slope from user space to
+            * device space
+            */
+           /* outer point of incoming line face */
+           x1 = _cairo_fixed_to_double (inpt->x);
+           y1 = _cairo_fixed_to_double (inpt->y);
+           dx1 = in->usr_vector.x;
+           dy1 = in->usr_vector.y;
+           cairo_matrix_transform_distance (stroker->ctm, &dx1, &dy1);
+
+           /* outer point of outgoing line face */
+           x2 = _cairo_fixed_to_double (outpt->x);
+           y2 = _cairo_fixed_to_double (outpt->y);
+           dx2 = out->usr_vector.x;
+           dy2 = out->usr_vector.y;
+           cairo_matrix_transform_distance (stroker->ctm, &dx2, &dy2);
+
+           /*
+            * Compute the location of the outer corner of the miter.
+            * That's pretty easy -- just the intersection of the two
+            * outer edges.  We've got slopes and points on each
+            * of those edges.  Compute my directly, then compute
+            * mx by using the edge with the larger dy; that avoids
+            * dividing by values close to zero.
+            */
+           my = (((x2 - x1) * dy1 * dy2 - y2 * dx2 * dy1 + y1 * dx1 * dy2) /
+                 (dx1 * dy2 - dx2 * dy1));
+           if (fabs (dy1) >= fabs (dy2))
+               mx = (my - y1) * dx1 / dy1 + x1;
+           else
+               mx = (my - y2) * dx2 / dy2 + x2;
+
+           /*
+            * When the two outer edges are nearly parallel, slight
+            * perturbations in the position of the outer points of the lines
+            * caused by representing them in fixed point form can cause the
+            * intersection point of the miter to move a large amount. If
+            * that moves the miter intersection from between the two faces,
+            * then draw a bevel instead.
+            */
+
+           ix = _cairo_fixed_to_double (in->point.x);
+           iy = _cairo_fixed_to_double (in->point.y);
+
+           /* slope of one face */
+           fdx1 = x1 - ix; fdy1 = y1 - iy;
+
+           /* slope of the other face */
+           fdx2 = x2 - ix; fdy2 = y2 - iy;
+
+           /* slope from the intersection to the miter point */
+           mdx = mx - ix; mdy = my - iy;
+
+           /*
+            * Make sure the miter point line lies between the two
+            * faces by comparing the slopes
+            */
+           if (slope_compare_sgn (fdx1, fdy1, mdx, mdy) !=
+               slope_compare_sgn (fdx2, fdy2, mdx, mdy))
+           {
+               cairo_point_t p;
+
+               p.x = _cairo_fixed_from_double (mx);
+               p.y = _cairo_fixed_from_double (my);
+
+               //*_cairo_contour_last_point (&outer->contour) = p;
+               //*_cairo_contour_first_point (&outer->contour) = p;
+               return;
+           }
+       }
+       break;
+    }
+
+    case CAIRO_LINE_JOIN_BEVEL:
+       break;
+    }
+    //contour_add_point (stroker, outer, outpt);
+}
+
+static void
+outer_join (struct stroker *stroker,
+           const cairo_stroke_face_t *in,
+           const cairo_stroke_face_t *out,
+           int clockwise)
+{
+    const cairo_point_t        *inpt, *outpt;
+
+    if (in->cw.x == out->cw.x && in->cw.y == out->cw.y &&
+       in->ccw.x == out->ccw.x && out->ccw.y == out->ccw.y)
+    {
+       return;
+    }
+    if (clockwise) {
+       inpt = &in->cw;
+       outpt = &out->cw;
+    } else {
+       inpt = &in->ccw;
+       outpt = &out->ccw;
+    }
+
+    switch (stroker->style.line_join) {
+    case CAIRO_LINE_JOIN_ROUND:
+       /* construct a fan around the common midpoint */
+       add_fan (stroker,
+                &in->dev_vector,
+                &out->dev_vector,
+                &in->point, inpt, outpt,
+                clockwise);
+       break;
+
+    case CAIRO_LINE_JOIN_MITER:
+    default: {
+       /* dot product of incoming slope vector with outgoing slope vector */
+       double  in_dot_out = -in->usr_vector.x * out->usr_vector.x +
+                            -in->usr_vector.y * out->usr_vector.y;
+       double  ml = stroker->style.miter_limit;
+
+       /* Check the miter limit -- lines meeting at an acute angle
+        * can generate long miters, the limit converts them to bevel
+        *
+        * Consider the miter join formed when two line segments
+        * meet at an angle psi:
+        *
+        *         /.\
+        *        /. .\
+        *       /./ \.\
+        *      /./psi\.\
+        *
+        * We can zoom in on the right half of that to see:
+        *
+        *          |\
+        *          | \ psi/2
+        *          |  \
+        *          |   \
+        *          |    \
+        *          |     \
+        *        miter    \
+        *       length     \
+        *          |        \
+        *          |        .\
+        *          |    .     \
+        *          |.   line   \
+        *           \    width  \
+        *            \           \
+        *
+        *
+        * The right triangle in that figure, (the line-width side is
+        * shown faintly with three '.' characters), gives us the
+        * following expression relating miter length, angle and line
+        * width:
+        *
+        *      1 /sin (psi/2) = miter_length / line_width
+        *
+        * The right-hand side of this relationship is the same ratio
+        * in which the miter limit (ml) is expressed. We want to know
+        * when the miter length is within the miter limit. That is
+        * when the following condition holds:
+        *
+        *      1/sin(psi/2) <= ml
+        *      1 <= ml sin(psi/2)
+        *      1 <= ml² sin²(psi/2)
+        *      2 <= ml² 2 sin²(psi/2)
+        *                              2·sin²(psi/2) = 1-cos(psi)
+        *      2 <= ml² (1-cos(psi))
+        *
+        *                              in · out = |in| |out| cos (psi)
+        *
+        * in and out are both unit vectors, so:
+        *
+        *                              in · out = cos (psi)
+        *
+        *      2 <= ml² (1 - in · out)
+        *
+        */
+       if (2 <= ml * ml * (1 - in_dot_out)) {
+           double              x1, y1, x2, y2;
+           double              mx, my;
+           double              dx1, dx2, dy1, dy2;
+           double              ix, iy;
+           double              fdx1, fdy1, fdx2, fdy2;
+           double              mdx, mdy;
+
+           /*
+            * we've got the points already transformed to device
+            * space, but need to do some computation with them and
+            * also need to transform the slope from user space to
+            * device space
+            */
+           /* outer point of incoming line face */
+           x1 = _cairo_fixed_to_double (inpt->x);
+           y1 = _cairo_fixed_to_double (inpt->y);
+           dx1 = in->usr_vector.x;
+           dy1 = in->usr_vector.y;
+           cairo_matrix_transform_distance (stroker->ctm, &dx1, &dy1);
+
+           /* outer point of outgoing line face */
+           x2 = _cairo_fixed_to_double (outpt->x);
+           y2 = _cairo_fixed_to_double (outpt->y);
+           dx2 = out->usr_vector.x;
+           dy2 = out->usr_vector.y;
+           cairo_matrix_transform_distance (stroker->ctm, &dx2, &dy2);
+
+           /*
+            * Compute the location of the outer corner of the miter.
+            * That's pretty easy -- just the intersection of the two
+            * outer edges.  We've got slopes and points on each
+            * of those edges.  Compute my directly, then compute
+            * mx by using the edge with the larger dy; that avoids
+            * dividing by values close to zero.
+            */
+           my = (((x2 - x1) * dy1 * dy2 - y2 * dx2 * dy1 + y1 * dx1 * dy2) /
+                 (dx1 * dy2 - dx2 * dy1));
+           if (fabs (dy1) >= fabs (dy2))
+               mx = (my - y1) * dx1 / dy1 + x1;
+           else
+               mx = (my - y2) * dx2 / dy2 + x2;
+
+           /*
+            * When the two outer edges are nearly parallel, slight
+            * perturbations in the position of the outer points of the lines
+            * caused by representing them in fixed point form can cause the
+            * intersection point of the miter to move a large amount. If
+            * that moves the miter intersection from between the two faces,
+            * then draw a bevel instead.
+            */
+
+           ix = _cairo_fixed_to_double (in->point.x);
+           iy = _cairo_fixed_to_double (in->point.y);
+
+           /* slope of one face */
+           fdx1 = x1 - ix; fdy1 = y1 - iy;
+
+           /* slope of the other face */
+           fdx2 = x2 - ix; fdy2 = y2 - iy;
+
+           /* slope from the intersection to the miter point */
+           mdx = mx - ix; mdy = my - iy;
+
+           /*
+            * Make sure the miter point line lies between the two
+            * faces by comparing the slopes
+            */
+           if (slope_compare_sgn (fdx1, fdy1, mdx, mdy) !=
+               slope_compare_sgn (fdx2, fdy2, mdx, mdy))
+           {
+               cairo_point_t p;
+
+               p.x = _cairo_fixed_from_double (mx);
+               p.y = _cairo_fixed_from_double (my);
+
+               //*_cairo_contour_last_point (&outer->contour) = p;
+               return;
+           }
+       }
+       break;
+    }
+
+    case CAIRO_LINE_JOIN_BEVEL:
+       break;
+    }
+    //contour_add_point (stroker,outer, outpt);
+}
+
+static void
+add_cap (struct stroker *stroker,
+        const cairo_stroke_face_t *f)
+{
+    switch (stroker->style.line_cap) {
+    case CAIRO_LINE_CAP_ROUND: {
+       cairo_slope_t slope;
+
+       slope.dx = -f->dev_vector.dx;
+       slope.dy = -f->dev_vector.dy;
+
+       add_fan (stroker, &f->dev_vector, &slope,
+                &f->point, &f->ccw, &f->cw,
+                FALSE);
+       break;
+    }
+
+    case CAIRO_LINE_CAP_SQUARE: {
+       double dx, dy;
+       cairo_slope_t   fvector;
+       cairo_point_t   quad[4];
+
+       dx = f->usr_vector.x;
+       dy = f->usr_vector.y;
+       dx *= stroker->style.line_width / 2.0;
+       dy *= stroker->style.line_width / 2.0;
+       cairo_matrix_transform_distance (stroker->ctm, &dx, &dy);
+       fvector.dx = _cairo_fixed_from_double (dx);
+       fvector.dy = _cairo_fixed_from_double (dy);
+
+       quad[0] = f->ccw;
+       quad[1].x = f->ccw.x + fvector.dx;
+       quad[1].y = f->ccw.y + fvector.dy;
+       quad[2].x = f->cw.x + fvector.dx;
+       quad[2].y = f->cw.y + fvector.dy;
+       quad[3] = f->cw;
+
+       //contour_add_point (stroker, c, &quad[1]);
+       //contour_add_point (stroker, c, &quad[2]);
+    }
+
+    case CAIRO_LINE_CAP_BUTT:
+    default:
+       break;
+    }
+    //contour_add_point (stroker, c, &f->cw);
+}
+
+static void
+add_leading_cap (struct stroker *stroker,
+                const cairo_stroke_face_t *face)
+{
+    cairo_stroke_face_t reversed;
+    cairo_point_t t;
+
+    reversed = *face;
+
+    /* The initial cap needs an outward facing vector. Reverse everything */
+    reversed.usr_vector.x = -reversed.usr_vector.x;
+    reversed.usr_vector.y = -reversed.usr_vector.y;
+    reversed.dev_vector.dx = -reversed.dev_vector.dx;
+    reversed.dev_vector.dy = -reversed.dev_vector.dy;
+
+    t = reversed.cw;
+    reversed.cw = reversed.ccw;
+    reversed.ccw = t;
+
+    add_cap (stroker, &reversed);
+}
+
+static void
+add_trailing_cap (struct stroker *stroker,
+                 const cairo_stroke_face_t *face)
+{
+    add_cap (stroker, face);
+}
+
+static inline double
+normalize_slope (double *dx, double *dy)
+{
+    double dx0 = *dx, dy0 = *dy;
+    double mag;
+
+    assert (dx0 != 0.0 || dy0 != 0.0);
+
+    if (dx0 == 0.0) {
+       *dx = 0.0;
+       if (dy0 > 0.0) {
+           mag = dy0;
+           *dy = 1.0;
+       } else {
+           mag = -dy0;
+           *dy = -1.0;
+       }
+    } else if (dy0 == 0.0) {
+       *dy = 0.0;
+       if (dx0 > 0.0) {
+           mag = dx0;
+           *dx = 1.0;
+       } else {
+           mag = -dx0;
+           *dx = -1.0;
+       }
+    } else {
+       mag = hypot (dx0, dy0);
+       *dx = dx0 / mag;
+       *dy = dy0 / mag;
+    }
+
+    return mag;
+}
+
+static void
+compute_face (const cairo_point_t *point,
+             const cairo_slope_t *dev_slope,
+             struct stroker *stroker,
+             cairo_stroke_face_t *face)
+{
+    double face_dx, face_dy;
+    cairo_point_t offset_ccw, offset_cw;
+    double slope_dx, slope_dy;
+
+    slope_dx = _cairo_fixed_to_double (dev_slope->dx);
+    slope_dy = _cairo_fixed_to_double (dev_slope->dy);
+    face->length = normalize_slope (&slope_dx, &slope_dy);
+    face->dev_slope.x = slope_dx;
+    face->dev_slope.y = slope_dy;
+
+    /*
+     * rotate to get a line_width/2 vector along the face, note that
+     * the vector must be rotated the right direction in device space,
+     * but by 90° in user space. So, the rotation depends on
+     * whether the ctm reflects or not, and that can be determined
+     * by looking at the determinant of the matrix.
+     */
+    if (! _cairo_matrix_is_identity (stroker->ctm_inverse)) {
+       /* Normalize the matrix! */
+       cairo_matrix_transform_distance (stroker->ctm_inverse,
+                                        &slope_dx, &slope_dy);
+       normalize_slope (&slope_dx, &slope_dy);
+
+       if (stroker->ctm_det_positive) {
+           face_dx = - slope_dy * (stroker->style.line_width / 2.0);
+           face_dy = slope_dx * (stroker->style.line_width / 2.0);
+       } else {
+           face_dx = slope_dy * (stroker->style.line_width / 2.0);
+           face_dy = - slope_dx * (stroker->style.line_width / 2.0);
+       }
+
+       /* back to device space */
+       cairo_matrix_transform_distance (stroker->ctm, &face_dx, &face_dy);
+    } else {
+       face_dx = - slope_dy * (stroker->style.line_width / 2.0);
+       face_dy = slope_dx * (stroker->style.line_width / 2.0);
+    }
+
+    offset_ccw.x = _cairo_fixed_from_double (face_dx);
+    offset_ccw.y = _cairo_fixed_from_double (face_dy);
+    offset_cw.x = -offset_ccw.x;
+    offset_cw.y = -offset_ccw.y;
+
+    face->ccw = *point;
+    translate_point (&face->ccw, &offset_ccw);
+
+    face->point = *point;
+
+    face->cw = *point;
+    translate_point (&face->cw, &offset_cw);
+
+    face->usr_vector.x = slope_dx;
+    face->usr_vector.y = slope_dy;
+
+    face->dev_vector = *dev_slope;
+}
+
+static void
+add_caps (struct stroker *stroker)
+{
+    /* check for a degenerative sub_path */
+    if (stroker->has_sub_path &&
+       ! stroker->has_first_face &&
+       ! stroker->has_current_face &&
+       stroker->style.line_cap == CAIRO_LINE_CAP_ROUND)
+    {
+       /* pick an arbitrary slope to use */
+       cairo_slope_t slope = { CAIRO_FIXED_ONE, 0 };
+       cairo_stroke_face_t face;
+
+       /* arbitrarily choose first_point */
+       compute_face (&stroker->first_point, &slope, stroker, &face);
+
+       add_leading_cap (stroker, &face);
+       add_trailing_cap (stroker, &face);
+
+       /* ensure the circle is complete */
+       //_cairo_contour_add_point (&stroker->ccw.contour,
+                                 //_cairo_contour_first_point (&stroker->ccw.contour));
+    } else {
+       if (stroker->has_current_face)
+           add_trailing_cap (stroker, &stroker->current_face);
+
+       //_cairo_polygon_add_contour (stroker->polygon, &stroker->ccw.contour);
+       //_cairo_contour_reset (&stroker->ccw.contour);
+
+       if (stroker->has_first_face) {
+           //_cairo_contour_add_point (&stroker->ccw.contour,
+                                     //&stroker->first_face.cw);
+           add_leading_cap (stroker, &stroker->first_face);
+           //_cairo_polygon_add_contour (stroker->polygon,
+                                       //&stroker->ccw.contour);
+           //_cairo_contour_reset (&stroker->ccw.contour);
+       }
+    }
+}
+
+static cairo_status_t
+move_to (void *closure,
+        const cairo_point_t *point)
+{
+    struct stroker *stroker = closure;
+
+    /* Cap the start and end of the previous sub path as needed */
+    add_caps (stroker);
+
+    stroker->has_first_face = FALSE;
+    stroker->has_current_face = FALSE;
+    stroker->has_sub_path = FALSE;
+
+    stroker->first_point = *point;
+
+    stroker->current_face.point = *point;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+line_to (void *closure,
+        const cairo_point_t *point)
+{
+    struct stroker *stroker = closure;
+    cairo_stroke_face_t start;
+    cairo_point_t *p1 = &stroker->current_face.point;
+    cairo_slope_t dev_slope;
+
+    stroker->has_sub_path = TRUE;
+
+    if (p1->x == point->x && p1->y == point->y)
+       return CAIRO_STATUS_SUCCESS;
+
+    _cairo_slope_init (&dev_slope, p1, point);
+    compute_face (p1, &dev_slope, stroker, &start);
+
+    if (stroker->has_current_face) {
+       int clockwise = join_is_clockwise (&stroker->current_face, &start);
+       /* Join with final face from previous segment */
+       outer_join (stroker, &stroker->current_face, &start, clockwise);
+       inner_join (stroker, &stroker->current_face, &start, clockwise);
+    } else {
+       if (! stroker->has_first_face) {
+           /* Save sub path's first face in case needed for closing join */
+           stroker->first_face = start;
+           _cairo_tristrip_move_to (stroker->strip, &start.cw);
+           stroker->has_first_face = TRUE;
+       }
+       stroker->has_current_face = TRUE;
+
+       _cairo_tristrip_add_point (stroker->strip, &start.cw);
+       _cairo_tristrip_add_point (stroker->strip, &start.ccw);
+    }
+
+    stroker->current_face = start;
+    stroker->current_face.point = *point;
+    stroker->current_face.ccw.x += dev_slope.dx;
+    stroker->current_face.ccw.y += dev_slope.dy;
+    stroker->current_face.cw.x += dev_slope.dx;
+    stroker->current_face.cw.y += dev_slope.dy;
+
+    _cairo_tristrip_add_point (stroker->strip, &stroker->current_face.cw);
+    _cairo_tristrip_add_point (stroker->strip, &stroker->current_face.ccw);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+spline_to (void *closure,
+          const cairo_point_t *point,
+          const cairo_slope_t *tangent)
+{
+    struct stroker *stroker = closure;
+    cairo_stroke_face_t face;
+
+    if (tangent->dx == 0 && tangent->dy == 0) {
+       const cairo_point_t *inpt, *outpt;
+       cairo_point_t t;
+       int clockwise;
+
+       face = stroker->current_face;
+
+       face.usr_vector.x = -face.usr_vector.x;
+       face.usr_vector.y = -face.usr_vector.y;
+       face.dev_vector.dx = -face.dev_vector.dx;
+       face.dev_vector.dy = -face.dev_vector.dy;
+
+       t = face.cw;
+       face.cw = face.ccw;
+       face.ccw = t;
+
+       clockwise = join_is_clockwise (&stroker->current_face, &face);
+       if (clockwise) {
+           inpt = &stroker->current_face.cw;
+           outpt = &face.cw;
+       } else {
+           inpt = &stroker->current_face.ccw;
+           outpt = &face.ccw;
+       }
+
+       add_fan (stroker,
+                &stroker->current_face.dev_vector,
+                &face.dev_vector,
+                &stroker->current_face.point, inpt, outpt,
+                clockwise);
+    } else {
+       compute_face (point, tangent, stroker, &face);
+
+       if (face.dev_slope.x * stroker->current_face.dev_slope.x +
+           face.dev_slope.y * stroker->current_face.dev_slope.y < 0)
+       {
+           const cairo_point_t *inpt, *outpt;
+           int clockwise = join_is_clockwise (&stroker->current_face, &face);
+
+           stroker->current_face.cw.x += face.point.x - stroker->current_face.point.x;
+           stroker->current_face.cw.y += face.point.y - stroker->current_face.point.y;
+           //contour_add_point (stroker, &stroker->cw, &stroker->current_face.cw);
+
+           stroker->current_face.ccw.x += face.point.x - stroker->current_face.point.x;
+           stroker->current_face.ccw.y += face.point.y - stroker->current_face.point.y;
+           //contour_add_point (stroker, &stroker->ccw, &stroker->current_face.ccw);
+
+           if (clockwise) {
+               inpt = &stroker->current_face.cw;
+               outpt = &face.cw;
+           } else {
+               inpt = &stroker->current_face.ccw;
+               outpt = &face.ccw;
+           }
+           add_fan (stroker,
+                    &stroker->current_face.dev_vector,
+                    &face.dev_vector,
+                    &stroker->current_face.point, inpt, outpt,
+                    clockwise);
+       }
+
+       _cairo_tristrip_add_point (stroker->strip, &face.cw);
+       _cairo_tristrip_add_point (stroker->strip, &face.ccw);
+    }
+
+    stroker->current_face = face;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+curve_to (void *closure,
+         const cairo_point_t *b,
+         const cairo_point_t *c,
+         const cairo_point_t *d)
+{
+    struct stroker *stroker = closure;
+    cairo_spline_t spline;
+    cairo_stroke_face_t face;
+
+    if (stroker->has_limits) {
+       if (! _cairo_spline_intersects (&stroker->current_face.point, b, c, d,
+                                       &stroker->limit))
+           return line_to (closure, d);
+    }
+
+    if (! _cairo_spline_init (&spline, spline_to, stroker,
+                             &stroker->current_face.point, b, c, d))
+       return line_to (closure, d);
+
+    compute_face (&stroker->current_face.point, &spline.initial_slope,
+                 stroker, &face);
+
+    if (stroker->has_current_face) {
+       int clockwise = join_is_clockwise (&stroker->current_face, &face);
+       /* Join with final face from previous segment */
+       outer_join (stroker, &stroker->current_face, &face, clockwise);
+       inner_join (stroker, &stroker->current_face, &face, clockwise);
+    } else {
+       if (! stroker->has_first_face) {
+           /* Save sub path's first face in case needed for closing join */
+           stroker->first_face = face;
+           _cairo_tristrip_move_to (stroker->strip, &face.cw);
+           stroker->has_first_face = TRUE;
+       }
+       stroker->has_current_face = TRUE;
+
+       _cairo_tristrip_add_point (stroker->strip, &face.cw);
+       _cairo_tristrip_add_point (stroker->strip, &face.ccw);
+    }
+    stroker->current_face = face;
+
+    return _cairo_spline_decompose (&spline, stroker->tolerance);
+}
+
+static cairo_status_t
+close_path (void *closure)
+{
+    struct stroker *stroker = closure;
+    cairo_status_t status;
+
+    status = line_to (stroker, &stroker->first_point);
+    if (unlikely (status))
+       return status;
+
+    if (stroker->has_first_face && stroker->has_current_face) {
+       /* Join first and final faces of sub path */
+       outer_close (stroker, &stroker->current_face, &stroker->first_face);
+       inner_close (stroker, &stroker->current_face, &stroker->first_face);
+    } else {
+       /* Cap the start and end of the sub path as needed */
+       add_caps (stroker);
+    }
+
+    stroker->has_sub_path = FALSE;
+    stroker->has_first_face = FALSE;
+    stroker->has_current_face = FALSE;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_int_status_t
+_cairo_path_fixed_stroke_to_tristrip (const cairo_path_fixed_t *path,
+                                     const cairo_stroke_style_t*style,
+                                     const cairo_matrix_t      *ctm,
+                                     const cairo_matrix_t      *ctm_inverse,
+                                     double                     tolerance,
+                                     cairo_tristrip_t           *strip)
+{
+    struct stroker stroker;
+    cairo_int_status_t status;
+    int i;
+
+    if (style->num_dashes)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    stroker.style = *style;
+    stroker.ctm = ctm;
+    stroker.ctm_inverse = ctm_inverse;
+    stroker.tolerance = tolerance;
+
+    stroker.ctm_det_positive =
+       _cairo_matrix_compute_determinant (ctm) >= 0.0;
+
+    status = _cairo_pen_init (&stroker.pen,
+                             style->line_width / 2.0,
+                             tolerance, ctm);
+    if (unlikely (status))
+       return status;
+
+    if (stroker.pen.num_vertices <= 1)
+       return CAIRO_INT_STATUS_NOTHING_TO_DO;
+
+    stroker.has_current_face = FALSE;
+    stroker.has_first_face = FALSE;
+    stroker.has_sub_path = FALSE;
+
+    stroker.has_limits = strip->num_limits > 0;
+    stroker.limit = strip->limits[0];
+    for (i = 1; i < strip->num_limits; i++)
+       _cairo_box_add_box (&stroker.limit, &strip->limits[i]);
+
+    stroker.strip = strip;
+
+    status = _cairo_path_fixed_interpret (path,
+                                         move_to,
+                                         line_to,
+                                         curve_to,
+                                         close_path,
+                                         &stroker);
+    /* Cap the start and end of the final sub path as needed */
+    if (likely (status == CAIRO_INT_STATUS_SUCCESS))
+       add_caps (&stroker);
+
+    _cairo_pen_fini (&stroker.pen);
+
+    return status;
+}
diff --git a/src/cairo-path-stroke.c b/src/cairo-path-stroke.c
new file mode 100644 (file)
index 0000000..f25cfe4
--- /dev/null
@@ -0,0 +1,1354 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *     Carl D. Worth <cworth@cworth.org>
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#define _BSD_SOURCE /* for hypot() */
+#include "cairoint.h"
+
+#include "cairo-box-private.h"
+#include "cairo-boxes-private.h"
+#include "cairo-error-private.h"
+#include "cairo-path-fixed-private.h"
+#include "cairo-slope-private.h"
+#include "cairo-stroke-dash-private.h"
+#include "cairo-traps-private.h"
+
+typedef struct cairo_stroker {
+    cairo_stroke_style_t style;
+
+    const cairo_matrix_t *ctm;
+    const cairo_matrix_t *ctm_inverse;
+    double tolerance;
+    double ctm_determinant;
+    cairo_bool_t ctm_det_positive;
+
+    void *closure;
+    cairo_status_t (*add_external_edge) (void *closure,
+                                        const cairo_point_t *p1,
+                                        const cairo_point_t *p2);
+    cairo_status_t (*add_triangle) (void *closure,
+                                   const cairo_point_t triangle[3]);
+    cairo_status_t (*add_triangle_fan) (void *closure,
+                                       const cairo_point_t *midpt,
+                                       const cairo_point_t *points,
+                                       int npoints);
+    cairo_status_t (*add_convex_quad) (void *closure,
+                                      const cairo_point_t quad[4]);
+
+    cairo_pen_t          pen;
+
+    cairo_point_t current_point;
+    cairo_point_t first_point;
+
+    cairo_bool_t has_initial_sub_path;
+
+    cairo_bool_t has_current_face;
+    cairo_stroke_face_t current_face;
+
+    cairo_bool_t has_first_face;
+    cairo_stroke_face_t first_face;
+
+    cairo_stroker_dash_t dash;
+
+    cairo_bool_t has_bounds;
+    cairo_box_t bounds;
+} cairo_stroker_t;
+
+static cairo_status_t
+_cairo_stroker_init (cairo_stroker_t           *stroker,
+                    const cairo_stroke_style_t *stroke_style,
+                    const cairo_matrix_t       *ctm,
+                    const cairo_matrix_t       *ctm_inverse,
+                    double                      tolerance)
+{
+    cairo_status_t status;
+
+    stroker->style = *stroke_style;
+    stroker->ctm = ctm;
+    stroker->ctm_inverse = ctm_inverse;
+    stroker->tolerance = tolerance;
+
+    stroker->ctm_determinant = _cairo_matrix_compute_determinant (stroker->ctm);
+    stroker->ctm_det_positive = stroker->ctm_determinant >= 0.0;
+
+    status = _cairo_pen_init (&stroker->pen,
+                             stroke_style->line_width / 2.0,
+                             tolerance, ctm);
+    if (unlikely (status))
+       return status;
+
+    stroker->has_bounds = FALSE;
+
+    stroker->has_current_face = FALSE;
+    stroker->has_first_face = FALSE;
+    stroker->has_initial_sub_path = FALSE;
+
+    _cairo_stroker_dash_init (&stroker->dash, stroke_style);
+
+    stroker->add_external_edge = NULL;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_stroker_limit (cairo_stroker_t *stroker,
+                     const cairo_path_fixed_t *path,
+                     const cairo_box_t *boxes,
+                     int num_boxes)
+{
+    double dx, dy;
+    cairo_fixed_t fdx, fdy;
+
+    stroker->has_bounds = TRUE;
+    _cairo_boxes_get_extents (boxes, num_boxes, &stroker->bounds);
+
+    /* Extend the bounds in each direction to account for the maximum area
+     * we might generate trapezoids, to capture line segments that are outside
+     * of the bounds but which might generate rendering that's within bounds.
+     */
+
+    _cairo_stroke_style_max_distance_from_path (&stroker->style, path,
+                                               stroker->ctm, &dx, &dy);
+
+    fdx = _cairo_fixed_from_double (dx);
+    fdy = _cairo_fixed_from_double (dy);
+
+    stroker->bounds.p1.x -= fdx;
+    stroker->bounds.p2.x += fdx;
+
+    stroker->bounds.p1.y -= fdy;
+    stroker->bounds.p2.y += fdy;
+}
+
+static void
+_cairo_stroker_fini (cairo_stroker_t *stroker)
+{
+    _cairo_pen_fini (&stroker->pen);
+}
+
+static void
+_translate_point (cairo_point_t *point, const cairo_point_t *offset)
+{
+    point->x += offset->x;
+    point->y += offset->y;
+}
+
+static int
+_cairo_stroker_join_is_clockwise (const cairo_stroke_face_t *in,
+                                 const cairo_stroke_face_t *out)
+{
+    cairo_slope_t in_slope, out_slope;
+
+    _cairo_slope_init (&in_slope, &in->point, &in->cw);
+    _cairo_slope_init (&out_slope, &out->point, &out->cw);
+
+    return _cairo_slope_compare (&in_slope, &out_slope) < 0;
+}
+
+/**
+ * _cairo_slope_compare_sgn
+ *
+ * Return -1, 0 or 1 depending on the relative slopes of
+ * two lines.
+ */
+static int
+_cairo_slope_compare_sgn (double dx1, double dy1, double dx2, double dy2)
+{
+    double  c = (dx1 * dy2 - dx2 * dy1);
+
+    if (c > 0) return 1;
+    if (c < 0) return -1;
+    return 0;
+}
+
+static inline int
+_range_step (int i, int step, int max)
+{
+    i += step;
+    if (i < 0)
+       i = max - 1;
+    if (i >= max)
+       i = 0;
+    return i;
+}
+
+/*
+ * Construct a fan around the midpoint using the vertices from pen between
+ * inpt and outpt.
+ */
+static cairo_status_t
+_tessellate_fan (cairo_stroker_t *stroker,
+                const cairo_slope_t *in_vector,
+                const cairo_slope_t *out_vector,
+                const cairo_point_t *midpt,
+                const cairo_point_t *inpt,
+                const cairo_point_t *outpt,
+                cairo_bool_t clockwise)
+{
+    cairo_point_t stack_points[64], *points = stack_points;
+    int start, stop, step, i, npoints;
+    cairo_status_t status;
+
+    if (clockwise) {
+       step  = -1;
+
+       start = _cairo_pen_find_active_ccw_vertex_index (&stroker->pen,
+                                                        in_vector);
+       if (_cairo_slope_compare (&stroker->pen.vertices[start].slope_ccw,
+                                 in_vector) < 0)
+           start = _range_step (start, -1, stroker->pen.num_vertices);
+
+       stop  = _cairo_pen_find_active_ccw_vertex_index (&stroker->pen,
+                                                        out_vector);
+       if (_cairo_slope_compare (&stroker->pen.vertices[stop].slope_cw,
+                                 out_vector) > 0)
+       {
+           stop = _range_step (stop, 1, stroker->pen.num_vertices);
+           if (_cairo_slope_compare (&stroker->pen.vertices[stop].slope_ccw,
+                                     in_vector) < 0)
+           {
+               goto BEVEL;
+           }
+       }
+
+       npoints = start - stop;
+    } else {
+       step  = 1;
+
+       start = _cairo_pen_find_active_cw_vertex_index (&stroker->pen,
+                                                       in_vector);
+       if (_cairo_slope_compare (&stroker->pen.vertices[start].slope_cw,
+                                 in_vector) < 0)
+           start = _range_step (start, 1, stroker->pen.num_vertices);
+
+       stop  = _cairo_pen_find_active_cw_vertex_index (&stroker->pen,
+                                                       out_vector);
+       if (_cairo_slope_compare (&stroker->pen.vertices[stop].slope_ccw,
+                                 out_vector) > 0)
+       {
+           stop = _range_step (stop, -1, stroker->pen.num_vertices);
+           if (_cairo_slope_compare (&stroker->pen.vertices[stop].slope_cw,
+                                     in_vector) < 0)
+           {
+               goto BEVEL;
+           }
+       }
+
+       npoints = stop - start;
+    }
+    stop = _range_step (stop, step, stroker->pen.num_vertices);
+
+    if (npoints < 0)
+       npoints += stroker->pen.num_vertices;
+    npoints += 3;
+
+    if (npoints <= 1)
+       goto BEVEL;
+
+    if (npoints > ARRAY_LENGTH (stack_points)) {
+       points = _cairo_malloc_ab (npoints, sizeof (cairo_point_t));
+       if (unlikely (points == NULL))
+           return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+    }
+
+
+    /* Construct the fan. */
+    npoints = 0;
+    points[npoints++] = *inpt;
+    for (i = start;
+        i != stop;
+       i = _range_step (i, step, stroker->pen.num_vertices))
+    {
+       points[npoints] = *midpt;
+       _translate_point (&points[npoints], &stroker->pen.vertices[i].point);
+       npoints++;
+    }
+    points[npoints++] = *outpt;
+
+    if (stroker->add_external_edge != NULL) {
+       for (i = 0; i < npoints - 1; i++) {
+           if (clockwise) {
+               status = stroker->add_external_edge (stroker->closure,
+                                                    &points[i], &points[i+1]);
+           } else {
+               status = stroker->add_external_edge (stroker->closure,
+                                                    &points[i+1], &points[i]);
+           }
+           if (unlikely (status))
+               break;
+       }
+    } else {
+       status = stroker->add_triangle_fan (stroker->closure,
+                                           midpt, points, npoints);
+    }
+
+    if (points != stack_points)
+       free (points);
+
+    return status;
+
+BEVEL:
+    /* Ensure a leak free connection... */
+    if (stroker->add_external_edge != NULL) {
+       if (clockwise)
+           return stroker->add_external_edge (stroker->closure, inpt, outpt);
+       else
+           return stroker->add_external_edge (stroker->closure, outpt, inpt);
+    } else {
+       stack_points[0] = *midpt;
+       stack_points[1] = *inpt;
+       stack_points[2] = *outpt;
+       return stroker->add_triangle (stroker->closure, stack_points);
+    }
+}
+
+static cairo_status_t
+_cairo_stroker_join (cairo_stroker_t *stroker,
+                    const cairo_stroke_face_t *in,
+                    const cairo_stroke_face_t *out)
+{
+    int         clockwise = _cairo_stroker_join_is_clockwise (out, in);
+    const cairo_point_t        *inpt, *outpt;
+    cairo_point_t points[4];
+    cairo_status_t status;
+
+    if (in->cw.x  == out->cw.x  && in->cw.y  == out->cw.y &&
+       in->ccw.x == out->ccw.x && in->ccw.y == out->ccw.y)
+    {
+       return CAIRO_STATUS_SUCCESS;
+    }
+
+    if (clockwise) {
+       if (stroker->add_external_edge != NULL) {
+           status = stroker->add_external_edge (stroker->closure,
+                                                &out->cw, &in->point);
+           if (unlikely (status))
+               return status;
+
+           status = stroker->add_external_edge (stroker->closure,
+                                                &in->point, &in->cw);
+           if (unlikely (status))
+               return status;
+       }
+
+       inpt = &in->ccw;
+       outpt = &out->ccw;
+    } else {
+       if (stroker->add_external_edge != NULL) {
+           status = stroker->add_external_edge (stroker->closure,
+                                                &in->ccw, &in->point);
+           if (unlikely (status))
+               return status;
+
+           status = stroker->add_external_edge (stroker->closure,
+                                                &in->point, &out->ccw);
+           if (unlikely (status))
+               return status;
+       }
+
+       inpt = &in->cw;
+       outpt = &out->cw;
+    }
+
+    switch (stroker->style.line_join) {
+    case CAIRO_LINE_JOIN_ROUND:
+       /* construct a fan around the common midpoint */
+       return _tessellate_fan (stroker,
+                               &in->dev_vector,
+                               &out->dev_vector,
+                               &in->point, inpt, outpt,
+                               clockwise);
+
+    case CAIRO_LINE_JOIN_MITER:
+    default: {
+       /* dot product of incoming slope vector with outgoing slope vector */
+       double  in_dot_out = -in->usr_vector.x * out->usr_vector.x +
+                            -in->usr_vector.y * out->usr_vector.y;
+       double  ml = stroker->style.miter_limit;
+
+       /* Check the miter limit -- lines meeting at an acute angle
+        * can generate long miters, the limit converts them to bevel
+        *
+        * Consider the miter join formed when two line segments
+        * meet at an angle psi:
+        *
+        *         /.\
+        *        /. .\
+        *       /./ \.\
+        *      /./psi\.\
+        *
+        * We can zoom in on the right half of that to see:
+        *
+        *          |\
+        *          | \ psi/2
+        *          |  \
+        *          |   \
+        *          |    \
+        *          |     \
+        *        miter    \
+        *       length     \
+        *          |        \
+        *          |        .\
+        *          |    .     \
+        *          |.   line   \
+        *           \    width  \
+        *            \           \
+        *
+        *
+        * The right triangle in that figure, (the line-width side is
+        * shown faintly with three '.' characters), gives us the
+        * following expression relating miter length, angle and line
+        * width:
+        *
+        *      1 /sin (psi/2) = miter_length / line_width
+        *
+        * The right-hand side of this relationship is the same ratio
+        * in which the miter limit (ml) is expressed. We want to know
+        * when the miter length is within the miter limit. That is
+        * when the following condition holds:
+        *
+        *      1/sin(psi/2) <= ml
+        *      1 <= ml sin(psi/2)
+        *      1 <= ml² sin²(psi/2)
+        *      2 <= ml² 2 sin²(psi/2)
+        *                              2·sin²(psi/2) = 1-cos(psi)
+        *      2 <= ml² (1-cos(psi))
+        *
+        *                              in · out = |in| |out| cos (psi)
+        *
+        * in and out are both unit vectors, so:
+        *
+        *                              in · out = cos (psi)
+        *
+        *      2 <= ml² (1 - in · out)
+        *
+        */
+       if (2 <= ml * ml * (1 - in_dot_out)) {
+           double              x1, y1, x2, y2;
+           double              mx, my;
+           double              dx1, dx2, dy1, dy2;
+           double              ix, iy;
+           double              fdx1, fdy1, fdx2, fdy2;
+           double              mdx, mdy;
+
+           /*
+            * we've got the points already transformed to device
+            * space, but need to do some computation with them and
+            * also need to transform the slope from user space to
+            * device space
+            */
+           /* outer point of incoming line face */
+           x1 = _cairo_fixed_to_double (inpt->x);
+           y1 = _cairo_fixed_to_double (inpt->y);
+           dx1 = in->usr_vector.x;
+           dy1 = in->usr_vector.y;
+           cairo_matrix_transform_distance (stroker->ctm, &dx1, &dy1);
+
+           /* outer point of outgoing line face */
+           x2 = _cairo_fixed_to_double (outpt->x);
+           y2 = _cairo_fixed_to_double (outpt->y);
+           dx2 = out->usr_vector.x;
+           dy2 = out->usr_vector.y;
+           cairo_matrix_transform_distance (stroker->ctm, &dx2, &dy2);
+
+           /*
+            * Compute the location of the outer corner of the miter.
+            * That's pretty easy -- just the intersection of the two
+            * outer edges.  We've got slopes and points on each
+            * of those edges.  Compute my directly, then compute
+            * mx by using the edge with the larger dy; that avoids
+            * dividing by values close to zero.
+            */
+           my = (((x2 - x1) * dy1 * dy2 - y2 * dx2 * dy1 + y1 * dx1 * dy2) /
+                 (dx1 * dy2 - dx2 * dy1));
+           if (fabs (dy1) >= fabs (dy2))
+               mx = (my - y1) * dx1 / dy1 + x1;
+           else
+               mx = (my - y2) * dx2 / dy2 + x2;
+
+           /*
+            * When the two outer edges are nearly parallel, slight
+            * perturbations in the position of the outer points of the lines
+            * caused by representing them in fixed point form can cause the
+            * intersection point of the miter to move a large amount. If
+            * that moves the miter intersection from between the two faces,
+            * then draw a bevel instead.
+            */
+
+           ix = _cairo_fixed_to_double (in->point.x);
+           iy = _cairo_fixed_to_double (in->point.y);
+
+           /* slope of one face */
+           fdx1 = x1 - ix; fdy1 = y1 - iy;
+
+           /* slope of the other face */
+           fdx2 = x2 - ix; fdy2 = y2 - iy;
+
+           /* slope from the intersection to the miter point */
+           mdx = mx - ix; mdy = my - iy;
+
+           /*
+            * Make sure the miter point line lies between the two
+            * faces by comparing the slopes
+            */
+           if (_cairo_slope_compare_sgn (fdx1, fdy1, mdx, mdy) !=
+               _cairo_slope_compare_sgn (fdx2, fdy2, mdx, mdy))
+           {
+               if (stroker->add_external_edge != NULL) {
+                   points[0].x = _cairo_fixed_from_double (mx);
+                   points[0].y = _cairo_fixed_from_double (my);
+
+                   if (clockwise) {
+                       status = stroker->add_external_edge (stroker->closure,
+                                                            inpt, &points[0]);
+                       if (unlikely (status))
+                           return status;
+
+                       status = stroker->add_external_edge (stroker->closure,
+                                                            &points[0], outpt);
+                       if (unlikely (status))
+                           return status;
+                   } else {
+                       status = stroker->add_external_edge (stroker->closure,
+                                                            outpt, &points[0]);
+                       if (unlikely (status))
+                           return status;
+
+                       status = stroker->add_external_edge (stroker->closure,
+                                                            &points[0], inpt);
+                       if (unlikely (status))
+                           return status;
+                   }
+
+                   return CAIRO_STATUS_SUCCESS;
+               } else {
+                   points[0] = in->point;
+                   points[1] = *inpt;
+                   points[2].x = _cairo_fixed_from_double (mx);
+                   points[2].y = _cairo_fixed_from_double (my);
+                   points[3] = *outpt;
+
+                   return stroker->add_convex_quad (stroker->closure, points);
+               }
+           }
+       }
+    }
+
+    /* fall through ... */
+
+    case CAIRO_LINE_JOIN_BEVEL:
+       if (stroker->add_external_edge != NULL) {
+           if (clockwise) {
+               return stroker->add_external_edge (stroker->closure,
+                                                  inpt, outpt);
+           } else {
+               return stroker->add_external_edge (stroker->closure,
+                                                  outpt, inpt);
+           }
+       } else {
+           points[0] = in->point;
+           points[1] = *inpt;
+           points[2] = *outpt;
+
+           return stroker->add_triangle (stroker->closure, points);
+       }
+    }
+}
+
+static cairo_status_t
+_cairo_stroker_add_cap (cairo_stroker_t *stroker,
+                       const cairo_stroke_face_t *f)
+{
+    switch (stroker->style.line_cap) {
+    case CAIRO_LINE_CAP_ROUND: {
+       cairo_slope_t slope;
+
+       slope.dx = -f->dev_vector.dx;
+       slope.dy = -f->dev_vector.dy;
+
+       return _tessellate_fan (stroker,
+                               &f->dev_vector,
+                               &slope,
+                               &f->point, &f->cw, &f->ccw,
+                               FALSE);
+
+    }
+
+    case CAIRO_LINE_CAP_SQUARE: {
+       double dx, dy;
+       cairo_slope_t   fvector;
+       cairo_point_t   quad[4];
+
+       dx = f->usr_vector.x;
+       dy = f->usr_vector.y;
+       dx *= stroker->style.line_width / 2.0;
+       dy *= stroker->style.line_width / 2.0;
+       cairo_matrix_transform_distance (stroker->ctm, &dx, &dy);
+       fvector.dx = _cairo_fixed_from_double (dx);
+       fvector.dy = _cairo_fixed_from_double (dy);
+
+       quad[0] = f->ccw;
+       quad[1].x = f->ccw.x + fvector.dx;
+       quad[1].y = f->ccw.y + fvector.dy;
+       quad[2].x = f->cw.x + fvector.dx;
+       quad[2].y = f->cw.y + fvector.dy;
+       quad[3] = f->cw;
+
+       if (stroker->add_external_edge != NULL) {
+           cairo_status_t status;
+
+           status = stroker->add_external_edge (stroker->closure,
+                                                &quad[0], &quad[1]);
+           if (unlikely (status))
+               return status;
+
+           status = stroker->add_external_edge (stroker->closure,
+                                                &quad[1], &quad[2]);
+           if (unlikely (status))
+               return status;
+
+           status = stroker->add_external_edge (stroker->closure,
+                                                &quad[2], &quad[3]);
+           if (unlikely (status))
+               return status;
+
+           return CAIRO_STATUS_SUCCESS;
+       } else {
+           return stroker->add_convex_quad (stroker->closure, quad);
+       }
+    }
+
+    case CAIRO_LINE_CAP_BUTT:
+    default:
+       if (stroker->add_external_edge != NULL) {
+           return stroker->add_external_edge (stroker->closure,
+                                              &f->ccw, &f->cw);
+       } else {
+           return CAIRO_STATUS_SUCCESS;
+       }
+    }
+}
+
+static cairo_status_t
+_cairo_stroker_add_leading_cap (cairo_stroker_t     *stroker,
+                               const cairo_stroke_face_t *face)
+{
+    cairo_stroke_face_t reversed;
+    cairo_point_t t;
+
+    reversed = *face;
+
+    /* The initial cap needs an outward facing vector. Reverse everything */
+    reversed.usr_vector.x = -reversed.usr_vector.x;
+    reversed.usr_vector.y = -reversed.usr_vector.y;
+    reversed.dev_vector.dx = -reversed.dev_vector.dx;
+    reversed.dev_vector.dy = -reversed.dev_vector.dy;
+    t = reversed.cw;
+    reversed.cw = reversed.ccw;
+    reversed.ccw = t;
+
+    return _cairo_stroker_add_cap (stroker, &reversed);
+}
+
+static cairo_status_t
+_cairo_stroker_add_trailing_cap (cairo_stroker_t     *stroker,
+                                const cairo_stroke_face_t *face)
+{
+    return _cairo_stroker_add_cap (stroker, face);
+}
+
+static inline cairo_bool_t
+_compute_normalized_device_slope (double *dx, double *dy,
+                                 const cairo_matrix_t *ctm_inverse,
+                                 double *mag_out)
+{
+    double dx0 = *dx, dy0 = *dy;
+    double mag;
+
+    cairo_matrix_transform_distance (ctm_inverse, &dx0, &dy0);
+
+    if (dx0 == 0.0 && dy0 == 0.0) {
+       if (mag_out)
+           *mag_out = 0.0;
+       return FALSE;
+    }
+
+    if (dx0 == 0.0) {
+       *dx = 0.0;
+       if (dy0 > 0.0) {
+           mag = dy0;
+           *dy = 1.0;
+       } else {
+           mag = -dy0;
+           *dy = -1.0;
+       }
+    } else if (dy0 == 0.0) {
+       *dy = 0.0;
+       if (dx0 > 0.0) {
+           mag = dx0;
+           *dx = 1.0;
+       } else {
+           mag = -dx0;
+           *dx = -1.0;
+       }
+    } else {
+       mag = hypot (dx0, dy0);
+       *dx = dx0 / mag;
+       *dy = dy0 / mag;
+    }
+
+    if (mag_out)
+       *mag_out = mag;
+
+    return TRUE;
+}
+
+static void
+_compute_face (const cairo_point_t *point, cairo_slope_t *dev_slope,
+              double slope_dx, double slope_dy,
+              cairo_stroker_t *stroker, cairo_stroke_face_t *face)
+{
+    double face_dx, face_dy;
+    cairo_point_t offset_ccw, offset_cw;
+
+    /*
+     * rotate to get a line_width/2 vector along the face, note that
+     * the vector must be rotated the right direction in device space,
+     * but by 90° in user space. So, the rotation depends on
+     * whether the ctm reflects or not, and that can be determined
+     * by looking at the determinant of the matrix.
+     */
+    if (stroker->ctm_det_positive)
+    {
+       face_dx = - slope_dy * (stroker->style.line_width / 2.0);
+       face_dy = slope_dx * (stroker->style.line_width / 2.0);
+    }
+    else
+    {
+       face_dx = slope_dy * (stroker->style.line_width / 2.0);
+       face_dy = - slope_dx * (stroker->style.line_width / 2.0);
+    }
+
+    /* back to device space */
+    cairo_matrix_transform_distance (stroker->ctm, &face_dx, &face_dy);
+
+    offset_ccw.x = _cairo_fixed_from_double (face_dx);
+    offset_ccw.y = _cairo_fixed_from_double (face_dy);
+    offset_cw.x = -offset_ccw.x;
+    offset_cw.y = -offset_ccw.y;
+
+    face->ccw = *point;
+    _translate_point (&face->ccw, &offset_ccw);
+
+    face->point = *point;
+
+    face->cw = *point;
+    _translate_point (&face->cw, &offset_cw);
+
+    face->usr_vector.x = slope_dx;
+    face->usr_vector.y = slope_dy;
+
+    face->dev_vector = *dev_slope;
+}
+
+static cairo_status_t
+_cairo_stroker_add_caps (cairo_stroker_t *stroker)
+{
+    cairo_status_t status;
+
+    /* check for a degenerative sub_path */
+    if (stroker->has_initial_sub_path
+       && ! stroker->has_first_face
+       && ! stroker->has_current_face
+       && stroker->style.line_cap == CAIRO_LINE_CAP_ROUND)
+    {
+       /* pick an arbitrary slope to use */
+       double dx = 1.0, dy = 0.0;
+       cairo_slope_t slope = { CAIRO_FIXED_ONE, 0 };
+       cairo_stroke_face_t face;
+
+       _compute_normalized_device_slope (&dx, &dy,
+                                         stroker->ctm_inverse, NULL);
+
+       /* arbitrarily choose first_point
+        * first_point and current_point should be the same */
+       _compute_face (&stroker->first_point, &slope, dx, dy, stroker, &face);
+
+       status = _cairo_stroker_add_leading_cap (stroker, &face);
+       if (unlikely (status))
+           return status;
+
+       status = _cairo_stroker_add_trailing_cap (stroker, &face);
+       if (unlikely (status))
+           return status;
+    }
+
+    if (stroker->has_first_face) {
+       status = _cairo_stroker_add_leading_cap (stroker,
+                                                &stroker->first_face);
+       if (unlikely (status))
+           return status;
+    }
+
+    if (stroker->has_current_face) {
+       status = _cairo_stroker_add_trailing_cap (stroker,
+                                                 &stroker->current_face);
+       if (unlikely (status))
+           return status;
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_stroker_add_sub_edge (cairo_stroker_t *stroker,
+                            const cairo_point_t *p1,
+                            const cairo_point_t *p2,
+                            cairo_slope_t *dev_slope,
+                            double slope_dx, double slope_dy,
+                            cairo_stroke_face_t *start,
+                            cairo_stroke_face_t *end)
+{
+    _compute_face (p1, dev_slope, slope_dx, slope_dy, stroker, start);
+    *end = *start;
+
+    if (p1->x == p2->x && p1->y == p2->y)
+       return CAIRO_STATUS_SUCCESS;
+
+    end->point = *p2;
+    end->ccw.x += p2->x - p1->x;
+    end->ccw.y += p2->y - p1->y;
+    end->cw.x += p2->x - p1->x;
+    end->cw.y += p2->y - p1->y;
+
+    if (stroker->add_external_edge != NULL) {
+       cairo_status_t status;
+
+       status = stroker->add_external_edge (stroker->closure,
+                                            &end->cw, &start->cw);
+       if (unlikely (status))
+           return status;
+
+       status = stroker->add_external_edge (stroker->closure,
+                                            &start->ccw, &end->ccw);
+       if (unlikely (status))
+           return status;
+
+       return CAIRO_STATUS_SUCCESS;
+    } else {
+       cairo_point_t quad[4];
+
+       quad[0] = start->cw;
+       quad[1] = end->cw;
+       quad[2] = end->ccw;
+       quad[3] = start->ccw;
+
+       return stroker->add_convex_quad (stroker->closure, quad);
+    }
+}
+
+static cairo_status_t
+_cairo_stroker_move_to (void *closure,
+                       const cairo_point_t *point)
+{
+    cairo_stroker_t *stroker = closure;
+    cairo_status_t status;
+
+    /* reset the dash pattern for new sub paths */
+    _cairo_stroker_dash_start (&stroker->dash);
+
+    /* Cap the start and end of the previous sub path as needed */
+    status = _cairo_stroker_add_caps (stroker);
+    if (unlikely (status))
+       return status;
+
+    stroker->first_point = *point;
+    stroker->current_point = *point;
+
+    stroker->has_first_face = FALSE;
+    stroker->has_current_face = FALSE;
+    stroker->has_initial_sub_path = FALSE;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_stroker_line_to (void *closure,
+                       const cairo_point_t *point)
+{
+    cairo_stroker_t *stroker = closure;
+    cairo_stroke_face_t start, end;
+    cairo_point_t *p1 = &stroker->current_point;
+    cairo_slope_t dev_slope;
+    double slope_dx, slope_dy;
+    cairo_status_t status;
+
+    stroker->has_initial_sub_path = TRUE;
+
+    if (p1->x == point->x && p1->y == point->y)
+       return CAIRO_STATUS_SUCCESS;
+
+    _cairo_slope_init (&dev_slope, p1, point);
+    slope_dx = _cairo_fixed_to_double (point->x - p1->x);
+    slope_dy = _cairo_fixed_to_double (point->y - p1->y);
+    _compute_normalized_device_slope (&slope_dx, &slope_dy,
+                                     stroker->ctm_inverse, NULL);
+
+    status = _cairo_stroker_add_sub_edge (stroker,
+                                         p1, point,
+                                         &dev_slope,
+                                         slope_dx, slope_dy,
+                                         &start, &end);
+    if (unlikely (status))
+       return status;
+
+    if (stroker->has_current_face) {
+       /* Join with final face from previous segment */
+       status = _cairo_stroker_join (stroker,
+                                     &stroker->current_face,
+                                     &start);
+       if (unlikely (status))
+           return status;
+    } else if (! stroker->has_first_face) {
+       /* Save sub path's first face in case needed for closing join */
+       stroker->first_face = start;
+       stroker->has_first_face = TRUE;
+    }
+    stroker->current_face = end;
+    stroker->has_current_face = TRUE;
+
+    stroker->current_point = *point;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+/*
+ * Dashed lines.  Cap each dash end, join around turns when on
+ */
+static cairo_status_t
+_cairo_stroker_line_to_dashed (void *closure,
+                              const cairo_point_t *p2)
+{
+    cairo_stroker_t *stroker = closure;
+    double mag, remain, step_length = 0;
+    double slope_dx, slope_dy;
+    double dx2, dy2;
+    cairo_stroke_face_t sub_start, sub_end;
+    cairo_point_t *p1 = &stroker->current_point;
+    cairo_slope_t dev_slope;
+    cairo_line_t segment;
+    cairo_bool_t fully_in_bounds;
+    cairo_status_t status;
+
+    stroker->has_initial_sub_path = stroker->dash.dash_starts_on;
+
+    if (p1->x == p2->x && p1->y == p2->y)
+       return CAIRO_STATUS_SUCCESS;
+
+    fully_in_bounds = TRUE;
+    if (stroker->has_bounds &&
+       (! _cairo_box_contains_point (&stroker->bounds, p1) ||
+        ! _cairo_box_contains_point (&stroker->bounds, p2)))
+    {
+       fully_in_bounds = FALSE;
+    }
+
+    _cairo_slope_init (&dev_slope, p1, p2);
+
+    slope_dx = _cairo_fixed_to_double (p2->x - p1->x);
+    slope_dy = _cairo_fixed_to_double (p2->y - p1->y);
+
+    if (! _compute_normalized_device_slope (&slope_dx, &slope_dy,
+                                           stroker->ctm_inverse, &mag))
+    {
+       return CAIRO_STATUS_SUCCESS;
+    }
+
+    remain = mag;
+    segment.p1 = *p1;
+    while (remain) {
+       step_length = MIN (stroker->dash.dash_remain, remain);
+       remain -= step_length;
+       dx2 = slope_dx * (mag - remain);
+       dy2 = slope_dy * (mag - remain);
+       cairo_matrix_transform_distance (stroker->ctm, &dx2, &dy2);
+       segment.p2.x = _cairo_fixed_from_double (dx2) + p1->x;
+       segment.p2.y = _cairo_fixed_from_double (dy2) + p1->y;
+
+       if (stroker->dash.dash_on &&
+           (fully_in_bounds ||
+            (! stroker->has_first_face && stroker->dash.dash_starts_on) ||
+            _cairo_box_intersects_line_segment (&stroker->bounds, &segment)))
+       {
+           status = _cairo_stroker_add_sub_edge (stroker,
+                                                 &segment.p1, &segment.p2,
+                                                 &dev_slope,
+                                                 slope_dx, slope_dy,
+                                                 &sub_start, &sub_end);
+           if (unlikely (status))
+               return status;
+
+           if (stroker->has_current_face)
+           {
+               /* Join with final face from previous segment */
+               status = _cairo_stroker_join (stroker,
+                                             &stroker->current_face,
+                                             &sub_start);
+               if (unlikely (status))
+                   return status;
+
+               stroker->has_current_face = FALSE;
+           }
+           else if (! stroker->has_first_face &&
+                      stroker->dash.dash_starts_on)
+           {
+               /* Save sub path's first face in case needed for closing join */
+               stroker->first_face = sub_start;
+               stroker->has_first_face = TRUE;
+           }
+           else
+           {
+               /* Cap dash start if not connecting to a previous segment */
+               status = _cairo_stroker_add_leading_cap (stroker, &sub_start);
+               if (unlikely (status))
+                   return status;
+           }
+
+           if (remain) {
+               /* Cap dash end if not at end of segment */
+               status = _cairo_stroker_add_trailing_cap (stroker, &sub_end);
+               if (unlikely (status))
+                   return status;
+           } else {
+               stroker->current_face = sub_end;
+               stroker->has_current_face = TRUE;
+           }
+       } else {
+           if (stroker->has_current_face) {
+               /* Cap final face from previous segment */
+               status = _cairo_stroker_add_trailing_cap (stroker,
+                                                         &stroker->current_face);
+               if (unlikely (status))
+                   return status;
+
+               stroker->has_current_face = FALSE;
+           }
+       }
+
+       _cairo_stroker_dash_step (&stroker->dash, step_length);
+       segment.p1 = segment.p2;
+    }
+
+    if (stroker->dash.dash_on && ! stroker->has_current_face) {
+       /* This segment ends on a transition to dash_on, compute a new face
+        * and add cap for the beginning of the next dash_on step.
+        *
+        * Note: this will create a degenerate cap if this is not the last line
+        * in the path. Whether this behaviour is desirable or not is debatable.
+        * On one side these degenerate caps can not be reproduced with regular
+        * path stroking.
+        * On the other hand, Acroread 7 also produces the degenerate caps.
+        */
+       _compute_face (p2, &dev_slope,
+                      slope_dx, slope_dy,
+                      stroker,
+                      &stroker->current_face);
+
+       status = _cairo_stroker_add_leading_cap (stroker,
+                                                &stroker->current_face);
+       if (unlikely (status))
+           return status;
+
+       stroker->has_current_face = TRUE;
+    }
+
+    stroker->current_point = *p2;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_stroker_curve_to (void *closure,
+                        const cairo_point_t *b,
+                        const cairo_point_t *c,
+                        const cairo_point_t *d)
+{
+    cairo_stroker_t *stroker = closure;
+    cairo_spline_t spline;
+    cairo_line_join_t line_join_save;
+    cairo_stroke_face_t face;
+    double slope_dx, slope_dy;
+    cairo_path_fixed_line_to_func_t *line_to;
+    cairo_status_t status = CAIRO_STATUS_SUCCESS;
+
+    line_to = stroker->dash.dashed ?
+       _cairo_stroker_line_to_dashed :
+       _cairo_stroker_line_to;
+
+    if (! _cairo_spline_init (&spline,
+                             (cairo_spline_add_point_func_t)line_to, stroker,
+                             &stroker->current_point, b, c, d))
+    {
+       return line_to (closure, d);
+    }
+
+    /* If the line width is so small that the pen is reduced to a
+       single point, then we have nothing to do. */
+    if (stroker->pen.num_vertices <= 1)
+       return CAIRO_STATUS_SUCCESS;
+
+    /* Compute the initial face */
+    if (! stroker->dash.dashed || stroker->dash.dash_on) {
+       slope_dx = _cairo_fixed_to_double (spline.initial_slope.dx);
+       slope_dy = _cairo_fixed_to_double (spline.initial_slope.dy);
+       if (_compute_normalized_device_slope (&slope_dx, &slope_dy,
+                                             stroker->ctm_inverse, NULL))
+       {
+           _compute_face (&stroker->current_point,
+                          &spline.initial_slope,
+                          slope_dx, slope_dy,
+                          stroker, &face);
+       }
+       if (stroker->has_current_face) {
+           status = _cairo_stroker_join (stroker,
+                                         &stroker->current_face, &face);
+           if (unlikely (status))
+               return status;
+       } else if (! stroker->has_first_face) {
+           stroker->first_face = face;
+           stroker->has_first_face = TRUE;
+       }
+
+       stroker->current_face = face;
+       stroker->has_current_face = TRUE;
+    }
+
+    /* Temporarily modify the stroker to use round joins to guarantee
+     * smooth stroked curves. */
+    line_join_save = stroker->style.line_join;
+    stroker->style.line_join = CAIRO_LINE_JOIN_ROUND;
+
+    status = _cairo_spline_decompose (&spline, stroker->tolerance);
+    if (unlikely (status))
+       return status;
+
+    /* And join the final face */
+    if (! stroker->dash.dashed || stroker->dash.dash_on) {
+       slope_dx = _cairo_fixed_to_double (spline.final_slope.dx);
+       slope_dy = _cairo_fixed_to_double (spline.final_slope.dy);
+       if (_compute_normalized_device_slope (&slope_dx, &slope_dy,
+                                             stroker->ctm_inverse, NULL))
+       {
+           _compute_face (&stroker->current_point,
+                          &spline.final_slope,
+                          slope_dx, slope_dy,
+                          stroker, &face);
+       }
+
+       status = _cairo_stroker_join (stroker, &stroker->current_face, &face);
+       if (unlikely (status))
+           return status;
+
+       stroker->current_face = face;
+    }
+
+    stroker->style.line_join = line_join_save;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_stroker_close_path (void *closure)
+{
+    cairo_stroker_t *stroker = closure;
+    cairo_status_t status;
+
+    if (stroker->dash.dashed)
+       status = _cairo_stroker_line_to_dashed (stroker, &stroker->first_point);
+    else
+       status = _cairo_stroker_line_to (stroker, &stroker->first_point);
+    if (unlikely (status))
+       return status;
+
+    if (stroker->has_first_face && stroker->has_current_face) {
+       /* Join first and final faces of sub path */
+       status = _cairo_stroker_join (stroker,
+                                     &stroker->current_face,
+                                     &stroker->first_face);
+       if (unlikely (status))
+           return status;
+    } else {
+       /* Cap the start and end of the sub path as needed */
+       status = _cairo_stroker_add_caps (stroker);
+       if (unlikely (status))
+           return status;
+    }
+
+    stroker->has_initial_sub_path = FALSE;
+    stroker->has_first_face = FALSE;
+    stroker->has_current_face = FALSE;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_status_t
+_cairo_path_fixed_stroke_to_shaper (cairo_path_fixed_t *path,
+                                   const cairo_stroke_style_t  *stroke_style,
+                                   const cairo_matrix_t        *ctm,
+                                   const cairo_matrix_t        *ctm_inverse,
+                                   double               tolerance,
+                                   cairo_status_t (*add_triangle) (void *closure,
+                                                                   const cairo_point_t triangle[3]),
+                                   cairo_status_t (*add_triangle_fan) (void *closure,
+                                                                       const cairo_point_t *midpt,
+                                                                       const cairo_point_t *points,
+                                                                       int npoints),
+                                   cairo_status_t (*add_convex_quad) (void *closure,
+                                                                      const cairo_point_t quad[4]),
+                                   void *closure)
+{
+    cairo_stroker_t stroker;
+    cairo_status_t status;
+
+    status = _cairo_stroker_init (&stroker, stroke_style,
+                                 ctm, ctm_inverse, tolerance);
+    if (unlikely (status))
+       return status;
+
+    stroker.add_triangle = add_triangle;
+    stroker.add_triangle_fan = add_triangle_fan;
+    stroker.add_convex_quad = add_convex_quad;
+    stroker.closure = closure;
+
+    status = _cairo_path_fixed_interpret (path,
+                                         _cairo_stroker_move_to,
+                                         stroker.dash.dashed ?
+                                         _cairo_stroker_line_to_dashed :
+                                         _cairo_stroker_line_to,
+                                         _cairo_stroker_curve_to,
+                                         _cairo_stroker_close_path,
+                                         &stroker);
+
+    if (unlikely (status))
+       goto BAIL;
+
+    /* Cap the start and end of the final sub path as needed */
+    status = _cairo_stroker_add_caps (&stroker);
+
+BAIL:
+    _cairo_stroker_fini (&stroker);
+
+    return status;
+}
+
+cairo_status_t
+_cairo_path_fixed_stroke_dashed_to_polygon (const cairo_path_fixed_t   *path,
+                                           const cairo_stroke_style_t  *stroke_style,
+                                           const cairo_matrix_t        *ctm,
+                                           const cairo_matrix_t        *ctm_inverse,
+                                           double               tolerance,
+                                           cairo_polygon_t *polygon)
+{
+    cairo_stroker_t stroker;
+    cairo_status_t status;
+
+    status = _cairo_stroker_init (&stroker, stroke_style,
+                                 ctm, ctm_inverse, tolerance);
+    if (unlikely (status))
+       return status;
+
+    stroker.add_external_edge = _cairo_polygon_add_external_edge,
+    stroker.closure = polygon;
+
+    if (polygon->num_limits)
+       _cairo_stroker_limit (&stroker, path,
+                             polygon->limits, polygon->num_limits);
+
+    status = _cairo_path_fixed_interpret (path,
+                                         _cairo_stroker_move_to,
+                                         stroker.dash.dashed ?
+                                         _cairo_stroker_line_to_dashed :
+                                         _cairo_stroker_line_to,
+                                         _cairo_stroker_curve_to,
+                                         _cairo_stroker_close_path,
+                                         &stroker);
+
+    if (unlikely (status))
+       goto BAIL;
+
+    /* Cap the start and end of the final sub path as needed */
+    status = _cairo_stroker_add_caps (&stroker);
+
+BAIL:
+    _cairo_stroker_fini (&stroker);
+
+    return status;
+}
+
+cairo_int_status_t
+_cairo_path_fixed_stroke_to_traps (const cairo_path_fixed_t    *path,
+                                  const cairo_stroke_style_t   *stroke_style,
+                                  const cairo_matrix_t *ctm,
+                                  const cairo_matrix_t *ctm_inverse,
+                                  double                tolerance,
+                                  cairo_traps_t        *traps)
+{
+    cairo_int_status_t status;
+    cairo_polygon_t polygon;
+
+    _cairo_polygon_init (&polygon, traps->limits, traps->num_limits);
+    status = _cairo_path_fixed_stroke_to_polygon (path,
+                                                 stroke_style,
+                                                 ctm,
+                                                 ctm_inverse,
+                                                 tolerance,
+                                                 &polygon);
+    if (unlikely (status))
+       goto BAIL;
+
+    status = _cairo_polygon_status (&polygon);
+    if (unlikely (status))
+       goto BAIL;
+
+    status = _cairo_bentley_ottmann_tessellate_polygon (traps, &polygon,
+                                                       CAIRO_FILL_RULE_WINDING);
+
+BAIL:
+    _cairo_polygon_fini (&polygon);
+
+    return status;
+}
diff --git a/src/cairo-path.c b/src/cairo-path.c
new file mode 100644 (file)
index 0000000..d3d9131
--- /dev/null
@@ -0,0 +1,476 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2005 Red Hat, Inc.
+ * Copyright © 2006 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ *     Carl D. Worth <cworth@redhat.com>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-private.h"
+#include "cairo-error-private.h"
+#include "cairo-path-private.h"
+#include "cairo-path-fixed-private.h"
+
+/**
+ * SECTION:cairo-paths
+ * @Title: Paths
+ * @Short_Description: Creating paths and manipulating path data
+ *
+ * Paths are the most basic drawing tools and are primarily used to implicitly
+ * generate simple masks.
+ */
+
+static const cairo_path_t _cairo_path_nil = { CAIRO_STATUS_NO_MEMORY, NULL, 0 };
+
+/* Closure for path interpretation. */
+typedef struct cairo_path_count {
+    int count;
+} cpc_t;
+
+static cairo_status_t
+_cpc_move_to (void *closure,
+             const cairo_point_t *point)
+{
+    cpc_t *cpc = closure;
+
+    cpc->count += 2;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cpc_line_to (void *closure,
+             const cairo_point_t *point)
+{
+    cpc_t *cpc = closure;
+
+    cpc->count += 2;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cpc_curve_to (void            *closure,
+              const cairo_point_t      *p1,
+              const cairo_point_t      *p2,
+              const cairo_point_t      *p3)
+{
+    cpc_t *cpc = closure;
+
+    cpc->count += 4;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cpc_close_path (void *closure)
+{
+    cpc_t *cpc = closure;
+
+    cpc->count += 1;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static int
+_cairo_path_count (cairo_path_t                *path,
+                  cairo_path_fixed_t   *path_fixed,
+                  double                tolerance,
+                  cairo_bool_t          flatten)
+{
+    cairo_status_t status;
+    cpc_t cpc;
+
+    cpc.count = 0;
+
+    if (flatten) {
+       status = _cairo_path_fixed_interpret_flat (path_fixed,
+                                                  _cpc_move_to,
+                                                  _cpc_line_to,
+                                                  _cpc_close_path,
+                                                  &cpc,
+                                                  tolerance);
+    } else {
+       status = _cairo_path_fixed_interpret (path_fixed,
+                                             _cpc_move_to,
+                                             _cpc_line_to,
+                                             _cpc_curve_to,
+                                             _cpc_close_path,
+                                             &cpc);
+    }
+
+    if (unlikely (status))
+       return -1;
+
+    return cpc.count;
+}
+
+/* Closure for path interpretation. */
+typedef struct cairo_path_populate {
+    cairo_path_data_t *data;
+    cairo_t *cr;
+} cpp_t;
+
+static cairo_status_t
+_cpp_move_to (void *closure,
+             const cairo_point_t *point)
+{
+    cpp_t *cpp = closure;
+    cairo_path_data_t *data = cpp->data;
+    double x, y;
+
+    x = _cairo_fixed_to_double (point->x);
+    y = _cairo_fixed_to_double (point->y);
+
+    cairo_device_to_user (cpp->cr, &x, &y);
+
+    data->header.type = CAIRO_PATH_MOVE_TO;
+    data->header.length = 2;
+
+    /* We index from 1 to leave room for data->header */
+    data[1].point.x = x;
+    data[1].point.y = y;
+
+    cpp->data += data->header.length;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cpp_line_to (void *closure,
+             const cairo_point_t *point)
+{
+    cpp_t *cpp = closure;
+    cairo_path_data_t *data = cpp->data;
+    double x, y;
+
+    x = _cairo_fixed_to_double (point->x);
+    y = _cairo_fixed_to_double (point->y);
+
+    cairo_device_to_user (cpp->cr, &x, &y);
+
+    data->header.type = CAIRO_PATH_LINE_TO;
+    data->header.length = 2;
+
+    /* We index from 1 to leave room for data->header */
+    data[1].point.x = x;
+    data[1].point.y = y;
+
+    cpp->data += data->header.length;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cpp_curve_to (void                    *closure,
+              const cairo_point_t      *p1,
+              const cairo_point_t      *p2,
+              const cairo_point_t      *p3)
+{
+    cpp_t *cpp = closure;
+    cairo_path_data_t *data = cpp->data;
+    double x1, y1;
+    double x2, y2;
+    double x3, y3;
+
+    x1 = _cairo_fixed_to_double (p1->x);
+    y1 = _cairo_fixed_to_double (p1->y);
+    cairo_device_to_user (cpp->cr, &x1, &y1);
+
+    x2 = _cairo_fixed_to_double (p2->x);
+    y2 = _cairo_fixed_to_double (p2->y);
+    cairo_device_to_user (cpp->cr, &x2, &y2);
+
+    x3 = _cairo_fixed_to_double (p3->x);
+    y3 = _cairo_fixed_to_double (p3->y);
+    cairo_device_to_user (cpp->cr, &x3, &y3);
+
+    data->header.type = CAIRO_PATH_CURVE_TO;
+    data->header.length = 4;
+
+    /* We index from 1 to leave room for data->header */
+    data[1].point.x = x1;
+    data[1].point.y = y1;
+
+    data[2].point.x = x2;
+    data[2].point.y = y2;
+
+    data[3].point.x = x3;
+    data[3].point.y = y3;
+
+    cpp->data += data->header.length;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cpp_close_path (void *closure)
+{
+    cpp_t *cpp = closure;
+    cairo_path_data_t *data = cpp->data;
+
+    data->header.type = CAIRO_PATH_CLOSE_PATH;
+    data->header.length = 1;
+
+    cpp->data += data->header.length;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_path_populate (cairo_path_t             *path,
+                     cairo_path_fixed_t        *path_fixed,
+                     cairo_t                   *cr,
+                     cairo_bool_t               flatten)
+{
+    cairo_status_t status;
+    cpp_t cpp;
+
+    cpp.data = path->data;
+    cpp.cr = cr;
+
+    if (flatten) {
+       status = _cairo_path_fixed_interpret_flat (path_fixed,
+                                                  _cpp_move_to,
+                                                  _cpp_line_to,
+                                                  _cpp_close_path,
+                                                  &cpp,
+                                                  cairo_get_tolerance (cr));
+    } else {
+       status = _cairo_path_fixed_interpret (path_fixed,
+                                         _cpp_move_to,
+                                         _cpp_line_to,
+                                         _cpp_curve_to,
+                                         _cpp_close_path,
+                                         &cpp);
+    }
+
+    if (unlikely (status))
+       return status;
+
+    /* Sanity check the count */
+    assert (cpp.data - path->data == path->num_data);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_path_t *
+_cairo_path_create_in_error (cairo_status_t status)
+{
+    cairo_path_t *path;
+
+    /* special case NO_MEMORY so as to avoid allocations */
+    if (status == CAIRO_STATUS_NO_MEMORY)
+       return (cairo_path_t*) &_cairo_path_nil;
+
+    path = malloc (sizeof (cairo_path_t));
+    if (unlikely (path == NULL)) {
+       _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+       return (cairo_path_t*) &_cairo_path_nil;
+    }
+
+    path->num_data = 0;
+    path->data = NULL;
+    path->status = status;
+
+    return path;
+}
+
+static cairo_path_t *
+_cairo_path_create_internal (cairo_path_fixed_t *path_fixed,
+                            cairo_t            *cr,
+                            cairo_bool_t        flatten)
+{
+    cairo_path_t *path;
+
+    path = malloc (sizeof (cairo_path_t));
+    if (unlikely (path == NULL)) {
+       _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+       return (cairo_path_t*) &_cairo_path_nil;
+    }
+
+    path->num_data = _cairo_path_count (path, path_fixed,
+                                       cairo_get_tolerance (cr),
+                                       flatten);
+    if (path->num_data < 0) {
+       free (path);
+       return (cairo_path_t*) &_cairo_path_nil;
+    }
+
+    if (path->num_data) {
+       path->data = _cairo_malloc_ab (path->num_data,
+                                      sizeof (cairo_path_data_t));
+       if (unlikely (path->data == NULL)) {
+           free (path);
+           _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+           return (cairo_path_t*) &_cairo_path_nil;
+       }
+
+       path->status = _cairo_path_populate (path, path_fixed, cr, flatten);
+    } else {
+       path->data = NULL;
+       path->status = CAIRO_STATUS_SUCCESS;
+    }
+
+    return path;
+}
+
+/**
+ * cairo_path_destroy:
+ * @path: a path previously returned by either cairo_copy_path() or
+ * cairo_copy_path_flat().
+ *
+ * Immediately releases all memory associated with @path. After a call
+ * to cairo_path_destroy() the @path pointer is no longer valid and
+ * should not be used further.
+ *
+ * Note: cairo_path_destroy() should only be called with a
+ * pointer to a #cairo_path_t returned by a cairo function. Any path
+ * that is created manually (ie. outside of cairo) should be destroyed
+ * manually as well.
+ **/
+void
+cairo_path_destroy (cairo_path_t *path)
+{
+    if (path == NULL || path == &_cairo_path_nil)
+       return;
+
+    free (path->data);
+
+    free (path);
+}
+slim_hidden_def (cairo_path_destroy);
+
+/**
+ * _cairo_path_create:
+ * @path: a fixed-point, device-space path to be converted and copied
+ * @cr: the current graphics context
+ *
+ * Creates a user-space #cairo_path_t copy of the given device-space
+ * @path. The @cr parameter provides the inverse CTM for the
+ * conversion.
+ *
+ * Return value: the new copy of the path. If there is insufficient
+ * memory a pointer to a special static nil #cairo_path_t will be
+ * returned instead with status==%CAIRO_STATUS_NO_MEMORY and
+ * data==%NULL.
+ **/
+cairo_path_t *
+_cairo_path_create (cairo_path_fixed_t *path,
+                   cairo_t             *cr)
+{
+    return _cairo_path_create_internal (path, cr, FALSE);
+}
+
+/**
+ * _cairo_path_create_flat:
+ * @path: a fixed-point, device-space path to be flattened, converted and copied
+ * @cr: the current graphics context
+ *
+ * Creates a flattened, user-space #cairo_path_t copy of the given
+ * device-space @path. The @cr parameter provide the inverse CTM
+ * for the conversion, as well as the tolerance value to control the
+ * accuracy of the flattening.
+ *
+ * Return value: the flattened copy of the path. If there is insufficient
+ * memory a pointer to a special static nil #cairo_path_t will be
+ * returned instead with status==%CAIRO_STATUS_NO_MEMORY and
+ * data==%NULL.
+ **/
+cairo_path_t *
+_cairo_path_create_flat (cairo_path_fixed_t *path,
+                        cairo_t            *cr)
+{
+    return _cairo_path_create_internal (path, cr, TRUE);
+}
+
+/**
+ * _cairo_path_append_to_context:
+ * @path: the path data to be appended
+ * @cr: a cairo context
+ *
+ * Append @path to the current path within @cr.
+ *
+ * Return value: %CAIRO_STATUS_INVALID_PATH_DATA if the data in @path
+ * is invalid, and %CAIRO_STATUS_SUCCESS otherwise.
+ **/
+cairo_status_t
+_cairo_path_append_to_context (const cairo_path_t      *path,
+                              cairo_t                  *cr)
+{
+    const cairo_path_data_t *p, *end;
+
+    end = &path->data[path->num_data];
+    for (p = &path->data[0]; p < end; p += p->header.length) {
+       switch (p->header.type) {
+       case CAIRO_PATH_MOVE_TO:
+           if (unlikely (p->header.length < 2))
+               return _cairo_error (CAIRO_STATUS_INVALID_PATH_DATA);
+
+           cairo_move_to (cr, p[1].point.x, p[1].point.y);
+           break;
+
+       case CAIRO_PATH_LINE_TO:
+           if (unlikely (p->header.length < 2))
+               return _cairo_error (CAIRO_STATUS_INVALID_PATH_DATA);
+
+           cairo_line_to (cr, p[1].point.x, p[1].point.y);
+           break;
+
+       case CAIRO_PATH_CURVE_TO:
+           if (unlikely (p->header.length < 4))
+               return _cairo_error (CAIRO_STATUS_INVALID_PATH_DATA);
+
+           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 (unlikely (p->header.length < 1))
+               return _cairo_error (CAIRO_STATUS_INVALID_PATH_DATA);
+
+           cairo_close_path (cr);
+           break;
+
+       default:
+           return _cairo_error (CAIRO_STATUS_INVALID_PATH_DATA);
+       }
+
+       if (unlikely (cr->status))
+           return cr->status;
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
diff --git a/src/cairo-pattern-private.h b/src/cairo-pattern-private.h
new file mode 100644 (file)
index 0000000..8be319e
--- /dev/null
@@ -0,0 +1,338 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ *     Carl D. Worth <cworth@redhat.com>
+ */
+
+#ifndef CAIRO_PATTERN_PRIVATE_H
+#define CAIRO_PATTERN_PRIVATE_H
+
+#include "cairo-error-private.h"
+#include "cairo-types-private.h"
+#include "cairo-list-private.h"
+
+#include <stdio.h> /* FILE* */
+
+CAIRO_BEGIN_DECLS
+
+typedef struct _cairo_pattern_observer cairo_pattern_observer_t;
+
+enum {
+    CAIRO_PATTERN_NOTIFY_MATRIX = 0x1,
+    CAIRO_PATTERN_NOTIFY_FILTER = 0x2,
+    CAIRO_PATTERN_NOTIFY_EXTEND = 0x4,
+    CAIRO_PATTERN_NOTIFY_OPACITY = 0x9,
+};
+
+struct _cairo_pattern_observer {
+    void (*notify) (cairo_pattern_observer_t *,
+                   cairo_pattern_t *pattern,
+                   unsigned int flags);
+    cairo_list_t link;
+};
+
+struct _cairo_pattern {
+    cairo_reference_count_t    ref_count;
+    cairo_status_t             status;
+    cairo_user_data_array_t    user_data;
+    cairo_list_t               observers;
+
+    cairo_pattern_type_t       type;
+
+    cairo_filter_t             filter;
+    cairo_extend_t             extend;
+    cairo_bool_t               has_component_alpha;
+
+    cairo_matrix_t             matrix;
+    double                     opacity;
+};
+
+struct _cairo_solid_pattern {
+    cairo_pattern_t base;
+    cairo_color_t color;
+};
+
+typedef struct _cairo_surface_pattern {
+    cairo_pattern_t base;
+
+    cairo_surface_t *surface;
+} cairo_surface_pattern_t;
+
+typedef struct _cairo_gradient_stop {
+    double offset;
+    cairo_color_stop_t color;
+} cairo_gradient_stop_t;
+
+typedef struct _cairo_gradient_pattern {
+    cairo_pattern_t base;
+
+    unsigned int           n_stops;
+    unsigned int           stops_size;
+    cairo_gradient_stop_t  *stops;
+    cairo_gradient_stop_t   stops_embedded[2];
+} cairo_gradient_pattern_t;
+
+typedef struct _cairo_linear_pattern {
+    cairo_gradient_pattern_t base;
+
+    cairo_point_double_t pd1;
+    cairo_point_double_t pd2;
+} cairo_linear_pattern_t;
+
+typedef struct _cairo_radial_pattern {
+    cairo_gradient_pattern_t base;
+
+    cairo_circle_double_t cd1;
+    cairo_circle_double_t cd2;
+} cairo_radial_pattern_t;
+
+typedef union {
+    cairo_gradient_pattern_t base;
+
+    cairo_linear_pattern_t linear;
+    cairo_radial_pattern_t radial;
+} cairo_gradient_pattern_union_t;
+
+/*
+ * A mesh patch is a tensor-product patch (bicubic Bezier surface
+ * patch). It has 16 control points. Each set of 4 points along the
+ * sides of the 4x4 grid of control points is a Bezier curve that
+ * defines one side of the patch. A color is assigned to each
+ * corner. The inner 4 points provide additional control over the
+ * shape and the color mapping.
+ *
+ * Cairo uses the same convention as the PDF Reference for numbering
+ * the points and side of the patch.
+ *
+ *
+ *                      Side 1
+ *
+ *          p[0][3] p[1][3] p[2][3] p[3][3]
+ * Side 0   p[0][2] p[1][2] p[2][2] p[3][2]  Side 2
+ *          p[0][1] p[1][1] p[2][1] p[3][1]
+ *          p[0][0] p[1][0] p[2][0] p[3][0]
+ *
+ *                      Side 3
+ *
+ *
+ *   Point            Color
+ *  -------------------------
+ *  points[0][0]    colors[0]
+ *  points[0][3]    colors[1]
+ *  points[3][3]    colors[2]
+ *  points[3][0]    colors[3]
+ */
+
+typedef struct _cairo_mesh_patch {
+    cairo_point_double_t points[4][4];
+    cairo_color_t colors[4];
+} cairo_mesh_patch_t;
+
+typedef struct _cairo_mesh_pattern {
+    cairo_pattern_t base;
+
+    cairo_array_t patches;
+    cairo_mesh_patch_t *current_patch;
+    int current_side;
+    cairo_bool_t has_control_point[4];
+    cairo_bool_t has_color[4];
+} cairo_mesh_pattern_t;
+
+typedef union {
+    cairo_pattern_t                base;
+
+    cairo_solid_pattern_t          solid;
+    cairo_surface_pattern_t        surface;
+    cairo_gradient_pattern_union_t  gradient;
+    cairo_mesh_pattern_t           mesh;
+} cairo_pattern_union_t;
+
+
+/* cairo-pattern.c */
+
+cairo_private cairo_pattern_t *
+_cairo_pattern_create_in_error (cairo_status_t status);
+
+cairo_private cairo_status_t
+_cairo_pattern_create_copy (cairo_pattern_t      **pattern,
+                           const cairo_pattern_t  *other);
+
+cairo_private cairo_status_t
+_cairo_pattern_init_copy (cairo_pattern_t      *pattern,
+                         const cairo_pattern_t *other);
+
+cairo_private void
+_cairo_pattern_init_static_copy (cairo_pattern_t       *pattern,
+                                const cairo_pattern_t *other);
+
+cairo_private cairo_status_t
+_cairo_pattern_init_snapshot (cairo_pattern_t       *pattern,
+                             const cairo_pattern_t *other);
+
+cairo_private void
+_cairo_pattern_init_solid (cairo_solid_pattern_t       *pattern,
+                          const cairo_color_t          *color);
+
+cairo_private void
+_cairo_pattern_init_for_surface (cairo_surface_pattern_t *pattern,
+                                cairo_surface_t *surface);
+
+cairo_private void
+_cairo_pattern_fini (cairo_pattern_t *pattern);
+
+cairo_private void
+_cairo_pattern_fini_snapshot (cairo_pattern_t *pattern);
+
+cairo_private cairo_pattern_t *
+_cairo_pattern_create_solid (const cairo_color_t       *color);
+
+cairo_private void
+_cairo_pattern_transform (cairo_pattern_t      *pattern,
+                         const cairo_matrix_t *ctm_inverse);
+
+cairo_private cairo_bool_t
+_cairo_pattern_is_opaque_solid (const cairo_pattern_t *pattern);
+
+cairo_private cairo_bool_t
+_cairo_pattern_is_opaque (const cairo_pattern_t *pattern,
+                         const cairo_rectangle_int_t *extents);
+
+cairo_private cairo_bool_t
+_cairo_pattern_is_clear (const cairo_pattern_t *pattern);
+
+cairo_private cairo_bool_t
+_cairo_gradient_pattern_is_solid (const cairo_gradient_pattern_t *gradient,
+                                 const cairo_rectangle_int_t *extents,
+                                 cairo_color_t *color);
+
+cairo_private void
+_cairo_gradient_pattern_fit_to_range (const cairo_gradient_pattern_t *gradient,
+                                     double                          max_value,
+                                     cairo_matrix_t                 *out_matrix,
+                                     cairo_circle_double_t           out_circle[2]);
+
+cairo_private cairo_bool_t
+_cairo_radial_pattern_focus_is_inside (const cairo_radial_pattern_t *radial);
+
+cairo_private void
+_cairo_gradient_pattern_box_to_parameter (const cairo_gradient_pattern_t *gradient,
+                                         double x0, double y0,
+                                         double x1, double y1,
+                                         double tolerance,
+                                         double out_range[2]);
+
+cairo_private void
+_cairo_gradient_pattern_interpolate (const cairo_gradient_pattern_t *gradient,
+                                    double                          t,
+                                    cairo_circle_double_t          *out_circle);
+
+cairo_private void
+_cairo_pattern_alpha_range (const cairo_pattern_t *pattern,
+                           double                *out_min,
+                           double                *out_max);
+
+cairo_private cairo_bool_t
+_cairo_mesh_pattern_coord_box (const cairo_mesh_pattern_t *mesh,
+                              double                     *out_xmin,
+                              double                     *out_ymin,
+                              double                     *out_xmax,
+                              double                     *out_ymax);
+
+cairo_private_no_warn cairo_filter_t
+_cairo_pattern_sampled_area (const cairo_pattern_t *pattern,
+                            const cairo_rectangle_int_t *extents,
+                            cairo_rectangle_int_t *sample);
+
+cairo_private void
+_cairo_pattern_get_extents (const cairo_pattern_t          *pattern,
+                           cairo_rectangle_int_t           *extents);
+
+cairo_private cairo_int_status_t
+_cairo_pattern_get_ink_extents (const cairo_pattern_t      *pattern,
+                               cairo_rectangle_int_t       *extents);
+
+cairo_private unsigned long
+_cairo_pattern_hash (const cairo_pattern_t *pattern);
+
+cairo_private unsigned long
+_cairo_linear_pattern_hash (unsigned long hash,
+                           const cairo_linear_pattern_t *linear);
+
+cairo_private unsigned long
+_cairo_radial_pattern_hash (unsigned long hash,
+                           const cairo_radial_pattern_t *radial);
+
+cairo_private cairo_bool_t
+_cairo_linear_pattern_equal (const cairo_linear_pattern_t *a,
+                            const cairo_linear_pattern_t *b);
+
+cairo_private unsigned long
+_cairo_pattern_size (const cairo_pattern_t *pattern);
+
+cairo_private cairo_bool_t
+_cairo_radial_pattern_equal (const cairo_radial_pattern_t *a,
+                            const cairo_radial_pattern_t *b);
+
+cairo_private cairo_bool_t
+_cairo_pattern_equal (const cairo_pattern_t *a,
+                     const cairo_pattern_t *b);
+
+static inline void
+_cairo_pattern_add_observer (cairo_pattern_t *pattern,
+                            cairo_pattern_observer_t *observer,
+                            void (*func) (cairo_pattern_observer_t *,
+                                          cairo_pattern_t *,
+                                          unsigned int))
+{
+    observer->notify = func;
+    cairo_list_add (&observer->link, &pattern->observers);
+}
+
+/* cairo-mesh-pattern-rasterizer.c */
+
+cairo_private void
+_cairo_mesh_pattern_rasterize (const cairo_mesh_pattern_t *mesh,
+                              void                       *data,
+                              int                         width,
+                              int                         height,
+                              int                         stride,
+                              double                      x_offset,
+                              double                      y_offset);
+
+cairo_private void
+_cairo_debug_print_pattern (FILE *file, const cairo_pattern_t *pattern);
+
+CAIRO_END_DECLS
+
+#endif /* CAIRO_PATTERN_PRIVATE */
+
+
diff --git a/src/cairo-pattern.c b/src/cairo-pattern.c
new file mode 100644 (file)
index 0000000..f91de26
--- /dev/null
@@ -0,0 +1,4458 @@
+/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2004 David Reveman
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of David
+ * Reveman not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. David Reveman makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * DAVID REVEMAN DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL DAVID REVEMAN BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Authors: David Reveman <davidr@novell.com>
+ *         Keith Packard <keithp@keithp.com>
+ *         Carl Worth <cworth@cworth.org>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-array-private.h"
+#include "cairo-error-private.h"
+#include "cairo-freed-pool-private.h"
+#include "cairo-image-surface-private.h"
+#include "cairo-path-private.h"
+#include "cairo-pattern-private.h"
+#include "cairo-recording-surface-private.h"
+
+#include <float.h>
+
+#define PIXMAN_MAX_INT ((pixman_fixed_1 >> 1) - pixman_fixed_e) /* need to ensure deltas also fit */
+
+/**
+ * SECTION:cairo-pattern
+ * @Title: cairo_pattern_t
+ * @Short_Description: Sources for drawing
+ * @See_Also: #cairo_t, #cairo_surface_t
+ *
+ * #cairo_pattern_t is the paint with which cairo draws.
+ * The primary use of patterns is as the source for all cairo drawing 
+ * operations, although they can also be used as masks, that is, as the 
+ * brush too.
+ *
+ * A cairo pattern is created by using one of the many constructors,
+ * of the form
+ * <function>cairo_pattern_create_<emphasis>type</emphasis>()</function>
+ * or implicitly through
+ * <function>cairo_set_source_<emphasis>type</emphasis>()</function>
+ * functions.
+ */
+
+static freed_pool_t freed_pattern_pool[5];
+
+static const cairo_solid_pattern_t _cairo_pattern_nil = {
+    {
+      CAIRO_REFERENCE_COUNT_INVALID,   /* ref_count */
+      CAIRO_STATUS_NO_MEMORY,          /* status */
+      { 0, 0, 0, NULL },               /* user_data */
+      { NULL, NULL },                  /* observers */
+
+      CAIRO_PATTERN_TYPE_SOLID,                /* type */
+      CAIRO_FILTER_DEFAULT,            /* filter */
+      CAIRO_EXTEND_GRADIENT_DEFAULT,   /* extend */
+      FALSE,                           /* has component alpha */
+      { 1., 0., 0., 1., 0., 0., },     /* matrix */
+      1.0                               /* opacity */
+    }
+};
+
+static const cairo_solid_pattern_t _cairo_pattern_nil_null_pointer = {
+    {
+      CAIRO_REFERENCE_COUNT_INVALID,   /* ref_count */
+      CAIRO_STATUS_NULL_POINTER,       /* status */
+      { 0, 0, 0, NULL },               /* user_data */
+      { NULL, NULL },                  /* observers */
+
+      CAIRO_PATTERN_TYPE_SOLID,                /* type */
+      CAIRO_FILTER_DEFAULT,            /* filter */
+      CAIRO_EXTEND_GRADIENT_DEFAULT,   /* extend */
+      FALSE,                           /* has component alpha */
+      { 1., 0., 0., 1., 0., 0., },     /* matrix */
+      1.0                               /* opacity */
+    }
+};
+
+const cairo_solid_pattern_t _cairo_pattern_black = {
+    {
+      CAIRO_REFERENCE_COUNT_INVALID,   /* ref_count */
+      CAIRO_STATUS_SUCCESS,            /* status */
+      { 0, 0, 0, NULL },               /* user_data */
+      { NULL, NULL },                  /* observers */
+
+      CAIRO_PATTERN_TYPE_SOLID,                /* type */
+      CAIRO_FILTER_NEAREST,            /* filter */
+      CAIRO_EXTEND_REPEAT,             /* extend */
+      FALSE,                           /* has component alpha */
+      { 1., 0., 0., 1., 0., 0., },     /* matrix */
+      1.0                               /* opacity */
+    },
+    { 0., 0., 0., 1., 0, 0, 0, 0xffff },/* color (double rgba, short rgba) */
+};
+
+const cairo_solid_pattern_t _cairo_pattern_clear = {
+    {
+      CAIRO_REFERENCE_COUNT_INVALID,   /* ref_count */
+      CAIRO_STATUS_SUCCESS,            /* status */
+      { 0, 0, 0, NULL },               /* user_data */
+      { NULL, NULL },                  /* observers */
+
+      CAIRO_PATTERN_TYPE_SOLID,                /* type */
+      CAIRO_FILTER_NEAREST,            /* filter */
+      CAIRO_EXTEND_REPEAT,             /* extend */
+      FALSE,                           /* has component alpha */
+      { 1., 0., 0., 1., 0., 0., },     /* matrix */
+      1.0                               /* opacity */
+    },
+    { 0., 0., 0., 0., 0, 0, 0, 0 },/* color (double rgba, short rgba) */
+};
+
+const cairo_solid_pattern_t _cairo_pattern_white = {
+    {
+      CAIRO_REFERENCE_COUNT_INVALID,   /* ref_count */
+      CAIRO_STATUS_SUCCESS,            /* status */
+      { 0, 0, 0, NULL },               /* user_data */
+      { NULL, NULL },                  /* observers */
+
+      CAIRO_PATTERN_TYPE_SOLID,                /* type */
+      CAIRO_FILTER_NEAREST,            /* filter */
+      CAIRO_EXTEND_REPEAT,             /* extend */
+      FALSE,                           /* has component alpha */
+      { 1., 0., 0., 1., 0., 0., },     /* matrix */
+      1.0                               /* opacity */
+    },
+    { 1., 1., 1., 1., 0xffff, 0xffff, 0xffff, 0xffff },/* color (double rgba, short rgba) */
+};
+
+static void
+_cairo_pattern_notify_observers (cairo_pattern_t *pattern,
+                                unsigned int flags)
+{
+    cairo_pattern_observer_t *pos;
+
+    cairo_list_foreach_entry (pos, cairo_pattern_observer_t, &pattern->observers, link)
+       pos->notify (pos, pattern, flags);
+}
+
+/**
+ * _cairo_pattern_set_error:
+ * @pattern: a pattern
+ * @status: a status value indicating an error
+ *
+ * Atomically sets pattern->status to @status and calls _cairo_error;
+ * Does nothing if status is %CAIRO_STATUS_SUCCESS.
+ *
+ * All assignments of an error status to pattern->status should happen
+ * through _cairo_pattern_set_error(). Note that due to the nature of
+ * the atomic operation, it is not safe to call this function on the nil
+ * objects.
+ *
+ * The purpose of this function is to allow the user to set a
+ * breakpoint in _cairo_error() to generate a stack trace for when the
+ * user causes cairo to detect an error.
+ **/
+static cairo_status_t
+_cairo_pattern_set_error (cairo_pattern_t *pattern,
+                         cairo_status_t status)
+{
+    if (status == CAIRO_STATUS_SUCCESS)
+       return status;
+
+    /* Don't overwrite an existing error. This preserves the first
+     * error, which is the most significant. */
+    _cairo_status_set_error (&pattern->status, status);
+
+    return _cairo_error (status);
+}
+
+static void
+_cairo_pattern_init (cairo_pattern_t *pattern, cairo_pattern_type_t type)
+{
+#if HAVE_VALGRIND
+    switch (type) {
+    case CAIRO_PATTERN_TYPE_SOLID:
+       VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_solid_pattern_t));
+       break;
+    case CAIRO_PATTERN_TYPE_SURFACE:
+       VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_surface_pattern_t));
+       break;
+    case CAIRO_PATTERN_TYPE_LINEAR:
+       VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_linear_pattern_t));
+       break;
+    case CAIRO_PATTERN_TYPE_RADIAL:
+       VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_radial_pattern_t));
+       break;
+    case CAIRO_PATTERN_TYPE_MESH:
+       VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_mesh_pattern_t));
+       break;
+    }
+#endif
+
+    pattern->type      = type;
+    pattern->status    = CAIRO_STATUS_SUCCESS;
+
+    /* Set the reference count to zero for on-stack patterns.
+     * Callers needs to explicitly increment the count for heap allocations. */
+    CAIRO_REFERENCE_COUNT_INIT (&pattern->ref_count, 0);
+
+    _cairo_user_data_array_init (&pattern->user_data);
+
+    if (type == CAIRO_PATTERN_TYPE_SURFACE)
+       pattern->extend = CAIRO_EXTEND_SURFACE_DEFAULT;
+    else
+       pattern->extend = CAIRO_EXTEND_GRADIENT_DEFAULT;
+
+    pattern->filter    = CAIRO_FILTER_DEFAULT;
+    pattern->opacity   = 1.0;
+
+    pattern->has_component_alpha = FALSE;
+
+    cairo_matrix_init_identity (&pattern->matrix);
+
+    cairo_list_init (&pattern->observers);
+}
+
+static cairo_status_t
+_cairo_gradient_pattern_init_copy (cairo_gradient_pattern_t      *pattern,
+                                  const cairo_gradient_pattern_t *other)
+{
+    if (CAIRO_INJECT_FAULT ())
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    if (other->base.type == CAIRO_PATTERN_TYPE_LINEAR)
+    {
+       cairo_linear_pattern_t *dst = (cairo_linear_pattern_t *) pattern;
+       cairo_linear_pattern_t *src = (cairo_linear_pattern_t *) other;
+
+       *dst = *src;
+    }
+    else
+    {
+       cairo_radial_pattern_t *dst = (cairo_radial_pattern_t *) pattern;
+       cairo_radial_pattern_t *src = (cairo_radial_pattern_t *) other;
+
+       *dst = *src;
+    }
+
+    if (other->stops == other->stops_embedded)
+       pattern->stops = pattern->stops_embedded;
+    else if (other->stops)
+    {
+       pattern->stops = _cairo_malloc_ab (other->stops_size,
+                                          sizeof (cairo_gradient_stop_t));
+       if (unlikely (pattern->stops == NULL)) {
+           pattern->stops_size = 0;
+           pattern->n_stops = 0;
+           return _cairo_pattern_set_error (&pattern->base, CAIRO_STATUS_NO_MEMORY);
+       }
+
+       memcpy (pattern->stops, other->stops,
+               other->n_stops * sizeof (cairo_gradient_stop_t));
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_mesh_pattern_init_copy (cairo_mesh_pattern_t       *pattern,
+                              const cairo_mesh_pattern_t *other)
+{
+    *pattern = *other;
+
+    _cairo_array_init (&pattern->patches,  sizeof (cairo_mesh_patch_t));
+    return _cairo_array_append_multiple (&pattern->patches,
+                                        _cairo_array_index_const (&other->patches, 0),
+                                        _cairo_array_num_elements (&other->patches));
+}
+
+cairo_status_t
+_cairo_pattern_init_copy (cairo_pattern_t      *pattern,
+                         const cairo_pattern_t *other)
+{
+    if (other->status)
+       return _cairo_pattern_set_error (pattern, other->status);
+
+    switch (other->type) {
+    case CAIRO_PATTERN_TYPE_SOLID: {
+       cairo_solid_pattern_t *dst = (cairo_solid_pattern_t *) pattern;
+       cairo_solid_pattern_t *src = (cairo_solid_pattern_t *) other;
+
+       VG (VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_solid_pattern_t)));
+
+       *dst = *src;
+    } break;
+    case CAIRO_PATTERN_TYPE_SURFACE: {
+       cairo_surface_pattern_t *dst = (cairo_surface_pattern_t *) pattern;
+       cairo_surface_pattern_t *src = (cairo_surface_pattern_t *) other;
+
+       VG (VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_surface_pattern_t)));
+
+       *dst = *src;
+       cairo_surface_reference (dst->surface);
+    } break;
+    case CAIRO_PATTERN_TYPE_LINEAR:
+    case CAIRO_PATTERN_TYPE_RADIAL: {
+       cairo_gradient_pattern_t *dst = (cairo_gradient_pattern_t *) pattern;
+       cairo_gradient_pattern_t *src = (cairo_gradient_pattern_t *) other;
+       cairo_status_t status;
+
+       if (other->type == CAIRO_PATTERN_TYPE_LINEAR) {
+           VG (VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_linear_pattern_t)));
+       } else {
+           VG (VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_radial_pattern_t)));
+       }
+
+       status = _cairo_gradient_pattern_init_copy (dst, src);
+       if (unlikely (status))
+           return status;
+
+    } break;
+    case CAIRO_PATTERN_TYPE_MESH: {
+       cairo_mesh_pattern_t *dst = (cairo_mesh_pattern_t *) pattern;
+       cairo_mesh_pattern_t *src = (cairo_mesh_pattern_t *) other;
+       cairo_status_t status;
+
+       VG (VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_mesh_pattern_t)));
+
+       status = _cairo_mesh_pattern_init_copy (dst, src);
+       if (unlikely (status))
+           return status;
+
+    } break;
+    }
+
+    /* The reference count and user_data array are unique to the copy. */
+    CAIRO_REFERENCE_COUNT_INIT (&pattern->ref_count, 0);
+    _cairo_user_data_array_init (&pattern->user_data);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+void
+_cairo_pattern_init_static_copy (cairo_pattern_t       *pattern,
+                                const cairo_pattern_t *other)
+{
+    int size;
+
+    assert (other->status == CAIRO_STATUS_SUCCESS);
+
+    switch (other->type) {
+    default:
+       ASSERT_NOT_REACHED;
+    case CAIRO_PATTERN_TYPE_SOLID:
+       size = sizeof (cairo_solid_pattern_t);
+       break;
+    case CAIRO_PATTERN_TYPE_SURFACE:
+       size = sizeof (cairo_surface_pattern_t);
+       break;
+    case CAIRO_PATTERN_TYPE_LINEAR:
+       size = sizeof (cairo_linear_pattern_t);
+       break;
+    case CAIRO_PATTERN_TYPE_RADIAL:
+       size = sizeof (cairo_radial_pattern_t);
+       break;
+    case CAIRO_PATTERN_TYPE_MESH:
+       size = sizeof (cairo_mesh_pattern_t);
+       break;
+    }
+
+    memcpy (pattern, other, size);
+
+    CAIRO_REFERENCE_COUNT_INIT (&pattern->ref_count, 0);
+    _cairo_user_data_array_init (&pattern->user_data);
+}
+
+cairo_status_t
+_cairo_pattern_init_snapshot (cairo_pattern_t       *pattern,
+                             const cairo_pattern_t *other)
+{
+    cairo_status_t status;
+
+    /* We don't bother doing any fancy copy-on-write implementation
+     * for the pattern's data. It's generally quite tiny. */
+    status = _cairo_pattern_init_copy (pattern, other);
+    if (unlikely (status))
+       return status;
+
+    /* But we do let the surface snapshot stuff be as fancy as it
+     * would like to be. */
+    if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) {
+       cairo_surface_pattern_t *surface_pattern =
+           (cairo_surface_pattern_t *) pattern;
+       cairo_surface_t *surface = surface_pattern->surface;
+
+       surface_pattern->surface = _cairo_surface_snapshot (surface);
+
+       cairo_surface_destroy (surface);
+
+       if (surface_pattern->surface->status)
+           return surface_pattern->surface->status;
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+void
+_cairo_pattern_fini (cairo_pattern_t *pattern)
+{
+    _cairo_user_data_array_fini (&pattern->user_data);
+
+    switch (pattern->type) {
+    case CAIRO_PATTERN_TYPE_SOLID:
+       break;
+    case CAIRO_PATTERN_TYPE_SURFACE: {
+       cairo_surface_pattern_t *surface_pattern =
+           (cairo_surface_pattern_t *) pattern;
+
+       cairo_surface_destroy (surface_pattern->surface);
+    } break;
+    case CAIRO_PATTERN_TYPE_LINEAR:
+    case CAIRO_PATTERN_TYPE_RADIAL: {
+       cairo_gradient_pattern_t *gradient =
+           (cairo_gradient_pattern_t *) pattern;
+
+       if (gradient->stops && gradient->stops != gradient->stops_embedded)
+           free (gradient->stops);
+    } break;
+    case CAIRO_PATTERN_TYPE_MESH: {
+       cairo_mesh_pattern_t *mesh =
+           (cairo_mesh_pattern_t *) pattern;
+
+       _cairo_array_fini (&mesh->patches);
+    } break;
+    }
+
+#if HAVE_VALGRIND
+    switch (pattern->type) {
+    case CAIRO_PATTERN_TYPE_SOLID:
+       VALGRIND_MAKE_MEM_NOACCESS (pattern, sizeof (cairo_solid_pattern_t));
+       break;
+    case CAIRO_PATTERN_TYPE_SURFACE:
+       VALGRIND_MAKE_MEM_NOACCESS (pattern, sizeof (cairo_surface_pattern_t));
+       break;
+    case CAIRO_PATTERN_TYPE_LINEAR:
+       VALGRIND_MAKE_MEM_NOACCESS (pattern, sizeof (cairo_linear_pattern_t));
+       break;
+    case CAIRO_PATTERN_TYPE_RADIAL:
+       VALGRIND_MAKE_MEM_NOACCESS (pattern, sizeof (cairo_radial_pattern_t));
+       break;
+    case CAIRO_PATTERN_TYPE_MESH:
+       VALGRIND_MAKE_MEM_NOACCESS (pattern, sizeof (cairo_mesh_pattern_t));
+       break;
+    }
+#endif
+}
+
+cairo_status_t
+_cairo_pattern_create_copy (cairo_pattern_t      **pattern_out,
+                           const cairo_pattern_t  *other)
+{
+    cairo_pattern_t *pattern;
+    cairo_status_t status;
+
+    if (other->status)
+       return other->status;
+
+    switch (other->type) {
+    case CAIRO_PATTERN_TYPE_SOLID:
+       pattern = malloc (sizeof (cairo_solid_pattern_t));
+       break;
+    case CAIRO_PATTERN_TYPE_SURFACE:
+       pattern = malloc (sizeof (cairo_surface_pattern_t));
+       break;
+    case CAIRO_PATTERN_TYPE_LINEAR:
+       pattern = malloc (sizeof (cairo_linear_pattern_t));
+       break;
+    case CAIRO_PATTERN_TYPE_RADIAL:
+       pattern = malloc (sizeof (cairo_radial_pattern_t));
+       break;
+    case CAIRO_PATTERN_TYPE_MESH:
+       pattern = malloc (sizeof (cairo_mesh_pattern_t));
+       break;
+    default:
+       ASSERT_NOT_REACHED;
+       return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH);
+    }
+    if (unlikely (pattern == NULL))
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    status = _cairo_pattern_init_copy (pattern, other);
+    if (unlikely (status)) {
+       free (pattern);
+       return status;
+    }
+
+    CAIRO_REFERENCE_COUNT_INIT (&pattern->ref_count, 1);
+    *pattern_out = pattern;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+void
+_cairo_pattern_init_solid (cairo_solid_pattern_t *pattern,
+                          const cairo_color_t   *color)
+{
+    _cairo_pattern_init (&pattern->base, CAIRO_PATTERN_TYPE_SOLID);
+    pattern->color = *color;
+}
+
+void
+_cairo_pattern_init_for_surface (cairo_surface_pattern_t *pattern,
+                                cairo_surface_t         *surface)
+{
+    if (surface->status) {
+       /* Force to solid to simplify the pattern_fini process. */
+       _cairo_pattern_init (&pattern->base, CAIRO_PATTERN_TYPE_SOLID);
+       _cairo_pattern_set_error (&pattern->base, surface->status);
+       return;
+    }
+
+    _cairo_pattern_init (&pattern->base, CAIRO_PATTERN_TYPE_SURFACE);
+
+    pattern->surface = cairo_surface_reference (surface);
+}
+
+static void
+_cairo_pattern_init_gradient (cairo_gradient_pattern_t *pattern,
+                             cairo_pattern_type_t     type)
+{
+    _cairo_pattern_init (&pattern->base, type);
+
+    pattern->n_stops    = 0;
+    pattern->stops_size = 0;
+    pattern->stops      = NULL;
+}
+
+static void
+_cairo_pattern_init_linear (cairo_linear_pattern_t *pattern,
+                           double x0, double y0, double x1, double y1)
+{
+    _cairo_pattern_init_gradient (&pattern->base, CAIRO_PATTERN_TYPE_LINEAR);
+
+    pattern->pd1.x = x0;
+    pattern->pd1.y = y0;
+    pattern->pd2.x = x1;
+    pattern->pd2.y = y1;
+}
+
+static void
+_cairo_pattern_init_radial (cairo_radial_pattern_t *pattern,
+                           double cx0, double cy0, double radius0,
+                           double cx1, double cy1, double radius1)
+{
+    _cairo_pattern_init_gradient (&pattern->base, CAIRO_PATTERN_TYPE_RADIAL);
+
+    pattern->cd1.center.x = cx0;
+    pattern->cd1.center.y = cy0;
+    pattern->cd1.radius   = fabs (radius0);
+    pattern->cd2.center.x = cx1;
+    pattern->cd2.center.y = cy1;
+    pattern->cd2.radius   = fabs (radius1);
+}
+
+cairo_pattern_t *
+_cairo_pattern_create_solid (const cairo_color_t *color)
+{
+    cairo_solid_pattern_t *pattern;
+
+    pattern =
+       _freed_pool_get (&freed_pattern_pool[CAIRO_PATTERN_TYPE_SOLID]);
+    if (unlikely (pattern == NULL)) {
+       /* None cached, need to create a new pattern. */
+       pattern = malloc (sizeof (cairo_solid_pattern_t));
+       if (unlikely (pattern == NULL)) {
+           _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+           return (cairo_pattern_t *) &_cairo_pattern_nil;
+       }
+    }
+
+    _cairo_pattern_init_solid (pattern, color);
+    CAIRO_REFERENCE_COUNT_INIT (&pattern->base.ref_count, 1);
+
+    return &pattern->base;
+}
+
+cairo_pattern_t *
+_cairo_pattern_create_in_error (cairo_status_t status)
+{
+    cairo_pattern_t *pattern;
+
+    if (status == CAIRO_STATUS_NO_MEMORY)
+       return (cairo_pattern_t *)&_cairo_pattern_nil.base;
+
+    CAIRO_MUTEX_INITIALIZE ();
+
+    pattern = _cairo_pattern_create_solid (CAIRO_COLOR_BLACK);
+    if (pattern->status == CAIRO_STATUS_SUCCESS)
+       status = _cairo_pattern_set_error (pattern, status);
+
+    return pattern;
+}
+
+/**
+ * cairo_pattern_create_rgb:
+ * @red: red component of the color
+ * @green: green component of the color
+ * @blue: blue component of the color
+ *
+ * Creates a new #cairo_pattern_t corresponding to an opaque color.  The
+ * color components are floating point numbers in the range 0 to 1.
+ * If the values passed in are outside that range, they will be
+ * clamped.
+ *
+ * Return value: the newly created #cairo_pattern_t if successful, or
+ * an error pattern in case of no memory.  The caller owns the
+ * returned object and should call cairo_pattern_destroy() when
+ * finished with it.
+ *
+ * This function will always return a valid pointer, but if an error
+ * occurred the pattern status will be set to an error.  To inspect
+ * the status of a pattern use cairo_pattern_status().
+ **/
+cairo_pattern_t *
+cairo_pattern_create_rgb (double red, double green, double blue)
+{
+    return cairo_pattern_create_rgba (red, green, blue, 1.0);
+}
+slim_hidden_def (cairo_pattern_create_rgb);
+
+/**
+ * cairo_pattern_create_rgba:
+ * @red: red component of the color
+ * @green: green component of the color
+ * @blue: blue component of the color
+ * @alpha: alpha component of the color
+ *
+ * Creates a new #cairo_pattern_t corresponding to a translucent color.
+ * The color components are floating point numbers in the range 0 to
+ * 1.  If the values passed in are outside that range, they will be
+ * clamped.
+ *
+ * Return value: the newly created #cairo_pattern_t if successful, or
+ * an error pattern in case of no memory.  The caller owns the
+ * returned object and should call cairo_pattern_destroy() when
+ * finished with it.
+ *
+ * This function will always return a valid pointer, but if an error
+ * occurred the pattern status will be set to an error.  To inspect
+ * the status of a pattern use cairo_pattern_status().
+ **/
+cairo_pattern_t *
+cairo_pattern_create_rgba (double red, double green, double blue,
+                          double alpha)
+{
+    cairo_color_t color;
+
+    red   = _cairo_restrict_value (red,   0.0, 1.0);
+    green = _cairo_restrict_value (green, 0.0, 1.0);
+    blue  = _cairo_restrict_value (blue,  0.0, 1.0);
+    alpha = _cairo_restrict_value (alpha, 0.0, 1.0);
+
+    _cairo_color_init_rgba (&color, red, green, blue, alpha);
+
+    CAIRO_MUTEX_INITIALIZE ();
+
+    return _cairo_pattern_create_solid (&color);
+}
+slim_hidden_def (cairo_pattern_create_rgba);
+
+/**
+ * cairo_pattern_create_for_surface:
+ * @surface: the surface
+ *
+ * Create a new #cairo_pattern_t for the given surface.
+ *
+ * Return value: the newly created #cairo_pattern_t if successful, or
+ * an error pattern in case of no memory.  The caller owns the
+ * returned object and should call cairo_pattern_destroy() when
+ * finished with it.
+ *
+ * This function will always return a valid pointer, but if an error
+ * occurred the pattern status will be set to an error.  To inspect
+ * the status of a pattern use cairo_pattern_status().
+ **/
+cairo_pattern_t *
+cairo_pattern_create_for_surface (cairo_surface_t *surface)
+{
+    cairo_surface_pattern_t *pattern;
+
+    if (surface == NULL) {
+       _cairo_error_throw (CAIRO_STATUS_NULL_POINTER);
+       return (cairo_pattern_t*) &_cairo_pattern_nil_null_pointer;
+    }
+
+    if (surface->status)
+       return _cairo_pattern_create_in_error (surface->status);
+
+    pattern =
+       _freed_pool_get (&freed_pattern_pool[CAIRO_PATTERN_TYPE_SURFACE]);
+    if (unlikely (pattern == NULL)) {
+       pattern = malloc (sizeof (cairo_surface_pattern_t));
+       if (unlikely (pattern == NULL)) {
+           _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+           return (cairo_pattern_t *)&_cairo_pattern_nil.base;
+       }
+    }
+
+    CAIRO_MUTEX_INITIALIZE ();
+
+    _cairo_pattern_init_for_surface (pattern, surface);
+    CAIRO_REFERENCE_COUNT_INIT (&pattern->base.ref_count, 1);
+
+    return &pattern->base;
+}
+slim_hidden_def (cairo_pattern_create_for_surface);
+
+/**
+ * cairo_pattern_create_linear:
+ * @x0: x coordinate of the start point
+ * @y0: y coordinate of the start point
+ * @x1: x coordinate of the end point
+ * @y1: y coordinate of the end point
+ *
+ * Create a new linear gradient #cairo_pattern_t along the line defined
+ * by (x0, y0) and (x1, y1).  Before using the gradient pattern, a
+ * number of color stops should be defined using
+ * cairo_pattern_add_color_stop_rgb() or
+ * cairo_pattern_add_color_stop_rgba().
+ *
+ * Note: The coordinates here are in pattern space. For a new pattern,
+ * pattern space is identical to user space, but the relationship
+ * between the spaces can be changed with cairo_pattern_set_matrix().
+ *
+ * Return value: the newly created #cairo_pattern_t if successful, or
+ * an error pattern in case of no memory.  The caller owns the
+ * returned object and should call cairo_pattern_destroy() when
+ * finished with it.
+ *
+ * This function will always return a valid pointer, but if an error
+ * occurred the pattern status will be set to an error.  To inspect
+ * the status of a pattern use cairo_pattern_status().
+ **/
+cairo_pattern_t *
+cairo_pattern_create_linear (double x0, double y0, double x1, double y1)
+{
+    cairo_linear_pattern_t *pattern;
+
+    pattern =
+       _freed_pool_get (&freed_pattern_pool[CAIRO_PATTERN_TYPE_LINEAR]);
+    if (unlikely (pattern == NULL)) {
+       pattern = malloc (sizeof (cairo_linear_pattern_t));
+       if (unlikely (pattern == NULL)) {
+           _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+           return (cairo_pattern_t *) &_cairo_pattern_nil.base;
+       }
+    }
+
+    CAIRO_MUTEX_INITIALIZE ();
+
+    _cairo_pattern_init_linear (pattern, x0, y0, x1, y1);
+    CAIRO_REFERENCE_COUNT_INIT (&pattern->base.base.ref_count, 1);
+
+    return &pattern->base.base;
+}
+
+/**
+ * cairo_pattern_create_radial:
+ * @cx0: x coordinate for the center of the start circle
+ * @cy0: y coordinate for the center of the start circle
+ * @radius0: radius of the start circle
+ * @cx1: x coordinate for the center of the end circle
+ * @cy1: y coordinate for the center of the end circle
+ * @radius1: radius of the end circle
+ *
+ * Creates a new radial gradient #cairo_pattern_t between the two
+ * circles defined by (cx0, cy0, radius0) and (cx1, cy1, radius1).  Before using the
+ * gradient pattern, a number of color stops should be defined using
+ * cairo_pattern_add_color_stop_rgb() or
+ * cairo_pattern_add_color_stop_rgba().
+ *
+ * Note: The coordinates here are in pattern space. For a new pattern,
+ * pattern space is identical to user space, but the relationship
+ * between the spaces can be changed with cairo_pattern_set_matrix().
+ *
+ * Return value: the newly created #cairo_pattern_t if successful, or
+ * an error pattern in case of no memory.  The caller owns the
+ * returned object and should call cairo_pattern_destroy() when
+ * finished with it.
+ *
+ * This function will always return a valid pointer, but if an error
+ * occurred the pattern status will be set to an error.  To inspect
+ * the status of a pattern use cairo_pattern_status().
+ **/
+cairo_pattern_t *
+cairo_pattern_create_radial (double cx0, double cy0, double radius0,
+                            double cx1, double cy1, double radius1)
+{
+    cairo_radial_pattern_t *pattern;
+
+    pattern =
+       _freed_pool_get (&freed_pattern_pool[CAIRO_PATTERN_TYPE_RADIAL]);
+    if (unlikely (pattern == NULL)) {
+       pattern = malloc (sizeof (cairo_radial_pattern_t));
+       if (unlikely (pattern == NULL)) {
+           _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+           return (cairo_pattern_t *) &_cairo_pattern_nil.base;
+       }
+    }
+
+    CAIRO_MUTEX_INITIALIZE ();
+
+    _cairo_pattern_init_radial (pattern, cx0, cy0, radius0, cx1, cy1, radius1);
+    CAIRO_REFERENCE_COUNT_INIT (&pattern->base.base.ref_count, 1);
+
+    return &pattern->base.base;
+}
+
+/* This order is specified in the diagram in the documentation for
+ * cairo_pattern_create_mesh() */
+static const int mesh_path_point_i[12] = { 0, 0, 0, 0, 1, 2, 3, 3, 3, 3, 2, 1 };
+static const int mesh_path_point_j[12] = { 0, 1, 2, 3, 3, 3, 3, 2, 1, 0, 0, 0 };
+static const int mesh_control_point_i[4] = { 1, 1, 2, 2 };
+static const int mesh_control_point_j[4] = { 1, 2, 2, 1 };
+
+/**
+ * cairo_pattern_create_mesh:
+ *
+ * Create a new mesh pattern.
+ *
+ * Mesh patterns are tensor-product patch meshes (type 7 shadings in
+ * PDF). Mesh patterns may also be used to create other types of
+ * shadings that are special cases of tensor-product patch meshes such
+ * as Coons patch meshes (type 6 shading in PDF) and Gouraud-shaded
+ * triangle meshes (type 4 and 5 shadings in PDF).
+ *
+ * Mesh patterns consist of one or more tensor-product patches, which
+ * should be defined before using the mesh pattern. Using a mesh
+ * pattern with a partially defined patch as source or mask will put
+ * the context in an error status with a status of
+ * %CAIRO_STATUS_INVALID_MESH_CONSTRUCTION.
+ *
+ * A tensor-product patch is defined by 4 Bézier curves (side 0, 1, 2,
+ * 3) and by 4 additional control points (P0, P1, P2, P3) that provide
+ * further control over the patch and complete the definition of the
+ * tensor-product patch. The corner C0 is the first point of the
+ * patch.
+ *
+ * Degenerate sides are permitted so straight lines may be used. A
+ * zero length line on one side may be used to create 3 sided patches.
+ *
+ * <informalexample><programlisting>
+ *       C1     Side 1       C2
+ *        +---------------+
+ *        |               |
+ *        |  P1       P2  |
+ *        |               |
+ * Side 0 |               | Side 2
+ *        |               |
+ *        |               |
+ *        |  P0       P3  |
+ *        |               |
+ *        +---------------+
+ *      C0     Side 3        C3
+ * </programlisting></informalexample>
+ *
+ * Each patch is constructed by first calling
+ * cairo_mesh_pattern_begin_patch(), then cairo_mesh_pattern_move_to()
+ * to specify the first point in the patch (C0). Then the sides are
+ * specified with calls to cairo_mesh_pattern_curve_to() and
+ * cairo_mesh_pattern_line_to().
+ *
+ * The four additional control points (P0, P1, P2, P3) in a patch can
+ * be specified with cairo_mesh_pattern_set_control_point().
+ *
+ * At each corner of the patch (C0, C1, C2, C3) a color may be
+ * specified with cairo_mesh_pattern_set_corner_color_rgb() or
+ * cairo_mesh_pattern_set_corner_color_rgba(). Any corner whose color
+ * is not explicitly specified defaults to transparent black.
+ *
+ * A Coons patch is a special case of the tensor-product patch where
+ * the control points are implicitly defined by the sides of the
+ * patch. The default value for any control point not specified is the
+ * implicit value for a Coons patch, i.e. if no control points are
+ * specified the patch is a Coons patch.
+ *
+ * A triangle is a special case of the tensor-product patch where the
+ * control points are implicitly defined by the sides of the patch,
+ * all the sides are lines and one of them has length 0, i.e. if the
+ * patch is specified using just 3 lines, it is a triangle. If the
+ * corners connected by the 0-length side have the same color, the
+ * patch is a Gouraud-shaded triangle.
+ *
+ * Patches may be oriented differently to the above diagram. For
+ * example the first point could be at the top left. The diagram only
+ * shows the relationship between the sides, corners and control
+ * points. Regardless of where the first point is located, when
+ * specifying colors, corner 0 will always be the first point, corner
+ * 1 the point between side 0 and side 1 etc.
+ *
+ * Calling cairo_mesh_pattern_end_patch() completes the current
+ * patch. If less than 4 sides have been defined, the first missing
+ * side is defined as a line from the current point to the first point
+ * of the patch (C0) and the other sides are degenerate lines from C0
+ * to C0. The corners between the added sides will all be coincident
+ * with C0 of the patch and their color will be set to be the same as
+ * the color of C0.
+ *
+ * Additional patches may be added with additional calls to
+ * cairo_mesh_pattern_begin_patch()/cairo_mesh_pattern_end_patch().
+ *
+ * <informalexample><programlisting>
+ * cairo_pattern_t *mesh = cairo_mesh_pattern_create_mesh ();
+ *
+ * /&ast; Add a Coons patch &ast;/
+ * cairo_mesh_pattern_begin_patch (mesh);
+ * cairo_mesh_pattern_move_to (pattern, 0, 0);
+ * cairo_mesh_pattern_curve_to (pattern, 30, -30,  60,  30, 100, 0);
+ * cairo_mesh_pattern_curve_to (pattern, 60,  30, 130,  60, 100, 100);
+ * cairo_mesh_pattern_curve_to (pattern, 60,  70,  30, 130,   0, 100);
+ * cairo_mesh_pattern_curve_to (pattern, 30,  70, -30,  30,   0, 0);
+ * cairo_mesh_pattern_set_corner_color_rgb (pattern, 0, 1, 0, 0);
+ * cairo_mesh_pattern_set_corner_color_rgb (pattern, 1, 0, 1, 0);
+ * cairo_mesh_pattern_set_corner_color_rgb (pattern, 2, 0, 0, 1);
+ * cairo_mesh_pattern_set_corner_color_rgb (pattern, 3, 1, 1, 0);
+ * cairo_mesh_pattern_end_patch (mesh);
+ *
+ * /&ast; Add a Gouraud-shaded triangle &ast;/
+ * cairo_mesh_pattern_begin_patch (mesh)
+ * cairo_mesh_pattern_move_to (pattern, 100, 100);
+ * cairo_mesh_pattern_line_to (pattern, 130, 130);
+ * cairo_mesh_pattern_line_to (pattern, 130,  70);
+ * cairo_mesh_pattern_set_corner_color_rgb (pattern, 0, 1, 0, 0);
+ * cairo_mesh_pattern_set_corner_color_rgb (pattern, 1, 0, 1, 0);
+ * cairo_mesh_pattern_set_corner_color_rgb (pattern, 2, 0, 0, 1);
+ * cairo_mesh_pattern_end_patch (mesh)
+ * </programlisting></informalexample>
+ *
+ * When two patches overlap, the last one that has been added is drawn
+ * over the first one.
+ *
+ * When a patch folds over itself, points are sorted depending on
+ * their parameter coordinates inside the patch. The v coordinate
+ * ranges from 0 to 1 when moving from side 3 to side 1; the u
+ * coordinate ranges from 0 to 1 when going from side 0 to side
+ * 2. Points with higher v coordinate hide points with lower v
+ * coordinate. When two points have the same v coordinate, the one
+ * with higher u coordinate is above. This means that points nearer to
+ * side 1 are above points nearer to side 3; when this is not
+ * sufficient to decide which point is above (for example when both
+ * points belong to side 1 or side 3) points nearer to side 2 are
+ * above points nearer to side 0.
+ *
+ * For a complete definition of tensor-product patches, see the PDF
+ * specification (ISO32000), which describes the parametrization in
+ * detail.
+ *
+ * Note: The coordinates are always in pattern space. For a new
+ * pattern, pattern space is identical to user space, but the
+ * relationship between the spaces can be changed with
+ * cairo_pattern_set_matrix().
+ *
+ * Return value: the newly created #cairo_pattern_t if successful, or
+ * an error pattern in case of no memory. The caller owns the returned
+ * object and should call cairo_pattern_destroy() when finished with
+ * it.
+ *
+ * This function will always return a valid pointer, but if an error
+ * occurred the pattern status will be set to an error. To inspect the
+ * status of a pattern use cairo_pattern_status().
+ *
+ * Since: 1.12
+ **/
+cairo_pattern_t *
+cairo_pattern_create_mesh (void)
+{
+    cairo_mesh_pattern_t *pattern;
+
+    pattern =
+       _freed_pool_get (&freed_pattern_pool[CAIRO_PATTERN_TYPE_MESH]);
+    if (unlikely (pattern == NULL)) {
+       pattern = malloc (sizeof (cairo_mesh_pattern_t));
+       if (unlikely (pattern == NULL)) {
+           _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+           return (cairo_pattern_t *) &_cairo_pattern_nil.base;
+       }
+    }
+
+    CAIRO_MUTEX_INITIALIZE ();
+
+    _cairo_pattern_init (&pattern->base, CAIRO_PATTERN_TYPE_MESH);
+    _cairo_array_init (&pattern->patches, sizeof (cairo_mesh_patch_t));
+    pattern->current_patch = NULL;
+    CAIRO_REFERENCE_COUNT_INIT (&pattern->base.ref_count, 1);
+
+    return &pattern->base;
+}
+
+/**
+ * cairo_pattern_reference:
+ * @pattern: a #cairo_pattern_t
+ *
+ * Increases the reference count on @pattern by one. This prevents
+ * @pattern from being destroyed until a matching call to
+ * cairo_pattern_destroy() is made.
+ *
+ * The number of references to a #cairo_pattern_t can be get using
+ * cairo_pattern_get_reference_count().
+ *
+ * Return value: the referenced #cairo_pattern_t.
+ **/
+cairo_pattern_t *
+cairo_pattern_reference (cairo_pattern_t *pattern)
+{
+    if (pattern == NULL ||
+           CAIRO_REFERENCE_COUNT_IS_INVALID (&pattern->ref_count))
+       return pattern;
+
+    assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&pattern->ref_count));
+
+    _cairo_reference_count_inc (&pattern->ref_count);
+
+    return pattern;
+}
+slim_hidden_def (cairo_pattern_reference);
+
+/**
+ * cairo_pattern_get_type:
+ * @pattern: a #cairo_pattern_t
+ *
+ * This function returns the type a pattern.
+ * See #cairo_pattern_type_t for available types.
+ *
+ * Return value: The type of @pattern.
+ *
+ * Since: 1.2
+ **/
+cairo_pattern_type_t
+cairo_pattern_get_type (cairo_pattern_t *pattern)
+{
+    return pattern->type;
+}
+
+/**
+ * cairo_pattern_status:
+ * @pattern: a #cairo_pattern_t
+ *
+ * Checks whether an error has previously occurred for this
+ * pattern.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS, %CAIRO_STATUS_NO_MEMORY,
+ * %CAIRO_STATUS_INVALID_MATRIX, %CAIRO_STATUS_PATTERN_TYPE_MISMATCH,
+ * or %CAIRO_STATUS_INVALID_MESH_CONSTRUCTION.
+ **/
+cairo_status_t
+cairo_pattern_status (cairo_pattern_t *pattern)
+{
+    return pattern->status;
+}
+
+/**
+ * cairo_pattern_destroy:
+ * @pattern: a #cairo_pattern_t
+ *
+ * Decreases the reference count on @pattern by one. If the result is
+ * zero, then @pattern and all associated resources are freed.  See
+ * cairo_pattern_reference().
+ **/
+void
+cairo_pattern_destroy (cairo_pattern_t *pattern)
+{
+    cairo_pattern_type_t type;
+
+    if (pattern == NULL ||
+           CAIRO_REFERENCE_COUNT_IS_INVALID (&pattern->ref_count))
+       return;
+
+    assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&pattern->ref_count));
+
+    if (! _cairo_reference_count_dec_and_test (&pattern->ref_count))
+       return;
+
+    type = pattern->type;
+    _cairo_pattern_fini (pattern);
+
+    /* maintain a small cache of freed patterns */
+    _freed_pool_put (&freed_pattern_pool[type], pattern);
+}
+slim_hidden_def (cairo_pattern_destroy);
+
+/**
+ * cairo_pattern_get_reference_count:
+ * @pattern: a #cairo_pattern_t
+ *
+ * Returns the current reference count of @pattern.
+ *
+ * Return value: the current reference count of @pattern.  If the
+ * object is a nil object, 0 will be returned.
+ *
+ * Since: 1.4
+ **/
+unsigned int
+cairo_pattern_get_reference_count (cairo_pattern_t *pattern)
+{
+    if (pattern == NULL ||
+           CAIRO_REFERENCE_COUNT_IS_INVALID (&pattern->ref_count))
+       return 0;
+
+    return CAIRO_REFERENCE_COUNT_GET_VALUE (&pattern->ref_count);
+}
+
+/**
+ * cairo_pattern_get_user_data:
+ * @pattern: a #cairo_pattern_t
+ * @key: the address of the #cairo_user_data_key_t the user data was
+ * attached to
+ *
+ * Return user data previously attached to @pattern using the
+ * specified key.  If no user data has been attached with the given
+ * key this function returns %NULL.
+ *
+ * Return value: the user data previously attached or %NULL.
+ *
+ * Since: 1.4
+ **/
+void *
+cairo_pattern_get_user_data (cairo_pattern_t            *pattern,
+                            const cairo_user_data_key_t *key)
+{
+    return _cairo_user_data_array_get_data (&pattern->user_data,
+                                           key);
+}
+
+/**
+ * cairo_pattern_set_user_data:
+ * @pattern: a #cairo_pattern_t
+ * @key: the address of a #cairo_user_data_key_t to attach the user data to
+ * @user_data: the user data to attach to the #cairo_pattern_t
+ * @destroy: a #cairo_destroy_func_t which will be called when the
+ * #cairo_t is destroyed or when new user data is attached using the
+ * same key.
+ *
+ * Attach user data to @pattern.  To remove user data from a surface,
+ * call this function with the key that was used to set it and %NULL
+ * for @data.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY if a
+ * slot could not be allocated for the user data.
+ *
+ * Since: 1.4
+ **/
+cairo_status_t
+cairo_pattern_set_user_data (cairo_pattern_t            *pattern,
+                            const cairo_user_data_key_t *key,
+                            void                        *user_data,
+                            cairo_destroy_func_t         destroy)
+{
+    if (CAIRO_REFERENCE_COUNT_IS_INVALID (&pattern->ref_count))
+       return pattern->status;
+
+    return _cairo_user_data_array_set_data (&pattern->user_data,
+                                           key, user_data, destroy);
+}
+
+/**
+ * cairo_mesh_pattern_begin_patch:
+ * @pattern: a #cairo_pattern_t
+ *
+ * Begin a patch in a mesh pattern.
+ *
+ * After calling this function, the patch shape should be defined with
+ * cairo_mesh_pattern_move_to(), cairo_mesh_pattern_line_to() and
+ * cairo_mesh_pattern_curve_to().
+ *
+ * After defining the patch, cairo_mesh_pattern_end_patch() must be
+ * called before using @pattern as a source or mask.
+ *
+ * Note: If @pattern is not a mesh pattern then @pattern will be put
+ * into an error status with a status of
+ * %CAIRO_STATUS_PATTERN_TYPE_MISMATCH. If @pattern already has a
+ * current patch, it will be put into an error status with a status of
+ * %CAIRO_STATUS_INVALID_MESH_CONSTRUCTION.
+ *
+ * Since: 1.12
+ **/
+void
+cairo_mesh_pattern_begin_patch (cairo_pattern_t *pattern)
+{
+    cairo_mesh_pattern_t *mesh;
+    cairo_status_t status;
+    cairo_mesh_patch_t *current_patch;
+    int i;
+
+    if (unlikely (pattern->status))
+       return;
+
+    if (unlikely (pattern->type != CAIRO_PATTERN_TYPE_MESH)) {
+       _cairo_pattern_set_error (pattern, CAIRO_STATUS_PATTERN_TYPE_MISMATCH);
+       return;
+    }
+
+    mesh = (cairo_mesh_pattern_t *) pattern;
+    if (unlikely (mesh->current_patch)) {
+       _cairo_pattern_set_error (pattern, CAIRO_STATUS_INVALID_MESH_CONSTRUCTION);
+       return;
+    }
+
+    status = _cairo_array_allocate (&mesh->patches, 1, (void **) &current_patch);
+    if (unlikely (status)) {
+       _cairo_pattern_set_error (pattern, status);
+       return;
+    }
+
+    mesh->current_patch = current_patch;
+    mesh->current_side = -2; /* no current point */
+
+    for (i = 0; i < 4; i++)
+       mesh->has_control_point[i] = FALSE;
+
+    for (i = 0; i < 4; i++)
+       mesh->has_color[i] = FALSE;
+}
+
+
+static void
+_calc_control_point (cairo_mesh_patch_t *patch, int control_point)
+{
+    /* The Coons patch is a special case of the Tensor Product patch
+     * where the four control points are:
+     *
+     * P11 = S(1/3, 1/3)
+     * P12 = S(1/3, 2/3)
+     * P21 = S(2/3, 1/3)
+     * P22 = S(2/3, 2/3)
+     *
+     * where S is the gradient surface.
+     *
+     * When one or more control points has not been specified
+     * calculated the Coons patch control points are substituted. If
+     * no control points are specified the gradient will be a Coons
+     * patch.
+     *
+     * The equations below are defined in the ISO32000 standard.
+     */
+    cairo_point_double_t *p[3][3];
+    int cp_i, cp_j, i, j;
+
+    cp_i = mesh_control_point_i[control_point];
+    cp_j = mesh_control_point_j[control_point];
+
+    for (i = 0; i < 3; i++)
+       for (j = 0; j < 3; j++)
+           p[i][j] = &patch->points[cp_i ^ i][cp_j ^ j];
+
+    p[0][0]->x = (- 4 * p[1][1]->x
+                 + 6 * (p[1][0]->x + p[0][1]->x)
+                 - 2 * (p[1][2]->x + p[2][1]->x)
+                 + 3 * (p[2][0]->x + p[0][2]->x)
+                 - 1 * p[2][2]->x) * (1. / 9);
+
+    p[0][0]->y = (- 4 * p[1][1]->y
+                 + 6 * (p[1][0]->y + p[0][1]->y)
+                 - 2 * (p[1][2]->y + p[2][1]->y)
+                 + 3 * (p[2][0]->y + p[0][2]->y)
+                 - 1 * p[2][2]->y) * (1. / 9);
+}
+
+/**
+ * cairo_mesh_pattern_end_patch:
+ * @pattern: a #cairo_pattern_t
+ *
+ * Indicates the end of the current patch in a mesh pattern.
+ *
+ * If the current patch has less than 4 sides, it is closed with a
+ * straight line from the current point to the first point of the
+ * patch as if cairo_mesh_pattern_line_to() was used.
+ *
+ * Note: If @pattern is not a mesh pattern then @pattern will be put
+ * into an error status with a status of
+ * %CAIRO_STATUS_PATTERN_TYPE_MISMATCH. If @pattern has no current
+ * patch or the current patch has no current point, @pattern will be
+ * put into an error status with a status of
+ * %CAIRO_STATUS_INVALID_MESH_CONSTRUCTION.
+ *
+ * Since: 1.12
+ **/
+void
+cairo_mesh_pattern_end_patch (cairo_pattern_t *pattern)
+{
+    cairo_mesh_pattern_t *mesh;
+    cairo_mesh_patch_t *current_patch;
+    int i;
+
+    if (unlikely (pattern->status))
+       return;
+
+    if (unlikely (pattern->type != CAIRO_PATTERN_TYPE_MESH)) {
+       _cairo_pattern_set_error (pattern, CAIRO_STATUS_PATTERN_TYPE_MISMATCH);
+       return;
+    }
+
+    mesh = (cairo_mesh_pattern_t *) pattern;
+    current_patch = mesh->current_patch;
+    if (unlikely (!current_patch)) {
+       _cairo_pattern_set_error (pattern, CAIRO_STATUS_INVALID_MESH_CONSTRUCTION);
+       return;
+    }
+
+    if (unlikely (mesh->current_side == -2)) {
+       _cairo_pattern_set_error (pattern, CAIRO_STATUS_INVALID_MESH_CONSTRUCTION);
+       return;
+    }
+
+    while (mesh->current_side < 3) {
+       int corner_num;
+
+       cairo_mesh_pattern_line_to (pattern,
+                                   current_patch->points[0][0].x,
+                                   current_patch->points[0][0].y);
+
+       corner_num = mesh->current_side + 1;
+       if (corner_num < 4 && ! mesh->has_color[corner_num]) {
+           current_patch->colors[corner_num] = current_patch->colors[0];
+           mesh->has_color[corner_num] = TRUE;
+       }
+    }
+
+    for (i = 0; i < 4; i++) {
+       if (! mesh->has_control_point[i])
+           _calc_control_point (current_patch, i);
+    }
+
+    for (i = 0; i < 4; i++) {
+       if (! mesh->has_color[i])
+           current_patch->colors[i] = *CAIRO_COLOR_TRANSPARENT;
+    }
+
+    mesh->current_patch = NULL;
+}
+
+/**
+ * cairo_mesh_pattern_curve_to:
+ * @pattern: a #cairo_pattern_t
+ * @x1: the X coordinate of the first control point
+ * @y1: the Y coordinate of the first control point
+ * @x2: the X coordinate of the second control point
+ * @y2: the Y coordinate of the second control point
+ * @x3: the X coordinate of the end of the curve
+ * @y3: the Y coordinate of the end of the curve
+ *
+ * Adds a cubic Bézier spline to the current patch from the current
+ * point to position (@x3, @y3) in pattern-space coordinates, using
+ * (@x1, @y1) and (@x2, @y2) as the control points.
+ *
+ * If the current patch has no current point before the call to
+ * cairo_mesh_pattern_curve_to(), this function will behave as if
+ * preceded by a call to cairo_mesh_pattern_move_to(@pattern, @x1,
+ * @y1).
+ *
+ * After this call the current point will be (@x3, @y3).
+ *
+ * Note: If @pattern is not a mesh pattern then @pattern will be put
+ * into an error status with a status of
+ * %CAIRO_STATUS_PATTERN_TYPE_MISMATCH. If @pattern has no current
+ * patch or the current patch already has 4 sides, @pattern will be
+ * put into an error status with a status of
+ * %CAIRO_STATUS_INVALID_MESH_CONSTRUCTION.
+ *
+ * Since: 1.12
+ **/
+void
+cairo_mesh_pattern_curve_to (cairo_pattern_t *pattern,
+                            double x1, double y1,
+                            double x2, double y2,
+                            double x3, double y3)
+{
+    cairo_mesh_pattern_t *mesh;
+    int current_point, i, j;
+
+    if (unlikely (pattern->status))
+       return;
+
+    if (unlikely (pattern->type != CAIRO_PATTERN_TYPE_MESH)) {
+       _cairo_pattern_set_error (pattern, CAIRO_STATUS_PATTERN_TYPE_MISMATCH);
+       return;
+    }
+
+    mesh = (cairo_mesh_pattern_t *) pattern;
+    if (unlikely (!mesh->current_patch)) {
+       _cairo_pattern_set_error (pattern, CAIRO_STATUS_INVALID_MESH_CONSTRUCTION);
+       return;
+    }
+
+    if (unlikely (mesh->current_side == 3)) {
+       _cairo_pattern_set_error (pattern, CAIRO_STATUS_INVALID_MESH_CONSTRUCTION);
+       return;
+    }
+
+    if (mesh->current_side == -2)
+       cairo_mesh_pattern_move_to (pattern, x1, y1);
+
+    assert (mesh->current_side >= -1);
+    assert (pattern->status == CAIRO_STATUS_SUCCESS);
+
+    mesh->current_side++;
+
+    current_point = 3 * mesh->current_side;
+
+    current_point++;
+    i = mesh_path_point_i[current_point];
+    j = mesh_path_point_j[current_point];
+    mesh->current_patch->points[i][j].x = x1;
+    mesh->current_patch->points[i][j].y = y1;
+
+    current_point++;
+    i = mesh_path_point_i[current_point];
+    j = mesh_path_point_j[current_point];
+    mesh->current_patch->points[i][j].x = x2;
+    mesh->current_patch->points[i][j].y = y2;
+
+    current_point++;
+    if (current_point < 12) {
+       i = mesh_path_point_i[current_point];
+       j = mesh_path_point_j[current_point];
+       mesh->current_patch->points[i][j].x = x3;
+       mesh->current_patch->points[i][j].y = y3;
+    }
+}
+slim_hidden_def (cairo_mesh_pattern_curve_to);
+
+/**
+ * cairo_mesh_pattern_line_to:
+ * @pattern: a #cairo_pattern_t
+ * @x: the X coordinate of the end of the new line
+ * @y: the Y coordinate of the end of the new line
+ *
+ * Adds a line to the current patch from the current point to position
+ * (@x, @y) in pattern-space coordinates.
+ *
+ * If there is no current point before the call to
+ * cairo_mesh_pattern_line_to() this function will behave as
+ * cairo_mesh_pattern_move_to(@pattern, @x, @y).
+ *
+ * After this call the current point will be (@x, @y).
+ *
+ * Note: If @pattern is not a mesh pattern then @pattern will be put
+ * into an error status with a status of
+ * %CAIRO_STATUS_PATTERN_TYPE_MISMATCH. If @pattern has no current
+ * patch or the current patch already has 4 sides, @pattern will be
+ * put into an error status with a status of
+ * %CAIRO_STATUS_INVALID_MESH_CONSTRUCTION.
+ *
+ * Since: 1.12
+ **/
+void
+cairo_mesh_pattern_line_to (cairo_pattern_t *pattern,
+                           double x, double y)
+{
+    cairo_mesh_pattern_t *mesh;
+    cairo_point_double_t last_point;
+    int last_point_idx, i, j;
+
+    if (unlikely (pattern->status))
+       return;
+
+    if (unlikely (pattern->type != CAIRO_PATTERN_TYPE_MESH)) {
+       _cairo_pattern_set_error (pattern, CAIRO_STATUS_PATTERN_TYPE_MISMATCH);
+       return;
+    }
+
+    mesh = (cairo_mesh_pattern_t *) pattern;
+    if (unlikely (!mesh->current_patch)) {
+       _cairo_pattern_set_error (pattern, CAIRO_STATUS_INVALID_MESH_CONSTRUCTION);
+       return;
+    }
+
+    if (unlikely (mesh->current_side == 3)) {
+       _cairo_pattern_set_error (pattern, CAIRO_STATUS_INVALID_MESH_CONSTRUCTION);
+       return;
+    }
+
+    if (mesh->current_side == -2) {
+       cairo_mesh_pattern_move_to (pattern, x, y);
+       return;
+    }
+
+    last_point_idx = 3 * (mesh->current_side + 1);
+    i = mesh_path_point_i[last_point_idx];
+    j = mesh_path_point_j[last_point_idx];
+
+    last_point = mesh->current_patch->points[i][j];
+
+    cairo_mesh_pattern_curve_to (pattern,
+                                (2 * last_point.x + x) * (1. / 3),
+                                (2 * last_point.y + y) * (1. / 3),
+                                (last_point.x + 2 * x) * (1. / 3),
+                                (last_point.y + 2 * y) * (1. / 3),
+                                x, y);
+}
+slim_hidden_def (cairo_mesh_pattern_line_to);
+
+/**
+ * cairo_mesh_pattern_move_to:
+ * @pattern: a #cairo_pattern_t
+ * @x: the X coordinate of the new position
+ * @y: the Y coordinate of the new position
+ *
+ * Define the first point of the current patch in a mesh pattern.
+ *
+ * After this call the current point will be (@x, @y).
+ *
+ * Note: If @pattern is not a mesh pattern then @pattern will be put
+ * into an error status with a status of
+ * %CAIRO_STATUS_PATTERN_TYPE_MISMATCH. If @pattern has no current
+ * patch or the current patch already has at leas one side, @pattern
+ * will be put into an error status with a status of
+ * %CAIRO_STATUS_INVALID_MESH_CONSTRUCTION.
+ *
+ * Since: 1.12
+ **/
+void
+cairo_mesh_pattern_move_to (cairo_pattern_t *pattern,
+                           double x, double y)
+{
+    cairo_mesh_pattern_t *mesh;
+
+    if (unlikely (pattern->status))
+       return;
+
+    if (unlikely (pattern->type != CAIRO_PATTERN_TYPE_MESH)) {
+       _cairo_pattern_set_error (pattern, CAIRO_STATUS_PATTERN_TYPE_MISMATCH);
+       return;
+    }
+
+    mesh = (cairo_mesh_pattern_t *) pattern;
+    if (unlikely (!mesh->current_patch)) {
+       _cairo_pattern_set_error (pattern, CAIRO_STATUS_INVALID_MESH_CONSTRUCTION);
+       return;
+    }
+
+    if (unlikely (mesh->current_side >= 0)) {
+       _cairo_pattern_set_error (pattern, CAIRO_STATUS_INVALID_MESH_CONSTRUCTION);
+       return;
+    }
+
+    mesh->current_side = -1;
+    mesh->current_patch->points[0][0].x = x;
+    mesh->current_patch->points[0][0].y = y;
+}
+slim_hidden_def (cairo_mesh_pattern_move_to);
+
+/**
+ * cairo_mesh_pattern_set_control_point:
+ * @pattern: a #cairo_pattern_t
+ * @point_num: the control point to set the position for
+ * @x: the X coordinate of the control point
+ * @y: the Y coordinate of the control point
+ *
+ * Set an internal control point of the current patch.
+ *
+ * Valid values for @point_num are from 0 to 3 and identify the
+ * control points as explained in cairo_pattern_create_mesh().
+ *
+ * Note: If @pattern is not a mesh pattern then @pattern will be put
+ * into an error status with a status of
+ * %CAIRO_STATUS_PATTERN_TYPE_MISMATCH. If @point_num is not valid,
+ * @pattern will be put into an error status with a status of
+ * %CAIRO_STATUS_INVALID_INDEX.  If @pattern has no current patch,
+ * @pattern will be put into an error status with a status of
+ * %CAIRO_STATUS_INVALID_MESH_CONSTRUCTION.
+ *
+ * Since: 1.12
+ **/
+void
+cairo_mesh_pattern_set_control_point (cairo_pattern_t *pattern,
+                                     unsigned int     point_num,
+                                     double           x,
+                                     double           y)
+{
+    cairo_mesh_pattern_t *mesh;
+    int i, j;
+
+    if (unlikely (pattern->status))
+       return;
+
+    if (unlikely (pattern->type != CAIRO_PATTERN_TYPE_MESH)) {
+       _cairo_pattern_set_error (pattern, CAIRO_STATUS_PATTERN_TYPE_MISMATCH);
+       return;
+    }
+
+    if (unlikely (point_num > 3)) {
+       _cairo_pattern_set_error (pattern, CAIRO_STATUS_INVALID_INDEX);
+       return;
+    }
+
+    mesh = (cairo_mesh_pattern_t *) pattern;
+    if (unlikely (!mesh->current_patch)) {
+       _cairo_pattern_set_error (pattern, CAIRO_STATUS_INVALID_MESH_CONSTRUCTION);
+       return;
+    }
+
+    i = mesh_control_point_i[point_num];
+    j = mesh_control_point_j[point_num];
+
+    mesh->current_patch->points[i][j].x = x;
+    mesh->current_patch->points[i][j].y = y;
+    mesh->has_control_point[point_num] = TRUE;
+}
+
+/* make room for at least one more color stop */
+static cairo_status_t
+_cairo_pattern_gradient_grow (cairo_gradient_pattern_t *pattern)
+{
+    cairo_gradient_stop_t *new_stops;
+    int old_size = pattern->stops_size;
+    int embedded_size = ARRAY_LENGTH (pattern->stops_embedded);
+    int new_size = 2 * MAX (old_size, 4);
+
+    /* we have a local buffer at pattern->stops_embedded.  try to fulfill the request
+     * from there. */
+    if (old_size < embedded_size) {
+       pattern->stops = pattern->stops_embedded;
+       pattern->stops_size = embedded_size;
+       return CAIRO_STATUS_SUCCESS;
+    }
+
+    if (CAIRO_INJECT_FAULT ())
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    assert (pattern->n_stops <= pattern->stops_size);
+
+    if (pattern->stops == pattern->stops_embedded) {
+       new_stops = _cairo_malloc_ab (new_size, sizeof (cairo_gradient_stop_t));
+       if (new_stops)
+           memcpy (new_stops, pattern->stops, old_size * sizeof (cairo_gradient_stop_t));
+    } else {
+       new_stops = _cairo_realloc_ab (pattern->stops,
+                                      new_size,
+                                      sizeof (cairo_gradient_stop_t));
+    }
+
+    if (unlikely (new_stops == NULL))
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    pattern->stops = new_stops;
+    pattern->stops_size = new_size;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_mesh_pattern_set_corner_color (cairo_mesh_pattern_t *mesh,
+                                     unsigned int     corner_num,
+                                     double red, double green, double blue,
+                                     double alpha)
+{
+    cairo_color_t *color;
+
+    assert (mesh->current_patch);
+    assert (corner_num <= 3);
+
+    color = &mesh->current_patch->colors[corner_num];
+    color->red   = red;
+    color->green = green;
+    color->blue  = blue;
+    color->alpha = alpha;
+
+    color->red_short   = _cairo_color_double_to_short (red);
+    color->green_short = _cairo_color_double_to_short (green);
+    color->blue_short  = _cairo_color_double_to_short (blue);
+    color->alpha_short = _cairo_color_double_to_short (alpha);
+
+    mesh->has_color[corner_num] = TRUE;
+}
+
+/**
+ * cairo_mesh_pattern_set_corner_color_rgb:
+ * @pattern: a #cairo_pattern_t
+ * @corner_num: the corner to set the color for
+ * @red: red component of color
+ * @green: green component of color
+ * @blue: blue component of color
+ *
+ * Sets the color of a corner of the current patch in a mesh pattern.
+ *
+ * The color is specified in the same way as in cairo_set_source_rgb().
+ *
+ * Valid values for @corner_num are from 0 to 3 and identify the
+ * corners as explained in cairo_pattern_create_mesh().
+ *
+ * Note: If @pattern is not a mesh pattern then @pattern will be put
+ * into an error status with a status of
+ * %CAIRO_STATUS_PATTERN_TYPE_MISMATCH. If @corner_num is not valid,
+ * @pattern will be put into an error status with a status of
+ * %CAIRO_STATUS_INVALID_INDEX.  If @pattern has no current patch,
+ * @pattern will be put into an error status with a status of
+ * %CAIRO_STATUS_INVALID_MESH_CONSTRUCTION.
+ *
+ * Since: 1.12
+ **/
+void
+cairo_mesh_pattern_set_corner_color_rgb (cairo_pattern_t *pattern,
+                                        unsigned int     corner_num,
+                                        double red, double green, double blue)
+{
+    cairo_mesh_pattern_set_corner_color_rgba (pattern, corner_num, red, green, blue, 1.0);
+}
+
+/**
+ * cairo_mesh_pattern_set_corner_color_rgba:
+ * @pattern: a #cairo_pattern_t
+ * @corner_num: the corner to set the color for
+ * @red: red component of color
+ * @green: green component of color
+ * @blue: blue component of color
+ * @alpha: alpha component of color
+ *
+ * Sets the color of a corner of the current patch in a mesh pattern.
+ *
+ * The color is specified in the same way as in cairo_set_source_rgba().
+ *
+ * Valid values for @corner_num are from 0 to 3 and identify the
+ * corners as explained in cairo_pattern_create_mesh().
+ *
+ * Note: If @pattern is not a mesh pattern then @pattern will be put
+ * into an error status with a status of
+ * %CAIRO_STATUS_PATTERN_TYPE_MISMATCH. If @corner_num is not valid,
+ * @pattern will be put into an error status with a status of
+ * %CAIRO_STATUS_INVALID_INDEX.  If @pattern has no current patch,
+ * @pattern will be put into an error status with a status of
+ * %CAIRO_STATUS_INVALID_MESH_CONSTRUCTION.
+ *
+ * Since: 1.12
+ **/
+void
+cairo_mesh_pattern_set_corner_color_rgba (cairo_pattern_t *pattern,
+                                         unsigned int     corner_num,
+                                         double red, double green, double blue,
+                                         double alpha)
+{
+    cairo_mesh_pattern_t *mesh;
+
+    if (unlikely (pattern->status))
+       return;
+
+    if (unlikely (pattern->type != CAIRO_PATTERN_TYPE_MESH)) {
+       _cairo_pattern_set_error (pattern, CAIRO_STATUS_PATTERN_TYPE_MISMATCH);
+       return;
+    }
+
+    if (unlikely (corner_num > 3)) {
+       _cairo_pattern_set_error (pattern, CAIRO_STATUS_INVALID_INDEX);
+       return;
+    }
+
+    mesh = (cairo_mesh_pattern_t *) pattern;
+    if (unlikely (!mesh->current_patch)) {
+       _cairo_pattern_set_error (pattern, CAIRO_STATUS_INVALID_MESH_CONSTRUCTION);
+       return;
+    }
+
+    red    = _cairo_restrict_value (red,    0.0, 1.0);
+    green  = _cairo_restrict_value (green,  0.0, 1.0);
+    blue   = _cairo_restrict_value (blue,   0.0, 1.0);
+    alpha  = _cairo_restrict_value (alpha,  0.0, 1.0);
+
+    _cairo_mesh_pattern_set_corner_color (mesh, corner_num, red, green, blue, alpha);
+}
+slim_hidden_def (cairo_mesh_pattern_set_corner_color_rgba);
+
+static void
+_cairo_pattern_add_color_stop (cairo_gradient_pattern_t        *pattern,
+                              double                    offset,
+                              double                    red,
+                              double                    green,
+                              double                    blue,
+                              double                    alpha)
+{
+    cairo_gradient_stop_t *stops;
+    unsigned int          i;
+
+    if (pattern->n_stops >= pattern->stops_size) {
+        cairo_status_t status = _cairo_pattern_gradient_grow (pattern);
+       if (unlikely (status)) {
+           status = _cairo_pattern_set_error (&pattern->base, status);
+           return;
+       }
+    }
+
+    stops = pattern->stops;
+
+    for (i = 0; i < pattern->n_stops; i++)
+    {
+       if (offset < stops[i].offset)
+       {
+           memmove (&stops[i + 1], &stops[i],
+                    sizeof (cairo_gradient_stop_t) * (pattern->n_stops - i));
+
+           break;
+       }
+    }
+
+    stops[i].offset = offset;
+
+    stops[i].color.red   = red;
+    stops[i].color.green = green;
+    stops[i].color.blue  = blue;
+    stops[i].color.alpha = alpha;
+
+    stops[i].color.red_short   = _cairo_color_double_to_short (red);
+    stops[i].color.green_short = _cairo_color_double_to_short (green);
+    stops[i].color.blue_short  = _cairo_color_double_to_short (blue);
+    stops[i].color.alpha_short = _cairo_color_double_to_short (alpha);
+
+    pattern->n_stops++;
+}
+
+/**
+ * cairo_pattern_add_color_stop_rgb:
+ * @pattern: a #cairo_pattern_t
+ * @offset: an offset in the range [0.0 .. 1.0]
+ * @red: red component of color
+ * @green: green component of color
+ * @blue: blue component of color
+ *
+ * Adds an opaque color stop to a gradient pattern. The offset
+ * specifies the location along the gradient's control vector. For
+ * example, a linear gradient's control vector is from (x0,y0) to
+ * (x1,y1) while a radial gradient's control vector is from any point
+ * on the start circle to the corresponding point on the end circle.
+ *
+ * The color is specified in the same way as in cairo_set_source_rgb().
+ *
+ * If two (or more) stops are specified with identical offset values,
+ * they will be sorted according to the order in which the stops are
+ * added, (stops added earlier will compare less than stops added
+ * later). This can be useful for reliably making sharp color
+ * transitions instead of the typical blend.
+ *
+ *
+ * Note: If the pattern is not a gradient pattern, (eg. a linear or
+ * radial pattern), then the pattern will be put into an error status
+ * with a status of %CAIRO_STATUS_PATTERN_TYPE_MISMATCH.
+ **/
+void
+cairo_pattern_add_color_stop_rgb (cairo_pattern_t *pattern,
+                                 double           offset,
+                                 double           red,
+                                 double           green,
+                                 double           blue)
+{
+    cairo_pattern_add_color_stop_rgba (pattern, offset, red, green, blue, 1.0);
+}
+
+/**
+ * cairo_pattern_add_color_stop_rgba:
+ * @pattern: a #cairo_pattern_t
+ * @offset: an offset in the range [0.0 .. 1.0]
+ * @red: red component of color
+ * @green: green component of color
+ * @blue: blue component of color
+ * @alpha: alpha component of color
+ *
+ * Adds a translucent color stop to a gradient pattern. The offset
+ * specifies the location along the gradient's control vector. For
+ * example, a linear gradient's control vector is from (x0,y0) to
+ * (x1,y1) while a radial gradient's control vector is from any point
+ * on the start circle to the corresponding point on the end circle.
+ *
+ * The color is specified in the same way as in cairo_set_source_rgba().
+ *
+ * If two (or more) stops are specified with identical offset values,
+ * they will be sorted according to the order in which the stops are
+ * added, (stops added earlier will compare less than stops added
+ * later). This can be useful for reliably making sharp color
+ * transitions instead of the typical blend.
+ *
+ * Note: If the pattern is not a gradient pattern, (eg. a linear or
+ * radial pattern), then the pattern will be put into an error status
+ * with a status of %CAIRO_STATUS_PATTERN_TYPE_MISMATCH.
+ */
+void
+cairo_pattern_add_color_stop_rgba (cairo_pattern_t *pattern,
+                                  double          offset,
+                                  double          red,
+                                  double          green,
+                                  double          blue,
+                                  double          alpha)
+{
+    if (pattern->status)
+       return;
+
+    if (pattern->type != CAIRO_PATTERN_TYPE_LINEAR &&
+       pattern->type != CAIRO_PATTERN_TYPE_RADIAL)
+    {
+       _cairo_pattern_set_error (pattern, CAIRO_STATUS_PATTERN_TYPE_MISMATCH);
+       return;
+    }
+
+    offset = _cairo_restrict_value (offset, 0.0, 1.0);
+    red    = _cairo_restrict_value (red,    0.0, 1.0);
+    green  = _cairo_restrict_value (green,  0.0, 1.0);
+    blue   = _cairo_restrict_value (blue,   0.0, 1.0);
+    alpha  = _cairo_restrict_value (alpha,  0.0, 1.0);
+
+    _cairo_pattern_add_color_stop ((cairo_gradient_pattern_t *) pattern,
+                                  offset, red, green, blue, alpha);
+}
+slim_hidden_def (cairo_pattern_add_color_stop_rgba);
+
+/**
+ * cairo_pattern_set_matrix:
+ * @pattern: a #cairo_pattern_t
+ * @matrix: a #cairo_matrix_t
+ *
+ * Sets the pattern's transformation matrix to @matrix. This matrix is
+ * a transformation from user space to pattern space.
+ *
+ * When a pattern is first created it always has the identity matrix
+ * for its transformation matrix, which means that pattern space is
+ * initially identical to user space.
+ *
+ * Important: Please note that the direction of this transformation
+ * matrix is from user space to pattern space. This means that if you
+ * imagine the flow from a pattern to user space (and on to device
+ * space), then coordinates in that flow will be transformed by the
+ * inverse of the pattern matrix.
+ *
+ * For example, if you want to make a pattern appear twice as large as
+ * it does by default the correct code to use is:
+ *
+ * <informalexample><programlisting>
+ * cairo_matrix_init_scale (&amp;matrix, 0.5, 0.5);
+ * cairo_pattern_set_matrix (pattern, &amp;matrix);
+ * </programlisting></informalexample>
+ *
+ * Meanwhile, using values of 2.0 rather than 0.5 in the code above
+ * would cause the pattern to appear at half of its default size.
+ *
+ * Also, please note the discussion of the user-space locking
+ * semantics of cairo_set_source().
+ **/
+void
+cairo_pattern_set_matrix (cairo_pattern_t      *pattern,
+                         const cairo_matrix_t *matrix)
+{
+    cairo_matrix_t inverse;
+    cairo_status_t status;
+
+    if (pattern->status)
+       return;
+
+    if (memcmp (&pattern->matrix, matrix, sizeof (cairo_matrix_t)) == 0)
+       return;
+
+    pattern->matrix = *matrix;
+    _cairo_pattern_notify_observers (pattern, CAIRO_PATTERN_NOTIFY_MATRIX);
+
+    inverse = *matrix;
+    status = cairo_matrix_invert (&inverse);
+    if (unlikely (status))
+       status = _cairo_pattern_set_error (pattern, status);
+}
+slim_hidden_def (cairo_pattern_set_matrix);
+
+/**
+ * cairo_pattern_get_matrix:
+ * @pattern: a #cairo_pattern_t
+ * @matrix: return value for the matrix
+ *
+ * Stores the pattern's transformation matrix into @matrix.
+ **/
+void
+cairo_pattern_get_matrix (cairo_pattern_t *pattern, cairo_matrix_t *matrix)
+{
+    *matrix = pattern->matrix;
+}
+
+/**
+ * cairo_pattern_set_filter:
+ * @pattern: a #cairo_pattern_t
+ * @filter: a #cairo_filter_t describing the filter to use for resizing
+ * the pattern
+ *
+ * Sets the filter to be used for resizing when using this pattern.
+ * See #cairo_filter_t for details on each filter.
+ *
+ * * Note that you might want to control filtering even when you do not
+ * have an explicit #cairo_pattern_t object, (for example when using
+ * cairo_set_source_surface()). In these cases, it is convenient to
+ * use cairo_get_source() to get access to the pattern that cairo
+ * creates implicitly. For example:
+ *
+ * <informalexample><programlisting>
+ * cairo_set_source_surface (cr, image, x, y);
+ * cairo_pattern_set_filter (cairo_get_source (cr), CAIRO_FILTER_NEAREST);
+ * </programlisting></informalexample>
+ **/
+void
+cairo_pattern_set_filter (cairo_pattern_t *pattern, cairo_filter_t filter)
+{
+    if (pattern->status)
+       return;
+
+    pattern->filter = filter;
+    _cairo_pattern_notify_observers (pattern, CAIRO_PATTERN_NOTIFY_FILTER);
+}
+
+/**
+ * cairo_pattern_get_filter:
+ * @pattern: a #cairo_pattern_t
+ *
+ * Gets the current filter for a pattern.  See #cairo_filter_t
+ * for details on each filter.
+ *
+ * Return value: the current filter used for resizing the pattern.
+ **/
+cairo_filter_t
+cairo_pattern_get_filter (cairo_pattern_t *pattern)
+{
+    return pattern->filter;
+}
+
+/**
+ * cairo_pattern_set_extend:
+ * @pattern: a #cairo_pattern_t
+ * @extend: a #cairo_extend_t describing how the area outside of the
+ * pattern will be drawn
+ *
+ * Sets the mode to be used for drawing outside the area of a pattern.
+ * See #cairo_extend_t for details on the semantics of each extend
+ * strategy.
+ *
+ * The default extend mode is %CAIRO_EXTEND_NONE for surface patterns
+ * and %CAIRO_EXTEND_PAD for gradient patterns.
+ **/
+void
+cairo_pattern_set_extend (cairo_pattern_t *pattern, cairo_extend_t extend)
+{
+    if (pattern->status)
+       return;
+
+    pattern->extend = extend;
+    _cairo_pattern_notify_observers (pattern, CAIRO_PATTERN_NOTIFY_EXTEND);
+}
+
+/**
+ * cairo_pattern_get_extend:
+ * @pattern: a #cairo_pattern_t
+ *
+ * Gets the current extend mode for a pattern.  See #cairo_extend_t
+ * for details on the semantics of each extend strategy.
+ *
+ * Return value: the current extend strategy used for drawing the
+ * pattern.
+ **/
+cairo_extend_t
+cairo_pattern_get_extend (cairo_pattern_t *pattern)
+{
+    return pattern->extend;
+}
+slim_hidden_def (cairo_pattern_get_extend);
+
+void
+_cairo_pattern_transform (cairo_pattern_t      *pattern,
+                         const cairo_matrix_t  *ctm_inverse)
+{
+    if (pattern->status)
+       return;
+
+    cairo_matrix_multiply (&pattern->matrix, ctm_inverse, &pattern->matrix);
+}
+
+static cairo_bool_t
+_linear_pattern_is_degenerate (const cairo_linear_pattern_t *linear)
+{
+    return fabs (linear->pd1.x - linear->pd2.x) < DBL_EPSILON &&
+          fabs (linear->pd1.y - linear->pd2.y) < DBL_EPSILON;
+}
+
+static cairo_bool_t
+_radial_pattern_is_degenerate (const cairo_radial_pattern_t *radial)
+{
+    /* A radial pattern is considered degenerate if it can be
+     * represented as a solid or clear pattern.  This corresponds to
+     * one of the two cases:
+     *
+     * 1) The radii are both very small:
+     *      |dr| < DBL_EPSILON && min (r0, r1) < DBL_EPSILON
+     *
+     * 2) The two circles have about the same radius and are very
+     *    close to each other (approximately a cylinder gradient that
+     *    doesn't move with the parameter):
+     *      |dr| < DBL_EPSILON && max (|dx|, |dy|) < 2 * DBL_EPSILON
+     *
+     * These checks are consistent with the assumptions used in
+     * _cairo_radial_pattern_box_to_parameter ().
+     */
+
+    return fabs (radial->cd1.radius - radial->cd2.radius) < DBL_EPSILON &&
+       (MIN (radial->cd1.radius, radial->cd2.radius) < DBL_EPSILON ||
+        MAX (fabs (radial->cd1.center.x - radial->cd2.center.x),
+             fabs (radial->cd1.center.y - radial->cd2.center.y)) < 2 * DBL_EPSILON);
+}
+
+static void
+_cairo_linear_pattern_box_to_parameter (const cairo_linear_pattern_t *linear,
+                                       double x0, double y0,
+                                       double x1, double y1,
+                                       double range[2])
+{
+    double t0, tdx, tdy;
+    double p1x, p1y, pdx, pdy, invsqnorm;
+
+    assert (! _linear_pattern_is_degenerate (linear));
+
+    /*
+     * Linear gradients are othrogonal to the line passing through
+     * their extremes. Because of convexity, the parameter range can
+     * be computed as the convex hull (one the real line) of the
+     * parameter values of the 4 corners of the box.
+     *
+     * The parameter value t for a point (x,y) can be computed as:
+     *
+     *   t = (p2 - p1) . (x,y) / |p2 - p1|^2
+     *
+     * t0  is the t value for the top left corner
+     * tdx is the difference between left and right corners
+     * tdy is the difference between top and bottom corners
+     */
+
+    p1x = linear->pd1.x;
+    p1y = linear->pd1.y;
+    pdx = linear->pd2.x - p1x;
+    pdy = linear->pd2.y - p1y;
+    invsqnorm = 1.0 / (pdx * pdx + pdy * pdy);
+    pdx *= invsqnorm;
+    pdy *= invsqnorm;
+
+    t0 = (x0 - p1x) * pdx + (y0 - p1y) * pdy;
+    tdx = (x1 - x0) * pdx;
+    tdy = (y1 - y0) * pdy;
+
+    /*
+     * Because of the linearity of the t value, tdx can simply be
+     * added the t0 to move along the top edge. After this, range[0]
+     * and range[1] represent the parameter range for the top edge, so
+     * extending it to include the whole box simply requires adding
+     * tdy to the correct extreme.
+     */
+
+    range[0] = range[1] = t0;
+    if (tdx < 0)
+       range[0] += tdx;
+    else
+       range[1] += tdx;
+
+    if (tdy < 0)
+       range[0] += tdy;
+    else
+       range[1] += tdy;
+}
+
+static cairo_bool_t
+_extend_range (double range[2], double value, cairo_bool_t valid)
+{
+    if (!valid)
+       range[0] = range[1] = value;
+    else if (value < range[0])
+       range[0] = value;
+    else if (value > range[1])
+       range[1] = value;
+
+    return TRUE;
+}
+
+/**
+ * _cairo_radial_pattern_focus_is_inside
+ *
+ * Returns %TRUE if and only if the focus point exists and is
+ * contained in one of the two extreme circles. This condition is
+ * equivalent to one of the two extreme circles being completely
+ * contained in the other one.
+ *
+ * Note: if the focus is on the border of one of the two circles (in
+ * which case the circles are tangent in the focus point), it is not
+ * considered as contained in the circle, hence this function returns
+ * %FALSE.
+ *
+ **/
+cairo_bool_t
+_cairo_radial_pattern_focus_is_inside (const cairo_radial_pattern_t *radial)
+{
+    double cx, cy, cr, dx, dy, dr;
+
+    cx = radial->cd1.center.x;
+    cy = radial->cd1.center.y;
+    cr = radial->cd1.radius;
+    dx = radial->cd2.center.x - cx;
+    dy = radial->cd2.center.y - cy;
+    dr = radial->cd2.radius   - cr;
+
+    return dx*dx + dy*dy < dr*dr;
+}
+
+static void
+_cairo_radial_pattern_box_to_parameter (const cairo_radial_pattern_t *radial,
+                                       double x0, double y0,
+                                       double x1, double y1,
+                                       double tolerance,
+                                       double range[2])
+{
+    double cx, cy, cr, dx, dy, dr;
+    double a, x_focus, y_focus;
+    double mindr, minx, miny, maxx, maxy;
+    cairo_bool_t valid;
+
+    assert (! _radial_pattern_is_degenerate (radial));
+    assert (x0 < x1);
+    assert (y0 < y1);
+
+    tolerance = MAX (tolerance, DBL_EPSILON);
+
+    range[0] = range[1] = 0;
+    valid = FALSE;
+
+    x_focus = y_focus = 0; /* silence gcc */
+
+    cx = radial->cd1.center.x;
+    cy = radial->cd1.center.y;
+    cr = radial->cd1.radius;
+    dx = radial->cd2.center.x - cx;
+    dy = radial->cd2.center.y - cy;
+    dr = radial->cd2.radius   - cr;
+
+    /* translate by -(cx, cy) to simplify computations */
+    x0 -= cx;
+    y0 -= cy;
+    x1 -= cx;
+    y1 -= cy;
+
+    /* enlarge boundaries slightly to avoid rounding problems in the
+     * parameter range computation */
+    x0 -= DBL_EPSILON;
+    y0 -= DBL_EPSILON;
+    x1 += DBL_EPSILON;
+    y1 += DBL_EPSILON;
+
+    /* enlarge boundaries even more to avoid rounding problems when
+     * testing if a point belongs to the box */
+    minx = x0 - DBL_EPSILON;
+    miny = y0 - DBL_EPSILON;
+    maxx = x1 + DBL_EPSILON;
+    maxy = y1 + DBL_EPSILON;
+
+    /* we dont' allow negative radiuses, so we will be checking that
+     * t*dr >= mindr to consider t valid */
+    mindr = -(cr + DBL_EPSILON);
+
+    /*
+     * After the previous transformations, the start circle is
+     * centered in the origin and has radius cr. A 1-unit change in
+     * the t parameter corresponds to dx,dy,dr changes in the x,y,r of
+     * the circle (center coordinates, radius).
+     *
+     * To compute the minimum range needed to correctly draw the
+     * pattern, we start with an empty range and extend it to include
+     * the circles touching the bounding box or within it.
+     */
+
+    /*
+     * Focus, the point where the circle has radius == 0.
+     *
+     * r = cr + t * dr = 0
+     * t = -cr / dr
+     *
+     * If the radius is constant (dr == 0) there is no focus (the
+     * gradient represents a cylinder instead of a cone).
+     */
+    if (fabs (dr) >= DBL_EPSILON) {
+       double t_focus;
+
+       t_focus = -cr / dr;
+       x_focus = t_focus * dx;
+       y_focus = t_focus * dy;
+       if (minx <= x_focus && x_focus <= maxx &&
+           miny <= y_focus && y_focus <= maxy)
+       {
+           valid = _extend_range (range, t_focus, valid);
+       }
+    }
+
+    /*
+     * Circles externally tangent to box edges.
+     *
+     * All circles have center in (dx, dy) * t
+     *
+     * If the circle is tangent to the line defined by the edge of the
+     * box, then at least one of the following holds true:
+     *
+     *   (dx*t) + (cr + dr*t) == x0 (left   edge)
+     *   (dx*t) - (cr + dr*t) == x1 (right  edge)
+     *   (dy*t) + (cr + dr*t) == y0 (top    edge)
+     *   (dy*t) - (cr + dr*t) == y1 (bottom edge)
+     *
+     * The solution is only valid if the tangent point is actually on
+     * the edge, i.e. if its y coordinate is in [y0,y1] for left/right
+     * edges and if its x coordinate is in [x0,x1] for top/bottom
+     * edges.
+     *
+     * For the first equation:
+     *
+     *   (dx + dr) * t = x0 - cr
+     *   t = (x0 - cr) / (dx + dr)
+     *   y = dy * t
+     *
+     * in the code this becomes:
+     *
+     *   t_edge = (num) / (den)
+     *   v = (delta) * t_edge
+     *
+     * If the denominator in t is 0, the pattern is tangent to a line
+     * parallel to the edge under examination. The corner-case where
+     * the boundary line is the same as the edge is handled by the
+     * focus point case and/or by the a==0 case.
+     */
+#define T_EDGE(num,den,delta,lower,upper)                              \
+    if (fabs (den) >= DBL_EPSILON) {                                   \
+       double t_edge, v;                                               \
+                                                                       \
+       t_edge = (num) / (den);                                         \
+       v = t_edge * (delta);                                           \
+       if (t_edge * dr >= mindr && (lower) <= v && v <= (upper))       \
+           valid = _extend_range (range, t_edge, valid);               \
+    }
+
+    /* circles tangent (externally) to left/right/top/bottom edge */
+    T_EDGE (x0 - cr, dx + dr, dy, miny, maxy);
+    T_EDGE (x1 + cr, dx - dr, dy, miny, maxy);
+    T_EDGE (y0 - cr, dy + dr, dx, minx, maxx);
+    T_EDGE (y1 + cr, dy - dr, dx, minx, maxx);
+
+#undef T_EDGE
+
+    /*
+     * Circles passing through a corner.
+     *
+     * A circle passing through the point (x,y) satisfies:
+     *
+     * (x-t*dx)^2 + (y-t*dy)^2 == (cr + t*dr)^2
+     *
+     * If we set:
+     *   a = dx^2 + dy^2 - dr^2
+     *   b = x*dx + y*dy + cr*dr
+     *   c = x^2 + y^2 - cr^2
+     * we have:
+     *   a*t^2 - 2*b*t + c == 0
+     */
+    a = dx * dx + dy * dy - dr * dr;
+    if (fabs (a) < DBL_EPSILON * DBL_EPSILON) {
+       double b, maxd2;
+
+       /* Ensure that gradients with both a and dr small are
+        * considered degenerate.
+        * The floating point version of the degeneracy test implemented
+        * in _radial_pattern_is_degenerate() is:
+        *
+        *  1) The circles are practically the same size:
+        *     |dr| < DBL_EPSILON
+        *  AND
+        *  2a) The circles are both very small:
+        *      min (r0, r1) < DBL_EPSILON
+        *   OR
+        *  2b) The circles are very close to each other:
+        *      max (|dx|, |dy|) < 2 * DBL_EPSILON
+        *
+        * Assuming that the gradient is not degenerate, we want to
+        * show that |a| < DBL_EPSILON^2 implies |dr| >= DBL_EPSILON.
+        *
+        * If the gradient is not degenerate yet it has |dr| <
+        * DBL_EPSILON, (2b) is false, thus:
+        *
+        *   max (|dx|, |dy|) >= 2*DBL_EPSILON
+        * which implies:
+        *   4*DBL_EPSILON^2 <= max (|dx|, |dy|)^2 <= dx^2 + dy^2
+        *
+        * From the definition of a, we get:
+        *   a = dx^2 + dy^2 - dr^2 < DBL_EPSILON^2
+        *   dx^2 + dy^2 - DBL_EPSILON^2 < dr^2
+        *   3*DBL_EPSILON^2 < dr^2
+        *
+        * which is inconsistent with the hypotheses, thus |dr| <
+        * DBL_EPSILON is false or the gradient is degenerate.
+        */
+       assert (fabs (dr) >= DBL_EPSILON);
+
+       /*
+        * If a == 0, all the circles are tangent to a line in the
+        * focus point. If this line is within the box extents, we
+        * should add the circle with infinite radius, but this would
+        * make the range unbounded, so we add the smallest circle whose
+        * distance to the desired (degenerate) circle within the
+        * bounding box does not exceed tolerance.
+        *
+        * The equation of the line is b==0, i.e.:
+        *   x*dx + y*dy + cr*dr == 0
+        *
+        * We compute the intersection of the line with the box and
+        * keep the intersection with maximum square distance (maxd2)
+        * from the focus point.
+        *
+        * In the code the intersection is represented in another
+        * coordinate system, whose origin is the focus point and
+        * which has a u,v axes, which are respectively orthogonal and
+        * parallel to the edge being intersected.
+        *
+        * The intersection is valid only if it belongs to the box,
+        * otherwise it is ignored.
+        *
+        * For example:
+        *
+        *   y = y0
+        *   x*dx + y0*dy + cr*dr == 0
+        *   x = -(y0*dy + cr*dr) / dx
+        *
+        * which in (u,v) is:
+        *   u = y0 - y_focus
+        *   v = -(y0*dy + cr*dr) / dx - x_focus
+        *
+        * In the code:
+        *   u = (edge) - (u_origin)
+        *   v = -((edge) * (delta) + cr*dr) / (den) - v_focus
+        */
+#define T_EDGE(edge,delta,den,lower,upper,u_origin,v_origin)   \
+       if (fabs (den) >= DBL_EPSILON) {                        \
+           double v;                                           \
+                                                               \
+           v = -((edge) * (delta) + cr * dr) / (den);          \
+           if ((lower) <= v && v <= (upper)) {                 \
+               double u, d2;                                   \
+                                                               \
+               u = (edge) - (u_origin);                        \
+               v -= (v_origin);                                \
+               d2 = u*u + v*v;                                 \
+               if (maxd2 < d2)                                 \
+                   maxd2 = d2;                                 \
+           }                                                   \
+       }
+
+       maxd2 = 0;
+
+       /* degenerate circles (lines) passing through each edge */
+       T_EDGE (y0, dy, dx, minx, maxx, y_focus, x_focus);
+       T_EDGE (y1, dy, dx, minx, maxx, y_focus, x_focus);
+       T_EDGE (x0, dx, dy, miny, maxy, x_focus, y_focus);
+       T_EDGE (x1, dx, dy, miny, maxy, x_focus, y_focus);
+
+#undef T_EDGE
+
+       /*
+        * The limit circle can be transformed rigidly to the y=0 line
+        * and the circles tangent to it in (0,0) are:
+        *
+        *   x^2 + (y-r)^2 = r^2  <=>  x^2 + y^2 - 2*y*r = 0
+        *
+        * y is the distance from the line, in our case tolerance;
+        * x is the distance along the line, i.e. sqrt(maxd2),
+        * so:
+        *
+        *   r = cr + dr * t = (maxd2 + tolerance^2) / (2*tolerance)
+        *   t = (r - cr) / dr =
+        *       (maxd2 + tolerance^2 - 2*tolerance*cr) / (2*tolerance*dr)
+        */
+       if (maxd2 > 0) {
+           double t_limit = maxd2 + tolerance*tolerance - 2*tolerance*cr;
+           t_limit /= 2 * tolerance * dr;
+           valid = _extend_range (range, t_limit, valid);
+       }
+
+       /*
+        * Nondegenerate, nonlimit circles passing through the corners.
+        *
+        * a == 0 && a*t^2 - 2*b*t + c == 0
+        *
+        * t = c / (2*b)
+        *
+        * The b == 0 case has just been handled, so we only have to
+        * compute this if b != 0.
+        */
+#define T_CORNER(x,y)                                                  \
+       b = (x) * dx + (y) * dy + cr * dr;                              \
+       if (fabs (b) >= DBL_EPSILON) {                                  \
+           double t_corner;                                            \
+           double x2 = (x) * (x);                                      \
+           double y2 = (y) * (y);                                      \
+           double cr2 = (cr) * (cr);                                   \
+           double c = x2 + y2 - cr2;                                   \
+                                                                       \
+           t_corner = 0.5 * c / b;                                     \
+           if (t_corner * dr >= mindr)                                 \
+               valid = _extend_range (range, t_corner, valid);         \
+       }
+
+       /* circles touching each corner */
+       T_CORNER (x0, y0);
+       T_CORNER (x0, y1);
+       T_CORNER (x1, y0);
+       T_CORNER (x1, y1);
+
+#undef T_CORNER
+    } else {
+       double inva, b, c, d;
+
+       inva = 1 / a;
+
+       /*
+        * Nondegenerate, nonlimit circles passing through the corners.
+        *
+        * a != 0 && a*t^2 - 2*b*t + c == 0
+        *
+        * t = (b +- sqrt (b*b - a*c)) / a
+        *
+        * If the argument of sqrt() is negative, then no circle
+        * passes through the corner.
+        */
+#define T_CORNER(x,y)                                                  \
+       b = (x) * dx + (y) * dy + cr * dr;                              \
+       c = (x) * (x) + (y) * (y) - cr * cr;                            \
+       d = b * b - a * c;                                              \
+       if (d >= 0) {                                                   \
+           double t_corner;                                            \
+                                                                       \
+           d = sqrt (d);                                               \
+           t_corner = (b + d) * inva;                                  \
+           if (t_corner * dr >= mindr)                                 \
+               valid = _extend_range (range, t_corner, valid);         \
+           t_corner = (b - d) * inva;                                  \
+           if (t_corner * dr >= mindr)                                 \
+               valid = _extend_range (range, t_corner, valid);         \
+       }
+
+       /* circles touching each corner */
+       T_CORNER (x0, y0);
+       T_CORNER (x0, y1);
+       T_CORNER (x1, y0);
+       T_CORNER (x1, y1);
+
+#undef T_CORNER
+    }
+}
+
+/**
+ * _cairo_gradient_pattern_box_to_parameter
+ *
+ * Compute a interpolation range sufficient to draw (within the given
+ * tolerance) the gradient in the given box getting the same result as
+ * using the (-inf, +inf) range.
+ *
+ * Assumes that the pattern is not degenerate. This can be guaranteed
+ * by simplifying it to a solid clear if _cairo_pattern_is_clear or to
+ * a solid color if _cairo_gradient_pattern_is_solid.
+ *
+ * The range isn't guaranteed to be minimal, but it tries to.
+ **/
+void
+_cairo_gradient_pattern_box_to_parameter (const cairo_gradient_pattern_t *gradient,
+                                         double x0, double y0,
+                                         double x1, double y1,
+                                         double tolerance,
+                                         double out_range[2])
+{
+    assert (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR ||
+           gradient->base.type == CAIRO_PATTERN_TYPE_RADIAL);
+
+    if (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR) {
+       _cairo_linear_pattern_box_to_parameter ((cairo_linear_pattern_t *) gradient,
+                                               x0, y0, x1, y1, out_range);
+    } else {
+       _cairo_radial_pattern_box_to_parameter ((cairo_radial_pattern_t *) gradient,
+                                               x0, y0, x1, y1, tolerance, out_range);
+    }
+}
+
+/**
+ * _cairo_gradient_pattern_interpolate
+ *
+ * Interpolate between the start and end objects of linear or radial
+ * gradients.  The interpolated object is stored in out_circle, with
+ * the radius being zero in the linear gradient case.
+ **/
+void
+_cairo_gradient_pattern_interpolate (const cairo_gradient_pattern_t *gradient,
+                                    double                          t,
+                                    cairo_circle_double_t          *out_circle)
+{
+    assert (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR ||
+           gradient->base.type == CAIRO_PATTERN_TYPE_RADIAL);
+
+#define lerp(a,b) (a)*(1-t) + (b)*t
+
+    if (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR) {
+       cairo_linear_pattern_t *linear = (cairo_linear_pattern_t *) gradient;
+       out_circle->center.x = lerp (linear->pd1.x, linear->pd2.x);
+       out_circle->center.y = lerp (linear->pd1.y, linear->pd2.y);
+       out_circle->radius = 0;
+    } else {
+       cairo_radial_pattern_t *radial = (cairo_radial_pattern_t *) gradient;
+       out_circle->center.x = lerp (radial->cd1.center.x, radial->cd2.center.x);
+       out_circle->center.y = lerp (radial->cd1.center.y, radial->cd2.center.y);
+       out_circle->radius   = lerp (radial->cd1.radius  , radial->cd2.radius);
+    }
+
+#undef lerp
+}
+
+
+/**
+ * _cairo_gradient_pattern_fit_to_range
+ *
+ * Scale the extremes of a gradient to guarantee that the coordinates
+ * and their deltas are within the range (-max_value, max_value). The
+ * new extremes are stored in out_circle.
+ *
+ * The pattern matrix is scaled to guarantee that the aspect of the
+ * gradient is the same and the result is stored in out_matrix.
+ *
+ **/
+void
+_cairo_gradient_pattern_fit_to_range (const cairo_gradient_pattern_t *gradient,
+                                     double                          max_value,
+                                     cairo_matrix_t                 *out_matrix,
+                                     cairo_circle_double_t           out_circle[2])
+{
+    double dim;
+
+    assert (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR ||
+           gradient->base.type == CAIRO_PATTERN_TYPE_RADIAL);
+
+    if (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR) {
+       cairo_linear_pattern_t *linear = (cairo_linear_pattern_t *) gradient;
+
+       out_circle[0].center = linear->pd1;
+       out_circle[0].radius = 0;
+       out_circle[1].center = linear->pd2;
+       out_circle[1].radius = 0;
+
+       dim = fabs (linear->pd1.x);
+       dim = MAX (dim, fabs (linear->pd1.y));
+       dim = MAX (dim, fabs (linear->pd2.x));
+       dim = MAX (dim, fabs (linear->pd2.y));
+       dim = MAX (dim, fabs (linear->pd1.x - linear->pd2.x));
+       dim = MAX (dim, fabs (linear->pd1.y - linear->pd2.y));
+    } else {
+       cairo_radial_pattern_t *radial = (cairo_radial_pattern_t *) gradient;
+
+       out_circle[0] = radial->cd1;
+       out_circle[1] = radial->cd2;
+
+       dim = fabs (radial->cd1.center.x);
+       dim = MAX (dim, fabs (radial->cd1.center.y));
+       dim = MAX (dim, fabs (radial->cd1.radius));
+       dim = MAX (dim, fabs (radial->cd2.center.x));
+       dim = MAX (dim, fabs (radial->cd2.center.y));
+       dim = MAX (dim, fabs (radial->cd2.radius));
+       dim = MAX (dim, fabs (radial->cd1.center.x - radial->cd2.center.x));
+       dim = MAX (dim, fabs (radial->cd1.center.y - radial->cd2.center.y));
+       dim = MAX (dim, fabs (radial->cd1.radius   - radial->cd2.radius));
+    }
+
+    if (unlikely (dim > max_value)) {
+       cairo_matrix_t scale;
+
+       dim = max_value / dim;
+
+       out_circle[0].center.x *= dim;
+       out_circle[0].center.y *= dim;
+       out_circle[0].radius   *= dim;
+       out_circle[1].center.x *= dim;
+       out_circle[1].center.y *= dim;
+       out_circle[1].radius   *= dim;
+
+       cairo_matrix_init_scale (&scale, dim, dim);
+       cairo_matrix_multiply (out_matrix, &gradient->base.matrix, &scale);
+    } else {
+       *out_matrix = gradient->base.matrix;
+    }
+}
+
+static cairo_bool_t
+_gradient_is_clear (const cairo_gradient_pattern_t *gradient,
+                   const cairo_rectangle_int_t *extents)
+{
+    unsigned int i;
+
+    assert (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR ||
+           gradient->base.type == CAIRO_PATTERN_TYPE_RADIAL);
+
+    if (gradient->n_stops == 0 ||
+       (gradient->base.extend == CAIRO_EXTEND_NONE &&
+        gradient->stops[0].offset == gradient->stops[gradient->n_stops - 1].offset))
+       return TRUE;
+
+    if (gradient->base.type == CAIRO_PATTERN_TYPE_RADIAL) {
+       /* degenerate radial gradients are clear */
+       if (_radial_pattern_is_degenerate ((cairo_radial_pattern_t *) gradient))
+           return TRUE;
+    } else if (gradient->base.extend == CAIRO_EXTEND_NONE) {
+       /* EXTEND_NONE degenerate linear gradients are clear */
+       if (_linear_pattern_is_degenerate ((cairo_linear_pattern_t *) gradient))
+           return TRUE;
+    }
+
+    /* Check if the extents intersect the drawn part of the pattern. */
+    if (extents != NULL &&
+       (gradient->base.extend == CAIRO_EXTEND_NONE ||
+        gradient->base.type == CAIRO_PATTERN_TYPE_RADIAL))
+    {
+       double t[2];
+
+       _cairo_gradient_pattern_box_to_parameter (gradient,
+                                                 extents->x,
+                                                 extents->y,
+                                                 extents->x + extents->width,
+                                                 extents->y + extents->height,
+                                                 DBL_EPSILON,
+                                                 t);
+
+       if (gradient->base.extend == CAIRO_EXTEND_NONE &&
+           (t[0] >= gradient->stops[gradient->n_stops - 1].offset ||
+            t[1] <= gradient->stops[0].offset))
+       {
+               return TRUE;
+       }
+
+       if (t[0] == t[1])
+           return TRUE;
+    }
+
+    for (i = 0; i < gradient->n_stops; i++)
+       if (! CAIRO_COLOR_IS_CLEAR (&gradient->stops[i].color))
+           return FALSE;
+
+    return TRUE;
+}
+
+static void
+_gradient_color_average (const cairo_gradient_pattern_t *gradient,
+                        cairo_color_t *color)
+{
+    double delta0, delta1;
+    double r, g, b, a;
+    unsigned int i, start = 1, end;
+
+    assert (gradient->n_stops > 0);
+    assert (gradient->base.extend != CAIRO_EXTEND_NONE);
+
+    if (gradient->n_stops == 1) {
+       _cairo_color_init_rgba (color,
+                               gradient->stops[0].color.red,
+                               gradient->stops[0].color.green,
+                               gradient->stops[0].color.blue,
+                               gradient->stops[0].color.alpha);
+       return;
+    }
+
+    end = gradient->n_stops - 1;
+
+    switch (gradient->base.extend) {
+    case CAIRO_EXTEND_REPEAT:
+      /*
+       * Sa, Sb and Sy, Sz are the first two and last two stops respectively.
+       * The weight of the first and last stop can be computed as the area of
+       * the following triangles (taken with height 1, since the whole [0-1]
+       * will have total weight 1 this way): b*h/2
+       *
+       *              +                   +
+       *            / |\                / | \
+       *          /   | \             /   |   \
+       *        /     |  \          /     |     \
+       * ~~~~~+---+---+---+~~~~~~~+-------+---+---+~~~~~
+       *   -1+Sz  0  Sa   Sb      Sy     Sz   1  1+Sa
+       *
+       * For the first stop: (Sb-(-1+Sz)/2 = (1+Sb-Sz)/2
+       * For the last stop: ((1+Sa)-Sy)/2 = (1+Sa-Sy)/2
+       * Halving the result is done after summing up all the areas.
+       */
+       delta0 = 1.0 + gradient->stops[1].offset - gradient->stops[end].offset;
+       delta1 = 1.0 + gradient->stops[0].offset - gradient->stops[end-1].offset;
+       break;
+
+    case CAIRO_EXTEND_REFLECT:
+      /*
+       * Sa, Sb and Sy, Sz are the first two and last two stops respectively.
+       * The weight of the first and last stop can be computed as the area of
+       * the following trapezoids (taken with height 1, since the whole [0-1]
+       * will have total weight 1 this way): (b+B)*h/2
+       *
+       * +-------+                   +---+
+       * |       |\                / |   |
+       * |       | \             /   |   |
+       * |       |  \          /     |   |
+       * +-------+---+~~~~~~~+-------+---+
+       * 0      Sa   Sb      Sy     Sz   1
+       *
+       * For the first stop: (Sa+Sb)/2
+       * For the last stop: ((1-Sz) + (1-Sy))/2 = (2-Sy-Sz)/2
+       * Halving the result is done after summing up all the areas.
+       */
+       delta0 = gradient->stops[0].offset + gradient->stops[1].offset;
+       delta1 = 2.0 - gradient->stops[end-1].offset - gradient->stops[end].offset;
+       break;
+
+    case CAIRO_EXTEND_PAD:
+      /* PAD is computed as the average of the first and last stop:
+       *  - take both of them with weight 1 (they will be halved
+       *    after the whole sum has been computed).
+       *  - avoid summing any of the inner stops.
+       */
+       delta0 = delta1 = 1.0;
+       start = end;
+       break;
+
+    case CAIRO_EXTEND_NONE:
+    default:
+       ASSERT_NOT_REACHED;
+       _cairo_color_init_rgba (color, 0, 0, 0, 0);
+       return;
+    }
+
+    r = delta0 * gradient->stops[0].color.red;
+    g = delta0 * gradient->stops[0].color.green;
+    b = delta0 * gradient->stops[0].color.blue;
+    a = delta0 * gradient->stops[0].color.alpha;
+
+    for (i = start; i < end; ++i) {
+      /* Inner stops weight is the same as the area of the triangle they influence
+       * (which goes from the stop before to the stop after), again with height 1
+       * since the whole must sum up to 1: b*h/2
+       * Halving is done after the whole sum has been computed.
+       */
+       double delta = gradient->stops[i+1].offset - gradient->stops[i-1].offset;
+       r += delta * gradient->stops[i].color.red;
+       g += delta * gradient->stops[i].color.green;
+       b += delta * gradient->stops[i].color.blue;
+       a += delta * gradient->stops[i].color.alpha;
+    }
+
+    r += delta1 * gradient->stops[end].color.red;
+    g += delta1 * gradient->stops[end].color.green;
+    b += delta1 * gradient->stops[end].color.blue;
+    a += delta1 * gradient->stops[end].color.alpha;
+
+    _cairo_color_init_rgba (color, r * .5, g * .5, b * .5, a * .5);
+}
+
+/**
+ * _cairo_pattern_alpha_range
+ *
+ * Convenience function to determine the minimum and maximum alpha in
+ * the drawn part of a pattern (i.e. ignoring clear parts caused by
+ * extend modes and/or pattern shape).
+ *
+ * If not NULL, out_min and out_max will be set respectively to the
+ * minimum and maximum alpha value of the pattern.
+ **/
+void
+_cairo_pattern_alpha_range (const cairo_pattern_t *pattern,
+                           double                *out_min,
+                           double                *out_max)
+{
+    double alpha_min, alpha_max;
+
+    switch (pattern->type) {
+    case CAIRO_PATTERN_TYPE_SOLID: {
+       const cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) pattern;
+       alpha_min = alpha_max = solid->color.alpha;
+       break;
+    }
+
+    case CAIRO_PATTERN_TYPE_LINEAR:
+    case CAIRO_PATTERN_TYPE_RADIAL: {
+       const cairo_gradient_pattern_t *gradient = (cairo_gradient_pattern_t *) pattern;
+       unsigned int i;
+
+       assert (gradient->n_stops >= 1);
+
+       alpha_min = alpha_max = gradient->stops[0].color.alpha;
+       for (i = 1; i < gradient->n_stops; i++) {
+           if (alpha_min > gradient->stops[i].color.alpha)
+               alpha_min = gradient->stops[i].color.alpha;
+           else if (alpha_max < gradient->stops[i].color.alpha)
+               alpha_max = gradient->stops[i].color.alpha;
+       }
+
+       break;
+    }
+
+    case CAIRO_PATTERN_TYPE_MESH: {
+       const cairo_mesh_pattern_t *mesh = (const cairo_mesh_pattern_t *) pattern;
+       const cairo_mesh_patch_t *patch = _cairo_array_index_const (&mesh->patches, 0);
+       unsigned int i, j, n = _cairo_array_num_elements (&mesh->patches);
+
+       assert (n >= 1);
+
+       alpha_min = alpha_max = patch[0].colors[0].alpha;
+       for (i = 0; i < n; i++) {
+           for (j = 0; j < 4; j++) {
+               if (patch[i].colors[j].alpha < alpha_min)
+                   alpha_min = patch[i].colors[j].alpha;
+               else if (patch[i].colors[j].alpha > alpha_max)
+                   alpha_max = patch[i].colors[j].alpha;
+           }
+       }
+
+       break;
+    }
+
+    default:
+       ASSERT_NOT_REACHED;
+       /* fall through */
+
+    case CAIRO_PATTERN_TYPE_SURFACE:
+       alpha_min = 0;
+       alpha_max = 1;
+       break;
+    }
+
+    if (out_min)
+       *out_min = alpha_min;
+    if (out_max)
+       *out_max = alpha_max;
+}
+
+/**
+ * _cairo_mesh_pattern_coord_box
+ *
+ * Convenience function to determine the range of the coordinates of
+ * the points used to define the patches of the mesh.
+ *
+ * This is guaranteed to contain the pattern extents, but might not be
+ * tight, just like a Bezier curve is always inside the convex hull of
+ * the control points.
+ *
+ * This function cannot be used while the mesh is being constructed.
+ *
+ * The function returns TRUE and sets the output parametes to define
+ * the coodrinate range if the mesh pattern contains at least one
+ * patch, otherwise it returns FALSE.
+ **/
+cairo_bool_t
+_cairo_mesh_pattern_coord_box (const cairo_mesh_pattern_t *mesh,
+                              double                     *out_xmin,
+                              double                     *out_ymin,
+                              double                     *out_xmax,
+                              double                     *out_ymax)
+{
+    const cairo_mesh_patch_t *patch;
+    unsigned int num_patches, i, j, k;
+    double x0, y0, x1, y1;
+
+    assert (mesh->current_patch == NULL);
+
+    num_patches = _cairo_array_num_elements (&mesh->patches);
+
+    if (num_patches == 0)
+       return FALSE;
+
+    patch = _cairo_array_index_const (&mesh->patches, 0);
+    x0 = x1 = patch->points[0][0].x;
+    y0 = y1 = patch->points[0][0].y;
+
+    for (i = 0; i < num_patches; i++) {
+       for (j = 0; j < 4; j++) {
+           for (k = 0; k < 4; k++) {
+               x0 = MIN (x0, patch[i].points[j][k].x);
+               y0 = MIN (y0, patch[i].points[j][k].y);
+               x1 = MAX (x1, patch[i].points[j][k].x);
+               y1 = MAX (y1, patch[i].points[j][k].y);
+           }
+       }
+    }
+
+    *out_xmin = x0;
+    *out_ymin = y0;
+    *out_xmax = x1;
+    *out_ymax = y1;
+
+    return TRUE;
+}
+
+/**
+ * _cairo_gradient_pattern_is_solid
+ *
+ * Convenience function to determine whether a gradient pattern is
+ * a solid color within the given extents. In this case the color
+ * argument is initialized to the color the pattern represents.
+ * This functions doesn't handle completely transparent gradients,
+ * thus it should be called only after _cairo_pattern_is_clear has
+ * returned FALSE.
+ *
+ * Return value: %TRUE if the pattern is a solid color.
+ **/
+cairo_bool_t
+_cairo_gradient_pattern_is_solid (const cairo_gradient_pattern_t *gradient,
+                                 const cairo_rectangle_int_t *extents,
+                                 cairo_color_t *color)
+{
+    unsigned int i;
+
+    assert (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR ||
+           gradient->base.type == CAIRO_PATTERN_TYPE_RADIAL);
+
+    /* TODO: radial */
+    if (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR) {
+       cairo_linear_pattern_t *linear = (cairo_linear_pattern_t *) gradient;
+       if (_linear_pattern_is_degenerate (linear)) {
+           _gradient_color_average (gradient, color);
+           return TRUE;
+       }
+
+       if (gradient->base.extend == CAIRO_EXTEND_NONE) {
+           double t[2];
+
+           /* We already know that the pattern is not clear, thus if some
+            * part of it is clear, the whole is not solid.
+            */
+
+           if (extents == NULL)
+               return FALSE;
+
+           _cairo_linear_pattern_box_to_parameter (linear,
+                                                   extents->x,
+                                                   extents->y,
+                                                   extents->x + extents->width,
+                                                   extents->y + extents->height,
+                                                   t);
+
+           if (t[0] < 0.0 || t[1] > 1.0)
+               return FALSE;
+       }
+    } else
+       return FALSE;
+
+    for (i = 1; i < gradient->n_stops; i++)
+       if (! _cairo_color_stop_equal (&gradient->stops[0].color,
+                                      &gradient->stops[i].color))
+           return FALSE;
+
+    _cairo_color_init_rgba (color,
+                           gradient->stops[0].color.red,
+                           gradient->stops[0].color.green,
+                           gradient->stops[0].color.blue,
+                           gradient->stops[0].color.alpha);
+
+    return TRUE;
+}
+
+static cairo_bool_t
+_mesh_is_clear (const cairo_mesh_pattern_t *mesh)
+{
+    double x1, y1, x2, y2;
+    cairo_bool_t is_valid;
+
+    is_valid = _cairo_mesh_pattern_coord_box (mesh, &x1, &y1, &x2, &y2);
+    if (!is_valid)
+       return TRUE;
+
+    if (x2 - x1 < DBL_EPSILON || y2 - y1 < DBL_EPSILON)
+       return TRUE;
+
+    return FALSE;
+}
+
+/**
+ * _cairo_pattern_is_opaque_solid
+ *
+ * Convenience function to determine whether a pattern is an opaque
+ * (alpha==1.0) solid color pattern. This is done by testing whether
+ * the pattern's alpha value when converted to a byte is 255, so if a
+ * backend actually supported deep alpha channels this function might
+ * not do the right thing.
+ *
+ * Return value: %TRUE if the pattern is an opaque, solid color.
+ **/
+cairo_bool_t
+_cairo_pattern_is_opaque_solid (const cairo_pattern_t *pattern)
+{
+    cairo_solid_pattern_t *solid;
+
+    if (pattern->type != CAIRO_PATTERN_TYPE_SOLID)
+       return FALSE;
+
+    solid = (cairo_solid_pattern_t *) pattern;
+
+    return CAIRO_COLOR_IS_OPAQUE (&solid->color);
+}
+
+static cairo_bool_t
+_surface_is_opaque (const cairo_surface_pattern_t *pattern,
+                   const cairo_rectangle_int_t *sample)
+{
+    if (pattern->surface->content & CAIRO_CONTENT_ALPHA)
+       return FALSE;
+
+    if (pattern->base.extend != CAIRO_EXTEND_NONE)
+       return TRUE;
+
+    if (sample != NULL) {
+       cairo_rectangle_int_t extents;
+
+       if (! _cairo_surface_get_extents (pattern->surface, &extents))
+           return TRUE;
+
+       if (sample->x >= extents.x &&
+           sample->y >= extents.y &&
+           sample->x + sample->width  <= extents.x + extents.width &&
+           sample->y + sample->height <= extents.y + extents.height)
+       {
+           return TRUE;
+       }
+    }
+
+    return FALSE;
+}
+
+static cairo_bool_t
+_surface_is_clear (const cairo_surface_pattern_t *pattern)
+{
+    cairo_rectangle_int_t extents;
+
+    if (_cairo_surface_get_extents (pattern->surface, &extents) &&
+       (extents.width == 0 || extents.height == 0))
+       return TRUE;
+
+    return pattern->surface->is_clear &&
+       pattern->surface->content & CAIRO_CONTENT_ALPHA;
+}
+
+static cairo_bool_t
+_gradient_is_opaque (const cairo_gradient_pattern_t *gradient,
+                    const cairo_rectangle_int_t *sample)
+{
+    unsigned int i;
+
+    assert (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR ||
+           gradient->base.type == CAIRO_PATTERN_TYPE_RADIAL);
+
+    if (gradient->n_stops == 0 ||
+       (gradient->base.extend == CAIRO_EXTEND_NONE &&
+        gradient->stops[0].offset == gradient->stops[gradient->n_stops - 1].offset))
+       return FALSE;
+
+    if (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR) {
+       if (gradient->base.extend == CAIRO_EXTEND_NONE) {
+           double t[2];
+           cairo_linear_pattern_t *linear = (cairo_linear_pattern_t *) gradient;
+
+           /* EXTEND_NONE degenerate radial gradients are clear */
+           if (_linear_pattern_is_degenerate (linear))
+               return FALSE;
+
+           if (sample == NULL)
+               return FALSE;
+
+           _cairo_linear_pattern_box_to_parameter (linear,
+                                                   sample->x,
+                                                   sample->y,
+                                                   sample->x + sample->width,
+                                                   sample->y + sample->height,
+                                                   t);
+
+           if (t[0] < 0.0 || t[1] > 1.0)
+               return FALSE;
+       }
+    } else
+       return FALSE; /* TODO: check actual intersection */
+
+    for (i = 0; i < gradient->n_stops; i++)
+       if (! CAIRO_COLOR_IS_OPAQUE (&gradient->stops[i].color))
+           return FALSE;
+
+    return TRUE;
+}
+
+/**
+ * _cairo_pattern_is_opaque
+ *
+ * Convenience function to determine whether a pattern is an opaque
+ * pattern (of any type). The same caveats that apply to
+ * _cairo_pattern_is_opaque_solid apply here as well.
+ *
+ * Return value: %TRUE if the pattern is a opaque.
+ **/
+cairo_bool_t
+_cairo_pattern_is_opaque (const cairo_pattern_t *abstract_pattern,
+                         const cairo_rectangle_int_t *sample)
+{
+    const cairo_pattern_union_t *pattern;
+
+    if (abstract_pattern->has_component_alpha)
+       return FALSE;
+
+    pattern = (cairo_pattern_union_t *) abstract_pattern;
+    switch (pattern->base.type) {
+    case CAIRO_PATTERN_TYPE_SOLID:
+       return _cairo_pattern_is_opaque_solid (abstract_pattern);
+    case CAIRO_PATTERN_TYPE_SURFACE:
+       return _surface_is_opaque (&pattern->surface, sample);
+    case CAIRO_PATTERN_TYPE_LINEAR:
+    case CAIRO_PATTERN_TYPE_RADIAL:
+       return _gradient_is_opaque (&pattern->gradient.base, sample);
+    case CAIRO_PATTERN_TYPE_MESH:
+       return FALSE;
+    }
+
+    ASSERT_NOT_REACHED;
+    return FALSE;
+}
+
+cairo_bool_t
+_cairo_pattern_is_clear (const cairo_pattern_t *abstract_pattern)
+{
+    const cairo_pattern_union_t *pattern;
+
+    if (abstract_pattern->has_component_alpha)
+       return FALSE;
+
+    pattern = (cairo_pattern_union_t *) abstract_pattern;
+    switch (abstract_pattern->type) {
+    case CAIRO_PATTERN_TYPE_SOLID:
+       return CAIRO_COLOR_IS_CLEAR (&pattern->solid.color);
+    case CAIRO_PATTERN_TYPE_SURFACE:
+       return _surface_is_clear (&pattern->surface);
+    case CAIRO_PATTERN_TYPE_LINEAR:
+    case CAIRO_PATTERN_TYPE_RADIAL:
+       return _gradient_is_clear (&pattern->gradient.base, NULL);
+    case CAIRO_PATTERN_TYPE_MESH:
+       return _mesh_is_clear (&pattern->mesh);
+    }
+
+    ASSERT_NOT_REACHED;
+    return FALSE;
+}
+
+/**
+ * _cairo_pattern_analyze_filter:
+ * @pattern: surface pattern
+ * @pad_out: location to store necessary padding in the source image, or %NULL
+ * Returns: the optimized #cairo_filter_t to use with @pattern.
+ *
+ * Analyze the filter to determine how much extra needs to be sampled
+ * from the source image to account for the filter radius and whether
+ * we can optimize the filter to a simpler value.
+ *
+ * XXX: We don't actually have any way of querying the backend for
+ *      the filter radius, so we just guess base on what we know that
+ *      backends do currently (see bug #10508)
+ */
+cairo_filter_t
+_cairo_pattern_analyze_filter (const cairo_pattern_t   *pattern,
+                              double                   *pad_out)
+{
+    double pad;
+    cairo_filter_t optimized_filter;
+
+    switch (pattern->filter) {
+    case CAIRO_FILTER_GOOD:
+    case CAIRO_FILTER_BEST:
+    case CAIRO_FILTER_BILINEAR:
+       /* If source pixels map 1:1 onto destination pixels, we do
+        * not need to filter (and do not want to filter, since it
+        * will cause blurriness)
+        */
+       if (_cairo_matrix_is_pixel_exact (&pattern->matrix)) {
+           pad = 0.;
+           optimized_filter = CAIRO_FILTER_NEAREST;
+       } else {
+           /* 0.5 is enough for a bilinear filter. It's possible we
+            * should defensively use more for CAIRO_FILTER_BEST, but
+            * without a single example, it's hard to know how much
+            * more would be defensive...
+            */
+           pad = 0.5;
+           optimized_filter = pattern->filter;
+       }
+       break;
+
+    case CAIRO_FILTER_FAST:
+    case CAIRO_FILTER_NEAREST:
+    case CAIRO_FILTER_GAUSSIAN:
+    default:
+       pad = 0.;
+       optimized_filter = pattern->filter;
+       break;
+    }
+
+    if (pad_out)
+       *pad_out = pad;
+
+    return optimized_filter;
+}
+
+cairo_filter_t
+_cairo_pattern_sampled_area (const cairo_pattern_t *pattern,
+                            const cairo_rectangle_int_t *extents,
+                            cairo_rectangle_int_t *sample)
+{
+    cairo_filter_t filter;
+    double x1, x2, y1, y2;
+    double pad;
+
+    filter = _cairo_pattern_analyze_filter (pattern, &pad);
+    if (pad == 0.0 && _cairo_matrix_is_identity (&pattern->matrix)) {
+       *sample = *extents;
+       return filter;
+    }
+
+    x1 = extents->x;
+    y1 = extents->y;
+    x2 = extents->x + (int) extents->width;
+    y2 = extents->y + (int) extents->height;
+
+    _cairo_matrix_transform_bounding_box (&pattern->matrix,
+                                         &x1, &y1, &x2, &y2,
+                                         NULL);
+    if (x1 > CAIRO_RECT_INT_MIN)
+       sample->x = floor (x1 - pad);
+    else
+       sample->x = CAIRO_RECT_INT_MIN;
+
+    if (y1 > CAIRO_RECT_INT_MIN)
+       sample->y = floor (y1 - pad);
+    else
+       sample->y = CAIRO_RECT_INT_MIN;
+
+    if (x2 < CAIRO_RECT_INT_MAX)
+       sample->width = ceil (x2 + pad);
+    else
+       sample->width = CAIRO_RECT_INT_MAX;
+
+    if (y2 < CAIRO_RECT_INT_MAX)
+       sample->height = ceil (y2 + pad);
+    else
+       sample->height = CAIRO_RECT_INT_MAX;
+
+    sample->width  -= sample->x;
+    sample->height -= sample->y;
+
+    return filter;
+}
+
+/**
+ * _cairo_pattern_get_extents:
+ *
+ * Return the "target-space" extents of @pattern in @extents.
+ *
+ * For unbounded patterns, the @extents will be initialized with
+ * "infinite" extents, (minimum and maximum fixed-point values).
+ *
+ * XXX: Currently, bounded gradient patterns will also return
+ * "infinite" extents, though it would be possible to optimize these
+ * with a little more work.
+ **/
+void
+_cairo_pattern_get_extents (const cairo_pattern_t         *pattern,
+                           cairo_rectangle_int_t         *extents)
+{
+    double x1, y1, x2, y2;
+    cairo_status_t status;
+
+    switch (pattern->type) {
+    case CAIRO_PATTERN_TYPE_SOLID:
+       goto UNBOUNDED;
+
+    case CAIRO_PATTERN_TYPE_SURFACE:
+       {
+           cairo_rectangle_int_t surface_extents;
+           const cairo_surface_pattern_t *surface_pattern =
+               (const cairo_surface_pattern_t *) pattern;
+           cairo_surface_t *surface = surface_pattern->surface;
+           double pad;
+
+           if (! _cairo_surface_get_extents (surface, &surface_extents))
+               goto UNBOUNDED;
+
+           if (surface_extents.width == 0 || surface_extents.height == 0)
+               goto EMPTY;
+
+           if (pattern->extend != CAIRO_EXTEND_NONE)
+               goto UNBOUNDED;
+
+           /* The filter can effectively enlarge the extents of the
+            * pattern, so extend as necessary.
+            */
+           _cairo_pattern_analyze_filter (&surface_pattern->base, &pad);
+           x1 = surface_extents.x - pad;
+           y1 = surface_extents.y - pad;
+           x2 = surface_extents.x + (int) surface_extents.width  + pad;
+           y2 = surface_extents.y + (int) surface_extents.height + pad;
+       }
+       break;
+
+    case CAIRO_PATTERN_TYPE_RADIAL:
+       {
+           const cairo_radial_pattern_t *radial =
+               (const cairo_radial_pattern_t *) pattern;
+           double cx1, cy1;
+           double cx2, cy2;
+           double r1, r2;
+
+           if (_radial_pattern_is_degenerate (radial)) {
+               /* cairo-gstate should have optimised degenerate
+                * patterns to solid clear patterns, so we can ignore
+                * them here. */
+               goto EMPTY;
+           }
+
+           /* TODO: in some cases (focus outside/on the circle) it is
+            * half-bounded. */
+           if (pattern->extend != CAIRO_EXTEND_NONE)
+               goto UNBOUNDED;
+
+           cx1 = radial->cd1.center.x;
+           cy1 = radial->cd1.center.y;
+           r1  = radial->cd1.radius;
+
+           cx2 = radial->cd2.center.x;
+           cy2 = radial->cd2.center.y;
+           r2  = radial->cd2.radius;
+
+           x1 = MIN (cx1 - r1, cx2 - r2);
+           y1 = MIN (cy1 - r1, cy2 - r2);
+           x2 = MAX (cx1 + r1, cx2 + r2);
+           y2 = MAX (cy1 + r1, cy2 + r2);
+       }
+       break;
+
+    case CAIRO_PATTERN_TYPE_LINEAR:
+       {
+           const cairo_linear_pattern_t *linear =
+               (const cairo_linear_pattern_t *) pattern;
+
+           if (pattern->extend != CAIRO_EXTEND_NONE)
+               goto UNBOUNDED;
+
+           if (_linear_pattern_is_degenerate (linear)) {
+               /* cairo-gstate should have optimised degenerate
+                * patterns to solid ones, so we can again ignore
+                * them here. */
+               goto EMPTY;
+           }
+
+           /* TODO: to get tight extents, use the matrix to transform
+            * the pattern instead of transforming the extents later. */
+           if (pattern->matrix.xy != 0. || pattern->matrix.yx != 0.)
+               goto UNBOUNDED;
+
+           if (linear->pd1.x == linear->pd2.x) {
+               x1 = -HUGE_VAL;
+               x2 = HUGE_VAL;
+               y1 = MIN (linear->pd1.y, linear->pd2.y);
+               y2 = MAX (linear->pd1.y, linear->pd2.y);
+           } else if (linear->pd1.y == linear->pd2.y) {
+               x1 = MIN (linear->pd1.x, linear->pd2.x);
+               x2 = MAX (linear->pd1.x, linear->pd2.x);
+               y1 = -HUGE_VAL;
+               y2 = HUGE_VAL;
+           } else {
+               goto  UNBOUNDED;
+           }
+       }
+       break;
+
+    case CAIRO_PATTERN_TYPE_MESH:
+       {
+           const cairo_mesh_pattern_t *mesh =
+               (const cairo_mesh_pattern_t *) pattern;
+           double padx, pady;
+           cairo_bool_t is_valid;
+
+           is_valid = _cairo_mesh_pattern_coord_box (mesh, &x1, &y1, &x2, &y2);
+           if (!is_valid)
+               goto EMPTY;
+
+           padx = pady = 1.;
+           cairo_matrix_transform_distance (&pattern->matrix, &padx, &pady);
+           padx = fabs (padx);
+           pady = fabs (pady);
+
+           x1 -= padx;
+           y1 -= pady;
+           x2 += padx;
+           y2 += pady;
+       }
+       break;
+
+    default:
+       ASSERT_NOT_REACHED;
+    }
+
+    if (_cairo_matrix_is_translation (&pattern->matrix)) {
+       x1 -= pattern->matrix.x0; x2 -= pattern->matrix.x0;
+       y1 -= pattern->matrix.y0; y2 -= pattern->matrix.y0;
+    } else {
+       cairo_matrix_t imatrix;
+
+       imatrix = pattern->matrix;
+       status = cairo_matrix_invert (&imatrix);
+       /* cairo_pattern_set_matrix ensures the matrix is invertible */
+       assert (status == CAIRO_STATUS_SUCCESS);
+
+       _cairo_matrix_transform_bounding_box (&imatrix,
+                                             &x1, &y1, &x2, &y2,
+                                             NULL);
+    }
+
+    x1 = floor (x1);
+    if (x1 < CAIRO_RECT_INT_MIN)
+       x1 = CAIRO_RECT_INT_MIN;
+    y1 = floor (y1);
+    if (y1 < CAIRO_RECT_INT_MIN)
+       y1 = CAIRO_RECT_INT_MIN;
+
+    x2 = ceil (x2);
+    if (x2 > CAIRO_RECT_INT_MAX)
+       x2 = CAIRO_RECT_INT_MAX;
+    y2 = ceil (y2);
+    if (y2 > CAIRO_RECT_INT_MAX)
+       y2 = CAIRO_RECT_INT_MAX;
+
+    extents->x = x1; extents->width  = x2 - x1;
+    extents->y = y1; extents->height = y2 - y1;
+    return;
+
+  UNBOUNDED:
+    /* unbounded patterns -> 'infinite' extents */
+    _cairo_unbounded_rectangle_init (extents);
+    return;
+
+  EMPTY:
+    extents->x = extents->y = 0;
+    extents->width = extents->height = 0;
+    return;
+}
+
+/**
+ * _cairo_pattern_get_ink_extents:
+ *
+ * Return the "target-space" inked extents of @pattern in @extents.
+ **/
+cairo_int_status_t
+_cairo_pattern_get_ink_extents (const cairo_pattern_t         *pattern,
+                               cairo_rectangle_int_t         *extents)
+{
+    if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE &&
+       pattern->extend == CAIRO_EXTEND_NONE)
+    {
+       const cairo_surface_pattern_t *surface_pattern =
+           (const cairo_surface_pattern_t *) pattern;
+       cairo_surface_t *surface = surface_pattern->surface;
+
+       if (_cairo_surface_is_recording (surface)) {
+           cairo_matrix_t imatrix;
+           cairo_box_t box;
+           cairo_status_t status;
+
+           imatrix = pattern->matrix;
+           status = cairo_matrix_invert (&imatrix);
+           /* cairo_pattern_set_matrix ensures the matrix is invertible */
+           assert (status == CAIRO_STATUS_SUCCESS);
+
+           status = _cairo_recording_surface_get_ink_bbox ((cairo_recording_surface_t *)surface,
+                                                  &box, &imatrix);
+           if (unlikely (status))
+               return status;
+
+           _cairo_box_round_to_rectangle (&box, extents);
+           return CAIRO_STATUS_SUCCESS;
+       }
+    }
+
+    _cairo_pattern_get_extents (pattern, extents);
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static unsigned long
+_cairo_solid_pattern_hash (unsigned long hash,
+                          const cairo_solid_pattern_t *solid)
+{
+    hash = _cairo_hash_bytes (hash, &solid->color, sizeof (solid->color));
+
+    return hash;
+}
+
+static unsigned long
+_cairo_gradient_color_stops_hash (unsigned long hash,
+                                 const cairo_gradient_pattern_t *gradient)
+{
+    unsigned int n;
+
+    hash = _cairo_hash_bytes (hash,
+                             &gradient->n_stops,
+                             sizeof (gradient->n_stops));
+
+    for (n = 0; n < gradient->n_stops; n++) {
+       hash = _cairo_hash_bytes (hash,
+                                 &gradient->stops[n].offset,
+                                 sizeof (double));
+       hash = _cairo_hash_bytes (hash,
+                                 &gradient->stops[n].color,
+                                 sizeof (cairo_color_stop_t));
+    }
+
+    return hash;
+}
+
+unsigned long
+_cairo_linear_pattern_hash (unsigned long hash,
+                           const cairo_linear_pattern_t *linear)
+{
+    hash = _cairo_hash_bytes (hash, &linear->pd1, sizeof (linear->pd1));
+    hash = _cairo_hash_bytes (hash, &linear->pd2, sizeof (linear->pd2));
+
+    return _cairo_gradient_color_stops_hash (hash, &linear->base);
+}
+
+unsigned long
+_cairo_radial_pattern_hash (unsigned long hash,
+                           const cairo_radial_pattern_t *radial)
+{
+    hash = _cairo_hash_bytes (hash, &radial->cd1.center, sizeof (radial->cd1.center));
+    hash = _cairo_hash_bytes (hash, &radial->cd1.radius, sizeof (radial->cd1.radius));
+    hash = _cairo_hash_bytes (hash, &radial->cd2.center, sizeof (radial->cd2.center));
+    hash = _cairo_hash_bytes (hash, &radial->cd2.radius, sizeof (radial->cd2.radius));
+
+    return _cairo_gradient_color_stops_hash (hash, &radial->base);
+}
+
+static unsigned long
+_cairo_mesh_pattern_hash (unsigned long hash, const cairo_mesh_pattern_t *mesh)
+{
+    const cairo_mesh_patch_t *patch = _cairo_array_index_const (&mesh->patches, 0);
+    unsigned int i, n = _cairo_array_num_elements (&mesh->patches);
+
+    for (i = 0; i < n; i++)
+       hash = _cairo_hash_bytes (hash, patch + i, sizeof (cairo_mesh_patch_t));
+
+    return hash;
+}
+
+static unsigned long
+_cairo_surface_pattern_hash (unsigned long hash,
+                            const cairo_surface_pattern_t *surface)
+{
+    hash ^= surface->surface->unique_id;
+
+    return hash;
+}
+
+unsigned long
+_cairo_pattern_hash (const cairo_pattern_t *pattern)
+{
+    unsigned long hash = _CAIRO_HASH_INIT_VALUE;
+
+    if (pattern->status)
+       return 0;
+
+    hash = _cairo_hash_bytes (hash, &pattern->type, sizeof (pattern->type));
+    if (pattern->type != CAIRO_PATTERN_TYPE_SOLID) {
+       hash = _cairo_hash_bytes (hash,
+                                 &pattern->matrix, sizeof (pattern->matrix));
+       hash = _cairo_hash_bytes (hash,
+                                 &pattern->filter, sizeof (pattern->filter));
+       hash = _cairo_hash_bytes (hash,
+                                 &pattern->extend, sizeof (pattern->extend));
+       hash = _cairo_hash_bytes (hash,
+                                 &pattern->has_component_alpha,
+                                 sizeof (pattern->has_component_alpha));
+    }
+
+    switch (pattern->type) {
+    case CAIRO_PATTERN_TYPE_SOLID:
+       return _cairo_solid_pattern_hash (hash, (cairo_solid_pattern_t *) pattern);
+    case CAIRO_PATTERN_TYPE_LINEAR:
+       return _cairo_linear_pattern_hash (hash, (cairo_linear_pattern_t *) pattern);
+    case CAIRO_PATTERN_TYPE_RADIAL:
+       return _cairo_radial_pattern_hash (hash, (cairo_radial_pattern_t *) pattern);
+    case CAIRO_PATTERN_TYPE_MESH:
+       return _cairo_mesh_pattern_hash (hash, (cairo_mesh_pattern_t *) pattern);
+    case CAIRO_PATTERN_TYPE_SURFACE:
+       return _cairo_surface_pattern_hash (hash, (cairo_surface_pattern_t *) pattern);
+    default:
+       ASSERT_NOT_REACHED;
+       return FALSE;
+    }
+}
+
+static cairo_bool_t
+_cairo_solid_pattern_equal (const cairo_solid_pattern_t *a,
+                           const cairo_solid_pattern_t *b)
+{
+    return _cairo_color_equal (&a->color, &b->color);
+}
+
+static cairo_bool_t
+_cairo_gradient_color_stops_equal (const cairo_gradient_pattern_t *a,
+                                  const cairo_gradient_pattern_t *b)
+{
+    unsigned int n;
+
+    if (a->n_stops != b->n_stops)
+       return FALSE;
+
+    for (n = 0; n < a->n_stops; n++) {
+       if (a->stops[n].offset != b->stops[n].offset)
+           return FALSE;
+       if (! _cairo_color_stop_equal (&a->stops[n].color, &b->stops[n].color))
+           return FALSE;
+    }
+
+    return TRUE;
+}
+
+cairo_bool_t
+_cairo_linear_pattern_equal (const cairo_linear_pattern_t *a,
+                            const cairo_linear_pattern_t *b)
+{
+    if (a->pd1.x != b->pd1.x)
+       return FALSE;
+
+    if (a->pd1.y != b->pd1.y)
+       return FALSE;
+
+    if (a->pd2.x != b->pd2.x)
+       return FALSE;
+
+    if (a->pd2.y != b->pd2.y)
+       return FALSE;
+
+    return _cairo_gradient_color_stops_equal (&a->base, &b->base);
+}
+
+cairo_bool_t
+_cairo_radial_pattern_equal (const cairo_radial_pattern_t *a,
+                            const cairo_radial_pattern_t *b)
+{
+    if (a->cd1.center.x != b->cd1.center.x)
+       return FALSE;
+
+    if (a->cd1.center.y != b->cd1.center.y)
+       return FALSE;
+
+    if (a->cd1.radius != b->cd1.radius)
+       return FALSE;
+
+    if (a->cd2.center.x != b->cd2.center.x)
+       return FALSE;
+
+    if (a->cd2.center.y != b->cd2.center.y)
+       return FALSE;
+
+    if (a->cd2.radius != b->cd2.radius)
+       return FALSE;
+
+    return _cairo_gradient_color_stops_equal (&a->base, &b->base);
+}
+
+static cairo_bool_t
+_cairo_mesh_pattern_equal (const cairo_mesh_pattern_t *a,
+                          const cairo_mesh_pattern_t *b)
+{
+    const cairo_mesh_patch_t *patch_a, *patch_b;
+    unsigned int i, num_patches_a, num_patches_b;
+
+    num_patches_a = _cairo_array_num_elements (&a->patches);
+    num_patches_b = _cairo_array_num_elements (&b->patches);
+
+    if (num_patches_a != num_patches_b)
+       return FALSE;
+
+    for (i = 0; i < num_patches_a; i++) {
+       patch_a = _cairo_array_index_const (&a->patches, i);
+       patch_b = _cairo_array_index_const (&a->patches, i);
+       if (memcmp (patch_a, patch_b, sizeof(cairo_mesh_patch_t)) != 0)
+           return FALSE;
+    }
+
+    return TRUE;
+}
+
+static cairo_bool_t
+_cairo_surface_pattern_equal (const cairo_surface_pattern_t *a,
+                             const cairo_surface_pattern_t *b)
+{
+    return a->surface->unique_id == b->surface->unique_id;
+}
+
+cairo_bool_t
+_cairo_pattern_equal (const cairo_pattern_t *a, const cairo_pattern_t *b)
+{
+    if (a->status || b->status)
+       return FALSE;
+
+    if (a == b)
+       return TRUE;
+
+    if (a->type != b->type)
+       return FALSE;
+
+    if (a->has_component_alpha != b->has_component_alpha)
+       return FALSE;
+
+    if (a->type != CAIRO_PATTERN_TYPE_SOLID) {
+       if (memcmp (&a->matrix, &b->matrix, sizeof (cairo_matrix_t)))
+           return FALSE;
+
+       if (a->filter != b->filter)
+           return FALSE;
+
+       if (a->extend != b->extend)
+           return FALSE;
+    }
+
+    switch (a->type) {
+    case CAIRO_PATTERN_TYPE_SOLID:
+       return _cairo_solid_pattern_equal ((cairo_solid_pattern_t *) a,
+                                          (cairo_solid_pattern_t *) b);
+    case CAIRO_PATTERN_TYPE_LINEAR:
+       return _cairo_linear_pattern_equal ((cairo_linear_pattern_t *) a,
+                                           (cairo_linear_pattern_t *) b);
+    case CAIRO_PATTERN_TYPE_RADIAL:
+       return _cairo_radial_pattern_equal ((cairo_radial_pattern_t *) a,
+                                           (cairo_radial_pattern_t *) b);
+    case CAIRO_PATTERN_TYPE_MESH:
+       return _cairo_mesh_pattern_equal ((cairo_mesh_pattern_t *) a,
+                                         (cairo_mesh_pattern_t *) b);
+    case CAIRO_PATTERN_TYPE_SURFACE:
+       return _cairo_surface_pattern_equal ((cairo_surface_pattern_t *) a,
+                                            (cairo_surface_pattern_t *) b);
+    default:
+       ASSERT_NOT_REACHED;
+       return FALSE;
+    }
+}
+
+/**
+ * cairo_pattern_get_rgba
+ * @pattern: a #cairo_pattern_t
+ * @red: return value for red component of color, or %NULL
+ * @green: return value for green component of color, or %NULL
+ * @blue: return value for blue component of color, or %NULL
+ * @alpha: return value for alpha component of color, or %NULL
+ *
+ * Gets the solid color for a solid color pattern.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS, or
+ * %CAIRO_STATUS_PATTERN_TYPE_MISMATCH if the pattern is not a solid
+ * color pattern.
+ *
+ * Since: 1.4
+ **/
+cairo_status_t
+cairo_pattern_get_rgba (cairo_pattern_t *pattern,
+                       double *red, double *green,
+                       double *blue, double *alpha)
+{
+    cairo_solid_pattern_t *solid = (cairo_solid_pattern_t*) pattern;
+    double r0, g0, b0, a0;
+
+    if (pattern->status)
+       return pattern->status;
+
+    if (pattern->type != CAIRO_PATTERN_TYPE_SOLID)
+       return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH);
+
+    _cairo_color_get_rgba (&solid->color, &r0, &g0, &b0, &a0);
+
+    if (red)
+       *red = r0;
+    if (green)
+       *green = g0;
+    if (blue)
+       *blue = b0;
+    if (alpha)
+       *alpha = a0;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+/**
+ * cairo_pattern_get_surface
+ * @pattern: a #cairo_pattern_t
+ * @surface: return value for surface of pattern, or %NULL
+ * 
+ * Gets the surface of a surface pattern.  The reference returned in
+ * @surface is owned by the pattern; the caller should call
+ * cairo_surface_reference() if the surface is to be retained.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS, or
+ * %CAIRO_STATUS_PATTERN_TYPE_MISMATCH if the pattern is not a surface
+ * pattern.
+ *
+ * Since: 1.4
+ **/
+cairo_status_t
+cairo_pattern_get_surface (cairo_pattern_t *pattern,
+                          cairo_surface_t **surface)
+{
+    cairo_surface_pattern_t *spat = (cairo_surface_pattern_t*) pattern;
+
+    if (pattern->status)
+       return pattern->status;
+
+    if (pattern->type != CAIRO_PATTERN_TYPE_SURFACE)
+       return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH);
+
+    if (surface)
+       *surface = spat->surface;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+/**
+ * cairo_pattern_get_color_stop_rgba
+ * @pattern: a #cairo_pattern_t
+ * @index: index of the stop to return data for
+ * @offset: return value for the offset of the stop, or %NULL
+ * @red: return value for red component of color, or %NULL
+ * @green: return value for green component of color, or %NULL
+ * @blue: return value for blue component of color, or %NULL
+ * @alpha: return value for alpha component of color, or %NULL
+ *
+ * Gets the color and offset information at the given @index for a
+ * gradient pattern.  Values of @index are 0 to 1 less than the number
+ * returned by cairo_pattern_get_color_stop_count().
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS, or %CAIRO_STATUS_INVALID_INDEX
+ * if @index is not valid for the given pattern.  If the pattern is
+ * not a gradient pattern, %CAIRO_STATUS_PATTERN_TYPE_MISMATCH is
+ * returned.
+ *
+ * Since: 1.4
+ **/
+cairo_status_t
+cairo_pattern_get_color_stop_rgba (cairo_pattern_t *pattern,
+                                  int index, double *offset,
+                                  double *red, double *green,
+                                  double *blue, double *alpha)
+{
+    cairo_gradient_pattern_t *gradient = (cairo_gradient_pattern_t*) pattern;
+
+    if (pattern->status)
+       return pattern->status;
+
+    if (pattern->type != CAIRO_PATTERN_TYPE_LINEAR &&
+       pattern->type != CAIRO_PATTERN_TYPE_RADIAL)
+       return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH);
+
+    if (index < 0 || (unsigned int) index >= gradient->n_stops)
+       return _cairo_error (CAIRO_STATUS_INVALID_INDEX);
+
+    if (offset)
+       *offset = gradient->stops[index].offset;
+    if (red)
+       *red = gradient->stops[index].color.red;
+    if (green)
+       *green = gradient->stops[index].color.green;
+    if (blue)
+       *blue = gradient->stops[index].color.blue;
+    if (alpha)
+       *alpha = gradient->stops[index].color.alpha;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+/**
+ * cairo_pattern_get_color_stop_count
+ * @pattern: a #cairo_pattern_t
+ * @count: return value for the number of color stops, or %NULL
+ *
+ * Gets the number of color stops specified in the given gradient
+ * pattern.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS, or
+ * %CAIRO_STATUS_PATTERN_TYPE_MISMATCH if @pattern is not a gradient
+ * pattern.
+ *
+ * Since: 1.4
+ */
+cairo_status_t
+cairo_pattern_get_color_stop_count (cairo_pattern_t *pattern,
+                                   int *count)
+{
+    cairo_gradient_pattern_t *gradient = (cairo_gradient_pattern_t*) pattern;
+
+    if (pattern->status)
+       return pattern->status;
+
+    if (pattern->type != CAIRO_PATTERN_TYPE_LINEAR &&
+       pattern->type != CAIRO_PATTERN_TYPE_RADIAL)
+       return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH);
+
+    if (count)
+       *count = gradient->n_stops;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+/**
+ * cairo_pattern_get_linear_points
+ * @pattern: a #cairo_pattern_t
+ * @x0: return value for the x coordinate of the first point, or %NULL
+ * @y0: return value for the y coordinate of the first point, or %NULL
+ * @x1: return value for the x coordinate of the second point, or %NULL
+ * @y1: return value for the y coordinate of the second point, or %NULL
+ *
+ * Gets the gradient endpoints for a linear gradient.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS, or
+ * %CAIRO_STATUS_PATTERN_TYPE_MISMATCH if @pattern is not a linear
+ * gradient pattern.
+ *
+ * Since: 1.4
+ **/
+cairo_status_t
+cairo_pattern_get_linear_points (cairo_pattern_t *pattern,
+                                double *x0, double *y0,
+                                double *x1, double *y1)
+{
+    cairo_linear_pattern_t *linear = (cairo_linear_pattern_t*) pattern;
+
+    if (pattern->status)
+       return pattern->status;
+
+    if (pattern->type != CAIRO_PATTERN_TYPE_LINEAR)
+       return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH);
+
+    if (x0)
+       *x0 = linear->pd1.x;
+    if (y0)
+       *y0 = linear->pd1.y;
+    if (x1)
+       *x1 = linear->pd2.x;
+    if (y1)
+       *y1 = linear->pd2.y;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+/**
+ * cairo_pattern_get_radial_circles
+ * @pattern: a #cairo_pattern_t
+ * @x0: return value for the x coordinate of the center of the first circle, or %NULL
+ * @y0: return value for the y coordinate of the center of the first circle, or %NULL
+ * @r0: return value for the radius of the first circle, or %NULL
+ * @x1: return value for the x coordinate of the center of the second circle, or %NULL
+ * @y1: return value for the y coordinate of the center of the second circle, or %NULL
+ * @r1: return value for the radius of the second circle, or %NULL
+ *
+ * Gets the gradient endpoint circles for a radial gradient, each
+ * specified as a center coordinate and a radius.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS, or
+ * %CAIRO_STATUS_PATTERN_TYPE_MISMATCH if @pattern is not a radial
+ * gradient pattern.
+ *
+ * Since: 1.4
+ **/
+cairo_status_t
+cairo_pattern_get_radial_circles (cairo_pattern_t *pattern,
+                                 double *x0, double *y0, double *r0,
+                                 double *x1, double *y1, double *r1)
+{
+    cairo_radial_pattern_t *radial = (cairo_radial_pattern_t*) pattern;
+
+    if (pattern->status)
+       return pattern->status;
+
+    if (pattern->type != CAIRO_PATTERN_TYPE_RADIAL)
+       return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH);
+
+    if (x0)
+       *x0 = radial->cd1.center.x;
+    if (y0)
+       *y0 = radial->cd1.center.y;
+    if (r0)
+       *r0 = radial->cd1.radius;
+    if (x1)
+       *x1 = radial->cd2.center.x;
+    if (y1)
+       *y1 = radial->cd2.center.y;
+    if (r1)
+       *r1 = radial->cd2.radius;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+/**
+ * cairo_mesh_pattern_get_patch_count
+ * @pattern: a #cairo_pattern_t
+ * @count: return value for the number patches, or %NULL
+ *
+ * Gets the number of patches specified in the given mesh pattern.
+ *
+ * The number only includes patches which have been finished by
+ * calling cairo_mesh_pattern_end_patch(). For example it will be 0
+ * during the definition of the first patch.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS, or
+ * %CAIRO_STATUS_PATTERN_TYPE_MISMATCH if @pattern is not a mesh
+ * pattern.
+ *
+ * Since: 1.12
+ */
+cairo_status_t
+cairo_mesh_pattern_get_patch_count (cairo_pattern_t *pattern,
+                                   unsigned int *count)
+{
+    cairo_mesh_pattern_t *mesh = (cairo_mesh_pattern_t *) pattern;
+
+    if (unlikely (pattern->status))
+       return pattern->status;
+
+    if (unlikely (pattern->type != CAIRO_PATTERN_TYPE_MESH))
+       return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH);
+
+    if (count) {
+       *count = _cairo_array_num_elements (&mesh->patches);
+       if (mesh->current_patch)
+           *count -= 1;
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+slim_hidden_def (cairo_mesh_pattern_get_patch_count);
+
+/**
+ * cairo_mesh_pattern_get_path
+ * @pattern: a #cairo_pattern_t
+ * @patch_num: the patch number to return data for
+ *
+ * Gets path defining the patch @patch_num for a mesh
+ * pattern.
+ *
+ * @patch_num can range 0 to 1 less than the number returned by
+ * cairo_mesh_pattern_get_patch_count().
+ *
+ * Return value: the path defining the patch, or a path with status
+ * %CAIRO_STATUS_INVALID_INDEX if @patch_num or @point_num is not
+ * valid for @pattern. If @pattern is not a mesh pattern, a path with
+ * status %CAIRO_STATUS_PATTERN_TYPE_MISMATCH is returned.
+ *
+ * Since: 1.12
+ */
+cairo_path_t *
+cairo_mesh_pattern_get_path (cairo_pattern_t *pattern,
+                            unsigned int patch_num)
+{
+    cairo_mesh_pattern_t *mesh = (cairo_mesh_pattern_t *) pattern;
+    const cairo_mesh_patch_t *patch;
+    cairo_path_t *path;
+    cairo_path_data_t *data;
+    unsigned int patch_count;
+    int l, current_point;
+
+    if (unlikely (pattern->status))
+       return _cairo_path_create_in_error (pattern->status);
+
+    if (unlikely (pattern->type != CAIRO_PATTERN_TYPE_MESH))
+       return _cairo_path_create_in_error (_cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH));
+
+    patch_count = _cairo_array_num_elements (&mesh->patches);
+    if (mesh->current_patch)
+       patch_count--;
+
+    if (unlikely (patch_num >= patch_count))
+       return _cairo_path_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_INDEX));
+
+    patch = _cairo_array_index_const (&mesh->patches, patch_num);
+
+    path = malloc (sizeof (cairo_path_t));
+    if (path == NULL)
+       return _cairo_path_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+    path->num_data = 18;
+    path->data = _cairo_malloc_ab (path->num_data,
+                                  sizeof (cairo_path_data_t));
+    if (path->data == NULL) {
+       free (path);
+       return _cairo_path_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+    }
+
+    data = path->data;
+    data[0].header.type = CAIRO_PATH_MOVE_TO;
+    data[0].header.length = 2;
+    data[1].point.x = patch->points[0][0].x;
+    data[1].point.y = patch->points[0][0].y;
+    data += data[0].header.length;
+
+    current_point = 0;
+
+    for (l = 0; l < 4; l++) {
+       int i, j, k;
+
+       data[0].header.type = CAIRO_PATH_CURVE_TO;
+       data[0].header.length = 4;
+
+       for (k = 1; k < 4; k++) {
+           current_point = (current_point + 1) % 12;
+           i = mesh_path_point_i[current_point];
+           j = mesh_path_point_j[current_point];
+           data[k].point.x = patch->points[i][j].x;
+           data[k].point.y = patch->points[i][j].y;
+       }
+
+       data += data[0].header.length;
+    }
+
+    path->status = CAIRO_STATUS_SUCCESS;
+
+    return path;
+}
+slim_hidden_def (cairo_mesh_pattern_get_path);
+
+/**
+ * cairo_mesh_pattern_get_corner_color_rgba
+ * @pattern: a #cairo_pattern_t
+ * @patch_num: the patch number to return data for
+ * @corner_num: the corner number to return data for
+ * @red: return value for red component of color, or %NULL
+ * @green: return value for green component of color, or %NULL
+ * @blue: return value for blue component of color, or %NULL
+ * @alpha: return value for alpha component of color, or %NULL
+ *
+ * Gets the color information in corner @corner_num of patch
+ * @patch_num for a mesh pattern.
+ *
+ * @patch_num can range 0 to 1 less than the number returned by
+ * cairo_mesh_pattern_get_patch_count().
+ *
+ * Valid values for @corner_num are from 0 to 3 and identify the
+ * corners as explained in cairo_pattern_create_mesh().
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS, or %CAIRO_STATUS_INVALID_INDEX
+ * if @patch_num or @corner_num is not valid for @pattern. If
+ * @pattern is not a mesh pattern, %CAIRO_STATUS_PATTERN_TYPE_MISMATCH
+ * is returned.
+ *
+ * Since: 1.12
+ **/
+cairo_status_t
+cairo_mesh_pattern_get_corner_color_rgba (cairo_pattern_t *pattern,
+                                         unsigned int patch_num,
+                                         unsigned int corner_num,
+                                         double *red, double *green,
+                                         double *blue, double *alpha)
+{
+    cairo_mesh_pattern_t *mesh = (cairo_mesh_pattern_t *) pattern;
+    unsigned int patch_count;
+    const cairo_mesh_patch_t *patch;
+
+    if (unlikely (pattern->status))
+       return pattern->status;
+
+    if (unlikely (pattern->type != CAIRO_PATTERN_TYPE_MESH))
+       return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH);
+
+    if (unlikely (corner_num > 3))
+       return _cairo_error (CAIRO_STATUS_INVALID_INDEX);
+
+    patch_count = _cairo_array_num_elements (&mesh->patches);
+    if (mesh->current_patch)
+       patch_count--;
+
+    if (unlikely (patch_num >= patch_count))
+       return _cairo_error (CAIRO_STATUS_INVALID_INDEX);
+
+    patch = _cairo_array_index_const (&mesh->patches, patch_num);
+
+    if (red)
+       *red = patch->colors[corner_num].red;
+    if (green)
+       *green = patch->colors[corner_num].green;
+    if (blue)
+       *blue = patch->colors[corner_num].blue;
+    if (alpha)
+       *alpha = patch->colors[corner_num].alpha;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+slim_hidden_def (cairo_mesh_pattern_get_corner_color_rgba);
+
+/**
+ * cairo_mesh_pattern_get_control_point
+ * @pattern: a #cairo_pattern_t
+ * @patch_num: the patch number to return data for
+ * @point_num: the control point number to return data for
+ * @x: return value for the x coordinate of the control point, or %NULL
+ * @y: return value for the y coordinate of the control point, or %NULL
+ *
+ * Gets the control point @point_num of patch @patch_num for a mesh
+ * pattern.
+ *
+ * @patch_num can range 0 to 1 less than the number returned by
+ * cairo_mesh_pattern_get_patch_count().
+ *
+ * Valid values for @point_num are from 0 to 3 and identify the
+ * control points as explained in cairo_pattern_create_mesh().
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS, or %CAIRO_STATUS_INVALID_INDEX
+ * if @patch_num or @point_num is not valid for @pattern. If @pattern
+ * is not a mesh pattern, %CAIRO_STATUS_PATTERN_TYPE_MISMATCH is
+ * returned.
+ *
+ * Since: 1.12
+ **/
+cairo_status_t
+cairo_mesh_pattern_get_control_point (cairo_pattern_t *pattern,
+                                     unsigned int patch_num,
+                                     unsigned int point_num,
+                                     double *x, double *y)
+{
+    cairo_mesh_pattern_t *mesh = (cairo_mesh_pattern_t *) pattern;
+    const cairo_mesh_patch_t *patch;
+    unsigned int patch_count;
+    int i, j;
+
+    if (pattern->status)
+       return pattern->status;
+
+    if (pattern->type != CAIRO_PATTERN_TYPE_MESH)
+       return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH);
+
+    if (point_num > 3)
+       return _cairo_error (CAIRO_STATUS_INVALID_INDEX);
+
+    patch_count = _cairo_array_num_elements (&mesh->patches);
+    if (mesh->current_patch)
+       patch_count--;
+
+    if (unlikely (patch_num >= patch_count))
+       return _cairo_error (CAIRO_STATUS_INVALID_INDEX);
+
+    patch = _cairo_array_index_const (&mesh->patches, patch_num);
+
+    i = mesh_control_point_i[point_num];
+    j = mesh_control_point_j[point_num];
+
+    if (x)
+       *x = patch->points[i][j].x;
+    if (y)
+       *y = patch->points[i][j].y;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+slim_hidden_def (cairo_mesh_pattern_get_control_point);
+
+void
+_cairo_pattern_reset_static_data (void)
+{
+    int i;
+
+    for (i = 0; i < ARRAY_LENGTH (freed_pattern_pool); i++)
+       _freed_pool_reset (&freed_pattern_pool[i]);
+}
+
+static void
+_cairo_debug_print_surface_pattern (FILE *file,
+                                   const cairo_surface_pattern_t *pattern)
+{
+    printf ("  surface type: %d\n", pattern->surface->type);
+}
+
+static void
+_cairo_debug_print_linear_pattern (FILE *file,
+                                   const cairo_linear_pattern_t *pattern)
+{
+}
+
+static void
+_cairo_debug_print_radial_pattern (FILE *file,
+                                  const cairo_radial_pattern_t *pattern)
+{
+}
+
+static void
+_cairo_debug_print_mesh_pattern (FILE *file,
+                                const cairo_mesh_pattern_t *pattern)
+{
+}
+
+void
+_cairo_debug_print_pattern (FILE *file, const cairo_pattern_t *pattern)
+{
+    const char *s;
+    switch (pattern->type) {
+    case CAIRO_PATTERN_TYPE_SOLID: s = "solid"; break;
+    case CAIRO_PATTERN_TYPE_SURFACE: s = "surface"; break;
+    case CAIRO_PATTERN_TYPE_LINEAR: s = "linear"; break;
+    case CAIRO_PATTERN_TYPE_RADIAL: s = "radial"; break;
+    case CAIRO_PATTERN_TYPE_MESH: s = "mesh"; break;
+    default: s = "invalid"; ASSERT_NOT_REACHED; break;
+    }
+
+    fprintf (file, "pattern: %s\n", s);
+    if (pattern->type == CAIRO_PATTERN_TYPE_SOLID)
+       return;
+
+    switch (pattern->extend) {
+    case CAIRO_EXTEND_NONE: s = "none"; break;
+    case CAIRO_EXTEND_REPEAT: s = "repeat"; break;
+    case CAIRO_EXTEND_REFLECT: s = "reflect"; break;
+    case CAIRO_EXTEND_PAD: s = "pad"; break;
+    default: s = "invalid"; ASSERT_NOT_REACHED; break;
+    }
+    fprintf (file, "  extend: %s\n", s);
+
+    switch (pattern->filter) {
+    case CAIRO_FILTER_FAST: s = "fast"; break;
+    case CAIRO_FILTER_GOOD: s = "good"; break;
+    case CAIRO_FILTER_BEST: s = "best"; break;
+    case CAIRO_FILTER_NEAREST: s = "nearest"; break;
+    case CAIRO_FILTER_BILINEAR: s = "bilinear"; break;
+    case CAIRO_FILTER_GAUSSIAN: s = "guassian"; break;
+    default: s = "invalid"; ASSERT_NOT_REACHED; break;
+    }
+    fprintf (file, "  filter: %s\n", s);
+    fprintf (file, "  matrix: [%g %g %g %g %g %g]\n",
+            pattern->matrix.xx, pattern->matrix.yx,
+            pattern->matrix.xy, pattern->matrix.yy,
+            pattern->matrix.x0, pattern->matrix.y0);
+    switch (pattern->type) {
+    default:
+    case CAIRO_PATTERN_TYPE_SOLID:
+       break;
+    case CAIRO_PATTERN_TYPE_SURFACE:
+       _cairo_debug_print_surface_pattern (file, (cairo_surface_pattern_t *)pattern);
+       break;
+    case CAIRO_PATTERN_TYPE_LINEAR:
+       _cairo_debug_print_linear_pattern (file, (cairo_linear_pattern_t *)pattern);
+       break;
+    case CAIRO_PATTERN_TYPE_RADIAL:
+       _cairo_debug_print_radial_pattern (file, (cairo_radial_pattern_t *)pattern);
+       break;
+    case CAIRO_PATTERN_TYPE_MESH:
+       _cairo_debug_print_mesh_pattern (file, (cairo_mesh_pattern_t *)pattern);
+       break;
+    }
+}
diff --git a/src/cairo-pdf-operators-private.h b/src/cairo-pdf-operators-private.h
new file mode 100644 (file)
index 0000000..6e1ae18
--- /dev/null
@@ -0,0 +1,173 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2004 Red Hat, Inc
+ * Copyright © 2006 Red Hat, Inc
+ * Copyright © 2007 Adrian Johnson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *     Kristian Høgsberg <krh@redhat.com>
+ *     Carl Worth <cworth@cworth.org>
+ *     Adrian Johnson <ajohnson@redneon.com>
+ */
+
+#ifndef CAIRO_PDF_OPERATORS_H
+#define CAIRO_PDF_OPERATORS_H
+
+#include "cairo-compiler-private.h"
+#include "cairo-error-private.h"
+#include "cairo-types-private.h"
+
+/* The glyph buffer size is based on the expected maximum glyphs in a
+ * line so that an entire line can be emitted in as one string. If the
+ * glyphs in a line exceeds this size the only downside is the slight
+ * overhead of emitting two strings.
+ */
+#define PDF_GLYPH_BUFFER_SIZE 200
+
+typedef cairo_status_t (*cairo_pdf_operators_use_font_subset_t) (unsigned int  font_id,
+                                                                unsigned int  subset_id,
+                                                                void         *closure);
+
+typedef struct _cairo_pdf_glyph {
+    unsigned int glyph_index;
+    double x_position;
+    double x_advance;
+} cairo_pdf_glyph_t;
+
+typedef struct _cairo_pdf_operators {
+    cairo_output_stream_t *stream;
+    cairo_matrix_t cairo_to_pdf;
+    cairo_scaled_font_subsets_t *font_subsets;
+    cairo_pdf_operators_use_font_subset_t use_font_subset;
+    void *use_font_subset_closure;
+    cairo_bool_t use_actual_text;
+    cairo_bool_t in_text_object; /* inside BT/ET pair */
+
+    /* PDF text state */
+    cairo_bool_t is_new_text_object; /* text object started but matrix and font not yet selected */
+    unsigned int font_id;
+    unsigned int subset_id;
+    cairo_matrix_t text_matrix; /* PDF text matrix (Tlm in the PDF reference) */
+    cairo_matrix_t cairo_to_pdftext; /* translate cairo coords to PDF text space */
+    cairo_matrix_t font_matrix_inverse;
+    double cur_x; /* Current position in PDF text space (Tm in the PDF reference) */
+    double cur_y;
+    int hex_width;
+    cairo_bool_t is_latin;
+    int num_glyphs;
+    double glyph_buf_x_pos;
+    cairo_pdf_glyph_t glyphs[PDF_GLYPH_BUFFER_SIZE];
+
+    /* PDF line style */
+    cairo_bool_t         has_line_style;
+    double              line_width;
+    cairo_line_cap_t    line_cap;
+    cairo_line_join_t   line_join;
+    double              miter_limit;
+    cairo_bool_t         has_dashes;
+} cairo_pdf_operators_t;
+
+cairo_private void
+_cairo_pdf_operators_init (cairo_pdf_operators_t       *pdf_operators,
+                          cairo_output_stream_t       *stream,
+                          cairo_matrix_t              *cairo_to_pdf,
+                          cairo_scaled_font_subsets_t *font_subsets);
+
+cairo_private cairo_status_t
+_cairo_pdf_operators_fini (cairo_pdf_operators_t       *pdf_operators);
+
+cairo_private void
+_cairo_pdf_operators_set_font_subsets_callback (cairo_pdf_operators_t               *pdf_operators,
+                                               cairo_pdf_operators_use_font_subset_t use_font_subset,
+                                               void                                 *closure);
+
+cairo_private void
+_cairo_pdf_operators_set_stream (cairo_pdf_operators_t          *pdf_operators,
+                                cairo_output_stream_t   *stream);
+
+
+cairo_private void
+_cairo_pdf_operators_set_cairo_to_pdf_matrix (cairo_pdf_operators_t *pdf_operators,
+                                             cairo_matrix_t        *cairo_to_pdf);
+
+cairo_private void
+_cairo_pdf_operators_enable_actual_text (cairo_pdf_operators_t *pdf_operators,
+                                        cairo_bool_t           enable);
+
+cairo_private cairo_status_t
+_cairo_pdf_operators_flush (cairo_pdf_operators_t       *pdf_operators);
+
+cairo_private void
+_cairo_pdf_operators_reset (cairo_pdf_operators_t       *pdf_operators);
+
+cairo_private cairo_int_status_t
+_cairo_pdf_operators_clip (cairo_pdf_operators_t       *pdf_operators,
+                          const cairo_path_fixed_t     *path,
+                          cairo_fill_rule_t             fill_rule);
+
+cairo_private cairo_int_status_t
+_cairo_pdf_operators_emit_stroke_style (cairo_pdf_operators_t          *pdf_operators,
+                                       const cairo_stroke_style_t      *style,
+                                       double                           scale);
+
+cairo_private cairo_int_status_t
+_cairo_pdf_operators_stroke (cairo_pdf_operators_t     *pdf_operators,
+                            const cairo_path_fixed_t   *path,
+                            const cairo_stroke_style_t *style,
+                            const cairo_matrix_t       *ctm,
+                            const cairo_matrix_t       *ctm_inverse);
+
+cairo_private cairo_int_status_t
+_cairo_pdf_operators_fill (cairo_pdf_operators_t       *pdf_operators,
+                          const cairo_path_fixed_t     *path,
+                          cairo_fill_rule_t            fill_rule);
+
+cairo_private cairo_int_status_t
+_cairo_pdf_operators_fill_stroke (cairo_pdf_operators_t                *pdf_operators,
+                                 const cairo_path_fixed_t      *path,
+                                 cairo_fill_rule_t              fill_rule,
+                                 const cairo_stroke_style_t    *style,
+                                 const cairo_matrix_t          *ctm,
+                                 const cairo_matrix_t          *ctm_inverse);
+
+cairo_private cairo_int_status_t
+_cairo_pdf_operators_show_text_glyphs (cairo_pdf_operators_t     *pdf_operators,
+                                      const char                 *utf8,
+                                      int                         utf8_len,
+                                      cairo_glyph_t              *glyphs,
+                                      int                         num_glyphs,
+                                      const cairo_text_cluster_t *clusters,
+                                      int                         num_clusters,
+                                      cairo_text_cluster_flags_t  cluster_flags,
+                                      cairo_scaled_font_t        *scaled_font);
+
+#endif /* CAIRO_PDF_OPERATORS_H */
diff --git a/src/cairo-pdf-operators.c b/src/cairo-pdf-operators.c
new file mode 100644 (file)
index 0000000..fceaf1c
--- /dev/null
@@ -0,0 +1,1557 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2004 Red Hat, Inc
+ * Copyright © 2006 Red Hat, Inc
+ * Copyright © 2007, 2008 Adrian Johnson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *     Kristian Høgsberg <krh@redhat.com>
+ *     Carl Worth <cworth@cworth.org>
+ *     Adrian Johnson <ajohnson@redneon.com>
+ */
+
+#include "cairoint.h"
+
+#if CAIRO_HAS_PDF_OPERATORS
+
+#include "cairo-error-private.h"
+#include "cairo-pdf-operators-private.h"
+#include "cairo-path-fixed-private.h"
+#include "cairo-output-stream-private.h"
+#include "cairo-scaled-font-subsets-private.h"
+
+static cairo_status_t
+_cairo_pdf_operators_end_text (cairo_pdf_operators_t    *pdf_operators);
+
+
+void
+_cairo_pdf_operators_init (cairo_pdf_operators_t       *pdf_operators,
+                          cairo_output_stream_t        *stream,
+                          cairo_matrix_t               *cairo_to_pdf,
+                          cairo_scaled_font_subsets_t  *font_subsets)
+{
+    pdf_operators->stream = stream;
+    pdf_operators->cairo_to_pdf = *cairo_to_pdf;
+    pdf_operators->font_subsets = font_subsets;
+    pdf_operators->use_font_subset = NULL;
+    pdf_operators->use_font_subset_closure = NULL;
+    pdf_operators->in_text_object = FALSE;
+    pdf_operators->num_glyphs = 0;
+    pdf_operators->has_line_style = FALSE;
+    pdf_operators->use_actual_text = FALSE;
+}
+
+cairo_status_t
+_cairo_pdf_operators_fini (cairo_pdf_operators_t       *pdf_operators)
+{
+    return _cairo_pdf_operators_flush (pdf_operators);
+}
+
+void
+_cairo_pdf_operators_set_font_subsets_callback (cairo_pdf_operators_t               *pdf_operators,
+                                               cairo_pdf_operators_use_font_subset_t use_font_subset,
+                                               void                                 *closure)
+{
+    pdf_operators->use_font_subset = use_font_subset;
+    pdf_operators->use_font_subset_closure = closure;
+}
+
+/* Change the output stream to a different stream.
+ * _cairo_pdf_operators_flush() should always be called before calling
+ * this function.
+ */
+void
+_cairo_pdf_operators_set_stream (cairo_pdf_operators_t  *pdf_operators,
+                                cairo_output_stream_t   *stream)
+{
+    pdf_operators->stream = stream;
+    pdf_operators->has_line_style = FALSE;
+}
+
+void
+_cairo_pdf_operators_set_cairo_to_pdf_matrix (cairo_pdf_operators_t *pdf_operators,
+                                             cairo_matrix_t        *cairo_to_pdf)
+{
+    pdf_operators->cairo_to_pdf = *cairo_to_pdf;
+    pdf_operators->has_line_style = FALSE;
+}
+
+cairo_private void
+_cairo_pdf_operators_enable_actual_text (cairo_pdf_operators_t *pdf_operators,
+                                        cairo_bool_t           enable)
+{
+    pdf_operators->use_actual_text = enable;
+}
+
+/* Finish writing out any pending commands to the stream. This
+ * function must be called by the surface before emitting anything
+ * into the PDF stream.
+ *
+ * pdf_operators may leave the emitted PDF for some operations
+ * unfinished in case subsequent operations can be merged. This
+ * function will finish off any incomplete operation so the stream
+ * will be in a state where the surface may emit its own PDF
+ * operations (eg changing patterns).
+ *
+ */
+cairo_status_t
+_cairo_pdf_operators_flush (cairo_pdf_operators_t       *pdf_operators)
+{
+    cairo_status_t status = CAIRO_STATUS_SUCCESS;
+
+    if (pdf_operators->in_text_object)
+       status = _cairo_pdf_operators_end_text (pdf_operators);
+
+    return status;
+}
+
+/* Reset the known graphics state of the PDF consumer. ie no
+ * assumptions will be made about the state. The next time a
+ * particular graphics state is required (eg line width) the state
+ * operator is always emitted and then remembered for subsequent
+ * operatations.
+ *
+ * This should be called when starting a new stream or after emitting
+ * the 'Q' operator (where pdf-operators functions were called inside
+ * the q/Q pair).
+ */
+void
+_cairo_pdf_operators_reset (cairo_pdf_operators_t *pdf_operators)
+{
+    pdf_operators->has_line_style = FALSE;
+}
+
+/* A word wrap stream can be used as a filter to do word wrapping on
+ * top of an existing output stream. The word wrapping is quite
+ * simple, using isspace to determine characters that separate
+ * words. Any word that will cause the column count exceed the given
+ * max_column will have a '\n' character emitted before it.
+ *
+ * The stream is careful to maintain integrity for words that cross
+ * the boundary from one call to write to the next.
+ *
+ * Note: This stream does not guarantee that the output will never
+ * exceed max_column. In particular, if a single word is larger than
+ * max_column it will not be broken up.
+ */
+
+typedef enum _cairo_word_wrap_state {
+    WRAP_STATE_DELIMITER,
+    WRAP_STATE_WORD,
+    WRAP_STATE_STRING,
+    WRAP_STATE_HEXSTRING
+} cairo_word_wrap_state_t;
+
+
+typedef struct _word_wrap_stream {
+    cairo_output_stream_t base;
+    cairo_output_stream_t *output;
+    int max_column;
+    int column;
+    cairo_word_wrap_state_t state;
+    cairo_bool_t in_escape;
+    int                 escape_digits;
+} word_wrap_stream_t;
+
+
+
+/* Emit word bytes up to the next delimiter character */
+static int
+_word_wrap_stream_count_word_up_to (word_wrap_stream_t *stream,
+                                  const unsigned char *data, int length)
+{
+    const unsigned char *s = data;
+    int count = 0;
+
+    while (length--) {
+       if (_cairo_isspace (*s) || *s == '<' || *s == '(') {
+           stream->state = WRAP_STATE_DELIMITER;
+           break;
+       }
+
+       count++;
+       stream->column++;
+       s++;
+    }
+
+    if (count)
+       _cairo_output_stream_write (stream->output, data, count);
+
+    return count;
+}
+
+
+/* Emit hexstring bytes up to either the end of the ASCII hexstring or the number
+ * of columns remaining.
+ */
+static int
+_word_wrap_stream_count_hexstring_up_to (word_wrap_stream_t *stream,
+                                        const unsigned char *data, int length)
+{
+    const unsigned char *s = data;
+    int count = 0;
+    cairo_bool_t newline = FALSE;
+
+    while (length--) {
+       count++;
+       stream->column++;
+       if (*s == '>') {
+           stream->state = WRAP_STATE_DELIMITER;
+           break;
+       }
+
+       if (stream->column > stream->max_column) {
+           newline = TRUE;
+           break;
+       }
+       s++;
+    }
+
+    if (count)
+       _cairo_output_stream_write (stream->output, data, count);
+
+    if (newline) {
+       _cairo_output_stream_printf (stream->output, "\n");
+       stream->column = 0;
+    }
+
+    return count;
+}
+
+/* Count up to either the end of the string or the number of columns
+ * remaining.
+ */
+static int
+_word_wrap_stream_count_string_up_to (word_wrap_stream_t *stream,
+                                     const unsigned char *data, int length)
+{
+    const unsigned char *s = data;
+    int count = 0;
+    cairo_bool_t newline = FALSE;
+
+    while (length--) {
+       count++;
+       stream->column++;
+       if (!stream->in_escape) {
+           if (*s == ')') {
+               stream->state = WRAP_STATE_DELIMITER;
+               break;
+           }
+           if (*s == '\\') {
+               stream->in_escape = TRUE;
+               stream->escape_digits = 0;
+           } else if (stream->column > stream->max_column) {
+               newline = TRUE;
+               break;
+           }
+       } else {
+           if (!_cairo_isdigit(*s) || ++stream->escape_digits == 3)
+               stream->in_escape = FALSE;
+       }
+       s++;
+    }
+
+    if (count)
+       _cairo_output_stream_write (stream->output, data, count);
+
+    if (newline) {
+       _cairo_output_stream_printf (stream->output, "\\\n");
+       stream->column = 0;
+    }
+
+    return count;
+}
+
+static cairo_status_t
+_word_wrap_stream_write (cairo_output_stream_t  *base,
+                        const unsigned char    *data,
+                        unsigned int            length)
+{
+    word_wrap_stream_t *stream = (word_wrap_stream_t *) base;
+    int count;
+
+    while (length) {
+       switch (stream->state) {
+       case WRAP_STATE_WORD:
+           count = _word_wrap_stream_count_word_up_to (stream, data, length);
+           break;
+       case WRAP_STATE_HEXSTRING:
+           count = _word_wrap_stream_count_hexstring_up_to (stream, data, length);
+           break;
+       case WRAP_STATE_STRING:
+           count = _word_wrap_stream_count_string_up_to (stream, data, length);
+           break;
+       case WRAP_STATE_DELIMITER:
+           count = 1;
+           stream->column++;
+           if (*data == '\n' || stream->column >= stream->max_column) {
+               _cairo_output_stream_printf (stream->output, "\n");
+               stream->column = 0;
+           } else if (*data == '<') {
+               stream->state = WRAP_STATE_HEXSTRING;
+           } else if (*data == '(') {
+               stream->state = WRAP_STATE_STRING;
+           } else if (!_cairo_isspace (*data)) {
+               stream->state = WRAP_STATE_WORD;
+           }
+           if (*data != '\n')
+               _cairo_output_stream_write (stream->output, data, 1);
+           break;
+
+       default:
+           ASSERT_NOT_REACHED;
+           count = length;
+           break;
+       }
+       data += count;
+       length -= count;
+    }
+
+    return _cairo_output_stream_get_status (stream->output);
+}
+
+static cairo_status_t
+_word_wrap_stream_close (cairo_output_stream_t *base)
+{
+    word_wrap_stream_t *stream = (word_wrap_stream_t *) base;
+
+    return _cairo_output_stream_get_status (stream->output);
+}
+
+static cairo_output_stream_t *
+_word_wrap_stream_create (cairo_output_stream_t *output, int max_column)
+{
+    word_wrap_stream_t *stream;
+
+    if (output->status)
+       return _cairo_output_stream_create_in_error (output->status);
+
+    stream = malloc (sizeof (word_wrap_stream_t));
+    if (unlikely (stream == NULL)) {
+       _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+       return (cairo_output_stream_t *) &_cairo_output_stream_nil;
+    }
+
+    _cairo_output_stream_init (&stream->base,
+                              _word_wrap_stream_write,
+                              NULL,
+                              _word_wrap_stream_close);
+    stream->output = output;
+    stream->max_column = max_column;
+    stream->column = 0;
+    stream->state = WRAP_STATE_DELIMITER;
+    stream->in_escape = FALSE;
+    stream->escape_digits = 0;
+
+    return &stream->base;
+}
+
+typedef struct _pdf_path_info {
+    cairo_output_stream_t   *output;
+    cairo_matrix_t         *path_transform;
+    cairo_line_cap_t         line_cap;
+    cairo_point_t            last_move_to_point;
+    cairo_bool_t             has_sub_path;
+} pdf_path_info_t;
+
+static cairo_status_t
+_cairo_pdf_path_move_to (void *closure,
+                        const cairo_point_t *point)
+{
+    pdf_path_info_t *info = closure;
+    double x = _cairo_fixed_to_double (point->x);
+    double y = _cairo_fixed_to_double (point->y);
+
+    info->last_move_to_point = *point;
+    info->has_sub_path = FALSE;
+    cairo_matrix_transform_point (info->path_transform, &x, &y);
+    _cairo_output_stream_printf (info->output,
+                                "%g %g m ", x, y);
+
+    return _cairo_output_stream_get_status (info->output);
+}
+
+static cairo_status_t
+_cairo_pdf_path_line_to (void *closure,
+                        const cairo_point_t *point)
+{
+    pdf_path_info_t *info = closure;
+    double x = _cairo_fixed_to_double (point->x);
+    double y = _cairo_fixed_to_double (point->y);
+
+    if (info->line_cap != CAIRO_LINE_CAP_ROUND &&
+       ! info->has_sub_path &&
+       point->x == info->last_move_to_point.x &&
+       point->y == info->last_move_to_point.y)
+    {
+       return CAIRO_STATUS_SUCCESS;
+    }
+
+    info->has_sub_path = TRUE;
+    cairo_matrix_transform_point (info->path_transform, &x, &y);
+    _cairo_output_stream_printf (info->output,
+                                "%g %g l ", x, y);
+
+    return _cairo_output_stream_get_status (info->output);
+}
+
+static cairo_status_t
+_cairo_pdf_path_curve_to (void          *closure,
+                         const cairo_point_t *b,
+                         const cairo_point_t *c,
+                         const cairo_point_t *d)
+{
+    pdf_path_info_t *info = closure;
+    double bx = _cairo_fixed_to_double (b->x);
+    double by = _cairo_fixed_to_double (b->y);
+    double cx = _cairo_fixed_to_double (c->x);
+    double cy = _cairo_fixed_to_double (c->y);
+    double dx = _cairo_fixed_to_double (d->x);
+    double dy = _cairo_fixed_to_double (d->y);
+
+    info->has_sub_path = TRUE;
+    cairo_matrix_transform_point (info->path_transform, &bx, &by);
+    cairo_matrix_transform_point (info->path_transform, &cx, &cy);
+    cairo_matrix_transform_point (info->path_transform, &dx, &dy);
+    _cairo_output_stream_printf (info->output,
+                                "%g %g %g %g %g %g c ",
+                                bx, by, cx, cy, dx, dy);
+    return _cairo_output_stream_get_status (info->output);
+}
+
+static cairo_status_t
+_cairo_pdf_path_close_path (void *closure)
+{
+    pdf_path_info_t *info = closure;
+
+    if (info->line_cap != CAIRO_LINE_CAP_ROUND &&
+       ! info->has_sub_path)
+    {
+       return CAIRO_STATUS_SUCCESS;
+    }
+
+    _cairo_output_stream_printf (info->output,
+                                "h\n");
+
+    return _cairo_output_stream_get_status (info->output);
+}
+
+static cairo_status_t
+_cairo_pdf_path_rectangle (pdf_path_info_t *info, cairo_box_t *box)
+{
+    double x1 = _cairo_fixed_to_double (box->p1.x);
+    double y1 = _cairo_fixed_to_double (box->p1.y);
+    double x2 = _cairo_fixed_to_double (box->p2.x);
+    double y2 = _cairo_fixed_to_double (box->p2.y);
+
+    cairo_matrix_transform_point (info->path_transform, &x1, &y1);
+    cairo_matrix_transform_point (info->path_transform, &x2, &y2);
+    _cairo_output_stream_printf (info->output,
+                                "%g %g %g %g re ",
+                                x1, y1, x2 - x1, y2 - y1);
+
+    return _cairo_output_stream_get_status (info->output);
+}
+
+/* The line cap value is needed to workaround the fact that PostScript
+ * and PDF semantics for stroking degenerate sub-paths do not match
+ * cairo semantics. (PostScript draws something for any line cap
+ * value, while cairo draws something only for round caps).
+ *
+ * When using this function to emit a path to be filled, rather than
+ * stroked, simply pass %CAIRO_LINE_CAP_ROUND which will guarantee that
+ * the stroke workaround will not modify the path being emitted.
+ */
+static cairo_status_t
+_cairo_pdf_operators_emit_path (cairo_pdf_operators_t  *pdf_operators,
+                               const cairo_path_fixed_t*path,
+                               cairo_matrix_t          *path_transform,
+                               cairo_line_cap_t         line_cap)
+{
+    cairo_output_stream_t *word_wrap;
+    cairo_status_t status, status2;
+    pdf_path_info_t info;
+    cairo_box_t box;
+
+    word_wrap = _word_wrap_stream_create (pdf_operators->stream, 72);
+    status = _cairo_output_stream_get_status (word_wrap);
+    if (unlikely (status))
+       return _cairo_output_stream_destroy (word_wrap);
+
+    info.output = word_wrap;
+    info.path_transform = path_transform;
+    info.line_cap = line_cap;
+    if (_cairo_path_fixed_is_rectangle (path, &box)) {
+       status = _cairo_pdf_path_rectangle (&info, &box);
+    } else {
+       status = _cairo_path_fixed_interpret (path,
+                                             _cairo_pdf_path_move_to,
+                                             _cairo_pdf_path_line_to,
+                                             _cairo_pdf_path_curve_to,
+                                             _cairo_pdf_path_close_path,
+                                             &info);
+    }
+
+    status2 = _cairo_output_stream_destroy (word_wrap);
+    if (status == CAIRO_STATUS_SUCCESS)
+       status = status2;
+
+    return status;
+}
+
+cairo_int_status_t
+_cairo_pdf_operators_clip (cairo_pdf_operators_t       *pdf_operators,
+                          const cairo_path_fixed_t     *path,
+                          cairo_fill_rule_t             fill_rule)
+{
+    const char *pdf_operator;
+    cairo_status_t status;
+
+    if (pdf_operators->in_text_object) {
+       status = _cairo_pdf_operators_end_text (pdf_operators);
+       if (unlikely (status))
+           return status;
+    }
+
+    if (! path->has_current_point) {
+       /* construct an empty path */
+       _cairo_output_stream_printf (pdf_operators->stream, "0 0 m ");
+    } else {
+       status = _cairo_pdf_operators_emit_path (pdf_operators,
+                                                path,
+                                                &pdf_operators->cairo_to_pdf,
+                                                CAIRO_LINE_CAP_ROUND);
+       if (unlikely (status))
+           return status;
+    }
+
+    switch (fill_rule) {
+    default:
+       ASSERT_NOT_REACHED;
+    case CAIRO_FILL_RULE_WINDING:
+       pdf_operator = "W";
+       break;
+    case CAIRO_FILL_RULE_EVEN_ODD:
+       pdf_operator = "W*";
+       break;
+    }
+
+    _cairo_output_stream_printf (pdf_operators->stream,
+                                "%s n\n",
+                                pdf_operator);
+
+    return _cairo_output_stream_get_status (pdf_operators->stream);
+}
+
+static int
+_cairo_pdf_line_cap (cairo_line_cap_t cap)
+{
+    switch (cap) {
+    case CAIRO_LINE_CAP_BUTT:
+       return 0;
+    case CAIRO_LINE_CAP_ROUND:
+       return 1;
+    case CAIRO_LINE_CAP_SQUARE:
+       return 2;
+    default:
+       ASSERT_NOT_REACHED;
+       return 0;
+    }
+}
+
+static int
+_cairo_pdf_line_join (cairo_line_join_t join)
+{
+    switch (join) {
+    case CAIRO_LINE_JOIN_MITER:
+       return 0;
+    case CAIRO_LINE_JOIN_ROUND:
+       return 1;
+    case CAIRO_LINE_JOIN_BEVEL:
+       return 2;
+    default:
+       ASSERT_NOT_REACHED;
+       return 0;
+    }
+}
+
+cairo_int_status_t
+_cairo_pdf_operators_emit_stroke_style (cairo_pdf_operators_t          *pdf_operators,
+                                       const cairo_stroke_style_t      *style,
+                                       double                           scale)
+{
+    double *dash = style->dash;
+    int num_dashes = style->num_dashes;
+    double dash_offset = style->dash_offset;
+    double line_width = style->line_width * scale;
+
+    /* PostScript has "special needs" when it comes to zero-length
+     * dash segments with butt caps. It apparently (at least
+     * according to ghostscript) draws hairlines for this
+     * case. That's not what the cairo semantics want, so we first
+     * touch up the array to eliminate any 0.0 values that will
+     * result in "on" segments.
+     */
+    if (num_dashes && style->line_cap == CAIRO_LINE_CAP_BUTT) {
+       int i;
+
+       /* If there's an odd number of dash values they will each get
+        * interpreted as both on and off. So we first explicitly
+        * expand the array to remove the duplicate usage so that we
+        * can modify some of the values.
+        */
+       if (num_dashes % 2) {
+           dash = _cairo_malloc_abc (num_dashes, 2, sizeof (double));
+           if (unlikely (dash == NULL))
+               return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+           memcpy (dash, style->dash, num_dashes * sizeof (double));
+           memcpy (dash + num_dashes, style->dash, num_dashes * sizeof (double));
+
+           num_dashes *= 2;
+       }
+
+       for (i = 0; i < num_dashes; i += 2) {
+           if (dash[i] == 0.0) {
+               /* Do not modify the dashes in-place, as we may need to also
+                * replay this stroke to an image fallback.
+                */
+               if (dash == style->dash) {
+                   dash = _cairo_malloc_ab (num_dashes, sizeof (double));
+                   if (unlikely (dash == NULL))
+                       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+                   memcpy (dash, style->dash, num_dashes * sizeof (double));
+               }
+
+               /* If we're at the front of the list, we first rotate
+                * two elements from the end of the list to the front
+                * of the list before folding away the 0.0. Or, if
+                * there are only two dash elements, then there is
+                * nothing at all to draw.
+                */
+               if (i == 0) {
+                   double last_two[2];
+
+                   if (num_dashes == 2) {
+                       free (dash);
+                       return CAIRO_INT_STATUS_NOTHING_TO_DO;
+                   }
+
+                   /* The cases of num_dashes == 0, 1, or 3 elements
+                    * cannot exist, so the rotation of 2 elements
+                    * will always be safe */
+                   memcpy (last_two, dash + num_dashes - 2, sizeof (last_two));
+                   memmove (dash + 2, dash, (num_dashes - 2) * sizeof (double));
+                   memcpy (dash, last_two, sizeof (last_two));
+                   dash_offset += dash[0] + dash[1];
+                   i = 2;
+               }
+               dash[i-1] += dash[i+1];
+               num_dashes -= 2;
+               memmove (dash + i, dash + i + 2, (num_dashes - i) * sizeof (double));
+               /* If we might have just rotated, it's possible that
+                * we rotated a 0.0 value to the front of the list.
+                * Set i to -2 so it will get incremented to 0. */
+               if (i == 2)
+                   i = -2;
+           }
+       }
+    }
+
+    if (!pdf_operators->has_line_style || pdf_operators->line_width != line_width) {
+       _cairo_output_stream_printf (pdf_operators->stream,
+                                    "%f w\n",
+                                    line_width);
+       pdf_operators->line_width = line_width;
+    }
+
+    if (!pdf_operators->has_line_style || pdf_operators->line_cap != style->line_cap) {
+       _cairo_output_stream_printf (pdf_operators->stream,
+                                    "%d J\n",
+                                    _cairo_pdf_line_cap (style->line_cap));
+       pdf_operators->line_cap = style->line_cap;
+    }
+
+    if (!pdf_operators->has_line_style || pdf_operators->line_join != style->line_join) {
+       _cairo_output_stream_printf (pdf_operators->stream,
+                                    "%d j\n",
+                                    _cairo_pdf_line_join (style->line_join));
+       pdf_operators->line_join = style->line_join;
+    }
+
+    if (num_dashes) {
+       int d;
+
+       _cairo_output_stream_printf (pdf_operators->stream, "[");
+       for (d = 0; d < num_dashes; d++)
+           _cairo_output_stream_printf (pdf_operators->stream, " %f", dash[d] * scale);
+       _cairo_output_stream_printf (pdf_operators->stream, "] %f d\n",
+                                    dash_offset * scale);
+       pdf_operators->has_dashes = TRUE;
+    } else if (!pdf_operators->has_line_style || pdf_operators->has_dashes) {
+       _cairo_output_stream_printf (pdf_operators->stream, "[] 0.0 d\n");
+       pdf_operators->has_dashes = FALSE;
+    }
+    if (dash != style->dash)
+        free (dash);
+
+    if (!pdf_operators->has_line_style || pdf_operators->miter_limit != style->miter_limit) {
+       _cairo_output_stream_printf (pdf_operators->stream,
+                                    "%f M ",
+                                    style->miter_limit < 1.0 ? 1.0 : style->miter_limit);
+       pdf_operators->miter_limit = style->miter_limit;
+    }
+    pdf_operators->has_line_style = TRUE;
+
+    return _cairo_output_stream_get_status (pdf_operators->stream);
+}
+
+/* Scale the matrix so the largest absolute value of the non
+ * translation components is 1.0. Return the scale required to restore
+ * the matrix to the original values.
+ *
+ * eg the matrix  [ 100  0  0  50   20   10  ]
+ *
+ * is rescaled to [  1   0  0  0.5  0.2  0.1 ]
+ * and the scale returned is 100
+ */
+static void
+_cairo_matrix_factor_out_scale (cairo_matrix_t *m, double *scale)
+{
+    double s;
+
+    s = fabs (m->xx);
+    if (fabs (m->xy) > s)
+       s = fabs (m->xy);
+    if (fabs (m->yx) > s)
+       s = fabs (m->yx);
+    if (fabs (m->yy) > s)
+       s = fabs (m->yy);
+    *scale = s;
+    s = 1.0/s;
+    cairo_matrix_scale (m, s, s);
+}
+
+static cairo_int_status_t
+_cairo_pdf_operators_emit_stroke (cairo_pdf_operators_t                *pdf_operators,
+                                 const cairo_path_fixed_t      *path,
+                                 const cairo_stroke_style_t    *style,
+                                 const cairo_matrix_t          *ctm,
+                                 const cairo_matrix_t          *ctm_inverse,
+                                 const char                    *pdf_operator)
+{
+    cairo_int_status_t status;
+    cairo_matrix_t m, path_transform;
+    cairo_bool_t has_ctm = TRUE;
+    double scale = 1.0;
+
+    if (pdf_operators->in_text_object) {
+       status = _cairo_pdf_operators_end_text (pdf_operators);
+       if (unlikely (status))
+           return status;
+    }
+
+    /* Optimize away the stroke ctm when it does not affect the
+     * stroke. There are other ctm cases that could be optimized
+     * however this is the most common.
+     */
+    if (fabs(ctm->xx) == 1.0 && fabs(ctm->yy) == 1.0 &&
+       fabs(ctm->xy) == 0.0 && fabs(ctm->yx) == 0.0)
+    {
+       has_ctm = FALSE;
+    }
+
+    /* The PDF CTM is transformed to the user space CTM when stroking
+     * so the corect pen shape will be used. This also requires that
+     * the path be transformed to user space when emitted. The
+     * conversion of path coordinates to user space may cause rounding
+     * errors. For example the device space point (1.234, 3.142) when
+     * transformed to a user space CTM of [100 0 0 100 0 0] will be
+     * emitted as (0.012, 0.031).
+     *
+     * To avoid the rounding problem we scale the user space CTM
+     * matrix so that all the non translation components of the matrix
+     * are <= 1. The line width and and dashes are scaled by the
+     * inverse of the scale applied to the CTM. This maintains the
+     * shape of the stroke pen while keeping the user space CTM within
+     * the range that maximizes the precision of the emitted path.
+     */
+    if (has_ctm) {
+       m = *ctm;
+       /* Zero out the translation since it does not affect the pen
+        * shape however it may cause unnecessary digits to be emitted.
+        */
+       m.x0 = 0.0;
+       m.y0 = 0.0;
+       _cairo_matrix_factor_out_scale (&m, &scale);
+       path_transform = m;
+       status = cairo_matrix_invert (&path_transform);
+       if (unlikely (status))
+           return status;
+
+       cairo_matrix_multiply (&m, &m, &pdf_operators->cairo_to_pdf);
+    }
+
+    status = _cairo_pdf_operators_emit_stroke_style (pdf_operators, style, scale);
+    if (status == CAIRO_INT_STATUS_NOTHING_TO_DO)
+       return CAIRO_STATUS_SUCCESS;
+    if (unlikely (status))
+       return status;
+
+    if (has_ctm) {
+       _cairo_output_stream_printf (pdf_operators->stream,
+                                    "q %f %f %f %f %f %f cm\n",
+                                    m.xx, m.yx, m.xy, m.yy,
+                                    m.x0, m.y0);
+    } else {
+       path_transform = pdf_operators->cairo_to_pdf;
+    }
+
+    status = _cairo_pdf_operators_emit_path (pdf_operators,
+                                            path,
+                                            &path_transform,
+                                            style->line_cap);
+    if (unlikely (status))
+       return status;
+
+    _cairo_output_stream_printf (pdf_operators->stream, "%s", pdf_operator);
+    if (has_ctm)
+       _cairo_output_stream_printf (pdf_operators->stream, " Q");
+
+    _cairo_output_stream_printf (pdf_operators->stream, "\n");
+
+    return _cairo_output_stream_get_status (pdf_operators->stream);
+}
+
+cairo_int_status_t
+_cairo_pdf_operators_stroke (cairo_pdf_operators_t             *pdf_operators,
+                            const cairo_path_fixed_t           *path,
+                            const cairo_stroke_style_t         *style,
+                            const cairo_matrix_t               *ctm,
+                            const cairo_matrix_t               *ctm_inverse)
+{
+    return _cairo_pdf_operators_emit_stroke (pdf_operators,
+                                            path,
+                                            style,
+                                            ctm,
+                                            ctm_inverse,
+                                            "S");
+}
+
+cairo_int_status_t
+_cairo_pdf_operators_fill (cairo_pdf_operators_t       *pdf_operators,
+                          const cairo_path_fixed_t     *path,
+                          cairo_fill_rule_t            fill_rule)
+{
+    const char *pdf_operator;
+    cairo_status_t status;
+
+    if (pdf_operators->in_text_object) {
+       status = _cairo_pdf_operators_end_text (pdf_operators);
+       if (unlikely (status))
+           return status;
+    }
+
+    status = _cairo_pdf_operators_emit_path (pdf_operators,
+                                            path,
+                                            &pdf_operators->cairo_to_pdf,
+                                            CAIRO_LINE_CAP_ROUND);
+    if (unlikely (status))
+       return status;
+
+    switch (fill_rule) {
+    default:
+       ASSERT_NOT_REACHED;
+    case CAIRO_FILL_RULE_WINDING:
+       pdf_operator = "f";
+       break;
+    case CAIRO_FILL_RULE_EVEN_ODD:
+       pdf_operator = "f*";
+       break;
+    }
+
+    _cairo_output_stream_printf (pdf_operators->stream,
+                                "%s\n",
+                                pdf_operator);
+
+    return _cairo_output_stream_get_status (pdf_operators->stream);
+}
+
+cairo_int_status_t
+_cairo_pdf_operators_fill_stroke (cairo_pdf_operators_t                *pdf_operators,
+                                 const cairo_path_fixed_t      *path,
+                                 cairo_fill_rule_t              fill_rule,
+                                 const cairo_stroke_style_t    *style,
+                                 const cairo_matrix_t          *ctm,
+                                 const cairo_matrix_t          *ctm_inverse)
+{
+    const char *operator;
+
+    switch (fill_rule) {
+    default:
+       ASSERT_NOT_REACHED;
+    case CAIRO_FILL_RULE_WINDING:
+       operator = "B";
+       break;
+    case CAIRO_FILL_RULE_EVEN_ODD:
+       operator = "B*";
+       break;
+    }
+
+    return _cairo_pdf_operators_emit_stroke (pdf_operators,
+                                            path,
+                                            style,
+                                            ctm,
+                                            ctm_inverse,
+                                            operator);
+}
+
+static void
+_cairo_pdf_operators_emit_glyph_index (cairo_pdf_operators_t *pdf_operators,
+                                      cairo_output_stream_t *stream,
+                                      unsigned int           glyph)
+{
+    if (pdf_operators->is_latin) {
+       if (glyph == '(' || glyph == ')' || glyph == '\\')
+           _cairo_output_stream_printf (stream, "\\%c", glyph);
+       else if (glyph >= 0x20 && glyph <= 0x7e)
+           _cairo_output_stream_printf (stream, "%c", glyph);
+       else
+           _cairo_output_stream_printf (stream, "\\%03o", glyph);
+    } else {
+       _cairo_output_stream_printf (stream,
+                                    "%0*x",
+                                    pdf_operators->hex_width,
+                                    glyph);
+    }
+}
+
+#define GLYPH_POSITION_TOLERANCE 0.001
+
+/* Emit the string of glyphs using the 'Tj' operator. This requires
+ * that the glyphs are positioned at their natural glyph advances. */
+static cairo_status_t
+_cairo_pdf_operators_emit_glyph_string (cairo_pdf_operators_t   *pdf_operators,
+                                       cairo_output_stream_t   *stream)
+{
+    int i;
+
+    _cairo_output_stream_printf (stream, "%s", pdf_operators->is_latin ? "(" : "<");
+    for (i = 0; i < pdf_operators->num_glyphs; i++) {
+       _cairo_pdf_operators_emit_glyph_index (pdf_operators,
+                                              stream,
+                                              pdf_operators->glyphs[i].glyph_index);
+       pdf_operators->cur_x += pdf_operators->glyphs[i].x_advance;
+    }
+    _cairo_output_stream_printf (stream, "%sTj\n", pdf_operators->is_latin ? ")" : ">");
+
+    return _cairo_output_stream_get_status (stream);
+}
+
+/* Emit the string of glyphs using the 'TJ' operator.
+ *
+ * The TJ operator takes an array of strings of glyphs. Each string of
+ * glyphs is displayed using the glyph advances of each glyph to
+ * position the glyphs. A relative adjustment to the glyph advance may
+ * be specified by including the adjustment between two strings. The
+ * adjustment is in units of text space * -1000.
+ */
+static cairo_status_t
+_cairo_pdf_operators_emit_glyph_string_with_positioning (
+    cairo_pdf_operators_t   *pdf_operators,
+    cairo_output_stream_t   *stream)
+{
+    int i;
+
+    _cairo_output_stream_printf (stream, "[%s", pdf_operators->is_latin ? "(" : "<");
+    for (i = 0; i < pdf_operators->num_glyphs; i++) {
+       if (pdf_operators->glyphs[i].x_position != pdf_operators->cur_x)
+       {
+           double delta = pdf_operators->glyphs[i].x_position - pdf_operators->cur_x;
+           int rounded_delta;
+
+           delta = -1000.0*delta;
+           /* As the delta is in 1/1000 of a unit of text space,
+            * rounding to an integer should still provide sufficient
+            * precision. We round the delta before adding to Tm_x so
+            * that we keep track of the accumulated rounding error in
+            * the PDF interpreter and compensate for it when
+            * calculating subsequent deltas.
+            */
+           rounded_delta = _cairo_lround (delta);
+           if (abs(rounded_delta) < 3)
+               rounded_delta = 0;
+           if (rounded_delta != 0) {
+               if (pdf_operators->is_latin) {
+                   _cairo_output_stream_printf (stream,
+                                                ")%d(",
+                                                rounded_delta);
+               } else {
+                   _cairo_output_stream_printf (stream,
+                                                ">%d<",
+                                                rounded_delta);
+               }
+           }
+
+           /* Convert the rounded delta back to text
+            * space before adding to the current text
+            * position. */
+           delta = rounded_delta/-1000.0;
+           pdf_operators->cur_x += delta;
+       }
+
+       _cairo_pdf_operators_emit_glyph_index (pdf_operators,
+                                              stream,
+                                              pdf_operators->glyphs[i].glyph_index);
+       pdf_operators->cur_x += pdf_operators->glyphs[i].x_advance;
+    }
+    _cairo_output_stream_printf (stream, "%s]TJ\n", pdf_operators->is_latin ? ")" : ">");
+
+    return _cairo_output_stream_get_status (stream);
+}
+
+static cairo_status_t
+_cairo_pdf_operators_flush_glyphs (cairo_pdf_operators_t    *pdf_operators)
+{
+    cairo_output_stream_t *word_wrap_stream;
+    cairo_status_t status, status2;
+    int i;
+    double x;
+
+    if (pdf_operators->num_glyphs == 0)
+       return CAIRO_STATUS_SUCCESS;
+
+    word_wrap_stream = _word_wrap_stream_create (pdf_operators->stream, 72);
+    status = _cairo_output_stream_get_status (word_wrap_stream);
+    if (unlikely (status))
+       return _cairo_output_stream_destroy (word_wrap_stream);
+
+    /* Check if glyph advance used to position every glyph */
+    x = pdf_operators->cur_x;
+    for (i = 0; i < pdf_operators->num_glyphs; i++) {
+       if (fabs(pdf_operators->glyphs[i].x_position - x) > GLYPH_POSITION_TOLERANCE)
+           break;
+       x += pdf_operators->glyphs[i].x_advance;
+    }
+    if (i == pdf_operators->num_glyphs) {
+       status = _cairo_pdf_operators_emit_glyph_string (pdf_operators,
+                                                        word_wrap_stream);
+    } else {
+       status = _cairo_pdf_operators_emit_glyph_string_with_positioning (
+           pdf_operators, word_wrap_stream);
+    }
+
+    pdf_operators->num_glyphs = 0;
+    pdf_operators->glyph_buf_x_pos = pdf_operators->cur_x;
+    status2 = _cairo_output_stream_destroy (word_wrap_stream);
+    if (status == CAIRO_STATUS_SUCCESS)
+       status = status2;
+
+    return status;
+}
+
+static cairo_status_t
+_cairo_pdf_operators_add_glyph (cairo_pdf_operators_t             *pdf_operators,
+                               cairo_scaled_font_subsets_glyph_t *glyph,
+                               double                             x_position)
+{
+    double x, y;
+
+    x = glyph->x_advance;
+    y = glyph->y_advance;
+    if (glyph->is_scaled)
+       cairo_matrix_transform_distance (&pdf_operators->font_matrix_inverse, &x, &y);
+
+    pdf_operators->glyphs[pdf_operators->num_glyphs].x_position = x_position;
+    pdf_operators->glyphs[pdf_operators->num_glyphs].glyph_index = glyph->subset_glyph_index;
+    pdf_operators->glyphs[pdf_operators->num_glyphs].x_advance = x;
+    pdf_operators->glyph_buf_x_pos += x;
+    pdf_operators->num_glyphs++;
+    if (pdf_operators->num_glyphs == PDF_GLYPH_BUFFER_SIZE)
+       return _cairo_pdf_operators_flush_glyphs (pdf_operators);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+/* Use 'Tm' operator to set the PDF text matrix. */
+static cairo_status_t
+_cairo_pdf_operators_set_text_matrix (cairo_pdf_operators_t  *pdf_operators,
+                                     cairo_matrix_t         *matrix)
+{
+    cairo_matrix_t inverse;
+    cairo_status_t status;
+
+    /* We require the matrix to be invertable. */
+    inverse = *matrix;
+    status = cairo_matrix_invert (&inverse);
+    if (unlikely (status))
+       return status;
+
+    pdf_operators->text_matrix = *matrix;
+    pdf_operators->cur_x = 0;
+    pdf_operators->cur_y = 0;
+    pdf_operators->glyph_buf_x_pos = 0;
+    _cairo_output_stream_printf (pdf_operators->stream,
+                                "%f %f %f %f %f %f Tm\n",
+                                pdf_operators->text_matrix.xx,
+                                pdf_operators->text_matrix.yx,
+                                pdf_operators->text_matrix.xy,
+                                pdf_operators->text_matrix.yy,
+                                pdf_operators->text_matrix.x0,
+                                pdf_operators->text_matrix.y0);
+
+    pdf_operators->cairo_to_pdftext = *matrix;
+    status = cairo_matrix_invert (&pdf_operators->cairo_to_pdftext);
+    assert (status == CAIRO_STATUS_SUCCESS);
+    cairo_matrix_multiply (&pdf_operators->cairo_to_pdftext,
+                          &pdf_operators->cairo_to_pdf,
+                          &pdf_operators->cairo_to_pdftext);
+
+    return _cairo_output_stream_get_status (pdf_operators->stream);
+}
+
+#define TEXT_MATRIX_TOLERANCE 1e-6
+
+/* Set the translation components of the PDF text matrix to x, y. The
+ * 'Td' operator is used to transform the text matrix.
+ */
+static cairo_status_t
+_cairo_pdf_operators_set_text_position (cairo_pdf_operators_t  *pdf_operators,
+                                       double                  x,
+                                       double                  y)
+{
+    cairo_matrix_t translate, inverse;
+    cairo_status_t status;
+
+    /* The Td operator transforms the text_matrix with:
+     *
+     *   text_matrix' = T x text_matrix
+     *
+     * where T is a translation matrix with the translation components
+     * set to the Td operands tx and ty.
+     */
+    inverse = pdf_operators->text_matrix;
+    status = cairo_matrix_invert (&inverse);
+    assert (status == CAIRO_STATUS_SUCCESS);
+    pdf_operators->text_matrix.x0 = x;
+    pdf_operators->text_matrix.y0 = y;
+    cairo_matrix_multiply (&translate, &pdf_operators->text_matrix, &inverse);
+    if (fabs(translate.x0) < TEXT_MATRIX_TOLERANCE)
+       translate.x0 = 0.0;
+    if (fabs(translate.y0) < TEXT_MATRIX_TOLERANCE)
+       translate.y0 = 0.0;
+    _cairo_output_stream_printf (pdf_operators->stream,
+                                "%f %f Td\n",
+                                translate.x0,
+                                translate.y0);
+    pdf_operators->cur_x = 0;
+    pdf_operators->cur_y = 0;
+    pdf_operators->glyph_buf_x_pos = 0;
+
+    pdf_operators->cairo_to_pdftext = pdf_operators->text_matrix;
+    status = cairo_matrix_invert (&pdf_operators->cairo_to_pdftext);
+    assert (status == CAIRO_STATUS_SUCCESS);
+    cairo_matrix_multiply (&pdf_operators->cairo_to_pdftext,
+                          &pdf_operators->cairo_to_pdf,
+                          &pdf_operators->cairo_to_pdftext);
+
+    return _cairo_output_stream_get_status (pdf_operators->stream);
+}
+
+/* Select the font using the 'Tf' operator. The font size is set to 1
+ * as we use the 'Tm' operator to set the font scale.
+ */
+static cairo_status_t
+_cairo_pdf_operators_set_font_subset (cairo_pdf_operators_t             *pdf_operators,
+                                     cairo_scaled_font_subsets_glyph_t *subset_glyph)
+{
+    cairo_status_t status;
+
+    _cairo_output_stream_printf (pdf_operators->stream,
+                                "/f-%d-%d 1 Tf\n",
+                                subset_glyph->font_id,
+                                subset_glyph->subset_id);
+    if (pdf_operators->use_font_subset) {
+       status = pdf_operators->use_font_subset (subset_glyph->font_id,
+                                                subset_glyph->subset_id,
+                                                pdf_operators->use_font_subset_closure);
+       if (unlikely (status))
+           return status;
+    }
+    pdf_operators->font_id = subset_glyph->font_id;
+    pdf_operators->subset_id = subset_glyph->subset_id;
+    pdf_operators->is_latin = subset_glyph->is_latin;
+
+    if (subset_glyph->is_composite)
+       pdf_operators->hex_width = 4;
+    else
+       pdf_operators->hex_width = 2;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_pdf_operators_begin_text (cairo_pdf_operators_t    *pdf_operators)
+{
+    _cairo_output_stream_printf (pdf_operators->stream, "BT\n");
+
+    pdf_operators->in_text_object = TRUE;
+    pdf_operators->num_glyphs = 0;
+    pdf_operators->glyph_buf_x_pos = 0;
+
+    return _cairo_output_stream_get_status (pdf_operators->stream);
+}
+
+static cairo_status_t
+_cairo_pdf_operators_end_text (cairo_pdf_operators_t    *pdf_operators)
+{
+    cairo_status_t status;
+
+    status = _cairo_pdf_operators_flush_glyphs (pdf_operators);
+    if (unlikely (status))
+       return status;
+
+    _cairo_output_stream_printf (pdf_operators->stream, "ET\n");
+
+    pdf_operators->in_text_object = FALSE;
+
+    return _cairo_output_stream_get_status (pdf_operators->stream);
+}
+
+/* Compare the scale components of two matrices. The translation
+ * components are ignored. */
+static cairo_bool_t
+_cairo_matrix_scale_equal (cairo_matrix_t *a, cairo_matrix_t *b)
+{
+    return (a->xx == b->xx &&
+           a->xy == b->xy &&
+           a->yx == b->yx &&
+           a->yy == b->yy);
+}
+
+static cairo_status_t
+_cairo_pdf_operators_begin_actualtext (cairo_pdf_operators_t *pdf_operators,
+                                      const char            *utf8,
+                                      int                    utf8_len)
+{
+    uint16_t *utf16;
+    int utf16_len;
+    cairo_status_t status;
+    int i;
+
+    _cairo_output_stream_printf (pdf_operators->stream, "/Span << /ActualText <feff");
+    if (utf8_len) {
+       status = _cairo_utf8_to_utf16 (utf8, utf8_len, &utf16, &utf16_len);
+       if (unlikely (status))
+           return status;
+
+       for (i = 0; i < utf16_len; i++) {
+           _cairo_output_stream_printf (pdf_operators->stream,
+                                        "%04x", (int) (utf16[i]));
+       }
+       free (utf16);
+    }
+    _cairo_output_stream_printf (pdf_operators->stream, "> >> BDC\n");
+
+    return _cairo_output_stream_get_status (pdf_operators->stream);
+}
+
+static cairo_status_t
+_cairo_pdf_operators_end_actualtext (cairo_pdf_operators_t    *pdf_operators)
+{
+    _cairo_output_stream_printf (pdf_operators->stream, "EMC\n");
+
+    return _cairo_output_stream_get_status (pdf_operators->stream);
+}
+
+static cairo_status_t
+_cairo_pdf_operators_emit_glyph (cairo_pdf_operators_t             *pdf_operators,
+                                cairo_glyph_t                     *glyph,
+                                cairo_scaled_font_subsets_glyph_t *subset_glyph)
+{
+    double x, y;
+    cairo_status_t status;
+
+    if (pdf_operators->is_new_text_object ||
+       pdf_operators->font_id != subset_glyph->font_id ||
+       pdf_operators->subset_id != subset_glyph->subset_id)
+    {
+       status = _cairo_pdf_operators_flush_glyphs (pdf_operators);
+       if (unlikely (status))
+           return status;
+
+       status = _cairo_pdf_operators_set_font_subset (pdf_operators, subset_glyph);
+       if (unlikely (status))
+           return status;
+
+       pdf_operators->is_new_text_object = FALSE;
+    }
+
+    x = glyph->x;
+    y = glyph->y;
+    cairo_matrix_transform_point (&pdf_operators->cairo_to_pdftext, &x, &y);
+
+    /* The TJ operator for displaying text strings can only set
+     * the horizontal position of the glyphs. If the y position
+     * (in text space) changes, use the Td operator to change the
+     * current position to the next glyph. We also use the Td
+     * operator to move the current position if the horizontal
+     * position changes by more than 10 (in text space
+     * units). This is becauses the horizontal glyph positioning
+     * in the TJ operator is intended for kerning and there may be
+     * PDF consumers that do not handle very large position
+     * adjustments in TJ.
+     */
+    if (fabs(x - pdf_operators->glyph_buf_x_pos) > 10 ||
+       fabs(y - pdf_operators->cur_y) > GLYPH_POSITION_TOLERANCE)
+    {
+       status = _cairo_pdf_operators_flush_glyphs (pdf_operators);
+       if (unlikely (status))
+           return status;
+
+       x = glyph->x;
+       y = glyph->y;
+       cairo_matrix_transform_point (&pdf_operators->cairo_to_pdf, &x, &y);
+       status = _cairo_pdf_operators_set_text_position (pdf_operators, x, y);
+       if (unlikely (status))
+           return status;
+
+       x = 0.0;
+       y = 0.0;
+    }
+
+    status = _cairo_pdf_operators_add_glyph (pdf_operators,
+                                            subset_glyph,
+                                            x);
+    return status;
+}
+
+/* A utf8_len of -1 indicates no unicode text. A utf8_len = 0 is an
+ * empty string.
+ */
+static cairo_int_status_t
+_cairo_pdf_operators_emit_cluster (cairo_pdf_operators_t      *pdf_operators,
+                                  const char                 *utf8,
+                                  int                         utf8_len,
+                                  cairo_glyph_t              *glyphs,
+                                  int                         num_glyphs,
+                                  cairo_text_cluster_flags_t  cluster_flags,
+                                  cairo_scaled_font_t        *scaled_font)
+{
+    cairo_scaled_font_subsets_glyph_t subset_glyph;
+    cairo_glyph_t *cur_glyph;
+    cairo_status_t status = CAIRO_STATUS_SUCCESS;
+    int i;
+
+    /* If the cluster maps 1 glyph to 1 or more unicode characters, we
+     * first try _map_glyph() with the unicode string to see if it can
+     * use toUnicode to map our glyph to the unicode. This will fail
+     * if the glyph is already mapped to a different unicode string.
+     *
+     * We also go through this path if no unicode mapping was
+     * supplied (utf8_len < 0).
+     *
+     * Mapping a glyph to a zero length unicode string requires the
+     * use of ActualText.
+     */
+    if (num_glyphs == 1 && utf8_len != 0) {
+       status = _cairo_scaled_font_subsets_map_glyph (pdf_operators->font_subsets,
+                                                      scaled_font,
+                                                      glyphs->index,
+                                                      utf8,
+                                                      utf8_len,
+                                                      &subset_glyph);
+       if (unlikely (status))
+           return status;
+
+       if (subset_glyph.utf8_is_mapped || utf8_len < 0) {
+           status = _cairo_pdf_operators_emit_glyph (pdf_operators,
+                                                     glyphs,
+                                                     &subset_glyph);
+           if (unlikely (status))
+               return status;
+
+           return CAIRO_STATUS_SUCCESS;
+       }
+    }
+
+    if (pdf_operators->use_actual_text) {
+       /* Fallback to using ActualText to map zero or more glyphs to a
+        * unicode string. */
+       status = _cairo_pdf_operators_flush_glyphs (pdf_operators);
+       if (unlikely (status))
+           return status;
+
+       status = _cairo_pdf_operators_begin_actualtext (pdf_operators, utf8, utf8_len);
+       if (unlikely (status))
+           return status;
+    }
+
+    cur_glyph = glyphs;
+    /* XXX
+     * If no glyphs, we should put *something* here for the text to be selectable. */
+    for (i = 0; i < num_glyphs; i++) {
+       status = _cairo_scaled_font_subsets_map_glyph (pdf_operators->font_subsets,
+                                                      scaled_font,
+                                                      cur_glyph->index,
+                                                      NULL, -1,
+                                                      &subset_glyph);
+       if (unlikely (status))
+           return status;
+
+       status = _cairo_pdf_operators_emit_glyph (pdf_operators,
+                                                 cur_glyph,
+                                                 &subset_glyph);
+       if (unlikely (status))
+           return status;
+
+       if ((cluster_flags & CAIRO_TEXT_CLUSTER_FLAG_BACKWARD))
+           cur_glyph--;
+       else
+           cur_glyph++;
+    }
+
+    if (pdf_operators->use_actual_text) {
+       status = _cairo_pdf_operators_flush_glyphs (pdf_operators);
+       if (unlikely (status))
+           return status;
+
+       status = _cairo_pdf_operators_end_actualtext (pdf_operators);
+    }
+
+    return status;
+}
+
+cairo_int_status_t
+_cairo_pdf_operators_show_text_glyphs (cairo_pdf_operators_t     *pdf_operators,
+                                      const char                 *utf8,
+                                      int                         utf8_len,
+                                      cairo_glyph_t              *glyphs,
+                                      int                         num_glyphs,
+                                      const cairo_text_cluster_t *clusters,
+                                      int                         num_clusters,
+                                      cairo_text_cluster_flags_t  cluster_flags,
+                                      cairo_scaled_font_t        *scaled_font)
+{
+    cairo_status_t status;
+    int i;
+    cairo_matrix_t text_matrix, invert_y_axis;
+    double x, y;
+    const char *cur_text;
+    cairo_glyph_t *cur_glyph;
+
+    pdf_operators->font_matrix_inverse = scaled_font->font_matrix;
+    status = cairo_matrix_invert (&pdf_operators->font_matrix_inverse);
+    if (status == CAIRO_STATUS_INVALID_MATRIX)
+       return CAIRO_STATUS_SUCCESS;
+    assert (status == CAIRO_STATUS_SUCCESS);
+
+    pdf_operators->is_new_text_object = FALSE;
+    if (pdf_operators->in_text_object == FALSE) {
+       status = _cairo_pdf_operators_begin_text (pdf_operators);
+       if (unlikely (status))
+           return status;
+
+       /* Force Tm and Tf to be emitted when starting a new text
+        * object.*/
+       pdf_operators->is_new_text_object = TRUE;
+    }
+
+    cairo_matrix_init_scale (&invert_y_axis, 1, -1);
+    text_matrix = scaled_font->scale;
+
+    /* Invert y axis in font space  */
+    cairo_matrix_multiply (&text_matrix, &text_matrix, &invert_y_axis);
+
+    /* Invert y axis in device space  */
+    cairo_matrix_multiply (&text_matrix, &invert_y_axis, &text_matrix);
+
+    if (pdf_operators->is_new_text_object ||
+       ! _cairo_matrix_scale_equal (&pdf_operators->text_matrix, &text_matrix))
+    {
+       status = _cairo_pdf_operators_flush_glyphs (pdf_operators);
+       if (unlikely (status))
+           return status;
+
+       x = glyphs[0].x;
+       y = glyphs[0].y;
+       cairo_matrix_transform_point (&pdf_operators->cairo_to_pdf, &x, &y);
+       text_matrix.x0 = x;
+       text_matrix.y0 = y;
+       status = _cairo_pdf_operators_set_text_matrix (pdf_operators, &text_matrix);
+       if (status == CAIRO_STATUS_INVALID_MATRIX)
+           return CAIRO_STATUS_SUCCESS;
+       if (unlikely (status))
+           return status;
+    }
+
+    if (num_clusters > 0) {
+       cur_text = utf8;
+       if ((cluster_flags & CAIRO_TEXT_CLUSTER_FLAG_BACKWARD))
+           cur_glyph = glyphs + num_glyphs;
+       else
+           cur_glyph = glyphs;
+       for (i = 0; i < num_clusters; i++) {
+           if ((cluster_flags & CAIRO_TEXT_CLUSTER_FLAG_BACKWARD))
+               cur_glyph -= clusters[i].num_glyphs;
+           status = _cairo_pdf_operators_emit_cluster (pdf_operators,
+                                                       cur_text,
+                                                       clusters[i].num_bytes,
+                                                       cur_glyph,
+                                                       clusters[i].num_glyphs,
+                                                       cluster_flags,
+                                                       scaled_font);
+           if (unlikely (status))
+               return status;
+
+           cur_text += clusters[i].num_bytes;
+           if (!(cluster_flags & CAIRO_TEXT_CLUSTER_FLAG_BACKWARD))
+               cur_glyph += clusters[i].num_glyphs;
+       }
+    } else {
+       for (i = 0; i < num_glyphs; i++) {
+           status = _cairo_pdf_operators_emit_cluster (pdf_operators,
+                                                       NULL,
+                                                       -1, /* no unicode string available */
+                                                       &glyphs[i],
+                                                       1,
+                                                       FALSE,
+                                                       scaled_font);
+           if (unlikely (status))
+               return status;
+       }
+    }
+
+    return _cairo_output_stream_get_status (pdf_operators->stream);
+}
+
+#endif /* CAIRO_HAS_PDF_OPERATORS */
diff --git a/src/cairo-pdf-shading-private.h b/src/cairo-pdf-shading-private.h
new file mode 100644 (file)
index 0000000..0ca8cb7
--- /dev/null
@@ -0,0 +1,100 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Adrian Johnson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Adrian Johnson.
+ *
+ * Contributor(s):
+ *     Adrian Johnson <ajohnson@redneon.com>
+ */
+
+#ifndef CAIRO_PDF_SHADING_H
+#define CAIRO_PDF_SHADING_H
+
+#include "cairo-compiler-private.h"
+#include "cairo-types-private.h"
+#include "cairo-pattern-private.h"
+
+
+typedef struct _cairo_pdf_shading {
+    int shading_type;
+    int bits_per_coordinate;
+    int bits_per_component;
+    int bits_per_flag;
+    double *decode_array;
+    int decode_array_length;
+    unsigned char *data;
+    unsigned long data_length;
+} cairo_pdf_shading_t;
+
+
+/**
+ * _cairo_pdf_shading_init_color:
+ * @shading: a #cairo_pdf_shading_t to initialize
+ * @pattern: the #cairo_mesh_pattern_t to initialize from
+ *
+ * Generate the PDF shading dictionary data for the a PDF type 7
+ * shading from RGB part of the specified mesh pattern.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS if successful, possible errors
+ * include %CAIRO_STATUS_NO_MEMORY.
+ **/
+cairo_private cairo_status_t
+_cairo_pdf_shading_init_color (cairo_pdf_shading_t        *shading,
+                              const cairo_mesh_pattern_t *pattern);
+
+
+/**
+ * _cairo_pdf_shading_init_alpha:
+ * @shading: a #cairo_pdf_shading_t to initialize
+ * @pattern: the #cairo_mesh_pattern_t to initialize from
+ *
+ * Generate the PDF shading dictionary data for a PDF type 7
+ * shading from alpha part of the specified mesh pattern.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS if successful, possible errors
+ * include %CAIRO_STATUS_NO_MEMORY.
+ **/
+cairo_private cairo_status_t
+_cairo_pdf_shading_init_alpha (cairo_pdf_shading_t        *shading,
+                              const cairo_mesh_pattern_t *pattern);
+
+/**
+ * _cairo_pdf_shading_fini:
+ * @shading: a #cairo_pdf_shading_t
+ *
+ * Free all resources associated with @shading.  After this call,
+ * @shading should not be used again without a subsequent call to
+ * _cairo_pdf_shading_init() again first.
+ **/
+cairo_private void
+_cairo_pdf_shading_fini (cairo_pdf_shading_t *shading);
+
+
+#endif /* CAIRO_PDF_SHADING_H */
diff --git a/src/cairo-pdf-shading.c b/src/cairo-pdf-shading.c
new file mode 100644 (file)
index 0000000..646e2cd
--- /dev/null
@@ -0,0 +1,279 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Adrian Johnson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Adrian Johnson.
+ *
+ * Contributor(s):
+ *     Adrian Johnson <ajohnson@redneon.com>
+ */
+
+#include "cairoint.h"
+
+#if CAIRO_HAS_PDF_OPERATORS
+
+#include "cairo-pdf-shading-private.h"
+
+#include "cairo-array-private.h"
+#include "cairo-error-private.h"
+#include <float.h>
+
+static unsigned char *
+encode_coordinate (unsigned char *p, double c)
+{
+    uint32_t f;
+
+    f = c;
+    *p++ = f >> 24;
+    *p++ = (f >> 16) & 0xff;
+    *p++ = (f >> 8)  & 0xff;
+    *p++ = f & 0xff;
+
+    return p;
+}
+
+static unsigned char *
+encode_point (unsigned char *p, const cairo_point_double_t *point)
+{
+    p = encode_coordinate (p, point->x);
+    p = encode_coordinate (p, point->y);
+
+    return p;
+}
+
+static unsigned char *
+encode_color_component (unsigned char *p, double color)
+{
+    uint16_t c;
+
+    c = _cairo_color_double_to_short (color);
+    *p++ = c >> 8;
+    *p++ = c & 0xff;
+
+    return p;
+}
+
+static unsigned char *
+encode_color (unsigned char *p, const cairo_color_t *color)
+{
+    p = encode_color_component (p, color->red);
+    p = encode_color_component (p, color->green);
+    p = encode_color_component (p, color->blue);
+
+    return p;
+}
+
+static unsigned char *
+encode_alpha (unsigned char *p, const cairo_color_t *color)
+{
+    p = encode_color_component (p, color->alpha);
+
+    return p;
+}
+
+static cairo_status_t
+_cairo_pdf_shading_generate_decode_array (cairo_pdf_shading_t        *shading,
+                                         const cairo_mesh_pattern_t *mesh,
+                                         cairo_bool_t                is_alpha)
+{
+    unsigned int num_color_components, i;
+    cairo_bool_t is_valid;
+
+    if (is_alpha)
+       num_color_components = 1;
+    else
+       num_color_components = 3;
+
+    shading->decode_array_length = 4 + num_color_components * 2;
+    shading->decode_array = _cairo_malloc_ab (shading->decode_array_length,
+                                             sizeof (double));
+    if (unlikely (shading->decode_array == NULL))
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    is_valid = _cairo_mesh_pattern_coord_box (mesh,
+                                             &shading->decode_array[0],
+                                             &shading->decode_array[2],
+                                             &shading->decode_array[1],
+                                             &shading->decode_array[3]);
+
+    assert (is_valid);
+    assert (shading->decode_array[1] - shading->decode_array[0] >= DBL_EPSILON);
+    assert (shading->decode_array[3] - shading->decode_array[2] >= DBL_EPSILON);
+
+    for (i = 0; i < num_color_components; i++) {
+       shading->decode_array[4 + 2*i] = 0;
+       shading->decode_array[5 + 2*i] = 1;
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+/* The ISO32000 specification mandates this order for the points which
+ * define the patch. */
+static const int pdf_points_order_i[16] = {
+    0, 0, 0, 0, 1, 2, 3, 3, 3, 3, 2, 1, 1, 1, 2, 2 };
+static const int pdf_points_order_j[16] = {
+    0, 1, 2, 3, 3, 3, 3, 2, 1, 0, 0, 0, 1, 2, 2, 1 };
+
+static cairo_status_t
+_cairo_pdf_shading_generate_data (cairo_pdf_shading_t        *shading,
+                                 const cairo_mesh_pattern_t *mesh,
+                                 cairo_bool_t                is_alpha)
+{
+    const cairo_mesh_patch_t *patch;
+    double x_off, y_off, x_scale, y_scale;
+    unsigned int num_patches;
+    unsigned int num_color_components;
+    unsigned char *p;
+    unsigned int i, j;
+
+    if (is_alpha)
+       num_color_components = 1;
+    else
+       num_color_components = 3;
+
+    num_patches = _cairo_array_num_elements (&mesh->patches);
+    patch = _cairo_array_index_const (&mesh->patches, 0);
+
+    /* Each patch requires:
+     *
+     * 1 flag - 1 byte
+     * 16 points. Each point is 2 coordinates. Each coordinate is
+     * stored in 4 bytes.
+     *
+     * 4 colors. Each color is stored in 2 bytes * num_color_components.
+     */
+    shading->data_length = num_patches * (1 + 16 * 2 * 4 + 4 * 2 * num_color_components);
+    shading->data = malloc (shading->data_length);
+    if (unlikely (shading->data == NULL))
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    x_off = shading->decode_array[0];
+    y_off = shading->decode_array[2];
+    x_scale = UINT32_MAX / (shading->decode_array[1] - x_off);
+    y_scale = UINT32_MAX / (shading->decode_array[3] - y_off);
+
+    p = shading->data;
+    for (i = 0; i < num_patches; i++) {
+       /* edge flag */
+       *p++ = 0;
+
+       /* 16 points */
+       for (j = 0; j < 16; j++) {
+           cairo_point_double_t point;
+           int pi, pj;
+
+           pi = pdf_points_order_i[j];
+           pj = pdf_points_order_j[j];
+           point = patch[i].points[pi][pj];
+
+           /* Transform the point as specified in the decode array */
+           point.x -= x_off;
+           point.y -= y_off;
+           point.x *= x_scale;
+           point.y *= y_scale;
+
+           /* Make sure that rounding errors don't cause
+            * wraparounds */
+           point.x = _cairo_restrict_value (point.x, 0, UINT32_MAX);
+           point.y = _cairo_restrict_value (point.y, 0, UINT32_MAX);
+
+           p = encode_point (p, &point);
+       }
+
+       /* 4 colors */
+       for (j = 0; j < 4; j++) {
+           if (is_alpha)
+               p = encode_alpha (p, &patch[i].colors[j]);
+           else
+               p = encode_color (p, &patch[i].colors[j]);
+       }
+    }
+
+    assert (p == shading->data + shading->data_length);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_pdf_shading_init (cairo_pdf_shading_t        *shading,
+                        const cairo_mesh_pattern_t *mesh,
+                        cairo_bool_t                is_alpha)
+{
+    cairo_status_t status;
+
+    assert (mesh->base.status == CAIRO_STATUS_SUCCESS);
+    assert (mesh->current_patch == NULL);
+
+    shading->shading_type = 7;
+
+    /*
+     * Coordinates from the minimum to the maximum value of the mesh
+     * map to the [0..UINT32_MAX] range and are represented as
+     * uint32_t values.
+     *
+     * Color components are represented as uint16_t (in a 0.16 fixed
+     * point format, as in the rest of cairo).
+     */
+    shading->bits_per_coordinate = 32;
+    shading->bits_per_component = 16;
+    shading->bits_per_flag = 8;
+
+    shading->decode_array = NULL;
+    shading->data = NULL;
+
+    status = _cairo_pdf_shading_generate_decode_array (shading, mesh, is_alpha);
+    if (unlikely (status))
+       return status;
+
+    return _cairo_pdf_shading_generate_data (shading, mesh, is_alpha);
+}
+
+cairo_status_t
+_cairo_pdf_shading_init_color (cairo_pdf_shading_t        *shading,
+                              const cairo_mesh_pattern_t *pattern)
+{
+    return _cairo_pdf_shading_init (shading, pattern, FALSE);
+}
+
+cairo_status_t
+_cairo_pdf_shading_init_alpha (cairo_pdf_shading_t        *shading,
+                              const cairo_mesh_pattern_t *pattern)
+{
+    return _cairo_pdf_shading_init (shading, pattern, TRUE);
+}
+
+void
+_cairo_pdf_shading_fini (cairo_pdf_shading_t *shading)
+{
+    free (shading->data);
+    free (shading->decode_array);
+}
+
+#endif /* CAIRO_HAS_PDF_OPERATORS */
diff --git a/src/cairo-pdf-surface-private.h b/src/cairo-pdf-surface-private.h
new file mode 100644 (file)
index 0000000..08620e6
--- /dev/null
@@ -0,0 +1,202 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2004 Red Hat, Inc
+ * Copyright © 2006 Red Hat, Inc
+ * Copyright © 2007, 2008 Adrian Johnson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *     Kristian Høgsberg <krh@redhat.com>
+ *     Carl Worth <cworth@cworth.org>
+ *     Adrian Johnson <ajohnson@redneon.com>
+ */
+
+#ifndef CAIRO_PDF_SURFACE_PRIVATE_H
+#define CAIRO_PDF_SURFACE_PRIVATE_H
+
+#include "cairo-pdf.h"
+
+#include "cairo-surface-private.h"
+#include "cairo-surface-clipper-private.h"
+#include "cairo-pdf-operators-private.h"
+#include "cairo-path-fixed-private.h"
+
+typedef struct _cairo_pdf_resource {
+    unsigned int id;
+} cairo_pdf_resource_t;
+
+#define CAIRO_NUM_OPERATORS (CAIRO_OPERATOR_HSL_LUMINOSITY + 1)
+
+typedef struct _cairo_pdf_group_resources {
+    cairo_bool_t  operators[CAIRO_NUM_OPERATORS];
+    cairo_array_t alphas;
+    cairo_array_t smasks;
+    cairo_array_t patterns;
+    cairo_array_t xobjects;
+    cairo_array_t fonts;
+} cairo_pdf_group_resources_t;
+
+typedef struct _cairo_pdf_source_surface_entry {
+    cairo_hash_entry_t base;
+    unsigned int id;
+    unsigned char *unique_id;
+    unsigned long unique_id_length;
+    cairo_bool_t interpolate;
+    cairo_bool_t stencil_mask;
+    cairo_pdf_resource_t surface_res;
+    int width;
+    int height;
+    cairo_rectangle_int_t extents;
+} cairo_pdf_source_surface_entry_t;
+
+typedef struct _cairo_pdf_source_surface {
+    cairo_surface_t *surface;
+    cairo_pdf_source_surface_entry_t *hash_entry;
+} cairo_pdf_source_surface_t;
+
+typedef struct _cairo_pdf_pattern {
+    double width;
+    double height;
+    cairo_rectangle_int_t extents;
+    cairo_pattern_t *pattern;
+    cairo_pdf_resource_t pattern_res;
+    cairo_pdf_resource_t gstate_res;
+} cairo_pdf_pattern_t;
+
+typedef enum _cairo_pdf_operation {
+    PDF_PAINT,
+    PDF_MASK,
+    PDF_FILL,
+    PDF_STROKE,
+    PDF_SHOW_GLYPHS
+} cairo_pdf_operation_t;
+
+typedef struct _cairo_pdf_smask_group {
+    double               width;
+    double               height;
+    cairo_rectangle_int_t extents;
+    cairo_pdf_resource_t  group_res;
+    cairo_pdf_operation_t operation;
+    cairo_pattern_t     *source;
+    cairo_pdf_resource_t  source_res;
+    cairo_pattern_t     *mask;
+    cairo_path_fixed_t   path;
+    cairo_fill_rule_t    fill_rule;
+    cairo_stroke_style_t  style;
+    cairo_matrix_t       ctm;
+    cairo_matrix_t       ctm_inverse;
+    char                *utf8;
+    int                   utf8_len;
+    cairo_glyph_t       *glyphs;
+    int                          num_glyphs;
+    cairo_text_cluster_t *clusters;
+    int                   num_clusters;
+    cairo_bool_t          cluster_flags;
+    cairo_scaled_font_t         *scaled_font;
+} cairo_pdf_smask_group_t;
+
+typedef struct _cairo_pdf_surface cairo_pdf_surface_t;
+
+struct _cairo_pdf_surface {
+    cairo_surface_t base;
+
+    /* Prefer the name "output" here to avoid confusion over the
+     * structure within a PDF document known as a "stream". */
+    cairo_output_stream_t *output;
+
+    double width;
+    double height;
+    cairo_matrix_t cairo_to_pdf;
+
+    cairo_array_t objects;
+    cairo_array_t pages;
+    cairo_array_t rgb_linear_functions;
+    cairo_array_t alpha_linear_functions;
+    cairo_array_t page_patterns;
+    cairo_array_t page_surfaces;
+    cairo_hash_table_t *all_surfaces;
+    cairo_array_t smask_groups;
+    cairo_array_t knockout_group;
+
+    cairo_scaled_font_subsets_t *font_subsets;
+    cairo_array_t fonts;
+
+    cairo_pdf_resource_t next_available_resource;
+    cairo_pdf_resource_t pages_resource;
+
+    cairo_pdf_version_t pdf_version;
+    cairo_bool_t compress_content;
+
+    cairo_pdf_resource_t content;
+    cairo_pdf_resource_t content_resources;
+    cairo_pdf_group_resources_t resources;
+    cairo_bool_t has_fallback_images;
+    cairo_bool_t header_emitted;
+
+    struct {
+       cairo_bool_t active;
+       cairo_pdf_resource_t self;
+       cairo_pdf_resource_t length;
+       long start_offset;
+       cairo_bool_t compressed;
+       cairo_output_stream_t *old_output;
+    } pdf_stream;
+
+    struct {
+       cairo_bool_t active;
+       cairo_output_stream_t *stream;
+       cairo_output_stream_t *mem_stream;
+       cairo_output_stream_t *old_output;
+       cairo_pdf_resource_t   resource;
+       cairo_box_double_t     bbox;
+       cairo_bool_t is_knockout;
+    } group_stream;
+
+    cairo_surface_clipper_t clipper;
+
+    cairo_pdf_operators_t pdf_operators;
+    cairo_paginated_mode_t paginated_mode;
+    cairo_bool_t select_pattern_gstate_saved;
+
+    cairo_bool_t force_fallbacks;
+
+    cairo_operator_t current_operator;
+    cairo_bool_t current_pattern_is_solid_color;
+    cairo_bool_t current_color_is_stroke;
+    double current_color_red;
+    double current_color_green;
+    double current_color_blue;
+    double current_color_alpha;
+
+    cairo_surface_t *paginated_surface;
+};
+
+#endif /* CAIRO_PDF_SURFACE_PRIVATE_H */
diff --git a/src/cairo-pdf-surface.c b/src/cairo-pdf-surface.c
new file mode 100644 (file)
index 0000000..988a215
--- /dev/null
@@ -0,0 +1,6908 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2004 Red Hat, Inc
+ * Copyright © 2006 Red Hat, Inc
+ * Copyright © 2007, 2008 Adrian Johnson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *     Kristian Høgsberg <krh@redhat.com>
+ *     Carl Worth <cworth@cworth.org>
+ *     Adrian Johnson <ajohnson@redneon.com>
+ */
+
+#define _BSD_SOURCE /* for snprintf() */
+#include "cairoint.h"
+
+#include "cairo-pdf.h"
+#include "cairo-pdf-surface-private.h"
+#include "cairo-pdf-operators-private.h"
+#include "cairo-pdf-shading-private.h"
+
+#include "cairo-array-private.h"
+#include "cairo-analysis-surface-private.h"
+#include "cairo-composite-rectangles-private.h"
+#include "cairo-default-context-private.h"
+#include "cairo-error-private.h"
+#include "cairo-image-surface-private.h"
+#include "cairo-image-info-private.h"
+#include "cairo-recording-surface-private.h"
+#include "cairo-output-stream-private.h"
+#include "cairo-paginated-private.h"
+#include "cairo-scaled-font-subsets-private.h"
+#include "cairo-surface-clipper-private.h"
+#include "cairo-surface-snapshot-private.h"
+#include "cairo-surface-subsurface-private.h"
+#include "cairo-type3-glyph-surface-private.h"
+
+#include <time.h>
+#include <zlib.h>
+
+/* Issues:
+ *
+ * - We embed an image in the stream each time it's composited.  We
+ *   could add generation counters to surfaces and remember the stream
+ *   ID for a particular generation for a particular surface.
+ *
+ * - Backend specific meta data.
+ */
+
+/*
+ * Page Structure of the Generated PDF:
+ *
+ * Each page requiring fallbacks images contains a knockout group at
+ * the top level. The first operation of the knockout group paints a
+ * group containing all the supported drawing operations. Fallback
+ * images (if any) are painted in the knockout group. This ensures
+ * that fallback images do not composite with any content under the
+ * fallback images.
+ *
+ * Streams:
+ *
+ * This PDF surface has three types of streams:
+ *  - PDF Stream
+ *  - Content Stream
+ *  - Group Stream
+ *
+ * Calling _cairo_output_stream_printf (surface->output, ...) will
+ * write to the currently open stream.
+ *
+ * PDF Stream:
+ *   A PDF Stream may be opened and closed with the following functions:
+ *     _cairo_pdf_surface_open stream ()
+ *     _cairo_pdf_surface_close_stream ()
+ *
+ *   PDF Streams are written directly to the PDF file. They are used for
+ *   fonts, images and patterns.
+ *
+ * Content Stream:
+ *   The Content Stream is opened and closed with the following functions:
+ *     _cairo_pdf_surface_open_content_stream ()
+ *     _cairo_pdf_surface_close_content_stream ()
+ *
+ *   The Content Stream contains the text and graphics operators.
+ *
+ * Group Stream:
+ *   A Group Stream may be opened and closed with the following functions:
+ *     _cairo_pdf_surface_open_group ()
+ *     _cairo_pdf_surface_close_group ()
+ *
+ *   A Group Stream is a Form XObject. It is used for short sequences
+ *   of operators. As the content is very short the group is stored in
+ *   memory until it is closed. This allows some optimization such as
+ *   including the Resource dictionary and stream length inside the
+ *   XObject instead of using an indirect object.
+ */
+
+/**
+ * SECTION:cairo-pdf
+ * @Title: PDF Surfaces
+ * @Short_Description: Rendering PDF documents
+ * @See_Also: #cairo_surface_t
+ *
+ * The PDF surface is used to render cairo graphics to Adobe
+ * PDF files and is a multi-page vector surface backend.
+ */
+
+static cairo_bool_t
+_cairo_pdf_surface_get_extents (void                   *abstract_surface,
+                               cairo_rectangle_int_t   *rectangle);
+
+/**
+ * CAIRO_HAS_PDF_SURFACE:
+ *
+ * Defined if the PDF surface backend is available.
+ * This macro can be used to conditionally compile backend-specific code.
+ */
+
+static const cairo_pdf_version_t _cairo_pdf_versions[] =
+{
+    CAIRO_PDF_VERSION_1_4,
+    CAIRO_PDF_VERSION_1_5
+};
+
+#define CAIRO_PDF_VERSION_LAST ARRAY_LENGTH (_cairo_pdf_versions)
+
+static const char * _cairo_pdf_version_strings[CAIRO_PDF_VERSION_LAST] =
+{
+    "PDF 1.4",
+    "PDF 1.5"
+};
+
+typedef struct _cairo_pdf_object {
+    long offset;
+} cairo_pdf_object_t;
+
+typedef struct _cairo_pdf_font {
+    unsigned int font_id;
+    unsigned int subset_id;
+    cairo_pdf_resource_t subset_resource;
+} cairo_pdf_font_t;
+
+typedef struct _cairo_pdf_rgb_linear_function {
+    cairo_pdf_resource_t resource;
+    double               color1[3];
+    double               color2[3];
+} cairo_pdf_rgb_linear_function_t;
+
+typedef struct _cairo_pdf_alpha_linear_function {
+    cairo_pdf_resource_t resource;
+    double               alpha1;
+    double               alpha2;
+} cairo_pdf_alpha_linear_function_t;
+
+static cairo_pdf_resource_t
+_cairo_pdf_surface_new_object (cairo_pdf_surface_t *surface);
+
+static void
+_cairo_pdf_surface_clear (cairo_pdf_surface_t *surface);
+
+static void
+_cairo_pdf_smask_group_destroy (cairo_pdf_smask_group_t *group);
+
+static cairo_status_t
+_cairo_pdf_surface_add_font (unsigned int        font_id,
+                            unsigned int        subset_id,
+                            void               *closure);
+
+static void
+_cairo_pdf_group_resources_init (cairo_pdf_group_resources_t *res);
+
+static cairo_status_t
+_cairo_pdf_surface_open_stream (cairo_pdf_surface_t    *surface,
+                               cairo_pdf_resource_t    *resource,
+                                cairo_bool_t             compressed,
+                               const char              *fmt,
+                               ...) CAIRO_PRINTF_FORMAT(4, 5);
+static cairo_status_t
+_cairo_pdf_surface_close_stream (cairo_pdf_surface_t   *surface);
+
+static cairo_status_t
+_cairo_pdf_surface_write_page (cairo_pdf_surface_t *surface);
+
+static void
+_cairo_pdf_surface_write_pages (cairo_pdf_surface_t *surface);
+
+static cairo_pdf_resource_t
+_cairo_pdf_surface_write_info (cairo_pdf_surface_t *surface);
+
+static cairo_pdf_resource_t
+_cairo_pdf_surface_write_catalog (cairo_pdf_surface_t *surface);
+
+static long
+_cairo_pdf_surface_write_xref (cairo_pdf_surface_t *surface);
+
+static cairo_status_t
+_cairo_pdf_surface_write_page (cairo_pdf_surface_t *surface);
+
+static cairo_status_t
+_cairo_pdf_surface_emit_font_subsets (cairo_pdf_surface_t *surface);
+
+static cairo_bool_t
+_cairo_pdf_source_surface_equal (const void *key_a, const void *key_b);
+
+static const cairo_surface_backend_t cairo_pdf_surface_backend;
+static const cairo_paginated_surface_backend_t cairo_pdf_surface_paginated_backend;
+
+static cairo_pdf_resource_t
+_cairo_pdf_surface_new_object (cairo_pdf_surface_t *surface)
+{
+    cairo_pdf_resource_t resource;
+    cairo_status_t status;
+    cairo_pdf_object_t object;
+
+    object.offset = _cairo_output_stream_get_position (surface->output);
+
+    status = _cairo_array_append (&surface->objects, &object);
+    if (unlikely (status)) {
+       resource.id = 0;
+       return resource;
+    }
+
+    resource = surface->next_available_resource;
+    surface->next_available_resource.id++;
+
+    return resource;
+}
+
+static void
+_cairo_pdf_surface_update_object (cairo_pdf_surface_t  *surface,
+                                 cairo_pdf_resource_t   resource)
+{
+    cairo_pdf_object_t *object;
+
+    object = _cairo_array_index (&surface->objects, resource.id - 1);
+    object->offset = _cairo_output_stream_get_position (surface->output);
+}
+
+static void
+_cairo_pdf_surface_set_size_internal (cairo_pdf_surface_t *surface,
+                                     double              width,
+                                     double              height)
+{
+    surface->width = width;
+    surface->height = height;
+    cairo_matrix_init (&surface->cairo_to_pdf, 1, 0, 0, -1, 0, height);
+    _cairo_pdf_operators_set_cairo_to_pdf_matrix (&surface->pdf_operators,
+                                                 &surface->cairo_to_pdf);
+}
+
+static cairo_bool_t
+_path_covers_bbox (cairo_pdf_surface_t *surface,
+                  cairo_path_fixed_t *path)
+{
+    cairo_box_t box;
+
+    return _cairo_path_fixed_is_box (path, &box) &&
+          box.p1.x <= 0 &&
+          box.p1.y <= 0 &&
+          box.p2.x >= _cairo_fixed_from_double (surface->width) &&
+          box.p2.y >= _cairo_fixed_from_double (surface->height);
+}
+
+static cairo_status_t
+_cairo_pdf_surface_clipper_intersect_clip_path (cairo_surface_clipper_t *clipper,
+                                               cairo_path_fixed_t      *path,
+                                               cairo_fill_rule_t       fill_rule,
+                                               double                  tolerance,
+                                               cairo_antialias_t       antialias)
+{
+    cairo_pdf_surface_t *surface = cairo_container_of (clipper,
+                                                      cairo_pdf_surface_t,
+                                                      clipper);
+    cairo_int_status_t status;
+
+    status = _cairo_pdf_operators_flush (&surface->pdf_operators);
+    if (unlikely (status))
+       return status;
+
+    if (path == NULL) {
+       _cairo_output_stream_printf (surface->output, "Q q\n");
+
+       surface->current_pattern_is_solid_color = FALSE;
+       _cairo_pdf_operators_reset (&surface->pdf_operators);
+
+       return CAIRO_STATUS_SUCCESS;
+    }
+
+    if (_path_covers_bbox (surface, path))
+       return CAIRO_STATUS_SUCCESS;
+
+    return _cairo_pdf_operators_clip (&surface->pdf_operators, path, fill_rule);
+}
+
+static cairo_surface_t *
+_cairo_pdf_surface_create_for_stream_internal (cairo_output_stream_t   *output,
+                                              double                    width,
+                                              double                    height)
+{
+    cairo_pdf_surface_t *surface;
+    cairo_status_t status, status_ignored;
+
+    surface = malloc (sizeof (cairo_pdf_surface_t));
+    if (unlikely (surface == NULL)) {
+       /* destroy stream on behalf of caller */
+       status = _cairo_output_stream_destroy (output);
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+    }
+
+    _cairo_surface_init (&surface->base,
+                        &cairo_pdf_surface_backend,
+                        NULL, /* device */
+                        CAIRO_CONTENT_COLOR_ALPHA);
+
+    surface->output = output;
+    surface->width = width;
+    surface->height = height;
+    cairo_matrix_init (&surface->cairo_to_pdf, 1, 0, 0, -1, 0, height);
+
+    _cairo_array_init (&surface->objects, sizeof (cairo_pdf_object_t));
+    _cairo_array_init (&surface->pages, sizeof (cairo_pdf_resource_t));
+    _cairo_array_init (&surface->rgb_linear_functions, sizeof (cairo_pdf_rgb_linear_function_t));
+    _cairo_array_init (&surface->alpha_linear_functions, sizeof (cairo_pdf_alpha_linear_function_t));
+    _cairo_array_init (&surface->fonts, sizeof (cairo_pdf_font_t));
+    _cairo_array_init (&surface->smask_groups, sizeof (cairo_pdf_smask_group_t *));
+    _cairo_array_init (&surface->knockout_group, sizeof (cairo_pdf_resource_t));
+
+    _cairo_array_init (&surface->page_patterns, sizeof (cairo_pdf_pattern_t));
+    _cairo_array_init (&surface->page_surfaces, sizeof (cairo_pdf_source_surface_t));
+    surface->all_surfaces = _cairo_hash_table_create (_cairo_pdf_source_surface_equal);
+    if (unlikely (surface->all_surfaces == NULL)) {
+       status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+       goto BAIL0;
+    }
+
+    _cairo_pdf_group_resources_init (&surface->resources);
+
+    surface->font_subsets = _cairo_scaled_font_subsets_create_composite ();
+    if (! surface->font_subsets) {
+       status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+       goto BAIL1;
+    }
+
+    _cairo_scaled_font_subsets_enable_latin_subset (surface->font_subsets, TRUE);
+
+    surface->next_available_resource.id = 1;
+    surface->pages_resource = _cairo_pdf_surface_new_object (surface);
+    if (surface->pages_resource.id == 0) {
+       status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+        goto BAIL2;
+    }
+
+    surface->pdf_version = CAIRO_PDF_VERSION_1_5;
+    surface->compress_content = TRUE;
+    surface->pdf_stream.active = FALSE;
+    surface->pdf_stream.old_output = NULL;
+    surface->group_stream.active = FALSE;
+    surface->group_stream.stream = NULL;
+    surface->group_stream.mem_stream = NULL;
+
+    surface->paginated_mode = CAIRO_PAGINATED_MODE_ANALYZE;
+
+    surface->force_fallbacks = FALSE;
+    surface->select_pattern_gstate_saved = FALSE;
+    surface->current_pattern_is_solid_color = FALSE;
+    surface->current_operator = CAIRO_OPERATOR_OVER;
+    surface->header_emitted = FALSE;
+
+    _cairo_surface_clipper_init (&surface->clipper,
+                                _cairo_pdf_surface_clipper_intersect_clip_path);
+
+    _cairo_pdf_operators_init (&surface->pdf_operators,
+                              surface->output,
+                              &surface->cairo_to_pdf,
+                              surface->font_subsets);
+    _cairo_pdf_operators_set_font_subsets_callback (&surface->pdf_operators,
+                                                   _cairo_pdf_surface_add_font,
+                                                   surface);
+    _cairo_pdf_operators_enable_actual_text(&surface->pdf_operators, TRUE);
+
+    surface->paginated_surface =  _cairo_paginated_surface_create (
+                                         &surface->base,
+                                         CAIRO_CONTENT_COLOR_ALPHA,
+                                         &cairo_pdf_surface_paginated_backend);
+
+    status = surface->paginated_surface->status;
+    if (status == CAIRO_STATUS_SUCCESS) {
+       /* paginated keeps the only reference to surface now, drop ours */
+       cairo_surface_destroy (&surface->base);
+       return surface->paginated_surface;
+    }
+
+BAIL2:
+    _cairo_scaled_font_subsets_destroy (surface->font_subsets);
+BAIL1:
+    _cairo_hash_table_destroy (surface->all_surfaces);
+BAIL0:
+    _cairo_array_fini (&surface->objects);
+    free (surface);
+
+    /* destroy stream on behalf of caller */
+    status_ignored = _cairo_output_stream_destroy (output);
+
+    return _cairo_surface_create_in_error (status);
+}
+
+/**
+ * cairo_pdf_surface_create_for_stream:
+ * @write_func: a #cairo_write_func_t to accept the output data, may be %NULL
+ *              to indicate a no-op @write_func. With a no-op @write_func,
+ *              the surface may be queried or used as a source without
+ *              generating any temporary files.
+ * @closure: the closure argument for @write_func
+ * @width_in_points: width of the surface, in points (1 point == 1/72.0 inch)
+ * @height_in_points: height of the surface, in points (1 point == 1/72.0 inch)
+ *
+ * Creates a PDF surface of the specified size in points to be written
+ * incrementally to the stream represented by @write_func and @closure.
+ *
+ * Return value: a pointer to the newly created surface. The caller
+ * owns the surface and should call cairo_surface_destroy() when done
+ * with it.
+ *
+ * This function always returns a valid pointer, but it will return a
+ * pointer to a "nil" surface if an error such as out of memory
+ * occurs. You can use cairo_surface_status() to check for this.
+ *
+ * Since: 1.2
+ */
+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_output_stream_t *output;
+
+    output = _cairo_output_stream_create (write_func, NULL, closure);
+    if (_cairo_output_stream_get_status (output))
+       return _cairo_surface_create_in_error (_cairo_output_stream_destroy (output));
+
+    return _cairo_pdf_surface_create_for_stream_internal (output,
+                                                         width_in_points,
+                                                         height_in_points);
+}
+
+/**
+ * cairo_pdf_surface_create:
+ * @filename: a filename for the PDF output (must be writable), %NULL may be
+ *            used to specify no output. This will generate a PDF surface that
+ *            may be queried and used as a source, without generating a
+ *            temporary file.
+ * @width_in_points: width of the surface, in points (1 point == 1/72.0 inch)
+ * @height_in_points: height of the surface, in points (1 point == 1/72.0 inch)
+ *
+ * Creates a PDF surface of the specified size in points to be written
+ * to @filename.
+ *
+ * Return value: a pointer to the newly created surface. The caller
+ * owns the surface and should call cairo_surface_destroy() when done
+ * with it.
+ *
+ * This function always returns a valid pointer, but it will return a
+ * pointer to a "nil" surface if an error such as out of memory
+ * occurs. You can use cairo_surface_status() to check for this.
+ *
+ * Since: 1.2
+ **/
+cairo_surface_t *
+cairo_pdf_surface_create (const char           *filename,
+                         double                 width_in_points,
+                         double                 height_in_points)
+{
+    cairo_output_stream_t *output;
+
+    output = _cairo_output_stream_create_for_filename (filename);
+    if (_cairo_output_stream_get_status (output))
+       return _cairo_surface_create_in_error (_cairo_output_stream_destroy (output));
+
+    return _cairo_pdf_surface_create_for_stream_internal (output,
+                                                         width_in_points,
+                                                         height_in_points);
+}
+
+static cairo_bool_t
+_cairo_surface_is_pdf (cairo_surface_t *surface)
+{
+    return surface->backend == &cairo_pdf_surface_backend;
+}
+
+/* If the abstract_surface is a paginated surface, and that paginated
+ * surface's target is a pdf_surface, then set pdf_surface to that
+ * target. Otherwise return FALSE.
+ */
+static cairo_bool_t
+_extract_pdf_surface (cairo_surface_t           *surface,
+                     cairo_pdf_surface_t       **pdf_surface)
+{
+    cairo_surface_t *target;
+    cairo_status_t status_ignored;
+
+    if (surface->status)
+       return FALSE;
+    if (surface->finished) {
+       status_ignored = _cairo_surface_set_error (surface,
+                                                  _cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
+        return FALSE;
+    }
+
+    if (! _cairo_surface_is_paginated (surface)) {
+       status_ignored = _cairo_surface_set_error (surface,
+                                                  _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH));
+       return FALSE;
+    }
+
+    target = _cairo_paginated_surface_get_target (surface);
+    if (target->status) {
+       status_ignored = _cairo_surface_set_error (surface,
+                                                  target->status);
+       return FALSE;
+    }
+    if (target->finished) {
+       status_ignored = _cairo_surface_set_error (surface,
+                                                  _cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
+       return FALSE;
+    }
+
+    if (! _cairo_surface_is_pdf (target)) {
+       status_ignored = _cairo_surface_set_error (surface,
+                                                  _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH));
+       return FALSE;
+    }
+
+    *pdf_surface = (cairo_pdf_surface_t *) target;
+    return TRUE;
+}
+
+/**
+ * cairo_pdf_surface_restrict_to_version:
+ * @surface: a PDF #cairo_surface_t
+ * @version: PDF version
+ *
+ * Restricts the generated PDF file to @version. See cairo_pdf_get_versions()
+ * for a list of available version values that can be used here.
+ *
+ * This function should only be called before any drawing operations
+ * have been performed on the given surface. The simplest way to do
+ * this is to call this function immediately after creating the
+ * surface.
+ *
+ * Since: 1.10
+ **/
+void
+cairo_pdf_surface_restrict_to_version (cairo_surface_t                 *abstract_surface,
+                                      cairo_pdf_version_t       version)
+{
+    cairo_pdf_surface_t *surface = NULL; /* hide compiler warning */
+
+    if (! _extract_pdf_surface (abstract_surface, &surface))
+       return;
+
+    if (version < CAIRO_PDF_VERSION_LAST)
+       surface->pdf_version = version;
+
+    _cairo_pdf_operators_enable_actual_text(&surface->pdf_operators,
+                                           version >= CAIRO_PDF_VERSION_1_5);
+}
+
+/**
+ * cairo_pdf_get_versions:
+ * @versions: supported version list
+ * @num_versions: list length
+ *
+ * Used to retrieve the list of supported versions. See
+ * cairo_pdf_surface_restrict_to_version().
+ *
+ * Since: 1.10
+ **/
+void
+cairo_pdf_get_versions (cairo_pdf_version_t const      **versions,
+                        int                             *num_versions)
+{
+    if (versions != NULL)
+       *versions = _cairo_pdf_versions;
+
+    if (num_versions != NULL)
+       *num_versions = CAIRO_PDF_VERSION_LAST;
+}
+
+/**
+ * cairo_pdf_version_to_string:
+ * @version: a version id
+ *
+ * Get the string representation of the given @version id. This function
+ * will return %NULL if @version isn't valid. See cairo_pdf_get_versions()
+ * for a way to get the list of valid version ids.
+ *
+ * Return value: the string associated to given version.
+ *
+ * Since: 1.10
+ **/
+const char *
+cairo_pdf_version_to_string (cairo_pdf_version_t version)
+{
+    if (version >= CAIRO_PDF_VERSION_LAST)
+       return NULL;
+
+    return _cairo_pdf_version_strings[version];
+}
+
+/**
+ * cairo_pdf_surface_set_size:
+ * @surface: a PDF #cairo_surface_t
+ * @width_in_points: new surface width, in points (1 point == 1/72.0 inch)
+ * @height_in_points: new surface height, in points (1 point == 1/72.0 inch)
+ *
+ * Changes the size of a PDF surface for the current (and
+ * subsequent) pages.
+ *
+ * This function should only be called before any drawing operations
+ * have been performed on the current page. The simplest way to do
+ * this is to call this function immediately after creating the
+ * surface or immediately after completing a page with either
+ * cairo_show_page() or cairo_copy_page().
+ *
+ * Since: 1.2
+ **/
+void
+cairo_pdf_surface_set_size (cairo_surface_t    *surface,
+                           double               width_in_points,
+                           double               height_in_points)
+{
+    cairo_pdf_surface_t *pdf_surface = NULL; /* hide compiler warning */
+    cairo_status_t status;
+
+    if (! _extract_pdf_surface (surface, &pdf_surface))
+       return;
+
+    _cairo_pdf_surface_set_size_internal (pdf_surface,
+                                         width_in_points,
+                                         height_in_points);
+    status = _cairo_paginated_surface_set_size (pdf_surface->paginated_surface,
+                                               width_in_points,
+                                               height_in_points);
+    if (status)
+       status = _cairo_surface_set_error (surface, status);
+}
+
+static void
+_cairo_pdf_surface_clear (cairo_pdf_surface_t *surface)
+{
+    int i, size;
+    cairo_pdf_pattern_t *pattern;
+    cairo_pdf_source_surface_t *src_surface;
+    cairo_pdf_smask_group_t *group;
+
+    size = _cairo_array_num_elements (&surface->page_patterns);
+    for (i = 0; i < size; i++) {
+       pattern = (cairo_pdf_pattern_t *) _cairo_array_index (&surface->page_patterns, i);
+       cairo_pattern_destroy (pattern->pattern);
+    }
+    _cairo_array_truncate (&surface->page_patterns, 0);
+
+    size = _cairo_array_num_elements (&surface->page_surfaces);
+    for (i = 0; i < size; i++) {
+       src_surface = (cairo_pdf_source_surface_t *) _cairo_array_index (&surface->page_surfaces, i);
+       cairo_surface_destroy (src_surface->surface);
+    }
+    _cairo_array_truncate (&surface->page_surfaces, 0);
+
+    size = _cairo_array_num_elements (&surface->smask_groups);
+    for (i = 0; i < size; i++) {
+       _cairo_array_copy_element (&surface->smask_groups, i, &group);
+       _cairo_pdf_smask_group_destroy (group);
+    }
+    _cairo_array_truncate (&surface->smask_groups, 0);
+    _cairo_array_truncate (&surface->knockout_group, 0);
+}
+
+static void
+_cairo_pdf_group_resources_init (cairo_pdf_group_resources_t *res)
+{
+    int i;
+
+    for (i = 0; i < CAIRO_NUM_OPERATORS; i++)
+       res->operators[i] = FALSE;
+
+    _cairo_array_init (&res->alphas, sizeof (double));
+    _cairo_array_init (&res->smasks, sizeof (cairo_pdf_resource_t));
+    _cairo_array_init (&res->patterns, sizeof (cairo_pdf_resource_t));
+    _cairo_array_init (&res->xobjects, sizeof (cairo_pdf_resource_t));
+    _cairo_array_init (&res->fonts, sizeof (cairo_pdf_font_t));
+}
+
+static void
+_cairo_pdf_group_resources_fini (cairo_pdf_group_resources_t *res)
+{
+    _cairo_array_fini (&res->alphas);
+    _cairo_array_fini (&res->smasks);
+    _cairo_array_fini (&res->patterns);
+    _cairo_array_fini (&res->xobjects);
+    _cairo_array_fini (&res->fonts);
+}
+
+static void
+_cairo_pdf_group_resources_clear (cairo_pdf_group_resources_t *res)
+{
+    int i;
+
+    for (i = 0; i < CAIRO_NUM_OPERATORS; i++)
+       res->operators[i] = FALSE;
+
+    _cairo_array_truncate (&res->alphas, 0);
+    _cairo_array_truncate (&res->smasks, 0);
+    _cairo_array_truncate (&res->patterns, 0);
+    _cairo_array_truncate (&res->xobjects, 0);
+    _cairo_array_truncate (&res->fonts, 0);
+}
+
+static void
+_cairo_pdf_surface_add_operator (cairo_pdf_surface_t *surface,
+                                cairo_operator_t     op)
+{
+    cairo_pdf_group_resources_t *res = &surface->resources;
+
+    res->operators[op] = TRUE;
+}
+
+static cairo_status_t
+_cairo_pdf_surface_add_alpha (cairo_pdf_surface_t *surface,
+                             double               alpha,
+                             int                 *index)
+{
+    int num_alphas, i;
+    double other;
+    cairo_status_t status;
+    cairo_pdf_group_resources_t *res = &surface->resources;
+
+    num_alphas = _cairo_array_num_elements (&res->alphas);
+    for (i = 0; i < num_alphas; i++) {
+       _cairo_array_copy_element (&res->alphas, i, &other);
+       if (alpha == other) {
+           *index = i;
+           return CAIRO_STATUS_SUCCESS;
+       }
+    }
+
+    status = _cairo_array_append (&res->alphas, &alpha);
+    if (unlikely (status))
+       return status;
+
+    *index = _cairo_array_num_elements (&res->alphas) - 1;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_pdf_surface_add_smask (cairo_pdf_surface_t  *surface,
+                             cairo_pdf_resource_t  smask)
+{
+    return _cairo_array_append (&(surface->resources.smasks), &smask);
+}
+
+static cairo_status_t
+_cairo_pdf_surface_add_pattern (cairo_pdf_surface_t  *surface,
+                               cairo_pdf_resource_t  pattern)
+{
+    return _cairo_array_append (&(surface->resources.patterns), &pattern);
+}
+
+static cairo_status_t
+_cairo_pdf_surface_add_xobject (cairo_pdf_surface_t  *surface,
+                               cairo_pdf_resource_t  xobject)
+{
+    return _cairo_array_append (&(surface->resources.xobjects), &xobject);
+}
+
+static cairo_status_t
+_cairo_pdf_surface_add_font (unsigned int        font_id,
+                            unsigned int        subset_id,
+                            void               *closure)
+{
+    cairo_pdf_surface_t *surface = closure;
+    cairo_pdf_font_t font;
+    int num_fonts, i;
+    cairo_status_t status;
+    cairo_pdf_group_resources_t *res = &surface->resources;
+
+    num_fonts = _cairo_array_num_elements (&res->fonts);
+    for (i = 0; i < num_fonts; i++) {
+       _cairo_array_copy_element (&res->fonts, i, &font);
+       if (font.font_id == font_id &&
+           font.subset_id == subset_id)
+           return CAIRO_STATUS_SUCCESS;
+    }
+
+    num_fonts = _cairo_array_num_elements (&surface->fonts);
+    for (i = 0; i < num_fonts; i++) {
+       _cairo_array_copy_element (&surface->fonts, i, &font);
+       if (font.font_id == font_id &&
+           font.subset_id == subset_id)
+           return _cairo_array_append (&res->fonts, &font);
+    }
+
+    font.font_id = font_id;
+    font.subset_id = subset_id;
+    font.subset_resource = _cairo_pdf_surface_new_object (surface);
+    if (font.subset_resource.id == 0)
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    status = _cairo_array_append (&surface->fonts, &font);
+    if (unlikely (status))
+       return status;
+
+    return _cairo_array_append (&res->fonts, &font);
+}
+
+static cairo_pdf_resource_t
+_cairo_pdf_surface_get_font_resource (cairo_pdf_surface_t *surface,
+                                     unsigned int         font_id,
+                                     unsigned int         subset_id)
+{
+    cairo_pdf_font_t font;
+    int num_fonts, i;
+
+    num_fonts = _cairo_array_num_elements (&surface->fonts);
+    for (i = 0; i < num_fonts; i++) {
+       _cairo_array_copy_element (&surface->fonts, i, &font);
+       if (font.font_id == font_id && font.subset_id == subset_id)
+           return font.subset_resource;
+    }
+
+    font.subset_resource.id = 0;
+    return font.subset_resource;
+}
+
+static const char *
+_cairo_operator_to_pdf_blend_mode (cairo_operator_t op)
+{
+    switch (op) {
+    /* The extend blend mode operators */
+    case CAIRO_OPERATOR_MULTIPLY:       return "Multiply";
+    case CAIRO_OPERATOR_SCREEN:         return "Screen";
+    case CAIRO_OPERATOR_OVERLAY:        return "Overlay";
+    case CAIRO_OPERATOR_DARKEN:         return "Darken";
+    case CAIRO_OPERATOR_LIGHTEN:        return "Lighten";
+    case CAIRO_OPERATOR_COLOR_DODGE:    return "ColorDodge";
+    case CAIRO_OPERATOR_COLOR_BURN:     return "ColorBurn";
+    case CAIRO_OPERATOR_HARD_LIGHT:     return "HardLight";
+    case CAIRO_OPERATOR_SOFT_LIGHT:     return "SoftLight";
+    case CAIRO_OPERATOR_DIFFERENCE:     return "Difference";
+    case CAIRO_OPERATOR_EXCLUSION:      return "Exclusion";
+    case CAIRO_OPERATOR_HSL_HUE:        return "Hue";
+    case CAIRO_OPERATOR_HSL_SATURATION: return "Saturation";
+    case CAIRO_OPERATOR_HSL_COLOR:      return "Color";
+    case CAIRO_OPERATOR_HSL_LUMINOSITY: return "Luminosity";
+
+    default:
+    /* The original Porter-Duff set */
+    case CAIRO_OPERATOR_CLEAR:
+    case CAIRO_OPERATOR_SOURCE:
+    case CAIRO_OPERATOR_OVER:
+    case CAIRO_OPERATOR_IN:
+    case CAIRO_OPERATOR_OUT:
+    case CAIRO_OPERATOR_ATOP:
+    case CAIRO_OPERATOR_DEST:
+    case CAIRO_OPERATOR_DEST_OVER:
+    case CAIRO_OPERATOR_DEST_IN:
+    case CAIRO_OPERATOR_DEST_OUT:
+    case CAIRO_OPERATOR_DEST_ATOP:
+    case CAIRO_OPERATOR_XOR:
+    case CAIRO_OPERATOR_ADD:
+    case CAIRO_OPERATOR_SATURATE:
+       return "Normal";
+    }
+}
+
+static void
+_cairo_pdf_surface_emit_group_resources (cairo_pdf_surface_t         *surface,
+                                        cairo_pdf_group_resources_t *res)
+{
+    int num_alphas, num_smasks, num_resources, i;
+    double alpha;
+    cairo_pdf_resource_t *smask, *pattern, *xobject;
+    cairo_pdf_font_t *font;
+
+    _cairo_output_stream_printf (surface->output, "<<\n");
+
+    num_alphas = _cairo_array_num_elements (&res->alphas);
+    num_smasks = _cairo_array_num_elements (&res->smasks);
+    if (num_alphas > 0 || num_smasks > 0) {
+       _cairo_output_stream_printf (surface->output,
+                                    "   /ExtGState <<\n");
+
+       for (i = 0; i < CAIRO_NUM_OPERATORS; i++) {
+           if (res->operators[i]) {
+               _cairo_output_stream_printf (surface->output,
+                                            "      /b%d << /BM /%s >>\n",
+                                            i, _cairo_operator_to_pdf_blend_mode(i));
+           }
+       }
+
+       for (i = 0; i < num_alphas; i++) {
+           _cairo_array_copy_element (&res->alphas, i, &alpha);
+           _cairo_output_stream_printf (surface->output,
+                                        "      /a%d << /CA %f /ca %f >>\n",
+                                        i, alpha, alpha);
+       }
+
+       for (i = 0; i < num_smasks; i++) {
+           smask = _cairo_array_index (&res->smasks, i);
+           _cairo_output_stream_printf (surface->output,
+                                        "      /s%d %d 0 R\n",
+                                        smask->id, smask->id);
+       }
+
+       _cairo_output_stream_printf (surface->output,
+                                    "   >>\n");
+    }
+
+    num_resources = _cairo_array_num_elements (&res->patterns);
+    if (num_resources > 0) {
+       _cairo_output_stream_printf (surface->output,
+                                    "   /Pattern <<");
+       for (i = 0; i < num_resources; i++) {
+           pattern = _cairo_array_index (&res->patterns, i);
+           _cairo_output_stream_printf (surface->output,
+                                        " /p%d %d 0 R",
+                                        pattern->id, pattern->id);
+       }
+
+       _cairo_output_stream_printf (surface->output,
+                                    " >>\n");
+    }
+
+    num_resources = _cairo_array_num_elements (&res->xobjects);
+    if (num_resources > 0) {
+       _cairo_output_stream_printf (surface->output,
+                                    "   /XObject <<");
+
+       for (i = 0; i < num_resources; i++) {
+           xobject = _cairo_array_index (&res->xobjects, i);
+           _cairo_output_stream_printf (surface->output,
+                                        " /x%d %d 0 R",
+                                        xobject->id, xobject->id);
+       }
+
+       _cairo_output_stream_printf (surface->output,
+                                    " >>\n");
+    }
+
+    num_resources = _cairo_array_num_elements (&res->fonts);
+    if (num_resources > 0) {
+       _cairo_output_stream_printf (surface->output,"   /Font <<\n");
+       for (i = 0; i < num_resources; i++) {
+           font = _cairo_array_index (&res->fonts, i);
+           _cairo_output_stream_printf (surface->output,
+                                        "      /f-%d-%d %d 0 R\n",
+                                        font->font_id,
+                                        font->subset_id,
+                                        font->subset_resource.id);
+       }
+       _cairo_output_stream_printf (surface->output, "   >>\n");
+    }
+
+    _cairo_output_stream_printf (surface->output,
+                                ">>\n");
+}
+
+static cairo_pdf_smask_group_t *
+_cairo_pdf_surface_create_smask_group (cairo_pdf_surface_t         *surface,
+                                      const cairo_rectangle_int_t  *extents)
+{
+    cairo_pdf_smask_group_t    *group;
+
+    group = calloc (1, sizeof (cairo_pdf_smask_group_t));
+    if (unlikely (group == NULL)) {
+       _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+       return NULL;
+    }
+
+    group->group_res = _cairo_pdf_surface_new_object (surface);
+    if (group->group_res.id == 0) {
+       _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+       free (group);
+       return NULL;
+    }
+    group->width = surface->width;
+    group->height = surface->height;
+    if (extents != NULL) {
+       group->extents = *extents;
+    } else {
+       group->extents.x = 0;
+       group->extents.y = 0;
+       group->extents.width = surface->width;
+       group->extents.height = surface->height;
+    }
+    group->extents = *extents;
+
+    return group;
+}
+
+static void
+_cairo_pdf_smask_group_destroy (cairo_pdf_smask_group_t *group)
+{
+    if (group->operation == PDF_FILL ||        group->operation == PDF_STROKE)
+       _cairo_path_fixed_fini (&group->path);
+    if (group->source)
+       cairo_pattern_destroy (group->source);
+    if (group->mask)
+       cairo_pattern_destroy (group->mask);
+    free (group->utf8);
+    free (group->glyphs);
+    free (group->clusters);
+    if (group->scaled_font)
+       cairo_scaled_font_destroy (group->scaled_font);
+    free (group);
+}
+
+static cairo_status_t
+_cairo_pdf_surface_add_smask_group (cairo_pdf_surface_t     *surface,
+                                   cairo_pdf_smask_group_t *group)
+{
+    return _cairo_array_append (&surface->smask_groups, &group);
+}
+
+static cairo_bool_t
+_cairo_pdf_source_surface_equal (const void *key_a, const void *key_b)
+{
+    const cairo_pdf_source_surface_entry_t *a = key_a;
+    const cairo_pdf_source_surface_entry_t *b = key_b;
+
+    if (a->interpolate != b->interpolate)
+       return FALSE;
+
+    if (a->unique_id && b->unique_id && a->unique_id_length == b->unique_id_length)
+       return (memcmp (a->unique_id, b->unique_id, a->unique_id_length) == 0);
+
+    return (a->id == b->id);
+}
+
+static void
+_cairo_pdf_source_surface_init_key (cairo_pdf_source_surface_entry_t *key)
+{
+    if (key->unique_id && key->unique_id_length > 0) {
+       key->base.hash = _cairo_hash_bytes (_CAIRO_HASH_INIT_VALUE,
+                                           key->unique_id, key->unique_id_length);
+    } else {
+       key->base.hash = key->id;
+    }
+}
+
+static cairo_int_status_t
+_get_jpx_image_info (cairo_surface_t            *source,
+                    cairo_image_info_t         *info,
+                    const unsigned char        **mime_data,
+                    unsigned long               *mime_data_length)
+{
+    cairo_surface_get_mime_data (source, CAIRO_MIME_TYPE_JP2,
+                                mime_data, mime_data_length);
+    if (*mime_data == NULL)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    return _cairo_image_info_get_jpx_info (info, *mime_data, *mime_data_length);
+}
+
+static cairo_int_status_t
+_get_jpeg_image_info (cairo_surface_t           *source,
+                     cairo_image_info_t         *info,
+                     const unsigned char       **mime_data,
+                     unsigned long              *mime_data_length)
+{
+    cairo_surface_get_mime_data (source, CAIRO_MIME_TYPE_JPEG,
+                                mime_data, mime_data_length);
+    if (*mime_data == NULL)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    return _cairo_image_info_get_jpeg_info (info, *mime_data, *mime_data_length);
+}
+
+static cairo_int_status_t
+_get_source_surface_size (cairo_surface_t         *source,
+                         int                     *width,
+                         int                     *height,
+                         cairo_rectangle_int_t   *extents)
+{
+    cairo_int_status_t status;
+    cairo_image_info_t info;
+    const unsigned char *mime_data;
+    unsigned long mime_data_length;
+
+    if (source->type == CAIRO_SURFACE_TYPE_RECORDING) {
+       if (source->backend->type == CAIRO_SURFACE_TYPE_SUBSURFACE) {
+            cairo_surface_subsurface_t *sub = (cairo_surface_subsurface_t *) source;
+
+            *extents = sub->extents;
+            *width  = extents->width;
+            *height = extents->height;
+       } else {
+           cairo_rectangle_int_t surf_extents;
+           cairo_box_t box;
+           cairo_bool_t bounded;
+
+           if (_cairo_surface_is_snapshot (source))
+               source = _cairo_surface_snapshot_get_target (source);
+
+           status = _cairo_recording_surface_get_ink_bbox ((cairo_recording_surface_t *)source,
+                                                           &box, NULL);
+           if (unlikely (status))
+               return status;
+
+           _cairo_box_round_to_rectangle (&box, extents);
+
+           bounded = _cairo_surface_get_extents (source, &surf_extents);
+           *width = surf_extents.width;
+           *height = surf_extents.height;
+       }
+
+       return CAIRO_STATUS_SUCCESS;
+    }
+
+    extents->x = 0;
+    extents->y = 0;
+
+    status = _get_jpx_image_info (source, &info, &mime_data, &mime_data_length);
+    if (status != CAIRO_INT_STATUS_UNSUPPORTED) {
+       *width = info.width;
+       *height = info.height;
+       extents->width = info.width;
+       extents->height = info.height;
+       return status;
+    }
+
+    status = _get_jpeg_image_info (source, &info, &mime_data, &mime_data_length);
+    if (status != CAIRO_INT_STATUS_UNSUPPORTED) {
+       *width = info.width;
+       *height = info.height;
+       extents->width = info.width;
+       extents->height = info.height;
+       return status;
+    }
+
+    if (! _cairo_surface_get_extents (source, extents))
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    *width = extents->width;
+    *height = extents->height;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_pdf_surface_add_source_surface (cairo_pdf_surface_t     *surface,
+                                      cairo_surface_t          *source,
+                                      cairo_filter_t            filter,
+                                      cairo_bool_t              stencil_mask,
+                                      cairo_pdf_resource_t     *surface_res,
+                                      int                      *width,
+                                      int                      *height,
+                                      cairo_rectangle_int_t    *extents)
+{
+    cairo_pdf_source_surface_t src_surface;
+    cairo_pdf_source_surface_entry_t surface_key;
+    cairo_pdf_source_surface_entry_t *surface_entry;
+    cairo_status_t status;
+    cairo_bool_t interpolate;
+    const unsigned char *unique_id;
+    unsigned long unique_id_length;
+
+    switch (filter) {
+    default:
+    case CAIRO_FILTER_GOOD:
+    case CAIRO_FILTER_BEST:
+    case CAIRO_FILTER_BILINEAR:
+       interpolate = TRUE;
+       break;
+    case CAIRO_FILTER_FAST:
+    case CAIRO_FILTER_NEAREST:
+    case CAIRO_FILTER_GAUSSIAN:
+       interpolate = FALSE;
+       break;
+    }
+
+    surface_key.id  = source->unique_id;
+    surface_key.interpolate = interpolate;
+    cairo_surface_get_mime_data (source, CAIRO_MIME_TYPE_UNIQUE_ID,
+                                (const unsigned char **) &surface_key.unique_id,
+                                &surface_key.unique_id_length);
+    _cairo_pdf_source_surface_init_key (&surface_key);
+    surface_entry = _cairo_hash_table_lookup (surface->all_surfaces, &surface_key.base);
+    if (surface_entry) {
+       *surface_res = surface_entry->surface_res;
+       *width = surface_entry->width;
+       *height = surface_entry->height;
+       *extents = surface_entry->extents;
+
+       return CAIRO_STATUS_SUCCESS;
+    }
+
+    surface_entry = malloc (sizeof (cairo_pdf_source_surface_entry_t));
+    if (surface_entry == NULL)
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    surface_entry->id = surface_key.id;
+    surface_entry->interpolate = interpolate;
+    surface_entry->stencil_mask = stencil_mask;
+    cairo_surface_get_mime_data (source, CAIRO_MIME_TYPE_UNIQUE_ID,
+                                &unique_id, &unique_id_length);
+    if (unique_id && unique_id_length > 0) {
+       surface_entry->unique_id = malloc (unique_id_length);
+       if (surface_entry->unique_id == NULL) {
+           cairo_surface_destroy (source);
+           free (surface_entry);
+           return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+       }
+
+       surface_entry->unique_id_length = unique_id_length;
+       memcpy (surface_entry->unique_id, unique_id, unique_id_length);
+    } else {
+       surface_entry->unique_id = NULL;
+       surface_entry->unique_id_length = 0;
+    }
+    _cairo_pdf_source_surface_init_key (surface_entry);
+
+    src_surface.hash_entry = surface_entry;
+    src_surface.surface = cairo_surface_reference (source);
+    surface_entry->surface_res = _cairo_pdf_surface_new_object (surface);
+    if (surface_entry->surface_res.id == 0) {
+       cairo_surface_destroy (source);
+       free (surface_entry);
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+    }
+
+    status = _get_source_surface_size (source,
+                                      &surface_entry->width,
+                                      &surface_entry->height,
+                                      &surface_entry->extents);
+
+    status = _cairo_array_append (&surface->page_surfaces, &src_surface);
+    if (unlikely (status)) {
+       cairo_surface_destroy (source);
+       free (surface_entry);
+       return status;
+    }
+
+    status = _cairo_hash_table_insert (surface->all_surfaces,
+                                      &surface_entry->base);
+
+    *surface_res = surface_entry->surface_res;
+    *width = surface_entry->width;
+    *height = surface_entry->height;
+    *extents = surface_entry->extents;
+
+    return status;
+}
+
+static cairo_status_t
+_cairo_pdf_surface_add_pdf_pattern (cairo_pdf_surface_t                *surface,
+                                   const cairo_pattern_t       *pattern,
+                                   const cairo_rectangle_int_t *extents,
+                                   cairo_pdf_resource_t        *pattern_res,
+                                   cairo_pdf_resource_t        *gstate_res)
+{
+    cairo_pdf_pattern_t pdf_pattern;
+    cairo_status_t status;
+
+    /* Solid colors are emitted into the content stream */
+    if (pattern->type == CAIRO_PATTERN_TYPE_SOLID) {
+       pattern_res->id = 0;
+       gstate_res->id = 0;
+       return CAIRO_STATUS_SUCCESS;
+    }
+
+    status = _cairo_pattern_create_copy (&pdf_pattern.pattern, pattern);
+    if (unlikely (status))
+       return status;
+
+    pdf_pattern.pattern_res = _cairo_pdf_surface_new_object (surface);
+    if (pdf_pattern.pattern_res.id == 0) {
+       cairo_pattern_destroy (pdf_pattern.pattern);
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+    }
+
+    pdf_pattern.gstate_res.id = 0;
+
+    /* gradient patterns require an smask object to implement transparency */
+    if (pattern->type == CAIRO_PATTERN_TYPE_LINEAR ||
+       pattern->type == CAIRO_PATTERN_TYPE_RADIAL ||
+       pattern->type == CAIRO_PATTERN_TYPE_MESH)
+    {
+       double min_alpha;
+
+       _cairo_pattern_alpha_range (pattern, &min_alpha, NULL);
+       if (! CAIRO_ALPHA_IS_OPAQUE (min_alpha)) {
+            pdf_pattern.gstate_res = _cairo_pdf_surface_new_object (surface);
+           if (pdf_pattern.gstate_res.id == 0) {
+               cairo_pattern_destroy (pdf_pattern.pattern);
+               return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+           }
+        }
+    }
+
+    pdf_pattern.width  = surface->width;
+    pdf_pattern.height = surface->height;
+    if (extents != NULL) {
+       pdf_pattern.extents = *extents;
+    } else {
+       pdf_pattern.extents.x = 0;
+       pdf_pattern.extents.y = 0;
+       pdf_pattern.extents.width  = surface->width;
+       pdf_pattern.extents.height = surface->height;
+    }
+
+    *pattern_res = pdf_pattern.pattern_res;
+    *gstate_res = pdf_pattern.gstate_res;
+
+    status = _cairo_array_append (&surface->page_patterns, &pdf_pattern);
+    if (unlikely (status)) {
+       cairo_pattern_destroy (pdf_pattern.pattern);
+       return status;
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+/* Get BBox in PDF coordinates from extents in cairo coordinates */
+static void
+_get_bbox_from_extents (double                       surface_height,
+                      const cairo_rectangle_int_t *extents,
+                      cairo_box_double_t          *bbox)
+{
+    bbox->p1.x = extents->x;
+    bbox->p1.y = surface_height - (extents->y + extents->height);
+    bbox->p2.x = extents->x + extents->width;
+    bbox->p2.y = surface_height - extents->y;
+}
+
+static cairo_status_t
+_cairo_pdf_surface_open_stream (cairo_pdf_surface_t    *surface,
+                               cairo_pdf_resource_t    *resource,
+                               cairo_bool_t             compressed,
+                               const char              *fmt,
+                               ...)
+{
+    va_list ap;
+    cairo_pdf_resource_t self, length;
+    cairo_output_stream_t *output = NULL;
+
+    if (resource) {
+       self = *resource;
+       _cairo_pdf_surface_update_object (surface, self);
+    } else {
+       self = _cairo_pdf_surface_new_object (surface);
+       if (self.id == 0)
+           return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+    }
+
+    length = _cairo_pdf_surface_new_object (surface);
+    if (length.id == 0)
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    if (compressed) {
+       output = _cairo_deflate_stream_create (surface->output);
+       if (_cairo_output_stream_get_status (output))
+           return _cairo_output_stream_destroy (output);
+    }
+
+    surface->pdf_stream.active = TRUE;
+    surface->pdf_stream.self = self;
+    surface->pdf_stream.length = length;
+    surface->pdf_stream.compressed = compressed;
+    surface->current_pattern_is_solid_color = FALSE;
+    surface->current_operator = CAIRO_OPERATOR_OVER;
+    _cairo_pdf_operators_reset (&surface->pdf_operators);
+
+    _cairo_output_stream_printf (surface->output,
+                                "%d 0 obj\n"
+                                "<< /Length %d 0 R\n",
+                                surface->pdf_stream.self.id,
+                                surface->pdf_stream.length.id);
+    if (compressed)
+       _cairo_output_stream_printf (surface->output,
+                                    "   /Filter /FlateDecode\n");
+
+    if (fmt != NULL) {
+       va_start (ap, fmt);
+       _cairo_output_stream_vprintf (surface->output, fmt, ap);
+       va_end (ap);
+    }
+
+    _cairo_output_stream_printf (surface->output,
+                                ">>\n"
+                                "stream\n");
+
+    surface->pdf_stream.start_offset = _cairo_output_stream_get_position (surface->output);
+
+    if (compressed) {
+       assert (surface->pdf_stream.old_output == NULL);
+        surface->pdf_stream.old_output = surface->output;
+        surface->output = output;
+       _cairo_pdf_operators_set_stream (&surface->pdf_operators, surface->output);
+    }
+
+    return _cairo_output_stream_get_status (surface->output);
+}
+
+static cairo_status_t
+_cairo_pdf_surface_close_stream (cairo_pdf_surface_t *surface)
+{
+    cairo_status_t status;
+    long length;
+
+    if (! surface->pdf_stream.active)
+       return CAIRO_STATUS_SUCCESS;
+
+    status = _cairo_pdf_operators_flush (&surface->pdf_operators);
+
+    if (surface->pdf_stream.compressed) {
+       cairo_status_t status2;
+
+       status2 = _cairo_output_stream_destroy (surface->output);
+       if (likely (status == CAIRO_STATUS_SUCCESS))
+           status = status2;
+
+       surface->output = surface->pdf_stream.old_output;
+       _cairo_pdf_operators_set_stream (&surface->pdf_operators, surface->output);
+       surface->pdf_stream.old_output = NULL;
+    }
+
+    length = _cairo_output_stream_get_position (surface->output) -
+       surface->pdf_stream.start_offset;
+    _cairo_output_stream_printf (surface->output,
+                                "\n"
+                                "endstream\n"
+                                "endobj\n");
+
+    _cairo_pdf_surface_update_object (surface,
+                                     surface->pdf_stream.length);
+    _cairo_output_stream_printf (surface->output,
+                                "%d 0 obj\n"
+                                "   %ld\n"
+                                "endobj\n",
+                                surface->pdf_stream.length.id,
+                                length);
+
+    surface->pdf_stream.active = FALSE;
+
+    if (likely (status == CAIRO_STATUS_SUCCESS))
+       status = _cairo_output_stream_get_status (surface->output);
+
+    return status;
+}
+
+static void
+_cairo_pdf_surface_write_memory_stream (cairo_pdf_surface_t         *surface,
+                                       cairo_output_stream_t       *mem_stream,
+                                       cairo_pdf_resource_t         resource,
+                                       cairo_pdf_group_resources_t *resources,
+                                       cairo_bool_t                 is_knockout_group,
+                                       const cairo_box_double_t    *bbox)
+{
+    _cairo_pdf_surface_update_object (surface, resource);
+
+    _cairo_output_stream_printf (surface->output,
+                                "%d 0 obj\n"
+                                "<< /Type /XObject\n"
+                                "   /Length %d\n",
+                                resource.id,
+                                _cairo_memory_stream_length (mem_stream));
+
+    if (surface->compress_content) {
+       _cairo_output_stream_printf (surface->output,
+                                    "   /Filter /FlateDecode\n");
+    }
+
+    _cairo_output_stream_printf (surface->output,
+                                "   /Subtype /Form\n"
+                                "   /BBox [ %f %f %f %f ]\n"
+                                "   /Group <<\n"
+                                "      /Type /Group\n"
+                                "      /S /Transparency\n"
+                                "      /CS /DeviceRGB\n",
+                                bbox->p1.x, bbox->p1.y, bbox->p2.x, bbox->p2.y);
+
+    if (is_knockout_group)
+        _cairo_output_stream_printf (surface->output,
+                                     "      /K true\n");
+
+    _cairo_output_stream_printf (surface->output,
+                                "   >>\n"
+                                "   /Resources\n");
+    _cairo_pdf_surface_emit_group_resources (surface, resources);
+    _cairo_output_stream_printf (surface->output,
+                                ">>\n"
+                                "stream\n");
+    _cairo_memory_stream_copy (mem_stream, surface->output);
+    _cairo_output_stream_printf (surface->output,
+                                "endstream\n"
+                                "endobj\n");
+}
+
+static cairo_status_t
+_cairo_pdf_surface_open_group (cairo_pdf_surface_t         *surface,
+                              const cairo_box_double_t    *bbox,
+                              cairo_pdf_resource_t        *resource)
+{
+    cairo_status_t status;
+
+    assert (surface->pdf_stream.active == FALSE);
+    assert (surface->group_stream.active == FALSE);
+
+    surface->group_stream.active = TRUE;
+    surface->current_pattern_is_solid_color = FALSE;
+    surface->current_operator = CAIRO_OPERATOR_OVER;
+    _cairo_pdf_operators_reset (&surface->pdf_operators);
+
+    surface->group_stream.mem_stream = _cairo_memory_stream_create ();
+
+    if (surface->compress_content) {
+       surface->group_stream.stream =
+           _cairo_deflate_stream_create (surface->group_stream.mem_stream);
+    } else {
+       surface->group_stream.stream = surface->group_stream.mem_stream;
+    }
+    status = _cairo_output_stream_get_status (surface->group_stream.stream);
+
+    surface->group_stream.old_output = surface->output;
+    surface->output = surface->group_stream.stream;
+    _cairo_pdf_operators_set_stream (&surface->pdf_operators, surface->output);
+    _cairo_pdf_group_resources_clear (&surface->resources);
+
+    if (resource) {
+       surface->group_stream.resource = *resource;
+    } else {
+       surface->group_stream.resource = _cairo_pdf_surface_new_object (surface);
+       if (surface->group_stream.resource.id == 0)
+           return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+    }
+    surface->group_stream.is_knockout = FALSE;
+    surface->group_stream.bbox = *bbox;
+
+    return status;
+}
+
+static cairo_status_t
+_cairo_pdf_surface_open_knockout_group (cairo_pdf_surface_t         *surface,
+                                       const cairo_box_double_t    *bbox)
+{
+    cairo_status_t status;
+
+    status = _cairo_pdf_surface_open_group (surface, bbox, NULL);
+    if (unlikely (status))
+       return status;
+
+    surface->group_stream.is_knockout = TRUE;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_pdf_surface_close_group (cairo_pdf_surface_t *surface,
+                               cairo_pdf_resource_t *group)
+{
+    cairo_status_t status = CAIRO_STATUS_SUCCESS, status2;
+
+    assert (surface->pdf_stream.active == FALSE);
+    assert (surface->group_stream.active == TRUE);
+
+    status = _cairo_pdf_operators_flush (&surface->pdf_operators);
+    if (unlikely (status))
+       return status;
+
+    if (surface->compress_content) {
+       status = _cairo_output_stream_destroy (surface->group_stream.stream);
+       surface->group_stream.stream = NULL;
+
+       _cairo_output_stream_printf (surface->group_stream.mem_stream,
+                                    "\n");
+    }
+    surface->output = surface->group_stream.old_output;
+    _cairo_pdf_operators_set_stream (&surface->pdf_operators, surface->output);
+    surface->group_stream.active = FALSE;
+    _cairo_pdf_surface_write_memory_stream (surface,
+                                           surface->group_stream.mem_stream,
+                                           surface->group_stream.resource,
+                                           &surface->resources,
+                                           surface->group_stream.is_knockout,
+                                           &surface->group_stream.bbox);
+    if (group)
+       *group = surface->group_stream.resource;
+
+    status2 = _cairo_output_stream_destroy (surface->group_stream.mem_stream);
+    if (status == CAIRO_STATUS_SUCCESS)
+       status = status2;
+
+    surface->group_stream.mem_stream = NULL;
+    surface->group_stream.stream = NULL;
+
+    return status;
+}
+
+static cairo_status_t
+_cairo_pdf_surface_open_content_stream (cairo_pdf_surface_t       *surface,
+                                       const cairo_box_double_t  *bbox,
+                                       cairo_pdf_resource_t      *resource,
+                                       cairo_bool_t               is_form)
+{
+    cairo_status_t status;
+
+    assert (surface->pdf_stream.active == FALSE);
+    assert (surface->group_stream.active == FALSE);
+
+    surface->content_resources = _cairo_pdf_surface_new_object (surface);
+    if (surface->content_resources.id == 0)
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    if (is_form) {
+       assert (bbox != NULL);
+
+       status =
+           _cairo_pdf_surface_open_stream (surface,
+                                           resource,
+                                           surface->compress_content,
+                                           "   /Type /XObject\n"
+                                           "   /Subtype /Form\n"
+                                           "   /BBox [ %f %f %f %f ]\n"
+                                           "   /Group <<\n"
+                                           "      /Type /Group\n"
+                                           "      /S /Transparency\n"
+                                           "      /CS /DeviceRGB\n"
+                                           "   >>\n"
+                                           "   /Resources %d 0 R\n",
+                                           bbox->p1.x,
+                                           bbox->p1.y,
+                                           bbox->p2.x,
+                                           bbox->p2.y,
+                                           surface->content_resources.id);
+    } else {
+       status =
+           _cairo_pdf_surface_open_stream (surface,
+                                           resource,
+                                           surface->compress_content,
+                                           NULL);
+    }
+    if (unlikely (status))
+       return status;
+
+    surface->content = surface->pdf_stream.self;
+
+    _cairo_output_stream_printf (surface->output, "q\n");
+
+    return _cairo_output_stream_get_status (surface->output);
+}
+
+static cairo_status_t
+_cairo_pdf_surface_close_content_stream (cairo_pdf_surface_t *surface)
+{
+    cairo_status_t status;
+
+    assert (surface->pdf_stream.active == TRUE);
+    assert (surface->group_stream.active == FALSE);
+
+    status = _cairo_pdf_operators_flush (&surface->pdf_operators);
+    if (unlikely (status))
+       return status;
+
+    _cairo_output_stream_printf (surface->output, "Q\n");
+    status = _cairo_pdf_surface_close_stream (surface);
+    if (unlikely (status))
+       return status;
+
+    _cairo_pdf_surface_update_object (surface, surface->content_resources);
+    _cairo_output_stream_printf (surface->output,
+                                "%d 0 obj\n",
+                                surface->content_resources.id);
+    _cairo_pdf_surface_emit_group_resources (surface, &surface->resources);
+    _cairo_output_stream_printf (surface->output,
+                                "endobj\n");
+
+    return _cairo_output_stream_get_status (surface->output);
+}
+
+static void
+_cairo_pdf_source_surface_entry_pluck (void *entry, void *closure)
+{
+    cairo_pdf_source_surface_entry_t *surface_entry = entry;
+    cairo_hash_table_t *patterns = closure;
+
+    _cairo_hash_table_remove (patterns, &surface_entry->base);
+    free (surface_entry->unique_id);
+
+    free (surface_entry);
+}
+
+static cairo_status_t
+_cairo_pdf_surface_finish (void *abstract_surface)
+{
+    cairo_pdf_surface_t *surface = abstract_surface;
+    long offset;
+    cairo_pdf_resource_t info, catalog;
+    cairo_status_t status, status2;
+
+    status = surface->base.status;
+    if (status == CAIRO_STATUS_SUCCESS)
+       status = _cairo_pdf_surface_emit_font_subsets (surface);
+
+    _cairo_pdf_surface_write_pages (surface);
+
+    info = _cairo_pdf_surface_write_info (surface);
+    if (info.id == 0 && status == CAIRO_STATUS_SUCCESS)
+       status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    catalog = _cairo_pdf_surface_write_catalog (surface);
+    if (catalog.id == 0 && status == CAIRO_STATUS_SUCCESS)
+       status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    offset = _cairo_pdf_surface_write_xref (surface);
+
+    _cairo_output_stream_printf (surface->output,
+                                "trailer\n"
+                                "<< /Size %d\n"
+                                "   /Root %d 0 R\n"
+                                "   /Info %d 0 R\n"
+                                ">>\n",
+                                surface->next_available_resource.id,
+                                catalog.id,
+                                info.id);
+
+    _cairo_output_stream_printf (surface->output,
+                                "startxref\n"
+                                "%ld\n"
+                                "%%%%EOF\n",
+                                offset);
+
+    /* pdf_operators has already been flushed when the last stream was
+     * closed so we should never be writing anything here - however,
+     * the stream may itself be in an error state. */
+    status2 = _cairo_pdf_operators_fini (&surface->pdf_operators);
+    if (status == CAIRO_STATUS_SUCCESS)
+       status = status2;
+
+    /* close any active streams still open due to fatal errors */
+    status2 = _cairo_pdf_surface_close_stream (surface);
+    if (status == CAIRO_STATUS_SUCCESS)
+       status = status2;
+
+    if (surface->group_stream.stream != NULL) {
+       status2 = _cairo_output_stream_destroy (surface->group_stream.stream);
+       if (status == CAIRO_STATUS_SUCCESS)
+           status = status2;
+    }
+    if (surface->group_stream.mem_stream != NULL) {
+       status2 = _cairo_output_stream_destroy (surface->group_stream.mem_stream);
+       if (status == CAIRO_STATUS_SUCCESS)
+           status = status2;
+    }
+    if (surface->pdf_stream.active)
+       surface->output = surface->pdf_stream.old_output;
+    if (surface->group_stream.active)
+       surface->output = surface->group_stream.old_output;
+
+    /* and finish the pdf surface */
+    status2 = _cairo_output_stream_destroy (surface->output);
+    if (status == CAIRO_STATUS_SUCCESS)
+       status = status2;
+
+    _cairo_pdf_surface_clear (surface);
+    _cairo_pdf_group_resources_fini (&surface->resources);
+
+    _cairo_array_fini (&surface->objects);
+    _cairo_array_fini (&surface->pages);
+    _cairo_array_fini (&surface->rgb_linear_functions);
+    _cairo_array_fini (&surface->alpha_linear_functions);
+    _cairo_array_fini (&surface->page_patterns);
+    _cairo_array_fini (&surface->page_surfaces);
+    _cairo_hash_table_foreach (surface->all_surfaces,
+                              _cairo_pdf_source_surface_entry_pluck,
+                              surface->all_surfaces);
+    _cairo_hash_table_destroy (surface->all_surfaces);
+    _cairo_array_fini (&surface->smask_groups);
+    _cairo_array_fini (&surface->fonts);
+    _cairo_array_fini (&surface->knockout_group);
+
+    if (surface->font_subsets) {
+       _cairo_scaled_font_subsets_destroy (surface->font_subsets);
+       surface->font_subsets = NULL;
+    }
+
+    _cairo_surface_clipper_reset (&surface->clipper);
+
+    return status;
+}
+
+static cairo_int_status_t
+_cairo_pdf_surface_start_page (void *abstract_surface)
+{
+    cairo_pdf_surface_t *surface = abstract_surface;
+
+    /* Document header */
+    if (! surface->header_emitted) {
+       const char *version;
+
+       switch (surface->pdf_version) {
+       case CAIRO_PDF_VERSION_1_4:
+           version = "1.4";
+           break;
+       default:
+       case CAIRO_PDF_VERSION_1_5:
+           version = "1.5";
+           break;
+       }
+
+       _cairo_output_stream_printf (surface->output,
+                                    "%%PDF-%s\n", version);
+       _cairo_output_stream_printf (surface->output,
+                                    "%%%c%c%c%c\n", 181, 237, 174, 251);
+       surface->header_emitted = TRUE;
+    }
+
+    _cairo_pdf_group_resources_clear (&surface->resources);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_pdf_surface_has_fallback_images (void           *abstract_surface,
+                                       cairo_bool_t     has_fallbacks)
+{
+    cairo_status_t status;
+    cairo_pdf_surface_t *surface = abstract_surface;
+    cairo_box_double_t bbox;
+
+    surface->has_fallback_images = has_fallbacks;
+    bbox.p1.x = 0;
+    bbox.p1.y = 0;
+    bbox.p2.x = surface->width;
+    bbox.p2.y = surface->height;
+    status = _cairo_pdf_surface_open_content_stream (surface, &bbox, NULL, has_fallbacks);
+    if (unlikely (status))
+       return status;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_bool_t
+_cairo_pdf_surface_supports_fine_grained_fallbacks (void *abstract_surface)
+{
+    return TRUE;
+}
+
+/* Emit alpha channel from the image into the given data, providing
+ * an id that can be used to reference the resulting SMask object.
+ *
+ * In the case that the alpha channel happens to be all opaque, then
+ * no SMask object will be emitted and *id_ret will be set to 0.
+ *
+ * When stencil_mask is TRUE, stream_res is an an input specifying the
+ * resource to use. When stencil_mask is FALSE, a new resource will be
+ * created and returned in stream_res.
+ */
+static cairo_status_t
+_cairo_pdf_surface_emit_smask (cairo_pdf_surface_t     *surface,
+                              cairo_image_surface_t    *image,
+                              cairo_bool_t              stencil_mask,
+                              const char               *interpolate,
+                              cairo_pdf_resource_t     *stream_res)
+{
+    cairo_status_t status = CAIRO_STATUS_SUCCESS;
+    char *alpha;
+    unsigned long alpha_size;
+    uint32_t *pixel32;
+    uint8_t *pixel8;
+    int i, x, y, bit, a;
+    cairo_image_transparency_t transparency;
+
+    /* This is the only image format we support, which simplifies things. */
+    assert (image->format == CAIRO_FORMAT_ARGB32 ||
+           image->format == CAIRO_FORMAT_A8 ||
+           image->format == CAIRO_FORMAT_A1 );
+
+    transparency = _cairo_image_analyze_transparency (image);
+    if (stencil_mask) {
+       assert (transparency == CAIRO_IMAGE_IS_OPAQUE ||
+               transparency == CAIRO_IMAGE_HAS_BILEVEL_ALPHA);
+    } else {
+       if (transparency == CAIRO_IMAGE_IS_OPAQUE)
+           return status;
+    }
+
+    if (transparency == CAIRO_IMAGE_HAS_BILEVEL_ALPHA) {
+       alpha_size = (image->width + 7) / 8 * image->height;
+       alpha = _cairo_malloc_ab ((image->width+7) / 8, image->height);
+    } else {
+       alpha_size = image->height * image->width;
+       alpha = _cairo_malloc_ab (image->height, image->width);
+    }
+
+    if (unlikely (alpha == NULL)) {
+       status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+       goto CLEANUP;
+    }
+
+    i = 0;
+    for (y = 0; y < image->height; y++) {
+       if (image->format == CAIRO_FORMAT_A1) {
+           pixel8 = (uint8_t *) (image->data + y * image->stride);
+
+           for (x = 0; x < (image->width + 7) / 8; x++, pixel8++) {
+               a = *pixel8;
+               a = CAIRO_BITSWAP8_IF_LITTLE_ENDIAN (a);
+               alpha[i++] = a;
+           }
+       } else {
+           pixel8 = (uint8_t *) (image->data + y * image->stride);
+           pixel32 = (uint32_t *) (image->data + y * image->stride);
+           bit = 7;
+           for (x = 0; x < image->width; x++) {
+               if (image->format == CAIRO_FORMAT_ARGB32) {
+                   a = (*pixel32 & 0xff000000) >> 24;
+                   pixel32++;
+               } else {
+                   a = *pixel8;
+                   pixel8++;
+               }
+
+               if (transparency == CAIRO_IMAGE_HAS_ALPHA) {
+                   alpha[i++] = a;
+               } else { /* transparency == CAIRO_IMAGE_HAS_BILEVEL_ALPHA or CAIRO_IMAGE_IS_OPAQUE */
+                   if (bit == 7)
+                       alpha[i] = 0;
+                   if (a != 0)
+                       alpha[i] |= (1 << bit);
+                   bit--;
+                   if (bit < 0) {
+                       bit = 7;
+                       i++;
+                   }
+               }
+           }
+           if (bit != 7)
+               i++;
+       }
+    }
+
+    if (stencil_mask) {
+       status = _cairo_pdf_surface_open_stream (surface,
+                                                stream_res,
+                                                TRUE,
+                                                "   /Type /XObject\n"
+                                                "   /Subtype /Image\n"
+                                                "   /ImageMask true\n"
+                                                "   /Width %d\n"
+                                                "   /Height %d\n"
+                                                "   /Interpolate %s\n"
+                                                "   /BitsPerComponent 1\n"
+                                                "   /Decode [1 0]\n",
+                                                image->width, image->height, interpolate);
+    } else {
+       stream_res->id = 0;
+       status = _cairo_pdf_surface_open_stream (surface,
+                                                NULL,
+                                                TRUE,
+                                                "   /Type /XObject\n"
+                                                "   /Subtype /Image\n"
+                                                "   /Width %d\n"
+                                                "   /Height %d\n"
+                                                "   /ColorSpace /DeviceGray\n"
+                                                "   /Interpolate %s\n"
+                                                "   /BitsPerComponent %d\n",
+                                                image->width, image->height, interpolate,
+                                                transparency == CAIRO_IMAGE_HAS_ALPHA ? 8 : 1);
+    }
+    if (unlikely (status))
+       goto CLEANUP_ALPHA;
+
+    if (!stencil_mask)
+       *stream_res = surface->pdf_stream.self;
+
+    _cairo_output_stream_write (surface->output, alpha, alpha_size);
+    status = _cairo_pdf_surface_close_stream (surface);
+
+ CLEANUP_ALPHA:
+    free (alpha);
+ CLEANUP:
+    return status;
+}
+
+/* Emit image data into the given surface, providing a resource that
+ * can be used to reference the data in image_ret. */
+static cairo_status_t
+_cairo_pdf_surface_emit_image (cairo_pdf_surface_t     *surface,
+                               cairo_image_surface_t   *image,
+                               cairo_pdf_resource_t    *image_res,
+                              cairo_filter_t           filter,
+                              cairo_bool_t             stencil_mask)
+{
+    cairo_status_t status = CAIRO_STATUS_SUCCESS;
+    char *data;
+    unsigned long data_size;
+    uint32_t *pixel;
+    int i, x, y, bit;
+    cairo_pdf_resource_t smask = {0}; /* squelch bogus compiler warning */
+    cairo_bool_t need_smask;
+    const char *interpolate = "true";
+    cairo_image_color_t color;
+
+    /* These are the only image formats we currently support, (which
+     * makes things a lot simpler here). This is enforced through
+     * _cairo_pdf_surface_analyze_operation which only accept source surfaces of
+     * CONTENT_COLOR or CONTENT_COLOR_ALPHA.
+     */
+    assert (image->format == CAIRO_FORMAT_RGB24 ||
+           image->format == CAIRO_FORMAT_ARGB32 ||
+           image->format == CAIRO_FORMAT_A8 ||
+           image->format == CAIRO_FORMAT_A1);
+
+    switch (filter) {
+    case CAIRO_FILTER_GOOD:
+    case CAIRO_FILTER_BEST:
+    case CAIRO_FILTER_BILINEAR:
+       interpolate = "true";
+       break;
+    case CAIRO_FILTER_FAST:
+    case CAIRO_FILTER_NEAREST:
+    case CAIRO_FILTER_GAUSSIAN:
+       interpolate = "false";
+       break;
+    }
+
+    if (stencil_mask)
+       return _cairo_pdf_surface_emit_smask (surface, image, stencil_mask, interpolate, image_res);
+
+    color = _cairo_image_analyze_color (image);
+    switch (color) {
+       case CAIRO_IMAGE_IS_COLOR:
+       case CAIRO_IMAGE_UNKNOWN_COLOR:
+           data_size = image->height * image->width * 3;
+           data = _cairo_malloc_abc (image->width, image->height, 3);
+           break;
+
+       case CAIRO_IMAGE_IS_GRAYSCALE:
+           data_size = image->height * image->width;
+           data = _cairo_malloc_ab (image->width, image->height);
+           break;
+       case CAIRO_IMAGE_IS_MONOCHROME:
+           data_size = (image->width + 7) / 8 * image->height;
+           data = _cairo_malloc_ab ((image->width+7) / 8, image->height);
+           break;
+    }
+    if (unlikely (data == NULL)) {
+       status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+       goto CLEANUP;
+    }
+
+    i = 0;
+    for (y = 0; y < image->height; y++) {
+       pixel = (uint32_t *) (image->data + y * image->stride);
+
+       bit = 7;
+       for (x = 0; x < image->width; x++, pixel++) {
+           int r, g, b;
+
+           /* XXX: We're un-premultiplying alpha here. My reading of the PDF
+            * specification suggests that we should be able to avoid having
+            * to do this by filling in the SMask's Matte dictionary
+            * appropriately, but my attempts to do that so far have
+            * failed. */
+           if (image->format == CAIRO_FORMAT_ARGB32) {
+               uint8_t a;
+               a = (*pixel & 0xff000000) >> 24;
+               if (a == 0) {
+                   r = g = b = 0;
+               } else {
+                   r = (((*pixel & 0xff0000) >> 16) * 255 + a / 2) / a;
+                   g = (((*pixel & 0x00ff00) >>  8) * 255 + a / 2) / a;
+                   b = (((*pixel & 0x0000ff) >>  0) * 255 + a / 2) / a;
+               }
+           } else if (image->format == CAIRO_FORMAT_RGB24) {
+               r = (*pixel & 0x00ff0000) >> 16;
+               g = (*pixel & 0x0000ff00) >>  8;
+               b = (*pixel & 0x000000ff) >>  0;
+           } else {
+               r = g = b = 0;
+           }
+
+           switch (color) {
+               case CAIRO_IMAGE_IS_COLOR:
+               case CAIRO_IMAGE_UNKNOWN_COLOR:
+                   data[i++] = r;
+                   data[i++] = g;
+                   data[i++] = b;
+                   break;
+
+               case CAIRO_IMAGE_IS_GRAYSCALE:
+                   data[i++] = r;
+                   break;
+
+               case CAIRO_IMAGE_IS_MONOCHROME:
+                   if (bit == 7)
+                       data[i] = 0;
+                   if (r != 0)
+                       data[i] |= (1 << bit);
+                   bit--;
+                   if (bit < 0) {
+                       bit = 7;
+                       i++;
+                   }
+                   break;
+           }
+       }
+       if (bit != 7)
+           i++;
+    }
+
+    need_smask = FALSE;
+    if (image->format == CAIRO_FORMAT_ARGB32 ||
+       image->format == CAIRO_FORMAT_A8 ||
+       image->format == CAIRO_FORMAT_A1) {
+       status = _cairo_pdf_surface_emit_smask (surface, image, FALSE, interpolate, &smask);
+       if (unlikely (status))
+           goto CLEANUP_RGB;
+
+       if (smask.id)
+           need_smask = TRUE;
+    }
+
+#define IMAGE_DICTIONARY       "   /Type /XObject\n"           \
+                               "   /Subtype /Image\n"  \
+                               "   /Width %d\n"                \
+                               "   /Height %d\n"               \
+                               "   /ColorSpace %s\n"   \
+                               "   /Interpolate %s\n" \
+                               "   /BitsPerComponent %d\n"
+
+    if (need_smask)
+       status = _cairo_pdf_surface_open_stream (surface,
+                                                image_res,
+                                                TRUE,
+                                                IMAGE_DICTIONARY
+                                                "   /SMask %d 0 R\n",
+                                                image->width, image->height,
+                                                color == CAIRO_IMAGE_IS_COLOR ? "/DeviceRGB" : "/DeviceGray",
+                                                interpolate,
+                                                color == CAIRO_IMAGE_IS_MONOCHROME? 1 : 8,
+                                                smask.id);
+    else
+       status = _cairo_pdf_surface_open_stream (surface,
+                                                image_res,
+                                                TRUE,
+                                                IMAGE_DICTIONARY,
+                                                image->width, image->height,
+                                                color == CAIRO_IMAGE_IS_COLOR ? "/DeviceRGB" : "/DeviceGray",
+                                                interpolate,
+                                                color == CAIRO_IMAGE_IS_MONOCHROME? 1 : 8);
+    if (unlikely (status))
+       goto CLEANUP_RGB;
+
+#undef IMAGE_DICTIONARY
+
+    _cairo_output_stream_write (surface->output, data, data_size);
+    status = _cairo_pdf_surface_close_stream (surface);
+
+CLEANUP_RGB:
+    free (data);
+CLEANUP:
+    return status;
+}
+
+static cairo_int_status_t
+_cairo_pdf_surface_emit_jpx_image (cairo_pdf_surface_t   *surface,
+                                  cairo_surface_t       *source,
+                                  cairo_pdf_resource_t   res)
+{
+    cairo_status_t status;
+    const unsigned char *mime_data;
+    unsigned long mime_data_length;
+    cairo_image_info_t info;
+
+    if (surface->pdf_version < CAIRO_PDF_VERSION_1_5)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    cairo_surface_get_mime_data (source, CAIRO_MIME_TYPE_JP2,
+                                &mime_data, &mime_data_length);
+    if (mime_data == NULL)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    status = _cairo_image_info_get_jpx_info (&info, mime_data, mime_data_length);
+    if (status)
+       return status;
+
+    status = _cairo_pdf_surface_open_stream (surface,
+                                            &res,
+                                            FALSE,
+                                            "   /Type /XObject\n"
+                                            "   /Subtype /Image\n"
+                                            "   /Width %d\n"
+                                            "   /Height %d\n"
+                                            "   /Filter /JPXDecode\n",
+                                            info.width,
+                                            info.height);
+    if (status)
+       return status;
+
+    _cairo_output_stream_write (surface->output, mime_data, mime_data_length);
+    status = _cairo_pdf_surface_close_stream (surface);
+
+    return status;
+}
+
+static cairo_int_status_t
+_cairo_pdf_surface_emit_jpeg_image (cairo_pdf_surface_t   *surface,
+                                   cairo_surface_t       *source,
+                                   cairo_pdf_resource_t   res)
+{
+    cairo_status_t status;
+    const unsigned char *mime_data;
+    unsigned long mime_data_length;
+    cairo_image_info_t info;
+
+    cairo_surface_get_mime_data (source, CAIRO_MIME_TYPE_JPEG,
+                                &mime_data, &mime_data_length);
+    if (unlikely (source->status))
+       return source->status;
+    if (mime_data == NULL)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    status = _cairo_image_info_get_jpeg_info (&info, mime_data, mime_data_length);
+    if (unlikely (status))
+       return status;
+
+    if (info.num_components != 1 && info.num_components != 3)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    status = _cairo_pdf_surface_open_stream (surface,
+                                            &res,
+                                            FALSE,
+                                            "   /Type /XObject\n"
+                                            "   /Subtype /Image\n"
+                                            "   /Width %d\n"
+                                            "   /Height %d\n"
+                                            "   /ColorSpace %s\n"
+                                            "   /BitsPerComponent %d\n"
+                                            "   /Filter /DCTDecode\n",
+                                            info.width,
+                                            info.height,
+                                            info.num_components == 1 ? "/DeviceGray" : "/DeviceRGB",
+                                            info.bits_per_component);
+    if (unlikely (status))
+       return status;
+
+    _cairo_output_stream_write (surface->output, mime_data, mime_data_length);
+    status = _cairo_pdf_surface_close_stream (surface);
+
+    return status;
+}
+
+static cairo_status_t
+_cairo_pdf_surface_emit_image_surface (cairo_pdf_surface_t     *surface,
+                                      cairo_surface_t         *source,
+                                      cairo_pdf_resource_t     resource,
+                                      cairo_bool_t             interpolate,
+                                      cairo_bool_t             stencil_mask)
+{
+    cairo_image_surface_t *image;
+    void *image_extra;
+    cairo_int_status_t status;
+
+    if (!stencil_mask) {
+       status = _cairo_pdf_surface_emit_jpx_image (surface, source, resource);
+       if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+           return status;
+
+       status = _cairo_pdf_surface_emit_jpeg_image (surface, source, resource);
+       if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+           return status;
+    }
+
+    status = _cairo_surface_acquire_source_image (source, &image, &image_extra);
+    if (unlikely (status))
+       return status;
+
+    status = _cairo_pdf_surface_emit_image (surface, image,
+                                           &resource, interpolate, stencil_mask);
+    if (unlikely (status))
+       goto BAIL;
+
+BAIL:
+    _cairo_surface_release_source_image (source, image, image_extra);
+
+    return status;
+}
+
+static cairo_status_t
+_cairo_pdf_surface_emit_padded_image_surface (cairo_pdf_surface_t     *surface,
+                                             cairo_pdf_pattern_t     *pdf_pattern,
+                                             cairo_pdf_resource_t    *resource,
+                                             int                     *width,
+                                             int                     *height,
+                                             int                     *origin_x,
+                                             int                     *origin_y)
+{
+    cairo_image_surface_t *image;
+    cairo_surface_t *pad_image;
+    void *image_extra;
+    cairo_int_status_t status;
+    cairo_surface_pattern_t *pattern = (cairo_surface_pattern_t *) pdf_pattern->pattern;
+    int x = 0;
+    int y = 0;
+    cairo_bool_t interpolate;
+
+    status = _cairo_surface_acquire_source_image (pattern->surface, &image, &image_extra);
+    if (unlikely (status))
+        return status;
+
+    pad_image = &image->base;
+    if (pattern->base.extend == CAIRO_EXTEND_PAD) {
+        cairo_box_t box;
+        cairo_rectangle_int_t rect;
+        cairo_surface_pattern_t pad_pattern;
+
+        /* get the operation extents in pattern space */
+        _cairo_box_from_rectangle (&box, &pdf_pattern->extents);
+        _cairo_matrix_transform_bounding_box_fixed (&pattern->base.matrix, &box, NULL);
+        _cairo_box_round_to_rectangle (&box, &rect);
+        x = -rect.x;
+        y = -rect.y;
+
+        pad_image = _cairo_image_surface_create_with_content (pattern->surface->content,
+                                                              rect.width,
+                                                              rect.height);
+        if (pad_image->status) {
+            status = pad_image->status;
+            goto BAIL;
+        }
+
+        _cairo_pattern_init_for_surface (&pad_pattern, &image->base);
+        cairo_matrix_init_translate (&pad_pattern.base.matrix, -x, -y);
+        pad_pattern.base.extend = CAIRO_EXTEND_PAD;
+        status = _cairo_surface_paint (pad_image,
+                                      CAIRO_OPERATOR_SOURCE, &pad_pattern.base,
+                                      NULL);
+        _cairo_pattern_fini (&pad_pattern.base);
+        if (unlikely (status))
+            goto BAIL;
+    }
+
+    switch (pdf_pattern->pattern->filter) {
+    default:
+    case CAIRO_FILTER_GOOD:
+    case CAIRO_FILTER_BEST:
+    case CAIRO_FILTER_BILINEAR:
+       interpolate = TRUE;
+       break;
+    case CAIRO_FILTER_FAST:
+    case CAIRO_FILTER_NEAREST:
+    case CAIRO_FILTER_GAUSSIAN:
+       interpolate = FALSE;
+       break;
+    }
+
+    *resource = _cairo_pdf_surface_new_object (surface);
+    if (resource->id == 0) {
+       status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+       goto BAIL;
+    }
+
+    status = _cairo_pdf_surface_emit_image (surface, (cairo_image_surface_t *)pad_image,
+                                            resource, interpolate, FALSE);
+    if (unlikely (status))
+        goto BAIL;
+
+    *width = ((cairo_image_surface_t *)pad_image)->width;
+    *height = ((cairo_image_surface_t *)pad_image)->height;
+    *origin_x = x;
+    *origin_y = y;
+
+BAIL:
+    if (pad_image != &image->base)
+        cairo_surface_destroy (pad_image);
+
+    _cairo_surface_release_source_image (pattern->surface, image, image_extra);
+
+    return status;
+}
+
+
+static cairo_status_t
+_cairo_pdf_surface_emit_recording_surface (cairo_pdf_surface_t        *surface,
+                                          cairo_pdf_source_surface_t *pdf_source)
+{
+    double old_width, old_height;
+    cairo_paginated_mode_t old_paginated_mode;
+    cairo_surface_clipper_t old_clipper;
+    cairo_box_double_t bbox;
+    cairo_int_status_t status;
+    int alpha = 0;
+    cairo_surface_t *source = pdf_source->surface;
+
+    if (_cairo_surface_is_snapshot (source))
+       source = _cairo_surface_snapshot_get_target (source);
+
+    old_width = surface->width;
+    old_height = surface->height;
+    old_paginated_mode = surface->paginated_mode;
+    old_clipper = surface->clipper;
+    _cairo_surface_clipper_init (&surface->clipper,
+                                _cairo_pdf_surface_clipper_intersect_clip_path);
+
+    _cairo_pdf_surface_set_size_internal (surface,
+                                         pdf_source->hash_entry->width,
+                                         pdf_source->hash_entry->height);
+    /* Patterns are emitted after fallback images. The paginated mode
+     * needs to be set to _RENDER while the recording surface is replayed
+     * back to this surface.
+     */
+    surface->paginated_mode = CAIRO_PAGINATED_MODE_RENDER;
+    _cairo_pdf_group_resources_clear (&surface->resources);
+    _get_bbox_from_extents (pdf_source->hash_entry->height, &pdf_source->hash_entry->extents, &bbox);
+    status = _cairo_pdf_surface_open_content_stream (surface, &bbox, &pdf_source->hash_entry->surface_res, TRUE);
+    if (unlikely (status))
+       return status;
+
+    if (cairo_surface_get_content (source) == CAIRO_CONTENT_COLOR) {
+       status = _cairo_pdf_surface_add_alpha (surface, 1.0, &alpha);
+       if (unlikely (status))
+           return status;
+
+       _cairo_output_stream_printf (surface->output,
+                                    "q /a%d gs 0 0 0 rg 0 0 %f %f re f Q\n",
+                                    alpha,
+                                    surface->width,
+                                    surface->height);
+    }
+
+    status = _cairo_recording_surface_replay_region (source,
+                                                    NULL,
+                                                    &surface->base,
+                                                    CAIRO_RECORDING_REGION_NATIVE);
+    assert (status != CAIRO_INT_STATUS_UNSUPPORTED);
+    if (unlikely (status))
+       return status;
+
+    status = _cairo_pdf_surface_close_content_stream (surface);
+
+    _cairo_surface_clipper_reset (&surface->clipper);
+    surface->clipper = old_clipper;
+    _cairo_pdf_surface_set_size_internal (surface,
+                                         old_width,
+                                         old_height);
+    surface->paginated_mode = old_paginated_mode;
+
+    return status;
+}
+
+static cairo_status_t
+_cairo_pdf_surface_emit_recording_subsurface (cairo_pdf_surface_t  *surface,
+                                             cairo_surface_t      *recording_surface,
+                                             const cairo_rectangle_int_t *extents,
+                                             cairo_pdf_resource_t  resource)
+{
+    double old_width, old_height;
+    cairo_paginated_mode_t old_paginated_mode;
+    cairo_surface_clipper_t old_clipper;
+    cairo_box_double_t bbox;
+    cairo_int_status_t status;
+    int alpha = 0;
+
+    old_width = surface->width;
+    old_height = surface->height;
+    old_paginated_mode = surface->paginated_mode;
+    old_clipper = surface->clipper;
+    _cairo_surface_clipper_init (&surface->clipper,
+                                _cairo_pdf_surface_clipper_intersect_clip_path);
+
+    _cairo_pdf_surface_set_size_internal (surface,
+                                         extents->width,
+                                         extents->height);
+    /* Patterns are emitted after fallback images. The paginated mode
+     * needs to be set to _RENDER while the recording surface is replayed
+     * back to this surface.
+     */
+    surface->paginated_mode = CAIRO_PAGINATED_MODE_RENDER;
+    _cairo_pdf_group_resources_clear (&surface->resources);
+    _get_bbox_from_extents (extents->height, extents, &bbox);
+    status = _cairo_pdf_surface_open_content_stream (surface, &bbox, &resource, TRUE);
+    if (unlikely (status))
+       return status;
+
+    if (cairo_surface_get_content (recording_surface) == CAIRO_CONTENT_COLOR) {
+       status = _cairo_pdf_surface_add_alpha (surface, 1.0, &alpha);
+       if (unlikely (status))
+           return status;
+
+       _cairo_output_stream_printf (surface->output,
+                                    "q /a%d gs 0 0 0 rg 0 0 %f %f re f Q\n",
+                                    alpha,
+                                    surface->width,
+                                    surface->height);
+    }
+
+    status = _cairo_recording_surface_replay_region (recording_surface,
+                                                    extents,
+                                                    &surface->base,
+                                                    CAIRO_RECORDING_REGION_NATIVE);
+    assert (status != CAIRO_INT_STATUS_UNSUPPORTED);
+    if (unlikely (status))
+       return status;
+
+    status = _cairo_pdf_surface_close_content_stream (surface);
+
+    _cairo_surface_clipper_reset (&surface->clipper);
+    surface->clipper = old_clipper;
+    _cairo_pdf_surface_set_size_internal (surface,
+                                         old_width,
+                                         old_height);
+    surface->paginated_mode = old_paginated_mode;
+
+    return status;
+}
+
+static cairo_status_t
+_cairo_pdf_surface_emit_surface (cairo_pdf_surface_t        *surface,
+                                cairo_pdf_source_surface_t *src_surface)
+{
+    if (src_surface->surface->type == CAIRO_SURFACE_TYPE_RECORDING) {
+       if (src_surface->surface->backend->type == CAIRO_SURFACE_TYPE_SUBSURFACE) {
+           cairo_surface_subsurface_t *sub = (cairo_surface_subsurface_t *) src_surface->surface;
+           return _cairo_pdf_surface_emit_recording_subsurface (surface,
+                                                                sub->target,
+                                                                &sub->extents,
+                                                                src_surface->hash_entry->surface_res);
+       } else {
+           return _cairo_pdf_surface_emit_recording_surface (surface,
+                                                             src_surface);
+       }
+    } else {
+       return _cairo_pdf_surface_emit_image_surface (surface,
+                                                     src_surface->surface,
+                                                     src_surface->hash_entry->surface_res,
+                                                     src_surface->hash_entry->interpolate,
+                                                     src_surface->hash_entry->stencil_mask);
+    }
+}
+
+static cairo_status_t
+_cairo_pdf_surface_emit_surface_pattern (cairo_pdf_surface_t   *surface,
+                                        cairo_pdf_pattern_t    *pdf_pattern)
+{
+    cairo_surface_pattern_t *pattern = (cairo_surface_pattern_t *) pdf_pattern->pattern;
+    cairo_status_t status;
+    cairo_pdf_resource_t pattern_resource = {0};
+    cairo_matrix_t cairo_p2d, pdf_p2d;
+    cairo_extend_t extend = cairo_pattern_get_extend (&pattern->base);
+    double xstep, ystep;
+    cairo_rectangle_int_t pattern_extents;
+    int pattern_width = 0; /* squelch bogus compiler warning */
+    int pattern_height = 0; /* squelch bogus compiler warning */
+    int origin_x = 0; /* squelch bogus compiler warning */
+    int origin_y = 0; /* squelch bogus compiler warning */
+    char draw_surface[200];
+    cairo_box_double_t     bbox;
+
+    if (pattern->base.extend == CAIRO_EXTEND_PAD &&
+       pattern->surface->type != CAIRO_SURFACE_TYPE_RECORDING)
+    {
+       status = _cairo_pdf_surface_emit_padded_image_surface (surface,
+                                                              pdf_pattern,
+                                                              &pattern_resource,
+                                                              &pattern_width,
+                                                              &pattern_height,
+                                                              &origin_x,
+                                                              &origin_y);
+       pattern_extents.x = 0;
+       pattern_extents.y = 0;
+       pattern_extents.width = pattern_width;
+       pattern_extents.height = pattern_height;
+    }
+    else
+    {
+       status = _cairo_pdf_surface_add_source_surface (surface,
+                                                       pattern->surface,
+                                                       pdf_pattern->pattern->filter,
+                                                       FALSE,
+                                                       &pattern_resource,
+                                                       &pattern_width,
+                                                       &pattern_height,
+                                                       &pattern_extents);
+    }
+    if (unlikely (status))
+       return status;
+
+    switch (extend) {
+    case CAIRO_EXTEND_PAD:
+    case CAIRO_EXTEND_NONE:
+    {
+       /* In PS/PDF, (as far as I can tell), all patterns are
+        * repeating. So we support cairo's EXTEND_NONE semantics
+        * by setting the repeat step size to a size large enough
+        * to guarantee that no more than a single occurrence will
+        * be visible.
+        *
+        * First, map the surface extents into pattern space (since
+        * xstep and ystep are in pattern space).  Then use an upper
+        * bound on the length of the diagonal of the pattern image
+        * and the surface as repeat size.  This guarantees to never
+        * repeat visibly.
+        */
+       double x1 = 0.0, y1 = 0.0;
+       double x2 = surface->width, y2 = surface->height;
+       _cairo_matrix_transform_bounding_box (&pattern->base.matrix,
+                                             &x1, &y1, &x2, &y2,
+                                             NULL);
+
+       /* Rather than computing precise bounds of the union, just
+        * add the surface extents unconditionally. We only
+        * required an answer that's large enough, we don't really
+        * care if it's not as tight as possible.*/
+       xstep = ystep = ceil ((x2 - x1) + (y2 - y1) +
+                             pattern_width + pattern_height);
+    }
+    break;
+    case CAIRO_EXTEND_REPEAT:
+       xstep = pattern_width;
+       ystep = pattern_height;
+       break;
+    case CAIRO_EXTEND_REFLECT:
+       pattern_extents.x = 0;
+       pattern_extents.y = 0;
+       pattern_extents.width = pattern_width*2;
+       pattern_extents.height = pattern_height*2;
+       xstep = pattern_width*2;
+       ystep = pattern_height*2;
+       break;
+       /* All the rest (if any) should have been analyzed away, so this
+        * case should be unreachable. */
+    default:
+       ASSERT_NOT_REACHED;
+       xstep = 0;
+       ystep = 0;
+    }
+
+    /* At this point, (that is, within the surface backend interface),
+     * the pattern's matrix maps from cairo's device space to cairo's
+     * pattern space, (both with their origin at the upper-left, and
+     * cairo's pattern space of size width,height).
+     *
+     * Then, we must emit a PDF pattern object that maps from its own
+     * pattern space, (which has a size that we establish in the BBox
+     * dictionary entry), to the PDF page's *initial* space, (which
+     * does not benefit from the Y-axis flipping matrix that we emit
+     * on each page). So the PDF patterns matrix maps from a
+     * (width,height) pattern space to a device space with the origin
+     * in the lower-left corner.
+     *
+     * So to handle all of that, we start with an identity matrix for
+     * the PDF pattern to device matrix. We translate it up by the
+     * image height then flip it in the Y direction, (moving us from
+     * the PDF origin to cairo's origin). We then multiply in the
+     * inverse of the cairo pattern matrix, (since it maps from device
+     * to pattern, while we're setting up pattern to device). Finally,
+     * we translate back down by the image height and flip again to
+     * end up at the lower-left origin that PDF expects.
+     *
+     * Additionally, within the stream that paints the pattern itself,
+     * we are using a PDF image object that has a size of (1,1) so we
+     * have to scale it up by the image width and height to fill our
+     * pattern cell.
+     */
+    cairo_p2d = pattern->base.matrix;
+    status = cairo_matrix_invert (&cairo_p2d);
+    /* cairo_pattern_set_matrix ensures the matrix is invertible */
+    assert (status == CAIRO_STATUS_SUCCESS);
+
+    cairo_matrix_multiply (&pdf_p2d, &cairo_p2d, &surface->cairo_to_pdf);
+    cairo_matrix_translate (&pdf_p2d, -origin_x, -origin_y);
+    cairo_matrix_translate (&pdf_p2d, 0.0, pattern_height);
+    cairo_matrix_scale (&pdf_p2d, 1.0, -1.0);
+
+    _get_bbox_from_extents (pattern_height, &pattern_extents, &bbox);
+    _cairo_pdf_surface_update_object (surface, pdf_pattern->pattern_res);
+    status = _cairo_pdf_surface_open_stream (surface,
+                                            &pdf_pattern->pattern_res,
+                                            FALSE,
+                                            "   /PatternType 1\n"
+                                            "   /BBox [ %f %f %f %f ]\n"
+                                            "   /XStep %f\n"
+                                            "   /YStep %f\n"
+                                            "   /TilingType 1\n"
+                                            "   /PaintType 1\n"
+                                            "   /Matrix [ %f %f %f %f %f %f ]\n"
+                                            "   /Resources << /XObject << /x%d %d 0 R >> >>\n",
+                                            bbox.p1.x, bbox.p1.y, bbox.p2.x, bbox.p2.y,
+                                            xstep, ystep,
+                                            pdf_p2d.xx, pdf_p2d.yx,
+                                            pdf_p2d.xy, pdf_p2d.yy,
+                                            pdf_p2d.x0, pdf_p2d.y0,
+                                            pattern_resource.id,
+                                            pattern_resource.id);
+    if (unlikely (status))
+       return status;
+
+    if (pattern->surface->type == CAIRO_SURFACE_TYPE_RECORDING) {
+       snprintf(draw_surface,
+                sizeof (draw_surface),
+                "/x%d Do\n",
+                pattern_resource.id);
+    } else {
+       snprintf(draw_surface,
+                sizeof (draw_surface),
+                "q %d 0 0 %d 0 0 cm /x%d Do Q",
+                pattern_width,
+                pattern_height,
+                pattern_resource.id);
+    }
+
+    if (extend == CAIRO_EXTEND_REFLECT) {
+       _cairo_output_stream_printf (surface->output,
+                                    "q 0 0 %d %d re W n %s Q\n"
+                                    "q -1 0 0 1 %d 0 cm 0 0 %d %d re W n %s Q\n"
+                                    "q 1 0 0 -1 0 %d cm 0 0 %d %d re W n %s Q\n"
+                                    "q -1 0 0 -1 %d %d cm 0 0 %d %d re W n %s Q\n",
+                                    pattern_width, pattern_height,
+                                    draw_surface,
+                                    pattern_width*2, pattern_width, pattern_height,
+                                    draw_surface,
+                                    pattern_height*2, pattern_width, pattern_height,
+                                    draw_surface,
+                                    pattern_width*2, pattern_height*2, pattern_width, pattern_height,
+                                    draw_surface);
+    } else {
+       _cairo_output_stream_printf (surface->output,
+                                    " %s \n",
+                                    draw_surface);
+    }
+
+    status = _cairo_pdf_surface_close_stream (surface);
+    if (unlikely (status))
+       return status;
+
+    return _cairo_output_stream_get_status (surface->output);
+}
+
+typedef struct _cairo_pdf_color_stop {
+    double offset;
+    double color[4];
+    cairo_pdf_resource_t resource;
+} cairo_pdf_color_stop_t;
+
+static cairo_status_t
+cairo_pdf_surface_emit_rgb_linear_function (cairo_pdf_surface_t    *surface,
+                                            cairo_pdf_color_stop_t *stop1,
+                                            cairo_pdf_color_stop_t *stop2,
+                                            cairo_pdf_resource_t   *function)
+{
+    int num_elems, i;
+    cairo_pdf_rgb_linear_function_t elem;
+    cairo_pdf_resource_t res;
+    cairo_status_t status;
+
+    num_elems = _cairo_array_num_elements (&surface->rgb_linear_functions);
+    for (i = 0; i < num_elems; i++) {
+       _cairo_array_copy_element (&surface->rgb_linear_functions, i, &elem);
+        if (memcmp (&elem.color1[0], &stop1->color[0], sizeof (double)*3) != 0)
+            continue;
+        if (memcmp (&elem.color2[0], &stop2->color[0], sizeof (double)*3) != 0)
+            continue;
+        *function =  elem.resource;
+        return CAIRO_STATUS_SUCCESS;
+    }
+
+    res = _cairo_pdf_surface_new_object (surface);
+    if (res.id == 0)
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    _cairo_output_stream_printf (surface->output,
+                                "%d 0 obj\n"
+                                "<< /FunctionType 2\n"
+                                "   /Domain [ 0 1 ]\n"
+                                "   /C0 [ %f %f %f ]\n"
+                                "   /C1 [ %f %f %f ]\n"
+                                "   /N 1\n"
+                                ">>\n"
+                                "endobj\n",
+                                res.id,
+                                 stop1->color[0],
+                                 stop1->color[1],
+                                 stop1->color[2],
+                                 stop2->color[0],
+                                 stop2->color[1],
+                                 stop2->color[2]);
+
+    elem.resource = res;
+    memcpy (&elem.color1[0], &stop1->color[0], sizeof (double)*3);
+    memcpy (&elem.color2[0], &stop2->color[0], sizeof (double)*3);
+
+    status = _cairo_array_append (&surface->rgb_linear_functions, &elem);
+    *function = res;
+
+    return status;
+}
+
+static cairo_status_t
+cairo_pdf_surface_emit_alpha_linear_function (cairo_pdf_surface_t    *surface,
+                                              cairo_pdf_color_stop_t *stop1,
+                                              cairo_pdf_color_stop_t *stop2,
+                                              cairo_pdf_resource_t   *function)
+{
+    int num_elems, i;
+    cairo_pdf_alpha_linear_function_t elem;
+    cairo_pdf_resource_t res;
+    cairo_status_t status;
+
+    num_elems = _cairo_array_num_elements (&surface->alpha_linear_functions);
+    for (i = 0; i < num_elems; i++) {
+       _cairo_array_copy_element (&surface->alpha_linear_functions, i, &elem);
+        if (elem.alpha1 != stop1->color[3])
+            continue;
+        if (elem.alpha2 != stop2->color[3])
+            continue;
+        *function =  elem.resource;
+        return CAIRO_STATUS_SUCCESS;
+    }
+
+    res = _cairo_pdf_surface_new_object (surface);
+    if (res.id == 0)
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    _cairo_output_stream_printf (surface->output,
+                                "%d 0 obj\n"
+                                "<< /FunctionType 2\n"
+                                "   /Domain [ 0 1 ]\n"
+                                "   /C0 [ %f ]\n"
+                                "   /C1 [ %f ]\n"
+                                "   /N 1\n"
+                                ">>\n"
+                                "endobj\n",
+                                res.id,
+                                 stop1->color[3],
+                                 stop2->color[3]);
+
+    elem.resource = res;
+    elem.alpha1 = stop1->color[3];
+    elem.alpha2 = stop2->color[3];
+
+    status = _cairo_array_append (&surface->alpha_linear_functions, &elem);
+    *function = res;
+
+    return status;
+}
+
+static cairo_status_t
+_cairo_pdf_surface_emit_stitched_colorgradient (cairo_pdf_surface_t    *surface,
+                                                unsigned int           n_stops,
+                                                cairo_pdf_color_stop_t *stops,
+                                                cairo_bool_t           is_alpha,
+                                                cairo_pdf_resource_t   *function)
+{
+    cairo_pdf_resource_t res;
+    unsigned int i;
+    cairo_status_t status;
+
+    /* emit linear gradients between pairs of subsequent stops... */
+    for (i = 0; i < n_stops-1; i++) {
+        if (is_alpha) {
+            status = cairo_pdf_surface_emit_alpha_linear_function (surface,
+                                                                   &stops[i],
+                                                                   &stops[i+1],
+                                                                   &stops[i].resource);
+            if (unlikely (status))
+                return status;
+        } else {
+            status = cairo_pdf_surface_emit_rgb_linear_function (surface,
+                                                                 &stops[i],
+                                                                 &stops[i+1],
+                                                                 &stops[i].resource);
+            if (unlikely (status))
+                return status;
+        }
+    }
+
+    /* ... and stitch them together */
+    res = _cairo_pdf_surface_new_object (surface);
+    if (res.id == 0)
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    _cairo_output_stream_printf (surface->output,
+                                "%d 0 obj\n"
+                                "<< /FunctionType 3\n"
+                                "   /Domain [ %f %f ]\n",
+                                res.id,
+                                 stops[0].offset,
+                                 stops[n_stops - 1].offset);
+
+    _cairo_output_stream_printf (surface->output,
+                                "   /Functions [ ");
+    for (i = 0; i < n_stops-1; i++)
+        _cairo_output_stream_printf (surface->output,
+                                     "%d 0 R ", stops[i].resource.id);
+    _cairo_output_stream_printf (surface->output,
+                                "]\n");
+
+    _cairo_output_stream_printf (surface->output,
+                                "   /Bounds [ ");
+    for (i = 1; i < n_stops-1; i++)
+        _cairo_output_stream_printf (surface->output,
+                                    "%f ", stops[i].offset);
+    _cairo_output_stream_printf (surface->output,
+                                "]\n");
+
+    _cairo_output_stream_printf (surface->output,
+                                "   /Encode [ ");
+    for (i = 1; i < n_stops; i++)
+        _cairo_output_stream_printf (surface->output,
+                                    "0 1 ");
+    _cairo_output_stream_printf (surface->output,
+                                "]\n");
+
+    _cairo_output_stream_printf (surface->output,
+                                ">>\n"
+                                "endobj\n");
+
+    *function = res;
+
+    return _cairo_output_stream_get_status (surface->output);
+}
+
+
+static void
+calc_gradient_color (cairo_pdf_color_stop_t *new_stop,
+                    cairo_pdf_color_stop_t *stop1,
+                    cairo_pdf_color_stop_t *stop2)
+{
+    int i;
+    double offset = stop1->offset / (stop1->offset + 1.0 - stop2->offset);
+
+    for (i = 0; i < 4; i++)
+       new_stop->color[i] = stop1->color[i] + offset*(stop2->color[i] - stop1->color[i]);
+}
+
+#define COLOR_STOP_EPSILON 1e-6
+
+static cairo_status_t
+_cairo_pdf_surface_emit_pattern_stops (cairo_pdf_surface_t      *surface,
+                                       cairo_gradient_pattern_t *pattern,
+                                       cairo_pdf_resource_t     *color_function,
+                                       cairo_pdf_resource_t     *alpha_function)
+{
+    cairo_pdf_color_stop_t *allstops, *stops;
+    unsigned int n_stops;
+    unsigned int i;
+    cairo_bool_t emit_alpha = FALSE;
+    cairo_status_t status;
+
+    color_function->id = 0;
+    alpha_function->id = 0;
+
+    allstops = _cairo_malloc_ab ((pattern->n_stops + 2), sizeof (cairo_pdf_color_stop_t));
+    if (unlikely (allstops == NULL))
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    stops = &allstops[1];
+    n_stops = pattern->n_stops;
+
+    for (i = 0; i < n_stops; i++) {
+       stops[i].color[0] = pattern->stops[i].color.red;
+       stops[i].color[1] = pattern->stops[i].color.green;
+       stops[i].color[2] = pattern->stops[i].color.blue;
+       stops[i].color[3] = pattern->stops[i].color.alpha;
+        if (!CAIRO_ALPHA_IS_OPAQUE (stops[i].color[3]))
+            emit_alpha = TRUE;
+       stops[i].offset = pattern->stops[i].offset;
+    }
+
+    if (pattern->base.extend == CAIRO_EXTEND_REPEAT ||
+       pattern->base.extend == CAIRO_EXTEND_REFLECT) {
+       if (stops[0].offset > COLOR_STOP_EPSILON) {
+           if (pattern->base.extend == CAIRO_EXTEND_REFLECT)
+               memcpy (allstops, stops, sizeof (cairo_pdf_color_stop_t));
+           else
+               calc_gradient_color (&allstops[0], &stops[0], &stops[n_stops-1]);
+           stops = allstops;
+           n_stops++;
+       }
+       stops[0].offset = 0.0;
+
+       if (stops[n_stops-1].offset < 1.0 - COLOR_STOP_EPSILON) {
+           if (pattern->base.extend == CAIRO_EXTEND_REFLECT) {
+               memcpy (&stops[n_stops],
+                       &stops[n_stops - 1],
+                       sizeof (cairo_pdf_color_stop_t));
+           } else {
+               calc_gradient_color (&stops[n_stops], &stops[0], &stops[n_stops-1]);
+           }
+           n_stops++;
+       }
+       stops[n_stops-1].offset = 1.0;
+    }
+
+    if (stops[0].offset == stops[n_stops - 1].offset) {
+       /*
+        * The first and the last stops have the same offset, but we
+        * don't want a function with an empty domain, because that
+        * would provoke underdefined behaviour from rasterisers.
+        * This can only happen with EXTEND_PAD, because EXTEND_NONE
+        * is optimised into a clear pattern in cairo-gstate, and
+        * REFLECT/REPEAT are always transformed to have the first
+        * stop at t=0 and the last stop at t=1.  Thus we want a step
+        * function going from the first color to the last one.
+        *
+        * This can be accomplished by stitching three functions:
+        *  - a constant first color function,
+        *  - a step from the first color to the last color (with empty domain)
+        *  - a constant last color function
+        */
+       cairo_pdf_color_stop_t pad_stops[4];
+
+       assert (pattern->base.extend == CAIRO_EXTEND_PAD);
+
+       pad_stops[0] = pad_stops[1] = stops[0];
+       pad_stops[2] = pad_stops[3] = stops[n_stops - 1];
+
+       pad_stops[0].offset = 0;
+       pad_stops[3].offset = 1;
+
+        status = _cairo_pdf_surface_emit_stitched_colorgradient (surface,
+                                                                 4,
+                                                                 pad_stops,
+                                                                 FALSE,
+                                                                 color_function);
+        if (unlikely (status))
+            goto BAIL;
+
+        if (emit_alpha) {
+            status = _cairo_pdf_surface_emit_stitched_colorgradient (surface,
+                                                                     4,
+                                                                     pad_stops,
+                                                                     TRUE,
+                                                                     alpha_function);
+            if (unlikely (status))
+                goto BAIL;
+        }
+    } else if (n_stops == 2) {
+        /* no need for stitched function */
+        status = cairo_pdf_surface_emit_rgb_linear_function (surface,
+                                                             &stops[0],
+                                                             &stops[n_stops - 1],
+                                                             color_function);
+        if (unlikely (status))
+            goto BAIL;
+
+        if (emit_alpha) {
+            status = cairo_pdf_surface_emit_alpha_linear_function (surface,
+                                                                   &stops[0],
+                                                                   &stops[n_stops - 1],
+                                                                   alpha_function);
+            if (unlikely (status))
+                goto BAIL;
+        }
+    } else {
+        /* multiple stops: stitch. XXX possible optimization: regularly spaced
+         * stops do not require stitching. XXX */
+        status = _cairo_pdf_surface_emit_stitched_colorgradient (surface,
+                                                                 n_stops,
+                                                                 stops,
+                                                                 FALSE,
+                                                                 color_function);
+        if (unlikely (status))
+            goto BAIL;
+
+        if (emit_alpha) {
+            status = _cairo_pdf_surface_emit_stitched_colorgradient (surface,
+                                                                     n_stops,
+                                                                     stops,
+                                                                     TRUE,
+                                                                     alpha_function);
+            if (unlikely (status))
+                goto BAIL;
+        }
+    }
+
+BAIL:
+    free (allstops);
+    return status;
+}
+
+static cairo_status_t
+_cairo_pdf_surface_emit_repeating_function (cairo_pdf_surface_t      *surface,
+                                           cairo_gradient_pattern_t *pattern,
+                                           cairo_pdf_resource_t     *function,
+                                           int                       begin,
+                                           int                       end)
+{
+    cairo_pdf_resource_t res;
+    int i;
+
+    res = _cairo_pdf_surface_new_object (surface);
+    if (res.id == 0)
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    _cairo_output_stream_printf (surface->output,
+                                "%d 0 obj\n"
+                                "<< /FunctionType 3\n"
+                                "   /Domain [ %d %d ]\n",
+                                res.id,
+                                 begin,
+                                 end);
+
+    _cairo_output_stream_printf (surface->output,
+                                "   /Functions [ ");
+    for (i = begin; i < end; i++)
+        _cairo_output_stream_printf (surface->output,
+                                     "%d 0 R ", function->id);
+    _cairo_output_stream_printf (surface->output,
+                                "]\n");
+
+    _cairo_output_stream_printf (surface->output,
+                                "   /Bounds [ ");
+    for (i = begin + 1; i < end; i++)
+        _cairo_output_stream_printf (surface->output,
+                                    "%d ", i);
+    _cairo_output_stream_printf (surface->output,
+                                "]\n");
+
+    _cairo_output_stream_printf (surface->output,
+                                "   /Encode [ ");
+    for (i = begin; i < end; i++) {
+       if ((i % 2) && pattern->base.extend == CAIRO_EXTEND_REFLECT) {
+           _cairo_output_stream_printf (surface->output,
+                                        "1 0 ");
+       } else {
+           _cairo_output_stream_printf (surface->output,
+                                        "0 1 ");
+       }
+    }
+    _cairo_output_stream_printf (surface->output,
+                                "]\n");
+
+    _cairo_output_stream_printf (surface->output,
+                                ">>\n"
+                                "endobj\n");
+
+    *function = res;
+
+    return _cairo_output_stream_get_status (surface->output);
+}
+
+static cairo_status_t
+cairo_pdf_surface_emit_transparency_group (cairo_pdf_surface_t  *surface,
+                                          cairo_pdf_resource_t  gstate_resource,
+                                          cairo_pdf_resource_t  gradient_mask)
+{
+    cairo_pdf_resource_t smask_resource;
+    cairo_status_t status;
+
+    status = _cairo_pdf_surface_open_stream (surface,
+                                            NULL,
+                                            surface->compress_content,
+                                            "   /Type /XObject\n"
+                                            "   /Subtype /Form\n"
+                                            "   /FormType 1\n"
+                                            "   /BBox [ 0 0 %f %f ]\n"
+                                            "   /Resources\n"
+                                            "      << /ExtGState\n"
+                                            "            << /a0 << /ca 1 /CA 1 >>"
+                                            "      >>\n"
+                                            "         /Pattern\n"
+                                            "            << /p%d %d 0 R >>\n"
+                                            "      >>\n"
+                                            "   /Group\n"
+                                            "      << /Type /Group\n"
+                                            "         /S /Transparency\n"
+                                            "         /CS /DeviceGray\n"
+                                            "      >>\n",
+                                            surface->width,
+                                            surface->height,
+                                            gradient_mask.id,
+                                            gradient_mask.id);
+    if (unlikely (status))
+       return status;
+
+    _cairo_output_stream_printf (surface->output,
+                                 "q\n"
+                                 "/a0 gs\n"
+                                 "/Pattern cs /p%d scn\n"
+                                 "0 0 %f %f re\n"
+                                 "f\n"
+                                 "Q\n",
+                                 gradient_mask.id,
+                                 surface->width,
+                                 surface->height);
+
+     status = _cairo_pdf_surface_close_stream (surface);
+     if (unlikely (status))
+       return status;
+
+    smask_resource = _cairo_pdf_surface_new_object (surface);
+    if (smask_resource.id == 0)
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    _cairo_output_stream_printf (surface->output,
+                                 "%d 0 obj\n"
+                                 "<< /Type /Mask\n"
+                                 "   /S /Luminosity\n"
+                                 "   /G %d 0 R\n"
+                                 ">>\n"
+                                 "endobj\n",
+                                 smask_resource.id,
+                                 surface->pdf_stream.self.id);
+
+    /* Create GState which uses the transparency group as an SMask. */
+    _cairo_pdf_surface_update_object (surface, gstate_resource);
+
+    _cairo_output_stream_printf (surface->output,
+                                 "%d 0 obj\n"
+                                 "<< /Type /ExtGState\n"
+                                 "   /SMask %d 0 R\n"
+                                 "   /ca 1\n"
+                                 "   /CA 1\n"
+                                 "   /AIS false\n"
+                                 ">>\n"
+                                 "endobj\n",
+                                 gstate_resource.id,
+                                 smask_resource.id);
+
+    return _cairo_output_stream_get_status (surface->output);
+}
+
+static void
+_cairo_pdf_surface_output_gradient (cairo_pdf_surface_t        *surface,
+                                   const cairo_pdf_pattern_t  *pdf_pattern,
+                                   cairo_pdf_resource_t        pattern_resource,
+                                   const cairo_matrix_t       *pat_to_pdf,
+                                   const cairo_circle_double_t*start,
+                                   const cairo_circle_double_t*end,
+                                   const double               *domain,
+                                   const char                 *colorspace,
+                                   cairo_pdf_resource_t        color_function)
+{
+    _cairo_output_stream_printf (surface->output,
+                                 "%d 0 obj\n"
+                                 "<< /Type /Pattern\n"
+                                 "   /PatternType 2\n"
+                                 "   /Matrix [ %f %f %f %f %f %f ]\n"
+                                 "   /Shading\n",
+                                pattern_resource.id,
+                                 pat_to_pdf->xx, pat_to_pdf->yx,
+                                 pat_to_pdf->xy, pat_to_pdf->yy,
+                                 pat_to_pdf->x0, pat_to_pdf->y0);
+
+    if (pdf_pattern->pattern->type == CAIRO_PATTERN_TYPE_LINEAR) {
+       _cairo_output_stream_printf (surface->output,
+                                    "      << /ShadingType 2\n"
+                                    "         /ColorSpace %s\n"
+                                    "         /Coords [ %f %f %f %f ]\n",
+                                    colorspace,
+                                    start->center.x, start->center.y,
+                                    end->center.x, end->center.y);
+    } else {
+       _cairo_output_stream_printf (surface->output,
+                                    "      << /ShadingType 3\n"
+                                    "         /ColorSpace %s\n"
+                                    "         /Coords [ %f %f %f %f %f %f ]\n",
+                                    colorspace,
+                                    start->center.x, start->center.y,
+                                    MAX (start->radius, 0),
+                                    end->center.x, end->center.y,
+                                    MAX (end->radius, 0));
+    }
+
+    _cairo_output_stream_printf (surface->output,
+                                "         /Domain [ %f %f ]\n",
+                                domain[0], domain[1]);
+
+    if (pdf_pattern->pattern->extend != CAIRO_EXTEND_NONE) {
+        _cairo_output_stream_printf (surface->output,
+                                     "         /Extend [ true true ]\n");
+    } else {
+        _cairo_output_stream_printf (surface->output,
+                                     "         /Extend [ false false ]\n");
+    }
+
+    _cairo_output_stream_printf (surface->output,
+                                "         /Function %d 0 R\n"
+                                 "      >>\n"
+                                 ">>\n"
+                                 "endobj\n",
+                                color_function.id);
+}
+
+static cairo_status_t
+_cairo_pdf_surface_emit_gradient (cairo_pdf_surface_t    *surface,
+                                 cairo_pdf_pattern_t    *pdf_pattern)
+{
+    cairo_gradient_pattern_t *pattern = (cairo_gradient_pattern_t *) pdf_pattern->pattern;
+    cairo_pdf_resource_t color_function, alpha_function;
+    cairo_matrix_t pat_to_pdf;
+    cairo_circle_double_t start, end;
+    double domain[2];
+    cairo_status_t status;
+
+    assert (pattern->n_stops != 0);
+
+    status = _cairo_pdf_surface_emit_pattern_stops (surface,
+                                                    pattern,
+                                                    &color_function,
+                                                    &alpha_function);
+    if (unlikely (status))
+       return status;
+
+    pat_to_pdf = pattern->base.matrix;
+    status = cairo_matrix_invert (&pat_to_pdf);
+    /* cairo_pattern_set_matrix ensures the matrix is invertible */
+    assert (status == CAIRO_STATUS_SUCCESS);
+    cairo_matrix_multiply (&pat_to_pdf, &pat_to_pdf, &surface->cairo_to_pdf);
+
+    if (pattern->base.extend == CAIRO_EXTEND_REPEAT ||
+       pattern->base.extend == CAIRO_EXTEND_REFLECT)
+    {
+       double bounds_x1, bounds_x2, bounds_y1, bounds_y2;
+       double x_scale, y_scale, tolerance;
+
+       /* TODO: use tighter extents */
+       bounds_x1 = 0;
+       bounds_y1 = 0;
+       bounds_x2 = surface->width;
+       bounds_y2 = surface->height;
+       _cairo_matrix_transform_bounding_box (&pattern->base.matrix,
+                                             &bounds_x1, &bounds_y1,
+                                             &bounds_x2, &bounds_y2,
+                                             NULL);
+
+       x_scale = surface->base.x_resolution / surface->base.x_fallback_resolution;
+       y_scale = surface->base.y_resolution / surface->base.y_fallback_resolution;
+
+       tolerance = fabs (_cairo_matrix_compute_determinant (&pattern->base.matrix));
+       tolerance /= _cairo_matrix_transformed_circle_major_axis (&pattern->base.matrix, 1);
+       tolerance *= MIN (x_scale, y_scale);
+
+       _cairo_gradient_pattern_box_to_parameter (pattern,
+                                                 bounds_x1, bounds_y1,
+                                                 bounds_x2, bounds_y2,
+                                                 tolerance, domain);
+    } else if (pattern->stops[0].offset == pattern->stops[pattern->n_stops - 1].offset) {
+       /*
+        * If the first and the last stop offset are the same, then
+        * the color function is a step function.
+        * _cairo_ps_surface_emit_pattern_stops emits it as a stitched
+        * function no matter how many stops the pattern has.  The
+        * domain of the stitched function will be [0 1] in this case.
+        *
+        * This is done to avoid emitting degenerate gradients for
+        * EXTEND_PAD patterns having a step color function.
+        */
+       domain[0] = 0.0;
+       domain[1] = 1.0;
+
+       assert (pattern->base.extend == CAIRO_EXTEND_PAD);
+    } else {
+       domain[0] = pattern->stops[0].offset;
+       domain[1] = pattern->stops[pattern->n_stops - 1].offset;
+    }
+
+    /* PDF requires the first and last stop to be the same as the
+     * extreme coordinates. For repeating patterns this moves the
+     * extreme coordinates out to the begin/end of the repeating
+     * function. For non repeating patterns this may move the extreme
+     * coordinates in if there are not stops at offset 0 and 1. */
+    _cairo_gradient_pattern_interpolate (pattern, domain[0], &start);
+    _cairo_gradient_pattern_interpolate (pattern, domain[1], &end);
+
+    if (pattern->base.extend == CAIRO_EXTEND_REPEAT ||
+       pattern->base.extend == CAIRO_EXTEND_REFLECT)
+    {
+       int repeat_begin, repeat_end;
+
+       repeat_begin = floor (domain[0]);
+       repeat_end = ceil (domain[1]);
+
+       status = _cairo_pdf_surface_emit_repeating_function (surface,
+                                                            pattern,
+                                                            &color_function,
+                                                            repeat_begin,
+                                                            repeat_end);
+       if (unlikely (status))
+           return status;
+
+       if (alpha_function.id != 0) {
+           status = _cairo_pdf_surface_emit_repeating_function (surface,
+                                                                pattern,
+                                                                &alpha_function,
+                                                                repeat_begin,
+                                                                repeat_end);
+           if (unlikely (status))
+               return status;
+       }
+    } else if (pattern->n_stops <= 2) {
+       /* For EXTEND_NONE and EXTEND_PAD if there are only two stops a
+        * Type 2 function is used by itself without a stitching
+        * function. Type 2 functions always have the domain [0 1] */
+       domain[0] = 0.0;
+       domain[1] = 1.0;
+    }
+
+    _cairo_pdf_surface_update_object (surface, pdf_pattern->pattern_res);
+    _cairo_pdf_surface_output_gradient (surface, pdf_pattern,
+                                       pdf_pattern->pattern_res,
+                                       &pat_to_pdf, &start, &end, domain,
+                                       "/DeviceRGB", color_function);
+
+    if (alpha_function.id != 0) {
+       cairo_pdf_resource_t mask_resource;
+
+       assert (pdf_pattern->gstate_res.id != 0);
+
+        /* Create pattern for SMask. */
+        mask_resource = _cairo_pdf_surface_new_object (surface);
+       if (mask_resource.id == 0)
+           return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+       _cairo_pdf_surface_output_gradient (surface, pdf_pattern,
+                                           mask_resource,
+                                           &pat_to_pdf, &start, &end, domain,
+                                           "/DeviceGray", alpha_function);
+
+        status = _cairo_pdf_surface_add_pattern (surface, mask_resource);
+        if (unlikely (status))
+            return status;
+
+       status = cairo_pdf_surface_emit_transparency_group (surface,
+                                                           pdf_pattern->gstate_res,
+                                                           mask_resource);
+       if (unlikely (status))
+           return status;
+    }
+
+    return _cairo_output_stream_get_status (surface->output);
+}
+
+static cairo_status_t
+_cairo_pdf_surface_emit_mesh_pattern (cairo_pdf_surface_t    *surface,
+                                     cairo_pdf_pattern_t    *pdf_pattern)
+{
+    cairo_matrix_t pat_to_pdf;
+    cairo_status_t status;
+    cairo_pattern_t *pattern = pdf_pattern->pattern;
+    cairo_pdf_shading_t shading;
+    int i;
+    cairo_pdf_resource_t res;
+
+    pat_to_pdf = pattern->matrix;
+    status = cairo_matrix_invert (&pat_to_pdf);
+    /* cairo_pattern_set_matrix ensures the matrix is invertible */
+    assert (status == CAIRO_STATUS_SUCCESS);
+
+    cairo_matrix_multiply (&pat_to_pdf, &pat_to_pdf, &surface->cairo_to_pdf);
+
+    status = _cairo_pdf_shading_init_color (&shading, (cairo_mesh_pattern_t *) pattern);
+    if (unlikely (status))
+       return status;
+
+    res = _cairo_pdf_surface_new_object (surface);
+    if (unlikely (res.id == 0))
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    _cairo_output_stream_printf (surface->output,
+                                "%d 0 obj\n"
+                                 "<< /ShadingType %d\n"
+                                 "   /ColorSpace /DeviceRGB\n"
+                                "   /BitsPerCoordinate %d\n"
+                                "   /BitsPerComponent %d\n"
+                                "   /BitsPerFlag %d\n"
+                                "   /Decode [",
+                                res.id,
+                                shading.shading_type,
+                                shading.bits_per_coordinate,
+                                shading.bits_per_component,
+                                shading.bits_per_flag);
+
+    for (i = 0; i < shading.decode_array_length; i++)
+       _cairo_output_stream_printf (surface->output, "%f ", shading.decode_array[i]);
+
+    _cairo_output_stream_printf (surface->output,
+                                "]\n"
+                                "   /Length %ld\n"
+                                ">>\n"
+                                "stream\n",
+                                shading.data_length);
+
+    _cairo_output_stream_write (surface->output, shading.data, shading.data_length);
+
+    _cairo_output_stream_printf (surface->output,
+                                "\nendstream\n"
+                                "endobj\n");
+
+    _cairo_pdf_shading_fini (&shading);
+
+    _cairo_pdf_surface_update_object (surface, pdf_pattern->pattern_res);
+    _cairo_output_stream_printf (surface->output,
+                                 "%d 0 obj\n"
+                                 "<< /Type /Pattern\n"
+                                 "   /PatternType 2\n"
+                                 "   /Matrix [ %f %f %f %f %f %f ]\n"
+                                 "   /Shading %d 0 R\n"
+                                ">>\n"
+                                "endobj\n",
+                                pdf_pattern->pattern_res.id,
+                                 pat_to_pdf.xx, pat_to_pdf.yx,
+                                 pat_to_pdf.xy, pat_to_pdf.yy,
+                                 pat_to_pdf.x0, pat_to_pdf.y0,
+                                res.id);
+
+    if (pdf_pattern->gstate_res.id != 0) {
+       cairo_pdf_resource_t mask_resource;
+
+       /* Create pattern for SMask. */
+        res = _cairo_pdf_surface_new_object (surface);
+       if (unlikely (res.id == 0))
+           return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+       status = _cairo_pdf_shading_init_alpha (&shading, (cairo_mesh_pattern_t *) pattern);
+       if (unlikely (status))
+           return status;
+
+       _cairo_output_stream_printf (surface->output,
+                                "%d 0 obj\n"
+                                 "<< /ShadingType %d\n"
+                                 "   /ColorSpace /DeviceGray\n"
+                                "   /BitsPerCoordinate %d\n"
+                                "   /BitsPerComponent %d\n"
+                                "   /BitsPerFlag %d\n"
+                                "   /Decode [",
+                                res.id,
+                                shading.shading_type,
+                                shading.bits_per_coordinate,
+                                shading.bits_per_component,
+                                shading.bits_per_flag);
+
+       for (i = 0; i < shading.decode_array_length; i++)
+           _cairo_output_stream_printf (surface->output, "%f ", shading.decode_array[i]);
+
+       _cairo_output_stream_printf (surface->output,
+                                    "]\n"
+                                    "   /Length %ld\n"
+                                    ">>\n"
+                                    "stream\n",
+                                    shading.data_length);
+
+       _cairo_output_stream_write (surface->output, shading.data, shading.data_length);
+
+       _cairo_output_stream_printf (surface->output,
+                                    "\nendstream\n"
+                                    "endobj\n");
+       _cairo_pdf_shading_fini (&shading);
+
+        mask_resource = _cairo_pdf_surface_new_object (surface);
+       if (unlikely (mask_resource.id == 0))
+           return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+       _cairo_output_stream_printf (surface->output,
+                                    "%d 0 obj\n"
+                                    "<< /Type /Pattern\n"
+                                    "   /PatternType 2\n"
+                                    "   /Matrix [ %f %f %f %f %f %f ]\n"
+                                    "   /Shading %d 0 R\n"
+                                    ">>\n"
+                                    "endobj\n",
+                                    mask_resource.id,
+                                    pat_to_pdf.xx, pat_to_pdf.yx,
+                                    pat_to_pdf.xy, pat_to_pdf.yy,
+                                    pat_to_pdf.x0, pat_to_pdf.y0,
+                                    res.id);
+
+       status = cairo_pdf_surface_emit_transparency_group (surface,
+                                                           pdf_pattern->gstate_res,
+                                                           mask_resource);
+       if (unlikely (status))
+           return status;
+    }
+
+    return _cairo_output_stream_get_status (surface->output);
+}
+
+static cairo_status_t
+_cairo_pdf_surface_emit_pattern (cairo_pdf_surface_t *surface, cairo_pdf_pattern_t *pdf_pattern)
+{
+    double old_width, old_height;
+    cairo_status_t status;
+
+    old_width = surface->width;
+    old_height = surface->height;
+    _cairo_pdf_surface_set_size_internal (surface,
+                                         pdf_pattern->width,
+                                         pdf_pattern->height);
+
+    switch (pdf_pattern->pattern->type) {
+    case CAIRO_PATTERN_TYPE_SOLID:
+       ASSERT_NOT_REACHED;
+       status = _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH);
+       break;
+
+    case CAIRO_PATTERN_TYPE_SURFACE:
+       status = _cairo_pdf_surface_emit_surface_pattern (surface, pdf_pattern);
+       break;
+
+    case CAIRO_PATTERN_TYPE_LINEAR:
+    case CAIRO_PATTERN_TYPE_RADIAL:
+       status = _cairo_pdf_surface_emit_gradient (surface, pdf_pattern);
+       break;
+
+    case CAIRO_PATTERN_TYPE_MESH:
+       status = _cairo_pdf_surface_emit_mesh_pattern (surface, pdf_pattern);
+       break;
+
+    default:
+       ASSERT_NOT_REACHED;
+       status = _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH);
+       break;
+    }
+
+    _cairo_pdf_surface_set_size_internal (surface,
+                                         old_width,
+                                         old_height);
+
+    return status;
+}
+
+static cairo_status_t
+_cairo_pdf_surface_paint_surface_pattern (cairo_pdf_surface_t     *surface,
+                                         cairo_surface_pattern_t *source,
+                                         cairo_bool_t             stencil_mask)
+{
+    cairo_pdf_resource_t surface_res;
+    int width, height;
+    cairo_matrix_t cairo_p2d, pdf_p2d;
+    cairo_status_t status;
+    int alpha;
+    cairo_rectangle_int_t extents;
+
+    status = _cairo_pdf_surface_add_source_surface (surface,
+                                                   source->surface,
+                                                   source->base.filter,
+                                                   stencil_mask,
+                                                   &surface_res,
+                                                   &width,
+                                                   &height,
+                                                   &extents);
+    if (unlikely (status))
+       return status;
+
+    cairo_p2d = source->base.matrix;
+    status = cairo_matrix_invert (&cairo_p2d);
+    /* cairo_pattern_set_matrix ensures the matrix is invertible */
+    assert (status == CAIRO_STATUS_SUCCESS);
+
+    pdf_p2d = surface->cairo_to_pdf;
+    cairo_matrix_multiply (&pdf_p2d, &cairo_p2d, &pdf_p2d);
+    cairo_matrix_translate (&pdf_p2d, 0.0, height);
+    cairo_matrix_scale (&pdf_p2d, 1.0, -1.0);
+    if (source->surface->type != CAIRO_SURFACE_TYPE_RECORDING)
+       cairo_matrix_scale (&pdf_p2d, width, height);
+
+    status = _cairo_pdf_operators_flush (&surface->pdf_operators);
+    if (unlikely (status))
+       return status;
+
+    if (! _cairo_matrix_is_identity (&pdf_p2d)) {
+       _cairo_output_stream_printf (surface->output,
+                                    "%f %f %f %f %f %f cm\n",
+                                    pdf_p2d.xx, pdf_p2d.yx,
+                                    pdf_p2d.xy, pdf_p2d.yy,
+                                    pdf_p2d.x0, pdf_p2d.y0);
+    }
+
+    status = _cairo_pdf_surface_add_alpha (surface, 1.0, &alpha);
+    if (unlikely (status))
+       return status;
+
+    if (stencil_mask) {
+       _cairo_output_stream_printf (surface->output,
+                                    "/x%d Do\n",
+                                    surface_res.id);
+    } else {
+       _cairo_output_stream_printf (surface->output,
+                                    "/a%d gs /x%d Do\n",
+                                    alpha,
+                                    surface_res.id);
+    }
+
+    return _cairo_pdf_surface_add_xobject (surface, surface_res);
+}
+
+static cairo_status_t
+_cairo_pdf_surface_select_operator (cairo_pdf_surface_t *surface,
+                                   cairo_operator_t     op)
+{
+    cairo_status_t status;
+
+    if (op == surface->current_operator)
+       return CAIRO_STATUS_SUCCESS;
+
+    status = _cairo_pdf_operators_flush (&surface->pdf_operators);
+    if (unlikely (status))
+       return status;
+
+    _cairo_output_stream_printf (surface->output,
+                                "/b%d gs\n", op);
+    surface->current_operator = op;
+    _cairo_pdf_surface_add_operator (surface, op);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_pdf_surface_select_pattern (cairo_pdf_surface_t *surface,
+                                  const cairo_pattern_t     *pattern,
+                                  cairo_pdf_resource_t pattern_res,
+                                  cairo_bool_t         is_stroke)
+{
+    cairo_status_t status;
+    int alpha;
+    const cairo_color_t *solid_color = NULL;
+
+    if (pattern->type == CAIRO_PATTERN_TYPE_SOLID) {
+       const cairo_solid_pattern_t *solid = (const cairo_solid_pattern_t *) pattern;
+
+       solid_color = &solid->color;
+    }
+
+    if (solid_color != NULL) {
+       if (surface->current_pattern_is_solid_color == FALSE ||
+           surface->current_color_red != solid_color->red ||
+           surface->current_color_green != solid_color->green ||
+           surface->current_color_blue != solid_color->blue ||
+           surface->current_color_is_stroke != is_stroke)
+       {
+           status = _cairo_pdf_operators_flush (&surface->pdf_operators);
+           if (unlikely (status))
+               return status;
+
+           _cairo_output_stream_printf (surface->output,
+                                        "%f %f %f ",
+                                        solid_color->red,
+                                        solid_color->green,
+                                        solid_color->blue);
+
+           if (is_stroke)
+               _cairo_output_stream_printf (surface->output, "RG ");
+           else
+               _cairo_output_stream_printf (surface->output, "rg ");
+
+           surface->current_color_red = solid_color->red;
+           surface->current_color_green = solid_color->green;
+           surface->current_color_blue = solid_color->blue;
+           surface->current_color_is_stroke = is_stroke;
+       }
+
+       if (surface->current_pattern_is_solid_color == FALSE ||
+           surface->current_color_alpha != solid_color->alpha)
+       {
+           status = _cairo_pdf_surface_add_alpha (surface, solid_color->alpha, &alpha);
+           if (unlikely (status))
+               return status;
+
+           status = _cairo_pdf_operators_flush (&surface->pdf_operators);
+           if (unlikely (status))
+               return status;
+
+           _cairo_output_stream_printf (surface->output,
+                                        "/a%d gs\n",
+                                        alpha);
+           surface->current_color_alpha = solid_color->alpha;
+       }
+
+       surface->current_pattern_is_solid_color = TRUE;
+    } else {
+       status = _cairo_pdf_surface_add_alpha (surface, 1.0, &alpha);
+       if (unlikely (status))
+           return status;
+
+       status = _cairo_pdf_surface_add_pattern (surface, pattern_res);
+       if (unlikely (status))
+           return status;
+
+       status = _cairo_pdf_operators_flush (&surface->pdf_operators);
+       if (unlikely (status))
+           return status;
+
+       /* fill-stroke calls select_pattern twice. Don't save if the
+        * gstate is already saved. */
+       if (!surface->select_pattern_gstate_saved)
+           _cairo_output_stream_printf (surface->output, "q ");
+
+       if (is_stroke) {
+           _cairo_output_stream_printf (surface->output,
+                                        "/Pattern CS /p%d SCN ",
+                                        pattern_res.id);
+       } else {
+           _cairo_output_stream_printf (surface->output,
+                                        "/Pattern cs /p%d scn ",
+                                        pattern_res.id);
+       }
+       _cairo_output_stream_printf (surface->output,
+                                    "/a%d gs\n",
+                                    alpha);
+       surface->select_pattern_gstate_saved = TRUE;
+       surface->current_pattern_is_solid_color = FALSE;
+    }
+
+    return _cairo_output_stream_get_status (surface->output);
+}
+
+static cairo_int_status_t
+_cairo_pdf_surface_unselect_pattern (cairo_pdf_surface_t *surface)
+{
+    cairo_int_status_t status;
+
+    if (surface->select_pattern_gstate_saved) {
+       status = _cairo_pdf_operators_flush (&surface->pdf_operators);
+       if (unlikely (status))
+           return status;
+
+       _cairo_output_stream_printf (surface->output, "Q\n");
+       _cairo_pdf_operators_reset (&surface->pdf_operators);
+       surface->current_pattern_is_solid_color = FALSE;
+    }
+    surface->select_pattern_gstate_saved = FALSE;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_pdf_surface_show_page (void *abstract_surface)
+{
+    cairo_pdf_surface_t *surface = abstract_surface;
+    cairo_int_status_t status;
+
+    status = _cairo_pdf_surface_close_content_stream (surface);
+    if (unlikely (status))
+       return status;
+
+    _cairo_surface_clipper_reset (&surface->clipper);
+
+    status = _cairo_pdf_surface_write_page (surface);
+    if (unlikely (status))
+       return status;
+
+    _cairo_pdf_surface_clear (surface);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_bool_t
+_cairo_pdf_surface_get_extents (void                   *abstract_surface,
+                               cairo_rectangle_int_t   *rectangle)
+{
+    cairo_pdf_surface_t *surface = abstract_surface;
+
+    rectangle->x = 0;
+    rectangle->y = 0;
+
+    /* XXX: The conversion to integers here is pretty bogus, (not to
+     * mention the arbitrary limitation of width to a short(!). We
+     * may need to come up with a better interface for get_size.
+     */
+    rectangle->width  = ceil (surface->width);
+    rectangle->height = ceil (surface->height);
+
+    return TRUE;
+}
+
+static void
+_cairo_pdf_surface_get_font_options (void                  *abstract_surface,
+                                    cairo_font_options_t  *options)
+{
+    _cairo_font_options_init_default (options);
+
+    cairo_font_options_set_hint_style (options, CAIRO_HINT_STYLE_NONE);
+    cairo_font_options_set_hint_metrics (options, CAIRO_HINT_METRICS_OFF);
+    cairo_font_options_set_antialias (options, CAIRO_ANTIALIAS_GRAY);
+    _cairo_font_options_set_round_glyph_positions (options, CAIRO_ROUND_GLYPH_POS_OFF);
+}
+
+static cairo_pdf_resource_t
+_cairo_pdf_surface_write_info (cairo_pdf_surface_t *surface)
+{
+    cairo_pdf_resource_t info;
+
+    info = _cairo_pdf_surface_new_object (surface);
+    if (info.id == 0)
+       return info;
+
+    _cairo_output_stream_printf (surface->output,
+                                "%d 0 obj\n"
+                                "<< /Creator (cairo %s (http://cairographics.org))\n"
+                                "   /Producer (cairo %s (http://cairographics.org))\n"
+                                ">>\n"
+                                "endobj\n",
+                                info.id,
+                                 cairo_version_string (),
+                                 cairo_version_string ());
+
+    return info;
+}
+
+static void
+_cairo_pdf_surface_write_pages (cairo_pdf_surface_t *surface)
+{
+    cairo_pdf_resource_t page;
+    int num_pages, i;
+
+    _cairo_pdf_surface_update_object (surface, surface->pages_resource);
+    _cairo_output_stream_printf (surface->output,
+                                "%d 0 obj\n"
+                                "<< /Type /Pages\n"
+                                "   /Kids [ ",
+                                surface->pages_resource.id);
+
+    num_pages = _cairo_array_num_elements (&surface->pages);
+    for (i = 0; i < num_pages; i++) {
+       _cairo_array_copy_element (&surface->pages, i, &page);
+       _cairo_output_stream_printf (surface->output, "%d 0 R ", page.id);
+    }
+
+    _cairo_output_stream_printf (surface->output, "]\n");
+    _cairo_output_stream_printf (surface->output, "   /Count %d\n", num_pages);
+
+
+    /* TODO: Figure out which other defaults to be inherited by /Page
+     * objects. */
+    _cairo_output_stream_printf (surface->output,
+                                ">>\n"
+                                "endobj\n");
+}
+
+static cairo_status_t
+_utf8_to_pdf_string (const char *utf8, char **str_out)
+{
+    int i;
+    int len;
+    cairo_bool_t ascii;
+    char *str;
+    cairo_status_t status = CAIRO_STATUS_SUCCESS;
+
+    ascii = TRUE;
+    len = strlen (utf8);
+    for (i = 0; i < len; i++) {
+       unsigned c = utf8[i];
+       if (c < 32 || c > 126 || c == '(' || c == ')' || c == '\\') {
+           ascii = FALSE;
+           break;
+       }
+    }
+
+    if (ascii) {
+       str = malloc (len + 3);
+       if (str == NULL)
+           return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+       str[0] = '(';
+       for (i = 0; i < len; i++)
+           str[i+1] = utf8[i];
+       str[i+1] = ')';
+       str[i+2] = 0;
+    } else {
+       uint16_t *utf16 = NULL;
+       int utf16_len = 0;
+
+       status = _cairo_utf8_to_utf16 (utf8, -1, &utf16, &utf16_len);
+       if (unlikely (status))
+           return status;
+
+       str = malloc (utf16_len*4 + 7);
+       if (str == NULL) {
+           free (utf16);
+           return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+       }
+
+       strcpy (str, "<FEFF");
+       for (i = 0; i < utf16_len; i++)
+           snprintf (str + 4*i + 5, 5, "%04X", utf16[i]);
+
+       strcat (str, ">");
+       free (utf16);
+    }
+    *str_out = str;
+
+    return status;
+}
+
+static cairo_status_t
+_cairo_pdf_surface_emit_unicode_for_glyph (cairo_pdf_surface_t *surface,
+                                          const char           *utf8)
+{
+    uint16_t *utf16 = NULL;
+    int utf16_len = 0;
+    cairo_status_t status;
+    int i;
+
+    if (utf8 && *utf8) {
+       status = _cairo_utf8_to_utf16 (utf8, -1, &utf16, &utf16_len);
+       if (unlikely (status))
+           return status;
+    }
+
+    _cairo_output_stream_printf (surface->output, "<");
+    if (utf16 == NULL || utf16_len == 0) {
+       /* According to the "ToUnicode Mapping File Tutorial"
+        * http://www.adobe.com/devnet/acrobat/pdfs/5411.ToUnicode.pdf
+        *
+        * Glyphs that do not map to a Unicode code point must be
+        * mapped to 0xfffd "REPLACEMENT CHARACTER".
+        */
+       _cairo_output_stream_printf (surface->output,
+                                    "fffd");
+    } else {
+       for (i = 0; i < utf16_len; i++)
+           _cairo_output_stream_printf (surface->output,
+                                        "%04x", (int) (utf16[i]));
+    }
+    _cairo_output_stream_printf (surface->output, ">");
+
+    free (utf16);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+/* Bob Jenkins hash
+ *
+ * Public domain code from:
+ *   http://burtleburtle.net/bob/hash/doobs.html
+ */
+
+#define HASH_MIX(a,b,c)                \
+{                                      \
+    a -= b; a -= c; a ^= (c>>13);      \
+    b -= c; b -= a; b ^= (a<<8);       \
+    c -= a; c -= b; c ^= (b>>13);      \
+    a -= b; a -= c; a ^= (c>>12);      \
+    b -= c; b -= a; b ^= (a<<16);      \
+    c -= a; c -= b; c ^= (b>>5);       \
+    a -= b; a -= c; a ^= (c>>3);       \
+    b -= c; b -= a; b ^= (a<<10);      \
+    c -= a; c -= b; c ^= (b>>15);      \
+}
+
+static uint32_t
+_hash_data (const unsigned char *data, int length, uint32_t initval)
+{
+    uint32_t a, b, c, len;
+
+    len = length;
+    a = b = 0x9e3779b9;  /* the golden ratio; an arbitrary value */
+    c = initval;         /* the previous hash value */
+
+    while (len >= 12) {
+       a += (data[0] + ((uint32_t)data[1]<<8) + ((uint32_t)data[2]<<16) + ((uint32_t)data[3]<<24));
+       b += (data[4] + ((uint32_t)data[5]<<8) + ((uint32_t)data[6]<<16) + ((uint32_t)data[7]<<24));
+       c += (data[8] + ((uint32_t)data[9]<<8) + ((uint32_t)data[10]<<16)+ ((uint32_t)data[11]<<24));
+       HASH_MIX (a,b,c);
+       data += 12;
+       len -= 12;
+    }
+
+    c += length;
+    switch(len) {
+    case 11: c+= ((uint32_t) data[10] << 24);
+    case 10: c+= ((uint32_t) data[9] << 16);
+    case 9 : c+= ((uint32_t) data[8] << 8);
+    case 8 : b+= ((uint32_t) data[7] << 24);
+    case 7 : b+= ((uint32_t) data[6] << 16);
+    case 6 : b+= ((uint32_t) data[5] << 8);
+    case 5 : b+= data[4];
+    case 4 : a+= ((uint32_t) data[3] << 24);
+    case 3 : a+= ((uint32_t) data[2] << 16);
+    case 2 : a+= ((uint32_t) data[1] << 8);
+    case 1 : a+= data[0];
+    }
+    HASH_MIX (a,b,c);
+
+    return c;
+}
+
+static void
+_create_font_subset_tag (cairo_scaled_font_subset_t    *font_subset,
+                        const char                     *font_name,
+                        char                           *tag)
+{
+    uint32_t hash;
+    int i;
+    long numerator;
+    ldiv_t d;
+
+    hash = _hash_data ((unsigned char *) font_name, strlen(font_name), 0);
+    hash = _hash_data ((unsigned char *) (font_subset->glyphs),
+                      font_subset->num_glyphs * sizeof(unsigned long), hash);
+
+    numerator = abs (hash);
+    for (i = 0; i < 6; i++) {
+       d = ldiv (numerator, 26);
+       numerator = d.quot;
+        tag[i] = 'A' + d.rem;
+    }
+    tag[i] = 0;
+}
+
+static cairo_int_status_t
+_cairo_pdf_surface_emit_to_unicode_stream (cairo_pdf_surface_t         *surface,
+                                          cairo_scaled_font_subset_t   *font_subset,
+                                          cairo_pdf_resource_t         *stream)
+{
+    unsigned int i, num_bfchar;
+    cairo_int_status_t status;
+
+    stream->id = 0;
+
+    status = _cairo_pdf_surface_open_stream (surface,
+                                             NULL,
+                                             surface->compress_content,
+                                             NULL);
+    if (unlikely (status))
+       return status;
+
+    _cairo_output_stream_printf (surface->output,
+                                 "/CIDInit /ProcSet findresource begin\n"
+                                 "12 dict begin\n"
+                                 "begincmap\n"
+                                 "/CIDSystemInfo\n"
+                                 "<< /Registry (Adobe)\n"
+                                 "   /Ordering (UCS)\n"
+                                 "   /Supplement 0\n"
+                                 ">> def\n"
+                                 "/CMapName /Adobe-Identity-UCS def\n"
+                                 "/CMapType 2 def\n"
+                                 "1 begincodespacerange\n");
+
+    if (font_subset->is_composite && !font_subset->is_latin) {
+        _cairo_output_stream_printf (surface->output,
+                                     "<0000> <ffff>\n");
+    } else {
+        _cairo_output_stream_printf (surface->output,
+                                     "<00> <ff>\n");
+    }
+
+    _cairo_output_stream_printf (surface->output,
+                                  "endcodespacerange\n");
+
+    if (font_subset->is_scaled) {
+       /* Type 3 fonts include glyph 0 in the subset */
+       num_bfchar = font_subset->num_glyphs;
+
+       /* The CMap specification has a limit of 100 characters per beginbfchar operator */
+       _cairo_output_stream_printf (surface->output,
+                                    "%d beginbfchar\n",
+                                    num_bfchar > 100 ? 100 : num_bfchar);
+
+       for (i = 0; i < num_bfchar; i++) {
+           if (i != 0 && i % 100 == 0) {
+               _cairo_output_stream_printf (surface->output,
+                                            "endbfchar\n"
+                                            "%d beginbfchar\n",
+                                            num_bfchar - i > 100 ? 100 : num_bfchar - i);
+           }
+           _cairo_output_stream_printf (surface->output, "<%02x> ", i);
+           status = _cairo_pdf_surface_emit_unicode_for_glyph (surface,
+                                                               font_subset->utf8[i]);
+           if (unlikely (status))
+               return status;
+
+           _cairo_output_stream_printf (surface->output,
+                                        "\n");
+       }
+    } else {
+       /* Other fonts reserve glyph 0 for .notdef. Omit glyph 0 from the /ToUnicode map */
+       num_bfchar = font_subset->num_glyphs - 1;
+
+       /* The CMap specification has a limit of 100 characters per beginbfchar operator */
+       _cairo_output_stream_printf (surface->output,
+                                    "%d beginbfchar\n",
+                                    num_bfchar > 100 ? 100 : num_bfchar);
+
+       for (i = 0; i < num_bfchar; i++) {
+           if (i != 0 && i % 100 == 0) {
+               _cairo_output_stream_printf (surface->output,
+                                            "endbfchar\n"
+                                            "%d beginbfchar\n",
+                                            num_bfchar - i > 100 ? 100 : num_bfchar - i);
+           }
+           if (font_subset->is_latin)
+               _cairo_output_stream_printf (surface->output, "<%02x> ", font_subset->to_latin_char[i + 1]);
+           else if (font_subset->is_composite)
+               _cairo_output_stream_printf (surface->output, "<%04x> ", i + 1);
+           else
+               _cairo_output_stream_printf (surface->output, "<%02x> ", i + 1);
+
+           status = _cairo_pdf_surface_emit_unicode_for_glyph (surface,
+                                                               font_subset->utf8[i + 1]);
+           if (unlikely (status))
+               return status;
+
+           _cairo_output_stream_printf (surface->output,
+                                        "\n");
+       }
+    }
+
+    _cairo_output_stream_printf (surface->output,
+                                 "endbfchar\n");
+
+    _cairo_output_stream_printf (surface->output,
+                                 "endcmap\n"
+                                 "CMapName currentdict /CMap defineresource pop\n"
+                                 "end\n"
+                                 "end\n");
+
+    *stream = surface->pdf_stream.self;
+    return _cairo_pdf_surface_close_stream (surface);
+}
+
+#define PDF_UNITS_PER_EM 1000
+
+static cairo_status_t
+_cairo_pdf_surface_emit_cff_font (cairo_pdf_surface_t          *surface,
+                                  cairo_scaled_font_subset_t   *font_subset,
+                                  cairo_cff_subset_t            *subset)
+{
+    cairo_pdf_resource_t stream, descriptor, cidfont_dict;
+    cairo_pdf_resource_t subset_resource, to_unicode_stream;
+    cairo_pdf_font_t font;
+    unsigned int i, last_glyph;
+    cairo_status_t status;
+    char tag[10];
+
+    _create_font_subset_tag (font_subset, subset->ps_name, tag);
+
+    subset_resource = _cairo_pdf_surface_get_font_resource (surface,
+                                                           font_subset->font_id,
+                                                           font_subset->subset_id);
+    if (subset_resource.id == 0)
+       return CAIRO_STATUS_SUCCESS;
+
+    status = _cairo_pdf_surface_open_stream (surface,
+                                            NULL,
+                                            TRUE,
+                                            font_subset->is_latin ?
+                                            "   /Subtype /Type1C\n" :
+                                            "   /Subtype /CIDFontType0C\n");
+    if (unlikely (status))
+       return status;
+
+    stream = surface->pdf_stream.self;
+    _cairo_output_stream_write (surface->output,
+                               subset->data, subset->data_length);
+    status = _cairo_pdf_surface_close_stream (surface);
+    if (unlikely (status))
+       return status;
+
+    status = _cairo_pdf_surface_emit_to_unicode_stream (surface,
+                                                       font_subset,
+                                                       &to_unicode_stream);
+    if (_cairo_status_is_error (status))
+       return status;
+
+    descriptor = _cairo_pdf_surface_new_object (surface);
+    if (descriptor.id == 0)
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    _cairo_output_stream_printf (surface->output,
+                                "%d 0 obj\n"
+                                "<< /Type /FontDescriptor\n"
+                                "   /FontName /%s+%s\n",
+                                descriptor.id,
+                                tag,
+                                subset->ps_name);
+
+    if (subset->family_name_utf8) {
+       char *pdf_str;
+
+       status = _utf8_to_pdf_string (subset->family_name_utf8, &pdf_str);
+       if (unlikely (status))
+           return status;
+
+       _cairo_output_stream_printf (surface->output,
+                                    "   /FontFamily %s\n",
+                                    pdf_str);
+       free (pdf_str);
+    }
+
+    _cairo_output_stream_printf (surface->output,
+                                "   /Flags 4\n"
+                                "   /FontBBox [ %ld %ld %ld %ld ]\n"
+                                "   /ItalicAngle 0\n"
+                                "   /Ascent %ld\n"
+                                "   /Descent %ld\n"
+                                "   /CapHeight %ld\n"
+                                "   /StemV 80\n"
+                                "   /StemH 80\n"
+                                "   /FontFile3 %u 0 R\n"
+                                ">>\n"
+                                "endobj\n",
+                                (long)(subset->x_min*PDF_UNITS_PER_EM),
+                                (long)(subset->y_min*PDF_UNITS_PER_EM),
+                                (long)(subset->x_max*PDF_UNITS_PER_EM),
+                                (long)(subset->y_max*PDF_UNITS_PER_EM),
+                                (long)(subset->ascent*PDF_UNITS_PER_EM),
+                                (long)(subset->descent*PDF_UNITS_PER_EM),
+                                (long)(subset->y_max*PDF_UNITS_PER_EM),
+                                stream.id);
+
+    if (font_subset->is_latin) {
+       /* find last glyph used */
+       for (i = 255; i >= 32; i--)
+           if (font_subset->latin_to_subset_glyph_index[i] > 0)
+               break;
+
+       last_glyph = i;
+       _cairo_pdf_surface_update_object (surface, subset_resource);
+       _cairo_output_stream_printf (surface->output,
+                                    "%d 0 obj\n"
+                                    "<< /Type /Font\n"
+                                    "   /Subtype /Type1\n"
+                                    "   /BaseFont /%s+%s\n"
+                                    "   /FirstChar 32\n"
+                                    "   /LastChar %d\n"
+                                    "   /FontDescriptor %d 0 R\n"
+                                    "   /Encoding /WinAnsiEncoding\n"
+                                    "   /Widths [",
+                                    subset_resource.id,
+                                    tag,
+                                    subset->ps_name,
+                                    last_glyph,
+                                    descriptor.id);
+
+       for (i = 32; i < last_glyph + 1; i++) {
+           int glyph = font_subset->latin_to_subset_glyph_index[i];
+           if (glyph > 0) {
+               _cairo_output_stream_printf (surface->output,
+                                            " %ld",
+                                            (long)(subset->widths[glyph]*PDF_UNITS_PER_EM));
+           } else {
+               _cairo_output_stream_printf (surface->output, " 0");
+           }
+       }
+
+       _cairo_output_stream_printf (surface->output,
+                                    " ]\n");
+
+       if (to_unicode_stream.id != 0)
+           _cairo_output_stream_printf (surface->output,
+                                        "    /ToUnicode %d 0 R\n",
+                                        to_unicode_stream.id);
+
+       _cairo_output_stream_printf (surface->output,
+                                    ">>\n"
+                                    "endobj\n");
+    } else {
+       cidfont_dict = _cairo_pdf_surface_new_object (surface);
+       if (cidfont_dict.id == 0)
+           return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+       _cairo_output_stream_printf (surface->output,
+                                    "%d 0 obj\n"
+                                    "<< /Type /Font\n"
+                                    "   /Subtype /CIDFontType0\n"
+                                    "   /BaseFont /%s+%s\n"
+                                    "   /CIDSystemInfo\n"
+                                    "   << /Registry (Adobe)\n"
+                                    "      /Ordering (Identity)\n"
+                                    "      /Supplement 0\n"
+                                    "   >>\n"
+                                    "   /FontDescriptor %d 0 R\n"
+                                    "   /W [0 [",
+                                    cidfont_dict.id,
+                                    tag,
+                                    subset->ps_name,
+                                    descriptor.id);
+
+       for (i = 0; i < font_subset->num_glyphs; i++)
+           _cairo_output_stream_printf (surface->output,
+                                        " %ld",
+                                        (long)(subset->widths[i]*PDF_UNITS_PER_EM));
+
+       _cairo_output_stream_printf (surface->output,
+                                    " ]]\n"
+                                    ">>\n"
+                                    "endobj\n");
+
+       _cairo_pdf_surface_update_object (surface, subset_resource);
+       _cairo_output_stream_printf (surface->output,
+                                    "%d 0 obj\n"
+                                    "<< /Type /Font\n"
+                                    "   /Subtype /Type0\n"
+                                    "   /BaseFont /%s+%s\n"
+                                    "   /Encoding /Identity-H\n"
+                                    "   /DescendantFonts [ %d 0 R]\n",
+                                    subset_resource.id,
+                                    tag,
+                                    subset->ps_name,
+                                    cidfont_dict.id);
+
+       if (to_unicode_stream.id != 0)
+           _cairo_output_stream_printf (surface->output,
+                                        "   /ToUnicode %d 0 R\n",
+                                        to_unicode_stream.id);
+
+       _cairo_output_stream_printf (surface->output,
+                                    ">>\n"
+                                    "endobj\n");
+    }
+
+    font.font_id = font_subset->font_id;
+    font.subset_id = font_subset->subset_id;
+    font.subset_resource = subset_resource;
+    status = _cairo_array_append (&surface->fonts, &font);
+
+    return status;
+}
+
+static cairo_status_t
+_cairo_pdf_surface_emit_cff_font_subset (cairo_pdf_surface_t        *surface,
+                                         cairo_scaled_font_subset_t  *font_subset)
+{
+    cairo_status_t status;
+    cairo_cff_subset_t subset;
+    char name[64];
+
+    snprintf (name, sizeof name, "CairoFont-%d-%d",
+              font_subset->font_id, font_subset->subset_id);
+    status = _cairo_cff_subset_init (&subset, name, font_subset);
+    if (unlikely (status))
+        return status;
+
+    status = _cairo_pdf_surface_emit_cff_font (surface, font_subset, &subset);
+
+    _cairo_cff_subset_fini (&subset);
+
+    return status;
+}
+
+static cairo_status_t
+_cairo_pdf_surface_emit_cff_fallback_font (cairo_pdf_surface_t        *surface,
+                                           cairo_scaled_font_subset_t  *font_subset)
+{
+    cairo_status_t status;
+    cairo_cff_subset_t subset;
+    char name[64];
+
+    /* CFF fallback subsetting does not work with 8-bit glyphs unless
+     * they are a latin subset */
+    if (!font_subset->is_composite && !font_subset->is_latin)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    snprintf (name, sizeof name, "CairoFont-%d-%d",
+              font_subset->font_id, font_subset->subset_id);
+    status = _cairo_cff_fallback_init (&subset, name, font_subset);
+    if (unlikely (status))
+        return status;
+
+    status = _cairo_pdf_surface_emit_cff_font (surface, font_subset, &subset);
+
+    _cairo_cff_fallback_fini (&subset);
+
+    return status;
+}
+
+static cairo_status_t
+_cairo_pdf_surface_emit_type1_font (cairo_pdf_surface_t                *surface,
+                                    cairo_scaled_font_subset_t *font_subset,
+                                    cairo_type1_subset_t        *subset)
+{
+    cairo_pdf_resource_t stream, descriptor, subset_resource, to_unicode_stream;
+    cairo_pdf_font_t font;
+    cairo_status_t status;
+    unsigned long length;
+    unsigned int i, last_glyph;
+    char tag[10];
+
+    _create_font_subset_tag (font_subset, subset->base_font, tag);
+
+    subset_resource = _cairo_pdf_surface_get_font_resource (surface,
+                                                           font_subset->font_id,
+                                                           font_subset->subset_id);
+    if (subset_resource.id == 0)
+       return CAIRO_STATUS_SUCCESS;
+
+    length = subset->header_length + subset->data_length + subset->trailer_length;
+    status = _cairo_pdf_surface_open_stream (surface,
+                                            NULL,
+                                            TRUE,
+                                            "   /Length1 %lu\n"
+                                            "   /Length2 %lu\n"
+                                            "   /Length3 %lu\n",
+                                            subset->header_length,
+                                            subset->data_length,
+                                            subset->trailer_length);
+    if (unlikely (status))
+       return status;
+
+    stream = surface->pdf_stream.self;
+    _cairo_output_stream_write (surface->output, subset->data, length);
+    status = _cairo_pdf_surface_close_stream (surface);
+    if (unlikely (status))
+       return status;
+
+    status = _cairo_pdf_surface_emit_to_unicode_stream (surface,
+                                                       font_subset,
+                                                       &to_unicode_stream);
+    if (_cairo_status_is_error (status))
+       return status;
+
+    last_glyph = font_subset->num_glyphs - 1;
+    if (font_subset->is_latin) {
+       /* find last glyph used */
+       for (i = 255; i >= 32; i--)
+           if (font_subset->latin_to_subset_glyph_index[i] > 0)
+               break;
+
+       last_glyph = i;
+    }
+
+    descriptor = _cairo_pdf_surface_new_object (surface);
+    if (descriptor.id == 0)
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    _cairo_output_stream_printf (surface->output,
+                                "%d 0 obj\n"
+                                "<< /Type /FontDescriptor\n"
+                                "   /FontName /%s+%s\n"
+                                "   /Flags 4\n"
+                                "   /FontBBox [ %ld %ld %ld %ld ]\n"
+                                "   /ItalicAngle 0\n"
+                                "   /Ascent %ld\n"
+                                "   /Descent %ld\n"
+                                "   /CapHeight %ld\n"
+                                "   /StemV 80\n"
+                                "   /StemH 80\n"
+                                "   /FontFile %u 0 R\n"
+                                ">>\n"
+                                "endobj\n",
+                                descriptor.id,
+                                tag,
+                                subset->base_font,
+                                (long)(subset->x_min*PDF_UNITS_PER_EM),
+                                (long)(subset->y_min*PDF_UNITS_PER_EM),
+                                (long)(subset->x_max*PDF_UNITS_PER_EM),
+                                (long)(subset->y_max*PDF_UNITS_PER_EM),
+                                (long)(subset->ascent*PDF_UNITS_PER_EM),
+                                (long)(subset->descent*PDF_UNITS_PER_EM),
+                                (long)(subset->y_max*PDF_UNITS_PER_EM),
+                                stream.id);
+
+    _cairo_pdf_surface_update_object (surface, subset_resource);
+    _cairo_output_stream_printf (surface->output,
+                                "%d 0 obj\n"
+                                "<< /Type /Font\n"
+                                "   /Subtype /Type1\n"
+                                "   /BaseFont /%s+%s\n"
+                                "   /FirstChar %d\n"
+                                "   /LastChar %d\n"
+                                "   /FontDescriptor %d 0 R\n",
+                                subset_resource.id,
+                                tag,
+                                subset->base_font,
+                                font_subset->is_latin ? 32 : 0,
+                                last_glyph,
+                                descriptor.id);
+
+    if (font_subset->is_latin)
+       _cairo_output_stream_printf (surface->output, "   /Encoding /WinAnsiEncoding\n");
+
+    _cairo_output_stream_printf (surface->output, "   /Widths [");
+    if (font_subset->is_latin) {
+       for (i = 32; i < last_glyph + 1; i++) {
+           int glyph = font_subset->latin_to_subset_glyph_index[i];
+           if (glyph > 0) {
+               _cairo_output_stream_printf (surface->output,
+                                            " %ld",
+                                            (long)(subset->widths[glyph]*PDF_UNITS_PER_EM));
+           } else {
+               _cairo_output_stream_printf (surface->output, " 0");
+           }
+       }
+    } else {
+       for (i = 0; i < font_subset->num_glyphs; i++)
+           _cairo_output_stream_printf (surface->output,
+                                        " %ld",
+                                        (long)(subset->widths[i]*PDF_UNITS_PER_EM));
+    }
+
+    _cairo_output_stream_printf (surface->output,
+                                " ]\n");
+
+    if (to_unicode_stream.id != 0)
+        _cairo_output_stream_printf (surface->output,
+                                     "    /ToUnicode %d 0 R\n",
+                                     to_unicode_stream.id);
+
+    _cairo_output_stream_printf (surface->output,
+                                ">>\n"
+                                "endobj\n");
+
+    font.font_id = font_subset->font_id;
+    font.subset_id = font_subset->subset_id;
+    font.subset_resource = subset_resource;
+    return _cairo_array_append (&surface->fonts, &font);
+}
+
+static cairo_status_t
+_cairo_pdf_surface_emit_type1_font_subset (cairo_pdf_surface_t         *surface,
+                                          cairo_scaled_font_subset_t   *font_subset)
+{
+    cairo_status_t status;
+    cairo_type1_subset_t subset;
+    char name[64];
+
+    /* 16-bit glyphs not compatible with Type 1 fonts */
+    if (font_subset->is_composite && !font_subset->is_latin)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    snprintf (name, sizeof name, "CairoFont-%d-%d",
+             font_subset->font_id, font_subset->subset_id);
+    status = _cairo_type1_subset_init (&subset, name, font_subset, FALSE);
+    if (unlikely (status))
+       return status;
+
+    status = _cairo_pdf_surface_emit_type1_font (surface, font_subset, &subset);
+
+    _cairo_type1_subset_fini (&subset);
+    return status;
+}
+
+static cairo_status_t
+_cairo_pdf_surface_emit_type1_fallback_font (cairo_pdf_surface_t       *surface,
+                                             cairo_scaled_font_subset_t        *font_subset)
+{
+    cairo_status_t status;
+    cairo_type1_subset_t subset;
+    char name[64];
+
+    /* 16-bit glyphs not compatible with Type 1 fonts */
+    if (font_subset->is_composite && !font_subset->is_latin)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    snprintf (name, sizeof name, "CairoFont-%d-%d",
+             font_subset->font_id, font_subset->subset_id);
+    status = _cairo_type1_fallback_init_binary (&subset, name, font_subset);
+    if (unlikely (status))
+       return status;
+
+    status = _cairo_pdf_surface_emit_type1_font (surface, font_subset, &subset);
+
+    _cairo_type1_fallback_fini (&subset);
+    return status;
+}
+
+static cairo_status_t
+_cairo_pdf_surface_emit_truetype_font_subset (cairo_pdf_surface_t              *surface,
+                                             cairo_scaled_font_subset_t        *font_subset)
+{
+    cairo_pdf_resource_t stream, descriptor, cidfont_dict;
+    cairo_pdf_resource_t subset_resource, to_unicode_stream;
+    cairo_status_t status;
+    cairo_pdf_font_t font;
+    cairo_truetype_subset_t subset;
+    unsigned int i, last_glyph;
+    char tag[10];
+
+    subset_resource = _cairo_pdf_surface_get_font_resource (surface,
+                                                           font_subset->font_id,
+                                                           font_subset->subset_id);
+    if (subset_resource.id == 0)
+       return CAIRO_STATUS_SUCCESS;
+
+    status = _cairo_truetype_subset_init_pdf (&subset, font_subset);
+    if (unlikely (status))
+       return status;
+
+    _create_font_subset_tag (font_subset, subset.ps_name, tag);
+
+    status = _cairo_pdf_surface_open_stream (surface,
+                                            NULL,
+                                            TRUE,
+                                            "   /Length1 %lu\n",
+                                            subset.data_length);
+    if (unlikely (status)) {
+       _cairo_truetype_subset_fini (&subset);
+       return status;
+    }
+
+    stream = surface->pdf_stream.self;
+    _cairo_output_stream_write (surface->output,
+                               subset.data, subset.data_length);
+    status = _cairo_pdf_surface_close_stream (surface);
+    if (unlikely (status)) {
+       _cairo_truetype_subset_fini (&subset);
+       return status;
+    }
+
+    status = _cairo_pdf_surface_emit_to_unicode_stream (surface,
+                                                       font_subset,
+                                                       &to_unicode_stream);
+    if (_cairo_status_is_error (status)) {
+       _cairo_truetype_subset_fini (&subset);
+       return status;
+    }
+
+    descriptor = _cairo_pdf_surface_new_object (surface);
+    if (descriptor.id == 0) {
+       _cairo_truetype_subset_fini (&subset);
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+    }
+
+    _cairo_output_stream_printf (surface->output,
+                                "%d 0 obj\n"
+                                "<< /Type /FontDescriptor\n"
+                                "   /FontName /%s+%s\n",
+                                descriptor.id,
+                                tag,
+                                subset.ps_name);
+
+    if (subset.family_name_utf8) {
+       char *pdf_str;
+
+       status = _utf8_to_pdf_string (subset.family_name_utf8, &pdf_str);
+       if (unlikely (status))
+           return status;
+
+       _cairo_output_stream_printf (surface->output,
+                                    "   /FontFamily %s\n",
+                                    pdf_str);
+       free (pdf_str);
+    }
+
+    _cairo_output_stream_printf (surface->output,
+                                "   /Flags %d\n"
+                                "   /FontBBox [ %ld %ld %ld %ld ]\n"
+                                "   /ItalicAngle 0\n"
+                                "   /Ascent %ld\n"
+                                "   /Descent %ld\n"
+                                "   /CapHeight %ld\n"
+                                "   /StemV 80\n"
+                                "   /StemH 80\n"
+                                "   /FontFile2 %u 0 R\n"
+                                ">>\n"
+                                "endobj\n",
+                                font_subset->is_latin ? 32 : 4,
+                                (long)(subset.x_min*PDF_UNITS_PER_EM),
+                                (long)(subset.y_min*PDF_UNITS_PER_EM),
+                                 (long)(subset.x_max*PDF_UNITS_PER_EM),
+                                (long)(subset.y_max*PDF_UNITS_PER_EM),
+                                (long)(subset.ascent*PDF_UNITS_PER_EM),
+                                (long)(subset.descent*PDF_UNITS_PER_EM),
+                                (long)(subset.y_max*PDF_UNITS_PER_EM),
+                                stream.id);
+
+    if (font_subset->is_latin) {
+       /* find last glyph used */
+       for (i = 255; i >= 32; i--)
+           if (font_subset->latin_to_subset_glyph_index[i] > 0)
+               break;
+
+       last_glyph = i;
+       _cairo_pdf_surface_update_object (surface, subset_resource);
+       _cairo_output_stream_printf (surface->output,
+                                    "%d 0 obj\n"
+                                    "<< /Type /Font\n"
+                                    "   /Subtype /TrueType\n"
+                                    "   /BaseFont /%s+%s\n"
+                                    "   /FirstChar 32\n"
+                                    "   /LastChar %d\n"
+                                    "   /FontDescriptor %d 0 R\n"
+                                    "   /Encoding /WinAnsiEncoding\n"
+                                    "   /Widths [",
+                                    subset_resource.id,
+                                    tag,
+                                    subset.ps_name,
+                                    last_glyph,
+                                    descriptor.id);
+
+       for (i = 32; i < last_glyph + 1; i++) {
+           int glyph = font_subset->latin_to_subset_glyph_index[i];
+           if (glyph > 0) {
+               _cairo_output_stream_printf (surface->output,
+                                            " %ld",
+                                            (long)(subset.widths[glyph]*PDF_UNITS_PER_EM));
+           } else {
+               _cairo_output_stream_printf (surface->output, " 0");
+           }
+       }
+
+       _cairo_output_stream_printf (surface->output,
+                                    " ]\n");
+
+       if (to_unicode_stream.id != 0)
+           _cairo_output_stream_printf (surface->output,
+                                        "    /ToUnicode %d 0 R\n",
+                                        to_unicode_stream.id);
+
+       _cairo_output_stream_printf (surface->output,
+                                    ">>\n"
+                                    "endobj\n");
+    } else {
+       cidfont_dict = _cairo_pdf_surface_new_object (surface);
+       if (cidfont_dict.id == 0) {
+           _cairo_truetype_subset_fini (&subset);
+           return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+       }
+
+       _cairo_output_stream_printf (surface->output,
+                                    "%d 0 obj\n"
+                                    "<< /Type /Font\n"
+                                    "   /Subtype /CIDFontType2\n"
+                                    "   /BaseFont /%s+%s\n"
+                                    "   /CIDSystemInfo\n"
+                                    "   << /Registry (Adobe)\n"
+                                    "      /Ordering (Identity)\n"
+                                    "      /Supplement 0\n"
+                                    "   >>\n"
+                                    "   /FontDescriptor %d 0 R\n"
+                                    "   /W [0 [",
+                                    cidfont_dict.id,
+                                    tag,
+                                    subset.ps_name,
+                                    descriptor.id);
+
+       for (i = 0; i < font_subset->num_glyphs; i++)
+           _cairo_output_stream_printf (surface->output,
+                                        " %ld",
+                                        (long)(subset.widths[i]*PDF_UNITS_PER_EM));
+
+       _cairo_output_stream_printf (surface->output,
+                                    " ]]\n"
+                                    ">>\n"
+                                    "endobj\n");
+
+       _cairo_pdf_surface_update_object (surface, subset_resource);
+       _cairo_output_stream_printf (surface->output,
+                                    "%d 0 obj\n"
+                                    "<< /Type /Font\n"
+                                    "   /Subtype /Type0\n"
+                                    "   /BaseFont /%s+%s\n"
+                                    "   /Encoding /Identity-H\n"
+                                    "   /DescendantFonts [ %d 0 R]\n",
+                                    subset_resource.id,
+                                    tag,
+                                    subset.ps_name,
+                                    cidfont_dict.id);
+
+       if (to_unicode_stream.id != 0)
+           _cairo_output_stream_printf (surface->output,
+                                        "   /ToUnicode %d 0 R\n",
+                                        to_unicode_stream.id);
+
+       _cairo_output_stream_printf (surface->output,
+                                    ">>\n"
+                                    "endobj\n");
+    }
+
+    font.font_id = font_subset->font_id;
+    font.subset_id = font_subset->subset_id;
+    font.subset_resource = subset_resource;
+    status = _cairo_array_append (&surface->fonts, &font);
+
+    _cairo_truetype_subset_fini (&subset);
+
+    return status;
+}
+
+static cairo_status_t
+_cairo_pdf_emit_imagemask (cairo_image_surface_t *image,
+                            cairo_output_stream_t *stream)
+{
+    uint8_t *byte, output_byte;
+    int row, col, num_cols;
+
+    /* The only image type supported by Type 3 fonts are 1-bit image
+     * masks */
+    assert (image->format == CAIRO_FORMAT_A1);
+
+    _cairo_output_stream_printf (stream,
+                                "BI\n"
+                                "/IM true\n"
+                                "/W %d\n"
+                                "/H %d\n"
+                                "/BPC 1\n"
+                                "/D [1 0]\n",
+                                image->width,
+                                image->height);
+
+    _cairo_output_stream_printf (stream,
+                                "ID ");
+
+    num_cols = (image->width + 7) / 8;
+    for (row = 0; row < image->height; row++) {
+       byte = image->data + row * image->stride;
+       for (col = 0; col < num_cols; col++) {
+           output_byte = CAIRO_BITSWAP8_IF_LITTLE_ENDIAN (*byte);
+           _cairo_output_stream_write (stream, &output_byte, 1);
+           byte++;
+       }
+    }
+
+    _cairo_output_stream_printf (stream,
+                                "\nEI\n");
+
+    return _cairo_output_stream_get_status (stream);
+}
+
+static cairo_int_status_t
+_cairo_pdf_surface_analyze_user_font_subset (cairo_scaled_font_subset_t *font_subset,
+                                            void                       *closure)
+{
+    cairo_pdf_surface_t *surface = closure;
+    cairo_status_t status = CAIRO_STATUS_SUCCESS;
+    cairo_status_t status2;
+    unsigned int i;
+    cairo_surface_t *type3_surface;
+    cairo_output_stream_t *null_stream;
+
+    null_stream = _cairo_null_stream_create ();
+    type3_surface = _cairo_type3_glyph_surface_create (font_subset->scaled_font,
+                                                      null_stream,
+                                                      _cairo_pdf_emit_imagemask,
+                                                      surface->font_subsets);
+    if (unlikely (type3_surface->status)) {
+       status2 = _cairo_output_stream_destroy (null_stream);
+       return type3_surface->status;
+    }
+
+    _cairo_type3_glyph_surface_set_font_subsets_callback (type3_surface,
+                                                         _cairo_pdf_surface_add_font,
+                                                         surface);
+
+    for (i = 0; i < font_subset->num_glyphs; i++) {
+       status = _cairo_type3_glyph_surface_analyze_glyph (type3_surface,
+                                                          font_subset->glyphs[i]);
+       if (unlikely (status))
+           break;
+    }
+
+    cairo_surface_destroy (type3_surface);
+    status2 = _cairo_output_stream_destroy (null_stream);
+    if (status == CAIRO_STATUS_SUCCESS)
+       status = status2;
+
+    return status;
+}
+
+static cairo_int_status_t
+_cairo_pdf_surface_emit_type3_font_subset (cairo_pdf_surface_t         *surface,
+                                          cairo_scaled_font_subset_t   *font_subset)
+{
+    cairo_status_t status = CAIRO_STATUS_SUCCESS;
+    cairo_pdf_resource_t *glyphs, encoding, char_procs, subset_resource, to_unicode_stream;
+    cairo_pdf_font_t font;
+    double *widths;
+    unsigned int i;
+    cairo_box_t font_bbox = {{0,0},{0,0}};
+    cairo_box_t bbox = {{0,0},{0,0}};
+    cairo_surface_t *type3_surface;
+
+    if (font_subset->num_glyphs == 0)
+       return CAIRO_STATUS_SUCCESS;
+
+    subset_resource = _cairo_pdf_surface_get_font_resource (surface,
+                                                           font_subset->font_id,
+                                                           font_subset->subset_id);
+    if (subset_resource.id == 0)
+       return CAIRO_STATUS_SUCCESS;
+
+    glyphs = _cairo_malloc_ab (font_subset->num_glyphs, sizeof (cairo_pdf_resource_t));
+    if (unlikely (glyphs == NULL))
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    widths = _cairo_malloc_ab (font_subset->num_glyphs, sizeof (double));
+    if (unlikely (widths == NULL)) {
+        free (glyphs);
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+    }
+
+    _cairo_pdf_group_resources_clear (&surface->resources);
+    type3_surface = _cairo_type3_glyph_surface_create (font_subset->scaled_font,
+                                                      NULL,
+                                                      _cairo_pdf_emit_imagemask,
+                                                      surface->font_subsets);
+    if (unlikely (type3_surface->status)) {
+        free (glyphs);
+        free (widths);
+       return type3_surface->status;
+    }
+
+    _cairo_type3_glyph_surface_set_font_subsets_callback (type3_surface,
+                                                         _cairo_pdf_surface_add_font,
+                                                         surface);
+
+    for (i = 0; i < font_subset->num_glyphs; i++) {
+       status = _cairo_pdf_surface_open_stream (surface,
+                                                NULL,
+                                                surface->compress_content,
+                                                NULL);
+       if (unlikely (status))
+           break;
+
+       glyphs[i] = surface->pdf_stream.self;
+       status = _cairo_type3_glyph_surface_emit_glyph (type3_surface,
+                                                       surface->output,
+                                                       font_subset->glyphs[i],
+                                                       &bbox,
+                                                       &widths[i]);
+       if (unlikely (status))
+           break;
+
+       status = _cairo_pdf_surface_close_stream (surface);
+       if (unlikely (status))
+           break;
+
+        if (i == 0) {
+            font_bbox.p1.x = bbox.p1.x;
+            font_bbox.p1.y = bbox.p1.y;
+            font_bbox.p2.x = bbox.p2.x;
+            font_bbox.p2.y = bbox.p2.y;
+        } else {
+            if (bbox.p1.x < font_bbox.p1.x)
+                font_bbox.p1.x = bbox.p1.x;
+            if (bbox.p1.y < font_bbox.p1.y)
+                font_bbox.p1.y = bbox.p1.y;
+            if (bbox.p2.x > font_bbox.p2.x)
+                font_bbox.p2.x = bbox.p2.x;
+            if (bbox.p2.y > font_bbox.p2.y)
+                font_bbox.p2.y = bbox.p2.y;
+        }
+    }
+    cairo_surface_destroy (type3_surface);
+    if (unlikely (status)) {
+       free (glyphs);
+       free (widths);
+       return status;
+    }
+
+    encoding = _cairo_pdf_surface_new_object (surface);
+    if (encoding.id == 0) {
+       free (glyphs);
+       free (widths);
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+    }
+
+    _cairo_output_stream_printf (surface->output,
+                                "%d 0 obj\n"
+                                "<< /Type /Encoding\n"
+                                "   /Differences [0", encoding.id);
+    for (i = 0; i < font_subset->num_glyphs; i++)
+       _cairo_output_stream_printf (surface->output,
+                                    " /%d", i);
+    _cairo_output_stream_printf (surface->output,
+                                "]\n"
+                                ">>\n"
+                                "endobj\n");
+
+    char_procs = _cairo_pdf_surface_new_object (surface);
+    if (char_procs.id == 0) {
+       free (glyphs);
+       free (widths);
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+    }
+
+    _cairo_output_stream_printf (surface->output,
+                                "%d 0 obj\n"
+                                "<<\n", char_procs.id);
+    for (i = 0; i < font_subset->num_glyphs; i++)
+       _cairo_output_stream_printf (surface->output,
+                                    " /%d %d 0 R\n",
+                                    i, glyphs[i].id);
+    _cairo_output_stream_printf (surface->output,
+                                ">>\n"
+                                "endobj\n");
+
+    free (glyphs);
+
+    status = _cairo_pdf_surface_emit_to_unicode_stream (surface,
+                                                       font_subset,
+                                                       &to_unicode_stream);
+    if (_cairo_status_is_error (status)) {
+       free (widths);
+       return status;
+    }
+
+    _cairo_pdf_surface_update_object (surface, subset_resource);
+    _cairo_output_stream_printf (surface->output,
+                                "%d 0 obj\n"
+                                "<< /Type /Font\n"
+                                "   /Subtype /Type3\n"
+                                "   /FontBBox [%f %f %f %f]\n"
+                                "   /FontMatrix [ 1 0 0 1 0 0 ]\n"
+                                "   /Encoding %d 0 R\n"
+                                "   /CharProcs %d 0 R\n"
+                                "   /FirstChar 0\n"
+                                "   /LastChar %d\n",
+                                subset_resource.id,
+                                _cairo_fixed_to_double (font_bbox.p1.x),
+                                - _cairo_fixed_to_double (font_bbox.p2.y),
+                                _cairo_fixed_to_double (font_bbox.p2.x),
+                                - _cairo_fixed_to_double (font_bbox.p1.y),
+                                encoding.id,
+                                char_procs.id,
+                                font_subset->num_glyphs - 1);
+
+    _cairo_output_stream_printf (surface->output,
+                                "   /Widths [");
+    for (i = 0; i < font_subset->num_glyphs; i++)
+       _cairo_output_stream_printf (surface->output, " %f", widths[i]);
+    _cairo_output_stream_printf (surface->output,
+                                "]\n");
+    free (widths);
+
+    _cairo_output_stream_printf (surface->output,
+                                "   /Resources\n");
+    _cairo_pdf_surface_emit_group_resources (surface, &surface->resources);
+
+    if (to_unicode_stream.id != 0)
+        _cairo_output_stream_printf (surface->output,
+                                     "    /ToUnicode %d 0 R\n",
+                                     to_unicode_stream.id);
+
+    _cairo_output_stream_printf (surface->output,
+                                ">>\n"
+                                "endobj\n");
+
+    font.font_id = font_subset->font_id;
+    font.subset_id = font_subset->subset_id;
+    font.subset_resource = subset_resource;
+    return _cairo_array_append (&surface->fonts, &font);
+}
+
+static cairo_int_status_t
+_cairo_pdf_surface_emit_unscaled_font_subset (cairo_scaled_font_subset_t *font_subset,
+                                              void                      *closure)
+{
+    cairo_pdf_surface_t *surface = closure;
+    cairo_int_status_t status;
+
+    status = _cairo_pdf_surface_emit_cff_font_subset (surface, font_subset);
+    if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+       return status;
+
+    status = _cairo_pdf_surface_emit_truetype_font_subset (surface, font_subset);
+    if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+       return status;
+
+    status = _cairo_pdf_surface_emit_type1_font_subset (surface, font_subset);
+    if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+       return status;
+
+    status = _cairo_pdf_surface_emit_cff_fallback_font (surface, font_subset);
+    if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+       return status;
+
+    status = _cairo_pdf_surface_emit_type1_fallback_font (surface, font_subset);
+    if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+       return status;
+
+    ASSERT_NOT_REACHED;
+    return CAIRO_INT_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_pdf_surface_emit_scaled_font_subset (cairo_scaled_font_subset_t *font_subset,
+                                            void                      *closure)
+{
+    cairo_pdf_surface_t *surface = closure;
+    cairo_int_status_t status;
+
+    status = _cairo_pdf_surface_emit_type3_font_subset (surface, font_subset);
+    if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+       return status;
+
+    ASSERT_NOT_REACHED;
+    return CAIRO_INT_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_pdf_surface_emit_font_subsets (cairo_pdf_surface_t *surface)
+{
+    cairo_status_t status;
+
+    status = _cairo_scaled_font_subsets_foreach_user (surface->font_subsets,
+                                                     _cairo_pdf_surface_analyze_user_font_subset,
+                                                     surface);
+    if (unlikely (status))
+       goto BAIL;
+
+    status = _cairo_scaled_font_subsets_foreach_unscaled (surface->font_subsets,
+                                                          _cairo_pdf_surface_emit_unscaled_font_subset,
+                                                          surface);
+    if (unlikely (status))
+       goto BAIL;
+
+    status = _cairo_scaled_font_subsets_foreach_scaled (surface->font_subsets,
+                                                        _cairo_pdf_surface_emit_scaled_font_subset,
+                                                        surface);
+    if (unlikely (status))
+       goto BAIL;
+
+    status = _cairo_scaled_font_subsets_foreach_user (surface->font_subsets,
+                                                     _cairo_pdf_surface_emit_scaled_font_subset,
+                                                     surface);
+
+BAIL:
+    _cairo_scaled_font_subsets_destroy (surface->font_subsets);
+    surface->font_subsets = NULL;
+
+    return status;
+}
+
+static cairo_pdf_resource_t
+_cairo_pdf_surface_write_catalog (cairo_pdf_surface_t *surface)
+{
+    cairo_pdf_resource_t catalog;
+
+    catalog = _cairo_pdf_surface_new_object (surface);
+    if (catalog.id == 0)
+       return catalog;
+
+    _cairo_output_stream_printf (surface->output,
+                                "%d 0 obj\n"
+                                "<< /Type /Catalog\n"
+                                "   /Pages %d 0 R\n"
+                                ">>\n"
+                                "endobj\n",
+                                catalog.id,
+                                surface->pages_resource.id);
+
+    return catalog;
+}
+
+static long
+_cairo_pdf_surface_write_xref (cairo_pdf_surface_t *surface)
+{
+    cairo_pdf_object_t *object;
+    int num_objects, i;
+    long offset;
+    char buffer[11];
+
+    num_objects = _cairo_array_num_elements (&surface->objects);
+
+    offset = _cairo_output_stream_get_position (surface->output);
+    _cairo_output_stream_printf (surface->output,
+                                "xref\n"
+                                "%d %d\n",
+                                0, num_objects + 1);
+
+    _cairo_output_stream_printf (surface->output,
+                                "0000000000 65535 f \n");
+    for (i = 0; i < num_objects; i++) {
+       object = _cairo_array_index (&surface->objects, i);
+       snprintf (buffer, sizeof buffer, "%010ld", object->offset);
+       _cairo_output_stream_printf (surface->output,
+                                    "%s 00000 n \n", buffer);
+    }
+
+    return offset;
+}
+
+static cairo_status_t
+_cairo_pdf_surface_write_mask_group (cairo_pdf_surface_t       *surface,
+                                    cairo_pdf_smask_group_t    *group)
+{
+    cairo_pdf_resource_t mask_group;
+    cairo_pdf_resource_t smask;
+    cairo_pdf_smask_group_t *smask_group;
+    cairo_pdf_resource_t pattern_res, gstate_res;
+    cairo_status_t status;
+    cairo_box_double_t bbox;
+
+    /* Create mask group */
+    _get_bbox_from_extents (group->height, &group->extents, &bbox);
+    status = _cairo_pdf_surface_open_group (surface, &bbox, NULL);
+    if (unlikely (status))
+       return status;
+
+    pattern_res.id = 0;
+    gstate_res.id = 0;
+    status = _cairo_pdf_surface_add_pdf_pattern (surface, group->mask, NULL,
+                                                &pattern_res, &gstate_res);
+    if (unlikely (status))
+       return status;
+
+    if (gstate_res.id != 0) {
+       smask_group = _cairo_pdf_surface_create_smask_group (surface, &group->extents);
+       if (unlikely (smask_group == NULL))
+           return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+       smask_group->width = group->width;
+       smask_group->height = group->height;
+       smask_group->operation = PDF_PAINT;
+       smask_group->source = cairo_pattern_reference (group->mask);
+       smask_group->source_res = pattern_res;
+       status = _cairo_pdf_surface_add_smask_group (surface, smask_group);
+       if (unlikely (status)) {
+           _cairo_pdf_smask_group_destroy (smask_group);
+           return status;
+       }
+
+       status = _cairo_pdf_surface_add_smask (surface, gstate_res);
+       if (unlikely (status))
+           return status;
+
+       status = _cairo_pdf_surface_add_xobject (surface, smask_group->group_res);
+       if (unlikely (status))
+           return status;
+
+       _cairo_output_stream_printf (surface->output,
+                                    "q /s%d gs /x%d Do Q\n",
+                                    gstate_res.id,
+                                    smask_group->group_res.id);
+    } else {
+       status = _cairo_pdf_surface_select_pattern (surface, group->mask, pattern_res, FALSE);
+       if (unlikely (status))
+           return status;
+
+       _cairo_output_stream_printf (surface->output,
+                                    "%f %f %f %f re f\n",
+                                    bbox.p1.x,
+                                    bbox.p1.y,
+                                    bbox.p2.x - bbox.p1.x,
+                                    bbox.p2.y - bbox.p1.y);
+
+       status = _cairo_pdf_surface_unselect_pattern (surface);
+       if (unlikely (status))
+           return status;
+    }
+
+    status = _cairo_pdf_surface_close_group (surface, &mask_group);
+    if (unlikely (status))
+       return status;
+
+    /* Create source group */
+    status = _cairo_pdf_surface_open_group (surface, &bbox, &group->source_res);
+    if (unlikely (status))
+       return status;
+
+    pattern_res.id = 0;
+    gstate_res.id = 0;
+    status = _cairo_pdf_surface_add_pdf_pattern (surface, group->source, NULL,
+                                                &pattern_res, &gstate_res);
+    if (unlikely (status))
+       return status;
+
+    if (gstate_res.id != 0) {
+       smask_group = _cairo_pdf_surface_create_smask_group (surface, &group->extents);
+       if (unlikely (smask_group == NULL))
+           return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+       smask_group->operation = PDF_PAINT;
+       smask_group->source = cairo_pattern_reference (group->source);
+       smask_group->source_res = pattern_res;
+       status = _cairo_pdf_surface_add_smask_group (surface, smask_group);
+       if (unlikely (status)) {
+           _cairo_pdf_smask_group_destroy (smask_group);
+           return status;
+       }
+
+       status = _cairo_pdf_surface_add_smask (surface, gstate_res);
+       if (unlikely (status))
+           return status;
+
+       status = _cairo_pdf_surface_add_xobject (surface, smask_group->group_res);
+       if (unlikely (status))
+           return status;
+
+       _cairo_output_stream_printf (surface->output,
+                                    "q /s%d gs /x%d Do Q\n",
+                                    gstate_res.id,
+                                    smask_group->group_res.id);
+    } else {
+       status = _cairo_pdf_surface_select_pattern (surface, group->source, pattern_res, FALSE);
+       if (unlikely (status))
+           return status;
+
+       _cairo_output_stream_printf (surface->output,
+                                    "%f %f %f %f re f\n",
+                                    bbox.p1.x,
+                                    bbox.p1.y,
+                                    bbox.p2.x - bbox.p1.x,
+                                    bbox.p2.y - bbox.p1.y);
+
+       status = _cairo_pdf_surface_unselect_pattern (surface);
+       if (unlikely (status))
+           return status;
+    }
+
+    status = _cairo_pdf_surface_close_group (surface, NULL);
+    if (unlikely (status))
+       return status;
+
+    /* Create an smask based on the alpha component of mask_group */
+    smask = _cairo_pdf_surface_new_object (surface);
+    if (smask.id == 0)
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    _cairo_output_stream_printf (surface->output,
+                                "%d 0 obj\n"
+                                "<< /Type /Mask\n"
+                                "   /S /Alpha\n"
+                                "   /G %d 0 R\n"
+                                ">>\n"
+                                "endobj\n",
+                                smask.id,
+                                mask_group.id);
+
+    /* Create a GState that uses the smask */
+    _cairo_pdf_surface_update_object (surface, group->group_res);
+    _cairo_output_stream_printf (surface->output,
+                                "%d 0 obj\n"
+                                "<< /Type /ExtGState\n"
+                                "   /SMask %d 0 R\n"
+                                "   /ca 1\n"
+                                "   /CA 1\n"
+                                "   /AIS false\n"
+                                ">>\n"
+                                "endobj\n",
+                                group->group_res.id,
+                                smask.id);
+
+    return _cairo_output_stream_get_status (surface->output);
+}
+
+static cairo_status_t
+_cairo_pdf_surface_write_smask_group (cairo_pdf_surface_t     *surface,
+                                     cairo_pdf_smask_group_t *group)
+{
+    double old_width, old_height;
+    cairo_status_t status;
+    cairo_box_double_t bbox;
+
+    old_width = surface->width;
+    old_height = surface->height;
+    _cairo_pdf_surface_set_size_internal (surface,
+                                         group->width,
+                                         group->height);
+    /* _mask is a special case that requires two groups - source
+     * and mask as well as a smask and gstate dictionary */
+    if (group->operation == PDF_MASK) {
+       status = _cairo_pdf_surface_write_mask_group (surface, group);
+       goto RESTORE_SIZE;
+    }
+
+    _get_bbox_from_extents (group->height, &group->extents, &bbox);
+    status = _cairo_pdf_surface_open_group (surface, &bbox, &group->group_res);
+    if (unlikely (status))
+       return status;
+
+    status = _cairo_pdf_surface_select_pattern (surface,
+                                               group->source,
+                                               group->source_res,
+                                               group->operation == PDF_STROKE);
+    if (unlikely (status))
+       return status;
+
+    switch (group->operation) {
+    case PDF_PAINT:
+       _cairo_output_stream_printf (surface->output,
+                                    "0 0 %f %f re f\n",
+                                    surface->width, surface->height);
+       break;
+    case PDF_MASK:
+       ASSERT_NOT_REACHED;
+       break;
+    case PDF_FILL:
+       status = _cairo_pdf_operators_fill (&surface->pdf_operators,
+                                           &group->path,
+                                           group->fill_rule);
+       break;
+    case PDF_STROKE:
+       status = _cairo_pdf_operators_stroke (&surface->pdf_operators,
+                                             &group->path,
+                                             &group->style,
+                                             &group->ctm,
+                                             &group->ctm_inverse);
+       break;
+    case PDF_SHOW_GLYPHS:
+       status = _cairo_pdf_operators_show_text_glyphs (&surface->pdf_operators,
+                                                       group->utf8, group->utf8_len,
+                                                       group->glyphs, group->num_glyphs,
+                                                       group->clusters, group->num_clusters,
+                                                       group->cluster_flags,
+                                                       group->scaled_font);
+       break;
+    }
+    if (unlikely (status))
+       return status;
+
+    status = _cairo_pdf_surface_unselect_pattern (surface);
+    if (unlikely (status))
+       return status;
+
+    status = _cairo_pdf_surface_close_group (surface, NULL);
+
+RESTORE_SIZE:
+    _cairo_pdf_surface_set_size_internal (surface,
+                                         old_width,
+                                         old_height);
+
+    return status;
+}
+
+static cairo_status_t
+_cairo_pdf_surface_write_patterns_and_smask_groups (cairo_pdf_surface_t *surface)
+{
+    cairo_pdf_pattern_t pattern;
+    cairo_pdf_smask_group_t *group;
+    cairo_pdf_source_surface_t src_surface;
+    unsigned int pattern_index, group_index, surface_index;
+    cairo_status_t status;
+
+    /* Writing out PDF_MASK groups will cause additional smask groups
+     * to be appended to surface->smask_groups. Additional patterns
+     * may also be appended to surface->patterns.
+     *
+     * Writing recording surface patterns will cause additional patterns
+     * and groups to be appended.
+     */
+    pattern_index = 0;
+    group_index = 0;
+    surface_index = 0;
+    while ((pattern_index < _cairo_array_num_elements (&surface->page_patterns)) ||
+          (group_index < _cairo_array_num_elements (&surface->smask_groups)) ||
+          (surface_index < _cairo_array_num_elements (&surface->page_surfaces)))
+    {
+       for (; group_index < _cairo_array_num_elements (&surface->smask_groups); group_index++) {
+           _cairo_array_copy_element (&surface->smask_groups, group_index, &group);
+           status = _cairo_pdf_surface_write_smask_group (surface, group);
+           if (unlikely (status))
+               return status;
+       }
+
+       for (; pattern_index < _cairo_array_num_elements (&surface->page_patterns); pattern_index++) {
+           _cairo_array_copy_element (&surface->page_patterns, pattern_index, &pattern);
+           status = _cairo_pdf_surface_emit_pattern (surface, &pattern);
+           if (unlikely (status))
+               return status;
+       }
+
+       for (; surface_index < _cairo_array_num_elements (&surface->page_surfaces); surface_index++) {
+           _cairo_array_copy_element (&surface->page_surfaces, surface_index, &src_surface);
+           status = _cairo_pdf_surface_emit_surface (surface, &src_surface);
+           if (unlikely (status))
+               return status;
+       }
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_pdf_surface_write_page (cairo_pdf_surface_t *surface)
+{
+    cairo_pdf_resource_t page, knockout, res;
+    cairo_status_t status;
+    unsigned int i, len;
+
+    _cairo_pdf_group_resources_clear (&surface->resources);
+    if (surface->has_fallback_images) {
+       cairo_rectangle_int_t extents;
+       cairo_box_double_t    bbox;
+
+       extents.x = 0;
+       extents.y = 0;
+       extents.width = ceil (surface->width);
+       extents.height = ceil (surface->height);
+       _get_bbox_from_extents (surface->height, &extents, &bbox);
+       status = _cairo_pdf_surface_open_knockout_group (surface, &bbox);
+       if (unlikely (status))
+           return status;
+
+       len = _cairo_array_num_elements (&surface->knockout_group);
+       for (i = 0; i < len; i++) {
+           _cairo_array_copy_element (&surface->knockout_group, i, &res);
+           _cairo_output_stream_printf (surface->output,
+                                        "/x%d Do\n",
+                                        res.id);
+           status = _cairo_pdf_surface_add_xobject (surface, res);
+           if (unlikely (status))
+               return status;
+       }
+       _cairo_output_stream_printf (surface->output,
+                                    "/x%d Do\n",
+                                    surface->content.id);
+       status = _cairo_pdf_surface_add_xobject (surface, surface->content);
+       if (unlikely (status))
+           return status;
+
+       status = _cairo_pdf_surface_close_group (surface, &knockout);
+       if (unlikely (status))
+           return status;
+
+       _cairo_pdf_group_resources_clear (&surface->resources);
+       status = _cairo_pdf_surface_open_content_stream (surface, NULL, NULL, FALSE);
+       if (unlikely (status))
+           return status;
+
+       _cairo_output_stream_printf (surface->output,
+                                    "/x%d Do\n",
+                                    knockout.id);
+       status = _cairo_pdf_surface_add_xobject (surface, knockout);
+       if (unlikely (status))
+           return status;
+
+       status = _cairo_pdf_surface_close_content_stream (surface);
+       if (unlikely (status))
+           return status;
+    }
+
+    page = _cairo_pdf_surface_new_object (surface);
+    if (page.id == 0)
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    _cairo_output_stream_printf (surface->output,
+                                "%d 0 obj\n"
+                                "<< /Type /Page\n"
+                                "   /Parent %d 0 R\n"
+                                "   /MediaBox [ 0 0 %f %f ]\n"
+                                "   /Contents %d 0 R\n"
+                                "   /Group <<\n"
+                                "      /Type /Group\n"
+                                "      /S /Transparency\n"
+                                "      /CS /DeviceRGB\n"
+                                "   >>\n"
+                                "   /Resources %d 0 R\n"
+                                ">>\n"
+                                "endobj\n",
+                                page.id,
+                                surface->pages_resource.id,
+                                surface->width,
+                                surface->height,
+                                surface->content.id,
+                                surface->content_resources.id);
+
+    status = _cairo_array_append (&surface->pages, &page);
+    if (unlikely (status))
+       return status;
+
+    status = _cairo_pdf_surface_write_patterns_and_smask_groups (surface);
+    if (unlikely (status))
+       return status;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_pdf_surface_analyze_surface_pattern_transparency (cairo_pdf_surface_t      *surface,
+                                                        cairo_surface_pattern_t *pattern)
+{
+    cairo_image_surface_t  *image;
+    void                  *image_extra;
+    cairo_int_status_t      status;
+    cairo_image_transparency_t transparency;
+
+    status = _cairo_surface_acquire_source_image (pattern->surface,
+                                                 &image,
+                                                 &image_extra);
+    if (unlikely (status))
+       return status;
+
+    if (image->base.status)
+       return image->base.status;
+
+    transparency = _cairo_image_analyze_transparency (image);
+    if (transparency == CAIRO_IMAGE_IS_OPAQUE)
+       status = CAIRO_STATUS_SUCCESS;
+    else
+       status = CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY;
+
+    _cairo_surface_release_source_image (pattern->surface, image, image_extra);
+
+    return status;
+}
+
+static cairo_bool_t
+_surface_pattern_supported (cairo_surface_pattern_t *pattern)
+{
+    cairo_extend_t extend;
+
+    if (pattern->surface->type == CAIRO_SURFACE_TYPE_RECORDING)
+       return TRUE;
+
+    if (pattern->surface->backend->acquire_source_image == NULL)
+       return FALSE;
+
+    /* Does an ALPHA-only source surface even make sense? Maybe, but I
+     * don't think it's worth the extra code to support it. */
+
+/* XXX: Need to write this function here...
+    content = cairo_surface_get_content (pattern->surface);
+    if (content == CAIRO_CONTENT_ALPHA)
+       return FALSE;
+*/
+
+    extend = cairo_pattern_get_extend (&pattern->base);
+    switch (extend) {
+    case CAIRO_EXTEND_NONE:
+    case CAIRO_EXTEND_REPEAT:
+    case CAIRO_EXTEND_REFLECT:
+    /* There's no point returning FALSE for EXTEND_PAD, as the image
+     * surface does not currently implement it either */
+    case CAIRO_EXTEND_PAD:
+       return TRUE;
+    }
+
+    ASSERT_NOT_REACHED;
+    return FALSE;
+}
+
+static cairo_bool_t
+_pattern_supported (const cairo_pattern_t *pattern)
+{
+    switch (pattern->type) {
+    case CAIRO_PATTERN_TYPE_SOLID:
+    case CAIRO_PATTERN_TYPE_LINEAR:
+    case CAIRO_PATTERN_TYPE_RADIAL:
+    case CAIRO_PATTERN_TYPE_MESH:      
+       return TRUE;
+
+    case CAIRO_PATTERN_TYPE_SURFACE:
+       return _surface_pattern_supported ((cairo_surface_pattern_t *) pattern);
+
+    default:
+       ASSERT_NOT_REACHED;
+       return FALSE;
+    }
+}
+
+static cairo_bool_t
+_pdf_operator_supported (cairo_operator_t op)
+{
+    switch (op) {
+    case CAIRO_OPERATOR_OVER:
+    case CAIRO_OPERATOR_MULTIPLY:
+    case CAIRO_OPERATOR_SCREEN:
+    case CAIRO_OPERATOR_OVERLAY:
+    case CAIRO_OPERATOR_DARKEN:
+    case CAIRO_OPERATOR_LIGHTEN:
+    case CAIRO_OPERATOR_COLOR_DODGE:
+    case CAIRO_OPERATOR_COLOR_BURN:
+    case CAIRO_OPERATOR_HARD_LIGHT:
+    case CAIRO_OPERATOR_SOFT_LIGHT:
+    case CAIRO_OPERATOR_DIFFERENCE:
+    case CAIRO_OPERATOR_EXCLUSION:
+    case CAIRO_OPERATOR_HSL_HUE:
+    case CAIRO_OPERATOR_HSL_SATURATION:
+    case CAIRO_OPERATOR_HSL_COLOR:
+    case CAIRO_OPERATOR_HSL_LUMINOSITY:
+       return TRUE;
+
+    default:
+    case CAIRO_OPERATOR_CLEAR:
+    case CAIRO_OPERATOR_SOURCE:
+    case CAIRO_OPERATOR_IN:
+    case CAIRO_OPERATOR_OUT:
+    case CAIRO_OPERATOR_ATOP:
+    case CAIRO_OPERATOR_DEST:
+    case CAIRO_OPERATOR_DEST_OVER:
+    case CAIRO_OPERATOR_DEST_IN:
+    case CAIRO_OPERATOR_DEST_OUT:
+    case CAIRO_OPERATOR_DEST_ATOP:
+    case CAIRO_OPERATOR_XOR:
+    case CAIRO_OPERATOR_ADD:
+    case CAIRO_OPERATOR_SATURATE:
+       return FALSE;
+    }
+}
+
+static cairo_int_status_t
+_cairo_pdf_surface_analyze_operation (cairo_pdf_surface_t  *surface,
+                                     cairo_operator_t      op,
+                                     const cairo_pattern_t      *pattern,
+                                     const cairo_rectangle_int_t        *extents)
+{
+    if (surface->force_fallbacks &&
+       surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
+    {
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+    }
+
+    if (! _pattern_supported (pattern))
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    if (_pdf_operator_supported (op)) {
+       if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) {
+           cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) pattern;
+
+           if (surface_pattern->surface->type == CAIRO_SURFACE_TYPE_RECORDING) {
+               if (pattern->extend == CAIRO_EXTEND_PAD)
+                   return CAIRO_INT_STATUS_UNSUPPORTED;
+               else
+                   return CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN;
+           }
+       }
+
+       return CAIRO_STATUS_SUCCESS;
+    }
+
+
+    /* The SOURCE operator is supported if the pattern is opaque or if
+     * there is nothing painted underneath. */
+    if (op == CAIRO_OPERATOR_SOURCE) {
+       if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) {
+           cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) pattern;
+
+           if (surface_pattern->surface->type == CAIRO_SURFACE_TYPE_RECORDING) {
+               if (_cairo_pattern_is_opaque (pattern, extents)) {
+                   return CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN;
+               } else {
+                   /* FIXME: The analysis surface does not yet have
+                    * the capability to analyze a non opaque recording
+                    * surface and mark it supported if there is
+                    * nothing underneath. For now recording surfaces of
+                    * type CONTENT_COLOR_ALPHA painted with
+                    * OPERATOR_SOURCE will result in a fallback
+                    * image. */
+
+                   return CAIRO_INT_STATUS_UNSUPPORTED;
+               }
+           } else {
+               return _cairo_pdf_surface_analyze_surface_pattern_transparency (surface,
+                                                                               surface_pattern);
+           }
+       }
+
+       if (_cairo_pattern_is_opaque (pattern, extents))
+           return CAIRO_STATUS_SUCCESS;
+       else
+           return CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY;
+    }
+
+    return CAIRO_INT_STATUS_UNSUPPORTED;
+}
+
+static cairo_bool_t
+_cairo_pdf_surface_operation_supported (cairo_pdf_surface_t  *surface,
+                                       cairo_operator_t      op,
+                                       const cairo_pattern_t      *pattern,
+                                       const cairo_rectangle_int_t *extents)
+{
+    return _cairo_pdf_surface_analyze_operation (surface, op, pattern, extents) != CAIRO_INT_STATUS_UNSUPPORTED;
+}
+
+static cairo_int_status_t
+_cairo_pdf_surface_start_fallback (cairo_pdf_surface_t *surface)
+{
+    cairo_box_double_t bbox;
+    cairo_status_t status;
+
+    status = _cairo_pdf_surface_close_content_stream (surface);
+    if (unlikely (status))
+       return status;
+
+    status = _cairo_array_append (&surface->knockout_group, &surface->content);
+    if (unlikely (status))
+       return status;
+
+    _cairo_pdf_group_resources_clear (&surface->resources);
+    bbox.p1.x = 0;
+    bbox.p1.y = 0;
+    bbox.p2.x = surface->width;
+    bbox.p2.y = surface->height;
+    return _cairo_pdf_surface_open_content_stream (surface, &bbox, NULL, TRUE);
+}
+
+/* A PDF stencil mask is an A1 mask used with the current color */
+static cairo_int_status_t
+_cairo_pdf_surface_emit_stencil_mask (cairo_pdf_surface_t   *surface,
+                                     const cairo_pattern_t *source,
+                                     const cairo_pattern_t *mask)
+{
+    cairo_status_t status;
+    cairo_surface_pattern_t *surface_pattern;
+    cairo_image_surface_t  *image;
+    void                  *image_extra;
+    cairo_image_transparency_t transparency;
+    cairo_pdf_resource_t pattern_res = {0};
+
+    if (! (source->type == CAIRO_PATTERN_TYPE_SOLID &&
+          mask->type == CAIRO_PATTERN_TYPE_SURFACE))
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    surface_pattern = (cairo_surface_pattern_t *) mask;
+    if (surface_pattern->surface->type == CAIRO_SURFACE_TYPE_RECORDING)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    status = _cairo_surface_acquire_source_image (surface_pattern->surface,
+                                                 &image,
+                                                 &image_extra);
+    if (unlikely (status))
+       return status;
+
+    if (image->base.status)
+       return image->base.status;
+
+    transparency = _cairo_image_analyze_transparency (image);
+    if (transparency != CAIRO_IMAGE_IS_OPAQUE &&
+       transparency != CAIRO_IMAGE_HAS_BILEVEL_ALPHA)
+    {
+       status = CAIRO_INT_STATUS_UNSUPPORTED;
+       goto cleanup;
+    }
+
+    status = _cairo_pdf_surface_select_pattern (surface, source,
+                                               pattern_res, FALSE);
+    if (unlikely (status))
+       return status;
+
+    status = _cairo_pdf_operators_flush (&surface->pdf_operators);
+    if (unlikely (status))
+       return status;
+
+    _cairo_output_stream_printf (surface->output, "q\n");
+    status = _cairo_pdf_surface_paint_surface_pattern (surface,
+                                                      (cairo_surface_pattern_t *) surface_pattern,
+                                                      TRUE);
+    if (unlikely (status))
+       return status;
+
+    _cairo_output_stream_printf (surface->output, "Q\n");
+
+    _cairo_surface_release_source_image (surface_pattern->surface, image, image_extra);
+    status = _cairo_output_stream_get_status (surface->output);
+
+cleanup:
+    _cairo_surface_release_source_image (surface_pattern->surface, image, image_extra);
+
+    return status;
+}
+
+static cairo_int_status_t
+_cairo_pdf_surface_set_clip (cairo_pdf_surface_t *surface,
+                            cairo_composite_rectangles_t *composite)
+{
+    cairo_clip_t *clip = composite->clip;
+
+    if (_cairo_composite_rectangles_can_reduce_clip (composite, clip))
+       clip = NULL;
+
+    if (clip == NULL) {
+       if (_cairo_composite_rectangles_can_reduce_clip (composite,
+                                                        surface->clipper.clip))
+           return CAIRO_STATUS_SUCCESS;
+    }
+
+    return _cairo_surface_clipper_set_clip (&surface->clipper, clip);
+}
+
+static cairo_int_status_t
+_cairo_pdf_surface_paint (void                 *abstract_surface,
+                         cairo_operator_t       op,
+                         const cairo_pattern_t *source,
+                         const cairo_clip_t    *clip)
+{
+    cairo_pdf_surface_t *surface = abstract_surface;
+    cairo_pdf_smask_group_t *group;
+    cairo_pdf_resource_t pattern_res, gstate_res;
+    cairo_composite_rectangles_t extents;
+    cairo_int_status_t status;
+
+    status = _cairo_composite_rectangles_init_for_paint (&extents,
+                                                        &surface->base,
+                                                        op, source, clip);
+    if (unlikely (status))
+       return status;
+
+    if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) {
+       status = _cairo_pdf_surface_analyze_operation (surface, op, source, &extents.bounded);
+       goto cleanup;
+    } else if (surface->paginated_mode == CAIRO_PAGINATED_MODE_FALLBACK) {
+       status = _cairo_pdf_surface_start_fallback (surface);
+       if (unlikely (status))
+           goto cleanup;
+    }
+
+    assert (_cairo_pdf_surface_operation_supported (surface, op, source, &extents.bounded));
+
+    status = _cairo_pdf_surface_set_clip (surface, &extents);
+    if (unlikely (status))
+       goto cleanup;
+
+    status = _cairo_pdf_surface_select_operator (surface, op);
+    if (unlikely (status))
+       goto cleanup;
+
+    status = _cairo_pdf_operators_flush (&surface->pdf_operators);
+    if (unlikely (status))
+       goto cleanup;
+
+    if (source->type == CAIRO_PATTERN_TYPE_SURFACE &&
+       source->extend == CAIRO_EXTEND_NONE)
+    {
+       _cairo_output_stream_printf (surface->output, "q\n");
+       status = _cairo_pdf_surface_paint_surface_pattern (surface,
+                                                          (cairo_surface_pattern_t *) source,
+                                                          FALSE);
+       if (unlikely (status))
+           goto cleanup;
+
+       _cairo_output_stream_printf (surface->output, "Q\n");
+       _cairo_composite_rectangles_fini (&extents);
+       return _cairo_output_stream_get_status (surface->output);
+    }
+
+    pattern_res.id = 0;
+    gstate_res.id = 0;
+    status = _cairo_pdf_surface_add_pdf_pattern (surface, source,
+                                                &extents.bounded,
+                                                &pattern_res, &gstate_res);
+    if (unlikely (status))
+       goto cleanup;
+
+    if (gstate_res.id != 0) {
+       group = _cairo_pdf_surface_create_smask_group (surface, &extents.bounded);
+       if (unlikely (group == NULL)) {
+           status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+           goto cleanup;
+       }
+
+       group->operation = PDF_PAINT;
+       status = _cairo_pattern_create_copy (&group->source, source);
+       if (unlikely (status)) {
+           _cairo_pdf_smask_group_destroy (group);
+           goto cleanup;
+       }
+       group->source_res = pattern_res;
+       status = _cairo_pdf_surface_add_smask_group (surface, group);
+       if (unlikely (status)) {
+           _cairo_pdf_smask_group_destroy (group);
+           goto cleanup;
+       }
+
+       status = _cairo_pdf_surface_add_smask (surface, gstate_res);
+       if (unlikely (status))
+           goto cleanup;
+
+       status = _cairo_pdf_surface_add_xobject (surface, group->group_res);
+       if (unlikely (status))
+           goto cleanup;
+
+       _cairo_output_stream_printf (surface->output,
+                                    "q /s%d gs /x%d Do Q\n",
+                                    gstate_res.id,
+                                    group->group_res.id);
+    } else {
+       status = _cairo_pdf_surface_select_pattern (surface, source,
+                                                   pattern_res, FALSE);
+       if (unlikely (status))
+           goto cleanup;
+
+       _cairo_output_stream_printf (surface->output,
+                                    "0 0 %f %f re f\n",
+                                    surface->width, surface->height);
+
+       status = _cairo_pdf_surface_unselect_pattern (surface);
+       if (unlikely (status))
+           goto cleanup;
+    }
+
+    _cairo_composite_rectangles_fini (&extents);
+    return _cairo_output_stream_get_status (surface->output);
+
+cleanup:
+    _cairo_composite_rectangles_fini (&extents);
+    return status;
+}
+
+static cairo_int_status_t
+_cairo_pdf_surface_mask (void                  *abstract_surface,
+                        cairo_operator_t        op,
+                        const cairo_pattern_t  *source,
+                        const cairo_pattern_t  *mask,
+                        const cairo_clip_t     *clip)
+{
+    cairo_pdf_surface_t *surface = abstract_surface;
+    cairo_pdf_smask_group_t *group;
+    cairo_composite_rectangles_t extents;
+    cairo_int_status_t status;
+    cairo_rectangle_int_t r;
+    cairo_box_t box;
+
+    status = _cairo_composite_rectangles_init_for_mask (&extents,
+                                                       &surface->base,
+                                                       op, source, mask, clip);
+    if (unlikely (status))
+       return status;
+
+    if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) {
+       cairo_status_t source_status, mask_status;
+
+       status = _cairo_pdf_surface_analyze_operation (surface, op, source, &extents.bounded);
+       if (_cairo_int_status_is_error (status))
+           goto cleanup;
+       source_status = status;
+
+       if (mask->has_component_alpha) {
+           status = CAIRO_INT_STATUS_UNSUPPORTED;
+       } else {
+           status = _cairo_pdf_surface_analyze_operation (surface, op, mask, &extents.bounded);
+           if (_cairo_int_status_is_error (status))
+               goto cleanup;
+       }
+       mask_status = status;
+
+       _cairo_composite_rectangles_fini (&extents);
+       return _cairo_analysis_surface_merge_status (source_status,
+                                                    mask_status);
+    } else if (surface->paginated_mode == CAIRO_PAGINATED_MODE_FALLBACK) {
+       status = _cairo_pdf_surface_start_fallback (surface);
+       if (unlikely (status))
+           goto cleanup;
+    }
+
+    assert (_cairo_pdf_surface_operation_supported (surface, op, source, &extents.bounded));
+    assert (_cairo_pdf_surface_operation_supported (surface, op, mask, &extents.bounded));
+
+    /* get the accurate extents */
+    status = _cairo_pattern_get_ink_extents (source, &r);
+    if (unlikely (status))
+       goto cleanup;
+
+    /* XXX slight impedance mismatch */
+    _cairo_box_from_rectangle (&box, &r);
+    status = _cairo_composite_rectangles_intersect_source_extents (&extents,
+                                                                  &box);
+    if (unlikely (status))
+       goto cleanup;
+
+    status = _cairo_pattern_get_ink_extents (mask, &r);
+    if (unlikely (status))
+       goto cleanup;
+
+    _cairo_box_from_rectangle (&box, &r);
+    status = _cairo_composite_rectangles_intersect_mask_extents (&extents,
+                                                                &box);
+    if (unlikely (status))
+       goto cleanup;
+
+    status = _cairo_pdf_surface_set_clip (surface, &extents);
+    if (unlikely (status))
+       goto cleanup;
+
+    status = _cairo_pdf_surface_select_operator (surface, op);
+    if (unlikely (status))
+       goto cleanup;
+
+    /* Check if we can use a stencil mask */
+    status = _cairo_pdf_surface_emit_stencil_mask (surface, source, mask);
+    if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+       goto cleanup;
+
+    group = _cairo_pdf_surface_create_smask_group (surface, &extents.bounded);
+    if (unlikely (group == NULL)) {
+       status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+       goto cleanup;
+    }
+
+    group->operation = PDF_MASK;
+    status = _cairo_pattern_create_copy (&group->source, source);
+    if (unlikely (status)) {
+       _cairo_pdf_smask_group_destroy (group);
+       goto cleanup;
+    }
+    status = _cairo_pattern_create_copy (&group->mask, mask);
+    if (unlikely (status)) {
+       _cairo_pdf_smask_group_destroy (group);
+       goto cleanup;
+    }
+    group->source_res = _cairo_pdf_surface_new_object (surface);
+    if (group->source_res.id == 0) {
+       _cairo_pdf_smask_group_destroy (group);
+       status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+       goto cleanup;
+    }
+
+    status = _cairo_pdf_surface_add_smask_group (surface, group);
+    if (unlikely (status)) {
+       _cairo_pdf_smask_group_destroy (group);
+       goto cleanup;
+    }
+
+    status = _cairo_pdf_surface_add_smask (surface, group->group_res);
+    if (unlikely (status))
+       goto cleanup;
+
+    status = _cairo_pdf_surface_add_xobject (surface, group->source_res);
+    if (unlikely (status))
+       goto cleanup;
+
+    status = _cairo_pdf_operators_flush (&surface->pdf_operators);
+    if (unlikely (status))
+       goto cleanup;
+
+    _cairo_output_stream_printf (surface->output,
+                                "q /s%d gs /x%d Do Q\n",
+                                group->group_res.id,
+                                group->source_res.id);
+
+    _cairo_composite_rectangles_fini (&extents);
+    return _cairo_output_stream_get_status (surface->output);
+
+cleanup:
+    _cairo_composite_rectangles_fini (&extents);
+    return status;
+}
+
+static cairo_int_status_t
+_cairo_pdf_surface_stroke (void                        *abstract_surface,
+                          cairo_operator_t      op,
+                          const cairo_pattern_t *source,
+                          const cairo_path_fixed_t     *path,
+                          const cairo_stroke_style_t   *style,
+                          const cairo_matrix_t *ctm,
+                          const cairo_matrix_t *ctm_inverse,
+                          double                tolerance,
+                          cairo_antialias_t     antialias,
+                          const cairo_clip_t   *clip)
+{
+    cairo_pdf_surface_t *surface = abstract_surface;
+    cairo_pdf_smask_group_t *group;
+    cairo_pdf_resource_t pattern_res, gstate_res;
+    cairo_composite_rectangles_t extents;
+    cairo_int_status_t status;
+
+    status = _cairo_composite_rectangles_init_for_stroke (&extents,
+                                                         &surface->base,
+                                                         op, source,
+                                                         path, style, ctm,
+                                                         clip);
+    if (unlikely (status))
+       return status;
+
+    /* use the more accurate extents */
+    if (extents.is_bounded) {
+       cairo_rectangle_int_t mask;
+       cairo_box_t box;
+
+       status = _cairo_path_fixed_stroke_extents (path, style,
+                                                  ctm, ctm_inverse,
+                                                  tolerance,
+                                                  &mask);
+       if (unlikely (status))
+           goto cleanup;
+
+       _cairo_box_from_rectangle (&box, &mask);
+       status = _cairo_composite_rectangles_intersect_mask_extents (&extents,
+                                                                    &box);
+       if (unlikely (status))
+           goto cleanup;
+    }
+
+    if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) {
+       status = _cairo_pdf_surface_analyze_operation (surface, op, source, &extents.bounded);
+       goto cleanup;
+    }
+
+    assert (_cairo_pdf_surface_operation_supported (surface, op, source, &extents.bounded));
+
+    status = _cairo_pdf_surface_set_clip (surface, &extents);
+    if (unlikely (status))
+       goto cleanup;
+
+    pattern_res.id = 0;
+    gstate_res.id = 0;
+    status = _cairo_pdf_surface_add_pdf_pattern (surface, source,
+                                                &extents.bounded,
+                                                &pattern_res, &gstate_res);
+    if (unlikely (status))
+       goto cleanup;
+
+    status = _cairo_pdf_surface_select_operator (surface, op);
+    if (unlikely (status))
+       goto cleanup;
+
+    if (gstate_res.id != 0) {
+       group = _cairo_pdf_surface_create_smask_group (surface, &extents.bounded);
+       if (unlikely (group == NULL)) {
+           status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+           goto cleanup;
+       }
+
+       group->operation = PDF_STROKE;
+       status = _cairo_pattern_create_copy (&group->source, source);
+       if (unlikely (status)) {
+           _cairo_pdf_smask_group_destroy (group);
+           goto cleanup;
+       }
+       group->source_res = pattern_res;
+       status = _cairo_path_fixed_init_copy (&group->path, path);
+       if (unlikely (status)) {
+           _cairo_pdf_smask_group_destroy (group);
+           goto cleanup;
+       }
+
+       group->style = *style;
+       group->ctm = *ctm;
+       group->ctm_inverse = *ctm_inverse;
+       status = _cairo_pdf_surface_add_smask_group (surface, group);
+       if (unlikely (status)) {
+           _cairo_pdf_smask_group_destroy (group);
+           goto cleanup;
+       }
+
+       status = _cairo_pdf_surface_add_smask (surface, gstate_res);
+       if (unlikely (status))
+           goto cleanup;
+
+       status = _cairo_pdf_surface_add_xobject (surface, group->group_res);
+       if (unlikely (status))
+           goto cleanup;
+
+       status = _cairo_pdf_operators_flush (&surface->pdf_operators);
+       if (unlikely (status))
+           goto cleanup;
+
+       _cairo_output_stream_printf (surface->output,
+                                    "q /s%d gs /x%d Do Q\n",
+                                    gstate_res.id,
+                                    group->group_res.id);
+    } else {
+       status = _cairo_pdf_surface_select_pattern (surface, source, pattern_res, TRUE);
+       if (unlikely (status))
+           goto cleanup;
+
+       status = _cairo_pdf_operators_stroke (&surface->pdf_operators,
+                                             path,
+                                             style,
+                                             ctm,
+                                             ctm_inverse);
+       if (unlikely (status))
+           goto cleanup;
+
+       status = _cairo_pdf_surface_unselect_pattern (surface);
+       if (unlikely (status))
+           goto cleanup;
+    }
+
+    _cairo_composite_rectangles_fini (&extents);
+    return _cairo_output_stream_get_status (surface->output);
+
+cleanup:
+    _cairo_composite_rectangles_fini (&extents);
+    return status;
+}
+
+static cairo_int_status_t
+_cairo_pdf_surface_fill (void                  *abstract_surface,
+                        cairo_operator_t        op,
+                        const cairo_pattern_t  *source,
+                        const cairo_path_fixed_t*path,
+                        cairo_fill_rule_t       fill_rule,
+                        double                  tolerance,
+                        cairo_antialias_t       antialias,
+                        const cairo_clip_t     *clip)
+{
+    cairo_pdf_surface_t *surface = abstract_surface;
+    cairo_int_status_t status;
+    cairo_pdf_smask_group_t *group;
+    cairo_pdf_resource_t pattern_res, gstate_res;
+    cairo_composite_rectangles_t extents;
+
+    status = _cairo_composite_rectangles_init_for_fill (&extents,
+                                                       &surface->base,
+                                                       op, source, path,
+                                                       clip);
+    if (unlikely (status))
+       return status;
+
+    /* use the more accurate extents */
+    if (extents.is_bounded) {
+       cairo_rectangle_int_t mask;
+       cairo_box_t box;
+
+       _cairo_path_fixed_fill_extents (path,
+                                       fill_rule,
+                                       tolerance,
+                                       &mask);
+
+       _cairo_box_from_rectangle (&box, &mask);
+       status = _cairo_composite_rectangles_intersect_mask_extents (&extents,
+                                                                    &box);
+       if (unlikely (status))
+           goto cleanup;
+    }
+
+    if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) {
+       status = _cairo_pdf_surface_analyze_operation (surface, op, source, &extents.bounded);
+       goto cleanup;
+    } else if (surface->paginated_mode == CAIRO_PAGINATED_MODE_FALLBACK) {
+       status = _cairo_pdf_surface_start_fallback (surface);
+       if (unlikely (status))
+           goto cleanup;
+    }
+
+    assert (_cairo_pdf_surface_operation_supported (surface, op, source, &extents.bounded));
+
+    status = _cairo_pdf_surface_set_clip (surface, &extents);
+    if (unlikely (status))
+       goto cleanup;
+
+    status = _cairo_pdf_surface_select_operator (surface, op);
+    if (unlikely (status))
+       goto cleanup;
+
+    if (source->type == CAIRO_PATTERN_TYPE_SURFACE &&
+       source->extend == CAIRO_EXTEND_NONE)
+    {
+       status = _cairo_pdf_operators_flush (&surface->pdf_operators);
+       if (unlikely (status))
+           goto cleanup;
+
+       _cairo_output_stream_printf (surface->output, "q\n");
+       status =  _cairo_pdf_operators_clip (&surface->pdf_operators,
+                                            path,
+                                            fill_rule);
+       if (unlikely (status))
+           goto cleanup;
+
+       status = _cairo_pdf_surface_paint_surface_pattern (surface,
+                                                          (cairo_surface_pattern_t *) source,
+                                                          FALSE);
+       if (unlikely (status))
+           goto cleanup;
+
+       _cairo_output_stream_printf (surface->output, "Q\n");
+       status = _cairo_output_stream_get_status (surface->output);
+       goto cleanup;
+    }
+
+    pattern_res.id = 0;
+    gstate_res.id = 0;
+    status = _cairo_pdf_surface_add_pdf_pattern (surface, source,
+                                                &extents.bounded,
+                                                &pattern_res, &gstate_res);
+    if (unlikely (status))
+       goto cleanup;
+
+    if (gstate_res.id != 0) {
+       group = _cairo_pdf_surface_create_smask_group (surface, &extents.bounded);
+       if (unlikely (group == NULL)) {
+           status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+           goto cleanup;
+       }
+
+       group->operation = PDF_FILL;
+       status = _cairo_pattern_create_copy (&group->source, source);
+       if (unlikely (status)) {
+           _cairo_pdf_smask_group_destroy (group);
+           goto cleanup;
+       }
+       group->source_res = pattern_res;
+       status = _cairo_path_fixed_init_copy (&group->path, path);
+       if (unlikely (status)) {
+           _cairo_pdf_smask_group_destroy (group);
+           goto cleanup;
+       }
+
+       group->fill_rule = fill_rule;
+       status = _cairo_pdf_surface_add_smask_group (surface, group);
+       if (unlikely (status)) {
+           _cairo_pdf_smask_group_destroy (group);
+           goto cleanup;
+       }
+
+       status = _cairo_pdf_surface_add_smask (surface, gstate_res);
+       if (unlikely (status))
+           goto cleanup;
+
+       status = _cairo_pdf_surface_add_xobject (surface, group->group_res);
+       if (unlikely (status))
+           goto cleanup;
+
+       status = _cairo_pdf_operators_flush (&surface->pdf_operators);
+       if (unlikely (status))
+           goto cleanup;
+
+       _cairo_output_stream_printf (surface->output,
+                                    "q /s%d gs /x%d Do Q\n",
+                                    gstate_res.id,
+                                    group->group_res.id);
+    } else {
+       status = _cairo_pdf_surface_select_pattern (surface, source, pattern_res, FALSE);
+       if (unlikely (status))
+           goto cleanup;
+
+       status = _cairo_pdf_operators_fill (&surface->pdf_operators,
+                                           path,
+                                           fill_rule);
+       if (unlikely (status))
+           goto cleanup;
+
+       status = _cairo_pdf_surface_unselect_pattern (surface);
+       if (unlikely (status))
+           goto cleanup;
+    }
+
+    _cairo_composite_rectangles_fini (&extents);
+    return _cairo_output_stream_get_status (surface->output);
+
+cleanup:
+    _cairo_composite_rectangles_fini (&extents);
+    return status;
+}
+
+static cairo_int_status_t
+_cairo_pdf_surface_fill_stroke (void                   *abstract_surface,
+                               cairo_operator_t         fill_op,
+                               const cairo_pattern_t   *fill_source,
+                               cairo_fill_rule_t        fill_rule,
+                               double                   fill_tolerance,
+                               cairo_antialias_t        fill_antialias,
+                               const cairo_path_fixed_t*path,
+                               cairo_operator_t         stroke_op,
+                               const cairo_pattern_t   *stroke_source,
+                               const cairo_stroke_style_t *stroke_style,
+                               const cairo_matrix_t    *stroke_ctm,
+                               const cairo_matrix_t    *stroke_ctm_inverse,
+                               double                   stroke_tolerance,
+                               cairo_antialias_t        stroke_antialias,
+                               const cairo_clip_t      *clip)
+{
+    cairo_pdf_surface_t *surface = abstract_surface;
+    cairo_int_status_t status;
+    cairo_pdf_resource_t fill_pattern_res, stroke_pattern_res, gstate_res;
+    cairo_composite_rectangles_t extents;
+
+    /* During analysis we return unsupported and let the _fill and
+     * _stroke functions that are on the fallback path do the analysis
+     * for us. During render we may still encounter unsupported
+     * combinations of fill/stroke patterns. However we can return
+     * unsupported anytime to let the _fill and _stroke functions take
+     * over.
+     */
+    if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    /* PDF rendering of fill-stroke is not the same as cairo when
+     * either the fill or stroke is not opaque.
+     */
+    if ( !_cairo_pattern_is_opaque (fill_source, NULL) ||
+        !_cairo_pattern_is_opaque (stroke_source, NULL))
+    {
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+    }
+
+    if (fill_op != stroke_op)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    /* Compute the operation extents using the stroke which will naturally
+     * be larger than the fill extents.
+     */
+    status = _cairo_composite_rectangles_init_for_stroke (&extents,
+                                                         &surface->base,
+                                                         stroke_op, stroke_source,
+                                                         path, stroke_style, stroke_ctm,
+                                                         clip);
+    if (unlikely (status))
+       return status;
+
+    /* use the more accurate extents */
+    if (extents.is_bounded) {
+       cairo_rectangle_int_t mask;
+       cairo_box_t box;
+
+       status = _cairo_path_fixed_stroke_extents (path, stroke_style,
+                                                  stroke_ctm, stroke_ctm_inverse,
+                                                  stroke_tolerance,
+                                                  &mask);
+       if (unlikely (status))
+           goto cleanup;
+
+       _cairo_box_from_rectangle (&box, &mask);
+       status = _cairo_composite_rectangles_intersect_mask_extents (&extents,
+                                                                    &box);
+       if (unlikely (status))
+           goto cleanup;
+    }
+
+    status = _cairo_pdf_surface_set_clip (surface, &extents);
+    if (unlikely (status))
+       goto cleanup;
+
+    status = _cairo_pdf_surface_select_operator (surface, fill_op);
+    if (unlikely (status))
+       goto cleanup;
+
+    /* use the more accurate extents */
+    if (extents.is_bounded) {
+       cairo_rectangle_int_t mask;
+       cairo_box_t box;
+
+       _cairo_path_fixed_fill_extents (path,
+                                       fill_rule,
+                                       fill_tolerance,
+                                       &mask);
+
+       _cairo_box_from_rectangle (&box, &mask);
+       status = _cairo_composite_rectangles_intersect_mask_extents (&extents,
+                                                                    &box);
+       if (unlikely (status))
+           goto cleanup;
+    }
+
+    fill_pattern_res.id = 0;
+    gstate_res.id = 0;
+    status = _cairo_pdf_surface_add_pdf_pattern (surface, fill_source,
+                                                &extents.bounded,
+                                                &fill_pattern_res,
+                                                &gstate_res);
+    if (unlikely (status))
+       goto cleanup;
+
+    assert (gstate_res.id == 0);
+
+    stroke_pattern_res.id = 0;
+    gstate_res.id = 0;
+    status = _cairo_pdf_surface_add_pdf_pattern (surface,
+                                                stroke_source,
+                                                &extents.bounded,
+                                                &stroke_pattern_res,
+                                                &gstate_res);
+    if (unlikely (status))
+       goto cleanup;
+
+    assert (gstate_res.id == 0);
+
+    /* As PDF has separate graphics state for fill and stroke we can
+     * select both at the same time */
+    status = _cairo_pdf_surface_select_pattern (surface, fill_source,
+                                               fill_pattern_res, FALSE);
+    if (unlikely (status))
+       goto cleanup;
+
+    status = _cairo_pdf_surface_select_pattern (surface, stroke_source,
+                                               stroke_pattern_res, TRUE);
+    if (unlikely (status))
+       goto cleanup;
+
+    status = _cairo_pdf_operators_fill_stroke (&surface->pdf_operators,
+                                              path,
+                                              fill_rule,
+                                              stroke_style,
+                                              stroke_ctm,
+                                              stroke_ctm_inverse);
+    if (unlikely (status))
+       goto cleanup;
+
+    status = _cairo_pdf_surface_unselect_pattern (surface);
+    if (unlikely (status))
+       goto cleanup;
+
+    _cairo_composite_rectangles_fini (&extents);
+    return _cairo_output_stream_get_status (surface->output);
+
+cleanup:
+    _cairo_composite_rectangles_fini (&extents);
+    return status;
+}
+
+static cairo_bool_t
+_cairo_pdf_surface_has_show_text_glyphs        (void                   *abstract_surface)
+{
+    return TRUE;
+}
+
+static cairo_int_status_t
+_cairo_pdf_surface_show_text_glyphs (void                      *abstract_surface,
+                                    cairo_operator_t            op,
+                                    const cairo_pattern_t      *source,
+                                    const char                 *utf8,
+                                    int                         utf8_len,
+                                    cairo_glyph_t              *glyphs,
+                                    int                         num_glyphs,
+                                    const cairo_text_cluster_t *clusters,
+                                    int                         num_clusters,
+                                    cairo_text_cluster_flags_t  cluster_flags,
+                                    cairo_scaled_font_t        *scaled_font,
+                                    const cairo_clip_t         *clip)
+{
+    cairo_pdf_surface_t *surface = abstract_surface;
+    cairo_pdf_smask_group_t *group;
+    cairo_pdf_resource_t pattern_res, gstate_res;
+    cairo_composite_rectangles_t extents;
+    cairo_bool_t overlap;
+    cairo_int_status_t status;
+
+    status = _cairo_composite_rectangles_init_for_glyphs (&extents,
+                                                         &surface->base,
+                                                         op, source,
+                                                         scaled_font,
+                                                         glyphs, num_glyphs,
+                                                         clip,
+                                                         &overlap);
+    if (unlikely (status))
+       return status;
+
+    if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) {
+       status = _cairo_pdf_surface_analyze_operation (surface, op, source, &extents.bounded);
+       goto cleanup;
+    }
+
+    assert (_cairo_pdf_surface_operation_supported (surface, op, source, &extents.bounded));
+
+    status = _cairo_pdf_surface_set_clip (surface, &extents);
+    if (unlikely (status))
+       goto cleanup;
+
+    pattern_res.id = 0;
+    gstate_res.id = 0;
+    status = _cairo_pdf_surface_add_pdf_pattern (surface, source,
+                                                &extents.bounded,
+                                                &pattern_res, &gstate_res);
+    if (unlikely (status))
+       goto cleanup;
+
+    status = _cairo_pdf_surface_select_operator (surface, op);
+    if (unlikely (status))
+       goto cleanup;
+
+    if (gstate_res.id != 0) {
+       group = _cairo_pdf_surface_create_smask_group (surface, &extents.bounded);
+       if (unlikely (group == NULL)) {
+           status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+           goto cleanup;
+       }
+
+       group->operation = PDF_SHOW_GLYPHS;
+       status = _cairo_pattern_create_copy (&group->source, source);
+       if (unlikely (status)) {
+           _cairo_pdf_smask_group_destroy (group);
+           goto cleanup;
+       }
+       group->source_res = pattern_res;
+
+       if (utf8_len) {
+           group->utf8 = malloc (utf8_len);
+           if (unlikely (group->utf8 == NULL)) {
+               _cairo_pdf_smask_group_destroy (group);
+               status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+               goto cleanup;
+           }
+           memcpy (group->utf8, utf8, utf8_len);
+       }
+       group->utf8_len = utf8_len;
+
+       if (num_glyphs) {
+           group->glyphs = _cairo_malloc_ab (num_glyphs, sizeof (cairo_glyph_t));
+           if (unlikely (group->glyphs == NULL)) {
+               _cairo_pdf_smask_group_destroy (group);
+               status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+               goto cleanup;
+           }
+           memcpy (group->glyphs, glyphs, sizeof (cairo_glyph_t) * num_glyphs);
+       }
+       group->num_glyphs = num_glyphs;
+
+       if (num_clusters) {
+           group->clusters = _cairo_malloc_ab (num_clusters, sizeof (cairo_text_cluster_t));
+           if (unlikely (group->clusters == NULL)) {
+               _cairo_pdf_smask_group_destroy (group);
+               status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+               goto cleanup;
+           }
+           memcpy (group->clusters, clusters, sizeof (cairo_text_cluster_t) * num_clusters);
+       }
+       group->num_clusters = num_clusters;
+
+       group->scaled_font = cairo_scaled_font_reference (scaled_font);
+       status = _cairo_pdf_surface_add_smask_group (surface, group);
+       if (unlikely (status)) {
+           _cairo_pdf_smask_group_destroy (group);
+           goto cleanup;
+       }
+
+       status = _cairo_pdf_surface_add_smask (surface, gstate_res);
+       if (unlikely (status))
+           goto cleanup;
+
+       status = _cairo_pdf_surface_add_xobject (surface, group->group_res);
+       if (unlikely (status))
+           goto cleanup;
+
+       status = _cairo_pdf_operators_flush (&surface->pdf_operators);
+       if (unlikely (status))
+           goto cleanup;
+
+       _cairo_output_stream_printf (surface->output,
+                                    "q /s%d gs /x%d Do Q\n",
+                                    gstate_res.id,
+                                    group->group_res.id);
+    } else {
+       status = _cairo_pdf_surface_select_pattern (surface, source, pattern_res, FALSE);
+       if (unlikely (status))
+           goto cleanup;
+
+       /* Each call to show_glyphs() with a transclucent pattern must
+        * be in a separate text object otherwise overlapping text
+        * from separate calls to show_glyphs will not composite with
+        * each other. */
+       if (! _cairo_pattern_is_opaque (source, &extents.bounded)) {
+           status = _cairo_pdf_operators_flush (&surface->pdf_operators);
+           if (unlikely (status))
+               goto cleanup;
+       }
+
+       status = _cairo_pdf_operators_show_text_glyphs (&surface->pdf_operators,
+                                                       utf8, utf8_len,
+                                                       glyphs, num_glyphs,
+                                                       clusters, num_clusters,
+                                                       cluster_flags,
+                                                       scaled_font);
+       if (unlikely (status))
+           goto cleanup;
+
+       status = _cairo_pdf_surface_unselect_pattern (surface);
+       if (unlikely (status))
+           goto cleanup;
+    }
+
+    _cairo_composite_rectangles_fini (&extents);
+    return _cairo_output_stream_get_status (surface->output);
+
+cleanup:
+    _cairo_composite_rectangles_fini (&extents);
+    return status;
+}
+
+
+static void
+_cairo_pdf_surface_set_paginated_mode (void                    *abstract_surface,
+                                      cairo_paginated_mode_t    paginated_mode)
+{
+    cairo_pdf_surface_t *surface = abstract_surface;
+
+    surface->paginated_mode = paginated_mode;
+}
+
+static const cairo_surface_backend_t cairo_pdf_surface_backend = {
+    CAIRO_SURFACE_TYPE_PDF,
+    _cairo_pdf_surface_finish,
+
+    _cairo_default_context_create,
+
+    NULL, /* create similar: handled by wrapper */
+    NULL, /* create similar image */
+    NULL, /* map to image */
+    NULL, /* unmap image */
+
+    NULL, /* acquire_source_image */
+    NULL, /* release_source_image */
+    NULL, /* snapshot */
+
+    NULL,  /* _cairo_pdf_surface_copy_page */
+    _cairo_pdf_surface_show_page,
+
+    _cairo_pdf_surface_get_extents,
+    _cairo_pdf_surface_get_font_options,
+
+    NULL, /* flush */
+    NULL, /* mark_dirty_rectangle */
+
+    /* Here are the drawing functions */
+    _cairo_pdf_surface_paint,
+    _cairo_pdf_surface_mask,
+    _cairo_pdf_surface_stroke,
+    _cairo_pdf_surface_fill,
+    _cairo_pdf_surface_fill_stroke,
+    NULL, /* show_glyphs */
+    _cairo_pdf_surface_has_show_text_glyphs,
+    _cairo_pdf_surface_show_text_glyphs,
+};
+
+static const cairo_paginated_surface_backend_t
+cairo_pdf_surface_paginated_backend = {
+    _cairo_pdf_surface_start_page,
+    _cairo_pdf_surface_set_paginated_mode,
+    NULL, /* set_bounding_box */
+    _cairo_pdf_surface_has_fallback_images,
+    _cairo_pdf_surface_supports_fine_grained_fallbacks,
+};
diff --git a/src/cairo-pdf.h b/src/cairo-pdf.h
new file mode 100644 (file)
index 0000000..50460cc
--- /dev/null
@@ -0,0 +1,94 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *     Carl D. Worth <cworth@cworth.org>
+ */
+
+#ifndef CAIRO_PDF_H
+#define CAIRO_PDF_H
+
+#include "cairo.h"
+
+#if CAIRO_HAS_PDF_SURFACE
+
+CAIRO_BEGIN_DECLS
+
+/**
+ * cairo_pdf_version_t:
+ * @CAIRO_PDF_VERSION_1_4: The version 1.4 of the PDF specification.
+ * @CAIRO_PDF_VERSION_1_5: The version 1.5 of the PDF specification.
+ *
+ * #cairo_pdf_version_t is used to describe the version number of the PDF
+ * specification that a generated PDF file will conform to.
+ *
+ * Since 1.10
+ */
+typedef enum _cairo_pdf_version {
+    CAIRO_PDF_VERSION_1_4,
+    CAIRO_PDF_VERSION_1_5
+} cairo_pdf_version_t;
+
+cairo_public cairo_surface_t *
+cairo_pdf_surface_create (const char           *filename,
+                         double                 width_in_points,
+                         double                 height_in_points);
+
+cairo_public 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_public void
+cairo_pdf_surface_restrict_to_version (cairo_surface_t                 *surface,
+                                      cairo_pdf_version_t       version);
+
+cairo_public void
+cairo_pdf_get_versions (cairo_pdf_version_t const      **versions,
+                        int                             *num_versions);
+
+cairo_public const char *
+cairo_pdf_version_to_string (cairo_pdf_version_t version);
+
+cairo_public void
+cairo_pdf_surface_set_size (cairo_surface_t    *surface,
+                           double               width_in_points,
+                           double               height_in_points);
+
+CAIRO_END_DECLS
+
+#else  /* CAIRO_HAS_PDF_SURFACE */
+# error Cairo was not compiled with support for the pdf backend
+#endif /* CAIRO_HAS_PDF_SURFACE */
+
+#endif /* CAIRO_PDF_H */
diff --git a/src/cairo-pen.c b/src/cairo-pen.c
new file mode 100644 (file)
index 0000000..e71f7b5
--- /dev/null
@@ -0,0 +1,398 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ * Copyright © 2008 Chris Wilson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *     Carl D. Worth <cworth@cworth.org>
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-error-private.h"
+#include "cairo-slope-private.h"
+
+static int
+_cairo_pen_vertices_needed (double tolerance,
+                           double radius,
+                           const cairo_matrix_t *matrix);
+
+static void
+_cairo_pen_compute_slopes (cairo_pen_t *pen);
+
+cairo_status_t
+_cairo_pen_init (cairo_pen_t   *pen,
+                double          radius,
+                double          tolerance,
+                const cairo_matrix_t   *ctm)
+{
+    int i;
+    int reflect;
+
+    if (CAIRO_INJECT_FAULT ())
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    VG (VALGRIND_MAKE_MEM_UNDEFINED (pen, sizeof (cairo_pen_t)));
+
+    pen->radius = radius;
+    pen->tolerance = tolerance;
+
+    reflect = _cairo_matrix_compute_determinant (ctm) < 0.;
+
+    pen->num_vertices = _cairo_pen_vertices_needed (tolerance,
+                                                   radius,
+                                                   ctm);
+
+    if (pen->num_vertices > ARRAY_LENGTH (pen->vertices_embedded)) {
+       pen->vertices = _cairo_malloc_ab (pen->num_vertices,
+                                         sizeof (cairo_pen_vertex_t));
+       if (unlikely (pen->vertices == NULL))
+           return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+    } else {
+       pen->vertices = pen->vertices_embedded;
+    }
+
+    /*
+     * Compute pen coordinates.  To generate the right ellipse, compute points around
+     * a circle in user space and transform them to device space.  To get a consistent
+     * orientation in device space, flip the pen if the transformation matrix
+     * is reflecting
+     */
+    for (i=0; i < pen->num_vertices; i++) {
+       double theta = 2 * M_PI * i / (double) pen->num_vertices;
+       double dx = radius * cos (reflect ? -theta : theta);
+       double dy = radius * sin (reflect ? -theta : theta);
+       cairo_pen_vertex_t *v = &pen->vertices[i];
+       cairo_matrix_transform_distance (ctm, &dx, &dy);
+       v->point.x = _cairo_fixed_from_double (dx);
+       v->point.y = _cairo_fixed_from_double (dy);
+    }
+
+    _cairo_pen_compute_slopes (pen);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+void
+_cairo_pen_fini (cairo_pen_t *pen)
+{
+    if (pen->vertices != pen->vertices_embedded)
+       free (pen->vertices);
+
+
+    VG (VALGRIND_MAKE_MEM_NOACCESS (pen, sizeof (cairo_pen_t)));
+}
+
+cairo_status_t
+_cairo_pen_init_copy (cairo_pen_t *pen, const cairo_pen_t *other)
+{
+    VG (VALGRIND_MAKE_MEM_UNDEFINED (pen, sizeof (cairo_pen_t)));
+
+    *pen = *other;
+
+    if (CAIRO_INJECT_FAULT ())
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    pen->vertices = pen->vertices_embedded;
+    if (pen->num_vertices) {
+       if (pen->num_vertices > ARRAY_LENGTH (pen->vertices_embedded)) {
+           pen->vertices = _cairo_malloc_ab (pen->num_vertices,
+                                             sizeof (cairo_pen_vertex_t));
+           if (unlikely (pen->vertices == NULL))
+               return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+       }
+
+       memcpy (pen->vertices, other->vertices,
+               pen->num_vertices * sizeof (cairo_pen_vertex_t));
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_status_t
+_cairo_pen_add_points (cairo_pen_t *pen, cairo_point_t *point, int num_points)
+{
+    cairo_status_t status;
+    int num_vertices;
+    int i;
+
+    if (CAIRO_INJECT_FAULT ())
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    num_vertices = pen->num_vertices + num_points;
+    if (num_vertices > ARRAY_LENGTH (pen->vertices_embedded) ||
+       pen->vertices != pen->vertices_embedded)
+    {
+       cairo_pen_vertex_t *vertices;
+
+       if (pen->vertices == pen->vertices_embedded) {
+           vertices = _cairo_malloc_ab (num_vertices,
+                                        sizeof (cairo_pen_vertex_t));
+           if (unlikely (vertices == NULL))
+               return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+           memcpy (vertices, pen->vertices,
+                   pen->num_vertices * sizeof (cairo_pen_vertex_t));
+       } else {
+           vertices = _cairo_realloc_ab (pen->vertices,
+                                         num_vertices,
+                                         sizeof (cairo_pen_vertex_t));
+           if (unlikely (vertices == NULL))
+               return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+       }
+
+       pen->vertices = vertices;
+    }
+
+    pen->num_vertices = num_vertices;
+
+    /* initialize new vertices */
+    for (i=0; i < num_points; i++)
+       pen->vertices[pen->num_vertices-num_points+i].point = point[i];
+
+    status = _cairo_hull_compute (pen->vertices, &pen->num_vertices);
+    if (unlikely (status))
+       return status;
+
+    _cairo_pen_compute_slopes (pen);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+/*
+The circular pen in user space is transformed into an ellipse in
+device space.
+
+We construct the pen by computing points along the circumference
+using equally spaced angles.
+
+We show that this approximation to the ellipse has maximum error at the
+major axis of the ellipse.
+
+Set
+
+           M = major axis length
+           m = minor axis length
+
+Align 'M' along the X axis and 'm' along the Y axis and draw
+an ellipse parameterized by angle 't':
+
+           x = M cos t                 y = m sin t
+
+Perturb t by ± d and compute two new points (x+,y+), (x-,y-).
+The distance from the average of these two points to (x,y) represents
+the maximum error in approximating the ellipse with a polygon formed
+from vertices 2∆ radians apart.
+
+           x+ = M cos (t+∆)          y+ = m sin (t+∆)
+           x- = M cos (t-∆)          y- = m sin (t-∆)
+
+Now compute the approximation error, E:
+
+       Ex = (x - (x+ + x-) / 2)
+       Ex = (M cos(t) - (Mcos(t+∆) + Mcos(t-∆))/2)
+          = M (cos(t) - (cos(t)cos(∆) + sin(t)sin(∆) +
+                         cos(t)cos(∆) - sin(t)sin(∆))/2)
+          = M(cos(t) - cos(t)cos(∆))
+          = M cos(t) (1 - cos(∆))
+
+       Ey = y - (y+ - y-) / 2
+          = m sin (t) - (m sin(t+∆) + m sin(t-∆)) / 2
+          = m (sin(t) - (sin(t)cos(∆) + cos(t)sin(∆) +
+                         sin(t)cos(∆) - cos(t)sin(∆))/2)
+          = m (sin(t) - sin(t)cos(∆))
+          = m sin(t) (1 - cos(∆))
+
+       E² = Ex² + Ey²
+          = (M cos(t) (1 - cos (∆)))² + (m sin(t) (1-cos(∆)))²
+          = (1 - cos(∆))² (M² cos²(t) + m² sin²(t))
+          = (1 - cos(∆))² ((m² + M² - m²) cos² (t) + m² sin²(t))
+          = (1 - cos(∆))² (M² - m²) cos² (t) + (1 - cos(∆))² m²
+
+Find the extremum by differentiation wrt t and setting that to zero
+
+∂(E²)/∂(t) = (1-cos(∆))² (M² - m²) (-2 cos(t) sin(t))
+
+         0 = 2 cos (t) sin (t)
+        0 = sin (2t)
+        t = nπ
+
+Which is to say that the maximum and minimum errors occur on the
+axes of the ellipse at 0 and π radians:
+
+       E²(0) = (1-cos(∆))² (M² - m²) + (1-cos(∆))² m²
+             = (1-cos(∆))² M²
+       E²(π) = (1-cos(∆))² m²
+
+maximum error = M (1-cos(∆))
+minimum error = m (1-cos(∆))
+
+We must make maximum error ≤ tolerance, so compute the ∆ needed:
+
+           tolerance = M (1-cos(∆))
+       tolerance / M = 1 - cos (∆)
+              cos(∆) = 1 - tolerance/M
+                    ∆ = acos (1 - tolerance / M);
+
+Remembering that ∆ is half of our angle between vertices,
+the number of vertices is then
+
+             vertices = ceil(2π/2∆).
+                      = ceil(π/∆).
+
+Note that this also equation works for M == m (a circle) as it
+doesn't matter where on the circle the error is computed.
+*/
+
+static int
+_cairo_pen_vertices_needed (double         tolerance,
+                           double          radius,
+                           const cairo_matrix_t  *matrix)
+{
+    /*
+     * the pen is a circle that gets transformed to an ellipse by matrix.
+     * compute major axis length for a pen with the specified radius.
+     * we don't need the minor axis length.
+     */
+
+    double  major_axis = _cairo_matrix_transformed_circle_major_axis (matrix,
+                                                                     radius);
+
+    /*
+     * compute number of vertices needed
+     */
+    int            num_vertices;
+
+    /* Where tolerance / M is > 1, we use 4 points */
+    if (tolerance >= major_axis) {
+       num_vertices = 4;
+    } else {
+       double delta = acos (1 - tolerance / major_axis);
+       num_vertices = ceil (M_PI / delta);
+
+       /* number of vertices must be even */
+       if (num_vertices % 2)
+           num_vertices++;
+
+       /* And we must always have at least 4 vertices. */
+       if (num_vertices < 4)
+           num_vertices = 4;
+    }
+
+    return num_vertices;
+}
+
+static void
+_cairo_pen_compute_slopes (cairo_pen_t *pen)
+{
+    int i, i_prev;
+    cairo_pen_vertex_t *prev, *v, *next;
+
+    for (i=0, i_prev = pen->num_vertices - 1;
+        i < pen->num_vertices;
+        i_prev = i++) {
+       prev = &pen->vertices[i_prev];
+       v = &pen->vertices[i];
+       next = &pen->vertices[(i + 1) % pen->num_vertices];
+
+       _cairo_slope_init (&v->slope_cw, &prev->point, &v->point);
+       _cairo_slope_init (&v->slope_ccw, &v->point, &next->point);
+    }
+}
+/*
+ * Find active pen vertex for clockwise edge of stroke at the given slope.
+ *
+ * The strictness of the inequalities here is delicate. The issue is
+ * that the slope_ccw member of one pen vertex will be equivalent to
+ * the slope_cw member of the next pen vertex in a counterclockwise
+ * order. However, for this function, we care strongly about which
+ * vertex is returned.
+ *
+ * [I think the "care strongly" above has to do with ensuring that the
+ * pen's "extra points" from the spline's initial and final slopes are
+ * properly found when beginning the spline stroking.]
+ */
+int
+_cairo_pen_find_active_cw_vertex_index (const cairo_pen_t *pen,
+                                       const cairo_slope_t *slope)
+{
+    int i;
+
+    for (i=0; i < pen->num_vertices; i++) {
+       if ((_cairo_slope_compare (slope, &pen->vertices[i].slope_ccw) < 0) &&
+           (_cairo_slope_compare (slope, &pen->vertices[i].slope_cw) >= 0))
+           break;
+    }
+
+    /* If the desired slope cannot be found between any of the pen
+     * vertices, then we must have a degenerate pen, (such as a pen
+     * that's been transformed to a line). In that case, we consider
+     * the first pen vertex as the appropriate clockwise vertex.
+     */
+    if (i == pen->num_vertices)
+       i = 0;
+
+    return i;
+}
+
+/* Find active pen vertex for counterclockwise edge of stroke at the given slope.
+ *
+ * Note: See the comments for _cairo_pen_find_active_cw_vertex_index
+ * for some details about the strictness of the inequalities here.
+ */
+int
+_cairo_pen_find_active_ccw_vertex_index (const cairo_pen_t *pen,
+                                        const cairo_slope_t *slope)
+{
+    cairo_slope_t slope_reverse;
+    int i;
+
+    slope_reverse = *slope;
+    slope_reverse.dx = -slope_reverse.dx;
+    slope_reverse.dy = -slope_reverse.dy;
+
+    for (i=pen->num_vertices-1; i >= 0; i--) {
+       if ((_cairo_slope_compare (&pen->vertices[i].slope_ccw, &slope_reverse) >= 0) &&
+           (_cairo_slope_compare (&pen->vertices[i].slope_cw, &slope_reverse) < 0))
+           break;
+    }
+
+    /* If the desired slope cannot be found between any of the pen
+     * vertices, then we must have a degenerate pen, (such as a pen
+     * that's been transformed to a line). In that case, we consider
+     * the last pen vertex as the appropriate counterclockwise vertex.
+     */
+    if (i < 0)
+       i = pen->num_vertices - 1;
+
+    return i;
+}
diff --git a/src/cairo-png.c b/src/cairo-png.c
new file mode 100644 (file)
index 0000000..59bbe68
--- /dev/null
@@ -0,0 +1,809 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2003 University of Southern California
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *     Carl D. Worth <cworth@cworth.org>
+ *     Kristian Høgsberg <krh@redhat.com>
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-error-private.h"
+#include "cairo-image-surface-private.h"
+#include "cairo-output-stream-private.h"
+
+#include <stdio.h>
+#include <errno.h>
+#include <png.h>
+
+/**
+ * SECTION:cairo-png
+ * @Title: PNG Support
+ * @Short_Description: Reading and writing PNG images
+ * @See_Also: #cairo_surface_t
+ *
+ * The PNG functions allow reading PNG images into image surfaces, and writing
+ * any surface to a PNG file.
+ *
+ * It is a toy API. It only offers very simple support for reading and
+ * writing PNG files, which is sufficient for testing and
+ * demonstration purposes. Applications which need more control over
+ * the generated PNG file should access the pixel data directly, using
+ * cairo_image_surface_get_data() or a backend-specific access
+ * function, and process it with another library, e.g. gdk-pixbuf or
+ * libpng.
+ */
+
+/**
+ * CAIRO_HAS_PNG_FUNCTIONS:
+ *
+ * Defined if the PNG functions are available.
+ * This macro can be used to conditionally compile code using the cairo
+ * PNG functions.
+ */
+
+struct png_read_closure_t {
+    cairo_read_func_t           read_func;
+    void                       *closure;
+    cairo_output_stream_t      *png_data;
+};
+
+
+/* Unpremultiplies data and converts native endian ARGB => RGBA bytes */
+static void
+unpremultiply_data (png_structp png, png_row_infop row_info, png_bytep data)
+{
+    unsigned int i;
+
+    for (i = 0; i < row_info->rowbytes; i += 4) {
+        uint8_t *b = &data[i];
+        uint32_t pixel;
+        uint8_t  alpha;
+
+       memcpy (&pixel, b, sizeof (uint32_t));
+       alpha = (pixel & 0xff000000) >> 24;
+        if (alpha == 0) {
+           b[0] = b[1] = b[2] = b[3] = 0;
+       } else {
+            b[0] = (((pixel & 0xff0000) >> 16) * 255 + alpha / 2) / alpha;
+            b[1] = (((pixel & 0x00ff00) >>  8) * 255 + alpha / 2) / alpha;
+            b[2] = (((pixel & 0x0000ff) >>  0) * 255 + alpha / 2) / alpha;
+           b[3] = alpha;
+       }
+    }
+}
+
+/* Converts native endian xRGB => RGBx bytes */
+static void
+convert_data_to_bytes (png_structp png, png_row_infop row_info, png_bytep data)
+{
+    unsigned int i;
+
+    for (i = 0; i < row_info->rowbytes; i += 4) {
+        uint8_t *b = &data[i];
+        uint32_t pixel;
+
+       memcpy (&pixel, b, sizeof (uint32_t));
+
+       b[0] = (pixel & 0xff0000) >> 16;
+       b[1] = (pixel & 0x00ff00) >>  8;
+       b[2] = (pixel & 0x0000ff) >>  0;
+       b[3] = 0;
+    }
+}
+
+/* Use a couple of simple error callbacks that do not print anything to
+ * stderr and rely on the user to check for errors via the #cairo_status_t
+ * return.
+ */
+static void
+png_simple_error_callback (png_structp png,
+                          png_const_charp error_msg)
+{
+    cairo_status_t *error = png_get_error_ptr (png);
+
+    /* default to the most likely error */
+    if (*error == CAIRO_STATUS_SUCCESS)
+       *error = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+#ifdef PNG_SETJMP_SUPPORTED
+    longjmp (png_jmpbuf (png), 1);
+#endif
+
+    /* if we get here, then we have to choice but to abort ... */
+}
+
+static void
+png_simple_warning_callback (png_structp png,
+                            png_const_charp error_msg)
+{
+    cairo_status_t *error = png_get_error_ptr (png);
+
+    /* default to the most likely error */
+    if (*error == CAIRO_STATUS_SUCCESS)
+       *error = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    /* png does not expect to abort and will try to tidy up after a warning */
+}
+
+
+/* Starting with libpng-1.2.30, we must explicitly specify an output_flush_fn.
+ * Otherwise, we will segfault if we are writing to a stream. */
+static void
+png_simple_output_flush_fn (png_structp png_ptr)
+{
+}
+
+static cairo_status_t
+write_png (cairo_surface_t     *surface,
+          png_rw_ptr           write_func,
+          void                 *closure)
+{
+    int i;
+    cairo_int_status_t status;
+    cairo_image_surface_t *image;
+    cairo_image_surface_t * volatile clone;
+    void *image_extra;
+    png_struct *png;
+    png_info *info;
+    png_byte **volatile rows = NULL;
+    png_color_16 white;
+    int png_color_type;
+    int bpc;
+
+    status = _cairo_surface_acquire_source_image (surface,
+                                                 &image,
+                                                 &image_extra);
+
+    if (status == CAIRO_INT_STATUS_UNSUPPORTED)
+       return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+    else if (unlikely (status))
+        return status;
+
+    /* PNG complains about "Image width or height is zero in IHDR" */
+    if (image->width == 0 || image->height == 0) {
+       status = _cairo_error (CAIRO_STATUS_WRITE_ERROR);
+       goto BAIL1;
+    }
+
+    /* Handle the various fallback formats (e.g. low bit-depth XServers)
+     * by coercing them to a simpler format using pixman.
+     */
+    clone = _cairo_image_surface_coerce (image);
+    status = clone->base.status;
+    if (unlikely (status))
+        goto BAIL1;
+
+    rows = _cairo_malloc_ab (clone->height, sizeof (png_byte*));
+    if (unlikely (rows == NULL)) {
+       status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+       goto BAIL2;
+    }
+
+    for (i = 0; i < clone->height; i++)
+       rows[i] = (png_byte *) clone->data + i * clone->stride;
+
+    png = png_create_write_struct (PNG_LIBPNG_VER_STRING, &status,
+                                  png_simple_error_callback,
+                                  png_simple_warning_callback);
+    if (unlikely (png == NULL)) {
+       status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+       goto BAIL3;
+    }
+
+    info = png_create_info_struct (png);
+    if (unlikely (info == NULL)) {
+       status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+       goto BAIL4;
+    }
+
+#ifdef PNG_SETJMP_SUPPORTED
+    if (setjmp (png_jmpbuf (png)))
+       goto BAIL4;
+#endif
+
+    png_set_write_fn (png, closure, write_func, png_simple_output_flush_fn);
+
+    switch (clone->format) {
+    case CAIRO_FORMAT_ARGB32:
+       bpc = 8;
+       if (_cairo_image_analyze_transparency (clone) == CAIRO_IMAGE_IS_OPAQUE)
+           png_color_type = PNG_COLOR_TYPE_RGB;
+       else
+           png_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
+       break;
+    case CAIRO_FORMAT_RGB30:
+       bpc = 10;
+       png_color_type = PNG_COLOR_TYPE_RGB;
+       break;
+    case CAIRO_FORMAT_RGB24:
+       bpc = 8;
+       png_color_type = PNG_COLOR_TYPE_RGB;
+       break;
+    case CAIRO_FORMAT_A8:
+       bpc = 8;
+       png_color_type = PNG_COLOR_TYPE_GRAY;
+       break;
+    case CAIRO_FORMAT_A1:
+       bpc = 1;
+       png_color_type = PNG_COLOR_TYPE_GRAY;
+#ifndef WORDS_BIGENDIAN
+       png_set_packswap (png);
+#endif
+       break;
+    case CAIRO_FORMAT_INVALID:
+    case CAIRO_FORMAT_RGB16_565:
+    default:
+       status = _cairo_error (CAIRO_STATUS_INVALID_FORMAT);
+       goto BAIL4;
+    }
+
+    png_set_IHDR (png, info,
+                 clone->width,
+                 clone->height, bpc,
+                 png_color_type,
+                 PNG_INTERLACE_NONE,
+                 PNG_COMPRESSION_TYPE_DEFAULT,
+                 PNG_FILTER_TYPE_DEFAULT);
+
+    white.gray = (1 << bpc) - 1;
+    white.red = white.blue = white.green = white.gray;
+    png_set_bKGD (png, info, &white);
+
+    if (0) { /* XXX extract meta-data from surface (i.e. creation date) */
+       png_time pt;
+
+       png_convert_from_time_t (&pt, time (NULL));
+       png_set_tIME (png, info, &pt);
+    }
+
+    /* We have to call png_write_info() before setting up the write
+     * transformation, since it stores data internally in 'png'
+     * that is needed for the write transformation functions to work.
+     */
+    png_write_info (png, info);
+
+    if (png_color_type == PNG_COLOR_TYPE_RGB_ALPHA) {
+       png_set_write_user_transform_fn (png, unpremultiply_data);
+    } else if (png_color_type == PNG_COLOR_TYPE_RGB) {
+       png_set_write_user_transform_fn (png, convert_data_to_bytes);
+       png_set_filler (png, 0, PNG_FILLER_AFTER);
+    }
+
+    png_write_image (png, rows);
+    png_write_end (png, info);
+
+BAIL4:
+    png_destroy_write_struct (&png, &info);
+BAIL3:
+    free (rows);
+BAIL2:
+    cairo_surface_destroy (&clone->base);
+BAIL1:
+    _cairo_surface_release_source_image (surface, image, image_extra);
+
+    return status;
+}
+
+static void
+stdio_write_func (png_structp png, png_bytep data, png_size_t size)
+{
+    FILE *fp;
+
+    fp = png_get_io_ptr (png);
+    while (size) {
+       size_t ret = fwrite (data, 1, size, fp);
+       size -= ret;
+       data += ret;
+       if (size && ferror (fp)) {
+           cairo_status_t *error = png_get_error_ptr (png);
+           if (*error == CAIRO_STATUS_SUCCESS)
+               *error = _cairo_error (CAIRO_STATUS_WRITE_ERROR);
+           png_error (png, NULL);
+       }
+    }
+}
+
+/**
+ * cairo_surface_write_to_png:
+ * @surface: a #cairo_surface_t with pixel contents
+ * @filename: the name of a file to write to
+ *
+ * Writes the contents of @surface to a new file @filename as a PNG
+ * image.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS if the PNG file was written
+ * successfully. Otherwise, %CAIRO_STATUS_NO_MEMORY if memory could not
+ * be allocated for the operation or
+ * %CAIRO_STATUS_SURFACE_TYPE_MISMATCH if the surface does not have
+ * pixel contents, or %CAIRO_STATUS_WRITE_ERROR if an I/O error occurs
+ * while attempting to write the file.
+ **/
+cairo_status_t
+cairo_surface_write_to_png (cairo_surface_t    *surface,
+                           const char          *filename)
+{
+    FILE *fp;
+    cairo_status_t status;
+
+    if (surface->status)
+       return surface->status;
+
+    if (surface->finished)
+       return _cairo_error (CAIRO_STATUS_SURFACE_FINISHED);
+
+    fp = fopen (filename, "wb");
+    if (fp == NULL) {
+       switch (errno) {
+       case ENOMEM:
+           return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+       default:
+           return _cairo_error (CAIRO_STATUS_WRITE_ERROR);
+       }
+    }
+
+    status = write_png (surface, stdio_write_func, fp);
+
+    if (fclose (fp) && status == CAIRO_STATUS_SUCCESS)
+       status = _cairo_error (CAIRO_STATUS_WRITE_ERROR);
+
+    return status;
+}
+
+struct png_write_closure_t {
+    cairo_write_func_t          write_func;
+    void                       *closure;
+};
+
+static void
+stream_write_func (png_structp png, png_bytep data, png_size_t size)
+{
+    cairo_status_t status;
+    struct png_write_closure_t *png_closure;
+
+    png_closure = png_get_io_ptr (png);
+    status = png_closure->write_func (png_closure->closure, data, size);
+    if (unlikely (status)) {
+       cairo_status_t *error = png_get_error_ptr (png);
+       if (*error == CAIRO_STATUS_SUCCESS)
+           *error = status;
+       png_error (png, NULL);
+    }
+}
+
+/**
+ * cairo_surface_write_to_png_stream:
+ * @surface: a #cairo_surface_t with pixel contents
+ * @write_func: a #cairo_write_func_t
+ * @closure: closure data for the write function
+ *
+ * Writes the image surface to the write function.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS if the PNG file was written
+ * successfully.  Otherwise, %CAIRO_STATUS_NO_MEMORY is returned if
+ * memory could not be allocated for the operation,
+ * %CAIRO_STATUS_SURFACE_TYPE_MISMATCH if the surface does not have
+ * pixel contents.
+ **/
+cairo_status_t
+cairo_surface_write_to_png_stream (cairo_surface_t     *surface,
+                                  cairo_write_func_t   write_func,
+                                  void                 *closure)
+{
+    struct png_write_closure_t png_closure;
+
+    if (surface->status)
+       return surface->status;
+
+    if (surface->finished)
+       return _cairo_error (CAIRO_STATUS_SURFACE_FINISHED);
+
+    png_closure.write_func = write_func;
+    png_closure.closure = closure;
+
+    return write_png (surface, stream_write_func, &png_closure);
+}
+slim_hidden_def (cairo_surface_write_to_png_stream);
+
+static inline int
+multiply_alpha (int alpha, int color)
+{
+    int temp = (alpha * color) + 0x80;
+    return ((temp + (temp >> 8)) >> 8);
+}
+
+/* Premultiplies data and converts RGBA bytes => native endian */
+static void
+premultiply_data (png_structp   png,
+                  png_row_infop row_info,
+                  png_bytep     data)
+{
+    unsigned int i;
+
+    for (i = 0; i < row_info->rowbytes; i += 4) {
+       uint8_t *base  = &data[i];
+       uint8_t  alpha = base[3];
+       uint32_t p;
+
+       if (alpha == 0) {
+           p = 0;
+       } else {
+           uint8_t  red   = base[0];
+           uint8_t  green = base[1];
+           uint8_t  blue  = base[2];
+
+           if (alpha != 0xff) {
+               red   = multiply_alpha (alpha, red);
+               green = multiply_alpha (alpha, green);
+               blue  = multiply_alpha (alpha, blue);
+           }
+           p = (alpha << 24) | (red << 16) | (green << 8) | (blue << 0);
+       }
+       memcpy (base, &p, sizeof (uint32_t));
+    }
+}
+
+/* Converts RGBx bytes to native endian xRGB */
+static void
+convert_bytes_to_data (png_structp png, png_row_infop row_info, png_bytep data)
+{
+    unsigned int i;
+
+    for (i = 0; i < row_info->rowbytes; i += 4) {
+       uint8_t *base  = &data[i];
+       uint8_t  red   = base[0];
+       uint8_t  green = base[1];
+       uint8_t  blue  = base[2];
+       uint32_t pixel;
+
+       pixel = (0xff << 24) | (red << 16) | (green << 8) | (blue << 0);
+       memcpy (base, &pixel, sizeof (uint32_t));
+    }
+}
+
+static cairo_status_t
+stdio_read_func (void *closure, unsigned char *data, unsigned int size)
+{
+    FILE *file = closure;
+
+    while (size) {
+       size_t ret;
+
+       ret = fread (data, 1, size, file);
+       size -= ret;
+       data += ret;
+
+       if (size && (feof (file) || ferror (file)))
+           return _cairo_error (CAIRO_STATUS_READ_ERROR);
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+stream_read_func (png_structp png, png_bytep data, png_size_t size)
+{
+    cairo_status_t status;
+    struct png_read_closure_t *png_closure;
+
+    png_closure = png_get_io_ptr (png);
+    status = png_closure->read_func (png_closure->closure, data, size);
+    if (unlikely (status)) {
+       cairo_status_t *error = png_get_error_ptr (png);
+       if (*error == CAIRO_STATUS_SUCCESS)
+           *error = status;
+       png_error (png, NULL);
+    }
+
+    _cairo_output_stream_write (png_closure->png_data, data, size);
+}
+
+static cairo_surface_t *
+read_png (struct png_read_closure_t *png_closure)
+{
+    cairo_surface_t *surface;
+    png_struct *png = NULL;
+    png_info *info;
+    png_byte *data = NULL;
+    png_byte **row_pointers = NULL;
+    png_uint_32 png_width, png_height;
+    int depth, color_type, interlace, stride;
+    unsigned int i;
+    cairo_format_t format;
+    cairo_status_t status;
+    unsigned char *mime_data;
+    unsigned long mime_data_length;
+
+    png_closure->png_data = _cairo_memory_stream_create ();
+
+    /* XXX: Perhaps we'll want some other error handlers? */
+    png = png_create_read_struct (PNG_LIBPNG_VER_STRING,
+                                  &status,
+                                 png_simple_error_callback,
+                                 png_simple_warning_callback);
+    if (unlikely (png == NULL)) {
+       surface = _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+       goto BAIL;
+    }
+
+    info = png_create_info_struct (png);
+    if (unlikely (info == NULL)) {
+       surface = _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+       goto BAIL;
+    }
+
+    png_set_read_fn (png, png_closure, stream_read_func);
+
+    status = CAIRO_STATUS_SUCCESS;
+#ifdef PNG_SETJMP_SUPPORTED
+    if (setjmp (png_jmpbuf (png))) {
+       surface = _cairo_surface_create_in_error (status);
+       goto BAIL;
+    }
+#endif
+
+    png_read_info (png, info);
+
+    png_get_IHDR (png, info,
+                  &png_width, &png_height, &depth,
+                  &color_type, &interlace, NULL, NULL);
+    if (unlikely (status)) { /* catch any early warnings */
+       surface = _cairo_surface_create_in_error (status);
+       goto BAIL;
+    }
+
+    /* convert palette/gray image to rgb */
+    if (color_type == PNG_COLOR_TYPE_PALETTE)
+        png_set_palette_to_rgb (png);
+
+    /* expand gray bit depth if needed */
+    if (color_type == PNG_COLOR_TYPE_GRAY) {
+#if PNG_LIBPNG_VER >= 10209
+        png_set_expand_gray_1_2_4_to_8 (png);
+#else
+        png_set_gray_1_2_4_to_8 (png);
+#endif
+    }
+
+    /* transform transparency to alpha */
+    if (png_get_valid (png, info, PNG_INFO_tRNS))
+        png_set_tRNS_to_alpha (png);
+
+    if (depth == 16)
+        png_set_strip_16 (png);
+
+    if (depth < 8)
+        png_set_packing (png);
+
+    /* convert grayscale to RGB */
+    if (color_type == PNG_COLOR_TYPE_GRAY ||
+       color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
+    {
+       png_set_gray_to_rgb (png);
+    }
+
+    if (interlace != PNG_INTERLACE_NONE)
+        png_set_interlace_handling (png);
+
+    png_set_filler (png, 0xff, PNG_FILLER_AFTER);
+
+    /* recheck header after setting EXPAND options */
+    png_read_update_info (png, info);
+    png_get_IHDR (png, info,
+                  &png_width, &png_height, &depth,
+                  &color_type, &interlace, NULL, NULL);
+    if (depth != 8 ||
+       ! (color_type == PNG_COLOR_TYPE_RGB ||
+          color_type == PNG_COLOR_TYPE_RGB_ALPHA))
+    {
+       surface = _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_READ_ERROR));
+       goto BAIL;
+    }
+
+    switch (color_type) {
+       default:
+           ASSERT_NOT_REACHED;
+           /* fall-through just in case ;-) */
+
+       case PNG_COLOR_TYPE_RGB_ALPHA:
+           format = CAIRO_FORMAT_ARGB32;
+           png_set_read_user_transform_fn (png, premultiply_data);
+           break;
+
+       case PNG_COLOR_TYPE_RGB:
+           format = CAIRO_FORMAT_RGB24;
+           png_set_read_user_transform_fn (png, convert_bytes_to_data);
+           break;
+    }
+
+    stride = cairo_format_stride_for_width (format, png_width);
+    if (stride < 0) {
+       surface = _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_STRIDE));
+       goto BAIL;
+    }
+
+    data = _cairo_malloc_ab (png_height, stride);
+    if (unlikely (data == NULL)) {
+       surface = _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+       goto BAIL;
+    }
+
+    row_pointers = _cairo_malloc_ab (png_height, sizeof (char *));
+    if (unlikely (row_pointers == NULL)) {
+       surface = _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+       goto BAIL;
+    }
+
+    for (i = 0; i < png_height; i++)
+        row_pointers[i] = &data[i * stride];
+
+    png_read_image (png, row_pointers);
+    png_read_end (png, info);
+
+    if (unlikely (status)) { /* catch any late warnings - probably hit an error already */
+       surface = _cairo_surface_create_in_error (status);
+       goto BAIL;
+    }
+
+    surface = cairo_image_surface_create_for_data (data, format,
+                                                  png_width, png_height,
+                                                  stride);
+    if (surface->status)
+       goto BAIL;
+
+    _cairo_image_surface_assume_ownership_of_data ((cairo_image_surface_t*)surface);
+    data = NULL;
+
+    _cairo_debug_check_image_surface_is_defined (surface);
+
+    status = _cairo_memory_stream_destroy (png_closure->png_data,
+                                          &mime_data,
+                                          &mime_data_length);
+    png_closure->png_data = NULL;
+    if (unlikely (status)) {
+       cairo_surface_destroy (surface);
+       surface = _cairo_surface_create_in_error (status);
+       goto BAIL;
+    }
+
+    status = cairo_surface_set_mime_data (surface,
+                                         CAIRO_MIME_TYPE_PNG,
+                                         mime_data,
+                                         mime_data_length,
+                                         free,
+                                         mime_data);
+    if (unlikely (status)) {
+       free (mime_data);
+       cairo_surface_destroy (surface);
+       surface = _cairo_surface_create_in_error (status);
+       goto BAIL;
+    }
+
+ BAIL:
+    free (row_pointers);
+    free (data);
+    if (png != NULL)
+       png_destroy_read_struct (&png, &info, NULL);
+    if (png_closure->png_data != NULL) {
+       cairo_status_t status_ignored;
+
+       status_ignored = _cairo_output_stream_destroy (png_closure->png_data);
+    }
+
+    return surface;
+}
+
+/**
+ * cairo_image_surface_create_from_png:
+ * @filename: name of PNG file to load
+ *
+ * Creates a new image surface and initializes the contents to the
+ * given PNG file.
+ *
+ * Return value: a new #cairo_surface_t initialized with the contents
+ * of the PNG file, or a "nil" surface if any error occurred. A nil
+ * surface can be checked for with cairo_surface_status(surface) which
+ * may return one of the following values:
+ *
+ *     %CAIRO_STATUS_NO_MEMORY
+ *     %CAIRO_STATUS_FILE_NOT_FOUND
+ *     %CAIRO_STATUS_READ_ERROR
+ *
+ * Alternatively, you can allow errors to propagate through the drawing
+ * operations and check the status on the context upon completion
+ * using cairo_status().
+ **/
+cairo_surface_t *
+cairo_image_surface_create_from_png (const char *filename)
+{
+    struct png_read_closure_t png_closure;
+    cairo_surface_t *surface;
+
+    png_closure.closure = fopen (filename, "rb");
+    if (png_closure.closure == NULL) {
+       cairo_status_t status;
+       switch (errno) {
+       case ENOMEM:
+           status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+           break;
+       case ENOENT:
+           status = _cairo_error (CAIRO_STATUS_FILE_NOT_FOUND);
+           break;
+       default:
+           status = _cairo_error (CAIRO_STATUS_READ_ERROR);
+           break;
+       }
+       return _cairo_surface_create_in_error (status);
+    }
+
+    png_closure.read_func = stdio_read_func;
+
+    surface = read_png (&png_closure);
+
+    fclose (png_closure.closure);
+
+    return surface;
+}
+
+/**
+ * cairo_image_surface_create_from_png_stream:
+ * @read_func: function called to read the data of the file
+ * @closure: data to pass to @read_func.
+ *
+ * Creates a new image surface from PNG data read incrementally
+ * via the @read_func function.
+ *
+ * Return value: a new #cairo_surface_t initialized with the contents
+ * of the PNG file or a "nil" surface if the data read is not a valid PNG image
+ * or memory could not be allocated for the operation.  A nil
+ * surface can be checked for with cairo_surface_status(surface) which
+ * may return one of the following values:
+ *
+ *     %CAIRO_STATUS_NO_MEMORY
+ *     %CAIRO_STATUS_READ_ERROR
+ *
+ * Alternatively, you can allow errors to propagate through the drawing
+ * operations and check the status on the context upon completion
+ * using cairo_status().
+ **/
+cairo_surface_t *
+cairo_image_surface_create_from_png_stream (cairo_read_func_t  read_func,
+                                           void                *closure)
+{
+    struct png_read_closure_t png_closure;
+
+    png_closure.read_func = read_func;
+    png_closure.closure = closure;
+
+    return read_png (&png_closure);
+}
diff --git a/src/cairo-polygon-intersect.c b/src/cairo-polygon-intersect.c
new file mode 100644 (file)
index 0000000..92becd0
--- /dev/null
@@ -0,0 +1,1513 @@
+/*
+ * Copyright © 2004 Carl Worth
+ * Copyright © 2006 Red Hat, Inc.
+ * Copyright © 2008 Chris Wilson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Carl Worth
+ *
+ * Contributor(s):
+ *     Carl D. Worth <cworth@cworth.org>
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+/* Provide definitions for standalone compilation */
+#include "cairoint.h"
+
+#include "cairo-error-private.h"
+#include "cairo-freelist-private.h"
+#include "cairo-combsort-private.h"
+
+typedef cairo_point_t cairo_bo_point32_t;
+
+typedef struct _cairo_bo_intersect_ordinate {
+    int32_t ordinate;
+    enum { EXACT, INEXACT } exactness;
+} cairo_bo_intersect_ordinate_t;
+
+typedef struct _cairo_bo_intersect_point {
+    cairo_bo_intersect_ordinate_t x;
+    cairo_bo_intersect_ordinate_t y;
+} cairo_bo_intersect_point_t;
+
+typedef struct _cairo_bo_edge cairo_bo_edge_t;
+
+typedef struct _cairo_bo_deferred {
+    cairo_bo_edge_t *other;
+    int32_t top;
+} cairo_bo_deferred_t;
+
+struct _cairo_bo_edge {
+    int a_or_b;
+    cairo_edge_t edge;
+    cairo_bo_edge_t *prev;
+    cairo_bo_edge_t *next;
+    cairo_bo_deferred_t deferred;
+};
+
+/* the parent is always given by index/2 */
+#define PQ_PARENT_INDEX(i) ((i) >> 1)
+#define PQ_FIRST_ENTRY 1
+
+/* left and right children are index * 2 and (index * 2) +1 respectively */
+#define PQ_LEFT_CHILD_INDEX(i) ((i) << 1)
+
+typedef enum {
+    CAIRO_BO_EVENT_TYPE_STOP,
+    CAIRO_BO_EVENT_TYPE_INTERSECTION,
+    CAIRO_BO_EVENT_TYPE_START
+} cairo_bo_event_type_t;
+
+typedef struct _cairo_bo_event {
+    cairo_bo_event_type_t type;
+    cairo_point_t point;
+} cairo_bo_event_t;
+
+typedef struct _cairo_bo_start_event {
+    cairo_bo_event_type_t type;
+    cairo_point_t point;
+    cairo_bo_edge_t edge;
+} cairo_bo_start_event_t;
+
+typedef struct _cairo_bo_queue_event {
+    cairo_bo_event_type_t type;
+    cairo_point_t point;
+    cairo_bo_edge_t *e1;
+    cairo_bo_edge_t *e2;
+} cairo_bo_queue_event_t;
+
+typedef struct _pqueue {
+    int size, max_size;
+
+    cairo_bo_event_t **elements;
+    cairo_bo_event_t *elements_embedded[1024];
+} pqueue_t;
+
+typedef struct _cairo_bo_event_queue {
+    cairo_freepool_t pool;
+    pqueue_t pqueue;
+    cairo_bo_event_t **start_events;
+} cairo_bo_event_queue_t;
+
+typedef struct _cairo_bo_sweep_line {
+    cairo_bo_edge_t *head;
+    int32_t current_y;
+    cairo_bo_edge_t *current_edge;
+} cairo_bo_sweep_line_t;
+
+static cairo_fixed_t
+_line_compute_intersection_x_for_y (const cairo_line_t *line,
+                                   cairo_fixed_t y)
+{
+    cairo_fixed_t x, dy;
+
+    if (y == line->p1.y)
+       return line->p1.x;
+    if (y == line->p2.y)
+       return line->p2.x;
+
+    x = line->p1.x;
+    dy = line->p2.y - line->p1.y;
+    if (dy != 0) {
+       x += _cairo_fixed_mul_div_floor (y - line->p1.y,
+                                        line->p2.x - line->p1.x,
+                                        dy);
+    }
+
+    return x;
+}
+
+static inline int
+_cairo_bo_point32_compare (cairo_bo_point32_t const *a,
+                          cairo_bo_point32_t const *b)
+{
+    int cmp;
+
+    cmp = a->y - b->y;
+    if (cmp)
+       return cmp;
+
+    return a->x - b->x;
+}
+
+/* Compare the slope of a to the slope of b, returning 1, 0, -1 if the
+ * slope a is respectively greater than, equal to, or less than the
+ * slope of b.
+ *
+ * For each edge, consider the direction vector formed from:
+ *
+ *     top -> bottom
+ *
+ * which is:
+ *
+ *     (dx, dy) = (line.p2.x - line.p1.x, line.p2.y - line.p1.y)
+ *
+ * We then define the slope of each edge as dx/dy, (which is the
+ * inverse of the slope typically used in math instruction). We never
+ * compute a slope directly as the value approaches infinity, but we
+ * can derive a slope comparison without division as follows, (where
+ * the ? represents our compare operator).
+ *
+ * 1.     slope(a) ? slope(b)
+ * 2.      adx/ady ? bdx/bdy
+ * 3.  (adx * bdy) ? (bdx * ady)
+ *
+ * Note that from step 2 to step 3 there is no change needed in the
+ * sign of the result since both ady and bdy are guaranteed to be
+ * greater than or equal to 0.
+ *
+ * When using this slope comparison to sort edges, some care is needed
+ * when interpreting the results. Since the slope compare operates on
+ * distance vectors from top to bottom it gives a correct left to
+ * right sort for edges that have a common top point, (such as two
+ * edges with start events at the same location). On the other hand,
+ * the sense of the result will be exactly reversed for two edges that
+ * have a common stop point.
+ */
+static inline int
+_slope_compare (const cairo_bo_edge_t *a,
+               const cairo_bo_edge_t *b)
+{
+    /* XXX: We're assuming here that dx and dy will still fit in 32
+     * bits. That's not true in general as there could be overflow. We
+     * should prevent that before the tessellation algorithm
+     * begins.
+     */
+    int32_t adx = a->edge.line.p2.x - a->edge.line.p1.x;
+    int32_t bdx = b->edge.line.p2.x - b->edge.line.p1.x;
+
+    /* Since the dy's are all positive by construction we can fast
+     * path several common cases.
+     */
+
+    /* First check for vertical lines. */
+    if (adx == 0)
+       return -bdx;
+    if (bdx == 0)
+       return adx;
+
+    /* Then where the two edges point in different directions wrt x. */
+    if ((adx ^ bdx) < 0)
+       return adx;
+
+    /* Finally we actually need to do the general comparison. */
+    {
+       int32_t ady = a->edge.line.p2.y - a->edge.line.p1.y;
+       int32_t bdy = b->edge.line.p2.y - b->edge.line.p1.y;
+       cairo_int64_t adx_bdy = _cairo_int32x32_64_mul (adx, bdy);
+       cairo_int64_t bdx_ady = _cairo_int32x32_64_mul (bdx, ady);
+
+       return _cairo_int64_cmp (adx_bdy, bdx_ady);
+    }
+}
+
+/*
+ * We need to compare the x-coordinates of a pair of lines for a particular y,
+ * without loss of precision.
+ *
+ * The x-coordinate along an edge for a given y is:
+ *   X = A_x + (Y - A_y) * A_dx / A_dy
+ *
+ * So the inequality we wish to test is:
+ *   A_x + (Y - A_y) * A_dx / A_dy ∘ B_x + (Y - B_y) * B_dx / B_dy,
+ * where ∘ is our inequality operator.
+ *
+ * By construction, we know that A_dy and B_dy (and (Y - A_y), (Y - B_y)) are
+ * all positive, so we can rearrange it thus without causing a sign change:
+ *   A_dy * B_dy * (A_x - B_x) ∘ (Y - B_y) * B_dx * A_dy
+ *                                 - (Y - A_y) * A_dx * B_dy
+ *
+ * Given the assumption that all the deltas fit within 32 bits, we can compute
+ * this comparison directly using 128 bit arithmetic. For certain, but common,
+ * input we can reduce this down to a single 32 bit compare by inspecting the
+ * deltas.
+ *
+ * (And put the burden of the work on developing fast 128 bit ops, which are
+ * required throughout the tessellator.)
+ *
+ * See the similar discussion for _slope_compare().
+ */
+static int
+edges_compare_x_for_y_general (const cairo_bo_edge_t *a,
+                              const cairo_bo_edge_t *b,
+                              int32_t y)
+{
+    /* XXX: We're assuming here that dx and dy will still fit in 32
+     * bits. That's not true in general as there could be overflow. We
+     * should prevent that before the tessellation algorithm
+     * begins.
+     */
+    int32_t dx;
+    int32_t adx, ady;
+    int32_t bdx, bdy;
+    enum {
+       HAVE_NONE    = 0x0,
+       HAVE_DX      = 0x1,
+       HAVE_ADX     = 0x2,
+       HAVE_DX_ADX  = HAVE_DX | HAVE_ADX,
+       HAVE_BDX     = 0x4,
+       HAVE_DX_BDX  = HAVE_DX | HAVE_BDX,
+       HAVE_ADX_BDX = HAVE_ADX | HAVE_BDX,
+       HAVE_ALL     = HAVE_DX | HAVE_ADX | HAVE_BDX
+    } have_dx_adx_bdx = HAVE_ALL;
+
+    /* don't bother solving for abscissa if the edges' bounding boxes
+     * can be used to order them. */
+    {
+           int32_t amin, amax;
+           int32_t bmin, bmax;
+           if (a->edge.line.p1.x < a->edge.line.p2.x) {
+                   amin = a->edge.line.p1.x;
+                   amax = a->edge.line.p2.x;
+           } else {
+                   amin = a->edge.line.p2.x;
+                   amax = a->edge.line.p1.x;
+           }
+           if (b->edge.line.p1.x < b->edge.line.p2.x) {
+                   bmin = b->edge.line.p1.x;
+                   bmax = b->edge.line.p2.x;
+           } else {
+                   bmin = b->edge.line.p2.x;
+                   bmax = b->edge.line.p1.x;
+           }
+           if (amax < bmin) return -1;
+           if (amin > bmax) return +1;
+    }
+
+    ady = a->edge.line.p2.y - a->edge.line.p1.y;
+    adx = a->edge.line.p2.x - a->edge.line.p1.x;
+    if (adx == 0)
+       have_dx_adx_bdx &= ~HAVE_ADX;
+
+    bdy = b->edge.line.p2.y - b->edge.line.p1.y;
+    bdx = b->edge.line.p2.x - b->edge.line.p1.x;
+    if (bdx == 0)
+       have_dx_adx_bdx &= ~HAVE_BDX;
+
+    dx = a->edge.line.p1.x - b->edge.line.p1.x;
+    if (dx == 0)
+       have_dx_adx_bdx &= ~HAVE_DX;
+
+#define L _cairo_int64x32_128_mul (_cairo_int32x32_64_mul (ady, bdy), dx)
+#define A _cairo_int64x32_128_mul (_cairo_int32x32_64_mul (adx, bdy), y - a->edge.line.p1.y)
+#define B _cairo_int64x32_128_mul (_cairo_int32x32_64_mul (bdx, ady), y - b->edge.line.p1.y)
+    switch (have_dx_adx_bdx) {
+    default:
+    case HAVE_NONE:
+       return 0;
+    case HAVE_DX:
+       /* A_dy * B_dy * (A_x - B_x) ∘ 0 */
+       return dx; /* ady * bdy is positive definite */
+    case HAVE_ADX:
+       /* 0 ∘  - (Y - A_y) * A_dx * B_dy */
+       return adx; /* bdy * (y - a->top.y) is positive definite */
+    case HAVE_BDX:
+       /* 0 ∘ (Y - B_y) * B_dx * A_dy */
+       return -bdx; /* ady * (y - b->top.y) is positive definite */
+    case HAVE_ADX_BDX:
+       /*  0 ∘ (Y - B_y) * B_dx * A_dy - (Y - A_y) * A_dx * B_dy */
+       if ((adx ^ bdx) < 0) {
+           return adx;
+       } else if (a->edge.line.p1.y == b->edge.line.p1.y) { /* common origin */
+           cairo_int64_t adx_bdy, bdx_ady;
+
+           /* ∴ A_dx * B_dy ∘ B_dx * A_dy */
+
+           adx_bdy = _cairo_int32x32_64_mul (adx, bdy);
+           bdx_ady = _cairo_int32x32_64_mul (bdx, ady);
+
+           return _cairo_int64_cmp (adx_bdy, bdx_ady);
+       } else
+           return _cairo_int128_cmp (A, B);
+    case HAVE_DX_ADX:
+       /* A_dy * (A_x - B_x) ∘ - (Y - A_y) * A_dx */
+       if ((-adx ^ dx) < 0) {
+           return dx;
+       } else {
+           cairo_int64_t ady_dx, dy_adx;
+
+           ady_dx = _cairo_int32x32_64_mul (ady, dx);
+           dy_adx = _cairo_int32x32_64_mul (a->edge.line.p1.y - y, adx);
+
+           return _cairo_int64_cmp (ady_dx, dy_adx);
+       }
+    case HAVE_DX_BDX:
+       /* B_dy * (A_x - B_x) ∘ (Y - B_y) * B_dx */
+       if ((bdx ^ dx) < 0) {
+           return dx;
+       } else {
+           cairo_int64_t bdy_dx, dy_bdx;
+
+           bdy_dx = _cairo_int32x32_64_mul (bdy, dx);
+           dy_bdx = _cairo_int32x32_64_mul (y - b->edge.line.p1.y, bdx);
+
+           return _cairo_int64_cmp (bdy_dx, dy_bdx);
+       }
+    case HAVE_ALL:
+       /* XXX try comparing (a->edge.line.p2.x - b->edge.line.p2.x) et al */
+       return _cairo_int128_cmp (L, _cairo_int128_sub (B, A));
+    }
+#undef B
+#undef A
+#undef L
+}
+
+/*
+ * We need to compare the x-coordinate of a line for a particular y wrt to a
+ * given x, without loss of precision.
+ *
+ * The x-coordinate along an edge for a given y is:
+ *   X = A_x + (Y - A_y) * A_dx / A_dy
+ *
+ * So the inequality we wish to test is:
+ *   A_x + (Y - A_y) * A_dx / A_dy ∘ X
+ * where ∘ is our inequality operator.
+ *
+ * By construction, we know that A_dy (and (Y - A_y)) are
+ * all positive, so we can rearrange it thus without causing a sign change:
+ *   (Y - A_y) * A_dx ∘ (X - A_x) * A_dy
+ *
+ * Given the assumption that all the deltas fit within 32 bits, we can compute
+ * this comparison directly using 64 bit arithmetic.
+ *
+ * See the similar discussion for _slope_compare() and
+ * edges_compare_x_for_y_general().
+ */
+static int
+edge_compare_for_y_against_x (const cairo_bo_edge_t *a,
+                             int32_t y,
+                             int32_t x)
+{
+    int32_t adx, ady;
+    int32_t dx, dy;
+    cairo_int64_t L, R;
+
+    if (x < a->edge.line.p1.x && x < a->edge.line.p2.x)
+       return 1;
+    if (x > a->edge.line.p1.x && x > a->edge.line.p2.x)
+       return -1;
+
+    adx = a->edge.line.p2.x - a->edge.line.p1.x;
+    dx = x - a->edge.line.p1.x;
+
+    if (adx == 0)
+       return -dx;
+    if (dx == 0 || (adx ^ dx) < 0)
+       return adx;
+
+    dy = y - a->edge.line.p1.y;
+    ady = a->edge.line.p2.y - a->edge.line.p1.y;
+
+    L = _cairo_int32x32_64_mul (dy, adx);
+    R = _cairo_int32x32_64_mul (dx, ady);
+
+    return _cairo_int64_cmp (L, R);
+}
+
+static int
+edges_compare_x_for_y (const cairo_bo_edge_t *a,
+                      const cairo_bo_edge_t *b,
+                      int32_t y)
+{
+    /* If the sweep-line is currently on an end-point of a line,
+     * then we know its precise x value (and considering that we often need to
+     * compare events at end-points, this happens frequently enough to warrant
+     * special casing).
+     */
+    enum {
+       HAVE_NEITHER = 0x0,
+       HAVE_AX      = 0x1,
+       HAVE_BX      = 0x2,
+       HAVE_BOTH    = HAVE_AX | HAVE_BX
+    } have_ax_bx = HAVE_BOTH;
+    int32_t ax, bx;
+
+    if (y == a->edge.line.p1.y)
+       ax = a->edge.line.p1.x;
+    else if (y == a->edge.line.p2.y)
+       ax = a->edge.line.p2.x;
+    else
+       have_ax_bx &= ~HAVE_AX;
+
+    if (y == b->edge.line.p1.y)
+       bx = b->edge.line.p1.x;
+    else if (y == b->edge.line.p2.y)
+       bx = b->edge.line.p2.x;
+    else
+       have_ax_bx &= ~HAVE_BX;
+
+    switch (have_ax_bx) {
+    default:
+    case HAVE_NEITHER:
+       return edges_compare_x_for_y_general (a, b, y);
+    case HAVE_AX:
+       return -edge_compare_for_y_against_x (b, y, ax);
+    case HAVE_BX:
+       return edge_compare_for_y_against_x (a, y, bx);
+    case HAVE_BOTH:
+       return ax - bx;
+    }
+}
+
+static inline int
+_line_equal (const cairo_line_t *a, const cairo_line_t *b)
+{
+    return a->p1.x == b->p1.x && a->p1.y == b->p1.y &&
+           a->p2.x == b->p2.x && a->p2.y == b->p2.y;
+}
+
+static int
+_cairo_bo_sweep_line_compare_edges (cairo_bo_sweep_line_t      *sweep_line,
+                                   const cairo_bo_edge_t       *a,
+                                   const cairo_bo_edge_t       *b)
+{
+    int cmp;
+
+    /* compare the edges if not identical */
+    if (! _line_equal (&a->edge.line, &b->edge.line)) {
+       cmp = edges_compare_x_for_y (a, b, sweep_line->current_y);
+       if (cmp)
+           return cmp;
+
+       /* The two edges intersect exactly at y, so fall back on slope
+        * comparison. We know that this compare_edges function will be
+        * called only when starting a new edge, (not when stopping an
+        * edge), so we don't have to worry about conditionally inverting
+        * the sense of _slope_compare. */
+       cmp = _slope_compare (a, b);
+       if (cmp)
+           return cmp;
+    }
+
+    /* We've got two collinear edges now. */
+    return b->edge.bottom - a->edge.bottom;
+}
+
+static inline cairo_int64_t
+det32_64 (int32_t a, int32_t b,
+         int32_t c, int32_t d)
+{
+    /* det = a * d - b * c */
+    return _cairo_int64_sub (_cairo_int32x32_64_mul (a, d),
+                            _cairo_int32x32_64_mul (b, c));
+}
+
+static inline cairo_int128_t
+det64x32_128 (cairo_int64_t a, int32_t       b,
+             cairo_int64_t c, int32_t       d)
+{
+    /* det = a * d - b * c */
+    return _cairo_int128_sub (_cairo_int64x32_128_mul (a, d),
+                             _cairo_int64x32_128_mul (c, b));
+}
+
+/* Compute the intersection of two lines as defined by two edges. The
+ * result is provided as a coordinate pair of 128-bit integers.
+ *
+ * Returns %CAIRO_BO_STATUS_INTERSECTION if there is an intersection or
+ * %CAIRO_BO_STATUS_PARALLEL if the two lines are exactly parallel.
+ */
+static cairo_bool_t
+intersect_lines (cairo_bo_edge_t               *a,
+                cairo_bo_edge_t                *b,
+                cairo_bo_intersect_point_t     *intersection)
+{
+    cairo_int64_t a_det, b_det;
+
+    /* XXX: We're assuming here that dx and dy will still fit in 32
+     * bits. That's not true in general as there could be overflow. We
+     * should prevent that before the tessellation algorithm begins.
+     * What we're doing to mitigate this is to perform clamping in
+     * cairo_bo_tessellate_polygon().
+     */
+    int32_t dx1 = a->edge.line.p1.x - a->edge.line.p2.x;
+    int32_t dy1 = a->edge.line.p1.y - a->edge.line.p2.y;
+
+    int32_t dx2 = b->edge.line.p1.x - b->edge.line.p2.x;
+    int32_t dy2 = b->edge.line.p1.y - b->edge.line.p2.y;
+
+    cairo_int64_t den_det;
+    cairo_int64_t R;
+    cairo_quorem64_t qr;
+
+    den_det = det32_64 (dx1, dy1, dx2, dy2);
+
+     /* Q: Can we determine that the lines do not intersect (within range)
+      * much more cheaply than computing the intersection point i.e. by
+      * avoiding the division?
+      *
+      *   X = ax + t * adx = bx + s * bdx;
+      *   Y = ay + t * ady = by + s * bdy;
+      *   ∴ t * (ady*bdx - bdy*adx) = bdx * (by - ay) + bdy * (ax - bx)
+      *   => t * L = R
+      *
+      * Therefore we can reject any intersection (under the criteria for
+      * valid intersection events) if:
+      *   L^R < 0 => t < 0, or
+      *   L<R => t > 1
+      *
+      * (where top/bottom must at least extend to the line endpoints).
+      *
+      * A similar substitution can be performed for s, yielding:
+      *   s * (ady*bdx - bdy*adx) = ady * (ax - bx) - adx * (ay - by)
+      */
+    R = det32_64 (dx2, dy2,
+                 b->edge.line.p1.x - a->edge.line.p1.x,
+                 b->edge.line.p1.y - a->edge.line.p1.y);
+    if (_cairo_int64_negative (den_det)) {
+       if (_cairo_int64_ge (den_det, R))
+           return FALSE;
+    } else {
+       if (_cairo_int64_le (den_det, R))
+           return FALSE;
+    }
+
+    R = det32_64 (dy1, dx1,
+                 a->edge.line.p1.y - b->edge.line.p1.y,
+                 a->edge.line.p1.x - b->edge.line.p1.x);
+    if (_cairo_int64_negative (den_det)) {
+       if (_cairo_int64_ge (den_det, R))
+           return FALSE;
+    } else {
+       if (_cairo_int64_le (den_det, R))
+           return FALSE;
+    }
+
+    /* We now know that the two lines should intersect within range. */
+
+    a_det = det32_64 (a->edge.line.p1.x, a->edge.line.p1.y,
+                     a->edge.line.p2.x, a->edge.line.p2.y);
+    b_det = det32_64 (b->edge.line.p1.x, b->edge.line.p1.y,
+                     b->edge.line.p2.x, b->edge.line.p2.y);
+
+    /* x = det (a_det, dx1, b_det, dx2) / den_det */
+    qr = _cairo_int_96by64_32x64_divrem (det64x32_128 (a_det, dx1,
+                                                      b_det, dx2),
+                                        den_det);
+    if (_cairo_int64_eq (qr.rem, den_det))
+       return FALSE;
+#if 0
+    intersection->x.exactness = _cairo_int64_is_zero (qr.rem) ? EXACT : INEXACT;
+#else
+    intersection->x.exactness = EXACT;
+    if (! _cairo_int64_is_zero (qr.rem)) {
+       if (_cairo_int64_negative (den_det) ^ _cairo_int64_negative (qr.rem))
+           qr.rem = _cairo_int64_negate (qr.rem);
+       qr.rem = _cairo_int64_mul (qr.rem, _cairo_int32_to_int64 (2));
+       if (_cairo_int64_ge (qr.rem, den_det)) {
+           qr.quo = _cairo_int64_add (qr.quo,
+                                      _cairo_int32_to_int64 (_cairo_int64_negative (qr.quo) ? -1 : 1));
+       } else
+           intersection->x.exactness = INEXACT;
+    }
+#endif
+    intersection->x.ordinate = _cairo_int64_to_int32 (qr.quo);
+
+    /* y = det (a_det, dy1, b_det, dy2) / den_det */
+    qr = _cairo_int_96by64_32x64_divrem (det64x32_128 (a_det, dy1,
+                                                      b_det, dy2),
+                                        den_det);
+    if (_cairo_int64_eq (qr.rem, den_det))
+       return FALSE;
+#if 0
+    intersection->y.exactness = _cairo_int64_is_zero (qr.rem) ? EXACT : INEXACT;
+#else
+    intersection->y.exactness = EXACT;
+    if (! _cairo_int64_is_zero (qr.rem)) {
+       if (_cairo_int64_negative (den_det) ^ _cairo_int64_negative (qr.rem))
+           qr.rem = _cairo_int64_negate (qr.rem);
+       qr.rem = _cairo_int64_mul (qr.rem, _cairo_int32_to_int64 (2));
+       if (_cairo_int64_ge (qr.rem, den_det)) {
+           qr.quo = _cairo_int64_add (qr.quo,
+                                      _cairo_int32_to_int64 (_cairo_int64_negative (qr.quo) ? -1 : 1));
+       } else
+           intersection->y.exactness = INEXACT;
+    }
+#endif
+    intersection->y.ordinate = _cairo_int64_to_int32 (qr.quo);
+
+    return TRUE;
+}
+
+static int
+_cairo_bo_intersect_ordinate_32_compare (cairo_bo_intersect_ordinate_t a,
+                                        int32_t                        b)
+{
+    /* First compare the quotient */
+    if (a.ordinate > b)
+       return +1;
+    if (a.ordinate < b)
+       return -1;
+    /* With quotient identical, if remainder is 0 then compare equal */
+    /* Otherwise, the non-zero remainder makes a > b */
+    return INEXACT == a.exactness;
+}
+
+/* Does the given edge contain the given point. The point must already
+ * be known to be contained within the line determined by the edge,
+ * (most likely the point results from an intersection of this edge
+ * with another).
+ *
+ * If we had exact arithmetic, then this function would simply be a
+ * matter of examining whether the y value of the point lies within
+ * the range of y values of the edge. But since intersection points
+ * are not exact due to being rounded to the nearest integer within
+ * the available precision, we must also examine the x value of the
+ * point.
+ *
+ * The definition of "contains" here is that the given intersection
+ * point will be seen by the sweep line after the start event for the
+ * given edge and before the stop event for the edge. See the comments
+ * in the implementation for more details.
+ */
+static cairo_bool_t
+_cairo_bo_edge_contains_intersect_point (cairo_bo_edge_t               *edge,
+                                        cairo_bo_intersect_point_t     *point)
+{
+    int cmp_top, cmp_bottom;
+
+    /* XXX: When running the actual algorithm, we don't actually need to
+     * compare against edge->top at all here, since any intersection above
+     * top is eliminated early via a slope comparison. We're leaving these
+     * here for now only for the sake of the quadratic-time intersection
+     * finder which needs them.
+     */
+
+    cmp_top = _cairo_bo_intersect_ordinate_32_compare (point->y,
+                                                      edge->edge.top);
+    cmp_bottom = _cairo_bo_intersect_ordinate_32_compare (point->y,
+                                                         edge->edge.bottom);
+
+    if (cmp_top < 0 || cmp_bottom > 0)
+    {
+       return FALSE;
+    }
+
+    if (cmp_top > 0 && cmp_bottom < 0)
+    {
+       return TRUE;
+    }
+
+    /* At this stage, the point lies on the same y value as either
+     * edge->top or edge->bottom, so we have to examine the x value in
+     * order to properly determine containment. */
+
+    /* If the y value of the point is the same as the y value of the
+     * top of the edge, then the x value of the point must be greater
+     * to be considered as inside the edge. Similarly, if the y value
+     * of the point is the same as the y value of the bottom of the
+     * edge, then the x value of the point must be less to be
+     * considered as inside. */
+
+    if (cmp_top == 0) {
+       cairo_fixed_t top_x;
+
+       top_x = _line_compute_intersection_x_for_y (&edge->edge.line,
+                                                   edge->edge.top);
+       return _cairo_bo_intersect_ordinate_32_compare (point->x, top_x) > 0;
+    } else { /* cmp_bottom == 0 */
+       cairo_fixed_t bot_x;
+
+       bot_x = _line_compute_intersection_x_for_y (&edge->edge.line,
+                                                   edge->edge.bottom);
+       return _cairo_bo_intersect_ordinate_32_compare (point->x, bot_x) < 0;
+    }
+}
+
+/* Compute the intersection of two edges. The result is provided as a
+ * coordinate pair of 128-bit integers.
+ *
+ * Returns %CAIRO_BO_STATUS_INTERSECTION if there is an intersection
+ * that is within both edges, %CAIRO_BO_STATUS_NO_INTERSECTION if the
+ * intersection of the lines defined by the edges occurs outside of
+ * one or both edges, and %CAIRO_BO_STATUS_PARALLEL if the two edges
+ * are exactly parallel.
+ *
+ * Note that when determining if a candidate intersection is "inside"
+ * an edge, we consider both the infinitesimal shortening and the
+ * infinitesimal tilt rules described by John Hobby. Specifically, if
+ * the intersection is exactly the same as an edge point, it is
+ * effectively outside (no intersection is returned). Also, if the
+ * intersection point has the same
+ */
+static cairo_bool_t
+_cairo_bo_edge_intersect (cairo_bo_edge_t      *a,
+                         cairo_bo_edge_t       *b,
+                         cairo_bo_point32_t    *intersection)
+{
+    cairo_bo_intersect_point_t quorem;
+
+    if (! intersect_lines (a, b, &quorem))
+       return FALSE;
+
+    if (! _cairo_bo_edge_contains_intersect_point (a, &quorem))
+       return FALSE;
+
+    if (! _cairo_bo_edge_contains_intersect_point (b, &quorem))
+       return FALSE;
+
+    /* Now that we've correctly compared the intersection point and
+     * determined that it lies within the edge, then we know that we
+     * no longer need any more bits of storage for the intersection
+     * than we do for our edge coordinates. We also no longer need the
+     * remainder from the division. */
+    intersection->x = quorem.x.ordinate;
+    intersection->y = quorem.y.ordinate;
+
+    return TRUE;
+}
+
+static inline int
+cairo_bo_event_compare (const cairo_bo_event_t *a,
+                       const cairo_bo_event_t *b)
+{
+    int cmp;
+
+    cmp = _cairo_bo_point32_compare (&a->point, &b->point);
+    if (cmp)
+       return cmp;
+
+    cmp = a->type - b->type;
+    if (cmp)
+       return cmp;
+
+    return a - b;
+}
+
+static inline void
+_pqueue_init (pqueue_t *pq)
+{
+    pq->max_size = ARRAY_LENGTH (pq->elements_embedded);
+    pq->size = 0;
+
+    pq->elements = pq->elements_embedded;
+}
+
+static inline void
+_pqueue_fini (pqueue_t *pq)
+{
+    if (pq->elements != pq->elements_embedded)
+       free (pq->elements);
+}
+
+static cairo_status_t
+_pqueue_grow (pqueue_t *pq)
+{
+    cairo_bo_event_t **new_elements;
+    pq->max_size *= 2;
+
+    if (pq->elements == pq->elements_embedded) {
+       new_elements = _cairo_malloc_ab (pq->max_size,
+                                        sizeof (cairo_bo_event_t *));
+       if (unlikely (new_elements == NULL))
+           return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+       memcpy (new_elements, pq->elements_embedded,
+               sizeof (pq->elements_embedded));
+    } else {
+       new_elements = _cairo_realloc_ab (pq->elements,
+                                         pq->max_size,
+                                         sizeof (cairo_bo_event_t *));
+       if (unlikely (new_elements == NULL))
+           return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+    }
+
+    pq->elements = new_elements;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static inline cairo_status_t
+_pqueue_push (pqueue_t *pq, cairo_bo_event_t *event)
+{
+    cairo_bo_event_t **elements;
+    int i, parent;
+
+    if (unlikely (pq->size + 1 == pq->max_size)) {
+       cairo_status_t status;
+
+       status = _pqueue_grow (pq);
+       if (unlikely (status))
+           return status;
+    }
+
+    elements = pq->elements;
+
+    for (i = ++pq->size;
+        i != PQ_FIRST_ENTRY &&
+        cairo_bo_event_compare (event,
+                                elements[parent = PQ_PARENT_INDEX (i)]) < 0;
+        i = parent)
+    {
+       elements[i] = elements[parent];
+    }
+
+    elements[i] = event;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static inline void
+_pqueue_pop (pqueue_t *pq)
+{
+    cairo_bo_event_t **elements = pq->elements;
+    cairo_bo_event_t *tail;
+    int child, i;
+
+    tail = elements[pq->size--];
+    if (pq->size == 0) {
+       elements[PQ_FIRST_ENTRY] = NULL;
+       return;
+    }
+
+    for (i = PQ_FIRST_ENTRY;
+        (child = PQ_LEFT_CHILD_INDEX (i)) <= pq->size;
+        i = child)
+    {
+       if (child != pq->size &&
+           cairo_bo_event_compare (elements[child+1],
+                                   elements[child]) < 0)
+       {
+           child++;
+       }
+
+       if (cairo_bo_event_compare (elements[child], tail) >= 0)
+           break;
+
+       elements[i] = elements[child];
+    }
+    elements[i] = tail;
+}
+
+static inline cairo_status_t
+_cairo_bo_event_queue_insert (cairo_bo_event_queue_t   *queue,
+                             cairo_bo_event_type_t      type,
+                             cairo_bo_edge_t           *e1,
+                             cairo_bo_edge_t           *e2,
+                             const cairo_point_t        *point)
+{
+    cairo_bo_queue_event_t *event;
+
+    event = _cairo_freepool_alloc (&queue->pool);
+    if (unlikely (event == NULL))
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    event->type = type;
+    event->e1 = e1;
+    event->e2 = e2;
+    event->point = *point;
+
+    return _pqueue_push (&queue->pqueue, (cairo_bo_event_t *) event);
+}
+
+static void
+_cairo_bo_event_queue_delete (cairo_bo_event_queue_t *queue,
+                             cairo_bo_event_t       *event)
+{
+    _cairo_freepool_free (&queue->pool, event);
+}
+
+static cairo_bo_event_t *
+_cairo_bo_event_dequeue (cairo_bo_event_queue_t *event_queue)
+{
+    cairo_bo_event_t *event, *cmp;
+
+    event = event_queue->pqueue.elements[PQ_FIRST_ENTRY];
+    cmp = *event_queue->start_events;
+    if (event == NULL ||
+       (cmp != NULL && cairo_bo_event_compare (cmp, event) < 0))
+    {
+       event = cmp;
+       event_queue->start_events++;
+    }
+    else
+    {
+       _pqueue_pop (&event_queue->pqueue);
+    }
+
+    return event;
+}
+
+CAIRO_COMBSORT_DECLARE (_cairo_bo_event_queue_sort,
+                       cairo_bo_event_t *,
+                       cairo_bo_event_compare)
+
+static void
+_cairo_bo_event_queue_init (cairo_bo_event_queue_t      *event_queue,
+                           cairo_bo_event_t            **start_events,
+                           int                           num_events)
+{
+    _cairo_bo_event_queue_sort (start_events, num_events);
+    start_events[num_events] = NULL;
+
+    event_queue->start_events = start_events;
+
+    _cairo_freepool_init (&event_queue->pool,
+                         sizeof (cairo_bo_queue_event_t));
+    _pqueue_init (&event_queue->pqueue);
+    event_queue->pqueue.elements[PQ_FIRST_ENTRY] = NULL;
+}
+
+static cairo_status_t
+event_queue_insert_stop (cairo_bo_event_queue_t        *event_queue,
+                        cairo_bo_edge_t                *edge)
+{
+    cairo_bo_point32_t point;
+
+    point.y = edge->edge.bottom;
+    point.x = _line_compute_intersection_x_for_y (&edge->edge.line,
+                                                 point.y);
+    return _cairo_bo_event_queue_insert (event_queue,
+                                        CAIRO_BO_EVENT_TYPE_STOP,
+                                        edge, NULL,
+                                        &point);
+}
+
+static void
+_cairo_bo_event_queue_fini (cairo_bo_event_queue_t *event_queue)
+{
+    _pqueue_fini (&event_queue->pqueue);
+    _cairo_freepool_fini (&event_queue->pool);
+}
+
+static inline cairo_status_t
+event_queue_insert_if_intersect_below_current_y (cairo_bo_event_queue_t        *event_queue,
+                                                cairo_bo_edge_t        *left,
+                                                cairo_bo_edge_t *right)
+{
+    cairo_bo_point32_t intersection;
+
+    if (_line_equal (&left->edge.line, &right->edge.line))
+       return CAIRO_STATUS_SUCCESS;
+
+    /* The names "left" and "right" here are correct descriptions of
+     * the order of the two edges within the active edge list. So if a
+     * slope comparison also puts left less than right, then we know
+     * that the intersection of these two segments has already
+     * occurred before the current sweep line position. */
+    if (_slope_compare (left, right) <= 0)
+       return CAIRO_STATUS_SUCCESS;
+
+    if (! _cairo_bo_edge_intersect (left, right, &intersection))
+       return CAIRO_STATUS_SUCCESS;
+
+    return _cairo_bo_event_queue_insert (event_queue,
+                                        CAIRO_BO_EVENT_TYPE_INTERSECTION,
+                                        left, right,
+                                        &intersection);
+}
+
+static void
+_cairo_bo_sweep_line_init (cairo_bo_sweep_line_t *sweep_line)
+{
+    sweep_line->head = NULL;
+    sweep_line->current_y = INT32_MIN;
+    sweep_line->current_edge = NULL;
+}
+
+static cairo_status_t
+sweep_line_insert (cairo_bo_sweep_line_t       *sweep_line,
+                  cairo_bo_edge_t              *edge)
+{
+    if (sweep_line->current_edge != NULL) {
+       cairo_bo_edge_t *prev, *next;
+       int cmp;
+
+       cmp = _cairo_bo_sweep_line_compare_edges (sweep_line,
+                                                 sweep_line->current_edge,
+                                                 edge);
+       if (cmp < 0) {
+           prev = sweep_line->current_edge;
+           next = prev->next;
+           while (next != NULL &&
+                  _cairo_bo_sweep_line_compare_edges (sweep_line,
+                                                      next, edge) < 0)
+           {
+               prev = next, next = prev->next;
+           }
+
+           prev->next = edge;
+           edge->prev = prev;
+           edge->next = next;
+           if (next != NULL)
+               next->prev = edge;
+       } else if (cmp > 0) {
+           next = sweep_line->current_edge;
+           prev = next->prev;
+           while (prev != NULL &&
+                  _cairo_bo_sweep_line_compare_edges (sweep_line,
+                                                      prev, edge) > 0)
+           {
+               next = prev, prev = next->prev;
+           }
+
+           next->prev = edge;
+           edge->next = next;
+           edge->prev = prev;
+           if (prev != NULL)
+               prev->next = edge;
+           else
+               sweep_line->head = edge;
+       } else {
+           prev = sweep_line->current_edge;
+           edge->prev = prev;
+           edge->next = prev->next;
+           if (prev->next != NULL)
+               prev->next->prev = edge;
+           prev->next = edge;
+       }
+    } else {
+       sweep_line->head = edge;
+    }
+
+    sweep_line->current_edge = edge;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_bo_sweep_line_delete (cairo_bo_sweep_line_t     *sweep_line,
+                            cairo_bo_edge_t    *edge)
+{
+    if (edge->prev != NULL)
+       edge->prev->next = edge->next;
+    else
+       sweep_line->head = edge->next;
+
+    if (edge->next != NULL)
+       edge->next->prev = edge->prev;
+
+    if (sweep_line->current_edge == edge)
+       sweep_line->current_edge = edge->prev ? edge->prev : edge->next;
+}
+
+static void
+_cairo_bo_sweep_line_swap (cairo_bo_sweep_line_t       *sweep_line,
+                          cairo_bo_edge_t              *left,
+                          cairo_bo_edge_t              *right)
+{
+    if (left->prev != NULL)
+       left->prev->next = right;
+    else
+       sweep_line->head = right;
+
+    if (right->next != NULL)
+       right->next->prev = left;
+
+    left->next = right->next;
+    right->next = left;
+
+    right->prev = left->prev;
+    left->prev = right;
+}
+
+static inline cairo_bool_t
+edges_colinear (const cairo_bo_edge_t *a, const cairo_bo_edge_t *b)
+{
+    if (_line_equal (&a->edge.line, &b->edge.line))
+       return TRUE;
+
+    if (_slope_compare (a, b))
+       return FALSE;
+
+    /* The choice of y is not truly arbitrary since we must guarantee that it
+     * is greater than the start of either line.
+     */
+    if (a->edge.line.p1.y == b->edge.line.p1.y) {
+       return a->edge.line.p1.x == b->edge.line.p1.x;
+    } else if (a->edge.line.p1.y < b->edge.line.p1.y) {
+       return edge_compare_for_y_against_x (b,
+                                            a->edge.line.p1.y,
+                                            a->edge.line.p1.x) == 0;
+    } else {
+       return edge_compare_for_y_against_x (a,
+                                            b->edge.line.p1.y,
+                                            b->edge.line.p1.x) == 0;
+    }
+}
+
+static void
+edges_end (cairo_bo_edge_t     *left,
+          int32_t               bot,
+          cairo_polygon_t      *polygon)
+{
+    cairo_bo_deferred_t *l = &left->deferred;
+    cairo_bo_edge_t *right = l->other;
+
+    assert(right->deferred.other == NULL);
+    if (likely (l->top < bot)) {
+       _cairo_polygon_add_line (polygon, &left->edge.line, l->top, bot, 1);
+       _cairo_polygon_add_line (polygon, &right->edge.line, l->top, bot, -1);
+    }
+
+    l->other = NULL;
+}
+
+static inline void
+edges_start_or_continue (cairo_bo_edge_t       *left,
+                        cairo_bo_edge_t        *right,
+                        int                     top,
+                        cairo_polygon_t        *polygon)
+{
+    if (left->deferred.other == right)
+       return;
+
+    if (left->deferred.other != NULL) {
+       if (right != NULL && edges_colinear (left->deferred.other, right)) {
+           /* continuation on right, so just swap edges */
+           assert (left->deferred.other->deferred.other == NULL);
+           left->deferred.other = right;
+           return;
+       }
+
+       edges_end (left, top, polygon);
+    }
+
+    if (right != NULL && ! edges_colinear (left, right)) {
+       left->deferred.top = top;
+       left->deferred.other = right;
+    }
+}
+
+#define is_zero(w) ((w)[0] == 0 || (w)[1] == 0)
+
+static inline void
+active_edges (cairo_bo_edge_t          *left,
+             int32_t                    top,
+             cairo_polygon_t           *polygon)
+{
+       cairo_bo_edge_t *right;
+       int winding[2] = {0, 0};
+
+       /* Yes, this is naive. Consider this a placeholder. */
+
+       while (left != NULL) {
+           assert (is_zero (winding));
+
+           do {
+               winding[left->a_or_b] += left->edge.dir;
+               if (! is_zero (winding))
+                   break;
+
+               if unlikely ((left->deferred.other))
+                   edges_end (left, top, polygon);
+
+               left = left->next;
+               if (! left)
+                   return;
+           } while (1);
+
+           right = left->next;
+           do {
+               if unlikely ((right->deferred.other))
+                   edges_end (right, top, polygon);
+
+               winding[right->a_or_b] += right->edge.dir;
+               if (is_zero (winding)) {
+                   if (right->next == NULL ||
+                       ! edges_colinear (right, right->next))
+                       break;
+               }
+
+               right = right->next;
+           } while (right);
+
+           edges_start_or_continue (left, right, top, polygon);
+
+           left = right;
+           if (left != NULL)
+               left = left->next;
+       }
+}
+
+static cairo_status_t
+intersection_sweep (cairo_bo_event_t   **start_events,
+                   int                  num_events,
+                   cairo_polygon_t     *polygon)
+{
+    cairo_status_t status = CAIRO_STATUS_SUCCESS; /* silence compiler */
+    cairo_bo_event_queue_t event_queue;
+    cairo_bo_sweep_line_t sweep_line;
+    cairo_bo_event_t *event;
+    cairo_bo_edge_t *left, *right;
+    cairo_bo_edge_t *e1, *e2;
+
+    _cairo_bo_event_queue_init (&event_queue, start_events, num_events);
+    _cairo_bo_sweep_line_init (&sweep_line);
+
+    while ((event = _cairo_bo_event_dequeue (&event_queue))) {
+       if (event->point.y != sweep_line.current_y) {
+           active_edges (sweep_line.head,
+                         sweep_line.current_y,
+                         polygon);
+           sweep_line.current_y = event->point.y;
+       }
+
+       switch (event->type) {
+       case CAIRO_BO_EVENT_TYPE_START:
+           e1 = &((cairo_bo_start_event_t *) event)->edge;
+
+           status = sweep_line_insert (&sweep_line, e1);
+           if (unlikely (status))
+               goto unwind;
+
+           status = event_queue_insert_stop (&event_queue, e1);
+           if (unlikely (status))
+               goto unwind;
+
+           left = e1->prev;
+           right = e1->next;
+
+           if (left != NULL) {
+               status = event_queue_insert_if_intersect_below_current_y (&event_queue, left, e1);
+               if (unlikely (status))
+                   goto unwind;
+           }
+
+           if (right != NULL) {
+               status = event_queue_insert_if_intersect_below_current_y (&event_queue, e1, right);
+               if (unlikely (status))
+                   goto unwind;
+           }
+
+           break;
+
+       case CAIRO_BO_EVENT_TYPE_STOP:
+           e1 = ((cairo_bo_queue_event_t *) event)->e1;
+           _cairo_bo_event_queue_delete (&event_queue, event);
+
+           if (e1->deferred.other)
+               edges_end (e1, sweep_line.current_y, polygon);
+
+           left = e1->prev;
+           right = e1->next;
+
+           _cairo_bo_sweep_line_delete (&sweep_line, e1);
+
+           if (left != NULL && right != NULL) {
+               status = event_queue_insert_if_intersect_below_current_y (&event_queue, left, right);
+               if (unlikely (status))
+                   goto unwind;
+           }
+
+           break;
+
+       case CAIRO_BO_EVENT_TYPE_INTERSECTION:
+           e1 = ((cairo_bo_queue_event_t *) event)->e1;
+           e2 = ((cairo_bo_queue_event_t *) event)->e2;
+           _cairo_bo_event_queue_delete (&event_queue, event);
+
+           /* skip this intersection if its edges are not adjacent */
+           if (e2 != e1->next)
+               break;
+
+           left = e1->prev;
+           right = e2->next;
+
+           _cairo_bo_sweep_line_swap (&sweep_line, e1, e2);
+
+           /* after the swap e2 is left of e1 */
+
+           if (left != NULL) {
+               status = event_queue_insert_if_intersect_below_current_y (&event_queue, left, e2);
+               if (unlikely (status))
+                   goto unwind;
+           }
+
+           if (right != NULL) {
+               status = event_queue_insert_if_intersect_below_current_y (&event_queue, e1, right);
+               if (unlikely (status))
+                   goto unwind;
+           }
+
+           break;
+       }
+    }
+
+ unwind:
+    _cairo_bo_event_queue_fini (&event_queue);
+
+    return status;
+}
+
+cairo_status_t
+_cairo_polygon_intersect (cairo_polygon_t *a, int winding_a,
+                         cairo_polygon_t *b, int winding_b)
+{
+    cairo_status_t status;
+    cairo_bo_start_event_t stack_events[CAIRO_STACK_ARRAY_LENGTH (cairo_bo_start_event_t)];
+    cairo_bo_start_event_t *events;
+    cairo_bo_event_t *stack_event_ptrs[ARRAY_LENGTH (stack_events) + 1];
+    cairo_bo_event_t **event_ptrs;
+    int num_events;
+    int i, j;
+
+    /* XXX lazy */
+    if (winding_a != CAIRO_FILL_RULE_WINDING) {
+       status = _cairo_polygon_reduce (a, winding_a);
+       if (unlikely (status))
+           return status;
+    }
+
+    if (winding_b != CAIRO_FILL_RULE_WINDING) {
+       status = _cairo_polygon_reduce (b, winding_b);
+       if (unlikely (status))
+           return status;
+    }
+
+    if (unlikely (0 == a->num_edges))
+       return CAIRO_STATUS_SUCCESS;
+
+    if (unlikely (b->num_edges == 0)) {
+       a->num_edges = 0;
+       return CAIRO_STATUS_SUCCESS;
+    }
+
+    events = stack_events;
+    event_ptrs = stack_event_ptrs;
+    num_events = a->num_edges + b->num_edges;
+    if (num_events > ARRAY_LENGTH (stack_events)) {
+       events = _cairo_malloc_ab_plus_c (num_events,
+                                         sizeof (cairo_bo_start_event_t) +
+                                         sizeof (cairo_bo_event_t *),
+                                         sizeof (cairo_bo_event_t *));
+       if (unlikely (events == NULL))
+           return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+       event_ptrs = (cairo_bo_event_t **) (events + num_events);
+    }
+
+    j = 0;
+    for (i = 0; i < a->num_edges; i++) {
+       event_ptrs[j] = (cairo_bo_event_t *) &events[j];
+
+       events[j].type = CAIRO_BO_EVENT_TYPE_START;
+       events[j].point.y = a->edges[i].top;
+       events[j].point.x =
+           _line_compute_intersection_x_for_y (&a->edges[i].line,
+                                               events[j].point.y);
+
+       events[j].edge.a_or_b = 0;
+       events[j].edge.edge = a->edges[i];
+       events[j].edge.deferred.other = NULL;
+       events[j].edge.prev = NULL;
+       events[j].edge.next = NULL;
+       j++;
+    }
+
+    for (i = 0; i < b->num_edges; i++) {
+       event_ptrs[j] = (cairo_bo_event_t *) &events[j];
+
+       events[j].type = CAIRO_BO_EVENT_TYPE_START;
+       events[j].point.y = b->edges[i].top;
+       events[j].point.x =
+           _line_compute_intersection_x_for_y (&b->edges[i].line,
+                                               events[j].point.y);
+
+       events[j].edge.a_or_b = 1;
+       events[j].edge.edge = b->edges[i];
+       events[j].edge.deferred.other = NULL;
+       events[j].edge.prev = NULL;
+       events[j].edge.next = NULL;
+       j++;
+    }
+    assert (j == num_events);
+
+#if 0
+    {
+       FILE *file = fopen ("clip_a.txt", "w");
+       _cairo_debug_print_polygon (file, a);
+       fclose (file);
+    }
+    {
+       FILE *file = fopen ("clip_b.txt", "w");
+       _cairo_debug_print_polygon (file, b);
+       fclose (file);
+    }
+#endif
+
+    a->num_edges = 0;
+    status = intersection_sweep (event_ptrs, num_events, a);
+    if (events != stack_events)
+       free (events);
+
+#if 0
+    {
+       FILE *file = fopen ("clip_result.txt", "w");
+       _cairo_debug_print_polygon (file, a);
+       fclose (file);
+    }
+#endif
+
+    return status;
+}
+
+cairo_status_t
+_cairo_polygon_intersect_with_boxes (cairo_polygon_t *polygon,
+                                    cairo_fill_rule_t *winding,
+                                    cairo_box_t *boxes,
+                                    int num_boxes)
+{
+    cairo_polygon_t b;
+    cairo_status_t status;
+    int n;
+
+    if (num_boxes == 0) {
+       polygon->num_edges = 0;
+       return CAIRO_STATUS_SUCCESS;
+    }
+
+    for (n = 0; n < num_boxes; n++) {
+       if (polygon->extents.p1.x >= boxes[n].p1.x &&
+           polygon->extents.p2.x <= boxes[n].p2.x &&
+           polygon->extents.p1.y >= boxes[n].p1.y &&
+           polygon->extents.p2.y <= boxes[n].p2.y)
+       {
+           return CAIRO_STATUS_SUCCESS;
+       }
+    }
+
+    _cairo_polygon_init (&b, NULL, 0);
+    for (n = 0; n < num_boxes; n++) {
+       cairo_point_t p1, p2;
+
+       p1.y = boxes[n].p1.y;
+       p2.y = boxes[n].p2.y;
+
+       p2.x = p1.x = boxes[n].p1.x;
+       _cairo_polygon_add_external_edge (&b, &p1, &p2);
+
+       p2.x = p1.x = boxes[n].p2.x;
+       _cairo_polygon_add_external_edge (&b, &p2, &p1);
+    }
+
+    status = _cairo_polygon_intersect (polygon, *winding,
+                                      &b, CAIRO_FILL_RULE_WINDING);
+    _cairo_polygon_fini (&b);
+
+    *winding = CAIRO_FILL_RULE_WINDING;
+    return status;
+}
diff --git a/src/cairo-polygon-reduce.c b/src/cairo-polygon-reduce.c
new file mode 100644 (file)
index 0000000..f18e234
--- /dev/null
@@ -0,0 +1,1491 @@
+/*
+ * Copyright © 2004 Carl Worth
+ * Copyright © 2006 Red Hat, Inc.
+ * Copyright © 2008 Chris Wilson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Carl Worth
+ *
+ * Contributor(s):
+ *     Carl D. Worth <cworth@cworth.org>
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+/* Provide definitions for standalone compilation */
+#include "cairoint.h"
+
+#include "cairo-error-private.h"
+#include "cairo-freelist-private.h"
+#include "cairo-combsort-private.h"
+
+typedef cairo_point_t cairo_bo_point32_t;
+
+typedef struct _cairo_bo_intersect_ordinate {
+    int32_t ordinate;
+    enum { EXACT, INEXACT } exactness;
+} cairo_bo_intersect_ordinate_t;
+
+typedef struct _cairo_bo_intersect_point {
+    cairo_bo_intersect_ordinate_t x;
+    cairo_bo_intersect_ordinate_t y;
+} cairo_bo_intersect_point_t;
+
+typedef struct _cairo_bo_edge cairo_bo_edge_t;
+
+typedef struct _cairo_bo_deferred {
+    cairo_bo_edge_t *right;
+    int32_t top;
+} cairo_bo_deferred_t;
+
+struct _cairo_bo_edge {
+    cairo_edge_t edge;
+    cairo_bo_edge_t *prev;
+    cairo_bo_edge_t *next;
+    cairo_bo_deferred_t deferred;
+};
+
+/* the parent is always given by index/2 */
+#define PQ_PARENT_INDEX(i) ((i) >> 1)
+#define PQ_FIRST_ENTRY 1
+
+/* left and right children are index * 2 and (index * 2) +1 respectively */
+#define PQ_LEFT_CHILD_INDEX(i) ((i) << 1)
+
+typedef enum {
+    CAIRO_BO_EVENT_TYPE_STOP,
+    CAIRO_BO_EVENT_TYPE_INTERSECTION,
+    CAIRO_BO_EVENT_TYPE_START
+} cairo_bo_event_type_t;
+
+typedef struct _cairo_bo_event {
+    cairo_bo_event_type_t type;
+    cairo_point_t point;
+} cairo_bo_event_t;
+
+typedef struct _cairo_bo_start_event {
+    cairo_bo_event_type_t type;
+    cairo_point_t point;
+    cairo_bo_edge_t edge;
+} cairo_bo_start_event_t;
+
+typedef struct _cairo_bo_queue_event {
+    cairo_bo_event_type_t type;
+    cairo_point_t point;
+    cairo_bo_edge_t *e1;
+    cairo_bo_edge_t *e2;
+} cairo_bo_queue_event_t;
+
+typedef struct _pqueue {
+    int size, max_size;
+
+    cairo_bo_event_t **elements;
+    cairo_bo_event_t *elements_embedded[1024];
+} pqueue_t;
+
+typedef struct _cairo_bo_event_queue {
+    cairo_freepool_t pool;
+    pqueue_t pqueue;
+    cairo_bo_event_t **start_events;
+} cairo_bo_event_queue_t;
+
+typedef struct _cairo_bo_sweep_line {
+    cairo_bo_edge_t *head;
+    cairo_bo_edge_t *stopped;
+    int32_t current_y;
+    cairo_bo_edge_t *current_edge;
+} cairo_bo_sweep_line_t;
+
+static cairo_fixed_t
+_line_compute_intersection_x_for_y (const cairo_line_t *line,
+                                   cairo_fixed_t y)
+{
+    cairo_fixed_t x, dy;
+
+    if (y == line->p1.y)
+       return line->p1.x;
+    if (y == line->p2.y)
+       return line->p2.x;
+
+    x = line->p1.x;
+    dy = line->p2.y - line->p1.y;
+    if (dy != 0) {
+       x += _cairo_fixed_mul_div_floor (y - line->p1.y,
+                                        line->p2.x - line->p1.x,
+                                        dy);
+    }
+
+    return x;
+}
+
+static inline int
+_cairo_bo_point32_compare (cairo_bo_point32_t const *a,
+                          cairo_bo_point32_t const *b)
+{
+    int cmp;
+
+    cmp = a->y - b->y;
+    if (cmp)
+       return cmp;
+
+    return a->x - b->x;
+}
+
+/* Compare the slope of a to the slope of b, returning 1, 0, -1 if the
+ * slope a is respectively greater than, equal to, or less than the
+ * slope of b.
+ *
+ * For each edge, consider the direction vector formed from:
+ *
+ *     top -> bottom
+ *
+ * which is:
+ *
+ *     (dx, dy) = (line.p2.x - line.p1.x, line.p2.y - line.p1.y)
+ *
+ * We then define the slope of each edge as dx/dy, (which is the
+ * inverse of the slope typically used in math instruction). We never
+ * compute a slope directly as the value approaches infinity, but we
+ * can derive a slope comparison without division as follows, (where
+ * the ? represents our compare operator).
+ *
+ * 1.     slope(a) ? slope(b)
+ * 2.      adx/ady ? bdx/bdy
+ * 3.  (adx * bdy) ? (bdx * ady)
+ *
+ * Note that from step 2 to step 3 there is no change needed in the
+ * sign of the result since both ady and bdy are guaranteed to be
+ * greater than or equal to 0.
+ *
+ * When using this slope comparison to sort edges, some care is needed
+ * when interpreting the results. Since the slope compare operates on
+ * distance vectors from top to bottom it gives a correct left to
+ * right sort for edges that have a common top point, (such as two
+ * edges with start events at the same location). On the other hand,
+ * the sense of the result will be exactly reversed for two edges that
+ * have a common stop point.
+ */
+static inline int
+_slope_compare (const cairo_bo_edge_t *a,
+               const cairo_bo_edge_t *b)
+{
+    /* XXX: We're assuming here that dx and dy will still fit in 32
+     * bits. That's not true in general as there could be overflow. We
+     * should prevent that before the tessellation algorithm
+     * begins.
+     */
+    int32_t adx = a->edge.line.p2.x - a->edge.line.p1.x;
+    int32_t bdx = b->edge.line.p2.x - b->edge.line.p1.x;
+
+    /* Since the dy's are all positive by construction we can fast
+     * path several common cases.
+     */
+
+    /* First check for vertical lines. */
+    if (adx == 0)
+       return -bdx;
+    if (bdx == 0)
+       return adx;
+
+    /* Then where the two edges point in different directions wrt x. */
+    if ((adx ^ bdx) < 0)
+       return adx;
+
+    /* Finally we actually need to do the general comparison. */
+    {
+       int32_t ady = a->edge.line.p2.y - a->edge.line.p1.y;
+       int32_t bdy = b->edge.line.p2.y - b->edge.line.p1.y;
+       cairo_int64_t adx_bdy = _cairo_int32x32_64_mul (adx, bdy);
+       cairo_int64_t bdx_ady = _cairo_int32x32_64_mul (bdx, ady);
+
+       return _cairo_int64_cmp (adx_bdy, bdx_ady);
+    }
+}
+
+/*
+ * We need to compare the x-coordinates of a pair of lines for a particular y,
+ * without loss of precision.
+ *
+ * The x-coordinate along an edge for a given y is:
+ *   X = A_x + (Y - A_y) * A_dx / A_dy
+ *
+ * So the inequality we wish to test is:
+ *   A_x + (Y - A_y) * A_dx / A_dy ∘ B_x + (Y - B_y) * B_dx / B_dy,
+ * where ∘ is our inequality operator.
+ *
+ * By construction, we know that A_dy and B_dy (and (Y - A_y), (Y - B_y)) are
+ * all positive, so we can rearrange it thus without causing a sign change:
+ *   A_dy * B_dy * (A_x - B_x) ∘ (Y - B_y) * B_dx * A_dy
+ *                                 - (Y - A_y) * A_dx * B_dy
+ *
+ * Given the assumption that all the deltas fit within 32 bits, we can compute
+ * this comparison directly using 128 bit arithmetic. For certain, but common,
+ * input we can reduce this down to a single 32 bit compare by inspecting the
+ * deltas.
+ *
+ * (And put the burden of the work on developing fast 128 bit ops, which are
+ * required throughout the tessellator.)
+ *
+ * See the similar discussion for _slope_compare().
+ */
+static int
+edges_compare_x_for_y_general (const cairo_bo_edge_t *a,
+                              const cairo_bo_edge_t *b,
+                              int32_t y)
+{
+    /* XXX: We're assuming here that dx and dy will still fit in 32
+     * bits. That's not true in general as there could be overflow. We
+     * should prevent that before the tessellation algorithm
+     * begins.
+     */
+    int32_t dx;
+    int32_t adx, ady;
+    int32_t bdx, bdy;
+    enum {
+       HAVE_NONE    = 0x0,
+       HAVE_DX      = 0x1,
+       HAVE_ADX     = 0x2,
+       HAVE_DX_ADX  = HAVE_DX | HAVE_ADX,
+       HAVE_BDX     = 0x4,
+       HAVE_DX_BDX  = HAVE_DX | HAVE_BDX,
+       HAVE_ADX_BDX = HAVE_ADX | HAVE_BDX,
+       HAVE_ALL     = HAVE_DX | HAVE_ADX | HAVE_BDX
+    } have_dx_adx_bdx = HAVE_ALL;
+
+    /* don't bother solving for abscissa if the edges' bounding boxes
+     * can be used to order them. */
+    {
+           int32_t amin, amax;
+           int32_t bmin, bmax;
+           if (a->edge.line.p1.x < a->edge.line.p2.x) {
+                   amin = a->edge.line.p1.x;
+                   amax = a->edge.line.p2.x;
+           } else {
+                   amin = a->edge.line.p2.x;
+                   amax = a->edge.line.p1.x;
+           }
+           if (b->edge.line.p1.x < b->edge.line.p2.x) {
+                   bmin = b->edge.line.p1.x;
+                   bmax = b->edge.line.p2.x;
+           } else {
+                   bmin = b->edge.line.p2.x;
+                   bmax = b->edge.line.p1.x;
+           }
+           if (amax < bmin) return -1;
+           if (amin > bmax) return +1;
+    }
+
+    ady = a->edge.line.p2.y - a->edge.line.p1.y;
+    adx = a->edge.line.p2.x - a->edge.line.p1.x;
+    if (adx == 0)
+       have_dx_adx_bdx &= ~HAVE_ADX;
+
+    bdy = b->edge.line.p2.y - b->edge.line.p1.y;
+    bdx = b->edge.line.p2.x - b->edge.line.p1.x;
+    if (bdx == 0)
+       have_dx_adx_bdx &= ~HAVE_BDX;
+
+    dx = a->edge.line.p1.x - b->edge.line.p1.x;
+    if (dx == 0)
+       have_dx_adx_bdx &= ~HAVE_DX;
+
+#define L _cairo_int64x32_128_mul (_cairo_int32x32_64_mul (ady, bdy), dx)
+#define A _cairo_int64x32_128_mul (_cairo_int32x32_64_mul (adx, bdy), y - a->edge.line.p1.y)
+#define B _cairo_int64x32_128_mul (_cairo_int32x32_64_mul (bdx, ady), y - b->edge.line.p1.y)
+    switch (have_dx_adx_bdx) {
+    default:
+    case HAVE_NONE:
+       return 0;
+    case HAVE_DX:
+       /* A_dy * B_dy * (A_x - B_x) ∘ 0 */
+       return dx; /* ady * bdy is positive definite */
+    case HAVE_ADX:
+       /* 0 ∘  - (Y - A_y) * A_dx * B_dy */
+       return adx; /* bdy * (y - a->top.y) is positive definite */
+    case HAVE_BDX:
+       /* 0 ∘ (Y - B_y) * B_dx * A_dy */
+       return -bdx; /* ady * (y - b->top.y) is positive definite */
+    case HAVE_ADX_BDX:
+       /*  0 ∘ (Y - B_y) * B_dx * A_dy - (Y - A_y) * A_dx * B_dy */
+       if ((adx ^ bdx) < 0) {
+           return adx;
+       } else if (a->edge.line.p1.y == b->edge.line.p1.y) { /* common origin */
+           cairo_int64_t adx_bdy, bdx_ady;
+
+           /* ∴ A_dx * B_dy ∘ B_dx * A_dy */
+
+           adx_bdy = _cairo_int32x32_64_mul (adx, bdy);
+           bdx_ady = _cairo_int32x32_64_mul (bdx, ady);
+
+           return _cairo_int64_cmp (adx_bdy, bdx_ady);
+       } else
+           return _cairo_int128_cmp (A, B);
+    case HAVE_DX_ADX:
+       /* A_dy * (A_x - B_x) ∘ - (Y - A_y) * A_dx */
+       if ((-adx ^ dx) < 0) {
+           return dx;
+       } else {
+           cairo_int64_t ady_dx, dy_adx;
+
+           ady_dx = _cairo_int32x32_64_mul (ady, dx);
+           dy_adx = _cairo_int32x32_64_mul (a->edge.line.p1.y - y, adx);
+
+           return _cairo_int64_cmp (ady_dx, dy_adx);
+       }
+    case HAVE_DX_BDX:
+       /* B_dy * (A_x - B_x) ∘ (Y - B_y) * B_dx */
+       if ((bdx ^ dx) < 0) {
+           return dx;
+       } else {
+           cairo_int64_t bdy_dx, dy_bdx;
+
+           bdy_dx = _cairo_int32x32_64_mul (bdy, dx);
+           dy_bdx = _cairo_int32x32_64_mul (y - b->edge.line.p1.y, bdx);
+
+           return _cairo_int64_cmp (bdy_dx, dy_bdx);
+       }
+    case HAVE_ALL:
+       /* XXX try comparing (a->edge.line.p2.x - b->edge.line.p2.x) et al */
+       return _cairo_int128_cmp (L, _cairo_int128_sub (B, A));
+    }
+#undef B
+#undef A
+#undef L
+}
+
+/*
+ * We need to compare the x-coordinate of a line for a particular y wrt to a
+ * given x, without loss of precision.
+ *
+ * The x-coordinate along an edge for a given y is:
+ *   X = A_x + (Y - A_y) * A_dx / A_dy
+ *
+ * So the inequality we wish to test is:
+ *   A_x + (Y - A_y) * A_dx / A_dy ∘ X
+ * where ∘ is our inequality operator.
+ *
+ * By construction, we know that A_dy (and (Y - A_y)) are
+ * all positive, so we can rearrange it thus without causing a sign change:
+ *   (Y - A_y) * A_dx ∘ (X - A_x) * A_dy
+ *
+ * Given the assumption that all the deltas fit within 32 bits, we can compute
+ * this comparison directly using 64 bit arithmetic.
+ *
+ * See the similar discussion for _slope_compare() and
+ * edges_compare_x_for_y_general().
+ */
+static int
+edge_compare_for_y_against_x (const cairo_bo_edge_t *a,
+                             int32_t y,
+                             int32_t x)
+{
+    int32_t adx, ady;
+    int32_t dx, dy;
+    cairo_int64_t L, R;
+
+    if (x < a->edge.line.p1.x && x < a->edge.line.p2.x)
+       return 1;
+    if (x > a->edge.line.p1.x && x > a->edge.line.p2.x)
+       return -1;
+
+    adx = a->edge.line.p2.x - a->edge.line.p1.x;
+    dx = x - a->edge.line.p1.x;
+
+    if (adx == 0)
+       return -dx;
+    if (dx == 0 || (adx ^ dx) < 0)
+       return adx;
+
+    dy = y - a->edge.line.p1.y;
+    ady = a->edge.line.p2.y - a->edge.line.p1.y;
+
+    L = _cairo_int32x32_64_mul (dy, adx);
+    R = _cairo_int32x32_64_mul (dx, ady);
+
+    return _cairo_int64_cmp (L, R);
+}
+
+static int
+edges_compare_x_for_y (const cairo_bo_edge_t *a,
+                      const cairo_bo_edge_t *b,
+                      int32_t y)
+{
+    /* If the sweep-line is currently on an end-point of a line,
+     * then we know its precise x value (and considering that we often need to
+     * compare events at end-points, this happens frequently enough to warrant
+     * special casing).
+     */
+    enum {
+       HAVE_NEITHER = 0x0,
+       HAVE_AX      = 0x1,
+       HAVE_BX      = 0x2,
+       HAVE_BOTH    = HAVE_AX | HAVE_BX
+    } have_ax_bx = HAVE_BOTH;
+    int32_t ax, bx;
+
+    if (y == a->edge.line.p1.y)
+       ax = a->edge.line.p1.x;
+    else if (y == a->edge.line.p2.y)
+       ax = a->edge.line.p2.x;
+    else
+       have_ax_bx &= ~HAVE_AX;
+
+    if (y == b->edge.line.p1.y)
+       bx = b->edge.line.p1.x;
+    else if (y == b->edge.line.p2.y)
+       bx = b->edge.line.p2.x;
+    else
+       have_ax_bx &= ~HAVE_BX;
+
+    switch (have_ax_bx) {
+    default:
+    case HAVE_NEITHER:
+       return edges_compare_x_for_y_general (a, b, y);
+    case HAVE_AX:
+       return -edge_compare_for_y_against_x (b, y, ax);
+    case HAVE_BX:
+       return edge_compare_for_y_against_x (a, y, bx);
+    case HAVE_BOTH:
+       return ax - bx;
+    }
+}
+
+static inline int
+_line_equal (const cairo_line_t *a, const cairo_line_t *b)
+{
+    return a->p1.x == b->p1.x && a->p1.y == b->p1.y &&
+           a->p2.x == b->p2.x && a->p2.y == b->p2.y;
+}
+
+static int
+_cairo_bo_sweep_line_compare_edges (cairo_bo_sweep_line_t      *sweep_line,
+                                   const cairo_bo_edge_t       *a,
+                                   const cairo_bo_edge_t       *b)
+{
+    int cmp;
+
+    /* compare the edges if not identical */
+    if (! _line_equal (&a->edge.line, &b->edge.line)) {
+       cmp = edges_compare_x_for_y (a, b, sweep_line->current_y);
+       if (cmp)
+           return cmp;
+
+       /* The two edges intersect exactly at y, so fall back on slope
+        * comparison. We know that this compare_edges function will be
+        * called only when starting a new edge, (not when stopping an
+        * edge), so we don't have to worry about conditionally inverting
+        * the sense of _slope_compare. */
+       cmp = _slope_compare (a, b);
+       if (cmp)
+           return cmp;
+    }
+
+    /* We've got two collinear edges now. */
+    return b->edge.bottom - a->edge.bottom;
+}
+
+static inline cairo_int64_t
+det32_64 (int32_t a, int32_t b,
+         int32_t c, int32_t d)
+{
+    /* det = a * d - b * c */
+    return _cairo_int64_sub (_cairo_int32x32_64_mul (a, d),
+                            _cairo_int32x32_64_mul (b, c));
+}
+
+static inline cairo_int128_t
+det64x32_128 (cairo_int64_t a, int32_t       b,
+             cairo_int64_t c, int32_t       d)
+{
+    /* det = a * d - b * c */
+    return _cairo_int128_sub (_cairo_int64x32_128_mul (a, d),
+                             _cairo_int64x32_128_mul (c, b));
+}
+
+/* Compute the intersection of two lines as defined by two edges. The
+ * result is provided as a coordinate pair of 128-bit integers.
+ *
+ * Returns %CAIRO_BO_STATUS_INTERSECTION if there is an intersection or
+ * %CAIRO_BO_STATUS_PARALLEL if the two lines are exactly parallel.
+ */
+static cairo_bool_t
+intersect_lines (cairo_bo_edge_t               *a,
+                cairo_bo_edge_t                *b,
+                cairo_bo_intersect_point_t     *intersection)
+{
+    cairo_int64_t a_det, b_det;
+
+    /* XXX: We're assuming here that dx and dy will still fit in 32
+     * bits. That's not true in general as there could be overflow. We
+     * should prevent that before the tessellation algorithm begins.
+     * What we're doing to mitigate this is to perform clamping in
+     * cairo_bo_tessellate_polygon().
+     */
+    int32_t dx1 = a->edge.line.p1.x - a->edge.line.p2.x;
+    int32_t dy1 = a->edge.line.p1.y - a->edge.line.p2.y;
+
+    int32_t dx2 = b->edge.line.p1.x - b->edge.line.p2.x;
+    int32_t dy2 = b->edge.line.p1.y - b->edge.line.p2.y;
+
+    cairo_int64_t den_det;
+    cairo_int64_t R;
+    cairo_quorem64_t qr;
+
+    den_det = det32_64 (dx1, dy1, dx2, dy2);
+
+     /* Q: Can we determine that the lines do not intersect (within range)
+      * much more cheaply than computing the intersection point i.e. by
+      * avoiding the division?
+      *
+      *   X = ax + t * adx = bx + s * bdx;
+      *   Y = ay + t * ady = by + s * bdy;
+      *   ∴ t * (ady*bdx - bdy*adx) = bdx * (by - ay) + bdy * (ax - bx)
+      *   => t * L = R
+      *
+      * Therefore we can reject any intersection (under the criteria for
+      * valid intersection events) if:
+      *   L^R < 0 => t < 0, or
+      *   L<R => t > 1
+      *
+      * (where top/bottom must at least extend to the line endpoints).
+      *
+      * A similar substitution can be performed for s, yielding:
+      *   s * (ady*bdx - bdy*adx) = ady * (ax - bx) - adx * (ay - by)
+      */
+    R = det32_64 (dx2, dy2,
+                 b->edge.line.p1.x - a->edge.line.p1.x,
+                 b->edge.line.p1.y - a->edge.line.p1.y);
+    if (_cairo_int64_negative (den_det)) {
+       if (_cairo_int64_ge (den_det, R))
+           return FALSE;
+    } else {
+       if (_cairo_int64_le (den_det, R))
+           return FALSE;
+    }
+
+    R = det32_64 (dy1, dx1,
+                 a->edge.line.p1.y - b->edge.line.p1.y,
+                 a->edge.line.p1.x - b->edge.line.p1.x);
+    if (_cairo_int64_negative (den_det)) {
+       if (_cairo_int64_ge (den_det, R))
+           return FALSE;
+    } else {
+       if (_cairo_int64_le (den_det, R))
+           return FALSE;
+    }
+
+    /* We now know that the two lines should intersect within range. */
+
+    a_det = det32_64 (a->edge.line.p1.x, a->edge.line.p1.y,
+                     a->edge.line.p2.x, a->edge.line.p2.y);
+    b_det = det32_64 (b->edge.line.p1.x, b->edge.line.p1.y,
+                     b->edge.line.p2.x, b->edge.line.p2.y);
+
+    /* x = det (a_det, dx1, b_det, dx2) / den_det */
+    qr = _cairo_int_96by64_32x64_divrem (det64x32_128 (a_det, dx1,
+                                                      b_det, dx2),
+                                        den_det);
+    if (_cairo_int64_eq (qr.rem, den_det))
+       return FALSE;
+#if 0
+    intersection->x.exactness = _cairo_int64_is_zero (qr.rem) ? EXACT : INEXACT;
+#else
+    intersection->x.exactness = EXACT;
+    if (! _cairo_int64_is_zero (qr.rem)) {
+       if (_cairo_int64_negative (den_det) ^ _cairo_int64_negative (qr.rem))
+           qr.rem = _cairo_int64_negate (qr.rem);
+       qr.rem = _cairo_int64_mul (qr.rem, _cairo_int32_to_int64 (2));
+       if (_cairo_int64_ge (qr.rem, den_det)) {
+           qr.quo = _cairo_int64_add (qr.quo,
+                                      _cairo_int32_to_int64 (_cairo_int64_negative (qr.quo) ? -1 : 1));
+       } else
+           intersection->x.exactness = INEXACT;
+    }
+#endif
+    intersection->x.ordinate = _cairo_int64_to_int32 (qr.quo);
+
+    /* y = det (a_det, dy1, b_det, dy2) / den_det */
+    qr = _cairo_int_96by64_32x64_divrem (det64x32_128 (a_det, dy1,
+                                                      b_det, dy2),
+                                        den_det);
+    if (_cairo_int64_eq (qr.rem, den_det))
+       return FALSE;
+#if 0
+    intersection->y.exactness = _cairo_int64_is_zero (qr.rem) ? EXACT : INEXACT;
+#else
+    intersection->y.exactness = EXACT;
+    if (! _cairo_int64_is_zero (qr.rem)) {
+       if (_cairo_int64_negative (den_det) ^ _cairo_int64_negative (qr.rem))
+           qr.rem = _cairo_int64_negate (qr.rem);
+       qr.rem = _cairo_int64_mul (qr.rem, _cairo_int32_to_int64 (2));
+       if (_cairo_int64_ge (qr.rem, den_det)) {
+           qr.quo = _cairo_int64_add (qr.quo,
+                                      _cairo_int32_to_int64 (_cairo_int64_negative (qr.quo) ? -1 : 1));
+       } else
+           intersection->y.exactness = INEXACT;
+    }
+#endif
+    intersection->y.ordinate = _cairo_int64_to_int32 (qr.quo);
+
+    return TRUE;
+}
+
+static int
+_cairo_bo_intersect_ordinate_32_compare (cairo_bo_intersect_ordinate_t a,
+                                        int32_t                        b)
+{
+    /* First compare the quotient */
+    if (a.ordinate > b)
+       return +1;
+    if (a.ordinate < b)
+       return -1;
+    /* With quotient identical, if remainder is 0 then compare equal */
+    /* Otherwise, the non-zero remainder makes a > b */
+    return INEXACT == a.exactness;
+}
+
+/* Does the given edge contain the given point. The point must already
+ * be known to be contained within the line determined by the edge,
+ * (most likely the point results from an intersection of this edge
+ * with another).
+ *
+ * If we had exact arithmetic, then this function would simply be a
+ * matter of examining whether the y value of the point lies within
+ * the range of y values of the edge. But since intersection points
+ * are not exact due to being rounded to the nearest integer within
+ * the available precision, we must also examine the x value of the
+ * point.
+ *
+ * The definition of "contains" here is that the given intersection
+ * point will be seen by the sweep line after the start event for the
+ * given edge and before the stop event for the edge. See the comments
+ * in the implementation for more details.
+ */
+static cairo_bool_t
+_cairo_bo_edge_contains_intersect_point (cairo_bo_edge_t               *edge,
+                                        cairo_bo_intersect_point_t     *point)
+{
+    int cmp_top, cmp_bottom;
+
+    /* XXX: When running the actual algorithm, we don't actually need to
+     * compare against edge->top at all here, since any intersection above
+     * top is eliminated early via a slope comparison. We're leaving these
+     * here for now only for the sake of the quadratic-time intersection
+     * finder which needs them.
+     */
+
+    cmp_top = _cairo_bo_intersect_ordinate_32_compare (point->y,
+                                                      edge->edge.top);
+    cmp_bottom = _cairo_bo_intersect_ordinate_32_compare (point->y,
+                                                         edge->edge.bottom);
+
+    if (cmp_top < 0 || cmp_bottom > 0)
+    {
+       return FALSE;
+    }
+
+    if (cmp_top > 0 && cmp_bottom < 0)
+    {
+       return TRUE;
+    }
+
+    /* At this stage, the point lies on the same y value as either
+     * edge->top or edge->bottom, so we have to examine the x value in
+     * order to properly determine containment. */
+
+    /* If the y value of the point is the same as the y value of the
+     * top of the edge, then the x value of the point must be greater
+     * to be considered as inside the edge. Similarly, if the y value
+     * of the point is the same as the y value of the bottom of the
+     * edge, then the x value of the point must be less to be
+     * considered as inside. */
+
+    if (cmp_top == 0) {
+       cairo_fixed_t top_x;
+
+       top_x = _line_compute_intersection_x_for_y (&edge->edge.line,
+                                                   edge->edge.top);
+       return _cairo_bo_intersect_ordinate_32_compare (point->x, top_x) > 0;
+    } else { /* cmp_bottom == 0 */
+       cairo_fixed_t bot_x;
+
+       bot_x = _line_compute_intersection_x_for_y (&edge->edge.line,
+                                                   edge->edge.bottom);
+       return _cairo_bo_intersect_ordinate_32_compare (point->x, bot_x) < 0;
+    }
+}
+
+/* Compute the intersection of two edges. The result is provided as a
+ * coordinate pair of 128-bit integers.
+ *
+ * Returns %CAIRO_BO_STATUS_INTERSECTION if there is an intersection
+ * that is within both edges, %CAIRO_BO_STATUS_NO_INTERSECTION if the
+ * intersection of the lines defined by the edges occurs outside of
+ * one or both edges, and %CAIRO_BO_STATUS_PARALLEL if the two edges
+ * are exactly parallel.
+ *
+ * Note that when determining if a candidate intersection is "inside"
+ * an edge, we consider both the infinitesimal shortening and the
+ * infinitesimal tilt rules described by John Hobby. Specifically, if
+ * the intersection is exactly the same as an edge point, it is
+ * effectively outside (no intersection is returned). Also, if the
+ * intersection point has the same
+ */
+static cairo_bool_t
+_cairo_bo_edge_intersect (cairo_bo_edge_t      *a,
+                         cairo_bo_edge_t       *b,
+                         cairo_bo_point32_t    *intersection)
+{
+    cairo_bo_intersect_point_t quorem;
+
+    if (! intersect_lines (a, b, &quorem))
+       return FALSE;
+
+    if (! _cairo_bo_edge_contains_intersect_point (a, &quorem))
+       return FALSE;
+
+    if (! _cairo_bo_edge_contains_intersect_point (b, &quorem))
+       return FALSE;
+
+    /* Now that we've correctly compared the intersection point and
+     * determined that it lies within the edge, then we know that we
+     * no longer need any more bits of storage for the intersection
+     * than we do for our edge coordinates. We also no longer need the
+     * remainder from the division. */
+    intersection->x = quorem.x.ordinate;
+    intersection->y = quorem.y.ordinate;
+
+    return TRUE;
+}
+
+static inline int
+cairo_bo_event_compare (const cairo_bo_event_t *a,
+                       const cairo_bo_event_t *b)
+{
+    int cmp;
+
+    cmp = _cairo_bo_point32_compare (&a->point, &b->point);
+    if (cmp)
+       return cmp;
+
+    cmp = a->type - b->type;
+    if (cmp)
+       return cmp;
+
+    return a - b;
+}
+
+static inline void
+_pqueue_init (pqueue_t *pq)
+{
+    pq->max_size = ARRAY_LENGTH (pq->elements_embedded);
+    pq->size = 0;
+
+    pq->elements = pq->elements_embedded;
+}
+
+static inline void
+_pqueue_fini (pqueue_t *pq)
+{
+    if (pq->elements != pq->elements_embedded)
+       free (pq->elements);
+}
+
+static cairo_status_t
+_pqueue_grow (pqueue_t *pq)
+{
+    cairo_bo_event_t **new_elements;
+    pq->max_size *= 2;
+
+    if (pq->elements == pq->elements_embedded) {
+       new_elements = _cairo_malloc_ab (pq->max_size,
+                                        sizeof (cairo_bo_event_t *));
+       if (unlikely (new_elements == NULL))
+           return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+       memcpy (new_elements, pq->elements_embedded,
+               sizeof (pq->elements_embedded));
+    } else {
+       new_elements = _cairo_realloc_ab (pq->elements,
+                                         pq->max_size,
+                                         sizeof (cairo_bo_event_t *));
+       if (unlikely (new_elements == NULL))
+           return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+    }
+
+    pq->elements = new_elements;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static inline cairo_status_t
+_pqueue_push (pqueue_t *pq, cairo_bo_event_t *event)
+{
+    cairo_bo_event_t **elements;
+    int i, parent;
+
+    if (unlikely (pq->size + 1 == pq->max_size)) {
+       cairo_status_t status;
+
+       status = _pqueue_grow (pq);
+       if (unlikely (status))
+           return status;
+    }
+
+    elements = pq->elements;
+
+    for (i = ++pq->size;
+        i != PQ_FIRST_ENTRY &&
+        cairo_bo_event_compare (event,
+                                elements[parent = PQ_PARENT_INDEX (i)]) < 0;
+        i = parent)
+    {
+       elements[i] = elements[parent];
+    }
+
+    elements[i] = event;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static inline void
+_pqueue_pop (pqueue_t *pq)
+{
+    cairo_bo_event_t **elements = pq->elements;
+    cairo_bo_event_t *tail;
+    int child, i;
+
+    tail = elements[pq->size--];
+    if (pq->size == 0) {
+       elements[PQ_FIRST_ENTRY] = NULL;
+       return;
+    }
+
+    for (i = PQ_FIRST_ENTRY;
+        (child = PQ_LEFT_CHILD_INDEX (i)) <= pq->size;
+        i = child)
+    {
+       if (child != pq->size &&
+           cairo_bo_event_compare (elements[child+1],
+                                   elements[child]) < 0)
+       {
+           child++;
+       }
+
+       if (cairo_bo_event_compare (elements[child], tail) >= 0)
+           break;
+
+       elements[i] = elements[child];
+    }
+    elements[i] = tail;
+}
+
+static inline cairo_status_t
+_cairo_bo_event_queue_insert (cairo_bo_event_queue_t   *queue,
+                             cairo_bo_event_type_t      type,
+                             cairo_bo_edge_t           *e1,
+                             cairo_bo_edge_t           *e2,
+                             const cairo_point_t        *point)
+{
+    cairo_bo_queue_event_t *event;
+
+    event = _cairo_freepool_alloc (&queue->pool);
+    if (unlikely (event == NULL))
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    event->type = type;
+    event->e1 = e1;
+    event->e2 = e2;
+    event->point = *point;
+
+    return _pqueue_push (&queue->pqueue, (cairo_bo_event_t *) event);
+}
+
+static void
+_cairo_bo_event_queue_delete (cairo_bo_event_queue_t *queue,
+                             cairo_bo_event_t       *event)
+{
+    _cairo_freepool_free (&queue->pool, event);
+}
+
+static cairo_bo_event_t *
+_cairo_bo_event_dequeue (cairo_bo_event_queue_t *event_queue)
+{
+    cairo_bo_event_t *event, *cmp;
+
+    event = event_queue->pqueue.elements[PQ_FIRST_ENTRY];
+    cmp = *event_queue->start_events;
+    if (event == NULL ||
+       (cmp != NULL && cairo_bo_event_compare (cmp, event) < 0))
+    {
+       event = cmp;
+       event_queue->start_events++;
+    }
+    else
+    {
+       _pqueue_pop (&event_queue->pqueue);
+    }
+
+    return event;
+}
+
+CAIRO_COMBSORT_DECLARE (_cairo_bo_event_queue_sort,
+                       cairo_bo_event_t *,
+                       cairo_bo_event_compare)
+
+static void
+_cairo_bo_event_queue_init (cairo_bo_event_queue_t      *event_queue,
+                           cairo_bo_event_t            **start_events,
+                           int                           num_events)
+{
+    _cairo_bo_event_queue_sort (start_events, num_events);
+    start_events[num_events] = NULL;
+
+    event_queue->start_events = start_events;
+
+    _cairo_freepool_init (&event_queue->pool,
+                         sizeof (cairo_bo_queue_event_t));
+    _pqueue_init (&event_queue->pqueue);
+    event_queue->pqueue.elements[PQ_FIRST_ENTRY] = NULL;
+}
+
+static cairo_status_t
+_cairo_bo_event_queue_insert_stop (cairo_bo_event_queue_t      *event_queue,
+                                  cairo_bo_edge_t              *edge)
+{
+    cairo_bo_point32_t point;
+
+    point.y = edge->edge.bottom;
+    point.x = _line_compute_intersection_x_for_y (&edge->edge.line,
+                                                 point.y);
+    return _cairo_bo_event_queue_insert (event_queue,
+                                        CAIRO_BO_EVENT_TYPE_STOP,
+                                        edge, NULL,
+                                        &point);
+}
+
+static void
+_cairo_bo_event_queue_fini (cairo_bo_event_queue_t *event_queue)
+{
+    _pqueue_fini (&event_queue->pqueue);
+    _cairo_freepool_fini (&event_queue->pool);
+}
+
+static inline cairo_status_t
+_cairo_bo_event_queue_insert_if_intersect_below_current_y (cairo_bo_event_queue_t      *event_queue,
+                                                          cairo_bo_edge_t      *left,
+                                                          cairo_bo_edge_t *right)
+{
+    cairo_bo_point32_t intersection;
+
+    if (_line_equal (&left->edge.line, &right->edge.line))
+       return CAIRO_STATUS_SUCCESS;
+
+    /* The names "left" and "right" here are correct descriptions of
+     * the order of the two edges within the active edge list. So if a
+     * slope comparison also puts left less than right, then we know
+     * that the intersection of these two segments has already
+     * occurred before the current sweep line position. */
+    if (_slope_compare (left, right) <= 0)
+       return CAIRO_STATUS_SUCCESS;
+
+    if (! _cairo_bo_edge_intersect (left, right, &intersection))
+       return CAIRO_STATUS_SUCCESS;
+
+    return _cairo_bo_event_queue_insert (event_queue,
+                                        CAIRO_BO_EVENT_TYPE_INTERSECTION,
+                                        left, right,
+                                        &intersection);
+}
+
+static void
+_cairo_bo_sweep_line_init (cairo_bo_sweep_line_t *sweep_line)
+{
+    sweep_line->head = NULL;
+    sweep_line->stopped = NULL;
+    sweep_line->current_y = INT32_MIN;
+    sweep_line->current_edge = NULL;
+}
+
+static cairo_status_t
+_cairo_bo_sweep_line_insert (cairo_bo_sweep_line_t     *sweep_line,
+                            cairo_bo_edge_t            *edge)
+{
+    if (sweep_line->current_edge != NULL) {
+       cairo_bo_edge_t *prev, *next;
+       int cmp;
+
+       cmp = _cairo_bo_sweep_line_compare_edges (sweep_line,
+                                                 sweep_line->current_edge,
+                                                 edge);
+       if (cmp < 0) {
+           prev = sweep_line->current_edge;
+           next = prev->next;
+           while (next != NULL &&
+                  _cairo_bo_sweep_line_compare_edges (sweep_line,
+                                                      next, edge) < 0)
+           {
+               prev = next, next = prev->next;
+           }
+
+           prev->next = edge;
+           edge->prev = prev;
+           edge->next = next;
+           if (next != NULL)
+               next->prev = edge;
+       } else if (cmp > 0) {
+           next = sweep_line->current_edge;
+           prev = next->prev;
+           while (prev != NULL &&
+                  _cairo_bo_sweep_line_compare_edges (sweep_line,
+                                                      prev, edge) > 0)
+           {
+               next = prev, prev = next->prev;
+           }
+
+           next->prev = edge;
+           edge->next = next;
+           edge->prev = prev;
+           if (prev != NULL)
+               prev->next = edge;
+           else
+               sweep_line->head = edge;
+       } else {
+           prev = sweep_line->current_edge;
+           edge->prev = prev;
+           edge->next = prev->next;
+           if (prev->next != NULL)
+               prev->next->prev = edge;
+           prev->next = edge;
+       }
+    } else {
+       sweep_line->head = edge;
+    }
+
+    sweep_line->current_edge = edge;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_bo_sweep_line_delete (cairo_bo_sweep_line_t     *sweep_line,
+                            cairo_bo_edge_t    *edge)
+{
+    if (edge->prev != NULL)
+       edge->prev->next = edge->next;
+    else
+       sweep_line->head = edge->next;
+
+    if (edge->next != NULL)
+       edge->next->prev = edge->prev;
+
+    if (sweep_line->current_edge == edge)
+       sweep_line->current_edge = edge->prev ? edge->prev : edge->next;
+}
+
+static void
+_cairo_bo_sweep_line_swap (cairo_bo_sweep_line_t       *sweep_line,
+                          cairo_bo_edge_t              *left,
+                          cairo_bo_edge_t              *right)
+{
+    if (left->prev != NULL)
+       left->prev->next = right;
+    else
+       sweep_line->head = right;
+
+    if (right->next != NULL)
+       right->next->prev = left;
+
+    left->next = right->next;
+    right->next = left;
+
+    right->prev = left->prev;
+    left->prev = right;
+}
+
+static inline cairo_bool_t
+edges_colinear (const cairo_bo_edge_t *a, const cairo_bo_edge_t *b)
+{
+    if (_line_equal (&a->edge.line, &b->edge.line))
+       return TRUE;
+
+    if (_slope_compare (a, b))
+       return FALSE;
+
+    /* The choice of y is not truly arbitrary since we must guarantee that it
+     * is greater than the start of either line.
+     */
+    if (a->edge.line.p1.y == b->edge.line.p1.y) {
+       return a->edge.line.p1.x == b->edge.line.p1.x;
+    } else if (a->edge.line.p1.y < b->edge.line.p1.y) {
+       return edge_compare_for_y_against_x (b,
+                                            a->edge.line.p1.y,
+                                            a->edge.line.p1.x) == 0;
+    } else {
+       return edge_compare_for_y_against_x (a,
+                                            b->edge.line.p1.y,
+                                            b->edge.line.p1.x) == 0;
+    }
+}
+
+static void
+_cairo_bo_edge_end (cairo_bo_edge_t    *left,
+                   int32_t              bot,
+                   cairo_polygon_t     *polygon)
+{
+    cairo_bo_deferred_t *d = &left->deferred;
+
+    if (likely (d->top < bot)) {
+       _cairo_polygon_add_line (polygon,
+                                &left->edge.line,
+                                d->top, bot,
+                                1);
+       _cairo_polygon_add_line (polygon,
+                                &d->right->edge.line,
+                                d->top, bot,
+                                -1);
+    }
+
+    d->right = NULL;
+}
+
+
+static inline void
+_cairo_bo_edge_start_or_continue (cairo_bo_edge_t      *left,
+                                 cairo_bo_edge_t       *right,
+                                 int                    top,
+                                 cairo_polygon_t       *polygon)
+{
+    if (left->deferred.right == right)
+       return;
+
+    if (left->deferred.right != NULL) {
+       if (right != NULL && edges_colinear (left->deferred.right, right))
+       {
+           /* continuation on right, so just swap edges */
+           left->deferred.right = right;
+           return;
+       }
+
+       _cairo_bo_edge_end (left, top, polygon);
+    }
+
+    if (right != NULL && ! edges_colinear (left, right)) {
+       left->deferred.top = top;
+       left->deferred.right = right;
+    }
+}
+
+static inline void
+_active_edges_to_polygon (cairo_bo_edge_t              *left,
+                         int32_t                        top,
+                         cairo_fill_rule_t              fill_rule,
+                         cairo_polygon_t               *polygon)
+{
+    cairo_bo_edge_t *right;
+
+    if (fill_rule == CAIRO_FILL_RULE_WINDING) {
+       while (left != NULL) {
+           int in_out = left->edge.dir;
+
+           right = left->next;
+           if (left->deferred.right == NULL) {
+               while (right != NULL && right->deferred.right == NULL)
+                   right = right->next;
+
+               if (right != NULL && edges_colinear (left, right)) {
+                   /* continuation on left */
+                   left->deferred = right->deferred;
+                   right->deferred.right = NULL;
+               }
+           }
+
+           right = left->next;
+           while (right != NULL) {
+               if (right->deferred.right != NULL)
+                   _cairo_bo_edge_end (right, top, polygon);
+
+               in_out += right->edge.dir;
+               if (in_out == 0) {
+                   cairo_bo_edge_t *next;
+                   cairo_bool_t skip = FALSE;
+
+                   /* skip co-linear edges */
+                   next = right->next;
+                   if (next != NULL)
+                       skip = edges_colinear (right, next);
+
+                   if (! skip)
+                       break;
+               }
+
+               right = right->next;
+           }
+
+           _cairo_bo_edge_start_or_continue (left, right, top, polygon);
+
+           left = right;
+           if (left != NULL)
+               left = left->next;
+       }
+    } else {
+       while (left != NULL) {
+           int in_out = 0;
+
+           right = left->next;
+           while (right != NULL) {
+               if (right->deferred.right != NULL)
+                   _cairo_bo_edge_end (right, top, polygon);
+
+               if ((in_out++ & 1) == 0) {
+                   cairo_bo_edge_t *next;
+                   cairo_bool_t skip = FALSE;
+
+                   /* skip co-linear edges */
+                   next = right->next;
+                   if (next != NULL)
+                       skip = edges_colinear (right, next);
+
+                   if (! skip)
+                       break;
+               }
+
+               right = right->next;
+           }
+
+           _cairo_bo_edge_start_or_continue (left, right, top, polygon);
+
+           left = right;
+           if (left != NULL)
+               left = left->next;
+       }
+    }
+}
+
+
+static cairo_status_t
+_cairo_bentley_ottmann_tessellate_bo_edges (cairo_bo_event_t   **start_events,
+                                           int                  num_events,
+                                           cairo_fill_rule_t    fill_rule,
+                                           cairo_polygon_t     *polygon)
+{
+    cairo_status_t status = CAIRO_STATUS_SUCCESS; /* silence compiler */
+    cairo_bo_event_queue_t event_queue;
+    cairo_bo_sweep_line_t sweep_line;
+    cairo_bo_event_t *event;
+    cairo_bo_edge_t *left, *right;
+    cairo_bo_edge_t *e1, *e2;
+
+    _cairo_bo_event_queue_init (&event_queue, start_events, num_events);
+    _cairo_bo_sweep_line_init (&sweep_line);
+
+    while ((event = _cairo_bo_event_dequeue (&event_queue))) {
+       if (event->point.y != sweep_line.current_y) {
+           for (e1 = sweep_line.stopped; e1; e1 = e1->next) {
+               if (e1->deferred.right != NULL)
+                    _cairo_bo_edge_end (e1, e1->edge.bottom, polygon);
+           }
+           sweep_line.stopped = NULL;
+
+           _active_edges_to_polygon (sweep_line.head,
+                                     sweep_line.current_y,
+                                     fill_rule, polygon);
+
+           sweep_line.current_y = event->point.y;
+       }
+
+       switch (event->type) {
+       case CAIRO_BO_EVENT_TYPE_START:
+           e1 = &((cairo_bo_start_event_t *) event)->edge;
+
+           status = _cairo_bo_sweep_line_insert (&sweep_line, e1);
+           if (unlikely (status))
+               goto unwind;
+
+           status = _cairo_bo_event_queue_insert_stop (&event_queue, e1);
+           if (unlikely (status))
+               goto unwind;
+
+           /* check to see if this is a continuation of a stopped edge */
+           /* XXX change to an infinitesimal lengthening rule */
+           for (left = sweep_line.stopped; left; left = left->next) {
+               if (e1->edge.top <= left->edge.bottom &&
+                   edges_colinear (e1, left))
+               {
+                   e1->deferred = left->deferred;
+                   if (left->prev != NULL)
+                       left->prev = left->next;
+                   else
+                       sweep_line.stopped = left->next;
+                   if (left->next != NULL)
+                       left->next->prev = left->prev;
+                   break;
+               }
+           }
+
+           left = e1->prev;
+           right = e1->next;
+
+           if (left != NULL) {
+               status = _cairo_bo_event_queue_insert_if_intersect_below_current_y (&event_queue, left, e1);
+               if (unlikely (status))
+                   goto unwind;
+           }
+
+           if (right != NULL) {
+               status = _cairo_bo_event_queue_insert_if_intersect_below_current_y (&event_queue, e1, right);
+               if (unlikely (status))
+                   goto unwind;
+           }
+
+           break;
+
+       case CAIRO_BO_EVENT_TYPE_STOP:
+           e1 = ((cairo_bo_queue_event_t *) event)->e1;
+           _cairo_bo_event_queue_delete (&event_queue, event);
+
+           left = e1->prev;
+           right = e1->next;
+
+           _cairo_bo_sweep_line_delete (&sweep_line, e1);
+
+           /* first, check to see if we have a continuation via a fresh edge */
+           if (e1->deferred.right != NULL) {
+               e1->next = sweep_line.stopped;
+               if (sweep_line.stopped != NULL)
+                   sweep_line.stopped->prev = e1;
+               sweep_line.stopped = e1;
+               e1->prev = NULL;
+           }
+
+           if (left != NULL && right != NULL) {
+               status = _cairo_bo_event_queue_insert_if_intersect_below_current_y (&event_queue, left, right);
+               if (unlikely (status))
+                   goto unwind;
+           }
+
+           break;
+
+       case CAIRO_BO_EVENT_TYPE_INTERSECTION:
+           e1 = ((cairo_bo_queue_event_t *) event)->e1;
+           e2 = ((cairo_bo_queue_event_t *) event)->e2;
+           _cairo_bo_event_queue_delete (&event_queue, event);
+
+           /* skip this intersection if its edges are not adjacent */
+           if (e2 != e1->next)
+               break;
+
+           left = e1->prev;
+           right = e2->next;
+
+           _cairo_bo_sweep_line_swap (&sweep_line, e1, e2);
+
+           /* after the swap e2 is left of e1 */
+
+           if (left != NULL) {
+               status = _cairo_bo_event_queue_insert_if_intersect_below_current_y (&event_queue, left, e2);
+               if (unlikely (status))
+                   goto unwind;
+           }
+
+           if (right != NULL) {
+               status = _cairo_bo_event_queue_insert_if_intersect_below_current_y (&event_queue, e1, right);
+               if (unlikely (status))
+                   goto unwind;
+           }
+
+           break;
+       }
+    }
+
+    for (e1 = sweep_line.stopped; e1; e1 = e1->next) {
+       if (e1->deferred.right != NULL)
+           _cairo_bo_edge_end (e1, e1->edge.bottom, polygon);
+    }
+ unwind:
+    _cairo_bo_event_queue_fini (&event_queue);
+
+    return status;
+}
+
+cairo_status_t
+_cairo_polygon_reduce (cairo_polygon_t *polygon,
+                      cairo_fill_rule_t fill_rule)
+{
+    cairo_status_t status;
+    cairo_bo_start_event_t stack_events[CAIRO_STACK_ARRAY_LENGTH (cairo_bo_start_event_t)];
+    cairo_bo_start_event_t *events;
+    cairo_bo_event_t *stack_event_ptrs[ARRAY_LENGTH (stack_events) + 1];
+    cairo_bo_event_t **event_ptrs;
+    int num_limits;
+    int num_events;
+    int i;
+
+    num_events = polygon->num_edges;
+    if (unlikely (0 == num_events))
+       return CAIRO_STATUS_SUCCESS;
+
+    events = stack_events;
+    event_ptrs = stack_event_ptrs;
+    if (num_events > ARRAY_LENGTH (stack_events)) {
+       events = _cairo_malloc_ab_plus_c (num_events,
+                                         sizeof (cairo_bo_start_event_t) +
+                                         sizeof (cairo_bo_event_t *),
+                                         sizeof (cairo_bo_event_t *));
+       if (unlikely (events == NULL))
+           return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+       event_ptrs = (cairo_bo_event_t **) (events + num_events);
+    }
+
+    for (i = 0; i < num_events; i++) {
+       event_ptrs[i] = (cairo_bo_event_t *) &events[i];
+
+       events[i].type = CAIRO_BO_EVENT_TYPE_START;
+       events[i].point.y = polygon->edges[i].top;
+       events[i].point.x =
+           _line_compute_intersection_x_for_y (&polygon->edges[i].line,
+                                               events[i].point.y);
+
+       events[i].edge.edge = polygon->edges[i];
+       events[i].edge.deferred.right = NULL;
+       events[i].edge.prev = NULL;
+       events[i].edge.next = NULL;
+    }
+
+    num_limits = polygon->num_limits; polygon->num_limits = 0;
+    polygon->num_edges = 0;
+
+    status = _cairo_bentley_ottmann_tessellate_bo_edges (event_ptrs,
+                                                        num_events,
+                                                        fill_rule,
+                                                        polygon);
+     polygon->num_limits = num_limits;
+
+    if (events != stack_events)
+       free (events);
+
+    return status;
+}
diff --git a/src/cairo-polygon.c b/src/cairo-polygon.c
new file mode 100644 (file)
index 0000000..90f81f7
--- /dev/null
@@ -0,0 +1,537 @@
+/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *     Carl D. Worth <cworth@cworth.org>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-boxes-private.h"
+#include "cairo-contour-private.h"
+#include "cairo-error-private.h"
+
+static void
+_cairo_polygon_add_edge (cairo_polygon_t *polygon,
+                        const cairo_point_t *p1,
+                        const cairo_point_t *p2,
+                        int dir);
+
+void
+_cairo_polygon_limit (cairo_polygon_t *polygon,
+                    const cairo_box_t *limits,
+                    int num_limits)
+{
+    int n;
+
+    polygon->limits = limits;
+    polygon->num_limits = num_limits;
+
+    if (polygon->num_limits) {
+       polygon->limit = limits[0];
+       for (n = 1; n < num_limits; n++) {
+           if (limits[n].p1.x < polygon->limit.p1.x)
+               polygon->limit.p1.x = limits[n].p1.x;
+
+           if (limits[n].p1.y < polygon->limit.p1.y)
+               polygon->limit.p1.y = limits[n].p1.y;
+
+           if (limits[n].p2.x > polygon->limit.p2.x)
+               polygon->limit.p2.x = limits[n].p2.x;
+
+           if (limits[n].p2.y > polygon->limit.p2.y)
+               polygon->limit.p2.y = limits[n].p2.y;
+       }
+    }
+}
+
+void
+_cairo_polygon_limit_to_clip (cairo_polygon_t *polygon,
+                             const cairo_clip_t *clip)
+{
+    if (clip)
+       _cairo_polygon_limit (polygon, clip->boxes, clip->num_boxes);
+    else
+       _cairo_polygon_limit (polygon, 0, 0);
+}
+
+void
+_cairo_polygon_init (cairo_polygon_t *polygon,
+                    const cairo_box_t *limits,
+                    int num_limits)
+{
+    VG (VALGRIND_MAKE_MEM_UNDEFINED (polygon, sizeof (cairo_polygon_t)));
+
+    polygon->status = CAIRO_STATUS_SUCCESS;
+
+    polygon->num_edges = 0;
+
+    polygon->edges = polygon->edges_embedded;
+    polygon->edges_size = ARRAY_LENGTH (polygon->edges_embedded);
+
+    polygon->extents.p1.x = polygon->extents.p1.y = INT32_MAX;
+    polygon->extents.p2.x = polygon->extents.p2.y = INT32_MIN;
+
+    _cairo_polygon_limit (polygon, limits, num_limits);
+}
+
+void
+_cairo_polygon_init_with_clip (cairo_polygon_t *polygon,
+                              const cairo_clip_t *clip)
+{
+    if (clip)
+       _cairo_polygon_init (polygon, clip->boxes, clip->num_boxes);
+    else
+       _cairo_polygon_init (polygon, 0, 0);
+}
+
+cairo_status_t
+_cairo_polygon_init_boxes (cairo_polygon_t *polygon,
+                          const cairo_boxes_t *boxes)
+{
+    const struct _cairo_boxes_chunk *chunk;
+    int i;
+
+    VG (VALGRIND_MAKE_MEM_UNDEFINED (polygon, sizeof (cairo_polygon_t)));
+
+    polygon->status = CAIRO_STATUS_SUCCESS;
+
+    polygon->num_edges = 0;
+
+    polygon->edges = polygon->edges_embedded;
+    polygon->edges_size = ARRAY_LENGTH (polygon->edges_embedded);
+    if (boxes->num_boxes > ARRAY_LENGTH (polygon->edges_embedded)/2) {
+       polygon->edges_size = 2 * boxes->num_boxes;
+       polygon->edges = _cairo_malloc_ab (polygon->edges_size,
+                                          2*sizeof(cairo_edge_t));
+       if (unlikely (polygon->edges == NULL))
+           return polygon->status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+    }
+
+    polygon->extents.p1.x = polygon->extents.p1.y = INT32_MAX;
+    polygon->extents.p2.x = polygon->extents.p2.y = INT32_MIN;
+
+    polygon->limits = NULL;
+    polygon->num_limits = 0;
+
+    for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
+       for (i = 0; i < chunk->count; i++) {
+           cairo_point_t p1, p2;
+
+           p1 = chunk->base[i].p1;
+           p2.x = p1.x;
+           p2.y = chunk->base[i].p2.y;
+           _cairo_polygon_add_edge (polygon, &p1, &p2, 1);
+
+           p1 = chunk->base[i].p2;
+           p2.x = p1.x;
+           p2.y = chunk->base[i].p1.y;
+           _cairo_polygon_add_edge (polygon, &p1, &p2, 1);
+       }
+    }
+
+    return polygon->status;
+}
+
+cairo_status_t
+_cairo_polygon_init_box_array (cairo_polygon_t *polygon,
+                              cairo_box_t *boxes,
+                              int num_boxes)
+{
+    int i;
+
+    VG (VALGRIND_MAKE_MEM_UNDEFINED (polygon, sizeof (cairo_polygon_t)));
+
+    polygon->status = CAIRO_STATUS_SUCCESS;
+
+    polygon->num_edges = 0;
+
+    polygon->edges = polygon->edges_embedded;
+    polygon->edges_size = ARRAY_LENGTH (polygon->edges_embedded);
+    if (num_boxes > ARRAY_LENGTH (polygon->edges_embedded)/2) {
+       polygon->edges_size = 2 * num_boxes;
+       polygon->edges = _cairo_malloc_ab (polygon->edges_size,
+                                          2*sizeof(cairo_edge_t));
+       if (unlikely (polygon->edges == NULL))
+           return polygon->status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+    }
+
+    polygon->extents.p1.x = polygon->extents.p1.y = INT32_MAX;
+    polygon->extents.p2.x = polygon->extents.p2.y = INT32_MIN;
+
+    polygon->limits = NULL;
+    polygon->num_limits = 0;
+
+    for (i = 0; i < num_boxes; i++) {
+       cairo_point_t p1, p2;
+
+       p1 = boxes[i].p1;
+       p2.x = p1.x;
+       p2.y = boxes[i].p2.y;
+       _cairo_polygon_add_edge (polygon, &p1, &p2, 1);
+
+       p1 = boxes[i].p2;
+       p2.x = p1.x;
+       p2.y = boxes[i].p1.y;
+       _cairo_polygon_add_edge (polygon, &p1, &p2, 1);
+    }
+
+    return polygon->status;
+}
+
+
+void
+_cairo_polygon_fini (cairo_polygon_t *polygon)
+{
+    if (polygon->edges != polygon->edges_embedded)
+       free (polygon->edges);
+
+    VG (VALGRIND_MAKE_MEM_NOACCESS (polygon, sizeof (cairo_polygon_t)));
+}
+
+/* make room for at least one more edge */
+static cairo_bool_t
+_cairo_polygon_grow (cairo_polygon_t *polygon)
+{
+    cairo_edge_t *new_edges;
+    int old_size = polygon->edges_size;
+    int new_size = 4 * old_size;
+
+    if (CAIRO_INJECT_FAULT ()) {
+       polygon->status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+       return FALSE;
+    }
+
+    if (polygon->edges == polygon->edges_embedded) {
+       new_edges = _cairo_malloc_ab (new_size, sizeof (cairo_edge_t));
+       if (new_edges != NULL)
+           memcpy (new_edges, polygon->edges, old_size * sizeof (cairo_edge_t));
+    } else {
+       new_edges = _cairo_realloc_ab (polygon->edges,
+                                      new_size, sizeof (cairo_edge_t));
+    }
+
+    if (unlikely (new_edges == NULL)) {
+       polygon->status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+       return FALSE;
+    }
+
+    polygon->edges = new_edges;
+    polygon->edges_size = new_size;
+
+    return TRUE;
+}
+
+static void
+_add_edge (cairo_polygon_t *polygon,
+          const cairo_point_t *p1,
+          const cairo_point_t *p2,
+          int top, int bottom,
+          int dir)
+{
+    cairo_edge_t *edge;
+
+    assert (top < bottom);
+
+    if (unlikely (polygon->num_edges == polygon->edges_size)) {
+       if (! _cairo_polygon_grow (polygon))
+           return;
+    }
+
+    edge = &polygon->edges[polygon->num_edges++];
+    edge->line.p1 = *p1;
+    edge->line.p2 = *p2;
+    edge->top = top;
+    edge->bottom = bottom;
+    edge->dir = dir;
+
+    if (top < polygon->extents.p1.y)
+       polygon->extents.p1.y = top;
+    if (bottom > polygon->extents.p2.y)
+       polygon->extents.p2.y = bottom;
+
+    if (p1->x < polygon->extents.p1.x || p1->x > polygon->extents.p2.x) {
+       cairo_fixed_t x = p1->x;
+       if (top != p1->y)
+           x = _cairo_edge_compute_intersection_x_for_y (p1, p2, top);
+       if (x < polygon->extents.p1.x)
+           polygon->extents.p1.x = x;
+       if (x > polygon->extents.p2.x)
+           polygon->extents.p2.x = x;
+    }
+
+    if (p2->x < polygon->extents.p1.x || p2->x > polygon->extents.p2.x) {
+       cairo_fixed_t x = p2->x;
+       if (bottom != p2->y)
+           x = _cairo_edge_compute_intersection_x_for_y (p1, p2, bottom);
+       if (x < polygon->extents.p1.x)
+           polygon->extents.p1.x = x;
+       if (x > polygon->extents.p2.x)
+           polygon->extents.p2.x = x;
+    }
+}
+
+static void
+_add_clipped_edge (cairo_polygon_t *polygon,
+                  const cairo_point_t *p1,
+                  const cairo_point_t *p2,
+                  const int top, const int bottom,
+                  const int dir)
+{
+    cairo_point_t bot_left, top_right;
+    cairo_fixed_t top_y, bot_y;
+    int n;
+
+    for (n = 0; n < polygon->num_limits; n++) {
+       const cairo_box_t *limits = &polygon->limits[n];
+       cairo_fixed_t pleft, pright;
+
+       if (top >= limits->p2.y)
+           continue;
+       if (bottom <= limits->p1.y)
+           continue;
+
+       bot_left.x = limits->p1.x;
+       bot_left.y = limits->p2.y;
+
+       top_right.x = limits->p2.x;
+       top_right.y = limits->p1.y;
+
+       /* The useful region */
+       top_y = MAX (top, limits->p1.y);
+       bot_y = MIN (bottom, limits->p2.y);
+
+       /* The projection of the edge on the horizontal axis */
+       pleft = MIN (p1->x, p2->x);
+       pright = MAX (p1->x, p2->x);
+
+       if (limits->p1.x <= pleft && pright <= limits->p2.x) {
+           /* Projection of the edge completely contained in the box:
+            * clip vertically by restricting top and bottom */
+
+           _add_edge (polygon, p1, p2, top_y, bot_y, dir);
+       } else if (pright <= limits->p1.x) {
+           /* Projection of the edge to the left of the box:
+            * replace with the left side of the box (clipped top/bottom) */
+
+           _add_edge (polygon, &limits->p1, &bot_left, top_y, bot_y, dir);
+       } else if (limits->p2.x <= pleft) {
+           /* Projection of the edge to the right of the box:
+            * replace with the right side of the box (clipped top/bottom) */
+
+           _add_edge (polygon, &top_right, &limits->p2, top_y, bot_y, dir);
+       } else {
+           /* The edge and the box intersect in a generic way */
+           cairo_fixed_t left_y, right_y;
+           cairo_bool_t top_left_to_bottom_right;
+
+           left_y = _cairo_edge_compute_intersection_y_for_x (p1, p2,
+                                                              limits->p1.x);
+           right_y = _cairo_edge_compute_intersection_y_for_x (p1, p2,
+                                                               limits->p2.x);
+
+           /*
+            * The edge intersects the lines corresponding to the left
+            * and right sides of the limit box at left_y and right_y,
+            * but we need to add edges for the range from top_y to
+            * bot_y.
+            *
+            * For both intersections, there are three cases:
+            *
+            *  1) It is outside the vertical range of the limit
+            *     box. In this case we can simply further clip the
+            *     edge we will be emitting (i.e. restrict its
+            *     top/bottom limits to those of the limit box).
+            *
+            *  2) It is inside the vertical range of the limit
+            *     box. In this case, we need to add the vertical edge
+            *     connecting the correct vertex to the intersection,
+            *     in order to preserve the winding count.
+            *
+            *  3) It is exactly on the box. In this case, do nothing.
+            *
+            * These operations restrict the active range (stored in
+            * top_y/bot_y) so that the p1-p2 edge is completely
+            * inside the box if it is clipped to this vertical range.
+            */
+
+           top_left_to_bottom_right = (p1->x < p2->x) == (p1->y < p2->y);
+
+           if (top_left_to_bottom_right) {
+               left_y = MIN (left_y, bot_y);
+               if (top_y < left_y) {
+                   _add_edge (polygon, &limits->p1, &bot_left,
+                              top_y, left_y, dir);
+                   top_y = left_y;
+               }
+
+               right_y = MAX (right_y, top_y);
+               if (bot_y > right_y) {
+                   _add_edge (polygon, &top_right, &limits->p2,
+                              right_y, bot_y, dir);
+                   bot_y = right_y;
+               }
+           } else {
+               right_y = MIN (right_y, bot_y);
+               if (top_y < right_y) {
+                   _add_edge (polygon, &top_right, &limits->p2,
+                              top_y, right_y, dir);
+                   top_y = right_y;
+               }
+
+               left_y = MAX (left_y, top_y);
+               if (bot_y > left_y) {
+                   _add_edge (polygon, &limits->p1, &bot_left,
+                              left_y, bot_y, dir);
+                   bot_y = left_y;
+               }
+           }
+
+           if (top_y != bot_y)
+               _add_edge (polygon, p1, p2, top_y, bot_y, dir);
+       }
+    }
+}
+
+static void
+_cairo_polygon_add_edge (cairo_polygon_t *polygon,
+                        const cairo_point_t *p1,
+                        const cairo_point_t *p2,
+                        int dir)
+{
+    /* drop horizontal edges */
+    if (p1->y == p2->y)
+       return;
+
+    if (p1->y > p2->y) {
+       const cairo_point_t *t;
+       t = p1, p1 = p2, p2 = t;
+       dir = -dir;
+    }
+
+    if (polygon->num_limits) {
+       if (p2->y <= polygon->limit.p1.y)
+           return;
+
+       if (p1->y >= polygon->limit.p2.y)
+           return;
+
+       _add_clipped_edge (polygon, p1, p2, p1->y, p2->y, dir);
+    } else
+       _add_edge (polygon, p1, p2, p1->y, p2->y, dir);
+}
+
+cairo_status_t
+_cairo_polygon_add_external_edge (void *polygon,
+                                 const cairo_point_t *p1,
+                                 const cairo_point_t *p2)
+{
+    _cairo_polygon_add_edge (polygon, p1, p2, 1);
+    return _cairo_polygon_status (polygon);
+}
+
+cairo_status_t
+_cairo_polygon_add_line (cairo_polygon_t *polygon,
+                        const cairo_line_t *line,
+                        int top, int bottom,
+                        int dir)
+{
+    /* drop horizontal edges */
+    if (line->p1.y == line->p2.y)
+       return CAIRO_STATUS_SUCCESS;
+
+    if (bottom <= top)
+       return CAIRO_STATUS_SUCCESS;
+
+    if (polygon->num_limits) {
+       if (line->p2.y <= polygon->limit.p1.y)
+           return CAIRO_STATUS_SUCCESS;
+
+       if (line->p1.y >= polygon->limit.p2.y)
+           return CAIRO_STATUS_SUCCESS;
+
+       _add_clipped_edge (polygon, &line->p1, &line->p2, top, bottom, dir);
+    } else
+       _add_edge (polygon, &line->p1, &line->p2, top, bottom, dir);
+
+    return polygon->status;
+}
+
+cairo_status_t
+_cairo_polygon_add_contour (cairo_polygon_t *polygon,
+                           const cairo_contour_t *contour)
+{
+    const struct _cairo_contour_chain *chain;
+    const cairo_point_t *prev = NULL;
+    int i;
+
+    if (contour->chain.num_points <= 1)
+       return CAIRO_INT_STATUS_SUCCESS;
+
+    prev = &contour->chain.points[0];
+    for (chain = &contour->chain; chain; chain = chain->next) {
+       for (i = 0; i < chain->num_points; i++) {
+           _cairo_polygon_add_edge (polygon, prev, &chain->points[i],
+                                    contour->direction);
+           prev = &chain->points[i];
+       }
+    }
+
+    return polygon->status;
+}
+
+void
+_cairo_polygon_translate (cairo_polygon_t *polygon, int dx, int dy)
+{
+    int n;
+
+    dx = _cairo_fixed_from_int (dx);
+    dy = _cairo_fixed_from_int (dy);
+
+    polygon->extents.p1.x += dx;
+    polygon->extents.p2.x += dx;
+    polygon->extents.p1.y += dy;
+    polygon->extents.p2.y += dy;
+
+    for (n = 0; n < polygon->num_edges; n++) {
+       cairo_edge_t *e = &polygon->edges[n];
+
+       e->top += dy;
+       e->bottom += dy;
+
+       e->line.p1.x += dx;
+       e->line.p2.x += dx;
+       e->line.p1.y += dy;
+       e->line.p2.y += dy;
+    }
+}
diff --git a/src/cairo-private.h b/src/cairo-private.h
new file mode 100644 (file)
index 0000000..9f4f55b
--- /dev/null
@@ -0,0 +1,64 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ *     Carl D. Worth <cworth@redhat.com>
+ */
+
+#ifndef CAIRO_PRIVATE_H
+#define CAIRO_PRIVATE_H
+
+#include "cairo-types-private.h"
+#include "cairo-reference-count-private.h"
+
+CAIRO_BEGIN_DECLS
+
+struct _cairo {
+    cairo_reference_count_t ref_count;
+    cairo_status_t status;
+    cairo_user_data_array_t user_data;
+
+    const cairo_backend_t *backend;
+};
+
+cairo_private cairo_t *
+_cairo_create_in_error (cairo_status_t status);
+
+cairo_private void
+_cairo_init (cairo_t *cr,
+            const cairo_backend_t *backend);
+
+cairo_private void
+_cairo_fini (cairo_t *cr);
+
+CAIRO_END_DECLS
+
+#endif /* CAIRO_PRIVATE_H */
diff --git a/src/cairo-ps-surface-private.h b/src/cairo-ps-surface-private.h
new file mode 100644 (file)
index 0000000..a5a8cd0
--- /dev/null
@@ -0,0 +1,109 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2003 University of Southern California
+ * Copyright © 2005 Red Hat, Inc
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *     Carl D. Worth <cworth@cworth.org>
+ *     Kristian Høgsberg <krh@redhat.com>
+ *     Keith Packard <keithp@keithp.com>
+ */
+
+#ifndef CAIRO_PS_SURFACE_PRIVATE_H
+#define CAIRO_PS_SURFACE_PRIVATE_H
+
+#include "cairo-ps.h"
+
+#include "cairo-surface-private.h"
+#include "cairo-surface-clipper-private.h"
+#include "cairo-pdf-operators-private.h"
+
+#include <time.h>
+
+typedef struct cairo_ps_surface {
+    cairo_surface_t base;
+
+    /* Here final_stream corresponds to the stream/file passed to
+     * cairo_ps_surface_create surface is built. Meanwhile stream is a
+     * temporary stream in which the file output is built, (so that
+     * the header can be built and inserted into the target stream
+     * before the contents of the temporary stream are copied). */
+    cairo_output_stream_t *final_stream;
+
+    FILE *tmpfile;
+    cairo_output_stream_t *stream;
+
+    cairo_bool_t eps;
+    cairo_content_t content;
+    double width;
+    double height;
+    cairo_rectangle_int_t page_bbox;
+    int bbox_x1, bbox_y1, bbox_x2, bbox_y2;
+    cairo_matrix_t cairo_to_ps;
+
+    /* XXX These 3 are used as temporary storage whilst emitting patterns */
+    cairo_image_surface_t *image;
+    cairo_image_surface_t *acquired_image;
+    void *image_extra;
+
+    cairo_bool_t use_string_datasource;
+
+    cairo_bool_t current_pattern_is_solid_color;
+    cairo_color_t current_color;
+
+    int num_pages;
+
+    cairo_paginated_mode_t paginated_mode;
+
+    cairo_bool_t force_fallbacks;
+    cairo_bool_t has_creation_date;
+    time_t creation_date;
+
+    cairo_scaled_font_subsets_t *font_subsets;
+
+    cairo_list_t document_media;
+    cairo_array_t dsc_header_comments;
+    cairo_array_t dsc_setup_comments;
+    cairo_array_t dsc_page_setup_comments;
+
+    cairo_array_t *dsc_comment_target;
+
+    cairo_ps_level_t ps_level;
+    cairo_ps_level_t ps_level_used;
+
+    cairo_surface_clipper_t clipper;
+
+    cairo_pdf_operators_t pdf_operators;
+    cairo_surface_t *paginated_surface;
+} cairo_ps_surface_t;
+
+#endif /* CAIRO_PS_SURFACE_PRIVATE_H */
diff --git a/src/cairo-ps-surface.c b/src/cairo-ps-surface.c
new file mode 100644 (file)
index 0000000..36d555d
--- /dev/null
@@ -0,0 +1,4275 @@
+/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2003 University of Southern California
+ * Copyright © 2005 Red Hat, Inc
+ * Copyright © 2007,2008 Adrian Johnson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *     Carl D. Worth <cworth@cworth.org>
+ *     Kristian Høgsberg <krh@redhat.com>
+ *     Keith Packard <keithp@keithp.com>
+ *     Adrian Johnson <ajohnson@redneon.com>
+ */
+
+
+/*
+ * Design of the PS output:
+ *
+ * The PS output is harmonised with the PDF operations using PS procedures
+ * to emulate the PDF operators.
+ *
+ * This has a number of advantages:
+ *   1. A large chunk of code is shared between the PDF and PS backends.
+ *      See cairo-pdf-operators.
+ *   2. Using gs to do PS -> PDF and PDF -> PS will always work well.
+ */
+
+#define _BSD_SOURCE /* for ctime_r(), snprintf(), strdup() */
+#include "cairoint.h"
+
+#include "cairo-ps.h"
+#include "cairo-ps-surface-private.h"
+
+#include "cairo-pdf-operators-private.h"
+#include "cairo-pdf-shading-private.h"
+
+#include "cairo-array-private.h"
+#include "cairo-composite-rectangles-private.h"
+#include "cairo-default-context-private.h"
+#include "cairo-error-private.h"
+#include "cairo-image-surface-private.h"
+#include "cairo-scaled-font-subsets-private.h"
+#include "cairo-paginated-private.h"
+#include "cairo-recording-surface-private.h"
+#include "cairo-surface-clipper-private.h"
+#include "cairo-surface-snapshot-private.h"
+#include "cairo-surface-subsurface-private.h"
+#include "cairo-output-stream-private.h"
+#include "cairo-type3-glyph-surface-private.h"
+#include "cairo-image-info-private.h"
+
+#include <stdio.h>
+#include <ctype.h>
+#include <time.h>
+#include <zlib.h>
+#include <errno.h>
+
+#define DEBUG_PS 0
+
+#if DEBUG_PS
+#define DEBUG_FALLBACK(s) \
+    fprintf (stderr, "%s::%d -- %s\n", __FUNCTION__, __LINE__, (s))
+#else
+#define DEBUG_FALLBACK(s)
+#endif
+
+#ifndef HAVE_CTIME_R
+#define ctime_r(T, BUF) ctime (T)
+#endif
+
+/**
+ * SECTION:cairo-ps
+ * @Title: PostScript Surfaces
+ * @Short_Description: Rendering PostScript documents
+ * @See_Also: #cairo_surface_t
+ *
+ * The PostScript surface is used to render cairo graphics to Adobe
+ * PostScript files and is a multi-page vector surface backend.
+ */
+
+/**
+ * CAIRO_HAS_PS_SURFACE:
+ * 
+ * Defined if the PostScript surface backend is available.
+ * This macro can be used to conditionally compile backend-specific code.
+ */
+
+typedef enum {
+    CAIRO_PS_COMPRESS_NONE,
+    CAIRO_PS_COMPRESS_LZW,
+    CAIRO_PS_COMPRESS_DEFLATE
+ } cairo_ps_compress_t;
+
+static const cairo_surface_backend_t cairo_ps_surface_backend;
+static const cairo_paginated_surface_backend_t cairo_ps_surface_paginated_backend;
+
+static cairo_bool_t
+_cairo_ps_surface_get_extents (void                   *abstract_surface,
+                              cairo_rectangle_int_t   *rectangle);
+
+static void
+_cairo_ps_surface_release_surface (cairo_ps_surface_t      *surface,
+                                  cairo_surface_pattern_t *pattern);
+
+static const cairo_ps_level_t _cairo_ps_levels[] =
+{
+    CAIRO_PS_LEVEL_2,
+    CAIRO_PS_LEVEL_3
+};
+
+#define CAIRO_PS_LEVEL_LAST ARRAY_LENGTH (_cairo_ps_levels)
+
+static const char * _cairo_ps_level_strings[CAIRO_PS_LEVEL_LAST] =
+{
+    "PS Level 2",
+    "PS Level 3"
+};
+
+typedef struct _cairo_page_standard_media {
+    const char *name;
+    int width;
+    int height;
+} cairo_page_standard_media_t;
+
+static const cairo_page_standard_media_t _cairo_page_standard_media[] =
+{
+    { "A0",       2384, 3371 },
+    { "A1",       1685, 2384 },
+    { "A2",       1190, 1684 },
+    { "A3",        842, 1190 },
+    { "A4",        595,  842 },
+    { "A5",        420,  595 },
+    { "B4",        729, 1032 },
+    { "B5",        516,  729 },
+    { "Letter",    612,  792 },
+    { "Tabloid",   792, 1224 },
+    { "Ledger",   1224,  792 },
+    { "Legal",     612, 1008 },
+    { "Statement", 396,  612 },
+    { "Executive", 540,  720 },
+    { "Folio",     612,  936 },
+    { "Quarto",    610,  780 },
+    { "10x14",     720, 1008 },
+};
+
+typedef struct _cairo_page_media {
+    char *name;
+    int width;
+    int height;
+    cairo_list_t link;
+} cairo_page_media_t;
+
+static void
+_cairo_ps_surface_emit_header (cairo_ps_surface_t *surface)
+{
+    char ctime_buf[26];
+    time_t now;
+    char **comments;
+    int i, num_comments;
+    int level;
+    const char *eps_header = "";
+    cairo_bool_t has_bbox;
+
+    if (surface->has_creation_date)
+       now = surface->creation_date;
+    else
+       now = time (NULL);
+
+    if (surface->ps_level_used == CAIRO_PS_LEVEL_2)
+       level = 2;
+    else
+       level = 3;
+
+    if (surface->eps)
+       eps_header = " EPSF-3.0";
+
+    _cairo_output_stream_printf (surface->final_stream,
+                                "%%!PS-Adobe-3.0%s\n"
+                                "%%%%Creator: cairo %s (http://cairographics.org)\n"
+                                "%%%%CreationDate: %s"
+                                "%%%%Pages: %d\n",
+                                eps_header,
+                                cairo_version_string (),
+                                ctime_r (&now, ctime_buf),
+                                surface->num_pages);
+
+    _cairo_output_stream_printf (surface->final_stream,
+                                "%%%%DocumentData: Clean7Bit\n"
+                                "%%%%LanguageLevel: %d\n",
+                                level);
+
+    if (!cairo_list_is_empty (&surface->document_media)) {
+       cairo_page_media_t *page;
+       cairo_bool_t first = TRUE;
+
+       cairo_list_foreach_entry (page, cairo_page_media_t, &surface->document_media, link) {
+           if (first) {
+               _cairo_output_stream_printf (surface->final_stream,
+                                            "%%%%DocumentMedia: ");
+               first = FALSE;
+           } else {
+               _cairo_output_stream_printf (surface->final_stream,
+                                            "%%%%+ ");
+           }
+           _cairo_output_stream_printf (surface->final_stream,
+                                        "%s %d %d 0 () ()\n",
+                                        page->name,
+                                        page->width,
+                                        page->height);
+       }
+    }
+
+    has_bbox = FALSE;
+    num_comments = _cairo_array_num_elements (&surface->dsc_header_comments);
+    comments = _cairo_array_index (&surface->dsc_header_comments, 0);
+    for (i = 0; i < num_comments; i++) {
+       _cairo_output_stream_printf (surface->final_stream,
+                                    "%s\n", comments[i]);
+       if (strncmp (comments[i], "%%BoundingBox:", 14) == 0)
+           has_bbox = TRUE;
+
+       free (comments[i]);
+       comments[i] = NULL;
+    }
+
+    if (!has_bbox) {
+       _cairo_output_stream_printf (surface->final_stream,
+                                    "%%%%BoundingBox: %d %d %d %d\n",
+                                    surface->bbox_x1,
+                                    surface->bbox_y1,
+                                    surface->bbox_x2,
+                                    surface->bbox_y2);
+    }
+
+    _cairo_output_stream_printf (surface->final_stream,
+                                "%%%%EndComments\n");
+
+    _cairo_output_stream_printf (surface->final_stream,
+                                "%%%%BeginProlog\n");
+
+    if (surface->eps) {
+       _cairo_output_stream_printf (surface->final_stream,
+                                    "/cairo_eps_state save def\n"
+                                    "/dict_count countdictstack def\n"
+                                    "/op_count count 1 sub def\n"
+                                    "userdict begin\n");
+    } else {
+       _cairo_output_stream_printf (surface->final_stream,
+                                    "/languagelevel where\n"
+                                    "{ pop languagelevel } { 1 } ifelse\n"
+                                    "%d lt { /Helvetica findfont 12 scalefont setfont 50 500 moveto\n"
+                                    "  (This print job requires a PostScript Language Level %d printer.) show\n"
+                                    "  showpage quit } if\n",
+                                    level,
+                                    level);
+    }
+
+    _cairo_output_stream_printf (surface->final_stream,
+                                "/q { gsave } bind def\n"
+                                "/Q { grestore } bind def\n"
+                                "/cm { 6 array astore concat } bind def\n"
+                                "/w { setlinewidth } bind def\n"
+                                "/J { setlinecap } bind def\n"
+                                "/j { setlinejoin } bind def\n"
+                                "/M { setmiterlimit } bind def\n"
+                                "/d { setdash } bind def\n"
+                                "/m { moveto } bind def\n"
+                                "/l { lineto } bind def\n"
+                                "/c { curveto } bind def\n"
+                                "/h { closepath } bind def\n"
+                                "/re { exch dup neg 3 1 roll 5 3 roll moveto 0 rlineto\n"
+                                "      0 exch rlineto 0 rlineto closepath } bind def\n"
+                                "/S { stroke } bind def\n"
+                                "/f { fill } bind def\n"
+                                "/f* { eofill } bind def\n"
+                                "/n { newpath } bind def\n"
+                                "/W { clip } bind def\n"
+                                "/W* { eoclip } bind def\n"
+                                "/BT { } bind def\n"
+                                "/ET { } bind def\n"
+                                "/pdfmark where { pop globaldict /?pdfmark /exec load put }\n"
+                                "    { globaldict begin /?pdfmark /pop load def /pdfmark\n"
+                                "    /cleartomark load def end } ifelse\n"
+                                "/BDC { mark 3 1 roll /BDC pdfmark } bind def\n"
+                                "/EMC { mark /EMC pdfmark } bind def\n"
+                                "/cairo_store_point { /cairo_point_y exch def /cairo_point_x exch def } def\n"
+                                "/Tj { show currentpoint cairo_store_point } bind def\n"
+                                "/TJ {\n"
+                                "  {\n"
+                                "    dup\n"
+                                "    type /stringtype eq\n"
+                                "    { show } { -0.001 mul 0 cairo_font_matrix dtransform rmoveto } ifelse\n"
+                                "  } forall\n"
+                                "  currentpoint cairo_store_point\n"
+                                "} bind def\n"
+                                "/cairo_selectfont { cairo_font_matrix aload pop pop pop 0 0 6 array astore\n"
+                                "    cairo_font exch selectfont cairo_point_x cairo_point_y moveto } bind def\n"
+                                "/Tf { pop /cairo_font exch def /cairo_font_matrix where\n"
+                                "      { pop cairo_selectfont } if } bind def\n"
+                                "/Td { matrix translate cairo_font_matrix matrix concatmatrix dup\n"
+                                "      /cairo_font_matrix exch def dup 4 get exch 5 get cairo_store_point\n"
+                                "      /cairo_font where { pop cairo_selectfont } if } bind def\n"
+                                "/Tm { 2 copy 8 2 roll 6 array astore /cairo_font_matrix exch def\n"
+                                "      cairo_store_point /cairo_font where { pop cairo_selectfont } if } bind def\n"
+                                "/g { setgray } bind def\n"
+                                "/rg { setrgbcolor } bind def\n"
+                                "/d1 { setcachedevice } bind def\n");
+
+    _cairo_output_stream_printf (surface->final_stream,
+                                "%%%%EndProlog\n");
+
+    num_comments = _cairo_array_num_elements (&surface->dsc_setup_comments);
+    if (num_comments) {
+       _cairo_output_stream_printf (surface->final_stream,
+                                    "%%%%BeginSetup\n");
+
+       comments = _cairo_array_index (&surface->dsc_setup_comments, 0);
+       for (i = 0; i < num_comments; i++) {
+           _cairo_output_stream_printf (surface->final_stream,
+                                        "%s\n", comments[i]);
+           free (comments[i]);
+           comments[i] = NULL;
+       }
+
+       _cairo_output_stream_printf (surface->final_stream,
+                                    "%%%%EndSetup\n");
+    }
+}
+
+static cairo_status_t
+_cairo_ps_surface_emit_type1_font_subset (cairo_ps_surface_t           *surface,
+                                         cairo_scaled_font_subset_t    *font_subset)
+
+
+{
+    cairo_type1_subset_t subset;
+    cairo_status_t status;
+    int length;
+    char name[64];
+
+    snprintf (name, sizeof name, "f-%d-%d",
+             font_subset->font_id, font_subset->subset_id);
+    status = _cairo_type1_subset_init (&subset, name, font_subset, TRUE);
+    if (unlikely (status))
+       return status;
+
+    /* FIXME: Figure out document structure convention for fonts */
+
+#if DEBUG_PS
+    _cairo_output_stream_printf (surface->final_stream,
+                                "%% _cairo_ps_surface_emit_type1_font_subset\n");
+#endif
+
+    length = subset.header_length + subset.data_length + subset.trailer_length;
+    _cairo_output_stream_write (surface->final_stream, subset.data, length);
+
+    _cairo_type1_subset_fini (&subset);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+
+static cairo_status_t
+_cairo_ps_surface_emit_type1_font_fallback (cairo_ps_surface_t         *surface,
+                                            cairo_scaled_font_subset_t *font_subset)
+{
+    cairo_type1_subset_t subset;
+    cairo_status_t status;
+    int length;
+    char name[64];
+
+    snprintf (name, sizeof name, "f-%d-%d",
+             font_subset->font_id, font_subset->subset_id);
+    status = _cairo_type1_fallback_init_hex (&subset, name, font_subset);
+    if (unlikely (status))
+       return status;
+
+    /* FIXME: Figure out document structure convention for fonts */
+
+#if DEBUG_PS
+    _cairo_output_stream_printf (surface->final_stream,
+                                "%% _cairo_ps_surface_emit_type1_font_fallback\n");
+#endif
+
+    length = subset.header_length + subset.data_length + subset.trailer_length;
+    _cairo_output_stream_write (surface->final_stream, subset.data, length);
+
+    _cairo_type1_fallback_fini (&subset);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_ps_surface_emit_truetype_font_subset (cairo_ps_surface_t                *surface,
+                                            cairo_scaled_font_subset_t *font_subset)
+
+
+{
+    cairo_truetype_subset_t subset;
+    cairo_status_t status;
+    unsigned int i, begin, end;
+
+    status = _cairo_truetype_subset_init_ps (&subset, font_subset);
+    if (unlikely (status))
+       return status;
+
+    /* FIXME: Figure out document structure convention for fonts */
+
+#if DEBUG_PS
+    _cairo_output_stream_printf (surface->final_stream,
+                                "%% _cairo_ps_surface_emit_truetype_font_subset\n");
+#endif
+
+    _cairo_output_stream_printf (surface->final_stream,
+                                "11 dict begin\n"
+                                "/FontType 42 def\n"
+                                "/FontName /%s def\n"
+                                "/PaintType 0 def\n"
+                                "/FontMatrix [ 1 0 0 1 0 0 ] def\n"
+                                "/FontBBox [ 0 0 0 0 ] def\n"
+                                "/Encoding 256 array def\n"
+                                "0 1 255 { Encoding exch /.notdef put } for\n",
+                                subset.ps_name);
+
+    /* FIXME: Figure out how subset->x_max etc maps to the /FontBBox */
+
+    if (font_subset->is_latin) {
+       for (i = 1; i < 256; i++) {
+           if (font_subset->latin_to_subset_glyph_index[i] > 0) {
+               if (font_subset->glyph_names != NULL) {
+                   _cairo_output_stream_printf (surface->final_stream,
+                                                "Encoding %d /%s put\n",
+                                                i, font_subset->glyph_names[font_subset->latin_to_subset_glyph_index[i]]);
+               } else {
+                   _cairo_output_stream_printf (surface->final_stream,
+                                                "Encoding %d /g%ld put\n", i, font_subset->latin_to_subset_glyph_index[i]);
+               }
+           }
+       }
+    } else {
+       for (i = 1; i < font_subset->num_glyphs; i++) {
+           if (font_subset->glyph_names != NULL) {
+               _cairo_output_stream_printf (surface->final_stream,
+                                            "Encoding %d /%s put\n",
+                                            i, font_subset->glyph_names[i]);
+           } else {
+               _cairo_output_stream_printf (surface->final_stream,
+                                            "Encoding %d /g%d put\n", i, i);
+           }
+       }
+    }
+
+    _cairo_output_stream_printf (surface->final_stream,
+                                "/CharStrings %d dict dup begin\n"
+                                "/.notdef 0 def\n",
+                                font_subset->num_glyphs);
+
+    for (i = 1; i < font_subset->num_glyphs; i++) {
+       if (font_subset->glyph_names != NULL) {
+           _cairo_output_stream_printf (surface->final_stream,
+                                        "/%s %d def\n",
+                                        font_subset->glyph_names[i], i);
+       } else {
+           _cairo_output_stream_printf (surface->final_stream,
+                                        "/g%d %d def\n", i, i);
+       }
+    }
+
+    _cairo_output_stream_printf (surface->final_stream,
+                                "end readonly def\n");
+
+    _cairo_output_stream_printf (surface->final_stream,
+                                "/sfnts [\n");
+    begin = 0;
+    end = 0;
+    for (i = 0; i < subset.num_string_offsets; i++) {
+        end = subset.string_offsets[i];
+        _cairo_output_stream_printf (surface->final_stream,"<");
+        _cairo_output_stream_write_hex_string (surface->final_stream,
+                                               subset.data + begin, end - begin);
+        _cairo_output_stream_printf (surface->final_stream,"00>\n");
+        begin = end;
+    }
+    if (subset.data_length > end) {
+        _cairo_output_stream_printf (surface->final_stream,"<");
+        _cairo_output_stream_write_hex_string (surface->final_stream,
+                                               subset.data + end, subset.data_length - end);
+        _cairo_output_stream_printf (surface->final_stream,"00>\n");
+    }
+
+    _cairo_output_stream_printf (surface->final_stream,
+                                "] def\n"
+                                "/f-%d-%d currentdict end definefont pop\n",
+                                font_subset->font_id,
+                                font_subset->subset_id);
+
+    _cairo_truetype_subset_fini (&subset);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_ps_emit_imagemask (cairo_image_surface_t *image,
+                         cairo_output_stream_t *stream)
+{
+    uint8_t *row, *byte;
+    int rows, cols;
+
+    /* The only image type supported by Type 3 fonts are 1-bit image
+     * masks */
+    assert (image->format == CAIRO_FORMAT_A1);
+
+    _cairo_output_stream_printf (stream,
+                                "<<\n"
+                                "   /ImageType 1\n"
+                                "   /Width %d\n"
+                                "   /Height %d\n"
+                                "   /ImageMatrix [%d 0 0 %d 0 %d]\n"
+                                "   /Decode [1 0]\n"
+                                "   /BitsPerComponent 1\n",
+                                image->width,
+                                image->height,
+                                image->width,
+                                -image->height,
+                                image->height);
+
+    _cairo_output_stream_printf (stream,
+                                "   /DataSource {<\n   ");
+    for (row = image->data, rows = image->height; rows; row += image->stride, rows--) {
+       for (byte = row, cols = (image->width + 7) / 8; cols; byte++, cols--) {
+           uint8_t output_byte = CAIRO_BITSWAP8_IF_LITTLE_ENDIAN (*byte);
+           _cairo_output_stream_printf (stream, "%02x ", output_byte);
+       }
+       _cairo_output_stream_printf (stream, "\n   ");
+    }
+    _cairo_output_stream_printf (stream, ">}\n>>\n");
+
+    _cairo_output_stream_printf (stream,
+                                "imagemask\n");
+
+    return _cairo_output_stream_get_status (stream);
+}
+
+static cairo_int_status_t
+_cairo_ps_surface_analyze_user_font_subset (cairo_scaled_font_subset_t *font_subset,
+                                           void                       *closure)
+{
+    cairo_ps_surface_t *surface = closure;
+    cairo_status_t status = CAIRO_STATUS_SUCCESS;
+    unsigned int i;
+    cairo_surface_t *type3_surface;
+
+    type3_surface = _cairo_type3_glyph_surface_create (font_subset->scaled_font,
+                                                      NULL,
+                                                      _cairo_ps_emit_imagemask,
+                                                      surface->font_subsets);
+
+    for (i = 0; i < font_subset->num_glyphs; i++) {
+       status = _cairo_type3_glyph_surface_analyze_glyph (type3_surface,
+                                                          font_subset->glyphs[i]);
+       if (unlikely (status))
+           break;
+
+    }
+    cairo_surface_finish (type3_surface);
+    cairo_surface_destroy (type3_surface);
+
+    return status;
+}
+
+static cairo_status_t
+_cairo_ps_surface_emit_type3_font_subset (cairo_ps_surface_t           *surface,
+                                         cairo_scaled_font_subset_t    *font_subset)
+
+
+{
+    cairo_status_t status;
+    unsigned int i;
+    cairo_box_t font_bbox = {{0,0},{0,0}};
+    cairo_box_t bbox = {{0,0},{0,0}};
+    cairo_surface_t *type3_surface;
+    double width;
+
+    if (font_subset->num_glyphs == 0)
+       return CAIRO_STATUS_SUCCESS;
+
+#if DEBUG_PS
+    _cairo_output_stream_printf (surface->final_stream,
+                                "%% _cairo_ps_surface_emit_type3_font_subset\n");
+#endif
+
+    _cairo_output_stream_printf (surface->final_stream,
+                                "8 dict begin\n"
+                                "/FontType 3 def\n"
+                                "/FontMatrix [1 0 0 1 0 0] def\n"
+                                "/Encoding 256 array def\n"
+                                "0 1 255 { Encoding exch /.notdef put } for\n");
+
+    type3_surface = _cairo_type3_glyph_surface_create (font_subset->scaled_font,
+                                                      NULL,
+                                                      _cairo_ps_emit_imagemask,
+                                                      surface->font_subsets);
+    status = type3_surface->status;
+    if (unlikely (status))
+       return status;
+
+    for (i = 0; i < font_subset->num_glyphs; i++) {
+       if (font_subset->glyph_names != NULL) {
+           _cairo_output_stream_printf (surface->final_stream,
+                                        "Encoding %d /%s put\n",
+                                        i, font_subset->glyph_names[i]);
+       } else {
+           _cairo_output_stream_printf (surface->final_stream,
+                                        "Encoding %d /g%d put\n", i, i);
+       }
+    }
+
+    _cairo_output_stream_printf (surface->final_stream,
+                                "/Glyphs [\n");
+
+    for (i = 0; i < font_subset->num_glyphs; i++) {
+       _cairo_output_stream_printf (surface->final_stream,
+                                    "    { %% %d\n", i);
+       status = _cairo_type3_glyph_surface_emit_glyph (type3_surface,
+                                                       surface->final_stream,
+                                                       font_subset->glyphs[i],
+                                                       &bbox,
+                                                       &width);
+       if (unlikely (status))
+           break;
+
+       _cairo_output_stream_printf (surface->final_stream,
+                                    "    }\n");
+        if (i == 0) {
+            font_bbox.p1.x = bbox.p1.x;
+            font_bbox.p1.y = bbox.p1.y;
+            font_bbox.p2.x = bbox.p2.x;
+            font_bbox.p2.y = bbox.p2.y;
+        } else {
+            if (bbox.p1.x < font_bbox.p1.x)
+                font_bbox.p1.x = bbox.p1.x;
+            if (bbox.p1.y < font_bbox.p1.y)
+                font_bbox.p1.y = bbox.p1.y;
+            if (bbox.p2.x > font_bbox.p2.x)
+                font_bbox.p2.x = bbox.p2.x;
+            if (bbox.p2.y > font_bbox.p2.y)
+                font_bbox.p2.y = bbox.p2.y;
+        }
+    }
+    cairo_surface_finish (type3_surface);
+    cairo_surface_destroy (type3_surface);
+    if (unlikely (status))
+       return status;
+
+    _cairo_output_stream_printf (surface->final_stream,
+                                "] def\n"
+                                "/FontBBox [%f %f %f %f] def\n"
+                                "/BuildChar {\n"
+                                "  exch /Glyphs get\n"
+                                "  exch get\n"
+                                "  10 dict begin exec end\n"
+                                "} bind def\n"
+                                "currentdict\n"
+                                "end\n"
+                                "/f-%d-%d exch definefont pop\n",
+                                _cairo_fixed_to_double (font_bbox.p1.x),
+                                - _cairo_fixed_to_double (font_bbox.p2.y),
+                                _cairo_fixed_to_double (font_bbox.p2.x),
+                                - _cairo_fixed_to_double (font_bbox.p1.y),
+                                font_subset->font_id,
+                                font_subset->subset_id);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_ps_surface_emit_unscaled_font_subset (cairo_scaled_font_subset_t        *font_subset,
+                                           void                        *closure)
+{
+    cairo_ps_surface_t *surface = closure;
+    cairo_int_status_t status;
+
+    status = _cairo_scaled_font_subset_create_glyph_names (font_subset);
+    if (_cairo_int_status_is_error (status))
+       return status;
+
+    status = _cairo_ps_surface_emit_type1_font_subset (surface, font_subset);
+    if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+       return status;
+
+    status = _cairo_ps_surface_emit_truetype_font_subset (surface, font_subset);
+    if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+       return status;
+
+    status = _cairo_ps_surface_emit_type1_font_fallback (surface, font_subset);
+    if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+       return status;
+
+    ASSERT_NOT_REACHED;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_ps_surface_emit_scaled_font_subset (cairo_scaled_font_subset_t *font_subset,
+                                           void                              *closure)
+{
+    cairo_ps_surface_t *surface = closure;
+    cairo_int_status_t status;
+
+    status = _cairo_scaled_font_subset_create_glyph_names (font_subset);
+    if (_cairo_int_status_is_error (status))
+       return status;
+
+    status = _cairo_ps_surface_emit_type3_font_subset (surface, font_subset);
+    if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+       return status;
+
+    ASSERT_NOT_REACHED;
+    return CAIRO_INT_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_ps_surface_emit_font_subsets (cairo_ps_surface_t *surface)
+{
+    cairo_status_t status;
+
+#if DEBUG_PS
+    _cairo_output_stream_printf (surface->final_stream,
+                                "%% _cairo_ps_surface_emit_font_subsets\n");
+#endif
+
+    status = _cairo_scaled_font_subsets_foreach_user (surface->font_subsets,
+                                                     _cairo_ps_surface_analyze_user_font_subset,
+                                                     surface);
+    if (unlikely (status))
+       return status;
+
+    status = _cairo_scaled_font_subsets_foreach_unscaled (surface->font_subsets,
+                                                          _cairo_ps_surface_emit_unscaled_font_subset,
+                                                          surface);
+    if (unlikely (status))
+       return status;
+
+    status = _cairo_scaled_font_subsets_foreach_scaled (surface->font_subsets,
+                                                        _cairo_ps_surface_emit_scaled_font_subset,
+                                                        surface);
+    if (unlikely (status))
+       return status;
+
+    return _cairo_scaled_font_subsets_foreach_user (surface->font_subsets,
+                                                   _cairo_ps_surface_emit_scaled_font_subset,
+                                                   surface);
+}
+
+static cairo_status_t
+_cairo_ps_surface_emit_body (cairo_ps_surface_t *surface)
+{
+    char    buf[4096];
+    int            n;
+
+    if (ferror (surface->tmpfile) != 0)
+       return _cairo_error (CAIRO_STATUS_TEMP_FILE_ERROR);
+
+    rewind (surface->tmpfile);
+    while ((n = fread (buf, 1, sizeof (buf), surface->tmpfile)) > 0)
+       _cairo_output_stream_write (surface->final_stream, buf, n);
+
+    if (ferror (surface->tmpfile) != 0)
+       return _cairo_error (CAIRO_STATUS_TEMP_FILE_ERROR);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_ps_surface_emit_footer (cairo_ps_surface_t *surface)
+{
+    _cairo_output_stream_printf (surface->final_stream,
+                                "%%%%Trailer\n");
+
+    if (surface->eps) {
+       _cairo_output_stream_printf (surface->final_stream,
+                                    "count op_count sub {pop} repeat\n"
+                                    "countdictstack dict_count sub {end} repeat\n"
+                                    "cairo_eps_state restore\n");
+    }
+
+    _cairo_output_stream_printf (surface->final_stream,
+                                "%%%%EOF\n");
+}
+
+static cairo_bool_t
+_path_covers_bbox (cairo_ps_surface_t *surface,
+                  cairo_path_fixed_t *path)
+{
+    cairo_box_t box;
+
+    if (_cairo_path_fixed_is_box (path, &box)) {
+       cairo_rectangle_int_t rect;
+
+       _cairo_box_round_to_rectangle (&box, &rect);
+
+       /* skip trivial whole-page clips */
+       if (_cairo_rectangle_intersect (&rect, &surface->page_bbox)) {
+           if (rect.x == surface->page_bbox.x &&
+               rect.width == surface->page_bbox.width &&
+               rect.y == surface->page_bbox.y &&
+               rect.height == surface->page_bbox.height)
+           {
+               return TRUE;
+           }
+       }
+    }
+
+    return FALSE;
+}
+
+static cairo_status_t
+_cairo_ps_surface_clipper_intersect_clip_path (cairo_surface_clipper_t *clipper,
+                                              cairo_path_fixed_t *path,
+                                              cairo_fill_rule_t   fill_rule,
+                                              double               tolerance,
+                                              cairo_antialias_t   antialias)
+{
+    cairo_ps_surface_t *surface = cairo_container_of (clipper,
+                                                     cairo_ps_surface_t,
+                                                     clipper);
+    cairo_output_stream_t *stream = surface->stream;
+    cairo_status_t status;
+
+    assert (surface->paginated_mode != CAIRO_PAGINATED_MODE_ANALYZE);
+
+#if DEBUG_PS
+    _cairo_output_stream_printf (stream,
+                                "%% _cairo_ps_surface_intersect_clip_path\n");
+#endif
+
+    if (path == NULL) {
+       status = _cairo_pdf_operators_flush (&surface->pdf_operators);
+       if (unlikely (status))
+           return status;
+
+       _cairo_output_stream_printf (stream, "Q q\n");
+
+       surface->current_pattern_is_solid_color = FALSE;
+       _cairo_pdf_operators_reset (&surface->pdf_operators);
+
+       return CAIRO_STATUS_SUCCESS;
+    }
+
+    if (_path_covers_bbox (surface, path))
+       return CAIRO_STATUS_SUCCESS;
+
+    return _cairo_pdf_operators_clip (&surface->pdf_operators,
+                                     path,
+                                     fill_rule);
+}
+
+/* PLRM specifies a tolerance of 5 points when matching page sizes */
+static cairo_bool_t
+_ps_page_dimension_equal (int a, int b)
+{
+    return (abs (a - b) < 5);
+}
+
+static const char *
+_cairo_ps_surface_get_page_media (cairo_ps_surface_t     *surface)
+{
+    int width, height, i;
+    char buf[50];
+    cairo_page_media_t *page;
+    const char *page_name;
+
+    width = _cairo_lround (surface->width);
+    height = _cairo_lround (surface->height);
+
+    /* search previously used page sizes */
+    cairo_list_foreach_entry (page, cairo_page_media_t, &surface->document_media, link) {
+       if (_ps_page_dimension_equal (width, page->width) &&
+           _ps_page_dimension_equal (height, page->height))
+           return page->name;
+    }
+
+    /* search list of standard page sizes */
+    page_name = NULL;
+    for (i = 0; i < ARRAY_LENGTH (_cairo_page_standard_media); i++) {
+       if (_ps_page_dimension_equal (width, _cairo_page_standard_media[i].width) &&
+           _ps_page_dimension_equal (height, _cairo_page_standard_media[i].height))
+       {
+           page_name = _cairo_page_standard_media[i].name;
+           width = _cairo_page_standard_media[i].width;
+           height = _cairo_page_standard_media[i].height;
+           break;
+       }
+    }
+
+    page = malloc (sizeof (cairo_page_media_t));
+    if (unlikely (page == NULL)) {
+       _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+       return NULL;
+    }
+
+    if (page_name) {
+       page->name = strdup (page_name);
+    } else {
+       snprintf (buf, sizeof (buf), "%dx%dmm",
+                 (int) _cairo_lround (surface->width * 25.4/72),
+                 (int) _cairo_lround (surface->height * 25.4/72));
+       page->name = strdup (buf);
+    }
+
+    if (unlikely (page->name == NULL)) {
+       free (page);
+       _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+       return NULL;
+    }
+
+    page->width = width;
+    page->height = height;
+    cairo_list_add_tail (&page->link, &surface->document_media);
+
+    return page->name;
+}
+
+static cairo_surface_t *
+_cairo_ps_surface_create_for_stream_internal (cairo_output_stream_t *stream,
+                                             double                 width,
+                                             double                 height)
+{
+    cairo_status_t status, status_ignored;
+    cairo_ps_surface_t *surface;
+
+    surface = malloc (sizeof (cairo_ps_surface_t));
+    if (unlikely (surface == NULL)) {
+       status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+       goto CLEANUP;
+    }
+
+    _cairo_surface_init (&surface->base,
+                        &cairo_ps_surface_backend,
+                        NULL, /* device */
+                        CAIRO_CONTENT_COLOR_ALPHA);
+
+    surface->final_stream = stream;
+
+    surface->tmpfile = tmpfile ();
+    if (surface->tmpfile == NULL) {
+       switch (errno) {
+       case ENOMEM:
+           status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+           break;
+       default:
+           status = _cairo_error (CAIRO_STATUS_TEMP_FILE_ERROR);
+           break;
+       }
+       goto CLEANUP_SURFACE;
+    }
+
+    surface->stream = _cairo_output_stream_create_for_file (surface->tmpfile);
+    status = _cairo_output_stream_get_status (surface->stream);
+    if (unlikely (status))
+       goto CLEANUP_OUTPUT_STREAM;
+
+    surface->font_subsets = _cairo_scaled_font_subsets_create_simple ();
+    if (unlikely (surface->font_subsets == NULL)) {
+       status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+       goto CLEANUP_OUTPUT_STREAM;
+    }
+
+    _cairo_scaled_font_subsets_enable_latin_subset (surface->font_subsets, TRUE);
+    surface->has_creation_date = FALSE;
+    surface->eps = FALSE;
+    surface->ps_level = CAIRO_PS_LEVEL_3;
+    surface->ps_level_used = CAIRO_PS_LEVEL_2;
+    surface->width  = width;
+    surface->height = height;
+    cairo_matrix_init (&surface->cairo_to_ps, 1, 0, 0, -1, 0, height);
+    surface->paginated_mode = CAIRO_PAGINATED_MODE_ANALYZE;
+    surface->force_fallbacks = FALSE;
+    surface->content = CAIRO_CONTENT_COLOR_ALPHA;
+    surface->use_string_datasource = FALSE;
+    surface->current_pattern_is_solid_color = FALSE;
+
+    surface->page_bbox.x = 0;
+    surface->page_bbox.y = 0;
+    surface->page_bbox.width  = width;
+    surface->page_bbox.height = height;
+
+    _cairo_surface_clipper_init (&surface->clipper,
+                                _cairo_ps_surface_clipper_intersect_clip_path);
+
+    _cairo_pdf_operators_init (&surface->pdf_operators,
+                              surface->stream,
+                              &surface->cairo_to_ps,
+                              surface->font_subsets);
+    surface->num_pages = 0;
+
+    cairo_list_init (&surface->document_media);
+    _cairo_array_init (&surface->dsc_header_comments, sizeof (char *));
+    _cairo_array_init (&surface->dsc_setup_comments, sizeof (char *));
+    _cairo_array_init (&surface->dsc_page_setup_comments, sizeof (char *));
+
+    surface->dsc_comment_target = &surface->dsc_header_comments;
+
+    surface->paginated_surface = _cairo_paginated_surface_create (
+                                          &surface->base,
+                                          CAIRO_CONTENT_COLOR_ALPHA,
+                                          &cairo_ps_surface_paginated_backend);
+    status = surface->paginated_surface->status;
+    if (status == CAIRO_STATUS_SUCCESS) {
+       /* paginated keeps the only reference to surface now, drop ours */
+       cairo_surface_destroy (&surface->base);
+       return surface->paginated_surface;
+    }
+
+    _cairo_scaled_font_subsets_destroy (surface->font_subsets);
+ CLEANUP_OUTPUT_STREAM:
+    status_ignored = _cairo_output_stream_destroy (surface->stream);
+    fclose (surface->tmpfile);
+ CLEANUP_SURFACE:
+    free (surface);
+ CLEANUP:
+    /* destroy stream on behalf of caller */
+    status_ignored = _cairo_output_stream_destroy (stream);
+
+    return _cairo_surface_create_in_error (status);
+}
+
+/**
+ * cairo_ps_surface_create:
+ * @filename: a filename for the PS output (must be writable), %NULL may be
+ *            used to specify no output. This will generate a PS surface that
+ *            may be queried and used as a source, without generating a
+ *            temporary file.
+ * @width_in_points: width of the surface, in points (1 point == 1/72.0 inch)
+ * @height_in_points: height of the surface, in points (1 point == 1/72.0 inch)
+ *
+ * Creates a PostScript surface of the specified size in points to be
+ * written to @filename. See cairo_ps_surface_create_for_stream() for
+ * a more flexible mechanism for handling the PostScript output than
+ * simply writing it to a named file.
+ *
+ * Note that the size of individual pages of the PostScript output can
+ * vary. See cairo_ps_surface_set_size().
+ *
+ * Return value: a pointer to the newly created surface. The caller
+ * owns the surface and should call cairo_surface_destroy() when done
+ * with it.
+ *
+ * This function always returns a valid pointer, but it will return a
+ * pointer to a "nil" surface if an error such as out of memory
+ * occurs. You can use cairo_surface_status() to check for this.
+ *
+ * Since: 1.2
+ **/
+cairo_surface_t *
+cairo_ps_surface_create (const char            *filename,
+                        double                  width_in_points,
+                        double                  height_in_points)
+{
+    cairo_output_stream_t *stream;
+
+    stream = _cairo_output_stream_create_for_filename (filename);
+    if (_cairo_output_stream_get_status (stream))
+       return _cairo_surface_create_in_error (_cairo_output_stream_destroy (stream));
+
+    return _cairo_ps_surface_create_for_stream_internal (stream,
+                                                        width_in_points,
+                                                        height_in_points);
+}
+
+/**
+ * cairo_ps_surface_create_for_stream:
+ * @write_func: a #cairo_write_func_t to accept the output data, may be %NULL
+ *              to indicate a no-op @write_func. With a no-op @write_func,
+ *              the surface may be queried or used as a source without
+ *              generating any temporary files.
+ * @closure: the closure argument for @write_func
+ * @width_in_points: width of the surface, in points (1 point == 1/72.0 inch)
+ * @height_in_points: height of the surface, in points (1 point == 1/72.0 inch)
+ *
+ * Creates a PostScript surface of the specified size in points to be
+ * written incrementally to the stream represented by @write_func and
+ * @closure. See cairo_ps_surface_create() for a more convenient way
+ * to simply direct the PostScript output to a named file.
+ *
+ * Note that the size of individual pages of the PostScript
+ * output can vary. See cairo_ps_surface_set_size().
+ *
+ * Return value: a pointer to the newly created surface. The caller
+ * owns the surface and should call cairo_surface_destroy() when done
+ * with it.
+ *
+ * This function always returns a valid pointer, but it will return a
+ * pointer to a "nil" surface if an error such as out of memory
+ * occurs. You can use cairo_surface_status() to check for this.
+ *
+ * Since: 1.2
+ */
+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_output_stream_t *stream;
+
+    stream = _cairo_output_stream_create (write_func, NULL, closure);
+    if (_cairo_output_stream_get_status (stream))
+       return _cairo_surface_create_in_error (_cairo_output_stream_destroy (stream));
+
+    return _cairo_ps_surface_create_for_stream_internal (stream,
+                                                        width_in_points,
+                                                        height_in_points);
+}
+
+static cairo_bool_t
+_cairo_surface_is_ps (cairo_surface_t *surface)
+{
+    return surface->backend == &cairo_ps_surface_backend;
+}
+
+/* If the abstract_surface is a paginated surface, and that paginated
+ * surface's target is a ps_surface, then set ps_surface to that
+ * target. Otherwise return FALSE.
+ */
+static cairo_bool_t
+_extract_ps_surface (cairo_surface_t    *surface,
+                     cairo_bool_t         set_error_on_failure,
+                    cairo_ps_surface_t **ps_surface)
+{
+    cairo_surface_t *target;
+
+    if (surface->status)
+       return FALSE;
+    if (surface->finished) {
+        if (set_error_on_failure)
+           _cairo_surface_set_error (surface,
+                                     _cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
+       return FALSE;
+    }
+
+    if (! _cairo_surface_is_paginated (surface)) {
+        if (set_error_on_failure)
+           _cairo_surface_set_error (surface,
+                                     _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH));
+       return FALSE;
+    }
+
+    target = _cairo_paginated_surface_get_target (surface);
+    if (target->status) {
+        if (set_error_on_failure)
+           _cairo_surface_set_error (surface, target->status);
+       return FALSE;
+    }
+    if (target->finished) {
+        if (set_error_on_failure)
+           _cairo_surface_set_error (surface,
+                                     _cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
+       return FALSE;
+    }
+
+    if (! _cairo_surface_is_ps (target)) {
+        if (set_error_on_failure)
+           _cairo_surface_set_error (surface,
+                                     _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH));
+       return FALSE;
+    }
+
+    *ps_surface = (cairo_ps_surface_t *) target;
+    return TRUE;
+}
+
+/**
+ * cairo_ps_surface_restrict_to_level:
+ * @surface: a PostScript #cairo_surface_t
+ * @level: PostScript level
+ *
+ * Restricts the generated PostSript file to @level. See
+ * cairo_ps_get_levels() for a list of available level values that
+ * can be used here.
+ *
+ * This function should only be called before any drawing operations
+ * have been performed on the given surface. The simplest way to do
+ * this is to call this function immediately after creating the
+ * surface.
+ *
+ * Since: 1.6
+ **/
+void
+cairo_ps_surface_restrict_to_level (cairo_surface_t  *surface,
+                                    cairo_ps_level_t  level)
+{
+    cairo_ps_surface_t *ps_surface = NULL;
+
+    if (! _extract_ps_surface (surface, TRUE, &ps_surface))
+       return;
+
+    if (level < CAIRO_PS_LEVEL_LAST)
+       ps_surface->ps_level = level;
+}
+
+/**
+ * cairo_ps_get_levels:
+ * @levels: supported level list
+ * @num_levels: list length
+ *
+ * Used to retrieve the list of supported levels. See
+ * cairo_ps_surface_restrict_to_level().
+ *
+ * Since: 1.6
+ **/
+void
+cairo_ps_get_levels (cairo_ps_level_t const    **levels,
+                     int                        *num_levels)
+{
+    if (levels != NULL)
+       *levels = _cairo_ps_levels;
+
+    if (num_levels != NULL)
+       *num_levels = CAIRO_PS_LEVEL_LAST;
+}
+
+/**
+ * cairo_ps_level_to_string:
+ * @level: a level id
+ *
+ * Get the string representation of the given @level id. This function
+ * will return %NULL if @level id isn't valid. See cairo_ps_get_levels()
+ * for a way to get the list of valid level ids.
+ *
+ * Return value: the string associated to given level.
+ *
+ * Since: 1.6
+ **/
+const char *
+cairo_ps_level_to_string (cairo_ps_level_t level)
+{
+    if (level >= CAIRO_PS_LEVEL_LAST)
+       return NULL;
+
+    return _cairo_ps_level_strings[level];
+}
+
+/**
+ * cairo_ps_surface_set_eps:
+ * @surface: a PostScript #cairo_surface_t
+ * @eps: %TRUE to output EPS format PostScript
+ *
+ * If @eps is %TRUE, the PostScript surface will output Encapsulated
+ * PostScript.
+ *
+ * This function should only be called before any drawing operations
+ * have been performed on the current page. The simplest way to do
+ * this is to call this function immediately after creating the
+ * surface. An Encapsulated PostScript file should never contain more
+ * than one page.
+ *
+ * Since: 1.6
+ **/
+void
+cairo_ps_surface_set_eps (cairo_surface_t      *surface,
+                         cairo_bool_t           eps)
+{
+    cairo_ps_surface_t *ps_surface = NULL;
+
+    if (! _extract_ps_surface (surface, TRUE, &ps_surface))
+       return;
+
+    ps_surface->eps = eps;
+}
+
+/**
+ * cairo_ps_surface_get_eps:
+ * @surface: a PostScript #cairo_surface_t
+ *
+ * Check whether the PostScript surface will output Encapsulated PostScript.
+ *
+ * Return value: %TRUE if the surface will output Encapsulated PostScript.
+ *
+ * Since: 1.6
+ **/
+cairo_public cairo_bool_t
+cairo_ps_surface_get_eps (cairo_surface_t      *surface)
+{
+    cairo_ps_surface_t *ps_surface = NULL;
+
+    if (! _extract_ps_surface (surface, FALSE, &ps_surface))
+       return FALSE;
+
+    return ps_surface->eps;
+}
+
+/**
+ * cairo_ps_surface_set_size:
+ * @surface: a PostScript #cairo_surface_t
+ * @width_in_points: new surface width, in points (1 point == 1/72.0 inch)
+ * @height_in_points: new surface height, in points (1 point == 1/72.0 inch)
+ *
+ * Changes the size of a PostScript surface for the current (and
+ * subsequent) pages.
+ *
+ * This function should only be called before any drawing operations
+ * have been performed on the current page. The simplest way to do
+ * this is to call this function immediately after creating the
+ * surface or immediately after completing a page with either
+ * cairo_show_page() or cairo_copy_page().
+ *
+ * Since: 1.2
+ **/
+void
+cairo_ps_surface_set_size (cairo_surface_t     *surface,
+                          double                width_in_points,
+                          double                height_in_points)
+{
+    cairo_ps_surface_t *ps_surface = NULL;
+    cairo_status_t status;
+
+    if (! _extract_ps_surface (surface, TRUE, &ps_surface))
+       return;
+
+    ps_surface->width = width_in_points;
+    ps_surface->height = height_in_points;
+    cairo_matrix_init (&ps_surface->cairo_to_ps, 1, 0, 0, -1, 0, height_in_points);
+    _cairo_pdf_operators_set_cairo_to_pdf_matrix (&ps_surface->pdf_operators,
+                                                 &ps_surface->cairo_to_ps);
+    status = _cairo_paginated_surface_set_size (ps_surface->paginated_surface,
+                                               width_in_points,
+                                               height_in_points);
+    if (status)
+       status = _cairo_surface_set_error (surface, status);
+}
+
+/**
+ * cairo_ps_surface_dsc_comment:
+ * @surface: a PostScript #cairo_surface_t
+ * @comment: a comment string to be emitted into the PostScript output
+ *
+ * Emit a comment into the PostScript output for the given surface.
+ *
+ * The comment is expected to conform to the PostScript Language
+ * Document Structuring Conventions (DSC). Please see that manual for
+ * details on the available comments and their meanings. In
+ * particular, the \%\%IncludeFeature comment allows a
+ * device-independent means of controlling printer device features. So
+ * the PostScript Printer Description Files Specification will also be
+ * a useful reference.
+ *
+ * The comment string must begin with a percent character (\%) and the
+ * total length of the string (including any initial percent
+ * characters) must not exceed 255 characters. Violating either of
+ * these conditions will place @surface into an error state. But
+ * beyond these two conditions, this function will not enforce
+ * conformance of the comment with any particular specification.
+ *
+ * The comment string should not have a trailing newline.
+ *
+ * The DSC specifies different sections in which particular comments
+ * can appear. This function provides for comments to be emitted
+ * within three sections: the header, the Setup section, and the
+ * PageSetup section.  Comments appearing in the first two sections
+ * apply to the entire document while comments in the BeginPageSetup
+ * section apply only to a single page.
+ *
+ * For comments to appear in the header section, this function should
+ * be called after the surface is created, but before a call to
+ * cairo_ps_surface_dsc_begin_setup().
+ *
+ * For comments to appear in the Setup section, this function should
+ * be called after a call to cairo_ps_surface_dsc_begin_setup() but
+ * before a call to cairo_ps_surface_dsc_begin_page_setup().
+ *
+ * For comments to appear in the PageSetup section, this function
+ * should be called after a call to
+ * cairo_ps_surface_dsc_begin_page_setup().
+ *
+ * Note that it is only necessary to call
+ * cairo_ps_surface_dsc_begin_page_setup() for the first page of any
+ * surface. After a call to cairo_show_page() or cairo_copy_page()
+ * comments are unambiguously directed to the PageSetup section of the
+ * current page. But it doesn't hurt to call this function at the
+ * beginning of every page as that consistency may make the calling
+ * code simpler.
+ *
+ * As a final note, cairo automatically generates several comments on
+ * its own. As such, applications must not manually generate any of
+ * the following comments:
+ *
+ * Header section: \%!PS-Adobe-3.0, \%\%Creator, \%\%CreationDate, \%\%Pages,
+ * \%\%BoundingBox, \%\%DocumentData, \%\%LanguageLevel, \%\%EndComments.
+ *
+ * Setup section: \%\%BeginSetup, \%\%EndSetup
+ *
+ * PageSetup section: \%\%BeginPageSetup, \%\%PageBoundingBox, \%\%EndPageSetup.
+ *
+ * Other sections: \%\%BeginProlog, \%\%EndProlog, \%\%Page, \%\%Trailer, \%\%EOF
+ *
+ * Here is an example sequence showing how this function might be used:
+ *
+ * <informalexample><programlisting>
+ * cairo_surface_t *surface = cairo_ps_surface_create (filename, width, height);
+ * ...
+ * cairo_ps_surface_dsc_comment (surface, "%%Title: My excellent document");
+ * cairo_ps_surface_dsc_comment (surface, "%%Copyright: Copyright (C) 2006 Cairo Lover")
+ * ...
+ * cairo_ps_surface_dsc_begin_setup (surface);
+ * cairo_ps_surface_dsc_comment (surface, "%%IncludeFeature: *MediaColor White");
+ * ...
+ * cairo_ps_surface_dsc_begin_page_setup (surface);
+ * cairo_ps_surface_dsc_comment (surface, "%%IncludeFeature: *PageSize A3");
+ * cairo_ps_surface_dsc_comment (surface, "%%IncludeFeature: *InputSlot LargeCapacity");
+ * cairo_ps_surface_dsc_comment (surface, "%%IncludeFeature: *MediaType Glossy");
+ * cairo_ps_surface_dsc_comment (surface, "%%IncludeFeature: *MediaColor Blue");
+ * ... draw to first page here ..
+ * cairo_show_page (cr);
+ * ...
+ * cairo_ps_surface_dsc_comment (surface, "%%IncludeFeature: *PageSize A5");
+ * ...
+ * </programlisting></informalexample>
+ *
+ * Since: 1.2
+ **/
+void
+cairo_ps_surface_dsc_comment (cairo_surface_t  *surface,
+                             const char        *comment)
+{
+    cairo_ps_surface_t *ps_surface = NULL;
+    cairo_status_t status;
+    char *comment_copy;
+
+    if (! _extract_ps_surface (surface, TRUE, &ps_surface))
+       return;
+
+    /* A couple of sanity checks on the comment value. */
+    if (comment == NULL) {
+       status = _cairo_surface_set_error (surface, CAIRO_STATUS_NULL_POINTER);
+       return;
+    }
+
+    if (comment[0] != '%' || strlen (comment) > 255) {
+       status = _cairo_surface_set_error (surface, CAIRO_STATUS_INVALID_DSC_COMMENT);
+       return;
+    }
+
+    /* Then, copy the comment and store it in the appropriate array. */
+    comment_copy = strdup (comment);
+    if (unlikely (comment_copy == NULL)) {
+       status = _cairo_surface_set_error (surface, CAIRO_STATUS_NO_MEMORY);
+       return;
+    }
+
+    status = _cairo_array_append (ps_surface->dsc_comment_target, &comment_copy);
+    if (unlikely (status)) {
+       free (comment_copy);
+       status = _cairo_surface_set_error (surface, status);
+       return;
+    }
+}
+
+/**
+ * cairo_ps_surface_dsc_begin_setup:
+ * @surface: a PostScript #cairo_surface_t
+ *
+ * This function indicates that subsequent calls to
+ * cairo_ps_surface_dsc_comment() should direct comments to the Setup
+ * section of the PostScript output.
+ *
+ * This function should be called at most once per surface, and must
+ * be called before any call to cairo_ps_surface_dsc_begin_page_setup()
+ * and before any drawing is performed to the surface.
+ *
+ * See cairo_ps_surface_dsc_comment() for more details.
+ *
+ * Since: 1.2
+ **/
+void
+cairo_ps_surface_dsc_begin_setup (cairo_surface_t *surface)
+{
+    cairo_ps_surface_t *ps_surface = NULL;
+
+    if (! _extract_ps_surface (surface, TRUE, &ps_surface))
+       return;
+
+    if (ps_surface->dsc_comment_target == &ps_surface->dsc_header_comments)
+       ps_surface->dsc_comment_target = &ps_surface->dsc_setup_comments;
+}
+
+/**
+ * cairo_ps_surface_dsc_begin_page_setup:
+ * @surface: a PostScript #cairo_surface_t
+ *
+ * This function indicates that subsequent calls to
+ * cairo_ps_surface_dsc_comment() should direct comments to the
+ * PageSetup section of the PostScript output.
+ *
+ * This function call is only needed for the first page of a
+ * surface. It should be called after any call to
+ * cairo_ps_surface_dsc_begin_setup() and before any drawing is
+ * performed to the surface.
+ *
+ * See cairo_ps_surface_dsc_comment() for more details.
+ *
+ * Since: 1.2
+ **/
+void
+cairo_ps_surface_dsc_begin_page_setup (cairo_surface_t *surface)
+{
+    cairo_ps_surface_t *ps_surface = NULL;
+
+    if (! _extract_ps_surface (surface, TRUE, &ps_surface))
+       return;
+
+    if (ps_surface->dsc_comment_target == &ps_surface->dsc_header_comments ||
+       ps_surface->dsc_comment_target == &ps_surface->dsc_setup_comments)
+    {
+       ps_surface->dsc_comment_target = &ps_surface->dsc_page_setup_comments;
+    }
+}
+
+static cairo_status_t
+_cairo_ps_surface_finish (void *abstract_surface)
+{
+    cairo_status_t status, status2;
+    cairo_ps_surface_t *surface = abstract_surface;
+    int i, num_comments;
+    char **comments;
+
+    status = surface->base.status;
+    if (unlikely (status))
+       goto CLEANUP;
+
+    _cairo_ps_surface_emit_header (surface);
+
+    status = _cairo_ps_surface_emit_font_subsets (surface);
+    if (unlikely (status))
+       goto CLEANUP;
+
+    status = _cairo_ps_surface_emit_body (surface);
+    if (unlikely (status))
+       goto CLEANUP;
+
+    _cairo_ps_surface_emit_footer (surface);
+
+CLEANUP:
+    _cairo_scaled_font_subsets_destroy (surface->font_subsets);
+
+    status2 = _cairo_output_stream_destroy (surface->stream);
+    if (status == CAIRO_STATUS_SUCCESS)
+       status = status2;
+
+    fclose (surface->tmpfile);
+
+    status2 = _cairo_output_stream_destroy (surface->final_stream);
+    if (status == CAIRO_STATUS_SUCCESS)
+       status = status2;
+
+    while (! cairo_list_is_empty (&surface->document_media)) {
+        cairo_page_media_t *page;
+
+        page = cairo_list_first_entry (&surface->document_media,
+                                       cairo_page_media_t,
+                                       link);
+        cairo_list_del (&page->link);
+       free (page->name);
+       free (page);
+    }
+
+    num_comments = _cairo_array_num_elements (&surface->dsc_header_comments);
+    comments = _cairo_array_index (&surface->dsc_header_comments, 0);
+    for (i = 0; i < num_comments; i++)
+       free (comments[i]);
+    _cairo_array_fini (&surface->dsc_header_comments);
+
+    num_comments = _cairo_array_num_elements (&surface->dsc_setup_comments);
+    comments = _cairo_array_index (&surface->dsc_setup_comments, 0);
+    for (i = 0; i < num_comments; i++)
+       free (comments[i]);
+    _cairo_array_fini (&surface->dsc_setup_comments);
+
+    num_comments = _cairo_array_num_elements (&surface->dsc_page_setup_comments);
+    comments = _cairo_array_index (&surface->dsc_page_setup_comments, 0);
+    for (i = 0; i < num_comments; i++)
+       free (comments[i]);
+    _cairo_array_fini (&surface->dsc_page_setup_comments);
+
+    _cairo_surface_clipper_reset (&surface->clipper);
+
+    return status;
+}
+
+static cairo_int_status_t
+_cairo_ps_surface_start_page (void *abstract_surface)
+{
+    cairo_ps_surface_t *surface = abstract_surface;
+
+    /* Increment before print so page numbers start at 1. */
+    surface->num_pages++;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_ps_surface_end_page (cairo_ps_surface_t *surface)
+{
+    cairo_int_status_t status;
+
+    status = _cairo_pdf_operators_flush (&surface->pdf_operators);
+    if (unlikely (status))
+       return status;
+
+    if (surface->clipper.clip != NULL) {
+       _cairo_output_stream_printf (surface->stream, "Q Q\n");
+       _cairo_surface_clipper_reset (&surface->clipper);
+    } else
+       _cairo_output_stream_printf (surface->stream, "Q\n");
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_ps_surface_show_page (void *abstract_surface)
+{
+    cairo_ps_surface_t *surface = abstract_surface;
+    cairo_int_status_t status;
+
+    status = _cairo_ps_surface_end_page (surface);
+    if (unlikely (status))
+       return status;
+
+    _cairo_output_stream_printf (surface->stream, "showpage\n");
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_bool_t
+color_is_gray (double red, double green, double blue)
+{
+    const double epsilon = 0.00001;
+
+    return (fabs (red - green) < epsilon &&
+           fabs (red - blue) < epsilon);
+}
+
+static cairo_int_status_t
+_cairo_ps_surface_analyze_surface_pattern_transparency (cairo_ps_surface_t      *surface,
+                                                      cairo_surface_pattern_t *pattern)
+{
+    cairo_image_surface_t  *image;
+    void                  *image_extra;
+    cairo_int_status_t      status;
+    cairo_image_transparency_t transparency;
+
+    status = _cairo_surface_acquire_source_image (pattern->surface,
+                                                 &image,
+                                                 &image_extra);
+    if (unlikely (status))
+       return status;
+
+    if (image->base.status)
+       return image->base.status;
+
+    transparency = _cairo_image_analyze_transparency (image);
+    switch (transparency) {
+    case CAIRO_IMAGE_IS_OPAQUE:
+       status = CAIRO_STATUS_SUCCESS;
+       break;
+
+    case CAIRO_IMAGE_HAS_BILEVEL_ALPHA:
+       if (surface->ps_level == CAIRO_PS_LEVEL_2) {
+           status = CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY;
+       } else {
+           surface->ps_level_used = CAIRO_PS_LEVEL_3;
+           status = CAIRO_STATUS_SUCCESS;
+       }
+       break;
+
+    case CAIRO_IMAGE_HAS_ALPHA:
+       status = CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY;
+       break;
+
+    case CAIRO_IMAGE_UNKNOWN:
+       ASSERT_NOT_REACHED;
+    }
+
+    _cairo_surface_release_source_image (pattern->surface, image, image_extra);
+
+    return status;
+}
+
+static cairo_bool_t
+surface_pattern_supported (const cairo_surface_pattern_t *pattern)
+{
+    if (pattern->surface->type == CAIRO_SURFACE_TYPE_RECORDING)
+       return TRUE;
+
+    if (pattern->surface->backend->acquire_source_image == NULL)
+       return FALSE;
+
+    /* Does an ALPHA-only source surface even make sense? Maybe, but I
+     * don't think it's worth the extra code to support it. */
+
+/* XXX: Need to write this function here...
+    content = pattern->surface->content;
+    if (content == CAIRO_CONTENT_ALPHA)
+       return FALSE;
+*/
+
+    return TRUE;
+}
+
+static cairo_bool_t
+_gradient_pattern_supported (cairo_ps_surface_t    *surface,
+                            const cairo_pattern_t *pattern)
+{
+    double min_alpha, max_alpha;
+
+    if (surface->ps_level == CAIRO_PS_LEVEL_2)
+       return FALSE;
+
+    /* Alpha gradients are only supported (by flattening the alpha)
+     * if there is no variation in the alpha across the gradient. */
+    _cairo_pattern_alpha_range (pattern, &min_alpha, &max_alpha);
+    if (min_alpha != max_alpha)
+       return FALSE;
+
+    surface->ps_level_used = CAIRO_PS_LEVEL_3;
+
+    return TRUE;
+}
+
+static cairo_bool_t
+pattern_supported (cairo_ps_surface_t *surface, const cairo_pattern_t *pattern)
+{
+    switch (pattern->type) {
+    case CAIRO_PATTERN_TYPE_SOLID:
+       return TRUE;
+
+    case CAIRO_PATTERN_TYPE_LINEAR:
+    case CAIRO_PATTERN_TYPE_RADIAL:
+    case CAIRO_PATTERN_TYPE_MESH:
+       return _gradient_pattern_supported (surface, pattern);
+
+    case CAIRO_PATTERN_TYPE_SURFACE:
+       return surface_pattern_supported ((cairo_surface_pattern_t *) pattern);
+
+    default:
+       ASSERT_NOT_REACHED;
+       return FALSE;
+    }
+}
+
+static cairo_bool_t
+mask_supported (cairo_ps_surface_t *surface, const cairo_pattern_t *mask)
+{
+    if (surface->ps_level == CAIRO_PS_LEVEL_2)
+       return FALSE;
+
+    if (mask->type == CAIRO_PATTERN_TYPE_SURFACE) {
+       cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) mask;
+       if (surface_pattern->surface->type == CAIRO_SURFACE_TYPE_IMAGE) {
+           /* check if mask if opaque or bilevel alpha */
+           if (_cairo_ps_surface_analyze_surface_pattern_transparency (surface, surface_pattern) == CAIRO_INT_STATUS_SUCCESS) {
+               surface->ps_level_used = CAIRO_PS_LEVEL_3;
+               return TRUE;
+           }
+       }
+    }
+
+    return FALSE;
+}
+
+static cairo_int_status_t
+_cairo_ps_surface_analyze_operation (cairo_ps_surface_t    *surface,
+                                    cairo_operator_t       op,
+                                    const cairo_pattern_t       *pattern,
+                                    const cairo_pattern_t       *mask,
+                                    const cairo_rectangle_int_t *extents)
+{
+    double min_alpha;
+
+    if (surface->force_fallbacks &&
+       surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
+    {
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+    }
+
+    if (! pattern_supported (surface, pattern))
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    if (! (op == CAIRO_OPERATOR_SOURCE || op == CAIRO_OPERATOR_OVER))
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    /* Mask is only supported when the mask is an image with opaque or bilevel alpha. */
+    if (mask && !mask_supported (surface, mask))
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) {
+       cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) pattern;
+
+       if (surface_pattern->surface->type == CAIRO_SURFACE_TYPE_RECORDING) {
+           if (pattern->extend == CAIRO_EXTEND_PAD)
+               return CAIRO_INT_STATUS_UNSUPPORTED;
+           else
+               return CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN;
+       }
+    }
+
+    if (op == CAIRO_OPERATOR_SOURCE) {
+       if (mask)
+           return CAIRO_INT_STATUS_UNSUPPORTED;
+       else
+           return CAIRO_STATUS_SUCCESS;
+    }
+
+    /* CAIRO_OPERATOR_OVER is only supported for opaque patterns. If
+     * the pattern contains transparency, we return
+     * CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY to the analysis
+     * surface. If the analysis surface determines that there is
+     * anything drawn under this operation, a fallback image will be
+     * used. Otherwise the operation will be replayed during the
+     * render stage and we blend the transparency into the white
+     * background to convert the pattern to opaque.
+     */
+    if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) {
+       cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) pattern;
+
+       return _cairo_ps_surface_analyze_surface_pattern_transparency (surface,
+                                                                      surface_pattern);
+    }
+
+    /* Patterns whose drawn part is opaque are directly supported;
+       those whose drawn part is partially transparent can be
+       supported by flattening the alpha. */
+    _cairo_pattern_alpha_range (pattern, &min_alpha, NULL);
+    if (CAIRO_ALPHA_IS_OPAQUE (min_alpha))
+       return CAIRO_STATUS_SUCCESS;
+
+    return CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY;
+}
+
+static cairo_bool_t
+_cairo_ps_surface_operation_supported (cairo_ps_surface_t    *surface,
+                                      cairo_operator_t       op,
+                                      const cairo_pattern_t       *pattern,
+                                      const cairo_pattern_t       *mask,
+                                      const cairo_rectangle_int_t *extents)
+{
+    return _cairo_ps_surface_analyze_operation (surface, op, pattern, mask, extents) != CAIRO_INT_STATUS_UNSUPPORTED;
+}
+
+/* The "standard" implementation limit for PostScript string sizes is
+ * 65535 characters (see PostScript Language Reference, Appendix
+ * B). We go one short of that because we sometimes need two
+ * characters in a string to represent a single ASCII85 byte, (for the
+ * escape sequences "\\", "\(", and "\)") and we must not split these
+ * across two strings. So we'd be in trouble if we went right to the
+ * limit and one of these escape sequences just happened to land at
+ * the end.
+ */
+#define STRING_ARRAY_MAX_STRING_SIZE (65535-1)
+#define STRING_ARRAY_MAX_COLUMN             72
+
+typedef struct _string_array_stream {
+    cairo_output_stream_t base;
+    cairo_output_stream_t *output;
+    int column;
+    int string_size;
+    cairo_bool_t use_strings;
+} string_array_stream_t;
+
+static cairo_status_t
+_string_array_stream_write (cairo_output_stream_t *base,
+                           const unsigned char   *data,
+                           unsigned int           length)
+{
+    string_array_stream_t *stream = (string_array_stream_t *) base;
+    unsigned char c;
+    const unsigned char backslash = '\\';
+
+    if (length == 0)
+       return CAIRO_STATUS_SUCCESS;
+
+    while (length--) {
+       if (stream->string_size == 0 && stream->use_strings) {
+           _cairo_output_stream_printf (stream->output, "(");
+           stream->column++;
+       }
+
+       c = *data++;
+       if (stream->use_strings) {
+           switch (c) {
+           case '\\':
+           case '(':
+           case ')':
+               _cairo_output_stream_write (stream->output, &backslash, 1);
+               stream->column++;
+               stream->string_size++;
+               break;
+           }
+       }
+       /* Have to be careful to never split the final ~> sequence. */
+        if (c == '~') {
+           _cairo_output_stream_write (stream->output, &c, 1);
+           stream->column++;
+           stream->string_size++;
+
+           if (length-- == 0)
+               break;
+
+           c = *data++;
+       }
+       _cairo_output_stream_write (stream->output, &c, 1);
+       stream->column++;
+       stream->string_size++;
+
+       if (stream->use_strings &&
+           stream->string_size >= STRING_ARRAY_MAX_STRING_SIZE)
+       {
+           _cairo_output_stream_printf (stream->output, ")\n");
+           stream->string_size = 0;
+           stream->column = 0;
+       }
+       if (stream->column >= STRING_ARRAY_MAX_COLUMN) {
+           _cairo_output_stream_printf (stream->output, "\n ");
+           stream->string_size += 2;
+           stream->column = 1;
+       }
+    }
+
+    return _cairo_output_stream_get_status (stream->output);
+}
+
+static cairo_status_t
+_string_array_stream_close (cairo_output_stream_t *base)
+{
+    cairo_status_t status;
+    string_array_stream_t *stream = (string_array_stream_t *) base;
+
+    if (stream->use_strings)
+       _cairo_output_stream_printf (stream->output, ")\n");
+
+    status = _cairo_output_stream_get_status (stream->output);
+
+    return status;
+}
+
+/* A string_array_stream wraps an existing output stream. It takes the
+ * data provided to it and output one or more consecutive string
+ * objects, each within the standard PostScript implementation limit
+ * of 65k characters.
+ *
+ * The strings are each separated by a space character for easy
+ * inclusion within an array object, (but the array delimiters are not
+ * added by the string_array_stream).
+ *
+ * The string array stream is also careful to wrap the output within
+ * STRING_ARRAY_MAX_COLUMN columns (+/- 1). The stream also adds
+ * necessary escaping for special characters within a string,
+ * (specifically '\', '(', and ')').
+ */
+static cairo_output_stream_t *
+_string_array_stream_create (cairo_output_stream_t *output)
+{
+    string_array_stream_t *stream;
+
+    stream = malloc (sizeof (string_array_stream_t));
+    if (unlikely (stream == NULL)) {
+       _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+       return (cairo_output_stream_t *) &_cairo_output_stream_nil;
+    }
+
+    _cairo_output_stream_init (&stream->base,
+                              _string_array_stream_write,
+                              NULL,
+                              _string_array_stream_close);
+    stream->output = output;
+    stream->column = 0;
+    stream->string_size = 0;
+    stream->use_strings = TRUE;
+
+    return &stream->base;
+}
+
+/* A base85_array_stream wraps an existing output stream. It wraps the
+ * output within STRING_ARRAY_MAX_COLUMN columns (+/- 1). The output
+ * is not enclosed in strings like string_array_stream.
+ */
+static cairo_output_stream_t *
+_base85_array_stream_create (cairo_output_stream_t *output)
+{
+    string_array_stream_t *stream;
+
+    stream = malloc (sizeof (string_array_stream_t));
+    if (unlikely (stream == NULL)) {
+       _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+       return (cairo_output_stream_t *) &_cairo_output_stream_nil;
+    }
+
+    _cairo_output_stream_init (&stream->base,
+                              _string_array_stream_write,
+                              NULL,
+                              _string_array_stream_close);
+    stream->output = output;
+    stream->column = 0;
+    stream->string_size = 0;
+    stream->use_strings = FALSE;
+
+    return &stream->base;
+}
+
+
+/* PS Output - this section handles output of the parts of the recording
+ * surface we can render natively in PS. */
+
+static cairo_status_t
+_cairo_ps_surface_flatten_image_transparency (cairo_ps_surface_t    *surface,
+                                             cairo_image_surface_t *image,
+                                             cairo_image_surface_t **opaque_image)
+{
+    cairo_surface_t *opaque;
+    cairo_surface_pattern_t pattern;
+    cairo_status_t status;
+
+    opaque = cairo_image_surface_create (CAIRO_FORMAT_RGB24,
+                                        image->width,
+                                        image->height);
+    if (unlikely (opaque->status))
+       return opaque->status;
+
+    if (surface->content == CAIRO_CONTENT_COLOR_ALPHA) {
+       status = _cairo_surface_paint (opaque,
+                                      CAIRO_OPERATOR_SOURCE,
+                                      &_cairo_pattern_white.base,
+                                      NULL);
+       if (unlikely (status)) {
+           cairo_surface_destroy (opaque);
+           return status;
+       }
+    }
+
+    _cairo_pattern_init_for_surface (&pattern, &image->base);
+    pattern.base.filter = CAIRO_FILTER_NEAREST;
+    status = _cairo_surface_paint (opaque, CAIRO_OPERATOR_OVER, &pattern.base, NULL);
+    _cairo_pattern_fini (&pattern.base);
+    if (unlikely (status)) {
+       cairo_surface_destroy (opaque);
+       return status;
+    }
+
+    *opaque_image = (cairo_image_surface_t *) opaque;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_ps_surface_emit_base85_string (cairo_ps_surface_t    *surface,
+                                     const unsigned char   *data,
+                                     unsigned long          length,
+                                     cairo_ps_compress_t    compress,
+                                     cairo_bool_t           use_strings)
+{
+    cairo_output_stream_t *base85_stream, *string_array_stream, *deflate_stream;
+    unsigned char *data_compressed;
+    unsigned long data_compressed_size;
+    cairo_status_t status, status2;
+
+    if (use_strings)
+       string_array_stream = _string_array_stream_create (surface->stream);
+    else
+       string_array_stream = _base85_array_stream_create (surface->stream);
+
+    status = _cairo_output_stream_get_status (string_array_stream);
+    if (unlikely (status))
+       return _cairo_output_stream_destroy (string_array_stream);
+
+    base85_stream = _cairo_base85_stream_create (string_array_stream);
+    status = _cairo_output_stream_get_status (base85_stream);
+    if (unlikely (status)) {
+       status2 = _cairo_output_stream_destroy (string_array_stream);
+       return _cairo_output_stream_destroy (base85_stream);
+    }
+
+    switch (compress) {
+       case CAIRO_PS_COMPRESS_NONE:
+           _cairo_output_stream_write (base85_stream, data, length);
+           break;
+
+       case CAIRO_PS_COMPRESS_LZW:
+           /* XXX: Should fix cairo-lzw to provide a stream-based interface
+            * instead. */
+           data_compressed_size = length;
+           data_compressed = _cairo_lzw_compress ((unsigned char*)data, &data_compressed_size);
+           if (unlikely (data_compressed == NULL)) {
+               status = _cairo_output_stream_destroy (string_array_stream);
+               status = _cairo_output_stream_destroy (base85_stream);
+               return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+           }
+           _cairo_output_stream_write (base85_stream, data_compressed, data_compressed_size);
+           free (data_compressed);
+           break;
+
+       case CAIRO_PS_COMPRESS_DEFLATE:
+           deflate_stream = _cairo_deflate_stream_create (base85_stream);
+           if (_cairo_output_stream_get_status (deflate_stream)) {
+               return _cairo_output_stream_destroy (deflate_stream);
+           }
+           _cairo_output_stream_write (deflate_stream, data, length);
+           status = _cairo_output_stream_destroy (deflate_stream);
+           if (unlikely (status)) {
+               status2 = _cairo_output_stream_destroy (string_array_stream);
+               status2 = _cairo_output_stream_destroy (base85_stream);
+               return _cairo_output_stream_destroy (deflate_stream);
+           }
+           break;
+    }
+    status = _cairo_output_stream_destroy (base85_stream);
+
+    /* Mark end of base85 data */
+    _cairo_output_stream_printf (string_array_stream, "~>");
+    status2 = _cairo_output_stream_destroy (string_array_stream);
+    if (status == CAIRO_STATUS_SUCCESS)
+       status = status2;
+
+    return status;
+}
+
+static cairo_status_t
+_cairo_ps_surface_emit_image (cairo_ps_surface_t    *surface,
+                             cairo_image_surface_t *image,
+                             cairo_operator_t       op,
+                             cairo_filter_t         filter,
+                             cairo_bool_t           stencil_mask)
+{
+    cairo_status_t status;
+    unsigned char *data;
+    unsigned long data_size;
+    cairo_image_surface_t *ps_image = image;
+    int x, y, i, a;
+    cairo_image_transparency_t transparency;
+    cairo_bool_t use_mask;
+    uint32_t *pixel32;
+    uint8_t *pixel8;
+    int bit;
+    cairo_image_color_t color;
+    const char *interpolate;
+    cairo_ps_compress_t compress;
+    const char *compress_filter;
+
+    if (image->base.status)
+       return image->base.status;
+
+    switch (filter) {
+    default:
+    case CAIRO_FILTER_GOOD:
+    case CAIRO_FILTER_BEST:
+    case CAIRO_FILTER_BILINEAR:
+       interpolate = "true";
+       break;
+    case CAIRO_FILTER_FAST:
+    case CAIRO_FILTER_NEAREST:
+    case CAIRO_FILTER_GAUSSIAN:
+       interpolate = "false";
+       break;
+    }
+
+    if (stencil_mask) {
+       use_mask = FALSE;
+       color = CAIRO_IMAGE_IS_MONOCHROME;
+       transparency = CAIRO_IMAGE_HAS_BILEVEL_ALPHA;
+    } else {
+       transparency = _cairo_image_analyze_transparency (image);
+
+       /* PostScript can not represent the alpha channel, so we blend the
+          current image over a white (or black for CONTENT_COLOR
+          surfaces) RGB surface to eliminate it. */
+
+       if (op == CAIRO_OPERATOR_SOURCE ||
+           transparency == CAIRO_IMAGE_HAS_ALPHA ||
+           (transparency == CAIRO_IMAGE_HAS_BILEVEL_ALPHA &&
+            surface->ps_level == CAIRO_PS_LEVEL_2))
+       {
+           status = _cairo_ps_surface_flatten_image_transparency (surface,
+                                                                  image,
+                                                                  &ps_image);
+           if (unlikely (status))
+               return status;
+
+           use_mask = FALSE;
+       } else if (transparency == CAIRO_IMAGE_IS_OPAQUE) {
+           use_mask = FALSE;
+       } else { /* transparency == CAIRO_IMAGE_HAS_BILEVEL_ALPHA */
+           use_mask = TRUE;
+       }
+
+       color = _cairo_image_analyze_color (ps_image);
+    }
+
+    /* Type 2 (mask and image interleaved) has the mask and image
+     * samples interleaved by row.  The mask row is first, one bit per
+     * pixel with (bit 7 first). The row is padded to byte
+     * boundaries. The image data is 3 bytes per pixel RGB format. */
+    switch (color) {
+    default:
+    case CAIRO_IMAGE_UNKNOWN_COLOR:
+       ASSERT_NOT_REACHED;
+    case CAIRO_IMAGE_IS_COLOR:
+       data_size = ps_image->width * 3;
+       break;
+    case CAIRO_IMAGE_IS_GRAYSCALE:
+       data_size = ps_image->width;
+       break;
+    case CAIRO_IMAGE_IS_MONOCHROME:
+       data_size = (ps_image->width + 7)/8;
+       break;
+    }
+    if (use_mask)
+       data_size += (ps_image->width + 7)/8;
+    data_size *= ps_image->height;
+    data = malloc (data_size);
+    if (unlikely (data == NULL)) {
+       status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+       goto bail1;
+    }
+
+    i = 0;
+    for (y = 0; y < ps_image->height; y++) {
+       if (stencil_mask || use_mask) {
+           /* mask row */
+           if (ps_image->format == CAIRO_FORMAT_A1) {
+               pixel8 = (uint8_t *) (ps_image->data + y * ps_image->stride);
+
+               for (x = 0; x < (ps_image->width + 7) / 8; x++, pixel8++) {
+                   a = *pixel8;
+                   a = CAIRO_BITSWAP8_IF_LITTLE_ENDIAN (a);
+                   data[i++] = a;
+               }
+           } else {
+               pixel8 = (uint8_t *) (ps_image->data + y * ps_image->stride);
+               pixel32 = (uint32_t *) (ps_image->data + y * ps_image->stride);
+               bit = 7;
+               for (x = 0; x < ps_image->width; x++) {
+                   if (ps_image->format == CAIRO_FORMAT_ARGB32) {
+                       a = (*pixel32 & 0xff000000) >> 24;
+                       pixel32++;
+                   } else {
+                       a = *pixel8;
+                       pixel8++;
+                   }
+
+                   if (transparency == CAIRO_IMAGE_HAS_ALPHA) {
+                       data[i++] = a;
+                   } else { /* transparency == CAIRO_IMAGE_HAS_BILEVEL_ALPHA or CAIRO_IMAGE_IS_OPAQUE */
+                       if (bit == 7)
+                           data[i] = 0;
+                       if (a != 0)
+                           data[i] |= (1 << bit);
+                       bit--;
+                       if (bit < 0) {
+                           bit = 7;
+                           i++;
+                       }
+                   }
+               }
+               if (bit != 7)
+                   i++;
+           }
+       }
+       if (stencil_mask)
+           continue;
+
+       /* image row*/
+       pixel32 = (uint32_t *) (ps_image->data + y * ps_image->stride);
+       bit = 7;
+       for (x = 0; x < ps_image->width; x++, pixel32++) {
+           int r, g, b;
+
+           if (ps_image->format == CAIRO_FORMAT_ARGB32) {
+               /* At this point ARGB32 images are either opaque or
+                * bilevel alpha so we don't need to unpremultiply. */
+               if (((*pixel32 & 0xff000000) >> 24) == 0) {
+                   r = g = b = 0;
+               } else {
+                   r = (*pixel32 & 0x00ff0000) >> 16;
+                   g = (*pixel32 & 0x0000ff00) >>  8;
+                   b = (*pixel32 & 0x000000ff) >>  0;
+               }
+           } else if (ps_image->format == CAIRO_FORMAT_RGB24) {
+               r = (*pixel32 & 0x00ff0000) >> 16;
+               g = (*pixel32 & 0x0000ff00) >>  8;
+               b = (*pixel32 & 0x000000ff) >>  0;
+           } else {
+               r = g = b = 0;
+           }
+
+           switch (color) {
+               case CAIRO_IMAGE_IS_COLOR:
+               case CAIRO_IMAGE_UNKNOWN_COLOR:
+                   data[i++] = r;
+                   data[i++] = g;
+                   data[i++] = b;
+                   break;
+
+               case CAIRO_IMAGE_IS_GRAYSCALE:
+                   data[i++] = r;
+                   break;
+
+               case CAIRO_IMAGE_IS_MONOCHROME:
+                   if (bit == 7)
+                       data[i] = 0;
+                   if (r != 0)
+                       data[i] |= (1 << bit);
+                   bit--;
+                   if (bit < 0) {
+                       bit = 7;
+                       i++;
+                   }
+                   break;
+           }
+       }
+       if (bit != 7)
+           i++;
+    }
+
+    if (surface->ps_level == CAIRO_PS_LEVEL_2) {
+       compress = CAIRO_PS_COMPRESS_LZW;
+       compress_filter = "LZWDecode";
+    } else {
+       compress = CAIRO_PS_COMPRESS_DEFLATE;
+       compress_filter = "FlateDecode";
+       surface->ps_level_used = CAIRO_PS_LEVEL_3;
+    }
+
+    if (surface->use_string_datasource) {
+       /* Emit the image data as a base85-encoded string which will
+        * be used as the data source for the image operator later. */
+       _cairo_output_stream_printf (surface->stream,
+                                    "/CairoImageData [\n");
+
+       status = _cairo_ps_surface_emit_base85_string (surface,
+                                                      data,
+                                                      data_size,
+                                                      compress,
+                                                      TRUE);
+       if (unlikely (status))
+           goto bail2;
+
+       _cairo_output_stream_printf (surface->stream,
+                                    "] def\n");
+       _cairo_output_stream_printf (surface->stream,
+                                    "/CairoImageDataIndex 0 def\n");
+    }
+
+    if (use_mask) {
+       _cairo_output_stream_printf (surface->stream,
+                                    "%s setcolorspace\n"
+                                    "5 dict dup begin\n"
+                                    "  /ImageType 3 def\n"
+                                    "  /InterleaveType 2 def\n"
+                                    "  /DataDict 8 dict def\n"
+                                    "    DataDict begin\n"
+                                    "    /ImageType 1 def\n"
+                                    "    /Width %d def\n"
+                                    "    /Height %d def\n"
+                                    "    /Interpolate %s def\n"
+                                    "    /BitsPerComponent %d def\n"
+                                    "    /Decode [ %s ] def\n",
+                                    color == CAIRO_IMAGE_IS_COLOR ? "/DeviceRGB" : "/DeviceGray",
+                                    ps_image->width,
+                                    ps_image->height,
+                                    interpolate,
+                                    color == CAIRO_IMAGE_IS_MONOCHROME ? 1 : 8,
+                                    color == CAIRO_IMAGE_IS_COLOR ? "0 1 0 1 0 1" : "0 1");
+
+       if (surface->use_string_datasource) {
+           _cairo_output_stream_printf (surface->stream,
+                                        "    /DataSource {\n"
+                                        "      CairoImageData CairoImageDataIndex get\n"
+                                        "      /CairoImageDataIndex CairoImageDataIndex 1 add def\n"
+                                        "      CairoImageDataIndex CairoImageData length 1 sub gt\n"
+                                        "       { /CairoImageDataIndex 0 def } if\n"
+                                        "    } /ASCII85Decode filter /%s filter def\n",
+                                        compress_filter);
+       } else {
+           _cairo_output_stream_printf (surface->stream,
+                                        "    /DataSource currentfile /ASCII85Decode filter /%s filter def\n",
+                                        compress_filter);
+       }
+
+       _cairo_output_stream_printf (surface->stream,
+                                    "    /ImageMatrix [ 1 0 0 -1 0 %d ] def\n"
+                                    "  end\n"
+                                    "  /MaskDict 8 dict def\n"
+                                    "     MaskDict begin\n"
+                                    "    /ImageType 1 def\n"
+                                    "    /Width %d def\n"
+                                    "    /Height %d def\n"
+                                    "    /Interpolate %s def\n"
+                                    "    /BitsPerComponent 1 def\n"
+                                    "    /Decode [ 1 0 ] def\n"
+                                    "    /ImageMatrix [ 1 0 0 -1 0 %d ] def\n"
+                                    "  end\n"
+                                    "end\n"
+                                    "image\n",
+                                    ps_image->height,
+                                    ps_image->width,
+                                    ps_image->height,
+                                    interpolate,
+                                    ps_image->height);
+    } else {
+       if (!stencil_mask) {
+           _cairo_output_stream_printf (surface->stream,
+                                        "%s setcolorspace\n",
+                                        color == CAIRO_IMAGE_IS_COLOR ? "/DeviceRGB" : "/DeviceGray");
+       }
+       _cairo_output_stream_printf (surface->stream,
+                                    "8 dict dup begin\n"
+                                    "  /ImageType 1 def\n"
+                                    "  /Width %d def\n"
+                                    "  /Height %d def\n"
+                                    "  /Interpolate %s def\n"
+                                    "  /BitsPerComponent %d def\n"
+                                    "  /Decode [ %s ] def\n",
+                                    ps_image->width,
+                                    ps_image->height,
+                                    interpolate,
+                                    color == CAIRO_IMAGE_IS_MONOCHROME ? 1 : 8,
+                                    stencil_mask ? "1 0" : color == CAIRO_IMAGE_IS_COLOR ? "0 1 0 1 0 1" : "0 1");
+       if (surface->use_string_datasource) {
+           _cairo_output_stream_printf (surface->stream,
+                                        "  /DataSource {\n"
+                                        "    CairoImageData CairoImageDataIndex get\n"
+                                        "    /CairoImageDataIndex CairoImageDataIndex 1 add def\n"
+                                        "    CairoImageDataIndex CairoImageData length 1 sub gt\n"
+                                        "     { /CairoImageDataIndex 0 def } if\n"
+                                        "  } /ASCII85Decode filter /%s filter def\n",
+                                        compress_filter);
+       } else {
+           _cairo_output_stream_printf (surface->stream,
+                                        "  /DataSource currentfile /ASCII85Decode filter /%s filter def\n",
+                                        compress_filter);
+       }
+
+       _cairo_output_stream_printf (surface->stream,
+                                    "  /Interpolate %s def\n"
+                                    "  /ImageMatrix [ 1 0 0 -1 0 %d ] def\n"
+                                    "end\n"
+                                    "%s\n",
+                                    interpolate,
+                                    ps_image->height,
+                                    stencil_mask ? "imagemask" : "image");
+    }
+
+    if (!surface->use_string_datasource) {
+       /* Emit the image data as a base85-encoded string which will
+        * be used as the data source for the image operator. */
+       status = _cairo_ps_surface_emit_base85_string (surface,
+                                                      data,
+                                                      data_size,
+                                                      compress,
+                                                      FALSE);
+       _cairo_output_stream_printf (surface->stream, "\n");
+    } else {
+       status = CAIRO_STATUS_SUCCESS;
+    }
+
+bail2:
+    free (data);
+
+bail1:
+    if (!use_mask && ps_image != image)
+       cairo_surface_destroy (&ps_image->base);
+
+    return status;
+}
+
+static cairo_status_t
+_cairo_ps_surface_emit_jpeg_image (cairo_ps_surface_t    *surface,
+                                  cairo_surface_t       *source,
+                                  int                    width,
+                                  int                    height)
+{
+    cairo_status_t status;
+    const unsigned char *mime_data;
+    unsigned long mime_data_length;
+    cairo_image_info_t info;
+
+    cairo_surface_get_mime_data (source, CAIRO_MIME_TYPE_JPEG,
+                                &mime_data, &mime_data_length);
+    if (unlikely (source->status))
+       return source->status;
+    if (mime_data == NULL)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    status = _cairo_image_info_get_jpeg_info (&info, mime_data, mime_data_length);
+    if (unlikely (status))
+       return status;
+
+    if (info.num_components != 1 && info.num_components != 3)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    if (surface->use_string_datasource) {
+       /* Emit the image data as a base85-encoded string which will
+        * be used as the data source for the image operator later. */
+       _cairo_output_stream_printf (surface->stream,
+                                    "/CairoImageData [\n");
+
+       status = _cairo_ps_surface_emit_base85_string (surface,
+                                                      mime_data,
+                                                      mime_data_length,
+                                                      CAIRO_PS_COMPRESS_NONE,
+                                                      TRUE);
+       if (unlikely (status))
+           return status;
+
+       _cairo_output_stream_printf (surface->stream,
+                                    "] def\n");
+       _cairo_output_stream_printf (surface->stream,
+                                    "/CairoImageDataIndex 0 def\n");
+    }
+
+    _cairo_output_stream_printf (surface->stream,
+                                "/%s setcolorspace\n"
+                                "8 dict dup begin\n"
+                                "  /ImageType 1 def\n"
+                                "  /Width %d def\n"
+                                "  /Height %d def\n"
+                                "  /BitsPerComponent %d def\n"
+                                "  /Decode [ %s ] def\n",
+                                info.num_components == 1 ? "DeviceGray" : "DeviceRGB",
+                                info.width,
+                                info.height,
+                                info.bits_per_component,
+                                 info.num_components == 1 ? "0 1" : "0 1 0 1 0 1");
+
+    if (surface->use_string_datasource) {
+       _cairo_output_stream_printf (surface->stream,
+                                    "  /DataSource {\n"
+                                    "    CairoImageData CairoImageDataIndex get\n"
+                                    "    /CairoImageDataIndex CairoImageDataIndex 1 add def\n"
+                                    "    CairoImageDataIndex CairoImageData length 1 sub gt\n"
+                                    "     { /CairoImageDataIndex 0 def } if\n"
+                                    "  } /ASCII85Decode filter /DCTDecode filter def\n");
+    } else {
+       _cairo_output_stream_printf (surface->stream,
+                                    "  /DataSource currentfile /ASCII85Decode filter /DCTDecode filter def\n");
+    }
+
+    _cairo_output_stream_printf (surface->stream,
+                                "  /ImageMatrix [ 1 0 0 -1 0 %d ] def\n"
+                                "end\n"
+                                "image\n",
+                                info.height);
+
+    if (!surface->use_string_datasource) {
+       /* Emit the image data as a base85-encoded string which will
+        * be used as the data source for the image operator. */
+       status = _cairo_ps_surface_emit_base85_string (surface,
+                                                      mime_data,
+                                                      mime_data_length,
+                                                      CAIRO_PS_COMPRESS_NONE,
+                                                      FALSE);
+    }
+
+    return status;
+}
+
+static cairo_status_t
+_cairo_ps_surface_emit_recording_surface (cairo_ps_surface_t *surface,
+                                         cairo_surface_t    *recording_surface)
+{
+    double old_width, old_height;
+    cairo_matrix_t old_cairo_to_ps;
+    cairo_content_t old_content;
+    cairo_rectangle_int_t old_page_bbox;
+    cairo_surface_clipper_t old_clipper;
+    cairo_box_t bbox;
+    cairo_int_status_t status;
+
+    old_content = surface->content;
+    old_width = surface->width;
+    old_height = surface->height;
+    old_page_bbox = surface->page_bbox;
+    old_cairo_to_ps = surface->cairo_to_ps;
+    old_clipper = surface->clipper;
+    _cairo_surface_clipper_init (&surface->clipper,
+                                _cairo_ps_surface_clipper_intersect_clip_path);
+
+    if (_cairo_surface_is_snapshot (recording_surface))
+       recording_surface = _cairo_surface_snapshot_get_target (recording_surface);
+
+    status =
+       _cairo_recording_surface_get_bbox ((cairo_recording_surface_t *) recording_surface,
+                                          &bbox,
+                                          NULL);
+    if (unlikely (status))
+       return status;
+
+#if DEBUG_PS
+    _cairo_output_stream_printf (surface->stream,
+                                "%% _cairo_ps_surface_emit_recording_surface (%f, %f), (%f, %f)\n",
+                                _cairo_fixed_to_double (bbox.p1.x),
+                                _cairo_fixed_to_double (bbox.p1.y),
+                                _cairo_fixed_to_double (bbox.p2.x),
+                                _cairo_fixed_to_double (bbox.p2.y));
+#endif
+
+    surface->width = _cairo_fixed_to_double (bbox.p2.x - bbox.p1.x);
+    surface->height = _cairo_fixed_to_double (bbox.p2.y - bbox.p1.y);
+    _cairo_box_round_to_rectangle (&bbox, &surface->page_bbox);
+
+    surface->current_pattern_is_solid_color = FALSE;
+    _cairo_pdf_operators_reset (&surface->pdf_operators);
+    cairo_matrix_init (&surface->cairo_to_ps, 1, 0, 0, -1, 0, surface->height);
+    _cairo_pdf_operators_set_cairo_to_pdf_matrix (&surface->pdf_operators,
+                                                 &surface->cairo_to_ps);
+    _cairo_output_stream_printf (surface->stream, "  q\n");
+
+    if (recording_surface->content == CAIRO_CONTENT_COLOR) {
+       surface->content = CAIRO_CONTENT_COLOR;
+       _cairo_output_stream_printf (surface->stream,
+                                    "  0 g %d %d %d %d rectfill\n",
+                                    surface->page_bbox.x,
+                                    surface->page_bbox.y,
+                                    surface->page_bbox.width,
+                                    surface->page_bbox.height);
+    }
+
+    status = _cairo_recording_surface_replay_region (recording_surface,
+                                                    NULL,
+                                                    &surface->base,
+                                                    CAIRO_RECORDING_REGION_NATIVE);
+    assert (status != CAIRO_INT_STATUS_UNSUPPORTED);
+    if (unlikely (status))
+       return status;
+
+    status = _cairo_pdf_operators_flush (&surface->pdf_operators);
+    if (unlikely (status))
+       return status;
+
+    _cairo_output_stream_printf (surface->stream, "  Q\n");
+
+    _cairo_surface_clipper_reset (&surface->clipper);
+    surface->clipper = old_clipper;
+    surface->content = old_content;
+    surface->width = old_width;
+    surface->height = old_height;
+    surface->page_bbox = old_page_bbox;
+    surface->current_pattern_is_solid_color = FALSE;
+    _cairo_pdf_operators_reset (&surface->pdf_operators);
+    surface->cairo_to_ps = old_cairo_to_ps;
+
+    _cairo_pdf_operators_set_cairo_to_pdf_matrix (&surface->pdf_operators,
+                                                 &surface->cairo_to_ps);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_ps_surface_emit_recording_subsurface (cairo_ps_surface_t *surface,
+                                            cairo_surface_t    *recording_surface,
+                                            const cairo_rectangle_int_t *extents)
+{
+    double old_width, old_height;
+    cairo_matrix_t old_cairo_to_ps;
+    cairo_content_t old_content;
+    cairo_rectangle_int_t old_page_bbox;
+    cairo_surface_clipper_t old_clipper;
+    cairo_int_status_t status;
+
+    old_content = surface->content;
+    old_width = surface->width;
+    old_height = surface->height;
+    old_page_bbox = surface->page_bbox;
+    old_cairo_to_ps = surface->cairo_to_ps;
+    old_clipper = surface->clipper;
+    _cairo_surface_clipper_init (&surface->clipper,
+                                _cairo_ps_surface_clipper_intersect_clip_path);
+
+#if DEBUG_PS
+    _cairo_output_stream_printf (surface->stream,
+                                "%% _cairo_ps_surface_emit_recording_subsurface (%d, %d), (%d, %d)\n",
+                                extents->x, extents->y,
+                                extents->width, extents->height);
+#endif
+
+    surface->page_bbox.x = surface->page_bbox.y = 0;
+    surface->page_bbox.width = surface->width  = extents->width;
+    surface->page_bbox.height = surface->height = extents->height;
+
+    surface->current_pattern_is_solid_color = FALSE;
+    _cairo_pdf_operators_reset (&surface->pdf_operators);
+    cairo_matrix_init (&surface->cairo_to_ps, 1, 0, 0, -1, 0, surface->height);
+    _cairo_pdf_operators_set_cairo_to_pdf_matrix (&surface->pdf_operators,
+                                                 &surface->cairo_to_ps);
+    _cairo_output_stream_printf (surface->stream, "  q\n");
+
+    if (_cairo_surface_is_snapshot (recording_surface))
+       recording_surface = _cairo_surface_snapshot_get_target (recording_surface);
+
+    if (recording_surface->content == CAIRO_CONTENT_COLOR) {
+       surface->content = CAIRO_CONTENT_COLOR;
+       _cairo_output_stream_printf (surface->stream,
+                                    "  0 g %d %d %d %d rectfill\n",
+                                    surface->page_bbox.x,
+                                    surface->page_bbox.y,
+                                    surface->page_bbox.width,
+                                    surface->page_bbox.height);
+    }
+
+    status = _cairo_recording_surface_replay_region (recording_surface,
+                                                    extents,
+                                                    &surface->base,
+                                                    CAIRO_RECORDING_REGION_NATIVE);
+    assert (status != CAIRO_INT_STATUS_UNSUPPORTED);
+    if (unlikely (status))
+       return status;
+
+    status = _cairo_pdf_operators_flush (&surface->pdf_operators);
+    if (unlikely (status))
+       return status;
+
+    _cairo_output_stream_printf (surface->stream, "  Q\n");
+
+    _cairo_surface_clipper_reset (&surface->clipper);
+    surface->clipper = old_clipper;
+    surface->content = old_content;
+    surface->width = old_width;
+    surface->height = old_height;
+    surface->page_bbox = old_page_bbox;
+    surface->current_pattern_is_solid_color = FALSE;
+    _cairo_pdf_operators_reset (&surface->pdf_operators);
+    surface->cairo_to_ps = old_cairo_to_ps;
+
+    _cairo_pdf_operators_set_cairo_to_pdf_matrix (&surface->pdf_operators,
+                                                 &surface->cairo_to_ps);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_ps_surface_flatten_transparency (cairo_ps_surface_t     *surface,
+                                       const cairo_color_t     *color,
+                                       double                  *red,
+                                       double                  *green,
+                                       double                  *blue)
+{
+    *red   = color->red;
+    *green = color->green;
+    *blue  = color->blue;
+
+    if (! CAIRO_COLOR_IS_OPAQUE (color)) {
+       *red   *= color->alpha;
+       *green *= color->alpha;
+       *blue  *= color->alpha;
+       if (surface->content == CAIRO_CONTENT_COLOR_ALPHA) {
+           double one_minus_alpha = 1. - color->alpha;
+           *red   += one_minus_alpha;
+           *green += one_minus_alpha;
+           *blue  += one_minus_alpha;
+       }
+    }
+}
+
+static void
+_cairo_ps_surface_emit_solid_pattern (cairo_ps_surface_t    *surface,
+                                     cairo_solid_pattern_t *pattern)
+{
+    double red, green, blue;
+
+    _cairo_ps_surface_flatten_transparency (surface, &pattern->color, &red, &green, &blue);
+
+    if (color_is_gray (red, green, blue))
+       _cairo_output_stream_printf (surface->stream,
+                                    "%f g\n",
+                                    red);
+    else
+       _cairo_output_stream_printf (surface->stream,
+                                    "%f %f %f rg\n",
+                                    red, green, blue);
+}
+
+static cairo_status_t
+_cairo_ps_surface_acquire_surface (cairo_ps_surface_t      *surface,
+                                  cairo_surface_pattern_t *pattern,
+                                  cairo_rectangle_int_t   *extents,
+                                  int                     *width,
+                                  int                     *height,
+                                  int                     *origin_x,
+                                  int                     *origin_y)
+{
+    cairo_status_t          status;
+    cairo_surface_t       *pad_image;
+    int x = 0;
+    int y = 0;
+
+    surface->acquired_image = NULL;
+    surface->image = NULL;
+
+    if (pattern->surface->type == CAIRO_SURFACE_TYPE_RECORDING) {
+       if (pattern->surface->backend->type == CAIRO_SURFACE_TYPE_SUBSURFACE) {
+           cairo_surface_subsurface_t *sub = (cairo_surface_subsurface_t *) pattern->surface;
+
+           *width  = sub->extents.width;
+           *height = sub->extents.height;
+       } else {
+           cairo_recording_surface_t *recording_surface;
+           cairo_box_t bbox;
+           cairo_rectangle_int_t extents;
+
+           recording_surface = (cairo_recording_surface_t *) pattern->surface;
+           if (_cairo_surface_is_snapshot (&recording_surface->base))
+               recording_surface = (cairo_recording_surface_t *)
+                   _cairo_surface_snapshot_get_target (&recording_surface->base);
+
+           status = _cairo_recording_surface_get_bbox (recording_surface, &bbox, NULL);
+           if (unlikely (status))
+               return status;
+
+           _cairo_box_round_to_rectangle (&bbox, &extents);
+           *width  = extents.width;
+           *height = extents.height;
+       }
+       return CAIRO_STATUS_SUCCESS;
+    } else {
+       status = _cairo_surface_acquire_source_image (pattern->surface,
+                                                     &surface->acquired_image,
+                                                     &surface->image_extra);
+       if (unlikely (status))
+           return status;
+
+       pad_image = &surface->acquired_image->base;
+       if (cairo_pattern_get_extend (&pattern->base) == CAIRO_EXTEND_PAD) {
+           cairo_box_t box;
+           cairo_rectangle_int_t rect;
+           cairo_surface_pattern_t pad_pattern;
+
+           /* get the operation extents in pattern space */
+           _cairo_box_from_rectangle (&box, extents);
+           _cairo_matrix_transform_bounding_box_fixed (&pattern->base.matrix, &box, NULL);
+           _cairo_box_round_to_rectangle (&box, &rect);
+           x = -rect.x;
+           y = -rect.y;
+
+           pad_image =
+               _cairo_image_surface_create_with_pixman_format (NULL,
+                                                               surface->acquired_image->pixman_format,
+                                                               rect.width, rect.height,
+                                                               0);
+           if (pad_image->status) {
+               status = pad_image->status;
+               goto BAIL;
+           }
+
+           _cairo_pattern_init_for_surface (&pad_pattern, &surface->acquired_image->base);
+           cairo_matrix_init_translate (&pad_pattern.base.matrix, -x, -y);
+           pad_pattern.base.extend = CAIRO_EXTEND_PAD;
+           status = _cairo_surface_paint (pad_image,
+                                          CAIRO_OPERATOR_SOURCE,
+                                          &pad_pattern.base,
+                                          NULL);
+           _cairo_pattern_fini (&pad_pattern.base);
+           if (unlikely (status)) {
+               if (pad_image != &surface->acquired_image->base)
+                   cairo_surface_destroy (pad_image);
+
+               goto BAIL;
+           }
+       }
+
+       surface->image = (cairo_image_surface_t *) pad_image;
+       *width = surface->image->width;
+       *height = surface->image->height;
+       *origin_x = x;
+       *origin_y = y;
+       return CAIRO_STATUS_SUCCESS;
+    }
+
+BAIL:
+    _cairo_ps_surface_release_surface (surface, pattern);
+    return status;
+}
+
+static cairo_status_t
+_cairo_ps_surface_emit_surface (cairo_ps_surface_t      *surface,
+                               cairo_surface_pattern_t *pattern,
+                               cairo_operator_t         op,
+                               int                      width,
+                               int                      height,
+                               cairo_bool_t             stencil_mask)
+{
+    cairo_int_status_t status;
+
+    if (pattern->surface->type == CAIRO_SURFACE_TYPE_RECORDING) {
+       cairo_surface_t *source = pattern->surface;
+
+       if (source->backend->type == CAIRO_SURFACE_TYPE_SUBSURFACE) {
+           cairo_surface_subsurface_t *sub = (cairo_surface_subsurface_t *) source;
+           status = _cairo_ps_surface_emit_recording_subsurface (surface, sub->target, &sub->extents);
+       } else {
+           status = _cairo_ps_surface_emit_recording_surface (surface, source);
+       }
+    } else {
+       if (pattern->base.extend != CAIRO_EXTEND_PAD) {
+           status = _cairo_ps_surface_emit_jpeg_image (surface, pattern->surface,
+                                                       width, height);
+           if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+               return status;
+       }
+
+       status = _cairo_ps_surface_emit_image (surface, surface->image,
+                                              op, pattern->base.filter, stencil_mask);
+    }
+
+    return status;
+}
+
+static void
+_cairo_ps_surface_release_surface (cairo_ps_surface_t      *surface,
+                                  cairo_surface_pattern_t *pattern)
+{
+    if (surface->image != surface->acquired_image)
+       cairo_surface_destroy (&surface->image->base);
+
+    if (pattern->surface->type != CAIRO_SURFACE_TYPE_RECORDING) {
+       _cairo_surface_release_source_image (pattern->surface,
+                                            surface->acquired_image,
+                                            surface->image_extra);
+    }
+
+    surface->acquired_image = NULL;
+    surface->image = NULL;
+}
+
+static void
+_path_fixed_init_rectangle (cairo_path_fixed_t *path,
+                           cairo_rectangle_int_t *rect)
+{
+    cairo_status_t status;
+
+    _cairo_path_fixed_init (path);
+
+    status = _cairo_path_fixed_move_to (path,
+                                       _cairo_fixed_from_int (rect->x),
+                                       _cairo_fixed_from_int (rect->y));
+    assert (status == CAIRO_STATUS_SUCCESS);
+    status = _cairo_path_fixed_rel_line_to (path,
+                                           _cairo_fixed_from_int (rect->width),
+                                           _cairo_fixed_from_int (0));
+    assert (status == CAIRO_STATUS_SUCCESS);
+    status = _cairo_path_fixed_rel_line_to (path,
+                                           _cairo_fixed_from_int (0),
+                                           _cairo_fixed_from_int (rect->height));
+    assert (status == CAIRO_STATUS_SUCCESS);
+    status = _cairo_path_fixed_rel_line_to (path,
+                                           _cairo_fixed_from_int (-rect->width),
+                                           _cairo_fixed_from_int (0));
+    assert (status == CAIRO_STATUS_SUCCESS);
+
+    status = _cairo_path_fixed_close_path (path);
+    assert (status == CAIRO_STATUS_SUCCESS);
+}
+
+static cairo_status_t
+_cairo_ps_surface_paint_surface (cairo_ps_surface_t      *surface,
+                                cairo_surface_pattern_t *pattern,
+                                cairo_rectangle_int_t   *extents,
+                                cairo_operator_t         op,
+                                cairo_bool_t             stencil_mask)
+{
+    cairo_status_t status;
+    int width, height;
+    cairo_matrix_t cairo_p2d, ps_p2d;
+    cairo_path_fixed_t path;
+    int origin_x = 0;
+    int origin_y = 0;
+
+    status = _cairo_pdf_operators_flush (&surface->pdf_operators);
+    if (unlikely (status))
+       return status;
+
+    status = _cairo_ps_surface_acquire_surface (surface,
+                                               pattern,
+                                               extents,
+                                               &width, &height,
+                                               &origin_x, &origin_y);
+    if (unlikely (status))
+       return status;
+
+    _path_fixed_init_rectangle (&path, extents);
+    status = _cairo_pdf_operators_clip (&surface->pdf_operators,
+                                       &path,
+                                       CAIRO_FILL_RULE_WINDING);
+    _cairo_path_fixed_fini (&path);
+    if (unlikely (status))
+       return status;
+
+    cairo_p2d = pattern->base.matrix;
+
+    if (surface->paginated_mode == CAIRO_PAGINATED_MODE_FALLBACK) {
+       double scale = cairo_p2d.xx;
+
+       _cairo_output_stream_printf (surface->stream,
+                                    "%% Fallback Image: x=%f y=%f w=%d h=%d res=%fppi size=%ld\n",
+                                    -cairo_p2d.x0/scale,
+                                    -cairo_p2d.y0/scale,
+                                    (int)(width/scale),
+                                    (int)(height/scale),
+                                    scale*72,
+                                    (long)width*height*3);
+    } else {
+       if (op == CAIRO_OPERATOR_SOURCE) {
+           _cairo_output_stream_printf (surface->stream,
+                                        "%d g 0 0 %f %f rectfill\n",
+                                        surface->content == CAIRO_CONTENT_COLOR ? 0 : 1,
+                                        surface->width,
+                                        surface->height);
+       }
+    }
+
+    status = cairo_matrix_invert (&cairo_p2d);
+    /* cairo_pattern_set_matrix ensures the matrix is invertible */
+    assert (status == CAIRO_STATUS_SUCCESS);
+
+    ps_p2d = surface->cairo_to_ps;
+    cairo_matrix_multiply (&ps_p2d, &cairo_p2d, &ps_p2d);
+    cairo_matrix_translate (&ps_p2d, -origin_x, -origin_y);
+    cairo_matrix_translate (&ps_p2d, 0.0, height);
+    cairo_matrix_scale (&ps_p2d, 1.0, -1.0);
+
+    if (! _cairo_matrix_is_identity (&ps_p2d)) {
+       _cairo_output_stream_printf (surface->stream,
+                                    "[ %f %f %f %f %f %f ] concat\n",
+                                    ps_p2d.xx, ps_p2d.yx,
+                                    ps_p2d.xy, ps_p2d.yy,
+                                    ps_p2d.x0, ps_p2d.y0);
+    }
+
+    status = _cairo_ps_surface_emit_surface (surface, pattern, op, width, height, stencil_mask);
+    _cairo_ps_surface_release_surface (surface, pattern);
+
+    return status;
+}
+
+static cairo_status_t
+_cairo_ps_surface_emit_surface_pattern (cairo_ps_surface_t      *surface,
+                                       cairo_surface_pattern_t *pattern,
+                                       cairo_rectangle_int_t   *extents,
+                                       cairo_operator_t         op)
+{
+    cairo_status_t status;
+    int pattern_width = 0; /* squelch bogus compiler warning */
+    int pattern_height = 0; /* squelch bogus compiler warning */
+    double xstep, ystep;
+    cairo_matrix_t cairo_p2d, ps_p2d;
+    cairo_bool_t old_use_string_datasource;
+    int origin_x = 0;
+    int origin_y = 0;
+
+    cairo_p2d = pattern->base.matrix;
+    status = cairo_matrix_invert (&cairo_p2d);
+    /* cairo_pattern_set_matrix ensures the matrix is invertible */
+    assert (status == CAIRO_STATUS_SUCCESS);
+
+    status = _cairo_ps_surface_acquire_surface (surface,
+                                               pattern,
+                                               extents,
+                                               &pattern_width, &pattern_height,
+                                               &origin_x, &origin_y);
+    if (unlikely (status))
+       return status;
+
+    switch (pattern->base.extend) {
+    case CAIRO_EXTEND_PAD:
+    case CAIRO_EXTEND_NONE:
+    {
+       /* In PS/PDF, (as far as I can tell), all patterns are
+        * repeating. So we support cairo's EXTEND_NONE semantics
+        * by setting the repeat step size to a size large enough
+        * to guarantee that no more than a single occurrence will
+        * be visible.
+        *
+        * First, map the surface extents into pattern space (since
+        * xstep and ystep are in pattern space).  Then use an upper
+        * bound on the length of the diagonal of the pattern image
+        * and the surface as repeat size.  This guarantees to never
+        * repeat visibly.
+        */
+       double x1 = 0.0, y1 = 0.0;
+       double x2 = surface->width, y2 = surface->height;
+       _cairo_matrix_transform_bounding_box (&pattern->base.matrix,
+                                             &x1, &y1, &x2, &y2,
+                                             NULL);
+
+       /* Rather than computing precise bounds of the union, just
+        * add the surface extents unconditionally. We only
+        * required an answer that's large enough, we don't really
+        * care if it's not as tight as possible.*/
+       xstep = ystep = ceil ((x2 - x1) + (y2 - y1) +
+                             pattern_width + pattern_height);
+       break;
+    }
+    case CAIRO_EXTEND_REPEAT:
+       xstep = pattern_width;
+       ystep = pattern_height;
+       break;
+    case CAIRO_EXTEND_REFLECT:
+       xstep = pattern_width*2;
+       ystep = pattern_height*2;
+       break;
+       /* All the rest (if any) should have been analyzed away, so these
+        * cases should be unreachable. */
+    default:
+       ASSERT_NOT_REACHED;
+       xstep = 0;
+       ystep = 0;
+    }
+
+    _cairo_output_stream_printf (surface->stream,
+                                "/CairoPattern {\n");
+
+    old_use_string_datasource = surface->use_string_datasource;
+    surface->use_string_datasource = TRUE;
+    if (op == CAIRO_OPERATOR_SOURCE) {
+       _cairo_output_stream_printf (surface->stream,
+                                    "%d g 0 0 %f %f rectfill\n",
+                                    surface->content == CAIRO_CONTENT_COLOR ? 0 : 1,
+                                    xstep, ystep);
+    }
+    status = _cairo_ps_surface_emit_surface (surface, pattern, op,
+                                            pattern_width, pattern_height, FALSE);
+    if (unlikely (status))
+       return status;
+
+    surface->use_string_datasource = old_use_string_datasource;
+    _cairo_output_stream_printf (surface->stream,
+                                "} bind def\n");
+
+    _cairo_output_stream_printf (surface->stream,
+                                "<< /PatternType 1\n"
+                                "   /PaintType 1\n"
+                                "   /TilingType 1\n");
+    _cairo_output_stream_printf (surface->stream,
+                                "   /XStep %f /YStep %f\n",
+                                xstep, ystep);
+
+    if (pattern->base.extend == CAIRO_EXTEND_REFLECT) {
+       _cairo_output_stream_printf (surface->stream,
+                                    "   /BBox [0 0 %d %d]\n"
+                                    "   /PaintProc {\n"
+                                    "      CairoPattern\n"
+                                    "      [-1 0 0  1 %d 0] concat CairoPattern\n"
+                                    "      [ 1 0 0 -1 0 %d] concat CairoPattern\n"
+                                    "      [-1 0 0  1 %d 0] concat CairoPattern\n"
+                                    "      CairoPattern\n"
+                                    "   } bind\n",
+                                    pattern_width*2, pattern_height*2,
+                                    pattern_width*2,
+                                    pattern_height*2,
+                                    pattern_width*2);
+    } else {
+       if (op == CAIRO_OPERATOR_SOURCE) {
+           _cairo_output_stream_printf (surface->stream,
+                                        "   /BBox [0 0 %f %f]\n",
+                                        xstep, ystep);
+       } else {
+           _cairo_output_stream_printf (surface->stream,
+                                        "   /BBox [0 0 %d %d]\n",
+                                        pattern_width, pattern_height);
+       }
+       _cairo_output_stream_printf (surface->stream,
+                                    "   /PaintProc { CairoPattern }\n");
+    }
+
+    _cairo_output_stream_printf (surface->stream,
+                                ">>\n");
+
+    cairo_p2d = pattern->base.matrix;
+    status = cairo_matrix_invert (&cairo_p2d);
+    /* cairo_pattern_set_matrix ensures the matrix is invertible */
+    assert (status == CAIRO_STATUS_SUCCESS);
+
+    cairo_matrix_init_identity (&ps_p2d);
+    cairo_matrix_translate (&ps_p2d, 0.0, surface->height);
+    cairo_matrix_scale (&ps_p2d, 1.0, -1.0);
+    cairo_matrix_multiply (&ps_p2d, &cairo_p2d, &ps_p2d);
+    cairo_matrix_translate (&ps_p2d, 0.0, pattern_height);
+    cairo_matrix_scale (&ps_p2d, 1.0, -1.0);
+
+    _cairo_output_stream_printf (surface->stream,
+                                "[ %f %f %f %f %f %f ]\n",
+                                ps_p2d.xx, ps_p2d.yx,
+                                ps_p2d.xy, ps_p2d.yy,
+                                ps_p2d.x0, ps_p2d.y0);
+    _cairo_output_stream_printf (surface->stream,
+                                "makepattern setpattern\n");
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+typedef struct _cairo_ps_color_stop {
+    double offset;
+    double color[4];
+} cairo_ps_color_stop_t;
+
+static void
+_cairo_ps_surface_emit_linear_colorgradient (cairo_ps_surface_t     *surface,
+                                            cairo_ps_color_stop_t  *stop1,
+                                            cairo_ps_color_stop_t  *stop2)
+{
+    _cairo_output_stream_printf (surface->stream,
+                                "   << /FunctionType 2\n"
+                                "      /Domain [ 0 1 ]\n"
+                                "      /C0 [ %f %f %f ]\n"
+                                "      /C1 [ %f %f %f ]\n"
+                                "      /N 1\n"
+                                "   >>\n",
+                                stop1->color[0],
+                                stop1->color[1],
+                                stop1->color[2],
+                                stop2->color[0],
+                                stop2->color[1],
+                                stop2->color[2]);
+}
+
+static void
+_cairo_ps_surface_emit_stitched_colorgradient (cairo_ps_surface_t    *surface,
+                                              unsigned int           n_stops,
+                                              cairo_ps_color_stop_t  stops[])
+{
+    unsigned int i;
+
+    _cairo_output_stream_printf (surface->stream,
+                                "<< /FunctionType 3\n"
+                                "   /Domain [ 0 1 ]\n"
+                                "   /Functions [\n");
+    for (i = 0; i < n_stops - 1; i++)
+       _cairo_ps_surface_emit_linear_colorgradient (surface, &stops[i], &stops[i+1]);
+
+    _cairo_output_stream_printf (surface->stream, "   ]\n");
+
+    _cairo_output_stream_printf (surface->stream, "   /Bounds [ ");
+    for (i = 1; i < n_stops-1; i++)
+       _cairo_output_stream_printf (surface->stream, "%f ", stops[i].offset);
+    _cairo_output_stream_printf (surface->stream, "]\n");
+
+    _cairo_output_stream_printf (surface->stream, "   /Encode [ 1 1 %d { pop 0 1 } for ]\n",
+                                n_stops - 1);
+
+    _cairo_output_stream_printf (surface->stream, ">>\n");
+}
+
+static void
+calc_gradient_color (cairo_ps_color_stop_t *new_stop,
+                    cairo_ps_color_stop_t *stop1,
+                    cairo_ps_color_stop_t *stop2)
+{
+    int i;
+    double offset = stop1->offset / (stop1->offset + 1.0 - stop2->offset);
+
+    for (i = 0; i < 4; i++)
+       new_stop->color[i] = stop1->color[i] + offset*(stop2->color[i] - stop1->color[i]);
+}
+
+#define COLOR_STOP_EPSILON 1e-6
+
+static cairo_status_t
+_cairo_ps_surface_emit_pattern_stops (cairo_ps_surface_t       *surface,
+                                     cairo_gradient_pattern_t *pattern)
+{
+    cairo_ps_color_stop_t *allstops, *stops;
+    unsigned int i, n_stops;
+
+    allstops = _cairo_malloc_ab ((pattern->n_stops + 2), sizeof (cairo_ps_color_stop_t));
+    if (unlikely (allstops == NULL))
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    stops = &allstops[1];
+    n_stops = pattern->n_stops;
+
+    for (i = 0; i < n_stops; i++) {
+       cairo_gradient_stop_t *stop = &pattern->stops[i];
+
+       stops[i].color[0] = stop->color.red;
+       stops[i].color[1] = stop->color.green;
+       stops[i].color[2] = stop->color.blue;
+       stops[i].color[3] = stop->color.alpha;
+       stops[i].offset = pattern->stops[i].offset;
+    }
+
+    if (pattern->base.extend == CAIRO_EXTEND_REPEAT ||
+       pattern->base.extend == CAIRO_EXTEND_REFLECT)
+    {
+       if (stops[0].offset > COLOR_STOP_EPSILON) {
+           if (pattern->base.extend == CAIRO_EXTEND_REFLECT)
+               memcpy (allstops, stops, sizeof (cairo_ps_color_stop_t));
+           else
+               calc_gradient_color (&allstops[0], &stops[0], &stops[n_stops-1]);
+           stops = allstops;
+           n_stops++;
+       }
+       stops[0].offset = 0.0;
+
+       if (stops[n_stops-1].offset < 1.0 - COLOR_STOP_EPSILON) {
+           if (pattern->base.extend == CAIRO_EXTEND_REFLECT) {
+               memcpy (&stops[n_stops],
+                       &stops[n_stops - 1],
+                       sizeof (cairo_ps_color_stop_t));
+           } else {
+               calc_gradient_color (&stops[n_stops], &stops[0], &stops[n_stops-1]);
+           }
+           n_stops++;
+       }
+       stops[n_stops-1].offset = 1.0;
+    }
+
+    for (i = 0; i < n_stops; i++) {
+       double red, green, blue;
+       cairo_color_t color;
+
+       _cairo_color_init_rgba (&color,
+                               stops[i].color[0],
+                               stops[i].color[1],
+                               stops[i].color[2],
+                               stops[i].color[3]);
+       _cairo_ps_surface_flatten_transparency (surface, &color,
+                                               &red, &green, &blue);
+       stops[i].color[0] = red;
+       stops[i].color[1] = green;
+       stops[i].color[2] = blue;
+    }
+
+    _cairo_output_stream_printf (surface->stream,
+                                "/CairoFunction\n");
+    if (stops[0].offset == stops[n_stops - 1].offset) {
+       /*
+        * The first and the last stops have the same offset, but we
+        * don't want a function with an empty domain, because that
+        * would provoke underdefined behaviour from rasterisers.
+        * This can only happen with EXTEND_PAD, because EXTEND_NONE
+        * is optimised into a clear pattern in cairo-gstate, and
+        * REFLECT/REPEAT are always transformed to have the first
+        * stop at t=0 and the last stop at t=1.  Thus we want a step
+        * function going from the first color to the last one.
+        *
+        * This can be accomplished by stitching three functions:
+        *  - a constant first color function,
+        *  - a step from the first color to the last color (with empty domain)
+        *  - a constant last color function
+        */
+       cairo_ps_color_stop_t pad_stops[4];
+
+       assert (pattern->base.extend == CAIRO_EXTEND_PAD);
+
+       pad_stops[0] = pad_stops[1] = stops[0];
+       pad_stops[2] = pad_stops[3] = stops[n_stops - 1];
+
+       pad_stops[0].offset = 0;
+       pad_stops[3].offset = 1;
+
+       _cairo_ps_surface_emit_stitched_colorgradient (surface, 4, pad_stops);
+    } else if (n_stops == 2) {
+       /* no need for stitched function */
+       _cairo_ps_surface_emit_linear_colorgradient (surface, &stops[0], &stops[1]);
+    } else {
+       /* multiple stops: stitch. XXX possible optimization: regulary spaced
+        * stops do not require stitching. XXX */
+       _cairo_ps_surface_emit_stitched_colorgradient (surface, n_stops, stops);
+    }
+    _cairo_output_stream_printf (surface->stream,
+                                "def\n");
+
+    free (allstops);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_ps_surface_emit_repeating_function (cairo_ps_surface_t       *surface,
+                                          cairo_gradient_pattern_t *pattern,
+                                          int                       begin,
+                                          int                       end)
+{
+    _cairo_output_stream_printf (surface->stream,
+                                "/CairoFunction\n"
+                                "<< /FunctionType 3\n"
+                                "   /Domain [ %d %d ]\n"
+                                "   /Functions [ %d {CairoFunction} repeat ]\n"
+                                "   /Bounds [ %d 1 %d {} for ]\n",
+                                begin,
+                                 end,
+                                end - begin,
+                                begin + 1,
+                                end - 1);
+
+    if (pattern->base.extend == CAIRO_EXTEND_REFLECT) {
+       _cairo_output_stream_printf (surface->stream, "   /Encode [ %d 1 %d { 2 mod 0 eq {0 1} {1 0} ifelse } for ]\n",
+                                    begin,
+                                    end - 1);
+    } else {
+       _cairo_output_stream_printf (surface->stream, "   /Encode [ %d 1 %d { pop 0 1 } for ]\n",
+                                    begin,
+                                    end - 1);
+    }
+
+    _cairo_output_stream_printf (surface->stream, ">> def\n");
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_ps_surface_emit_gradient (cairo_ps_surface_t       *surface,
+                                cairo_gradient_pattern_t *pattern)
+{
+    cairo_matrix_t pat_to_ps;
+    cairo_circle_double_t start, end;
+    double domain[2];
+    cairo_status_t status;
+
+    assert (pattern->n_stops != 0);
+
+    status = _cairo_ps_surface_emit_pattern_stops (surface, pattern);
+    if (unlikely (status))
+       return status;
+
+    pat_to_ps = pattern->base.matrix;
+    status = cairo_matrix_invert (&pat_to_ps);
+    /* cairo_pattern_set_matrix ensures the matrix is invertible */
+    assert (status == CAIRO_STATUS_SUCCESS);
+    cairo_matrix_multiply (&pat_to_ps, &pat_to_ps, &surface->cairo_to_ps);
+
+    if (pattern->base.extend == CAIRO_EXTEND_REPEAT ||
+       pattern->base.extend == CAIRO_EXTEND_REFLECT)
+    {
+       double bounds_x1, bounds_x2, bounds_y1, bounds_y2;
+       double x_scale, y_scale, tolerance;
+
+       /* TODO: use tighter extents */
+       bounds_x1 = 0;
+       bounds_y1 = 0;
+       bounds_x2 = surface->width;
+       bounds_y2 = surface->height;
+       _cairo_matrix_transform_bounding_box (&pattern->base.matrix,
+                                             &bounds_x1, &bounds_y1,
+                                             &bounds_x2, &bounds_y2,
+                                             NULL);
+
+       x_scale = surface->base.x_resolution / surface->base.x_fallback_resolution;
+       y_scale = surface->base.y_resolution / surface->base.y_fallback_resolution;
+
+       tolerance = fabs (_cairo_matrix_compute_determinant (&pattern->base.matrix));
+       tolerance /= _cairo_matrix_transformed_circle_major_axis (&pattern->base.matrix, 1);
+       tolerance *= MIN (x_scale, y_scale);
+
+       _cairo_gradient_pattern_box_to_parameter (pattern,
+                                                 bounds_x1, bounds_y1,
+                                                 bounds_x2, bounds_y2,
+                                                 tolerance, domain);
+    } else if (pattern->stops[0].offset == pattern->stops[pattern->n_stops - 1].offset) {
+       /*
+        * If the first and the last stop offset are the same, then
+        * the color function is a step function.
+        * _cairo_ps_surface_emit_pattern_stops emits it as a stitched
+        * function no matter how many stops the pattern has.  The
+        * domain of the stitched function will be [0 1] in this case.
+        *
+        * This is done to avoid emitting degenerate gradients for
+        * EXTEND_PAD patterns having a step color function.
+        */
+       domain[0] = 0.0;
+       domain[1] = 1.0;
+
+       assert (pattern->base.extend == CAIRO_EXTEND_PAD);
+    } else {
+       domain[0] = pattern->stops[0].offset;
+       domain[1] = pattern->stops[pattern->n_stops - 1].offset;
+    }
+
+    /* PS requires the first and last stop to be the same as the
+     * extreme coordinates. For repeating patterns this moves the
+     * extreme coordinates out to the begin/end of the repeating
+     * function. For non repeating patterns this may move the extreme
+     * coordinates in if there are not stops at offset 0 and 1. */
+    _cairo_gradient_pattern_interpolate (pattern, domain[0], &start);
+    _cairo_gradient_pattern_interpolate (pattern, domain[1], &end);
+
+    if (pattern->base.extend == CAIRO_EXTEND_REPEAT ||
+       pattern->base.extend == CAIRO_EXTEND_REFLECT)
+    {
+       int repeat_begin, repeat_end;
+
+       repeat_begin = floor (domain[0]);
+       repeat_end = ceil (domain[1]);
+
+       status = _cairo_ps_surface_emit_repeating_function (surface,
+                                                           pattern,
+                                                           repeat_begin,
+                                                           repeat_end);
+       if (unlikely (status))
+           return status;
+    } else if (pattern->n_stops <= 2) {
+       /* For EXTEND_NONE and EXTEND_PAD if there are only two stops a
+        * Type 2 function is used by itself without a stitching
+        * function. Type 2 functions always have the domain [0 1] */
+       domain[0] = 0.0;
+       domain[1] = 1.0;
+    }
+
+    if (pattern->base.type == CAIRO_PATTERN_TYPE_LINEAR) {
+       _cairo_output_stream_printf (surface->stream,
+                                    "<< /PatternType 2\n"
+                                    "   /Shading\n"
+                                    "   << /ShadingType 2\n"
+                                    "      /ColorSpace /DeviceRGB\n"
+                                    "      /Coords [ %f %f %f %f ]\n",
+                                    start.center.x, start.center.y,
+                                    end.center.x, end.center.y);
+    } else {
+       _cairo_output_stream_printf (surface->stream,
+                                    "<< /PatternType 2\n"
+                                    "   /Shading\n"
+                                    "   << /ShadingType 3\n"
+                                    "      /ColorSpace /DeviceRGB\n"
+                                    "      /Coords [ %f %f %f %f %f %f ]\n",
+                                    start.center.x, start.center.y,
+                                    MAX (start.radius, 0),
+                                    end.center.x, end.center.y,
+                                    MAX (end.radius, 0));
+    }
+
+    _cairo_output_stream_printf (surface->stream,
+                                "      /Domain [ %f %f ]\n",
+                                domain[0], domain[1]);
+
+    if (pattern->base.extend != CAIRO_EXTEND_NONE) {
+       _cairo_output_stream_printf (surface->stream,
+                                     "      /Extend [ true true ]\n");
+    } else {
+       _cairo_output_stream_printf (surface->stream,
+                                     "      /Extend [ false false ]\n");
+    }
+
+    _cairo_output_stream_printf (surface->stream,
+                                "      /Function CairoFunction\n"
+                                "   >>\n"
+                                ">>\n"
+                                "[ %f %f %f %f %f %f ]\n"
+                                "makepattern setpattern\n",
+                                 pat_to_ps.xx, pat_to_ps.yx,
+                                 pat_to_ps.xy, pat_to_ps.yy,
+                                 pat_to_ps.x0, pat_to_ps.y0);
+
+    return status;
+}
+
+static cairo_status_t
+_cairo_ps_surface_emit_mesh_pattern (cairo_ps_surface_t     *surface,
+                                    cairo_mesh_pattern_t   *pattern)
+{
+    cairo_matrix_t pat_to_ps;
+    cairo_status_t status;
+    cairo_pdf_shading_t shading;
+    int i;
+
+    if (_cairo_array_num_elements (&pattern->patches) == 0)
+        return CAIRO_INT_STATUS_NOTHING_TO_DO;
+
+    pat_to_ps = pattern->base.matrix;
+    status = cairo_matrix_invert (&pat_to_ps);
+    /* cairo_pattern_set_matrix ensures the matrix is invertible */
+    assert (status == CAIRO_STATUS_SUCCESS);
+
+    cairo_matrix_multiply (&pat_to_ps, &pat_to_ps, &surface->cairo_to_ps);
+
+    status = _cairo_pdf_shading_init_color (&shading, pattern);
+    if (unlikely (status))
+       return status;
+
+    _cairo_output_stream_printf (surface->stream,
+                                "currentfile\n"
+                                "/ASCII85Decode filter /FlateDecode filter /ReusableStreamDecode filter\n");
+
+    status = _cairo_ps_surface_emit_base85_string (surface,
+                                                  shading.data,
+                                                  shading.data_length,
+                                                  CAIRO_PS_COMPRESS_DEFLATE,
+                                                  FALSE);
+    if (status)
+       return status;
+
+    _cairo_output_stream_printf (surface->stream,
+                                "\n"
+                                "/CairoData exch def\n"
+                                "<< /PatternType 2\n"
+                                "   /Shading\n"
+                                "   << /ShadingType %d\n"
+                                "      /ColorSpace /DeviceRGB\n"
+                                "      /DataSource CairoData\n"
+                                "      /BitsPerCoordinate %d\n"
+                                "      /BitsPerComponent %d\n"
+                                "      /BitsPerFlag %d\n"
+                                "      /Decode [",
+                                shading.shading_type,
+                                shading.bits_per_coordinate,
+                                shading.bits_per_component,
+                                shading.bits_per_flag);
+
+    for (i = 0; i < shading.decode_array_length; i++)
+       _cairo_output_stream_printf (surface->stream, "%f ", shading.decode_array[i]);
+
+    _cairo_output_stream_printf (surface->stream,
+                                "]\n"
+                                "   >>\n"
+                                ">>\n");
+
+    _cairo_output_stream_printf (surface->stream,
+                                "[ %f %f %f %f %f %f ]\n",
+                                pat_to_ps.xx, pat_to_ps.yx,
+                                 pat_to_ps.xy, pat_to_ps.yy,
+                                 pat_to_ps.x0, pat_to_ps.y0);
+    _cairo_output_stream_printf (surface->stream,
+                                "makepattern\n"
+                                "setpattern\n"
+                                "currentdict /CairoData undef\n");
+
+    _cairo_pdf_shading_fini (&shading);
+
+    return status;
+}
+
+static cairo_status_t
+_cairo_ps_surface_emit_pattern (cairo_ps_surface_t *surface,
+                               const cairo_pattern_t *pattern,
+                               cairo_rectangle_int_t *extents,
+                               cairo_operator_t       op)
+{
+    cairo_status_t status;
+
+    if (pattern->type == CAIRO_PATTERN_TYPE_SOLID) {
+       cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) pattern;
+
+       if (surface->current_pattern_is_solid_color == FALSE ||
+           ! _cairo_color_equal (&surface->current_color, &solid->color))
+       {
+           status = _cairo_pdf_operators_flush (&surface->pdf_operators);
+           if (unlikely (status))
+               return status;
+
+           _cairo_ps_surface_emit_solid_pattern (surface, (cairo_solid_pattern_t *) pattern);
+
+           surface->current_pattern_is_solid_color = TRUE;
+           surface->current_color = solid->color;
+       }
+
+       return CAIRO_STATUS_SUCCESS;
+    }
+
+    surface->current_pattern_is_solid_color = FALSE;
+    status = _cairo_pdf_operators_flush (&surface->pdf_operators);
+    if (unlikely (status))
+           return status;
+
+    switch (pattern->type) {
+    case CAIRO_PATTERN_TYPE_SOLID:
+
+       _cairo_ps_surface_emit_solid_pattern (surface, (cairo_solid_pattern_t *) pattern);
+       break;
+
+    case CAIRO_PATTERN_TYPE_SURFACE:
+       status = _cairo_ps_surface_emit_surface_pattern (surface,
+                                                        (cairo_surface_pattern_t *) pattern,
+                                                        extents,
+                                                        op);
+       if (unlikely (status))
+           return status;
+       break;
+
+    case CAIRO_PATTERN_TYPE_LINEAR:
+    case CAIRO_PATTERN_TYPE_RADIAL:
+       status = _cairo_ps_surface_emit_gradient (surface,
+                                         (cairo_gradient_pattern_t *) pattern);
+       if (unlikely (status))
+           return status;
+       break;
+
+    case CAIRO_PATTERN_TYPE_MESH:
+       status = _cairo_ps_surface_emit_mesh_pattern (surface,
+                                         (cairo_mesh_pattern_t *) pattern);
+       if (unlikely (status))
+           return status;
+       break;
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_bool_t
+_cairo_ps_surface_get_extents (void                   *abstract_surface,
+                              cairo_rectangle_int_t   *rectangle)
+{
+    cairo_ps_surface_t *surface = abstract_surface;
+
+    rectangle->x = 0;
+    rectangle->y = 0;
+
+    /* XXX: The conversion to integers here is pretty bogus, (not to
+     * mention the aribitray limitation of width to a short(!). We
+     * may need to come up with a better interface for get_extents.
+     */
+    rectangle->width  = ceil (surface->width);
+    rectangle->height = ceil (surface->height);
+
+    return TRUE;
+}
+
+static void
+_cairo_ps_surface_get_font_options (void                  *abstract_surface,
+                                   cairo_font_options_t  *options)
+{
+    _cairo_font_options_init_default (options);
+
+    cairo_font_options_set_hint_style (options, CAIRO_HINT_STYLE_NONE);
+    cairo_font_options_set_hint_metrics (options, CAIRO_HINT_METRICS_OFF);
+    cairo_font_options_set_antialias (options, CAIRO_ANTIALIAS_GRAY);
+    _cairo_font_options_set_round_glyph_positions (options, CAIRO_ROUND_GLYPH_POS_OFF);
+}
+
+static cairo_int_status_t
+_cairo_ps_surface_set_clip (cairo_ps_surface_t *surface,
+                           cairo_composite_rectangles_t *composite)
+{
+    cairo_clip_t *clip = composite->clip;
+
+    if (_cairo_composite_rectangles_can_reduce_clip (composite, clip))
+       clip = NULL;
+
+    if (clip == NULL) {
+       if (_cairo_composite_rectangles_can_reduce_clip (composite,
+                                                        surface->clipper.clip))
+           return CAIRO_STATUS_SUCCESS;
+    }
+
+    return _cairo_surface_clipper_set_clip (&surface->clipper, clip);
+}
+
+static cairo_int_status_t
+_cairo_ps_surface_paint (void                  *abstract_surface,
+                        cairo_operator_t        op,
+                        const cairo_pattern_t  *source,
+                        const cairo_clip_t     *clip)
+{
+    cairo_ps_surface_t *surface = abstract_surface;
+    cairo_output_stream_t *stream = surface->stream;
+    cairo_composite_rectangles_t extents;
+    cairo_status_t status;
+
+    status = _cairo_composite_rectangles_init_for_paint (&extents,
+                                                        &surface->base,
+                                                        op, source, clip);
+    if (unlikely (status))
+       return status;
+
+    if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) {
+       status = _cairo_ps_surface_analyze_operation (surface, op, source, NULL, &extents.bounded);
+       goto cleanup_composite;
+    }
+
+    assert (_cairo_ps_surface_operation_supported (surface, op, source, NULL, &extents.bounded));
+
+#if DEBUG_PS
+    _cairo_output_stream_printf (stream,
+                                "%% _cairo_ps_surface_paint\n");
+#endif
+
+    status = _cairo_ps_surface_set_clip (surface, &extents);
+    if (unlikely (status))
+       goto cleanup_composite;
+
+    if (source->type == CAIRO_PATTERN_TYPE_SURFACE &&
+       (source->extend == CAIRO_EXTEND_NONE ||
+        source->extend == CAIRO_EXTEND_PAD))
+    {
+       status = _cairo_pdf_operators_flush (&surface->pdf_operators);
+       if (unlikely (status))
+           goto cleanup_composite;
+
+       _cairo_output_stream_printf (stream, "q\n");
+       status = _cairo_ps_surface_paint_surface (surface,
+                                                (cairo_surface_pattern_t *) source,
+                                                 &extents.bounded, op, FALSE);
+       if (unlikely (status))
+           goto cleanup_composite;
+
+       _cairo_output_stream_printf (stream, "Q\n");
+    } else {
+       status = _cairo_ps_surface_emit_pattern (surface, source, &extents.bounded, op);
+       if (unlikely (status))
+           goto cleanup_composite;
+
+       _cairo_output_stream_printf (stream, "0 0 %f %f rectfill\n",
+                                    surface->width, surface->height);
+    }
+
+cleanup_composite:
+    _cairo_composite_rectangles_fini (&extents);
+    return status;
+}
+
+static cairo_int_status_t
+_cairo_ps_surface_mask (void                   *abstract_surface,
+                       cairo_operator_t         op,
+                       const cairo_pattern_t   *source,
+                       const cairo_pattern_t   *mask,
+                       const cairo_clip_t      *clip)
+{
+    cairo_ps_surface_t *surface = abstract_surface;
+    cairo_output_stream_t *stream = surface->stream;
+    cairo_composite_rectangles_t extents;
+    cairo_status_t status;
+
+    status = _cairo_composite_rectangles_init_for_mask (&extents,
+                                                       &surface->base,
+                                                       op, source, mask, clip);
+    if (unlikely (status))
+       return status;
+
+    if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) {
+       status = _cairo_ps_surface_analyze_operation (surface, op, source, mask, &extents.bounded);
+       goto cleanup_composite;
+    }
+
+    assert (_cairo_ps_surface_operation_supported (surface, op, source, mask, &extents.bounded));
+
+#if DEBUG_PS
+    _cairo_output_stream_printf (stream,
+                                "%% _cairo_ps_surface_mask\n");
+#endif
+
+    status = _cairo_ps_surface_set_clip (surface, &extents);
+    if (unlikely (status))
+       goto cleanup_composite;
+
+    status = _cairo_ps_surface_emit_pattern (surface, source, &extents.bounded, op);
+    if (unlikely (status))
+       goto cleanup_composite;
+
+    _cairo_output_stream_printf (stream, "q\n");
+    status = _cairo_ps_surface_paint_surface (surface,
+                                             (cairo_surface_pattern_t *) mask,
+                                             &extents.bounded, op, TRUE);
+    if (unlikely (status))
+       goto cleanup_composite;
+
+    _cairo_output_stream_printf (stream, "Q\n");
+
+cleanup_composite:
+    _cairo_composite_rectangles_fini (&extents);
+    return status;
+}
+
+static cairo_int_status_t
+_cairo_ps_surface_stroke (void                 *abstract_surface,
+                         cairo_operator_t       op,
+                         const cairo_pattern_t *source,
+                         const cairo_path_fixed_t      *path,
+                         const cairo_stroke_style_t    *style,
+                         const cairo_matrix_t  *ctm,
+                         const cairo_matrix_t  *ctm_inverse,
+                         double                 tolerance,
+                         cairo_antialias_t      antialias,
+                         const cairo_clip_t            *clip)
+{
+    cairo_ps_surface_t *surface = abstract_surface;
+    cairo_composite_rectangles_t extents;
+    cairo_int_status_t status;
+
+    status = _cairo_composite_rectangles_init_for_stroke (&extents,
+                                                         &surface->base,
+                                                         op, source,
+                                                         path, style, ctm,
+                                                         clip);
+    if (unlikely (status))
+       return status;
+
+    /* use the more accurate extents */
+    {
+       cairo_rectangle_int_t r;
+       cairo_box_t b;
+
+       status = _cairo_path_fixed_stroke_extents (path, style,
+                                                  ctm, ctm_inverse,
+                                                  tolerance,
+                                                  &r);
+       if (unlikely (status))
+           goto cleanup_composite;
+
+       _cairo_box_from_rectangle (&b, &r);
+       status = _cairo_composite_rectangles_intersect_mask_extents (&extents, &b);
+       if (unlikely (status))
+           goto cleanup_composite;
+    }
+
+    if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) {
+       status = _cairo_ps_surface_analyze_operation (surface, op, source, NULL, &extents.bounded);
+       goto cleanup_composite;
+    }
+
+    assert (_cairo_ps_surface_operation_supported (surface, op, source, NULL, &extents.bounded));
+
+#if DEBUG_PS
+    _cairo_output_stream_printf (surface->stream,
+                                "%% _cairo_ps_surface_stroke\n");
+#endif
+
+    status = _cairo_ps_surface_set_clip (surface, &extents);
+    if (unlikely (status))
+       goto cleanup_composite;
+
+    status = _cairo_ps_surface_emit_pattern (surface, source, &extents.bounded, op);
+    if (unlikely (status))
+       goto cleanup_composite;
+
+    status = _cairo_pdf_operators_stroke (&surface->pdf_operators,
+                                         path,
+                                         style,
+                                         ctm,
+                                         ctm_inverse);
+
+cleanup_composite:
+    _cairo_composite_rectangles_fini (&extents);
+    return status;
+}
+
+static cairo_int_status_t
+_cairo_ps_surface_fill (void           *abstract_surface,
+                       cairo_operator_t         op,
+                       const cairo_pattern_t   *source,
+                       const cairo_path_fixed_t*path,
+                       cairo_fill_rule_t        fill_rule,
+                       double                   tolerance,
+                       cairo_antialias_t        antialias,
+                       const cairo_clip_t              *clip)
+{
+    cairo_ps_surface_t *surface = abstract_surface;
+    cairo_composite_rectangles_t extents;
+    cairo_int_status_t status;
+
+    status = _cairo_composite_rectangles_init_for_fill (&extents,
+                                                       &surface->base,
+                                                       op, source, path,
+                                                       clip);
+    if (unlikely (status))
+       return status;
+
+    /* use the more accurate extents */
+    {
+       cairo_rectangle_int_t r;
+       cairo_box_t b;
+
+       _cairo_path_fixed_fill_extents (path,
+                                       fill_rule,
+                                       tolerance,
+                                       &r);
+
+       _cairo_box_from_rectangle (&b, &r);
+       status = _cairo_composite_rectangles_intersect_mask_extents (&extents, &b);
+       if (unlikely (status))
+           goto cleanup_composite;
+    }
+
+    if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) {
+       status = _cairo_ps_surface_analyze_operation (surface, op, source, NULL, &extents.bounded);
+       goto cleanup_composite;
+    }
+
+    assert (_cairo_ps_surface_operation_supported (surface, op, source, NULL, &extents.bounded));
+
+#if DEBUG_PS
+    _cairo_output_stream_printf (surface->stream,
+                                "%% _cairo_ps_surface_fill\n");
+#endif
+
+    status = _cairo_pdf_operators_flush (&surface->pdf_operators);
+    if (unlikely (status))
+       goto cleanup_composite;
+
+    status = _cairo_ps_surface_set_clip (surface, &extents);
+    if (unlikely (status))
+       goto cleanup_composite;
+
+    if (source->type == CAIRO_PATTERN_TYPE_SURFACE &&
+       (source->extend == CAIRO_EXTEND_NONE ||
+        source->extend == CAIRO_EXTEND_PAD))
+    {
+       _cairo_output_stream_printf (surface->stream, "q\n");
+
+       status =  _cairo_pdf_operators_clip (&surface->pdf_operators,
+                                            path,
+                                            fill_rule);
+       if (unlikely (status))
+           goto cleanup_composite;
+
+       status = _cairo_ps_surface_paint_surface (surface,
+                                                (cairo_surface_pattern_t *) source,
+                                                 &extents.bounded, op, FALSE);
+       if (unlikely (status))
+           goto cleanup_composite;
+
+       _cairo_output_stream_printf (surface->stream, "Q\n");
+       _cairo_pdf_operators_reset (&surface->pdf_operators);
+    } else {
+       status = _cairo_ps_surface_emit_pattern (surface, source, &extents.bounded, op);
+       if (unlikely (status))
+           goto cleanup_composite;
+
+       status = _cairo_pdf_operators_fill (&surface->pdf_operators,
+                                           path,
+                                           fill_rule);
+    }
+
+cleanup_composite:
+    _cairo_composite_rectangles_fini (&extents);
+    return status;
+}
+
+static cairo_bool_t
+_cairo_ps_surface_has_show_text_glyphs (void                   *abstract_surface)
+{
+    return TRUE;
+}
+
+static cairo_int_status_t
+_cairo_ps_surface_show_text_glyphs (void                      *abstract_surface,
+                                   cairo_operator_t            op,
+                                   const cairo_pattern_t      *source,
+                                   const char                 *utf8,
+                                   int                         utf8_len,
+                                   cairo_glyph_t              *glyphs,
+                                   int                         num_glyphs,
+                                   const cairo_text_cluster_t *clusters,
+                                   int                         num_clusters,
+                                   cairo_text_cluster_flags_t  cluster_flags,
+                                   cairo_scaled_font_t        *scaled_font,
+                                   const cairo_clip_t         *clip)
+{
+    cairo_ps_surface_t *surface = abstract_surface;
+    cairo_composite_rectangles_t extents;
+    cairo_bool_t overlap;
+    cairo_status_t status;
+
+    status = _cairo_composite_rectangles_init_for_glyphs (&extents,
+                                                         &surface->base,
+                                                         op, source,
+                                                         scaled_font,
+                                                         glyphs, num_glyphs,
+                                                         clip,
+                                                         &overlap);
+    if (unlikely (status))
+       return status;
+
+    if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) {
+       status = _cairo_ps_surface_analyze_operation (surface, op, source, NULL, &extents.bounded);
+       goto cleanup_composite;
+    }
+
+    assert (_cairo_ps_surface_operation_supported (surface, op, source, NULL, &extents.bounded));
+
+#if DEBUG_PS
+    _cairo_output_stream_printf (surface->stream,
+                                "%% _cairo_ps_surface_show_glyphs\n");
+#endif
+
+    status = _cairo_ps_surface_set_clip (surface, &extents);
+    if (unlikely (status))
+       goto cleanup_composite;
+
+    status = _cairo_ps_surface_emit_pattern (surface, source, &extents.bounded, op);
+    if (unlikely (status))
+       goto cleanup_composite;
+
+    status = _cairo_pdf_operators_show_text_glyphs (&surface->pdf_operators,
+                                                   utf8, utf8_len,
+                                                   glyphs, num_glyphs,
+                                                   clusters, num_clusters,
+                                                   cluster_flags,
+                                                   scaled_font);
+
+cleanup_composite:
+    _cairo_composite_rectangles_fini (&extents);
+    return status;
+}
+
+static void
+_cairo_ps_surface_set_paginated_mode (void                     *abstract_surface,
+                                     cairo_paginated_mode_t     paginated_mode)
+{
+    cairo_ps_surface_t *surface = abstract_surface;
+    cairo_status_t status;
+
+    surface->paginated_mode = paginated_mode;
+
+    if (surface->clipper.clip != NULL) {
+       status = _cairo_pdf_operators_flush (&surface->pdf_operators);
+
+       _cairo_output_stream_printf (surface->stream, "Q q\n");
+       _cairo_surface_clipper_reset (&surface->clipper);
+    }
+}
+
+static cairo_int_status_t
+_cairo_ps_surface_set_bounding_box (void               *abstract_surface,
+                                   cairo_box_t         *bbox)
+{
+    cairo_ps_surface_t *surface = abstract_surface;
+    int i, num_comments;
+    char **comments;
+    int x1, y1, x2, y2;
+    cairo_bool_t has_page_media, has_page_bbox;
+    const char *page_media;
+
+    x1 = floor (_cairo_fixed_to_double (bbox->p1.x));
+    y1 = floor (surface->height - _cairo_fixed_to_double (bbox->p2.y));
+    x2 = ceil (_cairo_fixed_to_double (bbox->p2.x));
+    y2 = ceil (surface->height - _cairo_fixed_to_double (bbox->p1.y));
+
+    surface->page_bbox.x = x1;
+    surface->page_bbox.y = y1;
+    surface->page_bbox.width  = x2 - x1;
+    surface->page_bbox.height = y2 - y1;
+
+    _cairo_output_stream_printf (surface->stream,
+                                "%%%%Page: %d %d\n",
+                                surface->num_pages,
+                                surface->num_pages);
+
+    _cairo_output_stream_printf (surface->stream,
+                                "%%%%BeginPageSetup\n");
+
+    has_page_media = FALSE;
+    has_page_bbox = FALSE;
+    num_comments = _cairo_array_num_elements (&surface->dsc_page_setup_comments);
+    comments = _cairo_array_index (&surface->dsc_page_setup_comments, 0);
+    for (i = 0; i < num_comments; i++) {
+       _cairo_output_stream_printf (surface->stream,
+                                    "%s\n", comments[i]);
+       if (strncmp (comments[i], "%%PageMedia:", 11) == 0)
+           has_page_media = TRUE;
+
+       if (strncmp (comments[i], "%%PageBoundingBox:", 18) == 0)
+           has_page_bbox = TRUE;
+
+       free (comments[i]);
+       comments[i] = NULL;
+    }
+    _cairo_array_truncate (&surface->dsc_page_setup_comments, 0);
+
+    if (!has_page_media && !surface->eps) {
+       page_media = _cairo_ps_surface_get_page_media (surface);
+       if (unlikely (page_media == NULL))
+           return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+       _cairo_output_stream_printf (surface->stream,
+                                    "%%%%PageMedia: %s\n",
+                                    page_media);
+    }
+
+    if (!has_page_bbox) {
+       _cairo_output_stream_printf (surface->stream,
+                                    "%%%%PageBoundingBox: %d %d %d %d\n",
+                                    x1, y1, x2, y2);
+    }
+
+    _cairo_output_stream_printf (surface->stream,
+                                 "%%%%EndPageSetup\n"
+                                "q %d %d %d %d rectclip q\n",
+                                surface->page_bbox.x,
+                                surface->page_bbox.y,
+                                surface->page_bbox.width,
+                                surface->page_bbox.height);
+
+    if (surface->num_pages == 1) {
+       surface->bbox_x1 = x1;
+       surface->bbox_y1 = y1;
+       surface->bbox_x2 = x2;
+       surface->bbox_y2 = y2;
+    } else {
+       if (x1 < surface->bbox_x1)
+           surface->bbox_x1 = x1;
+       if (y1 < surface->bbox_y1)
+           surface->bbox_y1 = y1;
+       if (x2 > surface->bbox_x2)
+           surface->bbox_x2 = x2;
+       if (y2 > surface->bbox_y2)
+           surface->bbox_y2 = y2;
+    }
+    surface->current_pattern_is_solid_color = FALSE;
+    _cairo_pdf_operators_reset (&surface->pdf_operators);
+
+    return _cairo_output_stream_get_status (surface->stream);
+}
+
+static cairo_bool_t
+_cairo_ps_surface_supports_fine_grained_fallbacks (void            *abstract_surface)
+{
+    return TRUE;
+}
+
+static const cairo_surface_backend_t cairo_ps_surface_backend = {
+    CAIRO_SURFACE_TYPE_PS,
+    _cairo_ps_surface_finish,
+
+    _cairo_default_context_create,
+
+    NULL, /* create similar: handled by wrapper */
+    NULL, /* create similar image */
+    NULL, /* map to image */
+    NULL, /* unmap image */
+
+    NULL, /* acquire_source_image */
+    NULL, /* release_source_image */
+    NULL, /* snapshot */
+
+    NULL, /* cairo_ps_surface_copy_page */
+    _cairo_ps_surface_show_page,
+
+    _cairo_ps_surface_get_extents,
+    _cairo_ps_surface_get_font_options,
+
+    NULL, /* flush */
+    NULL, /* mark_dirty_rectangle */
+
+    /* Here are the drawing functions */
+
+    _cairo_ps_surface_paint, /* paint */
+    _cairo_ps_surface_mask,
+    _cairo_ps_surface_stroke,
+    _cairo_ps_surface_fill,
+    NULL, /* fill-stroke */
+    NULL, /* show_glyphs */
+    _cairo_ps_surface_has_show_text_glyphs,
+    _cairo_ps_surface_show_text_glyphs,
+};
+
+static const cairo_paginated_surface_backend_t cairo_ps_surface_paginated_backend = {
+    _cairo_ps_surface_start_page,
+    _cairo_ps_surface_set_paginated_mode,
+    _cairo_ps_surface_set_bounding_box,
+    NULL, /* _cairo_ps_surface_has_fallback_images, */
+    _cairo_ps_surface_supports_fine_grained_fallbacks,
+};
diff --git a/src/cairo-ps.h b/src/cairo-ps.h
new file mode 100644 (file)
index 0000000..fd1d21d
--- /dev/null
@@ -0,0 +1,114 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *     Carl D. Worth <cworth@cworth.org>
+ */
+
+#ifndef CAIRO_PS_H
+#define CAIRO_PS_H
+
+#include "cairo.h"
+
+#if CAIRO_HAS_PS_SURFACE
+
+#include <stdio.h>
+
+CAIRO_BEGIN_DECLS
+
+/* PS-surface functions */
+
+/**
+ * cairo_ps_level_t:
+ * @CAIRO_PS_LEVEL_2: The language level 2 of the PostScript specification.
+ * @CAIRO_PS_LEVEL_3: The language level 3 of the PostScript specification.
+ *
+ * #cairo_ps_level_t is used to describe the language level of the
+ * PostScript Language Reference that a generated PostScript file will
+ * conform to.
+ */
+typedef enum _cairo_ps_level {
+    CAIRO_PS_LEVEL_2,
+    CAIRO_PS_LEVEL_3
+} cairo_ps_level_t;
+
+cairo_public cairo_surface_t *
+cairo_ps_surface_create (const char            *filename,
+                        double                  width_in_points,
+                        double                  height_in_points);
+
+cairo_public 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_public void
+cairo_ps_surface_restrict_to_level (cairo_surface_t    *surface,
+                                    cairo_ps_level_t    level);
+
+cairo_public void
+cairo_ps_get_levels (cairo_ps_level_t const  **levels,
+                     int                      *num_levels);
+
+cairo_public const char *
+cairo_ps_level_to_string (cairo_ps_level_t level);
+
+cairo_public void
+cairo_ps_surface_set_eps (cairo_surface_t      *surface,
+                         cairo_bool_t           eps);
+
+cairo_public cairo_bool_t
+cairo_ps_surface_get_eps (cairo_surface_t      *surface);
+
+cairo_public void
+cairo_ps_surface_set_size (cairo_surface_t     *surface,
+                          double                width_in_points,
+                          double                height_in_points);
+
+cairo_public void
+cairo_ps_surface_dsc_comment (cairo_surface_t  *surface,
+                             const char        *comment);
+
+cairo_public void
+cairo_ps_surface_dsc_begin_setup (cairo_surface_t *surface);
+
+cairo_public void
+cairo_ps_surface_dsc_begin_page_setup (cairo_surface_t *surface);
+
+CAIRO_END_DECLS
+
+#else  /* CAIRO_HAS_PS_SURFACE */
+# error Cairo was not compiled with support for the ps backend
+#endif /* CAIRO_HAS_PS_SURFACE */
+
+#endif /* CAIRO_PS_H */
diff --git a/src/cairo-qt-surface.cpp b/src/cairo-qt-surface.cpp
new file mode 100644 (file)
index 0000000..aaed90c
--- /dev/null
@@ -0,0 +1,1788 @@
+/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2008 Mozilla Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Mozilla Corporation.
+ *
+ * Contributor(s):
+ *      Vladimir Vukicevic <vladimir@mozilla.com>
+ */
+
+/* Get INT16_MIN etc. as per C99 */
+#define __STDC_LIMIT_MACROS
+
+#include "cairoint.h"
+
+#include "cairo-clip-private.h"
+#include "cairo-default-context-private.h"
+#include "cairo-error-private.h"
+#include "cairo-region-private.h"
+#include "cairo-surface-clipper-private.h"
+#include "cairo-types-private.h"
+
+#include "cairo-ft.h"
+#include "cairo-qt.h"
+
+#include <memory>
+
+#include <QtGui/QPainter>
+#include <QtGui/QPaintEngine>
+#include <QtGui/QPaintDevice>
+#include <QtGui/QImage>
+#include <QtGui/QPixmap>
+#include <QtGui/QBrush>
+#include <QtGui/QPen>
+#include <QtGui/QWidget>
+#include <QtGui/QX11Info>
+#include <QtCore/QVarLengthArray>
+
+#if (QT_VERSION >= QT_VERSION_CHECK(4, 7, 0)) || defined(QT_GLYPHS_API_BACKPORT)
+extern void qt_draw_glyphs(QPainter *, const quint32 *glyphs, const QPointF *positions, int count);
+#endif
+
+#include <sys/time.h>
+
+/* Enable workaround slow regional Qt paths */
+#define ENABLE_FAST_FILL 0
+#define ENABLE_FAST_CLIP 0
+
+#if 0
+#define D(x)  x
+static const char *
+_opstr (cairo_operator_t op)
+{
+    const char *ops[] = {
+        "CLEAR",
+        "SOURCE",
+        "OVER",
+        "IN",
+        "OUT",
+        "ATOP",
+        "DEST",
+        "DEST_OVER",
+        "DEST_IN",
+        "DEST_OUT",
+        "DEST_ATOP",
+        "XOR",
+        "ADD",
+        "SATURATE"
+    };
+
+    if (op < CAIRO_OPERATOR_CLEAR || op > CAIRO_OPERATOR_SATURATE)
+        return "(\?\?\?)";
+
+    return ops[op];
+}
+#else
+#define D(x) do { } while(0)
+#endif
+
+#ifndef CAIRO_INT_STATUS_SUCCESS
+#define CAIRO_INT_STATUS_SUCCESS ((cairo_int_status_t) CAIRO_STATUS_SUCCESS)
+#endif
+
+/* Qt::PenStyle optimization based on the assumption that dots are 1*w and dashes are 3*w. */
+#define DOT_LENGTH  1.0
+#define DASH_LENGTH 3.0
+
+struct cairo_qt_surface_t {
+    cairo_surface_t base;
+
+    cairo_bool_t supports_porter_duff;
+
+    QPainter *p;
+
+    /* The pixmap/image constructors will store their objects here */
+    QPixmap *pixmap;
+    QImage *image;
+
+    QRect window;
+
+    cairo_surface_clipper_t clipper;
+
+    cairo_surface_t *image_equiv;
+};
+
+/* Will be true if we ever try to create a QPixmap and end
+ * up with one without an alpha channel.
+ */
+static cairo_bool_t _qpixmaps_have_no_alpha = FALSE;
+
+/**
+ ** Helper methods
+ **/
+
+static QPainter::CompositionMode
+_qpainter_compositionmode_from_cairo_op (cairo_operator_t op)
+{
+    switch (op) {
+    case CAIRO_OPERATOR_CLEAR:
+        return QPainter::CompositionMode_Clear;
+
+    case CAIRO_OPERATOR_SOURCE:
+        return QPainter::CompositionMode_Source;
+    case CAIRO_OPERATOR_OVER:
+        return QPainter::CompositionMode_SourceOver;
+    case CAIRO_OPERATOR_IN:
+        return QPainter::CompositionMode_SourceIn;
+    case CAIRO_OPERATOR_OUT:
+        return QPainter::CompositionMode_SourceOut;
+    case CAIRO_OPERATOR_ATOP:
+        return QPainter::CompositionMode_SourceAtop;
+
+    case CAIRO_OPERATOR_DEST:
+        return QPainter::CompositionMode_Destination;
+    case CAIRO_OPERATOR_DEST_OVER:
+        return QPainter::CompositionMode_DestinationOver;
+    case CAIRO_OPERATOR_DEST_IN:
+        return QPainter::CompositionMode_DestinationIn;
+    case CAIRO_OPERATOR_DEST_OUT:
+        return QPainter::CompositionMode_DestinationOut;
+    case CAIRO_OPERATOR_DEST_ATOP:
+        return QPainter::CompositionMode_DestinationAtop;
+
+    case CAIRO_OPERATOR_XOR:
+        return QPainter::CompositionMode_Xor;
+
+    default:
+    case CAIRO_OPERATOR_ADD:
+    case CAIRO_OPERATOR_SATURATE:
+    case CAIRO_OPERATOR_MULTIPLY:
+    case CAIRO_OPERATOR_SCREEN:
+    case CAIRO_OPERATOR_OVERLAY:
+    case CAIRO_OPERATOR_DARKEN:
+    case CAIRO_OPERATOR_LIGHTEN:
+    case CAIRO_OPERATOR_COLOR_DODGE:
+    case CAIRO_OPERATOR_COLOR_BURN:
+    case CAIRO_OPERATOR_HARD_LIGHT:
+    case CAIRO_OPERATOR_SOFT_LIGHT:
+    case CAIRO_OPERATOR_DIFFERENCE:
+    case CAIRO_OPERATOR_EXCLUSION:
+    case CAIRO_OPERATOR_HSL_HUE:
+    case CAIRO_OPERATOR_HSL_SATURATION:
+    case CAIRO_OPERATOR_HSL_COLOR:
+    case CAIRO_OPERATOR_HSL_LUMINOSITY:
+       ASSERT_NOT_REACHED;
+    }
+}
+
+static bool
+_op_is_supported (cairo_qt_surface_t *qs, cairo_operator_t op)
+{
+    if (qs->supports_porter_duff) {
+       switch (op) {
+       case CAIRO_OPERATOR_CLEAR:
+       case CAIRO_OPERATOR_SOURCE:
+       case CAIRO_OPERATOR_OVER:
+       case CAIRO_OPERATOR_IN:
+       case CAIRO_OPERATOR_OUT:
+       case CAIRO_OPERATOR_ATOP:
+
+       case CAIRO_OPERATOR_DEST:
+       case CAIRO_OPERATOR_DEST_OVER:
+       case CAIRO_OPERATOR_DEST_IN:
+       case CAIRO_OPERATOR_DEST_OUT:
+       case CAIRO_OPERATOR_DEST_ATOP:
+
+       case CAIRO_OPERATOR_XOR:
+           return TRUE;
+
+       default:
+           ASSERT_NOT_REACHED;
+       case CAIRO_OPERATOR_ADD:
+       case CAIRO_OPERATOR_SATURATE:
+       case CAIRO_OPERATOR_MULTIPLY:
+       case CAIRO_OPERATOR_SCREEN:
+       case CAIRO_OPERATOR_OVERLAY:
+       case CAIRO_OPERATOR_DARKEN:
+       case CAIRO_OPERATOR_LIGHTEN:
+       case CAIRO_OPERATOR_COLOR_DODGE:
+       case CAIRO_OPERATOR_COLOR_BURN:
+       case CAIRO_OPERATOR_HARD_LIGHT:
+       case CAIRO_OPERATOR_SOFT_LIGHT:
+       case CAIRO_OPERATOR_DIFFERENCE:
+       case CAIRO_OPERATOR_EXCLUSION:
+       case CAIRO_OPERATOR_HSL_HUE:
+       case CAIRO_OPERATOR_HSL_SATURATION:
+       case CAIRO_OPERATOR_HSL_COLOR:
+       case CAIRO_OPERATOR_HSL_LUMINOSITY:
+           return FALSE;
+
+       }
+    } else {
+       return op == CAIRO_OPERATOR_OVER;
+    }
+}
+
+static cairo_format_t
+_cairo_format_from_qimage_format (QImage::Format fmt)
+{
+    switch (fmt) {
+    case QImage::Format_ARGB32_Premultiplied:
+        return CAIRO_FORMAT_ARGB32;
+    case QImage::Format_RGB32:
+        return CAIRO_FORMAT_RGB24;
+    case QImage::Format_Indexed8: // XXX not quite
+        return CAIRO_FORMAT_A8;
+#ifdef WORDS_BIGENDIAN
+    case QImage::Format_Mono:
+#else
+    case QImage::Format_MonoLSB:
+#endif
+        return CAIRO_FORMAT_A1;
+
+    case QImage::Format_Invalid:
+#ifdef WORDS_BIGENDIAN
+    case QImage::Format_MonoLSB:
+#else
+    case QImage::Format_Mono:
+#endif
+    case QImage::Format_ARGB32:
+    case QImage::Format_RGB16:
+    case QImage::Format_ARGB8565_Premultiplied:
+    case QImage::Format_RGB666:
+    case QImage::Format_ARGB6666_Premultiplied:
+    case QImage::Format_RGB555:
+    case QImage::Format_ARGB8555_Premultiplied:
+    case QImage::Format_RGB888:
+    case QImage::Format_RGB444:
+    case QImage::Format_ARGB4444_Premultiplied:
+    case QImage::NImageFormats:
+    default:
+       ASSERT_NOT_REACHED;
+        return (cairo_format_t) -1;
+    }
+}
+
+static QImage::Format
+_qimage_format_from_cairo_format (cairo_format_t fmt)
+{
+    switch (fmt) {
+    case CAIRO_FORMAT_INVALID:
+       ASSERT_NOT_REACHED;
+    case CAIRO_FORMAT_ARGB32:
+        return QImage::Format_ARGB32_Premultiplied;
+    case CAIRO_FORMAT_RGB24:
+        return QImage::Format_RGB32;
+    case CAIRO_FORMAT_RGB16_565:
+        return QImage::Format_RGB16;
+    case CAIRO_FORMAT_A8:
+        return QImage::Format_Indexed8; // XXX not quite
+    case CAIRO_FORMAT_A1:
+#ifdef WORDS_BIGENDIAN
+        return QImage::Format_Mono; // XXX think we need to choose between this and LSB
+#else
+        return QImage::Format_MonoLSB;
+#endif
+    }
+
+    return QImage::Format_Mono;
+}
+
+static inline QMatrix
+_qmatrix_from_cairo_matrix (const cairo_matrix_t& m)
+{
+    return QMatrix(m.xx, m.yx, m.xy, m.yy, m.x0, m.y0);
+}
+
+/** Path conversion **/
+typedef struct _qpainter_path_transform {
+    QPainterPath path;
+    const cairo_matrix_t *ctm_inverse;
+} qpainter_path_data;
+
+/* cairo path -> execute in context */
+static cairo_status_t
+_cairo_path_to_qpainterpath_move_to (void *closure, const cairo_point_t *point)
+{
+    qpainter_path_data *pdata = static_cast <qpainter_path_data *> (closure);
+    double x = _cairo_fixed_to_double (point->x);
+    double y = _cairo_fixed_to_double (point->y);
+
+    if (pdata->ctm_inverse)
+        cairo_matrix_transform_point (pdata->ctm_inverse, &x, &y);
+
+    pdata->path.moveTo(x, y);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_path_to_qpainterpath_line_to (void *closure, const cairo_point_t *point)
+{
+    qpainter_path_data *pdata = static_cast <qpainter_path_data *> (closure);
+    double x = _cairo_fixed_to_double (point->x);
+    double y = _cairo_fixed_to_double (point->y);
+
+    if (pdata->ctm_inverse)
+        cairo_matrix_transform_point (pdata->ctm_inverse, &x, &y);
+
+    pdata->path.lineTo(x, y);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_path_to_qpainterpath_curve_to (void *closure, const cairo_point_t *p0, const cairo_point_t *p1, const cairo_point_t *p2)
+{
+    qpainter_path_data *pdata = static_cast <qpainter_path_data *> (closure);
+    double x0 = _cairo_fixed_to_double (p0->x);
+    double y0 = _cairo_fixed_to_double (p0->y);
+    double x1 = _cairo_fixed_to_double (p1->x);
+    double y1 = _cairo_fixed_to_double (p1->y);
+    double x2 = _cairo_fixed_to_double (p2->x);
+    double y2 = _cairo_fixed_to_double (p2->y);
+
+    if (pdata->ctm_inverse) {
+        cairo_matrix_transform_point (pdata->ctm_inverse, &x0, &y0);
+        cairo_matrix_transform_point (pdata->ctm_inverse, &x1, &y1);
+        cairo_matrix_transform_point (pdata->ctm_inverse, &x2, &y2);
+    }
+
+    pdata->path.cubicTo (x0, y0, x1, y1, x2, y2);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_path_to_qpainterpath_close_path (void *closure)
+{
+    qpainter_path_data *pdata = static_cast <qpainter_path_data *> (closure);
+
+    pdata->path.closeSubpath();
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static inline QPainterPath
+path_to_qt (cairo_path_fixed_t *path,
+           const cairo_matrix_t *ctm_inverse = NULL)
+{
+    qpainter_path_data data;
+    cairo_status_t status;
+
+    if (ctm_inverse && _cairo_matrix_is_identity (ctm_inverse))
+       ctm_inverse = NULL;
+    data.ctm_inverse = ctm_inverse;
+
+    status = _cairo_path_fixed_interpret (path,
+                                         _cairo_path_to_qpainterpath_move_to,
+                                         _cairo_path_to_qpainterpath_line_to,
+                                         _cairo_path_to_qpainterpath_curve_to,
+                                         _cairo_path_to_qpainterpath_close_path,
+                                         &data);
+    assert (status == CAIRO_STATUS_SUCCESS);
+
+    return data.path;
+}
+
+static inline QPainterPath
+path_to_qt (cairo_path_fixed_t *path,
+           cairo_fill_rule_t fill_rule,
+           cairo_matrix_t *ctm_inverse = NULL)
+{
+    QPainterPath qpath = path_to_qt (path, ctm_inverse);
+
+    qpath.setFillRule (fill_rule == CAIRO_FILL_RULE_WINDING ?
+                       Qt::WindingFill :
+                       Qt::OddEvenFill);
+
+    return qpath;
+}
+
+/**
+ ** Surface backend methods
+ **/
+static cairo_surface_t *
+_cairo_qt_surface_create_similar (void *abstract_surface,
+                                 cairo_content_t content,
+                                 int width,
+                                 int height)
+{
+    cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface;
+    bool use_pixmap;
+
+    D(fprintf(stderr, "q[%p] create_similar: %d %d [%d] -> ", abstract_surface, width, height, content));
+
+    use_pixmap = qs->image == NULL;
+    if (use_pixmap) {
+       switch (content) {
+       case CAIRO_CONTENT_ALPHA:
+           use_pixmap = FALSE;
+           break;
+       case CAIRO_CONTENT_COLOR:
+           break;
+       case CAIRO_CONTENT_COLOR_ALPHA:
+           use_pixmap = ! _qpixmaps_have_no_alpha;
+           break;
+       }
+    }
+
+    if (use_pixmap) {
+       cairo_surface_t *result =
+           cairo_qt_surface_create_with_qpixmap (content, width, height);
+
+       /* XXX result->content is always content. ??? */
+       if (result->content == content) {
+           D(fprintf(stderr, "qpixmap content: %d\n", content));
+           return result;
+       }
+
+       _qpixmaps_have_no_alpha = TRUE;
+       cairo_surface_destroy (result);
+    }
+
+    D(fprintf (stderr, "qimage\n"));
+    return cairo_qt_surface_create_with_qimage
+       (_cairo_format_from_content (content), width, height);
+}
+
+static cairo_status_t
+_cairo_qt_surface_finish (void *abstract_surface)
+{
+    cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface;
+
+    D(fprintf(stderr, "q[%p] finish\n", abstract_surface));
+
+    /* Only delete p if we created it */
+    if (qs->image || qs->pixmap)
+        delete qs->p;
+    else
+       qs->p->restore ();
+
+    if (qs->image_equiv)
+        cairo_surface_destroy (qs->image_equiv);
+
+    _cairo_surface_clipper_reset (&qs->clipper);
+
+    if (qs->image)
+        delete qs->image;
+
+    if (qs->pixmap)
+        delete qs->pixmap;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_qimg_destroy (void *closure)
+{
+    QImage *qimg = (QImage *) closure;
+    delete qimg;
+}
+
+static cairo_status_t
+_cairo_qt_surface_acquire_source_image (void *abstract_surface,
+                                       cairo_image_surface_t **image_out,
+                                       void **image_extra)
+{
+    cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface;
+
+    D(fprintf(stderr, "q[%p] acquire_source_image\n", abstract_surface));
+
+    *image_extra = NULL;
+
+    if (qs->image_equiv) {
+        *image_out = (cairo_image_surface_t*)
+                     cairo_surface_reference (qs->image_equiv);
+
+        return CAIRO_STATUS_SUCCESS;
+    }
+
+    if (qs->pixmap) {
+        QImage *qimg = new QImage(qs->pixmap->toImage());
+       cairo_surface_t *image;
+       cairo_status_t status;
+
+        image = cairo_image_surface_create_for_data (qimg->bits(),
+                                                    _cairo_format_from_qimage_format (qimg->format()),
+                                                    qimg->width(), qimg->height(),
+                                                    qimg->bytesPerLine());
+
+       status = _cairo_user_data_array_set_data (&image->user_data,
+                                                 (const cairo_user_data_key_t *)&_qimg_destroy,
+                                                 qimg,
+                                                 _qimg_destroy);
+       if (status) {
+           cairo_surface_destroy (image);
+           return status;
+       }
+
+       *image_out = (cairo_image_surface_t *) image;
+        return CAIRO_STATUS_SUCCESS;
+    }
+
+    return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+}
+
+static void
+_cairo_qt_surface_release_source_image (void *abstract_surface,
+                                       cairo_image_surface_t *image,
+                                       void *image_extra)
+{
+    //cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface;
+
+    D(fprintf(stderr, "q[%p] release_source_image\n", abstract_surface));
+
+    cairo_surface_destroy (&image->base);
+}
+
+static cairo_status_t
+_cairo_qt_surface_acquire_dest_image (void *abstract_surface,
+                                     cairo_rectangle_int_t *interest_rect,
+                                     cairo_image_surface_t **image_out,
+                                     cairo_rectangle_int_t *image_rect,
+                                     void **image_extra)
+{
+    cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface;
+    QImage *qimg = NULL;
+
+    D(fprintf(stderr, "q[%p] acquire_dest_image\n", abstract_surface));
+
+    *image_extra = NULL;
+
+    if (qs->image_equiv) {
+        *image_out = (cairo_image_surface_t*)
+                     cairo_surface_reference (qs->image_equiv);
+
+        image_rect->x = qs->window.x();
+        image_rect->y = qs->window.y();
+        image_rect->width = qs->window.width();
+        image_rect->height = qs->window.height();
+
+        return CAIRO_STATUS_SUCCESS;
+    }
+
+    QPoint offset;
+
+    if (qs->pixmap) {
+        qimg = new QImage(qs->pixmap->toImage());
+    } else {
+        // Try to figure out what kind of QPaintDevice we have, and
+        // how we can grab an image from it
+        QPaintDevice *pd = qs->p->device();
+       if (!pd)
+           return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+       QPaintDevice *rpd = QPainter::redirected(pd, &offset);
+       if (rpd)
+           pd = rpd;
+
+        if (pd->devType() == QInternal::Image) {
+            qimg = new QImage(((QImage*) pd)->copy());
+        } else if (pd->devType() == QInternal::Pixmap) {
+            qimg = new QImage(((QPixmap*) pd)->toImage());
+        } else if (pd->devType() == QInternal::Widget) {
+            qimg = new QImage(QPixmap::grabWindow(((QWidget*)pd)->winId()).toImage());
+        }
+    }
+
+    if (qimg == NULL)
+        return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    *image_out = (cairo_image_surface_t*)
+                 cairo_image_surface_create_for_data (qimg->bits(),
+                                                      _cairo_format_from_qimage_format (qimg->format()),
+                                                      qimg->width(), qimg->height(),
+                                                      qimg->bytesPerLine());
+    *image_extra = qimg;
+
+    image_rect->x = qs->window.x() + offset.x();
+    image_rect->y = qs->window.y() + offset.y();
+    image_rect->width = qs->window.width() - offset.x();
+    image_rect->height = qs->window.height() - offset.y();
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_qt_surface_release_dest_image (void *abstract_surface,
+                                     cairo_rectangle_int_t *interest_rect,
+                                     cairo_image_surface_t *image,
+                                     cairo_rectangle_int_t *image_rect,
+                                     void *image_extra)
+{
+    cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface;
+    D(fprintf(stderr, "q[%p] release_dest_image\n", abstract_surface));
+
+    cairo_surface_destroy (&image->base);
+
+    if (image_extra) {
+        QImage *qimg = (QImage*) image_extra;
+
+        // XXX should I be using setBackgroundMode here instead of setCompositionMode?
+        if (qs->supports_porter_duff)
+            qs->p->setCompositionMode (QPainter::CompositionMode_Source);
+
+        qs->p->drawImage (image_rect->x, image_rect->y, *qimg);
+
+        if (qs->supports_porter_duff)
+            qs->p->setCompositionMode (QPainter::CompositionMode_SourceOver);
+
+        delete qimg;
+    }
+}
+
+static cairo_status_t
+_cairo_qt_surface_clone_similar (void *abstract_surface,
+                                cairo_surface_t *src,
+                                int              src_x,
+                                int              src_y,
+                                int              width,
+                                int              height,
+                                int              *clone_offset_x,
+                                int              *clone_offset_y,
+                                cairo_surface_t **clone_out)
+{
+    cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface;
+
+    if (src->backend == qs->base.backend) {
+       *clone_offset_x = 0;
+       *clone_offset_y = 0;
+       *clone_out = cairo_surface_reference (src);
+       return CAIRO_STATUS_SUCCESS;
+    }
+
+    return (cairo_status_t) CAIRO_INT_STATUS_UNSUPPORTED;
+}
+
+static cairo_bool_t
+_cairo_qt_surface_get_extents (void *abstract_surface,
+                              cairo_rectangle_int_t *extents)
+{
+    cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface;
+
+    extents->x = qs->window.x();
+    extents->y = qs->window.y();
+    extents->width  = qs->window.width();
+    extents->height = qs->window.height();
+
+    return TRUE;
+}
+
+static cairo_status_t
+_cairo_qt_surface_clipper_intersect_clip_path (cairo_surface_clipper_t *clipper,
+                                              cairo_path_fixed_t *path,
+                                              cairo_fill_rule_t fill_rule,
+                                              double tolerance,
+                                              cairo_antialias_t antialias)
+{
+    cairo_qt_surface_t *qs = cairo_container_of (clipper,
+                                                cairo_qt_surface_t,
+                                                clipper);
+
+    if (path == NULL) {
+        if (qs->pixmap || qs->image) {
+            // we own p
+            qs->p->setClipping (false);
+        } else {
+            qs->p->restore ();
+            qs->p->save ();
+        }
+    } else {
+       // XXX Antialiasing is ignored
+       qs->p->setClipPath (path_to_qt (path, fill_rule), Qt::IntersectClip);
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_qt_surface_set_clip_region (cairo_qt_surface_t *qs,
+                                  cairo_region_t *clip_region)
+{
+    _cairo_surface_clipper_reset (&qs->clipper);
+
+    if (clip_region == NULL) {
+        // How the clip path is reset depends on whether we own p or not
+        if (qs->pixmap || qs->image) {
+            // we own p
+            qs->p->setClipping (false);
+        } else {
+            qs->p->restore ();
+            qs->p->save ();
+        }
+    } else {
+       QRegion qr;
+       int num_rects = cairo_region_num_rectangles (clip_region);
+       for (int i = 0; i < num_rects; ++i) {
+           cairo_rectangle_int_t rect;
+
+           cairo_region_get_rectangle (clip_region, i, &rect);
+
+           QRect r(rect.x, rect.y, rect.width, rect.height);
+           qr = qr.unite(r);
+       }
+
+       qs->p->setClipRegion (qr, Qt::IntersectClip);
+    }
+}
+
+static cairo_int_status_t
+_cairo_qt_surface_set_clip (cairo_qt_surface_t *qs,
+                           cairo_clip_t *clip)
+{
+    cairo_int_status_t status;
+
+    D(fprintf(stderr, "q[%p] intersect_clip_path %s\n", abstract_surface, path ? "(path)" : "(clear)"));
+
+    if (clip == NULL) {
+       _cairo_surface_clipper_reset (&qs->clipper);
+        // How the clip path is reset depends on whether we own p or not
+        if (qs->pixmap || qs->image) {
+            // we own p
+            qs->p->setClipping (false);
+        } else {
+            qs->p->restore ();
+            qs->p->save ();
+        }
+
+        return CAIRO_INT_STATUS_SUCCESS;
+    }
+
+#if ENABLE_FAST_CLIP
+    // Qt will implicitly enable clipping, and will use ReplaceClip
+    // instead of IntersectClip if clipping was disabled before
+
+    // Note: Qt is really bad at dealing with clip paths.  It doesn't
+    // seem to usefully recognize rectangular paths, instead going down
+    // extremely slow paths whenever a clip path is set.  So,
+    // we do a bunch of work here to try to get rectangles or regions
+    // down to Qt for clipping.
+
+    cairo_region_t *clip_region = NULL;
+
+    status = _cairo_clip_get_region (clip, &clip_region);
+    if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
+       // We weren't able to extract a region from the traps.
+       // Just hand the path down to QPainter.
+       status = (cairo_int_status_t)
+           _cairo_surface_clipper_set_clip (&qs->clipper, clip);
+    } else if (status == CAIRO_INT_STATUS_SUCCESS) {
+       _cairo_qt_surface_set_clip_region (qs, clip_region);
+       status = CAIRO_INT_STATUS_SUCCESS;
+    }
+#else
+    status = (cairo_int_status_t)
+       _cairo_surface_clipper_set_clip (&qs->clipper, clip);
+#endif
+
+    return status;
+}
+
+/**
+ ** Brush conversion
+ **/
+
+struct PatternToBrushConverter {
+    PatternToBrushConverter (const cairo_pattern_t *pattern) :
+       mAcquiredImageParent(0),
+       mAcquiredImage(0),
+       mAcquiredImageExtra(0)
+    {
+       if (pattern->type == CAIRO_PATTERN_TYPE_SOLID) {
+           cairo_solid_pattern_t *solid = (cairo_solid_pattern_t*) pattern;
+           QColor color;
+           color.setRgbF(solid->color.red,
+                         solid->color.green,
+                         solid->color.blue,
+                         solid->color.alpha);
+
+           mBrush = QBrush(color);
+       } else if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) {
+           cairo_surface_pattern_t *spattern = (cairo_surface_pattern_t*) pattern;
+           cairo_surface_t *surface = spattern->surface;
+
+           if (surface->type == CAIRO_SURFACE_TYPE_QT) {
+               cairo_qt_surface_t *qs = (cairo_qt_surface_t*) surface;
+
+               if (qs->image) {
+                   mBrush = QBrush(*qs->image);
+               } else if (qs->pixmap) {
+                   mBrush = QBrush(*qs->pixmap);
+               } else {
+                   // do something smart
+                   mBrush = QBrush(0xff0000ff);
+               }
+           } else {
+               cairo_image_surface_t *isurf = NULL;
+
+               if (surface->type == CAIRO_SURFACE_TYPE_IMAGE) {
+                   isurf = (cairo_image_surface_t*) surface;
+               } else {
+                   void *image_extra;
+
+                   if (_cairo_surface_acquire_source_image (surface, &isurf, &image_extra) == CAIRO_STATUS_SUCCESS) {
+                       mAcquiredImageParent = surface;
+                       mAcquiredImage = isurf;
+                       mAcquiredImageExtra = image_extra;
+                   } else {
+                       isurf = NULL;
+                   }
+               }
+
+               if (isurf) {
+                   mBrush = QBrush (QImage ((const uchar *) isurf->data,
+                                                isurf->width,
+                                                isurf->height,
+                                                isurf->stride,
+                                                _qimage_format_from_cairo_format (isurf->format)));
+               } else {
+                   mBrush = QBrush(0x0000ffff);
+               }
+           }
+       } else if (pattern->type == CAIRO_PATTERN_TYPE_LINEAR ||
+                  pattern->type == CAIRO_PATTERN_TYPE_RADIAL)
+       {
+           QGradient *grad;
+           cairo_bool_t reverse_stops = FALSE;
+           cairo_bool_t emulate_reflect = FALSE;
+           double offset = 0.0;
+
+           cairo_extend_t extend = pattern->extend;
+
+           cairo_gradient_pattern_t *gpat = (cairo_gradient_pattern_t *) pattern;
+
+           if (pattern->type == CAIRO_PATTERN_TYPE_LINEAR) {
+               cairo_linear_pattern_t *lpat = (cairo_linear_pattern_t *) pattern;
+               grad = new QLinearGradient (lpat->pd1.x, lpat->pd1.y,
+                                           lpat->pd2.x, lpat->pd2.y);
+           } else if (pattern->type == CAIRO_PATTERN_TYPE_RADIAL) {
+               cairo_radial_pattern_t *rpat = (cairo_radial_pattern_t *) pattern;
+
+               /* Based on the SVG surface code */
+
+               cairo_circle_double_t *c0, *c1;
+               double x0, y0, r0, x1, y1, r1;
+
+               if (rpat->cd1.radius < rpat->cd2.radius) {
+                   c0 = &rpat->cd1;
+                   c1 = &rpat->cd2;
+                   reverse_stops = FALSE;
+               } else {
+                   c0 = &rpat->cd2;
+                   c1 = &rpat->cd1;
+                   reverse_stops = TRUE;
+               }
+
+               x0 = c0->center.x;
+               y0 = c0->center.y;
+               r0 = c0->radius;
+               x1 = c1->center.x;
+               y1 = c1->center.y;
+               r1 = c1->radius;
+
+               if (r0 == r1) {
+                   grad = new QRadialGradient (x1, y1, r1, x1, y1);
+               } else {
+                   double fx = (r1 * x0 - r0 * x1) / (r1 - r0);
+                   double fy = (r1 * y0 - r0 * y1) / (r1 - r0);
+
+                   /* QPainter doesn't support the inner circle and use instead a gradient focal.
+                    * That means we need to emulate the cairo behaviour by processing the
+                    * cairo gradient stops.
+                    * The CAIRO_EXTENT_NONE and CAIRO_EXTENT_PAD modes are quite easy to handle,
+                    * it's just a matter of stop position translation and calculation of
+                    * the corresponding SVG radial gradient focal.
+                    * The CAIRO_EXTENT_REFLECT and CAIRO_EXTEND_REPEAT modes require to compute a new
+                    * radial gradient, with an new outer circle, equal to r1 - r0 in the CAIRO_EXTEND_REPEAT
+                    * case, and 2 * (r1 - r0) in the CAIRO_EXTENT_REFLECT case, and a new gradient stop
+                    * list that maps to the original cairo stop list.
+                    */
+                   if ((extend == CAIRO_EXTEND_REFLECT || extend == CAIRO_EXTEND_REPEAT) && r0 > 0.0) {
+                       double r_org = r1;
+                       double r, x, y;
+
+                       if (extend == CAIRO_EXTEND_REFLECT) {
+                           r1 = 2 * r1 - r0;
+                           emulate_reflect = TRUE;
+                       }
+
+                       offset = fmod (r1, r1 - r0) / (r1 - r0) - 1.0;
+                       r = r1 - r0;
+
+                       /* New position of outer circle. */
+                       x = r * (x1 - fx) / r_org + fx;
+                       y = r * (y1 - fy) / r_org + fy;
+
+                       x1 = x;
+                       y1 = y;
+                       r1 = r;
+                       r0 = 0.0;
+                   } else {
+                       offset = r0 / r1;
+                   }
+
+                   grad = new QRadialGradient (x1, y1, r1, fx, fy);
+
+                   if (extend == CAIRO_EXTEND_NONE && r0 != 0.0)
+                       grad->setColorAt (r0 / r1, Qt::transparent);
+               }
+           }
+
+           switch (extend) {
+               case CAIRO_EXTEND_NONE:
+               case CAIRO_EXTEND_PAD:
+                   grad->setSpread(QGradient::PadSpread);
+
+                   grad->setColorAt (0.0, Qt::transparent);
+                   grad->setColorAt (1.0, Qt::transparent);
+                   break;
+
+               case CAIRO_EXTEND_REFLECT:
+                   grad->setSpread(QGradient::ReflectSpread);
+                   break;
+
+               case CAIRO_EXTEND_REPEAT:
+                   grad->setSpread(QGradient::RepeatSpread);
+                   break;
+           }
+
+           for (unsigned int i = 0; i < gpat->n_stops; i++) {
+               int index = i;
+               if (reverse_stops)
+                   index = gpat->n_stops - i - 1;
+
+               double offset = gpat->stops[i].offset;
+               QColor color;
+               color.setRgbF (gpat->stops[i].color.red,
+                              gpat->stops[i].color.green,
+                              gpat->stops[i].color.blue,
+                              gpat->stops[i].color.alpha);
+
+               if (emulate_reflect) {
+                   offset = offset / 2.0;
+                   grad->setColorAt (1.0 - offset, color);
+               }
+
+               grad->setColorAt (offset, color);
+           }
+
+           mBrush = QBrush(*grad);
+
+           delete grad;
+       }
+
+       if (mBrush.style() != Qt::NoBrush  &&
+            pattern->type != CAIRO_PATTERN_TYPE_SOLID &&
+            ! _cairo_matrix_is_identity (&pattern->matrix))
+       {
+           cairo_matrix_t pm = pattern->matrix;
+           cairo_status_t status = cairo_matrix_invert (&pm);
+           assert (status == CAIRO_STATUS_SUCCESS);
+           mBrush.setMatrix (_qmatrix_from_cairo_matrix (pm));
+       }
+    }
+
+    ~PatternToBrushConverter () {
+       if (mAcquiredImageParent)
+           _cairo_surface_release_source_image (mAcquiredImageParent, mAcquiredImage, mAcquiredImageExtra);
+    }
+
+    operator QBrush& () {
+       return mBrush;
+    }
+
+    QBrush mBrush;
+
+    private:
+    cairo_surface_t *mAcquiredImageParent;
+    cairo_image_surface_t *mAcquiredImage;
+    void *mAcquiredImageExtra;
+};
+
+struct PatternToPenConverter {
+    PatternToPenConverter (const cairo_pattern_t *source,
+                           const cairo_stroke_style_t *style) :
+        mBrushConverter(source)
+    {
+        Qt::PenJoinStyle join = Qt::MiterJoin;
+        Qt::PenCapStyle cap = Qt::SquareCap;
+
+        switch (style->line_cap) {
+        case CAIRO_LINE_CAP_BUTT:
+            cap = Qt::FlatCap;
+            break;
+        case CAIRO_LINE_CAP_ROUND:
+            cap = Qt::RoundCap;
+            break;
+        case CAIRO_LINE_CAP_SQUARE:
+            cap = Qt::SquareCap;
+            break;
+        }
+
+        switch (style->line_join) {
+        case CAIRO_LINE_JOIN_MITER:
+            join = Qt::MiterJoin;
+            break;
+        case CAIRO_LINE_JOIN_ROUND:
+            join = Qt::RoundJoin;
+            break;
+        case CAIRO_LINE_JOIN_BEVEL:
+            join = Qt::BevelJoin;
+            break;
+        }
+
+        mPen = QPen(mBrushConverter, style->line_width, Qt::SolidLine, cap, join);
+        mPen.setMiterLimit (style->miter_limit);
+
+        if (style->dash && style->num_dashes) {
+            Qt::PenStyle pstyle = Qt::NoPen;
+
+            if (style->num_dashes == 2) {
+                if ((style->dash[0] == style->line_width &&
+                        style->dash[1] == style->line_width && style->line_width <= 2.0) ||
+                    (style->dash[0] == 0.0 &&
+                        style->dash[1] == style->line_width * 2 && cap == Qt::RoundCap))
+                {
+                    pstyle = Qt::DotLine;
+                } else if (style->dash[0] == style->line_width * DASH_LENGTH &&
+                           style->dash[1] == style->line_width * DASH_LENGTH &&
+                           cap == Qt::FlatCap)
+                {
+                    pstyle = Qt::DashLine;
+                }
+            }
+
+            if (pstyle != Qt::NoPen) {
+                mPen.setStyle(pstyle);
+                return;
+            }
+
+            unsigned int odd_dash = style->num_dashes % 2;
+
+            QVector<qreal> dashes (odd_dash ? style->num_dashes * 2 : style->num_dashes);
+            for (unsigned int i = 0; i < odd_dash+1; i++) {
+                for (unsigned int j = 0; j < style->num_dashes; j++) {
+                    // In Qt, the dash lengths are given in units of line width, whereas
+                    // in cairo, they are in user-space units.  We'll always apply the CTM,
+                    // so all we have to do here is divide cairo's dash lengths by the line
+                    // width.
+                    dashes.append (style->dash[j] / style->line_width);
+                }
+            }
+
+            mPen.setDashPattern(dashes);
+            mPen.setDashOffset(style->dash_offset / style->line_width);
+        }
+    }
+
+    ~PatternToPenConverter() { }
+
+    operator QPen& () {
+        return mPen;
+    }
+
+    QPen mPen;
+    PatternToBrushConverter mBrushConverter;
+};
+
+/**
+ ** Core drawing operations
+ **/
+
+static bool
+_cairo_qt_fast_fill (cairo_qt_surface_t *qs,
+                    const cairo_pattern_t *source,
+                    cairo_path_fixed_t *path = NULL,
+                    cairo_fill_rule_t fill_rule = CAIRO_FILL_RULE_WINDING,
+                    double tolerance = 0.0,
+                    cairo_antialias_t antialias = CAIRO_ANTIALIAS_NONE)
+{
+#if ENABLE_FAST_FILL
+    QImage *qsSrc_image = NULL;
+    QPixmap *qsSrc_pixmap = NULL;
+    std::auto_ptr<QImage> qsSrc_image_d;
+
+
+    if (source->type == CAIRO_PATTERN_TYPE_SURFACE) {
+       cairo_surface_pattern_t *spattern = (cairo_surface_pattern_t*) source;
+       if (spattern->surface->type == CAIRO_SURFACE_TYPE_QT) {
+           cairo_qt_surface_t *p = (cairo_qt_surface_t*) spattern->surface;
+
+           qsSrc_image = p->image;
+           qsSrc_pixmap = p->pixmap;
+       } else if (spattern->surface->type == CAIRO_SURFACE_TYPE_IMAGE) {
+           cairo_image_surface_t *p = (cairo_image_surface_t*) spattern->surface;
+           qsSrc_image = new QImage((const uchar*) p->data,
+                                    p->width,
+                                    p->height,
+                                    p->stride,
+                                    _qimage_format_from_cairo_format(p->format));
+           qsSrc_image_d.reset(qsSrc_image);
+       }
+    }
+
+    if (!qsSrc_image && !qsSrc_pixmap)
+       return false;
+
+    // We can only drawTiledPixmap; there's no drawTiledImage
+    if (! qsSrc_pixmap &&
+       (source->extend == CAIRO_EXTEND_REPEAT ||
+        source->extend == CAIRO_EXTEND_REFLECT))
+    {
+       return false;
+    }
+
+    QMatrix sourceMatrix = _qmatrix_from_cairo_matrix (source->matrix);
+
+    // We can draw this faster by clipping and calling drawImage/drawPixmap.
+    // Use our own clipping function so that we can get the
+    // region handling to end up with the fastest possible clip.
+    //
+    // XXX Antialiasing will fail pretty hard here, since we can't clip with AA
+    // with QPainter.
+    qs->p->save();
+
+    if (path) {
+       cairo_int_status_t status;
+
+       cairo_clip_t clip, old_clip = qs->clipper.clip;
+
+       _cairo_clip_init_copy (&clip, &qs->clipper.clip);
+       status = (cairo_int_status_t) _cairo_clip_clip (&clip,
+                                                       path,
+                                                       fill_rule,
+                                                       tolerance,
+                                                       antialias);
+       if (unlikely (status)) {
+           qs->p->restore();
+           return false;
+       }
+
+       status = _cairo_qt_surface_set_clip (qs, &clip);
+       if (unlikely (status)) {
+           qs->p->restore();
+           return false;
+       }
+
+       _cairo_clip_reset (&clip);
+       qs->clipper.clip = old_clip;
+    }
+
+    qs->p->setWorldMatrix (sourceMatrix.inverted(), true);
+
+    switch (source->extend) {
+    case CAIRO_EXTEND_REPEAT:
+    // XXX handle reflect by tiling 4 times first
+    case CAIRO_EXTEND_REFLECT: {
+            assert (qsSrc_pixmap);
+
+            // Render the tiling to cover the entire destination window (because
+            // it'll be clipped).  Transform the window rect by the inverse
+            // of the current world transform so that the device coordinates
+            // end up as the right thing.
+            QRectF dest = qs->p->worldTransform().inverted().mapRect(QRectF(qs->window));
+            QPointF origin = sourceMatrix.map(QPointF(0.0, 0.0));
+
+            qs->p->drawTiledPixmap (dest, *qsSrc_pixmap, origin);
+        }
+        break;
+    case CAIRO_EXTEND_NONE:
+    case CAIRO_EXTEND_PAD: // XXX not exactly right, but good enough
+    default:
+        if (qsSrc_image)
+            qs->p->drawImage (0, 0, *qsSrc_image);
+        else if (qsSrc_pixmap)
+            qs->p->drawPixmap (0, 0, *qsSrc_pixmap);
+        break;
+    }
+
+    qs->p->restore();
+
+    return true;
+#else
+    return false;
+#endif
+}
+
+static cairo_int_status_t
+_cairo_qt_surface_paint (void *abstract_surface,
+                        cairo_operator_t op,
+                        const cairo_pattern_t *source,
+                        cairo_clip_t          *clip)
+{
+    cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface;
+    cairo_int_status_t status;
+
+    D(fprintf(stderr, "q[%p] paint op:%s\n", abstract_surface, _opstr(op)));
+
+    if (!qs->p)
+        return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    if (! _op_is_supported (qs, op))
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    status = _cairo_qt_surface_set_clip (qs, clip);
+    if (unlikely (status))
+       return status;
+
+    if (qs->supports_porter_duff)
+        qs->p->setCompositionMode (_qpainter_compositionmode_from_cairo_op (op));
+
+    if (! _cairo_qt_fast_fill (qs, source)) {
+       PatternToBrushConverter brush (source);
+        qs->p->fillRect (qs->window, brush);
+    }
+
+    if (qs->supports_porter_duff)
+        qs->p->setCompositionMode (QPainter::CompositionMode_SourceOver);
+
+    return CAIRO_INT_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_qt_surface_fill (void *abstract_surface,
+                       cairo_operator_t op,
+                       const cairo_pattern_t *source,
+                       cairo_path_fixed_t *path,
+                       cairo_fill_rule_t fill_rule,
+                       double tolerance,
+                       cairo_antialias_t antialias,
+                       cairo_clip_t *clip)
+{
+    cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface;
+
+    D(fprintf(stderr, "q[%p] fill op:%s\n", abstract_surface, _opstr(op)));
+
+    if (!qs->p)
+        return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    if (! _op_is_supported (qs, op))
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    cairo_int_status_t status = _cairo_qt_surface_set_clip (qs, clip);
+    if (unlikely (status))
+       return status;
+
+    if (qs->supports_porter_duff)
+        qs->p->setCompositionMode (_qpainter_compositionmode_from_cairo_op (op));
+
+    // XXX Qt4.3, 4.4 misrenders some complex paths if antialiasing is
+    // enabled
+    //qs->p->setRenderHint (QPainter::Antialiasing, antialias == CAIRO_ANTIALIAS_NONE ? false : true);
+    qs->p->setRenderHint (QPainter::SmoothPixmapTransform, source->filter != CAIRO_FILTER_FAST);
+
+    if (! _cairo_qt_fast_fill (qs, source,
+                              path, fill_rule, tolerance, antialias))
+    {
+       PatternToBrushConverter brush(source);
+       qs->p->fillPath (path_to_qt (path, fill_rule), brush);
+    }
+
+    if (qs->supports_porter_duff)
+        qs->p->setCompositionMode (QPainter::CompositionMode_SourceOver);
+
+    return CAIRO_INT_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_qt_surface_stroke (void *abstract_surface,
+                         cairo_operator_t op,
+                         const cairo_pattern_t *source,
+                         cairo_path_fixed_t *path,
+                         const cairo_stroke_style_t *style,
+                         const cairo_matrix_t *ctm,
+                         const cairo_matrix_t *ctm_inverse,
+                         double tolerance,
+                         cairo_antialias_t antialias,
+                         cairo_clip_t *clip)
+{
+    cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface;
+
+    D(fprintf(stderr, "q[%p] stroke op:%s\n", abstract_surface, _opstr(op)));
+
+    if (!qs->p)
+        return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    if (! _op_is_supported (qs, op))
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    cairo_int_status_t int_status = _cairo_qt_surface_set_clip (qs, clip);
+    if (unlikely (int_status))
+       return int_status;
+
+
+    QMatrix savedMatrix = qs->p->worldMatrix();
+
+    if (qs->supports_porter_duff)
+        qs->p->setCompositionMode (_qpainter_compositionmode_from_cairo_op (op));
+
+    qs->p->setWorldMatrix (_qmatrix_from_cairo_matrix (*ctm), true);
+    // XXX Qt4.3, 4.4 misrenders some complex paths if antialiasing is
+    // enabled
+    //qs->p->setRenderHint (QPainter::Antialiasing, antialias == CAIRO_ANTIALIAS_NONE ? false : true);
+    qs->p->setRenderHint (QPainter::SmoothPixmapTransform, source->filter != CAIRO_FILTER_FAST);
+
+    PatternToPenConverter pen(source, style);
+
+    qs->p->setPen(pen);
+    qs->p->drawPath(path_to_qt (path, ctm_inverse));
+    qs->p->setPen(Qt::black);
+
+    qs->p->setWorldMatrix (savedMatrix, false);
+
+    if (qs->supports_porter_duff)
+        qs->p->setCompositionMode (QPainter::CompositionMode_SourceOver);
+
+    return CAIRO_INT_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_qt_surface_show_glyphs (void *abstract_surface,
+                              cairo_operator_t op,
+                              const cairo_pattern_t *source,
+                              cairo_glyph_t *glyphs,
+                              int num_glyphs,
+                              cairo_scaled_font_t *scaled_font,
+                              cairo_clip_t *clip,
+                              int *remaining_glyphs)
+{
+#if (QT_VERSION >= QT_VERSION_CHECK(4, 7, 0)) || defined(QT_GLYPHS_API_BACKPORT)
+    cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface;
+
+    // pick out the colour to use from the cairo source
+    cairo_solid_pattern_t *solid = (cairo_solid_pattern_t*) source;
+    cairo_scaled_glyph_t* glyph;
+    // documentation says you have to freeze the cache, but I don't believe it
+    _cairo_scaled_font_freeze_cache(scaled_font);
+
+    QColor tempColour(solid->color.red * 255, solid->color.green * 255, solid->color.blue * 255);
+    QVarLengthArray<QPointF> positions(num_glyphs);
+    QVarLengthArray<unsigned int> glyphss(num_glyphs);
+    FT_Face face = cairo_ft_scaled_font_lock_face (scaled_font);
+    const FT_Size_Metrics& ftMetrics = face->size->metrics;
+    QFont font(face->family_name);
+    font.setStyleStrategy(QFont::NoFontMerging);
+    font.setBold(face->style_flags & FT_STYLE_FLAG_BOLD);
+    font.setItalic(face->style_flags & FT_STYLE_FLAG_ITALIC);
+    font.setKerning(face->face_flags & FT_FACE_FLAG_KERNING);
+    font.setPixelSize(ftMetrics.y_ppem);
+    cairo_ft_scaled_font_unlock_face(scaled_font);
+    qs->p->setFont(font);
+    qs->p->setPen(tempColour);
+    for (int currentGlyph = 0; currentGlyph < num_glyphs; currentGlyph++) {
+        positions[currentGlyph].setX(glyphs[currentGlyph].x);
+        positions[currentGlyph].setY(glyphs[currentGlyph].y);
+        glyphss[currentGlyph] = glyphs[currentGlyph].index;
+    }
+    qt_draw_glyphs(qs->p, glyphss.data(), positions.data(), num_glyphs);
+    _cairo_scaled_font_thaw_cache(scaled_font);
+    return CAIRO_INT_STATUS_SUCCESS;
+#else
+    return CAIRO_INT_STATUS_UNSUPPORTED;
+#endif
+}
+
+static cairo_int_status_t
+_cairo_qt_surface_mask (void *abstract_surface,
+                       cairo_operator_t op,
+                       const cairo_pattern_t *source,
+                       const cairo_pattern_t *mask,
+                       cairo_clip_t        *clip)
+{
+    cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface;
+
+    D(fprintf(stderr, "q[%p] mask op:%s\n", abstract_surface, _opstr(op)));
+
+    if (!qs->p)
+        return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    if (mask->type == CAIRO_PATTERN_TYPE_SOLID) {
+        cairo_solid_pattern_t *solid_mask = (cairo_solid_pattern_t *) mask;
+        cairo_int_status_t result;
+
+        qs->p->setOpacity (solid_mask->color.alpha);
+
+        result = _cairo_qt_surface_paint (abstract_surface, op, source, clip);
+
+        qs->p->setOpacity (1.0);
+
+        return result;
+    }
+
+    // otherwise skip for now
+    return CAIRO_INT_STATUS_UNSUPPORTED;
+}
+
+static cairo_int_status_t
+_cairo_qt_surface_composite (cairo_operator_t op,
+                            const cairo_pattern_t *pattern,
+                            const cairo_pattern_t *mask_pattern,
+                            void *abstract_surface,
+                            int src_x,
+                            int src_y,
+                            int mask_x,
+                            int mask_y,
+                            int dst_x,
+                            int dst_y,
+                            unsigned int width,
+                            unsigned int height,
+                            cairo_region_t *clip_region)
+{
+    cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface;
+
+    if (mask_pattern)
+        return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    if (! _op_is_supported (qs, op))
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    _cairo_qt_surface_set_clip_region (qs, clip_region);
+
+    D(fprintf(stderr, "q[%p] composite op:%s src:%p [%d %d] dst [%d %d] dim [%d %d]\n",
+              abstract_surface, _opstr(op), (void*)pattern,
+              src_x, src_y, dst_x, dst_y, width, height));
+
+    if (pattern->type == CAIRO_PATTERN_TYPE_SOLID) {
+        cairo_solid_pattern_t *solid = (cairo_solid_pattern_t*) pattern;
+
+        QColor color;
+        color.setRgbF(solid->color.red,
+                      solid->color.green,
+                      solid->color.blue,
+                      solid->color.alpha);
+
+        if (qs->supports_porter_duff)
+            qs->p->setCompositionMode (_qpainter_compositionmode_from_cairo_op (op));
+
+        qs->p->fillRect (dst_x, dst_y, width, height, color);
+
+        if (qs->supports_porter_duff)
+            qs->p->setCompositionMode (QPainter::CompositionMode_SourceOver);
+    } else if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) {
+        cairo_surface_pattern_t *spattern = (cairo_surface_pattern_t*) pattern;
+        cairo_surface_t *surface = spattern->surface;
+
+        QImage *qimg = NULL;
+        QPixmap *qpixmap = NULL;
+        std::auto_ptr<QImage> qimg_d;
+
+        if (surface->type == CAIRO_SURFACE_TYPE_IMAGE) {
+            cairo_image_surface_t *isurf = (cairo_image_surface_t*) surface;
+            qimg = new QImage ((const uchar *) isurf->data,
+                               isurf->width,
+                               isurf->height,
+                               isurf->stride,
+                               _qimage_format_from_cairo_format (isurf->format));
+            qimg_d.reset(qimg);
+        }
+
+        if (surface->type == CAIRO_SURFACE_TYPE_QT) {
+            cairo_qt_surface_t *qsrc = (cairo_qt_surface_t*) surface;
+
+            if (qsrc->image)
+                qimg = qsrc->image;
+            else if (qsrc->pixmap)
+                qpixmap = qsrc->pixmap;
+        }
+
+        if (!qimg && !qpixmap)
+            return CAIRO_INT_STATUS_UNSUPPORTED;
+
+        QMatrix savedMatrix = qs->p->worldMatrix();
+        if (! _cairo_matrix_is_identity (&pattern->matrix)) {
+            cairo_matrix_t pm = pattern->matrix;
+           cairo_status_t status;
+
+            status = cairo_matrix_invert (&pm);
+           assert (status == CAIRO_STATUS_SUCCESS);
+           qs->p->setWorldMatrix(_qmatrix_from_cairo_matrix (pm), true);
+        }
+
+        if (qs->supports_porter_duff)
+            qs->p->setCompositionMode (_qpainter_compositionmode_from_cairo_op (op));
+
+        if (qimg)
+            qs->p->drawImage (dst_x, dst_y, *qimg, src_x, src_y, width, height);
+        else if (qpixmap)
+            qs->p->drawPixmap (dst_x, dst_y, *qpixmap, src_x, src_y, width, height);
+
+        if (qs->supports_porter_duff)
+            qs->p->setCompositionMode (QPainter::CompositionMode_SourceOver);
+    } else {
+        return CAIRO_INT_STATUS_UNSUPPORTED;
+    }
+
+    return CAIRO_INT_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_qt_surface_mark_dirty (void *abstract_surface,
+                             int x, int y,
+                             int width, int height)
+{
+    cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface;
+
+    if (qs->p && !(qs->image || qs->pixmap))
+       qs->p->save ();
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+/**
+ ** Backend struct
+ **/
+
+static const cairo_surface_backend_t cairo_qt_surface_backend = {
+    CAIRO_SURFACE_TYPE_QT,
+    _cairo_qt_surface_finish,
+
+    _cairo_default_context_create,
+
+    _cairo_qt_surface_create_similar,
+    NULL, /* similar image */
+    NULL, /* map to image */
+    NULL, /* unmap image */
+
+    _cairo_qt_surface_acquire_source_image,
+    _cairo_qt_surface_release_source_image,
+    _cairo_qt_surface_acquire_dest_image,
+    _cairo_qt_surface_release_dest_image,
+    _cairo_qt_surface_clone_similar,
+
+    _cairo_qt_surface_composite,
+    NULL, /* fill_rectangles */
+    NULL, /* composite_trapezoids */
+    NULL, /* create_span_renderer */
+    NULL, /* check_span_renderer */
+    NULL, /* copy_page */
+    NULL, /* show_page */
+    _cairo_qt_surface_get_extents,
+    NULL, /* old_show_glyphs */
+    NULL, /* get_font_options */
+    NULL, /* flush */
+    _cairo_qt_surface_mark_dirty,
+    NULL, /* scaled_font_fini */
+    NULL, /* scaled_glyph_fini */
+
+    _cairo_qt_surface_paint,
+    _cairo_qt_surface_mask,
+    _cairo_qt_surface_stroke,
+    _cairo_qt_surface_fill,
+    _cairo_qt_surface_show_glyphs,
+
+    NULL, /* snapshot */
+    NULL, /* is_similar */
+    NULL, /* fill_stroke */
+    NULL, /* create_solid_pattern_surface */
+    NULL, /* can_repaint_solid_pattern_surface */
+    NULL, /* has_show_text_glyphs */
+    NULL, /* show_text_glyphs */
+};
+
+cairo_surface_t *
+cairo_qt_surface_create (QPainter *painter)
+{
+    cairo_qt_surface_t *qs;
+
+    qs = (cairo_qt_surface_t *) malloc (sizeof(cairo_qt_surface_t));
+    if (qs == NULL)
+        return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+    memset (qs, 0, sizeof(cairo_qt_surface_t));
+
+    _cairo_surface_init (&qs->base,
+                        &cairo_qt_surface_backend,
+                        NULL, /* device */
+                        CAIRO_CONTENT_COLOR_ALPHA);
+
+    _cairo_surface_clipper_init (&qs->clipper,
+                                _cairo_qt_surface_clipper_intersect_clip_path);
+
+    qs->p = painter;
+    if (qs->p->paintEngine())
+        qs->supports_porter_duff = qs->p->paintEngine()->hasFeature(QPaintEngine::PorterDuff);
+    else
+        qs->supports_porter_duff = FALSE;
+
+    // Save so that we can always get back to the original state
+    qs->p->save();
+
+    qs->window = painter->window();
+
+    D(fprintf(stderr, "qpainter_surface_create: window: [%d %d %d %d] pd:%d\n",
+              qs->window.x(), qs->window.y(), qs->window.width(), qs->window.height(),
+              qs->supports_porter_duff));
+
+    return &qs->base;
+}
+
+cairo_surface_t *
+cairo_qt_surface_create_with_qimage (cairo_format_t format,
+                                    int width,
+                                    int height)
+{
+    cairo_qt_surface_t *qs;
+
+    qs = (cairo_qt_surface_t *) malloc (sizeof(cairo_qt_surface_t));
+    if (qs == NULL)
+        return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+    memset (qs, 0, sizeof(cairo_qt_surface_t));
+
+    _cairo_surface_init (&qs->base,
+                        &cairo_qt_surface_backend,
+                        NULL, /* device */
+                        _cairo_content_from_format (format));
+
+    _cairo_surface_clipper_init (&qs->clipper,
+                                _cairo_qt_surface_clipper_intersect_clip_path);
+
+
+    QImage *image = new QImage (width, height,
+                               _qimage_format_from_cairo_format (format));
+
+    qs->image = image;
+
+    if (!image->isNull()) {
+        qs->p = new QPainter(image);
+        qs->supports_porter_duff = qs->p->paintEngine()->hasFeature(QPaintEngine::PorterDuff);
+    }
+
+    qs->image_equiv = cairo_image_surface_create_for_data (image->bits(),
+                                                           format,
+                                                           width, height,
+                                                           image->bytesPerLine());
+
+    qs->window = QRect(0, 0, width, height);
+
+    D(fprintf(stderr, "qpainter_surface_create: qimage: [%d %d %d %d] pd:%d\n",
+              qs->window.x(), qs->window.y(), qs->window.width(), qs->window.height(),
+              qs->supports_porter_duff));
+
+    return &qs->base;
+}
+
+cairo_surface_t *
+cairo_qt_surface_create_with_qpixmap (cairo_content_t content,
+                                     int width,
+                                     int height)
+{
+    cairo_qt_surface_t *qs;
+
+    if ((content & CAIRO_CONTENT_COLOR) == 0)
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_CONTENT));
+
+    qs = (cairo_qt_surface_t *) malloc (sizeof(cairo_qt_surface_t));
+    if (qs == NULL)
+        return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+    memset (qs, 0, sizeof(cairo_qt_surface_t));
+
+    QPixmap *pixmap = new QPixmap (width, height);
+    if (pixmap == NULL) {
+       free (qs);
+        return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+    }
+
+    // By default, a QPixmap is opaque; however, if it's filled
+    // with a color with a transparency component, it is converted
+    // to a format that preserves transparency.
+    if (content == CAIRO_CONTENT_COLOR_ALPHA)
+       pixmap->fill(Qt::transparent);
+
+    _cairo_surface_init (&qs->base,
+                        &cairo_qt_surface_backend,
+                        NULL, /* device */
+                        content);
+
+    _cairo_surface_clipper_init (&qs->clipper,
+                                _cairo_qt_surface_clipper_intersect_clip_path);
+
+    qs->pixmap = pixmap;
+
+    if (!pixmap->isNull()) {
+        qs->p = new QPainter(pixmap);
+        qs->supports_porter_duff = qs->p->paintEngine()->hasFeature(QPaintEngine::PorterDuff);
+    }
+
+    qs->window = QRect(0, 0, width, height);
+
+    D(fprintf(stderr, "qpainter_surface_create: qpixmap: [%d %d %d %d] pd:%d\n",
+              qs->window.x(), qs->window.y(), qs->window.width(), qs->window.height(),
+              qs->supports_porter_duff));
+
+    return &qs->base;
+}
+
+QPainter *
+cairo_qt_surface_get_qpainter (cairo_surface_t *surface)
+{
+    cairo_qt_surface_t *qs = (cairo_qt_surface_t*) surface;
+
+    if (surface->type != CAIRO_SURFACE_TYPE_QT)
+        return NULL;
+
+    return qs->p;
+}
+
+QImage *
+cairo_qt_surface_get_qimage (cairo_surface_t *surface)
+{
+    cairo_qt_surface_t *qs = (cairo_qt_surface_t*) surface;
+
+    if (surface->type != CAIRO_SURFACE_TYPE_QT)
+        return NULL;
+
+    return qs->image;
+}
+
+cairo_surface_t *
+cairo_qt_surface_get_image (cairo_surface_t *surface)
+{
+    cairo_qt_surface_t *qs = (cairo_qt_surface_t*) surface;
+
+    if (surface->type != CAIRO_SURFACE_TYPE_QT)
+        return NULL;
+
+    return qs->image_equiv;
+}
+
+/*
+ * TODO:
+ *
+ * - Figure out why QBrush isn't working with non-repeated images
+ *
+ * - Correct repeat mode; right now, every surface source is EXTEND_REPEAT
+ *   - implement EXTEND_NONE (?? probably need to clip to the extents of the source)
+ *   - implement EXTEND_REFLECT (create temporary and copy 4x, then EXTEND_REPEAT that)
+ *
+ * - stroke-image failure
+ *
+ * - Implement mask() with non-solid masks (probably will need to use a temporary and use IN)
+ *
+ * - Implement gradient sources
+ *
+ * - Make create_similar smarter -- create QPixmaps in more circumstances
+ *   (e.g. if the pixmap can have alpha)
+ *
+ * - Implement show_glyphs() in terms of Qt
+ *
+ */
diff --git a/src/cairo-qt.h b/src/cairo-qt.h
new file mode 100644 (file)
index 0000000..c20bbb1
--- /dev/null
@@ -0,0 +1,85 @@
+/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2008 Mozilla Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Mozilla Corporation.
+ *
+ * Contributor(s):
+ *      Vladimir Vukicevic <vladimir@mozilla.com>
+ */
+
+#ifndef CAIRO_QT_H
+#define CAIRO_QT_H
+
+#include "cairo.h"
+
+#if CAIRO_HAS_QT_SURFACE
+
+#include <QtGui/QImage>
+#include <QtGui/QPainter>
+
+CAIRO_BEGIN_DECLS
+
+cairo_public cairo_surface_t *
+cairo_qt_surface_create (QPainter *painter);
+
+cairo_public cairo_surface_t *
+cairo_qt_surface_create_with_qimage (cairo_format_t format,
+                                    int width,
+                                    int height);
+
+cairo_public cairo_surface_t *
+cairo_qt_surface_create_with_qpixmap (cairo_content_t content,
+                                     int width,
+                                     int height);
+
+cairo_public QPainter *
+cairo_qt_surface_get_qpainter (cairo_surface_t *surface);
+
+/* XXX needs hooking to generic surface layer, my vote is for
+cairo_public cairo_surface_t *
+cairo_surface_map_image (cairo_surface_t *surface);
+cairo_public void
+cairo_surface_unmap_image (cairo_surface_t *surface, cairo_surface_t *image);
+*/
+cairo_public cairo_surface_t *
+cairo_qt_surface_get_image (cairo_surface_t *surface);
+
+cairo_public QImage *
+cairo_qt_surface_get_qimage (cairo_surface_t *surface);
+
+CAIRO_END_DECLS
+
+#else /* CAIRO_HAS_QT_SURFACE */
+
+# error Cairo was not compiled with support for the Qt backend
+
+#endif /* CAIRO_HAS_QT_SURFACE */
+
+#endif /* CAIRO_QT_H */
diff --git a/src/cairo-quartz-font.c b/src/cairo-quartz-font.c
new file mode 100644 (file)
index 0000000..53d5c35
--- /dev/null
@@ -0,0 +1,858 @@
+/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright � 2008 Mozilla Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation.
+ *
+ * Contributor(s):
+ *     Vladimir Vukicevic <vladimir@mozilla.com>
+ */
+
+#include "cairoint.h"
+
+#include <dlfcn.h>
+
+#include "cairo-image-surface-private.h"
+#include "cairo-quartz.h"
+#include "cairo-quartz-private.h"
+
+#include "cairo-error-private.h"
+
+/**
+ * SECTION:cairo-quartz-fonts
+ * @Title: Quartz (CGFont) Fonts
+ * @Short_Description: Font support via CGFont on OS X
+ * @See_Also: #cairo_font_face_t
+ *
+ * The Quartz font backend is primarily used to render text on Apple
+ * MacOS X systems.  The CGFont API is used for the internal
+ * implementation of the font backend methods.
+ */
+
+/**
+ * CAIRO_HAS_QUARTZ_FONT:
+ *
+ * Defined if the Quartz font backend is available.
+ * This macro can be used to conditionally compile backend-specific code.
+ */
+
+static CFDataRef (*CGFontCopyTableForTagPtr) (CGFontRef font, uint32_t tag) = NULL;
+
+/* CreateWithFontName exists in 10.5, but not in 10.4; CreateWithName isn't public in 10.4 */
+static CGFontRef (*CGFontCreateWithFontNamePtr) (CFStringRef) = NULL;
+static CGFontRef (*CGFontCreateWithNamePtr) (const char *) = NULL;
+
+/* These aren't public before 10.5, and some have different names in 10.4 */
+static int (*CGFontGetUnitsPerEmPtr) (CGFontRef) = NULL;
+static bool (*CGFontGetGlyphAdvancesPtr) (CGFontRef, const CGGlyph[], size_t, int[]) = NULL;
+static bool (*CGFontGetGlyphBBoxesPtr) (CGFontRef, const CGGlyph[], size_t, CGRect[]) = NULL;
+static CGRect (*CGFontGetFontBBoxPtr) (CGFontRef) = NULL;
+
+/* Not public, but present */
+static void (*CGFontGetGlyphsForUnicharsPtr) (CGFontRef, const UniChar[], const CGGlyph[], size_t) = NULL;
+static void (*CGContextSetAllowsFontSmoothingPtr) (CGContextRef, bool) = NULL;
+static bool (*CGContextGetAllowsFontSmoothingPtr) (CGContextRef) = NULL;
+
+/* Not public in the least bit */
+static CGPathRef (*CGFontGetGlyphPathPtr) (CGFontRef fontRef, CGAffineTransform *textTransform, int unknown, CGGlyph glyph) = NULL;
+
+/* CGFontGetHMetrics isn't public, but the other functions are public/present in 10.5 */
+typedef struct {
+    int ascent;
+    int descent;
+    int leading;
+} quartz_CGFontMetrics;
+static quartz_CGFontMetrics* (*CGFontGetHMetricsPtr) (CGFontRef fontRef) = NULL;
+static int (*CGFontGetAscentPtr) (CGFontRef fontRef) = NULL;
+static int (*CGFontGetDescentPtr) (CGFontRef fontRef) = NULL;
+static int (*CGFontGetLeadingPtr) (CGFontRef fontRef) = NULL;
+
+/* Not public anymore in 64-bits nor in 10.7 */
+static ATSFontRef (*FMGetATSFontRefFromFontPtr) (FMFont iFont) = NULL;
+
+static cairo_bool_t _cairo_quartz_font_symbol_lookup_done = FALSE;
+static cairo_bool_t _cairo_quartz_font_symbols_present = FALSE;
+
+static void
+quartz_font_ensure_symbols(void)
+{
+    if (_cairo_quartz_font_symbol_lookup_done)
+       return;
+
+    CGFontCopyTableForTagPtr = dlsym(RTLD_DEFAULT, "CGFontCopyTableForTag");
+
+    /* Look for the 10.5 versions first */
+    CGFontGetGlyphBBoxesPtr = dlsym(RTLD_DEFAULT, "CGFontGetGlyphBBoxes");
+    if (!CGFontGetGlyphBBoxesPtr)
+       CGFontGetGlyphBBoxesPtr = dlsym(RTLD_DEFAULT, "CGFontGetGlyphBoundingBoxes");
+
+    CGFontGetGlyphsForUnicharsPtr = dlsym(RTLD_DEFAULT, "CGFontGetGlyphsForUnichars");
+    if (!CGFontGetGlyphsForUnicharsPtr)
+       CGFontGetGlyphsForUnicharsPtr = dlsym(RTLD_DEFAULT, "CGFontGetGlyphsForUnicodes");
+
+    CGFontGetFontBBoxPtr = dlsym(RTLD_DEFAULT, "CGFontGetFontBBox");
+
+    /* We just need one of these two */
+    CGFontCreateWithFontNamePtr = dlsym(RTLD_DEFAULT, "CGFontCreateWithFontName");
+    CGFontCreateWithNamePtr = dlsym(RTLD_DEFAULT, "CGFontCreateWithName");
+
+    /* These have the same name in 10.4 and 10.5 */
+    CGFontGetUnitsPerEmPtr = dlsym(RTLD_DEFAULT, "CGFontGetUnitsPerEm");
+    CGFontGetGlyphAdvancesPtr = dlsym(RTLD_DEFAULT, "CGFontGetGlyphAdvances");
+    CGFontGetGlyphPathPtr = dlsym(RTLD_DEFAULT, "CGFontGetGlyphPath");
+
+    CGFontGetHMetricsPtr = dlsym(RTLD_DEFAULT, "CGFontGetHMetrics");
+    CGFontGetAscentPtr = dlsym(RTLD_DEFAULT, "CGFontGetAscent");
+    CGFontGetDescentPtr = dlsym(RTLD_DEFAULT, "CGFontGetDescent");
+    CGFontGetLeadingPtr = dlsym(RTLD_DEFAULT, "CGFontGetLeading");
+
+    CGContextGetAllowsFontSmoothingPtr = dlsym(RTLD_DEFAULT, "CGContextGetAllowsFontSmoothing");
+    CGContextSetAllowsFontSmoothingPtr = dlsym(RTLD_DEFAULT, "CGContextSetAllowsFontSmoothing");
+
+    FMGetATSFontRefFromFontPtr = dlsym(RTLD_DEFAULT, "FMGetATSFontRefFromFont");
+
+    if ((CGFontCreateWithFontNamePtr || CGFontCreateWithNamePtr) &&
+       CGFontGetGlyphBBoxesPtr &&
+       CGFontGetGlyphsForUnicharsPtr &&
+       CGFontGetUnitsPerEmPtr &&
+       CGFontGetGlyphAdvancesPtr &&
+       CGFontGetGlyphPathPtr &&
+       (CGFontGetHMetricsPtr || (CGFontGetAscentPtr && CGFontGetDescentPtr && CGFontGetLeadingPtr)))
+       _cairo_quartz_font_symbols_present = TRUE;
+
+    _cairo_quartz_font_symbol_lookup_done = TRUE;
+}
+
+typedef struct _cairo_quartz_font_face cairo_quartz_font_face_t;
+typedef struct _cairo_quartz_scaled_font cairo_quartz_scaled_font_t;
+
+struct _cairo_quartz_scaled_font {
+    cairo_scaled_font_t base;
+};
+
+struct _cairo_quartz_font_face {
+    cairo_font_face_t base;
+
+    CGFontRef cgFont;
+};
+
+/*
+ * font face backend
+ */
+
+static cairo_status_t
+_cairo_quartz_font_face_create_for_toy (cairo_toy_font_face_t   *toy_face,
+                                       cairo_font_face_t      **font_face)
+{
+    const char *family;
+    char *full_name;
+    CFStringRef cgFontName = NULL;
+    CGFontRef cgFont = NULL;
+    int loop;
+
+    quartz_font_ensure_symbols();
+    if (! _cairo_quartz_font_symbols_present)
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    family = toy_face->family;
+    full_name = malloc (strlen (family) + 64); // give us a bit of room to tack on Bold, Oblique, etc.
+    /* handle CSS-ish faces */
+    if (!strcmp(family, "serif") || !strcmp(family, "Times Roman"))
+       family = "Times";
+    else if (!strcmp(family, "sans-serif") || !strcmp(family, "sans"))
+       family = "Helvetica";
+    else if (!strcmp(family, "cursive"))
+       family = "Apple Chancery";
+    else if (!strcmp(family, "fantasy"))
+       family = "Papyrus";
+    else if (!strcmp(family, "monospace") || !strcmp(family, "mono"))
+       family = "Courier";
+
+    /* Try to build up the full name, e.g. "Helvetica Bold Oblique" first,
+     * then drop the bold, then drop the slant, then drop both.. finally
+     * just use "Helvetica".  And if Helvetica doesn't exist, give up.
+     */
+    for (loop = 0; loop < 5; loop++) {
+       if (loop == 4)
+           family = "Helvetica";
+
+       strcpy (full_name, family);
+
+       if (loop < 3 && (loop & 1) == 0) {
+           if (toy_face->weight == CAIRO_FONT_WEIGHT_BOLD)
+               strcat (full_name, " Bold");
+       }
+
+       if (loop < 3 && (loop & 2) == 0) {
+           if (toy_face->slant == CAIRO_FONT_SLANT_ITALIC)
+               strcat (full_name, " Italic");
+           else if (toy_face->slant == CAIRO_FONT_SLANT_OBLIQUE)
+               strcat (full_name, " Oblique");
+       }
+
+       if (CGFontCreateWithFontNamePtr) {
+           cgFontName = CFStringCreateWithCString (NULL, full_name, kCFStringEncodingASCII);
+           cgFont = CGFontCreateWithFontNamePtr (cgFontName);
+           CFRelease (cgFontName);
+       } else {
+           cgFont = CGFontCreateWithNamePtr (full_name);
+       }
+
+       if (cgFont)
+           break;
+    }
+
+    if (!cgFont) {
+       /* Give up */
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+    }
+
+    *font_face = cairo_quartz_font_face_create_for_cgfont (cgFont);
+    CGFontRelease (cgFont);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_quartz_font_face_destroy (void *abstract_face)
+{
+    cairo_quartz_font_face_t *font_face = (cairo_quartz_font_face_t*) abstract_face;
+
+    CGFontRelease (font_face->cgFont);
+}
+
+static const cairo_scaled_font_backend_t _cairo_quartz_scaled_font_backend;
+
+static cairo_status_t
+_cairo_quartz_font_face_scaled_font_create (void *abstract_face,
+                                           const cairo_matrix_t *font_matrix,
+                                           const cairo_matrix_t *ctm,
+                                           const cairo_font_options_t *options,
+                                           cairo_scaled_font_t **font_out)
+{
+    cairo_quartz_font_face_t *font_face = abstract_face;
+    cairo_quartz_scaled_font_t *font = NULL;
+    cairo_status_t status;
+    cairo_font_extents_t fs_metrics;
+    double ems;
+    CGRect bbox;
+
+    quartz_font_ensure_symbols();
+    if (!_cairo_quartz_font_symbols_present)
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    font = malloc(sizeof(cairo_quartz_scaled_font_t));
+    if (font == NULL)
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    memset (font, 0, sizeof(cairo_quartz_scaled_font_t));
+
+    status = _cairo_scaled_font_init (&font->base,
+                                     &font_face->base, font_matrix, ctm, options,
+                                     &_cairo_quartz_scaled_font_backend);
+    if (status)
+       goto FINISH;
+
+    ems = CGFontGetUnitsPerEmPtr (font_face->cgFont);
+
+    /* initialize metrics */
+    if (CGFontGetFontBBoxPtr && CGFontGetAscentPtr) {
+       fs_metrics.ascent = (CGFontGetAscentPtr (font_face->cgFont) / ems);
+       fs_metrics.descent = - (CGFontGetDescentPtr (font_face->cgFont) / ems);
+       fs_metrics.height = fs_metrics.ascent + fs_metrics.descent +
+           (CGFontGetLeadingPtr (font_face->cgFont) / ems);
+
+       bbox = CGFontGetFontBBoxPtr (font_face->cgFont);
+       fs_metrics.max_x_advance = CGRectGetMaxX(bbox) / ems;
+       fs_metrics.max_y_advance = 0.0;
+    } else {
+       CGGlyph wGlyph;
+       UniChar u;
+
+       quartz_CGFontMetrics *m;
+       m = CGFontGetHMetricsPtr (font_face->cgFont);
+
+       /* On OX 10.4, GetHMetricsPtr sometimes returns NULL for unknown reasons */
+       if (!m) {
+           status = _cairo_error(CAIRO_STATUS_NULL_POINTER);
+           goto FINISH;
+       }
+
+       fs_metrics.ascent = (m->ascent / ems);
+       fs_metrics.descent = - (m->descent / ems);
+       fs_metrics.height = fs_metrics.ascent + fs_metrics.descent + (m->leading / ems);
+
+       /* We kind of have to guess here; W's big, right? */
+       u = (UniChar) 'W';
+       CGFontGetGlyphsForUnicharsPtr (font_face->cgFont, &u, &wGlyph, 1);
+       if (wGlyph && CGFontGetGlyphBBoxesPtr (font_face->cgFont, &wGlyph, 1, &bbox)) {
+           fs_metrics.max_x_advance = CGRectGetMaxX(bbox) / ems;
+           fs_metrics.max_y_advance = 0.0;
+       } else {
+           fs_metrics.max_x_advance = 0.0;
+           fs_metrics.max_y_advance = 0.0;
+       }
+    }
+
+    status = _cairo_scaled_font_set_metrics (&font->base, &fs_metrics);
+
+FINISH:
+    if (status != CAIRO_STATUS_SUCCESS) {
+       free (font);
+    } else {
+       *font_out = (cairo_scaled_font_t*) font;
+    }
+
+    return status;
+}
+
+const cairo_font_face_backend_t _cairo_quartz_font_face_backend = {
+    CAIRO_FONT_TYPE_QUARTZ,
+    _cairo_quartz_font_face_create_for_toy,
+    _cairo_quartz_font_face_destroy,
+    _cairo_quartz_font_face_scaled_font_create
+};
+
+/**
+ * cairo_quartz_font_face_create_for_cgfont
+ * @font: a #CGFontRef obtained through a method external to cairo.
+ *
+ * Creates a new font for the Quartz font backend based on a
+ * #CGFontRef.  This font can then be used with
+ * cairo_set_font_face() or cairo_scaled_font_create().
+ *
+ * Return value: a newly created #cairo_font_face_t. Free with
+ *  cairo_font_face_destroy() when you are done using it.
+ *
+ * Since: 1.6
+ */
+cairo_font_face_t *
+cairo_quartz_font_face_create_for_cgfont (CGFontRef font)
+{
+    cairo_quartz_font_face_t *font_face;
+
+    quartz_font_ensure_symbols();
+
+    font_face = malloc (sizeof (cairo_quartz_font_face_t));
+    if (!font_face) {
+       cairo_status_t ignore_status;
+       ignore_status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+       return (cairo_font_face_t *)&_cairo_font_face_nil;
+    }
+
+    font_face->cgFont = CGFontRetain (font);
+
+    _cairo_font_face_init (&font_face->base, &_cairo_quartz_font_face_backend);
+
+    return &font_face->base;
+}
+
+/*
+ * scaled font backend
+ */
+
+static cairo_quartz_font_face_t *
+_cairo_quartz_scaled_to_face (void *abstract_font)
+{
+    cairo_quartz_scaled_font_t *sfont = (cairo_quartz_scaled_font_t*) abstract_font;
+    cairo_font_face_t *font_face = sfont->base.font_face;
+    assert (font_face->backend->type == CAIRO_FONT_TYPE_QUARTZ);
+    return (cairo_quartz_font_face_t*) font_face;
+}
+
+static void
+_cairo_quartz_scaled_font_fini(void *abstract_font)
+{
+}
+
+#define INVALID_GLYPH 0x00
+
+static inline CGGlyph
+_cairo_quartz_scaled_glyph_index (cairo_scaled_glyph_t *scaled_glyph) {
+    unsigned long index = _cairo_scaled_glyph_index (scaled_glyph);
+    if (index > 0xffff)
+       return INVALID_GLYPH;
+    return (CGGlyph) index;
+}
+
+static cairo_int_status_t
+_cairo_quartz_init_glyph_metrics (cairo_quartz_scaled_font_t *font,
+                                 cairo_scaled_glyph_t *scaled_glyph)
+{
+    cairo_int_status_t status = CAIRO_STATUS_SUCCESS;
+
+    cairo_quartz_font_face_t *font_face = _cairo_quartz_scaled_to_face(font);
+    cairo_text_extents_t extents = {0, 0, 0, 0, 0, 0};
+    CGGlyph glyph = _cairo_quartz_scaled_glyph_index (scaled_glyph);
+    int advance;
+    CGRect bbox;
+    double emscale = CGFontGetUnitsPerEmPtr (font_face->cgFont);
+    double xmin, ymin, xmax, ymax;
+
+    if (glyph == INVALID_GLYPH)
+       goto FAIL;
+
+    if (!CGFontGetGlyphAdvancesPtr (font_face->cgFont, &glyph, 1, &advance) ||
+       !CGFontGetGlyphBBoxesPtr (font_face->cgFont, &glyph, 1, &bbox))
+       goto FAIL;
+
+    /* broken fonts like Al Bayan return incorrect bounds for some null characters,
+       see https://bugzilla.mozilla.org/show_bug.cgi?id=534260 */
+    if (unlikely (bbox.origin.x == -32767 &&
+                  bbox.origin.y == -32767 &&
+                  bbox.size.width == 65534 &&
+                  bbox.size.height == 65534)) {
+        bbox.origin.x = bbox.origin.y = 0;
+        bbox.size.width = bbox.size.height = 0;
+    }
+
+    bbox = CGRectMake (bbox.origin.x / emscale,
+                      bbox.origin.y / emscale,
+                      bbox.size.width / emscale,
+                      bbox.size.height / emscale);
+
+    /* Should we want to always integer-align glyph extents, we can do so in this way */
+#if 0
+    {
+       CGAffineTransform textMatrix;
+       textMatrix = CGAffineTransformMake (font->base.scale.xx,
+                                           -font->base.scale.yx,
+                                           -font->base.scale.xy,
+                                           font->base.scale.yy,
+                                           0.0f, 0.0f);
+
+       bbox = CGRectApplyAffineTransform (bbox, textMatrix);
+       bbox = CGRectIntegral (bbox);
+       bbox = CGRectApplyAffineTransform (bbox, CGAffineTransformInvert (textMatrix));
+    }
+#endif
+
+#if 0
+    fprintf (stderr, "[0x%04x] bbox: %f %f %f %f\n", glyph,
+            bbox.origin.x / emscale, bbox.origin.y / emscale,
+            bbox.size.width / emscale, bbox.size.height / emscale);
+#endif
+
+    xmin = CGRectGetMinX(bbox);
+    ymin = CGRectGetMinY(bbox);
+    xmax = CGRectGetMaxX(bbox);
+    ymax = CGRectGetMaxY(bbox);
+
+    extents.x_bearing = xmin;
+    extents.y_bearing = - ymax;
+    extents.width = xmax - xmin;
+    extents.height = ymax - ymin;
+
+    extents.x_advance = (double) advance / emscale;
+    extents.y_advance = 0.0;
+
+#if 0
+    fprintf (stderr, "[0x%04x] extents: bearings: %f %f dim: %f %f adv: %f\n\n", glyph,
+            extents.x_bearing, extents.y_bearing, extents.width, extents.height, extents.x_advance);
+#endif
+
+  FAIL:
+    _cairo_scaled_glyph_set_metrics (scaled_glyph,
+                                    &font->base,
+                                    &extents);
+
+    return status;
+}
+
+static void
+_cairo_quartz_path_apply_func (void *info, const CGPathElement *el)
+{
+    cairo_path_fixed_t *path = (cairo_path_fixed_t *) info;
+    cairo_status_t status;
+
+    switch (el->type) {
+       case kCGPathElementMoveToPoint:
+           status = _cairo_path_fixed_move_to (path,
+                                               _cairo_fixed_from_double(el->points[0].x),
+                                               _cairo_fixed_from_double(el->points[0].y));
+           assert(!status);
+           break;
+       case kCGPathElementAddLineToPoint:
+           status = _cairo_path_fixed_line_to (path,
+                                               _cairo_fixed_from_double(el->points[0].x),
+                                               _cairo_fixed_from_double(el->points[0].y));
+           assert(!status);
+           break;
+       case kCGPathElementAddQuadCurveToPoint: {
+           cairo_fixed_t fx, fy;
+           double x, y;
+           if (!_cairo_path_fixed_get_current_point (path, &fx, &fy))
+               fx = fy = 0;
+           x = _cairo_fixed_to_double (fx);
+           y = _cairo_fixed_to_double (fy);
+
+           status = _cairo_path_fixed_curve_to (path,
+                                                _cairo_fixed_from_double((x + el->points[0].x * 2.0) / 3.0),
+                                                _cairo_fixed_from_double((y + el->points[0].y * 2.0) / 3.0),
+                                                _cairo_fixed_from_double((el->points[0].x * 2.0 + el->points[1].x) / 3.0),
+                                                _cairo_fixed_from_double((el->points[0].y * 2.0 + el->points[1].y) / 3.0),
+                                                _cairo_fixed_from_double(el->points[1].x),
+                                                _cairo_fixed_from_double(el->points[1].y));
+       }
+           assert(!status);
+           break;
+       case kCGPathElementAddCurveToPoint:
+           status = _cairo_path_fixed_curve_to (path,
+                                                _cairo_fixed_from_double(el->points[0].x),
+                                                _cairo_fixed_from_double(el->points[0].y),
+                                                _cairo_fixed_from_double(el->points[1].x),
+                                                _cairo_fixed_from_double(el->points[1].y),
+                                                _cairo_fixed_from_double(el->points[2].x),
+                                                _cairo_fixed_from_double(el->points[2].y));
+           assert(!status);        
+           break;
+       case kCGPathElementCloseSubpath:
+           status = _cairo_path_fixed_close_path (path);
+           assert(!status);
+           break;
+    }
+}
+
+static cairo_int_status_t
+_cairo_quartz_init_glyph_path (cairo_quartz_scaled_font_t *font,
+                              cairo_scaled_glyph_t *scaled_glyph)
+{
+    cairo_quartz_font_face_t *font_face = _cairo_quartz_scaled_to_face(font);
+    CGGlyph glyph = _cairo_quartz_scaled_glyph_index (scaled_glyph);
+    CGAffineTransform textMatrix;
+    CGPathRef glyphPath;
+    cairo_path_fixed_t *path;
+
+    if (glyph == INVALID_GLYPH) {
+       _cairo_scaled_glyph_set_path (scaled_glyph, &font->base, _cairo_path_fixed_create());
+       return CAIRO_STATUS_SUCCESS;
+    }
+
+    /* scale(1,-1) * font->base.scale */
+    textMatrix = CGAffineTransformMake (font->base.scale.xx,
+                                       font->base.scale.yx,
+                                       -font->base.scale.xy,
+                                       -font->base.scale.yy,
+                                       0, 0);
+
+    glyphPath = CGFontGetGlyphPathPtr (font_face->cgFont, &textMatrix, 0, glyph);
+    if (!glyphPath)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    path = _cairo_path_fixed_create ();
+    if (!path) {
+       CGPathRelease (glyphPath);
+       return _cairo_error(CAIRO_STATUS_NO_MEMORY);
+    }
+
+    CGPathApply (glyphPath, path, _cairo_quartz_path_apply_func);
+
+    CGPathRelease (glyphPath);
+
+    _cairo_scaled_glyph_set_path (scaled_glyph, &font->base, path);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_quartz_init_glyph_surface (cairo_quartz_scaled_font_t *font,
+                                 cairo_scaled_glyph_t *scaled_glyph)
+{
+    cairo_int_status_t status = CAIRO_STATUS_SUCCESS;
+
+    cairo_quartz_font_face_t *font_face = _cairo_quartz_scaled_to_face(font);
+
+    cairo_image_surface_t *surface = NULL;
+
+    CGGlyph glyph = _cairo_quartz_scaled_glyph_index (scaled_glyph);
+
+    int advance;
+    CGRect bbox;
+    double width, height;
+    double emscale = CGFontGetUnitsPerEmPtr (font_face->cgFont);
+
+    CGContextRef cgContext = NULL;
+    CGAffineTransform textMatrix;
+    CGRect glyphRect, glyphRectInt;
+    CGPoint glyphOrigin;
+
+    //fprintf (stderr, "scaled_glyph: %p surface: %p\n", scaled_glyph, scaled_glyph->surface);
+
+    /* Create blank 2x2 image if we don't have this character.
+     * Maybe we should draw a better missing-glyph slug or something,
+     * but this is ok for now.
+     */
+    if (glyph == INVALID_GLYPH) {
+       surface = (cairo_image_surface_t*) cairo_image_surface_create (CAIRO_FORMAT_A8, 2, 2);
+       status = cairo_surface_status ((cairo_surface_t *) surface);
+       if (status)
+           return status;
+
+       _cairo_scaled_glyph_set_surface (scaled_glyph,
+                                        &font->base,
+                                        surface);
+       return CAIRO_STATUS_SUCCESS;
+    }
+
+    if (!CGFontGetGlyphAdvancesPtr (font_face->cgFont, &glyph, 1, &advance) ||
+       !CGFontGetGlyphBBoxesPtr (font_face->cgFont, &glyph, 1, &bbox))
+    {
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+    }
+
+    /* scale(1,-1) * font->base.scale * scale(1,-1) */
+    textMatrix = CGAffineTransformMake (font->base.scale.xx,
+                                       -font->base.scale.yx,
+                                       -font->base.scale.xy,
+                                       font->base.scale.yy,
+                                       0, -0);
+    glyphRect = CGRectMake (bbox.origin.x / emscale,
+                           bbox.origin.y / emscale,
+                           bbox.size.width / emscale,
+                           bbox.size.height / emscale);
+
+    glyphRect = CGRectApplyAffineTransform (glyphRect, textMatrix);
+
+    /* Round the rectangle outwards, so that we don't have to deal
+     * with non-integer-pixel origins or dimensions.
+     */
+    glyphRectInt = CGRectIntegral (glyphRect);
+
+#if 0
+    fprintf (stderr, "glyphRect[o]: %f %f %f %f\n",
+            glyphRect.origin.x, glyphRect.origin.y, glyphRect.size.width, glyphRect.size.height);
+    fprintf (stderr, "glyphRectInt: %f %f %f %f\n",
+            glyphRectInt.origin.x, glyphRectInt.origin.y, glyphRectInt.size.width, glyphRectInt.size.height);
+#endif
+
+    glyphOrigin = glyphRectInt.origin;
+
+    //textMatrix = CGAffineTransformConcat (textMatrix, CGAffineTransformInvert (ctm));
+
+    width = glyphRectInt.size.width;
+    height = glyphRectInt.size.height;
+
+    //fprintf (stderr, "glyphRect[n]: %f %f %f %f\n", glyphRect.origin.x, glyphRect.origin.y, glyphRect.size.width, glyphRect.size.height);
+
+    surface = (cairo_image_surface_t*) cairo_image_surface_create (CAIRO_FORMAT_A8, width, height);
+    if (surface->base.status)
+       return surface->base.status;
+
+    if (surface->width != 0 && surface->height != 0) {
+       cgContext = CGBitmapContextCreate (surface->data,
+                                          surface->width,
+                                          surface->height,
+                                          8,
+                                          surface->stride,
+                                          NULL,
+                                          kCGImageAlphaOnly);
+
+       if (cgContext == NULL) {
+           cairo_surface_destroy (&surface->base);
+           return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+       }
+
+       CGContextSetFont (cgContext, font_face->cgFont);
+       CGContextSetFontSize (cgContext, 1.0);
+       CGContextSetTextMatrix (cgContext, textMatrix);
+
+       switch (font->base.options.antialias) {
+       case CAIRO_ANTIALIAS_SUBPIXEL:
+       case CAIRO_ANTIALIAS_BEST:
+           CGContextSetShouldAntialias (cgContext, TRUE);
+           CGContextSetShouldSmoothFonts (cgContext, TRUE);
+           if (CGContextSetAllowsFontSmoothingPtr &&
+               !CGContextGetAllowsFontSmoothingPtr (cgContext))
+               CGContextSetAllowsFontSmoothingPtr (cgContext, TRUE);
+           break;
+       case CAIRO_ANTIALIAS_NONE:
+           CGContextSetShouldAntialias (cgContext, FALSE);
+           break;
+       case CAIRO_ANTIALIAS_GRAY:
+       case CAIRO_ANTIALIAS_GOOD:
+       case CAIRO_ANTIALIAS_FAST:
+           CGContextSetShouldAntialias (cgContext, TRUE);
+           CGContextSetShouldSmoothFonts (cgContext, FALSE);
+           break;
+       case CAIRO_ANTIALIAS_DEFAULT:
+       default:
+           /* Don't do anything */
+           break;
+       }
+
+       CGContextSetAlpha (cgContext, 1.0);
+       CGContextShowGlyphsAtPoint (cgContext, - glyphOrigin.x, - glyphOrigin.y, &glyph, 1);
+
+       CGContextRelease (cgContext);
+    }
+
+    cairo_surface_set_device_offset (&surface->base,
+                                    - glyphOrigin.x,
+                                    height + glyphOrigin.y);
+
+    _cairo_scaled_glyph_set_surface (scaled_glyph, &font->base, surface);
+
+    return status;
+}
+
+static cairo_int_status_t
+_cairo_quartz_scaled_glyph_init (void *abstract_font,
+                                cairo_scaled_glyph_t *scaled_glyph,
+                                cairo_scaled_glyph_info_t info)
+{
+    cairo_quartz_scaled_font_t *font = (cairo_quartz_scaled_font_t *) abstract_font;
+    cairo_int_status_t status = CAIRO_STATUS_SUCCESS;
+
+    if (!status && (info & CAIRO_SCALED_GLYPH_INFO_METRICS))
+       status = _cairo_quartz_init_glyph_metrics (font, scaled_glyph);
+
+    if (!status && (info & CAIRO_SCALED_GLYPH_INFO_PATH))
+       status = _cairo_quartz_init_glyph_path (font, scaled_glyph);
+
+    if (!status && (info & CAIRO_SCALED_GLYPH_INFO_SURFACE))
+       status = _cairo_quartz_init_glyph_surface (font, scaled_glyph);
+
+    return status;
+}
+
+static unsigned long
+_cairo_quartz_ucs4_to_index (void *abstract_font,
+                            uint32_t ucs4)
+{
+    cairo_quartz_scaled_font_t *font = (cairo_quartz_scaled_font_t*) abstract_font;
+    cairo_quartz_font_face_t *ffont = _cairo_quartz_scaled_to_face(font);
+    UniChar u = (UniChar) ucs4;
+    CGGlyph glyph;
+
+    CGFontGetGlyphsForUnicharsPtr (ffont->cgFont, &u, &glyph, 1);
+
+    return glyph;
+}
+
+static cairo_int_status_t
+_cairo_quartz_load_truetype_table (void                    *abstract_font,
+                                  unsigned long     tag,
+                                  long              offset,
+                                  unsigned char    *buffer,
+                                  unsigned long    *length)
+{
+    cairo_quartz_font_face_t *font_face = _cairo_quartz_scaled_to_face (abstract_font);
+    CFDataRef data = NULL;
+
+    if (likely (CGFontCopyTableForTagPtr))
+       data = CGFontCopyTableForTagPtr (font_face->cgFont, tag);
+
+    if (!data)
+        return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    if (buffer == NULL) {
+       *length = CFDataGetLength (data);
+       CFRelease (data);
+       return CAIRO_STATUS_SUCCESS;
+    }
+
+    if (CFDataGetLength (data) < offset + (long) *length) {
+       CFRelease (data);
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+    }
+
+    CFDataGetBytes (data, CFRangeMake (offset, *length), buffer);
+    CFRelease (data);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static const cairo_scaled_font_backend_t _cairo_quartz_scaled_font_backend = {
+    CAIRO_FONT_TYPE_QUARTZ,
+    _cairo_quartz_scaled_font_fini,
+    _cairo_quartz_scaled_glyph_init,
+    NULL, /* text_to_glyphs */
+    _cairo_quartz_ucs4_to_index,
+    _cairo_quartz_load_truetype_table,
+    NULL, /* map_glyphs_to_unicode */
+};
+
+/*
+ * private methods that the quartz surface uses
+ */
+
+CGFontRef
+_cairo_quartz_scaled_font_get_cg_font_ref (cairo_scaled_font_t *abstract_font)
+{
+    cairo_quartz_font_face_t *ffont = _cairo_quartz_scaled_to_face(abstract_font);
+
+    return ffont->cgFont;
+}
+
+/*
+ * compat with old ATSUI backend
+ */
+
+/**
+ * cairo_quartz_font_face_create_for_atsu_font_id
+ * @font_id: an ATSUFontID for the font.
+ *
+ * Creates a new font for the Quartz font backend based on an
+ * #ATSUFontID. This font can then be used with
+ * cairo_set_font_face() or cairo_scaled_font_create().
+ *
+ * Return value: a newly created #cairo_font_face_t. Free with
+ *  cairo_font_face_destroy() when you are done using it.
+ *
+ * Since: 1.6
+ **/
+cairo_font_face_t *
+cairo_quartz_font_face_create_for_atsu_font_id (ATSUFontID font_id)
+{
+    quartz_font_ensure_symbols();
+
+    if (FMGetATSFontRefFromFontPtr != NULL) {
+       ATSFontRef atsFont = FMGetATSFontRefFromFontPtr (font_id);
+       CGFontRef cgFont = CGFontCreateWithPlatformFont (&atsFont);
+       cairo_font_face_t *ff;
+
+       ff = cairo_quartz_font_face_create_for_cgfont (cgFont);
+
+       CGFontRelease (cgFont);
+
+       return ff;
+    } else {
+       _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+       return (cairo_font_face_t *)&_cairo_font_face_nil;
+    }
+}
+
+/* This is the old name for the above function, exported for compat purposes */
+cairo_font_face_t *cairo_atsui_font_face_create_for_atsu_font_id (ATSUFontID font_id);
+
+cairo_font_face_t *
+cairo_atsui_font_face_create_for_atsu_font_id (ATSUFontID font_id)
+{
+    return cairo_quartz_font_face_create_for_atsu_font_id (font_id);
+}
diff --git a/src/cairo-quartz-image-surface.c b/src/cairo-quartz-image-surface.c
new file mode 100644 (file)
index 0000000..b04c421
--- /dev/null
@@ -0,0 +1,375 @@
+/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright � 2008 Mozilla Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation.
+ *
+ * Contributor(s):
+ *     Vladimir Vukicevic <vladimir@mozilla.com>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-quartz-image.h"
+#include "cairo-quartz-private.h"
+
+#include "cairo-error-private.h"
+#include "cairo-default-context-private.h"
+
+#define SURFACE_ERROR_NO_MEMORY (_cairo_surface_create_in_error(_cairo_error(CAIRO_STATUS_NO_MEMORY)))
+#define SURFACE_ERROR_TYPE_MISMATCH (_cairo_surface_create_in_error(_cairo_error(CAIRO_STATUS_SURFACE_TYPE_MISMATCH)))
+#define SURFACE_ERROR_INVALID_SIZE (_cairo_surface_create_in_error(_cairo_error(CAIRO_STATUS_INVALID_SIZE)))
+#define SURFACE_ERROR_INVALID_FORMAT (_cairo_surface_create_in_error(_cairo_error(CAIRO_STATUS_INVALID_FORMAT)))
+
+static void
+DataProviderReleaseCallback (void *info, const void *data, size_t size)
+{
+    cairo_surface_t *surface = (cairo_surface_t *) info;
+    cairo_surface_destroy (surface);
+}
+
+static cairo_surface_t *
+_cairo_quartz_image_surface_create_similar (void *asurface,
+                                           cairo_content_t content,
+                                           int width,
+                                           int height)
+{
+    cairo_surface_t *isurf =
+       _cairo_image_surface_create_with_content (content, width, height);
+    cairo_surface_t *result = cairo_quartz_image_surface_create (isurf);
+    cairo_surface_destroy (isurf);
+
+    return result;
+}
+
+static cairo_surface_t *
+_cairo_quartz_image_surface_create_similar_image (void *asurface,
+                                                 cairo_format_t format,
+                                                 int width,
+                                                 int height)
+{
+    cairo_surface_t *isurf = cairo_image_surface_create (format, width, height);
+    cairo_surface_t *result = cairo_quartz_image_surface_create (isurf);
+    cairo_surface_destroy (isurf);
+
+    return result;
+}
+
+static cairo_status_t
+_cairo_quartz_image_surface_finish (void *asurface)
+{
+    cairo_quartz_image_surface_t *surface = (cairo_quartz_image_surface_t *) asurface;
+
+    /* the imageSurface will be destroyed by the data provider's release callback */
+    CGImageRelease (surface->image);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_quartz_image_surface_acquire_source_image (void *asurface,
+                                                 cairo_image_surface_t **image_out,
+                                                 void **image_extra)
+{
+    cairo_quartz_image_surface_t *surface = (cairo_quartz_image_surface_t *) asurface;
+
+    *image_out = surface->imageSurface;
+    *image_extra = NULL;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_surface_t *
+_cairo_quartz_image_surface_map_to_image (void *asurface,
+                                         const cairo_rectangle_int_t *extents)
+{
+    cairo_quartz_image_surface_t *surface = (cairo_quartz_image_surface_t *) asurface;
+    return _cairo_surface_create_for_rectangle_int (surface->imageSurface, extents);
+}
+
+static cairo_int_status_t
+_cairo_quartz_image_surface_unmap_image (void *asurface,
+                                        cairo_image_surface_t *image)
+{
+    cairo_surface_destroy (&image->base);
+}
+
+static cairo_bool_t
+_cairo_quartz_image_surface_get_extents (void *asurface,
+                                        cairo_rectangle_int_t *extents)
+{
+    cairo_quartz_image_surface_t *surface = (cairo_quartz_image_surface_t *) asurface;
+
+    extents->x = 0;
+    extents->y = 0;
+    extents->width  = surface->width;
+    extents->height = surface->height;
+    return TRUE;
+}
+
+/* we assume some drawing happened to the image buffer; make sure it's
+ * represented in the CGImage on flush()
+ */
+
+static cairo_status_t
+_cairo_quartz_image_surface_flush (void *asurface)
+{
+    cairo_quartz_image_surface_t *surface = (cairo_quartz_image_surface_t *) asurface;
+    CGImageRef oldImage = surface->image;
+    CGImageRef newImage = NULL;
+
+    /* XXX only flush if the image has been modified. */
+
+    /* To be released by the ReleaseCallback */
+    cairo_surface_reference ((cairo_surface_t*) surface->imageSurface);
+
+    newImage = _cairo_quartz_create_cgimage (surface->imageSurface->format,
+                                            surface->imageSurface->width,
+                                            surface->imageSurface->height,
+                                            surface->imageSurface->stride,
+                                            surface->imageSurface->data,
+                                            TRUE,
+                                            NULL,
+                                            DataProviderReleaseCallback,
+                                            surface->imageSurface);
+
+    surface->image = newImage;
+    CGImageRelease (oldImage);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_quartz_image_surface_paint (void                        *abstract_surface,
+                                  cairo_operator_t              op,
+                                  const cairo_pattern_t        *source,
+                                  const cairo_clip_t           *clip)
+{
+    cairo_quartz_image_surface_t *surface = abstract_surface;
+    return _cairo_surface_paint (&surface->imageSurface->base,
+                                op, source, clip);
+}
+
+static cairo_int_status_t
+_cairo_quartz_image_surface_mask (void                         *abstract_surface,
+                                 cairo_operator_t               op,
+                                 const cairo_pattern_t         *source,
+                                 const cairo_pattern_t         *mask,
+                                 const cairo_clip_t            *clip)
+{
+    cairo_quartz_image_surface_t *surface = abstract_surface;
+    return _cairo_surface_mask (&surface->imageSurface->base,
+                               op, source, mask, clip);
+}
+
+static cairo_int_status_t
+_cairo_quartz_image_surface_stroke (void                       *abstract_surface,
+                                   cairo_operator_t             op,
+                                   const cairo_pattern_t       *source,
+                                   const cairo_path_fixed_t    *path,
+                                   const cairo_stroke_style_t  *style,
+                                   const cairo_matrix_t        *ctm,
+                                   const cairo_matrix_t        *ctm_inverse,
+                                   double                       tolerance,
+                                   cairo_antialias_t            antialias,
+                                   const cairo_clip_t          *clip)
+{
+    cairo_quartz_image_surface_t *surface = abstract_surface;
+    return _cairo_surface_stroke (&surface->imageSurface->base,
+                                 op, source, path,
+                                 style, ctm, ctm_inverse,
+                                 tolerance, antialias, clip);
+}
+
+static cairo_int_status_t
+_cairo_quartz_image_surface_fill (void                         *abstract_surface,
+                          cairo_operator_t              op,
+                          const cairo_pattern_t        *source,
+                          const cairo_path_fixed_t     *path,
+                          cairo_fill_rule_t             fill_rule,
+                          double                        tolerance,
+                          cairo_antialias_t             antialias,
+                          const cairo_clip_t           *clip)
+{
+    cairo_quartz_image_surface_t *surface = abstract_surface;
+    return _cairo_surface_fill (&surface->imageSurface->base,
+                               op, source, path,
+                               fill_rule, tolerance, antialias,
+                               clip);
+}
+
+static cairo_int_status_t
+_cairo_quartz_image_surface_glyphs (void                       *abstract_surface,
+                                   cairo_operator_t             op,
+                                   const cairo_pattern_t       *source,
+                                   cairo_glyph_t               *glyphs,
+                                   int                          num_glyphs,
+                                   cairo_scaled_font_t         *scaled_font,
+                                   const cairo_clip_t          *clip)
+{
+    cairo_quartz_image_surface_t *surface = abstract_surface;
+    return _cairo_surface_show_glyphs (&surface->imageSurface->base,
+                                      op, source,
+                                      glyphs, num_glyphs, scaled_font,
+                                      clip, num_remaining);
+}
+
+
+static const cairo_surface_backend_t cairo_quartz_image_surface_backend = {
+    CAIRO_SURFACE_TYPE_QUARTZ_IMAGE,
+    _cairo_quartz_image_surface_finish,
+
+    _cairo_default_context_create,
+
+    _cairo_quartz_image_surface_create_similar,
+    _cairo_quartz_image_surface_create_similar_image,
+    _cairo_quartz_image_surface_map_to_image,
+    _cairo_quartz_image_surface_unmap_image,
+
+    _cairo_quartz_image_surface_acquire_source_image,
+    NULL, /* release_source_image */
+    NULL, /* snapshot */
+
+    NULL, /* copy_page */
+    NULL, /* show_page */
+
+    _cairo_quartz_image_surface_get_extents,
+    NULL, /* get_font_options */
+
+    _cairo_quartz_image_surface_flush,
+    NULL, /* mark_dirty_rectangle */
+
+    _cairo_quartz_image_surface_paint,
+    _cairo_quartz_image_surface_mask,
+    _cairo_quartz_image_surface_stroke,
+    _cairo_quartz_image_surface_fill,
+    NULL  /* fill-stroke */
+    _cairo_quartz_image_surface_glyphs,
+};
+
+/**
+ * cairo_quartz_image_surface_create
+ * @surface: a cairo image surface to wrap with a quartz image surface
+ *
+ * Creates a Quartz surface backed by a CGImageRef that references the
+ * given image surface. The resulting surface can be rendered quickly
+ * when used as a source when rendering to a #cairo_quartz_surface.  If
+ * the data in the image surface is ever updated, cairo_surface_flush()
+ * must be called on the #cairo_quartz_image_surface to ensure that the
+ * CGImageRef refers to the updated data.
+ *
+ * Return value: the newly created surface.
+ *
+ * Since: 1.6
+ */
+cairo_surface_t *
+cairo_quartz_image_surface_create (cairo_surface_t *surface)
+{
+    cairo_quartz_image_surface_t *qisurf;
+
+    CGImageRef image;
+
+    cairo_image_surface_t *image_surface;
+    int width, height, stride;
+    cairo_format_t format;
+    unsigned char *data;
+
+    if (surface->status)
+       return surface;
+
+    if (cairo_surface_get_type(surface) != CAIRO_SURFACE_TYPE_IMAGE)
+       return SURFACE_ERROR_TYPE_MISMATCH;
+
+    image_surface = (cairo_image_surface_t*) surface;
+    width = image_surface->width;
+    height = image_surface->height;
+    stride = image_surface->stride;
+    format = image_surface->format;
+    data = image_surface->data;
+
+    if (!_cairo_quartz_verify_surface_size(width, height))
+       return SURFACE_ERROR_INVALID_SIZE;
+
+    if (width == 0 || height == 0)
+       return SURFACE_ERROR_INVALID_SIZE;
+
+    if (format != CAIRO_FORMAT_ARGB32 && format != CAIRO_FORMAT_RGB24)
+       return SURFACE_ERROR_INVALID_FORMAT;
+
+    qisurf = malloc(sizeof(cairo_quartz_image_surface_t));
+    if (qisurf == NULL)
+       return SURFACE_ERROR_NO_MEMORY;
+
+    memset (qisurf, 0, sizeof(cairo_quartz_image_surface_t));
+
+    /* In case the create_cgimage fails, this ref will
+     * be released via the callback (which will be called in
+     * case of failure.)
+     */
+    cairo_surface_reference (surface);
+
+    image = _cairo_quartz_create_cgimage (format,
+                                         width, height,
+                                         stride,
+                                         data,
+                                         TRUE,
+                                         NULL,
+                                         DataProviderReleaseCallback,
+                                         image_surface);
+
+    if (!image) {
+       free (qisurf);
+       return SURFACE_ERROR_NO_MEMORY;
+    }
+
+    _cairo_surface_init (&qisurf->base,
+                        &cairo_quartz_image_surface_backend,
+                        NULL, /* device */
+                        _cairo_content_from_format (format));
+
+    qisurf->width = width;
+    qisurf->height = height;
+
+    qisurf->image = image;
+    qisurf->imageSurface = image_surface;
+
+    return &qisurf->base;
+}
+
+
+cairo_surface_t *
+cairo_quartz_image_surface_get_image (cairo_surface_t *asurface)
+{
+    cairo_quartz_image_surface_t *surface = (cairo_quartz_image_surface_t*) asurface;
+
+    if (cairo_surface_get_type(asurface) != CAIRO_SURFACE_TYPE_QUARTZ_IMAGE)
+       return NULL;
+
+    return (cairo_surface_t*) surface->imageSurface;
+}
diff --git a/src/cairo-quartz-image.h b/src/cairo-quartz-image.h
new file mode 100644 (file)
index 0000000..dae234d
--- /dev/null
@@ -0,0 +1,59 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2008 Mozilla Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation.
+ *
+ * Contributor(s):
+ *      Vladimir Vukicevic <vladimir@mozilla.com>
+ */
+
+#ifndef CAIRO_QUARTZ_IMAGE_H
+#define CAIRO_QUARTZ_IMAGE_H
+
+#include "cairo.h"
+
+#if CAIRO_HAS_QUARTZ_IMAGE_SURFACE
+
+#include <Carbon/Carbon.h>
+
+CAIRO_BEGIN_DECLS
+
+cairo_public cairo_surface_t *
+cairo_quartz_image_surface_create (cairo_surface_t *image_surface);
+
+cairo_public cairo_surface_t *
+cairo_quartz_image_surface_get_image (cairo_surface_t *surface);
+
+CAIRO_END_DECLS
+
+#else  /* CAIRO_HAS_QUARTZ_IMAGE_SURFACE */
+# error Cairo was not compiled with support for the quartz-image backend
+#endif /* CAIRO_HAS_QUARTZ_IMAGE_SURFACE */
+
+#endif /* CAIRO_QUARTZ_IMAGE_H */
diff --git a/src/cairo-quartz-private.h b/src/cairo-quartz-private.h
new file mode 100644 (file)
index 0000000..474ec15
--- /dev/null
@@ -0,0 +1,106 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2004 Calum Robinson
+ * Copyright (C) 2006,2007 Mozilla Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Calum Robinson
+ *
+ * Contributor(s):
+ *    Calum Robinson <calumr@mac.com>
+ *    Vladimir Vukicevic <vladimir@mozilla.com>
+ */
+
+#ifndef CAIRO_QUARTZ_PRIVATE_H
+#define CAIRO_QUARTZ_PRIVATE_H
+
+#include "cairoint.h"
+
+#if CAIRO_HAS_QUARTZ_SURFACE
+#include "cairo-quartz.h"
+#include "cairo-surface-clipper-private.h"
+
+#ifdef CGFLOAT_DEFINED
+typedef CGFloat cairo_quartz_float_t;
+#else
+typedef float cairo_quartz_float_t;
+#endif
+
+typedef enum {
+    DO_DIRECT,
+    DO_SHADING,
+    DO_IMAGE,
+    DO_TILED_IMAGE
+} cairo_quartz_action_t;
+
+typedef struct cairo_quartz_surface {
+    cairo_surface_t base;
+
+    CGContextRef cgContext;
+    CGAffineTransform cgContextBaseCTM;
+
+    void *imageData;
+    cairo_surface_t *imageSurfaceEquiv;
+
+    cairo_surface_clipper_t clipper;
+    cairo_rectangle_int_t extents;
+    cairo_rectangle_int_t virtual_extents;
+} cairo_quartz_surface_t;
+
+typedef struct cairo_quartz_image_surface {
+    cairo_surface_t base;
+
+    int width, height;
+
+    CGImageRef image;
+    cairo_image_surface_t *imageSurface;
+} cairo_quartz_image_surface_t;
+
+cairo_bool_t
+_cairo_quartz_verify_surface_size(int width, int height);
+
+CGImageRef
+CairoQuartzCreateCGImage (cairo_format_t format,
+                             unsigned int width,
+                             unsigned int height,
+                             unsigned int stride,
+                             void *data,
+                             cairo_bool_t interpolate,
+                             CGColorSpaceRef colorSpaceOverride,
+                             CGDataProviderReleaseDataCallback releaseCallback,
+                             void *releaseInfo);
+
+CGFontRef
+_cairo_quartz_scaled_font_get_cg_font_ref (cairo_scaled_font_t *sfont);
+
+#else
+
+# error Cairo was not compiled with support for the quartz backend
+
+#endif /* CAIRO_HAS_QUARTZ_SURFACE */
+
+#endif /* CAIRO_QUARTZ_PRIVATE_H */
diff --git a/src/cairo-quartz-surface.c b/src/cairo-quartz-surface.c
new file mode 100644 (file)
index 0000000..e18461b
--- /dev/null
@@ -0,0 +1,2563 @@
+/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright � 2006, 2007 Mozilla Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation.
+ *
+ * Contributor(s):
+ *     Vladimir Vukicevic <vladimir@mozilla.com>
+ */
+
+#define _GNU_SOURCE /* required for RTLD_DEFAULT */
+#include "cairoint.h"
+
+#include "cairo-quartz-private.h"
+
+#include "cairo-composite-rectangles-private.h"
+#include "cairo-compositor-private.h"
+#include "cairo-default-context-private.h"
+#include "cairo-error-private.h"
+#include "cairo-image-surface-private.h"
+#include "cairo-pattern-private.h"
+#include "cairo-surface-backend-private.h"
+#include "cairo-surface-clipper-private.h"
+
+#include <dlfcn.h>
+
+#ifndef RTLD_DEFAULT
+#define RTLD_DEFAULT ((void *) 0)
+#endif
+
+#include <limits.h>
+
+#undef QUARTZ_DEBUG
+
+#ifdef QUARTZ_DEBUG
+#define ND(_x) fprintf _x
+#else
+#define ND(_x) do {} while(0)
+#endif
+
+#define IS_EMPTY(s) ((s)->extents.width == 0 || (s)->extents.height == 0)
+
+/**
+ * SECTION:cairo-quartz
+ * @Title: Quartz Surfaces
+ * @Short_Description: Rendering to Quartz surfaces
+ * @See_Also: #cairo_surface_t
+ *
+ * The Quartz surface is used to render cairo graphics targeting the
+ * Apple OS X Quartz rendering system.
+ */
+
+/**
+ * CAIRO_HAS_QUARTZ_SURFACE:
+ *
+ * Defined if the Quartz surface backend is available.
+ * This macro can be used to conditionally compile backend-specific code.
+ */
+
+#if __MAC_OS_X_VERSION_MIN_REQUIRED < 1050
+/* This method is private, but it exists.  Its params are are exposed
+ * as args to the NS* method, but not as CG.
+ */
+enum PrivateCGCompositeMode {
+    kPrivateCGCompositeClear           = 0,
+    kPrivateCGCompositeCopy            = 1,
+    kPrivateCGCompositeSourceOver      = 2,
+    kPrivateCGCompositeSourceIn                = 3,
+    kPrivateCGCompositeSourceOut       = 4,
+    kPrivateCGCompositeSourceAtop      = 5,
+    kPrivateCGCompositeDestinationOver = 6,
+    kPrivateCGCompositeDestinationIn   = 7,
+    kPrivateCGCompositeDestinationOut  = 8,
+    kPrivateCGCompositeDestinationAtop = 9,
+    kPrivateCGCompositeXOR             = 10,
+    kPrivateCGCompositePlusDarker      = 11, // (max (0, (1-d) + (1-s)))
+    kPrivateCGCompositePlusLighter     = 12, // (min (1, s + d))
+};
+typedef enum PrivateCGCompositeMode PrivateCGCompositeMode;
+CG_EXTERN void CGContextSetCompositeOperation (CGContextRef, PrivateCGCompositeMode);
+#endif
+
+/* Some of these are present in earlier versions of the OS than where
+ * they are public; other are not public at all
+ */
+/* public since 10.5 */
+static void (*CGContextDrawTiledImagePtr) (CGContextRef, CGRect, CGImageRef) = NULL;
+
+/* public since 10.6 */
+static CGPathRef (*CGContextCopyPathPtr) (CGContextRef) = NULL;
+static void (*CGContextSetAllowsFontSmoothingPtr) (CGContextRef, bool) = NULL;
+
+/* not yet public */
+static unsigned int (*CGContextGetTypePtr) (CGContextRef) = NULL;
+static bool (*CGContextGetAllowsFontSmoothingPtr) (CGContextRef) = NULL;
+
+static cairo_bool_t _cairo_quartz_symbol_lookup_done = FALSE;
+
+/*
+ * Utility functions
+ */
+
+#ifdef QUARTZ_DEBUG
+static void quartz_surface_to_png (cairo_quartz_surface_t *nq, char *dest);
+static void quartz_image_to_png (CGImageRef, char *dest);
+#endif
+
+static cairo_quartz_surface_t *
+_cairo_quartz_surface_create_internal (CGContextRef cgContext,
+                                      cairo_content_t content,
+                                      unsigned int width,
+                                      unsigned int height);
+
+static cairo_bool_t
+_cairo_surface_is_quartz (const cairo_surface_t *surface);
+
+/* Load all extra symbols */
+static void quartz_ensure_symbols (void)
+{
+    if (likely (_cairo_quartz_symbol_lookup_done))
+       return;
+
+    CGContextDrawTiledImagePtr = dlsym (RTLD_DEFAULT, "CGContextDrawTiledImage");
+    CGContextGetTypePtr = dlsym (RTLD_DEFAULT, "CGContextGetType");
+    CGContextCopyPathPtr = dlsym (RTLD_DEFAULT, "CGContextCopyPath");
+    CGContextGetAllowsFontSmoothingPtr = dlsym (RTLD_DEFAULT, "CGContextGetAllowsFontSmoothing");
+    CGContextSetAllowsFontSmoothingPtr = dlsym (RTLD_DEFAULT, "CGContextSetAllowsFontSmoothing");
+
+    _cairo_quartz_symbol_lookup_done = TRUE;
+}
+
+CGImageRef
+CairoQuartzCreateCGImage (cairo_format_t format,
+                         unsigned int width,
+                         unsigned int height,
+                         unsigned int stride,
+                         void *data,
+                         cairo_bool_t interpolate,
+                         CGColorSpaceRef colorSpaceOverride,
+                         CGDataProviderReleaseDataCallback releaseCallback,
+                         void *releaseInfo)
+{
+    CGImageRef image = NULL;
+    CGDataProviderRef dataProvider = NULL;
+    CGColorSpaceRef colorSpace = colorSpaceOverride;
+    CGBitmapInfo bitinfo = kCGBitmapByteOrder32Host;
+    int bitsPerComponent, bitsPerPixel;
+
+    switch (format) {
+       case CAIRO_FORMAT_ARGB32:
+           if (colorSpace == NULL)
+               colorSpace = CGColorSpaceCreateDeviceRGB ();
+           bitinfo |= kCGImageAlphaPremultipliedFirst;
+           bitsPerComponent = 8;
+           bitsPerPixel = 32;
+           break;
+
+       case CAIRO_FORMAT_RGB24:
+           if (colorSpace == NULL)
+               colorSpace = CGColorSpaceCreateDeviceRGB ();
+           bitinfo |= kCGImageAlphaNoneSkipFirst;
+           bitsPerComponent = 8;
+           bitsPerPixel = 32;
+           break;
+
+       case CAIRO_FORMAT_A8:
+           bitsPerComponent = 8;
+           bitsPerPixel = 8;
+           break;
+
+       case CAIRO_FORMAT_A1:
+#ifdef WORDS_BIGENDIAN
+           bitsPerComponent = 1;
+           bitsPerPixel = 1;
+           break;
+#endif
+
+        case CAIRO_FORMAT_RGB16_565:
+        case CAIRO_FORMAT_INVALID:
+       default:
+           return NULL;
+    }
+
+    dataProvider = CGDataProviderCreateWithData (releaseInfo,
+                                                data,
+                                                height * stride,
+                                                releaseCallback);
+
+    if (unlikely (!dataProvider)) {
+       // manually release
+       if (releaseCallback)
+           releaseCallback (releaseInfo, data, height * stride);
+       goto FINISH;
+    }
+
+    if (format == CAIRO_FORMAT_A8 || format == CAIRO_FORMAT_A1) {
+       cairo_quartz_float_t decode[] = {1.0, 0.0};
+       image = CGImageMaskCreate (width, height,
+                                  bitsPerComponent,
+                                  bitsPerPixel,
+                                  stride,
+                                  dataProvider,
+                                  decode,
+                                  interpolate);
+    } else
+       image = CGImageCreate (width, height,
+                              bitsPerComponent,
+                              bitsPerPixel,
+                              stride,
+                              colorSpace,
+                              bitinfo,
+                              dataProvider,
+                              NULL,
+                              interpolate,
+                              kCGRenderingIntentDefault);
+
+FINISH:
+
+    CGDataProviderRelease (dataProvider);
+
+    if (colorSpace != colorSpaceOverride)
+       CGColorSpaceRelease (colorSpace);
+
+    return image;
+}
+
+static inline cairo_bool_t
+_cairo_quartz_is_cgcontext_bitmap_context (CGContextRef cgc)
+{
+    if (unlikely (cgc == NULL))
+       return FALSE;
+
+    if (likely (CGContextGetTypePtr)) {
+       /* 4 is the type value of a bitmap context */
+       return CGContextGetTypePtr (cgc) == 4;
+    }
+
+    /* This will cause a (harmless) warning to be printed if called on a non-bitmap context */
+    return CGBitmapContextGetBitsPerPixel (cgc) != 0;
+}
+
+/* CoreGraphics limitation with flipped CTM surfaces: height must be less than signed 16-bit max */
+
+#define CG_MAX_HEIGHT   SHRT_MAX
+#define CG_MAX_WIDTH    USHRT_MAX
+
+/* is the desired size of the surface within bounds? */
+cairo_bool_t
+_cairo_quartz_verify_surface_size (int width, int height)
+{
+    /* hmmm, allow width, height == 0 ? */
+    if (width < 0 || height < 0)
+       return FALSE;
+
+    if (width > CG_MAX_WIDTH || height > CG_MAX_HEIGHT)
+       return FALSE;
+
+    return TRUE;
+}
+
+/*
+ * Cairo path -> Quartz path conversion helpers
+ */
+
+/* cairo path -> execute in context */
+static cairo_status_t
+_cairo_path_to_quartz_context_move_to (void *closure,
+                                      const cairo_point_t *point)
+{
+    //ND ((stderr, "moveto: %f %f\n", _cairo_fixed_to_double (point->x), _cairo_fixed_to_double (point->y)));
+    double x = _cairo_fixed_to_double (point->x);
+    double y = _cairo_fixed_to_double (point->y);
+
+    CGContextMoveToPoint (closure, x, y);
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_path_to_quartz_context_line_to (void *closure,
+                                      const cairo_point_t *point)
+{
+    //ND ((stderr, "lineto: %f %f\n",  _cairo_fixed_to_double (point->x), _cairo_fixed_to_double (point->y)));
+    double x = _cairo_fixed_to_double (point->x);
+    double y = _cairo_fixed_to_double (point->y);
+
+    CGContextAddLineToPoint (closure, x, y);
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_path_to_quartz_context_curve_to (void *closure,
+                                       const cairo_point_t *p0,
+                                       const cairo_point_t *p1,
+                                       const cairo_point_t *p2)
+{
+    //ND ((stderr, "curveto: %f,%f %f,%f %f,%f\n",
+    //            _cairo_fixed_to_double (p0->x), _cairo_fixed_to_double (p0->y),
+    //            _cairo_fixed_to_double (p1->x), _cairo_fixed_to_double (p1->y),
+    //            _cairo_fixed_to_double (p2->x), _cairo_fixed_to_double (p2->y)));
+    double x0 = _cairo_fixed_to_double (p0->x);
+    double y0 = _cairo_fixed_to_double (p0->y);
+    double x1 = _cairo_fixed_to_double (p1->x);
+    double y1 = _cairo_fixed_to_double (p1->y);
+    double x2 = _cairo_fixed_to_double (p2->x);
+    double y2 = _cairo_fixed_to_double (p2->y);
+
+    CGContextAddCurveToPoint (closure, x0, y0, x1, y1, x2, y2);
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_path_to_quartz_context_close_path (void *closure)
+{
+    //ND ((stderr, "closepath\n"));
+    CGContextClosePath (closure);
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_quartz_cairo_path_to_quartz_context (const cairo_path_fixed_t *path,
+                                           CGContextRef closure)
+{
+    cairo_status_t status;
+
+    CGContextBeginPath (closure);
+    status = _cairo_path_fixed_interpret (path,
+                                         _cairo_path_to_quartz_context_move_to,
+                                         _cairo_path_to_quartz_context_line_to,
+                                         _cairo_path_to_quartz_context_curve_to,
+                                         _cairo_path_to_quartz_context_close_path,
+                                         closure);
+
+    assert (status == CAIRO_STATUS_SUCCESS);
+}
+
+/*
+ * Misc helpers/callbacks
+ */
+
+#if __MAC_OS_X_VERSION_MIN_REQUIRED < 1050
+static PrivateCGCompositeMode
+_cairo_quartz_cairo_operator_to_quartz_composite (cairo_operator_t op)
+{
+    switch (op) {
+       case CAIRO_OPERATOR_CLEAR:
+           return kPrivateCGCompositeClear;
+       case CAIRO_OPERATOR_SOURCE:
+           return kPrivateCGCompositeCopy;
+       case CAIRO_OPERATOR_OVER:
+           return kPrivateCGCompositeSourceOver;
+       case CAIRO_OPERATOR_IN:
+           return kPrivateCGCompositeSourceIn;
+       case CAIRO_OPERATOR_OUT:
+           return kPrivateCGCompositeSourceOut;
+       case CAIRO_OPERATOR_ATOP:
+           return kPrivateCGCompositeSourceAtop;
+       case CAIRO_OPERATOR_DEST_OVER:
+           return kPrivateCGCompositeDestinationOver;
+       case CAIRO_OPERATOR_DEST_IN:
+           return kPrivateCGCompositeDestinationIn;
+       case CAIRO_OPERATOR_DEST_OUT:
+           return kPrivateCGCompositeDestinationOut;
+       case CAIRO_OPERATOR_DEST_ATOP:
+           return kPrivateCGCompositeDestinationAtop;
+       case CAIRO_OPERATOR_XOR:
+           return kPrivateCGCompositeXOR;
+       case CAIRO_OPERATOR_ADD:
+           return kPrivateCGCompositePlusLighter;
+
+       case CAIRO_OPERATOR_DEST:
+       case CAIRO_OPERATOR_SATURATE:
+       case CAIRO_OPERATOR_MULTIPLY:
+       case CAIRO_OPERATOR_SCREEN:
+       case CAIRO_OPERATOR_OVERLAY:
+       case CAIRO_OPERATOR_DARKEN:
+       case CAIRO_OPERATOR_LIGHTEN:
+       case CAIRO_OPERATOR_COLOR_DODGE:
+       case CAIRO_OPERATOR_COLOR_BURN:
+       case CAIRO_OPERATOR_HARD_LIGHT:
+       case CAIRO_OPERATOR_SOFT_LIGHT:
+       case CAIRO_OPERATOR_DIFFERENCE:
+       case CAIRO_OPERATOR_EXCLUSION:
+       case CAIRO_OPERATOR_HSL_HUE:
+       case CAIRO_OPERATOR_HSL_SATURATION:
+       case CAIRO_OPERATOR_HSL_COLOR:
+       case CAIRO_OPERATOR_HSL_LUMINOSITY:
+        default:
+           ASSERT_NOT_REACHED;
+    }
+}
+#endif
+
+static CGBlendMode
+_cairo_quartz_cairo_operator_to_quartz_blend (cairo_operator_t op)
+{
+    switch (op) {
+       case CAIRO_OPERATOR_MULTIPLY:
+           return kCGBlendModeMultiply;
+       case CAIRO_OPERATOR_SCREEN:
+           return kCGBlendModeScreen;
+       case CAIRO_OPERATOR_OVERLAY:
+           return kCGBlendModeOverlay;
+       case CAIRO_OPERATOR_DARKEN:
+           return kCGBlendModeDarken;
+       case CAIRO_OPERATOR_LIGHTEN:
+           return kCGBlendModeLighten;
+       case CAIRO_OPERATOR_COLOR_DODGE:
+           return kCGBlendModeColorDodge;
+       case CAIRO_OPERATOR_COLOR_BURN:
+           return kCGBlendModeColorBurn;
+       case CAIRO_OPERATOR_HARD_LIGHT:
+           return kCGBlendModeHardLight;
+       case CAIRO_OPERATOR_SOFT_LIGHT:
+           return kCGBlendModeSoftLight;
+       case CAIRO_OPERATOR_DIFFERENCE:
+           return kCGBlendModeDifference;
+       case CAIRO_OPERATOR_EXCLUSION:
+           return kCGBlendModeExclusion;
+       case CAIRO_OPERATOR_HSL_HUE:
+           return kCGBlendModeHue;
+       case CAIRO_OPERATOR_HSL_SATURATION:
+           return kCGBlendModeSaturation;
+       case CAIRO_OPERATOR_HSL_COLOR:
+           return kCGBlendModeColor;
+       case CAIRO_OPERATOR_HSL_LUMINOSITY:
+           return kCGBlendModeLuminosity;
+
+#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1050
+       case CAIRO_OPERATOR_CLEAR:
+           return kCGBlendModeClear;
+       case CAIRO_OPERATOR_SOURCE:
+           return kCGBlendModeCopy;
+       case CAIRO_OPERATOR_OVER:
+           return kCGBlendModeNormal;
+       case CAIRO_OPERATOR_IN:
+           return kCGBlendModeSourceIn;
+       case CAIRO_OPERATOR_OUT:
+           return kCGBlendModeSourceOut;
+       case CAIRO_OPERATOR_ATOP:
+           return kCGBlendModeSourceAtop;
+       case CAIRO_OPERATOR_DEST_OVER:
+           return kCGBlendModeDestinationOver;
+       case CAIRO_OPERATOR_DEST_IN:
+           return kCGBlendModeDestinationIn;
+       case CAIRO_OPERATOR_DEST_OUT:
+           return kCGBlendModeDestinationOut;
+       case CAIRO_OPERATOR_DEST_ATOP:
+           return kCGBlendModeDestinationAtop;
+       case CAIRO_OPERATOR_XOR:
+           return kCGBlendModeXOR;
+       case CAIRO_OPERATOR_ADD:
+           return kCGBlendModePlusLighter;
+#else
+       case CAIRO_OPERATOR_CLEAR:
+       case CAIRO_OPERATOR_SOURCE:
+       case CAIRO_OPERATOR_OVER:
+       case CAIRO_OPERATOR_IN:
+       case CAIRO_OPERATOR_OUT:
+       case CAIRO_OPERATOR_ATOP:
+       case CAIRO_OPERATOR_DEST_OVER:
+       case CAIRO_OPERATOR_DEST_IN:
+       case CAIRO_OPERATOR_DEST_OUT:
+       case CAIRO_OPERATOR_DEST_ATOP:
+       case CAIRO_OPERATOR_XOR:
+       case CAIRO_OPERATOR_ADD:
+#endif
+
+       case CAIRO_OPERATOR_DEST:
+       case CAIRO_OPERATOR_SATURATE:
+        default:
+           ASSERT_NOT_REACHED;
+    }
+}
+
+static cairo_int_status_t
+_cairo_cgcontext_set_cairo_operator (CGContextRef context, cairo_operator_t op)
+{
+    CGBlendMode blendmode;
+
+    assert (op != CAIRO_OPERATOR_DEST);
+
+    /* Quartz doesn't support SATURATE at all. COLOR_DODGE and
+     * COLOR_BURN in Quartz follow the ISO32000 definition, but cairo
+     * uses the definition from the Adobe Supplement.
+     */
+    if (op == CAIRO_OPERATOR_SATURATE ||
+       op == CAIRO_OPERATOR_COLOR_DODGE ||
+       op == CAIRO_OPERATOR_COLOR_BURN)
+    {
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+    }
+
+#if __MAC_OS_X_VERSION_MIN_REQUIRED < 1050
+    if (op <= CAIRO_OPERATOR_ADD) {
+       PrivateCGCompositeMode compmode;
+
+       compmode = _cairo_quartz_cairo_operator_to_quartz_composite (op);
+       CGContextSetCompositeOperation (context, compmode);
+       return CAIRO_STATUS_SUCCESS;
+    }
+#endif
+
+    blendmode = _cairo_quartz_cairo_operator_to_quartz_blend (op);
+    CGContextSetBlendMode (context, blendmode);
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_quartz_surface_set_cairo_operator (cairo_quartz_surface_t *surface, cairo_operator_t op)
+{
+    ND((stderr, "%p _cairo_quartz_surface_set_cairo_operator %d\n", surface, op));
+
+    /* When the destination has no color components, we can avoid some
+     * fallbacks, but we have to workaround operators which behave
+     * differently in Quartz. */
+    if (surface->base.content == CAIRO_CONTENT_ALPHA) {
+       assert (op != CAIRO_OPERATOR_ATOP); /* filtered by surface layer */
+
+       if (op == CAIRO_OPERATOR_SOURCE ||
+           op == CAIRO_OPERATOR_IN ||
+           op == CAIRO_OPERATOR_OUT ||
+           op == CAIRO_OPERATOR_DEST_IN ||
+           op == CAIRO_OPERATOR_DEST_ATOP ||
+           op == CAIRO_OPERATOR_XOR)
+       {
+           return CAIRO_INT_STATUS_UNSUPPORTED;
+       }
+
+       if (op == CAIRO_OPERATOR_DEST_OVER)
+           op = CAIRO_OPERATOR_OVER;
+       else if (op == CAIRO_OPERATOR_SATURATE)
+           op = CAIRO_OPERATOR_ADD;
+       else if (op == CAIRO_OPERATOR_COLOR_DODGE)
+           op = CAIRO_OPERATOR_OVER;
+       else if (op == CAIRO_OPERATOR_COLOR_BURN)
+           op = CAIRO_OPERATOR_OVER;
+    }
+
+    return _cairo_cgcontext_set_cairo_operator (surface->cgContext, op);
+}
+
+static inline CGLineCap
+_cairo_quartz_cairo_line_cap_to_quartz (cairo_line_cap_t ccap)
+{
+    switch (ccap) {
+    default:
+       ASSERT_NOT_REACHED;
+
+    case CAIRO_LINE_CAP_BUTT:
+       return kCGLineCapButt;
+
+    case CAIRO_LINE_CAP_ROUND:
+       return kCGLineCapRound;
+
+    case CAIRO_LINE_CAP_SQUARE:
+       return kCGLineCapSquare;
+    }
+}
+
+static inline CGLineJoin
+_cairo_quartz_cairo_line_join_to_quartz (cairo_line_join_t cjoin)
+{
+    switch (cjoin) {
+    default:
+       ASSERT_NOT_REACHED;
+
+    case CAIRO_LINE_JOIN_MITER:
+       return kCGLineJoinMiter;
+
+    case CAIRO_LINE_JOIN_ROUND:
+       return kCGLineJoinRound;
+
+    case CAIRO_LINE_JOIN_BEVEL:
+       return kCGLineJoinBevel;
+    }
+}
+
+static inline CGInterpolationQuality
+_cairo_quartz_filter_to_quartz (cairo_filter_t filter)
+{
+    switch (filter) {
+    case CAIRO_FILTER_NEAREST:
+    case CAIRO_FILTER_FAST:
+       return kCGInterpolationNone;
+
+    case CAIRO_FILTER_BEST:
+    case CAIRO_FILTER_GOOD:
+    case CAIRO_FILTER_BILINEAR:
+    case CAIRO_FILTER_GAUSSIAN:
+       return kCGInterpolationDefault;
+
+    default:
+       ASSERT_NOT_REACHED;
+       return kCGInterpolationDefault;
+    }
+}
+
+static inline void
+_cairo_quartz_cairo_matrix_to_quartz (const cairo_matrix_t *src,
+                                     CGAffineTransform *dst)
+{
+    dst->a = src->xx;
+    dst->b = src->yx;
+    dst->c = src->xy;
+    dst->d = src->yy;
+    dst->tx = src->x0;
+    dst->ty = src->y0;
+}
+
+
+/*
+ * Source -> Quartz setup and finish functions
+ */
+
+static void
+ComputeGradientValue (void *info,
+                      const cairo_quartz_float_t *in,
+                      cairo_quartz_float_t *out)
+{
+    double fdist = *in;
+    const cairo_gradient_pattern_t *grad = (cairo_gradient_pattern_t*) info;
+    unsigned int i;
+
+    /* Put fdist back in the 0.0..1.0 range if we're doing
+     * REPEAT/REFLECT
+     */
+    if (grad->base.extend == CAIRO_EXTEND_REPEAT) {
+       fdist = fdist - floor (fdist);
+    } else if (grad->base.extend == CAIRO_EXTEND_REFLECT) {
+       fdist = fmod (fabs (fdist), 2.0);
+       if (fdist > 1.0)
+           fdist = 2.0 - fdist;
+    }
+
+    for (i = 0; i < grad->n_stops; i++)
+       if (grad->stops[i].offset > fdist)
+           break;
+
+    if (i == 0 || i == grad->n_stops) {
+       if (i == grad->n_stops)
+           --i;
+       out[0] = grad->stops[i].color.red;
+       out[1] = grad->stops[i].color.green;
+       out[2] = grad->stops[i].color.blue;
+       out[3] = grad->stops[i].color.alpha;
+    } else {
+       cairo_quartz_float_t ax = grad->stops[i-1].offset;
+       cairo_quartz_float_t bx = grad->stops[i].offset - ax;
+       cairo_quartz_float_t bp = (fdist - ax)/bx;
+       cairo_quartz_float_t ap = 1.0 - bp;
+
+       out[0] =
+           grad->stops[i-1].color.red * ap +
+           grad->stops[i].color.red * bp;
+       out[1] =
+           grad->stops[i-1].color.green * ap +
+           grad->stops[i].color.green * bp;
+       out[2] =
+           grad->stops[i-1].color.blue * ap +
+           grad->stops[i].color.blue * bp;
+       out[3] =
+           grad->stops[i-1].color.alpha * ap +
+           grad->stops[i].color.alpha * bp;
+    }
+}
+
+static const cairo_quartz_float_t gradient_output_value_ranges[8] = {
+    0.f, 1.f, 0.f, 1.f, 0.f, 1.f, 0.f, 1.f
+};
+static const CGFunctionCallbacks gradient_callbacks = {
+    0, ComputeGradientValue, (CGFunctionReleaseInfoCallback) cairo_pattern_destroy
+};
+
+/* Quartz computes a small number of samples of the gradient color
+ * function. On MacOS X 10.5 it apparently computes only 1024
+ * samples. */
+#define MAX_GRADIENT_RANGE 1024
+
+static CGFunctionRef
+CairoQuartzCreateGradientFunction (const cairo_gradient_pattern_t *gradient,
+                                  const cairo_rectangle_int_t    *extents,
+                                  cairo_circle_double_t          *start,
+                                  cairo_circle_double_t          *end)
+{
+    cairo_pattern_t *pat;
+    cairo_quartz_float_t input_value_range[2];
+
+    if (gradient->base.extend != CAIRO_EXTEND_NONE) {
+       double bounds_x1, bounds_x2, bounds_y1, bounds_y2;
+       double t[2], tolerance;
+
+       tolerance = fabs (_cairo_matrix_compute_determinant (&gradient->base.matrix));
+       tolerance /= _cairo_matrix_transformed_circle_major_axis (&gradient->base.matrix, 1);
+
+       bounds_x1 = extents->x;
+       bounds_y1 = extents->y;
+       bounds_x2 = extents->x + extents->width;
+       bounds_y2 = extents->y + extents->height;
+       _cairo_matrix_transform_bounding_box (&gradient->base.matrix,
+                                             &bounds_x1, &bounds_y1,
+                                             &bounds_x2, &bounds_y2,
+                                             NULL);
+
+       _cairo_gradient_pattern_box_to_parameter (gradient,
+                                                 bounds_x1, bounds_y1,
+                                                 bounds_x2, bounds_y2,
+                                                 tolerance,
+                                                 t);
+
+       if (gradient->base.extend == CAIRO_EXTEND_PAD) {
+           t[0] = MAX (t[0], -0.5);
+           t[1] = MIN (t[1],  1.5);
+       } else if (t[1] - t[0] > MAX_GRADIENT_RANGE)
+           return NULL;
+
+       /* set the input range for the function -- the function knows how
+          to map values outside of 0.0 .. 1.0 to the correct color */
+       input_value_range[0] = t[0];
+       input_value_range[1] = t[1];
+    } else {
+       input_value_range[0] = 0;
+       input_value_range[1] = 1;
+    }
+
+    _cairo_gradient_pattern_interpolate (gradient, input_value_range[0], start);
+    _cairo_gradient_pattern_interpolate (gradient, input_value_range[1], end);
+
+    if (_cairo_pattern_create_copy (&pat, &gradient->base))
+       return NULL;
+
+    return CGFunctionCreate (pat,
+                            1,
+                            input_value_range,
+                            4,
+                            gradient_output_value_ranges,
+                            &gradient_callbacks);
+}
+
+/* Obtain a CGImageRef from a #cairo_surface_t * */
+
+typedef struct {
+    cairo_surface_t *surface;
+    cairo_image_surface_t *image_out;
+    void *image_extra;
+} quartz_source_image_t;
+
+static void
+DataProviderReleaseCallback (void *info, const void *data, size_t size)
+{
+    quartz_source_image_t *source_img = info;
+    _cairo_surface_release_source_image (source_img->surface, source_img->image_out, source_img->image_extra);
+    free (source_img);
+}
+
+static cairo_status_t
+_cairo_surface_to_cgimage (cairo_surface_t *source,
+                          CGImageRef *image_out)
+{
+    cairo_status_t status;
+    quartz_source_image_t *source_img;
+
+    if (source->backend && source->backend->type == CAIRO_SURFACE_TYPE_QUARTZ_IMAGE) {
+       cairo_quartz_image_surface_t *surface = (cairo_quartz_image_surface_t *) source;
+       *image_out = CGImageRetain (surface->image);
+       return CAIRO_STATUS_SUCCESS;
+    }
+
+    if (_cairo_surface_is_quartz (source)) {
+       cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) source;
+       if (IS_EMPTY (surface)) {
+           *image_out = NULL;
+           return CAIRO_INT_STATUS_NOTHING_TO_DO;
+       }
+
+       if (_cairo_quartz_is_cgcontext_bitmap_context (surface->cgContext)) {
+           *image_out = CGBitmapContextCreateImage (surface->cgContext);
+           if (*image_out)
+               return CAIRO_STATUS_SUCCESS;
+       }
+    }
+
+    source_img = malloc (sizeof (quartz_source_image_t));
+    if (unlikely (source_img == NULL))
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    source_img->surface = source;
+
+    status = _cairo_surface_acquire_source_image (source_img->surface, &source_img->image_out, &source_img->image_extra);
+    if (unlikely (status)) {
+       free (source_img);
+       return status;
+    }
+
+    if (source_img->image_out->width == 0 || source_img->image_out->height == 0) {
+       *image_out = NULL;
+       DataProviderReleaseCallback (source_img,
+                                    source_img->image_out->data,
+                                    source_img->image_out->height * source_img->image_out->stride);
+    } else {
+       *image_out = CairoQuartzCreateCGImage (source_img->image_out->format,
+                                              source_img->image_out->width,
+                                              source_img->image_out->height,
+                                              source_img->image_out->stride,
+                                              source_img->image_out->data,
+                                              TRUE,
+                                              NULL,
+                                              DataProviderReleaseCallback,
+                                              source_img);
+
+       /* TODO: differentiate memory error and unsupported surface type */
+       if (unlikely (*image_out == NULL))
+           status = CAIRO_INT_STATUS_UNSUPPORTED;
+    }
+
+    return status;
+}
+
+/* Generic #cairo_pattern_t -> CGPattern function */
+
+typedef struct {
+    CGImageRef image;
+    CGRect imageBounds;
+    cairo_bool_t do_reflect;
+} SurfacePatternDrawInfo;
+
+static void
+SurfacePatternDrawFunc (void *ainfo, CGContextRef context)
+{
+    SurfacePatternDrawInfo *info = (SurfacePatternDrawInfo*) ainfo;
+
+    CGContextTranslateCTM (context, 0, info->imageBounds.size.height);
+    CGContextScaleCTM (context, 1, -1);
+
+    CGContextDrawImage (context, info->imageBounds, info->image);
+    if (info->do_reflect) {
+       /* draw 3 more copies of the image, flipped.
+        * DrawImage draws the image according to the current Y-direction into the rectangle given
+        * (imageBounds); at the time of the first DrawImage above, the origin is at the bottom left
+        * of the base image position, and the Y axis is extending upwards.
+        */
+
+       /* Make the y axis extend downwards, and draw a flipped image below */
+       CGContextScaleCTM (context, 1, -1);
+       CGContextDrawImage (context, info->imageBounds, info->image);
+
+       /* Shift over to the right, and flip vertically (translation is 2x,
+        * since we'll be flipping and thus rendering the rectangle "backwards"
+        */
+       CGContextTranslateCTM (context, 2 * info->imageBounds.size.width, 0);
+       CGContextScaleCTM (context, -1, 1);
+       CGContextDrawImage (context, info->imageBounds, info->image);
+
+       /* Then unflip the Y-axis again, and draw the image above the point. */
+       CGContextScaleCTM (context, 1, -1);
+       CGContextDrawImage (context, info->imageBounds, info->image);
+    }
+}
+
+static void
+SurfacePatternReleaseInfoFunc (void *ainfo)
+{
+    SurfacePatternDrawInfo *info = (SurfacePatternDrawInfo*) ainfo;
+
+    CGImageRelease (info->image);
+    free (info);
+}
+
+static cairo_int_status_t
+_cairo_quartz_cairo_repeating_surface_pattern_to_quartz (cairo_quartz_surface_t *dest,
+                                                        const cairo_pattern_t *apattern,
+                                                        CGPatternRef *cgpat)
+{
+    cairo_surface_pattern_t *spattern;
+    cairo_surface_t *pat_surf;
+    cairo_rectangle_int_t extents;
+
+    CGImageRef image;
+    CGRect pbounds;
+    CGAffineTransform ptransform, stransform;
+    CGPatternCallbacks cb = { 0,
+                             SurfacePatternDrawFunc,
+                             SurfacePatternReleaseInfoFunc };
+    SurfacePatternDrawInfo *info;
+    cairo_quartz_float_t rw, rh;
+    cairo_status_t status;
+    cairo_bool_t is_bounded;
+
+    cairo_matrix_t m;
+
+    /* SURFACE is the only type we'll handle here */
+    assert (apattern->type == CAIRO_PATTERN_TYPE_SURFACE);
+
+    spattern = (cairo_surface_pattern_t *) apattern;
+    pat_surf = spattern->surface;
+
+    is_bounded = _cairo_surface_get_extents (pat_surf, &extents);
+    assert (is_bounded);
+
+    status = _cairo_surface_to_cgimage (pat_surf, &image);
+    if (unlikely (status))
+       return status;
+
+    info = malloc (sizeof (SurfacePatternDrawInfo));
+    if (unlikely (!info))
+       return CAIRO_STATUS_NO_MEMORY;
+
+    /* XXX -- if we're printing, we may need to call CGImageCreateCopy to make sure
+     * that the data will stick around for this image when the printer gets to it.
+     * Otherwise, the underlying data store may disappear from under us!
+     *
+     * _cairo_surface_to_cgimage will copy when it converts non-Quartz surfaces,
+     * since the Quartz surfaces have a higher chance of sticking around.  If the
+     * source is a quartz image surface, then it's set up to retain a ref to the
+     * image surface that it's backed by.
+     */
+    info->image = image;
+    info->imageBounds = CGRectMake (0, 0, extents.width, extents.height);
+    info->do_reflect = FALSE;
+
+    pbounds.origin.x = 0;
+    pbounds.origin.y = 0;
+
+    if (spattern->base.extend == CAIRO_EXTEND_REFLECT) {
+       pbounds.size.width = 2.0 * extents.width;
+       pbounds.size.height = 2.0 * extents.height;
+       info->do_reflect = TRUE;
+    } else {
+       pbounds.size.width = extents.width;
+       pbounds.size.height = extents.height;
+    }
+    rw = pbounds.size.width;
+    rh = pbounds.size.height;
+
+    m = spattern->base.matrix;
+    cairo_matrix_invert (&m);
+    _cairo_quartz_cairo_matrix_to_quartz (&m, &stransform);
+
+    /* The pattern matrix is relative to the bottom left, again; the
+     * incoming cairo pattern matrix is relative to the upper left.
+     * So we take the pattern matrix and the original context matrix,
+     * which gives us the correct base translation/y flip.
+     */
+    ptransform = CGAffineTransformConcat (stransform, dest->cgContextBaseCTM);
+
+#ifdef QUARTZ_DEBUG
+    ND ((stderr, "  pbounds: %f %f %f %f\n", pbounds.origin.x, pbounds.origin.y, pbounds.size.width, pbounds.size.height));
+    ND ((stderr, "  pattern xform: t: %f %f xx: %f xy: %f yx: %f yy: %f\n", ptransform.tx, ptransform.ty, ptransform.a, ptransform.b, ptransform.c, ptransform.d));
+    CGAffineTransform xform = CGContextGetCTM (dest->cgContext);
+    ND ((stderr, "  context xform: t: %f %f xx: %f xy: %f yx: %f yy: %f\n", xform.tx, xform.ty, xform.a, xform.b, xform.c, xform.d));
+#endif
+
+    *cgpat = CGPatternCreate (info,
+                             pbounds,
+                             ptransform,
+                             rw, rh,
+                             kCGPatternTilingConstantSpacing, /* kCGPatternTilingNoDistortion, */
+                             TRUE,
+                             &cb);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+/* State used during a drawing operation. */
+typedef struct {
+    /* The destination of the mask */
+    CGContextRef cgMaskContext;
+
+    /* The destination of the drawing of the source */
+    CGContextRef cgDrawContext;
+
+    /* The filter to be used when drawing the source */
+    CGInterpolationQuality filter;
+
+    /* Action type */
+    cairo_quartz_action_t action;
+
+    /* Destination rect */
+    CGRect rect;
+
+    /* Used with DO_SHADING, DO_IMAGE and DO_TILED_IMAGE */
+    CGAffineTransform transform;
+
+    /* Used with DO_IMAGE and DO_TILED_IMAGE */
+    CGImageRef image;
+
+    /* Used with DO_SHADING */
+    CGShadingRef shading;
+
+    /* Temporary destination for unbounded operations */
+    CGLayerRef layer;
+    CGRect clipRect;
+} cairo_quartz_drawing_state_t;
+
+/*
+Quartz does not support repeating radients. We handle repeating gradients
+by manually extending the gradient and repeating color stops. We need to
+minimize the number of repetitions since Quartz seems to sample our color
+function across the entire range, even if part of that range is not needed
+for the visible area of the gradient, and it samples with some fixed resolution,
+so if the gradient range is too large it samples with very low resolution and
+the gradient is very coarse. _cairo_quartz_create_gradient_function computes
+the number of repetitions needed based on the extents.
+*/
+static cairo_int_status_t
+_cairo_quartz_setup_gradient_source (cairo_quartz_drawing_state_t *state,
+                                    const cairo_gradient_pattern_t *gradient,
+                                    const cairo_rectangle_int_t *extents)
+{
+    cairo_matrix_t mat;
+    cairo_circle_double_t start, end;
+    CGFunctionRef gradFunc;
+    CGColorSpaceRef rgb;
+    bool extend = gradient->base.extend != CAIRO_EXTEND_NONE;
+
+    assert (gradient->n_stops > 0);
+
+    mat = gradient->base.matrix;
+    cairo_matrix_invert (&mat);
+    _cairo_quartz_cairo_matrix_to_quartz (&mat, &state->transform);
+
+    gradFunc = CairoQuartzCreateGradientFunction (gradient, extents,
+                                                 &start, &end);
+
+    if (unlikely (gradFunc == NULL))
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    rgb = CGColorSpaceCreateDeviceRGB ();
+
+    if (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR) {
+       state->shading = CGShadingCreateAxial (rgb,
+                                              CGPointMake (start.center.x,
+                                                           start.center.y),
+                                              CGPointMake (end.center.x,
+                                                           end.center.y),
+                                              gradFunc,
+                                              extend, extend);
+    } else {
+       state->shading = CGShadingCreateRadial (rgb,
+                                               CGPointMake (start.center.x,
+                                                            start.center.y),
+                                               MAX (start.radius, 0),
+                                               CGPointMake (end.center.x,
+                                                            end.center.y),
+                                               MAX (end.radius, 0),
+                                               gradFunc,
+                                               extend, extend);
+    }
+
+    CGColorSpaceRelease (rgb);
+    CGFunctionRelease (gradFunc);
+
+    state->action = DO_SHADING;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_quartz_setup_state (cairo_quartz_drawing_state_t *state,
+                          cairo_composite_rectangles_t *extents)
+{
+    cairo_quartz_surface_t       *surface = (cairo_quartz_surface_t *) extents->surface;
+    cairo_operator_t              op = extents->op;
+    const cairo_pattern_t        *source = &extents->source_pattern.base;
+    const cairo_clip_t           *clip = extents->clip;
+    cairo_bool_t needs_temp;
+    cairo_status_t status;
+
+    state->layer = NULL;
+    state->image = NULL;
+    state->shading = NULL;
+    state->cgDrawContext = NULL;
+    state->cgMaskContext = NULL;
+
+    status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
+    if (unlikely (status))
+       return status;
+
+    status = _cairo_quartz_surface_set_cairo_operator (surface, op);
+    if (unlikely (status))
+       return status;
+
+    /* Save before we change the pattern, colorspace, etc. so that
+     * we can restore and make sure that quartz releases our
+     * pattern (which may be stack allocated)
+     */
+
+    CGContextSaveGState (surface->cgContext);
+    state->clipRect = CGContextGetClipBoundingBox (surface->cgContext);
+    state->clipRect = CGRectIntegral (state->clipRect);
+    state->rect = state->clipRect;
+
+    state->cgMaskContext = surface->cgContext;
+    state->cgDrawContext = state->cgMaskContext;
+
+    state->filter = _cairo_quartz_filter_to_quartz (source->filter);
+
+    if (op == CAIRO_OPERATOR_CLEAR) {
+       CGContextSetRGBFillColor (state->cgDrawContext, 0, 0, 0, 1);
+
+       state->action = DO_DIRECT;
+       return CAIRO_STATUS_SUCCESS;
+    }
+
+    /*
+     * To implement mask unbounded operations Quartz needs a temporary
+     * surface which will be composited entirely (ignoring the mask).
+     * To implement source unbounded operations Quartz needs a
+     * temporary surface which allows extending the source to a size
+     * covering the whole mask, but there are some optimization
+     * opportunities:
+     *
+     * - CLEAR completely ignores the source, thus we can just use a
+     *   solid color fill.
+     *
+     * - SOURCE can be implemented by drawing the source and clearing
+     *   outside of the source as long as the two regions have no
+     *   intersection. This happens when the source is a pixel-aligned
+     *   rectangle. If the source is at least as big as the
+     *   intersection between the clip rectangle and the mask
+     *   rectangle, no clear operation is needed.
+     */
+    needs_temp = ! _cairo_operator_bounded_by_mask (op);
+
+    if (needs_temp) {
+       state->layer = CGLayerCreateWithContext (surface->cgContext,
+                                                state->clipRect.size,
+                                                NULL);
+       state->cgDrawContext = CGLayerGetContext (state->layer);
+       state->cgMaskContext = state->cgDrawContext;
+       CGContextTranslateCTM (state->cgDrawContext,
+                              -state->clipRect.origin.x,
+                              -state->clipRect.origin.y);
+    }
+
+    if (source->type == CAIRO_PATTERN_TYPE_SOLID) {
+       cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) source;
+
+       CGContextSetRGBStrokeColor (state->cgDrawContext,
+                                   solid->color.red,
+                                   solid->color.green,
+                                   solid->color.blue,
+                                   solid->color.alpha);
+       CGContextSetRGBFillColor (state->cgDrawContext,
+                                 solid->color.red,
+                                 solid->color.green,
+                                 solid->color.blue,
+                                 solid->color.alpha);
+
+       state->action = DO_DIRECT;
+       return CAIRO_STATUS_SUCCESS;
+    }
+
+    if (source->type == CAIRO_PATTERN_TYPE_LINEAR ||
+       source->type == CAIRO_PATTERN_TYPE_RADIAL)
+    {
+       const cairo_gradient_pattern_t *gpat = (const cairo_gradient_pattern_t *)source;
+       cairo_rectangle_int_t extents;
+
+       extents = surface->virtual_extents;
+       extents.x -= surface->base.device_transform.x0;
+       extents.y -= surface->base.device_transform.y0;
+       _cairo_rectangle_union (&extents, &surface->extents);
+
+       return _cairo_quartz_setup_gradient_source (state, gpat, &extents);
+    }
+
+    if (source->type == CAIRO_PATTERN_TYPE_SURFACE &&
+       (source->extend == CAIRO_EXTEND_NONE || (CGContextDrawTiledImagePtr && source->extend == CAIRO_EXTEND_REPEAT)))
+    {
+       const cairo_surface_pattern_t *spat = (const cairo_surface_pattern_t *) source;
+       cairo_surface_t *pat_surf = spat->surface;
+       CGImageRef img;
+       cairo_matrix_t m = spat->base.matrix;
+       cairo_rectangle_int_t extents;
+       CGAffineTransform xform;
+       CGRect srcRect;
+       cairo_fixed_t fw, fh;
+       cairo_bool_t is_bounded;
+
+       status = _cairo_surface_to_cgimage (pat_surf, &img);
+       if (unlikely (status))
+           return status;
+
+       state->image = img;
+
+       if (state->filter == kCGInterpolationNone && _cairo_matrix_is_translation (&m)) {
+           m.x0 = -ceil (m.x0 - 0.5);
+           m.y0 = -ceil (m.y0 - 0.5);
+       } else {
+           cairo_matrix_invert (&m);
+       }
+
+       _cairo_quartz_cairo_matrix_to_quartz (&m, &state->transform);
+
+       is_bounded = _cairo_surface_get_extents (pat_surf, &extents);
+       assert (is_bounded);
+
+       srcRect = CGRectMake (0, 0, extents.width, extents.height);
+
+       if (source->extend == CAIRO_EXTEND_NONE) {
+           int x, y;
+           if (op == CAIRO_OPERATOR_SOURCE &&
+               (pat_surf->content == CAIRO_CONTENT_ALPHA ||
+                ! _cairo_matrix_is_integer_translation (&m, &x, &y)))
+           {
+               state->layer = CGLayerCreateWithContext (surface->cgContext,
+                                                        state->clipRect.size,
+                                                        NULL);
+               state->cgDrawContext = CGLayerGetContext (state->layer);
+               CGContextTranslateCTM (state->cgDrawContext,
+                                      -state->clipRect.origin.x,
+                                      -state->clipRect.origin.y);
+           }
+
+           CGContextSetRGBFillColor (state->cgDrawContext, 0, 0, 0, 1);
+
+           state->rect = srcRect;
+           state->action = DO_IMAGE;
+           return CAIRO_STATUS_SUCCESS;
+       }
+
+       CGContextSetRGBFillColor (state->cgDrawContext, 0, 0, 0, 1);
+
+       /* Quartz seems to tile images at pixel-aligned regions only -- this
+        * leads to seams if the image doesn't end up scaling to fill the
+        * space exactly.  The CGPattern tiling approach doesn't have this
+        * problem.  Check if we're going to fill up the space (within some
+        * epsilon), and if not, fall back to the CGPattern type.
+        */
+
+       xform = CGAffineTransformConcat (CGContextGetCTM (state->cgDrawContext),
+                                        state->transform);
+
+       srcRect = CGRectApplyAffineTransform (srcRect, xform);
+
+       fw = _cairo_fixed_from_double (srcRect.size.width);
+       fh = _cairo_fixed_from_double (srcRect.size.height);
+
+       if ((fw & CAIRO_FIXED_FRAC_MASK) <= CAIRO_FIXED_EPSILON &&
+           (fh & CAIRO_FIXED_FRAC_MASK) <= CAIRO_FIXED_EPSILON)
+       {
+           /* We're good to use DrawTiledImage, but ensure that
+            * the math works out */
+
+           srcRect.size.width = round (srcRect.size.width);
+           srcRect.size.height = round (srcRect.size.height);
+
+           xform = CGAffineTransformInvert (xform);
+
+           srcRect = CGRectApplyAffineTransform (srcRect, xform);
+
+           state->rect = srcRect;
+           state->action = DO_TILED_IMAGE;
+           return CAIRO_STATUS_SUCCESS;
+       }
+
+       /* Fall through to generic SURFACE case */
+    }
+
+    if (source->type == CAIRO_PATTERN_TYPE_SURFACE) {
+       cairo_quartz_float_t patternAlpha = 1.0f;
+       CGColorSpaceRef patternSpace;
+       CGPatternRef pattern = NULL;
+       cairo_int_status_t status;
+
+       status = _cairo_quartz_cairo_repeating_surface_pattern_to_quartz (surface, source, &pattern);
+       if (unlikely (status))
+           return status;
+
+       patternSpace = CGColorSpaceCreatePattern (NULL);
+       CGContextSetFillColorSpace (state->cgDrawContext, patternSpace);
+       CGContextSetFillPattern (state->cgDrawContext, pattern, &patternAlpha);
+       CGContextSetStrokeColorSpace (state->cgDrawContext, patternSpace);
+       CGContextSetStrokePattern (state->cgDrawContext, pattern, &patternAlpha);
+       CGColorSpaceRelease (patternSpace);
+
+       /* Quartz likes to munge the pattern phase (as yet unexplained
+        * why); force it to 0,0 as we've already baked in the correct
+        * pattern translation into the pattern matrix
+        */
+       CGContextSetPatternPhase (state->cgDrawContext, CGSizeMake (0, 0));
+
+       CGPatternRelease (pattern);
+
+       state->action = DO_DIRECT;
+       return CAIRO_STATUS_SUCCESS;
+    }
+
+    return CAIRO_INT_STATUS_UNSUPPORTED;
+}
+
+static void
+_cairo_quartz_teardown_state (cairo_quartz_drawing_state_t *state,
+                             cairo_composite_rectangles_t *extents)
+{
+    cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) extents->surface;
+
+    if (state->layer) {
+       CGContextDrawLayerInRect (surface->cgContext,
+                                 state->clipRect,
+                                 state->layer);
+       CGContextRelease (state->cgDrawContext);
+       CGLayerRelease (state->layer);
+    }
+
+    if (state->cgMaskContext)
+       CGContextRestoreGState (surface->cgContext);
+
+    if (state->image)
+       CGImageRelease (state->image);
+
+    if (state->shading)
+       CGShadingRelease (state->shading);
+}
+
+static void
+_cairo_quartz_draw_source (cairo_quartz_drawing_state_t *state,
+                          cairo_operator_t              op)
+{
+    CGContextSetShouldAntialias (state->cgDrawContext, state->filter != kCGInterpolationNone);
+    CGContextSetInterpolationQuality(state->cgDrawContext, state->filter);
+
+    if (state->action == DO_DIRECT) {
+       CGContextFillRect (state->cgDrawContext, state->rect);
+       return;
+    }
+
+    CGContextConcatCTM (state->cgDrawContext, state->transform);
+
+    if (state->action == DO_SHADING) {
+       CGContextDrawShading (state->cgDrawContext, state->shading);
+       return;
+    }
+
+    CGContextTranslateCTM (state->cgDrawContext, 0, state->rect.size.height);
+    CGContextScaleCTM (state->cgDrawContext, 1, -1);
+
+    if (state->action == DO_IMAGE) {
+       CGContextDrawImage (state->cgDrawContext, state->rect, state->image);
+       if (op == CAIRO_OPERATOR_SOURCE &&
+           state->cgDrawContext == state->cgMaskContext)
+       {
+           CGContextBeginPath (state->cgDrawContext);
+           CGContextAddRect (state->cgDrawContext, state->rect);
+
+           CGContextTranslateCTM (state->cgDrawContext, 0, state->rect.size.height);
+           CGContextScaleCTM (state->cgDrawContext, 1, -1);
+           CGContextConcatCTM (state->cgDrawContext,
+                               CGAffineTransformInvert (state->transform));
+
+           CGContextAddRect (state->cgDrawContext, state->clipRect);
+
+           CGContextSetRGBFillColor (state->cgDrawContext, 0, 0, 0, 0);
+           CGContextEOFillPath (state->cgDrawContext);
+       }
+    } else {
+       CGContextDrawTiledImagePtr (state->cgDrawContext, state->rect, state->image);
+    }
+}
+
+/*
+ * get source/dest image implementation
+ */
+
+/* Read the image from the surface's front buffer */
+static cairo_int_status_t
+_cairo_quartz_get_image (cairo_quartz_surface_t *surface,
+                        cairo_image_surface_t **image_out)
+{
+    unsigned char *imageData;
+    cairo_image_surface_t *isurf;
+
+    if (IS_EMPTY (surface)) {
+       *image_out = (cairo_image_surface_t*) cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 0, 0);
+       return CAIRO_STATUS_SUCCESS;
+    }
+
+    if (surface->imageSurfaceEquiv) {
+       *image_out = (cairo_image_surface_t*) cairo_surface_reference (surface->imageSurfaceEquiv);
+       return CAIRO_STATUS_SUCCESS;
+    }
+
+    if (_cairo_quartz_is_cgcontext_bitmap_context (surface->cgContext)) {
+       unsigned int stride;
+       unsigned int bitinfo;
+       unsigned int bpc, bpp;
+       CGColorSpaceRef colorspace;
+       unsigned int color_comps;
+
+       imageData = (unsigned char *) CGBitmapContextGetData (surface->cgContext);
+
+       bitinfo = CGBitmapContextGetBitmapInfo (surface->cgContext);
+       stride = CGBitmapContextGetBytesPerRow (surface->cgContext);
+       bpp = CGBitmapContextGetBitsPerPixel (surface->cgContext);
+       bpc = CGBitmapContextGetBitsPerComponent (surface->cgContext);
+
+       // let's hope they don't add YUV under us
+       colorspace = CGBitmapContextGetColorSpace (surface->cgContext);
+       color_comps = CGColorSpaceGetNumberOfComponents (colorspace);
+
+       // XXX TODO: We can handle all of these by converting to
+       // pixman masks, including non-native-endian masks
+       if (bpc != 8)
+           return CAIRO_INT_STATUS_UNSUPPORTED;
+
+       if (bpp != 32 && bpp != 8)
+           return CAIRO_INT_STATUS_UNSUPPORTED;
+
+       if (color_comps != 3 && color_comps != 1)
+           return CAIRO_INT_STATUS_UNSUPPORTED;
+
+       if (bpp == 32 && color_comps == 3 &&
+           (bitinfo & kCGBitmapAlphaInfoMask) == kCGImageAlphaPremultipliedFirst &&
+           (bitinfo & kCGBitmapByteOrderMask) == kCGBitmapByteOrder32Host)
+       {
+           isurf = (cairo_image_surface_t *)
+               cairo_image_surface_create_for_data (imageData,
+                                                    CAIRO_FORMAT_ARGB32,
+                                                    surface->extents.width,
+                                                    surface->extents.height,
+                                                    stride);
+       } else if (bpp == 32 && color_comps == 3 &&
+                  (bitinfo & kCGBitmapAlphaInfoMask) == kCGImageAlphaNoneSkipFirst &&
+                  (bitinfo & kCGBitmapByteOrderMask) == kCGBitmapByteOrder32Host)
+       {
+           isurf = (cairo_image_surface_t *)
+               cairo_image_surface_create_for_data (imageData,
+                                                    CAIRO_FORMAT_RGB24,
+                                                    surface->extents.width,
+                                                    surface->extents.height,
+                                                    stride);
+       } else if (bpp == 8 && color_comps == 1)
+       {
+           isurf = (cairo_image_surface_t *)
+               cairo_image_surface_create_for_data (imageData,
+                                                    CAIRO_FORMAT_A8,
+                                                    surface->extents.width,
+                                                    surface->extents.height,
+                                                    stride);
+       } else {
+           return CAIRO_INT_STATUS_UNSUPPORTED;
+       }
+    } else {
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+    }
+
+    *image_out = isurf;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+/*
+ * Cairo surface backend implementations
+ */
+
+static cairo_status_t
+_cairo_quartz_surface_finish (void *abstract_surface)
+{
+    cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
+
+    ND ((stderr, "_cairo_quartz_surface_finish[%p] cgc: %p\n", surface, surface->cgContext));
+
+    if (IS_EMPTY (surface))
+       return CAIRO_STATUS_SUCCESS;
+
+    /* Restore our saved gstate that we use to reset clipping */
+    CGContextRestoreGState (surface->cgContext);
+    _cairo_surface_clipper_reset (&surface->clipper);
+
+    CGContextRelease (surface->cgContext);
+
+    surface->cgContext = NULL;
+
+    if (surface->imageSurfaceEquiv) {
+       cairo_surface_destroy (surface->imageSurfaceEquiv);
+       surface->imageSurfaceEquiv = NULL;
+    }
+
+    free (surface->imageData);
+    surface->imageData = NULL;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_quartz_surface_acquire_source_image (void *abstract_surface,
+                                            cairo_image_surface_t **image_out,
+                                            void **image_extra)
+{
+    cairo_int_status_t status;
+    cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
+
+    //ND ((stderr, "%p _cairo_quartz_surface_acquire_source_image\n", surface));
+
+    status = _cairo_quartz_get_image (surface, image_out);
+    if (unlikely (status))
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    *image_extra = NULL;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_surface_t *
+_cairo_quartz_surface_snapshot (void *abstract_surface)
+{
+    cairo_int_status_t status;
+    cairo_quartz_surface_t *surface = abstract_surface;
+    cairo_image_surface_t *image;
+
+    if (surface->imageSurfaceEquiv)
+       return NULL;
+
+    status = _cairo_quartz_get_image (surface, &image);
+    if (unlikely (status))
+        return _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY);
+
+    return &image->base;
+}
+
+static void
+_cairo_quartz_surface_release_source_image (void *abstract_surface,
+                                           cairo_image_surface_t *image,
+                                           void *image_extra)
+{
+    cairo_surface_destroy (&image->base);
+}
+
+
+static cairo_surface_t *
+_cairo_quartz_surface_map_to_image (void *abstract_surface,
+                                   const cairo_rectangle_int_t *extents)
+{
+    cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
+    cairo_image_surface_t *image;
+    cairo_surface_t *subsurface;
+    cairo_status_t status;
+
+    status = _cairo_quartz_get_image (surface, &image);
+    if (unlikely (status))
+       return _cairo_surface_create_in_error (status);
+
+    /* Is this legitimate? shouldn't it return an image surface? */
+
+    subsurface = _cairo_surface_create_for_rectangle_int (&image->base, extents);
+    cairo_surface_destroy (&image->base);
+
+    return subsurface;
+}
+
+static cairo_int_status_t
+_cairo_quartz_surface_unmap_image (void *abstract_surface,
+                                  cairo_image_surface_t *image)
+{
+    cairo_surface_destroy (&image->base);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_surface_t *
+_cairo_quartz_surface_create_similar (void *abstract_surface,
+                                     cairo_content_t content,
+                                     int width,
+                                     int height)
+{
+    cairo_quartz_surface_t *surface, *similar_quartz;
+    cairo_surface_t *similar;
+    cairo_format_t format;
+
+    if (content == CAIRO_CONTENT_COLOR_ALPHA)
+       format = CAIRO_FORMAT_ARGB32;
+    else if (content == CAIRO_CONTENT_COLOR)
+       format = CAIRO_FORMAT_RGB24;
+    else if (content == CAIRO_CONTENT_ALPHA)
+       format = CAIRO_FORMAT_A8;
+    else
+       return NULL;
+
+    // verify width and height of surface
+    if (!_cairo_quartz_verify_surface_size (width, height)) {
+       return _cairo_surface_create_in_error (_cairo_error
+                                              (CAIRO_STATUS_INVALID_SIZE));
+    }
+
+    similar = cairo_quartz_surface_create (format, width, height);
+    if (unlikely (similar->status))
+       return similar;
+
+    surface = (cairo_quartz_surface_t *) abstract_surface;
+    similar_quartz = (cairo_quartz_surface_t *) similar;
+    similar_quartz->virtual_extents = surface->virtual_extents;
+
+    return similar;
+}
+
+static cairo_bool_t
+_cairo_quartz_surface_get_extents (void *abstract_surface,
+                                  cairo_rectangle_int_t *extents)
+{
+    cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
+
+    *extents = surface->extents;
+    return TRUE;
+}
+
+static cairo_int_status_t
+_cairo_quartz_cg_paint (const cairo_compositor_t *compositor,
+                       cairo_composite_rectangles_t *extents)
+{
+    cairo_quartz_drawing_state_t state;
+    cairo_int_status_t rv;
+
+    ND ((stderr, "%p _cairo_quartz_surface_paint op %d source->type %d\n", surface, op, source->type));
+
+    rv = _cairo_quartz_setup_state (&state, extents);
+    if (unlikely (rv))
+       goto BAIL;
+
+    _cairo_quartz_draw_source (&state, extents->op);
+
+BAIL:
+    _cairo_quartz_teardown_state (&state, extents);
+
+    ND ((stderr, "-- paint\n"));
+    return rv;
+}
+
+static cairo_int_status_t
+_cairo_quartz_cg_mask_with_surface (cairo_composite_rectangles_t *extents,
+                                   cairo_surface_t              *mask_surf,
+                                   const cairo_matrix_t         *mask_mat,
+                                   CGInterpolationQuality        filter)
+{
+    CGRect rect;
+    CGImageRef img;
+    cairo_status_t status;
+    CGAffineTransform mask_matrix;
+    cairo_quartz_drawing_state_t state;
+
+    status = _cairo_surface_to_cgimage (mask_surf, &img);
+    if (unlikely (status))
+       return status;
+
+    status = _cairo_quartz_setup_state (&state, extents);
+    if (unlikely (status))
+       goto BAIL;
+
+    rect = CGRectMake (0.0, 0.0, CGImageGetWidth (img), CGImageGetHeight (img));
+    _cairo_quartz_cairo_matrix_to_quartz (mask_mat, &mask_matrix);
+
+    /* ClipToMask is essentially drawing an image, so we need to flip the CTM
+     * to get the image to appear oriented the right way */
+    CGContextConcatCTM (state.cgMaskContext, CGAffineTransformInvert (mask_matrix));
+    CGContextTranslateCTM (state.cgMaskContext, 0.0, rect.size.height);
+    CGContextScaleCTM (state.cgMaskContext, 1.0, -1.0);
+
+    state.filter = filter;
+
+    CGContextSetInterpolationQuality (state.cgMaskContext, filter);
+    CGContextSetShouldAntialias (state.cgMaskContext, filter != kCGInterpolationNone);
+
+    CGContextClipToMask (state.cgMaskContext, rect, img);
+
+    CGContextScaleCTM (state.cgMaskContext, 1.0, -1.0);
+    CGContextTranslateCTM (state.cgMaskContext, 0.0, -rect.size.height);
+    CGContextConcatCTM (state.cgMaskContext, mask_matrix);
+
+    _cairo_quartz_draw_source (&state, extents->op);
+
+BAIL:
+    _cairo_quartz_teardown_state (&state, extents);
+
+    CGImageRelease (img);
+
+    return status;
+}
+
+static cairo_int_status_t
+_cairo_quartz_cg_mask_with_solid (cairo_quartz_surface_t *surface,
+                                 cairo_composite_rectangles_t *extents)
+{
+    cairo_quartz_drawing_state_t state;
+    double alpha = extents->mask_pattern.solid.color.alpha;
+    cairo_status_t status;
+
+    status = _cairo_quartz_setup_state (&state, extents);
+    if (unlikely (status))
+       return status;
+
+    CGContextSetAlpha (surface->cgContext, alpha);
+    _cairo_quartz_draw_source (&state, extents->op);
+
+    _cairo_quartz_teardown_state (&state, extents);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_quartz_cg_mask (const cairo_compositor_t *compositor,
+                      cairo_composite_rectangles_t *extents)
+{
+    cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *)extents->surface;
+    const cairo_pattern_t *source = &extents->source_pattern.base;
+    const cairo_pattern_t *mask = &extents->mask_pattern.base;
+    cairo_surface_t *mask_surf;
+    cairo_matrix_t matrix;
+    cairo_status_t status;
+    cairo_bool_t need_temp;
+    CGInterpolationQuality filter;
+
+    ND ((stderr, "%p _cairo_quartz_surface_mask op %d source->type %d mask->type %d\n", surface, op, source->type, mask->type));
+
+    if (mask->type == CAIRO_PATTERN_TYPE_SOLID)
+       return _cairo_quartz_cg_mask_with_solid (surface, extents);
+
+    need_temp = (mask->type   != CAIRO_PATTERN_TYPE_SURFACE ||
+                mask->extend != CAIRO_EXTEND_NONE);
+
+    filter = _cairo_quartz_filter_to_quartz (source->filter);
+
+    if (! need_temp) {
+       mask_surf = extents->mask_pattern.surface.surface;
+
+       /* When an opaque surface used as a mask in Quartz, its
+        * luminosity is used as the alpha value, so we con only use
+        * surfaces with alpha without creating a temporary mask. */
+       need_temp = ! (mask_surf->content & CAIRO_CONTENT_ALPHA);
+    }
+
+    if (! need_temp) {
+       CGInterpolationQuality mask_filter;
+       cairo_bool_t simple_transform;
+
+       matrix = mask->matrix;
+
+       mask_filter = _cairo_quartz_filter_to_quartz (mask->filter);
+       if (mask_filter == kCGInterpolationNone) {
+           simple_transform = _cairo_matrix_is_translation (&matrix);
+           if (simple_transform) {
+               matrix.x0 = ceil (matrix.x0 - 0.5);
+               matrix.y0 = ceil (matrix.y0 - 0.5);
+           }
+       } else {
+           simple_transform = _cairo_matrix_is_integer_translation (&matrix,
+                                                                    NULL,
+                                                                    NULL);
+       }
+
+       /* Quartz only allows one interpolation to be set for mask and
+        * source, so we can skip the temp surface only if the source
+        * filtering makes the mask look correct. */
+       if (source->type == CAIRO_PATTERN_TYPE_SURFACE)
+           need_temp = ! (simple_transform || filter == mask_filter);
+       else
+           filter = mask_filter;
+    }
+
+    if (need_temp) {
+       /* Render the mask to a surface */
+       mask_surf = _cairo_quartz_surface_create_similar (surface,
+                                                         CAIRO_CONTENT_ALPHA,
+                                                         surface->extents.width,
+                                                         surface->extents.height);
+       status = mask_surf->status;
+       if (unlikely (status))
+           goto BAIL;
+
+       /* mask_surf is clear, so use OVER instead of SOURCE to avoid a
+        * temporary layer or fallback to cairo-image. */
+       status = _cairo_surface_paint (mask_surf, CAIRO_OPERATOR_OVER, mask, NULL);
+       if (unlikely (status))
+           goto BAIL;
+
+       cairo_matrix_init_identity (&matrix);
+    }
+
+    status = _cairo_quartz_cg_mask_with_surface (extents,
+                                                mask_surf, &matrix, filter);
+
+BAIL:
+
+    if (need_temp)
+       cairo_surface_destroy (mask_surf);
+
+    return status;
+}
+
+static cairo_int_status_t
+_cairo_quartz_cg_fill (const cairo_compositor_t *compositor,
+                      cairo_composite_rectangles_t *extents,
+                      const cairo_path_fixed_t *path,
+                      cairo_fill_rule_t fill_rule,
+                      double tolerance,
+                      cairo_antialias_t antialias)
+{
+    cairo_quartz_drawing_state_t state;
+    cairo_int_status_t rv = CAIRO_STATUS_SUCCESS;
+
+    ND ((stderr, "%p _cairo_quartz_surface_fill op %d source->type %d\n", surface, op, source->type));
+
+    rv = _cairo_quartz_setup_state (&state, extents);
+    if (unlikely (rv))
+       goto BAIL;
+
+    CGContextSetShouldAntialias (state.cgMaskContext, (antialias != CAIRO_ANTIALIAS_NONE));
+
+    _cairo_quartz_cairo_path_to_quartz_context (path, state.cgMaskContext);
+
+    if (state.action == DO_DIRECT) {
+       assert (state.cgDrawContext == state.cgMaskContext);
+       if (fill_rule == CAIRO_FILL_RULE_WINDING)
+           CGContextFillPath (state.cgMaskContext);
+       else
+           CGContextEOFillPath (state.cgMaskContext);
+    } else {
+       if (fill_rule == CAIRO_FILL_RULE_WINDING)
+           CGContextClip (state.cgMaskContext);
+       else
+           CGContextEOClip (state.cgMaskContext);
+
+       _cairo_quartz_draw_source (&state, extents->op);
+    }
+
+BAIL:
+    _cairo_quartz_teardown_state (&state, extents);
+
+    ND ((stderr, "-- fill\n"));
+    return rv;
+}
+
+static cairo_int_status_t
+_cairo_quartz_cg_stroke (const cairo_compositor_t *compositor,
+                        cairo_composite_rectangles_t *extents,
+                        const cairo_path_fixed_t *path,
+                        const cairo_stroke_style_t *style,
+                        const cairo_matrix_t *ctm,
+                        const cairo_matrix_t *ctm_inverse,
+                        double tolerance,
+                        cairo_antialias_t antialias)
+{
+    cairo_quartz_drawing_state_t state;
+    cairo_int_status_t rv = CAIRO_STATUS_SUCCESS;
+    CGAffineTransform strokeTransform, invStrokeTransform;
+
+    ND ((stderr, "%p _cairo_quartz_surface_stroke op %d source->type %d\n", surface, op, source->type));
+
+    rv = _cairo_quartz_setup_state (&state, extents);
+    if (unlikely (rv))
+       goto BAIL;
+
+    // Turning antialiasing off used to cause misrendering with
+    // single-pixel lines (e.g. 20,10.5 -> 21,10.5 end up being rendered as 2 pixels).
+    // That's been since fixed in at least 10.5, and in the latest 10.4 dot releases.
+    CGContextSetShouldAntialias (state.cgMaskContext, (antialias != CAIRO_ANTIALIAS_NONE));
+    CGContextSetLineWidth (state.cgMaskContext, style->line_width);
+    CGContextSetLineCap (state.cgMaskContext, _cairo_quartz_cairo_line_cap_to_quartz (style->line_cap));
+    CGContextSetLineJoin (state.cgMaskContext, _cairo_quartz_cairo_line_join_to_quartz (style->line_join));
+    CGContextSetMiterLimit (state.cgMaskContext, style->miter_limit);
+
+    if (style->dash && style->num_dashes) {
+       cairo_quartz_float_t sdash[CAIRO_STACK_ARRAY_LENGTH (cairo_quartz_float_t)];
+       cairo_quartz_float_t *fdash = sdash;
+       unsigned int max_dashes = style->num_dashes;
+       unsigned int k;
+
+       if (style->num_dashes%2)
+           max_dashes *= 2;
+       if (max_dashes > ARRAY_LENGTH (sdash))
+           fdash = _cairo_malloc_ab (max_dashes, sizeof (cairo_quartz_float_t));
+       if (unlikely (fdash == NULL)) {
+           rv = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+           goto BAIL;
+       }
+
+       for (k = 0; k < max_dashes; k++)
+           fdash[k] = (cairo_quartz_float_t) style->dash[k % style->num_dashes];
+
+       CGContextSetLineDash (state.cgMaskContext, style->dash_offset, fdash, max_dashes);
+       if (fdash != sdash)
+           free (fdash);
+    } else
+       CGContextSetLineDash (state.cgMaskContext, 0, NULL, 0);
+
+    _cairo_quartz_cairo_path_to_quartz_context (path, state.cgMaskContext);
+
+    _cairo_quartz_cairo_matrix_to_quartz (ctm, &strokeTransform);
+    CGContextConcatCTM (state.cgMaskContext, strokeTransform);
+
+    if (state.action == DO_DIRECT) {
+       assert (state.cgDrawContext == state.cgMaskContext);
+       CGContextStrokePath (state.cgMaskContext);
+    } else {
+       CGContextReplacePathWithStrokedPath (state.cgMaskContext);
+       CGContextClip (state.cgMaskContext);
+
+       _cairo_quartz_cairo_matrix_to_quartz (ctm_inverse, &invStrokeTransform);
+       CGContextConcatCTM (state.cgMaskContext, invStrokeTransform);
+
+       _cairo_quartz_draw_source (&state, extents->op);
+    }
+
+BAIL:
+    _cairo_quartz_teardown_state (&state, extents);
+
+    ND ((stderr, "-- stroke\n"));
+    return rv;
+}
+
+#if CAIRO_HAS_QUARTZ_FONT
+static cairo_int_status_t
+_cairo_quartz_cg_glyphs (const cairo_compositor_t *compositor,
+                        cairo_composite_rectangles_t *extents,
+                        cairo_scaled_font_t *scaled_font,
+                        cairo_glyph_t *glyphs,
+                        int num_glyphs,
+                        cairo_bool_t overlap)
+{
+    CGAffineTransform textTransform, invTextTransform;
+    CGGlyph glyphs_static[CAIRO_STACK_ARRAY_LENGTH (CGSize)];
+    CGSize cg_advances_static[CAIRO_STACK_ARRAY_LENGTH (CGSize)];
+    CGGlyph *cg_glyphs = &glyphs_static[0];
+    CGSize *cg_advances = &cg_advances_static[0];
+    COMPILE_TIME_ASSERT (sizeof (CGGlyph) <= sizeof (CGSize));
+
+    cairo_quartz_drawing_state_t state;
+    cairo_int_status_t rv = CAIRO_INT_STATUS_UNSUPPORTED;
+    cairo_quartz_float_t xprev, yprev;
+    int i;
+    CGFontRef cgfref = NULL;
+
+    cairo_bool_t didForceFontSmoothing = FALSE;
+
+    if (cairo_scaled_font_get_type (scaled_font) != CAIRO_FONT_TYPE_QUARTZ)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    rv = _cairo_quartz_setup_state (&state, extents);
+    if (unlikely (rv))
+       goto BAIL;
+
+    if (state.action == DO_DIRECT) {
+       assert (state.cgDrawContext == state.cgMaskContext);
+       CGContextSetTextDrawingMode (state.cgMaskContext, kCGTextFill);
+    } else {
+       CGContextSetTextDrawingMode (state.cgMaskContext, kCGTextClip);
+    }
+
+    /* this doesn't addref */
+    cgfref = _cairo_quartz_scaled_font_get_cg_font_ref (scaled_font);
+    CGContextSetFont (state.cgMaskContext, cgfref);
+    CGContextSetFontSize (state.cgMaskContext, 1.0);
+
+    switch (scaled_font->options.antialias) {
+       case CAIRO_ANTIALIAS_SUBPIXEL:
+           CGContextSetShouldAntialias (state.cgMaskContext, TRUE);
+           CGContextSetShouldSmoothFonts (state.cgMaskContext, TRUE);
+           if (CGContextSetAllowsFontSmoothingPtr &&
+               !CGContextGetAllowsFontSmoothingPtr (state.cgMaskContext))
+           {
+               didForceFontSmoothing = TRUE;
+               CGContextSetAllowsFontSmoothingPtr (state.cgMaskContext, TRUE);
+           }
+           break;
+       case CAIRO_ANTIALIAS_NONE:
+           CGContextSetShouldAntialias (state.cgMaskContext, FALSE);
+           break;
+       case CAIRO_ANTIALIAS_GRAY:
+           CGContextSetShouldAntialias (state.cgMaskContext, TRUE);
+           CGContextSetShouldSmoothFonts (state.cgMaskContext, FALSE);
+           break;
+       case CAIRO_ANTIALIAS_DEFAULT:
+           /* Don't do anything */
+           break;
+    }
+
+    if (num_glyphs > ARRAY_LENGTH (glyphs_static)) {
+       cg_glyphs = (CGGlyph*) _cairo_malloc_ab (num_glyphs, sizeof (CGGlyph) + sizeof (CGSize));
+       if (unlikely (cg_glyphs == NULL)) {
+           rv = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+           goto BAIL;
+       }
+
+       cg_advances = (CGSize*) (cg_glyphs + num_glyphs);
+    }
+
+    /* scale(1,-1) * scaled_font->scale */
+    textTransform = CGAffineTransformMake (scaled_font->scale.xx,
+                                          scaled_font->scale.yx,
+                                          -scaled_font->scale.xy,
+                                          -scaled_font->scale.yy,
+                                          0.0, 0.0);
+
+    /* scaled_font->scale_inverse * scale(1,-1) */
+    invTextTransform = CGAffineTransformMake (scaled_font->scale_inverse.xx,
+                                             -scaled_font->scale_inverse.yx,
+                                             scaled_font->scale_inverse.xy,
+                                             -scaled_font->scale_inverse.yy,
+                                             0.0, 0.0);
+
+    CGContextSetTextPosition (state.cgMaskContext, 0.0, 0.0);
+    CGContextSetTextMatrix (state.cgMaskContext, CGAffineTransformIdentity);
+
+    /* Convert our glyph positions to glyph advances.  We need n-1 advances,
+     * since the advance at index 0 is applied after glyph 0. */
+    xprev = glyphs[0].x;
+    yprev = glyphs[0].y;
+
+    cg_glyphs[0] = glyphs[0].index;
+
+    for (i = 1; i < num_glyphs; i++) {
+       cairo_quartz_float_t xf = glyphs[i].x;
+       cairo_quartz_float_t yf = glyphs[i].y;
+       cg_glyphs[i] = glyphs[i].index;
+       cg_advances[i - 1] = CGSizeApplyAffineTransform (CGSizeMake (xf - xprev, yf - yprev), invTextTransform);
+       xprev = xf;
+       yprev = yf;
+    }
+
+    /* Translate to the first glyph's position before drawing */
+    CGContextTranslateCTM (state.cgMaskContext, glyphs[0].x, glyphs[0].y);
+    CGContextConcatCTM (state.cgMaskContext, textTransform);
+
+    CGContextShowGlyphsWithAdvances (state.cgMaskContext,
+                                    cg_glyphs,
+                                    cg_advances,
+                                    num_glyphs);
+
+    CGContextConcatCTM (state.cgMaskContext, invTextTransform);
+    CGContextTranslateCTM (state.cgMaskContext, -glyphs[0].x, -glyphs[0].y);
+
+    if (state.action != DO_DIRECT)
+       _cairo_quartz_draw_source (&state, extents->op);
+
+BAIL:
+    if (didForceFontSmoothing)
+       CGContextSetAllowsFontSmoothingPtr (state.cgMaskContext, FALSE);
+
+    _cairo_quartz_teardown_state (&state, extents);
+
+    if (cg_glyphs != glyphs_static)
+       free (cg_glyphs);
+
+    return rv;
+}
+#endif /* CAIRO_HAS_QUARTZ_FONT */
+
+static const cairo_compositor_t _cairo_quartz_cg_compositor = {
+    &_cairo_fallback_compositor,
+
+    _cairo_quartz_cg_paint,
+    _cairo_quartz_cg_mask,
+    _cairo_quartz_cg_stroke,
+    _cairo_quartz_cg_fill,
+#if CAIRO_HAS_QUARTZ_FONT
+    _cairo_quartz_cg_glyphs,
+#else
+    NULL,
+#endif
+};
+
+static cairo_int_status_t
+_cairo_quartz_surface_paint (void *surface,
+                            cairo_operator_t op,
+                            const cairo_pattern_t *source,
+                            const cairo_clip_t *clip)
+{
+    return _cairo_compositor_paint (&_cairo_quartz_cg_compositor,
+                                   surface, op, source, clip);
+}
+
+static cairo_int_status_t
+_cairo_quartz_surface_mask (void *surface,
+                           cairo_operator_t op,
+                           const cairo_pattern_t *source,
+                           const cairo_pattern_t *mask,
+                           const cairo_clip_t *clip)
+{
+    return _cairo_compositor_mask (&_cairo_quartz_cg_compositor,
+                                  surface, op, source, mask,
+                                  clip);
+}
+
+static cairo_int_status_t
+_cairo_quartz_surface_fill (void *surface,
+                           cairo_operator_t op,
+                           const cairo_pattern_t *source,
+                           const cairo_path_fixed_t *path,
+                           cairo_fill_rule_t fill_rule,
+                           double tolerance,
+                           cairo_antialias_t antialias,
+                           const cairo_clip_t *clip)
+{
+    return _cairo_compositor_fill (&_cairo_quartz_cg_compositor,
+                                  surface, op, source, path,
+                                  fill_rule, tolerance, antialias,
+                                  clip);
+}
+
+static cairo_int_status_t
+_cairo_quartz_surface_stroke (void *surface,
+                             cairo_operator_t op,
+                             const cairo_pattern_t *source,
+                             const cairo_path_fixed_t *path,
+                             const cairo_stroke_style_t *style,
+                             const cairo_matrix_t *ctm,
+                             const cairo_matrix_t *ctm_inverse,
+                             double tolerance,
+                             cairo_antialias_t antialias,
+                             const cairo_clip_t *clip)
+{
+    return _cairo_compositor_stroke (&_cairo_quartz_cg_compositor,
+                                    surface, op, source, path,
+                                    style, ctm,ctm_inverse,
+                                    tolerance, antialias, clip);
+}
+
+static cairo_int_status_t
+_cairo_quartz_surface_glyphs (void *surface,
+                             cairo_operator_t op,
+                             const cairo_pattern_t *source,
+                             cairo_glyph_t *glyphs,
+                             int num_glyphs,
+                             cairo_scaled_font_t *scaled_font,
+                             const cairo_clip_t *clip)
+{
+    return _cairo_compositor_glyphs (&_cairo_quartz_cg_compositor,
+                                    surface, op, source,
+                                    glyphs, num_glyphs, scaled_font,
+                                    clip);
+}
+
+static cairo_status_t
+_cairo_quartz_surface_clipper_intersect_clip_path (cairo_surface_clipper_t *clipper,
+                                                  cairo_path_fixed_t *path,
+                                                  cairo_fill_rule_t fill_rule,
+                                                  double tolerance,
+                                                  cairo_antialias_t antialias)
+{
+    cairo_quartz_surface_t *surface =
+       cairo_container_of (clipper, cairo_quartz_surface_t, clipper);
+
+    ND ((stderr, "%p _cairo_quartz_surface_intersect_clip_path path: %p\n", surface, path));
+
+    if (IS_EMPTY (surface))
+       return CAIRO_STATUS_SUCCESS;
+
+    if (path == NULL) {
+       /* If we're being asked to reset the clip, we can only do it
+        * by restoring the gstate to our previous saved one, and
+        * saving it again.
+        *
+        * Note that this assumes that ALL quartz surface creation
+        * functions will do a SaveGState first; we do this in create_internal.
+        */
+       CGContextRestoreGState (surface->cgContext);
+       CGContextSaveGState (surface->cgContext);
+    } else {
+       CGContextSetShouldAntialias (surface->cgContext, (antialias != CAIRO_ANTIALIAS_NONE));
+
+       _cairo_quartz_cairo_path_to_quartz_context (path, surface->cgContext);
+
+       if (fill_rule == CAIRO_FILL_RULE_WINDING)
+           CGContextClip (surface->cgContext);
+       else
+           CGContextEOClip (surface->cgContext);
+    }
+
+    ND ((stderr, "-- intersect_clip_path\n"));
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+// XXXtodo implement show_page; need to figure out how to handle begin/end
+
+static const struct _cairo_surface_backend cairo_quartz_surface_backend = {
+    CAIRO_SURFACE_TYPE_QUARTZ,
+    _cairo_quartz_surface_finish,
+
+    _cairo_default_context_create,
+
+    _cairo_quartz_surface_create_similar,
+    NULL, /* similar image */
+    _cairo_quartz_surface_map_to_image,
+    _cairo_quartz_surface_unmap_image,
+
+    _cairo_quartz_surface_acquire_source_image,
+    _cairo_quartz_surface_release_source_image,
+    _cairo_quartz_surface_snapshot,
+
+    NULL, /* copy_page */
+    NULL, /* show_page */
+
+    _cairo_quartz_surface_get_extents,
+    NULL, /* get_font_options */
+
+    NULL, /* flush */
+    NULL, /* mark_dirty_rectangle */
+
+    _cairo_quartz_surface_paint,
+    _cairo_quartz_surface_mask,
+    _cairo_quartz_surface_stroke,
+    _cairo_quartz_surface_fill,
+    NULL,  /* fill-stroke */
+    _cairo_quartz_surface_glyphs,
+};
+
+cairo_quartz_surface_t *
+_cairo_quartz_surface_create_internal (CGContextRef cgContext,
+                                      cairo_content_t content,
+                                      unsigned int width,
+                                      unsigned int height)
+{
+    cairo_quartz_surface_t *surface;
+
+    quartz_ensure_symbols ();
+
+    /* Init the base surface */
+    surface = malloc (sizeof (cairo_quartz_surface_t));
+    if (unlikely (surface == NULL))
+       return (cairo_quartz_surface_t*) _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+    memset (surface, 0, sizeof (cairo_quartz_surface_t));
+
+    _cairo_surface_init (&surface->base,
+                        &cairo_quartz_surface_backend,
+                        NULL, /* device */
+                        content);
+
+    _cairo_surface_clipper_init (&surface->clipper,
+                                _cairo_quartz_surface_clipper_intersect_clip_path);
+
+    /* Save our extents */
+    surface->extents.x = surface->extents.y = 0;
+    surface->extents.width = width;
+    surface->extents.height = height;
+    surface->virtual_extents = surface->extents;
+
+    if (IS_EMPTY (surface)) {
+       surface->cgContext = NULL;
+       surface->cgContextBaseCTM = CGAffineTransformIdentity;
+       surface->imageData = NULL;
+       return surface;
+    }
+
+    /* Save so we can always get back to a known-good CGContext -- this is
+     * required for proper behaviour of intersect_clip_path(NULL)
+     */
+    CGContextSaveGState (cgContext);
+
+    surface->cgContext = cgContext;
+    surface->cgContextBaseCTM = CGContextGetCTM (cgContext);
+
+    surface->imageData = NULL;
+    surface->imageSurfaceEquiv = NULL;
+
+    return surface;
+}
+
+/**
+ * cairo_quartz_surface_create_for_cg_context
+ * @cgContext: the existing CGContext for which to create the surface
+ * @width: width of the surface, in pixels
+ * @height: height of the surface, in pixels
+ *
+ * Creates a Quartz surface that wraps the given CGContext.  The
+ * CGContext is assumed to be in the standard Cairo coordinate space
+ * (that is, with the origin at the upper left and the Y axis
+ * increasing downward).  If the CGContext is in the Quartz coordinate
+ * space (with the origin at the bottom left), then it should be
+ * flipped before this function is called.  The flip can be accomplished
+ * using a translate and a scale; for example:
+ *
+ * <informalexample><programlisting>
+ * CGContextTranslateCTM (cgContext, 0.0, height);
+ * CGContextScaleCTM (cgContext, 1.0, -1.0);
+ * </programlisting></informalexample>
+ *
+ * All Cairo operations are implemented in terms of Quartz operations,
+ * as long as Quartz-compatible elements are used (such as Quartz fonts).
+ *
+ * Return value: the newly created Cairo surface.
+ *
+ * Since: 1.4
+ **/
+
+cairo_surface_t *
+cairo_quartz_surface_create_for_cg_context (CGContextRef cgContext,
+                                           unsigned int width,
+                                           unsigned int height)
+{
+    cairo_quartz_surface_t *surf;
+
+    surf = _cairo_quartz_surface_create_internal (cgContext, CAIRO_CONTENT_COLOR_ALPHA,
+                                                 width, height);
+    if (likely (!surf->base.status))
+       CGContextRetain (cgContext);
+
+    return &surf->base;
+}
+
+/**
+ * cairo_quartz_surface_create
+ * @format: format of pixels in the surface to create
+ * @width: width of the surface, in pixels
+ * @height: height of the surface, in pixels
+ *
+ * Creates a Quartz surface backed by a CGBitmap.  The surface is
+ * created using the Device RGB (or Device Gray, for A8) color space.
+ * All Cairo operations, including those that require software
+ * rendering, will succeed on this surface.
+ *
+ * Return value: the newly created surface.
+ *
+ * Since: 1.4
+ **/
+cairo_surface_t *
+cairo_quartz_surface_create (cairo_format_t format,
+                            unsigned int width,
+                            unsigned int height)
+{
+    cairo_quartz_surface_t *surf;
+    CGContextRef cgc;
+    CGColorSpaceRef cgColorspace;
+    CGBitmapInfo bitinfo;
+    void *imageData;
+    int stride;
+    int bitsPerComponent;
+
+    if (!_cairo_quartz_verify_surface_size (width, height))
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE));
+
+    if (width == 0 || height == 0) {
+       return &_cairo_quartz_surface_create_internal (NULL, _cairo_content_from_format (format),
+                                                      width, height)->base;
+    }
+
+    if (format == CAIRO_FORMAT_ARGB32 ||
+       format == CAIRO_FORMAT_RGB24)
+    {
+       cgColorspace = CGColorSpaceCreateDeviceRGB ();
+       bitinfo = kCGBitmapByteOrder32Host;
+       if (format == CAIRO_FORMAT_ARGB32)
+           bitinfo |= kCGImageAlphaPremultipliedFirst;
+       else
+           bitinfo |= kCGImageAlphaNoneSkipFirst;
+       bitsPerComponent = 8;
+       stride = width * 4;
+    } else if (format == CAIRO_FORMAT_A8) {
+       cgColorspace = NULL;
+       stride = width;
+       bitinfo = kCGImageAlphaOnly;
+       bitsPerComponent = 8;
+    } else if (format == CAIRO_FORMAT_A1) {
+       /* I don't think we can usefully support this, as defined by
+        * cairo_format_t -- these are 1-bit pixels stored in 32-bit
+        * quantities.
+        */
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
+    } else {
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
+    }
+
+    /* The Apple docs say that for best performance, the stride and the data
+     * pointer should be 16-byte aligned.  malloc already aligns to 16-bytes,
+     * so we don't have to anything special on allocation.
+     */
+    stride = (stride + 15) & ~15;
+
+    imageData = _cairo_malloc_ab (height, stride);
+    if (unlikely (!imageData)) {
+       CGColorSpaceRelease (cgColorspace);
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+    }
+
+    /* zero the memory to match the image surface behaviour */
+    memset (imageData, 0, height * stride);
+
+    cgc = CGBitmapContextCreate (imageData,
+                                width,
+                                height,
+                                bitsPerComponent,
+                                stride,
+                                cgColorspace,
+                                bitinfo);
+    CGColorSpaceRelease (cgColorspace);
+
+    if (!cgc) {
+       free (imageData);
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+    }
+
+    /* flip the Y axis */
+    CGContextTranslateCTM (cgc, 0.0, height);
+    CGContextScaleCTM (cgc, 1.0, -1.0);
+
+    surf = _cairo_quartz_surface_create_internal (cgc, _cairo_content_from_format (format),
+                                                 width, height);
+    if (surf->base.status) {
+       CGContextRelease (cgc);
+       free (imageData);
+       // create_internal will have set an error
+       return &surf->base;
+    }
+
+    surf->imageData = imageData;
+    surf->imageSurfaceEquiv = cairo_image_surface_create_for_data (imageData, format, width, height, stride);
+
+    return &surf->base;
+}
+
+/**
+ * cairo_quartz_surface_get_cg_context
+ * @surface: the Cairo Quartz surface
+ *
+ * Returns the CGContextRef that the given Quartz surface is backed
+ * by.
+ *
+ * A call to cairo_surface_flush() is required before using the
+ * CGContextRef to ensure that all pending drawing operations are
+ * finished and to restore any temporary modification cairo has made
+ * to its state. A call to cairo_surface_mark_dirty() is required
+ * after the state or the content of the CGContextRef has been
+ * modified.
+ *
+ * Return value: the CGContextRef for the given surface.
+ *
+ * Since: 1.4
+ **/
+CGContextRef
+cairo_quartz_surface_get_cg_context (cairo_surface_t *surface)
+{
+    if (surface && _cairo_surface_is_quartz (surface)) {
+       cairo_quartz_surface_t *quartz = (cairo_quartz_surface_t *) surface;
+       return quartz->cgContext;
+    } else
+       return NULL;
+}
+
+static cairo_bool_t
+_cairo_surface_is_quartz (const cairo_surface_t *surface)
+{
+    return surface->backend == &cairo_quartz_surface_backend;
+}
+
+/* Debug stuff */
+
+#ifdef QUARTZ_DEBUG
+
+#include <Movies.h>
+
+void ExportCGImageToPNGFile (CGImageRef inImageRef, char* dest)
+{
+    Handle  dataRef = NULL;
+    OSType  dataRefType;
+    CFStringRef inPath = CFStringCreateWithCString (NULL, dest, kCFStringEncodingASCII);
+
+    GraphicsExportComponent grex = 0;
+    unsigned long sizeWritten;
+
+    ComponentResult result;
+
+    // create the data reference
+    result = QTNewDataReferenceFromFullPathCFString (inPath, kQTNativeDefaultPathStyle,
+                                                    0, &dataRef, &dataRefType);
+
+    if (NULL != dataRef && noErr == result) {
+       // get the PNG exporter
+       result = OpenADefaultComponent (GraphicsExporterComponentType, kQTFileTypePNG,
+                                       &grex);
+
+       if (grex) {
+           // tell the exporter where to find its source image
+           result = GraphicsExportSetInputCGImage (grex, inImageRef);
+
+           if (noErr == result) {
+               // tell the exporter where to save the exporter image
+               result = GraphicsExportSetOutputDataReference (grex, dataRef,
+                                                              dataRefType);
+
+               if (noErr == result) {
+                   // write the PNG file
+                   result = GraphicsExportDoExport (grex, &sizeWritten);
+               }
+           }
+
+           // remember to close the component
+           CloseComponent (grex);
+       }
+
+       // remember to dispose of the data reference handle
+       DisposeHandle (dataRef);
+    }
+}
+
+void
+quartz_image_to_png (CGImageRef imgref, char *dest)
+{
+    static int sctr = 0;
+    char sptr[] = "/Users/vladimir/Desktop/barXXXXX.png";
+
+    if (dest == NULL) {
+       fprintf (stderr, "** Writing %p to bar%d\n", imgref, sctr);
+       sprintf (sptr, "/Users/vladimir/Desktop/bar%d.png", sctr);
+       sctr++;
+       dest = sptr;
+    }
+
+    ExportCGImageToPNGFile (imgref, dest);
+}
+
+void
+quartz_surface_to_png (cairo_quartz_surface_t *nq, char *dest)
+{
+    static int sctr = 0;
+    char sptr[] = "/Users/vladimir/Desktop/fooXXXXX.png";
+
+    if (nq->base.type != CAIRO_SURFACE_TYPE_QUARTZ) {
+       fprintf (stderr, "** quartz_surface_to_png: surface %p isn't quartz!\n", nq);
+       return;
+    }
+
+    if (dest == NULL) {
+       fprintf (stderr, "** Writing %p to foo%d\n", nq, sctr);
+       sprintf (sptr, "/Users/vladimir/Desktop/foo%d.png", sctr);
+       sctr++;
+       dest = sptr;
+    }
+
+    CGImageRef imgref = CGBitmapContextCreateImage (nq->cgContext);
+    if (imgref == NULL) {
+       fprintf (stderr, "quartz surface at %p is not a bitmap context!\n", nq);
+       return;
+    }
+
+    ExportCGImageToPNGFile (imgref, dest);
+
+    CGImageRelease (imgref);
+}
+
+#endif /* QUARTZ_DEBUG */
diff --git a/src/cairo-quartz.h b/src/cairo-quartz.h
new file mode 100644 (file)
index 0000000..9be5f9a
--- /dev/null
@@ -0,0 +1,82 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2006, 2007 Mozilla Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation.
+ *
+ * Contributor(s):
+ *      Vladimir Vukicevic <vladimir@mozilla.com>
+ */
+
+#ifndef CAIRO_QUARTZ_H
+#define CAIRO_QUARTZ_H
+
+#include "cairo.h"
+
+#if CAIRO_HAS_QUARTZ_SURFACE
+
+#include <ApplicationServices/ApplicationServices.h>
+
+CAIRO_BEGIN_DECLS
+
+cairo_public cairo_surface_t *
+cairo_quartz_surface_create (cairo_format_t format,
+                             unsigned int width,
+                             unsigned int height);
+
+cairo_public cairo_surface_t *
+cairo_quartz_surface_create_for_cg_context (CGContextRef cgContext,
+                                            unsigned int width,
+                                            unsigned int height);
+
+cairo_public CGContextRef
+cairo_quartz_surface_get_cg_context (cairo_surface_t *surface);
+
+#if CAIRO_HAS_QUARTZ_FONT
+
+/*
+ * Quartz font support
+ */
+
+cairo_public cairo_font_face_t *
+cairo_quartz_font_face_create_for_cgfont (CGFontRef font);
+
+cairo_public cairo_font_face_t *
+cairo_quartz_font_face_create_for_atsu_font_id (ATSUFontID font_id);
+
+#endif /* CAIRO_HAS_QUARTZ_FONT */
+
+CAIRO_END_DECLS
+
+#else
+
+# error Cairo was not compiled with support for the quartz backend
+
+#endif /* CAIRO_HAS_QUARTZ_SURFACE */
+
+#endif /* CAIRO_QUARTZ_H */
diff --git a/src/cairo-recording-surface-private.h b/src/cairo-recording-surface-private.h
new file mode 100644 (file)
index 0000000..6e93eab
--- /dev/null
@@ -0,0 +1,213 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2005 Red Hat, Inc
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ *     Kristian Høgsberg <krh@redhat.com>
+ *     Adrian Johnson <ajohnson@redneon.com>
+ */
+
+#ifndef CAIRO_RECORDING_SURFACE_H
+#define CAIRO_RECORDING_SURFACE_H
+
+#include "cairoint.h"
+#include "cairo-path-fixed-private.h"
+#include "cairo-pattern-private.h"
+#include "cairo-surface-backend-private.h"
+
+typedef enum {
+    /* The 5 basic drawing operations. */
+    CAIRO_COMMAND_PAINT,
+    CAIRO_COMMAND_MASK,
+    CAIRO_COMMAND_STROKE,
+    CAIRO_COMMAND_FILL,
+    CAIRO_COMMAND_SHOW_TEXT_GLYPHS,
+} cairo_command_type_t;
+
+typedef enum {
+    CAIRO_RECORDING_REGION_ALL,
+    CAIRO_RECORDING_REGION_NATIVE,
+    CAIRO_RECORDING_REGION_IMAGE_FALLBACK
+} cairo_recording_region_type_t;
+
+typedef struct _cairo_command_header {
+    cairo_command_type_t        type;
+    cairo_recording_region_type_t region;
+    cairo_operator_t            op;
+    cairo_rectangle_int_t       extents;
+    cairo_clip_t               *clip;
+
+    int index;
+    struct _cairo_command_header *chain;
+} cairo_command_header_t;
+
+typedef struct _cairo_command_paint {
+    cairo_command_header_t       header;
+    cairo_pattern_union_t       source;
+} cairo_command_paint_t;
+
+typedef struct _cairo_command_mask {
+    cairo_command_header_t       header;
+    cairo_pattern_union_t       source;
+    cairo_pattern_union_t       mask;
+} cairo_command_mask_t;
+
+typedef struct _cairo_command_stroke {
+    cairo_command_header_t       header;
+    cairo_pattern_union_t       source;
+    cairo_path_fixed_t          path;
+    cairo_stroke_style_t        style;
+    cairo_matrix_t              ctm;
+    cairo_matrix_t              ctm_inverse;
+    double                      tolerance;
+    cairo_antialias_t           antialias;
+} cairo_command_stroke_t;
+
+typedef struct _cairo_command_fill {
+    cairo_command_header_t       header;
+    cairo_pattern_union_t       source;
+    cairo_path_fixed_t          path;
+    cairo_fill_rule_t           fill_rule;
+    double                      tolerance;
+    cairo_antialias_t           antialias;
+} cairo_command_fill_t;
+
+typedef struct _cairo_command_show_text_glyphs {
+    cairo_command_header_t       header;
+    cairo_pattern_union_t       source;
+    char                       *utf8;
+    int                                 utf8_len;
+    cairo_glyph_t              *glyphs;
+    unsigned int                num_glyphs;
+    cairo_text_cluster_t       *clusters;
+    int                                 num_clusters;
+    cairo_text_cluster_flags_t   cluster_flags;
+    cairo_scaled_font_t                *scaled_font;
+} cairo_command_show_text_glyphs_t;
+
+typedef union _cairo_command {
+    cairo_command_header_t      header;
+
+    cairo_command_paint_t                      paint;
+    cairo_command_mask_t                       mask;
+    cairo_command_stroke_t                     stroke;
+    cairo_command_fill_t                       fill;
+    cairo_command_show_text_glyphs_t           show_text_glyphs;
+} cairo_command_t;
+
+typedef struct _cairo_recording_surface {
+    cairo_surface_t base;
+
+    /* A recording-surface is logically unbounded, but when used as a
+     * source we need to render it to an image, so we need a size at
+     * which to create that image. */
+    cairo_rectangle_t extents_pixels;
+    cairo_rectangle_int_t extents;
+    cairo_bool_t unbounded;
+
+    cairo_array_t commands;
+    int *indices;
+    int num_indices;
+    cairo_bool_t optimize_clears;
+
+    struct bbtree {
+       cairo_box_t extents;
+       struct bbtree *left, *right;
+       cairo_command_header_t *chain;
+    } bbtree;
+} cairo_recording_surface_t;
+
+slim_hidden_proto (cairo_recording_surface_create);
+
+cairo_private cairo_int_status_t
+_cairo_recording_surface_get_path (cairo_surface_t      *surface,
+                                  cairo_path_fixed_t *path);
+
+cairo_private cairo_status_t
+_cairo_recording_surface_replay_one (cairo_recording_surface_t *surface,
+                                    long unsigned index,
+                                    cairo_surface_t *target);
+
+cairo_private cairo_status_t
+_cairo_recording_surface_replay (cairo_surface_t *surface,
+                                cairo_surface_t *target);
+
+cairo_private cairo_status_t
+_cairo_recording_surface_replay_with_clip (cairo_surface_t *surface,
+                                          const cairo_matrix_t *surface_transform,
+                                          cairo_surface_t *target,
+                                          const cairo_clip_t *target_clip);
+
+cairo_private cairo_status_t
+_cairo_recording_surface_replay_and_create_regions (cairo_surface_t *surface,
+                                                   cairo_surface_t *target);
+cairo_private cairo_status_t
+_cairo_recording_surface_replay_region (cairo_surface_t                        *surface,
+                                       const cairo_rectangle_int_t *surface_extents,
+                                       cairo_surface_t                 *target,
+                                       cairo_recording_region_type_t   region);
+
+cairo_private cairo_status_t
+_cairo_recording_surface_get_bbox (cairo_recording_surface_t *recording,
+                                  cairo_box_t *bbox,
+                                  const cairo_matrix_t *transform);
+
+cairo_private cairo_status_t
+_cairo_recording_surface_get_ink_bbox (cairo_recording_surface_t *surface,
+                                      cairo_box_t *bbox,
+                                      const cairo_matrix_t *transform);
+
+static inline cairo_bool_t
+_cairo_recording_surface_get_bounds (cairo_surface_t *surface,
+                                    cairo_rectangle_t *extents)
+{
+    cairo_recording_surface_t *recording = (cairo_recording_surface_t *)surface;
+    if (recording->unbounded)
+       return FALSE;
+
+    *extents = recording->extents_pixels;
+    return TRUE;
+}
+
+/**
+ * _cairo_surface_is_recording:
+ * @surface: a #cairo_surface_t
+ *
+ * Checks if a surface is a #cairo_recording_surface_t
+ *
+ * Return value: %TRUE if the surface is a recording surface
+ **/
+static inline cairo_bool_t
+_cairo_surface_is_recording (const cairo_surface_t *surface)
+{
+    return surface->backend->type == CAIRO_SURFACE_TYPE_RECORDING;
+}
+
+#endif /* CAIRO_RECORDING_SURFACE_H */
diff --git a/src/cairo-recording-surface.c b/src/cairo-recording-surface.c
new file mode 100644 (file)
index 0000000..5316a10
--- /dev/null
@@ -0,0 +1,1714 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2005 Red Hat, Inc
+ * Copyright © 2007 Adrian Johnson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ *     Kristian Høgsberg <krh@redhat.com>
+ *     Carl Worth <cworth@cworth.org>
+ *     Adrian Johnson <ajohnson@redneon.com>
+ */
+
+/**
+ * SECTION:cairo-recording
+ * @Title: Recording Surfaces
+ * @Short_Description: Records all drawing operations
+ * @See_Also: #cairo_surface_t
+ *
+ * A recording surface is a surface that records all drawing operations at
+ * the highest level of the surface backend interface, (that is, the
+ * level of paint, mask, stroke, fill, and show_text_glyphs). The recording
+ * surface can then be "replayed" against any target surface by using it
+ * as a source surface.
+ *
+ * If you want to replay a surface so that the results in target will be
+ * identical to the results that would have been obtained if the original
+ * operations applied to the recording surface had instead been applied to the
+ * target surface, you can use code like this:
+ * <informalexample><programlisting>
+ * cairo_t *cr;
+ *
+ * cr = cairo_create (target);
+ * cairo_set_source_surface (cr, recording_surface, 0.0, 0.0);
+ * cairo_paint (cr);
+ * cairo_destroy (cr);
+ * </programlisting></informalexample>
+ *
+ * A recording surface is logically unbounded, i.e. it has no implicit constraint
+ * on the size of the drawing surface. However, in practice this is rarely
+ * useful as you wish to replay against a particular target surface with
+ * known bounds. For this case, it is more efficient to specify the target
+ * extents to the recording surface upon creation.
+ *
+ * The recording phase of the recording surface is careful to snapshot all
+ * necessary objects (paths, patterns, etc.), in order to achieve
+ * accurate replay. The efficiency of the recording surface could be
+ * improved by improving the implementation of snapshot for the
+ * various objects. For example, it would be nice to have a
+ * copy-on-write implementation for _cairo_surface_snapshot.
+ */
+
+#include "cairoint.h"
+
+#include "cairo-array-private.h"
+#include "cairo-analysis-surface-private.h"
+#include "cairo-clip-private.h"
+#include "cairo-combsort-private.h"
+#include "cairo-composite-rectangles-private.h"
+#include "cairo-default-context-private.h"
+#include "cairo-error-private.h"
+#include "cairo-image-surface-private.h"
+#include "cairo-recording-surface-private.h"
+#include "cairo-surface-wrapper-private.h"
+#include "cairo-traps-private.h"
+
+typedef enum {
+    CAIRO_RECORDING_REPLAY,
+    CAIRO_RECORDING_CREATE_REGIONS
+} cairo_recording_replay_type_t;
+
+static const cairo_surface_backend_t cairo_recording_surface_backend;
+
+/**
+ * CAIRO_HAS_RECORDING_SURFACE:
+ *
+ * Defined if the recording surface backend is available.
+ * The recording surface backend is always built in.
+ * This macro was added for completeness in cairo 1.10.
+ *
+ * Since: 1.10
+ */
+
+/* Currently all recording surfaces do have a size which should be passed
+ * in as the maximum size of any target surface against which the
+ * recording-surface will ever be replayed.
+ *
+ * XXX: The naming of "pixels" in the size here is a misnomer. It's
+ * actually a size in whatever device-space units are desired (again,
+ * according to the intended replay target).
+ */
+
+static int bbtree_left_or_right (struct bbtree *bbt,
+                                const cairo_box_t *box)
+{
+    int left, right;
+
+    if (bbt->left) {
+       cairo_box_t *e = &bbt->left->extents;
+       cairo_box_t b;
+
+       b.p1.x = MIN (e->p1.x, box->p1.x);
+       b.p1.y = MIN (e->p1.y, box->p1.y);
+       b.p2.x = MAX (e->p2.x, box->p2.x);
+       b.p2.y = MAX (e->p2.y, box->p2.y);
+
+       left = _cairo_fixed_integer_part (b.p2.x - b.p1.x) * _cairo_fixed_integer_part (b.p2.y - b.p1.y);
+       left -= _cairo_fixed_integer_part (e->p2.x - e->p1.x) * _cairo_fixed_integer_part (e->p2.y - e->p1.y);
+    } else
+       left = 0;
+
+    if (bbt->right) {
+       cairo_box_t *e = &bbt->right->extents;
+       cairo_box_t b;
+
+       b.p1.x = MIN (e->p1.x, box->p1.x);
+       b.p1.y = MIN (e->p1.y, box->p1.y);
+       b.p2.x = MAX (e->p2.x, box->p2.x);
+       b.p2.y = MAX (e->p2.y, box->p2.y);
+
+       right = _cairo_fixed_integer_part (b.p2.x - b.p1.x) * _cairo_fixed_integer_part (b.p2.y - b.p1.y);
+       right -= _cairo_fixed_integer_part (e->p2.x - e->p1.x) * _cairo_fixed_integer_part (e->p2.y - e->p1.y);
+    } else
+       right = 0;
+
+    return left <= right;
+}
+
+#define INVALID_CHAIN ((cairo_command_header_t *)-1)
+
+static struct bbtree *
+bbtree_new (const cairo_box_t *box, cairo_command_header_t *chain)
+{
+    struct bbtree *bbt = malloc (sizeof (*bbt));
+    if (bbt == NULL)
+       return NULL;
+    bbt->extents = *box;
+    bbt->left = bbt->right = NULL;
+    bbt->chain = chain;
+    return bbt;
+}
+
+static void
+bbtree_init (struct bbtree *bbt, cairo_command_header_t *header)
+{
+    _cairo_box_from_rectangle (&bbt->extents, &header->extents);
+    bbt->chain = header;
+}
+
+static cairo_status_t
+bbtree_add (struct bbtree *bbt,
+           cairo_command_header_t *header,
+           const cairo_box_t *box)
+{
+    if (box->p1.x < bbt->extents.p1.x || box->p1.y < bbt->extents.p1.y ||
+       box->p2.x > bbt->extents.p2.x || box->p2.y > bbt->extents.p2.y)
+    {
+       if (bbt->chain) {
+           if (bbtree_left_or_right (bbt, &bbt->extents)) {
+               if (bbt->left == NULL) {
+                   bbt->left = bbtree_new (&bbt->extents, bbt->chain);
+                   if (unlikely (bbt->left == NULL))
+                       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+               } else
+                   bbtree_add (bbt->left, bbt->chain, &bbt->extents);
+           } else {
+               if (bbt->right == NULL) {
+                   bbt->right = bbtree_new (&bbt->extents, bbt->chain);
+                   if (unlikely (bbt->right == NULL))
+                       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+               } else
+                   bbtree_add (bbt->right, bbt->chain, &bbt->extents);
+           }
+
+           bbt->chain = NULL;
+       }
+
+       bbt->extents.p1.x = MIN (bbt->extents.p1.x, box->p1.x);
+       bbt->extents.p1.y = MIN (bbt->extents.p1.y, box->p1.y);
+       bbt->extents.p2.x = MAX (bbt->extents.p2.x, box->p2.x);
+       bbt->extents.p2.y = MAX (bbt->extents.p2.y, box->p2.y);
+    }
+
+    if (box->p1.x == bbt->extents.p1.x && box->p1.y == bbt->extents.p1.y &&
+       box->p2.x == bbt->extents.p2.x && box->p2.y == bbt->extents.p2.y)
+    {
+       header->chain = bbt->chain;
+       bbt->chain = header;
+       return CAIRO_STATUS_SUCCESS;
+    }
+
+    if (bbtree_left_or_right (bbt, box)) {
+       if (bbt->left == NULL) {
+           bbt->left = bbtree_new (box, header);
+           if (unlikely (bbt->left == NULL))
+               return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+       } else
+           return bbtree_add (bbt->left, header, box);
+    } else {
+       if (bbt->right == NULL) {
+           bbt->right = bbtree_new (box, header);
+           if (unlikely (bbt->right == NULL))
+               return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+       } else
+           return bbtree_add (bbt->right, header, box);
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void bbtree_del (struct bbtree *bbt)
+{
+    if (bbt->left)
+       bbtree_del (bbt->left);
+    if (bbt->right)
+       bbtree_del (bbt->right);
+
+    free (bbt);
+}
+
+static cairo_bool_t box_outside (const cairo_box_t *a, const cairo_box_t *b)
+{
+    return
+       a->p1.x >= b->p2.x || a->p1.y >= b->p2.y ||
+       a->p2.x <= b->p1.x || a->p2.y <= b->p1.y;
+}
+
+static void
+bbtree_foreach_mark_visible (struct bbtree *bbt,
+                            const cairo_box_t *box,
+                            int **indices)
+{
+    cairo_command_header_t *chain;
+
+    for (chain = bbt->chain; chain; chain = chain->chain)
+       *(*indices)++ = chain->index;
+
+    if (bbt->left && ! box_outside (box, &bbt->left->extents))
+       bbtree_foreach_mark_visible (bbt->left, box, indices);
+    if (bbt->right && ! box_outside (box, &bbt->right->extents))
+       bbtree_foreach_mark_visible (bbt->right, box, indices);
+}
+
+static inline int intcmp (const int a, const int b)
+{
+    return a - b;
+}
+CAIRO_COMBSORT_DECLARE (sort_indices, int, intcmp)
+
+static inline int sizecmp (int a, int b, cairo_command_header_t **elements)
+{
+    const cairo_rectangle_int_t *r;
+
+    r = &elements[a]->extents;
+    a = r->width * r->height;
+
+    r = &elements[b]->extents;
+    b = r->width * r->height;
+
+    return b - a;
+}
+CAIRO_COMBSORT_DECLARE_WITH_DATA (sort_commands, int, sizecmp)
+
+static void
+_cairo_recording_surface_destroy_bbtree (cairo_recording_surface_t *surface)
+{
+    cairo_command_t **elements;
+    int i, num_elements;
+
+    if (surface->bbtree.chain == INVALID_CHAIN)
+       return;
+
+    if (surface->bbtree.left) {
+       bbtree_del (surface->bbtree.left);
+       surface->bbtree.left = NULL;
+    }
+    if (surface->bbtree.right) {
+       bbtree_del (surface->bbtree.right);
+       surface->bbtree.right = NULL;
+    }
+
+    elements = _cairo_array_index (&surface->commands, 0);
+    num_elements = surface->commands.num_elements;
+    for (i = 0; i < num_elements; i++)
+       elements[i]->header.chain = NULL;
+
+    surface->bbtree.chain = INVALID_CHAIN;
+}
+
+static cairo_status_t
+_cairo_recording_surface_create_bbtree (cairo_recording_surface_t *surface)
+{
+    cairo_command_t **elements = _cairo_array_index (&surface->commands, 0);
+    cairo_status_t status;
+    int i, count;
+    int *indices;
+
+    count = surface->commands.num_elements;
+    if (count > surface->num_indices) {
+       free (surface->indices);
+       surface->indices = _cairo_malloc_ab (count, sizeof (int));
+       if (unlikely (surface->indices == NULL))
+           return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+       surface->num_indices = count;
+    }
+
+    indices = surface->indices;
+    for (i = 0; i < count; i++)
+       indices[i] = i;
+
+    sort_commands (indices, count, elements);
+
+    bbtree_init (&surface->bbtree, &elements[indices[0]]->header);
+    for (i = 1; i < count; i++) {
+       cairo_command_header_t *header = &elements[indices[i]]->header;
+       cairo_box_t box;
+
+       _cairo_box_from_rectangle (&box, &header->extents);
+       status = bbtree_add (&surface->bbtree, header, &box);
+       if (unlikely (status))
+           goto cleanup;
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+
+cleanup:
+    bbtree_del (&surface->bbtree);
+    return status;
+}
+
+/**
+ * cairo_recording_surface_create:
+ * @content: the content of the recording surface
+ * @extents: the extents to record in pixels, can be %NULL to record
+ *           unbounded operations.
+ *
+ * Creates a recording-surface which can be used to record all drawing operations
+ * at the highest level (that is, the level of paint, mask, stroke, fill
+ * and show_text_glyphs). The recording surface can then be "replayed" against
+ * any target surface by using it as a source to drawing operations.
+ *
+ * The recording phase of the recording surface is careful to snapshot all
+ * necessary objects (paths, patterns, etc.), in order to achieve
+ * accurate replay.
+ *
+ * Return value: a pointer to the newly created surface. The caller
+ * owns the surface and should call cairo_surface_destroy() when done
+ * with it.
+ *
+ * Since: 1.10
+ **/
+cairo_surface_t *
+cairo_recording_surface_create (cairo_content_t                 content,
+                               const cairo_rectangle_t *extents)
+{
+    cairo_recording_surface_t *surface;
+
+    surface = malloc (sizeof (cairo_recording_surface_t));
+    if (unlikely (surface == NULL))
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+    _cairo_surface_init (&surface->base,
+                        &cairo_recording_surface_backend,
+                        NULL, /* device */
+                        content);
+
+
+    surface->unbounded = TRUE;
+
+    /* unbounded -> 'infinite' extents */
+    if (extents != NULL) {
+       surface->extents_pixels = *extents;
+
+       /* XXX check for overflow */
+       surface->extents.x = floor (extents->x);
+       surface->extents.y = floor (extents->y);
+       surface->extents.width = ceil (extents->x + extents->width) - surface->extents.x;
+       surface->extents.height = ceil (extents->y + extents->height) - surface->extents.y;
+
+       surface->unbounded = FALSE;
+    }
+
+    _cairo_array_init (&surface->commands, sizeof (cairo_command_t *));
+
+    surface->base.is_clear = TRUE;
+
+    surface->bbtree.left = surface->bbtree.right = NULL;
+    surface->bbtree.chain = INVALID_CHAIN;
+
+    surface->indices = NULL;
+    surface->num_indices = 0;
+    surface->optimize_clears = TRUE;
+
+    return &surface->base;
+}
+slim_hidden_def (cairo_recording_surface_create);
+
+static cairo_surface_t *
+_cairo_recording_surface_create_similar (void                 *abstract_surface,
+                                        cairo_content_t        content,
+                                        int                    width,
+                                        int                    height)
+{
+    cairo_rectangle_t extents;
+    extents.x = extents.y = 0;
+    extents.width = width;
+    extents.height = height;
+    return cairo_recording_surface_create (content, &extents);
+}
+
+static cairo_status_t
+_cairo_recording_surface_finish (void *abstract_surface)
+{
+    cairo_recording_surface_t *surface = abstract_surface;
+    cairo_command_t **elements;
+    int i, num_elements;
+
+    num_elements = surface->commands.num_elements;
+    elements = _cairo_array_index (&surface->commands, 0);
+    for (i = 0; i < num_elements; i++) {
+       cairo_command_t *command = elements[i];
+
+       switch (command->header.type) {
+       case CAIRO_COMMAND_PAINT:
+           _cairo_pattern_fini (&command->paint.source.base);
+           break;
+
+       case CAIRO_COMMAND_MASK:
+           _cairo_pattern_fini (&command->mask.source.base);
+           _cairo_pattern_fini (&command->mask.mask.base);
+           break;
+
+       case CAIRO_COMMAND_STROKE:
+           _cairo_pattern_fini (&command->stroke.source.base);
+           _cairo_path_fixed_fini (&command->stroke.path);
+           _cairo_stroke_style_fini (&command->stroke.style);
+           break;
+
+       case CAIRO_COMMAND_FILL:
+           _cairo_pattern_fini (&command->fill.source.base);
+           _cairo_path_fixed_fini (&command->fill.path);
+           break;
+
+       case CAIRO_COMMAND_SHOW_TEXT_GLYPHS:
+           _cairo_pattern_fini (&command->show_text_glyphs.source.base);
+           free (command->show_text_glyphs.utf8);
+           free (command->show_text_glyphs.glyphs);
+           free (command->show_text_glyphs.clusters);
+           cairo_scaled_font_destroy (command->show_text_glyphs.scaled_font);
+           break;
+
+       default:
+           ASSERT_NOT_REACHED;
+       }
+
+       _cairo_clip_destroy (command->header.clip);
+       free (command);
+    }
+
+    _cairo_array_fini (&surface->commands);
+
+    if (surface->bbtree.left)
+       bbtree_del (surface->bbtree.left);
+    if (surface->bbtree.right)
+       bbtree_del (surface->bbtree.right);
+
+    free (surface->indices);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+struct proxy {
+    cairo_surface_t base;
+    cairo_surface_t *image;
+};
+
+static cairo_status_t
+proxy_acquire_source_image (void                        *abstract_surface,
+                           cairo_image_surface_t       **image_out,
+                           void                        **image_extra)
+{
+    struct proxy *proxy = abstract_surface;
+    return _cairo_surface_acquire_source_image (proxy->image, image_out, image_extra);
+}
+
+static void
+proxy_release_source_image (void                       *abstract_surface,
+                           cairo_image_surface_t       *image,
+                           void                        *image_extra)
+{
+    struct proxy *proxy = abstract_surface;
+    _cairo_surface_release_source_image (proxy->image, image, image_extra);
+}
+
+static cairo_status_t
+proxy_finish (void *abstract_surface)
+{
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static const cairo_surface_backend_t proxy_backend  = {
+    CAIRO_INTERNAL_SURFACE_TYPE_NULL,
+    proxy_finish,
+    NULL,
+
+    NULL, /* create similar */
+    NULL, /* create similar image */
+    NULL, /* map to image */
+    NULL, /* unmap image */
+
+    proxy_acquire_source_image,
+    proxy_release_source_image,
+};
+
+static cairo_surface_t *
+attach_proxy (cairo_surface_t *source,
+             cairo_surface_t *image)
+{
+    struct proxy *proxy;
+
+    proxy = malloc (sizeof (*proxy));
+    if (unlikely (proxy == NULL))
+       return _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY);
+
+    _cairo_surface_init (&proxy->base, &proxy_backend, NULL, image->content);
+
+    proxy->image = image;
+    _cairo_surface_attach_snapshot (source, &proxy->base, NULL);
+
+    return &proxy->base;
+}
+
+static void
+detach_proxy (cairo_surface_t *source,
+             cairo_surface_t *proxy)
+{
+    cairo_surface_finish (proxy);
+    cairo_surface_destroy (proxy);
+}
+
+static cairo_surface_t *
+get_proxy (cairo_surface_t *proxy)
+{
+    return ((struct proxy *)proxy)->image;
+}
+
+static cairo_status_t
+_cairo_recording_surface_acquire_source_image (void                     *abstract_surface,
+                                              cairo_image_surface_t    **image_out,
+                                              void                     **image_extra)
+{
+    cairo_recording_surface_t *surface = abstract_surface;
+    cairo_surface_t *image, *proxy;
+    cairo_status_t status;
+
+    proxy = _cairo_surface_has_snapshot (abstract_surface, &proxy_backend);
+    if (proxy != NULL) {
+       *image_out = (cairo_image_surface_t *)
+           cairo_surface_reference (get_proxy (proxy));
+       *image_extra = NULL;
+       return CAIRO_STATUS_SUCCESS;
+    }
+
+    assert (! surface->unbounded);
+    image = _cairo_image_surface_create_with_content (surface->base.content,
+                                                     surface->extents.width,
+                                                     surface->extents.height);
+    if (unlikely (image->status))
+       return image->status;
+
+    /* Handle recursion by returning future reads from the current image */
+    proxy = attach_proxy (abstract_surface, image);
+    status = _cairo_recording_surface_replay (&surface->base, image);
+    detach_proxy (abstract_surface, proxy);
+
+    if (unlikely (status)) {
+       cairo_surface_destroy (image);
+       return status;
+    }
+
+    *image_out = (cairo_image_surface_t *) image;
+    *image_extra = NULL;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_recording_surface_release_source_image (void                    *abstract_surface,
+                                              cairo_image_surface_t    *image,
+                                              void                     *image_extra)
+{
+    cairo_surface_destroy (&image->base);
+}
+
+static cairo_status_t
+_command_init (cairo_recording_surface_t *surface,
+              cairo_command_header_t *command,
+              cairo_command_type_t type,
+              cairo_operator_t op,
+              cairo_composite_rectangles_t *composite)
+{
+    cairo_status_t status = CAIRO_STATUS_SUCCESS;
+
+    command->type = type;
+    command->op = op;
+    command->region = CAIRO_RECORDING_REGION_ALL;
+
+    command->extents = composite->unbounded;
+    command->chain = NULL;
+    command->index = surface->commands.num_elements;
+
+    /* steal the clip */
+    command->clip = NULL;
+    if (! _cairo_composite_rectangles_can_reduce_clip (composite,
+                                                      composite->clip))
+    {
+       command->clip = composite->clip;
+       composite->clip = NULL;
+    }
+
+    return status;
+}
+
+static void
+_cairo_recording_surface_break_self_copy_loop (cairo_recording_surface_t *surface)
+{
+    cairo_surface_flush (&surface->base);
+}
+
+static cairo_status_t
+_cairo_recording_surface_commit (cairo_recording_surface_t *surface,
+                                cairo_command_header_t *command)
+{
+    _cairo_recording_surface_break_self_copy_loop (surface);
+    return _cairo_array_append (&surface->commands, &command);
+}
+
+static void
+_cairo_recording_surface_reset (cairo_recording_surface_t *surface)
+{
+    /* Reset the commands and temporaries */
+    _cairo_recording_surface_finish (surface);
+
+    surface->bbtree.left = surface->bbtree.right = NULL;
+    surface->bbtree.chain = INVALID_CHAIN;
+
+    surface->indices = NULL;
+    surface->num_indices = 0;
+
+    _cairo_array_init (&surface->commands, sizeof (cairo_command_t *));
+}
+
+static cairo_int_status_t
+_cairo_recording_surface_paint (void                     *abstract_surface,
+                               cairo_operator_t           op,
+                               const cairo_pattern_t     *source,
+                               const cairo_clip_t        *clip)
+{
+    cairo_status_t status;
+    cairo_recording_surface_t *surface = abstract_surface;
+    cairo_command_paint_t *command;
+    cairo_composite_rectangles_t composite;
+
+    if (op == CAIRO_OPERATOR_CLEAR && clip == NULL) {
+       if (surface->optimize_clears) {
+           _cairo_recording_surface_reset (surface);
+           return CAIRO_STATUS_SUCCESS;
+       }
+    }
+
+    if (clip == NULL && surface->optimize_clears &&
+       source->type == CAIRO_PATTERN_TYPE_SOLID &&
+       (op == CAIRO_OPERATOR_SOURCE ||
+        (op == CAIRO_OPERATOR_OVER &&
+         (surface->base.is_clear || _cairo_pattern_is_opaque_solid (source)))))
+    {
+       _cairo_recording_surface_reset (surface);
+    }
+
+    status = _cairo_composite_rectangles_init_for_paint (&composite,
+                                                        &surface->base,
+                                                        op, source,
+                                                        clip);
+    if (unlikely (status))
+       return status;
+
+    command = malloc (sizeof (cairo_command_paint_t));
+    if (unlikely (command == NULL)) {
+       status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+       goto CLEANUP_COMPOSITE;
+    }
+
+    status = _command_init (surface,
+                           &command->header, CAIRO_COMMAND_PAINT, op,
+                           &composite);
+    if (unlikely (status))
+       goto CLEANUP_COMMAND;
+
+    status = _cairo_pattern_init_snapshot (&command->source.base, source);
+    if (unlikely (status))
+       goto CLEANUP_COMMAND;
+
+    status = _cairo_recording_surface_commit (surface, &command->header);
+    if (unlikely (status))
+       goto CLEANUP_SOURCE;
+
+    _cairo_recording_surface_destroy_bbtree (surface);
+
+    _cairo_composite_rectangles_fini (&composite);
+    return CAIRO_STATUS_SUCCESS;
+
+  CLEANUP_SOURCE:
+    _cairo_pattern_fini (&command->source.base);
+  CLEANUP_COMMAND:
+    _cairo_clip_destroy (command->header.clip);
+    free (command);
+CLEANUP_COMPOSITE:
+    _cairo_composite_rectangles_fini (&composite);
+    return status;
+}
+
+static cairo_int_status_t
+_cairo_recording_surface_mask (void                    *abstract_surface,
+                              cairo_operator_t          op,
+                              const cairo_pattern_t    *source,
+                              const cairo_pattern_t    *mask,
+                              const cairo_clip_t       *clip)
+{
+    cairo_status_t status;
+    cairo_recording_surface_t *surface = abstract_surface;
+    cairo_command_mask_t *command;
+    cairo_composite_rectangles_t composite;
+
+    status = _cairo_composite_rectangles_init_for_mask (&composite,
+                                                       &surface->base,
+                                                       op, source, mask,
+                                                       clip);
+    if (unlikely (status))
+       return status;
+
+    command = malloc (sizeof (cairo_command_mask_t));
+    if (unlikely (command == NULL)) {
+       status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+       goto CLEANUP_COMPOSITE;
+    }
+
+    status = _command_init (surface,
+                           &command->header, CAIRO_COMMAND_MASK, op,
+                           &composite);
+    if (unlikely (status))
+       goto CLEANUP_COMMAND;
+
+    status = _cairo_pattern_init_snapshot (&command->source.base, source);
+    if (unlikely (status))
+       goto CLEANUP_COMMAND;
+
+    status = _cairo_pattern_init_snapshot (&command->mask.base, mask);
+    if (unlikely (status))
+       goto CLEANUP_SOURCE;
+
+    status = _cairo_recording_surface_commit (surface, &command->header);
+    if (unlikely (status))
+       goto CLEANUP_MASK;
+
+    _cairo_recording_surface_destroy_bbtree (surface);
+
+    _cairo_composite_rectangles_fini (&composite);
+    return CAIRO_STATUS_SUCCESS;
+
+  CLEANUP_MASK:
+    _cairo_pattern_fini (&command->mask.base);
+  CLEANUP_SOURCE:
+    _cairo_pattern_fini (&command->source.base);
+  CLEANUP_COMMAND:
+    _cairo_clip_destroy (command->header.clip);
+    free (command);
+CLEANUP_COMPOSITE:
+    _cairo_composite_rectangles_fini (&composite);
+    return status;
+}
+
+static cairo_int_status_t
+_cairo_recording_surface_stroke (void                  *abstract_surface,
+                                cairo_operator_t        op,
+                                const cairo_pattern_t  *source,
+                                const cairo_path_fixed_t       *path,
+                                const cairo_stroke_style_t     *style,
+                                const cairo_matrix_t           *ctm,
+                                const cairo_matrix_t           *ctm_inverse,
+                                double                  tolerance,
+                                cairo_antialias_t       antialias,
+                                const cairo_clip_t     *clip)
+{
+    cairo_status_t status;
+    cairo_recording_surface_t *surface = abstract_surface;
+    cairo_command_stroke_t *command;
+    cairo_composite_rectangles_t composite;
+
+    status = _cairo_composite_rectangles_init_for_stroke (&composite,
+                                                         &surface->base,
+                                                         op, source,
+                                                         path, style, ctm,
+                                                         clip);
+    if (unlikely (status))
+       return status;
+
+    command = malloc (sizeof (cairo_command_stroke_t));
+    if (unlikely (command == NULL)) {
+       status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+       goto CLEANUP_COMPOSITE;
+    }
+
+    status = _command_init (surface,
+                           &command->header, CAIRO_COMMAND_STROKE, op,
+                           &composite);
+    if (unlikely (status))
+       goto CLEANUP_COMMAND;
+
+    status = _cairo_pattern_init_snapshot (&command->source.base, source);
+    if (unlikely (status))
+       goto CLEANUP_COMMAND;
+
+    status = _cairo_path_fixed_init_copy (&command->path, path);
+    if (unlikely (status))
+       goto CLEANUP_SOURCE;
+
+    status = _cairo_stroke_style_init_copy (&command->style, style);
+    if (unlikely (status))
+       goto CLEANUP_PATH;
+
+    command->ctm = *ctm;
+    command->ctm_inverse = *ctm_inverse;
+    command->tolerance = tolerance;
+    command->antialias = antialias;
+
+    status = _cairo_recording_surface_commit (surface, &command->header);
+    if (unlikely (status))
+       goto CLEANUP_STYLE;
+
+    _cairo_recording_surface_destroy_bbtree (surface);
+
+    _cairo_composite_rectangles_fini (&composite);
+    return CAIRO_STATUS_SUCCESS;
+
+  CLEANUP_STYLE:
+    _cairo_stroke_style_fini (&command->style);
+  CLEANUP_PATH:
+    _cairo_path_fixed_fini (&command->path);
+  CLEANUP_SOURCE:
+    _cairo_pattern_fini (&command->source.base);
+  CLEANUP_COMMAND:
+    _cairo_clip_destroy (command->header.clip);
+    free (command);
+CLEANUP_COMPOSITE:
+    _cairo_composite_rectangles_fini (&composite);
+    return status;
+}
+
+static cairo_int_status_t
+_cairo_recording_surface_fill (void                    *abstract_surface,
+                              cairo_operator_t          op,
+                              const cairo_pattern_t    *source,
+                              const cairo_path_fixed_t *path,
+                              cairo_fill_rule_t         fill_rule,
+                              double                    tolerance,
+                              cairo_antialias_t         antialias,
+                              const cairo_clip_t       *clip)
+{
+    cairo_status_t status;
+    cairo_recording_surface_t *surface = abstract_surface;
+    cairo_command_fill_t *command;
+    cairo_composite_rectangles_t composite;
+
+    status = _cairo_composite_rectangles_init_for_fill (&composite,
+                                                       &surface->base,
+                                                       op, source, path,
+                                                       clip);
+    if (unlikely (status))
+       return status;
+
+    command = malloc (sizeof (cairo_command_fill_t));
+    if (unlikely (command == NULL)) {
+       status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+       goto CLEANUP_COMPOSITE;
+    }
+
+    status =_command_init (surface,
+                          &command->header, CAIRO_COMMAND_FILL, op,
+                          &composite);
+    if (unlikely (status))
+       goto CLEANUP_COMMAND;
+
+    status = _cairo_pattern_init_snapshot (&command->source.base, source);
+    if (unlikely (status))
+       goto CLEANUP_COMMAND;
+
+    status = _cairo_path_fixed_init_copy (&command->path, path);
+    if (unlikely (status))
+       goto CLEANUP_SOURCE;
+
+    command->fill_rule = fill_rule;
+    command->tolerance = tolerance;
+    command->antialias = antialias;
+
+    status = _cairo_recording_surface_commit (surface, &command->header);
+    if (unlikely (status))
+       goto CLEANUP_PATH;
+
+    _cairo_recording_surface_destroy_bbtree (surface);
+
+    _cairo_composite_rectangles_fini (&composite);
+    return CAIRO_STATUS_SUCCESS;
+
+  CLEANUP_PATH:
+    _cairo_path_fixed_fini (&command->path);
+  CLEANUP_SOURCE:
+    _cairo_pattern_fini (&command->source.base);
+  CLEANUP_COMMAND:
+    _cairo_clip_destroy (command->header.clip);
+    free (command);
+CLEANUP_COMPOSITE:
+    _cairo_composite_rectangles_fini (&composite);
+    return status;
+}
+
+static cairo_bool_t
+_cairo_recording_surface_has_show_text_glyphs (void *abstract_surface)
+{
+    return TRUE;
+}
+
+static cairo_int_status_t
+_cairo_recording_surface_show_text_glyphs (void                                *abstract_surface,
+                                          cairo_operator_t              op,
+                                          const cairo_pattern_t        *source,
+                                          const char                   *utf8,
+                                          int                           utf8_len,
+                                          cairo_glyph_t                *glyphs,
+                                          int                           num_glyphs,
+                                          const cairo_text_cluster_t   *clusters,
+                                          int                           num_clusters,
+                                          cairo_text_cluster_flags_t    cluster_flags,
+                                          cairo_scaled_font_t          *scaled_font,
+                                          const cairo_clip_t           *clip)
+{
+    cairo_status_t status;
+    cairo_recording_surface_t *surface = abstract_surface;
+    cairo_command_show_text_glyphs_t *command;
+    cairo_composite_rectangles_t composite;
+
+    status = _cairo_composite_rectangles_init_for_glyphs (&composite,
+                                                         &surface->base,
+                                                         op, source,
+                                                         scaled_font,
+                                                         glyphs, num_glyphs,
+                                                         clip,
+                                                         NULL);
+    if (unlikely (status))
+       return status;
+
+    command = malloc (sizeof (cairo_command_show_text_glyphs_t));
+    if (unlikely (command == NULL)) {
+       status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+       goto CLEANUP_COMPOSITE;
+    }
+
+    status = _command_init (surface,
+                           &command->header, CAIRO_COMMAND_SHOW_TEXT_GLYPHS,
+                           op, &composite);
+    if (unlikely (status))
+       goto CLEANUP_COMMAND;
+
+    status = _cairo_pattern_init_snapshot (&command->source.base, source);
+    if (unlikely (status))
+       goto CLEANUP_COMMAND;
+
+    command->utf8 = NULL;
+    command->utf8_len = utf8_len;
+    command->glyphs = NULL;
+    command->num_glyphs = num_glyphs;
+    command->clusters = NULL;
+    command->num_clusters = num_clusters;
+
+    if (utf8_len) {
+       command->utf8 = malloc (utf8_len);
+       if (unlikely (command->utf8 == NULL)) {
+           status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+           goto CLEANUP_ARRAYS;
+       }
+       memcpy (command->utf8, utf8, utf8_len);
+    }
+    if (num_glyphs) {
+       command->glyphs = _cairo_malloc_ab (num_glyphs, sizeof (glyphs[0]));
+       if (unlikely (command->glyphs == NULL)) {
+           status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+           goto CLEANUP_ARRAYS;
+       }
+       memcpy (command->glyphs, glyphs, sizeof (glyphs[0]) * num_glyphs);
+    }
+    if (num_clusters) {
+       command->clusters = _cairo_malloc_ab (num_clusters, sizeof (clusters[0]));
+       if (unlikely (command->clusters == NULL)) {
+           status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+           goto CLEANUP_ARRAYS;
+       }
+       memcpy (command->clusters, clusters, sizeof (clusters[0]) * num_clusters);
+    }
+
+    command->cluster_flags = cluster_flags;
+
+    command->scaled_font = cairo_scaled_font_reference (scaled_font);
+
+    status = _cairo_recording_surface_commit (surface, &command->header);
+    if (unlikely (status))
+       goto CLEANUP_SCALED_FONT;
+
+    _cairo_composite_rectangles_fini (&composite);
+    return CAIRO_STATUS_SUCCESS;
+
+  CLEANUP_SCALED_FONT:
+    cairo_scaled_font_destroy (command->scaled_font);
+  CLEANUP_ARRAYS:
+    free (command->utf8);
+    free (command->glyphs);
+    free (command->clusters);
+
+    _cairo_pattern_fini (&command->source.base);
+  CLEANUP_COMMAND:
+    _cairo_clip_destroy (command->header.clip);
+    free (command);
+CLEANUP_COMPOSITE:
+    _cairo_composite_rectangles_fini (&composite);
+    return status;
+}
+
+/**
+ * _cairo_recording_surface_snapshot
+ * @surface: a #cairo_surface_t which must be a recording surface
+ *
+ * Make an immutable copy of @surface. It is an error to call a
+ * surface-modifying function on the result of this function.
+ *
+ * The caller owns the return value and should call
+ * cairo_surface_destroy() when finished with it. This function will not
+ * return %NULL, but will return a nil surface instead.
+ *
+ * Return value: The snapshot surface.
+ **/
+static cairo_surface_t *
+_cairo_recording_surface_snapshot (void *abstract_other)
+{
+    cairo_recording_surface_t *other = abstract_other;
+    cairo_recording_surface_t *surface;
+    cairo_status_t status;
+
+    surface = malloc (sizeof (cairo_recording_surface_t));
+    if (unlikely (surface == NULL))
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+    _cairo_surface_init (&surface->base,
+                        &cairo_recording_surface_backend,
+                        NULL, /* device */
+                        other->base.content);
+
+    surface->extents_pixels = other->extents_pixels;
+    surface->extents = other->extents;
+    surface->unbounded = other->unbounded;
+
+    surface->base.is_clear = TRUE;
+
+    surface->bbtree.left = surface->bbtree.right = NULL;
+    surface->bbtree.chain = INVALID_CHAIN;
+
+    surface->indices = NULL;
+    surface->num_indices = 0;
+    surface->optimize_clears = TRUE;
+
+    _cairo_array_init (&surface->commands, sizeof (cairo_command_t *));
+    status = _cairo_recording_surface_replay (&other->base, &surface->base);
+    if (unlikely (status)) {
+       cairo_surface_destroy (&surface->base);
+       return _cairo_surface_create_in_error (status);
+    }
+
+    return &surface->base;
+}
+
+static cairo_bool_t
+_cairo_recording_surface_get_extents (void                 *abstract_surface,
+                                     cairo_rectangle_int_t *rectangle)
+{
+    cairo_recording_surface_t *surface = abstract_surface;
+
+    if (surface->unbounded)
+       return FALSE;
+
+    *rectangle = surface->extents;
+    return TRUE;
+}
+
+static const cairo_surface_backend_t cairo_recording_surface_backend = {
+    CAIRO_SURFACE_TYPE_RECORDING,
+    _cairo_recording_surface_finish,
+
+    _cairo_default_context_create,
+
+    _cairo_recording_surface_create_similar,
+    NULL, /* create similar image */
+    NULL, /* map to image */
+    NULL, /* unmap image */
+
+    _cairo_recording_surface_acquire_source_image,
+    _cairo_recording_surface_release_source_image,
+    _cairo_recording_surface_snapshot,
+
+    NULL, /* copy_page */
+    NULL, /* show_page */
+
+    _cairo_recording_surface_get_extents,
+    NULL, /* get_font_options */
+
+    NULL, /* flush */
+    NULL, /* mark_dirty_rectangle */
+
+    /* Here are the 5 basic drawing operations, (which are in some
+     * sense the only things that cairo_recording_surface should need to
+     * implement).  However, we implement the more generic show_text_glyphs
+     * instead of show_glyphs.  One or the other is eough. */
+
+    _cairo_recording_surface_paint,
+    _cairo_recording_surface_mask,
+    _cairo_recording_surface_stroke,
+    _cairo_recording_surface_fill,
+    NULL, /* fill-stroke */
+    NULL,
+    _cairo_recording_surface_has_show_text_glyphs,
+    _cairo_recording_surface_show_text_glyphs,
+};
+
+cairo_int_status_t
+_cairo_recording_surface_get_path (cairo_surface_t    *abstract_surface,
+                                  cairo_path_fixed_t *path)
+{
+    cairo_recording_surface_t *surface;
+    cairo_command_t **elements;
+    int i, num_elements;
+    cairo_int_status_t status;
+
+    if (unlikely (abstract_surface->status))
+       return abstract_surface->status;
+
+    surface = (cairo_recording_surface_t *) abstract_surface;
+    status = CAIRO_STATUS_SUCCESS;
+
+    num_elements = surface->commands.num_elements;
+    elements = _cairo_array_index (&surface->commands, 0);
+    for (i = 0; i < num_elements; i++) {
+       cairo_command_t *command = elements[i];
+
+       switch (command->header.type) {
+       case CAIRO_COMMAND_PAINT:
+       case CAIRO_COMMAND_MASK:
+           status = CAIRO_INT_STATUS_UNSUPPORTED;
+           break;
+
+       case CAIRO_COMMAND_STROKE:
+       {
+           cairo_traps_t traps;
+
+           _cairo_traps_init (&traps);
+
+           /* XXX call cairo_stroke_to_path() when that is implemented */
+           status = _cairo_path_fixed_stroke_to_traps (&command->stroke.path,
+                                                       &command->stroke.style,
+                                                       &command->stroke.ctm,
+                                                       &command->stroke.ctm_inverse,
+                                                       command->stroke.tolerance,
+                                                       &traps);
+
+           if (status == CAIRO_INT_STATUS_SUCCESS)
+               status = _cairo_traps_path (&traps, path);
+
+           _cairo_traps_fini (&traps);
+           break;
+       }
+       case CAIRO_COMMAND_FILL:
+       {
+           status = _cairo_path_fixed_append (path,
+                                              &command->fill.path,
+                                              0, 0);
+           break;
+       }
+       case CAIRO_COMMAND_SHOW_TEXT_GLYPHS:
+       {
+           status = _cairo_scaled_font_glyph_path (command->show_text_glyphs.scaled_font,
+                                                   command->show_text_glyphs.glyphs,
+                                                   command->show_text_glyphs.num_glyphs,
+                                                   path);
+           break;
+       }
+
+       default:
+           ASSERT_NOT_REACHED;
+       }
+
+       if (unlikely (status))
+           break;
+    }
+
+    return status;
+}
+
+static int
+_cairo_recording_surface_get_visible_commands (cairo_recording_surface_t *surface,
+                                              const cairo_rectangle_int_t *extents)
+{
+    int num_visible, *indices;
+    cairo_box_t box;
+
+    _cairo_box_from_rectangle (&box, extents);
+
+    if (surface->bbtree.chain == INVALID_CHAIN)
+       _cairo_recording_surface_create_bbtree (surface);
+
+    indices = surface->indices;
+    bbtree_foreach_mark_visible (&surface->bbtree, &box, &indices);
+    num_visible = indices - surface->indices;
+    if (num_visible > 1)
+       sort_indices (surface->indices, num_visible);
+
+    return num_visible;
+}
+
+static cairo_status_t
+_cairo_recording_surface_replay_internal (cairo_recording_surface_t    *surface,
+                                         const cairo_rectangle_int_t *surface_extents,
+                                         const cairo_matrix_t *surface_transform,
+                                         cairo_surface_t            *target,
+                                         const cairo_clip_t *target_clip,
+                                         cairo_recording_replay_type_t type,
+                                         cairo_recording_region_type_t region)
+{
+    cairo_surface_wrapper_t wrapper;
+    cairo_command_t **elements;
+    cairo_bool_t replay_all =
+       type == CAIRO_RECORDING_REPLAY &&
+       region == CAIRO_RECORDING_REGION_ALL;
+    cairo_int_status_t status = CAIRO_STATUS_SUCCESS;
+    cairo_rectangle_int_t extents;
+    cairo_bool_t use_indices = FALSE;
+    const cairo_rectangle_int_t *r;
+    int i, num_elements;
+
+    if (unlikely (surface->base.status))
+       return surface->base.status;
+
+    if (unlikely (target->status))
+       return target->status;
+
+    if (unlikely (surface->base.finished))
+       return _cairo_error (CAIRO_STATUS_SURFACE_FINISHED);
+
+    if (surface->base.is_clear)
+       return CAIRO_STATUS_SUCCESS;
+
+    assert (_cairo_surface_is_recording (&surface->base));
+
+    _cairo_surface_wrapper_init (&wrapper, target);
+    if (surface_extents)
+       _cairo_surface_wrapper_intersect_extents (&wrapper, surface_extents);
+    r = &_cairo_unbounded_rectangle;
+    if (! surface->unbounded) {
+       _cairo_surface_wrapper_intersect_extents (&wrapper, &surface->extents);
+       r = &surface->extents;
+    }
+    _cairo_surface_wrapper_set_inverse_transform (&wrapper, surface_transform);
+    _cairo_surface_wrapper_set_clip (&wrapper, target_clip);
+
+    /* Compute the extents of the target clip in recorded device space */
+    if (! _cairo_surface_wrapper_get_target_extents (&wrapper, &extents))
+       goto done;
+
+    num_elements = surface->commands.num_elements;
+    elements = _cairo_array_index (&surface->commands, 0);
+    if (extents.width < r->width || extents.height < r->height) {
+       num_elements =
+           _cairo_recording_surface_get_visible_commands (surface, &extents);
+       use_indices = TRUE;
+    }
+
+    for (i = 0; i < num_elements; i++) {
+       cairo_command_t *command = elements[use_indices ? surface->indices[i] : i];
+
+       if (! replay_all && command->header.region != region)
+           continue;
+
+       if (! _cairo_rectangle_intersects (&extents, &command->header.extents))
+           continue;
+
+       switch (command->header.type) {
+       case CAIRO_COMMAND_PAINT:
+           status = _cairo_surface_wrapper_paint (&wrapper,
+                                                  command->header.op,
+                                                  &command->paint.source.base,
+                                                  command->header.clip);
+           break;
+
+       case CAIRO_COMMAND_MASK:
+           status = _cairo_surface_wrapper_mask (&wrapper,
+                                                 command->header.op,
+                                                 &command->mask.source.base,
+                                                 &command->mask.mask.base,
+                                                 command->header.clip);
+           break;
+
+       case CAIRO_COMMAND_STROKE:
+           status = _cairo_surface_wrapper_stroke (&wrapper,
+                                                   command->header.op,
+                                                   &command->stroke.source.base,
+                                                   &command->stroke.path,
+                                                   &command->stroke.style,
+                                                   &command->stroke.ctm,
+                                                   &command->stroke.ctm_inverse,
+                                                   command->stroke.tolerance,
+                                                   command->stroke.antialias,
+                                                   command->header.clip);
+           break;
+
+       case CAIRO_COMMAND_FILL:
+           status = CAIRO_INT_STATUS_UNSUPPORTED;
+           if (_cairo_surface_wrapper_has_fill_stroke (&wrapper)) {
+               cairo_command_t *stroke_command;
+
+               stroke_command = NULL;
+               if (type != CAIRO_RECORDING_CREATE_REGIONS && i < num_elements - 1)
+                   stroke_command = elements[i + 1];
+
+               if (stroke_command != NULL &&
+                   type == CAIRO_RECORDING_REPLAY &&
+                   region != CAIRO_RECORDING_REGION_ALL)
+               {
+                   if (stroke_command->header.region != region)
+                       stroke_command = NULL;
+               }
+
+               if (stroke_command != NULL &&
+                   stroke_command->header.type == CAIRO_COMMAND_STROKE &&
+                   _cairo_path_fixed_equal (&command->fill.path,
+                                            &stroke_command->stroke.path) &&
+                   _cairo_clip_equal (command->header.clip,
+                                      stroke_command->header.clip))
+               {
+                   status = _cairo_surface_wrapper_fill_stroke (&wrapper,
+                                                                command->header.op,
+                                                                &command->fill.source.base,
+                                                                command->fill.fill_rule,
+                                                                command->fill.tolerance,
+                                                                command->fill.antialias,
+                                                                &command->fill.path,
+                                                                stroke_command->header.op,
+                                                                &stroke_command->stroke.source.base,
+                                                                &stroke_command->stroke.style,
+                                                                &stroke_command->stroke.ctm,
+                                                                &stroke_command->stroke.ctm_inverse,
+                                                                stroke_command->stroke.tolerance,
+                                                                stroke_command->stroke.antialias,
+                                                                command->header.clip);
+                   i++;
+               }
+           }
+           if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
+               status = _cairo_surface_wrapper_fill (&wrapper,
+                                                     command->header.op,
+                                                     &command->fill.source.base,
+                                                     &command->fill.path,
+                                                     command->fill.fill_rule,
+                                                     command->fill.tolerance,
+                                                     command->fill.antialias,
+                                                     command->header.clip);
+           }
+           break;
+
+       case CAIRO_COMMAND_SHOW_TEXT_GLYPHS:
+           status = _cairo_surface_wrapper_show_text_glyphs (&wrapper,
+                                                             command->header.op,
+                                                             &command->show_text_glyphs.source.base,
+                                                             command->show_text_glyphs.utf8, command->show_text_glyphs.utf8_len,
+                                                             command->show_text_glyphs.glyphs, command->show_text_glyphs.num_glyphs,
+                                                             command->show_text_glyphs.clusters, command->show_text_glyphs.num_clusters,
+                                                             command->show_text_glyphs.cluster_flags,
+                                                             command->show_text_glyphs.scaled_font,
+                                                             command->header.clip);
+           break;
+
+       default:
+           ASSERT_NOT_REACHED;
+       }
+
+       if (type == CAIRO_RECORDING_CREATE_REGIONS) {
+           if (status == CAIRO_INT_STATUS_SUCCESS) {
+               command->header.region = CAIRO_RECORDING_REGION_NATIVE;
+           } else if (status == CAIRO_INT_STATUS_IMAGE_FALLBACK) {
+               command->header.region = CAIRO_RECORDING_REGION_IMAGE_FALLBACK;
+               status = CAIRO_INT_STATUS_SUCCESS;
+           } else {
+               assert (_cairo_int_status_is_error (status));
+           }
+       }
+
+       if (unlikely (status))
+           break;
+    }
+
+done:
+    _cairo_surface_wrapper_fini (&wrapper);
+    return _cairo_surface_set_error (&surface->base, status);
+}
+
+cairo_status_t
+_cairo_recording_surface_replay_one (cairo_recording_surface_t *surface,
+                                    long unsigned index,
+                                    cairo_surface_t         *target)
+{
+    cairo_surface_wrapper_t wrapper;
+    cairo_command_t **elements, *command;
+    cairo_int_status_t status;
+
+    if (unlikely (surface->base.status))
+       return surface->base.status;
+
+    if (unlikely (target->status))
+       return target->status;
+
+    if (unlikely (surface->base.finished))
+       return _cairo_error (CAIRO_STATUS_SURFACE_FINISHED);
+
+    assert (_cairo_surface_is_recording (&surface->base));
+
+    /* XXX
+     * Use a surface wrapper because we may want to do transformed
+     * replay in the future.
+     */
+    _cairo_surface_wrapper_init (&wrapper, target);
+
+    if (index > surface->commands.num_elements)
+       return _cairo_error (CAIRO_STATUS_READ_ERROR);
+
+    elements = _cairo_array_index (&surface->commands, 0);
+    command = elements[index];
+    switch (command->header.type) {
+    case CAIRO_COMMAND_PAINT:
+       status = _cairo_surface_wrapper_paint (&wrapper,
+                                              command->header.op,
+                                              &command->paint.source.base,
+                                              command->header.clip);
+       break;
+
+    case CAIRO_COMMAND_MASK:
+       status = _cairo_surface_wrapper_mask (&wrapper,
+                                             command->header.op,
+                                             &command->mask.source.base,
+                                             &command->mask.mask.base,
+                                             command->header.clip);
+       break;
+
+    case CAIRO_COMMAND_STROKE:
+       status = _cairo_surface_wrapper_stroke (&wrapper,
+                                               command->header.op,
+                                               &command->stroke.source.base,
+                                               &command->stroke.path,
+                                               &command->stroke.style,
+                                               &command->stroke.ctm,
+                                               &command->stroke.ctm_inverse,
+                                               command->stroke.tolerance,
+                                               command->stroke.antialias,
+                                               command->header.clip);
+       break;
+
+    case CAIRO_COMMAND_FILL:
+       status = _cairo_surface_wrapper_fill (&wrapper,
+                                             command->header.op,
+                                             &command->fill.source.base,
+                                             &command->fill.path,
+                                             command->fill.fill_rule,
+                                             command->fill.tolerance,
+                                             command->fill.antialias,
+                                             command->header.clip);
+       break;
+
+    case CAIRO_COMMAND_SHOW_TEXT_GLYPHS:
+       status = _cairo_surface_wrapper_show_text_glyphs (&wrapper,
+                                                         command->header.op,
+                                                         &command->show_text_glyphs.source.base,
+                                                         command->show_text_glyphs.utf8, command->show_text_glyphs.utf8_len,
+                                                         command->show_text_glyphs.glyphs, command->show_text_glyphs.num_glyphs,
+                                                         command->show_text_glyphs.clusters, command->show_text_glyphs.num_clusters,
+                                                         command->show_text_glyphs.cluster_flags,
+                                                         command->show_text_glyphs.scaled_font,
+                                                         command->header.clip);
+       break;
+
+    default:
+       ASSERT_NOT_REACHED;
+    }
+
+    _cairo_surface_wrapper_fini (&wrapper);
+    return _cairo_surface_set_error (&surface->base, status);
+}
+/**
+ * _cairo_recording_surface_replay:
+ * @surface: the #cairo_recording_surface_t
+ * @target: a target #cairo_surface_t onto which to replay the operations
+ * @width_pixels: width of the surface, in pixels
+ * @height_pixels: height of the surface, in pixels
+ *
+ * A recording surface can be "replayed" against any target surface,
+ * after which the results in target will be identical to the results
+ * that would have been obtained if the original operations applied to
+ * the recording surface had instead been applied to the target surface.
+ **/
+cairo_status_t
+_cairo_recording_surface_replay (cairo_surface_t *surface,
+                                cairo_surface_t *target)
+{
+    return _cairo_recording_surface_replay_internal ((cairo_recording_surface_t *) surface, NULL, NULL,
+                                                    target, NULL,
+                                                    CAIRO_RECORDING_REPLAY,
+                                                    CAIRO_RECORDING_REGION_ALL);
+}
+
+cairo_status_t
+_cairo_recording_surface_replay_with_clip (cairo_surface_t *surface,
+                                          const cairo_matrix_t *surface_transform,
+                                          cairo_surface_t *target,
+                                          const cairo_clip_t *target_clip)
+{
+    return _cairo_recording_surface_replay_internal ((cairo_recording_surface_t *) surface, NULL, surface_transform,
+                                                    target, target_clip,
+                                                    CAIRO_RECORDING_REPLAY,
+                                                    CAIRO_RECORDING_REGION_ALL);
+}
+
+/* Replay recording to surface. When the return status of each operation is
+ * one of %CAIRO_STATUS_SUCCESS, %CAIRO_INT_STATUS_UNSUPPORTED, or
+ * %CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY the status of each operation
+ * will be stored in the recording surface. Any other status will abort the
+ * replay and return the status.
+ */
+cairo_status_t
+_cairo_recording_surface_replay_and_create_regions (cairo_surface_t *surface,
+                                                   cairo_surface_t *target)
+{
+    return _cairo_recording_surface_replay_internal ((cairo_recording_surface_t *) surface, NULL, NULL,
+                                                    target, NULL,
+                                                    CAIRO_RECORDING_CREATE_REGIONS,
+                                                    CAIRO_RECORDING_REGION_ALL);
+}
+
+cairo_status_t
+_cairo_recording_surface_replay_region (cairo_surface_t          *surface,
+                                       const cairo_rectangle_int_t *surface_extents,
+                                       cairo_surface_t          *target,
+                                       cairo_recording_region_type_t  region)
+{
+    return _cairo_recording_surface_replay_internal ((cairo_recording_surface_t *) surface,
+                                                    surface_extents, NULL,
+                                                    target, NULL,
+                                                    CAIRO_RECORDING_REPLAY,
+                                                    region);
+}
+
+static cairo_status_t
+_recording_surface_get_ink_bbox (cairo_recording_surface_t *surface,
+                                cairo_box_t *bbox,
+                                const cairo_matrix_t *transform)
+{
+    cairo_surface_t *null_surface;
+    cairo_surface_t *analysis_surface;
+    cairo_status_t status;
+
+    null_surface = _cairo_null_surface_create (surface->base.content);
+    analysis_surface = _cairo_analysis_surface_create (null_surface);
+    cairo_surface_destroy (null_surface);
+
+    status = analysis_surface->status;
+    if (unlikely (status))
+       return status;
+
+    if (transform != NULL)
+       _cairo_analysis_surface_set_ctm (analysis_surface, transform);
+
+    status = _cairo_recording_surface_replay (&surface->base, analysis_surface);
+    _cairo_analysis_surface_get_bounding_box (analysis_surface, bbox);
+    cairo_surface_destroy (analysis_surface);
+
+    return status;
+}
+
+/**
+ * cairo_recording_surface_ink_extents:
+ * @surface: a #cairo_recording_surface_t
+ * @x0: the x-coordinate of the top-left of the ink bounding box
+ * @y0: the y-coordinate of the top-left of the ink bounding box
+ * @width: the width of the ink bounding box
+ * @height: the height of the ink bounding box
+ *
+ * Measures the extents of the operations stored within the recording-surface.
+ * This is useful to compute the required size of an image surface (or
+ * equivalent) into which to replay the full sequence of drawing operations.
+ *
+ * Since: 1.10
+ **/
+void
+cairo_recording_surface_ink_extents (cairo_surface_t *surface,
+                                    double *x0,
+                                    double *y0,
+                                    double *width,
+                                    double *height)
+{
+    cairo_status_t status;
+    cairo_box_t bbox;
+
+    memset (&bbox, 0, sizeof (bbox));
+
+    if (surface->status || ! _cairo_surface_is_recording (surface)) {
+       _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+       goto DONE;
+    }
+
+    status = _recording_surface_get_ink_bbox ((cairo_recording_surface_t *) surface,
+                                        &bbox,
+                                        NULL);
+    if (unlikely (status))
+       status = _cairo_surface_set_error (surface, status);
+
+DONE:
+    if (x0)
+       *x0 = _cairo_fixed_to_double (bbox.p1.x);
+    if (y0)
+       *y0 = _cairo_fixed_to_double (bbox.p1.y);
+    if (width)
+       *width = _cairo_fixed_to_double (bbox.p2.x - bbox.p1.x);
+    if (height)
+       *height = _cairo_fixed_to_double (bbox.p2.y - bbox.p1.y);
+}
+
+cairo_status_t
+_cairo_recording_surface_get_bbox (cairo_recording_surface_t *surface,
+                                  cairo_box_t *bbox,
+                                  const cairo_matrix_t *transform)
+{
+    if (! surface->unbounded) {
+       _cairo_box_from_rectangle (bbox, &surface->extents);
+       if (transform != NULL)
+           _cairo_matrix_transform_bounding_box_fixed (transform, bbox, NULL);
+
+       return CAIRO_STATUS_SUCCESS;
+    }
+
+    return _recording_surface_get_ink_bbox (surface, bbox, transform);
+}
+
+cairo_status_t
+_cairo_recording_surface_get_ink_bbox (cairo_recording_surface_t *surface,
+                                      cairo_box_t *bbox,
+                                      const cairo_matrix_t *transform)
+{
+    return _recording_surface_get_ink_bbox (surface, bbox, transform);
+}
+
+cairo_bool_t
+cairo_recording_surface_get_extents (cairo_surface_t *surface,
+                                    cairo_rectangle_t *extents)
+{
+    cairo_recording_surface_t *record;
+
+    if (surface->status || ! _cairo_surface_is_recording (surface)) {
+       _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+       return FALSE;
+    }
+
+    record = (cairo_recording_surface_t *)surface;
+    if (record->unbounded)
+       return FALSE;
+
+    *extents = record->extents_pixels;
+    return TRUE;
+}
diff --git a/src/cairo-rectangle.c b/src/cairo-rectangle.c
new file mode 100644 (file)
index 0000000..edad369
--- /dev/null
@@ -0,0 +1,310 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ * Copyright © 2005 Red Hat, Inc.
+ * Copyright © 2006 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *     Carl D. Worth <cworth@cworth.org>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-box-private.h"
+
+const cairo_rectangle_int_t _cairo_empty_rectangle = { 0, 0, 0, 0 };
+const cairo_rectangle_int_t _cairo_unbounded_rectangle = {
+     CAIRO_RECT_INT_MIN, CAIRO_RECT_INT_MIN,
+     CAIRO_RECT_INT_MAX - CAIRO_RECT_INT_MIN,
+     CAIRO_RECT_INT_MAX - CAIRO_RECT_INT_MIN,
+};
+
+cairo_private void
+_cairo_box_from_doubles (cairo_box_t *box,
+                        double *x1, double *y1,
+                        double *x2, double *y2)
+{
+    box->p1.x = _cairo_fixed_from_double (*x1);
+    box->p1.y = _cairo_fixed_from_double (*y1);
+    box->p2.x = _cairo_fixed_from_double (*x2);
+    box->p2.y = _cairo_fixed_from_double (*y2);
+}
+
+cairo_private void
+_cairo_box_to_doubles (const cairo_box_t *box,
+                      double *x1, double *y1,
+                      double *x2, double *y2)
+{
+    *x1 = _cairo_fixed_to_double (box->p1.x);
+    *y1 = _cairo_fixed_to_double (box->p1.y);
+    *x2 = _cairo_fixed_to_double (box->p2.x);
+    *y2 = _cairo_fixed_to_double (box->p2.y);
+}
+
+void
+_cairo_box_from_rectangle (cairo_box_t                 *box,
+                          const cairo_rectangle_int_t *rect)
+{
+    box->p1.x = _cairo_fixed_from_int (rect->x);
+    box->p1.y = _cairo_fixed_from_int (rect->y);
+    box->p2.x = _cairo_fixed_from_int (rect->x + rect->width);
+    box->p2.y = _cairo_fixed_from_int (rect->y + rect->height);
+}
+
+void
+_cairo_boxes_get_extents (const cairo_box_t *boxes,
+                         int num_boxes,
+                         cairo_box_t *extents)
+{
+    int n;
+
+    assert (num_boxes > 0);
+    *extents = *boxes;
+
+    for (n = 1; n < num_boxes; n++) {
+       if (boxes[n].p1.x < extents->p1.x)
+           extents->p1.x = boxes[n].p1.x;
+       if (boxes[n].p2.x > extents->p2.x)
+           extents->p2.x = boxes[n].p2.x;
+
+       if (boxes[n].p1.y < extents->p1.y)
+           extents->p1.y = boxes[n].p1.y;
+       if (boxes[n].p2.y > extents->p2.y)
+           extents->p2.y = boxes[n].p2.y;
+    }
+}
+
+/* XXX We currently have a confusing mix of boxes and rectangles as
+ * exemplified by this function.  A #cairo_box_t is a rectangular area
+ * represented by the coordinates of the upper left and lower right
+ * corners, expressed in fixed point numbers.  A #cairo_rectangle_int_t is
+ * also a rectangular area, but represented by the upper left corner
+ * and the width and the height, as integer numbers.
+ *
+ * This function converts a #cairo_box_t to a #cairo_rectangle_int_t by
+ * increasing the area to the nearest integer coordinates.  We should
+ * standardize on #cairo_rectangle_fixed_t and #cairo_rectangle_int_t, and
+ * this function could be renamed to the more reasonable
+ * _cairo_rectangle_fixed_round.
+ */
+
+void
+_cairo_box_round_to_rectangle (const cairo_box_t     *box,
+                              cairo_rectangle_int_t *rectangle)
+{
+    rectangle->x = _cairo_fixed_integer_floor (box->p1.x);
+    rectangle->y = _cairo_fixed_integer_floor (box->p1.y);
+    rectangle->width = _cairo_fixed_integer_ceil (box->p2.x) - rectangle->x;
+    rectangle->height = _cairo_fixed_integer_ceil (box->p2.y) - rectangle->y;
+}
+
+cairo_bool_t
+_cairo_rectangle_intersect (cairo_rectangle_int_t *dst,
+                           const cairo_rectangle_int_t *src)
+{
+    int x1, y1, x2, y2;
+
+    x1 = MAX (dst->x, src->x);
+    y1 = MAX (dst->y, src->y);
+    /* Beware the unsigned promotion, fortunately we have bits to spare
+     * as (CAIRO_RECT_INT_MAX - CAIRO_RECT_INT_MIN) < UINT_MAX
+     */
+    x2 = MIN (dst->x + (int) dst->width,  src->x + (int) src->width);
+    y2 = MIN (dst->y + (int) dst->height, src->y + (int) src->height);
+
+    if (x1 >= x2 || y1 >= y2) {
+       dst->x = 0;
+       dst->y = 0;
+       dst->width  = 0;
+       dst->height = 0;
+
+       return FALSE;
+    } else {
+       dst->x = x1;
+       dst->y = y1;
+       dst->width  = x2 - x1;
+       dst->height = y2 - y1;
+
+       return TRUE;
+    }
+}
+
+/* Extends the dst rectangle to also contain src.
+ * If one of the rectangles is empty, the result is undefined
+ */
+void
+_cairo_rectangle_union (cairo_rectangle_int_t *dst,
+                       const cairo_rectangle_int_t *src)
+{
+    int x1, y1, x2, y2;
+
+    x1 = MIN (dst->x, src->x);
+    y1 = MIN (dst->y, src->y);
+    /* Beware the unsigned promotion, fortunately we have bits to spare
+     * as (CAIRO_RECT_INT_MAX - CAIRO_RECT_INT_MIN) < UINT_MAX
+     */
+    x2 = MAX (dst->x + (int) dst->width,  src->x + (int) src->width);
+    y2 = MAX (dst->y + (int) dst->height, src->y + (int) src->height);
+
+    dst->x = x1;
+    dst->y = y1;
+    dst->width  = x2 - x1;
+    dst->height = y2 - y1;
+}
+
+#define P1x (line->p1.x)
+#define P1y (line->p1.y)
+#define P2x (line->p2.x)
+#define P2y (line->p2.y)
+#define B1x (box->p1.x)
+#define B1y (box->p1.y)
+#define B2x (box->p2.x)
+#define B2y (box->p2.y)
+
+/*
+ * Check whether any part of line intersects box.  This function essentially
+ * computes whether the ray starting at line->p1 in the direction of line->p2
+ * intersects the box before it reaches p2.  Normally, this is done
+ * by dividing by the lengths of the line projected onto each axis.  Because
+ * we're in fixed point, this function does a bit more work to avoid having to
+ * do the division -- we don't care about the actual intersection point, so
+ * it's of no interest to us.
+ */
+
+cairo_bool_t
+_cairo_box_intersects_line_segment (cairo_box_t *box, cairo_line_t *line)
+{
+    cairo_fixed_t t1=0, t2=0, t3=0, t4=0;
+    cairo_int64_t t1y, t2y, t3x, t4x;
+
+    cairo_fixed_t xlen, ylen;
+
+    if (_cairo_box_contains_point (box, &line->p1) ||
+       _cairo_box_contains_point (box, &line->p2))
+       return TRUE;
+
+    xlen = P2x - P1x;
+    ylen = P2y - P1y;
+
+    if (xlen) {
+       if (xlen > 0) {
+           t1 = B1x - P1x;
+           t2 = B2x - P1x;
+       } else {
+           t1 = P1x - B2x;
+           t2 = P1x - B1x;
+           xlen = - xlen;
+       }
+
+       if ((t1 < 0 || t1 > xlen) &&
+           (t2 < 0 || t2 > xlen))
+           return FALSE;
+    } else {
+       /* Fully vertical line -- check that X is in bounds */
+       if (P1x < B1x || P1x > B2x)
+           return FALSE;
+    }
+
+    if (ylen) {
+       if (ylen > 0) {
+           t3 = B1y - P1y;
+           t4 = B2y - P1y;
+       } else {
+           t3 = P1y - B2y;
+           t4 = P1y - B1y;
+           ylen = - ylen;
+       }
+
+       if ((t3 < 0 || t3 > ylen) &&
+           (t4 < 0 || t4 > ylen))
+           return FALSE;
+    } else {
+       /* Fully horizontal line -- check Y */
+       if (P1y < B1y || P1y > B2y)
+           return FALSE;
+    }
+
+    /* If we had a horizontal or vertical line, then it's already been checked */
+    if (P1x == P2x || P1y == P2y)
+       return TRUE;
+
+    /* Check overlap.  Note that t1 < t2 and t3 < t4 here. */
+    t1y = _cairo_int32x32_64_mul (t1, ylen);
+    t2y = _cairo_int32x32_64_mul (t2, ylen);
+    t3x = _cairo_int32x32_64_mul (t3, xlen);
+    t4x = _cairo_int32x32_64_mul (t4, xlen);
+
+    if (_cairo_int64_lt(t1y, t4x) &&
+       _cairo_int64_lt(t3x, t2y))
+       return TRUE;
+
+    return FALSE;
+}
+
+static cairo_status_t
+_cairo_box_add_spline_point (void *closure,
+                            const cairo_point_t *point)
+{
+    _cairo_box_add_point (closure, point);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+/* assumes a has been previously added */
+void
+_cairo_box_add_curve_to (cairo_box_t *extents,
+                        const cairo_point_t *a,
+                        const cairo_point_t *b,
+                        const cairo_point_t *c,
+                        const cairo_point_t *d)
+{
+    _cairo_box_add_point (extents, d);
+    if (!_cairo_box_contains_point (extents, b) ||
+       !_cairo_box_contains_point (extents, c))
+    {
+       cairo_status_t status;
+
+       status = _cairo_spline_bound (_cairo_box_add_spline_point,
+                                     extents, a, b, c, d);
+       assert (status == CAIRO_STATUS_SUCCESS);
+    }
+}
+
+void
+_cairo_rectangle_int_from_double (cairo_rectangle_int_t *recti,
+                                 const cairo_rectangle_t *rectf)
+{
+       recti->x = floor (rectf->x);
+       recti->y = floor (rectf->y);
+       recti->width  = ceil (rectf->x + rectf->width) - floor (rectf->x);
+       recti->height = ceil (rectf->y + rectf->height) - floor (rectf->y);
+}
diff --git a/src/cairo-rectangular-scan-converter.c b/src/cairo-rectangular-scan-converter.c
new file mode 100644 (file)
index 0000000..b4214c8
--- /dev/null
@@ -0,0 +1,791 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * Contributor(s):
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-combsort-private.h"
+#include "cairo-error-private.h"
+#include "cairo-freelist-private.h"
+#include "cairo-list-private.h"
+#include "cairo-spans-private.h"
+
+#include <setjmp.h>
+
+typedef struct _rectangle {
+    struct _rectangle *next, *prev;
+    cairo_fixed_t left, right;
+    cairo_fixed_t top, bottom;
+    int32_t top_y, bottom_y;
+    int dir;
+} rectangle_t;
+
+#define UNROLL3(x) x x x
+
+/* the parent is always given by index/2 */
+#define PQ_PARENT_INDEX(i) ((i) >> 1)
+#define PQ_FIRST_ENTRY 1
+
+/* left and right children are index * 2 and (index * 2) +1 respectively */
+#define PQ_LEFT_CHILD_INDEX(i) ((i) << 1)
+
+typedef struct _pqueue {
+    int size, max_size;
+
+    rectangle_t **elements;
+    rectangle_t *elements_embedded[1024];
+} pqueue_t;
+
+typedef struct {
+    rectangle_t **start;
+    pqueue_t stop;
+    rectangle_t head, tail;
+    rectangle_t *insert_cursor;
+    int32_t current_y;
+    int32_t xmin, xmax;
+
+    struct coverage {
+       struct cell {
+           struct cell *prev, *next;
+           int x, covered, uncovered;
+       } head, tail, *cursor;
+       unsigned int count;
+       cairo_freepool_t pool;
+    } coverage;
+
+    cairo_half_open_span_t spans_stack[CAIRO_STACK_ARRAY_LENGTH (cairo_half_open_span_t)];
+    cairo_half_open_span_t *spans;
+    unsigned int num_spans;
+    unsigned int size_spans;
+
+    jmp_buf jmpbuf;
+} sweep_line_t;
+
+static inline int
+rectangle_compare_start (const rectangle_t *a,
+                        const rectangle_t *b)
+{
+    int cmp;
+
+    cmp = a->top_y - b->top_y;
+    if (cmp)
+       return cmp;
+
+    return a->left - b->left;
+}
+
+static inline int
+rectangle_compare_stop (const rectangle_t *a,
+                       const rectangle_t *b)
+{
+    return a->bottom_y - b->bottom_y;
+}
+
+static inline void
+pqueue_init (pqueue_t *pq)
+{
+    pq->max_size = ARRAY_LENGTH (pq->elements_embedded);
+    pq->size = 0;
+
+    pq->elements = pq->elements_embedded;
+    pq->elements[PQ_FIRST_ENTRY] = NULL;
+}
+
+static inline void
+pqueue_fini (pqueue_t *pq)
+{
+    if (pq->elements != pq->elements_embedded)
+       free (pq->elements);
+}
+
+static cairo_bool_t
+pqueue_grow (pqueue_t *pq)
+{
+    rectangle_t **new_elements;
+    pq->max_size *= 2;
+
+    if (pq->elements == pq->elements_embedded) {
+       new_elements = _cairo_malloc_ab (pq->max_size,
+                                        sizeof (rectangle_t *));
+       if (unlikely (new_elements == NULL))
+           return FALSE;
+
+       memcpy (new_elements, pq->elements_embedded,
+               sizeof (pq->elements_embedded));
+    } else {
+       new_elements = _cairo_realloc_ab (pq->elements,
+                                         pq->max_size,
+                                         sizeof (rectangle_t *));
+       if (unlikely (new_elements == NULL))
+           return FALSE;
+    }
+
+    pq->elements = new_elements;
+    return TRUE;
+}
+
+static inline void
+pqueue_push (sweep_line_t *sweep, rectangle_t *rectangle)
+{
+    rectangle_t **elements;
+    int i, parent;
+
+    if (unlikely (sweep->stop.size + 1 == sweep->stop.max_size)) {
+       if (unlikely (! pqueue_grow (&sweep->stop)))
+           longjmp (sweep->jmpbuf,
+                    _cairo_error (CAIRO_STATUS_NO_MEMORY));
+    }
+
+    elements = sweep->stop.elements;
+    for (i = ++sweep->stop.size;
+        i != PQ_FIRST_ENTRY &&
+        rectangle_compare_stop (rectangle,
+                                elements[parent = PQ_PARENT_INDEX (i)]) < 0;
+        i = parent)
+    {
+       elements[i] = elements[parent];
+    }
+
+    elements[i] = rectangle;
+}
+
+static inline void
+pqueue_pop (pqueue_t *pq)
+{
+    rectangle_t **elements = pq->elements;
+    rectangle_t *tail;
+    int child, i;
+
+    tail = elements[pq->size--];
+    if (pq->size == 0) {
+       elements[PQ_FIRST_ENTRY] = NULL;
+       return;
+    }
+
+    for (i = PQ_FIRST_ENTRY;
+        (child = PQ_LEFT_CHILD_INDEX (i)) <= pq->size;
+        i = child)
+    {
+       if (child != pq->size &&
+           rectangle_compare_stop (elements[child+1],
+                                   elements[child]) < 0)
+       {
+           child++;
+       }
+
+       if (rectangle_compare_stop (elements[child], tail) >= 0)
+           break;
+
+       elements[i] = elements[child];
+    }
+    elements[i] = tail;
+}
+
+static inline rectangle_t *
+peek_stop (sweep_line_t *sweep)
+{
+    return sweep->stop.elements[PQ_FIRST_ENTRY];
+}
+
+CAIRO_COMBSORT_DECLARE (rectangle_sort, rectangle_t *, rectangle_compare_start)
+
+static void
+sweep_line_init (sweep_line_t *sweep)
+{
+    sweep->head.left = INT_MIN;
+    sweep->head.next = &sweep->tail;
+    sweep->tail.left = INT_MAX;
+    sweep->tail.prev = &sweep->head;
+    sweep->insert_cursor = &sweep->tail;
+
+    _cairo_freepool_init (&sweep->coverage.pool, sizeof (struct cell));
+
+    sweep->spans = sweep->spans_stack;
+    sweep->size_spans = ARRAY_LENGTH (sweep->spans_stack);
+
+    sweep->coverage.head.prev = NULL;
+    sweep->coverage.head.x = INT_MIN;
+    sweep->coverage.tail.next = NULL;
+    sweep->coverage.tail.x = INT_MAX;
+
+    pqueue_init (&sweep->stop);
+}
+
+static void
+sweep_line_fini (sweep_line_t *sweep)
+{
+    _cairo_freepool_fini (&sweep->coverage.pool);
+    pqueue_fini (&sweep->stop);
+
+    if (sweep->spans != sweep->spans_stack)
+       free (sweep->spans);
+}
+
+static inline void
+add_cell (sweep_line_t *sweep, int x, int covered, int uncovered)
+{
+    struct cell *cell;
+
+    cell = sweep->coverage.cursor;
+    if (cell->x > x) {
+       do {
+           UNROLL3({
+               if (cell->prev->x < x)
+                   break;
+               cell = cell->prev;
+           })
+       } while (TRUE);
+    } else {
+       if (cell->x == x)
+           goto found;
+
+       do {
+           UNROLL3({
+               cell = cell->next;
+               if (cell->x >= x)
+                   break;
+           })
+       } while (TRUE);
+    }
+
+    if (x != cell->x) {
+       struct cell *c;
+
+       sweep->coverage.count++;
+
+       c = _cairo_freepool_alloc (&sweep->coverage.pool);
+       if (unlikely (c == NULL)) {
+           longjmp (sweep->jmpbuf,
+                    _cairo_error (CAIRO_STATUS_NO_MEMORY));
+       }
+
+       cell->prev->next = c;
+       c->prev = cell->prev;
+       c->next = cell;
+       cell->prev = c;
+
+       c->x = x;
+       c->covered = 0;
+       c->uncovered = 0;
+
+       cell = c;
+    }
+
+found:
+    cell->covered += covered;
+    cell->uncovered += uncovered;
+    sweep->coverage.cursor = cell;
+}
+
+static inline void
+_active_edges_to_spans (sweep_line_t   *sweep)
+{
+    int32_t y = sweep->current_y;
+    rectangle_t *rectangle;
+    int coverage, prev_coverage;
+    int prev_x;
+    struct cell *cell;
+
+    sweep->num_spans = 0;
+    if (sweep->head.next == &sweep->tail)
+       return;
+
+    sweep->coverage.head.next = &sweep->coverage.tail;
+    sweep->coverage.tail.prev = &sweep->coverage.head;
+    sweep->coverage.cursor = &sweep->coverage.tail;
+    sweep->coverage.count = 0;
+
+    /* XXX cell coverage only changes when a rectangle appears or
+     * disappears. Try only modifying coverage at such times.
+     */
+    for (rectangle = sweep->head.next;
+        rectangle != &sweep->tail;
+        rectangle = rectangle->next)
+    {
+       int height;
+       int frac, i;
+
+       if (y == rectangle->bottom_y) {
+           height = rectangle->bottom & CAIRO_FIXED_FRAC_MASK;
+           if (height == 0)
+               continue;
+       } else
+           height = CAIRO_FIXED_ONE;
+       if (y == rectangle->top_y)
+           height -= rectangle->top & CAIRO_FIXED_FRAC_MASK;
+       height *= rectangle->dir;
+
+       i = _cairo_fixed_integer_part (rectangle->left),
+       frac = _cairo_fixed_fractional_part (rectangle->left);
+       add_cell (sweep, i,
+                 (CAIRO_FIXED_ONE-frac) * height,
+                 frac * height);
+
+       i = _cairo_fixed_integer_part (rectangle->right),
+       frac = _cairo_fixed_fractional_part (rectangle->right);
+       add_cell (sweep, i,
+                 -(CAIRO_FIXED_ONE-frac) * height,
+                 -frac * height);
+    }
+
+    if (2*sweep->coverage.count >= sweep->size_spans) {
+       unsigned size;
+
+       size = sweep->size_spans;
+       while (size <= 2*sweep->coverage.count)
+           size <<= 1;
+
+       if (sweep->spans != sweep->spans_stack)
+           free (sweep->spans);
+
+       sweep->spans = _cairo_malloc_ab (size, sizeof (cairo_half_open_span_t));
+       if (unlikely (sweep->spans == NULL))
+           longjmp (sweep->jmpbuf, _cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+       sweep->size_spans = size;
+    }
+
+    prev_coverage = coverage = 0;
+    prev_x = INT_MIN;
+    for (cell = sweep->coverage.head.next; cell != &sweep->coverage.tail; cell = cell->next) {
+       if (cell->x != prev_x && coverage != prev_coverage) {
+           int n = sweep->num_spans++;
+           int c = coverage >> (CAIRO_FIXED_FRAC_BITS * 2 - 8);
+           sweep->spans[n].x = prev_x;
+           sweep->spans[n].inverse = 0;
+           sweep->spans[n].coverage = c - (c >> 8);
+           prev_coverage = coverage;
+       }
+
+       coverage += cell->covered;
+       if (coverage != prev_coverage) {
+           int n = sweep->num_spans++;
+           int c = coverage >> (CAIRO_FIXED_FRAC_BITS * 2 - 8);
+           sweep->spans[n].x = cell->x;
+           sweep->spans[n].inverse = 0;
+           sweep->spans[n].coverage = c - (c >> 8);
+           prev_coverage = coverage;
+       }
+       coverage += cell->uncovered;
+       prev_x = cell->x + 1;
+    }
+    _cairo_freepool_reset (&sweep->coverage.pool);
+
+    if (sweep->num_spans) {
+       if (prev_x <= sweep->xmax) {
+           int n = sweep->num_spans++;
+           int c = coverage >> (CAIRO_FIXED_FRAC_BITS * 2 - 8);
+           sweep->spans[n].x = prev_x;
+           sweep->spans[n].inverse = 0;
+           sweep->spans[n].coverage = c - (c >> 8);
+       }
+
+       if (coverage && prev_x < sweep->xmax) {
+           int n = sweep->num_spans++;
+           sweep->spans[n].x = sweep->xmax;
+           sweep->spans[n].inverse = 1;
+           sweep->spans[n].coverage = 0;
+       }
+    }
+}
+
+static inline void
+sweep_line_delete (sweep_line_t        *sweep,
+                            rectangle_t        *rectangle)
+{
+    if (sweep->insert_cursor == rectangle)
+       sweep->insert_cursor = rectangle->next;
+
+    rectangle->prev->next = rectangle->next;
+    rectangle->next->prev = rectangle->prev;
+
+    pqueue_pop (&sweep->stop);
+}
+
+static inline void
+sweep_line_insert (sweep_line_t        *sweep,
+                  rectangle_t  *rectangle)
+{
+    rectangle_t *pos;
+
+    pos = sweep->insert_cursor;
+    if (pos->left != rectangle->left) {
+       if (pos->left > rectangle->left) {
+           do {
+               UNROLL3({
+                   if (pos->prev->left < rectangle->left)
+                       break;
+                   pos = pos->prev;
+               })
+           } while (TRUE);
+       } else {
+           do {
+               UNROLL3({
+                   pos = pos->next;
+                   if (pos->left >= rectangle->left)
+                       break;
+               });
+           } while (TRUE);
+       }
+    }
+
+    pos->prev->next = rectangle;
+    rectangle->prev = pos->prev;
+    rectangle->next = pos;
+    pos->prev = rectangle;
+    sweep->insert_cursor = rectangle;
+
+    pqueue_push (sweep, rectangle);
+}
+
+static void
+render_rows (sweep_line_t *sweep_line,
+            cairo_span_renderer_t *renderer,
+            int height)
+{
+    cairo_status_t status;
+
+    _active_edges_to_spans (sweep_line);
+
+    status = renderer->render_rows (renderer,
+                                   sweep_line->current_y, height,
+                                   sweep_line->spans,
+                                   sweep_line->num_spans);
+    if (unlikely (status))
+       longjmp (sweep_line->jmpbuf, status);
+}
+
+static cairo_status_t
+generate (cairo_rectangular_scan_converter_t *self,
+         cairo_span_renderer_t *renderer,
+         rectangle_t **rectangles)
+{
+    sweep_line_t sweep_line;
+    rectangle_t *start, *stop;
+    cairo_status_t status;
+
+    sweep_line_init (&sweep_line);
+    sweep_line.xmin = _cairo_fixed_integer_part (self->extents.p1.x);
+    sweep_line.xmax = _cairo_fixed_integer_part (self->extents.p2.x);
+    sweep_line.start = rectangles;
+    if ((status = setjmp (sweep_line.jmpbuf)))
+       goto out;
+
+    sweep_line.current_y = _cairo_fixed_integer_part (self->extents.p1.y);
+    start = *sweep_line.start++;
+    do {
+       if (start->top_y != sweep_line.current_y) {
+           render_rows (&sweep_line, renderer,
+                        start->top_y - sweep_line.current_y);
+           sweep_line.current_y = start->top_y;
+       }
+
+       do {
+           sweep_line_insert (&sweep_line, start);
+           start = *sweep_line.start++;
+           if (start == NULL)
+               goto end;
+           if (start->top_y != sweep_line.current_y)
+               break;
+       } while (TRUE);
+
+       render_rows (&sweep_line, renderer, 1);
+
+       stop = peek_stop (&sweep_line);
+       while (stop->bottom_y == sweep_line.current_y) {
+           sweep_line_delete (&sweep_line, stop);
+           stop = peek_stop (&sweep_line);
+           if (stop == NULL)
+               break;
+       }
+
+       sweep_line.current_y++;
+
+       while (stop != NULL && stop->bottom_y < start->top_y) {
+           if (stop->bottom_y != sweep_line.current_y) {
+               render_rows (&sweep_line, renderer,
+                            stop->bottom_y - sweep_line.current_y);
+               sweep_line.current_y = stop->bottom_y;
+           }
+
+           render_rows (&sweep_line, renderer, 1);
+
+           do {
+               sweep_line_delete (&sweep_line, stop);
+               stop = peek_stop (&sweep_line);
+           } while (stop != NULL && stop->bottom_y == sweep_line.current_y);
+
+           sweep_line.current_y++;
+       }
+    } while (TRUE);
+
+  end:
+    render_rows (&sweep_line, renderer, 1);
+
+    stop = peek_stop (&sweep_line);
+    while (stop->bottom_y == sweep_line.current_y) {
+       sweep_line_delete (&sweep_line, stop);
+       stop = peek_stop (&sweep_line);
+       if (stop == NULL)
+           goto out;
+    }
+
+    while (++sweep_line.current_y < _cairo_fixed_integer_part (self->extents.p2.y)) {
+       if (stop->bottom_y != sweep_line.current_y) {
+           render_rows (&sweep_line, renderer,
+                        stop->bottom_y - sweep_line.current_y);
+           sweep_line.current_y = stop->bottom_y;
+       }
+
+       render_rows (&sweep_line, renderer, 1);
+
+       do {
+           sweep_line_delete (&sweep_line, stop);
+           stop = peek_stop (&sweep_line);
+           if (stop == NULL)
+               goto out;
+       } while (stop->bottom_y == sweep_line.current_y);
+
+    }
+
+  out:
+    sweep_line_fini (&sweep_line);
+
+    return status;
+}
+static void generate_row(cairo_span_renderer_t *renderer,
+                        const rectangle_t *r,
+                        int y, int h,
+                        uint16_t coverage)
+{
+    cairo_half_open_span_t spans[4];
+    unsigned int num_spans = 0;
+    int x1 = _cairo_fixed_integer_part (r->left);
+    int x2 = _cairo_fixed_integer_part (r->right);
+    if (x2 > x1) {
+       if (! _cairo_fixed_is_integer (r->left)) {
+           spans[num_spans].x = x1;
+           spans[num_spans].coverage =
+               coverage * (256 - _cairo_fixed_fractional_part (r->left)) >> 8;
+           num_spans++;
+           x1++;
+       }
+
+       if (x2 > x1) {
+           spans[num_spans].x = x1;
+           spans[num_spans].coverage = coverage - (coverage >> 8);
+           num_spans++;
+       }
+
+       if (! _cairo_fixed_is_integer (r->right)) {
+           spans[num_spans].x = x2++;
+           spans[num_spans].coverage =
+               coverage * _cairo_fixed_fractional_part (r->right) >> 8;
+           num_spans++;
+       }
+    } else {
+       spans[num_spans].x = x2++;
+       spans[num_spans].coverage = coverage * (r->right - r->left) >> 8;
+       num_spans++;
+    }
+
+    spans[num_spans].x = x2;
+    spans[num_spans].coverage = 0;
+    num_spans++;
+
+    renderer->render_rows (renderer, y, h, spans, num_spans);
+}
+
+static cairo_status_t
+generate_box (cairo_rectangular_scan_converter_t *self,
+             cairo_span_renderer_t     *renderer)
+{
+    const rectangle_t *r = self->chunks.base;
+    int y1 = _cairo_fixed_integer_part (r->top);
+    int y2 = _cairo_fixed_integer_part (r->bottom);
+    if (y2 > y1) {
+       if (! _cairo_fixed_is_integer (r->top)) {
+           generate_row(renderer, r, y1, 1,
+                        256 - _cairo_fixed_fractional_part (r->top));
+           y1++;
+       }
+
+       if (y2 > y1)
+           generate_row(renderer, r, y1, y2-y1, 256);
+
+       if (! _cairo_fixed_is_integer (r->bottom))
+           generate_row(renderer, r, y2, 1,
+                        _cairo_fixed_fractional_part (r->bottom));
+    } else
+       generate_row(renderer, r, y1, 1, r->bottom - r->top);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_rectangular_scan_converter_generate (void                       *converter,
+                                           cairo_span_renderer_t       *renderer)
+{
+    cairo_rectangular_scan_converter_t *self = converter;
+    rectangle_t *rectangles_stack[CAIRO_STACK_ARRAY_LENGTH (rectangle_t *)];
+    rectangle_t **rectangles;
+    struct _cairo_rectangular_scan_converter_chunk *chunk;
+    cairo_status_t status;
+    int i, j;
+
+    if (unlikely (self->num_rectangles == 0)) {
+       return renderer->render_rows (renderer,
+                                     _cairo_fixed_integer_part (self->extents.p1.y),
+                                     _cairo_fixed_integer_part (self->extents.p2.y - self->extents.p1.y),
+                                     NULL, 0);
+    }
+
+    if (self->num_rectangles == 1)
+       return generate_box (self, renderer);
+
+    rectangles = rectangles_stack;
+    if (unlikely (self->num_rectangles >= ARRAY_LENGTH (rectangles_stack))) {
+       rectangles = _cairo_malloc_ab (self->num_rectangles + 1,
+                                      sizeof (rectangle_t *));
+       if (unlikely (rectangles == NULL))
+           return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+    }
+
+    j = 0;
+    for (chunk = &self->chunks; chunk != NULL; chunk = chunk->next) {
+       rectangle_t *rectangle;
+
+       rectangle = chunk->base;
+       for (i = 0; i < chunk->count; i++)
+           rectangles[j++] = &rectangle[i];
+    }
+    rectangle_sort (rectangles, j);
+    rectangles[j] = NULL;
+
+    status = generate (self, renderer, rectangles);
+
+    if (rectangles != rectangles_stack)
+       free (rectangles);
+
+    return status;
+}
+
+static rectangle_t *
+_allocate_rectangle (cairo_rectangular_scan_converter_t *self)
+{
+    rectangle_t *rectangle;
+    struct _cairo_rectangular_scan_converter_chunk *chunk;
+
+    chunk = self->tail;
+    if (chunk->count == chunk->size) {
+       int size;
+
+       size = chunk->size * 2;
+       chunk->next = _cairo_malloc_ab_plus_c (size,
+                                              sizeof (rectangle_t),
+                                              sizeof (struct _cairo_rectangular_scan_converter_chunk));
+
+       if (unlikely (chunk->next == NULL))
+           return NULL;
+
+       chunk = chunk->next;
+       chunk->next = NULL;
+       chunk->count = 0;
+       chunk->size = size;
+       chunk->base = chunk + 1;
+       self->tail = chunk;
+    }
+
+    rectangle = chunk->base;
+    return rectangle + chunk->count++;
+}
+
+cairo_status_t
+_cairo_rectangular_scan_converter_add_box (cairo_rectangular_scan_converter_t *self,
+                                          const cairo_box_t *box,
+                                          int dir)
+{
+    rectangle_t *rectangle;
+
+    rectangle = _allocate_rectangle (self);
+    if (unlikely (rectangle == NULL))
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    rectangle->dir = dir;
+    rectangle->left  = MAX (box->p1.x, self->extents.p1.x);
+    rectangle->right = MIN (box->p2.x, self->extents.p2.x);
+    if (unlikely (rectangle->right <= rectangle->left)) {
+       self->tail->count--;
+       return CAIRO_STATUS_SUCCESS;
+    }
+
+    rectangle->top = MAX (box->p1.y, self->extents.p1.y);
+    rectangle->top_y  = _cairo_fixed_integer_floor (rectangle->top);
+    rectangle->bottom = MIN (box->p2.y, self->extents.p2.y);
+    rectangle->bottom_y = _cairo_fixed_integer_floor (rectangle->bottom);
+    if (likely (rectangle->bottom > rectangle->top))
+       self->num_rectangles++;
+    else
+       self->tail->count--;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_rectangular_scan_converter_destroy (void *converter)
+{
+    cairo_rectangular_scan_converter_t *self = converter;
+    struct _cairo_rectangular_scan_converter_chunk *chunk, *next;
+
+    for (chunk = self->chunks.next; chunk != NULL; chunk = next) {
+       next = chunk->next;
+       free (chunk);
+    }
+}
+
+void
+_cairo_rectangular_scan_converter_init (cairo_rectangular_scan_converter_t *self,
+                                       const cairo_rectangle_int_t *extents)
+{
+    self->base.destroy = _cairo_rectangular_scan_converter_destroy;
+    self->base.generate = _cairo_rectangular_scan_converter_generate;
+
+    _cairo_box_from_rectangle (&self->extents, extents);
+
+    self->chunks.base = self->buf;
+    self->chunks.next = NULL;
+    self->chunks.count = 0;
+    self->chunks.size = sizeof (self->buf) / sizeof (rectangle_t);
+    self->tail = &self->chunks;
+
+    self->num_rectangles = 0;
+}
diff --git a/src/cairo-reference-count-private.h b/src/cairo-reference-count-private.h
new file mode 100644 (file)
index 0000000..75fdf35
--- /dev/null
@@ -0,0 +1,62 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2007 Chris Wilson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#ifndef CAIRO_REFRENCE_COUNT_PRIVATE_H
+#define CAIRO_REFRENCE_COUNT_PRIVATE_H
+
+#include "cairo-atomic-private.h"
+
+/* Encapsulate operations on the object's reference count */
+typedef struct {
+    cairo_atomic_int_t ref_count;
+} cairo_reference_count_t;
+
+#define _cairo_reference_count_inc(RC) _cairo_atomic_int_inc (&(RC)->ref_count)
+#define _cairo_reference_count_dec(RC) _cairo_atomic_int_dec (&(RC)->ref_count)
+#define _cairo_reference_count_dec_and_test(RC) _cairo_atomic_int_dec_and_test (&(RC)->ref_count)
+
+#define CAIRO_REFERENCE_COUNT_INIT(RC, VALUE) ((RC)->ref_count = (VALUE))
+
+#define CAIRO_REFERENCE_COUNT_GET_VALUE(RC) _cairo_atomic_int_get (&(RC)->ref_count)
+
+#define CAIRO_REFERENCE_COUNT_INVALID_VALUE ((cairo_atomic_int_t) -1)
+#define CAIRO_REFERENCE_COUNT_INVALID {CAIRO_REFERENCE_COUNT_INVALID_VALUE}
+
+#define CAIRO_REFERENCE_COUNT_IS_INVALID(RC) (CAIRO_REFERENCE_COUNT_GET_VALUE (RC) == CAIRO_REFERENCE_COUNT_INVALID_VALUE)
+
+#define CAIRO_REFERENCE_COUNT_HAS_REFERENCE(RC) (CAIRO_REFERENCE_COUNT_GET_VALUE (RC) > 0)
+
+#endif
diff --git a/src/cairo-region-private.h b/src/cairo-region-private.h
new file mode 100644 (file)
index 0000000..11070ba
--- /dev/null
@@ -0,0 +1,71 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ *     Owen Taylor <otaylor@redhat.com>
+ *      Vladimir Vukicevic <vladimir@pobox.com>
+ *      Søren Sandmann <sandmann@daimi.au.dk>
+ */
+
+#ifndef CAIRO_REGION_PRIVATE_H
+#define CAIRO_REGION_PRIVATE_H
+
+#include "cairo-types-private.h"
+#include "cairo-reference-count-private.h"
+
+#include <pixman.h>
+
+CAIRO_BEGIN_DECLS
+
+struct _cairo_region {
+    cairo_reference_count_t ref_count;
+    cairo_status_t status;
+
+    pixman_region32_t rgn;
+};
+
+cairo_private cairo_region_t *
+_cairo_region_create_in_error (cairo_status_t status);
+
+cairo_private void
+_cairo_region_init (cairo_region_t *region);
+
+cairo_private void
+_cairo_region_init_rectangle (cairo_region_t *region,
+                             const cairo_rectangle_int_t *rectangle);
+
+cairo_private void
+_cairo_region_fini (cairo_region_t *region);
+
+CAIRO_END_DECLS
+
+#endif /* CAIRO_REGION_PRIVATE_H */
diff --git a/src/cairo-region.c b/src/cairo-region.c
new file mode 100644 (file)
index 0000000..f3ccb89
--- /dev/null
@@ -0,0 +1,916 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ *     Owen Taylor <otaylor@redhat.com>
+ *      Vladimir Vukicevic <vladimir@pobox.com>
+ *      Søren Sandmann <sandmann@daimi.au.dk>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-error-private.h"
+#include "cairo-region-private.h"
+
+/* XXX need to update pixman headers to be const as appropriate */
+#define CONST_CAST (pixman_region32_t *)
+
+/**
+ * SECTION:cairo-region
+ * @Title: Regions
+ * @Short_Description: Representing a pixel-aligned area
+ *
+ * Regions are a simple graphical data type representing an area of 
+ * integer-aligned rectangles. They are often used on raster surfaces 
+ * to track areas of interest, such as change or clip areas.
+ */
+
+static const cairo_region_t _cairo_region_nil = {
+    CAIRO_REFERENCE_COUNT_INVALID,     /* ref_count */
+    CAIRO_STATUS_NO_MEMORY,            /* status */
+};
+
+cairo_region_t *
+_cairo_region_create_in_error (cairo_status_t status)
+{
+    switch (status) {
+    case CAIRO_STATUS_NO_MEMORY:
+       return (cairo_region_t *) &_cairo_region_nil;
+
+    case CAIRO_STATUS_SUCCESS:
+    case CAIRO_STATUS_LAST_STATUS:
+       ASSERT_NOT_REACHED;
+       /* fall-through */
+    case CAIRO_STATUS_SURFACE_TYPE_MISMATCH:
+    case CAIRO_STATUS_INVALID_STATUS:
+    case CAIRO_STATUS_INVALID_CONTENT:
+    case CAIRO_STATUS_INVALID_FORMAT:
+    case CAIRO_STATUS_INVALID_VISUAL:
+    case CAIRO_STATUS_READ_ERROR:
+    case CAIRO_STATUS_WRITE_ERROR:
+    case CAIRO_STATUS_FILE_NOT_FOUND:
+    case CAIRO_STATUS_TEMP_FILE_ERROR:
+    case CAIRO_STATUS_INVALID_STRIDE:
+    case CAIRO_STATUS_INVALID_SIZE:
+    case CAIRO_STATUS_DEVICE_TYPE_MISMATCH:
+    case CAIRO_STATUS_DEVICE_ERROR:
+    case CAIRO_STATUS_INVALID_RESTORE:
+    case CAIRO_STATUS_INVALID_POP_GROUP:
+    case CAIRO_STATUS_NO_CURRENT_POINT:
+    case CAIRO_STATUS_INVALID_MATRIX:
+    case CAIRO_STATUS_NULL_POINTER:
+    case CAIRO_STATUS_INVALID_STRING:
+    case CAIRO_STATUS_INVALID_PATH_DATA:
+    case CAIRO_STATUS_SURFACE_FINISHED:
+    case CAIRO_STATUS_PATTERN_TYPE_MISMATCH:
+    case CAIRO_STATUS_INVALID_DASH:
+    case CAIRO_STATUS_INVALID_DSC_COMMENT:
+    case CAIRO_STATUS_INVALID_INDEX:
+    case CAIRO_STATUS_CLIP_NOT_REPRESENTABLE:
+    case CAIRO_STATUS_FONT_TYPE_MISMATCH:
+    case CAIRO_STATUS_USER_FONT_IMMUTABLE:
+    case CAIRO_STATUS_USER_FONT_ERROR:
+    case CAIRO_STATUS_NEGATIVE_COUNT:
+    case CAIRO_STATUS_INVALID_CLUSTERS:
+    case CAIRO_STATUS_INVALID_SLANT:
+    case CAIRO_STATUS_INVALID_WEIGHT:
+    case CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED:
+    case CAIRO_STATUS_INVALID_MESH_CONSTRUCTION:
+    case CAIRO_STATUS_DEVICE_FINISHED:
+    default:
+       _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+       return (cairo_region_t *) &_cairo_region_nil;
+    }
+}
+
+/**
+ * _cairo_region_set_error:
+ * @region: a region
+ * @status: a status value indicating an error
+ *
+ * Atomically sets region->status to @status and calls _cairo_error;
+ * Does nothing if status is %CAIRO_STATUS_SUCCESS or any of the internal
+ * status values.
+ *
+ * All assignments of an error status to region->status should happen
+ * through _cairo_region_set_error(). Note that due to the nature of
+ * the atomic operation, it is not safe to call this function on the
+ * nil objects.
+ *
+ * The purpose of this function is to allow the user to set a
+ * breakpoint in _cairo_error() to generate a stack trace for when the
+ * user causes cairo to detect an error.
+ *
+ * Return value: the error status.
+ **/
+static cairo_status_t
+_cairo_region_set_error (cairo_region_t *region,
+                        cairo_status_t status)
+{
+    if (status == CAIRO_STATUS_SUCCESS)
+        return CAIRO_STATUS_SUCCESS;
+
+    /* Don't overwrite an existing error. This preserves the first
+     * error, which is the most significant. */
+    _cairo_status_set_error (&region->status, status);
+
+    return _cairo_error (status);
+}
+
+void
+_cairo_region_init (cairo_region_t *region)
+{
+    VG (VALGRIND_MAKE_MEM_UNDEFINED (region, sizeof (cairo_region_t)));
+
+    region->status = CAIRO_STATUS_SUCCESS;
+    CAIRO_REFERENCE_COUNT_INIT (&region->ref_count, 0);
+    pixman_region32_init (&region->rgn);
+}
+
+void
+_cairo_region_init_rectangle (cairo_region_t *region,
+                             const cairo_rectangle_int_t *rectangle)
+{
+    VG (VALGRIND_MAKE_MEM_UNDEFINED (region, sizeof (cairo_region_t)));
+
+    region->status = CAIRO_STATUS_SUCCESS;
+    CAIRO_REFERENCE_COUNT_INIT (&region->ref_count, 0);
+    pixman_region32_init_rect (&region->rgn,
+                              rectangle->x, rectangle->y,
+                              rectangle->width, rectangle->height);
+}
+
+void
+_cairo_region_fini (cairo_region_t *region)
+{
+    assert (! CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&region->ref_count));
+    pixman_region32_fini (&region->rgn);
+    VG (VALGRIND_MAKE_MEM_NOACCESS (region, sizeof (cairo_region_t)));
+}
+
+/**
+ * cairo_region_create:
+ *
+ * Allocates a new empty region object.
+ *
+ * Return value: A newly allocated #cairo_region_t. Free with
+ *   cairo_region_destroy(). This function always returns a
+ *   valid pointer; if memory cannot be allocated, then a special
+ *   error object is returned where all operations on the object do nothing.
+ *   You can check for this with cairo_region_status().
+ *
+ * Since: 1.10
+ **/
+cairo_region_t *
+cairo_region_create (void)
+{
+    cairo_region_t *region;
+
+    region = _cairo_malloc (sizeof (cairo_region_t));
+    if (region == NULL)
+       return (cairo_region_t *) &_cairo_region_nil;
+
+    region->status = CAIRO_STATUS_SUCCESS;
+    CAIRO_REFERENCE_COUNT_INIT (&region->ref_count, 1);
+
+    pixman_region32_init (&region->rgn);
+
+    return region;
+}
+slim_hidden_def (cairo_region_create);
+
+/**
+ * cairo_region_create_rectangles:
+ * @rects: an array of @count rectangles
+ * @count: number of rectangles
+ *
+ * Allocates a new region object containing the union of all given @rects.
+ *
+ * Return value: A newly allocated #cairo_region_t. Free with
+ *   cairo_region_destroy(). This function always returns a
+ *   valid pointer; if memory cannot be allocated, then a special
+ *   error object is returned where all operations on the object do nothing.
+ *   You can check for this with cairo_region_status().
+ *
+ * Since: 1.10
+ **/
+cairo_region_t *
+cairo_region_create_rectangles (const cairo_rectangle_int_t *rects,
+                               int count)
+{
+    pixman_box32_t stack_pboxes[CAIRO_STACK_ARRAY_LENGTH (pixman_box32_t)];
+    pixman_box32_t *pboxes = stack_pboxes;
+    cairo_region_t *region;
+    int i;
+
+    region = _cairo_malloc (sizeof (cairo_region_t));
+    if (unlikely (region == NULL))
+       return _cairo_region_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+    CAIRO_REFERENCE_COUNT_INIT (&region->ref_count, 1);
+    region->status = CAIRO_STATUS_SUCCESS;
+
+    if (count == 1) {
+       pixman_region32_init_rect (&region->rgn,
+                                  rects->x, rects->y,
+                                  rects->width, rects->height);
+
+       return region;
+    }
+
+    if (count > ARRAY_LENGTH (stack_pboxes)) {
+       pboxes = _cairo_malloc_ab (count, sizeof (pixman_box32_t));
+       if (unlikely (pboxes == NULL)) {
+           free (region);
+           return _cairo_region_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+       }
+    }
+
+    for (i = 0; i < count; i++) {
+       pboxes[i].x1 = rects[i].x;
+       pboxes[i].y1 = rects[i].y;
+       pboxes[i].x2 = rects[i].x + rects[i].width;
+       pboxes[i].y2 = rects[i].y + rects[i].height;
+    }
+
+    i = pixman_region32_init_rects (&region->rgn, pboxes, count);
+
+    if (pboxes != stack_pboxes)
+       free (pboxes);
+
+    if (unlikely (i == 0)) {
+       free (region);
+       return _cairo_region_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+    }
+
+    return region;
+}
+slim_hidden_def (cairo_region_create_rectangles);
+
+/**
+ * cairo_region_create_rectangle:
+ * @rectangle: a #cairo_rectangle_int_t
+ *
+ * Allocates a new region object containing @rectangle.
+ *
+ * Return value: A newly allocated #cairo_region_t. Free with
+ *   cairo_region_destroy(). This function always returns a
+ *   valid pointer; if memory cannot be allocated, then a special
+ *   error object is returned where all operations on the object do nothing.
+ *   You can check for this with cairo_region_status().
+ *
+ * Since: 1.10
+ **/
+cairo_region_t *
+cairo_region_create_rectangle (const cairo_rectangle_int_t *rectangle)
+{
+    cairo_region_t *region;
+
+    region = _cairo_malloc (sizeof (cairo_region_t));
+    if (unlikely (region == NULL))
+       return (cairo_region_t *) &_cairo_region_nil;
+
+    region->status = CAIRO_STATUS_SUCCESS;
+    CAIRO_REFERENCE_COUNT_INIT (&region->ref_count, 1);
+
+    pixman_region32_init_rect (&region->rgn,
+                              rectangle->x, rectangle->y,
+                              rectangle->width, rectangle->height);
+
+    return region;
+}
+slim_hidden_def (cairo_region_create_rectangle);
+
+/**
+ * cairo_region_copy:
+ * @original: a #cairo_region_t
+ *
+ * Allocates a new region object copying the area from @original.
+ *
+ * Return value: A newly allocated #cairo_region_t. Free with
+ *   cairo_region_destroy(). This function always returns a
+ *   valid pointer; if memory cannot be allocated, then a special
+ *   error object is returned where all operations on the object do nothing.
+ *   You can check for this with cairo_region_status().
+ *
+ * Since: 1.10
+ **/
+cairo_region_t *
+cairo_region_copy (const cairo_region_t *original)
+{
+    cairo_region_t *copy;
+
+    if (original != NULL && original->status)
+       return (cairo_region_t *) &_cairo_region_nil;
+
+    copy = cairo_region_create ();
+    if (unlikely (copy->status))
+       return copy;
+
+    if (original != NULL &&
+       ! pixman_region32_copy (&copy->rgn, CONST_CAST &original->rgn))
+    {
+       cairo_region_destroy (copy);
+       return (cairo_region_t *) &_cairo_region_nil;
+    }
+
+    return copy;
+}
+slim_hidden_def (cairo_region_copy);
+
+/**
+ * cairo_region_reference:
+ * @region: a #cairo_region_t
+ *
+ * Increases the reference count on @region by one. This prevents
+ * @region from being destroyed until a matching call to
+ * cairo_region_destroy() is made.
+ *
+ * Return value: the referenced #cairo_region_t.
+ *
+ * Since: 1.10
+ **/
+cairo_region_t *
+cairo_region_reference (cairo_region_t *region)
+{
+    if (region == NULL || CAIRO_REFERENCE_COUNT_IS_INVALID (&region->ref_count))
+       return NULL;
+
+    assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&region->ref_count));
+
+    _cairo_reference_count_inc (&region->ref_count);
+    return region;
+}
+slim_hidden_def (cairo_region_reference);
+
+/**
+ * cairo_region_destroy:
+ * @region: a #cairo_region_t
+ *
+ * Destroys a #cairo_region_t object created with
+ * cairo_region_create(), cairo_region_copy(), or
+ * or cairo_region_create_rectangle().
+ *
+ * Since: 1.10
+ **/
+void
+cairo_region_destroy (cairo_region_t *region)
+{
+    if (region == NULL || CAIRO_REFERENCE_COUNT_IS_INVALID (&region->ref_count))
+       return;
+
+    assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&region->ref_count));
+
+    if (! _cairo_reference_count_dec_and_test (&region->ref_count))
+       return;
+
+    _cairo_region_fini (region);
+    free (region);
+}
+slim_hidden_def (cairo_region_destroy);
+
+/**
+ * cairo_region_num_rectangles:
+ * @region: a #cairo_region_t
+ *
+ * Returns the number of rectangles contained in @region.
+ *
+ * Return value: The number of rectangles contained in @region.
+ *
+ * Since: 1.10
+ **/
+int
+cairo_region_num_rectangles (const cairo_region_t *region)
+{
+    if (region->status)
+       return 0;
+
+    return pixman_region32_n_rects (CONST_CAST &region->rgn);
+}
+slim_hidden_def (cairo_region_num_rectangles);
+
+/**
+ * cairo_region_get_rectangle:
+ * @region: a #cairo_region_t
+ * @nth: a number indicating which rectangle should be returned
+ * @rectangle: return location for a #cairo_rectangle_int_t
+ *
+ * Stores the @nth rectangle from the region in @rectangle.
+ *
+ * Since: 1.10
+ **/
+void
+cairo_region_get_rectangle (const cairo_region_t *region,
+                           int nth,
+                           cairo_rectangle_int_t *rectangle)
+{
+    pixman_box32_t *pbox;
+
+    if (region->status) {
+       rectangle->x = rectangle->y = 0;
+       rectangle->width = rectangle->height = 0;
+       return;
+    }
+
+    pbox = pixman_region32_rectangles (CONST_CAST &region->rgn, NULL) + nth;
+
+    rectangle->x = pbox->x1;
+    rectangle->y = pbox->y1;
+    rectangle->width = pbox->x2 - pbox->x1;
+    rectangle->height = pbox->y2 - pbox->y1;
+}
+slim_hidden_def (cairo_region_get_rectangle);
+
+/**
+ * cairo_region_get_extents:
+ * @region: a #cairo_region_t
+ * @extents: rectangle into which to store the extents
+ *
+ * Gets the bounding rectangle of @region as a #cairo_rectangle_int_t
+ *
+ * Since: 1.10
+ **/
+void
+cairo_region_get_extents (const cairo_region_t *region,
+                         cairo_rectangle_int_t *extents)
+{
+    pixman_box32_t *pextents;
+
+    if (region->status) {
+       extents->x = extents->y = 0;
+       extents->width = extents->height = 0;
+       return;
+    }
+
+    pextents = pixman_region32_extents (CONST_CAST &region->rgn);
+
+    extents->x = pextents->x1;
+    extents->y = pextents->y1;
+    extents->width = pextents->x2 - pextents->x1;
+    extents->height = pextents->y2 - pextents->y1;
+}
+slim_hidden_def (cairo_region_get_extents);
+
+/**
+ * cairo_region_status:
+ * @region: a #cairo_region_t
+ *
+ * Checks whether an error has previous occured for this
+ * region object.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY
+ *
+ * Since: 1.10
+ **/
+cairo_status_t
+cairo_region_status (const cairo_region_t *region)
+{
+    return region->status;
+}
+slim_hidden_def (cairo_region_status);
+
+/**
+ * cairo_region_subtract:
+ * @dst: a #cairo_region_t
+ * @other: another #cairo_region_t
+ *
+ * Subtracts @other from @dst and places the result in @dst
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY
+ *
+ * Since: 1.10
+ **/
+cairo_status_t
+cairo_region_subtract (cairo_region_t *dst, const cairo_region_t *other)
+{
+    if (dst->status)
+       return dst->status;
+
+    if (other->status)
+       return _cairo_region_set_error (dst, other->status);
+
+    if (! pixman_region32_subtract (&dst->rgn,
+                                   &dst->rgn,
+                                   CONST_CAST &other->rgn))
+    {
+       return _cairo_region_set_error (dst, CAIRO_STATUS_NO_MEMORY);
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+slim_hidden_def (cairo_region_subtract);
+
+/**
+ * cairo_region_subtract_rectangle:
+ * @dst: a #cairo_region_t
+ * @rectangle: a #cairo_rectangle_int_t
+ *
+ * Subtracts @rectangle from @dst and places the result in @dst
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY
+ *
+ * Since: 1.10
+ **/
+cairo_status_t
+cairo_region_subtract_rectangle (cairo_region_t *dst,
+                                const cairo_rectangle_int_t *rectangle)
+{
+    cairo_status_t status = CAIRO_STATUS_SUCCESS;
+    pixman_region32_t region;
+
+    if (dst->status)
+       return dst->status;
+
+    pixman_region32_init_rect (&region,
+                              rectangle->x, rectangle->y,
+                              rectangle->width, rectangle->height);
+
+    if (! pixman_region32_subtract (&dst->rgn, &dst->rgn, &region))
+       status = _cairo_region_set_error (dst, CAIRO_STATUS_NO_MEMORY);
+
+    pixman_region32_fini (&region);
+
+    return status;
+}
+slim_hidden_def (cairo_region_subtract_rectangle);
+
+/**
+ * cairo_region_intersect:
+ * @dst: a #cairo_region_t
+ * @other: another #cairo_region_t
+ *
+ * Computes the intersection of @dst with @other and places the result in @dst
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY
+ *
+ * Since: 1.10
+ **/
+cairo_status_t
+cairo_region_intersect (cairo_region_t *dst, const cairo_region_t *other)
+{
+    if (dst->status)
+       return dst->status;
+
+    if (other->status)
+       return _cairo_region_set_error (dst, other->status);
+
+    if (! pixman_region32_intersect (&dst->rgn, &dst->rgn, CONST_CAST &other->rgn))
+       return _cairo_region_set_error (dst, CAIRO_STATUS_NO_MEMORY);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+slim_hidden_def (cairo_region_intersect);
+
+/**
+ * cairo_region_intersect_rectangle:
+ * @dst: a #cairo_region_t
+ * @rectangle: a #cairo_rectangle_int_t
+ *
+ * Computes the intersection of @dst with @rectangle and places the
+ * result in @dst
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY
+ *
+ * Since: 1.10
+ **/
+cairo_status_t
+cairo_region_intersect_rectangle (cairo_region_t *dst,
+                                 const cairo_rectangle_int_t *rectangle)
+{
+    cairo_status_t status = CAIRO_STATUS_SUCCESS;
+    pixman_region32_t region;
+
+    if (dst->status)
+       return dst->status;
+
+    pixman_region32_init_rect (&region,
+                              rectangle->x, rectangle->y,
+                              rectangle->width, rectangle->height);
+
+    if (! pixman_region32_intersect (&dst->rgn, &dst->rgn, &region))
+       status = _cairo_region_set_error (dst, CAIRO_STATUS_NO_MEMORY);
+
+    pixman_region32_fini (&region);
+
+    return status;
+}
+slim_hidden_def (cairo_region_intersect_rectangle);
+
+/**
+ * cairo_region_union:
+ * @dst: a #cairo_region_t
+ * @other: another #cairo_region_t
+ *
+ * Computes the union of @dst with @other and places the result in @dst
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY
+ *
+ * Since: 1.10
+ **/
+cairo_status_t
+cairo_region_union (cairo_region_t *dst,
+                   const cairo_region_t *other)
+{
+    if (dst->status)
+       return dst->status;
+
+    if (other->status)
+       return _cairo_region_set_error (dst, other->status);
+
+    if (! pixman_region32_union (&dst->rgn, &dst->rgn, CONST_CAST &other->rgn))
+       return _cairo_region_set_error (dst, CAIRO_STATUS_NO_MEMORY);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+slim_hidden_def (cairo_region_union);
+
+/**
+ * cairo_region_union_rectangle:
+ * @dst: a #cairo_region_t
+ * @rectangle: a #cairo_rectangle_int_t
+ *
+ * Computes the union of @dst with @rectangle and places the result in @dst.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY
+ *
+ * Since: 1.10
+ **/
+cairo_status_t
+cairo_region_union_rectangle (cairo_region_t *dst,
+                             const cairo_rectangle_int_t *rectangle)
+{
+    cairo_status_t status = CAIRO_STATUS_SUCCESS;
+    pixman_region32_t region;
+
+    if (dst->status)
+       return dst->status;
+
+    pixman_region32_init_rect (&region,
+                              rectangle->x, rectangle->y,
+                              rectangle->width, rectangle->height);
+
+    if (! pixman_region32_union (&dst->rgn, &dst->rgn, &region))
+       status = _cairo_region_set_error (dst, CAIRO_STATUS_NO_MEMORY);
+
+    pixman_region32_fini (&region);
+
+    return status;
+}
+slim_hidden_def (cairo_region_union_rectangle);
+
+/**
+ * cairo_region_xor:
+ * @dst: a #cairo_region_t
+ * @other: another #cairo_region_t
+ *
+ * Computes the exclusive difference of @dst with @other and places the
+ * result in @dst. That is, @dst will be set to contain all areas that
+ * are either in @dst or in @other, but not in both.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY
+ *
+ * Since: 1.10
+ **/
+cairo_status_t
+cairo_region_xor (cairo_region_t *dst, const cairo_region_t *other)
+{
+    cairo_status_t status = CAIRO_STATUS_SUCCESS;
+    pixman_region32_t tmp;
+
+    if (dst->status)
+       return dst->status;
+
+    if (other->status)
+       return _cairo_region_set_error (dst, other->status);
+
+    pixman_region32_init (&tmp);
+
+    /* XXX: get an xor function into pixman */
+    if (! pixman_region32_subtract (&tmp, CONST_CAST &other->rgn, &dst->rgn) ||
+        ! pixman_region32_subtract (&dst->rgn, &dst->rgn, CONST_CAST &other->rgn) || 
+        ! pixman_region32_union (&dst->rgn, &dst->rgn, &tmp))
+       status = _cairo_region_set_error (dst, CAIRO_STATUS_NO_MEMORY);
+
+    pixman_region32_fini (&tmp);
+
+    return status;
+}
+slim_hidden_def (cairo_region_xor);
+
+/**
+ * cairo_region_xor_rectangle:
+ * @dst: a #cairo_region_t
+ * @rectangle: a #cairo_rectangle_int_t
+ *
+ * Computes the exclusive difference of @dst with @rectangle and places the
+ * result in @dst. That is, @dst will be set to contain all areas that are 
+ * either in @dst or in @rectangle, but not in both.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY
+ *
+ * Since: 1.10
+ **/
+cairo_status_t
+cairo_region_xor_rectangle (cairo_region_t *dst,
+                           const cairo_rectangle_int_t *rectangle)
+{
+    cairo_status_t status = CAIRO_STATUS_SUCCESS;
+    pixman_region32_t region, tmp;
+
+    if (dst->status)
+       return dst->status;
+
+    pixman_region32_init_rect (&region,
+                              rectangle->x, rectangle->y,
+                              rectangle->width, rectangle->height);
+    pixman_region32_init (&tmp);
+
+    /* XXX: get an xor function into pixman */
+    if (! pixman_region32_subtract (&tmp, &region, &dst->rgn) ||
+        ! pixman_region32_subtract (&dst->rgn, &dst->rgn, &region) || 
+        ! pixman_region32_union (&dst->rgn, &dst->rgn, &tmp))
+       status = _cairo_region_set_error (dst, CAIRO_STATUS_NO_MEMORY);
+
+    pixman_region32_fini (&tmp);
+    pixman_region32_fini (&region);
+
+    return status;
+}
+slim_hidden_def (cairo_region_xor_rectangle);
+
+/**
+ * cairo_region_is_empty:
+ * @region: a #cairo_region_t
+ *
+ * Checks whether @region is empty.
+ *
+ * Return value: %TRUE if @region is empty, %FALSE if it isn't.
+ *
+ * Since: 1.10
+ **/
+cairo_bool_t
+cairo_region_is_empty (const cairo_region_t *region)
+{
+    if (region->status)
+       return TRUE;
+
+    return ! pixman_region32_not_empty (CONST_CAST &region->rgn);
+}
+slim_hidden_def (cairo_region_is_empty);
+
+/**
+ * cairo_region_translate:
+ * @region: a #cairo_region_t
+ * @dx: Amount to translate in the x direction
+ * @dy: Amount to translate in the y direction
+ *
+ * Translates @region by (@dx, @dy).
+ *
+ * Since: 1.10
+ **/
+void
+cairo_region_translate (cairo_region_t *region,
+                       int dx, int dy)
+{
+    if (region->status)
+       return;
+
+    pixman_region32_translate (&region->rgn, dx, dy);
+}
+slim_hidden_def (cairo_region_translate);
+
+/**
+ * cairo_region_overlap_t:
+ * @CAIRO_REGION_OVERLAP_IN: The contents are entirely inside the region
+ * @CAIRO_REGION_OVERLAP_OUT: The contents are entirely outside the region
+ * @CAIRO_REGION_OVERLAP_PART: The contents are partially inside and
+ *     partially outside the region.
+ * 
+ * Used as the return value for cairo_region_contains_rectangle().
+ */
+
+/**
+ * cairo_region_contains_rectangle:
+ * @region: a #cairo_region_t
+ * @rectangle: a #cairo_rectangle_int_t
+ *
+ * Checks whether @rectangle is inside, outside or partially contained
+ * in @region
+ *
+ * Return value:
+ *   %CAIRO_REGION_OVERLAP_IN if @rectangle is entirely inside @region,
+ *   %CAIRO_REGION_OVERLAP_OUT if @rectangle is entirely outside @region, or
+ *   %CAIRO_REGION_OVERLAP_PART if @rectangle is partially inside and partially outside @region.
+ *
+ * Since: 1.10
+ **/
+cairo_region_overlap_t
+cairo_region_contains_rectangle (const cairo_region_t *region,
+                                const cairo_rectangle_int_t *rectangle)
+{
+    pixman_box32_t pbox;
+    pixman_region_overlap_t poverlap;
+
+    if (region->status)
+       return CAIRO_REGION_OVERLAP_OUT;
+
+    pbox.x1 = rectangle->x;
+    pbox.y1 = rectangle->y;
+    pbox.x2 = rectangle->x + rectangle->width;
+    pbox.y2 = rectangle->y + rectangle->height;
+
+    poverlap = pixman_region32_contains_rectangle (CONST_CAST &region->rgn,
+                                                  &pbox);
+    switch (poverlap) {
+    default:
+    case PIXMAN_REGION_OUT:  return CAIRO_REGION_OVERLAP_OUT;
+    case PIXMAN_REGION_IN:   return CAIRO_REGION_OVERLAP_IN;
+    case PIXMAN_REGION_PART: return CAIRO_REGION_OVERLAP_PART;
+    }
+}
+slim_hidden_def (cairo_region_contains_rectangle);
+
+/**
+ * cairo_region_contains_point:
+ * @region: a #cairo_region_t
+ * @x: the x coordinate of a point
+ * @y: the y coordinate of a point
+ *
+ * Checks whether (@x, @y) is contained in @region.
+ *
+ * Return value: %TRUE if (@x, @y) is contained in @region, %FALSE if it is not.
+ *
+ * Since: 1.10
+ **/
+cairo_bool_t
+cairo_region_contains_point (const cairo_region_t *region,
+                            int x, int y)
+{
+    pixman_box32_t box;
+
+    if (region->status)
+       return FALSE;
+
+    return pixman_region32_contains_point (CONST_CAST &region->rgn, x, y, &box);
+}
+slim_hidden_def (cairo_region_contains_point);
+
+/**
+ * cairo_region_equal:
+ * @a: a #cairo_region_t or %NULL
+ * @b: a #cairo_region_t or %NULL
+ *
+ * Compares whether region_a is equivalent to region_b. %NULL as an argument
+ * is equal to itself, but not to any non-%NULL region.
+ *
+ * Return value: %TRUE if both regions contained the same coverage,
+ * %FALSE if it is not or any region is in an error status.
+ *
+ * Since: 1.10
+ **/
+cairo_bool_t
+cairo_region_equal (const cairo_region_t *a,
+                   const cairo_region_t *b)
+{
+    /* error objects are never equal */
+    if ((a != NULL && a->status) || (b != NULL && b->status))
+       return FALSE;
+
+    if (a == b)
+       return TRUE;
+
+    if (a == NULL || b == NULL)
+       return FALSE;
+
+    return pixman_region32_equal (CONST_CAST &a->rgn, CONST_CAST &b->rgn);
+}
+slim_hidden_def (cairo_region_equal);
diff --git a/src/cairo-rtree-private.h b/src/cairo-rtree-private.h
new file mode 100644 (file)
index 0000000..11079e7
--- /dev/null
@@ -0,0 +1,135 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Chris Wilson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Chris Wilson.
+ *
+ * Contributor(s):
+ *      Chris Wilson <chris@chris-wilson.co.uk>
+ *
+ */
+
+#ifndef CAIRO_RTREE_PRIVATE_H
+#define CAIRO_RTREE_PRIVATE_H
+
+#include "cairo-compiler-private.h"
+#include "cairo-error-private.h"
+#include "cairo-types-private.h"
+
+#include "cairo-freelist-private.h"
+#include "cairo-list-private.h"
+
+enum {
+    CAIRO_RTREE_NODE_AVAILABLE,
+    CAIRO_RTREE_NODE_DIVIDED,
+    CAIRO_RTREE_NODE_OCCUPIED,
+};
+
+typedef struct _cairo_rtree_node {
+    struct _cairo_rtree_node *children[4], *parent;
+    void **owner;
+    cairo_list_t link;
+    uint16_t pinned;
+    uint16_t state;
+    uint16_t x, y;
+    uint16_t width, height;
+} cairo_rtree_node_t;
+
+typedef struct _cairo_rtree {
+    cairo_rtree_node_t root;
+    int min_size;
+    cairo_list_t pinned;
+    cairo_list_t available;
+    cairo_list_t evictable;
+    cairo_freepool_t node_freepool;
+} cairo_rtree_t;
+
+cairo_private cairo_rtree_node_t *
+_cairo_rtree_node_create (cairo_rtree_t                 *rtree,
+                         cairo_rtree_node_t     *parent,
+                         int                     x,
+                         int                     y,
+                         int                     width,
+                         int                     height);
+
+cairo_private cairo_status_t
+_cairo_rtree_node_insert (cairo_rtree_t *rtree,
+                         cairo_rtree_node_t *node,
+                         int width,
+                         int height,
+                         cairo_rtree_node_t **out);
+
+cairo_private void
+_cairo_rtree_node_collapse (cairo_rtree_t *rtree, cairo_rtree_node_t *node);
+
+cairo_private void
+_cairo_rtree_node_remove (cairo_rtree_t *rtree, cairo_rtree_node_t *node);
+
+cairo_private void
+_cairo_rtree_node_destroy (cairo_rtree_t *rtree, cairo_rtree_node_t *node);
+
+cairo_private void
+_cairo_rtree_init (cairo_rtree_t       *rtree,
+                  int                   width,
+                  int                   height,
+                  int                   min_size,
+                  int                   node_size);
+
+cairo_private cairo_int_status_t
+_cairo_rtree_insert (cairo_rtree_t          *rtree,
+                    int                      width,
+                    int                      height,
+                    cairo_rtree_node_t     **out);
+
+cairo_private cairo_int_status_t
+_cairo_rtree_evict_random (cairo_rtree_t        *rtree,
+                          int                    width,
+                          int                    height,
+                          cairo_rtree_node_t   **out);
+
+static inline void *
+_cairo_rtree_pin (cairo_rtree_t *rtree, cairo_rtree_node_t *node)
+{
+    if (! node->pinned) {
+       cairo_list_move (&node->link, &rtree->pinned);
+       node->pinned = 1;
+    }
+
+    return node;
+}
+
+cairo_private void
+_cairo_rtree_unpin (cairo_rtree_t *rtree);
+
+cairo_private void
+_cairo_rtree_reset (cairo_rtree_t *rtree);
+
+cairo_private void
+_cairo_rtree_fini (cairo_rtree_t *rtree);
+
+#endif /* CAIRO_RTREE_PRIVATE_H */
diff --git a/src/cairo-rtree.c b/src/cairo-rtree.c
new file mode 100644 (file)
index 0000000..d6e5791
--- /dev/null
@@ -0,0 +1,385 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Chris Wilson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Chris Wilson.
+ *
+ * Contributor(s):
+ *      Chris Wilson <chris@chris-wilson.co.uk>
+ *
+ */
+
+#include "cairoint.h"
+
+#include "cairo-error-private.h"
+#include "cairo-rtree-private.h"
+
+cairo_rtree_node_t *
+_cairo_rtree_node_create (cairo_rtree_t                 *rtree,
+                         cairo_rtree_node_t     *parent,
+                         int                     x,
+                         int                     y,
+                         int                     width,
+                         int                     height)
+{
+    cairo_rtree_node_t *node;
+
+    node = _cairo_freepool_alloc (&rtree->node_freepool);
+    if (node == NULL) {
+       _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+       return NULL;
+    }
+
+    node->children[0] = NULL;
+    node->parent = parent;
+    node->owner  = NULL;
+    node->state  = CAIRO_RTREE_NODE_AVAILABLE;
+    node->pinned = FALSE;
+    node->x     = x;
+    node->y     = y;
+    node->width  = width;
+    node->height = height;
+
+    cairo_list_add (&node->link, &rtree->available);
+
+    return node;
+}
+
+void
+_cairo_rtree_node_destroy (cairo_rtree_t *rtree, cairo_rtree_node_t *node)
+{
+    int i;
+
+    cairo_list_del (&node->link);
+
+    if (node->state == CAIRO_RTREE_NODE_OCCUPIED) {
+       if (node->owner != NULL)
+           *node->owner = NULL;
+    } else {
+       for (i = 0; i < 4 && node->children[i] != NULL; i++)
+           _cairo_rtree_node_destroy (rtree, node->children[i]);
+    }
+
+    _cairo_freepool_free (&rtree->node_freepool, node);
+}
+
+void
+_cairo_rtree_node_collapse (cairo_rtree_t *rtree, cairo_rtree_node_t *node)
+{
+    int i;
+
+    do {
+       assert (node->state == CAIRO_RTREE_NODE_DIVIDED);
+
+       for (i = 0;  i < 4 && node->children[i] != NULL; i++)
+           if (node->children[i]->state != CAIRO_RTREE_NODE_AVAILABLE)
+               return;
+
+       for (i = 0; i < 4 && node->children[i] != NULL; i++)
+           _cairo_rtree_node_destroy (rtree, node->children[i]);
+
+       node->children[0] = NULL;
+       node->state = CAIRO_RTREE_NODE_AVAILABLE;
+       cairo_list_move (&node->link, &rtree->available);
+    } while ((node = node->parent) != NULL);
+}
+
+cairo_status_t
+_cairo_rtree_node_insert (cairo_rtree_t *rtree,
+                         cairo_rtree_node_t *node,
+                         int width,
+                         int height,
+                         cairo_rtree_node_t **out)
+{
+    int w, h, i;
+
+    assert (node->state == CAIRO_RTREE_NODE_AVAILABLE);
+    assert (node->pinned == FALSE);
+
+    if (node->width  - width  > rtree->min_size ||
+       node->height - height > rtree->min_size)
+    {
+       w = node->width  - width;
+       h = node->height - height;
+
+       i = 0;
+       node->children[i] = _cairo_rtree_node_create (rtree, node,
+                                                     node->x, node->y,
+                                                     width, height);
+       if (unlikely (node->children[i] == NULL))
+           return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+       i++;
+
+       if (w > rtree->min_size) {
+           node->children[i] = _cairo_rtree_node_create (rtree, node,
+                                                         node->x + width,
+                                                         node->y,
+                                                         w, height);
+           if (unlikely (node->children[i] == NULL))
+               return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+           i++;
+       }
+
+       if (h > rtree->min_size) {
+           node->children[i] = _cairo_rtree_node_create (rtree, node,
+                                                         node->x,
+                                                         node->y + height,
+                                                         width, h);
+           if (unlikely (node->children[i] == NULL))
+               return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+           i++;
+
+           if (w > rtree->min_size) {
+               node->children[i] = _cairo_rtree_node_create (rtree, node,
+                                                             node->x + width,
+                                                             node->y + height,
+                                                             w, h);
+               if (unlikely (node->children[i] == NULL))
+                   return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+               i++;
+           }
+       }
+
+       if (i < 4)
+           node->children[i] = NULL;
+
+       node->state = CAIRO_RTREE_NODE_DIVIDED;
+       cairo_list_move (&node->link, &rtree->evictable);
+       node = node->children[0];
+    }
+
+    node->state = CAIRO_RTREE_NODE_OCCUPIED;
+    cairo_list_move (&node->link, &rtree->evictable);
+    *out = node;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+void
+_cairo_rtree_node_remove (cairo_rtree_t *rtree, cairo_rtree_node_t *node)
+{
+    assert (node->state == CAIRO_RTREE_NODE_OCCUPIED);
+    assert (node->pinned == FALSE);
+
+    node->state = CAIRO_RTREE_NODE_AVAILABLE;
+    cairo_list_move (&node->link, &rtree->available);
+
+    _cairo_rtree_node_collapse (rtree, node->parent);
+}
+
+cairo_int_status_t
+_cairo_rtree_insert (cairo_rtree_t          *rtree,
+                    int                      width,
+                    int                      height,
+                    cairo_rtree_node_t     **out)
+{
+    cairo_rtree_node_t *node;
+
+    cairo_list_foreach_entry (node, cairo_rtree_node_t,
+                             &rtree->available, link)
+    {
+       if (node->width >= width && node->height >= height)
+           return _cairo_rtree_node_insert (rtree, node, width, height, out);
+    }
+
+    return CAIRO_INT_STATUS_UNSUPPORTED;
+}
+
+static uint32_t
+hars_petruska_f54_1_random (void)
+{
+#define rol(x,k) ((x << k) | (x >> (32-k)))
+    static uint32_t x;
+    return x = (x ^ rol (x, 5) ^ rol (x, 24)) + 0x37798849;
+#undef rol
+}
+
+cairo_int_status_t
+_cairo_rtree_evict_random (cairo_rtree_t        *rtree,
+                          int                    width,
+                          int                    height,
+                          cairo_rtree_node_t           **out)
+{
+    cairo_rtree_node_t *node, *next;
+    int i, cnt;
+
+    /* propagate pinned from children to root */
+    cairo_list_foreach_entry_safe (node, next, cairo_rtree_node_t,
+                                  &rtree->pinned, link)
+    {
+       if (node->parent != NULL)
+           _cairo_rtree_pin (rtree, node->parent);
+    }
+
+    cnt = 0;
+    cairo_list_foreach_entry (node, cairo_rtree_node_t,
+                             &rtree->evictable, link)
+    {
+       if (node->width >= width && node->height >= height)
+           cnt++;
+    }
+
+    if (cnt == 0)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    cnt = hars_petruska_f54_1_random () % cnt;
+    cairo_list_foreach_entry (node, cairo_rtree_node_t,
+                             &rtree->evictable, link)
+    {
+       if (node->width >= width && node->height >= height && cnt-- == 0) {
+           if (node->state == CAIRO_RTREE_NODE_OCCUPIED) {
+               if (node->owner != NULL)
+                   *node->owner = NULL;
+           } else {
+               for (i = 0; i < 4 && node->children[i] != NULL; i++)
+                   _cairo_rtree_node_destroy (rtree, node->children[i]);
+               node->children[0] = NULL;
+           }
+
+           node->state = CAIRO_RTREE_NODE_AVAILABLE;
+           cairo_list_move (&node->link, &rtree->available);
+
+           *out = node;
+           return CAIRO_STATUS_SUCCESS;
+       }
+    }
+
+    return CAIRO_INT_STATUS_UNSUPPORTED;
+}
+
+void
+_cairo_rtree_unpin (cairo_rtree_t *rtree)
+{
+    cairo_rtree_node_t *node, *next;
+    cairo_list_t can_collapse;
+
+    if (cairo_list_is_empty (&rtree->pinned))
+       return;
+
+    cairo_list_init (&can_collapse);
+
+    cairo_list_foreach_entry_safe (node, next,
+                                  cairo_rtree_node_t,
+                                  &rtree->pinned,
+                                  link)
+    {
+       node->pinned = FALSE;
+       if (node->state == CAIRO_RTREE_NODE_OCCUPIED && node->owner == NULL) {
+           cairo_bool_t all_available;
+           int i;
+
+           node->state = CAIRO_RTREE_NODE_AVAILABLE;
+           cairo_list_move (&node->link, &rtree->available);
+
+           all_available = TRUE;
+           node = node->parent;
+           for (i = 0; i < 4 && node->children[i] != NULL && all_available; i++)
+               all_available &= node->children[i]->state == CAIRO_RTREE_NODE_AVAILABLE;
+
+           if (all_available) {
+               cairo_list_move (&node->link, &can_collapse);
+               for (i = 0;  i < 4 && node->children[i] != NULL; i++)
+                   cairo_list_del (&node->children[i]->link);
+           }
+       }
+       else
+       {
+           cairo_list_move (&node->link, &rtree->evictable);
+       }
+    }
+
+    cairo_list_foreach_entry_safe (node, next,
+                                  cairo_rtree_node_t,
+                                  &can_collapse,
+                                  link)
+    {
+       _cairo_rtree_node_collapse (rtree, node);
+    }
+}
+
+void
+_cairo_rtree_init (cairo_rtree_t       *rtree,
+                  int                   width,
+                  int                   height,
+                  int                   min_size,
+                  int                   node_size)
+{
+    assert (node_size >= (int) sizeof (cairo_rtree_node_t));
+    _cairo_freepool_init (&rtree->node_freepool, node_size);
+
+    cairo_list_init (&rtree->available);
+    cairo_list_init (&rtree->pinned);
+    cairo_list_init (&rtree->evictable);
+
+    rtree->min_size = min_size;
+
+    memset (&rtree->root, 0, sizeof (rtree->root));
+    rtree->root.width = width;
+    rtree->root.height = height;
+    rtree->root.state = CAIRO_RTREE_NODE_AVAILABLE;
+    cairo_list_add (&rtree->root.link, &rtree->available);
+}
+
+void
+_cairo_rtree_reset (cairo_rtree_t *rtree)
+{
+    int i;
+
+    if (rtree->root.state == CAIRO_RTREE_NODE_OCCUPIED) {
+       if (rtree->root.owner != NULL)
+           *rtree->root.owner = NULL;
+    } else {
+       for (i = 0; i < 4 && rtree->root.children[i] != NULL; i++)
+           _cairo_rtree_node_destroy (rtree, rtree->root.children[i]);
+       rtree->root.children[0] = NULL;
+    }
+
+    cairo_list_init (&rtree->available);
+    cairo_list_init (&rtree->evictable);
+    cairo_list_init (&rtree->pinned);
+
+    rtree->root.state = CAIRO_RTREE_NODE_AVAILABLE;
+    rtree->root.pinned = FALSE;
+    cairo_list_add (&rtree->root.link, &rtree->available);
+}
+
+void
+_cairo_rtree_fini (cairo_rtree_t *rtree)
+{
+    int i;
+
+    if (rtree->root.state == CAIRO_RTREE_NODE_OCCUPIED) {
+       if (rtree->root.owner != NULL)
+           *rtree->root.owner = NULL;
+    } else {
+       for (i = 0; i < 4 && rtree->root.children[i] != NULL; i++)
+           _cairo_rtree_node_destroy (rtree, rtree->root.children[i]);
+    }
+
+    _cairo_freepool_fini (&rtree->node_freepool);
+}
diff --git a/src/cairo-scaled-font-private.h b/src/cairo-scaled-font-private.h
new file mode 100644 (file)
index 0000000..da7b346
--- /dev/null
@@ -0,0 +1,183 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *     Carl D. Worth <cworth@cworth.org>
+ */
+
+#ifndef CAIRO_SCALED_FONT_PRIVATE_H
+#define CAIRO_SCALED_FONT_PRIVATE_H
+
+#include "cairo.h"
+
+#include "cairo-types-private.h"
+#include "cairo-list-private.h"
+#include "cairo-mutex-type-private.h"
+#include "cairo-reference-count-private.h"
+
+CAIRO_BEGIN_DECLS
+
+typedef struct _cairo_scaled_glyph_page cairo_scaled_glyph_page_t;
+
+struct _cairo_scaled_font {
+    /* For most cairo objects, the rule for multiple threads is that
+     * the user is responsible for any locking if the same object is
+     * manipulated from multiple threads simultaneously.
+     *
+     * However, with the caching that cairo does for scaled fonts, a
+     * user can easily end up with the same cairo_scaled_font object
+     * being manipulated from multiple threads without the user ever
+     * being aware of this, (and in fact, unable to control it).
+     *
+     * So, as a special exception, the cairo implementation takes care
+     * of all locking needed for cairo_scaled_font_t. Most of what is
+     * in the scaled font is immutable, (which is what allows for the
+     * sharing in the first place). The things that are modified and
+     * the locks protecting them are as follows:
+     *
+     * 1. The reference count (scaled_font->ref_count)
+     *
+     *    Modifications to the reference count are protected by the
+     *    _cairo_scaled_font_map_mutex. This is because the reference
+     *    count of a scaled font is intimately related with the font
+     *    map itself, (and the magic holdovers array).
+     *
+     * 2. The cache of glyphs (scaled_font->glyphs)
+     * 3. The backend private data (scaled_font->surface_backend,
+     *                             scaled_font->surface_private)
+     *
+     *    Modifications to these fields are protected with locks on
+     *    scaled_font->mutex in the generic scaled_font code.
+     */
+
+    cairo_hash_entry_t hash_entry;
+
+    /* useful bits for _cairo_scaled_font_nil */
+    cairo_status_t status;
+    cairo_reference_count_t ref_count;
+    cairo_user_data_array_t user_data;
+
+    cairo_font_face_t *original_font_face; /* may be NULL */
+
+    /* hash key members */
+    cairo_font_face_t *font_face; /* may be NULL */
+    cairo_matrix_t font_matrix;          /* font space => user space */
+    cairo_matrix_t ctm;                  /* user space => device space */
+    cairo_font_options_t options;
+
+    unsigned int placeholder : 1; /*  protected by fontmap mutex */
+    unsigned int holdover : 1;
+    unsigned int finished : 1;
+
+    /* "live" scaled_font members */
+    cairo_matrix_t scale;           /* font space => device space */
+    cairo_matrix_t scale_inverse;    /* device space => font space */
+    double max_scale;               /* maximum x/y expansion of scale */
+    cairo_font_extents_t extents;    /* user space */
+    cairo_font_extents_t fs_extents; /* font space */
+
+    /* The mutex protects modification to all subsequent fields. */
+    cairo_mutex_t mutex;
+
+    cairo_hash_table_t *glyphs;
+    cairo_list_t glyph_pages;
+    cairo_bool_t cache_frozen;
+    cairo_bool_t global_cache_frozen;
+
+    cairo_list_t dev_privates;
+
+    /* font backend managing this scaled font */
+    const cairo_scaled_font_backend_t *backend;
+    cairo_list_t link;
+};
+
+struct _cairo_scaled_font_private {
+    cairo_list_t link;
+    const void *key;
+    void (*destroy) (cairo_scaled_font_private_t *,
+                    cairo_scaled_font_t *);
+};
+
+struct _cairo_scaled_glyph {
+    cairo_hash_entry_t hash_entry;
+
+    cairo_text_extents_t    metrics;           /* user-space metrics */
+    cairo_text_extents_t    fs_metrics;                /* font-space metrics */
+    cairo_box_t                    bbox;               /* device-space bounds */
+    int16_t                 x_advance;         /* device-space rounded X advance */
+    int16_t                 y_advance;         /* device-space rounded Y advance */
+
+    unsigned int           has_info;
+    cairo_image_surface_t   *surface;          /* device-space image */
+    cairo_path_fixed_t     *path;              /* device-space outline */
+    cairo_surface_t         *recording_surface;        /* device-space recording-surface */
+
+    const void            *dev_private_key;
+    void                  *dev_private;
+    cairo_list_t            dev_privates;
+};
+
+struct _cairo_scaled_glyph_private {
+    cairo_list_t link;
+    const void *key;
+    void (*destroy) (cairo_scaled_glyph_private_t *,
+                    cairo_scaled_glyph_t *,
+                    cairo_scaled_font_t *);
+};
+
+cairo_private cairo_scaled_font_private_t *
+_cairo_scaled_font_find_private (cairo_scaled_font_t *scaled_font,
+                                const void *key);
+
+cairo_private void
+_cairo_scaled_font_attach_private (cairo_scaled_font_t *scaled_font,
+                                  cairo_scaled_font_private_t *priv,
+                                  const void *key,
+                                  void (*destroy) (cairo_scaled_font_private_t *,
+                                                   cairo_scaled_font_t *));
+
+cairo_private cairo_scaled_glyph_private_t *
+_cairo_scaled_glyph_find_private (cairo_scaled_glyph_t *scaled_glyph,
+                                const void *key);
+
+cairo_private void
+_cairo_scaled_glyph_attach_private (cairo_scaled_glyph_t *scaled_glyph,
+                                  cairo_scaled_glyph_private_t *priv,
+                                  const void *key,
+                                  void (*destroy) (cairo_scaled_glyph_private_t *,
+                                                   cairo_scaled_glyph_t *,
+                                                   cairo_scaled_font_t *));
+
+CAIRO_END_DECLS
+
+#endif /* CAIRO_SCALED_FONT_PRIVATE_H */
diff --git a/src/cairo-scaled-font-subsets-private.h b/src/cairo-scaled-font-subsets-private.h
new file mode 100644 (file)
index 0000000..2edf770
--- /dev/null
@@ -0,0 +1,720 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2006 Red Hat, Inc
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *     Carl D. Worth <cworth@cworth.org>
+ */
+
+#ifndef CAIRO_SCALED_FONT_SUBSETS_PRIVATE_H
+#define CAIRO_SCALED_FONT_SUBSETS_PRIVATE_H
+
+#include "cairoint.h"
+
+#if CAIRO_HAS_FONT_SUBSET
+
+typedef struct _cairo_scaled_font_subsets_glyph {
+    unsigned int font_id;
+    unsigned int subset_id;
+    unsigned int subset_glyph_index;
+    cairo_bool_t is_scaled;
+    cairo_bool_t is_composite;
+    cairo_bool_t is_latin;
+    double       x_advance;
+    double       y_advance;
+    cairo_bool_t utf8_is_mapped;
+    uint32_t    unicode;
+} cairo_scaled_font_subsets_glyph_t;
+
+/**
+ * _cairo_scaled_font_subsets_create_scaled:
+ *
+ * Create a new #cairo_scaled_font_subsets_t object which can be used
+ * to create subsets of any number of #cairo_scaled_font_t
+ * objects. This allows the (arbitrarily large and sparse) glyph
+ * indices of a #cairo_scaled_font_t to be mapped to one or more font
+ * subsets with glyph indices packed into the range
+ * [0 .. max_glyphs_per_subset).
+ *
+ * Return value: a pointer to the newly creates font subsets. The
+ * caller owns this object and should call
+ * _cairo_scaled_font_subsets_destroy() when done with it.
+ **/
+cairo_private cairo_scaled_font_subsets_t *
+_cairo_scaled_font_subsets_create_scaled (void);
+
+/**
+ * _cairo_scaled_font_subsets_create_simple:
+ *
+ * Create a new #cairo_scaled_font_subsets_t object which can be used
+ * to create font subsets suitable for embedding as Postscript or PDF
+ * simple fonts.
+ *
+ * Glyphs with an outline path available will be mapped to one font
+ * subset for each font face. Glyphs from bitmap fonts will mapped to
+ * separate font subsets for each #cairo_scaled_font_t object.
+ *
+ * The maximum number of glyphs per subset is 256. Each subset
+ * reserves the first glyph for the .notdef glyph.
+ *
+ * Return value: a pointer to the newly creates font subsets. The
+ * caller owns this object and should call
+ * _cairo_scaled_font_subsets_destroy() when done with it.
+ **/
+cairo_private cairo_scaled_font_subsets_t *
+_cairo_scaled_font_subsets_create_simple (void);
+
+/**
+ * _cairo_scaled_font_subsets_create_composite:
+ *
+ * Create a new #cairo_scaled_font_subsets_t object which can be used
+ * to create font subsets suitable for embedding as Postscript or PDF
+ * composite fonts.
+ *
+ * Glyphs with an outline path available will be mapped to one font
+ * subset for each font face. Each unscaled subset has a maximum of
+ * 65536 glyphs except for Type1 fonts which have a maximum of 256 glyphs.
+ *
+ * Glyphs from bitmap fonts will mapped to separate font subsets for
+ * each #cairo_scaled_font_t object. Each unscaled subset has a maximum
+ * of 256 glyphs.
+ *
+ * Each subset reserves the first glyph for the .notdef glyph.
+ *
+ * Return value: a pointer to the newly creates font subsets. The
+ * caller owns this object and should call
+ * _cairo_scaled_font_subsets_destroy() when done with it.
+ **/
+cairo_private cairo_scaled_font_subsets_t *
+_cairo_scaled_font_subsets_create_composite (void);
+
+/**
+ * _cairo_scaled_font_subsets_destroy:
+ * @font_subsets: a #cairo_scaled_font_subsets_t object to be destroyed
+ *
+ * Destroys @font_subsets and all resources associated with it.
+ **/
+cairo_private void
+_cairo_scaled_font_subsets_destroy (cairo_scaled_font_subsets_t *font_subsets);
+
+/**
+ * _cairo_scaled_font_subsets_enable_latin_subset:
+ * @font_subsets: a #cairo_scaled_font_subsets_t object to be destroyed
+ * @use_latin: a #cairo_bool_t indicating if a latin subset is to be used
+ *
+ * If enabled, all CP1252 characters will be placed in a separate
+ * 8-bit latin subset.
+ **/
+cairo_private void
+_cairo_scaled_font_subsets_enable_latin_subset (cairo_scaled_font_subsets_t *font_subsets,
+                                               cairo_bool_t                 use_latin);
+
+/**
+ * _cairo_scaled_font_subsets_map_glyph:
+ * @font_subsets: a #cairo_scaled_font_subsets_t
+ * @scaled_font: the font of the glyph to be mapped
+ * @scaled_font_glyph_index: the index of the glyph to be mapped
+ * @utf8: a string of text encoded in UTF-8
+ * @utf8_len: length of @utf8 in bytes
+ * @subset_glyph_ret: return structure containing subset font and glyph id
+ *
+ * Map a glyph from a #cairo_scaled_font to a new index within a
+ * subset of that font. The mapping performed is from the tuple:
+ *
+ *     (scaled_font, scaled_font_glyph_index)
+ *
+ * to the tuple:
+ *
+ *     (font_id, subset_id, subset_glyph_index)
+ *
+ * This mapping is 1:1. If the input tuple has previously mapped, the
+ * the output tuple previously returned will be returned again.
+ *
+ * Otherwise, the return tuple will be constructed as follows:
+ *
+ * 1) There is a 1:1 correspondence between the input scaled_font
+ *    value and the output font_id value. If no mapping has been
+ *    previously performed with the scaled_font value then the
+ *    smallest unused font_id value will be returned.
+ *
+ * 2) Within the set of output tuples of the same font_id value the
+ *    smallest value of subset_id will be returned such that
+ *    subset_glyph_index does not exceed max_glyphs_per_subset (as
+ *    passed to _cairo_scaled_font_subsets_create()) and that the
+ *    resulting tuple is unique.
+ *
+ * 3) The smallest value of subset_glyph_index is returned such that
+ *    the resulting tuple is unique.
+ *
+ * The net result is that any #cairo_scaled_font_t will be represented
+ * by one or more font subsets. Each subset is effectively a tuple of
+ * (scaled_font, font_id, subset_id) and within each subset there
+ * exists a mapping of scaled_glyph_font_index to subset_glyph_index.
+ *
+ * This final description of a font subset is the same representation
+ * used by #cairo_scaled_font_subset_t as provided by
+ * _cairo_scaled_font_subsets_foreach.
+ *
+ * @utf8 and @utf8_len specify a string of unicode characters that the
+ * glyph @scaled_font_glyph_index maps to. If @utf8_is_mapped in
+ * @subset_glyph_ret is %TRUE, the font subsetting will (where index to
+ * unicode mapping is supported) ensure that @scaled_font_glyph_index
+ * maps to @utf8. If @utf8_is_mapped is %FALSE,
+ * @scaled_font_glyph_index has already been mapped to a different
+ * unicode string.
+ *
+ * The returned values in the #cairo_scaled_font_subsets_glyph_t struct are:
+ *
+ * @font_id: The font ID of the mapped glyph
+ * @subset_id : The subset ID of the mapped glyph within the @font_id
+ * @subset_glyph_index: The index of the mapped glyph within the @subset_id subset
+ * @is_scaled: If true, the mapped glyph is from a bitmap font, and separate font
+ * subset is created for each font scale used. If false, the outline of the mapped glyph
+ * is available. One font subset for each font face is created.
+ * @x_advance, @y_advance: When @is_scaled is true, @x_advance and @y_advance contain
+ * the x and y advance for the mapped glyph in device space.
+ * When @is_scaled is false, @x_advance and @y_advance contain the x and y advance for
+ * the the mapped glyph from an unhinted 1 point font.
+ * @utf8_is_mapped: If true the utf8 string provided to _cairo_scaled_font_subsets_map_glyph()
+ * is (or already was) the utf8 string mapped to this glyph. If false the glyph is already
+ * mapped to a different utf8 string.
+ * @unicode: the unicode character mapped to this glyph by the font backend.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS if successful, or a non-zero
+ * value indicating an error. Possible errors include
+ * %CAIRO_STATUS_NO_MEMORY.
+ **/
+cairo_private cairo_status_t
+_cairo_scaled_font_subsets_map_glyph (cairo_scaled_font_subsets_t      *font_subsets,
+                                     cairo_scaled_font_t               *scaled_font,
+                                     unsigned long                      scaled_font_glyph_index,
+                                     const char *                       utf8,
+                                     int                                utf8_len,
+                                      cairo_scaled_font_subsets_glyph_t *subset_glyph_ret);
+
+typedef cairo_int_status_t
+(*cairo_scaled_font_subset_callback_func_t) (cairo_scaled_font_subset_t        *font_subset,
+                                            void                       *closure);
+
+/**
+ * _cairo_scaled_font_subsets_foreach:
+ * @font_subsets: a #cairo_scaled_font_subsets_t
+ * @font_subset_callback: a function to be called for each font subset
+ * @closure: closure data for the callback function
+ *
+ * Iterate over each unique scaled font subset as created by calls to
+ * _cairo_scaled_font_subsets_map_glyph(). A subset is determined by
+ * unique pairs of (font_id, subset_id) as returned by
+ * _cairo_scaled_font_subsets_map_glyph().
+ *
+ * For each subset, @font_subset_callback will be called and will be
+ * provided with both a #cairo_scaled_font_subset_t object containing
+ * all the glyphs in the subset as well as the value of @closure.
+ *
+ * The #cairo_scaled_font_subset_t object contains the scaled_font,
+ * the font_id, and the subset_id corresponding to all glyphs
+ * belonging to the subset. In addition, it contains an array providing
+ * a mapping between subset glyph indices and the original scaled font
+ * glyph indices.
+ *
+ * The index of the array corresponds to subset_glyph_index values
+ * returned by _cairo_scaled_font_subsets_map_glyph() while the
+ * values of the array correspond to the scaled_font_glyph_index
+ * values passed as input to the same function.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS if successful, or a non-zero
+ * value indicating an error. Possible errors include
+ * %CAIRO_STATUS_NO_MEMORY.
+ **/
+cairo_private cairo_status_t
+_cairo_scaled_font_subsets_foreach_scaled (cairo_scaled_font_subsets_t             *font_subsets,
+                                          cairo_scaled_font_subset_callback_func_t  font_subset_callback,
+                                          void                                     *closure);
+
+/**
+ * _cairo_scaled_font_subsets_foreach_unscaled:
+ * @font_subsets: a #cairo_scaled_font_subsets_t
+ * @font_subset_callback: a function to be called for each font subset
+ * @closure: closure data for the callback function
+ *
+ * Iterate over each unique unscaled font subset as created by calls to
+ * _cairo_scaled_font_subsets_map_glyph(). A subset is determined by
+ * unique pairs of (font_id, subset_id) as returned by
+ * _cairo_scaled_font_subsets_map_glyph().
+ *
+ * For each subset, @font_subset_callback will be called and will be
+ * provided with both a #cairo_scaled_font_subset_t object containing
+ * all the glyphs in the subset as well as the value of @closure.
+ *
+ * The #cairo_scaled_font_subset_t object contains the scaled_font,
+ * the font_id, and the subset_id corresponding to all glyphs
+ * belonging to the subset. In addition, it contains an array providing
+ * a mapping between subset glyph indices and the original scaled font
+ * glyph indices.
+ *
+ * The index of the array corresponds to subset_glyph_index values
+ * returned by _cairo_scaled_font_subsets_map_glyph() while the
+ * values of the array correspond to the scaled_font_glyph_index
+ * values passed as input to the same function.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS if successful, or a non-zero
+ * value indicating an error. Possible errors include
+ * %CAIRO_STATUS_NO_MEMORY.
+ **/
+cairo_private cairo_status_t
+_cairo_scaled_font_subsets_foreach_unscaled (cairo_scaled_font_subsets_t              *font_subsets,
+                                             cairo_scaled_font_subset_callback_func_t  font_subset_callback,
+                                            void                                     *closure);
+
+/**
+ * _cairo_scaled_font_subsets_foreach_user:
+ * @font_subsets: a #cairo_scaled_font_subsets_t
+ * @font_subset_callback: a function to be called for each font subset
+ * @closure: closure data for the callback function
+ *
+ * Iterate over each unique scaled font subset as created by calls to
+ * _cairo_scaled_font_subsets_map_glyph(). A subset is determined by
+ * unique pairs of (font_id, subset_id) as returned by
+ * _cairo_scaled_font_subsets_map_glyph().
+ *
+ * For each subset, @font_subset_callback will be called and will be
+ * provided with both a #cairo_scaled_font_subset_t object containing
+ * all the glyphs in the subset as well as the value of @closure.
+ *
+ * The #cairo_scaled_font_subset_t object contains the scaled_font,
+ * the font_id, and the subset_id corresponding to all glyphs
+ * belonging to the subset. In addition, it contains an array providing
+ * a mapping between subset glyph indices and the original scaled font
+ * glyph indices.
+ *
+ * The index of the array corresponds to subset_glyph_index values
+ * returned by _cairo_scaled_font_subsets_map_glyph() while the
+ * values of the array correspond to the scaled_font_glyph_index
+ * values passed as input to the same function.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS if successful, or a non-zero
+ * value indicating an error. Possible errors include
+ * %CAIRO_STATUS_NO_MEMORY.
+ **/
+cairo_private cairo_status_t
+_cairo_scaled_font_subsets_foreach_user (cairo_scaled_font_subsets_t             *font_subsets,
+                                        cairo_scaled_font_subset_callback_func_t  font_subset_callback,
+                                        void                                     *closure);
+
+/**
+ * _cairo_scaled_font_subset_create_glyph_names:
+ * @font_subsets: a #cairo_scaled_font_subsets_t
+ *
+ * Create an array of strings containing the glyph name for each glyph
+ * in @font_subsets. The array as store in font_subsets->glyph_names.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS if successful,
+ * %CAIRO_INT_STATUS_UNSUPPORTED if the font backend does not support
+ * mapping the glyph indices to unicode characters. Possible errors
+ * include %CAIRO_STATUS_NO_MEMORY.
+ **/
+cairo_private cairo_int_status_t
+_cairo_scaled_font_subset_create_glyph_names (cairo_scaled_font_subset_t *subset);
+
+typedef struct _cairo_cff_subset {
+    char *family_name_utf8;
+    char *ps_name;
+    double *widths;
+    double x_min, y_min, x_max, y_max;
+    double ascent, descent;
+    char *data;
+    unsigned long data_length;
+} cairo_cff_subset_t;
+
+/**
+ * _cairo_cff_subset_init:
+ * @cff_subset: a #cairo_cff_subset_t to initialize
+ * @font_subset: the #cairo_scaled_font_subset_t to initialize from
+ *
+ * If possible (depending on the format of the underlying
+ * #cairo_scaled_font_t and the font backend in use) generate a
+ * cff file corresponding to @font_subset and initialize
+ * @cff_subset with information about the subset and the cff
+ * data.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS if successful,
+ * %CAIRO_INT_STATUS_UNSUPPORTED if the font can't be subset as a
+ * cff file, or an non-zero value indicating an error.  Possible
+ * errors include %CAIRO_STATUS_NO_MEMORY.
+ **/
+cairo_private cairo_status_t
+_cairo_cff_subset_init (cairo_cff_subset_t          *cff_subset,
+                        const char                  *name,
+                        cairo_scaled_font_subset_t  *font_subset);
+
+/**
+ * _cairo_cff_subset_fini:
+ * @cff_subset: a #cairo_cff_subset_t
+ *
+ * Free all resources associated with @cff_subset.  After this
+ * call, @cff_subset should not be used again without a
+ * subsequent call to _cairo_cff_subset_init() again first.
+ **/
+cairo_private void
+_cairo_cff_subset_fini (cairo_cff_subset_t *cff_subset);
+
+/**
+ * _cairo_cff_scaled_font_is_cid_cff:
+ * @scaled_font: a #cairo_scaled_font_t
+ *
+ * Return %TRUE if @scaled_font is a CID CFF font, otherwise return %FALSE.
+ **/
+cairo_private cairo_bool_t
+_cairo_cff_scaled_font_is_cid_cff (cairo_scaled_font_t *scaled_font);
+
+/**
+ * _cairo_cff_fallback_init:
+ * @cff_subset: a #cairo_cff_subset_t to initialize
+ * @font_subset: the #cairo_scaled_font_subset_t to initialize from
+ *
+ * If possible (depending on the format of the underlying
+ * #cairo_scaled_font_t and the font backend in use) generate a cff
+ * file corresponding to @font_subset and initialize @cff_subset
+ * with information about the subset and the cff data.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS if successful,
+ * %CAIRO_INT_STATUS_UNSUPPORTED if the font can't be subset as a
+ * cff file, or an non-zero value indicating an error.  Possible
+ * errors include %CAIRO_STATUS_NO_MEMORY.
+ **/
+cairo_private cairo_status_t
+_cairo_cff_fallback_init (cairo_cff_subset_t          *cff_subset,
+                          const char                  *name,
+                          cairo_scaled_font_subset_t  *font_subset);
+
+/**
+ * _cairo_cff_fallback_fini:
+ * @cff_subset: a #cairo_cff_subset_t
+ *
+ * Free all resources associated with @cff_subset.  After this
+ * call, @cff_subset should not be used again without a
+ * subsequent call to _cairo_cff_subset_init() again first.
+ **/
+cairo_private void
+_cairo_cff_fallback_fini (cairo_cff_subset_t *cff_subset);
+
+typedef struct _cairo_truetype_subset {
+    char *family_name_utf8;
+    char *ps_name;
+    double *widths;
+    double x_min, y_min, x_max, y_max;
+    double ascent, descent;
+    unsigned char *data;
+    unsigned long data_length;
+    unsigned long *string_offsets;
+    unsigned long num_string_offsets;
+} cairo_truetype_subset_t;
+
+/**
+ * _cairo_truetype_subset_init:
+ * @truetype_subset: a #cairo_truetype_subset_t to initialize
+ * @font_subset: the #cairo_scaled_font_subset_t to initialize from
+ *
+ * If possible (depending on the format of the underlying
+ * #cairo_scaled_font_t and the font backend in use) generate a
+ * truetype file corresponding to @font_subset and initialize
+ * @truetype_subset with information about the subset and the truetype
+ * data. The generated font will be suitable for embedding in
+ * PostScript.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS if successful,
+ * %CAIRO_INT_STATUS_UNSUPPORTED if the font can't be subset as a
+ * truetype file, or an non-zero value indicating an error.  Possible
+ * errors include %CAIRO_STATUS_NO_MEMORY.
+ **/
+cairo_private cairo_status_t
+_cairo_truetype_subset_init_ps (cairo_truetype_subset_t    *truetype_subset,
+                               cairo_scaled_font_subset_t *font_subset);
+
+/**
+ * _cairo_truetype_subset_init:
+ * @truetype_subset: a #cairo_truetype_subset_t to initialize
+ * @font_subset: the #cairo_scaled_font_subset_t to initialize from
+ *
+ * If possible (depending on the format of the underlying
+ * #cairo_scaled_font_t and the font backend in use) generate a
+ * truetype file corresponding to @font_subset and initialize
+ * @truetype_subset with information about the subset and the truetype
+ * data. The generated font will be suitable for embedding in
+ * PDF.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS if successful,
+ * %CAIRO_INT_STATUS_UNSUPPORTED if the font can't be subset as a
+ * truetype file, or an non-zero value indicating an error.  Possible
+ * errors include %CAIRO_STATUS_NO_MEMORY.
+ **/
+cairo_private cairo_status_t
+_cairo_truetype_subset_init_pdf (cairo_truetype_subset_t    *truetype_subset,
+                                cairo_scaled_font_subset_t *font_subset);
+
+/**
+ * _cairo_truetype_subset_fini:
+ * @truetype_subset: a #cairo_truetype_subset_t
+ *
+ * Free all resources associated with @truetype_subset.  After this
+ * call, @truetype_subset should not be used again without a
+ * subsequent call to _cairo_truetype_subset_init() again first.
+ **/
+cairo_private void
+_cairo_truetype_subset_fini (cairo_truetype_subset_t *truetype_subset);
+
+cairo_private const char *
+_cairo_ps_standard_encoding_to_glyphname (int glyph);
+
+cairo_private int
+_cairo_unicode_to_winansi (unsigned long unicode);
+
+cairo_private const char *
+_cairo_winansi_to_glyphname (int glyph);
+
+typedef struct _cairo_type1_subset {
+    char *base_font;
+    double *widths;
+    double x_min, y_min, x_max, y_max;
+    double ascent, descent;
+    char *data;
+    unsigned long header_length;
+    unsigned long data_length;
+    unsigned long trailer_length;
+} cairo_type1_subset_t;
+
+
+/**
+ * _cairo_type1_subset_init:
+ * @type1_subset: a #cairo_type1_subset_t to initialize
+ * @font_subset: the #cairo_scaled_font_subset_t to initialize from
+ * @hex_encode: if true the encrypted portion of the font is hex encoded
+ *
+ * If possible (depending on the format of the underlying
+ * #cairo_scaled_font_t and the font backend in use) generate a type1
+ * file corresponding to @font_subset and initialize @type1_subset
+ * with information about the subset and the type1 data.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS if successful,
+ * %CAIRO_INT_STATUS_UNSUPPORTED if the font can't be subset as a type1
+ * file, or an non-zero value indicating an error.  Possible errors
+ * include %CAIRO_STATUS_NO_MEMORY.
+ **/
+cairo_private cairo_status_t
+_cairo_type1_subset_init (cairo_type1_subset_t         *type_subset,
+                         const char                    *name,
+                         cairo_scaled_font_subset_t    *font_subset,
+                          cairo_bool_t                   hex_encode);
+
+/**
+ * _cairo_type1_subset_fini:
+ * @type1_subset: a #cairo_type1_subset_t
+ *
+ * Free all resources associated with @type1_subset.  After this call,
+ * @type1_subset should not be used again without a subsequent call to
+ * _cairo_truetype_type1_init() again first.
+ **/
+cairo_private void
+_cairo_type1_subset_fini (cairo_type1_subset_t *subset);
+
+/**
+ * _cairo_type1_scaled_font_is_type1:
+ * @scaled_font: a #cairo_scaled_font_t
+ *
+ * Return %TRUE if @scaled_font is a Type 1 font, otherwise return %FALSE.
+ **/
+cairo_private cairo_bool_t
+_cairo_type1_scaled_font_is_type1 (cairo_scaled_font_t *scaled_font);
+
+/**
+ * _cairo_type1_fallback_init_binary:
+ * @type1_subset: a #cairo_type1_subset_t to initialize
+ * @font_subset: the #cairo_scaled_font_subset_t to initialize from
+ *
+ * If possible (depending on the format of the underlying
+ * #cairo_scaled_font_t and the font backend in use) generate a type1
+ * file corresponding to @font_subset and initialize @type1_subset
+ * with information about the subset and the type1 data.  The encrypted
+ * part of the font is binary encoded.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS if successful,
+ * %CAIRO_INT_STATUS_UNSUPPORTED if the font can't be subset as a type1
+ * file, or an non-zero value indicating an error.  Possible errors
+ * include %CAIRO_STATUS_NO_MEMORY.
+ **/
+cairo_private cairo_status_t
+_cairo_type1_fallback_init_binary (cairo_type1_subset_t              *type_subset,
+                                   const char                *name,
+                                   cairo_scaled_font_subset_t *font_subset);
+
+/**
+ * _cairo_type1_fallback_init_hexencode:
+ * @type1_subset: a #cairo_type1_subset_t to initialize
+ * @font_subset: the #cairo_scaled_font_subset_t to initialize from
+ *
+ * If possible (depending on the format of the underlying
+ * #cairo_scaled_font_t and the font backend in use) generate a type1
+ * file corresponding to @font_subset and initialize @type1_subset
+ * with information about the subset and the type1 data. The encrypted
+ * part of the font is hex encoded.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS if successful,
+ * %CAIRO_INT_STATUS_UNSUPPORTED if the font can't be subset as a type1
+ * file, or an non-zero value indicating an error.  Possible errors
+ * include %CAIRO_STATUS_NO_MEMORY.
+ **/
+cairo_private cairo_status_t
+_cairo_type1_fallback_init_hex (cairo_type1_subset_t      *type_subset,
+                                const char                *name,
+                                cairo_scaled_font_subset_t *font_subset);
+
+/**
+ * _cairo_type1_fallback_fini:
+ * @type1_subset: a #cairo_type1_subset_t
+ *
+ * Free all resources associated with @type1_subset.  After this call,
+ * @type1_subset should not be used again without a subsequent call to
+ * _cairo_truetype_type1_init() again first.
+ **/
+cairo_private void
+_cairo_type1_fallback_fini (cairo_type1_subset_t *subset);
+
+typedef struct _cairo_type2_charstrings {
+    int *widths;
+    long x_min, y_min, x_max, y_max;
+    long ascent, descent;
+    cairo_array_t charstrings;
+} cairo_type2_charstrings_t;
+
+/**
+ * _cairo_type2_charstrings_init:
+ * @type2_subset: a #cairo_type2_subset_t to initialize
+ * @font_subset: the #cairo_scaled_font_subset_t to initialize from
+ *
+ * If possible (depending on the format of the underlying
+ * #cairo_scaled_font_t and the font backend in use) generate type2
+ * charstrings to @font_subset and initialize @type2_subset
+ * with information about the subset.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS if successful,
+ * %CAIRO_INT_STATUS_UNSUPPORTED if the font can't be subset as a type2
+ * charstrings, or an non-zero value indicating an error.  Possible errors
+ * include %CAIRO_STATUS_NO_MEMORY.
+ **/
+cairo_private cairo_status_t
+_cairo_type2_charstrings_init (cairo_type2_charstrings_t   *charstrings,
+                               cairo_scaled_font_subset_t  *font_subset);
+
+/**
+ * _cairo_type2_charstrings_fini:
+ * @subset: a #cairo_type2_charstrings_t
+ *
+ * Free all resources associated with @type2_charstring.  After this call,
+ * @type2_charstring should not be used again without a subsequent call to
+ * _cairo_type2_charstring_init() again first.
+ **/
+cairo_private void
+_cairo_type2_charstrings_fini (cairo_type2_charstrings_t *charstrings);
+
+/**
+ * _cairo_truetype_index_to_ucs4:
+ * @scaled_font: the #cairo_scaled_font_t
+ * @index: the glyph index
+ * @ucs4: return value for the unicode value of the glyph
+ *
+ * If possible (depending on the format of the underlying
+ * #cairo_scaled_font_t and the font backend in use) assign
+ * the unicode character of the glyph to @ucs4.
+ *
+ * If mapping glyph indices to unicode is supported but the unicode
+ * value of the specified glyph is not available, @ucs4 is set to -1.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS if successful,
+ * %CAIRO_INT_STATUS_UNSUPPORTED if mapping glyph indices to unicode
+ * is not supported.  Possible errors include %CAIRO_STATUS_NO_MEMORY.
+ **/
+cairo_private cairo_int_status_t
+_cairo_truetype_index_to_ucs4 (cairo_scaled_font_t *scaled_font,
+                               unsigned long        index,
+                               uint32_t            *ucs4);
+
+/**
+ * _cairo_truetype_read_font_name:
+ * @scaled_font: the #cairo_scaled_font_t
+ * @ps_name: returns the PostScript name of the font
+ *           or %NULL if the name could not be found.
+ * @font_name: returns the font name or %NULL if the name could not be found.
+ *
+ * If possible (depending on the format of the underlying
+ * #cairo_scaled_font_t and the font backend in use) read the
+ * PostScript and Font names from a TrueType/OpenType font.
+ *
+ * The font name is the full name of the font eg "DejaVu Sans Bold".
+ * The PostScript name is a shortened name with spaces removed
+ * suitable for use as the font name in a PS or PDF file eg
+ * "DejaVuSans-Bold".
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS if successful,
+ * %CAIRO_INT_STATUS_UNSUPPORTED if the font is not TrueType/OpenType
+ * or the name table is not present.  Possible errors include
+ * %CAIRO_STATUS_NO_MEMORY.
+ **/
+cairo_private cairo_int_status_t
+_cairo_truetype_read_font_name (cairo_scaled_font_t   *scaled_font,
+                               char                 **ps_name,
+                               char                 **font_name);
+
+/**
+ * _cairo_truetype_get_style:
+ * @scaled_font: the #cairo_scaled_font_t
+ * @weight: returns the font weight from the OS/2 table
+ * @bold: returns true if font is bold
+ * @italic: returns true if font is italic
+ *
+ * If the font is a truetype/opentype font with an OS/2 table, get the
+ * weight, bold, and italic data from the OS/2 table.  The weight
+ * values have the same meaning as the lfWeight field of the Windows
+ * LOGFONT structure.  Refer to the TrueType Specification for
+ * definition of the weight values.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS if successful,
+ * %CAIRO_INT_STATUS_UNSUPPORTED if the font is not TrueType/OpenType
+ * or the OS/2 table is not present.
+ **/
+cairo_private cairo_int_status_t
+_cairo_truetype_get_style (cairo_scaled_font_t          *scaled_font,
+                          int                           *weight,
+                          cairo_bool_t                  *bold,
+                          cairo_bool_t                  *italic);
+
+#endif /* CAIRO_HAS_FONT_SUBSET */
+
+#endif /* CAIRO_SCALED_FONT_SUBSETS_PRIVATE_H */
diff --git a/src/cairo-scaled-font-subsets.c b/src/cairo-scaled-font-subsets.c
new file mode 100644 (file)
index 0000000..355245a
--- /dev/null
@@ -0,0 +1,1264 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2003 University of Southern California
+ * Copyright © 2005 Red Hat, Inc
+ * Copyright © 2006 Keith Packard
+ * Copyright © 2006 Red Hat, Inc
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *     Carl D. Worth <cworth@cworth.org>
+ *     Kristian Høgsberg <krh@redhat.com>
+ *     Keith Packard <keithp@keithp.com>
+ *     Adrian Johnson <ajohnson@redneon.com>
+ */
+
+#define _BSD_SOURCE /* for snprintf(), strdup() */
+#include "cairoint.h"
+#include "cairo-error-private.h"
+
+#if CAIRO_HAS_FONT_SUBSET
+
+#include "cairo-scaled-font-subsets-private.h"
+#include "cairo-user-font-private.h"
+
+#define MAX_GLYPHS_PER_SIMPLE_FONT 256
+#define MAX_GLYPHS_PER_COMPOSITE_FONT 65536
+
+typedef enum {
+    CAIRO_SUBSETS_SCALED,
+    CAIRO_SUBSETS_SIMPLE,
+    CAIRO_SUBSETS_COMPOSITE
+} cairo_subsets_type_t;
+
+typedef enum {
+    CAIRO_SUBSETS_FOREACH_UNSCALED,
+    CAIRO_SUBSETS_FOREACH_SCALED,
+    CAIRO_SUBSETS_FOREACH_USER
+} cairo_subsets_foreach_type_t;
+
+typedef struct _cairo_sub_font {
+    cairo_hash_entry_t base;
+
+    cairo_bool_t is_scaled;
+    cairo_bool_t is_composite;
+    cairo_bool_t is_user;
+    cairo_bool_t use_latin_subset;
+    cairo_scaled_font_subsets_t *parent;
+    cairo_scaled_font_t *scaled_font;
+    unsigned int font_id;
+
+    int current_subset;
+    int num_glyphs_in_current_subset;
+    int num_glyphs_in_latin_subset;
+    int max_glyphs_per_subset;
+    char latin_char_map[256];
+
+    cairo_hash_table_t *sub_font_glyphs;
+    struct _cairo_sub_font *next;
+} cairo_sub_font_t;
+
+struct _cairo_scaled_font_subsets {
+    cairo_subsets_type_t type;
+    cairo_bool_t use_latin_subset;
+
+    int max_glyphs_per_unscaled_subset_used;
+    cairo_hash_table_t *unscaled_sub_fonts;
+    cairo_sub_font_t *unscaled_sub_fonts_list;
+    cairo_sub_font_t *unscaled_sub_fonts_list_end;
+
+    int max_glyphs_per_scaled_subset_used;
+    cairo_hash_table_t *scaled_sub_fonts;
+    cairo_sub_font_t *scaled_sub_fonts_list;
+    cairo_sub_font_t *scaled_sub_fonts_list_end;
+
+    int num_sub_fonts;
+};
+
+typedef struct _cairo_sub_font_glyph {
+    cairo_hash_entry_t base;
+
+    unsigned int subset_id;
+    unsigned int subset_glyph_index;
+    double       x_advance;
+    double       y_advance;
+
+    cairo_bool_t is_latin;
+    int                 latin_character;
+    cairo_bool_t is_mapped;
+    uint32_t     unicode;
+    char       *utf8;
+    int          utf8_len;
+} cairo_sub_font_glyph_t;
+
+typedef struct _cairo_sub_font_collection {
+    unsigned long *glyphs; /* scaled_font_glyph_index */
+    char       **utf8;
+    unsigned int glyphs_size;
+    int           *to_latin_char;
+    unsigned long *latin_to_subset_glyph_index;
+    unsigned int max_glyph;
+    unsigned int num_glyphs;
+
+    unsigned int subset_id;
+
+    cairo_status_t status;
+    cairo_scaled_font_subset_callback_func_t font_subset_callback;
+    void *font_subset_callback_closure;
+} cairo_sub_font_collection_t;
+
+typedef struct _cairo_string_entry {
+    cairo_hash_entry_t base;
+    char *string;
+} cairo_string_entry_t;
+
+static cairo_status_t
+_cairo_sub_font_map_glyph (cairo_sub_font_t    *sub_font,
+                          unsigned long         scaled_font_glyph_index,
+                          const char *          utf8,
+                          int                   utf8_len,
+                           cairo_scaled_font_subsets_glyph_t *subset_glyph);
+
+static void
+_cairo_sub_font_glyph_init_key (cairo_sub_font_glyph_t  *sub_font_glyph,
+                               unsigned long            scaled_font_glyph_index)
+{
+    sub_font_glyph->base.hash = scaled_font_glyph_index;
+}
+
+static cairo_sub_font_glyph_t *
+_cairo_sub_font_glyph_create (unsigned long    scaled_font_glyph_index,
+                             unsigned int      subset_id,
+                             unsigned int      subset_glyph_index,
+                              double            x_advance,
+                              double            y_advance,
+                             int               latin_character,
+                             uint32_t          unicode,
+                             char             *utf8,
+                             int               utf8_len)
+{
+    cairo_sub_font_glyph_t *sub_font_glyph;
+
+    sub_font_glyph = malloc (sizeof (cairo_sub_font_glyph_t));
+    if (unlikely (sub_font_glyph == NULL)) {
+       _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+       return NULL;
+    }
+
+    _cairo_sub_font_glyph_init_key (sub_font_glyph, scaled_font_glyph_index);
+    sub_font_glyph->subset_id = subset_id;
+    sub_font_glyph->subset_glyph_index = subset_glyph_index;
+    sub_font_glyph->x_advance = x_advance;
+    sub_font_glyph->y_advance = y_advance;
+    sub_font_glyph->is_latin = (latin_character >= 0);
+    sub_font_glyph->latin_character = latin_character;
+    sub_font_glyph->is_mapped = FALSE;
+    sub_font_glyph->unicode = unicode;
+    sub_font_glyph->utf8 = utf8;
+    sub_font_glyph->utf8_len = utf8_len;
+
+    return sub_font_glyph;
+}
+
+static void
+_cairo_sub_font_glyph_destroy (cairo_sub_font_glyph_t *sub_font_glyph)
+{
+    free (sub_font_glyph->utf8);
+
+    free (sub_font_glyph);
+}
+
+static void
+_cairo_sub_font_glyph_pluck (void *entry, void *closure)
+{
+    cairo_sub_font_glyph_t *sub_font_glyph = entry;
+    cairo_hash_table_t *sub_font_glyphs = closure;
+
+    _cairo_hash_table_remove (sub_font_glyphs, &sub_font_glyph->base);
+    _cairo_sub_font_glyph_destroy (sub_font_glyph);
+}
+
+static void
+_cairo_sub_font_glyph_collect (void *entry, void *closure)
+{
+    cairo_sub_font_glyph_t *sub_font_glyph = entry;
+    cairo_sub_font_collection_t *collection = closure;
+    unsigned long scaled_font_glyph_index;
+    unsigned int subset_glyph_index;
+
+    if (sub_font_glyph->subset_id != collection->subset_id)
+       return;
+
+    scaled_font_glyph_index = sub_font_glyph->base.hash;
+    subset_glyph_index = sub_font_glyph->subset_glyph_index;
+
+    /* Ensure we don't exceed the allocated bounds. */
+    assert (subset_glyph_index < collection->glyphs_size);
+
+    collection->glyphs[subset_glyph_index] = scaled_font_glyph_index;
+    collection->utf8[subset_glyph_index] = sub_font_glyph->utf8;
+    collection->to_latin_char[subset_glyph_index] = sub_font_glyph->latin_character;
+    if (sub_font_glyph->is_latin)
+       collection->latin_to_subset_glyph_index[sub_font_glyph->latin_character] = subset_glyph_index;
+
+    if (subset_glyph_index > collection->max_glyph)
+       collection->max_glyph = subset_glyph_index;
+
+    collection->num_glyphs++;
+}
+
+static cairo_bool_t
+_cairo_sub_fonts_equal (const void *key_a, const void *key_b)
+{
+    const cairo_sub_font_t *sub_font_a = key_a;
+    const cairo_sub_font_t *sub_font_b = key_b;
+    cairo_scaled_font_t *a = sub_font_a->scaled_font;
+    cairo_scaled_font_t *b = sub_font_b->scaled_font;
+
+    if (sub_font_a->is_scaled)
+        return a == b;
+    else
+       return a->font_face == b->font_face || a->original_font_face == b->original_font_face;
+}
+
+static void
+_cairo_sub_font_init_key (cairo_sub_font_t     *sub_font,
+                         cairo_scaled_font_t   *scaled_font)
+{
+    if (sub_font->is_scaled)
+    {
+        sub_font->base.hash = (unsigned long) scaled_font;
+        sub_font->scaled_font = scaled_font;
+    }
+    else
+    {
+        sub_font->base.hash = (unsigned long) scaled_font->font_face;
+        sub_font->scaled_font = scaled_font;
+    }
+}
+
+static cairo_status_t
+_cairo_sub_font_create (cairo_scaled_font_subsets_t    *parent,
+                       cairo_scaled_font_t             *scaled_font,
+                       unsigned int                     font_id,
+                       int                              max_glyphs_per_subset,
+                        cairo_bool_t                     is_scaled,
+                       cairo_bool_t                     is_composite,
+                       cairo_sub_font_t               **sub_font_out)
+{
+    cairo_sub_font_t *sub_font;
+    int i;
+
+    sub_font = malloc (sizeof (cairo_sub_font_t));
+    if (unlikely (sub_font == NULL))
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    sub_font->is_scaled = is_scaled;
+    sub_font->is_composite = is_composite;
+    sub_font->is_user = _cairo_font_face_is_user (scaled_font->font_face);
+    _cairo_sub_font_init_key (sub_font, scaled_font);
+
+    sub_font->parent = parent;
+    sub_font->scaled_font = scaled_font;
+    sub_font->font_id = font_id;
+
+    sub_font->use_latin_subset = parent->use_latin_subset;
+
+    /* latin subsets of Type 3 and CID CFF fonts are not supported */
+    if (sub_font->is_user || sub_font->is_scaled ||
+       _cairo_cff_scaled_font_is_cid_cff (scaled_font) )
+    {
+       sub_font->use_latin_subset = FALSE;
+    }
+
+    if (sub_font->use_latin_subset)
+       sub_font->current_subset = 1; /* reserve subset 0 for latin glyphs */
+    else
+       sub_font->current_subset = 0;
+
+    sub_font->num_glyphs_in_current_subset = 0;
+    sub_font->num_glyphs_in_latin_subset = 0;
+    sub_font->max_glyphs_per_subset = max_glyphs_per_subset;
+    for (i = 0; i < 256; i++)
+       sub_font->latin_char_map[i] = FALSE;
+
+    sub_font->sub_font_glyphs = _cairo_hash_table_create (NULL);
+    if (unlikely (sub_font->sub_font_glyphs == NULL)) {
+       free (sub_font);
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+    }
+    sub_font->next = NULL;
+    *sub_font_out = sub_font;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_sub_font_destroy (cairo_sub_font_t *sub_font)
+{
+    _cairo_hash_table_foreach (sub_font->sub_font_glyphs,
+                              _cairo_sub_font_glyph_pluck,
+                              sub_font->sub_font_glyphs);
+    _cairo_hash_table_destroy (sub_font->sub_font_glyphs);
+    cairo_scaled_font_destroy (sub_font->scaled_font);
+    free (sub_font);
+}
+
+static void
+_cairo_sub_font_pluck (void *entry, void *closure)
+{
+    cairo_sub_font_t *sub_font = entry;
+    cairo_hash_table_t *sub_fonts = closure;
+
+    _cairo_hash_table_remove (sub_fonts, &sub_font->base);
+    _cairo_sub_font_destroy (sub_font);
+}
+
+/* Characters 0x80 to 0x9f in the winansi encoding.
+ * All other characters in the range 0x00 to 0xff map 1:1 to unicode */
+static unsigned int _winansi_0x80_to_0x9f[] = {
+    0x20ac, 0x0000, 0x201a, 0x0192, 0x201e, 0x2026, 0x2020, 0x2021,
+    0x02c6, 0x2030, 0x0160, 0x2039, 0x0152, 0x0000, 0x017d, 0x0000,
+    0x0000, 0x2018, 0x2019, 0x201c, 0x201d, 0x2022, 0x2013, 0x2014,
+    0x02dc, 0x2122, 0x0161, 0x203a, 0x0153, 0x0000, 0x017e, 0x0178
+};
+
+int
+_cairo_unicode_to_winansi (unsigned long uni)
+{
+    int i;
+
+    /* exclude the extra "hyphen" at 0xad to avoid duplicate glyphnames */
+    if ((uni >= 0x20 && uni <= 0x7e) ||
+       (uni >= 0xa1 && uni <= 0xff && uni != 0xad) ||
+       uni == 0)
+        return uni;
+
+    for (i = 0; i < 32; i++)
+       if (_winansi_0x80_to_0x9f[i] == uni)
+           return i + 0x80;
+
+    return -1;
+}
+
+static cairo_status_t
+_cairo_sub_font_glyph_lookup_unicode (cairo_scaled_font_t    *scaled_font,
+                                     unsigned long           scaled_font_glyph_index,
+                                     uint32_t               *unicode_out,
+                                     char                  **utf8_out,
+                                     int                    *utf8_len_out)
+{
+    uint32_t unicode;
+    char buf[8];
+    int len;
+    cairo_status_t status;
+
+    /* Do a reverse lookup on the glyph index. unicode is -1 if the
+     * index could not be mapped to a unicode character. */
+    unicode = -1;
+    status = _cairo_truetype_index_to_ucs4 (scaled_font,
+                                           scaled_font_glyph_index,
+                                           &unicode);
+    if (_cairo_status_is_error (status))
+       return status;
+
+    if (unicode == (uint32_t)-1 && scaled_font->backend->index_to_ucs4) {
+       status = scaled_font->backend->index_to_ucs4 (scaled_font,
+                                                     scaled_font_glyph_index,
+                                                     &unicode);
+       if (unlikely (status))
+           return status;
+    }
+
+    *unicode_out = unicode;
+    *utf8_out = NULL;
+    *utf8_len_out = 0;
+    if (unicode != (uint32_t) -1) {
+       len = _cairo_ucs4_to_utf8 (unicode, buf);
+       if (len > 0) {
+           *utf8_out = malloc (len + 1);
+           if (unlikely (*utf8_out == NULL))
+               return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+           memcpy (*utf8_out, buf, len);
+           (*utf8_out)[len] = 0;
+           *utf8_len_out = len;
+       }
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_sub_font_glyph_map_to_unicode (cairo_sub_font_glyph_t *sub_font_glyph,
+                                     const char             *utf8,
+                                     int                     utf8_len,
+                                     cairo_bool_t           *is_mapped)
+{
+    *is_mapped = FALSE;
+
+    if (utf8_len < 0)
+       return CAIRO_STATUS_SUCCESS;
+
+    if (utf8 != NULL && utf8_len != 0 && utf8[utf8_len - 1] == '\0')
+       utf8_len--;
+
+    if (utf8 != NULL && utf8_len != 0) {
+       if (sub_font_glyph->utf8 != NULL) {
+           if (utf8_len == sub_font_glyph->utf8_len &&
+               memcmp (utf8, sub_font_glyph->utf8, utf8_len) == 0)
+           {
+               /* Requested utf8 mapping matches the existing mapping */
+               *is_mapped = TRUE;
+           }
+       } else {
+           /* No existing mapping. Use the requested mapping */
+           sub_font_glyph->utf8 = malloc (utf8_len + 1);
+           if (unlikely (sub_font_glyph->utf8 == NULL))
+               return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+           memcpy (sub_font_glyph->utf8, utf8, utf8_len);
+           sub_font_glyph->utf8[utf8_len] = 0;
+           sub_font_glyph->utf8_len = utf8_len;
+           *is_mapped = TRUE;
+       }
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_sub_font_lookup_glyph (cairo_sub_font_t                 *sub_font,
+                              unsigned long                     scaled_font_glyph_index,
+                             const char                        *utf8,
+                             int                                utf8_len,
+                              cairo_scaled_font_subsets_glyph_t *subset_glyph)
+{
+    cairo_sub_font_glyph_t key, *sub_font_glyph;
+    cairo_int_status_t status;
+
+    _cairo_sub_font_glyph_init_key (&key, scaled_font_glyph_index);
+    sub_font_glyph = _cairo_hash_table_lookup (sub_font->sub_font_glyphs,
+                                             &key.base);
+    if (sub_font_glyph != NULL) {
+        subset_glyph->font_id = sub_font->font_id;
+        subset_glyph->subset_id = sub_font_glyph->subset_id;
+       if (sub_font_glyph->is_latin)
+           subset_glyph->subset_glyph_index = sub_font_glyph->latin_character;
+       else
+           subset_glyph->subset_glyph_index = sub_font_glyph->subset_glyph_index;
+
+        subset_glyph->is_scaled = sub_font->is_scaled;
+        subset_glyph->is_composite = sub_font->is_composite;
+       subset_glyph->is_latin = sub_font_glyph->is_latin;
+        subset_glyph->x_advance = sub_font_glyph->x_advance;
+        subset_glyph->y_advance = sub_font_glyph->y_advance;
+       status = _cairo_sub_font_glyph_map_to_unicode (sub_font_glyph,
+                                                      utf8, utf8_len,
+                                                      &subset_glyph->utf8_is_mapped);
+       subset_glyph->unicode = sub_font_glyph->unicode;
+
+       return status;
+    }
+
+    return CAIRO_INT_STATUS_UNSUPPORTED;
+}
+
+static cairo_status_t
+_cairo_sub_font_add_glyph (cairo_sub_font_t       *sub_font,
+                          unsigned long            scaled_font_glyph_index,
+                          cairo_bool_t             is_latin,
+                          int                      latin_character,
+                          uint32_t                 unicode,
+                          char                    *utf8,
+                          int                      utf8_len,
+                          cairo_sub_font_glyph_t **sub_font_glyph_out)
+{
+    cairo_scaled_glyph_t *scaled_glyph;
+    cairo_sub_font_glyph_t *sub_font_glyph;
+    int *num_glyphs_in_subset_ptr;
+    double x_advance;
+    double y_advance;
+    cairo_int_status_t status;
+
+    _cairo_scaled_font_freeze_cache (sub_font->scaled_font);
+    status = _cairo_scaled_glyph_lookup (sub_font->scaled_font,
+                                        scaled_font_glyph_index,
+                                        CAIRO_SCALED_GLYPH_INFO_METRICS,
+                                        &scaled_glyph);
+    assert (status != CAIRO_INT_STATUS_UNSUPPORTED);
+    if (unlikely (status)) {
+       _cairo_scaled_font_thaw_cache (sub_font->scaled_font);
+       return status;
+    }
+
+    x_advance = scaled_glyph->metrics.x_advance;
+    y_advance = scaled_glyph->metrics.y_advance;
+    _cairo_scaled_font_thaw_cache (sub_font->scaled_font);
+
+    if (unlikely (status)) {
+       _cairo_sub_font_glyph_destroy (sub_font_glyph);
+       return status;
+    }
+
+    if (!is_latin && sub_font->num_glyphs_in_current_subset == sub_font->max_glyphs_per_subset)
+    {
+       sub_font->current_subset++;
+       sub_font->num_glyphs_in_current_subset = 0;
+    }
+
+    if (is_latin)
+       num_glyphs_in_subset_ptr = &sub_font->num_glyphs_in_latin_subset;
+    else
+       num_glyphs_in_subset_ptr = &sub_font->num_glyphs_in_current_subset;
+
+    /* Reserve first glyph in subset for the .notdef glyph except for
+     * Type 3 fonts */
+    if (*num_glyphs_in_subset_ptr == 0 &&
+       scaled_font_glyph_index != 0 &&
+       ! _cairo_font_face_is_user (sub_font->scaled_font->font_face))
+    {
+       status = _cairo_sub_font_add_glyph (sub_font,
+                                           0,
+                                           is_latin,
+                                           0,
+                                           0,
+                                           NULL,
+                                           -1,
+                                           &sub_font_glyph);
+       if (unlikely (status))
+           return status;
+    }
+
+    sub_font_glyph = _cairo_sub_font_glyph_create (scaled_font_glyph_index,
+                                                  is_latin ? 0 : sub_font->current_subset,
+                                                  *num_glyphs_in_subset_ptr,
+                                                  x_advance,
+                                                  y_advance,
+                                                  is_latin ? latin_character : -1,
+                                                  unicode,
+                                                  utf8,
+                                                  utf8_len);
+
+    if (unlikely (sub_font_glyph == NULL))
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    status = _cairo_hash_table_insert (sub_font->sub_font_glyphs, &sub_font_glyph->base);
+    if (unlikely (status)) {
+       _cairo_sub_font_glyph_destroy (sub_font_glyph);
+       return status;
+    }
+
+    (*num_glyphs_in_subset_ptr)++;
+    if (sub_font->is_scaled) {
+       if (*num_glyphs_in_subset_ptr > sub_font->parent->max_glyphs_per_scaled_subset_used)
+           sub_font->parent->max_glyphs_per_scaled_subset_used = *num_glyphs_in_subset_ptr;
+    } else {
+       if (*num_glyphs_in_subset_ptr > sub_font->parent->max_glyphs_per_unscaled_subset_used)
+           sub_font->parent->max_glyphs_per_unscaled_subset_used = *num_glyphs_in_subset_ptr;
+    }
+
+    *sub_font_glyph_out = sub_font_glyph;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_sub_font_map_glyph (cairo_sub_font_t    *sub_font,
+                          unsigned long         scaled_font_glyph_index,
+                          const char           *text_utf8,
+                          int                   text_utf8_len,
+                           cairo_scaled_font_subsets_glyph_t *subset_glyph)
+{
+    cairo_sub_font_glyph_t key, *sub_font_glyph;
+    cairo_status_t status;
+
+    _cairo_sub_font_glyph_init_key (&key, scaled_font_glyph_index);
+    sub_font_glyph = _cairo_hash_table_lookup (sub_font->sub_font_glyphs,
+                                              &key.base);
+    if (sub_font_glyph == NULL) {
+       uint32_t font_unicode;
+       char *font_utf8;
+       int font_utf8_len;
+       cairo_bool_t is_latin;
+       int latin_character;
+
+       status = _cairo_sub_font_glyph_lookup_unicode (sub_font->scaled_font,
+                                                          scaled_font_glyph_index,
+                                                          &font_unicode,
+                                                          &font_utf8,
+                                                          &font_utf8_len);
+       if (unlikely(status))
+           return status;
+
+       /* If the supplied utf8 is a valid single character, use it
+        * instead of the font lookup */
+       if (text_utf8 != NULL && text_utf8_len > 0) {
+           uint32_t  *ucs4;
+           int ucs4_len;
+
+           status = _cairo_utf8_to_ucs4 (text_utf8, text_utf8_len,
+                                         &ucs4, &ucs4_len);
+           if (status == CAIRO_STATUS_SUCCESS) {
+               if (ucs4_len == 1) {
+                   font_unicode = ucs4[0];
+                   free (font_utf8);
+                   font_utf8 = malloc (text_utf8_len + 1);
+                   if (font_utf8 == NULL) {
+                       free (ucs4);
+                       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+                   }
+                   memcpy (font_utf8, text_utf8, text_utf8_len);
+                   font_utf8[text_utf8_len] = 0;
+                   font_utf8_len = text_utf8_len;
+               }
+               free (ucs4);
+           }
+       }
+
+       /* If glyph is in the winansi encoding and font is not a user
+        * font, put glyph in the latin subset. If glyph is .notdef
+        * the latin subset is preferred but only if the latin subset
+        * already contains at least one glyph. We don't want to
+        * create a separate subset just for the .notdef glyph.
+        */
+       is_latin = FALSE;
+       latin_character = -1;
+       if (sub_font->use_latin_subset &&
+           (! _cairo_font_face_is_user (sub_font->scaled_font->font_face)))
+       {
+           latin_character = _cairo_unicode_to_winansi (font_unicode);
+           if (latin_character > 0 ||
+               (latin_character == 0 && sub_font->num_glyphs_in_latin_subset > 0))
+           {
+               if (!sub_font->latin_char_map[latin_character]) {
+                   sub_font->latin_char_map[latin_character] = TRUE;
+                   is_latin = TRUE;
+               }
+           }
+       }
+
+       status = _cairo_sub_font_add_glyph (sub_font,
+                                           scaled_font_glyph_index,
+                                           is_latin,
+                                           latin_character,
+                                           font_unicode,
+                                           font_utf8,
+                                           font_utf8_len,
+                                           &sub_font_glyph);
+       if (unlikely(status))
+           return status;
+    }
+
+    subset_glyph->font_id = sub_font->font_id;
+    subset_glyph->subset_id = sub_font_glyph->subset_id;
+    if (sub_font_glyph->is_latin)
+       subset_glyph->subset_glyph_index = sub_font_glyph->latin_character;
+    else
+       subset_glyph->subset_glyph_index = sub_font_glyph->subset_glyph_index;
+
+    subset_glyph->is_scaled = sub_font->is_scaled;
+    subset_glyph->is_composite = sub_font->is_composite;
+    subset_glyph->is_latin = sub_font_glyph->is_latin;
+    subset_glyph->x_advance = sub_font_glyph->x_advance;
+    subset_glyph->y_advance = sub_font_glyph->y_advance;
+    status = _cairo_sub_font_glyph_map_to_unicode (sub_font_glyph,
+                                                  text_utf8, text_utf8_len,
+                                                  &subset_glyph->utf8_is_mapped);
+    subset_glyph->unicode = sub_font_glyph->unicode;
+
+    return status;
+}
+
+static void
+_cairo_sub_font_collect (void *entry, void *closure)
+{
+    cairo_sub_font_t *sub_font = entry;
+    cairo_sub_font_collection_t *collection = closure;
+    cairo_scaled_font_subset_t subset;
+    int i;
+    unsigned int j;
+
+    if (collection->status)
+       return;
+
+    collection->status = sub_font->scaled_font->status;
+    if (collection->status)
+       return;
+
+    for (i = 0; i <= sub_font->current_subset; i++) {
+       collection->subset_id = i;
+       collection->num_glyphs = 0;
+       collection->max_glyph = 0;
+       memset (collection->latin_to_subset_glyph_index, 0, 256*sizeof(unsigned long));
+
+       _cairo_hash_table_foreach (sub_font->sub_font_glyphs,
+                                  _cairo_sub_font_glyph_collect, collection);
+       if (collection->status)
+           break;
+       if (collection->num_glyphs == 0)
+           continue;
+
+        /* Ensure the resulting array has no uninitialized holes */
+       assert (collection->num_glyphs == collection->max_glyph + 1);
+
+       subset.scaled_font = sub_font->scaled_font;
+       subset.is_composite = sub_font->is_composite;
+       subset.is_scaled = sub_font->is_scaled;
+       subset.font_id = sub_font->font_id;
+       subset.subset_id = i;
+       subset.glyphs = collection->glyphs;
+       subset.utf8 = collection->utf8;
+       subset.num_glyphs = collection->num_glyphs;
+        subset.glyph_names = NULL;
+
+       subset.is_latin = FALSE;
+       if (sub_font->use_latin_subset && i == 0) {
+           subset.is_latin = TRUE;
+           subset.to_latin_char = collection->to_latin_char;
+           subset.latin_to_subset_glyph_index = collection->latin_to_subset_glyph_index;
+       } else {
+           subset.to_latin_char = NULL;
+           subset.latin_to_subset_glyph_index = NULL;
+       }
+
+        collection->status = (collection->font_subset_callback) (&subset,
+                                           collection->font_subset_callback_closure);
+
+       if (subset.glyph_names != NULL) {
+            for (j = 0; j < collection->num_glyphs; j++)
+               free (subset.glyph_names[j]);
+           free (subset.glyph_names);
+       }
+
+       if (collection->status)
+           break;
+    }
+}
+
+static cairo_scaled_font_subsets_t *
+_cairo_scaled_font_subsets_create_internal (cairo_subsets_type_t type)
+{
+    cairo_scaled_font_subsets_t *subsets;
+
+    subsets = malloc (sizeof (cairo_scaled_font_subsets_t));
+    if (unlikely (subsets == NULL)) {
+       _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+       return NULL;
+    }
+
+    subsets->type = type;
+    subsets->use_latin_subset = FALSE;
+    subsets->max_glyphs_per_unscaled_subset_used = 0;
+    subsets->max_glyphs_per_scaled_subset_used = 0;
+    subsets->num_sub_fonts = 0;
+
+    subsets->unscaled_sub_fonts = _cairo_hash_table_create (_cairo_sub_fonts_equal);
+    if (! subsets->unscaled_sub_fonts) {
+       free (subsets);
+       return NULL;
+    }
+    subsets->unscaled_sub_fonts_list = NULL;
+    subsets->unscaled_sub_fonts_list_end = NULL;
+
+    subsets->scaled_sub_fonts = _cairo_hash_table_create (_cairo_sub_fonts_equal);
+    if (! subsets->scaled_sub_fonts) {
+       _cairo_hash_table_destroy (subsets->unscaled_sub_fonts);
+       free (subsets);
+       return NULL;
+    }
+    subsets->scaled_sub_fonts_list = NULL;
+    subsets->scaled_sub_fonts_list_end = NULL;
+
+    return subsets;
+}
+
+cairo_scaled_font_subsets_t *
+_cairo_scaled_font_subsets_create_scaled (void)
+{
+    return _cairo_scaled_font_subsets_create_internal (CAIRO_SUBSETS_SCALED);
+}
+
+cairo_scaled_font_subsets_t *
+_cairo_scaled_font_subsets_create_simple (void)
+{
+    return _cairo_scaled_font_subsets_create_internal (CAIRO_SUBSETS_SIMPLE);
+}
+
+cairo_scaled_font_subsets_t *
+_cairo_scaled_font_subsets_create_composite (void)
+{
+    return _cairo_scaled_font_subsets_create_internal (CAIRO_SUBSETS_COMPOSITE);
+}
+
+void
+_cairo_scaled_font_subsets_destroy (cairo_scaled_font_subsets_t *subsets)
+{
+    _cairo_hash_table_foreach (subsets->scaled_sub_fonts, _cairo_sub_font_pluck, subsets->scaled_sub_fonts);
+    _cairo_hash_table_destroy (subsets->scaled_sub_fonts);
+
+    _cairo_hash_table_foreach (subsets->unscaled_sub_fonts, _cairo_sub_font_pluck, subsets->unscaled_sub_fonts);
+    _cairo_hash_table_destroy (subsets->unscaled_sub_fonts);
+
+    free (subsets);
+}
+
+void
+_cairo_scaled_font_subsets_enable_latin_subset (cairo_scaled_font_subsets_t *font_subsets,
+                                               cairo_bool_t                 use_latin)
+{
+    font_subsets->use_latin_subset = use_latin;
+}
+
+cairo_status_t
+_cairo_scaled_font_subsets_map_glyph (cairo_scaled_font_subsets_t      *subsets,
+                                     cairo_scaled_font_t               *scaled_font,
+                                     unsigned long                      scaled_font_glyph_index,
+                                     const char *                       utf8,
+                                     int                                utf8_len,
+                                      cairo_scaled_font_subsets_glyph_t *subset_glyph)
+{
+    cairo_sub_font_t key, *sub_font;
+    cairo_scaled_glyph_t *scaled_glyph;
+    cairo_font_face_t *font_face;
+    cairo_matrix_t identity;
+    cairo_font_options_t font_options;
+    cairo_scaled_font_t        *unscaled_font;
+    cairo_int_status_t status;
+    int max_glyphs;
+    cairo_bool_t type1_font;
+
+    /* Lookup glyph in unscaled subsets */
+    if (subsets->type != CAIRO_SUBSETS_SCALED) {
+        key.is_scaled = FALSE;
+        _cairo_sub_font_init_key (&key, scaled_font);
+       sub_font = _cairo_hash_table_lookup (subsets->unscaled_sub_fonts,
+                                            &key.base);
+        if (sub_font != NULL) {
+            status = _cairo_sub_font_lookup_glyph (sub_font,
+                                                  scaled_font_glyph_index,
+                                                  utf8, utf8_len,
+                                                  subset_glyph);
+           if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+                return status;
+        }
+    }
+
+    /* Lookup glyph in scaled subsets */
+    key.is_scaled = TRUE;
+    _cairo_sub_font_init_key (&key, scaled_font);
+    sub_font = _cairo_hash_table_lookup (subsets->scaled_sub_fonts,
+                                        &key.base);
+    if (sub_font != NULL) {
+       status = _cairo_sub_font_lookup_glyph (sub_font,
+                                              scaled_font_glyph_index,
+                                              utf8, utf8_len,
+                                              subset_glyph);
+       if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+           return status;
+    }
+
+    /* Glyph not found. Determine whether the glyph is outline or
+     * bitmap and add to the appropriate subset.
+     *
+     * glyph_index 0 (the .notdef glyph) is a special case. Some fonts
+     * will return CAIRO_INT_STATUS_UNSUPPORTED when doing a
+     * _scaled_glyph_lookup(_GLYPH_INFO_PATH). Type1-fallback creates
+     * empty glyphs in this case so we can put the glyph in a unscaled
+     * subset. */
+    if (scaled_font_glyph_index == 0 ||
+       _cairo_font_face_is_user (scaled_font->font_face)) {
+       status = CAIRO_STATUS_SUCCESS;
+    } else {
+       _cairo_scaled_font_freeze_cache (scaled_font);
+       status = _cairo_scaled_glyph_lookup (scaled_font,
+                                            scaled_font_glyph_index,
+                                            CAIRO_SCALED_GLYPH_INFO_PATH,
+                                            &scaled_glyph);
+       _cairo_scaled_font_thaw_cache (scaled_font);
+    }
+    if (_cairo_int_status_is_error (status))
+        return status;
+
+    if (status == CAIRO_INT_STATUS_SUCCESS &&
+       subsets->type != CAIRO_SUBSETS_SCALED &&
+       ! _cairo_font_face_is_user (scaled_font->font_face))
+    {
+        /* Path available. Add to unscaled subset. */
+        key.is_scaled = FALSE;
+        _cairo_sub_font_init_key (&key, scaled_font);
+       sub_font = _cairo_hash_table_lookup (subsets->unscaled_sub_fonts,
+                                            &key.base);
+        if (sub_font == NULL) {
+            font_face = cairo_scaled_font_get_font_face (scaled_font);
+            cairo_matrix_init_identity (&identity);
+            _cairo_font_options_init_default (&font_options);
+            cairo_font_options_set_hint_style (&font_options, CAIRO_HINT_STYLE_NONE);
+            cairo_font_options_set_hint_metrics (&font_options, CAIRO_HINT_METRICS_OFF);
+            unscaled_font = cairo_scaled_font_create (font_face,
+                                                      &identity,
+                                                      &identity,
+                                                      &font_options);
+           if (unlikely (unscaled_font->status))
+               return unscaled_font->status;
+
+            subset_glyph->is_scaled = FALSE;
+            type1_font = _cairo_type1_scaled_font_is_type1 (unscaled_font);
+            if (subsets->type == CAIRO_SUBSETS_COMPOSITE && !type1_font) {
+                max_glyphs = MAX_GLYPHS_PER_COMPOSITE_FONT;
+                subset_glyph->is_composite = TRUE;
+            } else {
+                max_glyphs = MAX_GLYPHS_PER_SIMPLE_FONT;
+                subset_glyph->is_composite = FALSE;
+            }
+
+            status = _cairo_sub_font_create (subsets,
+                                            unscaled_font,
+                                            subsets->num_sub_fonts,
+                                            max_glyphs,
+                                            subset_glyph->is_scaled,
+                                            subset_glyph->is_composite,
+                                            &sub_font);
+
+            if (unlikely (status)) {
+               cairo_scaled_font_destroy (unscaled_font);
+                return status;
+           }
+
+            status = _cairo_hash_table_insert (subsets->unscaled_sub_fonts,
+                                               &sub_font->base);
+
+            if (unlikely (status)) {
+               _cairo_sub_font_destroy (sub_font);
+                return status;
+           }
+           if (!subsets->unscaled_sub_fonts_list)
+               subsets->unscaled_sub_fonts_list = sub_font;
+           else
+               subsets->unscaled_sub_fonts_list_end->next = sub_font;
+           subsets->unscaled_sub_fonts_list_end = sub_font;
+           subsets->num_sub_fonts++;
+        }
+    } else {
+        /* No path available. Add to scaled subset. */
+        key.is_scaled = TRUE;
+        _cairo_sub_font_init_key (&key, scaled_font);
+       sub_font = _cairo_hash_table_lookup (subsets->scaled_sub_fonts,
+                                            &key.base);
+        if (sub_font == NULL) {
+            subset_glyph->is_scaled = TRUE;
+            subset_glyph->is_composite = FALSE;
+            if (subsets->type == CAIRO_SUBSETS_SCALED)
+                max_glyphs = INT_MAX;
+            else
+                max_glyphs = MAX_GLYPHS_PER_SIMPLE_FONT;
+
+            status = _cairo_sub_font_create (subsets,
+                                            cairo_scaled_font_reference (scaled_font),
+                                            subsets->num_sub_fonts,
+                                            max_glyphs,
+                                            subset_glyph->is_scaled,
+                                            subset_glyph->is_composite,
+                                            &sub_font);
+            if (unlikely (status)) {
+               cairo_scaled_font_destroy (scaled_font);
+                return status;
+           }
+
+            status = _cairo_hash_table_insert (subsets->scaled_sub_fonts,
+                                               &sub_font->base);
+            if (unlikely (status)) {
+               _cairo_sub_font_destroy (sub_font);
+                return status;
+           }
+           if (!subsets->scaled_sub_fonts_list)
+               subsets->scaled_sub_fonts_list = sub_font;
+           else
+               subsets->scaled_sub_fonts_list_end->next = sub_font;
+           subsets->scaled_sub_fonts_list_end = sub_font;
+           subsets->num_sub_fonts++;
+        }
+    }
+
+    return _cairo_sub_font_map_glyph (sub_font,
+                                     scaled_font_glyph_index,
+                                     utf8, utf8_len,
+                                     subset_glyph);
+}
+
+static cairo_status_t
+_cairo_scaled_font_subsets_foreach_internal (cairo_scaled_font_subsets_t              *font_subsets,
+                                             cairo_scaled_font_subset_callback_func_t  font_subset_callback,
+                                             void                                    *closure,
+                                            cairo_subsets_foreach_type_t              type)
+{
+    cairo_sub_font_collection_t collection;
+    cairo_sub_font_t *sub_font;
+    cairo_bool_t is_scaled, is_user;
+
+    is_scaled = FALSE;
+    is_user = FALSE;
+
+    if (type == CAIRO_SUBSETS_FOREACH_USER)
+       is_user = TRUE;
+
+    if (type == CAIRO_SUBSETS_FOREACH_SCALED ||
+       type == CAIRO_SUBSETS_FOREACH_USER)
+    {
+       is_scaled = TRUE;
+    }
+
+    if (is_scaled)
+        collection.glyphs_size = font_subsets->max_glyphs_per_scaled_subset_used;
+    else
+        collection.glyphs_size = font_subsets->max_glyphs_per_unscaled_subset_used;
+
+    if (! collection.glyphs_size)
+       return CAIRO_STATUS_SUCCESS;
+
+    collection.glyphs = _cairo_malloc_ab (collection.glyphs_size, sizeof(unsigned long));
+    collection.utf8 = _cairo_malloc_ab (collection.glyphs_size, sizeof(char *));
+    collection.to_latin_char = _cairo_malloc_ab (collection.glyphs_size, sizeof(int));
+    collection.latin_to_subset_glyph_index = _cairo_malloc_ab (256, sizeof(unsigned long));
+    if (unlikely (collection.glyphs == NULL ||
+                 collection.utf8 == NULL ||
+                 collection.to_latin_char == NULL ||
+                 collection.latin_to_subset_glyph_index == NULL)) {
+       free (collection.glyphs);
+       free (collection.utf8);
+       free (collection.to_latin_char);
+       free (collection.latin_to_subset_glyph_index);
+
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+    }
+
+    collection.font_subset_callback = font_subset_callback;
+    collection.font_subset_callback_closure = closure;
+    collection.status = CAIRO_STATUS_SUCCESS;
+
+    if (is_scaled)
+       sub_font = font_subsets->scaled_sub_fonts_list;
+    else
+       sub_font = font_subsets->unscaled_sub_fonts_list;
+
+    while (sub_font) {
+       if (sub_font->is_user == is_user)
+           _cairo_sub_font_collect (sub_font, &collection);
+
+       sub_font = sub_font->next;
+    }
+    free (collection.utf8);
+    free (collection.glyphs);
+    free (collection.to_latin_char);
+    free (collection.latin_to_subset_glyph_index);
+
+    return collection.status;
+}
+
+cairo_status_t
+_cairo_scaled_font_subsets_foreach_scaled (cairo_scaled_font_subsets_t             *font_subsets,
+                                           cairo_scaled_font_subset_callback_func_t  font_subset_callback,
+                                           void                                            *closure)
+{
+    return _cairo_scaled_font_subsets_foreach_internal (font_subsets,
+                                                        font_subset_callback,
+                                                        closure,
+                                                       CAIRO_SUBSETS_FOREACH_SCALED);
+}
+
+cairo_status_t
+_cairo_scaled_font_subsets_foreach_unscaled (cairo_scaled_font_subsets_t           *font_subsets,
+                                           cairo_scaled_font_subset_callback_func_t  font_subset_callback,
+                                           void                                            *closure)
+{
+    return _cairo_scaled_font_subsets_foreach_internal (font_subsets,
+                                                        font_subset_callback,
+                                                        closure,
+                                                       CAIRO_SUBSETS_FOREACH_UNSCALED);
+}
+
+cairo_status_t
+_cairo_scaled_font_subsets_foreach_user (cairo_scaled_font_subsets_t             *font_subsets,
+                                        cairo_scaled_font_subset_callback_func_t  font_subset_callback,
+                                        void                                     *closure)
+{
+    return _cairo_scaled_font_subsets_foreach_internal (font_subsets,
+                                                        font_subset_callback,
+                                                        closure,
+                                                       CAIRO_SUBSETS_FOREACH_USER);
+}
+
+static cairo_bool_t
+_cairo_string_equal (const void *key_a, const void *key_b)
+{
+    const cairo_string_entry_t *a = key_a;
+    const cairo_string_entry_t *b = key_b;
+
+    if (strcmp (a->string, b->string) == 0)
+       return TRUE;
+    else
+       return FALSE;
+}
+
+static void
+_cairo_string_init_key (cairo_string_entry_t *key, char *s)
+{
+    unsigned long sum = 0;
+    unsigned int i;
+
+    for (i = 0; i < strlen(s); i++)
+        sum += s[i];
+    key->base.hash = sum;
+    key->string = s;
+}
+
+static cairo_status_t
+create_string_entry (char *s, cairo_string_entry_t **entry)
+{
+    *entry = malloc (sizeof (cairo_string_entry_t));
+    if (unlikely (*entry == NULL))
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    _cairo_string_init_key (*entry, s);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_pluck_entry (void *entry, void *closure)
+{
+    _cairo_hash_table_remove (closure, entry);
+    free (entry);
+}
+
+cairo_int_status_t
+_cairo_scaled_font_subset_create_glyph_names (cairo_scaled_font_subset_t *subset)
+{
+    unsigned int i;
+    cairo_hash_table_t *names;
+    cairo_string_entry_t key, *entry;
+    char buf[30];
+    char *utf8;
+    uint16_t *utf16;
+    int utf16_len;
+    cairo_status_t status = CAIRO_STATUS_SUCCESS;
+
+    names = _cairo_hash_table_create (_cairo_string_equal);
+    if (unlikely (names == NULL))
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    subset->glyph_names = calloc (subset->num_glyphs, sizeof (char *));
+    if (unlikely (subset->glyph_names == NULL)) {
+       status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+       goto CLEANUP_HASH;
+    }
+
+    i = 0;
+    if (! subset->is_scaled) {
+       subset->glyph_names[0] = strdup (".notdef");
+       if (unlikely (subset->glyph_names[0] == NULL)) {
+           status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+           goto CLEANUP_HASH;
+       }
+
+       status = create_string_entry (subset->glyph_names[0], &entry);
+       if (unlikely (status))
+           goto CLEANUP_HASH;
+
+       status = _cairo_hash_table_insert (names, &entry->base);
+       if (unlikely (status)) {
+           free (entry);
+           goto CLEANUP_HASH;
+       }
+       i++;
+    }
+
+    for (; i < subset->num_glyphs; i++) {
+       utf8 = subset->utf8[i];
+       utf16 = NULL;
+       utf16_len = 0;
+       if (utf8 && *utf8) {
+           status = _cairo_utf8_to_utf16 (utf8, -1, &utf16, &utf16_len);
+           if (unlikely (status))
+               goto CLEANUP_HASH;
+       }
+
+       if (utf16_len == 1) {
+           int ch = _cairo_unicode_to_winansi (utf16[0]);
+           if (ch > 0 && _cairo_winansi_to_glyphname (ch))
+               strncpy (buf, _cairo_winansi_to_glyphname (ch), sizeof (buf));
+           else
+               snprintf (buf, sizeof (buf), "uni%04X", (int) utf16[0]);
+
+           _cairo_string_init_key (&key, buf);
+           entry = _cairo_hash_table_lookup (names, &key.base);
+           if (entry != NULL)
+               snprintf (buf, sizeof (buf), "g%d", i);
+       } else {
+           snprintf (buf, sizeof (buf), "g%d", i);
+       }
+       free (utf16);
+
+       subset->glyph_names[i] = strdup (buf);
+       if (unlikely (subset->glyph_names[i] == NULL)) {
+           status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+           goto CLEANUP_HASH;
+       }
+
+       status = create_string_entry (subset->glyph_names[i], &entry);
+       if (unlikely (status))
+           goto CLEANUP_HASH;
+
+       status = _cairo_hash_table_insert (names, &entry->base);
+       if (unlikely (status)) {
+           free (entry);
+           goto CLEANUP_HASH;
+       }
+    }
+
+CLEANUP_HASH:
+    _cairo_hash_table_foreach (names, _pluck_entry, names);
+    _cairo_hash_table_destroy (names);
+
+    if (likely (status == CAIRO_STATUS_SUCCESS))
+       return CAIRO_STATUS_SUCCESS;
+
+    if (subset->glyph_names != NULL) {
+       for (i = 0; i < subset->num_glyphs; i++) {
+           free (subset->glyph_names[i]);
+       }
+
+       free (subset->glyph_names);
+       subset->glyph_names = NULL;
+    }
+
+    return status;
+}
+
+#endif /* CAIRO_HAS_FONT_SUBSET */
diff --git a/src/cairo-scaled-font.c b/src/cairo-scaled-font.c
new file mode 100644 (file)
index 0000000..be612a9
--- /dev/null
@@ -0,0 +1,3084 @@
+/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/*
+ * Copyright © 2005 Keith Packard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Keith Packard
+ *
+ * Contributor(s):
+ *      Keith Packard <keithp@keithp.com>
+ *     Carl D. Worth <cworth@cworth.org>
+ *      Graydon Hoare <graydon@redhat.com>
+ *      Owen Taylor <otaylor@redhat.com>
+ *      Behdad Esfahbod <behdad@behdad.org>
+ *      Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+#include "cairo-error-private.h"
+#include "cairo-image-surface-private.h"
+#include "cairo-pattern-private.h"
+#include "cairo-scaled-font-private.h"
+#include "cairo-surface-backend-private.h"
+
+#if _XOPEN_SOURCE >= 600 || defined (_ISOC99_SOURCE)
+#define ISFINITE(x) isfinite (x)
+#else
+#define ISFINITE(x) ((x) * (x) >= 0.) /* check for NaNs */
+#endif
+
+/**
+ * SECTION:cairo-scaled-font
+ * @Title: cairo_scaled_font_t
+ * @Short_Description: Font face at particular size and options
+ * @See_Also: #cairo_font_face_t, #cairo_matrix_t, #cairo_font_options_t
+ *
+ * #cairo_scaled_font_t represents a realization of a font face at a particular
+ * size and transformation and a certain set of font options.
+ */
+
+/* Global Glyph Cache
+ *
+ * We maintain a global pool of glyphs split between all active fonts. This
+ * allows a heavily used individual font to cache more glyphs than we could
+ * manage if we used per-font glyph caches, but at the same time maintains
+ * fairness across all fonts and provides a cap on the maximum number of
+ * global glyphs.
+ *
+ * The glyphs are allocated in pages, which are capped in the global pool.
+ * Using pages means we can reduce the frequency at which we have to probe the
+ * global pool and ameliorates the memory allocation pressure.
+ */
+
+/* XXX: This number is arbitrary---we've never done any measurement of this. */
+#define MAX_GLYPH_PAGES_CACHED 512
+static cairo_cache_t cairo_scaled_glyph_page_cache;
+
+#define CAIRO_SCALED_GLYPH_PAGE_SIZE 32
+struct _cairo_scaled_glyph_page {
+    cairo_cache_entry_t cache_entry;
+
+    cairo_list_t link;
+
+    unsigned int num_glyphs;
+    cairo_scaled_glyph_t glyphs[CAIRO_SCALED_GLYPH_PAGE_SIZE];
+};
+
+/*
+ *  Notes:
+ *
+ *  To store rasterizations of glyphs, we use an image surface and the
+ *  device offset to represent the glyph origin.
+ *
+ *  A device_transform converts from device space (a conceptual space) to
+ *  surface space.  For simple cases of translation only, it's called a
+ *  device_offset and is public API (cairo_surface_[gs]et_device_offset()).
+ *  A possibly better name for those functions could have been
+ *  cairo_surface_[gs]et_origin().  So, that's what they do: they set where
+ *  the device-space origin (0,0) is in the surface.  If the origin is inside
+ *  the surface, device_offset values are positive.  It may look like this:
+ *
+ *  Device space:
+ *        (-x,-y) <-- negative numbers
+ *           +----------------+
+ *           |      .         |
+ *           |      .         |
+ *           |......(0,0) <---|-- device-space origin
+ *           |                |
+ *           |                |
+ *           +----------------+
+ *                    (width-x,height-y)
+ *
+ *  Surface space:
+ *         (0,0) <-- surface-space origin
+ *           +---------------+
+ *           |      .        |
+ *           |      .        |
+ *           |......(x,y) <--|-- device_offset
+ *           |               |
+ *           |               |
+ *           +---------------+
+ *                     (width,height)
+ *
+ *  In other words: device_offset is the coordinates of the device-space
+ *  origin relative to the top-left of the surface.
+ *
+ *  We use device offsets in a couple of places:
+ *
+ *    - Public API: To let toolkits like Gtk+ give user a surface that
+ *      only represents part of the final destination (say, the expose
+ *      area), but has the same device space as the destination.  In these
+ *      cases device_offset is typically negative.  Example:
+ *
+ *           application window
+ *           +---------------+
+ *           |      .        |
+ *           | (x,y).        |
+ *           |......+---+    |
+ *           |      |   | <--|-- expose area
+ *           |      +---+    |
+ *           +---------------+
+ *
+ *      In this case, the user of cairo API can set the device_space on
+ *      the expose area to (-x,-y) to move the device space origin to that
+ *      of the application window, such that drawing in the expose area
+ *      surface and painting it in the application window has the same
+ *      effect as drawing in the application window directly.  Gtk+ has
+ *      been using this feature.
+ *
+ *    - Glyph surfaces: In most font rendering systems, glyph surfaces
+ *      have an origin at (0,0) and a bounding box that is typically
+ *      represented as (x_bearing,y_bearing,width,height).  Depending on
+ *      which way y progresses in the system, y_bearing may typically be
+ *      negative (for systems similar to cairo, with origin at top left),
+ *      or be positive (in systems like PDF with origin at bottom left).
+ *      No matter which is the case, it is important to note that
+ *      (x_bearing,y_bearing) is the coordinates of top-left of the glyph
+ *      relative to the glyph origin.  That is, for example:
+ *
+ *      Scaled-glyph space:
+ *
+ *        (x_bearing,y_bearing) <-- negative numbers
+ *           +----------------+
+ *           |      .         |
+ *           |      .         |
+ *           |......(0,0) <---|-- glyph origin
+ *           |                |
+ *           |                |
+ *           +----------------+
+ *                    (width+x_bearing,height+y_bearing)
+ *
+ *      Note the similarity of the origin to the device space.  That is
+ *      exactly how we use the device_offset to represent scaled glyphs:
+ *      to use the device-space origin as the glyph origin.
+ *
+ *  Now compare the scaled-glyph space to device-space and surface-space
+ *  and convince yourself that:
+ *
+ *     (x_bearing,y_bearing) = (-x,-y) = - device_offset
+ *
+ *  That's right.  If you are not convinced yet, contrast the definition
+ *  of the two:
+ *
+ *     "(x_bearing,y_bearing) is the coordinates of top-left of the
+ *      glyph relative to the glyph origin."
+ *
+ *     "In other words: device_offset is the coordinates of the
+ *      device-space origin relative to the top-left of the surface."
+ *
+ *  and note that glyph origin = device-space origin.
+ */
+
+static void
+_cairo_scaled_font_fini_internal (cairo_scaled_font_t *scaled_font);
+
+static void
+_cairo_scaled_glyph_fini (cairo_scaled_font_t *scaled_font,
+                         cairo_scaled_glyph_t *scaled_glyph)
+{
+    while (! cairo_list_is_empty (&scaled_glyph->dev_privates)) {
+       cairo_scaled_glyph_private_t *private =
+           cairo_list_first_entry (&scaled_glyph->dev_privates,
+                                   cairo_scaled_glyph_private_t,
+                                   link);
+       private->destroy (private, scaled_glyph, scaled_font);
+    }
+
+    if (scaled_glyph->surface != NULL)
+       cairo_surface_destroy (&scaled_glyph->surface->base);
+
+    if (scaled_glyph->path != NULL)
+       _cairo_path_fixed_destroy (scaled_glyph->path);
+
+    if (scaled_glyph->recording_surface != NULL) {
+       cairo_surface_finish (scaled_glyph->recording_surface);
+       cairo_surface_destroy (scaled_glyph->recording_surface);
+    }
+}
+
+#define ZOMBIE 0
+static const cairo_scaled_font_t _cairo_scaled_font_nil = {
+    { ZOMBIE },                        /* hash_entry */
+    CAIRO_STATUS_NO_MEMORY,    /* status */
+    CAIRO_REFERENCE_COUNT_INVALID,     /* ref_count */
+    { 0, 0, 0, NULL },         /* user_data */
+    NULL,                      /* original_font_face */
+    NULL,                      /* font_face */
+    { 1., 0., 0., 1., 0, 0},   /* font_matrix */
+    { 1., 0., 0., 1., 0, 0},   /* ctm */
+    { CAIRO_ANTIALIAS_DEFAULT, /* options */
+      CAIRO_SUBPIXEL_ORDER_DEFAULT,
+      CAIRO_HINT_STYLE_DEFAULT,
+      CAIRO_HINT_METRICS_DEFAULT} ,
+    FALSE,                     /* placeholder */
+    FALSE,                     /* holdover */
+    TRUE,                      /* finished */
+    { 1., 0., 0., 1., 0, 0},   /* scale */
+    { 1., 0., 0., 1., 0, 0},   /* scale_inverse */
+    1.,                                /* max_scale */
+    { 0., 0., 0., 0., 0. },    /* extents */
+    { 0., 0., 0., 0., 0. },    /* fs_extents */
+    CAIRO_MUTEX_NIL_INITIALIZER,/* mutex */
+    NULL,                      /* glyphs */
+    { NULL, NULL },            /* pages */
+    FALSE,                     /* cache_frozen */
+    FALSE,                     /* global_cache_frozen */
+    { NULL, NULL },            /* privates */
+    NULL                       /* backend */
+};
+
+/**
+ * _cairo_scaled_font_set_error:
+ * @scaled_font: a scaled_font
+ * @status: a status value indicating an error
+ *
+ * Atomically sets scaled_font->status to @status and calls _cairo_error;
+ * Does nothing if status is %CAIRO_STATUS_SUCCESS.
+ *
+ * All assignments of an error status to scaled_font->status should happen
+ * through _cairo_scaled_font_set_error(). Note that due to the nature of
+ * the atomic operation, it is not safe to call this function on the nil
+ * objects.
+ *
+ * The purpose of this function is to allow the user to set a
+ * breakpoint in _cairo_error() to generate a stack trace for when the
+ * user causes cairo to detect an error.
+ *
+ * Return value: the error status.
+ **/
+cairo_status_t
+_cairo_scaled_font_set_error (cairo_scaled_font_t *scaled_font,
+                             cairo_status_t status)
+{
+    if (status == CAIRO_STATUS_SUCCESS)
+       return status;
+
+    /* Don't overwrite an existing error. This preserves the first
+     * error, which is the most significant. */
+    _cairo_status_set_error (&scaled_font->status, status);
+
+    return _cairo_error (status);
+}
+
+/**
+ * cairo_scaled_font_get_type:
+ * @scaled_font: a #cairo_scaled_font_t
+ *
+ * This function returns the type of the backend used to create
+ * a scaled font. See #cairo_font_type_t for available types.
+ * However, this function never returns %CAIRO_FONT_TYPE_TOY.
+ *
+ * Return value: The type of @scaled_font.
+ *
+ * Since: 1.2
+ **/
+cairo_font_type_t
+cairo_scaled_font_get_type (cairo_scaled_font_t *scaled_font)
+{
+    if (CAIRO_REFERENCE_COUNT_IS_INVALID (&scaled_font->ref_count))
+       return CAIRO_FONT_TYPE_TOY;
+
+    return scaled_font->backend->type;
+}
+
+/**
+ * cairo_scaled_font_status:
+ * @scaled_font: a #cairo_scaled_font_t
+ *
+ * Checks whether an error has previously occurred for this
+ * scaled_font.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS or another error such as
+ *   %CAIRO_STATUS_NO_MEMORY.
+ **/
+cairo_status_t
+cairo_scaled_font_status (cairo_scaled_font_t *scaled_font)
+{
+    return scaled_font->status;
+}
+slim_hidden_def (cairo_scaled_font_status);
+
+/* Here we keep a unique mapping from
+ * font_face/matrix/ctm/font_options => #cairo_scaled_font_t.
+ *
+ * Here are the things that we want to map:
+ *
+ *  a) All otherwise referenced #cairo_scaled_font_t's
+ *  b) Some number of not otherwise referenced #cairo_scaled_font_t's
+ *
+ * The implementation uses a hash table which covers (a)
+ * completely. Then, for (b) we have an array of otherwise
+ * unreferenced fonts (holdovers) which are expired in
+ * least-recently-used order.
+ *
+ * The cairo_scaled_font_create() code gets to treat this like a regular
+ * hash table. All of the magic for the little holdover cache is in
+ * cairo_scaled_font_reference() and cairo_scaled_font_destroy().
+ */
+
+/* This defines the size of the holdover array ... that is, the number
+ * of scaled fonts we keep around even when not otherwise referenced
+ */
+#define CAIRO_SCALED_FONT_MAX_HOLDOVERS 256
+
+typedef struct _cairo_scaled_font_map {
+    cairo_scaled_font_t *mru_scaled_font;
+    cairo_hash_table_t *hash_table;
+    cairo_scaled_font_t *holdovers[CAIRO_SCALED_FONT_MAX_HOLDOVERS];
+    int num_holdovers;
+} cairo_scaled_font_map_t;
+
+static cairo_scaled_font_map_t *cairo_scaled_font_map;
+
+static int
+_cairo_scaled_font_keys_equal (const void *abstract_key_a, const void *abstract_key_b);
+
+static cairo_scaled_font_map_t *
+_cairo_scaled_font_map_lock (void)
+{
+    CAIRO_MUTEX_LOCK (_cairo_scaled_font_map_mutex);
+
+    if (cairo_scaled_font_map == NULL) {
+       cairo_scaled_font_map = malloc (sizeof (cairo_scaled_font_map_t));
+       if (unlikely (cairo_scaled_font_map == NULL))
+           goto CLEANUP_MUTEX_LOCK;
+
+       cairo_scaled_font_map->mru_scaled_font = NULL;
+       cairo_scaled_font_map->hash_table =
+           _cairo_hash_table_create (_cairo_scaled_font_keys_equal);
+
+       if (unlikely (cairo_scaled_font_map->hash_table == NULL))
+           goto CLEANUP_SCALED_FONT_MAP;
+
+       cairo_scaled_font_map->num_holdovers = 0;
+    }
+
+    return cairo_scaled_font_map;
+
+ CLEANUP_SCALED_FONT_MAP:
+    free (cairo_scaled_font_map);
+    cairo_scaled_font_map = NULL;
+ CLEANUP_MUTEX_LOCK:
+    CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_map_mutex);
+    _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+    return NULL;
+}
+
+static void
+_cairo_scaled_font_map_unlock (void)
+{
+   CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_map_mutex);
+}
+
+void
+_cairo_scaled_font_map_destroy (void)
+{
+    cairo_scaled_font_map_t *font_map;
+    cairo_scaled_font_t *scaled_font;
+
+    CAIRO_MUTEX_LOCK (_cairo_scaled_font_map_mutex);
+
+    font_map = cairo_scaled_font_map;
+    if (unlikely (font_map == NULL)) {
+        goto CLEANUP_MUTEX_LOCK;
+    }
+
+    scaled_font = font_map->mru_scaled_font;
+    if (scaled_font != NULL) {
+       CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_map_mutex);
+       cairo_scaled_font_destroy (scaled_font);
+       CAIRO_MUTEX_LOCK (_cairo_scaled_font_map_mutex);
+    }
+
+    /* remove scaled_fonts starting from the end so that font_map->holdovers
+     * is always in a consistent state when we release the mutex. */
+    while (font_map->num_holdovers) {
+       scaled_font = font_map->holdovers[font_map->num_holdovers-1];
+       assert (! CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&scaled_font->ref_count));
+       _cairo_hash_table_remove (font_map->hash_table,
+                                 &scaled_font->hash_entry);
+
+       font_map->num_holdovers--;
+
+       /* This releases the font_map lock to avoid the possibility of a
+        * recursive deadlock when the scaled font destroy closure gets
+        * called
+        */
+       _cairo_scaled_font_fini (scaled_font);
+
+       free (scaled_font);
+    }
+
+    _cairo_hash_table_destroy (font_map->hash_table);
+
+    free (cairo_scaled_font_map);
+    cairo_scaled_font_map = NULL;
+
+ CLEANUP_MUTEX_LOCK:
+    CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_map_mutex);
+}
+static void
+_cairo_scaled_glyph_page_destroy (void *closure)
+{
+    cairo_scaled_glyph_page_t *page = closure;
+    cairo_scaled_font_t *scaled_font;
+    unsigned int n;
+
+    assert (! cairo_list_is_empty (&page->link));
+
+    scaled_font = (cairo_scaled_font_t *) page->cache_entry.hash;
+    for (n = 0; n < page->num_glyphs; n++) {
+       _cairo_hash_table_remove (scaled_font->glyphs,
+                                 &page->glyphs[n].hash_entry);
+       _cairo_scaled_glyph_fini (scaled_font, &page->glyphs[n]);
+    }
+
+    cairo_list_del (&page->link);
+
+    free (page);
+}
+
+/* If a scaled font wants to unlock the font map while still being
+ * created (needed for user-fonts), we need to take extra care not
+ * ending up with multiple identical scaled fonts being created.
+ *
+ * What we do is, we create a fake identical scaled font, and mark
+ * it as placeholder, lock its mutex, and insert that in the fontmap
+ * hash table.  This makes other code trying to create an identical
+ * scaled font to just wait and retry.
+ *
+ * The reason we have to create a fake scaled font instead of just using
+ * scaled_font is for lifecycle management: we need to (or rather,
+ * other code needs to) reference the scaled_font in the hash table.
+ * We can't do that on the input scaled_font as it may be freed by
+ * font backend upon error.
+ */
+
+cairo_status_t
+_cairo_scaled_font_register_placeholder_and_unlock_font_map (cairo_scaled_font_t *scaled_font)
+{
+    cairo_status_t status;
+    cairo_scaled_font_t *placeholder_scaled_font;
+
+    assert (CAIRO_MUTEX_IS_LOCKED (_cairo_scaled_font_map_mutex));
+
+    status = scaled_font->status;
+    if (unlikely (status))
+       return status;
+
+    placeholder_scaled_font = malloc (sizeof (cairo_scaled_font_t));
+    if (unlikely (placeholder_scaled_font == NULL))
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    /* full initialization is wasteful, but who cares... */
+    status = _cairo_scaled_font_init (placeholder_scaled_font,
+                                     scaled_font->font_face,
+                                     &scaled_font->font_matrix,
+                                     &scaled_font->ctm,
+                                     &scaled_font->options,
+                                     NULL);
+    if (unlikely (status))
+       goto FREE_PLACEHOLDER;
+
+    placeholder_scaled_font->placeholder = TRUE;
+
+    status = _cairo_hash_table_insert (cairo_scaled_font_map->hash_table,
+                                      &placeholder_scaled_font->hash_entry);
+    if (unlikely (status))
+       goto FINI_PLACEHOLDER;
+
+    CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_map_mutex);
+    CAIRO_MUTEX_LOCK (placeholder_scaled_font->mutex);
+
+    return CAIRO_STATUS_SUCCESS;
+
+  FINI_PLACEHOLDER:
+    _cairo_scaled_font_fini_internal (placeholder_scaled_font);
+  FREE_PLACEHOLDER:
+    free (placeholder_scaled_font);
+
+    return _cairo_scaled_font_set_error (scaled_font, status);
+}
+
+void
+_cairo_scaled_font_unregister_placeholder_and_lock_font_map (cairo_scaled_font_t *scaled_font)
+{
+    cairo_scaled_font_t *placeholder_scaled_font;
+
+    CAIRO_MUTEX_LOCK (_cairo_scaled_font_map_mutex);
+
+    placeholder_scaled_font =
+       _cairo_hash_table_lookup (cairo_scaled_font_map->hash_table,
+                                 &scaled_font->hash_entry);
+    assert (placeholder_scaled_font != NULL);
+    assert (placeholder_scaled_font->placeholder);
+    assert (CAIRO_MUTEX_IS_LOCKED (placeholder_scaled_font->mutex));
+
+    _cairo_hash_table_remove (cairo_scaled_font_map->hash_table,
+                             &placeholder_scaled_font->hash_entry);
+
+    CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_map_mutex);
+
+    CAIRO_MUTEX_UNLOCK (placeholder_scaled_font->mutex);
+    cairo_scaled_font_destroy (placeholder_scaled_font);
+
+    CAIRO_MUTEX_LOCK (_cairo_scaled_font_map_mutex);
+}
+
+static void
+_cairo_scaled_font_placeholder_wait_for_creation_to_finish (cairo_scaled_font_t *placeholder_scaled_font)
+{
+    /* reference the place holder so it doesn't go away */
+    cairo_scaled_font_reference (placeholder_scaled_font);
+
+    /* now unlock the fontmap mutex so creation has a chance to finish */
+    CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_map_mutex);
+
+    /* wait on placeholder mutex until we are awaken */
+    CAIRO_MUTEX_LOCK (placeholder_scaled_font->mutex);
+
+    /* ok, creation done.  just clean up and back out */
+    CAIRO_MUTEX_UNLOCK (placeholder_scaled_font->mutex);
+    cairo_scaled_font_destroy (placeholder_scaled_font);
+
+    CAIRO_MUTEX_LOCK (_cairo_scaled_font_map_mutex);
+}
+
+/* Fowler / Noll / Vo (FNV) Hash (http://www.isthe.com/chongo/tech/comp/fnv/)
+ *
+ * Not necessarily better than a lot of other hashes, but should be OK, and
+ * well tested with binary data.
+ */
+
+#define FNV_32_PRIME ((uint32_t)0x01000193)
+#define FNV1_32_INIT ((uint32_t)0x811c9dc5)
+
+static uint32_t
+_hash_matrix_fnv (const cairo_matrix_t *matrix,
+                 uint32_t               hval)
+{
+    const uint8_t *buffer = (const uint8_t *) matrix;
+    int len = sizeof (cairo_matrix_t);
+    do {
+       hval *= FNV_32_PRIME;
+       hval ^= *buffer++;
+    } while (--len);
+
+    return hval;
+}
+
+static uint32_t
+_hash_mix_bits (uint32_t hash)
+{
+    hash += hash << 12;
+    hash ^= hash >> 7;
+    hash += hash << 3;
+    hash ^= hash >> 17;
+    hash += hash << 5;
+    return hash;
+}
+
+static void
+_cairo_scaled_font_init_key (cairo_scaled_font_t        *scaled_font,
+                            cairo_font_face_t          *font_face,
+                            const cairo_matrix_t       *font_matrix,
+                            const cairo_matrix_t       *ctm,
+                            const cairo_font_options_t *options)
+{
+    uint32_t hash = FNV1_32_INIT;
+
+    scaled_font->status = CAIRO_STATUS_SUCCESS;
+    scaled_font->placeholder = FALSE;
+    scaled_font->font_face = font_face;
+    scaled_font->original_font_face = font_face;
+    scaled_font->font_matrix = *font_matrix;
+    scaled_font->ctm = *ctm;
+    /* ignore translation values in the ctm */
+    scaled_font->ctm.x0 = 0.;
+    scaled_font->ctm.y0 = 0.;
+    _cairo_font_options_init_copy (&scaled_font->options, options);
+
+    /* We do a bytewise hash on the font matrices */
+    hash = _hash_matrix_fnv (&scaled_font->font_matrix, hash);
+    hash = _hash_matrix_fnv (&scaled_font->ctm, hash);
+    hash = _hash_mix_bits (hash);
+
+    hash ^= (unsigned long) scaled_font->font_face;
+    hash ^= cairo_font_options_hash (&scaled_font->options);
+
+    /* final mixing of bits */
+    hash = _hash_mix_bits (hash);
+
+    assert (hash != ZOMBIE);
+    scaled_font->hash_entry.hash = hash;
+}
+
+static cairo_bool_t
+_cairo_scaled_font_keys_equal (const void *abstract_key_a,
+                              const void *abstract_key_b)
+{
+    const cairo_scaled_font_t *key_a = abstract_key_a;
+    const cairo_scaled_font_t *key_b = abstract_key_b;
+
+    return key_a->original_font_face == key_b->original_font_face &&
+           memcmp ((unsigned char *)(&key_a->font_matrix.xx),
+                   (unsigned char *)(&key_b->font_matrix.xx),
+                   sizeof(cairo_matrix_t)) == 0 &&
+           memcmp ((unsigned char *)(&key_a->ctm.xx),
+                   (unsigned char *)(&key_b->ctm.xx),
+                   sizeof(cairo_matrix_t)) == 0 &&
+           cairo_font_options_equal (&key_a->options, &key_b->options);
+}
+
+static cairo_bool_t
+_cairo_scaled_font_matches (const cairo_scaled_font_t *scaled_font,
+                           const cairo_font_face_t *font_face,
+                           const cairo_matrix_t *font_matrix,
+                           const cairo_matrix_t *ctm,
+                           const cairo_font_options_t *options)
+{
+    return scaled_font->original_font_face == font_face &&
+           memcmp ((unsigned char *)(&scaled_font->font_matrix.xx),
+                   (unsigned char *)(&font_matrix->xx),
+                   sizeof(cairo_matrix_t)) == 0 &&
+           memcmp ((unsigned char *)(&scaled_font->ctm.xx),
+                   (unsigned char *)(&ctm->xx),
+                   sizeof(cairo_matrix_t)) == 0 &&
+           cairo_font_options_equal (&scaled_font->options, options);
+}
+
+/*
+ * Basic #cairo_scaled_font_t object management
+ */
+
+cairo_status_t
+_cairo_scaled_font_init (cairo_scaled_font_t               *scaled_font,
+                        cairo_font_face_t                 *font_face,
+                        const cairo_matrix_t              *font_matrix,
+                        const cairo_matrix_t              *ctm,
+                        const cairo_font_options_t        *options,
+                        const cairo_scaled_font_backend_t *backend)
+{
+    cairo_status_t status;
+
+    status = cairo_font_options_status ((cairo_font_options_t *) options);
+    if (unlikely (status))
+       return status;
+
+    _cairo_scaled_font_init_key (scaled_font, font_face,
+                                font_matrix, ctm, options);
+
+    cairo_matrix_multiply (&scaled_font->scale,
+                          &scaled_font->font_matrix,
+                          &scaled_font->ctm);
+
+    scaled_font->max_scale = MAX (fabs (scaled_font->scale.xx) + fabs (scaled_font->scale.xy),
+                                 fabs (scaled_font->scale.yx) + fabs (scaled_font->scale.yy));
+    scaled_font->scale_inverse = scaled_font->scale;
+    status = cairo_matrix_invert (&scaled_font->scale_inverse);
+    if (unlikely (status)) {
+       /* If the font scale matrix is rank 0, just using an all-zero inverse matrix
+        * makes everything work correctly.  This make font size 0 work without
+        * producing an error.
+        *
+        * FIXME:  If the scale is rank 1, we still go into error mode.  But then
+        * again, that's what we do everywhere in cairo.
+        *
+        * Also, the check for == 0. below may be too harsh...
+        */
+        if (_cairo_matrix_is_scale_0 (&scaled_font->scale)) {
+           cairo_matrix_init (&scaled_font->scale_inverse,
+                              0, 0, 0, 0,
+                              -scaled_font->scale.x0,
+                              -scaled_font->scale.y0);
+       } else
+           return status;
+    }
+
+    scaled_font->glyphs = _cairo_hash_table_create (NULL);
+    if (unlikely (scaled_font->glyphs == NULL))
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    cairo_list_init (&scaled_font->glyph_pages);
+    scaled_font->cache_frozen = FALSE;
+    scaled_font->global_cache_frozen = FALSE;
+
+    scaled_font->holdover = FALSE;
+    scaled_font->finished = FALSE;
+
+    CAIRO_REFERENCE_COUNT_INIT (&scaled_font->ref_count, 1);
+
+    _cairo_user_data_array_init (&scaled_font->user_data);
+
+    cairo_font_face_reference (font_face);
+    scaled_font->original_font_face = NULL;
+
+    CAIRO_MUTEX_INIT (scaled_font->mutex);
+
+    cairo_list_init (&scaled_font->dev_privates);
+
+    scaled_font->backend = backend;
+    cairo_list_init (&scaled_font->link);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+void
+_cairo_scaled_font_freeze_cache (cairo_scaled_font_t *scaled_font)
+{
+    /* ensure we do not modify an error object */
+    assert (scaled_font->status == CAIRO_STATUS_SUCCESS);
+
+    CAIRO_MUTEX_LOCK (scaled_font->mutex);
+    scaled_font->cache_frozen = TRUE;
+}
+
+void
+_cairo_scaled_font_thaw_cache (cairo_scaled_font_t *scaled_font)
+{
+    scaled_font->cache_frozen = FALSE;
+
+    if (scaled_font->global_cache_frozen) {
+       CAIRO_MUTEX_LOCK (_cairo_scaled_glyph_page_cache_mutex);
+       _cairo_cache_thaw (&cairo_scaled_glyph_page_cache);
+       CAIRO_MUTEX_UNLOCK (_cairo_scaled_glyph_page_cache_mutex);
+
+       scaled_font->global_cache_frozen = FALSE;
+    }
+
+    CAIRO_MUTEX_UNLOCK (scaled_font->mutex);
+}
+
+void
+_cairo_scaled_font_reset_cache (cairo_scaled_font_t *scaled_font)
+{
+    assert (! scaled_font->cache_frozen);
+
+    CAIRO_MUTEX_LOCK (_cairo_scaled_glyph_page_cache_mutex);
+    while (! cairo_list_is_empty (&scaled_font->glyph_pages)) {
+       _cairo_cache_remove (&cairo_scaled_glyph_page_cache,
+                             &cairo_list_first_entry (&scaled_font->glyph_pages,
+                                                      cairo_scaled_glyph_page_t,
+                                                      link)->cache_entry);
+    }
+    CAIRO_MUTEX_UNLOCK (_cairo_scaled_glyph_page_cache_mutex);
+}
+
+cairo_status_t
+_cairo_scaled_font_set_metrics (cairo_scaled_font_t        *scaled_font,
+                               cairo_font_extents_t        *fs_metrics)
+{
+    cairo_status_t status;
+    double  font_scale_x, font_scale_y;
+
+    scaled_font->fs_extents = *fs_metrics;
+
+    status = _cairo_matrix_compute_basis_scale_factors (&scaled_font->font_matrix,
+                                                 &font_scale_x, &font_scale_y,
+                                                 1);
+    if (unlikely (status))
+       return status;
+
+    /*
+     * The font responded in unscaled units, scale by the font
+     * matrix scale factors to get to user space
+     */
+
+    scaled_font->extents.ascent = fs_metrics->ascent * font_scale_y;
+    scaled_font->extents.descent = fs_metrics->descent * font_scale_y;
+    scaled_font->extents.height = fs_metrics->height * font_scale_y;
+    scaled_font->extents.max_x_advance = fs_metrics->max_x_advance * font_scale_x;
+    scaled_font->extents.max_y_advance = fs_metrics->max_y_advance * font_scale_y;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_scaled_font_fini_internal (cairo_scaled_font_t *scaled_font)
+{
+    scaled_font->finished = TRUE;
+
+    _cairo_scaled_font_reset_cache (scaled_font);
+    _cairo_hash_table_destroy (scaled_font->glyphs);
+
+    cairo_font_face_destroy (scaled_font->font_face);
+    cairo_font_face_destroy (scaled_font->original_font_face);
+
+    CAIRO_MUTEX_FINI (scaled_font->mutex);
+
+    while (! cairo_list_is_empty (&scaled_font->dev_privates)) {
+       cairo_scaled_font_private_t *private =
+           cairo_list_first_entry (&scaled_font->dev_privates,
+                                   cairo_scaled_font_private_t,
+                                   link);
+       private->destroy (private, scaled_font);
+    }
+
+    if (scaled_font->backend != NULL && scaled_font->backend->fini != NULL)
+       scaled_font->backend->fini (scaled_font);
+
+    _cairo_user_data_array_fini (&scaled_font->user_data);
+}
+
+void
+_cairo_scaled_font_fini (cairo_scaled_font_t *scaled_font)
+{
+    /* Release the lock to avoid the possibility of a recursive
+     * deadlock when the scaled font destroy closure gets called. */
+    CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_map_mutex);
+    _cairo_scaled_font_fini_internal (scaled_font);
+    CAIRO_MUTEX_LOCK (_cairo_scaled_font_map_mutex);
+}
+
+void
+_cairo_scaled_font_attach_private (cairo_scaled_font_t *scaled_font,
+                                  cairo_scaled_font_private_t *private,
+                                  const void *key,
+                                  void (*destroy) (cairo_scaled_font_private_t *,
+                                                   cairo_scaled_font_t *))
+{
+    private->key = key;
+    private->destroy = destroy;
+    cairo_list_add (&private->link, &scaled_font->dev_privates);
+}
+
+cairo_scaled_font_private_t *
+_cairo_scaled_font_find_private (cairo_scaled_font_t *scaled_font,
+                                const void *key)
+{
+    cairo_scaled_font_private_t *priv;
+
+    cairo_list_foreach_entry (priv, cairo_scaled_font_private_t,
+                             &scaled_font->dev_privates, link)
+    {
+       if (priv->key == key) {
+           if (priv->link.prev != &scaled_font->dev_privates)
+               cairo_list_move (&priv->link, &scaled_font->dev_privates);
+           return priv;
+       }
+    }
+
+    return NULL;
+}
+
+void
+_cairo_scaled_glyph_attach_private (cairo_scaled_glyph_t *scaled_glyph,
+                                  cairo_scaled_glyph_private_t *private,
+                                  const void *key,
+                                  void (*destroy) (cairo_scaled_glyph_private_t *,
+                                                   cairo_scaled_glyph_t *,
+                                                   cairo_scaled_font_t *))
+{
+    private->key = key;
+    private->destroy = destroy;
+    cairo_list_add (&private->link, &scaled_glyph->dev_privates);
+}
+
+cairo_scaled_glyph_private_t *
+_cairo_scaled_glyph_find_private (cairo_scaled_glyph_t *scaled_glyph,
+                                const void *key)
+{
+    cairo_scaled_glyph_private_t *priv;
+
+    cairo_list_foreach_entry (priv, cairo_scaled_glyph_private_t,
+                             &scaled_glyph->dev_privates, link)
+    {
+       if (priv->key == key) {
+           if (priv->link.prev != &scaled_glyph->dev_privates)
+               cairo_list_move (&priv->link, &scaled_glyph->dev_privates);
+           return priv;
+       }
+    }
+
+    return NULL;
+}
+
+/**
+ * cairo_scaled_font_create:
+ * @font_face: a #cairo_font_face_t
+ * @font_matrix: font space to user space transformation matrix for the
+ *       font. In the simplest case of a N point font, this matrix is
+ *       just a scale by N, but it can also be used to shear the font
+ *       or stretch it unequally along the two axes. See
+ *       cairo_set_font_matrix().
+ * @ctm: user to device transformation matrix with which the font will
+ *       be used.
+ * @options: options to use when getting metrics for the font and
+ *           rendering with it.
+ *
+ * Creates a #cairo_scaled_font_t object from a font face and matrices that
+ * describe the size of the font and the environment in which it will
+ * be used.
+ *
+ * Return value: a newly created #cairo_scaled_font_t. Destroy with
+ *  cairo_scaled_font_destroy()
+ **/
+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_status_t status;
+    cairo_scaled_font_map_t *font_map;
+    cairo_font_face_t *original_font_face = font_face;
+    cairo_scaled_font_t key, *old = NULL, *scaled_font = NULL, *dead = NULL;
+    double det;
+
+    status = font_face->status;
+    if (unlikely (status))
+       return _cairo_scaled_font_create_in_error (status);
+
+    det = _cairo_matrix_compute_determinant (font_matrix);
+    if (! ISFINITE (det))
+       return _cairo_scaled_font_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_MATRIX));
+
+    det = _cairo_matrix_compute_determinant (ctm);
+    if (! ISFINITE (det))
+       return _cairo_scaled_font_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_MATRIX));
+
+    status = cairo_font_options_status ((cairo_font_options_t *) options);
+    if (unlikely (status))
+       return _cairo_scaled_font_create_in_error (status);
+
+    /* Note that degenerate ctm or font_matrix *are* allowed.
+     * We want to support a font size of 0. */
+
+    font_map = _cairo_scaled_font_map_lock ();
+    if (unlikely (font_map == NULL))
+       return _cairo_scaled_font_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+    scaled_font = font_map->mru_scaled_font;
+    if (scaled_font != NULL &&
+       _cairo_scaled_font_matches (scaled_font,
+                                   font_face, font_matrix, ctm, options))
+    {
+       assert (scaled_font->hash_entry.hash != ZOMBIE);
+       assert (! scaled_font->placeholder);
+
+       if (likely (scaled_font->status == CAIRO_STATUS_SUCCESS)) {
+           /* We increment the reference count manually here, (rather
+            * than calling into cairo_scaled_font_reference), since we
+            * must modify the reference count while our lock is still
+            * held. */
+           _cairo_reference_count_inc (&scaled_font->ref_count);
+           _cairo_scaled_font_map_unlock ();
+           return scaled_font;
+       }
+
+       /* the font has been put into an error status - abandon the cache */
+       _cairo_hash_table_remove (font_map->hash_table,
+                                 &scaled_font->hash_entry);
+       scaled_font->hash_entry.hash = ZOMBIE;
+       dead = scaled_font;
+       font_map->mru_scaled_font = NULL;
+
+       if (font_face->backend->get_implementation != NULL) {
+           font_face = font_face->backend->get_implementation (font_face,
+                                                               font_matrix,
+                                                               ctm,
+                                                               options);
+           if (unlikely (font_face->status)) {
+               _cairo_scaled_font_map_unlock ();
+               cairo_scaled_font_destroy (scaled_font);
+               return _cairo_scaled_font_create_in_error (font_face->status);
+           }
+       }
+
+       _cairo_scaled_font_init_key (&key, original_font_face,
+                                    font_matrix, ctm, options);
+    }
+    else
+    {
+       if (font_face->backend->get_implementation != NULL) {
+           font_face = font_face->backend->get_implementation (font_face,
+                                                               font_matrix,
+                                                               ctm,
+                                                               options);
+           if (unlikely (font_face->status)) {
+               _cairo_scaled_font_map_unlock ();
+               return _cairo_scaled_font_create_in_error (font_face->status);
+           }
+       }
+
+       _cairo_scaled_font_init_key (&key, original_font_face,
+                                    font_matrix, ctm, options);
+
+       while ((scaled_font = _cairo_hash_table_lookup (font_map->hash_table,
+                                                       &key.hash_entry)))
+       {
+           if (! scaled_font->placeholder)
+               break;
+
+           /* If the scaled font is being created (happens for user-font),
+            * just wait until it's done, then retry */
+           _cairo_scaled_font_placeholder_wait_for_creation_to_finish (scaled_font);
+       }
+
+       /* Return existing scaled_font if it exists in the hash table. */
+       if (scaled_font != NULL) {
+           /* If the original reference count is 0, then this font must have
+            * been found in font_map->holdovers, (which means this caching is
+            * actually working). So now we remove it from the holdovers
+            * array, unless we caught the font in the middle of destruction.
+            */
+           if (! CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&scaled_font->ref_count)) {
+               if (scaled_font->holdover) {
+                   int i;
+
+                   for (i = 0; i < font_map->num_holdovers; i++) {
+                       if (font_map->holdovers[i] == scaled_font) {
+                           font_map->num_holdovers--;
+                           memmove (&font_map->holdovers[i],
+                                    &font_map->holdovers[i+1],
+                                    (font_map->num_holdovers - i) * sizeof (cairo_scaled_font_t*));
+                           break;
+                       }
+                   }
+
+                   scaled_font->holdover = FALSE;
+               }
+
+               /* reset any error status */
+               scaled_font->status = CAIRO_STATUS_SUCCESS;
+           }
+
+           if (likely (scaled_font->status == CAIRO_STATUS_SUCCESS)) {
+               /* We increment the reference count manually here, (rather
+                * than calling into cairo_scaled_font_reference), since we
+                * must modify the reference count while our lock is still
+                * held. */
+
+               old = font_map->mru_scaled_font;
+               font_map->mru_scaled_font = scaled_font;
+               /* increment reference count for the mru cache */
+               _cairo_reference_count_inc (&scaled_font->ref_count);
+               /* and increment for the returned reference */
+               _cairo_reference_count_inc (&scaled_font->ref_count);
+               _cairo_scaled_font_map_unlock ();
+
+               cairo_scaled_font_destroy (old);
+               if (font_face != original_font_face)
+                   cairo_font_face_destroy (font_face);
+
+               return scaled_font;
+           }
+
+           /* the font has been put into an error status - abandon the cache */
+           _cairo_hash_table_remove (font_map->hash_table,
+                                     &scaled_font->hash_entry);
+           scaled_font->hash_entry.hash = ZOMBIE;
+       }
+    }
+
+    /* Otherwise create it and insert it into the hash table. */
+    status = font_face->backend->scaled_font_create (font_face, font_matrix,
+                                                    ctm, options, &scaled_font);
+    /* Did we leave the backend in an error state? */
+    if (unlikely (status)) {
+       _cairo_scaled_font_map_unlock ();
+       if (font_face != original_font_face)
+           cairo_font_face_destroy (font_face);
+
+       if (dead != NULL)
+           cairo_scaled_font_destroy (dead);
+
+       status = _cairo_font_face_set_error (font_face, status);
+       return _cairo_scaled_font_create_in_error (status);
+    }
+    /* Or did we encounter an error whilst constructing the scaled font? */
+    if (unlikely (scaled_font->status)) {
+       _cairo_scaled_font_map_unlock ();
+       if (font_face != original_font_face)
+           cairo_font_face_destroy (font_face);
+
+       if (dead != NULL)
+           cairo_scaled_font_destroy (dead);
+
+       return scaled_font;
+    }
+
+    /* Our caching above is defeated if the backend switches fonts on us -
+     * e.g. old incarnations of toy-font-face and lazily resolved
+     * ft-font-faces
+     */
+    assert (scaled_font->font_face == font_face);
+
+    scaled_font->original_font_face =
+       cairo_font_face_reference (original_font_face);
+
+    status = _cairo_hash_table_insert (font_map->hash_table,
+                                      &scaled_font->hash_entry);
+    if (likely (status == CAIRO_STATUS_SUCCESS)) {
+       old = font_map->mru_scaled_font;
+       font_map->mru_scaled_font = scaled_font;
+       _cairo_reference_count_inc (&scaled_font->ref_count);
+    }
+
+    _cairo_scaled_font_map_unlock ();
+
+    cairo_scaled_font_destroy (old);
+    if (font_face != original_font_face)
+       cairo_font_face_destroy (font_face);
+
+    if (dead != NULL)
+       cairo_scaled_font_destroy (dead);
+
+    if (unlikely (status)) {
+       /* We can't call _cairo_scaled_font_destroy here since it expects
+        * that the font has already been successfully inserted into the
+        * hash table. */
+       _cairo_scaled_font_fini_internal (scaled_font);
+       free (scaled_font);
+       return _cairo_scaled_font_create_in_error (status);
+    }
+
+    return scaled_font;
+}
+slim_hidden_def (cairo_scaled_font_create);
+
+static cairo_scaled_font_t *_cairo_scaled_font_nil_objects[CAIRO_STATUS_LAST_STATUS + 1];
+
+/* XXX This should disappear in favour of a common pool of error objects. */
+cairo_scaled_font_t *
+_cairo_scaled_font_create_in_error (cairo_status_t status)
+{
+    cairo_scaled_font_t *scaled_font;
+
+    assert (status != CAIRO_STATUS_SUCCESS);
+
+    if (status == CAIRO_STATUS_NO_MEMORY)
+       return (cairo_scaled_font_t *) &_cairo_scaled_font_nil;
+
+    CAIRO_MUTEX_LOCK (_cairo_scaled_font_error_mutex);
+    scaled_font = _cairo_scaled_font_nil_objects[status];
+    if (unlikely (scaled_font == NULL)) {
+       scaled_font = malloc (sizeof (cairo_scaled_font_t));
+       if (unlikely (scaled_font == NULL)) {
+           CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_error_mutex);
+           _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+           return (cairo_scaled_font_t *) &_cairo_scaled_font_nil;
+       }
+
+       *scaled_font = _cairo_scaled_font_nil;
+       scaled_font->status = status;
+       _cairo_scaled_font_nil_objects[status] = scaled_font;
+    }
+    CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_error_mutex);
+
+    return scaled_font;
+}
+
+void
+_cairo_scaled_font_reset_static_data (void)
+{
+    int status;
+
+    CAIRO_MUTEX_LOCK (_cairo_scaled_font_error_mutex);
+    for (status = CAIRO_STATUS_SUCCESS;
+        status <= CAIRO_STATUS_LAST_STATUS;
+        status++)
+    {
+       free (_cairo_scaled_font_nil_objects[status]);
+       _cairo_scaled_font_nil_objects[status] = NULL;
+    }
+    CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_error_mutex);
+
+    CAIRO_MUTEX_LOCK (_cairo_scaled_glyph_page_cache_mutex);
+    if (cairo_scaled_glyph_page_cache.hash_table != NULL) {
+       _cairo_cache_fini (&cairo_scaled_glyph_page_cache);
+       cairo_scaled_glyph_page_cache.hash_table = NULL;
+    }
+    CAIRO_MUTEX_UNLOCK (_cairo_scaled_glyph_page_cache_mutex);
+}
+
+/**
+ * cairo_scaled_font_reference:
+ * @scaled_font: a #cairo_scaled_font_t, (may be %NULL in which case
+ * this function does nothing)
+ *
+ * Increases the reference count on @scaled_font by one. This prevents
+ * @scaled_font from being destroyed until a matching call to
+ * cairo_scaled_font_destroy() is made.
+ *
+ * The number of references to a #cairo_scaled_font_t can be get using
+ * cairo_scaled_font_get_reference_count().
+ *
+ * Returns: the referenced #cairo_scaled_font_t
+ **/
+cairo_scaled_font_t *
+cairo_scaled_font_reference (cairo_scaled_font_t *scaled_font)
+{
+    if (scaled_font == NULL ||
+           CAIRO_REFERENCE_COUNT_IS_INVALID (&scaled_font->ref_count))
+       return scaled_font;
+
+    assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&scaled_font->ref_count));
+
+    _cairo_reference_count_inc (&scaled_font->ref_count);
+
+    return scaled_font;
+}
+slim_hidden_def (cairo_scaled_font_reference);
+
+/**
+ * cairo_scaled_font_destroy:
+ * @scaled_font: a #cairo_scaled_font_t
+ *
+ * Decreases the reference count on @font by one. If the result
+ * is zero, then @font and all associated resources are freed.
+ * See cairo_scaled_font_reference().
+ **/
+void
+cairo_scaled_font_destroy (cairo_scaled_font_t *scaled_font)
+{
+    cairo_scaled_font_t *lru = NULL;
+    cairo_scaled_font_map_t *font_map;
+
+    assert (CAIRO_MUTEX_IS_UNLOCKED (_cairo_scaled_font_map_mutex));
+
+    if (scaled_font == NULL ||
+           CAIRO_REFERENCE_COUNT_IS_INVALID (&scaled_font->ref_count))
+       return;
+
+    assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&scaled_font->ref_count));
+
+    if (! _cairo_reference_count_dec_and_test (&scaled_font->ref_count))
+       return;
+
+    font_map = _cairo_scaled_font_map_lock ();
+    assert (font_map != NULL);
+
+    /* Another thread may have resurrected the font whilst we waited */
+    if (! CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&scaled_font->ref_count)) {
+       if (! scaled_font->placeholder &&
+           scaled_font->hash_entry.hash != ZOMBIE)
+       {
+           /* Another thread may have already inserted us into the holdovers */
+           if (scaled_font->holdover)
+               goto unlock;
+
+           /* Rather than immediately destroying this object, we put it into
+            * the font_map->holdovers array in case it will get used again
+            * soon (and is why we must hold the lock over the atomic op on
+            * the reference count). To make room for it, we do actually
+            * destroy the least-recently-used holdover.
+            */
+
+           if (font_map->num_holdovers == CAIRO_SCALED_FONT_MAX_HOLDOVERS) {
+               lru = font_map->holdovers[0];
+               assert (! CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&lru->ref_count));
+
+               _cairo_hash_table_remove (font_map->hash_table,
+                                         &lru->hash_entry);
+
+               font_map->num_holdovers--;
+               memmove (&font_map->holdovers[0],
+                        &font_map->holdovers[1],
+                        font_map->num_holdovers * sizeof (cairo_scaled_font_t*));
+           }
+
+           font_map->holdovers[font_map->num_holdovers++] = scaled_font;
+           scaled_font->holdover = TRUE;
+       } else
+           lru = scaled_font;
+    }
+
+  unlock:
+    _cairo_scaled_font_map_unlock ();
+
+    /* If we pulled an item from the holdovers array, (while the font
+     * map lock was held, of course), then there is no way that anyone
+     * else could have acquired a reference to it. So we can now
+     * safely call fini on it without any lock held. This is desirable
+     * as we never want to call into any backend function with a lock
+     * held. */
+    if (lru != NULL) {
+       _cairo_scaled_font_fini_internal (lru);
+       free (lru);
+    }
+}
+slim_hidden_def (cairo_scaled_font_destroy);
+
+/**
+ * cairo_scaled_font_get_reference_count:
+ * @scaled_font: a #cairo_scaled_font_t
+ *
+ * Returns the current reference count of @scaled_font.
+ *
+ * Return value: the current reference count of @scaled_font.  If the
+ * object is a nil object, 0 will be returned.
+ *
+ * Since: 1.4
+ **/
+unsigned int
+cairo_scaled_font_get_reference_count (cairo_scaled_font_t *scaled_font)
+{
+    if (scaled_font == NULL ||
+           CAIRO_REFERENCE_COUNT_IS_INVALID (&scaled_font->ref_count))
+       return 0;
+
+    return CAIRO_REFERENCE_COUNT_GET_VALUE (&scaled_font->ref_count);
+}
+
+/**
+ * cairo_scaled_font_get_user_data:
+ * @scaled_font: a #cairo_scaled_font_t
+ * @key: the address of the #cairo_user_data_key_t the user data was
+ * attached to
+ *
+ * Return user data previously attached to @scaled_font using the
+ * specified key.  If no user data has been attached with the given
+ * key this function returns %NULL.
+ *
+ * Return value: the user data previously attached or %NULL.
+ *
+ * Since: 1.4
+ **/
+void *
+cairo_scaled_font_get_user_data (cairo_scaled_font_t        *scaled_font,
+                                const cairo_user_data_key_t *key)
+{
+    return _cairo_user_data_array_get_data (&scaled_font->user_data,
+                                           key);
+}
+slim_hidden_def (cairo_scaled_font_get_user_data);
+
+/**
+ * cairo_scaled_font_set_user_data:
+ * @scaled_font: a #cairo_scaled_font_t
+ * @key: the address of a #cairo_user_data_key_t to attach the user data to
+ * @user_data: the user data to attach to the #cairo_scaled_font_t
+ * @destroy: a #cairo_destroy_func_t which will be called when the
+ * #cairo_t is destroyed or when new user data is attached using the
+ * same key.
+ *
+ * Attach user data to @scaled_font.  To remove user data from a surface,
+ * call this function with the key that was used to set it and %NULL
+ * for @data.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY if a
+ * slot could not be allocated for the user data.
+ *
+ * Since: 1.4
+ **/
+cairo_status_t
+cairo_scaled_font_set_user_data (cairo_scaled_font_t        *scaled_font,
+                                const cairo_user_data_key_t *key,
+                                void                        *user_data,
+                                cairo_destroy_func_t         destroy)
+{
+    if (CAIRO_REFERENCE_COUNT_IS_INVALID (&scaled_font->ref_count))
+       return scaled_font->status;
+
+    return _cairo_user_data_array_set_data (&scaled_font->user_data,
+                                           key, user_data, destroy);
+}
+slim_hidden_def (cairo_scaled_font_set_user_data);
+
+/* Public font API follows. */
+
+/**
+ * cairo_scaled_font_extents:
+ * @scaled_font: a #cairo_scaled_font_t
+ * @extents: a #cairo_font_extents_t which to store the retrieved extents.
+ *
+ * Gets the metrics for a #cairo_scaled_font_t.
+ **/
+void
+cairo_scaled_font_extents (cairo_scaled_font_t  *scaled_font,
+                          cairo_font_extents_t *extents)
+{
+    if (scaled_font->status) {
+       extents->ascent  = 0.0;
+       extents->descent = 0.0;
+       extents->height  = 0.0;
+       extents->max_x_advance = 0.0;
+       extents->max_y_advance = 0.0;
+       return;
+    }
+
+    *extents = scaled_font->extents;
+}
+slim_hidden_def (cairo_scaled_font_extents);
+
+/**
+ * cairo_scaled_font_text_extents:
+ * @scaled_font: a #cairo_scaled_font_t
+ * @utf8: a NUL-terminated string of text, encoded in UTF-8
+ * @extents: a #cairo_text_extents_t which to store the retrieved extents.
+ *
+ * Gets the extents for a string of text. The extents describe a
+ * user-space rectangle that encloses the "inked" portion of the text
+ * drawn at the origin (0,0) (as it would be drawn by cairo_show_text()
+ * if the cairo graphics state were set to the same font_face,
+ * font_matrix, ctm, and font_options as @scaled_font).  Additionally,
+ * the x_advance and y_advance values indicate the amount by which the
+ * current point would be advanced by cairo_show_text().
+ *
+ * Note that whitespace characters do not directly contribute to the
+ * size of the rectangle (extents.width and extents.height). They do
+ * contribute indirectly by changing the position of non-whitespace
+ * characters. In particular, trailing whitespace characters are
+ * likely to not affect the size of the rectangle, though they will
+ * affect the x_advance and y_advance values.
+ *
+ * Since: 1.2
+ **/
+void
+cairo_scaled_font_text_extents (cairo_scaled_font_t   *scaled_font,
+                               const char            *utf8,
+                               cairo_text_extents_t  *extents)
+{
+    cairo_status_t status;
+    cairo_glyph_t *glyphs = NULL;
+    int num_glyphs;
+
+    if (scaled_font->status)
+       goto ZERO_EXTENTS;
+
+    if (utf8 == NULL)
+       goto ZERO_EXTENTS;
+
+    status = cairo_scaled_font_text_to_glyphs (scaled_font, 0., 0.,
+                                              utf8, -1,
+                                              &glyphs, &num_glyphs,
+                                              NULL, NULL,
+                                              NULL);
+    if (unlikely (status)) {
+       status = _cairo_scaled_font_set_error (scaled_font, status);
+       goto ZERO_EXTENTS;
+    }
+
+    cairo_scaled_font_glyph_extents (scaled_font, glyphs, num_glyphs, extents);
+    free (glyphs);
+
+    return;
+
+ZERO_EXTENTS:
+    extents->x_bearing = 0.0;
+    extents->y_bearing = 0.0;
+    extents->width  = 0.0;
+    extents->height = 0.0;
+    extents->x_advance = 0.0;
+    extents->y_advance = 0.0;
+}
+
+/**
+ * cairo_scaled_font_glyph_extents:
+ * @scaled_font: a #cairo_scaled_font_t
+ * @glyphs: an array of glyph IDs with X and Y offsets.
+ * @num_glyphs: the number of glyphs in the @glyphs array
+ * @extents: a #cairo_text_extents_t which to store the retrieved extents.
+ *
+ * Gets the extents for an array of glyphs. The extents describe a
+ * user-space rectangle that encloses the "inked" portion of the
+ * glyphs, (as they would be drawn by cairo_show_glyphs() if the cairo
+ * graphics state were set to the same font_face, font_matrix, ctm,
+ * and font_options as @scaled_font).  Additionally, the x_advance and
+ * y_advance values indicate the amount by which the current point
+ * would be advanced by cairo_show_glyphs().
+ *
+ * Note that whitespace glyphs do not contribute to the size of the
+ * rectangle (extents.width and extents.height).
+ **/
+void
+cairo_scaled_font_glyph_extents (cairo_scaled_font_t   *scaled_font,
+                                const cairo_glyph_t   *glyphs,
+                                int                    num_glyphs,
+                                cairo_text_extents_t  *extents)
+{
+    cairo_status_t status;
+    int i;
+    double min_x = 0.0, min_y = 0.0, max_x = 0.0, max_y = 0.0;
+    cairo_bool_t visible = FALSE;
+    cairo_scaled_glyph_t *scaled_glyph = NULL;
+
+    extents->x_bearing = 0.0;
+    extents->y_bearing = 0.0;
+    extents->width  = 0.0;
+    extents->height = 0.0;
+    extents->x_advance = 0.0;
+    extents->y_advance = 0.0;
+
+    if (unlikely (scaled_font->status))
+       goto ZERO_EXTENTS;
+
+    if (num_glyphs == 0)
+       goto ZERO_EXTENTS;
+
+    if (unlikely (num_glyphs < 0)) {
+       _cairo_error_throw (CAIRO_STATUS_NEGATIVE_COUNT);
+       /* XXX Can't propagate error */
+       goto ZERO_EXTENTS;
+    }
+
+    if (unlikely (glyphs == NULL)) {
+       _cairo_error_throw (CAIRO_STATUS_NULL_POINTER);
+       /* XXX Can't propagate error */
+       goto ZERO_EXTENTS;
+    }
+
+    _cairo_scaled_font_freeze_cache (scaled_font);
+
+    for (i = 0; i < num_glyphs; i++) {
+       double                  left, top, right, bottom;
+
+       status = _cairo_scaled_glyph_lookup (scaled_font,
+                                            glyphs[i].index,
+                                            CAIRO_SCALED_GLYPH_INFO_METRICS,
+                                            &scaled_glyph);
+       if (unlikely (status)) {
+           status = _cairo_scaled_font_set_error (scaled_font, status);
+           goto UNLOCK;
+       }
+
+       /* "Ink" extents should skip "invisible" glyphs */
+       if (scaled_glyph->metrics.width == 0 || scaled_glyph->metrics.height == 0)
+           continue;
+
+       left = scaled_glyph->metrics.x_bearing + glyphs[i].x;
+       right = left + scaled_glyph->metrics.width;
+       top = scaled_glyph->metrics.y_bearing + glyphs[i].y;
+       bottom = top + scaled_glyph->metrics.height;
+
+       if (!visible) {
+           visible = TRUE;
+           min_x = left;
+           max_x = right;
+           min_y = top;
+           max_y = bottom;
+       } else {
+           if (left < min_x) min_x = left;
+           if (right > max_x) max_x = right;
+           if (top < min_y) min_y = top;
+           if (bottom > max_y) max_y = bottom;
+       }
+    }
+
+    if (visible) {
+       extents->x_bearing = min_x - glyphs[0].x;
+       extents->y_bearing = min_y - glyphs[0].y;
+       extents->width = max_x - min_x;
+       extents->height = max_y - min_y;
+    } else {
+       extents->x_bearing = 0.0;
+       extents->y_bearing = 0.0;
+       extents->width = 0.0;
+       extents->height = 0.0;
+    }
+
+    if (num_glyphs) {
+        double x0, y0, x1, y1;
+
+       x0 = glyphs[0].x;
+       y0 = glyphs[0].y;
+
+       /* scaled_glyph contains the glyph for num_glyphs - 1 already. */
+       x1 = glyphs[num_glyphs - 1].x + scaled_glyph->metrics.x_advance;
+       y1 = glyphs[num_glyphs - 1].y + scaled_glyph->metrics.y_advance;
+
+       extents->x_advance = x1 - x0;
+       extents->y_advance = y1 - y0;
+    } else {
+       extents->x_advance = 0.0;
+       extents->y_advance = 0.0;
+    }
+
+ UNLOCK:
+    _cairo_scaled_font_thaw_cache (scaled_font);
+    return;
+
+ZERO_EXTENTS:
+    extents->x_bearing = 0.0;
+    extents->y_bearing = 0.0;
+    extents->width  = 0.0;
+    extents->height = 0.0;
+    extents->x_advance = 0.0;
+    extents->y_advance = 0.0;
+}
+slim_hidden_def (cairo_scaled_font_glyph_extents);
+
+#define GLYPH_LUT_SIZE 64
+static cairo_status_t
+cairo_scaled_font_text_to_glyphs_internal_cached (cairo_scaled_font_t           *scaled_font,
+                                                   double                        x,
+                                                   double                        y,
+                                                   const char                   *utf8,
+                                                   cairo_glyph_t                *glyphs,
+                                                   cairo_text_cluster_t        **clusters,
+                                                   int                           num_chars)
+{
+    struct glyph_lut_elt {
+       unsigned long index;
+       double x_advance;
+       double y_advance;
+    } glyph_lut[GLYPH_LUT_SIZE];
+    uint32_t glyph_lut_unicode[GLYPH_LUT_SIZE];
+    cairo_status_t status;
+    const char *p;
+    int i;
+
+    for (i = 0; i < GLYPH_LUT_SIZE; i++)
+       glyph_lut_unicode[i] = ~0U;
+
+    p = utf8;
+    for (i = 0; i < num_chars; i++) {
+       int idx, num_bytes;
+       uint32_t unicode;
+       cairo_scaled_glyph_t *scaled_glyph;
+       struct glyph_lut_elt *glyph_slot;
+
+       num_bytes = _cairo_utf8_get_char_validated (p, &unicode);
+       p += num_bytes;
+
+       glyphs[i].x = x;
+       glyphs[i].y = y;
+
+       idx = unicode % ARRAY_LENGTH (glyph_lut);
+       glyph_slot = &glyph_lut[idx];
+       if (glyph_lut_unicode[idx] == unicode) {
+           glyphs[i].index = glyph_slot->index;
+           x += glyph_slot->x_advance;
+           y += glyph_slot->y_advance;
+       } else {
+           unsigned long g;
+
+           g = scaled_font->backend->ucs4_to_index (scaled_font, unicode);
+           status = _cairo_scaled_glyph_lookup (scaled_font,
+                                                g,
+                                                CAIRO_SCALED_GLYPH_INFO_METRICS,
+                                                &scaled_glyph);
+           if (unlikely (status))
+               return status;
+
+           x += scaled_glyph->metrics.x_advance;
+           y += scaled_glyph->metrics.y_advance;
+
+           glyph_lut_unicode[idx] = unicode;
+           glyph_slot->index = g;
+           glyph_slot->x_advance = scaled_glyph->metrics.x_advance;
+           glyph_slot->y_advance = scaled_glyph->metrics.y_advance;
+
+           glyphs[i].index = g;
+       }
+
+       if (clusters) {
+           (*clusters)[i].num_bytes  = num_bytes;
+           (*clusters)[i].num_glyphs = 1;
+       }
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+cairo_scaled_font_text_to_glyphs_internal_uncached (cairo_scaled_font_t         *scaled_font,
+                                                 double                  x,
+                                                 double                  y,
+                                                 const char             *utf8,
+                                                 cairo_glyph_t          *glyphs,
+                                                 cairo_text_cluster_t  **clusters,
+                                                 int                     num_chars)
+{
+    const char *p;
+    int i;
+
+    p = utf8;
+    for (i = 0; i < num_chars; i++) {
+       unsigned long g;
+       int num_bytes;
+       uint32_t unicode;
+       cairo_scaled_glyph_t *scaled_glyph;
+       cairo_status_t status;
+
+       num_bytes = _cairo_utf8_get_char_validated (p, &unicode);
+       p += num_bytes;
+
+       glyphs[i].x = x;
+       glyphs[i].y = y;
+
+       g = scaled_font->backend->ucs4_to_index (scaled_font, unicode);
+
+       /*
+        * No advance needed for a single character string. So, let's speed up
+        * one-character strings by skipping glyph lookup.
+        */
+       if (num_chars > 1) {
+           status = _cairo_scaled_glyph_lookup (scaled_font,
+                                            g,
+                                            CAIRO_SCALED_GLYPH_INFO_METRICS,
+                                            &scaled_glyph);
+           if (unlikely (status))
+               return status;
+
+           x += scaled_glyph->metrics.x_advance;
+           y += scaled_glyph->metrics.y_advance;
+       }
+
+       glyphs[i].index = g;
+
+       if (clusters) {
+           (*clusters)[i].num_bytes  = num_bytes;
+           (*clusters)[i].num_glyphs = 1;
+       }
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+/**
+ * cairo_scaled_font_text_to_glyphs:
+ * @x: X position to place first glyph
+ * @y: Y position to place first glyph
+ * @scaled_font: a #cairo_scaled_font_t
+ * @utf8: a string of text encoded in UTF-8
+ * @utf8_len: length of @utf8 in bytes, or -1 if it is NUL-terminated
+ * @glyphs: pointer to array of glyphs to fill
+ * @num_glyphs: pointer to number of glyphs
+ * @clusters: pointer to array of cluster mapping information to fill, or %NULL
+ * @num_clusters: pointer to number of clusters, or %NULL
+ * @cluster_flags: pointer to location to store cluster flags corresponding to the
+ *                 output @clusters, or %NULL
+ *
+ * Converts UTF-8 text to an array of glyphs, optionally with cluster
+ * mapping, that can be used to render later using @scaled_font.
+ *
+ * If @glyphs initially points to a non-%NULL value, that array is used
+ * as a glyph buffer, and @num_glyphs should point to the number of glyph
+ * entries available there.  If the provided glyph array is too short for
+ * the conversion, a new glyph array is allocated using cairo_glyph_allocate()
+ * and placed in @glyphs.  Upon return, @num_glyphs always contains the
+ * number of generated glyphs.  If the value @glyphs points to has changed
+ * after the call, the user is responsible for freeing the allocated glyph
+ * array using cairo_glyph_free().  This may happen even if the provided
+ * array was large enough.
+ *
+ * If @clusters is not %NULL, @num_clusters and @cluster_flags should not be %NULL,
+ * and cluster mapping will be computed.
+ * The semantics of how cluster array allocation works is similar to the glyph
+ * array.  That is,
+ * if @clusters initially points to a non-%NULL value, that array is used
+ * as a cluster buffer, and @num_clusters should point to the number of cluster
+ * entries available there.  If the provided cluster array is too short for
+ * the conversion, a new cluster array is allocated using cairo_text_cluster_allocate()
+ * and placed in @clusters.  Upon return, @num_clusters always contains the
+ * number of generated clusters.  If the value @clusters points at has changed
+ * after the call, the user is responsible for freeing the allocated cluster
+ * array using cairo_text_cluster_free().  This may happen even if the provided
+ * array was large enough.
+ *
+ * In the simplest case, @glyphs and @clusters can point to %NULL initially
+ * and a suitable array will be allocated.  In code:
+ * <informalexample><programlisting>
+ * cairo_status_t status;
+ *
+ * cairo_glyph_t *glyphs = NULL;
+ * int num_glyphs;
+ * cairo_text_cluster_t *clusters = NULL;
+ * int num_clusters;
+ * cairo_text_cluster_flags_t cluster_flags;
+ *
+ * status = cairo_scaled_font_text_to_glyphs (scaled_font,
+ *                                            x, y,
+ *                                            utf8, utf8_len,
+ *                                            &amp;glyphs, &amp;num_glyphs,
+ *                                            &amp;clusters, &amp;num_clusters, &amp;cluster_flags);
+ *
+ * if (status == CAIRO_STATUS_SUCCESS) {
+ *     cairo_show_text_glyphs (cr,
+ *                             utf8, utf8_len,
+ *                             glyphs, num_glyphs,
+ *                             clusters, num_clusters, cluster_flags);
+ *
+ *     cairo_glyph_free (glyphs);
+ *     cairo_text_cluster_free (clusters);
+ * }
+ * </programlisting></informalexample>
+ *
+ * If no cluster mapping is needed:
+ * <informalexample><programlisting>
+ * cairo_status_t status;
+ *
+ * cairo_glyph_t *glyphs = NULL;
+ * int num_glyphs;
+ *
+ * status = cairo_scaled_font_text_to_glyphs (scaled_font,
+ *                                            x, y,
+ *                                            utf8, utf8_len,
+ *                                            &amp;glyphs, &amp;num_glyphs,
+ *                                            NULL, NULL,
+ *                                            NULL);
+ *
+ * if (status == CAIRO_STATUS_SUCCESS) {
+ *     cairo_show_glyphs (cr, glyphs, num_glyphs);
+ *     cairo_glyph_free (glyphs);
+ * }
+ * </programlisting></informalexample>
+ *
+ * If stack-based glyph and cluster arrays are to be used for small
+ * arrays:
+ * <informalexample><programlisting>
+ * cairo_status_t status;
+ *
+ * cairo_glyph_t stack_glyphs[40];
+ * cairo_glyph_t *glyphs = stack_glyphs;
+ * int num_glyphs = sizeof (stack_glyphs) / sizeof (stack_glyphs[0]);
+ * cairo_text_cluster_t stack_clusters[40];
+ * cairo_text_cluster_t *clusters = stack_clusters;
+ * int num_clusters = sizeof (stack_clusters) / sizeof (stack_clusters[0]);
+ * cairo_text_cluster_flags_t cluster_flags;
+ *
+ * status = cairo_scaled_font_text_to_glyphs (scaled_font,
+ *                                            x, y,
+ *                                            utf8, utf8_len,
+ *                                            &amp;glyphs, &amp;num_glyphs,
+ *                                            &amp;clusters, &amp;num_clusters, &amp;cluster_flags);
+ *
+ * if (status == CAIRO_STATUS_SUCCESS) {
+ *     cairo_show_text_glyphs (cr,
+ *                             utf8, utf8_len,
+ *                             glyphs, num_glyphs,
+ *                             clusters, num_clusters, cluster_flags);
+ *
+ *     if (glyphs != stack_glyphs)
+ *         cairo_glyph_free (glyphs);
+ *     if (clusters != stack_clusters)
+ *         cairo_text_cluster_free (clusters);
+ * }
+ * </programlisting></informalexample>
+ *
+ * For details of how @clusters, @num_clusters, and @cluster_flags map input
+ * UTF-8 text to the output glyphs see cairo_show_text_glyphs().
+ *
+ * The output values can be readily passed to cairo_show_text_glyphs()
+ * cairo_show_glyphs(), or related functions, assuming that the exact
+ * same @scaled_font is used for the operation.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS upon success, or an error status
+ * if the input values are wrong or if conversion failed.  If the input
+ * values are correct but the conversion failed, the error status is also
+ * set on @scaled_font.
+ *
+ * Since: 1.8
+ **/
+#define CACHING_THRESHOLD 16
+cairo_status_t
+cairo_scaled_font_text_to_glyphs (cairo_scaled_font_t   *scaled_font,
+                                 double                 x,
+                                 double                 y,
+                                 const char            *utf8,
+                                 int                    utf8_len,
+                                 cairo_glyph_t        **glyphs,
+                                 int                   *num_glyphs,
+                                 cairo_text_cluster_t **clusters,
+                                 int                   *num_clusters,
+                                 cairo_text_cluster_flags_t *cluster_flags)
+{
+    int num_chars = 0;
+    cairo_int_status_t status;
+    cairo_glyph_t *orig_glyphs;
+    cairo_text_cluster_t *orig_clusters;
+
+    status = scaled_font->status;
+    if (unlikely (status))
+       return status;
+
+    /* A slew of sanity checks */
+
+    /* glyphs and num_glyphs can't be NULL */
+    if (glyphs     == NULL ||
+       num_glyphs == NULL) {
+       status = _cairo_error (CAIRO_STATUS_NULL_POINTER);
+       goto BAIL;
+    }
+
+    /* Special case for NULL and -1 */
+    if (utf8 == NULL && utf8_len == -1)
+       utf8_len = 0;
+
+    /* No NULLs for non-NULLs! */
+    if ((utf8_len && utf8          == NULL) ||
+       (clusters && num_clusters  == NULL) ||
+       (clusters && cluster_flags == NULL)) {
+       status = _cairo_error (CAIRO_STATUS_NULL_POINTER);
+       goto BAIL;
+    }
+
+    /* A -1 for utf8_len means NUL-terminated */
+    if (utf8_len == -1)
+       utf8_len = strlen (utf8);
+
+    /* A NULL *glyphs means no prealloced glyphs array */
+    if (glyphs && *glyphs == NULL)
+       *num_glyphs = 0;
+
+    /* A NULL *clusters means no prealloced clusters array */
+    if (clusters && *clusters == NULL)
+       *num_clusters = 0;
+
+    if (!clusters && num_clusters) {
+       num_clusters = NULL;
+    }
+
+    if (cluster_flags) {
+       *cluster_flags = FALSE;
+    }
+
+    if (!clusters && cluster_flags) {
+       cluster_flags = NULL;
+    }
+
+    /* Apart from that, no negatives */
+    if (utf8_len < 0 ||
+       *num_glyphs < 0 ||
+       (num_clusters && *num_clusters < 0)) {
+       status = _cairo_error (CAIRO_STATUS_NEGATIVE_COUNT);
+       goto BAIL;
+    }
+
+    if (utf8_len == 0) {
+       status = CAIRO_STATUS_SUCCESS;
+       goto BAIL;
+    }
+
+    /* validate input so backend does not have to */
+    status = _cairo_utf8_to_ucs4 (utf8, utf8_len, NULL, &num_chars);
+    if (unlikely (status))
+       goto BAIL;
+
+    _cairo_scaled_font_freeze_cache (scaled_font);
+
+    orig_glyphs = *glyphs;
+    orig_clusters = clusters ? *clusters : NULL;
+
+    if (scaled_font->backend->text_to_glyphs) {
+       status = scaled_font->backend->text_to_glyphs (scaled_font, x, y,
+                                                      utf8, utf8_len,
+                                                      glyphs, num_glyphs,
+                                                      clusters, num_clusters,
+                                                      cluster_flags);
+        if (status != CAIRO_INT_STATUS_UNSUPPORTED) {
+           if (status == CAIRO_INT_STATUS_SUCCESS) {
+               /* The checks here are crude; we only should do them in
+                * user-font backend, but they don't hurt here.  This stuff
+                * can be hard to get right. */
+
+               if (*num_glyphs < 0) {
+                   status = _cairo_error (CAIRO_STATUS_NEGATIVE_COUNT);
+                   goto DONE;
+               }
+               if (num_glyphs && *glyphs == NULL) {
+                   status = _cairo_error (CAIRO_STATUS_NULL_POINTER);
+                   goto DONE;
+               }
+
+               if (clusters) {
+                   if (*num_clusters < 0) {
+                       status = _cairo_error (CAIRO_STATUS_NEGATIVE_COUNT);
+                       goto DONE;
+                   }
+                   if (num_clusters && *clusters == NULL) {
+                       status = _cairo_error (CAIRO_STATUS_NULL_POINTER);
+                       goto DONE;
+                   }
+
+                   /* Don't trust the backend, validate clusters! */
+                   status =
+                       _cairo_validate_text_clusters (utf8, utf8_len,
+                                                      *glyphs, *num_glyphs,
+                                                      *clusters, *num_clusters,
+                                                      *cluster_flags);
+               }
+           }
+
+            goto DONE;
+       }
+    }
+
+    if (*num_glyphs < num_chars) {
+       *glyphs = cairo_glyph_allocate (num_chars);
+       if (unlikely (*glyphs == NULL)) {
+           status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+           goto DONE;
+       }
+    }
+    *num_glyphs = num_chars;
+
+    if (clusters) {
+       if (*num_clusters < num_chars) {
+           *clusters = cairo_text_cluster_allocate (num_chars);
+           if (unlikely (*clusters == NULL)) {
+               status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+               goto DONE;
+           }
+       }
+       *num_clusters = num_chars;
+    }
+
+    if (num_chars > CACHING_THRESHOLD)
+       status = cairo_scaled_font_text_to_glyphs_internal_cached (scaled_font,
+                                                                    x, y,
+                                                                    utf8,
+                                                                    *glyphs,
+                                                                    clusters,
+                                                                    num_chars);
+    else
+       status = cairo_scaled_font_text_to_glyphs_internal_uncached (scaled_font,
+                                                                  x, y,
+                                                                  utf8,
+                                                                  *glyphs,
+                                                                  clusters,
+                                                                  num_chars);
+
+ DONE: /* error that should be logged on scaled_font happened */
+    _cairo_scaled_font_thaw_cache (scaled_font);
+
+    if (unlikely (status)) {
+       *num_glyphs = 0;
+       if (*glyphs != orig_glyphs) {
+           cairo_glyph_free (*glyphs);
+           *glyphs = orig_glyphs;
+       }
+
+       if (clusters) {
+           *num_clusters = 0;
+           if (*clusters != orig_clusters) {
+               cairo_text_cluster_free (*clusters);
+               *clusters = orig_clusters;
+           }
+       }
+    }
+
+    return _cairo_scaled_font_set_error (scaled_font, status);
+
+ BAIL: /* error with input arguments */
+
+    if (num_glyphs)
+       *num_glyphs = 0;
+
+    if (num_clusters)
+       *num_clusters = 0;
+
+    return status;
+}
+slim_hidden_def (cairo_scaled_font_text_to_glyphs);
+
+static inline cairo_bool_t
+_range_contains_glyph (const cairo_box_t *extents,
+                      cairo_fixed_t left,
+                      cairo_fixed_t top,
+                      cairo_fixed_t right,
+                      cairo_fixed_t bottom)
+{
+    return right > extents->p1.x &&
+          left < extents->p2.x &&
+          bottom > extents->p1.y &&
+          top < extents->p2.y;
+}
+
+static cairo_status_t
+_cairo_scaled_font_single_glyph_device_extents (cairo_scaled_font_t     *scaled_font,
+                                               const cairo_glyph_t      *glyph,
+                                               cairo_rectangle_int_t   *extents)
+{
+    cairo_round_glyph_positions_t round_xy;
+    cairo_scaled_glyph_t *scaled_glyph;
+    cairo_status_t status;
+    cairo_box_t box;
+    cairo_fixed_t v;
+
+    status = _cairo_scaled_glyph_lookup (scaled_font,
+                                        glyph->index,
+                                        CAIRO_SCALED_GLYPH_INFO_METRICS,
+                                        &scaled_glyph);
+    if (unlikely (status))
+       return status;
+
+    round_xy = _cairo_font_options_get_round_glyph_positions (&scaled_font->options);
+    if (round_xy == CAIRO_ROUND_GLYPH_POS_ON)
+       v = _cairo_fixed_from_int (_cairo_lround (glyph->x));
+    else
+       v = _cairo_fixed_from_double (glyph->x);
+    box.p1.x = v + scaled_glyph->bbox.p1.x;
+    box.p2.x = v + scaled_glyph->bbox.p2.x;
+
+    if (round_xy == CAIRO_ROUND_GLYPH_POS_ON)
+       v = _cairo_fixed_from_int (_cairo_lround (glyph->y));
+    else
+       v = _cairo_fixed_from_double (glyph->y);
+    box.p1.y = v + scaled_glyph->bbox.p1.y;
+    box.p2.y = v + scaled_glyph->bbox.p2.y;
+
+    _cairo_box_round_to_rectangle (&box, extents);
+    return CAIRO_STATUS_SUCCESS;
+}
+
+/*
+ * Compute a device-space bounding box for the glyphs.
+ */
+cairo_status_t
+_cairo_scaled_font_glyph_device_extents (cairo_scaled_font_t    *scaled_font,
+                                        const cairo_glyph_t     *glyphs,
+                                        int                      num_glyphs,
+                                        cairo_rectangle_int_t   *extents,
+                                        cairo_bool_t *overlap_out)
+{
+    cairo_status_t status = CAIRO_STATUS_SUCCESS;
+    cairo_box_t box = { { INT_MAX, INT_MAX }, { INT_MIN, INT_MIN }};
+    cairo_scaled_glyph_t *glyph_cache[64];
+    cairo_bool_t overlap = overlap_out ? FALSE : TRUE;
+    cairo_round_glyph_positions_t round_glyph_positions = _cairo_font_options_get_round_glyph_positions (&scaled_font->options);
+    int i;
+
+    if (unlikely (scaled_font->status))
+       return scaled_font->status;
+
+    if (num_glyphs == 1) {
+       if (overlap_out)
+           *overlap_out = FALSE;
+       return _cairo_scaled_font_single_glyph_device_extents (scaled_font,
+                                                              glyphs,
+                                                              extents);
+    }
+
+    _cairo_scaled_font_freeze_cache (scaled_font);
+
+    memset (glyph_cache, 0, sizeof (glyph_cache));
+
+    for (i = 0; i < num_glyphs; i++) {
+       cairo_scaled_glyph_t    *scaled_glyph;
+       cairo_fixed_t x, y, x1, y1, x2, y2;
+       int cache_index = glyphs[i].index % ARRAY_LENGTH (glyph_cache);
+
+       scaled_glyph = glyph_cache[cache_index];
+       if (scaled_glyph == NULL ||
+           _cairo_scaled_glyph_index (scaled_glyph) != glyphs[i].index)
+       {
+           status = _cairo_scaled_glyph_lookup (scaled_font,
+                                                glyphs[i].index,
+                                                CAIRO_SCALED_GLYPH_INFO_METRICS,
+                                                &scaled_glyph);
+           if (unlikely (status))
+               break;
+
+           glyph_cache[cache_index] = scaled_glyph;
+       }
+
+       if (round_glyph_positions == CAIRO_ROUND_GLYPH_POS_ON)
+           x = _cairo_fixed_from_int (_cairo_lround (glyphs[i].x));
+       else
+           x = _cairo_fixed_from_double (glyphs[i].x);
+       x1 = x + scaled_glyph->bbox.p1.x;
+       x2 = x + scaled_glyph->bbox.p2.x;
+
+       if (round_glyph_positions == CAIRO_ROUND_GLYPH_POS_ON)
+           y = _cairo_fixed_from_int (_cairo_lround (glyphs[i].y));
+       else
+           y = _cairo_fixed_from_double (glyphs[i].y);
+       y1 = y + scaled_glyph->bbox.p1.y;
+       y2 = y + scaled_glyph->bbox.p2.y;
+
+       if (overlap == FALSE)
+           overlap = _range_contains_glyph (&box, x1, y1, x2, y2);
+
+       if (x1 < box.p1.x) box.p1.x = x1;
+       if (x2 > box.p2.x) box.p2.x = x2;
+       if (y1 < box.p1.y) box.p1.y = y1;
+       if (y2 > box.p2.y) box.p2.y = y2;
+    }
+
+    _cairo_scaled_font_thaw_cache (scaled_font);
+    if (unlikely (status))
+       return _cairo_scaled_font_set_error (scaled_font, status);
+
+    if (box.p1.x < box.p2.x) {
+       _cairo_box_round_to_rectangle (&box, extents);
+    } else {
+       extents->x = extents->y = 0;
+       extents->width = extents->height = 0;
+    }
+
+    if (overlap_out != NULL)
+       *overlap_out = overlap;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+void
+_cairo_scaled_font_glyph_approximate_extents (cairo_scaled_font_t       *scaled_font,
+                                             const cairo_glyph_t        *glyphs,
+                                             int                      num_glyphs,
+                                             cairo_rectangle_int_t   *extents)
+{
+    double x0, x1, y0, y1, pad;
+    int i;
+
+    assert (num_glyphs);
+
+    x0 = x1 = glyphs[0].x;
+    y0 = y1 = glyphs[0].y;
+    for (i = 1; i < num_glyphs; i++) {
+       double g;
+
+       g = glyphs[i].x;
+       if (g < x0) x0 = g;
+       if (g > x1) x1 = g;
+
+       g = glyphs[i].y;
+       if (g < y0) y0 = g;
+       if (g > y1) y1 = g;
+    }
+
+    pad = MAX(scaled_font->fs_extents.max_x_advance,
+             scaled_font->fs_extents.height);
+    pad *= scaled_font->max_scale;
+
+    extents->x = floor (x0 - pad);
+    extents->width = ceil (x1 + pad) - extents->x;
+    extents->y = floor (y0 - pad);
+    extents->height = ceil (y1 + pad) - extents->y;
+}
+
+#if 0
+/* XXX win32 */
+cairo_status_t
+_cairo_scaled_font_show_glyphs (cairo_scaled_font_t    *scaled_font,
+                               cairo_operator_t         op,
+                               const cairo_pattern_t   *pattern,
+                               cairo_surface_t         *surface,
+                               int                      source_x,
+                               int                      source_y,
+                               int                      dest_x,
+                               int                      dest_y,
+                               unsigned int             width,
+                               unsigned int             height,
+                               cairo_glyph_t           *glyphs,
+                               int                      num_glyphs,
+                               cairo_region_t          *clip_region)
+{
+    cairo_int_status_t status;
+    cairo_surface_t *mask = NULL;
+    cairo_format_t mask_format = CAIRO_FORMAT_A1; /* shut gcc up */
+    cairo_surface_pattern_t mask_pattern;
+    int i;
+
+    /* These operators aren't interpreted the same way by the backends;
+     * they are implemented in terms of other operators in cairo-gstate.c
+     */
+    assert (op != CAIRO_OPERATOR_SOURCE && op != CAIRO_OPERATOR_CLEAR);
+
+    if (scaled_font->status)
+       return scaled_font->status;
+
+    if (!num_glyphs)
+       return CAIRO_STATUS_SUCCESS;
+
+    if (scaled_font->backend->show_glyphs != NULL) {
+       int remaining_glyphs = num_glyphs;
+       status = scaled_font->backend->show_glyphs (scaled_font,
+                                                   op, pattern,
+                                                   surface,
+                                                   source_x, source_y,
+                                                   dest_x, dest_y,
+                                                   width, height,
+                                                   glyphs, num_glyphs,
+                                                   clip_region,
+                                                   &remaining_glyphs);
+       glyphs += num_glyphs - remaining_glyphs;
+       num_glyphs = remaining_glyphs;
+       if (remaining_glyphs == 0)
+           status = CAIRO_INT_STATUS_SUCCESS;
+       if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+           return _cairo_scaled_font_set_error (scaled_font, status);
+    }
+
+    /* Font display routine either does not exist or failed. */
+
+    _cairo_scaled_font_freeze_cache (scaled_font);
+
+    for (i = 0; i < num_glyphs; i++) {
+       int x, y;
+       cairo_image_surface_t *glyph_surface;
+       cairo_scaled_glyph_t *scaled_glyph;
+
+       status = _cairo_scaled_glyph_lookup (scaled_font,
+                                            glyphs[i].index,
+                                            CAIRO_SCALED_GLYPH_INFO_SURFACE,
+                                            &scaled_glyph);
+
+       if (unlikely (status))
+           goto CLEANUP_MASK;
+
+       glyph_surface = scaled_glyph->surface;
+
+       /* To start, create the mask using the format from the first
+        * glyph. Later we'll deal with different formats. */
+       if (mask == NULL) {
+           mask_format = glyph_surface->format;
+           mask = cairo_image_surface_create (mask_format, width, height);
+           status = mask->status;
+           if (unlikely (status))
+               goto CLEANUP_MASK;
+       }
+
+       /* If we have glyphs of different formats, we "upgrade" the mask
+        * to the wider of the formats. */
+       if (glyph_surface->format != mask_format &&
+           _cairo_format_bits_per_pixel (mask_format) <
+           _cairo_format_bits_per_pixel (glyph_surface->format) )
+       {
+           cairo_surface_t *new_mask;
+
+           switch (glyph_surface->format) {
+           case CAIRO_FORMAT_ARGB32:
+           case CAIRO_FORMAT_A8:
+           case CAIRO_FORMAT_A1:
+               mask_format = glyph_surface->format;
+               break;
+           case CAIRO_FORMAT_RGB16_565:
+           case CAIRO_FORMAT_RGB24:
+           case CAIRO_FORMAT_RGB30:
+           case CAIRO_FORMAT_INVALID:
+           default:
+               ASSERT_NOT_REACHED;
+               mask_format = CAIRO_FORMAT_ARGB32;
+               break;
+           }
+
+           new_mask = cairo_image_surface_create (mask_format, width, height);
+           status = new_mask->status;
+           if (unlikely (status)) {
+               cairo_surface_destroy (new_mask);
+               goto CLEANUP_MASK;
+           }
+
+           _cairo_pattern_init_for_surface (&mask_pattern, mask);
+           /* Note that we only upgrade masks, i.e. A1 -> A8 -> ARGB32, so there is
+            * never any component alpha here.
+            */
+           status = _cairo_surface_composite (CAIRO_OPERATOR_ADD,
+                                              &_cairo_pattern_white.base,
+                                              &mask_pattern.base,
+                                              new_mask,
+                                              0, 0,
+                                              0, 0,
+                                              0, 0,
+                                              width, height,
+                                              NULL);
+
+           _cairo_pattern_fini (&mask_pattern.base);
+
+           if (unlikely (status)) {
+               cairo_surface_destroy (new_mask);
+               goto CLEANUP_MASK;
+           }
+
+           cairo_surface_destroy (mask);
+           mask = new_mask;
+       }
+
+       if (glyph_surface->width && glyph_surface->height) {
+           cairo_surface_pattern_t glyph_pattern;
+
+           /* round glyph locations to the nearest pixel */
+           /* XXX: FRAGILE: We're ignoring device_transform scaling here. A bug? */
+           x = _cairo_lround (glyphs[i].x -
+                              glyph_surface->base.device_transform.x0);
+           y = _cairo_lround (glyphs[i].y -
+                              glyph_surface->base.device_transform.y0);
+
+           _cairo_pattern_init_for_surface (&glyph_pattern,
+                                            &glyph_surface->base);
+           if (mask_format == CAIRO_FORMAT_ARGB32)
+               glyph_pattern.base.has_component_alpha = TRUE;
+
+           status = _cairo_surface_composite (CAIRO_OPERATOR_ADD,
+                                              &_cairo_pattern_white.base,
+                                              &glyph_pattern.base,
+                                              mask,
+                                              0, 0,
+                                              0, 0,
+                                              x - dest_x, y - dest_y,
+                                              glyph_surface->width,
+                                              glyph_surface->height,
+                                              NULL);
+
+           _cairo_pattern_fini (&glyph_pattern.base);
+
+           if (unlikely (status))
+               goto CLEANUP_MASK;
+       }
+    }
+
+    _cairo_pattern_init_for_surface (&mask_pattern, mask);
+    if (mask_format == CAIRO_FORMAT_ARGB32)
+       mask_pattern.base.has_component_alpha = TRUE;
+
+    status = _cairo_surface_composite (op, pattern, &mask_pattern.base,
+                                      surface,
+                                      source_x, source_y,
+                                      0,        0,
+                                      dest_x,   dest_y,
+                                      width,    height,
+                                      clip_region);
+
+    _cairo_pattern_fini (&mask_pattern.base);
+
+CLEANUP_MASK:
+    _cairo_scaled_font_thaw_cache (scaled_font);
+
+    if (mask != NULL)
+       cairo_surface_destroy (mask);
+    return _cairo_scaled_font_set_error (scaled_font, status);
+}
+#endif
+
+/* Add a single-device-unit rectangle to a path. */
+static cairo_status_t
+_add_unit_rectangle_to_path (cairo_path_fixed_t *path,
+                            cairo_fixed_t x,
+                            cairo_fixed_t y)
+{
+    cairo_status_t status;
+
+    status = _cairo_path_fixed_move_to (path, x, y);
+    if (unlikely (status))
+       return status;
+
+    status = _cairo_path_fixed_rel_line_to (path,
+                                           _cairo_fixed_from_int (1),
+                                           _cairo_fixed_from_int (0));
+    if (unlikely (status))
+       return status;
+
+    status = _cairo_path_fixed_rel_line_to (path,
+                                           _cairo_fixed_from_int (0),
+                                           _cairo_fixed_from_int (1));
+    if (unlikely (status))
+       return status;
+
+    status = _cairo_path_fixed_rel_line_to (path,
+                                           _cairo_fixed_from_int (-1),
+                                           _cairo_fixed_from_int (0));
+    if (unlikely (status))
+       return status;
+
+    return _cairo_path_fixed_close_path (path);
+}
+
+/**
+ * _trace_mask_to_path:
+ * @bitmap: An alpha mask (either %CAIRO_FORMAT_A1 or %CAIRO_FORMAT_A8)
+ * @path: An initialized path to hold the result
+ *
+ * Given a mask surface, (an alpha image), fill out the provided path
+ * so that when filled it would result in something that approximates
+ * the mask.
+ *
+ * Note: The current tracing code here is extremely primitive. It
+ * operates only on an A1 surface, (converting an A8 surface to A1 if
+ * necessary), and performs the tracing by drawing a little square
+ * around each pixel that is on in the mask. We do not pretend that
+ * this is a high-quality result. But we are leaving it up to someone
+ * who cares enough about getting a better result to implement
+ * something more sophisticated.
+ **/
+static cairo_status_t
+_trace_mask_to_path (cairo_image_surface_t *mask,
+                    cairo_path_fixed_t *path,
+                    double tx, double ty)
+{
+    const uint8_t *row;
+    int rows, cols, bytes_per_row;
+    int x, y, bit;
+    double xoff, yoff;
+    cairo_fixed_t x0, y0;
+    cairo_fixed_t px, py;
+    cairo_status_t status;
+
+    mask = _cairo_image_surface_coerce_to_format (mask, CAIRO_FORMAT_A1);
+    status = mask->base.status;
+    if (unlikely (status))
+       return status;
+
+    cairo_surface_get_device_offset (&mask->base, &xoff, &yoff);
+    x0 = _cairo_fixed_from_double (tx - xoff);
+    y0 = _cairo_fixed_from_double (ty - yoff);
+
+    bytes_per_row = (mask->width + 7) / 8;
+    row = mask->data;
+    for (y = 0, rows = mask->height; rows--; row += mask->stride, y++) {
+       const uint8_t *byte_ptr = row;
+       x = 0;
+       py = _cairo_fixed_from_int (y);
+       for (cols = bytes_per_row; cols--; ) {
+           uint8_t byte = *byte_ptr++;
+           if (byte == 0) {
+               x += 8;
+               continue;
+           }
+
+           byte = CAIRO_BITSWAP8_IF_LITTLE_ENDIAN (byte);
+           for (bit = 1 << 7; bit && x < mask->width; bit >>= 1, x++) {
+               if (byte & bit) {
+                   px = _cairo_fixed_from_int (x);
+                   status = _add_unit_rectangle_to_path (path,
+                                                         px + x0,
+                                                         py + y0);
+                   if (unlikely (status))
+                       goto BAIL;
+               }
+           }
+       }
+    }
+
+BAIL:
+    cairo_surface_destroy (&mask->base);
+
+    return status;
+}
+
+cairo_status_t
+_cairo_scaled_font_glyph_path (cairo_scaled_font_t *scaled_font,
+                              const cairo_glyph_t *glyphs,
+                              int                  num_glyphs,
+                              cairo_path_fixed_t  *path)
+{
+    cairo_int_status_t status;
+    int        i;
+
+    status = scaled_font->status;
+    if (unlikely (status))
+       return status;
+
+    _cairo_scaled_font_freeze_cache (scaled_font);
+    for (i = 0; i < num_glyphs; i++) {
+       cairo_scaled_glyph_t *scaled_glyph;
+
+       status = _cairo_scaled_glyph_lookup (scaled_font,
+                                            glyphs[i].index,
+                                            CAIRO_SCALED_GLYPH_INFO_PATH,
+                                            &scaled_glyph);
+       if (status == CAIRO_INT_STATUS_SUCCESS) {
+           status = _cairo_path_fixed_append (path,
+                                              scaled_glyph->path,
+                                              _cairo_fixed_from_double (glyphs[i].x),
+                                              _cairo_fixed_from_double (glyphs[i].y));
+
+       } else if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
+           /* If the font is incapable of providing a path, then we'll
+            * have to trace our own from a surface.
+            */
+           status = _cairo_scaled_glyph_lookup (scaled_font,
+                                                glyphs[i].index,
+                                                CAIRO_SCALED_GLYPH_INFO_SURFACE,
+                                                &scaled_glyph);
+           if (unlikely (status))
+               goto BAIL;
+
+           status = _trace_mask_to_path (scaled_glyph->surface, path,
+                                         glyphs[i].x, glyphs[i].y);
+       }
+
+       if (unlikely (status))
+           goto BAIL;
+    }
+  BAIL:
+    _cairo_scaled_font_thaw_cache (scaled_font);
+
+    return _cairo_scaled_font_set_error (scaled_font, status);
+}
+
+/**
+ * _cairo_scaled_glyph_set_metrics:
+ * @scaled_glyph: a #cairo_scaled_glyph_t
+ * @scaled_font: a #cairo_scaled_font_t
+ * @fs_metrics: a #cairo_text_extents_t in font space
+ *
+ * _cairo_scaled_glyph_set_metrics() stores user space metrics
+ * for the specified glyph given font space metrics. It is
+ * called by the font backend when initializing a glyph with
+ * %CAIRO_SCALED_GLYPH_INFO_METRICS.
+ **/
+void
+_cairo_scaled_glyph_set_metrics (cairo_scaled_glyph_t *scaled_glyph,
+                                cairo_scaled_font_t *scaled_font,
+                                cairo_text_extents_t *fs_metrics)
+{
+    cairo_bool_t first = TRUE;
+    double hm, wm;
+    double min_user_x = 0.0, max_user_x = 0.0, min_user_y = 0.0, max_user_y = 0.0;
+    double min_device_x = 0.0, max_device_x = 0.0, min_device_y = 0.0, max_device_y = 0.0;
+    double device_x_advance, device_y_advance;
+
+    scaled_glyph->fs_metrics = *fs_metrics;
+
+    for (hm = 0.0; hm <= 1.0; hm += 1.0)
+       for (wm = 0.0; wm <= 1.0; wm += 1.0) {
+           double x, y;
+
+           /* Transform this corner to user space */
+           x = fs_metrics->x_bearing + fs_metrics->width * wm;
+           y = fs_metrics->y_bearing + fs_metrics->height * hm;
+           cairo_matrix_transform_point (&scaled_font->font_matrix,
+                                         &x, &y);
+           if (first) {
+               min_user_x = max_user_x = x;
+               min_user_y = max_user_y = y;
+           } else {
+               if (x < min_user_x) min_user_x = x;
+               if (x > max_user_x) max_user_x = x;
+               if (y < min_user_y) min_user_y = y;
+               if (y > max_user_y) max_user_y = y;
+           }
+
+           /* Transform this corner to device space from glyph origin */
+           x = fs_metrics->x_bearing + fs_metrics->width * wm;
+           y = fs_metrics->y_bearing + fs_metrics->height * hm;
+           cairo_matrix_transform_distance (&scaled_font->scale,
+                                            &x, &y);
+
+           if (first) {
+               min_device_x = max_device_x = x;
+               min_device_y = max_device_y = y;
+           } else {
+               if (x < min_device_x) min_device_x = x;
+               if (x > max_device_x) max_device_x = x;
+               if (y < min_device_y) min_device_y = y;
+               if (y > max_device_y) max_device_y = y;
+           }
+           first = FALSE;
+       }
+    scaled_glyph->metrics.x_bearing = min_user_x;
+    scaled_glyph->metrics.y_bearing = min_user_y;
+    scaled_glyph->metrics.width = max_user_x - min_user_x;
+    scaled_glyph->metrics.height = max_user_y - min_user_y;
+
+    scaled_glyph->metrics.x_advance = fs_metrics->x_advance;
+    scaled_glyph->metrics.y_advance = fs_metrics->y_advance;
+    cairo_matrix_transform_distance (&scaled_font->font_matrix,
+                                    &scaled_glyph->metrics.x_advance,
+                                    &scaled_glyph->metrics.y_advance);
+
+    device_x_advance = fs_metrics->x_advance;
+    device_y_advance = fs_metrics->y_advance;
+    cairo_matrix_transform_distance (&scaled_font->scale,
+                                    &device_x_advance,
+                                    &device_y_advance);
+
+    scaled_glyph->bbox.p1.x = _cairo_fixed_from_double (min_device_x);
+    scaled_glyph->bbox.p1.y = _cairo_fixed_from_double (min_device_y);
+    scaled_glyph->bbox.p2.x = _cairo_fixed_from_double (max_device_x);
+    scaled_glyph->bbox.p2.y = _cairo_fixed_from_double (max_device_y);
+
+    scaled_glyph->x_advance = _cairo_lround (device_x_advance);
+    scaled_glyph->y_advance = _cairo_lround (device_y_advance);
+
+    scaled_glyph->has_info |= CAIRO_SCALED_GLYPH_INFO_METRICS;
+}
+
+void
+_cairo_scaled_glyph_set_surface (cairo_scaled_glyph_t *scaled_glyph,
+                                cairo_scaled_font_t *scaled_font,
+                                cairo_image_surface_t *surface)
+{
+    if (scaled_glyph->surface != NULL)
+       cairo_surface_destroy (&scaled_glyph->surface->base);
+
+    /* sanity check the backend glyph contents */
+    _cairo_debug_check_image_surface_is_defined (&surface->base);
+    scaled_glyph->surface = surface;
+
+    if (surface != NULL)
+       scaled_glyph->has_info |= CAIRO_SCALED_GLYPH_INFO_SURFACE;
+    else
+       scaled_glyph->has_info &= ~CAIRO_SCALED_GLYPH_INFO_SURFACE;
+}
+
+void
+_cairo_scaled_glyph_set_path (cairo_scaled_glyph_t *scaled_glyph,
+                             cairo_scaled_font_t *scaled_font,
+                             cairo_path_fixed_t *path)
+{
+    if (scaled_glyph->path != NULL)
+       _cairo_path_fixed_destroy (scaled_glyph->path);
+
+    scaled_glyph->path = path;
+
+    if (path != NULL)
+       scaled_glyph->has_info |= CAIRO_SCALED_GLYPH_INFO_PATH;
+    else
+       scaled_glyph->has_info &= ~CAIRO_SCALED_GLYPH_INFO_PATH;
+}
+
+void
+_cairo_scaled_glyph_set_recording_surface (cairo_scaled_glyph_t *scaled_glyph,
+                                          cairo_scaled_font_t *scaled_font,
+                                          cairo_surface_t *recording_surface)
+{
+    if (scaled_glyph->recording_surface != NULL) {
+       cairo_surface_finish (scaled_glyph->recording_surface);
+       cairo_surface_destroy (scaled_glyph->recording_surface);
+    }
+
+    scaled_glyph->recording_surface = recording_surface;
+
+    if (recording_surface != NULL)
+       scaled_glyph->has_info |= CAIRO_SCALED_GLYPH_INFO_RECORDING_SURFACE;
+    else
+       scaled_glyph->has_info &= ~CAIRO_SCALED_GLYPH_INFO_RECORDING_SURFACE;
+}
+
+static cairo_bool_t
+_cairo_scaled_glyph_page_can_remove (const void *closure)
+{
+    const cairo_scaled_glyph_page_t *page = closure;
+    const cairo_scaled_font_t *scaled_font;
+
+    scaled_font = (cairo_scaled_font_t *) page->cache_entry.hash;
+    return scaled_font->cache_frozen == 0;
+}
+
+static cairo_status_t
+_cairo_scaled_font_allocate_glyph (cairo_scaled_font_t *scaled_font,
+                                  cairo_scaled_glyph_t **scaled_glyph)
+{
+    cairo_scaled_glyph_page_t *page;
+    cairo_status_t status;
+
+    /* only the first page in the list may contain available slots */
+    if (! cairo_list_is_empty (&scaled_font->glyph_pages)) {
+        page = cairo_list_last_entry (&scaled_font->glyph_pages,
+                                      cairo_scaled_glyph_page_t,
+                                      link);
+        if (page->num_glyphs < CAIRO_SCALED_GLYPH_PAGE_SIZE) {
+            *scaled_glyph = &page->glyphs[page->num_glyphs++];
+            return CAIRO_STATUS_SUCCESS;
+        }
+    }
+
+    page = malloc (sizeof (cairo_scaled_glyph_page_t));
+    if (unlikely (page == NULL))
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    page->cache_entry.hash = (unsigned long) scaled_font;
+    page->cache_entry.size = 1; /* XXX occupancy weighting? */
+    page->num_glyphs = 0;
+
+    CAIRO_MUTEX_LOCK (_cairo_scaled_glyph_page_cache_mutex);
+    if (scaled_font->global_cache_frozen == FALSE) {
+       if (unlikely (cairo_scaled_glyph_page_cache.hash_table == NULL)) {
+           status = _cairo_cache_init (&cairo_scaled_glyph_page_cache,
+                                       NULL,
+                                       _cairo_scaled_glyph_page_can_remove,
+                                       _cairo_scaled_glyph_page_destroy,
+                                       MAX_GLYPH_PAGES_CACHED);
+           if (unlikely (status)) {
+               CAIRO_MUTEX_UNLOCK (_cairo_scaled_glyph_page_cache_mutex);
+               free (page);
+               return status;
+           }
+       }
+
+       _cairo_cache_freeze (&cairo_scaled_glyph_page_cache);
+       scaled_font->global_cache_frozen = TRUE;
+    }
+
+    status = _cairo_cache_insert (&cairo_scaled_glyph_page_cache,
+                                 &page->cache_entry);
+    CAIRO_MUTEX_UNLOCK (_cairo_scaled_glyph_page_cache_mutex);
+    if (unlikely (status)) {
+       free (page);
+       return status;
+    }
+
+    cairo_list_add_tail (&page->link, &scaled_font->glyph_pages);
+
+    *scaled_glyph = &page->glyphs[page->num_glyphs++];
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_scaled_font_free_last_glyph (cairo_scaled_font_t *scaled_font,
+                                  cairo_scaled_glyph_t *scaled_glyph)
+{
+    cairo_scaled_glyph_page_t *page;
+
+    assert (! cairo_list_is_empty (&scaled_font->glyph_pages));
+    page = cairo_list_last_entry (&scaled_font->glyph_pages,
+                                  cairo_scaled_glyph_page_t,
+                                  link);
+    assert (scaled_glyph == &page->glyphs[page->num_glyphs-1]);
+
+    _cairo_scaled_glyph_fini (scaled_font, scaled_glyph);
+
+    if (--page->num_glyphs == 0) {
+       _cairo_cache_remove (&cairo_scaled_glyph_page_cache,
+                            &page->cache_entry);
+    }
+}
+
+/**
+ * _cairo_scaled_glyph_lookup:
+ * @scaled_font: a #cairo_scaled_font_t
+ * @index: the glyph to create
+ * @info: a #cairo_scaled_glyph_info_t marking which portions of
+ * the glyph should be filled in.
+ * @scaled_glyph_ret: a #cairo_scaled_glyph_t where the glyph
+ * is returned.
+ *
+ * If the desired info is not available, (for example, when trying to
+ * get INFO_PATH with a bitmapped font), this function will return
+ * %CAIRO_INT_STATUS_UNSUPPORTED.
+ *
+ * Note: This function must be called with the scaled font frozen, and it must
+ * remain frozen for as long as the @scaled_glyph_ret is alive. (If the scaled
+ * font was not frozen, then there is no guarantee that the glyph would not be
+ * evicted before you tried to access it.) See
+ * _cairo_scaled_font_freeze_cache() and _cairo_scaled_font_thaw_cache().
+ *
+ * Returns: a glyph with the requested portions filled in. Glyph
+ * lookup is cached and glyph will be automatically freed along
+ * with the scaled_font so no explicit free is required.
+ * @info can be one or more of:
+ *  %CAIRO_SCALED_GLYPH_INFO_METRICS - glyph metrics and bounding box
+ *  %CAIRO_SCALED_GLYPH_INFO_SURFACE - surface holding glyph image
+ *  %CAIRO_SCALED_GLYPH_INFO_PATH - path holding glyph outline in device space
+ **/
+cairo_int_status_t
+_cairo_scaled_glyph_lookup (cairo_scaled_font_t *scaled_font,
+                           unsigned long index,
+                           cairo_scaled_glyph_info_t info,
+                           cairo_scaled_glyph_t **scaled_glyph_ret)
+{
+    cairo_int_status_t          status = CAIRO_INT_STATUS_SUCCESS;
+    cairo_scaled_glyph_t       *scaled_glyph;
+    cairo_scaled_glyph_info_t   need_info;
+
+    *scaled_glyph_ret = NULL;
+
+    if (unlikely (scaled_font->status))
+       return scaled_font->status;
+
+    assert (CAIRO_MUTEX_IS_LOCKED(scaled_font->mutex));
+
+    if (CAIRO_INJECT_FAULT ())
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    /*
+     * Check cache for glyph
+     */
+    scaled_glyph = _cairo_hash_table_lookup (scaled_font->glyphs,
+                                            (cairo_hash_entry_t *) &index);
+    if (scaled_glyph == NULL) {
+       status = _cairo_scaled_font_allocate_glyph (scaled_font, &scaled_glyph);
+       if (unlikely (status))
+           goto err;
+
+       memset (scaled_glyph, 0, sizeof (cairo_scaled_glyph_t));
+       _cairo_scaled_glyph_set_index (scaled_glyph, index);
+       cairo_list_init (&scaled_glyph->dev_privates);
+
+       /* ask backend to initialize metrics and shape fields */
+       status =
+           scaled_font->backend->scaled_glyph_init (scaled_font,
+                                                    scaled_glyph,
+                                                    info | CAIRO_SCALED_GLYPH_INFO_METRICS);
+       if (unlikely (status)) {
+           _cairo_scaled_font_free_last_glyph (scaled_font, scaled_glyph);
+           goto err;
+       }
+
+       status = _cairo_hash_table_insert (scaled_font->glyphs,
+                                          &scaled_glyph->hash_entry);
+       if (unlikely (status)) {
+           _cairo_scaled_font_free_last_glyph (scaled_font, scaled_glyph);
+           goto err;
+       }
+    }
+
+    /*
+     * Check and see if the glyph, as provided,
+     * already has the requested data and amend it if not
+     */
+    need_info = info & ~scaled_glyph->has_info;
+    if (need_info) {
+       status = scaled_font->backend->scaled_glyph_init (scaled_font,
+                                                         scaled_glyph,
+                                                         need_info);
+       if (unlikely (status))
+           goto err;
+
+       /* Don't trust the scaled_glyph_init() return value, the font
+        * backend may not even know about some of the info.  For example,
+        * no backend other than the user-fonts knows about recording-surface
+        * glyph info. */
+       if (info & ~scaled_glyph->has_info)
+           return CAIRO_INT_STATUS_UNSUPPORTED;
+    }
+
+    *scaled_glyph_ret = scaled_glyph;
+    return CAIRO_STATUS_SUCCESS;
+
+err:
+    /* It's not an error for the backend to not support the info we want. */
+    if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+       status = _cairo_scaled_font_set_error (scaled_font, status);
+    return status;
+}
+
+double
+_cairo_scaled_font_get_max_scale (cairo_scaled_font_t *scaled_font)
+{
+    return scaled_font->max_scale;
+}
+
+
+/**
+ * cairo_scaled_font_get_font_face:
+ * @scaled_font: a #cairo_scaled_font_t
+ *
+ * Gets the font face that this scaled font uses.  This might be the
+ * font face passed to cairo_scaled_font_create(), but this does not
+ * hold true for all possible cases.
+ *
+ * Return value: The #cairo_font_face_t with which @scaled_font was
+ * created.
+ *
+ * Since: 1.2
+ **/
+cairo_font_face_t *
+cairo_scaled_font_get_font_face (cairo_scaled_font_t *scaled_font)
+{
+    if (scaled_font->status)
+       return (cairo_font_face_t*) &_cairo_font_face_nil;
+
+    if (scaled_font->original_font_face != NULL)
+       return scaled_font->original_font_face;
+
+    return scaled_font->font_face;
+}
+slim_hidden_def (cairo_scaled_font_get_font_face);
+
+/**
+ * cairo_scaled_font_get_font_matrix:
+ * @scaled_font: a #cairo_scaled_font_t
+ * @font_matrix: return value for the matrix
+ *
+ * Stores the font matrix with which @scaled_font was created into
+ * @matrix.
+ *
+ * Since: 1.2
+ **/
+void
+cairo_scaled_font_get_font_matrix (cairo_scaled_font_t *scaled_font,
+                                  cairo_matrix_t       *font_matrix)
+{
+    if (scaled_font->status) {
+       cairo_matrix_init_identity (font_matrix);
+       return;
+    }
+
+    *font_matrix = scaled_font->font_matrix;
+}
+slim_hidden_def (cairo_scaled_font_get_font_matrix);
+
+/**
+ * cairo_scaled_font_get_ctm:
+ * @scaled_font: a #cairo_scaled_font_t
+ * @ctm: return value for the CTM
+ *
+ * Stores the CTM with which @scaled_font was created into @ctm.
+ * Note that the translation offsets (x0, y0) of the CTM are ignored
+ * by cairo_scaled_font_create().  So, the matrix this
+ * function returns always has 0,0 as x0,y0.
+ *
+ * Since: 1.2
+ **/
+void
+cairo_scaled_font_get_ctm (cairo_scaled_font_t *scaled_font,
+                          cairo_matrix_t       *ctm)
+{
+    if (scaled_font->status) {
+       cairo_matrix_init_identity (ctm);
+       return;
+    }
+
+    *ctm = scaled_font->ctm;
+}
+slim_hidden_def (cairo_scaled_font_get_ctm);
+
+/**
+ * cairo_scaled_font_get_scale_matrix:
+ * @scaled_font: a #cairo_scaled_font_t
+ * @scale_matrix: return value for the matrix
+ *
+ * Stores the scale matrix of @scaled_font into @matrix.
+ * The scale matrix is product of the font matrix and the ctm
+ * associated with the scaled font, and hence is the matrix mapping from
+ * font space to device space.
+ *
+ * Since: 1.8
+ **/
+void
+cairo_scaled_font_get_scale_matrix (cairo_scaled_font_t        *scaled_font,
+                                   cairo_matrix_t      *scale_matrix)
+{
+    if (scaled_font->status) {
+       cairo_matrix_init_identity (scale_matrix);
+       return;
+    }
+
+    *scale_matrix = scaled_font->scale;
+}
+
+/**
+ * cairo_scaled_font_get_font_options:
+ * @scaled_font: a #cairo_scaled_font_t
+ * @options: return value for the font options
+ *
+ * Stores the font options with which @scaled_font was created into
+ * @options.
+ *
+ * Since: 1.2
+ **/
+void
+cairo_scaled_font_get_font_options (cairo_scaled_font_t                *scaled_font,
+                                   cairo_font_options_t        *options)
+{
+    if (cairo_font_options_status (options))
+       return;
+
+    if (scaled_font->status) {
+       _cairo_font_options_init_default (options);
+       return;
+    }
+
+    _cairo_font_options_init_copy (options, &scaled_font->options);
+}
+slim_hidden_def (cairo_scaled_font_get_font_options);
diff --git a/src/cairo-script-private.h b/src/cairo-script-private.h
new file mode 100644 (file)
index 0000000..5b506f5
--- /dev/null
@@ -0,0 +1,59 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2008 Chris Wilson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Chris Wilson
+ *
+ * Contributor(s):
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#ifndef CAIRO_SCRIPT_PRIVATE_H
+#define CAIRO_SCRIPT_PRIVATE_H
+
+#include "cairo.h"
+#include "cairo-script.h"
+
+#include "cairo-compiler-private.h"
+#include "cairo-output-stream-private.h"
+#include "cairo-types-private.h"
+
+CAIRO_BEGIN_DECLS
+
+cairo_private cairo_device_t *
+_cairo_script_context_create_internal (cairo_output_stream_t *stream);
+
+cairo_private void
+_cairo_script_context_attach_snapshots (cairo_device_t *device,
+                                       cairo_bool_t enable);
+
+slim_hidden_proto (cairo_script_surface_create);
+
+CAIRO_END_DECLS
+
+#endif /* CAIRO_SCRIPT_PRIVATE_H */
diff --git a/src/cairo-script-surface.c b/src/cairo-script-surface.c
new file mode 100644 (file)
index 0000000..811b015
--- /dev/null
@@ -0,0 +1,3818 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2008 Chris Wilson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Chris Wilson.
+ *
+ * Contributor(s):
+ *      Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+/* The script surface is one that records all operations performed on
+ * it in the form of a procedural script, similar in fashion to
+ * PostScript but using Cairo's imaging model. In essence, this is
+ * equivalent to the recording-surface, but as there is no impedance mismatch
+ * between Cairo and CairoScript, we can generate output immediately
+ * without having to copy and hold the data in memory.
+ */
+
+#include "cairoint.h"
+
+#include "cairo-script.h"
+#include "cairo-script-private.h"
+
+#include "cairo-analysis-surface-private.h"
+#include "cairo-default-context-private.h"
+#include "cairo-device-private.h"
+#include "cairo-error-private.h"
+#include "cairo-list-private.h"
+#include "cairo-image-surface-private.h"
+#include "cairo-recording-surface-private.h"
+#include "cairo-output-stream-private.h"
+#include "cairo-scaled-font-private.h"
+#include "cairo-surface-clipper-private.h"
+#include "cairo-surface-snapshot-private.h"
+#include "cairo-surface-subsurface-private.h"
+#include "cairo-surface-wrapper-private.h"
+
+#if CAIRO_HAS_FT_FONT
+#include "cairo-ft-private.h"
+#endif
+
+#include <ctype.h>
+
+#ifdef WORDS_BIGENDIAN
+#define to_be32(x) x
+#else
+#define to_be32(x) bswap_32(x)
+#endif
+
+#define _cairo_output_stream_puts(S, STR) \
+    _cairo_output_stream_write ((S), (STR), strlen (STR))
+
+#define static cairo_warn static
+
+typedef struct _cairo_script_context cairo_script_context_t;
+typedef struct _cairo_script_surface cairo_script_surface_t;
+typedef struct _cairo_script_implicit_context cairo_script_implicit_context_t;
+typedef struct _cairo_script_font cairo_script_font_t;
+
+typedef struct _operand {
+    enum {
+       SURFACE,
+       DEFERRED,
+    } type;
+    cairo_list_t link;
+} operand_t;
+
+
+struct deferred_finish {
+    cairo_list_t link;
+    operand_t operand;
+};
+
+struct _cairo_script_context {
+    cairo_device_t base;
+
+    int active;
+    int attach_snapshots;
+
+    cairo_bool_t owns_stream;
+    cairo_output_stream_t *stream;
+    cairo_script_mode_t mode;
+
+    struct _bitmap {
+       unsigned long min;
+       unsigned long count;
+       unsigned int map[64];
+       struct _bitmap *next;
+    } surface_id, font_id;
+
+    cairo_list_t operands;
+    cairo_list_t deferred;
+
+    cairo_list_t fonts;
+    cairo_list_t defines;
+};
+
+struct _cairo_script_font {
+    cairo_scaled_font_private_t base;
+
+    cairo_bool_t has_sfnt;
+    unsigned long id;
+    unsigned long subset_glyph_index;
+    cairo_list_t link;
+    cairo_scaled_font_t *parent;
+};
+
+struct _cairo_script_implicit_context {
+    cairo_operator_t current_operator;
+    cairo_fill_rule_t current_fill_rule;
+    double current_tolerance;
+    cairo_antialias_t current_antialias;
+    cairo_stroke_style_t current_style;
+    cairo_pattern_union_t current_source;
+    cairo_matrix_t current_ctm;
+    cairo_matrix_t current_stroke_matrix;
+    cairo_matrix_t current_font_matrix;
+    cairo_font_options_t current_font_options;
+    cairo_scaled_font_t *current_scaled_font;
+    cairo_path_fixed_t current_path;
+    cairo_bool_t has_clip;
+};
+
+struct _cairo_script_surface {
+    cairo_surface_t base;
+
+    cairo_surface_wrapper_t wrapper;
+
+    cairo_surface_clipper_t clipper;
+
+    operand_t operand;
+    cairo_bool_t emitted;
+    cairo_bool_t defined;
+    cairo_bool_t active;
+
+    double width, height;
+
+    /* implicit flattened context */
+    cairo_script_implicit_context_t cr;
+};
+
+static const cairo_surface_backend_t _cairo_script_surface_backend;
+
+static cairo_script_surface_t *
+_cairo_script_surface_create_internal (cairo_script_context_t *ctx,
+                                      cairo_content_t content,
+                                      cairo_rectangle_t *extents,
+                                      cairo_surface_t *passthrough);
+
+static void
+_cairo_script_scaled_font_fini (cairo_scaled_font_private_t *abstract_private,
+                               cairo_scaled_font_t *scaled_font);
+
+static void
+_cairo_script_implicit_context_init (cairo_script_implicit_context_t *cr);
+
+static void
+_cairo_script_implicit_context_reset (cairo_script_implicit_context_t *cr);
+
+static void
+_bitmap_release_id (struct _bitmap *b, unsigned long token)
+{
+    struct _bitmap **prev = NULL;
+
+    do {
+       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;
+    } while (b != NULL);
+}
+
+static cairo_status_t
+_bitmap_next_id (struct _bitmap *b,
+                unsigned long *id)
+{
+    struct _bitmap *bb, **prev = NULL;
+    unsigned long min = 0;
+
+    do {
+       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++;
+                       *id = n * sizeof (b->map[0])*CHAR_BIT + m + b->min;
+                       return CAIRO_STATUS_SUCCESS;
+                   }
+               }
+           }
+       }
+       min += sizeof (b->map) * CHAR_BIT;
+
+       prev = &b->next;
+       b = b->next;
+    } while (b != NULL);
+
+    bb = malloc (sizeof (struct _bitmap));
+    if (unlikely (bb == NULL))
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    *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]));
+    *id = min;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_bitmap_fini (struct _bitmap *b)
+{
+    while (b != NULL) {
+       struct _bitmap *next = b->next;
+       free (b);
+       b = next;
+    }
+}
+
+static const char *
+_direction_to_string (cairo_bool_t backward)
+{
+    static const char *names[] = {
+       "FORWARD",
+       "BACKWARD"
+    };
+    assert (backward < ARRAY_LENGTH (names));
+    return names[backward];
+}
+
+static const char *
+_operator_to_string (cairo_operator_t op)
+{
+    static const char *names[] = {
+       "CLEAR",        /* CAIRO_OPERATOR_CLEAR */
+
+       "SOURCE",       /* CAIRO_OPERATOR_SOURCE */
+       "OVER",         /* CAIRO_OPERATOR_OVER */
+       "IN",           /* CAIRO_OPERATOR_IN */
+       "OUT",          /* CAIRO_OPERATOR_OUT */
+       "ATOP",         /* CAIRO_OPERATOR_ATOP */
+
+       "DEST",         /* CAIRO_OPERATOR_DEST */
+       "DEST_OVER",    /* CAIRO_OPERATOR_DEST_OVER */
+       "DEST_IN",      /* CAIRO_OPERATOR_DEST_IN */
+       "DEST_OUT",     /* CAIRO_OPERATOR_DEST_OUT */
+       "DEST_ATOP",    /* CAIRO_OPERATOR_DEST_ATOP */
+
+       "XOR",          /* CAIRO_OPERATOR_XOR */
+       "ADD",          /* CAIRO_OPERATOR_ADD */
+       "SATURATE",     /* CAIRO_OPERATOR_SATURATE */
+
+       "MULTIPLY",     /* CAIRO_OPERATOR_MULTIPLY */
+       "SCREEN",       /* CAIRO_OPERATOR_SCREEN */
+       "OVERLAY",      /* CAIRO_OPERATOR_OVERLAY */
+       "DARKEN",       /* CAIRO_OPERATOR_DARKEN */
+       "LIGHTEN",      /* CAIRO_OPERATOR_LIGHTEN */
+       "DODGE",        /* CAIRO_OPERATOR_COLOR_DODGE */
+       "BURN",         /* CAIRO_OPERATOR_COLOR_BURN */
+       "HARD_LIGHT",   /* CAIRO_OPERATOR_HARD_LIGHT */
+       "SOFT_LIGHT",   /* CAIRO_OPERATOR_SOFT_LIGHT */
+       "DIFFERENCE",   /* CAIRO_OPERATOR_DIFFERENCE */
+       "EXCLUSION",    /* CAIRO_OPERATOR_EXCLUSION */
+       "HSL_HUE",      /* CAIRO_OPERATOR_HSL_HUE */
+       "HSL_SATURATION", /* CAIRO_OPERATOR_HSL_SATURATION */
+       "HSL_COLOR",    /* CAIRO_OPERATOR_HSL_COLOR */
+       "HSL_LUMINOSITY" /* CAIRO_OPERATOR_HSL_LUMINOSITY */
+    };
+    assert (op < ARRAY_LENGTH (names));
+    return names[op];
+}
+
+static const char *
+_extend_to_string (cairo_extend_t extend)
+{
+    static const char *names[] = {
+       "EXTEND_NONE",          /* CAIRO_EXTEND_NONE */
+       "EXTEND_REPEAT",        /* CAIRO_EXTEND_REPEAT */
+       "EXTEND_REFLECT",       /* CAIRO_EXTEND_REFLECT */
+       "EXTEND_PAD"            /* CAIRO_EXTEND_PAD */
+    };
+    assert (extend < ARRAY_LENGTH (names));
+    return names[extend];
+}
+
+static const char *
+_filter_to_string (cairo_filter_t filter)
+{
+    static const char *names[] = {
+       "FILTER_FAST",          /* CAIRO_FILTER_FAST */
+       "FILTER_GOOD",          /* CAIRO_FILTER_GOOD */
+       "FILTER_BEST",          /* CAIRO_FILTER_BEST */
+       "FILTER_NEAREST",       /* CAIRO_FILTER_NEAREST */
+       "FILTER_BILINEAR",      /* CAIRO_FILTER_BILINEAR */
+       "FILTER_GAUSSIAN",      /* CAIRO_FILTER_GAUSSIAN */
+    };
+    assert (filter < ARRAY_LENGTH (names));
+    return names[filter];
+}
+
+static const char *
+_fill_rule_to_string (cairo_fill_rule_t rule)
+{
+    static const char *names[] = {
+       "WINDING",      /* CAIRO_FILL_RULE_WINDING */
+       "EVEN_ODD"      /* CAIRO_FILL_RILE_EVEN_ODD */
+    };
+    assert (rule < ARRAY_LENGTH (names));
+    return names[rule];
+}
+
+static const char *
+_antialias_to_string (cairo_antialias_t antialias)
+{
+    static const char *names[] = {
+       "ANTIALIAS_DEFAULT",    /* CAIRO_ANTIALIAS_DEFAULT */
+       "ANTIALIAS_NONE",       /* CAIRO_ANTIALIAS_NONE */
+       "ANTIALIAS_GRAY",       /* CAIRO_ANTIALIAS_GRAY */
+       "ANTIALIAS_SUBPIXEL",   /* CAIRO_ANTIALIAS_SUBPIXEL */
+       "ANTIALIAS_FAST",       /* CAIRO_ANTIALIAS_FAST */
+       "ANTIALIAS_GOOD",       /* CAIRO_ANTIALIAS_GOOD */
+       "ANTIALIAS_BEST"        /* CAIRO_ANTIALIAS_BEST */
+    };
+    assert (antialias < ARRAY_LENGTH (names));
+    return names[antialias];
+}
+
+static const char *
+_line_cap_to_string (cairo_line_cap_t line_cap)
+{
+    static const char *names[] = {
+       "LINE_CAP_BUTT",        /* CAIRO_LINE_CAP_BUTT */
+       "LINE_CAP_ROUND",       /* CAIRO_LINE_CAP_ROUND */
+       "LINE_CAP_SQUARE"       /* CAIRO_LINE_CAP_SQUARE */
+    };
+    assert (line_cap < ARRAY_LENGTH (names));
+    return names[line_cap];
+}
+
+static const char *
+_line_join_to_string (cairo_line_join_t line_join)
+{
+    static const char *names[] = {
+       "LINE_JOIN_MITER",      /* CAIRO_LINE_JOIN_MITER */
+       "LINE_JOIN_ROUND",      /* CAIRO_LINE_JOIN_ROUND */
+       "LINE_JOIN_BEVEL",      /* CAIRO_LINE_JOIN_BEVEL */
+    };
+    assert (line_join < ARRAY_LENGTH (names));
+    return names[line_join];
+}
+
+static inline cairo_script_context_t *
+to_context (cairo_script_surface_t *surface)
+{
+    return (cairo_script_context_t *) surface->base.device;
+}
+
+static cairo_bool_t
+target_is_active (cairo_script_surface_t *surface)
+{
+    return cairo_list_is_first (&surface->operand.link,
+                               &to_context (surface)->operands);
+}
+
+static void
+target_push (cairo_script_surface_t *surface)
+{
+    cairo_list_move (&surface->operand.link, &to_context (surface)->operands);
+}
+
+static int
+target_depth (cairo_script_surface_t *surface)
+{
+    cairo_list_t *link;
+    int depth = 0;
+
+    cairo_list_foreach (link, &to_context (surface)->operands) {
+       if (link == &surface->operand.link)
+           break;
+       depth++;
+    }
+
+    return depth;
+}
+
+static void
+_get_target (cairo_script_surface_t *surface)
+{
+    cairo_script_context_t *ctx = to_context (surface);
+
+    if (target_is_active (surface)) {
+       _cairo_output_stream_puts (ctx->stream, "dup ");
+       return;
+    }
+
+    if (surface->defined) {
+       _cairo_output_stream_printf (ctx->stream, "s%u ",
+                                    surface->base.unique_id);
+    } else {
+       int depth = target_depth (surface);
+
+       assert (! cairo_list_is_empty (&surface->operand.link));
+       assert (! target_is_active (surface));
+
+       if (ctx->active) {
+           _cairo_output_stream_printf (ctx->stream, "%d index ", depth);
+           _cairo_output_stream_puts (ctx->stream, "/target get exch pop ");
+       } else {
+           if (depth == 1) {
+               _cairo_output_stream_puts (ctx->stream, "exch ");
+           } else {
+               _cairo_output_stream_printf (ctx->stream,
+                                            "%d -1 roll ", depth);
+           }
+           target_push (surface);
+           _cairo_output_stream_puts (ctx->stream, "dup ");
+       }
+    }
+}
+
+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";
+    }
+}
+
+static cairo_status_t
+_emit_surface (cairo_script_surface_t *surface)
+{
+    cairo_script_context_t *ctx = to_context (surface);
+
+    _cairo_output_stream_printf (ctx->stream,
+                                "<< /content //%s",
+                                _content_to_string (surface->base.content));
+    if (surface->width != -1 && surface->height != -1) {
+       _cairo_output_stream_printf (ctx->stream,
+                                    " /width %f /height %f",
+                                    surface->width,
+                                    surface->height);
+    }
+
+    if (surface->base.x_fallback_resolution !=
+       CAIRO_SURFACE_FALLBACK_RESOLUTION_DEFAULT ||
+       surface->base.y_fallback_resolution !=
+       CAIRO_SURFACE_FALLBACK_RESOLUTION_DEFAULT)
+    {
+       _cairo_output_stream_printf (ctx->stream,
+                                    " /fallback-resolution [%f %f]",
+                                    surface->base.x_fallback_resolution,
+                                    surface->base.y_fallback_resolution);
+    }
+
+    if (surface->base.device_transform.x0 != 0. ||
+       surface->base.device_transform.y0 != 0.)
+    {
+       /* XXX device offset is encoded into the pattern matrices etc. */
+       if (0) {
+       _cairo_output_stream_printf (ctx->stream,
+                                    " /device-offset [%f %f]",
+                                    surface->base.device_transform.x0,
+                                    surface->base.device_transform.y0);
+       }
+    }
+
+    _cairo_output_stream_puts (ctx->stream, " >> surface context\n");
+    surface->emitted = TRUE;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_emit_context (cairo_script_surface_t *surface)
+{
+    cairo_script_context_t *ctx = to_context (surface);
+
+    if (target_is_active (surface))
+       return CAIRO_STATUS_SUCCESS;
+
+    while (! cairo_list_is_empty (&ctx->operands)) {
+       operand_t *op;
+       cairo_script_surface_t *old;
+
+       op = cairo_list_first_entry (&ctx->operands,
+                                    operand_t,
+                                    link);
+       if (op->type == DEFERRED)
+           break;
+
+       old = cairo_container_of (op, cairo_script_surface_t, operand);
+       if (old == surface)
+           break;
+       if (old->active)
+           break;
+
+       if (! old->defined) {
+           assert (old->emitted);
+           _cairo_output_stream_printf (ctx->stream,
+                                        "/target get /s%u exch def pop\n",
+                                        old->base.unique_id);
+           old->defined = TRUE;
+       } else {
+           _cairo_output_stream_puts (ctx->stream, "pop\n");
+       }
+
+       cairo_list_del (&old->operand.link);
+    }
+
+    if (target_is_active (surface))
+       return CAIRO_STATUS_SUCCESS;
+
+    if (! surface->emitted) {
+       cairo_status_t status;
+
+       status = _emit_surface (surface);
+       if (unlikely (status))
+           return status;
+    } else if (cairo_list_is_empty (&surface->operand.link)) {
+       assert (surface->defined);
+       _cairo_output_stream_printf (ctx->stream,
+                                    "s%u context\n",
+                                    surface->base.unique_id);
+       _cairo_script_implicit_context_reset (&surface->cr);
+       _cairo_surface_clipper_reset (&surface->clipper);
+    } else {
+       int depth = target_depth (surface);
+       if (depth == 1) {
+           _cairo_output_stream_puts (ctx->stream, "exch\n");
+       } else {
+           _cairo_output_stream_printf (ctx->stream,
+                                        "%d -1 roll\n",
+                                        depth);
+       }
+    }
+    target_push (surface);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_emit_operator (cairo_script_surface_t *surface,
+               cairo_operator_t op)
+{
+    assert (target_is_active (surface));
+
+    if (surface->cr.current_operator == op)
+       return CAIRO_STATUS_SUCCESS;
+
+    surface->cr.current_operator = op;
+
+    _cairo_output_stream_printf (to_context (surface)->stream,
+                                "//%s set-operator\n",
+                                _operator_to_string (op));
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_emit_fill_rule (cairo_script_surface_t *surface,
+                cairo_fill_rule_t fill_rule)
+{
+    assert (target_is_active (surface));
+
+    if (surface->cr.current_fill_rule == fill_rule)
+       return CAIRO_STATUS_SUCCESS;
+
+    surface->cr.current_fill_rule = fill_rule;
+
+    _cairo_output_stream_printf (to_context (surface)->stream,
+                                "//%s set-fill-rule\n",
+                                _fill_rule_to_string (fill_rule));
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_emit_tolerance (cairo_script_surface_t *surface,
+                double tolerance,
+                cairo_bool_t force)
+{
+    assert (target_is_active (surface));
+
+    if ((! force ||
+        fabs (tolerance - CAIRO_GSTATE_TOLERANCE_DEFAULT) < 1e-5) &&
+       surface->cr.current_tolerance == tolerance)
+    {
+       return CAIRO_STATUS_SUCCESS;
+    }
+
+    surface->cr.current_tolerance = tolerance;
+
+    _cairo_output_stream_printf (to_context (surface)->stream,
+                                "%f set-tolerance\n",
+                                tolerance);
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_emit_antialias (cairo_script_surface_t *surface,
+                cairo_antialias_t antialias)
+{
+    assert (target_is_active (surface));
+
+    if (surface->cr.current_antialias == antialias)
+       return CAIRO_STATUS_SUCCESS;
+
+    surface->cr.current_antialias = antialias;
+
+    _cairo_output_stream_printf (to_context (surface)->stream,
+                                "//%s set-antialias\n",
+                                _antialias_to_string (antialias));
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_emit_line_width (cairo_script_surface_t *surface,
+                double line_width,
+                cairo_bool_t force)
+{
+    assert (target_is_active (surface));
+
+    if ((! force ||
+        fabs (line_width - CAIRO_GSTATE_LINE_WIDTH_DEFAULT) < 1e-5) &&
+       surface->cr.current_style.line_width == line_width)
+    {
+       return CAIRO_STATUS_SUCCESS;
+    }
+
+    surface->cr.current_style.line_width = line_width;
+
+    _cairo_output_stream_printf (to_context (surface)->stream,
+                                "%f set-line-width\n",
+                                line_width);
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_emit_line_cap (cairo_script_surface_t *surface,
+               cairo_line_cap_t line_cap)
+{
+    assert (target_is_active (surface));
+
+    if (surface->cr.current_style.line_cap == line_cap)
+       return CAIRO_STATUS_SUCCESS;
+
+    surface->cr.current_style.line_cap = line_cap;
+
+    _cairo_output_stream_printf (to_context (surface)->stream,
+                                "//%s set-line-cap\n",
+                                _line_cap_to_string (line_cap));
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_emit_line_join (cairo_script_surface_t *surface,
+                cairo_line_join_t line_join)
+{
+    assert (target_is_active (surface));
+
+    if (surface->cr.current_style.line_join == line_join)
+       return CAIRO_STATUS_SUCCESS;
+
+    surface->cr.current_style.line_join = line_join;
+
+    _cairo_output_stream_printf (to_context (surface)->stream,
+                                "//%s set-line-join\n",
+                                _line_join_to_string (line_join));
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_emit_miter_limit (cairo_script_surface_t *surface,
+                  double miter_limit,
+                  cairo_bool_t force)
+{
+    assert (target_is_active (surface));
+
+    if ((! force ||
+        fabs (miter_limit - CAIRO_GSTATE_MITER_LIMIT_DEFAULT) < 1e-5) &&
+       surface->cr.current_style.miter_limit == miter_limit)
+    {
+       return CAIRO_STATUS_SUCCESS;
+    }
+
+    surface->cr.current_style.miter_limit = miter_limit;
+
+    _cairo_output_stream_printf (to_context (surface)->stream,
+                                "%f set-miter-limit\n",
+                                miter_limit);
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_bool_t
+_dashes_equal (const double *a, const double *b, int num_dashes)
+{
+    while (num_dashes--) {
+       if (fabs (*a - *b) > 1e-5)
+           return FALSE;
+       a++, b++;
+    }
+
+    return TRUE;
+}
+
+static cairo_status_t
+_emit_dash (cairo_script_surface_t *surface,
+           const double *dash,
+           unsigned int num_dashes,
+           double offset,
+           cairo_bool_t force)
+{
+    unsigned int n;
+
+    assert (target_is_active (surface));
+
+    if (force &&
+       num_dashes == 0 &&
+       surface->cr.current_style.num_dashes == 0)
+    {
+       return CAIRO_STATUS_SUCCESS;
+    }
+
+    if (! force &&
+       (surface->cr.current_style.num_dashes == num_dashes &&
+        (num_dashes == 0 ||
+         (fabs (surface->cr.current_style.dash_offset - offset) < 1e-5 &&
+          _dashes_equal (surface->cr.current_style.dash, dash, num_dashes)))))
+    {
+       return CAIRO_STATUS_SUCCESS;
+    }
+
+
+    if (num_dashes) {
+       surface->cr.current_style.dash = _cairo_realloc_ab
+           (surface->cr.current_style.dash, num_dashes, sizeof (double));
+       if (unlikely (surface->cr.current_style.dash == NULL))
+           return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+       memcpy (surface->cr.current_style.dash, dash,
+               sizeof (double) * num_dashes);
+    } else {
+       free (surface->cr.current_style.dash);
+       surface->cr.current_style.dash = NULL;
+    }
+
+    surface->cr.current_style.num_dashes = num_dashes;
+    surface->cr.current_style.dash_offset = offset;
+
+    _cairo_output_stream_puts (to_context (surface)->stream, "[");
+    for (n = 0; n < num_dashes; n++) {
+       _cairo_output_stream_printf (to_context (surface)->stream, "%f", dash[n]);
+       if (n < num_dashes-1)
+           _cairo_output_stream_puts (to_context (surface)->stream, " ");
+    }
+    _cairo_output_stream_printf (to_context (surface)->stream,
+                                "] %f set-dash\n",
+                                offset);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_emit_stroke_style (cairo_script_surface_t *surface,
+                   const cairo_stroke_style_t *style,
+                   cairo_bool_t force)
+{
+    cairo_status_t status;
+
+    assert (target_is_active (surface));
+
+    status = _emit_line_width (surface, style->line_width, force);
+    if (unlikely (status))
+       return status;
+
+    status = _emit_line_cap (surface, style->line_cap);
+    if (unlikely (status))
+       return status;
+
+    status = _emit_line_join (surface, style->line_join);
+    if (unlikely (status))
+       return status;
+
+    status = _emit_miter_limit (surface, style->miter_limit, force);
+    if (unlikely (status))
+       return status;
+
+    status = _emit_dash (surface,
+                        style->dash, style->num_dashes, style->dash_offset,
+                        force);
+    if (unlikely (status))
+       return status;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static const char *
+_format_to_string (cairo_format_t format)
+{
+    switch (format) {
+    case CAIRO_FORMAT_ARGB32:  return "ARGB32";
+    case CAIRO_FORMAT_RGB30:   return "RGB30";
+    case CAIRO_FORMAT_RGB24:   return "RGB24";
+    case CAIRO_FORMAT_RGB16_565: return "RGB16_565";
+    case CAIRO_FORMAT_A8:      return "A8";
+    case CAIRO_FORMAT_A1:      return "A1";
+    case CAIRO_FORMAT_INVALID: return "INVALID";
+    }
+    ASSERT_NOT_REACHED;
+    return "INVALID";
+}
+
+static cairo_status_t
+_emit_solid_pattern (cairo_script_surface_t *surface,
+                    const cairo_pattern_t *pattern)
+{
+    cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) pattern;
+    cairo_script_context_t *ctx = to_context (surface);
+
+    if (! CAIRO_COLOR_IS_OPAQUE (&solid->color))
+    {
+       if (! (surface->base.content & CAIRO_CONTENT_COLOR) ||
+           ((solid->color.red_short   == 0 || solid->color.red_short   == 0xffff) &&
+            (solid->color.green_short == 0 || solid->color.green_short == 0xffff) &&
+            (solid->color.blue_short  == 0 || solid->color.blue_short  == 0xffff) ))
+       {
+           _cairo_output_stream_printf (ctx->stream,
+                                        "%f a",
+                                        solid->color.alpha);
+       }
+       else
+       {
+           _cairo_output_stream_printf (ctx->stream,
+                                        "%f %f %f %f rgba",
+                                        solid->color.red,
+                                        solid->color.green,
+                                        solid->color.blue,
+                                        solid->color.alpha);
+       }
+    }
+    else
+    {
+       if (solid->color.red_short == solid->color.green_short &&
+           solid->color.red_short == solid->color.blue_short)
+       {
+           _cairo_output_stream_printf (ctx->stream,
+                                        "%f g",
+                                        solid->color.red);
+       }
+       else
+       {
+           _cairo_output_stream_printf (ctx->stream,
+                                        "%f %f %f rgb",
+                                        solid->color.red,
+                                        solid->color.green,
+                                        solid->color.blue);
+       }
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+
+static cairo_status_t
+_emit_gradient_color_stops (cairo_gradient_pattern_t *gradient,
+                           cairo_output_stream_t *output)
+{
+    unsigned int n;
+
+    for (n = 0; n < gradient->n_stops; n++) {
+       _cairo_output_stream_printf (output,
+                                    "\n  %f %f %f %f %f add-color-stop",
+                                    gradient->stops[n].offset,
+                                    gradient->stops[n].color.red,
+                                    gradient->stops[n].color.green,
+                                    gradient->stops[n].color.blue,
+                                    gradient->stops[n].color.alpha);
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_emit_linear_pattern (cairo_script_surface_t *surface,
+                     const cairo_pattern_t *pattern)
+{
+    cairo_script_context_t *ctx = to_context (surface);
+    cairo_linear_pattern_t *linear;
+
+    linear = (cairo_linear_pattern_t *) pattern;
+
+    _cairo_output_stream_printf (ctx->stream,
+                                "%f %f %f %f linear",
+                                linear->pd1.x, linear->pd1.y,
+                                linear->pd2.x, linear->pd2.y);
+    return _emit_gradient_color_stops (&linear->base, ctx->stream);
+}
+
+static cairo_status_t
+_emit_radial_pattern (cairo_script_surface_t *surface,
+                     const cairo_pattern_t *pattern)
+{
+    cairo_script_context_t *ctx = to_context (surface);
+    cairo_radial_pattern_t *radial;
+
+    radial = (cairo_radial_pattern_t *) pattern;
+
+    _cairo_output_stream_printf (ctx->stream,
+                                "%f %f %f %f %f %f radial",
+                                radial->cd1.center.x,
+                                radial->cd1.center.y,
+                                radial->cd1.radius,
+                                radial->cd2.center.x,
+                                radial->cd2.center.y,
+                                radial->cd2.radius);
+    return _emit_gradient_color_stops (&radial->base, ctx->stream);
+}
+
+static cairo_status_t
+_emit_mesh_pattern (cairo_script_surface_t *surface,
+                   const cairo_pattern_t *pattern)
+{
+    cairo_script_context_t *ctx = to_context (surface);
+    cairo_pattern_t *mesh;
+    cairo_status_t status;
+    unsigned int i, n;
+
+    mesh = (cairo_pattern_t *) pattern;
+    status = cairo_mesh_pattern_get_patch_count (mesh, &n);
+    if (unlikely (status))
+       return status;
+
+    _cairo_output_stream_printf (ctx->stream, "mesh");
+    for (i = 0; i < n; i++) {
+       cairo_path_t *path;
+       cairo_path_data_t *data;
+       int j;
+
+       _cairo_output_stream_printf (ctx->stream, "\n  mesh-begin-patch");
+
+       path = cairo_mesh_pattern_get_path (mesh, i);
+       if (unlikely (path->status))
+           return path->status;
+
+       for (j = 0; j < path->num_data; j+=data[0].header.length) {
+           data = &path->data[j];
+           switch (data->header.type) {
+           case CAIRO_PATH_MOVE_TO:
+               _cairo_output_stream_printf (ctx->stream,
+                                            "\n  %f %f mesh-move-to",
+                                            data[1].point.x, data[1].point.y);
+               break;
+           case CAIRO_PATH_LINE_TO:
+               _cairo_output_stream_printf (ctx->stream,
+                                            "\n  %f %f mesh-line-to",
+                                            data[1].point.x, data[1].point.y);
+               break;
+           case CAIRO_PATH_CURVE_TO:
+               _cairo_output_stream_printf (ctx->stream,
+                                            "\n  %f %f %f %f %f %f mesh-curve-to",
+                                            data[1].point.x, data[1].point.y,
+                                            data[2].point.x, data[2].point.y,
+                                            data[3].point.x, data[3].point.y);
+               break;
+           case CAIRO_PATH_CLOSE_PATH:
+               break;
+           }
+       }
+       cairo_path_destroy (path);
+
+       for (j = 0; j < 4; j++) {
+           double x, y;
+
+           status = cairo_mesh_pattern_get_control_point (mesh, i, j, &x, &y);
+           if (unlikely (status))
+               return status;
+           _cairo_output_stream_printf (ctx->stream,
+                                        "\n  %d %f %f mesh-set-control-point",
+                                        j, x, y);
+       }
+
+       for (j = 0; j < 4; j++) {
+           double r, g, b, a;
+
+           status = cairo_mesh_pattern_get_corner_color_rgba (mesh, i, j, &r, &g, &b, &a);
+           if (unlikely (status))
+               return status;
+
+           _cairo_output_stream_printf (ctx->stream,
+                                        "\n  %d %f %f %f %f mesh-set-corner-color",
+                                        j, r, g, b, a);
+       }
+
+       _cairo_output_stream_printf (ctx->stream, "\n  mesh-end-patch");
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+struct script_snapshot {
+    cairo_surface_t base;
+};
+
+static cairo_status_t
+script_snapshot_finish (void *abstract_surface)
+{
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static const cairo_surface_backend_t script_snapshot_backend = {
+    CAIRO_SURFACE_TYPE_SCRIPT,
+    script_snapshot_finish,
+};
+
+static void
+detach_snapshot (cairo_surface_t *abstract_surface)
+{
+    cairo_script_surface_t *surface = (cairo_script_surface_t *)abstract_surface;
+    cairo_script_context_t *ctx = to_context (surface);
+
+    _cairo_output_stream_printf (ctx->stream,
+                                "/s%d undef\n",
+                                surface->base.unique_id);
+}
+
+static void
+attach_snapshot (cairo_script_context_t *ctx,
+                cairo_surface_t *source)
+{
+    struct script_snapshot *surface;
+
+    if (! ctx->attach_snapshots)
+       return;
+
+    surface = malloc (sizeof (*surface));
+    if (unlikely (surface == NULL))
+       return;
+
+    _cairo_surface_init (&surface->base,
+                        &script_snapshot_backend,
+                        &ctx->base,
+                        source->content);
+
+    _cairo_output_stream_printf (ctx->stream,
+                                "dup /s%d exch def ",
+                                surface->base.unique_id);
+
+    _cairo_surface_attach_snapshot (source, &surface->base, detach_snapshot);
+    cairo_surface_destroy (&surface->base);
+}
+
+static cairo_status_t
+_emit_recording_surface_pattern (cairo_script_surface_t *surface,
+                                cairo_recording_surface_t *source)
+{
+    cairo_script_implicit_context_t old_cr;
+    cairo_script_context_t *ctx = to_context (surface);
+    cairo_script_surface_t *similar;
+    cairo_surface_t *snapshot;
+    cairo_rectangle_t r, *extents;
+    cairo_status_t status;
+
+    snapshot = _cairo_surface_has_snapshot (&source->base, &script_snapshot_backend);
+    if (snapshot) {
+       _cairo_output_stream_printf (ctx->stream, "s%d", snapshot->unique_id);
+       return CAIRO_INT_STATUS_SUCCESS;
+    }
+
+    extents = NULL;
+    if (_cairo_recording_surface_get_bounds (&source->base, &r))
+       extents = &r;
+
+    similar = _cairo_script_surface_create_internal (ctx,
+                                                    source->base.content,
+                                                    extents,
+                                                    NULL);
+    if (unlikely (similar->base.status))
+       return similar->base.status;
+
+    similar->base.is_clear = TRUE;
+
+    _cairo_output_stream_printf (ctx->stream, "//%s ",
+                                _content_to_string (source->base.content));
+    if (extents) {
+       _cairo_output_stream_printf (ctx->stream, "[%f %f %f %f]",
+                                    extents->x, extents->y,
+                                    extents->width, extents->height);
+    } else
+       _cairo_output_stream_puts (ctx->stream, "[]");
+    _cairo_output_stream_puts (ctx->stream, " record\n");
+
+    attach_snapshot (ctx, &source->base);
+
+    _cairo_output_stream_puts (ctx->stream, "dup context\n");
+
+    target_push (similar);
+    similar->emitted = TRUE;
+
+
+    old_cr = surface->cr;
+    _cairo_script_implicit_context_init (&surface->cr);
+    status = _cairo_recording_surface_replay (&source->base, &similar->base);
+    surface->cr = old_cr;
+
+    if (unlikely (status)) {
+       cairo_surface_destroy (&similar->base);
+       return status;
+    }
+
+    cairo_list_del (&similar->operand.link);
+    assert (target_is_active (surface));
+
+    _cairo_output_stream_puts (ctx->stream, "pop ");
+    cairo_surface_destroy (&similar->base);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_emit_script_surface_pattern (cairo_script_surface_t *surface,
+                             cairo_script_surface_t *source)
+{
+    _get_target (source);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_write_image_surface (cairo_output_stream_t *output,
+                     const cairo_image_surface_t *image)
+{
+    int stride, row, width;
+    uint8_t row_stack[CAIRO_STACK_BUFFER_SIZE];
+    uint8_t *rowdata;
+    uint8_t *data;
+
+    stride = image->stride;
+    width = image->width;
+    data = image->data;
+#if WORDS_BIGENDIAN
+    switch (image->format) {
+    case CAIRO_FORMAT_A1:
+       for (row = image->height; row--; ) {
+           _cairo_output_stream_write (output, data, (width+7)/8);
+           data += stride;
+       }
+       break;
+    case CAIRO_FORMAT_A8:
+       for (row = image->height; row--; ) {
+           _cairo_output_stream_write (output, data, width);
+           data += stride;
+       }
+       break;
+    case CAIRO_FORMAT_RGB16_565:
+       for (row = image->height; row--; ) {
+           _cairo_output_stream_write (output, data, 2*width);
+           data += stride;
+       }
+       break;
+    case CAIRO_FORMAT_RGB24:
+       for (row = image->height; row--; ) {
+           int col;
+           rowdata = data;
+           for (col = width; col--; ) {
+               _cairo_output_stream_write (output, rowdata, 3);
+               rowdata+=4;
+           }
+           data += stride;
+       }
+       break;
+    case CAIRO_FORMAT_ARGB32:
+       for (row = image->height; row--; ) {
+           _cairo_output_stream_write (output, data, 4*width);
+           data += stride;
+       }
+       break;
+    case CAIRO_FORMAT_INVALID:
+    default:
+       ASSERT_NOT_REACHED;
+       break;
+    }
+#else
+    if (stride > ARRAY_LENGTH (row_stack)) {
+       rowdata = malloc (stride);
+       if (unlikely (rowdata == NULL))
+           return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+    } else
+       rowdata = row_stack;
+
+    switch (image->format) {
+    case CAIRO_FORMAT_A1:
+       for (row = image->height; row--; ) {
+           int col;
+           for (col = 0; col < (width + 7)/8; col++)
+               rowdata[col] = CAIRO_BITSWAP8 (data[col]);
+           _cairo_output_stream_write (output, rowdata, (width+7)/8);
+           data += stride;
+       }
+       break;
+    case CAIRO_FORMAT_A8:
+       for (row = image->height; row--; ) {
+           _cairo_output_stream_write (output, data, width);
+           data += stride;
+       }
+       break;
+    case CAIRO_FORMAT_RGB16_565:
+       for (row = image->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]);
+           _cairo_output_stream_write (output, rowdata, 2*width);
+           data += stride;
+       }
+       break;
+    case CAIRO_FORMAT_RGB24:
+       for (row = image->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++;
+           }
+           _cairo_output_stream_write (output, rowdata, 3*width);
+           data += stride;
+       }
+       break;
+    case CAIRO_FORMAT_RGB30:
+    case CAIRO_FORMAT_ARGB32:
+       for (row = image->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]);
+           _cairo_output_stream_write (output, rowdata, 4*width);
+           data += stride;
+       }
+       break;
+    case CAIRO_FORMAT_INVALID:
+    default:
+       ASSERT_NOT_REACHED;
+       break;
+    }
+    if (rowdata != row_stack)
+       free (rowdata);
+#endif
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_emit_png_surface (cairo_script_surface_t *surface,
+                  cairo_image_surface_t *image)
+{
+    cairo_script_context_t *ctx = to_context (surface);
+    cairo_output_stream_t *base85_stream;
+    cairo_status_t status;
+    const uint8_t *mime_data;
+    unsigned long mime_data_length;
+
+    cairo_surface_get_mime_data (&image->base, CAIRO_MIME_TYPE_PNG,
+                                &mime_data, &mime_data_length);
+    if (mime_data == NULL)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    _cairo_output_stream_printf (ctx->stream,
+                                "<< "
+                                "/width %d "
+                                "/height %d "
+                                "/format //%s "
+                                "/mime-type (image/png) "
+                                "/source <~",
+                                image->width, image->height,
+                                _format_to_string (image->format));
+
+    base85_stream = _cairo_base85_stream_create (ctx->stream);
+    _cairo_output_stream_write (base85_stream, mime_data, mime_data_length);
+    status = _cairo_output_stream_destroy (base85_stream);
+    if (unlikely (status))
+       return status;
+
+    _cairo_output_stream_puts (ctx->stream, "~> >> image ");
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_emit_image_surface (cairo_script_surface_t *surface,
+                    cairo_image_surface_t *image)
+{
+    cairo_script_context_t *ctx = to_context (surface);
+    cairo_output_stream_t *base85_stream;
+    cairo_output_stream_t *zlib_stream;
+    cairo_int_status_t status, status2;
+    cairo_surface_t *snapshot;
+    const uint8_t *mime_data;
+    unsigned long mime_data_length;
+
+    snapshot = _cairo_surface_has_snapshot (&image->base,
+                                           &script_snapshot_backend);
+    if (snapshot) {
+       _cairo_output_stream_printf (ctx->stream, "s%u ", snapshot->unique_id);
+       return CAIRO_INT_STATUS_SUCCESS;
+    }
+
+    status = _emit_png_surface (surface, image);
+    if (_cairo_int_status_is_error (status)) {
+       return status;
+    } else if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
+       cairo_image_surface_t *clone;
+       uint32_t len;
+
+       if (image->format == CAIRO_FORMAT_INVALID) {
+           clone = _cairo_image_surface_coerce (image);
+       } else {
+           clone = (cairo_image_surface_t *)
+               cairo_surface_reference (&image->base);
+       }
+
+       _cairo_output_stream_printf (ctx->stream,
+                                    "<< "
+                                    "/width %d "
+                                    "/height %d "
+                                    "/format //%s "
+                                    "/source ",
+                                    clone->width, clone->height,
+                                    _format_to_string (clone->format));
+
+       switch (clone->format) {
+       case CAIRO_FORMAT_A1:
+           len = (clone->width + 7)/8;
+           break;
+       case CAIRO_FORMAT_A8:
+           len = clone->width;
+           break;
+       case CAIRO_FORMAT_RGB16_565:
+           len = clone->width * 2;
+           break;
+       case CAIRO_FORMAT_RGB24:
+           len = clone->width * 3;
+           break;
+       case CAIRO_FORMAT_RGB30:
+       case CAIRO_FORMAT_ARGB32:
+           len = clone->width * 4;
+           break;
+       case CAIRO_FORMAT_INVALID:
+       default:
+           ASSERT_NOT_REACHED;
+           len = 0;
+           break;
+       }
+       len *= clone->height;
+
+       if (len > 24) {
+           _cairo_output_stream_puts (ctx->stream, "<|");
+
+           base85_stream = _cairo_base85_stream_create (ctx->stream);
+
+           len = to_be32 (len);
+           _cairo_output_stream_write (base85_stream, &len, sizeof (len));
+
+           zlib_stream = _cairo_deflate_stream_create (base85_stream);
+           status = _write_image_surface (zlib_stream, clone);
+
+           status2 = _cairo_output_stream_destroy (zlib_stream);
+           if (status == CAIRO_INT_STATUS_SUCCESS)
+               status = status2;
+           status2 = _cairo_output_stream_destroy (base85_stream);
+           if (status == CAIRO_INT_STATUS_SUCCESS)
+               status = status2;
+           if (unlikely (status))
+               return status;
+       } else {
+           _cairo_output_stream_puts (ctx->stream, "<~");
+
+           base85_stream = _cairo_base85_stream_create (ctx->stream);
+           status = _write_image_surface (base85_stream, clone);
+           status2 = _cairo_output_stream_destroy (base85_stream);
+           if (status == CAIRO_INT_STATUS_SUCCESS)
+               status = status2;
+           if (unlikely (status))
+               return status;
+       }
+       _cairo_output_stream_puts (ctx->stream, "~> >> image ");
+
+       cairo_surface_destroy (&clone->base);
+    }
+
+    cairo_surface_get_mime_data (&image->base, CAIRO_MIME_TYPE_JPEG,
+                                &mime_data, &mime_data_length);
+    if (mime_data != NULL) {
+       _cairo_output_stream_printf (ctx->stream,
+                                    "\n  (%s) <~",
+                                    CAIRO_MIME_TYPE_JPEG);
+
+       base85_stream = _cairo_base85_stream_create (ctx->stream);
+       _cairo_output_stream_write (base85_stream, mime_data, mime_data_length);
+       status = _cairo_output_stream_destroy (base85_stream);
+       if (unlikely (status))
+           return status;
+
+       _cairo_output_stream_puts (ctx->stream, "~> set-mime-data\n");
+    }
+
+    cairo_surface_get_mime_data (&image->base, CAIRO_MIME_TYPE_JP2,
+                                &mime_data, &mime_data_length);
+    if (mime_data != NULL) {
+       _cairo_output_stream_printf (ctx->stream,
+                                    "\n  (%s) <~",
+                                    CAIRO_MIME_TYPE_JP2);
+
+       base85_stream = _cairo_base85_stream_create (ctx->stream);
+       _cairo_output_stream_write (base85_stream, mime_data, mime_data_length);
+       status = _cairo_output_stream_destroy (base85_stream);
+       if (unlikely (status))
+           return status;
+
+       _cairo_output_stream_puts (ctx->stream, "~> set-mime-data\n");
+    }
+    attach_snapshot (ctx, &image->base);
+
+    return CAIRO_INT_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_emit_image_surface_pattern (cairo_script_surface_t *surface,
+                            cairo_surface_t *source)
+{
+    cairo_image_surface_t *image;
+    cairo_status_t status;
+    void *extra;
+
+    status = _cairo_surface_acquire_source_image (source, &image, &extra);
+    if (likely (status == CAIRO_STATUS_SUCCESS)) {
+       status = _emit_image_surface (surface, image);
+       _cairo_surface_release_source_image (source, image, extra);
+    }
+
+    return status;
+}
+
+static cairo_int_status_t
+_emit_subsurface_pattern (cairo_script_surface_t *surface,
+                         cairo_surface_subsurface_t *sub)
+{
+    cairo_surface_t *source = sub->target;
+    cairo_int_status_t status;
+
+    switch ((int) source->backend->type) {
+    case CAIRO_SURFACE_TYPE_RECORDING:
+       status = _emit_recording_surface_pattern (surface, (cairo_recording_surface_t *) source);
+       break;
+    case CAIRO_SURFACE_TYPE_SCRIPT:
+       status = _emit_script_surface_pattern (surface, (cairo_script_surface_t *) source);
+       break;
+    default:
+       status = _emit_image_surface_pattern (surface, source);
+       break;
+    }
+    if (unlikely (status))
+       return status;
+
+    _cairo_output_stream_printf (to_context (surface)->stream,
+                                "%d %d %d %d subsurface ",
+                                sub->extents.x,
+                                sub->extents.y,
+                                sub->extents.width,
+                                sub->extents.height);
+    return CAIRO_INT_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_emit_surface_pattern (cairo_script_surface_t *surface,
+                      const cairo_pattern_t *pattern)
+{
+    cairo_script_context_t *ctx = to_context (surface);
+    cairo_surface_pattern_t *surface_pattern;
+    cairo_surface_t *source, *snapshot;
+    cairo_surface_t *take_snapshot = NULL;
+    cairo_int_status_t status;
+
+    surface_pattern = (cairo_surface_pattern_t *) pattern;
+    source = surface_pattern->surface;
+
+    if (_cairo_surface_is_snapshot (source)) {
+       snapshot = _cairo_surface_has_snapshot (source, &script_snapshot_backend);
+       if (snapshot) {
+           _cairo_output_stream_printf (ctx->stream,
+                                        "s%d pattern ",
+                                        snapshot->unique_id);
+           return CAIRO_INT_STATUS_SUCCESS;
+       }
+
+       if (_cairo_surface_snapshot_is_reused (source))
+           take_snapshot = source;
+
+       source = _cairo_surface_snapshot_get_target (source);
+    }
+
+    switch ((int) source->backend->type) {
+    case CAIRO_SURFACE_TYPE_RECORDING:
+       status = _emit_recording_surface_pattern (surface, (cairo_recording_surface_t *) source);
+       break;
+    case CAIRO_SURFACE_TYPE_SCRIPT:
+       status = _emit_script_surface_pattern (surface, (cairo_script_surface_t *) source);
+       break;
+    case CAIRO_SURFACE_TYPE_SUBSURFACE:
+       status = _emit_subsurface_pattern (surface, (cairo_surface_subsurface_t *) source);
+       break;
+    default:
+       status = _emit_image_surface_pattern (surface, source);
+       break;
+    }
+    if (unlikely (status))
+       return status;
+
+    if (take_snapshot)
+       attach_snapshot (ctx, take_snapshot);
+
+    _cairo_output_stream_puts (ctx->stream, "pattern");
+    return CAIRO_INT_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_emit_pattern (cairo_script_surface_t *surface,
+              const cairo_pattern_t *pattern)
+{
+    cairo_script_context_t *ctx = to_context (surface);
+    cairo_int_status_t status;
+    cairo_bool_t is_default_extend;
+    cairo_bool_t need_newline = TRUE;
+
+    switch (pattern->type) {
+    case CAIRO_PATTERN_TYPE_SOLID:
+       /* solid colors do not need filter/extend/matrix */
+       return _emit_solid_pattern (surface, pattern);
+
+    case CAIRO_PATTERN_TYPE_LINEAR:
+       status = _emit_linear_pattern (surface, pattern);
+       is_default_extend = pattern->extend == CAIRO_EXTEND_GRADIENT_DEFAULT;
+       break;
+    case CAIRO_PATTERN_TYPE_RADIAL:
+       status = _emit_radial_pattern (surface, pattern);
+       is_default_extend = pattern->extend == CAIRO_EXTEND_GRADIENT_DEFAULT;
+       break;
+    case CAIRO_PATTERN_TYPE_MESH:
+       status = _emit_mesh_pattern (surface, pattern);
+       is_default_extend = TRUE;
+       break;
+    case CAIRO_PATTERN_TYPE_SURFACE:
+       status = _emit_surface_pattern (surface, pattern);
+       is_default_extend = pattern->extend == CAIRO_EXTEND_SURFACE_DEFAULT;
+       break;
+
+    default:
+       ASSERT_NOT_REACHED;
+       status = CAIRO_INT_STATUS_UNSUPPORTED;
+    }
+    if (unlikely (status))
+       return status;
+
+    if (! _cairo_matrix_is_identity (&pattern->matrix)) {
+       if (need_newline) {
+           _cairo_output_stream_puts (ctx->stream, "\n ");
+           need_newline = FALSE;
+       }
+
+       _cairo_output_stream_printf (ctx->stream,
+                                    " [%f %f %f %f %f %f] set-matrix\n ",
+                                    pattern->matrix.xx, pattern->matrix.yx,
+                                    pattern->matrix.xy, pattern->matrix.yy,
+                                    pattern->matrix.x0, pattern->matrix.y0);
+    }
+
+    /* XXX need to discriminate the user explicitly setting the default */
+    if (pattern->filter != CAIRO_FILTER_DEFAULT) {
+       if (need_newline) {
+           _cairo_output_stream_puts (ctx->stream, "\n ");
+           need_newline = FALSE;
+       }
+
+       _cairo_output_stream_printf (ctx->stream,
+                                    " //%s set-filter\n ",
+                                    _filter_to_string (pattern->filter));
+    }
+    if (! is_default_extend ){
+       if (need_newline) {
+           _cairo_output_stream_puts (ctx->stream, "\n ");
+           need_newline = FALSE;
+       }
+
+       _cairo_output_stream_printf (ctx->stream,
+                                    " //%s set-extend\n ",
+                                    _extend_to_string (pattern->extend));
+    }
+
+    if (need_newline)
+       _cairo_output_stream_puts (ctx->stream, "\n ");
+
+    return CAIRO_INT_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_emit_identity (cairo_script_surface_t *surface,
+               cairo_bool_t *matrix_updated)
+{
+    assert (target_is_active (surface));
+
+    if (_cairo_matrix_is_identity (&surface->cr.current_ctm))
+       return CAIRO_INT_STATUS_SUCCESS;
+
+    _cairo_output_stream_puts (to_context (surface)->stream,
+                              "identity set-matrix\n");
+
+    *matrix_updated = TRUE;
+    cairo_matrix_init_identity (&surface->cr.current_ctm);
+
+    return CAIRO_INT_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_emit_source (cairo_script_surface_t *surface,
+             cairo_operator_t op,
+             const cairo_pattern_t *source)
+{
+    cairo_bool_t matrix_updated = FALSE;
+    cairo_int_status_t status;
+
+    assert (target_is_active (surface));
+
+    if (op == CAIRO_OPERATOR_CLEAR) {
+       /* the source is ignored, so don't change it */
+       return CAIRO_INT_STATUS_SUCCESS;
+    }
+
+    if (_cairo_pattern_equal (&surface->cr.current_source.base, source))
+       return CAIRO_INT_STATUS_SUCCESS;
+
+    _cairo_pattern_fini (&surface->cr.current_source.base);
+    status = _cairo_pattern_init_copy (&surface->cr.current_source.base,
+                                      source);
+    if (unlikely (status))
+       return status;
+
+    status = _emit_identity (surface, &matrix_updated);
+    if (unlikely (status))
+       return status;
+
+    status = _emit_pattern (surface, source);
+    if (unlikely (status))
+       return status;
+
+    assert (target_is_active (surface));
+    _cairo_output_stream_puts (to_context (surface)->stream,
+                              " set-source\n");
+    return CAIRO_INT_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_path_move_to (void *closure,
+              const cairo_point_t *point)
+{
+    _cairo_output_stream_printf (closure,
+                                " %f %f m",
+                                _cairo_fixed_to_double (point->x),
+                                _cairo_fixed_to_double (point->y));
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_path_line_to (void *closure,
+              const cairo_point_t *point)
+{
+    _cairo_output_stream_printf (closure,
+                                " %f %f l",
+                                _cairo_fixed_to_double (point->x),
+                                _cairo_fixed_to_double (point->y));
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_path_curve_to (void *closure,
+               const cairo_point_t *p1,
+               const cairo_point_t *p2,
+               const cairo_point_t *p3)
+{
+    _cairo_output_stream_printf (closure,
+                                " %f %f %f %f %f %f c",
+                                _cairo_fixed_to_double (p1->x),
+                                _cairo_fixed_to_double (p1->y),
+                                _cairo_fixed_to_double (p2->x),
+                                _cairo_fixed_to_double (p2->y),
+                                _cairo_fixed_to_double (p3->x),
+                                _cairo_fixed_to_double (p3->y));
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_path_close (void *closure)
+{
+    _cairo_output_stream_printf (closure,
+                                " h");
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_emit_path_boxes (cairo_script_surface_t *surface,
+                 const cairo_path_fixed_t *path)
+{
+    cairo_script_context_t *ctx = to_context (surface);
+    cairo_path_fixed_iter_t iter;
+    cairo_status_t status;
+    struct _cairo_boxes_chunk *chunk;
+    cairo_boxes_t boxes;
+    cairo_box_t box;
+    int i;
+
+    _cairo_boxes_init (&boxes);
+    _cairo_path_fixed_iter_init (&iter, path);
+    while (_cairo_path_fixed_iter_is_fill_box (&iter, &box)) {
+       if (box.p1.y == box.p2.y || box.p1.x == box.p2.x)
+           continue;
+
+       status = _cairo_boxes_add (&boxes, CAIRO_ANTIALIAS_DEFAULT, &box);
+       if (unlikely (status)) {
+           _cairo_boxes_fini (&boxes);
+           return status;
+       }
+    }
+
+    if (! _cairo_path_fixed_iter_at_end (&iter)) {
+       _cairo_boxes_fini (&boxes);
+       return FALSE;
+    }
+
+    for (chunk = &boxes.chunks; chunk; chunk = chunk->next) {
+       for (i = 0; i < chunk->count; i++) {
+           const cairo_box_t *b = &chunk->base[i];
+           double x1 = _cairo_fixed_to_double (b->p1.x);
+           double y1 = _cairo_fixed_to_double (b->p1.y);
+           double x2 = _cairo_fixed_to_double (b->p2.x);
+           double y2 = _cairo_fixed_to_double (b->p2.y);
+
+           _cairo_output_stream_printf (ctx->stream,
+                                        "\n  %f %f %f %f rectangle",
+                                        x1, y1, x2 - x1, y2 - y1);
+       }
+    }
+
+    _cairo_boxes_fini (&boxes);
+    return status;
+}
+
+static cairo_status_t
+_emit_path (cairo_script_surface_t *surface,
+           const cairo_path_fixed_t *path,
+           cairo_bool_t is_fill)
+{
+    cairo_script_context_t *ctx = to_context (surface);
+    cairo_box_t box;
+    cairo_int_status_t status;
+
+    assert (target_is_active (surface));
+    assert (_cairo_matrix_is_identity (&surface->cr.current_ctm));
+
+    if (_cairo_path_fixed_equal (&surface->cr.current_path, path))
+       return CAIRO_STATUS_SUCCESS;
+
+    _cairo_path_fixed_fini (&surface->cr.current_path);
+
+    _cairo_output_stream_puts (ctx->stream, "n");
+
+    if (path == NULL) {
+       _cairo_path_fixed_init (&surface->cr.current_path);
+       _cairo_output_stream_puts (ctx->stream, "\n");
+       return CAIRO_STATUS_SUCCESS;
+    }
+
+    status = _cairo_path_fixed_init_copy (&surface->cr.current_path, path);
+    if (unlikely (status))
+       return status;
+
+    status = CAIRO_INT_STATUS_UNSUPPORTED;
+    if (_cairo_path_fixed_is_rectangle (path, &box)) {
+       double x1 = _cairo_fixed_to_double (box.p1.x);
+       double y1 = _cairo_fixed_to_double (box.p1.y);
+       double x2 = _cairo_fixed_to_double (box.p2.x);
+       double y2 = _cairo_fixed_to_double (box.p2.y);
+
+       assert (x1 > -9999);
+
+       _cairo_output_stream_printf (ctx->stream,
+                                    " %f %f %f %f rectangle",
+                                    x1, y1, x2 - x1, y2 - y1);
+       status = CAIRO_INT_STATUS_SUCCESS;
+    } else if (is_fill && _cairo_path_fixed_fill_is_rectilinear (path)) {
+       status = _emit_path_boxes (surface, path);
+    }
+
+    if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
+       status = _cairo_path_fixed_interpret (path,
+                                             _path_move_to,
+                                             _path_line_to,
+                                             _path_curve_to,
+                                             _path_close,
+                                             ctx->stream);
+    }
+
+    _cairo_output_stream_puts (ctx->stream, "\n");
+
+    return status;
+}
+static cairo_bool_t
+_scaling_matrix_equal (const cairo_matrix_t *a,
+                      const cairo_matrix_t *b)
+{
+    return fabs (a->xx - b->xx) < 1e-5 &&
+          fabs (a->xy - b->xy) < 1e-5 &&
+          fabs (a->yx - b->yx) < 1e-5 &&
+          fabs (a->yy - b->yy) < 1e-5;
+}
+
+static cairo_status_t
+_emit_scaling_matrix (cairo_script_surface_t *surface,
+                     const cairo_matrix_t *ctm,
+                     cairo_bool_t *matrix_updated)
+{
+    cairo_script_context_t *ctx = to_context (surface);
+    cairo_bool_t was_identity;
+    assert (target_is_active (surface));
+
+    if (_scaling_matrix_equal (&surface->cr.current_ctm, ctm))
+       return CAIRO_STATUS_SUCCESS;
+
+    was_identity = _cairo_matrix_is_identity (&surface->cr.current_ctm);
+
+    *matrix_updated = TRUE;
+    surface->cr.current_ctm = *ctm;
+    surface->cr.current_ctm.x0 = 0.;
+    surface->cr.current_ctm.y0 = 0.;
+
+    if (_cairo_matrix_is_identity (&surface->cr.current_ctm)) {
+       _cairo_output_stream_puts (ctx->stream,
+                                  "identity set-matrix\n");
+    } else if (was_identity && fabs (ctm->yx) < 1e-5 && fabs (ctm->xy) < 1e-5) {
+       _cairo_output_stream_printf (ctx->stream,
+                                    "%f %f scale\n",
+                                    ctm->xx, ctm->yy);
+    } else {
+       _cairo_output_stream_printf (ctx->stream,
+                                    "[%f %f %f %f 0 0] set-matrix\n",
+                                    ctm->xx, ctm->yx,
+                                    ctm->xy, ctm->yy);
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_emit_font_matrix (cairo_script_surface_t *surface,
+                  const cairo_matrix_t *font_matrix)
+{
+    cairo_script_context_t *ctx = to_context (surface);
+    assert (target_is_active (surface));
+
+    if (memcmp (&surface->cr.current_font_matrix,
+               font_matrix,
+               sizeof (cairo_matrix_t)) == 0)
+    {
+       return CAIRO_STATUS_SUCCESS;
+    }
+
+    surface->cr.current_font_matrix = *font_matrix;
+
+    if (_cairo_matrix_is_identity (font_matrix)) {
+       _cairo_output_stream_puts (ctx->stream,
+                                  "identity set-font-matrix\n");
+    } else {
+       _cairo_output_stream_printf (ctx->stream,
+                                    "[%f %f %f %f %f %f] set-font-matrix\n",
+                                    font_matrix->xx, font_matrix->yx,
+                                    font_matrix->xy, font_matrix->yy,
+                                    font_matrix->x0, font_matrix->y0);
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_surface_t *
+_cairo_script_surface_create_similar (void            *abstract_surface,
+                                     cairo_content_t   content,
+                                     int               width,
+                                     int               height)
+{
+    cairo_script_surface_t *surface, *other = abstract_surface;
+    cairo_surface_t *passthrough = NULL;
+    cairo_script_context_t *ctx;
+    cairo_rectangle_t extents;
+    cairo_status_t status;
+
+    ctx = to_context (other);
+
+    status = cairo_device_acquire (&ctx->base);
+    if (unlikely (status))
+       return _cairo_surface_create_in_error (status);
+
+    if (! other->emitted) {
+       status = _emit_surface (other);
+       if (unlikely (status)) {
+           cairo_device_release (&ctx->base);
+           return _cairo_surface_create_in_error (status);
+       }
+
+       target_push (other);
+    }
+
+    if (_cairo_surface_wrapper_is_active (&other->wrapper)) {
+       passthrough =
+           _cairo_surface_wrapper_create_similar (&other->wrapper,
+                                                  content, width, height);
+       if (unlikely (passthrough->status)) {
+           cairo_device_release (&ctx->base);
+           return passthrough;
+       }
+    }
+
+    extents.x = extents.y = 0;
+    extents.width = width;
+    extents.height = height;
+    surface = _cairo_script_surface_create_internal (ctx, content,
+                                                    &extents, passthrough);
+    cairo_surface_destroy (passthrough);
+
+    if (unlikely (surface->base.status)) {
+       cairo_device_release (&ctx->base);
+       return &surface->base;
+    }
+
+    _get_target (other);
+    _cairo_output_stream_printf (ctx->stream,
+                                "%u %u //%s similar dup /s%u exch def context\n",
+                                width, height,
+                                _content_to_string (content),
+                                surface->base.unique_id);
+
+    surface->emitted = TRUE;
+    surface->defined = TRUE;
+    surface->base.is_clear = TRUE;
+    target_push (surface);
+
+    cairo_device_release (&ctx->base);
+    return &surface->base;
+}
+
+static cairo_status_t
+_device_flush (void *abstract_device)
+{
+    cairo_script_context_t *ctx = abstract_device;
+
+    return _cairo_output_stream_flush (ctx->stream);
+}
+
+static void
+_device_destroy (void *abstract_device)
+{
+    cairo_script_context_t *ctx = abstract_device;
+    cairo_status_t status;
+
+    while (! cairo_list_is_empty (&ctx->fonts)) {
+       cairo_script_font_t *font;
+
+       font = cairo_list_first_entry (&ctx->fonts, cairo_script_font_t, link);
+       cairo_list_del (&font->base.link);
+       cairo_list_del (&font->link);
+       free (font);
+    }
+
+    _bitmap_fini (ctx->surface_id.next);
+    _bitmap_fini (ctx->font_id.next);
+
+    if (ctx->owns_stream)
+       status = _cairo_output_stream_destroy (ctx->stream);
+
+    free (ctx);
+}
+
+static cairo_status_t
+_cairo_script_surface_acquire_source_image (void                    *abstract_surface,
+                                           cairo_image_surface_t  **image_out,
+                                           void                   **image_extra)
+{
+    cairo_script_surface_t *surface = abstract_surface;
+
+    if (_cairo_surface_wrapper_is_active (&surface->wrapper)) {
+       return _cairo_surface_wrapper_acquire_source_image (&surface->wrapper,
+                                                           image_out,
+                                                           image_extra);
+    }
+
+    return CAIRO_INT_STATUS_UNSUPPORTED;
+}
+
+static void
+_cairo_script_surface_release_source_image (void                   *abstract_surface,
+                                          cairo_image_surface_t  *image,
+                                          void                   *image_extra)
+{
+    cairo_script_surface_t *surface = abstract_surface;
+
+    assert (_cairo_surface_wrapper_is_active (&surface->wrapper));
+    _cairo_surface_wrapper_release_source_image (&surface->wrapper,
+                                                image,
+                                                image_extra);
+}
+
+static cairo_status_t
+_cairo_script_surface_finish (void *abstract_surface)
+{
+    cairo_script_surface_t *surface = abstract_surface;
+    cairo_script_context_t *ctx = to_context (surface);
+    cairo_status_t status = CAIRO_STATUS_SUCCESS, status2;
+
+    _cairo_surface_wrapper_fini (&surface->wrapper);
+
+    free (surface->cr.current_style.dash);
+    surface->cr.current_style.dash = NULL;
+
+    _cairo_pattern_fini (&surface->cr.current_source.base);
+    _cairo_path_fixed_fini (&surface->cr.current_path);
+    _cairo_surface_clipper_reset (&surface->clipper);
+
+    status = cairo_device_acquire (&ctx->base);
+    if (unlikely (status))
+       return status;
+
+    if (surface->emitted) {
+       assert (! surface->active);
+
+       if (! cairo_list_is_empty (&surface->operand.link)) {
+           if (! ctx->active) {
+               if (target_is_active (surface)) {
+                   _cairo_output_stream_printf (ctx->stream,
+                                                "pop\n");
+               } else {
+                   int depth = target_depth (surface);
+                   if (depth == 1) {
+                       _cairo_output_stream_printf (ctx->stream,
+                                                    "exch pop\n");
+                   } else {
+                       _cairo_output_stream_printf (ctx->stream,
+                                                    "%d -1 roll pop\n",
+                                                    depth);
+                   }
+               }
+               cairo_list_del (&surface->operand.link);
+           } else {
+               struct deferred_finish *link = malloc (sizeof (*link));
+               if (link == NULL) {
+                   status2 = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+                   if (status == CAIRO_STATUS_SUCCESS)
+                       status = status2;
+                   cairo_list_del (&surface->operand.link);
+               } else {
+                   link->operand.type = DEFERRED;
+                   cairo_list_swap (&link->operand.link,
+                                    &surface->operand.link);
+                   cairo_list_add (&link->link, &ctx->deferred);
+               }
+           }
+       }
+
+       if (surface->defined) {
+           _cairo_output_stream_printf (ctx->stream,
+                                        "/s%u undef\n",
+                                        surface->base.unique_id);
+       }
+    }
+
+    if (status == CAIRO_STATUS_SUCCESS)
+       status = _cairo_output_stream_flush (to_context (surface)->stream);
+
+    cairo_device_release (&ctx->base);
+
+    return status;
+}
+
+static cairo_int_status_t
+_cairo_script_surface_copy_page (void *abstract_surface)
+{
+    cairo_script_surface_t *surface = abstract_surface;
+    cairo_status_t status;
+
+    status = cairo_device_acquire (surface->base.device);
+    if (unlikely (status))
+       return status;
+
+    status = _emit_context (surface);
+    if (unlikely (status))
+       goto BAIL;
+
+    _cairo_output_stream_puts (to_context (surface)->stream, "copy-page\n");
+
+BAIL:
+    cairo_device_release (surface->base.device);
+    return status;
+}
+
+static cairo_int_status_t
+_cairo_script_surface_show_page (void *abstract_surface)
+{
+    cairo_script_surface_t *surface = abstract_surface;
+    cairo_status_t status;
+
+    status = cairo_device_acquire (surface->base.device);
+    if (unlikely (status))
+       return status;
+
+    status = _emit_context (surface);
+    if (unlikely (status))
+       goto BAIL;
+
+    _cairo_output_stream_puts (to_context (surface)->stream, "show-page\n");
+
+BAIL:
+    cairo_device_release (surface->base.device);
+    return status;
+}
+
+static cairo_status_t
+_cairo_script_surface_clipper_intersect_clip_path (cairo_surface_clipper_t *clipper,
+                                                  cairo_path_fixed_t   *path,
+                                                  cairo_fill_rule_t     fill_rule,
+                                                  double                tolerance,
+                                                  cairo_antialias_t     antialias)
+{
+    cairo_script_surface_t *surface = cairo_container_of (clipper,
+                                                         cairo_script_surface_t,
+                                                         clipper);
+    cairo_script_context_t *ctx = to_context (surface);
+    cairo_bool_t matrix_updated = FALSE;
+    cairo_status_t status;
+    cairo_box_t box;
+
+    status = _emit_context (surface);
+    if (unlikely (status))
+       return status;
+
+    if (path == NULL) {
+       if (surface->cr.has_clip) {
+           _cairo_output_stream_puts (ctx->stream, "reset-clip\n");
+           surface->cr.has_clip = FALSE;
+       }
+       return CAIRO_STATUS_SUCCESS;
+    }
+
+    /* skip the trivial clip covering the surface extents */
+    if (surface->width >= 0 && surface->height >= 0 &&
+       _cairo_path_fixed_is_box (path, &box))
+    {
+       if (box.p1.x <= 0 && box.p1.y <= 0 &&
+           box.p2.x >= _cairo_fixed_from_double (surface->width) &&
+           box.p2.y >= _cairo_fixed_from_double (surface->height))
+       {
+           return CAIRO_STATUS_SUCCESS;
+       }
+    }
+
+    status = _emit_identity (surface, &matrix_updated);
+    if (unlikely (status))
+       return status;
+
+    status = _emit_fill_rule (surface, fill_rule);
+    if (unlikely (status))
+       return status;
+
+    if (path->has_curve_to) {
+       status = _emit_tolerance (surface, tolerance, matrix_updated);
+       if (unlikely (status))
+           return status;
+    }
+
+    if (! _cairo_path_fixed_fill_maybe_region (path)) {
+       status = _emit_antialias (surface, antialias);
+       if (unlikely (status))
+           return status;
+    }
+
+    status = _emit_path (surface, path, TRUE);
+    if (unlikely (status))
+       return status;
+
+    _cairo_output_stream_puts (ctx->stream, "clip+\n");
+    surface->cr.has_clip = TRUE;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+active (cairo_script_surface_t *surface)
+{
+    cairo_status_t status;
+
+    status = cairo_device_acquire (surface->base.device);
+    if (unlikely (status))
+       return status;
+
+    if (surface->active++ == 0)
+       to_context (surface)->active++;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+inactive (cairo_script_surface_t *surface)
+{
+    cairo_script_context_t *ctx = to_context (surface);
+    cairo_list_t sorted;
+
+    assert (surface->active > 0);
+    if (--surface->active)
+       goto DONE;
+
+    assert (ctx->active > 0);
+    if (--ctx->active)
+       goto DONE;
+
+    cairo_list_init (&sorted);
+    while (! cairo_list_is_empty (&ctx->deferred)) {
+       struct deferred_finish *df;
+       cairo_list_t *operand;
+       int depth;
+
+       df = cairo_list_first_entry (&ctx->deferred,
+                                    struct deferred_finish,
+                                    link);
+
+       depth = 0;
+       cairo_list_foreach (operand, &ctx->operands) {
+           if (operand == &df->operand.link)
+               break;
+           depth++;
+       }
+
+       df->operand.type = depth;
+
+       if (cairo_list_is_empty (&sorted)) {
+           cairo_list_move (&df->link, &sorted);
+       } else {
+           struct deferred_finish *pos;
+
+           cairo_list_foreach_entry (pos, struct deferred_finish,
+                                     &sorted,
+                                     link)
+           {
+               if (df->operand.type < pos->operand.type)
+                   break;
+           }
+           cairo_list_move_tail (&df->link, &pos->link);
+       }
+    }
+
+    while (! cairo_list_is_empty (&sorted)) {
+       struct deferred_finish *df;
+       cairo_list_t *operand;
+       int depth;
+
+       df = cairo_list_first_entry (&sorted,
+                                    struct deferred_finish,
+                                    link);
+
+       depth = 0;
+       cairo_list_foreach (operand, &ctx->operands) {
+           if (operand == &df->operand.link)
+               break;
+           depth++;
+       }
+
+       if (depth == 0) {
+           _cairo_output_stream_printf (ctx->stream,
+                                        "pop\n");
+       } else if (depth == 1) {
+           _cairo_output_stream_printf (ctx->stream,
+                                        "exch pop\n");
+       } else {
+           _cairo_output_stream_printf (ctx->stream,
+                                        "%d -1 roll pop\n",
+                                        depth);
+       }
+
+       cairo_list_del (&df->operand.link);
+       cairo_list_del (&df->link);
+       free (df);
+    }
+
+DONE:
+    cairo_device_release (surface->base.device);
+}
+
+static cairo_int_status_t
+_cairo_script_surface_paint (void                      *abstract_surface,
+                            cairo_operator_t            op,
+                            const cairo_pattern_t      *source,
+                            const cairo_clip_t         *clip)
+{
+    cairo_script_surface_t *surface = abstract_surface;
+    cairo_status_t status;
+
+    status = active (surface);
+    if (unlikely (status))
+       return status;
+
+    status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
+    if (unlikely (status))
+       goto BAIL;
+
+    status = _emit_context (surface);
+    if (unlikely (status))
+       goto BAIL;
+
+    status = _emit_source (surface, op, source);
+    if (unlikely (status))
+       goto BAIL;
+
+    status = _emit_operator (surface, op);
+    if (unlikely (status))
+       goto BAIL;
+
+    _cairo_output_stream_puts (to_context (surface)->stream,
+                              "paint\n");
+
+    inactive (surface);
+
+    if (_cairo_surface_wrapper_is_active (&surface->wrapper)) {
+       return _cairo_surface_wrapper_paint (&surface->wrapper,
+                                            op, source, clip);
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+
+BAIL:
+    inactive (surface);
+    return status;
+}
+
+static cairo_int_status_t
+_cairo_script_surface_mask (void                       *abstract_surface,
+                           cairo_operator_t             op,
+                           const cairo_pattern_t       *source,
+                           const cairo_pattern_t       *mask,
+                           const cairo_clip_t          *clip)
+{
+    cairo_script_surface_t *surface = abstract_surface;
+    cairo_status_t status;
+
+    status = active (surface);
+    if (unlikely (status))
+       return status;
+
+    status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
+    if (unlikely (status))
+       goto BAIL;
+
+    status = _emit_context (surface);
+    if (unlikely (status))
+       goto BAIL;
+
+    status = _emit_source (surface, op, source);
+    if (unlikely (status))
+       goto BAIL;
+
+    status = _emit_operator (surface, op);
+    if (unlikely (status))
+       goto BAIL;
+
+    if (_cairo_pattern_equal (source, mask)) {
+       _cairo_output_stream_puts (to_context (surface)->stream, "/source get");
+    } else {
+       status = _emit_pattern (surface, mask);
+       if (unlikely (status))
+           goto BAIL;
+    }
+
+    assert (surface->cr.current_operator == op);
+
+    _cairo_output_stream_puts (to_context (surface)->stream,
+                              " mask\n");
+
+    inactive (surface);
+
+    if (_cairo_surface_wrapper_is_active (&surface->wrapper)) {
+       return _cairo_surface_wrapper_mask (&surface->wrapper,
+                                           op, source, mask, clip);
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+
+BAIL:
+    inactive (surface);
+    return status;
+}
+
+static cairo_int_status_t
+_cairo_script_surface_stroke (void                             *abstract_surface,
+                             cairo_operator_t                   op,
+                             const cairo_pattern_t             *source,
+                             const cairo_path_fixed_t          *path,
+                             const cairo_stroke_style_t        *style,
+                             const cairo_matrix_t              *ctm,
+                             const cairo_matrix_t              *ctm_inverse,
+                             double                             tolerance,
+                             cairo_antialias_t                  antialias,
+                             const cairo_clip_t                *clip)
+{
+    cairo_script_surface_t *surface = abstract_surface;
+    cairo_bool_t matrix_updated = FALSE;
+    cairo_status_t status;
+
+    status = active (surface);
+    if (unlikely (status))
+       return status;
+
+    status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
+    if (unlikely (status))
+       goto BAIL;
+
+    status = _emit_context (surface);
+    if (unlikely (status))
+       goto BAIL;
+
+    status = _emit_identity (surface, &matrix_updated);
+    if (unlikely (status))
+       goto BAIL;
+
+    status = _emit_path (surface, path, FALSE);
+    if (unlikely (status))
+       goto BAIL;
+
+    status = _emit_source (surface, op, source);
+    if (unlikely (status))
+       goto BAIL;
+
+    status = _emit_scaling_matrix (surface, ctm, &matrix_updated);
+    if (unlikely (status))
+       goto BAIL;
+
+    status = _emit_operator (surface, op);
+    if (unlikely (status))
+       goto BAIL;
+
+    if (_scaling_matrix_equal (&surface->cr.current_ctm,
+                              &surface->cr.current_stroke_matrix))
+    {
+       matrix_updated = FALSE;
+    }
+    else
+    {
+       matrix_updated = TRUE;
+       surface->cr.current_stroke_matrix = surface->cr.current_ctm;
+    }
+
+    status = _emit_stroke_style (surface, style, matrix_updated);
+    if (unlikely (status))
+       goto BAIL;
+
+    status = _emit_tolerance (surface, tolerance, matrix_updated);
+    if (unlikely (status))
+       goto BAIL;
+
+    status = _emit_antialias (surface, antialias);
+    if (unlikely (status))
+       goto BAIL;
+
+    _cairo_output_stream_puts (to_context (surface)->stream, "stroke+\n");
+
+    inactive (surface);
+
+    if (_cairo_surface_wrapper_is_active (&surface->wrapper)) {
+       return _cairo_surface_wrapper_stroke (&surface->wrapper,
+                                             op, source, path,
+                                             style,
+                                             ctm, ctm_inverse,
+                                             tolerance, antialias,
+                                             clip);
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+
+BAIL:
+    inactive (surface);
+    return status;
+}
+
+static cairo_int_status_t
+_cairo_script_surface_fill (void                       *abstract_surface,
+                           cairo_operator_t             op,
+                           const cairo_pattern_t       *source,
+                           const cairo_path_fixed_t    *path,
+                           cairo_fill_rule_t            fill_rule,
+                           double                       tolerance,
+                           cairo_antialias_t            antialias,
+                           const cairo_clip_t          *clip)
+{
+    cairo_script_surface_t *surface = abstract_surface;
+    cairo_bool_t matrix_updated = FALSE;
+    cairo_status_t status;
+    cairo_box_t box;
+
+    status = active (surface);
+    if (unlikely (status))
+       return status;
+
+    status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
+    if (unlikely (status))
+       goto BAIL;
+
+    status = _emit_context (surface);
+    if (unlikely (status))
+       goto BAIL;
+
+    status = _emit_identity (surface, &matrix_updated);
+    if (unlikely (status))
+       goto BAIL;
+
+    status = _emit_source (surface, op, source);
+    if (unlikely (status))
+       goto BAIL;
+
+    if (! _cairo_path_fixed_is_box (path, &box)) {
+       status = _emit_fill_rule (surface, fill_rule);
+       if (unlikely (status))
+           goto BAIL;
+    }
+
+    if (path->has_curve_to) {
+       status = _emit_tolerance (surface, tolerance, matrix_updated);
+       if (unlikely (status))
+           goto BAIL;
+    }
+
+    if (! _cairo_path_fixed_fill_maybe_region (path)) {
+       status = _emit_antialias (surface, antialias);
+       if (unlikely (status))
+           goto BAIL;
+    }
+
+    status = _emit_path (surface, path, TRUE);
+    if (unlikely (status))
+       goto BAIL;
+
+    status = _emit_operator (surface, op);
+    if (unlikely (status))
+       goto BAIL;
+
+    _cairo_output_stream_puts (to_context (surface)->stream, "fill+\n");
+
+    inactive (surface);
+
+    if (_cairo_surface_wrapper_is_active (&surface->wrapper)) {
+       return _cairo_surface_wrapper_fill (&surface->wrapper,
+                                           op, source, path,
+                                           fill_rule,
+                                           tolerance,
+                                           antialias,
+                                           clip);
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+
+BAIL:
+    inactive (surface);
+    return status;
+}
+
+static cairo_surface_t *
+_cairo_script_surface_snapshot (void *abstract_surface)
+{
+    cairo_script_surface_t *surface = abstract_surface;
+
+    if (_cairo_surface_wrapper_is_active (&surface->wrapper))
+       return _cairo_surface_wrapper_snapshot (&surface->wrapper);
+
+    return NULL;
+}
+
+static cairo_bool_t
+_cairo_script_surface_has_show_text_glyphs (void *abstract_surface)
+{
+    return TRUE;
+}
+
+static const char *
+_subpixel_order_to_string (cairo_subpixel_order_t subpixel_order)
+{
+    static const char *names[] = {
+       "SUBPIXEL_ORDER_DEFAULT",       /* CAIRO_SUBPIXEL_ORDER_DEFAULT */
+       "SUBPIXEL_ORDER_RGB",           /* CAIRO_SUBPIXEL_ORDER_RGB */
+       "SUBPIXEL_ORDER_BGR",           /* CAIRO_SUBPIXEL_ORDER_BGR */
+       "SUBPIXEL_ORDER_VRGB",          /* CAIRO_SUBPIXEL_ORDER_VRGB */
+       "SUBPIXEL_ORDER_VBGR"           /* CAIRO_SUBPIXEL_ORDER_VBGR */
+    };
+    return names[subpixel_order];
+}
+static const char *
+_hint_style_to_string (cairo_hint_style_t hint_style)
+{
+    static const char *names[] = {
+       "HINT_STYLE_DEFAULT",   /* CAIRO_HINT_STYLE_DEFAULT */
+       "HINT_STYLE_NONE",      /* CAIRO_HINT_STYLE_NONE */
+       "HINT_STYLE_SLIGHT",    /* CAIRO_HINT_STYLE_SLIGHT */
+       "HINT_STYLE_MEDIUM",    /* CAIRO_HINT_STYLE_MEDIUM */
+       "HINT_STYLE_FULL"       /* CAIRO_HINT_STYLE_FULL */
+    };
+    return names[hint_style];
+}
+static const char *
+_hint_metrics_to_string (cairo_hint_metrics_t hint_metrics)
+{
+    static const char *names[] = {
+        "HINT_METRICS_DEFAULT",        /* CAIRO_HINT_METRICS_DEFAULT */
+        "HINT_METRICS_OFF",            /* CAIRO_HINT_METRICS_OFF */
+        "HINT_METRICS_ON"              /* CAIRO_HINT_METRICS_ON */
+    };
+    return names[hint_metrics];
+}
+
+static cairo_status_t
+_emit_font_options (cairo_script_surface_t *surface,
+                   cairo_font_options_t *font_options)
+{
+    cairo_script_context_t *ctx = to_context (surface);
+
+    if (cairo_font_options_equal (&surface->cr.current_font_options,
+                                 font_options))
+    {
+       return CAIRO_STATUS_SUCCESS;
+    }
+
+    _cairo_output_stream_printf (ctx->stream, "<<");
+
+    if (font_options->antialias != surface->cr.current_font_options.antialias) {
+       _cairo_output_stream_printf (ctx->stream,
+                                    " /antialias //%s",
+                                    _antialias_to_string (font_options->antialias));
+    }
+
+    if (font_options->subpixel_order !=
+       surface->cr.current_font_options.subpixel_order)
+    {
+       _cairo_output_stream_printf (ctx->stream,
+                                    " /subpixel-order //%s",
+                                    _subpixel_order_to_string (font_options->subpixel_order));
+    }
+
+    if (font_options->hint_style !=
+       surface->cr.current_font_options.hint_style)
+    {
+       _cairo_output_stream_printf (ctx->stream,
+                                    " /hint-style //%s",
+                                    _hint_style_to_string (font_options->hint_style));
+    }
+
+    if (font_options->hint_metrics !=
+       surface->cr.current_font_options.hint_metrics)
+    {
+       _cairo_output_stream_printf (ctx->stream,
+                                    " /hint-metrics //%s",
+                                    _hint_metrics_to_string (font_options->hint_metrics));
+    }
+
+    _cairo_output_stream_printf (ctx->stream,
+                                " >> set-font-options\n");
+
+    surface->cr.current_font_options = *font_options;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_script_scaled_font_fini (cairo_scaled_font_private_t *abstract_private,
+                               cairo_scaled_font_t *scaled_font)
+{
+    cairo_script_font_t *priv = (cairo_script_font_t *)abstract_private;
+    cairo_script_context_t *ctx = (cairo_script_context_t *)abstract_private->key;
+    cairo_status_t status;
+
+    status = cairo_device_acquire (&ctx->base);
+    if (likely (status == CAIRO_STATUS_SUCCESS)) {
+       _cairo_output_stream_printf (ctx->stream,
+                                    "/f%lu undef /sf%lu undef\n",
+                                    priv->id,
+                                    priv->id);
+
+       _bitmap_release_id (&ctx->font_id, priv->id);
+       cairo_device_release (&ctx->base);
+    }
+
+    cairo_list_del (&priv->link);
+    cairo_list_del (&priv->base.link);
+    free (priv);
+}
+
+static cairo_script_font_t *
+_cairo_script_font_get (cairo_script_context_t *ctx, cairo_scaled_font_t *font)
+{
+    return (cairo_script_font_t *) _cairo_scaled_font_find_private (font, ctx);
+}
+
+static long unsigned
+_cairo_script_font_id (cairo_script_context_t *ctx, cairo_scaled_font_t *font)
+{
+    return _cairo_script_font_get (ctx, font)->id;
+}
+
+static cairo_status_t
+_emit_type42_font (cairo_script_surface_t *surface,
+                  cairo_scaled_font_t *scaled_font)
+{
+    cairo_script_context_t *ctx = to_context (surface);
+    const cairo_scaled_font_backend_t *backend;
+    cairo_output_stream_t *base85_stream;
+    cairo_output_stream_t *zlib_stream;
+    cairo_status_t status, status2;
+    unsigned long size;
+    unsigned int load_flags;
+    uint32_t len;
+    uint8_t *buf;
+
+    backend = scaled_font->backend;
+    if (backend->load_truetype_table == NULL)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    size = 0;
+    status = backend->load_truetype_table (scaled_font, 0, 0, NULL, &size);
+    if (unlikely (status))
+       return status;
+
+    buf = malloc (size);
+    if (unlikely (buf == NULL))
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    status = backend->load_truetype_table (scaled_font, 0, 0, buf, &size);
+    if (unlikely (status)) {
+       free (buf);
+       return status;
+    }
+
+#if CAIRO_HAS_FT_FONT
+    load_flags = _cairo_ft_scaled_font_get_load_flags (scaled_font);
+#else
+    load_flags = 0;
+#endif
+    _cairo_output_stream_printf (ctx->stream,
+                                "<< "
+                                "/type 42 "
+                                "/index 0 "
+                                "/flags %d "
+                                "/source <|",
+                                load_flags);
+
+    base85_stream = _cairo_base85_stream_create (ctx->stream);
+    len = to_be32 (size);
+    _cairo_output_stream_write (base85_stream, &len, sizeof (len));
+
+    zlib_stream = _cairo_deflate_stream_create (base85_stream);
+
+    _cairo_output_stream_write (zlib_stream, buf, size);
+    free (buf);
+
+    status2 = _cairo_output_stream_destroy (zlib_stream);
+    if (status == CAIRO_STATUS_SUCCESS)
+       status = status2;
+
+    status2 = _cairo_output_stream_destroy (base85_stream);
+    if (status == CAIRO_STATUS_SUCCESS)
+       status = status2;
+
+    _cairo_output_stream_printf (ctx->stream,
+                                "~> >> font dup /f%lu exch def set-font-face",
+                                _cairo_script_font_id (ctx, scaled_font));
+
+    return status;
+}
+
+static cairo_status_t
+_emit_scaled_font_init (cairo_script_surface_t *surface,
+                       cairo_scaled_font_t *scaled_font,
+                       cairo_script_font_t **font_out)
+{
+    cairo_script_context_t *ctx = to_context (surface);
+    cairo_script_font_t *font_private;
+    cairo_int_status_t status;
+
+    font_private = malloc (sizeof (cairo_script_font_t));
+    if (unlikely (font_private == NULL))
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    _cairo_scaled_font_attach_private (scaled_font, &font_private->base, ctx,
+                                      _cairo_script_scaled_font_fini);
+
+    font_private->parent = scaled_font;
+    font_private->subset_glyph_index = 0;
+    font_private->has_sfnt = TRUE;
+
+    cairo_list_add (&font_private->link, &ctx->fonts);
+
+    status = _bitmap_next_id (&ctx->font_id,
+                             &font_private->id);
+    if (unlikely (status)) {
+       free (font_private);
+       return status;
+    }
+
+    status = _emit_context (surface);
+    if (unlikely (status)) {
+       free (font_private);
+       return status;
+    }
+
+    status = _emit_type42_font (surface, scaled_font);
+    if (status != CAIRO_INT_STATUS_UNSUPPORTED) {
+       *font_out = font_private;
+       return status;
+    }
+
+    font_private->has_sfnt = FALSE;
+    _cairo_output_stream_printf (ctx->stream,
+                                "dict\n"
+                                "  /type 3 set\n"
+                                "  /metrics [%f %f %f %f %f] set\n"
+                                "  /glyphs array set\n"
+                                "  font dup /f%lu exch def set-font-face",
+                                scaled_font->fs_extents.ascent,
+                                scaled_font->fs_extents.descent,
+                                scaled_font->fs_extents.height,
+                                scaled_font->fs_extents.max_x_advance,
+                                scaled_font->fs_extents.max_y_advance,
+                                font_private->id);
+
+    *font_out = font_private;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_emit_scaled_font (cairo_script_surface_t *surface,
+                  cairo_scaled_font_t *scaled_font)
+{
+    cairo_script_context_t *ctx = to_context (surface);
+    cairo_matrix_t matrix;
+    cairo_font_options_t options;
+    cairo_bool_t matrix_updated = FALSE;
+    cairo_status_t status;
+    cairo_script_font_t *font_private;
+
+    cairo_scaled_font_get_ctm (scaled_font, &matrix);
+    status = _emit_scaling_matrix (surface, &matrix, &matrix_updated);
+    if (unlikely (status))
+       return status;
+
+    if (! matrix_updated && surface->cr.current_scaled_font == scaled_font)
+       return CAIRO_STATUS_SUCCESS;
+
+    surface->cr.current_scaled_font = scaled_font;
+
+    font_private = _cairo_script_font_get (ctx, scaled_font);
+    if (font_private == NULL) {
+       cairo_scaled_font_get_font_matrix (scaled_font, &matrix);
+       status = _emit_font_matrix (surface, &matrix);
+       if (unlikely (status))
+           return status;
+
+       cairo_scaled_font_get_font_options (scaled_font, &options);
+       status = _emit_font_options (surface, &options);
+       if (unlikely (status))
+           return status;
+
+       status = _emit_scaled_font_init (surface, scaled_font, &font_private);
+       if (unlikely (status))
+           return status;
+
+       assert (target_is_active (surface));
+       _cairo_output_stream_printf (ctx->stream,
+                                    " /scaled-font get /sf%lu exch def\n",
+                                    font_private->id);
+    } else {
+       _cairo_output_stream_printf (ctx->stream,
+                                    "sf%lu set-scaled-font\n",
+                                    font_private->id);
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_emit_scaled_glyph_vector (cairo_script_surface_t *surface,
+                          cairo_scaled_font_t *scaled_font,
+                          cairo_script_font_t *font_private,
+                          cairo_scaled_glyph_t *scaled_glyph)
+{
+    cairo_script_context_t *ctx = to_context (surface);
+    cairo_script_implicit_context_t old_cr;
+    cairo_status_t status;
+    unsigned long index;
+
+    index = ++font_private->subset_glyph_index;
+    scaled_glyph->dev_private_key = ctx;
+    scaled_glyph->dev_private = (void *) index;
+
+    _cairo_output_stream_printf (ctx->stream,
+                                "%lu <<\n"
+                                "  /metrics [%f %f %f %f %f %f]\n"
+                                "  /render {\n",
+                                index,
+                                scaled_glyph->fs_metrics.x_bearing,
+                                scaled_glyph->fs_metrics.y_bearing,
+                                scaled_glyph->fs_metrics.width,
+                                scaled_glyph->fs_metrics.height,
+                                scaled_glyph->fs_metrics.x_advance,
+                                scaled_glyph->fs_metrics.y_advance);
+
+    if (! _cairo_matrix_is_identity (&scaled_font->scale_inverse)) {
+       _cairo_output_stream_printf (ctx->stream,
+                                    "[%f %f %f %f %f %f] transform\n",
+                                    scaled_font->scale_inverse.xx,
+                                    scaled_font->scale_inverse.yx,
+                                    scaled_font->scale_inverse.xy,
+                                    scaled_font->scale_inverse.yy,
+                                    scaled_font->scale_inverse.x0,
+                                    scaled_font->scale_inverse.y0);
+    }
+
+    old_cr = surface->cr;
+    _cairo_script_implicit_context_init (&surface->cr);
+    status = _cairo_recording_surface_replay (scaled_glyph->recording_surface,
+                                             &surface->base);
+    surface->cr = old_cr;
+
+    _cairo_output_stream_puts (ctx->stream, "} >> set\n");
+
+    return status;
+}
+
+static cairo_status_t
+_emit_scaled_glyph_bitmap (cairo_script_surface_t *surface,
+                          cairo_scaled_font_t *scaled_font,
+                          cairo_script_font_t *font_private,
+                          cairo_scaled_glyph_t *scaled_glyph)
+{
+    cairo_script_context_t *ctx = to_context (surface);
+    cairo_status_t status;
+    unsigned long index;
+
+    index = ++font_private->subset_glyph_index;
+    scaled_glyph->dev_private_key = ctx;
+    scaled_glyph->dev_private = (void *) index;
+
+    _cairo_output_stream_printf (ctx->stream,
+                                "%lu <<\n"
+                                "  /metrics [%f %f %f %f %f %f]\n"
+                                "  /render {\n"
+                                "%f %f translate\n",
+                                index,
+                                scaled_glyph->fs_metrics.x_bearing,
+                                scaled_glyph->fs_metrics.y_bearing,
+                                scaled_glyph->fs_metrics.width,
+                                scaled_glyph->fs_metrics.height,
+                                scaled_glyph->fs_metrics.x_advance,
+                                scaled_glyph->fs_metrics.y_advance,
+                                scaled_glyph->fs_metrics.x_bearing,
+                                scaled_glyph->fs_metrics.y_bearing);
+
+    status = _emit_image_surface (surface, scaled_glyph->surface);
+    if (unlikely (status))
+       return status;
+
+    _cairo_output_stream_puts (ctx->stream, "pattern ");
+
+    if (! _cairo_matrix_is_identity (&scaled_font->font_matrix)) {
+       _cairo_output_stream_printf (ctx->stream,
+                                    "\n  [%f %f %f %f %f %f] set-matrix\n",
+                                    scaled_font->font_matrix.xx,
+                                    scaled_font->font_matrix.yx,
+                                    scaled_font->font_matrix.xy,
+                                    scaled_font->font_matrix.yy,
+                                    scaled_font->font_matrix.x0,
+                                    scaled_font->font_matrix.y0);
+    }
+    _cairo_output_stream_puts (ctx->stream,
+                                "mask\n} >> set\n");
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_emit_scaled_glyph_prologue (cairo_script_surface_t *surface,
+                            cairo_scaled_font_t *scaled_font)
+{
+    cairo_script_context_t *ctx = to_context (surface);
+
+    _cairo_output_stream_printf (ctx->stream, "f%lu /glyphs get\n",
+                                _cairo_script_font_id (ctx, scaled_font));
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_emit_scaled_glyphs (cairo_script_surface_t *surface,
+                    cairo_scaled_font_t *scaled_font,
+                    cairo_glyph_t *glyphs,
+                    unsigned int num_glyphs)
+{
+    cairo_script_context_t *ctx = to_context (surface);
+    cairo_script_font_t *font_private;
+    cairo_status_t status;
+    unsigned int n;
+    cairo_bool_t have_glyph_prologue = FALSE;
+
+    if (num_glyphs == 0)
+       return CAIRO_STATUS_SUCCESS;
+
+    font_private = _cairo_script_font_get (ctx, scaled_font);
+    if (font_private->has_sfnt)
+       return CAIRO_STATUS_SUCCESS;
+
+    _cairo_scaled_font_freeze_cache (scaled_font);
+    for (n = 0; n < num_glyphs; n++) {
+       cairo_scaled_glyph_t *scaled_glyph;
+
+       status = _cairo_scaled_glyph_lookup (scaled_font,
+                                            glyphs[n].index,
+                                            CAIRO_SCALED_GLYPH_INFO_METRICS,
+                                            &scaled_glyph);
+       if (unlikely (status))
+           break;
+
+       if (scaled_glyph->dev_private_key == ctx)
+           continue;
+
+       status = _cairo_scaled_glyph_lookup (scaled_font,
+                                            glyphs[n].index,
+                                            CAIRO_SCALED_GLYPH_INFO_RECORDING_SURFACE,
+                                            &scaled_glyph);
+       if (_cairo_status_is_error (status))
+           break;
+
+       if (status == CAIRO_STATUS_SUCCESS) {
+           if (! have_glyph_prologue) {
+               status = _emit_scaled_glyph_prologue (surface, scaled_font);
+               if (unlikely (status))
+                   break;
+
+               have_glyph_prologue = TRUE;
+           }
+
+           status = _emit_scaled_glyph_vector (surface,
+                                               scaled_font, font_private,
+                                               scaled_glyph);
+           if (unlikely (status))
+               break;
+
+           continue;
+       }
+
+       status = _cairo_scaled_glyph_lookup (scaled_font,
+                                            glyphs[n].index,
+                                            CAIRO_SCALED_GLYPH_INFO_SURFACE,
+                                            &scaled_glyph);
+       if (_cairo_status_is_error (status))
+           break;
+
+       if (status == CAIRO_STATUS_SUCCESS) {
+           if (! have_glyph_prologue) {
+               status = _emit_scaled_glyph_prologue (surface, scaled_font);
+               if (unlikely (status))
+                   break;
+
+               have_glyph_prologue = TRUE;
+           }
+
+           status = _emit_scaled_glyph_bitmap (surface,
+                                               scaled_font,
+                                               font_private,
+                                               scaled_glyph);
+           if (unlikely (status))
+               break;
+
+           continue;
+       }
+    }
+    _cairo_scaled_font_thaw_cache (scaled_font);
+
+    if (have_glyph_prologue) {
+       _cairo_output_stream_puts (to_context (surface)->stream, "pop pop\n");
+    }
+
+    return status;
+}
+
+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 (cairo_script_surface_t *surface,
+                     const char *utf8, int len)
+{
+    cairo_script_context_t *ctx = to_context (surface);
+    char c;
+    const char *end;
+
+    _cairo_output_stream_puts (ctx->stream, "(");
+
+    if (utf8 == NULL) {
+       end = utf8;
+    } else {
+       if (len < 0)
+           len = strlen (utf8);
+       end = utf8 + len;
+    }
+
+    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:
+           _cairo_output_stream_printf (ctx->stream, "\\%c", c);
+           break;
+       default:
+           if (isprint (c) || isspace (c)) {
+               _cairo_output_stream_printf (ctx->stream, "%c", c);
+           } else {
+               char buf[4] = { '\\' };
+
+               to_octal (c, buf+1, 3);
+               _cairo_output_stream_write (ctx->stream, buf, 4);
+           }
+           break;
+       }
+    }
+    _cairo_output_stream_puts (ctx->stream, ")");
+}
+
+static cairo_int_status_t
+_cairo_script_surface_show_text_glyphs (void                       *abstract_surface,
+                                       cairo_operator_t             op,
+                                       const cairo_pattern_t       *source,
+                                       const char                  *utf8,
+                                       int                          utf8_len,
+                                       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         *scaled_font,
+                                       const cairo_clip_t          *clip)
+{
+    cairo_script_surface_t *surface = abstract_surface;
+    cairo_script_context_t *ctx = to_context (surface);
+    cairo_script_font_t *font_private;
+    cairo_scaled_glyph_t *scaled_glyph;
+    cairo_matrix_t matrix;
+    cairo_status_t status;
+    double x, y, ix, iy;
+    int n;
+    cairo_output_stream_t *base85_stream = NULL;
+
+    status = active (surface);
+    if (unlikely (status))
+       return status;
+
+    status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
+    if (unlikely (status))
+       goto BAIL;
+
+    status = _emit_context (surface);
+    if (unlikely (status))
+       goto BAIL;
+
+    status = _emit_source (surface, op, source);
+    if (unlikely (status))
+       goto BAIL;
+
+    status = _emit_scaled_font (surface, scaled_font);
+    if (unlikely (status))
+       goto BAIL;
+
+    status = _emit_operator (surface, op);
+    if (unlikely (status))
+       goto BAIL;
+
+    status = _emit_scaled_glyphs (surface, scaled_font, glyphs, num_glyphs);
+    if (unlikely (status))
+       goto BAIL;
+
+    /* (utf8) [cx cy [glyphs]] [clusters] backward show_text_glyphs */
+    /* [cx cy [glyphs]] show_glyphs */
+
+    if (utf8 != NULL && clusters != NULL) {
+       _emit_string_literal (surface, utf8, utf8_len);
+       _cairo_output_stream_puts (ctx->stream, " ");
+    }
+
+    matrix = surface->cr.current_ctm;
+    status = cairo_matrix_invert (&matrix);
+    assert (status == CAIRO_STATUS_SUCCESS);
+
+    ix = x = glyphs[0].x;
+    iy = y = glyphs[0].y;
+    cairo_matrix_transform_point (&matrix, &ix, &iy);
+    ix -= scaled_font->font_matrix.x0;
+    iy -= scaled_font->font_matrix.y0;
+
+    _cairo_scaled_font_freeze_cache (scaled_font);
+    font_private = _cairo_script_font_get (ctx, scaled_font);
+
+    _cairo_output_stream_printf (ctx->stream,
+                                "[%f %f ",
+                                ix, iy);
+
+    for (n = 0; n < num_glyphs; n++) {
+       if (font_private->has_sfnt) {
+           if (glyphs[n].index > 256)
+               break;
+       } else {
+           status = _cairo_scaled_glyph_lookup (scaled_font,
+                                                glyphs[n].index,
+                                                CAIRO_SCALED_GLYPH_INFO_METRICS,
+                                                &scaled_glyph);
+           if (unlikely (status)) {
+               _cairo_scaled_font_thaw_cache (scaled_font);
+               goto BAIL;
+           }
+
+           if ((long unsigned) scaled_glyph->dev_private > 256)
+               break;
+       }
+    }
+
+    if (n == num_glyphs) {
+       _cairo_output_stream_puts (ctx->stream, "<~");
+       base85_stream = _cairo_base85_stream_create (ctx->stream);
+    } else
+       _cairo_output_stream_puts (ctx->stream, "[");
+
+    for (n = 0; n < num_glyphs; n++) {
+       double dx, dy;
+
+       status = _cairo_scaled_glyph_lookup (scaled_font,
+                                            glyphs[n].index,
+                                            CAIRO_SCALED_GLYPH_INFO_METRICS,
+                                            &scaled_glyph);
+       if (unlikely (status))
+           goto BAIL;
+
+       if (fabs (glyphs[n].x - x) > 1e-5 || fabs (glyphs[n].y - y) > 1e-5) {
+           if (fabs (glyphs[n].y - y) < 1e-5) {
+               if (base85_stream != NULL) {
+                   status = _cairo_output_stream_destroy (base85_stream);
+                   if (unlikely (status)) {
+                       base85_stream = NULL;
+                       break;
+                   }
+
+                   _cairo_output_stream_printf (ctx->stream,
+                                                "~> %f <~", glyphs[n].x - x);
+                   base85_stream = _cairo_base85_stream_create (ctx->stream);
+               } else {
+                   _cairo_output_stream_printf (ctx->stream,
+                                                " ] %f [ ", glyphs[n].x - x);
+               }
+
+               x = glyphs[n].x;
+           } else {
+               ix = x = glyphs[n].x;
+               iy = y = glyphs[n].y;
+               cairo_matrix_transform_point (&matrix, &ix, &iy);
+               ix -= scaled_font->font_matrix.x0;
+               iy -= scaled_font->font_matrix.y0;
+               if (base85_stream != NULL) {
+                   status = _cairo_output_stream_destroy (base85_stream);
+                   if (unlikely (status)) {
+                       base85_stream = NULL;
+                       break;
+                   }
+
+                   _cairo_output_stream_printf (ctx->stream,
+                                                "~> %f %f <~",
+                                                ix, iy);
+                   base85_stream = _cairo_base85_stream_create (ctx->stream);
+               } else {
+                   _cairo_output_stream_printf (ctx->stream,
+                                                " ] %f %f [ ",
+                                                ix, iy);
+               }
+           }
+       }
+       if (base85_stream != NULL) {
+           uint8_t c;
+
+           if (font_private->has_sfnt)
+               c = glyphs[n].index;
+           else
+               c = (uint8_t) (long unsigned) scaled_glyph->dev_private;
+
+           _cairo_output_stream_write (base85_stream, &c, 1);
+       } else {
+           if (font_private->has_sfnt)
+               _cairo_output_stream_printf (ctx->stream, " %lu",
+                                            glyphs[n].index);
+           else
+               _cairo_output_stream_printf (ctx->stream, " %lu",
+                                            (long unsigned) scaled_glyph->dev_private);
+       }
+
+        dx = scaled_glyph->metrics.x_advance;
+        dy = scaled_glyph->metrics.y_advance;
+       cairo_matrix_transform_distance (&scaled_font->ctm, &dx, &dy);
+       x += dx;
+       y += dy;
+    }
+    _cairo_scaled_font_thaw_cache (scaled_font);
+
+    if (base85_stream != NULL) {
+       cairo_status_t status2;
+
+       status2 = _cairo_output_stream_destroy (base85_stream);
+       if (status == CAIRO_STATUS_SUCCESS)
+           status = status2;
+
+       _cairo_output_stream_printf (ctx->stream, "~>");
+    } else {
+       _cairo_output_stream_puts (ctx->stream, " ]");
+    }
+    if (unlikely (status))
+       return status;
+
+    if (utf8 != NULL && clusters != NULL) {
+       for (n = 0; n < num_clusters; n++) {
+           if (clusters[n].num_bytes > UCHAR_MAX ||
+               clusters[n].num_glyphs > UCHAR_MAX)
+           {
+               break;
+           }
+       }
+
+       if (n < num_clusters) {
+           _cairo_output_stream_puts (ctx->stream, "] [ ");
+           for (n = 0; n < num_clusters; n++) {
+               _cairo_output_stream_printf (ctx->stream,
+                                            "%d %d ",
+                                            clusters[n].num_bytes,
+                                            clusters[n].num_glyphs);
+           }
+           _cairo_output_stream_puts (ctx->stream, "]");
+       }
+       else
+       {
+           _cairo_output_stream_puts (ctx->stream, "] <~");
+           base85_stream = _cairo_base85_stream_create (ctx->stream);
+           for (n = 0; n < num_clusters; n++) {
+               uint8_t c[2];
+               c[0] = clusters[n].num_bytes;
+               c[1] = clusters[n].num_glyphs;
+               _cairo_output_stream_write (base85_stream, c, 2);
+           }
+           status = _cairo_output_stream_destroy (base85_stream);
+           if (unlikely (status))
+               goto BAIL;
+
+           _cairo_output_stream_puts (ctx->stream, "~>");
+       }
+
+       _cairo_output_stream_printf (ctx->stream,
+                                    " //%s show-text-glyphs\n",
+                                    _direction_to_string (backward));
+    } else {
+       _cairo_output_stream_puts (ctx->stream,
+                                  "] show-glyphs\n");
+    }
+
+    inactive (surface);
+
+    if (_cairo_surface_wrapper_is_active (&surface->wrapper)){
+       return _cairo_surface_wrapper_show_text_glyphs (&surface->wrapper,
+                                                       op, source,
+                                                       utf8, utf8_len,
+                                                       glyphs, num_glyphs,
+                                                       clusters, num_clusters,
+                                                       backward,
+                                                       scaled_font,
+                                                       clip);
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+
+BAIL:
+    inactive (surface);
+    return status;
+}
+
+static cairo_bool_t
+_cairo_script_surface_get_extents (void *abstract_surface,
+                                  cairo_rectangle_int_t *rectangle)
+{
+    cairo_script_surface_t *surface = abstract_surface;
+
+    if (_cairo_surface_wrapper_is_active (&surface->wrapper)) {
+       return _cairo_surface_wrapper_get_extents (&surface->wrapper,
+                                                  rectangle);
+    }
+
+    if (surface->width < 0 || surface->height < 0)
+       return FALSE;
+
+    rectangle->x = 0;
+    rectangle->y = 0;
+    rectangle->width = surface->width;
+    rectangle->height = surface->height;
+
+    return TRUE;
+}
+
+static const cairo_surface_backend_t
+_cairo_script_surface_backend = {
+    CAIRO_SURFACE_TYPE_SCRIPT,
+    _cairo_script_surface_finish,
+
+    _cairo_default_context_create,
+
+    _cairo_script_surface_create_similar,
+    NULL, /* create similar image */
+    NULL, /* map to image */
+    NULL, /* unmap image */
+
+    _cairo_script_surface_acquire_source_image,
+    _cairo_script_surface_release_source_image,
+    _cairo_script_surface_snapshot,
+
+    _cairo_script_surface_copy_page,
+    _cairo_script_surface_show_page,
+
+    _cairo_script_surface_get_extents,
+    NULL, /* get_font_options */
+
+    NULL, /* flush */
+    NULL, /* mark_dirty_rectangle */
+
+    _cairo_script_surface_paint,
+    _cairo_script_surface_mask,
+    _cairo_script_surface_stroke,
+    _cairo_script_surface_fill,
+    NULL, /* fill/stroke */
+    NULL, /* glyphs */
+    _cairo_script_surface_has_show_text_glyphs,
+    _cairo_script_surface_show_text_glyphs
+};
+
+static void
+_cairo_script_implicit_context_init (cairo_script_implicit_context_t *cr)
+{
+    cr->current_operator = CAIRO_GSTATE_OPERATOR_DEFAULT;
+    cr->current_fill_rule = CAIRO_GSTATE_FILL_RULE_DEFAULT;
+    cr->current_tolerance = CAIRO_GSTATE_TOLERANCE_DEFAULT;
+    cr->current_antialias = CAIRO_ANTIALIAS_DEFAULT;
+    _cairo_stroke_style_init (&cr->current_style);
+    _cairo_pattern_init_solid (&cr->current_source.solid,
+                              CAIRO_COLOR_BLACK);
+    _cairo_path_fixed_init (&cr->current_path);
+    cairo_matrix_init_identity (&cr->current_ctm);
+    cairo_matrix_init_identity (&cr->current_stroke_matrix);
+    cairo_matrix_init_identity (&cr->current_font_matrix);
+    _cairo_font_options_init_default (&cr->current_font_options);
+    cr->current_scaled_font = NULL;
+    cr->has_clip = FALSE;
+}
+
+static void
+_cairo_script_implicit_context_reset (cairo_script_implicit_context_t *cr)
+{
+    free (cr->current_style.dash);
+    cr->current_style.dash = NULL;
+
+    _cairo_pattern_fini (&cr->current_source.base);
+    _cairo_path_fixed_fini (&cr->current_path);
+
+    _cairo_script_implicit_context_init (cr);
+}
+
+static cairo_script_surface_t *
+_cairo_script_surface_create_internal (cairo_script_context_t *ctx,
+                                      cairo_content_t content,
+                                      cairo_rectangle_t *extents,
+                                      cairo_surface_t *passthrough)
+{
+    cairo_script_surface_t *surface;
+
+    if (unlikely (ctx == NULL))
+       return (cairo_script_surface_t *) _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NULL_POINTER));
+
+    surface = malloc (sizeof (cairo_script_surface_t));
+    if (unlikely (surface == NULL))
+       return (cairo_script_surface_t *) _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+    _cairo_surface_init (&surface->base,
+                        &_cairo_script_surface_backend,
+                        &ctx->base,
+                        content);
+
+    _cairo_surface_wrapper_init (&surface->wrapper, passthrough);
+
+    _cairo_surface_clipper_init (&surface->clipper,
+                                _cairo_script_surface_clipper_intersect_clip_path);
+
+    surface->width = surface->height = -1;
+    if (extents) {
+       surface->width = extents->width;
+       surface->height = extents->height;
+       cairo_surface_set_device_offset (&surface->base,
+                                        -extents->x, -extents->y);
+    }
+
+    surface->emitted = FALSE;
+    surface->defined = FALSE;
+    surface->active = FALSE;
+    surface->operand.type = SURFACE;
+    cairo_list_init (&surface->operand.link);
+
+    _cairo_script_implicit_context_init (&surface->cr);
+
+    return surface;
+}
+
+static const cairo_device_backend_t _cairo_script_device_backend = {
+    CAIRO_DEVICE_TYPE_SCRIPT,
+
+    NULL, NULL, /* lock, unlock */
+
+    _device_flush,  /* flush */
+    NULL,  /* finish */
+    _device_destroy
+};
+
+cairo_device_t *
+_cairo_script_context_create_internal (cairo_output_stream_t *stream)
+{
+    cairo_script_context_t *ctx;
+
+    ctx = malloc (sizeof (cairo_script_context_t));
+    if (unlikely (ctx == NULL))
+       return _cairo_device_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+    memset (ctx, 0, sizeof (cairo_script_context_t));
+
+    _cairo_device_init (&ctx->base, &_cairo_script_device_backend);
+
+    cairo_list_init (&ctx->operands);
+    cairo_list_init (&ctx->deferred);
+    ctx->stream = stream;
+    ctx->mode = CAIRO_SCRIPT_MODE_ASCII;
+
+    cairo_list_init (&ctx->fonts);
+    cairo_list_init (&ctx->defines);
+
+    ctx->attach_snapshots = TRUE;
+
+    return &ctx->base;
+}
+
+void
+_cairo_script_context_attach_snapshots (cairo_device_t *device,
+                                       cairo_bool_t enable)
+{
+    cairo_script_context_t *ctx;
+
+    ctx = (cairo_script_context_t *) device;
+    ctx->attach_snapshots = enable;
+}
+
+static cairo_device_t *
+_cairo_script_context_create (cairo_output_stream_t *stream)
+{
+    cairo_script_context_t *ctx;
+
+    ctx = (cairo_script_context_t *)
+       _cairo_script_context_create_internal (stream);
+    if (unlikely (ctx->base.status))
+       return &ctx->base;
+
+    ctx->owns_stream = TRUE;
+    _cairo_output_stream_puts (ctx->stream, "%!CairoScript\n");
+    return &ctx->base;
+}
+
+cairo_device_t *
+cairo_script_create (const char *filename)
+{
+    cairo_output_stream_t *stream;
+    cairo_status_t status;
+
+    stream = _cairo_output_stream_create_for_filename (filename);
+    if ((status = _cairo_output_stream_get_status (stream)))
+       return _cairo_device_create_in_error (status);
+
+    return _cairo_script_context_create (stream);
+}
+
+cairo_device_t *
+cairo_script_create_for_stream (cairo_write_func_t      write_func,
+                               void                    *closure)
+{
+    cairo_output_stream_t *stream;
+    cairo_status_t status;
+
+    stream = _cairo_output_stream_create (write_func, NULL, closure);
+    if ((status = _cairo_output_stream_get_status (stream)))
+       return _cairo_device_create_in_error (status);
+
+    return _cairo_script_context_create (stream);
+}
+
+void
+cairo_script_write_comment (cairo_device_t *device,
+                           const char *comment,
+                           int len)
+{
+    cairo_script_context_t *context = (cairo_script_context_t *) device;
+
+    if (len < 0)
+       len = strlen (comment);
+
+    _cairo_output_stream_puts (context->stream, "% ");
+    _cairo_output_stream_write (context->stream, comment, len);
+    _cairo_output_stream_puts (context->stream, "\n");
+}
+
+void
+cairo_script_set_mode (cairo_device_t *device,
+                      cairo_script_mode_t mode)
+{
+    cairo_script_context_t *context = (cairo_script_context_t *) device;
+
+    context->mode = mode;
+}
+
+cairo_script_mode_t
+cairo_script_get_mode (cairo_device_t *device)
+{
+    cairo_script_context_t *context = (cairo_script_context_t *) device;
+
+    return context->mode;
+}
+
+cairo_surface_t *
+cairo_script_surface_create (cairo_device_t *device,
+                            cairo_content_t content,
+                            double width,
+                            double height)
+{
+    cairo_rectangle_t *extents, r;
+
+    if (unlikely (device->backend->type != CAIRO_DEVICE_TYPE_SCRIPT))
+       return _cairo_surface_create_in_error (CAIRO_STATUS_DEVICE_TYPE_MISMATCH);
+
+    if (unlikely (device->status))
+       return _cairo_surface_create_in_error (device->status);
+
+    extents = NULL;
+    if (width > 0 && height > 0) {
+       r.x = r.y = 0;
+       r.width  = width;
+       r.height = height;
+       extents = &r;
+    }
+    return &_cairo_script_surface_create_internal ((cairo_script_context_t *) device,
+                                                  content, extents,
+                                                  NULL)->base;
+}
+slim_hidden_def (cairo_script_surface_create);
+
+cairo_surface_t *
+cairo_script_surface_create_for_target (cairo_device_t *device,
+                                       cairo_surface_t *target)
+{
+    cairo_rectangle_int_t extents;
+    cairo_rectangle_t rect, *r;
+
+    if (unlikely (device->backend->type != CAIRO_DEVICE_TYPE_SCRIPT))
+       return _cairo_surface_create_in_error (CAIRO_STATUS_DEVICE_TYPE_MISMATCH);
+
+    if (unlikely (device->status))
+       return _cairo_surface_create_in_error (device->status);
+
+    if (unlikely (target->status))
+       return _cairo_surface_create_in_error (target->status);
+
+    r = NULL;
+    if (_cairo_surface_get_extents (target, &extents)) {
+       rect.x = rect.y = 0;
+       rect.width = extents.width;
+       rect.height = extents.height;
+       r= &rect;
+    }
+    return &_cairo_script_surface_create_internal ((cairo_script_context_t *) device,
+                                                  target->content, r,
+                                                  target)->base;
+}
+
+cairo_status_t
+cairo_script_from_recording_surface (cairo_device_t *device,
+                                    cairo_surface_t *recording_surface)
+{
+    cairo_rectangle_t r, *extents;
+    cairo_surface_t *surface;
+    cairo_status_t status;
+
+    if (unlikely (device->backend->type != CAIRO_DEVICE_TYPE_SCRIPT))
+       return _cairo_error (CAIRO_STATUS_DEVICE_TYPE_MISMATCH);
+
+    if (unlikely (device->status))
+       return _cairo_error (device->status);
+
+    if (unlikely (recording_surface->status))
+       return recording_surface->status;
+
+    if (unlikely (! _cairo_surface_is_recording (recording_surface)))
+       return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+
+    extents = NULL;
+    if (_cairo_recording_surface_get_bounds (recording_surface, &r))
+       extents = &r;
+
+    surface = &_cairo_script_surface_create_internal ((cairo_script_context_t *) device,
+                                                     recording_surface->content,
+                                                     extents,
+                                                     NULL)->base;
+    if (unlikely (surface->status))
+       return surface->status;
+
+    status = _cairo_recording_surface_replay (recording_surface, surface);
+    cairo_surface_destroy (surface);
+
+    return status;
+}
diff --git a/src/cairo-script.h b/src/cairo-script.h
new file mode 100644 (file)
index 0000000..b82230f
--- /dev/null
@@ -0,0 +1,89 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2008 Chris Wilson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Chris Wilson
+ *
+ * Contributor(s):
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#ifndef CAIRO_SCRIPT_H
+#define CAIRO_SCRIPT_H
+
+#include "cairo.h"
+
+#if CAIRO_HAS_SCRIPT_SURFACE
+
+CAIRO_BEGIN_DECLS
+
+typedef enum {
+    CAIRO_SCRIPT_MODE_BINARY,
+    CAIRO_SCRIPT_MODE_ASCII
+} cairo_script_mode_t;
+
+cairo_public cairo_device_t *
+cairo_script_create (const char *filename);
+
+cairo_public cairo_device_t *
+cairo_script_create_for_stream (cairo_write_func_t      write_func,
+                               void                    *closure);
+
+cairo_public void
+cairo_script_write_comment (cairo_device_t *script,
+                           const char *comment,
+                           int len);
+
+cairo_public void
+cairo_script_set_mode (cairo_device_t *script,
+                      cairo_script_mode_t mode);
+
+cairo_public cairo_script_mode_t
+cairo_script_get_mode (cairo_device_t *script);
+
+cairo_public cairo_surface_t *
+cairo_script_surface_create (cairo_device_t *script,
+                            cairo_content_t content,
+                            double width,
+                            double height);
+
+cairo_public cairo_surface_t *
+cairo_script_surface_create_for_target (cairo_device_t *script,
+                                       cairo_surface_t *target);
+
+cairo_public cairo_status_t
+cairo_script_from_recording_surface (cairo_device_t    *script,
+                                    cairo_surface_t    *recording_surface);
+
+CAIRO_END_DECLS
+
+#else  /*CAIRO_HAS_SCRIPT_SURFACE*/
+# error Cairo was not compiled with support for the CairoScript backend
+#endif /*CAIRO_HAS_SCRIPT_SURFACE*/
+
+#endif /*CAIRO_SCRIPT_H*/
diff --git a/src/cairo-skia-surface.cpp b/src/cairo-skia-surface.cpp
new file mode 100644 (file)
index 0000000..537c1bc
--- /dev/null
@@ -0,0 +1,1174 @@
+/* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2007 Mozilla Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Mozilla Corporation.
+ *
+ * Contributor(s):
+ *     Vladimir Vukicevic <vladimir@mozilla.com>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-skia.h"
+
+#include "cairo-surface-clipper-private.h"
+
+#include <SkBitmap.h>
+#include <SkCanvas.h>
+#include <SkPaint.h>
+#include <SkPath.h>
+
+#include <SkColorShader.h>
+
+#include <SkGradientShader.h>
+#include <SkDashPathEffect.h>
+
+#if (CAIRO_FIXED_BITS == 32) && (CAIRO_FIXED_FRAC_BITS == 16) && defined(SK_SCALAR_IS_FIXED)
+# define CAIRO_FIXED_TO_SK_SCALAR(x)  (x)
+#elif defined(SK_SCALAR_IS_FIXED)
+/* This can be done better, but this will do for now */
+# define CAIRO_FIXED_TO_SK_SCALAR(x)  SkFloatToScalar(_cairo_fixed_to_double(x))
+#else
+# define CAIRO_FIXED_TO_SK_SCALAR(x)  SkFloatToScalar(_cairo_fixed_to_double(x))
+#endif
+
+#ifndef CAIRO_INT_STATUS_SUCCESS
+# define CAIRO_INT_STATUS_SUCCESS ((cairo_int_status_t) CAIRO_STATUS_SUCCESS)
+#endif
+
+#define CAIRO_MAYBE_UNSUPPORTED CAIRO_INT_STATUS_UNSUPPORTED
+//#define CAIRO_MAYBE_UNSUPPORTED _skia_unsupported ()
+
+static cairo_int_status_t _skia_unsupported () {
+    printf ("unsupported!\n");
+    return CAIRO_INT_STATUS_UNSUPPORTED;
+}
+
+typedef struct cairo_skia_surface {
+    cairo_surface_t base;
+
+    SkBitmap *bitmap;
+    SkCanvas *canvas;
+
+    cairo_surface_clipper_t clipper;
+
+    cairo_image_surface_t *_image_surface; /* wrapper around bitmap */
+} cairo_skia_surface_t;
+
+static cairo_skia_surface_t *
+_cairo_skia_surface_create_internal (SkBitmap::Config config,
+                                    bool opaque,
+                                    unsigned char *data,
+                                    int width,
+                                    int height,
+                                    int stride);
+
+/*
+ * conversion methods
+ */
+
+/*
+ * format conversion
+ */
+static inline bool
+format_to_sk_config (cairo_format_t format,
+                    SkBitmap::Config& config,
+                    bool& opaque)
+{
+    opaque = false;
+
+    switch (format) {
+    case CAIRO_FORMAT_ARGB32:
+       config = SkBitmap::kARGB_8888_Config;
+       break;
+    case CAIRO_FORMAT_RGB24:
+       config = SkBitmap::kARGB_8888_Config;
+       opaque = true;
+       break;
+    case CAIRO_FORMAT_A8:
+       config = SkBitmap::kA8_Config;
+       break;
+    case CAIRO_FORMAT_A1:
+       config = SkBitmap::kA1_Config;
+       break;
+    default:
+       return false;
+    }
+
+    return true;
+}
+
+static inline cairo_format_t
+sk_config_to_format (SkBitmap::Config config,
+                    bool opaque)
+{
+    switch (config) {
+    case SkBitmap::kARGB_8888_Config:
+       if (opaque)
+           return CAIRO_FORMAT_RGB24;
+       return CAIRO_FORMAT_ARGB32;
+
+    case SkBitmap::kA8_Config:
+       return CAIRO_FORMAT_A8;
+
+    case SkBitmap::kA1_Config:
+       return CAIRO_FORMAT_A1;
+
+    case SkBitmap::kNo_Config:
+    case SkBitmap::kIndex8_Config:
+    case SkBitmap::kRLE_Index8_Config:
+    case SkBitmap::kRGB_565_Config:
+    case SkBitmap::kARGB_4444_Config:
+    case SkBitmap::kConfigCount:
+    default:
+       return (cairo_format_t) -1;
+    }
+}
+
+/*
+ * image surface wrapping
+ */
+static inline bool
+surface_to_sk_bitmap (cairo_surface_t *surface, SkBitmap& bitmap)
+{
+    cairo_image_surface_t *img = (cairo_image_surface_t *) surface;
+    SkBitmap::Config config;
+    bool opaque;
+
+    if (unlikely (! format_to_sk_config (img->format, config, opaque)))
+       return false;
+
+    bitmap.reset ();
+    bitmap.setConfig (config, img->width, img->height, img->stride);
+    bitmap.setIsOpaque (opaque);
+    bitmap.setPixels (img->data);
+
+    return true;
+}
+
+/*
+ * operator conversion
+ */
+
+static inline SkXfermode::Mode
+operator_to_sk (cairo_operator_t op)
+{
+    static const SkXfermode::Mode modeMap[] = {
+       SkXfermode::kClear_Mode,
+
+       SkXfermode::kSrc_Mode,
+       SkXfermode::kSrcOver_Mode,
+       SkXfermode::kSrcIn_Mode,
+       SkXfermode::kSrcOut_Mode,
+       SkXfermode::kSrcATop_Mode,
+
+       SkXfermode::kDst_Mode,
+       SkXfermode::kDstOver_Mode,
+       SkXfermode::kDstIn_Mode,
+       SkXfermode::kDstOut_Mode,
+       SkXfermode::kDstATop_Mode,
+
+       SkXfermode::kXor_Mode,
+       SkXfermode::kPlus_Mode, // XXX Add?
+       SkXfermode::kPlus_Mode, // XXX SATURATE
+
+       SkXfermode::kPlus_Mode,
+       SkXfermode::kMultiply_Mode,
+       SkXfermode::kScreen_Mode,
+       SkXfermode::kOverlay_Mode,
+       SkXfermode::kDarken_Mode,
+       SkXfermode::kLighten_Mode,
+       SkXfermode::kColorDodge_Mode,
+       SkXfermode::kColorBurn_Mode,
+       SkXfermode::kHardLight_Mode,
+       SkXfermode::kSoftLight_Mode,
+       SkXfermode::kDifference_Mode,
+       SkXfermode::kExclusion_Mode,
+
+       SkXfermode::kSrcOver_Mode, // XXX: CAIRO_OPERATOR_HSL_HUE
+       SkXfermode::kSrcOver_Mode, // XXX: CAIRO_OPERATOR_HSL_SATURATION,
+       SkXfermode::kSrcOver_Mode, // XXX: CAIRO_OPERATOR_HSL_COLOR,
+       SkXfermode::kSrcOver_Mode, // XXX: CAIRO_OPERATOR_HSL_LUMINOSITY
+    };
+
+    return modeMap[op];
+}
+
+/*
+ * tiling mode conversion
+ */
+static SkShader::TileMode
+extend_to_sk (cairo_extend_t extend)
+{
+    static const SkShader::TileMode modeMap[] = {
+       SkShader::kClamp_TileMode,  // NONE behaves like PAD, because noone wants NONE
+       SkShader::kRepeat_TileMode,
+       SkShader::kMirror_TileMode,
+       SkShader::kClamp_TileMode
+    };
+
+    return modeMap[extend];
+}
+
+/*
+ * color conversion
+ */
+static inline SkColor
+color_to_sk (const cairo_color_t& c)
+{
+    /* Need unpremultiplied 1-byte values */
+    return SkColorSetARGB ((U8CPU) (c.alpha * 255),
+                          (U8CPU) (c.red * 255),
+                          (U8CPU) (c.green * 255),
+                          (U8CPU) (c.blue * 255));
+}
+
+/*
+ * matrix conversion
+ */
+static inline SkMatrix
+matrix_to_sk (const cairo_matrix_t& mat)
+{
+    SkMatrix skm;
+
+    skm.reset ();
+    skm.set (SkMatrix::kMScaleX, SkFloatToScalar (mat.xx));
+    skm.set (SkMatrix::kMSkewX,  SkFloatToScalar (mat.xy));
+    skm.set (SkMatrix::kMTransX, SkFloatToScalar (mat.x0));
+    skm.set (SkMatrix::kMSkewY,  SkFloatToScalar (mat.yx));
+    skm.set (SkMatrix::kMScaleY, SkFloatToScalar (mat.yy));
+    skm.set (SkMatrix::kMTransY, SkFloatToScalar (mat.y0));
+
+    /*
+    skm[6] = SkFloatToScalar (0.0);
+    skm[7] = SkFloatToScalar (0.0);
+    skm[8] = SkFloatToScalar (1.0); -- this isn't right, it wants a magic value in there that it'll set itself.  It wants Sk_Fract1 (2.30), not Sk_Scalar1
+    */
+
+    return skm;
+}
+
+static inline SkMatrix
+matrix_inverse_to_sk (const cairo_matrix_t& mat)
+{
+    cairo_matrix_t inv = mat;
+    cairo_status_t status = cairo_matrix_invert (&inv);
+    assert (status == CAIRO_STATUS_SUCCESS);
+    return matrix_to_sk (inv);
+}
+
+/*
+ * pattern conversion
+ */
+static inline cairo_surface_t *
+surface_from_pattern (const cairo_pattern_t *pattern)
+{
+    return (reinterpret_cast <const cairo_surface_pattern_t *> (pattern))->surface;
+}
+
+static SkShader*
+pattern_to_sk_shader (cairo_skia_surface_t *dst, const cairo_pattern_t *pattern,
+                     cairo_image_surface_t **image, void **image_extra)
+{
+    SkShader *shader = NULL;
+
+    if (pattern->type == CAIRO_PATTERN_TYPE_SOLID) {
+       cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) pattern;
+       return new SkColorShader (color_to_sk (solid->color));
+    } else if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) {
+       cairo_surface_t *surface = surface_from_pattern (pattern);
+
+       if (surface->type == CAIRO_SURFACE_TYPE_SKIA) {
+           cairo_skia_surface_t *esurf = (cairo_skia_surface_t *) surface;
+
+           shader = SkShader::CreateBitmapShader (*esurf->bitmap,
+                                                  extend_to_sk (pattern->extend),
+                                                  extend_to_sk (pattern->extend));
+       } else {
+           SkBitmap bitmap;
+
+           if (! _cairo_surface_is_image (surface)) {
+               cairo_status_t status;
+
+               status = _cairo_surface_acquire_source_image (surface,
+                                                             image, image_extra);
+               if (status)
+                   return NULL;
+
+               surface = &(*image)->base;
+           }
+
+
+           if (unlikely (! surface_to_sk_bitmap (surface, bitmap)))
+               return NULL;
+
+           shader = SkShader::CreateBitmapShader (bitmap,
+                                                  extend_to_sk (pattern->extend),
+                                                  extend_to_sk (pattern->extend));
+       }
+    } else if (pattern->type == CAIRO_PATTERN_TYPE_LINEAR
+              /* || pattern->type == CAIRO_PATTERN_TYPE_RADIAL */)
+    {
+       cairo_gradient_pattern_t *gradient = (cairo_gradient_pattern_t *) pattern;
+       SkColor colors_stack[10];
+       SkScalar pos_stack[10];
+       SkColor *colors = colors_stack;
+       SkScalar *pos = pos_stack;
+
+       if (gradient->n_stops > 10) {
+           colors = new SkColor[gradient->n_stops];
+           pos = new SkScalar[gradient->n_stops];
+       }
+
+       for (unsigned int i = 0; i < gradient->n_stops; i++) {
+           pos[i] = CAIRO_FIXED_TO_SK_SCALAR (gradient->stops[i].offset);
+           colors[i] = color_to_sk (gradient->stops[i].color);
+       }
+
+       if (pattern->type == CAIRO_PATTERN_TYPE_LINEAR) {
+           cairo_linear_pattern_t *linear = (cairo_linear_pattern_t *) gradient;
+           SkPoint points[2];
+
+           points[0].set (SkFloatToScalar (linear->pd1.x),
+                          SkFloatToScalar (linear->pd1.y));
+           points[1].set (SkFloatToScalar (linear->pd2.x),
+                          SkFloatToScalar (linear->pd2.y));
+           shader = SkGradientShader::CreateLinear (points, colors, pos, gradient->n_stops,
+                                                    extend_to_sk (pattern->extend));
+       } else {
+           // XXX todo -- implement real radial shaders in Skia
+       }
+
+       if (gradient->n_stops > 10) {
+           delete [] colors;
+           delete [] pos;
+       }
+    }
+
+    if (shader && ! _cairo_matrix_is_identity (&pattern->matrix))
+       shader->setLocalMatrix (matrix_inverse_to_sk (pattern->matrix));
+
+    return shader;
+}
+
+static inline bool
+pattern_filter_to_sk (const cairo_pattern_t *pattern)
+{
+    switch (pattern->filter) {
+    case CAIRO_FILTER_GOOD:
+    case CAIRO_FILTER_BEST:
+    case CAIRO_FILTER_BILINEAR:
+    case CAIRO_FILTER_GAUSSIAN:
+       return true;
+    default:
+    case CAIRO_FILTER_FAST:
+    case CAIRO_FILTER_NEAREST:
+       return false;
+    }
+}
+
+static inline bool
+pattern_to_sk_color (const cairo_pattern_t *pattern, SkColor& color)
+{
+    if (pattern->type != CAIRO_PATTERN_TYPE_SOLID)
+       return false;
+
+    color = color_to_sk (((cairo_solid_pattern_t *) pattern)->color);
+    return true;
+}
+
+/*
+ * path conversion
+ */
+
+struct cpc {
+    SkPath skPath;
+    cairo_matrix_t *matrix;
+};
+
+static cairo_status_t
+cpc_move_to (void *closure, const cairo_point_t *point)
+{
+    struct cpc *cpc = static_cast <struct cpc *> (closure);
+    if (cpc->matrix) {
+       double x = _cairo_fixed_to_double (point->x);
+       double y = _cairo_fixed_to_double (point->y);
+       cairo_matrix_transform_point (cpc->matrix, &x, &y);
+       cpc->skPath.moveTo (SkFloatToScalar (x), SkFloatToScalar (y));
+    } else {
+       cpc->skPath.moveTo (CAIRO_FIXED_TO_SK_SCALAR (point->x),
+                           CAIRO_FIXED_TO_SK_SCALAR (point->y));
+    }
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+cpc_line_to (void *closure, const cairo_point_t *point)
+{
+    struct cpc *cpc = static_cast <struct cpc *> (closure);
+    if (cpc->matrix) {
+       double x = _cairo_fixed_to_double (point->x);
+       double y = _cairo_fixed_to_double (point->y);
+       cairo_matrix_transform_point (cpc->matrix, &x, &y);
+       cpc->skPath.lineTo (SkFloatToScalar (x), SkFloatToScalar (y));
+    } else {
+       cpc->skPath.lineTo (CAIRO_FIXED_TO_SK_SCALAR (point->x),
+                           CAIRO_FIXED_TO_SK_SCALAR (point->y));
+    }
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+cpc_curve_to (void *closure,
+             const cairo_point_t *p0,
+             const cairo_point_t *p1,
+             const cairo_point_t *p2)
+{
+    struct cpc *cpc = static_cast <struct cpc *> (closure);
+    if (cpc->matrix) {
+       double x0 = _cairo_fixed_to_double (p0->x);
+       double y0 = _cairo_fixed_to_double (p0->y);
+       double x1 = _cairo_fixed_to_double (p1->x);
+       double y1 = _cairo_fixed_to_double (p1->y);
+       double x2 = _cairo_fixed_to_double (p2->x);
+       double y2 = _cairo_fixed_to_double (p2->y);
+       cairo_matrix_transform_point (cpc->matrix, &x0, &y0);
+       cairo_matrix_transform_point (cpc->matrix, &x1, &y1);
+       cairo_matrix_transform_point (cpc->matrix, &x2, &y2);
+
+       cpc->skPath.cubicTo (SkFloatToScalar (x0),
+                            SkFloatToScalar (y0),
+                            SkFloatToScalar (x1),
+                            SkFloatToScalar (y1),
+                            SkFloatToScalar (x2),
+                            SkFloatToScalar (y2));
+    } else {
+       cpc->skPath.cubicTo (CAIRO_FIXED_TO_SK_SCALAR (p0->x),
+                            CAIRO_FIXED_TO_SK_SCALAR (p0->y),
+                            CAIRO_FIXED_TO_SK_SCALAR (p1->x),
+                            CAIRO_FIXED_TO_SK_SCALAR (p1->y),
+                            CAIRO_FIXED_TO_SK_SCALAR (p2->x),
+                            CAIRO_FIXED_TO_SK_SCALAR (p2->y));
+    }
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+cpc_close_path (void *closure)
+{
+    struct cpc *cpc = static_cast <struct cpc *> (closure);
+    cpc->skPath.close ();
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static inline SkPath
+path_to_sk (cairo_path_fixed_t *path,
+           cairo_matrix_t *mat = NULL)
+{
+    struct cpc data;
+    cairo_status_t status;
+
+    if (mat && _cairo_matrix_is_identity (mat))
+       mat = NULL;
+    data.matrix = mat;
+
+    status = _cairo_path_fixed_interpret (path,
+                                         cpc_move_to,
+                                         cpc_line_to,
+                                         cpc_curve_to,
+                                         cpc_close_path,
+                                         &data);
+    assert (status == CAIRO_STATUS_SUCCESS);
+
+    return data.skPath;
+}
+
+static inline SkPath
+path_to_sk (cairo_path_fixed_t *path,
+           cairo_fill_rule_t fill_rule,
+           cairo_matrix_t *mat = NULL)
+{
+    SkPath skPath = path_to_sk (path, mat);
+
+    if (fill_rule == CAIRO_FILL_RULE_EVEN_ODD)
+       skPath.setFillType (SkPath::kEvenOdd_FillType);
+    else
+       skPath.setFillType (SkPath::kWinding_FillType);
+
+    return skPath;
+}
+
+/*
+ * cairo surface methods
+ */
+
+static cairo_surface_t *
+_cairo_skia_surface_create_similar (void *asurface,
+                                   cairo_content_t content,
+                                   int width,
+                                   int height)
+{
+    SkBitmap::Config config;
+    bool opaque;
+
+    if (! format_to_sk_config (_cairo_format_from_content (content),
+                              config, opaque))
+    {
+       _skia_unsupported ();
+       return NULL;
+    }
+
+    return &_cairo_skia_surface_create_internal (config, opaque,
+                                                NULL,
+                                                width, height,
+                                                0)->base;
+}
+
+static cairo_status_t
+_cairo_skia_surface_finish (void *asurface)
+{
+    cairo_skia_surface_t *surface = (cairo_skia_surface_t *) asurface;
+
+    _cairo_surface_clipper_reset (&surface->clipper);
+    cairo_surface_destroy (&surface->_image_surface->base);
+
+    delete surface->canvas;
+    delete surface->bitmap;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_image_surface_t *
+_get_image_surface (cairo_skia_surface_t *surface)
+{
+    if (! surface->_image_surface) {
+       SkBitmap *bitmap = surface->bitmap;
+       surface->_image_surface = (cairo_image_surface_t *)
+           cairo_image_surface_create_for_data ((unsigned char *) bitmap->getPixels (),
+                                                sk_config_to_format (bitmap->config (),
+                                                                     bitmap->isOpaque ()),
+                                                bitmap->width (),
+                                                bitmap->height (),
+                                                bitmap->rowBytes ());
+    }
+
+    return surface->_image_surface;
+}
+
+static cairo_status_t
+_cairo_skia_surface_acquire_source_image (void *asurface,
+                                         cairo_image_surface_t **image_out,
+                                         void **image_extra)
+{
+    cairo_skia_surface_t *surface = (cairo_skia_surface_t *) asurface;
+    cairo_image_surface_t *image = _get_image_surface (surface);
+
+    if (unlikely (image->base.status))
+       return image->base.status;
+
+    surface->bitmap->lockPixels ();
+
+    *image_out = image;
+    *image_extra = NULL;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_skia_surface_release_source_image (void *asurface,
+                                         cairo_image_surface_t *image,
+                                         void *image_extra)
+{
+    cairo_skia_surface_t *surface = (cairo_skia_surface_t *) asurface;
+
+    surface->bitmap->unlockPixels ();
+}
+
+static cairo_status_t
+_cairo_skia_surface_acquire_dest_image (void *asurface,
+                                       cairo_rectangle_int_t *interest_rect,
+                                       cairo_image_surface_t **image_out,
+                                       cairo_rectangle_int_t *image_rect,
+                                       void **image_extra)
+{
+    cairo_skia_surface_t *surface = (cairo_skia_surface_t *) asurface;
+    cairo_image_surface_t *image = _get_image_surface (surface);
+
+    if (unlikely (image->base.status))
+       return image->base.status;
+
+    image_rect->x = 0;
+    image_rect->y = 0;
+    image_rect->width  = image->width;
+    image_rect->height = image->height;
+
+    surface->bitmap->lockPixels ();
+
+    *image_out = image;
+    *image_extra = NULL;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_skia_surface_release_dest_image (void *asurface,
+                                       cairo_rectangle_int_t *interest_rect,
+                                       cairo_image_surface_t *image,
+                                       cairo_rectangle_int_t *image_rect,
+                                       void *image_extra)
+{
+    cairo_skia_surface_t *surface = (cairo_skia_surface_t *) asurface;
+
+    surface->bitmap->notifyPixelsChanged ();
+    surface->bitmap->unlockPixels ();
+}
+
+#if 0
+static cairo_status_t
+_cairo_skia_surface_clone_similar (void *asurface,
+                                  cairo_surface_t *src,
+                                  cairo_content_t content,
+                                  int src_x,
+                                  int src_y,
+                                  int width,
+                                  int height,
+                                  int *clone_offset_x,
+                                  int *clone_offset_y,
+                                  cairo_surface_t **clone_out)
+{
+    if (src->type == CAIRO_SURFACE_TYPE_SKIA || _cairo_surface_is_image (src)) {
+       *clone_offset_x = 0;
+       *clone_offset_y = 0;
+       *clone_out = cairo_surface_reference (src);
+       return CAIRO_STATUS_SUCCESS;
+    }
+
+    return (cairo_status_t) CAIRO_INT_STATUS_UNSUPPORTED;
+}
+#endif
+
+static cairo_status_t
+_cairo_skia_surface_clipper_intersect_clip_path (cairo_surface_clipper_t *clipper,
+                                                cairo_path_fixed_t *path,
+                                                cairo_fill_rule_t fill_rule,
+                                                double tolerance,
+                                                cairo_antialias_t antialias)
+{
+    cairo_skia_surface_t *surface = cairo_container_of (clipper,
+                                                       cairo_skia_surface_t,
+                                                       clipper);
+
+    if (path == NULL) {
+       /* XXX TODO: teach Skia how to reset the clip path */
+       surface->canvas->restore ();
+       surface->canvas->save ();
+    } else {
+       surface->canvas->clipPath (path_to_sk (path, fill_rule));
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_bool_t
+_cairo_skia_surface_get_extents (void *asurface,
+                                cairo_rectangle_int_t *extents)
+{
+    cairo_skia_surface_t *surface = (cairo_skia_surface_t *) asurface;
+
+    extents->x = 0;
+    extents->y = 0;
+    extents->width  = surface->bitmap->width ();
+    extents->height = surface->bitmap->height ();
+
+    return TRUE;
+}
+
+/*
+ * Core drawing operations
+ */
+
+static SkBitmap *
+pattern_to_sk_bitmap (cairo_skia_surface_t *dst,
+                     const cairo_pattern_t *pattern,
+                     SkMatrix *matrix,
+                     cairo_image_surface_t **image,
+                     void **image_extra)
+{
+    if (pattern->type != CAIRO_PATTERN_TYPE_SURFACE)
+       return NULL;
+
+    if (pattern->extend != CAIRO_EXTEND_NONE)
+       return NULL;
+
+    cairo_surface_t *surface = surface_from_pattern (pattern);
+    SkBitmap *bitmap;
+
+    if (surface->type == CAIRO_SURFACE_TYPE_SKIA) {
+       bitmap = new SkBitmap (*((cairo_skia_surface_t *) surface)->bitmap);
+    } else {
+       if (surface->type != CAIRO_SURFACE_TYPE_IMAGE) {
+           cairo_status_t status;
+
+           status = _cairo_surface_acquire_source_image (surface,
+                                                         image, image_extra);
+           if (unlikely (status))
+               return NULL;
+
+           surface = &(*image)->base;
+       }
+
+       bitmap = new SkBitmap;
+       if (unlikely (! surface_to_sk_bitmap (surface, *bitmap)))
+           return NULL;
+    }
+
+    *matrix = matrix_inverse_to_sk (pattern->matrix);
+    return bitmap;
+}
+
+static cairo_int_status_t
+_cairo_skia_surface_paint (void *asurface,
+                          cairo_operator_t op,
+                          const cairo_pattern_t *source,
+                          cairo_clip_t *clip)
+{
+    cairo_skia_surface_t *surface = (cairo_skia_surface_t *) asurface;
+    cairo_image_surface_t *image = NULL;
+    cairo_status_t status;
+    void *image_extra;
+    SkColor color;
+
+    status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
+    if (unlikely (status))
+       return (cairo_int_status_t) status;
+
+    if (pattern_to_sk_color (source, color)) {
+       surface->canvas->drawColor (color, operator_to_sk (op));
+       return CAIRO_INT_STATUS_SUCCESS;
+    }
+
+    SkMatrix bitmapMatrix;
+    SkBitmap *bitmap = pattern_to_sk_bitmap (surface, source, &bitmapMatrix,
+                                            &image, &image_extra);
+    SkShader *shader = NULL;
+    if (!bitmap)
+       shader = pattern_to_sk_shader (surface, source, &image, &image_extra);
+
+    if (!bitmap && !shader)
+       return CAIRO_MAYBE_UNSUPPORTED;
+
+    SkPaint paint;
+    paint.setFilterBitmap (pattern_filter_to_sk (source));
+    paint.setXfermodeMode (operator_to_sk (op));
+
+    if (shader) {
+       paint.setShader (shader);
+       surface->canvas->drawPaint (paint);
+    } else {
+       surface->canvas->drawBitmapMatrix (*bitmap, bitmapMatrix, &paint);
+    }
+
+    if (bitmap)
+       delete bitmap;
+    if (shader)
+       shader->unref ();
+
+    if (image != NULL) {
+       _cairo_surface_release_source_image (&surface->base,
+                                            image, image_extra);
+    }
+
+    return CAIRO_INT_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_skia_surface_stroke (void *asurface,
+                           cairo_operator_t op,
+                           const cairo_pattern_t *source,
+                           cairo_path_fixed_t *path,
+                           cairo_stroke_style_t *style,
+                           cairo_matrix_t *ctm,
+                           cairo_matrix_t *ctm_inverse,
+                           double tolerance,
+                           cairo_antialias_t antialias,
+                           cairo_clip_t *clip)
+{
+    cairo_skia_surface_t *surface = (cairo_skia_surface_t *) asurface;
+    cairo_image_surface_t *image = NULL;
+    cairo_status_t status;
+    void *image_extra;
+
+    status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
+    if (unlikely (status))
+       return (cairo_int_status_t) status;
+
+    SkPaint paint;
+    paint.setStyle (SkPaint::kStroke_Style);
+
+    SkColor color;
+    if (pattern_to_sk_color (source, color)) {
+       paint.setColor (color);
+    } else {
+       SkShader *shader = pattern_to_sk_shader (surface,
+                                                source, &image, &image_extra);
+       if (shader == NULL)
+           return CAIRO_MAYBE_UNSUPPORTED;
+
+       paint.setShader (shader);
+       shader->unref ();
+
+       paint.setFilterBitmap (pattern_filter_to_sk (source));
+    }
+
+    paint.setXfermodeMode (operator_to_sk (op));
+    paint.setAntiAlias (antialias != CAIRO_ANTIALIAS_NONE);
+
+    /* Convert the various stroke rendering bits */
+    paint.setStrokeWidth (SkFloatToScalar (style->line_width));
+    paint.setStrokeMiter (SkFloatToScalar (style->miter_limit));
+
+    static const SkPaint::Cap capMap[] = {
+       SkPaint::kButt_Cap,
+       SkPaint::kRound_Cap,
+       SkPaint::kSquare_Cap
+    };
+    paint.setStrokeCap (capMap[style->line_cap]);
+
+    static const SkPaint::Join joinMap[] = {
+       SkPaint::kMiter_Join,
+       SkPaint::kRound_Join,
+       SkPaint::kBevel_Join
+    };
+    paint.setStrokeJoin (joinMap[style->line_join]);
+
+    /* If we have a dash pattern, we need to
+     * create a SkDashPathEffect and set it on the Paint.
+     */
+    if (style->dash != NULL) {
+       SkScalar intervals_static[20];
+       SkScalar *intervals = intervals_static;
+
+       int loop = 0;
+       unsigned int dash_count = style->num_dashes;
+       if ((dash_count & 1) != 0) {
+           loop = 1;
+           dash_count <<= 1;
+       }
+
+       if (dash_count > 20)
+           intervals = new SkScalar[dash_count];
+
+       unsigned int i = 0;
+       do {
+           for (unsigned int j = 0; i < style->num_dashes; j++)
+               intervals[i++] = SkFloatToScalar (style->dash[j]);
+       } while (loop--);
+
+       SkDashPathEffect *dash = new SkDashPathEffect (intervals,
+                                                      dash_count,
+                                                      SkFloatToScalar (style->dash_offset));
+
+       paint.setPathEffect (dash);
+       dash->unref ();
+    }
+
+    surface->canvas->save (SkCanvas::kMatrix_SaveFlag);
+    surface->canvas->concat (matrix_to_sk (*ctm));
+    surface->canvas->drawPath (path_to_sk (path, ctm_inverse), paint);
+    surface->canvas->restore ();
+
+    if (image != NULL) {
+       _cairo_surface_release_source_image (&surface->base,
+                                            image, image_extra);
+    }
+
+    return CAIRO_INT_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_skia_surface_fill (void *asurface,
+                         cairo_operator_t op,
+                         const cairo_pattern_t *source,
+                         cairo_path_fixed_t *path,
+                         cairo_fill_rule_t fill_rule,
+                         double tolerance,
+                         cairo_antialias_t antialias,
+                         cairo_clip_t *clip)
+{
+    cairo_skia_surface_t *surface = (cairo_skia_surface_t *) asurface;
+    cairo_image_surface_t *image = NULL;
+    cairo_status_t status;
+    void *image_extra;
+
+    status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
+    if (unlikely (status))
+       return (cairo_int_status_t) status;
+
+
+    SkPaint paint;
+    paint.setStyle (SkPaint::kFill_Style);
+
+    SkColor color;
+    if (pattern_to_sk_color (source, color)) {
+       paint.setColor (color);
+    } else {
+       SkShader *shader = pattern_to_sk_shader (surface,
+                                                source, &image, &image_extra);
+       if (shader == NULL)
+           return CAIRO_MAYBE_UNSUPPORTED;
+
+       paint.setShader (shader);
+       shader->unref ();
+
+       paint.setFilterBitmap (pattern_filter_to_sk (source));
+    }
+
+    paint.setXfermodeMode (operator_to_sk (op));
+    paint.setAntiAlias (antialias != CAIRO_ANTIALIAS_NONE);
+
+    surface->canvas->drawPath (path_to_sk (path, fill_rule), paint);
+
+    if (image != NULL) {
+       _cairo_surface_release_source_image (&surface->base,
+                                            image, image_extra);
+    }
+
+    return CAIRO_INT_STATUS_SUCCESS;
+}
+
+static const struct _cairo_surface_backend
+cairo_skia_surface_backend = {
+    CAIRO_SURFACE_TYPE_SKIA,
+    _cairo_skia_surface_create_similar,
+    _cairo_skia_surface_finish,
+    _cairo_skia_surface_acquire_source_image,
+    _cairo_skia_surface_release_source_image,
+    _cairo_skia_surface_acquire_dest_image,
+    _cairo_skia_surface_release_dest_image,
+
+    NULL, // _cairo_skia_surface_clone_similar,
+    NULL, /* composite */
+    NULL, /* fill_rectangles */
+    NULL, /* composite_trapezoids */
+    NULL, /* create_span_renderer */
+    NULL, /* check_span_renderer */
+
+    NULL, /* copy_page */
+    NULL, /* show_page */
+
+    _cairo_skia_surface_get_extents,
+    NULL, /* old_show_glyphs */
+    NULL, /* get_font_options */
+    NULL, /* flush */
+    NULL, /* mark_dirty_rectangle */
+    NULL, /* scaled_font_fini */
+    NULL, /* scaled_glyph_fini */
+
+    _cairo_skia_surface_paint,
+    NULL, /* mask? */
+    _cairo_skia_surface_stroke,
+    _cairo_skia_surface_fill,
+    NULL, /* show_glyphs */
+
+    NULL, /* snapshot */
+    NULL, /* is_similar */
+    NULL, /* reset */
+};
+
+/*
+ * Surface constructors
+ */
+
+static cairo_skia_surface_t *
+_cairo_skia_surface_create_internal (SkBitmap::Config config,
+                                    bool opaque,
+                                    unsigned char *data,
+                                    int width,
+                                    int height,
+                                    int stride)
+{
+    cairo_skia_surface_t *surface;
+    cairo_format_t format;
+
+    surface = (cairo_skia_surface_t *) malloc (sizeof (cairo_skia_surface_t));
+    if (surface == NULL)
+       return (cairo_skia_surface_t *) _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+    memset (surface, 0, sizeof (cairo_skia_surface_t));
+
+    format = sk_config_to_format (config, opaque);
+    assert (format != -1);
+
+    _cairo_surface_init (&surface->base,
+                        &cairo_skia_surface_backend,
+                        NULL, /* device */
+                        _cairo_content_from_format (format));
+
+    _cairo_surface_clipper_init (&surface->clipper,
+                                _cairo_skia_surface_clipper_intersect_clip_path);
+
+    surface->bitmap = new SkBitmap;
+    if (data == NULL)
+       stride = cairo_format_stride_for_width (format, width);
+    surface->bitmap->setConfig (config, width, height, stride);
+    surface->bitmap->setIsOpaque (opaque);
+    if (data != NULL)
+       surface->bitmap->setPixels (data);
+    else
+       surface->bitmap->allocPixels ();
+
+    surface->canvas = new SkCanvas (*surface->bitmap);
+    //surface->canvas->translate (SkIntToScalar (0), SkIntToScalar (height));
+    //surface->canvas->scale (SkIntToScalar (1), SkIntToScalar (-1));
+    surface->canvas->save ();
+
+    return surface;
+}
+
+cairo_surface_t *
+cairo_skia_surface_create (cairo_format_t format,
+                          int width,
+                          int height)
+{
+    SkBitmap::Config config;
+    bool opaque;
+
+    if (! CAIRO_FORMAT_VALID (format) ||
+       ! format_to_sk_config (format, config, opaque))
+    {
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
+    }
+
+    return &_cairo_skia_surface_create_internal (config, opaque,
+                                                NULL,
+                                                width, height, 0)->base;
+}
+
+cairo_surface_t *
+cairo_skia_surface_create_for_data (unsigned char *data,
+                                   cairo_format_t format,
+                                   int width,
+                                   int height,
+                                   int stride)
+{
+    SkBitmap::Config config;
+    bool opaque;
+
+    if (! CAIRO_FORMAT_VALID (format) ||
+       ! format_to_sk_config (format, config, opaque))
+    {
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
+    }
+
+    return &_cairo_skia_surface_create_internal (config, opaque,
+                                               data,
+                                               width, height, stride)->base;
+}
+
+unsigned char *
+cairo_skia_surface_get_data (cairo_surface_t *surface)
+{
+    if (surface->type != CAIRO_SURFACE_TYPE_SKIA)
+       return NULL;
+
+    cairo_skia_surface_t *esurf = (cairo_skia_surface_t *) surface;
+    return (unsigned char *) esurf->bitmap->getPixels ();
+}
+
+cairo_format_t
+cairo_skia_surface_get_format (cairo_surface_t *surface)
+{
+    if (surface->type != CAIRO_SURFACE_TYPE_SKIA)
+       return (cairo_format_t) -1;
+
+    cairo_skia_surface_t *esurf = (cairo_skia_surface_t *) surface;
+    return sk_config_to_format (esurf->bitmap->config (),
+                               esurf->bitmap->isOpaque ());
+}
+
+int
+cairo_skia_surface_get_width (cairo_surface_t *surface)
+{
+    if (surface->type != CAIRO_SURFACE_TYPE_SKIA)
+       return 0;
+
+    cairo_skia_surface_t *esurf = (cairo_skia_surface_t *) surface;
+    return esurf->bitmap->width ();
+}
+
+int
+cairo_skia_surface_get_height (cairo_surface_t *surface)
+{
+    if (surface->type != CAIRO_SURFACE_TYPE_SKIA)
+       return 0;
+
+    cairo_skia_surface_t *esurf = (cairo_skia_surface_t *) surface;
+    return esurf->bitmap->height ();
+}
+
+int
+cairo_skia_surface_get_stride (cairo_surface_t *surface)
+{
+    if (surface->type != CAIRO_SURFACE_TYPE_SKIA)
+       return 0;
+
+    cairo_skia_surface_t *esurf = (cairo_skia_surface_t *) surface;
+    return esurf->bitmap->rowBytes ();
+}
+
+cairo_surface_t *
+cairo_skia_surface_get_image (cairo_surface_t *surface)
+{
+    if (surface->type != CAIRO_SURFACE_TYPE_SKIA)
+       return NULL;
+
+    cairo_skia_surface_t *esurf = (cairo_skia_surface_t *) surface;
+    return &_get_image_surface (esurf)->base;
+}
+
+/***
+
+Todo:
+
+*** Skia:
+
+- mask()
+
+*** Sk:
+
+High:
+- antialiased clipping?
+
+Medium:
+- implement clip path reset (to avoid restore/save)
+- implement complex radial patterns (2 centers and 2 radii)
+
+Low:
+- implement EXTEND_NONE
+
+***/
diff --git a/src/cairo-skia.h b/src/cairo-skia.h
new file mode 100644 (file)
index 0000000..99b9286
--- /dev/null
@@ -0,0 +1,66 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *     Carl D. Worth <cworth@cworth.org>
+ */
+
+#ifndef CAIRO_SKIA_H
+#define CAIRO_SKIA_H
+
+#include "cairo.h"
+
+#if CAIRO_HAS_SKIA_SURFACE
+
+CAIRO_BEGIN_DECLS
+
+cairo_public cairo_surface_t *
+cairo_skia_surface_create (cairo_format_t format,
+                          int width,
+                          int height);
+
+cairo_public cairo_surface_t *
+cairo_skia_surface_create_for_data (unsigned char *data,
+                                   cairo_format_t format,
+                                   int width,
+                                   int height,
+                                   int stride);
+
+CAIRO_END_DECLS
+
+#else
+
+# error Cairo was not compiled with support for the Skia backend
+
+#endif
+
+#endif
diff --git a/src/cairo-slope-private.h b/src/cairo-slope-private.h
new file mode 100644 (file)
index 0000000..6a58c9f
--- /dev/null
@@ -0,0 +1,72 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *     Carl D. Worth <cworth@cworth.org>
+ */
+
+#ifndef _CAIRO_SLOPE_PRIVATE_H
+#define _CAIRO_SLOPE_PRIVATE_H
+
+#include "cairo-types-private.h"
+#include "cairo-fixed-private.h"
+
+static inline void
+_cairo_slope_init (cairo_slope_t *slope,
+                  const cairo_point_t *a,
+                  const cairo_point_t *b)
+{
+    slope->dx = b->x - a->x;
+    slope->dy = b->y - a->y;
+}
+
+static inline cairo_bool_t
+_cairo_slope_equal (const cairo_slope_t *a, const cairo_slope_t *b)
+{
+    return _cairo_int64_eq (_cairo_int32x32_64_mul (a->dy, b->dx),
+                           _cairo_int32x32_64_mul (b->dy, a->dx));
+}
+
+static inline cairo_bool_t
+_cairo_slope_backwards (const cairo_slope_t *a, const cairo_slope_t *b)
+{
+    return _cairo_int64_negative (_cairo_int64_add (_cairo_int32x32_64_mul (a->dx, b->dx),
+                                                   _cairo_int32x32_64_mul (a->dy, b->dy)));
+}
+
+cairo_private int
+_cairo_slope_compare (const cairo_slope_t *a,
+                     const cairo_slope_t *b) cairo_pure;
+
+
+#endif /* _CAIRO_SLOPE_PRIVATE_H */
diff --git a/src/cairo-slope.c b/src/cairo-slope.c
new file mode 100644 (file)
index 0000000..cc5f30c
--- /dev/null
@@ -0,0 +1,99 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *     Carl D. Worth <cworth@cworth.org>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-slope-private.h"
+
+/* Compare two slopes. Slope angles begin at 0 in the direction of the
+   positive X axis and increase in the direction of the positive Y
+   axis.
+
+   This function always compares the slope vectors based on the
+   smaller angular difference between them, (that is based on an
+   angular difference that is strictly less than pi). To break ties
+   when comparing slope vectors with an angular difference of exactly
+   pi, the vector with a positive dx (or positive dy if dx's are zero)
+   is considered to be more positive than the other.
+
+   Also, all slope vectors with both dx==0 and dy==0 are considered
+   equal and more positive than any non-zero vector.
+
+   <  0 => a less positive than b
+   == 0 => a equal to b
+   >  0 => a more positive than b
+*/
+int
+_cairo_slope_compare (const cairo_slope_t *a, const cairo_slope_t *b)
+{
+    cairo_int64_t ady_bdx = _cairo_int32x32_64_mul (a->dy, b->dx);
+    cairo_int64_t bdy_adx = _cairo_int32x32_64_mul (b->dy, a->dx);
+    int cmp;
+
+    cmp = _cairo_int64_cmp (ady_bdx, bdy_adx);
+    if (cmp)
+       return cmp;
+
+    /* special-case zero vectors.  the intended logic here is:
+     * zero vectors all compare equal, and more positive than any
+     * non-zero vector.
+     */
+    if (a->dx == 0 && a->dy == 0 && b->dx == 0 && b->dy ==0)
+       return 0;
+    if (a->dx == 0 && a->dy == 0)
+       return 1;
+    if (b->dx == 0 && b->dy ==0)
+       return -1;
+
+    /* Finally, we're looking at two vectors that are either equal or
+     * that differ by exactly pi. We can identify the "differ by pi"
+     * case by looking for a change in sign in either dx or dy between
+     * a and b.
+     *
+     * And in these cases, we eliminate the ambiguity by reducing the angle
+     * of b by an infinitesimally small amount, (that is, 'a' will
+     * always be considered less than 'b').
+     */
+    if ((a->dx ^ b->dx) < 0 || (a->dy ^ b->dy) < 0) {
+       if (a->dx > 0 || (a->dx == 0 && a->dy > 0))
+           return -1;
+       else
+           return +1;
+    }
+
+    /* Finally, for identical slopes, we obviously return 0. */
+    return 0;
+}
diff --git a/src/cairo-spans-compositor-private.h b/src/cairo-spans-compositor-private.h
new file mode 100644 (file)
index 0000000..cab09f6
--- /dev/null
@@ -0,0 +1,99 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2011 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#ifndef CAIRO_SPANS_COMPOSITOR_PRIVATE_H
+#define CAIRO_SPANS_COMPOSITOR_PRIVATE_H
+
+#include "cairo-compositor-private.h"
+#include "cairo-types-private.h"
+#include "cairo-spans-private.h"
+
+CAIRO_BEGIN_DECLS
+
+typedef struct _cairo_abstract_span_renderer {
+    cairo_span_renderer_t base;
+    char data[2048];
+} cairo_abstract_span_renderer_t;
+
+struct cairo_spans_compositor {
+    cairo_compositor_t base;
+
+    unsigned int flags;
+#define CAIRO_SPANS_COMPOSITOR_HAS_LERP 0x1
+
+    /* pixel-aligned fast paths */
+    cairo_int_status_t (*fill_boxes)   (void                   *surface,
+                                        cairo_operator_t        op,
+                                        const cairo_color_t    *color,
+                                        cairo_boxes_t          *boxes);
+
+    cairo_surface_t * (*pattern_to_surface) (cairo_surface_t *dst,
+                                            const cairo_pattern_t *pattern,
+                                            cairo_bool_t is_mask,
+                                            const cairo_rectangle_int_t *extents,
+                                            const cairo_rectangle_int_t *sample,
+                                            int *src_x, int *src_y);
+
+    cairo_int_status_t (*composite_boxes) (void                        *surface,
+                                          cairo_operator_t      op,
+                                          cairo_surface_t      *source,
+                                          cairo_surface_t      *mask,
+                                          int                   src_x,
+                                          int                   src_y,
+                                          int                   mask_x,
+                                          int                   mask_y,
+                                          int                   dst_x,
+                                          int                   dst_y,
+                                          cairo_boxes_t                *boxes,
+                                          const cairo_rectangle_int_t  *extents);
+
+    /* general shape masks using a span renderer */
+    cairo_int_status_t (*renderer_init) (cairo_abstract_span_renderer_t *renderer,
+                                        const cairo_composite_rectangles_t *extents,
+                                        cairo_bool_t    needs_clip);
+
+    void (*renderer_fini) (cairo_abstract_span_renderer_t *renderer,
+                          cairo_int_status_t status);
+};
+
+cairo_private void
+_cairo_spans_compositor_init (cairo_spans_compositor_t *compositor,
+                             const cairo_compositor_t  *delegate);
+
+CAIRO_END_DECLS
+
+#endif /* CAIRO_SPANS_COMPOSITOR_PRIVATE_H */
diff --git a/src/cairo-spans-compositor.c b/src/cairo-spans-compositor.c
new file mode 100644 (file)
index 0000000..cbcd6b3
--- /dev/null
@@ -0,0 +1,1042 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ * Copyright © 2005 Red Hat, Inc.
+ * Copyright © 2011 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *     Carl D. Worth <cworth@cworth.org>
+ *      Joonas Pihlaja <jpihlaja@cc.helsinki.fi>
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-compositor-private.h"
+#include "cairo-clip-private.h"
+#include "cairo-image-surface-private.h"
+#include "cairo-paginated-private.h"
+#include "cairo-pattern-private.h"
+#include "cairo-region-private.h"
+#include "cairo-recording-surface-private.h"
+#include "cairo-spans-compositor-private.h"
+#include "cairo-surface-subsurface-private.h"
+#include "cairo-surface-snapshot-private.h"
+#include "cairo-surface-observer-private.h"
+
+typedef struct {
+    cairo_polygon_t    *polygon;
+    cairo_fill_rule_t   fill_rule;
+    cairo_antialias_t   antialias;
+} composite_spans_info_t;
+
+static cairo_int_status_t
+composite_polygon (const cairo_spans_compositor_t      *compositor,
+                  cairo_composite_rectangles_t          *extents,
+                  cairo_polygon_t                      *polygon,
+                  cairo_fill_rule_t                     fill_rule,
+                  cairo_antialias_t                     antialias);
+
+static cairo_int_status_t
+composite_boxes (const cairo_spans_compositor_t *compositor,
+                cairo_composite_rectangles_t *extents,
+                cairo_boxes_t          *boxes);
+
+static cairo_int_status_t
+clip_and_composite_polygon (const cairo_spans_compositor_t     *compositor,
+                           cairo_composite_rectangles_t         *extents,
+                           cairo_polygon_t                     *polygon,
+                           cairo_fill_rule_t                    fill_rule,
+                           cairo_antialias_t                    antialias);
+
+static cairo_int_status_t
+get_clip_polygon (const cairo_clip_path_t *clip_path,
+                 cairo_antialias_t antialias,
+                 cairo_polygon_t *polygon,
+                 cairo_fill_rule_t *fill_rule)
+{
+    cairo_int_status_t status;
+    cairo_bool_t is_first_polygon = TRUE;
+
+    while (clip_path) {
+       if (clip_path->antialias == antialias) {
+           if (is_first_polygon) {
+               status = _cairo_path_fixed_fill_to_polygon (&clip_path->path,
+                                                           clip_path->tolerance,
+                                                           polygon);
+               *fill_rule = clip_path->fill_rule;
+               polygon->limits = NULL;
+               polygon->num_limits = 0;
+               is_first_polygon = FALSE;
+           } else if (polygon->num_edges == 0) {
+               /* Intersecting with empty polygon will generate empty polygon. */
+               return CAIRO_INT_STATUS_NOTHING_TO_DO;
+           } else {
+               cairo_polygon_t next;
+
+               _cairo_polygon_init (&next, NULL, 0);
+               status = _cairo_path_fixed_fill_to_polygon (&clip_path->path,
+                                                           clip_path->tolerance,
+                                                           &next);
+               if (likely (status == CAIRO_INT_STATUS_SUCCESS))
+                   status = _cairo_polygon_intersect (polygon, *fill_rule,
+                                                      &next, clip_path->fill_rule);
+               _cairo_polygon_fini (&next);
+               *fill_rule = CAIRO_FILL_RULE_WINDING;
+           }
+
+           if (unlikely (status))
+               return status;
+       }
+
+       clip_path = clip_path->prev;
+    }
+
+    if (polygon->num_edges == 0)
+       return CAIRO_INT_STATUS_NOTHING_TO_DO;
+
+    assert (is_first_polygon == FALSE);
+    return CAIRO_INT_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+composite_clip_polygon (const cairo_spans_compositor_t *compositor,
+                       cairo_surface_t *surface,
+                       cairo_operator_t op,
+                       cairo_polygon_t *polygon,
+                       cairo_fill_rule_t fill_rule,
+                       cairo_antialias_t antialias)
+{
+    cairo_int_status_t status;
+    cairo_composite_rectangles_t composite;
+
+    status = _cairo_composite_rectangles_init_for_polygon (&composite, surface, op,
+                                                          &_cairo_pattern_white.base,
+                                                          polygon,
+                                                          NULL);
+    if (unlikely (status))
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    status = composite_polygon (compositor, &composite,
+                               polygon, fill_rule, antialias);
+    _cairo_composite_rectangles_fini (&composite);
+
+    return status;
+}
+
+static cairo_surface_t *
+get_clip_surface (const cairo_spans_compositor_t *compositor,
+                 cairo_surface_t *dst,
+                 const cairo_clip_t *clip,
+                 const cairo_rectangle_int_t *extents)
+{
+    cairo_surface_t *surface;
+    cairo_box_t box;
+    cairo_polygon_t polygon0;
+    cairo_polygon_t polygon1;
+    cairo_fill_rule_t fill_rule0;
+    cairo_fill_rule_t fill_rule1;
+    cairo_antialias_t antialias;
+    cairo_int_status_t status;
+
+    assert (clip->path);
+
+    surface = _cairo_surface_create_similar_solid (dst,
+                                                  CAIRO_CONTENT_ALPHA,
+                                                  extents->width,
+                                                  extents->height,
+                                                  CAIRO_COLOR_TRANSPARENT);
+
+    _cairo_box_from_rectangle (&box, extents);
+    _cairo_polygon_init (&polygon0, &box, 1);
+    _cairo_polygon_init (&polygon1, &box, 1);
+
+    if (! surface)
+       return _cairo_surface_create_in_error (CAIRO_INT_STATUS_NO_MEMORY);
+
+    status = _cairo_clip_get_polygon (clip, &polygon0, &fill_rule0, &antialias);
+
+    if (status != CAIRO_INT_STATUS_UNSUPPORTED) {
+       if (status == CAIRO_INT_STATUS_NOTHING_TO_DO)
+           goto cleanup_polygon;
+
+       _cairo_polygon_translate (&polygon0, -extents->x, -extents->y);
+       status = composite_clip_polygon (compositor, surface, CAIRO_OPERATOR_ADD,
+                                        &polygon0, fill_rule0, antialias);
+       _cairo_polygon_fini (&polygon0);
+       _cairo_polygon_fini (&polygon1);
+
+       if (unlikely (status))
+           goto cleanup_polygon;
+
+       return surface;
+    }
+
+    /* Here, we can assume that clip paths have heterogeneous antialias,
+     * as this code is fallback for that kind of clip.
+     * This code might not work if all clip paths have equal antialias.
+     */
+    status = get_clip_polygon (clip->path, CAIRO_ANTIALIAS_DEFAULT, &polygon0, &fill_rule0);
+
+    if (unlikely (status))
+       goto cleanup_polygon;
+
+    status = get_clip_polygon (clip->path, CAIRO_ANTIALIAS_NONE, &polygon1, &fill_rule1);
+
+    if (unlikely (status))
+       goto cleanup_polygon;
+
+    _cairo_polygon_translate (&polygon0, -extents->x, -extents->y);
+    status = composite_clip_polygon (compositor, surface, CAIRO_OPERATOR_ADD,
+                                    &polygon0, fill_rule0, CAIRO_ANTIALIAS_DEFAULT);
+
+    if (unlikely (status))
+       goto cleanup_polygon;
+
+    _cairo_polygon_translate (&polygon1, -extents->x, -extents->y);
+    status = composite_clip_polygon (compositor, surface, CAIRO_OPERATOR_IN,
+                                    &polygon1, fill_rule1, CAIRO_ANTIALIAS_NONE);
+
+    if (unlikely (status))
+       goto cleanup_polygon;
+
+    _cairo_polygon_fini (&polygon0);
+    _cairo_polygon_fini (&polygon1);
+
+    return surface;
+
+cleanup_polygon:
+    _cairo_polygon_fini (&polygon0);
+    _cairo_polygon_fini (&polygon1);
+    cairo_surface_destroy (surface);
+
+    return _cairo_int_surface_create_in_error (status);
+}
+
+static cairo_int_status_t
+fixup_unbounded_mask (const cairo_spans_compositor_t *compositor,
+                     const cairo_composite_rectangles_t *extents,
+                     cairo_boxes_t *boxes)
+{
+    cairo_composite_rectangles_t composite;
+    cairo_surface_t *clip;
+    cairo_int_status_t status;
+
+    clip = get_clip_surface (compositor, extents->surface, extents->clip,
+                            &extents->unbounded);
+    if (unlikely (clip->status)) {
+       if ((cairo_int_status_t)clip->status == CAIRO_INT_STATUS_NOTHING_TO_DO)
+           return CAIRO_STATUS_SUCCESS;
+
+       return clip->status;
+    }
+
+    status = _cairo_composite_rectangles_init_for_boxes (&composite,
+                                                        extents->surface,
+                                                        CAIRO_OPERATOR_CLEAR,
+                                                        &_cairo_pattern_clear.base,
+                                                        boxes,
+                                                        NULL);
+    if (unlikely (status))
+       goto cleanup_clip;
+
+    _cairo_pattern_init_for_surface (&composite.mask_pattern.surface, clip);
+    composite.mask_pattern.base.filter = CAIRO_FILTER_NEAREST;
+    composite.mask_pattern.base.extend = CAIRO_EXTEND_NONE;
+
+    status = composite_boxes (compositor, &composite, boxes);
+
+    _cairo_pattern_fini (&composite.mask_pattern.base);
+    _cairo_composite_rectangles_fini (&composite);
+
+cleanup_clip:
+    cairo_surface_destroy (clip);
+    return status;
+}
+
+static cairo_int_status_t
+fixup_unbounded_polygon (const cairo_spans_compositor_t *compositor,
+                        const cairo_composite_rectangles_t *extents,
+                        cairo_boxes_t *boxes)
+{
+    cairo_polygon_t polygon, intersect;
+    cairo_composite_rectangles_t composite;
+    cairo_fill_rule_t fill_rule;
+    cairo_antialias_t antialias;
+    cairo_int_status_t status;
+
+    /* Can we treat the clip as a regular clear-polygon and use it to fill? */
+    status = _cairo_clip_get_polygon (extents->clip, &polygon,
+                                     &fill_rule, &antialias);
+    if (status == CAIRO_INT_STATUS_UNSUPPORTED)
+       return status;
+
+    status= _cairo_polygon_init_boxes (&intersect, boxes);
+    if (unlikely (status))
+       goto cleanup_polygon;
+
+    status = _cairo_polygon_intersect (&polygon, fill_rule,
+                                      &intersect, CAIRO_FILL_RULE_WINDING);
+    _cairo_polygon_fini (&intersect);
+
+    if (unlikely (status))
+       goto cleanup_polygon;
+
+    status = _cairo_composite_rectangles_init_for_polygon (&composite,
+                                                          extents->surface,
+                                                          CAIRO_OPERATOR_CLEAR,
+                                                          &_cairo_pattern_clear.base,
+                                                          &polygon,
+                                                          NULL);
+    if (unlikely (status))
+       goto cleanup_polygon;
+
+    status = composite_polygon (compositor, &composite,
+                               &polygon, fill_rule, antialias);
+
+    _cairo_composite_rectangles_fini (&composite);
+cleanup_polygon:
+    _cairo_polygon_fini (&polygon);
+
+    return status;
+}
+
+static cairo_int_status_t
+fixup_unbounded_boxes (const cairo_spans_compositor_t *compositor,
+                      const cairo_composite_rectangles_t *extents,
+                      cairo_boxes_t *boxes)
+{
+    cairo_boxes_t tmp, clear;
+    cairo_box_t box;
+    cairo_int_status_t status;
+
+    assert (boxes->is_pixel_aligned);
+
+    if (extents->bounded.width  == extents->unbounded.width &&
+       extents->bounded.height == extents->unbounded.height)
+    {
+       return CAIRO_STATUS_SUCCESS;
+    }
+
+    /* subtract the drawn boxes from the unbounded area */
+    _cairo_boxes_init (&clear);
+
+    box.p1.x = _cairo_fixed_from_int (extents->unbounded.x + extents->unbounded.width);
+    box.p1.y = _cairo_fixed_from_int (extents->unbounded.y);
+    box.p2.x = _cairo_fixed_from_int (extents->unbounded.x);
+    box.p2.y = _cairo_fixed_from_int (extents->unbounded.y + extents->unbounded.height);
+
+    if (boxes->num_boxes) {
+       _cairo_boxes_init (&tmp);
+
+       status = _cairo_boxes_add (&tmp, CAIRO_ANTIALIAS_DEFAULT, &box);
+       assert (status == CAIRO_INT_STATUS_SUCCESS);
+
+       tmp.chunks.next = &boxes->chunks;
+       tmp.num_boxes += boxes->num_boxes;
+
+       status = _cairo_bentley_ottmann_tessellate_boxes (&tmp,
+                                                         CAIRO_FILL_RULE_WINDING,
+                                                         &clear);
+       tmp.chunks.next = NULL;
+       if (unlikely (status))
+           goto error;
+    } else {
+       box.p1.x = _cairo_fixed_from_int (extents->unbounded.x);
+       box.p2.x = _cairo_fixed_from_int (extents->unbounded.x + extents->unbounded.width);
+
+       status = _cairo_boxes_add (&clear, CAIRO_ANTIALIAS_DEFAULT, &box);
+       assert (status == CAIRO_INT_STATUS_SUCCESS);
+    }
+
+    /* Now intersect with the clip boxes */
+    if (extents->clip->num_boxes) {
+       _cairo_boxes_init_for_array (&tmp,
+                                    extents->clip->boxes,
+                                    extents->clip->num_boxes);
+       status = _cairo_boxes_intersect (&clear, &tmp, &clear);
+       if (unlikely (status))
+           goto error;
+    }
+
+    /* If we have a clip polygon, we need to intersect with that as well */
+    if (extents->clip->path) {
+       status = fixup_unbounded_polygon (compositor, extents, &clear);
+       if (status == CAIRO_INT_STATUS_UNSUPPORTED)
+           status = fixup_unbounded_mask (compositor, extents, &clear);
+    } else {
+       status = compositor->fill_boxes (extents->surface,
+                                        CAIRO_OPERATOR_CLEAR,
+                                        CAIRO_COLOR_TRANSPARENT,
+                                        &clear);
+    }
+
+error:
+    _cairo_boxes_fini (&clear);
+    return status;
+}
+
+static cairo_surface_t *
+unwrap_surface (const cairo_pattern_t *pattern)
+{
+    cairo_surface_t *surface;
+
+    surface = ((const cairo_surface_pattern_t *) pattern)->surface;
+    if (_cairo_surface_is_paginated (surface))
+       surface = _cairo_paginated_surface_get_recording (surface);
+    if (_cairo_surface_is_snapshot (surface))
+       surface = _cairo_surface_snapshot_get_target (surface);
+    if (_cairo_surface_is_observer (surface))
+       surface = _cairo_surface_observer_get_target (surface);
+    return surface;
+}
+
+static cairo_bool_t
+is_recording_pattern (const cairo_pattern_t *pattern)
+{
+    cairo_surface_t *surface;
+
+    if (pattern->type != CAIRO_PATTERN_TYPE_SURFACE)
+       return FALSE;
+
+    surface = ((const cairo_surface_pattern_t *) pattern)->surface;
+    return _cairo_surface_is_recording (surface);
+}
+
+static cairo_bool_t
+recording_pattern_contains_sample (const cairo_pattern_t *pattern,
+                                  const cairo_rectangle_int_t *sample)
+{
+    cairo_recording_surface_t *surface;
+
+    if (! is_recording_pattern (pattern))
+       return FALSE;
+
+    if (pattern->extend == CAIRO_EXTEND_NONE)
+       return TRUE;
+
+    surface = (cairo_recording_surface_t *) unwrap_surface (pattern);
+    if (surface->unbounded)
+       return TRUE;
+
+    if (sample->x >= surface->extents.x &&
+       sample->y >= surface->extents.y &&
+       sample->x + sample->width <= surface->extents.x + surface->extents.width &&
+       sample->y + sample->height <= surface->extents.y + surface->extents.height)
+    {
+       return TRUE;
+    }
+
+    return FALSE;
+}
+
+static cairo_bool_t
+op_reduces_to_source (const cairo_composite_rectangles_t *extents)
+{
+    if (extents->op == CAIRO_OPERATOR_SOURCE)
+       return TRUE;
+
+    if (extents->surface->is_clear)
+       return extents->op == CAIRO_OPERATOR_OVER || extents->op == CAIRO_OPERATOR_ADD;
+
+    return FALSE;
+}
+
+static cairo_int_status_t
+composite_aligned_boxes (const cairo_spans_compositor_t                *compositor,
+                        const cairo_composite_rectangles_t     *extents,
+                        cairo_boxes_t                          *boxes)
+{
+    cairo_surface_t *dst = extents->surface;
+    cairo_operator_t op = extents->op;
+    const cairo_pattern_t *source = &extents->source_pattern.base;
+    cairo_int_status_t status;
+    cairo_bool_t need_clip_mask = extents->clip->path != NULL;
+    cairo_bool_t op_is_source;
+    cairo_bool_t no_mask;
+    cairo_bool_t inplace;
+
+    if (need_clip_mask && ! extents->is_bounded)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    op_is_source = op_reduces_to_source (extents);
+    no_mask = extents->mask_pattern.base.type == CAIRO_PATTERN_TYPE_SOLID &&
+       CAIRO_ALPHA_IS_OPAQUE (extents->mask_pattern.solid.color.alpha);
+    inplace = ! need_clip_mask && op_is_source && no_mask;
+
+    if (op == CAIRO_OPERATOR_SOURCE && (need_clip_mask || ! no_mask)) {
+       /* SOURCE with a mask is actually a LERP in cairo semantics */
+       if ((compositor->flags & CAIRO_SPANS_COMPOSITOR_HAS_LERP) == 0)
+           return CAIRO_INT_STATUS_UNSUPPORTED;
+    }
+
+    /* Are we just copying a recording surface? */
+    if (inplace &&
+       recording_pattern_contains_sample (&extents->source_pattern.base,
+                                          &extents->source_sample_area))
+    {
+       cairo_clip_t *recording_clip;
+       const cairo_pattern_t *source = &extents->source_pattern.base;
+
+       /* XXX could also do tiling repeat modes... */
+
+       /* first clear the area about to be overwritten */
+       if (! dst->is_clear)
+           status = compositor->fill_boxes (dst,
+                                            CAIRO_OPERATOR_CLEAR,
+                                            CAIRO_COLOR_TRANSPARENT,
+                                            boxes);
+
+       recording_clip = _cairo_clip_from_boxes (boxes);
+       status = _cairo_recording_surface_replay_with_clip (unwrap_surface (source),
+                                                           &source->matrix,
+                                                           dst, recording_clip);
+       _cairo_clip_destroy (recording_clip);
+
+       return status;
+    }
+
+    if (! need_clip_mask && no_mask && source->type == CAIRO_PATTERN_TYPE_SOLID) {
+       const cairo_color_t *color;
+
+       color = &((cairo_solid_pattern_t *) source)->color;
+       if (op_is_source)
+           op = CAIRO_OPERATOR_SOURCE;
+       status = compositor->fill_boxes (dst, op, color, boxes);
+#if 0
+    } else if (inplace && source->type == CAIRO_PATTERN_TYPE_SURFACE) {
+       status = upload_inplace (compositor, extents, boxes);
+#endif
+    } else {
+       cairo_surface_t *src;
+       cairo_surface_t *mask = NULL;
+       int src_x, src_y;
+       int mask_x = 0, mask_y = 0;
+
+       /* All typical cases will have been resolved before now... */
+       if (need_clip_mask) {
+           mask = get_clip_surface (compositor, dst, extents->clip,
+                                    &extents->bounded);
+           if (unlikely (mask->status)) {
+               if ((cairo_int_status_t)mask->status == CAIRO_INT_STATUS_NOTHING_TO_DO)
+                   return CAIRO_STATUS_SUCCESS;
+
+               return mask->status;
+           }
+
+           mask_x = -extents->bounded.x;
+           mask_y = -extents->bounded.y;
+       }
+
+       /* XXX but this is still ugly */
+       if (! no_mask) {
+           src = compositor->pattern_to_surface (dst,
+                                                 &extents->mask_pattern.base,
+                                                 TRUE,
+                                                 &extents->bounded,
+                                                 &extents->mask_sample_area,
+                                                 &src_x, &src_y);
+           if (unlikely (src->status)) {
+               cairo_surface_destroy (mask);
+               return src->status;
+           }
+
+           if (mask != NULL) {
+               status = compositor->composite_boxes (mask, CAIRO_OPERATOR_IN,
+                                                     src, NULL,
+                                                     src_x, src_y,
+                                                     0, 0,
+                                                     mask_x, mask_y,
+                                                     boxes, &extents->bounded);
+
+               cairo_surface_destroy (src);
+           } else {
+               mask = src;
+               mask_x = src_x;
+               mask_y = src_y;
+           }
+       }
+
+       src = compositor->pattern_to_surface (dst, source, FALSE,
+                                             &extents->bounded,
+                                             &extents->source_sample_area,
+                                             &src_x, &src_y);
+       if (likely (src->status == CAIRO_STATUS_SUCCESS)) {
+           status = compositor->composite_boxes (dst, op, src, mask,
+                                                 src_x, src_y,
+                                                 mask_x, mask_y,
+                                                 0, 0,
+                                                 boxes, &extents->bounded);
+           cairo_surface_destroy (src);
+       } else
+           status = src->status;
+
+       cairo_surface_destroy (mask);
+    }
+
+    if (status == CAIRO_INT_STATUS_SUCCESS && ! extents->is_bounded)
+       status = fixup_unbounded_boxes (compositor, extents, boxes);
+
+    return status;
+}
+
+static cairo_bool_t
+composite_needs_clip (const cairo_composite_rectangles_t *composit,
+                     const cairo_box_t *extents)
+{
+    cairo_bool_t needs_clip;
+
+    if (composit->clip && composit->clip->path != NULL)
+       return TRUE;
+
+    needs_clip = ! composit->is_bounded;
+    if (needs_clip)
+       needs_clip = ! _cairo_clip_contains_box (composit->clip, extents);
+
+    return needs_clip;
+}
+
+static cairo_int_status_t
+composite_boxes (const cairo_spans_compositor_t *compositor,
+                cairo_composite_rectangles_t *extents,
+                cairo_boxes_t          *boxes)
+{
+    cairo_abstract_span_renderer_t renderer;
+    cairo_rectangular_scan_converter_t converter;
+    const struct _cairo_boxes_chunk *chunk;
+    cairo_int_status_t status;
+    cairo_box_t box;
+
+    _cairo_box_from_rectangle (&box, &extents->bounded);
+    if (composite_needs_clip (extents, &box))
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    _cairo_rectangular_scan_converter_init (&converter, &extents->bounded);
+    for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
+       const cairo_box_t *box = chunk->base;
+       int i;
+
+       for (i = 0; i < chunk->count; i++) {
+           status = _cairo_rectangular_scan_converter_add_box (&converter, &box[i], 1);
+           if (unlikely (status))
+               goto cleanup_converter;
+       }
+    }
+
+    status = compositor->renderer_init (&renderer, extents, FALSE);
+    if (likely (status == CAIRO_INT_STATUS_SUCCESS))
+       status = converter.base.generate (&converter.base, &renderer.base);
+    compositor->renderer_fini (&renderer, status);
+
+cleanup_converter:
+    converter.base.destroy (&converter.base);
+    return status;
+}
+
+static cairo_int_status_t
+composite_polygon (const cairo_spans_compositor_t      *compositor,
+                  cairo_composite_rectangles_t          *extents,
+                  cairo_polygon_t                      *polygon,
+                  cairo_fill_rule_t                     fill_rule,
+                  cairo_antialias_t                     antialias)
+{
+    cairo_abstract_span_renderer_t renderer;
+    cairo_scan_converter_t *converter;
+    cairo_bool_t needs_clip;
+    cairo_int_status_t status;
+
+    needs_clip = composite_needs_clip (extents, &polygon->extents);
+    if (needs_clip) {
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+       converter = _cairo_clip_tor_scan_converter_create (extents->clip,
+                                                          polygon,
+                                                          fill_rule, antialias);
+    } else {
+       const cairo_rectangle_int_t *r = &extents->bounded;
+
+       if (antialias == CAIRO_ANTIALIAS_FAST) {
+           converter = _cairo_tor22_scan_converter_create (r->x, r->y,
+                                                           r->x + r->width,
+                                                           r->y + r->height,
+                                                           fill_rule, antialias);
+           status = _cairo_tor22_scan_converter_add_polygon (converter, polygon);
+       } else if (antialias == CAIRO_ANTIALIAS_NONE) {
+           converter = _cairo_mono_scan_converter_create (r->x, r->y,
+                                                          r->x + r->width,
+                                                          r->y + r->height,
+                                                          fill_rule);
+           status = _cairo_mono_scan_converter_add_polygon (converter, polygon);
+       } else {
+           converter = _cairo_tor_scan_converter_create (r->x, r->y,
+                                                         r->x + r->width,
+                                                         r->y + r->height,
+                                                         fill_rule, antialias);
+           status = _cairo_tor_scan_converter_add_polygon (converter, polygon);
+       }
+    }
+    if (unlikely (status))
+       goto cleanup_converter;
+
+    status = compositor->renderer_init (&renderer, extents, needs_clip);
+    if (likely (status == CAIRO_INT_STATUS_SUCCESS))
+       status = converter->generate (converter, &renderer.base);
+    compositor->renderer_fini (&renderer, status);
+
+cleanup_converter:
+    converter->destroy (converter);
+    return status;
+}
+
+static cairo_int_status_t
+trim_extents_to_boxes (cairo_composite_rectangles_t *extents,
+                      cairo_boxes_t *boxes)
+{
+    cairo_box_t box;
+
+    _cairo_boxes_extents (boxes, &box);
+    return _cairo_composite_rectangles_intersect_mask_extents (extents, &box);
+}
+
+static cairo_int_status_t
+trim_extents_to_polygon (cairo_composite_rectangles_t *extents,
+                        cairo_polygon_t *polygon)
+{
+    return _cairo_composite_rectangles_intersect_mask_extents (extents,
+                                                              &polygon->extents);
+}
+
+static cairo_int_status_t
+clip_and_composite_boxes (const cairo_spans_compositor_t       *compositor,
+                         cairo_composite_rectangles_t          *extents,
+                         cairo_boxes_t                         *boxes)
+{
+    cairo_int_status_t status;
+    cairo_polygon_t polygon;
+    struct _cairo_boxes_chunk *chunk;
+    int i;
+
+    status = trim_extents_to_boxes (extents, boxes);
+    if (unlikely (status))
+       return status;
+
+    if (boxes->num_boxes == 0) {
+       if (extents->is_bounded)
+           return CAIRO_STATUS_SUCCESS;
+
+       return fixup_unbounded_boxes (compositor, extents, boxes);
+    }
+
+    /* Can we reduce drawing through a clip-mask to simply drawing the clip? */
+    if (extents->clip->path != NULL && extents->is_bounded) {
+       cairo_polygon_t polygon;
+       cairo_fill_rule_t fill_rule;
+       cairo_antialias_t antialias;
+       cairo_clip_t *clip;
+
+       clip = _cairo_clip_copy (extents->clip);
+       clip = _cairo_clip_intersect_boxes (clip, boxes);
+       status = _cairo_clip_get_polygon (clip, &polygon,
+                                         &fill_rule, &antialias);
+       _cairo_clip_path_destroy (clip->path);
+       clip->path = NULL;
+       if (likely (status == CAIRO_INT_STATUS_SUCCESS)) {
+           cairo_clip_t *saved_clip = extents->clip;
+           extents->clip = clip;
+
+           status = clip_and_composite_polygon (compositor, extents, &polygon,
+                                                fill_rule, antialias);
+
+           clip = extents->clip;
+           extents->clip = saved_clip;
+
+           _cairo_polygon_fini (&polygon);
+       }
+       _cairo_clip_destroy (clip);
+
+       if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+           return status;
+    }
+
+    if (boxes->is_pixel_aligned) {
+       status = composite_aligned_boxes (compositor, extents, boxes);
+       if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+           return status;
+    }
+
+    status = composite_boxes (compositor, extents, boxes);
+    if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+       return status;
+
+    status = _cairo_polygon_init_boxes (&polygon, boxes);
+    if (unlikely (status))
+       return status;
+
+    status = composite_polygon (compositor, extents, &polygon,
+                               CAIRO_FILL_RULE_WINDING,
+                               CAIRO_ANTIALIAS_DEFAULT);
+    _cairo_polygon_fini (&polygon);
+
+    return status;
+}
+
+static cairo_int_status_t
+clip_and_composite_polygon (const cairo_spans_compositor_t     *compositor,
+                           cairo_composite_rectangles_t         *extents,
+                           cairo_polygon_t                     *polygon,
+                           cairo_fill_rule_t                    fill_rule,
+                           cairo_antialias_t                    antialias)
+{
+    cairo_int_status_t status;
+
+    /* XXX simply uses polygon limits.point extemities, tessellation? */
+    status = trim_extents_to_polygon (extents, polygon);
+    if (unlikely (status))
+       return status;
+
+    if (_cairo_polygon_is_empty (polygon)) {
+       cairo_boxes_t boxes;
+
+       if (extents->is_bounded)
+           return CAIRO_STATUS_SUCCESS;
+
+       _cairo_boxes_init (&boxes);
+       extents->bounded.width = extents->bounded.height = 0;
+       return fixup_unbounded_boxes (compositor, extents, &boxes);
+    }
+
+    if (extents->is_bounded && extents->clip->path) {
+       cairo_polygon_t clipper;
+       cairo_antialias_t clip_antialias;
+       cairo_fill_rule_t clip_fill_rule;
+
+       status = _cairo_clip_get_polygon (extents->clip,
+                                         &clipper,
+                                         &clip_fill_rule,
+                                         &clip_antialias);
+       if (likely (status == CAIRO_INT_STATUS_SUCCESS)) {
+           cairo_clip_t *old_clip;
+
+           if (clip_antialias == antialias) {
+               /* refresh limits after trimming extents */
+               _cairo_polygon_limit_to_clip(polygon, extents->clip);
+
+               status = _cairo_polygon_intersect (polygon, fill_rule,
+                                                  &clipper, clip_fill_rule);
+               _cairo_polygon_fini (&clipper);
+               if (unlikely (status))
+                   return status;
+
+               old_clip = extents->clip;
+               extents->clip = _cairo_clip_copy_region (extents->clip);
+               _cairo_clip_destroy (old_clip);
+           } else {
+               _cairo_polygon_fini (&clipper);
+           }
+       }
+    }
+
+    return composite_polygon (compositor, extents,
+                             polygon, fill_rule, antialias);
+}
+
+/* high-level compositor interface */
+
+static cairo_int_status_t
+_cairo_spans_compositor_paint (const cairo_compositor_t                *_compositor,
+                              cairo_composite_rectangles_t     *extents)
+{
+    const cairo_spans_compositor_t *compositor = (cairo_spans_compositor_t*)_compositor;
+    cairo_boxes_t boxes;
+    cairo_int_status_t status;
+
+    _cairo_clip_steal_boxes (extents->clip, &boxes);
+    status = clip_and_composite_boxes (compositor, extents, &boxes);
+    _cairo_clip_unsteal_boxes (extents->clip, &boxes);
+
+    return status;
+}
+
+static cairo_int_status_t
+_cairo_spans_compositor_mask (const cairo_compositor_t         *_compositor,
+                             cairo_composite_rectangles_t      *extents)
+{
+    const cairo_spans_compositor_t *compositor = (cairo_spans_compositor_t*)_compositor;
+    cairo_int_status_t status;
+    cairo_boxes_t boxes;
+
+    _cairo_clip_steal_boxes (extents->clip, &boxes);
+    status = clip_and_composite_boxes (compositor, extents, &boxes);
+    _cairo_clip_unsteal_boxes (extents->clip, &boxes);
+
+    return status;
+}
+
+static cairo_int_status_t
+_cairo_spans_compositor_stroke (const cairo_compositor_t       *_compositor,
+                               cairo_composite_rectangles_t     *extents,
+                               const cairo_path_fixed_t        *path,
+                               const cairo_stroke_style_t      *style,
+                               const cairo_matrix_t            *ctm,
+                               const cairo_matrix_t            *ctm_inverse,
+                               double                           tolerance,
+                               cairo_antialias_t                antialias)
+{
+    const cairo_spans_compositor_t *compositor = (cairo_spans_compositor_t*)_compositor;
+    cairo_int_status_t status;
+
+    status = CAIRO_INT_STATUS_UNSUPPORTED;
+    if (_cairo_path_fixed_stroke_is_rectilinear (path)) {
+       cairo_boxes_t boxes;
+
+       _cairo_boxes_init (&boxes);
+       if (! _cairo_clip_contains_rectangle (extents->clip, &extents->mask))
+           _cairo_boxes_limit (&boxes,
+                               extents->clip->boxes,
+                               extents->clip->num_boxes);
+
+       status = _cairo_path_fixed_stroke_rectilinear_to_boxes (path,
+                                                               style,
+                                                               ctm,
+                                                               antialias,
+                                                               &boxes);
+       if (likely (status == CAIRO_INT_STATUS_SUCCESS))
+           status = clip_and_composite_boxes (compositor, extents, &boxes);
+       _cairo_boxes_fini (&boxes);
+    }
+
+    if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
+       cairo_polygon_t polygon;
+
+       if (extents->mask.width  > extents->unbounded.width ||
+           extents->mask.height > extents->unbounded.height)
+       {
+           _cairo_polygon_init_with_clip (&polygon, extents->clip);
+       }
+       else
+       {
+           _cairo_polygon_init_with_clip (&polygon, NULL);
+       }
+       status = _cairo_path_fixed_stroke_to_polygon (path,
+                                                     style,
+                                                     ctm, ctm_inverse,
+                                                     tolerance,
+                                                     &polygon);
+       if (likely (status == CAIRO_INT_STATUS_SUCCESS)) {
+           status = clip_and_composite_polygon (compositor, extents, &polygon,
+                                                CAIRO_FILL_RULE_WINDING,
+                                                antialias);
+       }
+       _cairo_polygon_fini (&polygon);
+    }
+
+    return status;
+}
+
+static cairo_int_status_t
+_cairo_spans_compositor_fill (const cairo_compositor_t         *_compositor,
+                             cairo_composite_rectangles_t       *extents,
+                             const cairo_path_fixed_t          *path,
+                             cairo_fill_rule_t                  fill_rule,
+                             double                             tolerance,
+                             cairo_antialias_t                  antialias)
+{
+    const cairo_spans_compositor_t *compositor = (cairo_spans_compositor_t*)_compositor;
+    cairo_int_status_t status;
+
+    status = CAIRO_INT_STATUS_UNSUPPORTED;
+    if (_cairo_path_fixed_fill_is_rectilinear (path)) {
+       cairo_boxes_t boxes;
+
+       _cairo_boxes_init (&boxes);
+       if (! _cairo_clip_contains_rectangle (extents->clip, &extents->mask))
+           _cairo_boxes_limit (&boxes,
+                               extents->clip->boxes,
+                               extents->clip->num_boxes);
+       status = _cairo_path_fixed_fill_rectilinear_to_boxes (path,
+                                                             fill_rule,
+                                                             antialias,
+                                                             &boxes);
+       if (likely (status == CAIRO_INT_STATUS_SUCCESS))
+           status = clip_and_composite_boxes (compositor, extents, &boxes);
+       _cairo_boxes_fini (&boxes);
+    }
+    if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
+       cairo_polygon_t polygon;
+
+       if (extents->mask.width  > extents->unbounded.width ||
+           extents->mask.height > extents->unbounded.height)
+       {
+           cairo_box_t limits;
+           _cairo_box_from_rectangle (&limits, &extents->unbounded);
+           _cairo_polygon_init (&polygon, &limits, 1);
+       }
+       else
+       {
+           _cairo_polygon_init (&polygon, NULL, 0);
+       }
+
+       status = _cairo_path_fixed_fill_to_polygon (path, tolerance, &polygon);
+       if (likely (status == CAIRO_INT_STATUS_SUCCESS)) {
+           status = _cairo_polygon_intersect_with_boxes (&polygon, &fill_rule,
+                                                         extents->clip->boxes,
+                                                         extents->clip->num_boxes);
+       }
+       if (likely (status == CAIRO_INT_STATUS_SUCCESS)) {
+           if (extents->is_bounded) {
+               if (extents->clip->boxes != &extents->clip->embedded_box)
+                   free (extents->clip->boxes);
+
+               extents->clip->num_boxes = 1;
+               extents->clip->boxes = &extents->clip->embedded_box;
+               extents->clip->boxes[0] = polygon.extents;
+           }
+           status = clip_and_composite_polygon (compositor, extents, &polygon,
+                                                fill_rule, antialias);
+       }
+       _cairo_polygon_fini (&polygon);
+    }
+
+    return status;
+}
+
+void
+_cairo_spans_compositor_init (cairo_spans_compositor_t *compositor,
+                             const cairo_compositor_t  *delegate)
+{
+    compositor->base.delegate = delegate;
+
+    compositor->base.paint  = _cairo_spans_compositor_paint;
+    compositor->base.mask   = _cairo_spans_compositor_mask;
+    compositor->base.fill   = _cairo_spans_compositor_fill;
+    compositor->base.stroke = _cairo_spans_compositor_stroke;
+    compositor->base.glyphs = NULL;
+}
diff --git a/src/cairo-spans-private.h b/src/cairo-spans-private.h
new file mode 100644 (file)
index 0000000..c42b5af
--- /dev/null
@@ -0,0 +1,206 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright (c) 2008  M Joonas Pihlaja
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef CAIRO_SPANS_PRIVATE_H
+#define CAIRO_SPANS_PRIVATE_H
+#include "cairo-types-private.h"
+#include "cairo-compiler-private.h"
+
+/* Number of bits of precision used for alpha. */
+#define CAIRO_SPANS_UNIT_COVERAGE_BITS 8
+#define CAIRO_SPANS_UNIT_COVERAGE ((1 << CAIRO_SPANS_UNIT_COVERAGE_BITS)-1)
+
+/* A structure representing an open-ended horizontal span of constant
+ * pixel coverage. */
+typedef struct _cairo_half_open_span {
+    int32_t x; /* The inclusive x-coordinate of the start of the span. */
+    uint8_t coverage; /* The pixel coverage for the pixels to the right. */
+    uint8_t inverse; /* between regular mask and clip */
+} cairo_half_open_span_t;
+
+/* Span renderer interface. Instances of renderers are provided by
+ * surfaces if they want to composite spans instead of trapezoids. */
+typedef struct _cairo_span_renderer cairo_span_renderer_t;
+struct _cairo_span_renderer {
+    /* Private status variable. */
+    cairo_status_t status;
+
+    /* Called to destroy the renderer. */
+    cairo_destroy_func_t       destroy;
+
+    /* Render the spans on row y of the destination by whatever compositing
+     * method is required. */
+    cairo_warn cairo_status_t
+    (*render_rows) (void *abstract_renderer,
+                   int y, int height,
+                   const cairo_half_open_span_t        *coverages,
+                   unsigned num_coverages);
+
+    /* Called after all rows have been rendered to perform whatever
+     * final rendering step is required.  This function is called just
+     * once before the renderer is destroyed. */
+    cairo_status_t (*finish) (void *abstract_renderer);
+};
+
+/* Scan converter interface. */
+typedef struct _cairo_scan_converter cairo_scan_converter_t;
+struct _cairo_scan_converter {
+    /* Destroy this scan converter. */
+    cairo_destroy_func_t       destroy;
+
+    /* Generates coverage spans for rows for the added edges and calls
+     * the renderer function for each row. After generating spans the
+     * only valid thing to do with the converter is to destroy it. */
+    cairo_status_t (*generate) (void                   *abstract_converter,
+                               cairo_span_renderer_t   *renderer);
+
+    /* Private status. Read with _cairo_scan_converter_status(). */
+    cairo_status_t status;
+};
+
+/* Scan converter constructors. */
+
+cairo_private cairo_scan_converter_t *
+_cairo_tor_scan_converter_create (int                  xmin,
+                                 int                   ymin,
+                                 int                   xmax,
+                                 int                   ymax,
+                                 cairo_fill_rule_t     fill_rule,
+                                 cairo_antialias_t     antialias);
+cairo_private cairo_status_t
+_cairo_tor_scan_converter_add_polygon (void            *converter,
+                                      const cairo_polygon_t *polygon);
+
+cairo_private cairo_scan_converter_t *
+_cairo_tor22_scan_converter_create (int                        xmin,
+                                   int                 ymin,
+                                   int                 xmax,
+                                   int                 ymax,
+                                   cairo_fill_rule_t   fill_rule,
+                                   cairo_antialias_t   antialias);
+cairo_private cairo_status_t
+_cairo_tor22_scan_converter_add_polygon (void          *converter,
+                                        const cairo_polygon_t *polygon);
+
+cairo_private cairo_scan_converter_t *
+_cairo_mono_scan_converter_create (int                 xmin,
+                                  int                  ymin,
+                                  int                  xmax,
+                                  int                  ymax,
+                                  cairo_fill_rule_t    fill_rule);
+cairo_private cairo_status_t
+_cairo_mono_scan_converter_add_polygon (void           *converter,
+                                       const cairo_polygon_t *polygon);
+
+cairo_private cairo_scan_converter_t *
+_cairo_clip_tor_scan_converter_create (cairo_clip_t *clip,
+                                      cairo_polygon_t *polygon,
+                                      cairo_fill_rule_t fill_rule,
+                                      cairo_antialias_t antialias);
+
+typedef struct _cairo_rectangular_scan_converter {
+    cairo_scan_converter_t base;
+
+    cairo_box_t extents;
+
+    struct _cairo_rectangular_scan_converter_chunk {
+       struct _cairo_rectangular_scan_converter_chunk *next;
+       void *base;
+       int count;
+       int size;
+    } chunks, *tail;
+    char buf[CAIRO_STACK_BUFFER_SIZE];
+    int num_rectangles;
+} cairo_rectangular_scan_converter_t;
+
+cairo_private void
+_cairo_rectangular_scan_converter_init (cairo_rectangular_scan_converter_t *self,
+                                       const cairo_rectangle_int_t *extents);
+
+cairo_private cairo_status_t
+_cairo_rectangular_scan_converter_add_box (cairo_rectangular_scan_converter_t *self,
+                                          const cairo_box_t *box,
+                                          int dir);
+
+typedef struct _cairo_botor_scan_converter {
+    cairo_scan_converter_t base;
+
+    cairo_box_t extents;
+    cairo_fill_rule_t fill_rule;
+
+    int xmin, xmax;
+
+    struct _cairo_botor_scan_converter_chunk {
+       struct _cairo_botor_scan_converter_chunk *next;
+       void *base;
+       int count;
+       int size;
+    } chunks, *tail;
+    char buf[CAIRO_STACK_BUFFER_SIZE];
+    int num_edges;
+} cairo_botor_scan_converter_t;
+
+cairo_private void
+_cairo_botor_scan_converter_init (cairo_botor_scan_converter_t *self,
+                                 const cairo_box_t *extents,
+                                 cairo_fill_rule_t fill_rule);
+
+/* cairo-spans.c: */
+
+cairo_private cairo_scan_converter_t *
+_cairo_scan_converter_create_in_error (cairo_status_t error);
+
+cairo_private cairo_status_t
+_cairo_scan_converter_status (void *abstract_converter);
+
+cairo_private cairo_status_t
+_cairo_scan_converter_set_error (void *abstract_converter,
+                                cairo_status_t error);
+
+cairo_private cairo_span_renderer_t *
+_cairo_span_renderer_create_in_error (cairo_status_t error);
+
+cairo_private cairo_status_t
+_cairo_span_renderer_status (void *abstract_renderer);
+
+/* Set the renderer into an error state.  This sets all the method
+ * pointers except ->destroy() of the renderer to no-op
+ * implementations that just return the error status. */
+cairo_private cairo_status_t
+_cairo_span_renderer_set_error (void *abstract_renderer,
+                               cairo_status_t error);
+
+cairo_private cairo_status_t
+_cairo_surface_composite_polygon (cairo_surface_t      *surface,
+                                 cairo_operator_t       op,
+                                 const cairo_pattern_t *pattern,
+                                 cairo_fill_rule_t     fill_rule,
+                                 cairo_antialias_t     antialias,
+                                 const cairo_composite_rectangles_t *rects,
+                                 cairo_polygon_t       *polygon,
+                                 cairo_region_t        *clip_region);
+
+#endif /* CAIRO_SPANS_PRIVATE_H */
diff --git a/src/cairo-spans.c b/src/cairo-spans.c
new file mode 100644 (file)
index 0000000..b8d4180
--- /dev/null
@@ -0,0 +1,248 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright (c) 2008  M Joonas Pihlaja
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "cairoint.h"
+
+#include "cairo-composite-rectangles-private.h"
+#include "cairo-clip-private.h"
+#include "cairo-error-private.h"
+#include "cairo-fixed-private.h"
+#include "cairo-types-private.h"
+
+static void
+_cairo_nil_destroy (void *abstract)
+{
+    (void) abstract;
+}
+
+static cairo_status_t
+_cairo_nil_scan_converter_generate (void *abstract_converter,
+                                   cairo_span_renderer_t *renderer)
+{
+    (void) abstract_converter;
+    (void) renderer;
+    return _cairo_scan_converter_status (abstract_converter);
+}
+
+cairo_status_t
+_cairo_scan_converter_status (void *abstract_converter)
+{
+    cairo_scan_converter_t *converter = abstract_converter;
+    return converter->status;
+}
+
+cairo_status_t
+_cairo_scan_converter_set_error (void *abstract_converter,
+                                cairo_status_t error)
+{
+    cairo_scan_converter_t *converter = abstract_converter;
+    if (error == CAIRO_STATUS_SUCCESS)
+       ASSERT_NOT_REACHED;
+    if (converter->status == CAIRO_STATUS_SUCCESS) {
+       converter->generate = _cairo_nil_scan_converter_generate;
+       converter->status = error;
+    }
+    return converter->status;
+}
+
+static void
+_cairo_nil_scan_converter_init (cairo_scan_converter_t *converter,
+                               cairo_status_t status)
+{
+    converter->destroy = _cairo_nil_destroy;
+    converter->status = CAIRO_STATUS_SUCCESS;
+    status = _cairo_scan_converter_set_error (converter, status);
+}
+
+cairo_scan_converter_t *
+_cairo_scan_converter_create_in_error (cairo_status_t status)
+{
+#define RETURN_NIL {\
+           static cairo_scan_converter_t nil;\
+           _cairo_nil_scan_converter_init (&nil, status);\
+           return &nil;\
+       }
+    switch (status) {
+    case CAIRO_STATUS_SUCCESS:
+    case CAIRO_STATUS_LAST_STATUS:
+       ASSERT_NOT_REACHED;
+       break;
+    case CAIRO_STATUS_INVALID_RESTORE: RETURN_NIL;
+    case CAIRO_STATUS_INVALID_POP_GROUP: RETURN_NIL;
+    case CAIRO_STATUS_NO_CURRENT_POINT: RETURN_NIL;
+    case CAIRO_STATUS_INVALID_MATRIX: RETURN_NIL;
+    case CAIRO_STATUS_INVALID_STATUS: RETURN_NIL;
+    case CAIRO_STATUS_NULL_POINTER: RETURN_NIL;
+    case CAIRO_STATUS_INVALID_STRING: RETURN_NIL;
+    case CAIRO_STATUS_INVALID_PATH_DATA: RETURN_NIL;
+    case CAIRO_STATUS_READ_ERROR: RETURN_NIL;
+    case CAIRO_STATUS_WRITE_ERROR: RETURN_NIL;
+    case CAIRO_STATUS_SURFACE_FINISHED: RETURN_NIL;
+    case CAIRO_STATUS_SURFACE_TYPE_MISMATCH: RETURN_NIL;
+    case CAIRO_STATUS_PATTERN_TYPE_MISMATCH: RETURN_NIL;
+    case CAIRO_STATUS_INVALID_CONTENT: RETURN_NIL;
+    case CAIRO_STATUS_INVALID_FORMAT: RETURN_NIL;
+    case CAIRO_STATUS_INVALID_VISUAL: RETURN_NIL;
+    case CAIRO_STATUS_FILE_NOT_FOUND: RETURN_NIL;
+    case CAIRO_STATUS_INVALID_DASH: RETURN_NIL;
+    case CAIRO_STATUS_INVALID_DSC_COMMENT: RETURN_NIL;
+    case CAIRO_STATUS_INVALID_INDEX: RETURN_NIL;
+    case CAIRO_STATUS_CLIP_NOT_REPRESENTABLE: RETURN_NIL;
+    case CAIRO_STATUS_TEMP_FILE_ERROR: RETURN_NIL;
+    case CAIRO_STATUS_INVALID_STRIDE: RETURN_NIL;
+    case CAIRO_STATUS_FONT_TYPE_MISMATCH: RETURN_NIL;
+    case CAIRO_STATUS_USER_FONT_IMMUTABLE: RETURN_NIL;
+    case CAIRO_STATUS_USER_FONT_ERROR: RETURN_NIL;
+    case CAIRO_STATUS_NEGATIVE_COUNT: RETURN_NIL;
+    case CAIRO_STATUS_INVALID_CLUSTERS: RETURN_NIL;
+    case CAIRO_STATUS_INVALID_SLANT: RETURN_NIL;
+    case CAIRO_STATUS_INVALID_WEIGHT: RETURN_NIL;
+    case CAIRO_STATUS_NO_MEMORY: RETURN_NIL;
+    case CAIRO_STATUS_INVALID_SIZE: RETURN_NIL;
+    case CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED: RETURN_NIL;
+    case CAIRO_STATUS_DEVICE_TYPE_MISMATCH: RETURN_NIL;
+    case CAIRO_STATUS_DEVICE_ERROR: RETURN_NIL;
+    case CAIRO_STATUS_INVALID_MESH_CONSTRUCTION: RETURN_NIL;
+    case CAIRO_STATUS_DEVICE_FINISHED: RETURN_NIL;
+    default:
+       break;
+    }
+    status = CAIRO_STATUS_NO_MEMORY;
+    RETURN_NIL;
+#undef RETURN_NIL
+}
+
+static cairo_status_t
+_cairo_nil_span_renderer_render_rows (
+    void                               *abstract_renderer,
+    int                                         y,
+    int                                         height,
+    const cairo_half_open_span_t       *coverages,
+    unsigned                            num_coverages)
+{
+    (void) y;
+    (void) height;
+    (void) coverages;
+    (void) num_coverages;
+    return _cairo_span_renderer_status (abstract_renderer);
+}
+
+static cairo_status_t
+_cairo_nil_span_renderer_finish (void *abstract_renderer)
+{
+    return _cairo_span_renderer_status (abstract_renderer);
+}
+
+cairo_status_t
+_cairo_span_renderer_status (void *abstract_renderer)
+{
+    cairo_span_renderer_t *renderer = abstract_renderer;
+    return renderer->status;
+}
+
+cairo_status_t
+_cairo_span_renderer_set_error (
+    void *abstract_renderer,
+    cairo_status_t error)
+{
+    cairo_span_renderer_t *renderer = abstract_renderer;
+    if (error == CAIRO_STATUS_SUCCESS) {
+       ASSERT_NOT_REACHED;
+    }
+    if (renderer->status == CAIRO_STATUS_SUCCESS) {
+       renderer->render_rows = _cairo_nil_span_renderer_render_rows;
+       renderer->finish = _cairo_nil_span_renderer_finish;
+       renderer->status = error;
+    }
+    return renderer->status;
+}
+
+static void
+_cairo_nil_span_renderer_init (cairo_span_renderer_t *renderer,
+                              cairo_status_t status)
+{
+    renderer->destroy = _cairo_nil_destroy;
+    renderer->status = CAIRO_STATUS_SUCCESS;
+    status = _cairo_span_renderer_set_error (renderer, status);
+}
+
+cairo_span_renderer_t *
+_cairo_span_renderer_create_in_error (cairo_status_t status)
+{
+#define RETURN_NIL {\
+           static cairo_span_renderer_t nil;\
+           _cairo_nil_span_renderer_init (&nil, status);\
+           return &nil;\
+       }
+    switch (status) {
+    case CAIRO_STATUS_SUCCESS:
+    case CAIRO_STATUS_LAST_STATUS:
+       ASSERT_NOT_REACHED;
+       break;
+    case CAIRO_STATUS_INVALID_RESTORE: RETURN_NIL;
+    case CAIRO_STATUS_INVALID_POP_GROUP: RETURN_NIL;
+    case CAIRO_STATUS_NO_CURRENT_POINT: RETURN_NIL;
+    case CAIRO_STATUS_INVALID_MATRIX: RETURN_NIL;
+    case CAIRO_STATUS_INVALID_STATUS: RETURN_NIL;
+    case CAIRO_STATUS_NULL_POINTER: RETURN_NIL;
+    case CAIRO_STATUS_INVALID_STRING: RETURN_NIL;
+    case CAIRO_STATUS_INVALID_PATH_DATA: RETURN_NIL;
+    case CAIRO_STATUS_READ_ERROR: RETURN_NIL;
+    case CAIRO_STATUS_WRITE_ERROR: RETURN_NIL;
+    case CAIRO_STATUS_SURFACE_FINISHED: RETURN_NIL;
+    case CAIRO_STATUS_SURFACE_TYPE_MISMATCH: RETURN_NIL;
+    case CAIRO_STATUS_PATTERN_TYPE_MISMATCH: RETURN_NIL;
+    case CAIRO_STATUS_INVALID_CONTENT: RETURN_NIL;
+    case CAIRO_STATUS_INVALID_FORMAT: RETURN_NIL;
+    case CAIRO_STATUS_INVALID_VISUAL: RETURN_NIL;
+    case CAIRO_STATUS_FILE_NOT_FOUND: RETURN_NIL;
+    case CAIRO_STATUS_INVALID_DASH: RETURN_NIL;
+    case CAIRO_STATUS_INVALID_DSC_COMMENT: RETURN_NIL;
+    case CAIRO_STATUS_INVALID_INDEX: RETURN_NIL;
+    case CAIRO_STATUS_CLIP_NOT_REPRESENTABLE: RETURN_NIL;
+    case CAIRO_STATUS_TEMP_FILE_ERROR: RETURN_NIL;
+    case CAIRO_STATUS_INVALID_STRIDE: RETURN_NIL;
+    case CAIRO_STATUS_FONT_TYPE_MISMATCH: RETURN_NIL;
+    case CAIRO_STATUS_USER_FONT_IMMUTABLE: RETURN_NIL;
+    case CAIRO_STATUS_USER_FONT_ERROR: RETURN_NIL;
+    case CAIRO_STATUS_NEGATIVE_COUNT: RETURN_NIL;
+    case CAIRO_STATUS_INVALID_CLUSTERS: RETURN_NIL;
+    case CAIRO_STATUS_INVALID_SLANT: RETURN_NIL;
+    case CAIRO_STATUS_INVALID_WEIGHT: RETURN_NIL;
+    case CAIRO_STATUS_NO_MEMORY: RETURN_NIL;
+    case CAIRO_STATUS_INVALID_SIZE: RETURN_NIL;
+    case CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED: RETURN_NIL;
+    case CAIRO_STATUS_DEVICE_TYPE_MISMATCH: RETURN_NIL;
+    case CAIRO_STATUS_DEVICE_ERROR: RETURN_NIL;
+    case CAIRO_STATUS_INVALID_MESH_CONSTRUCTION: RETURN_NIL;
+    case CAIRO_STATUS_DEVICE_FINISHED: RETURN_NIL;
+    default:
+       break;
+    }
+    status = CAIRO_STATUS_NO_MEMORY;
+    RETURN_NIL;
+#undef RETURN_NIL
+}
diff --git a/src/cairo-spline.c b/src/cairo-spline.c
new file mode 100644 (file)
index 0000000..34ad585
--- /dev/null
@@ -0,0 +1,424 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *     Carl D. Worth <cworth@cworth.org>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-box-private.h"
+#include "cairo-slope-private.h"
+
+cairo_bool_t
+_cairo_spline_intersects (const cairo_point_t *a,
+                         const cairo_point_t *b,
+                         const cairo_point_t *c,
+                         const cairo_point_t *d,
+                         const cairo_box_t *box)
+{
+    cairo_box_t bounds;
+
+    if (_cairo_box_contains_point (box, a) ||
+       _cairo_box_contains_point (box, b) ||
+       _cairo_box_contains_point (box, c) ||
+       _cairo_box_contains_point (box, d))
+    {
+       return TRUE;
+    }
+
+    bounds.p2 = bounds.p1 = *a;
+    _cairo_box_add_point (&bounds, b);
+    _cairo_box_add_point (&bounds, c);
+    _cairo_box_add_point (&bounds, d);
+
+    if (bounds.p2.x <= box->p1.x || bounds.p1.x >= box->p2.x ||
+       bounds.p2.y <= box->p1.y || bounds.p1.y >= box->p2.y)
+    {
+       return FALSE;
+    }
+
+#if 0 /* worth refining? */
+    bounds.p2 = bounds.p1 = *a;
+    _cairo_box_add_curve_to (&bounds, b, c, d);
+    if (bounds.p2.x <= box->p1.x || bounds.p1.x >= box->p2.x ||
+       bounds.p2.y <= box->p1.y || bounds.p1.y >= box->p2.y)
+    {
+       return FALSE;
+    }
+#endif
+
+    return TRUE;
+}
+
+cairo_bool_t
+_cairo_spline_init (cairo_spline_t *spline,
+                   cairo_spline_add_point_func_t add_point_func,
+                   void *closure,
+                   const cairo_point_t *a, const cairo_point_t *b,
+                   const cairo_point_t *c, const cairo_point_t *d)
+{
+    /* If both tangents are zero, this is just a straight line */
+    if (a->x == b->x && a->y == b->y && c->x == d->x && c->y == d->y)
+       return FALSE;
+
+    spline->add_point_func = add_point_func;
+    spline->closure = closure;
+
+    spline->knots.a = *a;
+    spline->knots.b = *b;
+    spline->knots.c = *c;
+    spline->knots.d = *d;
+
+    if (a->x != b->x || a->y != b->y)
+       _cairo_slope_init (&spline->initial_slope, &spline->knots.a, &spline->knots.b);
+    else if (a->x != c->x || a->y != c->y)
+       _cairo_slope_init (&spline->initial_slope, &spline->knots.a, &spline->knots.c);
+    else if (a->x != d->x || a->y != d->y)
+       _cairo_slope_init (&spline->initial_slope, &spline->knots.a, &spline->knots.d);
+    else
+       return FALSE;
+
+    if (c->x != d->x || c->y != d->y)
+       _cairo_slope_init (&spline->final_slope, &spline->knots.c, &spline->knots.d);
+    else if (b->x != d->x || b->y != d->y)
+       _cairo_slope_init (&spline->final_slope, &spline->knots.b, &spline->knots.d);
+    else
+       return FALSE; /* just treat this as a straight-line from a -> d */
+
+    /* XXX if the initial, final and vector are all equal, this is just a line */
+
+    return TRUE;
+}
+
+static cairo_status_t
+_cairo_spline_add_point (cairo_spline_t *spline,
+                        const cairo_point_t *point,
+                        const cairo_point_t *knot)
+{
+    cairo_point_t *prev;
+    cairo_slope_t slope;
+
+    prev = &spline->last_point;
+    if (prev->x == point->x && prev->y == point->y)
+       return CAIRO_STATUS_SUCCESS;
+
+    _cairo_slope_init (&slope, point, knot);
+
+    spline->last_point = *point;
+    return spline->add_point_func (spline->closure, point, &slope);
+}
+
+static void
+_lerp_half (const cairo_point_t *a, const cairo_point_t *b, cairo_point_t *result)
+{
+    result->x = a->x + ((b->x - a->x) >> 1);
+    result->y = a->y + ((b->y - a->y) >> 1);
+}
+
+static void
+_de_casteljau (cairo_spline_knots_t *s1, cairo_spline_knots_t *s2)
+{
+    cairo_point_t ab, bc, cd;
+    cairo_point_t abbc, bccd;
+    cairo_point_t final;
+
+    _lerp_half (&s1->a, &s1->b, &ab);
+    _lerp_half (&s1->b, &s1->c, &bc);
+    _lerp_half (&s1->c, &s1->d, &cd);
+    _lerp_half (&ab, &bc, &abbc);
+    _lerp_half (&bc, &cd, &bccd);
+    _lerp_half (&abbc, &bccd, &final);
+
+    s2->a = final;
+    s2->b = bccd;
+    s2->c = cd;
+    s2->d = s1->d;
+
+    s1->b = ab;
+    s1->c = abbc;
+    s1->d = final;
+}
+
+/* Return an upper bound on the error (squared) that could result from
+ * approximating a spline as a line segment connecting the two endpoints. */
+static double
+_cairo_spline_error_squared (const cairo_spline_knots_t *knots)
+{
+    double bdx, bdy, berr;
+    double cdx, cdy, cerr;
+
+    /* We are going to compute the distance (squared) between each of the the b
+     * and c control points and the segment a-b. The maximum of these two
+     * distances will be our approximation error. */
+
+    bdx = _cairo_fixed_to_double (knots->b.x - knots->a.x);
+    bdy = _cairo_fixed_to_double (knots->b.y - knots->a.y);
+
+    cdx = _cairo_fixed_to_double (knots->c.x - knots->a.x);
+    cdy = _cairo_fixed_to_double (knots->c.y - knots->a.y);
+
+    if (knots->a.x != knots->d.x || knots->a.y != knots->d.y) {
+       /* Intersection point (px):
+        *     px = p1 + u(p2 - p1)
+        *     (p - px) ∙ (p2 - p1) = 0
+        * Thus:
+        *     u = ((p - p1) ∙ (p2 - p1)) / ∥p2 - p1∥²;
+        */
+
+       double dx, dy, u, v;
+
+       dx = _cairo_fixed_to_double (knots->d.x - knots->a.x);
+       dy = _cairo_fixed_to_double (knots->d.y - knots->a.y);
+        v = dx * dx + dy * dy;
+
+       u = bdx * dx + bdy * dy;
+       if (u <= 0) {
+           /* bdx -= 0;
+            * bdy -= 0;
+            */
+       } else if (u >= v) {
+           bdx -= dx;
+           bdy -= dy;
+       } else {
+           bdx -= u/v * dx;
+           bdy -= u/v * dy;
+       }
+
+       u = cdx * dx + cdy * dy;
+       if (u <= 0) {
+           /* cdx -= 0;
+            * cdy -= 0;
+            */
+       } else if (u >= v) {
+           cdx -= dx;
+           cdy -= dy;
+       } else {
+           cdx -= u/v * dx;
+           cdy -= u/v * dy;
+       }
+    }
+
+    berr = bdx * bdx + bdy * bdy;
+    cerr = cdx * cdx + cdy * cdy;
+    if (berr > cerr)
+       return berr;
+    else
+       return cerr;
+}
+
+static cairo_status_t
+_cairo_spline_decompose_into (cairo_spline_knots_t *s1,
+                             double tolerance_squared,
+                             cairo_spline_t *result)
+{
+    cairo_spline_knots_t s2;
+    cairo_status_t status;
+
+    if (_cairo_spline_error_squared (s1) < tolerance_squared)
+       return _cairo_spline_add_point (result, &s1->a, &s1->b);
+
+    _de_casteljau (s1, &s2);
+
+    status = _cairo_spline_decompose_into (s1, tolerance_squared, result);
+    if (unlikely (status))
+       return status;
+
+    return _cairo_spline_decompose_into (&s2, tolerance_squared, result);
+}
+
+cairo_status_t
+_cairo_spline_decompose (cairo_spline_t *spline, double tolerance)
+{
+    cairo_spline_knots_t s1;
+    cairo_status_t status;
+
+    s1 = spline->knots;
+    spline->last_point = s1.a;
+    status = _cairo_spline_decompose_into (&s1, tolerance * tolerance, spline);
+    if (unlikely (status))
+       return status;
+
+    return spline->add_point_func (spline->closure,
+                                  &spline->knots.d, &spline->final_slope);
+}
+
+/* Note: this function is only good for computing bounds in device space. */
+cairo_status_t
+_cairo_spline_bound (cairo_spline_add_point_func_t add_point_func,
+                    void *closure,
+                    const cairo_point_t *p0, const cairo_point_t *p1,
+                    const cairo_point_t *p2, const cairo_point_t *p3)
+{
+    double x0, x1, x2, x3;
+    double y0, y1, y2, y3;
+    double a, b, c;
+    double t[4];
+    int t_num = 0, i;
+    cairo_status_t status;
+
+    x0 = _cairo_fixed_to_double (p0->x);
+    y0 = _cairo_fixed_to_double (p0->y);
+    x1 = _cairo_fixed_to_double (p1->x);
+    y1 = _cairo_fixed_to_double (p1->y);
+    x2 = _cairo_fixed_to_double (p2->x);
+    y2 = _cairo_fixed_to_double (p2->y);
+    x3 = _cairo_fixed_to_double (p3->x);
+    y3 = _cairo_fixed_to_double (p3->y);
+
+    /* The spline can be written as a polynomial of the four points:
+     *
+     *   (1-t)³p0 + 3t(1-t)²p1 + 3t²(1-t)p2 + t³p3
+     *
+     * for 0≤t≤1.  Now, the X and Y components of the spline follow the
+     * same polynomial but with x and y replaced for p.  To find the
+     * bounds of the spline, we just need to find the X and Y bounds.
+     * To find the bound, we take the derivative and equal it to zero,
+     * and solve to find the t's that give the extreme points.
+     *
+     * Here is the derivative of the curve, sorted on t:
+     *
+     *   3t²(-p0+3p1-3p2+p3) + 2t(3p0-6p1+3p2) -3p0+3p1
+     *
+     * Let:
+     *
+     *   a = -p0+3p1-3p2+p3
+     *   b =  p0-2p1+p2
+     *   c = -p0+p1
+     *
+     * Gives:
+     *
+     *   a.t² + 2b.t + c = 0
+     *
+     * With:
+     *
+     *   delta = b*b - a*c
+     *
+     * the extreme points are at -c/2b if a is zero, at (-b±√delta)/a if
+     * delta is positive, and at -b/a if delta is zero.
+     */
+
+#define ADD(t0) \
+    { \
+       double _t0 = (t0); \
+       if (0 < _t0 && _t0 < 1) \
+           t[t_num++] = _t0; \
+    }
+
+#define FIND_EXTREMES(a,b,c) \
+    { \
+       if (a == 0) { \
+           if (b != 0) \
+               ADD (-c / (2*b)); \
+       } else { \
+           double b2 = b * b; \
+           double delta = b2 - a * c; \
+           if (delta > 0) { \
+               cairo_bool_t feasible; \
+               double _2ab = 2 * a * b; \
+               /* We are only interested in solutions t that satisfy 0<t<1 \
+                * here.  We do some checks to avoid sqrt if the solutions \
+                * are not in that range.  The checks can be derived from: \
+                * \
+                *   0 < (-b±√delta)/a < 1 \
+                */ \
+               if (_2ab >= 0) \
+                   feasible = delta > b2 && delta < a*a + b2 + _2ab; \
+               else if (-b / a >= 1) \
+                   feasible = delta < b2 && delta > a*a + b2 + _2ab; \
+               else \
+                   feasible = delta < b2 || delta < a*a + b2 + _2ab; \
+               \
+               if (unlikely (feasible)) { \
+                   double sqrt_delta = sqrt (delta); \
+                   ADD ((-b - sqrt_delta) / a); \
+                   ADD ((-b + sqrt_delta) / a); \
+               } \
+           } else if (delta == 0) { \
+               ADD (-b / a); \
+           } \
+       } \
+    }
+
+    /* Find X extremes */
+    a = -x0 + 3*x1 - 3*x2 + x3;
+    b =  x0 - 2*x1 + x2;
+    c = -x0 + x1;
+    FIND_EXTREMES (a, b, c);
+
+    /* Find Y extremes */
+    a = -y0 + 3*y1 - 3*y2 + y3;
+    b =  y0 - 2*y1 + y2;
+    c = -y0 + y1;
+    FIND_EXTREMES (a, b, c);
+
+    status = add_point_func (closure, p0, NULL);
+    if (unlikely (status))
+       return status;
+
+    for (i = 0; i < t_num; i++) {
+       cairo_point_t p;
+       double x, y;
+        double t_1_0, t_0_1;
+        double t_2_0, t_0_2;
+        double t_3_0, t_2_1_3, t_1_2_3, t_0_3;
+
+        t_1_0 = t[i];          /*      t  */
+        t_0_1 = 1 - t_1_0;     /* (1 - t) */
+
+        t_2_0 = t_1_0 * t_1_0; /*      t  *      t  */
+        t_0_2 = t_0_1 * t_0_1; /* (1 - t) * (1 - t) */
+
+        t_3_0   = t_2_0 * t_1_0;     /*      t  *      t  *      t      */
+        t_2_1_3 = t_2_0 * t_0_1 * 3; /*      t  *      t  * (1 - t) * 3 */
+        t_1_2_3 = t_1_0 * t_0_2 * 3; /*      t  * (1 - t) * (1 - t) * 3 */
+        t_0_3   = t_0_1 * t_0_2;     /* (1 - t) * (1 - t) * (1 - t)     */
+
+        /* Bezier polynomial */
+        x = x0 * t_0_3
+          + x1 * t_1_2_3
+          + x2 * t_2_1_3
+          + x3 * t_3_0;
+        y = y0 * t_0_3
+          + y1 * t_1_2_3
+          + y2 * t_2_1_3
+          + y3 * t_3_0;
+
+       p.x = _cairo_fixed_from_double (x);
+       p.y = _cairo_fixed_from_double (y);
+       status = add_point_func (closure, &p, NULL);
+       if (unlikely (status))
+           return status;
+    }
+
+    return add_point_func (closure, p3, NULL);
+}
diff --git a/src/cairo-stroke-dash-private.h b/src/cairo-stroke-dash-private.h
new file mode 100644 (file)
index 0000000..75c000c
--- /dev/null
@@ -0,0 +1,70 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *     Carl D. Worth <cworth@cworth.org>
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#ifndef CAIRO_STROKE_DASH_PRIVATE_H
+#define CAIRO_STROKE_DASH_PRIVATE_H
+
+#include "cairoint.h"
+
+CAIRO_BEGIN_DECLS
+
+typedef struct _cairo_stroker_dash {
+    cairo_bool_t dashed;
+    unsigned int dash_index;
+    cairo_bool_t dash_on;
+    cairo_bool_t dash_starts_on;
+    double dash_remain;
+
+    double dash_offset;
+    const double *dashes;
+    unsigned int num_dashes;
+} cairo_stroker_dash_t;
+
+cairo_private void
+_cairo_stroker_dash_init (cairo_stroker_dash_t *dash,
+                         const cairo_stroke_style_t *style);
+
+cairo_private void
+_cairo_stroker_dash_start (cairo_stroker_dash_t *dash);
+
+cairo_private void
+_cairo_stroker_dash_step (cairo_stroker_dash_t *dash, double step);
+
+CAIRO_END_DECLS
+
+#endif /* CAIRO_STROKE_DASH_PRIVATE_H */
diff --git a/src/cairo-stroke-dash.c b/src/cairo-stroke-dash.c
new file mode 100644 (file)
index 0000000..d581bdc
--- /dev/null
@@ -0,0 +1,96 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *     Carl D. Worth <cworth@cworth.org>
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-stroke-dash-private.h"
+
+void
+_cairo_stroker_dash_start (cairo_stroker_dash_t *dash)
+{
+    double offset;
+    cairo_bool_t on = TRUE;
+    unsigned int i = 0;
+
+    if (! dash->dashed)
+       return;
+
+    offset = dash->dash_offset;
+
+    /* We stop searching for a starting point as soon as the
+       offset reaches zero.  Otherwise when an initial dash
+       segment shrinks to zero it will be skipped over. */
+    while (offset > 0.0 && offset >= dash->dashes[i]) {
+       offset -= dash->dashes[i];
+       on = !on;
+       if (++i == dash->num_dashes)
+           i = 0;
+    }
+
+    dash->dash_index = i;
+    dash->dash_on = dash->dash_starts_on = on;
+    dash->dash_remain = dash->dashes[i] - offset;
+}
+
+void
+_cairo_stroker_dash_step (cairo_stroker_dash_t *dash, double step)
+{
+    dash->dash_remain -= step;
+    if (dash->dash_remain <= 0.) {
+       if (++dash->dash_index == dash->num_dashes)
+           dash->dash_index = 0;
+
+       dash->dash_on = ! dash->dash_on;
+       dash->dash_remain = dash->dashes[dash->dash_index];
+    }
+}
+
+void
+_cairo_stroker_dash_init (cairo_stroker_dash_t *dash,
+                         const cairo_stroke_style_t *style)
+{
+    dash->dashed = style->dash != NULL;
+    if (! dash->dashed)
+       return;
+
+    dash->dashes = style->dash;
+    dash->num_dashes = style->num_dashes;
+    dash->dash_offset = style->dash_offset;
+
+    _cairo_stroker_dash_start (dash);
+}
diff --git a/src/cairo-stroke-style.c b/src/cairo-stroke-style.c
new file mode 100644 (file)
index 0000000..9b7e407
--- /dev/null
@@ -0,0 +1,315 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2005 Red Hat, Inc
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ *     Carl Worth <cworth@cworth.org>
+ */
+
+#include "cairoint.h"
+#include "cairo-error-private.h"
+
+void
+_cairo_stroke_style_init (cairo_stroke_style_t *style)
+{
+    VG (VALGRIND_MAKE_MEM_UNDEFINED (style, sizeof (cairo_stroke_style_t)));
+
+    style->line_width = CAIRO_GSTATE_LINE_WIDTH_DEFAULT;
+    style->line_cap = CAIRO_GSTATE_LINE_CAP_DEFAULT;
+    style->line_join = CAIRO_GSTATE_LINE_JOIN_DEFAULT;
+    style->miter_limit = CAIRO_GSTATE_MITER_LIMIT_DEFAULT;
+
+    style->dash = NULL;
+    style->num_dashes = 0;
+    style->dash_offset = 0.0;
+}
+
+cairo_status_t
+_cairo_stroke_style_init_copy (cairo_stroke_style_t *style,
+                              const cairo_stroke_style_t *other)
+{
+    if (CAIRO_INJECT_FAULT ())
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    VG (VALGRIND_MAKE_MEM_UNDEFINED (style, sizeof (cairo_stroke_style_t)));
+
+    style->line_width = other->line_width;
+    style->line_cap = other->line_cap;
+    style->line_join = other->line_join;
+    style->miter_limit = other->miter_limit;
+
+    style->num_dashes = other->num_dashes;
+
+    if (other->dash == NULL) {
+       style->dash = NULL;
+    } else {
+       style->dash = _cairo_malloc_ab (style->num_dashes, sizeof (double));
+       if (unlikely (style->dash == NULL))
+           return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+       memcpy (style->dash, other->dash,
+               style->num_dashes * sizeof (double));
+    }
+
+    style->dash_offset = other->dash_offset;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+void
+_cairo_stroke_style_fini (cairo_stroke_style_t *style)
+{
+    free (style->dash);
+    style->dash = NULL;
+
+    style->num_dashes = 0;
+
+    VG (VALGRIND_MAKE_MEM_NOACCESS (style, sizeof (cairo_stroke_style_t)));
+}
+
+/*
+ * For a stroke in the given style, compute the maximum distance
+ * from the path that vertices could be generated.  In the case
+ * of rotation in the ctm, the distance will not be exact.
+ */
+void
+_cairo_stroke_style_max_distance_from_path (const cairo_stroke_style_t *style,
+                                           const cairo_path_fixed_t *path,
+                                            const cairo_matrix_t *ctm,
+                                            double *dx, double *dy)
+{
+    double style_expansion = 0.5;
+
+    if (style->line_cap == CAIRO_LINE_CAP_SQUARE)
+       style_expansion = M_SQRT1_2;
+
+    if (style->line_join == CAIRO_LINE_JOIN_MITER &&
+       ! path->stroke_is_rectilinear &&
+       style_expansion < M_SQRT2 * style->miter_limit)
+    {
+       style_expansion = M_SQRT2 * style->miter_limit;
+    }
+
+    style_expansion *= style->line_width;
+
+    if (_cairo_matrix_has_unity_scale (ctm)) {
+       *dx = *dy = style_expansion;
+    } else {
+       *dx = style_expansion * hypot (ctm->xx, ctm->xy);
+       *dy = style_expansion * hypot (ctm->yy, ctm->yx);
+    }
+}
+
+/*
+ * Computes the period of a dashed stroke style.
+ * Returns 0 for non-dashed styles.
+ */
+double
+_cairo_stroke_style_dash_period (const cairo_stroke_style_t *style)
+{
+    double period;
+    unsigned int i;
+
+    period = 0.0;
+    for (i = 0; i < style->num_dashes; i++)
+       period += style->dash[i];
+
+    if (style->num_dashes & 1)
+       period *= 2.0;
+
+    return period;
+}
+
+/*
+ * Coefficient of the linear approximation (minimizing square difference)
+ * of the surface covered by round caps
+ *
+ * This can be computed in the following way:
+ * the area inside the circle with radius w/2 and the region -d/2 <= x <= d/2 is:
+ *   f(w,d) = 2 * integrate (sqrt (w*w/4 - x*x), x, -d/2, d/2)
+ * The square difference to a generic linear approximation (c*d) in the range (0,w) would be:
+ *   integrate ((f(w,d) - c*d)^2, d, 0, w)
+ * To minimize this difference it is sufficient to find a solution of the differential with
+ * respect to c:
+ *   solve ( diff (integrate ((f(w,d) - c*d)^2, d, 0, w), c), c)
+ * Which leads to c = 9/32*pi*w
+ * Since we're not interested in the true area, but just in a coverage extimate,
+ * we always divide the real area by the line width (w).
+ * The same computation for square caps would be
+ *   f(w,d) = 2 * integrate(w/2, x, -d/2, d/2)
+ *   c = 1*w
+ * but in this case it would not be an approximation, since f is already linear in d.
+ */
+#define ROUND_MINSQ_APPROXIMATION (9*M_PI/32)
+
+/*
+ * Computes the length of the "on" part of a dashed stroke style,
+ * taking into account also line caps.
+ * Returns 0 for non-dashed styles.
+ */
+double
+_cairo_stroke_style_dash_stroked (const cairo_stroke_style_t *style)
+{
+    double stroked, cap_scale;
+    unsigned int i;
+
+    switch (style->line_cap) {
+    default: ASSERT_NOT_REACHED;
+    case CAIRO_LINE_CAP_BUTT:   cap_scale = 0.0; break;
+    case CAIRO_LINE_CAP_ROUND:  cap_scale = ROUND_MINSQ_APPROXIMATION; break;
+    case CAIRO_LINE_CAP_SQUARE: cap_scale = 1.0; break;
+    }
+
+    stroked = 0.0;
+    if (style->num_dashes & 1) {
+        /* Each dash element is used both as on and as off. The order in which they are summed is
+        * irrelevant, so sum the coverage of one dash element, taken both on and off at each iteration */
+       for (i = 0; i < style->num_dashes; i++)
+           stroked += style->dash[i] + cap_scale * MIN (style->dash[i], style->line_width);
+    } else {
+        /* Even (0, 2, ...) dashes are on and simply counted for the coverage, odd dashes are off, thus
+        * their coverage is approximated based on the area covered by the caps of adjacent on dases. */
+       for (i = 0; i < style->num_dashes; i+=2)
+           stroked += style->dash[i] + cap_scale * MIN (style->dash[i+1], style->line_width);
+    }
+
+    return stroked;
+}
+
+/*
+ * Verifies if _cairo_stroke_style_dash_approximate should be used to generate
+ * an approximation of the dash pattern in the specified style, when used for
+ * stroking a path with the given CTM and tolerance.
+ * Always %FALSE for non-dashed styles.
+ */
+cairo_bool_t
+_cairo_stroke_style_dash_can_approximate (const cairo_stroke_style_t *style,
+                                         const cairo_matrix_t *ctm,
+                                         double tolerance)
+{
+    double period;
+
+    if (! style->num_dashes)
+        return FALSE;
+
+    period = _cairo_stroke_style_dash_period (style);
+    return _cairo_matrix_transformed_circle_major_axis (ctm, period) < tolerance;
+}
+
+/*
+ * Create a 2-dashes approximation of a dashed style, by making the "on" and "off"
+ * parts respect the original ratio.
+ */
+void
+_cairo_stroke_style_dash_approximate (const cairo_stroke_style_t *style,
+                                     const cairo_matrix_t *ctm,
+                                     double tolerance,
+                                     double *dash_offset,
+                                     double *dashes,
+                                     unsigned int *num_dashes)
+{
+    double coverage, scale, offset;
+    cairo_bool_t on = TRUE;
+    unsigned int i = 0;
+
+    coverage = _cairo_stroke_style_dash_stroked (style) / _cairo_stroke_style_dash_period (style);
+    coverage = MIN (coverage, 1.0);
+    scale = tolerance / _cairo_matrix_transformed_circle_major_axis (ctm, 1.0);
+
+    /* We stop searching for a starting point as soon as the
+     * offset reaches zero.  Otherwise when an initial dash
+     * segment shrinks to zero it will be skipped over. */
+    offset = style->dash_offset;
+    while (offset > 0.0 && offset >= style->dash[i]) {
+       offset -= style->dash[i];
+       on = !on;
+       if (++i == style->num_dashes)
+           i = 0;
+    }
+
+    *num_dashes = 2;
+
+    /*
+     * We want to create a new dash pattern with the same relative coverage,
+     * but composed of just 2 elements with total length equal to scale.
+     * Based on the formula in _cairo_stroke_style_dash_stroked:
+     * scale * coverage = dashes[0] + cap_scale * MIN (dashes[1], line_width)
+     *                  = MIN (dashes[0] + cap_scale * (scale - dashes[0]),
+     *                         dashes[0] + cap_scale * line_width) = 
+     *                  = MIN (dashes[0] * (1 - cap_scale) + cap_scale * scale,
+     *                        dashes[0] + cap_scale * line_width)
+     *
+     * Solving both cases we get:
+     *   dashes[0] = scale * (coverage - cap_scale) / (1 - cap_scale)
+     *   when scale - dashes[0] <= line_width
+     * dashes[0] = scale * coverage - cap_scale * line_width
+     *   when scale - dashes[0] > line_width.
+     *
+     * Comparing the two cases we get:
+     *   second > first
+     *   second > scale * (coverage - cap_scale) / (1 - cap_scale)
+     *   second - cap_scale * second - scale * coverage + scale * cap_scale > 0
+     *          (scale * coverage - cap_scale * line_width) - cap_scale * second - scale * coverage + scale * cap_scale > 0
+     *   - line_width - second + scale > 0
+     *   scale - second > line_width
+     * which is the condition for the second solution to be the valid one.
+     * So when second > first, the second solution is the correct one (i.e.
+     * the solution is always MAX (first, second).
+     */
+    switch (style->line_cap) {
+    default:
+        ASSERT_NOT_REACHED;
+       dashes[0] = 0.0;
+       break;
+
+    case CAIRO_LINE_CAP_BUTT:
+        /* Simplified formula (substituting 0 for cap_scale): */
+        dashes[0] = scale * coverage;
+       break;
+
+    case CAIRO_LINE_CAP_ROUND:
+        dashes[0] = MAX(scale * (coverage - ROUND_MINSQ_APPROXIMATION) / (1.0 - ROUND_MINSQ_APPROXIMATION),
+                       scale * coverage - ROUND_MINSQ_APPROXIMATION * style->line_width);
+       break;
+
+    case CAIRO_LINE_CAP_SQUARE:
+        /*
+        * Special attention is needed to handle the case cap_scale == 1 (since the first solution
+        * is either indeterminate or -inf in this case). Since dash lengths are always >=0, using
+        * 0 as first solution always leads to the correct solution.
+        */
+        dashes[0] = MAX(0.0, scale * coverage - style->line_width);
+       break;
+    }
+
+    dashes[1] = scale - dashes[0];
+
+    *dash_offset = on ? 0.0 : dashes[0];
+}
diff --git a/src/cairo-surface-backend-private.h b/src/cairo-surface-backend-private.h
new file mode 100644 (file)
index 0000000..03468cc
--- /dev/null
@@ -0,0 +1,199 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *     Carl D. Worth <cworth@cworth.org>
+ */
+
+#ifndef CAIRO_SURFACE_BACKEND_PRIVATE_H
+#define CAIRO_SURFACE_BACKEND_PRIVATE_H
+
+#include "cairo-compiler-private.h"
+#include "cairo-error-private.h"
+
+CAIRO_BEGIN_DECLS
+
+struct _cairo_surface_backend {
+    cairo_surface_type_t type;
+
+    cairo_warn cairo_status_t
+    (*finish)                  (void                   *surface);
+
+    cairo_t *
+    (*create_context)          (void                   *surface);
+
+    cairo_surface_t *
+    (*create_similar)          (void                   *surface,
+                                cairo_content_t         content,
+                                int                     width,
+                                int                     height);
+    cairo_surface_t *
+    (*create_similar_image)    (void                   *surface,
+                                cairo_format_t         format,
+                                int                     width,
+                                int                     height);
+
+    cairo_surface_t *
+    (*map_to_image)            (void                   *surface,
+                                const cairo_rectangle_int_t  *extents);
+    cairo_int_status_t
+    (*unmap_image)             (void                   *surface,
+                                cairo_image_surface_t  *image);
+
+    cairo_warn cairo_status_t
+    (*acquire_source_image)    (void                    *abstract_surface,
+                                cairo_image_surface_t  **image_out,
+                                void                   **image_extra);
+
+    cairo_warn void
+    (*release_source_image)    (void                   *abstract_surface,
+                                cairo_image_surface_t  *image_out,
+                                void                   *image_extra);
+
+    cairo_surface_t *
+    (*snapshot)                        (void                   *surface);
+
+    cairo_warn cairo_int_status_t
+    (*copy_page)               (void                   *surface);
+
+    cairo_warn cairo_int_status_t
+    (*show_page)               (void                   *surface);
+
+    /* Get the extents of the current surface. For many surface types
+     * this will be as simple as { x=0, y=0, width=surface->width,
+     * height=surface->height}.
+     *
+     * If this function is not implemented, or if it returns
+     * FALSE the surface is considered to be
+     * boundless and infinite bounds are used for it.
+     */
+    cairo_bool_t
+    (*get_extents)             (void                    *surface,
+                                cairo_rectangle_int_t   *extents);
+
+    void
+    (*get_font_options)         (void                  *surface,
+                                cairo_font_options_t  *options);
+
+    cairo_warn cairo_status_t
+    (*flush)                    (void                  *surface);
+
+    cairo_warn cairo_status_t
+    (*mark_dirty_rectangle)     (void                  *surface,
+                                int                    x,
+                                int                    y,
+                                int                    width,
+                                int                    height);
+
+    cairo_warn cairo_int_status_t
+    (*paint)                   (void                   *surface,
+                                cairo_operator_t        op,
+                                const cairo_pattern_t  *source,
+                                const cairo_clip_t             *clip);
+
+    cairo_warn cairo_int_status_t
+    (*mask)                    (void                   *surface,
+                                cairo_operator_t        op,
+                                const cairo_pattern_t  *source,
+                                const cairo_pattern_t  *mask,
+                                const cairo_clip_t             *clip);
+
+    cairo_warn cairo_int_status_t
+    (*stroke)                  (void                   *surface,
+                                cairo_operator_t        op,
+                                const cairo_pattern_t  *source,
+                                const cairo_path_fixed_t       *path,
+                                const cairo_stroke_style_t     *style,
+                                const cairo_matrix_t   *ctm,
+                                const cairo_matrix_t   *ctm_inverse,
+                                double                  tolerance,
+                                cairo_antialias_t       antialias,
+                                const cairo_clip_t             *clip);
+
+    cairo_warn cairo_int_status_t
+    (*fill)                    (void                   *surface,
+                                cairo_operator_t        op,
+                                const cairo_pattern_t  *source,
+                                const cairo_path_fixed_t       *path,
+                                cairo_fill_rule_t       fill_rule,
+                                double                  tolerance,
+                                cairo_antialias_t       antialias,
+                                const cairo_clip_t           *clip);
+
+    cairo_warn cairo_int_status_t
+    (*fill_stroke)             (void                   *surface,
+                                cairo_operator_t        fill_op,
+                                const cairo_pattern_t  *fill_source,
+                                cairo_fill_rule_t       fill_rule,
+                                double                  fill_tolerance,
+                                cairo_antialias_t       fill_antialias,
+                                const cairo_path_fixed_t*path,
+                                cairo_operator_t        stroke_op,
+                                const cairo_pattern_t  *stroke_source,
+                                const cairo_stroke_style_t     *stroke_style,
+                                const cairo_matrix_t   *stroke_ctm,
+                                const cairo_matrix_t   *stroke_ctm_inverse,
+                                double                  stroke_tolerance,
+                                cairo_antialias_t       stroke_antialias,
+                                const cairo_clip_t     *clip);
+
+    cairo_warn cairo_int_status_t
+    (*show_glyphs)             (void                   *surface,
+                                cairo_operator_t        op,
+                                const cairo_pattern_t  *source,
+                                cairo_glyph_t          *glyphs,
+                                int                     num_glyphs,
+                                cairo_scaled_font_t    *scaled_font,
+                                const cairo_clip_t     *clip);
+
+    cairo_bool_t
+    (*has_show_text_glyphs)    (void                       *surface);
+
+    cairo_warn cairo_int_status_t
+    (*show_text_glyphs)                (void                       *surface,
+                                cairo_operator_t            op,
+                                const cairo_pattern_t      *source,
+                                const char                 *utf8,
+                                int                         utf8_len,
+                                cairo_glyph_t              *glyphs,
+                                int                         num_glyphs,
+                                const cairo_text_cluster_t *clusters,
+                                int                         num_clusters,
+                                cairo_text_cluster_flags_t  cluster_flags,
+                                cairo_scaled_font_t        *scaled_font,
+                                const cairo_clip_t               *clip);
+};
+
+CAIRO_END_DECLS
+
+#endif /* CAIRO_SURFACE_BACKEND_PRIVATE_H */
diff --git a/src/cairo-surface-clipper-private.h b/src/cairo-surface-clipper-private.h
new file mode 100644 (file)
index 0000000..e5b00af
--- /dev/null
@@ -0,0 +1,71 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Chris Wilson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *     Chris Wilson <chris@chris-wilson.co.u>
+ */
+
+#ifndef CAIRO_SURFACE_CLIPPER_PRIVATE_H
+#define CAIRO_SURFACE_CLIPPER_PRIVATE_H
+
+#include "cairo-types-private.h"
+#include "cairo-clip-private.h"
+
+CAIRO_BEGIN_DECLS
+
+typedef struct _cairo_surface_clipper cairo_surface_clipper_t;
+
+typedef cairo_status_t
+(*cairo_surface_clipper_intersect_clip_path_func_t) (cairo_surface_clipper_t *,
+                                                    cairo_path_fixed_t *,
+                                                    cairo_fill_rule_t,
+                                                    double,
+                                                    cairo_antialias_t);
+struct _cairo_surface_clipper {
+    cairo_clip_t *clip;
+    cairo_surface_clipper_intersect_clip_path_func_t intersect_clip_path;
+};
+
+cairo_private cairo_status_t
+_cairo_surface_clipper_set_clip (cairo_surface_clipper_t *clipper,
+                                const cairo_clip_t *clip);
+
+cairo_private void
+_cairo_surface_clipper_init (cairo_surface_clipper_t *clipper,
+                            cairo_surface_clipper_intersect_clip_path_func_t intersect);
+
+cairo_private void
+_cairo_surface_clipper_reset (cairo_surface_clipper_t *clipper);
+
+CAIRO_END_DECLS
+
+#endif /* CAIRO_SURFACE_CLIPPER_PRIVATE_H */
diff --git a/src/cairo-surface-clipper.c b/src/cairo-surface-clipper.c
new file mode 100644 (file)
index 0000000..0d2890c
--- /dev/null
@@ -0,0 +1,195 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Chris Wilson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ *      Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-surface-clipper-private.h"
+
+/* A collection of routines to facilitate vector surface clipping */
+
+/* XXX Eliminate repeated paths and nested clips */
+
+static cairo_status_t
+_cairo_path_fixed_add_box (cairo_path_fixed_t *path,
+                          const cairo_box_t *box)
+{
+    cairo_status_t status;
+
+    status = _cairo_path_fixed_move_to (path, box->p1.x, box->p1.y);
+    if (unlikely (status))
+       return status;
+
+    status = _cairo_path_fixed_line_to (path, box->p2.x, box->p1.y);
+    if (unlikely (status))
+       return status;
+
+    status = _cairo_path_fixed_line_to (path, box->p2.x, box->p2.y);
+    if (unlikely (status))
+       return status;
+
+    status = _cairo_path_fixed_line_to (path, box->p1.x, box->p2.y);
+    if (unlikely (status))
+       return status;
+
+    return _cairo_path_fixed_close_path (path);
+}
+
+static cairo_status_t
+_cairo_surface_clipper_intersect_clip_boxes (cairo_surface_clipper_t *clipper,
+                                            const cairo_clip_t *clip)
+{
+    cairo_path_fixed_t path;
+    cairo_status_t status;
+    int i;
+
+    if (clip->num_boxes == 0)
+       return CAIRO_STATUS_SUCCESS;
+
+    /* Reconstruct the path for the clip boxes.
+     * XXX maybe a new clipper callback?
+     */
+
+    _cairo_path_fixed_init (&path);
+    for (i = 0; i < clip->num_boxes; i++) {
+       status = _cairo_path_fixed_add_box (&path, &clip->boxes[i]);
+       if (unlikely (status)) {
+           _cairo_path_fixed_fini (&path);
+           return status;
+       }
+    }
+
+    status = clipper->intersect_clip_path (clipper, &path,
+                                          CAIRO_FILL_RULE_WINDING,
+                                          0.,
+                                          CAIRO_ANTIALIAS_DEFAULT);
+    _cairo_path_fixed_fini (&path);
+
+    return status;
+}
+
+static cairo_status_t
+_cairo_surface_clipper_intersect_clip_path_recursive (cairo_surface_clipper_t *clipper,
+                                                     cairo_clip_path_t *clip_path,
+                                                     cairo_clip_path_t *end)
+{
+    cairo_status_t status;
+
+    if (clip_path->prev != end) {
+       status =
+           _cairo_surface_clipper_intersect_clip_path_recursive (clipper,
+                                                                 clip_path->prev,
+                                                                 end);
+       if (unlikely (status))
+           return status;
+    }
+
+    return clipper->intersect_clip_path (clipper,
+                                        &clip_path->path,
+                                        clip_path->fill_rule,
+                                        clip_path->tolerance,
+                                        clip_path->antialias);
+}
+
+cairo_status_t
+_cairo_surface_clipper_set_clip (cairo_surface_clipper_t *clipper,
+                                const cairo_clip_t *clip)
+{
+    cairo_status_t status;
+    cairo_bool_t incremental = FALSE;
+
+    if (_cairo_clip_equal (clip, clipper->clip))
+       return CAIRO_STATUS_SUCCESS;
+
+    /* all clipped out state should never propagate this far */
+    assert (!_cairo_clip_is_all_clipped (clip));
+
+    /* XXX Is this an incremental clip? */
+    if (clipper->clip && clip &&
+       clip->num_boxes == clipper->clip->num_boxes &&
+       memcmp (clip->boxes, clipper->clip->boxes,
+               sizeof (cairo_box_t) * clip->num_boxes) == 0)
+    {
+       cairo_clip_path_t *clip_path = clip->path;
+       while (clip_path != NULL && clip_path != clipper->clip->path)
+           clip_path = clip_path->prev;
+
+       if (clip_path) {
+           incremental = TRUE;
+           status = _cairo_surface_clipper_intersect_clip_path_recursive (clipper,
+                                                                          clip->path,
+                                                                          clipper->clip->path);
+       }
+    }
+
+    _cairo_clip_destroy (clipper->clip);
+    clipper->clip = _cairo_clip_copy (clip);
+
+    if (incremental)
+       return status;
+
+    status = clipper->intersect_clip_path (clipper, NULL, 0, 0, 0);
+    if (unlikely (status))
+       return status;
+
+    if (clip == NULL)
+       return CAIRO_STATUS_SUCCESS;
+
+    status = _cairo_surface_clipper_intersect_clip_boxes (clipper, clip);
+    if (unlikely (status))
+       return status;
+
+    if (clip->path != NULL) {
+           status = _cairo_surface_clipper_intersect_clip_path_recursive (clipper,
+                                                                          clip->path,
+                                                                          NULL);
+    }
+
+    return status;
+}
+
+void
+_cairo_surface_clipper_init (cairo_surface_clipper_t *clipper,
+                            cairo_surface_clipper_intersect_clip_path_func_t func)
+{
+    clipper->clip = NULL;
+    clipper->intersect_clip_path = func;
+}
+
+void
+_cairo_surface_clipper_reset (cairo_surface_clipper_t *clipper)
+{
+    _cairo_clip_destroy (clipper->clip);
+    clipper->clip = NULL;
+}
diff --git a/src/cairo-surface-fallback-private.h b/src/cairo-surface-fallback-private.h
new file mode 100644 (file)
index 0000000..ecf7b0e
--- /dev/null
@@ -0,0 +1,95 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ * Copyright © 2005 Red Hat, Inc.
+ * Copyright © 2011 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *     Carl D. Worth <cworth@cworth.org>
+ *      Joonas Pihlaja <jpihlaja@cc.helsinki.fi>
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#ifndef CAIRO_SURFACE_FALLBACK_PRIVATE_H
+#define CAIRO_SURFACE_FALLBACK_PRIVATE_H
+
+#include "cairoint.h"
+
+CAIRO_BEGIN_DECLS
+
+cairo_private cairo_int_status_t
+_cairo_surface_fallback_paint (void                    *abstract_surface,
+                              cairo_operator_t          op,
+                              const cairo_pattern_t    *source,
+                              const cairo_clip_t       *clip);
+
+cairo_private cairo_int_status_t
+_cairo_surface_fallback_mask (void                     *abstract_surface,
+                             cairo_operator_t           op,
+                             const cairo_pattern_t     *source,
+                             const cairo_pattern_t     *mask,
+                             const cairo_clip_t        *clip);
+
+cairo_private cairo_int_status_t
+_cairo_surface_fallback_stroke (void                   *abstract_surface,
+                               cairo_operator_t                 op,
+                               const cairo_pattern_t   *source,
+                               const cairo_path_fixed_t        *path,
+                               const cairo_stroke_style_t*style,
+                               const cairo_matrix_t    *ctm,
+                               const cairo_matrix_t    *ctm_inverse,
+                               double                   tolerance,
+                               cairo_antialias_t        antialias,
+                               const cairo_clip_t      *clip);
+
+cairo_private cairo_int_status_t
+_cairo_surface_fallback_fill (void                     *abstract_surface,
+                            cairo_operator_t            op,
+                            const cairo_pattern_t      *source,
+                            const cairo_path_fixed_t   *path,
+                            cairo_fill_rule_t           fill_rule,
+                            double                      tolerance,
+                            cairo_antialias_t           antialias,
+                            const cairo_clip_t         *clip);
+
+cairo_private cairo_int_status_t
+_cairo_surface_fallback_glyphs (void                   *abstract_surface,
+                               cairo_operator_t                 op,
+                               const cairo_pattern_t   *source,
+                               cairo_glyph_t           *glyphs,
+                               int                      num_glyphs,
+                               cairo_scaled_font_t     *scaled_font,
+                               const cairo_clip_t      *clip);
+
+CAIRO_END_DECLS
+
+#endif /* CAIRO_SURFACE_FALLBACK_PRIVATE_H */
diff --git a/src/cairo-surface-fallback.c b/src/cairo-surface-fallback.c
new file mode 100644 (file)
index 0000000..a0af159
--- /dev/null
@@ -0,0 +1,115 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ * Copyright © 2005 Red Hat, Inc.
+ * Copyright © 2011 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *     Carl D. Worth <cworth@cworth.org>
+ *      Joonas Pihlaja <jpihlaja@cc.helsinki.fi>
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-compositor-private.h"
+#include "cairo-surface-fallback-private.h"
+
+cairo_int_status_t
+_cairo_surface_fallback_paint (void                    *surface,
+                              cairo_operator_t          op,
+                              const cairo_pattern_t    *source,
+                              const cairo_clip_t       *clip)
+{
+    return _cairo_compositor_paint (&_cairo_fallback_compositor,
+                                   surface, op, source, clip);
+}
+
+cairo_int_status_t
+_cairo_surface_fallback_mask (void                     *surface,
+                             cairo_operator_t           op,
+                             const cairo_pattern_t     *source,
+                             const cairo_pattern_t     *mask,
+                             const cairo_clip_t        *clip)
+{
+    return _cairo_compositor_mask (&_cairo_fallback_compositor,
+                                  surface, op, source, mask, clip);
+}
+
+cairo_int_status_t
+_cairo_surface_fallback_stroke (void                   *surface,
+                               cairo_operator_t         op,
+                               const cairo_pattern_t   *source,
+                               const cairo_path_fixed_t*path,
+                               const cairo_stroke_style_t*style,
+                               const cairo_matrix_t    *ctm,
+                               const cairo_matrix_t    *ctm_inverse,
+                               double                   tolerance,
+                               cairo_antialias_t        antialias,
+                               const cairo_clip_t      *clip)
+{
+    return _cairo_compositor_stroke (&_cairo_fallback_compositor,
+                                    surface, op, source, path,
+                                    style, ctm,ctm_inverse,
+                                    tolerance, antialias, clip);
+}
+
+cairo_int_status_t
+_cairo_surface_fallback_fill (void                     *surface,
+                            cairo_operator_t            op,
+                            const cairo_pattern_t      *source,
+                            const cairo_path_fixed_t   *path,
+                            cairo_fill_rule_t           fill_rule,
+                            double                      tolerance,
+                            cairo_antialias_t           antialias,
+                            const cairo_clip_t         *clip)
+{
+    return _cairo_compositor_fill (&_cairo_fallback_compositor,
+                                  surface, op, source, path,
+                                  fill_rule, tolerance, antialias,
+                                  clip);
+}
+
+cairo_int_status_t
+_cairo_surface_fallback_glyphs (void                   *surface,
+                               cairo_operator_t         op,
+                               const cairo_pattern_t   *source,
+                               cairo_glyph_t           *glyphs,
+                               int                      num_glyphs,
+                               cairo_scaled_font_t     *scaled_font,
+                               const cairo_clip_t      *clip)
+{
+    return _cairo_compositor_glyphs (&_cairo_fallback_compositor,
+                                    surface, op, source,
+                                    glyphs, num_glyphs, scaled_font,
+                                    clip);
+}
diff --git a/src/cairo-surface-observer-private.h b/src/cairo-surface-observer-private.h
new file mode 100644 (file)
index 0000000..1b5d6d9
--- /dev/null
@@ -0,0 +1,227 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2011 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Intel Corporation.
+ *
+ * Contributor(s):
+ *      Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#ifndef CAIRO_SURFACE_OBSERVER_PRIVATE_H
+#define CAIRO_SURFACE_OBSERVER_PRIVATE_H
+
+#include "cairoint.h"
+
+#include "cairo-device-private.h"
+#include "cairo-list-private.h"
+#include "cairo-recording-surface-private.h"
+#include "cairo-surface-private.h"
+#include "cairo-surface-backend-private.h"
+#include "cairo-time-private.h"
+
+struct stat {
+    double min, max, sum, sum_sq;
+    unsigned count;
+};
+
+#define NUM_OPERATORS (CAIRO_OPERATOR_HSL_LUMINOSITY+1)
+#define NUM_CAPS (CAIRO_LINE_CAP_SQUARE+1)
+#define NUM_JOINS (CAIRO_LINE_JOIN_BEVEL+1)
+#define NUM_ANTIALIAS (CAIRO_ANTIALIAS_BEST+1)
+#define NUM_FILL_RULE (CAIRO_FILL_RULE_EVEN_ODD+1)
+
+struct extents {
+    struct stat area;
+    unsigned int bounded, unbounded;
+};
+
+struct pattern {
+    unsigned int type[7]; /* native/record/other surface/gradients */
+};
+
+struct path {
+    unsigned int type[5]; /* empty/pixel/rectilinear/straight/curved */
+};
+
+struct clip {
+    unsigned int type[6]; /* none, region, boxes, single path, polygon, general */
+};
+
+typedef struct _cairo_observation cairo_observation_t;
+typedef struct _cairo_observation_record cairo_observation_record_t;
+typedef struct _cairo_device_observer cairo_device_observer_t;
+
+struct _cairo_observation_record {
+    cairo_content_t target_content;
+    int target_width;
+    int target_height;
+
+    int index;
+    cairo_operator_t op;
+    int source;
+    int mask;
+    int num_glyphs;
+    int path;
+    int fill_rule;
+    double tolerance;
+    int antialias;
+    int clip;
+    cairo_time_t elapsed;
+};
+
+struct _cairo_observation {
+    int num_surfaces;
+    int num_contexts;
+    int num_sources_acquired;
+
+    /* XXX put interesting stats here! */
+
+    struct paint {
+       cairo_time_t elapsed;
+       unsigned int count;
+       struct extents extents;
+       unsigned int operators[NUM_OPERATORS];
+       struct pattern source;
+       struct clip clip;
+       unsigned int noop;
+
+       cairo_observation_record_t slowest;
+    } paint;
+
+    struct mask {
+       cairo_time_t elapsed;
+       unsigned int count;
+       struct extents extents;
+       unsigned int operators[NUM_OPERATORS];
+       struct pattern source;
+       struct pattern mask;
+       struct clip clip;
+       unsigned int noop;
+
+       cairo_observation_record_t slowest;
+    } mask;
+
+    struct fill {
+       cairo_time_t elapsed;
+       unsigned int count;
+       struct extents extents;
+       unsigned int operators[NUM_OPERATORS];
+       struct pattern source;
+       struct path path;
+       unsigned int antialias[NUM_ANTIALIAS];
+       unsigned int fill_rule[NUM_FILL_RULE];
+       struct clip clip;
+       unsigned int noop;
+
+       cairo_observation_record_t slowest;
+    } fill;
+
+    struct stroke {
+       cairo_time_t elapsed;
+       unsigned int count;
+       struct extents extents;
+       unsigned int operators[NUM_OPERATORS];
+       unsigned int caps[NUM_CAPS];
+       unsigned int joins[NUM_CAPS];
+       unsigned int antialias[NUM_ANTIALIAS];
+       struct pattern source;
+       struct path path;
+       struct stat line_width;
+       struct clip clip;
+       unsigned int noop;
+
+       cairo_observation_record_t slowest;
+    } stroke;
+
+    struct glyphs {
+       cairo_time_t elapsed;
+       unsigned int count;
+       struct extents extents;
+       unsigned int operators[NUM_OPERATORS];
+       struct pattern source;
+       struct clip clip;
+       unsigned int noop;
+
+       cairo_observation_record_t slowest;
+    } glyphs;
+
+    cairo_array_t timings;
+    cairo_recording_surface_t *record;
+};
+
+struct _cairo_device_observer {
+    cairo_device_t base;
+    cairo_device_t *target;
+
+    cairo_observation_t log;
+};
+
+struct callback_list {
+    cairo_list_t link;
+
+    cairo_surface_observer_callback_t func;
+    void *data;
+};
+
+struct _cairo_surface_observer {
+    cairo_surface_t base;
+    cairo_surface_t *target;
+
+    cairo_observation_t log;
+
+    cairo_list_t paint_callbacks;
+    cairo_list_t mask_callbacks;
+    cairo_list_t fill_callbacks;
+    cairo_list_t stroke_callbacks;
+    cairo_list_t glyphs_callbacks;
+
+    cairo_list_t flush_callbacks;
+    cairo_list_t finish_callbacks;
+};
+
+static inline cairo_surface_t *
+_cairo_surface_observer_get_target (cairo_surface_t *surface)
+{
+    return ((cairo_surface_observer_t *) surface)->target;
+}
+
+static inline cairo_bool_t
+_cairo_surface_is_observer (cairo_surface_t *surface)
+{
+    return surface->backend->type == (cairo_surface_type_t)CAIRO_INTERNAL_SURFACE_TYPE_OBSERVER;
+}
+
+static inline cairo_bool_t
+_cairo_device_is_observer (cairo_device_t *device)
+{
+    return device->backend->type == (cairo_device_type_t)CAIRO_INTERNAL_DEVICE_TYPE_OBSERVER;
+}
+
+
+#endif /* CAIRO_SURFACE_SNAPSHOT_PRIVATE_H */
diff --git a/src/cairo-surface-observer.c b/src/cairo-surface-observer.c
new file mode 100644 (file)
index 0000000..ebb38b9
--- /dev/null
@@ -0,0 +1,2092 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2011 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Intel Corporation.
+ *
+ * Contributor(s):
+ *      Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-surface-observer-private.h"
+
+#include "cairo-array-private.h"
+#include "cairo-combsort-private.h"
+#include "cairo-composite-rectangles-private.h"
+#include "cairo-error-private.h"
+#include "cairo-image-surface-private.h"
+#include "cairo-pattern-private.h"
+#include "cairo-output-stream-private.h"
+#include "cairo-recording-surface-private.h"
+#include "cairo-surface-subsurface-private.h"
+#include "cairo-reference-count-private.h"
+
+#if CAIRO_HAS_SCRIPT_SURFACE
+#include "cairo-script-private.h"
+#endif
+
+static const cairo_surface_backend_t _cairo_surface_observer_backend;
+
+/* observation/stats */
+
+static void init_stats (struct stat *s)
+{
+    s->min = HUGE_VAL;
+    s->max = -HUGE_VAL;
+}
+
+static void init_extents (struct extents *e)
+{
+    init_stats (&e->area);
+}
+
+static void init_pattern (struct pattern *p)
+{
+}
+
+static void init_path (struct path *p)
+{
+}
+
+static void init_clip (struct clip *c)
+{
+}
+
+static void init_paint (struct paint *p)
+{
+    init_extents (&p->extents);
+    init_pattern (&p->source);
+    init_clip (&p->clip);
+}
+
+static void init_mask (struct mask *m)
+{
+    init_extents (&m->extents);
+    init_pattern (&m->source);
+    init_pattern (&m->mask);
+    init_clip (&m->clip);
+}
+
+static void init_fill (struct fill *f)
+{
+    init_extents (&f->extents);
+    init_pattern (&f->source);
+    init_path (&f->path);
+    init_clip (&f->clip);
+}
+
+static void init_stroke (struct stroke *s)
+{
+    init_extents (&s->extents);
+    init_pattern (&s->source);
+    init_path (&s->path);
+    init_clip (&s->clip);
+}
+
+static void init_glyphs (struct glyphs *g)
+{
+    init_extents (&g->extents);
+    init_pattern (&g->source);
+    init_clip (&g->clip);
+}
+
+static cairo_status_t
+log_init (cairo_observation_t *log,
+         cairo_bool_t record)
+{
+    memset (log, 0, sizeof(*log));
+
+    init_paint (&log->paint);
+    init_mask (&log->mask);
+    init_fill (&log->fill);
+    init_stroke (&log->stroke);
+    init_glyphs (&log->glyphs);
+
+    _cairo_array_init (&log->timings, sizeof (cairo_observation_record_t));
+
+    if (record) {
+       log->record = (cairo_recording_surface_t *)
+           cairo_recording_surface_create (CAIRO_CONTENT_COLOR_ALPHA, NULL);
+       if (unlikely (log->record->base.status))
+           return log->record->base.status;
+
+       log->record->optimize_clears = FALSE;
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+log_fini (cairo_observation_t *log)
+{
+    _cairo_array_fini (&log->timings);
+    cairo_surface_destroy (&log->record->base);
+}
+
+static cairo_surface_t*
+get_pattern_surface (const cairo_pattern_t *pattern)
+{
+    return ((cairo_surface_pattern_t *)pattern)->surface;
+}
+
+static int
+classify_pattern (const cairo_pattern_t *pattern,
+                 const cairo_surface_t *target)
+{
+    int classify;
+
+    switch (pattern->type) {
+    case CAIRO_PATTERN_TYPE_SURFACE:
+       if (get_pattern_surface (pattern)->type == target->type)
+           classify = 0;
+       else if (get_pattern_surface (pattern)->type == CAIRO_SURFACE_TYPE_RECORDING)
+           classify = 1;
+       else
+           classify = 2;
+       break;
+    default:
+    case CAIRO_PATTERN_TYPE_SOLID:
+       classify = 3;
+       break;
+    case CAIRO_PATTERN_TYPE_LINEAR:
+       classify = 4;
+       break;
+    case CAIRO_PATTERN_TYPE_RADIAL:
+       classify = 5;
+       break;
+    case CAIRO_PATTERN_TYPE_MESH:
+       classify = 6;
+       break;
+    }
+    return classify;
+}
+
+static void
+add_pattern (struct pattern *stats,
+            const cairo_pattern_t *pattern,
+            const cairo_surface_t *target)
+{
+    stats->type[classify_pattern(pattern, target)]++;
+}
+
+static int
+classify_path (const cairo_path_fixed_t *path,
+              cairo_bool_t is_fill)
+{
+    int classify;
+
+    /* XXX improve for stroke */
+    classify = -1;
+    if (is_fill) {
+       if (path->fill_is_empty)
+           classify = 0;
+       else if (_cairo_path_fixed_fill_is_rectilinear (path))
+           classify = path->fill_maybe_region ? 1 : 2;
+    } else {
+       if (_cairo_path_fixed_stroke_is_rectilinear (path))
+           classify = 2;
+    }
+    if (classify == -1)
+       classify = 3 + (path->has_curve_to != 0);
+
+    return classify;
+}
+
+static void
+add_path (struct path *stats,
+         const cairo_path_fixed_t *path,
+         cairo_bool_t is_fill)
+{
+    stats->type[classify_path(path, is_fill)]++;
+}
+
+static int
+classify_clip (const cairo_clip_t *clip)
+{
+    int classify;
+
+    if (clip == NULL)
+       classify = 0;
+    else if (_cairo_clip_is_region (clip))
+       classify = 1;
+    else if (clip->path == NULL)
+       classify = 2;
+    else if (clip->path->prev == NULL)
+       classify = 3;
+    else if (_cairo_clip_is_polygon (clip))
+       classify = 4;
+    else
+       classify = 5;
+
+    return classify;
+}
+
+static void
+add_clip (struct clip *stats,
+         const cairo_clip_t *clip)
+{
+    stats->type[classify_clip (clip)]++;
+}
+
+static void
+stats_add (struct stat *s, double v)
+{
+    if (v < s->min)
+       s->min = v;
+    if (v > s->max)
+       s->max = v;
+    s->sum += v;
+    s->sum_sq += v*v;
+    s->count++;
+}
+
+static void
+add_extents (struct extents *stats,
+            const cairo_composite_rectangles_t *extents)
+{
+    const cairo_rectangle_int_t *r = extents->is_bounded ? &extents->bounded :&extents->unbounded;
+    stats_add (&stats->area, r->width * r->height);
+    stats->bounded += extents->is_bounded != 0;
+    stats->unbounded += extents->is_bounded == 0;
+}
+
+/* device interface */
+
+static void
+_cairo_device_observer_lock (void *_device)
+{
+    cairo_device_observer_t *device = (cairo_device_observer_t *) _device;
+    cairo_device_acquire (device->target);
+}
+
+static void
+_cairo_device_observer_unlock (void *_device)
+{
+    cairo_device_observer_t *device = (cairo_device_observer_t *) _device;
+    cairo_device_release (device->target);
+}
+
+static cairo_status_t
+_cairo_device_observer_flush (void *_device)
+{
+    cairo_device_observer_t *device = (cairo_device_observer_t *) _device;
+
+    if (device->target == NULL)
+       return CAIRO_STATUS_SUCCESS;
+
+    cairo_device_flush (device->target);
+    return device->target->status;
+}
+
+static void
+_cairo_device_observer_finish (void *_device)
+{
+    cairo_device_observer_t *device = (cairo_device_observer_t *) _device;
+    log_fini (&device->log);
+    cairo_device_finish (device->target);
+}
+
+static void
+_cairo_device_observer_destroy (void *_device)
+{
+    cairo_device_observer_t *device = (cairo_device_observer_t *) _device;
+    cairo_device_destroy (device->target);
+    free (device);
+}
+
+static const cairo_device_backend_t _cairo_device_observer_backend = {
+    CAIRO_INTERNAL_DEVICE_TYPE_OBSERVER,
+
+    _cairo_device_observer_lock,
+    _cairo_device_observer_unlock,
+
+    _cairo_device_observer_flush,
+    _cairo_device_observer_finish,
+    _cairo_device_observer_destroy,
+};
+
+static cairo_device_t *
+_cairo_device_create_observer_internal (cairo_device_t *target,
+                                       cairo_bool_t record)
+{
+    cairo_device_observer_t *device;
+    cairo_status_t status;
+
+    device = malloc (sizeof (cairo_device_observer_t));
+    if (unlikely (device == NULL))
+       return _cairo_device_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+    _cairo_device_init (&device->base, &_cairo_device_observer_backend);
+    status = log_init (&device->log, record);
+    if (unlikely (status)) {
+       free (device);
+       return _cairo_device_create_in_error (status);
+    }
+
+    device->target = cairo_device_reference (target);
+
+    return &device->base;
+}
+
+/* surface interface */
+
+static cairo_device_observer_t *
+to_device (cairo_surface_observer_t *suface)
+{
+    return (cairo_device_observer_t *)suface->base.device;
+}
+
+static cairo_surface_t *
+_cairo_surface_create_observer_internal (cairo_device_t *device,
+                                        cairo_surface_t *target)
+{
+    cairo_surface_observer_t *surface;
+    cairo_status_t status;
+
+    surface = malloc (sizeof (cairo_surface_observer_t));
+    if (unlikely (surface == NULL))
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+    _cairo_surface_init (&surface->base,
+                        &_cairo_surface_observer_backend, device,
+                        target->content);
+
+    status = log_init (&surface->log,
+                      ((cairo_device_observer_t *)device)->log.record != NULL);
+    if (unlikely (status)) {
+       free (surface);
+       return _cairo_surface_create_in_error (status);
+    }
+
+    surface->target = cairo_surface_reference (target);
+    surface->base.type = surface->target->type;
+    surface->base.is_clear = surface->target->is_clear;
+
+    cairo_list_init (&surface->paint_callbacks);
+    cairo_list_init (&surface->mask_callbacks);
+    cairo_list_init (&surface->fill_callbacks);
+    cairo_list_init (&surface->stroke_callbacks);
+    cairo_list_init (&surface->glyphs_callbacks);
+
+    cairo_list_init (&surface->flush_callbacks);
+    cairo_list_init (&surface->finish_callbacks);
+
+    surface->log.num_surfaces++;
+    to_device (surface)->log.num_surfaces++;
+
+    return &surface->base;
+}
+
+static inline void
+do_callbacks (cairo_surface_observer_t *surface, cairo_list_t *head)
+{
+    struct callback_list *cb;
+
+    cairo_list_foreach_entry (cb, struct callback_list, head, link)
+       cb->func (&surface->base, surface->target, cb->data);
+}
+
+
+static cairo_status_t
+_cairo_surface_observer_finish (void *abstract_surface)
+{
+    cairo_surface_observer_t *surface = abstract_surface;
+
+    do_callbacks (surface, &surface->finish_callbacks);
+
+    cairo_surface_destroy (surface->target);
+    log_fini (&surface->log);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_surface_t *
+_cairo_surface_observer_create_similar (void *abstract_other,
+                                       cairo_content_t content,
+                                       int width, int height)
+{
+    cairo_surface_observer_t *other = abstract_other;
+    cairo_surface_t *target, *surface;
+
+    target = NULL;
+    if (other->target->backend->create_similar)
+       target = other->target->backend->create_similar (other->target, content,
+                                                        width, height);
+    if (target == NULL)
+       target = _cairo_image_surface_create_with_content (content,
+                                                          width, height);
+
+    surface = _cairo_surface_create_observer_internal (other->base.device,
+                                                      target);
+    cairo_surface_destroy (target);
+
+    return surface;
+}
+
+static cairo_surface_t *
+_cairo_surface_observer_create_similar_image (void *other,
+                                             cairo_format_t format,
+                                             int width, int height)
+{
+    cairo_surface_observer_t *surface = other;
+
+    if (surface->target->backend->create_similar_image)
+       return surface->target->backend->create_similar_image (surface->target,
+                                                              format,
+                                                              width, height);
+
+    return NULL;
+}
+
+static cairo_surface_t *
+_cairo_surface_observer_map_to_image (void *abstract_surface,
+                                     const cairo_rectangle_int_t *extents)
+{
+    cairo_surface_observer_t *surface = abstract_surface;
+
+    if (surface->target->backend->map_to_image == NULL)
+       return NULL;
+
+    return surface->target->backend->map_to_image (surface->target, extents);
+}
+
+static cairo_int_status_t
+_cairo_surface_observer_unmap_image (void *abstract_surface,
+                                    cairo_image_surface_t *image)
+{
+    cairo_surface_observer_t *surface = abstract_surface;
+
+    if (surface->target->backend->unmap_image == NULL)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    return surface->target->backend->unmap_image (surface->target, image);
+}
+
+static void
+record_target (cairo_observation_record_t *r,
+              cairo_surface_t *target)
+{
+    cairo_rectangle_int_t extents;
+
+    r->target_content = target->content;
+    if (_cairo_surface_get_extents (target, &extents)) {
+       r->target_width = extents.width;
+       r->target_height = extents.height;
+    } else {
+       r->target_width = -1;
+       r->target_height = -1;
+    }
+}
+
+static cairo_observation_record_t *
+record_paint (cairo_observation_record_t *r,
+             cairo_surface_t *target,
+             cairo_operator_t op,
+             const cairo_pattern_t *source,
+             const cairo_clip_t *clip,
+             cairo_time_t elapsed)
+{
+    record_target (r, target);
+
+    r->op = op;
+    r->source = classify_pattern (source, target);
+    r->mask = -1;
+    r->num_glyphs = -1;
+    r->path = -1;
+    r->fill_rule = -1;
+    r->tolerance = -1;
+    r->antialias = -1;
+    r->clip = classify_clip (clip);
+    r->elapsed = elapsed;
+
+    return r;
+}
+
+static cairo_observation_record_t *
+record_mask (cairo_observation_record_t *r,
+            cairo_surface_t *target,
+            cairo_operator_t op,
+            const cairo_pattern_t *source,
+            const cairo_pattern_t *mask,
+            const cairo_clip_t *clip,
+            cairo_time_t elapsed)
+{
+    record_target (r, target);
+
+    r->op = op;
+    r->source = classify_pattern (source, target);
+    r->mask = classify_pattern (mask, target);
+    r->num_glyphs = -1;
+    r->path = -1;
+    r->fill_rule = -1;
+    r->tolerance = -1;
+    r->antialias = -1;
+    r->clip = classify_clip (clip);
+    r->elapsed = elapsed;
+
+    return r;
+}
+
+static cairo_observation_record_t *
+record_fill (cairo_observation_record_t *r,
+            cairo_surface_t            *target,
+            cairo_operator_t           op,
+            const cairo_pattern_t      *source,
+            const cairo_path_fixed_t   *path,
+            cairo_fill_rule_t           fill_rule,
+            double                      tolerance,
+            cairo_antialias_t           antialias,
+            const cairo_clip_t         *clip,
+            cairo_time_t elapsed)
+{
+    record_target (r, target);
+
+    r->op = op;
+    r->source = classify_pattern (source, target);
+    r->mask = -1;
+    r->num_glyphs = -1;
+    r->path = classify_path (path, TRUE);
+    r->fill_rule = fill_rule;
+    r->tolerance = tolerance;
+    r->antialias = antialias;
+    r->clip = classify_clip (clip);
+    r->elapsed = elapsed;
+
+    return r;
+}
+
+static cairo_observation_record_t *
+record_stroke (cairo_observation_record_t *r,
+              cairo_surface_t          *target,
+              cairo_operator_t         op,
+              const cairo_pattern_t    *source,
+              const cairo_path_fixed_t *path,
+              const cairo_stroke_style_t       *style,
+              const cairo_matrix_t     *ctm,
+              const cairo_matrix_t     *ctm_inverse,
+              double                    tolerance,
+              cairo_antialias_t         antialias,
+              const cairo_clip_t       *clip,
+              cairo_time_t              elapsed)
+{
+    record_target (r, target);
+
+    r->op = op;
+    r->source = classify_pattern (source, target);
+    r->mask = -1;
+    r->num_glyphs = -1;
+    r->path = classify_path (path, FALSE);
+    r->fill_rule = -1;
+    r->tolerance = tolerance;
+    r->antialias = antialias;
+    r->clip = classify_clip (clip);
+    r->elapsed = elapsed;
+
+    return r;
+}
+
+static cairo_observation_record_t *
+record_glyphs (cairo_observation_record_t *r,
+              cairo_surface_t          *target,
+              cairo_operator_t         op,
+              const cairo_pattern_t    *source,
+              cairo_glyph_t            *glyphs,
+              int                       num_glyphs,
+              cairo_scaled_font_t      *scaled_font,
+              const cairo_clip_t       *clip,
+              cairo_time_t              elapsed)
+{
+    record_target (r, target);
+
+    r->op = op;
+    r->source = classify_pattern (source, target);
+    r->mask = -1;
+    r->path = -1;
+    r->num_glyphs = num_glyphs;
+    r->fill_rule = -1;
+    r->tolerance = -1;
+    r->antialias = -1;
+    r->clip = classify_clip (clip);
+    r->elapsed = elapsed;
+
+    return r;
+}
+
+static void
+add_record (cairo_observation_t *log,
+           cairo_observation_record_t *r)
+{
+    cairo_int_status_t status;
+
+    r->index = log->record ? log->record->commands.num_elements : 0;
+
+    status = _cairo_array_append (&log->timings, r);
+    assert (status == CAIRO_INT_STATUS_SUCCESS);
+}
+
+static void
+sync (cairo_surface_t *target, int x, int y)
+{
+    cairo_rectangle_int_t extents;
+
+    extents.x = x;
+    extents.y = y;
+    extents.width  = 1;
+    extents.height = 1;
+
+    cairo_surface_unmap_image (target,
+                              cairo_surface_map_to_image (target,
+                                                          &extents));
+}
+
+static void
+midpt (const cairo_composite_rectangles_t *extents, int *x, int *y)
+{
+    *x = extents->bounded.x + extents->bounded.width / 2;
+    *y = extents->bounded.y + extents->bounded.height / 2;
+}
+
+static void
+add_record_paint (cairo_observation_t *log,
+                cairo_surface_t *target,
+                cairo_operator_t op,
+                const cairo_pattern_t *source,
+                const cairo_clip_t *clip,
+                cairo_time_t elapsed)
+{
+    cairo_observation_record_t record;
+    cairo_int_status_t status;
+
+    add_record (log,
+               record_paint (&record, target, op, source, clip, elapsed));
+
+    /* We have to bypass the high-level surface layer in case it tries to be
+     * too smart and discard operations; we need to record exactly what just
+     * happened on the target.
+     */
+    if (log->record) {
+       status = log->record->base.backend->paint (&log->record->base,
+                                                  op, source, clip);
+       assert (status == CAIRO_INT_STATUS_SUCCESS);
+    }
+
+    if (_cairo_time_gt (elapsed, log->paint.slowest.elapsed))
+       log->paint.slowest = record;
+    log->paint.elapsed = _cairo_time_add (log->paint.elapsed, elapsed);
+}
+
+static cairo_int_status_t
+_cairo_surface_observer_paint (void *abstract_surface,
+                              cairo_operator_t op,
+                              const cairo_pattern_t *source,
+                              const cairo_clip_t *clip)
+{
+    cairo_surface_observer_t *surface = abstract_surface;
+    cairo_device_observer_t *device = to_device (surface);
+    cairo_composite_rectangles_t composite;
+    cairo_int_status_t status;
+    cairo_time_t t;
+    int x, y;
+
+    /* XXX device locking */
+
+    surface->log.paint.count++;
+    surface->log.paint.operators[op]++;
+    add_pattern (&surface->log.paint.source, source, surface->target);
+    add_clip (&surface->log.paint.clip, clip);
+
+    device->log.paint.count++;
+    device->log.paint.operators[op]++;
+    add_pattern (&device->log.paint.source, source, surface->target);
+    add_clip (&device->log.paint.clip, clip);
+
+    status = _cairo_composite_rectangles_init_for_paint (&composite,
+                                                        surface->target,
+                                                        op, source,
+                                                        clip);
+    if (unlikely (status)) {
+       surface->log.paint.noop++;
+       device->log.paint.noop++;
+       return status;
+    }
+
+    midpt (&composite, &x, &y);
+
+    add_extents (&surface->log.paint.extents, &composite);
+    add_extents (&device->log.paint.extents, &composite);
+    _cairo_composite_rectangles_fini (&composite);
+
+    t = _cairo_time_get ();
+    status = _cairo_surface_paint (surface->target,
+                                  op, source,
+                                  clip);
+    if (unlikely (status))
+       return status;
+
+    sync (surface->target, x, y);
+    t = _cairo_time_get_delta (t);
+
+    add_record_paint (&surface->log, surface->target, op, source, clip, t);
+    add_record_paint (&device->log, surface->target, op, source, clip, t);
+
+    do_callbacks (surface, &surface->paint_callbacks);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+add_record_mask (cairo_observation_t *log,
+                cairo_surface_t *target,
+                cairo_operator_t op,
+                const cairo_pattern_t *source,
+                const cairo_pattern_t *mask,
+                const cairo_clip_t *clip,
+                cairo_time_t elapsed)
+{
+    cairo_observation_record_t record;
+    cairo_int_status_t status;
+
+    add_record (log,
+               record_mask (&record, target, op, source, mask, clip, elapsed));
+
+    if (log->record) {
+       status = log->record->base.backend->mask (&log->record->base,
+                                                 op, source, mask, clip);
+       assert (status == CAIRO_INT_STATUS_SUCCESS);
+    }
+
+    if (_cairo_time_gt (elapsed, log->mask.slowest.elapsed))
+       log->mask.slowest = record;
+    log->mask.elapsed = _cairo_time_add (log->mask.elapsed, elapsed);
+}
+
+static cairo_int_status_t
+_cairo_surface_observer_mask (void *abstract_surface,
+                             cairo_operator_t op,
+                             const cairo_pattern_t *source,
+                             const cairo_pattern_t *mask,
+                             const cairo_clip_t *clip)
+{
+    cairo_surface_observer_t *surface = abstract_surface;
+    cairo_device_observer_t *device = to_device (surface);
+    cairo_composite_rectangles_t composite;
+    cairo_int_status_t status;
+    cairo_time_t t;
+    int x, y;
+
+    surface->log.mask.count++;
+    surface->log.mask.operators[op]++;
+    add_pattern (&surface->log.mask.source, source, surface->target);
+    add_pattern (&surface->log.mask.mask, mask, surface->target);
+    add_clip (&surface->log.mask.clip, clip);
+
+    device->log.mask.count++;
+    device->log.mask.operators[op]++;
+    add_pattern (&device->log.mask.source, source, surface->target);
+    add_pattern (&device->log.mask.mask, mask, surface->target);
+    add_clip (&device->log.mask.clip, clip);
+
+    status = _cairo_composite_rectangles_init_for_mask (&composite,
+                                                       surface->target,
+                                                       op, source, mask,
+                                                       clip);
+    if (unlikely (status)) {
+       surface->log.mask.noop++;
+       device->log.mask.noop++;
+       return status;
+    }
+
+    midpt (&composite, &x, &y);
+
+    add_extents (&surface->log.mask.extents, &composite);
+    add_extents (&device->log.mask.extents, &composite);
+    _cairo_composite_rectangles_fini (&composite);
+
+    t = _cairo_time_get ();
+    status =  _cairo_surface_mask (surface->target,
+                                  op, source, mask,
+                                  clip);
+    if (unlikely (status))
+       return status;
+
+    sync (surface->target, x, y);
+    t = _cairo_time_get_delta (t);
+
+    add_record_mask (&surface->log,
+                    surface->target, op, source, mask, clip,
+                    t);
+    add_record_mask (&device->log,
+                    surface->target, op, source, mask, clip,
+                    t);
+
+    do_callbacks (surface, &surface->mask_callbacks);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+add_record_fill (cairo_observation_t *log,
+                cairo_surface_t *target,
+                cairo_operator_t               op,
+                const cairo_pattern_t          *source,
+                const cairo_path_fixed_t       *path,
+                cairo_fill_rule_t               fill_rule,
+                double                          tolerance,
+                cairo_antialias_t               antialias,
+                const cairo_clip_t              *clip,
+                cairo_time_t elapsed)
+{
+    cairo_observation_record_t record;
+    cairo_int_status_t status;
+
+    add_record (log,
+               record_fill (&record,
+                            target, op, source,
+                            path, fill_rule, tolerance, antialias,
+                            clip, elapsed));
+
+    if (log->record) {
+       status = log->record->base.backend->fill (&log->record->base,
+                                                 op, source,
+                                                 path, fill_rule,
+                                                 tolerance, antialias,
+                                                 clip);
+       assert (status == CAIRO_INT_STATUS_SUCCESS);
+    }
+
+    if (_cairo_time_gt (elapsed, log->fill.slowest.elapsed))
+       log->fill.slowest = record;
+    log->fill.elapsed = _cairo_time_add (log->fill.elapsed, elapsed);
+}
+
+static cairo_int_status_t
+_cairo_surface_observer_fill (void                     *abstract_surface,
+                             cairo_operator_t          op,
+                             const cairo_pattern_t     *source,
+                             const cairo_path_fixed_t  *path,
+                             cairo_fill_rule_t         fill_rule,
+                             double                     tolerance,
+                             cairo_antialias_t         antialias,
+                             const cairo_clip_t        *clip)
+{
+    cairo_surface_observer_t *surface = abstract_surface;
+    cairo_device_observer_t *device = to_device (surface);
+    cairo_composite_rectangles_t composite;
+    cairo_int_status_t status;
+    cairo_time_t t;
+    int x, y;
+
+    surface->log.fill.count++;
+    surface->log.fill.operators[op]++;
+    surface->log.fill.fill_rule[fill_rule]++;
+    surface->log.fill.antialias[antialias]++;
+    add_pattern (&surface->log.fill.source, source, surface->target);
+    add_path (&surface->log.fill.path, path, TRUE);
+    add_clip (&surface->log.fill.clip, clip);
+
+    device->log.fill.count++;
+    device->log.fill.operators[op]++;
+    device->log.fill.fill_rule[fill_rule]++;
+    device->log.fill.antialias[antialias]++;
+    add_pattern (&device->log.fill.source, source, surface->target);
+    add_path (&device->log.fill.path, path, TRUE);
+    add_clip (&device->log.fill.clip, clip);
+
+    status = _cairo_composite_rectangles_init_for_fill (&composite,
+                                                       surface->target,
+                                                       op, source, path,
+                                                       clip);
+    if (unlikely (status)) {
+       surface->log.fill.noop++;
+       device->log.fill.noop++;
+       return status;
+    }
+
+    midpt (&composite, &x, &y);
+
+    add_extents (&surface->log.fill.extents, &composite);
+    add_extents (&device->log.fill.extents, &composite);
+    _cairo_composite_rectangles_fini (&composite);
+
+    t = _cairo_time_get ();
+    status = _cairo_surface_fill (surface->target,
+                                 op, source, path,
+                                 fill_rule, tolerance, antialias,
+                                 clip);
+    if (unlikely (status))
+       return status;
+
+    sync (surface->target, x, y);
+    t = _cairo_time_get_delta (t);
+
+    add_record_fill (&surface->log,
+                    surface->target, op, source, path,
+                    fill_rule, tolerance, antialias,
+                    clip, t);
+
+    add_record_fill (&device->log,
+                    surface->target, op, source, path,
+                    fill_rule, tolerance, antialias,
+                    clip, t);
+
+    do_callbacks (surface, &surface->fill_callbacks);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+add_record_stroke (cairo_observation_t *log,
+                cairo_surface_t *target,
+                cairo_operator_t                op,
+                const cairo_pattern_t          *source,
+                const cairo_path_fixed_t       *path,
+                const cairo_stroke_style_t     *style,
+                const cairo_matrix_t           *ctm,
+                const cairo_matrix_t           *ctm_inverse,
+                double                          tolerance,
+                cairo_antialias_t               antialias,
+                const cairo_clip_t             *clip,
+                cairo_time_t elapsed)
+{
+    cairo_observation_record_t record;
+    cairo_int_status_t status;
+
+    add_record (log,
+               record_stroke (&record,
+                              target, op, source,
+                              path, style, ctm,ctm_inverse,
+                              tolerance, antialias,
+                              clip, elapsed));
+
+    if (log->record) {
+       status = log->record->base.backend->stroke (&log->record->base,
+                                                   op, source,
+                                                   path, style, ctm,ctm_inverse,
+                                                   tolerance, antialias,
+                                                   clip);
+       assert (status == CAIRO_INT_STATUS_SUCCESS);
+    }
+
+    if (_cairo_time_gt (elapsed, log->stroke.slowest.elapsed))
+       log->stroke.slowest = record;
+    log->stroke.elapsed = _cairo_time_add (log->stroke.elapsed, elapsed);
+}
+
+static cairo_int_status_t
+_cairo_surface_observer_stroke (void                           *abstract_surface,
+                               cairo_operator_t                 op,
+                               const cairo_pattern_t           *source,
+                               const cairo_path_fixed_t        *path,
+                               const cairo_stroke_style_t      *style,
+                               const cairo_matrix_t            *ctm,
+                               const cairo_matrix_t            *ctm_inverse,
+                               double                           tolerance,
+                               cairo_antialias_t                antialias,
+                               const cairo_clip_t              *clip)
+{
+    cairo_surface_observer_t *surface = abstract_surface;
+    cairo_device_observer_t *device = to_device (surface);
+    cairo_composite_rectangles_t composite;
+    cairo_int_status_t status;
+    cairo_time_t t;
+    int x, y;
+
+    surface->log.stroke.count++;
+    surface->log.stroke.operators[op]++;
+    surface->log.stroke.antialias[antialias]++;
+    surface->log.stroke.caps[style->line_cap]++;
+    surface->log.stroke.joins[style->line_join]++;
+    add_pattern (&surface->log.stroke.source, source, surface->target);
+    add_path (&surface->log.stroke.path, path, FALSE);
+    add_clip (&surface->log.stroke.clip, clip);
+
+    device->log.stroke.count++;
+    device->log.stroke.operators[op]++;
+    device->log.stroke.antialias[antialias]++;
+    device->log.stroke.caps[style->line_cap]++;
+    device->log.stroke.joins[style->line_join]++;
+    add_pattern (&device->log.stroke.source, source, surface->target);
+    add_path (&device->log.stroke.path, path, FALSE);
+    add_clip (&device->log.stroke.clip, clip);
+
+    status = _cairo_composite_rectangles_init_for_stroke (&composite,
+                                                         surface->target,
+                                                         op, source,
+                                                         path, style, ctm,
+                                                         clip);
+    if (unlikely (status)) {
+       surface->log.stroke.noop++;
+       device->log.stroke.noop++;
+       return status;
+    }
+
+    midpt (&composite, &x, &y);
+
+    add_extents (&surface->log.stroke.extents, &composite);
+    add_extents (&device->log.stroke.extents, &composite);
+    _cairo_composite_rectangles_fini (&composite);
+
+    t = _cairo_time_get ();
+    status = _cairo_surface_stroke (surface->target,
+                                 op, source, path,
+                                 style, ctm, ctm_inverse,
+                                 tolerance, antialias,
+                                 clip);
+    if (unlikely (status))
+       return status;
+
+    sync (surface->target, x, y);
+    t = _cairo_time_get_delta (t);
+
+    add_record_stroke (&surface->log,
+                      surface->target, op, source, path,
+                      style, ctm,ctm_inverse,
+                      tolerance, antialias,
+                      clip, t);
+
+    add_record_stroke (&device->log,
+                      surface->target, op, source, path,
+                      style, ctm,ctm_inverse,
+                      tolerance, antialias,
+                      clip, t);
+
+    do_callbacks (surface, &surface->stroke_callbacks);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+add_record_glyphs (cairo_observation_t *log,
+                  cairo_surface_t      *target,
+                  cairo_operator_t      op,
+                  const cairo_pattern_t*source,
+                  cairo_glyph_t        *glyphs,
+                  int                   num_glyphs,
+                  cairo_scaled_font_t  *scaled_font,
+                  const cairo_clip_t   *clip,
+                  cairo_time_t elapsed)
+{
+    cairo_observation_record_t record;
+    cairo_int_status_t status;
+
+    add_record (log,
+               record_glyphs (&record,
+                              target, op, source,
+                              glyphs, num_glyphs, scaled_font,
+                              clip, elapsed));
+
+    if (log->record) {
+       status = log->record->base.backend->show_text_glyphs (&log->record->base,
+                                                             op, source,
+                                                             NULL, 0,
+                                                             glyphs, num_glyphs,
+                                                             NULL, 0, 0,
+                                                             scaled_font,
+                                                             clip);
+       assert (status == CAIRO_INT_STATUS_SUCCESS);
+    }
+
+    if (_cairo_time_gt (elapsed, log->glyphs.slowest.elapsed))
+       log->glyphs.slowest = record;
+    log->glyphs.elapsed = _cairo_time_add (log->glyphs.elapsed, elapsed);
+}
+
+static cairo_int_status_t
+_cairo_surface_observer_glyphs (void                   *abstract_surface,
+                               cairo_operator_t         op,
+                               const cairo_pattern_t   *source,
+                               cairo_glyph_t           *glyphs,
+                               int                      num_glyphs,
+                               cairo_scaled_font_t     *scaled_font,
+                               const cairo_clip_t              *clip)
+{
+    cairo_surface_observer_t *surface = abstract_surface;
+    cairo_device_observer_t *device = to_device (surface);
+    cairo_composite_rectangles_t composite;
+    cairo_int_status_t status;
+    cairo_glyph_t *dev_glyphs;
+    cairo_time_t t;
+    int x, y;
+
+    surface->log.glyphs.count++;
+    surface->log.glyphs.operators[op]++;
+    add_pattern (&surface->log.glyphs.source, source, surface->target);
+    add_clip (&surface->log.glyphs.clip, clip);
+
+    device->log.glyphs.count++;
+    device->log.glyphs.operators[op]++;
+    add_pattern (&device->log.glyphs.source, source, surface->target);
+    add_clip (&device->log.glyphs.clip, clip);
+
+    status = _cairo_composite_rectangles_init_for_glyphs (&composite,
+                                                         surface->target,
+                                                         op, source,
+                                                         scaled_font,
+                                                         glyphs, num_glyphs,
+                                                         clip,
+                                                         NULL);
+    if (unlikely (status)) {
+       surface->log.glyphs.noop++;
+       device->log.glyphs.noop++;
+       return status;
+    }
+
+    midpt (&composite, &x, &y);
+
+    add_extents (&surface->log.glyphs.extents, &composite);
+    add_extents (&device->log.glyphs.extents, &composite);
+    _cairo_composite_rectangles_fini (&composite);
+
+    /* XXX We have to copy the glyphs, because the backend is allowed to
+     * modify! */
+    dev_glyphs = _cairo_malloc_ab (num_glyphs, sizeof (cairo_glyph_t));
+    if (unlikely (dev_glyphs == NULL))
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+    memcpy (dev_glyphs, glyphs, num_glyphs * sizeof (cairo_glyph_t));
+
+    t = _cairo_time_get ();
+    status = _cairo_surface_show_text_glyphs (surface->target, op, source,
+                                             NULL, 0,
+                                             dev_glyphs, num_glyphs,
+                                             NULL, 0, 0,
+                                             scaled_font,
+                                             clip);
+    free (dev_glyphs);
+    if (unlikely (status))
+       return status;
+
+    sync (surface->target, x, y);
+    t = _cairo_time_get_delta (t);
+
+    add_record_glyphs (&surface->log,
+                      surface->target, op, source,
+                      glyphs, num_glyphs, scaled_font,
+                      clip, t);
+
+    add_record_glyphs (&device->log,
+                      surface->target, op, source,
+                      glyphs, num_glyphs, scaled_font,
+                      clip, t);
+
+    do_callbacks (surface, &surface->glyphs_callbacks);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_surface_observer_flush (void *abstract_surface)
+{
+    cairo_surface_observer_t *surface = abstract_surface;
+
+    do_callbacks (surface, &surface->flush_callbacks);
+
+    cairo_surface_flush (surface->target);
+    return surface->target->status;
+}
+
+static cairo_status_t
+_cairo_surface_observer_mark_dirty (void *abstract_surface,
+                                     int x, int y,
+                                     int width, int height)
+{
+    cairo_surface_observer_t *surface = abstract_surface;
+    cairo_status_t status;
+
+    printf ("mark-dirty (%d, %d) x (%d, %d)\n", x, y, width, height);
+
+    status = CAIRO_STATUS_SUCCESS;
+    if (surface->target->backend->mark_dirty_rectangle)
+       status = surface->target->backend->mark_dirty_rectangle (surface->target,
+                                                      x,y, width,height);
+
+    return status;
+}
+
+static cairo_int_status_t
+_cairo_surface_observer_copy_page (void *abstract_surface)
+{
+    cairo_surface_observer_t *surface = abstract_surface;
+    cairo_status_t status;
+
+    status = CAIRO_STATUS_SUCCESS;
+    if (surface->target->backend->copy_page)
+       status = surface->target->backend->copy_page (surface->target);
+
+    return status;
+}
+
+static cairo_int_status_t
+_cairo_surface_observer_show_page (void *abstract_surface)
+{
+    cairo_surface_observer_t *surface = abstract_surface;
+    cairo_status_t status;
+
+    status = CAIRO_STATUS_SUCCESS;
+    if (surface->target->backend->show_page)
+       status = surface->target->backend->show_page (surface->target);
+
+    return status;
+}
+
+static cairo_bool_t
+_cairo_surface_observer_get_extents (void *abstract_surface,
+                                    cairo_rectangle_int_t *extents)
+{
+    cairo_surface_observer_t *surface = abstract_surface;
+    return _cairo_surface_get_extents (surface->target, extents);
+}
+
+static void
+_cairo_surface_observer_get_font_options (void *abstract_surface,
+                                         cairo_font_options_t *options)
+{
+    cairo_surface_observer_t *surface = abstract_surface;
+
+    if (surface->target->backend->get_font_options != NULL)
+       surface->target->backend->get_font_options (surface->target, options);
+}
+
+static cairo_status_t
+_cairo_surface_observer_acquire_source_image (void                    *abstract_surface,
+                                               cairo_image_surface_t  **image_out,
+                                               void                   **image_extra)
+{
+    cairo_surface_observer_t *surface = abstract_surface;
+
+    surface->log.num_sources_acquired++;
+    to_device (surface)->log.num_sources_acquired++;
+
+    return _cairo_surface_acquire_source_image (surface->target,
+                                               image_out, image_extra);
+}
+
+static void
+_cairo_surface_observer_release_source_image (void                   *abstract_surface,
+                                               cairo_image_surface_t  *image,
+                                               void                   *image_extra)
+{
+    cairo_surface_observer_t *surface = abstract_surface;
+
+    _cairo_surface_release_source_image (surface->target, image, image_extra);
+}
+
+static cairo_surface_t *
+_cairo_surface_observer_snapshot (void *abstract_surface)
+{
+    cairo_surface_observer_t *surface = abstract_surface;
+
+    /* XXX hook onto the snapshot so that we measure number of reads */
+
+    if (surface->target->backend->snapshot)
+       return surface->target->backend->snapshot (surface->target);
+
+    return NULL;
+}
+
+static cairo_t *
+_cairo_surface_observer_create_context(void *target)
+{
+    cairo_surface_observer_t *surface = target;
+
+    if (_cairo_surface_is_subsurface (&surface->base))
+       surface = (cairo_surface_observer_t *)
+           _cairo_surface_subsurface_get_target (&surface->base);
+
+    surface->log.num_contexts++;
+    to_device (surface)->log.num_contexts++;
+
+    return surface->target->backend->create_context (target);
+}
+
+static const cairo_surface_backend_t _cairo_surface_observer_backend = {
+    CAIRO_INTERNAL_SURFACE_TYPE_OBSERVER,
+    _cairo_surface_observer_finish,
+
+    _cairo_surface_observer_create_context,
+
+    _cairo_surface_observer_create_similar,
+    _cairo_surface_observer_create_similar_image,
+    _cairo_surface_observer_map_to_image,
+    _cairo_surface_observer_unmap_image,
+
+    _cairo_surface_observer_acquire_source_image,
+    _cairo_surface_observer_release_source_image,
+    _cairo_surface_observer_snapshot,
+
+    _cairo_surface_observer_copy_page,
+    _cairo_surface_observer_show_page,
+
+    _cairo_surface_observer_get_extents,
+    _cairo_surface_observer_get_font_options,
+
+    _cairo_surface_observer_flush,
+    _cairo_surface_observer_mark_dirty,
+
+    _cairo_surface_observer_paint,
+    _cairo_surface_observer_mask,
+    _cairo_surface_observer_stroke,
+    _cairo_surface_observer_fill,
+    NULL, /* fill-stroke */
+    _cairo_surface_observer_glyphs,
+};
+
+/**
+ * cairo_surface_create_observer:
+ * @target: an existing surface for which the observer will watch
+ *
+ * Create a new surface that exists solely to watch another is doing. In
+ * the process it will log operations and times, which are fast, which are
+ * slow, which are frequent, etc.
+ *
+ * Return value: a pointer to the newly allocated surface. The caller
+ * owns the surface and should call cairo_surface_destroy() when done
+ * with it.
+ *
+ * This function always returns a valid pointer, but it will return a
+ * pointer to a "nil" surface if @other is already in an error state
+ * or any other error occurs.
+ *
+ * Since: 1.12
+ **/
+cairo_surface_t *
+cairo_surface_create_observer (cairo_surface_t *target,
+                              cairo_surface_observer_mode_t mode)
+{
+    cairo_device_t *device;
+    cairo_surface_t *surface;
+    cairo_bool_t record;
+
+    if (unlikely (target->status))
+       return _cairo_surface_create_in_error (target->status);
+    if (unlikely (target->finished))
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
+
+    record = mode & CAIRO_SURFACE_OBSERVER_RECORD_OPERATIONS;
+    device = _cairo_device_create_observer_internal (target->device, record);
+    if (unlikely (device->status))
+       return _cairo_surface_create_in_error (device->status);
+
+    surface = _cairo_surface_create_observer_internal (device, target);
+    cairo_device_destroy (device);
+
+    return surface;
+}
+
+static cairo_status_t
+_cairo_surface_observer_add_callback (cairo_list_t *head,
+                                     cairo_surface_observer_callback_t func,
+                                     void *data)
+{
+    struct callback_list *cb;
+
+    cb = malloc (sizeof (*cb));
+    if (unlikely (cb == NULL))
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    cairo_list_add (&cb->link, head);
+    cb->func = func;
+    cb->data = data;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_status_t
+cairo_surface_observer_add_paint_callback (cairo_surface_t *abstract_surface,
+                                           cairo_surface_observer_callback_t func,
+                                           void *data)
+{
+    cairo_surface_observer_t *surface;
+
+    if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_surface->ref_count)))
+       return abstract_surface->status;
+
+    if (! _cairo_surface_is_observer (abstract_surface))
+       return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+
+    surface = (cairo_surface_observer_t *)abstract_surface;
+    return _cairo_surface_observer_add_callback (&surface->paint_callbacks,
+                                                func, data);
+}
+
+cairo_status_t
+cairo_surface_observer_add_mask_callback (cairo_surface_t *abstract_surface,
+                                         cairo_surface_observer_callback_t func,
+                                         void *data)
+{
+    cairo_surface_observer_t *surface;
+
+    if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_surface->ref_count)))
+       return abstract_surface->status;
+
+    if (! _cairo_surface_is_observer (abstract_surface))
+       return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+
+    surface = (cairo_surface_observer_t *)abstract_surface;
+    return _cairo_surface_observer_add_callback (&surface->mask_callbacks,
+                                                func, data);
+}
+
+cairo_status_t
+cairo_surface_observer_add_fill_callback (cairo_surface_t *abstract_surface,
+                                         cairo_surface_observer_callback_t func,
+                                         void *data)
+{
+    cairo_surface_observer_t *surface;
+
+    if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_surface->ref_count)))
+       return abstract_surface->status;
+
+    if (! _cairo_surface_is_observer (abstract_surface))
+       return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+
+    surface = (cairo_surface_observer_t *)abstract_surface;
+    return _cairo_surface_observer_add_callback (&surface->fill_callbacks,
+                                                func, data);
+}
+
+cairo_status_t
+cairo_surface_observer_add_stroke_callback (cairo_surface_t *abstract_surface,
+                                           cairo_surface_observer_callback_t func,
+                                           void *data)
+{
+    cairo_surface_observer_t *surface;
+
+    if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_surface->ref_count)))
+       return abstract_surface->status;
+
+    if (! _cairo_surface_is_observer (abstract_surface))
+       return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+
+    surface = (cairo_surface_observer_t *)abstract_surface;
+    return _cairo_surface_observer_add_callback (&surface->stroke_callbacks,
+                                                func, data);
+}
+
+cairo_status_t
+cairo_surface_observer_add_glyphs_callback (cairo_surface_t *abstract_surface,
+                                           cairo_surface_observer_callback_t func,
+                                           void *data)
+{
+    cairo_surface_observer_t *surface;
+
+    if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_surface->ref_count)))
+       return abstract_surface->status;
+
+    if (! _cairo_surface_is_observer (abstract_surface))
+       return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+
+    surface = (cairo_surface_observer_t *)abstract_surface;
+    return _cairo_surface_observer_add_callback (&surface->glyphs_callbacks,
+                                                func, data);
+}
+
+cairo_status_t
+cairo_surface_observer_add_flush_callback (cairo_surface_t *abstract_surface,
+                                          cairo_surface_observer_callback_t func,
+                                          void *data)
+{
+    cairo_surface_observer_t *surface;
+
+    if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_surface->ref_count)))
+       return abstract_surface->status;
+
+    if (! _cairo_surface_is_observer (abstract_surface))
+       return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+
+    surface = (cairo_surface_observer_t *)abstract_surface;
+    return _cairo_surface_observer_add_callback (&surface->flush_callbacks,
+                                                func, data);
+}
+
+cairo_status_t
+cairo_surface_observer_add_finish_callback (cairo_surface_t *abstract_surface,
+                                           cairo_surface_observer_callback_t func,
+                                           void *data)
+{
+    cairo_surface_observer_t *surface;
+
+    if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_surface->ref_count)))
+       return abstract_surface->status;
+
+    if (! _cairo_surface_is_observer (abstract_surface))
+       return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+
+    surface = (cairo_surface_observer_t *)abstract_surface;
+    return _cairo_surface_observer_add_callback (&surface->finish_callbacks,
+                                                func, data);
+}
+
+static void
+print_extents (cairo_output_stream_t *stream, const struct extents *e)
+{
+    _cairo_output_stream_printf (stream,
+                                "  extents: total %g, avg %g [unbounded %d]\n",
+                                e->area.sum,
+                                e->area.sum / e->area.count,
+                                e->unbounded);
+}
+
+static inline int ordercmp (int a, int b, const unsigned int *array)
+{
+    /* high to low */
+    return array[b] - array[a];
+}
+CAIRO_COMBSORT_DECLARE_WITH_DATA (sort_order, int, ordercmp)
+
+static void
+print_array (cairo_output_stream_t *stream,
+            const unsigned int *array,
+            const char **names,
+            int count)
+{
+    int order[64];
+    int i, j;
+
+    assert (count < ARRAY_LENGTH (order));
+    for (i = j = 0; i < count; i++) {
+       if (array[i] != 0)
+           order[j++] = i;
+    }
+
+    sort_order (order, j, (void *)array);
+    for (i = 0; i < j; i++)
+       _cairo_output_stream_printf (stream, " %d %s%s",
+                                    array[order[i]], names[order[i]],
+                                    i < j -1 ? "," : "");
+}
+
+static const char *operator_names[] = {
+    "CLEAR",   /* CAIRO_OPERATOR_CLEAR */
+
+    "SOURCE",  /* CAIRO_OPERATOR_SOURCE */
+    "OVER",            /* CAIRO_OPERATOR_OVER */
+    "IN",              /* CAIRO_OPERATOR_IN */
+    "OUT",             /* CAIRO_OPERATOR_OUT */
+    "ATOP",            /* CAIRO_OPERATOR_ATOP */
+
+    "DEST",            /* CAIRO_OPERATOR_DEST */
+    "DEST_OVER",       /* CAIRO_OPERATOR_DEST_OVER */
+    "DEST_IN", /* CAIRO_OPERATOR_DEST_IN */
+    "DEST_OUT",        /* CAIRO_OPERATOR_DEST_OUT */
+    "DEST_ATOP",       /* CAIRO_OPERATOR_DEST_ATOP */
+
+    "XOR",             /* CAIRO_OPERATOR_XOR */
+    "ADD",             /* CAIRO_OPERATOR_ADD */
+    "SATURATE",        /* CAIRO_OPERATOR_SATURATE */
+
+    "MULTIPLY",        /* CAIRO_OPERATOR_MULTIPLY */
+    "SCREEN",  /* CAIRO_OPERATOR_SCREEN */
+    "OVERLAY", /* CAIRO_OPERATOR_OVERLAY */
+    "DARKEN",  /* CAIRO_OPERATOR_DARKEN */
+    "LIGHTEN", /* CAIRO_OPERATOR_LIGHTEN */
+    "DODGE",   /* CAIRO_OPERATOR_COLOR_DODGE */
+    "BURN",            /* CAIRO_OPERATOR_COLOR_BURN */
+    "HARD_LIGHT",      /* CAIRO_OPERATOR_HARD_LIGHT */
+    "SOFT_LIGHT",      /* CAIRO_OPERATOR_SOFT_LIGHT */
+    "DIFFERENCE",      /* CAIRO_OPERATOR_DIFFERENCE */
+    "EXCLUSION",       /* CAIRO_OPERATOR_EXCLUSION */
+    "HSL_HUE", /* CAIRO_OPERATOR_HSL_HUE */
+    "HSL_SATURATION", /* CAIRO_OPERATOR_HSL_SATURATION */
+    "HSL_COLOR",       /* CAIRO_OPERATOR_HSL_COLOR */
+    "HSL_LUMINOSITY" /* CAIRO_OPERATOR_HSL_LUMINOSITY */
+};
+static void
+print_operators (cairo_output_stream_t *stream, unsigned int *array)
+{
+    _cairo_output_stream_printf (stream, "  op:");
+    print_array (stream, array, operator_names, NUM_OPERATORS);
+    _cairo_output_stream_printf (stream, "\n");
+}
+
+static const char *fill_rule_names[] = {
+    "non-zero",
+    "even-odd",
+};
+static void
+print_fill_rule (cairo_output_stream_t *stream, unsigned int *array)
+{
+    _cairo_output_stream_printf (stream, "  fill rule:");
+    print_array (stream, array, fill_rule_names, ARRAY_LENGTH(fill_rule_names));
+    _cairo_output_stream_printf (stream, "\n");
+}
+
+static const char *cap_names[] = {
+    "butt",            /* CAIRO_LINE_CAP_BUTT */
+    "round",   /* CAIRO_LINE_CAP_ROUND */
+    "square"   /* CAIRO_LINE_CAP_SQUARE */
+};
+static void
+print_line_caps (cairo_output_stream_t *stream, unsigned int *array)
+{
+    _cairo_output_stream_printf (stream, "  caps:");
+    print_array (stream, array, cap_names, NUM_CAPS);
+    _cairo_output_stream_printf (stream, "\n");
+}
+
+static const char *join_names[] = {
+    "miter",   /* CAIRO_LINE_JOIN_MITER */
+    "round",   /* CAIRO_LINE_JOIN_ROUND */
+    "bevel",   /* CAIRO_LINE_JOIN_BEVEL */
+};
+static void
+print_line_joins (cairo_output_stream_t *stream, unsigned int *array)
+{
+    _cairo_output_stream_printf (stream, "  joins:");
+    print_array (stream, array, join_names, NUM_JOINS);
+    _cairo_output_stream_printf (stream, "\n");
+}
+
+static const char *antialias_names[] = {
+    "default",
+    "none",
+    "gray",
+    "subpixel",
+    "fast",
+    "good",
+    "best"
+};
+static void
+print_antialias (cairo_output_stream_t *stream, unsigned int *array)
+{
+    _cairo_output_stream_printf (stream, "  antialias:");
+    print_array (stream, array, antialias_names, NUM_ANTIALIAS);
+    _cairo_output_stream_printf (stream, "\n");
+}
+
+static const char *pattern_names[] = {
+    "native",
+    "record",
+    "other surface",
+    "solid",
+    "linear",
+    "radial",
+    "mesh"
+};
+static void
+print_pattern (cairo_output_stream_t *stream,
+              const char *name,
+              const struct pattern *p)
+{
+    _cairo_output_stream_printf (stream, "  %s:", name);
+    print_array (stream, p->type, pattern_names, ARRAY_LENGTH (pattern_names));
+    _cairo_output_stream_printf (stream, "\n");
+}
+
+static const char *path_names[] = {
+    "empty",
+    "pixel-aligned",
+    "rectliinear",
+    "straight",
+    "curved",
+};
+static void
+print_path (cairo_output_stream_t *stream,
+           const struct path *p)
+{
+    _cairo_output_stream_printf (stream, "  path:");
+    print_array (stream, p->type, path_names, ARRAY_LENGTH (path_names));
+    _cairo_output_stream_printf (stream, "\n");
+}
+
+static const char *clip_names[] = {
+    "none",
+    "region",
+    "boxes",
+    "single path",
+    "polygon",
+    "general",
+};
+static void
+print_clip (cairo_output_stream_t *stream, const struct clip *c)
+{
+    _cairo_output_stream_printf (stream, "  clip:");
+    print_array (stream, c->type, clip_names, ARRAY_LENGTH (clip_names));
+    _cairo_output_stream_printf (stream, "\n");
+}
+
+static void
+print_record (cairo_output_stream_t *stream,
+             cairo_observation_record_t *r)
+{
+    _cairo_output_stream_printf (stream, "  op: %s\n", operator_names[r->op]);
+    _cairo_output_stream_printf (stream, "  source: %s\n",
+                                pattern_names[r->source]);
+    if (r->mask != -1)
+       _cairo_output_stream_printf (stream, "  mask: %s\n",
+                                    pattern_names[r->mask]);
+    if (r->num_glyphs != -1)
+       _cairo_output_stream_printf (stream, "  num_glyphs: %d\n",
+                                    r->num_glyphs);
+    if (r->path != -1)
+       _cairo_output_stream_printf (stream, "  path: %s\n",
+                                   path_names[r->path]);
+    if (r->fill_rule != -1)
+       _cairo_output_stream_printf (stream, "  fill rule: %s\n",
+                                    fill_rule_names[r->fill_rule]);
+    if (r->antialias != -1)
+       _cairo_output_stream_printf (stream, "  antialias: %s\n",
+                                    antialias_names[r->antialias]);
+    _cairo_output_stream_printf (stream, "  clip: %s\n", clip_names[r->clip]);
+    _cairo_output_stream_printf (stream, "  elapsed: %f ns\n",
+                                _cairo_time_to_ns (r->elapsed));
+}
+
+static double percent (cairo_time_t a, cairo_time_t b)
+{
+    /* Fake %.1f */
+    return _cairo_round (_cairo_time_to_s (a) * 1000 /
+                        _cairo_time_to_s (b)) / 10;
+}
+
+static cairo_bool_t
+replay_record (cairo_observation_t *log,
+              cairo_observation_record_t *r,
+              cairo_device_t *script)
+{
+#if CAIRO_HAS_SCRIPT_SURFACE
+    cairo_surface_t *surface;
+    cairo_int_status_t status;
+
+    if (log->record == NULL || script == NULL)
+       return FALSE;
+
+    surface = cairo_script_surface_create (script,
+                                          r->target_content,
+                                          r->target_width,
+                                          r->target_height);
+    status =
+       _cairo_recording_surface_replay_one (log->record, r->index, surface);
+    cairo_surface_destroy (surface);
+
+    assert (status == CAIRO_INT_STATUS_SUCCESS);
+
+    return TRUE;
+#else
+    return FALSE;
+#endif
+}
+
+static cairo_time_t
+_cairo_observation_total_elapsed (cairo_observation_t *log)
+{
+    cairo_time_t total;
+
+    total = log->paint.elapsed;
+    total = _cairo_time_add (total, log->mask.elapsed);
+    total = _cairo_time_add (total, log->fill.elapsed);
+    total = _cairo_time_add (total, log->stroke.elapsed);
+    total = _cairo_time_add (total, log->glyphs.elapsed);
+
+    return total;
+}
+
+static void
+_cairo_observation_print (cairo_output_stream_t *stream,
+                         cairo_observation_t *log)
+{
+    cairo_device_t *script;
+    cairo_time_t total;
+
+#if CAIRO_HAS_SCRIPT_SURFACE
+    script = _cairo_script_context_create_internal (stream);
+    _cairo_script_context_attach_snapshots (script, FALSE);
+#else
+    script = NULL;
+#endif
+
+    total = _cairo_observation_total_elapsed (log);
+
+    _cairo_output_stream_printf (stream, "elapsed: %f\n",
+                                _cairo_time_to_ns (total));
+    _cairo_output_stream_printf (stream, "surfaces: %d\n",
+                                log->num_surfaces);
+    _cairo_output_stream_printf (stream, "contexts: %d\n",
+                                log->num_contexts);
+    _cairo_output_stream_printf (stream, "sources acquired: %d\n",
+                                log->num_sources_acquired);
+
+
+    _cairo_output_stream_printf (stream, "paint: count %d [no-op %d], elapsed %f [%f%%]\n",
+                                log->paint.count, log->paint.noop,
+                                _cairo_time_to_ns (log->paint.elapsed),
+                                percent (log->paint.elapsed, total));
+    if (log->paint.count) {
+       print_extents (stream, &log->paint.extents);
+       print_operators (stream, log->paint.operators);
+       print_pattern (stream, "source", &log->paint.source);
+       print_clip (stream, &log->paint.clip);
+
+       _cairo_output_stream_printf (stream, "slowest paint: %f%%\n",
+                                    percent (log->paint.slowest.elapsed,
+                                             log->paint.elapsed));
+       print_record (stream, &log->paint.slowest);
+
+       _cairo_output_stream_printf (stream, "\n");
+       if (replay_record (log, &log->paint.slowest, script))
+           _cairo_output_stream_printf (stream, "\n\n");
+    }
+
+    _cairo_output_stream_printf (stream, "mask: count %d [no-op %d], elapsed %f [%f%%]\n",
+                                log->mask.count, log->mask.noop,
+                                _cairo_time_to_ns (log->mask.elapsed),
+                                percent (log->mask.elapsed, total));
+    if (log->mask.count) {
+       print_extents (stream, &log->mask.extents);
+       print_operators (stream, log->mask.operators);
+       print_pattern (stream, "source", &log->mask.source);
+       print_pattern (stream, "mask", &log->mask.mask);
+       print_clip (stream, &log->mask.clip);
+
+       _cairo_output_stream_printf (stream, "slowest mask: %f%%\n",
+                                    percent (log->mask.slowest.elapsed,
+                                             log->mask.elapsed));
+       print_record (stream, &log->mask.slowest);
+
+       _cairo_output_stream_printf (stream, "\n");
+       if (replay_record (log, &log->mask.slowest, script))
+           _cairo_output_stream_printf (stream, "\n\n");
+    }
+
+    _cairo_output_stream_printf (stream, "fill: count %d [no-op %d], elaspsed %f [%f%%]\n",
+                                log->fill.count, log->fill.noop,
+                                _cairo_time_to_ns (log->fill.elapsed),
+                                percent (log->fill.elapsed, total));
+    if (log->fill.count) {
+       print_extents (stream, &log->fill.extents);
+       print_operators (stream, log->fill.operators);
+       print_pattern (stream, "source", &log->fill.source);
+       print_path (stream, &log->fill.path);
+       print_fill_rule (stream, log->fill.fill_rule);
+       print_antialias (stream, log->fill.antialias);
+       print_clip (stream, &log->fill.clip);
+
+       _cairo_output_stream_printf (stream, "slowest fill: %f%%\n",
+                                    percent (log->fill.slowest.elapsed,
+                                             log->fill.elapsed));
+       print_record (stream, &log->fill.slowest);
+
+       _cairo_output_stream_printf (stream, "\n");
+       if (replay_record (log, &log->fill.slowest, script))
+           _cairo_output_stream_printf (stream, "\n\n");
+    }
+
+    _cairo_output_stream_printf (stream, "stroke: count %d [no-op %d], elapsed %f [%f%%]\n",
+                                log->stroke.count, log->stroke.noop,
+                                _cairo_time_to_ns (log->stroke.elapsed),
+                                percent (log->stroke.elapsed, total));
+    if (log->stroke.count) {
+       print_extents (stream, &log->stroke.extents);
+       print_operators (stream, log->stroke.operators);
+       print_pattern (stream, "source", &log->stroke.source);
+       print_path (stream, &log->stroke.path);
+       print_antialias (stream, log->stroke.antialias);
+       print_line_caps (stream, log->stroke.caps);
+       print_line_joins (stream, log->stroke.joins);
+       print_clip (stream, &log->stroke.clip);
+
+       _cairo_output_stream_printf (stream, "slowest stroke: %f%%\n",
+                                    percent (log->stroke.slowest.elapsed,
+                                             log->stroke.elapsed));
+       print_record (stream, &log->stroke.slowest);
+
+       _cairo_output_stream_printf (stream, "\n");
+       if (replay_record (log, &log->stroke.slowest, script))
+           _cairo_output_stream_printf (stream, "\n\n");
+    }
+
+    _cairo_output_stream_printf (stream, "glyphs: count %d [no-op %d], elasped %f [%f%%]\n",
+                                log->glyphs.count, log->glyphs.noop,
+                                _cairo_time_to_ns (log->glyphs.elapsed),
+                                percent (log->glyphs.elapsed, total));
+    if (log->glyphs.count) {
+       print_extents (stream, &log->glyphs.extents);
+       print_operators (stream, log->glyphs.operators);
+       print_pattern (stream, "source", &log->glyphs.source);
+       print_clip (stream, &log->glyphs.clip);
+
+       _cairo_output_stream_printf (stream, "slowest glyphs: %f%%\n",
+                                    percent (log->glyphs.slowest.elapsed,
+                                             log->glyphs.elapsed));
+       print_record (stream, &log->glyphs.slowest);
+
+       _cairo_output_stream_printf (stream, "\n");
+       if (replay_record (log, &log->glyphs.slowest, script))
+           _cairo_output_stream_printf (stream, "\n\n");
+    }
+
+    cairo_device_destroy (script);
+}
+
+void
+cairo_surface_observer_print (cairo_surface_t *abstract_surface,
+                             cairo_write_func_t write_func,
+                             void *closure)
+{
+    cairo_output_stream_t *stream;
+    cairo_surface_observer_t *surface;
+
+    if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_surface->ref_count)))
+       return;
+
+    if (! _cairo_surface_is_observer (abstract_surface))
+       return;
+
+    surface = (cairo_surface_observer_t *) abstract_surface;
+
+    stream = _cairo_output_stream_create (write_func, NULL, closure);
+    _cairo_observation_print (stream, &surface->log);
+    _cairo_output_stream_destroy (stream);
+}
+
+double
+cairo_surface_observer_elapsed (cairo_surface_t *abstract_surface)
+{
+    cairo_surface_observer_t *surface;
+
+    if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_surface->ref_count)))
+       return -1;
+
+    if (! _cairo_surface_is_observer (abstract_surface))
+       return -1;
+
+    surface = (cairo_surface_observer_t *) abstract_surface;
+    return _cairo_time_to_ns (_cairo_observation_total_elapsed (&surface->log));
+}
+
+void
+cairo_device_observer_print (cairo_device_t *abstract_device,
+                            cairo_write_func_t write_func,
+                            void *closure)
+{
+    cairo_output_stream_t *stream;
+    cairo_device_observer_t *device;
+
+    if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_device->ref_count)))
+       return;
+
+    if (! _cairo_device_is_observer (abstract_device))
+       return;
+
+    device = (cairo_device_observer_t *) abstract_device;
+
+    stream = _cairo_output_stream_create (write_func, NULL, closure);
+    _cairo_observation_print (stream, &device->log);
+    _cairo_output_stream_destroy (stream);
+}
+
+double
+cairo_device_observer_elapsed (cairo_device_t *abstract_device)
+{
+    cairo_device_observer_t *device;
+
+    if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_device->ref_count)))
+       return -1;
+
+    if (! _cairo_device_is_observer (abstract_device))
+       return -1;
+
+    device = (cairo_device_observer_t *) abstract_device;
+    return _cairo_time_to_ns (_cairo_observation_total_elapsed (&device->log));
+}
+
+double
+cairo_device_observer_paint_elapsed (cairo_device_t *abstract_device)
+{
+    cairo_device_observer_t *device;
+
+    if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_device->ref_count)))
+       return -1;
+
+    if (! _cairo_device_is_observer (abstract_device))
+       return -1;
+
+    device = (cairo_device_observer_t *) abstract_device;
+    return _cairo_time_to_ns (device->log.paint.elapsed);
+}
+
+double
+cairo_device_observer_mask_elapsed (cairo_device_t *abstract_device)
+{
+    cairo_device_observer_t *device;
+
+    if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_device->ref_count)))
+       return -1;
+
+    if (! _cairo_device_is_observer (abstract_device))
+       return -1;
+
+    device = (cairo_device_observer_t *) abstract_device;
+    return _cairo_time_to_ns (device->log.mask.elapsed);
+}
+
+double
+cairo_device_observer_fill_elapsed (cairo_device_t *abstract_device)
+{
+    cairo_device_observer_t *device;
+
+    if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_device->ref_count)))
+       return -1;
+
+    if (! _cairo_device_is_observer (abstract_device))
+       return -1;
+
+    device = (cairo_device_observer_t *) abstract_device;
+    return _cairo_time_to_ns (device->log.fill.elapsed);
+}
+
+double
+cairo_device_observer_stroke_elapsed (cairo_device_t *abstract_device)
+{
+    cairo_device_observer_t *device;
+
+    if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_device->ref_count)))
+       return -1;
+
+    if (! _cairo_device_is_observer (abstract_device))
+       return -1;
+
+    device = (cairo_device_observer_t *) abstract_device;
+    return _cairo_time_to_ns (device->log.stroke.elapsed);
+}
+
+double
+cairo_device_observer_glyphs_elapsed (cairo_device_t *abstract_device)
+{
+    cairo_device_observer_t *device;
+
+    if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_device->ref_count)))
+       return -1;
+
+    if (! _cairo_device_is_observer (abstract_device))
+       return -1;
+
+    device = (cairo_device_observer_t *) abstract_device;
+    return _cairo_time_to_ns (device->log.glyphs.elapsed);
+}
diff --git a/src/cairo-surface-offset-private.h b/src/cairo-surface-offset-private.h
new file mode 100644 (file)
index 0000000..310ba56
--- /dev/null
@@ -0,0 +1,95 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ * Copyright © 2005 Red Hat, Inc.
+ * Copyright © 2009 Chris Wilson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *     Chris Wilson <chris@chris-wilson.co.u>
+ */
+
+#ifndef CAIRO_SURFACE_OFFSET_PRIVATE_H
+#define CAIRO_SURFACE_OFFSET_PRIVATE_H
+
+#include "cairo-types-private.h"
+
+CAIRO_BEGIN_DECLS
+
+cairo_private cairo_status_t
+_cairo_surface_offset_paint (cairo_surface_t *target,
+                            int x, int y,
+                            cairo_operator_t    op,
+                            const cairo_pattern_t *source,
+                            const cairo_clip_t     *clip);
+
+cairo_private cairo_status_t
+_cairo_surface_offset_mask (cairo_surface_t *target,
+                           int x, int y,
+                           cairo_operator_t     op,
+                           const cairo_pattern_t *source,
+                           const cairo_pattern_t *mask,
+                           const cairo_clip_t      *clip);
+
+cairo_private cairo_status_t
+_cairo_surface_offset_stroke (cairo_surface_t *surface,
+                             int x, int y,
+                             cairo_operator_t           op,
+                             const cairo_pattern_t     *source,
+                             const cairo_path_fixed_t  *path,
+                             const cairo_stroke_style_t        *stroke_style,
+                             const cairo_matrix_t              *ctm,
+                             const cairo_matrix_t              *ctm_inverse,
+                             double                     tolerance,
+                             cairo_antialias_t  antialias,
+                             const cairo_clip_t                *clip);
+
+cairo_private cairo_status_t
+_cairo_surface_offset_fill (cairo_surface_t    *surface,
+                           int x, int y,
+                           cairo_operator_t     op,
+                           const cairo_pattern_t*source,
+                           const cairo_path_fixed_t    *path,
+                           cairo_fill_rule_t    fill_rule,
+                           double               tolerance,
+                           cairo_antialias_t    antialias,
+                           const cairo_clip_t          *clip);
+
+cairo_private cairo_status_t
+_cairo_surface_offset_glyphs (cairo_surface_t          *surface,
+                             int x, int y,
+                             cairo_operator_t           op,
+                             const cairo_pattern_t     *source,
+                             cairo_scaled_font_t       *scaled_font,
+                             cairo_glyph_t             *glyphs,
+                             int                        num_glyphs,
+                             const cairo_clip_t                *clip);
+
+#endif /* CAIRO_SURFACE_OFFSET_PRIVATE_H */
diff --git a/src/cairo-surface-offset.c b/src/cairo-surface-offset.c
new file mode 100644 (file)
index 0000000..d2cc030
--- /dev/null
@@ -0,0 +1,307 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2005 Red Hat, Inc
+ * Copyright © 2007 Adrian Johnson
+ * Copyright © 2009 Chris Wilson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ *      Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-error-private.h"
+#include "cairo-pattern-private.h"
+#include "cairo-surface-offset-private.h"
+
+/* A collection of routines to facilitate drawing to an alternate surface. */
+
+static void
+_copy_transformed_pattern (cairo_pattern_t *pattern,
+                          const cairo_pattern_t *original,
+                          const cairo_matrix_t  *ctm_inverse)
+{
+    _cairo_pattern_init_static_copy (pattern, original);
+
+    if (! _cairo_matrix_is_identity (ctm_inverse))
+       _cairo_pattern_transform (pattern, ctm_inverse);
+}
+
+cairo_status_t
+_cairo_surface_offset_paint (cairo_surface_t           *target,
+                            int x, int y,
+                            cairo_operator_t            op,
+                            const cairo_pattern_t      *source,
+                            const cairo_clip_t         *clip)
+{
+    cairo_status_t status;
+    cairo_clip_t *dev_clip = (cairo_clip_t *) clip;
+    cairo_pattern_union_t source_copy;
+
+    if (unlikely (target->status))
+       return target->status;
+
+    if (_cairo_clip_is_all_clipped (clip))
+       return CAIRO_STATUS_SUCCESS;
+
+    if (x | y) {
+       cairo_matrix_t m;
+
+       dev_clip = _cairo_clip_copy_with_translation (clip, -x, -y);
+
+       cairo_matrix_init_translate (&m, x, y);
+       _copy_transformed_pattern (&source_copy.base, source, &m);
+       source = &source_copy.base;
+    }
+
+    status = _cairo_surface_paint (target, op, source, dev_clip);
+
+    if (dev_clip != clip)
+       _cairo_clip_destroy (dev_clip);
+
+    return status;
+}
+
+cairo_status_t
+_cairo_surface_offset_mask (cairo_surface_t            *target,
+                           int x, int y,
+                           cairo_operator_t             op,
+                           const cairo_pattern_t       *source,
+                           const cairo_pattern_t       *mask,
+                           const cairo_clip_t          *clip)
+{
+    cairo_status_t status;
+    cairo_clip_t *dev_clip = (cairo_clip_t *) clip;
+    cairo_pattern_union_t source_copy;
+    cairo_pattern_union_t mask_copy;
+
+    if (unlikely (target->status))
+       return target->status;
+
+    if (_cairo_clip_is_all_clipped (clip))
+       return CAIRO_STATUS_SUCCESS;
+
+    if (x | y) {
+       cairo_matrix_t m;
+
+       dev_clip = _cairo_clip_copy_with_translation (clip, -x, -y);
+
+       cairo_matrix_init_translate (&m, x, y);
+       _copy_transformed_pattern (&source_copy.base, source, &m);
+       _copy_transformed_pattern (&mask_copy.base, mask, &m);
+       source = &source_copy.base;
+       mask = &mask_copy.base;
+    }
+
+    status = _cairo_surface_mask (target, op,
+                                 source, mask,
+                                 dev_clip);
+
+    if (dev_clip != clip)
+       _cairo_clip_destroy (dev_clip);
+
+    return status;
+}
+
+cairo_status_t
+_cairo_surface_offset_stroke (cairo_surface_t          *surface,
+                             int x, int y,
+                             cairo_operator_t           op,
+                             const cairo_pattern_t     *source,
+                             const cairo_path_fixed_t  *path,
+                             const cairo_stroke_style_t*stroke_style,
+                             const cairo_matrix_t      *ctm,
+                             const cairo_matrix_t      *ctm_inverse,
+                             double                     tolerance,
+                             cairo_antialias_t          antialias,
+                             const cairo_clip_t                *clip)
+{
+    cairo_path_fixed_t path_copy, *dev_path = (cairo_path_fixed_t *) path;
+    cairo_clip_t *dev_clip = (cairo_clip_t *) clip;
+    cairo_matrix_t dev_ctm = *ctm;
+    cairo_matrix_t dev_ctm_inverse = *ctm_inverse;
+    cairo_pattern_union_t source_copy;
+    cairo_status_t status;
+
+    if (unlikely (surface->status))
+       return surface->status;
+
+    if (_cairo_clip_is_all_clipped (clip))
+       return CAIRO_STATUS_SUCCESS;
+
+    if (x | y) {
+       cairo_matrix_t m;
+
+       dev_clip = _cairo_clip_copy_with_translation (clip, -x, -y);
+
+       status = _cairo_path_fixed_init_copy (&path_copy, dev_path);
+       if (unlikely (status))
+           goto FINISH;
+
+       _cairo_path_fixed_translate (&path_copy,
+                                    _cairo_fixed_from_int (-x),
+                                    _cairo_fixed_from_int (-y));
+       dev_path = &path_copy;
+
+       cairo_matrix_init_translate (&m, -x, -y);
+       cairo_matrix_multiply (&dev_ctm, &dev_ctm, &m);
+
+       cairo_matrix_init_translate (&m, x, y);
+       _copy_transformed_pattern (&source_copy.base, source, &m);
+       source = &source_copy.base;
+       cairo_matrix_multiply (&dev_ctm_inverse, &m, &dev_ctm_inverse);
+    }
+
+    status = _cairo_surface_stroke (surface, op, source,
+                                   dev_path, stroke_style,
+                                   &dev_ctm, &dev_ctm_inverse,
+                                   tolerance, antialias,
+                                   dev_clip);
+
+FINISH:
+    if (dev_path != path)
+       _cairo_path_fixed_fini (dev_path);
+    if (dev_clip != clip)
+       _cairo_clip_destroy (dev_clip);
+
+    return status;
+}
+
+cairo_status_t
+_cairo_surface_offset_fill (cairo_surface_t    *surface,
+                           int x, int y,
+                           cairo_operator_t     op,
+                           const cairo_pattern_t*source,
+                           const cairo_path_fixed_t    *path,
+                           cairo_fill_rule_t    fill_rule,
+                           double               tolerance,
+                           cairo_antialias_t    antialias,
+                           const cairo_clip_t  *clip)
+{
+    cairo_status_t status;
+    cairo_path_fixed_t path_copy, *dev_path = (cairo_path_fixed_t *) path;
+    cairo_clip_t *dev_clip = (cairo_clip_t *) clip;
+    cairo_pattern_union_t source_copy;
+
+    if (unlikely (surface->status))
+       return surface->status;
+
+    if (_cairo_clip_is_all_clipped (clip))
+       return CAIRO_STATUS_SUCCESS;
+
+    if (x | y) {
+       cairo_matrix_t m;
+
+       dev_clip = _cairo_clip_copy_with_translation (clip, -x, -y);
+
+       status = _cairo_path_fixed_init_copy (&path_copy, dev_path);
+       if (unlikely (status))
+           goto FINISH;
+
+       _cairo_path_fixed_translate (&path_copy,
+                                    _cairo_fixed_from_int (-x),
+                                    _cairo_fixed_from_int (-y));
+       dev_path = &path_copy;
+
+       cairo_matrix_init_translate (&m, x, y);
+       _copy_transformed_pattern (&source_copy.base, source, &m);
+       source = &source_copy.base;
+    }
+
+    status = _cairo_surface_fill (surface, op, source,
+                                 dev_path, fill_rule,
+                                 tolerance, antialias,
+                                 dev_clip);
+
+FINISH:
+    if (dev_path != path)
+       _cairo_path_fixed_fini (dev_path);
+    if (dev_clip != clip)
+       _cairo_clip_destroy (dev_clip);
+
+    return status;
+}
+
+cairo_status_t
+_cairo_surface_offset_glyphs (cairo_surface_t          *surface,
+                             int x, int y,
+                             cairo_operator_t           op,
+                             const cairo_pattern_t     *source,
+                             cairo_scaled_font_t       *scaled_font,
+                             cairo_glyph_t             *glyphs,
+                             int                        num_glyphs,
+                             const cairo_clip_t        *clip)
+{
+    cairo_status_t status;
+    cairo_clip_t *dev_clip = (cairo_clip_t *) clip;
+    cairo_pattern_union_t source_copy;
+    cairo_glyph_t *dev_glyphs;
+    int i;
+
+    if (unlikely (surface->status))
+       return surface->status;
+
+    if (_cairo_clip_is_all_clipped (clip))
+       return CAIRO_STATUS_SUCCESS;
+
+    dev_glyphs = _cairo_malloc_ab (num_glyphs, sizeof (cairo_glyph_t));
+    if (dev_glyphs == NULL)
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    memcpy (dev_glyphs, glyphs, sizeof (cairo_glyph_t) * num_glyphs);
+
+    if (x | y) {
+       cairo_matrix_t m;
+
+       dev_clip = _cairo_clip_copy_with_translation (clip, -x, -y);
+
+       cairo_matrix_init_translate (&m, x, y);
+       _copy_transformed_pattern (&source_copy.base, source, &m);
+       source = &source_copy.base;
+
+       for (i = 0; i < num_glyphs; i++) {
+           dev_glyphs[i].x -= x;
+           dev_glyphs[i].y -= y;
+       }
+    }
+
+    status = _cairo_surface_show_text_glyphs (surface, op, source,
+                                             NULL, 0,
+                                             dev_glyphs, num_glyphs,
+                                             NULL, 0, 0,
+                                             scaled_font,
+                                             dev_clip);
+
+    if (dev_clip != clip)
+       _cairo_clip_destroy (dev_clip);
+    free (dev_glyphs);
+
+    return status;
+}
diff --git a/src/cairo-surface-private.h b/src/cairo-surface-private.h
new file mode 100644 (file)
index 0000000..5a66480
--- /dev/null
@@ -0,0 +1,108 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *     Carl D. Worth <cworth@cworth.org>
+ */
+
+#ifndef CAIRO_SURFACE_PRIVATE_H
+#define CAIRO_SURFACE_PRIVATE_H
+
+#include "cairo.h"
+
+#include "cairo-types-private.h"
+#include "cairo-list-private.h"
+#include "cairo-reference-count-private.h"
+#include "cairo-clip-private.h"
+
+typedef void (*cairo_surface_func_t) (cairo_surface_t *);
+
+struct _cairo_surface {
+    const cairo_surface_backend_t *backend;
+    cairo_device_t *device;
+
+    /* We allow surfaces to override the backend->type by shoving something
+     * else into surface->type. This is for "wrapper" surfaces that want to
+     * hide their internal type from the user-level API. */
+    cairo_surface_type_t type;
+
+    cairo_content_t content;
+
+    cairo_reference_count_t ref_count;
+    cairo_status_t status;
+    unsigned int unique_id;
+    unsigned int serial;
+
+    unsigned finished : 1;
+    unsigned is_clear : 1;
+    unsigned has_font_options : 1;
+    unsigned owns_device : 1;
+
+    cairo_user_data_array_t user_data;
+    cairo_user_data_array_t mime_data;
+
+    cairo_matrix_t device_transform;
+    cairo_matrix_t device_transform_inverse;
+    cairo_list_t device_transform_observers;
+
+    /* The actual resolution of the device, in dots per inch. */
+    double x_resolution;
+    double y_resolution;
+
+    /* The resolution that should be used when generating image-based
+     * fallback; generally only used by the analysis/paginated
+     * surfaces
+     */
+    double x_fallback_resolution;
+    double y_fallback_resolution;
+
+    /* A "snapshot" surface is immutable. See _cairo_surface_snapshot. */
+    cairo_surface_t *snapshot_of;
+    cairo_surface_func_t snapshot_detach;
+    /* current snapshots of this surface*/
+    cairo_list_t snapshots;
+    /* place upon snapshot list */
+    cairo_list_t snapshot;
+
+    /*
+     * Surface font options, falling back to backend's default options,
+     * and set using _cairo_surface_set_font_options(), and propagated by
+     * cairo_surface_create_similar().
+     */
+    cairo_font_options_t font_options;
+};
+
+cairo_private cairo_surface_t *
+_cairo_int_surface_create_in_error (cairo_int_status_t status);
+
+#endif /* CAIRO_SURFACE_PRIVATE_H */
diff --git a/src/cairo-surface-snapshot-private.h b/src/cairo-surface-snapshot-private.h
new file mode 100644 (file)
index 0000000..b7a4d05
--- /dev/null
@@ -0,0 +1,67 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Intel Corporation.
+ *
+ * Contributor(s):
+ *      Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#ifndef CAIRO_SURFACE_SNAPSHOT_PRIVATE_H
+#define CAIRO_SURFACE_SNAPSHOT_PRIVATE_H
+
+#include "cairo-surface-private.h"
+#include "cairo-surface-backend-private.h"
+
+struct _cairo_surface_snapshot {
+    cairo_surface_t base;
+
+    cairo_surface_t *target;
+    cairo_surface_t *clone;
+};
+
+static inline cairo_bool_t
+_cairo_surface_snapshot_is_reused (cairo_surface_t *surface)
+{
+    return CAIRO_REFERENCE_COUNT_GET_VALUE (&surface->ref_count) > 2;
+}
+
+static inline cairo_surface_t *
+_cairo_surface_snapshot_get_target (cairo_surface_t *surface)
+{
+    return ((cairo_surface_snapshot_t *) surface)->target;
+}
+
+static inline cairo_bool_t
+_cairo_surface_is_snapshot (cairo_surface_t *surface)
+{
+    return surface->backend->type == (cairo_surface_type_t)CAIRO_INTERNAL_SURFACE_TYPE_SNAPSHOT;
+}
+
+#endif /* CAIRO_SURFACE_SNAPSHOT_PRIVATE_H */
diff --git a/src/cairo-surface-snapshot.c b/src/cairo-surface-snapshot.c
new file mode 100644 (file)
index 0000000..4bc40e7
--- /dev/null
@@ -0,0 +1,234 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ * Copyright © 2005 Red Hat, Inc.
+ * Copyright © 2009 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *     Carl D. Worth <cworth@cworth.org>
+ *      Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-error-private.h"
+#include "cairo-image-surface-private.h"
+#include "cairo-surface-snapshot-private.h"
+
+static cairo_status_t
+_cairo_surface_snapshot_finish (void *abstract_surface)
+{
+    cairo_surface_snapshot_t *surface = abstract_surface;
+    cairo_status_t status = CAIRO_STATUS_SUCCESS;
+
+    if (surface->clone != NULL) {
+       cairo_surface_finish (surface->clone);
+       status = surface->clone->status;
+
+       cairo_surface_destroy (surface->clone);
+    }
+
+    return status;
+}
+
+static cairo_status_t
+_cairo_surface_snapshot_flush (void *abstract_surface)
+{
+    cairo_surface_snapshot_t *surface = abstract_surface;
+
+    cairo_surface_flush (surface->target);
+    return surface->target->status;
+}
+
+static cairo_status_t
+_cairo_surface_snapshot_acquire_source_image (void                    *abstract_surface,
+                                             cairo_image_surface_t  **image_out,
+                                             void                   **extra_out)
+{
+    cairo_surface_snapshot_t *surface = abstract_surface;
+
+    return _cairo_surface_acquire_source_image (surface->target, image_out, extra_out);
+}
+
+static void
+_cairo_surface_snapshot_release_source_image (void                   *abstract_surface,
+                                             cairo_image_surface_t  *image,
+                                             void                   *extra)
+{
+    cairo_surface_snapshot_t *surface = abstract_surface;
+
+    _cairo_surface_release_source_image (surface->target, image, extra);
+}
+
+static cairo_bool_t
+_cairo_surface_snapshot_get_extents (void                  *abstract_surface,
+                                    cairo_rectangle_int_t *extents)
+{
+    cairo_surface_snapshot_t *surface = abstract_surface;
+
+    return _cairo_surface_get_extents (surface->target, extents);
+}
+
+static const cairo_surface_backend_t _cairo_surface_snapshot_backend = {
+    CAIRO_INTERNAL_SURFACE_TYPE_SNAPSHOT,
+    _cairo_surface_snapshot_finish,
+    NULL,
+
+    NULL, /* create similar */
+    NULL, /* create similar image  */
+    NULL, /* map to image */
+    NULL, /* unmap image  */
+
+    _cairo_surface_snapshot_acquire_source_image,
+    _cairo_surface_snapshot_release_source_image,
+    NULL, /* snapshot */
+
+    NULL, /* copy_page */
+    NULL, /* show_page */
+
+    _cairo_surface_snapshot_get_extents,
+    NULL, /* get-font-options */
+
+    _cairo_surface_snapshot_flush,
+};
+
+static void
+_cairo_surface_snapshot_copy_on_write (cairo_surface_t *surface)
+{
+    cairo_surface_snapshot_t *snapshot = (cairo_surface_snapshot_t *) surface;
+    cairo_image_surface_t *image;
+    cairo_surface_t *clone;
+    void *extra;
+    cairo_status_t status;
+
+    /* We need to make an image copy of the original surface since the
+     * snapshot may exceed the lifetime of the original device, i.e.
+     * when we later need to use the snapshot the data may have already
+     * been lost.
+     */
+
+    if (snapshot->target->backend->snapshot != NULL) {
+       clone = snapshot->target->backend->snapshot (snapshot->target);
+       if (clone != NULL) {
+           assert (clone->status || ! _cairo_surface_is_snapshot (clone));
+           goto done;
+       }
+    }
+
+    /* XXX copy to a similar surface, leave acquisition till later?
+     * We should probably leave such decisions to the backend in case we
+     * rely upon devices/connections like Xlib.
+    */
+    status = _cairo_surface_acquire_source_image (snapshot->target, &image, &extra);
+    if (unlikely (status)) {
+       snapshot->target = _cairo_surface_create_in_error (status);
+       status = _cairo_surface_set_error (surface, status);
+       return;
+    }
+    clone = image->base.backend->snapshot (&image->base);
+    _cairo_surface_release_source_image (snapshot->target, image, extra);
+
+done:
+    status = _cairo_surface_set_error (surface, clone->status);
+    snapshot->target = snapshot->clone = clone;
+    snapshot->base.type = clone->type;
+}
+
+/**
+ * _cairo_surface_snapshot
+ * @surface: a #cairo_surface_t
+ *
+ * Make an immutable reference to @surface. It is an error to call a
+ * surface-modifying function on the result of this function. The
+ * resulting 'snapshot' is a lazily copied-on-write surface i.e. it
+ * remains a reference to the original surface until that surface is
+ * written to again, at which time a copy is made of the original surface
+ * and the snapshot then points to that instead. Multiple snapshots of the
+ * same unmodified surface point to the same copy.
+ *
+ * The caller owns the return value and should call
+ * cairo_surface_destroy() when finished with it. This function will not
+ * return %NULL, but will return a nil surface instead.
+ *
+ * Return value: The snapshot surface. Note that the return surface
+ * may not necessarily be of the same type as @surface.
+ **/
+cairo_surface_t *
+_cairo_surface_snapshot (cairo_surface_t *surface)
+{
+    cairo_surface_snapshot_t *snapshot;
+    cairo_status_t status;
+
+    if (unlikely (surface->status))
+       return _cairo_surface_create_in_error (surface->status);
+
+    if (unlikely (surface->finished))
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
+
+    if (surface->snapshot_of != NULL)
+       return cairo_surface_reference (surface);
+
+    if (_cairo_surface_is_snapshot (surface))
+       return cairo_surface_reference (surface);
+
+    snapshot = (cairo_surface_snapshot_t *)
+       _cairo_surface_has_snapshot (surface, &_cairo_surface_snapshot_backend);
+    if (snapshot != NULL)
+       return cairo_surface_reference (&snapshot->base);
+
+    snapshot = malloc (sizeof (cairo_surface_snapshot_t));
+    if (unlikely (snapshot == NULL))
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
+
+    _cairo_surface_init (&snapshot->base,
+                        &_cairo_surface_snapshot_backend,
+                        NULL, /* device */
+                        surface->content);
+    snapshot->base.type = surface->type;
+
+    snapshot->target = surface;
+    snapshot->clone = NULL;
+
+    status = _cairo_surface_copy_mime_data (&snapshot->base, surface);
+    if (unlikely (status)) {
+       cairo_surface_destroy (&snapshot->base);
+       return _cairo_surface_create_in_error (status);
+    }
+
+    snapshot->base.device_transform = surface->device_transform;
+    snapshot->base.device_transform_inverse = surface->device_transform_inverse;
+
+    _cairo_surface_attach_snapshot (surface,
+                                   &snapshot->base,
+                                   _cairo_surface_snapshot_copy_on_write);
+
+    return &snapshot->base;
+}
diff --git a/src/cairo-surface-subsurface-private.h b/src/cairo-surface-subsurface-private.h
new file mode 100644 (file)
index 0000000..6a43c8f
--- /dev/null
@@ -0,0 +1,81 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Intel Corporation.
+ *
+ * Contributor(s):
+ *      Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#ifndef CAIRO_SURFACE_SUBSURFACE_PRIVATE_H
+#define CAIRO_SURFACE_SUBSURFACE_PRIVATE_H
+
+#include "cairo-surface-private.h"
+#include "cairo-surface-backend-private.h"
+
+struct _cairo_surface_subsurface {
+    cairo_surface_t base;
+
+    cairo_rectangle_int_t extents;
+
+    cairo_surface_t *target;
+};
+
+static inline cairo_surface_t *
+_cairo_surface_subsurface_get_target (cairo_surface_t *surface)
+{
+    return ((cairo_surface_subsurface_t *) surface)->target;
+}
+
+static inline void
+_cairo_surface_subsurface_offset (cairo_surface_t *surface,
+                                 int *x, int *y)
+{
+    cairo_surface_subsurface_t *ss = (cairo_surface_subsurface_t *) surface;
+    *x += ss->extents.x;
+    *y += ss->extents.y;
+}
+
+static inline cairo_surface_t *
+_cairo_surface_subsurface_get_target_with_offset (cairo_surface_t *surface,
+                                                 int *x, int *y)
+{
+    cairo_surface_subsurface_t *ss = (cairo_surface_subsurface_t *) surface;
+    *x += ss->extents.x;
+    *y += ss->extents.y;
+    return ss->target;
+}
+
+static inline cairo_bool_t
+_cairo_surface_is_subsurface (cairo_surface_t *surface)
+{
+    return surface->backend->type == CAIRO_SURFACE_TYPE_SUBSURFACE;
+}
+
+#endif /* CAIRO_SURFACE_SUBSURFACE_PRIVATE_H */
diff --git a/src/cairo-surface-subsurface.c b/src/cairo-surface-subsurface.c
new file mode 100644 (file)
index 0000000..9ed483c
--- /dev/null
@@ -0,0 +1,615 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Intel Corporation.
+ *
+ * Contributor(s):
+ *      Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-error-private.h"
+#include "cairo-image-surface-private.h"
+#include "cairo-recording-surface-private.h"
+#include "cairo-surface-offset-private.h"
+#include "cairo-surface-snapshot-private.h"
+#include "cairo-surface-subsurface-private.h"
+
+static const cairo_surface_backend_t _cairo_surface_subsurface_backend;
+
+static cairo_status_t
+_cairo_surface_subsurface_finish (void *abstract_surface)
+{
+    cairo_surface_subsurface_t *surface = abstract_surface;
+
+    cairo_surface_destroy (surface->target);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_surface_t *
+_cairo_surface_subsurface_create_similar (void *other,
+                                         cairo_content_t content,
+                                         int width, int height)
+{
+    cairo_surface_subsurface_t *surface = other;
+    return surface->target->backend->create_similar (surface->target, content, width, height);
+}
+
+static cairo_surface_t *
+_cairo_surface_subsurface_create_similar_image (void *other,
+                                               cairo_format_t format,
+                                               int width, int height)
+{
+    cairo_surface_subsurface_t *surface = other;
+    return surface->target->backend->create_similar_image (surface->target,
+                                                          format,
+                                                          width, height);
+}
+
+static cairo_surface_t *
+_cairo_surface_subsurface_map_to_image (void *abstract_surface,
+                                       const cairo_rectangle_int_t *extents)
+{
+    cairo_surface_subsurface_t *surface = abstract_surface;
+    cairo_rectangle_int_t target_extents;
+
+    if (surface->target->backend->map_to_image == NULL)
+       return NULL;
+
+    target_extents.x = extents->x + surface->extents.x;
+    target_extents.y = extents->y + surface->extents.y;
+    target_extents.width  = extents->width;
+    target_extents.height = extents->height;
+
+    return surface->target->backend->map_to_image (surface->target,
+                                                  &target_extents);
+}
+
+static cairo_int_status_t
+_cairo_surface_subsurface_unmap_image (void *abstract_surface,
+                                      cairo_image_surface_t *image)
+{
+    cairo_surface_subsurface_t *surface = abstract_surface;
+
+    if (surface->target->backend->unmap_image == NULL)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    return surface->target->backend->unmap_image (surface->target, image);
+}
+
+static cairo_int_status_t
+_cairo_surface_subsurface_paint (void *abstract_surface,
+                                cairo_operator_t op,
+                                const cairo_pattern_t *source,
+                                const cairo_clip_t *clip)
+{
+    cairo_surface_subsurface_t *surface = abstract_surface;
+    cairo_rectangle_int_t rect = { 0, 0, surface->extents.width, surface->extents.height };
+    cairo_status_t status;
+    cairo_clip_t *target_clip;
+
+    target_clip = _cairo_clip_copy_intersect_rectangle (clip, &rect);
+    status = _cairo_surface_offset_paint (surface->target,
+                                        -surface->extents.x, -surface->extents.y,
+                                         op, source, target_clip);
+    _cairo_clip_destroy (target_clip);
+    return status;
+}
+
+static cairo_int_status_t
+_cairo_surface_subsurface_mask (void *abstract_surface,
+                               cairo_operator_t op,
+                               const cairo_pattern_t *source,
+                               const cairo_pattern_t *mask,
+                               const cairo_clip_t *clip)
+{
+    cairo_surface_subsurface_t *surface = abstract_surface;
+    cairo_rectangle_int_t rect = { 0, 0, surface->extents.width, surface->extents.height };
+    cairo_status_t status;
+    cairo_clip_t *target_clip;
+
+    target_clip = _cairo_clip_copy_intersect_rectangle (clip, &rect);
+    status = _cairo_surface_offset_mask (surface->target,
+                                        -surface->extents.x, -surface->extents.y,
+                                        op, source, mask, target_clip);
+    _cairo_clip_destroy (target_clip);
+    return status;
+}
+
+static cairo_int_status_t
+_cairo_surface_subsurface_fill (void                   *abstract_surface,
+                               cairo_operator_t         op,
+                               const cairo_pattern_t   *source,
+                               const cairo_path_fixed_t        *path,
+                               cairo_fill_rule_t        fill_rule,
+                               double                   tolerance,
+                               cairo_antialias_t        antialias,
+                               const cairo_clip_t              *clip)
+{
+    cairo_surface_subsurface_t *surface = abstract_surface;
+    cairo_rectangle_int_t rect = { 0, 0, surface->extents.width, surface->extents.height };
+    cairo_status_t status;
+    cairo_clip_t *target_clip;
+
+    target_clip = _cairo_clip_copy_intersect_rectangle (clip, &rect);
+    status = _cairo_surface_offset_fill (surface->target,
+                                        -surface->extents.x, -surface->extents.y,
+                                        op, source, path, fill_rule, tolerance, antialias,
+                                        target_clip);
+    _cairo_clip_destroy (target_clip);
+    return status;
+}
+
+static cairo_int_status_t
+_cairo_surface_subsurface_stroke (void                         *abstract_surface,
+                                 cairo_operator_t               op,
+                                 const cairo_pattern_t         *source,
+                                 const cairo_path_fixed_t              *path,
+                                 const cairo_stroke_style_t    *stroke_style,
+                                 const cairo_matrix_t          *ctm,
+                                 const cairo_matrix_t          *ctm_inverse,
+                                 double                         tolerance,
+                                 cairo_antialias_t              antialias,
+                                 const cairo_clip_t                    *clip)
+{
+    cairo_surface_subsurface_t *surface = abstract_surface;
+    cairo_rectangle_int_t rect = { 0, 0, surface->extents.width, surface->extents.height };
+    cairo_status_t status;
+    cairo_clip_t *target_clip;
+
+    target_clip = _cairo_clip_copy_intersect_rectangle (clip, &rect);
+    status = _cairo_surface_offset_stroke (surface->target,
+                                          -surface->extents.x, -surface->extents.y,
+                                          op, source, path, stroke_style, ctm, ctm_inverse,
+                                          tolerance, antialias,
+                                          target_clip);
+    _cairo_clip_destroy (target_clip);
+    return status;
+}
+
+static cairo_int_status_t
+_cairo_surface_subsurface_glyphs (void                 *abstract_surface,
+                                 cairo_operator_t       op,
+                                 const cairo_pattern_t *source,
+                                 cairo_glyph_t         *glyphs,
+                                 int                    num_glyphs,
+                                 cairo_scaled_font_t   *scaled_font,
+                                 const cairo_clip_t    *clip)
+{
+    cairo_surface_subsurface_t *surface = abstract_surface;
+    cairo_rectangle_int_t rect = { 0, 0, surface->extents.width, surface->extents.height };
+    cairo_status_t status;
+    cairo_clip_t *target_clip;
+
+    target_clip = _cairo_clip_copy_intersect_rectangle (clip, &rect);
+    status = _cairo_surface_offset_glyphs (surface->target,
+                                          -surface->extents.x, -surface->extents.y,
+                                          op, source,
+                                          scaled_font, glyphs, num_glyphs,
+                                          target_clip);
+    _cairo_clip_destroy (target_clip);
+    return status;
+}
+
+static cairo_status_t
+_cairo_surface_subsurface_flush (void *abstract_surface)
+{
+    cairo_surface_subsurface_t *surface = abstract_surface;
+
+    cairo_surface_flush (surface->target);
+    return surface->target->status;
+}
+
+static cairo_status_t
+_cairo_surface_subsurface_mark_dirty (void *abstract_surface,
+                                     int x, int y,
+                                     int width, int height)
+{
+    cairo_surface_subsurface_t *surface = abstract_surface;
+    cairo_status_t status;
+
+    status = CAIRO_STATUS_SUCCESS;
+    if (surface->target->backend->mark_dirty_rectangle != NULL) {
+       cairo_rectangle_int_t rect, extents;
+
+       rect.x = x;
+       rect.y = y;
+       rect.width  = width;
+       rect.height = height;
+
+       extents.x = extents.y = 0;
+       extents.width  = surface->extents.width;
+       extents.height = surface->extents.height;
+
+       if (_cairo_rectangle_intersect (&rect, &extents)) {
+           status = surface->target->backend->mark_dirty_rectangle (surface->target,
+                                                                    rect.x + surface->extents.x,
+                                                                    rect.y + surface->extents.y,
+                                                                    rect.width, rect.height);
+       }
+    }
+
+    return status;
+}
+
+static cairo_bool_t
+_cairo_surface_subsurface_get_extents (void *abstract_surface,
+                                      cairo_rectangle_int_t *extents)
+{
+    cairo_surface_subsurface_t *surface = abstract_surface;
+
+    extents->x = 0;
+    extents->y = 0;
+    extents->width  = surface->extents.width;
+    extents->height = surface->extents.height;
+
+    return TRUE;
+}
+
+static void
+_cairo_surface_subsurface_get_font_options (void *abstract_surface,
+                                           cairo_font_options_t *options)
+{
+    cairo_surface_subsurface_t *surface = abstract_surface;
+
+    if (surface->target->backend->get_font_options != NULL)
+       surface->target->backend->get_font_options (surface->target, options);
+}
+
+struct extra {
+    cairo_image_surface_t *image;
+    void *image_extra;
+};
+
+static cairo_status_t
+_cairo_surface_subsurface_acquire_source_image (void                    *abstract_surface,
+                                               cairo_image_surface_t  **image_out,
+                                               void                   **extra_out)
+{
+    cairo_rectangle_int_t target_extents;
+    cairo_surface_subsurface_t *surface = abstract_surface;
+    cairo_image_surface_t *image;
+    cairo_status_t status;
+    struct extra *extra;
+    uint8_t *data;
+    cairo_bool_t ret;
+
+    if (surface->target->type == CAIRO_SURFACE_TYPE_RECORDING) {
+       cairo_surface_t *meta, *snapshot;
+
+       snapshot = _cairo_surface_has_snapshot (&surface->base,
+                                               &_cairo_image_surface_backend);
+       if (snapshot != NULL) {
+           *image_out = (cairo_image_surface_t *) cairo_surface_reference (snapshot);
+           *extra_out = NULL;
+           return CAIRO_STATUS_SUCCESS;
+       }
+
+       meta = surface->target;
+       if (_cairo_surface_is_snapshot (meta))
+           meta = _cairo_surface_snapshot_get_target (meta);
+
+       if (! _cairo_surface_has_snapshot (meta, &_cairo_image_surface_backend)) {
+           cairo_surface_pattern_t pattern;
+
+           image = (cairo_image_surface_t *)
+               _cairo_image_surface_create_with_content (meta->content,
+                                                         surface->extents.width,
+                                                         surface->extents.height);
+           if (unlikely (image->base.status))
+               return image->base.status;
+
+           _cairo_pattern_init_for_surface (&pattern, &image->base);
+           cairo_matrix_init_translate (&pattern.base.matrix,
+                                        -surface->extents.x, -surface->extents.y);
+           pattern.base.filter = CAIRO_FILTER_NEAREST;
+           status = _cairo_surface_paint (&image->base,
+                                          CAIRO_OPERATOR_SOURCE,
+                                          &pattern.base, NULL);
+           _cairo_pattern_fini (&pattern.base);
+           if (unlikely (status)) {
+               cairo_surface_destroy (&image->base);
+               return status;
+           }
+
+           _cairo_surface_attach_snapshot (&surface->base, &image->base, NULL);
+
+           *image_out = image;
+           *extra_out = NULL;
+           return CAIRO_STATUS_SUCCESS;
+       }
+    }
+
+    extra = malloc (sizeof (struct extra));
+    if (unlikely (extra == NULL))
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    status = _cairo_surface_acquire_source_image (surface->target, &extra->image, &extra->image_extra);
+    if (unlikely (status))
+       goto CLEANUP;
+
+    ret = _cairo_surface_get_extents (&extra->image->base, &target_extents);
+    assert (ret);
+
+    /* only copy if we need to perform sub-byte manipulation */
+    if (PIXMAN_FORMAT_BPP (extra->image->pixman_format) >= 8 &&
+       target_extents.x <= surface->extents.x &&
+       target_extents.y <= surface->extents.y &&
+       surface->extents.x + surface->extents.width <= target_extents.x + target_extents.width &&
+       surface->extents.y + surface->extents.height <= target_extents.y + target_extents.height) {
+
+       assert ((PIXMAN_FORMAT_BPP (extra->image->pixman_format) % 8) == 0);
+
+       data = extra->image->data + surface->extents.y * extra->image->stride;
+       data += PIXMAN_FORMAT_BPP (extra->image->pixman_format) / 8 * surface->extents.x;
+
+       image = (cairo_image_surface_t *)
+           _cairo_image_surface_create_with_pixman_format (data,
+                                                           extra->image->pixman_format,
+                                                           surface->extents.width,
+                                                           surface->extents.height,
+                                                           extra->image->stride);
+       if (unlikely ((status = image->base.status)))
+           goto CLEANUP_IMAGE;
+
+        image->base.is_clear = FALSE;
+    } else {
+       cairo_surface_pattern_t pattern;
+
+       image = (cairo_image_surface_t *)
+           _cairo_image_surface_create_with_pixman_format (NULL,
+                                                           extra->image->pixman_format,
+                                                           surface->extents.width,
+                                                           surface->extents.height,
+                                                           0);
+       if (unlikely ((status = image->base.status)))
+           goto CLEANUP_IMAGE;
+
+       _cairo_pattern_init_for_surface (&pattern, &image->base);
+       cairo_matrix_init_translate (&pattern.base.matrix,
+                                    -surface->extents.x, -surface->extents.y);
+       pattern.base.filter = CAIRO_FILTER_NEAREST;
+       status = _cairo_surface_paint (&image->base,
+                                      CAIRO_OPERATOR_SOURCE,
+                                      &pattern.base, NULL);
+       _cairo_pattern_fini (&pattern.base);
+       if (unlikely (status)) {
+           cairo_surface_destroy (&image->base);
+           return status;
+       }
+    }
+
+    *image_out = image;
+    *extra_out = extra;
+    return CAIRO_STATUS_SUCCESS;
+
+CLEANUP_IMAGE:
+    _cairo_surface_release_source_image (surface->target, extra->image, extra->image_extra);
+CLEANUP:
+    free (extra);
+    return status;
+}
+
+static void
+_cairo_surface_subsurface_release_source_image (void                   *abstract_surface,
+                                               cairo_image_surface_t  *image,
+                                               void                   *abstract_extra)
+{
+    cairo_surface_subsurface_t *surface = abstract_surface;
+
+    if (abstract_extra != NULL) {
+       struct extra *extra = abstract_extra;
+
+       _cairo_surface_release_source_image (surface->target, extra->image, extra->image_extra);
+       free (extra);
+    }
+
+    cairo_surface_destroy (&image->base);
+}
+
+static cairo_surface_t *
+_cairo_surface_subsurface_snapshot (void *abstract_surface)
+{
+    cairo_surface_subsurface_t *surface = abstract_surface;
+    cairo_surface_pattern_t pattern;
+    cairo_surface_t *clone;
+    cairo_status_t status;
+
+    clone = _cairo_surface_create_similar_scratch (surface->target,
+                                                  surface->target->content,
+                                                  surface->extents.width,
+                                                  surface->extents.height);
+    if (unlikely (clone->status))
+       return clone;
+
+    _cairo_pattern_init_for_surface (&pattern, surface->target);
+    cairo_matrix_init_translate (&pattern.base.matrix,
+                                surface->extents.x, surface->extents.y);
+    pattern.base.filter = CAIRO_FILTER_NEAREST;
+    status = _cairo_surface_paint (clone,
+                                  CAIRO_OPERATOR_SOURCE,
+                                  &pattern.base, NULL);
+    _cairo_pattern_fini (&pattern.base);
+
+    if (unlikely (status)) {
+       cairo_surface_destroy (clone);
+       clone = _cairo_surface_create_in_error (status);
+    }
+
+    return clone;
+}
+
+static cairo_t *
+_cairo_surface_subsurface_create_context(void *target)
+{
+    cairo_surface_subsurface_t *surface = target;
+    return surface->target->backend->create_context (&surface->base);
+}
+
+static const cairo_surface_backend_t _cairo_surface_subsurface_backend = {
+    CAIRO_SURFACE_TYPE_SUBSURFACE,
+    _cairo_surface_subsurface_finish,
+
+    _cairo_surface_subsurface_create_context,
+
+    _cairo_surface_subsurface_create_similar,
+    _cairo_surface_subsurface_create_similar_image,
+    _cairo_surface_subsurface_map_to_image,
+    _cairo_surface_subsurface_unmap_image,
+
+    _cairo_surface_subsurface_acquire_source_image,
+    _cairo_surface_subsurface_release_source_image,
+    _cairo_surface_subsurface_snapshot,
+
+    NULL, /* copy_page */
+    NULL, /* show_page */
+
+    _cairo_surface_subsurface_get_extents,
+    _cairo_surface_subsurface_get_font_options,
+
+    _cairo_surface_subsurface_flush,
+    _cairo_surface_subsurface_mark_dirty,
+
+    _cairo_surface_subsurface_paint,
+    _cairo_surface_subsurface_mask,
+    _cairo_surface_subsurface_stroke,
+    _cairo_surface_subsurface_fill,
+    NULL, /* fill/stroke */
+    _cairo_surface_subsurface_glyphs,
+};
+
+/**
+ * cairo_surface_create_for_rectangle:
+ * @target: an existing surface for which the sub-surface will point to
+ * @x: the x-origin of the sub-surface from the top-left of the target surface (in device-space units)
+ * @y: the y-origin of the sub-surface from the top-left of the target surface (in device-space units)
+ * @width: width of the sub-surface (in device-space units)
+ * @height: height of the sub-surface (in device-space units)
+ *
+ * Create a new surface that is a rectangle within the target surface.
+ * All operations drawn to this surface are then clipped and translated
+ * onto the target surface. Nothing drawn via this sub-surface outside of
+ * its bounds is drawn onto the target surface, making this a useful method
+ * for passing constrained child surfaces to library routines that draw
+ * directly onto the parent surface, i.e. with no further backend allocations,
+ * double buffering or copies.
+ *
+ * <note><para>The semantics of subsurfaces have not been finalized yet
+ * unless the rectangle is in full device units, is contained within
+ * the extents of the target surface, and the target or subsurface's
+ * device transforms are not changed.</para></note>
+ *
+ * Return value: a pointer to the newly allocated surface. The caller
+ * owns the surface and should call cairo_surface_destroy() when done
+ * with it.
+ *
+ * This function always returns a valid pointer, but it will return a
+ * pointer to a "nil" surface if @other is already in an error state
+ * or any other error occurs.
+ *
+ * Since: 1.10
+ **/
+cairo_surface_t *
+cairo_surface_create_for_rectangle (cairo_surface_t *target,
+                                   double x, double y,
+                                   double width, double height)
+{
+    cairo_surface_subsurface_t *surface;
+
+    if (unlikely (target->status))
+       return _cairo_surface_create_in_error (target->status);
+    if (unlikely (target->finished))
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
+
+    surface = malloc (sizeof (cairo_surface_subsurface_t));
+    if (unlikely (surface == NULL))
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+    assert (_cairo_matrix_is_translation (&target->device_transform));
+    x += target->device_transform.x0;
+    y += target->device_transform.y0;
+
+    _cairo_surface_init (&surface->base,
+                        &_cairo_surface_subsurface_backend,
+                        NULL, /* device */
+                        target->content);
+
+    /* XXX forced integer alignment */
+    surface->extents.x = ceil (x);
+    surface->extents.y = ceil (y);
+    surface->extents.width = floor (x + width) - surface->extents.x;
+    surface->extents.height = floor (y + height) - surface->extents.y;
+
+    if (target->backend->type == CAIRO_SURFACE_TYPE_SUBSURFACE) {
+       /* Maintain subsurfaces as 1-depth */
+       cairo_surface_subsurface_t *sub = (cairo_surface_subsurface_t *) target;
+       surface->extents.x += sub->extents.x;
+       surface->extents.y += sub->extents.y;
+       target = sub->target;
+    }
+
+    surface->target = cairo_surface_reference (target);
+
+    return &surface->base;
+}
+
+cairo_surface_t *
+_cairo_surface_create_for_rectangle_int (cairo_surface_t *target,
+                                        const cairo_rectangle_int_t *extents)
+{
+    cairo_surface_subsurface_t *surface;
+
+    if (unlikely (target->status))
+       return _cairo_surface_create_in_error (target->status);
+    if (unlikely (target->finished))
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
+
+    assert (target->backend->type != CAIRO_SURFACE_TYPE_SUBSURFACE);
+
+    surface = malloc (sizeof (cairo_surface_subsurface_t));
+    if (unlikely (surface == NULL))
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+    assert (_cairo_matrix_is_translation (&target->device_transform));
+
+    _cairo_surface_init (&surface->base,
+                        &_cairo_surface_subsurface_backend,
+                        NULL, /* device */
+                        target->content);
+
+    surface->extents = *extents;
+    surface->extents.x += target->device_transform.x0;
+    surface->extents.y += target->device_transform.y0;
+    surface->target = cairo_surface_reference (target);
+
+    return &surface->base;
+}
+/* XXX observe mark-dirty */
diff --git a/src/cairo-surface-wrapper-private.h b/src/cairo-surface-wrapper-private.h
new file mode 100644 (file)
index 0000000..6529ebc
--- /dev/null
@@ -0,0 +1,193 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ * Copyright © 2005 Red Hat, Inc.
+ * Copyright © 2009 Chris Wilson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *     Chris Wilson <chris@chris-wilson.co.u>
+ */
+
+#ifndef CAIRO_SURFACE_WRAPPER_PRIVATE_H
+#define CAIRO_SURFACE_WRAPPER_PRIVATE_H
+
+#include "cairoint.h"
+#include "cairo-types-private.h"
+#include "cairo-surface-backend-private.h"
+
+CAIRO_BEGIN_DECLS
+
+struct _cairo_surface_wrapper {
+    cairo_surface_t *target;
+
+    cairo_matrix_t transform;
+
+    cairo_bool_t has_extents;
+    cairo_rectangle_int_t extents;
+    const cairo_clip_t *clip;
+
+    cairo_bool_t needs_transform;
+};
+
+cairo_private void
+_cairo_surface_wrapper_init (cairo_surface_wrapper_t *wrapper,
+                            cairo_surface_t *target);
+
+cairo_private void
+_cairo_surface_wrapper_intersect_extents (cairo_surface_wrapper_t *wrapper,
+                                         const cairo_rectangle_int_t *extents);
+
+cairo_private void
+_cairo_surface_wrapper_set_inverse_transform (cairo_surface_wrapper_t *wrapper,
+                                             const cairo_matrix_t *transform);
+
+cairo_private void
+_cairo_surface_wrapper_set_clip (cairo_surface_wrapper_t *wrapper,
+                                const cairo_clip_t *clip);
+
+cairo_private void
+_cairo_surface_wrapper_fini (cairo_surface_wrapper_t *wrapper);
+
+static inline cairo_bool_t
+_cairo_surface_wrapper_has_fill_stroke (cairo_surface_wrapper_t *wrapper)
+{
+    return wrapper->target->backend->fill_stroke != NULL;
+}
+
+cairo_private cairo_status_t
+_cairo_surface_wrapper_acquire_source_image (cairo_surface_wrapper_t *wrapper,
+                                            cairo_image_surface_t  **image_out,
+                                            void                   **image_extra);
+
+cairo_private void
+_cairo_surface_wrapper_release_source_image (cairo_surface_wrapper_t *wrapper,
+                                            cairo_image_surface_t  *image,
+                                            void                   *image_extra);
+
+
+cairo_private cairo_status_t
+_cairo_surface_wrapper_paint (cairo_surface_wrapper_t *wrapper,
+                             cairo_operator_t   op,
+                             const cairo_pattern_t *source,
+                             const cairo_clip_t            *clip);
+
+cairo_private cairo_status_t
+_cairo_surface_wrapper_mask (cairo_surface_wrapper_t *wrapper,
+                            cairo_operator_t    op,
+                            const cairo_pattern_t *source,
+                            const cairo_pattern_t *mask,
+                            const cairo_clip_t     *clip);
+
+cairo_private cairo_status_t
+_cairo_surface_wrapper_stroke (cairo_surface_wrapper_t *wrapper,
+                              cairo_operator_t          op,
+                              const cairo_pattern_t    *source,
+                              const cairo_path_fixed_t *path,
+                              const cairo_stroke_style_t       *stroke_style,
+                              const cairo_matrix_t             *ctm,
+                              const cairo_matrix_t             *ctm_inverse,
+                              double                    tolerance,
+                              cairo_antialias_t         antialias,
+                              const cairo_clip_t               *clip);
+
+cairo_private cairo_status_t
+_cairo_surface_wrapper_fill_stroke (cairo_surface_wrapper_t *wrapper,
+                                   cairo_operator_t         fill_op,
+                                   const cairo_pattern_t   *fill_source,
+                                   cairo_fill_rule_t        fill_rule,
+                                   double                   fill_tolerance,
+                                   cairo_antialias_t        fill_antialias,
+                                   const cairo_path_fixed_t*path,
+                                   cairo_operator_t         stroke_op,
+                                   const cairo_pattern_t   *stroke_source,
+                                   const cairo_stroke_style_t    *stroke_style,
+                                   const cairo_matrix_t            *stroke_ctm,
+                                   const cairo_matrix_t            *stroke_ctm_inverse,
+                                   double                   stroke_tolerance,
+                                   cairo_antialias_t        stroke_antialias,
+                                   const cairo_clip_t      *clip);
+
+cairo_private cairo_status_t
+_cairo_surface_wrapper_fill (cairo_surface_wrapper_t *wrapper,
+                            cairo_operator_t    op,
+                            const cairo_pattern_t *source,
+                            const cairo_path_fixed_t   *path,
+                            cairo_fill_rule_t   fill_rule,
+                            double              tolerance,
+                            cairo_antialias_t   antialias,
+                            const cairo_clip_t *clip);
+
+cairo_private cairo_status_t
+_cairo_surface_wrapper_show_text_glyphs (cairo_surface_wrapper_t *wrapper,
+                                        cairo_operator_t            op,
+                                        const cairo_pattern_t      *source,
+                                        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  cluster_flags,
+                                        cairo_scaled_font_t        *scaled_font,
+                                        const cairo_clip_t         *clip);
+
+cairo_private cairo_surface_t *
+_cairo_surface_wrapper_create_similar (cairo_surface_wrapper_t *wrapper,
+                                      cairo_content_t  content,
+                                      int              width,
+                                      int              height);
+cairo_private cairo_bool_t
+_cairo_surface_wrapper_get_extents (cairo_surface_wrapper_t *wrapper,
+                                   cairo_rectangle_int_t   *extents);
+
+cairo_private void
+_cairo_surface_wrapper_get_font_options (cairo_surface_wrapper_t    *wrapper,
+                                        cairo_font_options_t       *options);
+
+cairo_private cairo_surface_t *
+_cairo_surface_wrapper_snapshot (cairo_surface_wrapper_t *wrapper);
+
+cairo_private cairo_bool_t
+_cairo_surface_wrapper_has_show_text_glyphs (cairo_surface_wrapper_t *wrapper);
+
+static inline cairo_bool_t
+_cairo_surface_wrapper_is_active (cairo_surface_wrapper_t *wrapper)
+{
+    return wrapper->target != (cairo_surface_t *) 0;
+}
+
+cairo_private cairo_bool_t
+_cairo_surface_wrapper_get_target_extents (cairo_surface_wrapper_t *wrapper,
+                                          cairo_rectangle_int_t *extents);
+
+CAIRO_END_DECLS
+
+#endif /* CAIRO_SURFACE_WRAPPER_PRIVATE_H */
diff --git a/src/cairo-surface-wrapper.c b/src/cairo-surface-wrapper.c
new file mode 100644 (file)
index 0000000..00d9aa3
--- /dev/null
@@ -0,0 +1,667 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2005 Red Hat, Inc
+ * Copyright © 2007 Adrian Johnson
+ * Copyright © 2009 Chris Wilson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ *      Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-error-private.h"
+#include "cairo-pattern-private.h"
+#include "cairo-surface-wrapper-private.h"
+
+/* A collection of routines to facilitate surface wrapping */
+
+static void
+_copy_transformed_pattern (cairo_pattern_t *pattern,
+                          const cairo_pattern_t *original,
+                          const cairo_matrix_t  *ctm_inverse)
+{
+    _cairo_pattern_init_static_copy (pattern, original);
+
+    if (! _cairo_matrix_is_identity (ctm_inverse))
+       _cairo_pattern_transform (pattern, ctm_inverse);
+}
+
+cairo_status_t
+_cairo_surface_wrapper_acquire_source_image (cairo_surface_wrapper_t *wrapper,
+                                            cairo_image_surface_t  **image_out,
+                                            void                   **image_extra)
+{
+    if (unlikely (wrapper->target->status))
+       return wrapper->target->status;
+
+    return _cairo_surface_acquire_source_image (wrapper->target,
+                                               image_out, image_extra);
+}
+
+void
+_cairo_surface_wrapper_release_source_image (cairo_surface_wrapper_t *wrapper,
+                                            cairo_image_surface_t  *image,
+                                            void                   *image_extra)
+{
+    _cairo_surface_release_source_image (wrapper->target, image, image_extra);
+}
+
+static void
+_cairo_surface_wrapper_get_transform (cairo_surface_wrapper_t *wrapper,
+                                     cairo_matrix_t *m)
+{
+    cairo_matrix_init_identity (m);
+
+    if (wrapper->has_extents && (wrapper->extents.x || wrapper->extents.y))
+       cairo_matrix_translate (m, -wrapper->extents.x, -wrapper->extents.y);
+
+    if (! _cairo_matrix_is_identity (&wrapper->transform))
+       cairo_matrix_multiply (m, &wrapper->transform, m);
+
+    if (! _cairo_matrix_is_identity (&wrapper->target->device_transform))
+       cairo_matrix_multiply (m, &wrapper->target->device_transform, m);
+}
+
+static void
+_cairo_surface_wrapper_get_inverse_transform (cairo_surface_wrapper_t *wrapper,
+                                             cairo_matrix_t *m)
+{
+    cairo_matrix_init_identity (m);
+
+    if (! _cairo_matrix_is_identity (&wrapper->target->device_transform_inverse))
+       cairo_matrix_multiply (m, &wrapper->target->device_transform_inverse, m);
+
+    if (! _cairo_matrix_is_identity (&wrapper->transform)) {
+       cairo_matrix_t inv;
+       cairo_status_t status;
+
+       inv = wrapper->transform;
+       status = cairo_matrix_invert (&inv);
+       assert (status == CAIRO_STATUS_SUCCESS);
+       cairo_matrix_multiply (m, &inv, m);
+    }
+
+    if (wrapper->has_extents && (wrapper->extents.x || wrapper->extents.y))
+       cairo_matrix_translate (m, wrapper->extents.x, wrapper->extents.y);
+}
+
+static cairo_clip_t *
+_cairo_surface_wrapper_get_clip (cairo_surface_wrapper_t *wrapper,
+                                const cairo_clip_t *clip)
+{
+    cairo_clip_t *copy;
+
+    copy = _cairo_clip_copy (clip);
+    if (wrapper->has_extents) {
+       copy = _cairo_clip_intersect_rectangle (copy, &wrapper->extents);
+       if (wrapper->extents.x | wrapper->extents.y)
+           copy = _cairo_clip_translate (copy,
+                                         -wrapper->extents.x,
+                                         -wrapper->extents.y);
+    }
+    copy = _cairo_clip_transform (copy, &wrapper->transform);
+    if (! _cairo_matrix_is_identity (&wrapper->target->device_transform)) {
+       /* XXX */
+       copy = _cairo_clip_translate (copy,
+                                     wrapper->target->device_transform.x0,
+                                     wrapper->target->device_transform.y0);
+    }
+    if (wrapper->clip)
+       copy = _cairo_clip_intersect_clip (copy, wrapper->clip);
+
+    return copy;
+}
+
+cairo_status_t
+_cairo_surface_wrapper_paint (cairo_surface_wrapper_t *wrapper,
+                             cairo_operator_t   op,
+                             const cairo_pattern_t *source,
+                             const cairo_clip_t    *clip)
+{
+    cairo_status_t status;
+    cairo_clip_t *dev_clip;
+    cairo_pattern_union_t source_copy;
+
+    if (unlikely (wrapper->target->status))
+       return wrapper->target->status;
+
+    dev_clip = _cairo_surface_wrapper_get_clip (wrapper, clip);
+    if (_cairo_clip_is_all_clipped (dev_clip))
+       return CAIRO_INT_STATUS_NOTHING_TO_DO;
+
+    if (wrapper->needs_transform) {
+       cairo_matrix_t m;
+
+       _cairo_surface_wrapper_get_transform (wrapper, &m);
+
+       status = cairo_matrix_invert (&m);
+       assert (status == CAIRO_STATUS_SUCCESS);
+
+       _copy_transformed_pattern (&source_copy.base, source, &m);
+       source = &source_copy.base;
+    }
+
+    status = _cairo_surface_paint (wrapper->target, op, source, dev_clip);
+
+    _cairo_clip_destroy (dev_clip);
+    return status;
+}
+
+
+cairo_status_t
+_cairo_surface_wrapper_mask (cairo_surface_wrapper_t *wrapper,
+                            cairo_operator_t    op,
+                            const cairo_pattern_t *source,
+                            const cairo_pattern_t *mask,
+                            const cairo_clip_t     *clip)
+{
+    cairo_status_t status;
+    cairo_clip_t *dev_clip;
+    cairo_pattern_union_t source_copy;
+    cairo_pattern_union_t mask_copy;
+
+    if (unlikely (wrapper->target->status))
+       return wrapper->target->status;
+
+    dev_clip = _cairo_surface_wrapper_get_clip (wrapper, clip);
+    if (_cairo_clip_is_all_clipped (dev_clip))
+       return CAIRO_INT_STATUS_NOTHING_TO_DO;
+
+    if (wrapper->needs_transform) {
+       cairo_matrix_t m;
+
+       _cairo_surface_wrapper_get_transform (wrapper, &m);
+
+       status = cairo_matrix_invert (&m);
+       assert (status == CAIRO_STATUS_SUCCESS);
+
+       _copy_transformed_pattern (&source_copy.base, source, &m);
+       source = &source_copy.base;
+
+       _copy_transformed_pattern (&mask_copy.base, mask, &m);
+       mask = &mask_copy.base;
+    }
+
+    status = _cairo_surface_mask (wrapper->target, op, source, mask, dev_clip);
+
+    _cairo_clip_destroy (dev_clip);
+    return status;
+}
+
+cairo_status_t
+_cairo_surface_wrapper_stroke (cairo_surface_wrapper_t *wrapper,
+                              cairo_operator_t          op,
+                              const cairo_pattern_t    *source,
+                              const cairo_path_fixed_t *path,
+                              const cairo_stroke_style_t       *stroke_style,
+                              const cairo_matrix_t             *ctm,
+                              const cairo_matrix_t             *ctm_inverse,
+                              double                    tolerance,
+                              cairo_antialias_t         antialias,
+                              const cairo_clip_t               *clip)
+{
+    cairo_status_t status;
+    cairo_path_fixed_t path_copy, *dev_path = (cairo_path_fixed_t *) path;
+    cairo_clip_t *dev_clip;
+    cairo_matrix_t dev_ctm = *ctm;
+    cairo_matrix_t dev_ctm_inverse = *ctm_inverse;
+    cairo_pattern_union_t source_copy;
+
+    if (unlikely (wrapper->target->status))
+       return wrapper->target->status;
+
+    dev_clip = _cairo_surface_wrapper_get_clip (wrapper, clip);
+    if (_cairo_clip_is_all_clipped (dev_clip))
+       return CAIRO_INT_STATUS_NOTHING_TO_DO;
+
+    if (wrapper->needs_transform) {
+       cairo_matrix_t m;
+
+       _cairo_surface_wrapper_get_transform (wrapper, &m);
+
+       status = _cairo_path_fixed_init_copy (&path_copy, dev_path);
+       if (unlikely (status))
+           goto FINISH;
+
+       _cairo_path_fixed_transform (&path_copy, &m);
+       dev_path = &path_copy;
+
+       cairo_matrix_multiply (&dev_ctm, &dev_ctm, &m);
+
+       status = cairo_matrix_invert (&m);
+       assert (status == CAIRO_STATUS_SUCCESS);
+
+       cairo_matrix_multiply (&dev_ctm_inverse, &m, &dev_ctm_inverse);
+
+       _copy_transformed_pattern (&source_copy.base, source, &m);
+       source = &source_copy.base;
+    }
+
+    status = _cairo_surface_stroke (wrapper->target, op, source,
+                                   dev_path, stroke_style,
+                                   &dev_ctm, &dev_ctm_inverse,
+                                   tolerance, antialias,
+                                   dev_clip);
+
+ FINISH:
+    if (dev_path != path)
+       _cairo_path_fixed_fini (dev_path);
+    _cairo_clip_destroy (dev_clip);
+    return status;
+}
+
+cairo_status_t
+_cairo_surface_wrapper_fill_stroke (cairo_surface_wrapper_t *wrapper,
+                                   cairo_operator_t         fill_op,
+                                   const cairo_pattern_t   *fill_source,
+                                   cairo_fill_rule_t        fill_rule,
+                                   double                   fill_tolerance,
+                                   cairo_antialias_t        fill_antialias,
+                                   const cairo_path_fixed_t*path,
+                                   cairo_operator_t         stroke_op,
+                                   const cairo_pattern_t   *stroke_source,
+                                   const cairo_stroke_style_t    *stroke_style,
+                                   const cairo_matrix_t            *stroke_ctm,
+                                   const cairo_matrix_t            *stroke_ctm_inverse,
+                                   double                   stroke_tolerance,
+                                   cairo_antialias_t        stroke_antialias,
+                                   const cairo_clip_t      *clip)
+{
+    cairo_status_t status;
+    cairo_path_fixed_t path_copy, *dev_path = (cairo_path_fixed_t *)path;
+    cairo_matrix_t dev_ctm = *stroke_ctm;
+    cairo_matrix_t dev_ctm_inverse = *stroke_ctm_inverse;
+    cairo_clip_t *dev_clip;
+    cairo_pattern_union_t stroke_source_copy;
+    cairo_pattern_union_t fill_source_copy;
+
+    if (unlikely (wrapper->target->status))
+       return wrapper->target->status;
+
+    dev_clip = _cairo_surface_wrapper_get_clip (wrapper, clip);
+    if (_cairo_clip_is_all_clipped (dev_clip))
+       return CAIRO_INT_STATUS_NOTHING_TO_DO;
+
+    if (wrapper->needs_transform) {
+       cairo_matrix_t m;
+
+       _cairo_surface_wrapper_get_transform (wrapper, &m);
+
+       status = _cairo_path_fixed_init_copy (&path_copy, dev_path);
+       if (unlikely (status))
+           goto FINISH;
+
+       _cairo_path_fixed_transform (&path_copy, &m);
+       dev_path = &path_copy;
+
+       cairo_matrix_multiply (&dev_ctm, &dev_ctm, &m);
+
+       status = cairo_matrix_invert (&m);
+       assert (status == CAIRO_STATUS_SUCCESS);
+
+       cairo_matrix_multiply (&dev_ctm_inverse, &m, &dev_ctm_inverse);
+
+       _copy_transformed_pattern (&stroke_source_copy.base, stroke_source, &m);
+       stroke_source = &stroke_source_copy.base;
+
+       _copy_transformed_pattern (&fill_source_copy.base, fill_source, &m);
+       fill_source = &fill_source_copy.base;
+    }
+
+    status = _cairo_surface_fill_stroke (wrapper->target,
+                                        fill_op, fill_source, fill_rule,
+                                        fill_tolerance, fill_antialias,
+                                        dev_path,
+                                        stroke_op, stroke_source,
+                                        stroke_style,
+                                        &dev_ctm, &dev_ctm_inverse,
+                                        stroke_tolerance, stroke_antialias,
+                                        dev_clip);
+
+  FINISH:
+    if (dev_path != path)
+       _cairo_path_fixed_fini (dev_path);
+    _cairo_clip_destroy (dev_clip);
+    return status;
+}
+
+cairo_status_t
+_cairo_surface_wrapper_fill (cairo_surface_wrapper_t   *wrapper,
+                            cairo_operator_t    op,
+                            const cairo_pattern_t *source,
+                            const cairo_path_fixed_t   *path,
+                            cairo_fill_rule_t   fill_rule,
+                            double              tolerance,
+                            cairo_antialias_t   antialias,
+                            const cairo_clip_t *clip)
+{
+    cairo_status_t status;
+    cairo_path_fixed_t path_copy, *dev_path = (cairo_path_fixed_t *) path;
+    cairo_pattern_union_t source_copy;
+    cairo_clip_t *dev_clip;
+
+    if (unlikely (wrapper->target->status))
+       return wrapper->target->status;
+
+    dev_clip = _cairo_surface_wrapper_get_clip (wrapper, clip);
+    if (_cairo_clip_is_all_clipped (dev_clip))
+       return CAIRO_INT_STATUS_NOTHING_TO_DO;
+
+    if (wrapper->needs_transform) {
+       cairo_matrix_t m;
+
+       _cairo_surface_wrapper_get_transform (wrapper, &m);
+
+       status = _cairo_path_fixed_init_copy (&path_copy, dev_path);
+       if (unlikely (status))
+           goto FINISH;
+
+       _cairo_path_fixed_transform (&path_copy, &m);
+       dev_path = &path_copy;
+
+       status = cairo_matrix_invert (&m);
+       assert (status == CAIRO_STATUS_SUCCESS);
+
+       _copy_transformed_pattern (&source_copy.base, source, &m);
+       source = &source_copy.base;
+    }
+
+    status = _cairo_surface_fill (wrapper->target, op, source,
+                                 dev_path, fill_rule,
+                                 tolerance, antialias,
+                                 dev_clip);
+
+ FINISH:
+    if (dev_path != path)
+       _cairo_path_fixed_fini (dev_path);
+    _cairo_clip_destroy (dev_clip);
+    return status;
+}
+
+cairo_status_t
+_cairo_surface_wrapper_show_text_glyphs (cairo_surface_wrapper_t *wrapper,
+                                        cairo_operator_t            op,
+                                        const cairo_pattern_t      *source,
+                                        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  cluster_flags,
+                                        cairo_scaled_font_t        *scaled_font,
+                                        const cairo_clip_t         *clip)
+{
+    cairo_status_t status;
+    cairo_clip_t *dev_clip;
+    cairo_glyph_t stack_glyphs [CAIRO_STACK_ARRAY_LENGTH(cairo_glyph_t)];
+    cairo_glyph_t *dev_glyphs = stack_glyphs;
+    cairo_pattern_union_t source_copy;
+
+    if (unlikely (wrapper->target->status))
+       return wrapper->target->status;
+
+    dev_clip = _cairo_surface_wrapper_get_clip (wrapper, clip);
+    if (_cairo_clip_is_all_clipped (dev_clip))
+       return CAIRO_INT_STATUS_NOTHING_TO_DO;
+
+    if (wrapper->needs_transform) {
+       cairo_matrix_t m;
+       int i;
+
+       _cairo_surface_wrapper_get_transform (wrapper, &m);
+
+       if (num_glyphs > ARRAY_LENGTH (stack_glyphs)) {
+           dev_glyphs = _cairo_malloc_ab (num_glyphs, sizeof (cairo_glyph_t));
+           if (unlikely (dev_glyphs == NULL)) {
+               status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+               goto FINISH;
+           }
+       }
+
+       for (i = 0; i < num_glyphs; i++) {
+           dev_glyphs[i] = glyphs[i];
+           cairo_matrix_transform_point (&m,
+                                         &dev_glyphs[i].x,
+                                         &dev_glyphs[i].y);
+       }
+
+       status = cairo_matrix_invert (&m);
+       assert (status == CAIRO_STATUS_SUCCESS);
+
+       _copy_transformed_pattern (&source_copy.base, source, &m);
+       source = &source_copy.base;
+    } else {
+       /* show_text_glyphs is special because _cairo_surface_show_text_glyphs is allowed
+        * to modify the glyph array that's passed in.  We must always
+        * copy the array before handing it to the backend.
+        */
+       if (num_glyphs > ARRAY_LENGTH (stack_glyphs)) {
+           dev_glyphs = _cairo_malloc_ab (num_glyphs, sizeof (cairo_glyph_t));
+           if (unlikely (dev_glyphs == NULL)) {
+               status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+               goto FINISH;
+           }
+       }
+
+       memcpy (dev_glyphs, glyphs, sizeof (cairo_glyph_t) * num_glyphs);
+    }
+
+    status = _cairo_surface_show_text_glyphs (wrapper->target, op, source,
+                                             utf8, utf8_len,
+                                             dev_glyphs, num_glyphs,
+                                             clusters, num_clusters,
+                                             cluster_flags,
+                                             scaled_font,
+                                             dev_clip);
+ FINISH:
+    _cairo_clip_destroy (dev_clip);
+    if (dev_glyphs != stack_glyphs)
+       free (dev_glyphs);
+    return status;
+}
+
+cairo_surface_t *
+_cairo_surface_wrapper_create_similar (cairo_surface_wrapper_t *wrapper,
+                                      cairo_content_t  content,
+                                      int              width,
+                                      int              height)
+{
+    return _cairo_surface_create_similar_scratch (wrapper->target,
+                                                 content, width, height);
+}
+
+cairo_bool_t
+_cairo_surface_wrapper_get_extents (cairo_surface_wrapper_t *wrapper,
+                                   cairo_rectangle_int_t   *extents)
+{
+    if (wrapper->has_extents) {
+       if (_cairo_surface_get_extents (wrapper->target, extents))
+           _cairo_rectangle_intersect (extents, &wrapper->extents);
+       else
+           *extents = wrapper->extents;
+
+       return TRUE;
+    } else {
+       return _cairo_surface_get_extents (wrapper->target, extents);
+    }
+}
+
+static cairo_bool_t
+_cairo_surface_wrapper_needs_device_transform (cairo_surface_wrapper_t *wrapper)
+{
+    return
+       (wrapper->has_extents && (wrapper->extents.x | wrapper->extents.y)) ||
+       ! _cairo_matrix_is_identity (&wrapper->transform) ||
+       ! _cairo_matrix_is_identity (&wrapper->target->device_transform);
+}
+
+void
+_cairo_surface_wrapper_intersect_extents (cairo_surface_wrapper_t *wrapper,
+                                         const cairo_rectangle_int_t *extents)
+{
+    if (! wrapper->has_extents) {
+       wrapper->extents = *extents;
+       wrapper->has_extents = TRUE;
+    } else
+       _cairo_rectangle_intersect (&wrapper->extents, extents);
+
+    wrapper->needs_transform =
+       _cairo_surface_wrapper_needs_device_transform (wrapper);
+}
+
+void
+_cairo_surface_wrapper_set_inverse_transform (cairo_surface_wrapper_t *wrapper,
+                                             const cairo_matrix_t *transform)
+{
+    cairo_status_t status;
+
+    if (transform == NULL || _cairo_matrix_is_identity (transform)) {
+       cairo_matrix_init_identity (&wrapper->transform);
+
+       wrapper->needs_transform =
+           _cairo_surface_wrapper_needs_device_transform (wrapper);
+    } else {
+       wrapper->transform = *transform;
+       status = cairo_matrix_invert (&wrapper->transform);
+       /* should always be invertible unless given pathological input */
+       assert (status == CAIRO_STATUS_SUCCESS);
+
+       wrapper->needs_transform = TRUE;
+    }
+}
+
+void
+_cairo_surface_wrapper_set_clip (cairo_surface_wrapper_t *wrapper,
+                                const cairo_clip_t *clip)
+{
+    wrapper->clip = clip;
+}
+
+void
+_cairo_surface_wrapper_get_font_options (cairo_surface_wrapper_t    *wrapper,
+                                        cairo_font_options_t       *options)
+{
+    cairo_surface_get_font_options (wrapper->target, options);
+}
+
+cairo_surface_t *
+_cairo_surface_wrapper_snapshot (cairo_surface_wrapper_t *wrapper)
+{
+    if (wrapper->target->backend->snapshot)
+       return wrapper->target->backend->snapshot (wrapper->target);
+
+    return NULL;
+}
+
+cairo_bool_t
+_cairo_surface_wrapper_has_show_text_glyphs (cairo_surface_wrapper_t *wrapper)
+{
+    return cairo_surface_has_show_text_glyphs (wrapper->target);
+}
+
+void
+_cairo_surface_wrapper_init (cairo_surface_wrapper_t *wrapper,
+                            cairo_surface_t *target)
+{
+    wrapper->target = cairo_surface_reference (target);
+    cairo_matrix_init_identity (&wrapper->transform);
+    wrapper->has_extents = FALSE;
+    wrapper->extents.x = wrapper->extents.y = 0;
+    wrapper->clip = NULL;
+
+    wrapper->needs_transform = FALSE;
+    if (target) {
+       wrapper->needs_transform =
+           ! _cairo_matrix_is_identity (&target->device_transform);
+    }
+}
+
+void
+_cairo_surface_wrapper_fini (cairo_surface_wrapper_t *wrapper)
+{
+    cairo_surface_destroy (wrapper->target);
+}
+
+cairo_bool_t
+_cairo_surface_wrapper_get_target_extents (cairo_surface_wrapper_t *wrapper,
+                                          cairo_rectangle_int_t *extents)
+{
+    cairo_rectangle_int_t clip;
+    cairo_bool_t has_clip;
+
+    has_clip = _cairo_surface_get_extents (wrapper->target, &clip);
+    if (wrapper->clip) {
+       if (has_clip) {
+           if (! _cairo_rectangle_intersect (&clip,
+                                             _cairo_clip_get_extents (wrapper->clip)))
+               return FALSE;
+       } else {
+           has_clip = TRUE;
+           clip = *_cairo_clip_get_extents (wrapper->clip);
+       }
+    }
+
+    if (has_clip && wrapper->needs_transform) {
+       cairo_matrix_t m;
+       double x1, y1, x2, y2;
+
+       _cairo_surface_wrapper_get_inverse_transform (wrapper, &m);
+
+       x1 = clip.x;
+       y1 = clip.y;
+       x2 = clip.x + clip.width;
+       y2 = clip.y + clip.height;
+
+       _cairo_matrix_transform_bounding_box (&m, &x1, &y1, &x2, &y2, NULL);
+
+       clip.x = floor (x1);
+       clip.y = floor (y1);
+       clip.width  = ceil (x2) - clip.x;
+       clip.height = ceil (y2) - clip.y;
+    }
+
+    if (has_clip) {
+       if (wrapper->has_extents) {
+           *extents = wrapper->extents;
+           return _cairo_rectangle_intersect (extents, &clip);
+       } else {
+           *extents = clip;
+           return TRUE;
+       }
+    } else if (wrapper->has_extents) {
+       *extents = wrapper->extents;
+       return TRUE;
+    } else {
+       _cairo_unbounded_rectangle_init (extents);
+       return TRUE;
+    }
+}
diff --git a/src/cairo-surface.c b/src/cairo-surface.c
new file mode 100644 (file)
index 0000000..c026a3d
--- /dev/null
@@ -0,0 +1,2346 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *     Carl D. Worth <cworth@cworth.org>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-array-private.h"
+#include "cairo-clip-private.h"
+#include "cairo-device-private.h"
+#include "cairo-error-private.h"
+#include "cairo-image-surface-private.h"
+#include "cairo-recording-surface-private.h"
+#include "cairo-region-private.h"
+#include "cairo-tee-surface-private.h"
+
+/**
+ * SECTION:cairo-surface
+ * @Title: cairo_surface_t
+ * @Short_Description: Base class for surfaces
+ * @See_Also: #cairo_t, #cairo_pattern_t
+ *
+ * #cairo_surface_t is the abstract type representing all different drawing
+ * targets that cairo can render to.  The actual drawings are
+ * performed using a cairo <firstterm>context</firstterm>.
+ *
+ * A cairo surface is created by using <firstterm>backend</firstterm>-specific
+ * constructors, typically of the form
+ * <function>cairo_<emphasis>backend</emphasis>_surface_create(<!-- -->)</function>.
+ *
+ * Most surface types allow accessing the surface without using Cairo
+ * functions. If you do this, keep in mind that it is mandatory that you call
+ * cairo_surface_flush() before reading from or writing to the surface and that
+ * you must use cairo_surface_mark_dirty() after modifying it.
+ * <example>
+ * <title>Directly modifying an image surface</title>
+ * <programlisting>
+ * void
+ * modify_image_surface (cairo_surface_t *surface)
+ * {
+ *   unsigned char *data;
+ *   int width, height, stride;
+ *
+ *   // flush to ensure all writing to the image was done
+ *   cairo_surface_flush (surface);
+ *
+ *   // modify the image
+ *   data = cairo_image_surface_get_data (surface);
+ *   width = cairo_image_surface_get_width (surface);
+ *   height = cairo_image_surface_get_height (surface);
+ *   stride = cairo_image_surface_get_stride (surface);
+ *   modify_image_data (data, width, height, stride);
+ *
+ *   // mark the image dirty so Cairo clears its caches.
+ *   cairo_surface_mark_dirty (surface);
+ * }
+ * </programlisting>
+ * </example>
+ * Note that for other surface types it might be necessary to acquire the
+ * surface's device first. See cairo_device_acquire() for a discussion of
+ * devices.
+ */
+
+#define DEFINE_NIL_SURFACE(status, name)                       \
+const cairo_surface_t name = {                                 \
+    NULL,                              /* backend */           \
+    NULL,                              /* device */            \
+    CAIRO_SURFACE_TYPE_IMAGE,          /* type */              \
+    CAIRO_CONTENT_COLOR,               /* content */           \
+    CAIRO_REFERENCE_COUNT_INVALID,     /* ref_count */         \
+    status,                            /* status */            \
+    0,                                 /* unique id */         \
+    0,                                 /* serial */            \
+    FALSE,                             /* finished */          \
+    TRUE,                              /* is_clear */          \
+    FALSE,                             /* has_font_options */  \
+    FALSE,                             /* owns_device */       \
+    { 0, 0, 0, NULL, },                        /* user_data */         \
+    { 0, 0, 0, NULL, },                        /* mime_data */         \
+    { 1.0, 0.0, 0.0, 1.0, 0.0, 0.0 },   /* device_transform */ \
+    { 1.0, 0.0,        0.0, 1.0, 0.0, 0.0 },   /* device_transform_inverse */  \
+    { NULL, NULL },                    /* device_transform_observers */ \
+    0.0,                               /* x_resolution */      \
+    0.0,                               /* y_resolution */      \
+    0.0,                               /* x_fallback_resolution */     \
+    0.0,                               /* y_fallback_resolution */     \
+    NULL,                              /* snapshot_of */       \
+    NULL,                              /* snapshot_detach */   \
+    { NULL, NULL },                    /* snapshots */         \
+    { NULL, NULL },                    /* snapshot */          \
+    { CAIRO_ANTIALIAS_DEFAULT,         /* antialias */         \
+      CAIRO_SUBPIXEL_ORDER_DEFAULT,    /* subpixel_order */    \
+      CAIRO_LCD_FILTER_DEFAULT,                /* lcd_filter */        \
+      CAIRO_HINT_STYLE_DEFAULT,                /* hint_style */        \
+      CAIRO_HINT_METRICS_DEFAULT,      /* hint_metrics */      \
+      CAIRO_ROUND_GLYPH_POS_DEFAULT    /* round_glyph_positions */     \
+    }                                  /* font_options */      \
+}
+
+/* XXX error object! */
+
+static DEFINE_NIL_SURFACE(CAIRO_STATUS_NO_MEMORY, _cairo_surface_nil);
+static DEFINE_NIL_SURFACE(CAIRO_STATUS_SURFACE_TYPE_MISMATCH, _cairo_surface_nil_surface_type_mismatch);
+static DEFINE_NIL_SURFACE(CAIRO_STATUS_INVALID_STATUS, _cairo_surface_nil_invalid_status);
+static DEFINE_NIL_SURFACE(CAIRO_STATUS_INVALID_CONTENT, _cairo_surface_nil_invalid_content);
+static DEFINE_NIL_SURFACE(CAIRO_STATUS_INVALID_FORMAT, _cairo_surface_nil_invalid_format);
+static DEFINE_NIL_SURFACE(CAIRO_STATUS_INVALID_VISUAL, _cairo_surface_nil_invalid_visual);
+static DEFINE_NIL_SURFACE(CAIRO_STATUS_FILE_NOT_FOUND, _cairo_surface_nil_file_not_found);
+static DEFINE_NIL_SURFACE(CAIRO_STATUS_TEMP_FILE_ERROR, _cairo_surface_nil_temp_file_error);
+static DEFINE_NIL_SURFACE(CAIRO_STATUS_READ_ERROR, _cairo_surface_nil_read_error);
+static DEFINE_NIL_SURFACE(CAIRO_STATUS_WRITE_ERROR, _cairo_surface_nil_write_error);
+static DEFINE_NIL_SURFACE(CAIRO_STATUS_INVALID_STRIDE, _cairo_surface_nil_invalid_stride);
+static DEFINE_NIL_SURFACE(CAIRO_STATUS_INVALID_SIZE, _cairo_surface_nil_invalid_size);
+static DEFINE_NIL_SURFACE(CAIRO_STATUS_DEVICE_TYPE_MISMATCH, _cairo_surface_nil_device_type_mismatch);
+static DEFINE_NIL_SURFACE(CAIRO_STATUS_DEVICE_ERROR, _cairo_surface_nil_device_error);
+
+static DEFINE_NIL_SURFACE(CAIRO_INT_STATUS_UNSUPPORTED, _cairo_surface_nil_unsupported);
+static DEFINE_NIL_SURFACE(CAIRO_INT_STATUS_NOTHING_TO_DO, _cairo_surface_nil_nothing_to_do);
+
+/**
+ * _cairo_surface_set_error:
+ * @surface: a surface
+ * @status: a status value indicating an error
+ *
+ * Atomically sets surface->status to @status and calls _cairo_error;
+ * Does nothing if status is %CAIRO_STATUS_SUCCESS or any of the internal
+ * status values.
+ *
+ * All assignments of an error status to surface->status should happen
+ * through _cairo_surface_set_error(). Note that due to the nature of
+ * the atomic operation, it is not safe to call this function on the
+ * nil objects.
+ *
+ * The purpose of this function is to allow the user to set a
+ * breakpoint in _cairo_error() to generate a stack trace for when the
+ * user causes cairo to detect an error.
+ *
+ * Return value: the error status.
+ **/
+cairo_int_status_t
+_cairo_surface_set_error (cairo_surface_t *surface,
+                         cairo_int_status_t status)
+{
+    /* NOTHING_TO_DO is magic. We use it to break out of the inner-most
+     * surface function, but anything higher just sees "success".
+     */
+    if (status == CAIRO_INT_STATUS_NOTHING_TO_DO)
+       status = CAIRO_INT_STATUS_SUCCESS;
+
+    if (status == CAIRO_INT_STATUS_SUCCESS ||
+        status >= (int)CAIRO_INT_STATUS_LAST_STATUS)
+        return status;
+
+    /* Don't overwrite an existing error. This preserves the first
+     * error, which is the most significant. */
+    _cairo_status_set_error (&surface->status, (cairo_status_t)status);
+
+    return _cairo_error (status);
+}
+
+/**
+ * cairo_surface_get_type:
+ * @surface: a #cairo_surface_t
+ *
+ * This function returns the type of the backend used to create
+ * a surface. See #cairo_surface_type_t for available types.
+ *
+ * Return value: The type of @surface.
+ *
+ * Since: 1.2
+ **/
+cairo_surface_type_t
+cairo_surface_get_type (cairo_surface_t *surface)
+{
+    /* We don't use surface->backend->type here so that some of the
+     * special "wrapper" surfaces such as cairo_paginated_surface_t
+     * can override surface->type with the type of the "child"
+     * surface. */
+    return surface->type;
+}
+slim_hidden_def (cairo_surface_get_type);
+
+/**
+ * cairo_surface_get_content:
+ * @surface: a #cairo_surface_t
+ *
+ * This function returns the content type of @surface which indicates
+ * whether the surface contains color and/or alpha information. See
+ * #cairo_content_t.
+ *
+ * Return value: The content type of @surface.
+ *
+ * Since: 1.2
+ **/
+cairo_content_t
+cairo_surface_get_content (cairo_surface_t *surface)
+{
+    return surface->content;
+}
+slim_hidden_def(cairo_surface_get_content);
+
+/**
+ * cairo_surface_status:
+ * @surface: a #cairo_surface_t
+ *
+ * Checks whether an error has previously occurred for this
+ * surface.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS, %CAIRO_STATUS_NULL_POINTER,
+ * %CAIRO_STATUS_NO_MEMORY, %CAIRO_STATUS_READ_ERROR,
+ * %CAIRO_STATUS_INVALID_CONTENT, %CAIRO_STATUS_INVALID_FORMAT, or
+ * %CAIRO_STATUS_INVALID_VISUAL.
+ **/
+cairo_status_t
+cairo_surface_status (cairo_surface_t *surface)
+{
+    return surface->status;
+}
+slim_hidden_def (cairo_surface_status);
+
+static unsigned int
+_cairo_surface_allocate_unique_id (void)
+{
+    static cairo_atomic_int_t unique_id;
+
+#if CAIRO_NO_MUTEX
+    if (++unique_id == 0)
+       unique_id = 1;
+    return unique_id;
+#else
+    cairo_atomic_int_t old, id;
+
+    do {
+       old = _cairo_atomic_uint_get (&unique_id);
+       id = old + 1;
+       if (id == 0)
+           id = 1;
+    } while (! _cairo_atomic_uint_cmpxchg (&unique_id, old, id));
+
+    return id;
+#endif
+}
+
+/**
+ * cairo_surface_get_device:
+ * @surface: a #cairo_surface_t
+ *
+ * This function returns the device for a @surface.
+ * See #cairo_device_t.
+ *
+ * Return value: The device for @surface or %NULL if the surface does
+ *               not have an associated device.
+ *
+ * Since: 1.10
+ **/
+cairo_device_t *
+cairo_surface_get_device (cairo_surface_t *surface)
+{
+    if (unlikely (surface->status))
+       return _cairo_device_create_in_error (surface->status);
+
+    return surface->device;
+}
+
+static cairo_bool_t
+_cairo_surface_has_snapshots (cairo_surface_t *surface)
+{
+    return ! cairo_list_is_empty (&surface->snapshots);
+}
+
+static cairo_bool_t
+_cairo_surface_has_mime_data (cairo_surface_t *surface)
+{
+    return surface->mime_data.num_elements != 0;
+}
+
+static void
+_cairo_surface_detach_mime_data (cairo_surface_t *surface)
+{
+    if (! _cairo_surface_has_mime_data (surface))
+       return;
+
+    _cairo_user_data_array_fini (&surface->mime_data);
+    _cairo_user_data_array_init (&surface->mime_data);
+}
+
+static void
+_cairo_surface_detach_snapshots (cairo_surface_t *surface)
+{
+    while (_cairo_surface_has_snapshots (surface)) {
+       _cairo_surface_detach_snapshot (cairo_list_first_entry (&surface->snapshots,
+                                                               cairo_surface_t,
+                                                               snapshot));
+    }
+}
+
+void
+_cairo_surface_detach_snapshot (cairo_surface_t *snapshot)
+{
+    assert (snapshot->snapshot_of != NULL);
+
+    snapshot->snapshot_of = NULL;
+    cairo_list_del (&snapshot->snapshot);
+
+    if (snapshot->snapshot_detach != NULL)
+       snapshot->snapshot_detach (snapshot);
+
+    cairo_surface_destroy (snapshot);
+}
+
+void
+_cairo_surface_attach_snapshot (cairo_surface_t *surface,
+                                cairo_surface_t *snapshot,
+                                cairo_surface_func_t detach_func)
+{
+    assert (surface != snapshot);
+    assert (snapshot->snapshot_of != surface);
+
+    cairo_surface_reference (snapshot);
+
+    if (snapshot->snapshot_of != NULL)
+       _cairo_surface_detach_snapshot (snapshot);
+
+    snapshot->snapshot_of = surface;
+    snapshot->snapshot_detach = detach_func;
+
+    cairo_list_add (&snapshot->snapshot, &surface->snapshots);
+
+    assert (_cairo_surface_has_snapshot (surface, snapshot->backend) == snapshot);
+}
+
+cairo_surface_t *
+_cairo_surface_has_snapshot (cairo_surface_t *surface,
+                            const cairo_surface_backend_t *backend)
+{
+    cairo_surface_t *snapshot;
+
+    cairo_list_foreach_entry (snapshot, cairo_surface_t,
+                             &surface->snapshots, snapshot)
+    {
+       if (snapshot->backend == backend)
+           return snapshot;
+    }
+
+    return NULL;
+}
+
+static void
+_cairo_surface_begin_modification (cairo_surface_t *surface)
+{
+    assert (surface->status == CAIRO_STATUS_SUCCESS);
+    assert (! surface->finished);
+    assert (surface->snapshot_of == NULL);
+
+    _cairo_surface_detach_snapshots (surface);
+    _cairo_surface_detach_mime_data (surface);
+}
+
+void
+_cairo_surface_init (cairo_surface_t                   *surface,
+                    const cairo_surface_backend_t      *backend,
+                    cairo_device_t                     *device,
+                    cairo_content_t                     content)
+{
+    CAIRO_MUTEX_INITIALIZE ();
+
+    surface->backend = backend;
+    surface->device = cairo_device_reference (device);
+    surface->content = content;
+    surface->type = backend->type;
+
+    CAIRO_REFERENCE_COUNT_INIT (&surface->ref_count, 1);
+    surface->status = CAIRO_STATUS_SUCCESS;
+    surface->unique_id = _cairo_surface_allocate_unique_id ();
+    surface->finished = FALSE;
+    surface->is_clear = FALSE;
+    surface->serial = 0;
+    surface->owns_device = (device != NULL);
+
+    _cairo_user_data_array_init (&surface->user_data);
+    _cairo_user_data_array_init (&surface->mime_data);
+
+    cairo_matrix_init_identity (&surface->device_transform);
+    cairo_matrix_init_identity (&surface->device_transform_inverse);
+    cairo_list_init (&surface->device_transform_observers);
+
+    surface->x_resolution = CAIRO_SURFACE_RESOLUTION_DEFAULT;
+    surface->y_resolution = CAIRO_SURFACE_RESOLUTION_DEFAULT;
+
+    surface->x_fallback_resolution = CAIRO_SURFACE_FALLBACK_RESOLUTION_DEFAULT;
+    surface->y_fallback_resolution = CAIRO_SURFACE_FALLBACK_RESOLUTION_DEFAULT;
+
+    cairo_list_init (&surface->snapshots);
+    surface->snapshot_of = NULL;
+
+    surface->has_font_options = FALSE;
+}
+
+static void
+_cairo_surface_copy_similar_properties (cairo_surface_t *surface,
+                                       cairo_surface_t *other)
+{
+    if (other->has_font_options || other->backend != surface->backend) {
+       cairo_font_options_t options;
+
+       cairo_surface_get_font_options (other, &options);
+       _cairo_surface_set_font_options (surface, &options);
+    }
+
+    cairo_surface_set_fallback_resolution (surface,
+                                          other->x_fallback_resolution,
+                                          other->y_fallback_resolution);
+}
+
+cairo_surface_t *
+_cairo_surface_create_similar_scratch (cairo_surface_t *other,
+                                      cairo_content_t  content,
+                                      int              width,
+                                      int              height)
+{
+    cairo_surface_t *surface;
+
+    if (unlikely (other->status))
+       return _cairo_surface_create_in_error (other->status);
+
+    surface = NULL;
+    if (other->backend->create_similar)
+       surface = other->backend->create_similar (other, content, width, height);
+    if (surface == NULL)
+       surface = cairo_surface_create_similar_image (other,
+                                                     _cairo_format_from_content (content),
+                                                     width, height);
+
+    if (unlikely (surface->status))
+       return surface;
+
+    _cairo_surface_copy_similar_properties (surface, other);
+
+    return surface;
+}
+
+/**
+ * cairo_surface_create_similar:
+ * @other: an existing surface used to select the backend of the new surface
+ * @content: the content for the new surface
+ * @width: width of the new surface, (in device-space units)
+ * @height: height of the new surface (in device-space units)
+ *
+ * Create a new surface that is as compatible as possible with an
+ * existing surface. For example the new surface will have the same
+ * fallback resolution and font options as @other. Generally, the new
+ * surface will also use the same backend as @other, unless that is
+ * not possible for some reason. The type of the returned surface may
+ * be examined with cairo_surface_get_type().
+ *
+ * Initially the surface contents are all 0 (transparent if contents
+ * have transparency, black otherwise.)
+ *
+ * Use cairo_surface_create_similar_image() if you need an image surface
+ * which can be painted quickly to the target surface.
+ *
+ * Return value: a pointer to the newly allocated surface. The caller
+ * owns the surface and should call cairo_surface_destroy() when done
+ * with it.
+ *
+ * This function always returns a valid pointer, but it will return a
+ * pointer to a "nil" surface if @other is already in an error state
+ * or any other error occurs.
+ **/
+cairo_surface_t *
+cairo_surface_create_similar (cairo_surface_t  *other,
+                             cairo_content_t   content,
+                             int               width,
+                             int               height)
+{
+    cairo_surface_t *surface;
+
+    if (unlikely (other->status))
+       return _cairo_surface_create_in_error (other->status);
+    if (unlikely (other->finished))
+       return _cairo_surface_create_in_error (CAIRO_STATUS_SURFACE_FINISHED);
+    if (unlikely (width < 0 || height < 0))
+       return _cairo_surface_create_in_error (CAIRO_STATUS_INVALID_SIZE);
+
+    if (unlikely (! CAIRO_CONTENT_VALID (content)))
+       return _cairo_surface_create_in_error (CAIRO_STATUS_INVALID_CONTENT);
+
+    surface = _cairo_surface_create_similar_solid (other,
+                                                  content, width, height,
+                                                  CAIRO_COLOR_TRANSPARENT);
+    assert (surface->is_clear);
+
+    return surface;
+}
+
+/**
+ * cairo_surface_create_similar_image:
+ * @other: an existing surface used to select the preference of the new surface
+ * @format: the format for the new surface
+ * @width: width of the new surface, (in device-space units)
+ * @height: height of the new surface (in device-space units)
+ *
+ * Create a new image surface that is as compatible as possible for uploading
+ * to and the use in conjunction with an existing surface. However, this surface
+ * can still be used like any normal image surface.
+ *
+ * Initially the surface contents are all 0 (transparent if contents
+ * have transparency, black otherwise.)
+ *
+ * Use cairo_surface_create_similar() if you don't need an image surface.
+ *
+ * Return value: a pointer to the newly allocated image surface. The caller
+ * owns the surface and should call cairo_surface_destroy() when done
+ * with it.
+ *
+ * This function always returns a valid pointer, but it will return a
+ * pointer to a "nil" surface if @other is already in an error state
+ * or any other error occurs.
+ *
+ * Since: 1.12
+ **/
+cairo_surface_t *
+cairo_surface_create_similar_image (cairo_surface_t  *other,
+                                   cairo_format_t    format,
+                                   int         width,
+                                   int         height)
+{
+    cairo_surface_t *image;
+
+    if (unlikely (other->status))
+       return _cairo_surface_create_in_error (other->status);
+    if (unlikely (other->finished))
+       return _cairo_surface_create_in_error (CAIRO_STATUS_SURFACE_FINISHED);
+
+    if (unlikely (width < 0 || height < 0))
+       return _cairo_surface_create_in_error (CAIRO_STATUS_INVALID_SIZE);
+    if (unlikely (! CAIRO_FORMAT_VALID (format)))
+       return _cairo_surface_create_in_error (CAIRO_STATUS_INVALID_FORMAT);
+
+    image = NULL;
+    if (other->backend->create_similar_image)
+       image = other->backend->create_similar_image (other,
+                                                     format, width, height);
+    if (image == NULL)
+       image = cairo_image_surface_create (format, width, height);
+
+    assert (image->is_clear);
+
+    return image;
+}
+slim_hidden_def (cairo_surface_create_similar_image);
+
+/**
+ * cairo_surface_map_to_image:
+ * @surface: an existing surface used to extract the image from
+ * @extents: limit the extraction to an rectangular region
+ *
+ * Returns an image surface that is the most efficient mechanism for
+ * modifying the backing store of the target surface. The region retrieved
+ * may be limited to the @extents or %NULL for the whole surface
+ *
+ * Note, the use of the original surface as a target or source whilst it is
+ * mapped is undefined. The result of mapping the surface multiple times is
+ * undefined. Calling cairo_surface_destroy() or cairo_surface_finish() on the
+ * resulting image surface results in undefined behavior.
+ *
+ * Return value: a pointer to the newly allocated image surface. The caller
+ * must use cairo_surface_unmap_image() to destroy this image surface.
+ *
+ * This function always returns a valid pointer, but it will return a
+ * pointer to a "nil" surface if @other is already in an error state
+ * or any other error occurs.
+ *
+ * Since: 1.12
+ **/
+cairo_surface_t *
+cairo_surface_map_to_image (cairo_surface_t  *surface,
+                           const cairo_rectangle_int_t *extents)
+{
+    cairo_rectangle_int_t rect;
+    cairo_surface_t *image;
+
+    if (unlikely (surface->status))
+       return _cairo_surface_create_in_error (surface->status);
+    if (unlikely (surface->finished))
+       return _cairo_surface_create_in_error (CAIRO_STATUS_SURFACE_FINISHED);
+
+    if (extents == NULL) {
+       if (unlikely (! surface->backend->get_extents (surface, &rect)))
+           return _cairo_surface_create_in_error (CAIRO_STATUS_INVALID_SIZE);
+
+       extents = &rect;
+    } else {
+       cairo_rectangle_int_t surface_extents;
+
+       /* If this surface is bounded, we can't map parts
+        * that are outside of it. */
+       if (likely (surface->backend->get_extents (surface, &surface_extents))) {
+           if (unlikely (extents->x < surface_extents.x ||
+                         extents->y < surface_extents.y ||
+                         extents->x + extents->width > surface_extents.x + surface_extents.width ||
+                         extents->y + extents->height > surface_extents.y + surface_extents.height))
+               return _cairo_surface_create_in_error (CAIRO_STATUS_INVALID_SIZE);
+       }
+    }
+
+    image = NULL;
+    if (surface->backend->map_to_image)
+       image = surface->backend->map_to_image (surface, extents);
+
+    if (image == NULL) {
+       cairo_surface_pattern_t pattern;
+       cairo_status_t status;
+
+       image = cairo_surface_create_similar_image (surface,
+                                                   _cairo_format_from_content (surface->content),
+                                                   extents->width,
+                                                   extents->height);
+       cairo_surface_set_device_offset (image, -extents->y, -extents->y);
+
+       _cairo_pattern_init_for_surface (&pattern, surface);
+       pattern.base.filter = CAIRO_FILTER_NEAREST;
+
+       status = _cairo_surface_paint (image,
+                                      CAIRO_OPERATOR_SOURCE,
+                                      &pattern.base,
+                                      NULL);
+
+       _cairo_pattern_fini (&pattern.base);
+
+       if (unlikely (status)) {
+           cairo_surface_destroy (image);
+           image = _cairo_surface_create_in_error (status);
+       }
+    }
+
+    return image;
+}
+slim_hidden_def (cairo_surface_map_to_image);
+
+/**
+ * cairo_surface_unmap_image:
+ * @surface: the surface passed to cairo_surface_map_to_image().
+ * @image: the currently mapped image
+ *
+ * Unmaps the image surface as returned from #cairo_surface_map_to_image().
+ *
+ * The content of the image will be uploaded to the target surface.
+ * Afterwards, the image is destroyed.
+ *
+ * Using an image surface which wasn't returned by cairo_surface_map_to_image()
+ * results in undefined behavior.
+ *
+ * Since: 1.12
+ **/
+void
+cairo_surface_unmap_image (cairo_surface_t *surface,
+                          cairo_surface_t *image)
+{
+    cairo_int_status_t status;
+
+    if (unlikely (surface->status)) {
+       status = surface->status;
+       goto error;
+    }
+    if (unlikely (surface->finished)) {
+       status = _cairo_error (CAIRO_STATUS_SURFACE_FINISHED);
+       goto error;
+    }
+
+    if (unlikely (image->status)) {
+       status = image->status;
+       goto error;
+    }
+    if (unlikely (image->finished)) {
+       status = _cairo_error (CAIRO_STATUS_SURFACE_FINISHED);
+       goto error;
+    }
+    if (unlikely (! _cairo_surface_is_image (image))) {
+       status = _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+       goto error;
+    }
+
+    /* If the image is untouched just skip the update */
+    if (image->serial == 0) {
+       status = CAIRO_STATUS_SUCCESS;
+       goto error;
+    }
+
+    status = CAIRO_INT_STATUS_UNSUPPORTED;
+    if (surface->backend->unmap_image)
+       status = surface->backend->unmap_image (surface, (cairo_image_surface_t *) image);
+    if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
+       cairo_image_surface_t *img = (cairo_image_surface_t *) image;
+       cairo_surface_pattern_t pattern;
+       cairo_clip_t *clip;
+       cairo_rectangle_int_t extents;
+
+       _cairo_pattern_init_for_surface (&pattern, image);
+       pattern.base.filter = CAIRO_FILTER_NEAREST;
+
+       /* We have to apply the translate from map_to_image's extents.x and .y */
+       cairo_matrix_init_translate (&pattern.base.matrix,
+                                    image->device_transform.x0,
+                                    image->device_transform.y0);
+
+       /* And we also have to clip the operation to the image's extents */
+       extents.x = image->device_transform_inverse.x0;
+       extents.y = image->device_transform_inverse.y0;
+       extents.width  = img->width;
+       extents.height = img->height;
+       clip = _cairo_clip_intersect_rectangle (NULL, &extents);
+
+       status = _cairo_surface_paint (surface,
+                                      CAIRO_OPERATOR_SOURCE,
+                                      &pattern.base,
+                                      clip);
+
+       _cairo_pattern_fini (&pattern.base);
+       _cairo_clip_destroy (clip);
+    }
+
+error:
+    cairo_surface_finish (image);
+    cairo_surface_destroy (image);
+    if (status)
+       _cairo_surface_set_error (surface, status);
+}
+slim_hidden_def (cairo_surface_unmap_image);
+
+cairo_surface_t *
+_cairo_surface_create_similar_solid (cairo_surface_t    *other,
+                                    cairo_content_t      content,
+                                    int                  width,
+                                    int                  height,
+                                    const cairo_color_t *color)
+{
+    cairo_status_t status;
+    cairo_surface_t *surface;
+    cairo_solid_pattern_t pattern;
+
+    surface = _cairo_surface_create_similar_scratch (other, content,
+                                                    width, height);
+    if (unlikely (surface->status))
+       return surface;
+
+    _cairo_pattern_init_solid (&pattern, color);
+    status = _cairo_surface_paint (surface,
+                                  color == CAIRO_COLOR_TRANSPARENT ?
+                                  CAIRO_OPERATOR_CLEAR : CAIRO_OPERATOR_SOURCE,
+                                  &pattern.base, NULL);
+    if (unlikely (status)) {
+       cairo_surface_destroy (surface);
+       surface = _cairo_surface_create_in_error (status);
+    }
+
+    return surface;
+}
+
+/**
+ * cairo_surface_reference:
+ * @surface: a #cairo_surface_t
+ *
+ * Increases the reference count on @surface by one. This prevents
+ * @surface from being destroyed until a matching call to
+ * cairo_surface_destroy() is made.
+ *
+ * The number of references to a #cairo_surface_t can be get using
+ * cairo_surface_get_reference_count().
+ *
+ * Return value: the referenced #cairo_surface_t.
+ **/
+cairo_surface_t *
+cairo_surface_reference (cairo_surface_t *surface)
+{
+    if (surface == NULL ||
+           CAIRO_REFERENCE_COUNT_IS_INVALID (&surface->ref_count))
+       return surface;
+
+    assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&surface->ref_count));
+
+    _cairo_reference_count_inc (&surface->ref_count);
+
+    return surface;
+}
+slim_hidden_def (cairo_surface_reference);
+
+/**
+ * cairo_surface_destroy:
+ * @surface: a #cairo_surface_t
+ *
+ * Decreases the reference count on @surface by one. If the result is
+ * zero, then @surface and all associated resources are freed.  See
+ * cairo_surface_reference().
+ **/
+void
+cairo_surface_destroy (cairo_surface_t *surface)
+{
+    if (surface == NULL ||
+           CAIRO_REFERENCE_COUNT_IS_INVALID (&surface->ref_count))
+       return;
+
+    assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&surface->ref_count));
+
+    if (! _cairo_reference_count_dec_and_test (&surface->ref_count))
+       return;
+
+    assert (surface->snapshot_of == NULL);
+
+    if (! surface->finished)
+       cairo_surface_finish (surface);
+
+    /* paranoid check that nobody took a reference whilst finishing */
+    assert (! CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&surface->ref_count));
+
+    _cairo_user_data_array_fini (&surface->user_data);
+    _cairo_user_data_array_fini (&surface->mime_data);
+
+    if (surface->owns_device)
+        cairo_device_destroy (surface->device);
+
+    assert (surface->snapshot_of == NULL);
+    assert (!_cairo_surface_has_snapshots (surface));
+
+    free (surface);
+}
+slim_hidden_def(cairo_surface_destroy);
+
+/**
+ * cairo_surface_get_reference_count:
+ * @surface: a #cairo_surface_t
+ *
+ * Returns the current reference count of @surface.
+ *
+ * Return value: the current reference count of @surface.  If the
+ * object is a nil object, 0 will be returned.
+ *
+ * Since: 1.4
+ **/
+unsigned int
+cairo_surface_get_reference_count (cairo_surface_t *surface)
+{
+    if (surface == NULL ||
+           CAIRO_REFERENCE_COUNT_IS_INVALID (&surface->ref_count))
+       return 0;
+
+    return CAIRO_REFERENCE_COUNT_GET_VALUE (&surface->ref_count);
+}
+
+/**
+ * cairo_surface_finish:
+ * @surface: the #cairo_surface_t to finish
+ *
+ * This function finishes the surface and drops all references to
+ * external resources.  For example, for the Xlib backend it means
+ * that cairo will no longer access the drawable, which can be freed.
+ * After calling cairo_surface_finish() the only valid operations on a
+ * surface are getting and setting user, referencing and
+ * destroying, and flushing and finishing it.
+ * Further drawing to the surface will not affect the
+ * surface but will instead trigger a %CAIRO_STATUS_SURFACE_FINISHED
+ * error.
+ *
+ * When the last call to cairo_surface_destroy() decreases the
+ * reference count to zero, cairo will call cairo_surface_finish() if
+ * it hasn't been called already, before freeing the resources
+ * associated with the surface.
+ **/
+void
+cairo_surface_finish (cairo_surface_t *surface)
+{
+    cairo_status_t status;
+
+    if (surface == NULL)
+       return;
+
+    if (CAIRO_REFERENCE_COUNT_IS_INVALID (&surface->ref_count))
+       return;
+
+    if (surface->finished)
+       return;
+
+    cairo_surface_flush (surface);
+
+    /* update the snapshots *before* we declare the surface as finished */
+    _cairo_surface_detach_snapshots (surface);
+    if (surface->snapshot_of != NULL)
+       _cairo_surface_detach_snapshot (surface);
+
+    surface->finished = TRUE;
+
+    /* call finish even if in error mode */
+    if (surface->backend->finish) {
+       status = surface->backend->finish (surface);
+       if (unlikely (status))
+           _cairo_surface_set_error (surface, status);
+    }
+
+    assert (surface->snapshot_of == NULL);
+    assert (!_cairo_surface_has_snapshots (surface));
+}
+slim_hidden_def (cairo_surface_finish);
+
+/**
+ * _cairo_surface_release_device_reference:
+ * @surface: a #cairo_surface_t
+ *
+ * This function makes @surface release the reference to its device. The
+ * function is intended to be used for avoiding cycling references for
+ * surfaces that are owned by their device, for example cache surfaces.
+ * Note that the @surface will still assume that the device is available.
+ * So it is the caller's responsibility to ensure the device stays around
+ * until the @surface is destroyed. Just calling cairo_surface_finish() is
+ * not enough.
+ **/
+void
+_cairo_surface_release_device_reference (cairo_surface_t *surface)
+{
+    assert (surface->owns_device);
+
+    cairo_device_destroy (surface->device);
+    surface->owns_device = FALSE;
+}
+
+/**
+ * cairo_surface_get_user_data:
+ * @surface: a #cairo_surface_t
+ * @key: the address of the #cairo_user_data_key_t the user data was
+ * attached to
+ *
+ * Return user data previously attached to @surface using the specified
+ * key.  If no user data has been attached with the given key this
+ * function returns %NULL.
+ *
+ * Return value: the user data previously attached or %NULL.
+ **/
+void *
+cairo_surface_get_user_data (cairo_surface_t            *surface,
+                            const cairo_user_data_key_t *key)
+{
+    return _cairo_user_data_array_get_data (&surface->user_data, key);
+}
+
+/**
+ * cairo_surface_set_user_data:
+ * @surface: a #cairo_surface_t
+ * @key: the address of a #cairo_user_data_key_t to attach the user data to
+ * @user_data: the user data to attach to the surface
+ * @destroy: a #cairo_destroy_func_t which will be called when the
+ * surface is destroyed or when new user data is attached using the
+ * same key.
+ *
+ * Attach user data to @surface.  To remove user data from a surface,
+ * call this function with the key that was used to set it and %NULL
+ * for @data.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY if a
+ * slot could not be allocated for the user data.
+ **/
+cairo_status_t
+cairo_surface_set_user_data (cairo_surface_t            *surface,
+                            const cairo_user_data_key_t *key,
+                            void                        *user_data,
+                            cairo_destroy_func_t        destroy)
+{
+    if (CAIRO_REFERENCE_COUNT_IS_INVALID (&surface->ref_count))
+       return surface->status;
+
+    return _cairo_user_data_array_set_data (&surface->user_data,
+                                           key, user_data, destroy);
+}
+
+/**
+ * cairo_surface_get_mime_data:
+ * @surface: a #cairo_surface_t
+ * @mime_type: the mime type of the image data
+ * @data: the image data to attached to the surface
+ * @length: the length of the image data
+ *
+ * Return mime data previously attached to @surface using the
+ * specified mime type.  If no data has been attached with the given
+ * mime type, @data is set %NULL.
+ *
+ * Since: 1.10
+ **/
+void
+cairo_surface_get_mime_data (cairo_surface_t           *surface,
+                             const char                        *mime_type,
+                             const unsigned char       **data,
+                             unsigned long             *length)
+{
+    cairo_user_data_slot_t *slots;
+    int i, num_slots;
+
+    *data = NULL;
+    *length = 0;
+    if (unlikely (surface->status))
+       return;
+
+    /* The number of mime-types attached to a surface is usually small,
+     * typically zero. Therefore it is quicker to do a strcmp() against
+     * each key than it is to intern the string (i.e. compute a hash,
+     * search the hash table, and do a final strcmp).
+     */
+    num_slots = surface->mime_data.num_elements;
+    slots = _cairo_array_index (&surface->mime_data, 0);
+    for (i = 0; i < num_slots; i++) {
+       if (slots[i].key != NULL && strcmp ((char *) slots[i].key, mime_type) == 0) {
+           cairo_mime_data_t *mime_data = slots[i].user_data;
+
+           *data = mime_data->data;
+           *length = mime_data->length;
+           return;
+       }
+    }
+}
+slim_hidden_def (cairo_surface_get_mime_data);
+
+static void
+_cairo_mime_data_destroy (void *ptr)
+{
+    cairo_mime_data_t *mime_data = ptr;
+
+    if (! _cairo_reference_count_dec_and_test (&mime_data->ref_count))
+       return;
+
+    if (mime_data->destroy && mime_data->closure)
+       mime_data->destroy (mime_data->closure);
+
+    free (mime_data);
+}
+
+/**
+ * CAIRO_MIME_TYPE_JP2:
+ *
+ * The Joint Photographic Experts Group (JPEG) 2000 image coding standard (ISO/IEC 15444-1).
+ *
+ * @Since: 1.10
+ */
+
+/**
+ * CAIRO_MIME_TYPE_JPEG:
+ *
+ * The Joint Photographic Experts Group (JPEG) image coding standard (ISO/IEC 10918-1).
+ *
+ * @Since: 1.10
+ */
+
+/**
+ * CAIRO_MIME_TYPE_PNG:
+ *
+ * The Portable Network Graphics image file format (ISO/IEC 15948).
+ *
+ * @Since: 1.10
+ */
+
+/**
+ * CAIRO_MIME_TYPE_URI:
+ *
+ * URI for an image file (unofficial MIME type).
+ *
+ * @Since: 1.10
+ */
+
+/**
+ * CAIRO_MIME_TYPE_UNIQUE_ID:
+ *
+ * Unique identifier for a surface (cairo specific MIME type).
+ *
+ * @Since: 1.12
+ */
+
+/**
+ * cairo_surface_set_mime_data:
+ * @surface: a #cairo_surface_t
+ * @mime_type: the MIME type of the image data
+ * @data: the image data to attach to the surface
+ * @length: the length of the image data
+ * @destroy: a #cairo_destroy_func_t which will be called when the
+ * surface is destroyed or when new image data is attached using the
+ * same mime type.
+ * @closure: the data to be passed to the @destroy notifier
+ *
+ * Attach an image in the format @mime_type to @surface. To remove
+ * the data from a surface, call this function with same mime type
+ * and %NULL for @data.
+ *
+ * The attached image (or filename) data can later be used by backends
+ * which support it (currently: PDF, PS, SVG and Win32 Printing
+ * surfaces) to emit this data instead of making a snapshot of the
+ * @surface.  This approach tends to be faster and requires less
+ * memory and disk space.
+ *
+ * The recognized MIME types are the following: %CAIRO_MIME_TYPE_JPEG,
+ * %CAIRO_MIME_TYPE_PNG, %CAIRO_MIME_TYPE_JP2, %CAIRO_MIME_TYPE_URI.
+ *
+ * See corresponding backend surface docs for details about which MIME
+ * types it can handle. Caution: the associated MIME data will be
+ * discarded if you draw on the surface afterwards. Use this function
+ * with care.
+ *
+ * Since: 1.10
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY if a
+ * slot could not be allocated for the user data.
+ **/
+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 status;
+    cairo_mime_data_t *mime_data;
+
+    if (unlikely (surface->status))
+       return surface->status;
+    if (unlikely (surface->finished))
+       return _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
+
+    status = _cairo_intern_string (&mime_type, -1);
+    if (unlikely (status))
+       return _cairo_surface_set_error (surface, status);
+
+    if (data != NULL) {
+       mime_data = malloc (sizeof (cairo_mime_data_t));
+       if (unlikely (mime_data == NULL))
+           return _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+       CAIRO_REFERENCE_COUNT_INIT (&mime_data->ref_count, 1);
+
+       mime_data->data = (unsigned char *) data;
+       mime_data->length = length;
+       mime_data->destroy = destroy;
+       mime_data->closure = closure;
+    } else
+       mime_data = NULL;
+
+    status = _cairo_user_data_array_set_data (&surface->mime_data,
+                                             (cairo_user_data_key_t *) mime_type,
+                                             mime_data,
+                                             _cairo_mime_data_destroy);
+    if (unlikely (status)) {
+       free (mime_data);
+
+       return _cairo_surface_set_error (surface, status);
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+slim_hidden_def (cairo_surface_set_mime_data);
+
+static void
+_cairo_mime_data_reference (const void *key, void *elt, void *closure)
+{
+    cairo_mime_data_t *mime_data = elt;
+
+    _cairo_reference_count_inc (&mime_data->ref_count);
+}
+
+cairo_status_t
+_cairo_surface_copy_mime_data (cairo_surface_t *dst,
+                              cairo_surface_t *src)
+{
+    cairo_status_t status;
+
+    if (dst->status)
+       return dst->status;
+
+    if (src->status)
+       return _cairo_surface_set_error (dst, src->status);
+
+    /* first copy the mime-data, discarding any already set on dst */
+    status = _cairo_user_data_array_copy (&dst->mime_data, &src->mime_data);
+    if (unlikely (status))
+       return _cairo_surface_set_error (dst, status);
+
+    /* now increment the reference counters for the copies */
+    _cairo_user_data_array_foreach (&dst->mime_data,
+                                   _cairo_mime_data_reference,
+                                   NULL);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+/**
+ * _cairo_surface_set_font_options:
+ * @surface: a #cairo_surface_t
+ * @options: a #cairo_font_options_t object that contains the
+ *   options to use for this surface instead of backend's default
+ *   font options.
+ *
+ * Sets the default font rendering options for the surface.
+ * This is useful to correctly propagate default font options when
+ * falling back to an image surface in a backend implementation.
+ * This affects the options returned in cairo_surface_get_font_options().
+ *
+ * If @options is %NULL the surface options are reset to those of
+ * the backend default.
+ **/
+void
+_cairo_surface_set_font_options (cairo_surface_t       *surface,
+                                cairo_font_options_t  *options)
+{
+    if (surface->status)
+       return;
+
+    assert (surface->snapshot_of == NULL);
+
+    if (surface->finished) {
+       _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
+       return;
+    }
+
+    if (options) {
+       surface->has_font_options = TRUE;
+       _cairo_font_options_init_copy (&surface->font_options, options);
+    } else {
+       surface->has_font_options = FALSE;
+    }
+}
+
+/**
+ * cairo_surface_get_font_options:
+ * @surface: a #cairo_surface_t
+ * @options: a #cairo_font_options_t object into which to store
+ *   the retrieved options. All existing values are overwritten
+ *
+ * Retrieves the default font rendering options for the surface.
+ * This allows display surfaces to report the correct subpixel order
+ * for rendering on them, print surfaces to disable hinting of
+ * metrics and so forth. The result can then be used with
+ * cairo_scaled_font_create().
+ **/
+void
+cairo_surface_get_font_options (cairo_surface_t       *surface,
+                               cairo_font_options_t  *options)
+{
+    if (cairo_font_options_status (options))
+       return;
+
+    if (surface->status) {
+       _cairo_font_options_init_default (options);
+       return;
+    }
+
+    if (! surface->has_font_options) {
+       surface->has_font_options = TRUE;
+
+       _cairo_font_options_init_default (&surface->font_options);
+
+       if (!surface->finished && surface->backend->get_font_options) {
+           surface->backend->get_font_options (surface, &surface->font_options);
+       }
+    }
+
+    _cairo_font_options_init_copy (options, &surface->font_options);
+}
+slim_hidden_def (cairo_surface_get_font_options);
+
+/**
+ * cairo_surface_flush:
+ * @surface: a #cairo_surface_t
+ *
+ * Do any pending drawing for the surface and also restore any
+ * temporary modifications cairo has made to the surface's
+ * state. This function must be called before switching from
+ * drawing on the surface with cairo to drawing on it directly
+ * with native APIs. If the surface doesn't support direct access,
+ * then this function does nothing.
+ **/
+void
+cairo_surface_flush (cairo_surface_t *surface)
+{
+    cairo_status_t status;
+
+    if (surface->status)
+       return;
+
+    if (surface->finished)
+       return;
+
+    /* update the current snapshots *before* the user updates the surface */
+    _cairo_surface_detach_snapshots (surface);
+    _cairo_surface_detach_mime_data (surface);
+
+    if (surface->backend->flush) {
+       status = surface->backend->flush (surface);
+       if (unlikely (status))
+           _cairo_surface_set_error (surface, status);
+    }
+}
+slim_hidden_def (cairo_surface_flush);
+
+/**
+ * cairo_surface_mark_dirty:
+ * @surface: a #cairo_surface_t
+ *
+ * Tells cairo that drawing has been done to surface using means other
+ * than cairo, and that cairo should reread any cached areas. Note
+ * that you must call cairo_surface_flush() before doing such drawing.
+ */
+void
+cairo_surface_mark_dirty (cairo_surface_t *surface)
+{
+    cairo_surface_mark_dirty_rectangle (surface, 0, 0, -1, -1);
+}
+slim_hidden_def (cairo_surface_mark_dirty);
+
+/**
+ * cairo_surface_mark_dirty_rectangle:
+ * @surface: a #cairo_surface_t
+ * @x: X coordinate of dirty rectangle
+ * @y: Y coordinate of dirty rectangle
+ * @width: width of dirty rectangle
+ * @height: height of dirty rectangle
+ *
+ * Like cairo_surface_mark_dirty(), but drawing has been done only to
+ * the specified rectangle, so that cairo can retain cached contents
+ * for other parts of the surface.
+ *
+ * Any cached clip set on the surface will be reset by this function,
+ * to make sure that future cairo calls have the clip set that they
+ * expect.
+ */
+void
+cairo_surface_mark_dirty_rectangle (cairo_surface_t *surface,
+                                   int              x,
+                                   int              y,
+                                   int              width,
+                                   int              height)
+{
+    cairo_status_t status;
+
+    if (unlikely (surface->status))
+       return;
+
+    assert (surface->snapshot_of == NULL);
+
+    if (unlikely (surface->finished)) {
+       _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
+       return;
+    }
+
+    /* The application *should* have called cairo_surface_flush() before
+     * modifying the surface independently of cairo (and thus having to
+     * call mark_dirty()). */
+    assert (! _cairo_surface_has_snapshots (surface));
+    assert (! _cairo_surface_has_mime_data (surface));
+
+    surface->is_clear = FALSE;
+    surface->serial++;
+
+    if (surface->backend->mark_dirty_rectangle != NULL) {
+       /* XXX: FRAGILE: We're ignoring the scaling component of
+        * device_transform here. I don't know what the right thing to
+        * do would actually be if there were some scaling here, but
+        * we avoid this since device_transfom scaling is not exported
+        * publicly and mark_dirty is not used internally. */
+       status = surface->backend->mark_dirty_rectangle (surface,
+                                                         x + surface->device_transform.x0,
+                                                         y + surface->device_transform.y0,
+                                                        width, height);
+
+       if (unlikely (status))
+           _cairo_surface_set_error (surface, status);
+    }
+}
+slim_hidden_def (cairo_surface_mark_dirty_rectangle);
+
+/**
+ * _cairo_surface_set_device_scale:
+ * @surface: a #cairo_surface_t
+ * @sx: a scale factor in the X direction
+ * @sy: a scale factor in the Y direction
+ *
+ * Private function for setting an extra scale factor to affect all
+ * drawing to a surface. This is used, for example, when replaying a
+ * recording surface to an image fallback intended for an eventual
+ * vector-oriented backend. Since the recording surface will record
+ * coordinates in one backend space, but the image fallback uses a
+ * different backend space, (differing by the fallback resolution
+ * scale factors), we need a scale factor correction.
+ *
+ * Caution: Not all places we use device transform correctly handle
+ * both a translate and a scale.  An audit would be nice.
+ **/
+void
+_cairo_surface_set_device_scale (cairo_surface_t *surface,
+                                double           sx,
+                                double           sy)
+{
+    cairo_status_t status;
+
+    if (unlikely (surface->status))
+       return;
+
+    assert (surface->snapshot_of == NULL);
+
+    if (unlikely (surface->finished)) {
+       _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
+       return;
+    }
+
+    _cairo_surface_begin_modification (surface);
+
+    surface->device_transform.xx = sx;
+    surface->device_transform.yy = sy;
+    surface->device_transform.xy = 0.0;
+    surface->device_transform.yx = 0.0;
+
+    surface->device_transform_inverse = surface->device_transform;
+    status = cairo_matrix_invert (&surface->device_transform_inverse);
+    /* should always be invertible unless given pathological input */
+    assert (status == CAIRO_STATUS_SUCCESS);
+
+    _cairo_observers_notify (&surface->device_transform_observers, surface);
+}
+
+/**
+ * cairo_surface_set_device_offset:
+ * @surface: a #cairo_surface_t
+ * @x_offset: the offset in the X direction, in device units
+ * @y_offset: the offset in the Y direction, in device units
+ *
+ * Sets an offset that is added to the device coordinates determined
+ * by the CTM when drawing to @surface. One use case for this function
+ * is when we want to create a #cairo_surface_t that redirects drawing
+ * for a portion of an onscreen surface to an offscreen surface in a
+ * way that is completely invisible to the user of the cairo
+ * API. Setting a transformation via cairo_translate() isn't
+ * sufficient to do this, since functions like
+ * cairo_device_to_user() will expose the hidden offset.
+ *
+ * Note that the offset affects drawing to the surface as well as
+ * using the surface in a source pattern.
+ **/
+void
+cairo_surface_set_device_offset (cairo_surface_t *surface,
+                                double           x_offset,
+                                double           y_offset)
+{
+    cairo_status_t status;
+
+    if (unlikely (surface->status))
+       return;
+
+    assert (surface->snapshot_of == NULL);
+
+    if (unlikely (surface->finished)) {
+       _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
+       return;
+    }
+
+    _cairo_surface_begin_modification (surface);
+
+    surface->device_transform.x0 = x_offset;
+    surface->device_transform.y0 = y_offset;
+
+    surface->device_transform_inverse = surface->device_transform;
+    status = cairo_matrix_invert (&surface->device_transform_inverse);
+    /* should always be invertible unless given pathological input */
+    assert (status == CAIRO_STATUS_SUCCESS);
+
+    _cairo_observers_notify (&surface->device_transform_observers, surface);
+}
+slim_hidden_def (cairo_surface_set_device_offset);
+
+/**
+ * cairo_surface_get_device_offset:
+ * @surface: a #cairo_surface_t
+ * @x_offset: the offset in the X direction, in device units
+ * @y_offset: the offset in the Y direction, in device units
+ *
+ * This function returns the previous device offset set by
+ * cairo_surface_set_device_offset().
+ *
+ * Since: 1.2
+ **/
+void
+cairo_surface_get_device_offset (cairo_surface_t *surface,
+                                double          *x_offset,
+                                double          *y_offset)
+{
+    if (x_offset)
+       *x_offset = surface->device_transform.x0;
+    if (y_offset)
+       *y_offset = surface->device_transform.y0;
+}
+slim_hidden_def (cairo_surface_get_device_offset);
+
+/**
+ * cairo_surface_set_fallback_resolution:
+ * @surface: a #cairo_surface_t
+ * @x_pixels_per_inch: horizontal setting for pixels per inch
+ * @y_pixels_per_inch: vertical setting for pixels per inch
+ *
+ * Set the horizontal and vertical resolution for image fallbacks.
+ *
+ * When certain operations aren't supported natively by a backend,
+ * cairo will fallback by rendering operations to an image and then
+ * overlaying that image onto the output. For backends that are
+ * natively vector-oriented, this function can be used to set the
+ * resolution used for these image fallbacks, (larger values will
+ * result in more detailed images, but also larger file sizes).
+ *
+ * Some examples of natively vector-oriented backends are the ps, pdf,
+ * and svg backends.
+ *
+ * For backends that are natively raster-oriented, image fallbacks are
+ * still possible, but they are always performed at the native
+ * device resolution. So this function has no effect on those
+ * backends.
+ *
+ * Note: The fallback resolution only takes effect at the time of
+ * completing a page (with cairo_show_page() or cairo_copy_page()) so
+ * there is currently no way to have more than one fallback resolution
+ * in effect on a single page.
+ *
+ * The default fallback resoultion is 300 pixels per inch in both
+ * dimensions.
+ *
+ * Since: 1.2
+ **/
+void
+cairo_surface_set_fallback_resolution (cairo_surface_t *surface,
+                                      double            x_pixels_per_inch,
+                                      double            y_pixels_per_inch)
+{
+    if (unlikely (surface->status))
+       return;
+
+    assert (surface->snapshot_of == NULL);
+
+    if (unlikely (surface->finished)) {
+       _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
+       return;
+    }
+
+    if (x_pixels_per_inch <= 0 || y_pixels_per_inch <= 0) {
+       /* XXX Could delay raising the error until we fallback, but throwing
+        * the error here means that we can catch the real culprit.
+        */
+       _cairo_surface_set_error (surface, CAIRO_STATUS_INVALID_MATRIX);
+       return;
+    }
+
+    _cairo_surface_begin_modification (surface);
+
+    surface->x_fallback_resolution = x_pixels_per_inch;
+    surface->y_fallback_resolution = y_pixels_per_inch;
+}
+slim_hidden_def (cairo_surface_set_fallback_resolution);
+
+/**
+ * cairo_surface_get_fallback_resolution:
+ * @surface: a #cairo_surface_t
+ * @x_pixels_per_inch: horizontal pixels per inch
+ * @y_pixels_per_inch: vertical pixels per inch
+ *
+ * This function returns the previous fallback resolution set by
+ * cairo_surface_set_fallback_resolution(), or default fallback
+ * resolution if never set.
+ *
+ * Since: 1.8
+ **/
+void
+cairo_surface_get_fallback_resolution (cairo_surface_t *surface,
+                                      double           *x_pixels_per_inch,
+                                      double           *y_pixels_per_inch)
+{
+    if (x_pixels_per_inch)
+       *x_pixels_per_inch = surface->x_fallback_resolution;
+    if (y_pixels_per_inch)
+       *y_pixels_per_inch = surface->y_fallback_resolution;
+}
+
+cairo_bool_t
+_cairo_surface_has_device_transform (cairo_surface_t *surface)
+{
+    return ! _cairo_matrix_is_identity (&surface->device_transform);
+}
+
+/**
+ * _cairo_surface_acquire_source_image:
+ * @surface: a #cairo_surface_t
+ * @image_out: location to store a pointer to an image surface that
+ *    has identical contents to @surface. This surface could be @surface
+ *    itself, a surface held internal to @surface, or it could be a new
+ *    surface with a copy of the relevant portion of @surface.
+ * @image_extra: location to store image specific backend data
+ *
+ * Gets an image surface to use when drawing as a fallback when drawing with
+ * @surface as a source. _cairo_surface_release_source_image() must be called
+ * when finished.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS if an image was stored in @image_out.
+ * %CAIRO_INT_STATUS_UNSUPPORTED if an image cannot be retrieved for the specified
+ * surface. Or %CAIRO_STATUS_NO_MEMORY.
+ **/
+cairo_status_t
+_cairo_surface_acquire_source_image (cairo_surface_t         *surface,
+                                    cairo_image_surface_t  **image_out,
+                                    void                   **image_extra)
+{
+    cairo_status_t status;
+
+    if (unlikely (surface->status))
+       return surface->status;
+
+    assert (!surface->finished);
+
+    if (surface->backend->acquire_source_image == NULL)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    status = surface->backend->acquire_source_image (surface,
+                                                    image_out, image_extra);
+    if (unlikely (status))
+       return _cairo_surface_set_error (surface, status);
+
+    _cairo_debug_check_image_surface_is_defined (&(*image_out)->base);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+/**
+ * _cairo_surface_release_source_image:
+ * @surface: a #cairo_surface_t
+ * @image_extra: same as return from the matching _cairo_surface_acquire_source_image()
+ *
+ * Releases any resources obtained with _cairo_surface_acquire_source_image()
+ **/
+void
+_cairo_surface_release_source_image (cairo_surface_t        *surface,
+                                    cairo_image_surface_t  *image,
+                                    void                   *image_extra)
+{
+    assert (!surface->finished);
+
+    if (surface->backend->release_source_image)
+       surface->backend->release_source_image (surface, image, image_extra);
+}
+
+static cairo_status_t
+_pattern_has_error (const cairo_pattern_t *pattern)
+{
+    const cairo_surface_pattern_t *spattern;
+
+    if (unlikely (pattern->status))
+       return pattern->status;
+
+    if (pattern->type != CAIRO_PATTERN_TYPE_SURFACE)
+       return CAIRO_STATUS_SUCCESS;
+
+    spattern = (const cairo_surface_pattern_t *) pattern;
+    if (unlikely (spattern->surface->status))
+       return spattern->surface->status;
+
+    if (unlikely (spattern->surface->finished))
+       return _cairo_error (CAIRO_STATUS_SURFACE_FINISHED);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_bool_t
+nothing_to_do (cairo_surface_t *surface,
+              cairo_operator_t op,
+              const cairo_pattern_t *source)
+{
+    if (_cairo_pattern_is_clear (source)) {
+       if (op == CAIRO_OPERATOR_OVER || op == CAIRO_OPERATOR_ADD)
+           return TRUE;
+
+       if (op == CAIRO_OPERATOR_SOURCE)
+           op = CAIRO_OPERATOR_CLEAR;
+    }
+
+    if (op == CAIRO_OPERATOR_CLEAR && surface->is_clear)
+       return TRUE;
+
+    if (op == CAIRO_OPERATOR_ATOP && (surface->content & CAIRO_CONTENT_COLOR) ==0)
+       return TRUE;
+
+    return FALSE;
+}
+
+cairo_status_t
+_cairo_surface_paint (cairo_surface_t          *surface,
+                     cairo_operator_t           op,
+                     const cairo_pattern_t     *source,
+                     const cairo_clip_t        *clip)
+{
+    cairo_int_status_t status;
+
+    if (unlikely (surface->status))
+       return surface->status;
+
+    if (_cairo_clip_is_all_clipped (clip))
+       return CAIRO_STATUS_SUCCESS;
+
+    status = _pattern_has_error (source);
+    if (unlikely (status))
+       return status;
+
+    if (nothing_to_do (surface, op, source))
+       return CAIRO_STATUS_SUCCESS;
+
+    _cairo_surface_begin_modification (surface);
+
+    status = surface->backend->paint (surface, op, source, clip);
+    if (status != CAIRO_INT_STATUS_NOTHING_TO_DO) {
+       surface->is_clear = op == CAIRO_OPERATOR_CLEAR && clip == NULL;
+       surface->serial++;
+    }
+
+    return _cairo_surface_set_error (surface, status);
+}
+
+cairo_status_t
+_cairo_surface_mask (cairo_surface_t           *surface,
+                    cairo_operator_t            op,
+                    const cairo_pattern_t      *source,
+                    const cairo_pattern_t      *mask,
+                    const cairo_clip_t         *clip)
+{
+    cairo_int_status_t status;
+
+    if (unlikely (surface->status))
+       return surface->status;
+
+    if (_cairo_clip_is_all_clipped (clip))
+       return CAIRO_STATUS_SUCCESS;
+
+    /* If the mask is blank, this is just an expensive no-op */
+    if (_cairo_pattern_is_clear (mask) &&
+       _cairo_operator_bounded_by_mask (op))
+    {
+       return CAIRO_STATUS_SUCCESS;
+    }
+
+    status = _pattern_has_error (source);
+    if (unlikely (status))
+       return status;
+
+    status = _pattern_has_error (mask);
+    if (unlikely (status))
+       return status;
+
+    if (nothing_to_do (surface, op, source))
+       return CAIRO_STATUS_SUCCESS;
+
+    _cairo_surface_begin_modification (surface);
+
+    status = surface->backend->mask (surface, op, source, mask, clip);
+    if (status != CAIRO_INT_STATUS_NOTHING_TO_DO) {
+       surface->is_clear = FALSE;
+       surface->serial++;
+    }
+
+    return _cairo_surface_set_error (surface, status);
+}
+
+cairo_status_t
+_cairo_surface_fill_stroke (cairo_surface_t        *surface,
+                           cairo_operator_t         fill_op,
+                           const cairo_pattern_t   *fill_source,
+                           cairo_fill_rule_t        fill_rule,
+                           double                   fill_tolerance,
+                           cairo_antialias_t        fill_antialias,
+                           cairo_path_fixed_t      *path,
+                           cairo_operator_t         stroke_op,
+                           const cairo_pattern_t   *stroke_source,
+                           const cairo_stroke_style_t    *stroke_style,
+                           const cairo_matrix_t            *stroke_ctm,
+                           const cairo_matrix_t            *stroke_ctm_inverse,
+                           double                   stroke_tolerance,
+                           cairo_antialias_t        stroke_antialias,
+                           const cairo_clip_t      *clip)
+{
+    cairo_int_status_t status;
+
+    if (unlikely (surface->status))
+       return surface->status;
+
+    if (_cairo_clip_is_all_clipped (clip))
+       return CAIRO_STATUS_SUCCESS;
+
+    if (surface->is_clear &&
+       fill_op == CAIRO_OPERATOR_CLEAR &&
+       stroke_op == CAIRO_OPERATOR_CLEAR)
+    {
+       return CAIRO_STATUS_SUCCESS;
+    }
+
+    status = _pattern_has_error (fill_source);
+    if (unlikely (status))
+       return status;
+
+    status = _pattern_has_error (stroke_source);
+    if (unlikely (status))
+       return status;
+
+    _cairo_surface_begin_modification (surface);
+
+    if (surface->backend->fill_stroke) {
+       cairo_matrix_t dev_ctm = *stroke_ctm;
+       cairo_matrix_t dev_ctm_inverse = *stroke_ctm_inverse;
+
+       status = surface->backend->fill_stroke (surface,
+                                               fill_op, fill_source, fill_rule,
+                                               fill_tolerance, fill_antialias,
+                                               path,
+                                               stroke_op, stroke_source,
+                                               stroke_style,
+                                               &dev_ctm, &dev_ctm_inverse,
+                                               stroke_tolerance, stroke_antialias,
+                                               clip);
+
+       if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+           goto FINISH;
+    }
+
+    status = _cairo_surface_fill (surface, fill_op, fill_source, path,
+                                 fill_rule, fill_tolerance, fill_antialias,
+                                 clip);
+    if (unlikely (status))
+       goto FINISH;
+
+    status = _cairo_surface_stroke (surface, stroke_op, stroke_source, path,
+                                   stroke_style, stroke_ctm, stroke_ctm_inverse,
+                                   stroke_tolerance, stroke_antialias,
+                                   clip);
+    if (unlikely (status))
+       goto FINISH;
+
+  FINISH:
+    if (status != CAIRO_INT_STATUS_NOTHING_TO_DO) {
+       surface->is_clear = FALSE;
+       surface->serial++;
+    }
+
+    return _cairo_surface_set_error (surface, status);
+}
+
+cairo_status_t
+_cairo_surface_stroke (cairo_surface_t                 *surface,
+                      cairo_operator_t                  op,
+                      const cairo_pattern_t            *source,
+                      const cairo_path_fixed_t         *path,
+                      const cairo_stroke_style_t       *stroke_style,
+                      const cairo_matrix_t             *ctm,
+                      const cairo_matrix_t             *ctm_inverse,
+                      double                            tolerance,
+                      cairo_antialias_t                 antialias,
+                      const cairo_clip_t               *clip)
+{
+    cairo_int_status_t status;
+
+    if (unlikely (surface->status))
+       return surface->status;
+
+    if (_cairo_clip_is_all_clipped (clip))
+       return CAIRO_STATUS_SUCCESS;
+
+    status = _pattern_has_error (source);
+    if (unlikely (status))
+       return status;
+
+    if (nothing_to_do (surface, op, source))
+       return CAIRO_STATUS_SUCCESS;
+
+    _cairo_surface_begin_modification (surface);
+
+    status = surface->backend->stroke (surface, op, source,
+                                      path, stroke_style,
+                                      ctm, ctm_inverse,
+                                      tolerance, antialias,
+                                      clip);
+    if (status != CAIRO_INT_STATUS_NOTHING_TO_DO) {
+       surface->is_clear = FALSE;
+       surface->serial++;
+    }
+
+    return _cairo_surface_set_error (surface, status);
+}
+
+cairo_status_t
+_cairo_surface_fill (cairo_surface_t           *surface,
+                    cairo_operator_t            op,
+                    const cairo_pattern_t       *source,
+                    const cairo_path_fixed_t   *path,
+                    cairo_fill_rule_t           fill_rule,
+                    double                      tolerance,
+                    cairo_antialias_t           antialias,
+                    const cairo_clip_t         *clip)
+{
+    cairo_int_status_t status;
+
+    if (unlikely (surface->status))
+       return surface->status;
+
+    if (_cairo_clip_is_all_clipped (clip))
+       return CAIRO_STATUS_SUCCESS;
+
+    status = _pattern_has_error (source);
+    if (unlikely (status))
+       return status;
+
+    if (nothing_to_do (surface, op, source))
+       return CAIRO_STATUS_SUCCESS;
+
+    _cairo_surface_begin_modification (surface);
+
+    status = surface->backend->fill (surface, op, source,
+                                    path, fill_rule,
+                                    tolerance, antialias,
+                                    clip);
+    if (status != CAIRO_INT_STATUS_NOTHING_TO_DO) {
+       surface->is_clear = FALSE;
+       surface->serial++;
+    }
+
+    return _cairo_surface_set_error (surface, status);
+}
+
+/**
+ * cairo_surface_copy_page:
+ * @surface: a #cairo_surface_t
+ *
+ * Emits the current page for backends that support multiple pages,
+ * but doesn't clear it, so that the contents of the current page will
+ * be retained for the next page.  Use cairo_surface_show_page() if you
+ * want to get an empty page after the emission.
+ *
+ * There is a convenience function for this that takes a #cairo_t,
+ * namely cairo_copy_page().
+ *
+ * Since: 1.6
+ */
+void
+cairo_surface_copy_page (cairo_surface_t *surface)
+{
+    if (unlikely (surface->status))
+       return;
+
+    assert (surface->snapshot_of == NULL);
+
+    if (unlikely (surface->finished)) {
+       _cairo_surface_set_error (surface, CAIRO_STATUS_SURFACE_FINISHED);
+       return;
+    }
+
+    /* It's fine if some backends don't implement copy_page */
+    if (surface->backend->copy_page == NULL)
+       return;
+
+    _cairo_surface_set_error (surface, surface->backend->copy_page (surface));
+}
+slim_hidden_def (cairo_surface_copy_page);
+
+/**
+ * cairo_surface_show_page:
+ * @surface: a #cairo_Surface_t
+ *
+ * Emits and clears the current page for backends that support multiple
+ * pages.  Use cairo_surface_copy_page() if you don't want to clear the page.
+ *
+ * There is a convenience function for this that takes a #cairo_t,
+ * namely cairo_show_page().
+ *
+ * Since: 1.6
+ **/
+void
+cairo_surface_show_page (cairo_surface_t *surface)
+{
+    if (unlikely (surface->status))
+       return;
+
+    if (unlikely (surface->finished)) {
+       _cairo_surface_set_error (surface, CAIRO_STATUS_SURFACE_FINISHED);
+       return;
+    }
+
+    _cairo_surface_begin_modification (surface);
+
+    /* It's fine if some backends don't implement show_page */
+    if (surface->backend->show_page == NULL)
+       return;
+
+    _cairo_surface_set_error (surface, surface->backend->show_page (surface));
+}
+slim_hidden_def (cairo_surface_show_page);
+
+/**
+ * _cairo_surface_get_extents:
+ * @surface: the #cairo_surface_t to fetch extents for
+ *
+ * This function returns a bounding box for the surface.  The surface
+ * bounds are defined as a region beyond which no rendering will
+ * possibly be recorded, in other words, it is the maximum extent of
+ * potentially usable coordinates.
+ *
+ * For vector surfaces, (PDF, PS, SVG and recording-surfaces), the surface
+ * might be conceived as unbounded, but we force the user to provide a
+ * maximum size at the time of surface_create. So get_extents uses
+ * that size.
+ *
+ * Note: The coordinates returned are in "backend" space rather than
+ * "surface" space. That is, they are relative to the true (0,0)
+ * origin rather than the device_transform origin. This might seem a
+ * bit inconsistent with other #cairo_surface_t interfaces, but all
+ * current callers are within the surface layer where backend space is
+ * desired.
+ *
+ * This behavior would have to be changed is we ever exported a public
+ * variant of this function.
+ */
+cairo_bool_t
+_cairo_surface_get_extents (cairo_surface_t         *surface,
+                           cairo_rectangle_int_t   *extents)
+{
+    cairo_bool_t bounded;
+
+    bounded = FALSE;
+    if (surface->backend->get_extents != NULL)
+       bounded = surface->backend->get_extents (surface, extents);
+
+    if (! bounded)
+       _cairo_unbounded_rectangle_init (extents);
+
+    return bounded;
+}
+
+/**
+ * cairo_surface_has_show_text_glyphs:
+ * @surface: a #cairo_surface_t
+ *
+ * Returns whether the surface supports
+ * sophisticated cairo_show_text_glyphs() operations.  That is,
+ * whether it actually uses the provided text and cluster data
+ * to a cairo_show_text_glyphs() call.
+ *
+ * Note: Even if this function returns %FALSE, a
+ * cairo_show_text_glyphs() operation targeted at @surface will
+ * still succeed.  It just will
+ * act like a cairo_show_glyphs() operation.  Users can use this
+ * function to avoid computing UTF-8 text and cluster mapping if the
+ * target surface does not use it.
+ *
+ * Return value: %TRUE if @surface supports
+ *               cairo_show_text_glyphs(), %FALSE otherwise
+ *
+ * Since: 1.8
+ **/
+cairo_bool_t
+cairo_surface_has_show_text_glyphs (cairo_surface_t        *surface)
+{
+    if (unlikely (surface->status))
+       return FALSE;
+
+    if (unlikely (surface->finished)) {
+       _cairo_surface_set_error (surface, CAIRO_STATUS_SURFACE_FINISHED);
+       return FALSE;
+    }
+
+    if (surface->backend->has_show_text_glyphs)
+       return surface->backend->has_show_text_glyphs (surface);
+    else
+       return surface->backend->show_text_glyphs != NULL;
+}
+slim_hidden_def (cairo_surface_has_show_text_glyphs);
+
+/* Note: the backends may modify the contents of the glyph array as long as
+ * they do not return %CAIRO_INT_STATUS_UNSUPPORTED. This makes it possible to
+ * avoid copying the array again and again, and edit it in-place.
+ * Backends are in fact free to use the array as a generic buffer as they
+ * see fit.
+ *
+ * For show_glyphs backend method, and NOT for show_text_glyphs method,
+ * when they do return UNSUPPORTED, they may adjust remaining_glyphs to notify
+ * that they have successfully rendered some of the glyphs (from the beginning
+ * of the array), but not all.  If they don't touch remaining_glyphs, it
+ * defaults to all glyphs.
+ *
+ * See commits 5a9642c5746fd677aed35ce620ce90b1029b1a0c and
+ * 1781e6018c17909311295a9cc74b70500c6b4d0a for the rationale.
+ */
+cairo_status_t
+_cairo_surface_show_text_glyphs (cairo_surface_t           *surface,
+                                cairo_operator_t            op,
+                                const cairo_pattern_t      *source,
+                                const char                 *utf8,
+                                int                         utf8_len,
+                                cairo_glyph_t              *glyphs,
+                                int                         num_glyphs,
+                                const cairo_text_cluster_t *clusters,
+                                int                         num_clusters,
+                                cairo_text_cluster_flags_t  cluster_flags,
+                                cairo_scaled_font_t        *scaled_font,
+                                const cairo_clip_t             *clip)
+{
+    cairo_int_status_t status;
+    cairo_scaled_font_t *dev_scaled_font = scaled_font;
+
+    if (unlikely (surface->status))
+       return surface->status;
+
+    if (num_glyphs == 0 && utf8_len == 0)
+       return CAIRO_STATUS_SUCCESS;
+
+    if (_cairo_clip_is_all_clipped (clip))
+       return CAIRO_STATUS_SUCCESS;
+
+    status = _pattern_has_error (source);
+    if (unlikely (status))
+       return status;
+
+    if (nothing_to_do (surface, op, source))
+       return CAIRO_STATUS_SUCCESS;
+
+    _cairo_surface_begin_modification (surface);
+
+    if (_cairo_surface_has_device_transform (surface) &&
+       ! _cairo_matrix_is_integer_translation (&surface->device_transform, NULL, NULL))
+    {
+       cairo_font_options_t font_options;
+       cairo_matrix_t dev_ctm, font_matrix;
+
+       cairo_scaled_font_get_font_matrix (scaled_font, &font_matrix);
+       cairo_scaled_font_get_ctm (scaled_font, &dev_ctm);
+       cairo_matrix_multiply (&dev_ctm, &dev_ctm, &surface->device_transform);
+       cairo_scaled_font_get_font_options (scaled_font, &font_options);
+       dev_scaled_font = cairo_scaled_font_create (cairo_scaled_font_get_font_face (scaled_font),
+                                                   &font_matrix,
+                                                   &dev_ctm,
+                                                   &font_options);
+    }
+    status = cairo_scaled_font_status (dev_scaled_font);
+    if (unlikely (status))
+       return _cairo_surface_set_error (surface, status);
+
+    status = CAIRO_INT_STATUS_UNSUPPORTED;
+
+    /* The logic here is duplicated in _cairo_analysis_surface show_glyphs and
+     * show_text_glyphs.  Keep in synch. */
+    if (clusters) {
+       /* A real show_text_glyphs call.  Try show_text_glyphs backend
+        * method first */
+       if (surface->backend->show_text_glyphs != NULL) {
+           status = surface->backend->show_text_glyphs (surface, op,
+                                                        source,
+                                                        utf8, utf8_len,
+                                                        glyphs, num_glyphs,
+                                                        clusters, num_clusters, cluster_flags,
+                                                        dev_scaled_font,
+                                                        clip);
+       }
+       if (status == CAIRO_INT_STATUS_UNSUPPORTED &&
+           surface->backend->show_glyphs)
+       {
+           status = surface->backend->show_glyphs (surface, op,
+                                                   source,
+                                                   glyphs, num_glyphs,
+                                                   dev_scaled_font,
+                                                   clip);
+       }
+    } else {
+       /* A mere show_glyphs call.  Try show_glyphs backend method first */
+       if (surface->backend->show_glyphs != NULL) {
+           status = surface->backend->show_glyphs (surface, op,
+                                                   source,
+                                                   glyphs, num_glyphs,
+                                                   dev_scaled_font,
+                                                   clip);
+       } else if (surface->backend->show_text_glyphs != NULL) {
+           /* Intentionally only try show_text_glyphs method for show_glyphs
+            * calls if backend does not have show_glyphs.  If backend has
+            * both methods implemented, we don't fallback from show_glyphs to
+            * show_text_glyphs, and hence the backend can assume in its
+            * show_text_glyphs call that clusters is not NULL (which also
+            * implies that UTF-8 is not NULL, unless the text is
+            * zero-length).
+            */
+           status = surface->backend->show_text_glyphs (surface, op,
+                                                        source,
+                                                        utf8, utf8_len,
+                                                        glyphs, num_glyphs,
+                                                        clusters, num_clusters, cluster_flags,
+                                                        dev_scaled_font,
+                                                        clip);
+       }
+    }
+
+    if (dev_scaled_font != scaled_font)
+       cairo_scaled_font_destroy (dev_scaled_font);
+
+    if (status != CAIRO_INT_STATUS_NOTHING_TO_DO) {
+       surface->is_clear = FALSE;
+       surface->serial++;
+    }
+
+    return _cairo_surface_set_error (surface, status);
+}
+
+/**
+ * _cairo_surface_set_resolution
+ * @surface: the surface
+ * @x_res: x resolution, in dpi
+ * @y_res: y resolution, in dpi
+ *
+ * Set the actual surface resolution of @surface to the given x and y DPI.
+ * Mainly used for correctly computing the scale factor when fallback
+ * rendering needs to take place in the paginated surface.
+ */
+void
+_cairo_surface_set_resolution (cairo_surface_t *surface,
+                              double x_res,
+                              double y_res)
+{
+    if (surface->status)
+       return;
+
+    surface->x_resolution = x_res;
+    surface->y_resolution = y_res;
+}
+
+cairo_surface_t *
+_cairo_surface_create_in_error (cairo_status_t status)
+{
+    assert (status < CAIRO_STATUS_LAST_STATUS);
+    switch (status) {
+    case CAIRO_STATUS_NO_MEMORY:
+       return (cairo_surface_t *) &_cairo_surface_nil;
+    case CAIRO_STATUS_SURFACE_TYPE_MISMATCH:
+       return (cairo_surface_t *) &_cairo_surface_nil_surface_type_mismatch;
+    case CAIRO_STATUS_INVALID_STATUS:
+       return (cairo_surface_t *) &_cairo_surface_nil_invalid_status;
+    case CAIRO_STATUS_INVALID_CONTENT:
+       return (cairo_surface_t *) &_cairo_surface_nil_invalid_content;
+    case CAIRO_STATUS_INVALID_FORMAT:
+       return (cairo_surface_t *) &_cairo_surface_nil_invalid_format;
+    case CAIRO_STATUS_INVALID_VISUAL:
+       return (cairo_surface_t *) &_cairo_surface_nil_invalid_visual;
+    case CAIRO_STATUS_READ_ERROR:
+       return (cairo_surface_t *) &_cairo_surface_nil_read_error;
+    case CAIRO_STATUS_WRITE_ERROR:
+       return (cairo_surface_t *) &_cairo_surface_nil_write_error;
+    case CAIRO_STATUS_FILE_NOT_FOUND:
+       return (cairo_surface_t *) &_cairo_surface_nil_file_not_found;
+    case CAIRO_STATUS_TEMP_FILE_ERROR:
+       return (cairo_surface_t *) &_cairo_surface_nil_temp_file_error;
+    case CAIRO_STATUS_INVALID_STRIDE:
+       return (cairo_surface_t *) &_cairo_surface_nil_invalid_stride;
+    case CAIRO_STATUS_INVALID_SIZE:
+       return (cairo_surface_t *) &_cairo_surface_nil_invalid_size;
+    case CAIRO_STATUS_DEVICE_TYPE_MISMATCH:
+       return (cairo_surface_t *) &_cairo_surface_nil_device_type_mismatch;
+    case CAIRO_STATUS_DEVICE_ERROR:
+       return (cairo_surface_t *) &_cairo_surface_nil_device_error;
+    case CAIRO_STATUS_SUCCESS:
+    case CAIRO_STATUS_LAST_STATUS:
+       ASSERT_NOT_REACHED;
+       /* fall-through */
+    case CAIRO_STATUS_INVALID_RESTORE:
+    case CAIRO_STATUS_INVALID_POP_GROUP:
+    case CAIRO_STATUS_NO_CURRENT_POINT:
+    case CAIRO_STATUS_INVALID_MATRIX:
+    case CAIRO_STATUS_NULL_POINTER:
+    case CAIRO_STATUS_INVALID_STRING:
+    case CAIRO_STATUS_INVALID_PATH_DATA:
+    case CAIRO_STATUS_SURFACE_FINISHED:
+    case CAIRO_STATUS_PATTERN_TYPE_MISMATCH:
+    case CAIRO_STATUS_INVALID_DASH:
+    case CAIRO_STATUS_INVALID_DSC_COMMENT:
+    case CAIRO_STATUS_INVALID_INDEX:
+    case CAIRO_STATUS_CLIP_NOT_REPRESENTABLE:
+    case CAIRO_STATUS_FONT_TYPE_MISMATCH:
+    case CAIRO_STATUS_USER_FONT_IMMUTABLE:
+    case CAIRO_STATUS_USER_FONT_ERROR:
+    case CAIRO_STATUS_NEGATIVE_COUNT:
+    case CAIRO_STATUS_INVALID_CLUSTERS:
+    case CAIRO_STATUS_INVALID_SLANT:
+    case CAIRO_STATUS_INVALID_WEIGHT:
+    case CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED:
+    case CAIRO_STATUS_INVALID_MESH_CONSTRUCTION:
+    case CAIRO_STATUS_DEVICE_FINISHED:
+    default:
+       _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+       return (cairo_surface_t *) &_cairo_surface_nil;
+    }
+}
+
+cairo_surface_t *
+_cairo_int_surface_create_in_error (cairo_int_status_t status)
+{
+    if (status < CAIRO_INT_STATUS_LAST_STATUS)
+       return _cairo_surface_create_in_error (status);
+
+    switch ((int)status) {
+    case CAIRO_INT_STATUS_UNSUPPORTED:
+       return (cairo_surface_t *) &_cairo_surface_nil_unsupported;
+    case CAIRO_INT_STATUS_NOTHING_TO_DO:
+       return (cairo_surface_t *) &_cairo_surface_nil_nothing_to_do;
+    default:
+       _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+       return (cairo_surface_t *) &_cairo_surface_nil;
+    }
+}
+
+/*  LocalWords:  rasterized
+ */
diff --git a/src/cairo-svg-surface-private.h b/src/cairo-svg-surface-private.h
new file mode 100644 (file)
index 0000000..ddbf464
--- /dev/null
@@ -0,0 +1,74 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2004 Red Hat, Inc
+ * Copyright © 2005-2006 Emmanuel Pacaud <emmanuel.pacaud@free.fr>
+ * Copyright © 2006 Red Hat, Inc
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *     Kristian Høgsberg <krh@redhat.com>
+ *     Emmanuel Pacaud <emmanuel.pacaud@univ-poitiers.fr>
+ *     Carl Worth <cworth@cworth.org>
+ */
+
+#ifndef CAIRO_SVG_SURFACE_PRIVATE_H
+#define CAIRO_SVG_SURFACE_PRIVATE_H
+
+#include "cairo-svg.h"
+
+#include "cairo-surface-private.h"
+#include "cairo-surface-clipper-private.h"
+
+typedef struct cairo_svg_document cairo_svg_document_t;
+
+typedef struct cairo_svg_surface {
+    cairo_surface_t base;
+
+    cairo_content_t content;
+
+    double width;
+    double height;
+
+    cairo_svg_document_t *document;
+
+    cairo_output_stream_t *xml_node;
+    cairo_array_t         page_set;
+
+    cairo_surface_clipper_t clipper;
+    unsigned int clip_level;
+    unsigned int base_clip;
+    cairo_bool_t is_base_clip_emitted;
+
+    cairo_paginated_mode_t paginated_mode;
+
+    cairo_bool_t force_fallbacks;
+} cairo_svg_surface_t;
+
+#endif /* CAIRO_SVG_SURFACE_PRIVATE_H */
diff --git a/src/cairo-svg-surface.c b/src/cairo-svg-surface.c
new file mode 100644 (file)
index 0000000..6f607d3
--- /dev/null
@@ -0,0 +1,2847 @@
+/* vim: set sw=4 sts=4: -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2004 Red Hat, Inc
+ * Copyright © 2005-2007 Emmanuel Pacaud <emmanuel.pacaud@free.fr>
+ * Copyright © 2006 Red Hat, Inc
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *     Kristian Høgsberg <krh@redhat.com>
+ *     Emmanuel Pacaud <emmanuel.pacaud@free.fr>
+ *     Carl Worth <cworth@cworth.org>
+ */
+
+#define _BSD_SOURCE /* for snprintf() */
+#include "cairoint.h"
+
+#include "cairo-svg.h"
+
+#include "cairo-array-private.h"
+#include "cairo-analysis-surface-private.h"
+#include "cairo-default-context-private.h"
+#include "cairo-error-private.h"
+#include "cairo-image-info-private.h"
+#include "cairo-image-surface-private.h"
+#include "cairo-recording-surface-private.h"
+#include "cairo-output-stream-private.h"
+#include "cairo-path-fixed-private.h"
+#include "cairo-paginated-private.h"
+#include "cairo-scaled-font-subsets-private.h"
+#include "cairo-surface-clipper-private.h"
+#include "cairo-svg-surface-private.h"
+
+/**
+ * SECTION:cairo-svg
+ * @Title: SVG Surfaces
+ * @Short_Description: Rendering SVG documents
+ * @See_Also: #cairo_surface_t
+ *
+ * The SVG surface is used to render cairo graphics to
+ * SVG files and is a multi-page vector surface backend.
+ */
+
+/**
+ * CAIRO_HAS_SVG_SURFACE:
+ *
+ * Defined if the SVG surface backend is available.
+ * This macro can be used to conditionally compile backend-specific code.
+ */
+
+typedef struct cairo_svg_page cairo_svg_page_t;
+
+static const int invalid_pattern_id = -1;
+
+static const cairo_svg_version_t _cairo_svg_versions[] =
+{
+    CAIRO_SVG_VERSION_1_1,
+    CAIRO_SVG_VERSION_1_2
+};
+
+#define CAIRO_SVG_VERSION_LAST ARRAY_LENGTH (_cairo_svg_versions)
+
+static void
+_cairo_svg_surface_emit_path (cairo_output_stream_t    *output,
+                             const cairo_path_fixed_t  *path,
+                             const cairo_matrix_t      *ctm_inverse);
+
+static cairo_bool_t
+_cairo_svg_version_has_page_set_support (cairo_svg_version_t version)
+{
+    return version > CAIRO_SVG_VERSION_1_1;
+}
+
+static const char * _cairo_svg_version_strings[CAIRO_SVG_VERSION_LAST] =
+{
+    "SVG 1.1",
+    "SVG 1.2"
+};
+
+static const char * _cairo_svg_internal_version_strings[CAIRO_SVG_VERSION_LAST] =
+{
+    "1.1",
+    "1.2"
+};
+
+struct cairo_svg_page {
+    unsigned int surface_id;
+    unsigned int clip_level;
+    cairo_output_stream_t *xml_node;
+};
+
+struct cairo_svg_document {
+    cairo_output_stream_t *output_stream;
+    unsigned long refcount;
+    cairo_surface_t *owner;
+    cairo_bool_t finished;
+
+    double width;
+    double height;
+
+    cairo_output_stream_t *xml_node_defs;
+    cairo_output_stream_t *xml_node_glyphs;
+
+    unsigned int linear_pattern_id;
+    unsigned int radial_pattern_id;
+    unsigned int pattern_id;
+    unsigned int filter_id;
+    unsigned int clip_id;
+    unsigned int mask_id;
+
+    cairo_bool_t alpha_filter;
+
+    cairo_svg_version_t svg_version;
+
+    cairo_scaled_font_subsets_t *font_subsets;
+};
+
+static cairo_status_t
+_cairo_svg_document_create (cairo_output_stream_t       *stream,
+                           double                        width,
+                           double                        height,
+                           cairo_svg_version_t           version,
+                           cairo_svg_document_t        **document_out);
+
+static cairo_status_t
+_cairo_svg_document_destroy (cairo_svg_document_t *document);
+
+static cairo_status_t
+_cairo_svg_document_finish (cairo_svg_document_t *document);
+
+static cairo_svg_document_t *
+_cairo_svg_document_reference (cairo_svg_document_t *document);
+
+static unsigned int
+_cairo_svg_document_allocate_mask_id (cairo_svg_document_t *document);
+
+static cairo_surface_t *
+_cairo_svg_surface_create_for_document (cairo_svg_document_t   *document,
+                                       cairo_content_t          content,
+                                       double                   width,
+                                       double                   height);
+static cairo_surface_t *
+_cairo_svg_surface_create_for_stream_internal (cairo_output_stream_t   *stream,
+                                              double                    width,
+                                              double                    height,
+                                              cairo_svg_version_t       version);
+
+static const cairo_surface_backend_t cairo_svg_surface_backend;
+static const cairo_paginated_surface_backend_t cairo_svg_surface_paginated_backend;
+
+/**
+ * cairo_svg_surface_create_for_stream:
+ * @write_func: a #cairo_write_func_t to accept the output data, may be %NULL
+ *              to indicate a no-op @write_func. With a no-op @write_func,
+ *              the surface may be queried or used as a source without
+ *              generating any temporary files.
+ * @closure: the closure argument for @write_func
+ * @width_in_points: width of the surface, in points (1 point == 1/72.0 inch)
+ * @height_in_points: height of the surface, in points (1 point == 1/72.0 inch)
+ *
+ * Creates a SVG surface of the specified size in points to be written
+ * incrementally to the stream represented by @write_func and @closure.
+ *
+ * Return value: a pointer to the newly created surface. The caller
+ * owns the surface and should call cairo_surface_destroy() when done
+ * with it.
+ *
+ * This function always returns a valid pointer, but it will return a
+ * pointer to a "nil" surface if an error such as out of memory
+ * occurs. You can use cairo_surface_status() to check for this.
+ *
+ * Since: 1.2
+ */
+cairo_surface_t *
+cairo_svg_surface_create_for_stream (cairo_write_func_t                 write_func,
+                                    void                       *closure,
+                                    double                      width,
+                                    double                      height)
+{
+    cairo_output_stream_t *stream;
+
+    stream = _cairo_output_stream_create (write_func, NULL, closure);
+    if (_cairo_output_stream_get_status (stream))
+       return _cairo_surface_create_in_error (_cairo_output_stream_destroy (stream));
+
+    return _cairo_svg_surface_create_for_stream_internal (stream, width, height, CAIRO_SVG_VERSION_1_1);
+}
+
+/**
+ * cairo_svg_surface_create:
+ * @filename: a filename for the SVG output (must be writable), %NULL may be
+ *            used to specify no output. This will generate a SVG surface that
+ *            may be queried and used as a source, without generating a
+ *            temporary file.
+ * @width_in_points: width of the surface, in points (1 point == 1/72.0 inch)
+ * @height_in_points: height of the surface, in points (1 point == 1/72.0 inch)
+ *
+ * Creates a SVG surface of the specified size in points to be written
+ * to @filename.
+ *
+ * The SVG surface backend recognizes the following MIME types for the
+ * data attached to a surface (see cairo_surface_set_mime_data()) when
+ * it is used as a source pattern for drawing on this surface:
+ * %CAIRO_MIME_TYPE_JPEG, %CAIRO_MIME_TYPE_PNG,
+ * %CAIRO_MIME_TYPE_URI. If any of them is specified, the SVG backend
+ * emits a href with the content of MIME data instead of a surface
+ * snapshot (PNG, Base64-encoded) in the corresponding image tag.
+ *
+ * The unofficial MIME type %CAIRO_MIME_TYPE_URI is examined
+ * first. If present, the URI is emitted as is: assuring the
+ * correctness of URI is left to the client code.
+ *
+ * If %CAIRO_MIME_TYPE_URI is not present, but %CAIRO_MIME_TYPE_JPEG
+ * or %CAIRO_MIME_TYPE_PNG is specified, the corresponding data is
+ * Base64-encoded and emitted.
+ *
+ * Return value: a pointer to the newly created surface. The caller
+ * owns the surface and should call cairo_surface_destroy() when done
+ * with it.
+ *
+ * This function always returns a valid pointer, but it will return a
+ * pointer to a "nil" surface if an error such as out of memory
+ * occurs. You can use cairo_surface_status() to check for this.
+ *
+ * Since: 1.2
+ **/
+cairo_surface_t *
+cairo_svg_surface_create (const char   *filename,
+                         double         width,
+                         double         height)
+{
+    cairo_output_stream_t *stream;
+
+    stream = _cairo_output_stream_create_for_filename (filename);
+    if (_cairo_output_stream_get_status (stream))
+       return _cairo_surface_create_in_error (_cairo_output_stream_destroy (stream));
+
+    return _cairo_svg_surface_create_for_stream_internal (stream, width, height, CAIRO_SVG_VERSION_1_1);
+}
+
+static cairo_bool_t
+_cairo_surface_is_svg (cairo_surface_t *surface)
+{
+    return surface->backend == &cairo_svg_surface_backend;
+}
+
+/* If the abstract_surface is a paginated surface, and that paginated
+ * surface's target is a svg_surface, then set svg_surface to that
+ * target. Otherwise return FALSE.
+ */
+static cairo_bool_t
+_extract_svg_surface (cairo_surface_t           *surface,
+                     cairo_svg_surface_t       **svg_surface)
+{
+    cairo_surface_t *target;
+    cairo_status_t status_ignored;
+
+    if (surface->status)
+       return FALSE;
+    if (surface->finished) {
+       status_ignored = _cairo_surface_set_error (surface,
+                                                  _cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
+        return FALSE;
+    }
+
+    if (! _cairo_surface_is_paginated (surface)) {
+       status_ignored = _cairo_surface_set_error (surface,
+                                                  _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH));
+       return FALSE;
+    }
+
+    target = _cairo_paginated_surface_get_target (surface);
+    if (target->status) {
+       status_ignored = _cairo_surface_set_error (surface,
+                                                  target->status);
+       return FALSE;
+    }
+    if (target->finished) {
+       status_ignored = _cairo_surface_set_error (surface,
+                                                  _cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
+        return FALSE;
+    }
+
+    if (! _cairo_surface_is_svg (target)) {
+       status_ignored = _cairo_surface_set_error (surface,
+                                                  _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH));
+       return FALSE;
+    }
+
+    *svg_surface = (cairo_svg_surface_t *) target;
+    return TRUE;
+}
+
+/**
+ * cairo_svg_surface_restrict_to_version:
+ * @surface: a SVG #cairo_surface_t
+ * @version: SVG version
+ *
+ * Restricts the generated SVG file to @version. See cairo_svg_get_versions()
+ * for a list of available version values that can be used here.
+ *
+ * This function should only be called before any drawing operations
+ * have been performed on the given surface. The simplest way to do
+ * this is to call this function immediately after creating the
+ * surface.
+ *
+ * Since: 1.2
+ **/
+void
+cairo_svg_surface_restrict_to_version (cairo_surface_t         *abstract_surface,
+                                      cairo_svg_version_t       version)
+{
+    cairo_svg_surface_t *surface = NULL; /* hide compiler warning */
+
+    if (! _extract_svg_surface (abstract_surface, &surface))
+       return;
+
+    if (version < CAIRO_SVG_VERSION_LAST)
+       surface->document->svg_version = version;
+}
+
+/**
+ * cairo_svg_get_versions:
+ * @versions: supported version list
+ * @num_versions: list length
+ *
+ * Used to retrieve the list of supported versions. See
+ * cairo_svg_surface_restrict_to_version().
+ *
+ * Since: 1.2
+ **/
+void
+cairo_svg_get_versions (cairo_svg_version_t const      **versions,
+                        int                             *num_versions)
+{
+    if (versions != NULL)
+       *versions = _cairo_svg_versions;
+
+    if (num_versions != NULL)
+       *num_versions = CAIRO_SVG_VERSION_LAST;
+}
+
+/**
+ * cairo_svg_version_to_string:
+ * @version: a version id
+ *
+ * Get the string representation of the given @version id. This function
+ * will return %NULL if @version isn't valid. See cairo_svg_get_versions()
+ * for a way to get the list of valid version ids.
+ *
+ * Return value: the string associated to given version.
+ *
+ * Since: 1.2
+ **/
+const char *
+cairo_svg_version_to_string (cairo_svg_version_t version)
+{
+    if (version >= CAIRO_SVG_VERSION_LAST)
+       return NULL;
+
+    return _cairo_svg_version_strings[version];
+}
+
+static cairo_bool_t
+_cliprect_covers_surface (cairo_svg_surface_t *surface,
+                         cairo_path_fixed_t *path)
+{
+    cairo_box_t box;
+
+    if (_cairo_path_fixed_is_box (path, &box)) {
+       if (box.p1.x <= 0 &&
+           box.p1.y <= 0 &&
+           _cairo_fixed_to_double (box.p2.x) >= surface->width &&
+           _cairo_fixed_to_double (box.p2.y) >= surface->height)
+       {
+           return TRUE;
+       }
+    }
+
+    return FALSE;
+}
+
+static cairo_status_t
+_cairo_svg_surface_clipper_intersect_clip_path (cairo_surface_clipper_t *clipper,
+                                               cairo_path_fixed_t      *path,
+                                               cairo_fill_rule_t        fill_rule,
+                                               double                   tolerance,
+                                               cairo_antialias_t        antialias)
+{
+    cairo_svg_surface_t *surface = cairo_container_of (clipper,
+                                                      cairo_svg_surface_t,
+                                                      clipper);
+    cairo_svg_document_t *document = surface->document;
+    unsigned int i;
+
+    if (path == NULL) {
+       for (i = 0; i < surface->clip_level; i++)
+           _cairo_output_stream_printf (surface->xml_node, "</g>\n");
+
+       surface->clip_level = 0;
+       return CAIRO_STATUS_SUCCESS;
+    }
+
+    /* skip trivial whole-page clips */
+    if (_cliprect_covers_surface (surface, path))
+       return CAIRO_STATUS_SUCCESS;
+
+    _cairo_output_stream_printf (document->xml_node_defs,
+                                "<clipPath id=\"clip%d\">\n"
+                                "  <path ",
+                                document->clip_id);
+    _cairo_svg_surface_emit_path (document->xml_node_defs, path, NULL);
+
+    _cairo_output_stream_printf (document->xml_node_defs,
+                                "/>\n"
+                                "</clipPath>\n");
+
+    _cairo_output_stream_printf (surface->xml_node,
+                                "<g clip-path=\"url(#clip%d)\" "
+                                "clip-rule=\"%s\">\n",
+                                document->clip_id,
+                                fill_rule == CAIRO_FILL_RULE_EVEN_ODD ?
+                                "evenodd" : "nonzero");
+
+    document->clip_id++;
+    surface->clip_level++;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_surface_t *
+_cairo_svg_surface_create_for_document (cairo_svg_document_t   *document,
+                                       cairo_content_t          content,
+                                       double                   width,
+                                       double                   height)
+{
+    cairo_svg_surface_t *surface;
+    cairo_surface_t *paginated;
+    cairo_status_t status, status_ignored;
+
+    surface = malloc (sizeof (cairo_svg_surface_t));
+    if (unlikely (surface == NULL))
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+    _cairo_surface_init (&surface->base,
+                        &cairo_svg_surface_backend,
+                        NULL, /* device */
+                        content);
+
+    surface->width = width;
+    surface->height = height;
+
+    surface->document = _cairo_svg_document_reference (document);
+
+    surface->clip_level = 0;
+    _cairo_surface_clipper_init (&surface->clipper,
+                                _cairo_svg_surface_clipper_intersect_clip_path);
+
+    surface->base_clip = document->clip_id++;
+    surface->is_base_clip_emitted = FALSE;
+
+    surface->xml_node = _cairo_memory_stream_create ();
+    status = _cairo_output_stream_get_status (surface->xml_node);
+    if (unlikely (status))
+       goto CLEANUP;
+
+    _cairo_array_init (&surface->page_set, sizeof (cairo_svg_page_t));
+
+    if (content == CAIRO_CONTENT_COLOR) {
+       _cairo_output_stream_printf (surface->xml_node,
+                                    "<rect width=\"%f\" height=\"%f\" "
+                                    "style=\"opacity:1;stroke:none;"
+                                    "fill:rgb(0,0,0);\"/>\n",
+                                    width, height);
+       status = _cairo_output_stream_get_status (surface->xml_node);
+       if (unlikely (status))
+           goto CLEANUP;
+    }
+
+    surface->paginated_mode = CAIRO_PAGINATED_MODE_ANALYZE;
+    surface->force_fallbacks = FALSE;
+    surface->content = content;
+
+    paginated = _cairo_paginated_surface_create (&surface->base,
+                                                surface->content,
+                                                &cairo_svg_surface_paginated_backend);
+    status = paginated->status;
+    if (status == CAIRO_STATUS_SUCCESS) {
+       /* paginated keeps the only reference to surface now, drop ours */
+       cairo_surface_destroy (&surface->base);
+       return paginated;
+    }
+
+    /* ignore status as we are on the error path */
+CLEANUP:
+    status_ignored = _cairo_output_stream_destroy (surface->xml_node);
+    status_ignored = _cairo_svg_document_destroy (document);
+
+    free (surface);
+
+    return _cairo_surface_create_in_error (status);
+}
+
+static cairo_surface_t *
+_cairo_svg_surface_create_for_stream_internal (cairo_output_stream_t   *stream,
+                                              double                    width,
+                                              double                    height,
+                                              cairo_svg_version_t       version)
+{
+    cairo_svg_document_t *document = NULL; /* silence compiler */
+    cairo_surface_t *surface;
+    cairo_status_t status;
+
+    status = _cairo_svg_document_create (stream,
+                                        width, height, version,
+                                        &document);
+    if (unlikely (status)) {
+       surface =  _cairo_surface_create_in_error (status);
+       /* consume the output stream on behalf of caller */
+       status = _cairo_output_stream_destroy (stream);
+       return surface;
+    }
+
+    surface = _cairo_svg_surface_create_for_document (document, CAIRO_CONTENT_COLOR_ALPHA,
+                                                     width, height);
+    if (surface->status) {
+       status = _cairo_svg_document_destroy (document);
+       return surface;
+    }
+
+    document->owner = surface;
+    status = _cairo_svg_document_destroy (document);
+    /* the ref count should be 2 at this point */
+    assert (status == CAIRO_STATUS_SUCCESS);
+
+    return surface;
+}
+
+static cairo_svg_page_t *
+_cairo_svg_surface_store_page (cairo_svg_surface_t *surface)
+{
+    cairo_svg_page_t page;
+    cairo_output_stream_t *stream;
+    cairo_int_status_t status;
+    unsigned int i;
+
+    stream = _cairo_memory_stream_create ();
+    if (_cairo_output_stream_get_status (stream)) {
+       status = _cairo_output_stream_destroy (stream);
+       return NULL;
+    }
+
+    page.surface_id = surface->base.unique_id;
+    page.clip_level = surface->clip_level;
+    page.xml_node = surface->xml_node;
+
+    if (_cairo_array_append (&surface->page_set, &page)) {
+       status = _cairo_output_stream_destroy (stream);
+       return NULL;
+    }
+
+    surface->xml_node = stream;
+    surface->clip_level = 0;
+    for (i = 0; i < page.clip_level; i++)
+       _cairo_output_stream_printf (page.xml_node, "</g>\n");
+
+    _cairo_surface_clipper_reset (&surface->clipper);
+
+    return _cairo_array_index (&surface->page_set,
+                              surface->page_set.num_elements - 1);
+}
+
+static cairo_int_status_t
+_cairo_svg_surface_copy_page (void *abstract_surface)
+{
+    cairo_svg_surface_t *surface = abstract_surface;
+    cairo_svg_page_t *page;
+
+    page = _cairo_svg_surface_store_page (surface);
+    if (unlikely (page == NULL))
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    _cairo_memory_stream_copy (page->xml_node, surface->xml_node);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_svg_surface_show_page (void *abstract_surface)
+{
+    cairo_svg_surface_t *surface = abstract_surface;
+
+    if (unlikely (_cairo_svg_surface_store_page (surface) == NULL))
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_svg_surface_emit_transform (cairo_output_stream_t *output,
+                                  char const            *attribute_str,
+                                  const cairo_matrix_t  *object_matrix,
+                                  const cairo_matrix_t  *parent_matrix)
+{
+    cairo_matrix_t matrix = *object_matrix;
+
+    if (parent_matrix != NULL)
+       cairo_matrix_multiply (&matrix, &matrix, parent_matrix);
+
+    if (!_cairo_matrix_is_identity (&matrix))
+       _cairo_output_stream_printf (output,
+                                    "%s=\"matrix(%f,%f,%f,%f,%f,%f)\"",
+                                    attribute_str,
+                                    matrix.xx, matrix.yx,
+                                    matrix.xy, matrix.yy,
+                                    matrix.x0, matrix.y0);
+}
+
+typedef struct {
+    cairo_output_stream_t *output;
+    const cairo_matrix_t *ctm_inverse;
+} svg_path_info_t;
+
+static cairo_status_t
+_cairo_svg_path_move_to (void *closure,
+                        const cairo_point_t *point)
+{
+    svg_path_info_t *info = closure;
+    double x = _cairo_fixed_to_double (point->x);
+    double y = _cairo_fixed_to_double (point->y);
+
+    if (info->ctm_inverse)
+       cairo_matrix_transform_point (info->ctm_inverse, &x, &y);
+
+    _cairo_output_stream_printf (info->output, "M %f %f ", x, y);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_svg_path_line_to (void *closure,
+                        const cairo_point_t *point)
+{
+    svg_path_info_t *info = closure;
+    double x = _cairo_fixed_to_double (point->x);
+    double y = _cairo_fixed_to_double (point->y);
+
+    if (info->ctm_inverse)
+       cairo_matrix_transform_point (info->ctm_inverse, &x, &y);
+
+    _cairo_output_stream_printf (info->output, "L %f %f ", x, y);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_svg_path_curve_to (void          *closure,
+                         const cairo_point_t *b,
+                         const cairo_point_t *c,
+                         const cairo_point_t *d)
+{
+    svg_path_info_t *info = closure;
+    double bx = _cairo_fixed_to_double (b->x);
+    double by = _cairo_fixed_to_double (b->y);
+    double cx = _cairo_fixed_to_double (c->x);
+    double cy = _cairo_fixed_to_double (c->y);
+    double dx = _cairo_fixed_to_double (d->x);
+    double dy = _cairo_fixed_to_double (d->y);
+
+    if (info->ctm_inverse) {
+       cairo_matrix_transform_point (info->ctm_inverse, &bx, &by);
+       cairo_matrix_transform_point (info->ctm_inverse, &cx, &cy);
+       cairo_matrix_transform_point (info->ctm_inverse, &dx, &dy);
+    }
+
+    _cairo_output_stream_printf (info->output,
+                                "C %f %f %f %f %f %f ",
+                                bx, by, cx, cy, dx, dy);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_svg_path_close_path (void *closure)
+{
+    svg_path_info_t *info = closure;
+
+    _cairo_output_stream_printf (info->output, "Z ");
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_svg_surface_emit_path (cairo_output_stream_t    *output,
+                             const cairo_path_fixed_t  *path,
+                             const cairo_matrix_t      *ctm_inverse)
+{
+    cairo_status_t status;
+    svg_path_info_t info;
+
+    _cairo_output_stream_printf (output, "d=\"");
+
+    info.output = output;
+    info.ctm_inverse = ctm_inverse;
+    status = _cairo_path_fixed_interpret (path,
+                                         _cairo_svg_path_move_to,
+                                         _cairo_svg_path_line_to,
+                                         _cairo_svg_path_curve_to,
+                                         _cairo_svg_path_close_path,
+                                         &info);
+    assert (status == CAIRO_STATUS_SUCCESS);
+
+    _cairo_output_stream_printf (output, "\"");
+}
+
+static cairo_int_status_t
+_cairo_svg_document_emit_outline_glyph_data (cairo_svg_document_t      *document,
+                                            cairo_scaled_font_t        *scaled_font,
+                                            unsigned long               glyph_index)
+{
+    cairo_scaled_glyph_t *scaled_glyph;
+    cairo_int_status_t status;
+
+    status = _cairo_scaled_glyph_lookup (scaled_font,
+                                        glyph_index,
+                                        CAIRO_SCALED_GLYPH_INFO_METRICS|
+                                        CAIRO_SCALED_GLYPH_INFO_PATH,
+                                        &scaled_glyph);
+    if (unlikely (status))
+       return status;
+
+    _cairo_output_stream_printf (document->xml_node_glyphs,
+                                "<path style=\"stroke:none;\" ");
+
+    _cairo_svg_surface_emit_path (document->xml_node_glyphs,
+                                 scaled_glyph->path, NULL);
+
+    _cairo_output_stream_printf (document->xml_node_glyphs,
+                                "/>\n");
+
+    return status;
+}
+
+static cairo_int_status_t
+_cairo_svg_document_emit_bitmap_glyph_data (cairo_svg_document_t       *document,
+                                           cairo_scaled_font_t         *scaled_font,
+                                           unsigned long                glyph_index)
+{
+    cairo_scaled_glyph_t *scaled_glyph;
+    cairo_image_surface_t *image;
+    cairo_status_t status;
+    uint8_t *row, *byte;
+    int rows, cols;
+    int x, y, bit;
+
+    status = _cairo_scaled_glyph_lookup (scaled_font,
+                                        glyph_index,
+                                        CAIRO_SCALED_GLYPH_INFO_METRICS |
+                                        CAIRO_SCALED_GLYPH_INFO_SURFACE,
+                                        &scaled_glyph);
+    if (unlikely (status))
+       return status;
+
+    image = _cairo_image_surface_coerce_to_format (scaled_glyph->surface,
+                                                  CAIRO_FORMAT_A1);
+    status = image->base.status;
+    if (unlikely (status))
+       return status;
+
+    _cairo_output_stream_printf (document->xml_node_glyphs, "<g");
+    _cairo_svg_surface_emit_transform (document->xml_node_glyphs, " transform",
+                                      &image->base.device_transform_inverse, NULL);
+    _cairo_output_stream_printf (document->xml_node_glyphs, ">/n");
+
+    for (y = 0, row = image->data, rows = image->height; rows; row += image->stride, rows--, y++) {
+       for (x = 0, byte = row, cols = (image->width + 7) / 8; cols; byte++, cols--) {
+           uint8_t output_byte = CAIRO_BITSWAP8_IF_LITTLE_ENDIAN (*byte);
+           for (bit = 7; bit >= 0 && x < image->width; bit--, x++) {
+               if (output_byte & (1 << bit)) {
+                   _cairo_output_stream_printf (document->xml_node_glyphs,
+                                                "<rect x=\"%d\" y=\"%d\" width=\"1\" height=\"1\"/>\n",
+                                                x, y);
+               }
+           }
+       }
+    }
+    _cairo_output_stream_printf (document->xml_node_glyphs, "</g>\n");
+
+    cairo_surface_destroy (&image->base);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_svg_document_emit_glyph (cairo_svg_document_t   *document,
+                               cairo_scaled_font_t     *scaled_font,
+                               unsigned long            scaled_font_glyph_index,
+                               unsigned int             font_id,
+                               unsigned int             subset_glyph_index)
+{
+    cairo_int_status_t      status;
+
+    _cairo_output_stream_printf (document->xml_node_glyphs,
+                                "<symbol overflow=\"visible\" id=\"glyph%d-%d\">\n",
+                                font_id,
+                                subset_glyph_index);
+
+    status = _cairo_svg_document_emit_outline_glyph_data (document,
+                                                         scaled_font,
+                                                         scaled_font_glyph_index);
+    if (status == CAIRO_INT_STATUS_UNSUPPORTED)
+       status = _cairo_svg_document_emit_bitmap_glyph_data (document,
+                                                            scaled_font,
+                                                            scaled_font_glyph_index);
+    if (unlikely (status))
+       return status;
+
+    _cairo_output_stream_printf (document->xml_node_glyphs, "</symbol>\n");
+
+    return CAIRO_INT_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_svg_document_emit_font_subset (cairo_scaled_font_subset_t       *font_subset,
+                                     void                              *closure)
+{
+    cairo_svg_document_t *document = closure;
+    cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS;
+    unsigned int i;
+
+    _cairo_scaled_font_freeze_cache (font_subset->scaled_font);
+    for (i = 0; i < font_subset->num_glyphs; i++) {
+       status = _cairo_svg_document_emit_glyph (document,
+                                                font_subset->scaled_font,
+                                                font_subset->glyphs[i],
+                                                font_subset->font_id, i);
+       if (unlikely (status))
+           break;
+    }
+    _cairo_scaled_font_thaw_cache (font_subset->scaled_font);
+
+    return status;
+}
+
+static cairo_status_t
+_cairo_svg_document_emit_font_subsets (cairo_svg_document_t *document)
+{
+    cairo_status_t status;
+
+    status = _cairo_scaled_font_subsets_foreach_scaled (document->font_subsets,
+                                                        _cairo_svg_document_emit_font_subset,
+                                                        document);
+    if (unlikely (status))
+       goto FAIL;
+
+    status = _cairo_scaled_font_subsets_foreach_user (document->font_subsets,
+                                                     _cairo_svg_document_emit_font_subset,
+                                                     document);
+
+  FAIL:
+    _cairo_scaled_font_subsets_destroy (document->font_subsets);
+    document->font_subsets = NULL;
+
+    return status;
+}
+
+static char const *
+_cairo_svg_surface_operators[] = {
+    "clear",
+
+    "src", "src-over", "src-in",
+    "src-out", "src-atop",
+
+    "dst", "dst-over", "dst-in",
+    "dst-out", "dst-atop",
+
+    "xor", "plus",
+    "color-dodge", /* FIXME: saturate ? */
+
+    "multiply",        "screen", "overlay",
+    "darken", "lighten",
+    "color-dodge", "color-burn",
+    "hard-light", "soft-light",
+    "difference", "exclusion"
+};
+
+static cairo_bool_t
+_cairo_svg_surface_analyze_operator (cairo_svg_surface_t   *surface,
+                                     cairo_operator_t       op)
+{
+    /* guard against newly added operators */
+    if (op >= ARRAY_LENGTH (_cairo_svg_surface_operators))
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    /* allow operators being NULL if they are unsupported */
+    if (_cairo_svg_surface_operators[op] == NULL)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_svg_surface_analyze_operation (cairo_svg_surface_t   *surface,
+                                     cairo_operator_t       op,
+                                     const cairo_pattern_t *pattern)
+{
+    cairo_svg_document_t *document = surface->document;
+
+    if (surface->force_fallbacks &&
+       surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
+    {
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+    }
+
+    if (pattern->type == CAIRO_PATTERN_TYPE_MESH)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    /* SVG doesn't support extend reflect for image pattern */
+    if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE &&
+       pattern->extend == CAIRO_EXTEND_REFLECT)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    if (document->svg_version >= CAIRO_SVG_VERSION_1_2)
+       return _cairo_svg_surface_analyze_operator (surface, op);
+
+    if (op == CAIRO_OPERATOR_OVER)
+       return CAIRO_STATUS_SUCCESS;
+
+    /* The SOURCE operator is only supported if there is nothing
+     * painted underneath. */
+    if (op == CAIRO_OPERATOR_SOURCE)
+       return CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY;
+
+    return CAIRO_INT_STATUS_UNSUPPORTED;
+}
+
+static cairo_int_status_t
+_cairo_svg_surface_operation_supported (cairo_svg_surface_t    *surface,
+                                       cairo_operator_t         op,
+                                       const cairo_pattern_t   *pattern)
+{
+    return _cairo_svg_surface_analyze_operation (surface, op, pattern) != CAIRO_INT_STATUS_UNSUPPORTED;
+}
+
+static cairo_status_t
+_cairo_svg_surface_finish (void *abstract_surface)
+{
+    cairo_status_t status, status2;
+    cairo_svg_surface_t *surface = abstract_surface;
+    cairo_svg_document_t *document = surface->document;
+    cairo_svg_page_t *page;
+    unsigned int i;
+
+    if (_cairo_paginated_surface_get_target (document->owner) == &surface->base)
+       status = _cairo_svg_document_finish (document);
+    else
+       status = CAIRO_STATUS_SUCCESS;
+
+    if (surface->xml_node != NULL) {
+       status2 = _cairo_output_stream_destroy (surface->xml_node);
+       if (status == CAIRO_STATUS_SUCCESS)
+           status = status2;
+    }
+
+    for (i = 0; i < surface->page_set.num_elements; i++) {
+       page = _cairo_array_index (&surface->page_set, i);
+       status2 = _cairo_output_stream_destroy (page->xml_node);
+       if (status == CAIRO_STATUS_SUCCESS)
+           status = status2;
+    }
+    _cairo_array_fini (&surface->page_set);
+
+    _cairo_surface_clipper_reset (&surface->clipper);
+
+    status2 = _cairo_svg_document_destroy (document);
+    if (status == CAIRO_STATUS_SUCCESS)
+       status = status2;
+
+    return status;
+}
+
+
+static void
+_cairo_svg_surface_emit_alpha_filter (cairo_svg_document_t *document)
+{
+    if (document->alpha_filter)
+       return;
+
+    _cairo_output_stream_printf (document->xml_node_defs,
+                                "<filter id=\"alpha\" "
+                                "filterUnits=\"objectBoundingBox\" "
+                                "x=\"0%%\" y=\"0%%\" "
+                                "width=\"100%%\" height=\"100%%\">\n"
+                                "  <feColorMatrix type=\"matrix\" "
+                                "in=\"SourceGraphic\" "
+                                "values=\"0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0\"/>\n"
+                                "</filter>\n");
+
+    document->alpha_filter = TRUE;
+}
+
+typedef struct {
+    cairo_output_stream_t *output;
+    unsigned int in_mem;
+    unsigned int trailing;
+    unsigned char src[3];
+} base64_write_closure_t;
+
+static char const base64_table[64] =
+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+static cairo_status_t
+base64_write_func (void *closure,
+                  const unsigned char *data,
+                  unsigned int length)
+{
+    base64_write_closure_t *info = (base64_write_closure_t *) closure;
+    unsigned int i;
+    unsigned char *src;
+
+    src = info->src;
+
+    if (info->in_mem + length < 3) {
+       for (i = 0; i < length; i++) {
+           src[i + info->in_mem] = *data++;
+       }
+       info->in_mem += length;
+       return CAIRO_STATUS_SUCCESS;
+    }
+
+    do {
+       unsigned char dst[4];
+
+       for (i = info->in_mem; i < 3; i++) {
+           src[i] = *data++;
+           length--;
+       }
+       info->in_mem = 0;
+
+       dst[0] = base64_table[src[0] >> 2];
+       dst[1] = base64_table[(src[0] & 0x03) << 4 | src[1] >> 4];
+       dst[2] = base64_table[(src[1] & 0x0f) << 2 | src[2] >> 6];
+       dst[3] = base64_table[src[2] & 0xfc >> 2];
+       /* Special case for the last missing bits */
+       switch (info->trailing) {
+           case 2:
+               dst[2] = '=';
+           case 1:
+               dst[3] = '=';
+           default:
+               break;
+       }
+       _cairo_output_stream_write (info->output, dst, 4);
+    } while (length >= 3);
+
+    for (i = 0; i < length; i++) {
+       src[i] = *data++;
+    }
+    info->in_mem = length;
+
+    return _cairo_output_stream_get_status (info->output);
+}
+
+static cairo_int_status_t
+_cairo_surface_base64_encode_jpeg (cairo_surface_t       *surface,
+                                  cairo_output_stream_t *output)
+{
+    const unsigned char *mime_data;
+    unsigned long mime_data_length;
+    cairo_image_info_t image_info;
+    base64_write_closure_t info;
+    cairo_status_t status;
+
+    cairo_surface_get_mime_data (surface, CAIRO_MIME_TYPE_JPEG,
+                                &mime_data, &mime_data_length);
+    if (mime_data == NULL)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    status = _cairo_image_info_get_jpeg_info (&image_info, mime_data, mime_data_length);
+    if (unlikely (status))
+       return status;
+
+    _cairo_output_stream_printf (output, "data:image/jpeg;base64,");
+
+    info.output = output;
+    info.in_mem = 0;
+    info.trailing = 0;
+
+    status = base64_write_func (&info, mime_data, mime_data_length);
+    if (unlikely (status))
+       return status;
+
+    if (info.in_mem > 0) {
+       memset (info.src + info.in_mem, 0, 3 - info.in_mem);
+       info.trailing = 3 - info.in_mem;
+       info.in_mem = 3;
+       status = base64_write_func (&info, NULL, 0);
+    }
+
+    return status;
+}
+
+static cairo_int_status_t
+_cairo_surface_base64_encode_png (cairo_surface_t       *surface,
+                                 cairo_output_stream_t *output)
+{
+    const unsigned char *mime_data;
+    unsigned long mime_data_length;
+    base64_write_closure_t info;
+    cairo_status_t status;
+
+    cairo_surface_get_mime_data (surface, CAIRO_MIME_TYPE_PNG,
+                                &mime_data, &mime_data_length);
+    if (unlikely (surface->status))
+       return surface->status;
+    if (mime_data == NULL)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    _cairo_output_stream_printf (output, "data:image/png;base64,");
+
+    info.output = output;
+    info.in_mem = 0;
+    info.trailing = 0;
+
+    status = base64_write_func (&info, mime_data, mime_data_length);
+    if (unlikely (status))
+       return status;
+
+    if (info.in_mem > 0) {
+       memset (info.src + info.in_mem, 0, 3 - info.in_mem);
+       info.trailing = 3 - info.in_mem;
+       info.in_mem = 3;
+       status = base64_write_func (&info, NULL, 0);
+    }
+
+    return status;
+}
+
+static cairo_int_status_t
+_cairo_surface_base64_encode (cairo_surface_t       *surface,
+                             cairo_output_stream_t *output)
+{
+    cairo_int_status_t status;
+    base64_write_closure_t info;
+
+    status = _cairo_surface_base64_encode_jpeg (surface, output);
+    if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+       return status;
+
+    status = _cairo_surface_base64_encode_png (surface, output);
+    if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+       return status;
+
+    info.output = output;
+    info.in_mem = 0;
+    info.trailing = 0;
+
+    _cairo_output_stream_printf (info.output, "data:image/png;base64,");
+
+    status = cairo_surface_write_to_png_stream (surface, base64_write_func,
+                                               (void *) &info);
+
+    if (unlikely (status))
+       return status;
+
+    if (info.in_mem > 0) {
+       memset (info.src + info.in_mem, 0, 3 - info.in_mem);
+       info.trailing = 3 - info.in_mem;
+       info.in_mem = 3;
+       status = base64_write_func (&info, NULL, 0);
+    }
+
+    return status;
+}
+
+static void
+_cairo_svg_surface_emit_operator (cairo_output_stream_t *output,
+                                 cairo_svg_surface_t   *surface,
+                                 cairo_operator_t       op)
+{
+    if (surface->document->svg_version >= CAIRO_SVG_VERSION_1_2 &&
+       op != CAIRO_OPERATOR_OVER) {
+       _cairo_output_stream_printf (output, " comp-op=\"%s\"", _cairo_svg_surface_operators[op]);
+       if (!_cairo_operator_bounded_by_source (op))
+          _cairo_output_stream_printf (output, " clip-to-self=\"true\"");
+    }
+}
+
+static void
+_cairo_svg_surface_emit_operator_for_style (cairo_output_stream_t *output,
+                                           cairo_svg_surface_t   *surface,
+                                           cairo_operator_t     op)
+{
+    if (surface->document->svg_version >= CAIRO_SVG_VERSION_1_2 &&
+       op != CAIRO_OPERATOR_OVER) {
+       _cairo_output_stream_printf (output, "comp-op:%s;", _cairo_svg_surface_operators[op]);
+       if (!_cairo_operator_bounded_by_source (op))
+          _cairo_output_stream_printf (output, "clip-to-self:true;");
+    }
+}
+
+/**
+ * _cairo_svg_surface_emit_attr_value:
+ *
+ * Write the value to output the stream as a sequence of characters,
+ * while escaping those which have special meaning in the XML
+ * attribute's value context: &amp; and &quot;.
+ **/
+static void
+_cairo_svg_surface_emit_attr_value (cairo_output_stream_t *stream,
+                                   const unsigned char *value,
+                                   unsigned int length)
+{
+    const unsigned char *p;
+    const unsigned char *q;
+    unsigned int i;
+
+    /* we'll accumulate non-special chars in [q, p) range */
+    p = value;
+    q = p;
+    for (i = 0; i < length; i++, p++) {
+       if (*p == '&' || *p == '"') {
+           /* flush what's left before special char */
+           if (p != q) {
+               _cairo_output_stream_write (stream, q, p - q);
+               q = p + 1;
+           }
+
+           if (*p == '&')
+               _cairo_output_stream_printf (stream, "&amp;");
+           else // p == '"'
+               _cairo_output_stream_printf (stream, "&quot;");
+       }
+    }
+
+    /* flush the trailing chars if any */
+    if (p != q)
+       _cairo_output_stream_write (stream, q, p - q);
+}
+
+static cairo_status_t
+_cairo_svg_surface_emit_surface (cairo_svg_document_t *document,
+                                cairo_surface_t *surface)
+{
+    cairo_rectangle_int_t extents;
+    cairo_bool_t is_bounded;
+    cairo_status_t status;
+    const unsigned char *uri;
+    unsigned long uri_len;
+
+    if (_cairo_user_data_array_get_data (&surface->user_data,
+                                        (cairo_user_data_key_t *) document))
+    {
+       return CAIRO_STATUS_SUCCESS;
+    }
+
+    is_bounded = _cairo_surface_get_extents (surface, &extents);
+    assert (is_bounded);
+
+    _cairo_output_stream_printf (document->xml_node_defs,
+                                "<image id=\"image%d\" width=\"%d\" height=\"%d\"",
+                                surface->unique_id,
+                                extents.width, extents.height);
+
+    _cairo_output_stream_printf (document->xml_node_defs, " xlink:href=\"");
+
+    cairo_surface_get_mime_data (surface, CAIRO_MIME_TYPE_URI,
+                                &uri, &uri_len);
+    if (uri != NULL) {
+       _cairo_svg_surface_emit_attr_value (document->xml_node_defs,
+                                           uri, uri_len);
+    } else {
+       status = _cairo_surface_base64_encode (surface,
+                                              document->xml_node_defs);
+       if (unlikely (status))
+           return status;
+    }
+
+    _cairo_output_stream_printf (document->xml_node_defs, "\"/>\n");
+
+    /* and tag it */
+    return _cairo_user_data_array_set_data (&surface->user_data,
+                                           (cairo_user_data_key_t *) document,
+                                           document, NULL);
+}
+
+static cairo_status_t
+_cairo_svg_surface_emit_composite_surface_pattern (cairo_output_stream_t   *output,
+                                                  cairo_svg_surface_t   *svg_surface,
+                                                  cairo_operator_t       op,
+                                                  cairo_surface_pattern_t *pattern,
+                                                  int                    pattern_id,
+                                                  const cairo_matrix_t  *parent_matrix,
+                                                  const char            *extra_attributes)
+{
+    cairo_status_t status;
+    cairo_matrix_t p2u;
+
+    p2u = pattern->base.matrix;
+    status = cairo_matrix_invert (&p2u);
+    /* cairo_pattern_set_matrix ensures the matrix is invertible */
+    assert (status == CAIRO_STATUS_SUCCESS);
+
+    status = _cairo_svg_surface_emit_surface (svg_surface->document,
+                                             pattern->surface);
+    if (unlikely (status))
+       return status;
+
+    if (pattern_id != invalid_pattern_id) {
+       cairo_rectangle_int_t extents;
+       cairo_bool_t is_bounded;
+
+       is_bounded = _cairo_surface_get_extents (pattern->surface, &extents);
+       assert (is_bounded);
+
+       _cairo_output_stream_printf (output,
+                                    "<pattern id=\"pattern%d\" "
+                                    "patternUnits=\"userSpaceOnUse\" "
+                                    "width=\"%d\" height=\"%d\" ",
+                                    pattern_id,
+                                    extents.width, extents.height);
+       _cairo_svg_surface_emit_transform (output,
+                                          " patternTransform",
+                                          &p2u, parent_matrix);
+       _cairo_output_stream_printf (output, ">\n  ");
+    }
+
+    _cairo_output_stream_printf (output,
+                                "<use xlink:href=\"#image%d\"",
+                                pattern->surface->unique_id);
+    if (extra_attributes)
+       _cairo_output_stream_printf (output, " %s", extra_attributes);
+
+    if (pattern_id == invalid_pattern_id) {
+       _cairo_svg_surface_emit_operator (output, svg_surface, op);
+       _cairo_svg_surface_emit_transform (output,
+                                          " transform",
+                                          &p2u, parent_matrix);
+    }
+    _cairo_output_stream_printf (output, "/>\n");
+
+
+    if (pattern_id != invalid_pattern_id)
+       _cairo_output_stream_printf (output, "</pattern>\n");
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_svg_surface_emit_recording_surface (cairo_svg_document_t      *document,
+                                          cairo_recording_surface_t *source)
+{
+    cairo_status_t status;
+    cairo_surface_t *paginated_surface;
+    cairo_svg_surface_t *svg_surface;
+    cairo_array_t *page_set;
+
+    cairo_output_stream_t *contents;
+
+    if (_cairo_user_data_array_get_data (&source->base.user_data,
+                                        (cairo_user_data_key_t *) document))
+    {
+       return CAIRO_STATUS_SUCCESS;
+    }
+
+    paginated_surface = _cairo_svg_surface_create_for_document (document,
+                                                               source->base.content,
+                                                               source->extents_pixels.width,
+                                                               source->extents_pixels.height);
+    if (unlikely (paginated_surface->status))
+       return paginated_surface->status;
+
+    svg_surface = (cairo_svg_surface_t *)
+    _cairo_paginated_surface_get_target (paginated_surface);
+    cairo_surface_set_fallback_resolution (paginated_surface,
+                                          document->owner->x_fallback_resolution,
+                                          document->owner->y_fallback_resolution);
+    cairo_surface_set_device_offset (&svg_surface->base,
+                                    -source->extents_pixels.x,
+                                    -source->extents_pixels.y);
+
+    status = _cairo_recording_surface_replay (&source->base, paginated_surface);
+    if (unlikely (status)) {
+       cairo_surface_destroy (paginated_surface);
+       return status;
+    }
+
+    cairo_surface_show_page (paginated_surface);
+    status = cairo_surface_status (paginated_surface);
+    if (unlikely (status)) {
+       cairo_surface_destroy (paginated_surface);
+       return status;
+    }
+
+    if (! svg_surface->is_base_clip_emitted) {
+       svg_surface->is_base_clip_emitted = TRUE;
+       _cairo_output_stream_printf (document->xml_node_defs,
+                                    "<clipPath id=\"clip%d\">\n"
+                                    "  <rect width=\"%f\" height=\"%f\"/>\n"
+                                    "</clipPath>\n",
+                                    svg_surface->base_clip,
+                                    svg_surface->width,
+                                    svg_surface->height);
+    }
+
+    if (source->base.content == CAIRO_CONTENT_ALPHA) {
+       _cairo_svg_surface_emit_alpha_filter (document);
+       _cairo_output_stream_printf (document->xml_node_defs,
+                                    "<g id=\"surface%d\" "
+                                    "clip-path=\"url(#clip%d)\" "
+                                    "filter=\"url(#alpha)\">\n",
+                                    source->base.unique_id,
+                                    svg_surface->base_clip);
+    } else {
+       _cairo_output_stream_printf (document->xml_node_defs,
+                                    "<g id=\"surface%d\" "
+                                    "clip-path=\"url(#clip%d)\">\n",
+                                    source->base.unique_id,
+                                    svg_surface->base_clip);
+    }
+
+    contents = svg_surface->xml_node;
+    page_set = &svg_surface->page_set;
+
+    if (_cairo_memory_stream_length (contents) > 0) {
+       if (unlikely (_cairo_svg_surface_store_page (svg_surface) == NULL)) {
+           cairo_surface_destroy (paginated_surface);
+           return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+       }
+    }
+
+    if (page_set->num_elements > 0) {
+       cairo_svg_page_t *page;
+
+       page = _cairo_array_index (page_set, page_set->num_elements - 1);
+       _cairo_memory_stream_copy (page->xml_node, document->xml_node_defs);
+    }
+
+    _cairo_output_stream_printf (document->xml_node_defs, "</g>\n");
+
+    status = cairo_surface_status (paginated_surface);
+    cairo_surface_destroy (paginated_surface);
+
+    if (unlikely (status))
+       return status;
+
+    /* and tag it */
+    return _cairo_user_data_array_set_data (&source->base.user_data,
+                                           (cairo_user_data_key_t *) document,
+                                           document, NULL);
+}
+
+static cairo_status_t
+_cairo_svg_surface_emit_composite_recording_pattern (cairo_output_stream_t     *output,
+                                                    cairo_svg_surface_t        *surface,
+                                                    cairo_operator_t            op,
+                                                    cairo_surface_pattern_t    *pattern,
+                                                    int                         pattern_id,
+                                                    const cairo_matrix_t       *parent_matrix,
+                                                    const char                 *extra_attributes)
+{
+    cairo_svg_document_t *document = surface->document;
+    cairo_recording_surface_t *recording_surface;
+    cairo_matrix_t p2u;
+    cairo_status_t status;
+
+    p2u = pattern->base.matrix;
+    status = cairo_matrix_invert (&p2u);
+    /* cairo_pattern_set_matrix ensures the matrix is invertible */
+    assert (status == CAIRO_STATUS_SUCCESS);
+
+    recording_surface = (cairo_recording_surface_t *) pattern->surface;
+    status = _cairo_svg_surface_emit_recording_surface (document, recording_surface);
+    if (unlikely (status))
+       return status;
+
+    if (pattern_id != invalid_pattern_id) {
+       _cairo_output_stream_printf (output,
+                                    "<pattern id=\"pattern%d\" "
+                                    "patternUnits=\"userSpaceOnUse\" "
+                                    "width=\"%d\" height=\"%d\"",
+                                    pattern_id,
+                                    recording_surface->extents.width,
+                                    recording_surface->extents.height);
+       _cairo_svg_surface_emit_transform (output, " patternTransform", &p2u, parent_matrix);
+       _cairo_output_stream_printf (output, ">\n");
+    }
+
+    _cairo_output_stream_printf (output,
+                                "<use xlink:href=\"#surface%d\"",
+                                recording_surface->base.unique_id);
+
+    if (pattern_id == invalid_pattern_id) {
+       _cairo_svg_surface_emit_operator (output, surface, op);
+       _cairo_svg_surface_emit_transform (output, " transform", &p2u, parent_matrix);
+    }
+
+    if (extra_attributes)
+       _cairo_output_stream_printf (output, " %s", extra_attributes);
+
+    _cairo_output_stream_printf (output, "/>\n");
+
+    if (pattern_id != invalid_pattern_id)
+       _cairo_output_stream_printf (output, "</pattern>\n");
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_svg_surface_emit_composite_pattern (cairo_output_stream_t   *output,
+                                          cairo_svg_surface_t     *surface,
+                                          cairo_operator_t         op,
+                                          cairo_surface_pattern_t *pattern,
+                                          int                      pattern_id,
+                                          const cairo_matrix_t    *parent_matrix,
+                                          const char              *extra_attributes)
+{
+
+    if (_cairo_surface_is_recording (pattern->surface)) {
+       return _cairo_svg_surface_emit_composite_recording_pattern (output, surface,
+                                                                   op, pattern,
+                                                                   pattern_id,
+                                                                   parent_matrix,
+                                                                   extra_attributes);
+    }
+
+    return _cairo_svg_surface_emit_composite_surface_pattern (output, surface,
+                                                             op, pattern,
+                                                             pattern_id,
+                                                             parent_matrix,
+                                                             extra_attributes);
+}
+
+static cairo_status_t
+_cairo_svg_surface_emit_solid_pattern (cairo_svg_surface_t    *surface,
+                                      cairo_solid_pattern_t  *pattern,
+                                      cairo_output_stream_t  *style,
+                                      cairo_bool_t            is_stroke)
+{
+    _cairo_output_stream_printf (style, is_stroke ?
+                                "stroke:rgb(%f%%,%f%%,%f%%);stroke-opacity:%f;":
+                                "fill:rgb(%f%%,%f%%,%f%%);fill-opacity:%f;",
+                                pattern->color.red * 100.0,
+                                pattern->color.green * 100.0,
+                                pattern->color.blue * 100.0,
+                                pattern->color.alpha);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_svg_surface_emit_surface_pattern (cairo_svg_surface_t    *surface,
+                                        cairo_surface_pattern_t *pattern,
+                                        cairo_output_stream_t   *style,
+                                        cairo_bool_t             is_stroke,
+                                        const cairo_matrix_t    *parent_matrix)
+{
+    cairo_svg_document_t *document = surface->document;
+    cairo_status_t status;
+    int pattern_id;
+
+    pattern_id = document->pattern_id++;
+    status = _cairo_svg_surface_emit_composite_pattern (document->xml_node_defs,
+                                                       surface, CAIRO_OPERATOR_SOURCE, pattern,
+                                                       pattern_id, parent_matrix, NULL);
+    if (unlikely (status))
+       return status;
+
+    _cairo_output_stream_printf (style,
+                                "%s:url(#pattern%d);",
+                                is_stroke ? "stroke" : "fill",
+                                pattern_id);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_svg_surface_emit_pattern_stops (cairo_output_stream_t          *output,
+                                      cairo_gradient_pattern_t const *pattern,
+                                      double                          start_offset,
+                                      cairo_bool_t                    reverse_stops,
+                                      cairo_bool_t                    emulate_reflect)
+{
+    cairo_gradient_stop_t *stops;
+    double offset;
+    unsigned int n_stops;
+    unsigned int i;
+
+    if (pattern->n_stops < 1)
+       return CAIRO_STATUS_SUCCESS;
+
+    if (pattern->n_stops == 1) {
+           _cairo_output_stream_printf (output,
+                                        "<stop offset=\"%f\" style=\""
+                                        "stop-color:rgb(%f%%,%f%%,%f%%);"
+                                        "stop-opacity:%f;\"/>\n",
+                                        pattern->stops[0].offset,
+                                        pattern->stops[0].color.red   * 100.0,
+                                        pattern->stops[0].color.green * 100.0,
+                                        pattern->stops[0].color.blue  * 100.0,
+                                        pattern->stops[0].color.alpha);
+           return CAIRO_STATUS_SUCCESS;
+    }
+
+    if (emulate_reflect || reverse_stops) {
+       n_stops = emulate_reflect ? pattern->n_stops * 2 - 2: pattern->n_stops;
+       stops = _cairo_malloc_ab (n_stops, sizeof (cairo_gradient_stop_t));
+       if (unlikely (stops == NULL))
+           return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+       for (i = 0; i < pattern->n_stops; i++) {
+           if (reverse_stops) {
+               stops[i] = pattern->stops[pattern->n_stops - i - 1];
+               stops[i].offset = 1.0 - stops[i].offset;
+           } else
+               stops[i] = pattern->stops[i];
+           if (emulate_reflect) {
+               stops[i].offset /= 2;
+               if (i > 0 && i < (pattern->n_stops - 1)) {
+                   if (reverse_stops) {
+                       stops[i + pattern->n_stops - 1] = pattern->stops[i];
+                       stops[i + pattern->n_stops - 1].offset =
+                           0.5 + 0.5 * stops[i + pattern->n_stops - 1].offset;
+                   } else {
+                       stops[i + pattern->n_stops - 1] = pattern->stops[pattern->n_stops - i - 1];
+                       stops[i + pattern->n_stops - 1].offset =
+                           1 - 0.5 * stops[i + pattern->n_stops - 1].offset;
+                   }
+               }
+           }
+       }
+    } else {
+       n_stops = pattern->n_stops;
+       stops = pattern->stops;
+    }
+
+    if (start_offset >= 0.0)
+       for (i = 0; i < n_stops; i++) {
+           offset = start_offset + (1 - start_offset ) * stops[i].offset;
+           _cairo_output_stream_printf (output,
+                                        "<stop offset=\"%f\" style=\""
+                                        "stop-color:rgb(%f%%,%f%%,%f%%);"
+                                        "stop-opacity:%f;\"/>\n",
+                                        offset,
+                                        stops[i].color.red   * 100.0,
+                                        stops[i].color.green * 100.0,
+                                        stops[i].color.blue  * 100.0,
+                                        stops[i].color.alpha);
+       }
+    else {
+       cairo_bool_t found = FALSE;
+       unsigned int offset_index;
+       cairo_color_stop_t offset_color_start, offset_color_stop;
+
+       for (i = 0; i < n_stops; i++) {
+           if (stops[i].offset >= -start_offset) {
+               if (i > 0) {
+                   if (stops[i].offset != stops[i-1].offset) {
+                       double x0, x1;
+                       cairo_color_stop_t *color0, *color1;
+
+                       x0 = stops[i-1].offset;
+                       x1 = stops[i].offset;
+                       color0 = &stops[i-1].color;
+                       color1 = &stops[i].color;
+                       offset_color_start.red = color0->red + (color1->red - color0->red)
+                           * (-start_offset - x0) / (x1 - x0);
+                       offset_color_start.green = color0->green + (color1->green - color0->green)
+                           * (-start_offset - x0) / (x1 - x0);
+                       offset_color_start.blue = color0->blue + (color1->blue - color0->blue)
+                           * (-start_offset - x0) / (x1 - x0);
+                       offset_color_start.alpha = color0->alpha + (color1->alpha - color0->alpha)
+                           * (-start_offset - x0) / (x1 - x0);
+                       offset_color_stop = offset_color_start;
+                   } else {
+                       offset_color_stop = stops[i-1].color;
+                       offset_color_start = stops[i].color;
+                   }
+               } else
+                       offset_color_stop = offset_color_start = stops[i].color;
+           offset_index = i;
+           found = TRUE;
+           break;
+           }
+       }
+
+       if (!found) {
+           offset_index = n_stops - 1;
+           offset_color_stop = offset_color_start = stops[offset_index].color;
+       }
+
+       _cairo_output_stream_printf (output,
+                                    "<stop offset=\"0\" style=\""
+                                    "stop-color:rgb(%f%%,%f%%,%f%%);"
+                                    "stop-opacity:%f;\"/>\n",
+                                    offset_color_start.red   * 100.0,
+                                    offset_color_start.green * 100.0,
+                                    offset_color_start.blue  * 100.0,
+                                    offset_color_start.alpha);
+       for (i = offset_index; i < n_stops; i++) {
+           _cairo_output_stream_printf (output,
+                                        "<stop offset=\"%f\" style=\""
+                                        "stop-color:rgb(%f%%,%f%%,%f%%);"
+                                        "stop-opacity:%f;\"/>\n",
+                                        stops[i].offset + start_offset,
+                                        stops[i].color.red   * 100.0,
+                                        stops[i].color.green * 100.0,
+                                        stops[i].color.blue  * 100.0,
+                                        stops[i].color.alpha);
+       }
+       for (i = 0; i < offset_index; i++) {
+           _cairo_output_stream_printf (output,
+                                        "<stop offset=\"%f\" style=\""
+                                        "stop-color:rgb(%f%%,%f%%,%f%%);"
+                                        "stop-opacity:%f;\"/>\n",
+                                        1.0 + stops[i].offset + start_offset,
+                                        stops[i].color.red   * 100.0,
+                                        stops[i].color.green * 100.0,
+                                        stops[i].color.blue  * 100.0,
+                                        stops[i].color.alpha);
+       }
+
+       _cairo_output_stream_printf (output,
+                                    "<stop offset=\"1\" style=\""
+                                    "stop-color:rgb(%f%%,%f%%,%f%%);"
+                                    "stop-opacity:%f;\"/>\n",
+                                    offset_color_stop.red   * 100.0,
+                                    offset_color_stop.green * 100.0,
+                                    offset_color_stop.blue  * 100.0,
+                                    offset_color_stop.alpha);
+
+    }
+
+    if (reverse_stops || emulate_reflect)
+       free (stops);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_svg_surface_emit_pattern_extend (cairo_output_stream_t *output,
+                                       cairo_pattern_t       *pattern)
+{
+    switch (pattern->extend) {
+       case CAIRO_EXTEND_REPEAT:
+           _cairo_output_stream_printf (output, "spreadMethod=\"repeat\" ");
+           break;
+       case CAIRO_EXTEND_REFLECT:
+           _cairo_output_stream_printf (output, "spreadMethod=\"reflect\" ");
+           break;
+       case CAIRO_EXTEND_NONE:
+       case CAIRO_EXTEND_PAD:
+           break;
+    }
+}
+
+static cairo_status_t
+_cairo_svg_surface_emit_linear_pattern (cairo_svg_surface_t    *surface,
+                                       cairo_linear_pattern_t *pattern,
+                                       cairo_output_stream_t  *style,
+                                       cairo_bool_t            is_stroke,
+                                       const cairo_matrix_t   *parent_matrix)
+{
+    cairo_svg_document_t *document = surface->document;
+    cairo_matrix_t p2u;
+    cairo_status_t status;
+
+    p2u = pattern->base.base.matrix;
+    status = cairo_matrix_invert (&p2u);
+    /* cairo_pattern_set_matrix ensures the matrix is invertible */
+    assert (status == CAIRO_STATUS_SUCCESS);
+
+    _cairo_output_stream_printf (document->xml_node_defs,
+                                "<linearGradient id=\"linear%d\" "
+                                "gradientUnits=\"userSpaceOnUse\" "
+                                "x1=\"%f\" y1=\"%f\" x2=\"%f\" y2=\"%f\" ",
+                                document->linear_pattern_id,
+                                pattern->pd1.x, pattern->pd1.y,
+                                pattern->pd2.x, pattern->pd2.y);
+
+    _cairo_svg_surface_emit_pattern_extend (document->xml_node_defs, &pattern->base.base),
+    _cairo_svg_surface_emit_transform (document->xml_node_defs, "gradientTransform", &p2u, parent_matrix);
+    _cairo_output_stream_printf (document->xml_node_defs, ">\n");
+
+    status = _cairo_svg_surface_emit_pattern_stops (document->xml_node_defs,
+                                                   &pattern->base, 0.0,
+                                                   FALSE, FALSE);
+    if (unlikely (status))
+       return status;
+
+    _cairo_output_stream_printf (document->xml_node_defs,
+                                "</linearGradient>\n");
+
+    _cairo_output_stream_printf (style,
+                                "%s:url(#linear%d);",
+                                is_stroke ? "stroke" : "fill",
+                                document->linear_pattern_id);
+
+    document->linear_pattern_id++;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_svg_surface_emit_radial_pattern (cairo_svg_surface_t    *surface,
+                                       cairo_radial_pattern_t *pattern,
+                                       cairo_output_stream_t  *style,
+                                       cairo_bool_t            is_stroke,
+                                       const cairo_matrix_t   *parent_matrix)
+{
+    cairo_svg_document_t *document = surface->document;
+    cairo_matrix_t p2u;
+    cairo_extend_t extend;
+    double x0, y0, x1, y1, r0, r1;
+    double fx, fy;
+    cairo_bool_t reverse_stops;
+    cairo_status_t status;
+    cairo_circle_double_t *c0, *c1;
+
+    extend = pattern->base.base.extend;
+
+    if (pattern->cd1.radius < pattern->cd2.radius) {
+       c0 = &pattern->cd1;
+       c1 = &pattern->cd2;
+       reverse_stops = FALSE;
+    } else {
+       c0 = &pattern->cd2;
+       c1 = &pattern->cd1;
+       reverse_stops = TRUE;
+    }
+
+    x0 = c0->center.x;
+    y0 = c0->center.y;
+    r0 = c0->radius;
+    x1 = c1->center.x;
+    y1 = c1->center.y;
+    r1 = c1->radius;
+
+    p2u = pattern->base.base.matrix;
+    status = cairo_matrix_invert (&p2u);
+    /* cairo_pattern_set_matrix ensures the matrix is invertible */
+    assert (status == CAIRO_STATUS_SUCCESS);
+
+    if (r0 == r1) {
+       unsigned int n_stops = pattern->base.n_stops;
+
+       _cairo_output_stream_printf (document->xml_node_defs,
+                                    "<radialGradient id=\"radial%d\" "
+                                    "gradientUnits=\"userSpaceOnUse\" "
+                                    "cx=\"%f\" cy=\"%f\" "
+                                    "fx=\"%f\" fy=\"%f\" r=\"%f\" ",
+                                    document->radial_pattern_id,
+                                    x1, y1,
+                                    x1, y1, r1);
+       _cairo_svg_surface_emit_transform (document->xml_node_defs,
+                                          "gradientTransform",
+                                          &p2u, parent_matrix);
+       _cairo_output_stream_printf (document->xml_node_defs, ">\n");
+
+       if (extend == CAIRO_EXTEND_NONE || n_stops < 1)
+           _cairo_output_stream_printf (document->xml_node_defs,
+                                        "<stop offset=\"0\" style=\""
+                                        "stop-color:rgb(0%%,0%%,0%%);"
+                                        "stop-opacity:0;\"/>\n");
+       else {
+           _cairo_output_stream_printf (document->xml_node_defs,
+                                        "<stop offset=\"0\" style=\""
+                                        "stop-color:rgb(%f%%,%f%%,%f%%);"
+                                        "stop-opacity %f;\"/>\n",
+                                        pattern->base.stops[0].color.red   * 100.0,
+                                        pattern->base.stops[0].color.green * 100.0,
+                                        pattern->base.stops[0].color.blue  * 100.0,
+                                        pattern->base.stops[0].color.alpha);
+           if (n_stops > 1)
+               _cairo_output_stream_printf (document->xml_node_defs,
+                                            "<stop offset=\"0\" style=\""
+                                            "stop-color:rgb(%f%%,%f%%,%f%%);"
+                                            "stop-opacity:%f;\"/>\n",
+                                            pattern->base.stops[n_stops - 1].color.red   * 100.0,
+                                            pattern->base.stops[n_stops - 1].color.green * 100.0,
+                                            pattern->base.stops[n_stops - 1].color.blue  * 100.0,
+                                            pattern->base.stops[n_stops - 1].color.alpha);
+       }
+
+    } else {
+       double offset, r, x, y;
+       cairo_bool_t emulate_reflect = FALSE;
+
+       fx = (r1 * x0 - r0 * x1) / (r1 - r0);
+       fy = (r1 * y0 - r0 * y1) / (r1 - r0);
+
+       /* SVG doesn't support the inner circle and use instead a gradient focal.
+        * That means we need to emulate the cairo behaviour by processing the
+        * cairo gradient stops.
+        * The CAIRO_EXTENT_NONE and CAIRO_EXTENT_PAD modes are quite easy to handle,
+        * it's just a matter of stop position translation and calculation of
+        * the corresponding SVG radial gradient focal.
+        * The CAIRO_EXTENT_REFLECT and CAIRO_EXTEND_REPEAT modes require to compute a new
+        * radial gradient, with an new outer circle, equal to r1 - r0 in the CAIRO_EXTEND_REPEAT
+        * case, and 2 * (r1 - r0) in the CAIRO_EXTENT_REFLECT case, and a new gradient stop
+        * list that maps to the original cairo stop list.
+        */
+       if ((extend == CAIRO_EXTEND_REFLECT
+            || extend == CAIRO_EXTEND_REPEAT)
+           && r0 > 0.0) {
+           double r_org = r1;
+
+           if (extend == CAIRO_EXTEND_REFLECT) {
+               r1 = 2 * r1 - r0;
+               emulate_reflect = TRUE;
+           }
+
+           offset = fmod (r1, r1 - r0) / (r1 - r0) - 1.0;
+           r = r1 - r0;
+
+           /* New position of outer circle. */
+           x = r * (x1 - fx) / r_org + fx;
+           y = r * (y1 - fy) / r_org + fy;
+
+           x1 = x;
+           y1 = y;
+           r1 = r;
+           r0 = 0.0;
+       } else {
+           offset = r0 / r1;
+       }
+
+       _cairo_output_stream_printf (document->xml_node_defs,
+                                    "<radialGradient id=\"radial%d\" "
+                                    "gradientUnits=\"userSpaceOnUse\" "
+                                    "cx=\"%f\" cy=\"%f\" "
+                                    "fx=\"%f\" fy=\"%f\" r=\"%f\" ",
+                                    document->radial_pattern_id,
+                                    x1, y1,
+                                    fx, fy, r1);
+
+       if (emulate_reflect)
+           _cairo_output_stream_printf (document->xml_node_defs, "spreadMethod=\"repeat\" ");
+       else
+           _cairo_svg_surface_emit_pattern_extend (document->xml_node_defs, &pattern->base.base);
+       _cairo_svg_surface_emit_transform (document->xml_node_defs, "gradientTransform", &p2u, parent_matrix);
+       _cairo_output_stream_printf (document->xml_node_defs, ">\n");
+
+       /* To support cairo's EXTEND_NONE, (for which SVG has no similar
+        * notion), we add transparent color stops on either end of the
+        * user-provided stops. */
+       if (extend == CAIRO_EXTEND_NONE) {
+           _cairo_output_stream_printf (document->xml_node_defs,
+                                        "<stop offset=\"0\" style=\""
+                                        "stop-color:rgb(0%%,0%%,0%%);"
+                                        "stop-opacity:0;\"/>\n");
+           if (r0 != 0.0)
+               _cairo_output_stream_printf (document->xml_node_defs,
+                                            "<stop offset=\"%f\" style=\""
+                                            "stop-color:rgb(0%%,0%%,0%%);"
+                                            "stop-opacity:0;\"/>\n",
+                                            r0 / r1);
+       }
+       status = _cairo_svg_surface_emit_pattern_stops (document->xml_node_defs,
+                                                       &pattern->base, offset,
+                                                       reverse_stops,
+                                                       emulate_reflect);
+       if (unlikely (status))
+           return status;
+
+       if (pattern->base.base.extend == CAIRO_EXTEND_NONE)
+           _cairo_output_stream_printf (document->xml_node_defs,
+                                        "<stop offset=\"1.0\" style=\""
+                                        "stop-color:rgb(0%%,0%%,0%%);"
+                                        "stop-opacity:0;\"/>\n");
+    }
+
+    _cairo_output_stream_printf (document->xml_node_defs,
+                                "</radialGradient>\n");
+
+    _cairo_output_stream_printf (style,
+                                "%s:url(#radial%d);",
+                                is_stroke ? "stroke" : "fill",
+                                document->radial_pattern_id);
+
+    document->radial_pattern_id++;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_svg_surface_emit_pattern (cairo_svg_surface_t   *surface,
+                                const cairo_pattern_t       *pattern,
+                                cairo_output_stream_t *output,
+                                cairo_bool_t           is_stroke,
+                                const cairo_matrix_t  *parent_matrix)
+{
+    switch (pattern->type) {
+    case CAIRO_PATTERN_TYPE_SOLID:
+       return _cairo_svg_surface_emit_solid_pattern (surface, (cairo_solid_pattern_t *) pattern,
+                                                     output, is_stroke);
+
+    case CAIRO_PATTERN_TYPE_SURFACE:
+       return _cairo_svg_surface_emit_surface_pattern (surface, (cairo_surface_pattern_t *) pattern,
+                                                       output, is_stroke, parent_matrix);
+
+    case CAIRO_PATTERN_TYPE_LINEAR:
+       return _cairo_svg_surface_emit_linear_pattern (surface, (cairo_linear_pattern_t *) pattern,
+                                                      output, is_stroke, parent_matrix);
+
+    case CAIRO_PATTERN_TYPE_RADIAL:
+       return _cairo_svg_surface_emit_radial_pattern (surface, (cairo_radial_pattern_t *) pattern,
+                                                      output, is_stroke, parent_matrix);
+
+    case CAIRO_PATTERN_TYPE_MESH:
+       ASSERT_NOT_REACHED;
+    }
+    return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH);
+}
+
+static cairo_status_t
+_cairo_svg_surface_emit_fill_style (cairo_output_stream_t      *output,
+                                   cairo_svg_surface_t         *surface,
+                                   cairo_operator_t             op,
+                                   const cairo_pattern_t       *source,
+                                   cairo_fill_rule_t            fill_rule,
+                                   const cairo_matrix_t        *parent_matrix)
+{
+    _cairo_output_stream_printf (output,
+                                "fill-rule:%s;",
+                                fill_rule == CAIRO_FILL_RULE_EVEN_ODD ?
+                                "evenodd" : "nonzero");
+    _cairo_svg_surface_emit_operator_for_style (output, surface, op);
+    return _cairo_svg_surface_emit_pattern (surface, source, output, FALSE, parent_matrix);
+}
+
+static cairo_status_t
+_cairo_svg_surface_emit_stroke_style (cairo_output_stream_t       *output,
+                                     cairo_svg_surface_t          *surface,
+                                     cairo_operator_t              op,
+                                     const cairo_pattern_t        *source,
+                                     const cairo_stroke_style_t   *stroke_style,
+                                     const cairo_matrix_t         *parent_matrix)
+{
+    cairo_status_t status;
+    const char *line_cap, *line_join;
+    unsigned int i;
+
+    switch (stroke_style->line_cap) {
+       case CAIRO_LINE_CAP_BUTT:
+           line_cap = "butt";
+           break;
+       case CAIRO_LINE_CAP_ROUND:
+           line_cap = "round";
+           break;
+       case CAIRO_LINE_CAP_SQUARE:
+           line_cap = "square";
+           break;
+       default:
+           ASSERT_NOT_REACHED;
+    }
+
+    switch (stroke_style->line_join) {
+       case CAIRO_LINE_JOIN_MITER:
+           line_join = "miter";
+           break;
+       case CAIRO_LINE_JOIN_ROUND:
+           line_join = "round";
+           break;
+       case CAIRO_LINE_JOIN_BEVEL:
+           line_join = "bevel";
+           break;
+       default:
+           ASSERT_NOT_REACHED;
+    }
+
+    _cairo_output_stream_printf (output,
+                                "stroke-width:%f;"
+                                "stroke-linecap:%s;"
+                                "stroke-linejoin:%s;",
+                                stroke_style->line_width,
+                                line_cap,
+                                line_join);
+
+     status = _cairo_svg_surface_emit_pattern (surface, source, output, TRUE, parent_matrix);
+     if (unlikely (status))
+        return status;
+
+     _cairo_svg_surface_emit_operator_for_style (output, surface, op);
+
+    if (stroke_style->num_dashes > 0) {
+       _cairo_output_stream_printf (output, "stroke-dasharray:");
+       for (i = 0; i < stroke_style->num_dashes; i++) {
+           _cairo_output_stream_printf (output, "%f",
+                                        stroke_style->dash[i]);
+           if (i + 1 < stroke_style->num_dashes)
+               _cairo_output_stream_printf (output, ",");
+           else
+               _cairo_output_stream_printf (output, ";");
+       }
+       if (stroke_style->dash_offset != 0.0) {
+           _cairo_output_stream_printf (output,
+                                        "stroke-dashoffset:%f;",
+                                        stroke_style->dash_offset);
+       }
+    }
+
+    _cairo_output_stream_printf (output,
+                                "stroke-miterlimit:%f;",
+                                stroke_style->miter_limit);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_svg_surface_fill_stroke (void                   *abstract_surface,
+                               cairo_operator_t         fill_op,
+                               const cairo_pattern_t   *fill_source,
+                               cairo_fill_rule_t        fill_rule,
+                               double                   fill_tolerance,
+                               cairo_antialias_t        fill_antialias,
+                               const cairo_path_fixed_t*path,
+                               cairo_operator_t         stroke_op,
+                               const cairo_pattern_t   *stroke_source,
+                               const cairo_stroke_style_t      *stroke_style,
+                               const cairo_matrix_t            *stroke_ctm,
+                               const cairo_matrix_t            *stroke_ctm_inverse,
+                               double                   stroke_tolerance,
+                               cairo_antialias_t        stroke_antialias,
+                               const cairo_clip_t      *clip)
+{
+    cairo_svg_surface_t *surface = abstract_surface;
+    cairo_status_t status;
+
+    status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
+    if (unlikely (status))
+       return status;
+
+    _cairo_output_stream_printf (surface->xml_node, "<path style=\"");
+    status = _cairo_svg_surface_emit_fill_style (surface->xml_node, surface, fill_op,
+                                                fill_source, fill_rule, stroke_ctm_inverse);
+    if (unlikely (status))
+       return status;
+
+    status = _cairo_svg_surface_emit_stroke_style (surface->xml_node, surface, stroke_op,
+                                                  stroke_source, stroke_style, stroke_ctm_inverse);
+    if (unlikely (status))
+       return status;
+
+    _cairo_output_stream_printf (surface->xml_node, "\" ");
+
+    _cairo_svg_surface_emit_path (surface->xml_node, path, stroke_ctm_inverse);
+
+    _cairo_svg_surface_emit_transform (surface->xml_node, " transform", stroke_ctm, NULL);
+    _cairo_output_stream_printf (surface->xml_node, "/>\n");
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_svg_surface_fill (void                  *abstract_surface,
+                        cairo_operator_t        op,
+                        const cairo_pattern_t  *source,
+                        const cairo_path_fixed_t*path,
+                        cairo_fill_rule_t       fill_rule,
+                        double                  tolerance,
+                        cairo_antialias_t       antialias,
+                        const cairo_clip_t     *clip)
+{
+    cairo_svg_surface_t *surface = abstract_surface;
+    cairo_status_t status;
+
+    if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
+       return _cairo_svg_surface_analyze_operation (surface, op, source);
+
+    assert (_cairo_svg_surface_operation_supported (surface, op, source));
+
+    status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
+    if (unlikely (status))
+       return status;
+
+    _cairo_output_stream_printf (surface->xml_node, "<path style=\" stroke:none;");
+    status = _cairo_svg_surface_emit_fill_style (surface->xml_node, surface, op, source, fill_rule, NULL);
+    if (unlikely (status))
+       return status;
+
+    _cairo_output_stream_printf (surface->xml_node, "\" ");
+
+    _cairo_svg_surface_emit_path (surface->xml_node, path, NULL);
+
+    _cairo_output_stream_printf (surface->xml_node, "/>\n");
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_bool_t
+_cairo_svg_surface_get_extents (void                   *abstract_surface,
+                               cairo_rectangle_int_t   *rectangle)
+{
+    cairo_svg_surface_t *surface = abstract_surface;
+
+    rectangle->x = 0;
+    rectangle->y = 0;
+
+    /* XXX: The conversion to integers here is pretty bogus, (not to
+     * mention the arbitrary limitation of width to a short(!). We
+     * may need to come up with a better interface for get_size.
+     */
+    rectangle->width  = ceil (surface->width);
+    rectangle->height = ceil (surface->height);
+
+    return TRUE;
+}
+
+static cairo_status_t
+_cairo_svg_surface_emit_paint (cairo_output_stream_t *output,
+                              cairo_svg_surface_t   *surface,
+                              cairo_operator_t       op,
+                              const cairo_pattern_t         *source,
+                              const cairo_pattern_t         *mask_source,
+                              const char            *extra_attributes)
+{
+    cairo_status_t status;
+
+    if (source->type == CAIRO_PATTERN_TYPE_SURFACE &&
+       source->extend == CAIRO_EXTEND_NONE)
+       return _cairo_svg_surface_emit_composite_pattern (output,
+                                                         surface,
+                                                         op,
+                                                         (cairo_surface_pattern_t *) source,
+                                                         invalid_pattern_id,
+                                                         mask_source ? &mask_source->matrix :NULL,
+                                                         extra_attributes);
+
+    _cairo_output_stream_printf (output,
+                                "<rect x=\"0\" y=\"0\" "
+                                "width=\"%f\" height=\"%f\" "
+                                "style=\"",
+                                surface->width, surface->height);
+    _cairo_svg_surface_emit_operator_for_style (output, surface, op);
+    status = _cairo_svg_surface_emit_pattern (surface, source, output, FALSE, NULL);
+    if (unlikely (status))
+       return status;
+
+    _cairo_output_stream_printf (output, "stroke:none;\"");
+
+    if (extra_attributes)
+       _cairo_output_stream_printf (output, " %s", extra_attributes);
+
+    _cairo_output_stream_printf (output, "/>\n");
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_svg_surface_paint (void             *abstract_surface,
+                         cairo_operator_t   op,
+                         const cairo_pattern_t   *source,
+                         const cairo_clip_t      *clip)
+{
+    cairo_status_t status;
+    cairo_svg_surface_t *surface = abstract_surface;
+
+    /* Emulation of clear and source operators, when no clipping region
+     * is defined. We just delete existing content of surface root node,
+     * and exit early if operator is clear.
+     */
+    if ((op == CAIRO_OPERATOR_CLEAR || op == CAIRO_OPERATOR_SOURCE) &&
+       clip == NULL)
+    {
+       switch (surface->paginated_mode) {
+       case CAIRO_PAGINATED_MODE_FALLBACK:
+           ASSERT_NOT_REACHED;
+       case CAIRO_PAGINATED_MODE_ANALYZE:
+           return CAIRO_STATUS_SUCCESS;
+
+       case CAIRO_PAGINATED_MODE_RENDER:
+           status = _cairo_output_stream_destroy (surface->xml_node);
+           if (unlikely (status)) {
+               surface->xml_node = NULL;
+               return status;
+           }
+
+           surface->xml_node = _cairo_memory_stream_create ();
+           if (_cairo_output_stream_get_status (surface->xml_node)) {
+               status = _cairo_output_stream_destroy (surface->xml_node);
+               surface->xml_node = NULL;
+               return status;
+           }
+
+           if (op == CAIRO_OPERATOR_CLEAR) {
+               if (surface->content == CAIRO_CONTENT_COLOR) {
+                   _cairo_output_stream_printf (surface->xml_node,
+                                                "<rect "
+                                                "width=\"%f\" height=\"%f\" "
+                                                "style=\"opacity:1;"
+                                                "stroke:none;"
+                                                "fill:rgb(0,0,0);\"/>\n",
+                                                surface->width, surface->height);
+               }
+               return CAIRO_STATUS_SUCCESS;
+           }
+           break;
+       }
+    } else {
+       if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
+           return _cairo_svg_surface_analyze_operation (surface, op, source);
+
+       assert (_cairo_svg_surface_operation_supported (surface, op, source));
+    }
+
+    status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
+    if (unlikely (status))
+       return status;
+
+    return _cairo_svg_surface_emit_paint (surface->xml_node,
+                                         surface, op, source, 0, NULL);
+}
+
+static cairo_int_status_t
+_cairo_svg_surface_mask (void              *abstract_surface,
+                        cairo_operator_t     op,
+                        const cairo_pattern_t      *source,
+                        const cairo_pattern_t      *mask,
+                        const cairo_clip_t         *clip)
+{
+    cairo_status_t status;
+    cairo_svg_surface_t *surface = abstract_surface;
+    cairo_svg_document_t *document = surface->document;
+    cairo_output_stream_t *mask_stream;
+    char buffer[64];
+    cairo_bool_t discard_filter = FALSE;
+    unsigned int mask_id;
+
+    if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) {
+       cairo_status_t source_status, mask_status;
+
+       source_status = _cairo_svg_surface_analyze_operation (surface, op, source);
+       if (_cairo_status_is_error (source_status))
+           return source_status;
+
+       if (mask->has_component_alpha) {
+           mask_status = CAIRO_INT_STATUS_UNSUPPORTED;
+       } else {
+           mask_status = _cairo_svg_surface_analyze_operation (surface, op, mask);
+           if (_cairo_status_is_error (mask_status))
+               return mask_status;
+       }
+
+       return _cairo_analysis_surface_merge_status (source_status,
+                                                    mask_status);
+    }
+
+    assert (_cairo_svg_surface_operation_supported (surface, op, source));
+    assert (_cairo_svg_surface_operation_supported (surface, CAIRO_OPERATOR_OVER, mask));
+
+    status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
+    if (unlikely (status))
+       return status;
+
+    if (mask->type == CAIRO_PATTERN_TYPE_SURFACE) {
+       const cairo_surface_pattern_t *surface_pattern = (const cairo_surface_pattern_t*) mask;
+       cairo_content_t content = cairo_surface_get_content (surface_pattern->surface);
+       if (content == CAIRO_CONTENT_ALPHA)
+           discard_filter = TRUE;
+    }
+
+    if (!discard_filter)
+       _cairo_svg_surface_emit_alpha_filter (document);
+
+    /* _cairo_svg_surface_emit_paint() will output a pattern definition to
+     * document->xml_node_defs so we need to write the mask element to
+     * a temporary stream and then copy that to xml_node_defs. */
+    mask_stream = _cairo_memory_stream_create ();
+    if (_cairo_output_stream_get_status (mask_stream))
+       return _cairo_output_stream_destroy (mask_stream);
+
+    mask_id = _cairo_svg_document_allocate_mask_id (document);
+
+    _cairo_output_stream_printf (mask_stream,
+                                "<mask id=\"mask%d\">\n"
+                                "%s",
+                                mask_id,
+                                discard_filter ? "" : "  <g filter=\"url(#alpha)\">\n");
+    status = _cairo_svg_surface_emit_paint (mask_stream, surface, CAIRO_OPERATOR_OVER, mask, source, NULL);
+    if (unlikely (status)) {
+       cairo_status_t ignore = _cairo_output_stream_destroy (mask_stream);
+       return status;
+       (void) ignore;
+    }
+
+    _cairo_output_stream_printf (mask_stream,
+                                "%s"
+                                "</mask>\n",
+                                discard_filter ? "" : "  </g>\n");
+    _cairo_memory_stream_copy (mask_stream, document->xml_node_defs);
+
+    status = _cairo_output_stream_destroy (mask_stream);
+    if (unlikely (status))
+       return status;
+
+    snprintf (buffer, sizeof buffer, "mask=\"url(#mask%d)\"",
+             mask_id);
+    status = _cairo_svg_surface_emit_paint (surface->xml_node, surface, op, source, 0, buffer);
+    if (unlikely (status))
+       return status;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_svg_surface_stroke (void                        *abstract_dst,
+                          cairo_operator_t      op,
+                          const cairo_pattern_t *source,
+                          const cairo_path_fixed_t*path,
+                          const cairo_stroke_style_t *stroke_style,
+                          const cairo_matrix_t *ctm,
+                          const cairo_matrix_t *ctm_inverse,
+                          double                tolerance,
+                          cairo_antialias_t     antialias,
+                          const cairo_clip_t   *clip)
+{
+    cairo_svg_surface_t *surface = abstract_dst;
+    cairo_status_t status;
+
+    if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
+       return _cairo_svg_surface_analyze_operation (surface, op, source);
+
+    assert (_cairo_svg_surface_operation_supported (surface, op, source));
+
+    status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
+    if (unlikely (status))
+       return status;
+
+    _cairo_output_stream_printf (surface->xml_node, "<path style=\"fill:none;");
+    status = _cairo_svg_surface_emit_stroke_style (surface->xml_node, surface, op,
+                                                  source, stroke_style, ctm_inverse);
+    if (unlikely (status))
+       return status;
+
+    _cairo_output_stream_printf (surface->xml_node, "\" ");
+
+    _cairo_svg_surface_emit_path (surface->xml_node, path, ctm_inverse);
+
+    _cairo_svg_surface_emit_transform (surface->xml_node, " transform", ctm, NULL);
+    _cairo_output_stream_printf (surface->xml_node, "/>\n");
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_svg_surface_show_glyphs (void                   *abstract_surface,
+                               cairo_operator_t         op,
+                               const cairo_pattern_t   *pattern,
+                               cairo_glyph_t           *glyphs,
+                               int                      num_glyphs,
+                               cairo_scaled_font_t     *scaled_font,
+                               const cairo_clip_t      *clip)
+{
+    cairo_svg_surface_t *surface = abstract_surface;
+    cairo_svg_document_t *document = surface->document;
+    cairo_path_fixed_t path;
+    cairo_int_status_t status;
+    cairo_scaled_font_subsets_glyph_t subset_glyph;
+    int i;
+
+    if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
+       return _cairo_svg_surface_analyze_operation (surface, op, pattern);
+
+    assert (_cairo_svg_surface_operation_supported (surface, op, pattern));
+
+    if (num_glyphs <= 0)
+       return CAIRO_STATUS_SUCCESS;
+
+    status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
+    if (unlikely (status))
+       return status;
+
+    /* FIXME it's probably possible to apply a pattern of a gradient to
+     * a group of symbols, but I don't know how yet. Gradients or patterns
+     * are translated by x and y properties of use element. */
+    if (pattern->type != CAIRO_PATTERN_TYPE_SOLID)
+       goto FALLBACK;
+
+    _cairo_output_stream_printf (surface->xml_node, "<g style=\"");
+    status = _cairo_svg_surface_emit_pattern (surface, pattern,
+                                             surface->xml_node, FALSE, NULL);
+    if (unlikely (status))
+       return status;
+
+    _cairo_svg_surface_emit_operator_for_style (surface->xml_node, surface, op);
+
+    _cairo_output_stream_printf (surface->xml_node, "\">\n");
+
+    for (i = 0; i < num_glyphs; i++) {
+       status = _cairo_scaled_font_subsets_map_glyph (document->font_subsets,
+                                                      scaled_font, glyphs[i].index,
+                                                      NULL, 0,
+                                                       &subset_glyph);
+       if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
+           _cairo_output_stream_printf (surface->xml_node, "</g>\n");
+
+           glyphs += i;
+           num_glyphs -= i;
+           goto FALLBACK;
+       }
+
+       if (unlikely (status))
+           return status;
+
+       _cairo_output_stream_printf (surface->xml_node,
+                                    "  <use xlink:href=\"#glyph%d-%d\" "
+                                    "x=\"%f\" y=\"%f\"/>\n",
+                                    subset_glyph.font_id,
+                                     subset_glyph.subset_glyph_index,
+                                    glyphs[i].x, glyphs[i].y);
+    }
+
+    _cairo_output_stream_printf (surface->xml_node, "</g>\n");
+
+    return CAIRO_STATUS_SUCCESS;
+
+FALLBACK:
+    _cairo_path_fixed_init (&path);
+
+    status = _cairo_scaled_font_glyph_path (scaled_font,
+                                           (cairo_glyph_t *) glyphs,
+                                           num_glyphs, &path);
+
+    if (unlikely (status)) {
+       _cairo_path_fixed_fini (&path);
+       return status;
+    }
+
+    status = _cairo_svg_surface_fill (abstract_surface, op, pattern,
+                                     &path, CAIRO_FILL_RULE_WINDING,
+                                     0.0, CAIRO_ANTIALIAS_SUBPIXEL,
+                                     clip);
+
+    _cairo_path_fixed_fini (&path);
+
+    return status;
+}
+
+static void
+_cairo_svg_surface_get_font_options (void                  *abstract_surface,
+                                    cairo_font_options_t  *options)
+{
+    _cairo_font_options_init_default (options);
+
+    cairo_font_options_set_hint_style (options, CAIRO_HINT_STYLE_NONE);
+    cairo_font_options_set_hint_metrics (options, CAIRO_HINT_METRICS_OFF);
+    cairo_font_options_set_antialias (options, CAIRO_ANTIALIAS_GRAY);
+    _cairo_font_options_set_round_glyph_positions (options, CAIRO_ROUND_GLYPH_POS_OFF);
+}
+
+static const cairo_surface_backend_t cairo_svg_surface_backend = {
+       CAIRO_SURFACE_TYPE_SVG,
+       _cairo_svg_surface_finish,
+
+       _cairo_default_context_create,
+
+       NULL, /* create_similar: handled by wrapper */
+       NULL, /* create_similar_image */
+       NULL, /* map to image */
+       NULL, /* unmap image */
+
+       NULL, /* acquire_source_image */
+       NULL, /* release_source_image */
+       NULL, /* snapshot */
+
+       _cairo_svg_surface_copy_page,
+       _cairo_svg_surface_show_page,
+
+       _cairo_svg_surface_get_extents,
+       _cairo_svg_surface_get_font_options,
+
+       NULL, /* flush */
+       NULL, /* mark dirty rectangle */
+
+       _cairo_svg_surface_paint,
+       _cairo_svg_surface_mask,
+       _cairo_svg_surface_stroke,
+       _cairo_svg_surface_fill,
+       _cairo_svg_surface_fill_stroke,
+       _cairo_svg_surface_show_glyphs,
+};
+
+static cairo_status_t
+_cairo_svg_document_create (cairo_output_stream_t       *output_stream,
+                           double                        width,
+                           double                        height,
+                           cairo_svg_version_t           version,
+                           cairo_svg_document_t        **document_out)
+{
+    cairo_svg_document_t *document;
+    cairo_status_t status, status_ignored;
+
+    if (output_stream->status)
+       return output_stream->status;
+
+    document = malloc (sizeof (cairo_svg_document_t));
+    if (unlikely (document == NULL))
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    /* The use of defs for font glyphs imposes no per-subset limit. */
+    document->font_subsets = _cairo_scaled_font_subsets_create_scaled ();
+    if (unlikely (document->font_subsets == NULL)) {
+       status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+       goto CLEANUP_DOCUMENT;
+    }
+
+    document->output_stream = output_stream;
+    document->refcount = 1;
+    document->owner = NULL;
+    document->finished = FALSE;
+    document->width = width;
+    document->height = height;
+
+    document->linear_pattern_id = 0;
+    document->radial_pattern_id = 0;
+    document->pattern_id = 0;
+    document->filter_id = 0;
+    document->clip_id = 0;
+    document->mask_id = 0;
+
+    document->xml_node_defs = _cairo_memory_stream_create ();
+    status = _cairo_output_stream_get_status (document->xml_node_defs);
+    if (unlikely (status))
+       goto CLEANUP_NODE_DEFS;
+
+    document->xml_node_glyphs = _cairo_memory_stream_create ();
+    status = _cairo_output_stream_get_status (document->xml_node_glyphs);
+    if (unlikely (status))
+       goto CLEANUP_NODE_GLYPHS;
+
+    document->alpha_filter = FALSE;
+
+    document->svg_version = version;
+
+    *document_out = document;
+    return CAIRO_STATUS_SUCCESS;
+
+  CLEANUP_NODE_GLYPHS:
+    status_ignored = _cairo_output_stream_destroy (document->xml_node_glyphs);
+  CLEANUP_NODE_DEFS:
+    status_ignored = _cairo_output_stream_destroy (document->xml_node_defs);
+    _cairo_scaled_font_subsets_destroy (document->font_subsets);
+  CLEANUP_DOCUMENT:
+    free (document);
+    return status;
+}
+
+static cairo_svg_document_t *
+_cairo_svg_document_reference (cairo_svg_document_t *document)
+{
+    document->refcount++;
+
+    return document;
+}
+
+static unsigned int
+_cairo_svg_document_allocate_mask_id (cairo_svg_document_t *document)
+{
+    return document->mask_id++;
+}
+
+static cairo_status_t
+_cairo_svg_document_destroy (cairo_svg_document_t *document)
+{
+    cairo_status_t status;
+
+    document->refcount--;
+    if (document->refcount > 0)
+      return CAIRO_STATUS_SUCCESS;
+
+    status = _cairo_svg_document_finish (document);
+
+    free (document);
+
+    return status;
+}
+
+static cairo_status_t
+_cairo_svg_document_finish (cairo_svg_document_t *document)
+{
+    cairo_status_t status, status2;
+    cairo_output_stream_t *output = document->output_stream;
+    cairo_svg_page_t *page;
+    unsigned int i;
+
+    if (document->finished)
+       return CAIRO_STATUS_SUCCESS;
+
+    /*
+     * Should we add DOCTYPE?
+     *
+     * Google says no.
+     *
+     * http://tech.groups.yahoo.com/group/svg-developers/message/48562:
+     *   There's a bunch of issues, but just to pick a few:
+     *   - they'll give false positives.
+     *   - they'll give false negatives.
+     *   - they're namespace-unaware.
+     *   - they don't wildcard.
+     *   So when they say OK they really haven't checked anything, when
+     *   they say NOT OK they might be on crack, and like all
+     *   namespace-unaware things they're a dead branch of the XML tree.
+     *
+     * http://jwatt.org/svg/authoring/:
+     *   Unfortunately the SVG DTDs are a source of so many issues that the
+     *   SVG WG has decided not to write one for the upcoming SVG 1.2
+     *   standard. In fact SVG WG members are even telling people not to use
+     *   a DOCTYPE declaration in SVG 1.0 and 1.1 documents.
+     */
+
+    _cairo_output_stream_printf (output,
+                                "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+                                "<svg xmlns=\"http://www.w3.org/2000/svg\" "
+                                "xmlns:xlink=\"http://www.w3.org/1999/xlink\" "
+                                "width=\"%fpt\" height=\"%fpt\" "
+                                "viewBox=\"0 0 %f %f\" version=\"%s\">\n",
+                                document->width, document->height,
+                                document->width, document->height,
+                                _cairo_svg_internal_version_strings [document->svg_version]);
+
+    status = _cairo_svg_document_emit_font_subsets (document);
+
+    if (_cairo_memory_stream_length (document->xml_node_glyphs) > 0 ||
+       _cairo_memory_stream_length (document->xml_node_defs) > 0) {
+       _cairo_output_stream_printf (output, "<defs>\n");
+       if (_cairo_memory_stream_length (document->xml_node_glyphs) > 0) {
+           _cairo_output_stream_printf (output, "<g>\n");
+           _cairo_memory_stream_copy (document->xml_node_glyphs, output);
+           _cairo_output_stream_printf (output, "</g>\n");
+       }
+       _cairo_memory_stream_copy (document->xml_node_defs, output);
+       _cairo_output_stream_printf (output, "</defs>\n");
+    }
+
+    if (document->owner != NULL) {
+       cairo_svg_surface_t *surface;
+
+       surface = (cairo_svg_surface_t *) _cairo_paginated_surface_get_target (document->owner);
+       if (surface->xml_node != NULL &&
+               _cairo_memory_stream_length (surface->xml_node) > 0) {
+           if (unlikely (_cairo_svg_surface_store_page (surface) == NULL)) {
+               if (status == CAIRO_STATUS_SUCCESS)
+                   status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+           }
+       }
+
+       if (surface->page_set.num_elements > 1 &&
+           _cairo_svg_version_has_page_set_support (document->svg_version)) {
+           _cairo_output_stream_printf (output, "<pageSet>\n");
+           for (i = 0; i < surface->page_set.num_elements; i++) {
+               page = _cairo_array_index (&surface->page_set, i);
+               _cairo_output_stream_printf (output, "<page>\n");
+               _cairo_output_stream_printf (output,
+                                            "<g id=\"surface%d\">\n",
+                                            page->surface_id);
+               _cairo_memory_stream_copy (page->xml_node, output);
+               _cairo_output_stream_printf (output, "</g>\n</page>\n");
+           }
+           _cairo_output_stream_printf (output, "</pageSet>\n");
+       } else if (surface->page_set.num_elements > 0) {
+           page = _cairo_array_index (&surface->page_set, surface->page_set.num_elements - 1);
+           _cairo_output_stream_printf (output,
+                                        "<g id=\"surface%d\">\n",
+                                        page->surface_id);
+           _cairo_memory_stream_copy (page->xml_node, output);
+           _cairo_output_stream_printf (output, "</g>\n");
+       }
+    }
+
+    _cairo_output_stream_printf (output, "</svg>\n");
+
+    status2 = _cairo_output_stream_destroy (document->xml_node_glyphs);
+    if (status == CAIRO_STATUS_SUCCESS)
+       status = status2;
+
+    status2 = _cairo_output_stream_destroy (document->xml_node_defs);
+    if (status == CAIRO_STATUS_SUCCESS)
+       status = status2;
+
+    status2 = _cairo_output_stream_destroy (output);
+    if (status == CAIRO_STATUS_SUCCESS)
+       status = status2;
+
+    document->finished = TRUE;
+
+    return status;
+}
+
+static void
+_cairo_svg_surface_set_paginated_mode (void                    *abstract_surface,
+                                      cairo_paginated_mode_t    paginated_mode)
+{
+    cairo_svg_surface_t *surface = abstract_surface;
+
+    surface->paginated_mode = paginated_mode;
+}
+
+static cairo_bool_t
+_cairo_svg_surface_supports_fine_grained_fallbacks (void       *abstract_surface)
+{
+    cairo_svg_surface_t *surface = abstract_surface;
+    cairo_int_status_t status = CAIRO_INT_STATUS_UNSUPPORTED;
+
+    if (surface->document->svg_version >= CAIRO_SVG_VERSION_1_2) {
+       status =  _cairo_svg_surface_analyze_operator (surface,
+                                                      CAIRO_OPERATOR_SOURCE);
+    }
+
+    return status == CAIRO_INT_STATUS_SUCCESS;
+}
+
+static const cairo_paginated_surface_backend_t cairo_svg_surface_paginated_backend = {
+    NULL /*_cairo_svg_surface_start_page*/,
+    _cairo_svg_surface_set_paginated_mode,
+    NULL, /* _cairo_svg_surface_set_bounding_box */
+    NULL, /* _cairo_svg_surface_set_fallback_images_required */
+    _cairo_svg_surface_supports_fine_grained_fallbacks,
+
+};
diff --git a/src/cairo-svg.h b/src/cairo-svg.h
new file mode 100644 (file)
index 0000000..0f739fc
--- /dev/null
@@ -0,0 +1,82 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * cairo-svg.h
+ *
+ * Copyright © 2005 Emmanuel Pacaud <emmanuel.pacaud@univ-poitiers.fr>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ */
+
+#ifndef CAIRO_SVG_H
+#define CAIRO_SVG_H
+
+#include "cairo.h"
+
+#if CAIRO_HAS_SVG_SURFACE
+
+CAIRO_BEGIN_DECLS
+
+/**
+ * cairo_svg_version_t:
+ * @CAIRO_SVG_VERSION_1_1: The version 1.1 of the SVG specification.
+ * @CAIRO_SVG_VERSION_1_2: The version 1.2 of the SVG specification.
+ *
+ * #cairo_svg_version_t is used to describe the version number of the SVG
+ * specification that a generated SVG file will conform to.
+ */
+typedef enum _cairo_svg_version {
+    CAIRO_SVG_VERSION_1_1,
+    CAIRO_SVG_VERSION_1_2
+} cairo_svg_version_t;
+
+cairo_public cairo_surface_t *
+cairo_svg_surface_create (const char   *filename,
+                         double        width_in_points,
+                         double        height_in_points);
+
+cairo_public cairo_surface_t *
+cairo_svg_surface_create_for_stream (cairo_write_func_t        write_func,
+                                    void              *closure,
+                                    double             width_in_points,
+                                    double             height_in_points);
+
+cairo_public void
+cairo_svg_surface_restrict_to_version (cairo_surface_t                 *surface,
+                                      cairo_svg_version_t       version);
+
+cairo_public void
+cairo_svg_get_versions (cairo_svg_version_t const      **versions,
+                        int                             *num_versions);
+
+cairo_public const char *
+cairo_svg_version_to_string (cairo_svg_version_t version);
+
+CAIRO_END_DECLS
+
+#else  /* CAIRO_HAS_SVG_SURFACE */
+# error Cairo was not compiled with support for the svg backend
+#endif /* CAIRO_HAS_SVG_SURFACE */
+
+#endif /* CAIRO_SVG_H */
diff --git a/src/cairo-system.c b/src/cairo-system.c
new file mode 100644 (file)
index 0000000..1ff4d07
--- /dev/null
@@ -0,0 +1,97 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* Cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ *     Owen Taylor <otaylor@redhat.com>
+ *     Stuart Parmenter <stuart@mozilla.com>
+ *     Vladimir Vukicevic <vladimir@pobox.com>
+ */
+
+/* This file should include code that is system-specific, not
+ * feature-specific.  For example, the DLL initialization/finalization
+ * code on Win32 or OS/2 must live here (not in cairo-whatever-surface.c).
+ * Same about possible ELF-specific code.
+ *
+ * And no other function should live here.
+ */
+
+
+#include "cairoint.h"
+
+
+
+#if CAIRO_MUTEX_IMPL_WIN32
+#if !CAIRO_WIN32_STATIC_BUILD
+
+#define WIN32_LEAN_AND_MEAN
+/* We require Windows 2000 features such as ETO_PDY */
+#if !defined(WINVER) || (WINVER < 0x0500)
+# define WINVER 0x0500
+#endif
+#if !defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x0500)
+# define _WIN32_WINNT 0x0500
+#endif
+
+#include "cairo-clip-private.h"
+#include "cairo-paginated-private.h"
+#include "cairo-win32-private.h"
+#include "cairo-scaled-font-subsets-private.h"
+
+#include <windows.h>
+
+/* declare to avoid "no previous prototype for 'DllMain'" warning */
+BOOL WINAPI
+DllMain (HINSTANCE hinstDLL,
+         DWORD     fdwReason,
+         LPVOID    lpvReserved);
+
+BOOL WINAPI
+DllMain (HINSTANCE hinstDLL,
+         DWORD     fdwReason,
+         LPVOID    lpvReserved)
+{
+    switch (fdwReason) {
+        case DLL_PROCESS_ATTACH:
+            CAIRO_MUTEX_INITIALIZE ();
+            break;
+
+        case DLL_PROCESS_DETACH:
+            CAIRO_MUTEX_FINALIZE ();
+            break;
+    }
+
+    return TRUE;
+}
+
+#endif
+#endif
+
diff --git a/src/cairo-tee-surface-private.h b/src/cairo-tee-surface-private.h
new file mode 100644 (file)
index 0000000..a83cfc9
--- /dev/null
@@ -0,0 +1,47 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Chris Wilson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#ifndef CAIRO_TEE_SURFACE_PRIVATE_H
+#define CAIRO_TEE_SURFACE_PRIVATE_H
+
+#include "cairoint.h"
+
+cairo_private cairo_surface_t *
+_cairo_tee_surface_find_match (void *abstract_surface,
+                              const cairo_surface_backend_t *backend,
+                              cairo_content_t content);
+
+#endif /* CAIRO_TEE_SURFACE_PRIVATE_H */
diff --git a/src/cairo-tee-surface.c b/src/cairo-tee-surface.c
new file mode 100644 (file)
index 0000000..4930a0b
--- /dev/null
@@ -0,0 +1,593 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2005 Red Hat, Inc
+ * Copyright © 2009 Chris Wilson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ *     Carl Worth <cworth@cworth.org>
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+/* This surface supports redirecting all its input to multiple surfaces.
+ */
+
+#include "cairoint.h"
+
+#include "cairo-tee.h"
+
+#include "cairo-default-context-private.h"
+#include "cairo-error-private.h"
+#include "cairo-tee-surface-private.h"
+#include "cairo-recording-surface-private.h"
+#include "cairo-surface-wrapper-private.h"
+#include "cairo-array-private.h"
+#include "cairo-image-surface-private.h"
+
+typedef struct _cairo_tee_surface {
+    cairo_surface_t base;
+
+    cairo_surface_wrapper_t master;
+    cairo_array_t slaves;
+} cairo_tee_surface_t;
+
+slim_hidden_proto (cairo_tee_surface_create);
+slim_hidden_proto (cairo_tee_surface_add);
+
+static cairo_surface_t *
+_cairo_tee_surface_create_similar (void                        *abstract_surface,
+                                  cairo_content_t       content,
+                                  int                   width,
+                                  int                   height)
+{
+
+    cairo_tee_surface_t *other = abstract_surface;
+    cairo_surface_t *similar;
+    cairo_surface_t *surface;
+    cairo_surface_wrapper_t *slaves;
+    int n, num_slaves;
+
+    similar = _cairo_surface_wrapper_create_similar (&other->master,
+                                                    content, width, height);
+    surface = cairo_tee_surface_create (similar);
+    cairo_surface_destroy (similar);
+    if (unlikely (surface->status))
+       return surface;
+
+    num_slaves = _cairo_array_num_elements (&other->slaves);
+    slaves = _cairo_array_index (&other->slaves, 0);
+    for (n = 0; n < num_slaves; n++) {
+
+       similar = _cairo_surface_wrapper_create_similar (&slaves[n],
+                                                        content,
+                                                        width, height);
+       cairo_tee_surface_add (surface, similar);
+       cairo_surface_destroy (similar);
+    }
+
+    if (unlikely (surface->status)) {
+       cairo_status_t status = surface->status;
+       cairo_surface_destroy (surface);
+       surface = _cairo_surface_create_in_error (status);
+    }
+
+    return surface;
+}
+
+static cairo_status_t
+_cairo_tee_surface_finish (void *abstract_surface)
+{
+    cairo_tee_surface_t *surface = abstract_surface;
+    cairo_surface_wrapper_t *slaves;
+    int n, num_slaves;
+
+    _cairo_surface_wrapper_fini (&surface->master);
+
+    num_slaves = _cairo_array_num_elements (&surface->slaves);
+    slaves = _cairo_array_index (&surface->slaves, 0);
+    for (n = 0; n < num_slaves; n++)
+       _cairo_surface_wrapper_fini (&slaves[n]);
+
+    _cairo_array_fini (&surface->slaves);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_tee_surface_acquire_source_image (void       *abstract_surface,
+                                        cairo_image_surface_t **image_out,
+                                        void            **image_extra)
+{
+    cairo_tee_surface_t *surface = abstract_surface;
+    cairo_surface_wrapper_t *slaves;
+    int num_slaves, n;
+
+    /* we prefer to use a real image surface if available */
+    if (_cairo_surface_is_image (surface->master.target)) {
+       return _cairo_surface_wrapper_acquire_source_image (&surface->master,
+                                                           image_out, image_extra);
+    }
+
+    num_slaves = _cairo_array_num_elements (&surface->slaves);
+    slaves = _cairo_array_index (&surface->slaves, 0);
+    for (n = 0; n < num_slaves; n++) {
+       if (_cairo_surface_is_image (slaves[n].target)) {
+           return _cairo_surface_wrapper_acquire_source_image (&slaves[n],
+                                                               image_out,
+                                                               image_extra);
+       }
+    }
+
+    return _cairo_surface_wrapper_acquire_source_image (&surface->master,
+                                                       image_out, image_extra);
+}
+
+static void
+_cairo_tee_surface_release_source_image (void       *abstract_surface,
+                                        cairo_image_surface_t  *image,
+                                        void             *image_extra)
+{
+    cairo_tee_surface_t *surface = abstract_surface;
+
+    _cairo_surface_wrapper_release_source_image (&surface->master,
+                                                image, image_extra);
+}
+
+static cairo_surface_t *
+_cairo_tee_surface_snapshot (void *abstract_surface)
+{
+    cairo_tee_surface_t *surface = abstract_surface;
+    cairo_surface_wrapper_t *slaves;
+    int num_slaves, n;
+
+    /* we prefer to use a recording surface for our snapshots */
+    if (_cairo_surface_is_recording (surface->master.target))
+       return _cairo_surface_wrapper_snapshot (&surface->master);
+
+    num_slaves = _cairo_array_num_elements (&surface->slaves);
+    slaves = _cairo_array_index (&surface->slaves, 0);
+    for (n = 0; n < num_slaves; n++) {
+       if (_cairo_surface_is_recording (slaves[n].target))
+           return _cairo_surface_wrapper_snapshot (&slaves[n]);
+    }
+
+    return _cairo_surface_wrapper_snapshot (&surface->master);
+}
+
+static cairo_bool_t
+_cairo_tee_surface_get_extents (void                   *abstract_surface,
+                               cairo_rectangle_int_t   *rectangle)
+{
+    cairo_tee_surface_t *surface = abstract_surface;
+
+    return _cairo_surface_wrapper_get_extents (&surface->master, rectangle);
+}
+
+static void
+_cairo_tee_surface_get_font_options (void                  *abstract_surface,
+                                    cairo_font_options_t  *options)
+{
+    cairo_tee_surface_t *surface = abstract_surface;
+
+    _cairo_surface_wrapper_get_font_options (&surface->master, options);
+}
+
+static cairo_int_status_t
+_cairo_tee_surface_paint (void                 *abstract_surface,
+                         cairo_operator_t       op,
+                         const cairo_pattern_t *source,
+                         const cairo_clip_t    *clip)
+{
+    cairo_tee_surface_t *surface = abstract_surface;
+    cairo_surface_wrapper_t *slaves;
+    int n, num_slaves;
+    cairo_int_status_t status;
+
+    num_slaves = _cairo_array_num_elements (&surface->slaves);
+    slaves = _cairo_array_index (&surface->slaves, 0);
+    for (n = 0; n < num_slaves; n++) {
+       status = _cairo_surface_wrapper_paint (&slaves[n], op, source, clip);
+       if (unlikely (status))
+           return status;
+    }
+
+    return _cairo_surface_wrapper_paint (&surface->master, op, source, clip);
+}
+
+static cairo_int_status_t
+_cairo_tee_surface_mask (void                  *abstract_surface,
+                        cairo_operator_t        op,
+                        const cairo_pattern_t  *source,
+                        const cairo_pattern_t  *mask,
+                        const cairo_clip_t     *clip)
+{
+    cairo_tee_surface_t *surface = abstract_surface;
+    cairo_surface_wrapper_t *slaves;
+    cairo_int_status_t status;
+    int n, num_slaves;
+
+    num_slaves = _cairo_array_num_elements (&surface->slaves);
+    slaves = _cairo_array_index (&surface->slaves, 0);
+    for (n = 0; n < num_slaves; n++) {
+       status = _cairo_surface_wrapper_mask (&slaves[n],
+                                             op, source, mask, clip);
+       if (unlikely (status))
+           return status;
+    }
+
+    return _cairo_surface_wrapper_mask (&surface->master,
+                                       op, source, mask, clip);
+}
+
+static cairo_int_status_t
+_cairo_tee_surface_stroke (void                                *abstract_surface,
+                          cairo_operator_t              op,
+                          const cairo_pattern_t        *source,
+                          const cairo_path_fixed_t     *path,
+                          const cairo_stroke_style_t   *style,
+                          const cairo_matrix_t         *ctm,
+                          const cairo_matrix_t         *ctm_inverse,
+                          double                        tolerance,
+                          cairo_antialias_t             antialias,
+                          const cairo_clip_t           *clip)
+{
+    cairo_tee_surface_t *surface = abstract_surface;
+    cairo_surface_wrapper_t *slaves;
+    cairo_int_status_t status;
+    int n, num_slaves;
+
+    num_slaves = _cairo_array_num_elements (&surface->slaves);
+    slaves = _cairo_array_index (&surface->slaves, 0);
+    for (n = 0; n < num_slaves; n++) {
+       status = _cairo_surface_wrapper_stroke (&slaves[n],
+                                               op, source,
+                                               path, style,
+                                               ctm, ctm_inverse,
+                                               tolerance, antialias,
+                                               clip);
+       if (unlikely (status))
+           return status;
+    }
+
+    return _cairo_surface_wrapper_stroke (&surface->master,
+                                         op, source,
+                                         path, style,
+                                         ctm, ctm_inverse,
+                                         tolerance, antialias,
+                                         clip);
+}
+
+static cairo_int_status_t
+_cairo_tee_surface_fill (void                          *abstract_surface,
+                        cairo_operator_t                op,
+                        const cairo_pattern_t          *source,
+                        const cairo_path_fixed_t       *path,
+                        cairo_fill_rule_t               fill_rule,
+                        double                          tolerance,
+                        cairo_antialias_t               antialias,
+                        const cairo_clip_t             *clip)
+{
+    cairo_tee_surface_t *surface = abstract_surface;
+    cairo_surface_wrapper_t *slaves;
+    cairo_int_status_t status;
+    int n, num_slaves;
+
+    num_slaves = _cairo_array_num_elements (&surface->slaves);
+    slaves = _cairo_array_index (&surface->slaves, 0);
+    for (n = 0; n < num_slaves; n++) {
+       status = _cairo_surface_wrapper_fill (&slaves[n],
+                                             op, source,
+                                             path, fill_rule,
+                                             tolerance, antialias,
+                                             clip);
+       if (unlikely (status))
+           return status;
+    }
+
+    return _cairo_surface_wrapper_fill (&surface->master,
+                                       op, source,
+                                       path, fill_rule,
+                                       tolerance, antialias,
+                                       clip);
+}
+
+static cairo_bool_t
+_cairo_tee_surface_has_show_text_glyphs (void *abstract_surface)
+{
+    return TRUE;
+}
+
+static cairo_int_status_t
+_cairo_tee_surface_show_text_glyphs (void                  *abstract_surface,
+                                    cairo_operator_t        op,
+                                    const cairo_pattern_t  *source,
+                                    const char             *utf8,
+                                    int                     utf8_len,
+                                    cairo_glyph_t          *glyphs,
+                                    int                     num_glyphs,
+                                    const cairo_text_cluster_t *clusters,
+                                    int                     num_clusters,
+                                    cairo_text_cluster_flags_t cluster_flags,
+                                    cairo_scaled_font_t    *scaled_font,
+                                    const cairo_clip_t     *clip)
+{
+    cairo_tee_surface_t *surface = abstract_surface;
+    cairo_surface_wrapper_t *slaves;
+    cairo_int_status_t status;
+    int n, num_slaves;
+    cairo_glyph_t *glyphs_copy;
+
+    /* XXX: This copying is ugly. */
+    glyphs_copy = _cairo_malloc_ab (num_glyphs, sizeof (cairo_glyph_t));
+    if (unlikely (glyphs_copy == NULL))
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    num_slaves = _cairo_array_num_elements (&surface->slaves);
+    slaves = _cairo_array_index (&surface->slaves, 0);
+    for (n = 0; n < num_slaves; n++) {
+       memcpy (glyphs_copy, glyphs, sizeof (cairo_glyph_t) * num_glyphs);
+       status = _cairo_surface_wrapper_show_text_glyphs (&slaves[n], op,
+                                                         source,
+                                                         utf8, utf8_len,
+                                                         glyphs_copy, num_glyphs,
+                                                         clusters, num_clusters,
+                                                         cluster_flags,
+                                                         scaled_font,
+                                                         clip);
+       if (unlikely (status))
+           goto CLEANUP;
+    }
+
+    memcpy (glyphs_copy, glyphs, sizeof (cairo_glyph_t) * num_glyphs);
+    status = _cairo_surface_wrapper_show_text_glyphs (&surface->master, op,
+                                                     source,
+                                                     utf8, utf8_len,
+                                                     glyphs_copy, num_glyphs,
+                                                     clusters, num_clusters,
+                                                     cluster_flags,
+                                                     scaled_font,
+                                                     clip);
+CLEANUP:
+    free (glyphs_copy);
+    return status;
+}
+
+static const cairo_surface_backend_t cairo_tee_surface_backend = {
+    CAIRO_SURFACE_TYPE_TEE,
+    _cairo_tee_surface_finish,
+
+    _cairo_default_context_create, /* XXX */
+
+    _cairo_tee_surface_create_similar,
+    NULL, /* create similar image */
+    NULL, /* map to image */
+    NULL, /* unmap image */
+
+    _cairo_tee_surface_acquire_source_image,
+    _cairo_tee_surface_release_source_image,
+    _cairo_tee_surface_snapshot,
+    NULL, /* copy_page */
+    NULL, /* show_page */
+    _cairo_tee_surface_get_extents,
+    _cairo_tee_surface_get_font_options,
+    NULL, /* flush */
+    NULL, /* mark_dirty_rectangle */
+
+    _cairo_tee_surface_paint,
+    _cairo_tee_surface_mask,
+    _cairo_tee_surface_stroke,
+    _cairo_tee_surface_fill,
+    NULL, /* fill_stroke */
+
+    NULL, /* show_glyphs */
+
+    _cairo_tee_surface_has_show_text_glyphs,
+    _cairo_tee_surface_show_text_glyphs
+};
+
+cairo_surface_t *
+cairo_tee_surface_create (cairo_surface_t *master)
+{
+    cairo_tee_surface_t *surface;
+
+    if (unlikely (master->status))
+       return _cairo_surface_create_in_error (master->status);
+
+    surface = malloc (sizeof (cairo_tee_surface_t));
+    if (unlikely (surface == NULL))
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+    _cairo_surface_init (&surface->base,
+                        &cairo_tee_surface_backend,
+                        master->device,
+                        master->content);
+
+    _cairo_surface_wrapper_init (&surface->master, master);
+
+    _cairo_array_init (&surface->slaves, sizeof (cairo_surface_wrapper_t));
+
+    return &surface->base;
+}
+slim_hidden_def (cairo_tee_surface_create);
+
+void
+cairo_tee_surface_add (cairo_surface_t *abstract_surface,
+                      cairo_surface_t *target)
+{
+    cairo_tee_surface_t *surface;
+    cairo_surface_wrapper_t slave;
+    cairo_status_t status;
+
+    if (unlikely (abstract_surface->status))
+       return;
+    if (unlikely (abstract_surface->finished)) {
+       status = _cairo_surface_set_error (abstract_surface,
+                                          _cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
+       return;
+    }
+
+    if (abstract_surface->backend != &cairo_tee_surface_backend) {
+       status = _cairo_surface_set_error (abstract_surface,
+                                          _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH));
+       return;
+    }
+
+    if (unlikely (target->status)) {
+       status = _cairo_surface_set_error (abstract_surface, target->status);
+       return;
+    }
+
+    surface = (cairo_tee_surface_t *) abstract_surface;
+
+    _cairo_surface_wrapper_init (&slave, target);
+    status = _cairo_array_append (&surface->slaves, &slave);
+    if (unlikely (status)) {
+       _cairo_surface_wrapper_fini (&slave);
+       status = _cairo_surface_set_error (&surface->base, status);
+    }
+}
+slim_hidden_def (cairo_tee_surface_add);
+
+void
+cairo_tee_surface_remove (cairo_surface_t *abstract_surface,
+                         cairo_surface_t *target)
+{
+    cairo_tee_surface_t *surface;
+    cairo_surface_wrapper_t *slaves;
+    int n, num_slaves;
+
+    if (unlikely (abstract_surface->status))
+       return;
+    if (unlikely (abstract_surface->finished)) {
+       _cairo_surface_set_error (abstract_surface,
+                                 _cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
+       return;
+    }
+
+    if (abstract_surface->backend != &cairo_tee_surface_backend) {
+       _cairo_surface_set_error (abstract_surface,
+                                 _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH));
+       return;
+    }
+
+    surface = (cairo_tee_surface_t *) abstract_surface;
+    if (target == surface->master.target) {
+       _cairo_surface_set_error (abstract_surface,
+                                 _cairo_error (CAIRO_STATUS_INVALID_INDEX));
+       return;
+    }
+
+    num_slaves = _cairo_array_num_elements (&surface->slaves);
+    slaves = _cairo_array_index (&surface->slaves, 0);
+    for (n = 0; n < num_slaves; n++) {
+       if (slaves[n].target == target)
+           break;
+    }
+
+    if (n == num_slaves) {
+       _cairo_surface_set_error (abstract_surface,
+                                 _cairo_error (CAIRO_STATUS_INVALID_INDEX));
+       return;
+    }
+
+    _cairo_surface_wrapper_fini (&slaves[n]);
+    for (n++; n < num_slaves; n++)
+       slaves[n-1] = slaves[n];
+    surface->slaves.num_elements--; /* XXX: cairo_array_remove()? */
+}
+
+cairo_surface_t *
+cairo_tee_surface_index (cairo_surface_t *abstract_surface,
+                        unsigned int index)
+{
+    cairo_tee_surface_t *surface;
+
+    if (unlikely (abstract_surface->status))
+       return _cairo_surface_create_in_error (abstract_surface->status);
+    if (unlikely (abstract_surface->finished))
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
+
+    if (abstract_surface->backend != &cairo_tee_surface_backend)
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH));
+
+    surface = (cairo_tee_surface_t *) abstract_surface;
+    if (index == 0) {
+       return surface->master.target;
+    } else {
+       cairo_surface_wrapper_t *slave;
+
+       index--;
+
+       if (index >= _cairo_array_num_elements (&surface->slaves))
+           return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_INDEX));
+
+       slave = _cairo_array_index (&surface->slaves, index);
+       return slave->target;
+    }
+}
+
+cairo_surface_t *
+_cairo_tee_surface_find_match (void *abstract_surface,
+                              const cairo_surface_backend_t *backend,
+                              cairo_content_t content)
+{
+    cairo_tee_surface_t *surface = abstract_surface;
+    cairo_surface_wrapper_t *slaves;
+    int num_slaves, n;
+
+    /* exact match first */
+    if (surface->master.target->backend == backend &&
+       surface->master.target->content == content)
+    {
+       return surface->master.target;
+    }
+
+    num_slaves = _cairo_array_num_elements (&surface->slaves);
+    slaves = _cairo_array_index (&surface->slaves, 0);
+    for (n = 0; n < num_slaves; n++) {
+       if (slaves[n].target->backend == backend &&
+           slaves[n].target->content == content)
+       {
+           return slaves[n].target;
+       }
+    }
+
+    /* matching backend? */
+    if (surface->master.target->backend == backend)
+       return surface->master.target;
+
+    num_slaves = _cairo_array_num_elements (&surface->slaves);
+    slaves = _cairo_array_index (&surface->slaves, 0);
+    for (n = 0; n < num_slaves; n++) {
+       if (slaves[n].target->backend == backend)
+           return slaves[n].target;
+    }
+
+    return NULL;
+}
diff --git a/src/cairo-tee.h b/src/cairo-tee.h
new file mode 100644 (file)
index 0000000..9125a3a
--- /dev/null
@@ -0,0 +1,66 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Chris Wilson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Chris Wilson
+ *
+ * Contributor(s):
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#ifndef CAIRO_TEE_H
+#define CAIRO_TEE_H
+
+#include "cairo.h"
+
+#if CAIRO_HAS_TEE_SURFACE
+
+CAIRO_BEGIN_DECLS
+
+cairo_public cairo_surface_t *
+cairo_tee_surface_create (cairo_surface_t *master);
+
+cairo_public void
+cairo_tee_surface_add (cairo_surface_t *surface,
+                      cairo_surface_t *target);
+
+cairo_public void
+cairo_tee_surface_remove (cairo_surface_t *surface,
+                         cairo_surface_t *target);
+
+cairo_public cairo_surface_t *
+cairo_tee_surface_index (cairo_surface_t *surface,
+                        unsigned int index);
+
+CAIRO_END_DECLS
+
+#else  /*CAIRO_HAS_TEE_SURFACE*/
+# error Cairo was not compiled with support for the TEE backend
+#endif /*CAIRO_HAS_TEE_SURFACE*/
+
+#endif /*CAIRO_TEE_H*/
diff --git a/src/cairo-time-private.h b/src/cairo-time-private.h
new file mode 100644 (file)
index 0000000..06dc912
--- /dev/null
@@ -0,0 +1,94 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright (C) 2011 Andrea Canciani
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of the
+ * copyright holders not be used in advertising or publicity
+ * pertaining to distribution of the software without specific,
+ * written prior permission. The copyright holders make no
+ * representations about the suitability of this software for any
+ * purpose.  It is provided "as is" without express or implied
+ * warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ * SOFTWARE.
+ *
+ * Authors: Andrea Canciani <ranma42@gmail.com>
+ *
+ */
+
+#ifndef CAIRO_TIME_PRIVATE_H
+#define CAIRO_TIME_PRIVATE_H
+
+#include "cairo-compiler-private.h"
+#include "cairo-wideint-private.h"
+
+/* Make the base type signed for easier arithmetic */
+typedef cairo_int64_t cairo_time_t;
+
+#define _cairo_time_add _cairo_int64_add
+#define _cairo_time_sub _cairo_int64_sub
+#define _cairo_time_gt  _cairo_int64_gt
+#define _cairo_time_lt  _cairo_int64_lt
+
+#define _cairo_time_to_double   _cairo_int64_to_double
+#define _cairo_time_from_double _cairo_double_to_int64
+
+cairo_private int
+_cairo_time_cmp (const void *a,
+                const void *b);
+
+cairo_private double
+_cairo_time_to_s (cairo_time_t t);
+
+cairo_private cairo_time_t
+_cairo_time_from_s (double t);
+
+cairo_private cairo_time_t
+_cairo_time_get (void);
+
+static cairo_always_inline cairo_time_t
+_cairo_time_get_delta (cairo_time_t t)
+{
+    cairo_time_t now;
+
+    now = _cairo_time_get ();
+
+    return _cairo_time_sub (now, t);
+}
+
+static cairo_always_inline double
+_cairo_time_to_ns (cairo_time_t t)
+{
+    return 1.e9 * _cairo_time_to_s (t);
+}
+
+static cairo_always_inline cairo_time_t
+_cairo_time_max (cairo_time_t a, cairo_time_t b)
+{
+    if (_cairo_int64_gt (a, b))
+       return a;
+    else
+       return b;
+}
+
+static cairo_always_inline cairo_time_t
+_cairo_time_min (cairo_time_t a, cairo_time_t b)
+{
+    if (_cairo_int64_lt (a, b))
+       return a;
+    else
+       return b;
+}
+
+#endif
diff --git a/src/cairo-time.c b/src/cairo-time.c
new file mode 100644 (file)
index 0000000..9a594e8
--- /dev/null
@@ -0,0 +1,206 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright (c) 2007 Netlabs
+ * Copyright (c) 2006 Mozilla Corporation
+ * Copyright (c) 2006 Red Hat, Inc.
+ * Copyright (c) 2011 Andrea Canciani
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * the authors not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. The authors make no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE AUTHORS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Authors: Peter Weilbacher <mozilla@weilbacher.org>
+ *         Vladimir Vukicevic <vladimir@pobox.com>
+ *         Carl Worth <cworth@cworth.org>
+ *          Andrea Canciani <ranma42@gmail.com>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-time-private.h"
+
+#if HAVE_CLOCK_GETTIME
+#if defined(CLOCK_MONOTONIC_RAW)
+#define CAIRO_CLOCK CLOCK_MONOTONIC_RAW
+#elif defined(CLOCK_MONOTONIC)
+#define CAIRO_CLOCK CLOCK_MONOTONIC
+#endif
+#endif
+
+#if defined(__APPLE__)
+#include <mach/mach_time.h>
+
+static cairo_always_inline double
+_cairo_time_1s (void)
+{
+    mach_timebase_info_data_t freq;
+
+    mach_timebase_info (&freq);
+
+    return 1000000000. * freq.denom / freq.numer;
+}
+
+cairo_time_t
+_cairo_time_get (void)
+{
+    return mach_absolute_time ();
+}
+
+#elif defined(__OS2__)
+#define INCL_BASE
+#include <os2.h>
+
+static cairo_always_inline double
+_cairo_time_1s (void)
+{
+    ULONG freq;
+
+    DosTmrQueryFreq (&freq);
+
+    return freq;
+}
+
+cairo_time_t
+_cairo_time_get (void)
+{
+    QWORD t;
+    cairo_int64_t r;
+
+    DosTmrQueryTime (&t);
+
+    r = _cairo_int64_lsl (_cairo_int32_to_int64 (t.ulHi), 32);
+    r = _cairo_int64_add (r, _cairo_int32_to_int64 (t.ulLo));
+
+    return r;
+}
+
+#elif _WIN32
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+static cairo_always_inline double
+_cairo_time_1s (void)
+{
+    LARGE_INTEGER freq;
+
+    QueryPerformanceFrequency (&freq);
+
+    return freq.QuadPart;
+}
+
+cairo_time_t
+_cairo_time_get (void)
+{
+    LARGE_INTEGER t;
+
+    QueryPerformanceCounter (&t);
+
+    return t.QuadPart;
+}
+
+#elif defined(CAIRO_CLOCK)
+#include <time.h>
+
+static cairo_always_inline double
+_cairo_time_1s (void)
+{
+    return 1000000000;
+}
+
+cairo_time_t
+_cairo_time_get (void)
+{
+    struct timespec t;
+    cairo_time_t r;
+
+    clock_gettime (CAIRO_CLOCK, &t);
+
+    r = _cairo_double_to_int64 (_cairo_time_1s ());
+    r = _cairo_int64_mul (r, _cairo_int32_to_int64 (t.tv_sec));
+    r = _cairo_int64_add (r, _cairo_int32_to_int64 (t.tv_nsec));
+
+    return r;
+}
+
+#else
+#include <sys/time.h>
+
+static cairo_always_inline double
+_cairo_time_1s (void)
+{
+    return 1000000;
+}
+
+cairo_time_t
+_cairo_time_get (void)
+{
+    struct timeval t;
+    cairo_time_t r;
+
+    gettimeofday (&t, NULL);
+
+    r = _cairo_double_to_int64 (_cairo_time_1s ());
+    r = _cairo_int64_mul (r, _cairo_int32_to_int64 (t.tv_sec));
+    r = _cairo_int64_add (r, _cairo_int32_to_int64 (t.tv_usec));
+
+    return r;
+}
+
+#endif
+
+int
+_cairo_time_cmp (const void *a,
+                const void *b)
+{
+    const cairo_time_t *ta = a, *tb = b;
+    return _cairo_int64_cmp (*ta, *tb);
+}
+
+static double
+_cairo_time_ticks_per_sec (void)
+{
+    static double ticks = 0;
+
+    if (unlikely (ticks == 0))
+       ticks = _cairo_time_1s ();
+
+    return ticks;
+}
+
+static double
+_cairo_time_s_per_tick (void)
+{
+    static double s = 0;
+
+    if (unlikely (s == 0))
+       s = 1. / _cairo_time_ticks_per_sec ();
+
+    return s;
+}
+
+double
+_cairo_time_to_s (cairo_time_t t)
+{
+    return _cairo_int64_to_double (t) * _cairo_time_s_per_tick ();
+}
+
+cairo_time_t
+_cairo_time_from_s (double t)
+{
+    return _cairo_double_to_int64 (t * _cairo_time_ticks_per_sec ());
+}
diff --git a/src/cairo-tor-scan-converter.c b/src/cairo-tor-scan-converter.c
new file mode 100644 (file)
index 0000000..d6ea778
--- /dev/null
@@ -0,0 +1,1853 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* glitter-paths - polygon scan converter
+ *
+ * Copyright (c) 2008  M Joonas Pihlaja
+ * Copyright (c) 2007  David Turner
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+/* This is the Glitter paths scan converter incorporated into cairo.
+ * The source is from commit 734c53237a867a773640bd5b64816249fa1730f8
+ * of
+ *
+ *   http://gitweb.freedesktop.org/?p=users/joonas/glitter-paths
+ */
+/* Glitter-paths is a stand alone polygon rasteriser derived from
+ * David Turner's reimplementation of Tor Anderssons's 15x17
+ * supersampling rasteriser from the Apparition graphics library.  The
+ * main new feature here is cheaply choosing per-scan line between
+ * doing fully analytical coverage computation for an entire row at a
+ * time vs. using a supersampling approach.
+ *
+ * David Turner's code can be found at
+ *
+ *   http://david.freetype.org/rasterizer-shootout/raster-comparison-20070813.tar.bz2
+ *
+ * In particular this file incorporates large parts of ftgrays_tor10.h
+ * from raster-comparison-20070813.tar.bz2
+ */
+/* Overview
+ *
+ * A scan converter's basic purpose to take polygon edges and convert
+ * them into an RLE compressed A8 mask.  This one works in two phases:
+ * gathering edges and generating spans.
+ *
+ * 1) As the user feeds the scan converter edges they are vertically
+ * clipped and bucketted into a _polygon_ data structure.  The edges
+ * are also snapped from the user's coordinates to the subpixel grid
+ * coordinates used during scan conversion.
+ *
+ *     user
+ *      |
+ *      | edges
+ *      V
+ *    polygon buckets
+ *
+ * 2) Generating spans works by performing a vertical sweep of pixel
+ * rows from top to bottom and maintaining an _active_list_ of edges
+ * that intersect the row.  From the active list the fill rule
+ * determines which edges are the left and right edges of the start of
+ * each span, and their contribution is then accumulated into a pixel
+ * coverage list (_cell_list_) as coverage deltas.  Once the coverage
+ * deltas of all edges are known we can form spans of constant pixel
+ * coverage by summing the deltas during a traversal of the cell list.
+ * At the end of a pixel row the cell list is sent to a coverage
+ * blitter for rendering to some target surface.
+ *
+ * The pixel coverages are computed by either supersampling the row
+ * and box filtering a mono rasterisation, or by computing the exact
+ * coverages of edges in the active list.  The supersampling method is
+ * used whenever some edge starts or stops within the row or there are
+ * edge intersections in the row.
+ *
+ *   polygon bucket for       \
+ *   current pixel row        |
+ *      |                     |
+ *      | activate new edges  |  Repeat GRID_Y times if we
+ *      V                     \  are supersampling this row,
+ *   active list              /  or just once if we're computing
+ *      |                     |  analytical coverage.
+ *      | coverage deltas     |
+ *      V                     |
+ *   pixel coverage list     /
+ *      |
+ *      V
+ *   coverage blitter
+ */
+#include "cairoint.h"
+#include "cairo-spans-private.h"
+#include "cairo-error-private.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <setjmp.h>
+
+/*-------------------------------------------------------------------------
+ * cairo specific config
+ */
+#define I static
+
+/* Prefer cairo's status type. */
+#define GLITTER_HAVE_STATUS_T 1
+#define GLITTER_STATUS_SUCCESS CAIRO_STATUS_SUCCESS
+#define GLITTER_STATUS_NO_MEMORY CAIRO_STATUS_NO_MEMORY
+typedef cairo_status_t glitter_status_t;
+
+/* The input coordinate scale and the rasterisation grid scales. */
+#define GLITTER_INPUT_BITS CAIRO_FIXED_FRAC_BITS
+//#define GRID_X_BITS CAIRO_FIXED_FRAC_BITS
+//#define GRID_Y 15
+#define GRID_X_BITS 2
+#define GRID_Y_BITS 2
+
+/* Set glitter up to use a cairo span renderer to do the coverage
+ * blitting. */
+struct pool;
+struct cell_list;
+
+/*-------------------------------------------------------------------------
+ * glitter-paths.h
+ */
+
+/* "Input scaled" numbers are fixed precision reals with multiplier
+ * 2**GLITTER_INPUT_BITS.  Input coordinates are given to glitter as
+ * pixel scaled numbers.  These get converted to the internal grid
+ * scaled numbers as soon as possible. Internal overflow is possible
+ * if GRID_X/Y inside glitter-paths.c is larger than
+ * 1<<GLITTER_INPUT_BITS. */
+#ifndef GLITTER_INPUT_BITS
+#  define GLITTER_INPUT_BITS 8
+#endif
+#define GLITTER_INPUT_SCALE (1<<GLITTER_INPUT_BITS)
+typedef int glitter_input_scaled_t;
+
+#if !GLITTER_HAVE_STATUS_T
+typedef enum {
+    GLITTER_STATUS_SUCCESS = 0,
+    GLITTER_STATUS_NO_MEMORY
+} glitter_status_t;
+#endif
+
+#ifndef I
+# define I /*static*/
+#endif
+
+/* Opaque type for scan converting. */
+typedef struct glitter_scan_converter glitter_scan_converter_t;
+
+/* Reset a scan converter to accept polygon edges and set the clip box
+ * in pixels.  Allocates O(ymax-ymin) bytes of memory. The clip box
+ * is set to integer pixel coordinates xmin <= x < xmax, ymin <= y <
+ * ymax. */
+I glitter_status_t
+glitter_scan_converter_reset(
+    glitter_scan_converter_t *converter,
+    int xmin, int ymin,
+    int xmax, int ymax);
+
+/* Render the polygon in the scan converter to the given A8 format
+ * image raster.  Only the pixels accessible as pixels[y*stride+x] for
+ * x,y inside the clip box are written to, where xmin <= x < xmax,
+ * ymin <= y < ymax.  The image is assumed to be clear on input.
+ *
+ * If nonzero_fill is true then the interior of the polygon is
+ * computed with the non-zero fill rule.  Otherwise the even-odd fill
+ * rule is used.
+ *
+ * The scan converter must be reset or destroyed after this call. */
+
+/*-------------------------------------------------------------------------
+ * glitter-paths.c: Implementation internal types
+ */
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+
+/* All polygon coordinates are snapped onto a subsample grid. "Grid
+ * scaled" numbers are fixed precision reals with multiplier GRID_X or
+ * GRID_Y. */
+typedef int grid_scaled_t;
+typedef int grid_scaled_x_t;
+typedef int grid_scaled_y_t;
+
+/* Default x/y scale factors.
+ *  You can either define GRID_X/Y_BITS to get a power-of-two scale
+ *  or define GRID_X/Y separately. */
+#if !defined(GRID_X) && !defined(GRID_X_BITS)
+#  define GRID_X_BITS 8
+#endif
+#if !defined(GRID_Y) && !defined(GRID_Y_BITS)
+#  define GRID_Y 15
+#endif
+
+/* Use GRID_X/Y_BITS to define GRID_X/Y if they're available. */
+#ifdef GRID_X_BITS
+#  define GRID_X (1 << GRID_X_BITS)
+#endif
+#ifdef GRID_Y_BITS
+#  define GRID_Y (1 << GRID_Y_BITS)
+#endif
+
+/* The GRID_X_TO_INT_FRAC macro splits a grid scaled coordinate into
+ * integer and fractional parts. The integer part is floored. */
+#if defined(GRID_X_TO_INT_FRAC)
+  /* do nothing */
+#elif defined(GRID_X_BITS)
+#  define GRID_X_TO_INT_FRAC(x, i, f) \
+       _GRID_TO_INT_FRAC_shift(x, i, f, GRID_X_BITS)
+#else
+#  define GRID_X_TO_INT_FRAC(x, i, f) \
+       _GRID_TO_INT_FRAC_general(x, i, f, GRID_X)
+#endif
+
+#define _GRID_TO_INT_FRAC_general(t, i, f, m) do {     \
+    (i) = (t) / (m);                                   \
+    (f) = (t) % (m);                                   \
+    if ((f) < 0) {                                     \
+       --(i);                                          \
+       (f) += (m);                                     \
+    }                                                  \
+} while (0)
+
+#define _GRID_TO_INT_FRAC_shift(t, i, f, b) do {       \
+    (f) = (t) & ((1 << (b)) - 1);                      \
+    (i) = (t) >> (b);                                  \
+} while (0)
+
+/* A grid area is a real in [0,1] scaled by 2*GRID_X*GRID_Y.  We want
+ * to be able to represent exactly areas of subpixel trapezoids whose
+ * vertices are given in grid scaled coordinates.  The scale factor
+ * comes from needing to accurately represent the area 0.5*dx*dy of a
+ * triangle with base dx and height dy in grid scaled numbers. */
+#define GRID_XY (2*GRID_X*GRID_Y) /* Unit area on the grid. */
+
+/* GRID_AREA_TO_ALPHA(area): map [0,GRID_XY] to [0,255]. */
+#if GRID_XY == 510
+#  define GRID_AREA_TO_ALPHA(c)          (((c)+1) >> 1)
+#elif GRID_XY == 255
+#  define  GRID_AREA_TO_ALPHA(c)  (c)
+#elif GRID_XY == 64
+#  define  GRID_AREA_TO_ALPHA(c)  (((c) << 2) | -(((c) & 0x40) >> 6))
+#elif GRID_XY == 128
+#  define  GRID_AREA_TO_ALPHA(c)  ((((c) << 1) | -((c) >> 7)) & 255)
+#elif GRID_XY == 256
+#  define  GRID_AREA_TO_ALPHA(c)  (((c) | -((c) >> 8)) & 255)
+#elif GRID_XY == 15
+#  define  GRID_AREA_TO_ALPHA(c)  (((c) << 4) + (c))
+#elif GRID_XY == 2*256*15
+#  define  GRID_AREA_TO_ALPHA(c)  (((c) + ((c)<<4) + 256) >> 9)
+#else
+#  define  GRID_AREA_TO_ALPHA(c)  (((c)*255 + GRID_XY/2) / GRID_XY)
+#endif
+
+#define UNROLL3(x) x x x
+
+struct quorem {
+    int32_t quo;
+    int32_t rem;
+};
+
+/* Header for a chunk of memory in a memory pool. */
+struct _pool_chunk {
+    /* # bytes used in this chunk. */
+    size_t size;
+
+    /* # bytes total in this chunk */
+    size_t capacity;
+
+    /* Pointer to the previous chunk or %NULL if this is the sentinel
+     * chunk in the pool header. */
+    struct _pool_chunk *prev_chunk;
+
+    /* Actual data starts here.         Well aligned for pointers. */
+};
+
+/* A memory pool.  This is supposed to be embedded on the stack or
+ * within some other structure.         It may optionally be followed by an
+ * embedded array from which requests are fulfilled until
+ * malloc needs to be called to allocate a first real chunk. */
+struct pool {
+    /* Chunk we're allocating from. */
+    struct _pool_chunk *current;
+
+    jmp_buf *jmp;
+
+    /* Free list of previously allocated chunks.  All have >= default
+     * capacity. */
+    struct _pool_chunk *first_free;
+
+    /* The default capacity of a chunk. */
+    size_t default_capacity;
+
+    /* Header for the sentinel chunk.  Directly following the pool
+     * struct should be some space for embedded elements from which
+     * the sentinel chunk allocates from. */
+    struct _pool_chunk sentinel[1];
+};
+
+/* A polygon edge. */
+struct edge {
+    /* Next in y-bucket or active list. */
+    struct edge *next, *prev;
+
+    /* Number of subsample rows remaining to scan convert of this
+     * edge. */
+    grid_scaled_y_t height_left;
+
+    /* Original sign of the edge: +1 for downwards, -1 for upwards
+     * edges.  */
+    int dir;
+    int vertical;
+
+    /* Current x coordinate while the edge is on the active
+     * list. Initialised to the x coordinate of the top of the
+     * edge. The quotient is in grid_scaled_x_t units and the
+     * remainder is mod dy in grid_scaled_y_t units.*/
+    struct quorem x;
+
+    /* Advance of the current x when moving down a subsample line. */
+    struct quorem dxdy;
+
+    /* Advance of the current x when moving down a full pixel
+     * row. Only initialised when the height of the edge is large
+     * enough that there's a chance the edge could be stepped by a
+     * full row's worth of subsample rows at a time. */
+    struct quorem dxdy_full;
+
+    /* The clipped y of the top of the edge. */
+    grid_scaled_y_t ytop;
+
+    /* y2-y1 after orienting the edge downwards.  */
+    grid_scaled_y_t dy;
+};
+
+#define EDGE_Y_BUCKET_INDEX(y, ymin) (((y) - (ymin))/GRID_Y)
+
+/* A collection of sorted and vertically clipped edges of the polygon.
+ * Edges are moved from the polygon to an active list while scan
+ * converting. */
+struct polygon {
+    /* The vertical clip extents. */
+    grid_scaled_y_t ymin, ymax;
+
+    /* Array of edges all starting in the same bucket. An edge is put
+     * into bucket EDGE_BUCKET_INDEX(edge->ytop, polygon->ymin) when
+     * it is added to the polygon. */
+    struct edge **y_buckets;
+    struct edge *y_buckets_embedded[64];
+
+    struct {
+       struct pool base[1];
+       struct edge embedded[32];
+    } edge_pool;
+};
+
+/* A cell records the effect on pixel coverage of polygon edges
+ * passing through a pixel.  It contains two accumulators of pixel
+ * coverage.
+ *
+ * Consider the effects of a polygon edge on the coverage of a pixel
+ * it intersects and that of the following one.  The coverage of the
+ * following pixel is the height of the edge multiplied by the width
+ * of the pixel, and the coverage of the pixel itself is the area of
+ * the trapezoid formed by the edge and the right side of the pixel.
+ *
+ * +-----------------------+-----------------------+
+ * |                       |                       |
+ * |                       |                       |
+ * |_______________________|_______________________|
+ * |   \...................|.......................|\
+ * |    \..................|.......................| |
+ * |     \.................|.......................| |
+ * |      \....covered.....|.......................| |
+ * |       \....area.......|.......................| } covered height
+ * |        \..............|.......................| |
+ * |uncovered\.............|.......................| |
+ * |  area    \............|.......................| |
+ * |___________\...........|.......................|/
+ * |                       |                       |
+ * |                       |                       |
+ * |                       |                       |
+ * +-----------------------+-----------------------+
+ *
+ * Since the coverage of the following pixel will always be a multiple
+ * of the width of the pixel, we can store the height of the covered
+ * area instead.  The coverage of the pixel itself is the total
+ * coverage minus the area of the uncovered area to the left of the
+ * edge.  As it's faster to compute the uncovered area we only store
+ * that and subtract it from the total coverage later when forming
+ * spans to blit.
+ *
+ * The heights and areas are signed, with left edges of the polygon
+ * having positive sign and right edges having negative sign.  When
+ * two edges intersect they swap their left/rightness so their
+ * contribution above and below the intersection point must be
+ * computed separately. */
+struct cell {
+    struct cell                *next;
+    int                         x;
+    int16_t             uncovered_area;
+    int16_t             covered_height;
+};
+
+/* A cell list represents the scan line sparsely as cells ordered by
+ * ascending x.  It is geared towards scanning the cells in order
+ * using an internal cursor. */
+struct cell_list {
+    /* Sentinel nodes */
+    struct cell head, tail;
+
+    /* Cursor state for iterating through the cell list. */
+    struct cell *cursor, *rewind;
+
+    /* Cells in the cell list are owned by the cell list and are
+     * allocated from this pool.  */
+    struct {
+       struct pool base[1];
+       struct cell embedded[32];
+    } cell_pool;
+};
+
+struct cell_pair {
+    struct cell *cell1;
+    struct cell *cell2;
+};
+
+/* The active list contains edges in the current scan line ordered by
+ * the x-coordinate of the intercept of the edge and the scan line. */
+struct active_list {
+    /* Leftmost edge on the current scan line. */
+    struct edge head, tail;
+
+    /* A lower bound on the height of the active edges is used to
+     * estimate how soon some active edge ends.         We can't advance the
+     * scan conversion by a full pixel row if an edge ends somewhere
+     * within it. */
+    grid_scaled_y_t min_height;
+    int is_vertical;
+};
+
+struct glitter_scan_converter {
+    struct polygon     polygon[1];
+    struct active_list active[1];
+    struct cell_list   coverages[1];
+
+    cairo_half_open_span_t *spans;
+    cairo_half_open_span_t spans_embedded[64];
+
+    /* Clip box. */
+    grid_scaled_x_t xmin, xmax;
+    grid_scaled_y_t ymin, ymax;
+};
+
+/* Compute the floored division a/b. Assumes / and % perform symmetric
+ * division. */
+inline static struct quorem
+floored_divrem(int a, int b)
+{
+    struct quorem qr;
+    qr.quo = a/b;
+    qr.rem = a%b;
+    if ((a^b)<0 && qr.rem) {
+       qr.quo -= 1;
+       qr.rem += b;
+    }
+    return qr;
+}
+
+/* Compute the floored division (x*a)/b. Assumes / and % perform symmetric
+ * division. */
+static struct quorem
+floored_muldivrem(int x, int a, int b)
+{
+    struct quorem qr;
+    long long xa = (long long)x*a;
+    qr.quo = xa/b;
+    qr.rem = xa%b;
+    if ((xa>=0) != (b>=0) && qr.rem) {
+       qr.quo -= 1;
+       qr.rem += b;
+    }
+    return qr;
+}
+
+static struct _pool_chunk *
+_pool_chunk_init(
+    struct _pool_chunk *p,
+    struct _pool_chunk *prev_chunk,
+    size_t capacity)
+{
+    p->prev_chunk = prev_chunk;
+    p->size = 0;
+    p->capacity = capacity;
+    return p;
+}
+
+static struct _pool_chunk *
+_pool_chunk_create(struct pool *pool, size_t size)
+{
+    struct _pool_chunk *p;
+
+    p = malloc(size + sizeof(struct _pool_chunk));
+    if (unlikely (NULL == p))
+       longjmp (*pool->jmp, _cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+    return _pool_chunk_init(p, pool->current, size);
+}
+
+static void
+pool_init(struct pool *pool,
+         jmp_buf *jmp,
+         size_t default_capacity,
+         size_t embedded_capacity)
+{
+    pool->jmp = jmp;
+    pool->current = pool->sentinel;
+    pool->first_free = NULL;
+    pool->default_capacity = default_capacity;
+    _pool_chunk_init(pool->sentinel, NULL, embedded_capacity);
+}
+
+static void
+pool_fini(struct pool *pool)
+{
+    struct _pool_chunk *p = pool->current;
+    do {
+       while (NULL != p) {
+           struct _pool_chunk *prev = p->prev_chunk;
+           if (p != pool->sentinel)
+               free(p);
+           p = prev;
+       }
+       p = pool->first_free;
+       pool->first_free = NULL;
+    } while (NULL != p);
+}
+
+/* Satisfy an allocation by first allocating a new large enough chunk
+ * and adding it to the head of the pool's chunk list. This function
+ * is called as a fallback if pool_alloc() couldn't do a quick
+ * allocation from the current chunk in the pool. */
+static void *
+_pool_alloc_from_new_chunk(
+    struct pool *pool,
+    size_t size)
+{
+    struct _pool_chunk *chunk;
+    void *obj;
+    size_t capacity;
+
+    /* If the allocation is smaller than the default chunk size then
+     * try getting a chunk off the free list.  Force alloc of a new
+     * chunk for large requests. */
+    capacity = size;
+    chunk = NULL;
+    if (size < pool->default_capacity) {
+       capacity = pool->default_capacity;
+       chunk = pool->first_free;
+       if (chunk) {
+           pool->first_free = chunk->prev_chunk;
+           _pool_chunk_init(chunk, pool->current, chunk->capacity);
+       }
+    }
+
+    if (NULL == chunk)
+       chunk = _pool_chunk_create (pool, capacity);
+    pool->current = chunk;
+
+    obj = ((unsigned char*)chunk + sizeof(*chunk) + chunk->size);
+    chunk->size += size;
+    return obj;
+}
+
+/* Allocate size bytes from the pool.  The first allocated address
+ * returned from a pool is aligned to sizeof(void*).  Subsequent
+ * addresses will maintain alignment as long as multiples of void* are
+ * allocated.  Returns the address of a new memory area or %NULL on
+ * allocation failures.         The pool retains ownership of the returned
+ * memory. */
+inline static void *
+pool_alloc (struct pool *pool, size_t size)
+{
+    struct _pool_chunk *chunk = pool->current;
+
+    if (size <= chunk->capacity - chunk->size) {
+       void *obj = ((unsigned char*)chunk + sizeof(*chunk) + chunk->size);
+       chunk->size += size;
+       return obj;
+    } else {
+       return _pool_alloc_from_new_chunk(pool, size);
+    }
+}
+
+/* Relinquish all pool_alloced memory back to the pool. */
+static void
+pool_reset (struct pool *pool)
+{
+    /* Transfer all used chunks to the chunk free list. */
+    struct _pool_chunk *chunk = pool->current;
+    if (chunk != pool->sentinel) {
+       while (chunk->prev_chunk != pool->sentinel) {
+           chunk = chunk->prev_chunk;
+       }
+       chunk->prev_chunk = pool->first_free;
+       pool->first_free = pool->current;
+    }
+    /* Reset the sentinel as the current chunk. */
+    pool->current = pool->sentinel;
+    pool->sentinel->size = 0;
+}
+
+/* Rewinds the cell list's cursor to the beginning.  After rewinding
+ * we're good to cell_list_find() the cell any x coordinate. */
+inline static void
+cell_list_rewind (struct cell_list *cells)
+{
+    cells->cursor = &cells->head;
+}
+
+inline static void
+cell_list_maybe_rewind (struct cell_list *cells, int x)
+{
+    if (x < cells->cursor->x) {
+       cells->cursor = cells->rewind;
+       if (x < cells->cursor->x)
+           cells->cursor = &cells->head;
+    }
+}
+
+inline static void
+cell_list_set_rewind (struct cell_list *cells)
+{
+    cells->rewind = cells->cursor;
+}
+
+static void
+cell_list_init(struct cell_list *cells, jmp_buf *jmp)
+{
+    pool_init(cells->cell_pool.base, jmp,
+             256*sizeof(struct cell),
+             sizeof(cells->cell_pool.embedded));
+    cells->tail.next = NULL;
+    cells->tail.x = INT_MAX;
+    cells->head.x = INT_MIN;
+    cells->head.next = &cells->tail;
+    cell_list_rewind (cells);
+}
+
+static void
+cell_list_fini(struct cell_list *cells)
+{
+    pool_fini (cells->cell_pool.base);
+}
+
+/* Empty the cell list.  This is called at the start of every pixel
+ * row. */
+inline static void
+cell_list_reset (struct cell_list *cells)
+{
+    cell_list_rewind (cells);
+    cells->head.next = &cells->tail;
+    pool_reset (cells->cell_pool.base);
+}
+
+inline static struct cell *
+cell_list_alloc (struct cell_list *cells,
+                struct cell *tail,
+                int x)
+{
+    struct cell *cell;
+
+    cell = pool_alloc (cells->cell_pool.base, sizeof (struct cell));
+    cell->next = tail->next;
+    tail->next = cell;
+    cell->x = x;
+    *(uint32_t *)&cell->uncovered_area = 0;
+
+    return cell;
+}
+
+/* Find a cell at the given x-coordinate.  Returns %NULL if a new cell
+ * needed to be allocated but couldn't be.  Cells must be found with
+ * non-decreasing x-coordinate until the cell list is rewound using
+ * cell_list_rewind(). Ownership of the returned cell is retained by
+ * the cell list. */
+inline static struct cell *
+cell_list_find (struct cell_list *cells, int x)
+{
+    struct cell *tail = cells->cursor;
+
+    if (tail->x == x)
+       return tail;
+
+    while (1) {
+       UNROLL3({
+               if (tail->next->x > x)
+                       break;
+               tail = tail->next;
+       });
+    }
+
+    if (tail->x != x)
+       tail = cell_list_alloc (cells, tail, x);
+    return cells->cursor = tail;
+
+}
+
+/* Find two cells at x1 and x2.         This is exactly equivalent
+ * to
+ *
+ *   pair.cell1 = cell_list_find(cells, x1);
+ *   pair.cell2 = cell_list_find(cells, x2);
+ *
+ * except with less function call overhead. */
+inline static struct cell_pair
+cell_list_find_pair(struct cell_list *cells, int x1, int x2)
+{
+    struct cell_pair pair;
+
+    pair.cell1 = cells->cursor;
+    while (1) {
+       UNROLL3({
+               if (pair.cell1->next->x > x1)
+                       break;
+               pair.cell1 = pair.cell1->next;
+       });
+    }
+    if (pair.cell1->x != x1)
+       pair.cell1 = cell_list_alloc (cells, pair.cell1, x1);
+
+    pair.cell2 = pair.cell1;
+    while (1) {
+       UNROLL3({
+               if (pair.cell2->next->x > x2)
+                       break;
+               pair.cell2 = pair.cell2->next;
+       });
+    }
+    if (pair.cell2->x != x2)
+       pair.cell2 = cell_list_alloc (cells, pair.cell2, x2);
+
+    cells->cursor = pair.cell2;
+    return pair;
+}
+
+/* Add a subpixel span covering [x1, x2) to the coverage cells. */
+inline static void
+cell_list_add_subspan(struct cell_list *cells,
+                     grid_scaled_x_t x1,
+                     grid_scaled_x_t x2)
+{
+    int ix1, fx1;
+    int ix2, fx2;
+
+    if (x1 == x2)
+       return;
+
+    GRID_X_TO_INT_FRAC(x1, ix1, fx1);
+    GRID_X_TO_INT_FRAC(x2, ix2, fx2);
+
+    if (ix1 != ix2) {
+       struct cell_pair p;
+       p = cell_list_find_pair(cells, ix1, ix2);
+       p.cell1->uncovered_area += 2*fx1;
+       ++p.cell1->covered_height;
+       p.cell2->uncovered_area -= 2*fx2;
+       --p.cell2->covered_height;
+    } else {
+       struct cell *cell = cell_list_find(cells, ix1);
+       cell->uncovered_area += 2*(fx1-fx2);
+    }
+}
+
+/* Adds the analytical coverage of an edge crossing the current pixel
+ * row to the coverage cells and advances the edge's x position to the
+ * following row.
+ *
+ * This function is only called when we know that during this pixel row:
+ *
+ * 1) The relative order of all edges on the active list doesn't
+ * change.  In particular, no edges intersect within this row to pixel
+ * precision.
+ *
+ * 2) No new edges start in this row.
+ *
+ * 3) No existing edges end mid-row.
+ *
+ * This function depends on being called with all edges from the
+ * active list in the order they appear on the list (i.e. with
+ * non-decreasing x-coordinate.)  */
+static void
+cell_list_render_edge(struct cell_list *cells,
+                     struct edge *edge,
+                     int sign)
+{
+    grid_scaled_y_t y1, y2, dy;
+    grid_scaled_x_t dx;
+    int ix1, ix2;
+    grid_scaled_x_t fx1, fx2;
+
+    struct quorem x1 = edge->x;
+    struct quorem x2 = x1;
+
+    if (! edge->vertical) {
+       x2.quo += edge->dxdy_full.quo;
+       x2.rem += edge->dxdy_full.rem;
+       if (x2.rem >= 0) {
+           ++x2.quo;
+           x2.rem -= edge->dy;
+       }
+
+       edge->x = x2;
+    }
+
+    GRID_X_TO_INT_FRAC(x1.quo, ix1, fx1);
+    GRID_X_TO_INT_FRAC(x2.quo, ix2, fx2);
+
+    /* Edge is entirely within a column? */
+    if (ix1 == ix2) {
+       /* We always know that ix1 is >= the cell list cursor in this
+        * case due to the no-intersections precondition.  */
+       struct cell *cell = cell_list_find(cells, ix1);
+       cell->covered_height += sign*GRID_Y;
+       cell->uncovered_area += sign*(fx1 + fx2)*GRID_Y;
+       return;
+    }
+
+    /* Orient the edge left-to-right. */
+    dx = x2.quo - x1.quo;
+    if (dx >= 0) {
+       y1 = 0;
+       y2 = GRID_Y;
+    } else {
+       int tmp;
+       tmp = ix1; ix1 = ix2; ix2 = tmp;
+       tmp = fx1; fx1 = fx2; fx2 = tmp;
+       dx = -dx;
+       sign = -sign;
+       y1 = GRID_Y;
+       y2 = 0;
+    }
+    dy = y2 - y1;
+
+    /* Add coverage for all pixels [ix1,ix2] on this row crossed
+     * by the edge. */
+    {
+       struct cell_pair pair;
+       struct quorem y = floored_divrem((GRID_X - fx1)*dy, dx);
+
+       /* When rendering a previous edge on the active list we may
+        * advance the cell list cursor past the leftmost pixel of the
+        * current edge even though the two edges don't intersect.
+        * e.g. consider two edges going down and rightwards:
+        *
+        *  --\_+---\_+-----+-----+----
+        *      \_    \_    |     |
+        *      | \_  | \_  |     |
+        *      |   \_|   \_|     |
+        *      |     \_    \_    |
+        *  ----+-----+-\---+-\---+----
+        *
+        * The left edge touches cells past the starting cell of the
+        * right edge.  Fortunately such cases are rare.
+        *
+        * The rewinding is never necessary if the current edge stays
+        * within a single column because we've checked before calling
+        * this function that the active list order won't change. */
+       cell_list_maybe_rewind(cells, ix1);
+
+       pair = cell_list_find_pair(cells, ix1, ix1+1);
+       pair.cell1->uncovered_area += sign*y.quo*(GRID_X + fx1);
+       pair.cell1->covered_height += sign*y.quo;
+       y.quo += y1;
+
+       if (ix1+1 < ix2) {
+           struct quorem dydx_full = floored_divrem(GRID_X*dy, dx);
+           struct cell *cell = pair.cell2;
+
+           ++ix1;
+           do {
+               grid_scaled_y_t y_skip = dydx_full.quo;
+               y.rem += dydx_full.rem;
+               if (y.rem >= dx) {
+                   ++y_skip;
+                   y.rem -= dx;
+               }
+
+               y.quo += y_skip;
+
+               y_skip *= sign;
+               cell->uncovered_area += y_skip*GRID_X;
+               cell->covered_height += y_skip;
+
+               ++ix1;
+               cell = cell_list_find(cells, ix1);
+           } while (ix1 != ix2);
+
+           pair.cell2 = cell;
+       }
+       pair.cell2->uncovered_area += sign*(y2 - y.quo)*fx2;
+       pair.cell2->covered_height += sign*(y2 - y.quo);
+    }
+}
+
+static void
+polygon_init (struct polygon *polygon, jmp_buf *jmp)
+{
+    polygon->ymin = polygon->ymax = 0;
+    polygon->y_buckets = polygon->y_buckets_embedded;
+    pool_init (polygon->edge_pool.base, jmp,
+              8192 - sizeof (struct _pool_chunk),
+              sizeof (polygon->edge_pool.embedded));
+}
+
+static void
+polygon_fini (struct polygon *polygon)
+{
+    if (polygon->y_buckets != polygon->y_buckets_embedded)
+       free (polygon->y_buckets);
+
+    pool_fini (polygon->edge_pool.base);
+}
+
+/* Empties the polygon of all edges. The polygon is then prepared to
+ * receive new edges and clip them to the vertical range
+ * [ymin,ymax). */
+static glitter_status_t
+polygon_reset (struct polygon *polygon,
+              grid_scaled_y_t ymin,
+              grid_scaled_y_t ymax)
+{
+    unsigned h = ymax - ymin;
+    unsigned num_buckets = EDGE_Y_BUCKET_INDEX(ymax + GRID_Y-1, ymin);
+
+    pool_reset(polygon->edge_pool.base);
+
+    if (unlikely (h > 0x7FFFFFFFU - GRID_Y))
+       goto bail_no_mem; /* even if you could, you wouldn't want to. */
+
+    if (polygon->y_buckets != polygon->y_buckets_embedded)
+       free (polygon->y_buckets);
+
+    polygon->y_buckets =  polygon->y_buckets_embedded;
+    if (num_buckets > ARRAY_LENGTH (polygon->y_buckets_embedded)) {
+       polygon->y_buckets = _cairo_malloc_ab (num_buckets,
+                                              sizeof (struct edge *));
+       if (unlikely (NULL == polygon->y_buckets))
+           goto bail_no_mem;
+    }
+    memset (polygon->y_buckets, 0, num_buckets * sizeof (struct edge *));
+
+    polygon->ymin = ymin;
+    polygon->ymax = ymax;
+    return GLITTER_STATUS_SUCCESS;
+
+bail_no_mem:
+    polygon->ymin = 0;
+    polygon->ymax = 0;
+    return GLITTER_STATUS_NO_MEMORY;
+}
+
+static void
+_polygon_insert_edge_into_its_y_bucket(struct polygon *polygon,
+                                      struct edge *e)
+{
+    unsigned ix = EDGE_Y_BUCKET_INDEX(e->ytop, polygon->ymin);
+    struct edge **ptail = &polygon->y_buckets[ix];
+    e->next = *ptail;
+    *ptail = e;
+}
+
+inline static void
+polygon_add_edge (struct polygon *polygon,
+                 const cairo_edge_t *edge)
+{
+    struct edge *e;
+    grid_scaled_x_t dx;
+    grid_scaled_y_t dy;
+    grid_scaled_y_t ytop, ybot;
+    grid_scaled_y_t ymin = polygon->ymin;
+    grid_scaled_y_t ymax = polygon->ymax;
+
+    if (unlikely (edge->top >= ymax || edge->bottom <= ymin))
+       return;
+
+    e = pool_alloc (polygon->edge_pool.base, sizeof (struct edge));
+
+    dx = edge->line.p2.x - edge->line.p1.x;
+    dy = edge->line.p2.y - edge->line.p1.y;
+    e->dy = dy;
+    e->dir = edge->dir;
+
+    ytop = edge->top >= ymin ? edge->top : ymin;
+    ybot = edge->bottom <= ymax ? edge->bottom : ymax;
+    e->ytop = ytop;
+    e->height_left = ybot - ytop;
+
+    if (dx == 0) {
+       e->vertical = TRUE;
+       e->x.quo = edge->line.p1.x;
+       e->x.rem = 0;
+       e->dxdy.quo = 0;
+       e->dxdy.rem = 0;
+       e->dxdy_full.quo = 0;
+       e->dxdy_full.rem = 0;
+    } else {
+       e->vertical = FALSE;
+       e->dxdy = floored_divrem (dx, dy);
+       if (ytop == edge->line.p1.y) {
+           e->x.quo = edge->line.p1.x;
+           e->x.rem = 0;
+       } else {
+           e->x = floored_muldivrem (ytop - edge->line.p1.y, dx, dy);
+           e->x.quo += edge->line.p1.x;
+       }
+
+       if (e->height_left >= GRID_Y) {
+           e->dxdy_full = floored_muldivrem (GRID_Y, dx, dy);
+       } else {
+           e->dxdy_full.quo = 0;
+           e->dxdy_full.rem = 0;
+       }
+    }
+
+    _polygon_insert_edge_into_its_y_bucket (polygon, e);
+
+    e->x.rem -= dy;            /* Bias the remainder for faster
+                                * edge advancement. */
+}
+
+static void
+active_list_reset (struct active_list *active)
+{
+    active->head.vertical = 1;
+    active->head.height_left = INT_MAX;
+    active->head.x.quo = INT_MIN;
+    active->head.prev = NULL;
+    active->head.next = &active->tail;
+    active->tail.prev = &active->head;
+    active->tail.next = NULL;
+    active->tail.x.quo = INT_MAX;
+    active->tail.height_left = INT_MAX;
+    active->tail.vertical = 1;
+    active->min_height = 0;
+    active->is_vertical = 1;
+}
+
+static void
+active_list_init(struct active_list *active)
+{
+    active_list_reset(active);
+}
+
+/*
+ * Merge two sorted edge lists.
+ * Input:
+ *  - head_a: The head of the first list.
+ *  - head_b: The head of the second list; head_b cannot be NULL.
+ * Output:
+ * Returns the head of the merged list.
+ *
+ * Implementation notes:
+ * To make it fast (in particular, to reduce to an insertion sort whenever
+ * one of the two input lists only has a single element) we iterate through
+ * a list until its head becomes greater than the head of the other list,
+ * then we switch their roles. As soon as one of the two lists is empty, we
+ * just attach the other one to the current list and exit.
+ * Writes to memory are only needed to "switch" lists (as it also requires
+ * attaching to the output list the list which we will be iterating next) and
+ * to attach the last non-empty list.
+ */
+static struct edge *
+merge_sorted_edges (struct edge *head_a, struct edge *head_b)
+{
+    struct edge *head, **next, *prev;
+    int32_t x;
+
+    prev = head_a->prev;
+    next = &head;
+    if (head_a->x.quo <= head_b->x.quo) {
+       head = head_a;
+    } else {
+       head = head_b;
+       head_b->prev = prev;
+       goto start_with_b;
+    }
+
+    do {
+       x = head_b->x.quo;
+       while (head_a != NULL && head_a->x.quo <= x) {
+           prev = head_a;
+           next = &head_a->next;
+           head_a = head_a->next;
+       }
+
+       head_b->prev = prev;
+       *next = head_b;
+       if (head_a == NULL)
+           return head;
+
+start_with_b:
+       x = head_a->x.quo;
+       while (head_b != NULL && head_b->x.quo <= x) {
+           prev = head_b;
+           next = &head_b->next;
+           head_b = head_b->next;
+       }
+
+       head_a->prev = prev;
+       *next = head_a;
+       if (head_b == NULL)
+           return head;
+    } while (1);
+}
+
+/*
+ * Sort (part of) a list.
+ * Input:
+ *  - list: The list to be sorted; list cannot be NULL.
+ *  - limit: Recursion limit.
+ * Output:
+ *  - head_out: The head of the sorted list containing the first 2^(level+1) elements of the
+ *              input list; if the input list has fewer elements, head_out be a sorted list
+ *              containing all the elements of the input list.
+ * Returns the head of the list of unprocessed elements (NULL if the sorted list contains
+ * all the elements of the input list).
+ *
+ * Implementation notes:
+ * Special case single element list, unroll/inline the sorting of the first two elements.
+ * Some tail recursion is used since we iterate on the bottom-up solution of the problem
+ * (we start with a small sorted list and keep merging other lists of the same size to it).
+ */
+static struct edge *
+sort_edges (struct edge *list,
+           unsigned int level,
+           struct edge **head_out)
+{
+    struct edge *head_other, *remaining;
+    unsigned int i;
+
+    head_other = list->next;
+
+    if (head_other == NULL) {
+       *head_out = list;
+       return NULL;
+    }
+
+    remaining = head_other->next;
+    if (list->x.quo <= head_other->x.quo) {
+       *head_out = list;
+       head_other->next = NULL;
+    } else {
+       *head_out = head_other;
+       head_other->prev = list->prev;
+       head_other->next = list;
+       list->prev = head_other;
+       list->next = NULL;
+    }
+
+    for (i = 0; i < level && remaining; i++) {
+       remaining = sort_edges (remaining, i, &head_other);
+       *head_out = merge_sorted_edges (*head_out, head_other);
+    }
+
+    return remaining;
+}
+
+ static struct edge *
+merge_unsorted_edges (struct edge *head, struct edge *unsorted)
+{
+    sort_edges (unsorted, UINT_MAX, &unsorted);
+    return merge_sorted_edges (head, unsorted);
+}
+
+/* Test if the edges on the active list can be safely advanced by a
+ * full row without intersections or any edges ending. */
+inline static int
+can_do_full_row (struct active_list *active)
+{
+    const struct edge *e;
+    int prev_x = INT_MIN;
+
+    /* Recomputes the minimum height of all edges on the active
+     * list if we have been dropping edges. */
+    if (active->min_height <= 0) {
+       int min_height = INT_MAX;
+       int is_vertical = 1;
+
+       e = active->head.next;
+       while (NULL != e) {
+           if (e->height_left < min_height)
+               min_height = e->height_left;
+           is_vertical &= e->vertical;
+           e = e->next;
+       }
+
+       active->is_vertical = is_vertical;
+       active->min_height = min_height;
+    }
+
+    if (active->min_height < GRID_Y)
+       return 0;
+
+    /* Check for intersections as no edges end during the next row. */
+    for (e = active->head.next; e != &active->tail; e = e->next) {
+       struct quorem x = e->x;
+
+       if (! e->vertical) {
+           x.quo += e->dxdy_full.quo;
+           x.rem += e->dxdy_full.rem;
+           if (x.rem >= 0)
+               ++x.quo;
+       }
+
+       if (x.quo < prev_x)
+           return 0;
+
+       prev_x = x.quo;
+    }
+
+    return 1;
+}
+
+/* Merges edges on the given subpixel row from the polygon to the
+ * active_list. */
+inline static void
+active_list_merge_edges_from_bucket(struct active_list *active,
+                                   struct edge *edges)
+{
+    active->head.next = merge_unsorted_edges (active->head.next, edges);
+}
+
+inline static void
+polygon_fill_buckets (struct active_list *active,
+                     struct edge *edge,
+                     int y,
+                     struct edge **buckets)
+{
+    grid_scaled_y_t min_height = active->min_height;
+    int is_vertical = active->is_vertical;
+
+    while (edge) {
+       struct edge *next = edge->next;
+       int suby = edge->ytop - y;
+       if (buckets[suby])
+           buckets[suby]->prev = edge;
+       edge->next = buckets[suby];
+       edge->prev = NULL;
+       buckets[suby] = edge;
+       if (edge->height_left < min_height)
+           min_height = edge->height_left;
+       is_vertical &= edge->vertical;
+       edge = next;
+    }
+
+    active->is_vertical = is_vertical;
+    active->min_height = min_height;
+}
+
+inline static void
+sub_row (struct active_list *active,
+        struct cell_list *coverages,
+        unsigned int mask)
+{
+    struct edge *edge = active->head.next;
+    int xstart = INT_MIN, prev_x = INT_MIN;
+    int winding = 0;
+
+    cell_list_rewind (coverages);
+
+    while (&active->tail != edge) {
+       struct edge *next = edge->next;
+       int xend = edge->x.quo;
+
+       if (--edge->height_left) {
+           edge->x.quo += edge->dxdy.quo;
+           edge->x.rem += edge->dxdy.rem;
+           if (edge->x.rem >= 0) {
+               ++edge->x.quo;
+               edge->x.rem -= edge->dy;
+           }
+
+           if (edge->x.quo < prev_x) {
+               struct edge *pos = edge->prev;
+               pos->next = next;
+               next->prev = pos;
+               do {
+                   pos = pos->prev;
+               } while (edge->x.quo < pos->x.quo);
+               pos->next->prev = edge;
+               edge->next = pos->next;
+               edge->prev = pos;
+               pos->next = edge;
+           } else
+               prev_x = edge->x.quo;
+       } else {
+           edge->prev->next = next;
+           next->prev = edge->prev;
+       }
+
+       winding += edge->dir;
+       if ((winding & mask) == 0) {
+           if (next->x.quo != xend) {
+               cell_list_add_subspan (coverages, xstart, xend);
+               xstart = INT_MIN;
+           }
+       } else if (xstart == INT_MIN)
+           xstart = xend;
+
+       edge = next;
+    }
+}
+
+inline static void dec (struct edge *e, int h)
+{
+    e->height_left -= h;
+    if (e->height_left == 0) {
+       e->prev->next = e->next;
+       e->next->prev = e->prev;
+    }
+}
+
+inline static void full_step (struct edge *e)
+{
+    if (! e->vertical) {
+       e->x.quo += e->dxdy_full.quo;
+       e->x.rem += e->dxdy_full.rem;
+       if (e->x.rem >= 0) {
+           ++e->x.quo;
+           e->x.rem -= e->dy;
+       }
+    }
+}
+
+static void
+full_row (struct active_list *active,
+         struct cell_list *coverages,
+         unsigned int mask)
+{
+    struct edge *left = active->head.next;
+
+    while (&active->tail != left) {
+       struct edge *right;
+       int winding;
+
+       dec (left, GRID_Y);
+
+       winding = left->dir;
+       right = left->next;
+       do {
+           dec (right, GRID_Y);
+
+           winding += right->dir;
+           if ((winding & mask) == 0 && right->next->x.quo != right->x.quo)
+               break;
+
+           full_step (right);
+
+           right = right->next;
+       } while (1);
+
+       cell_list_set_rewind (coverages);
+       cell_list_render_edge (coverages, left, +1);
+       cell_list_render_edge (coverages, right, -1);
+
+       left = right->next;
+    }
+}
+
+static void
+_glitter_scan_converter_init(glitter_scan_converter_t *converter, jmp_buf *jmp)
+{
+    polygon_init(converter->polygon, jmp);
+    active_list_init(converter->active);
+    cell_list_init(converter->coverages, jmp);
+    converter->xmin=0;
+    converter->ymin=0;
+    converter->xmax=0;
+    converter->ymax=0;
+}
+
+static void
+_glitter_scan_converter_fini(glitter_scan_converter_t *self)
+{
+    if (self->spans != self->spans_embedded)
+       free (self->spans);
+
+    polygon_fini(self->polygon);
+    cell_list_fini(self->coverages);
+
+    self->xmin=0;
+    self->ymin=0;
+    self->xmax=0;
+    self->ymax=0;
+}
+
+static grid_scaled_t
+int_to_grid_scaled(int i, int scale)
+{
+    /* Clamp to max/min representable scaled number. */
+    if (i >= 0) {
+       if (i >= INT_MAX/scale)
+           i = INT_MAX/scale;
+    }
+    else {
+       if (i <= INT_MIN/scale)
+           i = INT_MIN/scale;
+    }
+    return i*scale;
+}
+
+#define int_to_grid_scaled_x(x) int_to_grid_scaled((x), GRID_X)
+#define int_to_grid_scaled_y(x) int_to_grid_scaled((x), GRID_Y)
+
+I glitter_status_t
+glitter_scan_converter_reset(
+                            glitter_scan_converter_t *converter,
+                            int xmin, int ymin,
+                            int xmax, int ymax)
+{
+    glitter_status_t status;
+    int max_num_spans;
+
+    converter->xmin = 0; converter->xmax = 0;
+    converter->ymin = 0; converter->ymax = 0;
+
+    max_num_spans = xmax - xmin + 1;
+
+    if (max_num_spans > ARRAY_LENGTH(converter->spans_embedded)) {
+       converter->spans = _cairo_malloc_ab (max_num_spans,
+                                            sizeof (cairo_half_open_span_t));
+       if (unlikely (converter->spans == NULL))
+           return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+    } else
+       converter->spans = converter->spans_embedded;
+
+    xmin = int_to_grid_scaled_x(xmin);
+    ymin = int_to_grid_scaled_y(ymin);
+    xmax = int_to_grid_scaled_x(xmax);
+    ymax = int_to_grid_scaled_y(ymax);
+
+    active_list_reset(converter->active);
+    cell_list_reset(converter->coverages);
+    status = polygon_reset(converter->polygon, ymin, ymax);
+    if (status)
+       return status;
+
+    converter->xmin = xmin;
+    converter->xmax = xmax;
+    converter->ymin = ymin;
+    converter->ymax = ymax;
+    return GLITTER_STATUS_SUCCESS;
+}
+
+/* INPUT_TO_GRID_X/Y (in_coord, out_grid_scaled, grid_scale)
+ *   These macros convert an input coordinate in the client's
+ *   device space to the rasterisation grid.
+ */
+/* Gah.. this bit of ugly defines INPUT_TO_GRID_X/Y so as to use
+ * shifts if possible, and something saneish if not.
+ */
+#if !defined(INPUT_TO_GRID_Y) && defined(GRID_Y_BITS) && GRID_Y_BITS <= GLITTER_INPUT_BITS
+#  define INPUT_TO_GRID_Y(in, out) (out) = (in) >> (GLITTER_INPUT_BITS - GRID_Y_BITS)
+#else
+#  define INPUT_TO_GRID_Y(in, out) INPUT_TO_GRID_general(in, out, GRID_Y)
+#endif
+
+#if !defined(INPUT_TO_GRID_X) && defined(GRID_X_BITS) && GRID_X_BITS <= GLITTER_INPUT_BITS
+#  define INPUT_TO_GRID_X(in, out) (out) = (in) >> (GLITTER_INPUT_BITS - GRID_X_BITS)
+#else
+#  define INPUT_TO_GRID_X(in, out) INPUT_TO_GRID_general(in, out, GRID_X)
+#endif
+
+#define INPUT_TO_GRID_general(in, out, grid_scale) do {                \
+    long long tmp__ = (long long)(grid_scale) * (in);  \
+    tmp__ >>= GLITTER_INPUT_BITS;                              \
+    (out) = tmp__;                                             \
+} while (0)
+
+/* Add a new polygon edge from pixel (x1,y1) to (x2,y2) to the scan
+ * converter.  The coordinates represent pixel positions scaled by
+ * 2**GLITTER_PIXEL_BITS.  If this function fails then the scan
+ * converter should be reset or destroyed.  Dir must be +1 or -1,
+ * with the latter reversing the orientation of the edge. */
+I void
+glitter_scan_converter_add_edge (glitter_scan_converter_t *converter,
+                                const cairo_edge_t *edge)
+{
+    cairo_edge_t e;
+
+    INPUT_TO_GRID_Y (edge->top, e.top);
+    INPUT_TO_GRID_Y (edge->bottom, e.bottom);
+    if (e.top >= e.bottom)
+       return;
+
+    /* XXX: possible overflows if GRID_X/Y > 2**GLITTER_INPUT_BITS */
+    INPUT_TO_GRID_Y (edge->line.p1.y, e.line.p1.y);
+    INPUT_TO_GRID_Y (edge->line.p2.y, e.line.p2.y);
+    if (e.line.p1.y == e.line.p2.y)
+       return;
+
+    INPUT_TO_GRID_X (edge->line.p1.x, e.line.p1.x);
+    INPUT_TO_GRID_X (edge->line.p2.x, e.line.p2.x);
+
+    e.dir = edge->dir;
+
+    polygon_add_edge (converter->polygon, &e);
+}
+
+static void
+step_edges (struct active_list *active, int count)
+{
+    struct edge *edge;
+
+    count *= GRID_Y;
+    for (edge = active->head.next; edge != &active->tail; edge = edge->next) {
+       edge->height_left -= count;
+       if (! edge->height_left) {
+           edge->prev->next = edge->next;
+           edge->next->prev = edge->prev;
+       }
+    }
+}
+
+static glitter_status_t
+blit_a8 (struct cell_list *cells,
+        cairo_span_renderer_t *renderer,
+        cairo_half_open_span_t *spans,
+        int y, int height,
+        int xmin, int xmax)
+{
+    struct cell *cell = cells->head.next;
+    int prev_x = xmin, last_x = -1;
+    int16_t cover = 0, last_cover = 0;
+    unsigned num_spans;
+
+    if (cell == &cells->tail)
+       return CAIRO_STATUS_SUCCESS;
+
+    /* Skip cells to the left of the clip region. */
+    while (cell->x < xmin) {
+       cover += cell->covered_height;
+       cell = cell->next;
+    }
+    cover *= GRID_X*2;
+
+    /* Form the spans from the coverages and areas. */
+    num_spans = 0;
+    for (; cell->x < xmax; cell = cell->next) {
+       int x = cell->x;
+       int16_t area;
+
+       if (x > prev_x && cover != last_cover) {
+           spans[num_spans].x = prev_x;
+           spans[num_spans].coverage = GRID_AREA_TO_ALPHA (cover);
+           last_cover = cover;
+           last_x = prev_x;
+           ++num_spans;
+       }
+
+       cover += cell->covered_height*GRID_X*2;
+       area = cover - cell->uncovered_area;
+
+       if (area != last_cover) {
+           spans[num_spans].x = x;
+           spans[num_spans].coverage = GRID_AREA_TO_ALPHA (area);
+           last_cover = area;
+           last_x = x;
+           ++num_spans;
+       }
+
+       prev_x = x+1;
+    }
+
+    if (prev_x <= xmax && cover != last_cover) {
+       spans[num_spans].x = prev_x;
+       spans[num_spans].coverage = GRID_AREA_TO_ALPHA (cover);
+       last_cover = cover;
+       last_x = prev_x;
+       ++num_spans;
+    }
+
+    if (last_x < xmax && last_cover) {
+       spans[num_spans].x = xmax;
+       spans[num_spans].coverage = 0;
+       ++num_spans;
+    }
+
+    /* Dump them into the renderer. */
+    return renderer->render_rows (renderer, y, height, spans, num_spans);
+}
+
+#define GRID_AREA_TO_A1(A)  ((GRID_AREA_TO_ALPHA (A) > 127) ? 255 : 0)
+static glitter_status_t
+blit_a1 (struct cell_list *cells,
+        cairo_span_renderer_t *renderer,
+        cairo_half_open_span_t *spans,
+        int y, int height,
+        int xmin, int xmax)
+{
+    struct cell *cell = cells->head.next;
+    int prev_x = xmin, last_x = -1;
+    int16_t cover = 0;
+    uint8_t coverage, last_cover = 0;
+    unsigned num_spans;
+
+    if (cell == &cells->tail)
+       return CAIRO_STATUS_SUCCESS;
+
+    /* Skip cells to the left of the clip region. */
+    while (cell->x < xmin) {
+       cover += cell->covered_height;
+       cell = cell->next;
+    }
+    cover *= GRID_X*2;
+
+    /* Form the spans from the coverages and areas. */
+    num_spans = 0;
+    for (; cell->x < xmax; cell = cell->next) {
+       int x = cell->x;
+       int16_t area;
+
+       coverage = GRID_AREA_TO_A1 (cover);
+       if (x > prev_x && coverage != last_cover) {
+           last_x = spans[num_spans].x = prev_x;
+           last_cover = spans[num_spans].coverage = coverage;
+           ++num_spans;
+       }
+
+       cover += cell->covered_height*GRID_X*2;
+       area = cover - cell->uncovered_area;
+
+       coverage = GRID_AREA_TO_A1 (area);
+       if (coverage != last_cover) {
+           last_x = spans[num_spans].x = x;
+           last_cover = spans[num_spans].coverage = coverage;
+           ++num_spans;
+       }
+
+       prev_x = x+1;
+    }
+
+    coverage = GRID_AREA_TO_A1 (cover);
+    if (prev_x <= xmax && coverage != last_cover) {
+       last_x = spans[num_spans].x = prev_x;
+       last_cover = spans[num_spans].coverage = coverage;
+       ++num_spans;
+    }
+
+    if (last_x < xmax && last_cover) {
+       spans[num_spans].x = xmax;
+       spans[num_spans].coverage = 0;
+       ++num_spans;
+    }
+    if (num_spans == 1)
+       return CAIRO_STATUS_SUCCESS;
+
+    /* Dump them into the renderer. */
+    return renderer->render_rows (renderer, y, height, spans, num_spans);
+}
+
+
+I void
+glitter_scan_converter_render(glitter_scan_converter_t *converter,
+                             unsigned int winding_mask,
+                             int antialias,
+                             cairo_span_renderer_t *renderer)
+{
+    int i, j;
+    int ymax_i = converter->ymax / GRID_Y;
+    int ymin_i = converter->ymin / GRID_Y;
+    int xmin_i, xmax_i;
+    int h = ymax_i - ymin_i;
+    struct polygon *polygon = converter->polygon;
+    struct cell_list *coverages = converter->coverages;
+    struct active_list *active = converter->active;
+    struct edge *buckets[GRID_Y] = { 0 };
+
+    xmin_i = converter->xmin / GRID_X;
+    xmax_i = converter->xmax / GRID_X;
+    if (xmin_i >= xmax_i)
+       return;
+
+    /* Render each pixel row. */
+    for (i = 0; i < h; i = j) {
+       int do_full_row = 0;
+
+       j = i + 1;
+
+       /* Determine if we can ignore this row or use the full pixel
+        * stepper. */
+       if (! polygon->y_buckets[i]) {
+           if (active->head.next == &active->tail) {
+               active->min_height = INT_MAX;
+               active->is_vertical = 1;
+               for (; j < h && ! polygon->y_buckets[j]; j++)
+                   ;
+               continue;
+           }
+
+           do_full_row = can_do_full_row (active);
+       }
+
+       if (do_full_row) {
+           /* Step by a full pixel row's worth. */
+           full_row (active, coverages, winding_mask);
+
+           if (active->is_vertical) {
+               while (j < h &&
+                      polygon->y_buckets[j] == NULL &&
+                      active->min_height >= 2*GRID_Y)
+               {
+                   active->min_height -= GRID_Y;
+                   j++;
+               }
+               if (j != i + 1)
+                   step_edges (active, j - (i + 1));
+           }
+       } else {
+           int sub;
+
+           polygon_fill_buckets (active,
+                                 polygon->y_buckets[i],
+                                 (i+ymin_i)*GRID_Y,
+                                 buckets);
+
+           /* Subsample this row. */
+           for (sub = 0; sub < GRID_Y; sub++) {
+               if (buckets[sub]) {
+                   active_list_merge_edges_from_bucket (active, buckets[sub]);
+                   buckets[sub] = NULL;
+               }
+
+               sub_row (active, coverages, winding_mask);
+           }
+       }
+
+       if (antialias)
+           blit_a8 (coverages, renderer, converter->spans,
+                    i+ymin_i, j-i, xmin_i, xmax_i);
+       else
+           blit_a1 (coverages, renderer, converter->spans,
+                    i+ymin_i, j-i, xmin_i, xmax_i);
+       cell_list_reset (coverages);
+
+       active->min_height -= GRID_Y;
+    }
+}
+
+struct _cairo_tor_scan_converter {
+    cairo_scan_converter_t base;
+
+    glitter_scan_converter_t converter[1];
+    cairo_fill_rule_t fill_rule;
+    cairo_antialias_t antialias;
+
+    jmp_buf jmp;
+};
+
+typedef struct _cairo_tor_scan_converter cairo_tor_scan_converter_t;
+
+static void
+_cairo_tor_scan_converter_destroy (void *converter)
+{
+    cairo_tor_scan_converter_t *self = converter;
+    if (self == NULL) {
+       return;
+    }
+    _glitter_scan_converter_fini (self->converter);
+    free(self);
+}
+
+cairo_status_t
+_cairo_tor_scan_converter_add_polygon (void            *converter,
+                                      const cairo_polygon_t *polygon)
+{
+    cairo_tor_scan_converter_t *self = converter;
+    int i;
+
+#if 0
+    FILE *file = fopen ("polygon.txt", "w");
+    _cairo_debug_print_polygon (file, polygon);
+    fclose (file);
+#endif
+
+    for (i = 0; i < polygon->num_edges; i++)
+        glitter_scan_converter_add_edge (self->converter, &polygon->edges[i]);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_tor_scan_converter_generate (void                       *converter,
+                                   cairo_span_renderer_t       *renderer)
+{
+    cairo_tor_scan_converter_t *self = converter;
+    cairo_status_t status;
+
+    if ((status = setjmp (self->jmp)))
+       return _cairo_scan_converter_set_error (self, _cairo_error (status));
+
+    glitter_scan_converter_render (self->converter,
+                                  self->fill_rule == CAIRO_FILL_RULE_WINDING ? ~0 : 1,
+                                  self->antialias != CAIRO_ANTIALIAS_NONE,
+                                  renderer);
+    return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_scan_converter_t *
+_cairo_tor_scan_converter_create (int                  xmin,
+                                 int                   ymin,
+                                 int                   xmax,
+                                 int                   ymax,
+                                 cairo_fill_rule_t     fill_rule,
+                                 cairo_antialias_t     antialias)
+{
+    cairo_tor_scan_converter_t *self;
+    cairo_status_t status;
+
+    self = malloc (sizeof(struct _cairo_tor_scan_converter));
+    if (unlikely (self == NULL)) {
+       status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+       goto bail_nomem;
+    }
+
+    self->base.destroy = _cairo_tor_scan_converter_destroy;
+    self->base.generate = _cairo_tor_scan_converter_generate;
+
+    _glitter_scan_converter_init (self->converter, &self->jmp);
+    status = glitter_scan_converter_reset (self->converter,
+                                          xmin, ymin, xmax, ymax);
+    if (unlikely (status))
+       goto bail;
+
+    self->fill_rule = fill_rule;
+    self->antialias = antialias;
+
+    return &self->base;
+
+ bail:
+    self->base.destroy(&self->base);
+ bail_nomem:
+    return _cairo_scan_converter_create_in_error (status);
+}
diff --git a/src/cairo-tor22-scan-converter.c b/src/cairo-tor22-scan-converter.c
new file mode 100644 (file)
index 0000000..2f93030
--- /dev/null
@@ -0,0 +1,1707 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* glitter-paths - polygon scan converter
+ *
+ * Copyright (c) 2008  M Joonas Pihlaja
+ * Copyright (c) 2007  David Turner
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+/* This is the Glitter paths scan converter incorporated into cairo.
+ * The source is from commit 734c53237a867a773640bd5b64816249fa1730f8
+ * of
+ *
+ *   http://gitweb.freedesktop.org/?p=users/joonas/glitter-paths
+ */
+/* Glitter-paths is a stand alone polygon rasteriser derived from
+ * David Turner's reimplementation of Tor Anderssons's 15x17
+ * supersampling rasteriser from the Apparition graphics library.  The
+ * main new feature here is cheaply choosing per-scan line between
+ * doing fully analytical coverage computation for an entire row at a
+ * time vs. using a supersampling approach.
+ *
+ * David Turner's code can be found at
+ *
+ *   http://david.freetype.org/rasterizer-shootout/raster-comparison-20070813.tar.bz2
+ *
+ * In particular this file incorporates large parts of ftgrays_tor10.h
+ * from raster-comparison-20070813.tar.bz2
+ */
+/* Overview
+ *
+ * A scan converter's basic purpose to take polygon edges and convert
+ * them into an RLE compressed A8 mask.  This one works in two phases:
+ * gathering edges and generating spans.
+ *
+ * 1) As the user feeds the scan converter edges they are vertically
+ * clipped and bucketted into a _polygon_ data structure.  The edges
+ * are also snapped from the user's coordinates to the subpixel grid
+ * coordinates used during scan conversion.
+ *
+ *     user
+ *      |
+ *      | edges
+ *      V
+ *    polygon buckets
+ *
+ * 2) Generating spans works by performing a vertical sweep of pixel
+ * rows from top to bottom and maintaining an _active_list_ of edges
+ * that intersect the row.  From the active list the fill rule
+ * determines which edges are the left and right edges of the start of
+ * each span, and their contribution is then accumulated into a pixel
+ * coverage list (_cell_list_) as coverage deltas.  Once the coverage
+ * deltas of all edges are known we can form spans of constant pixel
+ * coverage by summing the deltas during a traversal of the cell list.
+ * At the end of a pixel row the cell list is sent to a coverage
+ * blitter for rendering to some target surface.
+ *
+ * The pixel coverages are computed by either supersampling the row
+ * and box filtering a mono rasterisation, or by computing the exact
+ * coverages of edges in the active list.  The supersampling method is
+ * used whenever some edge starts or stops within the row or there are
+ * edge intersections in the row.
+ *
+ *   polygon bucket for       \
+ *   current pixel row        |
+ *      |                     |
+ *      | activate new edges  |  Repeat GRID_Y times if we
+ *      V                     \  are supersampling this row,
+ *   active list              /  or just once if we're computing
+ *      |                     |  analytical coverage.
+ *      | coverage deltas     |
+ *      V                     |
+ *   pixel coverage list     /
+ *      |
+ *      V
+ *   coverage blitter
+ */
+#include "cairoint.h"
+#include "cairo-spans-private.h"
+#include "cairo-error-private.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <setjmp.h>
+
+/*-------------------------------------------------------------------------
+ * cairo specific config
+ */
+#define I static
+
+/* Prefer cairo's status type. */
+#define GLITTER_HAVE_STATUS_T 1
+#define GLITTER_STATUS_SUCCESS CAIRO_STATUS_SUCCESS
+#define GLITTER_STATUS_NO_MEMORY CAIRO_STATUS_NO_MEMORY
+typedef cairo_status_t glitter_status_t;
+
+/* The input coordinate scale and the rasterisation grid scales. */
+#define GLITTER_INPUT_BITS CAIRO_FIXED_FRAC_BITS
+//#define GRID_X_BITS CAIRO_FIXED_FRAC_BITS
+//#define GRID_Y 15
+#define GRID_X_BITS 2
+#define GRID_Y_BITS 2
+
+/* Set glitter up to use a cairo span renderer to do the coverage
+ * blitting. */
+struct pool;
+struct cell_list;
+
+/*-------------------------------------------------------------------------
+ * glitter-paths.h
+ */
+
+/* "Input scaled" numbers are fixed precision reals with multiplier
+ * 2**GLITTER_INPUT_BITS.  Input coordinates are given to glitter as
+ * pixel scaled numbers.  These get converted to the internal grid
+ * scaled numbers as soon as possible. Internal overflow is possible
+ * if GRID_X/Y inside glitter-paths.c is larger than
+ * 1<<GLITTER_INPUT_BITS. */
+#ifndef GLITTER_INPUT_BITS
+#  define GLITTER_INPUT_BITS 8
+#endif
+#define GLITTER_INPUT_SCALE (1<<GLITTER_INPUT_BITS)
+typedef int glitter_input_scaled_t;
+
+#if !GLITTER_HAVE_STATUS_T
+typedef enum {
+    GLITTER_STATUS_SUCCESS = 0,
+    GLITTER_STATUS_NO_MEMORY
+} glitter_status_t;
+#endif
+
+#ifndef I
+# define I /*static*/
+#endif
+
+/* Opaque type for scan converting. */
+typedef struct glitter_scan_converter glitter_scan_converter_t;
+
+/* Reset a scan converter to accept polygon edges and set the clip box
+ * in pixels.  Allocates O(ymax-ymin) bytes of memory. The clip box
+ * is set to integer pixel coordinates xmin <= x < xmax, ymin <= y <
+ * ymax. */
+I glitter_status_t
+glitter_scan_converter_reset(
+    glitter_scan_converter_t *converter,
+    int xmin, int ymin,
+    int xmax, int ymax);
+
+/* Render the polygon in the scan converter to the given A8 format
+ * image raster.  Only the pixels accessible as pixels[y*stride+x] for
+ * x,y inside the clip box are written to, where xmin <= x < xmax,
+ * ymin <= y < ymax.  The image is assumed to be clear on input.
+ *
+ * If nonzero_fill is true then the interior of the polygon is
+ * computed with the non-zero fill rule.  Otherwise the even-odd fill
+ * rule is used.
+ *
+ * The scan converter must be reset or destroyed after this call. */
+
+/*-------------------------------------------------------------------------
+ * glitter-paths.c: Implementation internal types
+ */
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+
+/* All polygon coordinates are snapped onto a subsample grid. "Grid
+ * scaled" numbers are fixed precision reals with multiplier GRID_X or
+ * GRID_Y. */
+typedef int grid_scaled_t;
+typedef int grid_scaled_x_t;
+typedef int grid_scaled_y_t;
+
+/* Default x/y scale factors.
+ *  You can either define GRID_X/Y_BITS to get a power-of-two scale
+ *  or define GRID_X/Y separately. */
+#if !defined(GRID_X) && !defined(GRID_X_BITS)
+#  define GRID_X_BITS 8
+#endif
+#if !defined(GRID_Y) && !defined(GRID_Y_BITS)
+#  define GRID_Y 15
+#endif
+
+/* Use GRID_X/Y_BITS to define GRID_X/Y if they're available. */
+#ifdef GRID_X_BITS
+#  define GRID_X (1 << GRID_X_BITS)
+#endif
+#ifdef GRID_Y_BITS
+#  define GRID_Y (1 << GRID_Y_BITS)
+#endif
+
+/* The GRID_X_TO_INT_FRAC macro splits a grid scaled coordinate into
+ * integer and fractional parts. The integer part is floored. */
+#if defined(GRID_X_TO_INT_FRAC)
+  /* do nothing */
+#elif defined(GRID_X_BITS)
+#  define GRID_X_TO_INT_FRAC(x, i, f) \
+       _GRID_TO_INT_FRAC_shift(x, i, f, GRID_X_BITS)
+#else
+#  define GRID_X_TO_INT_FRAC(x, i, f) \
+       _GRID_TO_INT_FRAC_general(x, i, f, GRID_X)
+#endif
+
+#define _GRID_TO_INT_FRAC_general(t, i, f, m) do {     \
+    (i) = (t) / (m);                                   \
+    (f) = (t) % (m);                                   \
+    if ((f) < 0) {                                     \
+       --(i);                                          \
+       (f) += (m);                                     \
+    }                                                  \
+} while (0)
+
+#define _GRID_TO_INT_FRAC_shift(t, i, f, b) do {       \
+    (f) = (t) & ((1 << (b)) - 1);                      \
+    (i) = (t) >> (b);                                  \
+} while (0)
+
+/* A grid area is a real in [0,1] scaled by 2*GRID_X*GRID_Y.  We want
+ * to be able to represent exactly areas of subpixel trapezoids whose
+ * vertices are given in grid scaled coordinates.  The scale factor
+ * comes from needing to accurately represent the area 0.5*dx*dy of a
+ * triangle with base dx and height dy in grid scaled numbers. */
+#define GRID_XY (2*GRID_X*GRID_Y) /* Unit area on the grid. */
+
+/* GRID_AREA_TO_ALPHA(area): map [0,GRID_XY] to [0,255]. */
+#if GRID_XY == 510
+#  define GRID_AREA_TO_ALPHA(c)          (((c)+1) >> 1)
+#elif GRID_XY == 255
+#  define  GRID_AREA_TO_ALPHA(c)  (c)
+#elif GRID_XY == 64
+#  define  GRID_AREA_TO_ALPHA(c)  (((c) << 2) | -(((c) & 0x40) >> 6))
+#elif GRID_XY == 128
+#  define  GRID_AREA_TO_ALPHA(c)  ((((c) << 1) | -((c) >> 7)) & 255)
+#elif GRID_XY == 256
+#  define  GRID_AREA_TO_ALPHA(c)  (((c) | -((c) >> 8)) & 255)
+#elif GRID_XY == 15
+#  define  GRID_AREA_TO_ALPHA(c)  (((c) << 4) + (c))
+#elif GRID_XY == 2*256*15
+#  define  GRID_AREA_TO_ALPHA(c)  (((c) + ((c)<<4) + 256) >> 9)
+#else
+#  define  GRID_AREA_TO_ALPHA(c)  (((c)*255 + GRID_XY/2) / GRID_XY)
+#endif
+
+#define UNROLL3(x) x x x
+
+struct quorem {
+    int32_t quo;
+    int32_t rem;
+};
+
+/* Header for a chunk of memory in a memory pool. */
+struct _pool_chunk {
+    /* # bytes used in this chunk. */
+    size_t size;
+
+    /* # bytes total in this chunk */
+    size_t capacity;
+
+    /* Pointer to the previous chunk or %NULL if this is the sentinel
+     * chunk in the pool header. */
+    struct _pool_chunk *prev_chunk;
+
+    /* Actual data starts here.         Well aligned for pointers. */
+};
+
+/* A memory pool.  This is supposed to be embedded on the stack or
+ * within some other structure.         It may optionally be followed by an
+ * embedded array from which requests are fulfilled until
+ * malloc needs to be called to allocate a first real chunk. */
+struct pool {
+    /* Chunk we're allocating from. */
+    struct _pool_chunk *current;
+
+    jmp_buf *jmp;
+
+    /* Free list of previously allocated chunks.  All have >= default
+     * capacity. */
+    struct _pool_chunk *first_free;
+
+    /* The default capacity of a chunk. */
+    size_t default_capacity;
+
+    /* Header for the sentinel chunk.  Directly following the pool
+     * struct should be some space for embedded elements from which
+     * the sentinel chunk allocates from. */
+    struct _pool_chunk sentinel[1];
+};
+
+/* A polygon edge. */
+struct edge {
+    /* Next in y-bucket or active list. */
+    struct edge *next, *prev;
+
+    /* Number of subsample rows remaining to scan convert of this
+     * edge. */
+    grid_scaled_y_t height_left;
+
+    /* Original sign of the edge: +1 for downwards, -1 for upwards
+     * edges.  */
+    int dir;
+    int vertical;
+
+    /* Current x coordinate while the edge is on the active
+     * list. Initialised to the x coordinate of the top of the
+     * edge. The quotient is in grid_scaled_x_t units and the
+     * remainder is mod dy in grid_scaled_y_t units.*/
+    struct quorem x;
+
+    /* Advance of the current x when moving down a subsample line. */
+    struct quorem dxdy;
+
+    /* The clipped y of the top of the edge. */
+    grid_scaled_y_t ytop;
+
+    /* y2-y1 after orienting the edge downwards.  */
+    grid_scaled_y_t dy;
+};
+
+#define EDGE_Y_BUCKET_INDEX(y, ymin) (((y) - (ymin))/GRID_Y)
+
+/* A collection of sorted and vertically clipped edges of the polygon.
+ * Edges are moved from the polygon to an active list while scan
+ * converting. */
+struct polygon {
+    /* The vertical clip extents. */
+    grid_scaled_y_t ymin, ymax;
+
+    /* Array of edges all starting in the same bucket. An edge is put
+     * into bucket EDGE_BUCKET_INDEX(edge->ytop, polygon->ymin) when
+     * it is added to the polygon. */
+    struct edge **y_buckets;
+    struct edge *y_buckets_embedded[64];
+
+    struct {
+       struct pool base[1];
+       struct edge embedded[32];
+    } edge_pool;
+};
+
+/* A cell records the effect on pixel coverage of polygon edges
+ * passing through a pixel.  It contains two accumulators of pixel
+ * coverage.
+ *
+ * Consider the effects of a polygon edge on the coverage of a pixel
+ * it intersects and that of the following one.  The coverage of the
+ * following pixel is the height of the edge multiplied by the width
+ * of the pixel, and the coverage of the pixel itself is the area of
+ * the trapezoid formed by the edge and the right side of the pixel.
+ *
+ * +-----------------------+-----------------------+
+ * |                       |                       |
+ * |                       |                       |
+ * |_______________________|_______________________|
+ * |   \...................|.......................|\
+ * |    \..................|.......................| |
+ * |     \.................|.......................| |
+ * |      \....covered.....|.......................| |
+ * |       \....area.......|.......................| } covered height
+ * |        \..............|.......................| |
+ * |uncovered\.............|.......................| |
+ * |  area    \............|.......................| |
+ * |___________\...........|.......................|/
+ * |                       |                       |
+ * |                       |                       |
+ * |                       |                       |
+ * +-----------------------+-----------------------+
+ *
+ * Since the coverage of the following pixel will always be a multiple
+ * of the width of the pixel, we can store the height of the covered
+ * area instead.  The coverage of the pixel itself is the total
+ * coverage minus the area of the uncovered area to the left of the
+ * edge.  As it's faster to compute the uncovered area we only store
+ * that and subtract it from the total coverage later when forming
+ * spans to blit.
+ *
+ * The heights and areas are signed, with left edges of the polygon
+ * having positive sign and right edges having negative sign.  When
+ * two edges intersect they swap their left/rightness so their
+ * contribution above and below the intersection point must be
+ * computed separately. */
+struct cell {
+    struct cell                *next;
+    int                         x;
+    int16_t             uncovered_area;
+    int16_t             covered_height;
+};
+
+/* A cell list represents the scan line sparsely as cells ordered by
+ * ascending x.  It is geared towards scanning the cells in order
+ * using an internal cursor. */
+struct cell_list {
+    /* Sentinel nodes */
+    struct cell head, tail;
+
+    /* Cursor state for iterating through the cell list. */
+    struct cell *cursor, *rewind;
+
+    /* Cells in the cell list are owned by the cell list and are
+     * allocated from this pool.  */
+    struct {
+       struct pool base[1];
+       struct cell embedded[32];
+    } cell_pool;
+};
+
+struct cell_pair {
+    struct cell *cell1;
+    struct cell *cell2;
+};
+
+/* The active list contains edges in the current scan line ordered by
+ * the x-coordinate of the intercept of the edge and the scan line. */
+struct active_list {
+    /* Leftmost edge on the current scan line. */
+    struct edge head, tail;
+
+    /* A lower bound on the height of the active edges is used to
+     * estimate how soon some active edge ends.         We can't advance the
+     * scan conversion by a full pixel row if an edge ends somewhere
+     * within it. */
+    grid_scaled_y_t min_height;
+    int is_vertical;
+};
+
+struct glitter_scan_converter {
+    struct polygon     polygon[1];
+    struct active_list active[1];
+    struct cell_list   coverages[1];
+
+    cairo_half_open_span_t *spans;
+    cairo_half_open_span_t spans_embedded[64];
+
+    /* Clip box. */
+    grid_scaled_x_t xmin, xmax;
+    grid_scaled_y_t ymin, ymax;
+};
+
+/* Compute the floored division a/b. Assumes / and % perform symmetric
+ * division. */
+inline static struct quorem
+floored_divrem(int a, int b)
+{
+    struct quorem qr;
+    qr.quo = a/b;
+    qr.rem = a%b;
+    if ((a^b)<0 && qr.rem) {
+       qr.quo -= 1;
+       qr.rem += b;
+    }
+    return qr;
+}
+
+/* Compute the floored division (x*a)/b. Assumes / and % perform symmetric
+ * division. */
+static struct quorem
+floored_muldivrem(int x, int a, int b)
+{
+    struct quorem qr;
+    long long xa = (long long)x*a;
+    qr.quo = xa/b;
+    qr.rem = xa%b;
+    if ((xa>=0) != (b>=0) && qr.rem) {
+       qr.quo -= 1;
+       qr.rem += b;
+    }
+    return qr;
+}
+
+static struct _pool_chunk *
+_pool_chunk_init(
+    struct _pool_chunk *p,
+    struct _pool_chunk *prev_chunk,
+    size_t capacity)
+{
+    p->prev_chunk = prev_chunk;
+    p->size = 0;
+    p->capacity = capacity;
+    return p;
+}
+
+static struct _pool_chunk *
+_pool_chunk_create(struct pool *pool, size_t size)
+{
+    struct _pool_chunk *p;
+
+    p = malloc(size + sizeof(struct _pool_chunk));
+    if (unlikely (NULL == p))
+       longjmp (*pool->jmp, _cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+    return _pool_chunk_init(p, pool->current, size);
+}
+
+static void
+pool_init(struct pool *pool,
+         jmp_buf *jmp,
+         size_t default_capacity,
+         size_t embedded_capacity)
+{
+    pool->jmp = jmp;
+    pool->current = pool->sentinel;
+    pool->first_free = NULL;
+    pool->default_capacity = default_capacity;
+    _pool_chunk_init(pool->sentinel, NULL, embedded_capacity);
+}
+
+static void
+pool_fini(struct pool *pool)
+{
+    struct _pool_chunk *p = pool->current;
+    do {
+       while (NULL != p) {
+           struct _pool_chunk *prev = p->prev_chunk;
+           if (p != pool->sentinel)
+               free(p);
+           p = prev;
+       }
+       p = pool->first_free;
+       pool->first_free = NULL;
+    } while (NULL != p);
+}
+
+/* Satisfy an allocation by first allocating a new large enough chunk
+ * and adding it to the head of the pool's chunk list. This function
+ * is called as a fallback if pool_alloc() couldn't do a quick
+ * allocation from the current chunk in the pool. */
+static void *
+_pool_alloc_from_new_chunk(
+    struct pool *pool,
+    size_t size)
+{
+    struct _pool_chunk *chunk;
+    void *obj;
+    size_t capacity;
+
+    /* If the allocation is smaller than the default chunk size then
+     * try getting a chunk off the free list.  Force alloc of a new
+     * chunk for large requests. */
+    capacity = size;
+    chunk = NULL;
+    if (size < pool->default_capacity) {
+       capacity = pool->default_capacity;
+       chunk = pool->first_free;
+       if (chunk) {
+           pool->first_free = chunk->prev_chunk;
+           _pool_chunk_init(chunk, pool->current, chunk->capacity);
+       }
+    }
+
+    if (NULL == chunk)
+       chunk = _pool_chunk_create (pool, capacity);
+    pool->current = chunk;
+
+    obj = ((unsigned char*)chunk + sizeof(*chunk) + chunk->size);
+    chunk->size += size;
+    return obj;
+}
+
+/* Allocate size bytes from the pool.  The first allocated address
+ * returned from a pool is aligned to sizeof(void*).  Subsequent
+ * addresses will maintain alignment as long as multiples of void* are
+ * allocated.  Returns the address of a new memory area or %NULL on
+ * allocation failures.         The pool retains ownership of the returned
+ * memory. */
+inline static void *
+pool_alloc (struct pool *pool, size_t size)
+{
+    struct _pool_chunk *chunk = pool->current;
+
+    if (size <= chunk->capacity - chunk->size) {
+       void *obj = ((unsigned char*)chunk + sizeof(*chunk) + chunk->size);
+       chunk->size += size;
+       return obj;
+    } else {
+       return _pool_alloc_from_new_chunk(pool, size);
+    }
+}
+
+/* Relinquish all pool_alloced memory back to the pool. */
+static void
+pool_reset (struct pool *pool)
+{
+    /* Transfer all used chunks to the chunk free list. */
+    struct _pool_chunk *chunk = pool->current;
+    if (chunk != pool->sentinel) {
+       while (chunk->prev_chunk != pool->sentinel) {
+           chunk = chunk->prev_chunk;
+       }
+       chunk->prev_chunk = pool->first_free;
+       pool->first_free = pool->current;
+    }
+    /* Reset the sentinel as the current chunk. */
+    pool->current = pool->sentinel;
+    pool->sentinel->size = 0;
+}
+
+/* Rewinds the cell list's cursor to the beginning.  After rewinding
+ * we're good to cell_list_find() the cell any x coordinate. */
+inline static void
+cell_list_rewind (struct cell_list *cells)
+{
+    cells->cursor = &cells->head;
+}
+
+inline static void
+cell_list_maybe_rewind (struct cell_list *cells, int x)
+{
+    if (x < cells->cursor->x) {
+       cells->cursor = cells->rewind;
+       if (x < cells->cursor->x)
+           cells->cursor = &cells->head;
+    }
+}
+
+inline static void
+cell_list_set_rewind (struct cell_list *cells)
+{
+    cells->rewind = cells->cursor;
+}
+
+static void
+cell_list_init(struct cell_list *cells, jmp_buf *jmp)
+{
+    pool_init(cells->cell_pool.base, jmp,
+             256*sizeof(struct cell),
+             sizeof(cells->cell_pool.embedded));
+    cells->tail.next = NULL;
+    cells->tail.x = INT_MAX;
+    cells->head.x = INT_MIN;
+    cells->head.next = &cells->tail;
+    cell_list_rewind (cells);
+}
+
+static void
+cell_list_fini(struct cell_list *cells)
+{
+    pool_fini (cells->cell_pool.base);
+}
+
+/* Empty the cell list.  This is called at the start of every pixel
+ * row. */
+inline static void
+cell_list_reset (struct cell_list *cells)
+{
+    cell_list_rewind (cells);
+    cells->head.next = &cells->tail;
+    pool_reset (cells->cell_pool.base);
+}
+
+inline static struct cell *
+cell_list_alloc (struct cell_list *cells,
+                struct cell *tail,
+                int x)
+{
+    struct cell *cell;
+
+    cell = pool_alloc (cells->cell_pool.base, sizeof (struct cell));
+    cell->next = tail->next;
+    tail->next = cell;
+    cell->x = x;
+    *(uint32_t *)&cell->uncovered_area = 0;
+
+    return cell;
+}
+
+/* Find a cell at the given x-coordinate.  Returns %NULL if a new cell
+ * needed to be allocated but couldn't be.  Cells must be found with
+ * non-decreasing x-coordinate until the cell list is rewound using
+ * cell_list_rewind(). Ownership of the returned cell is retained by
+ * the cell list. */
+inline static struct cell *
+cell_list_find (struct cell_list *cells, int x)
+{
+    struct cell *tail = cells->cursor;
+
+    if (tail->x == x)
+       return tail;
+
+    while (1) {
+       UNROLL3({
+               if (tail->next->x > x)
+                       break;
+               tail = tail->next;
+       });
+    }
+
+    if (tail->x != x)
+       tail = cell_list_alloc (cells, tail, x);
+    return cells->cursor = tail;
+
+}
+
+/* Find two cells at x1 and x2.         This is exactly equivalent
+ * to
+ *
+ *   pair.cell1 = cell_list_find(cells, x1);
+ *   pair.cell2 = cell_list_find(cells, x2);
+ *
+ * except with less function call overhead. */
+inline static struct cell_pair
+cell_list_find_pair(struct cell_list *cells, int x1, int x2)
+{
+    struct cell_pair pair;
+
+    pair.cell1 = cells->cursor;
+    while (1) {
+       UNROLL3({
+               if (pair.cell1->next->x > x1)
+                       break;
+               pair.cell1 = pair.cell1->next;
+       });
+    }
+    if (pair.cell1->x != x1)
+       pair.cell1 = cell_list_alloc (cells, pair.cell1, x1);
+
+    pair.cell2 = pair.cell1;
+    while (1) {
+       UNROLL3({
+               if (pair.cell2->next->x > x2)
+                       break;
+               pair.cell2 = pair.cell2->next;
+       });
+    }
+    if (pair.cell2->x != x2)
+       pair.cell2 = cell_list_alloc (cells, pair.cell2, x2);
+
+    cells->cursor = pair.cell2;
+    return pair;
+}
+
+/* Add a subpixel span covering [x1, x2) to the coverage cells. */
+inline static void
+cell_list_add_subspan(struct cell_list *cells,
+                     grid_scaled_x_t x1,
+                     grid_scaled_x_t x2)
+{
+    int ix1, fx1;
+    int ix2, fx2;
+
+    if (x1 == x2)
+       return;
+
+    GRID_X_TO_INT_FRAC(x1, ix1, fx1);
+    GRID_X_TO_INT_FRAC(x2, ix2, fx2);
+
+    if (ix1 != ix2) {
+       struct cell_pair p;
+       p = cell_list_find_pair(cells, ix1, ix2);
+       p.cell1->uncovered_area += 2*fx1;
+       ++p.cell1->covered_height;
+       p.cell2->uncovered_area -= 2*fx2;
+       --p.cell2->covered_height;
+    } else {
+       struct cell *cell = cell_list_find(cells, ix1);
+       cell->uncovered_area += 2*(fx1-fx2);
+    }
+}
+
+/* Adds the analytical coverage of an edge crossing the current pixel
+ * row to the coverage cells and advances the edge's x position to the
+ * following row.
+ *
+ * This function is only called when we know that during this pixel row:
+ *
+ * 1) The relative order of all edges on the active list doesn't
+ * change.  In particular, no edges intersect within this row to pixel
+ * precision.
+ *
+ * 2) No new edges start in this row.
+ *
+ * 3) No existing edges end mid-row.
+ *
+ * This function depends on being called with all edges from the
+ * active list in the order they appear on the list (i.e. with
+ * non-decreasing x-coordinate.)  */
+static void
+cell_list_render_edge(struct cell_list *cells,
+                     struct edge *edge,
+                     int sign)
+{
+    grid_scaled_x_t fx;
+    struct cell *cell;
+    int ix;
+
+    GRID_X_TO_INT_FRAC(edge->x.quo, ix, fx);
+
+    /* We always know that ix1 is >= the cell list cursor in this
+     * case due to the no-intersections precondition.  */
+    cell = cell_list_find(cells, ix);
+    cell->covered_height += sign*GRID_Y;
+    cell->uncovered_area += sign*2*fx*GRID_Y;
+}
+
+static void
+polygon_init (struct polygon *polygon, jmp_buf *jmp)
+{
+    polygon->ymin = polygon->ymax = 0;
+    polygon->y_buckets = polygon->y_buckets_embedded;
+    pool_init (polygon->edge_pool.base, jmp,
+              8192 - sizeof (struct _pool_chunk),
+              sizeof (polygon->edge_pool.embedded));
+}
+
+static void
+polygon_fini (struct polygon *polygon)
+{
+    if (polygon->y_buckets != polygon->y_buckets_embedded)
+       free (polygon->y_buckets);
+
+    pool_fini (polygon->edge_pool.base);
+}
+
+/* Empties the polygon of all edges. The polygon is then prepared to
+ * receive new edges and clip them to the vertical range
+ * [ymin,ymax). */
+static glitter_status_t
+polygon_reset (struct polygon *polygon,
+              grid_scaled_y_t ymin,
+              grid_scaled_y_t ymax)
+{
+    unsigned h = ymax - ymin;
+    unsigned num_buckets = EDGE_Y_BUCKET_INDEX(ymax + GRID_Y-1, ymin);
+
+    pool_reset(polygon->edge_pool.base);
+
+    if (unlikely (h > 0x7FFFFFFFU - GRID_Y))
+       goto bail_no_mem; /* even if you could, you wouldn't want to. */
+
+    if (polygon->y_buckets != polygon->y_buckets_embedded)
+       free (polygon->y_buckets);
+
+    polygon->y_buckets =  polygon->y_buckets_embedded;
+    if (num_buckets > ARRAY_LENGTH (polygon->y_buckets_embedded)) {
+       polygon->y_buckets = _cairo_malloc_ab (num_buckets,
+                                              sizeof (struct edge *));
+       if (unlikely (NULL == polygon->y_buckets))
+           goto bail_no_mem;
+    }
+    memset (polygon->y_buckets, 0, num_buckets * sizeof (struct edge *));
+
+    polygon->ymin = ymin;
+    polygon->ymax = ymax;
+    return GLITTER_STATUS_SUCCESS;
+
+bail_no_mem:
+    polygon->ymin = 0;
+    polygon->ymax = 0;
+    return GLITTER_STATUS_NO_MEMORY;
+}
+
+static void
+_polygon_insert_edge_into_its_y_bucket(struct polygon *polygon,
+                                      struct edge *e)
+{
+    unsigned ix = EDGE_Y_BUCKET_INDEX(e->ytop, polygon->ymin);
+    struct edge **ptail = &polygon->y_buckets[ix];
+    e->next = *ptail;
+    *ptail = e;
+}
+
+inline static void
+polygon_add_edge (struct polygon *polygon,
+                 const cairo_edge_t *edge)
+{
+    struct edge *e;
+    grid_scaled_x_t dx;
+    grid_scaled_y_t dy;
+    grid_scaled_y_t ytop, ybot;
+    grid_scaled_y_t ymin = polygon->ymin;
+    grid_scaled_y_t ymax = polygon->ymax;
+
+    if (unlikely (edge->top >= ymax || edge->bottom <= ymin))
+       return;
+
+    e = pool_alloc (polygon->edge_pool.base, sizeof (struct edge));
+
+    dx = edge->line.p2.x - edge->line.p1.x;
+    dy = edge->line.p2.y - edge->line.p1.y;
+    e->dy = dy;
+    e->dir = edge->dir;
+
+    ytop = edge->top >= ymin ? edge->top : ymin;
+    ybot = edge->bottom <= ymax ? edge->bottom : ymax;
+    e->ytop = ytop;
+    e->height_left = ybot - ytop;
+
+    if (dx == 0) {
+       e->vertical = TRUE;
+       e->x.quo = edge->line.p1.x;
+       e->x.rem = 0;
+       e->dxdy.quo = 0;
+       e->dxdy.rem = 0;
+    } else {
+       e->vertical = FALSE;
+       e->dxdy = floored_divrem (dx, dy);
+       if (ytop == edge->line.p1.y) {
+           e->x.quo = edge->line.p1.x;
+           e->x.rem = 0;
+       } else {
+           e->x = floored_muldivrem (ytop - edge->line.p1.y, dx, dy);
+           e->x.quo += edge->line.p1.x;
+       }
+    }
+
+    _polygon_insert_edge_into_its_y_bucket (polygon, e);
+
+    e->x.rem -= dy;            /* Bias the remainder for faster
+                                * edge advancement. */
+}
+
+static void
+active_list_reset (struct active_list *active)
+{
+    active->head.vertical = 1;
+    active->head.height_left = INT_MAX;
+    active->head.x.quo = INT_MIN;
+    active->head.prev = NULL;
+    active->head.next = &active->tail;
+    active->tail.prev = &active->head;
+    active->tail.next = NULL;
+    active->tail.x.quo = INT_MAX;
+    active->tail.height_left = INT_MAX;
+    active->tail.vertical = 1;
+    active->min_height = 0;
+    active->is_vertical = 1;
+}
+
+static void
+active_list_init(struct active_list *active)
+{
+    active_list_reset(active);
+}
+
+/*
+ * Merge two sorted edge lists.
+ * Input:
+ *  - head_a: The head of the first list.
+ *  - head_b: The head of the second list; head_b cannot be NULL.
+ * Output:
+ * Returns the head of the merged list.
+ *
+ * Implementation notes:
+ * To make it fast (in particular, to reduce to an insertion sort whenever
+ * one of the two input lists only has a single element) we iterate through
+ * a list until its head becomes greater than the head of the other list,
+ * then we switch their roles. As soon as one of the two lists is empty, we
+ * just attach the other one to the current list and exit.
+ * Writes to memory are only needed to "switch" lists (as it also requires
+ * attaching to the output list the list which we will be iterating next) and
+ * to attach the last non-empty list.
+ */
+static struct edge *
+merge_sorted_edges (struct edge *head_a, struct edge *head_b)
+{
+    struct edge *head, **next, *prev;
+    int32_t x;
+
+    prev = head_a->prev;
+    next = &head;
+    if (head_a->x.quo <= head_b->x.quo) {
+       head = head_a;
+    } else {
+       head = head_b;
+       head_b->prev = prev;
+       goto start_with_b;
+    }
+
+    do {
+       x = head_b->x.quo;
+       while (head_a != NULL && head_a->x.quo <= x) {
+           prev = head_a;
+           next = &head_a->next;
+           head_a = head_a->next;
+       }
+
+       head_b->prev = prev;
+       *next = head_b;
+       if (head_a == NULL)
+           return head;
+
+start_with_b:
+       x = head_a->x.quo;
+       while (head_b != NULL && head_b->x.quo <= x) {
+           prev = head_b;
+           next = &head_b->next;
+           head_b = head_b->next;
+       }
+
+       head_a->prev = prev;
+       *next = head_a;
+       if (head_b == NULL)
+           return head;
+    } while (1);
+}
+
+/*
+ * Sort (part of) a list.
+ * Input:
+ *  - list: The list to be sorted; list cannot be NULL.
+ *  - limit: Recursion limit.
+ * Output:
+ *  - head_out: The head of the sorted list containing the first 2^(level+1) elements of the
+ *              input list; if the input list has fewer elements, head_out be a sorted list
+ *              containing all the elements of the input list.
+ * Returns the head of the list of unprocessed elements (NULL if the sorted list contains
+ * all the elements of the input list).
+ *
+ * Implementation notes:
+ * Special case single element list, unroll/inline the sorting of the first two elements.
+ * Some tail recursion is used since we iterate on the bottom-up solution of the problem
+ * (we start with a small sorted list and keep merging other lists of the same size to it).
+ */
+static struct edge *
+sort_edges (struct edge *list,
+           unsigned int level,
+           struct edge **head_out)
+{
+    struct edge *head_other, *remaining;
+    unsigned int i;
+
+    head_other = list->next;
+
+    if (head_other == NULL) {
+       *head_out = list;
+       return NULL;
+    }
+
+    remaining = head_other->next;
+    if (list->x.quo <= head_other->x.quo) {
+       *head_out = list;
+       head_other->next = NULL;
+    } else {
+       *head_out = head_other;
+       head_other->prev = list->prev;
+       head_other->next = list;
+       list->prev = head_other;
+       list->next = NULL;
+    }
+
+    for (i = 0; i < level && remaining; i++) {
+       remaining = sort_edges (remaining, i, &head_other);
+       *head_out = merge_sorted_edges (*head_out, head_other);
+    }
+
+    return remaining;
+}
+
+static struct edge *
+merge_unsorted_edges (struct edge *head, struct edge *unsorted)
+{
+    sort_edges (unsorted, UINT_MAX, &unsorted);
+    return merge_sorted_edges (head, unsorted);
+}
+
+/* Test if the edges on the active list can be safely advanced by a
+ * full row without intersections or any edges ending. */
+inline static int
+can_do_full_row (struct active_list *active)
+{
+    const struct edge *e;
+
+    /* Recomputes the minimum height of all edges on the active
+     * list if we have been dropping edges. */
+    if (active->min_height <= 0) {
+       int min_height = INT_MAX;
+       int is_vertical = 1;
+
+       e = active->head.next;
+       while (NULL != e) {
+           if (e->height_left < min_height)
+               min_height = e->height_left;
+           is_vertical &= e->vertical;
+           e = e->next;
+       }
+
+       active->is_vertical = is_vertical;
+       active->min_height = min_height;
+    }
+
+    if (active->min_height < GRID_Y)
+       return 0;
+
+    return active->is_vertical;
+}
+
+/* Merges edges on the given subpixel row from the polygon to the
+ * active_list. */
+inline static void
+active_list_merge_edges_from_bucket(struct active_list *active,
+                                   struct edge *edges)
+{
+    active->head.next = merge_unsorted_edges (active->head.next, edges);
+}
+
+inline static void
+polygon_fill_buckets (struct active_list *active,
+                     struct edge *edge,
+                     int y,
+                     struct edge **buckets)
+{
+    grid_scaled_y_t min_height = active->min_height;
+    int is_vertical = active->is_vertical;
+
+    while (edge) {
+       struct edge *next = edge->next;
+       int suby = edge->ytop - y;
+       if (buckets[suby])
+           buckets[suby]->prev = edge;
+       edge->next = buckets[suby];
+       edge->prev = NULL;
+       buckets[suby] = edge;
+       if (edge->height_left < min_height)
+           min_height = edge->height_left;
+       is_vertical &= edge->vertical;
+       edge = next;
+    }
+
+    active->is_vertical = is_vertical;
+    active->min_height = min_height;
+}
+
+inline static void
+sub_row (struct active_list *active,
+        struct cell_list *coverages,
+        unsigned int mask)
+{
+    struct edge *edge = active->head.next;
+    int xstart = INT_MIN, prev_x = INT_MIN;
+    int winding = 0;
+
+    cell_list_rewind (coverages);
+
+    while (&active->tail != edge) {
+       struct edge *next = edge->next;
+       int xend = edge->x.quo;
+
+       if (--edge->height_left) {
+           edge->x.quo += edge->dxdy.quo;
+           edge->x.rem += edge->dxdy.rem;
+           if (edge->x.rem >= 0) {
+               ++edge->x.quo;
+               edge->x.rem -= edge->dy;
+           }
+
+           if (edge->x.quo < prev_x) {
+               struct edge *pos = edge->prev;
+               pos->next = next;
+               next->prev = pos;
+               do {
+                   pos = pos->prev;
+               } while (edge->x.quo < pos->x.quo);
+               pos->next->prev = edge;
+               edge->next = pos->next;
+               edge->prev = pos;
+               pos->next = edge;
+           } else
+               prev_x = edge->x.quo;
+       } else {
+           edge->prev->next = next;
+           next->prev = edge->prev;
+       }
+
+       winding += edge->dir;
+       if ((winding & mask) == 0) {
+           if (next->x.quo != xend) {
+               cell_list_add_subspan (coverages, xstart, xend);
+               xstart = INT_MIN;
+           }
+       } else if (xstart == INT_MIN)
+           xstart = xend;
+
+       edge = next;
+    }
+}
+
+inline static void dec (struct edge *e, int h)
+{
+    e->height_left -= h;
+    if (e->height_left == 0) {
+       e->prev->next = e->next;
+       e->next->prev = e->prev;
+    }
+}
+
+static void
+full_row (struct active_list *active,
+         struct cell_list *coverages,
+         unsigned int mask)
+{
+    struct edge *left = active->head.next;
+
+    while (&active->tail != left) {
+       struct edge *right;
+       int winding;
+
+       dec (left, GRID_Y);
+
+       winding = left->dir;
+       right = left->next;
+       do {
+           dec (right, GRID_Y);
+
+           winding += right->dir;
+           if ((winding & mask) == 0 && right->next->x.quo != right->x.quo)
+               break;
+
+           right = right->next;
+       } while (1);
+
+       cell_list_set_rewind (coverages);
+       cell_list_render_edge (coverages, left, +1);
+       cell_list_render_edge (coverages, right, -1);
+
+       left = right->next;
+    }
+}
+
+static void
+_glitter_scan_converter_init(glitter_scan_converter_t *converter, jmp_buf *jmp)
+{
+    polygon_init(converter->polygon, jmp);
+    active_list_init(converter->active);
+    cell_list_init(converter->coverages, jmp);
+    converter->xmin=0;
+    converter->ymin=0;
+    converter->xmax=0;
+    converter->ymax=0;
+}
+
+static void
+_glitter_scan_converter_fini(glitter_scan_converter_t *self)
+{
+    if (self->spans != self->spans_embedded)
+       free (self->spans);
+
+    polygon_fini(self->polygon);
+    cell_list_fini(self->coverages);
+
+    self->xmin=0;
+    self->ymin=0;
+    self->xmax=0;
+    self->ymax=0;
+}
+
+static grid_scaled_t
+int_to_grid_scaled(int i, int scale)
+{
+    /* Clamp to max/min representable scaled number. */
+    if (i >= 0) {
+       if (i >= INT_MAX/scale)
+           i = INT_MAX/scale;
+    }
+    else {
+       if (i <= INT_MIN/scale)
+           i = INT_MIN/scale;
+    }
+    return i*scale;
+}
+
+#define int_to_grid_scaled_x(x) int_to_grid_scaled((x), GRID_X)
+#define int_to_grid_scaled_y(x) int_to_grid_scaled((x), GRID_Y)
+
+I glitter_status_t
+glitter_scan_converter_reset(
+                            glitter_scan_converter_t *converter,
+                            int xmin, int ymin,
+                            int xmax, int ymax)
+{
+    glitter_status_t status;
+
+    converter->xmin = 0; converter->xmax = 0;
+    converter->ymin = 0; converter->ymax = 0;
+
+    if (xmax - xmin > ARRAY_LENGTH(converter->spans_embedded)) {
+       converter->spans = _cairo_malloc_ab (xmax - xmin,
+                                            sizeof (cairo_half_open_span_t));
+       if (unlikely (converter->spans == NULL))
+           return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+    } else
+       converter->spans = converter->spans_embedded;
+
+    xmin = int_to_grid_scaled_x(xmin);
+    ymin = int_to_grid_scaled_y(ymin);
+    xmax = int_to_grid_scaled_x(xmax);
+    ymax = int_to_grid_scaled_y(ymax);
+
+    active_list_reset(converter->active);
+    cell_list_reset(converter->coverages);
+    status = polygon_reset(converter->polygon, ymin, ymax);
+    if (status)
+       return status;
+
+    converter->xmin = xmin;
+    converter->xmax = xmax;
+    converter->ymin = ymin;
+    converter->ymax = ymax;
+    return GLITTER_STATUS_SUCCESS;
+}
+
+/* INPUT_TO_GRID_X/Y (in_coord, out_grid_scaled, grid_scale)
+ *   These macros convert an input coordinate in the client's
+ *   device space to the rasterisation grid.
+ */
+/* Gah.. this bit of ugly defines INPUT_TO_GRID_X/Y so as to use
+ * shifts if possible, and something saneish if not.
+ */
+#if !defined(INPUT_TO_GRID_Y) && defined(GRID_Y_BITS) && GRID_Y_BITS <= GLITTER_INPUT_BITS
+#  define INPUT_TO_GRID_Y(in, out) (out) = (in) >> (GLITTER_INPUT_BITS - GRID_Y_BITS)
+#else
+#  define INPUT_TO_GRID_Y(in, out) INPUT_TO_GRID_general(in, out, GRID_Y)
+#endif
+
+#if !defined(INPUT_TO_GRID_X) && defined(GRID_X_BITS) && GRID_X_BITS <= GLITTER_INPUT_BITS
+#  define INPUT_TO_GRID_X(in, out) (out) = (in) >> (GLITTER_INPUT_BITS - GRID_X_BITS)
+#else
+#  define INPUT_TO_GRID_X(in, out) INPUT_TO_GRID_general(in, out, GRID_X)
+#endif
+
+#define INPUT_TO_GRID_general(in, out, grid_scale) do {                \
+    long long tmp__ = (long long)(grid_scale) * (in);  \
+    tmp__ >>= GLITTER_INPUT_BITS;                              \
+    (out) = tmp__;                                             \
+} while (0)
+
+/* Add a new polygon edge from pixel (x1,y1) to (x2,y2) to the scan
+ * converter.  The coordinates represent pixel positions scaled by
+ * 2**GLITTER_PIXEL_BITS.  If this function fails then the scan
+ * converter should be reset or destroyed.  Dir must be +1 or -1,
+ * with the latter reversing the orientation of the edge. */
+I void
+glitter_scan_converter_add_edge (glitter_scan_converter_t *converter,
+                                const cairo_edge_t *edge)
+{
+    cairo_edge_t e;
+
+    INPUT_TO_GRID_Y (edge->top, e.top);
+    INPUT_TO_GRID_Y (edge->bottom, e.bottom);
+    if (e.top >= e.bottom)
+       return;
+
+    /* XXX: possible overflows if GRID_X/Y > 2**GLITTER_INPUT_BITS */
+    INPUT_TO_GRID_Y (edge->line.p1.y, e.line.p1.y);
+    INPUT_TO_GRID_Y (edge->line.p2.y, e.line.p2.y);
+    if (e.line.p1.y == e.line.p2.y)
+       return;
+
+    INPUT_TO_GRID_X (edge->line.p1.x, e.line.p1.x);
+    INPUT_TO_GRID_X (edge->line.p2.x, e.line.p2.x);
+
+    e.dir = edge->dir;
+
+    polygon_add_edge (converter->polygon, &e);
+}
+
+static void
+step_edges (struct active_list *active, int count)
+{
+    struct edge *edge;
+
+    count *= GRID_Y;
+    for (edge = active->head.next; edge != &active->tail; edge = edge->next) {
+       edge->height_left -= count;
+       if (! edge->height_left) {
+           edge->prev->next = edge->next;
+           edge->next->prev = edge->prev;
+       }
+    }
+}
+
+static glitter_status_t
+blit_a8 (struct cell_list *cells,
+        cairo_span_renderer_t *renderer,
+        cairo_half_open_span_t *spans,
+        int y, int height,
+        int xmin, int xmax)
+{
+    struct cell *cell = cells->head.next;
+    int prev_x = xmin, last_x = -1;
+    int16_t cover = 0, last_cover = 0;
+    unsigned num_spans;
+
+    if (cell == &cells->tail)
+       return CAIRO_STATUS_SUCCESS;
+
+    /* Skip cells to the left of the clip region. */
+    while (cell->x < xmin) {
+       cover += cell->covered_height;
+       cell = cell->next;
+    }
+    cover *= GRID_X*2;
+
+    /* Form the spans from the coverages and areas. */
+    num_spans = 0;
+    for (; cell->x < xmax; cell = cell->next) {
+       int x = cell->x;
+       int16_t area;
+
+       if (x > prev_x && cover != last_cover) {
+           spans[num_spans].x = prev_x;
+           spans[num_spans].coverage = GRID_AREA_TO_ALPHA (cover);
+           last_cover = cover;
+           last_x = prev_x;
+           ++num_spans;
+       }
+
+       cover += cell->covered_height*GRID_X*2;
+       area = cover - cell->uncovered_area;
+
+       if (area != last_cover) {
+           spans[num_spans].x = x;
+           spans[num_spans].coverage = GRID_AREA_TO_ALPHA (area);
+           last_cover = area;
+           last_x = x;
+           ++num_spans;
+       }
+
+       prev_x = x+1;
+    }
+
+    if (prev_x <= xmax && cover != last_cover) {
+       spans[num_spans].x = prev_x;
+       spans[num_spans].coverage = GRID_AREA_TO_ALPHA (cover);
+       last_cover = cover;
+       last_x = prev_x;
+       ++num_spans;
+    }
+
+    if (last_x < xmax && last_cover) {
+       spans[num_spans].x = xmax;
+       spans[num_spans].coverage = 0;
+       ++num_spans;
+    }
+
+    /* Dump them into the renderer. */
+    return renderer->render_rows (renderer, y, height, spans, num_spans);
+}
+
+#define GRID_AREA_TO_A1(A)  ((GRID_AREA_TO_ALPHA (A) > 127) ? 255 : 0)
+static glitter_status_t
+blit_a1 (struct cell_list *cells,
+        cairo_span_renderer_t *renderer,
+        cairo_half_open_span_t *spans,
+        int y, int height,
+        int xmin, int xmax)
+{
+    struct cell *cell = cells->head.next;
+    int prev_x = xmin, last_x = -1;
+    int16_t cover = 0;
+    uint8_t coverage, last_cover = 0;
+    unsigned num_spans;
+
+    if (cell == &cells->tail)
+       return CAIRO_STATUS_SUCCESS;
+
+    /* Skip cells to the left of the clip region. */
+    while (cell->x < xmin) {
+       cover += cell->covered_height;
+       cell = cell->next;
+    }
+    cover *= GRID_X*2;
+
+    /* Form the spans from the coverages and areas. */
+    num_spans = 0;
+    for (; cell->x < xmax; cell = cell->next) {
+       int x = cell->x;
+       int16_t area;
+
+       coverage = GRID_AREA_TO_A1 (cover);
+       if (x > prev_x && coverage != last_cover) {
+           last_x = spans[num_spans].x = prev_x;
+           last_cover = spans[num_spans].coverage = coverage;
+           ++num_spans;
+       }
+
+       cover += cell->covered_height*GRID_X*2;
+       area = cover - cell->uncovered_area;
+
+       coverage = GRID_AREA_TO_A1 (area);
+       if (coverage != last_cover) {
+           last_x = spans[num_spans].x = x;
+           last_cover = spans[num_spans].coverage = coverage;
+           ++num_spans;
+       }
+
+       prev_x = x+1;
+    }
+
+    coverage = GRID_AREA_TO_A1 (cover);
+    if (prev_x <= xmax && coverage != last_cover) {
+       last_x = spans[num_spans].x = prev_x;
+       last_cover = spans[num_spans].coverage = coverage;
+       ++num_spans;
+    }
+
+    if (last_x < xmax && last_cover) {
+       spans[num_spans].x = xmax;
+       spans[num_spans].coverage = 0;
+       ++num_spans;
+    }
+    if (num_spans == 1)
+       return CAIRO_STATUS_SUCCESS;
+
+    /* Dump them into the renderer. */
+    return renderer->render_rows (renderer, y, height, spans, num_spans);
+}
+
+
+I void
+glitter_scan_converter_render(glitter_scan_converter_t *converter,
+                             unsigned int winding_mask,
+                             int antialias,
+                             cairo_span_renderer_t *renderer)
+{
+    int i, j;
+    int ymax_i = converter->ymax / GRID_Y;
+    int ymin_i = converter->ymin / GRID_Y;
+    int xmin_i, xmax_i;
+    int h = ymax_i - ymin_i;
+    struct polygon *polygon = converter->polygon;
+    struct cell_list *coverages = converter->coverages;
+    struct active_list *active = converter->active;
+    struct edge *buckets[GRID_Y] = { 0 };
+
+    xmin_i = converter->xmin / GRID_X;
+    xmax_i = converter->xmax / GRID_X;
+    if (xmin_i >= xmax_i)
+       return;
+
+    /* Render each pixel row. */
+    for (i = 0; i < h; i = j) {
+       int do_full_row = 0;
+
+       j = i + 1;
+
+       /* Determine if we can ignore this row or use the full pixel
+        * stepper. */
+       if (! polygon->y_buckets[i]) {
+           if (active->head.next == &active->tail) {
+               active->min_height = INT_MAX;
+               active->is_vertical = 1;
+               for (; j < h && ! polygon->y_buckets[j]; j++)
+                   ;
+               continue;
+           }
+
+           do_full_row = can_do_full_row (active);
+       }
+
+       if (do_full_row) {
+           /* Step by a full pixel row's worth. */
+           full_row (active, coverages, winding_mask);
+
+           if (active->is_vertical) {
+               while (j < h &&
+                      polygon->y_buckets[j] == NULL &&
+                      active->min_height >= 2*GRID_Y)
+               {
+                   active->min_height -= GRID_Y;
+                   j++;
+               }
+               if (j != i + 1)
+                   step_edges (active, j - (i + 1));
+           }
+       } else {
+           int sub;
+
+           polygon_fill_buckets (active,
+                                 polygon->y_buckets[i],
+                                 (i+ymin_i)*GRID_Y,
+                                 buckets);
+
+           /* Subsample this row. */
+           for (sub = 0; sub < GRID_Y; sub++) {
+               if (buckets[sub]) {
+                   active_list_merge_edges_from_bucket (active, buckets[sub]);
+                   buckets[sub] = NULL;
+               }
+
+               sub_row (active, coverages, winding_mask);
+           }
+       }
+
+       if (antialias)
+           blit_a8 (coverages, renderer, converter->spans,
+                    i+ymin_i, j-i, xmin_i, xmax_i);
+       else
+           blit_a1 (coverages, renderer, converter->spans,
+                    i+ymin_i, j-i, xmin_i, xmax_i);
+       cell_list_reset (coverages);
+
+       active->min_height -= GRID_Y;
+    }
+}
+
+struct _cairo_tor22_scan_converter {
+    cairo_scan_converter_t base;
+
+    glitter_scan_converter_t converter[1];
+    cairo_fill_rule_t fill_rule;
+    cairo_antialias_t antialias;
+
+    jmp_buf jmp;
+};
+
+typedef struct _cairo_tor22_scan_converter cairo_tor22_scan_converter_t;
+
+static void
+_cairo_tor22_scan_converter_destroy (void *converter)
+{
+    cairo_tor22_scan_converter_t *self = converter;
+    if (self == NULL) {
+       return;
+    }
+    _glitter_scan_converter_fini (self->converter);
+    free(self);
+}
+
+cairo_status_t
+_cairo_tor22_scan_converter_add_polygon (void          *converter,
+                                      const cairo_polygon_t *polygon)
+{
+    cairo_tor22_scan_converter_t *self = converter;
+    int i;
+
+#if 0
+    FILE *file = fopen ("polygon.txt", "w");
+    _cairo_debug_print_polygon (file, polygon);
+    fclose (file);
+#endif
+
+    for (i = 0; i < polygon->num_edges; i++)
+        glitter_scan_converter_add_edge (self->converter, &polygon->edges[i]);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_tor22_scan_converter_generate (void                     *converter,
+                                   cairo_span_renderer_t       *renderer)
+{
+    cairo_tor22_scan_converter_t *self = converter;
+    cairo_status_t status;
+
+    if ((status = setjmp (self->jmp)))
+       return _cairo_scan_converter_set_error (self, _cairo_error (status));
+
+    glitter_scan_converter_render (self->converter,
+                                  self->fill_rule == CAIRO_FILL_RULE_WINDING ? ~0 : 1,
+                                  self->antialias != CAIRO_ANTIALIAS_NONE,
+                                  renderer);
+    return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_scan_converter_t *
+_cairo_tor22_scan_converter_create (int                        xmin,
+                                 int                   ymin,
+                                 int                   xmax,
+                                 int                   ymax,
+                                 cairo_fill_rule_t     fill_rule,
+                                 cairo_antialias_t     antialias)
+{
+    cairo_tor22_scan_converter_t *self;
+    cairo_status_t status;
+
+    self = malloc (sizeof(struct _cairo_tor22_scan_converter));
+    if (unlikely (self == NULL)) {
+       status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+       goto bail_nomem;
+    }
+
+    self->base.destroy = _cairo_tor22_scan_converter_destroy;
+    self->base.generate = _cairo_tor22_scan_converter_generate;
+
+    _glitter_scan_converter_init (self->converter, &self->jmp);
+    status = glitter_scan_converter_reset (self->converter,
+                                          xmin, ymin, xmax, ymax);
+    if (unlikely (status))
+       goto bail;
+
+    self->fill_rule = fill_rule;
+    self->antialias = antialias;
+
+    return &self->base;
+
+ bail:
+    self->base.destroy(&self->base);
+ bail_nomem:
+    return _cairo_scan_converter_create_in_error (status);
+}
diff --git a/src/cairo-toy-font-face.c b/src/cairo-toy-font-face.c
new file mode 100644 (file)
index 0000000..363b9a2
--- /dev/null
@@ -0,0 +1,523 @@
+/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ * Copyright © 2005,2008 Red Hat Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *     Carl D. Worth <cworth@cworth.org>
+ *      Graydon Hoare <graydon@redhat.com>
+ *      Owen Taylor <otaylor@redhat.com>
+ *      Behdad Esfahbod <behdad@behdad.org>
+ */
+
+#define _BSD_SOURCE /* for strdup() */
+#include "cairoint.h"
+#include "cairo-error-private.h"
+
+
+static const cairo_font_face_t _cairo_font_face_null_pointer = {
+    { 0 },                             /* hash_entry */
+    CAIRO_STATUS_NULL_POINTER,         /* status */
+    CAIRO_REFERENCE_COUNT_INVALID,     /* ref_count */
+    { 0, 0, 0, NULL },                 /* user_data */
+    NULL
+};
+
+static const cairo_font_face_t _cairo_font_face_invalid_string = {
+    { 0 },                             /* hash_entry */
+    CAIRO_STATUS_INVALID_STRING,       /* status */
+    CAIRO_REFERENCE_COUNT_INVALID,     /* ref_count */
+    { 0, 0, 0, NULL },                 /* user_data */
+    NULL
+};
+
+static const cairo_font_face_t _cairo_font_face_invalid_slant = {
+    { 0 },                             /* hash_entry */
+    CAIRO_STATUS_INVALID_SLANT,                /* status */
+    CAIRO_REFERENCE_COUNT_INVALID,     /* ref_count */
+    { 0, 0, 0, NULL },                 /* user_data */
+    NULL
+};
+
+static const cairo_font_face_t _cairo_font_face_invalid_weight = {
+    { 0 },                             /* hash_entry */
+    CAIRO_STATUS_INVALID_WEIGHT,       /* status */
+    CAIRO_REFERENCE_COUNT_INVALID,     /* ref_count */
+    { 0, 0, 0, NULL },                 /* user_data */
+    NULL
+};
+
+
+static const cairo_font_face_backend_t _cairo_toy_font_face_backend;
+
+static int
+_cairo_toy_font_face_keys_equal (const void *key_a,
+                                const void *key_b);
+
+/* We maintain a hash table from family/weight/slant =>
+ * #cairo_font_face_t for #cairo_toy_font_t. The primary purpose of
+ * this mapping is to provide unique #cairo_font_face_t values so that
+ * our cache and mapping from #cairo_font_face_t => #cairo_scaled_font_t
+ * works. Once the corresponding #cairo_font_face_t objects fall out of
+ * downstream caches, we don't need them in this hash table anymore.
+ *
+ * Modifications to this hash table are protected by
+ * _cairo_toy_font_face_mutex.
+ */
+static cairo_hash_table_t *cairo_toy_font_face_hash_table = NULL;
+
+static cairo_hash_table_t *
+_cairo_toy_font_face_hash_table_lock (void)
+{
+    CAIRO_MUTEX_LOCK (_cairo_toy_font_face_mutex);
+
+    if (cairo_toy_font_face_hash_table == NULL)
+    {
+       cairo_toy_font_face_hash_table =
+           _cairo_hash_table_create (_cairo_toy_font_face_keys_equal);
+
+       if (cairo_toy_font_face_hash_table == NULL) {
+           CAIRO_MUTEX_UNLOCK (_cairo_toy_font_face_mutex);
+           return NULL;
+       }
+    }
+
+    return cairo_toy_font_face_hash_table;
+}
+
+static void
+_cairo_toy_font_face_hash_table_unlock (void)
+{
+    CAIRO_MUTEX_UNLOCK (_cairo_toy_font_face_mutex);
+}
+
+/**
+ * _cairo_toy_font_face_init_key:
+ *
+ * Initialize those portions of #cairo_toy_font_face_t needed to use
+ * it as a hash table key, including the hash code buried away in
+ * font_face->base.hash_entry. No memory allocation is performed here
+ * so that no fini call is needed. We do this to make it easier to use
+ * an automatic #cairo_toy_font_face_t variable as a key.
+ **/
+static void
+_cairo_toy_font_face_init_key (cairo_toy_font_face_t *key,
+                              const char            *family,
+                              cairo_font_slant_t     slant,
+                              cairo_font_weight_t    weight)
+{
+    unsigned long hash;
+
+    key->family = family;
+    key->owns_family = FALSE;
+
+    key->slant = slant;
+    key->weight = weight;
+
+    /* 1607 and 1451 are just a couple of arbitrary primes. */
+    hash = _cairo_hash_string (family);
+    hash += ((unsigned long) slant) * 1607;
+    hash += ((unsigned long) weight) * 1451;
+
+    key->base.hash_entry.hash = hash;
+}
+
+static cairo_status_t
+_cairo_toy_font_face_create_impl_face (cairo_toy_font_face_t *font_face,
+                                      cairo_font_face_t **impl_font_face)
+{
+    const cairo_font_face_backend_t * backend = CAIRO_FONT_FACE_BACKEND_DEFAULT;
+    cairo_int_status_t status = CAIRO_INT_STATUS_UNSUPPORTED;
+
+    if (unlikely (font_face->base.status))
+       return font_face->base.status;
+
+    if (backend->create_for_toy != NULL &&
+       0 != strncmp (font_face->family, CAIRO_USER_FONT_FAMILY_DEFAULT,
+                     strlen (CAIRO_USER_FONT_FAMILY_DEFAULT)))
+    {
+       status = backend->create_for_toy (font_face, impl_font_face);
+    }
+
+    if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
+       backend = &_cairo_user_font_face_backend;
+       status = backend->create_for_toy (font_face, impl_font_face);
+    }
+
+    return status;
+}
+
+static cairo_status_t
+_cairo_toy_font_face_init (cairo_toy_font_face_t *font_face,
+                          const char            *family,
+                          cairo_font_slant_t     slant,
+                          cairo_font_weight_t    weight)
+{
+    char *family_copy;
+    cairo_status_t status;
+
+    family_copy = strdup (family);
+    if (unlikely (family_copy == NULL))
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    _cairo_toy_font_face_init_key (font_face, family_copy, slant, weight);
+    font_face->owns_family = TRUE;
+
+    _cairo_font_face_init (&font_face->base, &_cairo_toy_font_face_backend);
+
+    status = _cairo_toy_font_face_create_impl_face (font_face,
+                                                   &font_face->impl_face);
+    if (unlikely (status)) {
+       free (family_copy);
+       return status;
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_toy_font_face_fini (cairo_toy_font_face_t *font_face)
+{
+    /* We assert here that we own font_face->family before casting
+     * away the const qualifer. */
+    assert (font_face->owns_family);
+    free ((char*) font_face->family);
+
+    if (font_face->impl_face)
+       cairo_font_face_destroy (font_face->impl_face);
+}
+
+static int
+_cairo_toy_font_face_keys_equal (const void *key_a,
+                                const void *key_b)
+{
+    const cairo_toy_font_face_t *face_a = key_a;
+    const cairo_toy_font_face_t *face_b = key_b;
+
+    return (strcmp (face_a->family, face_b->family) == 0 &&
+           face_a->slant == face_b->slant &&
+           face_a->weight == face_b->weight);
+}
+
+/**
+ * cairo_toy_font_face_create:
+ * @family: a font family name, encoded in UTF-8
+ * @slant: the slant for the font
+ * @weight: the weight for the font
+ *
+ * Creates a font face from a triplet of family, slant, and weight.
+ * These font faces are used in implementation of the the #cairo_t "toy"
+ * font API.
+ *
+ * If @family is the zero-length string "", the platform-specific default
+ * family is assumed.  The default family then can be queried using
+ * cairo_toy_font_face_get_family().
+ *
+ * The cairo_select_font_face() function uses this to create font faces.
+ * See that function for limitations and other details of toy font faces.
+ *
+ * Return value: a newly created #cairo_font_face_t. Free with
+ *  cairo_font_face_destroy() when you are done using it.
+ *
+ * Since: 1.8
+ **/
+cairo_font_face_t *
+cairo_toy_font_face_create (const char          *family,
+                           cairo_font_slant_t   slant,
+                           cairo_font_weight_t  weight)
+{
+    cairo_status_t status;
+    cairo_toy_font_face_t key, *font_face;
+    cairo_hash_table_t *hash_table;
+
+    if (family == NULL)
+       return (cairo_font_face_t*) &_cairo_font_face_null_pointer;
+
+    /* Make sure we've got valid UTF-8 for the family */
+    status = _cairo_utf8_to_ucs4 (family, -1, NULL, NULL);
+    if (unlikely (status)) {
+       if (status == CAIRO_STATUS_INVALID_STRING)
+           return (cairo_font_face_t*) &_cairo_font_face_invalid_string;
+
+       return (cairo_font_face_t*) &_cairo_font_face_nil;
+    }
+
+    switch (slant) {
+       case CAIRO_FONT_SLANT_NORMAL:
+       case CAIRO_FONT_SLANT_ITALIC:
+       case CAIRO_FONT_SLANT_OBLIQUE:
+           break;
+       default:
+           return (cairo_font_face_t*) &_cairo_font_face_invalid_slant;
+    }
+
+    switch (weight) {
+       case CAIRO_FONT_WEIGHT_NORMAL:
+       case CAIRO_FONT_WEIGHT_BOLD:
+           break;
+       default:
+           return (cairo_font_face_t*) &_cairo_font_face_invalid_weight;
+    }
+
+    if (*family == '\0')
+       family = CAIRO_FONT_FAMILY_DEFAULT;
+
+    hash_table = _cairo_toy_font_face_hash_table_lock ();
+    if (unlikely (hash_table == NULL))
+       goto UNWIND;
+
+    _cairo_toy_font_face_init_key (&key, family, slant, weight);
+
+    /* Return existing font_face if it exists in the hash table. */
+    font_face = _cairo_hash_table_lookup (hash_table,
+                                         &key.base.hash_entry);
+    if (font_face != NULL) {
+       if (font_face->base.status == CAIRO_STATUS_SUCCESS) {
+           cairo_font_face_reference (&font_face->base);
+           _cairo_toy_font_face_hash_table_unlock ();
+           return &font_face->base;
+       }
+
+       /* remove the bad font from the hash table */
+       _cairo_hash_table_remove (hash_table, &font_face->base.hash_entry);
+    }
+
+    /* Otherwise create it and insert into hash table. */
+    font_face = malloc (sizeof (cairo_toy_font_face_t));
+    if (unlikely (font_face == NULL)) {
+       status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+       goto UNWIND_HASH_TABLE_LOCK;
+    }
+
+    status = _cairo_toy_font_face_init (font_face, family, slant, weight);
+    if (unlikely (status))
+       goto UNWIND_FONT_FACE_MALLOC;
+
+    assert (font_face->base.hash_entry.hash == key.base.hash_entry.hash);
+    status = _cairo_hash_table_insert (hash_table, &font_face->base.hash_entry);
+    if (unlikely (status))
+       goto UNWIND_FONT_FACE_INIT;
+
+    _cairo_toy_font_face_hash_table_unlock ();
+
+    return &font_face->base;
+
+ UNWIND_FONT_FACE_INIT:
+    _cairo_toy_font_face_fini (font_face);
+ UNWIND_FONT_FACE_MALLOC:
+    free (font_face);
+ UNWIND_HASH_TABLE_LOCK:
+    _cairo_toy_font_face_hash_table_unlock ();
+ UNWIND:
+    return (cairo_font_face_t*) &_cairo_font_face_nil;
+}
+slim_hidden_def (cairo_toy_font_face_create);
+
+static void
+_cairo_toy_font_face_destroy (void *abstract_face)
+{
+    cairo_toy_font_face_t *font_face = abstract_face;
+    cairo_hash_table_t *hash_table;
+
+    hash_table = _cairo_toy_font_face_hash_table_lock ();
+    /* All created objects must have been mapped in the hash table. */
+    assert (hash_table != NULL);
+
+    if (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&font_face->base.ref_count)) {
+       /* somebody recreated the font whilst we waited for the lock */
+       _cairo_toy_font_face_hash_table_unlock ();
+       return;
+    }
+
+    /* Font faces in SUCCESS status are guaranteed to be in the
+     * hashtable. Font faces in an error status are removed from the
+     * hashtable if they are found during a lookup, thus they should
+     * only be removed if they are in the hashtable. */
+    if (likely (font_face->base.status == CAIRO_STATUS_SUCCESS) ||
+       _cairo_hash_table_lookup (hash_table, &font_face->base.hash_entry) == font_face)
+       _cairo_hash_table_remove (hash_table, &font_face->base.hash_entry);
+
+    _cairo_toy_font_face_hash_table_unlock ();
+
+    _cairo_toy_font_face_fini (font_face);
+}
+
+static cairo_status_t
+_cairo_toy_font_face_scaled_font_create (void                *abstract_font_face,
+                                        const cairo_matrix_t       *font_matrix,
+                                        const cairo_matrix_t       *ctm,
+                                        const cairo_font_options_t *options,
+                                        cairo_scaled_font_t       **scaled_font)
+{
+    cairo_toy_font_face_t *font_face = (cairo_toy_font_face_t *) abstract_font_face;
+
+    ASSERT_NOT_REACHED;
+
+    return _cairo_font_face_set_error (&font_face->base, CAIRO_STATUS_FONT_TYPE_MISMATCH);
+}
+
+static cairo_font_face_t *
+_cairo_toy_font_face_get_implementation (void                *abstract_font_face,
+                                        const cairo_matrix_t       *font_matrix,
+                                        const cairo_matrix_t       *ctm,
+                                        const cairo_font_options_t *options)
+{
+    cairo_toy_font_face_t *font_face = abstract_font_face;
+
+    if (font_face->impl_face) {
+       cairo_font_face_t *impl = font_face->impl_face;
+
+       if (impl->backend->get_implementation != NULL) {
+           return impl->backend->get_implementation (impl,
+                                                     font_matrix,
+                                                     ctm,
+                                                     options);
+       }
+
+       return cairo_font_face_reference (impl);
+    }
+
+    return abstract_font_face;
+}
+
+static cairo_bool_t
+_cairo_font_face_is_toy (cairo_font_face_t *font_face)
+{
+    return font_face->backend == &_cairo_toy_font_face_backend;
+}
+
+/**
+ * cairo_toy_font_face_get_family:
+ * @font_face: A toy font face
+ *
+ * Gets the familly name of a toy font.
+ *
+ * Return value: The family name.  This string is owned by the font face
+ * and remains valid as long as the font face is alive (referenced).
+ *
+ * Since: 1.8
+ **/
+const char *
+cairo_toy_font_face_get_family (cairo_font_face_t *font_face)
+{
+    cairo_toy_font_face_t *toy_font_face;
+
+    if (font_face->status)
+       return CAIRO_FONT_FAMILY_DEFAULT;
+
+    toy_font_face = (cairo_toy_font_face_t *) font_face;
+    if (! _cairo_font_face_is_toy (font_face)) {
+       if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_FONT_TYPE_MISMATCH))
+           return CAIRO_FONT_FAMILY_DEFAULT;
+    }
+    assert (toy_font_face->owns_family);
+    return toy_font_face->family;
+}
+
+/**
+ * cairo_toy_font_face_get_slant:
+ * @font_face: A toy font face
+ *
+ * Gets the slant a toy font.
+ *
+ * Return value: The slant value
+ *
+ * Since: 1.8
+ **/
+cairo_font_slant_t
+cairo_toy_font_face_get_slant (cairo_font_face_t *font_face)
+{
+    cairo_toy_font_face_t *toy_font_face;
+
+    if (font_face->status)
+       return CAIRO_FONT_SLANT_DEFAULT;
+
+    toy_font_face = (cairo_toy_font_face_t *) font_face;
+    if (! _cairo_font_face_is_toy (font_face)) {
+       if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_FONT_TYPE_MISMATCH))
+           return CAIRO_FONT_SLANT_DEFAULT;
+    }
+    return toy_font_face->slant;
+}
+slim_hidden_def (cairo_toy_font_face_get_slant);
+
+/**
+ * cairo_toy_font_face_get_weight:
+ * @font_face: A toy font face
+ *
+ * Gets the weight a toy font.
+ *
+ * Return value: The weight value
+ *
+ * Since: 1.8
+ **/
+cairo_font_weight_t
+cairo_toy_font_face_get_weight (cairo_font_face_t *font_face)
+{
+    cairo_toy_font_face_t *toy_font_face;
+
+    if (font_face->status)
+       return CAIRO_FONT_WEIGHT_DEFAULT;
+
+    toy_font_face = (cairo_toy_font_face_t *) font_face;
+    if (! _cairo_font_face_is_toy (font_face)) {
+       if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_FONT_TYPE_MISMATCH))
+           return CAIRO_FONT_WEIGHT_DEFAULT;
+    }
+    return toy_font_face->weight;
+}
+slim_hidden_def (cairo_toy_font_face_get_weight);
+
+static const cairo_font_face_backend_t _cairo_toy_font_face_backend = {
+    CAIRO_FONT_TYPE_TOY,
+    NULL,                                      /* create_for_toy */
+    _cairo_toy_font_face_destroy,
+    _cairo_toy_font_face_scaled_font_create,
+    _cairo_toy_font_face_get_implementation
+};
+
+void
+_cairo_toy_font_face_reset_static_data (void)
+{
+    cairo_hash_table_t *hash_table;
+
+    /* We manually acquire the lock rather than calling
+     * cairo_toy_font_face_hash_table_lock simply to avoid
+     * creating the table only to destroy it again. */
+    CAIRO_MUTEX_LOCK (_cairo_toy_font_face_mutex);
+    hash_table = cairo_toy_font_face_hash_table;
+    cairo_toy_font_face_hash_table = NULL;
+    CAIRO_MUTEX_UNLOCK (_cairo_toy_font_face_mutex);
+
+    if (hash_table != NULL)
+       _cairo_hash_table_destroy (hash_table);
+}
diff --git a/src/cairo-traps-compositor.c b/src/cairo-traps-compositor.c
new file mode 100644 (file)
index 0000000..2f77ff8
--- /dev/null
@@ -0,0 +1,2040 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ * Copyright © 2005 Red Hat, Inc.
+ * Copyright © 2011 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *     Carl D. Worth <cworth@cworth.org>
+ *      Joonas Pihlaja <jpihlaja@cc.helsinki.fi>
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-box-private.h"
+#include "cairo-boxes-private.h"
+#include "cairo-clip-private.h"
+#include "cairo-composite-rectangles-private.h"
+#include "cairo-compositor-private.h"
+#include "cairo-error-private.h"
+#include "cairo-image-surface-private.h"
+#include "cairo-pattern-private.h"
+#include "cairo-paginated-private.h"
+#include "cairo-recording-surface-private.h"
+#include "cairo-surface-subsurface-private.h"
+#include "cairo-surface-snapshot-private.h"
+#include "cairo-surface-observer-private.h"
+#include "cairo-region-private.h"
+#include "cairo-spans-private.h"
+#include "cairo-traps-private.h"
+#include "cairo-tristrip-private.h"
+
+typedef cairo_int_status_t
+(*draw_func_t) (const cairo_traps_compositor_t *compositor,
+               cairo_surface_t                 *dst,
+               void                            *closure,
+               cairo_operator_t                 op,
+               cairo_surface_t         *src,
+               int                              src_x,
+               int                              src_y,
+               int                              dst_x,
+               int                              dst_y,
+               const cairo_rectangle_int_t     *extents,
+               cairo_clip_t                    *clip);
+
+static void do_unaligned_row(void (*blt)(void *closure,
+                                        int16_t x, int16_t y,
+                                        int16_t w, int16_t h,
+                                        uint16_t coverage),
+                            void *closure,
+                            const cairo_box_t *b,
+                            int tx, int y, int h,
+                            uint16_t coverage)
+{
+    int x1 = _cairo_fixed_integer_part (b->p1.x) - tx;
+    int x2 = _cairo_fixed_integer_part (b->p2.x) - tx;
+    if (x2 > x1) {
+       if (! _cairo_fixed_is_integer (b->p1.x)) {
+           blt(closure, x1, y, 1, h,
+               coverage * (256 - _cairo_fixed_fractional_part (b->p1.x)));
+           x1++;
+       }
+
+       if (x2 > x1)
+           blt(closure, x1, y, x2-x1, h, (coverage << 8) - (coverage >> 8));
+
+       if (! _cairo_fixed_is_integer (b->p2.x))
+           blt(closure, x2, y, 1, h,
+               coverage * _cairo_fixed_fractional_part (b->p2.x));
+    } else
+       blt(closure, x1, y, 1, h,
+           coverage * (b->p2.x - b->p1.x));
+}
+
+static void do_unaligned_box(void (*blt)(void *closure,
+                                        int16_t x, int16_t y,
+                                        int16_t w, int16_t h,
+                                        uint16_t coverage),
+                            void *closure,
+                            const cairo_box_t *b, int tx, int ty)
+{
+    int y1 = _cairo_fixed_integer_part (b->p1.y) - ty;
+    int y2 = _cairo_fixed_integer_part (b->p2.y) - ty;
+    if (y2 > y1) {
+       if (! _cairo_fixed_is_integer (b->p1.y)) {
+           do_unaligned_row(blt, closure, b, tx, y1, 1,
+                            256 - _cairo_fixed_fractional_part (b->p1.y));
+           y1++;
+       }
+
+       if (y2 > y1)
+           do_unaligned_row(blt, closure, b, tx, y1, y2-y1, 256);
+
+       if (! _cairo_fixed_is_integer (b->p2.y))
+           do_unaligned_row(blt, closure, b, tx, y2, 1,
+                            _cairo_fixed_fractional_part (b->p2.y));
+    } else
+       do_unaligned_row(blt, closure, b, tx, y1, 1,
+                        b->p2.y - b->p1.y);
+}
+
+struct blt_in {
+    const cairo_traps_compositor_t *compositor;
+    cairo_surface_t *dst;
+    cairo_boxes_t boxes;
+};
+
+static void blt_in(void *closure,
+                  int16_t x, int16_t y,
+                  int16_t w, int16_t h,
+                  uint16_t coverage)
+{
+    struct blt_in *info = closure;
+    cairo_color_t color;
+
+    if (CAIRO_ALPHA_SHORT_IS_OPAQUE (coverage))
+       return;
+
+    _cairo_box_from_integers (&info->boxes.chunks.base[0], x, y, w, h);
+
+    _cairo_color_init_rgba (&color, 0, 0, 0, coverage / (double) 0xffff);
+    info->compositor->fill_boxes (info->dst,
+                                 CAIRO_OPERATOR_IN, &color,
+                                 &info->boxes);
+}
+
+static cairo_int_status_t
+combine_clip_as_traps (const cairo_traps_compositor_t *compositor,
+                      cairo_surface_t *mask,
+                      const cairo_clip_t *clip,
+                      const cairo_rectangle_int_t *extents)
+{
+    cairo_polygon_t polygon;
+    cairo_fill_rule_t fill_rule;
+    cairo_antialias_t antialias;
+    cairo_traps_t traps;
+    cairo_surface_t *src;
+    int src_x, src_y;
+    cairo_int_status_t status;
+
+    status = _cairo_clip_get_polygon (clip, &polygon,
+                                     &fill_rule, &antialias);
+    if (status)
+       return status;
+
+    _cairo_traps_init (&traps);
+    status = _cairo_bentley_ottmann_tessellate_polygon (&traps,
+                                                       &polygon,
+                                                       fill_rule);
+    _cairo_polygon_fini (&polygon);
+    if (unlikely (status))
+       return status;
+
+    src = compositor->pattern_to_surface (mask, NULL, FALSE,
+                                         extents, NULL,
+                                         &src_x, &src_y);
+    if (unlikely (src->status)) {
+       _cairo_traps_fini (&traps);
+       return src->status;
+    }
+
+    status = compositor->composite_traps (mask, CAIRO_OPERATOR_IN, src,
+                                         src_x, src_y,
+                                         extents->x, extents->y,
+                                         extents,
+                                         antialias, &traps);
+
+    cairo_surface_destroy (src);
+    _cairo_traps_fini (&traps);
+
+    return status;
+}
+
+static cairo_surface_t *
+traps_get_clip_surface (const cairo_traps_compositor_t *compositor,
+                       cairo_surface_t *target,
+                       const cairo_clip_t *clip,
+                       const cairo_rectangle_int_t *extents)
+{
+    cairo_surface_t *surface;
+    cairo_int_status_t status;
+
+    surface = _cairo_surface_create_similar_solid (target,
+                                                  CAIRO_CONTENT_ALPHA,
+                                                  extents->width,
+                                                  extents->height,
+                                                  CAIRO_COLOR_WHITE);
+    if (unlikely (surface->status))
+       return surface;
+
+    status = compositor->acquire (surface);
+    if (unlikely (status)) {
+       cairo_surface_destroy (surface);
+       return _cairo_surface_create_in_error (status);
+    }
+
+    status = combine_clip_as_traps (compositor, surface, clip, extents);
+    if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
+       status = _cairo_clip_combine_with_surface (clip, surface,
+                                                  extents->x, extents->y);
+    }
+    compositor->release (surface);
+
+    return surface;
+}
+
+static cairo_surface_t *
+create_composite_mask (const cairo_traps_compositor_t *compositor,
+                      cairo_surface_t          *dst,
+                      void                     *draw_closure,
+                      draw_func_t               draw_func,
+                      draw_func_t               mask_func,
+                      const cairo_composite_rectangles_t *extents)
+{
+    cairo_surface_t *surface, *src;
+    cairo_int_status_t status;
+    struct blt_in info;
+    int src_x, src_y;
+    int i;
+
+    surface = _cairo_surface_create_similar_solid (dst,
+                                                  CAIRO_CONTENT_ALPHA,
+                                                  extents->bounded.width,
+                                                  extents->bounded.height,
+                                                  CAIRO_COLOR_TRANSPARENT);
+    if (unlikely (surface->status))
+       return surface;
+
+    src = compositor->pattern_to_surface (surface,
+                                         &_cairo_pattern_white.base,
+                                         FALSE,
+                                         &extents->bounded,
+                                         &extents->bounded,
+                                         &src_x, &src_y);
+    if (unlikely (src->status))
+       return src;
+
+    status = compositor->acquire (surface);
+    if (unlikely (status)) {
+       cairo_surface_destroy (src);
+       cairo_surface_destroy (surface);
+       return _cairo_surface_create_in_error (status);
+    }
+
+    if (mask_func && extents->clip->path == NULL) {
+       status = mask_func (compositor, surface, draw_closure,
+                           CAIRO_OPERATOR_SOURCE, src, src_x, src_y,
+                           extents->bounded.x, extents->bounded.y,
+                           &extents->bounded, extents->clip);
+       if (likely (status != CAIRO_INT_STATUS_UNSUPPORTED)) {
+           cairo_surface_destroy (src);
+           goto out;
+       }
+    }
+
+    /* Is it worth setting the clip region here? */
+    status = draw_func (compositor, surface, draw_closure,
+                       CAIRO_OPERATOR_ADD, src, src_x, src_y,
+                       extents->bounded.x, extents->bounded.y,
+                       &extents->bounded, NULL);
+    cairo_surface_destroy (src);
+    if (unlikely (status))
+       goto error;
+
+    info.compositor = compositor;
+    info.dst = surface;
+    _cairo_boxes_init (&info.boxes);
+    info.boxes.num_boxes = 1;
+    for (i = 0; i < extents->clip->num_boxes; i++) {
+       cairo_box_t *b = &extents->clip->boxes[i];
+
+       if (! _cairo_fixed_is_integer (b->p1.x) ||
+           ! _cairo_fixed_is_integer (b->p1.y) ||
+           ! _cairo_fixed_is_integer (b->p2.x) ||
+           ! _cairo_fixed_is_integer (b->p2.y))
+       {
+           do_unaligned_box(blt_in, &info, b,
+                            extents->bounded.x,
+                            extents->bounded.y);
+       }
+    }
+
+    if (extents->clip->path != NULL) {
+       status = combine_clip_as_traps (compositor, surface,
+                                       extents->clip, &extents->bounded);
+       if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
+           status = _cairo_clip_combine_with_surface (extents->clip, surface,
+                                                      extents->bounded.x,
+                                                      extents->bounded.y);
+       }
+       if (unlikely (status))
+           goto error;
+    }
+
+out:
+    compositor->release (surface);
+    surface->is_clear = FALSE;
+    return surface;
+
+error:
+    if (status != CAIRO_INT_STATUS_NOTHING_TO_DO) {
+       cairo_surface_destroy (surface);
+       surface = _cairo_surface_create_in_error (status);
+    }
+    compositor->release (surface);
+    return surface;
+}
+
+/* Handles compositing with a clip surface when the operator allows
+ * us to combine the clip with the mask
+ */
+static cairo_status_t
+clip_and_composite_with_mask (const cairo_traps_compositor_t *compositor,
+                             const cairo_composite_rectangles_t*extents,
+                             draw_func_t                draw_func,
+                             draw_func_t                mask_func,
+                             void                      *draw_closure,
+                             cairo_operator_t           op,
+                             cairo_surface_t   *src,
+                             int src_x, int src_y)
+{
+    cairo_surface_t *dst = extents->surface;
+    cairo_surface_t *mask;
+
+    mask = create_composite_mask (compositor, dst, draw_closure,
+                                 draw_func, mask_func,
+                                 extents);
+    if (unlikely (mask->status))
+       return mask->status;
+
+    if (mask->is_clear)
+       goto skip;
+
+    if (src != NULL || dst->content != CAIRO_CONTENT_ALPHA) {
+       compositor->composite (dst, op, src, mask,
+                              extents->bounded.x + src_x,
+                              extents->bounded.y + src_y,
+                              0, 0,
+                              extents->bounded.x,      extents->bounded.y,
+                              extents->bounded.width,  extents->bounded.height);
+    } else {
+       compositor->composite (dst, op, mask, NULL,
+                              0, 0,
+                              0, 0,
+                              extents->bounded.x,      extents->bounded.y,
+                              extents->bounded.width,  extents->bounded.height);
+    }
+
+skip:
+    cairo_surface_destroy (mask);
+    return CAIRO_STATUS_SUCCESS;
+}
+
+/* Handles compositing with a clip surface when we have to do the operation
+ * in two pieces and combine them together.
+ */
+static cairo_status_t
+clip_and_composite_combine (const cairo_traps_compositor_t *compositor,
+                           const cairo_composite_rectangles_t*extents,
+                           draw_func_t          draw_func,
+                           void                        *draw_closure,
+                           cairo_operator_t             op,
+                           cairo_surface_t     *src,
+                           int src_x, int src_y)
+{
+    cairo_surface_t *dst = extents->surface;
+    cairo_surface_t *tmp, *clip;
+    cairo_status_t status;
+
+    tmp = _cairo_surface_create_similar_scratch (dst, dst->content,
+                                                extents->bounded.width,
+                                                extents->bounded.height);
+    if (unlikely (tmp->status))
+       return tmp->status;
+
+    status = compositor->acquire (tmp);
+    if (unlikely (status)) {
+       cairo_surface_destroy (tmp);
+       return status;
+    }
+
+    compositor->composite (tmp,
+                          dst->is_clear ? CAIRO_OPERATOR_CLEAR : CAIRO_OPERATOR_SOURCE,
+                          dst, NULL,
+                          extents->bounded.x,      extents->bounded.y,
+                          0, 0,
+                          0, 0,
+                          extents->bounded.width,  extents->bounded.height);
+
+    status = draw_func (compositor, tmp, draw_closure, op,
+                       src, src_x, src_y,
+                       extents->bounded.x, extents->bounded.y,
+                       &extents->bounded, NULL);
+
+    if (unlikely (status))
+       goto cleanup;
+
+    clip = traps_get_clip_surface (compositor, dst, extents->clip,
+                                  &extents->bounded);
+    if (unlikely ((status = clip->status)))
+       goto cleanup;
+
+    if (dst->is_clear) {
+       compositor->composite (dst, CAIRO_OPERATOR_SOURCE, tmp, clip,
+                              0, 0,
+                              0, 0,
+                              extents->bounded.x,      extents->bounded.y,
+                              extents->bounded.width,  extents->bounded.height);
+    } else {
+       compositor->lerp (dst, tmp, clip,
+                         0, 0,
+                         0,0,
+                         extents->bounded.x,     extents->bounded.y,
+                         extents->bounded.width, extents->bounded.height);
+    }
+    cairo_surface_destroy (clip);
+
+cleanup:
+    compositor->release (tmp);
+    cairo_surface_destroy (tmp);
+
+    return status;
+}
+
+/* Handles compositing for %CAIRO_OPERATOR_SOURCE, which is special; it's
+ * defined as (src IN mask IN clip) ADD (dst OUT (mask IN clip))
+ */
+static cairo_status_t
+clip_and_composite_source (const cairo_traps_compositor_t      *compositor,
+                          cairo_surface_t                      *dst,
+                          draw_func_t                           draw_func,
+                          draw_func_t                           mask_func,
+                          void                                 *draw_closure,
+                          cairo_surface_t              *src,
+                          int src_x,
+                          int src_y,
+                          const cairo_composite_rectangles_t   *extents)
+{
+    cairo_surface_t *mask;
+
+    /* Create a surface that is mask IN clip */
+    mask = create_composite_mask (compositor, dst, draw_closure,
+                                 draw_func, mask_func,
+                                 extents);
+    if (unlikely (mask->status))
+       return mask->status;
+
+    if (mask->is_clear)
+       goto skip;
+
+    if (dst->is_clear) {
+       compositor->composite (dst, CAIRO_OPERATOR_SOURCE, src, mask,
+                              extents->bounded.x + src_x, extents->bounded.y + src_y,
+                              0, 0,
+                              extents->bounded.x,      extents->bounded.y,
+                              extents->bounded.width,  extents->bounded.height);
+    } else {
+       compositor->lerp (dst, src, mask,
+                         extents->bounded.x + src_x, extents->bounded.y + src_y,
+                         0, 0,
+                         extents->bounded.x,     extents->bounded.y,
+                         extents->bounded.width, extents->bounded.height);
+    }
+
+skip:
+    cairo_surface_destroy (mask);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_bool_t
+can_reduce_alpha_op (cairo_operator_t op)
+{
+    int iop = op;
+    switch (iop) {
+    case CAIRO_OPERATOR_OVER:
+    case CAIRO_OPERATOR_SOURCE:
+    case CAIRO_OPERATOR_ADD:
+       return TRUE;
+    default:
+       return FALSE;
+    }
+}
+
+static cairo_bool_t
+reduce_alpha_op (cairo_composite_rectangles_t *extents)
+{
+    cairo_surface_t *dst = extents->surface;
+    cairo_operator_t op = extents->op;
+    const cairo_pattern_t *pattern = &extents->source_pattern.base;
+    return dst->is_clear &&
+          dst->content == CAIRO_CONTENT_ALPHA &&
+          _cairo_pattern_is_opaque_solid (pattern) &&
+          can_reduce_alpha_op (op);
+}
+
+static cairo_status_t
+fixup_unbounded_with_mask (const cairo_traps_compositor_t *compositor,
+                          const cairo_composite_rectangles_t *extents)
+{
+    cairo_surface_t *dst = extents->surface;
+    cairo_clip_t *clip = extents->clip;
+    cairo_surface_t *mask;
+
+    /* XXX can we avoid querying the clip surface again? */
+    mask = traps_get_clip_surface (compositor, dst, clip, &extents->unbounded);
+    if (unlikely (mask->status))
+       return mask->status;
+
+    /* top */
+    if (extents->bounded.y != extents->unbounded.y) {
+       int x = extents->unbounded.x;
+       int y = extents->unbounded.y;
+       int width = extents->unbounded.width;
+       int height = extents->bounded.y - y;
+
+       compositor->composite (dst, CAIRO_OPERATOR_DEST_OUT, mask, NULL,
+                              0, 0,
+                              0, 0,
+                              x, y,
+                              width, height);
+    }
+
+    /* left */
+    if (extents->bounded.x != extents->unbounded.x) {
+       int x = extents->unbounded.x;
+       int y = extents->bounded.y;
+       int width = extents->bounded.x - x;
+       int height = extents->bounded.height;
+
+       compositor->composite (dst, CAIRO_OPERATOR_DEST_OUT, mask, NULL,
+                              0, y - extents->unbounded.y,
+                              0, 0,
+                              x, y,
+                              width, height);
+    }
+
+    /* right */
+    if (extents->bounded.x + extents->bounded.width != extents->unbounded.x + extents->unbounded.width) {
+       int x = extents->bounded.x + extents->bounded.width;
+       int y = extents->bounded.y;
+       int width = extents->unbounded.x + extents->unbounded.width - x;
+       int height = extents->bounded.height;
+
+       compositor->composite (dst, CAIRO_OPERATOR_DEST_OUT, mask, NULL,
+                              x - extents->unbounded.x, y - extents->unbounded.y,
+                              0, 0,
+                              x, y,
+                              width, height);
+    }
+
+    /* bottom */
+    if (extents->bounded.y + extents->bounded.height != extents->unbounded.y + extents->unbounded.height) {
+       int x = extents->unbounded.x;
+       int y = extents->bounded.y + extents->bounded.height;
+       int width = extents->unbounded.width;
+       int height = extents->unbounded.y + extents->unbounded.height - y;
+
+       compositor->composite (dst, CAIRO_OPERATOR_DEST_OUT, mask, NULL,
+                              0, y - extents->unbounded.y,
+                              0, 0,
+                              x, y,
+                              width, height);
+    }
+
+    cairo_surface_destroy (mask);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+add_rect (cairo_boxes_t *boxes, int x1, int y1, int x2, int y2)
+{
+    cairo_box_t box;
+    cairo_int_status_t status;
+
+    box.p1.x = _cairo_fixed_from_int (x1);
+    box.p1.y = _cairo_fixed_from_int (y1);
+    box.p2.x = _cairo_fixed_from_int (x2);
+    box.p2.y = _cairo_fixed_from_int (y2);
+
+    status = _cairo_boxes_add (boxes, CAIRO_ANTIALIAS_DEFAULT, &box);
+    assert (status == CAIRO_INT_STATUS_SUCCESS);
+}
+
+static cairo_status_t
+fixup_unbounded (const cairo_traps_compositor_t *compositor,
+                cairo_composite_rectangles_t *extents,
+                cairo_boxes_t *boxes)
+{
+    cairo_surface_t *dst = extents->surface;
+    cairo_boxes_t clear, tmp;
+    cairo_box_t box;
+    cairo_int_status_t status;
+
+    if (extents->bounded.width  == extents->unbounded.width &&
+       extents->bounded.height == extents->unbounded.height)
+    {
+       return CAIRO_STATUS_SUCCESS;
+    }
+
+    assert (extents->clip->path == NULL);
+
+    /* subtract the drawn boxes from the unbounded area */
+    _cairo_boxes_init (&clear);
+
+    box.p1.x = _cairo_fixed_from_int (extents->unbounded.x + extents->unbounded.width);
+    box.p1.y = _cairo_fixed_from_int (extents->unbounded.y);
+    box.p2.x = _cairo_fixed_from_int (extents->unbounded.x);
+    box.p2.y = _cairo_fixed_from_int (extents->unbounded.y + extents->unbounded.height);
+
+    if (boxes == NULL) {
+       if (extents->bounded.width == 0 || extents->bounded.height == 0) {
+           goto empty;
+       } else {
+           /* top */
+           if (extents->bounded.y != extents->unbounded.y) {
+               add_rect (&clear,
+                         extents->unbounded.x, extents->unbounded.y,
+                         extents->unbounded.x + extents->unbounded.width,
+                         extents->bounded.y);
+           }
+           /* left */
+           if (extents->bounded.x != extents->unbounded.x) {
+               add_rect (&clear,
+                         extents->unbounded.x, extents->bounded.y,
+                         extents->bounded.x,
+                         extents->bounded.y + extents->bounded.height);
+           }
+           /* right */
+           if (extents->bounded.x + extents->bounded.width != extents->unbounded.x + extents->unbounded.width) {
+               add_rect (&clear,
+                         extents->bounded.x + extents->bounded.width,
+                         extents->bounded.y,
+                         extents->unbounded.x + extents->unbounded.width,
+                         extents->bounded.y + extents->bounded.height);
+           }
+           /* bottom */
+           if (extents->bounded.y + extents->bounded.height != extents->unbounded.y + extents->unbounded.height) {
+               add_rect (&clear,
+                         extents->unbounded.x,
+                         extents->bounded.y + extents->bounded.height,
+                         extents->unbounded.x + extents->unbounded.width,
+                         extents->unbounded.y + extents->unbounded.height);
+           }
+       }
+    } else if (boxes->num_boxes) {
+       _cairo_boxes_init (&tmp);
+
+       assert (boxes->is_pixel_aligned);
+
+       status = _cairo_boxes_add (&tmp, CAIRO_ANTIALIAS_DEFAULT, &box);
+       assert (status == CAIRO_INT_STATUS_SUCCESS);
+
+       tmp.chunks.next = &boxes->chunks;
+       tmp.num_boxes += boxes->num_boxes;
+
+       status = _cairo_bentley_ottmann_tessellate_boxes (&tmp,
+                                                         CAIRO_FILL_RULE_WINDING,
+                                                         &clear);
+       tmp.chunks.next = NULL;
+       if (unlikely (status))
+           goto error;
+    } else {
+empty:
+       box.p1.x = _cairo_fixed_from_int (extents->unbounded.x);
+       box.p2.x = _cairo_fixed_from_int (extents->unbounded.x + extents->unbounded.width);
+
+       status = _cairo_boxes_add (&clear, CAIRO_ANTIALIAS_DEFAULT, &box);
+       assert (status == CAIRO_INT_STATUS_SUCCESS);
+    }
+
+    /* Now intersect with the clip boxes */
+    if (extents->clip->num_boxes) {
+       _cairo_boxes_init_for_array (&tmp,
+                                    extents->clip->boxes,
+                                    extents->clip->num_boxes);
+       status = _cairo_boxes_intersect (&clear, &tmp, &clear);
+       if (unlikely (status))
+           goto error;
+    }
+
+    status = compositor->fill_boxes (dst,
+                                    CAIRO_OPERATOR_CLEAR,
+                                    CAIRO_COLOR_TRANSPARENT,
+                                    &clear);
+
+error:
+    _cairo_boxes_fini (&clear);
+    return status;
+}
+
+enum {
+    NEED_CLIP_REGION = 0x1,
+    NEED_CLIP_SURFACE = 0x2,
+    FORCE_CLIP_REGION = 0x4,
+};
+
+static cairo_bool_t
+need_bounded_clip (cairo_composite_rectangles_t *extents)
+{
+    unsigned int flags = 0;
+
+    if (extents->unbounded.width < extents->destination.width ||
+       extents->unbounded.height < extents->destination.height)
+    {
+       flags |= NEED_CLIP_REGION;
+    }
+
+    if (! _cairo_clip_is_region (extents->clip))
+       flags |= NEED_CLIP_SURFACE;
+
+    return flags;
+}
+
+static cairo_bool_t
+need_unbounded_clip (cairo_composite_rectangles_t *extents)
+{
+    unsigned int flags = 0;
+    if (! extents->is_bounded) {
+       flags |= NEED_CLIP_REGION;
+       if (! _cairo_clip_is_region (extents->clip))
+           flags |= NEED_CLIP_SURFACE;
+    }
+    if (extents->clip->path != NULL)
+       flags |= NEED_CLIP_SURFACE;
+    return flags;
+}
+
+static cairo_status_t
+clip_and_composite (const cairo_traps_compositor_t *compositor,
+                   cairo_composite_rectangles_t *extents,
+                   draw_func_t          draw_func,
+                   draw_func_t          mask_func,
+                   void                *draw_closure,
+                   unsigned int need_clip)
+{
+    cairo_surface_t *dst = extents->surface;
+    cairo_operator_t op = extents->op;
+    cairo_pattern_t *source = &extents->source_pattern.base;
+    cairo_surface_t *src;
+    int src_x, src_y;
+    cairo_region_t *clip_region = NULL;
+    cairo_status_t status = CAIRO_STATUS_SUCCESS;
+
+    if (reduce_alpha_op (extents)) {
+       op = CAIRO_OPERATOR_ADD;
+       source = NULL;
+    }
+
+    if (op == CAIRO_OPERATOR_CLEAR) {
+       op = CAIRO_OPERATOR_DEST_OUT;
+       source = NULL;
+    }
+
+    compositor->acquire (dst);
+
+    if (need_clip & NEED_CLIP_REGION) {
+       const cairo_rectangle_int_t *limit;
+
+       if ((need_clip & FORCE_CLIP_REGION) == 0)
+           limit = &extents->unbounded;
+       else
+           limit = &extents->destination;
+
+       clip_region = _cairo_clip_get_region (extents->clip);
+       if (clip_region != NULL &&
+           cairo_region_contains_rectangle (clip_region,
+                                            limit) == CAIRO_REGION_OVERLAP_IN)
+           clip_region = NULL;
+
+       if (clip_region != NULL) {
+           status = compositor->set_clip_region (dst, clip_region);
+           if (unlikely (status)) {
+               compositor->release (dst);
+               return status;
+           }
+       }
+    }
+
+    if (extents->bounded.width == 0 || extents->bounded.height == 0)
+       goto skip;
+
+    src = compositor->pattern_to_surface (dst, source, FALSE,
+                                         &extents->bounded,
+                                         &extents->source_sample_area,
+                                         &src_x, &src_y);
+    if (unlikely (status = src->status))
+       goto error;
+
+    if (op == CAIRO_OPERATOR_SOURCE) {
+       status = clip_and_composite_source (compositor, dst,
+                                           draw_func, mask_func, draw_closure,
+                                           src, src_x, src_y,
+                                           extents);
+    } else {
+       if (need_clip & NEED_CLIP_SURFACE) {
+           if (extents->is_bounded) {
+               status = clip_and_composite_with_mask (compositor, extents,
+                                                      draw_func, mask_func,
+                                                      draw_closure,
+                                                      op, src, src_x, src_y);
+           } else {
+               status = clip_and_composite_combine (compositor, extents,
+                                                    draw_func, draw_closure,
+                                                    op, src, src_x, src_y);
+           }
+       } else {
+           status = draw_func (compositor,
+                               dst, draw_closure,
+                               op, src, src_x, src_y,
+                               0, 0,
+                               &extents->bounded,
+                               extents->clip);
+       }
+    }
+    cairo_surface_destroy (src);
+
+skip:
+    if (status == CAIRO_STATUS_SUCCESS && ! extents->is_bounded) {
+       if (need_clip & NEED_CLIP_SURFACE)
+           status = fixup_unbounded_with_mask (compositor, extents);
+       else
+           status = fixup_unbounded (compositor, extents, NULL);
+    }
+
+error:
+    if (clip_region)
+       compositor->set_clip_region (dst, NULL);
+
+    compositor->release (dst);
+
+    return status;
+}
+
+/* meta-ops */
+
+typedef struct {
+    cairo_traps_t traps;
+    cairo_antialias_t antialias;
+} composite_traps_info_t;
+
+static cairo_int_status_t
+composite_traps (const cairo_traps_compositor_t *compositor,
+                cairo_surface_t                *dst,
+                void                            *closure,
+                cairo_operator_t                op,
+                cairo_surface_t                *src,
+                int src_x, int src_y,
+                int dst_x, int dst_y,
+                const cairo_rectangle_int_t *extents,
+                cairo_clip_t                   *clip)
+{
+    composite_traps_info_t *info = closure;
+
+    return compositor->composite_traps (dst, op, src,
+                                       src_x - dst_x, src_y - dst_y,
+                                       dst_x, dst_y,
+                                       extents,
+                                       info->antialias, &info->traps);
+}
+
+typedef struct {
+    cairo_tristrip_t strip;
+    cairo_antialias_t antialias;
+} composite_tristrip_info_t;
+
+static cairo_int_status_t
+composite_tristrip (const cairo_traps_compositor_t *compositor,
+                   cairo_surface_t             *dst,
+                   void                                 *closure,
+                   cairo_operator_t             op,
+                   cairo_surface_t             *src,
+                   int src_x, int src_y,
+                   int dst_x, int dst_y,
+                   const cairo_rectangle_int_t *extents,
+                   cairo_clip_t                        *clip)
+{
+    composite_tristrip_info_t *info = closure;
+
+    return compositor->composite_tristrip (dst, op, src,
+                                          src_x - dst_x, src_y - dst_y,
+                                          dst_x, dst_y,
+                                          extents,
+                                          info->antialias, &info->strip);
+}
+
+static cairo_bool_t
+is_recording_pattern (const cairo_pattern_t *pattern)
+{
+    cairo_surface_t *surface;
+
+    if (pattern->type != CAIRO_PATTERN_TYPE_SURFACE)
+       return FALSE;
+
+    surface = ((const cairo_surface_pattern_t *) pattern)->surface;
+    if (_cairo_surface_is_paginated (surface))
+       surface = _cairo_paginated_surface_get_recording (surface);
+    if (_cairo_surface_is_snapshot (surface))
+       surface = _cairo_surface_snapshot_get_target (surface);
+    return _cairo_surface_is_recording (surface);
+}
+
+static cairo_surface_t *
+recording_pattern_get_surface (const cairo_pattern_t *pattern)
+{
+    cairo_surface_t *surface;
+
+    surface = ((const cairo_surface_pattern_t *) pattern)->surface;
+    if (_cairo_surface_is_paginated (surface))
+       surface = _cairo_paginated_surface_get_recording (surface);
+    if (_cairo_surface_is_snapshot (surface))
+       surface = _cairo_surface_snapshot_get_target (surface);
+    return surface;
+}
+
+static cairo_bool_t
+recording_pattern_contains_sample (const cairo_pattern_t *pattern,
+                                  const cairo_rectangle_int_t *sample)
+{
+    cairo_recording_surface_t *surface;
+
+    if (! is_recording_pattern (pattern))
+       return FALSE;
+
+    if (pattern->extend == CAIRO_EXTEND_NONE)
+       return TRUE;
+
+    surface = (cairo_recording_surface_t *) recording_pattern_get_surface (pattern);
+    if (surface->unbounded)
+       return TRUE;
+
+    if (sample->x >= surface->extents.x &&
+       sample->y >= surface->extents.y &&
+       sample->x + sample->width <= surface->extents.x + surface->extents.width &&
+       sample->y + sample->height <= surface->extents.y + surface->extents.height)
+    {
+       return TRUE;
+    }
+
+    return FALSE;
+}
+
+static cairo_bool_t
+op_reduces_to_source (cairo_composite_rectangles_t *extents)
+{
+    if (extents->op == CAIRO_OPERATOR_SOURCE)
+       return TRUE;
+
+    if (extents->surface->is_clear)
+       return extents->op == CAIRO_OPERATOR_OVER || extents->op == CAIRO_OPERATOR_ADD;
+
+    return FALSE;
+}
+
+static cairo_status_t
+composite_aligned_boxes (const cairo_traps_compositor_t *compositor,
+                        cairo_composite_rectangles_t *extents,
+                        cairo_boxes_t *boxes)
+{
+    cairo_surface_t *dst = extents->surface;
+    cairo_operator_t op = extents->op;
+    cairo_bool_t need_clip_mask = extents->clip->path != NULL;
+    cairo_bool_t op_is_source;
+    cairo_status_t status;
+
+    if (need_clip_mask &&
+       (! extents->is_bounded || extents->op == CAIRO_OPERATOR_SOURCE))
+    {
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+    }
+
+    op_is_source = op_reduces_to_source (extents);
+
+    /* Are we just copying a recording surface? */
+    if (! need_clip_mask && op_is_source &&
+       recording_pattern_contains_sample (&extents->source_pattern.base,
+                                          &extents->source_sample_area))
+    {
+       cairo_clip_t *recording_clip;
+       cairo_pattern_t *source = &extents->source_pattern.base;
+
+       /* XXX could also do tiling repeat modes... */
+
+       /* first clear the area about to be overwritten */
+       if (! dst->is_clear) {
+           status = compositor->acquire (dst);
+           if (unlikely (status))
+               return status;
+
+           status = compositor->fill_boxes (dst,
+                                            CAIRO_OPERATOR_CLEAR,
+                                            CAIRO_COLOR_TRANSPARENT,
+                                            boxes);
+           compositor->release (dst);
+           if (unlikely (status))
+               return status;
+       }
+
+       recording_clip = _cairo_clip_from_boxes (boxes);
+       status = _cairo_recording_surface_replay_with_clip (recording_pattern_get_surface (source),
+                                                           &source->matrix,
+                                                           dst, recording_clip);
+       _cairo_clip_destroy (recording_clip);
+
+       return status;
+    }
+
+    status = compositor->acquire (dst);
+    if (unlikely (status))
+       return status;
+
+    if (! need_clip_mask &&
+       (op == CAIRO_OPERATOR_CLEAR ||
+        extents->source_pattern.base.type == CAIRO_PATTERN_TYPE_SOLID))
+    {
+       const cairo_color_t *color;
+
+       if (op == CAIRO_OPERATOR_CLEAR) {
+           color = CAIRO_COLOR_TRANSPARENT;
+       } else {
+           color = &((cairo_solid_pattern_t *) &extents->source_pattern)->color;
+           if (op_is_source)
+               op = CAIRO_OPERATOR_SOURCE;
+       }
+
+       status = compositor->fill_boxes (dst, op, color, boxes);
+    }
+    else
+    {
+       cairo_surface_t *src, *mask = NULL;
+       cairo_pattern_t *source = &extents->source_pattern.base;
+       int src_x, src_y;
+       int mask_x = 0, mask_y = 0;
+
+       if (need_clip_mask) {
+           mask = traps_get_clip_surface (compositor, dst, extents->clip,
+                                          &extents->bounded);
+           if (unlikely (mask->status))
+               return mask->status;
+
+           mask_x = -extents->bounded.x;
+           mask_y = -extents->bounded.y;
+
+           if (op == CAIRO_OPERATOR_CLEAR) {
+               source = NULL;
+               op = CAIRO_OPERATOR_DEST_OUT;
+           }
+       } else if (op_is_source)
+           op = CAIRO_OPERATOR_SOURCE;
+
+       src = compositor->pattern_to_surface (dst, source, FALSE,
+                                             &extents->bounded,
+                                             &extents->source_sample_area,
+                                             &src_x, &src_y);
+       if (likely (src->status == CAIRO_STATUS_SUCCESS)) {
+           status = compositor->composite_boxes (dst, op, src, mask,
+                                                 src_x, src_y,
+                                                 mask_x, mask_y,
+                                                 0, 0,
+                                                 boxes, &extents->bounded);
+           cairo_surface_destroy (src);
+       } else
+           status = src->status;
+
+       cairo_surface_destroy (mask);
+    }
+
+    if (status == CAIRO_STATUS_SUCCESS && ! extents->is_bounded)
+       status = fixup_unbounded (compositor, extents, boxes);
+
+    compositor->release (dst);
+
+    return status;
+}
+
+static cairo_status_t
+upload_boxes (const cairo_traps_compositor_t *compositor,
+             cairo_composite_rectangles_t *extents,
+             cairo_boxes_t *boxes)
+{
+    cairo_surface_t *dst = extents->surface;
+    const cairo_pattern_t *source = &extents->source_pattern.base;
+    const cairo_surface_pattern_t *pattern;
+    cairo_surface_t *src;
+    cairo_rectangle_int_t limit;
+    cairo_int_status_t status;
+    int tx, ty;
+
+    pattern = (const cairo_surface_pattern_t *) source;
+    src = pattern->surface;
+    if (!(src->type == CAIRO_SURFACE_TYPE_IMAGE || src->type == dst->type))
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    if (! _cairo_matrix_is_integer_translation (&source->matrix, &tx, &ty))
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    if (_cairo_surface_is_snapshot (src))
+       src = _cairo_surface_snapshot_get_target (src);
+    if (_cairo_surface_is_observer (src))
+       src = _cairo_surface_observer_get_target (src);
+    if (_cairo_surface_is_subsurface (src)) {
+       _cairo_surface_subsurface_offset (src, &tx, &ty);
+       src = _cairo_surface_subsurface_get_target (src);
+    }
+
+    /* Check that the data is entirely within the image */
+    if (extents->bounded.x + tx < 0 || extents->bounded.y + ty < 0)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    _cairo_surface_get_extents (pattern->surface, &limit);
+    if (extents->bounded.x + extents->bounded.width  + tx > limit.width ||
+       extents->bounded.y + extents->bounded.height + ty > limit.height)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    if (pattern->surface->type == CAIRO_SURFACE_TYPE_IMAGE)
+       status = compositor->draw_image_boxes (dst,
+                                              (cairo_image_surface_t *)src,
+                                              boxes, tx, ty);
+    else
+       status = compositor->copy_boxes (dst, src, boxes, &extents->bounded,
+                                        tx, ty);
+
+    return status;
+}
+
+static cairo_int_status_t
+trim_extents_to_traps (cairo_composite_rectangles_t *extents,
+                      cairo_traps_t *traps)
+{
+    cairo_box_t box;
+
+    _cairo_traps_extents (traps, &box);
+    return _cairo_composite_rectangles_intersect_mask_extents (extents, &box);
+}
+
+static cairo_int_status_t
+trim_extents_to_tristrip (cairo_composite_rectangles_t *extents,
+                         cairo_tristrip_t *strip)
+{
+    cairo_box_t box;
+
+    _cairo_tristrip_extents (strip, &box);
+    return _cairo_composite_rectangles_intersect_mask_extents (extents, &box);
+}
+
+static cairo_int_status_t
+trim_extents_to_boxes (cairo_composite_rectangles_t *extents,
+                      cairo_boxes_t *boxes)
+{
+    cairo_box_t box;
+
+    _cairo_boxes_extents (boxes, &box);
+    return _cairo_composite_rectangles_intersect_mask_extents (extents, &box);
+}
+
+static cairo_int_status_t
+boxes_for_traps (cairo_boxes_t *boxes,
+                cairo_traps_t *traps,
+                cairo_antialias_t antialias)
+{
+    int i;
+
+    /* first check that the traps are rectilinear */
+    if (antialias == CAIRO_ANTIALIAS_NONE) {
+       for (i = 0; i < traps->num_traps; i++) {
+           const cairo_trapezoid_t *t = &traps->traps[i];
+           if (_cairo_fixed_integer_round_down (t->left.p1.x) !=
+               _cairo_fixed_integer_round_down (t->left.p2.x) ||
+               _cairo_fixed_integer_round_down (t->right.p1.x) !=
+               _cairo_fixed_integer_round_down (t->right.p2.x))
+           {
+               return CAIRO_INT_STATUS_UNSUPPORTED;
+           }
+       }
+    } else {
+       for (i = 0; i < traps->num_traps; i++) {
+           const cairo_trapezoid_t *t = &traps->traps[i];
+           if (t->left.p1.x != t->left.p2.x || t->right.p1.x != t->right.p2.x)
+               return CAIRO_INT_STATUS_UNSUPPORTED;
+       }
+    }
+
+    _cairo_boxes_init (boxes);
+
+    boxes->num_boxes    = traps->num_traps;
+    boxes->chunks.base  = (cairo_box_t *) traps->traps;
+    boxes->chunks.count = traps->num_traps;
+    boxes->chunks.size  = traps->num_traps;
+
+    if (antialias != CAIRO_ANTIALIAS_NONE) {
+       for (i = 0; i < traps->num_traps; i++) {
+           /* Note the traps and boxes alias so we need to take the local copies first. */
+           cairo_fixed_t x1 = traps->traps[i].left.p1.x;
+           cairo_fixed_t x2 = traps->traps[i].right.p1.x;
+           cairo_fixed_t y1 = traps->traps[i].top;
+           cairo_fixed_t y2 = traps->traps[i].bottom;
+
+           boxes->chunks.base[i].p1.x = x1;
+           boxes->chunks.base[i].p1.y = y1;
+           boxes->chunks.base[i].p2.x = x2;
+           boxes->chunks.base[i].p2.y = y2;
+
+           if (boxes->is_pixel_aligned) {
+               boxes->is_pixel_aligned =
+                   _cairo_fixed_is_integer (x1) && _cairo_fixed_is_integer (y1) &&
+                   _cairo_fixed_is_integer (x2) && _cairo_fixed_is_integer (y2);
+           }
+       }
+    } else {
+       boxes->is_pixel_aligned = TRUE;
+
+       for (i = 0; i < traps->num_traps; i++) {
+           /* Note the traps and boxes alias so we need to take the local copies first. */
+           cairo_fixed_t x1 = traps->traps[i].left.p1.x;
+           cairo_fixed_t x2 = traps->traps[i].right.p1.x;
+           cairo_fixed_t y1 = traps->traps[i].top;
+           cairo_fixed_t y2 = traps->traps[i].bottom;
+
+           /* round down here to match Pixman's behavior when using traps. */
+           boxes->chunks.base[i].p1.x = _cairo_fixed_round_down (x1);
+           boxes->chunks.base[i].p1.y = _cairo_fixed_round_down (y1);
+           boxes->chunks.base[i].p2.x = _cairo_fixed_round_down (x2);
+           boxes->chunks.base[i].p2.y = _cairo_fixed_round_down (y2);
+       }
+    }
+
+    return CAIRO_INT_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+clip_and_composite_boxes (const cairo_traps_compositor_t *compositor,
+                         cairo_composite_rectangles_t *extents,
+                         cairo_boxes_t *boxes);
+
+static cairo_status_t
+clip_and_composite_polygon (const cairo_traps_compositor_t *compositor,
+                           cairo_composite_rectangles_t *extents,
+                           cairo_polygon_t *polygon,
+                           cairo_antialias_t antialias,
+                           cairo_fill_rule_t fill_rule)
+{
+    composite_traps_info_t traps;
+    cairo_surface_t *dst = extents->surface;
+    cairo_bool_t clip_surface = ! _cairo_clip_is_region (extents->clip);
+    cairo_int_status_t status;
+
+    if (polygon->num_edges == 0) {
+       status = CAIRO_INT_STATUS_SUCCESS;
+
+       if (! extents->is_bounded) {
+           cairo_region_t *clip_region = _cairo_clip_get_region (extents->clip);
+
+           if (clip_region &&
+               cairo_region_contains_rectangle (clip_region,
+                                                &extents->unbounded) == CAIRO_REGION_OVERLAP_IN)
+               clip_region = NULL;
+
+           if (clip_region != NULL) {
+               status = compositor->set_clip_region (dst, clip_region);
+               if (unlikely (status))
+                   return status;
+           }
+
+           if (clip_surface)
+               status = fixup_unbounded_with_mask (compositor, extents);
+           else
+               status = fixup_unbounded (compositor, extents, NULL);
+
+           if (clip_region != NULL)
+               compositor->set_clip_region (dst, NULL);
+       }
+
+       return status;
+    }
+
+    if (extents->clip->path != NULL && extents->is_bounded) {
+       cairo_polygon_t clipper;
+       cairo_fill_rule_t clipper_fill_rule;
+       cairo_antialias_t clipper_antialias;
+
+       status = _cairo_clip_get_polygon (extents->clip,
+                                         &clipper,
+                                         &clipper_fill_rule,
+                                         &clipper_antialias);
+       if (likely (status == CAIRO_INT_STATUS_SUCCESS)) {
+           if (clipper_antialias == antialias) {
+               status = _cairo_polygon_intersect (polygon, fill_rule,
+                                                  &clipper, clipper_fill_rule);
+               if (likely (status == CAIRO_INT_STATUS_SUCCESS)) {
+                   cairo_clip_t * clip = _cairo_clip_copy_region (extents->clip);
+                   _cairo_clip_destroy (extents->clip);
+                   extents->clip = clip;
+
+                   fill_rule = CAIRO_FILL_RULE_WINDING;
+               }
+               _cairo_polygon_fini (&clipper);
+           }
+       }
+    }
+
+    _cairo_traps_init (&traps.traps);
+
+    status = _cairo_bentley_ottmann_tessellate_polygon (&traps.traps, polygon, fill_rule);
+    if (unlikely (status))
+       goto CLEANUP_TRAPS;
+
+    status = trim_extents_to_traps (extents, &traps.traps);
+    if (unlikely (status))
+       goto CLEANUP_TRAPS;
+
+    /* Use a fast path if the trapezoids consist of a set of boxes.  */
+    status = CAIRO_INT_STATUS_UNSUPPORTED;
+    if (1) {
+       cairo_boxes_t boxes;
+
+       status = boxes_for_traps (&boxes, &traps.traps, antialias);
+       if (status == CAIRO_INT_STATUS_SUCCESS) {
+           status = clip_and_composite_boxes (compositor, extents, &boxes);
+           /* XXX need to reconstruct the traps! */
+           assert (status != CAIRO_INT_STATUS_UNSUPPORTED);
+       }
+    }
+    if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
+       /* Otherwise render the trapezoids to a mask and composite in the usual
+        * fashion.
+        */
+       unsigned int flags = 0;
+
+       /* For unbounded operations, the X11 server will estimate the
+        * affected rectangle and apply the operation to that. However,
+        * there are cases where this is an overestimate (e.g. the
+        * clip-fill-{eo,nz}-unbounded test).
+        *
+        * The clip will trim that overestimate to our expectations.
+        */
+       if (! extents->is_bounded)
+               flags |= FORCE_CLIP_REGION;
+
+       traps.antialias = antialias;
+       status = clip_and_composite (compositor, extents,
+                                    composite_traps, NULL, &traps,
+                                    need_unbounded_clip (extents) | flags);
+    }
+
+CLEANUP_TRAPS:
+    _cairo_traps_fini (&traps.traps);
+
+    return status;
+}
+
+struct composite_opacity_info {
+    const cairo_traps_compositor_t *compositor;
+    uint8_t op;
+    cairo_surface_t *dst;
+    cairo_surface_t *src;
+    int src_x, src_y;
+    double opacity;
+};
+
+static void composite_opacity(void *closure,
+                             int16_t x, int16_t y,
+                             int16_t w, int16_t h,
+                             uint16_t coverage)
+{
+    struct composite_opacity_info *info = closure;
+    const cairo_traps_compositor_t *compositor = info->compositor;
+    cairo_surface_t *mask;
+    int mask_x, mask_y;
+    cairo_color_t color;
+    cairo_solid_pattern_t solid;
+
+    _cairo_color_init_rgba (&color, 0, 0, 0, info->opacity * coverage);
+    _cairo_pattern_init_solid (&solid, &color);
+    mask = compositor->pattern_to_surface (info->dst, &solid.base, TRUE,
+                                          &_cairo_unbounded_rectangle,
+                                          &_cairo_unbounded_rectangle,
+                                          &mask_x, &mask_y);
+    if (likely (mask->status == CAIRO_STATUS_SUCCESS)) {
+       if (info->src) {
+           compositor->composite (info->dst, info->op, info->src, mask,
+                                  x + info->src_x,  y + info->src_y,
+                                  mask_x,           mask_y,
+                                  x,                y,
+                                  w,                h);
+       } else {
+           compositor->composite (info->dst, info->op, mask, NULL,
+                                  mask_x,            mask_y,
+                                  0,                 0,
+                                  x,                 y,
+                                  w,                 h);
+       }
+    }
+
+    cairo_surface_destroy (mask);
+}
+
+
+static cairo_int_status_t
+composite_opacity_boxes (const cairo_traps_compositor_t *compositor,
+                        cairo_surface_t                *dst,
+                        void                           *closure,
+                        cairo_operator_t                op,
+                        cairo_surface_t                *src,
+                        int                             src_x,
+                        int                             src_y,
+                        int                             dst_x,
+                        int                             dst_y,
+                        const cairo_rectangle_int_t    *extents,
+                        cairo_clip_t                   *clip)
+{
+    const cairo_solid_pattern_t *mask = closure;
+    struct composite_opacity_info info;
+    int i;
+
+    info.compositor = compositor;
+    info.op = op;
+    info.dst = dst;
+
+    info.src = src;
+    info.src_x = src_x;
+    info.src_y = src_y;
+
+    info.opacity = mask->color.alpha / (double) 0xffff;
+
+    /* XXX for lots of boxes create a clip region for the fully opaque areas */
+    for (i = 0; i < clip->num_boxes; i++)
+       do_unaligned_box(composite_opacity, &info,
+                        &clip->boxes[i], dst_x, dst_y);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+composite_boxes (const cairo_traps_compositor_t *compositor,
+                cairo_surface_t                *dst,
+                void                           *closure,
+                cairo_operator_t                op,
+                cairo_surface_t                *src,
+                int                             src_x,
+                int                             src_y,
+                int                             dst_x,
+                int                             dst_y,
+                const cairo_rectangle_int_t    *extents,
+                cairo_clip_t                   *clip)
+{
+    cairo_traps_t traps;
+    cairo_status_t status;
+
+    status = _cairo_traps_init_boxes (&traps, closure);
+    if (unlikely (status))
+       return status;
+
+    status = compositor->composite_traps (dst, op, src,
+                                         src_x - dst_x, src_y - dst_y,
+                                         dst_x, dst_y,
+                                         extents,
+                                         CAIRO_ANTIALIAS_DEFAULT, &traps);
+    _cairo_traps_fini (&traps);
+
+    return status;
+}
+
+static cairo_status_t
+clip_and_composite_boxes (const cairo_traps_compositor_t *compositor,
+                         cairo_composite_rectangles_t *extents,
+                         cairo_boxes_t *boxes)
+{
+    cairo_int_status_t status;
+
+    if (boxes->num_boxes == 0 && extents->is_bounded)
+       return CAIRO_STATUS_SUCCESS;
+
+    status = trim_extents_to_boxes (extents, boxes);
+    if (unlikely (status))
+       return status;
+
+    if (boxes->is_pixel_aligned && extents->clip->path == NULL &&
+       extents->source_pattern.base.type == CAIRO_PATTERN_TYPE_SURFACE &&
+       (op_reduces_to_source (extents) ||
+        (extents->op == CAIRO_OPERATOR_OVER &&
+         (extents->source_pattern.surface.surface->content & CAIRO_CONTENT_ALPHA) == 0)))
+    {
+       status = upload_boxes (compositor, extents, boxes);
+       if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+           return status;
+    }
+
+    /* Can we reduce drawing through a clip-mask to simply drawing the clip? */
+    if (extents->clip->path != NULL && extents->is_bounded) {
+       cairo_polygon_t polygon;
+       cairo_fill_rule_t fill_rule;
+       cairo_antialias_t antialias;
+       cairo_clip_t *clip;
+
+       clip = _cairo_clip_copy (extents->clip);
+       clip = _cairo_clip_intersect_boxes (clip, boxes);
+       status = _cairo_clip_get_polygon (clip, &polygon,
+                                         &fill_rule, &antialias);
+       if (clip->path)
+           _cairo_clip_path_destroy (clip->path);
+       clip->path = NULL;
+       if (likely (status == CAIRO_INT_STATUS_SUCCESS)) {
+           cairo_clip_t *saved_clip = extents->clip;
+           extents->clip = clip;
+
+           status = clip_and_composite_polygon (compositor, extents, &polygon,
+                                                antialias, fill_rule);
+
+           clip = extents->clip;
+           extents->clip = saved_clip;
+
+           _cairo_polygon_fini (&polygon);
+       }
+       _cairo_clip_destroy (clip);
+
+       if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+           return status;
+    }
+
+    /* Use a fast path if the boxes are pixel aligned (or nearly aligned!) */
+    if (boxes->is_pixel_aligned) {
+       status = composite_aligned_boxes (compositor, extents, boxes);
+       if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+           return status;
+    }
+
+    return clip_and_composite (compositor, extents,
+                              composite_boxes, composite_boxes, boxes,
+                              need_unbounded_clip (extents));
+}
+
+static cairo_int_status_t
+composite_traps_as_boxes (const cairo_traps_compositor_t *compositor,
+                         cairo_composite_rectangles_t *extents,
+                         composite_traps_info_t *info)
+{
+    cairo_boxes_t boxes;
+
+    if (! _cairo_traps_to_boxes (&info->traps, info->antialias, &boxes))
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    return clip_and_composite_boxes (compositor, extents, &boxes);
+}
+
+static cairo_int_status_t
+clip_and_composite_traps (const cairo_traps_compositor_t *compositor,
+                         cairo_composite_rectangles_t *extents,
+                         composite_traps_info_t *info)
+{
+    cairo_int_status_t status;
+
+    status = trim_extents_to_traps (extents, &info->traps);
+    if (unlikely (status != CAIRO_INT_STATUS_SUCCESS))
+       return status;
+
+    status = composite_traps_as_boxes (compositor, extents, info);
+    if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
+       unsigned int flags = 0;
+
+       /* For unbounded operations, the X11 server will estimate the
+        * affected rectangle and apply the operation to that. However,
+        * there are cases where this is an overestimate (e.g. the
+        * clip-fill-{eo,nz}-unbounded test).
+        *
+        * The clip will trim that overestimate to our expectations.
+        */
+       if (! extents->is_bounded)
+           flags |= FORCE_CLIP_REGION;
+
+       status = clip_and_composite (compositor, extents,
+                                    composite_traps, NULL, info,
+                                    need_unbounded_clip (extents) | flags);
+    }
+
+    return status;
+}
+
+static cairo_int_status_t
+clip_and_composite_tristrip (const cairo_traps_compositor_t *compositor,
+                            cairo_composite_rectangles_t *extents,
+                            composite_tristrip_info_t *info)
+{
+    cairo_int_status_t status;
+    unsigned int flags = 0;
+
+    status = trim_extents_to_tristrip (extents, &info->strip);
+    if (unlikely (status != CAIRO_INT_STATUS_SUCCESS))
+       return status;
+
+    if (! extents->is_bounded)
+       flags |= FORCE_CLIP_REGION;
+
+    status = clip_and_composite (compositor, extents,
+                                composite_tristrip, NULL, info,
+                                need_unbounded_clip (extents) | flags);
+
+    return status;
+}
+
+struct composite_mask {
+    cairo_surface_t *mask;
+    int mask_x, mask_y;
+};
+
+static cairo_int_status_t
+composite_mask (const cairo_traps_compositor_t *compositor,
+               cairo_surface_t                 *dst,
+               void                            *closure,
+               cairo_operator_t                 op,
+               cairo_surface_t                 *src,
+               int                              src_x,
+               int                              src_y,
+               int                              dst_x,
+               int                              dst_y,
+               const cairo_rectangle_int_t     *extents,
+               cairo_clip_t                    *clip)
+{
+    struct composite_mask *data = closure;
+
+    if (src != NULL) {
+       compositor->composite (dst, op, src, data->mask,
+                              extents->x + src_x, extents->y + src_y,
+                              extents->x + data->mask_x, extents->y + data->mask_y,
+                              extents->x - dst_x,  extents->y - dst_y,
+                              extents->width,      extents->height);
+    } else {
+       compositor->composite (dst, op, data->mask, NULL,
+                              extents->x + data->mask_x, extents->y + data->mask_y,
+                              0, 0,
+                              extents->x - dst_x,  extents->y - dst_y,
+                              extents->width,      extents->height);
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+struct composite_box_info {
+    const cairo_traps_compositor_t *compositor;
+    cairo_surface_t *dst;
+    cairo_surface_t *src;
+    int src_x, src_y;
+    uint8_t op;
+};
+
+static void composite_box(void *closure,
+                         int16_t x, int16_t y,
+                         int16_t w, int16_t h,
+                         uint16_t coverage)
+{
+    struct composite_box_info *info = closure;
+    const cairo_traps_compositor_t *compositor = info->compositor;
+
+    if (! CAIRO_ALPHA_SHORT_IS_OPAQUE (coverage)) {
+       cairo_surface_t *mask;
+       cairo_color_t color;
+       cairo_solid_pattern_t solid;
+       int mask_x, mask_y;
+
+       _cairo_color_init_rgba (&color, 0, 0, 0, coverage / (double)0xffff);
+       _cairo_pattern_init_solid (&solid, &color);
+
+       mask = compositor->pattern_to_surface (info->dst, &solid.base, FALSE,
+                                              &_cairo_unbounded_rectangle,
+                                              &_cairo_unbounded_rectangle,
+                                              &mask_x, &mask_y);
+
+       if (likely (mask->status == CAIRO_STATUS_SUCCESS)) {
+           compositor->composite (info->dst, info->op, info->src, mask,
+                                  x + info->src_x,  y + info->src_y,
+                                  mask_x,           mask_y,
+                                  x,                y,
+                                  w,                h);
+       }
+
+       cairo_surface_destroy (mask);
+    } else {
+       compositor->composite (info->dst, info->op, info->src, NULL,
+                              x + info->src_x,  y + info->src_y,
+                              0,                0,
+                              x,                y,
+                              w,                h);
+    }
+}
+
+static cairo_int_status_t
+composite_mask_clip_boxes (const cairo_traps_compositor_t *compositor,
+                          cairo_surface_t              *dst,
+                          void                         *closure,
+                          cairo_operator_t              op,
+                          cairo_surface_t              *src,
+                          int                           src_x,
+                          int                           src_y,
+                          int                           dst_x,
+                          int                           dst_y,
+                          const cairo_rectangle_int_t  *extents,
+                          cairo_clip_t                 *clip)
+{
+    struct composite_mask *data = closure;
+    struct composite_box_info info;
+    int i;
+
+    info.compositor = compositor;
+    info.op = CAIRO_OPERATOR_SOURCE;
+    info.dst = dst;
+    info.src = data->mask;
+    info.src_x = data->mask_x;
+    info.src_y = data->mask_y;
+
+    info.src_x += dst_x;
+    info.src_y += dst_y;
+
+    for (i = 0; i < clip->num_boxes; i++)
+       do_unaligned_box(composite_box, &info, &clip->boxes[i], dst_x, dst_y);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+composite_mask_clip (const cairo_traps_compositor_t *compositor,
+                    cairo_surface_t                    *dst,
+                    void                               *closure,
+                    cairo_operator_t                    op,
+                    cairo_surface_t                    *src,
+                    int                                 src_x,
+                    int                                 src_y,
+                    int                                 dst_x,
+                    int                                 dst_y,
+                    const cairo_rectangle_int_t        *extents,
+                    cairo_clip_t                       *clip)
+{
+    struct composite_mask *data = closure;
+    cairo_polygon_t polygon;
+    cairo_fill_rule_t fill_rule;
+    composite_traps_info_t info;
+    cairo_status_t status;
+
+    status = _cairo_clip_get_polygon (clip, &polygon,
+                                     &fill_rule, &info.antialias);
+    if (unlikely (status))
+       return status;
+
+    _cairo_traps_init (&info.traps);
+    status = _cairo_bentley_ottmann_tessellate_polygon (&info.traps,
+                                                       &polygon,
+                                                       fill_rule);
+    _cairo_polygon_fini (&polygon);
+    if (unlikely (status))
+       return status;
+
+    status = composite_traps (compositor, dst, &info,
+                             CAIRO_OPERATOR_SOURCE,
+                             data->mask,
+                             data->mask_x + dst_x, data->mask_y + dst_y,
+                             dst_x, dst_y,
+                             extents, NULL);
+    _cairo_traps_fini (&info.traps);
+
+    return status;
+}
+
+/* high-level compositor interface */
+
+static cairo_int_status_t
+_cairo_traps_compositor_paint (const cairo_compositor_t *_compositor,
+                              cairo_composite_rectangles_t *extents)
+{
+    cairo_traps_compositor_t *compositor = (cairo_traps_compositor_t*)_compositor;
+    cairo_boxes_t boxes;
+    cairo_int_status_t status;
+
+     _cairo_clip_steal_boxes (extents->clip, &boxes);
+     status = clip_and_composite_boxes (compositor, extents, &boxes);
+     _cairo_clip_unsteal_boxes (extents->clip, &boxes);
+
+    return status;
+}
+
+static cairo_int_status_t
+_cairo_traps_compositor_mask (const cairo_compositor_t *_compositor,
+                             cairo_composite_rectangles_t *extents)
+{
+    const cairo_traps_compositor_t *compositor = (cairo_traps_compositor_t*)_compositor;
+    cairo_int_status_t status;
+
+    if (extents->mask_pattern.base.type == CAIRO_PATTERN_TYPE_SOLID &&
+       extents->clip->path == NULL) {
+       status = clip_and_composite (compositor, extents,
+                                    composite_opacity_boxes,
+                                    composite_opacity_boxes,
+                                    &extents->mask_pattern,
+                                    need_unbounded_clip (extents));
+    } else {
+       struct composite_mask data;
+
+       data.mask = compositor->pattern_to_surface (extents->surface,
+                                                   &extents->mask_pattern.base,
+                                                   TRUE,
+                                                   &extents->bounded,
+                                                   &extents->mask_sample_area,
+                                                   &data.mask_x,
+                                                   &data.mask_y);
+
+       status = clip_and_composite (compositor, extents,
+                                    composite_mask,
+                                    extents->clip->path ? composite_mask_clip : composite_mask_clip_boxes,
+                                    &data, need_bounded_clip (extents));
+
+       cairo_surface_destroy (data.mask);
+    }
+
+    return status;
+}
+
+static cairo_int_status_t
+_cairo_traps_compositor_stroke (const cairo_compositor_t *_compositor,
+                               cairo_composite_rectangles_t *extents,
+                               const cairo_path_fixed_t *path,
+                               const cairo_stroke_style_t *style,
+                               const cairo_matrix_t    *ctm,
+                               const cairo_matrix_t    *ctm_inverse,
+                               double                   tolerance,
+                               cairo_antialias_t        antialias)
+{
+    const cairo_traps_compositor_t *compositor = (cairo_traps_compositor_t *)_compositor;
+    cairo_int_status_t status;
+
+    status = CAIRO_INT_STATUS_UNSUPPORTED;
+    if (_cairo_path_fixed_stroke_is_rectilinear (path)) {
+       cairo_boxes_t boxes;
+
+       _cairo_boxes_init_with_clip (&boxes, extents->clip);
+       status = _cairo_path_fixed_stroke_rectilinear_to_boxes (path,
+                                                               style,
+                                                               ctm,
+                                                               antialias,
+                                                               &boxes);
+       if (likely (status == CAIRO_INT_STATUS_SUCCESS))
+           status = clip_and_composite_boxes (compositor, extents, &boxes);
+       _cairo_boxes_fini (&boxes);
+    }
+
+    if (status == CAIRO_INT_STATUS_UNSUPPORTED && 0 &&
+       _cairo_clip_is_region (extents->clip)) /* XXX */
+    {
+       composite_tristrip_info_t info;
+
+       info.antialias = antialias;
+       _cairo_tristrip_init_with_clip (&info.strip, extents->clip);
+       status = _cairo_path_fixed_stroke_to_tristrip (path, style,
+                                                      ctm, ctm_inverse,
+                                                      tolerance,
+                                                      &info.strip);
+       if (likely (status == CAIRO_INT_STATUS_SUCCESS))
+           status = clip_and_composite_tristrip (compositor, extents, &info);
+       _cairo_tristrip_fini (&info.strip);
+    }
+
+    if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
+       composite_traps_info_t info;
+
+       info.antialias = antialias;
+       _cairo_traps_init_with_clip (&info.traps, extents->clip);
+       status = _cairo_path_fixed_stroke_to_traps (path, style,
+                                                   ctm, ctm_inverse,
+                                                   tolerance,
+                                                   &info.traps);
+       if (likely (status == CAIRO_INT_STATUS_SUCCESS))
+           status = clip_and_composite_traps (compositor, extents, &info);
+       _cairo_traps_fini (&info.traps);
+    }
+
+    return status;
+}
+
+static cairo_int_status_t
+_cairo_traps_compositor_fill (const cairo_compositor_t *_compositor,
+                             cairo_composite_rectangles_t *extents,
+                             const cairo_path_fixed_t  *path,
+                             cairo_fill_rule_t          fill_rule,
+                             double                     tolerance,
+                             cairo_antialias_t          antialias)
+{
+    const cairo_traps_compositor_t *compositor = (cairo_traps_compositor_t *)_compositor;
+    cairo_int_status_t status;
+
+    status = CAIRO_INT_STATUS_UNSUPPORTED;
+    if (_cairo_path_fixed_fill_is_rectilinear (path)) {
+       cairo_boxes_t boxes;
+
+       _cairo_boxes_init_with_clip (&boxes, extents->clip);
+       status = _cairo_path_fixed_fill_rectilinear_to_boxes (path,
+                                                             fill_rule,
+                                                             antialias,
+                                                             &boxes);
+       if (likely (status == CAIRO_INT_STATUS_SUCCESS))
+           status = clip_and_composite_boxes (compositor, extents, &boxes);
+       _cairo_boxes_fini (&boxes);
+    }
+
+    if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
+       cairo_polygon_t polygon;
+
+#if 0
+       if (extents->mask.width  > extents->unbounded.width ||
+           extents->mask.height > extents->unbounded.height)
+       {
+           cairo_box_t limits;
+           _cairo_box_from_rectangle (&limits, &extents->unbounded);
+           _cairo_polygon_init (&polygon, &limits, 1);
+       }
+       else
+       {
+           _cairo_polygon_init (&polygon, NULL, 0);
+       }
+
+       status = _cairo_path_fixed_fill_to_polygon (path, tolerance, &polygon);
+       if (likely (status == CAIRO_INT_STATUS_SUCCESS)) {
+           status = _cairo_polygon_intersect_with_boxes (&polygon, &fill_rule,
+                                                         extents->clip->boxes,
+                                                         extents->clip->num_boxes);
+       }
+#else
+       _cairo_polygon_init_with_clip (&polygon, extents->clip);
+       status = _cairo_path_fixed_fill_to_polygon (path, tolerance, &polygon);
+#endif
+       if (likely (status == CAIRO_INT_STATUS_SUCCESS)) {
+           status = clip_and_composite_polygon (compositor, extents, &polygon,
+                                                antialias, fill_rule);
+       }
+       _cairo_polygon_fini (&polygon);
+    }
+
+    return status;
+}
+
+static cairo_int_status_t
+composite_glyphs (const cairo_traps_compositor_t *compositor,
+                 cairo_surface_t       *dst,
+                 void *closure,
+                 cairo_operator_t       op,
+                 cairo_surface_t       *src,
+                 int src_x, int src_y,
+                 int dst_x, int dst_y,
+                 const cairo_rectangle_int_t *extents,
+                 cairo_clip_t          *clip)
+{
+    cairo_composite_glyphs_info_t *info = closure;
+
+    if (op == CAIRO_OPERATOR_ADD && (dst->content & CAIRO_CONTENT_COLOR) == 0)
+       info->use_mask = 0;
+
+    return compositor->composite_glyphs (dst, op, src,
+                                        src_x, src_y,
+                                        dst_x, dst_y,
+                                        info);
+}
+
+static cairo_int_status_t
+_cairo_traps_compositor_glyphs (const cairo_compositor_t       *_compositor,
+                               cairo_composite_rectangles_t    *extents,
+                               cairo_scaled_font_t             *scaled_font,
+                               cairo_glyph_t                   *glyphs,
+                               int                              num_glyphs,
+                               cairo_bool_t                     overlap)
+{
+    const cairo_traps_compositor_t *compositor = (cairo_traps_compositor_t *)_compositor;
+    cairo_int_status_t status;
+
+    _cairo_scaled_font_freeze_cache (scaled_font);
+    status = compositor->check_composite_glyphs (extents,
+                                                scaled_font, glyphs,
+                                                &num_glyphs);
+    if (likely (status == CAIRO_INT_STATUS_SUCCESS)) {
+       cairo_composite_glyphs_info_t info;
+       unsigned flags = 0;
+
+       info.font = scaled_font;
+       info.glyphs = glyphs;
+       info.num_glyphs = num_glyphs;
+       info.use_mask = overlap || ! extents->is_bounded;
+       info.extents = extents->bounded;
+
+       if (extents->mask.width > extents->bounded.width ||
+           extents->mask.height > extents->bounded.height)
+       {
+           flags |= FORCE_CLIP_REGION;
+       }
+
+       status = clip_and_composite (compositor, extents,
+                                    composite_glyphs, NULL, &info,
+                                    need_bounded_clip (extents) |
+                                    flags);
+    }
+    _cairo_scaled_font_thaw_cache (scaled_font);
+
+    return status;
+}
+
+void
+_cairo_traps_compositor_init (cairo_traps_compositor_t *compositor,
+                             const cairo_compositor_t  *delegate)
+{
+    compositor->base.delegate = delegate;
+
+    compositor->base.paint = _cairo_traps_compositor_paint;
+    compositor->base.mask = _cairo_traps_compositor_mask;
+    compositor->base.fill = _cairo_traps_compositor_fill;
+    compositor->base.stroke = _cairo_traps_compositor_stroke;
+    compositor->base.glyphs = _cairo_traps_compositor_glyphs;
+}
diff --git a/src/cairo-traps-private.h b/src/cairo-traps-private.h
new file mode 100644 (file)
index 0000000..5b17719
--- /dev/null
@@ -0,0 +1,126 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *     Carl D. Worth <cworth@cworth.org>
+ */
+
+#ifndef CAIRO_TRAPS_PRIVATE_H
+#define CAIRO_TRAPS_PRIVATE_H
+
+#include "cairo-compiler-private.h"
+#include "cairo-error-private.h"
+#include "cairo-types-private.h"
+
+CAIRO_BEGIN_DECLS
+
+struct _cairo_traps {
+    cairo_status_t status;
+
+    const cairo_box_t *limits;
+    int num_limits;
+
+    unsigned int maybe_region : 1; /* hint: 0 implies that it cannot be */
+    unsigned int has_intersections : 1;
+    unsigned int is_rectilinear : 1;
+    unsigned int is_rectangular : 1;
+
+    int num_traps;
+    int traps_size;
+    cairo_trapezoid_t *traps;
+    cairo_trapezoid_t  traps_embedded[16];
+};
+
+/* cairo-traps.c */
+cairo_private void
+_cairo_traps_init (cairo_traps_t *traps);
+
+cairo_private void
+_cairo_traps_init_with_clip (cairo_traps_t *traps,
+                            const cairo_clip_t *clip);
+
+cairo_private void
+_cairo_traps_limit (cairo_traps_t      *traps,
+                   const cairo_box_t   *boxes,
+                   int                  num_boxes);
+
+cairo_private cairo_status_t
+_cairo_traps_init_boxes (cairo_traps_t     *traps,
+                        const cairo_boxes_t *boxes);
+
+cairo_private void
+_cairo_traps_clear (cairo_traps_t *traps);
+
+cairo_private void
+_cairo_traps_fini (cairo_traps_t *traps);
+
+#define _cairo_traps_status(T) (T)->status
+
+cairo_private void
+_cairo_traps_translate (cairo_traps_t *traps, int x, int y);
+
+cairo_private cairo_status_t
+_cairo_traps_tessellate_rectangle (cairo_traps_t *traps,
+                                  const cairo_point_t *top_left,
+                                  const cairo_point_t *bottom_right);
+
+cairo_private void
+_cairo_traps_add_trap (cairo_traps_t *traps,
+                      cairo_fixed_t top, cairo_fixed_t bottom,
+                      cairo_line_t *left, cairo_line_t *right);
+
+cairo_private int
+_cairo_traps_contain (const cairo_traps_t *traps,
+                     double x, double y);
+
+cairo_private void
+_cairo_traps_extents (const cairo_traps_t *traps,
+                     cairo_box_t         *extents);
+
+cairo_private cairo_int_status_t
+_cairo_traps_extract_region (cairo_traps_t  *traps,
+                            cairo_antialias_t antialias,
+                            cairo_region_t **region);
+
+cairo_private cairo_bool_t
+_cairo_traps_to_boxes (cairo_traps_t *traps,
+                      cairo_antialias_t antialias,
+                      cairo_boxes_t *boxes);
+
+cairo_private cairo_status_t
+_cairo_traps_path (const cairo_traps_t *traps,
+                  cairo_path_fixed_t  *path);
+
+CAIRO_END_DECLS
+
+#endif /* CAIRO_TRAPS_PRIVATE_H */
diff --git a/src/cairo-traps.c b/src/cairo-traps.c
new file mode 100644 (file)
index 0000000..a6e7f94
--- /dev/null
@@ -0,0 +1,717 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/*
+ * Copyright © 2002 Keith Packard
+ * Copyright © 2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Keith Packard
+ *
+ * Contributor(s):
+ *     Keith R. Packard <keithp@keithp.com>
+ *     Carl D. Worth <cworth@cworth.org>
+ *
+ * 2002-07-15: Converted from XRenderCompositeDoublePoly to #cairo_trap_t. Carl D. Worth
+ */
+
+#include "cairoint.h"
+
+#include "cairo-boxes-private.h"
+#include "cairo-error-private.h"
+#include "cairo-region-private.h"
+#include "cairo-slope-private.h"
+#include "cairo-traps-private.h"
+
+/* private functions */
+
+void
+_cairo_traps_init (cairo_traps_t *traps)
+{
+    VG (VALGRIND_MAKE_MEM_UNDEFINED (traps, sizeof (cairo_traps_t)));
+
+    traps->status = CAIRO_STATUS_SUCCESS;
+
+    traps->maybe_region = 1;
+    traps->is_rectilinear = 0;
+    traps->is_rectangular = 0;
+
+    traps->num_traps = 0;
+
+    traps->traps_size = ARRAY_LENGTH (traps->traps_embedded);
+    traps->traps = traps->traps_embedded;
+
+    traps->num_limits = 0;
+    traps->has_intersections = FALSE;
+}
+
+void
+_cairo_traps_limit (cairo_traps_t      *traps,
+                   const cairo_box_t   *limits,
+                   int                  num_limits)
+{
+    traps->limits = limits;
+    traps->num_limits = num_limits;
+}
+
+void
+_cairo_traps_init_with_clip (cairo_traps_t *traps,
+                            const cairo_clip_t *clip)
+{
+    _cairo_traps_init (traps);
+    if (clip)
+       _cairo_traps_limit (traps, clip->boxes, clip->num_boxes);
+}
+
+void
+_cairo_traps_clear (cairo_traps_t *traps)
+{
+    traps->status = CAIRO_STATUS_SUCCESS;
+
+    traps->maybe_region = 1;
+    traps->is_rectilinear = 0;
+    traps->is_rectangular = 0;
+
+    traps->num_traps = 0;
+    traps->has_intersections = FALSE;
+}
+
+void
+_cairo_traps_fini (cairo_traps_t *traps)
+{
+    if (traps->traps != traps->traps_embedded)
+       free (traps->traps);
+
+    VG (VALGRIND_MAKE_MEM_NOACCESS (traps, sizeof (cairo_traps_t)));
+}
+
+/* make room for at least one more trap */
+static cairo_bool_t
+_cairo_traps_grow (cairo_traps_t *traps)
+{
+    cairo_trapezoid_t *new_traps;
+    int new_size = 4 * traps->traps_size;
+
+    if (CAIRO_INJECT_FAULT ()) {
+       traps->status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+       return FALSE;
+    }
+
+    if (traps->traps == traps->traps_embedded) {
+       new_traps = _cairo_malloc_ab (new_size, sizeof (cairo_trapezoid_t));
+       if (new_traps != NULL)
+           memcpy (new_traps, traps->traps, sizeof (traps->traps_embedded));
+    } else {
+       new_traps = _cairo_realloc_ab (traps->traps,
+                                      new_size, sizeof (cairo_trapezoid_t));
+    }
+
+    if (unlikely (new_traps == NULL)) {
+       traps->status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+       return FALSE;
+    }
+
+    traps->traps = new_traps;
+    traps->traps_size = new_size;
+    return TRUE;
+}
+
+void
+_cairo_traps_add_trap (cairo_traps_t *traps,
+                      cairo_fixed_t top, cairo_fixed_t bottom,
+                      cairo_line_t *left, cairo_line_t *right)
+{
+    cairo_trapezoid_t *trap;
+
+    if (unlikely (traps->num_traps == traps->traps_size)) {
+       if (unlikely (! _cairo_traps_grow (traps)))
+           return;
+    }
+
+    trap = &traps->traps[traps->num_traps++];
+    trap->top = top;
+    trap->bottom = bottom;
+    trap->left = *left;
+    trap->right = *right;
+}
+
+/**
+ * _cairo_traps_init_box:
+ * @traps: a #cairo_traps_t
+ * @box: an array box that will each be converted to a single trapezoid
+ *       to store in @traps.
+ *
+ * Initializes a #cairo_traps_t to contain an array of rectangular
+ * trapezoids.
+ **/
+cairo_status_t
+_cairo_traps_init_boxes (cairo_traps_t     *traps,
+                        const cairo_boxes_t *boxes)
+{
+    cairo_trapezoid_t *trap;
+    const struct _cairo_boxes_chunk *chunk;
+
+    _cairo_traps_init (traps);
+
+    while (traps->traps_size < boxes->num_boxes) {
+       if (unlikely (! _cairo_traps_grow (traps))) {
+           _cairo_traps_fini (traps);
+           return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+       }
+    }
+
+    traps->num_traps = boxes->num_boxes;
+    traps->is_rectilinear = TRUE;
+    traps->is_rectangular = TRUE;
+    traps->maybe_region = boxes->is_pixel_aligned;
+
+    trap = &traps->traps[0];
+    for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
+       const cairo_box_t *box;
+       int i;
+
+       box = chunk->base;
+       for (i = 0; i < chunk->count; i++) {
+           trap->top    = box->p1.y;
+           trap->bottom = box->p2.y;
+
+           trap->left.p1   = box->p1;
+           trap->left.p2.x = box->p1.x;
+           trap->left.p2.y = box->p2.y;
+
+           trap->right.p1.x = box->p2.x;
+           trap->right.p1.y = box->p1.y;
+           trap->right.p2   = box->p2;
+
+           box++, trap++;
+       }
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_status_t
+_cairo_traps_tessellate_rectangle (cairo_traps_t *traps,
+                                  const cairo_point_t *top_left,
+                                  const cairo_point_t *bottom_right)
+{
+    cairo_line_t left;
+    cairo_line_t right;
+    cairo_fixed_t top, bottom;
+
+    if (top_left->y == bottom_right->y)
+       return CAIRO_STATUS_SUCCESS;
+
+    if (top_left->x == bottom_right->x)
+       return CAIRO_STATUS_SUCCESS;
+
+     left.p1.x =  left.p2.x = top_left->x;
+     left.p1.y = right.p1.y = top_left->y;
+    right.p1.x = right.p2.x = bottom_right->x;
+     left.p2.y = right.p2.y = bottom_right->y;
+
+     top = top_left->y;
+     bottom = bottom_right->y;
+
+    if (traps->num_limits) {
+       cairo_bool_t reversed;
+       int n;
+
+       /* support counter-clockwise winding for rectangular tessellation */
+       reversed = top_left->x > bottom_right->x;
+       if (reversed) {
+           right.p1.x = right.p2.x = top_left->x;
+           left.p1.x = left.p2.x = bottom_right->x;
+       }
+
+       for (n = 0; n < traps->num_limits; n++) {
+           const cairo_box_t *limits = &traps->limits[n];
+           cairo_line_t _left, _right;
+           cairo_fixed_t _top, _bottom;
+
+           if (top >= limits->p2.y)
+               continue;
+           if (bottom <= limits->p1.y)
+               continue;
+
+           /* Trivially reject if trapezoid is entirely to the right or
+            * to the left of the limits. */
+           if (left.p1.x >= limits->p2.x)
+               continue;
+           if (right.p1.x <= limits->p1.x)
+               continue;
+
+           /* Otherwise, clip the trapezoid to the limits. */
+           _top = top;
+           if (_top < limits->p1.y)
+               _top = limits->p1.y;
+
+           _bottom = bottom;
+           if (_bottom > limits->p2.y)
+               _bottom = limits->p2.y;
+
+           if (_bottom <= _top)
+               continue;
+
+           _left = left;
+           if (_left.p1.x < limits->p1.x) {
+               _left.p1.x = limits->p1.x;
+               _left.p1.y = limits->p1.y;
+               _left.p2.x = limits->p1.x;
+               _left.p2.y = limits->p2.y;
+           }
+
+           _right = right;
+           if (_right.p1.x > limits->p2.x) {
+               _right.p1.x = limits->p2.x;
+               _right.p1.y = limits->p1.y;
+               _right.p2.x = limits->p2.x;
+               _right.p2.y = limits->p2.y;
+           }
+
+           if (left.p1.x >= right.p1.x)
+               continue;
+
+           if (reversed)
+               _cairo_traps_add_trap (traps, _top, _bottom, &_right, &_left);
+           else
+               _cairo_traps_add_trap (traps, _top, _bottom, &_left, &_right);
+       }
+    } else {
+       _cairo_traps_add_trap (traps, top, bottom, &left, &right);
+    }
+
+    return traps->status;
+}
+
+void
+_cairo_traps_translate (cairo_traps_t *traps, int x, int y)
+{
+    cairo_fixed_t xoff, yoff;
+    cairo_trapezoid_t *t;
+    int i;
+
+    /* Ugh. The cairo_composite/(Render) interface doesn't allow
+       an offset for the trapezoids. Need to manually shift all
+       the coordinates to align with the offset origin of the
+       intermediate surface. */
+
+    xoff = _cairo_fixed_from_int (x);
+    yoff = _cairo_fixed_from_int (y);
+
+    for (i = 0, t = traps->traps; i < traps->num_traps; i++, t++) {
+       t->top += yoff;
+       t->bottom += yoff;
+       t->left.p1.x += xoff;
+       t->left.p1.y += yoff;
+       t->left.p2.x += xoff;
+       t->left.p2.y += yoff;
+       t->right.p1.x += xoff;
+       t->right.p1.y += yoff;
+       t->right.p2.x += xoff;
+       t->right.p2.y += yoff;
+    }
+}
+
+void
+_cairo_trapezoid_array_translate_and_scale (cairo_trapezoid_t *offset_traps,
+                                            cairo_trapezoid_t *src_traps,
+                                            int num_traps,
+                                            double tx, double ty,
+                                            double sx, double sy)
+{
+    int i;
+    cairo_fixed_t xoff = _cairo_fixed_from_double (tx);
+    cairo_fixed_t yoff = _cairo_fixed_from_double (ty);
+
+    if (sx == 1.0 && sy == 1.0) {
+        for (i = 0; i < num_traps; i++) {
+            offset_traps[i].top = src_traps[i].top + yoff;
+            offset_traps[i].bottom = src_traps[i].bottom + yoff;
+            offset_traps[i].left.p1.x = src_traps[i].left.p1.x + xoff;
+            offset_traps[i].left.p1.y = src_traps[i].left.p1.y + yoff;
+            offset_traps[i].left.p2.x = src_traps[i].left.p2.x + xoff;
+            offset_traps[i].left.p2.y = src_traps[i].left.p2.y + yoff;
+            offset_traps[i].right.p1.x = src_traps[i].right.p1.x + xoff;
+            offset_traps[i].right.p1.y = src_traps[i].right.p1.y + yoff;
+            offset_traps[i].right.p2.x = src_traps[i].right.p2.x + xoff;
+            offset_traps[i].right.p2.y = src_traps[i].right.p2.y + yoff;
+        }
+    } else {
+        cairo_fixed_t xsc = _cairo_fixed_from_double (sx);
+        cairo_fixed_t ysc = _cairo_fixed_from_double (sy);
+
+        for (i = 0; i < num_traps; i++) {
+            offset_traps[i].top = _cairo_fixed_mul (src_traps[i].top + yoff, ysc);
+            offset_traps[i].bottom = _cairo_fixed_mul (src_traps[i].bottom + yoff, ysc);
+            offset_traps[i].left.p1.x = _cairo_fixed_mul (src_traps[i].left.p1.x + xoff, xsc);
+            offset_traps[i].left.p1.y = _cairo_fixed_mul (src_traps[i].left.p1.y + yoff, ysc);
+            offset_traps[i].left.p2.x = _cairo_fixed_mul (src_traps[i].left.p2.x + xoff, xsc);
+            offset_traps[i].left.p2.y = _cairo_fixed_mul (src_traps[i].left.p2.y + yoff, ysc);
+            offset_traps[i].right.p1.x = _cairo_fixed_mul (src_traps[i].right.p1.x + xoff, xsc);
+            offset_traps[i].right.p1.y = _cairo_fixed_mul (src_traps[i].right.p1.y + yoff, ysc);
+            offset_traps[i].right.p2.x = _cairo_fixed_mul (src_traps[i].right.p2.x + xoff, xsc);
+            offset_traps[i].right.p2.y = _cairo_fixed_mul (src_traps[i].right.p2.y + yoff, ysc);
+        }
+    }
+}
+
+static cairo_bool_t
+_cairo_trap_contains (cairo_trapezoid_t *t, cairo_point_t *pt)
+{
+    cairo_slope_t slope_left, slope_pt, slope_right;
+
+    if (t->top > pt->y)
+       return FALSE;
+    if (t->bottom < pt->y)
+       return FALSE;
+
+    _cairo_slope_init (&slope_left, &t->left.p1, &t->left.p2);
+    _cairo_slope_init (&slope_pt, &t->left.p1, pt);
+
+    if (_cairo_slope_compare (&slope_left, &slope_pt) < 0)
+       return FALSE;
+
+    _cairo_slope_init (&slope_right, &t->right.p1, &t->right.p2);
+    _cairo_slope_init (&slope_pt, &t->right.p1, pt);
+
+    if (_cairo_slope_compare (&slope_pt, &slope_right) < 0)
+       return FALSE;
+
+    return TRUE;
+}
+
+cairo_bool_t
+_cairo_traps_contain (const cairo_traps_t *traps,
+                     double x, double y)
+{
+    int i;
+    cairo_point_t point;
+
+    point.x = _cairo_fixed_from_double (x);
+    point.y = _cairo_fixed_from_double (y);
+
+    for (i = 0; i < traps->num_traps; i++) {
+       if (_cairo_trap_contains (&traps->traps[i], &point))
+           return TRUE;
+    }
+
+    return FALSE;
+}
+
+static cairo_fixed_t
+_line_compute_intersection_x_for_y (const cairo_line_t *line,
+                                   cairo_fixed_t y)
+{
+    return _cairo_edge_compute_intersection_x_for_y (&line->p1, &line->p2, y);
+}
+
+void
+_cairo_traps_extents (const cairo_traps_t *traps,
+                     cairo_box_t *extents)
+{
+    int i;
+
+    if (traps->num_traps == 0) {
+       extents->p1.x = extents->p1.y = 0;
+       extents->p2.x = extents->p2.y = 0;
+       return;
+    }
+
+    extents->p1.x = extents->p1.y = INT32_MAX;
+    extents->p2.x = extents->p2.y = INT32_MIN;
+
+    for (i = 0; i < traps->num_traps; i++) {
+       const cairo_trapezoid_t *trap =  &traps->traps[i];
+
+       if (trap->top < extents->p1.y)
+           extents->p1.y = trap->top;
+       if (trap->bottom > extents->p2.y)
+           extents->p2.y = trap->bottom;
+
+       if (trap->left.p1.x < extents->p1.x) {
+           cairo_fixed_t x = trap->left.p1.x;
+           if (trap->top != trap->left.p1.y) {
+               x = _line_compute_intersection_x_for_y (&trap->left,
+                                                       trap->top);
+               if (x < extents->p1.x)
+                   extents->p1.x = x;
+           } else
+               extents->p1.x = x;
+       }
+       if (trap->left.p2.x < extents->p1.x) {
+           cairo_fixed_t x = trap->left.p2.x;
+           if (trap->bottom != trap->left.p2.y) {
+               x = _line_compute_intersection_x_for_y (&trap->left,
+                                                       trap->bottom);
+               if (x < extents->p1.x)
+                   extents->p1.x = x;
+           } else
+               extents->p1.x = x;
+       }
+
+       if (trap->right.p1.x > extents->p2.x) {
+           cairo_fixed_t x = trap->right.p1.x;
+           if (trap->top != trap->right.p1.y) {
+               x = _line_compute_intersection_x_for_y (&trap->right,
+                                                       trap->top);
+               if (x > extents->p2.x)
+                   extents->p2.x = x;
+           } else
+               extents->p2.x = x;
+       }
+       if (trap->right.p2.x > extents->p2.x) {
+           cairo_fixed_t x = trap->right.p2.x;
+           if (trap->bottom != trap->right.p2.y) {
+               x = _line_compute_intersection_x_for_y (&trap->right,
+                                                       trap->bottom);
+               if (x > extents->p2.x)
+                   extents->p2.x = x;
+           } else
+               extents->p2.x = x;
+       }
+    }
+}
+
+static cairo_bool_t
+_mono_edge_is_vertical (const cairo_line_t *line)
+{
+    return _cairo_fixed_integer_round_down (line->p1.x) == _cairo_fixed_integer_round_down (line->p2.x);
+}
+
+static cairo_bool_t
+_traps_are_pixel_aligned (cairo_traps_t *traps,
+                         cairo_antialias_t antialias)
+{
+    int i;
+
+    if (antialias == CAIRO_ANTIALIAS_NONE) {
+       for (i = 0; i < traps->num_traps; i++) {
+           if (! _mono_edge_is_vertical (&traps->traps[i].left)   ||
+               ! _mono_edge_is_vertical (&traps->traps[i].right))
+           {
+               traps->maybe_region = FALSE;
+               return FALSE;
+           }
+       }
+    } else {
+       for (i = 0; i < traps->num_traps; i++) {
+           if (traps->traps[i].left.p1.x != traps->traps[i].left.p2.x   ||
+               traps->traps[i].right.p1.x != traps->traps[i].right.p2.x ||
+               ! _cairo_fixed_is_integer (traps->traps[i].top)          ||
+               ! _cairo_fixed_is_integer (traps->traps[i].bottom)       ||
+               ! _cairo_fixed_is_integer (traps->traps[i].left.p1.x)    ||
+               ! _cairo_fixed_is_integer (traps->traps[i].right.p1.x))
+           {
+               traps->maybe_region = FALSE;
+               return FALSE;
+           }
+       }
+    }
+
+    return TRUE;
+}
+
+/**
+ * _cairo_traps_extract_region:
+ * @traps: a #cairo_traps_t
+ * @region: a #cairo_region_t
+ *
+ * Determines if a set of trapezoids are exactly representable as a
+ * cairo region.  If so, the passed-in region is initialized to
+ * the area representing the given traps.  It should be finalized
+ * with cairo_region_fini().  If not, %CAIRO_INT_STATUS_UNSUPPORTED
+ * is returned.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS, %CAIRO_INT_STATUS_UNSUPPORTED
+ * or %CAIRO_STATUS_NO_MEMORY
+ **/
+cairo_int_status_t
+_cairo_traps_extract_region (cairo_traps_t   *traps,
+                            cairo_antialias_t antialias,
+                            cairo_region_t **region)
+{
+    cairo_rectangle_int_t stack_rects[CAIRO_STACK_ARRAY_LENGTH (cairo_rectangle_int_t)];
+    cairo_rectangle_int_t *rects = stack_rects;
+    cairo_int_status_t status;
+    int i, rect_count;
+
+    /* we only treat this a hint... */
+    if (antialias != CAIRO_ANTIALIAS_NONE && ! traps->maybe_region)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    if (! _traps_are_pixel_aligned (traps, antialias)) {
+       traps->maybe_region = FALSE;
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+    }
+
+    if (traps->num_traps > ARRAY_LENGTH (stack_rects)) {
+       rects = _cairo_malloc_ab (traps->num_traps, sizeof (cairo_rectangle_int_t));
+
+       if (unlikely (rects == NULL))
+           return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+    }
+
+    rect_count = 0;
+    for (i = 0; i < traps->num_traps; i++) {
+       int x1, y1, x2, y2;
+
+       if (antialias == CAIRO_ANTIALIAS_NONE) {
+           x1 = _cairo_fixed_integer_round_down (traps->traps[i].left.p1.x);
+           y1 = _cairo_fixed_integer_round_down (traps->traps[i].top);
+           x2 = _cairo_fixed_integer_round_down (traps->traps[i].right.p1.x);
+           y2 = _cairo_fixed_integer_round_down (traps->traps[i].bottom);
+       } else {
+           x1 = _cairo_fixed_integer_part (traps->traps[i].left.p1.x);
+           y1 = _cairo_fixed_integer_part (traps->traps[i].top);
+           x2 = _cairo_fixed_integer_part (traps->traps[i].right.p1.x);
+           y2 = _cairo_fixed_integer_part (traps->traps[i].bottom);
+       }
+
+       if (x2 > x1 && y2 > y1) {
+           rects[rect_count].x = x1;
+           rects[rect_count].y = y1;
+           rects[rect_count].width  = x2 - x1;
+           rects[rect_count].height = y2 - y1;
+           rect_count++;
+       }
+    }
+
+
+    *region = cairo_region_create_rectangles (rects, rect_count);
+    status = (*region)->status;
+
+    if (rects != stack_rects)
+       free (rects);
+
+    return status;
+}
+
+cairo_bool_t
+_cairo_traps_to_boxes (cairo_traps_t *traps,
+                      cairo_antialias_t antialias,
+                      cairo_boxes_t *boxes)
+{
+    int i;
+
+    for (i = 0; i < traps->num_traps; i++) {
+       if (traps->traps[i].left.p1.x  != traps->traps[i].left.p2.x ||
+           traps->traps[i].right.p1.x != traps->traps[i].right.p2.x)
+           return FALSE;
+    }
+
+    _cairo_boxes_init (boxes);
+
+    boxes->num_boxes    = traps->num_traps;
+    boxes->chunks.base  = (cairo_box_t *) traps->traps;
+    boxes->chunks.count = traps->num_traps;
+    boxes->chunks.size  = traps->num_traps;
+
+    if (antialias != CAIRO_ANTIALIAS_NONE) {
+       for (i = 0; i < traps->num_traps; i++) {
+           /* Note the traps and boxes alias so we need to take the local copies first. */
+           cairo_fixed_t x1 = traps->traps[i].left.p1.x;
+           cairo_fixed_t x2 = traps->traps[i].right.p1.x;
+           cairo_fixed_t y1 = traps->traps[i].top;
+           cairo_fixed_t y2 = traps->traps[i].bottom;
+
+           boxes->chunks.base[i].p1.x = x1;
+           boxes->chunks.base[i].p1.y = y1;
+           boxes->chunks.base[i].p2.x = x2;
+           boxes->chunks.base[i].p2.y = y2;
+
+           if (boxes->is_pixel_aligned) {
+               boxes->is_pixel_aligned =
+                   _cairo_fixed_is_integer (x1) && _cairo_fixed_is_integer (y1) &&
+                   _cairo_fixed_is_integer (x2) && _cairo_fixed_is_integer (y2);
+           }
+       }
+    } else {
+       boxes->is_pixel_aligned = TRUE;
+
+       for (i = 0; i < traps->num_traps; i++) {
+           /* Note the traps and boxes alias so we need to take the local copies first. */
+           cairo_fixed_t x1 = traps->traps[i].left.p1.x;
+           cairo_fixed_t x2 = traps->traps[i].right.p1.x;
+           cairo_fixed_t y1 = traps->traps[i].top;
+           cairo_fixed_t y2 = traps->traps[i].bottom;
+
+           /* round down here to match Pixman's behavior when using traps. */
+           boxes->chunks.base[i].p1.x = _cairo_fixed_round_down (x1);
+           boxes->chunks.base[i].p1.y = _cairo_fixed_round_down (y1);
+           boxes->chunks.base[i].p2.x = _cairo_fixed_round_down (x2);
+           boxes->chunks.base[i].p2.y = _cairo_fixed_round_down (y2);
+       }
+    }
+
+    return TRUE;
+}
+
+/* moves trap points such that they become the actual corners of the trapezoid */
+static void
+_sanitize_trap (cairo_trapezoid_t *t)
+{
+    cairo_trapezoid_t s = *t;
+
+#define FIX(lr, tb, p) \
+    if (t->lr.p.y != t->tb) { \
+        t->lr.p.x = s.lr.p2.x + _cairo_fixed_mul_div_floor (s.lr.p1.x - s.lr.p2.x, s.tb - s.lr.p2.y, s.lr.p1.y - s.lr.p2.y); \
+        t->lr.p.y = s.tb; \
+    }
+    FIX (left,  top,    p1);
+    FIX (left,  bottom, p2);
+    FIX (right, top,    p1);
+    FIX (right, bottom, p2);
+}
+
+cairo_private cairo_status_t
+_cairo_traps_path (const cairo_traps_t *traps,
+                  cairo_path_fixed_t  *path)
+{
+    int i;
+
+    for (i = 0; i < traps->num_traps; i++) {
+       cairo_status_t status;
+       cairo_trapezoid_t trap = traps->traps[i];
+
+       if (trap.top == trap.bottom)
+           continue;
+
+       _sanitize_trap (&trap);
+
+       status = _cairo_path_fixed_move_to (path, trap.left.p1.x, trap.top);
+       if (unlikely (status)) return status;
+       status = _cairo_path_fixed_line_to (path, trap.right.p1.x, trap.top);
+       if (unlikely (status)) return status;
+       status = _cairo_path_fixed_line_to (path, trap.right.p2.x, trap.bottom);
+       if (unlikely (status)) return status;
+       status = _cairo_path_fixed_line_to (path, trap.left.p2.x, trap.bottom);
+       if (unlikely (status)) return status;
+       status = _cairo_path_fixed_close_path (path);
+       if (unlikely (status)) return status;
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
diff --git a/src/cairo-tristrip-private.h b/src/cairo-tristrip-private.h
new file mode 100644 (file)
index 0000000..ccd2879
--- /dev/null
@@ -0,0 +1,94 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2011 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#ifndef CAIRO_TRISTRIP_PRIVATE_H
+#define CAIRO_TRISTRIP_PRIVATE_H
+
+#include "cairo-compiler-private.h"
+#include "cairo-error-private.h"
+#include "cairo-types-private.h"
+
+CAIRO_BEGIN_DECLS
+
+struct _cairo_tristrip {
+    cairo_status_t status;
+
+    /* XXX clipping */
+
+    const cairo_box_t *limits;
+    int num_limits;
+
+    int num_points;
+    int size_points;
+    cairo_point_t *points;
+    cairo_point_t  points_embedded[64];
+};
+
+cairo_private void
+_cairo_tristrip_init (cairo_tristrip_t *strip);
+
+cairo_private void
+_cairo_tristrip_limit (cairo_tristrip_t        *strip,
+                      const cairo_box_t        *limits,
+                      int                       num_limits);
+
+cairo_private void
+_cairo_tristrip_init_with_clip (cairo_tristrip_t *strip,
+                               const cairo_clip_t *clip);
+
+cairo_private void
+_cairo_tristrip_translate (cairo_tristrip_t *strip, int x, int y);
+
+cairo_private void
+_cairo_tristrip_move_to (cairo_tristrip_t *strip,
+                        const cairo_point_t *point);
+
+cairo_private void
+_cairo_tristrip_add_point (cairo_tristrip_t *strip,
+                          const cairo_point_t *point);
+
+cairo_private void
+_cairo_tristrip_extents (const cairo_tristrip_t *strip,
+                        cairo_box_t         *extents);
+
+cairo_private void
+_cairo_tristrip_fini (cairo_tristrip_t *strip);
+
+#define _cairo_tristrip_status(T) ((T)->status)
+
+CAIRO_END_DECLS
+
+#endif /* CAIRO_TRISTRIP_PRIVATE_H */
diff --git a/src/cairo-tristrip.c b/src/cairo-tristrip.c
new file mode 100644 (file)
index 0000000..bb4972f
--- /dev/null
@@ -0,0 +1,185 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/*
+ * Copyright © 2011 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Chris Wilson
+ *
+ * Contributor(s):
+ *     Chris Wilson <chris@chris-wilsonc.co.uk>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-error-private.h"
+#include "cairo-tristrip-private.h"
+
+void
+_cairo_tristrip_init (cairo_tristrip_t *strip)
+{
+    VG (VALGRIND_MAKE_MEM_UNDEFINED (strip, sizeof (cairo_tristrip_t)));
+
+    strip->status = CAIRO_STATUS_SUCCESS;
+
+    strip->num_limits = 0;
+    strip->num_points = 0;
+
+    strip->size_points = ARRAY_LENGTH (strip->points_embedded);
+    strip->points = strip->points_embedded;
+}
+
+void
+_cairo_tristrip_fini (cairo_tristrip_t *strip)
+{
+    if (strip->points != strip->points_embedded)
+       free (strip->points);
+
+    VG (VALGRIND_MAKE_MEM_NOACCESS (strip, sizeof (cairo_tristrip_t)));
+}
+
+
+void
+_cairo_tristrip_limit (cairo_tristrip_t        *strip,
+                      const cairo_box_t        *limits,
+                      int                       num_limits)
+{
+    strip->limits = limits;
+    strip->num_limits = num_limits;
+}
+
+void
+_cairo_tristrip_init_with_clip (cairo_tristrip_t *strip,
+                               const cairo_clip_t *clip)
+{
+    _cairo_tristrip_init (strip);
+    if (clip)
+       _cairo_tristrip_limit (strip, clip->boxes, clip->num_boxes);
+}
+
+/* make room for at least one more trap */
+static cairo_bool_t
+_cairo_tristrip_grow (cairo_tristrip_t *strip)
+{
+    cairo_point_t *points;
+    int new_size = 4 * strip->size_points;
+
+    if (CAIRO_INJECT_FAULT ()) {
+       strip->status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+       return FALSE;
+    }
+
+    if (strip->points == strip->points_embedded) {
+       points = _cairo_malloc_ab (new_size, sizeof (cairo_point_t));
+       if (points != NULL)
+           memcpy (points, strip->points, sizeof (strip->points_embedded));
+    } else {
+       points = _cairo_realloc_ab (strip->points,
+                                      new_size, sizeof (cairo_trapezoid_t));
+    }
+
+    if (unlikely (points == NULL)) {
+       strip->status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+       return FALSE;
+    }
+
+    strip->points = points;
+    strip->size_points = new_size;
+    return TRUE;
+}
+
+void
+_cairo_tristrip_add_point (cairo_tristrip_t *strip,
+                          const cairo_point_t *p)
+{
+    if (unlikely (strip->num_points == strip->size_points)) {
+       if (unlikely (! _cairo_tristrip_grow (strip)))
+           return;
+    }
+
+    strip->points[strip->num_points++] = *p;
+}
+
+/* Insert degenerate triangles to advance to the given point. The
+ * next point inserted must also be @p. */
+void
+_cairo_tristrip_move_to (cairo_tristrip_t *strip,
+                        const cairo_point_t *p)
+{
+    if (strip->num_points == 0)
+       return;
+
+    _cairo_tristrip_add_point (strip, &strip->points[strip->num_points-1]);
+    _cairo_tristrip_add_point (strip, p);
+#if 0
+    /* and one more for luck! (to preserve cw/ccw ordering) */
+    _cairo_tristrip_add_point (strip, p);
+#endif
+}
+
+void
+_cairo_tristrip_translate (cairo_tristrip_t *strip, int x, int y)
+{
+    cairo_fixed_t xoff, yoff;
+    cairo_point_t *p;
+    int i;
+
+    xoff = _cairo_fixed_from_int (x);
+    yoff = _cairo_fixed_from_int (y);
+
+    for (i = 0, p = strip->points; i < strip->num_points; i++, p++) {
+       p->x += xoff;
+       p->y += yoff;
+    }
+}
+
+void
+_cairo_tristrip_extents (const cairo_tristrip_t *strip,
+                        cairo_box_t *extents)
+{
+    int i;
+
+    if (strip->num_points == 0) {
+       extents->p1.x = extents->p1.y = 0;
+       extents->p2.x = extents->p2.y = 0;
+       return;
+    }
+
+    extents->p2 = extents->p1 = strip->points[0];
+    for (i = 1; i < strip->num_points; i++) {
+       const cairo_point_t *p =  &strip->points[i];
+
+       if (p->x < extents->p1.x)
+           extents->p1.x = p->x;
+       else if (p->x > extents->p2.x)
+           extents->p2.x = p->x;
+
+       if (p->y < extents->p1.y)
+           extents->p1.y = p->y;
+       else if (p->y > extents->p2.y)
+           extents->p2.y = p->y;
+    }
+}
diff --git a/src/cairo-truetype-subset-private.h b/src/cairo-truetype-subset-private.h
new file mode 100644 (file)
index 0000000..dc95732
--- /dev/null
@@ -0,0 +1,212 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2006 Red Hat, Inc
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ *     Kristian Høgsberg <krh@redhat.com>
+ *     Adrian Johnson <ajohnson@redneon.com>
+ */
+
+#ifndef CAIRO_TRUETYPE_SUBSET_PRIVATE_H
+#define CAIRO_TRUETYPE_SUBSET_PRIVATE_H
+
+#include "cairoint.h"
+
+#if CAIRO_HAS_FONT_SUBSET
+
+/* The structs defined here should strictly follow the TrueType
+ * specification and not be padded.  We use only 16-bit integer
+ * in their definition to guarantee that.  The fields of type
+ * "FIXED" in the TT spec are broken into two *_1 and *_2 16-bit
+ * parts, and 64-bit members are broken into four.
+ *
+ * The test truetype-tables in the test suite makes sure that
+ * these tables have the right size.  Please update that test
+ * if you add new tables/structs that should be packed.
+ */
+
+#define MAKE_TT_TAG(a, b, c, d)    (a<<24 | b<<16 | c<<8 | d)
+#define TT_TAG_CFF    MAKE_TT_TAG('C','F','F',' ')
+#define TT_TAG_cmap   MAKE_TT_TAG('c','m','a','p')
+#define TT_TAG_cvt    MAKE_TT_TAG('c','v','t',' ')
+#define TT_TAG_fpgm   MAKE_TT_TAG('f','p','g','m')
+#define TT_TAG_glyf   MAKE_TT_TAG('g','l','y','f')
+#define TT_TAG_head   MAKE_TT_TAG('h','e','a','d')
+#define TT_TAG_hhea   MAKE_TT_TAG('h','h','e','a')
+#define TT_TAG_hmtx   MAKE_TT_TAG('h','m','t','x')
+#define TT_TAG_loca   MAKE_TT_TAG('l','o','c','a')
+#define TT_TAG_maxp   MAKE_TT_TAG('m','a','x','p')
+#define TT_TAG_name   MAKE_TT_TAG('n','a','m','e')
+#define TT_TAG_OS2    MAKE_TT_TAG('O','S','/','2')
+#define TT_TAG_post   MAKE_TT_TAG('p','o','s','t')
+#define TT_TAG_prep   MAKE_TT_TAG('p','r','e','p')
+
+/* All tt_* structs are big-endian */
+typedef struct _tt_cmap_index {
+    uint16_t platform;
+    uint16_t encoding;
+    uint32_t offset;
+} tt_cmap_index_t;
+
+typedef struct _tt_cmap {
+    uint16_t        version;
+    uint16_t        num_tables;
+    tt_cmap_index_t index[1];
+} tt_cmap_t;
+
+typedef struct _segment_map {
+    uint16_t format;
+    uint16_t length;
+    uint16_t version;
+    uint16_t segCountX2;
+    uint16_t searchRange;
+    uint16_t entrySelector;
+    uint16_t rangeShift;
+    uint16_t endCount[1];
+} tt_segment_map_t;
+
+typedef struct _tt_head {
+    int16_t     version_1;
+    int16_t     version_2;
+    int16_t     revision_1;
+    int16_t     revision_2;
+    uint16_t    checksum_1;
+    uint16_t    checksum_2;
+    uint16_t    magic_1;
+    uint16_t    magic_2;
+    uint16_t    flags;
+    uint16_t    units_per_em;
+    int16_t     created_1;
+    int16_t     created_2;
+    int16_t     created_3;
+    int16_t     created_4;
+    int16_t     modified_1;
+    int16_t     modified_2;
+    int16_t     modified_3;
+    int16_t     modified_4;
+    int16_t     x_min;                  /* FWORD */
+    int16_t     y_min;                  /* FWORD */
+    int16_t     x_max;                  /* FWORD */
+    int16_t     y_max;                  /* FWORD */
+    uint16_t    mac_style;
+    uint16_t    lowest_rec_pppem;
+    int16_t     font_direction_hint;
+    int16_t     index_to_loc_format;
+    int16_t     glyph_data_format;
+} tt_head_t;
+
+typedef struct _tt_hhea {
+    int16_t     version_1;
+    int16_t     version_2;
+    int16_t     ascender;               /* FWORD */
+    int16_t     descender;              /* FWORD */
+    int16_t     line_gap;               /* FWORD */
+    uint16_t    advance_max_width;      /* UFWORD */
+    int16_t     min_left_side_bearing;  /* FWORD */
+    int16_t     min_right_side_bearing; /* FWORD */
+    int16_t     x_max_extent;           /* FWORD */
+    int16_t     caret_slope_rise;
+    int16_t     caret_slope_run;
+    int16_t     reserved[5];
+    int16_t     metric_data_format;
+    uint16_t    num_hmetrics;
+} tt_hhea_t;
+
+typedef struct _tt_maxp {
+    int16_t     version_1;
+    int16_t     version_2;
+    uint16_t    num_glyphs;
+    uint16_t    max_points;
+    uint16_t    max_contours;
+    uint16_t    max_composite_points;
+    uint16_t    max_composite_contours;
+    uint16_t    max_zones;
+    uint16_t    max_twilight_points;
+    uint16_t    max_storage;
+    uint16_t    max_function_defs;
+    uint16_t    max_instruction_defs;
+    uint16_t    max_stack_elements;
+    uint16_t    max_size_of_instructions;
+    uint16_t    max_component_elements;
+    uint16_t    max_component_depth;
+} tt_maxp_t;
+
+typedef struct _tt_name_record {
+    uint16_t platform;
+    uint16_t encoding;
+    uint16_t language;
+    uint16_t name;
+    uint16_t length;
+    uint16_t offset;
+} tt_name_record_t;
+
+typedef struct _tt_name {
+    uint16_t   format;
+    uint16_t   num_records;
+    uint16_t   strings_offset;
+    tt_name_record_t records[1];
+} tt_name_t;
+
+
+/* bitmask for fsSelection field */
+#define TT_FS_SELECTION_ITALIC   1
+#define TT_FS_SELECTION_BOLD    32
+
+/* _unused fields are defined in TT spec but not used by cairo */
+typedef struct _tt_os2 {
+    uint16_t   _unused1[2];
+    uint16_t   usWeightClass;
+    uint16_t   _unused2[28];
+    uint16_t   fsSelection;
+    uint16_t   _unused3[11];
+} tt_os2_t;
+
+/* composite_glyph_t flags */
+#define TT_ARG_1_AND_2_ARE_WORDS     0x0001
+#define TT_WE_HAVE_A_SCALE           0x0008
+#define TT_MORE_COMPONENTS           0x0020
+#define TT_WE_HAVE_AN_X_AND_Y_SCALE  0x0040
+#define TT_WE_HAVE_A_TWO_BY_TWO      0x0080
+
+typedef struct _tt_composite_glyph {
+    uint16_t flags;
+    uint16_t index;
+    uint16_t args[6]; /* 1 to 6 arguments depending on value of flags */
+} tt_composite_glyph_t;
+
+typedef struct _tt_glyph_data {
+    int16_t           num_contours;
+    int8_t            data[8];
+    tt_composite_glyph_t glyph;
+} tt_glyph_data_t;
+
+#endif /* CAIRO_HAS_FONT_SUBSET */
+
+#endif /* CAIRO_TRUETYPE_SUBSET_PRIVATE_H */
diff --git a/src/cairo-truetype-subset.c b/src/cairo-truetype-subset.c
new file mode 100644 (file)
index 0000000..1afdf3a
--- /dev/null
@@ -0,0 +1,1662 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2004 Red Hat, Inc
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ *     Kristian Høgsberg <krh@redhat.com>
+ *     Adrian Johnson <ajohnson@redneon.com>
+ */
+
+/*
+ * Useful links:
+ * http://developer.apple.com/textfonts/TTRefMan/RM06/Chap6.html
+ * http://www.microsoft.com/typography/specs/default.htm
+ */
+
+#define _BSD_SOURCE /* for snprintf(), strdup() */
+#include "cairoint.h"
+
+#include "cairo-array-private.h"
+#include "cairo-error-private.h"
+
+#if CAIRO_HAS_FONT_SUBSET
+
+#include "cairo-scaled-font-subsets-private.h"
+#include "cairo-truetype-subset-private.h"
+
+
+typedef struct subset_glyph subset_glyph_t;
+struct subset_glyph {
+    int parent_index;
+    unsigned long location;
+};
+
+typedef struct _cairo_truetype_font cairo_truetype_font_t;
+
+typedef struct table table_t;
+struct table {
+    unsigned long tag;
+    cairo_status_t (*write) (cairo_truetype_font_t *font, unsigned long tag);
+    int pos; /* position in the font directory */
+};
+
+struct _cairo_truetype_font {
+
+    cairo_scaled_font_subset_t *scaled_font_subset;
+
+    table_t truetype_tables[10];
+    int num_tables;
+
+    struct {
+       char *font_name;
+       char *ps_name;
+       unsigned int num_glyphs;
+       int *widths;
+       long x_min, y_min, x_max, y_max;
+       long ascent, descent;
+        int  units_per_em;
+    } base;
+
+    subset_glyph_t *glyphs;
+    const cairo_scaled_font_backend_t *backend;
+    int num_glyphs_in_face;
+    int checksum_index;
+    cairo_array_t output;
+    cairo_array_t string_offsets;
+    unsigned long last_offset;
+    unsigned long last_boundary;
+    int *parent_to_subset;
+    cairo_status_t status;
+    cairo_bool_t is_pdf;
+};
+
+/*
+ * Test that the structs we define for TrueType tables have the
+ * correct size, ie. they are not padded.
+ */
+#define check(T, S) COMPILE_TIME_ASSERT (sizeof (T) == (S))
+check (tt_head_t,      54);
+check (tt_hhea_t,      36);
+check (tt_maxp_t,      32);
+check (tt_name_record_t, 12);
+check (tt_name_t,      18);
+check (tt_name_t,      18);
+check (tt_composite_glyph_t, 16);
+check (tt_glyph_data_t,        26);
+#undef check
+
+static cairo_status_t
+cairo_truetype_font_use_glyph (cairo_truetype_font_t       *font,
+                              unsigned short                glyph,
+                              unsigned short               *out);
+
+#define SFNT_VERSION                   0x00010000
+#define SFNT_STRING_MAX_LENGTH  65535
+
+static cairo_status_t
+_cairo_truetype_font_set_error (cairo_truetype_font_t *font,
+                               cairo_status_t status)
+{
+    if (status == CAIRO_STATUS_SUCCESS ||
+       status == (int)CAIRO_INT_STATUS_UNSUPPORTED)
+       return status;
+
+    _cairo_status_set_error (&font->status, status);
+
+    return _cairo_error (status);
+}
+
+static cairo_status_t
+_cairo_truetype_font_create (cairo_scaled_font_subset_t  *scaled_font_subset,
+                            cairo_bool_t is_pdf,
+                            cairo_truetype_font_t      **font_return)
+{
+    cairo_status_t status;
+    cairo_truetype_font_t *font;
+    const cairo_scaled_font_backend_t *backend;
+    tt_head_t head;
+    tt_hhea_t hhea;
+    tt_maxp_t maxp;
+    unsigned long size;
+
+    backend = scaled_font_subset->scaled_font->backend;
+    if (!backend->load_truetype_table)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    /* FIXME: We should either support subsetting vertical fonts, or fail on
+     * vertical.  Currently font_options_t doesn't have vertical flag, but
+     * it should be added in the future.  For now, the freetype backend
+     * returns UNSUPPORTED in load_truetype_table if the font is vertical.
+     *
+     *  if (cairo_font_options_get_vertical_layout (scaled_font_subset->scaled_font))
+     *   return CAIRO_INT_STATUS_UNSUPPORTED;
+     */
+
+    /* We need to use a fallback font generated from the synthesized outlines. */
+    if (backend->is_synthetic && backend->is_synthetic (scaled_font_subset->scaled_font))
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    size = sizeof (tt_head_t);
+    status = backend->load_truetype_table (scaled_font_subset->scaled_font,
+                                          TT_TAG_head, 0,
+                                         (unsigned char *) &head,
+                                          &size);
+    if (unlikely (status))
+       return status;
+
+    size = sizeof (tt_maxp_t);
+    status = backend->load_truetype_table (scaled_font_subset->scaled_font,
+                                           TT_TAG_maxp, 0,
+                                          (unsigned char *) &maxp,
+                                          &size);
+    if (unlikely (status))
+       return status;
+
+    size = sizeof (tt_hhea_t);
+    status = backend->load_truetype_table (scaled_font_subset->scaled_font,
+                                           TT_TAG_hhea, 0,
+                                          (unsigned char *) &hhea,
+                                          &size);
+    if (unlikely (status))
+       return status;
+
+    font = malloc (sizeof (cairo_truetype_font_t));
+    if (unlikely (font == NULL))
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    font->backend = backend;
+    font->num_glyphs_in_face = be16_to_cpu (maxp.num_glyphs);
+    font->scaled_font_subset = scaled_font_subset;
+
+    font->last_offset = 0;
+    font->last_boundary = 0;
+    _cairo_array_init (&font->output, sizeof (char));
+    status = _cairo_array_grow_by (&font->output, 4096);
+    if (unlikely (status))
+       goto fail1;
+
+    font->glyphs = calloc (font->num_glyphs_in_face + 1, sizeof (subset_glyph_t));
+    if (unlikely (font->glyphs == NULL)) {
+       status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+       goto fail1;
+    }
+
+    font->parent_to_subset = calloc (font->num_glyphs_in_face, sizeof (int));
+    if (unlikely (font->parent_to_subset == NULL)) {
+       status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+       goto fail2;
+    }
+
+    font->is_pdf = is_pdf;
+    font->base.num_glyphs = 0;
+    font->base.x_min = (int16_t) be16_to_cpu (head.x_min);
+    font->base.y_min = (int16_t) be16_to_cpu (head.y_min);
+    font->base.x_max = (int16_t) be16_to_cpu (head.x_max);
+    font->base.y_max = (int16_t) be16_to_cpu (head.y_max);
+    font->base.ascent = (int16_t) be16_to_cpu (hhea.ascender);
+    font->base.descent = (int16_t) be16_to_cpu (hhea.descender);
+    font->base.units_per_em = (int16_t) be16_to_cpu (head.units_per_em);
+    if (font->base.units_per_em == 0)
+        font->base.units_per_em = 2048;
+
+    font->base.ps_name = NULL;
+    font->base.font_name = NULL;
+    status = _cairo_truetype_read_font_name (scaled_font_subset->scaled_font,
+                                            &font->base.ps_name,
+                                            &font->base.font_name);
+    if (_cairo_status_is_error (status))
+       goto fail3;
+
+    /* If the PS name is not found, create a CairoFont-x-y name. */
+    if (font->base.ps_name == NULL) {
+        font->base.ps_name = malloc (30);
+        if (unlikely (font->base.ps_name == NULL)) {
+           status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+            goto fail3;
+       }
+
+        snprintf(font->base.ps_name, 30, "CairoFont-%u-%u",
+                 scaled_font_subset->font_id,
+                 scaled_font_subset->subset_id);
+    }
+
+    font->base.widths = calloc (font->num_glyphs_in_face, sizeof (int));
+    if (unlikely (font->base.widths == NULL)) {
+       status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+       goto fail4;
+    }
+
+    _cairo_array_init (&font->string_offsets, sizeof (unsigned long));
+    status = _cairo_array_grow_by (&font->string_offsets, 10);
+    if (unlikely (status))
+       goto fail5;
+
+    font->status = CAIRO_STATUS_SUCCESS;
+
+    *font_return = font;
+
+    return CAIRO_STATUS_SUCCESS;
+
+ fail5:
+    _cairo_array_fini (&font->string_offsets);
+    free (font->base.widths);
+ fail4:
+    free (font->base.ps_name);
+ fail3:
+    free (font->parent_to_subset);
+    free (font->base.font_name);
+ fail2:
+    free (font->glyphs);
+ fail1:
+    _cairo_array_fini (&font->output);
+    free (font);
+
+    return status;
+}
+
+static void
+cairo_truetype_font_destroy (cairo_truetype_font_t *font)
+{
+    _cairo_array_fini (&font->string_offsets);
+    free (font->base.widths);
+    free (font->base.ps_name);
+    free (font->base.font_name);
+    free (font->parent_to_subset);
+    free (font->glyphs);
+    _cairo_array_fini (&font->output);
+    free (font);
+}
+
+static cairo_status_t
+cairo_truetype_font_allocate_write_buffer (cairo_truetype_font_t  *font,
+                                          size_t                  length,
+                                          unsigned char         **buffer)
+{
+    cairo_status_t status;
+
+    if (font->status)
+       return font->status;
+
+    status = _cairo_array_allocate (&font->output, length, (void **) buffer);
+    if (unlikely (status))
+       return _cairo_truetype_font_set_error (font, status);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+cairo_truetype_font_write (cairo_truetype_font_t *font,
+                          const void            *data,
+                          size_t                 length)
+{
+    cairo_status_t status;
+
+    if (font->status)
+       return;
+
+    status = _cairo_array_append_multiple (&font->output, data, length);
+    if (unlikely (status))
+       status = _cairo_truetype_font_set_error (font, status);
+}
+
+static void
+cairo_truetype_font_write_be16 (cairo_truetype_font_t *font,
+                               uint16_t               value)
+{
+    uint16_t be16_value;
+
+    if (font->status)
+       return;
+
+    be16_value = cpu_to_be16 (value);
+    cairo_truetype_font_write (font, &be16_value, sizeof be16_value);
+}
+
+static void
+cairo_truetype_font_write_be32 (cairo_truetype_font_t *font,
+                               uint32_t               value)
+{
+    uint32_t be32_value;
+
+    if (font->status)
+       return;
+
+    be32_value = cpu_to_be32 (value);
+    cairo_truetype_font_write (font, &be32_value, sizeof be32_value);
+}
+
+static cairo_status_t
+cairo_truetype_font_align_output (cairo_truetype_font_t            *font,
+                                 unsigned long             *aligned)
+{
+    int length, pad;
+    unsigned char *padding;
+
+    length = _cairo_array_num_elements (&font->output);
+    *aligned = (length + 3) & ~3;
+    pad = *aligned - length;
+
+    if (pad) {
+       cairo_status_t status;
+
+       status = cairo_truetype_font_allocate_write_buffer (font, pad,
+                                                           &padding);
+       if (unlikely (status))
+           return status;
+
+       memset (padding, 0, pad);
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+cairo_truetype_font_check_boundary (cairo_truetype_font_t *font,
+                                   unsigned long          boundary)
+{
+    cairo_status_t status;
+
+    if (font->status)
+       return font->status;
+
+    if (boundary - font->last_offset > SFNT_STRING_MAX_LENGTH)
+    {
+        status = _cairo_array_append (&font->string_offsets,
+                                     &font->last_boundary);
+       if (unlikely (status))
+           return _cairo_truetype_font_set_error (font, status);
+
+        font->last_offset = font->last_boundary;
+    }
+    font->last_boundary = boundary;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+typedef struct _cmap_unicode_range {
+    unsigned int start;
+    unsigned int end;
+} cmap_unicode_range_t;
+
+static cmap_unicode_range_t winansi_unicode_ranges[] = {
+    { 0x0020, 0x007f },
+    { 0x00a0, 0x00ff },
+    { 0x0152, 0x0153 },
+    { 0x0160, 0x0161 },
+    { 0x0178, 0x0178 },
+    { 0x017d, 0x017e },
+    { 0x0192, 0x0192 },
+    { 0x02c6, 0x02c6 },
+    { 0x02dc, 0x02dc },
+    { 0x2013, 0x2026 },
+    { 0x2030, 0x2030 },
+    { 0x2039, 0x203a },
+    { 0x20ac, 0x20ac },
+    { 0x2122, 0x2122 },
+};
+
+static cairo_status_t
+cairo_truetype_font_write_cmap_table (cairo_truetype_font_t *font,
+                                     unsigned long          tag)
+{
+    int i;
+    unsigned int j;
+    int range_offset;
+    int num_ranges;
+    int entry_selector;
+    int length;
+
+    num_ranges = ARRAY_LENGTH (winansi_unicode_ranges);
+
+    length = 16 + (num_ranges + 1)*8;
+    for (i = 0; i < num_ranges; i++)
+       length += (winansi_unicode_ranges[i].end - winansi_unicode_ranges[i].start + 1)*2;
+
+    entry_selector = 0;
+    while ((1 << entry_selector) <= (num_ranges + 1))
+       entry_selector++;
+
+    entry_selector--;
+
+    cairo_truetype_font_write_be16 (font, 0);  /* Table version */
+    cairo_truetype_font_write_be16 (font, 1);  /* Num tables */
+
+    cairo_truetype_font_write_be16 (font, 3);  /* Platform */
+    cairo_truetype_font_write_be16 (font, 1);  /* Encoding */
+    cairo_truetype_font_write_be32 (font, 12); /* Offset to start of table */
+
+    /* Output a format 4 encoding table for the winansi encoding */
+
+    cairo_truetype_font_write_be16 (font, 4);  /* Format */
+    cairo_truetype_font_write_be16 (font, length); /* Length */
+    cairo_truetype_font_write_be16 (font, 0);  /* Version */
+    cairo_truetype_font_write_be16 (font, num_ranges*2 + 2);  /* 2*segcount */
+    cairo_truetype_font_write_be16 (font, (1 << (entry_selector + 1)));  /* searchrange */
+    cairo_truetype_font_write_be16 (font, entry_selector);  /* entry selector */
+    cairo_truetype_font_write_be16 (font, num_ranges*2 + 2 - (1 << (entry_selector + 1)));  /* rangeshift */
+    for (i = 0; i < num_ranges; i++)
+       cairo_truetype_font_write_be16 (font, winansi_unicode_ranges[i].end); /* end count[] */
+    cairo_truetype_font_write_be16 (font, 0xffff);  /* end count[] */
+
+    cairo_truetype_font_write_be16 (font, 0);       /* reserved */
+
+    for (i = 0; i < num_ranges; i++)
+       cairo_truetype_font_write_be16 (font, winansi_unicode_ranges[i].start);  /* startCode[] */
+    cairo_truetype_font_write_be16 (font, 0xffff);  /* startCode[] */
+
+    for (i = 0; i < num_ranges; i++)
+       cairo_truetype_font_write_be16 (font, 0x0000);  /* delta[] */
+    cairo_truetype_font_write_be16 (font, 1);       /* delta[] */
+
+    range_offset = num_ranges*2 + 2;
+    for (i = 0; i < num_ranges; i++) {
+       cairo_truetype_font_write_be16 (font, range_offset);       /* rangeOffset[] */
+       range_offset += (winansi_unicode_ranges[i].end - winansi_unicode_ranges[i].start + 1)*2 - 2;
+    }
+    cairo_truetype_font_write_be16 (font, 0);       /* rangeOffset[] */
+
+    for (i = 0; i < num_ranges; i++) {
+       for (j = winansi_unicode_ranges[i].start; j < winansi_unicode_ranges[i].end + 1; j++) {
+           int ch = _cairo_unicode_to_winansi (j);
+           int glyph;
+
+           if (ch > 0)
+               glyph = font->scaled_font_subset->latin_to_subset_glyph_index[ch];
+           else
+               glyph = 0;
+           cairo_truetype_font_write_be16 (font, glyph);
+       }
+    }
+
+    return font->status;
+}
+
+static cairo_status_t
+cairo_truetype_font_write_generic_table (cairo_truetype_font_t *font,
+                                        unsigned long          tag)
+{
+    cairo_status_t status;
+    unsigned char *buffer;
+    unsigned long size;
+
+    if (font->status)
+       return font->status;
+
+    size = 0;
+    status = font->backend->load_truetype_table(font->scaled_font_subset->scaled_font,
+                                               tag, 0, NULL, &size);
+    if (unlikely (status))
+        return _cairo_truetype_font_set_error (font, status);
+
+    status = cairo_truetype_font_allocate_write_buffer (font, size, &buffer);
+    if (unlikely (status))
+       return _cairo_truetype_font_set_error (font, status);
+
+    status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font,
+                                                tag, 0, buffer, &size);
+    if (unlikely (status))
+       return _cairo_truetype_font_set_error (font, status);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+cairo_truetype_font_remap_composite_glyph (cairo_truetype_font_t       *font,
+                                          unsigned char                *buffer,
+                                          unsigned long                 size)
+{
+    tt_glyph_data_t *glyph_data;
+    tt_composite_glyph_t *composite_glyph;
+    int num_args;
+    int has_more_components;
+    unsigned short flags;
+    unsigned short index;
+    cairo_status_t status;
+    unsigned char *end = buffer + size;
+
+    if (font->status)
+       return font->status;
+
+    glyph_data = (tt_glyph_data_t *) buffer;
+    if ((unsigned char *)(&glyph_data->data) >= end)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    if ((int16_t)be16_to_cpu (glyph_data->num_contours) >= 0)
+        return CAIRO_STATUS_SUCCESS;
+
+    composite_glyph = &glyph_data->glyph;
+    do {
+       if ((unsigned char *)(&composite_glyph->args[1]) > end)
+           return CAIRO_INT_STATUS_UNSUPPORTED;
+
+       flags = be16_to_cpu (composite_glyph->flags);
+        has_more_components = flags & TT_MORE_COMPONENTS;
+        status = cairo_truetype_font_use_glyph (font, be16_to_cpu (composite_glyph->index), &index);
+       if (unlikely (status))
+           return status;
+
+        composite_glyph->index = cpu_to_be16 (index);
+        num_args = 1;
+        if (flags & TT_ARG_1_AND_2_ARE_WORDS)
+            num_args += 1;
+
+       if (flags & TT_WE_HAVE_A_SCALE)
+            num_args += 1;
+        else if (flags & TT_WE_HAVE_AN_X_AND_Y_SCALE)
+            num_args += 2;
+        else if (flags & TT_WE_HAVE_A_TWO_BY_TWO)
+            num_args += 4;
+
+       composite_glyph = (tt_composite_glyph_t *) &(composite_glyph->args[num_args]);
+    } while (has_more_components);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+cairo_truetype_font_write_glyf_table (cairo_truetype_font_t *font,
+                                     unsigned long          tag)
+{
+    unsigned long start_offset, index, size, next;
+    tt_head_t header;
+    unsigned long begin, end;
+    unsigned char *buffer;
+    unsigned int i;
+    union {
+       unsigned char *bytes;
+       uint16_t      *short_offsets;
+       uint32_t      *long_offsets;
+    } u;
+    cairo_status_t status;
+
+    if (font->status)
+       return font->status;
+
+    size = sizeof (tt_head_t);
+    status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font,
+                                                TT_TAG_head, 0,
+                                                (unsigned char*) &header, &size);
+    if (unlikely (status))
+       return _cairo_truetype_font_set_error (font, status);
+
+    if (be16_to_cpu (header.index_to_loc_format) == 0)
+       size = sizeof (int16_t) * (font->num_glyphs_in_face + 1);
+    else
+       size = sizeof (int32_t) * (font->num_glyphs_in_face + 1);
+
+    u.bytes = malloc (size);
+    if (unlikely (u.bytes == NULL))
+       return _cairo_truetype_font_set_error (font, CAIRO_STATUS_NO_MEMORY);
+
+    status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font,
+                                                 TT_TAG_loca, 0, u.bytes, &size);
+    if (unlikely (status))
+       return _cairo_truetype_font_set_error (font, status);
+
+    start_offset = _cairo_array_num_elements (&font->output);
+    for (i = 0; i < font->base.num_glyphs; i++) {
+       index = font->glyphs[i].parent_index;
+       if (be16_to_cpu (header.index_to_loc_format) == 0) {
+           begin = be16_to_cpu (u.short_offsets[index]) * 2;
+           end = be16_to_cpu (u.short_offsets[index + 1]) * 2;
+       }
+       else {
+           begin = be32_to_cpu (u.long_offsets[index]);
+           end = be32_to_cpu (u.long_offsets[index + 1]);
+       }
+
+       /* quick sanity check... */
+       if (end < begin) {
+           status = CAIRO_INT_STATUS_UNSUPPORTED;
+           goto FAIL;
+       }
+
+       size = end - begin;
+        status = cairo_truetype_font_align_output (font, &next);
+       if (unlikely (status))
+           goto FAIL;
+
+        status = cairo_truetype_font_check_boundary (font, next);
+       if (unlikely (status))
+           goto FAIL;
+
+        font->glyphs[i].location = next - start_offset;
+
+       status = cairo_truetype_font_allocate_write_buffer (font, size, &buffer);
+       if (unlikely (status))
+           goto FAIL;
+
+        if (size != 0) {
+            status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font,
+                                                        TT_TAG_glyf, begin, buffer, &size);
+           if (unlikely (status))
+               goto FAIL;
+
+            status = cairo_truetype_font_remap_composite_glyph (font, buffer, size);
+           if (unlikely (status))
+               goto FAIL;
+        }
+    }
+
+    status = cairo_truetype_font_align_output (font, &next);
+    if (unlikely (status))
+       goto FAIL;
+
+    font->glyphs[i].location = next - start_offset;
+
+    status = font->status;
+FAIL:
+    free (u.bytes);
+
+    return _cairo_truetype_font_set_error (font, status);
+}
+
+static cairo_status_t
+cairo_truetype_font_write_head_table (cairo_truetype_font_t *font,
+                                      unsigned long          tag)
+{
+    unsigned char *buffer;
+    unsigned long size;
+    cairo_status_t status;
+
+    if (font->status)
+       return font->status;
+
+    size = 0;
+    status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font,
+                                                tag, 0, NULL, &size);
+    if (unlikely (status))
+       return _cairo_truetype_font_set_error (font, status);
+
+    font->checksum_index = _cairo_array_num_elements (&font->output) + 8;
+    status = cairo_truetype_font_allocate_write_buffer (font, size, &buffer);
+    if (unlikely (status))
+       return _cairo_truetype_font_set_error (font, status);
+
+    status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font,
+                                                tag, 0, buffer, &size);
+    if (unlikely (status))
+       return _cairo_truetype_font_set_error (font, status);
+
+    /* set checkSumAdjustment to 0 for table checksum calculation */
+    *(uint32_t *)(buffer + 8) = 0;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+cairo_truetype_font_write_hhea_table (cairo_truetype_font_t *font, unsigned long tag)
+{
+    tt_hhea_t *hhea;
+    unsigned long size;
+    cairo_status_t status;
+
+    if (font->status)
+       return font->status;
+
+    size = sizeof (tt_hhea_t);
+    status = cairo_truetype_font_allocate_write_buffer (font, size, (unsigned char **) &hhea);
+    if (unlikely (status))
+       return _cairo_truetype_font_set_error (font, status);
+
+    status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font,
+                                                tag, 0, (unsigned char *) hhea, &size);
+    if (unlikely (status))
+       return _cairo_truetype_font_set_error (font, status);
+
+    hhea->num_hmetrics = cpu_to_be16 ((uint16_t)(font->base.num_glyphs));
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+cairo_truetype_font_write_hmtx_table (cairo_truetype_font_t *font,
+                                     unsigned long          tag)
+{
+    unsigned long size;
+    unsigned long long_entry_size;
+    unsigned long short_entry_size;
+    short *p;
+    unsigned int i;
+    tt_hhea_t hhea;
+    int num_hmetrics;
+    cairo_status_t status;
+
+    if (font->status)
+       return font->status;
+
+    size = sizeof (tt_hhea_t);
+    status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font,
+                                                TT_TAG_hhea, 0,
+                                                (unsigned char*) &hhea, &size);
+    if (unlikely (status))
+       return _cairo_truetype_font_set_error (font, status);
+
+    num_hmetrics = be16_to_cpu(hhea.num_hmetrics);
+
+    for (i = 0; i < font->base.num_glyphs; i++) {
+        long_entry_size = 2 * sizeof (int16_t);
+        short_entry_size = sizeof (int16_t);
+        status = cairo_truetype_font_allocate_write_buffer (font,
+                                                           long_entry_size,
+                                                           (unsigned char **) &p);
+       if (unlikely (status))
+           return _cairo_truetype_font_set_error (font, status);
+
+        if (font->glyphs[i].parent_index < num_hmetrics) {
+            status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font,
+                                                         TT_TAG_hmtx,
+                                                         font->glyphs[i].parent_index * long_entry_size,
+                                                         (unsigned char *) p, &long_entry_size);
+           if (unlikely (status))
+               return _cairo_truetype_font_set_error (font, status);
+        }
+        else
+        {
+            status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font,
+                                                         TT_TAG_hmtx,
+                                                        (num_hmetrics - 1) * long_entry_size,
+                                                        (unsigned char *) p, &short_entry_size);
+           if (unlikely (status))
+               return _cairo_truetype_font_set_error (font, status);
+
+            status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font,
+                                                        TT_TAG_hmtx,
+                                                        num_hmetrics * long_entry_size +
+                                                        (font->glyphs[i].parent_index - num_hmetrics) * short_entry_size,
+                                                        (unsigned char *) (p + 1), &short_entry_size);
+           if (unlikely (status))
+               return _cairo_truetype_font_set_error (font, status);
+        }
+        font->base.widths[i] = be16_to_cpu (p[0]);
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+cairo_truetype_font_write_loca_table (cairo_truetype_font_t *font,
+                                     unsigned long          tag)
+{
+    unsigned int i;
+    tt_head_t header;
+    unsigned long size;
+    cairo_status_t status;
+
+    if (font->status)
+       return font->status;
+
+    size = sizeof(tt_head_t);
+    status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font,
+                                                TT_TAG_head, 0,
+                                                (unsigned char*) &header, &size);
+    if (unlikely (status))
+       return _cairo_truetype_font_set_error (font, status);
+
+    if (be16_to_cpu (header.index_to_loc_format) == 0)
+    {
+       for (i = 0; i < font->base.num_glyphs + 1; i++)
+           cairo_truetype_font_write_be16 (font, font->glyphs[i].location / 2);
+    } else {
+       for (i = 0; i < font->base.num_glyphs + 1; i++)
+           cairo_truetype_font_write_be32 (font, font->glyphs[i].location);
+    }
+
+    return font->status;
+}
+
+static cairo_status_t
+cairo_truetype_font_write_maxp_table (cairo_truetype_font_t *font,
+                                     unsigned long          tag)
+{
+    tt_maxp_t *maxp;
+    unsigned long size;
+    cairo_status_t status;
+
+    if (font->status)
+       return font->status;
+
+    size = sizeof (tt_maxp_t);
+    status = cairo_truetype_font_allocate_write_buffer (font, size, (unsigned char **) &maxp);
+    if (unlikely (status))
+       return _cairo_truetype_font_set_error (font, status);
+
+    status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font,
+                                                tag, 0, (unsigned char *) maxp, &size);
+    if (unlikely (status))
+       return _cairo_truetype_font_set_error (font, status);
+
+    maxp->num_glyphs = cpu_to_be16 (font->base.num_glyphs);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+cairo_truetype_font_write_offset_table (cairo_truetype_font_t *font)
+{
+    cairo_status_t status;
+    unsigned char *table_buffer;
+    size_t table_buffer_length;
+    unsigned short search_range, entry_selector, range_shift;
+
+    if (font->status)
+       return font->status;
+
+    search_range = 1;
+    entry_selector = 0;
+    while (search_range * 2 <= font->num_tables) {
+       search_range *= 2;
+       entry_selector++;
+    }
+    search_range *= 16;
+    range_shift = font->num_tables * 16 - search_range;
+
+    cairo_truetype_font_write_be32 (font, SFNT_VERSION);
+    cairo_truetype_font_write_be16 (font, font->num_tables);
+    cairo_truetype_font_write_be16 (font, search_range);
+    cairo_truetype_font_write_be16 (font, entry_selector);
+    cairo_truetype_font_write_be16 (font, range_shift);
+
+    /* Allocate space for the table directory. Each directory entry
+     * will be filled in by cairo_truetype_font_update_entry() after
+     * the table is written. */
+    table_buffer_length = font->num_tables * 16;
+    status = cairo_truetype_font_allocate_write_buffer (font, table_buffer_length,
+                                                     &table_buffer);
+    if (unlikely (status))
+       return _cairo_truetype_font_set_error (font, status);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static uint32_t
+cairo_truetype_font_calculate_checksum (cairo_truetype_font_t *font,
+                                       unsigned long          start,
+                                       unsigned long          end)
+{
+    uint32_t *padded_end;
+    uint32_t *p;
+    uint32_t checksum;
+    char *data;
+
+    checksum = 0;
+    data = _cairo_array_index (&font->output, 0);
+    p = (uint32_t *) (data + start);
+    padded_end = (uint32_t *) (data + ((end + 3) & ~3));
+    while (p < padded_end)
+       checksum += be32_to_cpu(*p++);
+
+    return checksum;
+}
+
+static void
+cairo_truetype_font_update_entry (cairo_truetype_font_t *font,
+                                 int                    index,
+                                 unsigned long          tag,
+                                 unsigned long          start,
+                                 unsigned long          end)
+{
+    uint32_t *entry;
+
+    entry = _cairo_array_index (&font->output, 12 + 16 * index);
+    entry[0] = cpu_to_be32 ((uint32_t)tag);
+    entry[1] = cpu_to_be32 (cairo_truetype_font_calculate_checksum (font, start, end));
+    entry[2] = cpu_to_be32 ((uint32_t)start);
+    entry[3] = cpu_to_be32 ((uint32_t)(end - start));
+}
+
+static cairo_status_t
+cairo_truetype_font_generate (cairo_truetype_font_t  *font,
+                             const char            **data,
+                             unsigned long          *length,
+                             const unsigned long   **string_offsets,
+                             unsigned long          *num_strings)
+{
+    cairo_status_t status;
+    unsigned long start, end, next;
+    uint32_t checksum, *checksum_location;
+    int i;
+
+    if (font->status)
+       return font->status;
+
+    status = cairo_truetype_font_write_offset_table (font);
+    if (unlikely (status))
+       goto FAIL;
+
+    status = cairo_truetype_font_align_output (font, &start);
+    if (unlikely (status))
+       goto FAIL;
+
+    end = 0;
+    for (i = 0; i < font->num_tables; i++) {
+       status = font->truetype_tables[i].write (font, font->truetype_tables[i].tag);
+       if (unlikely (status))
+           goto FAIL;
+
+       end = _cairo_array_num_elements (&font->output);
+       status = cairo_truetype_font_align_output (font, &next);
+       if (unlikely (status))
+           goto FAIL;
+
+       cairo_truetype_font_update_entry (font, font->truetype_tables[i].pos,
+                                          font->truetype_tables[i].tag, start, end);
+        status = cairo_truetype_font_check_boundary (font, next);
+       if (unlikely (status))
+           goto FAIL;
+
+       start = next;
+    }
+
+    checksum =
+       0xb1b0afba - cairo_truetype_font_calculate_checksum (font, 0, end);
+    checksum_location = _cairo_array_index (&font->output, font->checksum_index);
+    *checksum_location = cpu_to_be32 (checksum);
+
+    *data = _cairo_array_index (&font->output, 0);
+    *length = _cairo_array_num_elements (&font->output);
+    *num_strings = _cairo_array_num_elements (&font->string_offsets);
+    if (*num_strings != 0)
+       *string_offsets = _cairo_array_index (&font->string_offsets, 0);
+    else
+       *string_offsets = NULL;
+
+ FAIL:
+    return _cairo_truetype_font_set_error (font, status);
+}
+
+static cairo_status_t
+cairo_truetype_font_use_glyph (cairo_truetype_font_t       *font,
+                              unsigned short                glyph,
+                              unsigned short               *out)
+{
+    if (glyph >= font->num_glyphs_in_face)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    if (font->parent_to_subset[glyph] == 0) {
+       font->parent_to_subset[glyph] = font->base.num_glyphs;
+       font->glyphs[font->base.num_glyphs].parent_index = glyph;
+       font->base.num_glyphs++;
+    }
+
+    *out = font->parent_to_subset[glyph];
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+cairo_truetype_font_add_truetype_table (cairo_truetype_font_t *font,
+           unsigned long tag,
+           cairo_status_t (*write) (cairo_truetype_font_t *font, unsigned long tag),
+           int pos)
+{
+    font->truetype_tables[font->num_tables].tag = tag;
+    font->truetype_tables[font->num_tables].write = write;
+    font->truetype_tables[font->num_tables].pos = pos;
+    font->num_tables++;
+}
+
+/* cairo_truetype_font_create_truetype_table_list() builds the list of
+ * truetype tables to be embedded in the subsetted font. Each call to
+ * cairo_truetype_font_add_truetype_table() adds a table, the callback
+ * for generating the table, and the position in the table directory
+ * to the truetype_tables array.
+ *
+ * As we write out the glyf table we remap composite glyphs.
+ * Remapping composite glyphs will reference the sub glyphs the
+ * composite glyph is made up of. The "glyf" table callback needs to
+ * be called first so we have all the glyphs in the subset before
+ * going further.
+ *
+ * The order in which tables are added to the truetype_table array
+ * using cairo_truetype_font_add_truetype_table() specifies the order
+ * in which the callback functions will be called.
+ *
+ * The tables in the table directory must be listed in alphabetical
+ * order.  The "cvt", "fpgm", and "prep" are optional tables. They
+ * will only be embedded in the subset if they exist in the source
+ * font. "cmap" is only embedded for latin fonts. The pos parameter of
+ * cairo_truetype_font_add_truetype_table() specifies the position of
+ * the table in the table directory.
+ */
+static void
+cairo_truetype_font_create_truetype_table_list (cairo_truetype_font_t *font)
+{
+    cairo_bool_t has_cvt = FALSE;
+    cairo_bool_t has_fpgm = FALSE;
+    cairo_bool_t has_prep = FALSE;
+    unsigned long size;
+    int pos;
+
+    size = 0;
+    if (font->backend->load_truetype_table (font->scaled_font_subset->scaled_font,
+                                      TT_TAG_cvt, 0, NULL,
+                                      &size) == CAIRO_INT_STATUS_SUCCESS)
+        has_cvt = TRUE;
+
+    size = 0;
+    if (font->backend->load_truetype_table (font->scaled_font_subset->scaled_font,
+                                      TT_TAG_fpgm, 0, NULL,
+                                      &size) == CAIRO_INT_STATUS_SUCCESS)
+        has_fpgm = TRUE;
+
+    size = 0;
+    if (font->backend->load_truetype_table (font->scaled_font_subset->scaled_font,
+                                      TT_TAG_prep, 0, NULL,
+                                      &size) == CAIRO_INT_STATUS_SUCCESS)
+        has_prep = TRUE;
+
+    font->num_tables = 0;
+    pos = 0;
+    if (font->is_pdf && font->scaled_font_subset->is_latin)
+       pos++;
+    if (has_cvt)
+        pos++;
+    if (has_fpgm)
+        pos++;
+    cairo_truetype_font_add_truetype_table (font, TT_TAG_glyf, cairo_truetype_font_write_glyf_table, pos);
+
+    pos = 0;
+    if (font->is_pdf && font->scaled_font_subset->is_latin)
+       cairo_truetype_font_add_truetype_table (font, TT_TAG_cmap, cairo_truetype_font_write_cmap_table, pos++);
+    if (has_cvt)
+        cairo_truetype_font_add_truetype_table (font, TT_TAG_cvt, cairo_truetype_font_write_generic_table, pos++);
+    if (has_fpgm)
+        cairo_truetype_font_add_truetype_table (font, TT_TAG_fpgm, cairo_truetype_font_write_generic_table, pos++);
+    pos++;
+    cairo_truetype_font_add_truetype_table (font, TT_TAG_head, cairo_truetype_font_write_head_table, pos++);
+    cairo_truetype_font_add_truetype_table (font, TT_TAG_hhea, cairo_truetype_font_write_hhea_table, pos++);
+    cairo_truetype_font_add_truetype_table (font, TT_TAG_hmtx, cairo_truetype_font_write_hmtx_table, pos++);
+    cairo_truetype_font_add_truetype_table (font, TT_TAG_loca, cairo_truetype_font_write_loca_table, pos++);
+    cairo_truetype_font_add_truetype_table (font, TT_TAG_maxp, cairo_truetype_font_write_maxp_table, pos++);
+    if (has_prep)
+        cairo_truetype_font_add_truetype_table (font, TT_TAG_prep, cairo_truetype_font_write_generic_table, pos);
+}
+
+static cairo_status_t
+cairo_truetype_subset_init_internal (cairo_truetype_subset_t     *truetype_subset,
+                                     cairo_scaled_font_subset_t *font_subset,
+                                     cairo_bool_t                is_pdf)
+{
+    cairo_truetype_font_t *font = NULL;
+    cairo_status_t status;
+    const char *data = NULL; /* squelch bogus compiler warning */
+    unsigned long length = 0; /* squelch bogus compiler warning */
+    unsigned long offsets_length;
+    unsigned int i;
+    const unsigned long *string_offsets = NULL;
+    unsigned long num_strings = 0;
+
+    status = _cairo_truetype_font_create (font_subset, is_pdf, &font);
+    if (unlikely (status))
+       return status;
+
+    for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) {
+       unsigned short parent_glyph = font->scaled_font_subset->glyphs[i];
+       status = cairo_truetype_font_use_glyph (font, parent_glyph, &parent_glyph);
+       if (unlikely (status))
+           goto fail1;
+    }
+
+    cairo_truetype_font_create_truetype_table_list (font);
+    status = cairo_truetype_font_generate (font, &data, &length,
+                                           &string_offsets, &num_strings);
+    if (unlikely (status))
+       goto fail1;
+
+    truetype_subset->ps_name = strdup (font->base.ps_name);
+    if (unlikely (truetype_subset->ps_name == NULL)) {
+       status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+       goto fail1;
+    }
+
+    if (font->base.font_name != NULL) {
+       truetype_subset->family_name_utf8 = strdup (font->base.font_name);
+       if (unlikely (truetype_subset->family_name_utf8 == NULL)) {
+           status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+           goto fail2;
+       }
+    } else {
+       truetype_subset->family_name_utf8 = NULL;
+    }
+
+    /* The widths array returned must contain only widths for the
+     * glyphs in font_subset. Any subglyphs appended after
+     * font_subset->num_glyphs are omitted. */
+    truetype_subset->widths = calloc (sizeof (double),
+                                      font->scaled_font_subset->num_glyphs);
+    if (unlikely (truetype_subset->widths == NULL)) {
+       status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+       goto fail3;
+    }
+    for (i = 0; i < font->scaled_font_subset->num_glyphs; i++)
+       truetype_subset->widths[i] = (double)font->base.widths[i]/font->base.units_per_em;
+
+    truetype_subset->x_min = (double)font->base.x_min/font->base.units_per_em;
+    truetype_subset->y_min = (double)font->base.y_min/font->base.units_per_em;
+    truetype_subset->x_max = (double)font->base.x_max/font->base.units_per_em;
+    truetype_subset->y_max = (double)font->base.y_max/font->base.units_per_em;
+    truetype_subset->ascent = (double)font->base.ascent/font->base.units_per_em;
+    truetype_subset->descent = (double)font->base.descent/font->base.units_per_em;
+
+    if (length) {
+       truetype_subset->data = malloc (length);
+       if (unlikely (truetype_subset->data == NULL)) {
+           status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+           goto fail4;
+       }
+
+       memcpy (truetype_subset->data, data, length);
+    } else
+       truetype_subset->data = NULL;
+    truetype_subset->data_length = length;
+
+    if (num_strings) {
+       offsets_length = num_strings * sizeof (unsigned long);
+       truetype_subset->string_offsets = malloc (offsets_length);
+       if (unlikely (truetype_subset->string_offsets == NULL)) {
+           status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+           goto fail5;
+       }
+
+       memcpy (truetype_subset->string_offsets, string_offsets, offsets_length);
+       truetype_subset->num_string_offsets = num_strings;
+    } else {
+       truetype_subset->string_offsets = NULL;
+       truetype_subset->num_string_offsets = 0;
+    }
+
+    cairo_truetype_font_destroy (font);
+
+    return CAIRO_STATUS_SUCCESS;
+
+ fail5:
+    free (truetype_subset->data);
+ fail4:
+    free (truetype_subset->widths);
+ fail3:
+    free (truetype_subset->family_name_utf8);
+ fail2:
+    free (truetype_subset->ps_name);
+ fail1:
+    cairo_truetype_font_destroy (font);
+
+    return status;
+}
+
+cairo_status_t
+_cairo_truetype_subset_init_ps (cairo_truetype_subset_t    *truetype_subset,
+                               cairo_scaled_font_subset_t      *font_subset)
+{
+    return cairo_truetype_subset_init_internal (truetype_subset, font_subset, FALSE);
+}
+
+cairo_status_t
+_cairo_truetype_subset_init_pdf (cairo_truetype_subset_t    *truetype_subset,
+                               cairo_scaled_font_subset_t      *font_subset)
+{
+    return cairo_truetype_subset_init_internal (truetype_subset, font_subset, TRUE);
+}
+
+void
+_cairo_truetype_subset_fini (cairo_truetype_subset_t *subset)
+{
+    free (subset->ps_name);
+    free (subset->family_name_utf8);
+    free (subset->widths);
+    free (subset->data);
+    free (subset->string_offsets);
+}
+
+static cairo_int_status_t
+_cairo_truetype_reverse_cmap (cairo_scaled_font_t *scaled_font,
+                             unsigned long        table_offset,
+                             unsigned long        index,
+                             uint32_t            *ucs4)
+{
+    cairo_status_t status;
+    const cairo_scaled_font_backend_t *backend;
+    tt_segment_map_t *map;
+    char buf[4];
+    unsigned int num_segments, i;
+    unsigned long size;
+    uint16_t *start_code;
+    uint16_t *end_code;
+    uint16_t *delta;
+    uint16_t *range_offset;
+    uint16_t *glyph_array;
+    uint16_t  c;
+
+    backend = scaled_font->backend;
+    size = 4;
+    status = backend->load_truetype_table (scaled_font,
+                                           TT_TAG_cmap, table_offset,
+                                          (unsigned char *) &buf,
+                                          &size);
+    if (unlikely (status))
+       return status;
+
+    /* All table formats have the same first two words */
+    map = (tt_segment_map_t *) buf;
+    if (be16_to_cpu (map->format) != 4)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    size = be16_to_cpu (map->length);
+    map = malloc (size);
+    if (unlikely (map == NULL))
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    status = backend->load_truetype_table (scaled_font,
+                                           TT_TAG_cmap, table_offset,
+                                           (unsigned char *) map,
+                                           &size);
+    if (unlikely (status))
+       goto fail;
+
+    num_segments = be16_to_cpu (map->segCountX2)/2;
+
+    /* A Format 4 cmap contains 8 uint16_t numbers and 4 arrays of
+     * uint16_t each num_segments long. */
+    if (size < (8 + 4*num_segments)*sizeof(uint16_t))
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    end_code = map->endCount;
+    start_code = &(end_code[num_segments + 1]);
+    delta = &(start_code[num_segments]);
+    range_offset = &(delta[num_segments]);
+    glyph_array = &(range_offset[num_segments]);
+
+    /* search for glyph in segments with rangeOffset=0 */
+    for (i = 0; i < num_segments; i++) {
+       c = index - be16_to_cpu (delta[i]);
+       if (range_offset[i] == 0 &&
+           c >= be16_to_cpu (start_code[i]) &&
+           c <= be16_to_cpu (end_code[i]))
+       {
+           *ucs4 = c;
+           goto found;
+       }
+    }
+
+    /* search for glyph in segments with rangeOffset=1 */
+    for (i = 0; i < num_segments; i++) {
+       if (range_offset[i] != 0) {
+           uint16_t *glyph_ids = &range_offset[i] + be16_to_cpu (range_offset[i])/2;
+           int range_size = be16_to_cpu (end_code[i]) - be16_to_cpu (start_code[i]) + 1;
+           uint16_t g_id_be = cpu_to_be16 (index);
+           int j;
+
+           if (range_size > 0) {
+               if ((char*)glyph_ids + 2*range_size > (char*)map + size)
+                   return CAIRO_INT_STATUS_UNSUPPORTED;
+
+               for (j = 0; j < range_size; j++) {
+                   if (glyph_ids[j] == g_id_be) {
+                       *ucs4 = be16_to_cpu (start_code[i]) + j;
+                       goto found;
+                   }
+               }
+           }
+       }
+    }
+
+    /* glyph not found */
+    *ucs4 = -1;
+
+found:
+    status = CAIRO_STATUS_SUCCESS;
+
+fail:
+    free (map);
+
+    return status;
+}
+
+cairo_int_status_t
+_cairo_truetype_index_to_ucs4 (cairo_scaled_font_t *scaled_font,
+                               unsigned long        index,
+                               uint32_t            *ucs4)
+{
+    cairo_int_status_t status = CAIRO_INT_STATUS_UNSUPPORTED;
+    const cairo_scaled_font_backend_t *backend;
+    tt_cmap_t *cmap;
+    char buf[4];
+    int num_tables, i;
+    unsigned long size;
+
+    backend = scaled_font->backend;
+    if (!backend->load_truetype_table)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    size = 4;
+    status = backend->load_truetype_table (scaled_font,
+                                           TT_TAG_cmap, 0,
+                                          (unsigned char *) &buf,
+                                          &size);
+    if (unlikely (status))
+       return status;
+
+    cmap = (tt_cmap_t *) buf;
+    num_tables = be16_to_cpu (cmap->num_tables);
+    size = 4 + num_tables*sizeof(tt_cmap_index_t);
+    cmap = _cairo_malloc_ab_plus_c (num_tables, sizeof (tt_cmap_index_t), 4);
+    if (unlikely (cmap == NULL))
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    status = backend->load_truetype_table (scaled_font,
+                                          TT_TAG_cmap, 0,
+                                          (unsigned char *) cmap,
+                                          &size);
+    if (unlikely (status))
+        goto cleanup;
+
+    /* Find a table with Unicode mapping */
+    for (i = 0; i < num_tables; i++) {
+        if (be16_to_cpu (cmap->index[i].platform) == 3 &&
+            be16_to_cpu (cmap->index[i].encoding) == 1) {
+            status = _cairo_truetype_reverse_cmap (scaled_font,
+                                                  be32_to_cpu (cmap->index[i].offset),
+                                                  index,
+                                                  ucs4);
+            if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+                break;
+        }
+    }
+
+cleanup:
+    free (cmap);
+
+    return status;
+}
+
+static cairo_status_t
+find_name (tt_name_t *name, int name_id, int platform, int encoding, int language, char **str_out)
+{
+    tt_name_record_t *record;
+    int i, len;
+    char *str;
+    char *p;
+    cairo_bool_t has_tag;
+    cairo_status_t status;
+
+    str = NULL;
+    for (i = 0; i < be16_to_cpu (name->num_records); i++) {
+        record = &(name->records[i]);
+       if (be16_to_cpu (record->name) == name_id &&
+           be16_to_cpu (record->platform) == platform &&
+            be16_to_cpu (record->encoding) == encoding &&
+           (language == -1 || be16_to_cpu (record->language) == language)) {
+
+           str = malloc (be16_to_cpu (record->length) + 1);
+           if (str == NULL)
+               return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+           len = be16_to_cpu (record->length);
+           memcpy (str,
+                   ((char*)name) + be16_to_cpu (name->strings_offset) + be16_to_cpu (record->offset),
+                   len);
+           str[be16_to_cpu (record->length)] = 0;
+           break;
+       }
+    }
+    if (str == NULL) {
+       *str_out = NULL;
+       return CAIRO_STATUS_SUCCESS;
+    }
+
+    if (platform == 3) { /* Win platform, unicode encoding */
+       /* convert to utf8 */
+       int size = 0;
+       char *utf8;
+       uint16_t *u = (uint16_t *) str;
+       int u_len = len/2;
+
+       for (i = 0; i < u_len; i++)
+           size += _cairo_ucs4_to_utf8 (be16_to_cpu(u[i]), NULL);
+
+       utf8 = malloc (size + 1);
+       if (utf8 == NULL) {
+           status =_cairo_error (CAIRO_STATUS_NO_MEMORY);
+           goto fail;
+       }
+       p = utf8;
+       for (i = 0; i < u_len; i++)
+           p += _cairo_ucs4_to_utf8 (be16_to_cpu(u[i]), p);
+       *p = 0;
+       free (str);
+       str = utf8;
+    } else if (platform == 1) { /* Mac platform, Mac Roman encoding */
+       /* Replace characters above 127 with underscores. We could use
+        * a lookup table to convert to unicode but since most fonts
+        * include a unicode name this is just a rarely used fallback. */
+       for (i = 0; i < len; i++) {
+           if ((unsigned char)str[i] > 127)
+               str[i] = '_';
+       }
+    }
+
+    /* If font name is prefixed with a PDF subset tag, strip it off. */
+    p = str;
+    len = strlen (str);
+    has_tag = FALSE;
+    if (len > 7 && p[6] == '+') {
+       has_tag = TRUE;
+       for (i = 0; i < 6; i++) {
+           if (p[i] < 'A' || p[i] > 'Z') {
+               has_tag = FALSE;
+               break;
+           }
+       }
+    }
+    if (has_tag) {
+       p = malloc (len - 6);
+       if (unlikely (p == NULL)) {
+           status =_cairo_error (CAIRO_STATUS_NO_MEMORY);
+           goto fail;
+       }
+       memcpy (p, str + 7, len - 7);
+       p[len-7] = 0;
+       free (str);
+       str = p;
+    }
+
+    *str_out = str;
+
+    return CAIRO_STATUS_SUCCESS;
+
+  fail:
+    free (str);
+
+    return status;
+}
+
+cairo_int_status_t
+_cairo_truetype_read_font_name (cairo_scaled_font_t     *scaled_font,
+                               char                    **ps_name_out,
+                               char                    **font_name_out)
+{
+    cairo_status_t status;
+    const cairo_scaled_font_backend_t *backend;
+    tt_name_t *name;
+    unsigned long size;
+    char *ps_name = NULL;
+    char *family_name = NULL;
+
+    backend = scaled_font->backend;
+    if (!backend->load_truetype_table)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    size = 0;
+    status = backend->load_truetype_table (scaled_font,
+                                          TT_TAG_name, 0,
+                                          NULL,
+                                          &size);
+    if (status)
+       return status;
+
+    name = malloc (size);
+    if (name == NULL)
+        return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    status = backend->load_truetype_table (scaled_font,
+                                          TT_TAG_name, 0,
+                                          (unsigned char *) name,
+                                          &size);
+    if (status)
+       goto fail;
+
+    /* Find PS Name (name_id = 6). OT spec says PS name must be one of
+     * the following two encodings */
+    status = find_name (name, 6, 3, 1, 0x409, &ps_name); /* win, unicode, english-us */
+    if (unlikely(status))
+       goto fail;
+
+    if (!ps_name) {
+       status = find_name (name, 6, 1, 0, 0, &ps_name); /* mac, roman, english */
+       if (unlikely(status))
+           goto fail;
+    }
+
+    /* Find Family name (name_id = 1) */
+    status = find_name (name, 1, 3, 1, 0x409, &family_name); /* win, unicode, english-us */
+    if (unlikely(status))
+       goto fail;
+
+    if (!family_name) {
+       status = find_name (name, 1, 3, 0, 0x409, &family_name); /* win, symbol, english-us */
+       if (unlikely(status))
+           goto fail;
+    }
+
+    if (!family_name) {
+       status = find_name (name, 1, 1, 0, 0, &family_name); /* mac, roman, english */
+       if (unlikely(status))
+           goto fail;
+    }
+
+    if (!family_name) {
+       status = find_name (name, 1, 3, 1, -1, &family_name); /* win, unicode, any language */
+       if (unlikely(status))
+           goto fail;
+    }
+
+    free (name);
+
+    /* Ensure PS name is a valid PDF/PS name object. In PDF names are
+     * treated as UTF8 and non ASCII bytes, ' ', and '#' are encoded
+     * as '#' followed by 2 hex digits that encode the byte. By also
+     * encoding the characters in the reserved string we ensure the
+     * name is also PS compatible. */
+    if (ps_name) {
+       static const char *reserved = "()<>[]{}/%#\\";
+       char buf[128]; /* max name length is 127 bytes */
+       char *src = ps_name;
+       char *dst = buf;
+
+       while (*src && dst < buf + 127) {
+           unsigned char c = *src;
+           if (c < 0x21 || c > 0x7e || strchr (reserved, c)) {
+               if (dst + 4 > buf + 127)
+                   break;
+
+               snprintf (dst, 4, "#%02X", c);
+               src++;
+               dst += 3;
+           } else {
+               *dst++ = *src++;
+           }
+       }
+       *dst = 0;
+       free (ps_name);
+       ps_name = strdup (buf);
+       if (ps_name == NULL) {
+           status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+           goto fail;
+       }
+    }
+
+    *ps_name_out = ps_name;
+    *font_name_out = family_name;
+
+    return CAIRO_STATUS_SUCCESS;
+
+fail:
+    free (name);
+    free (ps_name);
+    free (family_name);
+    *ps_name_out = NULL;
+    *font_name_out = NULL;
+
+    return status;
+}
+
+cairo_int_status_t
+_cairo_truetype_get_style (cairo_scaled_font_t          *scaled_font,
+                          int                           *weight,
+                          cairo_bool_t                  *bold,
+                          cairo_bool_t                  *italic)
+{
+    cairo_status_t status;
+    const cairo_scaled_font_backend_t *backend;
+    tt_os2_t os2;
+    unsigned long size;
+    uint16_t selection;
+
+    backend = scaled_font->backend;
+    if (!backend->load_truetype_table)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    size = 0;
+    status = backend->load_truetype_table (scaled_font,
+                                          TT_TAG_OS2, 0,
+                                          NULL,
+                                          &size);
+    if (status)
+       return status;
+
+    if (size < sizeof(os2))
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    size = sizeof (os2);
+    status = backend->load_truetype_table (scaled_font,
+                                          TT_TAG_OS2, 0,
+                                          (unsigned char *) &os2,
+                                          &size);
+    if (status)
+       return status;
+
+    *weight = be16_to_cpu (os2.usWeightClass);
+    selection = be16_to_cpu (os2.fsSelection);
+    *bold = (selection & TT_FS_SELECTION_BOLD) ? TRUE : FALSE;
+    *italic = (selection & TT_FS_SELECTION_ITALIC) ? TRUE : FALSE;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+#endif /* CAIRO_HAS_FONT_SUBSET */
diff --git a/src/cairo-type1-fallback.c b/src/cairo-type1-fallback.c
new file mode 100644 (file)
index 0000000..4a65741
--- /dev/null
@@ -0,0 +1,903 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2006 Red Hat, Inc
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ *     Adrian Johnson <ajohnson@redneon.com>
+ */
+
+#define _BSD_SOURCE /* for snprintf(), strdup() */
+#include "cairoint.h"
+
+#include "cairo-array-private.h"
+#include "cairo-error-private.h"
+
+#if CAIRO_HAS_FONT_SUBSET
+
+#include "cairo-type1-private.h"
+#include "cairo-scaled-font-subsets-private.h"
+#include "cairo-path-fixed-private.h"
+#include "cairo-output-stream-private.h"
+
+typedef enum {
+    CAIRO_CHARSTRING_TYPE1,
+    CAIRO_CHARSTRING_TYPE2
+} cairo_charstring_type_t;
+
+typedef struct _cairo_type1_font {
+    int *widths;
+
+    cairo_scaled_font_subset_t *scaled_font_subset;
+    cairo_scaled_font_t        *type1_scaled_font;
+
+    cairo_array_t contents;
+
+    double x_min, y_min, x_max, y_max;
+
+    const char    *data;
+    unsigned long  header_size;
+    unsigned long  data_size;
+    unsigned long  trailer_size;
+    int            bbox_position;
+    int            bbox_max_chars;
+
+    cairo_output_stream_t *output;
+
+    unsigned short eexec_key;
+    cairo_bool_t hex_encode;
+    int hex_column;
+} cairo_type1_font_t;
+
+static cairo_status_t
+cairo_type1_font_create (cairo_scaled_font_subset_t  *scaled_font_subset,
+                         cairo_type1_font_t         **subset_return,
+                         cairo_bool_t                 hex_encode)
+{
+    cairo_type1_font_t *font;
+    cairo_font_face_t *font_face;
+    cairo_matrix_t font_matrix;
+    cairo_matrix_t ctm;
+    cairo_font_options_t font_options;
+    cairo_status_t status;
+
+    font = calloc (1, sizeof (cairo_type1_font_t));
+    if (unlikely (font == NULL))
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    font->widths = calloc (scaled_font_subset->num_glyphs, sizeof (int));
+    if (unlikely (font->widths == NULL)) {
+       free (font);
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+    }
+
+    font->scaled_font_subset = scaled_font_subset;
+    font->hex_encode = hex_encode;
+
+    font_face = cairo_scaled_font_get_font_face (scaled_font_subset->scaled_font);
+
+    cairo_matrix_init_scale (&font_matrix, 1000, -1000);
+    cairo_matrix_init_identity (&ctm);
+
+    _cairo_font_options_init_default (&font_options);
+    cairo_font_options_set_hint_style (&font_options, CAIRO_HINT_STYLE_NONE);
+    cairo_font_options_set_hint_metrics (&font_options, CAIRO_HINT_METRICS_OFF);
+
+    font->type1_scaled_font = cairo_scaled_font_create (font_face,
+                                                       &font_matrix,
+                                                       &ctm,
+                                                       &font_options);
+    status = font->type1_scaled_font->status;
+    if (unlikely (status))
+        goto fail;
+
+    _cairo_array_init (&font->contents, sizeof (unsigned char));
+    font->output = NULL;
+
+    *subset_return = font;
+
+    return CAIRO_STATUS_SUCCESS;
+
+fail:
+    free (font->widths);
+    free (font);
+
+    return status;
+}
+
+/* Charstring commands. If the high byte is 0 the command is encoded
+ * with a single byte. */
+#define CHARSTRING_sbw        0x0c07
+#define CHARSTRING_rmoveto    0x0015
+#define CHARSTRING_rlineto    0x0005
+#define CHARSTRING_rcurveto   0x0008
+#define CHARSTRING_closepath  0x0009
+#define CHARSTRING_endchar    0x000e
+
+/* Before calling this function, the caller must allocate sufficient
+ * space in data (see _cairo_array_grow_by). The maximum number of
+ * bytes that will be used is 2.
+ */
+static void
+charstring_encode_command (cairo_array_t *data, int command)
+{
+    cairo_status_t status;
+    unsigned int orig_size;
+    unsigned char buf[5];
+    unsigned char *p = buf;
+
+    if (command & 0xff00)
+        *p++ = command >> 8;
+    *p++ = command & 0x00ff;
+
+    /* Ensure the array doesn't grow, which allows this function to
+     * have no possibility of failure. */
+    orig_size = _cairo_array_size (data);
+    status = _cairo_array_append_multiple (data, buf, p - buf);
+
+    assert (status == CAIRO_STATUS_SUCCESS);
+    assert (_cairo_array_size (data) == orig_size);
+}
+
+/* Before calling this function, the caller must allocate sufficient
+ * space in data (see _cairo_array_grow_by). The maximum number of
+ * bytes that will be used is 5.
+ */
+static void
+charstring_encode_integer (cairo_array_t *data,
+                           int i,
+                           cairo_charstring_type_t type)
+{
+    cairo_status_t status;
+    unsigned int orig_size;
+    unsigned char buf[10];
+    unsigned char *p = buf;
+
+    if (i >= -107 && i <= 107) {
+        *p++ = i + 139;
+    } else if (i >= 108 && i <= 1131) {
+        i -= 108;
+        *p++ = (i >> 8)+ 247;
+        *p++ = i & 0xff;
+    } else if (i >= -1131 && i <= -108) {
+        i = -i - 108;
+        *p++ = (i >> 8)+ 251;
+        *p++ = i & 0xff;
+    } else {
+        if (type == CAIRO_CHARSTRING_TYPE1) {
+            *p++ = 0xff;
+            *p++ = i >> 24;
+            *p++ = (i >> 16) & 0xff;
+            *p++ = (i >> 8)  & 0xff;
+            *p++ = i & 0xff;
+        } else {
+            *p++ = 0xff;
+            *p++ = (i >> 8)  & 0xff;
+            *p++ = i & 0xff;
+            *p++ = 0;
+            *p++ = 0;
+        }
+    }
+
+    /* Ensure the array doesn't grow, which allows this function to
+     * have no possibility of failure. */
+    orig_size = _cairo_array_size (data);
+    status = _cairo_array_append_multiple (data, buf, p - buf);
+
+    assert (status == CAIRO_STATUS_SUCCESS);
+    assert (_cairo_array_size (data) == orig_size);
+}
+
+typedef struct _ps_path_info {
+    cairo_array_t *data;
+    int current_x, current_y;
+    cairo_charstring_type_t type;
+} t1_path_info_t;
+
+static cairo_status_t
+_charstring_move_to (void                  *closure,
+                     const cairo_point_t    *point)
+{
+    t1_path_info_t *path_info = (t1_path_info_t *) closure;
+    int dx, dy;
+    cairo_status_t status;
+
+    status = _cairo_array_grow_by (path_info->data, 12);
+    if (unlikely (status))
+        return status;
+
+    dx = _cairo_fixed_integer_part (point->x) - path_info->current_x;
+    dy = _cairo_fixed_integer_part (point->y) - path_info->current_y;
+    charstring_encode_integer (path_info->data, dx, path_info->type);
+    charstring_encode_integer (path_info->data, dy, path_info->type);
+    path_info->current_x += dx;
+    path_info->current_y += dy;
+
+    charstring_encode_command (path_info->data, CHARSTRING_rmoveto);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_charstring_line_to (void                  *closure,
+                     const cairo_point_t    *point)
+{
+    t1_path_info_t *path_info = (t1_path_info_t *) closure;
+    int dx, dy;
+    cairo_status_t status;
+
+    status = _cairo_array_grow_by (path_info->data, 12);
+    if (unlikely (status))
+        return status;
+
+    dx = _cairo_fixed_integer_part (point->x) - path_info->current_x;
+    dy = _cairo_fixed_integer_part (point->y) - path_info->current_y;
+    charstring_encode_integer (path_info->data, dx, path_info->type);
+    charstring_encode_integer (path_info->data, dy, path_info->type);
+    path_info->current_x += dx;
+    path_info->current_y += dy;
+
+    charstring_encode_command (path_info->data, CHARSTRING_rlineto);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_charstring_curve_to (void                 *closure,
+                      const cairo_point_t   *point1,
+                      const cairo_point_t   *point2,
+                      const cairo_point_t   *point3)
+{
+    t1_path_info_t *path_info = (t1_path_info_t *) closure;
+    int dx1, dy1, dx2, dy2, dx3, dy3;
+    cairo_status_t status;
+
+    status = _cairo_array_grow_by (path_info->data, 32);
+    if (unlikely (status))
+        return status;
+
+    dx1 = _cairo_fixed_integer_part (point1->x) - path_info->current_x;
+    dy1 = _cairo_fixed_integer_part (point1->y) - path_info->current_y;
+    dx2 = _cairo_fixed_integer_part (point2->x) - path_info->current_x - dx1;
+    dy2 = _cairo_fixed_integer_part (point2->y) - path_info->current_y - dy1;
+    dx3 = _cairo_fixed_integer_part (point3->x) - path_info->current_x - dx1 - dx2;
+    dy3 = _cairo_fixed_integer_part (point3->y) - path_info->current_y - dy1 - dy2;
+    charstring_encode_integer (path_info->data, dx1, path_info->type);
+    charstring_encode_integer (path_info->data, dy1, path_info->type);
+    charstring_encode_integer (path_info->data, dx2, path_info->type);
+    charstring_encode_integer (path_info->data, dy2, path_info->type);
+    charstring_encode_integer (path_info->data, dx3, path_info->type);
+    charstring_encode_integer (path_info->data, dy3, path_info->type);
+    path_info->current_x += dx1 + dx2 + dx3;
+    path_info->current_y += dy1 + dy2 + dy3;
+    charstring_encode_command (path_info->data, CHARSTRING_rcurveto);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_charstring_close_path (void *closure)
+{
+    cairo_status_t status;
+    t1_path_info_t *path_info = (t1_path_info_t *) closure;
+
+    if (path_info->type == CAIRO_CHARSTRING_TYPE2)
+        return CAIRO_STATUS_SUCCESS;
+
+    status = _cairo_array_grow_by (path_info->data, 2);
+    if (unlikely (status))
+       return status;
+
+    charstring_encode_command (path_info->data, CHARSTRING_closepath);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+charstring_encrypt (cairo_array_t *data)
+{
+    unsigned char *d, *end;
+    uint16_t c, p, r;
+
+    r = CAIRO_TYPE1_CHARSTRING_KEY;
+    d = (unsigned char *) _cairo_array_index (data, 0);
+    end = d + _cairo_array_num_elements (data);
+    while (d < end) {
+       p = *d;
+       c = p ^ (r >> 8);
+       r = (c + r) * CAIRO_TYPE1_ENCRYPT_C1 + CAIRO_TYPE1_ENCRYPT_C2;
+        *d++ = c;
+    }
+}
+
+static cairo_int_status_t
+cairo_type1_font_create_charstring (cairo_type1_font_t      *font,
+                                    int                      subset_index,
+                                    int                      glyph_index,
+                                    cairo_charstring_type_t  type,
+                                    cairo_array_t           *data)
+{
+    cairo_int_status_t status;
+    cairo_scaled_glyph_t *scaled_glyph;
+    t1_path_info_t path_info;
+    cairo_text_extents_t *metrics;
+    cairo_bool_t emit_path = TRUE;
+
+    /* This call may return CAIRO_INT_STATUS_UNSUPPORTED for bitmap fonts. */
+    status = _cairo_scaled_glyph_lookup (font->type1_scaled_font,
+                                        glyph_index,
+                                        CAIRO_SCALED_GLYPH_INFO_METRICS|
+                                        CAIRO_SCALED_GLYPH_INFO_PATH,
+                                        &scaled_glyph);
+
+    /* It is ok for the .notdef glyph to not have a path available. We
+     * just need the metrics to emit an empty glyph.  */
+    if (glyph_index == 0 && status == CAIRO_INT_STATUS_UNSUPPORTED) {
+       emit_path = FALSE;
+       status = _cairo_scaled_glyph_lookup (font->type1_scaled_font,
+                                            glyph_index,
+                                            CAIRO_SCALED_GLYPH_INFO_METRICS,
+                                            &scaled_glyph);
+    }
+    if (unlikely (status))
+        return status;
+
+    metrics = &scaled_glyph->metrics;
+    if (subset_index == 0) {
+        font->x_min = metrics->x_bearing;
+        font->y_min = metrics->y_bearing;
+        font->x_max = metrics->x_bearing + metrics->width;
+        font->y_max = metrics->y_bearing + metrics->height;
+    } else {
+        if (metrics->x_bearing < font->x_min)
+            font->x_min = metrics->x_bearing;
+        if (metrics->y_bearing < font->y_min)
+            font->y_min = metrics->y_bearing;
+        if (metrics->x_bearing + metrics->width > font->x_max)
+            font->x_max = metrics->x_bearing + metrics->width;
+        if (metrics->y_bearing + metrics->height > font->y_max)
+            font->y_max = metrics->y_bearing + metrics->height;
+    }
+    font->widths[subset_index] = metrics->x_advance;
+
+    status = _cairo_array_grow_by (data, 30);
+    if (unlikely (status))
+        return status;
+
+    if (type == CAIRO_CHARSTRING_TYPE1) {
+        charstring_encode_integer (data, (int) scaled_glyph->metrics.x_bearing, type);
+        charstring_encode_integer (data, (int) scaled_glyph->metrics.y_bearing, type);
+        charstring_encode_integer (data, (int) scaled_glyph->metrics.x_advance, type);
+        charstring_encode_integer (data, (int) scaled_glyph->metrics.y_advance, type);
+        charstring_encode_command (data, CHARSTRING_sbw);
+
+        path_info.current_x = (int) scaled_glyph->metrics.x_bearing;
+        path_info.current_y = (int) scaled_glyph->metrics.y_bearing;
+    } else {
+        charstring_encode_integer (data, (int) scaled_glyph->metrics.x_advance, type);
+
+        path_info.current_x = 0;
+        path_info.current_y = 0;
+    }
+    path_info.data = data;
+    path_info.type = type;
+    if (emit_path) {
+       status = _cairo_path_fixed_interpret (scaled_glyph->path,
+                                             _charstring_move_to,
+                                             _charstring_line_to,
+                                             _charstring_curve_to,
+                                             _charstring_close_path,
+                                             &path_info);
+       if (unlikely (status))
+           return status;
+    }
+
+    status = _cairo_array_grow_by (data, 1);
+    if (unlikely (status))
+        return status;
+    charstring_encode_command (path_info.data, CHARSTRING_endchar);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+cairo_type1_font_write_charstrings (cairo_type1_font_t    *font,
+                                    cairo_output_stream_t *encrypted_output)
+{
+    cairo_status_t status;
+    unsigned char zeros[] = { 0, 0, 0, 0 };
+    cairo_array_t data;
+    unsigned int i;
+    int length;
+
+    _cairo_array_init (&data, sizeof (unsigned char));
+    status = _cairo_array_grow_by (&data, 1024);
+    if (unlikely (status))
+        goto fail;
+
+    _cairo_output_stream_printf (encrypted_output,
+                                 "2 index /CharStrings %d dict dup begin\n",
+                                 font->scaled_font_subset->num_glyphs + 1);
+
+    _cairo_scaled_font_freeze_cache (font->type1_scaled_font);
+    for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) {
+        _cairo_array_truncate (&data, 0);
+        /* four "random" bytes required by encryption algorithm */
+        status = _cairo_array_append_multiple (&data, zeros, 4);
+        if (unlikely (status))
+           break;
+
+        status = cairo_type1_font_create_charstring (font, i,
+                                                    font->scaled_font_subset->glyphs[i],
+                                                     CAIRO_CHARSTRING_TYPE1,
+                                                    &data);
+        if (unlikely (status))
+           break;
+
+        charstring_encrypt (&data);
+        length = _cairo_array_num_elements (&data);
+       if (font->scaled_font_subset->glyph_names != NULL) {
+           _cairo_output_stream_printf (encrypted_output, "/%s %d RD ",
+                                        font->scaled_font_subset->glyph_names[i],
+                                        length);
+       } else if (i == 0) {
+           _cairo_output_stream_printf (encrypted_output, "/.notdef %d RD ", length);
+       } else {
+           _cairo_output_stream_printf (encrypted_output, "/g%d %d RD ", i, length);
+       }
+        _cairo_output_stream_write (encrypted_output,
+                                    _cairo_array_index (&data, 0),
+                                    length);
+        _cairo_output_stream_printf (encrypted_output, " ND\n");
+    }
+    _cairo_scaled_font_thaw_cache (font->type1_scaled_font);
+
+fail:
+    _cairo_array_fini (&data);
+    return status;
+}
+
+static void
+cairo_type1_font_write_header (cairo_type1_font_t *font,
+                               const char         *name)
+{
+    unsigned int i;
+    const char spaces[50] = "                                                  ";
+
+    _cairo_output_stream_printf (font->output,
+                                 "%%!FontType1-1.1 %s 1.0\n"
+                                 "11 dict begin\n"
+                                 "/FontName /%s def\n"
+                                 "/PaintType 0 def\n"
+                                 "/FontType 1 def\n"
+                                  "/FontMatrix [0.001 0 0 0.001 0 0] readonly def\n",
+                                 name,
+                                 name);
+
+    /* We don't know the bbox values until after the charstrings have
+     * been generated.  Reserve some space and fill in the bbox
+     * later. */
+
+    /* Worst case for four signed ints with spaces between each number */
+    font->bbox_max_chars = 50;
+
+    _cairo_output_stream_printf (font->output, "/FontBBox {");
+    font->bbox_position = _cairo_output_stream_get_position (font->output);
+    _cairo_output_stream_write (font->output, spaces, font->bbox_max_chars);
+
+    _cairo_output_stream_printf (font->output,
+                                 "} readonly def\n"
+                                 "/Encoding 256 array\n"
+                                "0 1 255 {1 index exch /.notdef put} for\n");
+    if (font->scaled_font_subset->is_latin) {
+       for (i = 1; i < 256; i++) {
+           int subset_glyph = font->scaled_font_subset->latin_to_subset_glyph_index[i];
+
+           if (subset_glyph > 0) {
+               if (font->scaled_font_subset->glyph_names != NULL) {
+                   _cairo_output_stream_printf (font->output, "dup %d /%s put\n",
+                                                i, font->scaled_font_subset->glyph_names[subset_glyph]);
+               } else {
+                   _cairo_output_stream_printf (font->output, "dup %d /g%d put\n", i, subset_glyph);
+               }
+           }
+       }
+    } else {
+       for (i = 1; i < font->scaled_font_subset->num_glyphs; i++) {
+           if (font->scaled_font_subset->glyph_names != NULL) {
+               _cairo_output_stream_printf (font->output, "dup %d /%s put\n",
+                                            i, font->scaled_font_subset->glyph_names[i]);
+           } else {
+               _cairo_output_stream_printf (font->output, "dup %d /g%d put\n", i, i);
+           }
+       }
+    }
+    _cairo_output_stream_printf (font->output,
+                                 "readonly def\n"
+                                 "currentdict end\n"
+                                 "currentfile eexec\n");
+}
+
+static cairo_status_t
+cairo_type1_write_stream_encrypted (void                *closure,
+                                    const unsigned char *data,
+                                    unsigned int         length)
+{
+    const unsigned char *in, *end;
+    uint16_t c, p;
+    static const char hex_digits[16] = "0123456789abcdef";
+    char digits[3];
+    cairo_type1_font_t *font = closure;
+
+    in = (const unsigned char *) data;
+    end = (const unsigned char *) data + length;
+    while (in < end) {
+       p = *in++;
+       c = p ^ (font->eexec_key >> 8);
+       font->eexec_key = (c + font->eexec_key) * CAIRO_TYPE1_ENCRYPT_C1 + CAIRO_TYPE1_ENCRYPT_C2;
+
+       if (font->hex_encode) {
+           digits[0] = hex_digits[c >> 4];
+           digits[1] = hex_digits[c & 0x0f];
+           digits[2] = '\n';
+           font->hex_column += 2;
+
+           if (font->hex_column == 78) {
+               _cairo_output_stream_write (font->output, digits, 3);
+               font->hex_column = 0;
+           } else {
+               _cairo_output_stream_write (font->output, digits, 2);
+           }
+       } else {
+           digits[0] = c;
+           _cairo_output_stream_write (font->output, digits, 1);
+       }
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+cairo_type1_font_write_private_dict (cairo_type1_font_t *font,
+                                     const char         *name)
+{
+    cairo_int_status_t status;
+    cairo_status_t status2;
+    cairo_output_stream_t *encrypted_output;
+
+    font->eexec_key = CAIRO_TYPE1_PRIVATE_DICT_KEY;
+    font->hex_column = 0;
+    encrypted_output = _cairo_output_stream_create (
+        cairo_type1_write_stream_encrypted,
+        NULL,
+        font);
+    if (_cairo_output_stream_get_status (encrypted_output))
+       return  _cairo_output_stream_destroy (encrypted_output);
+
+    /* Note: the first four spaces at the start of this private dict
+     * are the four "random" bytes of plaintext required by the
+     * encryption algorithm */
+    _cairo_output_stream_printf (encrypted_output,
+                                 "    dup /Private 9 dict dup begin\n"
+                                 "/RD {string currentfile exch readstring pop}"
+                                 " bind executeonly def\n"
+                                 "/ND {noaccess def} executeonly def\n"
+                                 "/NP {noaccess put} executeonly def\n"
+                                 "/BlueValues [] def\n"
+                                 "/MinFeature {16 16} def\n"
+                                 "/lenIV 4 def\n"
+                                 "/password 5839 def\n");
+
+    status = cairo_type1_font_write_charstrings (font, encrypted_output);
+    if (unlikely (status))
+       goto fail;
+
+    _cairo_output_stream_printf (encrypted_output,
+                                 "end\n"
+                                 "end\n"
+                                 "readonly put\n"
+                                 "noaccess put\n"
+                                 "dup /FontName get exch definefont pop\n"
+                                 "mark currentfile closefile\n");
+
+  fail:
+    status2 = _cairo_output_stream_destroy (encrypted_output);
+    if (status == CAIRO_INT_STATUS_SUCCESS)
+       status = status2;
+
+    return status;
+}
+
+static void
+cairo_type1_font_write_trailer(cairo_type1_font_t *font)
+{
+    int i;
+    static const char zeros[65] =
+       "0000000000000000000000000000000000000000000000000000000000000000\n";
+
+    for (i = 0; i < 8; i++)
+       _cairo_output_stream_write (font->output, zeros, sizeof zeros);
+
+    _cairo_output_stream_printf (font->output, "cleartomark\n");
+}
+
+static cairo_status_t
+cairo_type1_write_stream (void *closure,
+                         const unsigned char *data,
+                         unsigned int length)
+{
+    cairo_type1_font_t *font = closure;
+
+    return _cairo_array_append_multiple (&font->contents, data, length);
+}
+
+static cairo_int_status_t
+cairo_type1_font_write (cairo_type1_font_t *font,
+                        const char *name)
+{
+    cairo_int_status_t status;
+
+    cairo_type1_font_write_header (font, name);
+    font->header_size = _cairo_output_stream_get_position (font->output);
+
+    status = cairo_type1_font_write_private_dict (font, name);
+    if (unlikely (status))
+       return status;
+
+    font->data_size = _cairo_output_stream_get_position (font->output) -
+       font->header_size;
+
+    cairo_type1_font_write_trailer (font);
+    font->trailer_size =
+       _cairo_output_stream_get_position (font->output) -
+       font->header_size - font->data_size;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+cairo_type1_font_generate (cairo_type1_font_t *font, const char *name)
+{
+    cairo_int_status_t status;
+
+    status = _cairo_array_grow_by (&font->contents, 4096);
+    if (unlikely (status))
+       return status;
+
+    font->output = _cairo_output_stream_create (cairo_type1_write_stream, NULL, font);
+    if (_cairo_output_stream_get_status (font->output))
+       return _cairo_output_stream_destroy (font->output);
+
+    status = cairo_type1_font_write (font, name);
+    if (unlikely (status))
+       return status;
+
+    font->data = _cairo_array_index (&font->contents, 0);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+cairo_type1_font_destroy (cairo_type1_font_t *font)
+{
+    cairo_status_t status = CAIRO_STATUS_SUCCESS;
+
+    free (font->widths);
+    cairo_scaled_font_destroy (font->type1_scaled_font);
+    _cairo_array_fini (&font->contents);
+    if (font->output)
+       status = _cairo_output_stream_destroy (font->output);
+    free (font);
+
+    return status;
+}
+
+static cairo_status_t
+_cairo_type1_fallback_init_internal (cairo_type1_subset_t      *type1_subset,
+                                     const char                        *name,
+                                     cairo_scaled_font_subset_t        *scaled_font_subset,
+                                     cairo_bool_t                hex_encode)
+{
+    cairo_type1_font_t *font;
+    cairo_status_t status;
+    unsigned long length;
+    unsigned int i, len;
+
+    status = cairo_type1_font_create (scaled_font_subset, &font, hex_encode);
+    if (unlikely (status))
+       return status;
+
+    status = cairo_type1_font_generate (font, name);
+    if (unlikely (status))
+       goto fail1;
+
+    type1_subset->base_font = strdup (name);
+    if (unlikely (type1_subset->base_font == NULL)) {
+        status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+        goto fail1;
+    }
+
+    type1_subset->widths = calloc (sizeof (double), font->scaled_font_subset->num_glyphs);
+    if (unlikely (type1_subset->widths == NULL)) {
+        status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+        goto fail2;
+    }
+    for (i = 0; i < font->scaled_font_subset->num_glyphs; i++)
+       type1_subset->widths[i] = (double)font->widths[i]/1000;
+
+    type1_subset->x_min   = (double)font->x_min/1000;
+    type1_subset->y_min   = (double)font->y_min/1000;
+    type1_subset->x_max   = (double)font->x_max/1000;
+    type1_subset->y_max   = (double)font->y_max/1000;
+    type1_subset->ascent  = (double)font->y_max/1000;
+    type1_subset->descent = (double)font->y_min/1000;
+
+    length = font->header_size + font->data_size +
+       font->trailer_size;
+    type1_subset->data = malloc (length);
+    if (unlikely (type1_subset->data == NULL)) {
+        status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+       goto fail3;
+    }
+    memcpy (type1_subset->data,
+           _cairo_array_index (&font->contents, 0), length);
+
+    len = snprintf(type1_subset->data + font->bbox_position,
+                   font->bbox_max_chars,
+                   "%d %d %d %d",
+                   (int)font->x_min,
+                   (int)font->y_min,
+                   (int)font->x_max,
+                   (int)font->y_max);
+    type1_subset->data[font->bbox_position + len] = ' ';
+
+    type1_subset->header_length = font->header_size;
+    type1_subset->data_length = font->data_size;
+    type1_subset->trailer_length = font->trailer_size;
+
+    return cairo_type1_font_destroy (font);
+
+ fail3:
+    free (type1_subset->widths);
+ fail2:
+    free (type1_subset->base_font);
+ fail1:
+    /* status is already set, ignore further errors */
+    cairo_type1_font_destroy (font);
+
+    return status;
+}
+
+cairo_status_t
+_cairo_type1_fallback_init_binary (cairo_type1_subset_t              *type1_subset,
+                                   const char                *name,
+                                   cairo_scaled_font_subset_t *scaled_font_subset)
+{
+    return _cairo_type1_fallback_init_internal (type1_subset,
+                                                name,
+                                                scaled_font_subset, FALSE);
+}
+
+cairo_status_t
+_cairo_type1_fallback_init_hex (cairo_type1_subset_t      *type1_subset,
+                                const char                *name,
+                                cairo_scaled_font_subset_t *scaled_font_subset)
+{
+    return _cairo_type1_fallback_init_internal (type1_subset,
+                                                name,
+                                                scaled_font_subset, TRUE);
+}
+
+void
+_cairo_type1_fallback_fini (cairo_type1_subset_t *subset)
+{
+    free (subset->base_font);
+    free (subset->widths);
+    free (subset->data);
+}
+
+cairo_status_t
+_cairo_type2_charstrings_init (cairo_type2_charstrings_t *type2_subset,
+                               cairo_scaled_font_subset_t *scaled_font_subset)
+{
+    cairo_type1_font_t *font;
+    cairo_status_t status;
+    unsigned int i;
+    cairo_array_t charstring;
+
+    status = cairo_type1_font_create (scaled_font_subset, &font, FALSE);
+    if (unlikely (status))
+       return status;
+
+    _cairo_array_init (&type2_subset->charstrings, sizeof (cairo_array_t));
+
+    type2_subset->widths = calloc (sizeof (int), font->scaled_font_subset->num_glyphs);
+    if (unlikely (type2_subset->widths == NULL)) {
+        status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+        goto fail1;
+    }
+
+    _cairo_scaled_font_freeze_cache (font->type1_scaled_font);
+    for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) {
+        _cairo_array_init (&charstring, sizeof (unsigned char));
+        status = _cairo_array_grow_by (&charstring, 32);
+        if (unlikely (status))
+            goto fail2;
+
+       status = cairo_type1_font_create_charstring (font, i,
+                                                    font->scaled_font_subset->glyphs[i],
+                                                    CAIRO_CHARSTRING_TYPE2,
+                                                    &charstring);
+        if (unlikely (status))
+            goto fail2;
+
+        status = _cairo_array_append (&type2_subset->charstrings, &charstring);
+        if (unlikely (status))
+            goto fail2;
+    }
+    _cairo_scaled_font_thaw_cache (font->type1_scaled_font);
+
+    for (i = 0; i < font->scaled_font_subset->num_glyphs; i++)
+       type2_subset->widths[i] = font->widths[i];
+
+    type2_subset->x_min   = (int) font->x_min;
+    type2_subset->y_min   = (int) font->y_min;
+    type2_subset->x_max   = (int) font->x_max;
+    type2_subset->y_max   = (int) font->y_max;
+    type2_subset->ascent  = (int) font->y_max;
+    type2_subset->descent = (int) font->y_min;
+
+    return cairo_type1_font_destroy (font);
+
+fail2:
+    _cairo_scaled_font_thaw_cache (font->type1_scaled_font);
+    _cairo_array_fini (&charstring);
+    _cairo_type2_charstrings_fini (type2_subset);
+fail1:
+    cairo_type1_font_destroy (font);
+    return status;
+}
+
+void
+_cairo_type2_charstrings_fini (cairo_type2_charstrings_t *type2_subset)
+{
+    unsigned int i, num_charstrings;
+    cairo_array_t *charstring;
+
+    num_charstrings = _cairo_array_num_elements (&type2_subset->charstrings);
+    for (i = 0; i < num_charstrings; i++) {
+        charstring = _cairo_array_index (&type2_subset->charstrings, i);
+        _cairo_array_fini (charstring);
+    }
+    _cairo_array_fini (&type2_subset->charstrings);
+
+    free (type2_subset->widths);
+}
+
+#endif /* CAIRO_HAS_FONT_SUBSET */
diff --git a/src/cairo-type1-glyph-names.c b/src/cairo-type1-glyph-names.c
new file mode 100644 (file)
index 0000000..80ccb96
--- /dev/null
@@ -0,0 +1,410 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2006 Red Hat, Inc
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ *     Kristian Høgsberg <krh@redhat.com>
+ */
+
+#include "cairoint.h"
+
+#if CAIRO_HAS_FONT_SUBSET
+
+#include "cairo-type1-private.h"
+#include "cairo-scaled-font-subsets-private.h"
+
+#if 0
+/*
+ * The three tables that follow are generated using this perl code:
+ */
+
+@ps_standard_encoding = (
+       #   0
+       NULL,           NULL,           NULL,           NULL,
+       NULL,           NULL,           NULL,           NULL,
+       NULL,           NULL,           NULL,           NULL,
+       NULL,           NULL,           NULL,           NULL,
+       #  16
+       NULL,           NULL,           NULL,           NULL,
+       NULL,           NULL,           NULL,           NULL,
+       NULL,           NULL,           NULL,           NULL,
+       NULL,           NULL,           NULL,           NULL,
+       #  32
+       "space",        "exclam",       "quotedbl",     "numbersign",
+       "dollar",       "percent",      "ampersand",    "quoteright",
+       "parenleft",    "parenright",   "asterisk",     "plus",
+       "comma",        "hyphen",       "period",       "slash",
+       #  48
+       "zero",         "one",          "two",          "three",
+       "four",         "five",         "six",          "seven",
+       "eight",        "nine",         "colon",        "semicolon",
+       "less",         "equal",        "greater",      "question",
+       #  64
+       "at",           "A",            "B",            "C",
+       "D",            "E",            "F",            "G",
+       "H",            "I",            "J",            "K",
+       "L",            "M",            "N",            "O",
+       #  80
+       "P",            "Q",            "R",            "S",
+       "T",            "U",            "V",            "W",
+       "X",            "Y",            "Z",            "bracketleft",
+       "backslash",    "bracketright", "asciicircum",  "underscore",
+       #  96
+       "quoteleft",    "a",            "b",            "c",
+       "d",            "e",            "f",            "g",
+       "h",            "i",            "j",            "k",
+       "l",            "m",            "n",            "o",
+       # 112
+       "p",            "q",            "r",            "s",
+       "t",            "u",            "v",            "w",
+       "x",            "y",            "z",            "braceleft",
+       "bar",          "braceright",   "asciitilde",   NULL,
+       # 128
+       NULL,           NULL,           NULL,           NULL,
+       NULL,           NULL,           NULL,           NULL,
+       NULL,           NULL,           NULL,           NULL,
+       NULL,           NULL,           NULL,           NULL,
+       # 144
+       NULL,           NULL,           NULL,           NULL,
+       NULL,           NULL,           NULL,           NULL,
+       NULL,           NULL,           NULL,           NULL,
+       NULL,           NULL,           NULL,           NULL,
+       # 160
+       NULL,           "exclamdown",   "cent",         "sterling",
+       "fraction",     "yen",          "florin",       "section",
+       "currency",     "quotesingle",  "quotedblleft", "guillemotleft",
+       "guilsinglleft","guilsinglright","fi",          "fl",
+       # 176
+       NULL,           "endash",       "dagger",       "daggerdbl",
+       "periodcentered",NULL,          "paragraph",    "bullet",
+       "quotesinglbase","quotedblbase","quotedblright","guillemotright",
+       "ellipsis",     "perthousand",  NULL,           "questiondown",
+       # 192
+       NULL,           "grave",        "acute",        "circumflex",
+       "tilde",        "macron",       "breve",        "dotaccent",
+       "dieresis",     NULL,           "ring",         "cedilla",
+       NULL,           "hungarumlaut", "ogonek",       "caron",
+       # 208
+       "emdash",       NULL,           NULL,           NULL,
+       NULL,           NULL,           NULL,           NULL,
+       NULL,           NULL,           NULL,           NULL,
+       NULL,           NULL,           NULL,           NULL,
+       # 224
+       NULL,           "AE",           NULL,           "ordfeminine",
+       NULL,           NULL,           NULL,           NULL,
+       "Lslash",       "Oslash",       "OE",           "ordmasculine",
+       NULL,           NULL,           NULL,           NULL,
+       # 240
+       NULL,           "ae",           NULL,           NULL,
+       NULL,           "dotlessi",     NULL,           NULL,
+       "lslash",       "oslash",       "oe",           "germandbls",
+       NULL,           NULL,           NULL,           NULL
+       );
+
+@winansi_encoding = (
+       #   0
+       NULL,           NULL,           NULL,           NULL,
+       NULL,           NULL,           NULL,           NULL,
+       NULL,           NULL,           NULL,           NULL,
+       NULL,           NULL,           NULL,           NULL,
+       #  16
+       NULL,           NULL,           NULL,           NULL,
+       NULL,           NULL,           NULL,           NULL,
+       NULL,           NULL,           NULL,           NULL,
+       NULL,           NULL,           NULL,           NULL,
+       #  32
+       "space",        "exclam",       "quotedbl",     "numbersign",
+       "dollar",       "percent",      "ampersand",    "quotesingle",
+       "parenleft",    "parenright",   "asterisk",     "plus",
+       "comma",        "hyphen",       "period",       "slash",
+       #  48
+       "zero",         "one",          "two",          "three",
+       "four",         "five",         "six",          "seven",
+       "eight",        "nine",         "colon",        "semicolon",
+       "less",         "equal",        "greater",      "question",
+       #  64
+       "at",           "A",            "B",            "C",
+       "D",            "E",            "F",            "G",
+       "H",            "I",            "J",            "K",
+       "L",            "M",            "N",            "O",
+       #  80
+       "P",            "Q",            "R",            "S",
+       "T",            "U",            "V",            "W",
+       "X",            "Y",            "Z",            "bracketleft",
+       "backslash",    "bracketright", "asciicircum",  "underscore",
+       #  96
+       "grave",        "a",            "b",            "c",
+       "d",            "e",            "f",            "g",
+       "h",            "i",            "j",            "k",
+       "l",            "m",            "n",            "o",
+       # 112
+       "p",            "q",            "r",            "s",
+       "t",            "u",            "v",            "w",
+       "x",            "y",            "z",            "braceleft",
+       "bar",          "braceright",   "asciitilde",   NULL,
+       # 128
+       "Euro",         NULL,           "quotesinglbase","florin",
+       "quotedblbase", "ellipsis",     "dagger",       "daggerdbl",
+       "circumflex",   "perthousand",  "Scaron",       "guilsinglleft",
+       "OE",           NULL,           "Zcaron",       NULL,
+       # 144
+       NULL,           "quoteleft",    "quoteright",   "quotedblleft",
+       "quotedblright","bullet",       "endash",       "emdash",
+       "tilde",        "trademark",    "scaron",       "guilsinglright",
+       "oe",           NULL,           "zcaron",       "Ydieresis",
+       # 160
+       NULL,           "exclamdown",   "cent",         "sterling",
+       "currency",     "yen",          "brokenbar",    "section",
+       "dieresis",     "copyright",    "ordfeminine",  "guillemotleft",
+       # 173 is also "hyphen" but we leave this NULL to avoid duplicate names
+       "logicalnot",   NULL,           "registered",   "macron",
+       # 176
+       "degree",       "plusminus",    "twosuperior",  "threesuperior",
+       "acute",        "mu",           "paragraph",    "periodcentered",
+       "cedilla",      "onesuperior",  "ordmasculine", "guillemotright",
+       "onequarter",   "onehalf",      "threequarters","questiondown",
+       # 192
+       "Agrave",       "Aacute",       "Acircumflex",  "Atilde",
+       "Adieresis",    "Aring",        "AE",           "Ccedilla",
+       "Egrave",       "Eacute",       "Ecircumflex",  "Edieresis",
+       "Igrave",       "Iacute",       "Icircumflex",  "Idieresis",
+       # 208
+       "Eth",          "Ntilde",       "Ograve",       "Oacute",
+       "Ocircumflex",  "Otilde",       "Odieresis",    "multiply",
+       "Oslash",       "Ugrave",       "Uacute",       "Ucircumflex",
+       "Udieresis",    "Yacute",       "Thorn",        "germandbls",
+       # 224
+       "agrave",       "aacute",       "acircumflex",  "atilde",
+       "adieresis",    "aring",        "ae",           "ccedilla",
+       "egrave",       "eacute",       "ecircumflex",  "edieresis",
+       "igrave",       "iacute",       "icircumflex",  "idieresis",
+       # 240
+       "eth",          "ntilde",       "ograve",       "oacute",
+       "ocircumflex",  "otilde",       "odieresis",    "divide",
+       "oslash",       "ugrave",       "uacute",       "ucircumflex",
+       "udieresis",    "yacute",       "thorn",        "ydieresis"
+);
+
+sub print_offsets {
+    $s = qq();
+    for $sym (@_) {
+        if (! ($sym eq NULL)) {
+           $ss = qq( $hash{$sym}/*$sym*/,);
+       } else {
+           $ss = qq( 0,);
+       }
+       if (length($s) + length($ss) > 78) {
+           print qq( $s\n);
+           $s = "";
+       }
+       $s .= $ss;
+    }
+    print qq( $s\n);
+}
+
+@combined = (@ps_standard_encoding, @winansi_encoding);
+print "static const char glyph_name_symbol[] = {\n";
+%hash = ();
+$s = qq( "\\0");
+$offset = 1;
+for $sym (@combined) {
+    if (! ($sym eq NULL)) {
+        if (! exists $hash{$sym}) {
+           $hash{$sym} = $offset;
+           $offset += length($sym) + 1;
+           $ss = qq( "$sym\\0");
+           if (length($s) + length($ss) > 78) {
+               print qq( $s\n);
+               $s = "";
+           }
+           $s .= $ss;
+       }
+    }
+}
+print qq( $s\n);
+print "};\n\n";
+
+print "static const int16_t ps_standard_encoding_offset[256] = {\n";
+print_offsets(@ps_standard_encoding);
+print "};\n";
+
+print "static const int16_t winansi_encoding_offset[256] = {\n";
+print_offsets(@winansi_encoding);
+print "};\n";
+
+exit;
+#endif
+
+static const char glyph_name_symbol[] = {
+  "\0" "space\0" "exclam\0" "quotedbl\0" "numbersign\0" "dollar\0" "percent\0"
+  "ampersand\0" "quoteright\0" "parenleft\0" "parenright\0" "asterisk\0"
+  "plus\0" "comma\0" "hyphen\0" "period\0" "slash\0" "zero\0" "one\0" "two\0"
+  "three\0" "four\0" "five\0" "six\0" "seven\0" "eight\0" "nine\0" "colon\0"
+  "semicolon\0" "less\0" "equal\0" "greater\0" "question\0" "at\0" "A\0" "B\0"
+  "C\0" "D\0" "E\0" "F\0" "G\0" "H\0" "I\0" "J\0" "K\0" "L\0" "M\0" "N\0" "O\0"
+  "P\0" "Q\0" "R\0" "S\0" "T\0" "U\0" "V\0" "W\0" "X\0" "Y\0" "Z\0"
+  "bracketleft\0" "backslash\0" "bracketright\0" "asciicircum\0" "underscore\0"
+  "quoteleft\0" "a\0" "b\0" "c\0" "d\0" "e\0" "f\0" "g\0" "h\0" "i\0" "j\0"
+  "k\0" "l\0" "m\0" "n\0" "o\0" "p\0" "q\0" "r\0" "s\0" "t\0" "u\0" "v\0" "w\0"
+  "x\0" "y\0" "z\0" "braceleft\0" "bar\0" "braceright\0" "asciitilde\0"
+  "exclamdown\0" "cent\0" "sterling\0" "fraction\0" "yen\0" "florin\0"
+  "section\0" "currency\0" "quotesingle\0" "quotedblleft\0" "guillemotleft\0"
+  "guilsinglleft\0" "guilsinglright\0" "fi\0" "fl\0" "endash\0" "dagger\0"
+  "daggerdbl\0" "periodcentered\0" "paragraph\0" "bullet\0" "quotesinglbase\0"
+  "quotedblbase\0" "quotedblright\0" "guillemotright\0" "ellipsis\0"
+  "perthousand\0" "questiondown\0" "grave\0" "acute\0" "circumflex\0" "tilde\0"
+  "macron\0" "breve\0" "dotaccent\0" "dieresis\0" "ring\0" "cedilla\0"
+  "hungarumlaut\0" "ogonek\0" "caron\0" "emdash\0" "AE\0" "ordfeminine\0"
+  "Lslash\0" "Oslash\0" "OE\0" "ordmasculine\0" "ae\0" "dotlessi\0" "lslash\0"
+  "oslash\0" "oe\0" "germandbls\0" "Euro\0" "Scaron\0" "Zcaron\0" "trademark\0"
+  "scaron\0" "zcaron\0" "Ydieresis\0" "brokenbar\0" "copyright\0"
+  "logicalnot\0" "registered\0" "degree\0" "plusminus\0" "twosuperior\0"
+  "threesuperior\0" "mu\0" "onesuperior\0" "onequarter\0" "onehalf\0"
+  "threequarters\0" "Agrave\0" "Aacute\0" "Acircumflex\0" "Atilde\0"
+  "Adieresis\0" "Aring\0" "Ccedilla\0" "Egrave\0" "Eacute\0" "Ecircumflex\0"
+  "Edieresis\0" "Igrave\0" "Iacute\0" "Icircumflex\0" "Idieresis\0" "Eth\0"
+  "Ntilde\0" "Ograve\0" "Oacute\0" "Ocircumflex\0" "Otilde\0" "Odieresis\0"
+  "multiply\0" "Ugrave\0" "Uacute\0" "Ucircumflex\0" "Udieresis\0" "Yacute\0"
+  "Thorn\0" "agrave\0" "aacute\0" "acircumflex\0" "atilde\0" "adieresis\0"
+  "aring\0" "ccedilla\0" "egrave\0" "eacute\0" "ecircumflex\0" "edieresis\0"
+  "igrave\0" "iacute\0" "icircumflex\0" "idieresis\0" "eth\0" "ntilde\0"
+  "ograve\0" "oacute\0" "ocircumflex\0" "otilde\0" "odieresis\0" "divide\0"
+  "ugrave\0" "uacute\0" "ucircumflex\0" "udieresis\0" "yacute\0" "thorn\0"
+  "ydieresis\0"
+};
+
+static const int16_t ps_standard_encoding_offset[256] = {
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+  0, 0, 0, 0, 0, 0, 1/*space*/, 7/*exclam*/, 14/*quotedbl*/, 23/*numbersign*/,
+  34/*dollar*/, 41/*percent*/, 49/*ampersand*/, 59/*quoteright*/,
+  70/*parenleft*/, 80/*parenright*/, 91/*asterisk*/, 100/*plus*/, 105/*comma*/,
+  111/*hyphen*/, 118/*period*/, 125/*slash*/, 131/*zero*/, 136/*one*/,
+  140/*two*/, 144/*three*/, 150/*four*/, 155/*five*/, 160/*six*/, 164/*seven*/,
+  170/*eight*/, 176/*nine*/, 181/*colon*/, 187/*semicolon*/, 197/*less*/,
+  202/*equal*/, 208/*greater*/, 216/*question*/, 225/*at*/, 228/*A*/, 230/*B*/,
+  232/*C*/, 234/*D*/, 236/*E*/, 238/*F*/, 240/*G*/, 242/*H*/, 244/*I*/,
+  246/*J*/, 248/*K*/, 250/*L*/, 252/*M*/, 254/*N*/, 256/*O*/, 258/*P*/,
+  260/*Q*/, 262/*R*/, 264/*S*/, 266/*T*/, 268/*U*/, 270/*V*/, 272/*W*/,
+  274/*X*/, 276/*Y*/, 278/*Z*/, 280/*bracketleft*/, 292/*backslash*/,
+  302/*bracketright*/, 315/*asciicircum*/, 327/*underscore*/, 338/*quoteleft*/,
+  348/*a*/, 350/*b*/, 352/*c*/, 354/*d*/, 356/*e*/, 358/*f*/, 360/*g*/,
+  362/*h*/, 364/*i*/, 366/*j*/, 368/*k*/, 370/*l*/, 372/*m*/, 374/*n*/,
+  376/*o*/, 378/*p*/, 380/*q*/, 382/*r*/, 384/*s*/, 386/*t*/, 388/*u*/,
+  390/*v*/, 392/*w*/, 394/*x*/, 396/*y*/, 398/*z*/, 400/*braceleft*/,
+  410/*bar*/, 414/*braceright*/, 425/*asciitilde*/, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+  436/*exclamdown*/, 447/*cent*/, 452/*sterling*/, 461/*fraction*/, 470/*yen*/,
+  474/*florin*/, 481/*section*/, 489/*currency*/, 498/*quotesingle*/,
+  510/*quotedblleft*/, 523/*guillemotleft*/, 537/*guilsinglleft*/,
+  551/*guilsinglright*/, 566/*fi*/, 569/*fl*/, 0, 572/*endash*/, 579/*dagger*/,
+  586/*daggerdbl*/, 596/*periodcentered*/, 0, 611/*paragraph*/, 621/*bullet*/,
+  628/*quotesinglbase*/, 643/*quotedblbase*/, 656/*quotedblright*/,
+  670/*guillemotright*/, 685/*ellipsis*/, 694/*perthousand*/, 0,
+  706/*questiondown*/, 0, 719/*grave*/, 725/*acute*/, 731/*circumflex*/,
+  742/*tilde*/, 748/*macron*/, 755/*breve*/, 761/*dotaccent*/, 771/*dieresis*/,
+  0, 780/*ring*/, 785/*cedilla*/, 0, 793/*hungarumlaut*/, 806/*ogonek*/,
+  813/*caron*/, 819/*emdash*/, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+  826/*AE*/, 0, 829/*ordfeminine*/, 0, 0, 0, 0, 841/*Lslash*/, 848/*Oslash*/,
+  855/*OE*/, 858/*ordmasculine*/, 0, 0, 0, 0, 0, 871/*ae*/, 0, 0, 0,
+  874/*dotlessi*/, 0, 0, 883/*lslash*/, 890/*oslash*/, 897/*oe*/,
+  900/*germandbls*/, 0, 0, 0, 0,
+};
+
+static const int16_t winansi_encoding_offset[256] = {
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+  0, 0, 0, 0, 0, 0, 1/*space*/, 7/*exclam*/, 14/*quotedbl*/, 23/*numbersign*/,
+  34/*dollar*/, 41/*percent*/, 49/*ampersand*/, 498/*quotesingle*/,
+  70/*parenleft*/, 80/*parenright*/, 91/*asterisk*/, 100/*plus*/, 105/*comma*/,
+  111/*hyphen*/, 118/*period*/, 125/*slash*/, 131/*zero*/, 136/*one*/,
+  140/*two*/, 144/*three*/, 150/*four*/, 155/*five*/, 160/*six*/, 164/*seven*/,
+  170/*eight*/, 176/*nine*/, 181/*colon*/, 187/*semicolon*/, 197/*less*/,
+  202/*equal*/, 208/*greater*/, 216/*question*/, 225/*at*/, 228/*A*/, 230/*B*/,
+  232/*C*/, 234/*D*/, 236/*E*/, 238/*F*/, 240/*G*/, 242/*H*/, 244/*I*/,
+  246/*J*/, 248/*K*/, 250/*L*/, 252/*M*/, 254/*N*/, 256/*O*/, 258/*P*/,
+  260/*Q*/, 262/*R*/, 264/*S*/, 266/*T*/, 268/*U*/, 270/*V*/, 272/*W*/,
+  274/*X*/, 276/*Y*/, 278/*Z*/, 280/*bracketleft*/, 292/*backslash*/,
+  302/*bracketright*/, 315/*asciicircum*/, 327/*underscore*/, 719/*grave*/,
+  348/*a*/, 350/*b*/, 352/*c*/, 354/*d*/, 356/*e*/, 358/*f*/, 360/*g*/,
+  362/*h*/, 364/*i*/, 366/*j*/, 368/*k*/, 370/*l*/, 372/*m*/, 374/*n*/,
+  376/*o*/, 378/*p*/, 380/*q*/, 382/*r*/, 384/*s*/, 386/*t*/, 388/*u*/,
+  390/*v*/, 392/*w*/, 394/*x*/, 396/*y*/, 398/*z*/, 400/*braceleft*/,
+  410/*bar*/, 414/*braceright*/, 425/*asciitilde*/, 0, 911/*Euro*/, 0,
+  628/*quotesinglbase*/, 474/*florin*/, 643/*quotedblbase*/, 685/*ellipsis*/,
+  579/*dagger*/, 586/*daggerdbl*/, 731/*circumflex*/, 694/*perthousand*/,
+  916/*Scaron*/, 537/*guilsinglleft*/, 855/*OE*/, 0, 923/*Zcaron*/, 0, 0,
+  338/*quoteleft*/, 59/*quoteright*/, 510/*quotedblleft*/,
+  656/*quotedblright*/, 621/*bullet*/, 572/*endash*/, 819/*emdash*/,
+  742/*tilde*/, 930/*trademark*/, 940/*scaron*/, 551/*guilsinglright*/,
+  897/*oe*/, 0, 947/*zcaron*/, 954/*Ydieresis*/, 0, 436/*exclamdown*/,
+  447/*cent*/, 452/*sterling*/, 489/*currency*/, 470/*yen*/, 964/*brokenbar*/,
+  481/*section*/, 771/*dieresis*/, 974/*copyright*/, 829/*ordfeminine*/,
+  523/*guillemotleft*/, 984/*logicalnot*/, 0, 995/*registered*/, 748/*macron*/,
+  1006/*degree*/, 1013/*plusminus*/, 1023/*twosuperior*/,
+  1035/*threesuperior*/, 725/*acute*/, 1049/*mu*/, 611/*paragraph*/,
+  596/*periodcentered*/, 785/*cedilla*/, 1052/*onesuperior*/,
+  858/*ordmasculine*/, 670/*guillemotright*/, 1064/*onequarter*/,
+  1075/*onehalf*/, 1083/*threequarters*/, 706/*questiondown*/, 1097/*Agrave*/,
+  1104/*Aacute*/, 1111/*Acircumflex*/, 1123/*Atilde*/, 1130/*Adieresis*/,
+  1140/*Aring*/, 826/*AE*/, 1146/*Ccedilla*/, 1155/*Egrave*/, 1162/*Eacute*/,
+  1169/*Ecircumflex*/, 1181/*Edieresis*/, 1191/*Igrave*/, 1198/*Iacute*/,
+  1205/*Icircumflex*/, 1217/*Idieresis*/, 1227/*Eth*/, 1231/*Ntilde*/,
+  1238/*Ograve*/, 1245/*Oacute*/, 1252/*Ocircumflex*/, 1264/*Otilde*/,
+  1271/*Odieresis*/, 1281/*multiply*/, 848/*Oslash*/, 1290/*Ugrave*/,
+  1297/*Uacute*/, 1304/*Ucircumflex*/, 1316/*Udieresis*/, 1326/*Yacute*/,
+  1333/*Thorn*/, 900/*germandbls*/, 1339/*agrave*/, 1346/*aacute*/,
+  1353/*acircumflex*/, 1365/*atilde*/, 1372/*adieresis*/, 1382/*aring*/,
+  871/*ae*/, 1388/*ccedilla*/, 1397/*egrave*/, 1404/*eacute*/,
+  1411/*ecircumflex*/, 1423/*edieresis*/, 1433/*igrave*/, 1440/*iacute*/,
+  1447/*icircumflex*/, 1459/*idieresis*/, 1469/*eth*/, 1473/*ntilde*/,
+  1480/*ograve*/, 1487/*oacute*/, 1494/*ocircumflex*/, 1506/*otilde*/,
+  1513/*odieresis*/, 1523/*divide*/, 890/*oslash*/, 1530/*ugrave*/,
+  1537/*uacute*/, 1544/*ucircumflex*/, 1556/*udieresis*/, 1566/*yacute*/,
+  1573/*thorn*/, 1579/*ydieresis*/,
+};
+
+const char *
+_cairo_ps_standard_encoding_to_glyphname (int glyph)
+{
+    if (ps_standard_encoding_offset[glyph])
+       return glyph_name_symbol + ps_standard_encoding_offset[glyph];
+    else
+       return NULL;
+}
+
+const char *
+_cairo_winansi_to_glyphname (int glyph)
+{
+    if (winansi_encoding_offset[glyph])
+       return glyph_name_symbol + winansi_encoding_offset[glyph];
+    else
+       return NULL;
+}
+
+#endif /* CAIRO_HAS_FONT_SUBSET */
diff --git a/src/cairo-type1-private.h b/src/cairo-type1-private.h
new file mode 100644 (file)
index 0000000..1630397
--- /dev/null
@@ -0,0 +1,51 @@
+/* Cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ *     Adrian Johnson <ajohnson@redneon.com>
+ */
+
+#ifndef CAIRO_TYPE1_PRIVATE_H
+#define CAIRO_TYPE1_PRIVATE_H
+
+#include "cairoint.h"
+
+#if CAIRO_HAS_FONT_SUBSET
+
+/* Magic constants for the type1 eexec encryption */
+#define CAIRO_TYPE1_ENCRYPT_C1         ((unsigned short) 52845)
+#define CAIRO_TYPE1_ENCRYPT_C2         ((unsigned short) 22719)
+#define CAIRO_TYPE1_PRIVATE_DICT_KEY   ((unsigned short) 55665)
+#define CAIRO_TYPE1_CHARSTRING_KEY     ((unsigned short) 4330)
+
+#endif /* CAIRO_HAS_FONT_SUBSET */
+
+#endif /* CAIRO_TYPE1_PRIVATE_H */
diff --git a/src/cairo-type1-subset.c b/src/cairo-type1-subset.c
new file mode 100644 (file)
index 0000000..efe3028
--- /dev/null
@@ -0,0 +1,1620 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2006 Red Hat, Inc
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ *     Kristian Høgsberg <krh@redhat.com>
+ */
+
+/*
+ * Useful links:
+ * http://partners.adobe.com/public/developer/en/font/T1_SPEC.PDF
+ */
+
+
+#define _BSD_SOURCE /* for snprintf(), strdup() */
+#include "cairoint.h"
+
+#include "cairo-array-private.h"
+#include "cairo-error-private.h"
+
+#if CAIRO_HAS_FONT_SUBSET
+
+#include "cairo-type1-private.h"
+#include "cairo-scaled-font-subsets-private.h"
+#include "cairo-output-stream-private.h"
+
+#include <ctype.h>
+
+#define TYPE1_STACKSIZE 24 /* Defined in Type 1 Font Format */
+
+
+typedef struct {
+    int subset_index;
+    double width;
+    const char *encrypted_charstring;
+    int encrypted_charstring_length;
+} glyph_data_t;
+
+typedef struct _cairo_type1_font_subset {
+    cairo_scaled_font_subset_t *scaled_font_subset;
+
+    struct {
+       unsigned int font_id;
+       char *base_font;
+       unsigned int num_glyphs;
+       double x_min, y_min, x_max, y_max;
+       double ascent, descent;
+       double units_per_em;
+
+       const char    *data;
+       unsigned long  header_size;
+       unsigned long  data_size;
+       unsigned long  trailer_size;
+    } base;
+
+    int num_glyphs;
+
+    /* The glyphs and glyph_names arrays are indexed by the order of
+     * the Charstrings in the font. This is not necessarily the same
+     * order as the glyph index. The index_to_glyph_name() font backend
+     * function is used to map the glyph index to the glyph order in
+     * the Charstrings. */
+
+    glyph_data_t *glyphs;
+    char **glyph_names;
+    cairo_array_t glyphs_array;
+    cairo_array_t glyph_names_array;
+
+    int num_subrs;
+    cairo_bool_t subset_subrs;
+    struct {
+       const char *subr_string;
+       int subr_length;
+       cairo_bool_t used;
+    } *subrs;
+
+    /* Indexed by subset_index this maps to the glyph order in the
+     * glyph_names and glyphs arrays. Has font->num_golyphs
+     * elements. */
+    int *subset_index_to_glyphs;
+
+    cairo_output_stream_t *output;
+    cairo_array_t contents;
+
+    const char *rd, *nd, *np;
+
+    char *type1_data;
+    unsigned int type1_length;
+    char *type1_end;
+
+    char *header_segment;
+    int header_segment_size;
+    char *eexec_segment;
+    int eexec_segment_size;
+    cairo_bool_t eexec_segment_is_ascii;
+
+    char *cleartext;
+    char *cleartext_end;
+
+    int header_size;
+
+    unsigned short eexec_key;
+    cairo_bool_t hex_encode;
+    int hex_column;
+
+    struct {
+       int stack[TYPE1_STACKSIZE], sp, top_value;
+    } build_stack;
+
+    struct {
+       int other_subr_args[TYPE1_STACKSIZE], num_other_subr_args, cur_other_subr_arg;
+    } ps_stack;
+
+
+} cairo_type1_font_subset_t;
+
+
+static cairo_status_t
+_cairo_type1_font_subset_init (cairo_type1_font_subset_t  *font,
+                              cairo_scaled_font_subset_t *scaled_font_subset,
+                              cairo_bool_t                hex_encode)
+{
+    memset (font, 0, sizeof (*font));
+    font->scaled_font_subset = scaled_font_subset;
+
+    _cairo_array_init (&font->glyphs_array, sizeof (glyph_data_t));
+    _cairo_array_init (&font->glyph_names_array, sizeof (char *));
+    font->subset_index_to_glyphs = NULL;
+    font->base.num_glyphs = 0;
+    font->num_subrs = 0;
+    font->subset_subrs = TRUE;
+    font->subrs = NULL;
+
+    font->hex_encode = hex_encode;
+    font->num_glyphs = 0;
+
+    _cairo_array_init (&font->contents, sizeof (char));
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+cairo_type1_font_subset_use_glyph (cairo_type1_font_subset_t *font, int glyph)
+{
+    if (font->glyphs[glyph].subset_index >= 0)
+       return;
+
+    font->glyphs[glyph].subset_index = font->num_glyphs;
+    font->subset_index_to_glyphs[font->num_glyphs] = glyph;
+    font->num_glyphs++;
+}
+
+static cairo_bool_t
+is_ps_delimiter(int c)
+{
+    static const char delimiters[] = "()[]{}<>/% \t\r\n";
+
+    return strchr (delimiters, c) != NULL;
+}
+
+static const char *
+find_token (const char *buffer, const char *end, const char *token)
+{
+    int i, length;
+    /* FIXME: find substring really must be find_token */
+
+    if (buffer == NULL)
+       return NULL;
+
+    length = strlen (token);
+    for (i = 0; buffer + i < end - length + 1; i++)
+       if (memcmp (buffer + i, token, length) == 0)
+           if ((i == 0 || token[0] == '/' || is_ps_delimiter(buffer[i - 1])) &&
+               (buffer + i == end - length || is_ps_delimiter(buffer[i + length])))
+               return buffer + i;
+
+    return NULL;
+}
+
+static cairo_status_t
+cairo_type1_font_subset_find_segments (cairo_type1_font_subset_t *font)
+{
+    unsigned char *p;
+    const char *eexec_token;
+    int size, i;
+
+    p = (unsigned char *) font->type1_data;
+    font->type1_end = font->type1_data + font->type1_length;
+    if (p[0] == 0x80 && p[1] == 0x01) {
+       font->header_segment_size =
+           p[2] | (p[3] << 8) | (p[4] << 16) | (p[5] << 24);
+       font->header_segment = (char *) p + 6;
+
+       p += 6 + font->header_segment_size;
+       font->eexec_segment_size =
+           p[2] | (p[3] << 8) | (p[4] << 16) | (p[5] << 24);
+       font->eexec_segment = (char *) p + 6;
+       font->eexec_segment_is_ascii = (p[1] == 1);
+
+        p += 6 + font->eexec_segment_size;
+        while (p < (unsigned char *) (font->type1_end) && p[1] != 0x03) {
+            size = p[2] | (p[3] << 8) | (p[4] << 16) | (p[5] << 24);
+            p += 6 + size;
+        }
+        font->type1_end = (char *) p;
+    } else {
+       eexec_token = find_token ((char *) p, font->type1_end, "eexec");
+       if (eexec_token == NULL)
+           return CAIRO_INT_STATUS_UNSUPPORTED;
+
+       font->header_segment_size = eexec_token - (char *) p + strlen ("eexec\n");
+       font->header_segment = (char *) p;
+       font->eexec_segment_size = font->type1_length - font->header_segment_size;
+       font->eexec_segment = (char *) p + font->header_segment_size;
+       font->eexec_segment_is_ascii = TRUE;
+       for (i = 0; i < 4; i++) {
+           if (!isxdigit(font->eexec_segment[i]))
+               font->eexec_segment_is_ascii = FALSE;
+       }
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+/* Search for the definition of key and erase it by overwriting with spaces.
+ * This function is looks for definitions of the form:
+ *
+ * /key1 1234 def
+ * /key2 [12 34 56] def
+ *
+ * ie a key defined as an integer or array of integers.
+ *
+ */
+static void
+cairo_type1_font_erase_dict_key (cairo_type1_font_subset_t *font,
+                                const char *key)
+{
+    const char *start, *p, *segment_end;
+
+    segment_end = font->header_segment + font->header_segment_size;
+
+    start = font->header_segment;
+    do {
+       start = find_token (start, segment_end, key);
+       if (start) {
+           p = start + strlen(key);
+           /* skip integers or array of integers */
+           while (p < segment_end &&
+                  (_cairo_isspace(*p) ||
+                   _cairo_isdigit(*p) ||
+                   *p == '[' ||
+                   *p == ']'))
+           {
+               p++;
+           }
+
+           if (p + 3 < segment_end && memcmp(p, "def", 3) == 0) {
+               /* erase definition of the key */
+               memset((char *) start, ' ', p + 3 - start);
+           }
+           start += strlen(key);
+       }
+    } while (start);
+}
+
+static cairo_status_t
+cairo_type1_font_subset_get_matrix (cairo_type1_font_subset_t *font,
+                                   const char                *name,
+                                   double                    *a,
+                                   double                    *b,
+                                   double                    *c,
+                                   double                    *d)
+{
+    const char *start, *end, *segment_end;
+    int ret;
+    char *s;
+
+    segment_end = font->header_segment + font->header_segment_size;
+    start = find_token (font->header_segment, segment_end, name);
+    if (start == NULL)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    end = find_token (start, segment_end, "def");
+    if (end == NULL)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    s = malloc (end - start + 1);
+    if (unlikely (s == NULL))
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    strncpy (s, start, end - start);
+    s[end - start] = 0;
+
+    start = strpbrk (s, "{[");
+    if (!start) {
+       free (s);
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+    }
+
+    start++;
+    ret = 0;
+    if (*start)
+       ret = sscanf(start, "%lf %lf %lf %lf", a, b, c, d);
+
+    free (s);
+
+    if (ret != 4)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+cairo_type1_font_subset_get_bbox (cairo_type1_font_subset_t *font)
+{
+    cairo_status_t status;
+    double x_min, y_min, x_max, y_max;
+    double xx, yx, xy, yy;
+
+    status = cairo_type1_font_subset_get_matrix (font, "/FontBBox",
+                                                &x_min,
+                                                &y_min,
+                                                &x_max,
+                                                &y_max);
+    if (unlikely (status))
+       return status;
+
+    status = cairo_type1_font_subset_get_matrix (font, "/FontMatrix",
+                                                &xx, &yx, &xy, &yy);
+    if (unlikely (status))
+       return status;
+
+    if (yy == 0.0)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    /* Freetype uses 1/yy to get units per EM */
+    font->base.units_per_em = 1.0/yy;
+
+    font->base.x_min = x_min / font->base.units_per_em;
+    font->base.y_min = y_min / font->base.units_per_em;
+    font->base.x_max = x_max / font->base.units_per_em;
+    font->base.y_max = y_max / font->base.units_per_em;
+    font->base.ascent = font->base.y_max;
+    font->base.descent = font->base.y_min;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+cairo_type1_font_subset_get_fontname (cairo_type1_font_subset_t *font)
+{
+    const char *start, *end, *segment_end;
+    char *s;
+
+    segment_end = font->header_segment + font->header_segment_size;
+    start = find_token (font->header_segment, segment_end, "/FontName");
+    if (start == NULL)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    start += strlen ("/FontName");
+
+    end = find_token (start, segment_end, "def");
+    if (end == NULL)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    s = malloc (end - start + 1);
+    if (unlikely (s == NULL))
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    strncpy (s, start, end - start);
+    s[end - start] = 0;
+
+    start = strchr (s, '/');
+    if (!start++ || !start) {
+       free (s);
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+    }
+
+    font->base.base_font = strdup (start);
+    free (s);
+    if (unlikely (font->base.base_font == NULL))
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    s = font->base.base_font;
+    while (*s && !is_ps_delimiter(*s))
+       s++;
+
+    *s = 0;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+cairo_type1_font_subset_write_header (cairo_type1_font_subset_t *font,
+                                        const char *name)
+{
+    const char *start, *end, *segment_end;
+    unsigned int i;
+
+    /* FIXME:
+     * This function assumes that /FontName always appears
+     * before /Encoding. This appears to always be the case with Type1
+     * fonts.
+     *
+     * The more recently added code for removing the UniqueID and XUID
+     * keys can not make any assumptions about the position of the
+     * keys in the dictionary so it is implemented by overwriting the
+     * key definition with spaces before we start copying the font to
+     * the output.
+     *
+     * This code should be rewritten to not make any assumptions about
+     * the order of dictionary keys. This will allow UniqueID to be
+     * stripped out instead of leaving a bunch of spaces in the
+     * output.
+     */
+    cairo_type1_font_erase_dict_key (font, "/UniqueID");
+    cairo_type1_font_erase_dict_key (font, "/XUID");
+
+    segment_end = font->header_segment + font->header_segment_size;
+
+    /* Type 1 fonts created by Fontforge have some PostScript code at
+     * the start of the font that skips the font if the printer has a
+     * cached copy of the font with the same unique id. This breaks
+     * our subsetted font so we disable it by searching for the
+     * PostScript operator "known" when used to check for the
+     * "/UniqueID" dictionary key. We append " pop false " after it to
+     * pop the result of this check off the stack and replace it with
+     * "false" to make the PostScript code think "/UniqueID" does not
+     * exist.
+     */
+    end = font->header_segment;
+    start = find_token (font->header_segment, segment_end, "/UniqueID");
+    if (start) {
+       start += 9;
+       while (start < segment_end && _cairo_isspace (*start))
+           start++;
+       if (start + 5 < segment_end && memcmp(start, "known", 5) == 0) {
+           _cairo_output_stream_write (font->output, font->header_segment,
+                                       start + 5 - font->header_segment);
+           _cairo_output_stream_printf (font->output, " pop false ");
+           end = start + 5;
+       }
+    }
+
+    start = find_token (end, segment_end, "/FontName");
+    if (start == NULL)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    _cairo_output_stream_write (font->output, end,
+                               start - end);
+
+    _cairo_output_stream_printf (font->output, "/FontName /%s def", name);
+
+    end = find_token (start, segment_end, "def");
+    if (end == NULL)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+    end += 3;
+
+    start = find_token (end, segment_end, "/Encoding");
+    if (start == NULL)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+    _cairo_output_stream_write (font->output, end, start - end);
+
+    _cairo_output_stream_printf (font->output,
+                                "/Encoding 256 array\n"
+                                "0 1 255 {1 index exch /.notdef put} for\n");
+    if (font->scaled_font_subset->is_latin) {
+       for (i = 1; i < 256; i++) {
+           int subset_glyph = font->scaled_font_subset->latin_to_subset_glyph_index[i];
+           int glyph_num = font->subset_index_to_glyphs[subset_glyph];
+
+           if (subset_glyph > 0) {
+               _cairo_output_stream_printf (font->output,
+                                            "dup %d /%s put\n",
+                                            i,
+                                            font->glyph_names[glyph_num]);
+           }
+       }
+    } else {
+       for (i = 1; i < font->base.num_glyphs; i++) {
+           if (font->glyphs[i].subset_index < 0)
+               continue;
+           _cairo_output_stream_printf (font->output,
+                                        "dup %d /%s put\n",
+                                        font->glyphs[i].subset_index,
+                                        font->glyph_names[i]);
+       }
+    }
+    _cairo_output_stream_printf (font->output, "readonly def");
+
+    end = find_token (start, segment_end, "def");
+    if (end == NULL)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+    end += 3;
+
+    /* There are some buggy fonts that contain more than one /Encoding */
+    if (find_token (end, segment_end, "/Encoding"))
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    _cairo_output_stream_write (font->output, end, segment_end - end);
+
+    return font->output->status;
+}
+
+static int
+hex_to_int (int ch)
+{
+    if (ch <= '9')
+       return ch - '0';
+    else if (ch <= 'F')
+       return ch - 'A' + 10;
+    else
+       return ch - 'a' + 10;
+}
+
+static cairo_status_t
+cairo_type1_font_subset_write_encrypted (cairo_type1_font_subset_t *font,
+                                        const char *data, unsigned int length)
+{
+    const unsigned char *in, *end;
+    int c, p;
+    static const char hex_digits[16] = "0123456789abcdef";
+    char digits[3];
+
+    in = (const unsigned char *) data;
+    end = (const unsigned char *) data + length;
+    while (in < end) {
+       p = *in++;
+       c = p ^ (font->eexec_key >> 8);
+       font->eexec_key = (c + font->eexec_key) * CAIRO_TYPE1_ENCRYPT_C1 + CAIRO_TYPE1_ENCRYPT_C2;
+
+       if (font->hex_encode) {
+           digits[0] = hex_digits[c >> 4];
+           digits[1] = hex_digits[c & 0x0f];
+           digits[2] = '\n';
+           font->hex_column += 2;
+
+           if (font->hex_column == 78) {
+               _cairo_output_stream_write (font->output, digits, 3);
+               font->hex_column = 0;
+           } else {
+               _cairo_output_stream_write (font->output, digits, 2);
+           }
+       } else {
+           digits[0] = c;
+           _cairo_output_stream_write (font->output, digits, 1);
+       }
+    }
+
+    return font->output->status;
+}
+
+static cairo_status_t
+cairo_type1_font_subset_decrypt_eexec_segment (cairo_type1_font_subset_t *font)
+{
+    unsigned short r = CAIRO_TYPE1_PRIVATE_DICT_KEY;
+    unsigned char *in, *end;
+    char *out;
+    int c, p;
+    int i;
+
+    in = (unsigned char *) font->eexec_segment;
+    end = (unsigned char *) in + font->eexec_segment_size;
+
+    font->cleartext = malloc (font->eexec_segment_size + 1);
+    if (unlikely (font->cleartext == NULL))
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    out = font->cleartext;
+    while (in < end) {
+       if (font->eexec_segment_is_ascii) {
+           c = *in++;
+           if (_cairo_isspace (c))
+               continue;
+           c = (hex_to_int (c) << 4) | hex_to_int (*in++);
+       } else {
+           c = *in++;
+       }
+       p = c ^ (r >> 8);
+       r = (c + r) * CAIRO_TYPE1_ENCRYPT_C1 + CAIRO_TYPE1_ENCRYPT_C2;
+
+       *out++ = p;
+    }
+    font->cleartext_end = out;
+
+    /* Overwrite random bytes with spaces.
+     *
+     * The first 4 bytes of the cleartext are the random bytes
+     * required by the encryption algorithm. When encrypting the
+     * cleartext, the first ciphertext byte must not be a white space
+     * character and the first 4 bytes must not be an ASCII Hex
+     * character. Some fonts do not check that their randomly chosen
+     * bytes results in ciphertext that complies with this
+     * restriction. This may cause problems for some PDF consumers. By
+     * replacing the random bytes with spaces, the first four bytes of
+     * ciphertext will always be 0xf9, 0x83, 0xef, 0x00 which complies
+     * with this restriction. Using spaces also means we don't have to
+     * skip over the random bytes when parsing the cleartext.
+     */
+    for (i = 0; i < 4 && i < font->eexec_segment_size; i++)
+       font->cleartext[i] = ' ';
+
+    /* Ensure strtol() can not scan past the end of the cleartext */
+    font->cleartext[font->eexec_segment_size] = 0;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static const char *
+skip_token (const char *p, const char *end)
+{
+    while (p < end && _cairo_isspace(*p))
+       p++;
+
+    while (p < end && !_cairo_isspace(*p))
+       p++;
+
+    if (p == end)
+       return NULL;
+
+    return p;
+}
+
+static void
+cairo_type1_font_subset_decrypt_charstring (const unsigned char *in, int size, unsigned char *out)
+{
+    unsigned short r = CAIRO_TYPE1_CHARSTRING_KEY;
+    int c, p, i;
+
+    for (i = 0; i < size; i++) {
+        c = *in++;
+       p = c ^ (r >> 8);
+       r = (c + r) * CAIRO_TYPE1_ENCRYPT_C1 + CAIRO_TYPE1_ENCRYPT_C2;
+       *out++ = p;
+    }
+}
+
+static const unsigned char *
+cairo_type1_font_subset_decode_integer (const unsigned char *p, int *integer)
+{
+    if (*p <= 246) {
+        *integer = *p++ - 139;
+    } else if (*p <= 250) {
+        *integer = (p[0] - 247) * 256 + p[1] + 108;
+        p += 2;
+    } else if (*p <= 254) {
+        *integer = -(p[0] - 251) * 256 - p[1] - 108;
+        p += 2;
+    } else {
+        *integer = (p[1] << 24) | (p[2] << 16) | (p[3] << 8) | p[4];
+        p += 5;
+    }
+
+    return p;
+}
+
+static cairo_status_t
+use_standard_encoding_glyph (cairo_type1_font_subset_t *font, int index)
+{
+    const char *glyph_name;
+    unsigned int i;
+
+    if (index < 0 || index > 255)
+       return CAIRO_STATUS_SUCCESS;
+
+    glyph_name = _cairo_ps_standard_encoding_to_glyphname (index);
+    if (glyph_name == NULL)
+       return CAIRO_STATUS_SUCCESS;
+
+    for (i = 0; i < font->base.num_glyphs; i++) {
+       if (font->glyph_names[i] &&  strcmp (font->glyph_names[i], glyph_name) == 0) {
+           cairo_type1_font_subset_use_glyph (font, i);
+
+           return CAIRO_STATUS_SUCCESS;
+       }
+    }
+
+    return CAIRO_INT_STATUS_UNSUPPORTED;
+}
+
+#define TYPE1_CHARSTRING_COMMAND_ESCAPE                0x0c
+#define TYPE1_CHARSTRING_COMMAND_SEAC          0x0c06
+#define TYPE1_CHARSTRING_COMMAND_SBW           0x0c07
+#define TYPE1_CHARSTRING_COMMAND_HSBW          0x0d
+#define TYPE1_CHARSTRING_COMMAND_CALLSUBR      0x0a
+#define TYPE1_CHARSTRING_COMMAND_CALLOTHERSUBR  0x0c10
+#define TYPE1_CHARSTRING_COMMAND_POP           0x0c11
+
+
+
+/* Get glyph width and look for seac operatorParse charstring */
+static cairo_status_t
+cairo_type1_font_subset_parse_charstring (cairo_type1_font_subset_t *font,
+                                         int                        glyph,
+                                         const char                *encrypted_charstring,
+                                         int                        encrypted_charstring_length)
+{
+    cairo_status_t status;
+    unsigned char *charstring;
+    const unsigned char *end;
+    const unsigned char *p;
+    cairo_bool_t last_op_was_integer;
+    int command;
+    int subr_num, i;
+
+    charstring = malloc (encrypted_charstring_length);
+    if (unlikely (charstring == NULL))
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    cairo_type1_font_subset_decrypt_charstring ((const unsigned char *)
+                                               encrypted_charstring,
+                                               encrypted_charstring_length,
+                                               charstring);
+    end = charstring + encrypted_charstring_length;
+
+    p = charstring + 4;
+
+    last_op_was_integer = FALSE;
+
+    while (p < end) {
+        if (*p < 32) {
+           command = *p++;
+           switch (command) {
+               case TYPE1_CHARSTRING_COMMAND_HSBW:
+                   font->glyphs[glyph].width = font->build_stack.stack[1]/font->base.units_per_em;
+                   font->build_stack.sp = 0;
+                   last_op_was_integer = FALSE;
+                   break;
+
+               case TYPE1_CHARSTRING_COMMAND_CALLSUBR:
+                   if (font->subset_subrs  &&
+                       last_op_was_integer &&
+                       font->build_stack.top_value >= 0    &&
+                       font->build_stack.top_value < font->num_subrs)
+                   {
+                       subr_num = font->build_stack.top_value;
+                       font->subrs[subr_num].used = TRUE;
+                       last_op_was_integer = FALSE;
+                       status = cairo_type1_font_subset_parse_charstring (font,
+                                                                          glyph,
+                                                                          font->subrs[subr_num].subr_string,
+                                                                          font->subrs[subr_num].subr_length);
+                   } else {
+                       font->subset_subrs = FALSE;
+                   }
+                   break;
+
+               case TYPE1_CHARSTRING_COMMAND_ESCAPE:
+                   command = command << 8 | *p++;
+                   switch (command) {
+                       case TYPE1_CHARSTRING_COMMAND_SEAC:
+                           /* The seac command takes five integer arguments.  The
+                            * last two are glyph indices into the PS standard
+                            * encoding give the names of the glyphs that this
+                            * glyph is composed from.  All we need to do is to
+                            * make sure those glyphs are present in the subset
+                            * under their standard names. */
+                           status = use_standard_encoding_glyph (font, font->build_stack.stack[3]);
+                           if (unlikely (status))
+                               return status;
+
+                           status = use_standard_encoding_glyph (font, font->build_stack.stack[4]);
+                           if (unlikely (status))
+                               return status;
+
+                           font->build_stack.sp = 0;
+                           last_op_was_integer = FALSE;
+                           break;
+
+                       case TYPE1_CHARSTRING_COMMAND_SBW:
+                           font->glyphs[glyph].width = font->build_stack.stack[2]/font->base.units_per_em;
+                           font->build_stack.sp = 0;
+                           last_op_was_integer = FALSE;
+                           break;
+
+                       case TYPE1_CHARSTRING_COMMAND_CALLOTHERSUBR:
+                           for (i = 0; i < font->build_stack.sp; i++)
+                               font->ps_stack.other_subr_args[i] = font->build_stack.stack[i];
+                           font->ps_stack.num_other_subr_args = font->build_stack.sp;
+                           font->ps_stack.cur_other_subr_arg = 0;
+                           font->build_stack.sp = 0;
+                           last_op_was_integer = FALSE;
+                           break;
+
+                       case TYPE1_CHARSTRING_COMMAND_POP:
+                           if (font->ps_stack.num_other_subr_args > font->ps_stack.cur_other_subr_arg) {
+                               font->build_stack.top_value = font->ps_stack.other_subr_args[font->ps_stack.cur_other_subr_arg++];
+                               last_op_was_integer = TRUE;
+                           } else {
+                               font->subset_subrs = FALSE;
+                           }
+                           break;
+
+                       default:
+                           font->build_stack.sp = 0;
+                           last_op_was_integer = FALSE;
+                           break;
+                   }
+                   break;
+
+               default:
+                   font->build_stack.sp = 0;
+                   last_op_was_integer = FALSE;
+                   break;
+           }
+        } else {
+            /* integer argument */
+           p = cairo_type1_font_subset_decode_integer (p, &font->build_stack.top_value);
+           last_op_was_integer = TRUE;
+           if (font->build_stack.sp < TYPE1_STACKSIZE)
+               font->build_stack.stack[font->build_stack.sp++] = font->build_stack.top_value;
+        }
+    }
+
+    free (charstring);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+cairo_type1_font_subset_build_subr_list (cairo_type1_font_subset_t *font,
+                                        int subr_number,
+                                        const char *encrypted_charstring, int encrypted_charstring_length)
+{
+
+    font->subrs[subr_number].subr_string = encrypted_charstring;
+    font->subrs[subr_number].subr_length = encrypted_charstring_length;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+write_used_subrs (cairo_type1_font_subset_t *font,
+                 int subr_number,
+                 const char *subr_string, int subr_string_length)
+{
+    cairo_status_t status;
+    char buffer[256];
+    int length;
+
+    if (!font->subrs[subr_number].used)
+       return CAIRO_STATUS_SUCCESS;
+
+    length = snprintf (buffer, sizeof buffer,
+                      "dup %d %d %s ",
+                      subr_number, subr_string_length, font->rd);
+    status = cairo_type1_font_subset_write_encrypted (font, buffer, length);
+    if (unlikely (status))
+       return status;
+
+    status = cairo_type1_font_subset_write_encrypted (font,
+                                                     subr_string,
+                                                     subr_string_length);
+    if (unlikely (status))
+       return status;
+
+    length = snprintf (buffer, sizeof buffer, "%s\n", font->np);
+    status = cairo_type1_font_subset_write_encrypted (font, buffer, length);
+    if (unlikely (status))
+       return status;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+typedef cairo_status_t (*subr_func_t) (cairo_type1_font_subset_t *font,
+                                       int subr_number,
+                                       const char *subr_string, int subr_string_length);
+
+static cairo_status_t
+cairo_type1_font_for_each_subr (cairo_type1_font_subset_t  *font,
+                               const char                 *array_start,
+                               const char                 *cleartext_end,
+                               subr_func_t                 func,
+                               const char                **array_end)
+{
+    const char *p, *subr_string;
+    char *end;
+    int subr_num, subr_length;
+    cairo_status_t status;
+
+    /* We're looking at "dup" at the start of the first subroutine. The subroutines
+     * definitions are on the form:
+     *
+     *   dup 5 23 RD <23 binary bytes> NP
+     *
+     * or alternatively using -| and |- instead of RD and ND.
+     * The first number is the subroutine number.
+     */
+
+    p = array_start;
+    while (p + 3 < cleartext_end && strncmp (p, "dup", 3) == 0) {
+       p = skip_token (p, cleartext_end);
+
+       /* get subr number */
+       subr_num = strtol (p, &end, 10);
+       if (p == end)
+           return CAIRO_INT_STATUS_UNSUPPORTED;
+
+       if (subr_num < 0 || subr_num >= font->num_subrs)
+           return CAIRO_INT_STATUS_UNSUPPORTED;
+
+       /* get subr length */
+       p = end;
+       subr_length = strtol (p, &end, 10);
+       if (p == end)
+           return CAIRO_INT_STATUS_UNSUPPORTED;
+
+       /* Skip past -| or RD to binary data.  There is exactly one space
+        * between the -| or RD token and the encrypted data, thus '+ 1'. */
+       subr_string = skip_token (end, cleartext_end) + 1;
+
+       status = func (font, subr_num,
+                      subr_string, subr_length);
+       if (unlikely (status))
+           return status;
+
+       /* Skip binary data and | or NP token. */
+       p = skip_token (subr_string + subr_length, cleartext_end);
+       while (p < cleartext_end && _cairo_isspace(*p))
+           p++;
+    }
+
+    *array_end = (char *) p;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+cairo_type1_font_subset_build_glyph_list (cairo_type1_font_subset_t *font,
+                                         int glyph_number,
+                                         const char *name, int name_length,
+                                         const char *encrypted_charstring, int encrypted_charstring_length)
+{
+    char *s;
+    glyph_data_t glyph;
+    cairo_status_t status;
+
+    s = malloc (name_length + 1);
+    if (unlikely (s == NULL))
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    strncpy (s, name, name_length);
+    s[name_length] = 0;
+
+    status = _cairo_array_append (&font->glyph_names_array, &s);
+    if (unlikely (status))
+       return status;
+
+    glyph.subset_index = -1;
+    glyph.width = 0;
+    glyph.encrypted_charstring = encrypted_charstring;
+    glyph.encrypted_charstring_length = encrypted_charstring_length;
+    status = _cairo_array_append (&font->glyphs_array, &glyph);
+
+    return status;
+}
+
+static cairo_status_t
+write_used_glyphs (cairo_type1_font_subset_t *font,
+                  int glyph_number,
+                  const char *name, int name_length,
+                  const char *charstring, int charstring_length)
+{
+    cairo_status_t status;
+    char buffer[256];
+    int length;
+
+    if (font->glyphs[glyph_number].subset_index < 0)
+       return CAIRO_STATUS_SUCCESS;
+
+    length = snprintf (buffer, sizeof buffer,
+                      "/%.*s %d %s ",
+                      name_length, name, charstring_length, font->rd);
+    status = cairo_type1_font_subset_write_encrypted (font, buffer, length);
+    if (unlikely (status))
+       return status;
+
+    status = cairo_type1_font_subset_write_encrypted (font,
+                                                     charstring,
+                                                     charstring_length);
+    if (unlikely (status))
+       return status;
+
+    length = snprintf (buffer, sizeof buffer, "%s\n", font->nd);
+    status = cairo_type1_font_subset_write_encrypted (font, buffer, length);
+    if (unlikely (status))
+       return status;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+typedef cairo_status_t (*glyph_func_t) (cairo_type1_font_subset_t *font,
+                                       int glyph_number,
+                                       const char *name, int name_length,
+                                       const char *charstring, int charstring_length);
+
+static cairo_status_t
+cairo_type1_font_subset_for_each_glyph (cairo_type1_font_subset_t *font,
+                                       const char *dict_start,
+                                       const char *dict_end,
+                                       glyph_func_t func,
+                                       const char **dict_out)
+{
+    int charstring_length, name_length;
+    const char *p, *charstring, *name;
+    char *end;
+    cairo_status_t status;
+    int glyph_count;
+
+    /* We're looking at '/' in the name of the first glyph.  The glyph
+     * definitions are on the form:
+     *
+     *   /name 23 RD <23 binary bytes> ND
+     *
+     * or alternatively using -| and |- instead of RD and ND.
+     *
+     * We parse the glyph name and see if it is in the subset.  If it
+     * is, we call the specified callback with the glyph name and
+     * glyph data, otherwise we just skip it.  We need to parse
+     * through a glyph definition; we can't just find the next '/',
+     * since the binary data could contain a '/'.
+     */
+
+    p = dict_start;
+    glyph_count = 0;
+    while (*p == '/') {
+       name = p + 1;
+       p = skip_token (p, dict_end);
+       name_length = p - name;
+
+       charstring_length = strtol (p, &end, 10);
+       if (p == end)
+           return CAIRO_INT_STATUS_UNSUPPORTED;
+
+       /* Skip past -| or RD to binary data.  There is exactly one space
+        * between the -| or RD token and the encrypted data, thus '+ 1'. */
+       charstring = skip_token (end, dict_end) + 1;
+
+       /* Skip binary data and |- or ND token. */
+       p = skip_token (charstring + charstring_length, dict_end);
+       while (p < dict_end && _cairo_isspace(*p))
+           p++;
+
+       /* In case any of the skip_token() calls above reached EOF, p will
+        * be equal to dict_end. */
+       if (p == dict_end)
+           return CAIRO_INT_STATUS_UNSUPPORTED;
+
+       status = func (font, glyph_count++,
+                      name, name_length,
+                      charstring, charstring_length);
+       if (unlikely (status))
+           return status;
+    }
+
+    *dict_out = p;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+
+static cairo_status_t
+cairo_type1_font_subset_write_private_dict (cairo_type1_font_subset_t *font,
+                                           const char                *name)
+{
+    cairo_status_t status;
+    const char *p, *subrs, *charstrings, *array_start, *array_end, *dict_start, *dict_end;
+    const char *closefile_token;
+    char buffer[32], *subr_count_end, *glyph_count_end;
+    int num_charstrings, length;
+    const cairo_scaled_font_backend_t *backend;
+    unsigned int i;
+
+    /* The private dict holds hint information, common subroutines and
+     * the actual glyph definitions (charstrings).
+     *
+     * What we do here is scan directly to the /Subrs token, which
+     * marks the beginning of the subroutines. We then read in all the
+     * subroutines then move on to the /CharString token, which marks
+     * the beginning of the glyph definitions, and read in the chastrings.
+     *
+     * The charstrings are parsed to extracts glyph widths, work out
+     * which subroutines are called, and too see if any extra glyphs
+     * need to be included due to the use of the seac glyph combining
+     * operator.
+     *
+     * Finally the private dict is copied to the subset font minus the
+     * subroutines and charstrings not required.
+     */
+
+    /* Find start of Subrs */
+    subrs = find_token (font->cleartext, font->cleartext_end, "/Subrs");
+    if (subrs == NULL) {
+       font->subset_subrs = FALSE;
+       p = font->cleartext;
+       goto skip_subrs;
+    }
+
+    /* Scan past /Subrs and get the array size. */
+    p = subrs + strlen ("/Subrs");
+    font->num_subrs = strtol (p, &subr_count_end, 10);
+    if (subr_count_end == p)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    if (font->num_subrs <= 0)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    font->subrs = calloc (font->num_subrs, sizeof (font->subrs[0]));
+    if (unlikely (font->subrs == NULL))
+        return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    /* look for "dup" which marks the beginning of the first subr */
+    array_start = find_token (subr_count_end, font->cleartext_end, "dup");
+    if (subrs == NULL)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    /* Read in the subroutines */
+    status = cairo_type1_font_for_each_subr (font,
+                                            array_start,
+                                            font->cleartext_end,
+                                            cairo_type1_font_subset_build_subr_list,
+                                            &array_end);
+    if (unlikely(status))
+       return status;
+
+    p = array_end;
+skip_subrs:
+
+    /* Find start of CharStrings */
+    charstrings = find_token (p, font->cleartext_end, "/CharStrings");
+    if (charstrings == NULL)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    /* Scan past /CharStrings and the integer following it. */
+    p = charstrings + strlen ("/CharStrings");
+    num_charstrings = strtol (p, &glyph_count_end, 10);
+    if (p == glyph_count_end)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    /* Look for a '/' which marks the beginning of the first glyph
+     * definition. */
+    for (p = glyph_count_end; p < font->cleartext_end; p++)
+       if (*p == '/')
+           break;
+    if (p == font->cleartext_end)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+    dict_start = p;
+
+    /* Now that we have the private dictionary broken down in
+     * sections, do the first pass through the glyph definitions to
+     * build a list of glyph names and charstrings. */
+    status = cairo_type1_font_subset_for_each_glyph (font,
+                                                    dict_start,
+                                                    font->cleartext_end,
+                                                    cairo_type1_font_subset_build_glyph_list,
+                                                    &dict_end);
+    if (unlikely(status))
+       return status;
+
+    font->glyphs = _cairo_array_index (&font->glyphs_array, 0);
+    font->glyph_names = _cairo_array_index (&font->glyph_names_array, 0);
+    font->base.num_glyphs = _cairo_array_num_elements (&font->glyphs_array);
+    font->subset_index_to_glyphs = calloc (font->base.num_glyphs, sizeof font->subset_index_to_glyphs[0]);
+    if (unlikely (font->subset_index_to_glyphs == NULL))
+        return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    backend = font->scaled_font_subset->scaled_font->backend;
+    if (!backend->index_to_glyph_name)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    /* Find the glyph number corresponding to each glyph in the subset
+     * and mark it as in use */
+
+    for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) {
+       unsigned long index;
+
+       status = backend->index_to_glyph_name (font->scaled_font_subset->scaled_font,
+                                              font->glyph_names,
+                                              font->base.num_glyphs,
+                                              font->scaled_font_subset->glyphs[i],
+                                              &index);
+       if (unlikely(status))
+           return status;
+
+       cairo_type1_font_subset_use_glyph (font, index);
+    }
+
+    /* Go through the charstring of each glyph in use, get the glyph
+     * width and figure out which extra glyphs may be required by the
+     * seac operator. Also subset the Subrs. */
+    for (i = 0; i < font->base.num_glyphs; i++) {
+       if (font->glyphs[i].subset_index >= 0) {
+           font->build_stack.sp = 0;
+           font->ps_stack.num_other_subr_args = 0;
+           status = cairo_type1_font_subset_parse_charstring (font,
+                                                              i,
+                                                              font->glyphs[i].encrypted_charstring,
+                                                              font->glyphs[i].encrypted_charstring_length);
+           if (unlikely (status))
+               return status;
+       }
+    }
+
+    closefile_token = find_token (dict_end, font->cleartext_end, "closefile");
+    if (closefile_token == NULL)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    /* We're ready to start outputting. First write the header,
+     * i.e. the public part of the font dict.*/
+    status = cairo_type1_font_subset_write_header (font, name);
+    if (unlikely (status))
+       return status;
+
+    font->base.header_size = _cairo_output_stream_get_position (font->output);
+
+    /* Start outputting the private dict */
+    if (font->subset_subrs) {
+       /* First output everything up to the start of the Subrs array. */
+       status = cairo_type1_font_subset_write_encrypted (font, font->cleartext,
+                                                         array_start - font->cleartext);
+       if (unlikely (status))
+           return status;
+
+       /* Write out the subr definitions for each of the glyphs in
+        * the subset. */
+       status = cairo_type1_font_for_each_subr (font,
+                                                array_start,
+                                                font->cleartext_end,
+                                                write_used_subrs,
+                                                &p);
+       if (unlikely (status))
+           return status;
+    } else {
+       p = font->cleartext;
+    }
+
+    /* If subr subsetting, output everything from end of subrs to
+     * start of /CharStrings token.  If not subr subsetting, output
+     * everything start of private dict to start of /CharStrings
+     * token. */
+    status = cairo_type1_font_subset_write_encrypted (font, p, charstrings - p);
+    if (unlikely (status))
+       return status;
+
+    /* Write out new charstring count */
+    length = snprintf (buffer, sizeof buffer,
+                      "/CharStrings %d", font->num_glyphs);
+    status = cairo_type1_font_subset_write_encrypted (font, buffer, length);
+    if (unlikely (status))
+       return status;
+
+    /* Write out text between the charstring count and the first
+     * charstring definition */
+    status = cairo_type1_font_subset_write_encrypted (font, glyph_count_end,
+                                                 dict_start - glyph_count_end);
+    if (unlikely (status))
+       return status;
+
+    /* Write out the charstring definitions for each of the glyphs in
+     * the subset. */
+    status = cairo_type1_font_subset_for_each_glyph (font,
+                                                    dict_start,
+                                                    font->cleartext_end,
+                                                    write_used_glyphs,
+                                                    &p);
+    if (unlikely (status))
+       return status;
+
+    /* Output what's left between the end of the glyph definitions and
+     * the end of the private dict to the output. */
+    status = cairo_type1_font_subset_write_encrypted (font, p,
+                               closefile_token - p + strlen ("closefile") + 1);
+    if (unlikely (status))
+       return status;
+
+    if (font->hex_encode)
+       _cairo_output_stream_write (font->output, "\n", 1);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+cairo_type1_font_subset_write_trailer(cairo_type1_font_subset_t *font)
+{
+    const char *cleartomark_token;
+    int i;
+    static const char zeros[65] =
+       "0000000000000000000000000000000000000000000000000000000000000000\n";
+
+
+    for (i = 0; i < 8; i++)
+       _cairo_output_stream_write (font->output, zeros, sizeof zeros);
+
+    cleartomark_token = find_token (font->type1_data, font->type1_end, "cleartomark");
+    if (cleartomark_token) {
+       /* Some fonts have conditional save/restore around the entire
+        * font dict, so we need to retain whatever postscript code
+        * that may come after 'cleartomark'. */
+
+       _cairo_output_stream_write (font->output, cleartomark_token,
+                                   font->type1_end - cleartomark_token);
+       if (*(font->type1_end - 1) != '\n')
+           _cairo_output_stream_printf (font->output, "\n");
+
+    } else if (!font->eexec_segment_is_ascii) {
+       /* Fonts embedded in PDF may omit the fixed-content portion
+        * that includes the 'cleartomark' operator. Type 1 in PDF is
+        * always binary. */
+
+       _cairo_output_stream_printf (font->output, "cleartomark\n");
+    } else {
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+    }
+
+    /* some fonts do not have a newline at the end of the last line */
+    _cairo_output_stream_printf (font->output, "\n");
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+type1_font_write (void *closure, const unsigned char *data, unsigned int length)
+{
+    cairo_type1_font_subset_t *font = closure;
+
+    return _cairo_array_append_multiple (&font->contents, data, length);
+}
+
+static cairo_status_t
+cairo_type1_font_subset_write (cairo_type1_font_subset_t *font,
+                              const char *name)
+{
+    cairo_status_t status;
+
+    status = cairo_type1_font_subset_find_segments (font);
+    if (unlikely (status))
+       return status;
+
+    status = cairo_type1_font_subset_decrypt_eexec_segment (font);
+    if (unlikely (status))
+       return status;
+
+    /* Determine which glyph definition delimiters to use. */
+    if (find_token (font->cleartext, font->cleartext_end, "/-|") != NULL) {
+       font->rd = "-|";
+       font->nd = "|-";
+       font->np = "|";
+    } else if (find_token (font->cleartext, font->cleartext_end, "/RD") != NULL) {
+       font->rd = "RD";
+       font->nd = "ND";
+       font->np = "NP";
+    } else {
+       /* Don't know *what* kind of font this is... */
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+    }
+
+    font->eexec_key = CAIRO_TYPE1_PRIVATE_DICT_KEY;
+    font->hex_column = 0;
+
+    status = cairo_type1_font_subset_get_bbox (font);
+    if (unlikely (status))
+       return status;
+
+    status = cairo_type1_font_subset_get_fontname (font);
+    if (unlikely (status))
+       return status;
+
+    status = cairo_type1_font_subset_write_private_dict (font, name);
+    if (unlikely (status))
+       return status;
+
+    font->base.data_size = _cairo_output_stream_get_position (font->output) -
+       font->base.header_size;
+
+    status = cairo_type1_font_subset_write_trailer (font);
+    if (unlikely (status))
+       return status;
+
+    font->base.trailer_size =
+       _cairo_output_stream_get_position (font->output) -
+       font->base.header_size - font->base.data_size;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_bool_t
+check_fontdata_is_type1 (const unsigned char *data, long length)
+{
+    /* Test for  Type 1 Binary (PFB) */
+    if (length > 2 && data[0] == 0x80 && data[1] == 0x01)
+       return TRUE;
+
+    /* Test for Type 1 1 ASCII (PFA) */
+    if (length > 2 && data[0] == '%' && data[1] == '!')
+       return TRUE;
+
+    return FALSE;
+}
+
+static cairo_status_t
+cairo_type1_font_subset_generate (void       *abstract_font,
+                                 const char *name)
+
+{
+    cairo_type1_font_subset_t *font = abstract_font;
+    cairo_scaled_font_t *scaled_font;
+    cairo_status_t status;
+    unsigned long data_length;
+
+    scaled_font = font->scaled_font_subset->scaled_font;
+    if (!scaled_font->backend->load_type1_data)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    status = scaled_font->backend->load_type1_data (scaled_font, 0, NULL, &data_length);
+    if (status)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    font->type1_length = data_length;
+    font->type1_data = malloc (font->type1_length);
+    if (unlikely (font->type1_data == NULL))
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    status = scaled_font->backend->load_type1_data (scaled_font, 0,
+                                                   (unsigned char *) font->type1_data,
+                                                   &data_length);
+    if (unlikely (status))
+        return status;
+
+    if (!check_fontdata_is_type1 ((unsigned char *)font->type1_data, data_length))
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    status = _cairo_array_grow_by (&font->contents, 4096);
+    if (unlikely (status))
+       return status;
+
+    font->output = _cairo_output_stream_create (type1_font_write, NULL, font);
+    if (unlikely ((status = font->output->status)))
+       return status;
+
+    status = cairo_type1_font_subset_write (font, name);
+    if (unlikely (status))
+       return status;
+
+    font->base.data = _cairo_array_index (&font->contents, 0);
+
+    return status;
+}
+
+static cairo_status_t
+_cairo_type1_font_subset_fini (cairo_type1_font_subset_t *font)
+{
+    cairo_status_t status = CAIRO_STATUS_SUCCESS;
+    unsigned int i;
+
+    /* If the subset generation failed, some of the pointers below may
+     * be NULL depending on at which point the error occurred. */
+
+    _cairo_array_fini (&font->contents);
+
+    free (font->type1_data);
+    for (i = 0; i < _cairo_array_num_elements (&font->glyph_names_array); i++) {
+       char **s;
+
+       s = _cairo_array_index (&font->glyph_names_array, i);
+       free (*s);
+    }
+    _cairo_array_fini (&font->glyph_names_array);
+    _cairo_array_fini (&font->glyphs_array);
+
+    free (font->subrs);
+
+    if (font->output != NULL)
+       status = _cairo_output_stream_destroy (font->output);
+
+    free (font->base.base_font);
+
+    free (font->subset_index_to_glyphs);
+
+    return status;
+}
+
+cairo_status_t
+_cairo_type1_subset_init (cairo_type1_subset_t         *type1_subset,
+                         const char                    *name,
+                         cairo_scaled_font_subset_t    *scaled_font_subset,
+                          cairo_bool_t                   hex_encode)
+{
+    cairo_type1_font_subset_t font;
+    cairo_status_t status, status_ignored;
+    unsigned long length;
+    unsigned int i;
+    char buf[30];
+
+    /* We need to use a fallback font generated from the synthesized outlines. */
+    if (scaled_font_subset->scaled_font->backend->is_synthetic &&
+       scaled_font_subset->scaled_font->backend->is_synthetic (scaled_font_subset->scaled_font))
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    status = _cairo_type1_font_subset_init (&font, scaled_font_subset, hex_encode);
+    if (unlikely (status))
+       return status;
+
+    status = cairo_type1_font_subset_generate (&font, name);
+    if (unlikely (status))
+       goto fail1;
+
+    if (font.base.base_font) {
+       type1_subset->base_font = strdup (font.base.base_font);
+    } else {
+        snprintf(buf, sizeof (buf), "CairoFont-%u-%u",
+                 scaled_font_subset->font_id, scaled_font_subset->subset_id);
+       type1_subset->base_font = strdup (buf);
+    }
+    if (unlikely (type1_subset->base_font == NULL))
+       goto fail1;
+
+    type1_subset->widths = calloc (sizeof (double), font.num_glyphs);
+    if (unlikely (type1_subset->widths == NULL))
+       goto fail2;
+    for (i = 0; i < font.base.num_glyphs; i++) {
+       if (font.glyphs[i].subset_index < 0)
+           continue;
+       type1_subset->widths[font.glyphs[i].subset_index] =
+           font.glyphs[i].width;
+    }
+
+    type1_subset->x_min = font.base.x_min;
+    type1_subset->y_min = font.base.y_min;
+    type1_subset->x_max = font.base.x_max;
+    type1_subset->y_max = font.base.y_max;
+    type1_subset->ascent = font.base.ascent;
+    type1_subset->descent = font.base.descent;
+
+    length = font.base.header_size +
+            font.base.data_size +
+            font.base.trailer_size;
+    type1_subset->data = malloc (length);
+    if (unlikely (type1_subset->data == NULL))
+       goto fail3;
+
+    memcpy (type1_subset->data,
+           _cairo_array_index (&font.contents, 0), length);
+
+    type1_subset->header_length = font.base.header_size;
+    type1_subset->data_length = font.base.data_size;
+    type1_subset->trailer_length = font.base.trailer_size;
+
+    return _cairo_type1_font_subset_fini (&font);
+
+ fail3:
+    free (type1_subset->widths);
+ fail2:
+    free (type1_subset->base_font);
+ fail1:
+    status_ignored = _cairo_type1_font_subset_fini (&font);
+
+    return status;
+}
+
+void
+_cairo_type1_subset_fini (cairo_type1_subset_t *subset)
+{
+    free (subset->base_font);
+    free (subset->widths);
+    free (subset->data);
+}
+
+cairo_bool_t
+_cairo_type1_scaled_font_is_type1 (cairo_scaled_font_t *scaled_font)
+{
+    cairo_status_t status;
+    unsigned long length;
+    unsigned char buf[64];
+
+    if (!scaled_font->backend->load_type1_data)
+       return FALSE;
+
+    status = scaled_font->backend->load_type1_data (scaled_font, 0, NULL, &length);
+    if (status)
+       return FALSE;
+
+    /* We only need a few bytes to test for Type 1 */
+    if (length > sizeof (buf))
+       length = sizeof (buf);
+
+    status = scaled_font->backend->load_type1_data (scaled_font, 0, buf, &length);
+    if (status)
+       return FALSE;
+
+    return check_fontdata_is_type1 (buf, length);
+}
+
+#endif /* CAIRO_HAS_FONT_SUBSET */
diff --git a/src/cairo-type3-glyph-surface-private.h b/src/cairo-type3-glyph-surface-private.h
new file mode 100644 (file)
index 0000000..b4abcf6
--- /dev/null
@@ -0,0 +1,87 @@
+/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2008 Adrian Johnson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Adrian Johnson.
+ *
+ * Contributor(s):
+ *     Adrian Johnson <ajohnson@redneon.com>
+ */
+
+#ifndef CAIRO_TYPE3_GLYPH_SURFACE_PRIVATE_H
+#define CAIRO_TYPE3_GLYPH_SURFACE_PRIVATE_H
+
+#include "cairoint.h"
+
+#if CAIRO_HAS_FONT_SUBSET
+
+#include "cairo-surface-private.h"
+#include "cairo-surface-clipper-private.h"
+#include "cairo-pdf-operators-private.h"
+
+typedef cairo_status_t (*cairo_type3_glyph_surface_emit_image_t) (cairo_image_surface_t *image,
+                                                                 cairo_output_stream_t *stream);
+
+typedef struct cairo_type3_glyph_surface {
+    cairo_surface_t base;
+
+    cairo_scaled_font_t *scaled_font;
+    cairo_output_stream_t *stream;
+    cairo_pdf_operators_t pdf_operators;
+    cairo_matrix_t cairo_to_pdf;
+    cairo_type3_glyph_surface_emit_image_t emit_image;
+
+    cairo_surface_clipper_t clipper;
+} cairo_type3_glyph_surface_t;
+
+cairo_private cairo_surface_t *
+_cairo_type3_glyph_surface_create (cairo_scaled_font_t                  *scaled_font,
+                                  cairo_output_stream_t                 *stream,
+                                  cairo_type3_glyph_surface_emit_image_t emit_image,
+                                  cairo_scaled_font_subsets_t           *font_subsets);
+
+cairo_private void
+_cairo_type3_glyph_surface_set_font_subsets_callback (void                                 *abstract_surface,
+                                                     cairo_pdf_operators_use_font_subset_t  use_font_subset,
+                                                     void                                  *closure);
+
+cairo_private cairo_status_t
+_cairo_type3_glyph_surface_analyze_glyph (void              *abstract_surface,
+                                         unsigned long       glyph_index);
+
+cairo_private cairo_status_t
+_cairo_type3_glyph_surface_emit_glyph (void                 *abstract_surface,
+                                      cairo_output_stream_t *stream,
+                                      unsigned long          glyph_index,
+                                      cairo_box_t           *bbox,
+                                      double                *width);
+
+#endif /* CAIRO_HAS_FONT_SUBSET */
+
+#endif /* CAIRO_TYPE3_GLYPH_SURFACE_PRIVATE_H */
diff --git a/src/cairo-type3-glyph-surface.c b/src/cairo-type3-glyph-surface.c
new file mode 100644 (file)
index 0000000..ee2d0e3
--- /dev/null
@@ -0,0 +1,565 @@
+/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2008 Adrian Johnson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Adrian Johnson.
+ *
+ * Contributor(s):
+ *     Adrian Johnson <ajohnson@redneon.com>
+ */
+
+#include "cairoint.h"
+
+#if CAIRO_HAS_FONT_SUBSET
+
+#include "cairo-type3-glyph-surface-private.h"
+#include "cairo-output-stream-private.h"
+#include "cairo-recording-surface-private.h"
+#include "cairo-analysis-surface-private.h"
+#include "cairo-default-context-private.h"
+#include "cairo-error-private.h"
+#include "cairo-image-surface-private.h"
+#include "cairo-surface-clipper-private.h"
+
+static const cairo_surface_backend_t cairo_type3_glyph_surface_backend;
+
+static cairo_status_t
+_cairo_type3_glyph_surface_clipper_intersect_clip_path (cairo_surface_clipper_t *clipper,
+                                                       cairo_path_fixed_t *path,
+                                                       cairo_fill_rule_t   fill_rule,
+                                                       double              tolerance,
+                                                       cairo_antialias_t   antialias)
+{
+    cairo_type3_glyph_surface_t *surface = cairo_container_of (clipper,
+                                                              cairo_type3_glyph_surface_t,
+                                                              clipper);
+
+    if (path == NULL) {
+       _cairo_output_stream_printf (surface->stream, "Q q\n");
+       return CAIRO_STATUS_SUCCESS;
+    }
+
+    return _cairo_pdf_operators_clip (&surface->pdf_operators,
+                                     path,
+                                     fill_rule);
+}
+
+cairo_surface_t *
+_cairo_type3_glyph_surface_create (cairo_scaled_font_t                  *scaled_font,
+                                  cairo_output_stream_t                 *stream,
+                                  cairo_type3_glyph_surface_emit_image_t emit_image,
+                                  cairo_scaled_font_subsets_t           *font_subsets)
+{
+    cairo_type3_glyph_surface_t *surface;
+    cairo_matrix_t invert_y_axis;
+
+    if (unlikely (stream != NULL && stream->status))
+       return _cairo_surface_create_in_error (stream->status);
+
+    surface = malloc (sizeof (cairo_type3_glyph_surface_t));
+    if (unlikely (surface == NULL))
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+    _cairo_surface_init (&surface->base,
+                        &cairo_type3_glyph_surface_backend,
+                        NULL, /* device */
+                        CAIRO_CONTENT_COLOR_ALPHA);
+
+    surface->scaled_font = scaled_font;
+    surface->stream = stream;
+    surface->emit_image = emit_image;
+
+    /* Setup the transform from the user-font device space to Type 3
+     * font space. The Type 3 font space is defined by the FontMatrix
+     * entry in the Type 3 dictionary. In the PDF backend this is an
+     * identity matrix. */
+    surface->cairo_to_pdf = scaled_font->scale_inverse;
+    cairo_matrix_init_scale (&invert_y_axis, 1, -1);
+    cairo_matrix_multiply (&surface->cairo_to_pdf, &surface->cairo_to_pdf, &invert_y_axis);
+
+    _cairo_pdf_operators_init (&surface->pdf_operators,
+                              surface->stream,
+                              &surface->cairo_to_pdf,
+                              font_subsets);
+
+    _cairo_surface_clipper_init (&surface->clipper,
+                                _cairo_type3_glyph_surface_clipper_intersect_clip_path);
+
+    return &surface->base;
+}
+
+static cairo_status_t
+_cairo_type3_glyph_surface_emit_image (cairo_type3_glyph_surface_t *surface,
+                                      cairo_image_surface_t       *image,
+                                      cairo_matrix_t              *image_matrix)
+{
+    cairo_status_t status;
+
+    /* The only image type supported by Type 3 fonts are 1-bit masks */
+    image = _cairo_image_surface_coerce_to_format (image, CAIRO_FORMAT_A1);
+    status = image->base.status;
+    if (unlikely (status))
+       return status;
+
+    _cairo_output_stream_printf (surface->stream,
+                                "q %f %f %f %f %f %f cm\n",
+                                image_matrix->xx,
+                                image_matrix->xy,
+                                image_matrix->yx,
+                                image_matrix->yy,
+                                image_matrix->x0,
+                                image_matrix->y0);
+
+    status = surface->emit_image (image, surface->stream);
+    cairo_surface_destroy (&image->base);
+
+    _cairo_output_stream_printf (surface->stream,
+                                "Q\n");
+
+    return status;
+}
+
+static cairo_status_t
+_cairo_type3_glyph_surface_emit_image_pattern (cairo_type3_glyph_surface_t *surface,
+                                              cairo_image_surface_t       *image,
+                                              const cairo_matrix_t              *pattern_matrix)
+{
+    cairo_matrix_t mat, upside_down;
+    cairo_status_t status;
+
+    if (image->width == 0 || image->height == 0)
+       return CAIRO_STATUS_SUCCESS;
+
+    mat = *pattern_matrix;
+
+    /* Get the pattern space to user space matrix  */
+    status = cairo_matrix_invert (&mat);
+
+    /* cairo_pattern_set_matrix ensures the matrix is invertible */
+    assert (status == CAIRO_STATUS_SUCCESS);
+
+    /* Make this a pattern space to Type 3 font space matrix */
+    cairo_matrix_multiply (&mat, &mat, &surface->cairo_to_pdf);
+
+    /* PDF images are in a 1 unit by 1 unit image space. Turn the 1 by
+     * 1 image upside down to convert to flip the Y-axis going from
+     * cairo to PDF. Then scale the image up to the required size. */
+    cairo_matrix_scale (&mat, image->width, image->height);
+    cairo_matrix_init (&upside_down, 1, 0, 0, -1, 0, 1);
+    cairo_matrix_multiply (&mat, &upside_down, &mat);
+
+    return _cairo_type3_glyph_surface_emit_image (surface, image, &mat);
+}
+
+static cairo_status_t
+_cairo_type3_glyph_surface_finish (void *abstract_surface)
+{
+    cairo_type3_glyph_surface_t *surface = abstract_surface;
+
+    return _cairo_pdf_operators_fini (&surface->pdf_operators);
+}
+
+static cairo_int_status_t
+_cairo_type3_glyph_surface_paint (void                 *abstract_surface,
+                                 cairo_operator_t       op,
+                                 const cairo_pattern_t *source,
+                                 const cairo_clip_t    *clip)
+{
+    cairo_type3_glyph_surface_t *surface = abstract_surface;
+    const cairo_surface_pattern_t *pattern;
+    cairo_image_surface_t *image;
+    void *image_extra;
+    cairo_status_t status;
+
+    if (source->type != CAIRO_PATTERN_TYPE_SURFACE)
+       return CAIRO_INT_STATUS_IMAGE_FALLBACK;
+
+    status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
+    if (unlikely (status))
+       return status;
+
+    pattern = (const cairo_surface_pattern_t *) source;
+    status = _cairo_surface_acquire_source_image (pattern->surface,
+                                                 &image, &image_extra);
+    if (unlikely (status))
+       goto fail;
+
+    status = _cairo_type3_glyph_surface_emit_image_pattern (surface,
+                                                           image,
+                                                           &pattern->base.matrix);
+
+fail:
+    _cairo_surface_release_source_image (pattern->surface, image, image_extra);
+
+    return status;
+}
+
+static cairo_int_status_t
+_cairo_type3_glyph_surface_mask (void                  *abstract_surface,
+                                cairo_operator_t        op,
+                                const cairo_pattern_t  *source,
+                                const cairo_pattern_t  *mask,
+                                const cairo_clip_t     *clip)
+{
+    return _cairo_type3_glyph_surface_paint (abstract_surface,
+                                            op, mask,
+                                            clip);
+}
+
+static cairo_int_status_t
+_cairo_type3_glyph_surface_stroke (void                        *abstract_surface,
+                                  cairo_operator_t      op,
+                                  const cairo_pattern_t *source,
+                                  const cairo_path_fixed_t     *path,
+                                  const cairo_stroke_style_t   *style,
+                                  const cairo_matrix_t *ctm,
+                                  const cairo_matrix_t *ctm_inverse,
+                                  double                tolerance,
+                                  cairo_antialias_t     antialias,
+                                  const cairo_clip_t   *clip)
+{
+    cairo_type3_glyph_surface_t *surface = abstract_surface;
+    cairo_int_status_t status;
+
+    status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
+    if (unlikely (status))
+       return status;
+
+    return _cairo_pdf_operators_stroke (&surface->pdf_operators,
+                                       path,
+                                       style,
+                                       ctm,
+                                       ctm_inverse);
+}
+
+static cairo_int_status_t
+_cairo_type3_glyph_surface_fill (void                  *abstract_surface,
+                                cairo_operator_t        op,
+                                const cairo_pattern_t  *source,
+                                const cairo_path_fixed_t       *path,
+                                cairo_fill_rule_t       fill_rule,
+                                double                  tolerance,
+                                cairo_antialias_t       antialias,
+                                const cairo_clip_t             *clip)
+{
+    cairo_type3_glyph_surface_t *surface = abstract_surface;
+    cairo_int_status_t status;
+
+    status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
+    if (unlikely (status))
+       return status;
+
+    return _cairo_pdf_operators_fill (&surface->pdf_operators,
+                                     path,
+                                     fill_rule);
+}
+
+static cairo_int_status_t
+_cairo_type3_glyph_surface_show_glyphs (void                *abstract_surface,
+                                       cairo_operator_t      op,
+                                       const cairo_pattern_t *source,
+                                       cairo_glyph_t        *glyphs,
+                                       int                   num_glyphs,
+                                       cairo_scaled_font_t  *scaled_font,
+                                       const cairo_clip_t     *clip)
+{
+    cairo_type3_glyph_surface_t *surface = abstract_surface;
+    cairo_int_status_t status;
+    cairo_scaled_font_t *font;
+    cairo_matrix_t new_ctm, invert_y_axis;
+
+    status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
+    if (unlikely (status))
+       return status;
+
+    cairo_matrix_init_scale (&invert_y_axis, 1, -1);
+    cairo_matrix_multiply (&new_ctm, &invert_y_axis, &scaled_font->ctm);
+    cairo_matrix_multiply (&new_ctm, &surface->cairo_to_pdf, &new_ctm);
+    font = cairo_scaled_font_create (scaled_font->font_face,
+                                    &scaled_font->font_matrix,
+                                    &new_ctm,
+                                    &scaled_font->options);
+    if (unlikely (font->status))
+       return font->status;
+
+    status = _cairo_pdf_operators_show_text_glyphs (&surface->pdf_operators,
+                                                   NULL, 0,
+                                                   glyphs, num_glyphs,
+                                                   NULL, 0,
+                                                   FALSE,
+                                                   font);
+
+    cairo_scaled_font_destroy (font);
+
+    return status;
+}
+
+static const cairo_surface_backend_t cairo_type3_glyph_surface_backend = {
+    CAIRO_INTERNAL_SURFACE_TYPE_TYPE3_GLYPH,
+    _cairo_type3_glyph_surface_finish,
+
+    _cairo_default_context_create, /* XXX usable through a context? */
+
+    NULL, /* create similar */
+    NULL, /* create similar image */
+    NULL, /* map to image */
+    NULL, /* unmap image */
+
+    NULL, /* acquire_source_image */
+    NULL, /* release_source_image */
+    NULL, /* snapshot */
+
+    NULL, /* copy page */
+    NULL, /* show page */
+
+    NULL, /* _cairo_type3_glyph_surface_get_extents */
+    NULL, /* _cairo_type3_glyph_surface_get_font_options */
+
+    NULL, /* flush */
+    NULL, /* mark_dirty_rectangle */
+
+    _cairo_type3_glyph_surface_paint,
+    _cairo_type3_glyph_surface_mask,
+    _cairo_type3_glyph_surface_stroke,
+    _cairo_type3_glyph_surface_fill,
+    NULL, /* fill-stroke */
+    _cairo_type3_glyph_surface_show_glyphs,
+};
+
+static void
+_cairo_type3_glyph_surface_set_stream (cairo_type3_glyph_surface_t *surface,
+                                      cairo_output_stream_t       *stream)
+{
+    surface->stream = stream;
+    _cairo_pdf_operators_set_stream (&surface->pdf_operators, stream);
+}
+
+static cairo_status_t
+_cairo_type3_glyph_surface_emit_fallback_image (cairo_type3_glyph_surface_t *surface,
+                                               unsigned long                glyph_index)
+{
+    cairo_scaled_glyph_t *scaled_glyph;
+    cairo_status_t status;
+    cairo_image_surface_t *image;
+    cairo_matrix_t mat;
+    double x, y;
+
+    status = _cairo_scaled_glyph_lookup (surface->scaled_font,
+                                        glyph_index,
+                                        CAIRO_SCALED_GLYPH_INFO_METRICS |
+                                        CAIRO_SCALED_GLYPH_INFO_SURFACE,
+                                        &scaled_glyph);
+    if (unlikely (status))
+       return status;
+
+    image = scaled_glyph->surface;
+    if (image->width == 0 || image->height == 0)
+       return CAIRO_STATUS_SUCCESS;
+
+    x = _cairo_fixed_to_double (scaled_glyph->bbox.p1.x);
+    y = _cairo_fixed_to_double (scaled_glyph->bbox.p2.y);
+    mat.xx = image->width;
+    mat.xy = 0;
+    mat.yx = 0;
+    mat.yy = image->height;
+    mat.x0 = x;
+    mat.y0 = y;
+    cairo_matrix_multiply (&mat, &mat, &surface->scaled_font->scale_inverse);
+    mat.y0 *= -1;
+
+    return _cairo_type3_glyph_surface_emit_image (surface, image, &mat);
+}
+
+void
+_cairo_type3_glyph_surface_set_font_subsets_callback (void                                 *abstract_surface,
+                                                     cairo_pdf_operators_use_font_subset_t  use_font_subset,
+                                                     void                                  *closure)
+{
+    cairo_type3_glyph_surface_t *surface = abstract_surface;
+
+    if (unlikely (surface->base.status))
+       return;
+
+    _cairo_pdf_operators_set_font_subsets_callback (&surface->pdf_operators,
+                                                   use_font_subset,
+                                                   closure);
+}
+
+cairo_status_t
+_cairo_type3_glyph_surface_analyze_glyph (void              *abstract_surface,
+                                         unsigned long       glyph_index)
+{
+    cairo_type3_glyph_surface_t *surface = abstract_surface;
+    cairo_scaled_glyph_t *scaled_glyph;
+    cairo_int_status_t status, status2;
+    cairo_output_stream_t *null_stream;
+
+    if (unlikely (surface->base.status))
+       return surface->base.status;
+
+    null_stream = _cairo_null_stream_create ();
+    if (unlikely (null_stream->status))
+       return null_stream->status;
+
+    _cairo_type3_glyph_surface_set_stream (surface, null_stream);
+
+    _cairo_scaled_font_freeze_cache (surface->scaled_font);
+    status = _cairo_scaled_glyph_lookup (surface->scaled_font,
+                                        glyph_index,
+                                        CAIRO_SCALED_GLYPH_INFO_RECORDING_SURFACE,
+                                        &scaled_glyph);
+
+    if (_cairo_int_status_is_error (status))
+       goto cleanup;
+
+    if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
+       status = CAIRO_INT_STATUS_SUCCESS;
+       goto cleanup;
+    }
+
+    status = _cairo_recording_surface_replay (scaled_glyph->recording_surface,
+                                             &surface->base);
+    if (unlikely (status))
+       goto cleanup;
+
+    status = _cairo_pdf_operators_flush (&surface->pdf_operators);
+    if (status == CAIRO_INT_STATUS_IMAGE_FALLBACK)
+       status = CAIRO_INT_STATUS_SUCCESS;
+
+cleanup:
+    _cairo_scaled_font_thaw_cache (surface->scaled_font);
+
+    status2 = _cairo_output_stream_destroy (null_stream);
+    if (status == CAIRO_INT_STATUS_SUCCESS)
+       status = status2;
+
+    return status;
+}
+
+cairo_status_t
+_cairo_type3_glyph_surface_emit_glyph (void                 *abstract_surface,
+                                      cairo_output_stream_t *stream,
+                                      unsigned long          glyph_index,
+                                      cairo_box_t           *bbox,
+                                      double                *width)
+{
+    cairo_type3_glyph_surface_t *surface = abstract_surface;
+    cairo_scaled_glyph_t *scaled_glyph;
+    cairo_int_status_t status, status2;
+    double x_advance, y_advance;
+    cairo_matrix_t font_matrix_inverse;
+
+    if (unlikely (surface->base.status))
+       return surface->base.status;
+
+    _cairo_type3_glyph_surface_set_stream (surface, stream);
+
+    _cairo_scaled_font_freeze_cache (surface->scaled_font);
+    status = _cairo_scaled_glyph_lookup (surface->scaled_font,
+                                        glyph_index,
+                                        CAIRO_SCALED_GLYPH_INFO_METRICS |
+                                        CAIRO_SCALED_GLYPH_INFO_RECORDING_SURFACE,
+                                        &scaled_glyph);
+    if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
+       status = _cairo_scaled_glyph_lookup (surface->scaled_font,
+                                            glyph_index,
+                                            CAIRO_SCALED_GLYPH_INFO_METRICS,
+                                            &scaled_glyph);
+       if (status == CAIRO_INT_STATUS_SUCCESS)
+           status = CAIRO_INT_STATUS_IMAGE_FALLBACK;
+    }
+    if (_cairo_int_status_is_error (status)) {
+       _cairo_scaled_font_thaw_cache (surface->scaled_font);
+       return status;
+    }
+
+    x_advance = scaled_glyph->metrics.x_advance;
+    y_advance = scaled_glyph->metrics.y_advance;
+    font_matrix_inverse = surface->scaled_font->font_matrix;
+    status2 = cairo_matrix_invert (&font_matrix_inverse);
+
+    /* The invertability of font_matrix is tested in
+     * pdf_operators_show_glyphs before any glyphs are mapped to the
+     * subset. */
+    assert (status2 == CAIRO_INT_STATUS_SUCCESS);
+
+    cairo_matrix_transform_distance (&font_matrix_inverse, &x_advance, &y_advance);
+    *width = x_advance;
+
+    *bbox = scaled_glyph->bbox;
+    _cairo_matrix_transform_bounding_box_fixed (&surface->scaled_font->scale_inverse,
+                                               bbox, NULL);
+
+    _cairo_output_stream_printf (surface->stream,
+                                "%f 0 %f %f %f %f d1\n",
+                                 x_advance,
+                                _cairo_fixed_to_double (bbox->p1.x),
+                                - _cairo_fixed_to_double (bbox->p2.y),
+                                _cairo_fixed_to_double (bbox->p2.x),
+                                - _cairo_fixed_to_double (bbox->p1.y));
+
+    if (status == CAIRO_INT_STATUS_SUCCESS) {
+       cairo_output_stream_t *mem_stream;
+
+       mem_stream = _cairo_memory_stream_create ();
+       status = mem_stream->status;
+       if (unlikely (status))
+           goto FAIL;
+
+       _cairo_type3_glyph_surface_set_stream (surface, mem_stream);
+
+       _cairo_output_stream_printf (surface->stream, "q\n");
+       status = _cairo_recording_surface_replay (scaled_glyph->recording_surface,
+                                                 &surface->base);
+
+       status2 = _cairo_pdf_operators_flush (&surface->pdf_operators);
+       if (status == CAIRO_INT_STATUS_SUCCESS)
+           status = status2;
+
+       _cairo_output_stream_printf (surface->stream, "Q\n");
+
+       _cairo_type3_glyph_surface_set_stream (surface, stream);
+       if (status == CAIRO_INT_STATUS_SUCCESS)
+           _cairo_memory_stream_copy (mem_stream, stream);
+
+       status2 = _cairo_output_stream_destroy (mem_stream);
+       if (status == CAIRO_INT_STATUS_SUCCESS)
+           status = status2;
+    }
+
+    if (status == CAIRO_INT_STATUS_IMAGE_FALLBACK)
+       status = _cairo_type3_glyph_surface_emit_fallback_image (surface, glyph_index);
+
+  FAIL:
+    _cairo_scaled_font_thaw_cache (surface->scaled_font);
+
+    return status;
+}
+
+#endif /* CAIRO_HAS_FONT_SUBSET */
diff --git a/src/cairo-types-private.h b/src/cairo-types-private.h
new file mode 100644 (file)
index 0000000..2fdd0a1
--- /dev/null
@@ -0,0 +1,428 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *     Carl D. Worth <cworth@cworth.org>
+ */
+
+#ifndef CAIRO_TYPES_PRIVATE_H
+#define CAIRO_TYPES_PRIVATE_H
+
+#include "cairo.h"
+#include "cairo-fixed-type-private.h"
+#include "cairo-list-private.h"
+#include "cairo-reference-count-private.h"
+
+CAIRO_BEGIN_DECLS
+
+/**
+ * SECTION:cairo-types
+ * @Title: Types
+ * @Short_Description: Generic data types
+ *
+ * This section lists generic data types used in the cairo API.
+ */
+
+typedef struct _cairo_array cairo_array_t;
+typedef struct _cairo_backend cairo_backend_t;
+typedef struct _cairo_boxes_t cairo_boxes_t;
+typedef struct _cairo_cache cairo_cache_t;
+typedef struct _cairo_composite_rectangles cairo_composite_rectangles_t;
+typedef struct _cairo_clip cairo_clip_t;
+typedef struct _cairo_clip_path cairo_clip_path_t;
+typedef struct _cairo_color cairo_color_t;
+typedef struct _cairo_color_stop cairo_color_stop_t;
+typedef struct _cairo_contour cairo_contour_t;
+typedef struct _cairo_contour_chain cairo_contour_chain_t;
+typedef struct _cairo_contour_iter cairo_contour_iter_t;
+typedef struct _cairo_device_backend cairo_device_backend_t;
+typedef struct _cairo_font_face_backend     cairo_font_face_backend_t;
+typedef struct _cairo_gstate cairo_gstate_t;
+typedef struct _cairo_gstate_backend cairo_gstate_backend_t;
+typedef struct _cairo_glyph_text_info cairo_glyph_text_info_t;
+typedef struct _cairo_hash_entry cairo_hash_entry_t;
+typedef struct _cairo_hash_table cairo_hash_table_t;
+typedef struct _cairo_image_surface cairo_image_surface_t;
+typedef struct _cairo_mime_data cairo_mime_data_t;
+typedef struct _cairo_observer cairo_observer_t;
+typedef struct _cairo_output_stream cairo_output_stream_t;
+typedef struct _cairo_paginated_surface_backend cairo_paginated_surface_backend_t;
+typedef struct _cairo_path_fixed cairo_path_fixed_t;
+typedef struct _cairo_rectangle_int16 cairo_glyph_size_t;
+typedef struct _cairo_scaled_font_subsets cairo_scaled_font_subsets_t;
+typedef struct _cairo_solid_pattern cairo_solid_pattern_t;
+typedef struct _cairo_surface_attributes cairo_surface_attributes_t;
+typedef struct _cairo_surface_backend cairo_surface_backend_t;
+typedef struct _cairo_surface_observer cairo_surface_observer_t;
+typedef struct _cairo_surface_snapshot cairo_surface_snapshot_t;
+typedef struct _cairo_surface_subsurface cairo_surface_subsurface_t;
+typedef struct _cairo_surface_wrapper cairo_surface_wrapper_t;
+typedef struct _cairo_traps cairo_traps_t;
+typedef struct _cairo_tristrip cairo_tristrip_t;
+typedef struct _cairo_unscaled_font_backend cairo_unscaled_font_backend_t;
+typedef struct _cairo_xlib_screen_info cairo_xlib_screen_info_t;
+
+typedef cairo_array_t cairo_user_data_array_t;
+
+typedef struct _cairo_scaled_font_private cairo_scaled_font_private_t;
+typedef struct _cairo_scaled_font_backend   cairo_scaled_font_backend_t;
+typedef struct _cairo_scaled_glyph cairo_scaled_glyph_t;
+typedef struct _cairo_scaled_glyph_private cairo_scaled_glyph_private_t;
+
+typedef struct cairo_compositor cairo_compositor_t;
+typedef struct cairo_fallback_compositor cairo_fallback_compositor_t;
+typedef struct cairo_mask_compositor cairo_mask_compositor_t;
+typedef struct cairo_traps_compositor cairo_traps_compositor_t;
+typedef struct cairo_spans_compositor cairo_spans_compositor_t;
+
+struct _cairo_observer {
+    cairo_list_t link;
+    void (*callback) (cairo_observer_t *self, void *arg);
+};
+
+/**
+ * cairo_hash_entry_t:
+ *
+ * A #cairo_hash_entry_t contains both a key and a value for
+ * #cairo_hash_table_t. User-derived types for #cairo_hash_entry_t must
+ * be type-compatible with this structure (eg. they must have an
+ * unsigned long as the first parameter. The easiest way to get this
+ * is to use:
+ *
+ *     typedef _my_entry {
+ *         cairo_hash_entry_t base;
+ *         ... Remainder of key and value fields here ..
+ *     } my_entry_t;
+ *
+ * which then allows a pointer to my_entry_t to be passed to any of
+ * the #cairo_hash_table_t functions as follows without requiring a cast:
+ *
+ *     _cairo_hash_table_insert (hash_table, &my_entry->base);
+ *
+ * IMPORTANT: The caller is reponsible for initializing
+ * my_entry->base.hash with a hash code derived from the key. The
+ * essential property of the hash code is that keys_equal must never
+ * return %TRUE for two keys that have different hashes. The best hash
+ * code will reduce the frequency of two keys with the same code for
+ * which keys_equal returns %FALSE.
+ *
+ * Which parts of the entry make up the "key" and which part make up
+ * the value are entirely up to the caller, (as determined by the
+ * computation going into base.hash as well as the keys_equal
+ * function). A few of the #cairo_hash_table_t functions accept an entry
+ * which will be used exclusively as a "key", (indicated by a
+ * parameter name of key). In these cases, the value-related fields of
+ * the entry need not be initialized if so desired.
+ **/
+struct _cairo_hash_entry {
+    unsigned long hash;
+};
+
+struct _cairo_array {
+    unsigned int size;
+    unsigned int num_elements;
+    unsigned int element_size;
+    char *elements;
+};
+
+/**
+ * cairo_lcd_filter_t:
+ * @CAIRO_LCD_FILTER_DEFAULT: Use the default LCD filter for
+ *   font backend and target device
+ * @CAIRO_LCD_FILTER_NONE: Do not perform LCD filtering
+ * @CAIRO_LCD_FILTER_INTRA_PIXEL: Intra-pixel filter
+ * @CAIRO_LCD_FILTER_FIR3: FIR filter with a 3x3 kernel
+ * @CAIRO_LCD_FILTER_FIR5: FIR filter with a 5x5 kernel
+ *
+ * The LCD filter specifies the low-pass filter applied to LCD-optimized
+ * bitmaps generated with an antialiasing mode of %CAIRO_ANTIALIAS_SUBPIXEL.
+ *
+ * Note: This API was temporarily made available in the public
+ * interface during the 1.7.x development series, but was made private
+ * before 1.8.
+ **/
+typedef enum _cairo_lcd_filter {
+    CAIRO_LCD_FILTER_DEFAULT,
+    CAIRO_LCD_FILTER_NONE,
+    CAIRO_LCD_FILTER_INTRA_PIXEL,
+    CAIRO_LCD_FILTER_FIR3,
+    CAIRO_LCD_FILTER_FIR5
+} cairo_lcd_filter_t;
+
+typedef enum _cairo_round_glyph_positions {
+    CAIRO_ROUND_GLYPH_POS_DEFAULT,
+    CAIRO_ROUND_GLYPH_POS_ON,
+    CAIRO_ROUND_GLYPH_POS_OFF
+} cairo_round_glyph_positions_t;
+
+struct _cairo_font_options {
+    cairo_antialias_t antialias;
+    cairo_subpixel_order_t subpixel_order;
+    cairo_lcd_filter_t lcd_filter;
+    cairo_hint_style_t hint_style;
+    cairo_hint_metrics_t hint_metrics;
+    cairo_round_glyph_positions_t round_glyph_positions;
+};
+
+struct _cairo_glyph_text_info {
+    const char *utf8;
+    int utf8_len;
+
+    const cairo_text_cluster_t *clusters;
+    int num_clusters;
+    cairo_text_cluster_flags_t cluster_flags;
+};
+
+
+/* XXX: Right now, the _cairo_color structure puts unpremultiplied
+   color in the doubles and premultiplied color in the shorts. Yes,
+   this is crazy insane, (but at least we don't export this
+   madness). I'm still working on a cleaner API, but in the meantime,
+   at least this does prevent precision loss in color when changing
+   alpha. */
+struct _cairo_color {
+    double red;
+    double green;
+    double blue;
+    double alpha;
+
+    unsigned short red_short;
+    unsigned short green_short;
+    unsigned short blue_short;
+    unsigned short alpha_short;
+};
+
+struct _cairo_color_stop {
+    /* unpremultiplied */
+    double red;
+    double green;
+    double blue;
+    double alpha;
+
+    /* unpremultipled, for convenience */
+    uint16_t red_short;
+    uint16_t green_short;
+    uint16_t blue_short;
+    uint16_t alpha_short;
+};
+
+typedef enum _cairo_paginated_mode {
+    CAIRO_PAGINATED_MODE_ANALYZE,      /* analyze page regions */
+    CAIRO_PAGINATED_MODE_RENDER,       /* render page contents */
+    CAIRO_PAGINATED_MODE_FALLBACK      /* paint fallback images */
+} cairo_paginated_mode_t;
+
+typedef enum _cairo_internal_surface_type {
+    CAIRO_INTERNAL_SURFACE_TYPE_SNAPSHOT = 0x1000,
+    CAIRO_INTERNAL_SURFACE_TYPE_PAGINATED,
+    CAIRO_INTERNAL_SURFACE_TYPE_ANALYSIS,
+    CAIRO_INTERNAL_SURFACE_TYPE_OBSERVER,
+    CAIRO_INTERNAL_SURFACE_TYPE_TEST_FALLBACK,
+    CAIRO_INTERNAL_SURFACE_TYPE_TEST_PAGINATED,
+    CAIRO_INTERNAL_SURFACE_TYPE_TEST_WRAPPING,
+    CAIRO_INTERNAL_SURFACE_TYPE_NULL,
+    CAIRO_INTERNAL_SURFACE_TYPE_TYPE3_GLYPH
+} cairo_internal_surface_type_t;
+
+typedef enum _cairo_internal_device_type {
+    CAIRO_INTERNAL_DEVICE_TYPE_OBSERVER = 0x1000,
+} cairo_device_surface_type_t;
+
+#define CAIRO_HAS_TEST_PAGINATED_SURFACE 1
+
+typedef struct _cairo_slope {
+    cairo_fixed_t dx;
+    cairo_fixed_t dy;
+} cairo_slope_t, cairo_distance_t;
+
+typedef struct _cairo_point_double {
+    double x;
+    double y;
+} cairo_point_double_t;
+
+typedef struct _cairo_circle_double {
+    cairo_point_double_t center;
+    double               radius;
+} cairo_circle_double_t;
+
+typedef struct _cairo_distance_double {
+    double dx;
+    double dy;
+} cairo_distance_double_t;
+
+typedef struct _cairo_box_double {
+    cairo_point_double_t p1;
+    cairo_point_double_t p2;
+} cairo_box_double_t;
+
+typedef struct _cairo_line {
+    cairo_point_t p1;
+    cairo_point_t p2;
+} cairo_line_t, cairo_box_t;
+
+typedef struct _cairo_trapezoid {
+    cairo_fixed_t top, bottom;
+    cairo_line_t left, right;
+} cairo_trapezoid_t;
+
+typedef struct _cairo_point_int {
+    int x, y;
+} cairo_point_int_t;
+
+#define CAIRO_RECT_INT_MIN (INT_MIN >> CAIRO_FIXED_FRAC_BITS)
+#define CAIRO_RECT_INT_MAX (INT_MAX >> CAIRO_FIXED_FRAC_BITS)
+
+typedef enum _cairo_direction {
+    CAIRO_DIRECTION_FORWARD,
+    CAIRO_DIRECTION_REVERSE
+} cairo_direction_t;
+
+typedef struct _cairo_edge {
+    cairo_line_t line;
+    int top, bottom;
+    int dir;
+} cairo_edge_t;
+
+typedef struct _cairo_polygon {
+    cairo_status_t status;
+
+    cairo_box_t extents;
+    cairo_box_t limit;
+    const cairo_box_t *limits;
+    int num_limits;
+
+    int num_edges;
+    int edges_size;
+    cairo_edge_t *edges;
+    cairo_edge_t  edges_embedded[32];
+} cairo_polygon_t;
+
+typedef cairo_warn cairo_status_t
+(*cairo_spline_add_point_func_t) (void *closure,
+                                 const cairo_point_t *point,
+                                 const cairo_slope_t *tangent);
+
+typedef struct _cairo_spline_knots {
+    cairo_point_t a, b, c, d;
+} cairo_spline_knots_t;
+
+typedef struct _cairo_spline {
+    cairo_spline_add_point_func_t add_point_func;
+    void *closure;
+
+    cairo_spline_knots_t knots;
+
+    cairo_slope_t initial_slope;
+    cairo_slope_t final_slope;
+
+    cairo_bool_t has_point;
+    cairo_point_t last_point;
+} cairo_spline_t;
+
+typedef struct _cairo_pen_vertex {
+    cairo_point_t point;
+
+    cairo_slope_t slope_ccw;
+    cairo_slope_t slope_cw;
+} cairo_pen_vertex_t;
+
+typedef struct _cairo_pen {
+    double radius;
+    double tolerance;
+
+    int num_vertices;
+    cairo_pen_vertex_t *vertices;
+    cairo_pen_vertex_t  vertices_embedded[32];
+} cairo_pen_t;
+
+typedef struct _cairo_stroke_style {
+    double              line_width;
+    cairo_line_cap_t    line_cap;
+    cairo_line_join_t   line_join;
+    double              miter_limit;
+    double             *dash;
+    unsigned int        num_dashes;
+    double              dash_offset;
+} cairo_stroke_style_t;
+
+typedef struct _cairo_format_masks {
+    int bpp;
+    unsigned long alpha_mask;
+    unsigned long red_mask;
+    unsigned long green_mask;
+    unsigned long blue_mask;
+} cairo_format_masks_t;
+
+typedef enum {
+    CAIRO_STOCK_WHITE,
+    CAIRO_STOCK_BLACK,
+    CAIRO_STOCK_TRANSPARENT,
+    CAIRO_STOCK_NUM_COLORS,
+} cairo_stock_t;
+
+typedef enum _cairo_image_transparency {
+    CAIRO_IMAGE_IS_OPAQUE,
+    CAIRO_IMAGE_HAS_BILEVEL_ALPHA,
+    CAIRO_IMAGE_HAS_ALPHA,
+    CAIRO_IMAGE_UNKNOWN
+} cairo_image_transparency_t;
+
+typedef enum _cairo_image_color {
+    CAIRO_IMAGE_IS_COLOR,
+    CAIRO_IMAGE_IS_GRAYSCALE,
+    CAIRO_IMAGE_IS_MONOCHROME,
+    CAIRO_IMAGE_UNKNOWN_COLOR
+} cairo_image_color_t;
+
+
+struct _cairo_mime_data {
+    cairo_reference_count_t ref_count;
+    unsigned char *data;
+    unsigned long length;
+    cairo_destroy_func_t destroy;
+    void *closure;
+};
+
+/*
+ * A #cairo_unscaled_font_t is just an opaque handle we use in the
+ * glyph cache.
+ */
+typedef struct _cairo_unscaled_font {
+    cairo_hash_entry_t                  hash_entry;
+    cairo_reference_count_t             ref_count;
+    const cairo_unscaled_font_backend_t        *backend;
+} cairo_unscaled_font_t;
+CAIRO_END_DECLS
+
+#endif /* CAIRO_TYPES_PRIVATE_H */
diff --git a/src/cairo-unicode.c b/src/cairo-unicode.c
new file mode 100644 (file)
index 0000000..88de395
--- /dev/null
@@ -0,0 +1,422 @@
+/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * The code in this file is derived from GLib's gutf8.c and
+ *   ultimately from libunicode. It is relicensed under the
+ *   dual LGPL/MPL with permission of the original authors.
+ *
+ * Copyright © 1999 Tom Tromey
+ * Copyright © 2005 Red Hat, Inc
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Tom Tromey.
+ *  and Red Hat, Inc.
+ *
+ * Contributor(s):
+ *     Owen Taylor <otaylor@redhat.com>
+ */
+
+#include "cairoint.h"
+#include "cairo-error-private.h"
+
+#define UTF8_COMPUTE(Char, Mask, Len)                                        \
+  if (Char < 128)                                                            \
+    {                                                                        \
+      Len = 1;                                                               \
+      Mask = 0x7f;                                                           \
+    }                                                                        \
+  else if ((Char & 0xe0) == 0xc0)                                            \
+    {                                                                        \
+      Len = 2;                                                               \
+      Mask = 0x1f;                                                           \
+    }                                                                        \
+  else if ((Char & 0xf0) == 0xe0)                                            \
+    {                                                                        \
+      Len = 3;                                                               \
+      Mask = 0x0f;                                                           \
+    }                                                                        \
+  else if ((Char & 0xf8) == 0xf0)                                            \
+    {                                                                        \
+      Len = 4;                                                               \
+      Mask = 0x07;                                                           \
+    }                                                                        \
+  else if ((Char & 0xfc) == 0xf8)                                            \
+    {                                                                        \
+      Len = 5;                                                               \
+      Mask = 0x03;                                                           \
+    }                                                                        \
+  else if ((Char & 0xfe) == 0xfc)                                            \
+    {                                                                        \
+      Len = 6;                                                               \
+      Mask = 0x01;                                                           \
+    }                                                                        \
+  else                                                                       \
+    Len = -1;
+
+#define UTF8_LENGTH(Char)              \
+  ((Char) < 0x80 ? 1 :                 \
+   ((Char) < 0x800 ? 2 :               \
+    ((Char) < 0x10000 ? 3 :            \
+     ((Char) < 0x200000 ? 4 :          \
+      ((Char) < 0x4000000 ? 5 : 6)))))
+
+#define UTF8_GET(Result, Chars, Count, Mask, Len)                            \
+  (Result) = (Chars)[0] & (Mask);                                            \
+  for ((Count) = 1; (Count) < (Len); ++(Count))                                      \
+    {                                                                        \
+      if (((Chars)[(Count)] & 0xc0) != 0x80)                                 \
+       {                                                                     \
+         (Result) = -1;                                                      \
+         break;                                                              \
+       }                                                                     \
+      (Result) <<= 6;                                                        \
+      (Result) |= ((Chars)[(Count)] & 0x3f);                                 \
+    }
+
+#define UNICODE_VALID(Char)                   \
+    ((Char) < 0x110000 &&                     \
+     (((Char) & 0xFFFFF800) != 0xD800) &&     \
+     ((Char) < 0xFDD0 || (Char) > 0xFDEF) &&  \
+     ((Char) & 0xFFFE) != 0xFFFE)
+
+static const char utf8_skip_data[256] = {
+    1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+    1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+    1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+    1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+    1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+    1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+    2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+    3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,6,6,1,1
+};
+
+#define UTF8_NEXT_CHAR(p) ((p) + utf8_skip_data[*(unsigned char *)(p)])
+
+/* Converts a sequence of bytes encoded as UTF-8 to a Unicode character.
+ * If @p does not point to a valid UTF-8 encoded character, results are
+ * undefined.
+ **/
+static uint32_t
+_utf8_get_char (const unsigned char *p)
+{
+    int i, mask = 0, len;
+    uint32_t result;
+    unsigned char c = (unsigned char) *p;
+
+    UTF8_COMPUTE (c, mask, len);
+    if (len == -1)
+       return (uint32_t)-1;
+    UTF8_GET (result, p, i, mask, len);
+
+    return result;
+}
+
+/* Like _utf8_get_char, but take a maximum length
+ * and return (uint32_t)-2 on incomplete trailing character
+ */
+static uint32_t
+_utf8_get_char_extended (const unsigned char *p,
+                        long                 max_len)
+{
+    int i, len;
+    uint32_t wc = (unsigned char) *p;
+
+    if (wc < 0x80) {
+       return wc;
+    } else if (wc < 0xc0) {
+       return (uint32_t)-1;
+    } else if (wc < 0xe0) {
+       len = 2;
+       wc &= 0x1f;
+    } else if (wc < 0xf0) {
+       len = 3;
+       wc &= 0x0f;
+    } else if (wc < 0xf8) {
+       len = 4;
+       wc &= 0x07;
+    } else if (wc < 0xfc) {
+       len = 5;
+       wc &= 0x03;
+    } else if (wc < 0xfe) {
+       len = 6;
+       wc &= 0x01;
+    } else {
+       return (uint32_t)-1;
+    }
+
+    if (max_len >= 0 && len > max_len) {
+       for (i = 1; i < max_len; i++) {
+           if ((((unsigned char *)p)[i] & 0xc0) != 0x80)
+               return (uint32_t)-1;
+       }
+       return (uint32_t)-2;
+    }
+
+    for (i = 1; i < len; ++i) {
+       uint32_t ch = ((unsigned char *)p)[i];
+
+       if ((ch & 0xc0) != 0x80) {
+           if (ch)
+               return (uint32_t)-1;
+           else
+               return (uint32_t)-2;
+       }
+
+       wc <<= 6;
+       wc |= (ch & 0x3f);
+    }
+
+    if (UTF8_LENGTH(wc) != len)
+       return (uint32_t)-1;
+
+    return wc;
+}
+
+/**
+ * _cairo_utf8_get_char_validated:
+ * @p: a UTF-8 string
+ * @unicode: location to store one Unicode character
+ *
+ * Decodes the first character of a valid UTF-8 string, and returns
+ * the number of bytes consumed.
+ *
+ * Note that the string should be valid.  Do not use this without
+ * validating the string first.
+ *
+ * Returns: the number of bytes forming the character returned.
+ **/
+int
+_cairo_utf8_get_char_validated (const char *p,
+                               uint32_t   *unicode)
+{
+    int i, mask = 0, len;
+    uint32_t result;
+    unsigned char c = (unsigned char) *p;
+
+    UTF8_COMPUTE (c, mask, len);
+    if (len == -1) {
+       if (unicode)
+           *unicode = (uint32_t)-1;
+       return 1;
+    }
+    UTF8_GET (result, p, i, mask, len);
+
+    if (unicode)
+       *unicode = result;
+    return len;
+}
+
+/**
+ * _cairo_utf8_to_ucs4:
+ * @str: an UTF-8 string
+ * @len: length of @str in bytes, or -1 if it is nul-terminated.
+ *   If @len is supplied and the string has an embedded nul
+ *   byte, only the portion before the nul byte is converted.
+ * @result: location to store a pointer to a newly allocated UTF-32
+ *   string (always native endian), or %NULL. Free with free(). A 0
+ *   word will be written after the last character.
+ * @items_written: location to store number of 32-bit words
+ *   written. (Not including the trailing 0)
+ *
+ * Converts a UTF-8 string to UCS-4. UCS-4 is an encoding of Unicode
+ * with 1 32-bit word per character. The string is validated to
+ * consist entirely of valid Unicode characters.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS if the entire string was
+ *   successfully converted. %CAIRO_STATUS_INVALID_STRING if an
+ *   invalid sequence was found.
+ **/
+cairo_status_t
+_cairo_utf8_to_ucs4 (const char *str,
+                    int         len,
+                    uint32_t  **result,
+                    int        *items_written)
+{
+    uint32_t *str32 = NULL;
+    int n_chars, i;
+    const unsigned char *in;
+    const unsigned char * const ustr = (const unsigned char *) str;
+
+    in = ustr;
+    n_chars = 0;
+    while ((len < 0 || ustr + len - in > 0) && *in)
+    {
+       uint32_t wc = _utf8_get_char_extended (in, ustr + len - in);
+       if (wc & 0x80000000 || !UNICODE_VALID (wc))
+           return _cairo_error (CAIRO_STATUS_INVALID_STRING);
+
+       n_chars++;
+       if (n_chars == INT_MAX)
+           return _cairo_error (CAIRO_STATUS_INVALID_STRING);
+
+       in = UTF8_NEXT_CHAR (in);
+    }
+
+    if (result) {
+       str32 = _cairo_malloc_ab (n_chars + 1, sizeof (uint32_t));
+       if (!str32)
+           return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+       in = ustr;
+       for (i=0; i < n_chars; i++) {
+           str32[i] = _utf8_get_char (in);
+           in = UTF8_NEXT_CHAR (in);
+       }
+       str32[i] = 0;
+
+       *result = str32;
+    }
+
+    if (items_written)
+       *items_written = n_chars;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+/**
+ * _cairo_ucs4_to_utf8:
+ * @unicode: a UCS-4 character
+ * @utf8: buffer to write utf8 string into. Must have at least 4 bytes
+ * space available. Or %NULL.
+ *
+ * This space left intentionally blank.
+ *
+ * Return value: Number of bytes in the utf8 string or 0 if an invalid
+ * unicode character
+ **/
+int
+_cairo_ucs4_to_utf8 (uint32_t  unicode,
+                    char     *utf8)
+{
+    int bytes;
+    char *p;
+
+    if (unicode < 0x80) {
+       if (utf8)
+           *utf8 = unicode;
+       return 1;
+    } else if (unicode < 0x800) {
+       bytes = 2;
+    } else if (unicode < 0x10000) {
+       bytes = 3;
+    } else if (unicode < 0x200000) {
+       bytes = 4;
+    } else {
+       return 0;
+    }
+
+    if (!utf8)
+       return bytes;
+
+    p = utf8 + bytes;
+    while (p > utf8) {
+       *--p = 0x80 | (unicode & 0x3f);
+       unicode >>= 6;
+    }
+    *p |= 0xf0 << (4 - bytes);
+
+    return bytes;
+}
+
+#if CAIRO_HAS_UTF8_TO_UTF16
+/**
+ * _cairo_utf8_to_utf16:
+ * @str: an UTF-8 string
+ * @len: length of @str in bytes, or -1 if it is nul-terminated.
+ *   If @len is supplied and the string has an embedded nul
+ *   byte, only the portion before the nul byte is converted.
+ * @result: location to store a pointer to a newly allocated UTF-16
+ *   string (always native endian). Free with free(). A 0
+ *   word will be written after the last character.
+ * @items_written: location to store number of 16-bit words
+ *   written. (Not including the trailing 0)
+ *
+ * Converts a UTF-8 string to UTF-16. UTF-16 is an encoding of Unicode
+ * where characters are represented either as a single 16-bit word, or
+ * as a pair of 16-bit "surrogates". The string is validated to
+ * consist entirely of valid Unicode characters.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS if the entire string was
+ *   successfully converted. %CAIRO_STATUS_INVALID_STRING if an
+ *   an invalid sequence was found.
+ **/
+cairo_status_t
+_cairo_utf8_to_utf16 (const char *str,
+                     int         len,
+                     uint16_t **result,
+                     int       *items_written)
+{
+    uint16_t *str16 = NULL;
+    int n16, i;
+    const unsigned char *in;
+    const unsigned char * const ustr = (const unsigned char *) str;
+
+    in = ustr;
+    n16 = 0;
+    while ((len < 0 || ustr + len - in > 0) && *in) {
+       uint32_t wc = _utf8_get_char_extended (in, ustr + len - in);
+       if (wc & 0x80000000 || !UNICODE_VALID (wc))
+           return _cairo_error (CAIRO_STATUS_INVALID_STRING);
+
+       if (wc < 0x10000)
+           n16 += 1;
+       else
+           n16 += 2;
+
+       if (n16 == INT_MAX - 1 || n16 == INT_MAX)
+           return _cairo_error (CAIRO_STATUS_INVALID_STRING);
+
+       in = UTF8_NEXT_CHAR (in);
+    }
+
+    str16 = _cairo_malloc_ab (n16 + 1, sizeof (uint16_t));
+    if (!str16)
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    in = ustr;
+    for (i = 0; i < n16;) {
+       uint32_t wc = _utf8_get_char (in);
+
+       if (wc < 0x10000) {
+           str16[i++] = wc;
+       } else {
+           str16[i++] = (wc - 0x10000) / 0x400 + 0xd800;
+           str16[i++] = (wc - 0x10000) % 0x400 + 0xdc00;
+       }
+
+       in = UTF8_NEXT_CHAR (in);
+    }
+
+    str16[i] = 0;
+
+    *result = str16;
+    if (items_written)
+       *items_written = n16;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+#endif
diff --git a/src/cairo-uninstalled.pc.in b/src/cairo-uninstalled.pc.in
new file mode 100644 (file)
index 0000000..9dc3231
--- /dev/null
@@ -0,0 +1,8 @@
+Name: cairo
+Description: Multi-platform 2D graphics library
+Version: @VERSION@
+
+@PKGCONFIG_REQUIRES@: @CAIRO_REQUIRES@
+Libs: ${pc_top_builddir}/${pcfiledir}/src/libcairo.la
+Libs.private: @CAIRO_NONPKGCONFIG_LIBS@
+Cflags: -I${pc_top_builddir}/${pcfiledir}/@srcdir@/src
diff --git a/src/cairo-user-font-private.h b/src/cairo-user-font-private.h
new file mode 100644 (file)
index 0000000..d54ef78
--- /dev/null
@@ -0,0 +1,46 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2006, 2008 Red Hat, Inc
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ *      Kristian Høgsberg <krh@redhat.com>
+ *      Behdad Esfahbod <behdad@behdad.org>
+ */
+
+#ifndef CAIRO_USER_FONT_PRIVATE_H
+#define CAIRO_USER_FONT_PRIVATE_H
+
+#include "cairo.h"
+#include "cairo-compiler-private.h"
+
+cairo_private cairo_bool_t
+_cairo_font_face_is_user (cairo_font_face_t *font_face);
+
+#endif /* CAIRO_USER_FONT_PRIVATE_H */
diff --git a/src/cairo-user-font.c b/src/cairo-user-font.c
new file mode 100644 (file)
index 0000000..1b507fc
--- /dev/null
@@ -0,0 +1,831 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2006, 2008 Red Hat, Inc
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ *      Kristian Høgsberg <krh@redhat.com>
+ *      Behdad Esfahbod <behdad@behdad.org>
+ */
+
+#include "cairoint.h"
+#include "cairo-user-font-private.h"
+#include "cairo-recording-surface-private.h"
+#include "cairo-analysis-surface-private.h"
+#include "cairo-error-private.h"
+
+/**
+ * SECTION:cairo-user-fonts
+ * @Title:User Fonts
+ * @Short_Description: Font support with font data provided by the user
+ * 
+ * The user-font feature allows the cairo user to provide drawings for glyphs
+ * in a font.  This is most useful in implementing fonts in non-standard
+ * formats, like SVG fonts and Flash fonts, but can also be used by games and
+ * other application to draw "funky" fonts.
+ */
+
+/**
+ * CAIRO_HAS_USER_FONT:
+ *
+ * Defined if the user font backend is available.
+ * This macro can be used to conditionally compile backend-specific code.
+ * The user font backend is always built in versions of cairo that support
+ * this feature (1.8 and later).
+ *
+ * @Since: 1.8
+ */
+
+typedef struct _cairo_user_scaled_font_methods {
+    cairo_user_scaled_font_init_func_t                 init;
+    cairo_user_scaled_font_render_glyph_func_t         render_glyph;
+    cairo_user_scaled_font_unicode_to_glyph_func_t     unicode_to_glyph;
+    cairo_user_scaled_font_text_to_glyphs_func_t       text_to_glyphs;
+} cairo_user_scaled_font_methods_t;
+
+typedef struct _cairo_user_font_face {
+    cairo_font_face_t               base;
+
+    /* Set to true after first scaled font is created.  At that point,
+     * the scaled_font_methods cannot change anymore. */
+    cairo_bool_t                    immutable;
+
+    cairo_user_scaled_font_methods_t scaled_font_methods;
+} cairo_user_font_face_t;
+
+typedef struct _cairo_user_scaled_font {
+    cairo_scaled_font_t  base;
+
+    cairo_text_extents_t default_glyph_extents;
+
+    /* space to compute extents in, and factors to convert back to user space */
+    cairo_matrix_t extent_scale;
+    double extent_x_scale;
+    double extent_y_scale;
+
+    /* multiplier for metrics hinting */
+    double snap_x_scale;
+    double snap_y_scale;
+
+} cairo_user_scaled_font_t;
+
+/* #cairo_user_scaled_font_t */
+
+static cairo_surface_t *
+_cairo_user_scaled_font_create_recording_surface (const cairo_user_scaled_font_t *scaled_font)
+{
+    cairo_content_t content;
+
+    content = scaled_font->base.options.antialias == CAIRO_ANTIALIAS_SUBPIXEL ?
+                                                    CAIRO_CONTENT_COLOR_ALPHA :
+                                                    CAIRO_CONTENT_ALPHA;
+
+    return cairo_recording_surface_create (content, NULL);
+}
+
+
+static cairo_t *
+_cairo_user_scaled_font_create_recording_context (const cairo_user_scaled_font_t *scaled_font,
+                                                 cairo_surface_t                *recording_surface)
+{
+    cairo_t *cr;
+
+    cr = cairo_create (recording_surface);
+
+    if (!_cairo_matrix_is_scale_0 (&scaled_font->base.scale)) {
+        cairo_matrix_t scale;
+       scale = scaled_font->base.scale;
+       scale.x0 = scale.y0 = 0.;
+       cairo_set_matrix (cr, &scale);
+    }
+
+    cairo_set_font_size (cr, 1.0);
+    cairo_set_font_options (cr, &scaled_font->base.options);
+    cairo_set_source_rgb (cr, 1., 1., 1.);
+
+    return cr;
+}
+
+static cairo_int_status_t
+_cairo_user_scaled_glyph_init (void                     *abstract_font,
+                              cairo_scaled_glyph_t      *scaled_glyph,
+                              cairo_scaled_glyph_info_t  info)
+{
+    cairo_int_status_t status = CAIRO_STATUS_SUCCESS;
+    cairo_user_scaled_font_t *scaled_font = abstract_font;
+    cairo_surface_t *recording_surface = scaled_glyph->recording_surface;
+
+    if (!scaled_glyph->recording_surface) {
+       cairo_user_font_face_t *face =
+           (cairo_user_font_face_t *) scaled_font->base.font_face;
+       cairo_text_extents_t extents = scaled_font->default_glyph_extents;
+       cairo_t *cr;
+
+       if (!face->scaled_font_methods.render_glyph)
+           return CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED;
+
+       recording_surface = _cairo_user_scaled_font_create_recording_surface (scaled_font);
+
+       /* special case for 0 rank matrix (as in _cairo_scaled_font_init): empty surface */
+        if (!_cairo_matrix_is_scale_0 (&scaled_font->base.scale)) {
+           cr = _cairo_user_scaled_font_create_recording_context (scaled_font, recording_surface);
+           status = face->scaled_font_methods.render_glyph ((cairo_scaled_font_t *)scaled_font,
+                                                            _cairo_scaled_glyph_index(scaled_glyph),
+                                                            cr, &extents);
+           if (status == CAIRO_INT_STATUS_SUCCESS)
+               status = cairo_status (cr);
+
+           cairo_destroy (cr);
+
+           if (unlikely (status)) {
+               cairo_surface_destroy (recording_surface);
+               return status;
+           }
+       }
+
+       _cairo_scaled_glyph_set_recording_surface (scaled_glyph,
+                                                  &scaled_font->base,
+                                                  recording_surface);
+
+
+       /* set metrics */
+
+       if (extents.width == 0.) {
+           cairo_box_t bbox;
+           double x1, y1, x2, y2;
+           double x_scale, y_scale;
+
+           /* Compute extents.x/y/width/height from recording_surface,
+            * in font space.
+            */
+           status = _cairo_recording_surface_get_bbox ((cairo_recording_surface_t *) recording_surface,
+                                                       &bbox,
+                                                       &scaled_font->extent_scale);
+           if (unlikely (status))
+               return status;
+
+           _cairo_box_to_doubles (&bbox, &x1, &y1, &x2, &y2);
+
+           x_scale = scaled_font->extent_x_scale;
+           y_scale = scaled_font->extent_y_scale;
+           extents.x_bearing = x1 * x_scale;
+           extents.y_bearing = y1 * y_scale;
+           extents.width     = (x2 - x1) * x_scale;
+           extents.height    = (y2 - y1) * y_scale;
+       }
+
+       if (scaled_font->base.options.hint_metrics != CAIRO_HINT_METRICS_OFF) {
+           extents.x_advance = _cairo_lround (extents.x_advance / scaled_font->snap_x_scale) * scaled_font->snap_x_scale;
+           extents.y_advance = _cairo_lround (extents.y_advance / scaled_font->snap_y_scale) * scaled_font->snap_y_scale;
+       }
+
+       _cairo_scaled_glyph_set_metrics (scaled_glyph,
+                                        &scaled_font->base,
+                                        &extents);
+    }
+
+    if (info & CAIRO_SCALED_GLYPH_INFO_SURFACE) {
+       cairo_surface_t *surface;
+       cairo_format_t format;
+       int width, height;
+
+       /* TODO
+        * extend the glyph cache to support argb glyphs.
+        * need to figure out the semantics and interaction with subpixel
+        * rendering first.
+        */
+
+       width = _cairo_fixed_integer_ceil (scaled_glyph->bbox.p2.x) -
+         _cairo_fixed_integer_floor (scaled_glyph->bbox.p1.x);
+       height = _cairo_fixed_integer_ceil (scaled_glyph->bbox.p2.y) -
+         _cairo_fixed_integer_floor (scaled_glyph->bbox.p1.y);
+
+       switch (scaled_font->base.options.antialias) {
+       default:
+       case CAIRO_ANTIALIAS_DEFAULT:
+       case CAIRO_ANTIALIAS_FAST:
+       case CAIRO_ANTIALIAS_GOOD:
+       case CAIRO_ANTIALIAS_GRAY:      format = CAIRO_FORMAT_A8;       break;
+       case CAIRO_ANTIALIAS_NONE:      format = CAIRO_FORMAT_A1;       break;
+       case CAIRO_ANTIALIAS_BEST:
+       case CAIRO_ANTIALIAS_SUBPIXEL:  format = CAIRO_FORMAT_ARGB32;   break;
+       }
+       surface = cairo_image_surface_create (format, width, height);
+
+       cairo_surface_set_device_offset (surface,
+                                        - _cairo_fixed_integer_floor (scaled_glyph->bbox.p1.x),
+                                        - _cairo_fixed_integer_floor (scaled_glyph->bbox.p1.y));
+       status = _cairo_recording_surface_replay (recording_surface, surface);
+
+       if (unlikely (status)) {
+           cairo_surface_destroy(surface);
+           return status;
+       }
+
+       _cairo_scaled_glyph_set_surface (scaled_glyph,
+                                        &scaled_font->base,
+                                        (cairo_image_surface_t *) surface);
+    }
+
+    if (info & CAIRO_SCALED_GLYPH_INFO_PATH) {
+       cairo_path_fixed_t *path = _cairo_path_fixed_create ();
+       if (!path)
+           return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+       status = _cairo_recording_surface_get_path (recording_surface, path);
+       if (unlikely (status)) {
+           _cairo_path_fixed_destroy (path);
+           return status;
+       }
+
+       _cairo_scaled_glyph_set_path (scaled_glyph,
+                                     &scaled_font->base,
+                                     path);
+    }
+
+    return status;
+}
+
+static unsigned long
+_cairo_user_ucs4_to_index (void            *abstract_font,
+                          uint32_t  ucs4)
+{
+    cairo_user_scaled_font_t *scaled_font = abstract_font;
+    cairo_user_font_face_t *face =
+       (cairo_user_font_face_t *) scaled_font->base.font_face;
+    unsigned long glyph = 0;
+
+    if (face->scaled_font_methods.unicode_to_glyph) {
+       cairo_status_t status;
+
+       status = face->scaled_font_methods.unicode_to_glyph (&scaled_font->base,
+                                                            ucs4, &glyph);
+
+       if (status == CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED)
+           goto not_implemented;
+
+       if (status != CAIRO_STATUS_SUCCESS) {
+           status = _cairo_scaled_font_set_error (&scaled_font->base, status);
+           glyph = 0;
+       }
+
+    } else {
+not_implemented:
+       glyph = ucs4;
+    }
+
+    return glyph;
+}
+
+static cairo_int_status_t
+_cairo_user_text_to_glyphs (void                     *abstract_font,
+                           double                     x,
+                           double                     y,
+                           const char                *utf8,
+                           int                        utf8_len,
+                           cairo_glyph_t            **glyphs,
+                           int                        *num_glyphs,
+                           cairo_text_cluster_t      **clusters,
+                           int                        *num_clusters,
+                           cairo_text_cluster_flags_t *cluster_flags)
+{
+    cairo_int_status_t status = CAIRO_INT_STATUS_UNSUPPORTED;
+
+    cairo_user_scaled_font_t *scaled_font = abstract_font;
+    cairo_user_font_face_t *face =
+       (cairo_user_font_face_t *) scaled_font->base.font_face;
+
+    if (face->scaled_font_methods.text_to_glyphs) {
+       int i;
+       cairo_glyph_t *orig_glyphs = *glyphs;
+       int orig_num_glyphs = *num_glyphs;
+
+       status = face->scaled_font_methods.text_to_glyphs (&scaled_font->base,
+                                                          utf8, utf8_len,
+                                                          glyphs, num_glyphs,
+                                                          clusters, num_clusters, cluster_flags);
+
+       if (status != CAIRO_INT_STATUS_SUCCESS &&
+           status != CAIRO_INT_STATUS_USER_FONT_NOT_IMPLEMENTED)
+           return status;
+
+       if (status == CAIRO_INT_STATUS_USER_FONT_NOT_IMPLEMENTED ||
+           *num_glyphs < 0) {
+           if (orig_glyphs != *glyphs) {
+               cairo_glyph_free (*glyphs);
+               *glyphs = orig_glyphs;
+           }
+           *num_glyphs = orig_num_glyphs;
+           return CAIRO_INT_STATUS_UNSUPPORTED;
+       }
+
+       /* Convert from font space to user space and add x,y */
+       for (i = 0; i < *num_glyphs; i++) {
+           double gx = (*glyphs)[i].x;
+           double gy = (*glyphs)[i].y;
+
+           cairo_matrix_transform_point (&scaled_font->base.font_matrix,
+                                         &gx, &gy);
+
+           (*glyphs)[i].x = gx + x;
+           (*glyphs)[i].y = gy + y;
+       }
+    }
+
+    return status;
+}
+
+static cairo_status_t
+_cairo_user_font_face_scaled_font_create (void                        *abstract_face,
+                                         const cairo_matrix_t        *font_matrix,
+                                         const cairo_matrix_t        *ctm,
+                                         const cairo_font_options_t  *options,
+                                         cairo_scaled_font_t        **scaled_font);
+
+static cairo_status_t
+_cairo_user_font_face_create_for_toy (cairo_toy_font_face_t   *toy_face,
+                                     cairo_font_face_t      **font_face)
+{
+    return _cairo_font_face_twin_create_for_toy (toy_face, font_face);
+}
+
+static const cairo_scaled_font_backend_t _cairo_user_scaled_font_backend = {
+    CAIRO_FONT_TYPE_USER,
+    NULL,      /* scaled_font_fini */
+    _cairo_user_scaled_glyph_init,
+    _cairo_user_text_to_glyphs,
+    _cairo_user_ucs4_to_index,
+    NULL,      /* show_glyphs */
+    NULL,      /* load_truetype_table */
+    NULL       /* index_to_ucs4 */
+};
+
+/* #cairo_user_font_face_t */
+
+static cairo_status_t
+_cairo_user_font_face_scaled_font_create (void                        *abstract_face,
+                                         const cairo_matrix_t        *font_matrix,
+                                         const cairo_matrix_t        *ctm,
+                                         const cairo_font_options_t  *options,
+                                         cairo_scaled_font_t        **scaled_font)
+{
+    cairo_status_t status = CAIRO_STATUS_SUCCESS;
+    cairo_user_font_face_t *font_face = abstract_face;
+    cairo_user_scaled_font_t *user_scaled_font = NULL;
+    cairo_font_extents_t font_extents = {1., 0., 1., 1., 0.};
+
+    font_face->immutable = TRUE;
+
+    user_scaled_font = malloc (sizeof (cairo_user_scaled_font_t));
+    if (unlikely (user_scaled_font == NULL))
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    status = _cairo_scaled_font_init (&user_scaled_font->base,
+                                     &font_face->base,
+                                     font_matrix, ctm, options,
+                                     &_cairo_user_scaled_font_backend);
+
+    if (unlikely (status)) {
+       free (user_scaled_font);
+       return status;
+    }
+
+    /* XXX metrics hinting? */
+
+    /* compute a normalized version of font scale matrix to compute
+     * extents in.  This is to minimize error caused by the cairo_fixed_t
+     * representation. */
+    {
+       double fixed_scale, x_scale, y_scale;
+
+       user_scaled_font->extent_scale = user_scaled_font->base.scale_inverse;
+       status = _cairo_matrix_compute_basis_scale_factors (&user_scaled_font->extent_scale,
+                                                     &x_scale, &y_scale,
+                                                     1);
+       if (status == CAIRO_STATUS_SUCCESS) {
+
+           if (x_scale == 0) x_scale = 1.;
+           if (y_scale == 0) y_scale = 1.;
+
+           user_scaled_font->snap_x_scale = x_scale;
+           user_scaled_font->snap_y_scale = y_scale;
+
+           /* since glyphs are pretty much 1.0x1.0, we can reduce error by
+            * scaling to a larger square.  say, 1024.x1024. */
+           fixed_scale = 1024.;
+           x_scale /= fixed_scale;
+           y_scale /= fixed_scale;
+
+           cairo_matrix_scale (&user_scaled_font->extent_scale, 1. / x_scale, 1. / y_scale);
+
+           user_scaled_font->extent_x_scale = x_scale;
+           user_scaled_font->extent_y_scale = y_scale;
+       }
+    }
+
+    if (status == CAIRO_STATUS_SUCCESS &&
+       font_face->scaled_font_methods.init != NULL)
+    {
+       /* Lock the scaled_font mutex such that user doesn't accidentally try
+         * to use it just yet. */
+       CAIRO_MUTEX_LOCK (user_scaled_font->base.mutex);
+
+       /* Give away fontmap lock such that user-font can use other fonts */
+       status = _cairo_scaled_font_register_placeholder_and_unlock_font_map (&user_scaled_font->base);
+       if (status == CAIRO_STATUS_SUCCESS) {
+           cairo_surface_t *recording_surface;
+           cairo_t *cr;
+
+           recording_surface = _cairo_user_scaled_font_create_recording_surface (user_scaled_font);
+           cr = _cairo_user_scaled_font_create_recording_context (user_scaled_font, recording_surface);
+           cairo_surface_destroy (recording_surface);
+
+           status = font_face->scaled_font_methods.init (&user_scaled_font->base,
+                                                         cr,
+                                                         &font_extents);
+
+           if (status == CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED)
+               status = CAIRO_STATUS_SUCCESS;
+
+           if (status == CAIRO_STATUS_SUCCESS)
+               status = cairo_status (cr);
+
+           cairo_destroy (cr);
+
+           _cairo_scaled_font_unregister_placeholder_and_lock_font_map (&user_scaled_font->base);
+       }
+
+       CAIRO_MUTEX_UNLOCK (user_scaled_font->base.mutex);
+    }
+
+    if (status == CAIRO_STATUS_SUCCESS)
+       status = _cairo_scaled_font_set_metrics (&user_scaled_font->base, &font_extents);
+
+    if (status != CAIRO_STATUS_SUCCESS) {
+        _cairo_scaled_font_fini (&user_scaled_font->base);
+       free (user_scaled_font);
+    } else {
+        user_scaled_font->default_glyph_extents.x_bearing = 0.;
+        user_scaled_font->default_glyph_extents.y_bearing = -font_extents.ascent;
+        user_scaled_font->default_glyph_extents.width = 0.;
+        user_scaled_font->default_glyph_extents.height = font_extents.ascent + font_extents.descent;
+        user_scaled_font->default_glyph_extents.x_advance = font_extents.max_x_advance;
+        user_scaled_font->default_glyph_extents.y_advance = 0.;
+
+       *scaled_font = &user_scaled_font->base;
+    }
+
+    return status;
+}
+
+const cairo_font_face_backend_t _cairo_user_font_face_backend = {
+    CAIRO_FONT_TYPE_USER,
+    _cairo_user_font_face_create_for_toy,
+    NULL,      /* destroy */
+    _cairo_user_font_face_scaled_font_create
+};
+
+
+cairo_bool_t
+_cairo_font_face_is_user (cairo_font_face_t *font_face)
+{
+    return font_face->backend == &_cairo_user_font_face_backend;
+}
+
+/* Implement the public interface */
+
+/**
+ * cairo_user_font_face_create:
+ *
+ * Creates a new user font-face.
+ *
+ * Use the setter functions to associate callbacks with the returned
+ * user font.  The only mandatory callback is render_glyph.
+ *
+ * After the font-face is created, the user can attach arbitrary data
+ * (the actual font data) to it using cairo_font_face_set_user_data()
+ * and access it from the user-font callbacks by using
+ * cairo_scaled_font_get_font_face() followed by
+ * cairo_font_face_get_user_data().
+ *
+ * Return value: a newly created #cairo_font_face_t. Free with
+ *  cairo_font_face_destroy() when you are done using it.
+ *
+ * Since: 1.8
+ **/
+cairo_font_face_t *
+cairo_user_font_face_create (void)
+{
+    cairo_user_font_face_t *font_face;
+
+    font_face = malloc (sizeof (cairo_user_font_face_t));
+    if (!font_face) {
+       _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+       return (cairo_font_face_t *)&_cairo_font_face_nil;
+    }
+
+    _cairo_font_face_init (&font_face->base, &_cairo_user_font_face_backend);
+
+    font_face->immutable = FALSE;
+    memset (&font_face->scaled_font_methods, 0, sizeof (font_face->scaled_font_methods));
+
+    return &font_face->base;
+}
+slim_hidden_def(cairo_user_font_face_create);
+
+/* User-font method setters */
+
+
+/**
+ * cairo_user_font_face_set_init_func:
+ * @font_face: A user font face
+ * @init_func: The init callback, or %NULL
+ *
+ * Sets the scaled-font initialization function of a user-font.
+ * See #cairo_user_scaled_font_init_func_t for details of how the callback
+ * works.
+ *
+ * The font-face should not be immutable or a %CAIRO_STATUS_USER_FONT_IMMUTABLE
+ * error will occur.  A user font-face is immutable as soon as a scaled-font
+ * is created from it.
+ *
+ * Since: 1.8
+ **/
+void
+cairo_user_font_face_set_init_func (cairo_font_face_t                  *font_face,
+                                   cairo_user_scaled_font_init_func_t  init_func)
+{
+    cairo_user_font_face_t *user_font_face;
+
+    if (font_face->status)
+       return;
+
+    if (! _cairo_font_face_is_user (font_face)) {
+       if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_FONT_TYPE_MISMATCH))
+           return;
+    }
+
+    user_font_face = (cairo_user_font_face_t *) font_face;
+    if (user_font_face->immutable) {
+       if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_USER_FONT_IMMUTABLE))
+           return;
+    }
+    user_font_face->scaled_font_methods.init = init_func;
+}
+slim_hidden_def(cairo_user_font_face_set_init_func);
+
+/**
+ * cairo_user_font_face_set_render_glyph_func:
+ * @font_face: A user font face
+ * @render_glyph_func: The render_glyph callback, or %NULL
+ *
+ * Sets the glyph rendering function of a user-font.
+ * See #cairo_user_scaled_font_render_glyph_func_t for details of how the callback
+ * works.
+ *
+ * The font-face should not be immutable or a %CAIRO_STATUS_USER_FONT_IMMUTABLE
+ * error will occur.  A user font-face is immutable as soon as a scaled-font
+ * is created from it.
+ *
+ * The render_glyph callback is the only mandatory callback of a user-font.
+ * If the callback is %NULL and a glyph is tried to be rendered using
+ * @font_face, a %CAIRO_STATUS_USER_FONT_ERROR will occur.
+ *
+ * Since: 1.8
+ **/
+void
+cairo_user_font_face_set_render_glyph_func (cairo_font_face_t                          *font_face,
+                                           cairo_user_scaled_font_render_glyph_func_t  render_glyph_func)
+{
+    cairo_user_font_face_t *user_font_face;
+
+    if (font_face->status)
+       return;
+
+    if (! _cairo_font_face_is_user (font_face)) {
+       if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_FONT_TYPE_MISMATCH))
+           return;
+    }
+
+    user_font_face = (cairo_user_font_face_t *) font_face;
+    if (user_font_face->immutable) {
+       if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_USER_FONT_IMMUTABLE))
+           return;
+    }
+    user_font_face->scaled_font_methods.render_glyph = render_glyph_func;
+}
+slim_hidden_def(cairo_user_font_face_set_render_glyph_func);
+
+/**
+ * cairo_user_font_face_set_text_to_glyphs_func:
+ * @font_face: A user font face
+ * @text_to_glyphs_func: The text_to_glyphs callback, or %NULL
+ *
+ * Sets th text-to-glyphs conversion function of a user-font.
+ * See #cairo_user_scaled_font_text_to_glyphs_func_t for details of how the callback
+ * works.
+ *
+ * The font-face should not be immutable or a %CAIRO_STATUS_USER_FONT_IMMUTABLE
+ * error will occur.  A user font-face is immutable as soon as a scaled-font
+ * is created from it.
+ *
+ * Since: 1.8
+ **/
+void
+cairo_user_font_face_set_text_to_glyphs_func (cairo_font_face_t                            *font_face,
+                                             cairo_user_scaled_font_text_to_glyphs_func_t  text_to_glyphs_func)
+{
+    cairo_user_font_face_t *user_font_face;
+
+    if (font_face->status)
+       return;
+
+    if (! _cairo_font_face_is_user (font_face)) {
+       if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_FONT_TYPE_MISMATCH))
+           return;
+    }
+
+    user_font_face = (cairo_user_font_face_t *) font_face;
+    if (user_font_face->immutable) {
+       if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_USER_FONT_IMMUTABLE))
+           return;
+    }
+    user_font_face->scaled_font_methods.text_to_glyphs = text_to_glyphs_func;
+}
+
+/**
+ * cairo_user_font_face_set_unicode_to_glyph_func:
+ * @font_face: A user font face
+ * @unicode_to_glyph_func: The unicode_to_glyph callback, or %NULL
+ *
+ * Sets the unicode-to-glyph conversion function of a user-font.
+ * See #cairo_user_scaled_font_unicode_to_glyph_func_t for details of how the callback
+ * works.
+ *
+ * The font-face should not be immutable or a %CAIRO_STATUS_USER_FONT_IMMUTABLE
+ * error will occur.  A user font-face is immutable as soon as a scaled-font
+ * is created from it.
+ *
+ * Since: 1.8
+ **/
+void
+cairo_user_font_face_set_unicode_to_glyph_func (cairo_font_face_t                              *font_face,
+                                               cairo_user_scaled_font_unicode_to_glyph_func_t  unicode_to_glyph_func)
+{
+    cairo_user_font_face_t *user_font_face;
+    if (font_face->status)
+       return;
+
+    if (! _cairo_font_face_is_user (font_face)) {
+       if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_FONT_TYPE_MISMATCH))
+           return;
+    }
+
+    user_font_face = (cairo_user_font_face_t *) font_face;
+    if (user_font_face->immutable) {
+       if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_USER_FONT_IMMUTABLE))
+           return;
+    }
+    user_font_face->scaled_font_methods.unicode_to_glyph = unicode_to_glyph_func;
+}
+slim_hidden_def(cairo_user_font_face_set_unicode_to_glyph_func);
+
+/* User-font method getters */
+
+/**
+ * cairo_user_font_face_get_init_func:
+ * @font_face: A user font face
+ *
+ * Gets the scaled-font initialization function of a user-font.
+ *
+ * Return value: The init callback of @font_face
+ * or %NULL if none set or an error has occurred.
+ *
+ * Since: 1.8
+ **/
+cairo_user_scaled_font_init_func_t
+cairo_user_font_face_get_init_func (cairo_font_face_t *font_face)
+{
+    cairo_user_font_face_t *user_font_face;
+
+    if (font_face->status)
+       return NULL;
+
+    if (! _cairo_font_face_is_user (font_face)) {
+       if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_FONT_TYPE_MISMATCH))
+           return NULL;
+    }
+
+    user_font_face = (cairo_user_font_face_t *) font_face;
+    return user_font_face->scaled_font_methods.init;
+}
+
+/**
+ * cairo_user_font_face_get_render_glyph_func:
+ * @font_face: A user font face
+ *
+ * Gets the glyph rendering function of a user-font.
+ *
+ * Return value: The render_glyph callback of @font_face
+ * or %NULL if none set or an error has occurred.
+ *
+ * Since: 1.8
+ **/
+cairo_user_scaled_font_render_glyph_func_t
+cairo_user_font_face_get_render_glyph_func (cairo_font_face_t *font_face)
+{
+    cairo_user_font_face_t *user_font_face;
+
+    if (font_face->status)
+       return NULL;
+
+    if (! _cairo_font_face_is_user (font_face)) {
+       if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_FONT_TYPE_MISMATCH))
+           return NULL;
+    }
+
+    user_font_face = (cairo_user_font_face_t *) font_face;
+    return user_font_face->scaled_font_methods.render_glyph;
+}
+
+/**
+ * cairo_user_font_face_get_text_to_glyphs_func:
+ * @font_face: A user font face
+ *
+ * Gets the text-to-glyphs conversion function of a user-font.
+ *
+ * Return value: The text_to_glyphs callback of @font_face
+ * or %NULL if none set or an error occurred.
+ *
+ * Since: 1.8
+ **/
+cairo_user_scaled_font_text_to_glyphs_func_t
+cairo_user_font_face_get_text_to_glyphs_func (cairo_font_face_t *font_face)
+{
+    cairo_user_font_face_t *user_font_face;
+
+    if (font_face->status)
+       return NULL;
+
+    if (! _cairo_font_face_is_user (font_face)) {
+       if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_FONT_TYPE_MISMATCH))
+           return NULL;
+    }
+
+    user_font_face = (cairo_user_font_face_t *) font_face;
+    return user_font_face->scaled_font_methods.text_to_glyphs;
+}
+
+/**
+ * cairo_user_font_face_get_unicode_to_glyph_func:
+ * @font_face: A user font face
+ *
+ * Gets the unicode-to-glyph conversion function of a user-font.
+ *
+ * Return value: The unicode_to_glyph callback of @font_face
+ * or %NULL if none set or an error occurred.
+ *
+ * Since: 1.8
+ **/
+cairo_user_scaled_font_unicode_to_glyph_func_t
+cairo_user_font_face_get_unicode_to_glyph_func (cairo_font_face_t *font_face)
+{
+    cairo_user_font_face_t *user_font_face;
+
+    if (font_face->status)
+       return NULL;
+
+    if (! _cairo_font_face_is_user (font_face)) {
+       if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_FONT_TYPE_MISMATCH))
+           return NULL;
+    }
+
+    user_font_face = (cairo_user_font_face_t *) font_face;
+    return user_font_face->scaled_font_methods.unicode_to_glyph;
+}
diff --git a/src/cairo-version.c b/src/cairo-version.c
new file mode 100644 (file)
index 0000000..2f53e11
--- /dev/null
@@ -0,0 +1,244 @@
+/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *     Carl D. Worth <cworth@cworth.org>
+ */
+
+#define CAIRO_VERSION_H 1
+
+#include "cairoint.h"
+
+/* get the "real" version info instead of dummy cairo-version.h */
+#undef CAIRO_VERSION_H
+#include "../cairo-version.h"
+
+/**
+ * SECTION:cairo-version
+ * @Title: Version Information
+ * @Short_Description: Compile-time and run-time version checks.
+ *
+ * Cairo has a three-part version number scheme. In this scheme, we use
+ * even vs. odd numbers to distinguish fixed points in the software
+ * vs. in-progress development, (such as from git instead of a tar file,
+ * or as a "snapshot" tar file as opposed to a "release" tar file).
+ *
+ * <informalexample><programlisting>
+ *  _____ Major. Always 1, until we invent a new scheme.
+ * /  ___ Minor. Even/Odd = Release/Snapshot (tar files) or Branch/Head (git)
+ * | /  _ Micro. Even/Odd = Tar-file/git
+ * | | /
+ * 1.0.0
+ * </programlisting></informalexample>
+ *
+ * Here are a few examples of versions that one might see.
+ * <informalexample><programlisting>
+ * Releases
+ * --------
+ * 1.0.0 - A major release
+ * 1.0.2 - A subsequent maintenance release
+ * 1.2.0 - Another major release
+ *
+ * Snapshots
+ * ---------
+ * 1.1.2 - A snapshot (working toward the 1.2.0 release)
+ *
+ * In-progress development (eg. from git)
+ * --------------------------------------
+ * 1.0.1 - Development on a maintenance branch (toward 1.0.2 release)
+ * 1.1.1 - Development on head (toward 1.1.2 snapshot and 1.2.0 release)
+ * </programlisting></informalexample>
+ * </para>
+ * <refsect2>
+ * <title>Compatibility</title>
+ * <para>
+ * The API/ABI compatibility guarantees for various versions are as
+ * follows. First, let's assume some cairo-using application code that is
+ * successfully using the API/ABI "from" one version of cairo. Then let's
+ * ask the question whether this same code can be moved "to" the API/ABI
+ * of another version of cairo.
+ *
+ * Moving from a release to any later version (release, snapshot,
+ * development) is always guaranteed to provide compatibility.
+ *
+ * Moving from a snapshot to any later version is not guaranteed to
+ * provide compatibility, since snapshots may introduce new API that ends
+ * up being removed before the next release.
+ *
+ * Moving from an in-development version (odd micro component) to any
+ * later version is not guaranteed to provide compatibility. In fact,
+ * there's not even a guarantee that the code will even continue to work
+ * with the same in-development version number. This is because these
+ * numbers don't correspond to any fixed state of the software, but
+ * rather the many states between snapshots and releases.
+ * </para>
+ * </refsect2>
+ * <refsect2>
+ * <title>Examining the version</title>
+ * <para>
+ * Cairo provides the ability to examine the version at either
+ * compile-time or run-time and in both a human-readable form as well as
+ * an encoded form suitable for direct comparison. Cairo also provides the
+ * macro CAIRO_VERSION_ENCODE() to perform the encoding.
+ *
+ * <informalexample><programlisting>
+ * Compile-time
+ * ------------
+ * CAIRO_VERSION_STRING    Human-readable
+ * CAIRO_VERSION           Encoded, suitable for comparison
+ *
+ * Run-time
+ * --------
+ * cairo_version_string()  Human-readable
+ * cairo_version()         Encoded, suitable for comparison
+ * </programlisting></informalexample>
+ *
+ * For example, checking that the cairo version is greater than or equal
+ * to 1.0.0 could be achieved at compile-time or run-time as follows:
+ *
+ * <informalexample><programlisting>
+ * ##if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 0, 0)
+ * printf ("Compiling with suitable cairo version: %s\n", %CAIRO_VERSION_STRING);
+ * ##endif
+ *
+ * if (cairo_version() >= CAIRO_VERSION_ENCODE(1, 0, 0))
+ *     printf ("Running with suitable cairo version: %s\n", cairo_version_string ());
+ * </programlisting></informalexample>
+ * </para>
+ * </refsect2>
+ */
+
+/**
+ * CAIRO_VERSION:
+ *
+ * The version of cairo available at compile-time, encoded using
+ * CAIRO_VERSION_ENCODE().
+ */
+
+/**
+ * CAIRO_VERSION_MAJOR:
+ *
+ * The major component of the version of cairo available at compile-time.
+ */
+
+/**
+ * CAIRO_VERSION_MINOR:
+ *
+ * The minor component of the version of cairo available at compile-time.
+ */
+
+/**
+ * CAIRO_VERSION_MICRO:
+ *
+ * The micro component of the version of cairo available at compile-time.
+ */
+
+/**
+ * CAIRO_VERSION_STRING:
+ *
+ * A human-readable string literal containing the version of cairo available
+ * at compile-time, in the form of "X.Y.Z".
+ */
+
+/**
+ * CAIRO_VERSION_ENCODE:
+ * @major: the major component of the version number
+ * @minor: the minor component of the version number
+ * @micro: the micro component of the version number
+ * 
+ * This macro encodes the given cairo version into an integer.  The numbers
+ * returned by %CAIRO_VERSION and cairo_version() are encoded using this macro.
+ * Two encoded version numbers can be compared as integers.  The encoding ensures
+ * that later versions compare greater than earlier versions.
+ *
+ * @Returns: the encoded version.
+ */
+
+/**
+ * CAIRO_VERSION_STRINGIZE:
+ * @major: the major component of the version number
+ * @minor: the minor component of the version number
+ * @micro: the micro component of the version number
+ *
+ * This macro encodes the given cairo version into an string.  The numbers
+ * returned by %CAIRO_VERSION_STRING and cairo_version_string() are encoded using this macro.
+ * The parameters to this macro must expand to numerical literals.
+ *
+ * @Returns: a string literal containing the version.
+ *
+ * @Since: 1.8
+ */
+
+/**
+ * cairo_version:
+ *
+ * Returns the version of the cairo library encoded in a single
+ * integer as per %CAIRO_VERSION_ENCODE. The encoding ensures that
+ * later versions compare greater than earlier versions.
+ *
+ * A run-time comparison to check that cairo's version is greater than
+ * or equal to version X.Y.Z could be performed as follows:
+ *
+ * <informalexample><programlisting>
+ * if (cairo_version() >= CAIRO_VERSION_ENCODE(X,Y,Z)) {...}
+ * </programlisting></informalexample>
+ *
+ * See also cairo_version_string() as well as the compile-time
+ * equivalents %CAIRO_VERSION and %CAIRO_VERSION_STRING.
+ *
+ * Return value: the encoded version.
+ **/
+int
+cairo_version (void)
+{
+    return CAIRO_VERSION;
+}
+
+/**
+ * cairo_version_string:
+ *
+ * Returns the version of the cairo library as a human-readable string
+ * of the form "X.Y.Z".
+ *
+ * See also cairo_version() as well as the compile-time equivalents
+ * %CAIRO_VERSION_STRING and %CAIRO_VERSION.
+ *
+ * Return value: a string containing the version.
+ **/
+const char*
+cairo_version_string (void)
+{
+    return CAIRO_VERSION_STRING;
+}
+slim_hidden_def (cairo_version_string);
diff --git a/src/cairo-version.h b/src/cairo-version.h
new file mode 100644 (file)
index 0000000..5100800
--- /dev/null
@@ -0,0 +1,14 @@
+/* This is a dummy file.
+ * The actual version info is in toplevel cairo-version.h.
+ * The purpose of this file is to make most of the source files NOT depend
+ * on the real cairo-version.h, and as a result, changing library version
+ * would not cause a complete rebuild of all object files (just a relink).
+ * This is useful when bisecting. */
+#ifndef CAIRO_VERSION_H
+#define CAIRO_VERSION_H
+
+#define CAIRO_VERSION_MAJOR USE_cairo_version_OR_cairo_version_string_INSTEAD
+#define CAIRO_VERSION_MINOR USE_cairo_version_OR_cairo_version_string_INSTEAD
+#define CAIRO_VERSION_MICRO USE_cairo_version_OR_cairo_version_string_INSTEAD
+
+#endif
diff --git a/src/cairo-vg-surface.c b/src/cairo-vg-surface.c
new file mode 100644 (file)
index 0000000..a2ea547
--- /dev/null
@@ -0,0 +1,1935 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2008 Opened Hand Ltd.
+ * Copyright © 2009 Chris Wilson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.og/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * Contributor(s):
+ *      Pierre Tardy      <tardyp@gmail.com>
+ *      Øyvind Kolås      <pippin@gimp.org>
+ *      Vladimi Vukicevic <vladimir@mozilla.com> (stubbed out base backend)
+ *      Chris Wilson      <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-vg.h"
+
+#include "cairo-cache-private.h"
+#include "cairo-default-context-private.h"
+#include "cairo-error-private.h"
+#include "cairo-path-fixed-private.h"
+#include "cairo-recording-surface-private.h"
+#include "cairo-surface-clipper-private.h"
+
+#include <pixman.h>
+#include <VG/openvg.h>
+
+//#define OPENVG_DEBUG
+
+/*
+ * Work that needs to be done:
+ *  - Glyph cache / proper font support
+ *
+ *  - First-class paths
+ *    Paths are expensive for OpenVG, reuse paths whenever possible.
+ *    So add a path cache, and first class paths!
+ */
+
+typedef struct _cairo_vg_surface cairo_vg_surface_t;
+
+/* XXX need GL specific context control. :( */
+struct _cairo_vg_context {
+    cairo_status_t status;
+    cairo_reference_count_t ref_count;
+
+    unsigned long target_id;
+
+    VGPaint            paint;
+    cairo_vg_surface_t *source;
+    double             alpha;
+
+    cairo_cache_t snapshot_cache;
+
+    void *display;
+    void *context;
+
+    cairo_status_t (*create_target) (cairo_vg_context_t *,
+                                    cairo_vg_surface_t *);
+    cairo_status_t (*set_target) (cairo_vg_context_t *,
+                                 cairo_vg_surface_t *);
+    void (*destroy_target) (cairo_vg_context_t *, cairo_vg_surface_t *);
+};
+
+struct _cairo_vg_surface {
+    cairo_surface_t base;
+
+    cairo_vg_context_t *context;
+
+    VGImage        image;
+    VGImageFormat   format;
+    int             width;
+    int             height;
+    cairo_bool_t    own_image;
+
+    cairo_cache_entry_t snapshot_cache_entry;
+
+    cairo_surface_clipper_t clipper;
+
+    unsigned long target_id;
+};
+
+static const cairo_surface_backend_t cairo_vg_surface_backend;
+
+slim_hidden_proto (cairo_vg_surface_create);
+
+static cairo_surface_t *
+_vg_surface_create_internal (cairo_vg_context_t *context,
+                            VGImage image,
+                            VGImageFormat format,
+                            int width, int height);
+
+static cairo_vg_context_t *
+_vg_context_reference (cairo_vg_context_t *context)
+{
+    assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&context->ref_count));
+
+    _cairo_reference_count_inc (&context->ref_count);
+
+    return context;
+}
+
+static cairo_vg_context_t *
+_vg_context_lock (cairo_vg_context_t *context)
+{
+    /* XXX if we need to add locking, then it has to be recursive */
+    return context;
+}
+
+static cairo_int_status_t
+_vg_context_set_target (cairo_vg_context_t *context,
+                       cairo_vg_surface_t *surface)
+{
+    cairo_status_t status;
+
+    if (surface->target_id == 0) {
+       status = context->create_target (context, surface);
+       if (unlikely (status))
+           return status;
+    }
+
+    if (context->target_id == surface->target_id)
+       return CAIRO_STATUS_SUCCESS;
+
+    context->target_id = surface->target_id;
+
+    return context->set_target (context, surface);
+}
+
+static void
+_vg_context_destroy_target (cairo_vg_context_t *context,
+                           cairo_vg_surface_t *surface)
+{
+    if (surface->target_id == 0)
+       return;
+
+    if (context->target_id == surface->target_id)
+       context->set_target (context, NULL);
+
+    context->destroy_target (context, surface);
+}
+
+static cairo_bool_t
+_vg_snapshot_cache_can_remove (const void *entry)
+{
+    return TRUE;
+}
+
+static void
+_vg_snapshot_cache_remove (void *cache_entry)
+{
+    cairo_vg_surface_t *surface = cairo_container_of (cache_entry,
+                                                     cairo_vg_surface_t,
+                                                     snapshot_cache_entry);
+    surface->snapshot_cache_entry.hash = 0;
+    cairo_surface_destroy (&surface->base);
+}
+
+static cairo_status_t
+_vg_context_init (cairo_vg_context_t *context)
+{
+    cairo_status_t status;
+
+    context->status = CAIRO_STATUS_SUCCESS;
+    CAIRO_REFERENCE_COUNT_INIT (&context->ref_count, 1);
+
+    status = _cairo_cache_init (&context->snapshot_cache,
+                               NULL,
+                               _vg_snapshot_cache_can_remove,
+                               _vg_snapshot_cache_remove,
+                               16*1024*1024);
+    if (unlikely (status))
+       return status;
+
+    context->target_id = 0;
+    context->source = NULL;
+    context->alpha = 1.0;
+
+    context->paint = vgCreatePaint ();
+    vgLoadIdentity ();
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_vg_context_destroy (cairo_vg_context_t *context)
+{
+    assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&context->ref_count));
+
+    if (! _cairo_reference_count_dec_and_test (&context->ref_count))
+       return;
+
+    if (context->paint != VG_INVALID_HANDLE)
+       vgDestroyPaint (context->paint);
+
+    _cairo_cache_fini (&context->snapshot_cache);
+    free (context);
+}
+
+static void
+_vg_context_unlock (cairo_vg_context_t *context)
+{
+}
+
+#ifdef OPENVG_DEBUG
+static void check_vg_errors(const char*function,int line)
+{
+    int err = vgGetError();
+    if (err != VG_NO_ERROR){
+       printf("%s+%d:vgError detected: 0x%08x.\n",function, line,err);
+       assert(err == VG_NO_ERROR);
+    }
+
+}
+#define CHECK_VG_ERRORS() check_vg_errors(__FILE__,__LINE__)
+#else
+#define CHECK_VG_ERRORS() do{}while(0)
+#endif //OPENVG_DEBUG
+
+static pixman_format_code_t
+_vg_format_to_pixman (VGImageFormat format,
+                     cairo_bool_t *needs_premult_fixup)
+{
+    *needs_premult_fixup = FALSE;
+    switch (format) {
+       /* RGB{A,X} channel ordering */
+    case VG_sRGBX_8888: return PIXMAN_r8g8b8x8;
+    case VG_sRGBA_8888: *needs_premult_fixup = TRUE; return PIXMAN_r8g8b8a8;
+    case VG_sRGBA_8888_PRE: return PIXMAN_r8b8g8a8;
+    case VG_sRGB_565: return PIXMAN_r5g6b5;
+    case VG_sRGBA_5551: return 0;
+    case VG_sRGBA_4444: return 0;
+    case VG_sL_8: return PIXMAN_g8;
+    case VG_lRGBX_8888: return 0;
+    case VG_lRGBA_8888: return 0;
+    case VG_lRGBA_8888_PRE: return 0;
+    case VG_lL_8: return 0;
+    case VG_A_8: return PIXMAN_a8;
+    case VG_BW_1: return PIXMAN_a1;
+    case VG_A_1: return PIXMAN_a1;
+    case VG_A_4: return PIXMAN_a4;
+
+       /* {A,X}RGB channel ordering */
+    case VG_sXRGB_8888: return PIXMAN_x8r8g8b8;
+    case VG_sARGB_8888: *needs_premult_fixup = TRUE; return PIXMAN_a8r8g8b8;
+    case VG_sARGB_8888_PRE: return PIXMAN_a8r8g8b8;
+    case VG_sARGB_1555: return 0;
+    case VG_sARGB_4444: return 0;
+    case VG_lXRGB_8888: return 0;
+    case VG_lARGB_8888: return 0;
+    case VG_lARGB_8888_PRE: return 0;
+
+       /* BGR{A,X} channel ordering */
+    case VG_sBGRX_8888: return PIXMAN_b8g8r8x8;
+    case VG_sBGRA_8888: *needs_premult_fixup = TRUE; return PIXMAN_b8g8r8a8;
+    case VG_sBGRA_8888_PRE: return PIXMAN_b8g8r8a8;
+    case VG_sBGR_565: return PIXMAN_b5g6r5;
+    case VG_sBGRA_5551: return 0;
+    case VG_sBGRA_4444: return 0;
+    case VG_lBGRX_8888: return 0;
+    case VG_lBGRA_8888: return 0;
+    case VG_lBGRA_8888_PRE: return 0;
+
+       /* {A,X}BGR channel ordering */
+    case VG_sXBGR_8888: return PIXMAN_x8b8g8r8;
+    case VG_sABGR_8888: *needs_premult_fixup = TRUE; return PIXMAN_a8b8g8r8;
+    case VG_sABGR_8888_PRE: return PIXMAN_a8b8g8r8;
+    case VG_sABGR_1555: return 0;
+    case VG_sABGR_4444: return 0;
+    case VG_lXBGR_8888: return 0;
+    case VG_lABGR_8888: return 0;
+    case VG_lABGR_8888_PRE: return 0;
+    default: return 0;
+    }
+}
+
+static pixman_format_code_t
+_vg_format_to_content (VGImageFormat format)
+{
+    /* XXX could use more simple bit tests */
+    switch (format) {
+       /* RGB{A,X} channel ordering */
+    case VG_sRGBX_8888: return CAIRO_CONTENT_COLOR;
+    case VG_sRGBA_8888: return CAIRO_CONTENT_COLOR_ALPHA;
+    case VG_sRGBA_8888_PRE: return CAIRO_CONTENT_COLOR_ALPHA;
+    case VG_sRGB_565: return CAIRO_CONTENT_COLOR;
+    case VG_sRGBA_5551: return CAIRO_CONTENT_COLOR_ALPHA;
+    case VG_sRGBA_4444: return CAIRO_CONTENT_COLOR_ALPHA;
+    case VG_sL_8: return CAIRO_CONTENT_ALPHA;
+    case VG_lRGBX_8888: return CAIRO_CONTENT_COLOR;
+    case VG_lRGBA_8888: return CAIRO_CONTENT_COLOR_ALPHA;
+    case VG_lRGBA_8888_PRE: return CAIRO_CONTENT_COLOR_ALPHA;
+    case VG_lL_8: return CAIRO_CONTENT_ALPHA;
+    case VG_A_8: return CAIRO_CONTENT_ALPHA;
+    case VG_A_4: return CAIRO_CONTENT_ALPHA;
+    case VG_A_1: return CAIRO_CONTENT_ALPHA;
+    case VG_BW_1: return CAIRO_CONTENT_ALPHA;
+
+       /* {A,X}RGB channel ordering */
+    case VG_sXRGB_8888: return CAIRO_CONTENT_COLOR;
+    case VG_sARGB_8888: return CAIRO_CONTENT_COLOR_ALPHA;
+    case VG_sARGB_8888_PRE: return CAIRO_CONTENT_COLOR_ALPHA;
+    case VG_sARGB_1555: return CAIRO_CONTENT_COLOR_ALPHA;
+    case VG_sARGB_4444: return CAIRO_CONTENT_COLOR_ALPHA;
+    case VG_lXRGB_8888: return CAIRO_CONTENT_COLOR;
+    case VG_lARGB_8888: return CAIRO_CONTENT_COLOR_ALPHA;
+    case VG_lARGB_8888_PRE: return CAIRO_CONTENT_COLOR_ALPHA;
+
+       /* BGR{A,X} channel ordering */
+    case VG_sBGRX_8888: return CAIRO_CONTENT_COLOR;
+    case VG_sBGRA_8888: return CAIRO_CONTENT_COLOR_ALPHA;
+    case VG_sBGRA_8888_PRE: return CAIRO_CONTENT_COLOR_ALPHA;
+    case VG_sBGR_565: return CAIRO_CONTENT_COLOR;
+    case VG_sBGRA_5551: return CAIRO_CONTENT_COLOR_ALPHA;
+    case VG_sBGRA_4444: return CAIRO_CONTENT_COLOR_ALPHA;
+    case VG_lBGRX_8888: return CAIRO_CONTENT_COLOR;
+    case VG_lBGRA_8888: return CAIRO_CONTENT_COLOR_ALPHA;
+    case VG_lBGRA_8888_PRE: return CAIRO_CONTENT_COLOR_ALPHA;
+
+       /* {A,X}BGR channel ordering */
+    case VG_sXBGR_8888: return CAIRO_CONTENT_COLOR;
+    case VG_sABGR_8888: return CAIRO_CONTENT_COLOR_ALPHA;
+    case VG_sABGR_8888_PRE: return CAIRO_CONTENT_COLOR_ALPHA;
+    case VG_sABGR_1555: return CAIRO_CONTENT_COLOR_ALPHA;
+    case VG_sABGR_4444: return CAIRO_CONTENT_COLOR_ALPHA;
+    case VG_lXBGR_8888: return CAIRO_CONTENT_COLOR;
+    case VG_lABGR_8888: return CAIRO_CONTENT_COLOR_ALPHA;
+    case VG_lABGR_8888_PRE: return CAIRO_CONTENT_COLOR_ALPHA;
+    default: return 0;
+    }
+}
+
+static VGImageFormat
+_vg_format_from_pixman (pixman_format_code_t format)
+{
+    /* XXX _PRE needs fixup */
+    switch ((int) format) {
+    case PIXMAN_r5g6b5: return VG_sRGB_565;
+    case PIXMAN_g8: return VG_sL_8;
+    case PIXMAN_a8: return VG_A_8;
+    case PIXMAN_a1: return VG_BW_1;
+    case PIXMAN_x8r8g8b8: return VG_sXRGB_8888;
+    case PIXMAN_a8r8g8b8: return VG_sARGB_8888; // _PRE
+    case PIXMAN_b8g8r8x8: return VG_sBGRX_8888;
+    case PIXMAN_b8g8r8a8: return VG_sBGRA_8888; // _PRE
+    case PIXMAN_b5g6r5: return VG_sBGR_565;
+    case PIXMAN_x8b8g8r8: return VG_sXBGR_8888;
+    case PIXMAN_a8b8g8r8: return VG_sABGR_8888; // _PRE
+    default: return 0;
+    }
+}
+
+static VGImageFormat
+_vg_format_for_content (cairo_content_t content)
+{
+    switch (content) {
+    case CAIRO_CONTENT_ALPHA: return VG_A_8;
+    case CAIRO_CONTENT_COLOR: return VG_sXRGB_8888;
+    default: ASSERT_NOT_REACHED;
+    case CAIRO_CONTENT_COLOR_ALPHA: return VG_sARGB_8888; // _PRE
+    }
+}
+
+static cairo_surface_t *
+_vg_surface_create_similar (void            *abstract_surface,
+                           cairo_content_t  content,
+                           int              width,
+                           int              height)
+{
+    cairo_vg_surface_t *surface = abstract_surface;
+
+    if (width > vgGeti (VG_MAX_IMAGE_WIDTH) ||
+       height > vgGeti (VG_MAX_IMAGE_HEIGHT))
+    {
+       return NULL;
+    }
+
+    return cairo_vg_surface_create (surface->context, content, width, height);
+}
+
+static cairo_status_t
+_vg_surface_clipper_intersect_clip_path (cairo_surface_clipper_t *clipper,
+                                        cairo_path_fixed_t *path,
+                                        cairo_fill_rule_t   fill_rule,
+                                        double              tolerance,
+                                        cairo_antialias_t   antialias)
+{
+    cairo_vg_surface_t *surface = cairo_container_of (clipper,
+                                                     cairo_vg_surface_t,
+                                                     clipper);
+    cairo_vg_surface_t *mask;
+    cairo_status_t status;
+
+    if (path == NULL) {
+       vgMask (VG_INVALID_HANDLE,
+               VG_FILL_MASK, 0, 0, surface->width, surface->height);
+       vgSeti (VG_MASKING, VG_FALSE);
+       CHECK_VG_ERRORS();
+       return CAIRO_STATUS_SUCCESS;
+    }
+
+    mask = (cairo_vg_surface_t *)
+       _vg_surface_create_similar (surface, CAIRO_CONTENT_ALPHA,
+                                   surface->width, surface->height);
+    if (unlikely (mask == NULL))
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+    if (unlikely (mask->base.status))
+       return mask->base.status;
+
+    status = _cairo_surface_fill (&mask->base,
+                                 CAIRO_OPERATOR_SOURCE,
+                                 &_cairo_pattern_white.base,
+                                 path, fill_rule, tolerance, antialias,
+                                 NULL);
+    if (status) {
+       cairo_surface_destroy (&mask->base);
+       return status;
+    }
+
+    vgSeti (VG_MASKING, VG_TRUE);
+    vgMask (mask->image, VG_INTERSECT_MASK, 0, 0, mask->width, mask->height);
+
+    cairo_surface_destroy (&mask->base);
+
+    CHECK_VG_ERRORS();
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_bool_t
+_vg_surface_get_extents (void                  *abstract_surface,
+                        cairo_rectangle_int_t *extents)
+{
+    cairo_vg_surface_t *surface = abstract_surface;
+
+    extents->x = 0;
+    extents->y = 0;
+    extents->width  = surface->width;
+    extents->height = surface->height;
+
+    return TRUE;
+}
+
+#define MAX_SEG  16  /* max number of knots to upload in a batch */
+
+typedef struct _vg_path {
+    VGPath path;
+    cairo_matrix_t *ctm_inverse;
+
+    VGubyte gseg[MAX_SEG];
+    VGfloat gdata[MAX_SEG*3*2];
+    int dcount;
+    int scount;
+} vg_path_t;
+
+static cairo_status_t
+_vg_move_to (void          *closure,
+            const cairo_point_t *point)
+{
+    vg_path_t *path = closure;
+    double x = _cairo_fixed_to_double (point->x);
+    double y = _cairo_fixed_to_double (point->y);
+
+    if (path->ctm_inverse)
+       cairo_matrix_transform_point (path->ctm_inverse, &x, &y);
+
+    path->gseg[path->scount++] = VG_MOVE_TO;
+    path->gdata[path->dcount++] = x;
+    path->gdata[path->dcount++] = y;
+
+    if (path->scount >= MAX_SEG-1) {
+       vgAppendPathData (path->path, path->scount, path->gseg, path->gdata);
+       path->scount = 0;
+       path->dcount = 0;
+    }
+
+    CHECK_VG_ERRORS();
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_vg_line_to (void          *closure,
+            const cairo_point_t *point)
+{
+    vg_path_t *path = closure;
+    double x = _cairo_fixed_to_double (point->x);
+    double y = _cairo_fixed_to_double (point->y);
+
+    if (path->ctm_inverse)
+       cairo_matrix_transform_point (path->ctm_inverse, &x, &y);
+
+    path->gseg[path->scount++] = VG_LINE_TO;
+    path->gdata[path->dcount++] = x;
+    path->gdata[path->dcount++] = y;
+
+    if (path->scount >= MAX_SEG-1) {
+       vgAppendPathData (path->path, path->scount, path->gseg, path->gdata);
+       path->scount = 0;
+       path->dcount = 0;
+    }
+
+    CHECK_VG_ERRORS();
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_vg_curve_to (void          *closure,
+             const cairo_point_t *p0,
+             const cairo_point_t *p1,
+             const cairo_point_t *p2)
+{
+    vg_path_t *path = closure;
+    double x0 = _cairo_fixed_to_double (p0->x);
+    double y0 = _cairo_fixed_to_double (p0->y);
+    double x1 = _cairo_fixed_to_double (p1->x);
+    double y1 = _cairo_fixed_to_double (p1->y);
+    double x2 = _cairo_fixed_to_double (p2->x);
+    double y2 = _cairo_fixed_to_double (p2->y);
+
+    if (path->ctm_inverse) {
+       cairo_matrix_transform_point (path->ctm_inverse, &x0, &y0);
+       cairo_matrix_transform_point (path->ctm_inverse, &x1, &y1);
+       cairo_matrix_transform_point (path->ctm_inverse, &x2, &y2);
+    }
+
+    path->gseg[path->scount++] = VG_CUBIC_TO;
+    path->gdata[path->dcount++] = x0;
+    path->gdata[path->dcount++] = y0;
+    path->gdata[path->dcount++] = x1;
+    path->gdata[path->dcount++] = y1;
+    path->gdata[path->dcount++] = x2;
+    path->gdata[path->dcount++] = y2;
+
+    if (path->scount >= MAX_SEG-1) {
+       vgAppendPathData(path->path, path->scount, path->gseg, path->gdata);
+       path->scount = 0;
+       path->dcount = 0;
+    }
+
+    CHECK_VG_ERRORS();
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_vg_close_path (void *closure)
+{
+    vg_path_t *path = closure;
+
+    path->gseg[path->scount++] = VG_CLOSE_PATH;
+
+    if (path->scount >= MAX_SEG-1) {
+       vgAppendPathData (path->path, path->scount, path->gseg, path->gdata);
+       path->scount = 0;
+       path->dcount = 0;
+    }
+
+    CHECK_VG_ERRORS();
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_vg_path_from_cairo (vg_path_t    *vg_path,
+                    const cairo_path_fixed_t *path)
+{
+    cairo_status_t status;
+
+    vg_path->scount = 0;
+    vg_path->dcount = 0;
+
+    status = _cairo_path_fixed_interpret (path,
+                                         _vg_move_to,
+                                         _vg_line_to,
+                                         _vg_curve_to,
+                                         _vg_close_path,
+                                         vg_path);
+    assert (status == CAIRO_STATUS_SUCCESS);
+
+    vgAppendPathData (vg_path->path,
+                     vg_path->scount, vg_path->gseg, vg_path->gdata);
+    CHECK_VG_ERRORS();
+}
+
+static cairo_bool_t
+_vg_is_supported_operator (cairo_operator_t op)
+{
+    switch ((int) op) {
+    case CAIRO_OPERATOR_SOURCE:
+    case CAIRO_OPERATOR_OVER:
+    case CAIRO_OPERATOR_IN:
+    case CAIRO_OPERATOR_DEST_OVER:
+    case CAIRO_OPERATOR_DEST_IN:
+    case CAIRO_OPERATOR_ADD:
+       return TRUE;
+
+    default:
+       return FALSE;
+    }
+}
+
+static VGBlendMode
+_vg_operator (cairo_operator_t op)
+{
+    switch ((int) op) {
+    case CAIRO_OPERATOR_SOURCE:
+       return VG_BLEND_SRC;
+    case CAIRO_OPERATOR_OVER:
+       return VG_BLEND_SRC_OVER;
+    case CAIRO_OPERATOR_IN:
+       return VG_BLEND_SRC_IN;
+    case CAIRO_OPERATOR_DEST_OVER:
+       return VG_BLEND_DST_OVER;
+    case CAIRO_OPERATOR_DEST_IN:
+       return VG_BLEND_DST_IN;
+    case CAIRO_OPERATOR_ADD:
+       return VG_BLEND_ADDITIVE;
+    default:
+       ASSERT_NOT_REACHED;
+       return VG_BLEND_SRC_OVER;
+    }
+}
+
+static VGFillRule
+_vg_fill_rule_from_cairo (cairo_fill_rule_t rule)
+{
+    switch (rule) {
+    case CAIRO_FILL_RULE_EVEN_ODD: return VG_EVEN_ODD;
+    case CAIRO_FILL_RULE_WINDING: return VG_NON_ZERO;
+    }
+
+    ASSERT_NOT_REACHED;
+    return VG_NON_ZERO;
+}
+
+static VGRenderingQuality
+_vg_rendering_quality_from_cairo (cairo_antialias_t aa)
+{
+    switch (aa) {
+    case CAIRO_ANTIALIAS_DEFAULT:
+    case CAIRO_ANTIALIAS_SUBPIXEL:
+    case CAIRO_ANTIALIAS_GOOD:
+    case CAIRO_ANTIALIAS_BEST:
+       return VG_RENDERING_QUALITY_BETTER;
+
+    case CAIRO_ANTIALIAS_GRAY:
+    case CAIRO_ANTIALIAS_FAST:
+       return VG_RENDERING_QUALITY_FASTER;
+
+    case CAIRO_ANTIALIAS_NONE:
+       return VG_RENDERING_QUALITY_NONANTIALIASED;
+    }
+
+    ASSERT_NOT_REACHED;
+    return VG_RENDERING_QUALITY_BETTER;
+}
+
+static VGCapStyle
+_vg_line_cap_from_cairo (cairo_line_cap_t cap)
+{
+    switch (cap) {
+    case CAIRO_LINE_CAP_BUTT:   return VG_CAP_BUTT;
+    case CAIRO_LINE_CAP_ROUND:  return VG_CAP_ROUND;
+    case CAIRO_LINE_CAP_SQUARE: return VG_CAP_SQUARE;
+    }
+
+    ASSERT_NOT_REACHED;
+    return VG_CAP_BUTT;
+}
+
+static VGJoinStyle
+_vg_line_join_from_cairo (cairo_line_join_t join)
+{
+    switch (join) {
+    case CAIRO_LINE_JOIN_MITER: return VG_JOIN_MITER;
+    case CAIRO_LINE_JOIN_ROUND: return VG_JOIN_ROUND;
+    case CAIRO_LINE_JOIN_BEVEL: return VG_JOIN_BEVEL;
+    }
+
+    ASSERT_NOT_REACHED;
+    return VG_JOIN_MITER;
+}
+
+static void
+_vg_matrix_from_cairo (VGfloat *dst, const cairo_matrix_t *src)
+{
+    dst[0] = /* sx  */ src->xx;
+    dst[1] = /* shy */ src->yx;
+    dst[2] = /* w0  */ 0;
+    dst[3] = /* shx */ src->xy;
+    dst[4] = /* sy  */ src->yy;
+    dst[5] = /* w1  */ 0;
+    dst[6] = /* tx  */ src->x0;
+    dst[7] = /* ty  */ src->y0;
+    dst[8] = /* w2  */ 0;
+}
+
+static cairo_status_t
+_vg_setup_gradient_stops (cairo_vg_context_t *context,
+                         const cairo_gradient_pattern_t *pattern)
+{
+    VGint numstops = pattern->n_stops;
+    VGfloat *stops, stack_stops[CAIRO_STACK_ARRAY_LENGTH (VGfloat)];
+    int i;
+
+    if (numstops*5 < ARRAY_LENGTH (stack_stops)) {
+       stops = stack_stops;
+    } else {
+       stops = _cairo_malloc_ab (numstops, 5*sizeof (VGfloat));
+       if (unlikely (stops == NULL))
+           return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+    }
+
+    for (i = 0; i < numstops; i++) {
+       stops[i*5 + 0] = pattern->stops[i].offset;
+       stops[i*5 + 1] = pattern->stops[i].color.red;
+       stops[i*5 + 2] = pattern->stops[i].color.green;
+       stops[i*5 + 3] = pattern->stops[i].color.blue;
+       stops[i*5 + 4] = pattern->stops[i].color.alpha * context->alpha;
+    }
+
+    vgSetParameterfv (context->paint,
+                     VG_PAINT_COLOR_RAMP_STOPS, numstops * 5, stops);
+
+    if (stops != stack_stops)
+       free (stops);
+
+    CHECK_VG_ERRORS();
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_vg_set_source_matrix (const cairo_pattern_t *pat)
+{
+    cairo_matrix_t mat;
+    cairo_status_t status;
+    VGfloat vmat[9];
+
+    mat = pat->matrix;
+    status = cairo_matrix_invert (&mat);
+    assert (status == CAIRO_STATUS_SUCCESS);
+
+    _vg_matrix_from_cairo (vmat, &mat);
+
+    vgSeti (VG_MATRIX_MODE, VG_MATRIX_FILL_PAINT_TO_USER);
+    vgLoadMatrix (vmat);
+    vgSeti (VG_MATRIX_MODE, VG_MATRIX_STROKE_PAINT_TO_USER);
+    vgLoadMatrix (vmat);
+    vgSeti (VG_MATRIX_MODE, VG_MATRIX_PATH_USER_TO_SURFACE);
+
+    CHECK_VG_ERRORS();
+}
+
+static cairo_status_t
+_vg_setup_linear_source (cairo_vg_context_t *context,
+                        const cairo_linear_pattern_t *lpat)
+{
+    VGfloat linear[4];
+
+    linear[0] = lpat->pd1.x;
+    linear[1] = lpat->pd1.y;
+    linear[2] = lpat->pd2.x;
+    linear[3] = lpat->pd2.y;
+
+    vgSetParameteri (context->paint,
+                    VG_PAINT_COLOR_RAMP_SPREAD_MODE,
+                    VG_COLOR_RAMP_SPREAD_PAD);
+    vgSetParameteri (context->paint,
+                    VG_PAINT_TYPE,
+                    VG_PAINT_TYPE_LINEAR_GRADIENT);
+    vgSetParameterfv (context->paint,
+                     VG_PAINT_LINEAR_GRADIENT, 4, linear);
+
+    _vg_set_source_matrix (&lpat->base.base);
+
+    CHECK_VG_ERRORS();
+    return _vg_setup_gradient_stops (context, &lpat->base);
+
+}
+
+static cairo_status_t
+_vg_setup_radial_source (cairo_vg_context_t *context,
+                        const cairo_radial_pattern_t *rpat)
+{
+    VGfloat radial[5];
+
+    radial[0] = rpat->cd1.center.x;
+    radial[1] = rpat->cd1.center.y;
+    radial[2] = rpat->cd2.center.x;
+    radial[3] = rpat->cd2.center.y;
+    radial[4] = rpat->cd2.radius;
+
+    vgSetParameteri (context->paint,
+                    VG_PAINT_COLOR_RAMP_SPREAD_MODE, VG_COLOR_RAMP_SPREAD_PAD);
+    vgSetParameteri (context->paint,
+                    VG_PAINT_TYPE, VG_PAINT_TYPE_RADIAL_GRADIENT);
+    vgSetParameterfv (context->paint,
+                     VG_PAINT_RADIAL_GRADIENT, 5, radial);
+
+    _vg_set_source_matrix (&rpat->base.base);
+
+    /* FIXME: copy/adapt fixes from SVG backend to add inner radius */
+
+    CHECK_VG_ERRORS();
+    return _vg_setup_gradient_stops (context, &rpat->base);
+}
+
+static cairo_status_t
+_vg_setup_solid_source (cairo_vg_context_t *context,
+                       const cairo_solid_pattern_t *spat)
+{
+    VGfloat color[] = {
+       spat->color.red,
+       spat->color.green,
+       spat->color.blue,
+       spat->color.alpha * context->alpha
+    };
+
+    vgSetParameteri (context->paint, VG_PAINT_TYPE, VG_PAINT_TYPE_COLOR);
+    vgSetParameterfv (context->paint, VG_PAINT_COLOR, 4, color);
+
+    CHECK_VG_ERRORS();
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_vg_surface_t *
+_vg_clone_recording_surface (cairo_vg_context_t *context,
+                       cairo_surface_t *surface)
+{
+    VGImage vg_image;
+    VGImageFormat format;
+    cairo_status_t status;
+    cairo_rectangle_int_t extents;
+    cairo_vg_surface_t *clone;
+
+    status = _cairo_surface_get_extents (surface, &extents);
+    if (status)
+       return NULL;
+
+    if (extents.width > vgGeti (VG_MAX_IMAGE_WIDTH) ||
+       extents.height > vgGeti (VG_MAX_IMAGE_HEIGHT))
+    {
+       return NULL;
+    }
+
+    format = _vg_format_for_content (surface->content);
+
+    /* NONALIASED, FASTER, BETTER */
+    vg_image = vgCreateImage (format,
+                             extents.width, extents.height,
+                             VG_IMAGE_QUALITY_FASTER);
+    clone = (cairo_vg_surface_t *)
+       _vg_surface_create_internal (context, vg_image, format,
+                                    extents.width, extents.height);
+    cairo_surface_set_device_offset (&clone->base, -extents.x, -extents.y);
+
+    status = _cairo_recording_surface_replay (surface, &clone->base);
+    if (unlikely (status)) {
+       cairo_surface_destroy (&clone->base);
+       return (cairo_vg_surface_t *) _cairo_surface_create_in_error (status);
+    }
+
+    return clone;
+}
+
+static cairo_vg_surface_t *
+_vg_clone_image_surface (cairo_vg_context_t *context,
+                        cairo_surface_t *surface)
+{
+    cairo_image_surface_t *image;
+    void *image_extra;
+    cairo_status_t status;
+    VGImage vg_image;
+    VGImageFormat format;
+    cairo_rectangle_int_t extents;
+    cairo_vg_surface_t *clone;
+
+    if (surface->backend->acquire_source_image == NULL)
+       return NULL;
+
+    status = _cairo_surface_get_extents (surface, &extents);
+    if (status)
+       return NULL;
+
+    if (extents.width > vgGeti (VG_MAX_IMAGE_WIDTH) ||
+       extents.height > vgGeti (VG_MAX_IMAGE_HEIGHT))
+    {
+       return NULL;
+    }
+
+    status = _cairo_surface_acquire_source_image (surface,
+                                                 &image, &image_extra);
+    if (unlikely (status))
+       return (cairo_vg_surface_t *) _cairo_surface_create_in_error (status);
+
+    format = _vg_format_from_pixman (image->pixman_format);
+    if (format == 0)
+       format = _vg_format_for_content (image->base.content);
+
+    /* NONALIASED, FASTER, BETTER */
+    vg_image = vgCreateImage (format,
+                             image->width, image->height,
+                             VG_IMAGE_QUALITY_FASTER);
+    clone = (cairo_vg_surface_t *)
+       _vg_surface_create_internal (context, vg_image, format,
+                                   image->width, image->height);
+    if (unlikely (clone->base.status))
+       return clone;
+
+    vgImageSubData (clone->image,
+                   image->data, image->stride,
+                   format, 0, 0, image->width, image->height);
+
+    _cairo_surface_release_source_image (surface, image, image_extra);
+
+    return clone;
+}
+
+static void
+_vg_surface_remove_from_cache (cairo_surface_t *abstract_surface)
+{
+    cairo_vg_surface_t *surface = (cairo_vg_surface_t *) abstract_surface;
+
+    if (surface->snapshot_cache_entry.hash) {
+       cairo_vg_context_t *context;
+
+       context = _vg_context_lock (surface->context);
+       _cairo_cache_remove (&context->snapshot_cache,
+                            &surface->snapshot_cache_entry);
+       _vg_context_unlock (context);
+
+       surface->snapshot_cache_entry.hash = 0;
+    }
+}
+
+static cairo_status_t
+_vg_setup_surface_source (cairo_vg_context_t *context,
+                         const cairo_surface_pattern_t *spat)
+{
+    cairo_surface_t *snapshot;
+    cairo_vg_surface_t *clone;
+    cairo_status_t status;
+
+    snapshot = _cairo_surface_has_snapshot (spat->surface,
+                                           &cairo_vg_surface_backend);
+    if (snapshot != NULL) {
+       clone = (cairo_vg_surface_t *) cairo_surface_reference (snapshot);
+       goto DONE;
+    }
+
+    if (_cairo_surface_is_recording (spat->surface))
+       clone = _vg_clone_recording_surface (context, spat->surface);
+    else
+       clone = _vg_clone_image_surface (context, spat->surface);
+    if (clone == NULL)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+    if (unlikely (clone->base.status))
+       return clone->base.status;
+
+    clone->snapshot_cache_entry.hash = clone->base.unique_id;
+    status = _cairo_cache_insert (&context->snapshot_cache,
+                                 &clone->snapshot_cache_entry);
+    if (unlikely (status)) {
+       clone->snapshot_cache_entry.hash = 0;
+       cairo_surface_destroy (&clone->base);
+       return status;
+    }
+
+    _cairo_surface_attach_snapshot (spat->surface, &clone->base,
+                                   _vg_surface_remove_from_cache);
+
+DONE:
+    cairo_surface_destroy (&context->source->base);
+    context->source = clone;
+
+    vgSetParameteri (context->paint, VG_PAINT_TYPE, VG_PAINT_TYPE_PATTERN);
+
+    switch (spat->base.extend) {
+    case CAIRO_EXTEND_PAD:
+       vgSetParameteri (context->paint,
+                        VG_PAINT_PATTERN_TILING_MODE,
+                        VG_TILE_PAD);
+       break;
+
+    case CAIRO_EXTEND_NONE:
+       vgSetParameteri (context->paint,
+                        VG_PAINT_PATTERN_TILING_MODE,
+                        VG_TILE_FILL);
+       {
+           VGfloat color[] = {0,0,0,0};
+           vgSetfv (VG_TILE_FILL_COLOR, 4, color);
+       }
+       break;
+
+    case CAIRO_EXTEND_REPEAT:
+       vgSetParameteri (context->paint,
+                        VG_PAINT_PATTERN_TILING_MODE,
+                        VG_TILE_REPEAT);
+       break;
+
+    default:
+       ASSERT_NOT_REACHED;
+    case CAIRO_EXTEND_REFLECT:
+       vgSetParameteri (context->paint,
+                        VG_PAINT_PATTERN_TILING_MODE,
+                        VG_TILE_REFLECT);
+       break;
+    }
+    vgPaintPattern (context->paint, context->source->image);
+
+    _vg_set_source_matrix (&spat->base);
+
+    CHECK_VG_ERRORS();
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+setup_source (cairo_vg_context_t *context,
+             const cairo_pattern_t *source)
+{
+    switch (source->type) {
+    case CAIRO_PATTERN_TYPE_SOLID:
+       return _vg_setup_solid_source (context,
+                                      (cairo_solid_pattern_t *) source);
+    case CAIRO_PATTERN_TYPE_LINEAR:
+       return _vg_setup_linear_source (context,
+                                       (cairo_linear_pattern_t *) source);
+    case CAIRO_PATTERN_TYPE_RADIAL:
+       return _vg_setup_radial_source (context,
+                                       (cairo_radial_pattern_t *) source);
+    case CAIRO_PATTERN_TYPE_SURFACE:
+       return _vg_setup_surface_source (context,
+                                        (cairo_surface_pattern_t *) source);
+    default:
+       ASSERT_NOT_REACHED;
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+    }
+}
+
+static cairo_int_status_t
+_vg_surface_stroke (void                 *abstract_surface,
+                   cairo_operator_t      op,
+                   const cairo_pattern_t*source,
+                   cairo_path_fixed_t   *path,
+                   cairo_stroke_style_t *style,
+                   cairo_matrix_t       *ctm,
+                   cairo_matrix_t       *ctm_inverse,
+                   double                tolerance,
+                   cairo_antialias_t     antialias,
+                   cairo_clip_t         *clip)
+{
+    cairo_vg_surface_t *surface = abstract_surface;
+    cairo_vg_context_t *context;
+    cairo_status_t status;
+    VGfloat state[9];
+    VGfloat strokeTransform[9];
+    vg_path_t vg_path;
+
+    if (! _vg_is_supported_operator (op))
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    context = _vg_context_lock (surface->context);
+    status = _vg_context_set_target (context, surface);
+    if (status) {
+       _vg_context_unlock (context);
+       return status;
+    }
+
+    status = setup_source (context, source);
+    if (status) {
+       _vg_context_unlock (context);
+       return status;
+    }
+
+    status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
+    if (unlikely (status)) {
+       _vg_context_unlock (context);
+       return status;
+    }
+
+    vg_path.path = vgCreatePath (VG_PATH_FORMAT_STANDARD,
+                                VG_PATH_DATATYPE_F,
+                                1, 0, 0, 0,
+                                VG_PATH_CAPABILITY_ALL);
+
+    vgGetMatrix (state);
+    _vg_matrix_from_cairo (strokeTransform, ctm);
+    vgMultMatrix (strokeTransform);
+
+    vg_path.ctm_inverse = ctm_inverse;
+
+    _vg_path_from_cairo (&vg_path, path);
+
+    /* XXX DASH_PATTERN, DASH_PHASE */
+    vgSetf (VG_STROKE_LINE_WIDTH, style->line_width);
+    vgSetf (VG_STROKE_MITER_LIMIT, style->miter_limit);
+    vgSetf (VG_STROKE_JOIN_STYLE, _vg_line_join_from_cairo (style->line_join));
+    vgSetf (VG_STROKE_CAP_STYLE, _vg_line_cap_from_cairo (style->line_cap));
+
+    vgSeti (VG_BLEND_MODE, _vg_operator (op));
+
+    vgSetPaint (context->paint, VG_STROKE_PATH);
+
+    vgDrawPath (vg_path.path, VG_STROKE_PATH);
+
+    vgDestroyPath (vg_path.path);
+
+    vgLoadMatrix (state);
+
+    CHECK_VG_ERRORS();
+    _vg_context_unlock (context);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_vg_surface_fill (void                  *abstract_surface,
+                 cairo_operator_t       op,
+                 const cairo_pattern_t *source,
+                 cairo_path_fixed_t    *path,
+                 cairo_fill_rule_t      fill_rule,
+                 double                 tolerance,
+                 cairo_antialias_t      antialias,
+                 cairo_clip_t          *clip)
+{
+    cairo_vg_surface_t *surface = abstract_surface;
+    cairo_vg_context_t *context;
+    cairo_status_t status;
+    vg_path_t vg_path;
+
+    if (op == CAIRO_OPERATOR_DEST)
+       return CAIRO_STATUS_SUCCESS;
+
+    if (! _vg_is_supported_operator (op))
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    context = _vg_context_lock (surface->context);
+    status = _vg_context_set_target (context, surface);
+    if (status) {
+       _vg_context_unlock (context);
+       return status;
+    }
+
+    status = setup_source (context, source);
+    if (status) {
+       _vg_context_unlock (context);
+       return status;
+    }
+
+    status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
+    if (unlikely (status)) {
+       _vg_context_unlock (context);
+       return status;
+    }
+
+    vg_path.path = vgCreatePath (VG_PATH_FORMAT_STANDARD,
+                                VG_PATH_DATATYPE_F,
+                                1, 0,
+                                0, 0,
+                                VG_PATH_CAPABILITY_ALL);
+    vg_path.ctm_inverse = NULL;
+
+    _vg_path_from_cairo (&vg_path, path);
+
+    /* XXX tolerance */
+
+    vgSeti (VG_BLEND_MODE, _vg_operator (op));
+    vgSetf (VG_FILL_RULE, _vg_fill_rule_from_cairo (fill_rule));
+    vgSetf (VG_RENDERING_QUALITY, _vg_rendering_quality_from_cairo (antialias));
+
+    vgSetPaint (context->paint, VG_FILL_PATH);
+
+    vgDrawPath (vg_path.path, VG_FILL_PATH);
+
+    vgDestroyPath (vg_path.path);
+
+    _vg_context_unlock (context);
+
+    CHECK_VG_ERRORS();
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_vg_surface_paint (void             *abstract_surface,
+                  cairo_operator_t  op,
+                  const cairo_pattern_t  *source,
+                  cairo_clip_t      *clip)
+{
+    cairo_vg_surface_t *surface = abstract_surface;
+    cairo_vg_context_t *context;
+    cairo_status_t status;
+
+    if (op == CAIRO_OPERATOR_DEST)
+       return CAIRO_STATUS_SUCCESS;
+
+    if (! _vg_is_supported_operator (op))
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    context = _vg_context_lock (surface->context);
+    status = _vg_context_set_target (context, surface);
+    if (status) {
+       _vg_context_unlock (context);
+       return status;
+    }
+
+    status = setup_source (context, source);
+    if (status) {
+       _vg_context_unlock (context);
+       return status;
+    }
+
+    status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
+    if (unlikely (status)) {
+       _vg_context_unlock (context);
+       return status;
+    }
+
+    vgSeti (VG_BLEND_MODE, _vg_operator (op));
+    vgSetPaint (context->paint, VG_FILL_PATH);
+
+    { /* creating a rectangular path that should cover the extent */
+       VGubyte segs[] = {
+           VG_MOVE_TO_ABS, VG_LINE_TO_ABS,
+           VG_LINE_TO_ABS, VG_LINE_TO_ABS,
+           VG_CLOSE_PATH
+       };
+       VGfloat data[] = {
+           0, 0,
+           surface->width, 0,
+           surface->width, surface->height,
+           0, surface->height
+       };
+       VGPath fullext;
+
+       fullext = vgCreatePath (VG_PATH_FORMAT_STANDARD, VG_PATH_DATATYPE_F,
+                               1,0,0,0, VG_PATH_CAPABILITY_ALL);
+       vgAppendPathData (fullext, sizeof(segs), segs, data);
+
+       vgDrawPath (fullext, VG_FILL_PATH);
+
+       vgDestroyPath (fullext);
+    }
+
+    _vg_context_unlock (context);
+
+    CHECK_VG_ERRORS();
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_vg_surface_mask (void                   *abstract_surface,
+                 cairo_operator_t       op,
+                 const cairo_pattern_t  *source,
+                 const cairo_pattern_t  *mask,
+                 cairo_clip_t           *clip)
+{
+    cairo_vg_surface_t *surface = abstract_surface;
+    cairo_status_t status;
+
+    if (! _vg_is_supported_operator (op))
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    /* Handle paint-with-alpha to do fades cheaply */
+    if (mask->type == CAIRO_PATTERN_TYPE_SOLID) {
+       cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) mask;
+       cairo_vg_context_t *context = _vg_context_lock (surface->context);
+       double alpha = context->alpha;
+
+       context->alpha = solid->color.alpha;
+       status = _vg_surface_paint (abstract_surface, op, source, clip);
+       context->alpha = alpha;
+
+       _vg_context_unlock (context);
+
+       return status;
+    }
+
+    return CAIRO_INT_STATUS_UNSUPPORTED;
+}
+
+static void
+_vg_surface_get_font_options (void                  *abstract_surface,
+                             cairo_font_options_t  *options)
+{
+    _cairo_font_options_init_default (options);
+
+    cairo_font_options_set_hint_metrics (options, CAIRO_HINT_METRICS_ON);
+    _cairo_font_options_set_round_glyph_positions (options, CAIRO_ROUND_GLYPH_POS_OFF);
+}
+
+static cairo_int_status_t
+_vg_surface_show_glyphs (void                  *abstract_surface,
+                        cairo_operator_t        op,
+                        const cairo_pattern_t  *source,
+                        cairo_glyph_t          *glyphs,
+                        int                     num_glyphs,
+                        cairo_scaled_font_t    *scaled_font,
+                        cairo_clip_t           *clip,
+                        int                    *remaining_glyphs)
+{
+    cairo_status_t status = CAIRO_STATUS_SUCCESS;
+    cairo_path_fixed_t path;
+
+    if (num_glyphs <= 0)
+       return CAIRO_STATUS_SUCCESS;
+
+    _cairo_path_fixed_init (&path);
+
+    /* XXX Glyph cache! OpenVG font support in 1.1? */
+
+    status = _cairo_scaled_font_glyph_path (scaled_font,
+                                           glyphs, num_glyphs,
+                                           &path);
+    if (unlikely (status))
+       goto BAIL;
+
+    status = _vg_surface_fill (abstract_surface,
+                              op, source, &path,
+                              CAIRO_FILL_RULE_WINDING,
+                              CAIRO_GSTATE_TOLERANCE_DEFAULT,
+                              CAIRO_ANTIALIAS_DEFAULT,
+                              clip);
+BAIL:
+    _cairo_path_fixed_fini (&path);
+    return status;
+}
+
+static inline int
+multiply_alpha (int alpha, int color)
+{
+    int temp = alpha * color + 0x80;
+    return (temp + (temp >> 8)) >> 8;
+}
+
+static void
+premultiply_argb (uint8_t   *data,
+                 int        width,
+                 int        height,
+                 int        stride)
+{
+    int i;
+
+    while (height --) {
+       uint32_t *row = (uint32_t *) data;
+
+       for (i = 0; i < width; i++) {
+           uint32_t p = row[i];
+           uint8_t  alpha;
+
+           alpha = p >> 24;
+           if (alpha == 0) {
+                row[i] = 0;
+           } else if (alpha != 0xff) {
+               uint8_t r = multiply_alpha (alpha, (p >> 16) & 0xff);
+               uint8_t g = multiply_alpha (alpha, (p >>  8) & 0xff);
+               uint8_t b = multiply_alpha (alpha, (p >>  0) & 0xff);
+               row[i] = (alpha << 24) | (r << 16) | (g << 8) | (b << 0);
+           }
+       }
+
+       data += stride;
+    }
+}
+
+static cairo_int_status_t
+_vg_get_image (cairo_vg_surface_t *surface,
+              int x, int y,
+              int width, int height,
+              cairo_image_surface_t **image_out)
+{
+    cairo_image_surface_t *image;
+    pixman_image_t *pixman_image;
+    pixman_format_code_t pixman_format;
+    cairo_bool_t needs_premultiply;
+
+    pixman_format = _vg_format_to_pixman (surface->format,
+                                         &needs_premultiply);
+    if (pixman_format == 0)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    pixman_image = pixman_image_create_bits (pixman_format,
+                                            width, height,
+                                            NULL, 0);
+    if (unlikely (pixman_image == NULL))
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    vgFinish ();
+    CHECK_VG_ERRORS();
+
+    vgGetImageSubData (surface->image,
+                      pixman_image_get_data (pixman_image),
+                      pixman_image_get_stride (pixman_image),
+                      surface->format,
+                      x, y, width, height);
+
+    image = (cairo_image_surface_t *)
+       _cairo_image_surface_create_for_pixman_image (pixman_image,
+                                                     pixman_format);
+    if (unlikely (image->base.status)) {
+       pixman_image_unref (pixman_image);
+       return image->base.status;
+    }
+
+    if (needs_premultiply)
+       premultiply_argb (image->data, width, height, image->stride);
+
+    *image_out = image;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_vg_surface_acquire_source_image (void *abstract_surface,
+                                 cairo_image_surface_t **image_out,
+                                 void                  **image_extra)
+{
+    cairo_vg_surface_t *surface = abstract_surface;
+
+    CHECK_VG_ERRORS();
+    *image_extra = NULL;
+    return _vg_get_image (surface,
+                         0, 0, surface->width, surface->height,
+                         image_out);
+}
+
+static void
+_vg_surface_release_source_image (void                    *abstract_surface,
+                                 cairo_image_surface_t   *image,
+                                 void                    *image_extra)
+{
+    cairo_surface_destroy (&image->base);
+}
+
+static cairo_status_t
+_vg_surface_acquire_dest_image (void                    *abstract_surface,
+                               cairo_rectangle_int_t   *interest_rect,
+                               cairo_image_surface_t  **image_out,
+                               cairo_rectangle_int_t   *image_rect_out,
+                               void                   **image_extra)
+{
+    cairo_vg_surface_t *surface =  abstract_surface;
+
+    *image_rect_out = *interest_rect;
+    *image_extra = NULL;
+    return _vg_get_image (surface,
+                         interest_rect->x, interest_rect->y,
+                         interest_rect->width, interest_rect->height,
+                         image_out);
+}
+
+static void
+unpremultiply_argb (uint8_t *data,
+                   int      width,
+                   int      height,
+                   int      stride)
+{
+    int i;
+
+    while (height--) {
+       uint32_t *row = (uint32_t *) data;
+
+       for (i = 0; i < width; i ++) {
+           uint32_t p = row[i];
+           uint8_t  alpha;
+
+           alpha = p >> 24;
+           if (alpha == 0) {
+               row[i] = 0;
+           } else if (alpha != 0xff) {
+               uint8_t r = (((p >> 16) & 0xff) * 255 + alpha / 2) / alpha;
+               uint8_t g = (((p >>  8) & 0xff) * 255 + alpha / 2) / alpha;
+               uint8_t b = (((p >>  0) & 0xff) * 255 + alpha / 2) / alpha;
+               row[i] = (alpha << 24) | (r << 16) | (g << 8) | (b << 0);
+           }
+       }
+
+       data += stride;
+    }
+}
+
+static void
+_vg_surface_release_dest_image (void                    *abstract_surface,
+                               cairo_rectangle_int_t   *interest_rect,
+                               cairo_image_surface_t   *image,
+                               cairo_rectangle_int_t   *image_rect,
+                               void                    *image_extra)
+{
+    cairo_vg_surface_t *surface = abstract_surface;
+    cairo_bool_t needs_unpremultiply;
+
+    _vg_format_to_pixman (surface->format, &needs_unpremultiply);
+    if (needs_unpremultiply) {
+       unpremultiply_argb (image->data,
+                           image->width, image->height,
+                           image->stride);
+    }
+
+    vgImageSubData (surface->image,
+                   image->data, image->stride,
+                   surface->format,
+                   image_rect->x, image_rect->y,
+                   image_rect->width, image_rect->height);
+
+    cairo_surface_destroy (&image->base);
+}
+
+static cairo_status_t
+_vg_surface_finish (void *abstract_surface)
+{
+    cairo_vg_surface_t *surface = abstract_surface;
+    cairo_vg_context_t *context = _vg_context_lock (surface->context);
+
+    if (surface->snapshot_cache_entry.hash) {
+       _cairo_cache_remove (&context->snapshot_cache,
+                            &surface->snapshot_cache_entry);
+
+       surface->snapshot_cache_entry.hash = 0;
+    }
+
+    _cairo_surface_clipper_reset (&surface->clipper);
+
+    if (surface->own_image)
+       vgDestroyImage (surface->image);
+
+    _vg_context_destroy_target (context, surface);
+
+    _vg_context_unlock (context);
+    _vg_context_destroy (context);
+
+    CHECK_VG_ERRORS();
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static const cairo_surface_backend_t cairo_vg_surface_backend = {
+    CAIRO_SURFACE_TYPE_VG,
+    _vg_surface_finish,
+    _cairo_default_context_create, /* XXX */
+
+    _vg_surface_create_similar,
+    NULL, /* create similar image */
+    NULL, /* map to image */
+    NULL, /* unmap image */
+
+    _vg_surface_acquire_source_image,
+    _vg_surface_release_source_image,
+    _vg_surface_acquire_dest_image,
+    _vg_surface_release_dest_image,
+
+    NULL, /* clone_similar */
+    NULL, /* composite */
+    NULL, /* fill_rectangles */
+    NULL, /* composite_trapezoids */
+    NULL, /* create_span_renderer */
+    NULL, /* check_span_renderer */
+
+    NULL, /* copy_page */
+    NULL, /* show_page */
+    _vg_surface_get_extents,
+    NULL, /* old_show_glyphs */
+    _vg_surface_get_font_options, /* get_font_options */
+    NULL, /* flush */
+    NULL, /* mark_dirty_rectangle */
+    NULL, /* scaled_font_fini */
+    NULL, /* scaled_glyph_fini */
+
+    _vg_surface_paint,
+    _vg_surface_mask,
+    _vg_surface_stroke,
+    _vg_surface_fill,
+    _vg_surface_show_glyphs,
+
+    NULL, /* snapshot */
+    NULL, /* is_similar */
+};
+
+static cairo_surface_t *
+_vg_surface_create_internal (cairo_vg_context_t *context,
+                            VGImage image,
+                            VGImageFormat format,
+                            int width, int height)
+{
+    cairo_vg_surface_t *surface;
+
+    surface = malloc (sizeof (cairo_vg_surface_t));
+    if (unlikely (surface == NULL))
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+    surface->context = _vg_context_reference (context);
+
+    surface->image  = image;
+    surface->format = format;
+
+    _cairo_surface_init (&surface->base,
+                        &cairo_vg_surface_backend,
+                        NULL, /* device */
+                        _vg_format_to_content (format));
+
+    surface->width  = width;
+    surface->height = height;
+
+    _cairo_surface_clipper_init (&surface->clipper,
+                                _vg_surface_clipper_intersect_clip_path);
+
+    surface->snapshot_cache_entry.hash = 0;
+
+    surface->target_id = 0;
+
+    CHECK_VG_ERRORS();
+    return &surface->base;
+}
+
+cairo_surface_t *
+cairo_vg_surface_create_for_image (cairo_vg_context_t *context,
+                                  VGImage image,
+                                  VGImageFormat format,
+                                  int width, int height)
+{
+    cairo_bool_t premult;
+
+    if (context->status)
+       return _cairo_surface_create_in_error (context->status);
+
+    if (image == VG_INVALID_HANDLE)
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+    if (_vg_format_to_pixman (format, &premult) == 0)
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
+
+    return _vg_surface_create_internal (context, image, format, width, height);
+}
+
+cairo_surface_t *
+cairo_vg_surface_create (cairo_vg_context_t *context,
+                        cairo_content_t  content,
+                        int              width,
+                        int              height)
+{
+    VGImage image;
+    VGImageFormat format;
+    cairo_surface_t *surface;
+
+    if (context->status)
+       return _cairo_surface_create_in_error (context->status);
+
+    if (! CAIRO_CONTENT_VALID (content))
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_CONTENT));
+
+    if (width > vgGeti (VG_MAX_IMAGE_WIDTH) ||
+       height > vgGeti (VG_MAX_IMAGE_HEIGHT))
+    {
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE));
+    }
+
+
+    format = _vg_format_for_content (content);
+    image = vgCreateImage (format, width, height, VG_IMAGE_QUALITY_BETTER);
+    if (image == VG_INVALID_HANDLE)
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+    surface = _vg_surface_create_internal (context,
+                                          image, format, width, height);
+    if (unlikely (surface->status))
+       return surface;
+
+    ((cairo_vg_surface_t *) surface)->own_image = TRUE;
+    return surface;
+}
+slim_hidden_def (cairo_vg_surface_create);
+
+VGImage
+cairo_vg_surface_get_image (cairo_surface_t *abstract_surface)
+{
+    cairo_vg_surface_t *surface;
+
+    if (abstract_surface->backend != &cairo_vg_surface_backend) {
+       _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+       return VG_INVALID_HANDLE;
+    }
+
+    surface = (cairo_vg_surface_t *) abstract_surface;
+    return surface->image;
+}
+
+int
+cairo_vg_surface_get_width (cairo_surface_t *abstract_surface)
+{
+    cairo_vg_surface_t *surface;
+
+    if (abstract_surface->backend != &cairo_vg_surface_backend) {
+       _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+       return 0;
+    }
+
+    surface = (cairo_vg_surface_t *) abstract_surface;
+    return surface->width;
+}
+
+int
+cairo_vg_surface_get_height (cairo_surface_t *abstract_surface)
+{
+    cairo_vg_surface_t *surface;
+
+    if (abstract_surface->backend != &cairo_vg_surface_backend) {
+       _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+       return 0;
+    }
+
+    surface = (cairo_vg_surface_t *) abstract_surface;
+    return surface->height;
+}
+
+VGImageFormat
+cairo_vg_surface_get_format (cairo_surface_t *abstract_surface)
+{
+    cairo_vg_surface_t *surface;
+
+    if (abstract_surface->backend != &cairo_vg_surface_backend) {
+       _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+       return 0;
+    }
+
+    surface = (cairo_vg_surface_t *) abstract_surface;
+    return surface->format;
+}
+
+/* GL specific context support :-(
+ *
+ * OpenVG like cairo defers creation of surface (and the necessary
+ * paraphernalia to the application.
+ */
+
+static const cairo_vg_context_t _vg_context_nil = {
+    CAIRO_STATUS_NO_MEMORY,
+    CAIRO_REFERENCE_COUNT_INVALID
+};
+
+static const cairo_vg_context_t _vg_context_nil_invalid_visual = {
+    CAIRO_STATUS_INVALID_VISUAL,
+    CAIRO_REFERENCE_COUNT_INVALID
+};
+
+#if CAIRO_HAS_GLX_FUNCTIONS
+#include <GL/glx.h>
+
+static cairo_status_t
+glx_create_target (cairo_vg_context_t *context,
+                  cairo_vg_surface_t *surface)
+{
+    /* XXX hmm, magic required for creating an FBO points to VGImage! */
+    return CAIRO_INT_STATUS_UNSUPPORTED;
+}
+
+static cairo_status_t
+glx_set_target (cairo_vg_context_t *context,
+               cairo_vg_surface_t *surface)
+{
+#if 0
+    glXMakeContextCurrent (context->display,
+                          (GLXDrawable) surface->target_id,
+                          (GLXDrawable) surface->target_id,
+                          context->context);
+#else
+    return CAIRO_INT_STATUS_UNSUPPORTED;
+#endif
+}
+
+static void
+glx_destroy_target (cairo_vg_context_t *context,
+                   cairo_vg_surface_t *surface)
+{
+}
+
+cairo_vg_context_t *
+cairo_vg_context_create_for_glx (Display *dpy, GLXContext ctx)
+{
+    cairo_vg_context_t *context;
+    cairo_status_t status;
+
+    context = malloc (sizeof (*context));
+    if (unlikely (context == NULL))
+       return (cairo_vg_context_t *) &_vg_context_nil;
+
+    context->display = dpy;
+    context->context = ctx;
+
+    context->create_target  = glx_create_target;
+    context->set_target     = glx_set_target;
+    context->destroy_target = glx_destroy_target;
+
+    status = _vg_context_init (context);
+    if (unlikely (status)) {
+       free (context);
+       return (cairo_vg_context_t *) &_vg_context_nil;
+    }
+
+    return context;
+}
+#endif
+
+#if CAIRO_HAS_EGL_FUNCTIONS
+static cairo_status_t
+egl_create_target (cairo_vg_context_t *context,
+                  cairo_vg_surface_t *surface)
+{
+    EGLSurface *egl_surface;
+#define RED 1
+#define GREEN 3
+#define BLUE 5
+#define ALPHA 7
+    int attribs[] = {
+       EGL_RED_SIZE, 0,
+       EGL_GREEN_SIZE, 0,
+       EGL_BLUE_SIZE, 0,
+       EGL_ALPHA_SIZE, 0,
+       EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
+       EGL_RENDERABLE_TYPE, EGL_OPENVG_BIT,
+       EGL_NONE
+    };
+    pixman_format_code_t pixman_format;
+    EGLConfig config;
+    int num_configs = 0;
+    cairo_bool_t needs_premultiply;
+
+    pixman_format = _vg_format_to_pixman (surface->format, &needs_premultiply);
+    if (pixman_format == 0)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    /* XXX no control over pixel ordering! */
+    attribs[RED]   = PIXMAN_FORMAT_R (pixman_format);
+    attribs[GREEN] = PIXMAN_FORMAT_G (pixman_format);
+    attribs[BLUE]  = PIXMAN_FORMAT_B (pixman_format);
+    attribs[ALPHA] = PIXMAN_FORMAT_A (pixman_format);
+
+    if (! eglChooseConfig (context->display,
+                          attribs,
+                          &config, 1, &num_configs) ||
+       num_configs != 1)
+    {
+       fprintf(stderr, "Error: eglChooseConfig() failed.\n");
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+    }
+
+    egl_surface =
+       eglCreatePbufferFromClientBuffer (context->display,
+                                         EGL_OPENVG_IMAGE,
+                                         (EGLClientBuffer) surface->image,
+                                         config,
+                                         NULL);
+    surface->target_id = (unsigned long) egl_surface;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+egl_set_target (cairo_vg_context_t *context,
+               cairo_vg_surface_t *surface)
+{
+    if (! eglMakeCurrent (context->display,
+                         (EGLSurface *) surface->target_id,
+                         (EGLSurface *) surface->target_id,
+                         context->context))
+    {
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+egl_destroy_target (cairo_vg_context_t *context,
+                   cairo_vg_surface_t *surface)
+{
+    eglDestroySurface (context->display,
+                      (EGLSurface *) surface->target_id);
+}
+
+cairo_vg_context_t *
+cairo_vg_context_create_for_egl (EGLDisplay egl_display,
+                                EGLContext egl_context)
+{
+    cairo_vg_context_t *context;
+    cairo_status_t status;
+
+    context = malloc (sizeof (*context));
+    if (unlikely (context == NULL))
+       return (cairo_vg_context_t *) &_vg_context_nil;
+
+    status = _vg_context_init (context);
+    if (unlikely (status)) {
+       free (context);
+       return (cairo_vg_context_t *) &_vg_context_nil;
+    }
+
+    context->display = egl_display;
+    context->context = egl_context;
+
+    context->create_target  = egl_create_target;
+    context->set_target     = egl_set_target;
+    context->destroy_target = egl_destroy_target;
+
+    return context;
+}
+#endif
+
+cairo_status_t
+cairo_vg_context_status (cairo_vg_context_t *context)
+{
+    return context->status;
+}
+
+void
+cairo_vg_context_destroy (cairo_vg_context_t *context)
+{
+    if (context == NULL || CAIRO_REFERENCE_COUNT_IS_INVALID (&context->ref_count))
+       return;
+
+    _vg_context_destroy (context);
+}
diff --git a/src/cairo-vg.h b/src/cairo-vg.h
new file mode 100644 (file)
index 0000000..f9a62e5
--- /dev/null
@@ -0,0 +1,103 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2007 * Mozilla Corporation
+ * Copyright © 2009 Chris Wilson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Mozilla Corporation.
+ *
+ * Contributor(s):
+ *      Vladimir Vukicevic <vladimir@mozilla.com>
+ *      Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#ifndef CAIRO_VG_H
+#define CAIRO_VG_H
+
+#include "cairo.h"
+
+#if CAIRO_HAS_VG_SURFACE
+
+#include <VG/openvg.h>
+
+CAIRO_BEGIN_DECLS
+
+typedef struct _cairo_vg_context cairo_vg_context_t;
+
+#if CAIRO_HAS_GLX_FUNCTIONS
+typedef struct __GLXcontextRec *GLXContext;
+typedef struct _XDisplay Display;
+
+cairo_public cairo_vg_context_t *
+cairo_vg_context_create_for_glx (Display *dpy,
+                                GLXContext ctx);
+#endif
+
+#if CAIRO_HAS_EGL_FUNCTIONS
+#include <EGL/egl.h>
+
+cairo_public cairo_vg_context_t *
+cairo_vg_context_create_for_egl (EGLDisplay egl_display,
+                                EGLContext egl_context);
+#endif
+
+cairo_public cairo_status_t
+cairo_vg_context_status (cairo_vg_context_t *context);
+
+cairo_public void
+cairo_vg_context_destroy (cairo_vg_context_t *context);
+
+cairo_public cairo_surface_t *
+cairo_vg_surface_create (cairo_vg_context_t *context,
+                        cairo_content_t content, int width, int height);
+
+cairo_public cairo_surface_t *
+cairo_vg_surface_create_for_image (cairo_vg_context_t *context,
+                                  VGImage image,
+                                  VGImageFormat format,
+                                  int width, int height);
+
+cairo_public VGImage
+cairo_vg_surface_get_image (cairo_surface_t *abstract_surface);
+
+cairo_public VGImageFormat
+cairo_vg_surface_get_format (cairo_surface_t *abstract_surface);
+
+cairo_public int
+cairo_vg_surface_get_height (cairo_surface_t *abstract_surface);
+
+cairo_public int
+cairo_vg_surface_get_width (cairo_surface_t *abstract_surface);
+
+CAIRO_END_DECLS
+
+#else  /* CAIRO_HAS_VG_SURFACE*/
+# error Cairo was not compiled with support for the OpenVG backend
+#endif /* CAIRO_HAS_VG_SURFACE*/
+
+#endif /* CAIRO_VG_H */
diff --git a/src/cairo-wgl-context.c b/src/cairo-wgl-context.c
new file mode 100644 (file)
index 0000000..82f1211
--- /dev/null
@@ -0,0 +1,258 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Eric Anholt
+ * Copyright © 2009 Chris Wilson
+ * Copyright © 2005 Red Hat, Inc
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ *     Carl Worth <cworth@cworth.org>
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ *     Zoxc <zoxc32@gmail.com>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-gl-private.h"
+
+#include "cairo-error-private.h"
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+typedef struct _cairo_wgl_context {
+    cairo_gl_context_t base;
+
+    HDC dummy_dc;
+    HWND dummy_wnd;
+    HGLRC rc;
+
+    HDC prev_dc;
+    HGLRC prev_rc;
+} cairo_wgl_context_t;
+
+typedef struct _cairo_wgl_surface {
+    cairo_gl_surface_t base;
+
+    HDC dc;
+} cairo_wgl_surface_t;
+
+static void
+_wgl_acquire (void *abstract_ctx)
+{
+    cairo_wgl_context_t *ctx = abstract_ctx;
+
+    HDC current_dc;
+
+    ctx->prev_dc = wglGetCurrentDC ();
+    ctx->prev_rc = wglGetCurrentContext ();
+
+    if (ctx->base.current_target == NULL ||
+        _cairo_gl_surface_is_texture (ctx->base.current_target))
+    {
+        current_dc = ctx->dummy_dc;
+    }
+    else
+    {
+        cairo_wgl_surface_t *surface = (cairo_wgl_surface_t *) ctx->base.current_target;
+        current_dc = surface->dc;
+    }
+
+    if (ctx->prev_dc != current_dc ||
+       (ctx->prev_rc != ctx->rc &&
+        current_dc != ctx->dummy_dc))
+    {
+        wglMakeCurrent (current_dc, ctx->rc);
+    }
+}
+
+static void
+_wgl_make_current (void *abstract_ctx, cairo_gl_surface_t *abstract_surface)
+{
+    cairo_wgl_context_t *ctx = abstract_ctx;
+    cairo_wgl_surface_t *surface = (cairo_wgl_surface_t *) abstract_surface;
+
+    /* Set the window as the target of our context. */
+    wglMakeCurrent (surface->dc, ctx->rc);
+}
+
+static void
+_wgl_release (void *abstract_ctx)
+{
+    cairo_wgl_context_t *ctx = abstract_ctx;
+
+    if (ctx->prev_dc != wglGetCurrentDC () ||
+        ctx->prev_rc != wglGetCurrentContext ())
+    {
+        wglMakeCurrent (ctx->prev_dc,
+                        ctx->prev_rc);
+    }
+}
+
+static void
+_wgl_swap_buffers (void *abstract_ctx,
+                  cairo_gl_surface_t *abstract_surface)
+{
+    cairo_wgl_surface_t *surface = (cairo_wgl_surface_t *) abstract_surface;
+
+    SwapBuffers (surface->dc);
+}
+
+static void
+_wgl_destroy (void *abstract_ctx)
+{
+    cairo_wgl_context_t *ctx = abstract_ctx;
+
+    if (ctx->dummy_dc != 0) {  
+        wglMakeCurrent (ctx->dummy_dc, 0);
+        ReleaseDC (ctx->dummy_wnd, ctx->dummy_dc);
+        DestroyWindow (ctx->dummy_wnd);
+    }
+}
+
+static cairo_status_t
+_wgl_dummy_ctx (cairo_wgl_context_t *ctx)
+{
+    WNDCLASSEXA wincl;
+    PIXELFORMATDESCRIPTOR pfd;
+    int format;
+    HDC dc;
+
+    ZeroMemory (&wincl, sizeof (WNDCLASSEXA));
+    wincl.cbSize = sizeof (WNDCLASSEXA);
+    wincl.hInstance = GetModuleHandle (0);
+    wincl.lpszClassName = "cairo_wgl_context_dummy";
+    wincl.lpfnWndProc = DefWindowProcA;
+    wincl.style = CS_OWNDC;
+
+    RegisterClassExA (&wincl);
+
+    ctx->dummy_wnd = CreateWindowA ("cairo_wgl_context_dummy", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
+    ctx->dummy_dc = GetDC (ctx->dummy_wnd);
+
+    ZeroMemory (&pfd, sizeof (PIXELFORMATDESCRIPTOR));
+    pfd.nSize = sizeof (PIXELFORMATDESCRIPTOR);
+    pfd.nVersion = 1;
+    pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
+    pfd.iPixelType = PFD_TYPE_RGBA;
+    pfd.cColorBits = 24;
+    pfd.cDepthBits = 16;
+    pfd.iLayerType = PFD_MAIN_PLANE;
+
+    format = ChoosePixelFormat (ctx->dummy_dc, &pfd);
+    SetPixelFormat (ctx->dummy_dc, format, &pfd);
+
+    wglMakeCurrent(ctx->dummy_dc, ctx->rc);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_device_t *
+cairo_wgl_device_create (HGLRC rc)
+{
+    cairo_wgl_context_t *ctx;
+    cairo_status_t status;
+
+    ctx = calloc (1, sizeof (cairo_wgl_context_t));
+    if (unlikely (ctx == NULL))
+        return _cairo_gl_context_create_in_error (CAIRO_STATUS_NO_MEMORY);
+
+    ctx->rc = rc;
+    ctx->prev_dc = 0;
+    ctx->prev_rc = 0;
+
+    status = _wgl_dummy_ctx (ctx);
+    if (unlikely (status)) {
+        free (ctx);
+        return _cairo_gl_context_create_in_error (status);
+    }
+
+    ctx->base.acquire = _wgl_acquire;
+    ctx->base.release = _wgl_release;
+    ctx->base.make_current = _wgl_make_current;
+    ctx->base.swap_buffers = _wgl_swap_buffers;
+    ctx->base.destroy = _wgl_destroy;
+
+    status = _cairo_gl_dispatch_init (&ctx->base.dispatch,
+                                     (cairo_gl_get_proc_addr_func_t) wglGetProcAddress);
+    if (unlikely (status)) {
+       free (ctx);
+       return _cairo_gl_context_create_in_error (status);
+    }
+
+    status = _cairo_gl_context_init (&ctx->base);
+    if (unlikely (status)) {
+        free (ctx);
+        return _cairo_gl_context_create_in_error (status);
+    }
+
+    ctx->base.release (ctx);
+
+    return &ctx->base.base;
+}
+
+HGLRC
+cairo_wgl_device_get_context (cairo_device_t *device)
+{
+    cairo_wgl_context_t *ctx;
+
+    if (device->backend->type != CAIRO_DEVICE_TYPE_GL) {
+        _cairo_error_throw (CAIRO_STATUS_DEVICE_TYPE_MISMATCH);
+        return NULL;
+    }
+
+    ctx = (cairo_wgl_context_t *) device;
+
+    return ctx->rc;
+}
+
+cairo_surface_t *
+cairo_gl_surface_create_for_dc (cairo_device_t *device,
+                               HDC                      dc,
+                               int                      width,
+                               int                      height)
+{
+    cairo_wgl_surface_t *surface;
+
+    if (unlikely (device->status))
+        return _cairo_surface_create_in_error (device->status);
+
+    if (device->backend->type != CAIRO_DEVICE_TYPE_GL)
+        return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH));
+
+    surface = calloc (1, sizeof (cairo_wgl_surface_t));
+    if (unlikely (surface == NULL))
+        return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+    _cairo_gl_surface_init (device, &surface->base,
+                           CAIRO_CONTENT_COLOR_ALPHA, width, height);
+    surface->dc = dc;
+
+    return &surface->base.base;
+}
diff --git a/src/cairo-wideint-private.h b/src/cairo-wideint-private.h
new file mode 100644 (file)
index 0000000..24c506a
--- /dev/null
@@ -0,0 +1,338 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2004 Keith Packard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Keith Packard
+ *
+ * Contributor(s):
+ *     Keith R. Packard <keithp@keithp.com>
+ *
+ */
+
+#ifndef CAIRO_WIDEINT_H
+#define CAIRO_WIDEINT_H
+
+#include "cairo-wideint-type-private.h"
+
+#include "cairo-compiler-private.h"
+
+/*
+ * 64-bit datatypes.  Two separate implementations, one using
+ * built-in 64-bit signed/unsigned types another implemented
+ * as a pair of 32-bit ints
+ */
+
+#define I cairo_private cairo_const
+
+#if !HAVE_UINT64_T
+
+cairo_uquorem64_t I
+_cairo_uint64_divrem (cairo_uint64_t num, cairo_uint64_t den);
+
+cairo_uint64_t I       _cairo_double_to_uint64 (double i);
+double        I        _cairo_uint64_to_double (uint64_t i);
+cairo_int64_t  I       _cairo_double_to_int64 (double i);
+double        I        _cairo_int64_to_double (uint64_t i);
+
+cairo_uint64_t I       _cairo_uint32_to_uint64 (uint32_t i);
+#define                        _cairo_uint64_to_uint32(a)  ((a).lo)
+cairo_uint64_t I       _cairo_uint64_add (cairo_uint64_t a, cairo_uint64_t b);
+cairo_uint64_t I       _cairo_uint64_sub (cairo_uint64_t a, cairo_uint64_t b);
+cairo_uint64_t I       _cairo_uint64_mul (cairo_uint64_t a, cairo_uint64_t b);
+cairo_uint64_t I       _cairo_uint32x32_64_mul (uint32_t a, uint32_t b);
+cairo_uint64_t I       _cairo_uint64_lsl (cairo_uint64_t a, int shift);
+cairo_uint64_t I       _cairo_uint64_rsl (cairo_uint64_t a, int shift);
+cairo_uint64_t I       _cairo_uint64_rsa (cairo_uint64_t a, int shift);
+int           I        _cairo_uint64_lt (cairo_uint64_t a, cairo_uint64_t b);
+int           I        _cairo_uint64_cmp (cairo_uint64_t a, cairo_uint64_t b);
+int           I        _cairo_uint64_eq (cairo_uint64_t a, cairo_uint64_t b);
+cairo_uint64_t I       _cairo_uint64_negate (cairo_uint64_t a);
+#define                        _cairo_uint64_is_zero(a) ((a).hi == 0 && (a).lo == 0)
+#define                        _cairo_uint64_negative(a)   (((int32_t) ((a).hi)) < 0)
+cairo_uint64_t I       _cairo_uint64_not (cairo_uint64_t a);
+
+#define                        _cairo_uint64_to_int64(i)   (i)
+#define                        _cairo_int64_to_uint64(i)   (i)
+
+cairo_int64_t  I       _cairo_int32_to_int64(int32_t i);
+#define                        _cairo_int64_to_int32(a)    ((int32_t) _cairo_uint64_to_uint32(a))
+#define                        _cairo_int64_add(a,b)       _cairo_uint64_add (a,b)
+#define                        _cairo_int64_sub(a,b)       _cairo_uint64_sub (a,b)
+#define                        _cairo_int64_mul(a,b)       _cairo_uint64_mul (a,b)
+cairo_int64_t  I       _cairo_int32x32_64_mul (int32_t a, int32_t b);
+int           I        _cairo_int64_lt (cairo_int64_t a, cairo_int64_t b);
+int           I        _cairo_int64_cmp (cairo_int64_t a, cairo_int64_t b);
+#define                        _cairo_int64_is_zero(a)     _cairo_uint64_is_zero (a)
+#define                        _cairo_int64_eq(a,b)        _cairo_uint64_eq (a,b)
+#define                        _cairo_int64_lsl(a,b)       _cairo_uint64_lsl (a,b)
+#define                        _cairo_int64_rsl(a,b)       _cairo_uint64_rsl (a,b)
+#define                        _cairo_int64_rsa(a,b)       _cairo_uint64_rsa (a,b)
+#define                        _cairo_int64_negate(a)      _cairo_uint64_negate(a)
+#define                        _cairo_int64_negative(a)    (((int32_t) ((a).hi)) < 0)
+#define                        _cairo_int64_not(a)         _cairo_uint64_not(a)
+
+#else
+
+static inline cairo_uquorem64_t
+_cairo_uint64_divrem (cairo_uint64_t num, cairo_uint64_t den)
+{
+    cairo_uquorem64_t  qr;
+
+    qr.quo = num / den;
+    qr.rem = num % den;
+    return qr;
+}
+
+/*
+ * These need to be functions or gcc will complain when used on the
+ * result of a function:
+ *
+ * warning: cast from function call of type ‘#cairo_uint64_t’ to
+ * non-matching type ‘double’
+ */
+static cairo_always_inline cairo_const cairo_uint64_t _cairo_double_to_uint64 (double i) { return i; }
+static cairo_always_inline cairo_const double _cairo_uint64_to_double (cairo_uint64_t i) { return i; }
+
+static cairo_always_inline cairo_int64_t I _cairo_double_to_int64 (double i) { return i; }
+static cairo_always_inline double I _cairo_int64_to_double (cairo_int64_t i) { return i; }
+
+#define                        _cairo_uint32_to_uint64(i)  ((uint64_t) (i))
+#define                        _cairo_uint64_to_uint32(i)  ((uint32_t) (i))
+#define                        _cairo_uint64_add(a,b)      ((a) + (b))
+#define                        _cairo_uint64_sub(a,b)      ((a) - (b))
+#define                        _cairo_uint64_mul(a,b)      ((a) * (b))
+#define                        _cairo_uint32x32_64_mul(a,b)    ((uint64_t) (a) * (b))
+#define                        _cairo_uint64_lsl(a,b)      ((a) << (b))
+#define                        _cairo_uint64_rsl(a,b)      ((uint64_t) (a) >> (b))
+#define                        _cairo_uint64_rsa(a,b)      ((uint64_t) ((int64_t) (a) >> (b)))
+#define                        _cairo_uint64_lt(a,b)       ((a) < (b))
+#define                 _cairo_uint64_cmp(a,b)       ((a) == (b) ? 0 : (a) < (b) ? -1 : 1)
+#define                        _cairo_uint64_is_zero(a)    ((a) == 0)
+#define                        _cairo_uint64_eq(a,b)       ((a) == (b))
+#define                        _cairo_uint64_negate(a)     ((uint64_t) -((int64_t) (a)))
+#define                        _cairo_uint64_negative(a)   ((int64_t) (a) < 0)
+#define                        _cairo_uint64_not(a)        (~(a))
+
+#define                        _cairo_uint64_to_int64(i)   ((int64_t) (i))
+#define                        _cairo_int64_to_uint64(i)   ((uint64_t) (i))
+
+#define                        _cairo_int32_to_int64(i)    ((int64_t) (i))
+#define                        _cairo_int64_to_int32(i)    ((int32_t) (i))
+#define                        _cairo_int64_add(a,b)       ((a) + (b))
+#define                        _cairo_int64_sub(a,b)       ((a) - (b))
+#define                        _cairo_int64_mul(a,b)       ((a) * (b))
+#define                        _cairo_int32x32_64_mul(a,b) ((int64_t) (a) * (b))
+#define                        _cairo_int64_lt(a,b)        ((a) < (b))
+#define                 _cairo_int64_cmp(a,b)       ((a) == (b) ? 0 : (a) < (b) ? -1 : 1)
+#define                        _cairo_int64_is_zero(a)     ((a) == 0)
+#define                        _cairo_int64_eq(a,b)        ((a) == (b))
+#define                        _cairo_int64_lsl(a,b)       ((a) << (b))
+#define                        _cairo_int64_rsl(a,b)       ((int64_t) ((uint64_t) (a) >> (b)))
+#define                        _cairo_int64_rsa(a,b)       ((int64_t) (a) >> (b))
+#define                        _cairo_int64_negate(a)      (-(a))
+#define                        _cairo_int64_negative(a)    ((a) < 0)
+#define                        _cairo_int64_not(a)         (~(a))
+
+#endif
+
+/*
+ * 64-bit comparisions derived from lt or eq
+ */
+#define                        _cairo_uint64_le(a,b)       (!_cairo_uint64_gt(a,b))
+#define                        _cairo_uint64_ne(a,b)       (!_cairo_uint64_eq(a,b))
+#define                        _cairo_uint64_ge(a,b)       (!_cairo_uint64_lt(a,b))
+#define                        _cairo_uint64_gt(a,b)       _cairo_uint64_lt(b,a)
+
+#define                        _cairo_int64_le(a,b)        (!_cairo_int64_gt(a,b))
+#define                        _cairo_int64_ne(a,b)        (!_cairo_int64_eq(a,b))
+#define                        _cairo_int64_ge(a,b)        (!_cairo_int64_lt(a,b))
+#define                        _cairo_int64_gt(a,b)        _cairo_int64_lt(b,a)
+
+/*
+ * As the C implementation always computes both, create
+ * a function which returns both for the 'native' type as well
+ */
+
+static inline cairo_quorem64_t
+_cairo_int64_divrem (cairo_int64_t num, cairo_int64_t den)
+{
+    int                        num_neg = _cairo_int64_negative (num);
+    int                        den_neg = _cairo_int64_negative (den);
+    cairo_uquorem64_t  uqr;
+    cairo_quorem64_t   qr;
+
+    if (num_neg)
+       num = _cairo_int64_negate (num);
+    if (den_neg)
+       den = _cairo_int64_negate (den);
+    uqr = _cairo_uint64_divrem (num, den);
+    if (num_neg)
+       qr.rem = _cairo_int64_negate (uqr.rem);
+    else
+       qr.rem = uqr.rem;
+    if (num_neg != den_neg)
+       qr.quo = (cairo_int64_t) _cairo_int64_negate (uqr.quo);
+    else
+       qr.quo = (cairo_int64_t) uqr.quo;
+    return qr;
+}
+
+static inline int32_t
+_cairo_int64_32_div (cairo_int64_t num, int32_t den)
+{
+#if !HAVE_UINT64_T
+    return _cairo_int64_to_int32
+       (_cairo_int64_divrem (num, _cairo_int32_to_int64 (den)).quo);
+#else
+    return num / den;
+#endif
+}
+
+/*
+ * 128-bit datatypes.  Again, provide two implementations in
+ * case the machine has a native 128-bit datatype.  GCC supports int128_t
+ * on ia64
+ */
+
+#if !HAVE_UINT128_T
+
+cairo_uint128_t I      _cairo_uint32_to_uint128 (uint32_t i);
+cairo_uint128_t I      _cairo_uint64_to_uint128 (cairo_uint64_t i);
+#define                        _cairo_uint128_to_uint64(a)     ((a).lo)
+#define                        _cairo_uint128_to_uint32(a)     _cairo_uint64_to_uint32(_cairo_uint128_to_uint64(a))
+cairo_uint128_t I      _cairo_uint128_add (cairo_uint128_t a, cairo_uint128_t b);
+cairo_uint128_t I      _cairo_uint128_sub (cairo_uint128_t a, cairo_uint128_t b);
+cairo_uint128_t I      _cairo_uint128_mul (cairo_uint128_t a, cairo_uint128_t b);
+cairo_uint128_t I      _cairo_uint64x64_128_mul (cairo_uint64_t a, cairo_uint64_t b);
+cairo_uint128_t I      _cairo_uint128_lsl (cairo_uint128_t a, int shift);
+cairo_uint128_t I      _cairo_uint128_rsl (cairo_uint128_t a, int shift);
+cairo_uint128_t I      _cairo_uint128_rsa (cairo_uint128_t a, int shift);
+int            I       _cairo_uint128_lt (cairo_uint128_t a, cairo_uint128_t b);
+int            I       _cairo_uint128_cmp (cairo_uint128_t a, cairo_uint128_t b);
+int            I       _cairo_uint128_eq (cairo_uint128_t a, cairo_uint128_t b);
+#define                        _cairo_uint128_is_zero(a) (_cairo_uint64_is_zero ((a).hi) && _cairo_uint64_is_zero ((a).lo))
+cairo_uint128_t I      _cairo_uint128_negate (cairo_uint128_t a);
+#define                        _cairo_uint128_negative(a)  (_cairo_uint64_negative(a.hi))
+cairo_uint128_t I      _cairo_uint128_not (cairo_uint128_t a);
+
+#define                        _cairo_uint128_to_int128(i)     (i)
+#define                        _cairo_int128_to_uint128(i)     (i)
+
+cairo_int128_t  I      _cairo_int32_to_int128 (int32_t i);
+cairo_int128_t  I      _cairo_int64_to_int128 (cairo_int64_t i);
+#define                        _cairo_int128_to_int64(a)   ((cairo_int64_t) (a).lo)
+#define                        _cairo_int128_to_int32(a)   _cairo_int64_to_int32(_cairo_int128_to_int64(a))
+#define                        _cairo_int128_add(a,b)      _cairo_uint128_add(a,b)
+#define                        _cairo_int128_sub(a,b)      _cairo_uint128_sub(a,b)
+#define                        _cairo_int128_mul(a,b)      _cairo_uint128_mul(a,b)
+cairo_int128_t I _cairo_int64x64_128_mul (cairo_int64_t a, cairo_int64_t b);
+#define                 _cairo_int64x32_128_mul(a, b) _cairo_int64x64_128_mul(a, _cairo_int32_to_int64(b))
+#define                        _cairo_int128_lsl(a,b)      _cairo_uint128_lsl(a,b)
+#define                        _cairo_int128_rsl(a,b)      _cairo_uint128_rsl(a,b)
+#define                        _cairo_int128_rsa(a,b)      _cairo_uint128_rsa(a,b)
+int            I       _cairo_int128_lt (cairo_int128_t a, cairo_int128_t b);
+int            I       _cairo_int128_cmp (cairo_int128_t a, cairo_int128_t b);
+#define                        _cairo_int128_is_zero(a)    _cairo_uint128_is_zero (a)
+#define                        _cairo_int128_eq(a,b)       _cairo_uint128_eq (a,b)
+#define                        _cairo_int128_negate(a)     _cairo_uint128_negate(a)
+#define                        _cairo_int128_negative(a)   (_cairo_uint128_negative(a))
+#define                        _cairo_int128_not(a)        _cairo_uint128_not(a)
+
+#else  /* !HAVE_UINT128_T */
+
+#define                        _cairo_uint32_to_uint128(i) ((uint128_t) (i))
+#define                        _cairo_uint64_to_uint128(i) ((uint128_t) (i))
+#define                        _cairo_uint128_to_uint64(i) ((uint64_t) (i))
+#define                        _cairo_uint128_to_uint32(i) ((uint32_t) (i))
+#define                        _cairo_uint128_add(a,b)     ((a) + (b))
+#define                        _cairo_uint128_sub(a,b)     ((a) - (b))
+#define                        _cairo_uint128_mul(a,b)     ((a) * (b))
+#define                        _cairo_uint64x64_128_mul(a,b)   ((uint128_t) (a) * (b))
+#define                        _cairo_uint128_lsl(a,b)     ((a) << (b))
+#define                        _cairo_uint128_rsl(a,b)     ((uint128_t) (a) >> (b))
+#define                        _cairo_uint128_rsa(a,b)     ((uint128_t) ((int128_t) (a) >> (b)))
+#define                        _cairo_uint128_lt(a,b)      ((a) < (b))
+#define                        _cairo_uint128_cmp(a,b)     ((a) == (b) ? 0 : (a) < (b) ? -1 : 1)
+#define                        _cairo_uint128_is_zero(a)   ((a) == 0)
+#define                        _cairo_uint128_eq(a,b)      ((a) == (b))
+#define                        _cairo_uint128_negate(a)    ((uint128_t) -((int128_t) (a)))
+#define                        _cairo_uint128_negative(a)  ((int128_t) (a) < 0)
+#define                        _cairo_uint128_not(a)       (~(a))
+
+#define                        _cairo_uint128_to_int128(i) ((int128_t) (i))
+#define                        _cairo_int128_to_uint128(i) ((uint128_t) (i))
+
+#define                        _cairo_int32_to_int128(i)   ((int128_t) (i))
+#define                        _cairo_int64_to_int128(i)   ((int128_t) (i))
+#define                        _cairo_int128_to_int64(i)   ((int64_t) (i))
+#define                        _cairo_int128_to_int32(i)   ((int32_t) (i))
+#define                        _cairo_int128_add(a,b)      ((a) + (b))
+#define                        _cairo_int128_sub(a,b)      ((a) - (b))
+#define                        _cairo_int128_mul(a,b)      ((a) * (b))
+#define                        _cairo_int64x64_128_mul(a,b) ((int128_t) (a) * (b))
+#define                 _cairo_int64x32_128_mul(a, b) _cairo_int64x64_128_mul(a, _cairo_int32_to_int64(b))
+#define                        _cairo_int128_lt(a,b)       ((a) < (b))
+#define                        _cairo_int128_cmp(a,b)      ((a) == (b) ? 0 : (a) < (b) ? -1 : 1)
+#define                        _cairo_int128_is_zero(a)    ((a) == 0)
+#define                        _cairo_int128_eq(a,b)       ((a) == (b))
+#define                        _cairo_int128_lsl(a,b)      ((a) << (b))
+#define                        _cairo_int128_rsl(a,b)      ((int128_t) ((uint128_t) (a) >> (b)))
+#define                        _cairo_int128_rsa(a,b)      ((int128_t) (a) >> (b))
+#define                        _cairo_int128_negate(a)     (-(a))
+#define                        _cairo_int128_negative(a)   ((a) < 0)
+#define                        _cairo_int128_not(a)        (~(a))
+
+#endif /* HAVE_UINT128_T */
+
+cairo_uquorem128_t I
+_cairo_uint128_divrem (cairo_uint128_t num, cairo_uint128_t den);
+
+cairo_quorem128_t I
+_cairo_int128_divrem (cairo_int128_t num, cairo_int128_t den);
+
+cairo_uquorem64_t I
+_cairo_uint_96by64_32x64_divrem (cairo_uint128_t num,
+                                cairo_uint64_t  den);
+
+cairo_quorem64_t I
+_cairo_int_96by64_32x64_divrem (cairo_int128_t num,
+                               cairo_int64_t  den);
+
+#define                        _cairo_uint128_le(a,b)      (!_cairo_uint128_gt(a,b))
+#define                        _cairo_uint128_ne(a,b)      (!_cairo_uint128_eq(a,b))
+#define                        _cairo_uint128_ge(a,b)      (!_cairo_uint128_lt(a,b))
+#define                        _cairo_uint128_gt(a,b)      _cairo_uint128_lt(b,a)
+
+#define                        _cairo_int128_le(a,b)       (!_cairo_int128_gt(a,b))
+#define                        _cairo_int128_ne(a,b)       (!_cairo_int128_eq(a,b))
+#define                        _cairo_int128_ge(a,b)       (!_cairo_int128_lt(a,b))
+#define                        _cairo_int128_gt(a,b)       _cairo_int128_lt(b,a)
+
+#undef I
+
+#endif /* CAIRO_WIDEINT_H */
diff --git a/src/cairo-wideint-type-private.h b/src/cairo-wideint-type-private.h
new file mode 100644 (file)
index 0000000..84a3cba
--- /dev/null
@@ -0,0 +1,158 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2004 Keith Packard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Keith Packard
+ *
+ * Contributor(s):
+ *     Keith R. Packard <keithp@keithp.com>
+ *
+ */
+
+#ifndef CAIRO_WIDEINT_TYPE_H
+#define CAIRO_WIDEINT_TYPE_H
+
+#include "cairo.h"
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#if   HAVE_STDINT_H
+# include <stdint.h>
+#elif HAVE_INTTYPES_H
+# include <inttypes.h>
+#elif HAVE_SYS_INT_TYPES_H
+# include <sys/int_types.h>
+#elif defined(_MSC_VER)
+  typedef __int8 int8_t;
+  typedef unsigned __int8 uint8_t;
+  typedef __int16 int16_t;
+  typedef unsigned __int16 uint16_t;
+  typedef __int32 int32_t;
+  typedef unsigned __int32 uint32_t;
+  typedef __int64 int64_t;
+  typedef unsigned __int64 uint64_t;
+# ifndef HAVE_UINT64_T
+#  define HAVE_UINT64_T 1
+# endif
+#else
+#error Cannot find definitions for fixed-width integral types (uint8_t, uint32_t, etc.)
+#endif
+
+#ifndef INT16_MIN
+# define INT16_MIN     (-32767-1)
+#endif
+#ifndef INT16_MAX
+# define INT16_MAX     (32767)
+#endif
+#ifndef UINT16_MAX
+# define UINT16_MAX    (65535)
+#endif
+#ifndef INT32_MIN
+# define INT32_MIN     (-2147483647-1)
+#endif
+#ifndef INT32_MAX
+# define INT32_MAX     (2147483647)
+#endif
+#ifndef UINT32_MAX
+# define UINT32_MAX     (4294967295U)
+#endif
+
+#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 !HAVE_UINT64_T
+
+typedef struct _cairo_uint64 {
+    uint32_t   lo, hi;
+} cairo_uint64_t, cairo_int64_t;
+
+#else
+
+typedef uint64_t    cairo_uint64_t;
+typedef int64_t            cairo_int64_t;
+
+#endif
+
+typedef struct _cairo_uquorem64 {
+    cairo_uint64_t     quo;
+    cairo_uint64_t     rem;
+} cairo_uquorem64_t;
+
+typedef struct _cairo_quorem64 {
+    cairo_int64_t      quo;
+    cairo_int64_t      rem;
+} cairo_quorem64_t;
+
+/* gcc has a non-standard name. */
+#if HAVE___UINT128_T && !HAVE_UINT128_T
+typedef __uint128_t uint128_t;
+typedef __int128_t int128_t;
+#define HAVE_UINT128_T 1
+#endif
+
+#if !HAVE_UINT128_T
+
+typedef struct cairo_uint128 {
+    cairo_uint64_t     lo, hi;
+} cairo_uint128_t, cairo_int128_t;
+
+#else
+
+typedef uint128_t      cairo_uint128_t;
+typedef int128_t       cairo_int128_t;
+
+#endif
+
+typedef struct _cairo_uquorem128 {
+    cairo_uint128_t    quo;
+    cairo_uint128_t    rem;
+} cairo_uquorem128_t;
+
+typedef struct _cairo_quorem128 {
+    cairo_int128_t     quo;
+    cairo_int128_t     rem;
+} cairo_quorem128_t;
+
+
+#endif /* CAIRO_WIDEINT_H */
diff --git a/src/cairo-wideint.c b/src/cairo-wideint.c
new file mode 100644 (file)
index 0000000..59af2c8
--- /dev/null
@@ -0,0 +1,851 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2004 Keith Packard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Keith Packard
+ *
+ * Contributor(s):
+ *     Keith R. Packard <keithp@keithp.com>
+ */
+
+#include "cairoint.h"
+
+#if HAVE_UINT64_T
+
+#define uint64_lo32(i) ((i) & 0xffffffff)
+#define uint64_hi32(i) ((i) >> 32)
+#define uint64_lo(i)   ((i) & 0xffffffff)
+#define uint64_hi(i)   ((i) >> 32)
+#define uint64_shift32(i)   ((i) << 32)
+#define uint64_carry32 (((uint64_t) 1) << 32)
+
+#define _cairo_uint32s_to_uint64(h,l) ((uint64_t) (h) << 32 | (l))
+
+#else
+
+#define uint64_lo32(i) ((i).lo)
+#define uint64_hi32(i) ((i).hi)
+
+static cairo_uint64_t
+uint64_lo (cairo_uint64_t i)
+{
+    cairo_uint64_t  s;
+
+    s.lo = i.lo;
+    s.hi = 0;
+    return s;
+}
+
+static cairo_uint64_t
+uint64_hi (cairo_uint64_t i)
+{
+    cairo_uint64_t  s;
+
+    s.lo = i.hi;
+    s.hi = 0;
+    return s;
+}
+
+static cairo_uint64_t
+uint64_shift32 (cairo_uint64_t i)
+{
+    cairo_uint64_t  s;
+
+    s.lo = 0;
+    s.hi = i.lo;
+    return s;
+}
+
+static const cairo_uint64_t uint64_carry32 = { 0, 1 };
+
+cairo_uint64_t
+_cairo_double_to_uint64 (double i)
+{
+    cairo_uint64_t     q;
+
+    q.hi = i * (1. / 4294967296.);
+    q.lo = i - q.hi * 4294967296.;
+    return q;
+}
+
+double
+_cairo_uint64_to_double (cairo_uint64_t i)
+{
+    return i.hi * 4294967296. + i.lo;
+}
+
+cairo_int64_t
+_cairo_double_to_int64 (double i)
+{
+    cairo_uint64_t     q;
+
+    q.hi = i * (1. / INT32_MAX);
+    q.lo = i - q.hi * (double)INT32_MAX;
+    return q;
+}
+
+double
+_cairo_int64_to_double (cairo_int64_t i)
+{
+    return i.hi * INT32_MAX + i.lo;
+}
+
+cairo_uint64_t
+_cairo_uint32_to_uint64 (uint32_t i)
+{
+    cairo_uint64_t     q;
+
+    q.lo = i;
+    q.hi = 0;
+    return q;
+}
+
+cairo_int64_t
+_cairo_int32_to_int64 (int32_t i)
+{
+    cairo_uint64_t     q;
+
+    q.lo = i;
+    q.hi = i < 0 ? -1 : 0;
+    return q;
+}
+
+static cairo_uint64_t
+_cairo_uint32s_to_uint64 (uint32_t h, uint32_t l)
+{
+    cairo_uint64_t     q;
+
+    q.lo = l;
+    q.hi = h;
+    return q;
+}
+
+cairo_uint64_t
+_cairo_uint64_add (cairo_uint64_t a, cairo_uint64_t b)
+{
+    cairo_uint64_t     s;
+
+    s.hi = a.hi + b.hi;
+    s.lo = a.lo + b.lo;
+    if (s.lo < a.lo)
+       s.hi++;
+    return s;
+}
+
+cairo_uint64_t
+_cairo_uint64_sub (cairo_uint64_t a, cairo_uint64_t b)
+{
+    cairo_uint64_t     s;
+
+    s.hi = a.hi - b.hi;
+    s.lo = a.lo - b.lo;
+    if (s.lo > a.lo)
+       s.hi--;
+    return s;
+}
+
+#define uint32_lo(i)   ((i) & 0xffff)
+#define uint32_hi(i)   ((i) >> 16)
+#define uint32_carry16 ((1) << 16)
+
+cairo_uint64_t
+_cairo_uint32x32_64_mul (uint32_t a, uint32_t b)
+{
+    cairo_uint64_t  s;
+
+    uint16_t   ah, al, bh, bl;
+    uint32_t   r0, r1, r2, r3;
+
+    al = uint32_lo (a);
+    ah = uint32_hi (a);
+    bl = uint32_lo (b);
+    bh = uint32_hi (b);
+
+    r0 = (uint32_t) al * bl;
+    r1 = (uint32_t) al * bh;
+    r2 = (uint32_t) ah * bl;
+    r3 = (uint32_t) ah * bh;
+
+    r1 += uint32_hi(r0);    /* no carry possible */
+    r1 += r2;              /* but this can carry */
+    if (r1 < r2)           /* check */
+       r3 += uint32_carry16;
+
+    s.hi = r3 + uint32_hi(r1);
+    s.lo = (uint32_lo (r1) << 16) + uint32_lo (r0);
+    return s;
+}
+
+cairo_int64_t
+_cairo_int32x32_64_mul (int32_t a, int32_t b)
+{
+    cairo_int64_t s;
+    s = _cairo_uint32x32_64_mul ((uint32_t) a, (uint32_t) b);
+    if (a < 0)
+       s.hi -= b;
+    if (b < 0)
+       s.hi -= a;
+    return s;
+}
+
+cairo_uint64_t
+_cairo_uint64_mul (cairo_uint64_t a, cairo_uint64_t b)
+{
+    cairo_uint64_t     s;
+
+    s = _cairo_uint32x32_64_mul (a.lo, b.lo);
+    s.hi += a.lo * b.hi + a.hi * b.lo;
+    return s;
+}
+
+cairo_uint64_t
+_cairo_uint64_lsl (cairo_uint64_t a, int shift)
+{
+    if (shift >= 32)
+    {
+       a.hi = a.lo;
+       a.lo = 0;
+       shift -= 32;
+    }
+    if (shift)
+    {
+       a.hi = a.hi << shift | a.lo >> (32 - shift);
+       a.lo = a.lo << shift;
+    }
+    return a;
+}
+
+cairo_uint64_t
+_cairo_uint64_rsl (cairo_uint64_t a, int shift)
+{
+    if (shift >= 32)
+    {
+       a.lo = a.hi;
+       a.hi = 0;
+       shift -= 32;
+    }
+    if (shift)
+    {
+       a.lo = a.lo >> shift | a.hi << (32 - shift);
+       a.hi = a.hi >> shift;
+    }
+    return a;
+}
+
+#define _cairo_uint32_rsa(a,n) ((uint32_t) (((int32_t) (a)) >> (n)))
+
+cairo_int64_t
+_cairo_uint64_rsa (cairo_int64_t a, int shift)
+{
+    if (shift >= 32)
+    {
+       a.lo = a.hi;
+       a.hi = _cairo_uint32_rsa (a.hi, 31);
+       shift -= 32;
+    }
+    if (shift)
+    {
+       a.lo = a.lo >> shift | a.hi << (32 - shift);
+       a.hi = _cairo_uint32_rsa (a.hi, shift);
+    }
+    return a;
+}
+
+int
+_cairo_uint64_lt (cairo_uint64_t a, cairo_uint64_t b)
+{
+    return (a.hi < b.hi ||
+           (a.hi == b.hi && a.lo < b.lo));
+}
+
+int
+_cairo_uint64_eq (cairo_uint64_t a, cairo_uint64_t b)
+{
+    return a.hi == b.hi && a.lo == b.lo;
+}
+
+int
+_cairo_int64_lt (cairo_int64_t a, cairo_int64_t b)
+{
+    if (_cairo_int64_negative (a) && !_cairo_int64_negative (b))
+       return 1;
+    if (!_cairo_int64_negative (a) && _cairo_int64_negative (b))
+       return 0;
+    return _cairo_uint64_lt (a, b);
+}
+
+int
+_cairo_uint64_cmp (cairo_uint64_t a, cairo_uint64_t b)
+{
+    if (a.hi < b.hi)
+       return -1;
+    else if (a.hi > b.hi)
+       return 1;
+    else if (a.lo < b.lo)
+       return -1;
+    else if (a.lo > b.lo)
+       return 1;
+    else
+       return 0;
+}
+
+int
+_cairo_int64_cmp (cairo_int64_t a, cairo_int64_t b)
+{
+    if (_cairo_int64_negative (a) && !_cairo_int64_negative (b))
+       return -1;
+    if (!_cairo_int64_negative (a) && _cairo_int64_negative (b))
+       return 1;
+
+    return _cairo_uint64_cmp (a, b);
+}
+
+cairo_uint64_t
+_cairo_uint64_not (cairo_uint64_t a)
+{
+    a.lo = ~a.lo;
+    a.hi = ~a.hi;
+    return a;
+}
+
+cairo_uint64_t
+_cairo_uint64_negate (cairo_uint64_t a)
+{
+    a.lo = ~a.lo;
+    a.hi = ~a.hi;
+    if (++a.lo == 0)
+       ++a.hi;
+    return a;
+}
+
+/*
+ * Simple bit-at-a-time divide.
+ */
+cairo_uquorem64_t
+_cairo_uint64_divrem (cairo_uint64_t num, cairo_uint64_t den)
+{
+    cairo_uquorem64_t  qr;
+    cairo_uint64_t     bit;
+    cairo_uint64_t     quo;
+
+    bit = _cairo_uint32_to_uint64 (1);
+
+    /* normalize to make den >= num, but not overflow */
+    while (_cairo_uint64_lt (den, num) && (den.hi & 0x80000000) == 0)
+    {
+       bit = _cairo_uint64_lsl (bit, 1);
+       den = _cairo_uint64_lsl (den, 1);
+    }
+    quo = _cairo_uint32_to_uint64 (0);
+
+    /* generate quotient, one bit at a time */
+    while (bit.hi | bit.lo)
+    {
+       if (_cairo_uint64_le (den, num))
+       {
+           num = _cairo_uint64_sub (num, den);
+           quo = _cairo_uint64_add (quo, bit);
+       }
+       bit = _cairo_uint64_rsl (bit, 1);
+       den = _cairo_uint64_rsl (den, 1);
+    }
+    qr.quo = quo;
+    qr.rem = num;
+    return qr;
+}
+
+#endif /* !HAVE_UINT64_T */
+
+#if HAVE_UINT128_T
+cairo_uquorem128_t
+_cairo_uint128_divrem (cairo_uint128_t num, cairo_uint128_t den)
+{
+    cairo_uquorem128_t qr;
+
+    qr.quo = num / den;
+    qr.rem = num % den;
+    return qr;
+}
+
+#else
+
+cairo_uint128_t
+_cairo_uint32_to_uint128 (uint32_t i)
+{
+    cairo_uint128_t    q;
+
+    q.lo = _cairo_uint32_to_uint64 (i);
+    q.hi = _cairo_uint32_to_uint64 (0);
+    return q;
+}
+
+cairo_int128_t
+_cairo_int32_to_int128 (int32_t i)
+{
+    cairo_int128_t     q;
+
+    q.lo = _cairo_int32_to_int64 (i);
+    q.hi = _cairo_int32_to_int64 (i < 0 ? -1 : 0);
+    return q;
+}
+
+cairo_uint128_t
+_cairo_uint64_to_uint128 (cairo_uint64_t i)
+{
+    cairo_uint128_t    q;
+
+    q.lo = i;
+    q.hi = _cairo_uint32_to_uint64 (0);
+    return q;
+}
+
+cairo_int128_t
+_cairo_int64_to_int128 (cairo_int64_t i)
+{
+    cairo_int128_t     q;
+
+    q.lo = i;
+    q.hi = _cairo_int32_to_int64 (_cairo_int64_negative(i) ? -1 : 0);
+    return q;
+}
+
+cairo_uint128_t
+_cairo_uint128_add (cairo_uint128_t a, cairo_uint128_t b)
+{
+    cairo_uint128_t    s;
+
+    s.hi = _cairo_uint64_add (a.hi, b.hi);
+    s.lo = _cairo_uint64_add (a.lo, b.lo);
+    if (_cairo_uint64_lt (s.lo, a.lo))
+       s.hi = _cairo_uint64_add (s.hi, _cairo_uint32_to_uint64 (1));
+    return s;
+}
+
+cairo_uint128_t
+_cairo_uint128_sub (cairo_uint128_t a, cairo_uint128_t b)
+{
+    cairo_uint128_t    s;
+
+    s.hi = _cairo_uint64_sub (a.hi, b.hi);
+    s.lo = _cairo_uint64_sub (a.lo, b.lo);
+    if (_cairo_uint64_gt (s.lo, a.lo))
+       s.hi = _cairo_uint64_sub (s.hi, _cairo_uint32_to_uint64(1));
+    return s;
+}
+
+cairo_uint128_t
+_cairo_uint64x64_128_mul (cairo_uint64_t a, cairo_uint64_t b)
+{
+    cairo_uint128_t    s;
+    uint32_t           ah, al, bh, bl;
+    cairo_uint64_t     r0, r1, r2, r3;
+
+    al = uint64_lo32 (a);
+    ah = uint64_hi32 (a);
+    bl = uint64_lo32 (b);
+    bh = uint64_hi32 (b);
+
+    r0 = _cairo_uint32x32_64_mul (al, bl);
+    r1 = _cairo_uint32x32_64_mul (al, bh);
+    r2 = _cairo_uint32x32_64_mul (ah, bl);
+    r3 = _cairo_uint32x32_64_mul (ah, bh);
+
+    r1 = _cairo_uint64_add (r1, uint64_hi (r0));    /* no carry possible */
+    r1 = _cairo_uint64_add (r1, r2);               /* but this can carry */
+    if (_cairo_uint64_lt (r1, r2))                 /* check */
+       r3 = _cairo_uint64_add (r3, uint64_carry32);
+
+    s.hi = _cairo_uint64_add (r3, uint64_hi(r1));
+    s.lo = _cairo_uint64_add (uint64_shift32 (r1),
+                               uint64_lo (r0));
+    return s;
+}
+
+cairo_int128_t
+_cairo_int64x64_128_mul (cairo_int64_t a, cairo_int64_t b)
+{
+    cairo_int128_t  s;
+    s = _cairo_uint64x64_128_mul (_cairo_int64_to_uint64(a),
+                                 _cairo_int64_to_uint64(b));
+    if (_cairo_int64_negative (a))
+       s.hi = _cairo_uint64_sub (s.hi,
+                                 _cairo_int64_to_uint64 (b));
+    if (_cairo_int64_negative (b))
+       s.hi = _cairo_uint64_sub (s.hi,
+                                 _cairo_int64_to_uint64 (a));
+    return s;
+}
+
+cairo_uint128_t
+_cairo_uint128_mul (cairo_uint128_t a, cairo_uint128_t b)
+{
+    cairo_uint128_t    s;
+
+    s = _cairo_uint64x64_128_mul (a.lo, b.lo);
+    s.hi = _cairo_uint64_add (s.hi,
+                               _cairo_uint64_mul (a.lo, b.hi));
+    s.hi = _cairo_uint64_add (s.hi,
+                               _cairo_uint64_mul (a.hi, b.lo));
+    return s;
+}
+
+cairo_uint128_t
+_cairo_uint128_lsl (cairo_uint128_t a, int shift)
+{
+    if (shift >= 64)
+    {
+       a.hi = a.lo;
+       a.lo = _cairo_uint32_to_uint64 (0);
+       shift -= 64;
+    }
+    if (shift)
+    {
+       a.hi = _cairo_uint64_add (_cairo_uint64_lsl (a.hi, shift),
+                                   _cairo_uint64_rsl (a.lo, (64 - shift)));
+       a.lo = _cairo_uint64_lsl (a.lo, shift);
+    }
+    return a;
+}
+
+cairo_uint128_t
+_cairo_uint128_rsl (cairo_uint128_t a, int shift)
+{
+    if (shift >= 64)
+    {
+       a.lo = a.hi;
+       a.hi = _cairo_uint32_to_uint64 (0);
+       shift -= 64;
+    }
+    if (shift)
+    {
+       a.lo = _cairo_uint64_add (_cairo_uint64_rsl (a.lo, shift),
+                                   _cairo_uint64_lsl (a.hi, (64 - shift)));
+       a.hi = _cairo_uint64_rsl (a.hi, shift);
+    }
+    return a;
+}
+
+cairo_uint128_t
+_cairo_uint128_rsa (cairo_int128_t a, int shift)
+{
+    if (shift >= 64)
+    {
+       a.lo = a.hi;
+       a.hi = _cairo_uint64_rsa (a.hi, 64-1);
+       shift -= 64;
+    }
+    if (shift)
+    {
+       a.lo = _cairo_uint64_add (_cairo_uint64_rsl (a.lo, shift),
+                                   _cairo_uint64_lsl (a.hi, (64 - shift)));
+       a.hi = _cairo_uint64_rsa (a.hi, shift);
+    }
+    return a;
+}
+
+int
+_cairo_uint128_lt (cairo_uint128_t a, cairo_uint128_t b)
+{
+    return (_cairo_uint64_lt (a.hi, b.hi) ||
+           (_cairo_uint64_eq (a.hi, b.hi) &&
+            _cairo_uint64_lt (a.lo, b.lo)));
+}
+
+int
+_cairo_int128_lt (cairo_int128_t a, cairo_int128_t b)
+{
+    if (_cairo_int128_negative (a) && !_cairo_int128_negative (b))
+       return 1;
+    if (!_cairo_int128_negative (a) && _cairo_int128_negative (b))
+       return 0;
+    return _cairo_uint128_lt (a, b);
+}
+
+int
+_cairo_uint128_cmp (cairo_uint128_t a, cairo_uint128_t b)
+{
+    int cmp;
+
+    cmp = _cairo_uint64_cmp (a.hi, b.hi);
+    if (cmp)
+       return cmp;
+    return _cairo_uint64_cmp (a.lo, b.lo);
+}
+
+int
+_cairo_int128_cmp (cairo_int128_t a, cairo_int128_t b)
+{
+    if (_cairo_int128_negative (a) && !_cairo_int128_negative (b))
+       return -1;
+    if (!_cairo_int128_negative (a) && _cairo_int128_negative (b))
+       return 1;
+
+    return _cairo_uint128_cmp (a, b);
+}
+
+int
+_cairo_uint128_eq (cairo_uint128_t a, cairo_uint128_t b)
+{
+    return (_cairo_uint64_eq (a.hi, b.hi) &&
+           _cairo_uint64_eq (a.lo, b.lo));
+}
+
+#if HAVE_UINT64_T
+#define _cairo_msbset64(q)  (q & ((uint64_t) 1 << 63))
+#else
+#define _cairo_msbset64(q)  (q.hi & ((uint32_t) 1 << 31))
+#endif
+
+cairo_uquorem128_t
+_cairo_uint128_divrem (cairo_uint128_t num, cairo_uint128_t den)
+{
+    cairo_uquorem128_t qr;
+    cairo_uint128_t    bit;
+    cairo_uint128_t    quo;
+
+    bit = _cairo_uint32_to_uint128 (1);
+
+    /* normalize to make den >= num, but not overflow */
+    while (_cairo_uint128_lt (den, num) && !_cairo_msbset64(den.hi))
+    {
+       bit = _cairo_uint128_lsl (bit, 1);
+       den = _cairo_uint128_lsl (den, 1);
+    }
+    quo = _cairo_uint32_to_uint128 (0);
+
+    /* generate quotient, one bit at a time */
+    while (_cairo_uint128_ne (bit, _cairo_uint32_to_uint128(0)))
+    {
+       if (_cairo_uint128_le (den, num))
+       {
+           num = _cairo_uint128_sub (num, den);
+           quo = _cairo_uint128_add (quo, bit);
+       }
+       bit = _cairo_uint128_rsl (bit, 1);
+       den = _cairo_uint128_rsl (den, 1);
+    }
+    qr.quo = quo;
+    qr.rem = num;
+    return qr;
+}
+
+cairo_int128_t
+_cairo_int128_negate (cairo_int128_t a)
+{
+    a.lo = _cairo_uint64_not (a.lo);
+    a.hi = _cairo_uint64_not (a.hi);
+    return _cairo_uint128_add (a, _cairo_uint32_to_uint128 (1));
+}
+
+cairo_int128_t
+_cairo_int128_not (cairo_int128_t a)
+{
+    a.lo = _cairo_uint64_not (a.lo);
+    a.hi = _cairo_uint64_not (a.hi);
+    return a;
+}
+
+#endif /* !HAVE_UINT128_T */
+
+cairo_quorem128_t
+_cairo_int128_divrem (cairo_int128_t num, cairo_int128_t den)
+{
+    int                        num_neg = _cairo_int128_negative (num);
+    int                        den_neg = _cairo_int128_negative (den);
+    cairo_uquorem128_t uqr;
+    cairo_quorem128_t  qr;
+
+    if (num_neg)
+       num = _cairo_int128_negate (num);
+    if (den_neg)
+       den = _cairo_int128_negate (den);
+    uqr = _cairo_uint128_divrem (num, den);
+    if (num_neg)
+       qr.rem = _cairo_int128_negate (uqr.rem);
+    else
+       qr.rem = uqr.rem;
+    if (num_neg != den_neg)
+       qr.quo = _cairo_int128_negate (uqr.quo);
+    else
+       qr.quo = uqr.quo;
+    return qr;
+}
+
+/**
+ * _cairo_uint_96by64_32x64_divrem:
+ *
+ * Compute a 32 bit quotient and 64 bit remainder of a 96 bit unsigned
+ * dividend and 64 bit divisor.  If the quotient doesn't fit into 32
+ * bits then the returned remainder is equal to the divisor, and the
+ * quotient is the largest representable 64 bit integer.  It is an
+ * error to call this function with the high 32 bits of @num being
+ * non-zero. */
+cairo_uquorem64_t
+_cairo_uint_96by64_32x64_divrem (cairo_uint128_t num,
+                                cairo_uint64_t den)
+{
+    cairo_uquorem64_t result;
+    cairo_uint64_t B = _cairo_uint32s_to_uint64 (1, 0);
+
+    /* These are the high 64 bits of the *96* bit numerator.  We're
+     * going to represent the numerator as xB + y, where x is a 64,
+     * and y is a 32 bit number. */
+    cairo_uint64_t x = _cairo_uint128_to_uint64 (_cairo_uint128_rsl(num, 32));
+
+    /* Initialise the result to indicate overflow. */
+    result.quo = _cairo_uint32s_to_uint64 (-1U, -1U);
+    result.rem = den;
+
+    /* Don't bother if the quotient is going to overflow. */
+    if (_cairo_uint64_ge (x, den)) {
+       return /* overflow */ result;
+    }
+
+    if (_cairo_uint64_lt (x, B)) {
+       /* When the final quotient is known to fit in 32 bits, then
+        * num < 2^64 if and only if den < 2^32. */
+       return _cairo_uint64_divrem (_cairo_uint128_to_uint64 (num), den);
+    }
+    else {
+       /* Denominator is >= 2^32. the numerator is >= 2^64, and the
+        * division won't overflow: need two divrems.  Write the
+        * numerator and denominator as
+        *
+        *      num = xB + y            x : 64 bits, y : 32 bits
+        *      den = uB + v            u, v : 32 bits
+        */
+       uint32_t y = _cairo_uint128_to_uint32 (num);
+       uint32_t u = uint64_hi32 (den);
+       uint32_t v = _cairo_uint64_to_uint32 (den);
+
+       /* Compute a lower bound approximate quotient of num/den
+        * from x/(u+1).  Then we have
+        *
+        * x    = q(u+1) + r    ; q : 32 bits, r <= u : 32 bits.
+        *
+        * xB + y       = q(u+1)B       + (rB+y)
+        *              = q(uB + B + v - v) + (rB+y)
+        *              = q(uB + v)     + qB - qv + (rB+y)
+        *              = q(uB + v)     + q(B-v) + (rB+y)
+        *
+        * The true quotient of num/den then is q plus the
+        * contribution of q(B-v) + (rB+y).  The main contribution
+        * comes from the term q(B-v), with the term (rB+y) only
+        * contributing at most one part.
+        *
+        * The term q(B-v) must fit into 64 bits, since q fits into 32
+        * bits on account of being a lower bound to the true
+        * quotient, and as B-v <= 2^32, we may safely use a single
+        * 64/64 bit division to find its contribution. */
+
+       cairo_uquorem64_t quorem;
+       cairo_uint64_t remainder; /* will contain final remainder */
+       uint32_t quotient;      /* will contain final quotient. */
+       uint32_t q;
+       uint32_t r;
+
+       /* Approximate quotient by dividing the high 64 bits of num by
+        * u+1. Watch out for overflow of u+1. */
+       if (u+1) {
+           quorem = _cairo_uint64_divrem (x, _cairo_uint32_to_uint64 (u+1));
+           q = _cairo_uint64_to_uint32 (quorem.quo);
+           r = _cairo_uint64_to_uint32 (quorem.rem);
+       }
+       else {
+           q = uint64_hi32 (x);
+           r = _cairo_uint64_to_uint32 (x);
+       }
+       quotient = q;
+
+       /* Add the main term's contribution to quotient.  Note B-v =
+        * -v as an uint32 (unless v = 0) */
+       if (v)
+           quorem = _cairo_uint64_divrem (_cairo_uint32x32_64_mul (q, -v), den);
+       else
+           quorem = _cairo_uint64_divrem (_cairo_uint32s_to_uint64 (q, 0), den);
+       quotient += _cairo_uint64_to_uint32 (quorem.quo);
+
+       /* Add the contribution of the subterm and start computing the
+        * true remainder. */
+       remainder = _cairo_uint32s_to_uint64 (r, y);
+       if (_cairo_uint64_ge (remainder, den)) {
+           remainder = _cairo_uint64_sub (remainder, den);
+           quotient++;
+       }
+
+       /* Add the contribution of the main term's remainder. The
+        * funky test here checks that remainder + main_rem >= den,
+        * taking into account overflow of the addition. */
+       remainder = _cairo_uint64_add (remainder, quorem.rem);
+       if (_cairo_uint64_ge (remainder, den) ||
+           _cairo_uint64_lt (remainder, quorem.rem))
+       {
+           remainder = _cairo_uint64_sub (remainder, den);
+           quotient++;
+       }
+
+       result.quo = _cairo_uint32_to_uint64 (quotient);
+       result.rem = remainder;
+    }
+    return result;
+}
+
+cairo_quorem64_t
+_cairo_int_96by64_32x64_divrem (cairo_int128_t num, cairo_int64_t den)
+{
+    int                        num_neg = _cairo_int128_negative (num);
+    int                        den_neg = _cairo_int64_negative (den);
+    cairo_uint64_t     nonneg_den;
+    cairo_uquorem64_t  uqr;
+    cairo_quorem64_t   qr;
+
+    if (num_neg)
+       num = _cairo_int128_negate (num);
+    if (den_neg)
+       nonneg_den = _cairo_int64_negate (den);
+    else
+       nonneg_den = den;
+
+    uqr = _cairo_uint_96by64_32x64_divrem (num, nonneg_den);
+    if (_cairo_uint64_eq (uqr.rem, nonneg_den)) {
+       /* bail on overflow. */
+       qr.quo = _cairo_uint32s_to_uint64 (0x7FFFFFFF, -1U);
+       qr.rem = den;
+       return qr;
+    }
+
+    if (num_neg)
+       qr.rem = _cairo_int64_negate (uqr.rem);
+    else
+       qr.rem = uqr.rem;
+    if (num_neg != den_neg)
+       qr.quo = _cairo_int64_negate (uqr.quo);
+    else
+       qr.quo = uqr.quo;
+    return qr;
+}
diff --git a/src/cairo-win32-font.c b/src/cairo-win32-font.c
new file mode 100644 (file)
index 0000000..73fc052
--- /dev/null
@@ -0,0 +1,2437 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2005 Red Hat, Inc
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ */
+
+#define WIN32_LEAN_AND_MEAN
+/* We require Windows 2000 features such as GetGlyphIndices */
+#if !defined(WINVER) || (WINVER < 0x0500)
+# define WINVER 0x0500
+#endif
+#if !defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x0500)
+# define _WIN32_WINNT 0x0500
+#endif
+
+#include "cairoint.h"
+
+#include "cairo-win32-private.h"
+
+#include "cairo-array-private.h"
+#include "cairo-error-private.h"
+#include "cairo-image-surface-private.h"
+#include "cairo-pattern-private.h"
+#include "cairo-scaled-font-subsets-private.h"
+
+#include <wchar.h>
+
+#ifndef SPI_GETFONTSMOOTHINGTYPE
+#define SPI_GETFONTSMOOTHINGTYPE 0x200a
+#endif
+#ifndef FE_FONTSMOOTHINGCLEARTYPE
+#define FE_FONTSMOOTHINGCLEARTYPE 2
+#endif
+#ifndef CLEARTYPE_QUALITY
+#define CLEARTYPE_QUALITY 5
+#endif
+#ifndef TT_PRIM_CSPLINE
+#define TT_PRIM_CSPLINE 3
+#endif
+
+#define CMAP_TAG 0x70616d63
+
+/**
+ * SECTION:cairo-win32-fonts
+ * @Title: Win32 Fonts
+ * @Short_Description: Font support for Microsoft Windows
+ * @See_Also: #cairo_font_face_t
+ *
+ * The Microsoft Windows font backend is primarily used to render text on
+ * Microsoft Windows systems.
+ */
+
+/**
+ * CAIRO_HAS_WIN32_FONT:
+ *
+ * Defined if the Microsoft Windows font backend is available.
+ * This macro can be used to conditionally compile backend-specific code.
+ */
+
+const cairo_scaled_font_backend_t _cairo_win32_scaled_font_backend;
+
+typedef struct {
+    cairo_scaled_font_t base;
+
+    LOGFONTW logfont;
+
+    BYTE quality;
+
+    /* We do drawing and metrics computation in a "logical space" which
+     * is similar to font space, except that it is scaled by a factor
+     * of the (desired font size) * (WIN32_FONT_LOGICAL_SCALE). The multiplication
+     * by WIN32_FONT_LOGICAL_SCALE allows for sub-pixel precision.
+     */
+    double logical_scale;
+
+    /* The size we should actually request the font at from Windows; differs
+     * from the logical_scale because it is quantized for orthogonal
+     * transformations
+     */
+    double logical_size;
+
+    /* Transformations from device <=> logical space
+     */
+    cairo_matrix_t logical_to_device;
+    cairo_matrix_t device_to_logical;
+
+    /* We special case combinations of 90-degree-rotations, scales and
+     * flips ... that is transformations that take the axes to the
+     * axes. If preserve_axes is true, then swap_axes/swap_x/swap_y
+     * encode the 8 possibilities for orientation (4 rotation angles with
+     * and without a flip), and scale_x, scale_y the scale components.
+     */
+    cairo_bool_t preserve_axes;
+    cairo_bool_t swap_axes;
+    cairo_bool_t swap_x;
+    cairo_bool_t swap_y;
+    double x_scale;
+    double y_scale;
+
+    /* The size of the design unit of the font
+     */
+    int em_square;
+
+    HFONT scaled_hfont;
+    HFONT unscaled_hfont;
+
+    cairo_bool_t is_bitmap;
+    cairo_bool_t is_type1;
+    cairo_bool_t delete_scaled_hfont;
+    cairo_bool_t has_type1_notdef_index;
+    unsigned long type1_notdef_index;
+} cairo_win32_scaled_font_t;
+
+static cairo_status_t
+_cairo_win32_scaled_font_set_metrics (cairo_win32_scaled_font_t *scaled_font);
+
+static cairo_status_t
+_cairo_win32_scaled_font_init_glyph_metrics (cairo_win32_scaled_font_t *scaled_font,
+                                            cairo_scaled_glyph_t      *scaled_glyph);
+
+static cairo_status_t
+_cairo_win32_scaled_font_init_glyph_surface (cairo_win32_scaled_font_t *scaled_font,
+                                             cairo_scaled_glyph_t      *scaled_glyph);
+
+static cairo_status_t
+_cairo_win32_scaled_font_init_glyph_path (cairo_win32_scaled_font_t *scaled_font,
+                                         cairo_scaled_glyph_t      *scaled_glyph);
+
+static void
+_cairo_win32_font_face_destroy (void *abstract_face);
+
+
+#define NEARLY_ZERO(d) (fabs(d) < (1. / 65536.))
+
+static HDC
+_get_global_font_dc (void)
+{
+    static HDC hdc;
+
+    if (!hdc) {
+       hdc = CreateCompatibleDC (NULL);
+       if (!hdc) {
+           _cairo_win32_print_gdi_error ("_get_global_font_dc");
+           return NULL;
+       }
+
+       if (!SetGraphicsMode (hdc, GM_ADVANCED)) {
+           _cairo_win32_print_gdi_error ("_get_global_font_dc");
+           DeleteDC (hdc);
+           return NULL;
+       }
+    }
+
+    return hdc;
+}
+
+static cairo_status_t
+_compute_transform (cairo_win32_scaled_font_t *scaled_font,
+                   cairo_matrix_t            *sc)
+{
+    cairo_status_t status;
+
+    if (NEARLY_ZERO (sc->yx) && NEARLY_ZERO (sc->xy) &&
+           !NEARLY_ZERO(sc->xx) && !NEARLY_ZERO(sc->yy)) {
+       scaled_font->preserve_axes = TRUE;
+       scaled_font->x_scale = sc->xx;
+       scaled_font->swap_x = (sc->xx < 0);
+       scaled_font->y_scale = sc->yy;
+       scaled_font->swap_y = (sc->yy < 0);
+       scaled_font->swap_axes = FALSE;
+
+    } else if (NEARLY_ZERO (sc->xx) && NEARLY_ZERO (sc->yy) &&
+           !NEARLY_ZERO(sc->yx) && !NEARLY_ZERO(sc->xy)) {
+       scaled_font->preserve_axes = TRUE;
+       scaled_font->x_scale = sc->yx;
+       scaled_font->swap_x = (sc->yx < 0);
+       scaled_font->y_scale = sc->xy;
+       scaled_font->swap_y = (sc->xy < 0);
+       scaled_font->swap_axes = TRUE;
+
+    } else {
+       scaled_font->preserve_axes = FALSE;
+       scaled_font->swap_x = scaled_font->swap_y = scaled_font->swap_axes = FALSE;
+    }
+
+    if (scaled_font->preserve_axes) {
+       if (scaled_font->swap_x)
+           scaled_font->x_scale = - scaled_font->x_scale;
+       if (scaled_font->swap_y)
+           scaled_font->y_scale = - scaled_font->y_scale;
+
+       scaled_font->logical_scale = WIN32_FONT_LOGICAL_SCALE * scaled_font->y_scale;
+       scaled_font->logical_size = WIN32_FONT_LOGICAL_SCALE *
+                                    _cairo_lround (scaled_font->y_scale);
+    }
+
+    /* The font matrix has x and y "scale" components which we extract and
+     * use as character scale values.
+     */
+    cairo_matrix_init (&scaled_font->logical_to_device,
+                      sc->xx, sc->yx, sc->xy, sc->yy, 0, 0);
+
+    if (!scaled_font->preserve_axes) {
+       status = _cairo_matrix_compute_basis_scale_factors (&scaled_font->logical_to_device,
+                                                     &scaled_font->x_scale, &scaled_font->y_scale,
+                                                     TRUE);    /* XXX: Handle vertical text */
+       if (status)
+           return status;
+
+       scaled_font->logical_size = _cairo_lround (WIN32_FONT_LOGICAL_SCALE *
+                                                   scaled_font->y_scale);
+       scaled_font->logical_scale = WIN32_FONT_LOGICAL_SCALE * scaled_font->y_scale;
+    }
+
+    cairo_matrix_scale (&scaled_font->logical_to_device,
+                       1.0 / scaled_font->logical_scale, 1.0 / scaled_font->logical_scale);
+
+    scaled_font->device_to_logical = scaled_font->logical_to_device;
+
+    status = cairo_matrix_invert (&scaled_font->device_to_logical);
+    if (status)
+       cairo_matrix_init_identity (&scaled_font->device_to_logical);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_bool_t
+_have_cleartype_quality (void)
+{
+    OSVERSIONINFO version_info;
+
+    version_info.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
+
+    if (!GetVersionEx (&version_info)) {
+       _cairo_win32_print_gdi_error ("_have_cleartype_quality");
+       return FALSE;
+    }
+
+    return (version_info.dwMajorVersion > 5 ||
+           (version_info.dwMajorVersion == 5 &&
+            version_info.dwMinorVersion >= 1));        /* XP or newer */
+}
+
+static BYTE
+_get_system_quality (void)
+{
+    BOOL font_smoothing;
+    UINT smoothing_type;
+
+    if (!SystemParametersInfo (SPI_GETFONTSMOOTHING, 0, &font_smoothing, 0)) {
+       _cairo_win32_print_gdi_error ("_get_system_quality");
+       return DEFAULT_QUALITY;
+    }
+
+    if (font_smoothing) {
+       if (_have_cleartype_quality ()) {
+           if (!SystemParametersInfo (SPI_GETFONTSMOOTHINGTYPE,
+                                      0, &smoothing_type, 0)) {
+               _cairo_win32_print_gdi_error ("_get_system_quality");
+               return DEFAULT_QUALITY;
+           }
+
+           if (smoothing_type == FE_FONTSMOOTHINGCLEARTYPE)
+               return CLEARTYPE_QUALITY;
+       }
+
+       return ANTIALIASED_QUALITY;
+    } else {
+       return DEFAULT_QUALITY;
+    }
+}
+
+/* If face_hfont is non-%NULL then font_matrix must be a simple scale by some
+ * factor S, ctm must be the identity, logfont->lfHeight must be -S,
+ * logfont->lfWidth, logfont->lfEscapement, logfont->lfOrientation must
+ * all be 0, and face_hfont is the result of calling CreateFontIndirectW on
+ * logfont.
+ */
+static cairo_status_t
+_win32_scaled_font_create (LOGFONTW                   *logfont,
+                          HFONT                      face_hfont,
+                          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       **font_out)
+{
+    HDC hdc;
+    cairo_win32_scaled_font_t *f;
+    cairo_matrix_t scale;
+    cairo_status_t status;
+
+    hdc = _get_global_font_dc ();
+    if (hdc == NULL)
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    f = malloc (sizeof(cairo_win32_scaled_font_t));
+    if (f == NULL)
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    f->logfont = *logfont;
+
+    /* We don't have any control over the hinting style or subpixel
+     * order in the Win32 font API, so we ignore those parts of
+     * cairo_font_options_t. We use the 'antialias' field to set
+     * the 'quality'.
+     *
+     * XXX: The other option we could pay attention to, but don't
+     *      here is the hint_metrics options.
+     */
+    if (options->antialias == CAIRO_ANTIALIAS_DEFAULT)
+       f->quality = _get_system_quality ();
+    else {
+       switch (options->antialias) {
+       case CAIRO_ANTIALIAS_NONE:
+           f->quality = NONANTIALIASED_QUALITY;
+           break;
+       case CAIRO_ANTIALIAS_GRAY:
+       case CAIRO_ANTIALIAS_FAST:
+       case CAIRO_ANTIALIAS_GOOD:
+           f->quality = ANTIALIASED_QUALITY;
+           break;
+       case CAIRO_ANTIALIAS_SUBPIXEL:
+       case CAIRO_ANTIALIAS_BEST:
+           if (_have_cleartype_quality ())
+               f->quality = CLEARTYPE_QUALITY;
+           else
+               f->quality = ANTIALIASED_QUALITY;
+           break;
+       case CAIRO_ANTIALIAS_DEFAULT:
+           ASSERT_NOT_REACHED;
+       }
+    }
+
+    f->em_square = 0;
+    f->scaled_hfont = NULL;
+    f->unscaled_hfont = NULL;
+    f->has_type1_notdef_index = FALSE;
+
+    if (f->quality == logfont->lfQuality ||
+        (logfont->lfQuality == DEFAULT_QUALITY &&
+         options->antialias == CAIRO_ANTIALIAS_DEFAULT)) {
+        /* If face_hfont is non-NULL, then we can use it to avoid creating our
+         * own --- because the constraints on face_hfont mentioned above
+         * guarantee it was created in exactly the same way that
+         * _win32_scaled_font_get_scaled_hfont would create it.
+         */
+        f->scaled_hfont = face_hfont;
+    }
+    /* don't delete the hfont if we're using the one passed in to us */
+    f->delete_scaled_hfont = !f->scaled_hfont;
+
+    cairo_matrix_multiply (&scale, font_matrix, ctm);
+    status = _compute_transform (f, &scale);
+    if (status)
+       goto FAIL;
+
+    status = _cairo_scaled_font_init (&f->base, font_face,
+                                     font_matrix, ctm, options,
+                                     &_cairo_win32_scaled_font_backend);
+    if (status)
+       goto FAIL;
+
+    status = _cairo_win32_scaled_font_set_metrics (f);
+    if (status) {
+       _cairo_scaled_font_fini (&f->base);
+       goto FAIL;
+    }
+
+    *font_out = &f->base;
+    return CAIRO_STATUS_SUCCESS;
+
+ FAIL:
+    free (f);
+    return status;
+}
+
+static cairo_status_t
+_win32_scaled_font_set_world_transform (cairo_win32_scaled_font_t *scaled_font,
+                                       HDC                        hdc)
+{
+    XFORM xform;
+
+    _cairo_matrix_to_win32_xform (&scaled_font->logical_to_device, &xform);
+
+    if (!SetWorldTransform (hdc, &xform))
+       return _cairo_win32_print_gdi_error ("_win32_scaled_font_set_world_transform");
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_win32_scaled_font_set_identity_transform (HDC hdc)
+{
+    if (!ModifyWorldTransform (hdc, NULL, MWT_IDENTITY))
+       return _cairo_win32_print_gdi_error ("_win32_scaled_font_set_identity_transform");
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_win32_scaled_font_get_scaled_hfont (cairo_win32_scaled_font_t *scaled_font,
+                                    HFONT *hfont_out)
+{
+    if (!scaled_font->scaled_hfont) {
+       LOGFONTW logfont = scaled_font->logfont;
+       logfont.lfHeight = -scaled_font->logical_size;
+       logfont.lfWidth = 0;
+       logfont.lfEscapement = 0;
+       logfont.lfOrientation = 0;
+       logfont.lfQuality = scaled_font->quality;
+
+       scaled_font->scaled_hfont = CreateFontIndirectW (&logfont);
+       if (!scaled_font->scaled_hfont)
+           return _cairo_win32_print_gdi_error ("_win32_scaled_font_get_scaled_hfont");
+    }
+
+    *hfont_out = scaled_font->scaled_hfont;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_win32_scaled_font_get_unscaled_hfont (cairo_win32_scaled_font_t *scaled_font,
+                                      HDC                        hdc,
+                                      HFONT                     *hfont_out)
+{
+    if (scaled_font->unscaled_hfont == NULL) {
+       OUTLINETEXTMETRIC *otm;
+       unsigned int otm_size;
+       HFONT scaled_hfont;
+       LOGFONTW logfont;
+       cairo_status_t status;
+
+       status = _win32_scaled_font_get_scaled_hfont (scaled_font,
+                                                     &scaled_hfont);
+       if (status)
+           return status;
+
+       if (! SelectObject (hdc, scaled_hfont))
+           return _cairo_win32_print_gdi_error ("_win32_scaled_font_get_unscaled_hfont:SelectObject");
+
+       otm_size = GetOutlineTextMetrics (hdc, 0, NULL);
+       if (! otm_size)
+           return _cairo_win32_print_gdi_error ("_win32_scaled_font_get_unscaled_hfont:GetOutlineTextMetrics");
+
+       otm = malloc (otm_size);
+       if (otm == NULL)
+           return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+       if (! GetOutlineTextMetrics (hdc, otm_size, otm)) {
+           status = _cairo_win32_print_gdi_error ("_win32_scaled_font_get_unscaled_hfont:GetOutlineTextMetrics");
+           free (otm);
+           return status;
+       }
+
+       scaled_font->em_square = otm->otmEMSquare;
+       free (otm);
+
+       logfont = scaled_font->logfont;
+       logfont.lfHeight = -scaled_font->em_square;
+       logfont.lfWidth = 0;
+       logfont.lfEscapement = 0;
+       logfont.lfOrientation = 0;
+       logfont.lfQuality = scaled_font->quality;
+
+       scaled_font->unscaled_hfont = CreateFontIndirectW (&logfont);
+       if (! scaled_font->unscaled_hfont)
+           return _cairo_win32_print_gdi_error ("_win32_scaled_font_get_unscaled_hfont:CreateIndirect");
+    }
+
+    *hfont_out = scaled_font->unscaled_hfont;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_win32_scaled_font_select_unscaled_font (cairo_scaled_font_t *scaled_font,
+                                              HDC                  hdc)
+{
+    cairo_status_t status;
+    HFONT hfont;
+    HFONT old_hfont = NULL;
+
+    status = _win32_scaled_font_get_unscaled_hfont ((cairo_win32_scaled_font_t *)scaled_font, hdc, &hfont);
+    if (status)
+       return status;
+
+    old_hfont = SelectObject (hdc, hfont);
+    if (!old_hfont)
+       return _cairo_win32_print_gdi_error ("_cairo_win32_scaled_font_select_unscaled_font");
+
+    status = _win32_scaled_font_set_identity_transform (hdc);
+    if (status) {
+       SelectObject (hdc, old_hfont);
+       return status;
+    }
+
+    SetMapMode (hdc, MM_TEXT);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_bool_t
+_cairo_win32_scaled_font_is_type1 (cairo_scaled_font_t *scaled_font)
+{
+    cairo_win32_scaled_font_t *win32_scaled_font;
+
+    win32_scaled_font = (cairo_win32_scaled_font_t *) scaled_font;
+
+    return win32_scaled_font->is_type1;
+}
+
+cairo_bool_t
+_cairo_win32_scaled_font_is_bitmap (cairo_scaled_font_t *scaled_font)
+{
+    cairo_win32_scaled_font_t *win32_scaled_font;
+
+    win32_scaled_font = (cairo_win32_scaled_font_t *) scaled_font;
+
+    return win32_scaled_font->is_bitmap;
+}
+
+static void
+_cairo_win32_scaled_font_done_unscaled_font (cairo_scaled_font_t *scaled_font)
+{
+}
+
+/* implement the font backend interface */
+
+static cairo_status_t
+_cairo_win32_font_face_create_for_toy (cairo_toy_font_face_t   *toy_face,
+                                      cairo_font_face_t      **font_face)
+{
+    LOGFONTW logfont;
+    uint16_t *face_name;
+    int face_name_len;
+    cairo_status_t status;
+
+    status = _cairo_utf8_to_utf16 (toy_face->family, -1,
+                                  &face_name, &face_name_len);
+    if (status)
+       return status;
+
+    if (face_name_len > LF_FACESIZE - 1)
+       face_name_len = LF_FACESIZE - 1;
+
+    memcpy (logfont.lfFaceName, face_name, sizeof (uint16_t) * face_name_len);
+    logfont.lfFaceName[face_name_len] = 0;
+    free (face_name);
+
+    logfont.lfHeight = 0;      /* filled in later */
+    logfont.lfWidth = 0;       /* filled in later */
+    logfont.lfEscapement = 0;  /* filled in later */
+    logfont.lfOrientation = 0; /* filled in later */
+
+    switch (toy_face->weight) {
+    case CAIRO_FONT_WEIGHT_NORMAL:
+    default:
+       logfont.lfWeight = FW_NORMAL;
+       break;
+    case CAIRO_FONT_WEIGHT_BOLD:
+       logfont.lfWeight = FW_BOLD;
+       break;
+    }
+
+    switch (toy_face->slant) {
+    case CAIRO_FONT_SLANT_NORMAL:
+    default:
+       logfont.lfItalic = FALSE;
+       break;
+    case CAIRO_FONT_SLANT_ITALIC:
+    case CAIRO_FONT_SLANT_OBLIQUE:
+       logfont.lfItalic = TRUE;
+       break;
+    }
+
+    logfont.lfUnderline = FALSE;
+    logfont.lfStrikeOut = FALSE;
+    /* The docs for LOGFONT discourage using this, since the
+     * interpretation is locale-specific, but it's not clear what
+     * would be a better alternative.
+     */
+    logfont.lfCharSet = DEFAULT_CHARSET;
+    logfont.lfOutPrecision = OUT_DEFAULT_PRECIS;
+    logfont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
+    logfont.lfQuality = DEFAULT_QUALITY; /* filled in later */
+    logfont.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
+
+    *font_face = cairo_win32_font_face_create_for_logfontw (&logfont);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_win32_scaled_font_fini (void *abstract_font)
+{
+    cairo_win32_scaled_font_t *scaled_font = abstract_font;
+
+    if (scaled_font == NULL)
+       return;
+
+    if (scaled_font->scaled_hfont && scaled_font->delete_scaled_hfont)
+       DeleteObject (scaled_font->scaled_hfont);
+
+    if (scaled_font->unscaled_hfont)
+       DeleteObject (scaled_font->unscaled_hfont);
+}
+
+static cairo_int_status_t
+_cairo_win32_scaled_font_type1_text_to_glyphs (cairo_win32_scaled_font_t *scaled_font,
+                                              double                     x,
+                                              double                     y,
+                                              const char                *utf8,
+                                              cairo_glyph_t            **glyphs,
+                                              int                       *num_glyphs)
+{
+    uint16_t *utf16;
+    int n16;
+    int i;
+    WORD *glyph_indices = NULL;
+    cairo_status_t status;
+    double x_pos, y_pos;
+    HDC hdc = NULL;
+    cairo_matrix_t mat;
+
+    status = _cairo_utf8_to_utf16 (utf8, -1, &utf16, &n16);
+    if (status)
+       return status;
+
+    glyph_indices = _cairo_malloc_ab (n16 + 1, sizeof (WORD));
+    if (!glyph_indices) {
+       status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+       goto FAIL1;
+    }
+
+    hdc = _get_global_font_dc ();
+    assert (hdc != NULL);
+
+    status = cairo_win32_scaled_font_select_font (&scaled_font->base, hdc);
+    if (status)
+       goto FAIL2;
+
+    if (GetGlyphIndicesW (hdc, utf16, n16, glyph_indices, 0) == GDI_ERROR) {
+       status = _cairo_win32_print_gdi_error ("_cairo_win32_scaled_font_type1_text_to_glyphs:GetGlyphIndicesW");
+       goto FAIL3;
+    }
+
+    *num_glyphs = n16;
+    *glyphs = _cairo_malloc_ab (n16, sizeof (cairo_glyph_t));
+    if (!*glyphs) {
+       status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+       goto FAIL3;
+    }
+
+    x_pos = x;
+    y_pos = y;
+
+    mat = scaled_font->base.ctm;
+    status = cairo_matrix_invert (&mat);
+    assert (status == CAIRO_STATUS_SUCCESS);
+
+    _cairo_scaled_font_freeze_cache (&scaled_font->base);
+
+    for (i = 0; i < n16; i++) {
+       cairo_scaled_glyph_t *scaled_glyph;
+
+       (*glyphs)[i].index = glyph_indices[i];
+       (*glyphs)[i].x = x_pos;
+       (*glyphs)[i].y = y_pos;
+
+       status = _cairo_scaled_glyph_lookup (&scaled_font->base,
+                                            glyph_indices[i],
+                                            CAIRO_SCALED_GLYPH_INFO_METRICS,
+                                            &scaled_glyph);
+       if (status) {
+           free (*glyphs);
+           *glyphs = NULL;
+           break;
+       }
+
+       x = scaled_glyph->x_advance;
+       y = scaled_glyph->y_advance;
+       cairo_matrix_transform_distance (&mat, &x, &y);
+       x_pos += x;
+       y_pos += y;
+    }
+
+    _cairo_scaled_font_thaw_cache (&scaled_font->base);
+
+FAIL3:
+    cairo_win32_scaled_font_done_font (&scaled_font->base);
+FAIL2:
+    free (glyph_indices);
+FAIL1:
+    free (utf16);
+
+    return status;
+}
+
+static cairo_int_status_t
+_cairo_win32_scaled_font_text_to_glyphs (void          *abstract_font,
+                                        double         x,
+                                        double         y,
+                                        const char     *utf8,
+                                        cairo_glyph_t **glyphs,
+                                        int            *num_glyphs)
+{
+    cairo_win32_scaled_font_t *scaled_font = abstract_font;
+    uint16_t *utf16;
+    int n16;
+    GCP_RESULTSW gcp_results;
+    unsigned int buffer_size, i;
+    WCHAR *glyph_indices = NULL;
+    int *dx = NULL;
+    cairo_status_t status;
+    double x_pos, y_pos;
+    double x_incr, y_incr;
+    HDC hdc = NULL;
+
+    /* GetCharacterPlacement() returns utf16 instead of glyph indices
+     * for Type 1 fonts. Use GetGlyphIndices for Type 1 fonts. */
+    if (scaled_font->is_type1)
+        return _cairo_win32_scaled_font_type1_text_to_glyphs (scaled_font,
+                                                              x,
+                                                              y,
+                                                              utf8,
+                                                              glyphs,
+                                                              num_glyphs);
+
+    /* Compute a vector in user space along the baseline of length one logical space unit */
+    x_incr = 1;
+    y_incr = 0;
+    cairo_matrix_transform_distance (&scaled_font->base.font_matrix, &x_incr, &y_incr);
+    x_incr /= scaled_font->logical_scale;
+    y_incr /= scaled_font->logical_scale;
+
+    status = _cairo_utf8_to_utf16 (utf8, -1, &utf16, &n16);
+    if (status)
+       return status;
+
+    gcp_results.lStructSize = sizeof (GCP_RESULTS);
+    gcp_results.lpOutString = NULL;
+    gcp_results.lpOrder = NULL;
+    gcp_results.lpCaretPos = NULL;
+    gcp_results.lpClass = NULL;
+
+    buffer_size = MAX (n16 * 1.2, 16);         /* Initially guess number of chars plus a few */
+    if (buffer_size > INT_MAX) {
+       status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+       goto FAIL1;
+    }
+
+    hdc = _get_global_font_dc ();
+    assert (hdc != NULL);
+
+    status = cairo_win32_scaled_font_select_font (&scaled_font->base, hdc);
+    if (status)
+       goto FAIL1;
+
+    while (TRUE) {
+       free (glyph_indices);
+       glyph_indices = NULL;
+
+       free (dx);
+       dx = NULL;
+
+       glyph_indices = _cairo_malloc_ab (buffer_size, sizeof (WCHAR));
+       dx = _cairo_malloc_ab (buffer_size, sizeof (int));
+       if (!glyph_indices || !dx) {
+           status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+           goto FAIL2;
+       }
+
+       gcp_results.nGlyphs = buffer_size;
+       gcp_results.lpDx = dx;
+       gcp_results.lpGlyphs = glyph_indices;
+
+       if (!GetCharacterPlacementW (hdc, utf16, n16,
+                                    0,
+                                    &gcp_results,
+                                    GCP_DIACRITIC | GCP_LIGATE | GCP_GLYPHSHAPE | GCP_REORDER)) {
+           status = _cairo_win32_print_gdi_error ("_cairo_win32_scaled_font_text_to_glyphs");
+           goto FAIL2;
+       }
+
+       if (gcp_results.lpDx && gcp_results.lpGlyphs)
+           break;
+
+       /* Too small a buffer, try again */
+
+       buffer_size += buffer_size / 2;
+       if (buffer_size > INT_MAX) {
+           status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+           goto FAIL2;
+       }
+    }
+
+    *num_glyphs = gcp_results.nGlyphs;
+    *glyphs = _cairo_malloc_ab (gcp_results.nGlyphs, sizeof (cairo_glyph_t));
+    if (!*glyphs) {
+       status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+       goto FAIL2;
+    }
+
+    x_pos = x;
+    y_pos = y;
+
+    for (i = 0; i < gcp_results.nGlyphs; i++) {
+       (*glyphs)[i].index = glyph_indices[i];
+       (*glyphs)[i].x = x_pos ;
+       (*glyphs)[i].y = y_pos;
+
+       x_pos += x_incr * dx[i];
+       y_pos += y_incr * dx[i];
+    }
+
+ FAIL2:
+    free (glyph_indices);
+    free (dx);
+
+    cairo_win32_scaled_font_done_font (&scaled_font->base);
+
+ FAIL1:
+    free (utf16);
+
+    return status;
+}
+
+static unsigned long
+_cairo_win32_scaled_font_ucs4_to_index (void           *abstract_font,
+                                       uint32_t         ucs4)
+{
+    cairo_win32_scaled_font_t *scaled_font = abstract_font;
+    wchar_t unicode[2];
+    WORD glyph_index;
+    HDC hdc = NULL;
+    cairo_status_t status;
+
+    hdc = _get_global_font_dc ();
+    assert (hdc != NULL);
+
+    status = cairo_win32_scaled_font_select_font (&scaled_font->base, hdc);
+    if (status)
+       return 0;
+
+    unicode[0] = ucs4;
+    unicode[1] = 0;
+    if (GetGlyphIndicesW (hdc, unicode, 1, &glyph_index, 0) == GDI_ERROR) {
+       _cairo_win32_print_gdi_error ("_cairo_win32_scaled_font_ucs4_to_index:GetGlyphIndicesW");
+       glyph_index = 0;
+    }
+
+    cairo_win32_scaled_font_done_font (&scaled_font->base);
+
+    return glyph_index;
+}
+
+static cairo_status_t
+_cairo_win32_scaled_font_set_metrics (cairo_win32_scaled_font_t *scaled_font)
+{
+    cairo_status_t status;
+    cairo_font_extents_t extents;
+
+    TEXTMETRIC metrics;
+    HDC hdc;
+
+    hdc = _get_global_font_dc ();
+    assert (hdc != NULL);
+
+    if (scaled_font->preserve_axes || scaled_font->base.options.hint_metrics == CAIRO_HINT_METRICS_OFF) {
+       /* For 90-degree rotations (including 0), we get the metrics
+        * from the GDI in logical space, then convert back to font space
+        */
+       status = cairo_win32_scaled_font_select_font (&scaled_font->base, hdc);
+       if (status)
+           return status;
+       GetTextMetrics (hdc, &metrics);
+       cairo_win32_scaled_font_done_font (&scaled_font->base);
+
+       extents.ascent = metrics.tmAscent / scaled_font->logical_scale;
+       extents.descent = metrics.tmDescent / scaled_font->logical_scale;
+
+       extents.height = (metrics.tmHeight + metrics.tmExternalLeading) / scaled_font->logical_scale;
+       extents.max_x_advance = metrics.tmMaxCharWidth / scaled_font->logical_scale;
+       extents.max_y_advance = 0;
+
+    } else {
+       /* For all other transformations, we use the design metrics
+        * of the font. The GDI results from GetTextMetrics() on a
+        * transformed font are inexplicably large and we want to
+        * avoid them.
+        */
+       status = _cairo_win32_scaled_font_select_unscaled_font (&scaled_font->base, hdc);
+       if (status)
+           return status;
+       GetTextMetrics (hdc, &metrics);
+       _cairo_win32_scaled_font_done_unscaled_font (&scaled_font->base);
+
+       extents.ascent = (double)metrics.tmAscent / scaled_font->em_square;
+       extents.descent = (double)metrics.tmDescent / scaled_font->em_square;
+       extents.height = (double)(metrics.tmHeight + metrics.tmExternalLeading) / scaled_font->em_square;
+       extents.max_x_advance = (double)(metrics.tmMaxCharWidth) / scaled_font->em_square;
+       extents.max_y_advance = 0;
+
+    }
+
+    scaled_font->is_bitmap = !(metrics.tmPitchAndFamily & TMPF_VECTOR);
+
+    /* Need to determine if this is a Type 1 font for the special
+     * handling in _text_to_glyphs.  Unlike TrueType or OpenType,
+     * Type1 fonts do not have a "cmap" table (or any other table).
+     * However GetFontData() will retrieve a Type1 font when
+     * requesting that GetFontData() retrieve data from the start of
+     * the file. This is to distinguish Type1 from stroke fonts such
+     * as "Script" and "Modern". The TMPF_TRUETYPE test is redundant
+     * but improves performance for the most common fonts.
+     */
+    scaled_font->is_type1 = FALSE;
+    if (!(metrics.tmPitchAndFamily & TMPF_TRUETYPE) &&
+       (metrics.tmPitchAndFamily & TMPF_VECTOR))
+    {
+        if ((GetFontData (hdc, CMAP_TAG, 0, NULL, 0) == GDI_ERROR) &&
+            (GetFontData (hdc, 0, 0, NULL, 0) != GDI_ERROR))
+        {
+             scaled_font->is_type1 = TRUE;
+        }
+    }
+
+    return _cairo_scaled_font_set_metrics (&scaled_font->base, &extents);
+}
+
+static cairo_status_t
+_cairo_win32_scaled_font_init_glyph_metrics (cairo_win32_scaled_font_t *scaled_font,
+                                            cairo_scaled_glyph_t      *scaled_glyph)
+{
+    static const MAT2 matrix = { { 0, 1 }, { 0, 0 }, { 0, 0 }, { 0, 1 } };
+    GLYPHMETRICS metrics;
+    cairo_status_t status;
+    cairo_text_extents_t extents;
+    HDC hdc;
+
+    hdc = _get_global_font_dc ();
+    assert (hdc != NULL);
+
+    if (scaled_font->is_bitmap) {
+       /* GetGlyphOutline will not work. Assume that the glyph does not extend outside the font box. */
+       cairo_font_extents_t font_extents;
+       INT width = 0;
+       UINT charIndex = _cairo_scaled_glyph_index (scaled_glyph);
+
+       cairo_scaled_font_extents (&scaled_font->base, &font_extents);
+
+       status = cairo_win32_scaled_font_select_font (&scaled_font->base, hdc);
+       if (status)
+           return status;
+
+       if (!GetCharWidth32(hdc, charIndex, charIndex, &width)) {
+           status = _cairo_win32_print_gdi_error ("_cairo_win32_scaled_font_init_glyph_metrics:GetCharWidth32");
+           width = 0;
+       }
+       cairo_win32_scaled_font_done_font (&scaled_font->base);
+       if (status)
+           return status;
+
+       extents.x_bearing = 0;
+       extents.y_bearing = scaled_font->base.ctm.yy * (-font_extents.ascent / scaled_font->y_scale);
+       extents.width = width / (WIN32_FONT_LOGICAL_SCALE * scaled_font->x_scale);
+       extents.height = scaled_font->base.ctm.yy * (font_extents.ascent + font_extents.descent) / scaled_font->y_scale;
+       extents.x_advance = extents.width;
+       extents.y_advance = 0;
+    } else if (scaled_font->preserve_axes && scaled_font->base.options.hint_metrics != CAIRO_HINT_METRICS_OFF) {
+       /* If we aren't rotating / skewing the axes, then we get the metrics
+        * from the GDI in device space and convert to font space.
+        */
+       status = cairo_win32_scaled_font_select_font (&scaled_font->base, hdc);
+       if (status)
+           return status;
+
+       if (GetGlyphOutlineW (hdc, _cairo_scaled_glyph_index (scaled_glyph),
+                             GGO_METRICS | GGO_GLYPH_INDEX,
+                             &metrics, 0, NULL, &matrix) == GDI_ERROR) {
+           memset (&metrics, 0, sizeof (GLYPHMETRICS));
+       } else {
+           if (metrics.gmBlackBoxX > 0 && scaled_font->base.options.antialias != CAIRO_ANTIALIAS_NONE) {
+               /* The bounding box reported by Windows supposedly contains the glyph's "black" area;
+                * however, antialiasing (especially with ClearType) means that the actual image that
+                * needs to be rendered may "bleed" into the adjacent pixels, mainly on the right side.
+                * To avoid clipping the glyphs when drawn by _cairo_surface_fallback_show_glyphs,
+                * for example, or other code that uses glyph extents to determine the area to update,
+                * we add a pixel of "slop" to left side of the nominal "black" area returned by GDI,
+                * and two pixels to the right (as tests show some glyphs bleed into this column).
+                */
+               metrics.gmptGlyphOrigin.x -= 1;
+               metrics.gmBlackBoxX += 3;
+           }
+       }
+       cairo_win32_scaled_font_done_font (&scaled_font->base);
+
+       if (scaled_font->swap_axes) {
+           extents.x_bearing = - metrics.gmptGlyphOrigin.y / scaled_font->y_scale;
+           extents.y_bearing = metrics.gmptGlyphOrigin.x / scaled_font->x_scale;
+           extents.width = metrics.gmBlackBoxY / scaled_font->y_scale;
+           extents.height = metrics.gmBlackBoxX / scaled_font->x_scale;
+           extents.x_advance = metrics.gmCellIncY / scaled_font->x_scale;
+           extents.y_advance = metrics.gmCellIncX / scaled_font->y_scale;
+       } else {
+           extents.x_bearing = metrics.gmptGlyphOrigin.x / scaled_font->x_scale;
+           extents.y_bearing = - metrics.gmptGlyphOrigin.y / scaled_font->y_scale;
+           extents.width = metrics.gmBlackBoxX / scaled_font->x_scale;
+           extents.height = metrics.gmBlackBoxY / scaled_font->y_scale;
+           extents.x_advance = metrics.gmCellIncX / scaled_font->x_scale;
+           extents.y_advance = metrics.gmCellIncY / scaled_font->y_scale;
+       }
+
+       if (scaled_font->swap_x) {
+           extents.x_bearing = (- extents.x_bearing - extents.width);
+           extents.x_advance = - extents.x_advance;
+       }
+
+       if (scaled_font->swap_y) {
+           extents.y_bearing = (- extents.y_bearing - extents.height);
+           extents.y_advance = - extents.y_advance;
+       }
+
+    } else {
+       /* For all other transformations, we use the design metrics
+        * of the font.
+        */
+       status = _cairo_win32_scaled_font_select_unscaled_font (&scaled_font->base, hdc);
+       if (status)
+           return status;
+
+       if (GetGlyphOutlineW (hdc, _cairo_scaled_glyph_index (scaled_glyph),
+                             GGO_METRICS | GGO_GLYPH_INDEX,
+                             &metrics, 0, NULL, &matrix) == GDI_ERROR) {
+           memset (&metrics, 0, sizeof (GLYPHMETRICS));
+       }
+       _cairo_win32_scaled_font_done_unscaled_font (&scaled_font->base);
+
+       extents.x_bearing = (double)metrics.gmptGlyphOrigin.x / scaled_font->em_square;
+       extents.y_bearing = - (double)metrics.gmptGlyphOrigin.y / scaled_font->em_square;
+       extents.width = (double)metrics.gmBlackBoxX / scaled_font->em_square;
+       extents.height = (double)metrics.gmBlackBoxY / scaled_font->em_square;
+       extents.x_advance = (double)metrics.gmCellIncX / scaled_font->em_square;
+       extents.y_advance = (double)metrics.gmCellIncY / scaled_font->em_square;
+    }
+
+    _cairo_scaled_glyph_set_metrics (scaled_glyph,
+                                    &scaled_font->base,
+                                    &extents);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+/* Not currently used code, but may be useful in the future if we add
+ * back the capability to the scaled font backend interface to get the
+ * actual device space bbox rather than computing it from the
+ * font-space metrics.
+ */
+#if 0
+static cairo_status_t
+_cairo_win32_scaled_font_glyph_bbox (void               *abstract_font,
+                                    const cairo_glyph_t *glyphs,
+                                    int                  num_glyphs,
+                                    cairo_box_t         *bbox)
+{
+    static const MAT2 matrix = { { 0, 1 }, { 0, 0 }, { 0, 0 }, { 0, 1 } };
+    cairo_win32_scaled_font_t *scaled_font = abstract_font;
+    int x1 = 0, x2 = 0, y1 = 0, y2 = 0;
+
+    if (num_glyphs > 0) {
+       HDC hdc;
+       GLYPHMETRICS metrics;
+       cairo_status_t status;
+       int i;
+
+       hdc = _get_global_font_dc ();
+       assert (hdc != NULL);
+
+       status = cairo_win32_scaled_font_select_font (&scaled_font->base, hdc);
+       if (status)
+           return status;
+
+       for (i = 0; i < num_glyphs; i++) {
+           int x = _cairo_lround (glyphs[i].x);
+           int y = _cairo_lround (glyphs[i].y);
+
+           GetGlyphOutlineW (hdc, glyphs[i].index, GGO_METRICS | GGO_GLYPH_INDEX,
+                            &metrics, 0, NULL, &matrix);
+
+           if (i == 0 || x1 > x + metrics.gmptGlyphOrigin.x)
+               x1 = x + metrics.gmptGlyphOrigin.x;
+           if (i == 0 || y1 > y - metrics.gmptGlyphOrigin.y)
+               y1 = y - metrics.gmptGlyphOrigin.y;
+           if (i == 0 || x2 < x + metrics.gmptGlyphOrigin.x + (int)metrics.gmBlackBoxX)
+               x2 = x + metrics.gmptGlyphOrigin.x + (int)metrics.gmBlackBoxX;
+           if (i == 0 || y2 < y - metrics.gmptGlyphOrigin.y + (int)metrics.gmBlackBoxY)
+               y2 = y - metrics.gmptGlyphOrigin.y + (int)metrics.gmBlackBoxY;
+       }
+
+       cairo_win32_scaled_font_done_font (&scaled_font->base);
+    }
+
+    bbox->p1.x = _cairo_fixed_from_int (x1);
+    bbox->p1.y = _cairo_fixed_from_int (y1);
+    bbox->p2.x = _cairo_fixed_from_int (x2);
+    bbox->p2.y = _cairo_fixed_from_int (y2);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+#endif
+
+typedef struct {
+    cairo_win32_scaled_font_t *scaled_font;
+    HDC hdc;
+
+    cairo_array_t glyphs;
+    cairo_array_t dx;
+
+    int start_x;
+    int last_x;
+    int last_y;
+} cairo_glyph_state_t;
+
+static void
+_start_glyphs (cairo_glyph_state_t        *state,
+              cairo_win32_scaled_font_t  *scaled_font,
+              HDC                         hdc)
+{
+    state->hdc = hdc;
+    state->scaled_font = scaled_font;
+
+    _cairo_array_init (&state->glyphs, sizeof (WCHAR));
+    _cairo_array_init (&state->dx, sizeof (int));
+}
+
+static cairo_status_t
+_flush_glyphs (cairo_glyph_state_t *state)
+{
+    cairo_status_t status;
+    int dx = 0;
+    WCHAR * elements;
+    int * dx_elements;
+
+    status = _cairo_array_append (&state->dx, &dx);
+    if (status)
+       return status;
+
+    elements = _cairo_array_index (&state->glyphs, 0);
+    dx_elements = _cairo_array_index (&state->dx, 0);
+    if (!ExtTextOutW (state->hdc,
+                     state->start_x, state->last_y,
+                     ETO_GLYPH_INDEX,
+                     NULL,
+                     elements,
+                     state->glyphs.num_elements,
+                     dx_elements)) {
+       return _cairo_win32_print_gdi_error ("_flush_glyphs");
+    }
+
+    _cairo_array_truncate (&state->glyphs, 0);
+    _cairo_array_truncate (&state->dx, 0);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_add_glyph (cairo_glyph_state_t *state,
+           unsigned long        index,
+           double               device_x,
+           double               device_y)
+{
+    cairo_status_t status;
+    double user_x = device_x;
+    double user_y = device_y;
+    WCHAR glyph_index = index;
+    int logical_x, logical_y;
+
+    cairo_matrix_transform_point (&state->scaled_font->device_to_logical, &user_x, &user_y);
+
+    logical_x = _cairo_lround (user_x);
+    logical_y = _cairo_lround (user_y);
+
+    if (state->glyphs.num_elements > 0) {
+       int dx;
+
+       if (logical_y != state->last_y) {
+           status = _flush_glyphs (state);
+           if (status)
+               return status;
+           state->start_x = logical_x;
+       } else {
+           dx = logical_x - state->last_x;
+           status = _cairo_array_append (&state->dx, &dx);
+           if (status)
+               return status;
+       }
+    } else {
+       state->start_x = logical_x;
+    }
+
+    state->last_x = logical_x;
+    state->last_y = logical_y;
+
+    status = _cairo_array_append (&state->glyphs, &glyph_index);
+    if (status)
+       return status;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_finish_glyphs (cairo_glyph_state_t *state)
+{
+    cairo_status_t status;
+
+    status = _flush_glyphs (state);
+
+    _cairo_array_fini (&state->glyphs);
+    _cairo_array_fini (&state->dx);
+
+    return status;
+}
+
+static cairo_status_t
+_draw_glyphs_on_surface (cairo_win32_surface_t     *surface,
+                        cairo_win32_scaled_font_t *scaled_font,
+                        COLORREF                   color,
+                        int                        x_offset,
+                        int                        y_offset,
+                        const cairo_glyph_t       *glyphs,
+                        int                        num_glyphs)
+{
+    cairo_glyph_state_t state;
+    cairo_status_t status, status2;
+    int i;
+
+    if (!SaveDC (surface->dc))
+       return _cairo_win32_print_gdi_error ("_draw_glyphs_on_surface:SaveDC");
+
+    status = cairo_win32_scaled_font_select_font (&scaled_font->base, surface->dc);
+    if (status)
+       goto FAIL1;
+
+    SetTextColor (surface->dc, color);
+    SetTextAlign (surface->dc, TA_BASELINE | TA_LEFT);
+    SetBkMode (surface->dc, TRANSPARENT);
+
+    _start_glyphs (&state, scaled_font, surface->dc);
+
+    for (i = 0; i < num_glyphs; i++) {
+       status = _add_glyph (&state, glyphs[i].index,
+                            glyphs[i].x - x_offset, glyphs[i].y - y_offset);
+       if (status)
+           goto FAIL2;
+    }
+
+ FAIL2:
+    status2 = _finish_glyphs (&state);
+    if (status == CAIRO_STATUS_SUCCESS)
+       status = status2;
+
+    cairo_win32_scaled_font_done_font (&scaled_font->base);
+ FAIL1:
+    RestoreDC (surface->dc, -1);
+
+    return status;
+}
+
+/* Duplicate the green channel of a 4-channel mask in the alpha channel, then
+ * invert the whole mask.
+ */
+static void
+_compute_argb32_mask_alpha (cairo_win32_surface_t *mask_surface)
+{
+    cairo_image_surface_t *image = (cairo_image_surface_t *)mask_surface->image;
+    int i, j;
+
+    for (i = 0; i < image->height; i++) {
+       uint32_t *p = (uint32_t *) (image->data + i * image->stride);
+       for (j = 0; j < image->width; j++) {
+           *p = 0xffffffff ^ (*p | ((*p & 0x0000ff00) << 16));
+           p++;
+       }
+    }
+}
+
+/* Invert a mask
+ */
+static void
+_invert_argb32_mask (cairo_win32_surface_t *mask_surface)
+{
+    cairo_image_surface_t *image = (cairo_image_surface_t *)mask_surface->image;
+    int i, j;
+
+    for (i = 0; i < image->height; i++) {
+       uint32_t *p = (uint32_t *) (image->data + i * image->stride);
+       for (j = 0; j < image->width; j++) {
+           *p = 0xffffffff ^ *p;
+           p++;
+       }
+    }
+}
+
+/* Compute an alpha-mask from a monochrome RGB24 image
+ */
+static cairo_surface_t *
+_compute_a8_mask (cairo_win32_surface_t *mask_surface)
+{
+    cairo_image_surface_t *image24 = (cairo_image_surface_t *)mask_surface->image;
+    cairo_image_surface_t *image8;
+    int i, j;
+
+    if (image24->base.status)
+       return cairo_surface_reference (&image24->base);
+
+    image8 = (cairo_image_surface_t *)cairo_image_surface_create (CAIRO_FORMAT_A8,
+                                                                 image24->width, image24->height);
+    if (image8->base.status)
+       return &image8->base;
+
+    for (i = 0; i < image24->height; i++) {
+       uint32_t *p = (uint32_t *) (image24->data + i * image24->stride);
+       unsigned char *q = (unsigned char *) (image8->data + i * image8->stride);
+
+       for (j = 0; j < image24->width; j++) {
+           *q = 255 - ((*p & 0x0000ff00) >> 8);
+           p++;
+           q++;
+       }
+    }
+
+    return &image8->base;
+}
+
+static cairo_int_status_t
+_cairo_win32_scaled_font_glyph_init (void                     *abstract_font,
+                                    cairo_scaled_glyph_t      *scaled_glyph,
+                                    cairo_scaled_glyph_info_t  info)
+{
+    cairo_win32_scaled_font_t *scaled_font = abstract_font;
+    cairo_status_t status;
+
+    if ((info & CAIRO_SCALED_GLYPH_INFO_METRICS) != 0) {
+       status = _cairo_win32_scaled_font_init_glyph_metrics (scaled_font, scaled_glyph);
+       if (status)
+           return status;
+    }
+
+    if (info & CAIRO_SCALED_GLYPH_INFO_SURFACE) {
+       status = _cairo_win32_scaled_font_init_glyph_surface (scaled_font, scaled_glyph);
+       if (status)
+           return status;
+    }
+
+    if ((info & CAIRO_SCALED_GLYPH_INFO_PATH) != 0) {
+       status = _cairo_win32_scaled_font_init_glyph_path (scaled_font, scaled_glyph);
+       if (status)
+           return status;
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+#if 0
+static cairo_int_status_t
+_cairo_win32_scaled_font_show_glyphs (void                     *abstract_font,
+                                     cairo_operator_t           op,
+                                     const cairo_pattern_t     *pattern,
+                                     cairo_surface_t           *generic_surface,
+                                     int                        source_x,
+                                     int                        source_y,
+                                     int                        dest_x,
+                                     int                        dest_y,
+                                     unsigned int               width,
+                                     unsigned int               height,
+                                     cairo_glyph_t             *glyphs,
+                                     int                        num_glyphs,
+                                     cairo_region_t            *clip_region,
+                                     int                       *remaining_glyphs)
+{
+    cairo_win32_scaled_font_t *scaled_font = abstract_font;
+    cairo_win32_surface_t *surface = (cairo_win32_surface_t *)generic_surface;
+    cairo_status_t status;
+
+    if (width == 0 || height == 0)
+       return CAIRO_STATUS_SUCCESS;
+
+    if (_cairo_surface_is_win32 (generic_surface) &&
+       surface->format == CAIRO_FORMAT_RGB24 &&
+       op == CAIRO_OPERATOR_OVER &&
+       _cairo_pattern_is_opaque_solid (pattern)) {
+
+       cairo_solid_pattern_t *solid_pattern = (cairo_solid_pattern_t *)pattern;
+
+       /* When compositing OVER on a GDI-understood surface, with a
+        * solid opaque color, we can just call ExtTextOut directly.
+        */
+       COLORREF new_color;
+
+       status = _cairo_win32_surface_set_clip_region (surface, clip_region);
+       if (unlikely (status))
+           return status;
+
+       new_color = RGB (((int)solid_pattern->color.red_short) >> 8,
+                        ((int)solid_pattern->color.green_short) >> 8,
+                        ((int)solid_pattern->color.blue_short) >> 8);
+
+       return _draw_glyphs_on_surface (surface, scaled_font, new_color,
+                                       0, 0,
+                                       glyphs, num_glyphs);
+    } else {
+       /* Otherwise, we need to draw using software fallbacks. We create a mask
+        * surface by drawing the the glyphs onto a DIB, black-on-white then
+        * inverting. GDI outputs gamma-corrected images so inverted black-on-white
+        * is very different from white-on-black. We favor the more common
+        * case where the final output is dark-on-light.
+        */
+       cairo_win32_surface_t *tmp_surface;
+       cairo_surface_t *mask_surface;
+       cairo_surface_pattern_t mask;
+       RECT r;
+
+       tmp_surface = (cairo_win32_surface_t *)cairo_win32_surface_create_with_dib (CAIRO_FORMAT_ARGB32, width, height);
+       if (tmp_surface->base.status)
+           return tmp_surface->base.status;
+
+       r.left = 0;
+       r.top = 0;
+       r.right = width;
+       r.bottom = height;
+       FillRect (tmp_surface->dc, &r, GetStockObject (WHITE_BRUSH));
+
+       status = _draw_glyphs_on_surface (tmp_surface,
+                                         scaled_font, RGB (0, 0, 0),
+                                         dest_x, dest_y,
+                                         glyphs, num_glyphs);
+       if (status) {
+           cairo_surface_destroy (&tmp_surface->base);
+           return status;
+       }
+
+       if (scaled_font->quality == CLEARTYPE_QUALITY) {
+           /* For ClearType, we need a 4-channel mask. If we are compositing on
+            * a surface with alpha, we need to compute the alpha channel of
+            * the mask (we just copy the green channel). But for a destination
+            * surface without alpha the alpha channel of the mask is ignored
+            */
+
+           if (surface->format != CAIRO_FORMAT_RGB24)
+               _compute_argb32_mask_alpha (tmp_surface);
+           else
+               _invert_argb32_mask (tmp_surface);
+
+           mask_surface = &tmp_surface->base;
+       } else {
+           mask_surface = _compute_a8_mask (tmp_surface);
+           cairo_surface_destroy (&tmp_surface->base);
+           status = mask_surface->status;
+           if (status)
+               return status;
+       }
+
+       /* For op == OVER, no-cleartype, a possible optimization here is to
+        * draw onto an intermediate ARGB32 surface and alpha-blend that with the
+        * destination
+        */
+       _cairo_pattern_init_for_surface (&mask, mask_surface);
+       cairo_surface_destroy (mask_surface);
+
+       if (scaled_font->quality == CLEARTYPE_QUALITY)
+           mask.base.has_component_alpha = TRUE;
+
+       status = _cairo_surface_mask (&surface->base, op, pattern, &mask.base,
+                                          clip_region);
+
+       _cairo_pattern_fini (&mask.base);
+
+       return status;
+    }
+}
+#endif
+
+static cairo_int_status_t
+_cairo_win32_scaled_font_load_truetype_table (void            *abstract_font,
+                                             unsigned long      tag,
+                                             long               offset,
+                                             unsigned char     *buffer,
+                                             unsigned long     *length)
+{
+    cairo_win32_scaled_font_t *scaled_font = abstract_font;
+    HDC hdc;
+    cairo_status_t status;
+    DWORD ret;
+
+    hdc = _get_global_font_dc ();
+    assert (hdc != NULL);
+
+    tag = (tag&0x000000ff)<<24 | (tag&0x0000ff00)<<8 | (tag&0x00ff0000)>>8 | (tag&0xff000000)>>24;
+    status = cairo_win32_scaled_font_select_font (&scaled_font->base, hdc);
+    if (status)
+       return status;
+
+    ret = GetFontData (hdc, tag, offset, buffer, *length);
+    if (ret == GDI_ERROR || (buffer && ret != *length))
+        status = CAIRO_INT_STATUS_UNSUPPORTED;
+    else
+       *length = ret;
+
+    cairo_win32_scaled_font_done_font (&scaled_font->base);
+
+    return status;
+}
+
+static cairo_int_status_t
+_cairo_win32_scaled_font_index_to_ucs4 (void           *abstract_font,
+                                       unsigned long    index,
+                                       uint32_t        *ucs4)
+{
+    cairo_win32_scaled_font_t *scaled_font = abstract_font;
+    GLYPHSET *glyph_set;
+    uint16_t *utf16 = NULL;
+    WORD *glyph_indices = NULL;
+    HDC hdc = NULL;
+    int res;
+    unsigned int i, j, num_glyphs;
+    cairo_status_t status;
+
+    hdc = _get_global_font_dc ();
+    assert (hdc != NULL);
+
+    status = cairo_win32_scaled_font_select_font (&scaled_font->base, hdc);
+    if (status)
+       return status;
+
+    res = GetFontUnicodeRanges(hdc, NULL);
+    if (res == 0) {
+       status = _cairo_win32_print_gdi_error (
+           "_cairo_win32_scaled_font_index_to_ucs4:GetFontUnicodeRanges");
+       goto exit1;
+    }
+
+    glyph_set = malloc (res);
+    if (glyph_set == NULL) {
+       status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+       goto exit1;
+    }
+
+    res = GetFontUnicodeRanges(hdc, glyph_set);
+    if (res == 0) {
+       status = _cairo_win32_print_gdi_error (
+           "_cairo_win32_scaled_font_index_to_ucs4:GetFontUnicodeRanges");
+       goto exit1;
+    }
+
+    *ucs4 = (uint32_t) -1;
+    for (i = 0; i < glyph_set->cRanges; i++) {
+       num_glyphs = glyph_set->ranges[i].cGlyphs;
+
+       utf16 = _cairo_malloc_ab (num_glyphs + 1, sizeof (uint16_t));
+       if (utf16 == NULL) {
+           status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+           goto exit1;
+       }
+
+       glyph_indices = _cairo_malloc_ab (num_glyphs + 1, sizeof (WORD));
+       if (glyph_indices == NULL) {
+           status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+           goto exit2;
+       }
+
+       for (j = 0; j < num_glyphs; j++)
+           utf16[j] = glyph_set->ranges[i].wcLow + j;
+       utf16[j] = 0;
+
+       if (GetGlyphIndicesW (hdc, utf16, num_glyphs, glyph_indices, 0) == GDI_ERROR) {
+           status = _cairo_win32_print_gdi_error (
+               "_cairo_win32_scaled_font_index_to_ucs4:GetGlyphIndicesW");
+           goto exit2;
+       }
+
+       for (j = 0; j < num_glyphs; j++) {
+           if (glyph_indices[j] == index) {
+               *ucs4 = utf16[j];
+               goto exit2;
+           }
+       }
+
+       free (glyph_indices);
+       glyph_indices = NULL;
+       free (utf16);
+       utf16 = NULL;
+    }
+
+exit2:
+    free (glyph_indices);
+    free (utf16);
+    free (glyph_set);
+exit1:
+    cairo_win32_scaled_font_done_font (&scaled_font->base);
+
+    return status;
+}
+
+static cairo_bool_t
+_cairo_win32_scaled_font_is_synthetic (void           *abstract_font)
+{
+    cairo_win32_scaled_font_t *scaled_font = abstract_font;
+    cairo_status_t status;
+    int        weight;
+    cairo_bool_t bold;
+    cairo_bool_t italic;
+
+    status = _cairo_truetype_get_style (&scaled_font->base,
+                                       &weight,
+                                       &bold,
+                                       &italic);
+    /* If this doesn't work assume it is not synthetic to avoid
+     * unneccessary subsetting fallbacks. */
+    if (status != CAIRO_STATUS_SUCCESS)
+       return FALSE;
+
+    if (scaled_font->logfont.lfWeight != weight ||
+       scaled_font->logfont.lfItalic != italic)
+       return TRUE;
+
+    return FALSE;
+}
+
+static cairo_int_status_t
+_cairo_win32_scaled_font_index_to_glyph_name (void             *abstract_font,
+                                             char             **glyph_names,
+                                             int                num_glyph_names,
+                                             unsigned long      glyph_index,
+                                             unsigned long     *glyph_array_index)
+{
+    cairo_win32_scaled_font_t *scaled_font = abstract_font;
+    int i;
+
+    /* Windows puts .notdef at index 0 then numbers the remaining
+     * glyphs starting from 1 in the order they appear in the font. */
+
+    /* Find the position of .notdef in the list of glyph names. We
+     * only need to do this once per scaled font. */
+    if (! scaled_font->has_type1_notdef_index) {
+       for (i = 0; i < num_glyph_names; i++) {
+           if (strcmp (glyph_names[i], ".notdef") == 0) {
+               scaled_font->type1_notdef_index = i;
+               scaled_font->has_type1_notdef_index = TRUE;
+               break;
+           }
+       }
+       if (! scaled_font->has_type1_notdef_index)
+           return CAIRO_INT_STATUS_UNSUPPORTED;
+    }
+
+    /* Once we know the position of .notdef the position of any glyph
+     * in the font can easily be obtained. */
+    if (glyph_index == 0)
+       *glyph_array_index = scaled_font->type1_notdef_index;
+    else if (glyph_index <= scaled_font->type1_notdef_index)
+       *glyph_array_index = glyph_index - 1;
+    else if (glyph_index < num_glyph_names)
+       *glyph_array_index = glyph_index;
+    else
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_win32_scaled_font_load_type1_data (void             *abstract_font,
+                                         long               offset,
+                                         unsigned char     *buffer,
+                                         unsigned long     *length)
+{
+    cairo_win32_scaled_font_t *scaled_font = abstract_font;
+
+    if (! scaled_font->is_type1)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    /* Using the tag 0 retrieves the entire font file. This works with
+     * Type 1 fonts as well as TTF/OTF fonts. */
+    return _cairo_win32_scaled_font_load_truetype_table (scaled_font,
+                                                        0,
+                                                        offset,
+                                                        buffer,
+                                                        length);
+}
+
+static cairo_status_t
+_cairo_win32_scaled_font_init_glyph_surface (cairo_win32_scaled_font_t *scaled_font,
+                                             cairo_scaled_glyph_t      *scaled_glyph)
+{
+    cairo_status_t status;
+    cairo_glyph_t glyph;
+    cairo_win32_surface_t *surface;
+    cairo_t *cr;
+    cairo_surface_t *image;
+    int width, height;
+    int x1, y1, x2, y2;
+
+    x1 = _cairo_fixed_integer_floor (scaled_glyph->bbox.p1.x);
+    y1 = _cairo_fixed_integer_floor (scaled_glyph->bbox.p1.y);
+    x2 = _cairo_fixed_integer_ceil (scaled_glyph->bbox.p2.x);
+    y2 = _cairo_fixed_integer_ceil (scaled_glyph->bbox.p2.y);
+    width = x2 - x1;
+    height = y2 - y1;
+
+    surface = (cairo_win32_surface_t *)
+       cairo_win32_surface_create_with_dib (CAIRO_FORMAT_RGB24, width, height);
+
+    cr = cairo_create (&surface->base);
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_paint (cr);
+    status = cairo_status (cr);
+    cairo_destroy(cr);
+    if (status)
+       goto FAIL;
+
+    glyph.index = _cairo_scaled_glyph_index (scaled_glyph);
+    glyph.x = -x1;
+    glyph.y = -y1;
+    status = _draw_glyphs_on_surface (surface, scaled_font, RGB(0,0,0),
+                                      0, 0, &glyph, 1);
+    if (status)
+       goto FAIL;
+
+    GdiFlush();
+
+    image = _compute_a8_mask (surface);
+    status = image->status;
+    if (status)
+       goto FAIL;
+
+    cairo_surface_set_device_offset (image, -x1, -y1);
+    _cairo_scaled_glyph_set_surface (scaled_glyph,
+                                     &scaled_font->base,
+                                     (cairo_image_surface_t *) image);
+
+  FAIL:
+    cairo_surface_destroy (&surface->base);
+
+    return status;
+}
+
+static void
+_cairo_win32_transform_FIXED_to_fixed (cairo_matrix_t *matrix,
+                                       FIXED Fx, FIXED Fy,
+                                       cairo_fixed_t *fx, cairo_fixed_t *fy)
+{
+    double x = Fx.value + Fx.fract / 65536.0;
+    double y = Fy.value + Fy.fract / 65536.0;
+    cairo_matrix_transform_point (matrix, &x, &y);
+    *fx = _cairo_fixed_from_double (x);
+    *fy = _cairo_fixed_from_double (y);
+}
+
+static cairo_status_t
+_cairo_win32_scaled_font_init_glyph_path (cairo_win32_scaled_font_t *scaled_font,
+                                         cairo_scaled_glyph_t      *scaled_glyph)
+{
+    static const MAT2 matrix = { { 0, 1 }, { 0, 0 }, { 0, 0 }, { 0, -1 } };
+    cairo_status_t status;
+    GLYPHMETRICS metrics;
+    HDC hdc;
+    DWORD bytesGlyph;
+    unsigned char *buffer, *ptr;
+    cairo_path_fixed_t *path;
+    cairo_matrix_t transform;
+    cairo_fixed_t x, y;
+
+    if (scaled_font->is_bitmap)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    hdc = _get_global_font_dc ();
+    assert (hdc != NULL);
+
+    path = _cairo_path_fixed_create ();
+    if (!path)
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    if (scaled_font->base.options.hint_style == CAIRO_HINT_STYLE_NONE) {
+        status = _cairo_win32_scaled_font_select_unscaled_font (&scaled_font->base, hdc);
+        transform = scaled_font->base.scale;
+        cairo_matrix_scale (&transform, 1.0/scaled_font->em_square, 1.0/scaled_font->em_square);
+    } else {
+        status = cairo_win32_scaled_font_select_font (&scaled_font->base, hdc);
+        cairo_matrix_init_identity(&transform);
+    }
+    if (status)
+        goto CLEANUP_PATH;
+
+    bytesGlyph = GetGlyphOutlineW (hdc, _cairo_scaled_glyph_index (scaled_glyph),
+                                  GGO_NATIVE | GGO_GLYPH_INDEX,
+                                  &metrics, 0, NULL, &matrix);
+
+    if (bytesGlyph == GDI_ERROR) {
+       status = _cairo_win32_print_gdi_error ("_cairo_win32_scaled_font_glyph_path");
+       goto CLEANUP_FONT;
+    }
+
+    ptr = buffer = malloc (bytesGlyph);
+    if (!buffer) {
+       status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+       goto CLEANUP_FONT;
+    }
+
+    if (GetGlyphOutlineW (hdc, _cairo_scaled_glyph_index (scaled_glyph),
+                         GGO_NATIVE | GGO_GLYPH_INDEX,
+                         &metrics, bytesGlyph, buffer, &matrix) == GDI_ERROR) {
+       status = _cairo_win32_print_gdi_error ("_cairo_win32_scaled_font_glyph_path");
+       goto CLEANUP_BUFFER;
+    }
+
+    while (ptr < buffer + bytesGlyph) {
+       TTPOLYGONHEADER *header = (TTPOLYGONHEADER *)ptr;
+       unsigned char *endPoly = ptr + header->cb;
+
+       ptr += sizeof (TTPOLYGONHEADER);
+
+        _cairo_win32_transform_FIXED_to_fixed (&transform,
+                                               header->pfxStart.x,
+                                               header->pfxStart.y,
+                                               &x, &y);
+        status = _cairo_path_fixed_move_to (path, x, y);
+       if (status)
+           goto CLEANUP_BUFFER;
+
+       while (ptr < endPoly) {
+           TTPOLYCURVE *curve = (TTPOLYCURVE *)ptr;
+           POINTFX *points = curve->apfx;
+           int i;
+           switch (curve->wType) {
+           case TT_PRIM_LINE:
+               for (i = 0; i < curve->cpfx; i++) {
+                    _cairo_win32_transform_FIXED_to_fixed (&transform,
+                                                           points[i].x,
+                                                           points[i].y,
+                                                           &x, &y);
+                   status = _cairo_path_fixed_line_to (path, x, y);
+                   if (status)
+                       goto CLEANUP_BUFFER;
+               }
+               break;
+           case TT_PRIM_QSPLINE:
+               for (i = 0; i < curve->cpfx - 1; i++) {
+                   cairo_fixed_t p1x, p1y, p2x, p2y, cx, cy, c1x, c1y, c2x, c2y;
+                   if (! _cairo_path_fixed_get_current_point (path, &p1x, &p1y))
+                       goto CLEANUP_BUFFER;
+                    _cairo_win32_transform_FIXED_to_fixed (&transform,
+                                                           points[i].x,
+                                                           points[i].y,
+                                                           &cx, &cy);
+
+                   if (i + 1 == curve->cpfx - 1) {
+                        _cairo_win32_transform_FIXED_to_fixed (&transform,
+                                                               points[i + 1].x,
+                                                               points[i + 1].y,
+                                                               &p2x, &p2y);
+                   } else {
+                       /* records with more than one curve use interpolation for
+                          control points, per http://support.microsoft.com/kb/q87115/ */
+                        _cairo_win32_transform_FIXED_to_fixed (&transform,
+                                                               points[i + 1].x,
+                                                               points[i + 1].y,
+                                                               &x, &y);
+                        p2x = (cx + x) / 2;
+                       p2y = (cy + y) / 2;
+                   }
+
+                   c1x = 2 * cx / 3 + p1x / 3;
+                   c1y = 2 * cy / 3 + p1y / 3;
+                   c2x = 2 * cx / 3 + p2x / 3;
+                   c2y = 2 * cy / 3 + p2y / 3;
+
+                   status = _cairo_path_fixed_curve_to (path, c1x, c1y, c2x, c2y, p2x, p2y);
+                   if (status)
+                       goto CLEANUP_BUFFER;
+               }
+               break;
+           case TT_PRIM_CSPLINE:
+               for (i = 0; i < curve->cpfx - 2; i += 2) {
+                   cairo_fixed_t x1, y1, x2, y2;
+                    _cairo_win32_transform_FIXED_to_fixed (&transform,
+                                                           points[i].x,
+                                                           points[i].y,
+                                                           &x, &y);
+                    _cairo_win32_transform_FIXED_to_fixed (&transform,
+                                                           points[i + 1].x,
+                                                           points[i + 1].y,
+                                                           &x1, &y1);
+                    _cairo_win32_transform_FIXED_to_fixed (&transform,
+                                                           points[i + 2].x,
+                                                           points[i + 2].y,
+                                                           &x2, &y2);
+                   status = _cairo_path_fixed_curve_to (path, x, y, x1, y1, x2, y2);
+                   if (status)
+                       goto CLEANUP_BUFFER;
+               }
+               break;
+           }
+           ptr += sizeof(TTPOLYCURVE) + sizeof (POINTFX) * (curve->cpfx - 1);
+       }
+       status = _cairo_path_fixed_close_path (path);
+       if (status)
+           goto CLEANUP_BUFFER;
+    }
+
+    _cairo_scaled_glyph_set_path (scaled_glyph,
+                                 &scaled_font->base,
+                                 path);
+
+ CLEANUP_BUFFER:
+    free (buffer);
+
+ CLEANUP_FONT:
+    if (scaled_font->base.options.hint_style == CAIRO_HINT_STYLE_NONE)
+       _cairo_win32_scaled_font_done_unscaled_font (&scaled_font->base);
+    else
+       cairo_win32_scaled_font_done_font (&scaled_font->base);
+
+ CLEANUP_PATH:
+    if (status != CAIRO_STATUS_SUCCESS)
+       _cairo_path_fixed_destroy (path);
+
+    return status;
+}
+
+const cairo_scaled_font_backend_t _cairo_win32_scaled_font_backend = {
+    CAIRO_FONT_TYPE_WIN32,
+    _cairo_win32_scaled_font_fini,
+    _cairo_win32_scaled_font_glyph_init,
+    NULL, /* _cairo_win32_scaled_font_text_to_glyphs, FIXME */
+    _cairo_win32_scaled_font_ucs4_to_index,
+    _cairo_win32_scaled_font_load_truetype_table,
+    _cairo_win32_scaled_font_index_to_ucs4,
+    _cairo_win32_scaled_font_is_synthetic,
+    _cairo_win32_scaled_font_index_to_glyph_name,
+    _cairo_win32_scaled_font_load_type1_data
+};
+
+/* #cairo_win32_font_face_t */
+
+typedef struct _cairo_win32_font_face cairo_win32_font_face_t;
+
+/* If hfont is non-%NULL then logfont->lfHeight must be -S for some S,
+ * logfont->lfWidth, logfont->lfEscapement, logfont->lfOrientation must
+ * all be 0, and hfont is the result of calling CreateFontIndirectW on
+ * logfont.
+ */
+struct _cairo_win32_font_face {
+    cairo_font_face_t base;
+    LOGFONTW logfont;
+    HFONT hfont;
+};
+
+/* implement the platform-specific interface */
+
+static cairo_bool_t
+_is_scale (const cairo_matrix_t *matrix, double scale)
+{
+    return matrix->xx == scale && matrix->yy == scale &&
+           matrix->xy == 0. && matrix->yx == 0. &&
+           matrix->x0 == 0. && matrix->y0 == 0.;
+}
+
+static cairo_status_t
+_cairo_win32_font_face_scaled_font_create (void                        *abstract_face,
+                                          const cairo_matrix_t *font_matrix,
+                                          const cairo_matrix_t *ctm,
+                                          const cairo_font_options_t *options,
+                                          cairo_scaled_font_t **font)
+{
+    HFONT hfont = NULL;
+
+    cairo_win32_font_face_t *font_face = abstract_face;
+
+    if (font_face->hfont) {
+        /* Check whether it's OK to go ahead and use the font-face's HFONT. */
+        if (_is_scale (ctm, 1.) &&
+            _is_scale (font_matrix, -font_face->logfont.lfHeight)) {
+            hfont = font_face->hfont;
+        }
+    }
+
+    return _win32_scaled_font_create (&font_face->logfont,
+                                     hfont,
+                                     &font_face->base,
+                                     font_matrix, ctm, options,
+                                     font);
+}
+
+const cairo_font_face_backend_t _cairo_win32_font_face_backend = {
+    CAIRO_FONT_TYPE_WIN32,
+    _cairo_win32_font_face_create_for_toy,
+    _cairo_win32_font_face_destroy,
+    _cairo_win32_font_face_scaled_font_create
+};
+
+/* We maintain a hash table from LOGFONT,HFONT => #cairo_font_face_t.
+ * The primary purpose of this mapping is to provide unique
+ * #cairo_font_face_t values so that our cache and mapping from
+ * #cairo_font_face_t => #cairo_scaled_font_t works. Once the
+ * corresponding #cairo_font_face_t objects fall out of downstream
+ * caches, we don't need them in this hash table anymore.
+ *
+ * Modifications to this hash table are protected by
+ * _cairo_win32_font_face_mutex.
+ */
+
+static cairo_hash_table_t *cairo_win32_font_face_hash_table = NULL;
+
+static int
+_cairo_win32_font_face_keys_equal (const void *key_a,
+                                  const void *key_b);
+
+static void
+_cairo_win32_font_face_hash_table_destroy (void)
+{
+    cairo_hash_table_t *hash_table;
+
+    /* We manually acquire the lock rather than calling
+     * _cairo_win32_font_face_hash_table_lock simply to avoid creating
+     * the table only to destroy it again. */
+    CAIRO_MUTEX_LOCK (_cairo_win32_font_face_mutex);
+    hash_table = cairo_win32_font_face_hash_table;
+    cairo_win32_font_face_hash_table = NULL;
+    CAIRO_MUTEX_UNLOCK (_cairo_win32_font_face_mutex);
+
+    if (hash_table != NULL)
+       _cairo_hash_table_destroy (hash_table);
+}
+
+static cairo_hash_table_t *
+_cairo_win32_font_face_hash_table_lock (void)
+{
+    CAIRO_MUTEX_LOCK (_cairo_win32_font_face_mutex);
+
+    if (unlikely (cairo_win32_font_face_hash_table == NULL))
+    {
+       cairo_win32_font_face_hash_table =
+       _cairo_hash_table_create (_cairo_win32_font_face_keys_equal);
+
+       if (unlikely (cairo_win32_font_face_hash_table == NULL)) {
+           CAIRO_MUTEX_UNLOCK (_cairo_win32_font_face_mutex);
+           _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+           return NULL;
+       }
+    }
+
+    return cairo_win32_font_face_hash_table;
+}
+
+static void
+_cairo_win32_font_face_hash_table_unlock (void)
+{
+    CAIRO_MUTEX_UNLOCK (_cairo_win32_font_face_mutex);
+}
+
+static void
+_cairo_win32_font_face_destroy (void *abstract_face)
+{
+    cairo_win32_font_face_t *font_face = abstract_face;
+    cairo_hash_table_t *hash_table;
+
+    hash_table = _cairo_win32_font_face_hash_table_lock ();
+    /* All created objects must have been mapped in the hash table. */
+    assert (hash_table != NULL);
+
+    if (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&font_face->base.ref_count)) {
+       /* somebody recreated the font whilst we waited for the lock */
+       _cairo_win32_font_face_hash_table_unlock ();
+       return;
+    }
+
+    /* Font faces in SUCCESS status are guaranteed to be in the
+     * hashtable. Font faces in an error status are removed from the
+     * hashtable if they are found during a lookup, thus they should
+     * only be removed if they are in the hashtable. */
+    if (likely (font_face->base.status == CAIRO_STATUS_SUCCESS) ||
+       _cairo_hash_table_lookup (hash_table, &font_face->base.hash_entry) == font_face)
+       _cairo_hash_table_remove (hash_table, &font_face->base.hash_entry);
+
+    _cairo_win32_font_face_hash_table_unlock ();
+}
+
+static void
+_cairo_win32_font_face_init_key (cairo_win32_font_face_t *key,
+                                LOGFONTW                *logfont,
+                                HFONT                    font)
+{
+    unsigned long hash = _CAIRO_HASH_INIT_VALUE;
+
+    key->logfont = *logfont;
+    key->hfont = font;
+
+    hash = _cairo_hash_bytes (0, logfont->lfFaceName, 2*wcslen(logfont->lfFaceName));
+    hash = _cairo_hash_bytes (hash, &logfont->lfWeight, sizeof(logfont->lfWeight));
+    hash = _cairo_hash_bytes (hash, &logfont->lfItalic, sizeof(logfont->lfItalic));
+
+    key->base.hash_entry.hash = hash;
+}
+
+static int
+_cairo_win32_font_face_keys_equal (const void *key_a,
+                                  const void *key_b)
+{
+    const cairo_win32_font_face_t *face_a = key_a;
+    const cairo_win32_font_face_t *face_b = key_b;
+
+    if (face_a->logfont.lfWeight         == face_b->logfont.lfWeight &&
+       face_a->logfont.lfItalic         == face_b->logfont.lfItalic &&
+       face_a->logfont.lfUnderline      == face_b->logfont.lfUnderline &&
+       face_a->logfont.lfStrikeOut      == face_b->logfont.lfStrikeOut &&
+       face_a->logfont.lfCharSet        == face_b->logfont.lfCharSet &&
+       face_a->logfont.lfOutPrecision   == face_b->logfont.lfOutPrecision &&
+       face_a->logfont.lfClipPrecision  == face_b->logfont.lfClipPrecision &&
+       face_a->logfont.lfPitchAndFamily == face_b->logfont.lfPitchAndFamily &&
+       (wcscmp (face_a->logfont.lfFaceName, face_b->logfont.lfFaceName) == 0))
+       return TRUE;
+    else
+       return FALSE;
+}
+
+/**
+ * cairo_win32_font_face_create_for_logfontw_hfont:
+ * @logfont: A #LOGFONTW structure specifying the font to use.
+ *   If @font is %NULL then the lfHeight, lfWidth, lfOrientation and lfEscapement
+ *   fields of this structure are ignored. Otherwise lfWidth, lfOrientation and
+ *   lfEscapement must be zero.
+ * @font: An #HFONT that can be used when the font matrix is a scale by
+ *   -lfHeight and the CTM is identity.
+ *
+ * Creates a new font for the Win32 font backend based on a
+ * #LOGFONT. This font can then be used with
+ * cairo_set_font_face() or cairo_scaled_font_create().
+ * The #cairo_scaled_font_t
+ * returned from cairo_scaled_font_create() is also for the Win32 backend
+ * and can be used with functions such as cairo_win32_scaled_font_select_font().
+ *
+ * Return value: a newly created #cairo_font_face_t. Free with
+ *  cairo_font_face_destroy() when you are done using it.
+ **/
+cairo_font_face_t *
+cairo_win32_font_face_create_for_logfontw_hfont (LOGFONTW *logfont, HFONT font)
+{
+    cairo_win32_font_face_t *font_face, key;
+    cairo_hash_table_t *hash_table;
+    cairo_status_t status;
+
+    hash_table = _cairo_win32_font_face_hash_table_lock ();
+    if (unlikely (hash_table == NULL)) {
+        _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+       return (cairo_font_face_t *)&_cairo_font_face_nil;
+    }
+
+    _cairo_win32_font_face_init_key (&key, logfont, font);
+
+    /* Return existing unscaled font if it exists in the hash table. */
+    font_face = _cairo_hash_table_lookup (hash_table,
+                                        &key.base.hash_entry);
+    if (font_face != NULL) {
+       if (font_face->base.status == CAIRO_STATUS_SUCCESS) {
+           cairo_font_face_reference (&font_face->base);
+           _cairo_win32_font_face_hash_table_unlock ();
+           return &font_face->base;
+       }
+
+       /* remove the bad font from the hash table */
+       _cairo_hash_table_remove (hash_table, &font_face->base.hash_entry);
+    }
+
+    /* Otherwise create it and insert into hash table. */
+    font_face = malloc (sizeof (cairo_win32_font_face_t));
+    if (!font_face) {
+        _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+       goto FAIL;
+    }
+
+    _cairo_win32_font_face_init_key (font_face, logfont, font);
+    _cairo_font_face_init (&font_face->base, &_cairo_win32_font_face_backend);
+
+    assert (font_face->base.hash_entry.hash == key.base.hash_entry.hash);
+    status = _cairo_hash_table_insert (hash_table,
+                                      &font_face->base.hash_entry);
+    if (unlikely (status))
+       goto FAIL;
+
+    _cairo_win32_font_face_hash_table_unlock ();
+    return &font_face->base;
+
+FAIL:
+    _cairo_win32_font_face_hash_table_unlock ();
+    return (cairo_font_face_t *)&_cairo_font_face_nil;
+}
+
+/**
+ * cairo_win32_font_face_create_for_logfontw:
+ * @logfont: A #LOGFONTW structure specifying the font to use.
+ *   The lfHeight, lfWidth, lfOrientation and lfEscapement
+ *   fields of this structure are ignored.
+ *
+ * Creates a new font for the Win32 font backend based on a
+ * #LOGFONT. This font can then be used with
+ * cairo_set_font_face() or cairo_scaled_font_create().
+ * The #cairo_scaled_font_t
+ * returned from cairo_scaled_font_create() is also for the Win32 backend
+ * and can be used with functions such as cairo_win32_scaled_font_select_font().
+ *
+ * Return value: a newly created #cairo_font_face_t. Free with
+ *  cairo_font_face_destroy() when you are done using it.
+ **/
+cairo_font_face_t *
+cairo_win32_font_face_create_for_logfontw (LOGFONTW *logfont)
+{
+    return cairo_win32_font_face_create_for_logfontw_hfont (logfont, NULL);
+}
+
+/**
+ * cairo_win32_font_face_create_for_hfont:
+ * @font: An #HFONT structure specifying the font to use.
+ *
+ * Creates a new font for the Win32 font backend based on a
+ * #HFONT. This font can then be used with
+ * cairo_set_font_face() or cairo_scaled_font_create().
+ * The #cairo_scaled_font_t
+ * returned from cairo_scaled_font_create() is also for the Win32 backend
+ * and can be used with functions such as cairo_win32_scaled_font_select_font().
+ *
+ * Return value: a newly created #cairo_font_face_t. Free with
+ *  cairo_font_face_destroy() when you are done using it.
+ **/
+cairo_font_face_t *
+cairo_win32_font_face_create_for_hfont (HFONT font)
+{
+    LOGFONTW logfont;
+    GetObjectW (font, sizeof(logfont), &logfont);
+
+    if (logfont.lfEscapement != 0 || logfont.lfOrientation != 0 ||
+        logfont.lfWidth != 0) {
+        /* We can't use this font because that optimization requires that
+         * lfEscapement, lfOrientation and lfWidth be zero. */
+        font = NULL;
+    }
+
+    return cairo_win32_font_face_create_for_logfontw_hfont (&logfont, font);
+}
+
+static cairo_bool_t
+_cairo_scaled_font_is_win32 (cairo_scaled_font_t *scaled_font)
+{
+    return scaled_font->backend == &_cairo_win32_scaled_font_backend;
+}
+
+/**
+ * cairo_win32_scaled_font_select_font:
+ * @scaled_font: A #cairo_scaled_font_t from the Win32 font backend. Such an
+ *   object can be created with cairo_win32_scaled_font_create_for_logfontw().
+ * @hdc: a device context
+ *
+ * Selects the font into the given device context and changes the
+ * map mode and world transformation of the device context to match
+ * that of the font. This function is intended for use when using
+ * layout APIs such as Uniscribe to do text layout with the
+ * cairo font. After finishing using the device context, you must call
+ * cairo_win32_scaled_font_done_font() to release any resources allocated
+ * by this function.
+ *
+ * See cairo_win32_scaled_font_get_metrics_factor() for converting logical
+ * coordinates from the device context to font space.
+ *
+ * Normally, calls to SaveDC() and RestoreDC() would be made around
+ * the use of this function to preserve the original graphics state.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS if the operation succeeded.
+ *   otherwise an error such as %CAIRO_STATUS_NO_MEMORY and
+ *   the device context is unchanged.
+ **/
+cairo_status_t
+cairo_win32_scaled_font_select_font (cairo_scaled_font_t *scaled_font,
+                                    HDC                  hdc)
+{
+    cairo_status_t status;
+    HFONT hfont;
+    HFONT old_hfont = NULL;
+    int old_mode;
+
+    if (! _cairo_scaled_font_is_win32 (scaled_font)) {
+       return _cairo_error (CAIRO_STATUS_FONT_TYPE_MISMATCH);
+    }
+
+    if (scaled_font->status)
+       return scaled_font->status;
+
+    status = _win32_scaled_font_get_scaled_hfont ((cairo_win32_scaled_font_t *)scaled_font, &hfont);
+    if (status)
+       return status;
+
+    old_hfont = SelectObject (hdc, hfont);
+    if (!old_hfont)
+       return _cairo_win32_print_gdi_error ("cairo_win32_scaled_font_select_font:SelectObject");
+
+    old_mode = SetGraphicsMode (hdc, GM_ADVANCED);
+    if (!old_mode) {
+       status = _cairo_win32_print_gdi_error ("cairo_win32_scaled_font_select_font:SetGraphicsMode");
+       SelectObject (hdc, old_hfont);
+       return status;
+    }
+
+    status = _win32_scaled_font_set_world_transform ((cairo_win32_scaled_font_t *)scaled_font, hdc);
+    if (status) {
+       SetGraphicsMode (hdc, old_mode);
+       SelectObject (hdc, old_hfont);
+       return status;
+    }
+
+    SetMapMode (hdc, MM_TEXT);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+/**
+ * cairo_win32_scaled_font_done_font:
+ * @scaled_font: A scaled font from the Win32 font backend.
+ *
+ * Releases any resources allocated by cairo_win32_scaled_font_select_font()
+ **/
+void
+cairo_win32_scaled_font_done_font (cairo_scaled_font_t *scaled_font)
+{
+    if (! _cairo_scaled_font_is_win32 (scaled_font)) {
+       _cairo_error_throw (CAIRO_STATUS_FONT_TYPE_MISMATCH);
+    }
+}
+
+/**
+ * cairo_win32_scaled_font_get_metrics_factor:
+ * @scaled_font: a scaled font from the Win32 font backend
+ *
+ * Gets a scale factor between logical coordinates in the coordinate
+ * space used by cairo_win32_scaled_font_select_font() (that is, the
+ * coordinate system used by the Windows functions to return metrics) and
+ * font space coordinates.
+ *
+ * Return value: factor to multiply logical units by to get font space
+ *               coordinates.
+ **/
+double
+cairo_win32_scaled_font_get_metrics_factor (cairo_scaled_font_t *scaled_font)
+{
+    if (! _cairo_scaled_font_is_win32 (scaled_font)) {
+       _cairo_error_throw (CAIRO_STATUS_FONT_TYPE_MISMATCH);
+       return 1.;
+    }
+    return 1. / ((cairo_win32_scaled_font_t *)scaled_font)->logical_scale;
+}
+
+/**
+ * cairo_win32_scaled_font_get_logical_to_device:
+ * @scaled_font: a scaled font from the Win32 font backend
+ * @logical_to_device: matrix to return
+ *
+ * Gets the transformation mapping the logical space used by @scaled_font
+ * to device space.
+ *
+ * Since: 1.4
+ **/
+void
+cairo_win32_scaled_font_get_logical_to_device (cairo_scaled_font_t *scaled_font,
+                                              cairo_matrix_t *logical_to_device)
+{
+    cairo_win32_scaled_font_t *win_font = (cairo_win32_scaled_font_t *)scaled_font;
+    if (! _cairo_scaled_font_is_win32 (scaled_font)) {
+       _cairo_error_throw (CAIRO_STATUS_FONT_TYPE_MISMATCH);
+       cairo_matrix_init_identity (logical_to_device);
+       return;
+    }
+    *logical_to_device = win_font->logical_to_device;
+}
+
+/**
+ * cairo_win32_scaled_font_get_device_to_logical:
+ * @scaled_font: a scaled font from the Win32 font backend
+ * @device_to_logical: matrix to return
+ *
+ * Gets the transformation mapping device space to the logical space
+ * used by @scaled_font.
+ *
+ * Since: 1.4
+ **/
+void
+cairo_win32_scaled_font_get_device_to_logical (cairo_scaled_font_t *scaled_font,
+                                              cairo_matrix_t *device_to_logical)
+{
+    cairo_win32_scaled_font_t *win_font = (cairo_win32_scaled_font_t *)scaled_font;
+    if (! _cairo_scaled_font_is_win32 (scaled_font)) {
+       _cairo_error_throw (CAIRO_STATUS_FONT_TYPE_MISMATCH);
+       cairo_matrix_init_identity (device_to_logical);
+       return;
+    }
+    *device_to_logical = win_font->device_to_logical;
+}
+
+void
+_cairo_win32_font_reset_static_data (void)
+{
+    _cairo_win32_font_face_hash_table_destroy ();
+}
diff --git a/src/cairo-win32-printing-surface.c b/src/cairo-win32-printing-surface.c
new file mode 100644 (file)
index 0000000..296e626
--- /dev/null
@@ -0,0 +1,1898 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* Cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2007, 2008 Adrian Johnson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Adrian Johnson.
+ *
+ * Contributor(s):
+ *      Adrian Johnson <ajohnson@redneon.com>
+ *      Vladimir Vukicevic <vladimir@pobox.com>
+ */
+
+#define WIN32_LEAN_AND_MEAN
+/* We require Windows 2000 features such as ETO_PDY */
+#if !defined(WINVER) || (WINVER < 0x0500)
+# define WINVER 0x0500
+#endif
+#if !defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x0500)
+# define _WIN32_WINNT 0x0500
+#endif
+
+#include "cairoint.h"
+
+#include "cairo-default-context-private.h"
+#include "cairo-error-private.h"
+#include "cairo-paginated-private.h"
+
+#include "cairo-clip-private.h"
+#include "cairo-win32-private.h"
+#include "cairo-recording-surface-private.h"
+#include "cairo-scaled-font-subsets-private.h"
+#include "cairo-image-info-private.h"
+#include "cairo-image-surface-private.h"
+#include "cairo-surface-backend-private.h"
+#include "cairo-surface-clipper-private.h"
+
+#include <windows.h>
+
+#if !defined(POSTSCRIPT_IDENTIFY)
+# define POSTSCRIPT_IDENTIFY 0x1015
+#endif
+
+#if !defined(PSIDENT_GDICENTRIC)
+# define PSIDENT_GDICENTRIC 0x0000
+#endif
+
+#if !defined(GET_PS_FEATURESETTING)
+# define GET_PS_FEATURESETTING 0x1019
+#endif
+
+#if !defined(FEATURESETTING_PSLEVEL)
+# define FEATURESETTING_PSLEVEL 0x0002
+#endif
+
+#if !defined(GRADIENT_FILL_RECT_H)
+# define GRADIENT_FILL_RECT_H 0x00
+#endif
+
+#if !defined(CHECKJPEGFORMAT)
+# define CHECKJPEGFORMAT 0x1017
+#endif
+
+#if !defined(CHECKPNGFORMAT)
+# define CHECKPNGFORMAT 0x1018
+#endif
+
+#define PELS_72DPI  ((LONG)(72. / 0.0254))
+
+static const cairo_surface_backend_t cairo_win32_printing_surface_backend;
+static const cairo_paginated_surface_backend_t cairo_win32_surface_paginated_backend;
+
+static void
+_cairo_win32_printing_surface_init_ps_mode (cairo_win32_surface_t *surface)
+{
+    DWORD word;
+    INT ps_feature, ps_level;
+
+    word = PSIDENT_GDICENTRIC;
+    if (ExtEscape (surface->dc, POSTSCRIPT_IDENTIFY, sizeof(DWORD), (char *)&word, 0, (char *)NULL) <= 0)
+       return;
+
+    ps_feature = FEATURESETTING_PSLEVEL;
+    if (ExtEscape (surface->dc, GET_PS_FEATURESETTING, sizeof(INT),
+                  (char *)&ps_feature, sizeof(INT), (char *)&ps_level) <= 0)
+       return;
+
+    if (ps_level >= 3)
+       surface->flags |= CAIRO_WIN32_SURFACE_CAN_RECT_GRADIENT;
+}
+
+static void
+_cairo_win32_printing_surface_init_image_support (cairo_win32_surface_t *surface)
+{
+    DWORD word;
+
+    word = CHECKJPEGFORMAT;
+    if (ExtEscape(surface->dc, QUERYESCSUPPORT, sizeof(word), (char *)&word, 0, (char *)NULL) > 0)
+       surface->flags |= CAIRO_WIN32_SURFACE_CAN_CHECK_JPEG;
+
+    word = CHECKPNGFORMAT;
+    if (ExtEscape(surface->dc, QUERYESCSUPPORT, sizeof(word), (char *)&word, 0, (char *)NULL) > 0)
+       surface->flags |= CAIRO_WIN32_SURFACE_CAN_CHECK_PNG;
+}
+
+/* When creating an EMF file, ExtTextOut with ETO_GLYPH_INDEX does not
+ * work unless the GDI function GdiInitializeLanguagePack() has been
+ * called.
+ *
+ *   http://m-a-tech.blogspot.com/2009/04/emf-buffer-idiocracy.html
+ *
+ * The only information I could find on the how to use this
+ * undocumented function is the use in:
+ *
+ * http://src.chromium.org/viewvc/chrome/trunk/src/chrome/renderer/render_process.cc?view=markup
+ *
+ * to solve the same problem. The above code first checks if LPK.DLL
+ * is already loaded. If it is not it calls
+ * GdiInitializeLanguagePack() using the prototype
+ *   BOOL GdiInitializeLanguagePack (int)
+ * and argument 0.
+ */
+static void
+_cairo_win32_printing_surface_init_language_pack (cairo_win32_surface_t *surface)
+{
+    typedef BOOL (WINAPI *gdi_init_lang_pack_func_t)(int);
+    gdi_init_lang_pack_func_t gdi_init_lang_pack;
+    HMODULE module;
+
+    if (GetModuleHandleW (L"LPK.DLL"))
+       return;
+
+    module = GetModuleHandleW (L"GDI32.DLL");
+    if (module) {
+       gdi_init_lang_pack = (gdi_init_lang_pack_func_t)
+           GetProcAddress (module, "GdiInitializeLanguagePack");
+       if (gdi_init_lang_pack)
+           gdi_init_lang_pack (0);
+    }
+}
+
+static cairo_int_status_t
+analyze_surface_pattern_transparency (cairo_surface_pattern_t *pattern)
+{
+    cairo_image_surface_t  *image;
+    void                  *image_extra;
+    cairo_int_status_t      status;
+    cairo_image_transparency_t transparency;
+
+    status = _cairo_surface_acquire_source_image (pattern->surface,
+                                                 &image,
+                                                 &image_extra);
+    if (status)
+       return status;
+
+    transparency = _cairo_image_analyze_transparency (image);
+    switch (transparency) {
+    case CAIRO_IMAGE_UNKNOWN:
+       ASSERT_NOT_REACHED;
+    case CAIRO_IMAGE_IS_OPAQUE:
+       status = CAIRO_STATUS_SUCCESS;
+       break;
+
+    case CAIRO_IMAGE_HAS_BILEVEL_ALPHA:
+    case CAIRO_IMAGE_HAS_ALPHA:
+       status = CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY;
+       break;
+    }
+
+    _cairo_surface_release_source_image (pattern->surface, image, image_extra);
+
+    return status;
+}
+
+static cairo_bool_t
+surface_pattern_supported (const cairo_surface_pattern_t *pattern)
+{
+    if (_cairo_surface_is_recording (pattern->surface))
+       return TRUE;
+
+    if (cairo_surface_get_type (pattern->surface) != CAIRO_SURFACE_TYPE_WIN32 &&
+       pattern->surface->backend->acquire_source_image == NULL)
+    {
+       return FALSE;
+    }
+
+    return TRUE;
+}
+
+static cairo_bool_t
+pattern_supported (cairo_win32_surface_t *surface, const cairo_pattern_t *pattern)
+{
+    if (pattern->type == CAIRO_PATTERN_TYPE_SOLID)
+       return TRUE;
+
+    if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE)
+       return surface_pattern_supported ((const cairo_surface_pattern_t *) pattern);
+
+    if (pattern->type == CAIRO_PATTERN_TYPE_LINEAR)
+       return surface->flags & CAIRO_WIN32_SURFACE_CAN_RECT_GRADIENT;
+
+    return FALSE;
+}
+
+static cairo_int_status_t
+_cairo_win32_printing_surface_analyze_operation (cairo_win32_surface_t *surface,
+                                                 cairo_operator_t       op,
+                                                 const cairo_pattern_t *pattern)
+{
+    if (! pattern_supported (surface, pattern))
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    if (!(op == CAIRO_OPERATOR_SOURCE ||
+         op == CAIRO_OPERATOR_OVER ||
+         op == CAIRO_OPERATOR_CLEAR))
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) {
+       cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) pattern;
+
+       if ( _cairo_surface_is_recording (surface_pattern->surface))
+           return CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN;
+    }
+
+    if (op == CAIRO_OPERATOR_SOURCE ||
+       op == CAIRO_OPERATOR_CLEAR)
+       return CAIRO_STATUS_SUCCESS;
+
+    /* CAIRO_OPERATOR_OVER is only supported for opaque patterns. If
+     * the pattern contains transparency, we return
+     * CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY to the analysis
+     * surface. If the analysis surface determines that there is
+     * anything drawn under this operation, a fallback image will be
+     * used. Otherwise the operation will be replayed during the
+     * render stage and we blend the transarency into the white
+     * background to convert the pattern to opaque.
+     */
+
+    if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) {
+       cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) pattern;
+
+       return analyze_surface_pattern_transparency (surface_pattern);
+    }
+
+    if (_cairo_pattern_is_opaque (pattern, NULL))
+       return CAIRO_STATUS_SUCCESS;
+    else
+       return CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY;
+}
+
+static cairo_bool_t
+_cairo_win32_printing_surface_operation_supported (cairo_win32_surface_t *surface,
+                                                   cairo_operator_t       op,
+                                                   const cairo_pattern_t *pattern)
+{
+    if (_cairo_win32_printing_surface_analyze_operation (surface, op, pattern) != CAIRO_INT_STATUS_UNSUPPORTED)
+       return TRUE;
+    else
+       return FALSE;
+}
+
+static void
+_cairo_win32_printing_surface_init_clear_color (cairo_win32_surface_t *surface,
+                                               cairo_solid_pattern_t *color)
+{
+    if (surface->content == CAIRO_CONTENT_COLOR_ALPHA)
+       _cairo_pattern_init_solid (color, CAIRO_COLOR_WHITE);
+    else
+       _cairo_pattern_init_solid (color, CAIRO_COLOR_BLACK);
+}
+
+static COLORREF
+_cairo_win32_printing_surface_flatten_transparency (cairo_win32_surface_t *surface,
+                                                   const cairo_color_t   *color)
+{
+    COLORREF c;
+    BYTE red, green, blue;
+
+    red   = color->red_short   >> 8;
+    green = color->green_short >> 8;
+    blue  = color->blue_short  >> 8;
+
+    if (!CAIRO_COLOR_IS_OPAQUE(color)) {
+       if (surface->content == CAIRO_CONTENT_COLOR_ALPHA) {
+           /* Blend into white */
+           uint8_t one_minus_alpha = 255 - (color->alpha_short >> 8);
+
+           red   = (color->red_short   >> 8) + one_minus_alpha;
+           green = (color->green_short >> 8) + one_minus_alpha;
+           blue  = (color->blue_short  >> 8) + one_minus_alpha;
+       } else {
+           /* Blend into black */
+           red   = (color->red_short   >> 8);
+           green = (color->green_short >> 8);
+           blue  = (color->blue_short  >> 8);
+       }
+    }
+    c = RGB (red, green, blue);
+
+    return c;
+}
+
+static cairo_status_t
+_cairo_win32_printing_surface_select_solid_brush (cairo_win32_surface_t *surface,
+                                                  const cairo_pattern_t *source)
+{
+    cairo_solid_pattern_t *pattern = (cairo_solid_pattern_t *) source;
+    COLORREF color;
+
+    color = _cairo_win32_printing_surface_flatten_transparency (surface,
+                                                               &pattern->color);
+    surface->brush = CreateSolidBrush (color);
+    if (!surface->brush)
+       return _cairo_win32_print_gdi_error ("_cairo_win32_surface_select_solid_brush(CreateSolidBrush)");
+    surface->old_brush = SelectObject (surface->dc, surface->brush);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_win32_printing_surface_done_solid_brush (cairo_win32_surface_t *surface)
+{
+    if (surface->old_brush) {
+       SelectObject (surface->dc, surface->old_brush);
+       DeleteObject (surface->brush);
+       surface->old_brush = NULL;
+    }
+}
+
+static cairo_status_t
+_cairo_win32_printing_surface_get_ctm_clip_box (cairo_win32_surface_t *surface,
+                                               RECT                  *clip)
+{
+    XFORM xform;
+
+    _cairo_matrix_to_win32_xform (&surface->ctm, &xform);
+    if (!ModifyWorldTransform (surface->dc, &xform, MWT_LEFTMULTIPLY))
+       return _cairo_win32_print_gdi_error ("_cairo_win32_printing_surface_get_clip_box:ModifyWorldTransform");
+    GetClipBox (surface->dc, clip);
+
+    _cairo_matrix_to_win32_xform (&surface->gdi_ctm, &xform);
+    if (!SetWorldTransform (surface->dc, &xform))
+       return _cairo_win32_print_gdi_error ("_cairo_win32_printing_surface_get_clip_box:SetWorldTransform");
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_win32_printing_surface_paint_solid_pattern (cairo_win32_surface_t *surface,
+                                                   const cairo_pattern_t *pattern)
+{
+    RECT clip;
+    cairo_status_t status;
+
+    GetClipBox (surface->dc, &clip);
+    status = _cairo_win32_printing_surface_select_solid_brush (surface, pattern);
+    if (status)
+       return status;
+
+    FillRect (surface->dc, &clip, surface->brush);
+    _cairo_win32_printing_surface_done_solid_brush (surface);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_win32_printing_surface_paint_recording_pattern (cairo_win32_surface_t   *surface,
+                                                      cairo_surface_pattern_t *pattern)
+{
+    cairo_content_t old_content;
+    cairo_matrix_t old_ctm;
+    cairo_bool_t old_has_ctm;
+    cairo_rectangle_int_t recording_extents;
+    cairo_status_t status;
+    cairo_extend_t extend;
+    cairo_matrix_t p2d;
+    XFORM xform;
+    int x_tile, y_tile, left, right, top, bottom;
+    RECT clip;
+    cairo_recording_surface_t *recording_surface = (cairo_recording_surface_t *) pattern->surface;
+    cairo_box_t bbox;
+
+    extend = cairo_pattern_get_extend (&pattern->base);
+
+    p2d = pattern->base.matrix;
+    status = cairo_matrix_invert (&p2d);
+    /* _cairo_pattern_set_matrix guarantees invertibility */
+    assert (status == CAIRO_STATUS_SUCCESS);
+
+    old_ctm = surface->ctm;
+    old_has_ctm = surface->has_ctm;
+    cairo_matrix_multiply (&p2d, &p2d, &surface->ctm);
+    surface->ctm = p2d;
+    SaveDC (surface->dc);
+    _cairo_matrix_to_win32_xform (&p2d, &xform);
+
+    status = _cairo_recording_surface_get_bbox (recording_surface, &bbox, NULL);
+    if (status)
+       return status;
+
+    _cairo_box_round_to_rectangle (&bbox, &recording_extents);
+
+    status = _cairo_win32_printing_surface_get_ctm_clip_box (surface, &clip);
+    if (status)
+       return status;
+
+    if (extend == CAIRO_EXTEND_REPEAT || extend == CAIRO_EXTEND_REFLECT) {
+       left = floor (clip.left / _cairo_fixed_to_double (bbox.p2.x - bbox.p1.x));
+       right = ceil (clip.right / _cairo_fixed_to_double (bbox.p2.x - bbox.p1.x));
+       top = floor (clip.top / _cairo_fixed_to_double (bbox.p2.y - bbox.p1.y));
+       bottom = ceil (clip.bottom / _cairo_fixed_to_double (bbox.p2.y - bbox.p1.y));
+    } else {
+       left = 0;
+       right = 1;
+       top = 0;
+       bottom = 1;
+    }
+
+    old_content = surface->content;
+    if (recording_surface->base.content == CAIRO_CONTENT_COLOR) {
+       surface->content = CAIRO_CONTENT_COLOR;
+       status = _cairo_win32_printing_surface_paint_solid_pattern (surface,
+                                                                   &_cairo_pattern_black.base);
+       if (status)
+           return status;
+    }
+
+    for (y_tile = top; y_tile < bottom; y_tile++) {
+       for (x_tile = left; x_tile < right; x_tile++) {
+           cairo_matrix_t m;
+           double x, y;
+
+           SaveDC (surface->dc);
+           m = p2d;
+           cairo_matrix_translate (&m,
+                                   x_tile*recording_extents.width,
+                                   y_tile*recording_extents.height);
+           if (extend == CAIRO_EXTEND_REFLECT) {
+               if (x_tile % 2) {
+                   cairo_matrix_translate (&m, recording_extents.width, 0);
+                   cairo_matrix_scale (&m, -1, 1);
+               }
+               if (y_tile % 2) {
+                   cairo_matrix_translate (&m, 0, recording_extents.height);
+                   cairo_matrix_scale (&m, 1, -1);
+               }
+           }
+           surface->ctm = m;
+           surface->has_ctm = !_cairo_matrix_is_identity (&surface->ctm);
+
+           /* Set clip path around bbox of the pattern. */
+           BeginPath (surface->dc);
+
+           x = 0;
+           y = 0;
+           cairo_matrix_transform_point (&surface->ctm, &x, &y);
+           MoveToEx (surface->dc, (int) x, (int) y, NULL);
+
+           x = recording_extents.width;
+           y = 0;
+           cairo_matrix_transform_point (&surface->ctm, &x, &y);
+           LineTo (surface->dc, (int) x, (int) y);
+
+           x = recording_extents.width;
+           y = recording_extents.height;
+           cairo_matrix_transform_point (&surface->ctm, &x, &y);
+           LineTo (surface->dc, (int) x, (int) y);
+
+           x = 0;
+           y = recording_extents.height;
+           cairo_matrix_transform_point (&surface->ctm, &x, &y);
+           LineTo (surface->dc, (int) x, (int) y);
+
+           CloseFigure (surface->dc);
+           EndPath (surface->dc);
+           SelectClipPath (surface->dc, RGN_AND);
+
+           SaveDC (surface->dc); /* Allow clip path to be reset during replay */
+           status = _cairo_recording_surface_replay_region (&recording_surface->base, NULL,
+                                                            &surface->base,
+                                                            CAIRO_RECORDING_REGION_NATIVE);
+           assert (status != CAIRO_INT_STATUS_UNSUPPORTED);
+           /* Restore both the clip save and our earlier path SaveDC */
+           RestoreDC (surface->dc, -2);
+
+           if (status)
+               return status;
+       }
+    }
+
+    surface->content = old_content;
+    surface->ctm = old_ctm;
+    surface->has_ctm = old_has_ctm;
+    RestoreDC (surface->dc, -1);
+
+    return status;
+}
+
+static cairo_int_status_t
+_cairo_win32_printing_surface_check_jpeg (cairo_win32_surface_t   *surface,
+                                         cairo_surface_t         *source,
+                                         const unsigned char    **data,
+                                         unsigned long           *length,
+                                         cairo_image_info_t      *info)
+{
+    const unsigned char *mime_data;
+    unsigned long mime_data_length;
+    cairo_int_status_t status;
+    DWORD result;
+
+    if (!(surface->flags & CAIRO_WIN32_SURFACE_CAN_CHECK_JPEG))
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    cairo_surface_get_mime_data (source, CAIRO_MIME_TYPE_JPEG,
+                                &mime_data, &mime_data_length);
+    if (mime_data == NULL)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    status = _cairo_image_info_get_jpeg_info (info, mime_data, mime_data_length);
+    if (status)
+       return status;
+
+    result = 0;
+    if (ExtEscape(surface->dc, CHECKJPEGFORMAT, mime_data_length, (char *) mime_data,
+                 sizeof(result), (char *) &result) <= 0)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    if (result != 1)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    *data = mime_data;
+    *length = mime_data_length;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_win32_printing_surface_check_png (cairo_win32_surface_t   *surface,
+                                        cairo_surface_t         *source,
+                                        const unsigned char    **data,
+                                        unsigned long           *length,
+                                        cairo_image_info_t      *info)
+{
+    const unsigned char *mime_data;
+    unsigned long mime_data_length;
+
+    cairo_int_status_t status;
+    DWORD result;
+
+    if (!(surface->flags & CAIRO_WIN32_SURFACE_CAN_CHECK_PNG))
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    cairo_surface_get_mime_data (source, CAIRO_MIME_TYPE_PNG,
+                                &mime_data, &mime_data_length);
+    if (mime_data == NULL)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    status = _cairo_image_info_get_png_info (info, mime_data, mime_data_length);
+    if (status)
+       return status;
+
+    result = 0;
+    if (ExtEscape(surface->dc, CHECKPNGFORMAT, mime_data_length, (char *) mime_data,
+                 sizeof(result), (char *) &result) <= 0)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    if (result != 1)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    *data = mime_data;
+    *length = mime_data_length;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_win32_printing_surface_paint_image_pattern (cairo_win32_surface_t   *surface,
+                                                  cairo_surface_pattern_t *pattern)
+{
+    cairo_status_t status;
+    cairo_extend_t extend;
+    cairo_image_surface_t *image;
+    void *image_extra;
+    cairo_image_surface_t *opaque_image = NULL;
+    BITMAPINFO bi;
+    cairo_matrix_t m;
+    int oldmode;
+    XFORM xform;
+    int x_tile, y_tile, left, right, top, bottom;
+    RECT clip;
+    const cairo_color_t *background_color;
+    const unsigned char *mime_data;
+    unsigned long mime_size;
+    cairo_image_info_t mime_info;
+    cairo_bool_t use_mime;
+    DWORD mime_type;
+
+    /* If we can't use StretchDIBits with this surface, we can't do anything
+     * here.
+     */
+    if (!(surface->flags & CAIRO_WIN32_SURFACE_CAN_STRETCHDIB))
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    if (surface->content == CAIRO_CONTENT_COLOR_ALPHA)
+       background_color = CAIRO_COLOR_WHITE;
+    else
+       background_color = CAIRO_COLOR_BLACK;
+
+    extend = cairo_pattern_get_extend (&pattern->base);
+
+    status = _cairo_surface_acquire_source_image (pattern->surface,
+                                                 &image, &image_extra);
+    if (status)
+       return status;
+
+    if (image->base.status) {
+       status = image->base.status;
+       goto CLEANUP_IMAGE;
+    }
+
+    if (image->width == 0 || image->height == 0) {
+       status = CAIRO_STATUS_SUCCESS;
+       goto CLEANUP_IMAGE;
+    }
+
+    mime_type = BI_JPEG;
+    status = _cairo_win32_printing_surface_check_jpeg (surface,
+                                                      pattern->surface,
+                                                      &mime_data,
+                                                      &mime_size,
+                                                      &mime_info);
+    if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
+       mime_type = BI_PNG;
+       status = _cairo_win32_printing_surface_check_png (surface,
+                                                         pattern->surface,
+                                                         &mime_data,
+                                                         &mime_size,
+                                                         &mime_info);
+    }
+    if (_cairo_status_is_error (status))
+       return status;
+
+    use_mime = (status == CAIRO_STATUS_SUCCESS);
+
+    if (!use_mime && image->format != CAIRO_FORMAT_RGB24) {
+       cairo_surface_t *opaque_surface;
+       cairo_surface_pattern_t image_pattern;
+       cairo_solid_pattern_t background_pattern;
+
+       opaque_surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24,
+                                                    image->width,
+                                                    image->height);
+       if (opaque_surface->status) {
+           status = opaque_surface->status;
+           goto CLEANUP_OPAQUE_IMAGE;
+       }
+
+       _cairo_pattern_init_solid (&background_pattern,
+                                  background_color);
+       status = _cairo_surface_paint (opaque_surface,
+                                      CAIRO_OPERATOR_SOURCE,
+                                      &background_pattern.base,
+                                      NULL);
+       if (status)
+           goto CLEANUP_OPAQUE_IMAGE;
+
+       _cairo_pattern_init_for_surface (&image_pattern, &image->base);
+       status = _cairo_surface_paint (opaque_surface,
+                                      CAIRO_OPERATOR_OVER,
+                                      &image_pattern.base,
+                                      NULL);
+       _cairo_pattern_fini (&image_pattern.base);
+       if (status)
+           goto CLEANUP_OPAQUE_IMAGE;
+
+       opaque_image = (cairo_image_surface_t *) opaque_surface;
+    } else {
+       opaque_image = image;
+    }
+
+    bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+    bi.bmiHeader.biWidth = use_mime ? mime_info.width : opaque_image->width;
+    bi.bmiHeader.biHeight = use_mime ? - mime_info.height : -opaque_image->height;
+    bi.bmiHeader.biSizeImage = use_mime ? mime_size : 0;
+    bi.bmiHeader.biXPelsPerMeter = PELS_72DPI;
+    bi.bmiHeader.biYPelsPerMeter = PELS_72DPI;
+    bi.bmiHeader.biPlanes = 1;
+    bi.bmiHeader.biBitCount = 32;
+    bi.bmiHeader.biCompression = use_mime ? mime_type : BI_RGB;
+    bi.bmiHeader.biClrUsed = 0;
+    bi.bmiHeader.biClrImportant = 0;
+
+    m = pattern->base.matrix;
+    status = cairo_matrix_invert (&m);
+    /* _cairo_pattern_set_matrix guarantees invertibility */
+    assert (status == CAIRO_STATUS_SUCCESS);
+
+    cairo_matrix_multiply (&m, &m, &surface->gdi_ctm);
+    SaveDC (surface->dc);
+    _cairo_matrix_to_win32_xform (&m, &xform);
+
+    if (! SetWorldTransform (surface->dc, &xform)) {
+       status = _cairo_win32_print_gdi_error ("_cairo_win32_printing_surface_paint_image_pattern");
+       goto CLEANUP_OPAQUE_IMAGE;
+    }
+
+    oldmode = SetStretchBltMode(surface->dc, HALFTONE);
+
+    GetClipBox (surface->dc, &clip);
+    if (extend == CAIRO_EXTEND_REPEAT || extend == CAIRO_EXTEND_REFLECT) {
+       left = floor ( clip.left / (double) opaque_image->width);
+       right = ceil (clip.right / (double) opaque_image->width);
+       top = floor (clip.top / (double) opaque_image->height);
+       bottom = ceil (clip.bottom / (double) opaque_image->height);
+    } else {
+       left = 0;
+       right = 1;
+       top = 0;
+       bottom = 1;
+    }
+
+    for (y_tile = top; y_tile < bottom; y_tile++) {
+       for (x_tile = left; x_tile < right; x_tile++) {
+           if (!StretchDIBits (surface->dc,
+                               x_tile*opaque_image->width,
+                               y_tile*opaque_image->height,
+                               opaque_image->width,
+                               opaque_image->height,
+                               0,
+                               0,
+                               use_mime ? mime_info.width : opaque_image->width,
+                               use_mime ? mime_info.height : opaque_image->height,
+                               use_mime ? mime_data : opaque_image->data,
+                               &bi,
+                               DIB_RGB_COLORS,
+                               SRCCOPY))
+           {
+               status = _cairo_win32_print_gdi_error ("_cairo_win32_printing_surface_paint(StretchDIBits)");
+               goto CLEANUP_OPAQUE_IMAGE;
+           }
+       }
+    }
+    SetStretchBltMode(surface->dc, oldmode);
+    RestoreDC (surface->dc, -1);
+
+CLEANUP_OPAQUE_IMAGE:
+    if (opaque_image != image)
+       cairo_surface_destroy (&opaque_image->base);
+CLEANUP_IMAGE:
+    _cairo_surface_release_source_image (pattern->surface, image, image_extra);
+
+    return status;
+}
+
+static cairo_status_t
+_cairo_win32_printing_surface_paint_surface_pattern (cairo_win32_surface_t   *surface,
+                                                     cairo_surface_pattern_t *pattern)
+{
+    if (_cairo_surface_is_recording (pattern->surface)) {
+       return _cairo_win32_printing_surface_paint_recording_pattern (surface,
+                                                                     pattern);
+    } else {
+       return _cairo_win32_printing_surface_paint_image_pattern (surface,
+                                                                 pattern);
+    }
+}
+
+static void
+vertex_set_color (TRIVERTEX *vert, cairo_color_stop_t *color)
+{
+    /* MSDN says that the range here is 0x0000 .. 0xff00;
+     * that may well be a typo, but just chop the low bits
+     * here. */
+    vert->Alpha = 0xff00;
+    vert->Red   = color->red_short & 0xff00;
+    vert->Green = color->green_short & 0xff00;
+    vert->Blue  = color->blue_short & 0xff00;
+}
+
+static cairo_int_status_t
+_cairo_win32_printing_surface_paint_linear_pattern (cairo_win32_surface_t *surface,
+                                                    cairo_linear_pattern_t *pattern)
+{
+    TRIVERTEX *vert;
+    GRADIENT_RECT *rect;
+    RECT clip;
+    XFORM xform;
+    int i, num_stops;
+    cairo_matrix_t mat, rot;
+    double p1x, p1y, p2x, p2y, xd, yd, d, sn, cs;
+    cairo_extend_t extend;
+    int range_start, range_stop, num_ranges, num_rects, stop;
+    int total_verts, total_rects;
+    cairo_status_t status;
+
+    extend = cairo_pattern_get_extend (&pattern->base.base);
+    SaveDC (surface->dc);
+
+    mat = pattern->base.base.matrix;
+    status = cairo_matrix_invert (&mat);
+    /* _cairo_pattern_set_matrix guarantees invertibility */
+    assert (status == CAIRO_STATUS_SUCCESS);
+
+    cairo_matrix_multiply (&mat, &surface->ctm, &mat);
+
+    p1x = pattern->pd1.x;
+    p1y = pattern->pd1.y;
+    p2x = pattern->pd2.x;
+    p2y = pattern->pd2.y;
+    cairo_matrix_translate (&mat, p1x, p1y);
+
+    xd = p2x - p1x;
+    yd = p2y - p1y;
+    d = sqrt (xd*xd + yd*yd);
+    sn = yd/d;
+    cs = xd/d;
+    cairo_matrix_init (&rot,
+                      cs, sn,
+                      -sn, cs,
+                       0, 0);
+    cairo_matrix_multiply (&mat, &rot, &mat);
+
+    _cairo_matrix_to_win32_xform (&mat, &xform);
+
+    if (!SetWorldTransform (surface->dc, &xform))
+       return _cairo_win32_print_gdi_error ("_win32_printing_surface_paint_linear_pattern:SetWorldTransform2");
+
+    GetClipBox (surface->dc, &clip);
+
+    if (extend == CAIRO_EXTEND_REPEAT || extend == CAIRO_EXTEND_REFLECT) {
+       range_start = floor (clip.left / d);
+       range_stop = ceil (clip.right / d);
+    } else {
+       range_start = 0;
+       range_stop = 1;
+    }
+    num_ranges = range_stop - range_start;
+    num_stops = pattern->base.n_stops;
+    num_rects = num_stops - 1;
+
+    /* Add an extra four points and two rectangles for EXTEND_PAD */
+    vert = malloc (sizeof (TRIVERTEX) * (num_rects*2*num_ranges + 4));
+    rect = malloc (sizeof (GRADIENT_RECT) * (num_rects*num_ranges + 2));
+
+    for (i = 0; i < num_ranges*num_rects; i++) {
+       vert[i*2].y = (LONG) clip.top;
+       if (i%num_rects == 0) {
+           stop = 0;
+           if (extend == CAIRO_EXTEND_REFLECT && (range_start+(i/num_rects))%2)
+               stop = num_rects;
+           vert[i*2].x = (LONG)(d*(range_start + i/num_rects));
+           vertex_set_color (&vert[i*2], &pattern->base.stops[stop].color);
+       } else {
+           vert[i*2].x = vert[i*2-1].x;
+           vert[i*2].Red = vert[i*2-1].Red;
+           vert[i*2].Green = vert[i*2-1].Green;
+           vert[i*2].Blue = vert[i*2-1].Blue;
+           vert[i*2].Alpha = vert[i*2-1].Alpha;
+       }
+
+       stop = i%num_rects + 1;
+       vert[i*2+1].x = (LONG)(d*(range_start + i/num_rects + pattern->base.stops[stop].offset));
+       vert[i*2+1].y = (LONG) clip.bottom;
+       if (extend == CAIRO_EXTEND_REFLECT && (range_start+(i/num_rects))%2)
+           stop = num_rects - stop;
+       vertex_set_color (&vert[i*2+1], &pattern->base.stops[stop].color);
+
+       rect[i].UpperLeft = i*2;
+       rect[i].LowerRight = i*2 + 1;
+    }
+    total_verts = 2*num_ranges*num_rects;
+    total_rects = num_ranges*num_rects;
+
+    if (extend == CAIRO_EXTEND_PAD) {
+       vert[i*2].x = vert[i*2-1].x;
+       vert[i*2].y = (LONG) clip.top;
+       vert[i*2].Red = vert[i*2-1].Red;
+       vert[i*2].Green = vert[i*2-1].Green;
+       vert[i*2].Blue = vert[i*2-1].Blue;
+       vert[i*2].Alpha = 0xff00;
+       vert[i*2+1].x = clip.right;
+       vert[i*2+1].y = (LONG) clip.bottom;
+       vert[i*2+1].Red = vert[i*2-1].Red;
+       vert[i*2+1].Green = vert[i*2-1].Green;
+       vert[i*2+1].Blue = vert[i*2-1].Blue;
+       vert[i*2+1].Alpha = 0xff00;
+       rect[i].UpperLeft = i*2;
+       rect[i].LowerRight = i*2 + 1;
+
+       i++;
+
+       vert[i*2].x = clip.left;
+       vert[i*2].y = (LONG) clip.top;
+       vert[i*2].Red = vert[0].Red;
+       vert[i*2].Green = vert[0].Green;
+       vert[i*2].Blue = vert[0].Blue;
+       vert[i*2].Alpha = 0xff00;
+       vert[i*2+1].x = vert[0].x;
+       vert[i*2+1].y = (LONG) clip.bottom;
+       vert[i*2+1].Red = vert[0].Red;
+       vert[i*2+1].Green = vert[0].Green;
+       vert[i*2+1].Blue = vert[0].Blue;
+       vert[i*2+1].Alpha = 0xff00;
+       rect[i].UpperLeft = i*2;
+       rect[i].LowerRight = i*2 + 1;
+
+       total_verts += 4;
+       total_rects += 2;
+    }
+
+    if (!GradientFill (surface->dc,
+                      vert, total_verts,
+                      rect, total_rects,
+                      GRADIENT_FILL_RECT_H))
+       return _cairo_win32_print_gdi_error ("_win32_printing_surface_paint_linear_pattern:GradientFill");
+
+    free (rect);
+    free (vert);
+    RestoreDC (surface->dc, -1);
+
+    return 0;
+}
+
+static cairo_int_status_t
+_cairo_win32_printing_surface_paint_pattern (cairo_win32_surface_t *surface,
+                                             const cairo_pattern_t *pattern)
+{
+    cairo_status_t status;
+
+    switch (pattern->type) {
+    case CAIRO_PATTERN_TYPE_SOLID:
+       status = _cairo_win32_printing_surface_paint_solid_pattern (surface, pattern);
+       if (status)
+           return status;
+       break;
+
+    case CAIRO_PATTERN_TYPE_SURFACE:
+       status = _cairo_win32_printing_surface_paint_surface_pattern (surface,
+                                                                      (cairo_surface_pattern_t *) pattern);
+       if (status)
+           return status;
+       break;
+
+    case CAIRO_PATTERN_TYPE_LINEAR:
+       status = _cairo_win32_printing_surface_paint_linear_pattern (surface, (cairo_linear_pattern_t *) pattern);
+       if (status)
+           return status;
+       break;
+
+    case CAIRO_PATTERN_TYPE_RADIAL:
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+       break;
+
+    case CAIRO_PATTERN_TYPE_MESH:
+       ASSERT_NOT_REACHED;
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+typedef struct _win32_print_path_info {
+    cairo_win32_surface_t *surface;
+} win32_path_info_t;
+
+static cairo_status_t
+_cairo_win32_printing_surface_path_move_to (void *closure,
+                                           const cairo_point_t *point)
+{
+    win32_path_info_t *path_info = closure;
+
+    if (path_info->surface->has_ctm) {
+       double x, y;
+
+       x = _cairo_fixed_to_double (point->x);
+       y = _cairo_fixed_to_double (point->y);
+       cairo_matrix_transform_point (&path_info->surface->ctm, &x, &y);
+       MoveToEx (path_info->surface->dc, (int) x, (int) y, NULL);
+    } else {
+       MoveToEx (path_info->surface->dc,
+                 _cairo_fixed_integer_part (point->x),
+                 _cairo_fixed_integer_part (point->y),
+                 NULL);
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_win32_printing_surface_path_line_to (void *closure,
+                                           const cairo_point_t *point)
+{
+    win32_path_info_t *path_info = closure;
+
+    path_info->surface->path_empty = FALSE;
+    if (path_info->surface->has_ctm) {
+       double x, y;
+
+       x = _cairo_fixed_to_double (point->x);
+       y = _cairo_fixed_to_double (point->y);
+       cairo_matrix_transform_point (&path_info->surface->ctm, &x, &y);
+       LineTo (path_info->surface->dc, (int) x, (int) y);
+    } else {
+       LineTo (path_info->surface->dc,
+               _cairo_fixed_integer_part (point->x),
+               _cairo_fixed_integer_part (point->y));
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_win32_printing_surface_path_curve_to (void          *closure,
+                                             const cairo_point_t *b,
+                                             const cairo_point_t *c,
+                                             const cairo_point_t *d)
+{
+    win32_path_info_t *path_info = closure;
+    POINT points[3];
+
+    path_info->surface->path_empty = FALSE;
+    if (path_info->surface->has_ctm) {
+       double x, y;
+
+       x = _cairo_fixed_to_double (b->x);
+       y = _cairo_fixed_to_double (b->y);
+       cairo_matrix_transform_point (&path_info->surface->ctm, &x, &y);
+       points[0].x = (LONG) x;
+       points[0].y = (LONG) y;
+
+       x = _cairo_fixed_to_double (c->x);
+       y = _cairo_fixed_to_double (c->y);
+       cairo_matrix_transform_point (&path_info->surface->ctm, &x, &y);
+       points[1].x = (LONG) x;
+       points[1].y = (LONG) y;
+
+       x = _cairo_fixed_to_double (d->x);
+       y = _cairo_fixed_to_double (d->y);
+       cairo_matrix_transform_point (&path_info->surface->ctm, &x, &y);
+       points[2].x = (LONG) x;
+       points[2].y = (LONG) y;
+    } else {
+       points[0].x = _cairo_fixed_integer_part (b->x);
+       points[0].y = _cairo_fixed_integer_part (b->y);
+       points[1].x = _cairo_fixed_integer_part (c->x);
+       points[1].y = _cairo_fixed_integer_part (c->y);
+       points[2].x = _cairo_fixed_integer_part (d->x);
+       points[2].y = _cairo_fixed_integer_part (d->y);
+    }
+    PolyBezierTo (path_info->surface->dc, points, 3);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_win32_printing_surface_path_close_path (void *closure)
+{
+    win32_path_info_t *path_info = closure;
+
+    CloseFigure (path_info->surface->dc);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_win32_printing_surface_emit_path (cairo_win32_surface_t    *surface,
+                                        const cairo_path_fixed_t *path)
+{
+    win32_path_info_t path_info;
+
+    path_info.surface = surface;
+    return _cairo_path_fixed_interpret (path,
+                                       _cairo_win32_printing_surface_path_move_to,
+                                       _cairo_win32_printing_surface_path_line_to,
+                                       _cairo_win32_printing_surface_path_curve_to,
+                                       _cairo_win32_printing_surface_path_close_path,
+                                       &path_info);
+}
+
+static cairo_int_status_t
+_cairo_win32_printing_surface_show_page (void *abstract_surface)
+{
+    cairo_win32_surface_t *surface = abstract_surface;
+
+    /* Undo both SaveDC's that we did in start_page */
+    RestoreDC (surface->dc, -2);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_win32_printing_surface_clipper_intersect_clip_path (cairo_surface_clipper_t *clipper,
+                                                   cairo_path_fixed_t *path,
+                                                   cairo_fill_rule_t   fill_rule,
+                                                   double             tolerance,
+                                                   cairo_antialias_t   antialias)
+{
+    cairo_win32_surface_t *surface = cairo_container_of (clipper,
+                                                        cairo_win32_surface_t,
+                                                        clipper);
+    cairo_status_t status;
+
+    if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
+       return CAIRO_STATUS_SUCCESS;
+
+    if (path == NULL) {
+       RestoreDC (surface->dc, -1);
+       SaveDC (surface->dc);
+
+       return CAIRO_STATUS_SUCCESS;
+    }
+
+    BeginPath (surface->dc);
+    status = _cairo_win32_printing_surface_emit_path (surface, path);
+    EndPath (surface->dc);
+
+    switch (fill_rule) {
+    case CAIRO_FILL_RULE_WINDING:
+       SetPolyFillMode (surface->dc, WINDING);
+       break;
+    case CAIRO_FILL_RULE_EVEN_ODD:
+       SetPolyFillMode (surface->dc, ALTERNATE);
+       break;
+    default:
+       ASSERT_NOT_REACHED;
+    }
+
+    SelectClipPath (surface->dc, RGN_AND);
+
+    return status;
+}
+
+static void
+_cairo_win32_printing_surface_get_font_options (void                  *abstract_surface,
+                                                cairo_font_options_t  *options)
+{
+    _cairo_font_options_init_default (options);
+
+    cairo_font_options_set_hint_style (options, CAIRO_HINT_STYLE_NONE);
+    cairo_font_options_set_hint_metrics (options, CAIRO_HINT_METRICS_OFF);
+    cairo_font_options_set_antialias (options, CAIRO_ANTIALIAS_GRAY);
+    _cairo_font_options_set_round_glyph_positions (options, CAIRO_ROUND_GLYPH_POS_ON);
+}
+
+static cairo_int_status_t
+_cairo_win32_printing_surface_paint (void                      *abstract_surface,
+                                     cairo_operator_t           op,
+                                     const cairo_pattern_t     *source,
+                                    const cairo_clip_t      *clip)
+{
+    cairo_win32_surface_t *surface = abstract_surface;
+    cairo_solid_pattern_t clear;
+    cairo_status_t status;
+
+    status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
+    if (status)
+       return status;
+
+    if (op == CAIRO_OPERATOR_CLEAR) {
+       _cairo_win32_printing_surface_init_clear_color (surface, &clear);
+       source = (cairo_pattern_t*) &clear;
+       op = CAIRO_OPERATOR_SOURCE;
+    }
+
+    if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
+       return _cairo_win32_printing_surface_analyze_operation (surface, op, source);
+
+    assert (_cairo_win32_printing_surface_operation_supported (surface, op, source));
+
+    return _cairo_win32_printing_surface_paint_pattern (surface, source);
+}
+
+static int
+_cairo_win32_line_cap (cairo_line_cap_t cap)
+{
+    switch (cap) {
+    case CAIRO_LINE_CAP_BUTT:
+       return PS_ENDCAP_FLAT;
+    case CAIRO_LINE_CAP_ROUND:
+       return PS_ENDCAP_ROUND;
+    case CAIRO_LINE_CAP_SQUARE:
+       return PS_ENDCAP_SQUARE;
+    default:
+       ASSERT_NOT_REACHED;
+       return 0;
+    }
+}
+
+static int
+_cairo_win32_line_join (cairo_line_join_t join)
+{
+    switch (join) {
+    case CAIRO_LINE_JOIN_MITER:
+       return PS_JOIN_MITER;
+    case CAIRO_LINE_JOIN_ROUND:
+       return PS_JOIN_ROUND;
+    case CAIRO_LINE_JOIN_BEVEL:
+       return PS_JOIN_BEVEL;
+    default:
+       ASSERT_NOT_REACHED;
+       return 0;
+    }
+}
+
+static void
+_cairo_matrix_factor_out_scale (cairo_matrix_t *m, double *scale)
+{
+    double s;
+
+    s = fabs (m->xx);
+    if (fabs (m->xy) > s)
+       s = fabs (m->xy);
+    if (fabs (m->yx) > s)
+       s = fabs (m->yx);
+    if (fabs (m->yy) > s)
+       s = fabs (m->yy);
+    *scale = s;
+    s = 1.0/s;
+    cairo_matrix_scale (m, s, s);
+}
+
+static cairo_int_status_t
+_cairo_win32_printing_surface_stroke (void                     *abstract_surface,
+                                      cairo_operator_t          op,
+                                      const cairo_pattern_t    *source,
+                                      const cairo_path_fixed_t *path,
+                                      const cairo_stroke_style_t *style,
+                                      const cairo_matrix_t     *stroke_ctm,
+                                      const cairo_matrix_t     *stroke_ctm_inverse,
+                                      double                   tolerance,
+                                      cairo_antialias_t                antialias,
+                                     const cairo_clip_t    *clip)
+{
+    cairo_win32_surface_t *surface = abstract_surface;
+    cairo_int_status_t status;
+    HPEN pen;
+    LOGBRUSH brush;
+    COLORREF color;
+    XFORM xform;
+    DWORD pen_style;
+    DWORD *dash_array;
+    HGDIOBJ obj;
+    unsigned int i;
+    cairo_solid_pattern_t clear;
+    cairo_matrix_t mat;
+    double scale;
+
+    status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
+    if (status)
+       return status;
+
+    if (op == CAIRO_OPERATOR_CLEAR) {
+       _cairo_win32_printing_surface_init_clear_color (surface, &clear);
+       source = (cairo_pattern_t*) &clear;
+       op = CAIRO_OPERATOR_SOURCE;
+    }
+
+    if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) {
+       /* Win32 does not support a dash offset. */
+       if (style->num_dashes > 0 && style->dash_offset != 0.0)
+           return CAIRO_INT_STATUS_UNSUPPORTED;
+
+       return _cairo_win32_printing_surface_analyze_operation (surface, op, source);
+    }
+
+    assert (_cairo_win32_printing_surface_operation_supported (surface, op, source));
+    assert (!(style->num_dashes > 0 && style->dash_offset != 0.0));
+
+    cairo_matrix_multiply (&mat, stroke_ctm, &surface->ctm);
+    _cairo_matrix_factor_out_scale (&mat, &scale);
+
+    pen_style = PS_GEOMETRIC;
+    dash_array = NULL;
+    if (style->num_dashes) {
+       pen_style |= PS_USERSTYLE;
+       dash_array = calloc (sizeof (DWORD), style->num_dashes);
+       for (i = 0; i < style->num_dashes; i++) {
+           dash_array[i] = (DWORD) (scale * style->dash[i]);
+       }
+    } else {
+       pen_style |= PS_SOLID;
+    }
+
+    SetMiterLimit (surface->dc, (FLOAT) (style->miter_limit), NULL);
+    if (source->type == CAIRO_PATTERN_TYPE_SOLID) {
+       cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) source;
+
+
+       color = _cairo_win32_printing_surface_flatten_transparency (surface,
+                                                                   &solid->color);
+    } else {
+       /* Color not used as the pen will only be used by WidenPath() */
+       color = RGB (0,0,0);
+    }
+    brush.lbStyle = BS_SOLID;
+    brush.lbColor = color;
+    brush.lbHatch = 0;
+    pen_style |= _cairo_win32_line_cap (style->line_cap);
+    pen_style |= _cairo_win32_line_join (style->line_join);
+    pen = ExtCreatePen(pen_style,
+                      scale * style->line_width,
+                      &brush,
+                      style->num_dashes,
+                      dash_array);
+    if (pen == NULL)
+       return _cairo_win32_print_gdi_error ("_win32_surface_stroke:ExtCreatePen");
+    obj = SelectObject (surface->dc, pen);
+    if (obj == NULL)
+       return _cairo_win32_print_gdi_error ("_win32_surface_stroke:SelectObject");
+
+    BeginPath (surface->dc);
+    status = _cairo_win32_printing_surface_emit_path (surface, path);
+    EndPath (surface->dc);
+    if (status)
+       return status;
+
+    /*
+     * Switch to user space to set line parameters
+     */
+    SaveDC (surface->dc);
+
+    _cairo_matrix_to_win32_xform (&mat, &xform);
+    xform.eDx = 0.0f;
+    xform.eDy = 0.0f;
+
+    if (!ModifyWorldTransform (surface->dc, &xform, MWT_LEFTMULTIPLY))
+       return _cairo_win32_print_gdi_error ("_win32_surface_stroke:SetWorldTransform");
+
+    if (source->type == CAIRO_PATTERN_TYPE_SOLID) {
+       StrokePath (surface->dc);
+    } else {
+       if (!WidenPath (surface->dc))
+           return _cairo_win32_print_gdi_error ("_win32_surface_stroke:WidenPath");
+       if (!SelectClipPath (surface->dc, RGN_AND))
+           return _cairo_win32_print_gdi_error ("_win32_surface_stroke:SelectClipPath");
+
+       /* Return to device space to paint the pattern */
+       _cairo_matrix_to_win32_xform (&surface->gdi_ctm, &xform);
+       if (!SetWorldTransform (surface->dc, &xform))
+           return _cairo_win32_print_gdi_error ("_win32_surface_stroke:ModifyWorldTransform");
+       status = _cairo_win32_printing_surface_paint_pattern (surface, source);
+    }
+    RestoreDC (surface->dc, -1);
+    DeleteObject (pen);
+    free (dash_array);
+
+    return status;
+}
+
+static cairo_int_status_t
+_cairo_win32_printing_surface_fill (void                       *abstract_surface,
+                                   cairo_operator_t             op,
+                                   const cairo_pattern_t       *source,
+                                   const cairo_path_fixed_t    *path,
+                                   cairo_fill_rule_t            fill_rule,
+                                   double                       tolerance,
+                                   cairo_antialias_t            antialias,
+                                   const cairo_clip_t          *clip)
+{
+    cairo_win32_surface_t *surface = abstract_surface;
+    cairo_int_status_t status;
+    cairo_solid_pattern_t clear;
+
+    status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
+    if (status)
+       return status;
+
+    if (op == CAIRO_OPERATOR_CLEAR) {
+       _cairo_win32_printing_surface_init_clear_color (surface, &clear);
+       source = (cairo_pattern_t*) &clear;
+       op = CAIRO_OPERATOR_SOURCE;
+    }
+
+    if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
+       return _cairo_win32_printing_surface_analyze_operation (surface, op, source);
+
+    assert (_cairo_win32_printing_surface_operation_supported (surface, op, source));
+
+    surface->path_empty = TRUE;
+    BeginPath (surface->dc);
+    status = _cairo_win32_printing_surface_emit_path (surface, path);
+    EndPath (surface->dc);
+
+    switch (fill_rule) {
+    case CAIRO_FILL_RULE_WINDING:
+       SetPolyFillMode (surface->dc, WINDING);
+       break;
+    case CAIRO_FILL_RULE_EVEN_ODD:
+       SetPolyFillMode (surface->dc, ALTERNATE);
+       break;
+    default:
+       ASSERT_NOT_REACHED;
+    }
+
+    if (source->type == CAIRO_PATTERN_TYPE_SOLID) {
+       status = _cairo_win32_printing_surface_select_solid_brush (surface, source);
+       if (status)
+           return status;
+
+       FillPath (surface->dc);
+       _cairo_win32_printing_surface_done_solid_brush (surface);
+    } else if (surface->path_empty == FALSE) {
+       SaveDC (surface->dc);
+       SelectClipPath (surface->dc, RGN_AND);
+       status = _cairo_win32_printing_surface_paint_pattern (surface, source);
+       RestoreDC (surface->dc, -1);
+    }
+
+    fflush(stderr);
+
+    return status;
+}
+
+static cairo_int_status_t
+_cairo_win32_printing_surface_emit_win32_glyphs (cairo_win32_surface_t         *surface,
+                                                cairo_operator_t        op,
+                                                const cairo_pattern_t  *source,
+                                                cairo_glyph_t          *glyphs,
+                                                int                     num_glyphs,
+                                                cairo_scaled_font_t    *scaled_font,
+                                                const cairo_clip_t     *clip)
+{
+    cairo_matrix_t ctm;
+    cairo_glyph_t  *unicode_glyphs;
+    cairo_scaled_font_subsets_glyph_t subset_glyph;
+    int i, first;
+    cairo_bool_t sequence_is_unicode;
+    cairo_status_t status = CAIRO_STATUS_SUCCESS;
+
+    /* Where possible reverse the glyph indices back to unicode
+     * characters. Strings of glyphs that could not be reversed to
+     * unicode will be printed with ETO_GLYPH_INDEX.
+     *
+     * As _cairo_win32_scaled_font_index_to_ucs4() is a slow
+     * operation, the font subsetting function
+     * _cairo_scaled_font_subsets_map_glyph() is used to obtain
+     * the unicode value because it caches the reverse mapping in
+     * the subsets.
+     */
+
+    if (surface->has_ctm) {
+       for (i = 0; i < num_glyphs; i++)
+           cairo_matrix_transform_point (&surface->ctm, &glyphs[i].x, &glyphs[i].y);
+       cairo_matrix_multiply (&ctm, &scaled_font->ctm, &surface->ctm);
+       scaled_font = cairo_scaled_font_create (scaled_font->font_face,
+                                               &scaled_font->font_matrix,
+                                               &ctm,
+                                               &scaled_font->options);
+    }
+
+    unicode_glyphs = _cairo_malloc_ab (num_glyphs, sizeof (cairo_glyph_t));
+    if (unicode_glyphs == NULL)
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    memcpy (unicode_glyphs, glyphs, num_glyphs * sizeof (cairo_glyph_t));
+    for (i = 0; i < num_glyphs; i++) {
+       status = _cairo_scaled_font_subsets_map_glyph (surface->font_subsets,
+                                                      scaled_font,
+                                                      glyphs[i].index,
+                                                      NULL, 0,
+                                                      &subset_glyph);
+       if (status)
+           goto fail;
+
+       unicode_glyphs[i].index = subset_glyph.unicode;
+    }
+
+    i = 0;
+    first = 0;
+    sequence_is_unicode = unicode_glyphs[0].index <= 0xffff;
+    while (i < num_glyphs) {
+       if (i == num_glyphs - 1 ||
+           ((unicode_glyphs[i + 1].index < 0xffff) != sequence_is_unicode))
+       {
+           status = _cairo_win32_surface_show_glyphs_internal (
+               surface,
+               op,
+               source,
+               sequence_is_unicode ? &unicode_glyphs[first] : &glyphs[first],
+               i - first + 1,
+               scaled_font,
+               clip,
+               ! sequence_is_unicode);
+           first = i + 1;
+           if (i < num_glyphs - 1)
+               sequence_is_unicode = unicode_glyphs[i + 1].index <= 0xffff;
+       }
+       i++;
+    }
+
+fail:
+    if (surface->has_ctm)
+       cairo_scaled_font_destroy (scaled_font);
+
+    free (unicode_glyphs);
+
+    return status;
+}
+
+static cairo_int_status_t
+_cairo_win32_printing_surface_show_glyphs (void                 *abstract_surface,
+                                           cairo_operator_t     op,
+                                           const cairo_pattern_t *source,
+                                           cairo_glyph_t        *glyphs,
+                                           int                  num_glyphs,
+                                           cairo_scaled_font_t  *scaled_font,
+                                          const cairo_clip_t   *clip)
+{
+    cairo_win32_surface_t *surface = abstract_surface;
+    cairo_status_t status = CAIRO_STATUS_SUCCESS;
+    cairo_scaled_glyph_t *scaled_glyph;
+    cairo_pattern_t *opaque = NULL;
+    int i;
+    cairo_matrix_t old_ctm;
+    cairo_bool_t old_has_ctm;
+    cairo_solid_pattern_t clear;
+
+    status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
+    if (status)
+       return status;
+
+    if (op == CAIRO_OPERATOR_CLEAR) {
+       _cairo_win32_printing_surface_init_clear_color (surface, &clear);
+       source = (cairo_pattern_t*) &clear;
+       op = CAIRO_OPERATOR_SOURCE;
+    }
+
+    if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) {
+       /* When printing bitmap fonts to a printer DC, Windows may
+        * substitute an outline font for bitmap font. As the win32
+        * font backend always uses a screen DC when obtaining the
+        * font metrics the metrics of the substituted font will not
+        * match the metrics that the win32 font backend returns.
+        *
+        * If we are printing a bitmap font, use fallback images to
+        * ensure the font is not substituted.
+        */
+#if CAIRO_HAS_WIN32_FONT
+       if (cairo_scaled_font_get_type (scaled_font) == CAIRO_FONT_TYPE_WIN32) {
+           if (_cairo_win32_scaled_font_is_bitmap (scaled_font))
+               return CAIRO_INT_STATUS_UNSUPPORTED;
+           else
+               return _cairo_win32_printing_surface_analyze_operation (surface, op, source);
+       }
+#endif
+
+       /* For non win32 fonts we need to check that each glyph has a
+        * path available. If a path is not available,
+        * _cairo_scaled_glyph_lookup() will return
+        * CAIRO_INT_STATUS_UNSUPPORTED and a fallback image will be
+        * used.
+        */
+       for (i = 0; i < num_glyphs; i++) {
+           status = _cairo_scaled_glyph_lookup (scaled_font,
+                                                glyphs[i].index,
+                                                CAIRO_SCALED_GLYPH_INFO_PATH,
+                                                &scaled_glyph);
+           if (status)
+               return status;
+       }
+
+       return _cairo_win32_printing_surface_analyze_operation (surface, op, source);
+    }
+
+    if (source->type == CAIRO_PATTERN_TYPE_SOLID) {
+       cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) source;
+       COLORREF color;
+
+       color = _cairo_win32_printing_surface_flatten_transparency (surface,
+                                                                   &solid->color);
+       opaque = cairo_pattern_create_rgb (GetRValue (color) / 255.0,
+                                          GetGValue (color) / 255.0,
+                                          GetBValue (color) / 255.0);
+       if (opaque->status)
+           return opaque->status;
+       source = opaque;
+    }
+
+#if CAIRO_HAS_WIN32_FONT
+    if (cairo_scaled_font_get_type (scaled_font) == CAIRO_FONT_TYPE_WIN32 &&
+       source->type == CAIRO_PATTERN_TYPE_SOLID)
+    {
+       return _cairo_win32_printing_surface_emit_win32_glyphs (surface,
+                                                               op,
+                                                               source,
+                                                               glyphs,
+                                                               num_glyphs,
+                                                               scaled_font,
+                                                               clip);
+    }
+#endif
+
+    SaveDC (surface->dc);
+    old_ctm = surface->ctm;
+    old_has_ctm = surface->has_ctm;
+    surface->has_ctm = TRUE;
+    surface->path_empty = TRUE;
+    BeginPath (surface->dc);
+    for (i = 0; i < num_glyphs; i++) {
+       status = _cairo_scaled_glyph_lookup (scaled_font,
+                                            glyphs[i].index,
+                                            CAIRO_SCALED_GLYPH_INFO_PATH,
+                                            &scaled_glyph);
+       if (status)
+           break;
+       surface->ctm = old_ctm;
+       cairo_matrix_translate (&surface->ctm, glyphs[i].x, glyphs[i].y);
+       status = _cairo_win32_printing_surface_emit_path (surface, scaled_glyph->path);
+    }
+    EndPath (surface->dc);
+    surface->ctm = old_ctm;
+    surface->has_ctm = old_has_ctm;
+    if (status == CAIRO_STATUS_SUCCESS && surface->path_empty == FALSE) {
+       if (source->type == CAIRO_PATTERN_TYPE_SOLID) {
+           status = _cairo_win32_printing_surface_select_solid_brush (surface, source);
+           if (status)
+               return status;
+
+           SetPolyFillMode (surface->dc, WINDING);
+           FillPath (surface->dc);
+           _cairo_win32_printing_surface_done_solid_brush (surface);
+       } else {
+           SelectClipPath (surface->dc, RGN_AND);
+           status = _cairo_win32_printing_surface_paint_pattern (surface, source);
+       }
+    }
+    RestoreDC (surface->dc, -1);
+
+    if (opaque)
+       cairo_pattern_destroy (opaque);
+
+    return status;
+}
+
+static cairo_surface_t *
+_cairo_win32_printing_surface_create_similar (void             *abstract_surface,
+                                             cairo_content_t    content,
+                                             int                width,
+                                             int                height)
+{
+    cairo_rectangle_t extents;
+
+    extents.x = extents.y = 0;
+    extents.width  = width;
+    extents.height = height;
+    return cairo_recording_surface_create (content, &extents);
+}
+
+static cairo_int_status_t
+_cairo_win32_printing_surface_start_page (void *abstract_surface)
+{
+    cairo_win32_surface_t *surface = abstract_surface;
+    XFORM xform;
+    double x_res, y_res;
+    cairo_matrix_t inverse_ctm;
+    cairo_status_t status;
+
+    SaveDC (surface->dc); /* Save application context first, before doing MWT */
+
+    /* As the logical coordinates used by GDI functions (eg LineTo)
+     * are integers we need to do some additional work to prevent
+     * rounding errors. For example the obvious way to paint a recording
+     * pattern is to:
+     *
+     *   SaveDC()
+     *   transform the device context DC by the pattern to device matrix
+     *   replay the recording surface
+     *   RestoreDC()
+     *
+     * The problem here is that if the pattern to device matrix is
+     * [100 0 0 100 0 0], coordinates in the recording pattern such as
+     * (1.56, 2.23) which correspond to (156, 223) in device space
+     * will be rounded to (100, 200) due to (1.56, 2.23) being
+     * truncated to integers.
+     *
+     * This is solved by saving the current GDI CTM in surface->ctm,
+     * switch the GDI CTM to identity, and transforming all
+     * coordinates by surface->ctm before passing them to GDI. When
+     * painting a recording pattern, surface->ctm is transformed by the
+     * pattern to device matrix.
+     *
+     * For printing device contexts where 1 unit is 1 dpi, switching
+     * the GDI CTM to identity maximises the possible resolution of
+     * coordinates.
+     *
+     * If the device context is an EMF file, using an identity
+     * transform often provides insufficent resolution. The workaround
+     * is to set the GDI CTM to a scale < 1 eg [1.0/16 0 0 1/0/16 0 0]
+     * and scale the cairo CTM by [16 0 0 16 0 0]. The
+     * SetWorldTransform function call to scale the GDI CTM by 1.0/16
+     * will be recorded in the EMF followed by all the graphics
+     * functions by their coordinateds multiplied by 16.
+     *
+     * To support allowing the user to set a GDI CTM with scale < 1,
+     * we avoid switching to an identity CTM if the CTM xx and yy is < 1.
+     */
+    SetGraphicsMode (surface->dc, GM_ADVANCED);
+    GetWorldTransform(surface->dc, &xform);
+    if (xform.eM11 < 1 && xform.eM22 < 1) {
+       cairo_matrix_init_identity (&surface->ctm);
+       surface->gdi_ctm.xx = xform.eM11;
+       surface->gdi_ctm.xy = xform.eM21;
+       surface->gdi_ctm.yx = xform.eM12;
+       surface->gdi_ctm.yy = xform.eM22;
+       surface->gdi_ctm.x0 = xform.eDx;
+       surface->gdi_ctm.y0 = xform.eDy;
+    } else {
+       surface->ctm.xx = xform.eM11;
+       surface->ctm.xy = xform.eM21;
+       surface->ctm.yx = xform.eM12;
+       surface->ctm.yy = xform.eM22;
+       surface->ctm.x0 = xform.eDx;
+       surface->ctm.y0 = xform.eDy;
+       cairo_matrix_init_identity (&surface->gdi_ctm);
+       if (!ModifyWorldTransform (surface->dc, NULL, MWT_IDENTITY))
+           return _cairo_win32_print_gdi_error ("_cairo_win32_printing_surface_start_page:ModifyWorldTransform");
+    }
+
+    surface->has_ctm = !_cairo_matrix_is_identity (&surface->ctm);
+    surface->has_gdi_ctm = !_cairo_matrix_is_identity (&surface->gdi_ctm);
+    inverse_ctm = surface->ctm;
+    status = cairo_matrix_invert (&inverse_ctm);
+    if (status)
+       return status;
+
+    x_res = GetDeviceCaps (surface->dc, LOGPIXELSX);
+    y_res = GetDeviceCaps (surface->dc, LOGPIXELSY);
+    cairo_matrix_transform_distance (&inverse_ctm, &x_res, &y_res);
+    _cairo_surface_set_resolution (&surface->base, x_res, y_res);
+
+    SaveDC (surface->dc); /* Then save Cairo's known-good clip state, so the clip path can be reset */
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_win32_printing_surface_set_paginated_mode (void *abstract_surface,
+                                                  cairo_paginated_mode_t paginated_mode)
+{
+    cairo_win32_surface_t *surface = abstract_surface;
+
+    surface->paginated_mode = paginated_mode;
+}
+
+static cairo_bool_t
+_cairo_win32_printing_surface_supports_fine_grained_fallbacks (void *abstract_surface)
+{
+    return TRUE;
+}
+
+/**
+ * cairo_win32_printing_surface_create:
+ * @hdc: the DC to create a surface for
+ *
+ * Creates a cairo surface that targets the given DC.  The DC will be
+ * queried for its initial clip extents, and this will be used as the
+ * size of the cairo surface.  The DC should be a printing DC;
+ * antialiasing will be ignored, and GDI will be used as much as
+ * possible to draw to the surface.
+ *
+ * The returned surface will be wrapped using the paginated surface to
+ * provide correct complex rendering behaviour; cairo_surface_show_page() and
+ * associated methods must be used for correct output.
+ *
+ * Return value: the newly created surface
+ *
+ * Since: 1.6
+ **/
+cairo_surface_t *
+cairo_win32_printing_surface_create (HDC hdc)
+{
+    cairo_win32_surface_t *surface;
+    cairo_surface_t *paginated;
+    RECT rect;
+
+    surface = malloc (sizeof (cairo_win32_surface_t));
+    if (surface == NULL)
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+    if (_cairo_win32_save_initial_clip (hdc, surface) != CAIRO_STATUS_SUCCESS) {
+       free (surface);
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+    }
+
+    _cairo_surface_clipper_init (&surface->clipper,
+                                _cairo_win32_printing_surface_clipper_intersect_clip_path);
+
+    surface->image = NULL;
+    surface->format = CAIRO_FORMAT_RGB24;
+    surface->content = CAIRO_CONTENT_COLOR_ALPHA;
+
+    surface->dc = hdc;
+    surface->bitmap = NULL;
+    surface->is_dib = FALSE;
+    surface->saved_dc_bitmap = NULL;
+    surface->brush = NULL;
+    surface->old_brush = NULL;
+    surface->font_subsets = _cairo_scaled_font_subsets_create_scaled ();
+    if (surface->font_subsets == NULL) {
+       free (surface);
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+    }
+
+    GetClipBox(hdc, &rect);
+    surface->extents.x = rect.left;
+    surface->extents.y = rect.top;
+    surface->extents.width = rect.right - rect.left;
+    surface->extents.height = rect.bottom - rect.top;
+
+    surface->flags = _cairo_win32_flags_for_dc (surface->dc);
+    surface->flags |= CAIRO_WIN32_SURFACE_FOR_PRINTING;
+
+    _cairo_win32_printing_surface_init_ps_mode (surface);
+    _cairo_win32_printing_surface_init_image_support (surface);
+    _cairo_win32_printing_surface_init_language_pack (surface);
+    _cairo_surface_init (&surface->base,
+                        &cairo_win32_printing_surface_backend,
+                        NULL, /* device */
+                         CAIRO_CONTENT_COLOR_ALPHA);
+
+    paginated = _cairo_paginated_surface_create (&surface->base,
+                                                CAIRO_CONTENT_COLOR_ALPHA,
+                                                &cairo_win32_surface_paginated_backend);
+
+    /* paginated keeps the only reference to surface now, drop ours */
+    cairo_surface_destroy (&surface->base);
+
+    return paginated;
+}
+
+cairo_bool_t
+_cairo_surface_is_win32_printing (cairo_surface_t *surface)
+{
+    return surface->backend == &cairo_win32_printing_surface_backend;
+}
+
+static const cairo_surface_backend_t cairo_win32_printing_surface_backend = {
+    CAIRO_SURFACE_TYPE_WIN32_PRINTING,
+    _cairo_win32_surface_finish,
+
+    _cairo_default_context_create,
+
+    _cairo_win32_printing_surface_create_similar,
+    NULL, /* create similar image */
+    NULL, /* map to image */
+    NULL, /* unmap image */
+
+    NULL, /* acquire_source_image */
+    NULL, /* release_source_image */
+    NULL, /* snapshot */
+
+    NULL, /* copy_page */
+    _cairo_win32_printing_surface_show_page,
+
+    _cairo_win32_surface_get_extents,
+    _cairo_win32_printing_surface_get_font_options,
+
+    NULL, /* flush */
+    NULL, /* mark_dirty_rectangle */
+
+    _cairo_win32_printing_surface_paint,
+    NULL, /* mask */
+    _cairo_win32_printing_surface_stroke,
+    _cairo_win32_printing_surface_fill,
+    NULL, /* fill/stroke */
+    _cairo_win32_printing_surface_show_glyphs,
+};
+
+static const cairo_paginated_surface_backend_t cairo_win32_surface_paginated_backend = {
+    _cairo_win32_printing_surface_start_page,
+    _cairo_win32_printing_surface_set_paginated_mode,
+    NULL, /* set_bounding_box */
+    NULL, /* _cairo_win32_printing_surface_has_fallback_images, */
+    _cairo_win32_printing_surface_supports_fine_grained_fallbacks,
+};
diff --git a/src/cairo-win32-private.h b/src/cairo-win32-private.h
new file mode 100644 (file)
index 0000000..07830dc
--- /dev/null
@@ -0,0 +1,219 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2005 Red Hat, Inc
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ *     Owen Taylor <otaylor@redhat.com>
+ */
+
+#ifndef CAIRO_WIN32_PRIVATE_H
+#define CAIRO_WIN32_PRIVATE_H
+
+#include "cairo-win32.h"
+#include "cairoint.h"
+#include "cairo-surface-clipper-private.h"
+
+#ifndef SHADEBLENDCAPS
+#define SHADEBLENDCAPS 120
+#endif
+#ifndef SB_NONE
+#define SB_NONE 0
+#endif
+
+#define WIN32_FONT_LOGICAL_SCALE 32
+
+typedef struct _cairo_win32_surface {
+    cairo_surface_t base;
+
+    cairo_format_t format;
+
+    HDC dc;
+
+    /* We create off-screen surfaces as DIBs or DDBs, based on what we created
+     * originally*/
+    HBITMAP bitmap;
+    cairo_bool_t is_dib;
+
+    /* Used to save the initial 1x1 monochrome bitmap for the DC to
+     * select back into the DC before deleting the DC and our
+     * bitmap. For Windows XP, this doesn't seem to be necessary
+     * ... we can just delete the DC and that automatically unselects
+     * out bitmap. But it's standard practice so apparently is needed
+     * on some versions of Windows.
+     */
+    HBITMAP saved_dc_bitmap;
+
+    cairo_surface_t *image;
+
+    /* We use the x and y parts of extents for situations where
+     * we're not supposed to draw to the entire surface.
+     * For example, during a paint event a program will get
+     * a DC that has been clipped to the dirty region.
+     * A cairo surface constructed for that DC will have extents
+     * that match bounds of the clipped region.
+     *
+     * jrmuizel: I'm not sure if storing these extents instead
+     * of just using the size is better... */
+    cairo_rectangle_int_t extents;
+
+    /* Initial clip bits
+     * We need these kept around so that we maintain
+     * whatever clip was set on the original DC at creation
+     * time when cairo is asked to reset the surface clip.
+     */
+    cairo_rectangle_int_t clip_rect;
+    HRGN initial_clip_rgn;
+    cairo_bool_t had_simple_clip;
+    cairo_region_t *clip_region;
+
+    /* For path clipping to the printing-surface */
+    cairo_surface_clipper_t clipper;
+
+    /* Surface DC flags */
+    uint32_t flags;
+
+    /* printing surface bits */
+    cairo_paginated_mode_t paginated_mode;
+    cairo_content_t content;
+    cairo_bool_t path_empty;
+    cairo_bool_t has_ctm;
+    cairo_matrix_t ctm;
+    cairo_bool_t has_gdi_ctm;
+    cairo_matrix_t gdi_ctm;
+    HBRUSH brush, old_brush;
+    cairo_scaled_font_subsets_t *font_subsets;
+} cairo_win32_surface_t;
+
+/* Surface DC flag values */
+enum {
+    /* If this is a surface created for printing or not */
+    CAIRO_WIN32_SURFACE_FOR_PRINTING = (1<<0),
+
+    /* Whether the DC is a display DC or not */
+    CAIRO_WIN32_SURFACE_IS_DISPLAY = (1<<1),
+
+    /* Whether we can use BitBlt with this surface */
+    CAIRO_WIN32_SURFACE_CAN_BITBLT = (1<<2),
+
+    /* Whether we can use AlphaBlend with this surface */
+    CAIRO_WIN32_SURFACE_CAN_ALPHABLEND = (1<<3),
+
+    /* Whether we can use StretchBlt with this surface */
+    CAIRO_WIN32_SURFACE_CAN_STRETCHBLT = (1<<4),
+
+    /* Whether we can use StretchDIBits with this surface */
+    CAIRO_WIN32_SURFACE_CAN_STRETCHDIB = (1<<5),
+
+    /* Whether we can use GradientFill rectangles with this surface */
+    CAIRO_WIN32_SURFACE_CAN_RECT_GRADIENT = (1<<6),
+
+    /* Whether we can use the CHECKJPEGFORMAT escape function */
+    CAIRO_WIN32_SURFACE_CAN_CHECK_JPEG = (1<<7),
+
+    /* Whether we can use the CHECKJPEGFORMAT escape function */
+    CAIRO_WIN32_SURFACE_CAN_CHECK_PNG = (1<<8),
+};
+
+cairo_status_t
+_cairo_win32_print_gdi_error (const char *context);
+
+cairo_bool_t
+_cairo_surface_is_win32 (cairo_surface_t *surface);
+
+cairo_bool_t
+_cairo_surface_is_win32_printing (cairo_surface_t *surface);
+
+cairo_status_t
+_cairo_win32_surface_finish (void *abstract_surface);
+
+cairo_bool_t
+_cairo_win32_surface_get_extents (void                   *abstract_surface,
+                                 cairo_rectangle_int_t   *rectangle);
+
+uint32_t
+_cairo_win32_flags_for_dc (HDC dc);
+
+cairo_status_t
+_cairo_win32_surface_set_clip_region (void           *abstract_surface,
+                                     cairo_region_t *region);
+
+cairo_int_status_t
+_cairo_win32_surface_show_glyphs_internal (void                         *surface,
+                                          cairo_operator_t       op,
+                                          const cairo_pattern_t *source,
+                                          cairo_glyph_t         *glyphs,
+                                          int                    num_glyphs,
+                                          cairo_scaled_font_t   *scaled_font,
+                                          const cairo_clip_t    *clip,
+                                          cairo_bool_t           glyph_indices);
+
+cairo_int_status_t
+_cairo_win32_surface_show_glyphs (void                 *surface,
+                                 cairo_operator_t       op,
+                                 const cairo_pattern_t *source,
+                                 cairo_glyph_t         *glyphs,
+                                 int                    num_glyphs,
+                                 cairo_scaled_font_t   *scaled_font,
+                                 const cairo_clip_t    *clip);
+
+cairo_surface_t *
+_cairo_win32_surface_create_similar (void          *abstract_src,
+                                    cairo_content_t content,
+                                    int             width,
+                                    int             height);
+
+static inline void
+_cairo_matrix_to_win32_xform (const cairo_matrix_t *m,
+                              XFORM *xform)
+{
+    xform->eM11 = (FLOAT) m->xx;
+    xform->eM21 = (FLOAT) m->xy;
+    xform->eM12 = (FLOAT) m->yx;
+    xform->eM22 = (FLOAT) m->yy;
+    xform->eDx = (FLOAT) m->x0;
+    xform->eDy = (FLOAT) m->y0;
+}
+
+cairo_int_status_t
+_cairo_win32_save_initial_clip (HDC dc, cairo_win32_surface_t *surface);
+
+cairo_int_status_t
+_cairo_win32_restore_initial_clip (cairo_win32_surface_t *surface);
+
+void
+_cairo_win32_debug_dump_hrgn (HRGN rgn, char *header);
+
+cairo_bool_t
+_cairo_win32_scaled_font_is_type1 (cairo_scaled_font_t *scaled_font);
+
+cairo_bool_t
+_cairo_win32_scaled_font_is_bitmap (cairo_scaled_font_t *scaled_font);
+
+#endif /* CAIRO_WIN32_PRIVATE_H */
diff --git a/src/cairo-win32-surface.c b/src/cairo-win32-surface.c
new file mode 100644 (file)
index 0000000..c7d3216
--- /dev/null
@@ -0,0 +1,2097 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* Cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ *     Owen Taylor <otaylor@redhat.com>
+ *     Stuart Parmenter <stuart@mozilla.com>
+ *     Vladimir Vukicevic <vladimir@pobox.com>
+ */
+
+#define WIN32_LEAN_AND_MEAN
+/* We require Windows 2000 features such as ETO_PDY */
+#if !defined(WINVER) || (WINVER < 0x0500)
+# define WINVER 0x0500
+#endif
+#if !defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x0500)
+# define _WIN32_WINNT 0x0500
+#endif
+
+#include "cairoint.h"
+
+#include "cairo-clip-private.h"
+#include "cairo-composite-rectangles-private.h"
+#include "cairo-default-context-private.h"
+#include "cairo-error-private.h"
+#include "cairo-image-surface-private.h"
+#include "cairo-paginated-private.h"
+#include "cairo-pattern-private.h"
+#include "cairo-win32-private.h"
+#include "cairo-scaled-font-subsets-private.h"
+#include "cairo-surface-fallback-private.h"
+#include "cairo-surface-backend-private.h"
+
+#include <wchar.h>
+#include <windows.h>
+
+#if defined(__MINGW32__) && !defined(ETO_PDY)
+# define ETO_PDY 0x2000
+#endif
+
+#undef DEBUG_COMPOSITE
+
+/* for older SDKs */
+#ifndef SHADEBLENDCAPS
+#define SHADEBLENDCAPS  120
+#endif
+#ifndef SB_NONE
+#define SB_NONE         0x00000000
+#endif
+
+#define PELS_72DPI  ((LONG)(72. / 0.0254))
+
+/**
+ * SECTION:cairo-win32
+ * @Title: Win32 Surfaces
+ * @Short_Description: Microsoft Windows surface support
+ * @See_Also: #cairo_surface_t
+ *
+ * The Microsoft Windows surface is used to render cairo graphics to
+ * Microsoft Windows windows, bitmaps, and printing device contexts.
+ *
+ * The surface returned by cairo_win32_printing_surface_create() is of surface
+ * type %CAIRO_SURFACE_TYPE_WIN32_PRINTING and is a multi-page vector surface
+ * type.
+ *
+ * The surface returned by the other win32 constructors is of surface type
+ * %CAIRO_SURFACE_TYPE_WIN32 and is a raster surface type.
+ */
+
+/**
+ * CAIRO_HAS_WIN32_SURFACE:
+ *
+ * Defined if the Microsoft Windows surface backend is available.
+ * This macro can be used to conditionally compile backend-specific code.
+ */
+
+static const cairo_surface_backend_t cairo_win32_surface_backend;
+
+/**
+ * _cairo_win32_print_gdi_error:
+ * @context: context string to display along with the error
+ *
+ * Helper function to dump out a human readable form of the
+ * current error code.
+ *
+ * Return value: A cairo status code for the error code
+ **/
+cairo_status_t
+_cairo_win32_print_gdi_error (const char *context)
+{
+    void *lpMsgBuf;
+    DWORD last_error = GetLastError ();
+
+    if (!FormatMessageW (FORMAT_MESSAGE_ALLOCATE_BUFFER |
+                        FORMAT_MESSAGE_FROM_SYSTEM,
+                        NULL,
+                        last_error,
+                        MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
+                        (LPWSTR) &lpMsgBuf,
+                        0, NULL)) {
+       fprintf (stderr, "%s: Unknown GDI error", context);
+    } else {
+       fprintf (stderr, "%s: %S", context, (wchar_t *)lpMsgBuf);
+
+       LocalFree (lpMsgBuf);
+    }
+
+    fflush (stderr);
+
+    /* We should switch off of last_status, but we'd either return
+     * CAIRO_STATUS_NO_MEMORY or CAIRO_STATUS_UNKNOWN_ERROR and there
+     * is no CAIRO_STATUS_UNKNOWN_ERROR.
+     */
+
+    return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+}
+
+uint32_t
+_cairo_win32_flags_for_dc (HDC dc)
+{
+    uint32_t flags = 0;
+
+    if (GetDeviceCaps(dc, TECHNOLOGY) == DT_RASDISPLAY) {
+       flags |= CAIRO_WIN32_SURFACE_IS_DISPLAY;
+
+       /* These will always be possible, but the actual GetDeviceCaps
+        * calls will return whether they're accelerated or not.
+        * We may want to use our own (pixman) routines sometimes
+        * if they're eventually faster, but for now have GDI do
+        * everything.
+        */
+       flags |= CAIRO_WIN32_SURFACE_CAN_BITBLT;
+       flags |= CAIRO_WIN32_SURFACE_CAN_ALPHABLEND;
+       flags |= CAIRO_WIN32_SURFACE_CAN_STRETCHBLT;
+       flags |= CAIRO_WIN32_SURFACE_CAN_STRETCHDIB;
+    } else {
+       int cap;
+
+       cap = GetDeviceCaps(dc, SHADEBLENDCAPS);
+       if (cap != SB_NONE)
+           flags |= CAIRO_WIN32_SURFACE_CAN_ALPHABLEND;
+
+       cap = GetDeviceCaps(dc, RASTERCAPS);
+       if (cap & RC_BITBLT)
+           flags |= CAIRO_WIN32_SURFACE_CAN_BITBLT;
+       if (cap & RC_STRETCHBLT)
+           flags |= CAIRO_WIN32_SURFACE_CAN_STRETCHBLT;
+       if (cap & RC_STRETCHDIB)
+           flags |= CAIRO_WIN32_SURFACE_CAN_STRETCHDIB;
+    }
+
+    return flags;
+}
+
+static cairo_status_t
+_create_dc_and_bitmap (cairo_win32_surface_t *surface,
+                      HDC                    original_dc,
+                      cairo_format_t         format,
+                      int                    width,
+                      int                    height,
+                      unsigned char        **bits_out,
+                      int                   *rowstride_out)
+{
+    cairo_status_t status;
+
+    BITMAPINFO *bitmap_info = NULL;
+    struct {
+       BITMAPINFOHEADER bmiHeader;
+       RGBQUAD bmiColors[2];
+    } bmi_stack;
+    void *bits;
+
+    int num_palette = 0;       /* Quiet GCC */
+    int i;
+
+    surface->dc = NULL;
+    surface->bitmap = NULL;
+    surface->is_dib = FALSE;
+
+    switch (format) {
+    default:
+    case CAIRO_FORMAT_INVALID:
+       return _cairo_error (CAIRO_STATUS_INVALID_FORMAT);
+    case CAIRO_FORMAT_ARGB32:
+    case CAIRO_FORMAT_RGB24:
+       num_palette = 0;
+       break;
+
+    case CAIRO_FORMAT_A8:
+       num_palette = 256;
+       break;
+
+    case CAIRO_FORMAT_A1:
+       num_palette = 2;
+       break;
+    }
+
+    if (num_palette > 2) {
+       bitmap_info = _cairo_malloc_ab_plus_c (num_palette, sizeof(RGBQUAD), sizeof(BITMAPINFOHEADER));
+       if (!bitmap_info)
+           return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+    } else {
+       bitmap_info = (BITMAPINFO *)&bmi_stack;
+    }
+
+    bitmap_info->bmiHeader.biSize = sizeof (BITMAPINFOHEADER);
+    bitmap_info->bmiHeader.biWidth = width == 0 ? 1 : width;
+    bitmap_info->bmiHeader.biHeight = height == 0 ? -1 : - height; /* top-down */
+    bitmap_info->bmiHeader.biSizeImage = 0;
+    bitmap_info->bmiHeader.biXPelsPerMeter = PELS_72DPI; /* unused here */
+    bitmap_info->bmiHeader.biYPelsPerMeter = PELS_72DPI; /* unused here */
+    bitmap_info->bmiHeader.biPlanes = 1;
+
+    switch (format) {
+    /* We can't create real RGB24 bitmaps because something seems to
+     * break if we do, especially if we don't set up an image
+     * fallback.  It could be a bug with using a 24bpp pixman image
+     * (and creating one with masks).  So treat them like 32bpp.
+     * Note: This causes problems when using BitBlt/AlphaBlend/etc!
+     * see end of file.
+     */
+    case CAIRO_FORMAT_RGB24:
+    case CAIRO_FORMAT_ARGB32:
+       bitmap_info->bmiHeader.biBitCount = 32;
+       bitmap_info->bmiHeader.biCompression = BI_RGB;
+       bitmap_info->bmiHeader.biClrUsed = 0;   /* unused */
+       bitmap_info->bmiHeader.biClrImportant = 0;
+       break;
+
+    case CAIRO_FORMAT_A8:
+       bitmap_info->bmiHeader.biBitCount = 8;
+       bitmap_info->bmiHeader.biCompression = BI_RGB;
+       bitmap_info->bmiHeader.biClrUsed = 256;
+       bitmap_info->bmiHeader.biClrImportant = 0;
+
+       for (i = 0; i < 256; i++) {
+           bitmap_info->bmiColors[i].rgbBlue = i;
+           bitmap_info->bmiColors[i].rgbGreen = i;
+           bitmap_info->bmiColors[i].rgbRed = i;
+           bitmap_info->bmiColors[i].rgbReserved = 0;
+       }
+
+       break;
+
+    case CAIRO_FORMAT_A1:
+       bitmap_info->bmiHeader.biBitCount = 1;
+       bitmap_info->bmiHeader.biCompression = BI_RGB;
+       bitmap_info->bmiHeader.biClrUsed = 2;
+       bitmap_info->bmiHeader.biClrImportant = 0;
+
+       for (i = 0; i < 2; i++) {
+           bitmap_info->bmiColors[i].rgbBlue = i * 255;
+           bitmap_info->bmiColors[i].rgbGreen = i * 255;
+           bitmap_info->bmiColors[i].rgbRed = i * 255;
+           bitmap_info->bmiColors[i].rgbReserved = 0;
+       }
+
+       break;
+    }
+
+    surface->dc = CreateCompatibleDC (original_dc);
+    if (!surface->dc)
+       goto FAIL;
+
+    surface->bitmap = CreateDIBSection (surface->dc,
+                                       bitmap_info,
+                                       DIB_RGB_COLORS,
+                                       &bits,
+                                       NULL, 0);
+    if (!surface->bitmap)
+       goto FAIL;
+
+    surface->is_dib = TRUE;
+
+    GdiFlush();
+
+    surface->saved_dc_bitmap = SelectObject (surface->dc,
+                                            surface->bitmap);
+    if (!surface->saved_dc_bitmap)
+       goto FAIL;
+
+    if (bitmap_info && num_palette > 2)
+       free (bitmap_info);
+
+    if (bits_out)
+       *bits_out = bits;
+
+    if (rowstride_out) {
+       /* Windows bitmaps are padded to 32-bit (dword) boundaries */
+       switch (format) {
+       case CAIRO_FORMAT_ARGB32:
+       case CAIRO_FORMAT_RGB24:
+           *rowstride_out = 4 * width;
+           break;
+
+       case CAIRO_FORMAT_A8:
+           *rowstride_out = (width + 3) & ~3;
+           break;
+
+       case CAIRO_FORMAT_A1:
+           *rowstride_out = ((width + 31) & ~31) / 8;
+           break;
+       }
+    }
+
+    surface->flags = _cairo_win32_flags_for_dc (surface->dc);
+
+    return CAIRO_STATUS_SUCCESS;
+
+ FAIL:
+    status = _cairo_win32_print_gdi_error ("_create_dc_and_bitmap");
+
+    if (bitmap_info && num_palette > 2)
+       free (bitmap_info);
+
+    if (surface->saved_dc_bitmap) {
+       SelectObject (surface->dc, surface->saved_dc_bitmap);
+       surface->saved_dc_bitmap = NULL;
+    }
+
+    if (surface->bitmap) {
+       DeleteObject (surface->bitmap);
+       surface->bitmap = NULL;
+    }
+
+    if (surface->dc) {
+       DeleteDC (surface->dc);
+       surface->dc = NULL;
+    }
+
+    return status;
+}
+
+static cairo_surface_t *
+_cairo_win32_surface_create_for_dc (HDC             original_dc,
+                                   cairo_format_t  format,
+                                   int             width,
+                                   int             height)
+{
+    cairo_status_t status;
+    cairo_win32_surface_t *surface;
+    unsigned char *bits;
+    int rowstride;
+
+    if (! CAIRO_FORMAT_VALID (format))
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
+
+    surface = malloc (sizeof (cairo_win32_surface_t));
+    if (surface == NULL)
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+    surface->clip_region = NULL;
+
+    status = _create_dc_and_bitmap (surface, original_dc, format,
+                                   width, height,
+                                   &bits, &rowstride);
+    if (status)
+       goto FAIL;
+
+    surface->image = cairo_image_surface_create_for_data (bits, format,
+                                                         width, height, rowstride);
+    status = surface->image->status;
+    if (status)
+       goto FAIL;
+
+    surface->format = format;
+
+    surface->clip_rect.x = 0;
+    surface->clip_rect.y = 0;
+    surface->clip_rect.width = width;
+    surface->clip_rect.height = height;
+
+    surface->initial_clip_rgn = NULL;
+    surface->had_simple_clip = FALSE;
+
+    surface->extents = surface->clip_rect;
+    surface->font_subsets = NULL;
+
+    _cairo_surface_init (&surface->base,
+                        &cairo_win32_surface_backend,
+                        NULL, /* device */
+                        _cairo_content_from_format (format));
+
+    return &surface->base;
+
+ FAIL:
+    if (surface->bitmap) {
+       SelectObject (surface->dc, surface->saved_dc_bitmap);
+       DeleteObject (surface->bitmap);
+       DeleteDC (surface->dc);
+    }
+    free (surface);
+
+    return _cairo_surface_create_in_error (status);
+}
+
+static cairo_surface_t *
+_cairo_win32_surface_create_similar_internal (void         *abstract_src,
+                                             cairo_content_t content,
+                                             int            width,
+                                             int            height,
+                                             cairo_bool_t   force_dib)
+{
+    cairo_win32_surface_t *src = abstract_src;
+    cairo_format_t format = _cairo_format_from_content (content);
+    cairo_surface_t *new_surf = NULL;
+
+    /* We force a DIB always if:
+     * - we need alpha; or
+     * - the parent is a DIB; or
+     * - the parent is for printing (because we don't care about the bit depth at that point)
+     *
+     * We also might end up with a DIB even if a DDB is requested if DDB creation failed
+     * due to out of memory.
+     */
+    if (src->is_dib ||
+       (content & CAIRO_CONTENT_ALPHA) ||
+       src->base.backend->type == CAIRO_SURFACE_TYPE_WIN32_PRINTING)
+    {
+       force_dib = TRUE;
+    }
+
+    if (!force_dib) {
+       /* try to create a ddb */
+       new_surf = cairo_win32_surface_create_with_ddb (src->dc, CAIRO_FORMAT_RGB24, width, height);
+
+       if (new_surf->status != CAIRO_STATUS_SUCCESS)
+           new_surf = NULL;
+    }
+
+    if (new_surf == NULL) {
+       new_surf = _cairo_win32_surface_create_for_dc (src->dc, format, width, height);
+    }
+
+    return new_surf;
+}
+
+cairo_surface_t *
+_cairo_win32_surface_create_similar (void          *abstract_src,
+                                    cairo_content_t content,
+                                    int             width,
+                                    int             height)
+{
+    return _cairo_win32_surface_create_similar_internal (abstract_src, content, width, height, FALSE);
+}
+
+cairo_status_t
+_cairo_win32_surface_finish (void *abstract_surface)
+{
+    cairo_win32_surface_t *surface = abstract_surface;
+
+    if (surface->image)
+       cairo_surface_destroy (surface->image);
+
+    /* If we created the Bitmap and DC, destroy them */
+    if (surface->bitmap) {
+       SelectObject (surface->dc, surface->saved_dc_bitmap);
+       DeleteObject (surface->bitmap);
+       DeleteDC (surface->dc);
+    } else {
+       _cairo_win32_restore_initial_clip (surface);
+    }
+
+    if (surface->initial_clip_rgn)
+       DeleteObject (surface->initial_clip_rgn);
+
+    if (surface->font_subsets != NULL)
+       _cairo_scaled_font_subsets_destroy (surface->font_subsets);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_win32_surface_get_subimage (cairo_win32_surface_t  *surface,
+                                  int                     x,
+                                  int                     y,
+                                  int                     width,
+                                  int                     height,
+                                  cairo_win32_surface_t **local_out)
+{
+    cairo_win32_surface_t *local;
+    cairo_int_status_t status;
+    cairo_content_t content = _cairo_content_from_format (surface->format);
+
+    local =
+       (cairo_win32_surface_t *) _cairo_win32_surface_create_similar_internal
+       (surface, content, width, height, TRUE);
+    if (local == NULL)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+    if (local->base.status)
+       return local->base.status;
+
+    status = CAIRO_INT_STATUS_UNSUPPORTED;
+
+    /* Only BitBlt if the source surface supports it. */
+    if ((surface->flags & CAIRO_WIN32_SURFACE_CAN_BITBLT) &&
+       BitBlt (local->dc,
+               0, 0,
+               width, height,
+               surface->dc,
+               x, y,
+               SRCCOPY))
+    {
+       status = CAIRO_STATUS_SUCCESS;
+    }
+
+    if (status) {
+       /* If we failed here, most likely the source or dest doesn't
+        * support BitBlt/AlphaBlend (e.g. a printer).
+        * You can't reliably get bits from a printer DC, so just fill in
+        * the surface as white (common case for printing).
+        */
+
+       RECT r;
+       r.left = r.top = 0;
+       r.right = width;
+       r.bottom = height;
+       FillRect(local->dc, &r, (HBRUSH)GetStockObject(WHITE_BRUSH));
+    }
+
+    *local_out = local;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_surface_t *
+_cairo_win32_surface_map_to_image (void                    *abstract_surface,
+                                  const cairo_rectangle_int_t   *extents)
+{
+    cairo_win32_surface_t *surface = abstract_surface;
+    cairo_win32_surface_t *local = NULL;
+    cairo_status_t status;
+
+    if (surface->image) {
+       GdiFlush();
+       return _cairo_surface_create_for_rectangle_int (surface->image,
+                                                       extents);
+    }
+
+    status = _cairo_win32_surface_get_subimage (abstract_surface,
+                                               extents->x,
+                                               extents->y,
+                                               extents->width,
+                                               extents->height,
+                                               &local);
+    if (unlikely (status))
+       return _cairo_surface_create_in_error (status);
+
+    status = cairo_surface_set_user_data (local->image,
+                                         (const cairo_user_data_key_t *)surface->image,
+                                         local, NULL);
+    if (unlikely (status)) {
+       cairo_surface_destroy (&local->base);
+       return _cairo_surface_create_in_error (status);
+    }
+
+    cairo_surface_set_device_offset (local->image, -extents->x, -extents->y);
+    return local->image;
+}
+
+static cairo_int_status_t
+_cairo_win32_surface_unmap_image (void                    *abstract_surface,
+                                 cairo_image_surface_t   *image)
+{
+    cairo_win32_surface_t *surface = abstract_surface;
+    cairo_win32_surface_t *local;
+
+    local = cairo_surface_get_user_data (&image->base,
+                                        (const cairo_user_data_key_t *) surface->image);
+    if (!local)
+       return CAIRO_INT_STATUS_SUCCESS;
+
+    if (!BitBlt (surface->dc,
+                image->base.device_transform.x0,
+                image->base.device_transform.y0,
+                image->width, image->height,
+                local->dc,
+                0, 0,
+                SRCCOPY))
+       _cairo_win32_print_gdi_error ("_cairo_win32_surface_release_dest_image");
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_win32_surface_acquire_source_image (void                    *abstract_surface,
+                                          cairo_image_surface_t  **image_out,
+                                          void                   **image_extra)
+{
+    cairo_win32_surface_t *surface = abstract_surface;
+    cairo_win32_surface_t *local;
+    cairo_status_t status;
+
+    if (surface->image) {
+       *image_out = (cairo_image_surface_t *)surface->image;
+       *image_extra = NULL;
+       return CAIRO_STATUS_SUCCESS;
+    }
+
+    status = _cairo_win32_surface_get_subimage (abstract_surface, 0, 0,
+                                               surface->extents.width,
+                                               surface->extents.height, &local);
+    if (status)
+       return status;
+
+    *image_out = (cairo_image_surface_t *)local->image;
+    *image_extra = local;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_win32_surface_release_source_image (void                   *abstract_surface,
+                                          cairo_image_surface_t  *image,
+                                          void                   *image_extra)
+{
+    cairo_win32_surface_t *local = image_extra;
+
+    if (local)
+       cairo_surface_destroy ((cairo_surface_t *)local);
+}
+
+cairo_status_t
+_cairo_win32_surface_set_clip_region (void           *abstract_surface,
+                                     cairo_region_t *region)
+{
+    cairo_win32_surface_t *surface = abstract_surface;
+    cairo_status_t status = CAIRO_STATUS_SUCCESS;
+
+    if (surface->clip_region == region)
+       return CAIRO_STATUS_SUCCESS;
+
+    cairo_region_destroy (surface->clip_region);
+    surface->clip_region = cairo_region_reference (region);
+
+    /* The semantics we want is that any clip set by cairo combines
+     * is intersected with the clip on device context that the
+     * surface was created for. To implement this, we need to
+     * save the original clip when first setting a clip on surface.
+     */
+
+    /* Clear any clip set by cairo, return to the original first */
+    status = _cairo_win32_restore_initial_clip (surface);
+
+    /* Then combine any new region with it */
+    if (region) {
+       cairo_rectangle_int_t extents;
+       int num_rects;
+       RGNDATA *data;
+       size_t data_size;
+       RECT *rects;
+       int i;
+       HRGN gdi_region;
+
+       /* Create a GDI region for the cairo region */
+
+       cairo_region_get_extents (region, &extents);
+       num_rects = cairo_region_num_rectangles (region);
+       /* XXX see notes in _cairo_win32_save_initial_clip --
+        * this code will interact badly with a HDC which had an initial
+        * world transform -- we should probably manually transform the
+        * region rects, because SelectClipRgn takes device units, not
+        * logical units (unlike IntersectClipRect).
+        */
+
+       data_size = sizeof (RGNDATAHEADER) + num_rects * sizeof (RECT);
+       data = malloc (data_size);
+       if (!data)
+           return _cairo_error(CAIRO_STATUS_NO_MEMORY);
+       rects = (RECT *)data->Buffer;
+
+       data->rdh.dwSize = sizeof (RGNDATAHEADER);
+       data->rdh.iType = RDH_RECTANGLES;
+       data->rdh.nCount = num_rects;
+       data->rdh.nRgnSize = num_rects * sizeof (RECT);
+       data->rdh.rcBound.left = extents.x;
+       data->rdh.rcBound.top = extents.y;
+       data->rdh.rcBound.right = extents.x + extents.width;
+       data->rdh.rcBound.bottom = extents.y + extents.height;
+
+       for (i = 0; i < num_rects; i++) {
+           cairo_rectangle_int_t rect;
+
+           cairo_region_get_rectangle (region, i, &rect);
+
+           rects[i].left   = rect.x;
+           rects[i].top    = rect.y;
+           rects[i].right  = rect.x + rect.width;
+           rects[i].bottom = rect.y + rect.height;
+       }
+
+       gdi_region = ExtCreateRegion (NULL, data_size, data);
+       free (data);
+
+       if (!gdi_region)
+           return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+       /* AND the new region into our DC */
+       if (ExtSelectClipRgn (surface->dc, gdi_region, RGN_AND) == ERROR)
+           status = _cairo_win32_print_gdi_error ("_cairo_win32_surface_set_clip_region");
+
+       DeleteObject (gdi_region);
+    }
+
+    return status;
+}
+
+#if !defined(AC_SRC_OVER)
+#define AC_SRC_OVER                 0x00
+#pragma pack(1)
+typedef struct {
+    BYTE   BlendOp;
+    BYTE   BlendFlags;
+    BYTE   SourceConstantAlpha;
+    BYTE   AlphaFormat;
+}BLENDFUNCTION;
+#pragma pack()
+#endif
+
+/* for compatibility with VC++ 6 */
+#ifndef AC_SRC_ALPHA
+#define AC_SRC_ALPHA                0x01
+#endif
+
+typedef BOOL (WINAPI *cairo_alpha_blend_func_t) (HDC hdcDest,
+                                                int nXOriginDest,
+                                                int nYOriginDest,
+                                                int nWidthDest,
+                                                int hHeightDest,
+                                                HDC hdcSrc,
+                                                int nXOriginSrc,
+                                                int nYOriginSrc,
+                                                int nWidthSrc,
+                                                int nHeightSrc,
+                                                BLENDFUNCTION blendFunction);
+
+static cairo_int_status_t
+_composite_alpha_blend (cairo_win32_surface_t *dst,
+                       cairo_win32_surface_t *src,
+                       int                    alpha,
+                       int                    src_x,
+                       int                    src_y,
+                       int                    src_w,
+                       int                    src_h,
+                       int                    dst_x,
+                       int                    dst_y,
+                       int                    dst_w,
+                       int                    dst_h)
+{
+    static unsigned alpha_blend_checked = FALSE;
+    static cairo_alpha_blend_func_t alpha_blend = NULL;
+
+    BLENDFUNCTION blend_function;
+
+    /* Check for AlphaBlend dynamically to allow compiling on
+     * MSVC 6 and use on older windows versions
+     */
+    if (!alpha_blend_checked) {
+       OSVERSIONINFO os;
+
+       os.dwOSVersionInfoSize = sizeof (os);
+       GetVersionEx (&os);
+
+       /* If running on Win98, disable using AlphaBlend()
+        * to avoid Win98 AlphaBlend() bug */
+       if (VER_PLATFORM_WIN32_WINDOWS != os.dwPlatformId ||
+           os.dwMajorVersion != 4 || os.dwMinorVersion != 10)
+       {
+           HMODULE msimg32_dll = LoadLibraryW (L"msimg32");
+
+           if (msimg32_dll != NULL)
+               alpha_blend = (cairo_alpha_blend_func_t)GetProcAddress (msimg32_dll,
+                                                                       "AlphaBlend");
+       }
+
+       alpha_blend_checked = TRUE;
+    }
+
+    if (alpha_blend == NULL)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+    if (!(dst->flags & CAIRO_WIN32_SURFACE_CAN_ALPHABLEND))
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+    if (src->format == CAIRO_FORMAT_RGB24 && dst->format == CAIRO_FORMAT_ARGB32)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    blend_function.BlendOp = AC_SRC_OVER;
+    blend_function.BlendFlags = 0;
+    blend_function.SourceConstantAlpha = alpha;
+    blend_function.AlphaFormat = (src->format == CAIRO_FORMAT_ARGB32) ? AC_SRC_ALPHA : 0;
+
+    if (!alpha_blend (dst->dc,
+                     dst_x, dst_y,
+                     dst_w, dst_h,
+                     src->dc,
+                     src_x, src_y,
+                     src_w, src_h,
+                     blend_function))
+       return _cairo_win32_print_gdi_error ("_cairo_win32_surface_composite(AlphaBlend)");
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_win32_surface_composite_inner (cairo_win32_surface_t *src,
+                                     cairo_image_surface_t *src_image,
+                                     cairo_win32_surface_t *dst,
+                                     cairo_rectangle_int_t src_extents,
+                                     cairo_rectangle_int_t src_r,
+                                     cairo_rectangle_int_t dst_r,
+                                     int alpha,
+                                     cairo_bool_t needs_alpha,
+                                     cairo_bool_t needs_scale)
+{
+    /* Then do BitBlt, StretchDIBits, StretchBlt, AlphaBlend, or MaskBlt */
+    if (src_image) {
+       if (needs_alpha || needs_scale)
+           return CAIRO_INT_STATUS_UNSUPPORTED;
+
+       if (dst->flags & CAIRO_WIN32_SURFACE_CAN_STRETCHBLT) {
+           BITMAPINFO bi;
+           bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+           bi.bmiHeader.biWidth = src_image->width;
+           bi.bmiHeader.biHeight = - src_image->height;
+           bi.bmiHeader.biSizeImage = 0;
+           bi.bmiHeader.biXPelsPerMeter = PELS_72DPI;
+           bi.bmiHeader.biYPelsPerMeter = PELS_72DPI;
+           bi.bmiHeader.biPlanes = 1;
+           bi.bmiHeader.biBitCount = 32;
+           bi.bmiHeader.biCompression = BI_RGB;
+           bi.bmiHeader.biClrUsed = 0;
+           bi.bmiHeader.biClrImportant = 0;
+
+           /* StretchDIBits is broken with top-down dibs; you need to do some
+            * special munging to make the coordinate space work (basically,
+            * need to address everything based on the bottom left, instead of top left,
+            * and need to tell it to flip the resulting image.
+            *
+            * See http://blog.vlad1.com/archives/2006/10/26/134/ and comments.
+            */
+           if (!StretchDIBits (dst->dc,
+                               /* dst x,y,w,h */
+                               dst_r.x, dst_r.y + dst_r.height - 1,
+                               dst_r.width, - (int) dst_r.height,
+                               /* src x,y,w,h */
+                               src_r.x, src_extents.height - src_r.y + 1,
+                               src_r.width, - (int) src_r.height,
+                               src_image->data,
+                               &bi,
+                               DIB_RGB_COLORS,
+                               SRCCOPY))
+               return _cairo_win32_print_gdi_error ("_cairo_win32_surface_composite(StretchDIBits)");
+       }
+    } else if (!needs_alpha) {
+       /* BitBlt or StretchBlt? */
+       if (!needs_scale && (dst->flags & CAIRO_WIN32_SURFACE_CAN_BITBLT)) {
+           if (!BitBlt (dst->dc,
+                        dst_r.x, dst_r.y,
+                        dst_r.width, dst_r.height,
+                        src->dc,
+                        src_r.x, src_r.y,
+                        SRCCOPY))
+               return _cairo_win32_print_gdi_error ("_cairo_win32_surface_composite(BitBlt)");
+       } else if (dst->flags & CAIRO_WIN32_SURFACE_CAN_STRETCHBLT) {
+           /* StretchBlt? */
+           /* XXX check if we want HALFTONE, based on the src filter */
+           BOOL success;
+           int oldmode = SetStretchBltMode(dst->dc, HALFTONE);
+           success = StretchBlt(dst->dc,
+                                dst_r.x, dst_r.y,
+                                dst_r.width, dst_r.height,
+                                src->dc,
+                                src_r.x, src_r.y,
+                                src_r.width, src_r.height,
+                                SRCCOPY);
+           SetStretchBltMode(dst->dc, oldmode);
+
+           if (!success)
+               return _cairo_win32_print_gdi_error ("StretchBlt");
+       }
+    } else if (needs_alpha && !needs_scale) {
+       return _composite_alpha_blend (dst, src, alpha,
+                                      src_r.x, src_r.y, src_r.width, src_r.height,
+                                      dst_r.x, dst_r.y, dst_r.width, dst_r.height);
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+/* from pixman-private.h */
+#define MOD(a,b) ((a) < 0 ? ((b) - ((-(a) - 1) % (b))) - 1 : (a) % (b))
+
+static cairo_int_status_t
+_cairo_win32_surface_composite (cairo_operator_t       op,
+                               const cairo_pattern_t   *pattern,
+                               const cairo_pattern_t   *mask_pattern,
+                               void                    *abstract_dst,
+                               int                     src_x,
+                               int                     src_y,
+                               int                     mask_x,
+                               int                     mask_y,
+                               int                     dst_x,
+                               int                     dst_y,
+                               unsigned int            width,
+                               unsigned int            height,
+                               cairo_region_t         *clip_region)
+{
+    cairo_win32_surface_t *dst = abstract_dst;
+    cairo_win32_surface_t *src;
+    cairo_surface_pattern_t *src_surface_pattern;
+    int alpha;
+    double scalex, scaley;
+    cairo_fixed_t x0_fixed, y0_fixed;
+    cairo_int_status_t status;
+
+    cairo_bool_t needs_alpha, needs_scale, needs_repeat;
+    cairo_image_surface_t *src_image = NULL;
+
+    cairo_format_t src_format;
+    cairo_rectangle_int_t src_extents;
+
+    cairo_rectangle_int_t src_r = { src_x, src_y, width, height };
+    cairo_rectangle_int_t dst_r = { dst_x, dst_y, width, height };
+
+#ifdef DEBUG_COMPOSITE
+    fprintf (stderr, "+++ composite: %d %p %p %p [%d %d] [%d %d] [%d %d] %dx%d\n",
+            op, pattern, mask_pattern, abstract_dst, src_x, src_y, mask_x, mask_y, dst_x, dst_y, width, height);
+#endif
+
+    /* If the destination can't do any of these, then
+     * we may as well give up, since this is what we'll
+     * look to for optimization.
+     */
+    if ((dst->flags & (CAIRO_WIN32_SURFACE_CAN_BITBLT |
+                      CAIRO_WIN32_SURFACE_CAN_ALPHABLEND |
+                      CAIRO_WIN32_SURFACE_CAN_STRETCHBLT |
+                      CAIRO_WIN32_SURFACE_CAN_STRETCHDIB))
+       == 0)
+    {
+       goto UNSUPPORTED;
+    }
+
+    if (pattern->type != CAIRO_PATTERN_TYPE_SURFACE)
+       goto UNSUPPORTED;
+
+    if (pattern->extend != CAIRO_EXTEND_NONE &&
+       pattern->extend != CAIRO_EXTEND_REPEAT)
+       goto UNSUPPORTED;
+
+    if (mask_pattern) {
+       /* FIXME: When we fully support RENDER style 4-channel
+        * masks we need to check r/g/b != 1.0.
+        */
+       if (mask_pattern->type != CAIRO_PATTERN_TYPE_SOLID)
+           return CAIRO_INT_STATUS_UNSUPPORTED;
+
+       alpha = ((cairo_solid_pattern_t *)mask_pattern)->color.alpha_short >> 8;
+    } else {
+       alpha = 255;
+    }
+
+    src_surface_pattern = (cairo_surface_pattern_t *)pattern;
+    src = (cairo_win32_surface_t *)src_surface_pattern->surface;
+
+    if (src->base.type == CAIRO_SURFACE_TYPE_IMAGE &&
+       dst->flags & (CAIRO_WIN32_SURFACE_CAN_STRETCHDIB))
+    {
+       /* In some very limited cases, we can use StretchDIBits to draw
+        * an image surface directly:
+        *  - source is CAIRO_FORMAT_ARGB32
+        *  - dest is CAIRO_FORMAT_ARGB32
+        *  - alpha is 255
+        *  - operator is SOURCE or OVER
+        *  - image stride is 4*width
+        */
+       src_image = (cairo_image_surface_t*) src;
+
+       if (src_image->format != CAIRO_FORMAT_RGB24 ||
+           dst->format != CAIRO_FORMAT_RGB24 ||
+           alpha != 255 ||
+           (op != CAIRO_OPERATOR_SOURCE && op != CAIRO_OPERATOR_OVER) ||
+           src_image->stride != (src_image->width * 4))
+       {
+           goto UNSUPPORTED;
+       }
+
+       src_format = src_image->format;
+       src_extents.x = 0;
+       src_extents.y = 0;
+       src_extents.width = src_image->width;
+       src_extents.height = src_image->height;
+    } else if (src->base.backend != dst->base.backend) {
+       goto UNSUPPORTED;
+    } else {
+       src_format = src->format;
+       src_extents = src->extents;
+    }
+
+
+#ifdef DEBUG_COMPOSITE
+    fprintf (stderr, "Before check: src size: (%d %d) xy [%d %d] -> dst [%d %d %d %d] {srcmat %f %f %f %f}\n",
+            src_extents.width, src_extents.height,
+            src_x, src_y,
+            dst_x, dst_y, width, height,
+            pattern->matrix.x0, pattern->matrix.y0, pattern->matrix.xx, pattern->matrix.yy);
+#endif
+
+    /* We can only use GDI functions if the source and destination rectangles
+     * are on integer pixel boundaries.  Figure that out here.
+     */
+    x0_fixed = _cairo_fixed_from_double(pattern->matrix.x0 / pattern->matrix.xx);
+    y0_fixed = _cairo_fixed_from_double(pattern->matrix.y0 / pattern->matrix.yy);
+
+    if (pattern->matrix.yx != 0.0 ||
+       pattern->matrix.xy != 0.0 ||
+       !_cairo_fixed_is_integer(x0_fixed) ||
+       !_cairo_fixed_is_integer(y0_fixed))
+    {
+       goto UNSUPPORTED;
+    }
+
+    scalex = pattern->matrix.xx;
+    scaley = pattern->matrix.yy;
+
+    src_r.x += _cairo_fixed_integer_part(x0_fixed);
+    src_r.y += _cairo_fixed_integer_part(y0_fixed);
+
+    /* Success, right? */
+    if (scalex == 0.0 || scaley == 0.0)
+       return CAIRO_STATUS_SUCCESS;
+
+    if (scalex != 1.0 || scaley != 1.0)
+       goto UNSUPPORTED;
+
+    /* If the src coordinates are outside of the source surface bounds,
+     * we have to fix them up, because this is an error for the GDI
+     * functions.
+     */
+
+#ifdef DEBUG_COMPOSITE
+    fprintf (stderr, "before: [%d %d %d %d] -> [%d %d %d %d]\n",
+            src_r.x, src_r.y, src_r.width, src_r.height,
+            dst_r.x, dst_r.y, dst_r.width, dst_r.height);
+    fflush (stderr);
+#endif
+
+    /* If the src rectangle doesn't wholly lie within the src extents,
+     * fudge things.  We really need to do fixup on the unpainted
+     * region -- e.g. the SOURCE operator is broken for areas outside
+     * of the extents, because it won't clear that area to transparent
+     * black.
+     */
+
+    if (pattern->extend != CAIRO_EXTEND_REPEAT) {
+       needs_repeat = FALSE;
+
+       /* If the src rect and the extents of the source image don't overlap at all,
+        * we can't do anything useful here.
+        */
+       if (src_r.x > src_extents.width || src_r.y > src_extents.height ||
+           (src_r.x + src_r.width) < 0 || (src_r.y + src_r.height) < 0)
+       {
+           if (op == CAIRO_OPERATOR_OVER)
+               return CAIRO_STATUS_SUCCESS;
+           goto UNSUPPORTED;
+       }
+
+       if (src_r.x < 0) {
+           src_r.width += src_r.x;
+           src_r.x = 0;
+
+           dst_r.width += src_r.x;
+           dst_r.x -= src_r.x;
+       }
+
+       if (src_r.y < 0) {
+           src_r.height += src_r.y;
+           src_r.y = 0;
+
+           dst_r.height += dst_r.y;
+           dst_r.y -= src_r.y;
+       }
+
+       if (src_r.x + src_r.width > src_extents.width) {
+           src_r.width = src_extents.width - src_r.x;
+           dst_r.width = src_r.width;
+       }
+
+       if (src_r.y + src_r.height > src_extents.height) {
+           src_r.height = src_extents.height - src_r.y;
+           dst_r.height = src_r.height;
+       }
+    } else {
+       needs_repeat = TRUE;
+    }
+
+    /*
+     * Operations that we can do:
+     *
+     *  RGB OVER  RGB -> BitBlt (same as SOURCE)
+     *  RGB OVER ARGB -> UNSUPPORTED (AlphaBlend treats this as a BitBlt, even with SCA 255 and no AC_SRC_ALPHA)
+     * ARGB OVER ARGB -> AlphaBlend, with AC_SRC_ALPHA
+     * ARGB OVER  RGB -> AlphaBlend, with AC_SRC_ALPHA; we'll have junk in the dst A byte
+     * 
+     *  RGB OVER  RGB + mask -> AlphaBlend, no AC_SRC_ALPHA
+     *  RGB OVER ARGB + mask -> UNSUPPORTED
+     * ARGB OVER ARGB + mask -> AlphaBlend, with AC_SRC_ALPHA
+     * ARGB OVER  RGB + mask -> AlphaBlend, with AC_SRC_ALPHA; junk in the dst A byte
+     * 
+     *  RGB SOURCE  RGB -> BitBlt
+     *  RGB SOURCE ARGB -> UNSUPPORTED (AlphaBlend treats this as a BitBlt, even with SCA 255 and no AC_SRC_ALPHA)
+     * ARGB SOURCE ARGB -> BitBlt
+     * ARGB SOURCE  RGB -> BitBlt
+     * 
+     *  RGB SOURCE  RGB + mask -> unsupported
+     *  RGB SOURCE ARGB + mask -> unsupported
+     * ARGB SOURCE ARGB + mask -> unsupported
+     * ARGB SOURCE  RGB + mask -> unsupported
+     */
+
+    /*
+     * Figure out what action to take.
+     */
+    if (op == CAIRO_OPERATOR_OVER) {
+       if (alpha == 0)
+           return CAIRO_STATUS_SUCCESS;
+
+       if (src_format == dst->format) {
+           if (alpha == 255 && src_format == CAIRO_FORMAT_RGB24) {
+               needs_alpha = FALSE;
+           } else {
+               needs_alpha = TRUE;
+           }
+       } else if (src_format == CAIRO_FORMAT_ARGB32 &&
+                  dst->format == CAIRO_FORMAT_RGB24)
+       {
+           needs_alpha = TRUE;
+       } else {
+           goto UNSUPPORTED;
+       }
+    } else if (alpha == 255 && op == CAIRO_OPERATOR_SOURCE) {
+       if ((src_format == dst->format) ||
+           (src_format == CAIRO_FORMAT_ARGB32 && dst->format == CAIRO_FORMAT_RGB24))
+       {
+           needs_alpha = FALSE;
+       } else {
+           goto UNSUPPORTED;
+       }
+    } else {
+       goto UNSUPPORTED;
+    }
+
+    if (scalex == 1.0 && scaley == 1.0) {
+       needs_scale = FALSE;
+    } else {
+       /* Should never be reached until we turn StretchBlt back on */
+       needs_scale = TRUE;
+    }
+
+#ifdef DEBUG_COMPOSITE
+    fprintf (stderr, "action: [%d %d %d %d] -> [%d %d %d %d]\n",
+            src_r.x, src_r.y, src_r.width, src_r.height,
+            dst_r.x, dst_r.y, dst_r.width, dst_r.height);
+    fflush (stderr);
+#endif
+
+    status = _cairo_win32_surface_set_clip_region (dst, clip_region);
+    if (status)
+       return status;
+
+    /* If we need to repeat, we turn the repeated blit into
+     * a bunch of piece-by-piece blits.
+     */
+    if (needs_repeat) {
+       cairo_rectangle_int_t piece_src_r, piece_dst_r;
+       uint32_t rendered_width = 0, rendered_height = 0;
+       uint32_t to_render_height, to_render_width;
+       int32_t piece_x, piece_y;
+       int32_t src_start_x = MOD(src_r.x, src_extents.width);
+       int32_t src_start_y = MOD(src_r.y, src_extents.height);
+
+       if (needs_scale)
+           goto UNSUPPORTED;
+
+       /* If both the src and dest have an image, we may as well fall
+        * back, because it will be faster than our separate blits.
+        * Our blit code will be fastest when the src is a DDB and the
+        * destination is a DDB.
+        */
+       if ((src_image || src->image) && dst->image)
+           goto UNSUPPORTED;
+
+       /* If the src is not a bitmap but an on-screen (or unknown)
+        * DC, chances are that fallback will be faster.
+        */
+       if (src->bitmap == NULL)
+           goto UNSUPPORTED;
+
+       /* If we can use PatBlt, just do so */
+       if (!src_image && !needs_alpha)
+       {
+           HBRUSH brush;
+           HGDIOBJ old_brush;
+           POINT old_brush_origin;
+
+           /* Set up the brush with our bitmap */
+           brush = CreatePatternBrush (src->bitmap);
+
+           /* SetBrushOrgEx sets the coordinates in the destination DC of where the
+            * pattern should start.
+            */
+           SetBrushOrgEx (dst->dc, dst_r.x - src_start_x,
+                          dst_r.y - src_start_y, &old_brush_origin);
+
+           old_brush = SelectObject (dst->dc, brush);
+
+           PatBlt (dst->dc, dst_r.x, dst_r.y, dst_r.width, dst_r.height, PATCOPY);
+
+           /* Restore the old brush and pen */
+           SetBrushOrgEx (dst->dc, old_brush_origin.x, old_brush_origin.y, NULL);
+           SelectObject (dst->dc, old_brush);
+           DeleteObject (brush);
+
+           return CAIRO_STATUS_SUCCESS;
+       }
+
+       /* If we were not able to use PatBlt, then manually expand out the blit */
+
+       /* Arbitrary factor; we think that going through
+        * fallback will be faster if we have to do more
+        * than this amount of blits in either direction.
+        */
+       if (dst_r.width / src_extents.width > 5 ||
+           dst_r.height / src_extents.height > 5)
+           goto UNSUPPORTED;
+
+       for (rendered_height = 0;
+            rendered_height < dst_r.height;
+            rendered_height += to_render_height)
+       {
+           piece_y = (src_start_y + rendered_height) % src_extents.height;
+           to_render_height = src_extents.height - piece_y;
+
+           if (rendered_height + to_render_height > dst_r.height)
+               to_render_height = dst_r.height - rendered_height;
+
+           for (rendered_width = 0;
+                rendered_width < dst_r.width;
+                rendered_width += to_render_width)
+           {
+               piece_x = (src_start_x + rendered_width) % src_extents.width;
+               to_render_width = src_extents.width - piece_x;
+
+               if (rendered_width + to_render_width > dst_r.width)
+                   to_render_width = dst_r.width - rendered_width;
+
+               piece_src_r.x = piece_x;
+               piece_src_r.y = piece_y;
+               piece_src_r.width = to_render_width;
+               piece_src_r.height = to_render_height;
+
+               piece_dst_r.x = dst_r.x + rendered_width;
+               piece_dst_r.y = dst_r.y + rendered_height;
+               piece_dst_r.width = to_render_width;
+               piece_dst_r.height = to_render_height;
+
+               status = _cairo_win32_surface_composite_inner (src, src_image, dst,
+                                                              src_extents, piece_src_r, piece_dst_r,
+                                                              alpha, needs_alpha, needs_scale);
+               if (status != CAIRO_STATUS_SUCCESS) {
+                   /* Uh oh.  If something failed, and it's the first
+                    * piece, then we can jump to UNSUPPORTED. 
+                    * Otherwise, this is bad times, because part of the
+                    * rendering was already done. */
+                   if (rendered_width == 0 &&
+                       rendered_height == 0)
+                   {
+                       goto UNSUPPORTED;
+                   }
+
+                   return status;
+               }
+           }
+       }
+    } else {
+       status = _cairo_win32_surface_composite_inner (src, src_image, dst,
+                                                      src_extents, src_r, dst_r,
+                                                      alpha, needs_alpha, needs_scale);
+    }
+
+    if (status == CAIRO_STATUS_SUCCESS)
+       return status;
+
+UNSUPPORTED:
+    /* Fall back to image surface directly, if this is a DIB surface */
+    if (dst->image) {
+       GdiFlush();
+
+#if 0
+       return dst->image->backend->composite (op, pattern, mask_pattern,
+                                              dst->image,
+                                              src_x, src_y,
+                                              mask_x, mask_y,
+                                              dst_x, dst_y,
+                                              width, height,
+                                              clip_region);
+#endif
+    }
+
+    return CAIRO_INT_STATUS_UNSUPPORTED;
+}
+
+/* This big function tells us how to optimize operators for the
+ * case of solid destination and constant-alpha source
+ *
+ * Note: This function needs revisiting if we add support for
+ *       super-luminescent colors (a == 0, r,g,b > 0)
+ */
+static enum { DO_CLEAR, DO_SOURCE, DO_NOTHING, DO_UNSUPPORTED }
+categorize_solid_dest_operator (cairo_operator_t op,
+                               unsigned short   alpha)
+{
+    enum { SOURCE_TRANSPARENT, SOURCE_LIGHT, SOURCE_SOLID, SOURCE_OTHER } source;
+
+    if (alpha >= 0xff00)
+       source = SOURCE_SOLID;
+    else if (alpha < 0x100)
+       source = SOURCE_TRANSPARENT;
+    else
+       source = SOURCE_OTHER;
+
+    switch (op) {
+    case CAIRO_OPERATOR_CLEAR:    /* 0                 0 */
+    case CAIRO_OPERATOR_OUT:      /* 1 - Ab            0 */
+       return DO_CLEAR;
+       break;
+
+    case CAIRO_OPERATOR_SOURCE:   /* 1                 0 */
+    case CAIRO_OPERATOR_IN:       /* Ab                0 */
+       return DO_SOURCE;
+       break;
+
+    case CAIRO_OPERATOR_OVER:     /* 1            1 - Aa */
+    case CAIRO_OPERATOR_ATOP:     /* Ab           1 - Aa */
+       if (source == SOURCE_SOLID)
+           return DO_SOURCE;
+       else if (source == SOURCE_TRANSPARENT)
+           return DO_NOTHING;
+       else
+           return DO_UNSUPPORTED;
+       break;
+
+    case CAIRO_OPERATOR_DEST_OUT: /* 0            1 - Aa */
+    case CAIRO_OPERATOR_XOR:      /* 1 - Ab       1 - Aa */
+       if (source == SOURCE_SOLID)
+           return DO_CLEAR;
+       else if (source == SOURCE_TRANSPARENT)
+           return DO_NOTHING;
+       else
+           return DO_UNSUPPORTED;
+       break;
+
+    case CAIRO_OPERATOR_DEST:     /* 0                 1 */
+    case CAIRO_OPERATOR_DEST_OVER:/* 1 - Ab            1 */
+    case CAIRO_OPERATOR_SATURATE: /* min(1,(1-Ab)/Aa)  1 */
+       return DO_NOTHING;
+       break;
+
+    case CAIRO_OPERATOR_DEST_IN:  /* 0                Aa */
+    case CAIRO_OPERATOR_DEST_ATOP:/* 1 - Ab           Aa */
+       if (source == SOURCE_SOLID)
+           return DO_NOTHING;
+       else if (source == SOURCE_TRANSPARENT)
+           return DO_CLEAR;
+       else
+           return DO_UNSUPPORTED;
+       break;
+
+    case CAIRO_OPERATOR_ADD:     /* 1                1 */
+       if (source == SOURCE_TRANSPARENT)
+           return DO_NOTHING;
+       else
+           return DO_UNSUPPORTED;
+       break;
+
+    case CAIRO_OPERATOR_MULTIPLY:
+    case CAIRO_OPERATOR_SCREEN:
+    case CAIRO_OPERATOR_OVERLAY:
+    case CAIRO_OPERATOR_DARKEN:
+    case CAIRO_OPERATOR_LIGHTEN:
+    case CAIRO_OPERATOR_COLOR_DODGE:
+    case CAIRO_OPERATOR_COLOR_BURN:
+    case CAIRO_OPERATOR_HARD_LIGHT:
+    case CAIRO_OPERATOR_SOFT_LIGHT:
+    case CAIRO_OPERATOR_DIFFERENCE:
+    case CAIRO_OPERATOR_EXCLUSION:
+    case CAIRO_OPERATOR_HSL_HUE:
+    case CAIRO_OPERATOR_HSL_SATURATION:
+    case CAIRO_OPERATOR_HSL_COLOR:
+    case CAIRO_OPERATOR_HSL_LUMINOSITY:
+       return DO_UNSUPPORTED;
+
+    default:
+       ASSERT_NOT_REACHED;
+       return DO_UNSUPPORTED;
+    }
+}
+
+static cairo_int_status_t
+_cairo_win32_surface_fill_rectangles (void                     *abstract_surface,
+                                     cairo_operator_t          op,
+                                     const cairo_color_t       *color,
+                                     cairo_rectangle_int_t     *rects,
+                                     int                       num_rects)
+{
+    cairo_win32_surface_t *surface = abstract_surface;
+    cairo_status_t status;
+    COLORREF new_color;
+    HBRUSH new_brush;
+    int i;
+
+    /* XXXperf If it's not RGB24, we need to do a little more checking
+     * to figure out when we can use GDI.  We don't have that checking
+     * anywhere at the moment, so just bail and use the fallback
+     * paths. */
+    if (surface->format != CAIRO_FORMAT_RGB24)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    status = _cairo_win32_surface_set_clip_region (surface, NULL);
+    if (status)
+       return status;
+
+    /* Optimize for no destination alpha (surface->pixman_image is non-NULL for all
+     * surfaces with alpha.)
+     */
+    switch (categorize_solid_dest_operator (op, color->alpha_short)) {
+    case DO_CLEAR:
+       new_color = RGB (0, 0, 0);
+       break;
+    case DO_SOURCE:
+       new_color = RGB (color->red_short >> 8, color->green_short >> 8, color->blue_short >> 8);
+       break;
+    case DO_NOTHING:
+       return CAIRO_STATUS_SUCCESS;
+    case DO_UNSUPPORTED:
+    default:
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+    }
+
+    new_brush = CreateSolidBrush (new_color);
+    if (!new_brush)
+       return _cairo_win32_print_gdi_error ("_cairo_win32_surface_fill_rectangles");
+
+    for (i = 0; i < num_rects; i++) {
+       RECT rect;
+
+       rect.left = rects[i].x;
+       rect.top = rects[i].y;
+       rect.right = rects[i].x + rects[i].width;
+       rect.bottom = rects[i].y + rects[i].height;
+
+       if (!FillRect (surface->dc, &rect, new_brush))
+           goto FAIL;
+    }
+
+    DeleteObject (new_brush);
+
+    return CAIRO_STATUS_SUCCESS;
+
+ FAIL:
+    status = _cairo_win32_print_gdi_error ("_cairo_win32_surface_fill_rectangles");
+
+    DeleteObject (new_brush);
+
+    return status;
+}
+
+cairo_bool_t
+_cairo_win32_surface_get_extents (void                   *abstract_surface,
+                                 cairo_rectangle_int_t   *rectangle)
+{
+    cairo_win32_surface_t *surface = abstract_surface;
+
+    *rectangle = surface->extents;
+    return TRUE;
+}
+
+static cairo_status_t
+_cairo_win32_surface_flush (void *abstract_surface)
+{
+    return _cairo_win32_surface_set_clip_region (abstract_surface, NULL);
+}
+
+#define STACK_GLYPH_SIZE 256
+
+cairo_int_status_t
+_cairo_win32_surface_show_glyphs_internal (void                         *surface,
+                                          cairo_operator_t       op,
+                                          const cairo_pattern_t *source,
+                                          cairo_glyph_t         *glyphs,
+                                          int                    num_glyphs,
+                                          cairo_scaled_font_t   *scaled_font,
+                                          const cairo_clip_t    *clip,
+                                          cairo_bool_t           glyph_indexing)
+{
+#if CAIRO_HAS_WIN32_FONT
+    cairo_win32_surface_t *dst = surface;
+
+    WORD glyph_buf_stack[STACK_GLYPH_SIZE];
+    WORD *glyph_buf = glyph_buf_stack;
+    int dxy_buf_stack[2 * STACK_GLYPH_SIZE];
+    int *dxy_buf = dxy_buf_stack;
+
+    BOOL win_result = 0;
+    int i, j;
+
+    cairo_solid_pattern_t *solid_pattern;
+    COLORREF color;
+
+    cairo_matrix_t device_to_logical;
+
+    int start_x, start_y;
+    double user_x, user_y;
+    int logical_x, logical_y;
+    unsigned int glyph_index_option;
+
+    /* We can only handle win32 fonts */
+    if (cairo_scaled_font_get_type (scaled_font) != CAIRO_FONT_TYPE_WIN32)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    /* We can only handle opaque solid color sources */
+    if (!_cairo_pattern_is_opaque_solid(source))
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    /* We can only handle operator SOURCE or OVER with the destination
+     * having no alpha */
+    if ((op != CAIRO_OPERATOR_SOURCE && op != CAIRO_OPERATOR_OVER) ||
+       (dst->format != CAIRO_FORMAT_RGB24))
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    /* If we have a fallback mask clip set on the dst, we have
+     * to go through the fallback path, but only if we're not
+     * doing this for printing */
+    if (clip != NULL) {
+       if ((dst->flags & CAIRO_WIN32_SURFACE_FOR_PRINTING) == 0) {
+           if (! _cairo_clip_is_region (clip))
+               return CAIRO_INT_STATUS_UNSUPPORTED;
+
+           _cairo_win32_surface_set_clip_region (surface,
+                                                 _cairo_clip_get_region (clip));
+       }
+    }
+
+    solid_pattern = (cairo_solid_pattern_t *)source;
+    color = RGB(((int)solid_pattern->color.red_short) >> 8,
+               ((int)solid_pattern->color.green_short) >> 8,
+               ((int)solid_pattern->color.blue_short) >> 8);
+
+    cairo_win32_scaled_font_get_device_to_logical(scaled_font, &device_to_logical);
+
+    SaveDC(dst->dc);
+
+    cairo_win32_scaled_font_select_font(scaled_font, dst->dc);
+    SetTextColor(dst->dc, color);
+    SetTextAlign(dst->dc, TA_BASELINE | TA_LEFT);
+    SetBkMode(dst->dc, TRANSPARENT);
+
+    if (num_glyphs > STACK_GLYPH_SIZE) {
+       glyph_buf = (WORD *) _cairo_malloc_ab (num_glyphs, sizeof(WORD));
+        dxy_buf = (int *) _cairo_malloc_abc (num_glyphs, sizeof(int), 2);
+    }
+
+    /* It is vital that dx values for dxy_buf are calculated from the delta of
+     * _logical_ x coordinates (not user x coordinates) or else the sum of all
+     * previous dx values may start to diverge from the current glyph's x
+     * coordinate due to accumulated rounding error. As a result strings could
+     * be painted shorter or longer than expected. */
+
+    user_x = glyphs[0].x;
+    user_y = glyphs[0].y;
+
+    cairo_matrix_transform_point(&device_to_logical,
+                                 &user_x, &user_y);
+
+    logical_x = _cairo_lround (user_x);
+    logical_y = _cairo_lround (user_y);
+
+    start_x = logical_x;
+    start_y = logical_y;
+
+    for (i = 0, j = 0; i < num_glyphs; ++i, j = 2 * i) {
+        glyph_buf[i] = (WORD) glyphs[i].index;
+        if (i == num_glyphs - 1) {
+            dxy_buf[j] = 0;
+            dxy_buf[j+1] = 0;
+        } else {
+            double next_user_x = glyphs[i+1].x;
+            double next_user_y = glyphs[i+1].y;
+            int next_logical_x, next_logical_y;
+
+            cairo_matrix_transform_point(&device_to_logical,
+                                         &next_user_x, &next_user_y);
+
+            next_logical_x = _cairo_lround (next_user_x);
+            next_logical_y = _cairo_lround (next_user_y);
+
+            dxy_buf[j] = _cairo_lround (next_logical_x - logical_x);
+            dxy_buf[j+1] = _cairo_lround (next_logical_y - logical_y);
+
+            logical_x = next_logical_x;
+            logical_y = next_logical_y;
+        }
+    }
+
+    if (glyph_indexing)
+       glyph_index_option = ETO_GLYPH_INDEX;
+    else
+       glyph_index_option = 0;
+
+    win_result = ExtTextOutW(dst->dc,
+                             start_x,
+                             start_y,
+                             glyph_index_option | ETO_PDY,
+                             NULL,
+                             glyph_buf,
+                             num_glyphs,
+                             dxy_buf);
+    if (!win_result) {
+        _cairo_win32_print_gdi_error("_cairo_win32_surface_show_glyphs(ExtTextOutW failed)");
+    }
+
+    RestoreDC(dst->dc, -1);
+
+    if (glyph_buf != glyph_buf_stack) {
+       free(glyph_buf);
+        free(dxy_buf);
+    }
+    return (win_result) ? CAIRO_STATUS_SUCCESS : CAIRO_INT_STATUS_UNSUPPORTED;
+#else
+    return CAIRO_INT_STATUS_UNSUPPORTED;
+#endif
+}
+
+#undef STACK_GLYPH_SIZE
+
+cairo_int_status_t
+_cairo_win32_surface_show_glyphs (void                 *surface,
+                                 cairo_operator_t       op,
+                                 const cairo_pattern_t *source,
+                                 cairo_glyph_t         *glyphs,
+                                 int                    num_glyphs,
+                                 cairo_scaled_font_t   *scaled_font,
+                                 const cairo_clip_t    *clip)
+{
+    return _cairo_win32_surface_show_glyphs_internal (surface,
+                                                     op,
+                                                     source,
+                                                     glyphs,
+                                                     num_glyphs,
+                                                     scaled_font,
+                                                     clip,
+                                                     TRUE);
+}
+
+
+/**
+ * cairo_win32_surface_create:
+ * @hdc: the DC to create a surface for
+ *
+ * Creates a cairo surface that targets the given DC.  The DC will be
+ * queried for its initial clip extents, and this will be used as the
+ * size of the cairo surface.  The resulting surface will always be of
+ * format %CAIRO_FORMAT_RGB24; should you need another surface format,
+ * you will need to create one through
+ * cairo_win32_surface_create_with_dib().
+ *
+ * Return value: the newly created surface
+ **/
+cairo_surface_t *
+cairo_win32_surface_create (HDC hdc)
+{
+    cairo_win32_surface_t *surface;
+
+    cairo_format_t format;
+    RECT rect;
+
+    /* Assume that everything coming in as a HDC is RGB24 */
+    format = CAIRO_FORMAT_RGB24;
+
+    surface = malloc (sizeof (cairo_win32_surface_t));
+    if (surface == NULL)
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+    if (_cairo_win32_save_initial_clip (hdc, surface) != CAIRO_STATUS_SUCCESS) {
+       free (surface);
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+    }
+
+    surface->clip_region = NULL;
+    surface->image = NULL;
+    surface->format = format;
+
+    surface->dc = hdc;
+    surface->bitmap = NULL;
+    surface->is_dib = FALSE;
+    surface->saved_dc_bitmap = NULL;
+    surface->brush = NULL;
+    surface->old_brush = NULL;
+    surface->font_subsets = NULL;
+
+    GetClipBox(hdc, &rect);
+    surface->extents.x = rect.left;
+    surface->extents.y = rect.top;
+    surface->extents.width = rect.right - rect.left;
+    surface->extents.height = rect.bottom - rect.top;
+
+    surface->flags = _cairo_win32_flags_for_dc (surface->dc);
+
+    _cairo_surface_init (&surface->base,
+                        &cairo_win32_surface_backend,
+                        NULL, /* device */
+                        _cairo_content_from_format (format));
+
+    return (cairo_surface_t *)surface;
+}
+
+/**
+ * cairo_win32_surface_create_with_dib:
+ * @format: format of pixels in the surface to create
+ * @width: width of the surface, in pixels
+ * @height: height of the surface, in pixels
+ *
+ * Creates a device-independent-bitmap surface not associated with
+ * any particular existing surface or device context. The created
+ * bitmap will be uninitialized.
+ *
+ * Return value: the newly created surface
+ *
+ * Since: 1.2
+ **/
+cairo_surface_t *
+cairo_win32_surface_create_with_dib (cairo_format_t format,
+                                    int            width,
+                                    int            height)
+{
+    return _cairo_win32_surface_create_for_dc (NULL, format, width, height);
+}
+
+/**
+ * cairo_win32_surface_create_with_ddb:
+ * @hdc: a DC compatible with the surface to create
+ * @format: format of pixels in the surface to create
+ * @width: width of the surface, in pixels
+ * @height: height of the surface, in pixels
+ *
+ * Creates a device-dependent-bitmap surface not associated with
+ * any particular existing surface or device context. The created
+ * bitmap will be uninitialized.
+ *
+ * Return value: the newly created surface
+ *
+ * Since: 1.4
+ **/
+cairo_surface_t *
+cairo_win32_surface_create_with_ddb (HDC hdc,
+                                    cairo_format_t format,
+                                    int width,
+                                    int height)
+{
+    cairo_win32_surface_t *new_surf;
+    HBITMAP ddb;
+    HDC screen_dc, ddb_dc;
+    HBITMAP saved_dc_bitmap;
+
+    if (format != CAIRO_FORMAT_RGB24)
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
+/* XXX handle these eventually
+       format != CAIRO_FORMAT_A8 ||
+       format != CAIRO_FORMAT_A1)
+*/
+
+    if (!hdc) {
+       screen_dc = GetDC (NULL);
+       hdc = screen_dc;
+    } else {
+       screen_dc = NULL;
+    }
+
+    ddb_dc = CreateCompatibleDC (hdc);
+    if (ddb_dc == NULL) {
+       new_surf = (cairo_win32_surface_t*) _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+       goto FINISH;
+    }
+
+    ddb = CreateCompatibleBitmap (hdc, width, height);
+    if (ddb == NULL) {
+       DeleteDC (ddb_dc);
+
+       /* Note that if an app actually does hit this out of memory
+        * condition, it's going to have lots of other issues, as
+        * video memory is probably exhausted.  However, it can often
+        * continue using DIBs instead of DDBs.
+        */
+       new_surf = (cairo_win32_surface_t*) _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+       goto FINISH;
+    }
+
+    saved_dc_bitmap = SelectObject (ddb_dc, ddb);
+
+    new_surf = (cairo_win32_surface_t*) cairo_win32_surface_create (ddb_dc);
+    new_surf->bitmap = ddb;
+    new_surf->saved_dc_bitmap = saved_dc_bitmap;
+    new_surf->is_dib = FALSE;
+
+FINISH:
+    if (screen_dc)
+       ReleaseDC (NULL, screen_dc);
+
+    return (cairo_surface_t*) new_surf;
+}
+
+/**
+ * _cairo_surface_is_win32:
+ * @surface: a #cairo_surface_t
+ *
+ * Checks if a surface is a win32 surface.  This will
+ * return False if this is a win32 printing surface; use
+ * _cairo_surface_is_win32_printing() to check for that.
+ *
+ * Return value: True if the surface is an win32 surface
+ **/
+int
+_cairo_surface_is_win32 (cairo_surface_t *surface)
+{
+    return surface->backend == &cairo_win32_surface_backend;
+}
+
+/**
+ * cairo_win32_surface_get_dc
+ * @surface: a #cairo_surface_t
+ *
+ * Returns the HDC associated with this surface, or %NULL if none.
+ * Also returns %NULL if the surface is not a win32 surface.
+ *
+ * A call to cairo_surface_flush() is required before using the HDC to
+ * ensure that all pending drawing operations are finished and to
+ * restore any temporary modification cairo has made to its state. A
+ * call to cairo_surface_mark_dirty() is required after the state or
+ * the content of the HDC has been modified.
+ *
+ * Return value: HDC or %NULL if no HDC available.
+ *
+ * Since: 1.2
+ **/
+HDC
+cairo_win32_surface_get_dc (cairo_surface_t *surface)
+{
+    cairo_win32_surface_t *winsurf;
+
+    if (_cairo_surface_is_win32 (surface)){
+       winsurf = (cairo_win32_surface_t *) surface;
+
+       return winsurf->dc;
+    }
+
+    if (_cairo_surface_is_paginated (surface)) {
+       cairo_surface_t *target;
+
+       target = _cairo_paginated_surface_get_target (surface);
+
+       if (_cairo_surface_is_win32_printing (target)) {
+           winsurf = (cairo_win32_surface_t *) target;
+
+           return winsurf->dc;
+       }
+    }
+
+    return NULL;
+}
+
+/**
+ * cairo_win32_surface_get_image
+ * @surface: a #cairo_surface_t
+ *
+ * Returns a #cairo_surface_t image surface that refers to the same bits
+ * as the DIB of the Win32 surface.  If the passed-in win32 surface
+ * is not a DIB surface, %NULL is returned.
+ *
+ * Return value: a #cairo_surface_t (owned by the win32 #cairo_surface_t),
+ * or %NULL if the win32 surface is not a DIB.
+ *
+ * Since: 1.4
+ */
+cairo_surface_t *
+cairo_win32_surface_get_image (cairo_surface_t *surface)
+{
+    if (!_cairo_surface_is_win32(surface))
+       return NULL;
+
+    return ((cairo_win32_surface_t*)surface)->image;
+}
+
+static const cairo_surface_backend_t cairo_win32_surface_backend = {
+    CAIRO_SURFACE_TYPE_WIN32,
+    _cairo_win32_surface_finish,
+
+    _cairo_default_context_create,
+
+    _cairo_win32_surface_create_similar,
+    NULL,
+    _cairo_win32_surface_map_to_image,
+    _cairo_win32_surface_unmap_image,
+
+    _cairo_win32_surface_acquire_source_image,
+    _cairo_win32_surface_release_source_image,
+    NULL,  /* snapshot */
+
+    NULL, /* copy_page */
+    NULL, /* show_page */
+
+    _cairo_win32_surface_get_extents,
+    NULL, /* get_font_options */
+
+    _cairo_win32_surface_flush,
+    NULL, /* mark_dirty_rectangle */
+
+    NULL, /* paint */
+    NULL, /* mask */
+    NULL, /* stroke */
+    NULL, /* fill */
+    NULL, /* fill/stroke */
+    _cairo_win32_surface_show_glyphs,
+};
+
+/* Notes:
+ *
+ * Win32 alpha-understanding functions
+ *
+ * BitBlt - will copy full 32 bits from a 32bpp DIB to result
+ *          (so it's safe to use for ARGB32->ARGB32 SOURCE blits)
+ *          (but not safe going RGB24->ARGB32, if RGB24 is also represented
+ *           as a 32bpp DIB, since the alpha isn't discarded!)
+ *
+ * AlphaBlend - if both the source and dest have alpha, even if AC_SRC_ALPHA isn't set,
+ *              it will still copy over the src alpha, because the SCA value (255) will be
+ *              multiplied by all the src components.
+ */
+
+
+cairo_int_status_t
+_cairo_win32_save_initial_clip (HDC hdc, cairo_win32_surface_t *surface)
+{
+    RECT rect;
+    int clipBoxType;
+    int gm;
+    XFORM saved_xform;
+
+    /* GetClipBox/GetClipRgn and friends interact badly with a world transform
+     * set.  GetClipBox returns values in logical (transformed) coordinates;
+     * it's unclear what GetClipRgn returns, because the region is empty in the
+     * case of a SIMPLEREGION clip, but I assume device (untransformed) coordinates.
+     * Similarly, IntersectClipRect works in logical units, whereas SelectClipRgn
+     * works in device units.
+     *
+     * So, avoid the whole mess and get rid of the world transform
+     * while we store our initial data and when we restore initial coordinates.
+     *
+     * XXX we may need to modify x/y by the ViewportOrg or WindowOrg
+     * here in GM_COMPATIBLE; unclear.
+     */
+    gm = GetGraphicsMode (hdc);
+    if (gm == GM_ADVANCED) {
+       GetWorldTransform (hdc, &saved_xform);
+       ModifyWorldTransform (hdc, NULL, MWT_IDENTITY);
+    }
+
+    clipBoxType = GetClipBox (hdc, &rect);
+    if (clipBoxType == ERROR) {
+       _cairo_win32_print_gdi_error ("cairo_win32_surface_create");
+       SetGraphicsMode (hdc, gm);
+       /* XXX: Can we make a more reasonable guess at the error cause here? */
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+    }
+
+    surface->clip_rect.x = rect.left;
+    surface->clip_rect.y = rect.top;
+    surface->clip_rect.width = rect.right - rect.left;
+    surface->clip_rect.height = rect.bottom - rect.top;
+
+    surface->initial_clip_rgn = NULL;
+    surface->had_simple_clip = FALSE;
+
+    if (clipBoxType == COMPLEXREGION) {
+       surface->initial_clip_rgn = CreateRectRgn (0, 0, 0, 0);
+       if (GetClipRgn (hdc, surface->initial_clip_rgn) <= 0) {
+           DeleteObject(surface->initial_clip_rgn);
+           surface->initial_clip_rgn = NULL;
+       }
+    } else if (clipBoxType == SIMPLEREGION) {
+       surface->had_simple_clip = TRUE;
+    }
+
+    if (gm == GM_ADVANCED)
+       SetWorldTransform (hdc, &saved_xform);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_int_status_t
+_cairo_win32_restore_initial_clip (cairo_win32_surface_t *surface)
+{
+    cairo_int_status_t status = CAIRO_STATUS_SUCCESS;
+
+    XFORM saved_xform;
+    int gm = GetGraphicsMode (surface->dc);
+    if (gm == GM_ADVANCED) {
+       GetWorldTransform (surface->dc, &saved_xform);
+       ModifyWorldTransform (surface->dc, NULL, MWT_IDENTITY);
+    }
+
+    /* initial_clip_rgn will either be a real region or NULL (which means reset to no clip region) */
+    SelectClipRgn (surface->dc, surface->initial_clip_rgn);
+
+    if (surface->had_simple_clip) {
+       /* then if we had a simple clip, intersect */
+       IntersectClipRect (surface->dc,
+                          surface->clip_rect.x,
+                          surface->clip_rect.y,
+                          surface->clip_rect.x + surface->clip_rect.width,
+                          surface->clip_rect.y + surface->clip_rect.height);
+    }
+
+    if (gm == GM_ADVANCED)
+       SetWorldTransform (surface->dc, &saved_xform);
+
+    return status;
+}
+
+void
+_cairo_win32_debug_dump_hrgn (HRGN rgn, char *header)
+{
+    RGNDATA *rd;
+    unsigned int z;
+
+    if (header)
+       fprintf (stderr, "%s\n", header);
+
+    if (rgn == NULL) {
+       fprintf (stderr, " NULL\n");
+    }
+
+    z = GetRegionData(rgn, 0, NULL);
+    rd = (RGNDATA*) malloc(z);
+    z = GetRegionData(rgn, z, rd);
+
+    fprintf (stderr, " %ld rects, bounds: %ld %ld %ld %ld\n",
+            rd->rdh.nCount,
+            rd->rdh.rcBound.left,
+            rd->rdh.rcBound.top,
+            rd->rdh.rcBound.right - rd->rdh.rcBound.left,
+            rd->rdh.rcBound.bottom - rd->rdh.rcBound.top);
+
+    for (z = 0; z < rd->rdh.nCount; z++) {
+       RECT r = ((RECT*)rd->Buffer)[z];
+       fprintf (stderr, " [%d]: [%ld %ld %ld %ld]\n",
+                z, r.left, r.top, r.right - r.left, r.bottom - r.top);
+    }
+
+    free(rd);
+    fflush (stderr);
+}
diff --git a/src/cairo-win32.h b/src/cairo-win32.h
new file mode 100644 (file)
index 0000000..3d2e1c6
--- /dev/null
@@ -0,0 +1,112 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2005 Red Hat, Inc
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ *     Owen Taylor <otaylor@redhat.com>
+ */
+
+#ifndef _CAIRO_WIN32_H_
+#define _CAIRO_WIN32_H_
+
+#include "cairo.h"
+
+#if CAIRO_HAS_WIN32_SURFACE
+
+#include <windows.h>
+
+CAIRO_BEGIN_DECLS
+
+cairo_public cairo_surface_t *
+cairo_win32_surface_create (HDC hdc);
+
+cairo_public cairo_surface_t *
+cairo_win32_printing_surface_create (HDC hdc);
+
+cairo_public cairo_surface_t *
+cairo_win32_surface_create_with_ddb (HDC hdc,
+                                     cairo_format_t format,
+                                     int width,
+                                     int height);
+
+cairo_public cairo_surface_t *
+cairo_win32_surface_create_with_dib (cairo_format_t format,
+                                     int width,
+                                     int height);
+
+cairo_public HDC
+cairo_win32_surface_get_dc (cairo_surface_t *surface);
+
+cairo_public cairo_surface_t *
+cairo_win32_surface_get_image (cairo_surface_t *surface);
+
+#if CAIRO_HAS_WIN32_FONT
+
+/*
+ * Win32 font support
+ */
+
+cairo_public cairo_font_face_t *
+cairo_win32_font_face_create_for_logfontw (LOGFONTW *logfont);
+
+cairo_public cairo_font_face_t *
+cairo_win32_font_face_create_for_hfont (HFONT font);
+
+cairo_public cairo_font_face_t *
+cairo_win32_font_face_create_for_logfontw_hfont (LOGFONTW *logfont, HFONT font);
+
+cairo_public cairo_status_t
+cairo_win32_scaled_font_select_font (cairo_scaled_font_t *scaled_font,
+                                    HDC                  hdc);
+
+cairo_public void
+cairo_win32_scaled_font_done_font (cairo_scaled_font_t *scaled_font);
+
+cairo_public double
+cairo_win32_scaled_font_get_metrics_factor (cairo_scaled_font_t *scaled_font);
+
+cairo_public void
+cairo_win32_scaled_font_get_logical_to_device (cairo_scaled_font_t *scaled_font,
+                                              cairo_matrix_t *logical_to_device);
+
+cairo_public void
+cairo_win32_scaled_font_get_device_to_logical (cairo_scaled_font_t *scaled_font,
+                                              cairo_matrix_t *device_to_logical);
+
+#endif /* CAIRO_HAS_WIN32_FONT */
+
+CAIRO_END_DECLS
+
+#else  /* CAIRO_HAS_WIN32_SURFACE */
+# error Cairo was not compiled with support for the win32 backend
+#endif /* CAIRO_HAS_WIN32_SURFACE */
+
+#endif /* _CAIRO_WIN32_H_ */
diff --git a/src/cairo-xcb-connection-core.c b/src/cairo-xcb-connection-core.c
new file mode 100644 (file)
index 0000000..fe86876
--- /dev/null
@@ -0,0 +1,270 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * Contributor(s):
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-xcb-private.h"
+
+#include <xcb/xcbext.h>
+
+xcb_pixmap_t
+_cairo_xcb_connection_create_pixmap (cairo_xcb_connection_t *connection,
+                                    uint8_t depth,
+                                    xcb_drawable_t drawable,
+                                    uint16_t width,
+                                    uint16_t height)
+{
+    xcb_pixmap_t pixmap = _cairo_xcb_connection_get_xid (connection);
+
+    assert (width > 0);
+    assert (height > 0);
+    xcb_create_pixmap (connection->xcb_connection,
+                      depth, pixmap, drawable,
+                      width, height);
+    return pixmap;
+}
+
+void
+_cairo_xcb_connection_free_pixmap (cairo_xcb_connection_t *connection,
+                                  xcb_pixmap_t pixmap)
+{
+    xcb_free_pixmap (connection->xcb_connection, pixmap);
+    _cairo_xcb_connection_put_xid (connection, pixmap);
+}
+
+xcb_gcontext_t
+_cairo_xcb_connection_create_gc (cairo_xcb_connection_t *connection,
+                                xcb_drawable_t drawable,
+                                uint32_t value_mask,
+                                uint32_t *values)
+{
+    xcb_gcontext_t gc = _cairo_xcb_connection_get_xid (connection);
+    xcb_create_gc (connection->xcb_connection, gc, drawable,
+                  value_mask, values);
+    return gc;
+}
+
+void
+_cairo_xcb_connection_free_gc (cairo_xcb_connection_t *connection,
+                              xcb_gcontext_t gc)
+{
+    xcb_free_gc (connection->xcb_connection, gc);
+    _cairo_xcb_connection_put_xid (connection, gc);
+}
+
+void
+_cairo_xcb_connection_change_gc (cairo_xcb_connection_t *connection,
+                                xcb_gcontext_t gc,
+                                uint32_t value_mask,
+                                uint32_t *values)
+{
+    xcb_change_gc (connection->xcb_connection, gc,
+                  value_mask, values);
+}
+
+void
+_cairo_xcb_connection_copy_area (cairo_xcb_connection_t *connection,
+                                xcb_drawable_t src,
+                                xcb_drawable_t dst,
+                                xcb_gcontext_t gc,
+                                int16_t src_x,
+                                int16_t src_y,
+                                int16_t dst_x,
+                                int16_t dst_y,
+                                uint16_t width,
+                                uint16_t height)
+{
+    xcb_copy_area (connection->xcb_connection, src, dst, gc,
+                  src_x, src_y, dst_x, dst_y, width, height);
+}
+
+void
+_cairo_xcb_connection_poly_fill_rectangle (cairo_xcb_connection_t *connection,
+                                          xcb_drawable_t dst,
+                                          xcb_gcontext_t gc,
+                                          uint32_t num_rectangles,
+                                          xcb_rectangle_t *rectangles)
+{
+    xcb_poly_fill_rectangle (connection->xcb_connection, dst, gc,
+                            num_rectangles, rectangles);
+}
+
+void
+_cairo_xcb_connection_put_image (cairo_xcb_connection_t *connection,
+                                xcb_drawable_t dst,
+                                xcb_gcontext_t gc,
+                                uint16_t width,
+                                uint16_t height,
+                                int16_t dst_x,
+                                int16_t dst_y,
+                                uint8_t depth,
+                                uint32_t stride,
+                                void *data)
+{
+    const uint32_t req_size = 18;
+    uint32_t length = height * stride;
+    uint32_t len = (req_size + length) >> 2;
+
+    if (len < connection->maximum_request_length) {
+       xcb_put_image (connection->xcb_connection, XCB_IMAGE_FORMAT_Z_PIXMAP,
+                      dst, gc, width, height, dst_x, dst_y, 0, depth,
+                      length, data);
+    } else {
+       int rows = (connection->maximum_request_length - req_size - 4) / stride;
+       if (rows > 0) {
+           do {
+               if (rows > height)
+                   rows = height;
+
+               length = rows * stride;
+
+               xcb_put_image (connection->xcb_connection, XCB_IMAGE_FORMAT_Z_PIXMAP,
+                              dst, gc, width, rows, dst_x, dst_y, 0, depth, length, data);
+
+               height -= rows;
+               dst_y += rows;
+               data = (char *) data + length;
+           } while (height);
+       } else {
+           ASSERT_NOT_REACHED;
+       }
+    }
+}
+
+void
+_cairo_xcb_connection_put_subimage (cairo_xcb_connection_t *connection,
+                                   xcb_drawable_t dst,
+                                   xcb_gcontext_t gc,
+                                   int16_t src_x,
+                                   int16_t src_y,
+                                   uint16_t width,
+                                   uint16_t height,
+                                   uint16_t cpp,
+                                   uint16_t stride,
+                                   int16_t dst_x,
+                                   int16_t dst_y,
+                                   uint8_t depth,
+                                   void *_data)
+{
+    xcb_protocol_request_t xcb_req = {
+       0 /* count */,
+       0 /* ext */,
+       XCB_PUT_IMAGE /* opcode */,
+       1 /* isvoid (doesn't cause a reply) */
+    };
+    xcb_put_image_request_t req;
+    struct iovec vec_stack[CAIRO_STACK_ARRAY_LENGTH (struct iovec)];
+    struct iovec *vec = vec_stack;
+    uint32_t len = 0;
+    uint8_t *data = _data;
+    int n = 3;
+    /* Two extra entries are needed for xcb, two for us */
+    int entries_needed = height + 2 + 2;
+
+    req.format = XCB_IMAGE_FORMAT_Z_PIXMAP;
+    req.drawable = dst;
+    req.gc = gc;
+    req.width = width;
+    req.height = height;
+    req.dst_x = dst_x;
+    req.dst_y = dst_y;
+    req.left_pad = 0;
+    req.depth = depth;
+    req.pad0[0] = 0;
+    req.pad0[1] = 0;
+
+    if (entries_needed > ARRAY_LENGTH (vec_stack)) {
+       vec = _cairo_malloc_ab (entries_needed, sizeof (struct iovec));
+       if (unlikely (vec == NULL)) {
+           /* XXX loop over ARRAY_LENGTH (vec_stack) */
+           return;
+       }
+    }
+
+    data += src_y * stride + src_x * cpp;
+    /* vec[1] will be used in XCB if it has to use BigRequests or insert a sync,
+     * vec[0] is used if the internal queue needs to be flushed. */
+    vec[2].iov_base = (char *) &req;
+    vec[2].iov_len = sizeof (req);
+
+    /* Now comes the actual data */
+    while (height--) {
+       vec[n].iov_base = data;
+       vec[n].iov_len = cpp * width;
+       len += cpp * width;
+       data += stride;
+       n++;
+    }
+
+    /* And again some padding */
+    vec[n].iov_base = 0;
+    vec[n].iov_len = -len & 3;
+    n++;
+
+    /* For efficiency reasons, this functions writes the request "directly" to
+     * the xcb connection to avoid having to copy the data around. */
+    assert (n == entries_needed);
+    xcb_req.count = n - 2;
+    xcb_send_request (connection->xcb_connection, 0, &vec[2], &xcb_req);
+
+    if (vec != vec_stack)
+       free (vec);
+}
+
+cairo_status_t
+_cairo_xcb_connection_get_image (cairo_xcb_connection_t *connection,
+                                xcb_drawable_t src,
+                                int16_t src_x,
+                                int16_t src_y,
+                                uint16_t width,
+                                uint16_t height,
+                                xcb_get_image_reply_t **reply)
+{
+    xcb_generic_error_t *error;
+
+    *reply = xcb_get_image_reply (connection->xcb_connection,
+                                 xcb_get_image (connection->xcb_connection,
+                                                XCB_IMAGE_FORMAT_Z_PIXMAP,
+                                                src,
+                                                src_x, src_y,
+                                                width, height,
+                                                (uint32_t) -1),
+
+                                 &error);
+    if (error) {
+       free (error);
+
+       free (*reply);
+       *reply = NULL;
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
diff --git a/src/cairo-xcb-connection-render.c b/src/cairo-xcb-connection-render.c
new file mode 100644 (file)
index 0000000..8d95509
--- /dev/null
@@ -0,0 +1,279 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * Contributor(s):
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-xcb-private.h"
+
+#include <xcb/xcbext.h>
+
+void
+_cairo_xcb_connection_render_create_picture (cairo_xcb_connection_t  *connection,
+                                            xcb_render_picture_t     picture,
+                                            xcb_drawable_t           drawable,
+                                            xcb_render_pictformat_t  format,
+                                            uint32_t                 value_mask,
+                                            uint32_t                *value_list)
+{
+    xcb_render_create_picture (connection->xcb_connection, picture, drawable,
+                              format, value_mask, value_list);
+}
+
+void
+_cairo_xcb_connection_render_change_picture (cairo_xcb_connection_t     *connection,
+                                            xcb_render_picture_t  picture,
+                                            uint32_t              value_mask,
+                                            uint32_t             *value_list)
+{
+    xcb_render_change_picture (connection->xcb_connection, picture,
+                              value_mask, value_list);
+}
+
+void
+_cairo_xcb_connection_render_set_picture_clip_rectangles (cairo_xcb_connection_t      *connection,
+                                                         xcb_render_picture_t   picture,
+                                                         int16_t                clip_x_origin,
+                                                         int16_t                clip_y_origin,
+                                                         uint32_t               rectangles_len,
+                                                         xcb_rectangle_t *rectangles)
+{
+    xcb_render_set_picture_clip_rectangles (connection->xcb_connection, picture,
+                                           clip_x_origin, clip_y_origin,
+                                           rectangles_len, rectangles);
+}
+
+void
+_cairo_xcb_connection_render_free_picture (cairo_xcb_connection_t *connection,
+                                          xcb_render_picture_t  picture)
+{
+    xcb_render_free_picture (connection->xcb_connection, picture);
+    _cairo_xcb_connection_put_xid (connection, picture);
+}
+
+void
+_cairo_xcb_connection_render_composite (cairo_xcb_connection_t     *connection,
+                                       uint8_t               op,
+                                       xcb_render_picture_t  src,
+                                       xcb_render_picture_t  mask,
+                                       xcb_render_picture_t  dst,
+                                       int16_t               src_x,
+                                       int16_t               src_y,
+                                       int16_t               mask_x,
+                                       int16_t               mask_y,
+                                       int16_t               dst_x,
+                                       int16_t               dst_y,
+                                       uint16_t              width,
+                                       uint16_t              height)
+{
+    xcb_render_composite (connection->xcb_connection, op, src, mask, dst,
+                         src_x, src_y, mask_x, mask_y, dst_x, dst_y, width, height);
+}
+
+void
+_cairo_xcb_connection_render_trapezoids (cairo_xcb_connection_t *connection,
+                                        uint8_t                       op,
+                                        xcb_render_picture_t          src,
+                                        xcb_render_picture_t          dst,
+                                        xcb_render_pictformat_t       mask_format,
+                                        int16_t                       src_x,
+                                        int16_t                       src_y,
+                                        uint32_t                      traps_len,
+                                        xcb_render_trapezoid_t *traps)
+{
+    xcb_render_trapezoids (connection->xcb_connection, op, src, dst,
+                          mask_format, src_x, src_y, traps_len, traps);
+}
+
+void
+_cairo_xcb_connection_render_create_glyph_set (cairo_xcb_connection_t  *connection,
+                                              xcb_render_glyphset_t     id,
+                                              xcb_render_pictformat_t  format)
+{
+    xcb_render_create_glyph_set (connection->xcb_connection, id, format);
+}
+
+void
+_cairo_xcb_connection_render_free_glyph_set (cairo_xcb_connection_t      *connection,
+                                            xcb_render_glyphset_t  glyphset)
+{
+    xcb_render_free_glyph_set (connection->xcb_connection, glyphset);
+    _cairo_xcb_connection_put_xid (connection, glyphset);
+}
+
+void
+_cairo_xcb_connection_render_add_glyphs (cairo_xcb_connection_t             *connection,
+                                        xcb_render_glyphset_t         glyphset,
+                                        uint32_t                      num_glyphs,
+                                        uint32_t               *glyphs_id,
+                                        xcb_render_glyphinfo_t *glyphs,
+                                        uint32_t                      data_len,
+                                        uint8_t                *data)
+{
+    xcb_render_add_glyphs (connection->xcb_connection, glyphset, num_glyphs,
+                                  glyphs_id, glyphs, data_len, data);
+}
+
+void
+_cairo_xcb_connection_render_free_glyphs (cairo_xcb_connection_t         *connection,
+                                         xcb_render_glyphset_t     glyphset,
+                                         uint32_t                  num_glyphs,
+                                         xcb_render_glyph_t *glyphs)
+{
+    xcb_render_free_glyphs (connection->xcb_connection, glyphset, num_glyphs, glyphs);
+}
+
+void
+_cairo_xcb_connection_render_composite_glyphs_8 (cairo_xcb_connection_t        *connection,
+                                                uint8_t                  op,
+                                                xcb_render_picture_t     src,
+                                                xcb_render_picture_t     dst,
+                                                xcb_render_pictformat_t  mask_format,
+                                                xcb_render_glyphset_t    glyphset,
+                                                int16_t                  src_x,
+                                                int16_t                  src_y,
+                                                uint32_t                 glyphcmds_len,
+                                                uint8_t           *glyphcmds)
+{
+    xcb_render_composite_glyphs_8 (connection->xcb_connection, op, src, dst, mask_format,
+                                  glyphset, src_x, src_y, glyphcmds_len, glyphcmds);
+}
+
+void
+_cairo_xcb_connection_render_composite_glyphs_16 (cairo_xcb_connection_t        *connection,
+                                                 uint8_t                  op,
+                                                 xcb_render_picture_t     src,
+                                                 xcb_render_picture_t     dst,
+                                                 xcb_render_pictformat_t  mask_format,
+                                                 xcb_render_glyphset_t    glyphset,
+                                                 int16_t                  src_x,
+                                                 int16_t                  src_y,
+                                                 uint32_t                 glyphcmds_len,
+                                                 uint8_t           *glyphcmds)
+{
+    xcb_render_composite_glyphs_16 (connection->xcb_connection, op, src, dst, mask_format,
+                                   glyphset, src_x, src_y, glyphcmds_len, glyphcmds);
+}
+
+void
+_cairo_xcb_connection_render_composite_glyphs_32 (cairo_xcb_connection_t        *connection,
+                                                 uint8_t                  op,
+                                                 xcb_render_picture_t     src,
+                                                 xcb_render_picture_t     dst,
+                                                 xcb_render_pictformat_t  mask_format,
+                                                 xcb_render_glyphset_t    glyphset,
+                                                 int16_t                  src_x,
+                                                 int16_t                  src_y,
+                                                 uint32_t                 glyphcmds_len,
+                                                 uint8_t           *glyphcmds)
+{
+    xcb_render_composite_glyphs_32 (connection->xcb_connection, op, src, dst, mask_format,
+                                   glyphset, src_x, src_y, glyphcmds_len, glyphcmds);
+}
+
+void
+_cairo_xcb_connection_render_fill_rectangles (cairo_xcb_connection_t      *connection,
+                                             uint8_t                op,
+                                             xcb_render_picture_t   dst,
+                                             xcb_render_color_t     color,
+                                             uint32_t               num_rects,
+                                             xcb_rectangle_t *rects)
+{
+    xcb_render_fill_rectangles (connection->xcb_connection, op, dst, color,
+                               num_rects, rects);
+}
+
+void
+_cairo_xcb_connection_render_set_picture_transform (cairo_xcb_connection_t       *connection,
+                                                   xcb_render_picture_t    picture,
+                                                   xcb_render_transform_t  *transform)
+{
+    xcb_render_set_picture_transform (connection->xcb_connection, picture, *transform);
+}
+
+void
+_cairo_xcb_connection_render_set_picture_filter (cairo_xcb_connection_t         *connection,
+                                                xcb_render_picture_t      picture,
+                                                uint16_t                  filter_len,
+                                                char               *filter)
+{
+    xcb_render_set_picture_filter (connection->xcb_connection, picture,
+                                  filter_len, filter, 0, NULL);
+}
+
+void
+_cairo_xcb_connection_render_create_solid_fill (cairo_xcb_connection_t     *connection,
+                                               xcb_render_picture_t  picture,
+                                               xcb_render_color_t    color)
+{
+    xcb_render_create_solid_fill (connection->xcb_connection, picture, color);
+}
+
+void
+_cairo_xcb_connection_render_create_linear_gradient (cairo_xcb_connection_t         *connection,
+                                                    xcb_render_picture_t      picture,
+                                                    xcb_render_pointfix_t     p1,
+                                                    xcb_render_pointfix_t     p2,
+                                                    uint32_t                  num_stops,
+                                                    xcb_render_fixed_t *stops,
+                                                    xcb_render_color_t *colors)
+{
+    xcb_render_create_linear_gradient (connection->xcb_connection, picture,
+                                      p1, p2, num_stops, stops, colors);
+}
+
+void
+_cairo_xcb_connection_render_create_radial_gradient (cairo_xcb_connection_t         *connection,
+                                                    xcb_render_picture_t      picture,
+                                                    xcb_render_pointfix_t     inner,
+                                                    xcb_render_pointfix_t     outer,
+                                                    xcb_render_fixed_t        inner_radius,
+                                                    xcb_render_fixed_t        outer_radius,
+                                                    uint32_t                  num_stops,
+                                                    xcb_render_fixed_t *stops,
+                                                    xcb_render_color_t *colors)
+{
+    xcb_render_create_radial_gradient (connection->xcb_connection, picture,
+                                      inner, outer, inner_radius, outer_radius,
+                                      num_stops, stops, colors);
+}
+
+void
+_cairo_xcb_connection_render_create_conical_gradient (cairo_xcb_connection_t         *connection,
+                                                     xcb_render_picture_t      picture,
+                                                     xcb_render_pointfix_t     center,
+                                                     xcb_render_fixed_t        angle,
+                                                     uint32_t                  num_stops,
+                                                     xcb_render_fixed_t *stops,
+                                                     xcb_render_color_t *colors)
+{
+    xcb_render_create_conical_gradient (connection->xcb_connection, picture,
+                                      center, angle, num_stops, stops, colors);
+}
diff --git a/src/cairo-xcb-connection-shm.c b/src/cairo-xcb-connection-shm.c
new file mode 100644 (file)
index 0000000..2ba9583
--- /dev/null
@@ -0,0 +1,113 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * Contributor(s):
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+
+#if CAIRO_HAS_XCB_SHM_FUNCTIONS
+
+#include "cairo-xcb-private.h"
+
+#include <xcb/xcbext.h>
+#include <xcb/shm.h>
+
+uint32_t
+_cairo_xcb_connection_shm_attach (cairo_xcb_connection_t *connection,
+                                 uint32_t id,
+                                 cairo_bool_t readonly)
+{
+    uint32_t segment = _cairo_xcb_connection_get_xid (connection);
+    xcb_shm_attach (connection->xcb_connection, segment, id, readonly);
+    return segment;
+}
+
+void
+_cairo_xcb_connection_shm_put_image (cairo_xcb_connection_t *connection,
+                                    xcb_drawable_t dst,
+                                    xcb_gcontext_t gc,
+                                    uint16_t total_width,
+                                    uint16_t total_height,
+                                    int16_t src_x,
+                                    int16_t src_y,
+                                    uint16_t width,
+                                    uint16_t height,
+                                    int16_t dst_x,
+                                    int16_t dst_y,
+                                    uint8_t depth,
+                                    uint32_t shm,
+                                    uint32_t offset)
+{
+    xcb_shm_put_image (connection->xcb_connection, dst, gc, total_width, total_height,
+                      src_x, src_y, width, height, dst_x, dst_y, depth,
+                      XCB_IMAGE_FORMAT_Z_PIXMAP, 0, shm, offset);
+}
+
+cairo_status_t
+_cairo_xcb_connection_shm_get_image (cairo_xcb_connection_t *connection,
+                                    xcb_drawable_t src,
+                                    int16_t src_x,
+                                    int16_t src_y,
+                                    uint16_t width,
+                                    uint16_t height,
+                                    uint32_t shmseg,
+                                    uint32_t offset)
+{
+    xcb_shm_get_image_reply_t *reply;
+    xcb_generic_error_t *error;
+
+    reply = xcb_shm_get_image_reply (connection->xcb_connection,
+                                    xcb_shm_get_image (connection->xcb_connection,
+                                                       src,
+                                                       src_x, src_y,
+                                                       width, height,
+                                                       (uint32_t) -1,
+                                                       XCB_IMAGE_FORMAT_Z_PIXMAP,
+                                                       shmseg, offset),
+                                    &error);
+    free (reply);
+
+    if (error) {
+       /* an error here should be impossible */
+       free (error);
+       return _cairo_error (CAIRO_STATUS_READ_ERROR);
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+void
+_cairo_xcb_connection_shm_detach (cairo_xcb_connection_t *connection,
+                                 uint32_t segment)
+{
+    xcb_shm_detach (connection->xcb_connection, segment);
+    _cairo_xcb_connection_put_xid (connection, segment);
+}
+
+#endif /* CAIRO_HAS_XCB_SHM_FUNCTIONS */
diff --git a/src/cairo-xcb-connection.c b/src/cairo-xcb-connection.c
new file mode 100644 (file)
index 0000000..2c63a93
--- /dev/null
@@ -0,0 +1,935 @@
+/* Cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * Authors:
+ *    Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+
+#include "cairoint.h"
+
+#include "cairo-xcb-private.h"
+#include "cairo-hash-private.h"
+#include "cairo-freelist-private.h"
+#include "cairo-list-private.h"
+
+#include <xcb/xcbext.h>
+#include <xcb/bigreq.h>
+#include <errno.h>
+
+#if CAIRO_HAS_XCB_SHM_FUNCTIONS
+#include <sys/ipc.h>
+#include <sys/shm.h>
+#include <xcb/shm.h>
+#endif
+
+typedef struct _cairo_xcb_xrender_format {
+    cairo_hash_entry_t key;
+    xcb_render_pictformat_t xrender_format;
+} cairo_xcb_xrender_format_t;
+
+typedef struct _cairo_xcb_xid {
+    cairo_list_t link;
+    uint32_t xid;
+} cairo_xcb_xid_t;
+
+#define XCB_RENDER_AT_LEAST(V, major, minor)   \
+       (((V)->major_version > major) ||                        \
+        (((V)->major_version == major) && ((V)->minor_version >= minor)))
+
+#define XCB_RENDER_HAS_CREATE_PICTURE(surface)         XCB_RENDER_AT_LEAST((surface), 0, 0)
+#define XCB_RENDER_HAS_COMPOSITE(surface)              XCB_RENDER_AT_LEAST((surface), 0, 0)
+#define XCB_RENDER_HAS_COMPOSITE_TEXT(surface)         XCB_RENDER_AT_LEAST((surface), 0, 0)
+
+#define XCB_RENDER_HAS_FILL_RECTANGLES(surface)                XCB_RENDER_AT_LEAST((surface), 0, 1)
+
+#define XCB_RENDER_HAS_DISJOINT(surface)               XCB_RENDER_AT_LEAST((surface), 0, 2)
+#define XCB_RENDER_HAS_CONJOINT(surface)               XCB_RENDER_AT_LEAST((surface), 0, 2)
+
+#define XCB_RENDER_HAS_TRAPEZOIDS(surface)             XCB_RENDER_AT_LEAST((surface), 0, 4)
+#define XCB_RENDER_HAS_TRIANGLES(surface)              XCB_RENDER_AT_LEAST((surface), 0, 4)
+#define XCB_RENDER_HAS_TRISTRIP(surface)               XCB_RENDER_AT_LEAST((surface), 0, 4)
+#define XCB_RENDER_HAS_TRIFAN(surface)                 XCB_RENDER_AT_LEAST((surface), 0, 4)
+
+#define XCB_RENDER_HAS_PICTURE_TRANSFORM(surface)      XCB_RENDER_AT_LEAST((surface), 0, 6)
+#define XCB_RENDER_HAS_FILTERS(surface)                        XCB_RENDER_AT_LEAST((surface), 0, 6)
+
+#define XCB_RENDER_HAS_EXTENDED_REPEAT(surface)        XCB_RENDER_AT_LEAST((surface), 0, 10)
+#define XCB_RENDER_HAS_GRADIENTS(surface)      XCB_RENDER_AT_LEAST((surface), 0, 10)
+
+#define XCB_RENDER_HAS_PDF_OPERATORS(surface)  XCB_RENDER_AT_LEAST((surface), 0, 11)
+
+static cairo_list_t connections;
+
+static cairo_status_t
+_cairo_xcb_connection_find_visual_formats (cairo_xcb_connection_t *connection,
+                                         const xcb_render_query_pict_formats_reply_t *formats)
+{
+    xcb_render_pictscreen_iterator_t screens;
+    xcb_render_pictdepth_iterator_t depths;
+    xcb_render_pictvisual_iterator_t visuals;
+
+    for (screens = xcb_render_query_pict_formats_screens_iterator (formats);
+        screens.rem;
+        xcb_render_pictscreen_next (&screens))
+    {
+       for (depths = xcb_render_pictscreen_depths_iterator (screens.data);
+            depths.rem;
+            xcb_render_pictdepth_next (&depths))
+       {
+           for (visuals = xcb_render_pictdepth_visuals_iterator (depths.data);
+                visuals.rem;
+                xcb_render_pictvisual_next (&visuals))
+           {
+               cairo_xcb_xrender_format_t *f;
+               cairo_status_t status;
+
+               f = malloc (sizeof (cairo_xcb_xrender_format_t));
+               if (unlikely (f == NULL))
+                   return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+               f->key.hash = visuals.data->visual;
+               f->xrender_format = visuals.data->format;
+               status = _cairo_hash_table_insert (connection->visual_to_xrender_format,
+                                                  &f->key);
+               if (unlikely (status))
+                   return status;
+           }
+       }
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+#if 0
+static xcb_format_t *
+find_format_for_depth (const xcb_setup_t *setup, uint8_t depth)
+{
+    xcb_format_t *fmt = xcb_setup_pixmap_formats (setup);
+    xcb_format_t *fmtend = fmt + xcb_setup_pixmap_formats_length (setup);
+
+    for (; fmt != fmtend; ++fmt)
+       if (fmt->depth == depth)
+           return fmt;
+
+    return 0;
+}
+#endif
+
+static cairo_status_t
+_cairo_xcb_connection_parse_xrender_formats (cairo_xcb_connection_t *connection,
+                                            const xcb_render_query_pict_formats_reply_t *formats)
+{
+    xcb_render_pictforminfo_iterator_t i;
+    cairo_status_t status;
+
+    for (i = xcb_render_query_pict_formats_formats_iterator (formats);
+        i.rem;
+        xcb_render_pictforminfo_next (&i))
+    {
+       cairo_format_masks_t masks;
+       pixman_format_code_t pixman_format;
+
+       if (i.data->type != XCB_RENDER_PICT_TYPE_DIRECT)
+           continue;
+
+       masks.alpha_mask =
+           (unsigned long) i.data->direct.alpha_mask << i.data->direct.alpha_shift;
+       masks.red_mask =
+           (unsigned long) i.data->direct.red_mask << i.data->direct.red_shift;
+       masks.green_mask =
+           (unsigned long) i.data->direct.green_mask << i.data->direct.green_shift;
+       masks.blue_mask =
+           (unsigned long) i.data->direct.blue_mask << i.data->direct.blue_shift;
+       masks.bpp = i.data->depth;
+
+       if (_pixman_format_from_masks (&masks, &pixman_format)) {
+           cairo_hash_entry_t key;
+
+           key.hash = pixman_format;
+           if (! _cairo_hash_table_lookup (connection->xrender_formats, &key)) {
+               cairo_xcb_xrender_format_t *f;
+
+               f = malloc (sizeof (cairo_xcb_xrender_format_t));
+               if (unlikely (f == NULL))
+                   return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+               f->key.hash = pixman_format;
+               f->xrender_format = i.data->id;
+               status = _cairo_hash_table_insert (connection->xrender_formats,
+                                                  &f->key);
+               if (unlikely (status))
+                   return status;
+
+#if 0
+               printf ("xrender %x -> (%lx, %lx, %lx, %lx, %d) %x [%d, %d]\n",
+                       i.data->id,
+                       masks.alpha_mask,
+                       masks.red_mask,
+                       masks.green_mask,
+                       masks.blue_mask,
+                       masks.bpp,
+                       pixman_format,
+                       PIXMAN_FORMAT_DEPTH(pixman_format),
+                       PIXMAN_FORMAT_BPP(pixman_format));
+#endif
+           }
+       }
+    }
+
+    status = _cairo_xcb_connection_find_visual_formats (connection, formats);
+    if (unlikely (status))
+       return status;
+
+    connection->standard_formats[CAIRO_FORMAT_A1] =
+       _cairo_xcb_connection_get_xrender_format (connection, PIXMAN_a1);
+
+    connection->standard_formats[CAIRO_FORMAT_A8] =
+       _cairo_xcb_connection_get_xrender_format (connection, PIXMAN_a8);
+
+    connection->standard_formats[CAIRO_FORMAT_RGB24] =
+       _cairo_xcb_connection_get_xrender_format (connection,
+                                                 PIXMAN_FORMAT (24,
+                                                                PIXMAN_TYPE_ARGB,
+                                                                0, 8, 8, 8));
+    if (connection->standard_formats[CAIRO_FORMAT_RGB24] == XCB_NONE) {
+       connection->standard_formats[CAIRO_FORMAT_RGB24] =
+           _cairo_xcb_connection_get_xrender_format (connection,
+                                                     PIXMAN_FORMAT (24, PIXMAN_TYPE_ABGR,
+                                                                    0, 8, 8, 8));
+    }
+
+    connection->standard_formats[CAIRO_FORMAT_ARGB32] =
+       _cairo_xcb_connection_get_xrender_format (connection, PIXMAN_a8r8g8b8);
+    if (connection->standard_formats[CAIRO_FORMAT_ARGB32] == XCB_NONE) {
+       connection->standard_formats[CAIRO_FORMAT_ARGB32] =
+           _cairo_xcb_connection_get_xrender_format (connection, PIXMAN_a8b8g8r8);
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+/*
+ * We require support for depth 1, 8, 24 and 32 pixmaps
+ */
+#define DEPTH_MASK(d)  (1 << ((d) - 1))
+#define REQUIRED_DEPTHS        (DEPTH_MASK(1) | \
+                        DEPTH_MASK(8) | \
+                        DEPTH_MASK(24) | \
+                        DEPTH_MASK(32))
+static cairo_bool_t
+pixmap_depths_usable (cairo_xcb_connection_t *connection,
+                     uint32_t missing,
+                     xcb_drawable_t root)
+{
+    xcb_connection_t *c = connection->xcb_connection;
+    xcb_void_cookie_t create_cookie[32];
+    xcb_pixmap_t pixmap;
+    cairo_bool_t success = TRUE;
+    int depth, i, j;
+
+    pixmap = _cairo_xcb_connection_get_xid (connection);
+
+    for (depth = 1, i = 0; depth <= 32; depth++) {
+       if (missing & DEPTH_MASK(depth)) {
+           create_cookie[i] = xcb_create_pixmap_checked (c, depth, pixmap, root, 1, 1);
+           xcb_free_pixmap (c, pixmap);
+           if (!create_cookie[i].sequence) {
+               success = FALSE;
+               break;
+           }
+           i++;
+       }
+    }
+
+    for (j = 0; j < i; j++) {
+       xcb_generic_error_t *create_error = xcb_request_check (c, create_cookie[j]);
+       success &= create_error == NULL;
+       free (create_error);
+    }
+
+    _cairo_xcb_connection_put_xid (connection, pixmap);
+
+    return success;
+}
+
+static cairo_bool_t
+has_required_depths (cairo_xcb_connection_t *connection)
+{
+    xcb_screen_iterator_t screens;
+
+    for (screens = xcb_setup_roots_iterator (connection->root);
+        screens.rem;
+        xcb_screen_next (&screens))
+    {
+       xcb_depth_iterator_t depths;
+       uint32_t missing = REQUIRED_DEPTHS;
+
+       for (depths = xcb_screen_allowed_depths_iterator (screens.data);
+            depths.rem;
+            xcb_depth_next (&depths))
+       {
+           missing &= ~DEPTH_MASK (depths.data->depth);
+       }
+       if (missing == 0)
+           continue;
+
+       /*
+        * Ok, this is ugly.  It should be sufficient at this
+        * point to just return false, but Xinerama is broken at
+        * this point and only advertises depths which have an
+        * associated visual.  Of course, the other depths still
+        * work, but the only way to find out is to try them.
+        */
+       if (! pixmap_depths_usable (connection, missing, screens.data->root))
+           return FALSE;
+    }
+
+    return TRUE;
+}
+
+static xcb_render_query_version_reply_t *
+_render_restrict_env(xcb_render_query_version_reply_t *version)
+{
+    const char *env;
+
+    if (version == NULL)
+       return NULL;
+
+    env = getenv ("CAIRO_DEBUG");
+    if (env != NULL)
+       env = strstr (env, "xrender-version=");
+    if (env != NULL) {
+       int max_render_major, max_render_minor;
+
+       env += sizeof ("xrender-version=") - 1;
+       if (sscanf (env, "%d.%d", &max_render_major, &max_render_minor) != 2)
+           max_render_major = max_render_minor = -1;
+
+       if (max_render_major < 0 || max_render_minor < 0) {
+           free (version);
+           return NULL;
+       }
+
+       if (max_render_major < (int) version->major_version ||
+           (max_render_major == (int) version->major_version &&
+            max_render_minor < (int) version->minor_version))
+       {
+           version->major_version = max_render_major;
+           version->minor_version = max_render_minor;
+       }
+    }
+
+    return version;
+}
+
+static cairo_status_t
+_cairo_xcb_connection_query_render (cairo_xcb_connection_t *connection)
+{
+    xcb_connection_t *c = connection->xcb_connection;
+    xcb_render_query_version_cookie_t version_cookie;
+    xcb_render_query_pict_formats_cookie_t formats_cookie;
+    xcb_render_query_version_reply_t *version;
+    xcb_render_query_pict_formats_reply_t *formats;
+    cairo_status_t status;
+    cairo_bool_t present;
+
+    version_cookie = xcb_render_query_version (c, XCB_RENDER_MAJOR_VERSION, XCB_RENDER_MINOR_VERSION);
+    formats_cookie = xcb_render_query_pict_formats (c);
+
+    present = has_required_depths (connection);
+    version = xcb_render_query_version_reply (c, version_cookie, 0);
+    formats = xcb_render_query_pict_formats_reply (c, formats_cookie, 0);
+
+    version = _render_restrict_env (version);
+
+    if (! present || version == NULL || formats == NULL) {
+       free (version);
+       free (formats);
+       return CAIRO_STATUS_SUCCESS;
+    }
+
+    /* always true if the extension is present (i.e. >= 0.0) */
+    connection->flags |= CAIRO_XCB_HAS_RENDER;
+    connection->flags |= CAIRO_XCB_RENDER_HAS_COMPOSITE;
+    connection->flags |= CAIRO_XCB_RENDER_HAS_COMPOSITE_GLYPHS;
+
+    if (XCB_RENDER_HAS_FILL_RECTANGLES (version))
+       connection->flags |= CAIRO_XCB_RENDER_HAS_FILL_RECTANGLES;
+
+    if (XCB_RENDER_HAS_TRAPEZOIDS (version))
+       connection->flags |= CAIRO_XCB_RENDER_HAS_COMPOSITE_TRAPEZOIDS;
+
+    if (XCB_RENDER_HAS_PICTURE_TRANSFORM (version))
+       connection->flags |= CAIRO_XCB_RENDER_HAS_PICTURE_TRANSFORM;
+
+    if (XCB_RENDER_HAS_FILTERS (version))
+       connection->flags |= CAIRO_XCB_RENDER_HAS_FILTERS;
+
+    if (XCB_RENDER_HAS_PDF_OPERATORS (version))
+       connection->flags |= CAIRO_XCB_RENDER_HAS_PDF_OPERATORS;
+
+    if (XCB_RENDER_HAS_EXTENDED_REPEAT (version))
+       connection->flags |= CAIRO_XCB_RENDER_HAS_EXTENDED_REPEAT;
+
+    if (XCB_RENDER_HAS_GRADIENTS (version))
+       connection->flags |= CAIRO_XCB_RENDER_HAS_GRADIENTS;
+
+    free (version);
+
+    status = _cairo_xcb_connection_parse_xrender_formats (connection, formats);
+    free (formats);
+
+    return status;
+}
+
+#if 0
+static void
+_cairo_xcb_connection_query_cairo (cairo_xcb_connection_t *connection)
+{
+    xcb_connection_t *c = connection->xcb_connection;
+    xcb_cairo_query_version_reply_t *version;
+
+    version = xcb_cairo_query_version_reply (c,
+                                            xcb_cairo_query_version (c, 0, 0),
+                                            0);
+
+    free (version);
+}
+#endif
+
+#if CAIRO_HAS_XCB_SHM_FUNCTIONS
+static cairo_bool_t
+can_use_shm (cairo_xcb_connection_t *connection)
+{
+    cairo_bool_t success = TRUE;
+    xcb_connection_t *c = connection->xcb_connection;
+    xcb_void_cookie_t cookie[2];
+    xcb_generic_error_t *error;
+    int shmid;
+    uint32_t shmseg;
+    void *ptr;
+
+    shmid = shmget (IPC_PRIVATE, 0x1000, IPC_CREAT | 0600);
+    if (shmid == -1)
+       return FALSE;
+
+    ptr = shmat (shmid, NULL, 0);
+    if (ptr == (char *) -1) {
+       shmctl (shmid, IPC_RMID, NULL);
+       return FALSE;
+    }
+
+    shmseg = _cairo_xcb_connection_get_xid (connection);
+    cookie[0] = xcb_shm_attach_checked (c, shmseg, shmid, FALSE);
+    cookie[1] = xcb_shm_detach_checked (c, shmseg);
+    _cairo_xcb_connection_put_xid (connection, shmseg);
+
+    error = xcb_request_check (c, cookie[0]);
+    if (error != NULL)
+       success = FALSE;
+
+    error = xcb_request_check (c, cookie[1]);
+    if (error != NULL)
+       success = FALSE;
+
+    shmctl (shmid, IPC_RMID, NULL);
+    shmdt (ptr);
+
+    return success;
+}
+
+static void
+_cairo_xcb_connection_query_shm (cairo_xcb_connection_t *connection)
+{
+    xcb_connection_t *c = connection->xcb_connection;
+    xcb_shm_query_version_reply_t *version;
+
+    version = xcb_shm_query_version_reply (c, xcb_shm_query_version (c), 0);
+    if (version == NULL)
+       return;
+
+    free (version);
+
+    if (can_use_shm (connection))
+       connection->flags |= CAIRO_XCB_HAS_SHM;
+}
+#endif
+
+static cairo_status_t
+_device_flush (void *device)
+{
+    cairo_xcb_connection_t *connection = device;
+    cairo_status_t status;
+
+    status = cairo_device_acquire (&connection->device);
+    if (unlikely (status))
+       return status;
+
+#if CAIRO_HAS_XCB_SHM_FUNCTIONS
+    _cairo_xcb_connection_shm_mem_pools_flush (connection);
+#endif
+
+    xcb_flush (connection->xcb_connection);
+
+    cairo_device_release (&connection->device);
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_pluck_xrender_format (void *entry,
+                      void *closure)
+{
+    _cairo_hash_table_remove (closure, entry);
+    free (entry);
+}
+
+static void
+_device_finish (void *device)
+{
+    cairo_xcb_connection_t *connection = device;
+    cairo_bool_t was_cached = FALSE;
+
+    if (! cairo_list_is_empty (&connection->link)) {
+       CAIRO_MUTEX_LOCK (_cairo_xcb_connections_mutex);
+       cairo_list_del (&connection->link);
+       CAIRO_MUTEX_UNLOCK (_cairo_xcb_connections_mutex);
+       was_cached = TRUE;
+    }
+
+    while (! cairo_list_is_empty (&connection->fonts)) {
+       cairo_xcb_font_t *font;
+
+       font = cairo_list_first_entry (&connection->fonts,
+                                      cairo_xcb_font_t,
+                                      link);
+       _cairo_xcb_font_close (font);
+    }
+
+    while (! cairo_list_is_empty (&connection->screens)) {
+       cairo_xcb_screen_t *screen;
+
+       screen = cairo_list_first_entry (&connection->screens,
+                                        cairo_xcb_screen_t,
+                                        link);
+       _cairo_xcb_screen_finish (screen);
+    }
+
+#if CAIRO_HAS_XCB_SHM_FUNCTIONS
+    /* _cairo_xcb_screen_finish finishes surfaces. If any of those surfaces had
+     * a fallback image, we might have done a SHM PutImage. */
+    _cairo_xcb_connection_shm_mem_pools_flush (connection);
+#endif
+
+    if (was_cached)
+       cairo_device_destroy (device);
+}
+
+static void
+_device_destroy (void *device)
+{
+    cairo_xcb_connection_t *connection = device;
+
+    _cairo_hash_table_foreach (connection->xrender_formats,
+                              _pluck_xrender_format, connection->xrender_formats);
+    _cairo_hash_table_destroy (connection->xrender_formats);
+
+    _cairo_hash_table_foreach (connection->visual_to_xrender_format,
+                              _pluck_xrender_format,
+                              connection->visual_to_xrender_format);
+    _cairo_hash_table_destroy (connection->visual_to_xrender_format);
+
+#if CAIRO_HAS_XCB_SHM_FUNCTIONS
+    _cairo_xcb_connection_shm_mem_pools_fini (connection);
+#endif
+    _cairo_freepool_fini (&connection->shm_info_freelist);
+
+    _cairo_freepool_fini (&connection->xid_pool);
+
+    CAIRO_MUTEX_FINI (connection->shm_mutex);
+    CAIRO_MUTEX_FINI (connection->screens_mutex);
+
+    free (connection);
+}
+
+static const cairo_device_backend_t _cairo_xcb_device_backend = {
+    CAIRO_DEVICE_TYPE_XCB,
+
+    NULL, NULL, /* lock, unlock */
+
+    _device_flush,
+    _device_finish,
+    _device_destroy,
+};
+
+cairo_xcb_connection_t *
+_cairo_xcb_connection_get (xcb_connection_t *xcb_connection)
+{
+    cairo_xcb_connection_t *connection;
+    const xcb_query_extension_reply_t *ext;
+    cairo_status_t status;
+
+    CAIRO_MUTEX_INITIALIZE ();
+
+    CAIRO_MUTEX_LOCK (_cairo_xcb_connections_mutex);
+    if (connections.next == NULL) {
+       /* XXX _cairo_init () */
+       cairo_list_init (&connections);
+    }
+
+    cairo_list_foreach_entry (connection,
+                             cairo_xcb_connection_t,
+                             &connections,
+                             link)
+    {
+       if (connection->xcb_connection == xcb_connection) {
+           /* Maintain MRU order. */
+           if (connections.next != &connection->link)
+               cairo_list_move (&connection->link, &connections);
+
+           goto unlock;
+       }
+    }
+
+    connection = malloc (sizeof (cairo_xcb_connection_t));
+    if (unlikely (connection == NULL))
+       goto unlock;
+
+    _cairo_device_init (&connection->device, &_cairo_xcb_device_backend);
+
+    connection->xcb_connection = xcb_connection;
+
+    cairo_list_init (&connection->fonts);
+    cairo_list_init (&connection->screens);
+    cairo_list_init (&connection->link);
+    connection->xrender_formats = _cairo_hash_table_create (NULL);
+    if (connection->xrender_formats == NULL) {
+       CAIRO_MUTEX_FINI (connection->device.mutex);
+       free (connection);
+       connection = NULL;
+       goto unlock;
+    }
+
+    connection->visual_to_xrender_format = _cairo_hash_table_create (NULL);
+    if (connection->visual_to_xrender_format == NULL) {
+       _cairo_hash_table_destroy (connection->xrender_formats);
+       CAIRO_MUTEX_FINI (connection->device.mutex);
+       free (connection);
+       connection = NULL;
+       goto unlock;
+    }
+
+    cairo_list_init (&connection->free_xids);
+    _cairo_freepool_init (&connection->xid_pool,
+                         sizeof (cairo_xcb_xid_t));
+
+    cairo_list_init (&connection->shm_pools);
+    cairo_list_init (&connection->shm_pending);
+    _cairo_freepool_init (&connection->shm_info_freelist,
+                         sizeof (cairo_xcb_shm_info_t));
+
+    connection->maximum_request_length =
+       xcb_get_maximum_request_length (xcb_connection);
+
+    CAIRO_MUTEX_INIT (connection->shm_mutex);
+    CAIRO_MUTEX_INIT (connection->screens_mutex);
+
+    CAIRO_MUTEX_LOCK (connection->device.mutex);
+
+    connection->flags = 0;
+    connection->force_precision = -1;
+
+    xcb_prefetch_extension_data (xcb_connection, &xcb_big_requests_id);
+    xcb_prefetch_extension_data (xcb_connection, &xcb_render_id);
+#if CAIRO_HAS_XCB_SHM_FUNCTIONS
+    xcb_prefetch_extension_data (xcb_connection, &xcb_shm_id);
+#endif
+#if 0
+    xcb_prefetch_extension_data (xcb_connection, &xcb_cairo_id);
+#endif
+
+    xcb_prefetch_maximum_request_length (xcb_connection);
+
+    connection->root = xcb_get_setup (xcb_connection);
+    connection->render = NULL;
+    ext = xcb_get_extension_data (xcb_connection, &xcb_render_id);
+    if (ext != NULL && ext->present) {
+       status = _cairo_xcb_connection_query_render (connection);
+       if (unlikely (status)) {
+           CAIRO_MUTEX_UNLOCK (connection->device.mutex);
+           _cairo_xcb_connection_destroy (connection);
+           connection = NULL;
+           goto unlock;
+       }
+
+       connection->render = ext;
+    }
+
+#if 0
+    ext = xcb_get_extension_data (connection, &xcb_cairo_id);
+    if (ext != NULL && ext->present)
+       _cairo_xcb_connection_query_cairo (connection);
+#endif
+
+    connection->shm = NULL;
+#if CAIRO_HAS_XCB_SHM_FUNCTIONS
+    ext = xcb_get_extension_data (xcb_connection, &xcb_shm_id);
+    if (ext != NULL && ext->present) {
+       _cairo_xcb_connection_query_shm (connection);
+       connection->shm = ext;
+    }
+#endif
+
+    connection->original_flags = connection->flags;
+
+    CAIRO_MUTEX_UNLOCK (connection->device.mutex);
+
+    cairo_list_add (&connection->link, &connections);
+unlock:
+    CAIRO_MUTEX_UNLOCK (_cairo_xcb_connections_mutex);
+
+    return connection;
+}
+
+xcb_render_pictformat_t
+_cairo_xcb_connection_get_xrender_format (cairo_xcb_connection_t *connection,
+                                         pixman_format_code_t pixman_format)
+{
+    cairo_hash_entry_t key;
+    cairo_xcb_xrender_format_t *format;
+
+    key.hash = pixman_format;
+    format = _cairo_hash_table_lookup (connection->xrender_formats, &key);
+    return format ? format->xrender_format : XCB_NONE;
+}
+
+xcb_render_pictformat_t
+_cairo_xcb_connection_get_xrender_format_for_visual (cairo_xcb_connection_t *connection,
+                                                    const xcb_visualid_t visual)
+{
+    cairo_hash_entry_t key;
+    cairo_xcb_xrender_format_t *format;
+
+    key.hash = visual;
+    format = _cairo_hash_table_lookup (connection->visual_to_xrender_format, &key);
+    return format ? format->xrender_format : XCB_NONE;
+}
+
+void
+_cairo_xcb_connection_put_xid (cairo_xcb_connection_t *connection,
+                              uint32_t xid)
+{
+    cairo_xcb_xid_t *cache;
+
+    assert (CAIRO_MUTEX_IS_LOCKED (connection->device.mutex));
+    cache = _cairo_freepool_alloc (&connection->xid_pool);
+    if (likely (cache != NULL)) {
+       cache->xid = xid;
+       cairo_list_add (&cache->link, &connection->free_xids);
+    }
+}
+
+uint32_t
+_cairo_xcb_connection_get_xid (cairo_xcb_connection_t *connection)
+{
+    uint32_t xid;
+
+    assert (CAIRO_MUTEX_IS_LOCKED (connection->device.mutex));
+    if (! cairo_list_is_empty (&connection->free_xids)) {
+       cairo_xcb_xid_t *cache;
+
+       cache = cairo_list_first_entry (&connection->free_xids,
+                                       cairo_xcb_xid_t,
+                                       link);
+       xid = cache->xid;
+
+       cairo_list_del (&cache->link);
+       _cairo_freepool_free (&connection->xid_pool, cache);
+    } else {
+       xid = xcb_generate_id (connection->xcb_connection);
+    }
+
+    return xid;
+}
+
+xcb_connection_t *
+cairo_xcb_device_get_connection (cairo_device_t *device)
+{
+    if (device->backend->type != CAIRO_DEVICE_TYPE_XCB)
+           return NULL;
+
+    return ((cairo_xcb_connection_t *)device)->xcb_connection;
+}
+
+/* public (debug) interface */
+
+/**
+ * cairo_xcb_device_debug_cap_xshm_version:
+ * @device: a #cairo_device_t for the XCB backend
+ * @major_version: major version to restrict to
+ * @minor_version: minor version to restrict to
+ *
+ * Restricts all future XCB surfaces for this devices to the specified version
+ * of the SHM extension. This function exists solely for debugging purpose.
+ * It let's you find out how cairo would behave with an older version of
+ * the SHM extension.
+ *
+ * Use the special values -1 and -1 for disabling the SHM extension.
+ **/
+void
+cairo_xcb_device_debug_cap_xshm_version (cairo_device_t *device,
+                                         int major_version,
+                                         int minor_version)
+{
+    cairo_xcb_connection_t *connection = (cairo_xcb_connection_t *) device;
+
+    if (device->backend->type != CAIRO_DEVICE_TYPE_XCB) {
+       _cairo_device_set_error (device, CAIRO_STATUS_DEVICE_TYPE_MISMATCH);
+       return;
+    }
+
+    /* First reset all the SHM flags to their original value. This works
+     * because we only ever clear bits after the connection was created.
+     */
+    connection->flags |= (connection->original_flags & CAIRO_XCB_SHM_MASK);
+
+    /* clear any flags that are inappropriate for the desired version */
+    if (major_version < 0 && minor_version < 0) {
+       connection->flags &= ~(CAIRO_XCB_HAS_SHM);
+    }
+}
+
+/**
+ * cairo_xcb_device_debug_cap_xrender_version:
+ * @device: a #cairo_device_t for the XCB backend
+ * @major_version: major version to restrict to
+ * @minor_version: minor version to restrict to
+ *
+ * Restricts all future XCB surfaces for this devices to the specified version
+ * of the RENDER extension. This function exists solely for debugging purpose.
+ * It let's you find out how cairo would behave with an older version of
+ * the RENDER extension.
+ *
+ * Use the special values -1 and -1 for disabling the RENDER extension.
+ **/
+void
+cairo_xcb_device_debug_cap_xrender_version (cairo_device_t *device,
+                                            int major_version,
+                                            int minor_version)
+{
+    cairo_xcb_connection_t *connection = (cairo_xcb_connection_t *) device;
+
+    if (device->backend->type != CAIRO_DEVICE_TYPE_XCB) {
+       _cairo_device_set_error (device, CAIRO_STATUS_DEVICE_TYPE_MISMATCH);
+       return;
+    }
+
+    /* First reset all the RENDER flags to their original value. This works
+     * because we only ever clear bits after the connection was created.
+     */
+    connection->flags |= (connection->original_flags & CAIRO_XCB_RENDER_MASK);
+
+    /* clear any flags that are inappropriate for the desired version */
+    if (major_version < 0 && minor_version < 0) {
+       connection->flags &= ~(CAIRO_XCB_HAS_RENDER |
+                              CAIRO_XCB_RENDER_HAS_COMPOSITE |
+                              CAIRO_XCB_RENDER_HAS_COMPOSITE_GLYPHS |
+                              CAIRO_XCB_RENDER_HAS_FILL_RECTANGLES |
+                              CAIRO_XCB_RENDER_HAS_COMPOSITE_TRAPEZOIDS |
+                              CAIRO_XCB_RENDER_HAS_PICTURE_TRANSFORM |
+                              CAIRO_XCB_RENDER_HAS_FILTERS |
+                              CAIRO_XCB_RENDER_HAS_PDF_OPERATORS |
+                              CAIRO_XCB_RENDER_HAS_EXTENDED_REPEAT |
+                              CAIRO_XCB_RENDER_HAS_GRADIENTS);
+    } else {
+       xcb_render_query_version_reply_t version;
+
+       version.major_version = major_version;
+       version.minor_version = minor_version;
+
+       if (! XCB_RENDER_HAS_FILL_RECTANGLES (&version))
+           connection->flags &= ~CAIRO_XCB_RENDER_HAS_FILL_RECTANGLES;
+
+       if (! XCB_RENDER_HAS_TRAPEZOIDS (&version))
+           connection->flags &= ~CAIRO_XCB_RENDER_HAS_COMPOSITE_TRAPEZOIDS;
+
+       if (! XCB_RENDER_HAS_PICTURE_TRANSFORM (&version))
+           connection->flags &= ~CAIRO_XCB_RENDER_HAS_PICTURE_TRANSFORM;
+
+       if (! XCB_RENDER_HAS_FILTERS (&version))
+           connection->flags &= ~CAIRO_XCB_RENDER_HAS_FILTERS;
+
+       if (! XCB_RENDER_HAS_PDF_OPERATORS (&version))
+           connection->flags &= ~CAIRO_XCB_RENDER_HAS_PDF_OPERATORS;
+
+       if (! XCB_RENDER_HAS_EXTENDED_REPEAT (&version))
+           connection->flags &= ~CAIRO_XCB_RENDER_HAS_EXTENDED_REPEAT;
+
+       if (! XCB_RENDER_HAS_GRADIENTS (&version))
+           connection->flags &= ~CAIRO_XCB_RENDER_HAS_GRADIENTS;
+    }
+}
+
+
+void
+cairo_xcb_device_debug_set_precision (cairo_device_t *device,
+                                     int precision)
+{
+    if (device == NULL || device->status)
+       return;
+    if (device->backend->type != CAIRO_DEVICE_TYPE_XCB) {
+       cairo_status_t status;
+
+       status = _cairo_device_set_error (device, CAIRO_STATUS_DEVICE_TYPE_MISMATCH);
+       (void) status;
+       return;
+    }
+
+    ((cairo_xcb_connection_t *) device)->force_precision = precision;
+}
+#if CAIRO_HAS_XLIB_XCB_FUNCTIONS
+slim_hidden_def (cairo_xcb_device_debug_set_precision);
+#endif
+
+int
+cairo_xcb_device_debug_get_precision (cairo_device_t *device)
+{
+    if (device == NULL || device->status)
+       return -1;
+    if (device->backend->type != CAIRO_DEVICE_TYPE_XCB) {
+       cairo_status_t status;
+
+       status = _cairo_device_set_error (device, CAIRO_STATUS_DEVICE_TYPE_MISMATCH);
+       (void) status;
+       return -1;
+    }
+
+    return ((cairo_xcb_connection_t *) device)->force_precision;
+}
+#if CAIRO_HAS_XLIB_XCB_FUNCTIONS
+slim_hidden_def (cairo_xcb_device_debug_get_precision);
+#endif
diff --git a/src/cairo-xcb-private.h b/src/cairo-xcb-private.h
new file mode 100644 (file)
index 0000000..42a112c
--- /dev/null
@@ -0,0 +1,813 @@
+/* Cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2005 Red Hat, Inc.
+ * Copyright © 2009 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributors(s):
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#ifndef CAIRO_XCB_PRIVATE_H
+#define CAIRO_XCB_PRIVATE_H
+
+#include "cairo-xcb.h"
+
+#include "cairo-cache-private.h"
+#include "cairo-compiler-private.h"
+#include "cairo-device-private.h"
+#include "cairo-error-private.h"
+#include "cairo-freelist-private.h"
+#include "cairo-list-private.h"
+#include "cairo-mutex-private.h"
+#include "cairo-pattern-private.h"
+#include "cairo-reference-count-private.h"
+#include "cairo-scaled-font-private.h"
+#include "cairo-spans-private.h"
+#include "cairo-surface-private.h"
+
+#include <xcb/xcb.h>
+#include <xcb/render.h>
+#include <xcb/xcbext.h>
+#include <pixman.h>
+
+#define XLIB_COORD_MAX 32767
+
+/* maximum number of cached GC's */
+#define GC_CACHE_SIZE 4
+
+#define CAIRO_XCB_RENDER_AT_LEAST(major, minor)        \
+       ((XCB_RENDER_MAJOR_VERSION > major) ||  \
+       ((XCB_RENDER_MAJOR_VERSION == major) && (XCB_RENDER_MINOR_VERSION >= minor)))
+
+typedef struct _cairo_xcb_connection cairo_xcb_connection_t;
+typedef struct _cairo_xcb_font cairo_xcb_font_t;
+typedef struct _cairo_xcb_screen cairo_xcb_screen_t;
+typedef struct _cairo_xcb_surface cairo_xcb_surface_t;
+typedef struct _cairo_xcb_picture cairo_xcb_picture_t;
+typedef struct _cairo_xcb_shm_mem_pool cairo_xcb_shm_mem_pool_t;
+typedef struct _cairo_xcb_shm_info cairo_xcb_shm_info_t;
+
+struct _cairo_xcb_shm_info {
+    cairo_xcb_connection_t *connection;
+    uint32_t shm;
+    uint32_t offset;
+    size_t size;
+    void *mem;
+    cairo_xcb_shm_mem_pool_t *pool;
+    xcb_get_input_focus_cookie_t sync;
+    cairo_list_t pending;
+};
+
+struct _cairo_xcb_surface {
+    cairo_surface_t base;
+    cairo_image_surface_t *fallback;
+    cairo_boxes_t fallback_damage;
+
+    cairo_xcb_connection_t *connection;
+    cairo_xcb_screen_t *screen;
+
+    xcb_drawable_t drawable;
+    cairo_bool_t owns_pixmap;
+
+    cairo_bool_t deferred_clear;
+    cairo_color_t deferred_clear_color;
+
+    int width;
+    int height;
+    int depth;
+
+    xcb_render_picture_t picture;
+    xcb_render_pictformat_t xrender_format;
+    pixman_format_code_t pixman_format;
+    uint32_t precision;
+
+    cairo_list_t link;
+};
+
+struct _cairo_xcb_picture {
+    cairo_surface_t base;
+
+    cairo_xcb_screen_t *screen;
+    xcb_render_picture_t picture;
+    xcb_render_pictformat_t xrender_format;
+    pixman_format_code_t pixman_format;
+
+    int width, height;
+
+    cairo_extend_t extend;
+    cairo_filter_t filter;
+    cairo_bool_t has_component_alpha;
+    xcb_render_transform_t transform;
+
+    int x0, y0;
+    int x, y;
+
+    cairo_list_t link;
+};
+
+#if CAIRO_HAS_XLIB_XCB_FUNCTIONS
+typedef struct _cairo_xlib_xcb_surface {
+    cairo_surface_t base;
+
+    cairo_xcb_surface_t *xcb;
+
+    /* original settings for query */
+    void *display;
+    void *screen;
+    void *visual;
+    void *format;
+} cairo_xlib_xcb_surface_t;
+#endif
+
+
+enum {
+    GLYPHSET_INDEX_ARGB32,
+    GLYPHSET_INDEX_A8,
+    GLYPHSET_INDEX_A1,
+    NUM_GLYPHSETS
+};
+
+typedef struct _cairo_xcb_font_glyphset_free_glyphs {
+    xcb_render_glyphset_t   glyphset;
+    int                            glyph_count;
+    xcb_render_glyph_t     glyph_indices[128];
+} cairo_xcb_font_glyphset_free_glyphs_t;
+
+typedef struct _cairo_xcb_font_glyphset_info {
+    xcb_render_glyphset_t   glyphset;
+    cairo_format_t         format;
+    xcb_render_pictformat_t xrender_format;
+    cairo_xcb_font_glyphset_free_glyphs_t *pending_free_glyphs;
+} cairo_xcb_font_glyphset_info_t;
+
+struct _cairo_xcb_font {
+    cairo_scaled_font_private_t      base;
+    cairo_scaled_font_t                    *scaled_font;
+    cairo_xcb_connection_t         *connection;
+    cairo_xcb_font_glyphset_info_t  glyphset_info[NUM_GLYPHSETS];
+    cairo_list_t link;
+};
+
+struct _cairo_xcb_screen {
+    cairo_xcb_connection_t *connection;
+
+    xcb_screen_t           *xcb_screen;
+
+    xcb_gcontext_t gc[GC_CACHE_SIZE];
+    uint8_t gc_depths[GC_CACHE_SIZE];
+
+    cairo_surface_t *stock_colors[CAIRO_STOCK_NUM_COLORS];
+    struct {
+       cairo_surface_t *picture;
+       cairo_color_t color;
+    } solid_cache[16];
+    int solid_cache_size;
+
+    cairo_cache_t linear_pattern_cache;
+    cairo_cache_t radial_pattern_cache;
+    cairo_freelist_t pattern_cache_entry_freelist;
+
+    cairo_list_t link;
+    cairo_list_t surfaces;
+    cairo_list_t pictures;
+};
+
+struct _cairo_xcb_connection {
+    cairo_device_t device;
+
+    xcb_connection_t *xcb_connection;
+
+    xcb_render_pictformat_t standard_formats[5];
+    cairo_hash_table_t *xrender_formats;
+    cairo_hash_table_t *visual_to_xrender_format;
+
+    unsigned int maximum_request_length;
+    unsigned int flags;
+    unsigned int original_flags;
+
+    int force_precision;
+
+    const xcb_setup_t *root;
+    const xcb_query_extension_reply_t *render;
+    const xcb_query_extension_reply_t *shm;
+
+    cairo_list_t free_xids;
+    cairo_freepool_t xid_pool;
+
+    cairo_mutex_t shm_mutex;
+    cairo_list_t shm_pools;
+    cairo_list_t shm_pending;
+    cairo_freepool_t shm_info_freelist;
+
+    cairo_mutex_t screens_mutex;
+    cairo_list_t screens;
+
+    cairo_list_t fonts;
+
+    cairo_list_t link;
+};
+
+enum {
+    CAIRO_XCB_HAS_RENDER                       = 0x0001,
+    CAIRO_XCB_RENDER_HAS_FILL_RECTANGLES       = 0x0002,
+    CAIRO_XCB_RENDER_HAS_COMPOSITE             = 0x0004,
+    CAIRO_XCB_RENDER_HAS_COMPOSITE_TRAPEZOIDS  = 0x0008,
+    CAIRO_XCB_RENDER_HAS_COMPOSITE_GLYPHS      = 0x0010,
+    CAIRO_XCB_RENDER_HAS_PICTURE_TRANSFORM     = 0x0020,
+    CAIRO_XCB_RENDER_HAS_FILTERS               = 0x0040,
+    CAIRO_XCB_RENDER_HAS_PDF_OPERATORS         = 0x0080,
+    CAIRO_XCB_RENDER_HAS_EXTENDED_REPEAT       = 0x0100,
+    CAIRO_XCB_RENDER_HAS_GRADIENTS             = 0x0200,
+
+    CAIRO_XCB_HAS_SHM                          = 0x80000000,
+
+    CAIRO_XCB_RENDER_MASK = CAIRO_XCB_HAS_RENDER |
+                           CAIRO_XCB_RENDER_HAS_FILL_RECTANGLES |
+                           CAIRO_XCB_RENDER_HAS_COMPOSITE |
+                           CAIRO_XCB_RENDER_HAS_COMPOSITE_TRAPEZOIDS |
+                           CAIRO_XCB_RENDER_HAS_COMPOSITE_GLYPHS |
+                           CAIRO_XCB_RENDER_HAS_PICTURE_TRANSFORM |
+                           CAIRO_XCB_RENDER_HAS_FILTERS |
+                           CAIRO_XCB_RENDER_HAS_PDF_OPERATORS |
+                           CAIRO_XCB_RENDER_HAS_EXTENDED_REPEAT |
+                           CAIRO_XCB_RENDER_HAS_GRADIENTS,
+    CAIRO_XCB_SHM_MASK    = CAIRO_XCB_HAS_SHM
+};
+
+#define CAIRO_XCB_SHM_SMALL_IMAGE 8192
+
+cairo_private extern const cairo_surface_backend_t _cairo_xcb_surface_backend;
+
+cairo_private cairo_xcb_connection_t *
+_cairo_xcb_connection_get (xcb_connection_t *connection);
+
+static inline cairo_xcb_connection_t *
+_cairo_xcb_connection_reference (cairo_xcb_connection_t *connection)
+{
+    return (cairo_xcb_connection_t *) cairo_device_reference (&connection->device);
+}
+
+cairo_private xcb_render_pictformat_t
+_cairo_xcb_connection_get_xrender_format (cairo_xcb_connection_t *connection,
+                                         pixman_format_code_t pixman_format);
+
+cairo_private xcb_render_pictformat_t
+_cairo_xcb_connection_get_xrender_format_for_visual (cairo_xcb_connection_t *connection,
+                                                    const xcb_visualid_t visual);
+
+static inline cairo_status_t cairo_warn
+_cairo_xcb_connection_acquire (cairo_xcb_connection_t *connection)
+{
+    return cairo_device_acquire (&connection->device);
+}
+
+cairo_private uint32_t
+_cairo_xcb_connection_get_xid (cairo_xcb_connection_t *connection);
+
+cairo_private void
+_cairo_xcb_connection_put_xid (cairo_xcb_connection_t *connection,
+                              uint32_t xid);
+
+static inline void
+_cairo_xcb_connection_release (cairo_xcb_connection_t *connection)
+{
+    cairo_device_release (&connection->device);
+}
+
+static inline void
+_cairo_xcb_connection_destroy (cairo_xcb_connection_t *connection)
+{
+    cairo_device_destroy (&connection->device);
+}
+
+cairo_private cairo_int_status_t
+_cairo_xcb_connection_allocate_shm_info (cairo_xcb_connection_t *display,
+                                        size_t size,
+                                        cairo_bool_t might_reuse,
+                                        cairo_xcb_shm_info_t **shm_info_out);
+
+cairo_private void
+_cairo_xcb_shm_info_destroy (cairo_xcb_shm_info_t *shm_info);
+
+cairo_private void
+_cairo_xcb_connection_shm_mem_pools_flush (cairo_xcb_connection_t *connection);
+
+cairo_private void
+_cairo_xcb_connection_shm_mem_pools_fini (cairo_xcb_connection_t *connection);
+
+cairo_private void
+_cairo_xcb_font_close (cairo_xcb_font_t *font);
+
+cairo_private cairo_xcb_screen_t *
+_cairo_xcb_screen_get (xcb_connection_t *connection,
+                      xcb_screen_t *screen);
+
+cairo_private void
+_cairo_xcb_screen_finish (cairo_xcb_screen_t *screen);
+
+cairo_private xcb_gcontext_t
+_cairo_xcb_screen_get_gc (cairo_xcb_screen_t *screen,
+                         xcb_drawable_t drawable,
+                         int depth);
+
+cairo_private void
+_cairo_xcb_screen_put_gc (cairo_xcb_screen_t *screen, int depth, xcb_gcontext_t gc);
+
+cairo_private cairo_status_t
+_cairo_xcb_screen_store_linear_picture (cairo_xcb_screen_t *screen,
+                                       const cairo_linear_pattern_t *linear,
+                                       cairo_surface_t *picture);
+
+cairo_private cairo_surface_t *
+_cairo_xcb_screen_lookup_linear_picture (cairo_xcb_screen_t *screen,
+                                        const cairo_linear_pattern_t *linear);
+
+cairo_private cairo_status_t
+_cairo_xcb_screen_store_radial_picture (cairo_xcb_screen_t *screen,
+                                       const cairo_radial_pattern_t *radial,
+                                       cairo_surface_t *picture);
+
+cairo_private cairo_surface_t *
+_cairo_xcb_screen_lookup_radial_picture (cairo_xcb_screen_t *screen,
+                                        const cairo_radial_pattern_t *radial);
+
+cairo_private cairo_surface_t *
+_cairo_xcb_surface_create_similar_image (void *abstrct_other,
+                                        cairo_format_t format,
+                                        int width,
+                                        int height);
+
+cairo_private cairo_surface_t *
+_cairo_xcb_surface_create_similar (void                        *abstract_other,
+                                  cairo_content_t       content,
+                                  int                   width,
+                                  int                   height);
+
+cairo_private cairo_surface_t *
+_cairo_xcb_surface_create_internal (cairo_xcb_screen_t         *screen,
+                                   xcb_drawable_t               drawable,
+                                   cairo_bool_t                 owns_pixmap,
+                                   pixman_format_code_t         pixman_format,
+                                   xcb_render_pictformat_t      xrender_format,
+                                   int                          width,
+                                   int                          height);
+
+cairo_private_no_warn cairo_bool_t
+_cairo_xcb_surface_get_extents (void *abstract_surface,
+                               cairo_rectangle_int_t *extents);
+
+cairo_private cairo_int_status_t
+_cairo_xcb_surface_cairo_paint (cairo_xcb_surface_t    *surface,
+                               cairo_operator_t         op,
+                               const cairo_pattern_t   *source,
+                               const cairo_clip_t              *clip);
+
+cairo_private cairo_int_status_t
+_cairo_xcb_surface_cairo_mask (cairo_xcb_surface_t     *surface,
+                              cairo_operator_t          op,
+                              const cairo_pattern_t    *source,
+                              const cairo_pattern_t    *mask,
+                              const cairo_clip_t               *clip);
+
+cairo_private cairo_int_status_t
+_cairo_xcb_surface_cairo_stroke (cairo_xcb_surface_t   *surface,
+                                cairo_operator_t        op,
+                                const cairo_pattern_t  *source,
+                                const cairo_path_fixed_t       *path,
+                                const cairo_stroke_style_t     *style,
+                                const cairo_matrix_t           *ctm,
+                                const cairo_matrix_t           *ctm_inverse,
+                                double                  tolerance,
+                                cairo_antialias_t       antialias,
+                                const cairo_clip_t             *clip);
+
+cairo_private cairo_int_status_t
+_cairo_xcb_surface_cairo_fill (cairo_xcb_surface_t     *surface,
+                              cairo_operator_t          op,
+                              const cairo_pattern_t    *source,
+                              const cairo_path_fixed_t *path,
+                              cairo_fill_rule_t         fill_rule,
+                              double                    tolerance,
+                              cairo_antialias_t         antialias,
+                              const cairo_clip_t               *clip);
+
+cairo_private cairo_int_status_t
+_cairo_xcb_surface_cairo_glyphs (cairo_xcb_surface_t   *surface,
+                                cairo_operator_t                op,
+                                const cairo_pattern_t  *source,
+                                cairo_scaled_font_t    *scaled_font,
+                                cairo_glyph_t          *glyphs,
+                                int                     num_glyphs,
+                                const cairo_clip_t             *clip);
+
+cairo_private cairo_int_status_t
+_cairo_xcb_surface_render_paint (cairo_xcb_surface_t   *surface,
+                                cairo_operator_t        op,
+                                const cairo_pattern_t  *source,
+                                cairo_composite_rectangles_t *composite);
+
+cairo_private cairo_int_status_t
+_cairo_xcb_surface_render_mask (cairo_xcb_surface_t    *surface,
+                               cairo_operator_t                 op,
+                               const cairo_pattern_t   *source,
+                               const cairo_pattern_t   *mask,
+                               cairo_composite_rectangles_t *composite);
+
+cairo_private cairo_int_status_t
+_cairo_xcb_surface_render_stroke (cairo_xcb_surface_t          *surface,
+                                 cairo_operator_t               op,
+                                 const cairo_pattern_t         *source,
+                                 const cairo_path_fixed_t      *path,
+                                 const cairo_stroke_style_t    *style,
+                                 const cairo_matrix_t          *ctm,
+                                 const cairo_matrix_t          *ctm_inverse,
+                                 double                         tolerance,
+                                 cairo_antialias_t              antialias,
+                                 cairo_composite_rectangles_t *composite);
+
+cairo_private cairo_int_status_t
+_cairo_xcb_surface_render_fill (cairo_xcb_surface_t    *surface,
+                               cairo_operator_t         op,
+                               const cairo_pattern_t   *source,
+                               const cairo_path_fixed_t*path,
+                               cairo_fill_rule_t        fill_rule,
+                               double                   tolerance,
+                               cairo_antialias_t        antialias,
+                               cairo_composite_rectangles_t *composite);
+
+cairo_private cairo_int_status_t
+_cairo_xcb_surface_render_glyphs (cairo_xcb_surface_t  *surface,
+                                 cairo_operator_t               op,
+                                 const cairo_pattern_t *source,
+                                 cairo_scaled_font_t   *scaled_font,
+                                 cairo_glyph_t         *glyphs,
+                                 int                    num_glyphs,
+                                 cairo_composite_rectangles_t *composite,
+                                 cairo_bool_t overlap);
+cairo_private void
+_cairo_xcb_surface_scaled_font_fini (cairo_scaled_font_t *scaled_font);
+
+cairo_private void
+_cairo_xcb_surface_scaled_glyph_fini (cairo_scaled_glyph_t *scaled_glyph,
+                                     cairo_scaled_font_t  *scaled_font);
+
+cairo_private cairo_status_t
+_cairo_xcb_surface_clear (cairo_xcb_surface_t *dst);
+
+cairo_private cairo_status_t
+_cairo_xcb_surface_core_copy_boxes (cairo_xcb_surface_t                *dst,
+                                  const cairo_pattern_t        *src_pattern,
+                                  const cairo_rectangle_int_t  *extents,
+                                  const cairo_boxes_t          *boxes);
+
+cairo_private cairo_status_t
+_cairo_xcb_surface_core_fill_boxes (cairo_xcb_surface_t *dst,
+                                   const cairo_color_t *color,
+                                   cairo_boxes_t *boxes);
+
+cairo_private xcb_pixmap_t
+_cairo_xcb_connection_create_pixmap (cairo_xcb_connection_t *connection,
+                                    uint8_t depth,
+                                    xcb_drawable_t drawable,
+                                    uint16_t width,
+                                    uint16_t height);
+
+cairo_private void
+_cairo_xcb_connection_free_pixmap (cairo_xcb_connection_t *connection,
+                                  xcb_pixmap_t pixmap);
+
+cairo_private xcb_gcontext_t
+_cairo_xcb_connection_create_gc (cairo_xcb_connection_t *connection,
+                                xcb_drawable_t drawable,
+                                uint32_t value_mask,
+                                uint32_t *values);
+
+cairo_private void
+_cairo_xcb_connection_free_gc (cairo_xcb_connection_t *connection,
+                              xcb_gcontext_t gc);
+
+cairo_private void
+_cairo_xcb_connection_change_gc (cairo_xcb_connection_t *connection,
+                                xcb_gcontext_t gc,
+                                uint32_t value_mask,
+                                uint32_t *values);
+
+cairo_private void
+_cairo_xcb_connection_copy_area (cairo_xcb_connection_t *connection,
+                                xcb_drawable_t src,
+                                xcb_drawable_t dst,
+                                xcb_gcontext_t gc,
+                                int16_t src_x,
+                                int16_t src_y,
+                                int16_t dst_x,
+                                int16_t dst_y,
+                                uint16_t width,
+                                uint16_t height);
+
+cairo_private void
+_cairo_xcb_connection_put_image (cairo_xcb_connection_t *connection,
+                                xcb_drawable_t dst,
+                                xcb_gcontext_t gc,
+                                uint16_t width,
+                                uint16_t height,
+                                int16_t dst_x,
+                                int16_t dst_y,
+                                uint8_t depth,
+                                uint32_t length,
+                                void *data);
+
+cairo_private void
+_cairo_xcb_connection_put_subimage (cairo_xcb_connection_t *connection,
+                                   xcb_drawable_t dst,
+                                   xcb_gcontext_t gc,
+                                   int16_t src_x,
+                                   int16_t src_y,
+                                   uint16_t width,
+                                   uint16_t height,
+                                   uint16_t cpp,
+                                   uint16_t stride,
+                                   int16_t dst_x,
+                                   int16_t dst_y,
+                                   uint8_t depth,
+                                   void *data);
+
+cairo_private cairo_status_t
+_cairo_xcb_connection_get_image (cairo_xcb_connection_t *connection,
+                                xcb_drawable_t src,
+                                int16_t src_x,
+                                int16_t src_y,
+                                uint16_t width,
+                                uint16_t height,
+                                xcb_get_image_reply_t **reply);
+
+cairo_private void
+_cairo_xcb_connection_poly_fill_rectangle (cairo_xcb_connection_t *connection,
+                                          xcb_drawable_t dst,
+                                          xcb_gcontext_t gc,
+                                          uint32_t num_rectangles,
+                                          xcb_rectangle_t *rectangles);
+
+cairo_private cairo_status_t
+_cairo_xcb_shm_image_create (cairo_xcb_connection_t *connection,
+                            pixman_format_code_t pixman_format,
+                            int width, int height,
+                            cairo_image_surface_t **image_out,
+                            cairo_xcb_shm_info_t **shm_info_out);
+
+#if CAIRO_HAS_XCB_SHM_FUNCTIONS
+cairo_private uint32_t
+_cairo_xcb_connection_shm_attach (cairo_xcb_connection_t *connection,
+                                 uint32_t id,
+                                 cairo_bool_t readonly);
+
+cairo_private void
+_cairo_xcb_connection_shm_put_image (cairo_xcb_connection_t *connection,
+                                    xcb_drawable_t dst,
+                                    xcb_gcontext_t gc,
+                                    uint16_t total_width,
+                                    uint16_t total_height,
+                                    int16_t src_x,
+                                    int16_t src_y,
+                                    uint16_t width,
+                                    uint16_t height,
+                                    int16_t dst_x,
+                                    int16_t dst_y,
+                                    uint8_t depth,
+                                    uint32_t shm,
+                                    uint32_t offset);
+
+cairo_private cairo_status_t
+_cairo_xcb_connection_shm_get_image (cairo_xcb_connection_t *connection,
+                                    xcb_drawable_t src,
+                                    int16_t src_x,
+                                    int16_t src_y,
+                                    uint16_t width,
+                                    uint16_t height,
+                                    uint32_t shmseg,
+                                    uint32_t offset);
+
+cairo_private void
+_cairo_xcb_connection_shm_detach (cairo_xcb_connection_t *connection,
+                                 uint32_t segment);
+#else
+static inline void
+_cairo_xcb_connection_shm_put_image (cairo_xcb_connection_t *connection,
+                                    xcb_drawable_t dst,
+                                    xcb_gcontext_t gc,
+                                    uint16_t total_width,
+                                    uint16_t total_height,
+                                    int16_t src_x,
+                                    int16_t src_y,
+                                    uint16_t width,
+                                    uint16_t height,
+                                    int16_t dst_x,
+                                    int16_t dst_y,
+                                    uint8_t depth,
+                                    uint32_t shm,
+                                    uint32_t offset)
+{
+    ASSERT_NOT_REACHED;
+}
+#endif
+
+cairo_private void
+_cairo_xcb_connection_render_create_picture (cairo_xcb_connection_t  *connection,
+                                            xcb_render_picture_t     picture,
+                                            xcb_drawable_t           drawable,
+                                            xcb_render_pictformat_t  format,
+                                            uint32_t                 value_mask,
+                                            uint32_t                *value_list);
+
+cairo_private void
+_cairo_xcb_connection_render_change_picture (cairo_xcb_connection_t     *connection,
+                                            xcb_render_picture_t  picture,
+                                            uint32_t              value_mask,
+                                            uint32_t             *value_list);
+
+cairo_private void
+_cairo_xcb_connection_render_set_picture_clip_rectangles (cairo_xcb_connection_t      *connection,
+                                                         xcb_render_picture_t   picture,
+                                                         int16_t                clip_x_origin,
+                                                         int16_t                clip_y_origin,
+                                                         uint32_t               rectangles_len,
+                                                         xcb_rectangle_t       *rectangles);
+
+cairo_private void
+_cairo_xcb_connection_render_free_picture (cairo_xcb_connection_t *connection,
+                                          xcb_render_picture_t  picture);
+
+cairo_private void
+_cairo_xcb_connection_render_composite (cairo_xcb_connection_t     *connection,
+                                       uint8_t               op,
+                                       xcb_render_picture_t  src,
+                                       xcb_render_picture_t  mask,
+                                       xcb_render_picture_t  dst,
+                                       int16_t               src_x,
+                                       int16_t               src_y,
+                                       int16_t               mask_x,
+                                       int16_t               mask_y,
+                                       int16_t               dst_x,
+                                       int16_t               dst_y,
+                                       uint16_t              width,
+                                       uint16_t              height);
+
+cairo_private void
+_cairo_xcb_connection_render_trapezoids (cairo_xcb_connection_t *connection,
+                                        uint8_t                       op,
+                                        xcb_render_picture_t          src,
+                                        xcb_render_picture_t          dst,
+                                        xcb_render_pictformat_t       mask_format,
+                                        int16_t                       src_x,
+                                        int16_t                       src_y,
+                                        uint32_t                      traps_len,
+                                        xcb_render_trapezoid_t       *traps);
+
+cairo_private void
+_cairo_xcb_connection_render_create_glyph_set (cairo_xcb_connection_t  *connection,
+                                              xcb_render_glyphset_t     id,
+                                              xcb_render_pictformat_t  format);
+
+cairo_private void
+_cairo_xcb_connection_render_free_glyph_set (cairo_xcb_connection_t      *connection,
+                                            xcb_render_glyphset_t  glyphset);
+
+cairo_private void
+_cairo_xcb_connection_render_add_glyphs (cairo_xcb_connection_t             *connection,
+                                        xcb_render_glyphset_t         glyphset,
+                                        uint32_t                      num_glyphs,
+                                        uint32_t                     *glyphs_id,
+                                        xcb_render_glyphinfo_t       *glyphs,
+                                        uint32_t                      data_len,
+                                        uint8_t                      *data);
+
+cairo_private void
+_cairo_xcb_connection_render_free_glyphs (cairo_xcb_connection_t         *connection,
+                                         xcb_render_glyphset_t     glyphset,
+                                         uint32_t                  num_glyphs,
+                                         xcb_render_glyph_t       *glyphs);
+
+cairo_private void
+_cairo_xcb_connection_render_composite_glyphs_8 (cairo_xcb_connection_t        *connection,
+                                                uint8_t                  op,
+                                                xcb_render_picture_t     src,
+                                                xcb_render_picture_t     dst,
+                                                xcb_render_pictformat_t  mask_format,
+                                                xcb_render_glyphset_t    glyphset,
+                                                int16_t                  src_x,
+                                                int16_t                  src_y,
+                                                uint32_t                 glyphcmds_len,
+                                                uint8_t                 *glyphcmds);
+
+cairo_private void
+_cairo_xcb_connection_render_composite_glyphs_16 (cairo_xcb_connection_t        *connection,
+                                                 uint8_t                  op,
+                                                 xcb_render_picture_t     src,
+                                                 xcb_render_picture_t     dst,
+                                                 xcb_render_pictformat_t  mask_format,
+                                                 xcb_render_glyphset_t    glyphset,
+                                                 int16_t                  src_x,
+                                                 int16_t                  src_y,
+                                                 uint32_t                 glyphcmds_len,
+                                                 uint8_t                 *glyphcmds);
+
+cairo_private void
+_cairo_xcb_connection_render_composite_glyphs_32 (cairo_xcb_connection_t        *connection,
+                                                 uint8_t                  op,
+                                                 xcb_render_picture_t     src,
+                                                 xcb_render_picture_t     dst,
+                                                 xcb_render_pictformat_t  mask_format,
+                                                 xcb_render_glyphset_t    glyphset,
+                                                 int16_t                  src_x,
+                                                 int16_t                  src_y,
+                                                 uint32_t                 glyphcmds_len,
+                                                 uint8_t                 *glyphcmds);
+
+cairo_private void
+_cairo_xcb_connection_render_fill_rectangles (cairo_xcb_connection_t      *connection,
+                                             uint8_t                op,
+                                             xcb_render_picture_t   dst,
+                                             xcb_render_color_t     color,
+                                             uint32_t               num_rects,
+                                             xcb_rectangle_t       *rects);
+
+cairo_private void
+_cairo_xcb_connection_render_set_picture_transform (cairo_xcb_connection_t       *connection,
+                                                   xcb_render_picture_t    picture,
+                                                   xcb_render_transform_t  *transform);
+
+cairo_private void
+_cairo_xcb_connection_render_set_picture_filter (cairo_xcb_connection_t         *connection,
+                                                xcb_render_picture_t      picture,
+                                                uint16_t                  filter_len,
+                                                char                     *filter);
+
+cairo_private void
+_cairo_xcb_connection_render_create_solid_fill (cairo_xcb_connection_t     *connection,
+                                               xcb_render_picture_t  picture,
+                                               xcb_render_color_t    color);
+
+cairo_private void
+_cairo_xcb_connection_render_create_linear_gradient (cairo_xcb_connection_t         *connection,
+                                                    xcb_render_picture_t      picture,
+                                                    xcb_render_pointfix_t     p1,
+                                                    xcb_render_pointfix_t     p2,
+                                                    uint32_t                  num_stops,
+                                                    xcb_render_fixed_t *stops,
+                                                    xcb_render_color_t *colors);
+
+cairo_private void
+_cairo_xcb_connection_render_create_radial_gradient (cairo_xcb_connection_t         *connection,
+                                                    xcb_render_picture_t      picture,
+                                                    xcb_render_pointfix_t     inner,
+                                                    xcb_render_pointfix_t     outer,
+                                                    xcb_render_fixed_t        inner_radius,
+                                                    xcb_render_fixed_t        outer_radius,
+                                                    uint32_t                  num_stops,
+                                                    xcb_render_fixed_t *stops,
+                                                    xcb_render_color_t *colors);
+
+cairo_private void
+_cairo_xcb_connection_render_create_conical_gradient (cairo_xcb_connection_t         *c,
+                                                     xcb_render_picture_t      picture,
+                                                     xcb_render_pointfix_t     center,
+                                                     xcb_render_fixed_t        angle,
+                                                     uint32_t                  num_stops,
+                                                     xcb_render_fixed_t *stops,
+                                                     xcb_render_color_t *colors);
+#if CAIRO_HAS_XLIB_XCB_FUNCTIONS
+slim_hidden_proto (cairo_xcb_surface_create);
+slim_hidden_proto (cairo_xcb_surface_create_for_bitmap);
+slim_hidden_proto (cairo_xcb_surface_create_with_xrender_format);
+slim_hidden_proto (cairo_xcb_surface_set_size);
+slim_hidden_proto (cairo_xcb_device_debug_get_precision);
+slim_hidden_proto_no_warn (cairo_xcb_device_debug_set_precision);
+#endif
+
+#endif /* CAIRO_XCB_PRIVATE_H */
diff --git a/src/cairo-xcb-screen.c b/src/cairo-xcb-screen.c
new file mode 100644 (file)
index 0000000..18e75a5
--- /dev/null
@@ -0,0 +1,367 @@
+/* Cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2008 Chris Wilson
+ * Copyright © 2009 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * Authors:
+ *    Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-xcb-private.h"
+
+struct pattern_cache_entry {
+    cairo_cache_entry_t key;
+    cairo_xcb_screen_t *screen;
+    cairo_pattern_union_t pattern;
+    cairo_surface_t *picture;
+};
+
+void
+_cairo_xcb_screen_finish (cairo_xcb_screen_t *screen)
+{
+    int i;
+
+    CAIRO_MUTEX_LOCK (screen->connection->screens_mutex);
+    cairo_list_del (&screen->link);
+    CAIRO_MUTEX_UNLOCK (screen->connection->screens_mutex);
+
+    while (! cairo_list_is_empty (&screen->surfaces)) {
+       cairo_surface_t *surface;
+
+       surface = &cairo_list_first_entry (&screen->surfaces,
+                                          cairo_xcb_surface_t,
+                                          link)->base;
+
+       cairo_surface_reference (surface);
+       cairo_surface_finish (surface);
+       cairo_surface_destroy (surface);
+    }
+
+    while (! cairo_list_is_empty (&screen->pictures)) {
+       cairo_surface_t *surface;
+
+       surface = &cairo_list_first_entry (&screen->pictures,
+                                          cairo_xcb_picture_t,
+                                          link)->base;
+
+       cairo_surface_reference (surface);
+       cairo_surface_finish (surface);
+       cairo_surface_destroy (surface);
+    }
+
+    for (i = 0; i < screen->solid_cache_size; i++)
+       cairo_surface_destroy (screen->solid_cache[i].picture);
+
+    for (i = 0; i < ARRAY_LENGTH (screen->stock_colors); i++)
+       cairo_surface_destroy (screen->stock_colors[i]);
+
+    for (i = 0; i < ARRAY_LENGTH (screen->gc); i++) {
+       if (screen->gc_depths[i] != 0)
+           _cairo_xcb_connection_free_gc (screen->connection, screen->gc[i]);
+    }
+
+    _cairo_cache_fini (&screen->linear_pattern_cache);
+    _cairo_cache_fini (&screen->radial_pattern_cache);
+    _cairo_freelist_fini (&screen->pattern_cache_entry_freelist);
+
+    free (screen);
+}
+
+static cairo_bool_t
+_linear_pattern_cache_entry_equal (const void *A, const void *B)
+{
+    const struct pattern_cache_entry *a = A, *b = B;
+
+    return _cairo_linear_pattern_equal (&a->pattern.gradient.linear,
+                                       &b->pattern.gradient.linear);
+}
+
+static cairo_bool_t
+_radial_pattern_cache_entry_equal (const void *A, const void *B)
+{
+    const struct pattern_cache_entry *a = A, *b = B;
+
+    return _cairo_radial_pattern_equal (&a->pattern.gradient.radial,
+                                       &b->pattern.gradient.radial);
+}
+
+static void
+_pattern_cache_entry_destroy (void *closure)
+{
+    struct pattern_cache_entry *entry = closure;
+
+    _cairo_pattern_fini (&entry->pattern.base);
+    cairo_surface_destroy (entry->picture);
+    _cairo_freelist_free (&entry->screen->pattern_cache_entry_freelist, entry);
+}
+
+cairo_xcb_screen_t *
+_cairo_xcb_screen_get (xcb_connection_t *xcb_connection,
+                      xcb_screen_t *xcb_screen)
+{
+    cairo_xcb_connection_t *connection;
+    cairo_xcb_screen_t *screen;
+    cairo_status_t status;
+    int i;
+
+    connection = _cairo_xcb_connection_get (xcb_connection);
+    if (unlikely (connection == NULL))
+       return NULL;
+
+    CAIRO_MUTEX_LOCK (connection->screens_mutex);
+
+    cairo_list_foreach_entry (screen,
+                             cairo_xcb_screen_t,
+                             &connection->screens,
+                             link)
+    {
+       if (screen->xcb_screen == xcb_screen) {
+           /* Maintain list in MRU order */
+           if (&screen->link != connection->screens.next)
+               cairo_list_move (&screen->link, &connection->screens);
+
+           goto unlock;
+       }
+    }
+
+    screen = malloc (sizeof (cairo_xcb_screen_t));
+    if (unlikely (screen == NULL))
+       goto unlock;
+
+    screen->connection = connection;
+    screen->xcb_screen = xcb_screen;
+
+    _cairo_freelist_init (&screen->pattern_cache_entry_freelist,
+                         sizeof (struct pattern_cache_entry));
+    cairo_list_init (&screen->link);
+    cairo_list_init (&screen->surfaces);
+    cairo_list_init (&screen->pictures);
+
+    memset (screen->gc_depths, 0, sizeof (screen->gc_depths));
+    memset (screen->gc, 0, sizeof (screen->gc));
+
+    screen->solid_cache_size = 0;
+    for (i = 0; i < ARRAY_LENGTH (screen->stock_colors); i++)
+       screen->stock_colors[i] = NULL;
+
+    status = _cairo_cache_init (&screen->linear_pattern_cache,
+                               _linear_pattern_cache_entry_equal,
+                               NULL,
+                               _pattern_cache_entry_destroy,
+                               16);
+    if (unlikely (status))
+       goto error_screen;
+
+    status = _cairo_cache_init (&screen->radial_pattern_cache,
+                               _radial_pattern_cache_entry_equal,
+                               NULL,
+                               _pattern_cache_entry_destroy,
+                               4);
+    if (unlikely (status))
+       goto error_linear;
+
+    cairo_list_add (&screen->link, &connection->screens);
+
+unlock:
+    CAIRO_MUTEX_UNLOCK (connection->screens_mutex);
+
+    return screen;
+
+error_linear:
+    _cairo_cache_fini (&screen->linear_pattern_cache);
+error_screen:
+    CAIRO_MUTEX_UNLOCK (connection->screens_mutex);
+    free (screen);
+
+    return NULL;
+}
+
+static xcb_gcontext_t
+_create_gc (cairo_xcb_screen_t *screen,
+           xcb_drawable_t drawable)
+{
+    uint32_t values[] = { 0 };
+
+    return _cairo_xcb_connection_create_gc (screen->connection, drawable,
+                                           XCB_GC_GRAPHICS_EXPOSURES,
+                                           values);
+}
+
+xcb_gcontext_t
+_cairo_xcb_screen_get_gc (cairo_xcb_screen_t *screen,
+                         xcb_drawable_t drawable,
+                         int depth)
+{
+    int i;
+
+    assert (CAIRO_MUTEX_IS_LOCKED (screen->connection->device.mutex));
+
+    for (i = 0; i < ARRAY_LENGTH (screen->gc); i++) {
+       if (screen->gc_depths[i] == depth) {
+           screen->gc_depths[i] = 0;
+           return screen->gc[i];
+       }
+    }
+
+    return _create_gc (screen, drawable);
+}
+
+void
+_cairo_xcb_screen_put_gc (cairo_xcb_screen_t *screen, int depth, xcb_gcontext_t gc)
+{
+    int i;
+
+    assert (CAIRO_MUTEX_IS_LOCKED (screen->connection->device.mutex));
+
+    for (i = 0; i < ARRAY_LENGTH (screen->gc); i++) {
+       if (screen->gc_depths[i] == 0)
+           break;
+    }
+
+    if (i == ARRAY_LENGTH (screen->gc)) {
+       /* perform random substitution to ensure fair caching over depths */
+       i = rand () % ARRAY_LENGTH (screen->gc);
+       _cairo_xcb_connection_free_gc (screen->connection, screen->gc[i]);
+    }
+
+    screen->gc[i] = gc;
+    screen->gc_depths[i] = depth;
+}
+
+cairo_status_t
+_cairo_xcb_screen_store_linear_picture (cairo_xcb_screen_t *screen,
+                                       const cairo_linear_pattern_t *linear,
+                                       cairo_surface_t *picture)
+{
+    struct pattern_cache_entry *entry;
+    cairo_status_t status;
+
+    assert (CAIRO_MUTEX_IS_LOCKED (screen->connection->device.mutex));
+
+    entry = _cairo_freelist_alloc (&screen->pattern_cache_entry_freelist);
+    if (unlikely (entry == NULL))
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    entry->key.hash = _cairo_linear_pattern_hash (_CAIRO_HASH_INIT_VALUE, linear);
+    entry->key.size = 1;
+
+    status = _cairo_pattern_init_copy (&entry->pattern.base, &linear->base.base);
+    if (unlikely (status)) {
+       _cairo_freelist_free (&screen->pattern_cache_entry_freelist, entry);
+       return status;
+    }
+
+    entry->picture = cairo_surface_reference (picture);
+    entry->screen = screen;
+
+    status = _cairo_cache_insert (&screen->linear_pattern_cache,
+                                 &entry->key);
+    if (unlikely (status)) {
+       cairo_surface_destroy (picture);
+       _cairo_freelist_free (&screen->pattern_cache_entry_freelist, entry);
+       return status;
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_surface_t *
+_cairo_xcb_screen_lookup_linear_picture (cairo_xcb_screen_t *screen,
+                                        const cairo_linear_pattern_t *linear)
+{
+    cairo_surface_t *picture = NULL;
+    struct pattern_cache_entry tmpl;
+    struct pattern_cache_entry *entry;
+
+    assert (CAIRO_MUTEX_IS_LOCKED (screen->connection->device.mutex));
+
+    tmpl.key.hash = _cairo_linear_pattern_hash (_CAIRO_HASH_INIT_VALUE, linear);
+    _cairo_pattern_init_static_copy (&tmpl.pattern.base, &linear->base.base);
+
+    entry = _cairo_cache_lookup (&screen->linear_pattern_cache, &tmpl.key);
+    if (entry != NULL)
+       picture = cairo_surface_reference (entry->picture);
+
+    return picture;
+}
+
+cairo_status_t
+_cairo_xcb_screen_store_radial_picture (cairo_xcb_screen_t *screen,
+                                       const cairo_radial_pattern_t *radial,
+                                       cairo_surface_t *picture)
+{
+    struct pattern_cache_entry *entry;
+    cairo_status_t status;
+
+    assert (CAIRO_MUTEX_IS_LOCKED (screen->connection->device.mutex));
+
+    entry = _cairo_freelist_alloc (&screen->pattern_cache_entry_freelist);
+    if (unlikely (entry == NULL))
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    entry->key.hash = _cairo_radial_pattern_hash (_CAIRO_HASH_INIT_VALUE, radial);
+    entry->key.size = 1;
+
+    status = _cairo_pattern_init_copy (&entry->pattern.base, &radial->base.base);
+    if (unlikely (status)) {
+       _cairo_freelist_free (&screen->pattern_cache_entry_freelist, entry);
+       return status;
+    }
+
+    entry->picture = cairo_surface_reference (picture);
+    entry->screen = screen;
+
+    status = _cairo_cache_insert (&screen->radial_pattern_cache, &entry->key);
+    if (unlikely (status)) {
+       cairo_surface_destroy (picture);
+       _cairo_freelist_free (&screen->pattern_cache_entry_freelist, entry);
+       return status;
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_surface_t *
+_cairo_xcb_screen_lookup_radial_picture (cairo_xcb_screen_t *screen,
+                                        const cairo_radial_pattern_t *radial)
+{
+    cairo_surface_t *picture = NULL;
+    struct pattern_cache_entry tmpl;
+    struct pattern_cache_entry *entry;
+
+    assert (CAIRO_MUTEX_IS_LOCKED (screen->connection->device.mutex));
+
+    tmpl.key.hash = _cairo_radial_pattern_hash (_CAIRO_HASH_INIT_VALUE, radial);
+    _cairo_pattern_init_static_copy (&tmpl.pattern.base, &radial->base.base);
+
+    entry = _cairo_cache_lookup (&screen->radial_pattern_cache, &tmpl.key);
+    if (entry != NULL)
+       picture = cairo_surface_reference (entry->picture);
+
+    return picture;
+}
diff --git a/src/cairo-xcb-shm.c b/src/cairo-xcb-shm.c
new file mode 100644 (file)
index 0000000..6972720
--- /dev/null
@@ -0,0 +1,679 @@
+/* Cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2007 Chris Wilson
+ * Copyright © 2009 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributors(s):
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+
+#if CAIRO_HAS_XCB_SHM_FUNCTIONS
+
+#include "cairo-xcb-private.h"
+
+#include <xcb/shm.h>
+#include <sys/ipc.h>
+#include <sys/shm.h>
+#include <errno.h>
+
+#define CAIRO_MAX_SHM_MEMORY (16*1024*1024)
+
+/* a simple buddy allocator for memory pools
+ * XXX fragmentation? use Doug Lea's malloc?
+ */
+
+typedef struct _cairo_xcb_shm_mem_block cairo_xcb_shm_mem_block_t;
+
+typedef enum {
+    PENDING_WAIT,
+    PENDING_POLL
+} shm_wait_type_t;
+
+struct _cairo_xcb_shm_mem_block {
+    unsigned int bits;
+    cairo_list_t link;
+};
+
+struct _cairo_xcb_shm_mem_pool {
+    int shmid;
+    uint32_t shmseg;
+
+    char *base;
+    unsigned int nBlocks;
+    cairo_xcb_shm_mem_block_t *blocks;
+    cairo_list_t free[32];
+    unsigned char *map;
+
+    unsigned int min_bits;     /* Minimum block size is 1 << min_bits */
+    unsigned int num_sizes;
+
+    size_t free_bytes;
+    size_t max_bytes;
+    unsigned int max_free_bits;
+
+    cairo_list_t link;
+};
+
+#define BITTEST(p, n)  ((p)->map[(n) >> 3] &   (128 >> ((n) & 7)))
+#define BITSET(p, n)   ((p)->map[(n) >> 3] |=  (128 >> ((n) & 7)))
+#define BITCLEAR(p, n) ((p)->map[(n) >> 3] &= ~(128 >> ((n) & 7)))
+
+static void
+clear_bits (cairo_xcb_shm_mem_pool_t *pi, size_t first, size_t last)
+{
+    size_t i, n = last;
+    size_t first_full = (first + 7) & ~7;
+    size_t past_full = last & ~7;
+    size_t bytes;
+
+    if (n > first_full)
+       n = first_full;
+    for (i = first; i < n; i++)
+         BITCLEAR (pi, i);
+
+    if (past_full > first_full) {
+       bytes = past_full - first_full;
+       bytes = bytes >> 3;
+       memset (pi->map + (first_full >> 3), 0, bytes);
+    }
+
+    if (past_full < n)
+       past_full = n;
+    for (i = past_full; i < last; i++)
+       BITCLEAR (pi, i);
+}
+
+static void
+free_bits (cairo_xcb_shm_mem_pool_t *pi,
+          size_t start,
+          unsigned int bits,
+          cairo_bool_t clear)
+{
+    cairo_xcb_shm_mem_block_t *block;
+
+    if (clear)
+       clear_bits (pi, start, start + (1 << bits));
+
+    block = pi->blocks + start;
+    block->bits = bits;
+
+    cairo_list_add (&block->link, &pi->free[bits]);
+
+    pi->free_bytes += 1 << (bits + pi->min_bits);
+    if (bits > pi->max_free_bits)
+       pi->max_free_bits = bits;
+}
+
+/* Add a chunk to the free list */
+static void
+free_blocks (cairo_xcb_shm_mem_pool_t *pi,
+            size_t first,
+            size_t last,
+            cairo_bool_t clear)
+{
+    size_t i;
+    size_t bits = 0;
+    size_t len = 1;
+
+    i = first;
+    while (i < last) {
+        /* To avoid cost quadratic in the number of different
+        * blocks produced from this chunk of store, we have to
+        * use the size of the previous block produced from this
+        * chunk as the starting point to work out the size of the
+        * next block we can produce. If you look at the binary
+        * representation of the starting points of the blocks
+        * produced, you can see that you first of all increase the
+        * size of the blocks produced up to some maximum as the
+        * address dealt with gets offsets added on which zap out
+        * low order bits, then decrease as the low order bits of the
+        * final block produced get added in. E.g. as you go from
+        * 001 to 0111 you generate blocks
+        * of size 001 at 001 taking you to 010
+        * of size 010 at 010 taking you to 100
+        * of size 010 at 100 taking you to 110
+        * of size 001 at 110 taking you to 111
+        * So the maximum total cost of the loops below this comment
+        * is one trip from the lowest blocksize to the highest and
+        * back again.
+        */
+       while (bits < pi->num_sizes - 1) {
+           size_t next_bits = bits + 1;
+           size_t next_len = len << 1;
+
+           if (i + next_bits > last) {
+               /* off end of chunk to be freed */
+               break;
+           }
+
+           if (i & (next_len - 1)) /* block would not be on boundary */
+               break;
+
+           bits = next_bits;
+           len = next_len;
+       }
+
+       do {
+           if (i + len > last) /* off end of chunk to be freed */
+               continue;
+
+           if (i & (len - 1)) /* block would not be on boundary */
+               continue;
+
+           /* OK */
+           break;
+
+           bits--;
+           len >>=1;
+       } while (len > 0);
+
+       if (len == 0)
+           break;
+
+       free_bits (pi, i, bits, clear);
+       i += len;
+    }
+}
+
+static cairo_status_t
+_cairo_xcb_shm_mem_pool_init (cairo_xcb_shm_mem_pool_t *pi,
+                             size_t bytes,
+                             unsigned int min_bits,
+                             unsigned int num_sizes)
+{
+    size_t setBits;
+    int i;
+
+    assert ((((unsigned long) pi->base) & ((1 << min_bits) - 1)) == 0);
+    assert (num_sizes < ARRAY_LENGTH (pi->free));
+
+    pi->free_bytes = 0;
+    pi->max_bytes = bytes;
+    pi->max_free_bits = 0;
+
+    setBits = bytes >> min_bits;
+    pi->blocks = calloc (setBits, sizeof (cairo_xcb_shm_mem_block_t));
+    if (pi->blocks == NULL)
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    pi->nBlocks = setBits;
+    pi->min_bits = min_bits;
+    pi->num_sizes = num_sizes;
+
+    for (i = 0; i < ARRAY_LENGTH (pi->free); i++)
+       cairo_list_init (&pi->free[i]);
+
+    pi->map = malloc ((setBits + 7) >> 3);
+    if (pi->map == NULL) {
+       free (pi->blocks);
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+    }
+
+    memset (pi->map, -1, (setBits + 7) >> 3);
+    clear_bits (pi, 0, setBits);
+
+    /* Now add all blocks to the free list */
+    free_blocks (pi, 0, setBits, 1);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_xcb_shm_mem_block_t *
+get_buddy (cairo_xcb_shm_mem_pool_t *pi,
+          size_t offset,
+          unsigned int bits)
+{
+    cairo_xcb_shm_mem_block_t *block;
+
+    assert (offset + (1 << bits) <= pi->nBlocks);
+
+    if (BITTEST (pi, offset + (1 << bits) - 1))
+       return NULL; /* buddy is allocated */
+
+    block = pi->blocks + offset;
+    if (block->bits != bits)
+       return NULL; /* buddy is partially allocated */
+
+    return block;
+}
+
+static void
+merge_buddies (cairo_xcb_shm_mem_pool_t *pi,
+              cairo_xcb_shm_mem_block_t *block,
+              unsigned int max_bits)
+{
+    size_t block_offset = block - pi->blocks;
+    unsigned int bits = block->bits;
+
+    while (bits < max_bits - 1) {
+       /* while you can, merge two blocks and get a legal block size */
+       size_t buddy_offset = block_offset ^ (1 << bits);
+
+       block = get_buddy (pi, buddy_offset, bits);
+       if (block == NULL)
+           break;
+
+       cairo_list_del (&block->link);
+
+       /* Merged block starts at buddy */
+       if (buddy_offset < block_offset)
+           block_offset = buddy_offset;
+
+       bits++;
+    }
+
+    block = pi->blocks + block_offset;
+    block->bits = bits;
+    cairo_list_add (&block->link, &pi->free[bits]);
+
+    if (bits > pi->max_free_bits)
+       pi->max_free_bits = bits;
+}
+
+/* attempt to merge all available buddies up to a particular size */
+static unsigned int
+merge_bits (cairo_xcb_shm_mem_pool_t *pi,
+           unsigned int max_bits)
+{
+    cairo_xcb_shm_mem_block_t *block, *buddy, *next;
+    unsigned int bits;
+
+    for (bits = 0; bits < max_bits - 1; bits++) {
+       cairo_list_foreach_entry_safe (block, next,
+                                      cairo_xcb_shm_mem_block_t,
+                                      &pi->free[bits],
+                                      link)
+       {
+           size_t buddy_offset = (block - pi->blocks) ^ (1 << bits);
+
+           buddy = get_buddy (pi, buddy_offset, bits);
+           if (buddy == NULL)
+               continue;
+
+           if (buddy == next) {
+               next = cairo_container_of (buddy->link.next,
+                                          cairo_xcb_shm_mem_block_t,
+                                          link);
+           }
+
+           cairo_list_del (&block->link);
+           merge_buddies (pi, block, max_bits);
+       }
+    }
+
+    return pi->max_free_bits;
+}
+
+/* find store for 1 << bits blocks */
+static void *
+buddy_malloc (cairo_xcb_shm_mem_pool_t *pi,
+             unsigned int bits)
+{
+    unsigned int b;
+    size_t offset;
+    size_t past;
+    cairo_xcb_shm_mem_block_t *block;
+
+    if (bits > pi->max_free_bits && bits > merge_bits (pi, bits))
+       return NULL;
+
+    /* Find a list with blocks big enough on it */
+    block = NULL;
+    for (b = bits; b <= pi->max_free_bits; b++) {
+       if (! cairo_list_is_empty (&pi->free[b])) {
+           block = cairo_list_first_entry (&pi->free[b],
+                                           cairo_xcb_shm_mem_block_t,
+                                           link);
+           break;
+       }
+    }
+    assert (block != NULL);
+
+    cairo_list_del (&block->link);
+
+    while (cairo_list_is_empty (&pi->free[pi->max_free_bits])) {
+       if (--pi->max_free_bits == 0)
+           break;
+    }
+
+    /* Mark end of allocated area */
+    offset = block - pi->blocks;
+    past = offset + (1 << bits);
+    BITSET (pi, past - 1);
+    block->bits = bits;
+
+    /* If we used a larger free block than we needed, free the rest */
+    pi->free_bytes -= 1 << (b + pi->min_bits);
+    free_blocks (pi, past, offset + (1 << b), 0);
+
+    return pi->base + ((block - pi->blocks) << pi->min_bits);
+}
+
+static void *
+_cairo_xcb_shm_mem_pool_malloc (cairo_xcb_shm_mem_pool_t *pi,
+                               size_t bytes)
+{
+    unsigned int bits;
+    size_t size;
+
+    size = 1 << pi->min_bits;
+    for (bits = 0; size < bytes; bits++)
+       size <<= 1;
+    if (bits >= pi->num_sizes)
+       return NULL;
+
+    return buddy_malloc (pi, bits);
+}
+
+static void
+_cairo_xcb_shm_mem_pool_free (cairo_xcb_shm_mem_pool_t *pi,
+                             char *storage)
+{
+    size_t block_offset;
+    cairo_xcb_shm_mem_block_t *block;
+
+    block_offset = (storage - pi->base) >> pi->min_bits;
+    block = pi->blocks + block_offset;
+
+    BITCLEAR (pi, block_offset + ((1 << block->bits) - 1));
+    pi->free_bytes += 1 << (block->bits + pi->min_bits);
+
+    merge_buddies (pi, block, pi->num_sizes);
+}
+
+static void
+_cairo_xcb_shm_mem_pool_destroy (cairo_xcb_shm_mem_pool_t *pool)
+{
+    shmdt (pool->base);
+    cairo_list_del (&pool->link);
+
+    free (pool->map);
+    free (pool->blocks);
+    free (pool);
+}
+
+static void
+_cairo_xcb_shm_info_finalize (cairo_xcb_shm_info_t *shm_info)
+{
+    cairo_xcb_connection_t *connection = shm_info->connection;
+
+    assert (CAIRO_MUTEX_IS_LOCKED (connection->shm_mutex));
+
+    _cairo_xcb_shm_mem_pool_free (shm_info->pool, shm_info->mem);
+    _cairo_freepool_free (&connection->shm_info_freelist, shm_info);
+
+    /* scan for old, unused pools - hold at least one in reserve */
+    if (! cairo_list_is_singular (&connection->shm_pools))
+    {
+       cairo_xcb_shm_mem_pool_t *pool, *next;
+       cairo_list_t head;
+
+       cairo_list_init (&head);
+       cairo_list_move (connection->shm_pools.next, &head);
+
+       cairo_list_foreach_entry_safe (pool, next, cairo_xcb_shm_mem_pool_t,
+                                      &connection->shm_pools, link)
+       {
+           if (pool->free_bytes == pool->max_bytes) {
+               _cairo_xcb_connection_shm_detach (connection, pool->shmseg);
+               _cairo_xcb_shm_mem_pool_destroy (pool);
+           }
+       }
+
+       cairo_list_move (head.next, &connection->shm_pools);
+    }
+}
+
+static void
+_cairo_xcb_shm_process_pending (cairo_xcb_connection_t *connection, shm_wait_type_t wait)
+{
+    cairo_xcb_shm_info_t *info, *next;
+    xcb_get_input_focus_reply_t *reply;
+
+    assert (CAIRO_MUTEX_IS_LOCKED (connection->shm_mutex));
+    cairo_list_foreach_entry_safe (info, next, cairo_xcb_shm_info_t,
+                                  &connection->shm_pending, pending)
+    {
+       switch (wait) {
+       case PENDING_WAIT:
+            reply = xcb_wait_for_reply (connection->xcb_connection,
+                                        info->sync.sequence, NULL);
+            break;
+       case PENDING_POLL:
+           if (! xcb_poll_for_reply (connection->xcb_connection,
+                                     info->sync.sequence,
+                                     (void **) &reply, NULL))
+               /* We cannot be sure the server finished with this image yet, so
+                * try again later. All other shm info are guranteed to have a
+                * larger sequence number and thus don't have to be checked. */
+               return;
+           break;
+       default:
+           /* silence Clang static analyzer warning */
+           ASSERT_NOT_REACHED;
+           reply = NULL;
+       }
+
+       free (reply);
+       cairo_list_del (&info->pending);
+       _cairo_xcb_shm_info_finalize (info);
+    }
+}
+
+cairo_int_status_t
+_cairo_xcb_connection_allocate_shm_info (cairo_xcb_connection_t *connection,
+                                        size_t size,
+                                        cairo_bool_t might_reuse,
+                                        cairo_xcb_shm_info_t **shm_info_out)
+{
+    cairo_xcb_shm_info_t *shm_info;
+    cairo_xcb_shm_mem_pool_t *pool, *next;
+    size_t bytes, maxbits = 16, minbits = 8;
+    size_t shm_allocated = 0;
+    void *mem = NULL;
+    cairo_status_t status;
+
+    assert (connection->flags & CAIRO_XCB_HAS_SHM);
+
+    CAIRO_MUTEX_LOCK (connection->shm_mutex);
+    _cairo_xcb_shm_process_pending (connection, PENDING_POLL);
+
+    if (might_reuse) {
+       cairo_list_foreach_entry (shm_info, cairo_xcb_shm_info_t,
+               &connection->shm_pending, pending) {
+           if (shm_info->size >= size) {
+               cairo_list_del (&shm_info->pending);
+               CAIRO_MUTEX_UNLOCK (connection->shm_mutex);
+
+               xcb_discard_reply (connection->xcb_connection, shm_info->sync.sequence);
+               shm_info->sync.sequence = XCB_NONE;
+
+               *shm_info_out = shm_info;
+               return CAIRO_STATUS_SUCCESS;
+           }
+       }
+    }
+
+    cairo_list_foreach_entry_safe (pool, next, cairo_xcb_shm_mem_pool_t,
+                                  &connection->shm_pools, link)
+    {
+       if (pool->free_bytes > size) {
+           mem = _cairo_xcb_shm_mem_pool_malloc (pool, size);
+           if (mem != NULL) {
+               /* keep the active pools towards the front */
+               cairo_list_move (&pool->link, &connection->shm_pools);
+               goto allocate_shm_info;
+           }
+       }
+       /* scan for old, unused pools */
+       if (pool->free_bytes == pool->max_bytes) {
+           _cairo_xcb_connection_shm_detach (connection,
+                                             pool->shmseg);
+           _cairo_xcb_shm_mem_pool_destroy (pool);
+       } else {
+           shm_allocated += pool->max_bytes;
+       }
+    }
+
+    if (unlikely (shm_allocated >= CAIRO_MAX_SHM_MEMORY)) {
+       CAIRO_MUTEX_UNLOCK (connection->shm_mutex);
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+    }
+
+    pool = malloc (sizeof (cairo_xcb_shm_mem_pool_t));
+    if (unlikely (pool == NULL)) {
+       CAIRO_MUTEX_UNLOCK (connection->shm_mutex);
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+    }
+
+    bytes = 1 << maxbits;
+    while (bytes <= size)
+       bytes <<= 1, maxbits++;
+    bytes <<= 3;
+
+    do {
+       pool->shmid = shmget (IPC_PRIVATE, bytes, IPC_CREAT | 0600);
+       if (pool->shmid != -1)
+           break;
+
+       /* If the allocation failed because we asked for too much memory, we try
+        * again with a smaller request, as long as our allocation still fits. */
+       bytes >>= 1;
+       if (errno != EINVAL || bytes < size)
+           break;
+    } while (TRUE);
+    if (pool->shmid == -1) {
+       int err = errno;
+       if (! (err == EINVAL || err == ENOMEM))
+           connection->flags &= ~CAIRO_XCB_HAS_SHM;
+       free (pool);
+       CAIRO_MUTEX_UNLOCK (connection->shm_mutex);
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+    }
+
+    pool->base = shmat (pool->shmid, NULL, 0);
+    if (unlikely (pool->base == (char *) -1)) {
+       shmctl (pool->shmid, IPC_RMID, NULL);
+       free (pool);
+       CAIRO_MUTEX_UNLOCK (connection->shm_mutex);
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+    }
+
+    status = _cairo_xcb_shm_mem_pool_init (pool,
+                                          bytes,
+                                          minbits,
+                                          maxbits - minbits + 1);
+    if (unlikely (status)) {
+       shmdt (pool->base);
+       free (pool);
+       CAIRO_MUTEX_UNLOCK (connection->shm_mutex);
+       return status;
+    }
+
+    pool->shmseg = _cairo_xcb_connection_shm_attach (connection, pool->shmid, FALSE);
+    shmctl (pool->shmid, IPC_RMID, NULL);
+
+    cairo_list_add (&pool->link, &connection->shm_pools);
+    mem = _cairo_xcb_shm_mem_pool_malloc (pool, size);
+
+  allocate_shm_info:
+    shm_info = _cairo_freepool_alloc (&connection->shm_info_freelist);
+    if (unlikely (shm_info == NULL)) {
+       _cairo_xcb_shm_mem_pool_free (pool, mem);
+       CAIRO_MUTEX_UNLOCK (connection->shm_mutex);
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+    }
+
+    shm_info->connection = connection;
+    shm_info->pool = pool;
+    shm_info->shm = pool->shmseg;
+    shm_info->size = size;
+    shm_info->offset = (char *) mem - (char *) pool->base;
+    shm_info->mem = mem;
+    shm_info->sync.sequence = XCB_NONE;
+
+    /* scan for old, unused pools */
+    cairo_list_foreach_entry_safe (pool, next, cairo_xcb_shm_mem_pool_t,
+                                  &connection->shm_pools, link)
+    {
+       if (pool->free_bytes == pool->max_bytes) {
+           _cairo_xcb_connection_shm_detach (connection,
+                                             pool->shmseg);
+           _cairo_xcb_shm_mem_pool_destroy (pool);
+       }
+    }
+    CAIRO_MUTEX_UNLOCK (connection->shm_mutex);
+
+    *shm_info_out = shm_info;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+void
+_cairo_xcb_shm_info_destroy (cairo_xcb_shm_info_t *shm_info)
+{
+    cairo_xcb_connection_t *connection = shm_info->connection;
+
+    /* We can only return shm_info->mem to the allocator when we can be sure
+     * that the X server no longer reads from it. Since the X server processes
+     * requests in order, we send a GetInputFocus here.
+     * _cairo_xcb_shm_process_pending () will later check if the reply for that
+     * request was received and then actually mark this memory area as free. */
+
+    CAIRO_MUTEX_LOCK (connection->shm_mutex);
+    assert (shm_info->sync.sequence == XCB_NONE);
+    shm_info->sync = xcb_get_input_focus (connection->xcb_connection);
+
+    cairo_list_init (&shm_info->pending);
+    cairo_list_add_tail (&shm_info->pending, &connection->shm_pending);
+    CAIRO_MUTEX_UNLOCK (connection->shm_mutex);
+}
+
+void
+_cairo_xcb_connection_shm_mem_pools_flush (cairo_xcb_connection_t *connection)
+{
+    CAIRO_MUTEX_LOCK (connection->shm_mutex);
+    _cairo_xcb_shm_process_pending (connection, PENDING_WAIT);
+    CAIRO_MUTEX_UNLOCK (connection->shm_mutex);
+}
+
+void
+_cairo_xcb_connection_shm_mem_pools_fini (cairo_xcb_connection_t *connection)
+{
+    assert (cairo_list_is_empty (&connection->shm_pending));
+    while (! cairo_list_is_empty (&connection->shm_pools)) {
+       _cairo_xcb_shm_mem_pool_destroy (cairo_list_first_entry (&connection->shm_pools,
+                                                                cairo_xcb_shm_mem_pool_t,
+                                                                link));
+    }
+}
+
+#endif /* CAIRO_HAS_XCB_SHM_FUNCTIONS */
diff --git a/src/cairo-xcb-surface-cairo.c b/src/cairo-xcb-surface-cairo.c
new file mode 100644 (file)
index 0000000..338a616
--- /dev/null
@@ -0,0 +1,94 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * Contributor(s):
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-clip-private.h"
+#include "cairo-xcb-private.h"
+
+cairo_int_status_t
+_cairo_xcb_surface_cairo_paint (cairo_xcb_surface_t    *surface,
+                               cairo_operator_t         op,
+                               const cairo_pattern_t   *source,
+                               const cairo_clip_t      *clip)
+{
+    return CAIRO_INT_STATUS_UNSUPPORTED;
+}
+
+cairo_int_status_t
+_cairo_xcb_surface_cairo_mask (cairo_xcb_surface_t     *surface,
+                              cairo_operator_t          op,
+                              const cairo_pattern_t    *source,
+                              const cairo_pattern_t    *mask,
+                              const cairo_clip_t       *clip)
+{
+    return CAIRO_INT_STATUS_UNSUPPORTED;
+}
+
+cairo_int_status_t
+_cairo_xcb_surface_cairo_stroke (cairo_xcb_surface_t   *surface,
+                                cairo_operator_t        op,
+                                const cairo_pattern_t  *source,
+                                const cairo_path_fixed_t       *path,
+                                const cairo_stroke_style_t     *style,
+                                const cairo_matrix_t           *ctm,
+                                const cairo_matrix_t           *ctm_inverse,
+                                double                  tolerance,
+                                cairo_antialias_t       antialias,
+                                const cairo_clip_t             *clip)
+{
+    return CAIRO_INT_STATUS_UNSUPPORTED;
+}
+
+cairo_int_status_t
+_cairo_xcb_surface_cairo_fill (cairo_xcb_surface_t     *surface,
+                              cairo_operator_t          op,
+                              const cairo_pattern_t    *source,
+                              const cairo_path_fixed_t *path,
+                              cairo_fill_rule_t         fill_rule,
+                              double                    tolerance,
+                              cairo_antialias_t         antialias,
+                              const cairo_clip_t               *clip)
+{
+    return CAIRO_INT_STATUS_UNSUPPORTED;
+}
+
+cairo_int_status_t
+_cairo_xcb_surface_cairo_glyphs (cairo_xcb_surface_t   *surface,
+                                cairo_operator_t                op,
+                                const cairo_pattern_t  *source,
+                                cairo_scaled_font_t    *scaled_font,
+                                cairo_glyph_t          *glyphs,
+                                int                     num_glyphs,
+                                const cairo_clip_t     *clip)
+{
+    return CAIRO_INT_STATUS_UNSUPPORTED;
+}
diff --git a/src/cairo-xcb-surface-core.c b/src/cairo-xcb-surface-core.c
new file mode 100644 (file)
index 0000000..447529d
--- /dev/null
@@ -0,0 +1,638 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * Contributor(s):
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-boxes-private.h"
+#include "cairo-xcb-private.h"
+#include "cairo-image-surface-private.h"
+#include "cairo-surface-backend-private.h"
+
+/* XXX dithering */
+
+typedef struct _cairo_xcb_pixmap {
+    cairo_surface_t base;
+
+    cairo_xcb_connection_t *connection;
+    cairo_xcb_screen_t *screen;
+
+    cairo_surface_t *owner;
+    xcb_pixmap_t pixmap;
+    int width;
+    int height;
+    int depth;
+    int x0, y0;
+    cairo_bool_t repeat;
+} cairo_xcb_pixmap_t;
+
+static cairo_status_t
+_cairo_xcb_pixmap_finish (void *abstract_surface)
+{
+    cairo_xcb_pixmap_t *surface = abstract_surface;
+    cairo_status_t status;
+
+    if (surface->owner != NULL) {
+       cairo_surface_destroy (surface->owner);
+    } else {
+       status = _cairo_xcb_connection_acquire (surface->connection);
+       if (unlikely (status))
+           return status;
+
+       _cairo_xcb_connection_free_pixmap (surface->connection,
+                                          surface->pixmap);
+       _cairo_xcb_connection_release (surface->connection);
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static const cairo_surface_backend_t _cairo_xcb_pixmap_backend = {
+    CAIRO_SURFACE_TYPE_XCB,
+    _cairo_xcb_pixmap_finish,
+};
+
+static cairo_xcb_pixmap_t *
+_cairo_xcb_pixmap_create (cairo_xcb_surface_t *target,
+                         int width, int height)
+{
+    cairo_xcb_pixmap_t *surface;
+
+    surface = malloc (sizeof (cairo_xcb_pixmap_t));
+    if (unlikely (surface == NULL))
+       return (cairo_xcb_pixmap_t *)
+           _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+    _cairo_surface_init (&surface->base,
+                        &_cairo_xcb_pixmap_backend,
+                        NULL,
+                        target->base.content);
+
+    surface->connection = target->connection;
+    surface->screen = target->screen;
+    surface->owner = NULL;
+    surface->width = width;
+    surface->height = height;
+    surface->depth = target->depth;
+    surface->x0 = surface->y0 = 0;
+    surface->repeat = FALSE;
+
+    surface->pixmap =
+       _cairo_xcb_connection_create_pixmap (surface->connection,
+                                            surface->depth,
+                                            target->drawable,
+                                            width, height);
+
+    return surface;
+}
+
+static cairo_xcb_pixmap_t *
+_cairo_xcb_pixmap_copy (cairo_xcb_surface_t *target)
+{
+    cairo_xcb_pixmap_t *surface;
+
+    surface = malloc (sizeof (cairo_xcb_pixmap_t));
+    if (unlikely (surface == NULL))
+       return (cairo_xcb_pixmap_t *)
+           _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+    _cairo_surface_init (&surface->base,
+                        &_cairo_xcb_pixmap_backend,
+                        NULL,
+                        target->base.content);
+
+    surface->connection = target->connection;
+    surface->screen = target->screen;
+    surface->pixmap = target->drawable;
+    surface->owner = cairo_surface_reference (&target->base);
+    surface->width = target->width;
+    surface->height = target->height;
+    surface->depth = target->depth;
+    surface->x0 = surface->y0 = 0;
+    surface->repeat = FALSE;
+
+    return surface;
+}
+
+#if CAIRO_HAS_XCB_SHM_FUNCTIONS
+static cairo_status_t
+_cairo_xcb_shm_image_create_shm (cairo_xcb_connection_t *connection,
+                                pixman_format_code_t pixman_format,
+                                int width, int height,
+                                cairo_image_surface_t **image_out,
+                                cairo_xcb_shm_info_t **shm_info_out)
+{
+    cairo_surface_t *image = NULL;
+    cairo_xcb_shm_info_t *shm_info = NULL;
+    cairo_status_t status;
+    size_t size, stride;
+
+    if (! (connection->flags & CAIRO_XCB_HAS_SHM))
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    if (unlikely (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX))
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    stride = CAIRO_STRIDE_FOR_WIDTH_BPP (width, PIXMAN_FORMAT_BPP (pixman_format));
+    size = stride * height;
+    if (size <= CAIRO_XCB_SHM_SMALL_IMAGE)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    status = _cairo_xcb_connection_allocate_shm_info (connection, size,
+                                                     FALSE, &shm_info);
+    if (unlikely (status))
+       return status;
+
+    image = _cairo_image_surface_create_with_pixman_format (shm_info->mem,
+                                                           pixman_format,
+                                                           width, height,
+                                                           stride);
+    status = image->status;
+    if (unlikely (status)) {
+       _cairo_xcb_shm_info_destroy (shm_info);
+       return status;
+    }
+
+    status = _cairo_user_data_array_set_data (&image->user_data,
+                                             (const cairo_user_data_key_t *) connection,
+                                             shm_info,
+                                             (cairo_destroy_func_t) _cairo_xcb_shm_info_destroy);
+
+    if (unlikely (status)) {
+       cairo_surface_destroy (image);
+       _cairo_xcb_shm_info_destroy (shm_info);
+       return status;
+    }
+
+    *image_out = (cairo_image_surface_t *) image;
+    *shm_info_out = shm_info;
+    return CAIRO_STATUS_SUCCESS;
+}
+#else
+static cairo_status_t
+_cairo_xcb_shm_image_create_shm (cairo_xcb_connection_t *connection,
+                                pixman_format_code_t pixman_format,
+                                int width, int height,
+                                cairo_image_surface_t **image_out,
+                                cairo_xcb_shm_info_t **shm_info_out)
+{
+    return CAIRO_INT_STATUS_UNSUPPORTED;
+}
+#endif
+
+cairo_status_t
+_cairo_xcb_shm_image_create (cairo_xcb_connection_t *connection,
+                            pixman_format_code_t pixman_format,
+                            int width, int height,
+                            cairo_image_surface_t **image_out,
+                            cairo_xcb_shm_info_t **shm_info_out)
+{
+    cairo_surface_t *image = NULL;
+    cairo_xcb_shm_info_t *shm_info = NULL;
+    cairo_status_t status;
+
+    status = _cairo_xcb_shm_image_create_shm (connection,
+                                             pixman_format,
+                                             width,
+                                             height,
+                                             image_out,
+                                             shm_info_out);
+
+    if (status != CAIRO_STATUS_SUCCESS) {
+       image = _cairo_image_surface_create_with_pixman_format (NULL,
+                                                               pixman_format,
+                                                               width, height,
+                                                               0);
+       status = image->status;
+       if (unlikely (status))
+           return status;
+
+       *image_out = (cairo_image_surface_t *) image;
+       *shm_info_out = shm_info;
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_xcb_pixmap_t *
+_pixmap_from_image (cairo_xcb_surface_t *target,
+                   xcb_render_pictformat_t format,
+                   cairo_image_surface_t *image,
+                   cairo_xcb_shm_info_t *shm_info)
+{
+    xcb_gcontext_t gc;
+    cairo_xcb_pixmap_t *pixmap;
+
+    pixmap = _cairo_xcb_pixmap_create (target,
+                                      image->width,
+                                      image->height);
+    if (unlikely (pixmap->base.status))
+       return pixmap;
+
+    gc = _cairo_xcb_screen_get_gc (target->screen, pixmap->pixmap, image->depth);
+
+    if (shm_info != NULL) {
+       _cairo_xcb_connection_shm_put_image (target->connection,
+                                            pixmap->pixmap, gc,
+                                            image->width, image->height,
+                                            0, 0,
+                                            image->width, image->height,
+                                            0, 0,
+                                            image->depth,
+                                            shm_info->shm,
+                                            shm_info->offset);
+    } else {
+       int len;
+
+       /* Do we need to trim the image? */
+       len = CAIRO_STRIDE_FOR_WIDTH_BPP (image->width,
+                                         PIXMAN_FORMAT_BPP (image->pixman_format));
+       if (len == image->stride) {
+           _cairo_xcb_connection_put_image (target->connection,
+                                            pixmap->pixmap, gc,
+                                            image->width, image->height,
+                                            0, 0,
+                                            image->depth,
+                                            image->stride,
+                                            image->data);
+       } else {
+           _cairo_xcb_connection_put_subimage (target->connection,
+                                               pixmap->pixmap, gc,
+                                               0, 0,
+                                               image->width, image->height,
+                                               PIXMAN_FORMAT_BPP (image->pixman_format) / 8,
+                                               image->stride,
+                                               0, 0,
+                                               image->depth,
+                                               image->data);
+
+       }
+    }
+
+    _cairo_xcb_screen_put_gc (target->screen, image->depth, gc);
+
+    return pixmap;
+}
+
+static cairo_xcb_pixmap_t *
+_render_to_pixmap (cairo_xcb_surface_t *target,
+                  const cairo_pattern_t *pattern,
+                  const cairo_rectangle_int_t *extents)
+{
+    cairo_image_surface_t *image;
+    cairo_xcb_shm_info_t *shm_info;
+    cairo_pattern_union_t copy;
+    cairo_status_t status;
+    cairo_xcb_pixmap_t *pixmap;
+
+    status = _cairo_xcb_shm_image_create (target->screen->connection,
+                                         target->pixman_format,
+                                         extents->width, extents->height,
+                                         &image, &shm_info);
+    if (unlikely (status))
+       return (cairo_xcb_pixmap_t *) _cairo_surface_create_in_error (status);
+
+    _cairo_pattern_init_static_copy (&copy.base, pattern);
+    cairo_matrix_translate (&copy.base.matrix, -extents->x, -extents->y);
+    status = _cairo_surface_paint (&image->base,
+                                  CAIRO_OPERATOR_SOURCE,
+                                  &copy.base,
+                                  NULL);
+    if (unlikely (status)) {
+       cairo_surface_destroy (&image->base);
+       return (cairo_xcb_pixmap_t *) _cairo_surface_create_in_error (status);
+    }
+
+    pixmap = _pixmap_from_image (target, target->xrender_format, image, shm_info);
+    cairo_surface_destroy (&image->base);
+
+    if (unlikely (pixmap->base.status))
+       return pixmap;
+
+    pixmap->x0 = -extents->x;
+    pixmap->y0 = -extents->y;
+    return pixmap;
+}
+
+static cairo_xcb_pixmap_t *
+_copy_to_pixmap (cairo_xcb_surface_t *source)
+{
+    cairo_xcb_pixmap_t *pixmap;
+
+    /* If the source may be a window, we need to copy it and its children
+     * via a temporary pixmap so that we can IncludeInferiors on the source
+     * and use ClipByChildren on the destination.
+     */
+    if (source->owns_pixmap) {
+       pixmap = _cairo_xcb_pixmap_copy (source);
+       if (unlikely (pixmap->base.status))
+           return pixmap;
+    } else {
+       uint32_t values[1];
+       xcb_gcontext_t gc;
+
+       pixmap = _cairo_xcb_pixmap_create (source,
+                                          source->width,
+                                          source->height);
+       if (unlikely (pixmap->base.status))
+           return pixmap;
+
+       gc = _cairo_xcb_screen_get_gc (source->screen,
+                                      pixmap->pixmap,
+                                      pixmap->depth);
+
+       values[0] = TRUE;
+       _cairo_xcb_connection_change_gc (pixmap->connection, gc,
+                                        XCB_GC_SUBWINDOW_MODE, values);
+
+       _cairo_xcb_connection_copy_area (pixmap->connection,
+                                        source->drawable,
+                                        pixmap->pixmap, gc,
+                                        0, 0,
+                                        0, 0,
+                                        source->width,
+                                        source->height);
+
+       values[0] = FALSE;
+       _cairo_xcb_connection_change_gc (pixmap->connection, gc,
+                                        XCB_GC_SUBWINDOW_MODE, values);
+
+       _cairo_xcb_screen_put_gc (source->screen,
+                                 pixmap->depth,
+                                 gc);
+    }
+
+    return pixmap;
+}
+static cairo_xcb_pixmap_t *
+_cairo_xcb_surface_pixmap (cairo_xcb_surface_t *target,
+                          const cairo_surface_pattern_t *pattern,
+                          const cairo_rectangle_int_t *extents,
+                          int tx, int ty)
+{
+    cairo_surface_t *source;
+    cairo_xcb_pixmap_t *pixmap;
+
+    source =  pattern->surface;
+    pixmap = (cairo_xcb_pixmap_t *)
+       _cairo_surface_has_snapshot (source, &_cairo_xcb_pixmap_backend);
+    if (pixmap != NULL && pixmap->screen == target->screen)
+       return (cairo_xcb_pixmap_t *) cairo_surface_reference (&pixmap->base);
+
+    if (source->type == CAIRO_SURFACE_TYPE_XCB &&
+       ((cairo_xcb_surface_t *) source)->screen == target->screen)
+    {
+       cairo_xcb_surface_t *xcb_source = (cairo_xcb_surface_t *) source;
+
+       if (xcb_source->depth == target->depth)
+           pixmap = _copy_to_pixmap (xcb_source);
+    }
+#if CAIRO_HAS_XLIB_XCB_FUNCTIONS
+    else if (source->type == CAIRO_SURFACE_TYPE_XLIB &&
+            ((cairo_xlib_xcb_surface_t *) source)->xcb->screen == target->screen)
+    {
+       cairo_xcb_surface_t *xcb_source = ((cairo_xlib_xcb_surface_t *) source)->xcb;
+
+       if (xcb_source->depth == target->depth)
+           pixmap = _copy_to_pixmap (xcb_source);
+    }
+#endif
+
+    if (pixmap == NULL) {
+       cairo_rectangle_int_t rect;
+
+       if (! _cairo_surface_get_extents (source, &rect)) {
+           rect.x = rect.y = 0;
+           rect.width  = target->width;
+           rect.height = target->height;
+       }
+
+       pixmap = _render_to_pixmap (target, &pattern->base, &rect);
+    }
+
+    if (unlikely (pixmap->base.status))
+       return pixmap;
+
+    _cairo_surface_attach_snapshot (source, &pixmap->base, NULL);
+
+    if (pattern->base.extend != CAIRO_EXTEND_NONE) {
+       if (extents->x < 0 || extents->y < 0 ||
+           extents->x + extents->width  > pixmap->width ||
+           extents->y + extents->height > pixmap->height)
+       {
+           pixmap->repeat = TRUE;
+       }
+    }
+
+    pixmap->x0 += tx;
+    pixmap->y0 += ty;
+
+    return pixmap;
+}
+
+static cairo_xcb_pixmap_t *
+_cairo_xcb_pixmap_for_pattern (cairo_xcb_surface_t *target,
+                              const cairo_pattern_t *pattern,
+                              const cairo_rectangle_int_t *extents)
+{
+    int tx, ty;
+
+    switch (pattern->type) {
+    case CAIRO_PATTERN_TYPE_SURFACE:
+       /* Core can only perform a native, unscaled blit, but can handle tiles */
+       if (_cairo_matrix_is_integer_translation (&pattern->matrix, &tx, &ty)) {
+           switch (pattern->extend) {
+           case CAIRO_EXTEND_NONE:
+           case CAIRO_EXTEND_REPEAT:
+               return _cairo_xcb_surface_pixmap (target,
+                                                 (cairo_surface_pattern_t *) pattern,
+                                                 extents, tx, ty);
+
+           default:
+           case CAIRO_EXTEND_PAD:
+           case CAIRO_EXTEND_REFLECT:
+               break;
+           }
+       }
+       /* fallthrough */
+    case CAIRO_PATTERN_TYPE_LINEAR:
+    case CAIRO_PATTERN_TYPE_RADIAL:
+    case CAIRO_PATTERN_TYPE_MESH:
+       return _render_to_pixmap (target, pattern, extents);
+
+    default:
+    case CAIRO_PATTERN_TYPE_SOLID:
+       ASSERT_NOT_REACHED;
+       return NULL;
+    }
+}
+
+cairo_status_t
+_cairo_xcb_surface_core_copy_boxes (cairo_xcb_surface_t                *dst,
+                                  const cairo_pattern_t        *src_pattern,
+                                  const cairo_rectangle_int_t  *extents,
+                                  const cairo_boxes_t          *boxes)
+{
+    cairo_xcb_pixmap_t *src;
+    const struct _cairo_boxes_chunk *chunk;
+    xcb_gcontext_t gc;
+    cairo_status_t status;
+
+    status = _cairo_xcb_connection_acquire (dst->connection);
+    if (unlikely (status))
+       return status;
+
+    src = _cairo_xcb_pixmap_for_pattern (dst, src_pattern, extents);
+    status = src->base.status;
+    if (unlikely (status))
+       goto CLEANUP_CONNECTION;
+
+    assert (src->depth == dst->depth);
+
+    gc = _cairo_xcb_screen_get_gc (dst->screen, src->pixmap, src->depth);
+
+    if (src->repeat) {
+       uint32_t mask =
+           XCB_GC_FILL_STYLE |
+           XCB_GC_TILE |
+           XCB_GC_TILE_STIPPLE_ORIGIN_X |
+           XCB_GC_TILE_STIPPLE_ORIGIN_Y;
+       uint32_t values[] = {
+           XCB_FILL_STYLE_TILED,
+           src->pixmap,
+           - src->x0, - src->y0,
+       };
+       xcb_rectangle_t *xcb_rects;
+
+       _cairo_xcb_connection_change_gc (dst->connection, gc, mask, values);
+
+       for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
+           int i;
+
+           xcb_rects = (xcb_rectangle_t *) chunk->base;
+
+           for (i = 0; i < chunk->count; i++) {
+               int x1 = _cairo_fixed_integer_round (chunk->base[i].p1.x);
+               int x2 = _cairo_fixed_integer_round (chunk->base[i].p2.x);
+               int y1 = _cairo_fixed_integer_round (chunk->base[i].p1.y);
+               int y2 = _cairo_fixed_integer_round (chunk->base[i].p2.y);
+
+               xcb_rects[i].x = x1;
+               xcb_rects[i].y = y1;
+               xcb_rects[i].width  = x2 - x1;
+               xcb_rects[i].height = y2 - y1;
+           }
+           _cairo_xcb_connection_poly_fill_rectangle (dst->connection,
+                                                      dst->drawable,
+                                                      gc, chunk->count, xcb_rects);
+       }
+
+       values[0] = 0;
+       _cairo_xcb_connection_change_gc (dst->connection, gc, XCB_GC_FILL_STYLE, values);
+    } else {
+       for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
+           int i;
+
+           for (i = 0; i < chunk->count; i++) {
+               int x1 = _cairo_fixed_integer_round (chunk->base[i].p1.x);
+               int x2 = _cairo_fixed_integer_round (chunk->base[i].p2.x);
+               int y1 = _cairo_fixed_integer_round (chunk->base[i].p1.y);
+               int y2 = _cairo_fixed_integer_round (chunk->base[i].p2.y);
+
+               _cairo_xcb_connection_copy_area (dst->connection,
+                                                src->pixmap,
+                                                dst->drawable, gc,
+                                                src->x0 + x1,
+                                                src->y0 + y1,
+                                                x1, y1,
+                                                x2 - x2, y2 - x2);
+           }
+       }
+    }
+
+    _cairo_xcb_screen_put_gc (dst->screen, src->depth, gc);
+    cairo_surface_destroy (&src->base);
+
+  CLEANUP_CONNECTION:
+    _cairo_xcb_connection_release (dst->connection);
+
+    return status;
+}
+
+cairo_status_t
+_cairo_xcb_surface_core_fill_boxes (cairo_xcb_surface_t *dst,
+                                   const cairo_color_t *color,
+                                   cairo_boxes_t *boxes)
+{
+    struct _cairo_boxes_chunk *chunk;
+    xcb_gcontext_t gc;
+    cairo_status_t status;
+
+    status = _cairo_xcb_connection_acquire (dst->connection);
+    if (unlikely (status))
+       return status;
+
+    gc = _cairo_xcb_screen_get_gc (dst->screen, dst->drawable, dst->depth);
+
+#if 0
+    xcb_pixmap_t source;
+
+    source = _dither_source (dst, color);
+    XSetTSOrigin (surface->dpy, gc, 0, 0);
+    XSetTile (surface->dpy, gc, source);
+#endif
+
+    for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
+       xcb_rectangle_t *xcb_rects;
+       int i;
+
+       xcb_rects = (xcb_rectangle_t *) chunk->base;
+       for (i = 0; i < chunk->count; i++) {
+           int x1 = _cairo_fixed_integer_round (chunk->base[i].p1.x);
+           int x2 = _cairo_fixed_integer_round (chunk->base[i].p2.x);
+           int y1 = _cairo_fixed_integer_round (chunk->base[i].p1.y);
+           int y2 = _cairo_fixed_integer_round (chunk->base[i].p2.y);
+
+           xcb_rects[i].x = x1;
+           xcb_rects[i].y = y1;
+           xcb_rects[i].width  = x2 - x1;
+           xcb_rects[i].height = y2 - y1;
+       }
+
+       _cairo_xcb_connection_poly_fill_rectangle (dst->connection,
+                                                  dst->drawable, gc,
+                                                  chunk->count, xcb_rects);
+    }
+
+    _cairo_xcb_screen_put_gc (dst->screen, dst->depth, gc);
+    _cairo_xcb_connection_release (dst->connection);
+
+    return CAIRO_STATUS_SUCCESS;
+}
diff --git a/src/cairo-xcb-surface-render.c b/src/cairo-xcb-surface-render.c
new file mode 100644 (file)
index 0000000..033aef9
--- /dev/null
@@ -0,0 +1,4776 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * Contributor(s):
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-xcb-private.h"
+
+#include "cairo-boxes-private.h"
+#include "cairo-clip-private.h"
+#include "cairo-composite-rectangles-private.h"
+#include "cairo-image-surface-private.h"
+#include "cairo-region-private.h"
+#include "cairo-surface-offset-private.h"
+#include "cairo-surface-snapshot-private.h"
+#include "cairo-surface-subsurface-private.h"
+#include "cairo-traps-private.h"
+
+#define PIXMAN_MAX_INT ((pixman_fixed_1 >> 1) - pixman_fixed_e) /* need to ensure deltas also fit */
+
+/**
+ * SECTION:cairo-xcb-xrender
+ * @Title: XCB Surfaces
+ * @Short_Description: X Window System rendering using the XCB library and the X Render extension
+ * @See_Also: #cairo_surface_t
+ *
+ * The XCB surface is used to render cairo graphics to X Window System
+ * windows and pixmaps using the XCB library and its X Render extension.
+ *
+ * Note that the XCB surface automatically takes advantage of the X Render
+ * extension if it is available.
+ */
+
+static cairo_status_t
+_clip_and_composite_boxes (cairo_xcb_surface_t *dst,
+                          cairo_operator_t op,
+                          const cairo_pattern_t *src,
+                          cairo_boxes_t *boxes,
+                          cairo_composite_rectangles_t *extents);
+
+static inline cairo_xcb_connection_t *
+_picture_to_connection (cairo_xcb_picture_t *picture)
+{
+    return (cairo_xcb_connection_t *) picture->base.device;
+}
+
+static void
+_cairo_xcb_surface_ensure_picture (cairo_xcb_surface_t *surface);
+
+static uint32_t
+hars_petruska_f54_1_random (void)
+{
+#define rol(x,k) ((x << k) | (x >> (32-k)))
+    static uint32_t x;
+    return x = (x ^ rol (x, 5) ^ rol (x, 24)) + 0x37798849;
+#undef rol
+}
+
+static cairo_status_t
+_cairo_xcb_picture_finish (void *abstract_surface)
+{
+    cairo_xcb_picture_t *surface = abstract_surface;
+    cairo_xcb_connection_t *connection = _picture_to_connection (surface);
+    cairo_status_t status;
+
+    status = _cairo_xcb_connection_acquire (connection);
+    cairo_list_del (&surface->link);
+    if (unlikely (status))
+       return status;
+
+    _cairo_xcb_connection_render_free_picture (connection, surface->picture);
+
+    _cairo_xcb_connection_release (connection);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static const cairo_surface_backend_t _cairo_xcb_picture_backend = {
+    CAIRO_SURFACE_TYPE_XCB,
+    _cairo_xcb_picture_finish,
+};
+
+static const struct xcb_render_transform_t identity_transform = {
+    1 << 16, 0, 0,
+    0, 1 << 16, 0,
+    0, 0, 1 << 16,
+};
+
+static cairo_xcb_picture_t *
+_cairo_xcb_picture_create (cairo_xcb_screen_t *screen,
+                          pixman_format_code_t pixman_format,
+                          xcb_render_pictformat_t xrender_format,
+                          int width, int height)
+{
+    cairo_xcb_picture_t *surface;
+
+    surface = malloc (sizeof (cairo_xcb_picture_t));
+    if (unlikely (surface == NULL))
+       return (cairo_xcb_picture_t *)
+           _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+    _cairo_surface_init (&surface->base,
+                        &_cairo_xcb_picture_backend,
+                        &screen->connection->device,
+                        _cairo_content_from_pixman_format (pixman_format));
+
+    cairo_list_add (&surface->link, &screen->pictures);
+
+    surface->screen = screen;
+    surface->picture = _cairo_xcb_connection_get_xid (screen->connection);
+    surface->pixman_format = pixman_format;
+    surface->xrender_format = xrender_format;
+
+    surface->x0 = surface->y0 = 0;
+    surface->x = surface->y = 0;
+    surface->width = width;
+    surface->height = height;
+
+    surface->transform = identity_transform;
+    surface->extend = CAIRO_EXTEND_NONE;
+    surface->filter = CAIRO_FILTER_NEAREST;
+    surface->has_component_alpha = FALSE;
+
+    return surface;
+}
+
+static inline cairo_bool_t
+_operator_is_supported (uint32_t flags, cairo_operator_t op)
+{
+    if (op <= CAIRO_OPERATOR_SATURATE)
+       return TRUE;
+
+    /* Can we use PDF operators? */
+#if CAIRO_XCB_RENDER_AT_LEAST(0, 11)
+    if (op <= CAIRO_OPERATOR_HSL_LUMINOSITY)
+       return flags & CAIRO_XCB_RENDER_HAS_PDF_OPERATORS;
+#endif
+
+    return FALSE;
+}
+
+static int
+_render_operator (cairo_operator_t op)
+{
+#define C(x,y) case CAIRO_OPERATOR_##x: return XCB_RENDER_PICT_OP_##y
+    switch (op) {
+    C(CLEAR, CLEAR);
+    C(SOURCE, SRC);
+
+    C(OVER, OVER);
+    C(IN, IN);
+    C(OUT, OUT);
+    C(ATOP, ATOP);
+
+    C(DEST, DST);
+    C(DEST_OVER, OVER_REVERSE);
+    C(DEST_IN, IN_REVERSE);
+    C(DEST_OUT, OUT_REVERSE);
+    C(DEST_ATOP, ATOP_REVERSE);
+
+    C(XOR, XOR);
+    C(ADD, ADD);
+    C(SATURATE, SATURATE);
+
+    /* PDF operators were added in RENDER 0.11, check if the xcb headers have
+     * the defines, else fall through to the default case. */
+#if CAIRO_XCB_RENDER_AT_LEAST(0, 11)
+#define BLEND(x,y) C(x,y)
+#else
+#define BLEND(x,y) case CAIRO_OPERATOR_##x:
+#endif
+    BLEND(MULTIPLY, MULTIPLY);
+    BLEND(SCREEN, SCREEN);
+    BLEND(OVERLAY, OVERLAY);
+    BLEND(DARKEN, DARKEN);
+    BLEND(LIGHTEN, LIGHTEN);
+    BLEND(COLOR_DODGE, COLOR_DODGE);
+    BLEND(COLOR_BURN, COLOR_BURN);
+    BLEND(HARD_LIGHT, HARD_LIGHT);
+    BLEND(SOFT_LIGHT, SOFT_LIGHT);
+    BLEND(DIFFERENCE, DIFFERENCE);
+    BLEND(EXCLUSION, EXCLUSION);
+    BLEND(HSL_HUE, HSL_HUE);
+    BLEND(HSL_SATURATION, HSL_SATURATION);
+    BLEND(HSL_COLOR, HSL_COLOR);
+    BLEND(HSL_LUMINOSITY, HSL_LUMINOSITY);
+
+    default:
+       ASSERT_NOT_REACHED;
+       return XCB_RENDER_PICT_OP_OVER;
+    }
+}
+
+static cairo_status_t
+_cairo_xcb_surface_set_clip_region (cairo_xcb_surface_t *surface,
+                                   cairo_region_t      *region)
+{
+    xcb_rectangle_t stack_rects[CAIRO_STACK_ARRAY_LENGTH (xcb_rectangle_t)];
+    xcb_rectangle_t *rects = stack_rects;
+    int i, num_rects;
+
+    num_rects = cairo_region_num_rectangles (region);
+
+    if (num_rects > ARRAY_LENGTH (stack_rects)) {
+       rects = _cairo_malloc_ab (num_rects, sizeof (xcb_rectangle_t));
+       if (unlikely (rects == NULL)) {
+           return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+       }
+    }
+
+    for (i = 0; i < num_rects; i++) {
+       cairo_rectangle_int_t rect;
+
+       cairo_region_get_rectangle (region, i, &rect);
+
+       rects[i].x = rect.x;
+       rects[i].y = rect.y;
+       rects[i].width  = rect.width;
+       rects[i].height = rect.height;
+    }
+
+    _cairo_xcb_connection_render_set_picture_clip_rectangles (surface->connection,
+                                                             surface->picture,
+                                                             0, 0,
+                                                             num_rects, rects);
+
+    if (rects != stack_rects)
+       free (rects);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_xcb_surface_clear_clip_region (cairo_xcb_surface_t *surface)
+{
+    uint32_t values[] = { XCB_NONE };
+    _cairo_xcb_connection_render_change_picture (surface->connection,
+                                                surface->picture,
+                                                XCB_RENDER_CP_CLIP_MASK,
+                                                values);
+}
+
+static void
+_cairo_xcb_surface_set_precision (cairo_xcb_surface_t  *surface,
+                                 cairo_antialias_t      antialias)
+{
+    cairo_xcb_connection_t *connection = surface->connection;
+    uint32_t precision;
+
+    if (connection->force_precision != -1)
+           precision = connection->force_precision;
+    else switch (antialias) {
+    default:
+    case CAIRO_ANTIALIAS_DEFAULT:
+    case CAIRO_ANTIALIAS_GRAY:
+    case CAIRO_ANTIALIAS_NONE:
+    case CAIRO_ANTIALIAS_FAST:
+    case CAIRO_ANTIALIAS_GOOD:
+       precision = XCB_RENDER_POLY_MODE_IMPRECISE;
+       break;
+    case CAIRO_ANTIALIAS_SUBPIXEL:
+    case CAIRO_ANTIALIAS_BEST:
+       precision = XCB_RENDER_POLY_MODE_PRECISE;
+       break;
+    }
+
+    if (surface->precision != precision) {
+       _cairo_xcb_connection_render_change_picture (connection,
+                                                    surface->picture,
+                                                    XCB_RENDER_CP_POLY_MODE,
+                                                    &precision);
+       surface->precision = precision;
+    }
+}
+
+
+static void
+_cairo_xcb_surface_ensure_picture (cairo_xcb_surface_t *surface)
+{
+    assert (surface->fallback == NULL);
+    if (surface->picture == XCB_NONE) {
+       uint32_t values[1];
+       uint32_t flags = 0;
+
+       if (surface->precision != XCB_RENDER_POLY_MODE_PRECISE) {
+           flags |= XCB_RENDER_CP_POLY_MODE;
+           values[0] = surface->precision;
+       }
+
+       surface->picture = _cairo_xcb_connection_get_xid (surface->connection);
+       _cairo_xcb_connection_render_create_picture (surface->connection,
+                                                    surface->picture,
+                                                    surface->drawable,
+                                                    surface->xrender_format,
+                                                    flags, values);
+    }
+}
+
+static cairo_xcb_picture_t *
+_picture_from_image (cairo_xcb_surface_t *target,
+                    xcb_render_pictformat_t format,
+                    cairo_image_surface_t *image,
+                    cairo_xcb_shm_info_t *shm_info)
+{
+    xcb_pixmap_t pixmap;
+    xcb_gcontext_t gc;
+    cairo_xcb_picture_t *picture;
+
+    pixmap = _cairo_xcb_connection_create_pixmap (target->connection,
+                                                 image->depth,
+                                                 target->drawable,
+                                                 image->width, image->height);
+
+    gc = _cairo_xcb_screen_get_gc (target->screen, pixmap, image->depth);
+
+    if (shm_info != NULL) {
+       _cairo_xcb_connection_shm_put_image (target->connection,
+                                            pixmap, gc,
+                                            image->width, image->height,
+                                            0, 0,
+                                            image->width, image->height,
+                                            0, 0,
+                                            image->depth,
+                                            shm_info->shm,
+                                            shm_info->offset);
+    } else {
+       int len;
+
+       /* Do we need to trim the image? */
+       len = CAIRO_STRIDE_FOR_WIDTH_BPP (image->width, PIXMAN_FORMAT_BPP (image->pixman_format));
+       if (len == image->stride) {
+           _cairo_xcb_connection_put_image (target->connection,
+                                            pixmap, gc,
+                                            image->width, image->height,
+                                            0, 0,
+                                            image->depth,
+                                            image->stride,
+                                            image->data);
+       } else {
+           _cairo_xcb_connection_put_subimage (target->connection,
+                                               pixmap, gc,
+                                               0, 0,
+                                               image->width, image->height,
+                                               PIXMAN_FORMAT_BPP (image->pixman_format) / 8,
+                                               image->stride,
+                                               0, 0,
+                                               image->depth,
+                                               image->data);
+
+       }
+    }
+
+    _cairo_xcb_screen_put_gc (target->screen, image->depth, gc);
+
+    picture = _cairo_xcb_picture_create (target->screen,
+                                        image->pixman_format, format,
+                                        image->width, image->height);
+    if (likely (picture->base.status == CAIRO_STATUS_SUCCESS)) {
+       _cairo_xcb_connection_render_create_picture (target->connection,
+                                                    picture->picture, pixmap, format,
+                                                    0, 0);
+    }
+
+    _cairo_xcb_connection_free_pixmap (target->connection, pixmap);
+
+    return picture;
+}
+
+static cairo_bool_t
+_pattern_is_supported (uint32_t flags,
+                      const cairo_pattern_t *pattern)
+
+{
+    if (pattern->type == CAIRO_PATTERN_TYPE_SOLID)
+       return TRUE;
+
+    if (! _cairo_matrix_is_integer_translation (&pattern->matrix, NULL, NULL)) {
+       if ((flags & CAIRO_XCB_RENDER_HAS_PICTURE_TRANSFORM) == 0)
+           return FALSE;
+    }
+
+    switch (pattern->extend) {
+    default:
+       ASSERT_NOT_REACHED;
+    case CAIRO_EXTEND_NONE:
+    case CAIRO_EXTEND_REPEAT:
+       break;
+    case CAIRO_EXTEND_PAD:
+    case CAIRO_EXTEND_REFLECT:
+       if ((flags & CAIRO_XCB_RENDER_HAS_EXTENDED_REPEAT) == 0)
+           return FALSE;
+    }
+
+    if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) {
+       cairo_filter_t filter;
+
+       filter = pattern->filter;
+       if (_cairo_matrix_has_unity_scale (&pattern->matrix) &&
+           _cairo_matrix_is_integer_translation (&pattern->matrix, NULL, NULL))
+       {
+           filter = CAIRO_FILTER_NEAREST;
+       }
+
+       if (! (filter == CAIRO_FILTER_NEAREST || filter == CAIRO_FILTER_FAST)) {
+           if ((flags & CAIRO_XCB_RENDER_HAS_FILTERS) == 0)
+               return FALSE;
+       }
+    } else { /* gradient */
+       if ((flags & CAIRO_XCB_RENDER_HAS_GRADIENTS) == 0)
+           return FALSE;
+
+       /* The RENDER specification says that the inner circle has to be
+        * completely contained inside the outer one. */
+       if (pattern->type == CAIRO_PATTERN_TYPE_RADIAL &&
+           ! _cairo_radial_pattern_focus_is_inside ((cairo_radial_pattern_t *) pattern))
+       {
+           return FALSE;
+       }
+    }
+
+    return pattern->type != CAIRO_PATTERN_TYPE_MESH;
+}
+
+static void
+_cairo_xcb_picture_set_matrix (cairo_xcb_picture_t *picture,
+                              const cairo_matrix_t *matrix,
+                              cairo_filter_t filter,
+                              double xc, double yc)
+{
+    xcb_render_transform_t transform;
+    pixman_transform_t *pixman_transform;
+    cairo_int_status_t ignored;
+
+    /* Casting between pixman_transform_t and xcb_render_transform_t is safe
+     * because they happen to be the exact same type.
+     */
+    pixman_transform = (pixman_transform_t *) &transform;
+
+    picture->x = picture->x0;
+    picture->y = picture->y0;
+    ignored = _cairo_matrix_to_pixman_matrix_offset (matrix, filter, xc, yc,
+                                                    pixman_transform,
+                                                    &picture->x, &picture->y);
+
+    if (memcmp (&picture->transform, &transform, sizeof (xcb_render_transform_t))) {
+       _cairo_xcb_connection_render_set_picture_transform (_picture_to_connection (picture),
+                                                           picture->picture,
+                                                           &transform);
+
+       picture->transform = transform;
+    }
+}
+
+static void
+_cairo_xcb_picture_set_filter (cairo_xcb_picture_t *picture,
+                              cairo_filter_t filter)
+{
+    const char *render_filter;
+    int len;
+
+    if (picture->filter == filter)
+       return;
+
+    switch (filter) {
+    case CAIRO_FILTER_FAST:
+       render_filter = "fast";
+       len = strlen ("fast");
+       break;
+
+    case CAIRO_FILTER_GOOD:
+       render_filter = "good";
+       len = strlen ("good");
+       break;
+
+    case CAIRO_FILTER_BEST:
+       render_filter = "best";
+       len = strlen ("best");
+       break;
+
+    case CAIRO_FILTER_NEAREST:
+       render_filter = "nearest";
+       len = strlen ("nearest");
+       break;
+
+    case CAIRO_FILTER_BILINEAR:
+       render_filter = "bilinear";
+       len = strlen ("bilinear");
+       break;
+
+    default:
+       ASSERT_NOT_REACHED;
+    case CAIRO_FILTER_GAUSSIAN:
+       render_filter = "best";
+       len = strlen ("best");
+       break;
+    }
+
+    _cairo_xcb_connection_render_set_picture_filter (_picture_to_connection (picture),
+                                                    picture->picture,
+                                                    len, (char *) render_filter);
+    picture->filter = filter;
+}
+
+static void
+_cairo_xcb_picture_set_extend (cairo_xcb_picture_t *picture,
+                              cairo_extend_t extend)
+{
+    uint32_t pa[1];
+
+    if (picture->extend == extend)
+       return;
+
+    switch (extend) {
+    default:
+       ASSERT_NOT_REACHED;
+    case CAIRO_EXTEND_NONE:
+       pa[0] = XCB_RENDER_REPEAT_NONE;
+       break;
+
+    case CAIRO_EXTEND_REPEAT:
+       pa[0] = XCB_RENDER_REPEAT_NORMAL;
+       break;
+
+    case CAIRO_EXTEND_REFLECT:
+       pa[0] = XCB_RENDER_REPEAT_REFLECT;
+       break;
+
+    case CAIRO_EXTEND_PAD:
+       pa[0] = XCB_RENDER_REPEAT_PAD;
+       break;
+    }
+
+    _cairo_xcb_connection_render_change_picture (_picture_to_connection (picture),
+                                                picture->picture,
+                                                XCB_RENDER_CP_REPEAT, pa);
+    picture->extend = extend;
+}
+
+static void
+_cairo_xcb_picture_set_component_alpha (cairo_xcb_picture_t *picture,
+                                       cairo_bool_t ca)
+{
+    uint32_t pa[1];
+
+    if (picture->has_component_alpha == ca)
+       return;
+
+    pa[0] = ca;
+
+    _cairo_xcb_connection_render_change_picture (_picture_to_connection (picture),
+                                                picture->picture,
+                                                XCB_RENDER_CP_COMPONENT_ALPHA,
+                                                pa);
+    picture->has_component_alpha = ca;
+}
+
+static cairo_xcb_picture_t *
+_solid_picture (cairo_xcb_surface_t *target,
+               const cairo_color_t *color)
+{
+    xcb_render_color_t xcb_color;
+    xcb_render_pictformat_t xrender_format;
+    cairo_xcb_picture_t *picture;
+
+    xcb_color.red   = color->red_short;
+    xcb_color.green = color->green_short;
+    xcb_color.blue  = color->blue_short;
+    xcb_color.alpha = color->alpha_short;
+
+    xrender_format = target->screen->connection->standard_formats[CAIRO_FORMAT_ARGB32];
+    picture = _cairo_xcb_picture_create (target->screen,
+                                        PIXMAN_a8r8g8b8,
+                                        xrender_format,
+                                        -1, -1);
+    if (unlikely (picture->base.status))
+       return picture;
+
+    if (target->connection->flags & CAIRO_XCB_RENDER_HAS_GRADIENTS) {
+       _cairo_xcb_connection_render_create_solid_fill (target->connection,
+                                                       picture->picture,
+                                                       xcb_color);
+    } else {
+       xcb_pixmap_t pixmap;
+       uint32_t values[] = { XCB_RENDER_REPEAT_NORMAL };
+
+       pixmap = _cairo_xcb_connection_create_pixmap (target->connection,
+                                                     32, target->drawable, 1, 1);
+       _cairo_xcb_connection_render_create_picture (target->connection,
+                                                    picture->picture,
+                                                    pixmap,
+                                                    xrender_format,
+                                                    XCB_RENDER_CP_REPEAT,
+                                                    values);
+       if (target->connection->flags & CAIRO_XCB_RENDER_HAS_FILL_RECTANGLES) {
+           xcb_rectangle_t rect;
+
+           rect.x = rect.y = 0;
+           rect.width = rect.height = 1;
+
+           _cairo_xcb_connection_render_fill_rectangles (_picture_to_connection (picture),
+                                                         XCB_RENDER_PICT_OP_SRC,
+                                                         picture->picture,
+                                                         xcb_color, 1, &rect);
+       } else {
+           xcb_gcontext_t gc;
+           uint32_t pixel;
+
+           gc = _cairo_xcb_screen_get_gc (target->screen, pixmap, 32);
+
+           /* XXX byte ordering? */
+           pixel = ((color->alpha_short >> 8) << 24) |
+                   ((color->red_short   >> 8) << 16) |
+                   ((color->green_short >> 8) << 8) |
+                   ((color->blue_short  >> 8) << 0);
+
+           _cairo_xcb_connection_put_image (target->connection,
+                                            pixmap, gc,
+                                            1, 1, 0, 0,
+                                            32, 4, &pixel);
+
+           _cairo_xcb_screen_put_gc (target->screen, 32, gc);
+       }
+
+       _cairo_xcb_connection_free_pixmap (target->connection, pixmap);
+    }
+
+    return picture;
+}
+
+static cairo_xcb_picture_t *
+_cairo_xcb_transparent_picture (cairo_xcb_surface_t *target)
+{
+    cairo_xcb_picture_t *picture;
+
+    picture = (cairo_xcb_picture_t *) target->screen->stock_colors[CAIRO_STOCK_TRANSPARENT];
+    if (picture == NULL) {
+       picture = _solid_picture (target, CAIRO_COLOR_TRANSPARENT);
+       target->screen->stock_colors[CAIRO_STOCK_TRANSPARENT] = &picture->base;
+    }
+
+    return (cairo_xcb_picture_t *) cairo_surface_reference (&picture->base);
+}
+
+static cairo_xcb_picture_t *
+_cairo_xcb_black_picture (cairo_xcb_surface_t *target)
+{
+    cairo_xcb_picture_t *picture;
+
+    picture = (cairo_xcb_picture_t *) target->screen->stock_colors[CAIRO_STOCK_BLACK];
+    if (picture == NULL) {
+       picture = _solid_picture (target, CAIRO_COLOR_BLACK);
+       target->screen->stock_colors[CAIRO_STOCK_BLACK] = &picture->base;
+    }
+
+    return (cairo_xcb_picture_t *) cairo_surface_reference (&picture->base);
+}
+
+static cairo_xcb_picture_t *
+_cairo_xcb_white_picture (cairo_xcb_surface_t *target)
+{
+    cairo_xcb_picture_t *picture;
+
+    picture = (cairo_xcb_picture_t *) target->screen->stock_colors[CAIRO_STOCK_WHITE];
+    if (picture == NULL) {
+       picture = _solid_picture (target, CAIRO_COLOR_WHITE);
+       target->screen->stock_colors[CAIRO_STOCK_WHITE] = &picture->base;
+    }
+
+    return (cairo_xcb_picture_t *) cairo_surface_reference (&picture->base);
+}
+
+static cairo_xcb_picture_t *
+_cairo_xcb_solid_picture (cairo_xcb_surface_t *target,
+                         const cairo_solid_pattern_t *pattern)
+{
+    cairo_xcb_picture_t *picture;
+    cairo_xcb_screen_t *screen;
+    int i, n_cached;
+
+    if (pattern->color.alpha_short <= 0x00ff)
+       return _cairo_xcb_transparent_picture (target);
+
+    if (pattern->color.alpha_short >= 0xff00) {
+       if (pattern->color.red_short <= 0x00ff &&
+           pattern->color.green_short <= 0x00ff &&
+           pattern->color.blue_short <= 0x00ff)
+       {
+           return _cairo_xcb_black_picture (target);
+       }
+
+       if (pattern->color.red_short >= 0xff00 &&
+           pattern->color.green_short >= 0xff00 &&
+           pattern->color.blue_short >= 0xff00)
+       {
+           return _cairo_xcb_white_picture (target);
+       }
+    }
+
+    screen = target->screen;
+    n_cached = screen->solid_cache_size;
+    for (i = 0; i < n_cached; i++) {
+       if (_cairo_color_equal (&screen->solid_cache[i].color, &pattern->color)) {
+           return (cairo_xcb_picture_t *) cairo_surface_reference (screen->solid_cache[i].picture);
+       }
+    }
+
+    picture = _solid_picture (target, &pattern->color);
+    if (unlikely (picture->base.status))
+       return picture;
+
+    if (screen->solid_cache_size < ARRAY_LENGTH (screen->solid_cache)) {
+       i = screen->solid_cache_size++;
+    } else {
+       i = hars_petruska_f54_1_random () % ARRAY_LENGTH (screen->solid_cache);
+       cairo_surface_destroy (screen->solid_cache[i].picture);
+    }
+    screen->solid_cache[i].picture = cairo_surface_reference (&picture->base);
+    screen->solid_cache[i].color = pattern->color;
+
+    return picture;
+}
+
+static cairo_xcb_picture_t *
+_render_to_picture (cairo_xcb_surface_t *target,
+                   const cairo_pattern_t *pattern,
+                   const cairo_rectangle_int_t *extents)
+{
+    cairo_image_surface_t *image;
+    cairo_xcb_shm_info_t *shm_info;
+    cairo_pattern_union_t copy;
+    cairo_status_t status;
+    cairo_xcb_picture_t *picture;
+    pixman_format_code_t pixman_format;
+    xcb_render_pictformat_t xrender_format;
+
+    /* XXX handle extend modes via tiling? */
+    /* XXX alpha-only masks? */
+
+    pixman_format = PIXMAN_a8r8g8b8;
+    xrender_format = target->screen->connection->standard_formats[CAIRO_FORMAT_ARGB32];
+
+    status = _cairo_xcb_shm_image_create (target->screen->connection,
+                                         pixman_format,
+                                         extents->width, extents->height,
+                                         &image, &shm_info);
+    if (unlikely (status))
+       return (cairo_xcb_picture_t *) _cairo_surface_create_in_error (status);
+
+    _cairo_pattern_init_static_copy (&copy.base, pattern);
+    cairo_matrix_translate (&copy.base.matrix, extents->x, extents->y);
+    status = _cairo_surface_paint (&image->base,
+                                  CAIRO_OPERATOR_SOURCE,
+                                  &copy.base,
+                                  NULL);
+    if (unlikely (status)) {
+       cairo_surface_destroy (&image->base);
+       return (cairo_xcb_picture_t *) _cairo_surface_create_in_error (status);
+    }
+
+    picture = _picture_from_image (target, xrender_format, image, shm_info);
+    cairo_surface_destroy (&image->base);
+
+    if (unlikely (picture->base.status))
+       return picture;
+
+    _cairo_xcb_picture_set_component_alpha (picture, pattern->has_component_alpha);
+    picture->x = -extents->x;
+    picture->y = -extents->y;
+
+    return picture;
+}
+
+static xcb_render_fixed_t *
+_gradient_to_xcb (const cairo_gradient_pattern_t *gradient,
+                 unsigned int *n_stops,
+                 char *buf, unsigned int buflen)
+{
+    xcb_render_fixed_t *stops;
+    xcb_render_color_t *colors;
+    unsigned int i;
+
+    assert (gradient->n_stops > 0);
+    *n_stops = MAX (gradient->n_stops, 2);
+
+    if (*n_stops * (sizeof (xcb_render_fixed_t) + sizeof (xcb_render_color_t)) < buflen)
+    {
+       stops = (xcb_render_fixed_t *) buf;
+    }
+    else
+    {
+       stops =
+           _cairo_malloc_ab (*n_stops,
+                             sizeof (xcb_render_fixed_t) + sizeof (xcb_render_color_t));
+       if (unlikely (stops == NULL))
+           return NULL;
+    }
+
+    colors = (xcb_render_color_t *) (stops + *n_stops);
+    for (i = 0; i < gradient->n_stops; i++) {
+       stops[i] =
+           _cairo_fixed_16_16_from_double (gradient->stops[i].offset);
+
+       colors[i].red   = gradient->stops[i].color.red_short;
+       colors[i].green = gradient->stops[i].color.green_short;
+       colors[i].blue  = gradient->stops[i].color.blue_short;
+       colors[i].alpha = gradient->stops[i].color.alpha_short;
+    }
+
+    /* RENDER does not support gradients with less than 2 stops. If a
+     * gradient has only a single stop, duplicate it to make RENDER
+     * happy. */
+    if (gradient->n_stops == 1) {
+       stops[1] = _cairo_fixed_16_16_from_double (gradient->stops[0].offset);
+
+       colors[1].red   = gradient->stops[0].color.red_short;
+       colors[1].green = gradient->stops[0].color.green_short;
+       colors[1].blue  = gradient->stops[0].color.blue_short;
+       colors[1].alpha = gradient->stops[0].color.alpha_short;
+    }
+
+    return stops;
+}
+
+static cairo_xcb_picture_t *
+_cairo_xcb_linear_picture (cairo_xcb_surface_t *target,
+                          const cairo_linear_pattern_t *pattern,
+                          const cairo_rectangle_int_t *extents)
+{
+    char buf[CAIRO_STACK_BUFFER_SIZE];
+    xcb_render_fixed_t *stops;
+    xcb_render_color_t *colors;
+    xcb_render_pointfix_t p1, p2;
+    cairo_matrix_t matrix;
+    cairo_circle_double_t extremes[2];
+    cairo_xcb_picture_t *picture;
+    cairo_status_t status;
+    unsigned int n_stops;
+
+    _cairo_gradient_pattern_fit_to_range (&pattern->base, PIXMAN_MAX_INT >> 1, &matrix, extremes);
+
+    picture = (cairo_xcb_picture_t *)
+       _cairo_xcb_screen_lookup_linear_picture (target->screen, pattern);
+    if (picture != NULL)
+       goto setup_picture;
+
+    stops = _gradient_to_xcb (&pattern->base, &n_stops, buf, sizeof (buf));
+    if (unlikely (stops == NULL))
+       return (cairo_xcb_picture_t *) _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY);
+
+    picture = _cairo_xcb_picture_create (target->screen,
+                                        target->screen->connection->standard_formats[CAIRO_FORMAT_ARGB32],
+                                        PIXMAN_a8r8g8b8,
+                                        -1, -1);
+    if (unlikely (picture->base.status)) {
+       if (stops != (xcb_render_fixed_t *) buf)
+           free (stops);
+       return picture;
+    }
+    picture->filter = CAIRO_FILTER_DEFAULT;
+
+    colors = (xcb_render_color_t *) (stops + n_stops);
+
+    p1.x = _cairo_fixed_16_16_from_double (extremes[0].center.x);
+    p1.y = _cairo_fixed_16_16_from_double (extremes[0].center.y);
+    p2.x = _cairo_fixed_16_16_from_double (extremes[1].center.x);
+    p2.y = _cairo_fixed_16_16_from_double (extremes[1].center.y);
+
+    _cairo_xcb_connection_render_create_linear_gradient (target->connection,
+                                                        picture->picture,
+                                                        p1, p2,
+                                                        n_stops,
+                                                        stops, colors);
+
+    if (stops != (xcb_render_fixed_t *) buf)
+       free (stops);
+
+    status = _cairo_xcb_screen_store_linear_picture (target->screen,
+                                                    pattern,
+                                                    &picture->base);
+    if (unlikely (status)) {
+       cairo_surface_destroy (&picture->base);
+       return (cairo_xcb_picture_t *) _cairo_surface_create_in_error (status);
+    }
+
+setup_picture:
+    _cairo_xcb_picture_set_matrix (picture, &matrix,
+                                  pattern->base.base.filter,
+                                  extents->x + extents->width/2.,
+                                  extents->y + extents->height/2.);
+    _cairo_xcb_picture_set_filter (picture, pattern->base.base.filter);
+    _cairo_xcb_picture_set_extend (picture, pattern->base.base.extend);
+    _cairo_xcb_picture_set_component_alpha (picture,
+                                           pattern->base.base.has_component_alpha);
+
+    return picture;
+}
+
+static cairo_xcb_picture_t *
+_cairo_xcb_radial_picture (cairo_xcb_surface_t *target,
+                          const cairo_radial_pattern_t *pattern,
+                          const cairo_rectangle_int_t *extents)
+{
+    char buf[CAIRO_STACK_BUFFER_SIZE];
+    xcb_render_fixed_t *stops;
+    xcb_render_color_t *colors;
+    xcb_render_pointfix_t p1, p2;
+    xcb_render_fixed_t r1, r2;
+    cairo_matrix_t matrix;
+    cairo_circle_double_t extremes[2];
+    cairo_xcb_picture_t *picture;
+    cairo_status_t status;
+    unsigned int n_stops;
+
+    _cairo_gradient_pattern_fit_to_range (&pattern->base, PIXMAN_MAX_INT >> 1, &matrix, extremes);
+
+    picture = (cairo_xcb_picture_t *)
+       _cairo_xcb_screen_lookup_radial_picture (target->screen, pattern);
+    if (picture != NULL)
+       goto setup_picture;
+
+    stops = _gradient_to_xcb (&pattern->base, &n_stops, buf, sizeof (buf));
+    if (unlikely (stops == NULL))
+       return (cairo_xcb_picture_t *) _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY);
+
+    picture = _cairo_xcb_picture_create (target->screen,
+                                        target->screen->connection->standard_formats[CAIRO_FORMAT_ARGB32],
+                                        PIXMAN_a8r8g8b8,
+                                        -1, -1);
+    if (unlikely (picture->base.status)) {
+       if (stops != (xcb_render_fixed_t *) buf)
+           free (stops);
+       return picture;
+    }
+    picture->filter = CAIRO_FILTER_DEFAULT;
+
+    colors = (xcb_render_color_t *) (stops + n_stops);
+
+    p1.x = _cairo_fixed_16_16_from_double (extremes[0].center.x);
+    p1.y = _cairo_fixed_16_16_from_double (extremes[0].center.y);
+    p2.x = _cairo_fixed_16_16_from_double (extremes[1].center.x);
+    p2.y = _cairo_fixed_16_16_from_double (extremes[1].center.y);
+
+    r1 = _cairo_fixed_16_16_from_double (extremes[0].radius);
+    r2 = _cairo_fixed_16_16_from_double (extremes[1].radius);
+
+    _cairo_xcb_connection_render_create_radial_gradient (target->connection,
+                                                        picture->picture,
+                                                        p1, p2, r1, r2,
+                                                        n_stops,
+                                                        stops, colors);
+
+    if (stops != (xcb_render_fixed_t *) buf)
+       free (stops);
+
+    status = _cairo_xcb_screen_store_radial_picture (target->screen,
+                                                    pattern,
+                                                    &picture->base);
+    if (unlikely (status)) {
+       cairo_surface_destroy (&picture->base);
+       return (cairo_xcb_picture_t *) _cairo_surface_create_in_error (status);
+    }
+
+setup_picture:
+    _cairo_xcb_picture_set_matrix (picture, &matrix,
+                                  pattern->base.base.filter,
+                                  extents->x + extents->width/2.,
+                                  extents->y + extents->height/2.);
+    _cairo_xcb_picture_set_filter (picture, pattern->base.base.filter);
+    _cairo_xcb_picture_set_extend (picture, pattern->base.base.extend);
+    _cairo_xcb_picture_set_component_alpha (picture,
+                                           pattern->base.base.has_component_alpha);
+
+    return picture;
+}
+
+static cairo_xcb_picture_t *
+_copy_to_picture (cairo_xcb_surface_t *source)
+{
+    cairo_xcb_picture_t *picture;
+    uint32_t values[] = { 0, 1 };
+
+    if (source->deferred_clear) {
+       cairo_status_t status = _cairo_xcb_surface_clear (source);
+       if (unlikely (status))
+           return (cairo_xcb_picture_t *) _cairo_surface_create_in_error (status);
+    }
+
+    picture = _cairo_xcb_picture_create (source->screen,
+                                        source->xrender_format,
+                                        source->pixman_format,
+                                        source->width,
+                                        source->height);
+    if (unlikely (picture->base.status))
+       return picture;
+
+    _cairo_xcb_connection_render_create_picture (source->connection,
+                                                picture->picture,
+                                                source->drawable,
+                                                source->xrender_format,
+                                                XCB_RENDER_CP_GRAPHICS_EXPOSURE |
+                                                XCB_RENDER_CP_SUBWINDOW_MODE,
+                                                values);
+
+    return picture;
+}
+
+static cairo_xcb_picture_t *
+_cairo_xcb_surface_picture (cairo_xcb_surface_t *target,
+                           const cairo_surface_pattern_t *pattern,
+                           const cairo_rectangle_int_t *extents)
+{
+    cairo_surface_t *source = pattern->surface;
+    cairo_xcb_picture_t *picture;
+    cairo_filter_t filter;
+
+    picture = (cairo_xcb_picture_t *)
+       _cairo_surface_has_snapshot (source, &_cairo_xcb_picture_backend);
+    if (picture != NULL) {
+       if (picture->screen == target->screen) {
+           picture = (cairo_xcb_picture_t *) cairo_surface_reference (&picture->base);
+           goto setup_picture;
+       }
+       picture = NULL;
+    }
+
+    if (source->type == CAIRO_SURFACE_TYPE_XCB && ((cairo_xcb_surface_t *) source)->fallback == NULL)
+    {
+       if (source->backend->type == CAIRO_SURFACE_TYPE_XCB) {
+           if (((cairo_xcb_surface_t *) source)->screen == target->screen) {
+               picture = _copy_to_picture ((cairo_xcb_surface_t *) source);
+               if (unlikely (picture->base.status))
+                   return picture;
+           }
+       } else if (source->backend->type == CAIRO_SURFACE_TYPE_SUBSURFACE) {
+           cairo_surface_subsurface_t *sub = (cairo_surface_subsurface_t *) source;
+           cairo_xcb_surface_t *xcb = (cairo_xcb_surface_t *) sub->target;
+
+           /* XXX repeat interval with source clipping? */
+           if (FALSE && xcb->screen == target->screen) {
+               xcb_rectangle_t rect;
+
+               picture = _copy_to_picture (xcb);
+               if (unlikely (picture->base.status))
+                   return picture;
+
+               rect.x = sub->extents.x;
+               rect.y = sub->extents.y;
+               rect.width  = sub->extents.width;
+               rect.height = sub->extents.height;
+
+               _cairo_xcb_connection_render_set_picture_clip_rectangles (xcb->connection,
+                                                                         picture->picture,
+                                                                         0, 0,
+                                                                         1, &rect);
+               picture->x0 = rect.x;
+               picture->y0 = rect.y;
+               picture->width  = rect.width;
+               picture->height = rect.height;
+           }
+       } else if (_cairo_surface_is_snapshot (source)) {
+           cairo_surface_snapshot_t *snap = (cairo_surface_snapshot_t *) source;
+           cairo_xcb_surface_t *xcb = (cairo_xcb_surface_t *) snap->target;
+
+           if (xcb->screen == target->screen) {
+               picture = _copy_to_picture (xcb);
+               if (unlikely (picture->base.status))
+                   return picture;
+           }
+       }
+    }
+#if CAIRO_HAS_XLIB_XCB_FUNCTIONS
+    else if (source->type == CAIRO_SURFACE_TYPE_XLIB && ((cairo_xlib_xcb_surface_t *) source)->xcb->fallback == NULL)
+    {
+       if (source->backend->type == CAIRO_SURFACE_TYPE_XLIB) {
+           if (((cairo_xlib_xcb_surface_t *) source)->xcb->screen == target->screen) {
+               picture = _copy_to_picture (((cairo_xlib_xcb_surface_t *) source)->xcb);
+               if (unlikely (picture->base.status))
+                   return picture;
+           }
+       } else if (source->backend->type == CAIRO_SURFACE_TYPE_SUBSURFACE) {
+           cairo_surface_subsurface_t *sub = (cairo_surface_subsurface_t *) source;
+           cairo_xcb_surface_t *xcb = ((cairo_xlib_xcb_surface_t *) sub->target)->xcb;
+
+           if (FALSE && xcb->screen == target->screen) {
+               xcb_rectangle_t rect;
+
+               picture = _copy_to_picture (xcb);
+               if (unlikely (picture->base.status))
+                   return picture;
+
+               rect.x = sub->extents.x;
+               rect.y = sub->extents.y;
+               rect.width  = sub->extents.width;
+               rect.height = sub->extents.height;
+
+               _cairo_xcb_connection_render_set_picture_clip_rectangles (xcb->connection,
+                                                                         picture->picture,
+                                                                         0, 0,
+                                                                         1, &rect);
+               picture->x0 = rect.x;
+               picture->y0 = rect.y;
+               picture->width  = rect.width;
+               picture->height = rect.height;
+           }
+       } else if (source->backend->type == CAIRO_INTERNAL_SURFACE_TYPE_SNAPSHOT) {
+           cairo_surface_snapshot_t *snap = (cairo_surface_snapshot_t *) source;
+           cairo_xcb_surface_t *xcb = ((cairo_xlib_xcb_surface_t *) snap->target)->xcb;
+
+           if (xcb->screen == target->screen) {
+               picture = _copy_to_picture (xcb);
+               if (unlikely (picture->base.status))
+                   return picture;
+           }
+       }
+    }
+#endif
+#if CAIRO_HAS_GL_FUNCTIONS
+    else if (source->type == CAIRO_SURFACE_TYPE_GL)
+    {
+       /* pixmap from texture */
+    }
+#endif
+
+    if (picture == NULL) {
+       cairo_image_surface_t *image;
+       void *image_extra;
+       cairo_status_t status;
+
+       status = _cairo_surface_acquire_source_image (source, &image, &image_extra);
+       if (unlikely (status))
+           return (cairo_xcb_picture_t *) _cairo_surface_create_in_error (status);
+
+       if (image->format != CAIRO_FORMAT_INVALID) {
+           xcb_render_pictformat_t format;
+
+           format = target->screen->connection->standard_formats[image->format];
+
+           picture = _picture_from_image (target, format, image, NULL);
+           _cairo_surface_release_source_image (source, image, image_extra);
+       } else {
+           cairo_image_surface_t *conv;
+           xcb_render_pictformat_t render_format;
+
+           /* XXX XRenderPutImage! */
+
+           conv = _cairo_image_surface_coerce (image);
+           _cairo_surface_release_source_image (source, image, image_extra);
+           if (unlikely (conv->base.status))
+               return (cairo_xcb_picture_t *) conv;
+
+           render_format = target->screen->connection->standard_formats[conv->format];
+           picture = _picture_from_image (target, render_format, conv, NULL);
+           cairo_surface_destroy (&conv->base);
+       }
+
+       if (unlikely (picture->base.status))
+           return picture;
+    }
+
+    _cairo_surface_attach_snapshot (source,
+                                   &picture->base,
+                                   NULL);
+
+setup_picture:
+    filter = pattern->base.filter;
+    if (filter != CAIRO_FILTER_NEAREST &&
+       _cairo_matrix_has_unity_scale (&pattern->base.matrix) &&
+       _cairo_fixed_is_integer (_cairo_fixed_from_double (pattern->base.matrix.x0)) &&
+       _cairo_fixed_is_integer (_cairo_fixed_from_double (pattern->base.matrix.y0)))
+    {
+       filter = CAIRO_FILTER_NEAREST;
+    }
+    _cairo_xcb_picture_set_filter (picture, filter);
+
+    _cairo_xcb_picture_set_matrix (picture,
+                                  &pattern->base.matrix, filter,
+                                  extents->x + extents->width/2.,
+                                  extents->y + extents->height/2.);
+
+
+    _cairo_xcb_picture_set_extend (picture, pattern->base.extend);
+    _cairo_xcb_picture_set_component_alpha (picture, pattern->base.has_component_alpha);
+
+    return picture;
+}
+
+static cairo_xcb_picture_t *
+_cairo_xcb_picture_for_pattern (cairo_xcb_surface_t *target,
+                               const cairo_pattern_t *pattern,
+                               const cairo_rectangle_int_t *extents)
+{
+    if (pattern == NULL)
+       return _cairo_xcb_white_picture (target);
+
+    if (! _pattern_is_supported (target->connection->flags, pattern))
+       return _render_to_picture (target, pattern, extents);
+
+    switch (pattern->type) {
+    case CAIRO_PATTERN_TYPE_SOLID:
+       return _cairo_xcb_solid_picture (target, (cairo_solid_pattern_t *) pattern);
+
+    case CAIRO_PATTERN_TYPE_LINEAR:
+       return _cairo_xcb_linear_picture (target,
+                                         (cairo_linear_pattern_t *) pattern,
+                                         extents);
+
+    case CAIRO_PATTERN_TYPE_RADIAL:
+       return _cairo_xcb_radial_picture (target,
+                                         (cairo_radial_pattern_t *) pattern,
+                                         extents);
+
+    case CAIRO_PATTERN_TYPE_SURFACE:
+       return _cairo_xcb_surface_picture (target,
+                                          (cairo_surface_pattern_t *) pattern,
+                                          extents);
+    case CAIRO_PATTERN_TYPE_MESH:
+    default:
+       ASSERT_NOT_REACHED;
+       return _render_to_picture (target, pattern, extents);
+    }
+}
+
+COMPILE_TIME_ASSERT (sizeof (xcb_rectangle_t) <= sizeof (cairo_box_t));
+
+static cairo_status_t
+_render_fill_boxes (void                       *abstract_dst,
+                   cairo_operator_t             op,
+                   const cairo_color_t         *color,
+                   cairo_boxes_t               *boxes)
+{
+    cairo_xcb_surface_t *dst = abstract_dst;
+    xcb_rectangle_t stack_xrects[CAIRO_STACK_ARRAY_LENGTH (sizeof (xcb_rectangle_t))];
+    xcb_rectangle_t *xrects = stack_xrects;
+    xcb_render_color_t render_color;
+    int render_op = _render_operator (op);
+    struct _cairo_boxes_chunk *chunk;
+    int max_count;
+
+    render_color.red   = color->red_short;
+    render_color.green = color->green_short;
+    render_color.blue  = color->blue_short;
+    render_color.alpha = color->alpha_short;
+
+    max_count = 0;
+    for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
+       if (chunk->count > max_count)
+           max_count = chunk->count;
+    }
+    if (max_count > ARRAY_LENGTH (stack_xrects)) {
+       xrects = _cairo_malloc_ab (max_count, sizeof (xcb_rectangle_t));
+       if (unlikely (xrects == NULL))
+           return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+    }
+
+    for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
+       int i, j;
+
+       for (i = j = 0; i < chunk->count; i++) {
+           int x1 = _cairo_fixed_integer_round_down (chunk->base[i].p1.x);
+           int y1 = _cairo_fixed_integer_round_down (chunk->base[i].p1.y);
+           int x2 = _cairo_fixed_integer_round_down (chunk->base[i].p2.x);
+           int y2 = _cairo_fixed_integer_round_down (chunk->base[i].p2.y);
+
+           if (x2 > x1 && y2 > y1) {
+               xrects[j].x = x1;
+               xrects[j].y = y1;
+               xrects[j].width  = x2 - x1;
+               xrects[j].height = y2 - y1;
+               j++;
+           }
+       }
+
+       if (j) {
+           _cairo_xcb_connection_render_fill_rectangles
+               (dst->connection,
+                render_op, dst->picture,
+                render_color, j, xrects);
+       }
+    }
+
+    if (xrects != stack_xrects)
+       free (xrects);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+/* pixel aligned, non-overlapping boxes */
+static cairo_int_status_t
+_render_composite_boxes (cairo_xcb_surface_t   *dst,
+                        cairo_operator_t        op,
+                        const cairo_pattern_t  *src_pattern,
+                        const cairo_pattern_t  *mask_pattern,
+                        const cairo_rectangle_int_t *extents,
+                        const cairo_boxes_t *boxes)
+{
+    cairo_xcb_picture_t *src, *mask;
+    const struct _cairo_boxes_chunk *chunk;
+    xcb_rectangle_t stack_boxes[CAIRO_STACK_ARRAY_LENGTH (xcb_rectangle_t)];
+    xcb_rectangle_t *clip_boxes;
+    cairo_rectangle_int_t stack_extents;
+    cairo_status_t status;
+    int num_boxes;
+    int render_op;
+
+    render_op = _render_operator (op);
+
+    if (src_pattern == NULL) {
+       src_pattern = mask_pattern;
+       mask_pattern = NULL;
+    }
+
+    /* amalgamate into a single Composite call by setting a clip region */
+    clip_boxes = stack_boxes;
+    if (boxes->num_boxes > ARRAY_LENGTH (stack_boxes)) {
+       clip_boxes = _cairo_malloc_ab (boxes->num_boxes, sizeof (xcb_rectangle_t));
+       if (unlikely (clip_boxes == NULL))
+           return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+    }
+
+    src = _cairo_xcb_picture_for_pattern (dst, src_pattern, extents);
+    status = src->base.status;
+    if (unlikely (status))
+       goto cleanup_boxes;
+
+    num_boxes = 0;
+    for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
+       const cairo_box_t *box = chunk->base;
+       int i;
+
+       for (i = 0; i < chunk->count; i++) {
+           int x = _cairo_fixed_integer_round_down (box[i].p1.x);
+           int y = _cairo_fixed_integer_round_down (box[i].p1.y);
+           int width  = _cairo_fixed_integer_round_down (box[i].p2.x) - x;
+           int height = _cairo_fixed_integer_round_down (box[i].p2.y) - y;
+
+           if (width && height) {
+               clip_boxes[num_boxes].x = x;
+               clip_boxes[num_boxes].y = y;
+               clip_boxes[num_boxes].width = width;
+               clip_boxes[num_boxes].height = height;
+               num_boxes++;
+           }
+       }
+    }
+
+    if (num_boxes) {
+       if (num_boxes > 1) {
+           _cairo_xcb_connection_render_set_picture_clip_rectangles (dst->connection,
+                                                                     dst->picture,
+                                                                     0, 0,
+                                                                     num_boxes,
+                                                                     clip_boxes);
+       } else {
+           stack_extents.x = clip_boxes[0].x;
+           stack_extents.y = clip_boxes[0].y;
+           stack_extents.width  = clip_boxes[0].width;
+           stack_extents.height = clip_boxes[0].height;
+           extents = &stack_extents;
+       }
+
+       if (mask_pattern != NULL) {
+           mask = _cairo_xcb_picture_for_pattern (dst, mask_pattern, extents);
+           status = mask->base.status;
+           if (unlikely (status))
+               goto cleanup_clip;
+
+           _cairo_xcb_connection_render_composite (dst->connection,
+                                                   render_op,
+                                                   src->picture,
+                                                   mask->picture,
+                                                   dst->picture,
+                                                   src->x + extents->x, src->y + extents->y,
+                                                   mask->x + extents->x, mask->y + extents->y,
+                                                   extents->x, extents->y,
+                                                   extents->width, extents->height);
+
+           cairo_surface_destroy (&mask->base);
+       } else {
+           _cairo_xcb_connection_render_composite (dst->connection,
+                                                   render_op,
+                                                   src->picture,
+                                                   XCB_NONE,
+                                                   dst->picture,
+                                                   src->x + extents->x, src->y + extents->y,
+                                                   0, 0,
+                                                   extents->x, extents->y,
+                                                   extents->width, extents->height);
+       }
+
+cleanup_clip:
+
+       if (num_boxes > 1)
+           _cairo_xcb_surface_clear_clip_region (dst);
+    }
+
+    cairo_surface_destroy (&src->base);
+
+cleanup_boxes:
+
+    if (clip_boxes != stack_boxes)
+       free (clip_boxes);
+
+    return status;
+}
+
+
+#define CAIRO_FIXED_16_16_MIN _cairo_fixed_from_int (-32768)
+#define CAIRO_FIXED_16_16_MAX _cairo_fixed_from_int (32767)
+
+static cairo_bool_t
+_line_exceeds_16_16 (const cairo_line_t *line)
+{
+    return
+       line->p1.x <= CAIRO_FIXED_16_16_MIN ||
+       line->p1.x >= CAIRO_FIXED_16_16_MAX ||
+
+       line->p2.x <= CAIRO_FIXED_16_16_MIN ||
+       line->p2.x >= CAIRO_FIXED_16_16_MAX ||
+
+       line->p1.y <= CAIRO_FIXED_16_16_MIN ||
+       line->p1.y >= CAIRO_FIXED_16_16_MAX ||
+
+       line->p2.y <= CAIRO_FIXED_16_16_MIN ||
+       line->p2.y >= CAIRO_FIXED_16_16_MAX;
+}
+
+static void
+_project_line_x_onto_16_16 (const cairo_line_t *line,
+                           cairo_fixed_t top,
+                           cairo_fixed_t bottom,
+                           xcb_render_linefix_t *out)
+{
+    cairo_point_double_t p1, p2;
+    double m;
+
+    p1.x = _cairo_fixed_to_double (line->p1.x);
+    p1.y = _cairo_fixed_to_double (line->p1.y);
+
+    p2.x = _cairo_fixed_to_double (line->p2.x);
+    p2.y = _cairo_fixed_to_double (line->p2.y);
+
+    m = (p2.x - p1.x) / (p2.y - p1.y);
+    out->p1.x = _cairo_fixed_16_16_from_double (p1.x + m * _cairo_fixed_to_double (top - line->p1.y));
+    out->p2.x = _cairo_fixed_16_16_from_double (p1.x + m * _cairo_fixed_to_double (bottom - line->p1.y));
+}
+
+typedef struct {
+    cairo_traps_t traps;
+    cairo_antialias_t antialias;
+} composite_traps_info_t;
+
+COMPILE_TIME_ASSERT (sizeof (xcb_render_trapezoid_t) <= sizeof (cairo_trapezoid_t));
+
+static cairo_int_status_t
+_composite_traps (void *closure,
+                 cairo_xcb_surface_t   *dst,
+                 cairo_operator_t       op,
+                 const cairo_pattern_t *pattern,
+                 int dst_x, int dst_y,
+                 const cairo_rectangle_int_t *extents,
+                 cairo_clip_t          *clip)
+{
+    composite_traps_info_t *info = closure;
+    const cairo_traps_t *traps = &info->traps;
+    cairo_xcb_picture_t *src;
+    cairo_format_t format;
+    xcb_render_pictformat_t xrender_format;
+    xcb_render_trapezoid_t *xtraps;
+    int render_reference_x, render_reference_y;
+    cairo_status_t status;
+    int i;
+
+    if (dst->deferred_clear) {
+       status = _cairo_xcb_surface_clear (dst);
+       if (unlikely (status))
+               return status;
+    }
+
+    src = _cairo_xcb_picture_for_pattern (dst, pattern, extents);
+    if (unlikely (src->base.status))
+       return src->base.status;
+
+    if (info->antialias == CAIRO_ANTIALIAS_NONE)
+       format = CAIRO_FORMAT_A1;
+    else
+       format = CAIRO_FORMAT_A8;
+    xrender_format = dst->screen->connection->standard_formats[format];
+
+    xtraps = (xcb_render_trapezoid_t *) traps->traps;
+    for (i = 0; i < traps->num_traps; i++) {
+       cairo_trapezoid_t t = traps->traps[i];
+
+       /* top/bottom will be clamped to surface bounds */
+       xtraps[i].top = _cairo_fixed_to_16_16 (t.top);
+       xtraps[i].top -= dst_y << 16;
+       xtraps[i].bottom = _cairo_fixed_to_16_16 (t.bottom);
+       xtraps[i].bottom -= dst_y << 16;
+
+       /* However, all the other coordinates will have been left untouched so
+        * as not to introduce numerical error. Recompute them if they
+        * exceed the 16.16 limits.
+        */
+       if (unlikely (_line_exceeds_16_16 (&t.left))) {
+           _project_line_x_onto_16_16 (&t.left,
+                                       t.top,
+                                       t.bottom,
+                                       &xtraps[i].left);
+           xtraps[i].left.p1.y = xtraps[i].top;
+           xtraps[i].left.p2.y = xtraps[i].bottom;
+       } else {
+           xtraps[i].left.p1.x = _cairo_fixed_to_16_16 (t.left.p1.x);
+           xtraps[i].left.p1.y = _cairo_fixed_to_16_16 (t.left.p1.y);
+           xtraps[i].left.p2.x = _cairo_fixed_to_16_16 (t.left.p2.x);
+           xtraps[i].left.p2.y = _cairo_fixed_to_16_16 (t.left.p2.y);
+       }
+       xtraps[i].left.p1.x -= dst_x << 16;
+       xtraps[i].left.p1.y -= dst_y << 16;
+       xtraps[i].left.p2.x -= dst_x << 16;
+       xtraps[i].left.p2.y -= dst_y << 16;
+
+       if (unlikely (_line_exceeds_16_16 (&t.right))) {
+           _project_line_x_onto_16_16 (&t.right,
+                                       t.top,
+                                       t.bottom,
+                                       &xtraps[i].right);
+           xtraps[i].right.p1.y = xtraps[i].top;
+           xtraps[i].right.p2.y = xtraps[i].bottom;
+       } else {
+           xtraps[i].right.p1.x = _cairo_fixed_to_16_16 (t.right.p1.x);
+           xtraps[i].right.p1.y = _cairo_fixed_to_16_16 (t.right.p1.y);
+           xtraps[i].right.p2.x = _cairo_fixed_to_16_16 (t.right.p2.x);
+           xtraps[i].right.p2.y = _cairo_fixed_to_16_16 (t.right.p2.y);
+       }
+       xtraps[i].right.p1.x -= dst_x << 16;
+       xtraps[i].right.p1.y -= dst_y << 16;
+       xtraps[i].right.p2.x -= dst_x << 16;
+       xtraps[i].right.p2.y -= dst_y << 16;
+    }
+
+    if (xtraps[0].left.p1.y < xtraps[0].left.p2.y) {
+       render_reference_x = xtraps[0].left.p1.x >> 16;
+       render_reference_y = xtraps[0].left.p1.y >> 16;
+    } else {
+       render_reference_x = xtraps[0].left.p2.x >> 16;
+       render_reference_y = xtraps[0].left.p2.y >> 16;
+    }
+    render_reference_x += src->x + dst_x;
+    render_reference_y += src->y + dst_y;
+
+    _cairo_xcb_surface_set_precision (dst, info->antialias);
+    _cairo_xcb_connection_render_trapezoids (dst->connection,
+                                            _render_operator (op),
+                                            src->picture,
+                                            dst->picture,
+                                            xrender_format,
+                                            render_reference_x,
+                                            render_reference_y,
+                                            traps->num_traps, xtraps);
+
+    cairo_surface_destroy (&src->base);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+/* low-level composite driver */
+
+static cairo_xcb_surface_t *
+get_clip_surface (const cairo_clip_t *clip,
+                 cairo_xcb_surface_t *target,
+                 int *tx, int *ty)
+{
+    cairo_surface_t *surface;
+    cairo_status_t status;
+
+    surface = _cairo_surface_create_similar_solid (&target->base,
+                                                  CAIRO_CONTENT_ALPHA,
+                                                  clip->extents.width,
+                                                  clip->extents.height,
+                                                  CAIRO_COLOR_WHITE);
+    if (unlikely (surface->status))
+       return (cairo_xcb_surface_t *) surface;
+
+    assert (surface->backend == &_cairo_xcb_surface_backend);
+    status = _cairo_clip_combine_with_surface (clip, surface,
+                                              clip->extents.x, clip->extents.y);
+    if (unlikely (status)) {
+       cairo_surface_destroy (surface);
+       surface = _cairo_surface_create_in_error (status);
+    }
+
+    *tx = clip->extents.x;
+    *ty = clip->extents.y;
+
+    return (cairo_xcb_surface_t *) surface;
+}
+
+typedef cairo_int_status_t
+(*xcb_draw_func_t) (void                               *closure,
+                   cairo_xcb_surface_t                 *dst,
+                   cairo_operator_t                     op,
+                   const cairo_pattern_t               *src,
+                   int                                  dst_x,
+                   int                                  dst_y,
+                   const cairo_rectangle_int_t         *extents,
+                   cairo_clip_t                        *clip);
+
+static void do_unaligned_row(void (*blt)(void *closure,
+                                        int16_t x, int16_t y,
+                                        int16_t w, int16_t h,
+                                        uint16_t coverage),
+                            void *closure,
+                            const cairo_box_t *b,
+                            int tx, int y, int h,
+                            uint16_t coverage)
+{
+    int x1 = _cairo_fixed_integer_part (b->p1.x) - tx;
+    int x2 = _cairo_fixed_integer_part (b->p2.x) - tx;
+    if (x2 > x1) {
+       if (! _cairo_fixed_is_integer (b->p1.x)) {
+           blt(closure, x1, y, 1, h,
+               coverage * (256 - _cairo_fixed_fractional_part (b->p1.x)));
+           x1++;
+       }
+
+       if (x2 > x1)
+           blt(closure, x1, y, x2-x1, h, (coverage << 8) - (coverage >> 8));
+
+       if (! _cairo_fixed_is_integer (b->p2.x))
+           blt(closure, x2, y, 1, h,
+               coverage * _cairo_fixed_fractional_part (b->p2.x));
+    } else
+       blt(closure, x1, y, 1, h,
+           coverage * (b->p2.x - b->p1.x));
+}
+
+static void do_unaligned_box(void (*blt)(void *closure,
+                                        int16_t x, int16_t y,
+                                        int16_t w, int16_t h,
+                                        uint16_t coverage),
+                            void *closure,
+                            const cairo_box_t *b, int tx, int ty)
+{
+    int y1 = _cairo_fixed_integer_part (b->p1.y) - ty;
+    int y2 = _cairo_fixed_integer_part (b->p2.y) - ty;
+    if (y2 > y1) {
+       if (! _cairo_fixed_is_integer (b->p1.y)) {
+           do_unaligned_row(blt, closure, b, tx, y1, 1,
+                            256 - _cairo_fixed_fractional_part (b->p1.y));
+           y1++;
+       }
+
+       if (y2 > y1)
+           do_unaligned_row(blt, closure, b, tx, y1, y2-y1, 256);
+
+       if (! _cairo_fixed_is_integer (b->p2.y))
+           do_unaligned_row(blt, closure, b, tx, y2, 1,
+                            _cairo_fixed_fractional_part (b->p2.y));
+    } else
+       do_unaligned_row(blt, closure, b, tx, y1, 1,
+                        b->p2.y - b->p1.y);
+}
+
+
+static void blt_in(void *closure,
+                  int16_t x, int16_t y,
+                  int16_t w, int16_t h,
+                  uint16_t coverage)
+{
+    cairo_xcb_surface_t *mask = closure;
+    xcb_render_color_t color;
+    xcb_rectangle_t rect;
+
+    if (coverage == 0xffff)
+       return;
+
+    color.red = color.green = color.blue = 0;
+    color.alpha = coverage;
+
+    rect.x = x;
+    rect.y = y;
+    rect.width  = w;
+    rect.height = h;
+
+    _cairo_xcb_connection_render_fill_rectangles (mask->connection,
+                                                 XCB_RENDER_PICT_OP_IN,
+                                                 mask->picture,
+                                                 color, 1, &rect);
+}
+
+static cairo_xcb_surface_t *
+_create_composite_mask (cairo_clip_t           *clip,
+                       xcb_draw_func_t          draw_func,
+                       xcb_draw_func_t          mask_func,
+                       void                    *draw_closure,
+                       cairo_xcb_surface_t     *dst,
+                       const cairo_rectangle_int_t*extents)
+{
+    cairo_xcb_surface_t *surface;
+    cairo_bool_t need_clip_combine;
+    cairo_int_status_t status;
+
+    surface = (cairo_xcb_surface_t *)
+       _cairo_xcb_surface_create_similar (dst, CAIRO_CONTENT_ALPHA,
+                                          extents->width, extents->height);
+    if (unlikely (surface->base.status))
+       return surface;
+
+    _cairo_xcb_surface_ensure_picture (surface);
+
+    surface->deferred_clear_color = *CAIRO_COLOR_TRANSPARENT;
+    surface->deferred_clear = TRUE;
+    surface->base.is_clear = TRUE;
+
+    if (mask_func) {
+       status = mask_func (draw_closure, surface,
+                           CAIRO_OPERATOR_ADD, NULL,
+                           extents->x, extents->y,
+                           extents, clip);
+       if (likely (status != CAIRO_INT_STATUS_UNSUPPORTED))
+           return surface;
+    }
+
+    /* Is it worth setting the clip region here? */
+    status = draw_func (draw_closure, surface,
+                       CAIRO_OPERATOR_ADD, NULL,
+                       extents->x, extents->y,
+                       extents, NULL);
+    if (unlikely (status)) {
+       cairo_surface_destroy (&surface->base);
+       return (cairo_xcb_surface_t *) _cairo_surface_create_in_error (status);
+    }
+
+    if (surface->connection->flags & CAIRO_XCB_RENDER_HAS_FILL_RECTANGLES) {
+       int i;
+
+       for (i = 0; i < clip->num_boxes; i++) {
+           cairo_box_t *b = &clip->boxes[i];
+
+           if (! _cairo_fixed_is_integer (b->p1.x) ||
+               ! _cairo_fixed_is_integer (b->p1.y) ||
+               ! _cairo_fixed_is_integer (b->p2.x) ||
+               ! _cairo_fixed_is_integer (b->p2.y))
+           {
+               do_unaligned_box(blt_in, surface, b, extents->x, extents->y);
+           }
+       }
+
+       need_clip_combine = clip->path != NULL;
+    } else
+       need_clip_combine = ! _cairo_clip_is_region (clip);
+
+    if (need_clip_combine) {
+       status = _cairo_clip_combine_with_surface (clip, &surface->base,
+                                                  extents->x, extents->y);
+       if (unlikely (status)) {
+           cairo_surface_destroy (&surface->base);
+           return (cairo_xcb_surface_t *) _cairo_surface_create_in_error (status);
+       }
+    }
+
+    return surface;
+}
+
+/* Handles compositing with a clip surface when the operator allows
+ * us to combine the clip with the mask
+ */
+static cairo_status_t
+_clip_and_composite_with_mask (cairo_clip_t            *clip,
+                              cairo_operator_t          op,
+                              const cairo_pattern_t    *pattern,
+                              xcb_draw_func_t           draw_func,
+                              xcb_draw_func_t           mask_func,
+                              void                     *draw_closure,
+                              cairo_xcb_surface_t      *dst,
+                              const cairo_rectangle_int_t*extents)
+{
+    cairo_xcb_surface_t *mask;
+    cairo_xcb_picture_t *src;
+
+    mask = _create_composite_mask (clip,
+                                  draw_func, mask_func, draw_closure,
+                                  dst, extents);
+    if (unlikely (mask->base.status))
+       return mask->base.status;
+
+    if (pattern != NULL || dst->base.content != CAIRO_CONTENT_ALPHA) {
+       src = _cairo_xcb_picture_for_pattern (dst, pattern, extents);
+       if (unlikely (src->base.status)) {
+           cairo_surface_destroy (&mask->base);
+           return src->base.status;
+       }
+
+       _cairo_xcb_connection_render_composite (dst->connection,
+                                               _render_operator (op),
+                                               src->picture,
+                                               mask->picture,
+                                               dst->picture,
+                                               extents->x + src->x, extents->y + src->y,
+                                               0, 0,
+                                               extents->x,      extents->y,
+                                               extents->width,  extents->height);
+
+       cairo_surface_destroy (&src->base);
+    } else {
+       _cairo_xcb_connection_render_composite (dst->connection,
+                                               _render_operator (op),
+                                               mask->picture,
+                                               XCB_NONE,
+                                               dst->picture,
+                                               0, 0,
+                                               0, 0,
+                                               extents->x,      extents->y,
+                                               extents->width,  extents->height);
+    }
+    cairo_surface_destroy (&mask->base);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+/* Handles compositing with a clip surface when we have to do the operation
+ * in two pieces and combine them together.
+ */
+static cairo_status_t
+_clip_and_composite_combine (cairo_clip_t              *clip,
+                            cairo_operator_t            op,
+                            const cairo_pattern_t      *pattern,
+                            xcb_draw_func_t             draw_func,
+                            void                       *draw_closure,
+                            cairo_xcb_surface_t        *dst,
+                            const cairo_rectangle_int_t*extents)
+{
+    cairo_xcb_surface_t *tmp;
+    cairo_xcb_surface_t *clip_surface;
+    int clip_x, clip_y;
+    xcb_render_picture_t clip_picture;
+    cairo_status_t status;
+
+    tmp = (cairo_xcb_surface_t *)
+       _cairo_xcb_surface_create_similar (dst, dst->base.content,
+                                          extents->width, extents->height);
+    if (unlikely (tmp->base.status))
+       return tmp->base.status;
+
+    /* create_similar() could have done a fallback to an image surface */
+    assert (tmp->base.backend == &_cairo_xcb_surface_backend);
+
+    _cairo_xcb_surface_ensure_picture (tmp);
+
+    if (pattern == NULL) {
+       status = (*draw_func) (draw_closure, tmp,
+                              CAIRO_OPERATOR_ADD, NULL,
+                              extents->x, extents->y,
+                              extents, NULL);
+    } else {
+       /* Initialize the temporary surface from the destination surface */
+       if (! dst->base.is_clear ||
+           (dst->connection->flags & CAIRO_XCB_RENDER_HAS_FILL_RECTANGLES) == 0)
+       {
+           /* XCopyArea may actually be quicker here.
+            * A good driver should translate if appropriate.
+            */
+           _cairo_xcb_connection_render_composite (dst->connection,
+                                                   XCB_RENDER_PICT_OP_SRC,
+                                                   dst->picture,
+                                                   XCB_NONE,
+                                                   tmp->picture,
+                                                   extents->x,      extents->y,
+                                                   0, 0,
+                                                   0, 0,
+                                                   extents->width,  extents->height);
+       }
+       else
+       {
+           xcb_render_color_t clear;
+           xcb_rectangle_t xrect;
+
+           clear.red = clear.green = clear.blue = clear.alpha = 0;
+
+           xrect.x = xrect.y = 0;
+           xrect.width  = extents->width;
+           xrect.height = extents->height;
+
+           _cairo_xcb_connection_render_fill_rectangles (dst->connection,
+                                                         XCB_RENDER_PICT_OP_CLEAR,
+                                                         dst->picture,
+                                                         clear, 1, &xrect);
+       }
+
+       status = (*draw_func) (draw_closure, tmp, op, pattern,
+                              extents->x, extents->y,
+                              extents, NULL);
+    }
+    if (unlikely (status))
+       goto CLEANUP_SURFACE;
+
+    clip_surface = get_clip_surface (clip, dst, &clip_x, &clip_y);
+    status = clip_surface->base.status;
+    if (unlikely (status))
+       goto CLEANUP_SURFACE;
+
+    assert (clip_surface->base.backend == &_cairo_xcb_surface_backend);
+    clip_picture = clip_surface->picture;
+    assert (clip_picture != XCB_NONE);
+
+    if (dst->base.is_clear) {
+       _cairo_xcb_connection_render_composite (dst->connection,
+                                               XCB_RENDER_PICT_OP_SRC,
+                                               tmp->picture, clip_picture, dst->picture,
+                                               0, 0,
+                                               0, 0,
+                                               extents->x,      extents->y,
+                                               extents->width,  extents->height);
+    } else {
+       /* Punch the clip out of the destination */
+       _cairo_xcb_connection_render_composite (dst->connection,
+                                               XCB_RENDER_PICT_OP_OUT_REVERSE,
+                                               clip_picture, XCB_NONE, dst->picture,
+                                               extents->x - clip_x,
+                                               extents->y - clip_y,
+                                               0, 0,
+                                               extents->x,     extents->y,
+                                               extents->width, extents->height);
+
+       /* Now add the two results together */
+       _cairo_xcb_connection_render_composite (dst->connection,
+                                               XCB_RENDER_PICT_OP_ADD,
+                                               tmp->picture, clip_picture, dst->picture,
+                                               0, 0,
+                                               extents->x - clip_x,
+                                               extents->y - clip_y,
+                                               extents->x,     extents->y,
+                                               extents->width, extents->height);
+    }
+    cairo_surface_destroy (&clip_surface->base);
+
+ CLEANUP_SURFACE:
+    cairo_surface_destroy (&tmp->base);
+
+    return status;
+}
+
+/* Handles compositing for %CAIRO_OPERATOR_SOURCE, which is special; it's
+ * defined as (src IN mask IN clip) ADD (dst OUT (mask IN clip))
+ */
+static cairo_status_t
+_clip_and_composite_source (cairo_clip_t               *clip,
+                           const cairo_pattern_t       *pattern,
+                           xcb_draw_func_t              draw_func,
+                           xcb_draw_func_t              mask_func,
+                           void                        *draw_closure,
+                           cairo_xcb_surface_t         *dst,
+                           const cairo_rectangle_int_t *extents)
+{
+    cairo_xcb_surface_t *mask;
+    cairo_xcb_picture_t *src;
+
+    /* Create a surface that is mask IN clip */
+    mask = _create_composite_mask (clip,
+                                  draw_func, mask_func, draw_closure,
+                                  dst, extents);
+    if (unlikely (mask->base.status))
+       return mask->base.status;
+
+    src = _cairo_xcb_picture_for_pattern (dst, pattern, extents);
+    if (unlikely (src->base.status)) {
+       cairo_surface_destroy (&mask->base);
+       return src->base.status;
+    }
+
+    if (dst->base.is_clear) {
+       _cairo_xcb_connection_render_composite (dst->connection,
+                                               XCB_RENDER_PICT_OP_SRC,
+                                               src->picture,
+                                               mask->picture,
+                                               dst->picture,
+                                               extents->x + src->x, extents->y + src->y,
+                                               0, 0,
+                                               extents->x,      extents->y,
+                                               extents->width,  extents->height);
+    } else {
+       /* Compute dest' = dest OUT (mask IN clip) */
+       _cairo_xcb_connection_render_composite (dst->connection,
+                                               XCB_RENDER_PICT_OP_OUT_REVERSE,
+                                               mask->picture,
+                                               XCB_NONE,
+                                               dst->picture,
+                                               0, 0, 0, 0,
+                                               extents->x,     extents->y,
+                                               extents->width, extents->height);
+
+       /* Now compute (src IN (mask IN clip)) ADD dest' */
+       _cairo_xcb_connection_render_composite (dst->connection,
+                                               XCB_RENDER_PICT_OP_ADD,
+                                               src->picture,
+                                               mask->picture,
+                                               dst->picture,
+                                               extents->x + src->x, extents->y + src->y,
+                                               0, 0,
+                                               extents->x,     extents->y,
+                                               extents->width, extents->height);
+    }
+
+    cairo_surface_destroy (&src->base);
+    cairo_surface_destroy (&mask->base);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_bool_t
+can_reduce_alpha_op (cairo_operator_t op)
+{
+    int iop = op;
+    switch (iop) {
+    case CAIRO_OPERATOR_OVER:
+    case CAIRO_OPERATOR_SOURCE:
+    case CAIRO_OPERATOR_ADD:
+       return TRUE;
+    default:
+       return FALSE;
+    }
+}
+
+static cairo_bool_t
+reduce_alpha_op (cairo_surface_t *dst,
+                cairo_operator_t op,
+                const cairo_pattern_t *pattern)
+{
+    return dst->is_clear &&
+          dst->content == CAIRO_CONTENT_ALPHA &&
+          _cairo_pattern_is_opaque_solid (pattern) &&
+          can_reduce_alpha_op (op);
+}
+
+static cairo_status_t
+_cairo_xcb_surface_fixup_unbounded (cairo_xcb_surface_t *dst,
+                                   const cairo_composite_rectangles_t *rects)
+{
+    xcb_rectangle_t xrects[4];
+    int n;
+
+    if (rects->bounded.width  == rects->unbounded.width &&
+       rects->bounded.height == rects->unbounded.height)
+    {
+       return CAIRO_STATUS_SUCCESS;
+    }
+
+    n = 0;
+    if (rects->bounded.width == 0 || rects->bounded.height == 0) {
+       xrects[n].x = rects->unbounded.x;
+       xrects[n].width = rects->unbounded.width;
+       xrects[n].y = rects->unbounded.y;
+       xrects[n].height = rects->unbounded.height;
+       n++;
+    } else {
+       /* top */
+       if (rects->bounded.y != rects->unbounded.y) {
+           xrects[n].x = rects->unbounded.x;
+           xrects[n].width = rects->unbounded.width;
+           xrects[n].y = rects->unbounded.y;
+           xrects[n].height = rects->bounded.y - rects->unbounded.y;
+           n++;
+       }
+       /* left */
+       if (rects->bounded.x != rects->unbounded.x) {
+           xrects[n].x = rects->unbounded.x;
+           xrects[n].width = rects->bounded.x - rects->unbounded.x;
+           xrects[n].y = rects->bounded.y;
+           xrects[n].height = rects->bounded.height;
+           n++;
+       }
+       /* right */
+       if (rects->bounded.x + rects->bounded.width != rects->unbounded.x + rects->unbounded.width) {
+           xrects[n].x = rects->bounded.x + rects->bounded.width;
+           xrects[n].width = rects->unbounded.x + rects->unbounded.width - xrects[n].x;
+           xrects[n].y = rects->bounded.y;
+           xrects[n].height = rects->bounded.height;
+           n++;
+       }
+       /* bottom */
+       if (rects->bounded.y + rects->bounded.height != rects->unbounded.y + rects->unbounded.height) {
+           xrects[n].x = rects->unbounded.x;
+           xrects[n].width = rects->unbounded.width;
+           xrects[n].y = rects->bounded.y + rects->bounded.height;
+           xrects[n].height = rects->unbounded.y + rects->unbounded.height - xrects[n].y;
+           n++;
+       }
+    }
+
+    if (dst->connection->flags & CAIRO_XCB_RENDER_HAS_FILL_RECTANGLES) {
+       xcb_render_color_t color;
+
+       color.red   = 0;
+       color.green = 0;
+       color.blue  = 0;
+       color.alpha = 0;
+
+       _cairo_xcb_connection_render_fill_rectangles (dst->connection,
+                                                     XCB_RENDER_PICT_OP_CLEAR,
+                                                     dst->picture,
+                                                     color, n, xrects);
+    } else {
+       int i;
+       cairo_xcb_picture_t *src;
+
+       src = _cairo_xcb_transparent_picture (dst);
+       if (unlikely (src->base.status))
+           return src->base.status;
+
+       for (i = 0; i < n; i++) {
+           _cairo_xcb_connection_render_composite (dst->connection,
+                                                   XCB_RENDER_PICT_OP_CLEAR,
+                                                   src->picture, XCB_NONE, dst->picture,
+                                                   0, 0,
+                                                   0, 0,
+                                                   xrects[i].x, xrects[i].y,
+                                                   xrects[i].width, xrects[i].height);
+       }
+       cairo_surface_destroy (&src->base);
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_xcb_surface_fixup_unbounded_with_mask (cairo_xcb_surface_t *dst,
+                                             const cairo_composite_rectangles_t *rects,
+                                             cairo_clip_t *clip)
+{
+    cairo_xcb_surface_t *mask;
+    int mask_x, mask_y;
+
+    mask = get_clip_surface (clip, dst, &mask_x, &mask_y);
+    if (unlikely (mask->base.status))
+       return mask->base.status;
+
+    /* top */
+    if (rects->bounded.y != rects->unbounded.y) {
+       int x = rects->unbounded.x;
+       int y = rects->unbounded.y;
+       int width = rects->unbounded.width;
+       int height = rects->bounded.y - y;
+
+       _cairo_xcb_connection_render_composite (dst->connection,
+                                               XCB_RENDER_PICT_OP_OUT_REVERSE,
+                                               mask->picture, XCB_NONE, dst->picture,
+                                               x - mask_x, y - mask_y,
+                                               0, 0,
+                                               x, y,
+                                               width, height);
+    }
+
+    /* left */
+    if (rects->bounded.x != rects->unbounded.x) {
+       int x = rects->unbounded.x;
+       int y = rects->bounded.y;
+       int width = rects->bounded.x - x;
+       int height = rects->bounded.height;
+
+       _cairo_xcb_connection_render_composite (dst->connection,
+                                               XCB_RENDER_PICT_OP_OUT_REVERSE,
+                                               mask->picture, XCB_NONE, dst->picture,
+                                               x - mask_x, y - mask_y,
+                                               0, 0,
+                                               x, y,
+                                               width, height);
+    }
+
+    /* right */
+    if (rects->bounded.x + rects->bounded.width != rects->unbounded.x + rects->unbounded.width) {
+       int x = rects->bounded.x + rects->bounded.width;
+       int y = rects->bounded.y;
+       int width = rects->unbounded.x + rects->unbounded.width - x;
+       int height = rects->bounded.height;
+
+       _cairo_xcb_connection_render_composite (dst->connection,
+                                               XCB_RENDER_PICT_OP_OUT_REVERSE,
+                                               mask->picture, XCB_NONE, dst->picture,
+                                               x - mask_x, y - mask_y,
+                                               0, 0,
+                                               x, y,
+                                               width, height);
+    }
+
+    /* bottom */
+    if (rects->bounded.y + rects->bounded.height != rects->unbounded.y + rects->unbounded.height) {
+       int x = rects->unbounded.x;
+       int y = rects->bounded.y + rects->bounded.height;
+       int width = rects->unbounded.width;
+       int height = rects->unbounded.y + rects->unbounded.height - y;
+
+       _cairo_xcb_connection_render_composite (dst->connection,
+                                               XCB_RENDER_PICT_OP_OUT_REVERSE,
+                                               mask->picture, XCB_NONE, dst->picture,
+                                               x - mask_x, y - mask_y,
+                                               0, 0,
+                                               x, y,
+                                               width, height);
+    }
+
+    cairo_surface_destroy (&mask->base);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_xcb_surface_fixup_unbounded_boxes (cairo_xcb_surface_t *dst,
+                                         const cairo_composite_rectangles_t *extents,
+                                         cairo_clip_t *clip,
+                                         cairo_boxes_t *boxes)
+{
+    cairo_boxes_t clear;
+    cairo_box_t box;
+    cairo_status_t status;
+    struct _cairo_boxes_chunk *chunk;
+    int i;
+
+    if (boxes->num_boxes <= 1 && clip == NULL)
+       return _cairo_xcb_surface_fixup_unbounded (dst, extents);
+
+    _cairo_boxes_init (&clear);
+
+    box.p1.x = _cairo_fixed_from_int (extents->unbounded.x + extents->unbounded.width);
+    box.p1.y = _cairo_fixed_from_int (extents->unbounded.y);
+    box.p2.x = _cairo_fixed_from_int (extents->unbounded.x);
+    box.p2.y = _cairo_fixed_from_int (extents->unbounded.y + extents->unbounded.height);
+
+    if (clip == NULL) {
+       cairo_boxes_t tmp;
+
+       _cairo_boxes_init (&tmp);
+
+       status = _cairo_boxes_add (&tmp, CAIRO_ANTIALIAS_DEFAULT, &box);
+       assert (status == CAIRO_STATUS_SUCCESS);
+
+       tmp.chunks.next = &boxes->chunks;
+       tmp.num_boxes += boxes->num_boxes;
+
+       status = _cairo_bentley_ottmann_tessellate_boxes (&tmp,
+                                                         CAIRO_FILL_RULE_WINDING,
+                                                         &clear);
+
+       tmp.chunks.next = NULL;
+    } else {
+       _cairo_boxes_init_with_clip (&clear, clip);
+
+       status = _cairo_boxes_add (&clear, CAIRO_ANTIALIAS_DEFAULT, &box);
+       assert (status == CAIRO_STATUS_SUCCESS);
+
+       for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
+           for (i = 0; i < chunk->count; i++) {
+               status = _cairo_boxes_add (&clear,
+                                          CAIRO_ANTIALIAS_DEFAULT,
+                                          &chunk->base[i]);
+               if (unlikely (status)) {
+                   _cairo_boxes_fini (&clear);
+                   return status;
+               }
+           }
+       }
+
+       status = _cairo_bentley_ottmann_tessellate_boxes (&clear,
+                                                         CAIRO_FILL_RULE_WINDING,
+                                                         &clear);
+    }
+
+    if (likely (status == CAIRO_STATUS_SUCCESS)) {
+       status = _render_fill_boxes (dst,
+                                    CAIRO_OPERATOR_CLEAR,
+                                    CAIRO_COLOR_TRANSPARENT,
+                                    &clear);
+    }
+
+    _cairo_boxes_fini (&clear);
+
+    return status;
+}
+
+cairo_status_t
+_cairo_xcb_surface_clear (cairo_xcb_surface_t *dst)
+{
+    xcb_gcontext_t gc;
+    xcb_rectangle_t rect;
+    cairo_status_t status;
+
+    status = _cairo_xcb_connection_acquire (dst->connection);
+    if (unlikely (status))
+       return status;
+
+    rect.x = rect.y = 0;
+    rect.width  = dst->width;
+    rect.height = dst->height;
+
+    if (dst->connection->flags & CAIRO_XCB_RENDER_HAS_COMPOSITE) {
+       xcb_render_color_t color;
+       uint8_t op;
+
+       color.red   = dst->deferred_clear_color.red_short;
+       color.green = dst->deferred_clear_color.green_short;
+       color.blue  = dst->deferred_clear_color.blue_short;
+       color.alpha = dst->deferred_clear_color.alpha_short;
+
+       if (color.alpha == 0)
+           op = XCB_RENDER_PICT_OP_CLEAR;
+       else
+           op = XCB_RENDER_PICT_OP_SRC;
+
+       _cairo_xcb_surface_ensure_picture (dst);
+       _cairo_xcb_connection_render_fill_rectangles (dst->connection,
+                                                     op, dst->picture, color,
+                                                     1, &rect);
+    } else {
+       gc = _cairo_xcb_screen_get_gc (dst->screen, dst->drawable, dst->depth);
+
+       /* XXX color */
+       _cairo_xcb_connection_poly_fill_rectangle (dst->connection,
+                                                  dst->drawable, gc,
+                                                  1, &rect);
+
+       _cairo_xcb_screen_put_gc (dst->screen, dst->depth, gc);
+    }
+
+    _cairo_xcb_connection_release (dst->connection);
+
+    dst->deferred_clear = FALSE;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+enum {
+    NEED_CLIP_REGION = 0x1,
+    NEED_CLIP_SURFACE = 0x2,
+    FORCE_CLIP_REGION = 0x4,
+};
+
+static cairo_bool_t
+need_bounded_clip (cairo_composite_rectangles_t *extents)
+{
+    unsigned int flags = NEED_CLIP_REGION;
+    if (! _cairo_clip_is_region (extents->clip))
+       flags |= NEED_CLIP_SURFACE;
+    return flags;
+}
+
+static cairo_bool_t
+need_unbounded_clip (cairo_composite_rectangles_t *extents)
+{
+    unsigned int flags = 0;
+    if (! extents->is_bounded) {
+       flags |= NEED_CLIP_REGION;
+       if (! _cairo_clip_is_region (extents->clip))
+           flags |= NEED_CLIP_SURFACE;
+    }
+    if (extents->clip->path != NULL)
+       flags |= NEED_CLIP_SURFACE;
+    return flags;
+}
+
+static cairo_status_t
+_clip_and_composite (cairo_xcb_surface_t       *dst,
+                    cairo_operator_t            op,
+                    const cairo_pattern_t      *src,
+                    xcb_draw_func_t             draw_func,
+                    xcb_draw_func_t             mask_func,
+                    void                       *draw_closure,
+                    cairo_composite_rectangles_t*extents,
+                    unsigned int need_clip)
+{
+    cairo_region_t *clip_region = NULL;
+    cairo_status_t status;
+
+    status = _cairo_xcb_connection_acquire (dst->connection);
+    if (unlikely (status))
+       return status;
+
+    if (dst->deferred_clear) {
+       status = _cairo_xcb_surface_clear (dst);
+       if (unlikely (status)) {
+           _cairo_xcb_connection_release (dst->connection);
+           return status;
+       }
+    }
+
+    _cairo_xcb_surface_ensure_picture (dst);
+
+    if (need_clip & NEED_CLIP_REGION) {
+       clip_region = _cairo_clip_get_region (extents->clip);
+       if ((need_clip & FORCE_CLIP_REGION) == 0 && clip_region != NULL &&
+           cairo_region_contains_rectangle (clip_region,
+                                            &extents->unbounded) == CAIRO_REGION_OVERLAP_IN)
+           clip_region = NULL;
+       if (clip_region != NULL) {
+           status = _cairo_xcb_surface_set_clip_region (dst, clip_region);
+           if (unlikely (status)) {
+               _cairo_xcb_connection_release (dst->connection);
+               return status;
+           }
+       }
+    }
+
+    if (reduce_alpha_op (&dst->base, op, src)) {
+       op = CAIRO_OPERATOR_ADD;
+       src = NULL;
+    }
+
+    if (extents->bounded.width != 0 && extents->bounded.height != 0) {
+       if (op == CAIRO_OPERATOR_SOURCE) {
+           status = _clip_and_composite_source (extents->clip, src,
+                                                draw_func, mask_func, draw_closure,
+                                                dst, &extents->bounded);
+       } else {
+           if (op == CAIRO_OPERATOR_CLEAR) {
+               op = CAIRO_OPERATOR_DEST_OUT;
+               src = NULL;
+           }
+
+           if (need_clip & NEED_CLIP_SURFACE) {
+               if (extents->is_bounded) {
+                   status = _clip_and_composite_with_mask (extents->clip, op, src,
+                                                           draw_func,
+                                                           mask_func,
+                                                           draw_closure,
+                                                           dst, &extents->bounded);
+               } else {
+                   status = _clip_and_composite_combine (extents->clip, op, src,
+                                                         draw_func, draw_closure,
+                                                         dst, &extents->bounded);
+               }
+           } else {
+               status = draw_func (draw_closure,
+                                   dst, op, src,
+                                   0, 0,
+                                   &extents->bounded,
+                                   extents->clip);
+           }
+       }
+    }
+
+    if (status == CAIRO_STATUS_SUCCESS && ! extents->is_bounded) {
+       if (need_clip & NEED_CLIP_SURFACE)
+           status = _cairo_xcb_surface_fixup_unbounded_with_mask (dst, extents, extents->clip);
+       else
+           status = _cairo_xcb_surface_fixup_unbounded (dst, extents);
+    }
+
+    if (clip_region)
+       _cairo_xcb_surface_clear_clip_region (dst);
+
+    _cairo_xcb_connection_release (dst->connection);
+
+    return status;
+}
+
+static cairo_status_t
+_core_boxes (cairo_xcb_surface_t *dst,
+            cairo_operator_t op,
+            const cairo_pattern_t *src,
+            cairo_boxes_t *boxes,
+            const cairo_composite_rectangles_t *extents)
+{
+    if (! boxes->is_pixel_aligned)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    if (! _cairo_clip_is_region (extents->clip))
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    if (op == CAIRO_OPERATOR_CLEAR)
+       return _cairo_xcb_surface_core_fill_boxes (dst, CAIRO_COLOR_TRANSPARENT, boxes);
+
+    if (op == CAIRO_OPERATOR_OVER) {
+       if (dst->base.is_clear || _cairo_pattern_is_opaque (src, &extents->bounded))
+           op = CAIRO_OPERATOR_SOURCE;
+    }
+    if (op != CAIRO_OPERATOR_SOURCE)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    if (src->type == CAIRO_PATTERN_TYPE_SOLID) {
+       return _cairo_xcb_surface_core_fill_boxes (dst,
+                                                  &((cairo_solid_pattern_t *) src)->color,
+                                                  boxes);
+    }
+
+    return _cairo_xcb_surface_core_copy_boxes (dst, src, &extents->bounded, boxes);
+}
+
+static cairo_status_t
+_composite_boxes (cairo_xcb_surface_t *dst,
+                 cairo_operator_t op,
+                 const cairo_pattern_t *src,
+                 cairo_boxes_t *boxes,
+                 const cairo_composite_rectangles_t *extents)
+{
+    cairo_clip_t *clip = extents->clip;
+    cairo_bool_t need_clip_mask = ! _cairo_clip_is_region (clip);
+    cairo_status_t status;
+
+    /* If the boxes are not pixel-aligned, we will need to compute a real mask */
+    if (! boxes->is_pixel_aligned)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    if (need_clip_mask &&
+       (! extents->is_bounded || op == CAIRO_OPERATOR_SOURCE))
+    {
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+    }
+
+    status = _cairo_xcb_connection_acquire (dst->connection);
+    if (unlikely (status))
+       return status;
+
+    _cairo_xcb_surface_ensure_picture (dst);
+    if (dst->connection->flags & CAIRO_XCB_RENDER_HAS_FILL_RECTANGLES && ! need_clip_mask &&
+       (op == CAIRO_OPERATOR_CLEAR || src->type == CAIRO_PATTERN_TYPE_SOLID))
+    {
+       const cairo_color_t *color;
+
+       if (op == CAIRO_OPERATOR_CLEAR)
+           color = CAIRO_COLOR_TRANSPARENT;
+       else
+           color = &((cairo_solid_pattern_t *) src)->color;
+
+       status = _render_fill_boxes (dst, op, color, boxes);
+    }
+    else
+    {
+       cairo_surface_pattern_t mask;
+
+       if (need_clip_mask) {
+           cairo_xcb_surface_t *clip_surface;
+           int clip_x, clip_y;
+
+           clip_surface = get_clip_surface (extents->clip, dst,
+                                            &clip_x, &clip_y);
+           if (unlikely (clip_surface->base.status))
+               return clip_surface->base.status;
+
+           _cairo_pattern_init_for_surface (&mask, &clip_surface->base);
+           mask.base.filter = CAIRO_FILTER_NEAREST;
+           cairo_matrix_init_translate (&mask.base.matrix,
+                                        -clip_x,
+                                        -clip_y);
+           cairo_surface_destroy (&clip_surface->base);
+
+           if (op == CAIRO_OPERATOR_CLEAR) {
+               src = NULL;
+               op = CAIRO_OPERATOR_DEST_OUT;
+           }
+       }
+
+       status = _render_composite_boxes (dst, op, src,
+                                         need_clip_mask ? &mask.base : NULL,
+                                         &extents->bounded, boxes);
+
+       if (need_clip_mask)
+           _cairo_pattern_fini (&mask.base);
+    }
+
+    if (status == CAIRO_STATUS_SUCCESS && ! extents->is_bounded) {
+       status =
+           _cairo_xcb_surface_fixup_unbounded_boxes (dst, extents,
+                                                     clip, boxes);
+    }
+
+    _cairo_xcb_connection_release (dst->connection);
+
+    return status;
+}
+
+static cairo_bool_t
+cairo_boxes_for_each_box (cairo_boxes_t *boxes,
+                         cairo_bool_t (*func) (cairo_box_t *box,
+                                               void *data),
+                         void *data)
+{
+    struct _cairo_boxes_chunk *chunk;
+    int i;
+
+    for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
+       for (i = 0; i < chunk->count; i++)
+           if (! func (&chunk->base[i], data))
+               return FALSE;
+    }
+
+    return TRUE;
+}
+
+struct _image_contains_box {
+    int width, height;
+    int tx, ty;
+};
+
+static cairo_bool_t image_contains_box (cairo_box_t *box, void *closure)
+{
+    struct _image_contains_box *data = closure;
+
+    /* The box is pixel-aligned so the truncation is safe. */
+    return
+       _cairo_fixed_integer_part (box->p1.x) + data->tx >= 0 &&
+       _cairo_fixed_integer_part (box->p1.y) + data->ty >= 0 &&
+       _cairo_fixed_integer_part (box->p2.x) + data->tx <= data->width &&
+       _cairo_fixed_integer_part (box->p2.y) + data->ty <= data->height;
+}
+
+struct _image_upload_box {
+    cairo_xcb_surface_t *surface;
+    cairo_image_surface_t *image;
+    xcb_gcontext_t gc;
+    int tx, ty;
+};
+
+static cairo_bool_t image_upload_box (cairo_box_t *box, void *closure)
+{
+    const struct _image_upload_box *iub = closure;
+    /* The box is pixel-aligned so the truncation is safe. */
+    int x = _cairo_fixed_integer_part (box->p1.x);
+    int y = _cairo_fixed_integer_part (box->p1.y);
+    int width  = _cairo_fixed_integer_part (box->p2.x - box->p1.x);
+    int height = _cairo_fixed_integer_part (box->p2.y - box->p1.y);
+    int bpp = PIXMAN_FORMAT_BPP (iub->image->pixman_format);
+    int len = CAIRO_STRIDE_FOR_WIDTH_BPP (width, bpp);
+    if (len == iub->image->stride) {
+       _cairo_xcb_connection_put_image (iub->surface->connection,
+                                        iub->surface->drawable,
+                                        iub->gc,
+                                        width, height,
+                                        x, y,
+                                        iub->image->depth,
+                                        iub->image->stride,
+                                        iub->image->data +
+                                        (y + iub->ty) * iub->image->stride +
+                                        (x + iub->tx) * bpp/8);
+    } else {
+       _cairo_xcb_connection_put_subimage (iub->surface->connection,
+                                           iub->surface->drawable,
+                                           iub->gc,
+                                           x + iub->tx,
+                                           y + iub->ty,
+                                           width, height,
+                                           bpp / 8,
+                                           iub->image->stride,
+                                           x, y,
+                                           iub->image->depth,
+                                           iub->image->data);
+    }
+
+    return TRUE;
+}
+
+static cairo_status_t
+_upload_image_inplace (cairo_xcb_surface_t *surface,
+                      const cairo_pattern_t *source,
+                      cairo_boxes_t *boxes)
+{
+    const cairo_surface_pattern_t *pattern;
+    struct _image_contains_box icb;
+    struct _image_upload_box iub;
+    cairo_image_surface_t *image;
+    cairo_status_t status;
+    int tx, ty;
+
+    if (! boxes->is_pixel_aligned)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    if (source->type != CAIRO_PATTERN_TYPE_SURFACE)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    pattern = (const cairo_surface_pattern_t *) source;
+    if (pattern->surface->type != CAIRO_SURFACE_TYPE_IMAGE)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    /* Have we already upload this image to a pixmap? */
+    {
+       cairo_xcb_picture_t *snapshot;
+
+       snapshot = (cairo_xcb_picture_t *)
+           _cairo_surface_has_snapshot (pattern->surface, &_cairo_xcb_picture_backend);
+       if (snapshot != NULL) {
+           if (snapshot->screen == surface->screen)
+               return CAIRO_INT_STATUS_UNSUPPORTED;
+       }
+    }
+
+    image = (cairo_image_surface_t *) pattern->surface;
+    if (image->format == CAIRO_FORMAT_INVALID)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    if (image->depth != surface->depth)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    if (! _cairo_matrix_is_integer_translation (&source->matrix, &tx, &ty))
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    /* Check that the data is entirely within the image */
+    icb.width = image->width;
+    icb.height = image->height;
+    icb.tx = tx;
+    icb.ty = ty;
+    if (! cairo_boxes_for_each_box (boxes, image_contains_box, &icb))
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    if (surface->deferred_clear) {
+       status = _cairo_xcb_surface_clear (surface);
+       if (unlikely (status))
+           return status;
+    }
+
+    status = _cairo_xcb_connection_acquire (surface->connection);
+    if (unlikely (status))
+       return status;
+
+    iub.surface = surface;
+    iub.image = image;
+    iub.gc = _cairo_xcb_screen_get_gc (surface->screen,
+                                      surface->drawable,
+                                      image->depth);
+    iub.tx = tx;
+    iub.ty = ty;
+    cairo_boxes_for_each_box (boxes, image_upload_box, &iub);
+
+    _cairo_xcb_screen_put_gc (surface->screen, image->depth, iub.gc);
+    _cairo_xcb_connection_release (surface->connection);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+trim_extents_to_traps (cairo_composite_rectangles_t *extents,
+                      cairo_traps_t *traps)
+{
+    cairo_box_t box;
+
+    /* X trims the affected area to the extents of the trapezoids, so
+     * we need to compensate when fixing up the unbounded area.
+    */
+    _cairo_traps_extents (traps, &box);
+    return _cairo_composite_rectangles_intersect_mask_extents (extents, &box);
+}
+
+static cairo_bool_t
+_mono_edge_is_vertical (const cairo_line_t *line)
+{
+    return _cairo_fixed_integer_round_down (line->p1.x) == _cairo_fixed_integer_round_down (line->p2.x);
+}
+
+static cairo_bool_t
+_traps_are_pixel_aligned (cairo_traps_t *traps,
+                         cairo_antialias_t antialias)
+{
+    int i;
+
+    if (antialias == CAIRO_ANTIALIAS_NONE) {
+       for (i = 0; i < traps->num_traps; i++) {
+           if (! _mono_edge_is_vertical (&traps->traps[i].left)   ||
+               ! _mono_edge_is_vertical (&traps->traps[i].right))
+           {
+               traps->maybe_region = FALSE;
+               return FALSE;
+           }
+       }
+    } else {
+       for (i = 0; i < traps->num_traps; i++) {
+           if (traps->traps[i].left.p1.x != traps->traps[i].left.p2.x   ||
+               traps->traps[i].right.p1.x != traps->traps[i].right.p2.x ||
+               ! _cairo_fixed_is_integer (traps->traps[i].top)          ||
+               ! _cairo_fixed_is_integer (traps->traps[i].bottom)       ||
+               ! _cairo_fixed_is_integer (traps->traps[i].left.p1.x)    ||
+               ! _cairo_fixed_is_integer (traps->traps[i].right.p1.x))
+           {
+               traps->maybe_region = FALSE;
+               return FALSE;
+           }
+       }
+    }
+
+    return TRUE;
+}
+
+static void
+_boxes_for_traps (cairo_boxes_t *boxes,
+                 cairo_traps_t *traps,
+                 cairo_antialias_t antialias)
+{
+    int i;
+
+    _cairo_boxes_init (boxes);
+
+    boxes->num_boxes    = traps->num_traps;
+    boxes->chunks.base  = (cairo_box_t *) traps->traps;
+    boxes->chunks.count = traps->num_traps;
+    boxes->chunks.size  = traps->num_traps;
+
+    if (antialias != CAIRO_ANTIALIAS_NONE) {
+       for (i = 0; i < traps->num_traps; i++) {
+           /* Note the traps and boxes alias so we need to take the local copies first. */
+           cairo_fixed_t x1 = traps->traps[i].left.p1.x;
+           cairo_fixed_t x2 = traps->traps[i].right.p1.x;
+           cairo_fixed_t y1 = traps->traps[i].top;
+           cairo_fixed_t y2 = traps->traps[i].bottom;
+
+           boxes->chunks.base[i].p1.x = x1;
+           boxes->chunks.base[i].p1.y = y1;
+           boxes->chunks.base[i].p2.x = x2;
+           boxes->chunks.base[i].p2.y = y2;
+
+           if (boxes->is_pixel_aligned) {
+               boxes->is_pixel_aligned =
+                   _cairo_fixed_is_integer (x1) && _cairo_fixed_is_integer (y1) &&
+                   _cairo_fixed_is_integer (x2) && _cairo_fixed_is_integer (y2);
+           }
+       }
+    } else {
+       boxes->is_pixel_aligned = TRUE;
+
+       for (i = 0; i < traps->num_traps; i++) {
+           /* Note the traps and boxes alias so we need to take the local copies first. */
+           cairo_fixed_t x1 = traps->traps[i].left.p1.x;
+           cairo_fixed_t x2 = traps->traps[i].right.p1.x;
+           cairo_fixed_t y1 = traps->traps[i].top;
+           cairo_fixed_t y2 = traps->traps[i].bottom;
+
+           /* round down here to match Pixman's behavior when using traps. */
+           boxes->chunks.base[i].p1.x = _cairo_fixed_round_down (x1);
+           boxes->chunks.base[i].p1.y = _cairo_fixed_round_down (y1);
+           boxes->chunks.base[i].p2.x = _cairo_fixed_round_down (x2);
+           boxes->chunks.base[i].p2.y = _cairo_fixed_round_down (y2);
+       }
+    }
+}
+
+static cairo_status_t
+_composite_polygon (cairo_xcb_surface_t *dst,
+                   cairo_operator_t op,
+                   const cairo_pattern_t *source,
+                   cairo_polygon_t *polygon,
+                   cairo_antialias_t antialias,
+                   cairo_fill_rule_t fill_rule,
+                   cairo_composite_rectangles_t *extents)
+{
+    composite_traps_info_t traps;
+    cairo_bool_t clip_surface = ! _cairo_clip_is_region (extents->clip);
+    cairo_region_t *clip_region = _cairo_clip_get_region (extents->clip);
+    cairo_status_t status;
+
+    if (polygon->num_edges == 0) {
+       status = CAIRO_STATUS_SUCCESS;
+
+       if (! extents->is_bounded) {
+           if (cairo_region_contains_rectangle (clip_region, &extents->unbounded) == CAIRO_REGION_OVERLAP_IN)
+               clip_region = NULL;
+
+           if (clip_surface == FALSE) {
+               if (clip_region != NULL) {
+                   status = _cairo_xcb_surface_set_clip_region (dst, clip_region);
+                   if (unlikely (status))
+                       return status;
+               }
+
+               status = _cairo_xcb_surface_fixup_unbounded (dst, extents);
+
+               if (clip_region != NULL)
+                   _cairo_xcb_surface_clear_clip_region (dst);
+           } else {
+               status = _cairo_xcb_surface_fixup_unbounded_with_mask (dst,
+                                                                      extents,
+                                                                      extents->clip);
+           }
+       }
+
+       return status;
+    }
+
+    if (extents->clip->path != NULL && extents->is_bounded) {
+       cairo_polygon_t clipper;
+       cairo_fill_rule_t clipper_fill_rule;
+       cairo_antialias_t clipper_antialias;
+
+       status = _cairo_clip_get_polygon (extents->clip,
+                                         &clipper,
+                                         &clipper_fill_rule,
+                                         &clipper_antialias);
+       if (likely (status == CAIRO_STATUS_SUCCESS)) {
+           if (clipper_antialias == antialias) {
+               status = _cairo_polygon_intersect (polygon, fill_rule,
+                                                  &clipper, clipper_fill_rule);
+               if (likely (status == CAIRO_STATUS_SUCCESS)) {
+                   cairo_clip_t * clip = _cairo_clip_copy_region (extents->clip);
+                   _cairo_clip_destroy (extents->clip);
+                   extents->clip = clip;
+
+                   fill_rule = CAIRO_FILL_RULE_WINDING;
+               }
+               _cairo_polygon_fini (&clipper);
+           }
+       }
+    }
+
+    _cairo_traps_init (&traps.traps);
+
+    status = _cairo_bentley_ottmann_tessellate_polygon (&traps.traps, polygon, fill_rule);
+    if (unlikely (status))
+       goto CLEANUP_TRAPS;
+
+    if (traps.traps.has_intersections) {
+       if (traps.traps.is_rectangular)
+           status = _cairo_bentley_ottmann_tessellate_rectangular_traps (&traps.traps, CAIRO_FILL_RULE_WINDING);
+       else if (traps.traps.is_rectilinear)
+           status = _cairo_bentley_ottmann_tessellate_rectilinear_traps (&traps.traps, CAIRO_FILL_RULE_WINDING);
+       else
+           status = _cairo_bentley_ottmann_tessellate_traps (&traps.traps, CAIRO_FILL_RULE_WINDING);
+       if (unlikely (status))
+           goto CLEANUP_TRAPS;
+    }
+
+    /* Use a fast path if the trapezoids consist of a simple region,
+     * but we can only do this if we do not have a clip surface, or can
+     * substitute the mask with the clip.
+     */
+    if (traps.traps.maybe_region &&
+       _traps_are_pixel_aligned (&traps.traps, antialias) &&
+       (! clip_surface ||
+        (extents->is_bounded && op != CAIRO_OPERATOR_SOURCE)))
+    {
+       cairo_boxes_t boxes;
+
+       _boxes_for_traps (&boxes, &traps.traps, antialias);
+       status = _clip_and_composite_boxes (dst, op, source, &boxes, extents);
+    }
+    else
+    {
+       /* Otherwise render the trapezoids to a mask and composite in the usual
+        * fashion.
+        */
+       traps.antialias = antialias;
+       status = trim_extents_to_traps (extents, &traps.traps);
+       if (likely (status == CAIRO_STATUS_SUCCESS)) {
+           unsigned int flags = 0;
+
+           /* For unbounded operations, the X11 server will estimate the
+            * affected rectangle and apply the operation to that. However,
+            * there are cases where this is an overestimate (e.g. the
+            * clip-fill-{eo,nz}-unbounded test).
+            *
+            * The clip will trim that overestimate to our expectations.
+            */
+           if (! extents->is_bounded)
+               flags |= FORCE_CLIP_REGION;
+
+           status = _clip_and_composite (dst, op, source, _composite_traps,
+                                         NULL, &traps, extents,
+                                         need_unbounded_clip (extents) | flags);
+       }
+    }
+
+CLEANUP_TRAPS:
+    _cairo_traps_fini (&traps.traps);
+
+    return status;
+}
+
+static cairo_status_t
+_clip_and_composite_boxes (cairo_xcb_surface_t *dst,
+                          cairo_operator_t op,
+                          const cairo_pattern_t *src,
+                          cairo_boxes_t *boxes,
+                          cairo_composite_rectangles_t *extents)
+{
+    composite_traps_info_t info;
+    cairo_int_status_t status;
+
+    if (boxes->num_boxes == 0 && extents->is_bounded)
+       return CAIRO_STATUS_SUCCESS;
+
+    if (boxes->is_pixel_aligned && _cairo_clip_is_region (extents->clip) &&
+       (op == CAIRO_OPERATOR_SOURCE ||
+        (dst->base.is_clear && (op == CAIRO_OPERATOR_OVER || op == CAIRO_OPERATOR_ADD))))
+    {
+       if (boxes->num_boxes == 1 &&
+           extents->bounded.width  == dst->width &&
+           extents->bounded.height == dst->height)
+       {
+           op = CAIRO_OPERATOR_SOURCE;
+           dst->deferred_clear = FALSE;
+       }
+
+       status = _upload_image_inplace (dst, src, boxes);
+       if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+           return status;
+    }
+
+    /* Can we reduce drawing through a clip-mask to simply drawing the clip? */
+    if (extents->clip->path != NULL && extents->is_bounded) {
+       cairo_polygon_t polygon;
+       cairo_fill_rule_t fill_rule;
+       cairo_antialias_t antialias;
+       cairo_clip_t *clip;
+
+       clip = _cairo_clip_copy (extents->clip);
+       clip = _cairo_clip_intersect_boxes (clip, boxes);
+       status = _cairo_clip_get_polygon (clip, &polygon,
+                                         &fill_rule, &antialias);
+       _cairo_clip_path_destroy (clip->path);
+       clip->path = NULL;
+       if (likely (status == CAIRO_INT_STATUS_SUCCESS)) {
+           cairo_clip_t *saved_clip = extents->clip;
+           extents->clip = clip;
+           status = _composite_polygon (dst, op, src,
+                                        &polygon,
+                                        antialias,
+                                        fill_rule,
+                                        extents);
+           if (extents->clip != clip)
+               clip = NULL;
+           extents->clip = saved_clip;
+           _cairo_polygon_fini (&polygon);
+       }
+       if (clip)
+           _cairo_clip_destroy (clip);
+
+       if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+           return status;
+    }
+
+    if (dst->deferred_clear) {
+       status = _cairo_xcb_surface_clear (dst);
+       if (unlikely (status))
+           return status;
+    }
+
+    if (boxes->is_pixel_aligned &&
+       _cairo_clip_is_region (extents->clip) &&
+       op == CAIRO_OPERATOR_SOURCE) {
+       status = _upload_image_inplace (dst, src, boxes);
+       if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+           return status;
+    }
+
+    if ((dst->connection->flags & CAIRO_XCB_RENDER_HAS_COMPOSITE) == 0)
+       return _core_boxes (dst, op, src, boxes, extents);
+
+    /* Use a fast path if the boxes are pixel aligned */
+    status = _composite_boxes (dst, op, src, boxes, extents);
+    if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+       return status;
+
+    if ((dst->connection->flags & CAIRO_XCB_RENDER_HAS_COMPOSITE_TRAPEZOIDS) == 0)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    /* Otherwise render via a mask and composite in the usual fashion.  */
+    status = _cairo_traps_init_boxes (&info.traps, boxes);
+    if (unlikely (status))
+       return status;
+
+    info.antialias = CAIRO_ANTIALIAS_DEFAULT;
+    status = trim_extents_to_traps (extents, &info.traps);
+    if (status == CAIRO_INT_STATUS_SUCCESS) {
+       status = _clip_and_composite (dst, op, src,
+                                     _composite_traps, NULL, &info,
+                                     extents, need_unbounded_clip (extents));
+    }
+
+    _cairo_traps_fini (&info.traps);
+    return status;
+}
+
+static cairo_int_status_t
+_composite_mask (void                          *closure,
+                cairo_xcb_surface_t            *dst,
+                cairo_operator_t                op,
+                const cairo_pattern_t          *src_pattern,
+                int                             dst_x,
+                int                             dst_y,
+                const cairo_rectangle_int_t    *extents,
+                cairo_clip_t                   *clip)
+{
+    const cairo_pattern_t *mask_pattern = closure;
+    cairo_xcb_picture_t *src, *mask = NULL;
+    cairo_status_t status;
+
+    if (dst->base.is_clear) {
+       if (op == CAIRO_OPERATOR_OVER || op == CAIRO_OPERATOR_ADD)
+           op = CAIRO_OPERATOR_SOURCE;
+    }
+
+    if (op == CAIRO_OPERATOR_SOURCE && clip == NULL)
+       dst->deferred_clear = FALSE;
+
+    if (dst->deferred_clear) {
+       status = _cairo_xcb_surface_clear (dst);
+       if (unlikely (status))
+               return status;
+    }
+
+    if (src_pattern != NULL) {
+       src = _cairo_xcb_picture_for_pattern (dst, src_pattern, extents);
+       if (unlikely (src->base.status))
+           return src->base.status;
+
+       mask = _cairo_xcb_picture_for_pattern (dst, mask_pattern, extents);
+       if (unlikely (mask->base.status)) {
+           cairo_surface_destroy (&src->base);
+           return mask->base.status;
+       }
+
+       _cairo_xcb_connection_render_composite (dst->connection,
+                                               _render_operator (op),
+                                               src->picture,
+                                               mask->picture,
+                                               dst->picture,
+                                               extents->x + src->x,  extents->y + src->y,
+                                               extents->x + mask->x, extents->y + mask->y,
+                                               extents->x - dst_x,   extents->y - dst_y,
+                                               extents->width,       extents->height);
+       cairo_surface_destroy (&mask->base);
+       cairo_surface_destroy (&src->base);
+    } else {
+       src = _cairo_xcb_picture_for_pattern (dst, mask_pattern, extents);
+       if (unlikely (src->base.status))
+           return src->base.status;
+
+       _cairo_xcb_connection_render_composite (dst->connection,
+                                               _render_operator (op),
+                                               src->picture,
+                                               XCB_NONE,
+                                               dst->picture,
+                                               extents->x + src->x,  extents->y + src->y,
+                                               0, 0,
+                                               extents->x - dst_x,   extents->y - dst_y,
+                                               extents->width,       extents->height);
+       cairo_surface_destroy (&src->base);
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+struct composite_box_info {
+    cairo_xcb_surface_t *dst;
+    cairo_xcb_picture_t *src;
+    uint8_t op;
+};
+
+static void composite_box(void *closure,
+                         int16_t x, int16_t y,
+                         int16_t w, int16_t h,
+                         uint16_t coverage)
+{
+    struct composite_box_info *info = closure;
+
+    if (coverage < 0xff00) {
+       cairo_xcb_picture_t *mask;
+       cairo_color_t color;
+
+       color.red_short = color.green_short = color.blue_short = 0;
+       color.alpha_short = coverage;
+
+       mask = _solid_picture (info->dst, &color);
+       if (likely (mask->base.status == CAIRO_STATUS_SUCCESS)) {
+           _cairo_xcb_connection_render_composite (info->dst->connection,
+                                                   info->op,
+                                                   info->src->picture,
+                                                   mask->picture,
+                                                   info->dst->picture,
+                                                   x + info->src->x,  y + info->src->y,
+                                                   0,                 0,
+                                                   x,                 y,
+                                                   w,                 h);
+       }
+       cairo_surface_destroy (&mask->base);
+    } else {
+       _cairo_xcb_connection_render_composite (info->dst->connection,
+                                               info->op,
+                                               info->src->picture,
+                                               XCB_NONE,
+                                               info->dst->picture,
+                                               x + info->src->x,  y + info->src->y,
+                                               0,                 0,
+                                               x,                 y,
+                                               w,                 h);
+    }
+}
+
+static cairo_int_status_t
+_composite_mask_clip_boxes (void                       *closure,
+                           cairo_xcb_surface_t         *dst,
+                           cairo_operator_t             op,
+                           const cairo_pattern_t       *src_pattern,
+                           int                          dst_x,
+                           int                          dst_y,
+                           const cairo_rectangle_int_t *extents,
+                           cairo_clip_t                *clip)
+{
+    struct composite_box_info info;
+    cairo_status_t status;
+    int i;
+
+    assert (src_pattern == NULL);
+    assert (op == CAIRO_OPERATOR_ADD);
+    assert (dst->base.is_clear);
+
+    if (clip->num_boxes > 1) {
+       status = _cairo_xcb_surface_clear (dst);
+       if (unlikely (status))
+           return status;
+    }
+
+    info.op = XCB_RENDER_PICT_OP_SRC;
+    info.dst = dst;
+    info.src = _cairo_xcb_picture_for_pattern (dst, closure, extents);
+    if (unlikely (info.src->base.status))
+       return info.src->base.status;
+
+    info.src->x += dst_x;
+    info.src->y += dst_y;
+
+    for (i = 0; i < clip->num_boxes; i++)
+       do_unaligned_box(composite_box, &info, &clip->boxes[i], dst_x, dst_y);
+    cairo_surface_destroy (&info.src->base);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_composite_mask_clip (void                             *closure,
+                     cairo_xcb_surface_t               *dst,
+                     cairo_operator_t                   op,
+                     const cairo_pattern_t             *src_pattern,
+                     int                                dst_x,
+                     int                                dst_y,
+                     const cairo_rectangle_int_t       *extents,
+                     cairo_clip_t                      *clip)
+{
+    const cairo_pattern_t *mask_pattern = closure;
+    cairo_polygon_t polygon;
+    cairo_fill_rule_t fill_rule;
+    composite_traps_info_t info;
+    cairo_status_t status;
+
+    assert (src_pattern == NULL);
+    assert (op == CAIRO_OPERATOR_ADD);
+    assert (dst->base.is_clear);
+
+    status = _cairo_clip_get_polygon (clip, &polygon,
+                                     &fill_rule, &info.antialias);
+    if (unlikely (status))
+       return status;
+
+    _cairo_traps_init (&info.traps);
+    status = _cairo_bentley_ottmann_tessellate_polygon (&info.traps,
+                                                       &polygon,
+                                                       fill_rule);
+    _cairo_polygon_fini (&polygon);
+    if (unlikely (status))
+       return status;
+
+    if (info.traps.has_intersections) {
+       if (info.traps.is_rectangular)
+           status = _cairo_bentley_ottmann_tessellate_rectangular_traps (&info.traps, CAIRO_FILL_RULE_WINDING);
+       else if (info.traps.is_rectilinear)
+           status = _cairo_bentley_ottmann_tessellate_rectilinear_traps (&info.traps, CAIRO_FILL_RULE_WINDING);
+       else
+           status = _cairo_bentley_ottmann_tessellate_traps (&info.traps, CAIRO_FILL_RULE_WINDING);
+       if (unlikely (status)) {
+           _cairo_traps_fini (&info.traps);
+           return status;
+       }
+    }
+
+    dst->deferred_clear = FALSE; /* assert(trap extents == extents); */
+
+    status = _composite_traps (&info,
+                              dst, CAIRO_OPERATOR_SOURCE, mask_pattern,
+                              dst_x, dst_y,
+                              extents, NULL);
+    _cairo_traps_fini (&info.traps);
+
+    return status;
+}
+
+struct composite_opacity_info {
+    uint8_t op;
+    cairo_xcb_surface_t *dst;
+    cairo_xcb_picture_t *src;
+    double opacity;
+};
+
+static void composite_opacity(void *closure,
+                             int16_t x, int16_t y,
+                             int16_t w, int16_t h,
+                             uint16_t coverage)
+{
+    struct composite_opacity_info *info = closure;
+    cairo_xcb_picture_t *mask;
+    cairo_color_t color;
+
+    color.red_short = color.green_short = color.blue_short = 0;
+    color.alpha_short = info->opacity * coverage;
+
+    mask = _solid_picture (info->dst, &color);
+    if (likely (mask->base.status == CAIRO_STATUS_SUCCESS)) {
+       if (info->src) {
+           _cairo_xcb_connection_render_composite (info->dst->connection,
+                                                   info->op,
+                                                   info->src->picture,
+                                                   mask->picture,
+                                                   info->dst->picture,
+                                                   x + info->src->x,  y + info->src->y,
+                                                   0,                 0,
+                                                   x,                 y,
+                                                   w,                 h);
+       } else {
+           _cairo_xcb_connection_render_composite (info->dst->connection,
+                                                   info->op,
+                                                   mask->picture,
+                                                   XCB_NONE,
+                                                   info->dst->picture,
+                                                   0,                 0,
+                                                   0,                 0,
+                                                   x,                 y,
+                                                   w,                 h);
+       }
+    }
+
+    cairo_surface_destroy (&mask->base);
+}
+
+static cairo_int_status_t
+_composite_opacity_boxes (void                         *closure,
+                         cairo_xcb_surface_t           *dst,
+                         cairo_operator_t               op,
+                         const cairo_pattern_t         *src_pattern,
+                         int                            dst_x,
+                         int                            dst_y,
+                         const cairo_rectangle_int_t   *extents,
+                         cairo_clip_t                  *clip)
+{
+    const cairo_solid_pattern_t *mask_pattern = closure;
+    struct composite_opacity_info info;
+    cairo_status_t status;
+    int i;
+
+    if (dst->base.is_clear) {
+       if (op == CAIRO_OPERATOR_OVER || op == CAIRO_OPERATOR_ADD)
+           op = CAIRO_OPERATOR_SOURCE;
+    }
+
+    if (op == CAIRO_OPERATOR_SOURCE &&
+       (clip == NULL ||
+        (clip->extents.width >= extents->width &&
+         clip->extents.height >= extents->height)))
+       dst->deferred_clear = FALSE;
+
+    if (dst->deferred_clear) {
+       status = _cairo_xcb_surface_clear (dst);
+       if (unlikely (status))
+           return status;
+    }
+
+    info.op = _render_operator (op);
+    info.dst = dst;
+
+    if (src_pattern != NULL) {
+       info.src = _cairo_xcb_picture_for_pattern (dst, src_pattern, extents);
+       if (unlikely (info.src->base.status))
+           return info.src->base.status;
+    } else
+       info.src = NULL;
+
+    info.opacity = mask_pattern->color.alpha;
+
+    /* XXX for lots of boxes create a clip region for the fully opaque areas */
+    if (clip) {
+       for (i = 0; i < clip->num_boxes; i++)
+           do_unaligned_box(composite_opacity, &info,
+                            &clip->boxes[i], dst_x, dst_y);
+    } else {
+       composite_opacity(&info,
+                         extents->x - dst_x,
+                         extents->y - dst_y,
+                         extents->width,
+                         extents->height,
+                         0xffff);
+    }
+    cairo_surface_destroy (&info.src->base);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+/* high level rasteriser -> compositor */
+
+cairo_int_status_t
+_cairo_xcb_surface_render_paint (cairo_xcb_surface_t   *surface,
+                                cairo_operator_t        op,
+                                const cairo_pattern_t  *source,
+                                cairo_composite_rectangles_t *composite)
+{
+    cairo_boxes_t boxes;
+    cairo_status_t status;
+
+    if (unlikely (! _operator_is_supported (surface->connection->flags, op)))
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    if ((surface->connection->flags & (CAIRO_XCB_RENDER_HAS_COMPOSITE_TRAPEZOIDS |
+                                      CAIRO_XCB_RENDER_HAS_COMPOSITE)) == 0)
+    {
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+    }
+
+    if (composite->clip == NULL &&
+       source->type == CAIRO_PATTERN_TYPE_SOLID &&
+       (op == CAIRO_OPERATOR_SOURCE ||
+        op == CAIRO_OPERATOR_CLEAR ||
+        (surface->base.is_clear &&
+         (op == CAIRO_OPERATOR_ADD || op == CAIRO_OPERATOR_OVER))))
+    {
+       surface->deferred_clear = TRUE;
+       surface->deferred_clear_color = composite->source_pattern.solid.color;
+       return CAIRO_STATUS_SUCCESS;
+    }
+
+     _cairo_clip_steal_boxes(composite->clip, &boxes);
+     status = _clip_and_composite_boxes (surface, op, source, &boxes, composite);
+     _cairo_clip_unsteal_boxes (composite->clip, &boxes);
+
+    return status;
+}
+
+cairo_int_status_t
+_cairo_xcb_surface_render_mask (cairo_xcb_surface_t    *surface,
+                               cairo_operator_t         op,
+                               const cairo_pattern_t   *source,
+                               const cairo_pattern_t   *mask,
+                               cairo_composite_rectangles_t *composite)
+{
+    cairo_status_t status;
+
+    if (unlikely (! _operator_is_supported (surface->connection->flags, op)))
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    if ((surface->connection->flags & CAIRO_XCB_RENDER_HAS_COMPOSITE) == 0)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    if (mask->type == CAIRO_PATTERN_TYPE_SOLID &&
+       composite->clip->path == NULL &&
+       ! _cairo_clip_is_region (composite->clip)) {
+       status = _clip_and_composite (surface, op, source,
+                                     _composite_opacity_boxes,
+                                     _composite_opacity_boxes,
+                                     (void *) mask,
+                                     composite, need_unbounded_clip (composite));
+    } else {
+       xcb_draw_func_t mask_func = NULL;
+       if (surface->connection->flags & CAIRO_XCB_RENDER_HAS_COMPOSITE_TRAPEZOIDS)
+           mask_func = composite->clip->path ? _composite_mask_clip : _composite_mask_clip_boxes;
+       status = _clip_and_composite (surface, op, source,
+                                     _composite_mask, mask_func,
+                                     (void *) mask,
+                                     composite, need_bounded_clip (composite));
+    }
+
+    return status;
+}
+
+static cairo_int_status_t
+_cairo_xcb_surface_render_stroke_as_polygon (cairo_xcb_surface_t       *dst,
+                                            cairo_operator_t            op,
+                                            const cairo_pattern_t      *source,
+                                            const cairo_path_fixed_t           *path,
+                                            const cairo_stroke_style_t *stroke_style,
+                                            const cairo_matrix_t       *ctm,
+                                            const cairo_matrix_t       *ctm_inverse,
+                                            double                      tolerance,
+                                            cairo_antialias_t           antialias,
+                                            cairo_composite_rectangles_t *extents)
+{
+    cairo_polygon_t polygon;
+    cairo_status_t status;
+
+    _cairo_polygon_init_with_clip (&polygon, extents->clip);
+    status = _cairo_path_fixed_stroke_to_polygon (path,
+                                                 stroke_style,
+                                                 ctm, ctm_inverse,
+                                                 tolerance,
+                                                 &polygon);
+    if (likely (status == CAIRO_STATUS_SUCCESS)) {
+       status = _composite_polygon (dst, op, source,
+                                    &polygon, antialias,
+                                    CAIRO_FILL_RULE_WINDING,
+                                    extents);
+    }
+    _cairo_polygon_fini (&polygon);
+
+    return status;
+}
+
+static void
+_clear_image (cairo_surface_t *surface)
+{
+    cairo_image_surface_t *image = (cairo_image_surface_t *) surface;
+    memset (image->data, 0, image->stride * image->height);
+    surface->is_clear = TRUE;
+}
+
+static cairo_status_t
+_cairo_xcb_surface_render_stroke_via_mask (cairo_xcb_surface_t         *dst,
+                                          cairo_operator_t              op,
+                                          const cairo_pattern_t        *source,
+                                          const cairo_path_fixed_t             *path,
+                                          const cairo_stroke_style_t   *stroke_style,
+                                          const cairo_matrix_t         *ctm,
+                                          const cairo_matrix_t         *ctm_inverse,
+                                          double                        tolerance,
+                                          cairo_antialias_t             antialias,
+                                          cairo_composite_rectangles_t *extents)
+{
+    cairo_surface_t *image;
+    cairo_status_t status;
+    cairo_clip_t *clip;
+    int x, y;
+
+    x = extents->bounded.x;
+    y = extents->bounded.y;
+    image = _cairo_xcb_surface_create_similar_image (dst, CAIRO_CONTENT_ALPHA,
+                                                    extents->bounded.width,
+                                                    extents->bounded.height);
+    if (unlikely (image->status))
+       return image->status;
+
+    _clear_image (image);
+
+    clip = _cairo_clip_copy_region (extents->clip);
+    status = _cairo_surface_offset_stroke (image, x, y,
+                                          CAIRO_OPERATOR_ADD,
+                                          &_cairo_pattern_white.base,
+                                          path, stroke_style,
+                                          ctm, ctm_inverse,
+                                          tolerance, antialias,
+                                          clip);
+    _cairo_clip_destroy (clip);
+    if (likely (status == CAIRO_STATUS_SUCCESS)) {
+       cairo_surface_pattern_t mask;
+
+       _cairo_pattern_init_for_surface (&mask, image);
+       mask.base.filter = CAIRO_FILTER_NEAREST;
+
+       cairo_matrix_init_translate (&mask.base.matrix, -x, -y);
+       status = _clip_and_composite (dst, op, source,
+                                     _composite_mask, NULL, &mask.base,
+                                     extents, need_bounded_clip (extents));
+       _cairo_pattern_fini (&mask.base);
+    }
+
+    cairo_surface_finish (image);
+    cairo_surface_destroy (image);
+
+    return status;
+}
+
+cairo_int_status_t
+_cairo_xcb_surface_render_stroke (cairo_xcb_surface_t  *surface,
+                                 cairo_operator_t       op,
+                                 const cairo_pattern_t *source,
+                                 const cairo_path_fixed_t      *path,
+                                 const cairo_stroke_style_t    *style,
+                                 const cairo_matrix_t  *ctm,
+                                 const cairo_matrix_t  *ctm_inverse,
+                                 double                 tolerance,
+                                 cairo_antialias_t      antialias,
+                                 cairo_composite_rectangles_t *composite)
+{
+    cairo_int_status_t status;
+
+    if (unlikely (! _operator_is_supported (surface->connection->flags, op)))
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    if ((surface->connection->flags & (CAIRO_XCB_RENDER_HAS_COMPOSITE_TRAPEZOIDS |
+                          CAIRO_XCB_RENDER_HAS_COMPOSITE)) == 0)
+    {
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+    }
+
+    status = CAIRO_INT_STATUS_UNSUPPORTED;
+    if (_cairo_path_fixed_stroke_is_rectilinear (path)) {
+       cairo_boxes_t boxes;
+
+       _cairo_boxes_init_with_clip (&boxes, composite->clip);
+       status = _cairo_path_fixed_stroke_rectilinear_to_boxes (path,
+                                                               style,
+                                                               ctm,
+                                                               antialias,
+                                                               &boxes);
+       if (likely (status == CAIRO_INT_STATUS_SUCCESS)) {
+           status = _clip_and_composite_boxes (surface, op, source,
+                                               &boxes, composite);
+       }
+       _cairo_boxes_fini (&boxes);
+    }
+
+    if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
+       if (surface->connection->flags & CAIRO_XCB_RENDER_HAS_COMPOSITE_TRAPEZOIDS) {
+           status = _cairo_xcb_surface_render_stroke_as_polygon (surface, op, source,
+                                                                 path, style,
+                                                                 ctm, ctm_inverse,
+                                                                 tolerance, antialias,
+                                                                 composite);
+       } else if (surface->connection->flags & CAIRO_XCB_RENDER_HAS_COMPOSITE) {
+           status = _cairo_xcb_surface_render_stroke_via_mask (surface, op, source,
+                                                               path, style,
+                                                               ctm, ctm_inverse,
+                                                               tolerance, antialias,
+                                                               composite);
+       } else {
+           ASSERT_NOT_REACHED;
+       }
+    }
+
+    return status;
+}
+
+static cairo_status_t
+_cairo_xcb_surface_render_fill_as_polygon (cairo_xcb_surface_t *dst,
+                                          cairo_operator_t      op,
+                                          const cairo_pattern_t*source,
+                                          const cairo_path_fixed_t     *path,
+                                          cairo_fill_rule_t     fill_rule,
+                                          double                tolerance,
+                                          cairo_antialias_t     antialias,
+                                          cairo_composite_rectangles_t *extents)
+{
+    cairo_polygon_t polygon;
+    cairo_status_t status;
+
+    _cairo_polygon_init_with_clip (&polygon, extents->clip);
+    status = _cairo_path_fixed_fill_to_polygon (path, tolerance, &polygon);
+    if (likely (status == CAIRO_STATUS_SUCCESS)) {
+       status = _composite_polygon (dst, op, source,
+                                    &polygon,
+                                    antialias,
+                                    fill_rule,
+                                    extents);
+    }
+    _cairo_polygon_fini (&polygon);
+
+    return status;
+}
+
+static cairo_status_t
+_cairo_xcb_surface_render_fill_via_mask (cairo_xcb_surface_t   *dst,
+                                        cairo_operator_t        op,
+                                        const cairo_pattern_t  *source,
+                                        const cairo_path_fixed_t       *path,
+                                        cairo_fill_rule_t       fill_rule,
+                                        double                  tolerance,
+                                        cairo_antialias_t       antialias,
+                                        cairo_composite_rectangles_t *extents)
+{
+    cairo_surface_t *image;
+    cairo_status_t status;
+    cairo_clip_t *clip;
+    int x, y;
+
+    x = extents->bounded.x;
+    y = extents->bounded.y;
+    image = _cairo_xcb_surface_create_similar_image (dst,
+                                                    CAIRO_CONTENT_ALPHA,
+                                                    extents->bounded.width,
+                                                    extents->bounded.height);
+    if (unlikely (image->status))
+       return image->status;
+
+    _clear_image (image);
+
+    clip = _cairo_clip_copy_region (extents->clip);
+    status = _cairo_surface_offset_fill (image, x, y,
+                                        CAIRO_OPERATOR_ADD,
+                                        &_cairo_pattern_white.base,
+                                        path, fill_rule, tolerance, antialias,
+                                        clip);
+    _cairo_clip_destroy (clip);
+    if (likely (status == CAIRO_STATUS_SUCCESS)) {
+       cairo_surface_pattern_t mask;
+
+       _cairo_pattern_init_for_surface (&mask, image);
+       mask.base.filter = CAIRO_FILTER_NEAREST;
+
+       cairo_matrix_init_translate (&mask.base.matrix, -x, -y);
+       status = _clip_and_composite (dst, op, source,
+                                     _composite_mask, NULL, &mask.base,
+                                     extents, need_bounded_clip (extents));
+
+       _cairo_pattern_fini (&mask.base);
+    }
+
+    cairo_surface_finish (image);
+    cairo_surface_destroy (image);
+
+    return status;
+}
+
+cairo_int_status_t
+_cairo_xcb_surface_render_fill (cairo_xcb_surface_t    *surface,
+                              cairo_operator_t          op,
+                              const cairo_pattern_t    *source,
+                              const cairo_path_fixed_t *path,
+                              cairo_fill_rule_t         fill_rule,
+                              double                    tolerance,
+                              cairo_antialias_t         antialias,
+                              cairo_composite_rectangles_t *composite)
+{
+    cairo_int_status_t status;
+
+    if (unlikely (! _operator_is_supported (surface->connection->flags, op)))
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    if ((surface->connection->flags & (CAIRO_XCB_RENDER_HAS_COMPOSITE_TRAPEZOIDS |
+                                      CAIRO_XCB_RENDER_HAS_COMPOSITE)) == 0)
+    {
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+    }
+
+    status = CAIRO_INT_STATUS_UNSUPPORTED;
+    if (_cairo_path_fixed_fill_is_rectilinear (path)) {
+       cairo_boxes_t boxes;
+
+       _cairo_boxes_init_with_clip (&boxes, composite->clip);
+       status = _cairo_path_fixed_fill_rectilinear_to_boxes (path,
+                                                             fill_rule,
+                                                             antialias,
+                                                             &boxes);
+       if (likely (status == CAIRO_INT_STATUS_SUCCESS)) {
+           status = _clip_and_composite_boxes (surface, op, source,
+                                               &boxes, composite);
+       }
+       _cairo_boxes_fini (&boxes);
+    }
+
+    if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
+       if (surface->connection->flags & CAIRO_XCB_RENDER_HAS_COMPOSITE_TRAPEZOIDS) {
+           status = _cairo_xcb_surface_render_fill_as_polygon (surface, op, source, path,
+                                                               fill_rule, tolerance, antialias,
+                                                               composite);
+       } else if (surface->connection->flags & CAIRO_XCB_RENDER_HAS_COMPOSITE) {
+           status = _cairo_xcb_surface_render_fill_via_mask (surface, op, source, path,
+                                                             fill_rule, tolerance, antialias,
+                                                             composite);
+       } else {
+           ASSERT_NOT_REACHED;
+       }
+    }
+
+    return status;
+}
+
+static cairo_status_t
+_cairo_xcb_surface_render_glyphs_via_mask (cairo_xcb_surface_t         *dst,
+                                          cairo_operator_t              op,
+                                          const cairo_pattern_t        *source,
+                                          cairo_scaled_font_t          *scaled_font,
+                                          cairo_glyph_t                *glyphs,
+                                          int                           num_glyphs,
+                                          cairo_composite_rectangles_t *extents)
+{
+    cairo_surface_t *image;
+    cairo_content_t content;
+    cairo_status_t status;
+    cairo_clip_t *clip;
+    int x, y;
+
+    content = CAIRO_CONTENT_ALPHA;
+    if (scaled_font->options.antialias == CAIRO_ANTIALIAS_SUBPIXEL)
+       content = CAIRO_CONTENT_COLOR_ALPHA;
+
+    x = extents->bounded.x;
+    y = extents->bounded.y;
+    image = _cairo_xcb_surface_create_similar_image (dst, content,
+                                                    extents->bounded.width,
+                                                    extents->bounded.height);
+    if (unlikely (image->status))
+       return image->status;
+
+    _clear_image (image);
+
+    clip = _cairo_clip_copy_region (extents->clip);
+    status = _cairo_surface_offset_glyphs (image, x, y,
+                                          CAIRO_OPERATOR_ADD,
+                                          &_cairo_pattern_white.base,
+                                          scaled_font, glyphs, num_glyphs,
+                                          clip);
+    _cairo_clip_destroy (clip);
+    if (likely (status == CAIRO_STATUS_SUCCESS)) {
+       cairo_surface_pattern_t mask;
+
+       _cairo_pattern_init_for_surface (&mask, image);
+       mask.base.filter = CAIRO_FILTER_NEAREST;
+       if (content & CAIRO_CONTENT_COLOR)
+           mask.base.has_component_alpha = TRUE;
+
+       cairo_matrix_init_translate (&mask.base.matrix, -x, -y);
+       status = _clip_and_composite (dst, op, source,
+                                     _composite_mask, NULL, &mask.base,
+                                     extents, need_bounded_clip (extents));
+
+       _cairo_pattern_fini (&mask.base);
+    }
+
+    cairo_surface_finish (image);
+    cairo_surface_destroy (image);
+
+    return status;
+}
+
+/* Build a struct of the same size of #cairo_glyph_t that can be used both as
+ * an input glyph with double coordinates, and as "working" glyph with
+ * integer from-current-point offsets. */
+typedef union {
+    cairo_glyph_t d;
+    unsigned long index;
+    struct {
+        unsigned long index;
+        int x;
+        int y;
+    } i;
+} cairo_xcb_glyph_t;
+
+/* compile-time assert that #cairo_xcb_glyph_t is the same size as #cairo_glyph_t */
+COMPILE_TIME_ASSERT (sizeof (cairo_xcb_glyph_t) == sizeof (cairo_glyph_t));
+
+typedef struct {
+    cairo_scaled_font_t *font;
+    cairo_xcb_glyph_t *glyphs;
+    int num_glyphs;
+    cairo_bool_t use_mask;
+} composite_glyphs_info_t;
+
+static cairo_status_t
+_can_composite_glyphs (cairo_xcb_surface_t *dst,
+                      cairo_rectangle_int_t *extents,
+                      cairo_scaled_font_t *scaled_font,
+                      cairo_glyph_t *glyphs,
+                      int *num_glyphs)
+{
+#define GLYPH_CACHE_SIZE 64
+    cairo_box_t bbox_cache[GLYPH_CACHE_SIZE];
+    unsigned long glyph_cache[GLYPH_CACHE_SIZE];
+#undef GLYPH_CACHE_SIZE
+    cairo_status_t status = CAIRO_STATUS_SUCCESS;
+    cairo_glyph_t *glyphs_end, *valid_glyphs;
+    const int max_glyph_size = dst->connection->maximum_request_length - 64;
+
+    /* We must initialize the cache with values that cannot match the
+     * "hash" to guarantee that when compared for the first time they
+     * will result in a mismatch. The hash function is simply modulus,
+     * so we cannot use 0 in glyph_cache[0], but we can use it in all
+     * other array cells.
+     */
+    memset (glyph_cache, 0, sizeof (glyph_cache));
+    glyph_cache[0] = 1;
+
+    /* Scan for oversized glyphs or glyphs outside the representable
+     * range and fallback in that case, discard glyphs outside of the
+     * image.
+     */
+    valid_glyphs = glyphs;
+    for (glyphs_end = glyphs + *num_glyphs; glyphs != glyphs_end; glyphs++) {
+       double x1, y1, x2, y2;
+       cairo_scaled_glyph_t *glyph;
+       cairo_box_t *bbox;
+       int width, height, len;
+       int g;
+
+       g = glyphs->index % ARRAY_LENGTH (glyph_cache);
+       if (glyph_cache[g] != glyphs->index) {
+           status = _cairo_scaled_glyph_lookup (scaled_font,
+                                                glyphs->index,
+                                                CAIRO_SCALED_GLYPH_INFO_METRICS,
+                                                &glyph);
+           if (unlikely (status))
+               break;
+
+           glyph_cache[g] = glyphs->index;
+           bbox_cache[g] = glyph->bbox;
+       }
+       bbox = &bbox_cache[g];
+
+       /* Drop glyphs outside the clipping */
+       x1 = _cairo_fixed_to_double (bbox->p1.x);
+       y1 = _cairo_fixed_to_double (bbox->p1.y);
+       y2 = _cairo_fixed_to_double (bbox->p2.y);
+       x2 = _cairo_fixed_to_double (bbox->p2.x);
+       if (unlikely (glyphs->x + x2 <= extents->x ||
+                     glyphs->y + y2 <= extents->y ||
+                     glyphs->x + x1 >= extents->x + extents->width ||
+                     glyphs->y + y1 >= extents->y + extents->height))
+       {
+           (*num_glyphs)--;
+           continue;
+       }
+
+       /* XRenderAddGlyph does not handle a glyph surface larger than
+        * the extended maximum XRequest size.
+        */
+       width  = _cairo_fixed_integer_ceil (bbox->p2.x - bbox->p1.x);
+       height = _cairo_fixed_integer_ceil (bbox->p2.y - bbox->p1.y);
+       len = CAIRO_STRIDE_FOR_WIDTH_BPP (width, 32) * height;
+       if (unlikely (len >= max_glyph_size)) {
+           status = CAIRO_INT_STATUS_UNSUPPORTED;
+           break;
+       }
+
+       /* The glyph coordinates must be representable in an int16_t.
+        * When possible, they will be expressed as an offset from the
+        * previous glyph, otherwise they will be an offset from the
+        * operation extents or from the surface origin. If the last
+        * two options are not valid, fallback.
+        */
+       if (unlikely (glyphs->x > INT16_MAX ||
+                     glyphs->y > INT16_MAX ||
+                     glyphs->x - extents->x < INT16_MIN ||
+                     glyphs->y - extents->y < INT16_MIN))
+       {
+           status = CAIRO_INT_STATUS_UNSUPPORTED;
+           break;
+       }
+
+
+       if (unlikely (valid_glyphs != glyphs))
+           *valid_glyphs = *glyphs;
+       valid_glyphs++;
+    }
+
+    if (unlikely (valid_glyphs != glyphs)) {
+       for (; glyphs != glyphs_end; glyphs++) {
+           *valid_glyphs = *glyphs;
+           valid_glyphs++;
+       }
+    }
+
+    return status;
+}
+
+/* Start a new element for the first glyph,
+ * or for any glyph that has unexpected position,
+ * or if current element has too many glyphs
+ * (Xrender limits each element to 252 glyphs, we limit them to 128)
+ *
+ * These same conditions need to be mirrored between
+ * _cairo_xcb_surface_emit_glyphs and _emit_glyph_chunks
+ */
+#define _start_new_glyph_elt(count, glyph) \
+    (((count) & 127) == 0 || (glyph)->i.x || (glyph)->i.y)
+
+/* sz_xGlyphtElt required alignment to a 32-bit boundary, so ensure we have
+ * enough room for padding */
+typedef struct {
+    uint8_t   len;
+    uint8_t   pad1;
+    uint16_t  pad2;
+    int16_t   deltax;
+    int16_t   deltay;
+} x_glyph_elt_t;
+#define _cairo_sz_x_glyph_elt_t (sizeof (x_glyph_elt_t) + 4)
+
+static void
+_cairo_xcb_font_destroy (cairo_xcb_font_t *font)
+{
+    int i;
+
+    for (i = 0; i < NUM_GLYPHSETS; i++) {
+       cairo_xcb_font_glyphset_info_t *info;
+
+       info = &font->glyphset_info[i];
+       free (info->pending_free_glyphs);
+    }
+
+    cairo_list_del (&font->base.link);
+    cairo_list_del (&font->link);
+
+    _cairo_xcb_connection_destroy (font->connection);
+
+    free (font);
+}
+
+static void
+_cairo_xcb_font_fini (cairo_scaled_font_private_t *abstract_private,
+                     cairo_scaled_font_t *scaled_font)
+{
+    cairo_xcb_font_t *font_private = (cairo_xcb_font_t *)abstract_private;
+    cairo_xcb_connection_t *connection;
+    cairo_bool_t have_connection;
+    cairo_status_t status;
+    int i;
+
+    connection = font_private->connection;
+
+    status = _cairo_xcb_connection_acquire (connection);
+    have_connection = status == CAIRO_STATUS_SUCCESS;
+
+    for (i = 0; i < NUM_GLYPHSETS; i++) {
+       cairo_xcb_font_glyphset_info_t *info;
+
+       info = &font_private->glyphset_info[i];
+       if (info->glyphset && status == CAIRO_STATUS_SUCCESS) {
+           _cairo_xcb_connection_render_free_glyph_set (connection,
+                                                        info->glyphset);
+       }
+    }
+
+    if (have_connection)
+       _cairo_xcb_connection_release (connection);
+
+    _cairo_xcb_font_destroy (font_private);
+}
+
+
+static cairo_xcb_font_t *
+_cairo_xcb_font_create (cairo_xcb_connection_t *connection,
+                       cairo_scaled_font_t  *font)
+{
+    cairo_xcb_font_t   *priv;
+    int i;
+
+    priv = malloc (sizeof (cairo_xcb_font_t));
+    if (unlikely (priv == NULL))
+       return NULL;
+
+    _cairo_scaled_font_attach_private (font, &priv->base, connection,
+                                      _cairo_xcb_font_fini);
+
+    priv->scaled_font = font;
+    priv->connection = _cairo_xcb_connection_reference (connection);
+    cairo_list_add (&priv->link, &connection->fonts);
+
+    for (i = 0; i < NUM_GLYPHSETS; i++) {
+       cairo_xcb_font_glyphset_info_t *info = &priv->glyphset_info[i];
+       switch (i) {
+       case GLYPHSET_INDEX_ARGB32: info->format = CAIRO_FORMAT_ARGB32; break;
+       case GLYPHSET_INDEX_A8:     info->format = CAIRO_FORMAT_A8;     break;
+       case GLYPHSET_INDEX_A1:     info->format = CAIRO_FORMAT_A1;     break;
+       default:                    ASSERT_NOT_REACHED;                          break;
+       }
+       info->xrender_format = 0;
+       info->glyphset = XCB_NONE;
+       info->pending_free_glyphs = NULL;
+    }
+
+    return priv;
+}
+
+void
+_cairo_xcb_font_close (cairo_xcb_font_t *font)
+{
+    cairo_scaled_font_t        *scaled_font;
+
+    scaled_font = font->scaled_font;
+
+    CAIRO_MUTEX_LOCK (scaled_font->mutex);
+    //scaled_font->surface_private = NULL;
+    _cairo_scaled_font_reset_cache (scaled_font);
+    CAIRO_MUTEX_UNLOCK (scaled_font->mutex);
+
+    _cairo_xcb_font_destroy (font);
+}
+
+static void
+_cairo_xcb_render_free_glyphs (cairo_xcb_connection_t *connection,
+                              cairo_xcb_font_glyphset_free_glyphs_t *to_free)
+{
+    _cairo_xcb_connection_render_free_glyphs (connection,
+                                             to_free->glyphset,
+                                             to_free->glyph_count,
+                                             to_free->glyph_indices);
+}
+
+static int
+_cairo_xcb_get_glyphset_index_for_format (cairo_format_t format)
+{
+    if (format == CAIRO_FORMAT_A8)
+        return GLYPHSET_INDEX_A8;
+    if (format == CAIRO_FORMAT_A1)
+        return GLYPHSET_INDEX_A1;
+
+    assert (format == CAIRO_FORMAT_ARGB32);
+    return GLYPHSET_INDEX_ARGB32;
+}
+
+
+
+static inline cairo_xcb_font_t *
+_cairo_xcb_font_get (const cairo_xcb_connection_t *c,
+                    cairo_scaled_font_t *font)
+{
+    return (cairo_xcb_font_t *)_cairo_scaled_font_find_private (font, c);
+}
+
+
+static cairo_xcb_font_glyphset_info_t *
+_cairo_xcb_scaled_font_get_glyphset_info_for_format (cairo_xcb_connection_t *c,
+                                                    cairo_scaled_font_t *font,
+                                                    cairo_format_t       format)
+{
+    cairo_xcb_font_t *priv;
+    cairo_xcb_font_glyphset_info_t *info;
+    int glyphset_index;
+
+    glyphset_index = _cairo_xcb_get_glyphset_index_for_format (format);
+
+    priv = _cairo_xcb_font_get (c, font);
+    if (priv == NULL) {
+       priv = _cairo_xcb_font_create (c, font);
+       if (priv == NULL)
+           return NULL;
+    }
+
+    info = &priv->glyphset_info[glyphset_index];
+    if (info->glyphset == XCB_NONE) {
+       info->glyphset = _cairo_xcb_connection_get_xid (c);
+       info->xrender_format = c->standard_formats[info->format];
+
+       _cairo_xcb_connection_render_create_glyph_set (c,
+                                                      info->glyphset,
+                                                      info->xrender_format);
+    }
+
+    return info;
+}
+
+static cairo_bool_t
+_cairo_xcb_glyphset_info_has_pending_free_glyph (
+                               cairo_xcb_font_glyphset_info_t *info,
+                               unsigned long glyph_index)
+{
+    if (info->pending_free_glyphs != NULL) {
+       cairo_xcb_font_glyphset_free_glyphs_t *to_free;
+       int i;
+
+       to_free = info->pending_free_glyphs;
+       for (i = 0; i < to_free->glyph_count; i++) {
+           if (to_free->glyph_indices[i] == glyph_index) {
+               to_free->glyph_count--;
+               memmove (&to_free->glyph_indices[i],
+                        &to_free->glyph_indices[i+1],
+                        (to_free->glyph_count - i) * sizeof (to_free->glyph_indices[0]));
+               return TRUE;
+           }
+       }
+    }
+
+    return FALSE;
+}
+
+typedef struct {
+    cairo_scaled_glyph_private_t base;
+
+    cairo_xcb_font_glyphset_info_t *glyphset;
+} cairo_xcb_glyph_private_t;
+
+static cairo_xcb_font_glyphset_info_t *
+_cairo_xcb_scaled_font_get_glyphset_info_for_pending_free_glyph (cairo_xcb_connection_t *c,
+                                              cairo_scaled_font_t *font,
+                                              unsigned long glyph_index,
+                                              cairo_image_surface_t *surface)
+{
+    cairo_xcb_font_t *priv;
+    int i;
+
+    priv = _cairo_xcb_font_get (c, font);
+    if (priv == NULL)
+       return NULL;
+
+    if (surface != NULL) {
+        i = _cairo_xcb_get_glyphset_index_for_format (surface->format);
+
+       if (_cairo_xcb_glyphset_info_has_pending_free_glyph (
+                                               &priv->glyphset_info[i],
+                                               glyph_index))
+       {
+           return &priv->glyphset_info[i];
+       }
+    } else {
+       for (i = 0; i < NUM_GLYPHSETS; i++) {
+           if (_cairo_xcb_glyphset_info_has_pending_free_glyph (
+                                               &priv->glyphset_info[i],
+                                               glyph_index))
+           {
+               return &priv->glyphset_info[i];
+           }
+       }
+    }
+
+    return NULL;
+}
+
+static void
+_cairo_xcb_glyph_fini (cairo_scaled_glyph_private_t *glyph_private,
+                      cairo_scaled_glyph_t *glyph,
+                      cairo_scaled_font_t  *font)
+{
+    cairo_xcb_glyph_private_t *priv = (cairo_xcb_glyph_private_t *)glyph_private;
+
+    if (! font->finished) {
+       cairo_xcb_font_glyphset_info_t *info = priv->glyphset;
+       cairo_xcb_font_glyphset_free_glyphs_t *to_free;
+       cairo_xcb_font_t *font_private;
+
+       font_private = _cairo_xcb_font_get (glyph_private->key, font);
+       assert (font_private);
+
+       to_free = info->pending_free_glyphs;
+       if (to_free != NULL &&
+           to_free->glyph_count == ARRAY_LENGTH (to_free->glyph_indices))
+       {
+           _cairo_xcb_render_free_glyphs (font_private->connection, to_free);
+           to_free = info->pending_free_glyphs = NULL;
+       }
+
+       if (to_free == NULL) {
+           to_free = malloc (sizeof (cairo_xcb_font_glyphset_free_glyphs_t));
+           if (unlikely (to_free == NULL)) {
+               _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+               return; /* XXX cannot propagate failure */
+           }
+
+           to_free->glyphset = info->glyphset;
+           to_free->glyph_count = 0;
+           info->pending_free_glyphs = to_free;
+       }
+
+       to_free->glyph_indices[to_free->glyph_count++] =
+           _cairo_scaled_glyph_index (glyph);
+    }
+
+    cairo_list_del (&glyph_private->link);
+    free (glyph_private);
+}
+
+
+static cairo_status_t
+_cairo_xcb_glyph_attach (cairo_xcb_connection_t  *c,
+                        cairo_scaled_glyph_t  *glyph,
+                        cairo_xcb_font_glyphset_info_t *info)
+{
+    cairo_xcb_glyph_private_t *priv;
+
+    priv = malloc (sizeof (*priv));
+    if (unlikely (priv == NULL))
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    _cairo_scaled_glyph_attach_private (glyph, &priv->base, c,
+                                       _cairo_xcb_glyph_fini);
+    priv->glyphset = info;
+
+    glyph->dev_private = info;
+    glyph->dev_private_key = c;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_xcb_surface_add_glyph (cairo_xcb_connection_t *connection,
+                              cairo_scaled_font_t   *font,
+                              cairo_scaled_glyph_t **scaled_glyph_out)
+{
+    xcb_render_glyphinfo_t glyph_info;
+    uint32_t glyph_index;
+    uint8_t *data;
+    cairo_status_t status = CAIRO_STATUS_SUCCESS;
+    cairo_scaled_glyph_t *scaled_glyph = *scaled_glyph_out;
+    cairo_image_surface_t *glyph_surface = scaled_glyph->surface;
+    cairo_bool_t already_had_glyph_surface;
+    cairo_xcb_font_glyphset_info_t *info;
+
+    glyph_index = _cairo_scaled_glyph_index (scaled_glyph);
+
+    /* check to see if we have a pending XRenderFreeGlyph for this glyph */
+    info = _cairo_xcb_scaled_font_get_glyphset_info_for_pending_free_glyph (connection, font, glyph_index, glyph_surface);
+    if (info != NULL)
+       return _cairo_xcb_glyph_attach (connection, scaled_glyph, info);
+
+    if (glyph_surface == NULL) {
+       status = _cairo_scaled_glyph_lookup (font,
+                                            glyph_index,
+                                            CAIRO_SCALED_GLYPH_INFO_METRICS |
+                                            CAIRO_SCALED_GLYPH_INFO_SURFACE,
+                                            scaled_glyph_out);
+       if (unlikely (status))
+           return status;
+
+       scaled_glyph = *scaled_glyph_out;
+       glyph_surface = scaled_glyph->surface;
+       already_had_glyph_surface = FALSE;
+    } else {
+       already_had_glyph_surface = TRUE;
+    }
+
+    info = _cairo_xcb_scaled_font_get_glyphset_info_for_format (connection,
+                                                               font,
+                                                               glyph_surface->format);
+    if (unlikely (info == NULL)) {
+       status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+       goto BAIL;
+    }
+
+#if 0
+    /* If the glyph surface has zero height or width, we create
+     * a clear 1x1 surface, to avoid various X server bugs.
+     */
+    if (glyph_surface->width == 0 || glyph_surface->height == 0) {
+       cairo_surface_t *tmp_surface;
+
+       tmp_surface = cairo_image_surface_create (info->format, 1, 1);
+       status = tmp_surface->status;
+       if (unlikely (status))
+           goto BAIL;
+
+       tmp_surface->device_transform = glyph_surface->base.device_transform;
+       tmp_surface->device_transform_inverse = glyph_surface->base.device_transform_inverse;
+
+       glyph_surface = (cairo_image_surface_t *) tmp_surface;
+    }
+#endif
+
+    /* If the glyph format does not match the font format, then we
+     * create a temporary surface for the glyph image with the font's
+     * format.
+     */
+    if (glyph_surface->format != info->format) {
+       glyph_surface = _cairo_image_surface_coerce_to_format (glyph_surface,
+                                                              info->format);
+       status = glyph_surface->base.status;
+       if (unlikely (status))
+           goto BAIL;
+    }
+
+    /* XXX: FRAGILE: We're ignore device_transform scaling here. A bug? */
+    glyph_info.x = _cairo_lround (glyph_surface->base.device_transform.x0);
+    glyph_info.y = _cairo_lround (glyph_surface->base.device_transform.y0);
+    glyph_info.width  = glyph_surface->width;
+    glyph_info.height = glyph_surface->height;
+    glyph_info.x_off = scaled_glyph->x_advance;
+    glyph_info.y_off = scaled_glyph->y_advance;
+
+    data = glyph_surface->data;
+
+    /* flip formats around */
+    switch (_cairo_xcb_get_glyphset_index_for_format (scaled_glyph->surface->format)) {
+    case GLYPHSET_INDEX_A1:
+       /* local bitmaps are always stored with bit == byte */
+       if (_cairo_is_little_endian() != (connection->root->bitmap_format_bit_order == XCB_IMAGE_ORDER_LSB_FIRST)) {
+           int             c = glyph_surface->stride * glyph_surface->height;
+           const uint8_t *d;
+           uint8_t *new, *n;
+
+           new = malloc (c);
+           if (unlikely (new == NULL)) {
+               status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+               goto BAIL;
+           }
+
+           n = new;
+           d = data;
+           do {
+               uint8_t b = *d++;
+               b = ((b << 1) & 0xaa) | ((b >> 1) & 0x55);
+               b = ((b << 2) & 0xcc) | ((b >> 2) & 0x33);
+               b = ((b << 4) & 0xf0) | ((b >> 4) & 0x0f);
+               *n++ = b;
+           } while (--c);
+           data = new;
+       }
+       break;
+
+    case GLYPHSET_INDEX_A8:
+       break;
+
+    case GLYPHSET_INDEX_ARGB32:
+       if (_cairo_is_little_endian() != (connection->root->image_byte_order == XCB_IMAGE_ORDER_LSB_FIRST)) {
+           unsigned int c = glyph_surface->stride * glyph_surface->height / 4;
+           const uint32_t *d;
+           uint32_t *new, *n;
+
+           new = malloc (4 * c);
+           if (unlikely (new == NULL)) {
+               status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+               goto BAIL;
+           }
+
+           n = new;
+           d = (uint32_t *) data;
+           do {
+               *n++ = bswap_32 (*d);
+               d++;
+           } while (--c);
+           data = (uint8_t *) new;
+       }
+       break;
+
+    default:
+       ASSERT_NOT_REACHED;
+       break;
+    }
+    /* XXX assume X server wants pixman padding. Xft assumes this as well */
+
+    _cairo_xcb_connection_render_add_glyphs (connection,
+                                            info->glyphset,
+                                            1, &glyph_index, &glyph_info,
+                                            glyph_surface->stride * glyph_surface->height,
+                                            data);
+
+    if (data != glyph_surface->data)
+       free (data);
+
+    status = _cairo_xcb_glyph_attach (connection, scaled_glyph, info);
+
+ BAIL:
+    if (glyph_surface != scaled_glyph->surface)
+       cairo_surface_destroy (&glyph_surface->base);
+
+    /* If the scaled glyph didn't already have a surface attached
+     * to it, release the created surface now that we have it
+     * uploaded to the X server.  If the surface has already been
+     * there (e.g. because image backend requested it), leave it in
+     * the cache
+     */
+    if (! already_had_glyph_surface)
+       _cairo_scaled_glyph_set_surface (scaled_glyph, font, NULL);
+
+    return status;
+}
+
+typedef void (*cairo_xcb_render_composite_text_func_t)
+             (cairo_xcb_connection_t       *connection,
+              uint8_t                          op,
+              xcb_render_picture_t src,
+              xcb_render_picture_t dst,
+              xcb_render_pictformat_t mask_format,
+              xcb_render_glyphset_t glyphset,
+              int16_t                          src_x,
+              int16_t                          src_y,
+              uint32_t                          len,
+              uint8_t                        *cmd);
+
+
+static cairo_status_t
+_emit_glyphs_chunk (cairo_xcb_surface_t *dst,
+                   cairo_operator_t op,
+                   cairo_xcb_picture_t *src,
+                   /* info for this chunk */
+                   cairo_xcb_glyph_t *glyphs,
+                   int num_glyphs,
+                   int width,
+                   int estimated_req_size,
+                   cairo_xcb_font_glyphset_info_t *info,
+                   xcb_render_pictformat_t mask_format)
+{
+    cairo_xcb_render_composite_text_func_t composite_text_func;
+    uint8_t stack_buf[CAIRO_STACK_BUFFER_SIZE];
+    uint8_t *buf = stack_buf;
+    x_glyph_elt_t *elt = NULL; /* silence compiler */
+    uint32_t len;
+    int i;
+
+    if (estimated_req_size > ARRAY_LENGTH (stack_buf)) {
+       buf = malloc (estimated_req_size);
+       if (unlikely (buf == NULL))
+           return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+    }
+
+    len = 0;
+    for (i = 0; i < num_glyphs; i++) {
+      if (_start_new_glyph_elt (i, &glyphs[i])) {
+         if (len & 3)
+             len += 4 - (len & 3);
+
+         elt = (x_glyph_elt_t *) (buf + len);
+         elt->len = 0;
+         elt->deltax = glyphs[i].i.x;
+         elt->deltay = glyphs[i].i.y;
+         len += sizeof (x_glyph_elt_t);
+      }
+
+      switch (width) {
+      case 1: *(uint8_t *) (buf + len) = glyphs[i].index; break;
+      case 2: *(uint16_t *) (buf + len) = glyphs[i].index; break;
+      default:
+      case 4: *(uint32_t *) (buf + len) = glyphs[i].index; break;
+      }
+      len += width;
+      elt->len++;
+    }
+    if (len & 3)
+       len += 4 - (len & 3);
+
+    switch (width) {
+    case 1:
+       composite_text_func = _cairo_xcb_connection_render_composite_glyphs_8;
+       break;
+    case 2:
+       composite_text_func = _cairo_xcb_connection_render_composite_glyphs_16;
+       break;
+    default:
+    case 4:
+       composite_text_func = _cairo_xcb_connection_render_composite_glyphs_32;
+       break;
+    }
+    composite_text_func (dst->connection,
+                        _render_operator (op),
+                        src->picture,
+                        dst->picture,
+                        mask_format,
+                        info->glyphset,
+                        src->x + glyphs[0].i.x,
+                        src->y + glyphs[0].i.y,
+                        len, buf);
+
+    if (buf != stack_buf)
+      free (buf);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_composite_glyphs (void                                *closure,
+                 cairo_xcb_surface_t           *dst,
+                 cairo_operator_t               op,
+                 const cairo_pattern_t         *pattern,
+                 int                            dst_x,
+                 int                            dst_y,
+                 const cairo_rectangle_int_t   *extents,
+                 cairo_clip_t                  *clip)
+{
+    composite_glyphs_info_t *info = closure;
+    cairo_scaled_glyph_t *glyph_cache[64];
+    cairo_status_t status = CAIRO_STATUS_SUCCESS;
+    cairo_fixed_t x = 0, y = 0;
+    cairo_xcb_font_glyphset_info_t *glyphset_info = NULL, *this_glyphset_info;
+    const unsigned int max_request_size = dst->connection->maximum_request_length - 64;
+    cairo_xcb_picture_t *src;
+
+    unsigned long max_index = 0;
+    int width = 1;
+
+    unsigned int request_size = 0;
+    int i;
+
+    if (dst->deferred_clear) {
+       status = _cairo_xcb_surface_clear (dst);
+       if (unlikely (status))
+               return status;
+    }
+
+    src = _cairo_xcb_picture_for_pattern (dst, pattern, extents);
+    if (unlikely (src->base.status))
+       return src->base.status;
+
+    memset (glyph_cache, 0, sizeof (glyph_cache));
+
+    for (i = 0; i < info->num_glyphs; i++) {
+       cairo_scaled_glyph_t *glyph;
+       unsigned long glyph_index = info->glyphs[i].index;
+       int cache_index = glyph_index % ARRAY_LENGTH (glyph_cache);
+       int old_width = width;
+       int this_x, this_y;
+
+       glyph = glyph_cache[cache_index];
+       if (glyph == NULL ||
+           _cairo_scaled_glyph_index (glyph) != glyph_index)
+       {
+           status = _cairo_scaled_glyph_lookup (info->font,
+                                                glyph_index,
+                                                CAIRO_SCALED_GLYPH_INFO_METRICS,
+                                                &glyph);
+           if (unlikely (status)) {
+               cairo_surface_destroy (&src->base);
+               return status;
+           }
+
+           /* Send unseen glyphs to the server */
+           if (glyph->dev_private_key != dst->connection) {
+               status = _cairo_xcb_surface_add_glyph (dst->connection,
+                                                      info->font,
+                                                      &glyph);
+               if (unlikely (status)) {
+                   cairo_surface_destroy (&src->base);
+                   return status;
+               }
+           }
+
+           glyph_cache[cache_index] = glyph;
+       }
+
+       this_x = _cairo_lround (info->glyphs[i].d.x) - dst_x;
+       this_y = _cairo_lround (info->glyphs[i].d.y) - dst_y;
+
+       this_glyphset_info = glyph->dev_private;
+       if (glyphset_info == NULL)
+           glyphset_info = this_glyphset_info;
+
+       /* Update max glyph index */
+       if (glyph_index > max_index) {
+           max_index = glyph_index;
+           if (max_index >= 65536)
+               width = 4;
+           else if (max_index >= 256)
+               width = 2;
+           if (width != old_width)
+               request_size += (width - old_width) * i;
+       }
+
+       /* If we will pass the max request size by adding this glyph,
+        * flush current glyphs.  Note that we account for a
+        * possible element being added below.
+        *
+        * Also flush if changing glyphsets, as Xrender limits one mask
+        * format per request, so we can either break up, or use a
+        * wide-enough mask format.  We do the former.  One reason to
+        * prefer the latter is the fact that Xserver ADDs all glyphs
+        * to the mask first, and then composes that to final surface,
+        * though it's not a big deal.
+        *
+        * If the glyph has a coordinate which cannot be represented
+        * as a 16-bit offset from the previous glyph, flush the
+        * current chunk. The current glyph will be the first one in
+        * the next chunk, thus its coordinates will be an offset from
+        * the destination origin. This offset is guaranteed to be
+        * representable as 16-bit offset in _can_composite_glyphs().
+        */
+       if (request_size + width > max_request_size - _cairo_sz_x_glyph_elt_t ||
+           this_x - x > INT16_MAX || this_x - x < INT16_MIN ||
+           this_y - y > INT16_MAX || this_y - y < INT16_MIN ||
+           this_glyphset_info != glyphset_info)
+       {
+           status = _emit_glyphs_chunk (dst, op, src,
+                                        info->glyphs, i,
+                                        old_width, request_size,
+                                        glyphset_info,
+                                        info->use_mask ? glyphset_info->xrender_format : 0);
+           if (unlikely (status)) {
+               cairo_surface_destroy (&src->base);
+               return status;
+           }
+
+           info->glyphs += i;
+           info->num_glyphs -= i;
+           i = 0;
+
+           max_index = info->glyphs[0].index;
+           width = max_index < 256 ? 1 : max_index < 65536 ? 2 : 4;
+
+           request_size = 0;
+
+           x = y = 0;
+           glyphset_info = this_glyphset_info;
+       }
+
+       /* Convert absolute glyph position to relative-to-current-point
+        * position */
+       info->glyphs[i].i.x = this_x - x;
+       info->glyphs[i].i.y = this_y - y;
+
+       /* Start a new element for the first glyph,
+        * or for any glyph that has unexpected position,
+        * or if current element has too many glyphs.
+        *
+        * These same conditions are mirrored in _emit_glyphs_chunk().
+        */
+      if (_start_new_glyph_elt (i, &info->glyphs[i]))
+           request_size += _cairo_sz_x_glyph_elt_t;
+
+       /* adjust current-position */
+       x = this_x + glyph->x_advance;
+       y = this_y + glyph->y_advance;
+
+       request_size += width;
+    }
+
+    if (i) {
+       status = _emit_glyphs_chunk (dst, op, src,
+                                    info->glyphs, i,
+                                    width, request_size,
+                                    glyphset_info,
+                                    info->use_mask ? glyphset_info->xrender_format : 0);
+    }
+
+    cairo_surface_destroy (&src->base);
+
+    return status;
+}
+
+cairo_int_status_t
+_cairo_xcb_surface_render_glyphs (cairo_xcb_surface_t  *surface,
+                                 cairo_operator_t       op,
+                                 const cairo_pattern_t *source,
+                                 cairo_scaled_font_t   *scaled_font,
+                                 cairo_glyph_t         *glyphs,
+                                 int                    num_glyphs,
+                                 cairo_composite_rectangles_t *composite,
+                                 cairo_bool_t overlap)
+{
+    cairo_int_status_t status;
+
+    if (unlikely (! _operator_is_supported (surface->connection->flags, op)))
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    if ((surface->connection->flags & (CAIRO_XCB_RENDER_HAS_COMPOSITE_GLYPHS | CAIRO_XCB_RENDER_HAS_COMPOSITE)) == 0)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    status = CAIRO_INT_STATUS_UNSUPPORTED;
+    if (surface->connection->flags & CAIRO_XCB_RENDER_HAS_COMPOSITE_GLYPHS) {
+       _cairo_scaled_font_freeze_cache (scaled_font);
+
+       status = _can_composite_glyphs (surface, &composite->bounded,
+                                       scaled_font, glyphs, &num_glyphs);
+       if (likely (status == CAIRO_INT_STATUS_SUCCESS)) {
+           composite_glyphs_info_t info;
+           unsigned flags = 0;
+
+           info.font = scaled_font;
+           info.glyphs = (cairo_xcb_glyph_t *) glyphs;
+           info.num_glyphs = num_glyphs;
+           info.use_mask =
+               overlap ||
+               ! composite->is_bounded ||
+               ! _cairo_clip_is_region(composite->clip);
+
+           if (composite->mask.width  > composite->unbounded.width ||
+               composite->mask.height > composite->unbounded.height)
+           {
+               /* Glyphs are tricky since we do not directly control the
+                * geometry and their inked extents depend on the
+                * individual glyph-surface size. We must set a clip region
+                * so that the X server can trim the glyphs appropriately.
+                */
+               flags |= FORCE_CLIP_REGION;
+           }
+           status = _clip_and_composite (surface, op, source,
+                                         _composite_glyphs, NULL,
+                                         &info, composite,
+                                         need_bounded_clip (composite) |
+                                         flags);
+       }
+
+       _cairo_scaled_font_thaw_cache (scaled_font);
+    }
+
+    if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
+       assert (surface->connection->flags & CAIRO_XCB_RENDER_HAS_COMPOSITE);
+       status =
+           _cairo_xcb_surface_render_glyphs_via_mask (surface, op, source,
+                                                      scaled_font, glyphs, num_glyphs,
+                                                      composite);
+    }
+
+    return status;
+}
diff --git a/src/cairo-xcb-surface.c b/src/cairo-xcb-surface.c
new file mode 100644 (file)
index 0000000..75a1f87
--- /dev/null
@@ -0,0 +1,1430 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ * Copyright © 2009 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *     Behdad Esfahbod <behdad@behdad.org>
+ *     Carl D. Worth <cworth@cworth.org>
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ *     Karl Tomlinson <karlt+@karlt.net>, Mozilla Corporation
+ */
+
+#include "cairoint.h"
+
+#include "cairo-xcb.h"
+#include "cairo-xcb-private.h"
+
+#include "cairo-composite-rectangles-private.h"
+#include "cairo-default-context-private.h"
+#include "cairo-image-surface-private.h"
+#include "cairo-surface-backend-private.h"
+
+#if CAIRO_HAS_XLIB_XCB_FUNCTIONS
+slim_hidden_proto (cairo_xcb_surface_create);
+slim_hidden_proto (cairo_xcb_surface_create_for_bitmap);
+slim_hidden_proto (cairo_xcb_surface_create_with_xrender_format);
+#endif
+
+/**
+ * SECTION:cairo-xcb
+ * @Title: XCB Surfaces
+ * @Short_Description: X Window System rendering using the XCB library
+ * @See_Also: #cairo_surface_t
+ *
+ * The XCB surface is used to render cairo graphics to X Window System
+ * windows and pixmaps using the XCB library.
+ *
+ * Note that the XCB surface automatically takes advantage of the X render
+ * extension if it is available.
+ */
+
+/**
+ * CAIRO_HAS_XCB_SURFACE:
+ *
+ * Defined if the xcb surface backend is available.
+ * This macro can be used to conditionally compile backend-specific code.
+ */
+
+cairo_surface_t *
+_cairo_xcb_surface_create_similar (void                        *abstract_other,
+                                  cairo_content_t       content,
+                                  int                   width,
+                                  int                   height)
+{
+    cairo_xcb_surface_t *other = abstract_other;
+    cairo_xcb_surface_t *surface;
+    cairo_xcb_connection_t *connection;
+    xcb_pixmap_t pixmap;
+    cairo_status_t status;
+
+    if (unlikely(width  > XLIB_COORD_MAX ||
+                height > XLIB_COORD_MAX ||
+                width  <= 0 ||
+                height <= 0))
+       return cairo_image_surface_create (_cairo_format_from_content (content),
+                                          width, height);
+
+    if ((other->connection->flags & CAIRO_XCB_HAS_RENDER) == 0)
+       return _cairo_xcb_surface_create_similar_image (other,
+                                                       _cairo_format_from_content (content),
+                                                       width, height);
+
+    connection = other->connection;
+    status = _cairo_xcb_connection_acquire (connection);
+    if (unlikely (status))
+       return _cairo_surface_create_in_error (status);
+
+    if (content == other->base.content) {
+       pixmap = _cairo_xcb_connection_create_pixmap (connection,
+                                                     other->depth,
+                                                     other->drawable,
+                                                     width, height);
+
+       surface = (cairo_xcb_surface_t *)
+           _cairo_xcb_surface_create_internal (other->screen,
+                                               pixmap, TRUE,
+                                               other->pixman_format,
+                                               other->xrender_format,
+                                               width, height);
+    } else {
+       cairo_format_t format;
+       pixman_format_code_t pixman_format;
+
+       /* XXX find a compatible xrender format */
+       switch (content) {
+       case CAIRO_CONTENT_ALPHA:
+           pixman_format = PIXMAN_a8;
+           format = CAIRO_FORMAT_A8;
+           break;
+       case CAIRO_CONTENT_COLOR:
+           pixman_format = PIXMAN_x8r8g8b8;
+           format = CAIRO_FORMAT_RGB24;
+           break;
+       default:
+           ASSERT_NOT_REACHED;
+       case CAIRO_CONTENT_COLOR_ALPHA:
+           pixman_format = PIXMAN_a8r8g8b8;
+           format = CAIRO_FORMAT_ARGB32;
+           break;
+       }
+
+       pixmap = _cairo_xcb_connection_create_pixmap (connection,
+                                                     PIXMAN_FORMAT_DEPTH (pixman_format),
+                                                     other->drawable,
+                                                     width, height);
+
+       surface = (cairo_xcb_surface_t *)
+           _cairo_xcb_surface_create_internal (other->screen,
+                                               pixmap, TRUE,
+                                               pixman_format,
+                                               connection->standard_formats[format],
+                                               width, height);
+    }
+
+    if (unlikely (surface->base.status))
+       _cairo_xcb_connection_free_pixmap (connection, pixmap);
+
+    _cairo_xcb_connection_release (connection);
+
+    return &surface->base;
+}
+
+cairo_surface_t *
+_cairo_xcb_surface_create_similar_image (void                  *abstract_other,
+                                        cairo_format_t          format,
+                                        int                     width,
+                                        int                     height)
+{
+    cairo_xcb_surface_t *other = abstract_other;
+    cairo_xcb_connection_t *connection = other->connection;
+
+    cairo_xcb_shm_info_t *shm_info;
+    cairo_image_surface_t *image;
+    cairo_status_t status;
+    pixman_format_code_t pixman_format;
+
+    pixman_format = _cairo_format_to_pixman_format_code (format);
+
+    status = _cairo_xcb_shm_image_create (connection, pixman_format,
+                                         width, height, &image,
+                                         &shm_info);
+    if (unlikely (status))
+       return _cairo_surface_create_in_error (status);
+    return &image->base;
+}
+
+static cairo_status_t
+_cairo_xcb_surface_finish (void *abstract_surface)
+{
+    cairo_xcb_surface_t *surface = abstract_surface;
+    cairo_status_t status;
+
+    if (surface->fallback != NULL) {
+       cairo_surface_finish (&surface->fallback->base);
+       cairo_surface_destroy (&surface->fallback->base);
+    }
+    _cairo_boxes_fini (&surface->fallback_damage);
+
+    cairo_list_del (&surface->link);
+
+    status = _cairo_xcb_connection_acquire (surface->connection);
+    if (status == CAIRO_STATUS_SUCCESS) {
+       if (surface->picture != XCB_NONE) {
+           _cairo_xcb_connection_render_free_picture (surface->connection,
+                                                      surface->picture);
+       }
+
+       if (surface->owns_pixmap)
+           _cairo_xcb_connection_free_pixmap (surface->connection, surface->drawable);
+       _cairo_xcb_connection_release (surface->connection);
+    }
+
+    _cairo_xcb_connection_destroy (surface->connection);
+
+    return status;
+}
+
+static void
+_destroy_image (pixman_image_t *image, void *data)
+{
+    free (data);
+}
+
+#if CAIRO_HAS_XCB_SHM_FUNCTIONS
+static cairo_surface_t *
+_cairo_xcb_surface_create_shm_image (cairo_xcb_connection_t *connection,
+                                    pixman_format_code_t pixman_format,
+                                    int width, int height,
+                                    cairo_bool_t might_reuse,
+                                    cairo_xcb_shm_info_t **shm_info_out)
+{
+    cairo_surface_t *image;
+    cairo_xcb_shm_info_t *shm_info;
+    cairo_status_t status;
+    size_t stride;
+
+    *shm_info_out = NULL;
+
+    stride = CAIRO_STRIDE_FOR_WIDTH_BPP (width,
+                                        PIXMAN_FORMAT_BPP (pixman_format));
+    status = _cairo_xcb_connection_allocate_shm_info (connection,
+                                                     stride * height,
+                                                     might_reuse,
+                                                     &shm_info);
+    if (unlikely (status))
+       return _cairo_surface_create_in_error (status);
+
+    image = _cairo_image_surface_create_with_pixman_format (shm_info->mem,
+                                                           pixman_format,
+                                                           width, height,
+                                                           stride);
+    if (unlikely (image->status)) {
+       _cairo_xcb_shm_info_destroy (shm_info);
+       return image;
+    }
+
+    status = _cairo_user_data_array_set_data (&image->user_data,
+                                             (const cairo_user_data_key_t *) connection,
+                                             shm_info,
+                                             (cairo_destroy_func_t) _cairo_xcb_shm_info_destroy);
+    if (unlikely (status)) {
+       cairo_surface_destroy (image);
+       _cairo_xcb_shm_info_destroy (shm_info);
+       return _cairo_surface_create_in_error (status);
+    }
+
+    *shm_info_out = shm_info;
+    return image;
+}
+#endif
+
+static cairo_surface_t *
+_get_shm_image (cairo_xcb_surface_t *surface,
+               int x, int y,
+               int width, int height)
+{
+#if CAIRO_HAS_XCB_SHM_FUNCTIONS
+    cairo_xcb_shm_info_t *shm_info;
+    cairo_surface_t *image;
+    cairo_status_t status;
+
+    if ((surface->connection->flags & CAIRO_XCB_HAS_SHM) == 0)
+       return NULL;
+
+    image = _cairo_xcb_surface_create_shm_image (surface->connection,
+                                                surface->pixman_format,
+                                                width, height,
+                                                TRUE,
+                                                &shm_info);
+    if (unlikely (image == NULL || image->status))
+       goto done;
+
+    status = _cairo_xcb_connection_shm_get_image (surface->connection,
+                                                 surface->drawable,
+                                                 x, y,
+                                                 width, height,
+                                                 shm_info->shm,
+                                                 shm_info->offset);
+    if (unlikely (status)) {
+       cairo_surface_destroy (image);
+       image = _cairo_surface_create_in_error (status);
+    }
+
+done:
+    return image;
+#else
+    return NULL;
+#endif
+}
+
+static cairo_surface_t *
+_get_image (cairo_xcb_surface_t                 *surface,
+           cairo_bool_t                  use_shm,
+           int x, int y,
+           int width, int height)
+{
+    cairo_surface_t *image;
+    cairo_xcb_connection_t *connection;
+    xcb_get_image_reply_t *reply;
+    cairo_int_status_t status;
+
+    assert (surface->fallback == NULL);
+    assert (x >= 0);
+    assert (y >= 0);
+    assert (x + width <= surface->width);
+    assert (y + height <= surface->height);
+
+    if (surface->deferred_clear) {
+       image =
+           _cairo_image_surface_create_with_pixman_format (NULL,
+                                                           surface->pixman_format,
+                                                           width, height,
+                                                           0);
+       if (surface->deferred_clear_color.alpha_short > 0x00ff) {
+           cairo_solid_pattern_t solid;
+
+           _cairo_pattern_init_solid (&solid, &surface->deferred_clear_color);
+           status = _cairo_surface_paint (image,
+                                          CAIRO_OPERATOR_SOURCE,
+                                          &solid.base,
+                                          NULL);
+           if (unlikely (status)) {
+               cairo_surface_destroy (image);
+               image = _cairo_surface_create_in_error (status);
+           }
+       }
+       return image;
+    }
+
+    connection = surface->connection;
+
+    status = _cairo_xcb_connection_acquire (connection);
+    if (unlikely (status))
+       return _cairo_surface_create_in_error (status);
+
+    if (use_shm) {
+       image = _get_shm_image (surface, x, y, width, height);
+       if (image) {
+           /* XXX This only wants to catch SHM exhaustion,
+            * not other allocation failures. */
+           if (image->status != CAIRO_STATUS_NO_MEMORY) {
+               _cairo_xcb_connection_release (connection);
+               return image;
+           }
+           cairo_surface_destroy (image);
+       }
+    }
+
+    status = _cairo_xcb_connection_get_image (connection,
+                                             surface->drawable,
+                                             x, y,
+                                             width, height,
+                                             &reply);
+    if (unlikely (status))
+       goto FAIL;
+
+    if (reply == NULL && ! surface->owns_pixmap) {
+       /* xcb_get_image_t from a window is dangerous because it can
+        * produce errors if the window is unmapped or partially
+        * outside the screen. We could check for errors and
+        * retry, but to keep things simple, we just create a
+        * temporary pixmap
+        *
+        * If we hit this fallback too often, we should remember so and
+        * skip the round-trip from the above GetImage request,
+        * similar to what cairo-xlib does.
+        */
+       xcb_pixmap_t pixmap;
+       xcb_gcontext_t gc;
+
+       gc = _cairo_xcb_screen_get_gc (surface->screen,
+                                      surface->drawable,
+                                      surface->depth);
+       pixmap = _cairo_xcb_connection_create_pixmap (connection,
+                                                     surface->depth,
+                                                     surface->drawable,
+                                                     width, height);
+
+       /* XXX IncludeInferiors? */
+       _cairo_xcb_connection_copy_area (connection,
+                                        surface->drawable,
+                                        pixmap, gc,
+                                        x, y,
+                                        0, 0,
+                                        width, height);
+
+       _cairo_xcb_screen_put_gc (surface->screen, surface->depth, gc);
+
+       status = _cairo_xcb_connection_get_image (connection,
+                                                 pixmap,
+                                                 0, 0,
+                                                 width, height,
+                                                 &reply);
+       _cairo_xcb_connection_free_pixmap (connection, pixmap);
+
+       if (unlikely (status))
+           goto FAIL;
+    }
+
+    if (unlikely (reply == NULL)) {
+       status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+       goto FAIL;
+    }
+
+    /* XXX byte swap */
+    /* XXX format conversion */
+    assert (reply->depth == surface->depth);
+
+    image = _cairo_image_surface_create_with_pixman_format
+       (xcb_get_image_data (reply),
+        surface->pixman_format,
+        width, height,
+        CAIRO_STRIDE_FOR_WIDTH_BPP (width,
+                                    PIXMAN_FORMAT_BPP (surface->pixman_format)));
+    status = image->status;
+    if (unlikely (status)) {
+       free (reply);
+       goto FAIL;
+    }
+
+    /* XXX */
+    pixman_image_set_destroy_function (((cairo_image_surface_t *)image)->pixman_image, _destroy_image, reply);
+
+    _cairo_xcb_connection_release (connection);
+
+    return image;
+
+FAIL:
+    _cairo_xcb_connection_release (connection);
+    return _cairo_surface_create_in_error (status);
+}
+
+static cairo_status_t
+_cairo_xcb_surface_acquire_source_image (void *abstract_surface,
+                                        cairo_image_surface_t **image_out,
+                                        void **image_extra)
+{
+    cairo_xcb_surface_t *surface = abstract_surface;
+    cairo_surface_t *image;
+
+    if (surface->fallback != NULL) {
+       image = cairo_surface_reference (&surface->fallback->base);
+       goto DONE;
+    }
+
+    image = _cairo_surface_has_snapshot (&surface->base,
+                                        &_cairo_image_surface_backend);
+    if (image != NULL) {
+       image = cairo_surface_reference (image);
+       goto DONE;
+    }
+
+    image = _get_image (surface, FALSE, 0, 0, surface->width, surface->height);
+    if (unlikely (image->status))
+       return image->status;
+
+    _cairo_surface_attach_snapshot (&surface->base, image, NULL);
+
+DONE:
+    *image_out = (cairo_image_surface_t *) image;
+    *image_extra = NULL;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_xcb_surface_release_source_image (void *abstract_surface,
+                                        cairo_image_surface_t *image,
+                                        void *image_extra)
+{
+    cairo_surface_destroy (&image->base);
+}
+
+cairo_bool_t
+_cairo_xcb_surface_get_extents (void *abstract_surface,
+                               cairo_rectangle_int_t *extents)
+{
+    cairo_xcb_surface_t *surface = abstract_surface;
+
+    extents->x = extents->y = 0;
+    extents->width  = surface->width;
+    extents->height = surface->height;
+    return TRUE;
+}
+
+static void
+_cairo_xcb_surface_get_font_options (void *abstract_surface,
+                                    cairo_font_options_t *options)
+{
+    /* XXX  copy from xlib */
+    _cairo_font_options_init_default (options);
+    _cairo_font_options_set_round_glyph_positions (options, CAIRO_ROUND_GLYPH_POS_ON);
+}
+
+static cairo_status_t
+_put_shm_image (cairo_xcb_surface_t    *surface,
+               xcb_gcontext_t          gc,
+               cairo_image_surface_t  *image)
+{
+#if CAIRO_HAS_XCB_SHM_FUNCTIONS
+    cairo_xcb_shm_info_t *shm_info;
+
+    shm_info = _cairo_user_data_array_get_data (&image->base.user_data,
+                                               (const cairo_user_data_key_t *) surface->connection);
+    if (shm_info == NULL)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    _cairo_xcb_connection_shm_put_image (surface->connection,
+                                        surface->drawable,
+                                        gc,
+                                        surface->width, surface->height,
+                                        0, 0,
+                                        image->width, image->height,
+                                        image->base.device_transform_inverse.x0,
+                                        image->base.device_transform_inverse.y0,
+                                        image->depth,
+                                        shm_info->shm,
+                                        shm_info->offset);
+
+    return CAIRO_STATUS_SUCCESS;
+#else
+    return CAIRO_INT_STATUS_UNSUPPORTED;
+#endif
+}
+
+static cairo_status_t
+_put_image (cairo_xcb_surface_t    *surface,
+           cairo_image_surface_t  *image)
+{
+    cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS;
+
+    /* XXX track damaged region? */
+
+    status = _cairo_xcb_connection_acquire (surface->connection);
+    if (unlikely (status))
+       return status;
+
+    if (image->pixman_format == surface->pixman_format) {
+       xcb_gcontext_t gc;
+
+       assert (image->depth == surface->depth);
+       assert (image->stride == (int) CAIRO_STRIDE_FOR_WIDTH_BPP (image->width, PIXMAN_FORMAT_BPP (image->pixman_format)));
+
+       gc = _cairo_xcb_screen_get_gc (surface->screen,
+                                      surface->drawable,
+                                      surface->depth);
+
+       status = _put_shm_image (surface, gc, image);
+       if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
+           _cairo_xcb_connection_put_image (surface->connection,
+                                            surface->drawable, gc,
+                                            image->width, image->height,
+                                            image->base.device_transform_inverse.x0,
+                                            image->base.device_transform_inverse.y0,
+                                            image->depth,
+                                            image->stride,
+                                            image->data);
+           status = CAIRO_STATUS_SUCCESS;
+       }
+
+       _cairo_xcb_screen_put_gc (surface->screen, surface->depth, gc);
+    } else {
+       ASSERT_NOT_REACHED;
+    }
+
+    _cairo_xcb_connection_release (surface->connection);
+    return status;
+}
+
+static cairo_int_status_t
+_put_shm_image_boxes (cairo_xcb_surface_t    *surface,
+                     cairo_image_surface_t  *image,
+                     xcb_gcontext_t gc,
+                     cairo_boxes_t *boxes)
+{
+#if CAIRO_HAS_XCB_SHM_FUNCTIONS
+    cairo_xcb_shm_info_t *shm_info;
+
+    shm_info = _cairo_user_data_array_get_data (&image->base.user_data,
+                                               (const cairo_user_data_key_t *) surface->connection);
+    if (shm_info != NULL) {
+       struct _cairo_boxes_chunk *chunk;
+
+       for (chunk = &boxes->chunks; chunk; chunk = chunk->next) {
+           int i;
+
+           for (i = 0; i < chunk->count; i++) {
+               cairo_box_t *b = &chunk->base[i];
+               int x = _cairo_fixed_integer_part (b->p1.x);
+               int y = _cairo_fixed_integer_part (b->p1.y);
+               int width = _cairo_fixed_integer_part (b->p2.x - b->p1.x);
+               int height = _cairo_fixed_integer_part (b->p2.y - b->p1.y);
+
+               _cairo_xcb_connection_shm_put_image (surface->connection,
+                                                    surface->drawable,
+                                                    gc,
+                                                    surface->width, surface->height,
+                                                    x, y,
+                                                    width, height,
+                                                    x, y,
+                                                    image->depth,
+                                                    shm_info->shm,
+                                                    shm_info->offset);
+           }
+       }
+    }
+
+    return CAIRO_INT_STATUS_SUCCESS;
+#endif
+
+    return CAIRO_INT_STATUS_UNSUPPORTED;
+}
+
+static cairo_status_t
+_put_image_boxes (cairo_xcb_surface_t    *surface,
+                 cairo_image_surface_t  *image,
+                 cairo_boxes_t *boxes)
+{
+    cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS;
+    xcb_gcontext_t gc;
+
+    if (boxes->num_boxes == 0)
+           return CAIRO_STATUS_SUCCESS;
+
+    /* XXX track damaged region? */
+
+    status = _cairo_xcb_connection_acquire (surface->connection);
+    if (unlikely (status))
+       return status;
+
+    assert (image->pixman_format == surface->pixman_format);
+    assert (image->depth == surface->depth);
+    assert (image->stride == (int) CAIRO_STRIDE_FOR_WIDTH_BPP (image->width, PIXMAN_FORMAT_BPP (image->pixman_format)));
+
+    gc = _cairo_xcb_screen_get_gc (surface->screen,
+                                  surface->drawable,
+                                  surface->depth);
+
+    status = _put_shm_image_boxes (surface, image, gc, boxes);
+    if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
+           struct _cairo_boxes_chunk *chunk;
+
+           for (chunk = &boxes->chunks; chunk; chunk = chunk->next) {
+                   int i;
+
+                   for (i = 0; i < chunk->count; i++) {
+                           cairo_box_t *b = &chunk->base[i];
+                           int x = _cairo_fixed_integer_part (b->p1.x);
+                           int y = _cairo_fixed_integer_part (b->p1.y);
+                           int width = _cairo_fixed_integer_part (b->p2.x - b->p1.x);
+                           int height = _cairo_fixed_integer_part (b->p2.y - b->p1.y);
+                           _cairo_xcb_connection_put_image (surface->connection,
+                                                            surface->drawable, gc,
+                                                            width, height,
+                                                            x, y,
+                                                            image->depth,
+                                                            image->stride,
+                                                            image->data +
+                                                            x * PIXMAN_FORMAT_BPP (image->pixman_format) / 8 +
+                                                            y * image->stride);
+
+                   }
+           }
+           status = CAIRO_STATUS_SUCCESS;
+    }
+
+    _cairo_xcb_screen_put_gc (surface->screen, surface->depth, gc);
+    _cairo_xcb_connection_release (surface->connection);
+    return status;
+}
+
+static cairo_status_t
+_cairo_xcb_surface_flush (void *abstract_surface)
+{
+    cairo_xcb_surface_t *surface = abstract_surface;
+    cairo_status_t status;
+
+    if (likely (surface->fallback == NULL)) {
+       status = CAIRO_STATUS_SUCCESS;
+       if (! surface->base.finished && surface->deferred_clear)
+           status = _cairo_xcb_surface_clear (surface);
+
+       return status;
+    }
+
+    status = surface->base.status;
+    if (status == CAIRO_STATUS_SUCCESS && ! surface->base.finished) {
+       status = cairo_surface_status (&surface->fallback->base);
+
+       if (status == CAIRO_STATUS_SUCCESS)
+               status = _cairo_bentley_ottmann_tessellate_boxes (&surface->fallback_damage,
+                                                                 CAIRO_FILL_RULE_WINDING,
+                                                                 &surface->fallback_damage);
+
+       if (status == CAIRO_STATUS_SUCCESS)
+           status = _put_image_boxes (surface,
+                                      surface->fallback,
+                                      &surface->fallback_damage);
+
+       if (status == CAIRO_STATUS_SUCCESS) {
+           _cairo_surface_attach_snapshot (&surface->base,
+                                           &surface->fallback->base,
+                                           cairo_surface_finish);
+       }
+    }
+
+    _cairo_boxes_clear (&surface->fallback_damage);
+    cairo_surface_destroy (&surface->fallback->base);
+    surface->fallback = NULL;
+
+    return status;
+}
+
+static cairo_surface_t *
+_cairo_xcb_surface_map_to_image (void *abstract_surface,
+                                const cairo_rectangle_int_t *extents)
+{
+    cairo_xcb_surface_t *surface = abstract_surface;
+    cairo_surface_t *image;
+
+    if (surface->fallback)
+       return surface->fallback->base.backend->map_to_image (&surface->fallback->base, extents);
+
+    image = _get_image (surface, TRUE,
+                       extents->x, extents->y,
+                       extents->width, extents->height);
+    if (unlikely (image->status))
+       return image;
+
+    /* Do we have a deferred clear and this image surface does NOT cover the
+     * whole xcb surface? Have to apply the clear in that case, else
+     * uploading the image will handle the problem for us.
+     */
+    if (surface->deferred_clear &&
+       ! (extents->width == surface->width &&
+          extents->height == surface->height)) {
+       cairo_status_t status = _cairo_xcb_surface_clear (surface);
+       if (unlikely (status)) {
+           cairo_surface_destroy(image);
+           return _cairo_surface_create_in_error (status);
+       }
+    }
+    surface->deferred_clear = FALSE;
+
+    cairo_surface_set_device_offset (image, -extents->x, -extents->y);
+    return image;
+}
+
+static cairo_int_status_t
+_cairo_xcb_surface_unmap (void *abstract_surface,
+                         cairo_image_surface_t *image)
+{
+    cairo_xcb_surface_t *surface = abstract_surface;
+
+    if (surface->fallback)
+       return surface->fallback->base.backend->unmap_image (&surface->fallback->base, image);
+    return _put_image (abstract_surface, image);
+}
+
+static cairo_surface_t *
+_cairo_xcb_surface_fallback (cairo_xcb_surface_t *surface,
+                            cairo_composite_rectangles_t *composite)
+{
+    cairo_image_surface_t *image;
+    cairo_status_t status;
+
+    status = _cairo_composite_rectangles_add_to_damage (composite,
+                                                       &surface->fallback_damage);
+    if (unlikely (status))
+           return _cairo_surface_create_in_error (status);
+
+    if (surface->fallback)
+       return &surface->fallback->base;
+
+    image = (cairo_image_surface_t *)
+           _get_image (surface, TRUE, 0, 0, surface->width, surface->height);
+
+    /* If there was a deferred clear, _get_image applied it */
+    if (image->base.status == CAIRO_STATUS_SUCCESS) {
+       surface->deferred_clear = FALSE;
+
+       surface->fallback = image;
+    }
+
+    return &surface->fallback->base;
+}
+
+static cairo_int_status_t
+_cairo_xcb_surface_paint (void                 *abstract_surface,
+                         cairo_operator_t       op,
+                         const cairo_pattern_t *source,
+                         const cairo_clip_t    *clip)
+{
+    cairo_xcb_surface_t *surface = abstract_surface;
+    cairo_composite_rectangles_t composite;
+    cairo_int_status_t status;
+
+    status = _cairo_composite_rectangles_init_for_paint (&composite,
+                                                        &surface->base,
+                                                        op, source,
+                                                        clip);
+    if (unlikely (status))
+       return status;
+
+    if (surface->fallback == NULL) {
+       status = _cairo_xcb_surface_cairo_paint (surface, op, source, clip);
+       if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+           goto done;
+
+       status = _cairo_xcb_surface_render_paint (surface, op, source,
+                                                 &composite);
+       if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+           goto done;
+    }
+
+    status = _cairo_surface_paint (_cairo_xcb_surface_fallback (surface,
+                                                               &composite),
+                                  op, source, clip);
+
+done:
+    _cairo_composite_rectangles_fini (&composite);
+    return status;
+}
+
+static cairo_int_status_t
+_cairo_xcb_surface_mask (void                  *abstract_surface,
+                        cairo_operator_t        op,
+                        const cairo_pattern_t  *source,
+                        const cairo_pattern_t  *mask,
+                        const cairo_clip_t     *clip)
+{
+    cairo_xcb_surface_t *surface = abstract_surface;
+    cairo_composite_rectangles_t composite;
+    cairo_int_status_t status;
+
+    status = _cairo_composite_rectangles_init_for_mask (&composite,
+                                                       &surface->base,
+                                                       op, source, mask, clip);
+    if (unlikely (status))
+       return status;
+
+    if (surface->fallback == NULL) {
+       status =  _cairo_xcb_surface_cairo_mask (surface,
+                                                op, source, mask, clip);
+       if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+           goto done;
+
+       status =  _cairo_xcb_surface_render_mask (surface,
+                                                 op, source, mask, &composite);
+       if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+           goto done;
+    }
+
+    status = _cairo_surface_mask (_cairo_xcb_surface_fallback (surface,
+                                                              &composite),
+                                 op, source, mask,
+                                 clip);
+done:
+    _cairo_composite_rectangles_fini (&composite);
+    return status;
+}
+
+static cairo_int_status_t
+_cairo_xcb_surface_stroke (void                                *abstract_surface,
+                          cairo_operator_t              op,
+                          const cairo_pattern_t        *source,
+                          const cairo_path_fixed_t     *path,
+                          const cairo_stroke_style_t   *style,
+                          const cairo_matrix_t         *ctm,
+                          const cairo_matrix_t         *ctm_inverse,
+                          double                        tolerance,
+                          cairo_antialias_t             antialias,
+                          const cairo_clip_t           *clip)
+{
+    cairo_xcb_surface_t *surface = abstract_surface;
+    cairo_composite_rectangles_t composite;
+    cairo_int_status_t status;
+
+    status = _cairo_composite_rectangles_init_for_stroke (&composite,
+                                                         &surface->base,
+                                                         op, source,
+                                                         path, style, ctm,
+                                                         clip);
+    if (unlikely (status))
+       return status;
+
+    if (surface->fallback == NULL) {
+       status = _cairo_xcb_surface_cairo_stroke (surface, op, source,
+                                                 path, style,
+                                                 ctm, ctm_inverse,
+                                                 tolerance, antialias,
+                                                 clip);
+
+       if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+           goto done;
+
+       status = _cairo_xcb_surface_render_stroke (surface, op, source,
+                                                  path, style,
+                                                  ctm, ctm_inverse,
+                                                  tolerance, antialias,
+                                                  &composite);
+
+       if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+           goto done;
+    }
+
+    status = _cairo_surface_stroke (_cairo_xcb_surface_fallback (surface,
+                                                                &composite),
+                                   op, source,
+                                   path, style,
+                                   ctm, ctm_inverse,
+                                   tolerance, antialias,
+                                   clip);
+done:
+    _cairo_composite_rectangles_fini (&composite);
+    return status;
+}
+
+static cairo_int_status_t
+_cairo_xcb_surface_fill (void                  *abstract_surface,
+                        cairo_operator_t        op,
+                        const cairo_pattern_t  *source,
+                        const cairo_path_fixed_t*path,
+                        cairo_fill_rule_t       fill_rule,
+                        double                  tolerance,
+                        cairo_antialias_t       antialias,
+                        const cairo_clip_t     *clip)
+{
+    cairo_xcb_surface_t *surface = abstract_surface;
+    cairo_composite_rectangles_t composite;
+    cairo_int_status_t status;
+
+    status = _cairo_composite_rectangles_init_for_fill (&composite,
+                                                       &surface->base,
+                                                       op, source, path,
+                                                       clip);
+    if (unlikely (status))
+       return status;
+
+    if (surface->fallback == NULL) {
+       status = _cairo_xcb_surface_cairo_fill (surface, op, source,
+                                               path, fill_rule,
+                                               tolerance, antialias,
+                                               clip);
+       if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+           goto done;
+
+       status = _cairo_xcb_surface_render_fill (surface, op, source,
+                                                path, fill_rule,
+                                                tolerance, antialias,
+                                                &composite);
+       if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+           goto done;
+    }
+
+    status = _cairo_surface_fill (_cairo_xcb_surface_fallback (surface,
+                                                              &composite),
+                                 op, source,
+                                 path, fill_rule,
+                                 tolerance, antialias,
+                                 clip);
+done:
+    _cairo_composite_rectangles_fini (&composite);
+    return status;
+}
+
+static cairo_int_status_t
+_cairo_xcb_surface_glyphs (void                                *abstract_surface,
+                          cairo_operator_t              op,
+                          const cairo_pattern_t        *source,
+                          cairo_glyph_t                *glyphs,
+                          int                           num_glyphs,
+                          cairo_scaled_font_t          *scaled_font,
+                          const cairo_clip_t           *clip)
+{
+    cairo_xcb_surface_t *surface = abstract_surface;
+    cairo_composite_rectangles_t composite;
+    cairo_int_status_t status;
+    cairo_bool_t overlap;
+
+    status = _cairo_composite_rectangles_init_for_glyphs (&composite,
+                                                         &surface->base,
+                                                         op, source,
+                                                         scaled_font,
+                                                         glyphs, num_glyphs,
+                                                         clip, &overlap);
+    if (unlikely (status))
+       return status;
+
+    if (surface->fallback == NULL) {
+       status = _cairo_xcb_surface_cairo_glyphs (surface,
+                                                 op, source,
+                                                 scaled_font, glyphs, num_glyphs,
+                                                 clip);
+       if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+           goto done;
+
+       status = _cairo_xcb_surface_render_glyphs (surface,
+                                                  op, source,
+                                                  scaled_font, glyphs, num_glyphs,
+                                                  &composite, overlap);
+       if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+           goto done;
+    }
+
+    status =  _cairo_surface_show_text_glyphs (_cairo_xcb_surface_fallback (surface,
+                                                                           &composite),
+                                              op, source,
+                                              NULL, 0,
+                                              glyphs, num_glyphs,
+                                              NULL, 0, 0,
+                                              scaled_font,
+                                              clip);
+done:
+    _cairo_composite_rectangles_fini (&composite);
+    return status;
+}
+
+const cairo_surface_backend_t _cairo_xcb_surface_backend = {
+    CAIRO_SURFACE_TYPE_XCB,
+    _cairo_xcb_surface_finish,
+    _cairo_default_context_create,
+
+    _cairo_xcb_surface_create_similar,
+    _cairo_xcb_surface_create_similar_image,
+    _cairo_xcb_surface_map_to_image,
+    _cairo_xcb_surface_unmap,
+
+    _cairo_xcb_surface_acquire_source_image,
+    _cairo_xcb_surface_release_source_image,
+    NULL, /* snapshot */
+
+
+    NULL, /* copy_page */
+    NULL, /* show_page */
+
+    _cairo_xcb_surface_get_extents,
+    _cairo_xcb_surface_get_font_options,
+
+    _cairo_xcb_surface_flush,
+    NULL,
+
+    _cairo_xcb_surface_paint,
+    _cairo_xcb_surface_mask,
+    _cairo_xcb_surface_stroke,
+    _cairo_xcb_surface_fill,
+    NULL, /* fill-stroke */
+    _cairo_xcb_surface_glyphs,
+};
+
+cairo_surface_t *
+_cairo_xcb_surface_create_internal (cairo_xcb_screen_t         *screen,
+                                   xcb_drawable_t               drawable,
+                                   cairo_bool_t                 owns_pixmap,
+                                   pixman_format_code_t         pixman_format,
+                                   xcb_render_pictformat_t      xrender_format,
+                                   int                          width,
+                                   int                          height)
+{
+    cairo_xcb_surface_t *surface;
+
+    surface = malloc (sizeof (cairo_xcb_surface_t));
+    if (unlikely (surface == NULL))
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+    _cairo_surface_init (&surface->base,
+                        &_cairo_xcb_surface_backend,
+                        &screen->connection->device,
+                        _cairo_content_from_pixman_format (pixman_format));
+
+    surface->connection = _cairo_xcb_connection_reference (screen->connection);
+    surface->screen = screen;
+    cairo_list_add (&surface->link, &screen->surfaces);
+
+    surface->drawable = drawable;
+    surface->owns_pixmap = owns_pixmap;
+
+    surface->deferred_clear = FALSE;
+    surface->deferred_clear_color = *CAIRO_COLOR_TRANSPARENT;
+
+    surface->width  = width;
+    surface->height = height;
+    surface->depth  = PIXMAN_FORMAT_DEPTH (pixman_format);
+
+    surface->picture = XCB_NONE;
+    if (screen->connection->force_precision != -1)
+       surface->precision = screen->connection->force_precision;
+    else
+       surface->precision = XCB_RENDER_POLY_MODE_IMPRECISE;
+
+    surface->pixman_format = pixman_format;
+    surface->xrender_format = xrender_format;
+
+    surface->fallback = NULL;
+    _cairo_boxes_init (&surface->fallback_damage);
+
+    return &surface->base;
+}
+
+static xcb_screen_t *
+_cairo_xcb_screen_from_visual (xcb_connection_t *connection,
+                              xcb_visualtype_t *visual,
+                              int *depth)
+{
+    xcb_depth_iterator_t d;
+    xcb_screen_iterator_t s;
+
+    s = xcb_setup_roots_iterator (xcb_get_setup (connection));
+    for (; s.rem; xcb_screen_next (&s)) {
+       if (s.data->root_visual == visual->visual_id) {
+           *depth = s.data->root_depth;
+           return s.data;
+       }
+
+       d = xcb_screen_allowed_depths_iterator(s.data);
+       for (; d.rem; xcb_depth_next (&d)) {
+           xcb_visualtype_iterator_t v = xcb_depth_visuals_iterator (d.data);
+
+           for (; v.rem; xcb_visualtype_next (&v)) {
+               if (v.data->visual_id == visual->visual_id) {
+                   *depth = d.data->depth;
+                   return s.data;
+               }
+           }
+       }
+    }
+
+    return NULL;
+}
+
+/**
+ * cairo_xcb_surface_create:
+ * @xcb_connection: an XCB connection
+ * @drawable: an XCB drawable
+ * @visual: the visual to use for drawing to @drawable. The depth
+ *          of the visual must match the depth of the drawable.
+ *          Currently, only TrueColor visuals are fully supported.
+ * @width: the current width of @drawable
+ * @height: the current height of @drawable
+ *
+ * Creates an XCB surface that draws to the given drawable.
+ * The way that colors are represented in the drawable is specified
+ * by the provided visual.
+ *
+ * Note: If @drawable is a Window, then the function
+ * cairo_xcb_surface_set_size() must be called whenever the size of the
+ * window changes.
+ *
+ * When @drawable is a Window containing child windows then drawing to
+ * the created surface will be clipped by those child windows.  When
+ * the created surface is used as a source, the contents of the
+ * children will be included.
+ *
+ * Return value: a pointer to the newly created surface. The caller
+ * owns the surface and should call cairo_surface_destroy() when done
+ * with it.
+ *
+ * This function always returns a valid pointer, but it will return a
+ * pointer to a "nil" surface if an error such as out of memory
+ * occurs. You can use cairo_surface_status() to check for this.
+ **/
+cairo_surface_t *
+cairo_xcb_surface_create (xcb_connection_t  *xcb_connection,
+                         xcb_drawable_t     drawable,
+                         xcb_visualtype_t  *visual,
+                         int                width,
+                         int                height)
+{
+    cairo_xcb_screen_t *screen;
+    xcb_screen_t *xcb_screen;
+    cairo_format_masks_t image_masks;
+    pixman_format_code_t pixman_format;
+    xcb_render_pictformat_t xrender_format;
+    int depth;
+
+    if (xcb_connection_has_error (xcb_connection))
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_WRITE_ERROR));
+
+    if (unlikely (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX))
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE));
+    if (unlikely (width <= 0 || height <= 0))
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE));
+
+    xcb_screen = _cairo_xcb_screen_from_visual (xcb_connection, visual, &depth);
+    if (unlikely (xcb_screen == NULL))
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_VISUAL));
+
+    image_masks.alpha_mask = 0;
+    image_masks.red_mask   = visual->red_mask;
+    image_masks.green_mask = visual->green_mask;
+    image_masks.blue_mask  = visual->blue_mask;
+    if (depth == 32) /* XXX visuals have no alpha! */
+       image_masks.alpha_mask =
+           0xffffffff & ~(visual->red_mask | visual->green_mask | visual->blue_mask);
+    if (depth > 16)
+       image_masks.bpp = 32;
+    else if (depth > 8)
+       image_masks.bpp = 16;
+    else if (depth > 1)
+       image_masks.bpp = 8;
+    else
+       image_masks.bpp = 1;
+
+    if (! _pixman_format_from_masks (&image_masks, &pixman_format))
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
+
+    screen = _cairo_xcb_screen_get (xcb_connection, xcb_screen);
+    if (unlikely (screen == NULL))
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+    xrender_format =
+       _cairo_xcb_connection_get_xrender_format_for_visual (screen->connection,
+                                                            visual->visual_id);
+
+    return _cairo_xcb_surface_create_internal (screen, drawable, FALSE,
+                                              pixman_format,
+                                              xrender_format,
+                                              width, height);
+}
+#if CAIRO_HAS_XLIB_XCB_FUNCTIONS
+slim_hidden_def (cairo_xcb_surface_create);
+#endif
+
+/**
+ * cairo_xcb_surface_create_for_bitmap:
+ * @xcb_connection: an XCB connection
+ * @xcb_screen: the XCB screen associated with @bitmap
+ * @bitmap: an XCB drawable (a Pixmap with depth 1)
+ * @width: the current width of @drawable
+ * @height: the current height of @drawable
+ *
+ * Creates an XCB surface that draws to the given bitmap.
+ * This will be drawn to as a %CAIRO_FORMAT_A1 object.
+ *
+ * Return value: a pointer to the newly created surface. The caller
+ * owns the surface and should call cairo_surface_destroy() when done
+ * with it.
+ *
+ * This function always returns a valid pointer, but it will return a
+ * pointer to a "nil" surface if an error such as out of memory
+ * occurs. You can use cairo_surface_status() to check for this.
+ **/
+cairo_surface_t *
+cairo_xcb_surface_create_for_bitmap (xcb_connection_t  *xcb_connection,
+                                    xcb_screen_t       *xcb_screen,
+                                    xcb_pixmap_t        bitmap,
+                                    int                 width,
+                                    int                 height)
+{
+    cairo_xcb_screen_t *screen;
+
+    if (xcb_connection_has_error (xcb_connection))
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_WRITE_ERROR));
+
+    if (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX)
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE));
+    if (unlikely (width <= 0 || height <= 0))
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE));
+
+    screen = _cairo_xcb_screen_get (xcb_connection, xcb_screen);
+    if (unlikely (screen == NULL))
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+    return _cairo_xcb_surface_create_internal (screen, bitmap, FALSE,
+                                              PIXMAN_a1,
+                                              screen->connection->standard_formats[CAIRO_FORMAT_A1],
+                                              width, height);
+}
+#if CAIRO_HAS_XLIB_XCB_FUNCTIONS
+slim_hidden_def (cairo_xcb_surface_create_for_bitmap);
+#endif
+
+/**
+ * cairo_xcb_surface_create_with_xrender_format:
+ * @connection: an XCB connection
+ * @drawable: an XCB drawable
+ * @screen: the XCB screen associated with @drawable
+ * @format: the picture format to use for drawing to @drawable. The
+ *          depth of @format mush match the depth of the drawable.
+ * @width: the current width of @drawable
+ * @height: the current height of @drawable
+ *
+ * Creates an XCB surface that draws to the given drawable.
+ * The way that colors are represented in the drawable is specified
+ * by the provided picture format.
+ *
+ * Note: If @drawable is a Window, then the function
+ * cairo_xcb_surface_set_size() must be called whenever the size of the
+ * window changes.
+ *
+ * When @drawable is a Window containing child windows then drawing to
+ * the created surface will be clipped by those child windows.  When
+ * the created surface is used as a source, the contents of the
+ * children will be included.
+ *
+ * Return value: a pointer to the newly created surface. The caller
+ * owns the surface and should call cairo_surface_destroy() when done
+ * with it.
+ *
+ * This function always returns a valid pointer, but it will return a
+ * pointer to a "nil" surface if an error such as out of memory
+ * occurs. You can use cairo_surface_status() to check for this.
+ **/
+cairo_surface_t *
+cairo_xcb_surface_create_with_xrender_format (xcb_connection_t     *xcb_connection,
+                                             xcb_screen_t          *xcb_screen,
+                                             xcb_drawable_t         drawable,
+                                             xcb_render_pictforminfo_t *format,
+                                             int                    width,
+                                             int                    height)
+{
+    cairo_xcb_screen_t *screen;
+    cairo_format_masks_t image_masks;
+    pixman_format_code_t pixman_format;
+
+    if (xcb_connection_has_error (xcb_connection))
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_WRITE_ERROR));
+
+    if (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX)
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE));
+    if (unlikely (width <= 0 || height <= 0))
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE));
+
+    image_masks.alpha_mask =
+       (unsigned long) format->direct.alpha_mask << format->direct.alpha_shift;
+    image_masks.red_mask =
+       (unsigned long) format->direct.red_mask << format->direct.red_shift;
+    image_masks.green_mask =
+       (unsigned long) format->direct.green_mask << format->direct.green_shift;
+    image_masks.blue_mask =
+       (unsigned long) format->direct.blue_mask << format->direct.blue_shift;
+#if 0
+    image_masks.bpp = format->depth;
+#else
+    if (format->depth > 16)
+       image_masks.bpp = 32;
+    else if (format->depth > 8)
+       image_masks.bpp = 16;
+    else if (format->depth > 1)
+       image_masks.bpp = 8;
+    else
+       image_masks.bpp = 1;
+#endif
+
+    if (! _pixman_format_from_masks (&image_masks, &pixman_format))
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
+
+    screen = _cairo_xcb_screen_get (xcb_connection, xcb_screen);
+    if (unlikely (screen == NULL))
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+    return _cairo_xcb_surface_create_internal (screen,
+                                              drawable,
+                                              FALSE,
+                                              pixman_format,
+                                              format->id,
+                                              width, height);
+}
+#if CAIRO_HAS_XLIB_XCB_FUNCTIONS
+slim_hidden_def (cairo_xcb_surface_create_with_xrender_format);
+#endif
+
+/**
+ * cairo_xcb_surface_set_size:
+ * @surface: a #cairo_surface_t for the XCB backend
+ * @width: the new width of the surface
+ * @height: the new height of the surface
+ *
+ * Informs cairo of the new size of the XCB drawable underlying the
+ * surface. For a surface created for a window (rather than a pixmap),
+ * this function must be called each time the size of the window
+ * changes. (For a subwindow, you are normally resizing the window
+ * yourself, but for a toplevel window, it is necessary to listen for
+ * ConfigureNotify events.)
+ *
+ * A pixmap can never change size, so it is never necessary to call
+ * this function on a surface created for a pixmap.
+ **/
+void
+cairo_xcb_surface_set_size (cairo_surface_t *abstract_surface,
+                           int              width,
+                           int              height)
+{
+    cairo_xcb_surface_t *surface;
+
+    if (unlikely (abstract_surface->status))
+       return;
+    if (unlikely (abstract_surface->finished)) {
+       _cairo_surface_set_error (abstract_surface,
+                                 _cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
+       return;
+    }
+
+
+    if (abstract_surface->type != CAIRO_SURFACE_TYPE_XCB) {
+       _cairo_surface_set_error (abstract_surface,
+                                 _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH));
+       return;
+    }
+
+    if (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX || width <= 0 || height <= 0) {
+       _cairo_surface_set_error (abstract_surface,
+                                 _cairo_error (CAIRO_STATUS_INVALID_SIZE));
+       return;
+    }
+
+    surface = (cairo_xcb_surface_t *) abstract_surface;
+    surface->width  = width;
+    surface->height = height;
+}
+#if CAIRO_HAS_XLIB_XCB_FUNCTIONS
+slim_hidden_def (cairo_xcb_surface_set_size);
+#endif
diff --git a/src/cairo-xcb.h b/src/cairo-xcb.h
new file mode 100644 (file)
index 0000000..4bfb0b5
--- /dev/null
@@ -0,0 +1,110 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ * Copyright © 2009 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *     Carl D. Worth <cworth@cworth.org>
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#ifndef CAIRO_XCB_H
+#define CAIRO_XCB_H
+
+#include "cairo.h"
+
+#if CAIRO_HAS_XCB_SURFACE
+
+#include <xcb/xcb.h>
+#include <xcb/render.h>
+
+CAIRO_BEGIN_DECLS
+
+cairo_public cairo_surface_t *
+cairo_xcb_surface_create (xcb_connection_t     *connection,
+                         xcb_drawable_t         drawable,
+                         xcb_visualtype_t      *visual,
+                         int                    width,
+                         int                    height);
+
+cairo_public cairo_surface_t *
+cairo_xcb_surface_create_for_bitmap (xcb_connection_t  *connection,
+                                    xcb_screen_t       *screen,
+                                    xcb_pixmap_t        bitmap,
+                                    int                 width,
+                                    int                 height);
+
+cairo_public cairo_surface_t *
+cairo_xcb_surface_create_with_xrender_format (xcb_connection_t                 *connection,
+                                             xcb_screen_t                      *screen,
+                                             xcb_drawable_t                     drawable,
+                                             xcb_render_pictforminfo_t         *format,
+                                             int                                width,
+                                             int                                height);
+
+cairo_public void
+cairo_xcb_surface_set_size (cairo_surface_t *surface,
+                           int              width,
+                           int              height);
+
+cairo_public xcb_connection_t *
+cairo_xcb_device_get_connection (cairo_device_t *device);
+
+/* debug interface */
+
+cairo_public void
+cairo_xcb_device_debug_cap_xshm_version (cairo_device_t *device,
+                                         int major_version,
+                                         int minor_version);
+
+cairo_public void
+cairo_xcb_device_debug_cap_xrender_version (cairo_device_t *device,
+                                            int major_version,
+                                            int minor_version);
+
+/*
+ * @precision: -1 implies automatically choose based on antialiasing mode,
+ *            any other value overrides and sets the corresponding PolyMode.
+ */
+cairo_public void
+cairo_xcb_device_debug_set_precision (cairo_device_t *device,
+                                     int precision);
+
+cairo_public int
+cairo_xcb_device_debug_get_precision (cairo_device_t *device);
+
+CAIRO_END_DECLS
+
+#else  /* CAIRO_HAS_XCB_SURFACE */
+# error Cairo was not compiled with support for the xcb backend
+#endif /* CAIRO_HAS_XCB_SURFACE */
+
+#endif /* CAIRO_XCB_H */
diff --git a/src/cairo-xlib-core-compositor.c b/src/cairo-xlib-core-compositor.c
new file mode 100644 (file)
index 0000000..fe871ab
--- /dev/null
@@ -0,0 +1,528 @@
+/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ * Copyright © 2005 Red Hat, Inc.
+ * Copyright © 2011 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *     Carl D. Worth <cworth@cworth.org>
+ *     Behdad Esfahbod <behdad@behdad.org>
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ *     Karl Tomlinson <karlt+@karlt.net>, Mozilla Corporation
+ */
+
+/* The original X drawing API was very restrictive in what it could handle,
+ * pixel-aligned fill/blits are all that map into Cairo's drawing model.
+ */
+
+#include "cairoint.h"
+
+#if !CAIRO_HAS_XLIB_XCB_FUNCTIONS
+
+#include "cairo-xlib-private.h"
+#include "cairo-xlib-surface-private.h"
+
+#include "cairo-boxes-private.h"
+#include "cairo-compositor-private.h"
+#include "cairo-image-surface-private.h"
+#include "cairo-pattern-private.h"
+#include "cairo-region-private.h"
+#include "cairo-surface-offset-private.h"
+
+/* the low-level interface */
+
+static cairo_int_status_t
+acquire (void *abstract_dst)
+{
+    cairo_xlib_surface_t *dst = abstract_dst;
+    return _cairo_xlib_display_acquire (dst->base.device, &dst->display);
+}
+
+static cairo_int_status_t
+release (void *abstract_dst)
+{
+    cairo_xlib_surface_t *dst = abstract_dst;
+
+    cairo_device_release (&dst->display->base);
+    dst->display = NULL;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+struct _fill_box {
+    Display *dpy;
+    Drawable drawable;
+    GC gc;
+};
+
+static cairo_bool_t fill_box (cairo_box_t *box, void *closure)
+{
+    struct _fill_box *data = closure;
+    int x = _cairo_fixed_integer_part (box->p1.x);
+    int y = _cairo_fixed_integer_part (box->p1.y);
+    int width  = _cairo_fixed_integer_part (box->p2.x - box->p1.x);
+    int height = _cairo_fixed_integer_part (box->p2.y - box->p1.y);
+
+    XFillRectangle (data->dpy, data->drawable, data->gc, x, y, width, height);
+    return TRUE;
+}
+
+static cairo_int_status_t
+fill_boxes (cairo_xlib_surface_t    *dst,
+           const cairo_color_t     *color,
+           cairo_boxes_t           *boxes)
+{
+    cairo_surface_t *dither = NULL;
+    cairo_status_t status;
+    struct _fill_box fb;
+
+    status = _cairo_xlib_surface_get_gc (dst->display, dst, &fb.gc);
+    if (unlikely (status))
+        return status;
+
+    fb.dpy = dst->display->display;
+    fb.drawable = dst->drawable;
+
+    if (dst->visual && dst->visual->class != TrueColor) {
+       cairo_solid_pattern_t solid;
+       cairo_surface_attributes_t attrs;
+
+       _cairo_pattern_init_solid (&solid, color);
+#if 0
+       status = _cairo_pattern_acquire_surface (&solid.base, &dst->base,
+                                                0, 0,
+                                                ARRAY_LENGTH (dither_pattern[0]),
+                                                ARRAY_LENGTH (dither_pattern),
+                                                CAIRO_PATTERN_ACQUIRE_NONE,
+                                                &dither,
+                                                &attrs);
+       if (unlikely (status)) {
+           _cairo_xlib_surface_put_gc (dst->display, dst, fb.gc);
+           return status;
+       }
+#endif
+
+       XSetTSOrigin (fb.dpy, fb.gc,
+                     - (dst->base.device_transform.x0 + attrs.x_offset),
+                     - (dst->base.device_transform.y0 + attrs.y_offset));
+       XSetTile (fb.dpy, fb.gc, ((cairo_xlib_surface_t *) dither)->drawable);
+    } else {
+       //XChangeGC (fb.dpy, fb.gc, GCForeground, color_to_pixel (&color));
+    }
+
+    _cairo_boxes_for_each_box (boxes, fill_box, &fb);
+    _cairo_xlib_surface_put_gc (dst->display, dst, fb.gc);
+
+    cairo_surface_destroy (dither);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+struct _fallback_box {
+    cairo_xlib_surface_t       *dst;
+    cairo_format_t              format;
+    const cairo_pattern_t      *pattern;
+};
+
+static cairo_bool_t fallback_box (cairo_box_t *box, void *closure)
+{
+    struct _fallback_box *data = closure;
+    int x = _cairo_fixed_integer_part (box->p1.x);
+    int y = _cairo_fixed_integer_part (box->p1.y);
+    int width  = _cairo_fixed_integer_part (box->p2.x - box->p1.x);
+    int height = _cairo_fixed_integer_part (box->p2.y - box->p1.y);
+    cairo_surface_t *image;
+    cairo_status_t status;
+
+    /* XXX for EXTEND_NONE and if the box is wholly outside we can just fill */
+
+    image = cairo_surface_create_similar_image (&data->dst->base, data->format,
+                                               width, height);
+    status = _cairo_surface_offset_paint (image, x, y,
+                                         CAIRO_OPERATOR_SOURCE,
+                                         data->pattern, NULL);
+    if (status == CAIRO_STATUS_SUCCESS) {
+       status = _cairo_xlib_surface_draw_image (data->dst,
+                                                (cairo_image_surface_t *)image,
+                                                0, 0,
+                                                width, height,
+                                                x, y);
+    }
+    cairo_surface_destroy (image);
+
+    return status == CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+fallback_boxes (cairo_xlib_surface_t   *dst,
+               const cairo_pattern_t   *pattern,
+               cairo_boxes_t           *boxes)
+{
+    struct _fallback_box fb;
+
+    /* XXX create_similar_image using pixman_format? */
+    switch (dst->depth) {
+    case 8: fb.format = CAIRO_FORMAT_A8; break;
+    case 16: fb.format = CAIRO_FORMAT_RGB16_565; break;
+    case 24: fb.format = CAIRO_FORMAT_RGB24; break;
+    case 30: fb.format = CAIRO_FORMAT_RGB30; break;
+    case 32: fb.format = CAIRO_FORMAT_ARGB32; break;
+    default: return CAIRO_INT_STATUS_UNSUPPORTED;
+    }
+
+    fb.dst = dst;
+    fb.pattern = pattern;
+
+    if (! _cairo_boxes_for_each_box (boxes, fallback_box, &fb))
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+render_boxes (cairo_xlib_surface_t     *dst,
+             const cairo_pattern_t     *pattern,
+             cairo_boxes_t             *boxes)
+{
+    double pad;
+
+    if (_cairo_pattern_analyze_filter (pattern, &pad) != CAIRO_FILTER_NEAREST)
+       return fallback_boxes (dst, pattern, boxes);
+
+    switch (pattern->extend) {
+    default:
+    case CAIRO_EXTEND_NONE:
+    case CAIRO_EXTEND_REFLECT:
+    case CAIRO_EXTEND_PAD:
+       return fallback_boxes (dst, pattern, boxes);
+
+    case CAIRO_EXTEND_REPEAT: /* XXX Use tiling */
+       return fallback_boxes (dst, pattern, boxes);
+    }
+}
+
+/* the mid-level: converts boxes into drawing operations */
+
+struct _box_data {
+    Display *dpy;
+    cairo_xlib_surface_t *dst;
+    cairo_surface_t *src;
+    GC gc;
+    int tx, ty;
+    int width, height;
+};
+
+static cairo_bool_t source_contains_box (cairo_box_t *box, void *closure)
+{
+    struct _box_data *data = closure;
+
+    /* The box is pixel-aligned so the truncation is safe. */
+    return
+       _cairo_fixed_integer_part (box->p1.x) + data->tx >= 0 &&
+       _cairo_fixed_integer_part (box->p1.y) + data->ty >= 0 &&
+       _cairo_fixed_integer_part (box->p2.x) + data->tx <= data->width &&
+       _cairo_fixed_integer_part (box->p2.y) + data->ty <= data->height;
+}
+
+static cairo_bool_t image_upload_box (cairo_box_t *box, void *closure)
+{
+    const struct _box_data *iub = closure;
+    int x = _cairo_fixed_integer_part (box->p1.x);
+    int y = _cairo_fixed_integer_part (box->p1.y);
+    int width  = _cairo_fixed_integer_part (box->p2.x - box->p1.x);
+    int height = _cairo_fixed_integer_part (box->p2.y - box->p1.y);
+
+    return _cairo_xlib_surface_draw_image (iub->dst,
+                                          (cairo_image_surface_t *)iub->src,
+                                          x + iub->tx, y + iub->ty,
+                                          width, height,
+                                          x, y) == CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+upload_image_inplace (cairo_xlib_surface_t *dst,
+                     const cairo_pattern_t *source,
+                     cairo_boxes_t *boxes)
+{
+    const cairo_surface_pattern_t *pattern;
+    struct _box_data iub;
+    cairo_image_surface_t *image;
+
+    if (source->type != CAIRO_PATTERN_TYPE_SURFACE)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    pattern = (const cairo_surface_pattern_t *) source;
+    if (pattern->surface->type != CAIRO_SURFACE_TYPE_IMAGE)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    image = (cairo_image_surface_t *) pattern->surface;
+    if (image->format == CAIRO_FORMAT_INVALID)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    if (image->depth != dst->depth)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    /* XXX subsurface */
+
+    if (! _cairo_matrix_is_integer_translation (&source->matrix,
+                                               &iub.tx, &iub.ty))
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    iub.dst = dst;
+    iub.src = &image->base;
+    iub.width  = image->width;
+    iub.height = image->height;
+
+    /* First check that the data is entirely within the image */
+    if (! _cairo_boxes_for_each_box (boxes, source_contains_box, &iub))
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    if (! _cairo_boxes_for_each_box (boxes, image_upload_box, &iub))
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_bool_t copy_box (cairo_box_t *box, void *closure)
+{
+    const struct _box_data *cb = closure;
+    int x = _cairo_fixed_integer_part (box->p1.x);
+    int y = _cairo_fixed_integer_part (box->p1.y);
+    int width  = _cairo_fixed_integer_part (box->p2.x - box->p1.x);
+    int height = _cairo_fixed_integer_part (box->p2.y - box->p1.y);
+
+    XCopyArea (cb->dpy,
+              ((cairo_xlib_surface_t *)cb->src)->drawable,
+              cb->dst->drawable,
+              cb->gc,
+              x + cb->tx, y + cb->ty,
+              width, height,
+              x, y);
+    return TRUE;
+}
+
+static cairo_status_t
+copy_boxes (cairo_xlib_surface_t *dst,
+           const cairo_pattern_t *source,
+           cairo_boxes_t *boxes)
+{
+    const cairo_surface_pattern_t *pattern;
+    struct _box_data cb;
+    cairo_xlib_surface_t *src;
+    cairo_status_t status;
+
+    if (source->type != CAIRO_PATTERN_TYPE_SURFACE)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    pattern = (const cairo_surface_pattern_t *) source;
+    if (pattern->surface->type != CAIRO_SURFACE_TYPE_XLIB)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    src = (cairo_xlib_surface_t *) pattern->surface;
+    if (src->depth != dst->depth)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    if (! _cairo_xlib_surface_same_screen (dst, src))
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    /* XXX subsurface */
+
+    if (! _cairo_matrix_is_integer_translation (&source->matrix,
+                                               &cb.tx, &cb.ty))
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    cb.dpy = dst->display->display;
+    cb.dst = dst;
+    cb.src = &src->base;
+    cb.width  = src->width;
+    cb.height = src->height;
+
+    /* First check that the data is entirely within the image */
+    if (! _cairo_boxes_for_each_box (boxes, source_contains_box, &cb))
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    status = _cairo_xlib_surface_get_gc (dst->display, dst, &cb.gc);
+    if (unlikely (status))
+       return status;
+
+    if (! _cairo_boxes_for_each_box (boxes, copy_box, &cb))
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    _cairo_xlib_surface_put_gc (dst->display, dst, cb.gc);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+draw_boxes (cairo_composite_rectangles_t *extents,
+           cairo_boxes_t *boxes)
+{
+    cairo_xlib_surface_t *dst = (cairo_xlib_surface_t *)extents->surface;
+    cairo_operator_t op = extents->op;
+    const cairo_pattern_t *src = &extents->source_pattern.base;
+    cairo_int_status_t status;
+
+    if (boxes->num_boxes == 0 && extents->is_bounded)
+       return CAIRO_STATUS_SUCCESS;
+
+    if (! boxes->is_pixel_aligned)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    if (op == CAIRO_OPERATOR_CLEAR)
+       op = CAIRO_OPERATOR_SOURCE;
+
+    if (_cairo_pattern_is_opaque (src, &extents->bounded))
+       op = CAIRO_OPERATOR_SOURCE;
+
+    if (dst->base.is_clear && op == CAIRO_OPERATOR_OVER)
+       op = CAIRO_OPERATOR_SOURCE;
+
+    if (op != CAIRO_OPERATOR_SOURCE)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    status = acquire (dst);
+    if (unlikely (status))
+       return status;
+
+    if (src->type == CAIRO_PATTERN_TYPE_SOLID) {
+       status = fill_boxes (dst,
+                            &((cairo_solid_pattern_t *) src)->color,
+                            boxes);
+    } else {
+       status = upload_image_inplace (dst, src, boxes);
+       if (status == CAIRO_INT_STATUS_UNSUPPORTED)
+           status = copy_boxes (dst, src, boxes);
+       if (status == CAIRO_INT_STATUS_UNSUPPORTED)
+           status = render_boxes (dst, src, boxes);
+    }
+
+    release (dst);
+
+    return status;
+}
+
+/* high-level compositor interface */
+
+static cairo_int_status_t
+_cairo_xlib_core_compositor_paint (const cairo_compositor_t    *compositor,
+                                  cairo_composite_rectangles_t *extents)
+{
+    cairo_int_status_t status;
+
+    status = CAIRO_INT_STATUS_UNSUPPORTED;
+    if (_cairo_clip_is_region (extents->clip)) {
+       cairo_boxes_t boxes;
+
+        _cairo_clip_steal_boxes (extents->clip, &boxes);
+        status = draw_boxes (extents, &boxes);
+        _cairo_clip_unsteal_boxes (extents->clip, &boxes);
+    }
+
+    return status;
+}
+
+static cairo_int_status_t
+_cairo_xlib_core_compositor_stroke (const cairo_compositor_t   *compositor,
+                                   cairo_composite_rectangles_t *extents,
+                                   const cairo_path_fixed_t    *path,
+                                   const cairo_stroke_style_t  *style,
+                                   const cairo_matrix_t        *ctm,
+                                   const cairo_matrix_t        *ctm_inverse,
+                                   double                       tolerance,
+                                   cairo_antialias_t            antialias)
+{
+    cairo_int_status_t status;
+
+    status = CAIRO_INT_STATUS_UNSUPPORTED;
+    if (_cairo_path_fixed_stroke_is_rectilinear (path)) {
+       cairo_boxes_t boxes;
+
+       _cairo_boxes_init_with_clip (&boxes, extents->clip);
+       status = _cairo_path_fixed_stroke_rectilinear_to_boxes (path,
+                                                               style,
+                                                               ctm,
+                                                               antialias,
+                                                               &boxes);
+       if (likely (status == CAIRO_INT_STATUS_SUCCESS))
+           status = draw_boxes (extents, &boxes);
+       _cairo_boxes_fini (&boxes);
+    }
+
+    return status;
+}
+
+static cairo_int_status_t
+_cairo_xlib_core_compositor_fill (const cairo_compositor_t     *compositor,
+                                 cairo_composite_rectangles_t  *extents,
+                                 const cairo_path_fixed_t      *path,
+                                 cairo_fill_rule_t              fill_rule,
+                                 double                         tolerance,
+                                 cairo_antialias_t              antialias)
+{
+    cairo_int_status_t status;
+
+    status = CAIRO_INT_STATUS_UNSUPPORTED;
+    if (_cairo_path_fixed_fill_is_rectilinear (path)) {
+       cairo_boxes_t boxes;
+
+       _cairo_boxes_init_with_clip (&boxes, extents->clip);
+       status = _cairo_path_fixed_fill_rectilinear_to_boxes (path,
+                                                             fill_rule,
+                                                             antialias,
+                                                             &boxes);
+       if (likely (status == CAIRO_INT_STATUS_SUCCESS))
+           status = draw_boxes (extents, &boxes);
+       _cairo_boxes_fini (&boxes);
+    }
+
+    return status;
+}
+
+const cairo_compositor_t *
+_cairo_xlib_core_compositor_get (void)
+{
+    static cairo_compositor_t compositor;
+
+    if (compositor.delegate == NULL) {
+       compositor.delegate = _cairo_xlib_fallback_compositor_get ();
+
+       compositor.paint = _cairo_xlib_core_compositor_paint;
+       compositor.mask  = NULL;
+       compositor.fill  = _cairo_xlib_core_compositor_fill;
+       compositor.stroke = _cairo_xlib_core_compositor_stroke;
+       compositor.glyphs = NULL; /* XXX PolyGlyph? */
+    }
+
+    return &compositor;
+}
+
+#endif /* !CAIRO_HAS_XLIB_XCB_FUNCTIONS */
diff --git a/src/cairo-xlib-display.c b/src/cairo-xlib-display.c
new file mode 100644 (file)
index 0000000..fd7253c
--- /dev/null
@@ -0,0 +1,588 @@
+/* Cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2007 Chris Wilson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Chris Wilson.
+ *
+ * Contributor(s):
+ *     Karl Tomlinson <karlt+@karlt.net>, Mozilla Corporation
+ */
+
+#include "cairoint.h"
+
+#if !CAIRO_HAS_XLIB_XCB_FUNCTIONS
+
+#include "cairo-xlib-private.h"
+#include "cairo-xlib-xrender-private.h"
+#include "cairo-freelist-private.h"
+#include "cairo-error-private.h"
+
+#include <X11/Xlibint.h>       /* For XESetCloseDisplay */
+
+typedef int (*cairo_xlib_error_func_t) (Display     *display,
+                                       XErrorEvent *event);
+
+static cairo_xlib_display_t *_cairo_xlib_display_list;
+
+static int
+_noop_error_handler (Display     *display,
+                    XErrorEvent *event)
+{
+    return False;              /* return value is ignored */
+}
+
+static void
+_cairo_xlib_display_finish (void *abstract_display)
+{
+    cairo_xlib_display_t *display = abstract_display;
+    Display *dpy = display->display;
+
+    if (! cairo_device_acquire (&display->base)) {
+       cairo_xlib_error_func_t old_handler;
+
+       /* protect the notifies from triggering XErrors */
+       XSync (dpy, False);
+       old_handler = XSetErrorHandler (_noop_error_handler);
+
+       while (! cairo_list_is_empty (&display->fonts)) {
+           _cairo_xlib_font_close (cairo_list_first_entry (&display->fonts,
+                                                           cairo_xlib_font_t,
+                                                           link));
+       }
+
+       while (! cairo_list_is_empty (&display->screens)) {
+           _cairo_xlib_screen_destroy (display,
+                                       cairo_list_first_entry (&display->screens,
+                                                               cairo_xlib_screen_t,
+                                                               link));
+       }
+
+       XSync (dpy, False);
+       XSetErrorHandler (old_handler);
+
+       cairo_device_release (&display->base);
+    }
+}
+
+static void
+_cairo_xlib_display_destroy (void *abstract_display)
+{
+    cairo_xlib_display_t *display = abstract_display;
+
+    free (display);
+}
+
+static int
+_cairo_xlib_close_display (Display *dpy, XExtCodes *codes)
+{
+    cairo_xlib_display_t *display, **prev, *next;
+
+    CAIRO_MUTEX_LOCK (_cairo_xlib_display_mutex);
+    for (display = _cairo_xlib_display_list; display; display = display->next)
+       if (display->display == dpy)
+           break;
+    CAIRO_MUTEX_UNLOCK (_cairo_xlib_display_mutex);
+    if (display == NULL)
+       return 0;
+
+    cairo_device_finish (&display->base);
+
+    /*
+     * Unhook from the global list
+     */
+    CAIRO_MUTEX_LOCK (_cairo_xlib_display_mutex);
+    prev = &_cairo_xlib_display_list;
+    for (display = _cairo_xlib_display_list; display; display = next) {
+       next = display->next;
+       if (display->display == dpy) {
+           *prev = next;
+           break;
+       } else
+           prev = &display->next;
+    }
+    CAIRO_MUTEX_UNLOCK (_cairo_xlib_display_mutex);
+
+    display->display = NULL; /* catch any later invalid access */
+    cairo_device_destroy (&display->base);
+
+    /* Return value in accordance with requirements of
+     * XESetCloseDisplay */
+    return 0;
+}
+
+static const cairo_device_backend_t _cairo_xlib_device_backend = {
+    CAIRO_DEVICE_TYPE_XLIB,
+
+    NULL,
+    NULL,
+
+    NULL, /* flush */
+    _cairo_xlib_display_finish,
+    _cairo_xlib_display_destroy,
+};
+
+/**
+ * cairo_xlib_device_create:
+ * @dpy: the display to create the device for
+ *
+ * Gets the device belonging to @dpy, or creates it if it doesn't exist yet.
+ *
+ * Returns: the device belonging to @dpy
+ **/
+cairo_device_t *
+_cairo_xlib_device_create (Display *dpy)
+{
+    cairo_xlib_display_t *display;
+    cairo_xlib_display_t **prev;
+    cairo_device_t *device;
+    XExtCodes *codes;
+    const char *env;
+
+    CAIRO_MUTEX_INITIALIZE ();
+
+    /* There is an apparent deadlock between this mutex and the
+     * mutex for the display, but it's actually safe. For the
+     * app to call XCloseDisplay() while any other thread is
+     * inside this function would be an error in the logic
+     * app, and the CloseDisplay hook is the only other place we
+     * acquire this mutex.
+     */
+    CAIRO_MUTEX_LOCK (_cairo_xlib_display_mutex);
+
+    for (prev = &_cairo_xlib_display_list; (display = *prev); prev = &(*prev)->next)
+    {
+       if (display->display == dpy) {
+           /*
+            * MRU the list
+            */
+           if (prev != &_cairo_xlib_display_list) {
+               *prev = display->next;
+               display->next = _cairo_xlib_display_list;
+               _cairo_xlib_display_list = display;
+           }
+            device = cairo_device_reference (&display->base);
+           goto UNLOCK;
+       }
+    }
+
+    display = malloc (sizeof (cairo_xlib_display_t));
+    if (unlikely (display == NULL)) {
+       device = _cairo_device_create_in_error (CAIRO_STATUS_NO_MEMORY);
+       goto UNLOCK;
+    }
+
+    /* Xlib calls out to the extension close_display hooks in LIFO
+     * order. So we have to ensure that all extensions that we depend
+     * on in our close_display hook are properly initialized before we
+     * add our hook. For now, that means Render, so we call into its
+     * QueryVersion function to ensure it gets initialized.
+     */
+    display->render_major = display->render_minor = -1;
+    XRenderQueryVersion (dpy, &display->render_major, &display->render_minor);
+    env = getenv ("CAIRO_DEBUG");
+    if (env != NULL && (env = strstr (env, "xrender-version=")) != NULL) {
+       int max_render_major, max_render_minor;
+
+       env += sizeof ("xrender-version=") - 1;
+       if (sscanf (env, "%d.%d", &max_render_major, &max_render_minor) != 2)
+           max_render_major = max_render_minor = -1;
+
+       if (max_render_major < display->render_major ||
+           (max_render_major == display->render_major &&
+            max_render_minor < display->render_minor))
+       {
+           display->render_major = max_render_major;
+           display->render_minor = max_render_minor;
+       }
+    }
+
+    codes = XAddExtension (dpy);
+    if (unlikely (codes == NULL)) {
+       device = _cairo_device_create_in_error (CAIRO_STATUS_NO_MEMORY);
+       free (display);
+       goto UNLOCK;
+    }
+
+    _cairo_device_init (&display->base, &_cairo_xlib_device_backend);
+
+    XESetCloseDisplay (dpy, codes->extension, _cairo_xlib_close_display);
+
+    cairo_device_reference (&display->base); /* add one for the CloseDisplay */
+    display->display = dpy;
+    cairo_list_init (&display->screens);
+    cairo_list_init (&display->fonts);
+    display->closed = FALSE;
+
+    display->white = NULL;
+    memset (display->alpha, 0, sizeof (display->alpha));
+    memset (display->solid, 0, sizeof (display->solid));
+    memset (display->solid_cache, 0, sizeof (display->solid_cache));
+    memset (display->last_solid_cache, 0, sizeof (display->last_solid_cache));
+
+    memset (display->cached_xrender_formats, 0,
+           sizeof (display->cached_xrender_formats));
+
+    display->force_precision = -1;
+
+    /* Prior to Render 0.10, there is no protocol support for gradients and
+     * we call function stubs instead, which would silently consume the drawing.
+     */
+#if RENDER_MAJOR == 0 && RENDER_MINOR < 10
+    display->buggy_gradients = TRUE;
+#else
+    display->buggy_gradients = FALSE;
+#endif
+    display->buggy_pad_reflect = FALSE;
+    display->buggy_repeat = FALSE;
+
+    /* This buggy_repeat condition is very complicated because there
+     * are multiple X server code bases (with multiple versioning
+     * schemes within a code base), and multiple bugs.
+     *
+     * The X servers:
+     *
+     *    1. The Vendor=="XFree86" code base with release numbers such
+     *    as 4.7.0 (VendorRelease==40700000).
+     *
+     *    2. The Vendor=="X.Org" code base (a descendant of the
+     *    XFree86 code base). It originally had things like
+     *    VendorRelease==60700000 for release 6.7.0 but then changed
+     *    its versioning scheme so that, for example,
+     *    VendorRelease==10400000 for the 1.4.0 X server within the
+     *    X.Org 7.3 release.
+     *
+     * The bugs:
+     *
+     *    1. The original bug that led to the buggy_repeat
+     *    workaround. This was a bug that Owen Taylor investigated,
+     *    understood well, and characterized against carious X
+     *    servers. Confirmed X servers with this bug include:
+     *
+     *         "XFree86" <= 40500000
+     *         "X.Org" <= 60802000 (only with old numbering >= 60700000)
+     *
+     *    2. A separate bug resulting in a crash of the X server when
+     *    using cairo's extend-reflect test case, (which, surprisingly
+     *    enough was not passing RepeatReflect to the X server, but
+     *    instead using RepeatNormal in a workaround). Nobody to date
+     *    has understood the bug well, but it appears to be gone as of
+     *    the X.Org 1.4.0 server. This bug is coincidentally avoided
+     *    by using the same buggy_repeat workaround. Confirmed X
+     *    servers with this bug include:
+     *
+     *         "X.org" == 60900000 (old versioning scheme)
+     *         "X.org"  < 10400000 (new numbering scheme)
+     *
+     *    For the old-versioning-scheme X servers we don't know
+     *    exactly when second the bug started, but since bug 1 is
+     *    present through 6.8.2 and bug 2 is present in 6.9.0 it seems
+     *    safest to just blacklist all old-versioning-scheme X servers,
+     *    (just using VendorRelease < 70000000), as buggy_repeat=TRUE.
+     */
+    if (strstr (ServerVendor (dpy), "X.Org") != NULL) {
+       if (VendorRelease (dpy) >= 60700000) {
+           if (VendorRelease (dpy) < 70000000)
+               display->buggy_repeat = TRUE;
+
+           /* We know that gradients simply do not work in early Xorg servers */
+           if (VendorRelease (dpy) < 70200000)
+               display->buggy_gradients = TRUE;
+
+           /* And the extended repeat modes were not fixed until much later */
+           display->buggy_pad_reflect = TRUE;
+       } else {
+           if (VendorRelease (dpy) < 10400000)
+               display->buggy_repeat = TRUE;
+
+           /* Too many bugs in the early drivers */
+           if (VendorRelease (dpy) < 10699000)
+               display->buggy_pad_reflect = TRUE;
+       }
+    } else if (strstr (ServerVendor (dpy), "XFree86") != NULL) {
+       if (VendorRelease (dpy) <= 40500000)
+           display->buggy_repeat = TRUE;
+
+       display->buggy_gradients = TRUE;
+       display->buggy_pad_reflect = TRUE;
+    }
+
+    if (display->render_major > 0 || display->render_minor >= 4)
+       display->compositor = _cairo_xlib_traps_compositor_get ();
+    else if (display->render_major > 0 || display->render_minor >= 0)
+       display->compositor = _cairo_xlib_mask_compositor_get ();
+    else
+       display->compositor = _cairo_xlib_core_compositor_get ();
+
+    display->next = _cairo_xlib_display_list;
+    _cairo_xlib_display_list = display;
+
+    device = &display->base;
+
+UNLOCK:
+    CAIRO_MUTEX_UNLOCK (_cairo_xlib_display_mutex);
+    return device;
+}
+
+cairo_status_t
+_cairo_xlib_display_acquire (cairo_device_t *device, cairo_xlib_display_t **display)
+{
+    cairo_status_t status;
+
+    status = cairo_device_acquire (device);
+    if (status)
+        return status;
+
+    *display = (cairo_xlib_display_t *) device;
+    return status;
+}
+
+XRenderPictFormat *
+_cairo_xlib_display_get_xrender_format_for_pixman(cairo_xlib_display_t *display,
+                                                 pixman_format_code_t format)
+{
+    Display *dpy = display->display;
+    XRenderPictFormat tmpl;
+    int mask;
+
+#define MASK(x) ((1<<(x))-1)
+
+    tmpl.depth = PIXMAN_FORMAT_DEPTH(format);
+    mask = PictFormatType | PictFormatDepth;
+
+    switch (PIXMAN_FORMAT_TYPE(format)) {
+    case PIXMAN_TYPE_ARGB:
+       tmpl.type = PictTypeDirect;
+
+       tmpl.direct.alphaMask = MASK(PIXMAN_FORMAT_A(format));
+       if (PIXMAN_FORMAT_A(format))
+           tmpl.direct.alpha = (PIXMAN_FORMAT_R(format) +
+                                PIXMAN_FORMAT_G(format) +
+                                PIXMAN_FORMAT_B(format));
+
+       tmpl.direct.redMask = MASK(PIXMAN_FORMAT_R(format));
+       tmpl.direct.red = (PIXMAN_FORMAT_G(format) +
+                          PIXMAN_FORMAT_B(format));
+
+       tmpl.direct.greenMask = MASK(PIXMAN_FORMAT_G(format));
+       tmpl.direct.green = PIXMAN_FORMAT_B(format);
+
+       tmpl.direct.blueMask = MASK(PIXMAN_FORMAT_B(format));
+       tmpl.direct.blue = 0;
+
+       mask |= PictFormatRed | PictFormatRedMask;
+       mask |= PictFormatGreen | PictFormatGreenMask;
+       mask |= PictFormatBlue | PictFormatBlueMask;
+       mask |= PictFormatAlpha | PictFormatAlphaMask;
+       break;
+
+    case PIXMAN_TYPE_ABGR:
+       tmpl.type = PictTypeDirect;
+
+       tmpl.direct.alphaMask = MASK(PIXMAN_FORMAT_A(format));
+       if (tmpl.direct.alphaMask)
+           tmpl.direct.alpha = (PIXMAN_FORMAT_B(format) +
+                                PIXMAN_FORMAT_G(format) +
+                                PIXMAN_FORMAT_R(format));
+
+       tmpl.direct.blueMask = MASK(PIXMAN_FORMAT_B(format));
+       tmpl.direct.blue = (PIXMAN_FORMAT_G(format) +
+                           PIXMAN_FORMAT_R(format));
+
+       tmpl.direct.greenMask = MASK(PIXMAN_FORMAT_G(format));
+       tmpl.direct.green = PIXMAN_FORMAT_R(format);
+
+       tmpl.direct.redMask = MASK(PIXMAN_FORMAT_R(format));
+       tmpl.direct.red = 0;
+
+       mask |= PictFormatRed | PictFormatRedMask;
+       mask |= PictFormatGreen | PictFormatGreenMask;
+       mask |= PictFormatBlue | PictFormatBlueMask;
+       mask |= PictFormatAlpha | PictFormatAlphaMask;
+       break;
+
+    case PIXMAN_TYPE_BGRA:
+       tmpl.type = PictTypeDirect;
+
+       tmpl.direct.blueMask = MASK(PIXMAN_FORMAT_B(format));
+       tmpl.direct.blue = (PIXMAN_FORMAT_BPP(format) - PIXMAN_FORMAT_B(format));
+
+       tmpl.direct.greenMask = MASK(PIXMAN_FORMAT_G(format));
+       tmpl.direct.green = (PIXMAN_FORMAT_BPP(format) - PIXMAN_FORMAT_B(format) -
+                            PIXMAN_FORMAT_G(format));
+
+       tmpl.direct.redMask = MASK(PIXMAN_FORMAT_R(format));
+       tmpl.direct.red = (PIXMAN_FORMAT_BPP(format) - PIXMAN_FORMAT_B(format) -
+                          PIXMAN_FORMAT_G(format) - PIXMAN_FORMAT_R(format));
+
+       tmpl.direct.alphaMask = MASK(PIXMAN_FORMAT_A(format));
+       tmpl.direct.alpha = 0;
+
+       mask |= PictFormatRed | PictFormatRedMask;
+       mask |= PictFormatGreen | PictFormatGreenMask;
+       mask |= PictFormatBlue | PictFormatBlueMask;
+       mask |= PictFormatAlpha | PictFormatAlphaMask;
+       break;
+
+    case PIXMAN_TYPE_A:
+       tmpl.type = PictTypeDirect;
+
+       tmpl.direct.alpha = 0;
+       tmpl.direct.alphaMask = MASK(PIXMAN_FORMAT_A(format));
+
+       mask |= PictFormatAlpha | PictFormatAlphaMask;
+       break;
+
+    case PIXMAN_TYPE_COLOR:
+    case PIXMAN_TYPE_GRAY:
+       /* XXX Find matching visual/colormap */
+       tmpl.type = PictTypeIndexed;
+       //tmpl.colormap = screen->visuals[PIXMAN_FORMAT_VIS(format)].vid;
+       //mask |= PictFormatColormap;
+       return NULL;
+    }
+#undef MASK
+
+    /* XXX caching? */
+    return XRenderFindFormat(dpy, mask, &tmpl, 1);
+}
+
+XRenderPictFormat *
+_cairo_xlib_display_get_xrender_format (cairo_xlib_display_t   *display,
+                                       cairo_format_t           format)
+{
+    XRenderPictFormat *xrender_format;
+
+#if ! ATOMIC_OP_NEEDS_MEMORY_BARRIER
+    xrender_format = display->cached_xrender_formats[format];
+    if (likely (xrender_format != NULL))
+       return xrender_format;
+#endif
+
+    xrender_format = display->cached_xrender_formats[format];
+    if (xrender_format == NULL) {
+       int pict_format;
+
+       switch (format) {
+       case CAIRO_FORMAT_A1:
+           pict_format = PictStandardA1; break;
+       case CAIRO_FORMAT_A8:
+           pict_format = PictStandardA8; break;
+       case CAIRO_FORMAT_RGB24:
+           pict_format = PictStandardRGB24; break;
+       case CAIRO_FORMAT_RGB16_565:
+           xrender_format = _cairo_xlib_display_get_xrender_format_for_pixman(display,
+                                                                              PIXMAN_r5g6b5);
+           break;
+       case CAIRO_FORMAT_RGB30:
+           xrender_format = _cairo_xlib_display_get_xrender_format_for_pixman(display,
+                                                                              PIXMAN_x2r10g10b10);
+           break;
+       case CAIRO_FORMAT_INVALID:
+       default:
+           ASSERT_NOT_REACHED;
+       case CAIRO_FORMAT_ARGB32:
+           pict_format = PictStandardARGB32; break;
+       }
+       if (!xrender_format)
+           xrender_format = XRenderFindStandardFormat (display->display,
+                                                       pict_format);
+       display->cached_xrender_formats[format] = xrender_format;
+    }
+
+    return xrender_format;
+}
+
+cairo_xlib_screen_t *
+_cairo_xlib_display_get_screen (cairo_xlib_display_t *display,
+                               Screen *screen)
+{
+    cairo_xlib_screen_t *info;
+
+    cairo_list_foreach_entry (info, cairo_xlib_screen_t, &display->screens, link) {
+       if (info->screen == screen) {
+            if (display->screens.next != &info->link)
+                cairo_list_move (&info->link, &display->screens);
+            return info;
+        }
+    }
+
+    return NULL;
+}
+
+cairo_bool_t
+_cairo_xlib_display_has_repeat (cairo_device_t *device)
+{
+    return ! ((cairo_xlib_display_t *) device)->buggy_repeat;
+}
+
+cairo_bool_t
+_cairo_xlib_display_has_reflect (cairo_device_t *device)
+{
+    return ! ((cairo_xlib_display_t *) device)->buggy_pad_reflect;
+}
+
+cairo_bool_t
+_cairo_xlib_display_has_gradients (cairo_device_t *device)
+{
+    return ! ((cairo_xlib_display_t *) device)->buggy_gradients;
+}
+
+void
+cairo_xlib_device_debug_set_precision (cairo_device_t *device,
+                                      int precision)
+{
+    if (device == NULL || device->status)
+       return;
+    if (device->backend->type != CAIRO_DEVICE_TYPE_XLIB) {
+       cairo_status_t status;
+
+       status = _cairo_device_set_error (device, CAIRO_STATUS_DEVICE_TYPE_MISMATCH);
+       (void) status;
+       return;
+    }
+
+    ((cairo_xlib_display_t *) device)->force_precision = precision;
+}
+
+int
+cairo_xlib_device_debug_get_precision (cairo_device_t *device)
+{
+    if (device == NULL || device->status)
+       return -1;
+    if (device->backend->type != CAIRO_DEVICE_TYPE_XLIB) {
+       cairo_status_t status;
+
+       status = _cairo_device_set_error (device, CAIRO_STATUS_DEVICE_TYPE_MISMATCH);
+       (void) status;
+       return -1;
+    }
+
+    return ((cairo_xlib_display_t *) device)->force_precision;
+}
+
+#endif /* !CAIRO_HAS_XLIB_XCB_FUNCTIONS */
diff --git a/src/cairo-xlib-fallback-compositor.c b/src/cairo-xlib-fallback-compositor.c
new file mode 100644 (file)
index 0000000..5391926
--- /dev/null
@@ -0,0 +1,54 @@
+/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ * Copyright © 2005 Red Hat, Inc.
+ * Copyright © 2011 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *     Carl D. Worth <cworth@cworth.org>
+ *     Behdad Esfahbod <behdad@behdad.org>
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ *     Karl Tomlinson <karlt+@karlt.net>, Mozilla Corporation
+ */
+
+#include "cairoint.h"
+
+#include "cairo-xlib-private.h"
+
+#include "cairo-compositor-private.h"
+
+const cairo_compositor_t *
+_cairo_xlib_fallback_compositor_get (void)
+{
+    /* XXX Do something interesting here to mitigate fallbacks ala xcb */
+    return &_cairo_fallback_compositor;
+}
diff --git a/src/cairo-xlib-private.h b/src/cairo-xlib-private.h
new file mode 100644 (file)
index 0000000..4522832
--- /dev/null
@@ -0,0 +1,384 @@
+/* Cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributors(s):
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ *     Karl Tomlinson <karlt+@karlt.net>, Mozilla Corporation
+ */
+
+#ifndef CAIRO_XLIB_PRIVATE_H
+#define CAIRO_XLIB_PRIVATE_H
+
+#include "cairo-xlib.h"
+#include "cairo-xlib-xrender-private.h"
+
+#include "cairo-compiler-private.h"
+#include "cairo-device-private.h"
+#include "cairo-freelist-type-private.h"
+#include "cairo-list-private.h"
+#include "cairo-reference-count-private.h"
+#include "cairo-types-private.h"
+#include "cairo-scaled-font-private.h"
+#include "cairo-surface-private.h"
+
+#include <pixman.h>
+
+typedef struct _cairo_xlib_display cairo_xlib_display_t;
+typedef struct _cairo_xlib_screen cairo_xlib_screen_t;
+typedef struct _cairo_xlib_source cairo_xlib_source_t;
+typedef struct _cairo_xlib_surface cairo_xlib_surface_t;
+
+/* size of color cube */
+#define CUBE_SIZE 6
+/* size of gray ramp */
+#define RAMP_SIZE 16
+/* maximum number of cached GC's */
+#define GC_CACHE_SIZE 4
+
+struct _cairo_xlib_display {
+    cairo_device_t base;
+
+    cairo_xlib_display_t *next;
+
+    Display *display;
+    cairo_list_t screens;
+    cairo_list_t fonts;
+
+    const cairo_compositor_t *compositor;
+
+    int render_major;
+    int render_minor;
+    XRenderPictFormat *cached_xrender_formats[CAIRO_FORMAT_RGB16_565 + 1];
+
+    int force_precision;
+
+    cairo_surface_t *white;
+    cairo_surface_t *alpha[256];
+    cairo_surface_t *solid[32];
+    uint32_t solid_cache[32]; /* low 16 are opaque, high 16 transparent */
+    struct {
+       uint32_t color;
+       int index;
+    } last_solid_cache[2];
+
+    /* TRUE if the server has a bug with repeating pictures
+     *
+     *  https://bugs.freedesktop.org/show_bug.cgi?id=3566
+     *
+     * We can't test for this because it depends on whether the
+     * picture is in video memory or not.
+     *
+     * We also use this variable as a guard against a second
+     * independent bug with transformed repeating pictures:
+     *
+     * http://lists.freedesktop.org/archives/cairo/2004-September/001839.html
+     *
+     * Both are fixed in xorg >= 6.9 and hopefully in > 6.8.2, so
+     * we can reuse the test for now.
+     */
+    unsigned int buggy_gradients : 1;
+    unsigned int buggy_pad_reflect : 1;
+    unsigned int buggy_repeat : 1;
+    unsigned int closed :1;
+};
+
+typedef struct _cairo_xlib_visual_info {
+    cairo_list_t link;
+    VisualID visualid;
+    struct { uint8_t a, r, g, b; } colors[256];
+    uint8_t cube_to_pseudocolor[CUBE_SIZE][CUBE_SIZE][CUBE_SIZE];
+    uint8_t field8_to_cube[256];
+    int8_t  dither8_to_cube[256];
+    uint8_t gray8_to_pseudocolor[256];
+} cairo_xlib_visual_info_t;
+
+struct _cairo_xlib_screen {
+    cairo_list_t link;
+
+    cairo_device_t *device;
+    Screen *screen;
+
+    cairo_list_t surfaces;
+
+    cairo_bool_t has_font_options;
+    cairo_font_options_t font_options;
+
+    GC gc[GC_CACHE_SIZE];
+    uint8_t gc_depths[GC_CACHE_SIZE];
+
+    cairo_list_t visuals;
+};
+
+enum {
+    GLYPHSET_INDEX_ARGB32,
+    GLYPHSET_INDEX_A8,
+    GLYPHSET_INDEX_A1,
+    NUM_GLYPHSETS
+};
+
+typedef struct _cairo_xlib_font_glyphset {
+    GlyphSet           glyphset;
+    cairo_format_t     format;
+    XRenderPictFormat  *xrender_format;
+    struct _cairo_xlib_font_glyphset_free_glyphs {
+       int             count;
+       unsigned long   indices[128];
+    } to_free;
+} cairo_xlib_font_glyphset_t;
+
+typedef struct _cairo_xlib_font {
+    cairo_scaled_font_private_t                base;
+    cairo_scaled_font_t                        *font;
+    cairo_device_t                     *device;
+    cairo_list_t                       link;
+    cairo_xlib_font_glyphset_t         glyphset[NUM_GLYPHSETS];
+} cairo_xlib_font_t;
+
+struct _cairo_xlib_surface {
+    cairo_surface_t base;
+
+    Picture picture;
+
+    const cairo_compositor_t *compositor;
+
+    cairo_xlib_display_t *display;
+    cairo_xlib_screen_t *screen;
+    cairo_list_t link;
+
+    Display *dpy; /* only valid between acquire/release */
+    Drawable drawable;
+    cairo_bool_t owns_pixmap;
+    Visual *visual;
+
+    int use_pixmap;
+
+    int width;
+    int height;
+    int depth;
+
+    int precision;
+    XRenderPictFormat *xrender_format;
+    /* XXX pixman_format instead of masks? */
+    uint32_t a_mask;
+    uint32_t r_mask;
+    uint32_t g_mask;
+    uint32_t b_mask;
+
+    struct _cairo_xlib_source {
+       cairo_surface_t base;
+
+       Picture picture;
+       Display *dpy;
+
+       unsigned int filter:3;
+       unsigned int extend:3;
+       unsigned int has_matrix:1;
+       unsigned int has_component_alpha:1;
+    } embedded_source;
+};
+
+cairo_private cairo_status_t
+_cairo_xlib_surface_get_gc (cairo_xlib_display_t *display,
+                            cairo_xlib_surface_t *surface,
+                            GC                   *gc);
+
+cairo_private cairo_device_t *
+_cairo_xlib_device_create (Display *display);
+
+cairo_private cairo_xlib_screen_t *
+_cairo_xlib_display_get_screen (cairo_xlib_display_t *display,
+                               Screen *screen);
+
+cairo_private cairo_status_t
+_cairo_xlib_display_acquire (cairo_device_t *device,
+                             cairo_xlib_display_t **display);
+
+cairo_private cairo_bool_t
+_cairo_xlib_display_has_repeat (cairo_device_t *device);
+
+cairo_private cairo_bool_t
+_cairo_xlib_display_has_reflect (cairo_device_t *device);
+
+cairo_private cairo_bool_t
+_cairo_xlib_display_has_gradients (cairo_device_t *device);
+
+cairo_private void
+_cairo_xlib_display_set_precision(cairo_device_t *device,
+                                 int precision);
+
+cairo_private XRenderPictFormat *
+_cairo_xlib_display_get_xrender_format (cairo_xlib_display_t   *display,
+                                       cairo_format_t           format);
+
+cairo_private XRenderPictFormat *
+_cairo_xlib_display_get_xrender_format_for_pixman (cairo_xlib_display_t *display,
+                                                  pixman_format_code_t format);
+
+cairo_private cairo_status_t
+_cairo_xlib_screen_get (Display *dpy,
+                       Screen *screen,
+                       cairo_xlib_screen_t **out);
+
+cairo_private void
+_cairo_xlib_screen_destroy (cairo_xlib_display_t *display,
+                           cairo_xlib_screen_t *info);
+
+cairo_private GC
+_cairo_xlib_screen_get_gc (cairo_xlib_display_t *display,
+                           cairo_xlib_screen_t *info,
+                          int depth,
+                          Drawable drawable);
+cairo_private void
+_cairo_xlib_screen_put_gc (cairo_xlib_display_t *display,
+                          cairo_xlib_screen_t *info,
+                          int depth,
+                          GC gc);
+
+cairo_private cairo_font_options_t *
+_cairo_xlib_screen_get_font_options (cairo_xlib_screen_t *info);
+
+cairo_private cairo_status_t
+_cairo_xlib_screen_get_visual_info (cairo_xlib_display_t *display,
+                                    cairo_xlib_screen_t *info,
+                                   Visual *visual,
+                                   cairo_xlib_visual_info_t **out);
+
+cairo_private cairo_status_t
+_cairo_xlib_visual_info_create (Display *dpy,
+                               int screen,
+                               VisualID visualid,
+                               cairo_xlib_visual_info_t **out);
+
+cairo_private void
+_cairo_xlib_visual_info_destroy (cairo_xlib_visual_info_t *info);
+
+cairo_private const cairo_compositor_t *
+_cairo_xlib_core_compositor_get (void);
+
+cairo_private const cairo_compositor_t *
+_cairo_xlib_fallback_compositor_get (void);
+
+cairo_private const cairo_compositor_t *
+_cairo_xlib_mask_compositor_get (void);
+
+cairo_private const cairo_compositor_t *
+_cairo_xlib_traps_compositor_get (void);
+
+cairo_private void
+_cairo_xlib_surface_ensure_picture (cairo_xlib_surface_t    *surface);
+
+cairo_private void
+_cairo_xlib_surface_set_precision (cairo_xlib_surface_t        *surface,
+                                  cairo_antialias_t     antialias);
+
+cairo_private cairo_int_status_t
+_cairo_xlib_surface_set_attributes (cairo_xlib_display_t       *display,
+                                    cairo_xlib_surface_t       *surface,
+                                   cairo_surface_attributes_t *attributes,
+                                   double                      xc,
+                                   double                      yc);
+
+cairo_private cairo_status_t
+_cairo_xlib_surface_draw_image (cairo_xlib_surface_t   *surface,
+                               cairo_image_surface_t  *image,
+                               int                    src_x,
+                               int                    src_y,
+                               int                    width,
+                               int                    height,
+                               int                    dst_x,
+                               int                    dst_y);
+
+cairo_private cairo_surface_t *
+_cairo_xlib_source_create_for_pattern (cairo_surface_t *dst,
+                                      const cairo_pattern_t *pattern,
+                                      cairo_bool_t is_mask,
+                                      const cairo_rectangle_int_t *extents,
+                                      const cairo_rectangle_int_t *sample,
+                                      int *src_x, int *src_y);
+
+cairo_private void
+_cairo_xlib_font_close (cairo_xlib_font_t *font);
+
+#define CAIRO_RENDER_AT_LEAST(surface, major, minor)   \
+       (((surface)->render_major > major) ||                   \
+        (((surface)->render_major == major) && ((surface)->render_minor >= minor)))
+
+#define CAIRO_RENDER_HAS_CREATE_PICTURE(surface)               CAIRO_RENDER_AT_LEAST((surface), 0, 0)
+#define CAIRO_RENDER_HAS_COMPOSITE(surface)            CAIRO_RENDER_AT_LEAST((surface), 0, 0)
+#define CAIRO_RENDER_HAS_COMPOSITE_TEXT(surface)       CAIRO_RENDER_AT_LEAST((surface), 0, 0)
+
+#define CAIRO_RENDER_HAS_FILL_RECTANGLES(surface)              CAIRO_RENDER_AT_LEAST((surface), 0, 1)
+
+#define CAIRO_RENDER_HAS_DISJOINT(surface)                     CAIRO_RENDER_AT_LEAST((surface), 0, 2)
+#define CAIRO_RENDER_HAS_CONJOINT(surface)                     CAIRO_RENDER_AT_LEAST((surface), 0, 2)
+
+#define CAIRO_RENDER_HAS_TRAPEZOIDS(surface)           CAIRO_RENDER_AT_LEAST((surface), 0, 4)
+#define CAIRO_RENDER_HAS_TRIANGLES(surface)            CAIRO_RENDER_AT_LEAST((surface), 0, 4)
+#define CAIRO_RENDER_HAS_TRISTRIP(surface)                     CAIRO_RENDER_AT_LEAST((surface), 0, 4)
+#define CAIRO_RENDER_HAS_TRIFAN(surface)                       CAIRO_RENDER_AT_LEAST((surface), 0, 4)
+
+#define CAIRO_RENDER_HAS_PICTURE_TRANSFORM(surface)    CAIRO_RENDER_AT_LEAST((surface), 0, 6)
+#define CAIRO_RENDER_HAS_FILTERS(surface)      CAIRO_RENDER_AT_LEAST((surface), 0, 6)
+
+#define CAIRO_RENDER_HAS_EXTENDED_REPEAT(surface)      CAIRO_RENDER_AT_LEAST((surface), 0, 10)
+#define CAIRO_RENDER_HAS_GRADIENTS(surface)    CAIRO_RENDER_AT_LEAST((surface), 0, 10)
+
+#define CAIRO_RENDER_HAS_PDF_OPERATORS(surface)        CAIRO_RENDER_AT_LEAST((surface), 0, 11)
+
+#define CAIRO_RENDER_SUPPORTS_OPERATOR(surface, op)    \
+     ((op) <= CAIRO_OPERATOR_SATURATE ||                       \
+      (CAIRO_RENDER_HAS_PDF_OPERATORS(surface) &&      \
+       (op) <= CAIRO_OPERATOR_HSL_LUMINOSITY))
+
+/*
+ * Return whether two xlib surfaces share the same
+ * screen.  Both core and Render drawing require this
+ * when using multiple drawables in an operation.
+ */
+static inline cairo_bool_t
+_cairo_xlib_surface_same_screen (cairo_xlib_surface_t *dst,
+                                cairo_xlib_surface_t *src)
+{
+    return dst->screen == src->screen;
+}
+
+static inline void
+_cairo_xlib_surface_put_gc (cairo_xlib_display_t *display,
+                            cairo_xlib_surface_t *surface,
+                            GC                    gc)
+{
+    _cairo_xlib_screen_put_gc (display,
+                               surface->screen,
+                              surface->depth,
+                              gc);
+}
+
+#endif /* CAIRO_XLIB_PRIVATE_H */
diff --git a/src/cairo-xlib-render-compositor.c b/src/cairo-xlib-render-compositor.c
new file mode 100644 (file)
index 0000000..60d9984
--- /dev/null
@@ -0,0 +1,1689 @@
+/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ * Copyright © 2005 Red Hat, Inc.
+ * Copyright © 2011 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *     Carl D. Worth <cworth@cworth.org>
+ *     Behdad Esfahbod <behdad@behdad.org>
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ *     Karl Tomlinson <karlt+@karlt.net>, Mozilla Corporation
+ */
+
+#include "cairoint.h"
+
+#if !CAIRO_HAS_XLIB_XCB_FUNCTIONS
+
+#include "cairo-xlib-private.h"
+
+#include "cairo-compositor-private.h"
+#include "cairo-image-surface-private.h"
+#include "cairo-pattern-private.h"
+#include "cairo-traps-private.h"
+#include "cairo-tristrip-private.h"
+
+static cairo_int_status_t
+acquire (void *abstract_dst)
+{
+    cairo_xlib_surface_t *dst = abstract_dst;
+    cairo_int_status_t status;
+
+    status = _cairo_xlib_display_acquire (dst->base.device, &dst->display);
+    if (unlikely (status))
+        return status;
+
+    dst->dpy = dst->display->display;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+release (void *abstract_dst)
+{
+    cairo_xlib_surface_t *dst = abstract_dst;
+
+    cairo_device_release (&dst->display->base);
+    dst->dpy = NULL;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+set_clip_region (void *_surface,
+                cairo_region_t *region)
+{
+    cairo_xlib_surface_t *surface = _surface;
+
+    _cairo_xlib_surface_ensure_picture (surface);
+
+    if (region != NULL) {
+       XRectangle stack_rects[CAIRO_STACK_ARRAY_LENGTH (sizeof (XRectangle))];
+       XRectangle *rects = stack_rects;
+       int n_rects, i;
+
+       n_rects = cairo_region_num_rectangles (region);
+       if (n_rects > ARRAY_LENGTH (stack_rects)) {
+           rects = _cairo_malloc_ab (n_rects, sizeof (XRectangle));
+           if (unlikely (rects == NULL))
+               return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+       }
+       for (i = 0; i < n_rects; i++) {
+           cairo_rectangle_int_t rect;
+
+           cairo_region_get_rectangle (region, i, &rect);
+
+           rects[i].x = rect.x;
+           rects[i].y = rect.y;
+           rects[i].width  = rect.width;
+           rects[i].height = rect.height;
+       }
+       XRenderSetPictureClipRectangles (surface->dpy,
+                                        surface->picture,
+                                        0, 0,
+                                        rects, n_rects);
+       if (rects != stack_rects)
+           free (rects);
+    } else {
+       XRenderPictureAttributes pa;
+       pa.clip_mask = None;
+       XRenderChangePicture (surface->dpy,
+                             surface->picture,
+                             CPClipMask, &pa);
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+draw_image_boxes (void *_dst,
+                 cairo_image_surface_t *image,
+                 cairo_boxes_t *boxes,
+                 int dx, int dy)
+{
+    struct _cairo_boxes_chunk *chunk;
+    int i;
+
+    for (chunk = &boxes->chunks; chunk; chunk = chunk->next) {
+       for (i = 0; i < chunk->count; i++) {
+           cairo_box_t *b = &chunk->base[i];
+           int x1 = _cairo_fixed_integer_part (b->p1.x);
+           int y1 = _cairo_fixed_integer_part (b->p1.y);
+           int x2 = _cairo_fixed_integer_part (b->p2.x);
+           int y2 = _cairo_fixed_integer_part (b->p2.y);
+           if ( _cairo_xlib_surface_draw_image (_dst, image,
+                                                x1 + dx, y1 + dy,
+                                                x2 - x1, y2 - y1,
+                                                x1, y1))
+               return CAIRO_INT_STATUS_UNSUPPORTED;
+       }
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+copy_boxes (void *_dst,
+           cairo_surface_t *_src,
+           cairo_boxes_t *boxes,
+           const cairo_rectangle_int_t *extents,
+           int dx, int dy)
+{
+    cairo_xlib_surface_t *dst = _dst;
+    cairo_xlib_surface_t *src = (cairo_xlib_surface_t *)_src;
+    struct _cairo_boxes_chunk *chunk;
+    cairo_int_status_t status;
+    GC gc;
+    int i, j;
+
+    if (! _cairo_xlib_surface_same_screen  (dst, src))
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    if (dst->depth != src->depth)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    status = acquire (dst);
+    if (unlikely (status))
+       return status;
+
+    status = _cairo_xlib_surface_get_gc (dst->display, dst, &gc);
+    if (unlikely (status)) {
+       release (dst);
+       return status;
+    }
+
+    if (boxes->num_boxes == 1) {
+       int x1 = _cairo_fixed_integer_part (boxes->chunks.base[0].p1.x);
+       int y1 = _cairo_fixed_integer_part (boxes->chunks.base[0].p1.y);
+       int x2 = _cairo_fixed_integer_part (boxes->chunks.base[0].p2.x);
+       int y2 = _cairo_fixed_integer_part (boxes->chunks.base[0].p2.y);
+
+       XCopyArea (dst->dpy, src->drawable, dst->drawable, gc,
+                  x1 + dx, y1 + dy,
+                  x2 - x1, y2 - y1,
+                  x1,      y1);
+    } else {
+       XRectangle stack_rects[CAIRO_STACK_ARRAY_LENGTH (XRectangle)];
+       XRectangle *rects = stack_rects;
+
+       if (boxes->num_boxes > ARRAY_LENGTH (stack_rects)) {
+           rects = _cairo_malloc_ab (boxes->num_boxes, sizeof (XRectangle));
+           if (unlikely (rects == NULL))
+               return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+       }
+
+       j = 0;
+       for (chunk = &boxes->chunks; chunk; chunk = chunk->next) {
+           for (i = 0; i < chunk->count; i++) {
+               int x1 = _cairo_fixed_integer_part (chunk->base[i].p1.x);
+               int y1 = _cairo_fixed_integer_part (chunk->base[i].p1.y);
+               int x2 = _cairo_fixed_integer_part (chunk->base[i].p2.x);
+               int y2 = _cairo_fixed_integer_part (chunk->base[i].p2.y);
+
+               rects[j].x = x1;
+               rects[j].y = y1;
+               rects[j].width  = x2 - x1;
+               rects[j].height = y2 - y1;
+               j++;
+           }
+       }
+       assert (j == boxes->num_boxes);
+
+       XSetClipRectangles (dst->dpy, gc, 0, 0, rects, j, YSorted);
+
+       XCopyArea (dst->dpy, src->drawable, dst->drawable, gc,
+                  extents->x + dx, extents->y + dy,
+                  extents->width,  extents->height,
+                  extents->x,      extents->y);
+
+       XSetClipMask (dst->dpy, gc, None);
+
+       if (rects != stack_rects)
+           free (rects);
+    }
+
+    _cairo_xlib_surface_put_gc (dst->display, dst, gc);
+    release (dst);
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static int
+_render_operator (cairo_operator_t op)
+{
+    switch (op) {
+    case CAIRO_OPERATOR_CLEAR:
+       return PictOpClear;
+
+    case CAIRO_OPERATOR_SOURCE:
+       return PictOpSrc;
+    case CAIRO_OPERATOR_OVER:
+       return PictOpOver;
+    case CAIRO_OPERATOR_IN:
+       return PictOpIn;
+    case CAIRO_OPERATOR_OUT:
+       return PictOpOut;
+    case CAIRO_OPERATOR_ATOP:
+       return PictOpAtop;
+
+    case CAIRO_OPERATOR_DEST:
+       return PictOpDst;
+    case CAIRO_OPERATOR_DEST_OVER:
+       return PictOpOverReverse;
+    case CAIRO_OPERATOR_DEST_IN:
+       return PictOpInReverse;
+    case CAIRO_OPERATOR_DEST_OUT:
+       return PictOpOutReverse;
+    case CAIRO_OPERATOR_DEST_ATOP:
+       return PictOpAtopReverse;
+
+    case CAIRO_OPERATOR_XOR:
+       return PictOpXor;
+    case CAIRO_OPERATOR_ADD:
+       return PictOpAdd;
+    case CAIRO_OPERATOR_SATURATE:
+       return PictOpSaturate;
+
+    case CAIRO_OPERATOR_MULTIPLY:
+       return PictOpMultiply;
+    case CAIRO_OPERATOR_SCREEN:
+       return PictOpScreen;
+    case CAIRO_OPERATOR_OVERLAY:
+       return PictOpOverlay;
+    case CAIRO_OPERATOR_DARKEN:
+       return PictOpDarken;
+    case CAIRO_OPERATOR_LIGHTEN:
+       return PictOpLighten;
+    case CAIRO_OPERATOR_COLOR_DODGE:
+       return PictOpColorDodge;
+    case CAIRO_OPERATOR_COLOR_BURN:
+       return PictOpColorBurn;
+    case CAIRO_OPERATOR_HARD_LIGHT:
+       return PictOpHardLight;
+    case CAIRO_OPERATOR_SOFT_LIGHT:
+       return PictOpSoftLight;
+    case CAIRO_OPERATOR_DIFFERENCE:
+       return PictOpDifference;
+    case CAIRO_OPERATOR_EXCLUSION:
+       return PictOpExclusion;
+    case CAIRO_OPERATOR_HSL_HUE:
+       return PictOpHSLHue;
+    case CAIRO_OPERATOR_HSL_SATURATION:
+       return PictOpHSLSaturation;
+    case CAIRO_OPERATOR_HSL_COLOR:
+       return PictOpHSLColor;
+    case CAIRO_OPERATOR_HSL_LUMINOSITY:
+       return PictOpHSLLuminosity;
+
+    default:
+       ASSERT_NOT_REACHED;
+       return PictOpOver;
+    }
+}
+
+static cairo_bool_t
+fill_reduces_to_source (cairo_operator_t op,
+                       const cairo_color_t *color,
+                       cairo_xlib_surface_t *dst)
+{
+    if (dst->base.is_clear || CAIRO_COLOR_IS_OPAQUE (color))
+       return op == CAIRO_OPERATOR_OVER || op == CAIRO_OPERATOR_ADD;
+
+    return FALSE;
+}
+
+static cairo_int_status_t
+fill_rectangles (void                          *abstract_surface,
+                cairo_operator_t                op,
+                const cairo_color_t            *color,
+                cairo_rectangle_int_t          *rects,
+                int                             num_rects)
+{
+    cairo_xlib_surface_t *dst = abstract_surface;
+    XRenderColor render_color;
+    int i;
+
+    //X_DEBUG ((display->display, "fill_rectangles (dst=%x)", (unsigned int) surface->drawable));
+
+    render_color.red   = color->red_short;
+    render_color.green = color->green_short;
+    render_color.blue  = color->blue_short;
+    render_color.alpha = color->alpha_short;
+
+    if (fill_reduces_to_source (op, color, dst))
+       op = CAIRO_OPERATOR_SOURCE;
+
+    _cairo_xlib_surface_ensure_picture (dst);
+    if (num_rects == 1) {
+       /* Take advantage of the protocol compaction that libXrender performs
+        * to amalgamate sequences of XRenderFillRectangle().
+        */
+       XRenderFillRectangle (dst->dpy,
+                             _render_operator (op),
+                             dst->picture,
+                             &render_color,
+                             rects->x, rects->y,
+                             rects->width, rects->height);
+    } else {
+       XRectangle stack_xrects[CAIRO_STACK_ARRAY_LENGTH (XRectangle)];
+       XRectangle *xrects = stack_xrects;
+
+       if (num_rects > ARRAY_LENGTH (stack_xrects)) {
+           xrects = _cairo_malloc_ab (num_rects, sizeof (XRectangle));
+           if (unlikely (xrects == NULL))
+               return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+       }
+
+       for (i = 0; i < num_rects; i++) {
+           xrects[i].x = rects[i].x;
+           xrects[i].y = rects[i].y;
+           xrects[i].width  = rects[i].width;
+           xrects[i].height = rects[i].height;
+       }
+
+       XRenderFillRectangles (dst->dpy,
+                              _render_operator (op),
+                              dst->picture,
+                              &render_color, xrects, num_rects);
+
+       if (xrects != stack_xrects)
+           free (xrects);
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+fill_boxes (void               *abstract_surface,
+           cairo_operator_t     op,
+           const cairo_color_t *color,
+           cairo_boxes_t       *boxes)
+{
+    cairo_xlib_surface_t *dst = abstract_surface;
+    XRenderColor render_color;
+
+    render_color.red   = color->red_short;
+    render_color.green = color->green_short;
+    render_color.blue  = color->blue_short;
+    render_color.alpha = color->alpha_short;
+
+    if (fill_reduces_to_source (op, color, dst))
+       op = CAIRO_OPERATOR_SOURCE;
+
+    _cairo_xlib_surface_ensure_picture (dst);
+    if (boxes->num_boxes == 1) {
+       int x1 = _cairo_fixed_integer_part (boxes->chunks.base[0].p1.x);
+       int y1 = _cairo_fixed_integer_part (boxes->chunks.base[0].p1.y);
+       int x2 = _cairo_fixed_integer_part (boxes->chunks.base[0].p2.x);
+       int y2 = _cairo_fixed_integer_part (boxes->chunks.base[0].p2.y);
+
+       /* Take advantage of the protocol compaction that libXrender performs
+        * to amalgamate sequences of XRenderFillRectangle().
+        */
+       XRenderFillRectangle (dst->dpy,
+                             _render_operator (op),
+                             dst->picture,
+                             &render_color,
+                             x1, y1,
+                             x2 - x1, y2 - y1);
+    } else {
+       XRectangle stack_xrects[CAIRO_STACK_ARRAY_LENGTH (XRectangle)];
+       XRectangle *xrects = stack_xrects;
+       struct _cairo_boxes_chunk *chunk;
+       int i, j;
+
+       if (boxes->num_boxes > ARRAY_LENGTH (stack_xrects)) {
+           xrects = _cairo_malloc_ab (boxes->num_boxes, sizeof (XRectangle));
+           if (unlikely (xrects == NULL))
+               return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+       }
+
+       j = 0;
+       for (chunk = &boxes->chunks; chunk; chunk = chunk->next) {
+           for (i = 0; i < chunk->count; i++) {
+               int x1 = _cairo_fixed_integer_part (chunk->base[i].p1.x);
+               int y1 = _cairo_fixed_integer_part (chunk->base[i].p1.y);
+               int x2 = _cairo_fixed_integer_part (chunk->base[i].p2.x);
+               int y2 = _cairo_fixed_integer_part (chunk->base[i].p2.y);
+
+               xrects[j].x = x1;
+               xrects[j].y = y1;
+               xrects[j].width  = x2 - x1;
+               xrects[j].height = y2 - y1;
+               j++;
+           }
+       }
+
+       XRenderFillRectangles (dst->dpy,
+                              _render_operator (op),
+                              dst->picture,
+                              &render_color, xrects, j);
+
+       if (xrects != stack_xrects)
+           free (xrects);
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+#if 0
+check_composite ()
+    operation = _categorize_composite_operation (dst, op, src_pattern,
+                                                mask_pattern != NULL);
+    if (operation == DO_UNSUPPORTED)
+       return UNSUPPORTED ("unsupported operation");
+
+    //X_DEBUG ((display->display, "composite (dst=%x)", (unsigned int) dst->drawable));
+
+    operation = _recategorize_composite_operation (dst, op, src, &src_attr,
+                                                  mask_pattern != NULL);
+    if (operation == DO_UNSUPPORTED) {
+       status = UNSUPPORTED ("unsupported operation");
+       goto BAIL;
+    }
+#endif
+
+static cairo_int_status_t
+composite (void *abstract_dst,
+          cairo_operator_t     op,
+          cairo_surface_t      *abstract_src,
+          cairo_surface_t      *abstract_mask,
+          int                  src_x,
+          int                  src_y,
+          int                  mask_x,
+          int                  mask_y,
+          int                  dst_x,
+          int                  dst_y,
+          unsigned int         width,
+          unsigned int         height)
+{
+    cairo_xlib_surface_t *dst = abstract_dst;
+    cairo_xlib_source_t *src = (cairo_xlib_source_t *)abstract_src;
+
+    op = _render_operator (op);
+
+    _cairo_xlib_surface_ensure_picture (dst);
+    if (abstract_mask) {
+       cairo_xlib_source_t *mask = (cairo_xlib_source_t *)abstract_mask;
+
+       XRenderComposite (dst->dpy, op,
+                         src->picture, mask->picture, dst->picture,
+                         src_x,  src_y,
+                         mask_x, mask_y,
+                         dst_x,  dst_y,
+                         width,  height);
+    } else {
+       XRenderComposite (dst->dpy, op,
+                         src->picture, 0, dst->picture,
+                         src_x, src_y,
+                         0, 0,
+                         dst_x, dst_y,
+                         width, height);
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+lerp (void *abstract_dst,
+      cairo_surface_t  *abstract_src,
+      cairo_surface_t  *abstract_mask,
+      int                      src_x,
+      int                      src_y,
+      int                      mask_x,
+      int                      mask_y,
+      int                      dst_x,
+      int                      dst_y,
+      unsigned int             width,
+      unsigned int             height)
+{
+    cairo_xlib_surface_t *dst = abstract_dst;
+    cairo_xlib_source_t *src = (cairo_xlib_source_t *)abstract_src;
+    cairo_xlib_source_t *mask = (cairo_xlib_source_t *)abstract_mask;
+
+    _cairo_xlib_surface_ensure_picture (dst);
+    XRenderComposite (dst->dpy, PictOpOutReverse,
+                     mask->picture, None, dst->picture,
+                     mask_x, mask_y,
+                     0,      0,
+                     dst_x,  dst_y,
+                     width,  height);
+    XRenderComposite (dst->dpy, PictOpAdd,
+                     src->picture, mask->picture, dst->picture,
+                     src_x,  src_y,
+                     mask_x, mask_y,
+                     dst_x,  dst_y,
+                     width,  height);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+composite_boxes (void                  *abstract_dst,
+                cairo_operator_t        op,
+                cairo_surface_t        *abstract_src,
+                cairo_surface_t        *abstract_mask,
+                int                    src_x,
+                int                    src_y,
+                int                    mask_x,
+                int                    mask_y,
+                int                    dst_x,
+                int                    dst_y,
+                cairo_boxes_t          *boxes,
+                const cairo_rectangle_int_t  *extents)
+{
+    cairo_xlib_surface_t *dst = abstract_dst;
+    Picture src = ((cairo_xlib_source_t *)abstract_src)->picture;
+    Picture mask = abstract_mask ? ((cairo_xlib_source_t *)abstract_mask)->picture : 0;
+    XRectangle stack_rects[CAIRO_STACK_ARRAY_LENGTH (XRectangle)];
+    XRectangle *rects = stack_rects;
+    struct _cairo_boxes_chunk *chunk;
+    int i, j;
+
+    op = _render_operator (op);
+    _cairo_xlib_surface_ensure_picture (dst);
+    if (boxes->num_boxes == 1) {
+       int x1 = _cairo_fixed_integer_part (boxes->chunks.base[0].p1.x);
+       int y1 = _cairo_fixed_integer_part (boxes->chunks.base[0].p1.y);
+       int x2 = _cairo_fixed_integer_part (boxes->chunks.base[0].p2.x);
+       int y2 = _cairo_fixed_integer_part (boxes->chunks.base[0].p2.y);
+
+       XRenderComposite (dst->dpy, op,
+                         src, mask, dst->picture,
+                         x1 + src_x,   y1 + src_y,
+                         x1 + mask_x,  y1 + mask_y,
+                         x1 - dst_x,   y1 - dst_y,
+                         x2 - x1,      y2 - y1);
+       return CAIRO_STATUS_SUCCESS;
+    }
+
+    if (boxes->num_boxes > ARRAY_LENGTH (stack_rects)) {
+       rects = _cairo_malloc_ab (boxes->num_boxes, sizeof (XRectangle));
+       if (unlikely (rects == NULL))
+           return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+    }
+
+    j = 0;
+    for (chunk = &boxes->chunks; chunk; chunk = chunk->next) {
+       for (i = 0; i < chunk->count; i++) {
+           int x1 = _cairo_fixed_integer_part (chunk->base[i].p1.x);
+           int y1 = _cairo_fixed_integer_part (chunk->base[i].p1.y);
+           int x2 = _cairo_fixed_integer_part (chunk->base[i].p2.x);
+           int y2 = _cairo_fixed_integer_part (chunk->base[i].p2.y);
+
+           rects[j].x = x1 - dst_x;
+           rects[j].y = y1 - dst_y;
+           rects[j].width  = x2 - x1;
+           rects[j].height = y2 - y1;
+           j++;
+       }
+    }
+    assert (j == boxes->num_boxes);
+
+    XRenderSetPictureClipRectangles (dst->dpy,
+                                    dst->picture,
+                                    0, 0,
+                                    rects, j);
+    if (rects != stack_rects)
+       free (rects);
+
+    XRenderComposite (dst->dpy, op,
+                     src, mask, dst->picture,
+                     extents->x + src_x,  extents->y + src_y,
+                     extents->x + mask_x, extents->y + mask_y,
+                     extents->x - dst_x,  extents->y - dst_y,
+                     extents->width,      extents->height);
+
+    set_clip_region (dst, NULL);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+/* font rendering */
+
+void
+_cairo_xlib_font_close (cairo_xlib_font_t *priv)
+{
+    cairo_xlib_display_t *display = (cairo_xlib_display_t *)priv->base.key;
+    int i;
+
+    /* XXX All I really want is to do is zap my glyphs... */
+    _cairo_scaled_font_reset_cache (priv->font);
+
+    for (i = 0; i < NUM_GLYPHSETS; i++) {
+       cairo_xlib_font_glyphset_t *info;
+
+       info = &priv->glyphset[i];
+       if (info->glyphset)
+           XRenderFreeGlyphSet (display->display, info->glyphset);
+    }
+
+    /* XXX locking */
+    cairo_list_del (&priv->link);
+    cairo_list_del (&priv->base.link);
+    free (priv);
+}
+
+static void
+_cairo_xlib_font_fini (cairo_scaled_font_private_t *abstract_private,
+                      cairo_scaled_font_t *font)
+{
+    cairo_xlib_font_t *priv = (cairo_xlib_font_t *) abstract_private;
+    cairo_status_t status;
+    cairo_xlib_display_t *display;
+    int i;
+
+    cairo_list_del (&priv->base.link);
+    cairo_list_del (&priv->link);
+
+    status = _cairo_xlib_display_acquire (priv->device, &display);
+    if (status)
+       goto BAIL;
+
+    for (i = 0; i < NUM_GLYPHSETS; i++) {
+       cairo_xlib_font_glyphset_t *info;
+
+       info = &priv->glyphset[i];
+       if (info->glyphset)
+           XRenderFreeGlyphSet (display->display, info->glyphset);
+    }
+
+    cairo_device_release (&display->base);
+BAIL:
+    cairo_device_destroy (&display->base);
+    free (priv);
+}
+
+static cairo_xlib_font_t *
+_cairo_xlib_font_create (cairo_xlib_display_t *display,
+                        cairo_scaled_font_t  *font)
+{
+    cairo_xlib_font_t *priv;
+    int i;
+
+    priv = malloc (sizeof (cairo_xlib_font_t));
+    if (unlikely (priv == NULL))
+       return NULL;
+
+    _cairo_scaled_font_attach_private (font, &priv->base, display,
+                                      _cairo_xlib_font_fini);
+
+    priv->device = cairo_device_reference (&display->base);
+    priv->font = font;
+    cairo_list_add (&priv->link, &display->fonts);
+
+    for (i = 0; i < NUM_GLYPHSETS; i++) {
+       cairo_xlib_font_glyphset_t *info = &priv->glyphset[i];
+       switch (i) {
+       case GLYPHSET_INDEX_ARGB32: info->format = CAIRO_FORMAT_ARGB32; break;
+       case GLYPHSET_INDEX_A8:     info->format = CAIRO_FORMAT_A8;     break;
+       case GLYPHSET_INDEX_A1:     info->format = CAIRO_FORMAT_A1;     break;
+       default:                    ASSERT_NOT_REACHED;                          break;
+       }
+       info->xrender_format = NULL;
+       info->glyphset = None;
+       info->to_free.count = 0;
+    }
+
+    return priv;
+}
+
+static int
+_cairo_xlib_get_glyphset_index_for_format (cairo_format_t format)
+{
+    if (format == CAIRO_FORMAT_A8)
+        return GLYPHSET_INDEX_A8;
+    if (format == CAIRO_FORMAT_A1)
+        return GLYPHSET_INDEX_A1;
+
+    assert (format == CAIRO_FORMAT_ARGB32);
+    return GLYPHSET_INDEX_ARGB32;
+}
+
+static inline cairo_xlib_font_t *
+_cairo_xlib_font_get (const cairo_xlib_display_t *display,
+                     cairo_scaled_font_t *font)
+{
+    return (cairo_xlib_font_t *)_cairo_scaled_font_find_private (font, display);
+}
+
+typedef struct {
+    cairo_scaled_glyph_private_t base;
+
+
+    cairo_xlib_font_glyphset_t *glyphset;
+} cairo_xlib_glyph_private_t;
+
+static void
+_cairo_xlib_glyph_fini (cairo_scaled_glyph_private_t *glyph_private,
+                       cairo_scaled_glyph_t *glyph,
+                       cairo_scaled_font_t  *font)
+{
+    cairo_xlib_glyph_private_t *priv = (cairo_xlib_glyph_private_t *)glyph_private;
+
+    if (! font->finished) {
+       cairo_xlib_font_t *font_private;
+       struct _cairo_xlib_font_glyphset_free_glyphs *to_free;
+       cairo_xlib_font_glyphset_t *info;
+
+       font_private = _cairo_xlib_font_get (glyph_private->key, font);
+       assert (font_private);
+
+       info = priv->glyphset;
+       to_free = &info->to_free;
+       if (to_free->count == ARRAY_LENGTH (to_free->indices)) {
+           cairo_xlib_display_t *display;
+
+           if (_cairo_xlib_display_acquire (font_private->device,
+                                            &display) == CAIRO_STATUS_SUCCESS) {
+               XRenderFreeGlyphs (display->display,
+                                  info->glyphset,
+                                  to_free->indices,
+                                  to_free->count);
+               cairo_device_release (&display->base);
+           }
+
+           to_free->count = 0;
+       }
+
+       to_free->indices[to_free->count++] =
+           _cairo_scaled_glyph_index (glyph);
+    }
+
+    cairo_list_del (&glyph_private->link);
+    free (glyph_private);
+}
+
+static cairo_status_t
+_cairo_xlib_glyph_attach (cairo_xlib_display_t *display,
+                         cairo_scaled_glyph_t  *glyph,
+                         cairo_xlib_font_glyphset_t *info)
+{
+    cairo_xlib_glyph_private_t *priv;
+
+    priv = malloc (sizeof (*priv));
+    if (unlikely (priv == NULL))
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    _cairo_scaled_glyph_attach_private (glyph, &priv->base, display,
+                                       _cairo_xlib_glyph_fini);
+    priv->glyphset = info;
+
+    glyph->dev_private = info;
+    glyph->dev_private_key = display;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_xlib_font_glyphset_t *
+_cairo_xlib_font_get_glyphset_info_for_format (cairo_xlib_display_t *display,
+                                              cairo_scaled_font_t *font,
+                                              cairo_format_t       format)
+{
+    cairo_xlib_font_t *priv;
+    cairo_xlib_font_glyphset_t *info;
+    int glyphset_index;
+
+    glyphset_index = _cairo_xlib_get_glyphset_index_for_format (format);
+
+    priv = _cairo_xlib_font_get (display, font);
+    if (priv == NULL) {
+       priv = _cairo_xlib_font_create (display, font);
+       if (priv == NULL)
+           return NULL;
+    }
+
+    info = &priv->glyphset[glyphset_index];
+    if (info->glyphset == None) {
+       info->xrender_format =
+           _cairo_xlib_display_get_xrender_format (display, info->format);
+       info->glyphset = XRenderCreateGlyphSet (display->display,
+                                               info->xrender_format);
+    }
+
+    return info;
+}
+
+static cairo_bool_t
+has_pending_free_glyph (cairo_xlib_font_glyphset_t *info,
+                       unsigned long glyph_index)
+{
+    struct _cairo_xlib_font_glyphset_free_glyphs *to_free;
+    int i;
+
+    to_free = &info->to_free;
+    for (i = 0; i < to_free->count; i++) {
+       if (to_free->indices[i] == glyph_index) {
+           to_free->count--;
+           memmove (&to_free->indices[i],
+                    &to_free->indices[i+1],
+                    (to_free->count - i) * sizeof (to_free->indices[0]));
+           return TRUE;
+       }
+    }
+
+    return FALSE;
+}
+
+static cairo_xlib_font_glyphset_t *
+find_pending_free_glyph (cairo_xlib_display_t *display,
+                        cairo_scaled_font_t *font,
+                        unsigned long glyph_index,
+                        cairo_image_surface_t *surface)
+{
+    cairo_xlib_font_t *priv;
+    int i;
+
+    priv = _cairo_xlib_font_get (display, font);
+    if (priv == NULL)
+       return NULL;
+
+    if (surface != NULL) {
+       i = _cairo_xlib_get_glyphset_index_for_format (surface->format);
+       if (has_pending_free_glyph (&priv->glyphset[i], glyph_index))
+           return &priv->glyphset[i];
+    } else {
+       for (i = 0; i < NUM_GLYPHSETS; i++) {
+           if (has_pending_free_glyph (&priv->glyphset[i], glyph_index))
+               return &priv->glyphset[i];
+       }
+    }
+
+    return NULL;
+}
+
+static cairo_status_t
+_cairo_xlib_surface_add_glyph (cairo_xlib_display_t *display,
+                              cairo_scaled_font_t   *font,
+                              cairo_scaled_glyph_t **pscaled_glyph)
+{
+    XGlyphInfo glyph_info;
+    unsigned long glyph_index;
+    unsigned char *data;
+    cairo_status_t status = CAIRO_STATUS_SUCCESS;
+    cairo_scaled_glyph_t *glyph = *pscaled_glyph;
+    cairo_image_surface_t *glyph_surface = glyph->surface;
+    cairo_bool_t already_had_glyph_surface;
+    cairo_xlib_font_glyphset_t *info;
+
+    glyph_index = _cairo_scaled_glyph_index (glyph);
+
+    /* check to see if we have a pending XRenderFreeGlyph for this glyph */
+    info = find_pending_free_glyph (display, font, glyph_index, glyph_surface);
+    if (info != NULL)
+       return _cairo_xlib_glyph_attach (display, glyph, info);
+
+    if (glyph_surface == NULL) {
+       status = _cairo_scaled_glyph_lookup (font,
+                                            glyph_index,
+                                            CAIRO_SCALED_GLYPH_INFO_METRICS |
+                                            CAIRO_SCALED_GLYPH_INFO_SURFACE,
+                                            pscaled_glyph);
+       if (unlikely (status))
+           return status;
+
+       glyph = *pscaled_glyph;
+       glyph_surface = glyph->surface;
+       already_had_glyph_surface = FALSE;
+    } else {
+       already_had_glyph_surface = TRUE;
+    }
+
+    info = _cairo_xlib_font_get_glyphset_info_for_format (display, font,
+                                                         glyph_surface->format);
+
+#if 0
+    /* If the glyph surface has zero height or width, we create
+     * a clear 1x1 surface, to avoid various X server bugs.
+     */
+    if (glyph_surface->width == 0 || glyph_surface->height == 0) {
+       cairo_surface_t *tmp_surface;
+
+       tmp_surface = cairo_image_surface_create (info->format, 1, 1);
+       status = tmp_surface->status;
+       if (unlikely (status))
+           goto BAIL;
+
+       tmp_surface->device_transform = glyph_surface->base.device_transform;
+       tmp_surface->device_transform_inverse = glyph_surface->base.device_transform_inverse;
+
+       glyph_surface = (cairo_image_surface_t *) tmp_surface;
+    }
+#endif
+
+    /* If the glyph format does not match the font format, then we
+     * create a temporary surface for the glyph image with the font's
+     * format.
+     */
+    if (glyph_surface->format != info->format) {
+       cairo_surface_pattern_t pattern;
+       cairo_surface_t *tmp_surface;
+
+       tmp_surface = cairo_image_surface_create (info->format,
+                                                 glyph_surface->width,
+                                                 glyph_surface->height);
+       status = tmp_surface->status;
+       if (unlikely (status))
+           goto BAIL;
+
+       tmp_surface->device_transform = glyph_surface->base.device_transform;
+       tmp_surface->device_transform_inverse = glyph_surface->base.device_transform_inverse;
+
+       _cairo_pattern_init_for_surface (&pattern, &glyph_surface->base);
+       status = _cairo_surface_paint (tmp_surface,
+                                      CAIRO_OPERATOR_SOURCE, &pattern.base,
+                                      NULL);
+       _cairo_pattern_fini (&pattern.base);
+
+       glyph_surface = (cairo_image_surface_t *) tmp_surface;
+
+       if (unlikely (status))
+           goto BAIL;
+    }
+
+    /* XXX: FRAGILE: We're ignore device_transform scaling here. A bug? */
+    glyph_info.x = _cairo_lround (glyph_surface->base.device_transform.x0);
+    glyph_info.y = _cairo_lround (glyph_surface->base.device_transform.y0);
+    glyph_info.width = glyph_surface->width;
+    glyph_info.height = glyph_surface->height;
+    glyph_info.xOff = glyph->x_advance;
+    glyph_info.yOff = glyph->y_advance;
+
+    data = glyph_surface->data;
+
+    /* flip formats around */
+    switch (_cairo_xlib_get_glyphset_index_for_format (glyph->surface->format)) {
+    case GLYPHSET_INDEX_A1:
+       /* local bitmaps are always stored with bit == byte */
+       if (_cairo_is_little_endian() != (BitmapBitOrder (display->display) == LSBFirst)) {
+           int             c = glyph_surface->stride * glyph_surface->height;
+           unsigned char   *d;
+           unsigned char   *new, *n;
+
+           new = malloc (c);
+           if (!new) {
+               status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+               goto BAIL;
+           }
+           n = new;
+           d = data;
+           do {
+               char    b = *d++;
+               b = ((b << 1) & 0xaa) | ((b >> 1) & 0x55);
+               b = ((b << 2) & 0xcc) | ((b >> 2) & 0x33);
+               b = ((b << 4) & 0xf0) | ((b >> 4) & 0x0f);
+               *n++ = b;
+           } while (--c);
+           data = new;
+       }
+       break;
+    case GLYPHSET_INDEX_A8:
+       break;
+    case GLYPHSET_INDEX_ARGB32:
+       if (_cairo_is_little_endian() != (ImageByteOrder (display->display) == LSBFirst)) {
+           unsigned int c = glyph_surface->stride * glyph_surface->height / 4;
+           const uint32_t *d;
+           uint32_t *new, *n;
+
+           new = malloc (4 * c);
+           if (unlikely (new == NULL)) {
+               status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+               goto BAIL;
+           }
+           n = new;
+           d = (uint32_t *) data;
+           do {
+               *n++ = bswap_32 (*d);
+               d++;
+           } while (--c);
+           data = (uint8_t *) new;
+       }
+       break;
+    default:
+       ASSERT_NOT_REACHED;
+       break;
+    }
+    /* XXX assume X server wants pixman padding. Xft assumes this as well */
+
+    XRenderAddGlyphs (display->display, info->glyphset,
+                     &glyph_index, &glyph_info, 1,
+                     (char *) data,
+                     glyph_surface->stride * glyph_surface->height);
+
+    if (data != glyph_surface->data)
+       free (data);
+
+    status = _cairo_xlib_glyph_attach (display, glyph, info);
+
+ BAIL:
+    if (glyph_surface != glyph->surface)
+       cairo_surface_destroy (&glyph_surface->base);
+
+    /* if the scaled glyph didn't already have a surface attached
+     * to it, release the created surface now that we have it
+     * uploaded to the X server.  If the surface has already been
+     * there (eg. because image backend requested it), leave it in
+     * the cache
+     */
+    if (!already_had_glyph_surface)
+       _cairo_scaled_glyph_set_surface (glyph, font, NULL);
+
+    return status;
+}
+
+typedef void (*cairo_xrender_composite_text_func_t)
+             (Display                      *dpy,
+              int                          op,
+              Picture                      src,
+              Picture                      dst,
+              _Xconst XRenderPictFormat    *maskFormat,
+              int                          xSrc,
+              int                          ySrc,
+              int                          xDst,
+              int                          yDst,
+              _Xconst XGlyphElt8           *elts,
+              int                          nelt);
+
+/* Build a struct of the same size of #cairo_glyph_t that can be used both as
+ * an input glyph with double coordinates, and as "working" glyph with
+ * integer from-current-point offsets. */
+typedef union {
+    cairo_glyph_t d;
+    unsigned long index;
+    struct {
+        unsigned long index;
+        int x;
+        int y;
+    } i;
+} cairo_xlib_glyph_t;
+
+/* compile-time assert that #cairo_xlib_glyph_t is the same size as #cairo_glyph_t */
+COMPILE_TIME_ASSERT (sizeof (cairo_xlib_glyph_t) == sizeof (cairo_glyph_t));
+
+/* Start a new element for the first glyph,
+ * or for any glyph that has unexpected position,
+ * or if current element has too many glyphs
+ * (Xrender limits each element to 252 glyphs, we limit them to 128)
+ *
+ * These same conditions need to be mirrored between
+ * _cairo_xlib_surface_emit_glyphs and _emit_glyph_chunks
+ */
+#define _start_new_glyph_elt(count, glyph) \
+    (((count) & 127) == 0 || (glyph)->i.x || (glyph)->i.y)
+
+static cairo_status_t
+_emit_glyphs_chunk (cairo_xlib_display_t *display,
+                    cairo_xlib_surface_t *dst,
+                   int dst_x, int dst_y,
+                   cairo_xlib_glyph_t *glyphs,
+                   int num_glyphs,
+                   cairo_scaled_font_t *font,
+                   cairo_bool_t use_mask,
+                   cairo_operator_t op,
+                   cairo_xlib_source_t *src,
+                   int src_x, int src_y,
+                   /* info for this chunk */
+                   int num_elts,
+                   int width,
+                   cairo_xlib_font_glyphset_t *info)
+{
+    /* Which XRenderCompositeText function to use */
+    cairo_xrender_composite_text_func_t composite_text_func;
+    int size;
+
+    /* Element buffer stuff */
+    XGlyphElt8 *elts;
+    XGlyphElt8 stack_elts[CAIRO_STACK_ARRAY_LENGTH (XGlyphElt8)];
+
+    /* Reuse the input glyph array for output char generation */
+    char *char8 = (char *) glyphs;
+    unsigned short *char16 = (unsigned short *) glyphs;
+    unsigned int *char32 = (unsigned int *) glyphs;
+
+    int i;
+    int nelt; /* Element index */
+    int n; /* Num output glyphs in current element */
+    int j; /* Num output glyphs so far */
+
+    switch (width) {
+    case 1:
+       /* don't cast the 8-variant, to catch possible mismatches */
+       composite_text_func = XRenderCompositeText8;
+       size = sizeof (char);
+       break;
+    case 2:
+       composite_text_func = (cairo_xrender_composite_text_func_t) XRenderCompositeText16;
+       size = sizeof (unsigned short);
+       break;
+    default:
+    case 4:
+       composite_text_func = (cairo_xrender_composite_text_func_t) XRenderCompositeText32;
+       size = sizeof (unsigned int);
+    }
+
+    /* Allocate element array */
+    if (num_elts <= ARRAY_LENGTH (stack_elts)) {
+      elts = stack_elts;
+    } else {
+      elts = _cairo_malloc_ab (num_elts, sizeof (XGlyphElt8));
+      if (unlikely (elts == NULL))
+         return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+    }
+
+    /* Fill them in */
+    nelt = 0;
+    n = 0;
+    j = 0;
+    for (i = 0; i < num_glyphs; i++) {
+      /* Start a new element for first output glyph,
+       * or for any glyph that has unexpected position,
+       * or if current element has too many glyphs.
+       *
+       * These same conditions are mirrored in _cairo_xlib_surface_emit_glyphs()
+       */
+      if (_start_new_glyph_elt (j, &glyphs[i])) {
+         if (j) {
+           elts[nelt].nchars = n;
+           nelt++;
+           n = 0;
+         }
+         elts[nelt].chars = char8 + size * j;
+         elts[nelt].glyphset = info->glyphset;
+         elts[nelt].xOff = glyphs[i].i.x;
+         elts[nelt].yOff = glyphs[i].i.y;
+      }
+
+      switch (width) {
+      case 1: char8 [j] = (char)           glyphs[i].index; break;
+      case 2: char16[j] = (unsigned short) glyphs[i].index; break;
+      default:
+      case 4: char32[j] = (unsigned int)   glyphs[i].index; break;
+      }
+
+      n++;
+      j++;
+    }
+
+    if (n) {
+       elts[nelt].nchars = n;
+       nelt++;
+    }
+
+    /* Check that we agree with _cairo_xlib_surface_emit_glyphs() on the
+     * expected number of xGlyphElts.  */
+    assert (nelt == num_elts);
+
+    composite_text_func (display->display, op,
+                        src->picture,
+                        dst->picture,
+                        use_mask ? info->xrender_format : NULL,
+                        src_x + elts[0].xOff,
+                        src_y + elts[0].yOff,
+                        elts[0].xOff - dst_x, elts[0].yOff - dst_y,
+                        (XGlyphElt8 *) elts, nelt);
+
+    if (elts != stack_elts)
+      free (elts);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+check_composite_glyphs (const cairo_composite_rectangles_t *extents,
+                       cairo_scaled_font_t *font,
+                       cairo_glyph_t *glyphs,
+                       int *num_glyphs)
+{
+    cairo_xlib_surface_t *dst = (cairo_xlib_surface_t *)extents->surface;
+    cairo_xlib_display_t *display = dst->display;
+    int max_request_size, size;
+
+    if (! CAIRO_RENDER_SUPPORTS_OPERATOR (display, extents->op))
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    /* The glyph coordinates must be representable in an int16_t.
+     * When possible, they will be expressed as an offset from the
+     * previous glyph, otherwise they will be an offset from the
+     * surface origin. If we can't guarantee this to be possible,
+     * fallback.
+     */
+    if (extents->bounded.x + extents->bounded.width > INT16_MAX ||
+       extents->bounded.y + extents->bounded.height> INT16_MAX ||
+       extents->bounded.x < INT16_MIN ||
+       extents->bounded.y < INT16_MIN)
+    {
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+    }
+
+    /* Approximate the size of the largest glyph and fallback if we can not
+     * upload it to the xserver.
+     */
+    size = ceil (font->max_scale);
+    size = 4 * size * size;
+    max_request_size = (XExtendedMaxRequestSize (display->display) ? XExtendedMaxRequestSize (display->display)
+                       : XMaxRequestSize (display->display)) * 4 -
+       sz_xRenderAddGlyphsReq -
+       sz_xGlyphInfo          -
+       8;
+    if (size >= max_request_size)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+/* sz_xGlyphtElt required alignment to a 32-bit boundary, so ensure we have
+ * enough room for padding */
+#define _cairo_sz_xGlyphElt (sz_xGlyphElt + 4)
+
+static cairo_int_status_t
+composite_glyphs (void                         *surface,
+                 cairo_operator_t               op,
+                 cairo_surface_t               *_src,
+                 int                            src_x,
+                 int                            src_y,
+                 int                            dst_x,
+                 int                            dst_y,
+                 cairo_composite_glyphs_info_t *info)
+{
+    cairo_xlib_surface_t *dst = surface;
+    cairo_xlib_glyph_t *glyphs = (cairo_xlib_glyph_t *)info->glyphs;
+    cairo_xlib_source_t *src = (cairo_xlib_source_t *)_src;
+    cairo_xlib_display_t *display = dst->display;
+    cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS;
+    cairo_scaled_glyph_t *glyph;
+    cairo_fixed_t x = 0, y = 0;
+    cairo_xlib_font_glyphset_t *glyphset = NULL, *this_glyphset_info;
+
+    unsigned long max_index = 0;
+    int width = 1;
+    int num_elts = 0;
+    int num_out_glyphs = 0;
+    int num_glyphs = info->num_glyphs;
+
+    int max_request_size = XMaxRequestSize (display->display) * 4
+                        - MAX (sz_xRenderCompositeGlyphs8Req,
+                               MAX(sz_xRenderCompositeGlyphs16Req,
+                                   sz_xRenderCompositeGlyphs32Req));
+    int request_size = 0;
+    int i;
+
+    op = _render_operator (op),
+    _cairo_xlib_surface_ensure_picture (dst);
+    for (i = 0; i < num_glyphs; i++) {
+       int this_x, this_y;
+       int old_width;
+
+       status = _cairo_scaled_glyph_lookup (info->font,
+                                            glyphs[i].index,
+                                            CAIRO_SCALED_GLYPH_INFO_METRICS,
+                                            &glyph);
+       if (unlikely (status))
+           return status;
+
+       this_x = _cairo_lround (glyphs[i].d.x);
+       this_y = _cairo_lround (glyphs[i].d.y);
+
+       /* Send unsent glyphs to the server */
+       if (glyph->dev_private_key != display) {
+           status = _cairo_xlib_surface_add_glyph (display, info->font, &glyph);
+           if (unlikely (status))
+               return status;
+       }
+
+       this_glyphset_info = glyph->dev_private;
+       if (!glyphset)
+           glyphset = this_glyphset_info;
+
+       /* The invariant here is that we can always flush the glyphs
+        * accumulated before this one, using old_width, and they
+        * would fit in the request.
+        */
+       old_width = width;
+
+       /* Update max glyph index */
+       if (glyphs[i].index > max_index) {
+           max_index = glyphs[i].index;
+           if (max_index >= 65536)
+             width = 4;
+           else if (max_index >= 256)
+             width = 2;
+           if (width != old_width)
+             request_size += (width - old_width) * num_out_glyphs;
+       }
+
+       /* If we will pass the max request size by adding this glyph,
+        * flush current glyphs.  Note that we account for a
+        * possible element being added below.
+        *
+        * Also flush if changing glyphsets, as Xrender limits one mask
+        * format per request, so we can either break up, or use a
+        * wide-enough mask format.  We do the former.  One reason to
+        * prefer the latter is the fact that Xserver ADDs all glyphs
+        * to the mask first, and then composes that to final surface,
+        * though it's not a big deal.
+        *
+        * If the glyph has a coordinate which cannot be represented
+        * as a 16-bit offset from the previous glyph, flush the
+        * current chunk. The current glyph will be the first one in
+        * the next chunk, thus its coordinates will be an offset from
+        * the destination origin. This offset is guaranteed to be
+        * representable as 16-bit offset (otherwise we would have
+        * fallen back).
+        */
+       if (request_size + width > max_request_size - _cairo_sz_xGlyphElt ||
+           this_x - x > INT16_MAX || this_x - x < INT16_MIN ||
+           this_y - y > INT16_MAX || this_y - y < INT16_MIN ||
+           (this_glyphset_info != glyphset)) {
+           status = _emit_glyphs_chunk (display, dst, dst_x, dst_y,
+                                        glyphs, i, info->font, info->use_mask,
+                                        op, src, src_x, src_y,
+                                        num_elts, old_width, glyphset);
+           if (unlikely (status))
+               return status;
+
+           glyphs += i;
+           num_glyphs -= i;
+           i = 0;
+           max_index = glyphs[i].index;
+           width = max_index < 256 ? 1 : max_index < 65536 ? 2 : 4;
+           request_size = 0;
+           num_elts = 0;
+           num_out_glyphs = 0;
+           x = y = 0;
+           glyphset = this_glyphset_info;
+       }
+
+       /* Convert absolute glyph position to relative-to-current-point
+        * position */
+       glyphs[i].i.x = this_x - x;
+       glyphs[i].i.y = this_y - y;
+
+       /* Start a new element for the first glyph,
+        * or for any glyph that has unexpected position,
+        * or if current element has too many glyphs.
+        *
+        * These same conditions are mirrored in _emit_glyphs_chunk().
+        */
+      if (_start_new_glyph_elt (num_out_glyphs, &glyphs[i])) {
+           num_elts++;
+           request_size += _cairo_sz_xGlyphElt;
+       }
+
+       /* adjust current-position */
+       x = this_x + glyph->x_advance;
+       y = this_y + glyph->y_advance;
+
+       num_out_glyphs++;
+       request_size += width;
+    }
+
+    if (num_elts) {
+       status = _emit_glyphs_chunk (display, dst, dst_x, dst_y,
+                                    glyphs, i, info->font, info->use_mask,
+                                    op, src, src_x, src_y,
+                                    num_elts, width, glyphset);
+    }
+
+    return status;
+}
+
+const cairo_compositor_t *
+_cairo_xlib_mask_compositor_get (void)
+{
+    static cairo_mask_compositor_t compositor;
+
+    if (compositor.base.delegate == NULL) {
+       _cairo_mask_compositor_init (&compositor,
+                                    _cairo_xlib_fallback_compositor_get ());
+
+       compositor.acquire = acquire;
+       compositor.release = release;
+       compositor.set_clip_region = set_clip_region;
+       compositor.pattern_to_surface = _cairo_xlib_source_create_for_pattern;
+       compositor.draw_image_boxes = draw_image_boxes;
+       compositor.fill_rectangles = fill_rectangles;
+       compositor.fill_boxes = fill_boxes;
+       //compositor.check_composite = check_composite;
+       compositor.composite = composite;
+       //compositor.check_composite_boxes = check_composite_boxes;
+       compositor.composite_boxes = composite_boxes;
+       compositor.check_composite_glyphs = check_composite_glyphs;
+       compositor.composite_glyphs = composite_glyphs;
+    }
+
+    return &compositor.base;
+}
+
+#define CAIRO_FIXED_16_16_MIN -32768
+#define CAIRO_FIXED_16_16_MAX 32767
+
+static cairo_bool_t
+line_exceeds_16_16 (const cairo_line_t *line)
+{
+    return
+       line->p1.x < _cairo_fixed_from_int (CAIRO_FIXED_16_16_MIN) ||
+       line->p1.x > _cairo_fixed_from_int (CAIRO_FIXED_16_16_MAX) ||
+       line->p2.x < _cairo_fixed_from_int (CAIRO_FIXED_16_16_MIN) ||
+       line->p2.x > _cairo_fixed_from_int (CAIRO_FIXED_16_16_MAX) ||
+       line->p1.y < _cairo_fixed_from_int (CAIRO_FIXED_16_16_MIN) ||
+       line->p1.y > _cairo_fixed_from_int (CAIRO_FIXED_16_16_MAX) ||
+       line->p2.y < _cairo_fixed_from_int (CAIRO_FIXED_16_16_MIN) ||
+       line->p2.y > _cairo_fixed_from_int (CAIRO_FIXED_16_16_MAX);
+}
+
+static void
+project_line_x_onto_16_16 (const cairo_line_t *line,
+                           cairo_fixed_t top,
+                           cairo_fixed_t bottom,
+                           XLineFixed *out)
+{
+    cairo_point_double_t p1, p2;
+    double m;
+
+    p1.x = _cairo_fixed_to_double (line->p1.x);
+    p1.y = _cairo_fixed_to_double (line->p1.y);
+
+    p2.x = _cairo_fixed_to_double (line->p2.x);
+    p2.y = _cairo_fixed_to_double (line->p2.y);
+
+    m = (p2.x - p1.x) / (p2.y - p1.y);
+    out->p1.x = _cairo_fixed_16_16_from_double (p1.x + m * _cairo_fixed_to_double (top - line->p1.y));
+    out->p2.x = _cairo_fixed_16_16_from_double (p1.x + m * _cairo_fixed_to_double (bottom - line->p1.y));
+}
+#if 0
+static cairo_int_status_T
+check_composite_trapezoids ()
+{
+    operation = _categorize_composite_operation (dst, op, pattern, TRUE);
+    if (operation == DO_UNSUPPORTED)
+       return UNSUPPORTED ("unsupported operation");
+
+    operation = _recategorize_composite_operation (dst, op, src,
+                                                  &attributes, TRUE);
+    if (operation == DO_UNSUPPORTED) {
+       status = UNSUPPORTED ("unsupported operation");
+       goto BAIL;
+    }
+
+}
+#endif
+
+static cairo_int_status_t
+composite_traps (void                  *abstract_dst,
+                cairo_operator_t       op,
+                cairo_surface_t        *abstract_src,
+                int                    src_x,
+                int                    src_y,
+                int                    dst_x,
+                int                    dst_y,
+                const cairo_rectangle_int_t *extents,
+                cairo_antialias_t      antialias,
+                cairo_traps_t          *traps)
+{
+    cairo_xlib_surface_t       *dst = abstract_dst;
+    cairo_xlib_display_t        *display = dst->display;
+    cairo_xlib_source_t                *src = (cairo_xlib_source_t *)abstract_src;
+    XRenderPictFormat          *pict_format;
+    XTrapezoid xtraps_stack[CAIRO_STACK_ARRAY_LENGTH (XTrapezoid)];
+    XTrapezoid *xtraps = xtraps_stack;
+    int dx, dy;
+    int i;
+
+    //X_DEBUG ((display->display, "composite_trapezoids (dst=%x)", (unsigned int) dst->drawable));
+
+    pict_format =
+       _cairo_xlib_display_get_xrender_format (display,
+                                               antialias == CAIRO_ANTIALIAS_NONE ?  CAIRO_FORMAT_A1 : CAIRO_FORMAT_A8);
+
+    if (traps->num_traps > ARRAY_LENGTH (xtraps_stack)) {
+       xtraps = _cairo_malloc_ab (traps->num_traps, sizeof (XTrapezoid));
+       if (unlikely (xtraps == NULL))
+           return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+    }
+
+    dx = -dst_x << 16;
+    dy = -dst_y << 16;
+    for (i = 0; i < traps->num_traps; i++) {
+       cairo_trapezoid_t *t = &traps->traps[i];
+
+       /* top/bottom will be clamped to surface bounds */
+       xtraps[i].top = _cairo_fixed_to_16_16(t->top) + dy;
+       xtraps[i].bottom = _cairo_fixed_to_16_16(t->bottom) + dy;
+
+       /* However, all the other coordinates will have been left untouched so
+        * as not to introduce numerical error. Recompute them if they
+        * exceed the 16.16 limits.
+        */
+       if (unlikely (line_exceeds_16_16 (&t->left))) {
+           project_line_x_onto_16_16 (&t->left, t->top, t->bottom,
+                                       &xtraps[i].left);
+           xtraps[i].left.p1.x += dx;
+           xtraps[i].left.p2.x += dx;
+           xtraps[i].left.p1.y = xtraps[i].top;
+           xtraps[i].left.p2.y = xtraps[i].bottom;
+       } else {
+           xtraps[i].left.p1.x = _cairo_fixed_to_16_16(t->left.p1.x) + dx;
+           xtraps[i].left.p1.y = _cairo_fixed_to_16_16(t->left.p1.y) + dy;
+           xtraps[i].left.p2.x = _cairo_fixed_to_16_16(t->left.p2.x) + dx;
+           xtraps[i].left.p2.y = _cairo_fixed_to_16_16(t->left.p2.y) + dy;
+       }
+
+       if (unlikely (line_exceeds_16_16 (&t->right))) {
+           project_line_x_onto_16_16 (&t->right, t->top, t->bottom,
+                                      &xtraps[i].right);
+           xtraps[i].right.p1.x += dx;
+           xtraps[i].right.p2.x += dx;
+           xtraps[i].right.p1.y = xtraps[i].top;
+           xtraps[i].right.p2.y = xtraps[i].bottom;
+       } else {
+           xtraps[i].right.p1.x = _cairo_fixed_to_16_16(t->right.p1.x) + dx;
+           xtraps[i].right.p1.y = _cairo_fixed_to_16_16(t->right.p1.y) + dy;
+           xtraps[i].right.p2.x = _cairo_fixed_to_16_16(t->right.p2.x) + dx;
+           xtraps[i].right.p2.y = _cairo_fixed_to_16_16(t->right.p2.y) + dy;
+       }
+    }
+
+    if (xtraps[0].left.p1.y < xtraps[0].left.p2.y) {
+       src_x += _cairo_fixed_16_16_floor (xtraps[0].left.p1.x);
+       src_y += _cairo_fixed_16_16_floor (xtraps[0].left.p1.y);
+    } else {
+       src_x += _cairo_fixed_16_16_floor (xtraps[0].left.p2.x);
+       src_y += _cairo_fixed_16_16_floor (xtraps[0].left.p2.y);
+    }
+    src_x += dst_x;
+    src_y += dst_y;
+
+    _cairo_xlib_surface_ensure_picture (dst);
+    _cairo_xlib_surface_set_precision (dst, antialias);
+    XRenderCompositeTrapezoids (dst->dpy,
+                               _render_operator (op),
+                               src->picture, dst->picture,
+                               pict_format,
+                               src_x, src_y,
+                               xtraps, traps->num_traps);
+
+    if (xtraps != xtraps_stack)
+       free (xtraps);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+composite_tristrip (void               *abstract_dst,
+                   cairo_operator_t    op,
+                   cairo_surface_t     *abstract_src,
+                   int                 src_x,
+                   int                 src_y,
+                   int                 dst_x,
+                   int                 dst_y,
+                   const cairo_rectangle_int_t *extents,
+                   cairo_antialias_t   antialias,
+                   cairo_tristrip_t    *strip)
+{
+    cairo_xlib_surface_t       *dst = abstract_dst;
+    cairo_xlib_display_t        *display = dst->display;
+    cairo_xlib_source_t                *src = (cairo_xlib_source_t *)abstract_src;
+    XRenderPictFormat          *pict_format;
+    XPointFixed points_stack[CAIRO_STACK_ARRAY_LENGTH (XPointFixed)];
+    XPointFixed *points = points_stack;
+    int dx, dy;
+    int i;
+
+    //X_DEBUG ((display->display, "composite_trapezoids (dst=%x)", (unsigned int) dst->drawable));
+
+    pict_format =
+       _cairo_xlib_display_get_xrender_format (display,
+                                               antialias == CAIRO_ANTIALIAS_NONE ?  CAIRO_FORMAT_A1 : CAIRO_FORMAT_A8);
+
+    if (strip->num_points > ARRAY_LENGTH (points_stack)) {
+       points = _cairo_malloc_ab (strip->num_points, sizeof (XPointFixed));
+       if (unlikely (points == NULL))
+           return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+    }
+
+    dx = -dst_x << 16;
+    dy = -dst_y << 16;
+    for (i = 0; i < strip->num_points; i++) {
+       cairo_point_t *p = &strip->points[i];
+
+       points[i].x = _cairo_fixed_to_16_16(p->x) + dx;
+       points[i].y = _cairo_fixed_to_16_16(p->y) + dy;
+    }
+
+    src_x += _cairo_fixed_16_16_floor (points[0].x) + dst_x;
+    src_y += _cairo_fixed_16_16_floor (points[0].y) + dst_y;
+
+    _cairo_xlib_surface_ensure_picture (dst);
+    _cairo_xlib_surface_set_precision (dst, antialias);
+    XRenderCompositeTriStrip (dst->dpy,
+                             _render_operator (op),
+                             src->picture, dst->picture,
+                             pict_format,
+                             src_x, src_y,
+                             points, strip->num_points);
+
+    if (points != points_stack)
+       free (points);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+const cairo_compositor_t *
+_cairo_xlib_traps_compositor_get (void)
+{
+    static cairo_traps_compositor_t compositor;
+
+    if (compositor.base.delegate == NULL) {
+       _cairo_traps_compositor_init (&compositor,
+                                     _cairo_xlib_mask_compositor_get ());
+
+       compositor.acquire = acquire;
+       compositor.release = release;
+       compositor.set_clip_region = set_clip_region;
+       compositor.pattern_to_surface = _cairo_xlib_source_create_for_pattern;
+       compositor.draw_image_boxes = draw_image_boxes;
+       compositor.copy_boxes = copy_boxes;
+       compositor.fill_boxes = fill_boxes;
+       //compositor.check_composite = check_composite;
+       compositor.composite = composite;
+       compositor.lerp = lerp;
+       //compositor.check_composite_boxes = check_composite_boxes;
+       compositor.composite_boxes = composite_boxes;
+       //compositor.check_composite_traps = check_composite_traps;
+       compositor.composite_traps = composite_traps;
+       //compositor.check_composite_tristrip = check_composite_tristrip;
+       compositor.composite_tristrip = composite_tristrip;
+       compositor.check_composite_glyphs = check_composite_glyphs;
+       compositor.composite_glyphs = composite_glyphs;
+    }
+
+    return &compositor.base;
+}
+
+#endif /* !CAIRO_HAS_XLIB_XCB_FUNCTIONS */
diff --git a/src/cairo-xlib-screen.c b/src/cairo-xlib-screen.c
new file mode 100644 (file)
index 0000000..7bfdf15
--- /dev/null
@@ -0,0 +1,466 @@
+/* Cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Partially on code from xftdpy.c
+ *
+ * Copyright © 2000 Keith Packard
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of Keith Packard not be used in
+ * advertising or publicity pertaining to distribution of the software without
+ * specific, written prior permission.  Keith Packard makes no
+ * representations about the suitability of this software for any purpose.  It
+ * is provided "as is" without express or implied warranty.
+ *
+ * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "cairoint.h"
+
+#if !CAIRO_HAS_XLIB_XCB_FUNCTIONS
+
+#include "cairo-xlib-private.h"
+#include "cairo-xlib-xrender-private.h"
+
+#include "cairo-xlib-surface-private.h"
+#include "cairo-error-private.h"
+
+#include "cairo-fontconfig-private.h"
+
+static int
+parse_boolean (const char *v)
+{
+    char c0, c1;
+
+    c0 = *v;
+    if (c0 == 't' || c0 == 'T' || c0 == 'y' || c0 == 'Y' || c0 == '1')
+       return 1;
+    if (c0 == 'f' || c0 == 'F' || c0 == 'n' || c0 == 'N' || c0 == '0')
+       return 0;
+    if (c0 == 'o')
+    {
+       c1 = v[1];
+       if (c1 == 'n' || c1 == 'N')
+           return 1;
+       if (c1 == 'f' || c1 == 'F')
+           return 0;
+    }
+
+    return -1;
+}
+
+static cairo_bool_t
+get_boolean_default (Display       *dpy,
+                    const char    *option,
+                    cairo_bool_t  *value)
+{
+    char *v;
+    int i;
+
+    v = XGetDefault (dpy, "Xft", option);
+    if (v) {
+       i = parse_boolean (v);
+       if (i >= 0) {
+           *value = i;
+           return TRUE;
+       }
+    }
+
+    return FALSE;
+}
+
+static cairo_bool_t
+get_integer_default (Display    *dpy,
+                    const char *option,
+                    int        *value)
+{
+    char *v, *e;
+
+    v = XGetDefault (dpy, "Xft", option);
+    if (v) {
+#if CAIRO_HAS_FC_FONT
+       if (FcNameConstant ((FcChar8 *) v, value))
+           return TRUE;
+#endif
+
+       *value = strtol (v, &e, 0);
+       if (e != v)
+           return TRUE;
+    }
+
+    return FALSE;
+}
+
+static void
+_cairo_xlib_init_screen_font_options (Display *dpy,
+                                     cairo_xlib_screen_t *info)
+{
+    cairo_bool_t xft_hinting;
+    cairo_bool_t xft_antialias;
+    int xft_hintstyle;
+    int xft_rgba;
+    int xft_lcdfilter;
+    cairo_antialias_t antialias;
+    cairo_subpixel_order_t subpixel_order;
+    cairo_lcd_filter_t lcd_filter;
+    cairo_hint_style_t hint_style;
+
+    if (!get_boolean_default (dpy, "antialias", &xft_antialias))
+       xft_antialias = TRUE;
+
+    if (!get_integer_default (dpy, "lcdfilter", &xft_lcdfilter)) {
+       /* -1 is an non-existant Fontconfig constant used to differentiate
+        * the case when no lcdfilter property is available.
+        */
+       xft_lcdfilter = -1;
+    }
+
+    if (!get_boolean_default (dpy, "hinting", &xft_hinting))
+       xft_hinting = TRUE;
+
+    if (!get_integer_default (dpy, "hintstyle", &xft_hintstyle))
+       xft_hintstyle = FC_HINT_FULL;
+
+    if (!get_integer_default (dpy, "rgba", &xft_rgba))
+    {
+        cairo_xlib_display_t *display = (cairo_xlib_display_t *) info->device;
+
+       xft_rgba = FC_RGBA_UNKNOWN;
+
+#if RENDER_MAJOR > 0 || RENDER_MINOR >= 6
+        if (display->render_major > 0 || display->render_minor >= 6) {
+           int render_order = XRenderQuerySubpixelOrder (dpy,
+                                                         XScreenNumberOfScreen (info->screen));
+
+           switch (render_order) {
+           default:
+           case SubPixelUnknown:
+               xft_rgba = FC_RGBA_UNKNOWN;
+               break;
+           case SubPixelHorizontalRGB:
+               xft_rgba = FC_RGBA_RGB;
+               break;
+           case SubPixelHorizontalBGR:
+               xft_rgba = FC_RGBA_BGR;
+               break;
+           case SubPixelVerticalRGB:
+               xft_rgba = FC_RGBA_VRGB;
+               break;
+           case SubPixelVerticalBGR:
+               xft_rgba = FC_RGBA_VBGR;
+               break;
+           case SubPixelNone:
+               xft_rgba = FC_RGBA_NONE;
+               break;
+           }
+       }
+#endif
+    }
+
+    if (xft_hinting) {
+       switch (xft_hintstyle) {
+       case FC_HINT_NONE:
+           hint_style = CAIRO_HINT_STYLE_NONE;
+           break;
+       case FC_HINT_SLIGHT:
+           hint_style = CAIRO_HINT_STYLE_SLIGHT;
+           break;
+       case FC_HINT_MEDIUM:
+           hint_style = CAIRO_HINT_STYLE_MEDIUM;
+           break;
+       case FC_HINT_FULL:
+           hint_style = CAIRO_HINT_STYLE_FULL;
+           break;
+       default:
+           hint_style = CAIRO_HINT_STYLE_DEFAULT;
+       }
+    } else {
+       hint_style = CAIRO_HINT_STYLE_NONE;
+    }
+
+    switch (xft_rgba) {
+    case FC_RGBA_RGB:
+       subpixel_order = CAIRO_SUBPIXEL_ORDER_RGB;
+       break;
+    case FC_RGBA_BGR:
+       subpixel_order = CAIRO_SUBPIXEL_ORDER_BGR;
+       break;
+    case FC_RGBA_VRGB:
+       subpixel_order = CAIRO_SUBPIXEL_ORDER_VRGB;
+       break;
+    case FC_RGBA_VBGR:
+       subpixel_order = CAIRO_SUBPIXEL_ORDER_VBGR;
+       break;
+    case FC_RGBA_UNKNOWN:
+    case FC_RGBA_NONE:
+    default:
+       subpixel_order = CAIRO_SUBPIXEL_ORDER_DEFAULT;
+    }
+
+    switch (xft_lcdfilter) {
+    case FC_LCD_NONE:
+       lcd_filter = CAIRO_LCD_FILTER_NONE;
+       break;
+    case FC_LCD_DEFAULT:
+       lcd_filter = CAIRO_LCD_FILTER_FIR5;
+       break;
+    case FC_LCD_LIGHT:
+       lcd_filter = CAIRO_LCD_FILTER_FIR3;
+       break;
+    case FC_LCD_LEGACY:
+       lcd_filter = CAIRO_LCD_FILTER_INTRA_PIXEL;
+       break;
+    default:
+       lcd_filter = CAIRO_LCD_FILTER_DEFAULT;
+       break;
+    }
+
+    if (xft_antialias) {
+       if (subpixel_order == CAIRO_SUBPIXEL_ORDER_DEFAULT)
+           antialias = CAIRO_ANTIALIAS_GRAY;
+       else
+           antialias = CAIRO_ANTIALIAS_SUBPIXEL;
+    } else {
+       antialias = CAIRO_ANTIALIAS_NONE;
+    }
+
+    cairo_font_options_set_hint_style (&info->font_options, hint_style);
+    cairo_font_options_set_antialias (&info->font_options, antialias);
+    cairo_font_options_set_subpixel_order (&info->font_options, subpixel_order);
+    _cairo_font_options_set_lcd_filter (&info->font_options, lcd_filter);
+    cairo_font_options_set_hint_metrics (&info->font_options, CAIRO_HINT_METRICS_ON);
+}
+
+void
+_cairo_xlib_screen_destroy (cairo_xlib_display_t *display,
+                           cairo_xlib_screen_t *info)
+{
+    Display *dpy;
+    int i;
+
+    dpy = display->display;
+
+    while (! cairo_list_is_empty (&info->surfaces)) {
+       cairo_xlib_surface_t *surface;
+
+       surface = cairo_list_first_entry (&info->surfaces,
+                                         cairo_xlib_surface_t,
+                                         link);
+       cairo_surface_finish (&surface->base);
+    }
+
+    for (i = 0; i < ARRAY_LENGTH (info->gc); i++) {
+       if (info->gc_depths[i] != 0) {
+           XFreeGC (dpy, info->gc[i]);
+           info->gc_depths[i] = 0;
+       }
+    }
+
+    while (! cairo_list_is_empty (&info->visuals)) {
+        _cairo_xlib_visual_info_destroy (cairo_list_first_entry (&info->visuals,
+                                                                 cairo_xlib_visual_info_t,
+                                                                 link));
+    }
+
+    cairo_list_del (&info->link);
+
+    free (info);
+}
+
+cairo_status_t
+_cairo_xlib_screen_get (Display *dpy,
+                       Screen *screen,
+                       cairo_xlib_screen_t **out)
+{
+    cairo_xlib_display_t *display;
+    cairo_device_t *device;
+    cairo_xlib_screen_t *info;
+    cairo_status_t status;
+
+    device = _cairo_xlib_device_create (dpy);
+    status = device->status;
+    if (unlikely (status))
+        goto CLEANUP_DEVICE;
+
+    status =  _cairo_xlib_display_acquire (device, &display);
+    if (unlikely (status))
+        goto CLEANUP_DEVICE;
+
+    info = _cairo_xlib_display_get_screen (display, screen);
+    if (info != NULL) {
+       *out = info;
+       goto CLEANUP_DISPLAY;
+    }
+
+    info = malloc (sizeof (cairo_xlib_screen_t));
+    if (unlikely (info == NULL)) {
+       status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+       goto CLEANUP_DISPLAY;
+    }
+
+    info->device = device;
+    info->screen = screen;
+    info->has_font_options = FALSE;
+    memset (info->gc_depths, 0, sizeof (info->gc_depths));
+    memset (info->gc, 0, sizeof (info->gc));
+
+    cairo_list_init (&info->surfaces);
+    cairo_list_init (&info->visuals);
+    cairo_list_add (&info->link, &display->screens);
+
+    *out = info;
+
+  CLEANUP_DISPLAY:
+    cairo_device_release (&display->base);
+
+  CLEANUP_DEVICE:
+    cairo_device_destroy (device);
+    return status;
+}
+
+GC
+_cairo_xlib_screen_get_gc (cairo_xlib_display_t *display,
+                           cairo_xlib_screen_t *info,
+                          int depth,
+                          Drawable drawable)
+{
+    GC gc = NULL;
+    int i;
+
+    for (i = 0; i < ARRAY_LENGTH (info->gc); i++) {
+       if (info->gc_depths[i] == depth) {
+           info->gc_depths[i] = 0;
+           gc = info->gc[i];
+           break;
+       }
+    }
+
+    if (gc == NULL) {
+       XGCValues gcv;
+
+       gcv.graphics_exposures = False;
+       gcv.fill_style = FillTiled;
+       gc = XCreateGC (display->display,
+                       drawable,
+                       GCGraphicsExposures | GCFillStyle, &gcv);
+    }
+
+    return gc;
+}
+
+void
+_cairo_xlib_screen_put_gc (cairo_xlib_display_t *display,
+                           cairo_xlib_screen_t *info,
+                          int depth,
+                          GC gc)
+{
+    int i;
+
+    for (i = 0; i < ARRAY_LENGTH (info->gc); i++) {
+       if (info->gc_depths[i] == 0)
+           break;
+    }
+
+    if (i == ARRAY_LENGTH (info->gc)) {
+       /* perform random substitution to ensure fair caching over depths */
+       i = rand () % ARRAY_LENGTH (info->gc);
+       XFreeGC(display->display, info->gc[i]);
+    }
+
+    info->gc[i] = gc;
+    info->gc_depths[i] = depth;
+}
+
+cairo_status_t
+_cairo_xlib_screen_get_visual_info (cairo_xlib_display_t *display,
+                                    cairo_xlib_screen_t *info,
+                                   Visual *v,
+                                   cairo_xlib_visual_info_t **out)
+{
+    cairo_xlib_visual_info_t *visual;
+    cairo_status_t status;
+
+    cairo_list_foreach_entry (visual,
+                              cairo_xlib_visual_info_t,
+                              &info->visuals,
+                              link)
+    {
+       if (visual->visualid == v->visualid) {
+            *out = visual;
+            return CAIRO_STATUS_SUCCESS;
+       }
+    }
+
+    status = _cairo_xlib_visual_info_create (display->display,
+                                            XScreenNumberOfScreen (info->screen),
+                                            v->visualid,
+                                            &visual);
+    if (unlikely (status))
+       return status;
+
+    cairo_list_add (&visual->link, &info->visuals);
+    *out = visual;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_font_options_t *
+_cairo_xlib_screen_get_font_options (cairo_xlib_screen_t *info)
+{
+    if (! info->has_font_options) {
+       _cairo_font_options_init_default (&info->font_options);
+       _cairo_font_options_set_round_glyph_positions (&info->font_options, CAIRO_ROUND_GLYPH_POS_ON);
+
+       if (info->screen != NULL) {
+            cairo_xlib_display_t *display;
+
+            if (! _cairo_xlib_display_acquire (info->device, &display)) {
+                _cairo_xlib_init_screen_font_options (display->display,
+                                                      info);
+                cairo_device_release (&display->base);
+            }
+       }
+
+       info->has_font_options = TRUE;
+    }
+
+    return &info->font_options;
+}
+
+#endif /* !CAIRO_HAS_XLIB_XCB_FUNCTIONS */
diff --git a/src/cairo-xlib-source.c b/src/cairo-xlib-source.c
new file mode 100644 (file)
index 0000000..dc5aa7a
--- /dev/null
@@ -0,0 +1,938 @@
+/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *     Carl D. Worth <cworth@cworth.org>
+ *     Behdad Esfahbod <behdad@behdad.org>
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ *     Karl Tomlinson <karlt+@karlt.net>, Mozilla Corporation
+ */
+#include "cairoint.h"
+
+#if !CAIRO_HAS_XLIB_XCB_FUNCTIONS
+
+#include "cairo-xlib-private.h"
+#include "cairo-xlib-surface-private.h"
+
+#include "cairo-error-private.h"
+#include "cairo-image-surface-private.h"
+#include "cairo-paginated-private.h"
+#include "cairo-pattern-private.h"
+#include "cairo-recording-surface-private.h"
+#include "cairo-surface-backend-private.h"
+#include "cairo-surface-offset-private.h"
+#include "cairo-surface-observer-private.h"
+#include "cairo-surface-snapshot-private.h"
+
+#define PIXMAN_MAX_INT ((pixman_fixed_1 >> 1) - pixman_fixed_e) /* need to ensure deltas also fit */
+
+static cairo_surface_t *
+unwrap_surface (cairo_surface_t *surface, int *tx, int *ty)
+{
+    *tx = *ty = 0;
+
+    if (_cairo_surface_is_paginated (surface))
+       surface = _cairo_paginated_surface_get_recording (surface);
+    if (_cairo_surface_is_snapshot (surface))
+       surface = _cairo_surface_snapshot_get_target (surface);
+    if (_cairo_surface_is_observer (surface))
+       surface = _cairo_surface_observer_get_target (surface);
+    return surface;
+}
+
+static cairo_status_t
+_cairo_xlib_source_finish (void *abstract_surface)
+{
+    cairo_xlib_source_t *source = abstract_surface;
+
+    XRenderFreePicture (source->dpy, source->picture);
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static const cairo_surface_backend_t cairo_xlib_source_backend = {
+    CAIRO_SURFACE_TYPE_IMAGE,
+    _cairo_xlib_source_finish,
+    NULL, /* read-only wrapper */
+};
+
+static cairo_surface_t *
+source (cairo_xlib_surface_t *dst, Picture picture)
+{
+    cairo_xlib_source_t *source;
+
+    if (picture == None)
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+    source = malloc (sizeof (cairo_image_surface_t));
+    if (unlikely (source == NULL)) {
+       XRenderFreePicture (dst->display->display, picture);
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+    }
+
+    _cairo_surface_init (&source->base,
+                        &cairo_xlib_source_backend,
+                        NULL, /* device */
+                        CAIRO_CONTENT_COLOR_ALPHA);
+
+    /* The source exists only within an operation */
+    source->picture = picture;
+    source->dpy = dst->display->display;
+
+    return &source->base;
+}
+
+static uint32_t
+hars_petruska_f54_1_random (void)
+{
+#define rol(x,k) ((x << k) | (x >> (32-k)))
+    static uint32_t x;
+    return x = (x ^ rol (x, 5) ^ rol (x, 24)) + 0x37798849;
+#undef rol
+}
+
+static const XTransform identity = {
+    {
+       { 1 << 16, 0x00000, 0x00000 },
+       { 0x00000, 1 << 16, 0x00000 },
+       { 0x00000, 0x00000, 1 << 16 },
+    }
+};
+
+static cairo_bool_t
+picture_set_matrix (cairo_xlib_display_t *display,
+                   Picture picture,
+                   const cairo_matrix_t *matrix,
+                   cairo_filter_t        filter,
+                   double                xc,
+                   double                yc,
+                   int                  *x_offset,
+                   int                  *y_offset)
+{
+    XTransform xtransform;
+    pixman_transform_t *pixman_transform;
+    cairo_int_status_t status;
+
+    /* Casting between pixman_transform_t and XTransform is safe because
+     * they happen to be the exact same type.
+     */
+    pixman_transform = (pixman_transform_t *) &xtransform;
+    status = _cairo_matrix_to_pixman_matrix_offset (matrix, filter, xc, yc,
+                                                   pixman_transform,
+                                                   x_offset, y_offset);
+    if (status == CAIRO_INT_STATUS_NOTHING_TO_DO)
+       return TRUE;
+    if (unlikely (status != CAIRO_INT_STATUS_SUCCESS))
+       return FALSE;
+
+    if (memcmp (&xtransform, &identity, sizeof (XTransform)) == 0)
+       return TRUE;
+
+    /* a late check in case we perturb the matrix too far */
+    if (! CAIRO_RENDER_HAS_PICTURE_TRANSFORM (display))
+       return FALSE;
+
+    XRenderSetPictureTransform (display->display, picture, &xtransform);
+    return TRUE;
+}
+
+static cairo_status_t
+picture_set_filter (Display *dpy,
+                   Picture picture,
+                   cairo_filter_t filter)
+{
+    const char *render_filter;
+
+    switch (filter) {
+    case CAIRO_FILTER_FAST:
+       render_filter = FilterFast;
+       break;
+    case CAIRO_FILTER_GOOD:
+       render_filter = FilterGood;
+       break;
+    case CAIRO_FILTER_BEST:
+       render_filter = FilterBest;
+       break;
+    case CAIRO_FILTER_NEAREST:
+       render_filter = FilterNearest;
+       break;
+    case CAIRO_FILTER_BILINEAR:
+       render_filter = FilterBilinear;
+       break;
+    case CAIRO_FILTER_GAUSSIAN:
+       /* XXX: The GAUSSIAN value has no implementation in cairo
+        * whatsoever, so it was really a mistake to have it in the
+        * API. We could fix this by officially deprecating it, or
+        * else inventing semantics and providing an actual
+        * implementation for it. */
+    default:
+       render_filter = FilterBest;
+       break;
+    }
+
+    XRenderSetPictureFilter (dpy, picture, (char *) render_filter, NULL, 0);
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static int
+extend_to_repeat (cairo_extend_t extend)
+{
+    switch (extend) {
+    default:
+       ASSERT_NOT_REACHED;
+    case CAIRO_EXTEND_NONE:
+       return RepeatNone;
+    case CAIRO_EXTEND_REPEAT:
+       return RepeatNormal;
+    case CAIRO_EXTEND_REFLECT:
+       return RepeatReflect;
+    case CAIRO_EXTEND_PAD:
+       return RepeatPad;
+    }
+}
+
+static cairo_bool_t
+picture_set_properties (cairo_xlib_display_t *display,
+                       Picture picture,
+                       const cairo_pattern_t *pattern,
+                       const cairo_matrix_t *matrix,
+                       const cairo_rectangle_int_t *extents,
+                       int *x_off, int *y_off)
+{
+    XRenderPictureAttributes pa;
+    int mask = 0;
+
+    if (! picture_set_matrix (display, picture, matrix, pattern->filter,
+                             extents->x + extents->width / 2,
+                             extents->y + extents->height / 2,
+                             x_off, y_off))
+       return FALSE;
+
+    picture_set_filter (display->display, picture, pattern->filter);
+
+    if (pattern->has_component_alpha) {
+       pa.component_alpha = 1;
+       mask |= CPComponentAlpha;
+    }
+
+    if (pattern->extend != CAIRO_EXTEND_NONE) {
+       pa.repeat = extend_to_repeat (pattern->extend);
+       mask |= CPRepeat;
+    }
+
+    if (mask)
+       XRenderChangePicture (display->display, picture, mask, &pa);
+
+    return TRUE;
+}
+
+static cairo_surface_t *
+render_pattern (cairo_xlib_surface_t *dst,
+               const cairo_pattern_t *pattern,
+               cairo_bool_t is_mask,
+               const cairo_rectangle_int_t *extents,
+               int *src_x, int *src_y)
+{
+    Display *dpy = dst->display->display;
+    cairo_xlib_surface_t *src;
+    cairo_surface_t *image;
+    cairo_status_t status;
+
+    src = (cairo_xlib_surface_t *)
+       _cairo_surface_create_similar_scratch (&dst->base,
+                                              is_mask ? CAIRO_CONTENT_ALPHA : CAIRO_CONTENT_COLOR_ALPHA,
+                                              extents->width,
+                                              extents->height);
+    if (src->base.type != CAIRO_SURFACE_TYPE_XLIB) {
+       cairo_surface_destroy (&src->base);
+       return None;
+    }
+
+    image = cairo_surface_map_to_image (&src->base, NULL);
+    status = _cairo_surface_offset_paint (image, extents->x, extents->y,
+                                         CAIRO_OPERATOR_SOURCE, pattern,
+                                         NULL);
+    cairo_surface_unmap_image (&src->base, image);
+    if (unlikely (status)) {
+       cairo_surface_destroy (&src->base);
+       return _cairo_surface_create_in_error (status);
+    }
+
+    src->picture = XRenderCreatePicture (dpy,
+                                        src->drawable, src->xrender_format,
+                                        0, NULL);
+
+    *src_x = -extents->x;
+    *src_y = -extents->y;
+    return &src->base;
+}
+
+
+static cairo_surface_t *
+gradient_source (cairo_xlib_surface_t *dst,
+                const cairo_gradient_pattern_t *gradient,
+                cairo_bool_t is_mask,
+                const cairo_rectangle_int_t *extents,
+                int *src_x, int *src_y)
+{
+    cairo_xlib_display_t *display = dst->display;
+    cairo_matrix_t matrix = gradient->base.matrix;
+    char buf[CAIRO_STACK_BUFFER_SIZE];
+    cairo_circle_double_t extremes[2];
+    XFixed *stops;
+    XRenderColor *colors;
+    Picture picture;
+    unsigned int i, n_stops;
+
+    /* The RENDER specification says that the inner circle has
+     * to be completely contained inside the outer one. */
+    if (gradient->base.type == CAIRO_PATTERN_TYPE_RADIAL &&
+       ! _cairo_radial_pattern_focus_is_inside ((cairo_radial_pattern_t *) gradient))
+       return render_pattern (dst, &gradient->base, is_mask, extents, src_x, src_y);
+
+    assert (gradient->n_stops > 0);
+    n_stops = MAX (gradient->n_stops, 2);
+
+    if (n_stops < sizeof (buf) / (sizeof (XFixed) + sizeof (XRenderColor)))
+    {
+       stops = (XFixed *) buf;
+    }
+    else
+    {
+       stops =
+           _cairo_malloc_ab (n_stops,
+                             sizeof (XFixed) + sizeof (XRenderColor));
+       if (unlikely (stops == NULL))
+           return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+    }
+
+    colors = (XRenderColor *) (stops + n_stops);
+    for (i = 0; i < gradient->n_stops; i++) {
+       stops[i] =
+           _cairo_fixed_16_16_from_double (gradient->stops[i].offset);
+
+       colors[i].red   = gradient->stops[i].color.red_short;
+       colors[i].green = gradient->stops[i].color.green_short;
+       colors[i].blue  = gradient->stops[i].color.blue_short;
+       colors[i].alpha = gradient->stops[i].color.alpha_short;
+    }
+
+    /* RENDER does not support gradients with less than 2
+     * stops. If a gradient has only a single stop, duplicate
+     * it to make RENDER happy. */
+    if (gradient->n_stops == 1) {
+       stops[1] =
+           _cairo_fixed_16_16_from_double (gradient->stops[0].offset);
+
+       colors[1].red   = gradient->stops[0].color.red_short;
+       colors[1].green = gradient->stops[0].color.green_short;
+       colors[1].blue  = gradient->stops[0].color.blue_short;
+       colors[1].alpha = gradient->stops[0].color.alpha_short;
+    }
+
+#if 0
+    /* For some weird reason the X server is sometimes getting
+     * CreateGradient requests with bad length. So far I've only seen
+     * XRenderCreateLinearGradient request with 4 stops sometime end up
+     * with length field matching 0 stops at the server side. I've
+     * looked at the libXrender code and I can't see anything that
+     * could cause this behavior. However, for some reason having a
+     * XSync call here seems to avoid the issue so I'll keep it here
+     * until it's solved.
+     */
+    XSync (display->display, False);
+#endif
+
+    _cairo_gradient_pattern_fit_to_range (gradient, PIXMAN_MAX_INT >> 1, &matrix, extremes);
+
+    if (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR) {
+       XLinearGradient grad;
+
+       grad.p1.x = _cairo_fixed_16_16_from_double (extremes[0].center.x);
+       grad.p1.y = _cairo_fixed_16_16_from_double (extremes[0].center.y);
+       grad.p2.x = _cairo_fixed_16_16_from_double (extremes[1].center.x);
+       grad.p2.y = _cairo_fixed_16_16_from_double (extremes[1].center.y);
+
+       picture = XRenderCreateLinearGradient (display->display, &grad,
+                                              stops, colors,
+                                              n_stops);
+    } else {
+       XRadialGradient grad;
+
+       grad.inner.x      = _cairo_fixed_16_16_from_double (extremes[0].center.x);
+       grad.inner.y      = _cairo_fixed_16_16_from_double (extremes[0].center.y);
+       grad.inner.radius = _cairo_fixed_16_16_from_double (extremes[0].radius);
+       grad.outer.x      = _cairo_fixed_16_16_from_double (extremes[1].center.x);
+       grad.outer.y      = _cairo_fixed_16_16_from_double (extremes[1].center.y);
+       grad.outer.radius = _cairo_fixed_16_16_from_double (extremes[1].radius);
+
+       picture = XRenderCreateRadialGradient (display->display, &grad,
+                                              stops, colors,
+                                              n_stops);
+    }
+
+    if (stops != (XFixed *) buf)
+       free (stops);
+
+    *src_x = *src_y = 0;
+    if (! picture_set_properties (display, picture,
+                                 &gradient->base, &gradient->base.matrix,
+                                 extents,
+                                 src_x, src_y)) {
+       XRenderFreePicture (display->display, picture);
+       return render_pattern (dst, &gradient->base, is_mask, extents, src_x, src_y);
+    }
+
+    return source (dst, picture);
+}
+
+static cairo_surface_t *
+color_source (cairo_xlib_surface_t *dst, const cairo_color_t *color)
+{
+    XRenderColor xrender_color;
+
+    xrender_color.red   = color->red_short;
+    xrender_color.green = color->green_short;
+    xrender_color.blue  = color->blue_short;
+    xrender_color.alpha = color->alpha_short;
+
+    return source (dst,
+                  XRenderCreateSolidFill (dst->display->display,
+                                          &xrender_color));
+}
+
+static cairo_surface_t *
+alpha_source (cairo_xlib_surface_t *dst, uint8_t alpha)
+{
+    cairo_xlib_display_t *display = dst->display;
+
+    if (display->alpha[alpha] == NULL) {
+       cairo_color_t color;
+
+       color.red_short = color.green_short = color.blue_short = 0;
+       color.alpha_short = alpha << 8 | alpha;
+
+       display->alpha[alpha] = color_source (dst, &color);
+    }
+
+    return cairo_surface_reference (display->alpha[alpha]);
+}
+
+static cairo_surface_t *
+white_source (cairo_xlib_surface_t *dst)
+{
+    cairo_xlib_display_t *display = dst->display;
+
+    if (display->white == NULL)
+       display->white = color_source (dst, CAIRO_COLOR_WHITE);
+
+    return cairo_surface_reference (display->white);
+}
+
+static cairo_surface_t *
+opaque_source (cairo_xlib_surface_t *dst, const cairo_color_t *color)
+{
+    cairo_xlib_display_t *display = dst->display;
+    uint32_t pixel =
+       0xff000000 |
+       color->red_short   >> 8 << 16 |
+       color->green_short >> 8 << 8 |
+       color->blue_short  >> 8 << 0;
+    int i;
+
+    if (display->last_solid_cache[0].color == pixel)
+       return cairo_surface_reference (display->solid[display->last_solid_cache[0].index]);
+
+    for (i = 0; i < 16; i++) {
+       if (display->solid_cache[i] == pixel)
+           goto done;
+    }
+
+    i = hars_petruska_f54_1_random () % 16;
+    cairo_surface_destroy (display->solid[i]);
+
+    display->solid[i] = color_source (dst, color);
+    display->solid_cache[i] = pixel;
+
+done:
+    display->last_solid_cache[0].color = pixel;
+    display->last_solid_cache[0].index = i;
+    return cairo_surface_reference (display->solid[i]);
+}
+
+static cairo_surface_t *
+transparent_source (cairo_xlib_surface_t *dst, const cairo_color_t *color)
+{
+    cairo_xlib_display_t *display = dst->display;
+    uint32_t pixel =
+       color->alpha_short >> 8 << 24 |
+       color->red_short   >> 8 << 16 |
+       color->green_short >> 8 << 8 |
+       color->blue_short  >> 8 << 0;
+    int i;
+
+    if (display->last_solid_cache[1].color == pixel) {
+    assert (display->solid[display->last_solid_cache[1].index]);
+       return cairo_surface_reference (display->solid[display->last_solid_cache[1].index]);
+    }
+
+    for (i = 16; i < 32; i++) {
+       if (display->solid_cache[i] == pixel)
+           goto done;
+    }
+
+    i = 16 + (hars_petruska_f54_1_random () % 16);
+    cairo_surface_destroy (display->solid[i]);
+
+    display->solid[i] = color_source (dst, color);
+    display->solid_cache[i] = pixel;
+
+done:
+    display->last_solid_cache[1].color = pixel;
+    display->last_solid_cache[1].index = i;
+    assert (display->solid[i]);
+    return cairo_surface_reference (display->solid[i]);
+}
+
+static cairo_surface_t *
+solid_source (cairo_xlib_surface_t *dst,
+             const cairo_color_t *color)
+{
+    if ((color->red_short | color->green_short | color->blue_short) <= 0xff)
+       return alpha_source (dst, color->alpha_short >> 8);
+
+    if (CAIRO_ALPHA_SHORT_IS_OPAQUE (color->alpha_short)) {
+       if (color->red_short >= 0xff00 && color->green_short >= 0xff00 && color->blue_short >= 0xff00)
+           return white_source (dst);
+
+       return opaque_source (dst, color);
+    } else
+       return transparent_source (dst, color);
+}
+
+static cairo_surface_t *
+native_source (cairo_xlib_surface_t *dst,
+              const cairo_surface_pattern_t *pattern,
+              cairo_bool_t is_mask,
+              const cairo_rectangle_int_t *extents,
+              const cairo_rectangle_int_t *sample,
+              int *src_x, int *src_y)
+{
+    cairo_xlib_surface_t *src = (cairo_xlib_surface_t *) pattern->surface;
+    cairo_xlib_source_t *source;
+    Display *dpy = dst->display->display;
+    cairo_bool_t is_contained = FALSE;
+    cairo_int_status_t status;
+    XTransform xtransform;
+    XRenderPictureAttributes pa;
+    int mask = 0;
+
+    src = (cairo_xlib_surface_t *)
+           unwrap_surface (pattern->surface, src_x, src_y);
+
+    if (sample->x >= 0 && sample->y >= 0 &&
+       sample->x + sample->width  <= src->width &&
+       sample->y + sample->height <= src->height)
+    {
+       is_contained = TRUE;
+    }
+
+    if (pattern->base.filter == CAIRO_FILTER_NEAREST && is_contained) {
+       *src_x += pattern->base.matrix.x0;
+       *src_y += pattern->base.matrix.y0;
+       _cairo_xlib_surface_ensure_picture (src);
+       return cairo_surface_reference (&src->base);
+    }
+
+    /* As these are frequent and meant to be fast we track pictures for
+     * enative surface and minimse update requests.
+     */
+    source = &src->embedded_source;
+    if (source->picture == None) {
+       Picture picture;
+       XRenderPictureAttributes pa;
+       int mask = 0;
+
+       pa.subwindow_mode = IncludeInferiors;
+       mask |= CPSubwindowMode;
+
+       picture = XRenderCreatePicture (dpy,
+                                       src->drawable,
+                                       src->xrender_format,
+                                       mask, &pa);
+
+       _cairo_surface_init (&source->base,
+                            &cairo_xlib_source_backend,
+                            NULL, /* device */
+                            CAIRO_CONTENT_COLOR_ALPHA);
+       source->picture = picture;
+
+       source->has_component_alpha = 0;
+       source->has_matrix = 0;
+       source->filter = CAIRO_FILTER_NEAREST;
+       source->extend = CAIRO_EXTEND_NONE;
+    }
+
+    status = _cairo_matrix_to_pixman_matrix_offset (&pattern->base.matrix,
+                                                   pattern->base.filter,
+                                                   extents->x + extents->width / 2,
+                                                   extents->y + extents->height / 2,
+                                                   (pixman_transform_t *)&xtransform,
+                                                   src_x, src_y);
+
+    if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) {
+       if (source->has_matrix) {
+           source->has_matrix = 0;
+           memcpy (&xtransform, &identity, sizeof (identity));
+           status = CAIRO_INT_STATUS_SUCCESS;
+       }
+    } else
+       source->has_matrix = 1;
+    if (status == CAIRO_INT_STATUS_SUCCESS)
+       XRenderSetPictureTransform (dpy, source->picture, &xtransform);
+
+    if (source->filter != pattern->base.filter) {
+       picture_set_filter (dpy, source->picture, pattern->base.filter);
+       source->filter = pattern->base.filter;
+    }
+
+    if (source->has_component_alpha != pattern->base.has_component_alpha) {
+       pa.component_alpha = pattern->base.has_component_alpha;
+       mask |= CPComponentAlpha;
+       source->has_component_alpha = pattern->base.has_component_alpha;
+    }
+
+    if (source->extend != pattern->base.extend) {
+       pa.repeat = extend_to_repeat (pattern->base.extend);
+       mask |= CPRepeat;
+       source->extend = pattern->base.extend;
+    }
+
+    if (mask)
+       XRenderChangePicture (dpy, source->picture, mask, &pa);
+
+    return cairo_surface_reference (&source->base);
+}
+
+#if 0
+/* It is general quicker if we let the application choose which images
+ * to cache for itself and only upload the fragments required for this
+ * operation.
+ */
+static cairo_surface_t *
+image_source (cairo_xlib_surface_t *dst,
+             const cairo_surface_pattern_t *pattern,
+             const cairo_rectangle_int_t *extents,
+             int *src_x, int *src_y)
+{
+    cairo_image_surface_t *src = (cairo_image_surface_t *) pattern->surface;
+    cairo_xlib_surface_t *snapshot;
+    cairo_surface_pattern_t local_pattern;
+    cairo_status_t status;
+
+    snapshot = (cairo_xlib_surface_t *)
+       _cairo_surface_has_snapshot (&src->base, dst->base.backend);
+    if (snapshot == NULL || snapshot->screen != dst->screen) {
+       if (snapshot)
+           _cairo_surface_detach_snapshot (&snapshot->base);
+
+       snapshot = (cairo_xlib_surface_t *)
+           _cairo_surface_create_similar_scratch (&dst->base,
+                                                  src->base.content,
+                                                  src->width,
+                                                  src->height);
+       if (snapshot->base.type != CAIRO_SURFACE_TYPE_XLIB) {
+           cairo_surface_destroy (&snapshot->base);
+           return _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY);
+       }
+
+       status = _cairo_xlib_surface_draw_image (snapshot, src,
+                                                0, 0,
+                                                src->width, src->height,
+                                                0, 0);
+       if (unlikely (status)) {
+           cairo_surface_destroy (&snapshot->base);
+           return _cairo_surface_create_in_error (status);
+       }
+
+       _cairo_surface_attach_snapshot (&src->base,
+                                       &snapshot->base,
+                                       cairo_surface_finish);
+
+       /* reference remains held by the snapshot from image */
+       cairo_surface_destroy (&snapshot->base);
+    }
+
+    local_pattern = *pattern;
+    local_pattern.surface = &snapshot->base;
+
+    return native_source (dst, &local_pattern, extents, src_x, src_y);
+}
+#endif
+
+static cairo_surface_t *
+recording_pattern_get_surface (const cairo_pattern_t *pattern)
+{
+    cairo_surface_t *surface;
+
+    surface = ((const cairo_surface_pattern_t *) pattern)->surface;
+    if (_cairo_surface_is_paginated (surface))
+       surface = _cairo_paginated_surface_get_recording (surface);
+    if (_cairo_surface_is_snapshot (surface))
+       surface = _cairo_surface_snapshot_get_target (surface);
+    return surface;
+}
+
+static cairo_surface_t *
+record_source (cairo_xlib_surface_t *dst,
+              const cairo_surface_pattern_t *pattern,
+              cairo_bool_t is_mask,
+              const cairo_rectangle_int_t *extents,
+              const cairo_rectangle_int_t *sample,
+              int *src_x, int *src_y)
+{
+    cairo_xlib_surface_t *src;
+    cairo_matrix_t matrix, m;
+    cairo_status_t status;
+    cairo_rectangle_int_t upload, limit;
+
+    upload = *sample;
+    if (_cairo_surface_get_extents (pattern->surface, &limit) &&
+       ! _cairo_rectangle_intersect (&upload, &limit))
+    {
+       if (pattern->base.extend == CAIRO_EXTEND_NONE)
+           return alpha_source (dst, 0);
+
+       upload = limit;
+    }
+
+    src = (cairo_xlib_surface_t *)
+       _cairo_surface_create_similar_scratch (&dst->base,
+                                              pattern->surface->content,
+                                              upload.width,
+                                              upload.height);
+    if (src->base.type != CAIRO_SURFACE_TYPE_XLIB) {
+       cairo_surface_destroy (&src->base);
+       return _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY);
+    }
+
+    cairo_matrix_init_translate (&matrix, upload.x, upload.y);
+    status = _cairo_recording_surface_replay_with_clip (recording_pattern_get_surface (&pattern->base),
+                                                       &matrix, &src->base,
+                                                       NULL);
+    if (unlikely (status)) {
+       cairo_surface_destroy (&src->base);
+       return _cairo_surface_create_in_error (status);
+    }
+
+    matrix = pattern->base.matrix;
+    if (upload.x | upload.y) {
+       cairo_matrix_init_translate (&m, -upload.x, -upload.y);
+       cairo_matrix_multiply (&matrix, &matrix, &m);
+    }
+
+    _cairo_xlib_surface_ensure_picture (src);
+    if (! picture_set_properties (src->display, src->picture,
+                                 &pattern->base, &matrix, extents,
+                                 src_x, src_y))
+    {
+       cairo_surface_destroy (&src->base);
+       return render_pattern (dst, &pattern->base, is_mask,
+                              extents, src_x, src_y);
+    }
+
+    return &src->base;
+}
+
+static cairo_surface_t *
+surface_source (cairo_xlib_surface_t *dst,
+               const cairo_surface_pattern_t *pattern,
+               cairo_bool_t is_mask,
+               const cairo_rectangle_int_t *extents,
+               const cairo_rectangle_int_t *sample,
+               int *src_x, int *src_y)
+{
+    cairo_xlib_surface_t *src;
+    cairo_surface_t *image;
+    cairo_surface_pattern_t local_pattern;
+    cairo_status_t status;
+    cairo_rectangle_int_t upload, limit;
+    cairo_matrix_t m;
+
+    upload = *sample;
+    if (_cairo_surface_get_extents (pattern->surface, &limit) &&
+       ! _cairo_rectangle_intersect (&upload, &limit))
+    {
+       if (pattern->base.extend == CAIRO_EXTEND_NONE)
+           return alpha_source (dst, 0);
+
+       upload = limit;
+    }
+
+    src = (cairo_xlib_surface_t *)
+       _cairo_surface_create_similar_scratch (&dst->base,
+                                              pattern->surface->content,
+                                              upload.width,
+                                              upload.height);
+    if (src->base.type != CAIRO_SURFACE_TYPE_XLIB) {
+       cairo_surface_destroy (&src->base);
+       return _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY);
+    }
+
+    _cairo_pattern_init_for_surface (&local_pattern, pattern->surface);
+    cairo_matrix_init_translate (&local_pattern.base.matrix,
+                                upload.x, upload.y);
+
+    image = cairo_surface_map_to_image (&src->base, NULL);
+    status = _cairo_surface_paint (image,
+                                  CAIRO_OPERATOR_SOURCE,
+                                  &local_pattern.base,
+                                  NULL);
+    cairo_surface_unmap_image (&src->base, image);
+    _cairo_pattern_fini (&local_pattern.base);
+
+    if (unlikely (status)) {
+       cairo_surface_destroy (&src->base);
+       return _cairo_surface_create_in_error (status);
+    }
+
+    local_pattern.base.matrix = pattern->base.matrix;
+    if (upload.x | upload.y) {
+       cairo_matrix_init_translate (&m, -upload.x, -upload.y);
+       cairo_matrix_multiply (&local_pattern.base.matrix,
+                              &local_pattern.base.matrix,
+                              &m);
+    }
+
+    *src_x = *src_y = 0;
+    _cairo_xlib_surface_ensure_picture (src);
+    if (! picture_set_properties (src->display,
+                                 src->picture,
+                                 &pattern->base,
+                                 &local_pattern.base.matrix,
+                                 extents,
+                                 src_x, src_y))
+    {
+       cairo_surface_destroy (&src->base);
+       return render_pattern (dst, &pattern->base,
+                              is_mask, extents,
+                              src_x, src_y);
+    }
+
+    return &src->base;
+}
+
+static cairo_bool_t
+pattern_is_supported (cairo_xlib_display_t *display,
+                     const cairo_pattern_t *pattern)
+{
+    if (pattern->type == CAIRO_PATTERN_TYPE_MESH)
+       return FALSE;
+
+    if (display->buggy_pad_reflect) {
+       if (pattern->extend == CAIRO_EXTEND_REPEAT || pattern->extend == CAIRO_EXTEND_PAD)
+           return FALSE;
+    }
+
+    if (display->buggy_gradients) {
+       if (pattern->type == CAIRO_PATTERN_TYPE_LINEAR || pattern->type == CAIRO_PATTERN_TYPE_RADIAL)
+           return FALSE;
+    }
+
+    if (! CAIRO_RENDER_HAS_PICTURE_TRANSFORM (display)) {
+       if (!_cairo_matrix_is_integer_translation (&pattern->matrix, NULL, NULL))
+           return FALSE;
+    }
+
+    if (! CAIRO_RENDER_HAS_FILTERS (display)) {
+           /* No filters implies no transforms, so we optimise away BILINEAR */
+    }
+
+    return TRUE;
+}
+cairo_surface_t *
+_cairo_xlib_source_create_for_pattern (cairo_surface_t *_dst,
+                                      const cairo_pattern_t *pattern,
+                                      cairo_bool_t is_mask,
+                                      const cairo_rectangle_int_t *extents,
+                                      const cairo_rectangle_int_t *sample,
+                                      int *src_x, int *src_y)
+{
+    cairo_xlib_surface_t *dst = (cairo_xlib_surface_t *)_dst;
+    int tx, ty;
+
+    if (pattern == NULL || pattern->type == CAIRO_PATTERN_TYPE_SOLID) {
+       *src_x = *src_y = 0;
+
+       if (pattern == NULL)
+           pattern = &_cairo_pattern_white.base;
+
+       return solid_source (dst, &((cairo_solid_pattern_t *)pattern)->color);
+    }
+
+    if (pattern_is_supported (dst->display, pattern)) {
+       if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) {
+           cairo_surface_pattern_t *spattern = (cairo_surface_pattern_t *)pattern;
+           if (spattern->surface->type == CAIRO_SURFACE_TYPE_XLIB &&
+               _cairo_xlib_surface_same_screen (dst,
+                                                (cairo_xlib_surface_t *)unwrap_surface (spattern->surface, &tx, &ty)))
+               return native_source (dst, spattern, is_mask,
+                                     extents, sample,
+                                     src_x, src_y);
+
+           if (spattern->surface->type == CAIRO_SURFACE_TYPE_RECORDING)
+               return record_source (dst, spattern, is_mask,
+                                     extents, sample,
+                                     src_x, src_y);
+#if 0
+           if (spattern->surface->type == CAIRO_SURFACE_TYPE_IMAGE)
+               return image_source (dst, spattern, extents, src_x, src_y);
+#endif
+
+           return surface_source (dst, spattern, is_mask,
+                                  extents, sample,
+                                  src_x, src_y);
+       }
+
+       if (pattern->type == CAIRO_PATTERN_TYPE_LINEAR ||
+           pattern->type == CAIRO_PATTERN_TYPE_RADIAL)
+       {
+           cairo_gradient_pattern_t *gpattern = (cairo_gradient_pattern_t *)pattern;
+           return gradient_source (dst, gpattern, is_mask, extents, src_x, src_y);
+       }
+    }
+
+    return render_pattern (dst, pattern, is_mask, extents, src_x, src_y);
+}
+
+#endif /* !CAIRO_HAS_XLIB_XCB_FUNCTIONS */
diff --git a/src/cairo-xlib-surface-private.h b/src/cairo-xlib-surface-private.h
new file mode 100644 (file)
index 0000000..87db696
--- /dev/null
@@ -0,0 +1,44 @@
+/* Cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ */
+
+#ifndef CAIRO_XLIB_SURFACE_PRIVATE_H
+#define CAIRO_XLIB_SURFACE_PRIVATE_H
+
+#include "cairo-xlib.h"
+#include "cairo-xlib-private.h"
+#include "cairo-xlib-xrender-private.h"
+
+#include "cairo-surface-private.h"
+#include "cairo-surface-backend-private.h"
+
+
+#endif /* CAIRO_XLIB_SURFACE_PRIVATE_H */
diff --git a/src/cairo-xlib-surface.c b/src/cairo-xlib-surface.c
new file mode 100644 (file)
index 0000000..1806e64
--- /dev/null
@@ -0,0 +1,2038 @@
+/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *     Carl D. Worth <cworth@cworth.org>
+ *     Behdad Esfahbod <behdad@behdad.org>
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ *     Karl Tomlinson <karlt+@karlt.net>, Mozilla Corporation
+ */
+
+/* Heed well the words of Owen Taylor:
+ * "Any patch that works around a render bug, or claims to, without a
+ * specific reference to the bug filed in bugzilla.freedesktop.org will
+ * never pass approval."
+ */
+
+#include "cairoint.h"
+
+#if !CAIRO_HAS_XLIB_XCB_FUNCTIONS
+
+#include "cairo-xlib-private.h"
+#include "cairo-xlib-surface-private.h"
+
+#include "cairo-compositor-private.h"
+#include "cairo-clip-private.h"
+#include "cairo-default-context-private.h"
+#include "cairo-error-private.h"
+#include "cairo-image-surface-private.h"
+#include "cairo-pattern-private.h"
+#include "cairo-region-private.h"
+#include "cairo-scaled-font-private.h"
+#include "cairo-surface-snapshot-private.h"
+#include "cairo-surface-subsurface-private.h"
+
+#include <X11/Xutil.h> /* for XDestroyImage */
+
+#define XLIB_COORD_MAX 32767
+
+#define DEBUG 0
+
+#if DEBUG
+#define UNSUPPORTED(reason) \
+    fprintf (stderr, \
+            "cairo-xlib: hit unsupported operation %s(), line %d: %s\n", \
+            __FUNCTION__, __LINE__, reason), \
+    CAIRO_INT_STATUS_UNSUPPORTED
+#else
+#define UNSUPPORTED(reason) CAIRO_INT_STATUS_UNSUPPORTED
+#endif
+
+#if DEBUG
+#include <X11/Xlibint.h>
+static void CAIRO_PRINTF_FORMAT (2, 3)
+_x_bread_crumb (Display *dpy,
+               const char *fmt,
+               ...)
+{
+    xReq *req;
+    char buf[2048];
+    unsigned int len, len_dwords;
+    va_list ap;
+
+    va_start (ap, fmt);
+    len = vsnprintf (buf, sizeof (buf), fmt, ap);
+    va_end (ap);
+
+    buf[len++] = '\0';
+    while (len & 3)
+       buf[len++] = '\0';
+
+    LockDisplay (dpy);
+    GetEmptyReq (NoOperation, req);
+
+    len_dwords = len >> 2;
+    SetReqLen (req, len_dwords, len_dwords);
+    Data (dpy, buf, len);
+
+    UnlockDisplay (dpy);
+    SyncHandle ();
+}
+#define X_DEBUG(x) _x_bread_crumb x
+#else
+#define X_DEBUG(x)
+#endif
+
+/**
+ * SECTION:cairo-xlib
+ * @Title: XLib Surfaces
+ * @Short_Description: X Window System rendering using XLib
+ * @See_Also: #cairo_surface_t
+ *
+ * The XLib surface is used to render cairo graphics to X Window System
+ * windows and pixmaps using the XLib library.
+ *
+ * Note that the XLib surface automatically takes advantage of X render extension
+ * if it is available.
+ */
+
+/**
+ * CAIRO_HAS_XLIB_SURFACE:
+ *
+ * Defined if the Xlib surface backend is available.
+ * This macro can be used to conditionally compile backend-specific code.
+ */
+
+/**
+ * SECTION:cairo-xlib-xrender
+ * @Title: XLib/XRender Backend
+ * @Short_Description: X Window System rendering using XLib and the X Render extension
+ * @See_Also: #cairo_surface_t
+ *
+ * The XLib surface is used to render cairo graphics to X Window System
+ * windows and pixmaps using the XLib and Xrender libraries.
+ *
+ * Note that the XLib surface automatically takes advantage of X Render extension
+ * if it is available.
+ */
+
+/**
+ * CAIRO_HAS_XLIB_XRENDER_SURFACE:
+ *
+ * Defined if the XLib/XRender surface functions are available.
+ * This macro can be used to conditionally compile backend-specific code.
+ */
+
+/* Xlib doesn't define a typedef, so define one ourselves */
+typedef int (*cairo_xlib_error_func_t) (Display     *display,
+                                       XErrorEvent *event);
+
+static cairo_surface_t *
+_cairo_xlib_surface_create_internal (cairo_xlib_screen_t       *screen,
+                                    Drawable                   drawable,
+                                    Visual                    *visual,
+                                    XRenderPictFormat         *xrender_format,
+                                    int                        width,
+                                    int                        height,
+                                    int                        depth);
+
+static cairo_bool_t
+_cairo_surface_is_xlib (cairo_surface_t *surface);
+
+/*
+ * Instead of taking two round trips for each blending request,
+ * assume that if a particular drawable fails GetImage that it will
+ * fail for a "while"; use temporary pixmaps to avoid the errors
+ */
+
+#define CAIRO_ASSUME_PIXMAP    20
+
+static const XTransform identity = { {
+    { 1 << 16, 0x00000, 0x00000 },
+    { 0x00000, 1 << 16, 0x00000 },
+    { 0x00000, 0x00000, 1 << 16 },
+} };
+
+static Visual *
+_visual_for_xrender_format(Screen *screen,
+                          XRenderPictFormat *xrender_format)
+{
+    int d, v;
+
+    /* XXX Consider searching through the list of known cairo_visual_t for
+     * the reverse mapping.
+     */
+
+    for (d = 0; d < screen->ndepths; d++) {
+       Depth *d_info = &screen->depths[d];
+
+       if (d_info->depth != xrender_format->depth)
+           continue;
+
+       for (v = 0; v < d_info->nvisuals; v++) {
+           Visual *visual = &d_info->visuals[v];
+
+           switch (visual->class) {
+           case TrueColor:
+               if (xrender_format->type != PictTypeDirect)
+                   continue;
+               break;
+
+           case DirectColor:
+               /* Prefer TrueColor to DirectColor.
+                * (XRenderFindVisualFormat considers both TrueColor and DirectColor
+                * Visuals to match the same PictFormat.)
+                */
+               continue;
+
+           case StaticGray:
+           case GrayScale:
+           case StaticColor:
+           case PseudoColor:
+               if (xrender_format->type != PictTypeIndexed)
+                   continue;
+               break;
+           }
+
+           if (xrender_format ==
+               XRenderFindVisualFormat (DisplayOfScreen(screen), visual))
+               return visual;
+       }
+    }
+
+    return NULL;
+}
+
+static cairo_content_t
+_xrender_format_to_content (XRenderPictFormat *xrender_format)
+{
+    cairo_bool_t xrender_format_has_alpha;
+    cairo_bool_t xrender_format_has_color;
+
+    /* This only happens when using a non-Render server. Let's punt
+     * and say there's no alpha here. */
+    if (xrender_format == NULL)
+       return CAIRO_CONTENT_COLOR;
+
+    xrender_format_has_alpha = (xrender_format->direct.alphaMask != 0);
+    xrender_format_has_color = (xrender_format->direct.redMask   != 0 ||
+                               xrender_format->direct.greenMask != 0 ||
+                               xrender_format->direct.blueMask  != 0);
+
+    if (xrender_format_has_alpha)
+       if (xrender_format_has_color)
+           return CAIRO_CONTENT_COLOR_ALPHA;
+       else
+           return CAIRO_CONTENT_ALPHA;
+    else
+       return CAIRO_CONTENT_COLOR;
+}
+
+static cairo_surface_t *
+_cairo_xlib_surface_create_similar (void              *abstract_src,
+                                   cairo_content_t     content,
+                                   int                 width,
+                                   int                 height)
+{
+    cairo_xlib_surface_t *src = abstract_src;
+    XRenderPictFormat *xrender_format;
+    cairo_xlib_surface_t *surface;
+    cairo_xlib_display_t *display;
+    Pixmap pix;
+
+    if (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX)
+       return NULL;
+
+    if (width == 0 || height == 0)
+       return NULL;
+
+    if (_cairo_xlib_display_acquire (src->base.device, &display))
+        return NULL;
+
+    /* If we never found an XRenderFormat or if it isn't compatible
+     * with the content being requested, then we fallback to just
+     * constructing a cairo_format_t instead, (which will fairly
+     * arbitrarily pick a visual/depth for the similar surface.
+     */
+    xrender_format = NULL;
+    if (src->xrender_format &&
+       _xrender_format_to_content (src->xrender_format) == content)
+    {
+       xrender_format = src->xrender_format;
+    }
+    if (xrender_format == NULL) {
+       xrender_format =
+           _cairo_xlib_display_get_xrender_format (display,
+                                                   _cairo_format_from_content (content));
+    }
+    if (xrender_format) {
+       Visual *visual;
+
+       /* We've got a compatible XRenderFormat now, which means the
+        * similar surface will match the existing surface as closely in
+        * visual/depth etc. as possible. */
+       pix = XCreatePixmap (display->display, src->drawable,
+                            width, height, xrender_format->depth);
+
+       if (xrender_format == src->xrender_format)
+           visual = src->visual;
+       else
+           visual = _visual_for_xrender_format(src->screen->screen,
+                                               xrender_format);
+
+       surface = (cairo_xlib_surface_t *)
+                 _cairo_xlib_surface_create_internal (src->screen, pix, visual,
+                                                      xrender_format,
+                                                      width, height,
+                                                      xrender_format->depth);
+    }
+    else
+    {
+       Screen *screen = src->screen->screen;
+       int depth;
+
+       /* No compatabile XRenderFormat, see if we can make an ordinary pixmap,
+        * so that we can still accelerate blits with XCopyArea(). */
+       if (content != CAIRO_CONTENT_COLOR) {
+            cairo_device_release (&display->base);
+           return NULL;
+        }
+
+       depth = DefaultDepthOfScreen (screen);
+
+       pix = XCreatePixmap (display->display, RootWindowOfScreen (screen),
+                            width <= 0 ? 1 : width, height <= 0 ? 1 : height,
+                            depth);
+
+       surface = (cairo_xlib_surface_t *)
+                 _cairo_xlib_surface_create_internal (src->screen, pix,
+                                                      DefaultVisualOfScreen (screen),
+                                                      NULL,
+                                                      width, height, depth);
+    }
+
+    if (likely (surface->base.status == CAIRO_STATUS_SUCCESS))
+       surface->owns_pixmap = TRUE;
+    else
+       XFreePixmap (display->display, pix);
+
+    cairo_device_release (&display->base);
+
+    return &surface->base;
+}
+
+static cairo_status_t
+_cairo_xlib_surface_finish (void *abstract_surface)
+{
+    cairo_xlib_surface_t *surface = abstract_surface;
+    cairo_status_t        status;
+    cairo_xlib_display_t *display;
+
+    X_DEBUG ((display->display, "finish (drawable=%x)", (unsigned int) surface->drawable));
+
+    cairo_list_del (&surface->link);
+
+    status = _cairo_xlib_display_acquire (surface->base.device, &display);
+    if (unlikely (status))
+        return status;
+
+    if (surface->embedded_source.picture)
+       XRenderFreePicture (display->display, surface->embedded_source.picture);
+    if (surface->picture)
+       XRenderFreePicture (display->display, surface->picture);
+    if (surface->owns_pixmap)
+       XFreePixmap (display->display, surface->drawable);
+
+    cairo_device_release (&display->base);
+
+    return status;
+}
+
+cairo_status_t
+_cairo_xlib_surface_get_gc (cairo_xlib_display_t *display,
+                            cairo_xlib_surface_t *surface,
+                            GC                   *gc)
+{
+    *gc = _cairo_xlib_screen_get_gc (display,
+                                     surface->screen,
+                                    surface->depth,
+                                    surface->drawable);
+    if (unlikely (*gc == NULL))
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static int
+_noop_error_handler (Display     *display,
+                    XErrorEvent *event)
+{
+    return False;              /* return value is ignored */
+}
+
+static void
+_swap_ximage_2bytes (XImage *ximage)
+{
+    int i, j;
+    char *line = ximage->data;
+
+    for (j = ximage->height; j; j--) {
+       uint16_t *p = (uint16_t *) line;
+       for (i = ximage->width; i; i--) {
+           *p = bswap_16 (*p);
+           p++;
+       }
+
+       line += ximage->bytes_per_line;
+    }
+}
+
+static void
+_swap_ximage_3bytes (XImage *ximage)
+{
+    int i, j;
+    char *line = ximage->data;
+
+    for (j = ximage->height; j; j--) {
+       uint8_t *p = (uint8_t *) line;
+       for (i = ximage->width; i; i--) {
+           uint8_t tmp;
+           tmp = p[2];
+           p[2] = p[0];
+           p[0] = tmp;
+           p += 3;
+       }
+
+       line += ximage->bytes_per_line;
+    }
+}
+
+static void
+_swap_ximage_4bytes (XImage *ximage)
+{
+    int i, j;
+    char *line = ximage->data;
+
+    for (j = ximage->height; j; j--) {
+       uint32_t *p = (uint32_t *) line;
+       for (i = ximage->width; i; i--) {
+           *p = bswap_32 (*p);
+           p++;
+       }
+
+       line += ximage->bytes_per_line;
+    }
+}
+
+static void
+_swap_ximage_nibbles (XImage *ximage)
+{
+    int i, j;
+    char *line = ximage->data;
+
+    for (j = ximage->height; j; j--) {
+       uint8_t *p = (uint8_t *) line;
+       for (i = (ximage->width + 1) / 2; i; i--) {
+           *p = ((*p >> 4) & 0xf) | ((*p << 4) & ~0xf);
+           p++;
+       }
+
+       line += ximage->bytes_per_line;
+    }
+}
+
+static void
+_swap_ximage_bits (XImage *ximage)
+{
+    int i, j;
+    char *line = ximage->data;
+    int unit = ximage->bitmap_unit;
+    int line_bytes = ((ximage->width + unit - 1) & ~(unit - 1)) / 8;
+
+    for (j = ximage->height; j; j--) {
+       char *p = line;
+
+       for (i = line_bytes; i; i--) {
+           char b = *p;
+           b = ((b << 1) & 0xaa) | ((b >> 1) & 0x55);
+           b = ((b << 2) & 0xcc) | ((b >> 2) & 0x33);
+           b = ((b << 4) & 0xf0) | ((b >> 4) & 0x0f);
+           *p = b;
+
+           p++;
+       }
+
+       line += ximage->bytes_per_line;
+    }
+}
+
+static void
+_swap_ximage_to_native (XImage *ximage)
+{
+    int unit_bytes = 0;
+    int native_byte_order = _cairo_is_little_endian () ? LSBFirst : MSBFirst;
+
+    if (ximage->bits_per_pixel == 1 &&
+       ximage->bitmap_bit_order != native_byte_order)
+    {
+       _swap_ximage_bits (ximage);
+       if (ximage->bitmap_bit_order == ximage->byte_order)
+           return;
+    }
+
+    if (ximage->byte_order == native_byte_order)
+       return;
+
+    switch (ximage->bits_per_pixel) {
+    case 1:
+       unit_bytes = ximage->bitmap_unit / 8;
+       break;
+    case 4:
+       _swap_ximage_nibbles (ximage);
+       /* fall-through */
+    case 8:
+    case 16:
+    case 20:
+    case 24:
+    case 28:
+    case 30:
+    case 32:
+       unit_bytes = (ximage->bits_per_pixel + 7) / 8;
+       break;
+    default:
+        /* This could be hit on some rare but possible cases. */
+       ASSERT_NOT_REACHED;
+    }
+
+    switch (unit_bytes) {
+    case 1:
+       break;
+    case 2:
+       _swap_ximage_2bytes (ximage);
+       break;
+    case 3:
+       _swap_ximage_3bytes (ximage);
+       break;
+    case 4:
+       _swap_ximage_4bytes (ximage);
+       break;
+    default:
+       ASSERT_NOT_REACHED;
+    }
+}
+
+
+/* Given a mask, (with a single sequence of contiguous 1 bits), return
+ * the number of 1 bits in 'width' and the number of 0 bits to its
+ * right in 'shift'. */
+static void
+_characterize_field (uint32_t mask, int *width, int *shift)
+{
+    *width = _cairo_popcount (mask);
+    /* The final '& 31' is to force a 0 mask to result in 0 shift. */
+    *shift = _cairo_popcount ((mask - 1) & ~mask) & 31;
+}
+
+/* Convert a field of 'width' bits to 'new_width' bits with correct
+ * rounding. */
+static inline uint32_t
+_resize_field (uint32_t field, int width, int new_width)
+{
+    if (width == 0)
+       return 0;
+
+    if (width >= new_width) {
+       return field >> (width - new_width);
+    } else {
+       uint32_t result = field << (new_width - width);
+
+       while (width < new_width) {
+           result |= result >> width;
+           width <<= 1;
+       }
+       return result;
+    }
+}
+
+static inline uint32_t
+_adjust_field (uint32_t field, int adjustment)
+{
+    return MIN (255, MAX(0, (int)field + adjustment));
+}
+
+/* Given a shifted field value, (described by 'width' and 'shift),
+ * resize it 8-bits and return that value.
+ *
+ * Note that the original field value must not have any non-field bits
+ * set.
+ */
+static inline uint32_t
+_field_to_8 (uint32_t field, int width, int shift)
+{
+    return _resize_field (field >> shift, width, 8);
+}
+
+static inline uint32_t
+_field_to_8_undither (uint32_t field, int width, int shift,
+                     int dither_adjustment)
+{
+    return _adjust_field (_field_to_8 (field, width, shift), - dither_adjustment>>width);
+}
+
+/* Given an 8-bit value, convert it to a field of 'width', shift it up
+ *  to 'shift, and return it. */
+static inline uint32_t
+_field_from_8 (uint32_t field, int width, int shift)
+{
+    return _resize_field (field, 8, width) << shift;
+}
+
+static inline uint32_t
+_field_from_8_dither (uint32_t field, int width, int shift,
+                     int8_t dither_adjustment)
+{
+    return _field_from_8 (_adjust_field (field, dither_adjustment>>width), width, shift);
+}
+
+static inline uint32_t
+_pseudocolor_from_rgb888_dither (cairo_xlib_visual_info_t *visual_info,
+                                uint32_t r, uint32_t g, uint32_t b,
+                                int8_t dither_adjustment)
+{
+    if (r == g && g == b) {
+       dither_adjustment /= RAMP_SIZE;
+       return visual_info->gray8_to_pseudocolor[_adjust_field (r, dither_adjustment)];
+    } else {
+       dither_adjustment = visual_info->dither8_to_cube[dither_adjustment+128];
+       return visual_info->cube_to_pseudocolor[visual_info->field8_to_cube[_adjust_field (r, dither_adjustment)]]
+                                              [visual_info->field8_to_cube[_adjust_field (g, dither_adjustment)]]
+                                              [visual_info->field8_to_cube[_adjust_field (b, dither_adjustment)]];
+    }
+}
+
+static inline uint32_t
+_pseudocolor_to_rgb888 (cairo_xlib_visual_info_t *visual_info,
+                       uint32_t pixel)
+{
+    uint32_t r, g, b;
+    pixel &= 0xff;
+    r = visual_info->colors[pixel].r;
+    g = visual_info->colors[pixel].g;
+    b = visual_info->colors[pixel].b;
+    return (r << 16) |
+          (g <<  8) |
+          (b      );
+}
+
+/* should range from -128 to 127 */
+#define X 16
+static const int8_t dither_pattern[4][4] = {
+    {-8*X, +0*X, -6*X, +2*X},
+    {+4*X, -4*X, +6*X, -2*X},
+    {-5*X, +4*X, -7*X, +1*X},
+    {+7*X, -1*X, +5*X, -3*X}
+};
+#undef X
+
+static cairo_surface_t *
+_get_image_surface (cairo_xlib_surface_t    *surface,
+                   const cairo_rectangle_int_t *extents)
+{
+    cairo_int_status_t status;
+    cairo_image_surface_t *image = NULL;
+    XImage *ximage;
+    pixman_format_code_t pixman_format;
+    cairo_format_masks_t xlib_masks;
+    cairo_xlib_display_t *display;
+
+    assert (extents->x >= 0);
+    assert (extents->y >= 0);
+    assert (extents->x + extents->width <= surface->width);
+    assert (extents->y + extents->height <= surface->height);
+
+    if (surface->base.serial == 0) {
+       xlib_masks.bpp = (surface->depth + 7) & ~7;
+       xlib_masks.alpha_mask = surface->a_mask;
+       xlib_masks.red_mask = surface->r_mask;
+       xlib_masks.green_mask = surface->g_mask;
+       xlib_masks.blue_mask = surface->b_mask;
+       _pixman_format_from_masks (&xlib_masks, &pixman_format);
+       return _cairo_image_surface_create_with_pixman_format (NULL,
+                                                              pixman_format,
+                                                              extents->width,
+                                                              extents->height,
+                                                              0);
+    }
+
+    status = _cairo_xlib_display_acquire (surface->base.device, &display);
+    if (status)
+        return _cairo_surface_create_in_error (status);
+
+    /* XXX: This should try to use the XShm extension if available */
+
+    if (surface->use_pixmap == 0) {
+       cairo_xlib_error_func_t old_handler;
+
+       old_handler = XSetErrorHandler (_noop_error_handler);
+
+       ximage = XGetImage (display->display,
+                           surface->drawable,
+                           extents->x, extents->y,
+                           extents->width, extents->height,
+                           AllPlanes, ZPixmap);
+
+       XSetErrorHandler (old_handler);
+
+       /* If we get an error, the surface must have been a window,
+        * so retry with the safe code path.
+        */
+       if (!ximage)
+           surface->use_pixmap = CAIRO_ASSUME_PIXMAP;
+    } else {
+       surface->use_pixmap--;
+       ximage = NULL;
+    }
+
+    if (ximage == NULL) {
+       /* XGetImage from a window is dangerous because it can
+        * produce errors if the window is unmapped or partially
+        * outside the screen. We could check for errors and
+        * retry, but to keep things simple, we just create a
+        * temporary pixmap
+        */
+       Pixmap pixmap;
+       GC gc;
+
+       status = _cairo_xlib_surface_get_gc (display, surface, &gc);
+       if (unlikely (status))
+            goto BAIL;
+
+       pixmap = XCreatePixmap (display->display,
+                               surface->drawable,
+                               extents->width, extents->height,
+                               surface->depth);
+       if (pixmap) {
+           XCopyArea (display->display, surface->drawable, pixmap, gc,
+                      extents->x, extents->y,
+                      extents->width, extents->height,
+                      0, 0);
+
+           ximage = XGetImage (display->display,
+                               pixmap,
+                               0, 0,
+                               extents->width, extents->height,
+                               AllPlanes, ZPixmap);
+
+           XFreePixmap (display->display, pixmap);
+       }
+
+       _cairo_xlib_surface_put_gc (display, surface, gc);
+
+       if (ximage == NULL) {
+           status =  _cairo_error (CAIRO_STATUS_NO_MEMORY);
+            goto BAIL;
+        }
+    }
+
+    _swap_ximage_to_native (ximage);
+
+    xlib_masks.bpp = ximage->bits_per_pixel;
+    xlib_masks.alpha_mask = surface->a_mask;
+    xlib_masks.red_mask = surface->r_mask;
+    xlib_masks.green_mask = surface->g_mask;
+    xlib_masks.blue_mask = surface->b_mask;
+
+    /* We can't use pixman to simply write to image if:
+     *   (a) the pixels are not appropriately aligned,
+     *   (b) pixman does not the pixel format, or
+     *   (c) if the image is palettized and we need to convert.
+     */
+    if (ximage->bitmap_unit == 32 && ximage->bitmap_pad == 32 &&
+       _pixman_format_from_masks (&xlib_masks, &pixman_format) &&
+       (surface->visual == NULL || surface->visual->class == TrueColor))
+    {
+       image = (cairo_image_surface_t*)
+           _cairo_image_surface_create_with_pixman_format ((unsigned char *) ximage->data,
+                                                           pixman_format,
+                                                           ximage->width,
+                                                           ximage->height,
+                                                           ximage->bytes_per_line);
+       status = image->base.status;
+       if (unlikely (status))
+           goto BAIL;
+
+       /* Let the surface take ownership of the data */
+       _cairo_image_surface_assume_ownership_of_data (image);
+       ximage->data = NULL;
+    } else {
+       /* The visual we are dealing with is not supported by the
+        * standard pixman formats. So we must first convert the data
+        * to a supported format. */
+
+       cairo_format_t format;
+       unsigned char *data;
+       uint32_t *row;
+       uint32_t in_pixel, out_pixel;
+       unsigned int rowstride;
+       uint32_t a_mask=0, r_mask=0, g_mask=0, b_mask=0;
+       int a_width=0, r_width=0, g_width=0, b_width=0;
+       int a_shift=0, r_shift=0, g_shift=0, b_shift=0;
+       int x, y, x0, y0, x_off, y_off;
+       cairo_xlib_visual_info_t *visual_info = NULL;
+
+       if (surface->visual == NULL || surface->visual->class == TrueColor) {
+           cairo_bool_t has_alpha;
+           cairo_bool_t has_color;
+
+           has_alpha =  surface->a_mask;
+           has_color = (surface->r_mask ||
+                        surface->g_mask ||
+                        surface->b_mask);
+
+           if (has_color) {
+               if (has_alpha) {
+                   format = CAIRO_FORMAT_ARGB32;
+               } else {
+                   format = CAIRO_FORMAT_RGB24;
+               }
+           } else {
+               /* XXX: Using CAIRO_FORMAT_A8 here would be more
+                * efficient, but would require slightly different code in
+                * the image conversion to put the alpha channel values
+                * into the right place. */
+               format = CAIRO_FORMAT_ARGB32;
+           }
+
+           a_mask = surface->a_mask;
+           r_mask = surface->r_mask;
+           g_mask = surface->g_mask;
+           b_mask = surface->b_mask;
+
+           _characterize_field (a_mask, &a_width, &a_shift);
+           _characterize_field (r_mask, &r_width, &r_shift);
+           _characterize_field (g_mask, &g_width, &g_shift);
+           _characterize_field (b_mask, &b_width, &b_shift);
+
+       } else {
+           format = CAIRO_FORMAT_RGB24;
+
+           status = _cairo_xlib_screen_get_visual_info (display,
+                                                         surface->screen,
+                                                        surface->visual,
+                                                        &visual_info);
+           if (unlikely (status))
+               goto BAIL;
+       }
+
+       image = (cairo_image_surface_t *) cairo_image_surface_create
+           (format, ximage->width, ximage->height);
+       status = image->base.status;
+       if (unlikely (status))
+           goto BAIL;
+
+       data = cairo_image_surface_get_data (&image->base);
+       rowstride = cairo_image_surface_get_stride (&image->base) >> 2;
+       row = (uint32_t *) data;
+       x0 = extents->x + surface->base.device_transform.x0;
+       y0 = extents->y + surface->base.device_transform.y0;
+       for (y = 0, y_off = y0 % ARRAY_LENGTH (dither_pattern);
+            y < ximage->height;
+            y++, y_off = (y_off+1) % ARRAY_LENGTH (dither_pattern)) {
+           const int8_t *dither_row = dither_pattern[y_off];
+           for (x = 0, x_off = x0 % ARRAY_LENGTH (dither_pattern[0]);
+                x < ximage->width;
+                x++, x_off = (x_off+1) % ARRAY_LENGTH (dither_pattern[0])) {
+               int dither_adjustment = dither_row[x_off];
+
+               in_pixel = XGetPixel (ximage, x, y);
+               if (visual_info == NULL) {
+                   out_pixel = (
+                       _field_to_8 (in_pixel & a_mask, a_width, a_shift) << 24 |
+                       _field_to_8_undither (in_pixel & r_mask, r_width, r_shift, dither_adjustment) << 16 |
+                       _field_to_8_undither (in_pixel & g_mask, g_width, g_shift, dither_adjustment) << 8 |
+                       _field_to_8_undither (in_pixel & b_mask, b_width, b_shift, dither_adjustment));
+               } else {
+                   /* Undithering pseudocolor does not look better */
+                   out_pixel = _pseudocolor_to_rgb888 (visual_info, in_pixel);
+               }
+               row[x] = out_pixel;
+           }
+           row += rowstride;
+       }
+       cairo_surface_mark_dirty (&image->base);
+    }
+
+ BAIL:
+    if (ximage)
+        XDestroyImage (ximage);
+
+    cairo_device_release (&display->base);
+
+    if (unlikely (status)) {
+       cairo_surface_destroy (&image->base);
+       return _cairo_surface_create_in_error (status);
+    }
+
+    return &image->base;
+}
+
+void
+_cairo_xlib_surface_set_precision (cairo_xlib_surface_t        *surface,
+                                  cairo_antialias_t     antialias)
+{
+    cairo_xlib_display_t       *display = surface->display;
+    int precision;
+
+    if (display->force_precision != -1)
+           precision = display->force_precision;
+    else switch (antialias) {
+    default:
+    case CAIRO_ANTIALIAS_DEFAULT:
+    case CAIRO_ANTIALIAS_GRAY:
+    case CAIRO_ANTIALIAS_NONE:
+    case CAIRO_ANTIALIAS_FAST:
+    case CAIRO_ANTIALIAS_GOOD:
+       precision = PolyModeImprecise;
+       break;
+    case CAIRO_ANTIALIAS_BEST:
+    case CAIRO_ANTIALIAS_SUBPIXEL:
+       precision = PolyModePrecise;
+       break;
+    }
+
+    if (surface->precision != precision) {
+       XRenderPictureAttributes pa;
+
+       pa.poly_mode = precision;
+       XRenderChangePicture (display->display, surface->picture,
+                             CPPolyMode, &pa);
+
+       surface->precision = precision;
+    }
+}
+
+void
+_cairo_xlib_surface_ensure_picture (cairo_xlib_surface_t    *surface)
+{
+    cairo_xlib_display_t *display = surface->display;
+    XRenderPictureAttributes pa;
+    int mask = 0;
+
+    if (surface->picture)
+       return;
+
+    if (display->force_precision != -1)
+       pa.poly_mode = display->force_precision;
+    else
+       pa.poly_mode = PolyModeImprecise;
+    if (pa.poly_mode)
+           mask |= CPPolyMode;
+
+    surface->precision = pa.poly_mode;
+    surface->picture = XRenderCreatePicture (display->display,
+                                            surface->drawable,
+                                            surface->xrender_format,
+                                            mask, &pa);
+}
+
+cairo_status_t
+_cairo_xlib_surface_draw_image (cairo_xlib_surface_t   *surface,
+                               cairo_image_surface_t  *image,
+                               int                    src_x,
+                               int                    src_y,
+                               int                    width,
+                               int                    height,
+                               int                    dst_x,
+                               int                    dst_y)
+{
+    cairo_xlib_display_t *display;
+    XImage ximage;
+    cairo_format_masks_t image_masks;
+    int native_byte_order = _cairo_is_little_endian () ? LSBFirst : MSBFirst;
+    pixman_image_t *pixman_image = NULL;
+    cairo_status_t status;
+    cairo_bool_t own_data;
+    cairo_bool_t is_rgb_image;
+    GC gc;
+
+    ximage.width = image->width;
+    ximage.height = image->height;
+    ximage.format = ZPixmap;
+    ximage.byte_order = native_byte_order;
+    ximage.bitmap_unit = 32;   /* always for libpixman */
+    ximage.bitmap_bit_order = native_byte_order;
+    ximage.bitmap_pad = 32;    /* always for libpixman */
+    ximage.depth = surface->depth;
+    ximage.red_mask = surface->r_mask;
+    ximage.green_mask = surface->g_mask;
+    ximage.blue_mask = surface->b_mask;
+    ximage.xoffset = 0;
+
+    status = _cairo_xlib_display_acquire (surface->base.device, &display);
+    if (unlikely (status))
+        return status;
+
+    is_rgb_image = _pixman_format_to_masks (image->pixman_format, &image_masks);
+
+    if (is_rgb_image &&
+       (image_masks.alpha_mask == surface->a_mask || surface->a_mask == 0) &&
+       (image_masks.red_mask   == surface->r_mask || surface->r_mask == 0) &&
+       (image_masks.green_mask == surface->g_mask || surface->g_mask == 0) &&
+       (image_masks.blue_mask  == surface->b_mask || surface->b_mask == 0))
+    {
+       int ret;
+
+       ximage.bits_per_pixel = image_masks.bpp;
+       ximage.bytes_per_line = image->stride;
+       ximage.data = (char *)image->data;
+       own_data = FALSE;
+
+       ret = XInitImage (&ximage);
+       assert (ret != 0);
+    }
+    else if (!is_rgb_image ||
+            (surface->visual == NULL || surface->visual->class == TrueColor))
+    {
+        pixman_format_code_t intermediate_format;
+        int ret;
+
+        image_masks.alpha_mask = surface->a_mask;
+        image_masks.red_mask   = surface->r_mask;
+        image_masks.green_mask = surface->g_mask;
+        image_masks.blue_mask  = surface->b_mask;
+        image_masks.bpp        = surface->depth;
+        ret = _pixman_format_from_masks (&image_masks, &intermediate_format);
+        assert (ret);
+
+       own_data = FALSE;
+
+        pixman_image = pixman_image_create_bits (intermediate_format,
+                                                 image->width,
+                                                 image->height,
+                                                 NULL,
+                                                 0);
+        if (pixman_image == NULL) {
+           status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+            goto BAIL;
+        }
+
+        pixman_image_composite32 (PIXMAN_OP_SRC,
+                                  image->pixman_image,
+                                  NULL,
+                                  pixman_image,
+                                  0, 0,
+                                  0, 0,
+                                  0, 0,
+                                  image->width, image->height);
+
+       ximage.bits_per_pixel = image_masks.bpp;
+       ximage.data = (char *) pixman_image_get_data (pixman_image);
+       ximage.bytes_per_line = pixman_image_get_stride (pixman_image);
+
+       ret = XInitImage (&ximage);
+       assert (ret != 0);
+    }
+    else
+    {
+       unsigned int stride, rowstride;
+       int x, y, x0, y0, x_off, y_off;
+       uint32_t in_pixel, out_pixel, *row;
+       int i_a_width=0, i_r_width=0, i_g_width=0, i_b_width=0;
+       int i_a_shift=0, i_r_shift=0, i_g_shift=0, i_b_shift=0;
+       int o_a_width=0, o_r_width=0, o_g_width=0, o_b_width=0;
+       int o_a_shift=0, o_r_shift=0, o_g_shift=0, o_b_shift=0;
+       cairo_xlib_visual_info_t *visual_info = NULL;
+       cairo_bool_t true_color;
+       int ret;
+
+       if (surface->depth > 16)
+           ximage.bits_per_pixel = 32;
+       else if (surface->depth > 8)
+           ximage.bits_per_pixel = 16;
+       else if (surface->depth > 1)
+           ximage.bits_per_pixel = 8;
+       else
+           ximage.bits_per_pixel = 1;
+       stride = CAIRO_STRIDE_FOR_WIDTH_BPP (ximage.width,
+                                            ximage.bits_per_pixel);
+       ximage.bytes_per_line = stride;
+       ximage.data = _cairo_malloc_ab (stride, ximage.height);
+       if (unlikely (ximage.data == NULL)) {
+            own_data = FALSE;
+           status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+            goto BAIL;
+        }
+
+       own_data = TRUE;
+
+       ret = XInitImage (&ximage);
+       assert (ret != 0);
+
+       _characterize_field (image_masks.alpha_mask, &i_a_width, &i_a_shift);
+       _characterize_field (image_masks.red_mask  , &i_r_width, &i_r_shift);
+       _characterize_field (image_masks.green_mask, &i_g_width, &i_g_shift);
+       _characterize_field (image_masks.blue_mask , &i_b_width, &i_b_shift);
+
+       true_color = surface->visual == NULL ||
+                    surface->visual->class == TrueColor;
+       if (true_color) {
+           _characterize_field (surface->a_mask, &o_a_width, &o_a_shift);
+           _characterize_field (surface->r_mask, &o_r_width, &o_r_shift);
+           _characterize_field (surface->g_mask, &o_g_width, &o_g_shift);
+           _characterize_field (surface->b_mask, &o_b_width, &o_b_shift);
+       } else {
+           status = _cairo_xlib_screen_get_visual_info (display,
+                                                         surface->screen,
+                                                        surface->visual,
+                                                        &visual_info);
+           if (unlikely (status))
+               goto BAIL;
+       }
+
+       rowstride = image->stride >> 2;
+       row = (uint32_t *) image->data;
+       x0 = dst_x + surface->base.device_transform.x0;
+       y0 = dst_y + surface->base.device_transform.y0;
+       for (y = 0, y_off = y0 % ARRAY_LENGTH (dither_pattern);
+            y < ximage.height;
+            y++, y_off = (y_off+1) % ARRAY_LENGTH (dither_pattern))
+       {
+           const int8_t *dither_row = dither_pattern[y_off];
+
+           for (x = 0, x_off = x0 % ARRAY_LENGTH (dither_pattern[0]);
+                x < ximage.width;
+                x++, x_off = (x_off+1) % ARRAY_LENGTH (dither_pattern[0]))
+           {
+               int dither_adjustment = dither_row[x_off];
+               int a, r, g, b;
+
+               if (image_masks.bpp == 1)
+                   in_pixel = !! (((uint8_t*)row)[x/8] & (1 << (x & 7)));
+               else if (image_masks.bpp <= 8)
+                   in_pixel = ((uint8_t*)row)[x];
+               else if (image_masks.bpp <= 16)
+                   in_pixel = ((uint16_t*)row)[x];
+               else if (image_masks.bpp <= 24)
+#ifdef WORDS_BIGENDIAN
+                   in_pixel = ((uint8_t*)row)[3 * x]     << 16 |
+                              ((uint8_t*)row)[3 * x + 1] << 8  |
+                              ((uint8_t*)row)[3 * x + 2];
+#else
+                   in_pixel = ((uint8_t*)row)[3 * x]           |
+                              ((uint8_t*)row)[3 * x + 1] << 8  |
+                              ((uint8_t*)row)[3 * x + 2] << 16;
+#endif
+               else
+                   in_pixel = row[x];
+
+               /* If the incoming image has no alpha channel, then the input
+                * is opaque and the output should have the maximum alpha value.
+                * For all other channels, their absence implies 0.
+                */
+               if (image_masks.alpha_mask == 0x0)
+                   a = 0xff;
+               else
+                   a = _field_to_8 (in_pixel & image_masks.alpha_mask, i_a_width, i_a_shift);
+               r = _field_to_8 (in_pixel & image_masks.red_mask  , i_r_width, i_r_shift);
+               g = _field_to_8 (in_pixel & image_masks.green_mask, i_g_width, i_g_shift);
+               b = _field_to_8 (in_pixel & image_masks.blue_mask , i_b_width, i_b_shift);
+
+               if (true_color) {
+                   out_pixel = _field_from_8        (a, o_a_width, o_a_shift) |
+                               _field_from_8_dither (r, o_r_width, o_r_shift, dither_adjustment) |
+                               _field_from_8_dither (g, o_g_width, o_g_shift, dither_adjustment) |
+                               _field_from_8_dither (b, o_b_width, o_b_shift, dither_adjustment);
+               } else {
+                   out_pixel = _pseudocolor_from_rgb888_dither (visual_info, r, g, b, dither_adjustment);
+               }
+
+               XPutPixel (&ximage, x, y, out_pixel);
+           }
+
+           row += rowstride;
+       }
+    }
+
+    status = _cairo_xlib_surface_get_gc (display, surface, &gc);
+    if (unlikely (status))
+       goto BAIL;
+
+    XPutImage (display->display, surface->drawable, gc,
+              &ximage, src_x, src_y, dst_x, dst_y,
+              width, height);
+
+    _cairo_xlib_surface_put_gc (display, surface, gc);
+
+  BAIL:
+
+    cairo_device_release (&display->base);
+
+    if (own_data)
+       free (ximage.data);
+    if (pixman_image)
+        pixman_image_unref (pixman_image);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_xlib_surface_acquire_source_image (void                    *abstract_surface,
+                                         cairo_image_surface_t  **image_out,
+                                         void                   **image_extra)
+{
+    cairo_xlib_surface_t *surface = abstract_surface;
+    cairo_rectangle_int_t extents;
+
+    extents.x = extents.y = 0;
+    extents.width = surface->width;
+    extents.height = surface->height;
+
+    *image_extra = NULL;
+    *image_out = (cairo_image_surface_t*)_get_image_surface (surface, &extents);
+    return (*image_out)->base.status;
+}
+
+static cairo_surface_t *
+_cairo_xlib_surface_snapshot (void *abstract_surface)
+{
+    cairo_xlib_surface_t *surface = abstract_surface;
+    cairo_rectangle_int_t extents;
+
+    extents.x = extents.y = 0;
+    extents.width = surface->width;
+    extents.height = surface->height;
+
+    /* XXX notice the duplication with acquire source */
+    return _get_image_surface (surface, &extents);
+}
+
+static void
+_cairo_xlib_surface_release_source_image (void                   *abstract_surface,
+                                         cairo_image_surface_t  *image,
+                                         void                   *image_extra)
+{
+    cairo_surface_destroy (&image->base);
+}
+
+static cairo_surface_t *
+_cairo_xlib_surface_map_to_image (void                    *abstract_surface,
+                                 const cairo_rectangle_int_t   *extents)
+{
+    cairo_surface_t *image;
+
+    image = _get_image_surface (abstract_surface, extents);
+    cairo_surface_set_device_offset (image, -extents->y, -extents->y);
+
+    return image;
+}
+
+static cairo_int_status_t
+_cairo_xlib_surface_unmap_image (void *abstract_surface,
+                                cairo_image_surface_t *image)
+{
+    return _cairo_xlib_surface_draw_image (abstract_surface, image,
+                                          0, 0,
+                                          image->width, image->height,
+                                          image->base.device_transform_inverse.x0,
+                                          image->base.device_transform_inverse.y0);
+}
+
+static cairo_bool_t
+_cairo_xlib_surface_get_extents (void                   *abstract_surface,
+                                cairo_rectangle_int_t   *rectangle)
+{
+    cairo_xlib_surface_t *surface = abstract_surface;
+
+    rectangle->x = 0;
+    rectangle->y = 0;
+
+    rectangle->width  = surface->width;
+    rectangle->height = surface->height;
+
+    return TRUE;
+}
+
+static void
+_cairo_xlib_surface_get_font_options (void                  *abstract_surface,
+                                     cairo_font_options_t  *options)
+{
+    cairo_xlib_surface_t *surface = abstract_surface;
+
+    *options = *_cairo_xlib_screen_get_font_options (surface->screen);
+}
+
+
+static cairo_int_status_t
+_cairo_xlib_surface_paint (void                                *_surface,
+                          cairo_operator_t              op,
+                          const cairo_pattern_t        *source,
+                          const cairo_clip_t           *clip)
+{
+    cairo_xlib_surface_t *surface = _surface;
+    return _cairo_compositor_paint (surface->compositor,
+                                   &surface->base, op, source,
+                                   clip);
+}
+
+static cairo_int_status_t
+_cairo_xlib_surface_mask (void                 *_surface,
+                         cairo_operator_t       op,
+                         const cairo_pattern_t *source,
+                         const cairo_pattern_t *mask,
+                         const cairo_clip_t    *clip)
+{
+    cairo_xlib_surface_t *surface = _surface;
+    return _cairo_compositor_mask (surface->compositor,
+                                  &surface->base, op, source, mask,
+                                  clip);
+}
+
+static cairo_int_status_t
+_cairo_xlib_surface_stroke (void                       *_surface,
+                           cairo_operator_t             op,
+                           const cairo_pattern_t       *source,
+                           const cairo_path_fixed_t    *path,
+                           const cairo_stroke_style_t  *style,
+                           const cairo_matrix_t        *ctm,
+                           const cairo_matrix_t        *ctm_inverse,
+                           double                       tolerance,
+                           cairo_antialias_t            antialias,
+                           const cairo_clip_t          *clip)
+{
+    cairo_xlib_surface_t *surface = _surface;
+    return _cairo_compositor_stroke (surface->compositor,
+                                    &surface->base, op, source,
+                                    path, style, ctm, ctm_inverse,
+                                    tolerance, antialias,
+                                    clip);
+}
+
+static cairo_int_status_t
+_cairo_xlib_surface_fill (void                         *_surface,
+                         cairo_operator_t               op,
+                         const cairo_pattern_t         *source,
+                         const cairo_path_fixed_t      *path,
+                         cairo_fill_rule_t              fill_rule,
+                         double                         tolerance,
+                         cairo_antialias_t              antialias,
+                         const cairo_clip_t            *clip)
+{
+    cairo_xlib_surface_t *surface = _surface;
+    return _cairo_compositor_fill (surface->compositor,
+                                  &surface->base, op, source,
+                                  path, fill_rule, tolerance, antialias,
+                                  clip);
+}
+
+static cairo_int_status_t
+_cairo_xlib_surface_glyphs (void                       *_surface,
+                           cairo_operator_t             op,
+                           const cairo_pattern_t       *source,
+                           cairo_glyph_t               *glyphs,
+                           int                          num_glyphs,
+                           cairo_scaled_font_t         *scaled_font,
+                           const cairo_clip_t          *clip)
+{
+    cairo_xlib_surface_t *surface = _surface;
+    return _cairo_compositor_glyphs (surface->compositor,
+                                    &surface->base, op, source,
+                                    glyphs, num_glyphs, scaled_font,
+                                    clip);
+}
+
+static const cairo_surface_backend_t cairo_xlib_surface_backend = {
+    CAIRO_SURFACE_TYPE_XLIB,
+    _cairo_xlib_surface_finish,
+
+    _cairo_default_context_create,
+
+    _cairo_xlib_surface_create_similar,
+    NULL, //_cairo_xlib_surface_create_similar_image, /* XXX shm */
+    _cairo_xlib_surface_map_to_image,
+    _cairo_xlib_surface_unmap_image,
+
+    _cairo_xlib_surface_acquire_source_image,
+    _cairo_xlib_surface_release_source_image,
+    _cairo_xlib_surface_snapshot,
+
+    NULL, /* copy_page */
+    NULL, /* show_page */
+
+    _cairo_xlib_surface_get_extents,
+    _cairo_xlib_surface_get_font_options,
+
+    NULL, /* flush */
+    NULL, /* mark_dirty_rectangle */
+
+    _cairo_xlib_surface_paint,
+    _cairo_xlib_surface_mask,
+    _cairo_xlib_surface_stroke,
+    _cairo_xlib_surface_fill,
+    NULL, /* fill-stroke */
+    _cairo_xlib_surface_glyphs,
+};
+
+/**
+ * _cairo_surface_is_xlib:
+ * @surface: a #cairo_surface_t
+ *
+ * Checks if a surface is a #cairo_xlib_surface_t
+ *
+ * Return value: True if the surface is an xlib surface
+ **/
+static cairo_bool_t
+_cairo_surface_is_xlib (cairo_surface_t *surface)
+{
+    return surface->backend == &cairo_xlib_surface_backend;
+}
+
+static cairo_surface_t *
+_cairo_xlib_surface_create_internal (cairo_xlib_screen_t       *screen,
+                                    Drawable                    drawable,
+                                    Visual                     *visual,
+                                    XRenderPictFormat          *xrender_format,
+                                    int                         width,
+                                    int                         height,
+                                    int                         depth)
+{
+    cairo_xlib_surface_t *surface;
+    cairo_xlib_display_t *display;
+    cairo_status_t status;
+
+    if (depth == 0) {
+       if (xrender_format) {
+           depth = xrender_format->depth;
+
+           /* XXX find matching visual for core/dithering fallbacks? */
+       } else if (visual) {
+           Screen *scr = screen->screen;
+
+           if (visual == DefaultVisualOfScreen (scr)) {
+               depth = DefaultDepthOfScreen (scr);
+           } else  {
+               int j, k;
+
+               /* This is ugly, but we have to walk over all visuals
+                * for the display to find the correct depth.
+                */
+               depth = 0;
+               for (j = 0; j < scr->ndepths; j++) {
+                   Depth *d = &scr->depths[j];
+                   for (k = 0; k < d->nvisuals; k++) {
+                       if (&d->visuals[k] == visual) {
+                           depth = d->depth;
+                           goto found;
+                       }
+                   }
+               }
+           }
+       }
+
+       if (depth == 0)
+           return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_VISUAL));
+
+found:
+       ;
+    }
+
+    surface = malloc (sizeof (cairo_xlib_surface_t));
+    if (unlikely (surface == NULL))
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+    status = _cairo_xlib_display_acquire (screen->device, &display);
+    if (unlikely (status)) {
+        free (surface);
+        return _cairo_surface_create_in_error (_cairo_error (status));
+    }
+
+    surface->display = display;
+    if (CAIRO_RENDER_HAS_CREATE_PICTURE (display)) {
+       if (!xrender_format) {
+           if (visual) {
+               xrender_format = XRenderFindVisualFormat (display->display, visual);
+           } else if (depth == 1) {
+               xrender_format =
+                   _cairo_xlib_display_get_xrender_format (display,
+                                                           CAIRO_FORMAT_A1);
+           }
+       }
+    }
+
+    cairo_device_release (&display->base);
+
+    _cairo_surface_init (&surface->base,
+                        &cairo_xlib_surface_backend,
+                        screen->device,
+                        _xrender_format_to_content (xrender_format));
+
+    surface->screen = screen;
+    surface->compositor = display->compositor;
+
+    surface->drawable = drawable;
+    surface->owns_pixmap = FALSE;
+    surface->use_pixmap = 0;
+    surface->width = width;
+    surface->height = height;
+
+    surface->picture = None;
+    surface->precision = PolyModePrecise;
+
+    surface->embedded_source.picture = None;
+
+    surface->visual = visual;
+    surface->xrender_format = xrender_format;
+    surface->depth = depth;
+
+    /*
+     * Compute the pixel format masks from either a XrenderFormat or
+     * else from a visual; failing that we assume the drawable is an
+     * alpha-only pixmap as it could only have been created that way
+     * through the cairo_xlib_surface_create_for_bitmap function.
+     */
+    if (xrender_format) {
+       surface->a_mask = (unsigned long)
+           surface->xrender_format->direct.alphaMask
+           << surface->xrender_format->direct.alpha;
+       surface->r_mask = (unsigned long)
+           surface->xrender_format->direct.redMask
+           << surface->xrender_format->direct.red;
+       surface->g_mask = (unsigned long)
+           surface->xrender_format->direct.greenMask
+           << surface->xrender_format->direct.green;
+       surface->b_mask = (unsigned long)
+           surface->xrender_format->direct.blueMask
+           << surface->xrender_format->direct.blue;
+    } else if (visual) {
+       surface->a_mask = 0;
+       surface->r_mask = visual->red_mask;
+       surface->g_mask = visual->green_mask;
+       surface->b_mask = visual->blue_mask;
+    } else {
+       if (depth < 32)
+           surface->a_mask = (1 << depth) - 1;
+       else
+           surface->a_mask = 0xffffffff;
+       surface->r_mask = 0;
+       surface->g_mask = 0;
+       surface->b_mask = 0;
+    }
+
+    cairo_list_add (&surface->link, &screen->surfaces);
+
+    return &surface->base;
+}
+
+static Screen *
+_cairo_xlib_screen_from_visual (Display *dpy, Visual *visual)
+{
+    int s, d, v;
+
+    for (s = 0; s < ScreenCount (dpy); s++) {
+       Screen *screen;
+
+       screen = ScreenOfDisplay (dpy, s);
+       if (visual == DefaultVisualOfScreen (screen))
+           return screen;
+
+       for (d = 0; d < screen->ndepths; d++) {
+           Depth  *depth;
+
+           depth = &screen->depths[d];
+           for (v = 0; v < depth->nvisuals; v++)
+               if (visual == &depth->visuals[v])
+                   return screen;
+       }
+    }
+
+    return NULL;
+}
+
+static cairo_bool_t valid_size (int width, int height)
+{
+    return width > 0 && width <= XLIB_COORD_MAX && height > 0 && height <= XLIB_COORD_MAX;
+}
+
+/**
+ * cairo_xlib_surface_create:
+ * @dpy: an X Display
+ * @drawable: an X Drawable, (a Pixmap or a Window)
+ * @visual: the visual to use for drawing to @drawable. The depth
+ *          of the visual must match the depth of the drawable.
+ *          Currently, only TrueColor visuals are fully supported.
+ * @width: the current width of @drawable.
+ * @height: the current height of @drawable.
+ *
+ * Creates an Xlib surface that draws to the given drawable.
+ * The way that colors are represented in the drawable is specified
+ * by the provided visual.
+ *
+ * Note: If @drawable is a Window, then the function
+ * cairo_xlib_surface_set_size() must be called whenever the size of the
+ * window changes.
+ *
+ * When @drawable is a Window containing child windows then drawing to
+ * the created surface will be clipped by those child windows.  When
+ * the created surface is used as a source, the contents of the
+ * children will be included.
+ *
+ * Return value: the newly created surface
+ **/
+cairo_surface_t *
+cairo_xlib_surface_create (Display     *dpy,
+                          Drawable     drawable,
+                          Visual      *visual,
+                          int          width,
+                          int          height)
+{
+    Screen *scr;
+    cairo_xlib_screen_t *screen;
+    cairo_status_t status;
+
+    if (! valid_size (width, height)) {
+       /* you're lying, and you know it! */
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE));
+    }
+
+    scr = _cairo_xlib_screen_from_visual (dpy, visual);
+    if (scr == NULL)
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_VISUAL));
+
+    status = _cairo_xlib_screen_get (dpy, scr, &screen);
+    if (unlikely (status))
+       return _cairo_surface_create_in_error (status);
+
+    X_DEBUG ((dpy, "create (drawable=%x)", (unsigned int) drawable));
+
+    return _cairo_xlib_surface_create_internal (screen, drawable,
+                                                visual, NULL,
+                                                width, height, 0);
+}
+
+/**
+ * cairo_xlib_surface_create_for_bitmap:
+ * @dpy: an X Display
+ * @bitmap: an X Drawable, (a depth-1 Pixmap)
+ * @screen: the X Screen associated with @bitmap
+ * @width: the current width of @bitmap.
+ * @height: the current height of @bitmap.
+ *
+ * Creates an Xlib surface that draws to the given bitmap.
+ * This will be drawn to as a %CAIRO_FORMAT_A1 object.
+ *
+ * Return value: the newly created surface
+ **/
+cairo_surface_t *
+cairo_xlib_surface_create_for_bitmap (Display  *dpy,
+                                     Pixmap    bitmap,
+                                     Screen   *scr,
+                                     int       width,
+                                     int       height)
+{
+    cairo_xlib_screen_t *screen;
+    cairo_status_t status;
+
+    if (! valid_size (width, height))
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE));
+
+    status = _cairo_xlib_screen_get (dpy, scr, &screen);
+    if (unlikely (status))
+       return _cairo_surface_create_in_error (status);
+
+    X_DEBUG ((dpy, "create_for_bitmap (drawable=%x)", (unsigned int) bitmap));
+
+    return _cairo_xlib_surface_create_internal (screen, bitmap,
+                                                NULL, NULL,
+                                                width, height, 1);
+}
+
+#if CAIRO_HAS_XLIB_XRENDER_SURFACE
+/**
+ * cairo_xlib_surface_create_with_xrender_format:
+ * @dpy: an X Display
+ * @drawable: an X Drawable, (a Pixmap or a Window)
+ * @screen: the X Screen associated with @drawable
+ * @format: the picture format to use for drawing to @drawable. The depth
+ *          of @format must match the depth of the drawable.
+ * @width: the current width of @drawable.
+ * @height: the current height of @drawable.
+ *
+ * Creates an Xlib surface that draws to the given drawable.
+ * The way that colors are represented in the drawable is specified
+ * by the provided picture format.
+ *
+ * Note: If @drawable is a Window, then the function
+ * cairo_xlib_surface_set_size() must be called whenever the size of the
+ * window changes.
+ *
+ * Return value: the newly created surface
+ **/
+cairo_surface_t *
+cairo_xlib_surface_create_with_xrender_format (Display             *dpy,
+                                              Drawable             drawable,
+                                              Screen               *scr,
+                                              XRenderPictFormat    *format,
+                                              int                  width,
+                                              int                  height)
+{
+    cairo_xlib_screen_t *screen;
+    cairo_status_t status;
+
+    if (! valid_size (width, height))
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE));
+
+    status = _cairo_xlib_screen_get (dpy, scr, &screen);
+    if (unlikely (status))
+       return _cairo_surface_create_in_error (status);
+
+    X_DEBUG ((dpy, "create_with_xrender_format (drawable=%x)", (unsigned int) drawable));
+
+    return _cairo_xlib_surface_create_internal (screen, drawable,
+                                               _visual_for_xrender_format (scr, format),
+                                                format, width, height, 0);
+}
+
+/**
+ * cairo_xlib_surface_get_xrender_format:
+ * @surface: an xlib surface
+ *
+ * Gets the X Render picture format that @surface uses for rendering with the
+ * X Render extension. If the surface was created by
+ * cairo_xlib_surface_create_with_xrender_format() originally, the return
+ * value is the format passed to that constructor.
+ *
+ * Return value: the XRenderPictFormat* associated with @surface,
+ * or %NULL if the surface is not an xlib surface
+ * or if the X Render extension is not available.
+ *
+ * Since: 1.6
+ **/
+XRenderPictFormat *
+cairo_xlib_surface_get_xrender_format (cairo_surface_t *surface)
+{
+    cairo_xlib_surface_t *xlib_surface = (cairo_xlib_surface_t *) surface;
+
+    /* Throw an error for a non-xlib surface */
+    if (! _cairo_surface_is_xlib (surface)) {
+       _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+       return NULL;
+    }
+
+    return xlib_surface->xrender_format;
+}
+#endif
+
+/**
+ * cairo_xlib_surface_set_size:
+ * @surface: a #cairo_surface_t for the XLib backend
+ * @width: the new width of the surface
+ * @height: the new height of the surface
+ *
+ * Informs cairo of the new size of the X Drawable underlying the
+ * surface. For a surface created for a Window (rather than a Pixmap),
+ * this function must be called each time the size of the window
+ * changes. (For a subwindow, you are normally resizing the window
+ * yourself, but for a toplevel window, it is necessary to listen for
+ * ConfigureNotify events.)
+ *
+ * A Pixmap can never change size, so it is never necessary to call
+ * this function on a surface created for a Pixmap.
+ **/
+void
+cairo_xlib_surface_set_size (cairo_surface_t *abstract_surface,
+                            int              width,
+                            int              height)
+{
+    cairo_xlib_surface_t *surface = (cairo_xlib_surface_t *) abstract_surface;
+
+    if (unlikely (abstract_surface->status))
+       return;
+    if (unlikely (abstract_surface->finished)) {
+       _cairo_surface_set_error (abstract_surface,
+                                 _cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
+       return;
+    }
+
+    if (! _cairo_surface_is_xlib (abstract_surface)) {
+       _cairo_surface_set_error (abstract_surface,
+                                 _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH));
+       return;
+    }
+
+    if (! valid_size (width, height)) {
+       _cairo_surface_set_error (abstract_surface,
+                                 _cairo_error (CAIRO_STATUS_INVALID_SIZE));
+       return;
+    }
+
+    surface->width = width;
+    surface->height = height;
+}
+/**
+ * cairo_xlib_surface_set_drawable:
+ * @surface: a #cairo_surface_t for the XLib backend
+ * @drawable: the new drawable for the surface
+ * @width: the width of the new drawable
+ * @height: the height of the new drawable
+ *
+ * Informs cairo of a new X Drawable underlying the
+ * surface. The drawable must match the display, screen
+ * and format of the existing drawable or the application
+ * will get X protocol errors and will probably terminate.
+ * No checks are done by this function to ensure this
+ * compatibility.
+ **/
+void
+cairo_xlib_surface_set_drawable (cairo_surface_t   *abstract_surface,
+                                Drawable           drawable,
+                                int                width,
+                                int                height)
+{
+    cairo_xlib_surface_t *surface = (cairo_xlib_surface_t *)abstract_surface;
+    cairo_status_t status;
+
+    if (unlikely (abstract_surface->status))
+       return;
+    if (unlikely (abstract_surface->finished)) {
+       status = _cairo_surface_set_error (abstract_surface,
+                                          _cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
+       return;
+    }
+
+    if (! _cairo_surface_is_xlib (abstract_surface)) {
+       status = _cairo_surface_set_error (abstract_surface,
+                                          _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH));
+       return;
+    }
+
+    if (! valid_size (width, height)) {
+       status = _cairo_surface_set_error (abstract_surface,
+                                          _cairo_error (CAIRO_STATUS_INVALID_SIZE));
+       return;
+    }
+
+    /* XXX: and what about this case? */
+    if (surface->owns_pixmap)
+       return;
+
+    if (surface->drawable != drawable) {
+        cairo_xlib_display_t *display;
+
+        status = _cairo_xlib_display_acquire (surface->base.device, &display);
+        if (unlikely (status))
+            return;
+
+       X_DEBUG ((display->display, "set_drawable (drawable=%x)", (unsigned int) drawable));
+
+       if (surface->picture != None) {
+           XRenderFreePicture (display->display, surface->picture);
+           if (unlikely (status)) {
+               status = _cairo_surface_set_error (&surface->base, status);
+               return;
+           }
+
+           surface->picture = None;
+       }
+
+        cairo_device_release (&display->base);
+
+       surface->drawable = drawable;
+    }
+    surface->width = width;
+    surface->height = height;
+}
+
+/**
+ * cairo_xlib_surface_get_display:
+ * @surface: a #cairo_xlib_surface_t
+ *
+ * Get the X Display for the underlying X Drawable.
+ *
+ * Return value: the display.
+ *
+ * Since: 1.2
+ **/
+Display *
+cairo_xlib_surface_get_display (cairo_surface_t *abstract_surface)
+{
+    if (! _cairo_surface_is_xlib (abstract_surface)) {
+       _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+       return NULL;
+    }
+
+    return ((cairo_xlib_display_t *) abstract_surface->device)->display;
+}
+
+/**
+ * cairo_xlib_surface_get_drawable:
+ * @surface: a #cairo_xlib_surface_t
+ *
+ * Get the underlying X Drawable used for the surface.
+ *
+ * Return value: the drawable.
+ *
+ * Since: 1.2
+ **/
+Drawable
+cairo_xlib_surface_get_drawable (cairo_surface_t *abstract_surface)
+{
+    cairo_xlib_surface_t *surface = (cairo_xlib_surface_t *) abstract_surface;
+
+    if (! _cairo_surface_is_xlib (abstract_surface)) {
+       _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+       return 0;
+    }
+
+    return surface->drawable;
+}
+
+/**
+ * cairo_xlib_surface_get_screen:
+ * @surface: a #cairo_xlib_surface_t
+ *
+ * Get the X Screen for the underlying X Drawable.
+ *
+ * Return value: the screen.
+ *
+ * Since: 1.2
+ **/
+Screen *
+cairo_xlib_surface_get_screen (cairo_surface_t *abstract_surface)
+{
+    cairo_xlib_surface_t *surface = (cairo_xlib_surface_t *) abstract_surface;
+
+    if (! _cairo_surface_is_xlib (abstract_surface)) {
+       _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+       return NULL;
+    }
+
+    return surface->screen->screen;
+}
+
+/**
+ * cairo_xlib_surface_get_visual:
+ * @surface: a #cairo_xlib_surface_t
+ *
+ * Gets the X Visual associated with @surface, suitable for use with the
+ * underlying X Drawable.  If @surface was created by
+ * cairo_xlib_surface_create(), the return value is the Visual passed to that
+ * constructor.
+ *
+ * Return value: the Visual or %NULL if there is no appropriate Visual for
+ * @surface.
+ *
+ * Since: 1.2
+ **/
+Visual *
+cairo_xlib_surface_get_visual (cairo_surface_t *surface)
+{
+    cairo_xlib_surface_t *xlib_surface = (cairo_xlib_surface_t *) surface;
+
+    if (! _cairo_surface_is_xlib (surface)) {
+       _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+       return NULL;
+    }
+
+    return xlib_surface->visual;
+}
+
+/**
+ * cairo_xlib_surface_get_depth:
+ * @surface: a #cairo_xlib_surface_t
+ *
+ * Get the number of bits used to represent each pixel value.
+ *
+ * Return value: the depth of the surface in bits.
+ *
+ * Since: 1.2
+ **/
+int
+cairo_xlib_surface_get_depth (cairo_surface_t *abstract_surface)
+{
+    cairo_xlib_surface_t *surface = (cairo_xlib_surface_t *) abstract_surface;
+
+    if (! _cairo_surface_is_xlib (abstract_surface)) {
+       _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+       return 0;
+    }
+
+    return surface->depth;
+}
+
+/**
+ * cairo_xlib_surface_get_width:
+ * @surface: a #cairo_xlib_surface_t
+ *
+ * Get the width of the X Drawable underlying the surface in pixels.
+ *
+ * Return value: the width of the surface in pixels.
+ *
+ * Since: 1.2
+ **/
+int
+cairo_xlib_surface_get_width (cairo_surface_t *abstract_surface)
+{
+    cairo_xlib_surface_t *surface = (cairo_xlib_surface_t *) abstract_surface;
+
+    if (! _cairo_surface_is_xlib (abstract_surface)) {
+       _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+       return 0;
+    }
+
+    return surface->width;
+}
+
+/**
+ * cairo_xlib_surface_get_height:
+ * @surface: a #cairo_xlib_surface_t
+ *
+ * Get the height of the X Drawable underlying the surface in pixels.
+ *
+ * Return value: the height of the surface in pixels.
+ *
+ * Since: 1.2
+ **/
+int
+cairo_xlib_surface_get_height (cairo_surface_t *abstract_surface)
+{
+    cairo_xlib_surface_t *surface = (cairo_xlib_surface_t *) abstract_surface;
+
+    if (! _cairo_surface_is_xlib (abstract_surface)) {
+       _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+       return 0;
+    }
+
+    return surface->height;
+}
+
+#endif /* !CAIRO_HAS_XLIB_XCB_FUNCTIONS */
diff --git a/src/cairo-xlib-visual.c b/src/cairo-xlib-visual.c
new file mode 100644 (file)
index 0000000..d9aac44
--- /dev/null
@@ -0,0 +1,191 @@
+/* Cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2008 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ *     Carl D. Worth <cworth@cworth.org>
+ */
+
+#include "cairoint.h"
+
+#if !CAIRO_HAS_XLIB_XCB_FUNCTIONS
+
+#include "cairo-xlib-private.h"
+
+#include "cairo-error-private.h"
+
+/* A perceptual distance metric between two colors. No sqrt needed
+ * since the square of the distance is still a valid metric. */
+
+/* XXX: This is currently using linear distance in RGB space which is
+ * decidedly not perceptually linear. If someone cared a lot about the
+ * quality, they might choose something else here. Then again, they
+ * might also choose not to use a PseudoColor visual... */
+static inline int
+_color_distance (unsigned short r1, unsigned short g1, unsigned short b1,
+                unsigned short r2, unsigned short g2, unsigned short b2)
+{
+    r1 >>= 8; g1 >>= 8; b1 >>= 8;
+    r2 >>= 8; g2 >>= 8; b2 >>= 8;
+
+    return ((r2 - r1) * (r2 - r1) +
+           (g2 - g1) * (g2 - g1) +
+           (b2 - b1) * (b2 - b1));
+}
+
+cairo_status_t
+_cairo_xlib_visual_info_create (Display *dpy,
+                               int screen,
+                               VisualID visualid,
+                               cairo_xlib_visual_info_t **out)
+{
+    cairo_xlib_visual_info_t *info;
+    Colormap colormap = DefaultColormap (dpy, screen);
+    XColor color;
+    int gray, red, green, blue;
+    int i, j, distance, min_distance = 0;
+    XColor colors[256];
+    unsigned short cube_index_to_short[CUBE_SIZE];
+    unsigned short ramp_index_to_short[RAMP_SIZE];
+    unsigned char  gray_to_pseudocolor[RAMP_SIZE];
+
+    for (i = 0; i < CUBE_SIZE; i++)
+       cube_index_to_short[i] = (0xffff * i + ((CUBE_SIZE-1)>>1)) / (CUBE_SIZE-1);
+    for (i = 0; i < RAMP_SIZE; i++)
+       ramp_index_to_short[i] = (0xffff * i + ((RAMP_SIZE-1)>>1)) / (RAMP_SIZE-1);
+
+    info = malloc (sizeof (cairo_xlib_visual_info_t));
+    if (unlikely (info == NULL))
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    info->visualid = visualid;
+
+    /* Allocate a gray ramp and a color cube.
+     * Give up as soon as failures start. */
+
+    for (gray = 0; gray < RAMP_SIZE; gray++) {
+       color.red = color.green = color.blue = ramp_index_to_short[gray];
+       if (! XAllocColor (dpy, colormap, &color))
+           goto DONE_ALLOCATE;
+    }
+
+    /* XXX: Could do this in a more clever order to have the best
+     * possible results from early failure. Could also choose a cube
+     * uniformly distributed in a better space than RGB. */
+    for (red = 0; red < CUBE_SIZE; red++) {
+       for (green = 0; green < CUBE_SIZE; green++) {
+           for (blue = 0; blue < CUBE_SIZE; blue++) {
+               color.red = cube_index_to_short[red];
+               color.green = cube_index_to_short[green];
+               color.blue = cube_index_to_short[blue];
+               color.pixel = 0;
+               color.flags = 0;
+               color.pad = 0;
+               if (! XAllocColor (dpy, colormap, &color))
+                   goto DONE_ALLOCATE;
+           }
+       }
+    }
+  DONE_ALLOCATE:
+
+    for (i = 0; i < ARRAY_LENGTH (colors); i++)
+       colors[i].pixel = i;
+    XQueryColors (dpy, colormap, colors, ARRAY_LENGTH (colors));
+
+    /* Search for nearest colors within allocated colormap. */
+    for (gray = 0; gray < RAMP_SIZE; gray++) {
+       for (i = 0; i < 256; i++) {
+           distance = _color_distance (ramp_index_to_short[gray],
+                                       ramp_index_to_short[gray],
+                                       ramp_index_to_short[gray],
+                                       colors[i].red,
+                                       colors[i].green,
+                                       colors[i].blue);
+           if (i == 0 || distance < min_distance) {
+               gray_to_pseudocolor[gray] = colors[i].pixel;
+               min_distance = distance;
+               if (!min_distance)
+                   break;
+           }
+       }
+    }
+    for (red = 0; red < CUBE_SIZE; red++) {
+       for (green = 0; green < CUBE_SIZE; green++) {
+           for (blue = 0; blue < CUBE_SIZE; blue++) {
+               for (i = 0; i < 256; i++) {
+                   distance = _color_distance (cube_index_to_short[red],
+                                               cube_index_to_short[green],
+                                               cube_index_to_short[blue],
+                                               colors[i].red,
+                                               colors[i].green,
+                                               colors[i].blue);
+                   if (i == 0 || distance < min_distance) {
+                       info->cube_to_pseudocolor[red][green][blue] = colors[i].pixel;
+                       min_distance = distance;
+                       if (!min_distance)
+                           break;
+                   }
+               }
+           }
+       }
+    }
+
+    for (i = 0, j = 0; i < 256; i++) {
+       if (j < CUBE_SIZE - 1 && (((i<<8)+i) - (int)cube_index_to_short[j]) > ((int)cube_index_to_short[j+1] - ((i<<8)+i)))
+           j++;
+       info->field8_to_cube[i] = j;
+
+       info->dither8_to_cube[i] = ((int)i - 128) / (CUBE_SIZE - 1);
+    }
+    for (i = 0, j = 0; i < 256; i++) {
+       if (j < RAMP_SIZE - 1 && (((i<<8)+i) - (int)ramp_index_to_short[j]) > ((int)ramp_index_to_short[j+1] - ((i<<8)+i)))
+           j++;
+       info->gray8_to_pseudocolor[i] = gray_to_pseudocolor[j];
+    }
+
+    for (i = 0; i < 256; i++) {
+       info->colors[i].a = 0xff;
+       info->colors[i].r = colors[i].red   >> 8;
+       info->colors[i].g = colors[i].green >> 8;
+       info->colors[i].b = colors[i].blue  >> 8;
+    }
+
+    *out = info;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+void
+_cairo_xlib_visual_info_destroy (cairo_xlib_visual_info_t *info)
+{
+    /* No need for XFreeColors() whilst using DefaultColormap */
+    free (info);
+}
+
+#endif /* !CAIRO_HAS_XLIB_XCB_FUNCTIONS */
diff --git a/src/cairo-xlib-xcb-surface.c b/src/cairo-xlib-xcb-surface.c
new file mode 100644 (file)
index 0000000..f4ddd97
--- /dev/null
@@ -0,0 +1,821 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ * Copyright © 2009 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *     Carl D. Worth <cworth@cworth.org>
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+
+#if CAIRO_HAS_XLIB_XCB_FUNCTIONS
+
+#include "cairo-xlib.h"
+#include "cairo-xcb.h"
+
+#include "cairo-xcb-private.h"
+#include "cairo-xlib-xrender-private.h"
+
+#include "cairo-default-context-private.h"
+#include "cairo-image-surface-private.h"
+#include "cairo-surface-backend-private.h"
+
+#include <X11/Xlib-xcb.h>
+#include <X11/Xlibint.h>       /* For XESetCloseDisplay */
+
+struct cairo_xlib_xcb_display_t {
+    cairo_device_t  base;
+
+    Display        *dpy;
+    cairo_device_t *xcb_device;
+    XExtCodes      *codes;
+
+    cairo_list_t    link;
+};
+typedef struct cairo_xlib_xcb_display_t cairo_xlib_xcb_display_t;
+
+/* List of all #cairo_xlib_xcb_display_t alive,
+ * protected by _cairo_xlib_display_mutex */
+static cairo_list_t displays;
+
+static cairo_surface_t *
+_cairo_xlib_xcb_surface_create (void *dpy,
+                               void *scr,
+                               void *visual,
+                               void *format,
+                               cairo_surface_t *xcb);
+
+static cairo_surface_t *
+_cairo_xlib_xcb_surface_create_similar (void                   *abstract_other,
+                                       cairo_content_t          content,
+                                       int                      width,
+                                       int                      height)
+{
+    cairo_xlib_xcb_surface_t *other = abstract_other;
+    cairo_surface_t *xcb;
+
+    xcb = other->xcb->base.backend->create_similar (other->xcb, content, width, height);
+    if (unlikely (xcb == NULL || xcb->status))
+       return xcb;
+
+    return _cairo_xlib_xcb_surface_create (other->display, other->screen, NULL, NULL, xcb);
+}
+
+static cairo_status_t
+_cairo_xlib_xcb_surface_finish (void *abstract_surface)
+{
+    cairo_xlib_xcb_surface_t *surface = abstract_surface;
+    cairo_status_t status;
+
+    cairo_surface_finish (&surface->xcb->base);
+    status = surface->xcb->base.status;
+    cairo_surface_destroy (&surface->xcb->base);
+    surface->xcb = NULL;
+
+    return status;
+}
+
+static cairo_surface_t *
+_cairo_xlib_xcb_surface_create_similar_image (void                     *abstract_other,
+                                             cairo_format_t             format,
+                                             int                        width,
+                                             int                        height)
+{
+    cairo_xlib_xcb_surface_t *surface = abstract_other;
+    return cairo_surface_create_similar_image (&surface->xcb->base, format, width, height);
+}
+
+static cairo_surface_t *
+_cairo_xlib_xcb_surface_map_to_image (void *abstract_surface,
+                                     const cairo_rectangle_int_t *extents)
+{
+    cairo_xlib_xcb_surface_t *surface = abstract_surface;
+    return cairo_surface_map_to_image (&surface->xcb->base, extents);
+}
+
+static cairo_int_status_t
+_cairo_xlib_xcb_surface_unmap (void *abstract_surface,
+                              cairo_image_surface_t *image)
+{
+    cairo_xlib_xcb_surface_t *surface = abstract_surface;
+
+    /* cairo_surface_unmap_image destroys the surface, so get a new reference
+     * for it to destroy.
+     */
+    cairo_surface_reference (&image->base);
+    cairo_surface_unmap_image (&surface->xcb->base, &image->base);
+    return cairo_surface_status (&surface->xcb->base);
+}
+
+static cairo_status_t
+_cairo_xlib_xcb_surface_acquire_source_image (void *abstract_surface,
+                                             cairo_image_surface_t **image_out,
+                                             void **image_extra)
+{
+    cairo_xlib_xcb_surface_t *surface = abstract_surface;
+    return _cairo_surface_acquire_source_image (&surface->xcb->base,
+                                               image_out, image_extra);
+}
+
+static void
+_cairo_xlib_xcb_surface_release_source_image (void *abstract_surface,
+                                             cairo_image_surface_t *image_out,
+                                             void *image_extra)
+{
+    cairo_xlib_xcb_surface_t *surface = abstract_surface;
+    _cairo_surface_release_source_image (&surface->xcb->base, image_out, image_extra);
+}
+
+static cairo_bool_t
+_cairo_xlib_xcb_surface_get_extents (void *abstract_surface,
+                                    cairo_rectangle_int_t *extents)
+{
+    cairo_xlib_xcb_surface_t *surface = abstract_surface;
+    return _cairo_surface_get_extents (&surface->xcb->base, extents);
+}
+
+static void
+_cairo_xlib_xcb_surface_get_font_options (void *abstract_surface,
+                                         cairo_font_options_t *options)
+{
+    cairo_xlib_xcb_surface_t *surface = abstract_surface;
+    cairo_surface_get_font_options (&surface->xcb->base, options);
+}
+
+static cairo_int_status_t
+_cairo_xlib_xcb_surface_paint (void                    *abstract_surface,
+                              cairo_operator_t          op,
+                              const cairo_pattern_t    *source,
+                              const cairo_clip_t       *clip)
+{
+    cairo_xlib_xcb_surface_t *surface = abstract_surface;
+    return _cairo_surface_paint (&surface->xcb->base, op, source, clip);
+}
+
+static cairo_int_status_t
+_cairo_xlib_xcb_surface_mask (void                     *abstract_surface,
+                             cairo_operator_t           op,
+                             const cairo_pattern_t     *source,
+                             const cairo_pattern_t     *mask,
+                             const cairo_clip_t        *clip)
+{
+    cairo_xlib_xcb_surface_t *surface = abstract_surface;
+    return _cairo_surface_mask (&surface->xcb->base, op, source, mask, clip);
+}
+
+static cairo_int_status_t
+_cairo_xlib_xcb_surface_stroke (void                           *abstract_surface,
+                               cairo_operator_t                 op,
+                               const cairo_pattern_t           *source,
+                               const cairo_path_fixed_t        *path,
+                               const cairo_stroke_style_t      *style,
+                               const cairo_matrix_t            *ctm,
+                               const cairo_matrix_t            *ctm_inverse,
+                               double                           tolerance,
+                               cairo_antialias_t                antialias,
+                               const cairo_clip_t              *clip)
+{
+    cairo_xlib_xcb_surface_t *surface = abstract_surface;
+    return _cairo_surface_stroke (&surface->xcb->base,
+                                 op, source, path, style, ctm, ctm_inverse,
+                                 tolerance, antialias, clip);
+}
+
+static cairo_int_status_t
+_cairo_xlib_xcb_surface_fill (void                     *abstract_surface,
+                             cairo_operator_t           op,
+                             const cairo_pattern_t     *source,
+                             const cairo_path_fixed_t  *path,
+                             cairo_fill_rule_t          fill_rule,
+                             double                     tolerance,
+                             cairo_antialias_t          antialias,
+                             const cairo_clip_t        *clip)
+{
+    cairo_xlib_xcb_surface_t *surface = abstract_surface;
+    return _cairo_surface_fill (&surface->xcb->base,
+                               op, source, path,
+                               fill_rule, tolerance,
+                               antialias, clip);
+}
+
+static cairo_int_status_t
+_cairo_xlib_xcb_surface_glyphs (void                   *abstract_surface,
+                               cairo_operator_t         op,
+                               const cairo_pattern_t   *source,
+                               cairo_glyph_t           *glyphs,
+                               int                      num_glyphs,
+                               cairo_scaled_font_t     *scaled_font,
+                               const cairo_clip_t      *clip)
+{
+    cairo_xlib_xcb_surface_t *surface = abstract_surface;
+    return _cairo_surface_show_text_glyphs (&surface->xcb->base, op, source,
+                                           NULL, 0,
+                                           glyphs, num_glyphs,
+                                           NULL, 0, 0,
+                                           scaled_font, clip);
+}
+
+static cairo_status_t
+_cairo_xlib_xcb_surface_flush (void *abstract_surface)
+{
+    cairo_xlib_xcb_surface_t *surface = abstract_surface;
+    /* We have to call cairo_surface_flush() to make sure snapshots are detached */
+    cairo_surface_flush (&surface->xcb->base);
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_xlib_xcb_surface_mark_dirty (void *abstract_surface,
+                                   int x, int y,
+                                   int width, int height)
+{
+    cairo_xlib_xcb_surface_t *surface = abstract_surface;
+    cairo_surface_mark_dirty_rectangle (&surface->xcb->base, x, y, width, height);
+    return cairo_surface_status (&surface->xcb->base);
+}
+
+static const cairo_surface_backend_t _cairo_xlib_xcb_surface_backend = {
+    CAIRO_SURFACE_TYPE_XLIB,
+    _cairo_xlib_xcb_surface_finish,
+
+    _cairo_default_context_create, /* XXX */
+
+    _cairo_xlib_xcb_surface_create_similar,
+    _cairo_xlib_xcb_surface_create_similar_image,
+    _cairo_xlib_xcb_surface_map_to_image,
+    _cairo_xlib_xcb_surface_unmap,
+
+    _cairo_xlib_xcb_surface_acquire_source_image,
+    _cairo_xlib_xcb_surface_release_source_image,
+    NULL, /* snapshot */
+
+    NULL, /* copy_page */
+    NULL, /* show_page */
+
+    _cairo_xlib_xcb_surface_get_extents,
+    _cairo_xlib_xcb_surface_get_font_options,
+
+    _cairo_xlib_xcb_surface_flush,
+    _cairo_xlib_xcb_surface_mark_dirty,
+
+    _cairo_xlib_xcb_surface_paint,
+    _cairo_xlib_xcb_surface_mask,
+    _cairo_xlib_xcb_surface_stroke,
+    _cairo_xlib_xcb_surface_fill,
+    NULL, /* fill_stroke */
+    _cairo_xlib_xcb_surface_glyphs,
+};
+
+static void
+_cairo_xlib_xcb_display_finish (void *abstract_display)
+{
+    cairo_xlib_xcb_display_t *display = (cairo_xlib_xcb_display_t *) abstract_display;
+
+    CAIRO_MUTEX_LOCK (_cairo_xlib_display_mutex);
+    cairo_list_del (&display->link);
+    CAIRO_MUTEX_UNLOCK (_cairo_xlib_display_mutex);
+
+    cairo_device_destroy (display->xcb_device);
+    display->xcb_device = NULL;
+
+    XESetCloseDisplay (display->dpy, display->codes->extension, NULL);
+    /* Drop the reference from _cairo_xlib_xcb_device_create */
+    cairo_device_destroy (&display->base);
+}
+
+static int
+_cairo_xlib_xcb_close_display(Display *dpy, XExtCodes *codes)
+{
+    cairo_xlib_xcb_display_t *display;
+
+    CAIRO_MUTEX_LOCK (_cairo_xlib_display_mutex);
+    cairo_list_foreach_entry (display,
+                             cairo_xlib_xcb_display_t,
+                             &displays,
+                             link)
+    {
+       if (display->dpy == dpy)
+       {
+           /* _cairo_xlib_xcb_display_finish will lock the mutex again
+            * -> deadlock (This mutex isn't recursive) */
+           cairo_device_reference (&display->base);
+           CAIRO_MUTEX_UNLOCK (_cairo_xlib_display_mutex);
+
+           /* Make sure the xcb and xlib-xcb devices are finished */
+           cairo_device_finish (display->xcb_device);
+           cairo_device_finish (&display->base);
+
+           cairo_device_destroy (&display->base);
+           return 0;
+       }
+    }
+    CAIRO_MUTEX_UNLOCK (_cairo_xlib_display_mutex);
+
+    return 0;
+}
+
+static const cairo_device_backend_t _cairo_xlib_xcb_device_backend = {
+    CAIRO_DEVICE_TYPE_XLIB,
+
+    NULL,
+    NULL,
+
+    NULL, /* flush */
+    _cairo_xlib_xcb_display_finish,
+    free, /* destroy */
+};
+
+static cairo_device_t *
+_cairo_xlib_xcb_device_create (Display *dpy, cairo_device_t *xcb_device)
+{
+    cairo_xlib_xcb_display_t *display = NULL;
+    cairo_device_t *device;
+
+    if (xcb_device == NULL)
+       return NULL;
+
+    CAIRO_MUTEX_INITIALIZE ();
+
+    CAIRO_MUTEX_LOCK (_cairo_xlib_display_mutex);
+    if (displays.next == NULL) {
+       cairo_list_init (&displays);
+    }
+
+    cairo_list_foreach_entry (display,
+                             cairo_xlib_xcb_display_t,
+                             &displays,
+                             link)
+    {
+       if (display->dpy == dpy) {
+           /* Maintain MRU order. */
+           if (displays.next != &display->link)
+               cairo_list_move (&display->link, &displays);
+
+           /* Grab a reference for our caller */
+           device = cairo_device_reference (&display->base);
+           assert (display->xcb_device == xcb_device);
+           goto unlock;
+       }
+    }
+
+    display = malloc (sizeof (cairo_xlib_xcb_display_t));
+    if (unlikely (display == NULL)) {
+       device = _cairo_device_create_in_error (CAIRO_STATUS_NO_MEMORY);
+       goto unlock;
+    }
+
+    display->codes = XAddExtension (dpy);
+    if (unlikely (display->codes == NULL)) {
+       device = _cairo_device_create_in_error (CAIRO_STATUS_NO_MEMORY);
+       free (display);
+       goto unlock;
+    }
+
+    _cairo_device_init (&display->base, &_cairo_xlib_xcb_device_backend);
+
+    XESetCloseDisplay (dpy, display->codes->extension, _cairo_xlib_xcb_close_display);
+    /* Add a reference for _cairo_xlib_xcb_display_finish. This basically means
+     * that the device's reference count never drops to zero
+     * as long as our Display* is alive. We need this because there is no
+     * "XDelExtension" to undo XAddExtension and having lots of registered
+     * extensions slows down libX11. */
+    cairo_device_reference (&display->base);
+
+    display->dpy = dpy;
+    display->xcb_device = cairo_device_reference(xcb_device);
+
+    cairo_list_add (&display->link, &displays);
+    device = &display->base;
+
+unlock:
+    CAIRO_MUTEX_UNLOCK (_cairo_xlib_display_mutex);
+
+    return device;
+}
+
+static cairo_surface_t *
+_cairo_xlib_xcb_surface_create (void *dpy,
+                               void *scr,
+                               void *visual,
+                               void *format,
+                               cairo_surface_t *xcb)
+{
+    cairo_xlib_xcb_surface_t *surface;
+
+    if (unlikely (xcb->status))
+       return xcb;
+
+    surface = malloc (sizeof (*surface));
+    if (unlikely (surface == NULL)) {
+       cairo_surface_destroy (xcb);
+       return _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY);
+    }
+
+    _cairo_surface_init (&surface->base,
+                        &_cairo_xlib_xcb_surface_backend,
+                        _cairo_xlib_xcb_device_create (dpy, xcb->device),
+                        xcb->content);
+
+    /* _cairo_surface_init() got another reference to the device, drop ours */
+    cairo_device_destroy (surface->base.device);
+
+    surface->display = dpy;
+    surface->screen = scr;
+    surface->visual = visual;
+    surface->format = format;
+    surface->xcb = (cairo_xcb_surface_t *) xcb;
+
+    return &surface->base;
+}
+
+static Screen *
+_cairo_xlib_screen_from_visual (Display *dpy, Visual *visual)
+{
+    int s, d, v;
+
+    for (s = 0; s < ScreenCount (dpy); s++) {
+       Screen *screen;
+
+       screen = ScreenOfDisplay (dpy, s);
+       if (visual == DefaultVisualOfScreen (screen))
+           return screen;
+
+       for (d = 0; d < screen->ndepths; d++) {
+           Depth  *depth;
+
+           depth = &screen->depths[d];
+           for (v = 0; v < depth->nvisuals; v++)
+               if (visual == &depth->visuals[v])
+                   return screen;
+       }
+    }
+
+    return NULL;
+}
+
+cairo_surface_t *
+cairo_xlib_surface_create (Display     *dpy,
+                          Drawable     drawable,
+                          Visual      *visual,
+                          int          width,
+                          int          height)
+{
+    Screen *scr;
+    xcb_visualtype_t xcb_visual;
+
+    scr = _cairo_xlib_screen_from_visual (dpy, visual);
+    if (scr == NULL)
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_VISUAL));
+
+    xcb_visual.visual_id = visual->visualid;
+#if defined(__cplusplus) || defined(c_plusplus)
+    xcb_visual._class = visual->c_class;
+#else
+    xcb_visual._class = visual->class;
+#endif
+    xcb_visual.bits_per_rgb_value = visual->bits_per_rgb;
+    xcb_visual.colormap_entries = visual->map_entries;
+    xcb_visual.red_mask = visual->red_mask;
+    xcb_visual.green_mask = visual->green_mask;
+    xcb_visual.blue_mask = visual->blue_mask;
+
+    return _cairo_xlib_xcb_surface_create (dpy, scr, visual, NULL,
+                                          cairo_xcb_surface_create (XGetXCBConnection (dpy),
+                                                                    drawable,
+                                                                    &xcb_visual,
+                                                                    width, height));
+}
+
+cairo_surface_t *
+cairo_xlib_surface_create_for_bitmap (Display  *dpy,
+                                     Pixmap    bitmap,
+                                     Screen   *scr,
+                                     int       width,
+                                     int       height)
+{
+    return _cairo_xlib_xcb_surface_create (dpy, scr, NULL, NULL,
+                                          cairo_xcb_surface_create_for_bitmap (XGetXCBConnection (dpy),
+                                                                               (xcb_screen_t *) scr,
+                                                                               bitmap,
+                                                                               width, height));
+}
+
+#if CAIRO_HAS_XLIB_XRENDER_SURFACE
+static xcb_screen_t *
+_cairo_xcb_screen_from_root (xcb_connection_t *connection,
+                            xcb_window_t id)
+{
+    xcb_screen_iterator_t s;
+
+    s = xcb_setup_roots_iterator (xcb_get_setup (connection));
+    for (; s.rem; xcb_screen_next (&s)) {
+       if (s.data->root == id)
+           return s.data;
+    }
+
+    return NULL;
+}
+cairo_surface_t *
+cairo_xlib_surface_create_with_xrender_format (Display             *dpy,
+                                              Drawable             drawable,
+                                              Screen               *scr,
+                                              XRenderPictFormat    *format,
+                                              int                  width,
+                                              int                  height)
+{
+    xcb_render_pictforminfo_t xcb_format;
+    xcb_connection_t *connection;
+    xcb_screen_t *screen;
+
+    xcb_format.id = format->id;
+    xcb_format.type = format->type;
+    xcb_format.depth = format->depth;
+    xcb_format.direct.red_shift = format->direct.red;
+    xcb_format.direct.red_mask = format->direct.redMask;
+    xcb_format.direct.green_shift = format->direct.green;
+    xcb_format.direct.green_mask = format->direct.greenMask;
+    xcb_format.direct.blue_shift = format->direct.blue;
+    xcb_format.direct.blue_mask = format->direct.blueMask;
+    xcb_format.direct.alpha_shift = format->direct.alpha;
+    xcb_format.direct.alpha_mask = format->direct.alphaMask;
+    xcb_format.colormap = format->colormap;
+
+    connection = XGetXCBConnection (dpy);
+    screen = _cairo_xcb_screen_from_root (connection, (xcb_window_t) scr->root);
+
+    return _cairo_xlib_xcb_surface_create (dpy, scr, NULL, format,
+                                          cairo_xcb_surface_create_with_xrender_format (connection, screen,
+                                                                                        drawable,
+                                                                                        &xcb_format,
+                                                                                        width, height));
+}
+
+XRenderPictFormat *
+cairo_xlib_surface_get_xrender_format (cairo_surface_t *surface)
+{
+    cairo_xlib_xcb_surface_t *xlib_surface = (cairo_xlib_xcb_surface_t *) surface;
+
+    /* Throw an error for a non-xlib surface */
+    if (surface->type != CAIRO_SURFACE_TYPE_XLIB) {
+       _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+       return NULL;
+    }
+
+    return xlib_surface->format;
+}
+#endif
+
+void
+cairo_xlib_surface_set_size (cairo_surface_t *abstract_surface,
+                            int              width,
+                            int              height)
+{
+    cairo_xlib_xcb_surface_t *surface = (cairo_xlib_xcb_surface_t *) abstract_surface;
+    cairo_status_t status;
+
+    if (unlikely (abstract_surface->status))
+       return;
+    if (unlikely (abstract_surface->finished)) {
+       status = _cairo_surface_set_error (abstract_surface,
+                                          _cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
+       return;
+    }
+
+    if (surface->base.type != CAIRO_SURFACE_TYPE_XLIB) {
+       status = _cairo_surface_set_error (abstract_surface,
+                                          CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+       return;
+    }
+
+    cairo_xcb_surface_set_size (&surface->xcb->base, width, height);
+    if (unlikely (surface->xcb->base.status)) {
+       status = _cairo_surface_set_error (abstract_surface,
+                                          _cairo_error (surface->xcb->base.status));
+    }
+}
+
+void
+cairo_xlib_surface_set_drawable (cairo_surface_t   *abstract_surface,
+                                Drawable           drawable,
+                                int                width,
+                                int                height)
+{
+    cairo_xlib_xcb_surface_t *surface = (cairo_xlib_xcb_surface_t *)abstract_surface;
+    cairo_status_t status;
+
+    if (unlikely (abstract_surface->status))
+       return;
+    if (unlikely (abstract_surface->finished)) {
+       status = _cairo_surface_set_error (abstract_surface,
+                                          _cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
+       return;
+    }
+
+    if (surface->base.type != CAIRO_SURFACE_TYPE_XLIB) {
+       status = _cairo_surface_set_error (abstract_surface,
+                                          CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+       return;
+    }
+
+    ASSERT_NOT_REACHED;
+}
+
+Display *
+cairo_xlib_surface_get_display (cairo_surface_t *abstract_surface)
+{
+    cairo_xlib_xcb_surface_t *surface = (cairo_xlib_xcb_surface_t *) abstract_surface;
+
+    if (surface->base.type != CAIRO_SURFACE_TYPE_XLIB) {
+       _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+       return NULL;
+    }
+
+    return surface->display;
+}
+
+Drawable
+cairo_xlib_surface_get_drawable (cairo_surface_t *abstract_surface)
+{
+    cairo_xlib_xcb_surface_t *surface = (cairo_xlib_xcb_surface_t *) abstract_surface;
+
+    if (unlikely (abstract_surface->finished)) {
+       _cairo_error_throw (CAIRO_STATUS_SURFACE_FINISHED);
+       return 0;
+    }
+    if (surface->base.type != CAIRO_SURFACE_TYPE_XLIB) {
+       _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+       return 0;
+    }
+    /* This can happen when e.g. create_similar falls back to an image surface
+     * because we don't have the RENDER extension. */
+    if (surface->xcb->base.type != CAIRO_SURFACE_TYPE_XCB) {
+       _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+       return 0;
+    }
+
+    return surface->xcb->drawable;
+}
+
+Screen *
+cairo_xlib_surface_get_screen (cairo_surface_t *abstract_surface)
+{
+    cairo_xlib_xcb_surface_t *surface = (cairo_xlib_xcb_surface_t *) abstract_surface;
+
+    if (surface->base.type != CAIRO_SURFACE_TYPE_XLIB) {
+       _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+       return NULL;
+    }
+
+    return surface->screen;
+}
+
+Visual *
+cairo_xlib_surface_get_visual (cairo_surface_t *abstract_surface)
+{
+    cairo_xlib_xcb_surface_t *surface = (cairo_xlib_xcb_surface_t *) abstract_surface;
+
+    if (surface->base.type != CAIRO_SURFACE_TYPE_XLIB) {
+       _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+       return NULL;
+    }
+
+    return surface->visual;
+}
+
+int
+cairo_xlib_surface_get_depth (cairo_surface_t *abstract_surface)
+{
+    cairo_xlib_xcb_surface_t *surface = (cairo_xlib_xcb_surface_t *) abstract_surface;
+
+    if (unlikely (abstract_surface->finished)) {
+       _cairo_error_throw (CAIRO_STATUS_SURFACE_FINISHED);
+       return 0;
+    }
+    if (surface->base.type != CAIRO_SURFACE_TYPE_XLIB) {
+       _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+       return 0;
+    }
+    /* This can happen when e.g. create_similar falls back to an image surface
+     * because we don't have the RENDER extension. */
+    if (surface->xcb->base.type != CAIRO_SURFACE_TYPE_XCB) {
+       _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+       return 0;
+    }
+
+    return surface->xcb->depth;
+}
+
+int
+cairo_xlib_surface_get_width (cairo_surface_t *abstract_surface)
+{
+    cairo_xlib_xcb_surface_t *surface = (cairo_xlib_xcb_surface_t *) abstract_surface;
+
+    if (unlikely (abstract_surface->finished)) {
+       _cairo_error_throw (CAIRO_STATUS_SURFACE_FINISHED);
+       return 0;
+    }
+    if (surface->base.type != CAIRO_SURFACE_TYPE_XLIB) {
+       _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+       return 0;
+    }
+    /* This can happen when e.g. create_similar falls back to an image surface
+     * because we don't have the RENDER extension. */
+    if (surface->xcb->base.type != CAIRO_SURFACE_TYPE_XCB) {
+       _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+       return 0;
+    }
+
+    return surface->xcb->width;
+}
+
+int
+cairo_xlib_surface_get_height (cairo_surface_t *abstract_surface)
+{
+    cairo_xlib_xcb_surface_t *surface = (cairo_xlib_xcb_surface_t *) abstract_surface;
+
+    if (unlikely (abstract_surface->finished)) {
+       _cairo_error_throw (CAIRO_STATUS_SURFACE_FINISHED);
+       return 0;
+    }
+    if (surface->base.type != CAIRO_SURFACE_TYPE_XLIB) {
+       _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+       return 0;
+    }
+    /* This can happen when e.g. create_similar falls back to an image surface
+     * because we don't have the RENDER extension. */
+    if (surface->xcb->base.type != CAIRO_SURFACE_TYPE_XCB) {
+       _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+       return 0;
+    }
+
+    return surface->xcb->height;
+}
+
+void
+cairo_xlib_device_debug_set_precision (cairo_device_t *device,
+                                      int precision)
+{
+    cairo_xlib_xcb_display_t *display = (cairo_xlib_xcb_display_t *) device;
+
+    if (device == NULL || device->status)
+       return;
+    if (device->backend->type != CAIRO_DEVICE_TYPE_XLIB) {
+       cairo_status_t status;
+
+       status = _cairo_device_set_error (device, CAIRO_STATUS_DEVICE_TYPE_MISMATCH);
+       (void) status;
+       return;
+    }
+
+    cairo_xcb_device_debug_set_precision (display->xcb_device, precision);
+}
+
+int
+cairo_xlib_device_debug_get_precision (cairo_device_t *device)
+{
+    cairo_xlib_xcb_display_t *display = (cairo_xlib_xcb_display_t *) device;
+
+    if (device == NULL || device->status)
+       return -1;
+    if (device->backend->type != CAIRO_DEVICE_TYPE_XLIB) {
+       cairo_status_t status;
+
+       status = _cairo_device_set_error (device, CAIRO_STATUS_DEVICE_TYPE_MISMATCH);
+       (void) status;
+       return -1;
+    }
+
+    return cairo_xcb_device_debug_get_precision (display->xcb_device);
+}
+
+#endif /* CAIRO_HAS_XLIB_XCB_FUNCTIONS */
diff --git a/src/cairo-xlib-xrender-private.h b/src/cairo-xlib-xrender-private.h
new file mode 100644 (file)
index 0000000..bf3199c
--- /dev/null
@@ -0,0 +1,1169 @@
+/* Cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ */
+
+#ifndef CAIRO_XLIB_XRENDER_PRIVATE_H
+#define CAIRO_XLIB_XRENDER_PRIVATE_H
+
+#include "cairo-features.h"
+#include "cairo-compiler-private.h"
+
+#include <X11/Xlib.h>
+
+/* These prototypes are used when defining interfaces missing from the
+ * render headers.  As it happens, it is the case that all libxrender
+ * functions take a pointer as first argument. */
+
+__attribute__((__unused__)) static void   _void_consume        (void *p, ...)   { }
+__attribute__((__unused__)) static void * _voidp_consume       (void *p, ...)   { return (void *)0; }
+__attribute__((__unused__)) static int    _int_consume         (void *p, ...)   { return 0; }
+__attribute__((__unused__)) static void   _void_consume_free   (Display *p, XID n) { }
+
+
+#if CAIRO_HAS_XLIB_XRENDER_SURFACE
+
+#include "cairo-xlib-xrender.h"
+
+#include <X11/extensions/Xrender.h>
+#include <X11/extensions/renderproto.h>
+
+/* We require Render >= 0.6.  The following defines were only added in
+ * 0.10.  Make sure they are defined.
+ */
+
+/* Filters included in 0.10 */
+#ifndef FilterConvolution
+#define FilterConvolution                  "convolution"
+#endif
+
+/* Extended repeat attributes included in 0.10 */
+#ifndef RepeatNone
+#define RepeatNone                          0
+#define RepeatNormal                        1
+#define RepeatPad                           2
+#define RepeatReflect                       3
+#endif
+
+
+#ifndef PictOptBlendMinimum
+/*
+ * Operators only available in version 0.11
+ */
+#define PictOpBlendMinimum                         0x30
+#define PictOpMultiply                             0x30
+#define PictOpScreen                               0x31
+#define PictOpOverlay                              0x32
+#define PictOpDarken                               0x33
+#define PictOpLighten                              0x34
+#define PictOpColorDodge                           0x35
+#define PictOpColorBurn                                    0x36
+#define PictOpHardLight                                    0x37
+#define PictOpSoftLight                                    0x38
+#define PictOpDifference                           0x39
+#define PictOpExclusion                                    0x3a
+#define PictOpHSLHue                               0x3b
+#define PictOpHSLSaturation                        0x3c
+#define PictOpHSLColor                             0x3d
+#define PictOpHSLLuminosity                        0x3e
+#define PictOpBlendMaximum                         0x3e
+#endif
+
+#if !HAVE_XRENDERCREATELINEARGRADIENT
+#define XRenderCreateLinearGradient                    _int_consume
+
+typedef struct _XLinearGradient {
+    XPointFixed p1;
+    XPointFixed p2;
+} XLinearGradient;
+#endif
+
+#if !HAVE_XRENDERCREATERADIALGRADIENT
+#define XRenderCreateRadialGradient                    _int_consume
+
+typedef struct _XCircle {
+    XFixed x;
+    XFixed y;
+    XFixed radius;
+} XCircle;
+typedef struct _XRadialGradient {
+    XCircle inner;
+    XCircle outer;
+} XRadialGradient;
+#endif
+
+#if !HAVE_XRENDERCREATECONICALGRADIENT
+#define XRenderCreateConicalGradient                   _int_consume
+
+typedef struct _XConicalGradient {
+    XPointFixed center;
+    XFixed angle; /* in degrees */
+} XConicalGradient;
+#endif
+
+
+#else /* !CAIRO_HAS_XLIB_XRENDER_SURFACE */
+
+/* Provide dummy symbols and macros to get it compile and take the fallback
+ * route, just like as if Xrender is not available in the server at run-time. */
+
+
+/* Functions */
+
+#define XRenderQueryExtension                          _int_consume
+#define XRenderQueryVersion                            _int_consume
+#define XRenderQueryFormats                            _int_consume
+#define XRenderQuerySubpixelOrder                      _int_consume
+#define XRenderSetSubpixelOrder                                _int_consume
+#define XRenderFindVisualFormat                                _voidp_consume
+#define XRenderFindFormat                              _voidp_consume
+#define XRenderFindStandardFormat                      _voidp_consume
+#define XRenderQueryPictIndexValues                    _voidp_consume
+#define XRenderCreatePicture                           _int_consume
+#define XRenderChangePicture                           _void_consume
+#define XRenderSetPictureClipRectangles                        _void_consume
+#define XRenderSetPictureClipRegion                    _void_consume
+#define XRenderSetPictureTransform                     _void_consume
+#define XRenderFreePicture                             _void_consume_free
+#define XRenderComposite                               _void_consume
+#define XRenderCreateGlyphSet                          _int_consume
+#define XRenderReferenceGlyphSet                       _int_consume
+#define XRenderFreeGlyphSet                            _void_consume_free
+#define XRenderAddGlyphs                               _void_consume
+#define XRenderFreeGlyphs                              _void_consume
+#define XRenderCompositeString8                                _void_consume
+#define XRenderCompositeString16                       _void_consume
+#define XRenderCompositeString32                       _void_consume
+#define XRenderCompositeText8                          (cairo_xrender_composite_text_func_t) _void_consume
+#define XRenderCompositeText16                         _void_consume
+#define XRenderCompositeText32                         _void_consume
+#define XRenderFillRectangle                           _void_consume
+#define XRenderFillRectangles                          _void_consume
+#define XRenderCompositeTrapezoids                     _void_consume
+#define XRenderCompositeTriangles                      _void_consume
+#define XRenderCompositeTriStrip                       _void_consume
+#define XRenderCompositeTriFan                         _void_consume
+#define XRenderCompositeDoublePoly                     _void_consume
+#define XRenderParseColor                              _int_consume
+#define XRenderCreateCursor                            _int_consume
+#define XRenderQueryFilters                            _voidp_consume
+#define XRenderSetPictureFilter                                _void_consume
+#define XRenderCreateAnimCursor                                _int_consume
+#define XRenderAddTraps                                        _void_consume
+#define XRenderCreateSolidFill                         _int_consume
+#define XRenderCreateLinearGradient                    _int_consume
+#define XRenderCreateRadialGradient                    _int_consume
+#define XRenderCreateConicalGradient                   _int_consume
+
+#define cairo_xlib_surface_create_with_xrender_format  _voidp_consume
+
+
+
+/* The rest of this file is copied from various Xrender header files, with
+ * the following copyright/license information:
+ *
+ * Copyright © 2000 SuSE, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of SuSE not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission.  SuSE makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as is"
+ * without express or implied warranty.
+ *
+ * SuSE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL SuSE
+ * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author:  Keith Packard, SuSE, Inc.
+ */
+
+
+/* Copied from X11/extensions/render.h */
+
+typedef unsigned long  Glyph;
+typedef unsigned long  GlyphSet;
+typedef unsigned long  Picture;
+typedef unsigned long  PictFormat;
+
+#define BadPictFormat                      0
+#define BadPicture                         1
+#define BadPictOp                          2
+#define BadGlyphSet                        3
+#define BadGlyph                           4
+#define RenderNumberErrors                 (BadGlyph+1)
+
+#define PictTypeIndexed                            0
+#define PictTypeDirect                     1
+
+#define PictOpMinimum                      0
+#define PictOpClear                        0
+#define PictOpSrc                          1
+#define PictOpDst                          2
+#define PictOpOver                         3
+#define PictOpOverReverse                  4
+#define PictOpIn                           5
+#define PictOpInReverse                            6
+#define PictOpOut                          7
+#define PictOpOutReverse                   8
+#define PictOpAtop                         9
+#define PictOpAtopReverse                  10
+#define PictOpXor                          11
+#define PictOpAdd                          12
+#define PictOpSaturate                     13
+#define PictOpMaximum                      13
+
+/*
+ * Operators only available in version 0.2
+ */
+#define PictOpDisjointMinimum                      0x10
+#define PictOpDisjointClear                        0x10
+#define PictOpDisjointSrc                          0x11
+#define PictOpDisjointDst                          0x12
+#define PictOpDisjointOver                         0x13
+#define PictOpDisjointOverReverse                  0x14
+#define PictOpDisjointIn                           0x15
+#define PictOpDisjointInReverse                            0x16
+#define PictOpDisjointOut                          0x17
+#define PictOpDisjointOutReverse                   0x18
+#define PictOpDisjointAtop                         0x19
+#define PictOpDisjointAtopReverse                  0x1a
+#define PictOpDisjointXor                          0x1b
+#define PictOpDisjointMaximum                      0x1b
+
+#define PictOpConjointMinimum                      0x20
+#define PictOpConjointClear                        0x20
+#define PictOpConjointSrc                          0x21
+#define PictOpConjointDst                          0x22
+#define PictOpConjointOver                         0x23
+#define PictOpConjointOverReverse                  0x24
+#define PictOpConjointIn                           0x25
+#define PictOpConjointInReverse                            0x26
+#define PictOpConjointOut                          0x27
+#define PictOpConjointOutReverse                   0x28
+#define PictOpConjointAtop                         0x29
+#define PictOpConjointAtopReverse                  0x2a
+#define PictOpConjointXor                          0x2b
+#define PictOpConjointMaximum                      0x2b
+
+/*
+ * Operators only available in version 0.11
+ */
+#define PictOpBlendMinimum                         0x30
+#define PictOpMultiply                             0x30
+#define PictOpScreen                               0x31
+#define PictOpOverlay                              0x32
+#define PictOpDarken                               0x33
+#define PictOpLighten                              0x34
+#define PictOpColorDodge                           0x35
+#define PictOpColorBurn                                    0x36
+#define PictOpHardLight                                    0x37
+#define PictOpSoftLight                                    0x38
+#define PictOpDifference                           0x39
+#define PictOpExclusion                                    0x3a
+#define PictOpHSLHue                               0x3b
+#define PictOpHSLSaturation                        0x3c
+#define PictOpHSLColor                             0x3d
+#define PictOpHSLLuminosity                        0x3e
+#define PictOpBlendMaximum                         0x3e
+
+#define PolyEdgeSharp                      0
+#define PolyEdgeSmooth                     1
+
+#define PolyModePrecise                            0
+#define PolyModeImprecise                  1
+
+#define CPRepeat                           (1 << 0)
+#define CPAlphaMap                         (1 << 1)
+#define CPAlphaXOrigin                     (1 << 2)
+#define CPAlphaYOrigin                     (1 << 3)
+#define CPClipXOrigin                      (1 << 4)
+#define CPClipYOrigin                      (1 << 5)
+#define CPClipMask                         (1 << 6)
+#define CPGraphicsExposure                 (1 << 7)
+#define CPSubwindowMode                            (1 << 8)
+#define CPPolyEdge                         (1 << 9)
+#define CPPolyMode                         (1 << 10)
+#define CPDither                           (1 << 11)
+#define CPComponentAlpha                   (1 << 12)
+#define CPLastBit                          12
+
+/* Filters included in 0.6 */
+#define FilterNearest                      "nearest"
+#define FilterBilinear                     "bilinear"
+/* Filters included in 0.10 */
+#define FilterConvolution                  "convolution"
+
+#define FilterFast                         "fast"
+#define FilterGood                         "good"
+#define FilterBest                         "best"
+
+#define FilterAliasNone                            -1
+
+/* Subpixel orders included in 0.6 */
+#define SubPixelUnknown                            0
+#define SubPixelHorizontalRGB              1
+#define SubPixelHorizontalBGR              2
+#define SubPixelVerticalRGB                3
+#define SubPixelVerticalBGR                4
+#define SubPixelNone                       5
+
+/* Extended repeat attributes included in 0.10 */
+#define RepeatNone                          0
+#define RepeatNormal                        1
+#define RepeatPad                           2
+#define RepeatReflect                       3
+
+
+
+/* Copied from X11/extensions/Xrender.h */
+
+typedef struct {
+    short   red;
+    short   redMask;
+    short   green;
+    short   greenMask;
+    short   blue;
+    short   blueMask;
+    short   alpha;
+    short   alphaMask;
+} XRenderDirectFormat;
+
+typedef struct {
+    PictFormat         id;
+    int                        type;
+    int                        depth;
+    XRenderDirectFormat        direct;
+    Colormap           colormap;
+} XRenderPictFormat;
+
+#define PictFormatID       (1 << 0)
+#define PictFormatType     (1 << 1)
+#define PictFormatDepth            (1 << 2)
+#define PictFormatRed      (1 << 3)
+#define PictFormatRedMask   (1 << 4)
+#define PictFormatGreen            (1 << 5)
+#define PictFormatGreenMask (1 << 6)
+#define PictFormatBlue     (1 << 7)
+#define PictFormatBlueMask  (1 << 8)
+#define PictFormatAlpha            (1 << 9)
+#define PictFormatAlphaMask (1 << 10)
+#define PictFormatColormap  (1 << 11)
+
+typedef struct _XRenderPictureAttributes {
+    int                repeat;
+    Picture            alpha_map;
+    int                        alpha_x_origin;
+    int                        alpha_y_origin;
+    int                        clip_x_origin;
+    int                        clip_y_origin;
+    Pixmap             clip_mask;
+    Bool               graphics_exposures;
+    int                        subwindow_mode;
+    int                        poly_edge;
+    int                        poly_mode;
+    Atom               dither;
+    Bool               component_alpha;
+} XRenderPictureAttributes;
+
+typedef struct {
+    unsigned short   red;
+    unsigned short   green;
+    unsigned short   blue;
+    unsigned short   alpha;
+} XRenderColor;
+
+typedef struct _XGlyphInfo {
+    unsigned short  width;
+    unsigned short  height;
+    short          x;
+    short          y;
+    short          xOff;
+    short          yOff;
+} XGlyphInfo;
+
+typedef struct _XGlyphElt8 {
+    GlyphSet               glyphset;
+    _Xconst char           *chars;
+    int                            nchars;
+    int                            xOff;
+    int                            yOff;
+} XGlyphElt8;
+
+typedef struct _XGlyphElt16 {
+    GlyphSet               glyphset;
+    _Xconst unsigned short  *chars;
+    int                            nchars;
+    int                            xOff;
+    int                            yOff;
+} XGlyphElt16;
+
+typedef struct _XGlyphElt32 {
+    GlyphSet               glyphset;
+    _Xconst unsigned int    *chars;
+    int                            nchars;
+    int                            xOff;
+    int                            yOff;
+} XGlyphElt32;
+
+typedef double XDouble;
+
+typedef struct _XPointDouble {
+    XDouble  x, y;
+} XPointDouble;
+
+#define XDoubleToFixed(f)    ((XFixed) ((f) * 65536))
+#define XFixedToDouble(f)    (((XDouble) (f)) / 65536)
+
+typedef int XFixed;
+
+typedef struct _XPointFixed {
+    XFixed  x, y;
+} XPointFixed;
+
+typedef struct _XLineFixed {
+    XPointFixed        p1, p2;
+} XLineFixed;
+
+typedef struct _XTriangle {
+    XPointFixed        p1, p2, p3;
+} XTriangle;
+
+typedef struct _XCircle {
+    XFixed x;
+    XFixed y;
+    XFixed radius;
+} XCircle;
+
+typedef struct _XTrapezoid {
+    XFixed  top, bottom;
+    XLineFixed left, right;
+} XTrapezoid;
+
+typedef struct _XTransform {
+    XFixed  matrix[3][3];
+} XTransform;
+
+typedef struct _XFilters {
+    int            nfilter;
+    char    **filter;
+    int            nalias;
+    short   *alias;
+} XFilters;
+
+typedef struct _XIndexValue {
+    unsigned long    pixel;
+    unsigned short   red, green, blue, alpha;
+} XIndexValue;
+
+typedef struct _XAnimCursor {
+    Cursor         cursor;
+    unsigned long   delay;
+} XAnimCursor;
+
+typedef struct _XSpanFix {
+    XFixed         left, right, y;
+} XSpanFix;
+
+typedef struct _XTrap {
+    XSpanFix       top, bottom;
+} XTrap;
+
+typedef struct _XLinearGradient {
+    XPointFixed p1;
+    XPointFixed p2;
+} XLinearGradient;
+
+typedef struct _XRadialGradient {
+    XCircle inner;
+    XCircle outer;
+} XRadialGradient;
+
+typedef struct _XConicalGradient {
+    XPointFixed center;
+    XFixed angle; /* in degrees */
+} XConicalGradient;
+
+#define PictStandardARGB32  0
+#define PictStandardRGB24   1
+#define PictStandardA8     2
+#define PictStandardA4     3
+#define PictStandardA1     4
+#define PictStandardNUM            5
+
+
+
+/* Copied from X11/extensions/renderproto.h */
+
+#include <X11/Xmd.h>
+
+#define Window CARD32
+#define Drawable CARD32
+#define Font CARD32
+#define Pixmap CARD32
+#define Cursor CARD32
+#define Colormap CARD32
+#define GContext CARD32
+#define Atom CARD32
+#define VisualID CARD32
+#define Time CARD32
+#define KeyCode CARD8
+#define KeySym CARD32
+
+#define Picture            CARD32
+#define PictFormat  CARD32
+#define Fixed      INT32
+#define Glyphset    CARD32
+#define Glyph      CARD32
+
+/*
+ * data structures
+ */
+
+typedef struct {
+    CARD16  red B16;
+    CARD16  redMask B16;
+    CARD16  green B16;
+    CARD16  greenMask B16;
+    CARD16  blue B16;
+    CARD16  blueMask B16;
+    CARD16  alpha B16;
+    CARD16  alphaMask B16;
+} xDirectFormat;
+
+#define sz_xDirectFormat    16
+
+typedef struct {
+    PictFormat id B32;
+    CARD8      type;
+    CARD8      depth;
+    CARD16     pad1 B16;
+    xDirectFormat   direct;
+    Colormap   colormap;
+} xPictFormInfo;
+
+#define sz_xPictFormInfo    28
+
+typedef struct {
+    VisualID   visual;
+    PictFormat format;
+} xPictVisual;
+
+#define sz_xPictVisual     8
+
+typedef struct {
+    CARD8      depth;
+    CARD8      pad1;
+    CARD16     nPictVisuals B16;
+    CARD32     pad2 B32;
+} xPictDepth;
+
+#define sz_xPictDepth  8
+
+typedef struct {
+    CARD32     nDepth B32;
+    PictFormat fallback B32;
+} xPictScreen;
+
+#define sz_xPictScreen 8
+
+typedef struct {
+    CARD32     pixel B32;
+    CARD16     red B16;
+    CARD16     green B16;
+    CARD16     blue B16;
+    CARD16     alpha B16;
+} xIndexValue;
+
+#define sz_xIndexValue 12
+
+typedef struct {
+    CARD16     red B16;
+    CARD16     green B16;
+    CARD16     blue B16;
+    CARD16     alpha B16;
+} xRenderColor;
+
+#define sz_xRenderColor        8
+
+typedef struct {
+    Fixed      x B32;
+    Fixed      y B32;
+} xPointFixed;
+
+#define sz_xPointFixed 8
+
+typedef struct {
+    xPointFixed        p1;
+    xPointFixed p2;
+} xLineFixed;
+
+#define sz_xLineFixed  16
+
+typedef struct {
+    xPointFixed        p1, p2, p3;
+} xTriangle;
+
+#define sz_xTriangle   24
+
+typedef struct {
+    Fixed      top B32;
+    Fixed      bottom B32;
+    xLineFixed left;
+    xLineFixed right;
+} xTrapezoid;
+
+#define sz_xTrapezoid  40
+
+typedef struct {
+    CARD16  width B16;
+    CARD16  height B16;
+    INT16   x B16;
+    INT16   y B16;
+    INT16   xOff B16;
+    INT16   yOff B16;
+} xGlyphInfo;
+
+#define sz_xGlyphInfo  12
+
+typedef struct {
+    CARD8   len;
+    CARD8   pad1;
+    CARD16  pad2;
+    INT16   deltax;
+    INT16   deltay;
+} xGlyphElt;
+
+#define sz_xGlyphElt   8
+
+typedef struct {
+    Fixed   l, r, y;
+} xSpanFix;
+
+#define sz_xSpanFix    12
+
+typedef struct {
+    xSpanFix   top, bot;
+} xTrap;
+
+#define sz_xTrap       24
+
+/*
+ * requests and replies
+ */
+typedef struct {
+    CARD8   reqType;
+    CARD8   renderReqType;
+    CARD16  length B16;
+    CARD32  majorVersion B32;
+    CARD32  minorVersion B32;
+} xRenderQueryVersionReq;
+
+#define sz_xRenderQueryVersionReq   12
+
+typedef struct {
+    BYTE    type;   /* X_Reply */
+    BYTE    pad1;
+    CARD16  sequenceNumber B16;
+    CARD32  length B32;
+    CARD32  majorVersion B32;
+    CARD32  minorVersion B32;
+    CARD32  pad2 B32;
+    CARD32  pad3 B32;
+    CARD32  pad4 B32;
+    CARD32  pad5 B32;
+} xRenderQueryVersionReply;
+
+#define sz_xRenderQueryVersionReply    32
+
+typedef struct {
+    CARD8   reqType;
+    CARD8   renderReqType;
+    CARD16  length B16;
+} xRenderQueryPictFormatsReq;
+
+#define sz_xRenderQueryPictFormatsReq  4
+
+typedef struct {
+    BYTE    type;   /* X_Reply */
+    BYTE    pad1;
+    CARD16  sequenceNumber B16;
+    CARD32  length B32;
+    CARD32  numFormats B32;
+    CARD32  numScreens B32;
+    CARD32  numDepths B32;
+    CARD32  numVisuals B32;
+    CARD32  numSubpixel B32;       /* Version 0.6 */
+    CARD32  pad5 B32;
+} xRenderQueryPictFormatsReply;
+
+#define sz_xRenderQueryPictFormatsReply        32
+
+typedef struct {
+    CARD8   reqType;
+    CARD8   renderReqType;
+    CARD16  length B16;
+    PictFormat format B32;
+} xRenderQueryPictIndexValuesReq;
+
+#define sz_xRenderQueryPictIndexValuesReq   8
+
+typedef struct {
+    BYTE    type;   /* X_Reply */
+    BYTE    pad1;
+    CARD16  sequenceNumber B16;
+    CARD32  length B32;
+    CARD32  numIndexValues;
+    CARD32  pad2 B32;
+    CARD32  pad3 B32;
+    CARD32  pad4 B32;
+    CARD32  pad5 B32;
+    CARD32  pad6 B32;
+} xRenderQueryPictIndexValuesReply;
+
+#define sz_xRenderQueryPictIndexValuesReply 32
+
+typedef struct {
+    CARD8      reqType;
+    CARD8      renderReqType;
+    CARD16     length B16;
+    Picture    pid B32;
+    Drawable   drawable B32;
+    PictFormat format B32;
+    CARD32     mask B32;
+} xRenderCreatePictureReq;
+
+#define sz_xRenderCreatePictureReq         20
+
+typedef struct {
+    CARD8      reqType;
+    CARD8      renderReqType;
+    CARD16     length B16;
+    Picture    picture B32;
+    CARD32     mask B32;
+} xRenderChangePictureReq;
+
+#define sz_xRenderChangePictureReq         12
+
+typedef struct {
+    CARD8       reqType;
+    CARD8       renderReqType;
+    CARD16      length B16;
+    Picture     picture B32;
+    INT16      xOrigin B16;
+    INT16      yOrigin B16;
+} xRenderSetPictureClipRectanglesReq;
+
+#define sz_xRenderSetPictureClipRectanglesReq      12
+
+typedef struct {
+    CARD8       reqType;
+    CARD8       renderReqType;
+    CARD16      length B16;
+    Picture     picture B32;
+} xRenderFreePictureReq;
+
+#define sz_xRenderFreePictureReq           8
+
+typedef struct {
+    CARD8       reqType;
+    CARD8       renderReqType;
+    CARD16      length B16;
+    CARD8      op;
+    CARD8      pad1;
+    CARD16     pad2 B16;
+    Picture    src B32;
+    Picture    mask B32;
+    Picture    dst B32;
+    INT16      xSrc B16;
+    INT16      ySrc B16;
+    INT16      xMask B16;
+    INT16      yMask B16;
+    INT16      xDst B16;
+    INT16      yDst B16;
+    CARD16     width B16;
+    CARD16     height B16;
+} xRenderCompositeReq;
+
+#define sz_xRenderCompositeReq             36
+
+typedef struct {
+    CARD8       reqType;
+    CARD8       renderReqType;
+    CARD16      length B16;
+    Picture    src B32;
+    Picture    dst B32;
+    CARD32     colorScale B32;
+    CARD32     alphaScale B32;
+    INT16      xSrc B16;
+    INT16      ySrc B16;
+    INT16      xDst B16;
+    INT16      yDst B16;
+    CARD16     width B16;
+    CARD16     height B16;
+} xRenderScaleReq;
+
+#define sz_xRenderScaleReq                         32
+
+typedef struct {
+    CARD8       reqType;
+    CARD8       renderReqType;
+    CARD16      length B16;
+    CARD8      op;
+    CARD8      pad1;
+    CARD16     pad2 B16;
+    Picture    src B32;
+    Picture    dst B32;
+    PictFormat maskFormat B32;
+    INT16      xSrc B16;
+    INT16      ySrc B16;
+} xRenderTrapezoidsReq;
+
+#define sz_xRenderTrapezoidsReq                            24
+
+typedef struct {
+    CARD8       reqType;
+    CARD8       renderReqType;
+    CARD16      length B16;
+    CARD8      op;
+    CARD8      pad1;
+    CARD16     pad2 B16;
+    Picture    src B32;
+    Picture    dst B32;
+    PictFormat maskFormat B32;
+    INT16      xSrc B16;
+    INT16      ySrc B16;
+} xRenderTrianglesReq;
+
+#define sz_xRenderTrianglesReq                     24
+
+typedef struct {
+    CARD8       reqType;
+    CARD8       renderReqType;
+    CARD16      length B16;
+    CARD8      op;
+    CARD8      pad1;
+    CARD16     pad2 B16;
+    Picture    src B32;
+    Picture    dst B32;
+    PictFormat maskFormat B32;
+    INT16      xSrc B16;
+    INT16      ySrc B16;
+} xRenderTriStripReq;
+
+#define sz_xRenderTriStripReq                      24
+
+typedef struct {
+    CARD8       reqType;
+    CARD8       renderReqType;
+    CARD16      length B16;
+    CARD8      op;
+    CARD8      pad1;
+    CARD16     pad2 B16;
+    Picture    src B32;
+    Picture    dst B32;
+    PictFormat maskFormat B32;
+    INT16      xSrc B16;
+    INT16      ySrc B16;
+} xRenderTriFanReq;
+
+#define sz_xRenderTriFanReq                        24
+
+typedef struct {
+    CARD8       reqType;
+    CARD8       renderReqType;
+    CARD16      length B16;
+    Glyphset   gsid B32;
+    PictFormat format B32;
+} xRenderCreateGlyphSetReq;
+
+#define sz_xRenderCreateGlyphSetReq                12
+
+typedef struct {
+    CARD8       reqType;
+    CARD8       renderReqType;
+    CARD16      length B16;
+    Glyphset    gsid B32;
+    Glyphset    existing B32;
+} xRenderReferenceGlyphSetReq;
+
+#define sz_xRenderReferenceGlyphSetReq             24
+
+typedef struct {
+    CARD8       reqType;
+    CARD8       renderReqType;
+    CARD16      length B16;
+    Glyphset    glyphset B32;
+} xRenderFreeGlyphSetReq;
+
+#define sz_xRenderFreeGlyphSetReq                  8
+
+typedef struct {
+    CARD8       reqType;
+    CARD8       renderReqType;
+    CARD16      length B16;
+    Glyphset    glyphset B32;
+    CARD32     nglyphs;
+} xRenderAddGlyphsReq;
+
+#define sz_xRenderAddGlyphsReq                     12
+
+typedef struct {
+    CARD8       reqType;
+    CARD8       renderReqType;
+    CARD16      length B16;
+    Glyphset    glyphset B32;
+} xRenderFreeGlyphsReq;
+
+#define sz_xRenderFreeGlyphsReq                            8
+
+typedef struct {
+    CARD8       reqType;
+    CARD8       renderReqType;
+    CARD16      length B16;
+    CARD8      op;
+    CARD8      pad1;
+    CARD16     pad2 B16;
+    Picture    src B32;
+    Picture    dst B32;
+    PictFormat maskFormat B32;
+    Glyphset    glyphset B32;
+    INT16      xSrc B16;
+    INT16      ySrc B16;
+} xRenderCompositeGlyphsReq, xRenderCompositeGlyphs8Req,
+xRenderCompositeGlyphs16Req, xRenderCompositeGlyphs32Req;
+
+#define sz_xRenderCompositeGlyphs8Req              28
+#define sz_xRenderCompositeGlyphs16Req             28
+#define sz_xRenderCompositeGlyphs32Req             28
+
+/* 0.1 and higher */
+
+typedef struct {
+    CARD8      reqType;
+    CARD8       renderReqType;
+    CARD16      length B16;
+    CARD8      op;
+    CARD8      pad1;
+    CARD16     pad2 B16;
+    Picture    dst B32;
+    xRenderColor    color;
+} xRenderFillRectanglesReq;
+
+#define sz_xRenderFillRectanglesReq                20
+
+/* 0.5 and higher */
+
+typedef struct {
+    CARD8      reqType;
+    CARD8      renderReqType;
+    CARD16     length B16;
+    Cursor     cid B32;
+    Picture    src B32;
+    CARD16     x B16;
+    CARD16     y B16;
+} xRenderCreateCursorReq;
+
+#define sz_xRenderCreateCursorReq                  16
+
+/* 0.6 and higher */
+
+/*
+ * This can't use an array because 32-bit values may be in bitfields
+ */
+typedef struct {
+    Fixed      matrix11 B32;
+    Fixed      matrix12 B32;
+    Fixed      matrix13 B32;
+    Fixed      matrix21 B32;
+    Fixed      matrix22 B32;
+    Fixed      matrix23 B32;
+    Fixed      matrix31 B32;
+    Fixed      matrix32 B32;
+    Fixed      matrix33 B32;
+} xRenderTransform;
+
+#define sz_xRenderTransform 36
+
+typedef struct {
+    CARD8              reqType;
+    CARD8              renderReqType;
+    CARD16             length B16;
+    Picture            picture B32;
+    xRenderTransform   transform;
+} xRenderSetPictureTransformReq;
+
+#define sz_xRenderSetPictureTransformReq           44
+
+typedef struct {
+    CARD8              reqType;
+    CARD8              renderReqType;
+    CARD16             length B16;
+    Drawable           drawable B32;
+} xRenderQueryFiltersReq;
+
+#define sz_xRenderQueryFiltersReq                  8
+
+typedef struct {
+    BYTE    type;   /* X_Reply */
+    BYTE    pad1;
+    CARD16  sequenceNumber B16;
+    CARD32  length B32;
+    CARD32  numAliases B32;    /* LISTofCARD16 */
+    CARD32  numFilters B32;    /* LISTofSTRING8 */
+    CARD32  pad2 B32;
+    CARD32  pad3 B32;
+    CARD32  pad4 B32;
+    CARD32  pad5 B32;
+} xRenderQueryFiltersReply;
+
+#define sz_xRenderQueryFiltersReply                32
+
+typedef struct {
+    CARD8              reqType;
+    CARD8              renderReqType;
+    CARD16             length B16;
+    Picture            picture B32;
+    CARD16             nbytes B16; /* number of bytes in name */
+    CARD16             pad B16;
+} xRenderSetPictureFilterReq;
+
+#define sz_xRenderSetPictureFilterReq              12
+
+/* 0.8 and higher */
+
+typedef struct {
+    Cursor             cursor B32;
+    CARD32             delay B32;
+} xAnimCursorElt;
+
+#define sz_xAnimCursorElt                          8
+
+typedef struct {
+    CARD8              reqType;
+    CARD8              renderReqType;
+    CARD16             length B16;
+    Cursor             cid B32;
+} xRenderCreateAnimCursorReq;
+
+#define sz_xRenderCreateAnimCursorReq              8
+
+/* 0.9 and higher */
+
+typedef struct {
+    CARD8              reqType;
+    CARD8              renderReqType;
+    CARD16             length B16;
+    Picture            picture;
+    INT16              xOff B16;
+    INT16              yOff B16;
+} xRenderAddTrapsReq;
+
+#define sz_xRenderAddTrapsReq                      12
+
+/* 0.10 and higher */
+
+typedef struct {
+    CARD8      reqType;
+    CARD8      renderReqType;
+    CARD16     length B16;
+    Picture    pid B32;
+    xRenderColor color;
+} xRenderCreateSolidFillReq;
+
+#define sz_xRenderCreateSolidFillReq                 16
+
+typedef struct {
+    CARD8      reqType;
+    CARD8      renderReqType;
+    CARD16     length B16;
+    Picture    pid B32;
+    xPointFixed p1;
+    xPointFixed p2;
+    CARD32      nStops;
+} xRenderCreateLinearGradientReq;
+
+#define sz_xRenderCreateLinearGradientReq                 28
+
+typedef struct {
+    CARD8      reqType;
+    CARD8      renderReqType;
+    CARD16     length B16;
+    Picture    pid B32;
+    xPointFixed inner;
+    xPointFixed outer;
+    Fixed       inner_radius;
+    Fixed       outer_radius;
+    CARD32      nStops;
+} xRenderCreateRadialGradientReq;
+
+#define sz_xRenderCreateRadialGradientReq                 36
+
+typedef struct {
+    CARD8      reqType;
+    CARD8      renderReqType;
+    CARD16     length B16;
+    Picture    pid B32;
+    xPointFixed center;
+    Fixed       angle; /* in degrees */
+    CARD32      nStops;
+} xRenderCreateConicalGradientReq;
+
+#define sz_xRenderCreateConicalGradientReq                 24
+
+#undef Window
+#undef Drawable
+#undef Font
+#undef Pixmap
+#undef Cursor
+#undef Colormap
+#undef GContext
+#undef Atom
+#undef VisualID
+#undef Time
+#undef KeyCode
+#undef KeySym
+
+#undef Picture
+#undef PictFormat
+#undef Fixed
+#undef Glyphset
+#undef Glyph
+
+
+#endif /* CAIRO_HAS_XLIB_XRENDER_SURFACE */
+
+#endif /* CAIRO_XLIB_XRENDER_PRIVATE_H */
diff --git a/src/cairo-xlib-xrender.h b/src/cairo-xlib-xrender.h
new file mode 100644 (file)
index 0000000..b34b057
--- /dev/null
@@ -0,0 +1,66 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *     Carl D. Worth <cworth@cworth.org>
+ */
+
+#ifndef CAIRO_XLIB_XRENDER_H
+#define CAIRO_XLIB_XRENDER_H
+
+#include "cairo.h"
+
+#if CAIRO_HAS_XLIB_XRENDER_SURFACE
+
+#include <X11/Xlib.h>
+#include <X11/extensions/Xrender.h>
+
+CAIRO_BEGIN_DECLS
+
+cairo_public cairo_surface_t *
+cairo_xlib_surface_create_with_xrender_format (Display          *dpy,
+                                               Drawable                  drawable,
+                                              Screen            *screen,
+                                               XRenderPictFormat *format,
+                                               int               width,
+                                               int               height);
+
+cairo_public XRenderPictFormat *
+cairo_xlib_surface_get_xrender_format (cairo_surface_t *surface);
+
+CAIRO_END_DECLS
+
+#else  /* CAIRO_HAS_XLIB_XRENDER_SURFACE */
+# error Cairo was not compiled with support for the xlib XRender backend
+#endif /* CAIRO_HAS_XLIB_XRENDER_SURFACE */
+
+#endif /* CAIRO_XLIB_XRENDER_H */
diff --git a/src/cairo-xlib.h b/src/cairo-xlib.h
new file mode 100644 (file)
index 0000000..7f770b9
--- /dev/null
@@ -0,0 +1,113 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *     Carl D. Worth <cworth@cworth.org>
+ */
+
+#ifndef CAIRO_XLIB_H
+#define CAIRO_XLIB_H
+
+#include "cairo.h"
+
+#if CAIRO_HAS_XLIB_SURFACE
+
+#include <X11/Xlib.h>
+
+CAIRO_BEGIN_DECLS
+
+cairo_public cairo_surface_t *
+cairo_xlib_surface_create (Display     *dpy,
+                          Drawable     drawable,
+                          Visual      *visual,
+                          int          width,
+                          int          height);
+
+cairo_public cairo_surface_t *
+cairo_xlib_surface_create_for_bitmap (Display  *dpy,
+                                     Pixmap    bitmap,
+                                     Screen    *screen,
+                                     int       width,
+                                     int       height);
+
+cairo_public void
+cairo_xlib_surface_set_size (cairo_surface_t *surface,
+                            int              width,
+                            int              height);
+
+cairo_public void
+cairo_xlib_surface_set_drawable (cairo_surface_t *surface,
+                                Drawable         drawable,
+                                int              width,
+                                int              height);
+
+cairo_public Display *
+cairo_xlib_surface_get_display (cairo_surface_t *surface);
+
+cairo_public Drawable
+cairo_xlib_surface_get_drawable (cairo_surface_t *surface);
+
+cairo_public Screen *
+cairo_xlib_surface_get_screen (cairo_surface_t *surface);
+
+cairo_public Visual *
+cairo_xlib_surface_get_visual (cairo_surface_t *surface);
+
+cairo_public int
+cairo_xlib_surface_get_depth (cairo_surface_t *surface);
+
+cairo_public int
+cairo_xlib_surface_get_width (cairo_surface_t *surface);
+
+cairo_public int
+cairo_xlib_surface_get_height (cairo_surface_t *surface);
+
+/* debug interface */
+
+/*
+ * @precision: -1 implies automatically choose based on antialiasing mode,
+ *            any other value overrides and sets the corresponding PolyMode.
+ */
+cairo_public void
+cairo_xlib_device_debug_set_precision (cairo_device_t *device,
+                                      int precision);
+
+cairo_public int
+cairo_xlib_device_debug_get_precision (cairo_device_t *device);
+
+CAIRO_END_DECLS
+
+#else  /* CAIRO_HAS_XLIB_SURFACE */
+# error Cairo was not compiled with support for the xlib backend
+#endif /* CAIRO_HAS_XLIB_SURFACE */
+
+#endif /* CAIRO_XLIB_H */
diff --git a/src/cairo-xml-surface.c b/src/cairo-xml-surface.c
new file mode 100644 (file)
index 0000000..40b6160
--- /dev/null
@@ -0,0 +1,1157 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Chris Wilson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Chris Wilson.
+ *
+ * Contributor(s):
+ *      Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+/* This surface is intended to produce a verbose, hierarchical, DAG XML file
+ * representing a single surface. It is intended to be used by debuggers,
+ * such as cairo-sphinx, or by application test-suites that what a log of
+ * operations.
+ */
+
+#include "cairoint.h"
+
+#include "cairo-xml.h"
+
+#include "cairo-clip-private.h"
+#include "cairo-device-private.h"
+#include "cairo-default-context-private.h"
+#include "cairo-image-surface-private.h"
+#include "cairo-error-private.h"
+#include "cairo-output-stream-private.h"
+#include "cairo-recording-surface-private.h"
+
+#define static cairo_warn static
+
+typedef struct _cairo_xml_surface cairo_xml_surface_t;
+
+typedef struct _cairo_xml {
+    cairo_device_t base;
+
+    cairo_output_stream_t *stream;
+    int indent;
+} cairo_xml_t;
+
+struct _cairo_xml_surface {
+    cairo_surface_t base;
+
+    double width, height;
+};
+
+slim_hidden_proto (cairo_xml_for_recording_surface);
+
+static const cairo_surface_backend_t _cairo_xml_surface_backend;
+
+static const char *
+_operator_to_string (cairo_operator_t op)
+{
+    static const char *names[] = {
+       "CLEAR",        /* CAIRO_OPERATOR_CLEAR */
+
+       "SOURCE",       /* CAIRO_OPERATOR_SOURCE */
+       "OVER",         /* CAIRO_OPERATOR_OVER */
+       "IN",           /* CAIRO_OPERATOR_IN */
+       "OUT",          /* CAIRO_OPERATOR_OUT */
+       "ATOP",         /* CAIRO_OPERATOR_ATOP */
+
+       "DEST",         /* CAIRO_OPERATOR_DEST */
+       "DEST_OVER",    /* CAIRO_OPERATOR_DEST_OVER */
+       "DEST_IN",      /* CAIRO_OPERATOR_DEST_IN */
+       "DEST_OUT",     /* CAIRO_OPERATOR_DEST_OUT */
+       "DEST_ATOP",    /* CAIRO_OPERATOR_DEST_ATOP */
+
+       "XOR",          /* CAIRO_OPERATOR_XOR */
+       "ADD",          /* CAIRO_OPERATOR_ADD */
+       "SATURATE",     /* CAIRO_OPERATOR_SATURATE */
+
+       "MULTIPLY",     /* CAIRO_OPERATOR_MULTIPLY */
+       "SCREEN",       /* CAIRO_OPERATOR_SCREEN */
+       "OVERLAY",      /* CAIRO_OPERATOR_OVERLAY */
+       "DARKEN",       /* CAIRO_OPERATOR_DARKEN */
+       "LIGHTEN",      /* CAIRO_OPERATOR_LIGHTEN */
+       "DODGE",        /* CAIRO_OPERATOR_COLOR_DODGE */
+       "BURN",         /* CAIRO_OPERATOR_COLOR_BURN */
+       "HARD_LIGHT",   /* CAIRO_OPERATOR_HARD_LIGHT */
+       "SOFT_LIGHT",   /* CAIRO_OPERATOR_SOFT_LIGHT */
+       "DIFFERENCE",   /* CAIRO_OPERATOR_DIFFERENCE */
+       "EXCLUSION",    /* CAIRO_OPERATOR_EXCLUSION */
+       "HSL_HUE",      /* CAIRO_OPERATOR_HSL_HUE */
+       "HSL_SATURATION", /* CAIRO_OPERATOR_HSL_SATURATION */
+       "HSL_COLOR",    /* CAIRO_OPERATOR_HSL_COLOR */
+       "HSL_LUMINOSITY" /* CAIRO_OPERATOR_HSL_LUMINOSITY */
+    };
+    assert (op < ARRAY_LENGTH (names));
+    return names[op];
+}
+
+static const char *
+_extend_to_string (cairo_extend_t extend)
+{
+    static const char *names[] = {
+       "EXTEND_NONE",          /* CAIRO_EXTEND_NONE */
+       "EXTEND_REPEAT",        /* CAIRO_EXTEND_REPEAT */
+       "EXTEND_REFLECT",       /* CAIRO_EXTEND_REFLECT */
+       "EXTEND_PAD"            /* CAIRO_EXTEND_PAD */
+    };
+    assert (extend < ARRAY_LENGTH (names));
+    return names[extend];
+}
+
+static const char *
+_filter_to_string (cairo_filter_t filter)
+{
+    static const char *names[] = {
+       "FILTER_FAST",          /* CAIRO_FILTER_FAST */
+       "FILTER_GOOD",          /* CAIRO_FILTER_GOOD */
+       "FILTER_BEST",          /* CAIRO_FILTER_BEST */
+       "FILTER_NEAREST",       /* CAIRO_FILTER_NEAREST */
+       "FILTER_BILINEAR",      /* CAIRO_FILTER_BILINEAR */
+       "FILTER_GAUSSIAN",      /* CAIRO_FILTER_GAUSSIAN */
+    };
+    assert (filter < ARRAY_LENGTH (names));
+    return names[filter];
+}
+
+static const char *
+_fill_rule_to_string (cairo_fill_rule_t rule)
+{
+    static const char *names[] = {
+       "WINDING",      /* CAIRO_FILL_RULE_WINDING */
+       "EVEN_ODD"      /* CAIRO_FILL_RILE_EVEN_ODD */
+    };
+    assert (rule < ARRAY_LENGTH (names));
+    return names[rule];
+}
+
+static const char *
+_antialias_to_string (cairo_antialias_t antialias)
+{
+    static const char *names[] = {
+       "DEFAULT",      /* CAIRO_ANTIALIAS_DEFAULT */
+       "NONE",         /* CAIRO_ANTIALIAS_NONE */
+       "GRAY",         /* CAIRO_ANTIALIAS_GRAY */
+       "SUBPIXEL",     /* CAIRO_ANTIALIAS_SUBPIXEL */
+       "FAST",         /* CAIRO_ANTIALIAS_FAST */
+       "GOOD",         /* CAIRO_ANTIALIAS_GOOD */
+       "BEST",         /* CAIRO_ANTIALIAS_BEST */
+    };
+    assert (antialias < ARRAY_LENGTH (names));
+    return names[antialias];
+}
+
+static const char *
+_line_cap_to_string (cairo_line_cap_t line_cap)
+{
+    static const char *names[] = {
+       "LINE_CAP_BUTT",        /* CAIRO_LINE_CAP_BUTT */
+       "LINE_CAP_ROUND",       /* CAIRO_LINE_CAP_ROUND */
+       "LINE_CAP_SQUARE"       /* CAIRO_LINE_CAP_SQUARE */
+    };
+    assert (line_cap < ARRAY_LENGTH (names));
+    return names[line_cap];
+}
+
+static const char *
+_line_join_to_string (cairo_line_join_t line_join)
+{
+    static const char *names[] = {
+       "LINE_JOIN_MITER",      /* CAIRO_LINE_JOIN_MITER */
+       "LINE_JOIN_ROUND",      /* CAIRO_LINE_JOIN_ROUND */
+       "LINE_JOIN_BEVEL",      /* CAIRO_LINE_JOIN_BEVEL */
+    };
+    assert (line_join < ARRAY_LENGTH (names));
+    return names[line_join];
+}
+
+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";
+    }
+}
+
+static const char *
+_format_to_string (cairo_format_t format)
+{
+    switch (format) {
+    case CAIRO_FORMAT_ARGB32:  return "ARGB32";
+    case CAIRO_FORMAT_RGB30:   return "RGB30";
+    case CAIRO_FORMAT_RGB24:   return "RGB24";
+    case CAIRO_FORMAT_RGB16_565:   return "RGB16_565";
+    case CAIRO_FORMAT_A8:      return "A8";
+    case CAIRO_FORMAT_A1:      return "A1";
+    case CAIRO_FORMAT_INVALID: return "INVALID";
+    }
+    ASSERT_NOT_REACHED;
+    return "INVALID";
+}
+
+static cairo_status_t
+_device_flush (void *abstract_device)
+{
+    cairo_xml_t *xml = abstract_device;
+    cairo_status_t status;
+
+    status = _cairo_output_stream_flush (xml->stream);
+
+    return status;
+}
+
+static void
+_device_destroy (void *abstract_device)
+{
+    cairo_xml_t *xml = abstract_device;
+    cairo_status_t status;
+
+    status = _cairo_output_stream_destroy (xml->stream);
+
+    free (xml);
+}
+
+static const cairo_device_backend_t _cairo_xml_device_backend = {
+    CAIRO_DEVICE_TYPE_XML,
+
+    NULL, NULL, /* lock, unlock */
+
+    _device_flush,
+    NULL,  /* finish */
+    _device_destroy
+};
+
+static cairo_device_t *
+_cairo_xml_create_internal (cairo_output_stream_t *stream)
+{
+    cairo_xml_t *xml;
+
+    xml = malloc (sizeof (cairo_xml_t));
+    if (unlikely (xml == NULL))
+       return _cairo_device_create_in_error (CAIRO_STATUS_NO_MEMORY);
+
+    memset (xml, 0, sizeof (cairo_xml_t));
+
+    _cairo_device_init (&xml->base, &_cairo_xml_device_backend);
+
+    xml->indent = 0;
+    xml->stream = stream;
+
+    return &xml->base;
+}
+
+static void
+_cairo_xml_indent (cairo_xml_t *xml, int indent)
+{
+    xml->indent += indent;
+    assert (xml->indent >= 0);
+}
+
+static void CAIRO_PRINTF_FORMAT (2, 3)
+_cairo_xml_printf (cairo_xml_t *xml, const char *fmt, ...)
+{
+    va_list ap;
+    char indent[80];
+    int len;
+
+    len = MIN (xml->indent, ARRAY_LENGTH (indent));
+    memset (indent, ' ', len);
+    _cairo_output_stream_write (xml->stream, indent, len);
+
+    va_start (ap, fmt);
+    _cairo_output_stream_vprintf (xml->stream, fmt, ap);
+    va_end (ap);
+
+    _cairo_output_stream_write (xml->stream, "\n", 1);
+}
+
+static void CAIRO_PRINTF_FORMAT (2, 3)
+_cairo_xml_printf_start (cairo_xml_t *xml, const char *fmt, ...)
+{
+    char indent[80];
+    int len;
+
+    len = MIN (xml->indent, ARRAY_LENGTH (indent));
+    memset (indent, ' ', len);
+    _cairo_output_stream_write (xml->stream, indent, len);
+
+    if (fmt != NULL) {
+       va_list ap;
+
+       va_start (ap, fmt);
+       _cairo_output_stream_vprintf (xml->stream, fmt, ap);
+       va_end (ap);
+    }
+}
+
+static void CAIRO_PRINTF_FORMAT (2, 3)
+_cairo_xml_printf_continue (cairo_xml_t *xml, const char *fmt, ...)
+{
+    va_list ap;
+
+    va_start (ap, fmt);
+    _cairo_output_stream_vprintf (xml->stream, fmt, ap);
+    va_end (ap);
+}
+
+static void CAIRO_PRINTF_FORMAT (2, 3)
+_cairo_xml_printf_end (cairo_xml_t *xml, const char *fmt, ...)
+{
+    if (fmt != NULL) {
+       va_list ap;
+
+       va_start (ap, fmt);
+       _cairo_output_stream_vprintf (xml->stream, fmt, ap);
+       va_end (ap);
+    }
+
+    _cairo_output_stream_write (xml->stream, "\n", 1);
+}
+
+static cairo_surface_t *
+_cairo_xml_surface_create_similar (void                        *abstract_surface,
+                                  cairo_content_t       content,
+                                  int                   width,
+                                  int                   height)
+{
+    cairo_rectangle_t extents;
+
+    extents.x = extents.y = 0;
+    extents.width  = width;
+    extents.height = height;
+
+    return cairo_recording_surface_create (content, &extents);
+}
+
+static cairo_bool_t
+_cairo_xml_surface_get_extents (void *abstract_surface,
+                               cairo_rectangle_int_t *rectangle)
+{
+    cairo_xml_surface_t *surface = abstract_surface;
+
+    if (surface->width < 0 || surface->height < 0)
+       return FALSE;
+
+    rectangle->x = 0;
+    rectangle->y = 0;
+    rectangle->width  = surface->width;
+    rectangle->height = surface->height;
+
+    return TRUE;
+}
+
+static cairo_status_t
+_cairo_xml_move_to (void *closure,
+                   const cairo_point_t *p1)
+{
+    _cairo_xml_printf_continue (closure, " %f %f m",
+                               _cairo_fixed_to_double (p1->x),
+                               _cairo_fixed_to_double (p1->y));
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_xml_line_to (void *closure,
+                   const cairo_point_t *p1)
+{
+    _cairo_xml_printf_continue (closure, " %f %f l",
+                               _cairo_fixed_to_double (p1->x),
+                               _cairo_fixed_to_double (p1->y));
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_xml_curve_to (void *closure,
+                    const cairo_point_t *p1,
+                    const cairo_point_t *p2,
+                    const cairo_point_t *p3)
+{
+    _cairo_xml_printf_continue (closure, " %f %f %f %f %f %f c",
+                               _cairo_fixed_to_double (p1->x),
+                               _cairo_fixed_to_double (p1->y),
+                               _cairo_fixed_to_double (p2->x),
+                               _cairo_fixed_to_double (p2->y),
+                               _cairo_fixed_to_double (p3->x),
+                               _cairo_fixed_to_double (p3->y));
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_xml_close_path (void *closure)
+{
+    _cairo_xml_printf_continue (closure, " h");
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_xml_emit_path (cairo_xml_t *xml,
+                     const cairo_path_fixed_t *path)
+{
+    cairo_status_t status;
+
+    _cairo_xml_printf_start (xml, "<path>");
+    status = _cairo_path_fixed_interpret (path,
+                                       _cairo_xml_move_to,
+                                       _cairo_xml_line_to,
+                                       _cairo_xml_curve_to,
+                                       _cairo_xml_close_path,
+                                       xml);
+    assert (status == CAIRO_STATUS_SUCCESS);
+    _cairo_xml_printf_end (xml, "</path>");
+}
+
+static void
+_cairo_xml_emit_string (cairo_xml_t *xml,
+                       const char *node,
+                       const char *data)
+{
+    _cairo_xml_printf (xml, "<%s>%s</%s>", node, data, node);
+}
+
+static void
+_cairo_xml_emit_double (cairo_xml_t *xml,
+                       const char *node,
+                       double data)
+{
+    _cairo_xml_printf (xml, "<%s>%f</%s>", node, data, node);
+}
+
+static cairo_xml_t *
+to_xml (cairo_xml_surface_t *surface)
+{
+    return (cairo_xml_t *) surface->base.device;
+}
+
+static cairo_status_t
+_cairo_xml_surface_emit_clip_path (cairo_xml_surface_t *surface,
+                                  cairo_clip_path_t *clip_path)
+{
+    cairo_box_t box;
+    cairo_status_t status;
+    cairo_xml_t *xml;
+
+    if (clip_path->prev != NULL) {
+       status = _cairo_xml_surface_emit_clip_path (surface, clip_path->prev);
+       if (unlikely (status))
+           return status;
+    }
+
+
+    /* skip the trivial clip covering the surface extents */
+    if (surface->width >= 0 && surface->height >= 0 &&
+       _cairo_path_fixed_is_box (&clip_path->path, &box))
+    {
+       if (box.p1.x <= 0 && box.p1.y <= 0 &&
+           box.p2.x - box.p1.x >= _cairo_fixed_from_double (surface->width) &&
+           box.p2.y - box.p1.y >= _cairo_fixed_from_double (surface->height))
+       {
+           return CAIRO_STATUS_SUCCESS;
+       }
+    }
+
+    xml = to_xml (surface);
+
+    _cairo_xml_printf_start (xml, "<clip>");
+    _cairo_xml_indent (xml, 2);
+
+    _cairo_xml_emit_path (xml, &clip_path->path);
+    _cairo_xml_emit_double (xml, "tolerance", clip_path->tolerance);
+    _cairo_xml_emit_string (xml, "antialias",
+                           _antialias_to_string (clip_path->antialias));
+    _cairo_xml_emit_string (xml, "fill-rule",
+                           _fill_rule_to_string (clip_path->fill_rule));
+
+    _cairo_xml_indent (xml, -2);
+    _cairo_xml_printf_end (xml, "</clip>");
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_xml_surface_emit_clip (cairo_xml_surface_t *surface,
+                             const cairo_clip_t *clip)
+{
+    if (clip == NULL)
+       return CAIRO_STATUS_SUCCESS;
+
+    return _cairo_xml_surface_emit_clip_path (surface, clip->path);
+}
+
+static cairo_status_t
+_cairo_xml_emit_solid (cairo_xml_t *xml,
+                      const cairo_solid_pattern_t *solid)
+{
+    _cairo_xml_printf (xml, "<solid>%f %f %f %f</solid>",
+                      solid->color.red,
+                      solid->color.green,
+                      solid->color.blue,
+                      solid->color.alpha);
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_xml_emit_matrix (cairo_xml_t *xml,
+                       const cairo_matrix_t *matrix)
+{
+    if (! _cairo_matrix_is_identity (matrix)) {
+       _cairo_xml_printf (xml, "<matrix>%f %f %f %f %f %f</matrix>",
+                          matrix->xx, matrix->yx,
+                          matrix->xy, matrix->yy,
+                          matrix->x0, matrix->y0);
+    }
+}
+
+static void
+_cairo_xml_emit_gradient (cairo_xml_t *xml,
+                         const cairo_gradient_pattern_t *gradient)
+{
+    unsigned int i;
+
+    for (i = 0; i < gradient->n_stops; i++) {
+       _cairo_xml_printf (xml,
+                          "<color-stop>%f %f %f %f %f</color-stop>",
+                          gradient->stops[i].offset,
+                          gradient->stops[i].color.red,
+                          gradient->stops[i].color.green,
+                          gradient->stops[i].color.blue,
+                          gradient->stops[i].color.alpha);
+    }
+}
+
+static cairo_status_t
+_cairo_xml_emit_linear (cairo_xml_t *xml,
+                       const cairo_linear_pattern_t *linear)
+{
+    _cairo_xml_printf (xml,
+                      "<linear x1='%f' y1='%f' x2='%f' y2='%f'>",
+                      linear->pd1.x, linear->pd1.y,
+                      linear->pd2.x, linear->pd2.y);
+    _cairo_xml_indent (xml, 2);
+    _cairo_xml_emit_gradient (xml, &linear->base);
+    _cairo_xml_indent (xml, -2);
+    _cairo_xml_printf (xml, "</linear>");
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_xml_emit_radial (cairo_xml_t *xml,
+                       const cairo_radial_pattern_t *radial)
+{
+    _cairo_xml_printf (xml,
+                      "<radial x1='%f' y1='%f' r1='%f' x2='%f' y2='%f' r2='%f'>",
+                      radial->cd1.center.x, radial->cd1.center.y, radial->cd1.radius,
+                      radial->cd2.center.x, radial->cd2.center.y, radial->cd2.radius);
+    _cairo_xml_indent (xml, 2);
+    _cairo_xml_emit_gradient (xml, &radial->base);
+    _cairo_xml_indent (xml, -2);
+    _cairo_xml_printf (xml, "</radial>");
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_write_func (void *closure, const unsigned char *data, unsigned len)
+{
+    _cairo_output_stream_write (closure, data, len);
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_xml_emit_image (cairo_xml_t *xml,
+                      cairo_image_surface_t *image)
+{
+    cairo_output_stream_t *stream;
+    cairo_status_t status;
+
+    _cairo_xml_printf_start (xml,
+                            "<image width='%d' height='%d' format='%s'>",
+                            image->width, image->height,
+                            _format_to_string (image->format));
+
+    stream = _cairo_base64_stream_create (xml->stream);
+    status = cairo_surface_write_to_png_stream (&image->base,
+                                               _write_func, stream);
+    assert (status == CAIRO_STATUS_SUCCESS);
+    status = _cairo_output_stream_destroy (stream);
+    if (unlikely (status))
+       return status;
+
+    _cairo_xml_printf_end (xml, "</image>");
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_xml_emit_surface (cairo_xml_t *xml,
+                        const cairo_surface_pattern_t *pattern)
+{
+    cairo_surface_t *source = pattern->surface;
+    cairo_status_t status;
+
+    if (_cairo_surface_is_recording (source)) {
+       status = cairo_xml_for_recording_surface (&xml->base, source);
+    } else {
+       cairo_image_surface_t *image;
+       void *image_extra;
+
+       status = _cairo_surface_acquire_source_image (source,
+                                                     &image, &image_extra);
+       if (unlikely (status))
+           return status;
+
+       status = _cairo_xml_emit_image (xml, image);
+
+       _cairo_surface_release_source_image (source, image, image_extra);
+    }
+
+    return status;
+}
+
+static cairo_status_t
+_cairo_xml_emit_pattern (cairo_xml_t *xml,
+                        const char *source_or_mask,
+                        const cairo_pattern_t *pattern)
+{
+    cairo_status_t status;
+
+    _cairo_xml_printf (xml, "<%s-pattern>", source_or_mask);
+    _cairo_xml_indent (xml, 2);
+
+    switch (pattern->type) {
+    case CAIRO_PATTERN_TYPE_SOLID:
+       status = _cairo_xml_emit_solid (xml, (cairo_solid_pattern_t *) pattern);
+       break;
+    case CAIRO_PATTERN_TYPE_LINEAR:
+       status = _cairo_xml_emit_linear (xml, (cairo_linear_pattern_t *) pattern);
+       break;
+    case CAIRO_PATTERN_TYPE_RADIAL:
+       status = _cairo_xml_emit_radial (xml, (cairo_radial_pattern_t *) pattern);
+       break;
+    case CAIRO_PATTERN_TYPE_SURFACE:
+       status = _cairo_xml_emit_surface (xml, (cairo_surface_pattern_t *) pattern);
+       break;
+    default:
+       ASSERT_NOT_REACHED;
+       status = CAIRO_INT_STATUS_UNSUPPORTED;
+       break;
+    }
+
+    if (pattern->type != CAIRO_PATTERN_TYPE_SOLID) {
+       _cairo_xml_emit_matrix (xml, &pattern->matrix);
+       _cairo_xml_printf (xml,
+                          "<extend>%s</extend>",
+                          _extend_to_string (pattern->extend));
+       _cairo_xml_printf (xml,
+                          "<filter>%s</filter>",
+                          _filter_to_string (pattern->filter));
+    }
+
+    _cairo_xml_indent (xml, -2);
+    _cairo_xml_printf (xml, "</%s-pattern>", source_or_mask);
+
+    return status;
+}
+
+static cairo_int_status_t
+_cairo_xml_surface_paint (void                 *abstract_surface,
+                         cairo_operator_t       op,
+                         const cairo_pattern_t *source,
+                         const cairo_clip_t    *clip)
+{
+    cairo_xml_surface_t *surface = abstract_surface;
+    cairo_xml_t *xml = to_xml (surface);
+    cairo_status_t status;
+
+    _cairo_xml_printf (xml, "<paint>");
+    _cairo_xml_indent (xml, 2);
+
+    _cairo_xml_emit_string (xml, "operator", _operator_to_string (op));
+
+    status = _cairo_xml_surface_emit_clip (surface, clip);
+    if (unlikely (status))
+       return status;
+
+    status = _cairo_xml_emit_pattern (xml, "source", source);
+    if (unlikely (status))
+       return status;
+
+    _cairo_xml_indent (xml, -2);
+    _cairo_xml_printf (xml, "</paint>");
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_xml_surface_mask (void                  *abstract_surface,
+                        cairo_operator_t        op,
+                        const cairo_pattern_t  *source,
+                        const cairo_pattern_t  *mask,
+                        const cairo_clip_t     *clip)
+{
+    cairo_xml_surface_t *surface = abstract_surface;
+    cairo_xml_t *xml = to_xml (surface);
+    cairo_status_t status;
+
+    _cairo_xml_printf (xml, "<mask>");
+    _cairo_xml_indent (xml, 2);
+
+    _cairo_xml_emit_string (xml, "operator", _operator_to_string (op));
+
+    status = _cairo_xml_surface_emit_clip (surface, clip);
+    if (unlikely (status))
+       return status;
+
+    status = _cairo_xml_emit_pattern (xml, "source", source);
+    if (unlikely (status))
+       return status;
+
+    status = _cairo_xml_emit_pattern (xml, "mask", mask);
+    if (unlikely (status))
+       return status;
+
+    _cairo_xml_indent (xml, -2);
+    _cairo_xml_printf (xml, "</mask>");
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_xml_surface_stroke (void                                *abstract_surface,
+                          cairo_operator_t              op,
+                          const cairo_pattern_t        *source,
+                          const cairo_path_fixed_t             *path,
+                          const cairo_stroke_style_t           *style,
+                          const cairo_matrix_t         *ctm,
+                          const cairo_matrix_t         *ctm_inverse,
+                          double                        tolerance,
+                          cairo_antialias_t             antialias,
+                          const cairo_clip_t           *clip)
+{
+    cairo_xml_surface_t *surface = abstract_surface;
+    cairo_xml_t *xml = to_xml (surface);
+    cairo_status_t status;
+
+    _cairo_xml_printf (xml, "<stroke>");
+    _cairo_xml_indent (xml, 2);
+
+    _cairo_xml_emit_string (xml, "operator", _operator_to_string (op));
+    _cairo_xml_emit_double (xml, "line-width", style->line_width);
+    _cairo_xml_emit_double (xml, "miter-limit", style->miter_limit);
+    _cairo_xml_emit_string (xml, "line-cap", _line_cap_to_string (style->line_cap));
+    _cairo_xml_emit_string (xml, "line-join", _line_join_to_string (style->line_join));
+
+    status = _cairo_xml_surface_emit_clip (surface, clip);
+    if (unlikely (status))
+       return status;
+
+    status = _cairo_xml_emit_pattern (xml, "source", source);
+    if (unlikely (status))
+       return status;
+
+    if (style->num_dashes) {
+       unsigned int i;
+
+       _cairo_xml_printf_start (xml, "<dash offset='%f'>",
+                                style->dash_offset);
+       for (i = 0; i < style->num_dashes; i++)
+           _cairo_xml_printf_continue (xml, "%f ", style->dash[i]);
+
+       _cairo_xml_printf_end (xml, "</dash>");
+    }
+
+    _cairo_xml_emit_path (xml, path);
+    _cairo_xml_emit_double (xml, "tolerance", tolerance);
+    _cairo_xml_emit_string (xml, "antialias", _antialias_to_string (antialias));
+
+    _cairo_xml_emit_matrix (xml, ctm);
+
+    _cairo_xml_indent (xml, -2);
+    _cairo_xml_printf (xml, "</stroke>");
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_xml_surface_fill (void                  *abstract_surface,
+                        cairo_operator_t        op,
+                        const cairo_pattern_t  *source,
+                        const cairo_path_fixed_t*path,
+                        cairo_fill_rule_t       fill_rule,
+                        double                  tolerance,
+                        cairo_antialias_t       antialias,
+                        const cairo_clip_t     *clip)
+{
+    cairo_xml_surface_t *surface = abstract_surface;
+    cairo_xml_t *xml = to_xml (surface);
+    cairo_status_t status;
+
+    _cairo_xml_printf (xml, "<fill>");
+    _cairo_xml_indent (xml, 2);
+
+    _cairo_xml_emit_string (xml, "operator", _operator_to_string (op));
+
+    status = _cairo_xml_surface_emit_clip (surface, clip);
+    if (unlikely (status))
+       return status;
+
+    status = _cairo_xml_emit_pattern (xml, "source", source);
+    if (unlikely (status))
+       return status;
+
+    _cairo_xml_emit_path (xml, path);
+    _cairo_xml_emit_double (xml, "tolerance", tolerance);
+    _cairo_xml_emit_string (xml, "antialias", _antialias_to_string (antialias));
+    _cairo_xml_emit_string (xml, "fill-rule", _fill_rule_to_string (fill_rule));
+
+    _cairo_xml_indent (xml, -2);
+    _cairo_xml_printf (xml, "</fill>");
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+#if CAIRO_HAS_FT_FONT
+#include "cairo-ft-private.h"
+static cairo_status_t
+_cairo_xml_emit_type42_font (cairo_xml_t *xml,
+                            cairo_scaled_font_t *scaled_font)
+{
+    const cairo_scaled_font_backend_t *backend;
+    cairo_output_stream_t *base64_stream;
+    cairo_output_stream_t *zlib_stream;
+    cairo_status_t status, status2;
+    unsigned long size;
+    uint32_t len;
+    uint8_t *buf;
+
+    backend = scaled_font->backend;
+    if (backend->load_truetype_table == NULL)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    size = 0;
+    status = backend->load_truetype_table (scaled_font, 0, 0, NULL, &size);
+    if (unlikely (status))
+       return status;
+
+    buf = malloc (size);
+    if (unlikely (buf == NULL))
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    status = backend->load_truetype_table (scaled_font, 0, 0, buf, &size);
+    if (unlikely (status)) {
+       free (buf);
+       return status;
+    }
+
+    _cairo_xml_printf_start (xml, "<font type='42' flags='%d' index='0'>",
+                      _cairo_ft_scaled_font_get_load_flags (scaled_font));
+
+
+    base64_stream = _cairo_base64_stream_create (xml->stream);
+    len = size;
+    _cairo_output_stream_write (base64_stream, &len, sizeof (len));
+
+    zlib_stream = _cairo_deflate_stream_create (base64_stream);
+
+    _cairo_output_stream_write (zlib_stream, buf, size);
+    free (buf);
+
+    status2 = _cairo_output_stream_destroy (zlib_stream);
+    if (status == CAIRO_STATUS_SUCCESS)
+       status = status2;
+
+    status2 = _cairo_output_stream_destroy (base64_stream);
+    if (status == CAIRO_STATUS_SUCCESS)
+       status = status2;
+
+    _cairo_xml_printf_end (xml, "</font>");
+
+    return status;
+}
+#else
+static cairo_status_t
+_cairo_xml_emit_type42_font (cairo_xml_t *xml,
+                            cairo_scaled_font_t *scaled_font)
+{
+    return CAIRO_INT_STATUS_UNSUPPORTED;
+}
+#endif
+
+static cairo_status_t
+_cairo_xml_emit_type3_font (cairo_xml_t *xml,
+                           cairo_scaled_font_t *scaled_font,
+                           cairo_glyph_t *glyphs,
+                           int num_glyphs)
+{
+    _cairo_xml_printf_start (xml, "<font type='3'>");
+    _cairo_xml_printf_end (xml, "</font>");
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_xml_emit_scaled_font (cairo_xml_t *xml,
+                            cairo_scaled_font_t *scaled_font,
+                            cairo_glyph_t *glyphs,
+                            int num_glyphs)
+{
+    cairo_int_status_t status;
+
+    _cairo_xml_printf (xml, "<scaled-font>");
+    _cairo_xml_indent (xml, 2);
+
+    status = _cairo_xml_emit_type42_font (xml, scaled_font);
+    if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
+       status = _cairo_xml_emit_type3_font (xml, scaled_font,
+                                            glyphs, num_glyphs);
+    }
+
+    _cairo_xml_indent (xml, -2);
+    _cairo_xml_printf (xml, "<scaled-font>");
+
+    return status;
+}
+
+static cairo_int_status_t
+_cairo_xml_surface_glyphs (void                            *abstract_surface,
+                          cairo_operator_t          op,
+                          const cairo_pattern_t    *source,
+                          cairo_glyph_t            *glyphs,
+                          int                       num_glyphs,
+                          cairo_scaled_font_t      *scaled_font,
+                          const cairo_clip_t       *clip,
+                          int                      *remaining_glyphs)
+{
+    cairo_xml_surface_t *surface = abstract_surface;
+    cairo_xml_t *xml = to_xml (surface);
+    cairo_status_t status;
+    int i;
+
+    _cairo_xml_printf (xml, "<glyphs>");
+    _cairo_xml_indent (xml, 2);
+
+    _cairo_xml_emit_string (xml, "operator", _operator_to_string (op));
+
+    status = _cairo_xml_surface_emit_clip (surface, clip);
+    if (unlikely (status))
+       return status;
+
+    status = _cairo_xml_emit_pattern (xml, "source", source);
+    if (unlikely (status))
+       return status;
+
+    status = _cairo_xml_emit_scaled_font (xml, scaled_font, glyphs, num_glyphs);
+    if (unlikely (status))
+       return status;
+
+    for (i = 0; i < num_glyphs; i++) {
+       _cairo_xml_printf (xml, "<glyph index='%lu'>%f %f</glyph>",
+                          glyphs[i].index,
+                          glyphs[i].x,
+                          glyphs[i].y);
+    }
+
+    _cairo_xml_indent (xml, -2);
+    _cairo_xml_printf (xml, "</glyphs>");
+
+    *remaining_glyphs = 0;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static const cairo_surface_backend_t
+_cairo_xml_surface_backend = {
+    CAIRO_SURFACE_TYPE_XML,
+    NULL,
+    _cairo_default_context_create,
+
+    _cairo_xml_surface_create_similar,
+    NULL, /* create_similar_image */
+    NULL, /* map_to_image */
+    NULL, /* unmap_image */
+
+    NULL, NULL, /* source image */
+    NULL, NULL, /* dst image */
+    NULL, /* clone_similar */
+    NULL, /* composite */
+    NULL, /* fill_rectangles */
+    NULL, /* composite_trapezoids */
+    NULL, /* create_span_renderer */
+    NULL, /* check_span_renderer */
+    NULL, NULL, /* copy/show page */
+    _cairo_xml_surface_get_extents,
+    NULL, /* old_show_glyphs */
+    NULL, /* get_font_options */
+    NULL, /* flush */
+    NULL, /* mark_dirty_rectangle */
+    NULL, /* font fini */
+    NULL, /* scaled_glyph_fini */
+
+    /* The 5 high level operations */
+    _cairo_xml_surface_paint,
+    _cairo_xml_surface_mask,
+    _cairo_xml_surface_stroke,
+    _cairo_xml_surface_fill,
+    _cairo_xml_surface_glyphs,
+
+    NULL, /* snapshot */
+
+    NULL, /* is_similar */
+    NULL, /* fill_stroke */
+    NULL, /* create_solid_pattern_surface */
+    NULL, /* can_repaint_solid_pattern_surface */
+
+    /* The alternate high-level text operation */
+    NULL, NULL, /* has, show_text_glyphs */
+};
+
+static cairo_surface_t *
+_cairo_xml_surface_create_internal (cairo_device_t *device,
+                                   cairo_content_t content,
+                                   double width,
+                                   double height)
+{
+    cairo_xml_surface_t *surface;
+
+    surface = malloc (sizeof (cairo_xml_surface_t));
+    if (unlikely (surface == NULL))
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+    _cairo_surface_init (&surface->base,
+                        &_cairo_xml_surface_backend,
+                        device,
+                        content);
+
+    surface->width = width;
+    surface->height = height;
+
+    return &surface->base;
+}
+
+cairo_device_t *
+cairo_xml_create (const char *filename)
+{
+    cairo_output_stream_t *stream;
+    cairo_status_t status;
+
+    stream = _cairo_output_stream_create_for_filename (filename);
+    if ((status = _cairo_output_stream_get_status (stream)))
+       return _cairo_device_create_in_error (status);
+
+    return _cairo_xml_create_internal (stream);
+}
+
+cairo_device_t *
+cairo_xml_create_for_stream (cairo_write_func_t         write_func,
+                            void               *closure)
+{
+    cairo_output_stream_t *stream;
+    cairo_status_t status;
+
+    stream = _cairo_output_stream_create (write_func, NULL, closure);
+    if ((status = _cairo_output_stream_get_status (stream)))
+       return _cairo_device_create_in_error (status);
+
+    return _cairo_xml_create_internal (stream);
+}
+
+cairo_surface_t *
+cairo_xml_surface_create (cairo_device_t *device,
+                         cairo_content_t content,
+                         double width, double height)
+{
+    if (unlikely (device->backend->type != CAIRO_DEVICE_TYPE_XML))
+       return _cairo_surface_create_in_error (CAIRO_STATUS_DEVICE_TYPE_MISMATCH);
+
+    if (unlikely (device->status))
+       return _cairo_surface_create_in_error (device->status);
+
+    return _cairo_xml_surface_create_internal (device, content, width, height);
+}
+
+cairo_status_t
+cairo_xml_for_recording_surface (cairo_device_t         *device,
+                                cairo_surface_t *recording_surface)
+{
+    cairo_box_t bbox;
+    cairo_rectangle_int_t extents;
+    cairo_surface_t *surface;
+    cairo_xml_t *xml;
+    cairo_status_t status;
+
+    if (unlikely (device->status))
+       return device->status;
+
+    if (unlikely (recording_surface->status))
+       return recording_surface->status;
+
+    if (unlikely (device->backend->type != CAIRO_DEVICE_TYPE_XML))
+       return _cairo_error (CAIRO_STATUS_DEVICE_TYPE_MISMATCH);
+
+    if (unlikely (! _cairo_surface_is_recording (recording_surface)))
+       return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+
+    status = _cairo_recording_surface_get_bbox ((cairo_recording_surface_t *) recording_surface,
+                                               &bbox, NULL);
+    if (unlikely (status))
+       return status;
+
+    _cairo_box_round_to_rectangle (&bbox, &extents);
+    surface = _cairo_xml_surface_create_internal (device,
+                                                 recording_surface->content,
+                                                 extents.width,
+                                                 extents.height);
+    if (unlikely (surface->status))
+       return surface->status;
+
+    xml = (cairo_xml_t *) device;
+
+    _cairo_xml_printf (xml,
+                      "<surface content='%s' width='%d' height='%d'>",
+                      _content_to_string (recording_surface->content),
+                      extents.width, extents.height);
+    _cairo_xml_indent (xml, 2);
+
+    cairo_surface_set_device_offset (surface, -extents.x, -extents.y);
+    status = _cairo_recording_surface_replay (recording_surface, surface);
+    cairo_surface_destroy (surface);
+
+    _cairo_xml_indent (xml, -2);
+    _cairo_xml_printf (xml, "</surface>");
+
+    return status;
+}
+slim_hidden_def (cairo_xml_for_recording_surface);
diff --git a/src/cairo-xml.h b/src/cairo-xml.h
new file mode 100644 (file)
index 0000000..9ae76e9
--- /dev/null
@@ -0,0 +1,67 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Chris Wilson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Chris Wilson
+ *
+ * Contributor(s):
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#ifndef CAIRO_XML_H
+#define CAIRO_XML_H
+
+#include "cairo.h"
+
+#if CAIRO_HAS_XML_SURFACE
+
+CAIRO_BEGIN_DECLS
+
+cairo_public cairo_device_t *
+cairo_xml_create (const char *filename);
+
+cairo_public cairo_device_t *
+cairo_xml_create_for_stream (cairo_write_func_t         write_func,
+                            void               *closure);
+
+cairo_public cairo_surface_t *
+cairo_xml_surface_create (cairo_device_t *xml,
+                         cairo_content_t content,
+                         double width, double height);
+
+cairo_public cairo_status_t
+cairo_xml_for_recording_surface (cairo_device_t *xml,
+                                cairo_surface_t *surface);
+
+CAIRO_END_DECLS
+
+#else  /*CAIRO_HAS_XML_SURFACE*/
+# error Cairo was not compiled with support for the XML backend
+#endif /*CAIRO_HAS_XML_SURFACE*/
+
+#endif /*CAIRO_XML_H*/
diff --git a/src/cairo.c b/src/cairo.c
new file mode 100644 (file)
index 0000000..cbc3974
--- /dev/null
@@ -0,0 +1,3849 @@
+/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ * Copyright © 2005 Red Hat, Inc.
+ * Copyright © 2011 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *     Carl D. Worth <cworth@cworth.org>
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+#include "cairo-private.h"
+
+#include "cairo-backend-private.h"
+#include "cairo-error-private.h"
+#include "cairo-path-private.h"
+#include "cairo-pattern-private.h"
+#include "cairo-surface-private.h"
+#include "cairo-surface-backend-private.h"
+
+#include <assert.h>
+
+/**
+ * SECTION:cairo
+ * @Title: cairo_t
+ * @Short_Description: The cairo drawing context
+ * @See_Also: #cairo_surface_t
+ *
+ * #cairo_t is the main object used when drawing with cairo. To
+ * draw with cairo, you create a #cairo_t, set the target surface,
+ * and drawing options for the #cairo_t, create shapes with
+ * functions like cairo_move_to() and cairo_line_to(), and then
+ * draw shapes with cairo_stroke() or cairo_fill().
+ *
+ * #cairo_t<!-- -->'s can be pushed to a stack via cairo_save().
+ * They may then safely be changed, without loosing the current state.
+ * Use cairo_restore() to restore to the saved state.
+ */
+
+/**
+ * SECTION:cairo-text
+ * @Title: text
+ * @Short_Description: Rendering text and glyphs
+ * @See_Also: #cairo_font_face_t, #cairo_scaled_font_t, cairo_text_path(),
+ *            cairo_glyph_path()
+ *
+ * The functions with <emphasis>text</emphasis> in their name form cairo's
+ * <firstterm>toy</firstterm> text API.  The toy API takes UTF-8 encoded
+ * text and is limited in its functionality to rendering simple
+ * left-to-right text with no advanced features.  That means for example
+ * that most complex scripts like Hebrew, Arabic, and Indic scripts are
+ * out of question.  No kerning or correct positioning of diacritical marks
+ * either.  The font selection is pretty limited too and doesn't handle the
+ * case that the selected font does not cover the characters in the text.
+ * This set of functions are really that, a toy text API, for testing and
+ * demonstration purposes.  Any serious application should avoid them.
+ *
+ * The functions with <emphasis>glyphs</emphasis> in their name form cairo's
+ * <firstterm>low-level</firstterm> text API.  The low-level API relies on
+ * the user to convert text to a set of glyph indexes and positions.  This
+ * is a very hard problem and is best handled by external libraries, like
+ * the pangocairo that is part of the Pango text layout and rendering library.
+ * Pango is available from <ulink
+ * url="http://www.pango.org/">http://www.pango.org/</ulink>.
+ */
+
+/**
+ * SECTION:cairo-transforms
+ * @Title: Transformations
+ * @Short_Description: Manipulating the current transformation matrix
+ * @See_Also: #cairo_matrix_t
+ *
+ * The current transformation matrix, <firstterm>ctm</firstterm>, is a
+ * two-dimensional affine transformation that maps all coordinates and other
+ * drawing instruments from the <firstterm>user space</firstterm> into the
+ * surface's canonical coordinate system, also known as the <firstterm>device
+ * space</firstterm>.
+ */
+
+#define DEFINE_NIL_CONTEXT(status)                                     \
+    {                                                                  \
+       CAIRO_REFERENCE_COUNT_INVALID,  /* ref_count */                 \
+       status,                         /* status */                    \
+       { 0, 0, 0, NULL },              /* user_data */                 \
+       NULL                                                            \
+    }
+
+static const cairo_t _cairo_nil[] = {
+    DEFINE_NIL_CONTEXT (CAIRO_STATUS_NO_MEMORY),
+    DEFINE_NIL_CONTEXT (CAIRO_STATUS_INVALID_RESTORE),
+    DEFINE_NIL_CONTEXT (CAIRO_STATUS_INVALID_POP_GROUP),
+    DEFINE_NIL_CONTEXT (CAIRO_STATUS_NO_CURRENT_POINT),
+    DEFINE_NIL_CONTEXT (CAIRO_STATUS_INVALID_MATRIX),
+    DEFINE_NIL_CONTEXT (CAIRO_STATUS_INVALID_STATUS),
+    DEFINE_NIL_CONTEXT (CAIRO_STATUS_NULL_POINTER),
+    DEFINE_NIL_CONTEXT (CAIRO_STATUS_INVALID_STRING),
+    DEFINE_NIL_CONTEXT (CAIRO_STATUS_INVALID_PATH_DATA),
+    DEFINE_NIL_CONTEXT (CAIRO_STATUS_READ_ERROR),
+    DEFINE_NIL_CONTEXT (CAIRO_STATUS_WRITE_ERROR),
+    DEFINE_NIL_CONTEXT (CAIRO_STATUS_SURFACE_FINISHED),
+    DEFINE_NIL_CONTEXT (CAIRO_STATUS_SURFACE_TYPE_MISMATCH),
+    DEFINE_NIL_CONTEXT (CAIRO_STATUS_PATTERN_TYPE_MISMATCH),
+    DEFINE_NIL_CONTEXT (CAIRO_STATUS_INVALID_CONTENT),
+    DEFINE_NIL_CONTEXT (CAIRO_STATUS_INVALID_FORMAT),
+    DEFINE_NIL_CONTEXT (CAIRO_STATUS_INVALID_VISUAL),
+    DEFINE_NIL_CONTEXT (CAIRO_STATUS_FILE_NOT_FOUND),
+    DEFINE_NIL_CONTEXT (CAIRO_STATUS_INVALID_DASH),
+    DEFINE_NIL_CONTEXT (CAIRO_STATUS_INVALID_DSC_COMMENT),
+    DEFINE_NIL_CONTEXT (CAIRO_STATUS_INVALID_INDEX),
+    DEFINE_NIL_CONTEXT (CAIRO_STATUS_CLIP_NOT_REPRESENTABLE),
+    DEFINE_NIL_CONTEXT (CAIRO_STATUS_TEMP_FILE_ERROR),
+    DEFINE_NIL_CONTEXT (CAIRO_STATUS_INVALID_STRIDE),
+    DEFINE_NIL_CONTEXT (CAIRO_STATUS_FONT_TYPE_MISMATCH),
+    DEFINE_NIL_CONTEXT (CAIRO_STATUS_USER_FONT_IMMUTABLE),
+    DEFINE_NIL_CONTEXT (CAIRO_STATUS_USER_FONT_ERROR),
+    DEFINE_NIL_CONTEXT (CAIRO_STATUS_NEGATIVE_COUNT),
+    DEFINE_NIL_CONTEXT (CAIRO_STATUS_INVALID_CLUSTERS),
+    DEFINE_NIL_CONTEXT (CAIRO_STATUS_INVALID_SLANT),
+    DEFINE_NIL_CONTEXT (CAIRO_STATUS_INVALID_WEIGHT),
+    DEFINE_NIL_CONTEXT (CAIRO_STATUS_INVALID_SIZE),
+    DEFINE_NIL_CONTEXT (CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED),
+    DEFINE_NIL_CONTEXT (CAIRO_STATUS_DEVICE_TYPE_MISMATCH),
+    DEFINE_NIL_CONTEXT (CAIRO_STATUS_DEVICE_ERROR),
+    DEFINE_NIL_CONTEXT (CAIRO_STATUS_INVALID_MESH_CONSTRUCTION),
+    DEFINE_NIL_CONTEXT (CAIRO_STATUS_DEVICE_FINISHED)
+};
+COMPILE_TIME_ASSERT (ARRAY_LENGTH (_cairo_nil) == CAIRO_STATUS_LAST_STATUS - 1);
+
+/**
+ * _cairo_set_error:
+ * @cr: a cairo context
+ * @status: a status value indicating an error
+ *
+ * Atomically sets cr->status to @status and calls _cairo_error;
+ * Does nothing if status is %CAIRO_STATUS_SUCCESS.
+ *
+ * All assignments of an error status to cr->status should happen
+ * through _cairo_set_error(). Note that due to the nature of the atomic
+ * operation, it is not safe to call this function on the nil objects.
+ *
+ * The purpose of this function is to allow the user to set a
+ * breakpoint in _cairo_error() to generate a stack trace for when the
+ * user causes cairo to detect an error.
+ **/
+static void
+_cairo_set_error (cairo_t *cr, cairo_status_t status)
+{
+    /* Don't overwrite an existing error. This preserves the first
+     * error, which is the most significant. */
+    _cairo_status_set_error (&cr->status, _cairo_error (status));
+}
+
+cairo_t *
+_cairo_create_in_error (cairo_status_t status)
+{
+    cairo_t *cr;
+
+    assert (status != CAIRO_STATUS_SUCCESS);
+
+    cr = (cairo_t *) &_cairo_nil[status - CAIRO_STATUS_NO_MEMORY];
+    assert (status == cr->status);
+
+    return cr;
+}
+
+/**
+ * cairo_create:
+ * @target: target surface for the context
+ *
+ * Creates a new #cairo_t with all graphics state parameters set to
+ * default values and with @target as a target surface. The target
+ * surface should be constructed with a backend-specific function such
+ * as cairo_image_surface_create() (or any other
+ * <function>cairo_<emphasis>backend</emphasis>_surface_create(<!-- -->)</function>
+ * variant).
+ *
+ * This function references @target, so you can immediately
+ * call cairo_surface_destroy() on it if you don't need to
+ * maintain a separate reference to it.
+ *
+ * Return value: a newly allocated #cairo_t with a reference
+ *  count of 1. The initial reference count should be released
+ *  with cairo_destroy() when you are done using the #cairo_t.
+ *  This function never returns %NULL. If memory cannot be
+ *  allocated, a special #cairo_t object will be returned on
+ *  which cairo_status() returns %CAIRO_STATUS_NO_MEMORY. If
+ *  you attempt to target a surface which does not support
+ *  writing (such as #cairo_mime_surface_t) then a
+ *  %CAIRO_STATUS_WRITE_ERROR will be raised.  You can use this
+ *  object normally, but no drawing will be done.
+ **/
+cairo_t *
+cairo_create (cairo_surface_t *target)
+{
+    if (unlikely (target == NULL))
+       return _cairo_create_in_error (_cairo_error (CAIRO_STATUS_NULL_POINTER));
+    if (unlikely (target->status))
+       return _cairo_create_in_error (target->status);
+
+    if (target->backend->create_context == NULL)
+       return _cairo_create_in_error (_cairo_error (CAIRO_STATUS_WRITE_ERROR));
+
+    return target->backend->create_context (target);
+
+}
+slim_hidden_def (cairo_create);
+
+void
+_cairo_init (cairo_t *cr,
+            const cairo_backend_t *backend)
+{
+    CAIRO_REFERENCE_COUNT_INIT (&cr->ref_count, 1);
+    cr->status = CAIRO_STATUS_SUCCESS;
+    _cairo_user_data_array_init (&cr->user_data);
+
+    cr->backend = backend;
+}
+
+/**
+ * cairo_reference:
+ * @cr: a #cairo_t
+ *
+ * Increases the reference count on @cr by one. This prevents
+ * @cr from being destroyed until a matching call to cairo_destroy()
+ * is made.
+ *
+ * The number of references to a #cairo_t can be get using
+ * cairo_get_reference_count().
+ *
+ * Return value: the referenced #cairo_t.
+ **/
+cairo_t *
+cairo_reference (cairo_t *cr)
+{
+    if (cr == NULL || CAIRO_REFERENCE_COUNT_IS_INVALID (&cr->ref_count))
+       return cr;
+
+    assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&cr->ref_count));
+
+    _cairo_reference_count_inc (&cr->ref_count);
+
+    return cr;
+}
+
+void
+_cairo_fini (cairo_t *cr)
+{
+    _cairo_user_data_array_fini (&cr->user_data);
+}
+
+/**
+ * cairo_destroy:
+ * @cr: a #cairo_t
+ *
+ * Decreases the reference count on @cr by one. If the result
+ * is zero, then @cr and all associated resources are freed.
+ * See cairo_reference().
+ **/
+void
+cairo_destroy (cairo_t *cr)
+{
+    if (cr == NULL || CAIRO_REFERENCE_COUNT_IS_INVALID (&cr->ref_count))
+       return;
+
+    assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&cr->ref_count));
+
+    if (! _cairo_reference_count_dec_and_test (&cr->ref_count))
+       return;
+
+    cr->backend->destroy (cr);
+}
+slim_hidden_def (cairo_destroy);
+
+/**
+ * cairo_get_user_data:
+ * @cr: a #cairo_t
+ * @key: the address of the #cairo_user_data_key_t the user data was
+ * attached to
+ *
+ * Return user data previously attached to @cr using the specified
+ * key.  If no user data has been attached with the given key this
+ * function returns %NULL.
+ *
+ * Return value: the user data previously attached or %NULL.
+ *
+ * Since: 1.4
+ **/
+void *
+cairo_get_user_data (cairo_t                    *cr,
+                    const cairo_user_data_key_t *key)
+{
+    return _cairo_user_data_array_get_data (&cr->user_data, key);
+}
+
+/**
+ * cairo_set_user_data:
+ * @cr: a #cairo_t
+ * @key: the address of a #cairo_user_data_key_t to attach the user data to
+ * @user_data: the user data to attach to the #cairo_t
+ * @destroy: a #cairo_destroy_func_t which will be called when the
+ * #cairo_t is destroyed or when new user data is attached using the
+ * same key.
+ *
+ * Attach user data to @cr.  To remove user data from a surface,
+ * call this function with the key that was used to set it and %NULL
+ * for @data.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY if a
+ * slot could not be allocated for the user data.
+ *
+ * Since: 1.4
+ **/
+cairo_status_t
+cairo_set_user_data (cairo_t                    *cr,
+                    const cairo_user_data_key_t *key,
+                    void                        *user_data,
+                    cairo_destroy_func_t        destroy)
+{
+    if (CAIRO_REFERENCE_COUNT_IS_INVALID (&cr->ref_count))
+       return cr->status;
+
+    return _cairo_user_data_array_set_data (&cr->user_data,
+                                           key, user_data, destroy);
+}
+
+/**
+ * cairo_get_reference_count:
+ * @cr: a #cairo_t
+ *
+ * Returns the current reference count of @cr.
+ *
+ * Return value: the current reference count of @cr.  If the
+ * object is a nil object, 0 will be returned.
+ *
+ * Since: 1.4
+ **/
+unsigned int
+cairo_get_reference_count (cairo_t *cr)
+{
+    if (cr == NULL || CAIRO_REFERENCE_COUNT_IS_INVALID (&cr->ref_count))
+       return 0;
+
+    return CAIRO_REFERENCE_COUNT_GET_VALUE (&cr->ref_count);
+}
+
+/**
+ * cairo_save:
+ * @cr: a #cairo_t
+ *
+ * Makes a copy of the current state of @cr and saves it
+ * on an internal stack of saved states for @cr. When
+ * cairo_restore() is called, @cr will be restored to
+ * the saved state. Multiple calls to cairo_save() and
+ * cairo_restore() can be nested; each call to cairo_restore()
+ * restores the state from the matching paired cairo_save().
+ *
+ * It isn't necessary to clear all saved states before
+ * a #cairo_t is freed. If the reference count of a #cairo_t
+ * drops to zero in response to a call to cairo_destroy(),
+ * any saved states will be freed along with the #cairo_t.
+ **/
+void
+cairo_save (cairo_t *cr)
+{
+    cairo_status_t status;
+
+    if (unlikely (cr->status))
+       return;
+
+    status = cr->backend->save (cr);
+    if (unlikely (status))
+       _cairo_set_error (cr, status);
+}
+slim_hidden_def(cairo_save);
+
+/**
+ * cairo_restore:
+ * @cr: a #cairo_t
+ *
+ * Restores @cr to the state saved by a preceding call to
+ * cairo_save() and removes that state from the stack of
+ * saved states.
+ **/
+void
+cairo_restore (cairo_t *cr)
+{
+    cairo_status_t status;
+
+    if (unlikely (cr->status))
+       return;
+
+    status = cr->backend->restore (cr);
+    if (unlikely (status))
+       _cairo_set_error (cr, status);
+}
+slim_hidden_def(cairo_restore);
+
+/**
+ * cairo_push_group:
+ * @cr: a cairo context
+ *
+ * Temporarily redirects drawing to an intermediate surface known as a
+ * group. The redirection lasts until the group is completed by a call
+ * to cairo_pop_group() or cairo_pop_group_to_source(). These calls
+ * provide the result of any drawing to the group as a pattern,
+ * (either as an explicit object, or set as the source pattern).
+ *
+ * This group functionality can be convenient for performing
+ * intermediate compositing. One common use of a group is to render
+ * objects as opaque within the group, (so that they occlude each
+ * other), and then blend the result with translucence onto the
+ * destination.
+ *
+ * Groups can be nested arbitrarily deep by making balanced calls to
+ * cairo_push_group()/cairo_pop_group(). Each call pushes/pops the new
+ * target group onto/from a stack.
+ *
+ * The cairo_push_group() function calls cairo_save() so that any
+ * changes to the graphics state will not be visible outside the
+ * group, (the pop_group functions call cairo_restore()).
+ *
+ * By default the intermediate group will have a content type of
+ * %CAIRO_CONTENT_COLOR_ALPHA. Other content types can be chosen for
+ * the group by using cairo_push_group_with_content() instead.
+ *
+ * As an example, here is how one might fill and stroke a path with
+ * translucence, but without any portion of the fill being visible
+ * under the stroke:
+ *
+ * <informalexample><programlisting>
+ * cairo_push_group (cr);
+ * cairo_set_source (cr, fill_pattern);
+ * cairo_fill_preserve (cr);
+ * cairo_set_source (cr, stroke_pattern);
+ * cairo_stroke (cr);
+ * cairo_pop_group_to_source (cr);
+ * cairo_paint_with_alpha (cr, alpha);
+ * </programlisting></informalexample>
+ *
+ * Since: 1.2
+ */
+void
+cairo_push_group (cairo_t *cr)
+{
+    cairo_push_group_with_content (cr, CAIRO_CONTENT_COLOR_ALPHA);
+}
+
+/**
+ * cairo_push_group_with_content:
+ * @cr: a cairo context
+ * @content: a #cairo_content_t indicating the type of group that
+ *           will be created
+ *
+ * Temporarily redirects drawing to an intermediate surface known as a
+ * group. The redirection lasts until the group is completed by a call
+ * to cairo_pop_group() or cairo_pop_group_to_source(). These calls
+ * provide the result of any drawing to the group as a pattern,
+ * (either as an explicit object, or set as the source pattern).
+ *
+ * The group will have a content type of @content. The ability to
+ * control this content type is the only distinction between this
+ * function and cairo_push_group() which you should see for a more
+ * detailed description of group rendering.
+ *
+ * Since: 1.2
+ */
+void
+cairo_push_group_with_content (cairo_t *cr, cairo_content_t content)
+{
+    cairo_status_t status;
+
+    if (unlikely (cr->status))
+       return;
+
+    status = cr->backend->push_group (cr, content);
+    if (unlikely (status))
+       _cairo_set_error (cr, status);
+}
+slim_hidden_def(cairo_push_group_with_content);
+
+/**
+ * cairo_pop_group:
+ * @cr: a cairo context
+ *
+ * Terminates the redirection begun by a call to cairo_push_group() or
+ * cairo_push_group_with_content() and returns a new pattern
+ * containing the results of all drawing operations performed to the
+ * group.
+ *
+ * The cairo_pop_group() function calls cairo_restore(), (balancing a
+ * call to cairo_save() by the push_group function), so that any
+ * changes to the graphics state will not be visible outside the
+ * group.
+ *
+ * Return value: a newly created (surface) pattern containing the
+ * results of all drawing operations performed to the group. The
+ * caller owns the returned object and should call
+ * cairo_pattern_destroy() when finished with it.
+ *
+ * Since: 1.2
+ **/
+cairo_pattern_t *
+cairo_pop_group (cairo_t *cr)
+{
+    cairo_pattern_t *group_pattern;
+
+    if (unlikely (cr->status))
+       return _cairo_pattern_create_in_error (cr->status);
+
+    group_pattern = cr->backend->pop_group (cr);
+    if (unlikely (group_pattern->status))
+       _cairo_set_error (cr, group_pattern->status);
+
+    return group_pattern;
+}
+slim_hidden_def(cairo_pop_group);
+
+/**
+ * cairo_pop_group_to_source:
+ * @cr: a cairo context
+ *
+ * Terminates the redirection begun by a call to cairo_push_group() or
+ * cairo_push_group_with_content() and installs the resulting pattern
+ * as the source pattern in the given cairo context.
+ *
+ * The behavior of this function is equivalent to the sequence of
+ * operations:
+ *
+ * <informalexample><programlisting>
+ * cairo_pattern_t *group = cairo_pop_group (cr);
+ * cairo_set_source (cr, group);
+ * cairo_pattern_destroy (group);
+ * </programlisting></informalexample>
+ *
+ * but is more convenient as their is no need for a variable to store
+ * the short-lived pointer to the pattern.
+ *
+ * The cairo_pop_group() function calls cairo_restore(), (balancing a
+ * call to cairo_save() by the push_group function), so that any
+ * changes to the graphics state will not be visible outside the
+ * group.
+ *
+ * Since: 1.2
+ **/
+void
+cairo_pop_group_to_source (cairo_t *cr)
+{
+    cairo_pattern_t *group_pattern;
+
+    group_pattern = cairo_pop_group (cr);
+    cairo_set_source (cr, group_pattern);
+    cairo_pattern_destroy (group_pattern);
+}
+
+/**
+ * cairo_set_operator:
+ * @cr: a #cairo_t
+ * @op: a compositing operator, specified as a #cairo_operator_t
+ *
+ * Sets the compositing operator to be used for all drawing
+ * operations. See #cairo_operator_t for details on the semantics of
+ * each available compositing operator.
+ *
+ * The default operator is %CAIRO_OPERATOR_OVER.
+ **/
+void
+cairo_set_operator (cairo_t *cr, cairo_operator_t op)
+{
+    cairo_status_t status;
+
+    if (unlikely (cr->status))
+       return;
+
+    status = cr->backend->set_operator (cr, op);
+    if (unlikely (status))
+       _cairo_set_error (cr, status);
+}
+slim_hidden_def (cairo_set_operator);
+
+
+#if 0
+/**
+ * cairo_set_opacity:
+ * @cr: a #cairo_t
+ * @opacity: the level of opacity to use when compositing
+ *
+ * Sets the compositing opacity to be used for all drawing
+ * operations. The effect is to fade out the operations
+ * using the alpha value.
+ *
+ * The default opacity is 1.
+ **/
+void
+cairo_set_opacity (cairo_t *cr, double opacity)
+{
+    cairo_status_t status;
+
+    if (unlikely (cr->status))
+       return;
+
+    status = cr->backend->set_opacity (cr, opacity);
+    if (unlikely (status))
+       _cairo_set_error (cr, status);
+}
+#endif
+
+/**
+ * cairo_set_source_rgb
+ * @cr: a cairo context
+ * @red: red component of color
+ * @green: green component of color
+ * @blue: blue component of color
+ *
+ * Sets the source pattern within @cr to an opaque color. This opaque
+ * color will then be used for any subsequent drawing operation until
+ * a new source pattern is set.
+ *
+ * The color components are floating point numbers in the range 0 to
+ * 1. If the values passed in are outside that range, they will be
+ * clamped.
+ *
+ * The default source pattern is opaque black, (that is, it is
+ * equivalent to cairo_set_source_rgb(cr, 0.0, 0.0, 0.0)).
+ **/
+void
+cairo_set_source_rgb (cairo_t *cr, double red, double green, double blue)
+{
+    cairo_status_t status;
+
+    if (unlikely (cr->status))
+       return;
+
+    status = cr->backend->set_source_rgba (cr, red, green, blue, 1.);
+    if (unlikely (status))
+       _cairo_set_error (cr, status);
+}
+slim_hidden_def (cairo_set_source_rgb);
+
+/**
+ * cairo_set_source_rgba:
+ * @cr: a cairo context
+ * @red: red component of color
+ * @green: green component of color
+ * @blue: blue component of color
+ * @alpha: alpha component of color
+ *
+ * Sets the source pattern within @cr to a translucent color. This
+ * color will then be used for any subsequent drawing operation until
+ * a new source pattern is set.
+ *
+ * The color and alpha components are floating point numbers in the
+ * range 0 to 1. If the values passed in are outside that range, they
+ * will be clamped.
+ *
+ * The default source pattern is opaque black, (that is, it is
+ * equivalent to cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 1.0)).
+ **/
+void
+cairo_set_source_rgba (cairo_t *cr,
+                      double red, double green, double blue,
+                      double alpha)
+{
+    cairo_status_t status;
+
+    if (unlikely (cr->status))
+       return;
+
+    status = cr->backend->set_source_rgba (cr, red, green, blue, alpha);
+    if (unlikely (status))
+       _cairo_set_error (cr, status);
+}
+
+/**
+ * cairo_set_source_surface:
+ * @cr: a cairo context
+ * @surface: a surface to be used to set the source pattern
+ * @x: User-space X coordinate for surface origin
+ * @y: User-space Y coordinate for surface origin
+ *
+ * This is a convenience function for creating a pattern from @surface
+ * and setting it as the source in @cr with cairo_set_source().
+ *
+ * The @x and @y parameters give the user-space coordinate at which
+ * the surface origin should appear. (The surface origin is its
+ * upper-left corner before any transformation has been applied.) The
+ * @x and @y parameters are negated and then set as translation values
+ * in the pattern matrix.
+ *
+ * Other than the initial translation pattern matrix, as described
+ * above, all other pattern attributes, (such as its extend mode), are
+ * set to the default values as in cairo_pattern_create_for_surface().
+ * The resulting pattern can be queried with cairo_get_source() so
+ * that these attributes can be modified if desired, (eg. to create a
+ * repeating pattern with cairo_pattern_set_extend()).
+ **/
+void
+cairo_set_source_surface (cairo_t        *cr,
+                         cairo_surface_t *surface,
+                         double           x,
+                         double           y)
+{
+    cairo_status_t status;
+
+    if (unlikely (cr->status))
+       return;
+
+    if (unlikely (surface == NULL)) {
+       _cairo_set_error (cr, CAIRO_STATUS_NULL_POINTER);
+       return;
+    }
+
+    status = cr->backend->set_source_surface (cr, surface, x, y);
+    if (unlikely (status))
+       _cairo_set_error (cr, status);
+}
+slim_hidden_def (cairo_set_source_surface);
+
+/**
+ * cairo_set_source
+ * @cr: a cairo context
+ * @source: a #cairo_pattern_t to be used as the source for
+ * subsequent drawing operations.
+ *
+ * Sets the source pattern within @cr to @source. This pattern
+ * will then be used for any subsequent drawing operation until a new
+ * source pattern is set.
+ *
+ * Note: The pattern's transformation matrix will be locked to the
+ * user space in effect at the time of cairo_set_source(). This means
+ * that further modifications of the current transformation matrix
+ * will not affect the source pattern. See cairo_pattern_set_matrix().
+ *
+ * The default source pattern is a solid pattern that is opaque black,
+ * (that is, it is equivalent to cairo_set_source_rgb(cr, 0.0, 0.0,
+ * 0.0)).
+ **/
+void
+cairo_set_source (cairo_t *cr, cairo_pattern_t *source)
+{
+    cairo_status_t status;
+
+    if (unlikely (cr->status))
+       return;
+
+    if (unlikely (source == NULL)) {
+       _cairo_set_error (cr, CAIRO_STATUS_NULL_POINTER);
+       return;
+    }
+
+    if (unlikely (source->status)) {
+       _cairo_set_error (cr, source->status);
+       return;
+    }
+
+    status = cr->backend->set_source (cr, source);
+    if (unlikely (status))
+       _cairo_set_error (cr, status);
+}
+slim_hidden_def (cairo_set_source);
+
+/**
+ * cairo_get_source:
+ * @cr: a cairo context
+ *
+ * Gets the current source pattern for @cr.
+ *
+ * Return value: the current source pattern. This object is owned by
+ * cairo. To keep a reference to it, you must call
+ * cairo_pattern_reference().
+ **/
+cairo_pattern_t *
+cairo_get_source (cairo_t *cr)
+{
+    if (unlikely (cr->status))
+       return _cairo_pattern_create_in_error (cr->status);
+
+    return cr->backend->get_source (cr);
+}
+
+/**
+ * cairo_set_tolerance:
+ * @cr: a #cairo_t
+ * @tolerance: the tolerance, in device units (typically pixels)
+ *
+ * Sets the tolerance used when converting paths into trapezoids.
+ * Curved segments of the path will be subdivided until the maximum
+ * deviation between the original path and the polygonal approximation
+ * is less than @tolerance. The default value is 0.1. A larger
+ * value will give better performance, a smaller value, better
+ * appearance. (Reducing the value from the default value of 0.1
+ * is unlikely to improve appearance significantly.)  The accuracy of paths
+ * within Cairo is limited by the precision of its internal arithmetic, and
+ * the prescribed @tolerance is restricted to the smallest
+ * representable internal value.
+ **/
+void
+cairo_set_tolerance (cairo_t *cr, double tolerance)
+{
+    cairo_status_t status;
+
+    if (unlikely (cr->status))
+       return;
+
+    status = cr->backend->set_tolerance (cr, tolerance);
+    if (unlikely (status))
+       _cairo_set_error (cr, status);
+}
+slim_hidden_def (cairo_set_tolerance);
+
+/**
+ * cairo_set_antialias:
+ * @cr: a #cairo_t
+ * @antialias: the new antialiasing mode
+ *
+ * Set the antialiasing mode of the rasterizer used for drawing shapes.
+ * This value is a hint, and a particular backend may or may not support
+ * a particular value.  At the current time, no backend supports
+ * %CAIRO_ANTIALIAS_SUBPIXEL when drawing shapes.
+ *
+ * Note that this option does not affect text rendering, instead see
+ * cairo_font_options_set_antialias().
+ **/
+void
+cairo_set_antialias (cairo_t *cr, cairo_antialias_t antialias)
+{
+    cairo_status_t status;
+
+    if (unlikely (cr->status))
+       return;
+
+    status = cr->backend->set_antialias (cr, antialias);
+    if (unlikely (status))
+       _cairo_set_error (cr, status);
+}
+
+/**
+ * cairo_set_fill_rule:
+ * @cr: a #cairo_t
+ * @fill_rule: a fill rule, specified as a #cairo_fill_rule_t
+ *
+ * Set the current fill rule within the cairo context. The fill rule
+ * is used to determine which regions are inside or outside a complex
+ * (potentially self-intersecting) path. The current fill rule affects
+ * both cairo_fill() and cairo_clip(). See #cairo_fill_rule_t for details
+ * on the semantics of each available fill rule.
+ *
+ * The default fill rule is %CAIRO_FILL_RULE_WINDING.
+ **/
+void
+cairo_set_fill_rule (cairo_t *cr, cairo_fill_rule_t fill_rule)
+{
+    cairo_status_t status;
+
+    if (unlikely (cr->status))
+       return;
+
+    status = cr->backend->set_fill_rule (cr, fill_rule);
+    if (unlikely (status))
+       _cairo_set_error (cr, status);
+}
+
+/**
+ * cairo_set_line_width:
+ * @cr: a #cairo_t
+ * @width: a line width
+ *
+ * Sets the current line width within the cairo context. The line
+ * width value specifies the diameter of a pen that is circular in
+ * user space, (though device-space pen may be an ellipse in general
+ * due to scaling/shear/rotation of the CTM).
+ *
+ * Note: When the description above refers to user space and CTM it
+ * refers to the user space and CTM in effect at the time of the
+ * stroking operation, not the user space and CTM in effect at the
+ * time of the call to cairo_set_line_width(). The simplest usage
+ * makes both of these spaces identical. That is, if there is no
+ * change to the CTM between a call to cairo_set_line_width() and the
+ * stroking operation, then one can just pass user-space values to
+ * cairo_set_line_width() and ignore this note.
+ *
+ * As with the other stroke parameters, the current line width is
+ * examined by cairo_stroke(), cairo_stroke_extents(), and
+ * cairo_stroke_to_path(), but does not have any effect during path
+ * construction.
+ *
+ * The default line width value is 2.0.
+ **/
+void
+cairo_set_line_width (cairo_t *cr, double width)
+{
+    cairo_status_t status;
+
+    if (unlikely (cr->status))
+       return;
+
+    if (width < 0.)
+       width = 0.;
+
+    status = cr->backend->set_line_width (cr, width);
+    if (unlikely (status))
+       _cairo_set_error (cr, status);
+}
+slim_hidden_def (cairo_set_line_width);
+
+/**
+ * cairo_set_line_cap:
+ * @cr: a cairo context
+ * @line_cap: a line cap style
+ *
+ * Sets the current line cap style within the cairo context. See
+ * #cairo_line_cap_t for details about how the available line cap
+ * styles are drawn.
+ *
+ * As with the other stroke parameters, the current line cap style is
+ * examined by cairo_stroke(), cairo_stroke_extents(), and
+ * cairo_stroke_to_path(), but does not have any effect during path
+ * construction.
+ *
+ * The default line cap style is %CAIRO_LINE_CAP_BUTT.
+ **/
+void
+cairo_set_line_cap (cairo_t *cr, cairo_line_cap_t line_cap)
+{
+    cairo_status_t status;
+
+    if (unlikely (cr->status))
+       return;
+
+    status = cr->backend->set_line_cap (cr, line_cap);
+    if (unlikely (status))
+       _cairo_set_error (cr, status);
+}
+slim_hidden_def (cairo_set_line_cap);
+
+/**
+ * cairo_set_line_join:
+ * @cr: a cairo context
+ * @line_join: a line join style
+ *
+ * Sets the current line join style within the cairo context. See
+ * #cairo_line_join_t for details about how the available line join
+ * styles are drawn.
+ *
+ * As with the other stroke parameters, the current line join style is
+ * examined by cairo_stroke(), cairo_stroke_extents(), and
+ * cairo_stroke_to_path(), but does not have any effect during path
+ * construction.
+ *
+ * The default line join style is %CAIRO_LINE_JOIN_MITER.
+ **/
+void
+cairo_set_line_join (cairo_t *cr, cairo_line_join_t line_join)
+{
+    cairo_status_t status;
+
+    if (unlikely (cr->status))
+       return;
+
+    status = cr->backend->set_line_join (cr, line_join);
+    if (unlikely (status))
+       _cairo_set_error (cr, status);
+}
+slim_hidden_def (cairo_set_line_join);
+
+/**
+ * cairo_set_dash:
+ * @cr: a cairo context
+ * @dashes: an array specifying alternate lengths of on and off stroke portions
+ * @num_dashes: the length of the dashes array
+ * @offset: an offset into the dash pattern at which the stroke should start
+ *
+ * Sets the dash pattern to be used by cairo_stroke(). A dash pattern
+ * is specified by @dashes, an array of positive values. Each value
+ * provides the length of alternate "on" and "off" portions of the
+ * stroke. The @offset specifies an offset into the pattern at which
+ * the stroke begins.
+ *
+ * Each "on" segment will have caps applied as if the segment were a
+ * separate sub-path. In particular, it is valid to use an "on" length
+ * of 0.0 with %CAIRO_LINE_CAP_ROUND or %CAIRO_LINE_CAP_SQUARE in order
+ * to distributed dots or squares along a path.
+ *
+ * Note: The length values are in user-space units as evaluated at the
+ * time of stroking. This is not necessarily the same as the user
+ * space at the time of cairo_set_dash().
+ *
+ * If @num_dashes is 0 dashing is disabled.
+ *
+ * If @num_dashes is 1 a symmetric pattern is assumed with alternating
+ * on and off portions of the size specified by the single value in
+ * @dashes.
+ *
+ * If any value in @dashes is negative, or if all values are 0, then
+ * @cr will be put into an error state with a status of
+ * %CAIRO_STATUS_INVALID_DASH.
+ **/
+void
+cairo_set_dash (cairo_t             *cr,
+               const double *dashes,
+               int           num_dashes,
+               double        offset)
+{
+    cairo_status_t status;
+
+    if (unlikely (cr->status))
+       return;
+
+    status = cr->backend->set_dash (cr, dashes, num_dashes, offset);
+    if (unlikely (status))
+       _cairo_set_error (cr, status);
+}
+
+/**
+ * cairo_get_dash_count:
+ * @cr: a #cairo_t
+ *
+ * This function returns the length of the dash array in @cr (0 if dashing
+ * is not currently in effect).
+ *
+ * See also cairo_set_dash() and cairo_get_dash().
+ *
+ * Return value: the length of the dash array, or 0 if no dash array set.
+ *
+ * Since: 1.4
+ */
+int
+cairo_get_dash_count (cairo_t *cr)
+{
+    int num_dashes;
+
+    if (unlikely (cr->status))
+       return 0;
+
+    cr->backend->get_dash (cr, NULL, &num_dashes, NULL);
+
+    return num_dashes;
+}
+
+/**
+ * cairo_get_dash:
+ * @cr: a #cairo_t
+ * @dashes: return value for the dash array, or %NULL
+ * @offset: return value for the current dash offset, or %NULL
+ *
+ * Gets the current dash array.  If not %NULL, @dashes should be big
+ * enough to hold at least the number of values returned by
+ * cairo_get_dash_count().
+ *
+ * Since: 1.4
+ **/
+void
+cairo_get_dash (cairo_t *cr,
+               double  *dashes,
+               double  *offset)
+{
+    if (unlikely (cr->status))
+       return;
+
+    cr->backend->get_dash (cr, dashes, NULL, offset);
+}
+
+/**
+ * cairo_set_miter_limit:
+ * @cr: a cairo context
+ * @limit: miter limit to set
+ *
+ * Sets the current miter limit within the cairo context.
+ *
+ * If the current line join style is set to %CAIRO_LINE_JOIN_MITER
+ * (see cairo_set_line_join()), the miter limit is used to determine
+ * whether the lines should be joined with a bevel instead of a miter.
+ * Cairo divides the length of the miter by the line width.
+ * If the result is greater than the miter limit, the style is
+ * converted to a bevel.
+ *
+ * As with the other stroke parameters, the current line miter limit is
+ * examined by cairo_stroke(), cairo_stroke_extents(), and
+ * cairo_stroke_to_path(), but does not have any effect during path
+ * construction.
+ *
+ * The default miter limit value is 10.0, which will convert joins
+ * with interior angles less than 11 degrees to bevels instead of
+ * miters. For reference, a miter limit of 2.0 makes the miter cutoff
+ * at 60 degrees, and a miter limit of 1.414 makes the cutoff at 90
+ * degrees.
+ *
+ * A miter limit for a desired angle can be computed as: miter limit =
+ * 1/sin(angle/2)
+ **/
+void
+cairo_set_miter_limit (cairo_t *cr, double limit)
+{
+    cairo_status_t status;
+
+    if (unlikely (cr->status))
+       return;
+
+    status = cr->backend->set_miter_limit (cr, limit);
+    if (unlikely (status))
+       _cairo_set_error (cr, status);
+}
+
+/**
+ * cairo_translate:
+ * @cr: a cairo context
+ * @tx: amount to translate in the X direction
+ * @ty: amount to translate in the Y direction
+ *
+ * Modifies the current transformation matrix (CTM) by translating the
+ * user-space origin by (@tx, @ty). This offset is interpreted as a
+ * user-space coordinate according to the CTM in place before the new
+ * call to cairo_translate(). In other words, the translation of the
+ * user-space origin takes place after any existing transformation.
+ **/
+void
+cairo_translate (cairo_t *cr, double tx, double ty)
+{
+    cairo_status_t status;
+
+    if (unlikely (cr->status))
+       return;
+
+    status = cr->backend->translate (cr, tx, ty);
+    if (unlikely (status))
+       _cairo_set_error (cr, status);
+}
+slim_hidden_def (cairo_translate);
+
+/**
+ * cairo_scale:
+ * @cr: a cairo context
+ * @sx: scale factor for the X dimension
+ * @sy: scale factor for the Y dimension
+ *
+ * Modifies the current transformation matrix (CTM) by scaling the X
+ * and Y user-space axes by @sx and @sy respectively. The scaling of
+ * the axes takes place after any existing transformation of user
+ * space.
+ **/
+void
+cairo_scale (cairo_t *cr, double sx, double sy)
+{
+    cairo_status_t status;
+
+    if (unlikely (cr->status))
+       return;
+
+    status = cr->backend->scale (cr, sx, sy);
+    if (unlikely (status))
+       _cairo_set_error (cr, status);
+}
+slim_hidden_def (cairo_scale);
+
+/**
+ * cairo_rotate:
+ * @cr: a cairo context
+ * @angle: angle (in radians) by which the user-space axes will be
+ * rotated
+ *
+ * Modifies the current transformation matrix (CTM) by rotating the
+ * user-space axes by @angle radians. The rotation of the axes takes
+ * places after any existing transformation of user space. The
+ * rotation direction for positive angles is from the positive X axis
+ * toward the positive Y axis.
+ **/
+void
+cairo_rotate (cairo_t *cr, double angle)
+{
+    cairo_status_t status;
+
+    if (unlikely (cr->status))
+       return;
+
+    status = cr->backend->rotate (cr, angle);
+    if (unlikely (status))
+       _cairo_set_error (cr, status);
+}
+
+/**
+ * cairo_transform:
+ * @cr: a cairo context
+ * @matrix: a transformation to be applied to the user-space axes
+ *
+ * Modifies the current transformation matrix (CTM) by applying
+ * @matrix as an additional transformation. The new transformation of
+ * user space takes place after any existing transformation.
+ **/
+void
+cairo_transform (cairo_t             *cr,
+                const cairo_matrix_t *matrix)
+{
+    cairo_status_t status;
+
+    if (unlikely (cr->status))
+       return;
+
+    status = cr->backend->transform (cr, matrix);
+    if (unlikely (status))
+       _cairo_set_error (cr, status);
+}
+slim_hidden_def (cairo_transform);
+
+/**
+ * cairo_set_matrix:
+ * @cr: a cairo context
+ * @matrix: a transformation matrix from user space to device space
+ *
+ * Modifies the current transformation matrix (CTM) by setting it
+ * equal to @matrix.
+ **/
+void
+cairo_set_matrix (cairo_t             *cr,
+                 const cairo_matrix_t *matrix)
+{
+    cairo_status_t status;
+
+    if (unlikely (cr->status))
+       return;
+
+    status = cr->backend->set_matrix (cr, matrix);
+    if (unlikely (status))
+       _cairo_set_error (cr, status);
+}
+slim_hidden_def (cairo_set_matrix);
+
+/**
+ * cairo_identity_matrix:
+ * @cr: a cairo context
+ *
+ * Resets the current transformation matrix (CTM) by setting it equal
+ * to the identity matrix. That is, the user-space and device-space
+ * axes will be aligned and one user-space unit will transform to one
+ * device-space unit.
+ **/
+void
+cairo_identity_matrix (cairo_t *cr)
+{
+    cairo_status_t status;
+
+    if (unlikely (cr->status))
+       return;
+
+    status = cr->backend->set_identity_matrix (cr);
+    if (unlikely (status))
+       _cairo_set_error (cr, status);
+}
+
+/**
+ * cairo_user_to_device:
+ * @cr: a cairo context
+ * @x: X value of coordinate (in/out parameter)
+ * @y: Y value of coordinate (in/out parameter)
+ *
+ * Transform a coordinate from user space to device space by
+ * multiplying the given point by the current transformation matrix
+ * (CTM).
+ **/
+void
+cairo_user_to_device (cairo_t *cr, double *x, double *y)
+{
+    if (unlikely (cr->status))
+       return;
+
+    cr->backend->user_to_device (cr, x, y);
+}
+slim_hidden_def (cairo_user_to_device);
+
+/**
+ * cairo_user_to_device_distance:
+ * @cr: a cairo context
+ * @dx: X component of a distance vector (in/out parameter)
+ * @dy: Y component of a distance vector (in/out parameter)
+ *
+ * Transform a distance vector from user space to device space. This
+ * function is similar to cairo_user_to_device() except that the
+ * translation components of the CTM will be ignored when transforming
+ * (@dx,@dy).
+ **/
+void
+cairo_user_to_device_distance (cairo_t *cr, double *dx, double *dy)
+{
+    if (unlikely (cr->status))
+       return;
+
+    cr->backend->user_to_device_distance (cr, dx, dy);
+}
+slim_hidden_def (cairo_user_to_device_distance);
+
+/**
+ * cairo_device_to_user:
+ * @cr: a cairo
+ * @x: X value of coordinate (in/out parameter)
+ * @y: Y value of coordinate (in/out parameter)
+ *
+ * Transform a coordinate from device space to user space by
+ * multiplying the given point by the inverse of the current
+ * transformation matrix (CTM).
+ **/
+void
+cairo_device_to_user (cairo_t *cr, double *x, double *y)
+{
+    if (unlikely (cr->status))
+       return;
+
+    cr->backend->device_to_user (cr, x, y);
+}
+slim_hidden_def (cairo_device_to_user);
+
+/**
+ * cairo_device_to_user_distance:
+ * @cr: a cairo context
+ * @dx: X component of a distance vector (in/out parameter)
+ * @dy: Y component of a distance vector (in/out parameter)
+ *
+ * Transform a distance vector from device space to user space. This
+ * function is similar to cairo_device_to_user() except that the
+ * translation components of the inverse CTM will be ignored when
+ * transforming (@dx,@dy).
+ **/
+void
+cairo_device_to_user_distance (cairo_t *cr, double *dx, double *dy)
+{
+    if (unlikely (cr->status))
+       return;
+
+    cr->backend->device_to_user_distance (cr, dx, dy);
+}
+
+/**
+ * cairo_new_path:
+ * @cr: a cairo context
+ *
+ * Clears the current path. After this call there will be no path and
+ * no current point.
+ **/
+void
+cairo_new_path (cairo_t *cr)
+{
+    cairo_status_t status;
+
+    if (unlikely (cr->status))
+       return;
+
+    status = cr->backend->new_path (cr);
+    if (unlikely (status))
+       _cairo_set_error (cr, status);
+}
+slim_hidden_def(cairo_new_path);
+
+/**
+ * cairo_new_sub_path:
+ * @cr: a cairo context
+ *
+ * Begin a new sub-path. Note that the existing path is not
+ * affected. After this call there will be no current point.
+ *
+ * In many cases, this call is not needed since new sub-paths are
+ * frequently started with cairo_move_to().
+ *
+ * A call to cairo_new_sub_path() is particularly useful when
+ * beginning a new sub-path with one of the cairo_arc() calls. This
+ * makes things easier as it is no longer necessary to manually
+ * compute the arc's initial coordinates for a call to
+ * cairo_move_to().
+ *
+ * Since: 1.2
+ **/
+void
+cairo_new_sub_path (cairo_t *cr)
+{
+    cairo_status_t status;
+
+    if (unlikely (cr->status))
+       return;
+
+    status = cr->backend->new_sub_path (cr);
+    if (unlikely (status))
+       _cairo_set_error (cr, status);
+}
+
+/**
+ * cairo_move_to:
+ * @cr: a cairo context
+ * @x: the X coordinate of the new position
+ * @y: the Y coordinate of the new position
+ *
+ * Begin a new sub-path. After this call the current point will be (@x,
+ * @y).
+ **/
+void
+cairo_move_to (cairo_t *cr, double x, double y)
+{
+    cairo_status_t status;
+
+    if (unlikely (cr->status))
+       return;
+
+    status = cr->backend->move_to (cr, x, y);
+    if (unlikely (status))
+       _cairo_set_error (cr, status);
+}
+slim_hidden_def(cairo_move_to);
+
+
+/**
+ * cairo_line_to:
+ * @cr: a cairo context
+ * @x: the X coordinate of the end of the new line
+ * @y: the Y coordinate of the end of the new line
+ *
+ * Adds a line to the path from the current point to position (@x, @y)
+ * in user-space coordinates. After this call the current point
+ * will be (@x, @y).
+ *
+ * If there is no current point before the call to cairo_line_to()
+ * this function will behave as cairo_move_to(@cr, @x, @y).
+ **/
+void
+cairo_line_to (cairo_t *cr, double x, double y)
+{
+    cairo_status_t status;
+
+    if (unlikely (cr->status))
+       return;
+
+    status = cr->backend->line_to (cr, x, y);
+    if (unlikely (status))
+       _cairo_set_error (cr, status);
+}
+slim_hidden_def (cairo_line_to);
+
+/**
+ * cairo_curve_to:
+ * @cr: a cairo context
+ * @x1: the X coordinate of the first control point
+ * @y1: the Y coordinate of the first control point
+ * @x2: the X coordinate of the second control point
+ * @y2: the Y coordinate of the second control point
+ * @x3: the X coordinate of the end of the curve
+ * @y3: the Y coordinate of the end of the curve
+ *
+ * Adds a cubic Bézier spline to the path from the current point to
+ * position (@x3, @y3) in user-space coordinates, using (@x1, @y1) and
+ * (@x2, @y2) as the control points. After this call the current point
+ * will be (@x3, @y3).
+ *
+ * If there is no current point before the call to cairo_curve_to()
+ * this function will behave as if preceded by a call to
+ * cairo_move_to(@cr, @x1, @y1).
+ **/
+void
+cairo_curve_to (cairo_t *cr,
+               double x1, double y1,
+               double x2, double y2,
+               double x3, double y3)
+{
+    cairo_status_t status;
+
+    if (unlikely (cr->status))
+       return;
+
+    status = cr->backend->curve_to (cr,
+                                   x1, y1,
+                                   x2, y2,
+                                   x3, y3);
+    if (unlikely (status))
+       _cairo_set_error (cr, status);
+}
+slim_hidden_def (cairo_curve_to);
+
+/**
+ * cairo_arc:
+ * @cr: a cairo context
+ * @xc: X position of the center of the arc
+ * @yc: Y position of the center of the arc
+ * @radius: the radius of the arc
+ * @angle1: the start angle, in radians
+ * @angle2: the end angle, in radians
+ *
+ * Adds a circular arc of the given @radius to the current path.  The
+ * arc is centered at (@xc, @yc), begins at @angle1 and proceeds in
+ * the direction of increasing angles to end at @angle2. If @angle2 is
+ * less than @angle1 it will be progressively increased by
+ * <literal>2*M_PI</literal> until it is greater than @angle1.
+ *
+ * If there is a current point, an initial line segment will be added
+ * to the path to connect the current point to the beginning of the
+ * arc. If this initial line is undesired, it can be avoided by
+ * calling cairo_new_sub_path() before calling cairo_arc().
+ *
+ * Angles are measured in radians. An angle of 0.0 is in the direction
+ * of the positive X axis (in user space). An angle of
+ * <literal>M_PI/2.0</literal> radians (90 degrees) is in the
+ * direction of the positive Y axis (in user space). Angles increase
+ * in the direction from the positive X axis toward the positive Y
+ * axis. So with the default transformation matrix, angles increase in
+ * a clockwise direction.
+ *
+ * (To convert from degrees to radians, use <literal>degrees * (M_PI /
+ * 180.)</literal>.)
+ *
+ * This function gives the arc in the direction of increasing angles;
+ * see cairo_arc_negative() to get the arc in the direction of
+ * decreasing angles.
+ *
+ * The arc is circular in user space. To achieve an elliptical arc,
+ * you can scale the current transformation matrix by different
+ * amounts in the X and Y directions. For example, to draw an ellipse
+ * in the box given by @x, @y, @width, @height:
+ *
+ * <informalexample><programlisting>
+ * cairo_save (cr);
+ * cairo_translate (cr, x + width / 2., y + height / 2.);
+ * cairo_scale (cr, width / 2., height / 2.);
+ * cairo_arc (cr, 0., 0., 1., 0., 2 * M_PI);
+ * cairo_restore (cr);
+ * </programlisting></informalexample>
+ **/
+void
+cairo_arc (cairo_t *cr,
+          double xc, double yc,
+          double radius,
+          double angle1, double angle2)
+{
+    cairo_status_t status;
+
+    if (unlikely (cr->status))
+       return;
+
+    if (angle2 < angle1) {
+       /* increase angle2 by multiples of full circle until it
+        * satisfies angle2 >= angle1 */
+       angle2 = fmod (angle2 - angle1, 2 * M_PI);
+       if (angle2 < 0)
+           angle2 += 2 * M_PI;
+       angle2 += angle1;
+    }
+
+    status = cr->backend->arc (cr, xc, yc, radius, angle1, angle2, TRUE);
+    if (unlikely (status))
+       _cairo_set_error (cr, status);
+}
+
+/**
+ * cairo_arc_negative:
+ * @cr: a cairo context
+ * @xc: X position of the center of the arc
+ * @yc: Y position of the center of the arc
+ * @radius: the radius of the arc
+ * @angle1: the start angle, in radians
+ * @angle2: the end angle, in radians
+ *
+ * Adds a circular arc of the given @radius to the current path.  The
+ * arc is centered at (@xc, @yc), begins at @angle1 and proceeds in
+ * the direction of decreasing angles to end at @angle2. If @angle2 is
+ * greater than @angle1 it will be progressively decreased by
+ * <literal>2*M_PI</literal> until it is less than @angle1.
+ *
+ * See cairo_arc() for more details. This function differs only in the
+ * direction of the arc between the two angles.
+ **/
+void
+cairo_arc_negative (cairo_t *cr,
+                   double xc, double yc,
+                   double radius,
+                   double angle1, double angle2)
+{
+    cairo_status_t status;
+
+    if (unlikely (cr->status))
+       return;
+
+    if (angle2 > angle1) {
+       /* decrease angle2 by multiples of full circle until it
+        * satisfies angle2 <= angle1 */
+       angle2 = fmod (angle2 - angle1, 2 * M_PI);
+       if (angle2 > 0)
+           angle2 -= 2 * M_PI;
+       angle2 += angle1;
+    }
+
+    status = cr->backend->arc (cr, xc, yc, radius, angle1, angle2, FALSE);
+    if (unlikely (status))
+       _cairo_set_error (cr, status);
+}
+
+/* XXX: NYI
+void
+cairo_arc_to (cairo_t *cr,
+             double x1, double y1,
+             double x2, double y2,
+             double radius)
+{
+    cairo_status_t status;
+
+    if (unlikely (cr->status))
+       return;
+
+    status = cr->backend->arc_to (cr, x1, y1, x2, y2, radius);
+    if (unlikely (status))
+       _cairo_set_error (cr, status);
+}
+
+void
+cairo_rel_arc_to (cairo_t *cr,
+             double dx1, double dy1,
+             double dx2, double dy2,
+             double radius)
+{
+    cairo_status_t status;
+
+    if (unlikely (cr->status))
+       return;
+
+    status = cr->backend->rel_arc_to (cr, dx1, dy1, dx2, dy2, radius);
+    if (unlikely (status))
+       _cairo_set_error (cr, status);
+}
+*/
+
+/**
+ * cairo_rel_move_to:
+ * @cr: a cairo context
+ * @dx: the X offset
+ * @dy: the Y offset
+ *
+ * Begin a new sub-path. After this call the current point will offset
+ * by (@x, @y).
+ *
+ * Given a current point of (x, y), cairo_rel_move_to(@cr, @dx, @dy)
+ * is logically equivalent to cairo_move_to(@cr, x + @dx, y + @dy).
+ *
+ * It is an error to call this function with no current point. Doing
+ * so will cause @cr to shutdown with a status of
+ * %CAIRO_STATUS_NO_CURRENT_POINT.
+ **/
+void
+cairo_rel_move_to (cairo_t *cr, double dx, double dy)
+{
+    cairo_status_t status;
+
+    if (unlikely (cr->status))
+       return;
+
+    status = cr->backend->rel_move_to (cr, dx, dy);
+    if (unlikely (status))
+       _cairo_set_error (cr, status);
+}
+
+/**
+ * cairo_rel_line_to:
+ * @cr: a cairo context
+ * @dx: the X offset to the end of the new line
+ * @dy: the Y offset to the end of the new line
+ *
+ * Relative-coordinate version of cairo_line_to(). Adds a line to the
+ * path from the current point to a point that is offset from the
+ * current point by (@dx, @dy) in user space. After this call the
+ * current point will be offset by (@dx, @dy).
+ *
+ * Given a current point of (x, y), cairo_rel_line_to(@cr, @dx, @dy)
+ * is logically equivalent to cairo_line_to(@cr, x + @dx, y + @dy).
+ *
+ * It is an error to call this function with no current point. Doing
+ * so will cause @cr to shutdown with a status of
+ * %CAIRO_STATUS_NO_CURRENT_POINT.
+ **/
+void
+cairo_rel_line_to (cairo_t *cr, double dx, double dy)
+{
+    cairo_status_t status;
+
+    if (unlikely (cr->status))
+       return;
+
+    status = cr->backend->rel_line_to (cr, dx, dy);
+    if (unlikely (status))
+       _cairo_set_error (cr, status);
+}
+slim_hidden_def(cairo_rel_line_to);
+
+/**
+ * cairo_rel_curve_to:
+ * @cr: a cairo context
+ * @dx1: the X offset to the first control point
+ * @dy1: the Y offset to the first control point
+ * @dx2: the X offset to the second control point
+ * @dy2: the Y offset to the second control point
+ * @dx3: the X offset to the end of the curve
+ * @dy3: the Y offset to the end of the curve
+ *
+ * Relative-coordinate version of cairo_curve_to(). All offsets are
+ * relative to the current point. Adds a cubic Bézier spline to the
+ * path from the current point to a point offset from the current
+ * point by (@dx3, @dy3), using points offset by (@dx1, @dy1) and
+ * (@dx2, @dy2) as the control points. After this call the current
+ * point will be offset by (@dx3, @dy3).
+ *
+ * Given a current point of (x, y), cairo_rel_curve_to(@cr, @dx1,
+ * @dy1, @dx2, @dy2, @dx3, @dy3) is logically equivalent to
+ * cairo_curve_to(@cr, x+@dx1, y+@dy1, x+@dx2, y+@dy2, x+@dx3, y+@dy3).
+ *
+ * It is an error to call this function with no current point. Doing
+ * so will cause @cr to shutdown with a status of
+ * %CAIRO_STATUS_NO_CURRENT_POINT.
+ **/
+void
+cairo_rel_curve_to (cairo_t *cr,
+                   double dx1, double dy1,
+                   double dx2, double dy2,
+                   double dx3, double dy3)
+{
+    cairo_status_t status;
+
+    if (unlikely (cr->status))
+       return;
+
+    status = cr->backend->rel_curve_to (cr,
+                                       dx1, dy1,
+                                       dx2, dy2,
+                                       dx3, dy3);
+    if (unlikely (status))
+       _cairo_set_error (cr, status);
+}
+
+/**
+ * cairo_rectangle:
+ * @cr: a cairo context
+ * @x: the X coordinate of the top left corner of the rectangle
+ * @y: the Y coordinate to the top left corner of the rectangle
+ * @width: the width of the rectangle
+ * @height: the height of the rectangle
+ *
+ * Adds a closed sub-path rectangle of the given size to the current
+ * path at position (@x, @y) in user-space coordinates.
+ *
+ * This function is logically equivalent to:
+ * <informalexample><programlisting>
+ * cairo_move_to (cr, x, y);
+ * cairo_rel_line_to (cr, width, 0);
+ * cairo_rel_line_to (cr, 0, height);
+ * cairo_rel_line_to (cr, -width, 0);
+ * cairo_close_path (cr);
+ * </programlisting></informalexample>
+ **/
+void
+cairo_rectangle (cairo_t *cr,
+                double x, double y,
+                double width, double height)
+{
+    cairo_status_t status;
+
+    if (unlikely (cr->status))
+       return;
+
+    status = cr->backend->rectangle (cr, x, y, width, height);
+    if (unlikely (status))
+       _cairo_set_error (cr, status);
+}
+
+#if 0
+/* XXX: NYI */
+void
+cairo_stroke_to_path (cairo_t *cr)
+{
+    cairo_status_t status;
+
+    if (unlikely (cr->status))
+       return;
+
+    /* The code in _cairo_recording_surface_get_path has a poorman's stroke_to_path */
+
+    status = _cairo_gstate_stroke_path (cr->gstate);
+    if (unlikely (status))
+       _cairo_set_error (cr, status);
+}
+#endif
+
+/**
+ * cairo_close_path:
+ * @cr: a cairo context
+ *
+ * Adds a line segment to the path from the current point to the
+ * beginning of the current sub-path, (the most recent point passed to
+ * cairo_move_to()), and closes this sub-path. After this call the
+ * current point will be at the joined endpoint of the sub-path.
+ *
+ * The behavior of cairo_close_path() is distinct from simply calling
+ * cairo_line_to() with the equivalent coordinate in the case of
+ * stroking. When a closed sub-path is stroked, there are no caps on
+ * the ends of the sub-path. Instead, there is a line join connecting
+ * the final and initial segments of the sub-path.
+ *
+ * If there is no current point before the call to cairo_close_path(),
+ * this function will have no effect.
+ *
+ * Note: As of cairo version 1.2.4 any call to cairo_close_path() will
+ * place an explicit MOVE_TO element into the path immediately after
+ * the CLOSE_PATH element, (which can be seen in cairo_copy_path() for
+ * example). This can simplify path processing in some cases as it may
+ * not be necessary to save the "last move_to point" during processing
+ * as the MOVE_TO immediately after the CLOSE_PATH will provide that
+ * point.
+ **/
+void
+cairo_close_path (cairo_t *cr)
+{
+    cairo_status_t status;
+
+    if (unlikely (cr->status))
+       return;
+
+    status = cr->backend->close_path (cr);
+    if (unlikely (status))
+       _cairo_set_error (cr, status);
+}
+slim_hidden_def(cairo_close_path);
+
+/**
+ * cairo_path_extents:
+ * @cr: a cairo context
+ * @x1: left of the resulting extents
+ * @y1: top of the resulting extents
+ * @x2: right of the resulting extents
+ * @y2: bottom of the resulting extents
+ *
+ * Computes a bounding box in user-space coordinates covering the
+ * points on the current path. If the current path is empty, returns
+ * an empty rectangle ((0,0), (0,0)). Stroke parameters, fill rule,
+ * surface dimensions and clipping are not taken into account.
+ *
+ * Contrast with cairo_fill_extents() and cairo_stroke_extents() which
+ * return the extents of only the area that would be "inked" by
+ * the corresponding drawing operations.
+ *
+ * The result of cairo_path_extents() is defined as equivalent to the
+ * limit of cairo_stroke_extents() with %CAIRO_LINE_CAP_ROUND as the
+ * line width approaches 0.0, (but never reaching the empty-rectangle
+ * returned by cairo_stroke_extents() for a line width of 0.0).
+ *
+ * Specifically, this means that zero-area sub-paths such as
+ * cairo_move_to();cairo_line_to() segments, (even degenerate cases
+ * where the coordinates to both calls are identical), will be
+ * considered as contributing to the extents. However, a lone
+ * cairo_move_to() will not contribute to the results of
+ * cairo_path_extents().
+ *
+ * Since: 1.6
+ **/
+void
+cairo_path_extents (cairo_t *cr,
+                   double *x1, double *y1, double *x2, double *y2)
+{
+    if (unlikely (cr->status)) {
+       if (x1)
+           *x1 = 0.0;
+       if (y1)
+           *y1 = 0.0;
+       if (x2)
+           *x2 = 0.0;
+       if (y2)
+           *y2 = 0.0;
+
+       return;
+    }
+
+    cr->backend->path_extents (cr, x1, y1, x2, y2);
+}
+
+/**
+ * cairo_paint:
+ * @cr: a cairo context
+ *
+ * A drawing operator that paints the current source everywhere within
+ * the current clip region.
+ **/
+void
+cairo_paint (cairo_t *cr)
+{
+    cairo_status_t status;
+
+    if (unlikely (cr->status))
+       return;
+
+    status = cr->backend->paint (cr);
+    if (unlikely (status))
+       _cairo_set_error (cr, status);
+}
+slim_hidden_def (cairo_paint);
+
+/**
+ * cairo_paint_with_alpha:
+ * @cr: a cairo context
+ * @alpha: alpha value, between 0 (transparent) and 1 (opaque)
+ *
+ * A drawing operator that paints the current source everywhere within
+ * the current clip region using a mask of constant alpha value
+ * @alpha. The effect is similar to cairo_paint(), but the drawing
+ * is faded out using the alpha value.
+ **/
+void
+cairo_paint_with_alpha (cairo_t *cr,
+                       double   alpha)
+{
+    cairo_status_t status;
+
+    if (unlikely (cr->status))
+       return;
+
+    status = cr->backend->paint_with_alpha (cr, alpha);
+    if (unlikely (status))
+       _cairo_set_error (cr, status);
+}
+
+/**
+ * cairo_mask:
+ * @cr: a cairo context
+ * @pattern: a #cairo_pattern_t
+ *
+ * A drawing operator that paints the current source
+ * using the alpha channel of @pattern as a mask. (Opaque
+ * areas of @pattern are painted with the source, transparent
+ * areas are not painted.)
+ */
+void
+cairo_mask (cairo_t         *cr,
+           cairo_pattern_t *pattern)
+{
+    cairo_status_t status;
+
+    if (unlikely (cr->status))
+       return;
+
+    if (unlikely (pattern == NULL)) {
+       _cairo_set_error (cr, CAIRO_STATUS_NULL_POINTER);
+       return;
+    }
+
+    if (unlikely (pattern->status)) {
+       _cairo_set_error (cr, pattern->status);
+       return;
+    }
+
+    status = cr->backend->mask (cr, pattern);
+    if (unlikely (status))
+       _cairo_set_error (cr, status);
+}
+slim_hidden_def (cairo_mask);
+
+/**
+ * cairo_mask_surface:
+ * @cr: a cairo context
+ * @surface: a #cairo_surface_t
+ * @surface_x: X coordinate at which to place the origin of @surface
+ * @surface_y: Y coordinate at which to place the origin of @surface
+ *
+ * A drawing operator that paints the current source
+ * using the alpha channel of @surface as a mask. (Opaque
+ * areas of @surface are painted with the source, transparent
+ * areas are not painted.)
+ */
+void
+cairo_mask_surface (cairo_t         *cr,
+                   cairo_surface_t *surface,
+                   double           surface_x,
+                   double           surface_y)
+{
+    cairo_pattern_t *pattern;
+    cairo_matrix_t matrix;
+
+    if (unlikely (cr->status))
+       return;
+
+    pattern = cairo_pattern_create_for_surface (surface);
+
+    cairo_matrix_init_translate (&matrix, - surface_x, - surface_y);
+    cairo_pattern_set_matrix (pattern, &matrix);
+
+    cairo_mask (cr, pattern);
+
+    cairo_pattern_destroy (pattern);
+}
+
+/**
+ * cairo_stroke:
+ * @cr: a cairo context
+ *
+ * A drawing operator that strokes the current path according to the
+ * current line width, line join, line cap, and dash settings. After
+ * cairo_stroke(), the current path will be cleared from the cairo
+ * context. See cairo_set_line_width(), cairo_set_line_join(),
+ * cairo_set_line_cap(), cairo_set_dash(), and
+ * cairo_stroke_preserve().
+ *
+ * Note: Degenerate segments and sub-paths are treated specially and
+ * provide a useful result. These can result in two different
+ * situations:
+ *
+ * 1. Zero-length "on" segments set in cairo_set_dash(). If the cap
+ * style is %CAIRO_LINE_CAP_ROUND or %CAIRO_LINE_CAP_SQUARE then these
+ * segments will be drawn as circular dots or squares respectively. In
+ * the case of %CAIRO_LINE_CAP_SQUARE, the orientation of the squares
+ * is determined by the direction of the underlying path.
+ *
+ * 2. A sub-path created by cairo_move_to() followed by either a
+ * cairo_close_path() or one or more calls to cairo_line_to() to the
+ * same coordinate as the cairo_move_to(). If the cap style is
+ * %CAIRO_LINE_CAP_ROUND then these sub-paths will be drawn as circular
+ * dots. Note that in the case of %CAIRO_LINE_CAP_SQUARE a degenerate
+ * sub-path will not be drawn at all, (since the correct orientation
+ * is indeterminate).
+ *
+ * In no case will a cap style of %CAIRO_LINE_CAP_BUTT cause anything
+ * to be drawn in the case of either degenerate segments or sub-paths.
+ **/
+void
+cairo_stroke (cairo_t *cr)
+{
+    cairo_status_t status;
+
+    if (unlikely (cr->status))
+       return;
+
+    status = cr->backend->stroke (cr);
+    if (unlikely (status))
+       _cairo_set_error (cr, status);
+}
+slim_hidden_def(cairo_stroke);
+
+/**
+ * cairo_stroke_preserve:
+ * @cr: a cairo context
+ *
+ * A drawing operator that strokes the current path according to the
+ * current line width, line join, line cap, and dash settings. Unlike
+ * cairo_stroke(), cairo_stroke_preserve() preserves the path within the
+ * cairo context.
+ *
+ * See cairo_set_line_width(), cairo_set_line_join(),
+ * cairo_set_line_cap(), cairo_set_dash(), and
+ * cairo_stroke_preserve().
+ **/
+void
+cairo_stroke_preserve (cairo_t *cr)
+{
+    cairo_status_t status;
+
+    if (unlikely (cr->status))
+       return;
+
+    status = cr->backend->stroke_preserve (cr);
+    if (unlikely (status))
+       _cairo_set_error (cr, status);
+}
+slim_hidden_def(cairo_stroke_preserve);
+
+/**
+ * cairo_fill:
+ * @cr: a cairo context
+ *
+ * A drawing operator that fills the current path according to the
+ * current fill rule, (each sub-path is implicitly closed before being
+ * filled). After cairo_fill(), the current path will be cleared from
+ * the cairo context. See cairo_set_fill_rule() and
+ * cairo_fill_preserve().
+ **/
+void
+cairo_fill (cairo_t *cr)
+{
+    cairo_status_t status;
+
+    if (unlikely (cr->status))
+       return;
+
+    status = cr->backend->fill (cr);
+    if (unlikely (status))
+       _cairo_set_error (cr, status);
+}
+
+/**
+ * cairo_fill_preserve:
+ * @cr: a cairo context
+ *
+ * A drawing operator that fills the current path according to the
+ * current fill rule, (each sub-path is implicitly closed before being
+ * filled). Unlike cairo_fill(), cairo_fill_preserve() preserves the
+ * path within the cairo context.
+ *
+ * See cairo_set_fill_rule() and cairo_fill().
+ **/
+void
+cairo_fill_preserve (cairo_t *cr)
+{
+    cairo_status_t status;
+
+    if (unlikely (cr->status))
+       return;
+
+    status = cr->backend->fill_preserve (cr);
+    if (unlikely (status))
+       _cairo_set_error (cr, status);
+}
+slim_hidden_def(cairo_fill_preserve);
+
+/**
+ * cairo_copy_page:
+ * @cr: a cairo context
+ *
+ * Emits the current page for backends that support multiple pages, but
+ * doesn't clear it, so, the contents of the current page will be retained
+ * for the next page too.  Use cairo_show_page() if you want to get an
+ * empty page after the emission.
+ *
+ * This is a convenience function that simply calls
+ * cairo_surface_copy_page() on @cr's target.
+ **/
+void
+cairo_copy_page (cairo_t *cr)
+{
+    cairo_status_t status;
+
+    if (unlikely (cr->status))
+       return;
+
+    status = cr->backend->copy_page (cr);
+    if (unlikely (status))
+       _cairo_set_error (cr, status);
+}
+
+/**
+ * cairo_show_page:
+ * @cr: a cairo context
+ *
+ * Emits and clears the current page for backends that support multiple
+ * pages.  Use cairo_copy_page() if you don't want to clear the page.
+ *
+ * This is a convenience function that simply calls
+ * cairo_surface_show_page() on @cr's target.
+ **/
+void
+cairo_show_page (cairo_t *cr)
+{
+    cairo_status_t status;
+
+    if (unlikely (cr->status))
+       return;
+
+    status = cr->backend->show_page (cr);
+    if (unlikely (status))
+       _cairo_set_error (cr, status);
+}
+
+/**
+ * cairo_in_stroke:
+ * @cr: a cairo context
+ * @x: X coordinate of the point to test
+ * @y: Y coordinate of the point to test
+ *
+ * Tests whether the given point is inside the area that would be
+ * affected by a cairo_stroke() operation given the current path and
+ * stroking parameters. Surface dimensions and clipping are not taken
+ * into account.
+ *
+ * See cairo_stroke(), cairo_set_line_width(), cairo_set_line_join(),
+ * cairo_set_line_cap(), cairo_set_dash(), and
+ * cairo_stroke_preserve().
+ *
+ * Return value: A non-zero value if the point is inside, or zero if
+ * outside.
+ **/
+cairo_bool_t
+cairo_in_stroke (cairo_t *cr, double x, double y)
+{
+    cairo_status_t status;
+    cairo_bool_t inside = FALSE;
+
+    if (unlikely (cr->status))
+       return FALSE;
+
+    status = cr->backend->in_stroke (cr, x, y, &inside);
+    if (unlikely (status))
+       _cairo_set_error (cr, status);
+
+    return inside;
+}
+
+/**
+ * cairo_in_fill:
+ * @cr: a cairo context
+ * @x: X coordinate of the point to test
+ * @y: Y coordinate of the point to test
+ *
+ * Tests whether the given point is inside the area that would be
+ * affected by a cairo_fill() operation given the current path and
+ * filling parameters. Surface dimensions and clipping are not taken
+ * into account.
+ *
+ * See cairo_fill(), cairo_set_fill_rule() and cairo_fill_preserve().
+ *
+ * Return value: A non-zero value if the point is inside, or zero if
+ * outside.
+ **/
+cairo_bool_t
+cairo_in_fill (cairo_t *cr, double x, double y)
+{
+    cairo_status_t status;
+    cairo_bool_t inside = FALSE;
+
+    if (unlikely (cr->status))
+       return FALSE;
+
+    status = cr->backend->in_fill (cr, x, y, &inside);
+    if (unlikely (status))
+       _cairo_set_error (cr, status);
+
+    return inside;
+}
+
+/**
+ * cairo_stroke_extents:
+ * @cr: a cairo context
+ * @x1: left of the resulting extents
+ * @y1: top of the resulting extents
+ * @x2: right of the resulting extents
+ * @y2: bottom of the resulting extents
+ *
+ * Computes a bounding box in user coordinates covering the area that
+ * would be affected, (the "inked" area), by a cairo_stroke()
+ * operation given the current path and stroke parameters.
+ * If the current path is empty, returns an empty rectangle ((0,0), (0,0)).
+ * Surface dimensions and clipping are not taken into account.
+ *
+ * Note that if the line width is set to exactly zero, then
+ * cairo_stroke_extents() will return an empty rectangle. Contrast with
+ * cairo_path_extents() which can be used to compute the non-empty
+ * bounds as the line width approaches zero.
+ *
+ * Note that cairo_stroke_extents() must necessarily do more work to
+ * compute the precise inked areas in light of the stroke parameters,
+ * so cairo_path_extents() may be more desirable for sake of
+ * performance if non-inked path extents are desired.
+ *
+ * See cairo_stroke(), cairo_set_line_width(), cairo_set_line_join(),
+ * cairo_set_line_cap(), cairo_set_dash(), and
+ * cairo_stroke_preserve().
+ **/
+void
+cairo_stroke_extents (cairo_t *cr,
+                      double *x1, double *y1, double *x2, double *y2)
+{
+    cairo_status_t status;
+
+    if (unlikely (cr->status)) {
+       if (x1)
+           *x1 = 0.0;
+       if (y1)
+           *y1 = 0.0;
+       if (x2)
+           *x2 = 0.0;
+       if (y2)
+           *y2 = 0.0;
+
+       return;
+    }
+
+    status = cr->backend->stroke_extents (cr, x1, y1, x2, y2);
+    if (unlikely (status))
+       _cairo_set_error (cr, status);
+}
+
+/**
+ * cairo_fill_extents:
+ * @cr: a cairo context
+ * @x1: left of the resulting extents
+ * @y1: top of the resulting extents
+ * @x2: right of the resulting extents
+ * @y2: bottom of the resulting extents
+ *
+ * Computes a bounding box in user coordinates covering the area that
+ * would be affected, (the "inked" area), by a cairo_fill() operation
+ * given the current path and fill parameters. If the current path is
+ * empty, returns an empty rectangle ((0,0), (0,0)). Surface
+ * dimensions and clipping are not taken into account.
+ *
+ * Contrast with cairo_path_extents(), which is similar, but returns
+ * non-zero extents for some paths with no inked area, (such as a
+ * simple line segment).
+ *
+ * Note that cairo_fill_extents() must necessarily do more work to
+ * compute the precise inked areas in light of the fill rule, so
+ * cairo_path_extents() may be more desirable for sake of performance
+ * if the non-inked path extents are desired.
+ *
+ * See cairo_fill(), cairo_set_fill_rule() and cairo_fill_preserve().
+ **/
+void
+cairo_fill_extents (cairo_t *cr,
+                    double *x1, double *y1, double *x2, double *y2)
+{
+    cairo_status_t status;
+
+    if (unlikely (cr->status)) {
+       if (x1)
+           *x1 = 0.0;
+       if (y1)
+           *y1 = 0.0;
+       if (x2)
+           *x2 = 0.0;
+       if (y2)
+           *y2 = 0.0;
+
+       return;
+    }
+
+    status = cr->backend->fill_extents (cr, x1, y1, x2, y2);
+    if (unlikely (status))
+       _cairo_set_error (cr, status);
+}
+
+/**
+ * cairo_clip:
+ * @cr: a cairo context
+ *
+ * Establishes a new clip region by intersecting the current clip
+ * region with the current path as it would be filled by cairo_fill()
+ * and according to the current fill rule (see cairo_set_fill_rule()).
+ *
+ * After cairo_clip(), the current path will be cleared from the cairo
+ * context.
+ *
+ * The current clip region affects all drawing operations by
+ * effectively masking out any changes to the surface that are outside
+ * the current clip region.
+ *
+ * Calling cairo_clip() can only make the clip region smaller, never
+ * larger. But the current clip is part of the graphics state, so a
+ * temporary restriction of the clip region can be achieved by
+ * calling cairo_clip() within a cairo_save()/cairo_restore()
+ * pair. The only other means of increasing the size of the clip
+ * region is cairo_reset_clip().
+ **/
+void
+cairo_clip (cairo_t *cr)
+{
+    cairo_status_t status;
+
+    if (unlikely (cr->status))
+       return;
+
+    status = cr->backend->clip (cr);
+    if (unlikely (status))
+       _cairo_set_error (cr, status);
+}
+
+/**
+ * cairo_clip_preserve:
+ * @cr: a cairo context
+ *
+ * Establishes a new clip region by intersecting the current clip
+ * region with the current path as it would be filled by cairo_fill()
+ * and according to the current fill rule (see cairo_set_fill_rule()).
+ *
+ * Unlike cairo_clip(), cairo_clip_preserve() preserves the path within
+ * the cairo context.
+ *
+ * The current clip region affects all drawing operations by
+ * effectively masking out any changes to the surface that are outside
+ * the current clip region.
+ *
+ * Calling cairo_clip_preserve() can only make the clip region smaller, never
+ * larger. But the current clip is part of the graphics state, so a
+ * temporary restriction of the clip region can be achieved by
+ * calling cairo_clip_preserve() within a cairo_save()/cairo_restore()
+ * pair. The only other means of increasing the size of the clip
+ * region is cairo_reset_clip().
+ **/
+void
+cairo_clip_preserve (cairo_t *cr)
+{
+    cairo_status_t status;
+
+    if (unlikely (cr->status))
+       return;
+
+    status = cr->backend->clip_preserve (cr);
+    if (unlikely (status))
+       _cairo_set_error (cr, status);
+}
+slim_hidden_def(cairo_clip_preserve);
+
+/**
+ * cairo_reset_clip:
+ * @cr: a cairo context
+ *
+ * Reset the current clip region to its original, unrestricted
+ * state. That is, set the clip region to an infinitely large shape
+ * containing the target surface. Equivalently, if infinity is too
+ * hard to grasp, one can imagine the clip region being reset to the
+ * exact bounds of the target surface.
+ *
+ * Note that code meant to be reusable should not call
+ * cairo_reset_clip() as it will cause results unexpected by
+ * higher-level code which calls cairo_clip(). Consider using
+ * cairo_save() and cairo_restore() around cairo_clip() as a more
+ * robust means of temporarily restricting the clip region.
+ **/
+void
+cairo_reset_clip (cairo_t *cr)
+{
+    cairo_status_t status;
+
+    if (unlikely (cr->status))
+       return;
+
+    status = cr->backend->reset_clip (cr);
+    if (unlikely (status))
+       _cairo_set_error (cr, status);
+}
+
+/**
+ * cairo_clip_extents:
+ * @cr: a cairo context
+ * @x1: left of the resulting extents
+ * @y1: top of the resulting extents
+ * @x2: right of the resulting extents
+ * @y2: bottom of the resulting extents
+ *
+ * Computes a bounding box in user coordinates covering the area inside the
+ * current clip.
+ *
+ * Since: 1.4
+ **/
+void
+cairo_clip_extents (cairo_t *cr,
+                   double *x1, double *y1,
+                   double *x2, double *y2)
+{
+    cairo_status_t status;
+
+    if (x1)
+       *x1 = 0.0;
+    if (y1)
+       *y1 = 0.0;
+    if (x2)
+       *x2 = 0.0;
+    if (y2)
+       *y2 = 0.0;
+
+    if (unlikely (cr->status))
+       return;
+
+    status = cr->backend->clip_extents (cr, x1, y1, x2, y2);
+    if (unlikely (status))
+       _cairo_set_error (cr, status);
+}
+
+/**
+ * cairo_in_clip:
+ * @cr: a cairo context
+ * @x: X coordinate of the point to test
+ * @y: Y coordinate of the point to test
+ *
+ * Tests whether the given point is inside the area that would be
+ * visible through the current clip, i.e. the area that would be filled by
+ * a cairo_paint() operation.
+ *
+ * See cairo_clip(), and cairo_clip_preserve().
+ *
+ * Return value: A non-zero value if the point is inside, or zero if
+ * outside.
+ *
+ * Since: 1.10
+ **/
+cairo_bool_t
+cairo_in_clip (cairo_t *cr, double x, double y)
+{
+    cairo_status_t status;
+    cairo_bool_t inside = FALSE;
+
+    if (unlikely (cr->status))
+       return FALSE;
+
+    status = cr->backend->in_clip (cr, x, y, &inside);
+    if (unlikely (status))
+       _cairo_set_error (cr, status);
+
+    return inside;
+}
+
+/**
+ * cairo_copy_clip_rectangle_list:
+ * @cr: a cairo context
+ *
+ * Gets the current clip region as a list of rectangles in user coordinates.
+ * Never returns %NULL.
+ *
+ * The status in the list may be %CAIRO_STATUS_CLIP_NOT_REPRESENTABLE to
+ * indicate that the clip region cannot be represented as a list of
+ * user-space rectangles. The status may have other values to indicate
+ * other errors.
+ *
+ * Returns: the current clip region as a list of rectangles in user coordinates,
+ * which should be destroyed using cairo_rectangle_list_destroy().
+ *
+ * Since: 1.4
+ **/
+cairo_rectangle_list_t *
+cairo_copy_clip_rectangle_list (cairo_t *cr)
+{
+    if (unlikely (cr->status))
+        return _cairo_rectangle_list_create_in_error (cr->status);
+
+    return cr->backend->clip_copy_rectangle_list (cr);
+}
+
+/**
+ * cairo_select_font_face:
+ * @cr: a #cairo_t
+ * @family: a font family name, encoded in UTF-8
+ * @slant: the slant for the font
+ * @weight: the weight for the font
+ *
+ * Note: The cairo_select_font_face() function call is part of what
+ * the cairo designers call the "toy" text API. It is convenient for
+ * short demos and simple programs, but it is not expected to be
+ * adequate for serious text-using applications.
+ *
+ * Selects a family and style of font from a simplified description as
+ * a family name, slant and weight. Cairo provides no operation to
+ * list available family names on the system (this is a "toy",
+ * remember), but the standard CSS2 generic family names, ("serif",
+ * "sans-serif", "cursive", "fantasy", "monospace"), are likely to
+ * work as expected.
+ *
+ * If @family starts with the string "@cairo:", or if no native font
+ * backends are compiled in, cairo will use an internal font family.
+ * The internal font family recognizes many modifiers in the @family
+ * string, most notably, it recognizes the string "monospace".  That is,
+ * the family name "@cairo:monospace" will use the monospace version of
+ * the internal font family.
+ *
+ * For "real" font selection, see the font-backend-specific
+ * font_face_create functions for the font backend you are using. (For
+ * example, if you are using the freetype-based cairo-ft font backend,
+ * see cairo_ft_font_face_create_for_ft_face() or
+ * cairo_ft_font_face_create_for_pattern().) The resulting font face
+ * could then be used with cairo_scaled_font_create() and
+ * cairo_set_scaled_font().
+ *
+ * Similarly, when using the "real" font support, you can call
+ * directly into the underlying font system, (such as fontconfig or
+ * freetype), for operations such as listing available fonts, etc.
+ *
+ * It is expected that most applications will need to use a more
+ * comprehensive font handling and text layout library, (for example,
+ * pango), in conjunction with cairo.
+ *
+ * If text is drawn without a call to cairo_select_font_face(), (nor
+ * cairo_set_font_face() nor cairo_set_scaled_font()), the default
+ * family is platform-specific, but is essentially "sans-serif".
+ * Default slant is %CAIRO_FONT_SLANT_NORMAL, and default weight is
+ * %CAIRO_FONT_WEIGHT_NORMAL.
+ *
+ * This function is equivalent to a call to cairo_toy_font_face_create()
+ * followed by cairo_set_font_face().
+ **/
+void
+cairo_select_font_face (cairo_t              *cr,
+                       const char           *family,
+                       cairo_font_slant_t    slant,
+                       cairo_font_weight_t   weight)
+{
+    cairo_font_face_t *font_face;
+    cairo_status_t status;
+
+    if (unlikely (cr->status))
+       return;
+
+    font_face = cairo_toy_font_face_create (family, slant, weight);
+    if (unlikely (font_face->status)) {
+       _cairo_set_error (cr, font_face->status);
+       return;
+    }
+
+    status = cr->backend->set_font_face (cr, font_face);
+    cairo_font_face_destroy (font_face);
+
+    if (unlikely (status))
+       _cairo_set_error (cr, status);
+}
+
+/**
+ * cairo_font_extents:
+ * @cr: a #cairo_t
+ * @extents: a #cairo_font_extents_t object into which the results
+ * will be stored.
+ *
+ * Gets the font extents for the currently selected font.
+ **/
+void
+cairo_font_extents (cairo_t              *cr,
+                   cairo_font_extents_t *extents)
+{
+    cairo_status_t status;
+
+    extents->ascent = 0.0;
+    extents->descent = 0.0;
+    extents->height = 0.0;
+    extents->max_x_advance = 0.0;
+    extents->max_y_advance = 0.0;
+
+    if (unlikely (cr->status))
+       return;
+
+    status = cr->backend->font_extents (cr, extents);
+    if (unlikely (status))
+       _cairo_set_error (cr, status);
+}
+
+/**
+ * cairo_set_font_face:
+ * @cr: a #cairo_t
+ * @font_face: a #cairo_font_face_t, or %NULL to restore to the default font
+ *
+ * Replaces the current #cairo_font_face_t object in the #cairo_t with
+ * @font_face. The replaced font face in the #cairo_t will be
+ * destroyed if there are no other references to it.
+ **/
+void
+cairo_set_font_face (cairo_t           *cr,
+                    cairo_font_face_t *font_face)
+{
+    cairo_status_t status;
+
+    if (unlikely (cr->status))
+       return;
+
+    status = cr->backend->set_font_face (cr, font_face);
+    if (unlikely (status))
+       _cairo_set_error (cr, status);
+}
+
+/**
+ * cairo_get_font_face:
+ * @cr: a #cairo_t
+ *
+ * Gets the current font face for a #cairo_t.
+ *
+ * Return value: the current font face.  This object is owned by
+ * cairo. To keep a reference to it, you must call
+ * cairo_font_face_reference().
+ *
+ * This function never returns %NULL. If memory cannot be allocated, a
+ * special "nil" #cairo_font_face_t object will be returned on which
+ * cairo_font_face_status() returns %CAIRO_STATUS_NO_MEMORY. Using
+ * this nil object will cause its error state to propagate to other
+ * objects it is passed to, (for example, calling
+ * cairo_set_font_face() with a nil font will trigger an error that
+ * will shutdown the #cairo_t object).
+ **/
+cairo_font_face_t *
+cairo_get_font_face (cairo_t *cr)
+{
+    if (unlikely (cr->status))
+       return (cairo_font_face_t*) &_cairo_font_face_nil;
+
+    return cr->backend->get_font_face (cr);
+}
+
+/**
+ * cairo_set_font_size:
+ * @cr: a #cairo_t
+ * @size: the new font size, in user space units
+ *
+ * Sets the current font matrix to a scale by a factor of @size, replacing
+ * any font matrix previously set with cairo_set_font_size() or
+ * cairo_set_font_matrix(). This results in a font size of @size user space
+ * units. (More precisely, this matrix will result in the font's
+ * em-square being a @size by @size square in user space.)
+ *
+ * If text is drawn without a call to cairo_set_font_size(), (nor
+ * cairo_set_font_matrix() nor cairo_set_scaled_font()), the default
+ * font size is 10.0.
+ **/
+void
+cairo_set_font_size (cairo_t *cr, double size)
+{
+    cairo_status_t status;
+
+    if (unlikely (cr->status))
+       return;
+
+    status = cr->backend->set_font_size (cr, size);
+    if (unlikely (status))
+       _cairo_set_error (cr, status);
+}
+slim_hidden_def (cairo_set_font_size);
+
+/**
+ * cairo_set_font_matrix
+ * @cr: a #cairo_t
+ * @matrix: a #cairo_matrix_t describing a transform to be applied to
+ * the current font.
+ *
+ * Sets the current font matrix to @matrix. The font matrix gives a
+ * transformation from the design space of the font (in this space,
+ * the em-square is 1 unit by 1 unit) to user space. Normally, a
+ * simple scale is used (see cairo_set_font_size()), but a more
+ * complex font matrix can be used to shear the font
+ * or stretch it unequally along the two axes
+ **/
+void
+cairo_set_font_matrix (cairo_t             *cr,
+                      const cairo_matrix_t *matrix)
+{
+    cairo_status_t status;
+
+    if (unlikely (cr->status))
+       return;
+
+    status = cr->backend->set_font_matrix (cr, matrix);
+    if (unlikely (status))
+       _cairo_set_error (cr, status);
+}
+slim_hidden_def (cairo_set_font_matrix);
+
+/**
+ * cairo_get_font_matrix
+ * @cr: a #cairo_t
+ * @matrix: return value for the matrix
+ *
+ * Stores the current font matrix into @matrix. See
+ * cairo_set_font_matrix().
+ **/
+void
+cairo_get_font_matrix (cairo_t *cr, cairo_matrix_t *matrix)
+{
+    if (unlikely (cr->status)) {
+       cairo_matrix_init_identity (matrix);
+       return;
+    }
+
+    cr->backend->get_font_matrix (cr, matrix);
+}
+
+/**
+ * cairo_set_font_options:
+ * @cr: a #cairo_t
+ * @options: font options to use
+ *
+ * Sets a set of custom font rendering options for the #cairo_t.
+ * Rendering options are derived by merging these options with the
+ * options derived from underlying surface; if the value in @options
+ * has a default value (like %CAIRO_ANTIALIAS_DEFAULT), then the value
+ * from the surface is used.
+ **/
+void
+cairo_set_font_options (cairo_t                    *cr,
+                       const cairo_font_options_t *options)
+{
+    cairo_status_t status;
+
+    if (unlikely (cr->status))
+       return;
+
+    status = cairo_font_options_status ((cairo_font_options_t *) options);
+    if (unlikely (status)) {
+       _cairo_set_error (cr, status);
+       return;
+    }
+
+    status = cr->backend->set_font_options (cr, options);
+    if (unlikely (status))
+       _cairo_set_error (cr, status);
+}
+slim_hidden_def (cairo_set_font_options);
+
+/**
+ * cairo_get_font_options:
+ * @cr: a #cairo_t
+ * @options: a #cairo_font_options_t object into which to store
+ *   the retrieved options. All existing values are overwritten
+ *
+ * Retrieves font rendering options set via #cairo_set_font_options.
+ * Note that the returned options do not include any options derived
+ * from the underlying surface; they are literally the options
+ * passed to cairo_set_font_options().
+ **/
+void
+cairo_get_font_options (cairo_t              *cr,
+                       cairo_font_options_t *options)
+{
+    /* check that we aren't trying to overwrite the nil object */
+    if (cairo_font_options_status (options))
+       return;
+
+    if (unlikely (cr->status)) {
+       _cairo_font_options_init_default (options);
+       return;
+    }
+
+    cr->backend->get_font_options (cr, options);
+}
+
+/**
+ * cairo_set_scaled_font:
+ * @cr: a #cairo_t
+ * @scaled_font: a #cairo_scaled_font_t
+ *
+ * Replaces the current font face, font matrix, and font options in
+ * the #cairo_t with those of the #cairo_scaled_font_t.  Except for
+ * some translation, the current CTM of the #cairo_t should be the
+ * same as that of the #cairo_scaled_font_t, which can be accessed
+ * using cairo_scaled_font_get_ctm().
+ *
+ * Since: 1.2
+ **/
+void
+cairo_set_scaled_font (cairo_t                   *cr,
+                      const cairo_scaled_font_t *scaled_font)
+{
+    cairo_status_t status;
+
+    if (unlikely (cr->status))
+       return;
+
+    if ((scaled_font == NULL)) {
+       _cairo_set_error (cr, _cairo_error (CAIRO_STATUS_NULL_POINTER));
+       return;
+    }
+
+    status = scaled_font->status;
+    if (unlikely (status)) {
+       _cairo_set_error (cr, status);
+       return;
+    }
+
+    status = cr->backend->set_scaled_font (cr, (cairo_scaled_font_t *) scaled_font);
+    if (unlikely (status))
+       _cairo_set_error (cr, status);
+}
+
+/**
+ * cairo_get_scaled_font:
+ * @cr: a #cairo_t
+ *
+ * Gets the current scaled font for a #cairo_t.
+ *
+ * Return value: the current scaled font. This object is owned by
+ * cairo. To keep a reference to it, you must call
+ * cairo_scaled_font_reference().
+ *
+ * This function never returns %NULL. If memory cannot be allocated, a
+ * special "nil" #cairo_scaled_font_t object will be returned on which
+ * cairo_scaled_font_status() returns %CAIRO_STATUS_NO_MEMORY. Using
+ * this nil object will cause its error state to propagate to other
+ * objects it is passed to, (for example, calling
+ * cairo_set_scaled_font() with a nil font will trigger an error that
+ * will shutdown the #cairo_t object).
+ *
+ * Since: 1.4
+ **/
+cairo_scaled_font_t *
+cairo_get_scaled_font (cairo_t *cr)
+{
+    if (unlikely (cr->status))
+       return _cairo_scaled_font_create_in_error (cr->status);
+
+    return cr->backend->get_scaled_font (cr);
+}
+slim_hidden_def (cairo_get_scaled_font);
+
+/**
+ * cairo_text_extents:
+ * @cr: a #cairo_t
+ * @utf8: a NUL-terminated string of text encoded in UTF-8, or %NULL
+ * @extents: a #cairo_text_extents_t object into which the results
+ * will be stored
+ *
+ * Gets the extents for a string of text. The extents describe a
+ * user-space rectangle that encloses the "inked" portion of the text,
+ * (as it would be drawn by cairo_show_text()). Additionally, the
+ * x_advance and y_advance values indicate the amount by which the
+ * current point would be advanced by cairo_show_text().
+ *
+ * Note that whitespace characters do not directly contribute to the
+ * size of the rectangle (extents.width and extents.height). They do
+ * contribute indirectly by changing the position of non-whitespace
+ * characters. In particular, trailing whitespace characters are
+ * likely to not affect the size of the rectangle, though they will
+ * affect the x_advance and y_advance values.
+ **/
+void
+cairo_text_extents (cairo_t              *cr,
+                   const char           *utf8,
+                   cairo_text_extents_t *extents)
+{
+    cairo_status_t status;
+    cairo_scaled_font_t *scaled_font;
+    cairo_glyph_t *glyphs = NULL;
+    int num_glyphs = 0;
+    double x, y;
+
+    extents->x_bearing = 0.0;
+    extents->y_bearing = 0.0;
+    extents->width  = 0.0;
+    extents->height = 0.0;
+    extents->x_advance = 0.0;
+    extents->y_advance = 0.0;
+
+    if (unlikely (cr->status))
+       return;
+
+    if (utf8 == NULL)
+       return;
+
+    scaled_font = cairo_get_scaled_font (cr);
+    if (unlikely (scaled_font->status)) {
+       _cairo_set_error (cr, scaled_font->status);
+       return;
+    }
+
+    cairo_get_current_point (cr, &x, &y);
+    status = cairo_scaled_font_text_to_glyphs (scaled_font,
+                                              x, y,
+                                              utf8, -1,
+                                              &glyphs, &num_glyphs,
+                                              NULL, NULL, NULL);
+
+    if (likely (status == CAIRO_STATUS_SUCCESS)) {
+       status = cr->backend->glyph_extents (cr,
+                                            glyphs, num_glyphs,
+                                            extents);
+    }
+    cairo_glyph_free (glyphs);
+
+    if (unlikely (status))
+       _cairo_set_error (cr, status);
+}
+
+/**
+ * cairo_glyph_extents:
+ * @cr: a #cairo_t
+ * @glyphs: an array of #cairo_glyph_t objects
+ * @num_glyphs: the number of elements in @glyphs
+ * @extents: a #cairo_text_extents_t object into which the results
+ * will be stored
+ *
+ * Gets the extents for an array of glyphs. The extents describe a
+ * user-space rectangle that encloses the "inked" portion of the
+ * glyphs, (as they would be drawn by cairo_show_glyphs()).
+ * Additionally, the x_advance and y_advance values indicate the
+ * amount by which the current point would be advanced by
+ * cairo_show_glyphs().
+ *
+ * Note that whitespace glyphs do not contribute to the size of the
+ * rectangle (extents.width and extents.height).
+ **/
+void
+cairo_glyph_extents (cairo_t                *cr,
+                    const cairo_glyph_t    *glyphs,
+                    int                    num_glyphs,
+                    cairo_text_extents_t   *extents)
+{
+    cairo_status_t status;
+
+    extents->x_bearing = 0.0;
+    extents->y_bearing = 0.0;
+    extents->width  = 0.0;
+    extents->height = 0.0;
+    extents->x_advance = 0.0;
+    extents->y_advance = 0.0;
+
+    if (unlikely (cr->status))
+       return;
+
+    if (num_glyphs == 0)
+       return;
+
+    if (unlikely (num_glyphs < 0)) {
+       _cairo_set_error (cr, CAIRO_STATUS_NEGATIVE_COUNT);
+       return;
+    }
+
+    if (unlikely (glyphs == NULL)) {
+       _cairo_set_error (cr, CAIRO_STATUS_NULL_POINTER);
+       return;
+    }
+
+    status = cr->backend->glyph_extents (cr, glyphs, num_glyphs, extents);
+    if (unlikely (status))
+       _cairo_set_error (cr, status);
+}
+
+/**
+ * cairo_show_text:
+ * @cr: a cairo context
+ * @utf8: a NUL-terminated string of text encoded in UTF-8, or %NULL
+ *
+ * A drawing operator that generates the shape from a string of UTF-8
+ * characters, rendered according to the current font_face, font_size
+ * (font_matrix), and font_options.
+ *
+ * This function first computes a set of glyphs for the string of
+ * text. The first glyph is placed so that its origin is at the
+ * current point. The origin of each subsequent glyph is offset from
+ * that of the previous glyph by the advance values of the previous
+ * glyph.
+ *
+ * After this call the current point is moved to the origin of where
+ * the next glyph would be placed in this same progression. That is,
+ * the current point will be at the origin of the final glyph offset
+ * by its advance values. This allows for easy display of a single
+ * logical string with multiple calls to cairo_show_text().
+ *
+ * Note: The cairo_show_text() function call is part of what the cairo
+ * designers call the "toy" text API. It is convenient for short demos
+ * and simple programs, but it is not expected to be adequate for
+ * serious text-using applications. See cairo_show_glyphs() for the
+ * "real" text display API in cairo.
+ **/
+void
+cairo_show_text (cairo_t *cr, const char *utf8)
+{
+    cairo_text_extents_t extents;
+    cairo_status_t status;
+    cairo_glyph_t *glyphs, *last_glyph;
+    cairo_text_cluster_t *clusters;
+    int utf8_len, num_glyphs, num_clusters;
+    cairo_text_cluster_flags_t cluster_flags;
+    double x, y;
+    cairo_bool_t has_show_text_glyphs;
+    cairo_glyph_t stack_glyphs[CAIRO_STACK_ARRAY_LENGTH (cairo_glyph_t)];
+    cairo_text_cluster_t stack_clusters[CAIRO_STACK_ARRAY_LENGTH (cairo_text_cluster_t)];
+    cairo_scaled_font_t *scaled_font;
+    cairo_glyph_text_info_t info, *i;
+
+    if (unlikely (cr->status))
+       return;
+
+    if (utf8 == NULL)
+       return;
+
+    scaled_font = cairo_get_scaled_font (cr);
+    if (unlikely (scaled_font->status)) {
+       _cairo_set_error (cr, scaled_font->status);
+       return;
+    }
+
+    utf8_len = strlen (utf8);
+
+    has_show_text_glyphs =
+       cairo_surface_has_show_text_glyphs (cairo_get_target (cr));
+
+    glyphs = stack_glyphs;
+    num_glyphs = ARRAY_LENGTH (stack_glyphs);
+
+    if (has_show_text_glyphs) {
+       clusters = stack_clusters;
+       num_clusters = ARRAY_LENGTH (stack_clusters);
+    } else {
+       clusters = NULL;
+       num_clusters = 0;
+    }
+
+    cairo_get_current_point (cr, &x, &y);
+    status = cairo_scaled_font_text_to_glyphs (scaled_font,
+                                              x, y,
+                                              utf8, utf8_len,
+                                              &glyphs, &num_glyphs,
+                                              has_show_text_glyphs ? &clusters : NULL, &num_clusters,
+                                              &cluster_flags);
+    if (unlikely (status))
+       goto BAIL;
+
+    if (num_glyphs == 0)
+       return;
+
+    i = NULL;
+    if (has_show_text_glyphs) {
+       info.utf8 = utf8;
+       info.utf8_len = utf8_len;
+       info.clusters = clusters;
+       info.num_clusters = num_clusters;
+       info.cluster_flags = cluster_flags;
+       i = &info;
+    }
+
+    status = cr->backend->glyphs (cr, glyphs, num_glyphs, i);
+    if (unlikely (status))
+       goto BAIL;
+
+    last_glyph = &glyphs[num_glyphs - 1];
+    status = cr->backend->glyph_extents (cr, last_glyph, 1, &extents);
+    if (unlikely (status))
+       goto BAIL;
+
+    x = last_glyph->x + extents.x_advance;
+    y = last_glyph->y + extents.y_advance;
+    cr->backend->move_to (cr, x, y);
+
+ BAIL:
+    if (glyphs != stack_glyphs)
+       cairo_glyph_free (glyphs);
+    if (clusters != stack_clusters)
+       cairo_text_cluster_free (clusters);
+
+    if (unlikely (status))
+       _cairo_set_error (cr, status);
+}
+
+/**
+ * cairo_show_glyphs:
+ * @cr: a cairo context
+ * @glyphs: array of glyphs to show
+ * @num_glyphs: number of glyphs to show
+ *
+ * A drawing operator that generates the shape from an array of glyphs,
+ * rendered according to the current font face, font size
+ * (font matrix), and font options.
+ **/
+void
+cairo_show_glyphs (cairo_t *cr, const cairo_glyph_t *glyphs, int num_glyphs)
+{
+    cairo_status_t status;
+
+    if (unlikely (cr->status))
+       return;
+
+    if (num_glyphs == 0)
+       return;
+
+    if (num_glyphs < 0) {
+       _cairo_set_error (cr, CAIRO_STATUS_NEGATIVE_COUNT);
+       return;
+    }
+
+    if (glyphs == NULL) {
+       _cairo_set_error (cr, CAIRO_STATUS_NULL_POINTER);
+       return;
+    }
+
+    status = cr->backend->glyphs (cr, glyphs, num_glyphs, NULL);
+    if (unlikely (status))
+       _cairo_set_error (cr, status);
+}
+
+/**
+ * cairo_show_text_glyphs:
+ * @cr: a cairo context
+ * @utf8: a string of text encoded in UTF-8
+ * @utf8_len: length of @utf8 in bytes, or -1 if it is NUL-terminated
+ * @glyphs: array of glyphs to show
+ * @num_glyphs: number of glyphs to show
+ * @clusters: array of cluster mapping information
+ * @num_clusters: number of clusters in the mapping
+ * @cluster_flags: cluster mapping flags
+ *
+ * This operation has rendering effects similar to cairo_show_glyphs()
+ * but, if the target surface supports it, uses the provided text and
+ * cluster mapping to embed the text for the glyphs shown in the output.
+ * If the target does not support the extended attributes, this function
+ * acts like the basic cairo_show_glyphs() as if it had been passed
+ * @glyphs and @num_glyphs.
+ *
+ * The mapping between @utf8 and @glyphs is provided by an array of
+ * <firstterm>clusters</firstterm>.  Each cluster covers a number of
+ * text bytes and glyphs, and neighboring clusters cover neighboring
+ * areas of @utf8 and @glyphs.  The clusters should collectively cover @utf8
+ * and @glyphs in entirety.
+ *
+ * The first cluster always covers bytes from the beginning of @utf8.
+ * If @cluster_flags do not have the %CAIRO_TEXT_CLUSTER_FLAG_BACKWARD
+ * set, the first cluster also covers the beginning
+ * of @glyphs, otherwise it covers the end of the @glyphs array and
+ * following clusters move backward.
+ *
+ * See #cairo_text_cluster_t for constraints on valid clusters.
+ *
+ * Since: 1.8
+ **/
+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  cluster_flags)
+{
+    cairo_status_t status;
+
+    if (unlikely (cr->status))
+       return;
+
+    /* A slew of sanity checks */
+
+    /* Special case for NULL and -1 */
+    if (utf8 == NULL && utf8_len == -1)
+       utf8_len = 0;
+
+    /* No NULLs for non-zeros */
+    if ((num_glyphs   && glyphs   == NULL) ||
+       (utf8_len     && utf8     == NULL) ||
+       (num_clusters && clusters == NULL)) {
+       _cairo_set_error (cr, CAIRO_STATUS_NULL_POINTER);
+       return;
+    }
+
+    /* A -1 for utf8_len means NUL-terminated */
+    if (utf8_len == -1)
+       utf8_len = strlen (utf8);
+
+    /* Apart from that, no negatives */
+    if (num_glyphs < 0 || utf8_len < 0 || num_clusters < 0) {
+       _cairo_set_error (cr, CAIRO_STATUS_NEGATIVE_COUNT);
+       return;
+    }
+
+    if (num_glyphs == 0 && utf8_len == 0)
+       return;
+
+    if (utf8) {
+       /* Make sure clusters cover the entire glyphs and utf8 arrays,
+        * and that cluster boundaries are UTF-8 boundaries. */
+       status = _cairo_validate_text_clusters (utf8, utf8_len,
+                                               glyphs, num_glyphs,
+                                               clusters, num_clusters, cluster_flags);
+       if (status == CAIRO_STATUS_INVALID_CLUSTERS) {
+           /* Either got invalid UTF-8 text, or cluster mapping is bad.
+            * Differentiate those. */
+
+           cairo_status_t status2;
+
+           status2 = _cairo_utf8_to_ucs4 (utf8, utf8_len, NULL, NULL);
+           if (status2)
+               status = status2;
+       } else {
+           cairo_glyph_text_info_t info;
+
+           info.utf8 = utf8;
+           info.utf8_len = utf8_len;
+           info.clusters = clusters;
+           info.num_clusters = num_clusters;
+           info.cluster_flags = cluster_flags;
+
+           status = cr->backend->glyphs (cr, glyphs, num_glyphs, &info);
+       }
+    } else {
+       status = cr->backend->glyphs (cr, glyphs, num_glyphs, NULL);
+    }
+    if (unlikely (status))
+       _cairo_set_error (cr, status);
+}
+
+/**
+ * cairo_text_path:
+ * @cr: a cairo context
+ * @utf8: a NUL-terminated string of text encoded in UTF-8, or %NULL
+ *
+ * Adds closed paths for text to the current path.  The generated
+ * path if filled, achieves an effect similar to that of
+ * cairo_show_text().
+ *
+ * Text conversion and positioning is done similar to cairo_show_text().
+ *
+ * Like cairo_show_text(), After this call the current point is
+ * moved to the origin of where the next glyph would be placed in
+ * this same progression.  That is, the current point will be at
+ * the origin of the final glyph offset by its advance values.
+ * This allows for chaining multiple calls to to cairo_text_path()
+ * without having to set current point in between.
+ *
+ * Note: The cairo_text_path() function call is part of what the cairo
+ * designers call the "toy" text API. It is convenient for short demos
+ * and simple programs, but it is not expected to be adequate for
+ * serious text-using applications. See cairo_glyph_path() for the
+ * "real" text path API in cairo.
+ **/
+void
+cairo_text_path (cairo_t *cr, const char *utf8)
+{
+    cairo_status_t status;
+    cairo_text_extents_t extents;
+    cairo_glyph_t stack_glyphs[CAIRO_STACK_ARRAY_LENGTH (cairo_glyph_t)];
+    cairo_glyph_t *glyphs, *last_glyph;
+    cairo_scaled_font_t *scaled_font;
+    int num_glyphs;
+    double x, y;
+
+    if (unlikely (cr->status))
+       return;
+
+    if (utf8 == NULL)
+       return;
+
+
+    glyphs = stack_glyphs;
+    num_glyphs = ARRAY_LENGTH (stack_glyphs);
+
+    scaled_font = cairo_get_scaled_font (cr);
+    if (unlikely (scaled_font->status)) {
+       _cairo_set_error (cr, scaled_font->status);
+       return;
+    }
+
+    cairo_get_current_point (cr, &x, &y);
+    status = cairo_scaled_font_text_to_glyphs (scaled_font,
+                                              x, y,
+                                              utf8, -1,
+                                              &glyphs, &num_glyphs,
+                                              NULL, NULL, NULL);
+
+    if (num_glyphs == 0)
+       return;
+
+    status = cr->backend->glyph_path (cr, glyphs, num_glyphs);
+
+    if (unlikely (status))
+       goto BAIL;
+
+    last_glyph = &glyphs[num_glyphs - 1];
+    status = cr->backend->glyph_extents (cr, last_glyph, 1, &extents);
+
+    if (unlikely (status))
+       goto BAIL;
+
+    x = last_glyph->x + extents.x_advance;
+    y = last_glyph->y + extents.y_advance;
+    cr->backend->move_to (cr, x, y);
+
+ BAIL:
+    if (glyphs != stack_glyphs)
+       cairo_glyph_free (glyphs);
+
+    if (unlikely (status))
+       _cairo_set_error (cr, status);
+}
+
+/**
+ * cairo_glyph_path:
+ * @cr: a cairo context
+ * @glyphs: array of glyphs to show
+ * @num_glyphs: number of glyphs to show
+ *
+ * Adds closed paths for the glyphs to the current path.  The generated
+ * path if filled, achieves an effect similar to that of
+ * cairo_show_glyphs().
+ **/
+void
+cairo_glyph_path (cairo_t *cr, const cairo_glyph_t *glyphs, int num_glyphs)
+{
+    cairo_status_t status;
+
+    if (unlikely (cr->status))
+       return;
+
+    if (num_glyphs == 0)
+       return;
+
+    if (unlikely (num_glyphs < 0)) {
+       _cairo_set_error (cr, CAIRO_STATUS_NEGATIVE_COUNT);
+       return;
+    }
+
+    if (unlikely (glyphs == NULL)) {
+       _cairo_set_error (cr, CAIRO_STATUS_NULL_POINTER);
+       return;
+    }
+
+    status = cr->backend->glyph_path (cr, glyphs, num_glyphs);
+    if (unlikely (status))
+       _cairo_set_error (cr, status);
+}
+
+/**
+ * cairo_get_operator:
+ * @cr: a cairo context
+ *
+ * Gets the current compositing operator for a cairo context.
+ *
+ * Return value: the current compositing operator.
+ **/
+cairo_operator_t
+cairo_get_operator (cairo_t *cr)
+{
+    if (unlikely (cr->status))
+        return CAIRO_GSTATE_OPERATOR_DEFAULT;
+
+    return cr->backend->get_operator (cr);
+}
+
+#if 0
+/**
+ * cairo_get_opacity:
+ * @cr: a cairo context
+ *
+ * Gets the current compositing opacity for a cairo context.
+ *
+ * Return value: the current compositing opacity.
+ **/
+double
+cairo_get_opacity (cairo_t *cr)
+{
+    if (unlikely (cr->status))
+        return 1.;
+
+    return cr->backend->get_opacity (cr);
+}
+#endif
+
+/**
+ * cairo_get_tolerance:
+ * @cr: a cairo context
+ *
+ * Gets the current tolerance value, as set by cairo_set_tolerance().
+ *
+ * Return value: the current tolerance value.
+ **/
+double
+cairo_get_tolerance (cairo_t *cr)
+{
+    if (unlikely (cr->status))
+        return CAIRO_GSTATE_TOLERANCE_DEFAULT;
+
+    return cr->backend->get_tolerance (cr);
+}
+slim_hidden_def (cairo_get_tolerance);
+
+/**
+ * cairo_get_antialias:
+ * @cr: a cairo context
+ *
+ * Gets the current shape antialiasing mode, as set by
+ * cairo_set_antialias().
+ *
+ * Return value: the current shape antialiasing mode.
+ **/
+cairo_antialias_t
+cairo_get_antialias (cairo_t *cr)
+{
+    if (unlikely (cr->status))
+        return CAIRO_ANTIALIAS_DEFAULT;
+
+    return cr->backend->get_antialias (cr);
+}
+
+/**
+ * cairo_has_current_point:
+ * @cr: a cairo context
+ *
+ * Returns whether a current point is defined on the current path.
+ * See cairo_get_current_point() for details on the current point.
+ *
+ * Return value: whether a current point is defined.
+ *
+ * Since: 1.6
+ **/
+cairo_bool_t
+cairo_has_current_point (cairo_t *cr)
+{
+    if (unlikely (cr->status))
+       return FALSE;
+
+    return cr->backend->has_current_point (cr);
+}
+
+/**
+ * cairo_get_current_point:
+ * @cr: a cairo context
+ * @x: return value for X coordinate of the current point
+ * @y: return value for Y coordinate of the current point
+ *
+ * Gets the current point of the current path, which is
+ * conceptually the final point reached by the path so far.
+ *
+ * The current point is returned in the user-space coordinate
+ * system. If there is no defined current point or if @cr is in an
+ * error status, @x and @y will both be set to 0.0. It is possible to
+ * check this in advance with cairo_has_current_point().
+ *
+ * Most path construction functions alter the current point. See the
+ * following for details on how they affect the current point:
+ * cairo_new_path(), cairo_new_sub_path(),
+ * cairo_append_path(), cairo_close_path(),
+ * cairo_move_to(), cairo_line_to(), cairo_curve_to(),
+ * cairo_rel_move_to(), cairo_rel_line_to(), cairo_rel_curve_to(),
+ * cairo_arc(), cairo_arc_negative(), cairo_rectangle(),
+ * cairo_text_path(), cairo_glyph_path(), cairo_stroke_to_path().
+ *
+ * Some functions use and alter the current point but do not
+ * otherwise change current path:
+ * cairo_show_text().
+ *
+ * Some functions unset the current path and as a result, current point:
+ * cairo_fill(), cairo_stroke().
+ **/
+void
+cairo_get_current_point (cairo_t *cr, double *x_ret, double *y_ret)
+{
+    double x, y;
+
+    x = y = 0;
+    if (cr->status == CAIRO_STATUS_SUCCESS &&
+       cr->backend->has_current_point (cr))
+    {
+       cr->backend->get_current_point (cr, &x, &y);
+    }
+
+    if (x_ret)
+       *x_ret = x;
+    if (y_ret)
+       *y_ret = y;
+}
+slim_hidden_def(cairo_get_current_point);
+
+/**
+ * cairo_get_fill_rule:
+ * @cr: a cairo context
+ *
+ * Gets the current fill rule, as set by cairo_set_fill_rule().
+ *
+ * Return value: the current fill rule.
+ **/
+cairo_fill_rule_t
+cairo_get_fill_rule (cairo_t *cr)
+{
+    if (unlikely (cr->status))
+        return CAIRO_GSTATE_FILL_RULE_DEFAULT;
+
+    return cr->backend->get_fill_rule (cr);
+}
+
+/**
+ * cairo_get_line_width:
+ * @cr: a cairo context
+ *
+ * This function returns the current line width value exactly as set by
+ * cairo_set_line_width(). Note that the value is unchanged even if
+ * the CTM has changed between the calls to cairo_set_line_width() and
+ * cairo_get_line_width().
+ *
+ * Return value: the current line width.
+ **/
+double
+cairo_get_line_width (cairo_t *cr)
+{
+    if (unlikely (cr->status))
+        return CAIRO_GSTATE_LINE_WIDTH_DEFAULT;
+
+    return cr->backend->get_line_width (cr);
+}
+slim_hidden_def (cairo_get_line_width);
+
+/**
+ * cairo_get_line_cap:
+ * @cr: a cairo context
+ *
+ * Gets the current line cap style, as set by cairo_set_line_cap().
+ *
+ * Return value: the current line cap style.
+ **/
+cairo_line_cap_t
+cairo_get_line_cap (cairo_t *cr)
+{
+    if (unlikely (cr->status))
+        return CAIRO_GSTATE_LINE_CAP_DEFAULT;
+
+    return cr->backend->get_line_cap (cr);
+}
+
+/**
+ * cairo_get_line_join:
+ * @cr: a cairo context
+ *
+ * Gets the current line join style, as set by cairo_set_line_join().
+ *
+ * Return value: the current line join style.
+ **/
+cairo_line_join_t
+cairo_get_line_join (cairo_t *cr)
+{
+    if (unlikely (cr->status))
+        return CAIRO_GSTATE_LINE_JOIN_DEFAULT;
+
+    return cr->backend->get_line_join (cr);
+}
+
+/**
+ * cairo_get_miter_limit:
+ * @cr: a cairo context
+ *
+ * Gets the current miter limit, as set by cairo_set_miter_limit().
+ *
+ * Return value: the current miter limit.
+ **/
+double
+cairo_get_miter_limit (cairo_t *cr)
+{
+    if (unlikely (cr->status))
+        return CAIRO_GSTATE_MITER_LIMIT_DEFAULT;
+
+    return cr->backend->get_miter_limit (cr);
+}
+
+/**
+ * cairo_get_matrix:
+ * @cr: a cairo context
+ * @matrix: return value for the matrix
+ *
+ * Stores the current transformation matrix (CTM) into @matrix.
+ **/
+void
+cairo_get_matrix (cairo_t *cr, cairo_matrix_t *matrix)
+{
+    if (unlikely (cr->status)) {
+       cairo_matrix_init_identity (matrix);
+       return;
+    }
+
+    cr->backend->get_matrix (cr, matrix);
+}
+slim_hidden_def (cairo_get_matrix);
+
+/**
+ * cairo_get_target:
+ * @cr: a cairo context
+ *
+ * Gets the target surface for the cairo context as passed to
+ * cairo_create().
+ *
+ * This function will always return a valid pointer, but the result
+ * can be a "nil" surface if @cr is already in an error state,
+ * (ie. cairo_status() <literal>!=</literal> %CAIRO_STATUS_SUCCESS).
+ * A nil surface is indicated by cairo_surface_status()
+ * <literal>!=</literal> %CAIRO_STATUS_SUCCESS.
+ *
+ * Return value: the target surface. This object is owned by cairo. To
+ * keep a reference to it, you must call cairo_surface_reference().
+ **/
+cairo_surface_t *
+cairo_get_target (cairo_t *cr)
+{
+    if (unlikely (cr->status))
+       return _cairo_surface_create_in_error (cr->status);
+
+    return cr->backend->get_original_target (cr);
+}
+slim_hidden_def (cairo_get_target);
+
+/**
+ * cairo_get_group_target:
+ * @cr: a cairo context
+ *
+ * Gets the current destination surface for the context. This is either
+ * the original target surface as passed to cairo_create() or the target
+ * surface for the current group as started by the most recent call to
+ * cairo_push_group() or cairo_push_group_with_content().
+ *
+ * This function will always return a valid pointer, but the result
+ * can be a "nil" surface if @cr is already in an error state,
+ * (ie. cairo_status() <literal>!=</literal> %CAIRO_STATUS_SUCCESS).
+ * A nil surface is indicated by cairo_surface_status()
+ * <literal>!=</literal> %CAIRO_STATUS_SUCCESS.
+ *
+ * Return value: the target surface. This object is owned by cairo. To
+ * keep a reference to it, you must call cairo_surface_reference().
+ *
+ * Since: 1.2
+ **/
+cairo_surface_t *
+cairo_get_group_target (cairo_t *cr)
+{
+    if (unlikely (cr->status))
+       return _cairo_surface_create_in_error (cr->status);
+
+    return cr->backend->get_current_target (cr);
+}
+
+/**
+ * cairo_copy_path:
+ * @cr: a cairo context
+ *
+ * Creates a copy of the current path and returns it to the user as a
+ * #cairo_path_t. See #cairo_path_data_t for hints on how to iterate
+ * over the returned data structure.
+ *
+ * This function will always return a valid pointer, but the result
+ * will have no data (<literal>data==%NULL</literal> and
+ * <literal>num_data==0</literal>), if either of the following
+ * conditions hold:
+ *
+ * <orderedlist>
+ * <listitem>If there is insufficient memory to copy the path. In this
+ *     case <literal>path->status</literal> will be set to
+ *     %CAIRO_STATUS_NO_MEMORY.</listitem>
+ * <listitem>If @cr is already in an error state. In this case
+ *    <literal>path->status</literal> will contain the same status that
+ *    would be returned by cairo_status().</listitem>
+ * </orderedlist>
+ *
+ * Return value: the copy of the current path. The caller owns the
+ * returned object and should call cairo_path_destroy() when finished
+ * with it.
+ **/
+cairo_path_t *
+cairo_copy_path (cairo_t *cr)
+{
+    if (unlikely (cr->status))
+       return _cairo_path_create_in_error (cr->status);
+
+    return cr->backend->copy_path (cr);
+}
+
+/**
+ * cairo_copy_path_flat:
+ * @cr: a cairo context
+ *
+ * Gets a flattened copy of the current path and returns it to the
+ * user as a #cairo_path_t. See #cairo_path_data_t for hints on
+ * how to iterate over the returned data structure.
+ *
+ * This function is like cairo_copy_path() except that any curves
+ * in the path will be approximated with piecewise-linear
+ * approximations, (accurate to within the current tolerance
+ * value). That is, the result is guaranteed to not have any elements
+ * of type %CAIRO_PATH_CURVE_TO which will instead be replaced by a
+ * series of %CAIRO_PATH_LINE_TO elements.
+ *
+ * This function will always return a valid pointer, but the result
+ * will have no data (<literal>data==%NULL</literal> and
+ * <literal>num_data==0</literal>), if either of the following
+ * conditions hold:
+ *
+ * <orderedlist>
+ * <listitem>If there is insufficient memory to copy the path. In this
+ *     case <literal>path->status</literal> will be set to
+ *     %CAIRO_STATUS_NO_MEMORY.</listitem>
+ * <listitem>If @cr is already in an error state. In this case
+ *    <literal>path->status</literal> will contain the same status that
+ *    would be returned by cairo_status().</listitem>
+ * </orderedlist>
+ *
+ * Return value: the copy of the current path. The caller owns the
+ * returned object and should call cairo_path_destroy() when finished
+ * with it.
+ **/
+cairo_path_t *
+cairo_copy_path_flat (cairo_t *cr)
+{
+    if (unlikely (cr->status))
+       return _cairo_path_create_in_error (cr->status);
+
+    return cr->backend->copy_path_flat (cr);
+}
+
+/**
+ * cairo_append_path:
+ * @cr: a cairo context
+ * @path: path to be appended
+ *
+ * Append the @path onto the current path. The @path may be either the
+ * return value from one of cairo_copy_path() or
+ * cairo_copy_path_flat() or it may be constructed manually.  See
+ * #cairo_path_t for details on how the path data structure should be
+ * initialized, and note that <literal>path->status</literal> must be
+ * initialized to %CAIRO_STATUS_SUCCESS.
+ **/
+void
+cairo_append_path (cairo_t             *cr,
+                  const cairo_path_t   *path)
+{
+    cairo_status_t status;
+
+    if (unlikely (cr->status))
+       return;
+
+    if (unlikely (path == NULL)) {
+       _cairo_set_error (cr, CAIRO_STATUS_NULL_POINTER);
+       return;
+    }
+
+    if (unlikely (path->status)) {
+       if (path->status > CAIRO_STATUS_SUCCESS &&
+           path->status <= CAIRO_STATUS_LAST_STATUS)
+           _cairo_set_error (cr, path->status);
+       else
+           _cairo_set_error (cr, CAIRO_STATUS_INVALID_STATUS);
+       return;
+    }
+
+    if (path->num_data == 0)
+       return;
+
+    if (unlikely (path->data == NULL)) {
+       _cairo_set_error (cr, CAIRO_STATUS_NULL_POINTER);
+       return;
+    }
+
+    status = cr->backend->append_path (cr, path);
+    if (unlikely (status))
+       _cairo_set_error (cr, status);
+}
+
+/**
+ * cairo_status:
+ * @cr: a cairo context
+ *
+ * Checks whether an error has previously occurred for this context.
+ *
+ * Returns: the current status of this context, see #cairo_status_t
+ **/
+cairo_status_t
+cairo_status (cairo_t *cr)
+{
+    return cr->status;
+}
+slim_hidden_def (cairo_status);
diff --git a/src/cairo.h b/src/cairo.h
new file mode 100644 (file)
index 0000000..f453035
--- /dev/null
@@ -0,0 +1,2948 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *     Carl D. Worth <cworth@cworth.org>
+ */
+
+#ifndef CAIRO_H
+#define CAIRO_H
+
+#include "cairo-version.h"
+#include "cairo-features.h"
+#include "cairo-deprecated.h"
+
+#ifdef  __cplusplus
+# define CAIRO_BEGIN_DECLS  extern "C" {
+# define CAIRO_END_DECLS    }
+#else
+# define CAIRO_BEGIN_DECLS
+# define CAIRO_END_DECLS
+#endif
+
+#ifndef cairo_public
+# if defined (_MSC_VER) && ! defined (CAIRO_WIN32_STATIC_BUILD)
+#  define cairo_public __declspec(dllimport)
+# else
+#  define cairo_public
+# endif
+#endif
+
+CAIRO_BEGIN_DECLS
+
+#define CAIRO_VERSION_ENCODE(major, minor, micro) (    \
+         ((major) * 10000)                             \
+       + ((minor) *   100)                             \
+       + ((micro) *     1))
+
+#define CAIRO_VERSION CAIRO_VERSION_ENCODE(    \
+       CAIRO_VERSION_MAJOR,                    \
+       CAIRO_VERSION_MINOR,                    \
+       CAIRO_VERSION_MICRO)
+
+
+#define CAIRO_VERSION_STRINGIZE_(major, minor, micro)  \
+       #major"."#minor"."#micro
+#define CAIRO_VERSION_STRINGIZE(major, minor, micro)   \
+       CAIRO_VERSION_STRINGIZE_(major, minor, micro)
+
+#define CAIRO_VERSION_STRING CAIRO_VERSION_STRINGIZE(  \
+       CAIRO_VERSION_MAJOR,                            \
+       CAIRO_VERSION_MINOR,                            \
+       CAIRO_VERSION_MICRO)
+
+
+cairo_public int
+cairo_version (void);
+
+cairo_public const char*
+cairo_version_string (void);
+
+/**
+ * cairo_bool_t:
+ *
+ * #cairo_bool_t is used for boolean values. Returns of type
+ * #cairo_bool_t will always be either 0 or 1, but testing against
+ * these values explicitly is not encouraged; just use the
+ * value as a boolean condition.
+ *
+ * <informalexample><programlisting>
+ *  if (cairo_in_stroke (cr, x, y)) {
+ *      /<!-- -->* do something *<!-- -->/
+ *  }
+ * </programlisting></informalexample>
+ **/
+typedef int cairo_bool_t;
+
+/**
+ * cairo_t:
+ *
+ * A #cairo_t contains the current state of the rendering device,
+ * including coordinates of yet to be drawn shapes.
+ *
+ * Cairo contexts, as #cairo_t objects are named, are central to
+ * cairo and all drawing with cairo is always done to a #cairo_t
+ * object.
+ *
+ * Memory management of #cairo_t is done with
+ * cairo_reference() and cairo_destroy().
+ **/
+typedef struct _cairo cairo_t;
+
+/**
+ * cairo_surface_t:
+ *
+ * A #cairo_surface_t represents an image, either as the destination
+ * of a drawing operation or as source when drawing onto another
+ * surface.  To draw to a #cairo_surface_t, create a cairo context
+ * with the surface as the target, using cairo_create().
+ *
+ * There are different subtypes of #cairo_surface_t for
+ * different drawing backends; for example, cairo_image_surface_create()
+ * creates a bitmap image in memory.
+ * The type of a surface can be queried with cairo_surface_get_type().
+ *
+ * The initial contents of a surface after creation depend upon the manner
+ * of its creation. If cairo creates the surface and backing storage for
+ * the user, it will be initially cleared; for example,
+ * cairo_image_surface_create() and cairo_surface_create_similar().
+ * Alternatively, if the user passes in a reference to some backing storage
+ * and asks cairo to wrap that in a #cairo_surface_t, then the contents are
+ * not modified; for example, cairo_image_surface_create_for_data() and
+ * cairo_xlib_surface_create().
+ *
+ * Memory management of #cairo_surface_t is done with
+ * cairo_surface_reference() and cairo_surface_destroy().
+ **/
+typedef struct _cairo_surface cairo_surface_t;
+
+/**
+ * cairo_device_t:
+ *
+ * A #cairo_device_t represents the driver interface for drawing
+ * operations to a #cairo_surface_t.  There are different subtypes of
+ * #cairo_device_t for different drawing backends; for example,
+ * cairo_egl_device_create() creates a device that wraps an EGL display and
+ * context.
+ *
+ * The type of a device can be queried with cairo_device_get_type().
+ *
+ * Memory management of #cairo_device_t is done with
+ * cairo_device_reference() and cairo_device_destroy().
+ *
+ * Since: 1.10
+ **/
+typedef struct _cairo_device cairo_device_t;
+
+/**
+ * cairo_matrix_t:
+ * @xx: xx component of the affine transformation
+ * @yx: yx component of the affine transformation
+ * @xy: xy component of the affine transformation
+ * @yy: yy component of the affine transformation
+ * @x0: X translation component of the affine transformation
+ * @y0: Y translation component of the affine transformation
+ *
+ * A #cairo_matrix_t holds an affine transformation, such as a scale,
+ * rotation, shear, or a combination of those. The transformation of
+ * a point (x, y) is given by:
+ * <programlisting>
+ *     x_new = xx * x + xy * y + x0;
+ *     y_new = yx * x + yy * y + y0;
+ * </programlisting>
+ **/
+typedef struct _cairo_matrix {
+    double xx; double yx;
+    double xy; double yy;
+    double x0; double y0;
+} cairo_matrix_t;
+
+/**
+ * cairo_pattern_t:
+ *
+ * A #cairo_pattern_t represents a source when drawing onto a
+ * surface. There are different subtypes of #cairo_pattern_t,
+ * for different types of sources; for example,
+ * cairo_pattern_create_rgb() creates a pattern for a solid
+ * opaque color.
+ *
+ * Other than various
+ * <function>cairo_pattern_create_<emphasis>type</emphasis>()</function>
+ * functions, some of the pattern types can be implicitly created using various
+ * <function>cairo_set_source_<emphasis>type</emphasis>()</function> functions;
+ * for example cairo_set_source_rgb().
+ *
+ * The type of a pattern can be queried with cairo_pattern_get_type().
+ *
+ * Memory management of #cairo_pattern_t is done with
+ * cairo_pattern_reference() and cairo_pattern_destroy().
+ **/
+typedef struct _cairo_pattern cairo_pattern_t;
+
+/**
+ * cairo_destroy_func_t:
+ * @data: The data element being destroyed.
+ *
+ * #cairo_destroy_func_t the type of function which is called when a
+ * data element is destroyed. It is passed the pointer to the data
+ * element and should free any memory and resources allocated for it.
+ **/
+typedef void (*cairo_destroy_func_t) (void *data);
+
+/**
+ * cairo_user_data_key_t:
+ * @unused: not used; ignore.
+ *
+ * #cairo_user_data_key_t is used for attaching user data to cairo
+ * data structures.  The actual contents of the struct is never used,
+ * and there is no need to initialize the object; only the unique
+ * address of a #cairo_data_key_t object is used.  Typically, you
+ * would just use the address of a static #cairo_data_key_t object.
+ **/
+typedef struct _cairo_user_data_key {
+    int unused;
+} cairo_user_data_key_t;
+
+/**
+ * cairo_status_t:
+ * @CAIRO_STATUS_SUCCESS: no error has occurred
+ * @CAIRO_STATUS_NO_MEMORY: out of memory
+ * @CAIRO_STATUS_INVALID_RESTORE: cairo_restore() called without matching cairo_save()
+ * @CAIRO_STATUS_INVALID_POP_GROUP: no saved group to pop, i.e. cairo_pop_group() without matching cairo_push_group()
+ * @CAIRO_STATUS_NO_CURRENT_POINT: no current point defined
+ * @CAIRO_STATUS_INVALID_MATRIX: invalid matrix (not invertible)
+ * @CAIRO_STATUS_INVALID_STATUS: invalid value for an input #cairo_status_t
+ * @CAIRO_STATUS_NULL_POINTER: %NULL pointer
+ * @CAIRO_STATUS_INVALID_STRING: input string not valid UTF-8
+ * @CAIRO_STATUS_INVALID_PATH_DATA: input path data not valid
+ * @CAIRO_STATUS_READ_ERROR: error while reading from input stream
+ * @CAIRO_STATUS_WRITE_ERROR: error while writing to output stream
+ * @CAIRO_STATUS_SURFACE_FINISHED: target surface has been finished
+ * @CAIRO_STATUS_SURFACE_TYPE_MISMATCH: the surface type is not appropriate for the operation
+ * @CAIRO_STATUS_PATTERN_TYPE_MISMATCH: the pattern type is not appropriate for the operation
+ * @CAIRO_STATUS_INVALID_CONTENT: invalid value for an input #cairo_content_t
+ * @CAIRO_STATUS_INVALID_FORMAT: invalid value for an input #cairo_format_t
+ * @CAIRO_STATUS_INVALID_VISUAL: invalid value for an input Visual*
+ * @CAIRO_STATUS_FILE_NOT_FOUND: file not found
+ * @CAIRO_STATUS_INVALID_DASH: invalid value for a dash setting
+ * @CAIRO_STATUS_INVALID_DSC_COMMENT: invalid value for a DSC comment (Since 1.2)
+ * @CAIRO_STATUS_INVALID_INDEX: invalid index passed to getter (Since 1.4)
+ * @CAIRO_STATUS_CLIP_NOT_REPRESENTABLE: clip region not representable in desired format (Since 1.4)
+ * @CAIRO_STATUS_TEMP_FILE_ERROR: error creating or writing to a temporary file (Since 1.6)
+ * @CAIRO_STATUS_INVALID_STRIDE: invalid value for stride (Since 1.6)
+ * @CAIRO_STATUS_FONT_TYPE_MISMATCH: the font type is not appropriate for the operation (Since 1.8)
+ * @CAIRO_STATUS_USER_FONT_IMMUTABLE: the user-font is immutable (Since 1.8)
+ * @CAIRO_STATUS_USER_FONT_ERROR: error occurred in a user-font callback function (Since 1.8)
+ * @CAIRO_STATUS_NEGATIVE_COUNT: negative number used where it is not allowed (Since 1.8)
+ * @CAIRO_STATUS_INVALID_CLUSTERS: input clusters do not represent the accompanying text and glyph array (Since 1.8)
+ * @CAIRO_STATUS_INVALID_SLANT: invalid value for an input #cairo_font_slant_t (Since 1.8)
+ * @CAIRO_STATUS_INVALID_WEIGHT: invalid value for an input #cairo_font_weight_t (Since 1.8)
+ * @CAIRO_STATUS_INVALID_SIZE: invalid value (typically too big) for the size of the input (surface, pattern, etc.) (Since 1.10)
+ * @CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED: user-font method not implemented (Since 1.10)
+ * @CAIRO_STATUS_DEVICE_TYPE_MISMATCH: the device type is not appropriate for the operation (Since 1.10)
+ * @CAIRO_STATUS_DEVICE_ERROR: an operation to the device caused an unspecified error (Since 1.10)
+ * @CAIRO_STATUS_INVALID_MESH_CONSTRUCTION: a mesh pattern
+ *   construction operation was used outside of a
+ *   cairo_mesh_pattern_begin_patch()/cairo_mesh_pattern_end_patch()
+ *   pair (Since 1.12)
+ * @CAIRO_STATUS_DEVICE_FINISHED: target device has been finished (Since 1.12)
+ * @CAIRO_STATUS_LAST_STATUS: this is a special value indicating the number of
+ *   status values defined in this enumeration.  When using this value, note
+ *   that the version of cairo at run-time may have additional status values
+ *   defined than the value of this symbol at compile-time. (Since 1.10)
+ *
+ * #cairo_status_t is used to indicate errors that can occur when
+ * using Cairo. In some cases it is returned directly by functions.
+ * but when using #cairo_t, the last error, if any, is stored in
+ * the context and can be retrieved with cairo_status().
+ *
+ * New entries may be added in future versions.  Use cairo_status_to_string()
+ * to get a human-readable representation of an error message.
+ **/
+typedef enum _cairo_status {
+    CAIRO_STATUS_SUCCESS = 0,
+
+    CAIRO_STATUS_NO_MEMORY,
+    CAIRO_STATUS_INVALID_RESTORE,
+    CAIRO_STATUS_INVALID_POP_GROUP,
+    CAIRO_STATUS_NO_CURRENT_POINT,
+    CAIRO_STATUS_INVALID_MATRIX,
+    CAIRO_STATUS_INVALID_STATUS,
+    CAIRO_STATUS_NULL_POINTER,
+    CAIRO_STATUS_INVALID_STRING,
+    CAIRO_STATUS_INVALID_PATH_DATA,
+    CAIRO_STATUS_READ_ERROR,
+    CAIRO_STATUS_WRITE_ERROR,
+    CAIRO_STATUS_SURFACE_FINISHED,
+    CAIRO_STATUS_SURFACE_TYPE_MISMATCH,
+    CAIRO_STATUS_PATTERN_TYPE_MISMATCH,
+    CAIRO_STATUS_INVALID_CONTENT,
+    CAIRO_STATUS_INVALID_FORMAT,
+    CAIRO_STATUS_INVALID_VISUAL,
+    CAIRO_STATUS_FILE_NOT_FOUND,
+    CAIRO_STATUS_INVALID_DASH,
+    CAIRO_STATUS_INVALID_DSC_COMMENT,
+    CAIRO_STATUS_INVALID_INDEX,
+    CAIRO_STATUS_CLIP_NOT_REPRESENTABLE,
+    CAIRO_STATUS_TEMP_FILE_ERROR,
+    CAIRO_STATUS_INVALID_STRIDE,
+    CAIRO_STATUS_FONT_TYPE_MISMATCH,
+    CAIRO_STATUS_USER_FONT_IMMUTABLE,
+    CAIRO_STATUS_USER_FONT_ERROR,
+    CAIRO_STATUS_NEGATIVE_COUNT,
+    CAIRO_STATUS_INVALID_CLUSTERS,
+    CAIRO_STATUS_INVALID_SLANT,
+    CAIRO_STATUS_INVALID_WEIGHT,
+    CAIRO_STATUS_INVALID_SIZE,
+    CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED,
+    CAIRO_STATUS_DEVICE_TYPE_MISMATCH,
+    CAIRO_STATUS_DEVICE_ERROR,
+    CAIRO_STATUS_INVALID_MESH_CONSTRUCTION,
+    CAIRO_STATUS_DEVICE_FINISHED,
+
+    CAIRO_STATUS_LAST_STATUS
+} cairo_status_t;
+
+/**
+ * cairo_content_t:
+ * @CAIRO_CONTENT_COLOR: The surface will hold color content only.
+ * @CAIRO_CONTENT_ALPHA: The surface will hold alpha content only.
+ * @CAIRO_CONTENT_COLOR_ALPHA: The surface will hold color and alpha content.
+ *
+ * #cairo_content_t is used to describe the content that a surface will
+ * contain, whether color information, alpha information (translucence
+ * vs. opacity), or both.
+ *
+ * Note: The large values here are designed to keep #cairo_content_t
+ * values distinct from #cairo_format_t values so that the
+ * implementation can detect the error if users confuse the two types.
+ **/
+typedef enum _cairo_content {
+    CAIRO_CONTENT_COLOR                = 0x1000,
+    CAIRO_CONTENT_ALPHA                = 0x2000,
+    CAIRO_CONTENT_COLOR_ALPHA  = 0x3000
+} cairo_content_t;
+
+/**
+ * cairo_format_t:
+ * @CAIRO_FORMAT_INVALID: no such format exists or is supported.
+ * @CAIRO_FORMAT_ARGB32: each pixel is a 32-bit quantity, with
+ *   alpha in the upper 8 bits, then red, then green, then blue.
+ *   The 32-bit quantities are stored native-endian. Pre-multiplied
+ *   alpha is used. (That is, 50% transparent red is 0x80800000,
+ *   not 0x80ff0000.)
+ * @CAIRO_FORMAT_RGB24: each pixel is a 32-bit quantity, with
+ *   the upper 8 bits unused. Red, Green, and Blue are stored
+ *   in the remaining 24 bits in that order.
+ * @CAIRO_FORMAT_A8: each pixel is a 8-bit quantity holding
+ *   an alpha value.
+ * @CAIRO_FORMAT_A1: each pixel is a 1-bit quantity holding
+ *   an alpha value. Pixels are packed together into 32-bit
+ *   quantities. The ordering of the bits matches the
+ *   endianess of the platform. On a big-endian machine, the
+ *   first pixel is in the uppermost bit, on a little-endian
+ *   machine the first pixel is in the least-significant bit.
+ * @CAIRO_FORMAT_RGB16_565: each pixel is a 16-bit quantity
+ *   with red in the upper 5 bits, then green in the middle
+ *   6 bits, and blue in the lower 5 bits.
+ * @CAIRO_FORMAT_RGB30: like RGB24 but with 10bpc
+ *
+ * #cairo_format_t is used to identify the memory format of
+ * image data.
+ *
+ * New entries may be added in future versions.
+ **/
+typedef enum _cairo_format {
+    CAIRO_FORMAT_INVALID   = -1,
+    CAIRO_FORMAT_ARGB32    = 0,
+    CAIRO_FORMAT_RGB24     = 1,
+    CAIRO_FORMAT_A8        = 2,
+    CAIRO_FORMAT_A1        = 3,
+    CAIRO_FORMAT_RGB16_565 = 4,
+    CAIRO_FORMAT_RGB30     = 5
+} cairo_format_t;
+
+
+/**
+ * cairo_write_func_t:
+ * @closure: the output closure
+ * @data: the buffer containing the data to write
+ * @length: the amount of data to write
+ *
+ * #cairo_write_func_t is the type of function which is called when a
+ * backend needs to write data to an output stream.  It is passed the
+ * closure which was specified by the user at the time the write
+ * function was registered, the data to write and the length of the
+ * data in bytes.  The write function should return
+ * %CAIRO_STATUS_SUCCESS if all the data was successfully written,
+ * %CAIRO_STATUS_WRITE_ERROR otherwise.
+ *
+ * Returns: the status code of the write operation
+ **/
+typedef cairo_status_t (*cairo_write_func_t) (void               *closure,
+                                             const unsigned char *data,
+                                             unsigned int         length);
+
+/**
+ * cairo_read_func_t:
+ * @closure: the input closure
+ * @data: the buffer into which to read the data
+ * @length: the amount of data to read
+ *
+ * #cairo_read_func_t is the type of function which is called when a
+ * backend needs to read data from an input stream.  It is passed the
+ * closure which was specified by the user at the time the read
+ * function was registered, the buffer to read the data into and the
+ * length of the data in bytes.  The read function should return
+ * %CAIRO_STATUS_SUCCESS if all the data was successfully read,
+ * %CAIRO_STATUS_READ_ERROR otherwise.
+ *
+ * Returns: the status code of the read operation
+ **/
+typedef cairo_status_t (*cairo_read_func_t) (void              *closure,
+                                            unsigned char      *data,
+                                            unsigned int       length);
+
+/**
+ * cairo_rectangle_int_t:
+ * @x: X coordinate of the left side of the rectangle
+ * @y: Y coordinate of the the top side of the rectangle
+ * @width: width of the rectangle
+ * @height: height of the rectangle
+ *
+ * A data structure for holding a rectangle with integer coordinates.
+ *
+ * Since: 1.10
+ **/
+
+typedef struct _cairo_rectangle_int {
+    int x, y;
+    int width, height;
+} cairo_rectangle_int_t;
+
+
+/* Functions for manipulating state objects */
+cairo_public cairo_t *
+cairo_create (cairo_surface_t *target);
+
+cairo_public cairo_t *
+cairo_reference (cairo_t *cr);
+
+cairo_public void
+cairo_destroy (cairo_t *cr);
+
+cairo_public unsigned int
+cairo_get_reference_count (cairo_t *cr);
+
+cairo_public void *
+cairo_get_user_data (cairo_t                    *cr,
+                    const cairo_user_data_key_t *key);
+
+cairo_public cairo_status_t
+cairo_set_user_data (cairo_t                    *cr,
+                    const cairo_user_data_key_t *key,
+                    void                        *user_data,
+                    cairo_destroy_func_t         destroy);
+
+cairo_public void
+cairo_save (cairo_t *cr);
+
+cairo_public void
+cairo_restore (cairo_t *cr);
+
+cairo_public void
+cairo_push_group (cairo_t *cr);
+
+cairo_public void
+cairo_push_group_with_content (cairo_t *cr, cairo_content_t content);
+
+cairo_public cairo_pattern_t *
+cairo_pop_group (cairo_t *cr);
+
+cairo_public void
+cairo_pop_group_to_source (cairo_t *cr);
+
+/* Modify state */
+
+/**
+ * cairo_operator_t:
+ * @CAIRO_OPERATOR_CLEAR: clear destination layer (bounded)
+ * @CAIRO_OPERATOR_SOURCE: replace destination layer (bounded)
+ * @CAIRO_OPERATOR_OVER: draw source layer on top of destination layer
+ * (bounded)
+ * @CAIRO_OPERATOR_IN: draw source where there was destination content
+ * (unbounded)
+ * @CAIRO_OPERATOR_OUT: draw source where there was no destination
+ * content (unbounded)
+ * @CAIRO_OPERATOR_ATOP: draw source on top of destination content and
+ * only there
+ * @CAIRO_OPERATOR_DEST: ignore the source
+ * @CAIRO_OPERATOR_DEST_OVER: draw destination on top of source
+ * @CAIRO_OPERATOR_DEST_IN: leave destination only where there was
+ * source content (unbounded)
+ * @CAIRO_OPERATOR_DEST_OUT: leave destination only where there was no
+ * source content
+ * @CAIRO_OPERATOR_DEST_ATOP: leave destination on top of source content
+ * and only there (unbounded)
+ * @CAIRO_OPERATOR_XOR: source and destination are shown where there is only
+ * one of them
+ * @CAIRO_OPERATOR_ADD: source and destination layers are accumulated
+ * @CAIRO_OPERATOR_SATURATE: like over, but assuming source and dest are
+ * disjoint geometries
+ * @CAIRO_OPERATOR_MULTIPLY: source and destination layers are multiplied.
+ * This causes the result to be at least as dark as the darker inputs.
+ * @CAIRO_OPERATOR_SCREEN: source and destination are complemented and
+ * multiplied. This causes the result to be at least as light as the lighter
+ * inputs.
+ * @CAIRO_OPERATOR_OVERLAY: multiplies or screens, depending on the
+ * lightness of the destination color.
+ * @CAIRO_OPERATOR_DARKEN: replaces the destination with the source if it
+ * is darker, otherwise keeps the source.
+ * @CAIRO_OPERATOR_LIGHTEN: replaces the destination with the source if it
+ * is lighter, otherwise keeps the source.
+ * @CAIRO_OPERATOR_COLOR_DODGE: brightens the destination color to reflect
+ * the source color.
+ * @CAIRO_OPERATOR_COLOR_BURN: darkens the destination color to reflect
+ * the source color.
+ * @CAIRO_OPERATOR_HARD_LIGHT: Multiplies or screens, dependent on source
+ * color.
+ * @CAIRO_OPERATOR_SOFT_LIGHT: Darkens or lightens, dependent on source
+ * color.
+ * @CAIRO_OPERATOR_DIFFERENCE: Takes the difference of the source and
+ * destination color.
+ * @CAIRO_OPERATOR_EXCLUSION: Produces an effect similar to difference, but
+ * with lower contrast.
+ * @CAIRO_OPERATOR_HSL_HUE: Creates a color with the hue of the source
+ * and the saturation and luminosity of the target.
+ * @CAIRO_OPERATOR_HSL_SATURATION: Creates a color with the saturation
+ * of the source and the hue and luminosity of the target. Painting with
+ * this mode onto a gray area produces no change.
+ * @CAIRO_OPERATOR_HSL_COLOR: Creates a color with the hue and saturation
+ * of the source and the luminosity of the target. This preserves the gray
+ * levels of the target and is useful for coloring monochrome images or
+ * tinting color images.
+ * @CAIRO_OPERATOR_HSL_LUMINOSITY: Creates a color with the luminosity of
+ * the source and the hue and saturation of the target. This produces an
+ * inverse effect to @CAIRO_OPERATOR_HSL_COLOR.
+ *
+ * #cairo_operator_t is used to set the compositing operator for all cairo
+ * drawing operations.
+ *
+ * The default operator is %CAIRO_OPERATOR_OVER.
+ *
+ * The operators marked as <firstterm>unbounded</firstterm> modify their
+ * destination even outside of the mask layer (that is, their effect is not
+ * bound by the mask layer).  However, their effect can still be limited by
+ * way of clipping.
+ *
+ * To keep things simple, the operator descriptions here
+ * document the behavior for when both source and destination are either fully
+ * transparent or fully opaque.  The actual implementation works for
+ * translucent layers too.
+ * For a more detailed explanation of the effects of each operator, including
+ * the mathematical definitions, see
+ * <ulink url="http://cairographics.org/operators/">http://cairographics.org/operators/</ulink>.
+ **/
+typedef enum _cairo_operator {
+    CAIRO_OPERATOR_CLEAR,
+
+    CAIRO_OPERATOR_SOURCE,
+    CAIRO_OPERATOR_OVER,
+    CAIRO_OPERATOR_IN,
+    CAIRO_OPERATOR_OUT,
+    CAIRO_OPERATOR_ATOP,
+
+    CAIRO_OPERATOR_DEST,
+    CAIRO_OPERATOR_DEST_OVER,
+    CAIRO_OPERATOR_DEST_IN,
+    CAIRO_OPERATOR_DEST_OUT,
+    CAIRO_OPERATOR_DEST_ATOP,
+
+    CAIRO_OPERATOR_XOR,
+    CAIRO_OPERATOR_ADD,
+    CAIRO_OPERATOR_SATURATE,
+
+    CAIRO_OPERATOR_MULTIPLY,
+    CAIRO_OPERATOR_SCREEN,
+    CAIRO_OPERATOR_OVERLAY,
+    CAIRO_OPERATOR_DARKEN,
+    CAIRO_OPERATOR_LIGHTEN,
+    CAIRO_OPERATOR_COLOR_DODGE,
+    CAIRO_OPERATOR_COLOR_BURN,
+    CAIRO_OPERATOR_HARD_LIGHT,
+    CAIRO_OPERATOR_SOFT_LIGHT,
+    CAIRO_OPERATOR_DIFFERENCE,
+    CAIRO_OPERATOR_EXCLUSION,
+    CAIRO_OPERATOR_HSL_HUE,
+    CAIRO_OPERATOR_HSL_SATURATION,
+    CAIRO_OPERATOR_HSL_COLOR,
+    CAIRO_OPERATOR_HSL_LUMINOSITY
+} cairo_operator_t;
+
+cairo_public void
+cairo_set_operator (cairo_t *cr, cairo_operator_t op);
+
+cairo_public void
+cairo_set_source (cairo_t *cr, cairo_pattern_t *source);
+
+cairo_public void
+cairo_set_source_rgb (cairo_t *cr, double red, double green, double blue);
+
+cairo_public void
+cairo_set_source_rgba (cairo_t *cr,
+                      double red, double green, double blue,
+                      double alpha);
+
+cairo_public void
+cairo_set_source_surface (cairo_t        *cr,
+                         cairo_surface_t *surface,
+                         double           x,
+                         double           y);
+
+cairo_public void
+cairo_set_tolerance (cairo_t *cr, double tolerance);
+
+/**
+ * cairo_antialias_t:
+ * @CAIRO_ANTIALIAS_DEFAULT: Use the default antialiasing for
+ *   the subsystem and target device
+ * @CAIRO_ANTIALIAS_NONE: Use a bilevel alpha mask
+ * @CAIRO_ANTIALIAS_GRAY: Perform single-color antialiasing (using
+ *  shades of gray for black text on a white background, for example).
+ * @CAIRO_ANTIALIAS_SUBPIXEL: Perform antialiasing by taking
+ *  advantage of the order of subpixel elements on devices
+ *  such as LCD panels
+ *
+ * Specifies the type of antialiasing to do when rendering text or shapes.
+ *
+ * As it is not necessarily clear from the above what advantages a particular
+ * antialias method provides, since 1.12, there is also a set of hints:
+ * @CAIRO_ANTIALIAS_FAST: Allow the backend to degrade raster quality for speed
+ * @CAIRO_ANTIALIAS_GOOD: A balance between speed and quality
+ * @CAIRO_ANTIALIAS_BEST: A high-fidelity, but potentially slow, raster mode
+ *
+ * These make no guarantee on how the backend will perform its rasterisation
+ * (if it even rasterises!), nor that they have any differing effect other
+ * than to enable some form of antialiasing. In the case of glyph rendering,
+ * @CAIRO_ANTIALIAS_FAST and @CAIRO_ANTIALIAS_GOOD will be mapped to
+ * @CAIRO_ANTIALIAS_GRAY, with @CAIRO_ANTALIAS_BEST being equivalent to
+ * @CAIRO_ANTIALIAS_SUBPIXEL.
+ *
+ * The interpretation of @CAIRO_ANTIALIAS_DEFAULT is left entirely up to
+ * the backend, typically this will be similar to @CAIRO_ANTIALIAS_GOOD.
+ **/
+typedef enum _cairo_antialias {
+    CAIRO_ANTIALIAS_DEFAULT,
+
+    /* method */
+    CAIRO_ANTIALIAS_NONE,
+    CAIRO_ANTIALIAS_GRAY,
+    CAIRO_ANTIALIAS_SUBPIXEL,
+
+    /* hints */
+    CAIRO_ANTIALIAS_FAST,
+    CAIRO_ANTIALIAS_GOOD,
+    CAIRO_ANTIALIAS_BEST
+} cairo_antialias_t;
+
+cairo_public void
+cairo_set_antialias (cairo_t *cr, cairo_antialias_t antialias);
+
+/**
+ * cairo_fill_rule_t:
+ * @CAIRO_FILL_RULE_WINDING: If the path crosses the ray from
+ * left-to-right, counts +1. If the path crosses the ray
+ * from right to left, counts -1. (Left and right are determined
+ * from the perspective of looking along the ray from the starting
+ * point.) If the total count is non-zero, the point will be filled.
+ * @CAIRO_FILL_RULE_EVEN_ODD: Counts the total number of
+ * intersections, without regard to the orientation of the contour. If
+ * the total number of intersections is odd, the point will be
+ * filled.
+ *
+ * #cairo_fill_rule_t is used to select how paths are filled. For both
+ * fill rules, whether or not a point is included in the fill is
+ * determined by taking a ray from that point to infinity and looking
+ * at intersections with the path. The ray can be in any direction,
+ * as long as it doesn't pass through the end point of a segment
+ * or have a tricky intersection such as intersecting tangent to the path.
+ * (Note that filling is not actually implemented in this way. This
+ * is just a description of the rule that is applied.)
+ *
+ * The default fill rule is %CAIRO_FILL_RULE_WINDING.
+ *
+ * New entries may be added in future versions.
+ **/
+typedef enum _cairo_fill_rule {
+    CAIRO_FILL_RULE_WINDING,
+    CAIRO_FILL_RULE_EVEN_ODD
+} cairo_fill_rule_t;
+
+cairo_public void
+cairo_set_fill_rule (cairo_t *cr, cairo_fill_rule_t fill_rule);
+
+cairo_public void
+cairo_set_line_width (cairo_t *cr, double width);
+
+/**
+ * cairo_line_cap_t:
+ * @CAIRO_LINE_CAP_BUTT: start(stop) the line exactly at the start(end) point
+ * @CAIRO_LINE_CAP_ROUND: use a round ending, the center of the circle is the end point
+ * @CAIRO_LINE_CAP_SQUARE: use squared ending, the center of the square is the end point
+ *
+ * Specifies how to render the endpoints of the path when stroking.
+ *
+ * The default line cap style is %CAIRO_LINE_CAP_BUTT.
+ **/
+typedef enum _cairo_line_cap {
+    CAIRO_LINE_CAP_BUTT,
+    CAIRO_LINE_CAP_ROUND,
+    CAIRO_LINE_CAP_SQUARE
+} cairo_line_cap_t;
+
+cairo_public void
+cairo_set_line_cap (cairo_t *cr, cairo_line_cap_t line_cap);
+
+/**
+ * cairo_line_join_t:
+ * @CAIRO_LINE_JOIN_MITER: use a sharp (angled) corner, see
+ * cairo_set_miter_limit()
+ * @CAIRO_LINE_JOIN_ROUND: use a rounded join, the center of the circle is the
+ * joint point
+ * @CAIRO_LINE_JOIN_BEVEL: use a cut-off join, the join is cut off at half
+ * the line width from the joint point
+ *
+ * Specifies how to render the junction of two lines when stroking.
+ *
+ * The default line join style is %CAIRO_LINE_JOIN_MITER.
+ **/
+typedef enum _cairo_line_join {
+    CAIRO_LINE_JOIN_MITER,
+    CAIRO_LINE_JOIN_ROUND,
+    CAIRO_LINE_JOIN_BEVEL
+} cairo_line_join_t;
+
+cairo_public void
+cairo_set_line_join (cairo_t *cr, cairo_line_join_t line_join);
+
+cairo_public void
+cairo_set_dash (cairo_t      *cr,
+               const double *dashes,
+               int           num_dashes,
+               double        offset);
+
+cairo_public void
+cairo_set_miter_limit (cairo_t *cr, double limit);
+
+cairo_public void
+cairo_translate (cairo_t *cr, double tx, double ty);
+
+cairo_public void
+cairo_scale (cairo_t *cr, double sx, double sy);
+
+cairo_public void
+cairo_rotate (cairo_t *cr, double angle);
+
+cairo_public void
+cairo_transform (cairo_t             *cr,
+                const cairo_matrix_t *matrix);
+
+cairo_public void
+cairo_set_matrix (cairo_t             *cr,
+                 const cairo_matrix_t *matrix);
+
+cairo_public void
+cairo_identity_matrix (cairo_t *cr);
+
+cairo_public void
+cairo_user_to_device (cairo_t *cr, double *x, double *y);
+
+cairo_public void
+cairo_user_to_device_distance (cairo_t *cr, double *dx, double *dy);
+
+cairo_public void
+cairo_device_to_user (cairo_t *cr, double *x, double *y);
+
+cairo_public void
+cairo_device_to_user_distance (cairo_t *cr, double *dx, double *dy);
+
+/* Path creation functions */
+cairo_public void
+cairo_new_path (cairo_t *cr);
+
+cairo_public void
+cairo_move_to (cairo_t *cr, double x, double y);
+
+cairo_public void
+cairo_new_sub_path (cairo_t *cr);
+
+cairo_public void
+cairo_line_to (cairo_t *cr, double x, double y);
+
+cairo_public void
+cairo_curve_to (cairo_t *cr,
+               double x1, double y1,
+               double x2, double y2,
+               double x3, double y3);
+
+cairo_public void
+cairo_arc (cairo_t *cr,
+          double xc, double yc,
+          double radius,
+          double angle1, double angle2);
+
+cairo_public void
+cairo_arc_negative (cairo_t *cr,
+                   double xc, double yc,
+                   double radius,
+                   double angle1, double angle2);
+
+/* XXX: NYI
+cairo_public void
+cairo_arc_to (cairo_t *cr,
+             double x1, double y1,
+             double x2, double y2,
+             double radius);
+*/
+
+cairo_public void
+cairo_rel_move_to (cairo_t *cr, double dx, double dy);
+
+cairo_public void
+cairo_rel_line_to (cairo_t *cr, double dx, double dy);
+
+cairo_public void
+cairo_rel_curve_to (cairo_t *cr,
+                   double dx1, double dy1,
+                   double dx2, double dy2,
+                   double dx3, double dy3);
+
+cairo_public void
+cairo_rectangle (cairo_t *cr,
+                double x, double y,
+                double width, double height);
+
+/* XXX: NYI
+cairo_public void
+cairo_stroke_to_path (cairo_t *cr);
+*/
+
+cairo_public void
+cairo_close_path (cairo_t *cr);
+
+cairo_public void
+cairo_path_extents (cairo_t *cr,
+                   double *x1, double *y1,
+                   double *x2, double *y2);
+
+/* Painting functions */
+cairo_public void
+cairo_paint (cairo_t *cr);
+
+cairo_public void
+cairo_paint_with_alpha (cairo_t *cr,
+                       double   alpha);
+
+cairo_public void
+cairo_mask (cairo_t         *cr,
+           cairo_pattern_t *pattern);
+
+cairo_public void
+cairo_mask_surface (cairo_t         *cr,
+                   cairo_surface_t *surface,
+                   double           surface_x,
+                   double           surface_y);
+
+cairo_public void
+cairo_stroke (cairo_t *cr);
+
+cairo_public void
+cairo_stroke_preserve (cairo_t *cr);
+
+cairo_public void
+cairo_fill (cairo_t *cr);
+
+cairo_public void
+cairo_fill_preserve (cairo_t *cr);
+
+cairo_public void
+cairo_copy_page (cairo_t *cr);
+
+cairo_public void
+cairo_show_page (cairo_t *cr);
+
+/* Insideness testing */
+cairo_public cairo_bool_t
+cairo_in_stroke (cairo_t *cr, double x, double y);
+
+cairo_public cairo_bool_t
+cairo_in_fill (cairo_t *cr, double x, double y);
+
+cairo_public cairo_bool_t
+cairo_in_clip (cairo_t *cr, double x, double y);
+
+/* Rectangular extents */
+cairo_public void
+cairo_stroke_extents (cairo_t *cr,
+                     double *x1, double *y1,
+                     double *x2, double *y2);
+
+cairo_public void
+cairo_fill_extents (cairo_t *cr,
+                   double *x1, double *y1,
+                   double *x2, double *y2);
+
+/* Clipping */
+cairo_public void
+cairo_reset_clip (cairo_t *cr);
+
+cairo_public void
+cairo_clip (cairo_t *cr);
+
+cairo_public void
+cairo_clip_preserve (cairo_t *cr);
+
+cairo_public void
+cairo_clip_extents (cairo_t *cr,
+                   double *x1, double *y1,
+                   double *x2, double *y2);
+
+/**
+ * cairo_rectangle_t:
+ * @x: X coordinate of the left side of the rectangle
+ * @y: Y coordinate of the the top side of the rectangle
+ * @width: width of the rectangle
+ * @height: height of the rectangle
+ *
+ * A data structure for holding a rectangle.
+ *
+ * Since: 1.4
+ **/
+typedef struct _cairo_rectangle {
+    double x, y, width, height;
+} cairo_rectangle_t;
+
+/**
+ * cairo_rectangle_list_t:
+ * @status: Error status of the rectangle list
+ * @rectangles: Array containing the rectangles
+ * @num_rectangles: Number of rectangles in this list
+ *
+ * A data structure for holding a dynamically allocated
+ * array of rectangles.
+ *
+ * Since: 1.4
+ **/
+typedef struct _cairo_rectangle_list {
+    cairo_status_t     status;
+    cairo_rectangle_t *rectangles;
+    int                num_rectangles;
+} cairo_rectangle_list_t;
+
+cairo_public cairo_rectangle_list_t *
+cairo_copy_clip_rectangle_list (cairo_t *cr);
+
+cairo_public void
+cairo_rectangle_list_destroy (cairo_rectangle_list_t *rectangle_list);
+
+/* Font/Text functions */
+
+/**
+ * cairo_scaled_font_t:
+ *
+ * A #cairo_scaled_font_t is a font scaled to a particular size and device
+ * resolution. A #cairo_scaled_font_t is most useful for low-level font
+ * usage where a library or application wants to cache a reference
+ * to a scaled font to speed up the computation of metrics.
+ *
+ * There are various types of scaled fonts, depending on the
+ * <firstterm>font backend</firstterm> they use. The type of a
+ * scaled font can be queried using cairo_scaled_font_get_type().
+ *
+ * Memory management of #cairo_scaled_font_t is done with
+ * cairo_scaled_font_reference() and cairo_scaled_font_destroy().
+ **/
+typedef struct _cairo_scaled_font cairo_scaled_font_t;
+
+/**
+ * cairo_font_face_t:
+ *
+ * A #cairo_font_face_t specifies all aspects of a font other
+ * than the size or font matrix (a font matrix is used to distort
+ * a font by sheering it or scaling it unequally in the two
+ * directions) . A font face can be set on a #cairo_t by using
+ * cairo_set_font_face(); the size and font matrix are set with
+ * cairo_set_font_size() and cairo_set_font_matrix().
+ *
+ * There are various types of font faces, depending on the
+ * <firstterm>font backend</firstterm> they use. The type of a
+ * font face can be queried using cairo_font_face_get_type().
+ *
+ * Memory management of #cairo_font_face_t is done with
+ * cairo_font_face_reference() and cairo_font_face_destroy().
+ **/
+typedef struct _cairo_font_face cairo_font_face_t;
+
+/**
+ * cairo_glyph_t:
+ * @index: glyph index in the font. The exact interpretation of the
+ *      glyph index depends on the font technology being used.
+ * @x: the offset in the X direction between the origin used for
+ *     drawing or measuring the string and the origin of this glyph.
+ * @y: the offset in the Y direction between the origin used for
+ *     drawing or measuring the string and the origin of this glyph.
+ *
+ * The #cairo_glyph_t structure holds information about a single glyph
+ * when drawing or measuring text. A font is (in simple terms) a
+ * collection of shapes used to draw text. A glyph is one of these
+ * shapes. There can be multiple glyphs for a single character
+ * (alternates to be used in different contexts, for example), or a
+ * glyph can be a <firstterm>ligature</firstterm> of multiple
+ * characters. Cairo doesn't expose any way of converting input text
+ * into glyphs, so in order to use the Cairo interfaces that take
+ * arrays of glyphs, you must directly access the appropriate
+ * underlying font system.
+ *
+ * Note that the offsets given by @x and @y are not cumulative. When
+ * drawing or measuring text, each glyph is individually positioned
+ * with respect to the overall origin
+ **/
+typedef struct {
+    unsigned long        index;
+    double               x;
+    double               y;
+} cairo_glyph_t;
+
+cairo_public cairo_glyph_t *
+cairo_glyph_allocate (int num_glyphs);
+
+cairo_public void
+cairo_glyph_free (cairo_glyph_t *glyphs);
+
+/**
+ * cairo_text_cluster_t:
+ * @num_bytes: the number of bytes of UTF-8 text covered by cluster
+ * @num_glyphs: the number of glyphs covered by cluster
+ *
+ * The #cairo_text_cluster_t structure holds information about a single
+ * <firstterm>text cluster</firstterm>.  A text cluster is a minimal
+ * mapping of some glyphs corresponding to some UTF-8 text.
+ *
+ * For a cluster to be valid, both @num_bytes and @num_glyphs should
+ * be non-negative, and at least one should be non-zero.
+ * Note that clusters with zero glyphs are not as well supported as
+ * normal clusters.  For example, PDF rendering applications typically
+ * ignore those clusters when PDF text is being selected.
+ *
+ * See cairo_show_text_glyphs() for how clusters are used in advanced
+ * text operations.
+ *
+ * Since: 1.8
+ **/
+typedef struct {
+    int        num_bytes;
+    int        num_glyphs;
+} cairo_text_cluster_t;
+
+cairo_public cairo_text_cluster_t *
+cairo_text_cluster_allocate (int num_clusters);
+
+cairo_public void
+cairo_text_cluster_free (cairo_text_cluster_t *clusters);
+
+/**
+ * cairo_text_cluster_flags_t:
+ * @CAIRO_TEXT_CLUSTER_FLAG_BACKWARD: The clusters in the cluster array
+ * map to glyphs in the glyph array from end to start.
+ *
+ * Specifies properties of a text cluster mapping.
+ *
+ * Since: 1.8
+ **/
+typedef enum _cairo_text_cluster_flags {
+    CAIRO_TEXT_CLUSTER_FLAG_BACKWARD = 0x00000001
+} cairo_text_cluster_flags_t;
+
+/**
+ * cairo_text_extents_t:
+ * @x_bearing: the horizontal distance from the origin to the
+ *   leftmost part of the glyphs as drawn. Positive if the
+ *   glyphs lie entirely to the right of the origin.
+ * @y_bearing: the vertical distance from the origin to the
+ *   topmost part of the glyphs as drawn. Positive only if the
+ *   glyphs lie completely below the origin; will usually be
+ *   negative.
+ * @width: width of the glyphs as drawn
+ * @height: height of the glyphs as drawn
+ * @x_advance:distance to advance in the X direction
+ *    after drawing these glyphs
+ * @y_advance: distance to advance in the Y direction
+ *   after drawing these glyphs. Will typically be zero except
+ *   for vertical text layout as found in East-Asian languages.
+ *
+ * The #cairo_text_extents_t structure stores the extents of a single
+ * glyph or a string of glyphs in user-space coordinates. Because text
+ * extents are in user-space coordinates, they are mostly, but not
+ * entirely, independent of the current transformation matrix. If you call
+ * <literal>cairo_scale(cr, 2.0, 2.0)</literal>, text will
+ * be drawn twice as big, but the reported text extents will not be
+ * doubled. They will change slightly due to hinting (so you can't
+ * assume that metrics are independent of the transformation matrix),
+ * but otherwise will remain unchanged.
+ **/
+typedef struct {
+    double x_bearing;
+    double y_bearing;
+    double width;
+    double height;
+    double x_advance;
+    double y_advance;
+} cairo_text_extents_t;
+
+/**
+ * cairo_font_extents_t:
+ * @ascent: the distance that the font extends above the baseline.
+ *          Note that this is not always exactly equal to the maximum
+ *          of the extents of all the glyphs in the font, but rather
+ *          is picked to express the font designer's intent as to
+ *          how the font should align with elements above it.
+ * @descent: the distance that the font extends below the baseline.
+ *           This value is positive for typical fonts that include
+ *           portions below the baseline. Note that this is not always
+ *           exactly equal to the maximum of the extents of all the
+ *           glyphs in the font, but rather is picked to express the
+ *           font designer's intent as to how the font should
+ *           align with elements below it.
+ * @height: the recommended vertical distance between baselines when
+ *          setting consecutive lines of text with the font. This
+ *          is greater than @ascent+@descent by a
+ *          quantity known as the <firstterm>line spacing</firstterm>
+ *          or <firstterm>external leading</firstterm>. When space
+ *          is at a premium, most fonts can be set with only
+ *          a distance of @ascent+@descent between lines.
+ * @max_x_advance: the maximum distance in the X direction that
+ *         the origin is advanced for any glyph in the font.
+ * @max_y_advance: the maximum distance in the Y direction that
+ *         the origin is advanced for any glyph in the font.
+ *         This will be zero for normal fonts used for horizontal
+ *         writing. (The scripts of East Asia are sometimes written
+ *         vertically.)
+ *
+ * The #cairo_font_extents_t structure stores metric information for
+ * a font. Values are given in the current user-space coordinate
+ * system.
+ *
+ * Because font metrics are in user-space coordinates, they are
+ * mostly, but not entirely, independent of the current transformation
+ * matrix. If you call <literal>cairo_scale(cr, 2.0, 2.0)</literal>,
+ * text will be drawn twice as big, but the reported text extents will
+ * not be doubled. They will change slightly due to hinting (so you
+ * can't assume that metrics are independent of the transformation
+ * matrix), but otherwise will remain unchanged.
+ **/
+typedef struct {
+    double ascent;
+    double descent;
+    double height;
+    double max_x_advance;
+    double max_y_advance;
+} cairo_font_extents_t;
+
+/**
+ * cairo_font_slant_t:
+ * @CAIRO_FONT_SLANT_NORMAL: Upright font style
+ * @CAIRO_FONT_SLANT_ITALIC: Italic font style
+ * @CAIRO_FONT_SLANT_OBLIQUE: Oblique font style
+ *
+ * Specifies variants of a font face based on their slant.
+ **/
+typedef enum _cairo_font_slant {
+    CAIRO_FONT_SLANT_NORMAL,
+    CAIRO_FONT_SLANT_ITALIC,
+    CAIRO_FONT_SLANT_OBLIQUE
+} cairo_font_slant_t;
+
+/**
+ * cairo_font_weight_t:
+ * @CAIRO_FONT_WEIGHT_NORMAL: Normal font weight
+ * @CAIRO_FONT_WEIGHT_BOLD: Bold font weight
+ *
+ * Specifies variants of a font face based on their weight.
+ **/
+typedef enum _cairo_font_weight {
+    CAIRO_FONT_WEIGHT_NORMAL,
+    CAIRO_FONT_WEIGHT_BOLD
+} cairo_font_weight_t;
+
+/**
+ * cairo_subpixel_order_t:
+ * @CAIRO_SUBPIXEL_ORDER_DEFAULT: Use the default subpixel order for
+ *   for the target device
+ * @CAIRO_SUBPIXEL_ORDER_RGB: Subpixel elements are arranged horizontally
+ *   with red at the left
+ * @CAIRO_SUBPIXEL_ORDER_BGR:  Subpixel elements are arranged horizontally
+ *   with blue at the left
+ * @CAIRO_SUBPIXEL_ORDER_VRGB: Subpixel elements are arranged vertically
+ *   with red at the top
+ * @CAIRO_SUBPIXEL_ORDER_VBGR: Subpixel elements are arranged vertically
+ *   with blue at the top
+ *
+ * The subpixel order specifies the order of color elements within
+ * each pixel on the display device when rendering with an
+ * antialiasing mode of %CAIRO_ANTIALIAS_SUBPIXEL.
+ **/
+typedef enum _cairo_subpixel_order {
+    CAIRO_SUBPIXEL_ORDER_DEFAULT,
+    CAIRO_SUBPIXEL_ORDER_RGB,
+    CAIRO_SUBPIXEL_ORDER_BGR,
+    CAIRO_SUBPIXEL_ORDER_VRGB,
+    CAIRO_SUBPIXEL_ORDER_VBGR
+} cairo_subpixel_order_t;
+
+/**
+ * cairo_hint_style_t:
+ * @CAIRO_HINT_STYLE_DEFAULT: Use the default hint style for
+ *   font backend and target device
+ * @CAIRO_HINT_STYLE_NONE: Do not hint outlines
+ * @CAIRO_HINT_STYLE_SLIGHT: Hint outlines slightly to improve
+ *   contrast while retaining good fidelity to the original
+ *   shapes.
+ * @CAIRO_HINT_STYLE_MEDIUM: Hint outlines with medium strength
+ *   giving a compromise between fidelity to the original shapes
+ *   and contrast
+ * @CAIRO_HINT_STYLE_FULL: Hint outlines to maximize contrast
+ *
+ * Specifies the type of hinting to do on font outlines. Hinting
+ * is the process of fitting outlines to the pixel grid in order
+ * to improve the appearance of the result. Since hinting outlines
+ * involves distorting them, it also reduces the faithfulness
+ * to the original outline shapes. Not all of the outline hinting
+ * styles are supported by all font backends.
+ *
+ * New entries may be added in future versions.
+ **/
+typedef enum _cairo_hint_style {
+    CAIRO_HINT_STYLE_DEFAULT,
+    CAIRO_HINT_STYLE_NONE,
+    CAIRO_HINT_STYLE_SLIGHT,
+    CAIRO_HINT_STYLE_MEDIUM,
+    CAIRO_HINT_STYLE_FULL
+} cairo_hint_style_t;
+
+/**
+ * cairo_hint_metrics_t:
+ * @CAIRO_HINT_METRICS_DEFAULT: Hint metrics in the default
+ *  manner for the font backend and target device
+ * @CAIRO_HINT_METRICS_OFF: Do not hint font metrics
+ * @CAIRO_HINT_METRICS_ON: Hint font metrics
+ *
+ * Specifies whether to hint font metrics; hinting font metrics
+ * means quantizing them so that they are integer values in
+ * device space. Doing this improves the consistency of
+ * letter and line spacing, however it also means that text
+ * will be laid out differently at different zoom factors.
+ **/
+typedef enum _cairo_hint_metrics {
+    CAIRO_HINT_METRICS_DEFAULT,
+    CAIRO_HINT_METRICS_OFF,
+    CAIRO_HINT_METRICS_ON
+} cairo_hint_metrics_t;
+
+/**
+ * cairo_font_options_t:
+ *
+ * An opaque structure holding all options that are used when
+ * rendering fonts.
+ *
+ * Individual features of a #cairo_font_options_t can be set or
+ * accessed using functions named
+ * <function>cairo_font_options_set_<emphasis>feature_name</emphasis>()</function> and
+ * <function>cairo_font_options_get_<emphasis>feature_name</emphasis>()</function>, like
+ * cairo_font_options_set_antialias() and
+ * cairo_font_options_get_antialias().
+ *
+ * New features may be added to a #cairo_font_options_t in the
+ * future.  For this reason, cairo_font_options_copy(),
+ * cairo_font_options_equal(), cairo_font_options_merge(), and
+ * cairo_font_options_hash() should be used to copy, check
+ * for equality, merge, or compute a hash value of
+ * #cairo_font_options_t objects.
+ **/
+typedef struct _cairo_font_options cairo_font_options_t;
+
+cairo_public cairo_font_options_t *
+cairo_font_options_create (void);
+
+cairo_public cairo_font_options_t *
+cairo_font_options_copy (const cairo_font_options_t *original);
+
+cairo_public void
+cairo_font_options_destroy (cairo_font_options_t *options);
+
+cairo_public cairo_status_t
+cairo_font_options_status (cairo_font_options_t *options);
+
+cairo_public void
+cairo_font_options_merge (cairo_font_options_t       *options,
+                         const cairo_font_options_t *other);
+cairo_public cairo_bool_t
+cairo_font_options_equal (const cairo_font_options_t *options,
+                         const cairo_font_options_t *other);
+
+cairo_public unsigned long
+cairo_font_options_hash (const cairo_font_options_t *options);
+
+cairo_public void
+cairo_font_options_set_antialias (cairo_font_options_t *options,
+                                 cairo_antialias_t     antialias);
+cairo_public cairo_antialias_t
+cairo_font_options_get_antialias (const cairo_font_options_t *options);
+
+cairo_public void
+cairo_font_options_set_subpixel_order (cairo_font_options_t   *options,
+                                      cairo_subpixel_order_t  subpixel_order);
+cairo_public cairo_subpixel_order_t
+cairo_font_options_get_subpixel_order (const cairo_font_options_t *options);
+
+cairo_public void
+cairo_font_options_set_hint_style (cairo_font_options_t *options,
+                                  cairo_hint_style_t     hint_style);
+cairo_public cairo_hint_style_t
+cairo_font_options_get_hint_style (const cairo_font_options_t *options);
+
+cairo_public void
+cairo_font_options_set_hint_metrics (cairo_font_options_t *options,
+                                    cairo_hint_metrics_t  hint_metrics);
+cairo_public cairo_hint_metrics_t
+cairo_font_options_get_hint_metrics (const cairo_font_options_t *options);
+
+/* This interface is for dealing with text as text, not caring about the
+   font object inside the the cairo_t. */
+
+cairo_public void
+cairo_select_font_face (cairo_t              *cr,
+                       const char           *family,
+                       cairo_font_slant_t   slant,
+                       cairo_font_weight_t  weight);
+
+cairo_public void
+cairo_set_font_size (cairo_t *cr, double size);
+
+cairo_public void
+cairo_set_font_matrix (cairo_t             *cr,
+                      const cairo_matrix_t *matrix);
+
+cairo_public void
+cairo_get_font_matrix (cairo_t *cr,
+                      cairo_matrix_t *matrix);
+
+cairo_public void
+cairo_set_font_options (cairo_t                    *cr,
+                       const cairo_font_options_t *options);
+
+cairo_public void
+cairo_get_font_options (cairo_t              *cr,
+                       cairo_font_options_t *options);
+
+cairo_public void
+cairo_set_font_face (cairo_t *cr, cairo_font_face_t *font_face);
+
+cairo_public cairo_font_face_t *
+cairo_get_font_face (cairo_t *cr);
+
+cairo_public void
+cairo_set_scaled_font (cairo_t                   *cr,
+                      const cairo_scaled_font_t *scaled_font);
+
+cairo_public cairo_scaled_font_t *
+cairo_get_scaled_font (cairo_t *cr);
+
+cairo_public void
+cairo_show_text (cairo_t *cr, const char *utf8);
+
+cairo_public void
+cairo_show_glyphs (cairo_t *cr, const cairo_glyph_t *glyphs, int num_glyphs);
+
+cairo_public 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  cluster_flags);
+
+cairo_public void
+cairo_text_path  (cairo_t *cr, const char *utf8);
+
+cairo_public void
+cairo_glyph_path (cairo_t *cr, const cairo_glyph_t *glyphs, int num_glyphs);
+
+cairo_public void
+cairo_text_extents (cairo_t              *cr,
+                   const char           *utf8,
+                   cairo_text_extents_t *extents);
+
+cairo_public void
+cairo_glyph_extents (cairo_t               *cr,
+                    const cairo_glyph_t   *glyphs,
+                    int                   num_glyphs,
+                    cairo_text_extents_t  *extents);
+
+cairo_public void
+cairo_font_extents (cairo_t              *cr,
+                   cairo_font_extents_t *extents);
+
+/* Generic identifier for a font style */
+
+cairo_public cairo_font_face_t *
+cairo_font_face_reference (cairo_font_face_t *font_face);
+
+cairo_public void
+cairo_font_face_destroy (cairo_font_face_t *font_face);
+
+cairo_public unsigned int
+cairo_font_face_get_reference_count (cairo_font_face_t *font_face);
+
+cairo_public cairo_status_t
+cairo_font_face_status (cairo_font_face_t *font_face);
+
+
+/**
+ * cairo_font_type_t:
+ * @CAIRO_FONT_TYPE_TOY: The font was created using cairo's toy font api
+ * @CAIRO_FONT_TYPE_FT: The font is of type FreeType
+ * @CAIRO_FONT_TYPE_WIN32: The font is of type Win32
+ * @CAIRO_FONT_TYPE_QUARTZ: The font is of type Quartz (Since: 1.6)
+ * @CAIRO_FONT_TYPE_USER: The font was create using cairo's user font api (Since: 1.8)
+ *
+ * #cairo_font_type_t is used to describe the type of a given font
+ * face or scaled font. The font types are also known as "font
+ * backends" within cairo.
+ *
+ * The type of a font face is determined by the function used to
+ * create it, which will generally be of the form
+ * <function>cairo_<emphasis>type</emphasis>_font_face_create(<!-- -->)</function>.
+ * The font face type can be queried with cairo_font_face_get_type()
+ *
+ * The various #cairo_font_face_t functions can be used with a font face
+ * of any type.
+ *
+ * The type of a scaled font is determined by the type of the font
+ * face passed to cairo_scaled_font_create(). The scaled font type can
+ * be queried with cairo_scaled_font_get_type()
+ *
+ * The various #cairo_scaled_font_t functions can be used with scaled
+ * fonts of any type, but some font backends also provide
+ * type-specific functions that must only be called with a scaled font
+ * of the appropriate type. These functions have names that begin with
+ * <function>cairo_<emphasis>type</emphasis>_scaled_font(<!-- -->)</function>
+ * such as cairo_ft_scaled_font_lock_face().
+ *
+ * The behavior of calling a type-specific function with a scaled font
+ * of the wrong type is undefined.
+ *
+ * New entries may be added in future versions.
+ *
+ * Since: 1.2
+ **/
+typedef enum _cairo_font_type {
+    CAIRO_FONT_TYPE_TOY,
+    CAIRO_FONT_TYPE_FT,
+    CAIRO_FONT_TYPE_WIN32,
+    CAIRO_FONT_TYPE_QUARTZ,
+    CAIRO_FONT_TYPE_USER
+} cairo_font_type_t;
+
+cairo_public cairo_font_type_t
+cairo_font_face_get_type (cairo_font_face_t *font_face);
+
+cairo_public void *
+cairo_font_face_get_user_data (cairo_font_face_t          *font_face,
+                              const cairo_user_data_key_t *key);
+
+cairo_public cairo_status_t
+cairo_font_face_set_user_data (cairo_font_face_t          *font_face,
+                              const cairo_user_data_key_t *key,
+                              void                        *user_data,
+                              cairo_destroy_func_t         destroy);
+
+/* Portable interface to general font features. */
+
+cairo_public 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_public cairo_scaled_font_t *
+cairo_scaled_font_reference (cairo_scaled_font_t *scaled_font);
+
+cairo_public void
+cairo_scaled_font_destroy (cairo_scaled_font_t *scaled_font);
+
+cairo_public unsigned int
+cairo_scaled_font_get_reference_count (cairo_scaled_font_t *scaled_font);
+
+cairo_public cairo_status_t
+cairo_scaled_font_status (cairo_scaled_font_t *scaled_font);
+
+cairo_public cairo_font_type_t
+cairo_scaled_font_get_type (cairo_scaled_font_t *scaled_font);
+
+cairo_public void *
+cairo_scaled_font_get_user_data (cairo_scaled_font_t         *scaled_font,
+                                const cairo_user_data_key_t *key);
+
+cairo_public cairo_status_t
+cairo_scaled_font_set_user_data (cairo_scaled_font_t         *scaled_font,
+                                const cairo_user_data_key_t *key,
+                                void                        *user_data,
+                                cairo_destroy_func_t         destroy);
+
+cairo_public void
+cairo_scaled_font_extents (cairo_scaled_font_t  *scaled_font,
+                          cairo_font_extents_t *extents);
+
+cairo_public void
+cairo_scaled_font_text_extents (cairo_scaled_font_t  *scaled_font,
+                               const char           *utf8,
+                               cairo_text_extents_t *extents);
+
+cairo_public void
+cairo_scaled_font_glyph_extents (cairo_scaled_font_t   *scaled_font,
+                                const cairo_glyph_t   *glyphs,
+                                int                   num_glyphs,
+                                cairo_text_extents_t  *extents);
+
+cairo_public cairo_status_t
+cairo_scaled_font_text_to_glyphs (cairo_scaled_font_t        *scaled_font,
+                                 double                      x,
+                                 double                      y,
+                                 const char                 *utf8,
+                                 int                         utf8_len,
+                                 cairo_glyph_t             **glyphs,
+                                 int                        *num_glyphs,
+                                 cairo_text_cluster_t      **clusters,
+                                 int                        *num_clusters,
+                                 cairo_text_cluster_flags_t *cluster_flags);
+
+cairo_public cairo_font_face_t *
+cairo_scaled_font_get_font_face (cairo_scaled_font_t *scaled_font);
+
+cairo_public void
+cairo_scaled_font_get_font_matrix (cairo_scaled_font_t *scaled_font,
+                                  cairo_matrix_t       *font_matrix);
+
+cairo_public void
+cairo_scaled_font_get_ctm (cairo_scaled_font_t *scaled_font,
+                          cairo_matrix_t       *ctm);
+
+cairo_public void
+cairo_scaled_font_get_scale_matrix (cairo_scaled_font_t        *scaled_font,
+                                   cairo_matrix_t      *scale_matrix);
+
+cairo_public void
+cairo_scaled_font_get_font_options (cairo_scaled_font_t                *scaled_font,
+                                   cairo_font_options_t        *options);
+
+
+/* Toy fonts */
+
+cairo_public cairo_font_face_t *
+cairo_toy_font_face_create (const char           *family,
+                           cairo_font_slant_t    slant,
+                           cairo_font_weight_t   weight);
+
+cairo_public const char *
+cairo_toy_font_face_get_family (cairo_font_face_t *font_face);
+
+cairo_public cairo_font_slant_t
+cairo_toy_font_face_get_slant (cairo_font_face_t *font_face);
+
+cairo_public cairo_font_weight_t
+cairo_toy_font_face_get_weight (cairo_font_face_t *font_face);
+
+
+/* User fonts */
+
+cairo_public cairo_font_face_t *
+cairo_user_font_face_create (void);
+
+/* User-font method signatures */
+
+/**
+ * cairo_user_scaled_font_init_func_t:
+ * @scaled_font: the scaled-font being created
+ * @cr: a cairo context, in font space
+ * @extents: font extents to fill in, in font space
+ *
+ * #cairo_user_scaled_font_init_func_t is the type of function which is
+ * called when a scaled-font needs to be created for a user font-face.
+ *
+ * The cairo context @cr is not used by the caller, but is prepared in font
+ * space, similar to what the cairo contexts passed to the render_glyph
+ * method will look like.  The callback can use this context for extents
+ * computation for example.  After the callback is called, @cr is checked
+ * for any error status.
+ *
+ * The @extents argument is where the user font sets the font extents for
+ * @scaled_font.  It is in font space, which means that for most cases its
+ * ascent and descent members should add to 1.0.  @extents is preset to
+ * hold a value of 1.0 for ascent, height, and max_x_advance, and 0.0 for
+ * descent and max_y_advance members.
+ *
+ * The callback is optional.  If not set, default font extents as described
+ * in the previous paragraph will be used.
+ *
+ * Note that @scaled_font is not fully initialized at this
+ * point and trying to use it for text operations in the callback will result
+ * in deadlock.
+ *
+ * Returns: %CAIRO_STATUS_SUCCESS upon success, or an error status on error.
+ *
+ * Since: 1.8
+ **/
+typedef cairo_status_t (*cairo_user_scaled_font_init_func_t) (cairo_scaled_font_t  *scaled_font,
+                                                             cairo_t              *cr,
+                                                             cairo_font_extents_t *extents);
+
+/**
+ * cairo_user_scaled_font_render_glyph_func_t:
+ * @scaled_font: user scaled-font
+ * @glyph: glyph code to render
+ * @cr: cairo context to draw to, in font space
+ * @extents: glyph extents to fill in, in font space
+ *
+ * #cairo_user_scaled_font_render_glyph_func_t is the type of function which
+ * is called when a user scaled-font needs to render a glyph.
+ *
+ * The callback is mandatory, and expected to draw the glyph with code @glyph to
+ * the cairo context @cr.  @cr is prepared such that the glyph drawing is done in
+ * font space.  That is, the matrix set on @cr is the scale matrix of @scaled_font,
+ * The @extents argument is where the user font sets the font extents for
+ * @scaled_font.  However, if user prefers to draw in user space, they can
+ * achieve that by changing the matrix on @cr.  All cairo rendering operations
+ * to @cr are permitted, however, the result is undefined if any source other
+ * than the default source on @cr is used.  That means, glyph bitmaps should
+ * be rendered using cairo_mask() instead of cairo_paint().
+ *
+ * Other non-default settings on @cr include a font size of 1.0 (given that
+ * it is set up to be in font space), and font options corresponding to
+ * @scaled_font.
+ *
+ * The @extents argument is preset to have <literal>x_bearing</literal>,
+ * <literal>width</literal>, and <literal>y_advance</literal> of zero,
+ * <literal>y_bearing</literal> set to <literal>-font_extents.ascent</literal>,
+ * <literal>height</literal> to <literal>font_extents.ascent+font_extents.descent</literal>,
+ * and <literal>x_advance</literal> to <literal>font_extents.max_x_advance</literal>.
+ * The only field user needs to set in majority of cases is
+ * <literal>x_advance</literal>.
+ * If the <literal>width</literal> field is zero upon the callback returning
+ * (which is its preset value), the glyph extents are automatically computed
+ * based on the drawings done to @cr.  This is in most cases exactly what the
+ * desired behavior is.  However, if for any reason the callback sets the
+ * extents, it must be ink extents, and include the extents of all drawing
+ * done to @cr in the callback.
+ *
+ * Returns: %CAIRO_STATUS_SUCCESS upon success, or
+ * %CAIRO_STATUS_USER_FONT_ERROR or any other error status on error.
+ *
+ * Since: 1.8
+ **/
+typedef cairo_status_t (*cairo_user_scaled_font_render_glyph_func_t) (cairo_scaled_font_t  *scaled_font,
+                                                                     unsigned long         glyph,
+                                                                     cairo_t              *cr,
+                                                                     cairo_text_extents_t *extents);
+
+/**
+ * cairo_user_scaled_font_text_to_glyphs_func_t:
+ * @scaled_font: the scaled-font being created
+ * @utf8: a string of text encoded in UTF-8
+ * @utf8_len: length of @utf8 in bytes
+ * @glyphs: pointer to array of glyphs to fill, in font space
+ * @num_glyphs: pointer to number of glyphs
+ * @clusters: pointer to array of cluster mapping information to fill, or %NULL
+ * @num_clusters: pointer to number of clusters
+ * @cluster_flags: pointer to location to store cluster flags corresponding to the
+ *                 output @clusters
+ *
+ * #cairo_user_scaled_font_text_to_glyphs_func_t is the type of function which
+ * is called to convert input text to an array of glyphs.  This is used by the
+ * cairo_show_text() operation.
+ *
+ * Using this callback the user-font has full control on glyphs and their
+ * positions.  That means, it allows for features like ligatures and kerning,
+ * as well as complex <firstterm>shaping</firstterm> required for scripts like
+ * Arabic and Indic.
+ *
+ * The @num_glyphs argument is preset to the number of glyph entries available
+ * in the @glyphs buffer. If the @glyphs buffer is %NULL, the value of
+ * @num_glyphs will be zero.  If the provided glyph array is too short for
+ * the conversion (or for convenience), a new glyph array may be allocated
+ * using cairo_glyph_allocate() and placed in @glyphs.  Upon return,
+ * @num_glyphs should contain the number of generated glyphs.  If the value
+ * @glyphs points at has changed after the call, the caller will free the
+ * allocated glyph array using cairo_glyph_free().  The caller will also free
+ * the original value of @glyphs, so the callback shouldn't do so.
+ * The callback should populate the glyph indices and positions (in font space)
+ * assuming that the text is to be shown at the origin.
+ *
+ * If @clusters is not %NULL, @num_clusters and @cluster_flags are also
+ * non-%NULL, and cluster mapping should be computed. The semantics of how
+ * cluster array allocation works is similar to the glyph array.  That is,
+ * if @clusters initially points to a non-%NULL value, that array may be used
+ * as a cluster buffer, and @num_clusters points to the number of cluster
+ * entries available there.  If the provided cluster array is too short for
+ * the conversion (or for convenience), a new cluster array may be allocated
+ * using cairo_text_cluster_allocate() and placed in @clusters.  In this case,
+ * the original value of @clusters will still be freed by the caller.  Upon
+ * return, @num_clusters should contain the number of generated clusters.
+ * If the value @clusters points at has changed after the call, the caller
+ * will free the allocated cluster array using cairo_text_cluster_free().
+ *
+ * The callback is optional.  If @num_glyphs is negative upon
+ * the callback returning or if the return value
+ * is %CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED, the unicode_to_glyph callback
+ * is tried.  See #cairo_user_scaled_font_unicode_to_glyph_func_t.
+ *
+ * Note: While cairo does not impose any limitation on glyph indices,
+ * some applications may assume that a glyph index fits in a 16-bit
+ * unsigned integer.  As such, it is advised that user-fonts keep their
+ * glyphs in the 0 to 65535 range.  Furthermore, some applications may
+ * assume that glyph 0 is a special glyph-not-found glyph.  User-fonts
+ * are advised to use glyph 0 for such purposes and do not use that
+ * glyph value for other purposes.
+ *
+ * Returns: %CAIRO_STATUS_SUCCESS upon success,
+ * %CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED if fallback options should be tried,
+ * or %CAIRO_STATUS_USER_FONT_ERROR or any other error status on error.
+ *
+ * Since: 1.8
+ **/
+typedef cairo_status_t (*cairo_user_scaled_font_text_to_glyphs_func_t) (cairo_scaled_font_t        *scaled_font,
+                                                                       const char                 *utf8,
+                                                                       int                         utf8_len,
+                                                                       cairo_glyph_t             **glyphs,
+                                                                       int                        *num_glyphs,
+                                                                       cairo_text_cluster_t      **clusters,
+                                                                       int                        *num_clusters,
+                                                                       cairo_text_cluster_flags_t *cluster_flags);
+
+/**
+ * cairo_user_scaled_font_unicode_to_glyph_func_t:
+ * @scaled_font: the scaled-font being created
+ * @unicode: input unicode character code-point
+ * @glyph_index: output glyph index
+ *
+ * #cairo_user_scaled_font_unicode_to_glyph_func_t is the type of function which
+ * is called to convert an input Unicode character to a single glyph.
+ * This is used by the cairo_show_text() operation.
+ *
+ * This callback is used to provide the same functionality as the
+ * text_to_glyphs callback does (see #cairo_user_scaled_font_text_to_glyphs_func_t)
+ * but has much less control on the output,
+ * in exchange for increased ease of use.  The inherent assumption to using
+ * this callback is that each character maps to one glyph, and that the
+ * mapping is context independent.  It also assumes that glyphs are positioned
+ * according to their advance width.  These mean no ligatures, kerning, or
+ * complex scripts can be implemented using this callback.
+ *
+ * The callback is optional, and only used if text_to_glyphs callback is not
+ * set or fails to return glyphs.  If this callback is not set or if it returns
+ * %CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED, an identity mapping from Unicode
+ * code-points to glyph indices is assumed.
+ *
+ * Note: While cairo does not impose any limitation on glyph indices,
+ * some applications may assume that a glyph index fits in a 16-bit
+ * unsigned integer.  As such, it is advised that user-fonts keep their
+ * glyphs in the 0 to 65535 range.  Furthermore, some applications may
+ * assume that glyph 0 is a special glyph-not-found glyph.  User-fonts
+ * are advised to use glyph 0 for such purposes and do not use that
+ * glyph value for other purposes.
+ *
+ * Returns: %CAIRO_STATUS_SUCCESS upon success,
+ * %CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED if fallback options should be tried,
+ * or %CAIRO_STATUS_USER_FONT_ERROR or any other error status on error.
+ *
+ * Since: 1.8
+ **/
+typedef cairo_status_t (*cairo_user_scaled_font_unicode_to_glyph_func_t) (cairo_scaled_font_t *scaled_font,
+                                                                         unsigned long        unicode,
+                                                                         unsigned long       *glyph_index);
+
+/* User-font method setters */
+
+cairo_public void
+cairo_user_font_face_set_init_func (cairo_font_face_t                  *font_face,
+                                   cairo_user_scaled_font_init_func_t  init_func);
+
+cairo_public void
+cairo_user_font_face_set_render_glyph_func (cairo_font_face_t                          *font_face,
+                                           cairo_user_scaled_font_render_glyph_func_t  render_glyph_func);
+
+cairo_public void
+cairo_user_font_face_set_text_to_glyphs_func (cairo_font_face_t                            *font_face,
+                                             cairo_user_scaled_font_text_to_glyphs_func_t  text_to_glyphs_func);
+
+cairo_public void
+cairo_user_font_face_set_unicode_to_glyph_func (cairo_font_face_t                              *font_face,
+                                               cairo_user_scaled_font_unicode_to_glyph_func_t  unicode_to_glyph_func);
+
+/* User-font method getters */
+
+cairo_public cairo_user_scaled_font_init_func_t
+cairo_user_font_face_get_init_func (cairo_font_face_t *font_face);
+
+cairo_public cairo_user_scaled_font_render_glyph_func_t
+cairo_user_font_face_get_render_glyph_func (cairo_font_face_t *font_face);
+
+cairo_public cairo_user_scaled_font_text_to_glyphs_func_t
+cairo_user_font_face_get_text_to_glyphs_func (cairo_font_face_t *font_face);
+
+cairo_public cairo_user_scaled_font_unicode_to_glyph_func_t
+cairo_user_font_face_get_unicode_to_glyph_func (cairo_font_face_t *font_face);
+
+
+/* Query functions */
+
+cairo_public cairo_operator_t
+cairo_get_operator (cairo_t *cr);
+
+cairo_public cairo_pattern_t *
+cairo_get_source (cairo_t *cr);
+
+cairo_public double
+cairo_get_tolerance (cairo_t *cr);
+
+cairo_public cairo_antialias_t
+cairo_get_antialias (cairo_t *cr);
+
+cairo_public cairo_bool_t
+cairo_has_current_point (cairo_t *cr);
+
+cairo_public void
+cairo_get_current_point (cairo_t *cr, double *x, double *y);
+
+cairo_public cairo_fill_rule_t
+cairo_get_fill_rule (cairo_t *cr);
+
+cairo_public double
+cairo_get_line_width (cairo_t *cr);
+
+cairo_public cairo_line_cap_t
+cairo_get_line_cap (cairo_t *cr);
+
+cairo_public cairo_line_join_t
+cairo_get_line_join (cairo_t *cr);
+
+cairo_public double
+cairo_get_miter_limit (cairo_t *cr);
+
+cairo_public int
+cairo_get_dash_count (cairo_t *cr);
+
+cairo_public void
+cairo_get_dash (cairo_t *cr, double *dashes, double *offset);
+
+cairo_public void
+cairo_get_matrix (cairo_t *cr, cairo_matrix_t *matrix);
+
+cairo_public cairo_surface_t *
+cairo_get_target (cairo_t *cr);
+
+cairo_public cairo_surface_t *
+cairo_get_group_target (cairo_t *cr);
+
+/**
+ * cairo_path_data_type_t:
+ * @CAIRO_PATH_MOVE_TO: A move-to operation
+ * @CAIRO_PATH_LINE_TO: A line-to operation
+ * @CAIRO_PATH_CURVE_TO: A curve-to operation
+ * @CAIRO_PATH_CLOSE_PATH: A close-path operation
+ *
+ * #cairo_path_data_t is used to describe the type of one portion
+ * of a path when represented as a #cairo_path_t.
+ * See #cairo_path_data_t for details.
+ **/
+typedef enum _cairo_path_data_type {
+    CAIRO_PATH_MOVE_TO,
+    CAIRO_PATH_LINE_TO,
+    CAIRO_PATH_CURVE_TO,
+    CAIRO_PATH_CLOSE_PATH
+} cairo_path_data_type_t;
+
+/**
+ * cairo_path_data_t:
+ *
+ * #cairo_path_data_t is used to represent the path data inside a
+ * #cairo_path_t.
+ *
+ * The data structure is designed to try to balance the demands of
+ * efficiency and ease-of-use. A path is represented as an array of
+ * #cairo_path_data_t, which is a union of headers and points.
+ *
+ * Each portion of the path is represented by one or more elements in
+ * the array, (one header followed by 0 or more points). The length
+ * value of the header is the number of array elements for the current
+ * portion including the header, (ie. length == 1 + # of points), and
+ * where the number of points for each element type is as follows:
+ *
+ * <programlisting>
+ *     %CAIRO_PATH_MOVE_TO:     1 point
+ *     %CAIRO_PATH_LINE_TO:     1 point
+ *     %CAIRO_PATH_CURVE_TO:    3 points
+ *     %CAIRO_PATH_CLOSE_PATH:  0 points
+ * </programlisting>
+ *
+ * The semantics and ordering of the coordinate values are consistent
+ * with cairo_move_to(), cairo_line_to(), cairo_curve_to(), and
+ * cairo_close_path().
+ *
+ * Here is sample code for iterating through a #cairo_path_t:
+ *
+ * <informalexample><programlisting>
+ *      int i;
+ *      cairo_path_t *path;
+ *      cairo_path_data_t *data;
+ * &nbsp;
+ *      path = cairo_copy_path (cr);
+ * &nbsp;
+ *      for (i=0; i < path->num_data; i += path->data[i].header.length) {
+ *          data = &amp;path->data[i];
+ *          switch (data->header.type) {
+ *          case CAIRO_PATH_MOVE_TO:
+ *              do_move_to_things (data[1].point.x, data[1].point.y);
+ *              break;
+ *          case CAIRO_PATH_LINE_TO:
+ *              do_line_to_things (data[1].point.x, data[1].point.y);
+ *              break;
+ *          case CAIRO_PATH_CURVE_TO:
+ *              do_curve_to_things (data[1].point.x, data[1].point.y,
+ *                                  data[2].point.x, data[2].point.y,
+ *                                  data[3].point.x, data[3].point.y);
+ *              break;
+ *          case CAIRO_PATH_CLOSE_PATH:
+ *              do_close_path_things ();
+ *              break;
+ *          }
+ *      }
+ *      cairo_path_destroy (path);
+ * </programlisting></informalexample>
+ *
+ * As of cairo 1.4, cairo does not mind if there are more elements in
+ * a portion of the path than needed.  Such elements can be used by
+ * users of the cairo API to hold extra values in the path data
+ * structure.  For this reason, it is recommended that applications
+ * always use <literal>data->header.length</literal> to
+ * iterate over the path data, instead of hardcoding the number of
+ * elements for each element type.
+ **/
+typedef union _cairo_path_data_t cairo_path_data_t;
+union _cairo_path_data_t {
+    struct {
+       cairo_path_data_type_t type;
+       int length;
+    } header;
+    struct {
+       double x, y;
+    } point;
+};
+
+/**
+ * cairo_path_t:
+ * @status: the current error status
+ * @data: the elements in the path
+ * @num_data: the number of elements in the data array
+ *
+ * A data structure for holding a path. This data structure serves as
+ * the return value for cairo_copy_path() and
+ * cairo_copy_path_flat() as well the input value for
+ * cairo_append_path().
+ *
+ * See #cairo_path_data_t for hints on how to iterate over the
+ * actual data within the path.
+ *
+ * The num_data member gives the number of elements in the data
+ * array. This number is larger than the number of independent path
+ * portions (defined in #cairo_path_data_type_t), since the data
+ * includes both headers and coordinates for each portion.
+ **/
+typedef struct cairo_path {
+    cairo_status_t status;
+    cairo_path_data_t *data;
+    int num_data;
+} cairo_path_t;
+
+cairo_public cairo_path_t *
+cairo_copy_path (cairo_t *cr);
+
+cairo_public cairo_path_t *
+cairo_copy_path_flat (cairo_t *cr);
+
+cairo_public void
+cairo_append_path (cairo_t             *cr,
+                  const cairo_path_t   *path);
+
+cairo_public void
+cairo_path_destroy (cairo_path_t *path);
+
+/* Error status queries */
+
+cairo_public cairo_status_t
+cairo_status (cairo_t *cr);
+
+cairo_public const char *
+cairo_status_to_string (cairo_status_t status);
+
+/* Backend device manipulation */
+
+cairo_public cairo_device_t *
+cairo_device_reference (cairo_device_t *device);
+
+/**
+ * cairo_device_type_t:
+ * @CAIRO_DEVICE_TYPE_INVALID: The device is not valid
+ * @CAIRO_DEVICE_TYPE_DRM: The device is of type Direct Render Manager
+ * @CAIRO_DEVICE_TYPE_GL: The device is of type OpenGL
+ * @CAIRO_DEVICE_TYPE_SCRIPT: The device is of type script
+ * @CAIRO_DEVICE_TYPE_XCB: The device is of type xcb
+ * @CAIRO_DEVICE_TYPE_XLIB: The device is of type xlib
+ * @CAIRO_DEVICE_TYPE_XML: The device is of type XML
+ * @CAIRO_DEVICE_TYPE_INVALID: The device is invalid
+ *
+ * #cairo_device_type_t is used to describe the type of a given
+ * device. The devices types are also known as "backends" within cairo.
+ *
+ * The device type can be queried with cairo_device_get_type()
+ *
+ * The various #cairo_device_t functions can be used with devices of
+ * any type, but some backends also provide type-specific functions
+ * that must only be called with a device of the appropriate
+ * type. These functions have names that begin with
+ * <literal>cairo_<emphasis>type</emphasis>_device</literal> such as
+ * cairo_xcb_device_debug_cap_xrender_version().
+ *
+ * The behavior of calling a type-specific function with a device of
+ * the wrong type is undefined.
+ *
+ * New entries may be added in future versions.
+ *
+ * Since: 1.10
+ **/
+typedef enum _cairo_device_type {
+    CAIRO_DEVICE_TYPE_DRM,
+    CAIRO_DEVICE_TYPE_GL,
+    CAIRO_DEVICE_TYPE_SCRIPT,
+    CAIRO_DEVICE_TYPE_XCB,
+    CAIRO_DEVICE_TYPE_XLIB,
+    CAIRO_DEVICE_TYPE_XML,
+
+    CAIRO_DEVICE_TYPE_INVALID = -1
+} cairo_device_type_t;
+
+cairo_public cairo_device_type_t
+cairo_device_get_type (cairo_device_t *device);
+
+cairo_public cairo_status_t
+cairo_device_status (cairo_device_t *device);
+
+cairo_public cairo_status_t
+cairo_device_acquire (cairo_device_t *device);
+
+cairo_public void
+cairo_device_release (cairo_device_t *device);
+
+cairo_public void
+cairo_device_flush (cairo_device_t *device);
+
+cairo_public void
+cairo_device_finish (cairo_device_t *device);
+
+cairo_public void
+cairo_device_destroy (cairo_device_t *device);
+
+cairo_public unsigned int
+cairo_device_get_reference_count (cairo_device_t *device);
+
+cairo_public void *
+cairo_device_get_user_data (cairo_device_t              *device,
+                           const cairo_user_data_key_t *key);
+
+cairo_public cairo_status_t
+cairo_device_set_user_data (cairo_device_t              *device,
+                           const cairo_user_data_key_t *key,
+                           void                         *user_data,
+                           cairo_destroy_func_t          destroy);
+
+
+/* Surface manipulation */
+
+cairo_public cairo_surface_t *
+cairo_surface_create_similar (cairo_surface_t  *other,
+                             cairo_content_t   content,
+                             int               width,
+                             int               height);
+
+cairo_public cairo_surface_t *
+cairo_surface_create_similar_image (cairo_surface_t  *other,
+                                   cairo_format_t    format,
+                                   int         width,
+                                   int         height);
+
+cairo_public cairo_surface_t *
+cairo_surface_map_to_image (cairo_surface_t  *surface,
+                           const cairo_rectangle_int_t *extents);
+
+cairo_public void
+cairo_surface_unmap_image (cairo_surface_t *surface,
+                          cairo_surface_t *image);
+
+cairo_public cairo_surface_t *
+cairo_surface_create_for_rectangle (cairo_surface_t    *target,
+                                    double              x,
+                                    double              y,
+                                    double              width,
+                                    double              height);
+
+typedef enum {
+       CAIRO_SURFACE_OBSERVER_NORMAL = 0,
+       CAIRO_SURFACE_OBSERVER_RECORD_OPERATIONS = 0x1,
+} cairo_surface_observer_mode_t;
+
+cairo_public cairo_surface_t *
+cairo_surface_create_observer (cairo_surface_t *target,
+                              cairo_surface_observer_mode_t mode);
+
+typedef void (*cairo_surface_observer_callback_t) (cairo_surface_t *observer,
+                                                  cairo_surface_t *target,
+                                                  void *data);
+
+cairo_public cairo_status_t
+cairo_surface_observer_add_paint_callback (cairo_surface_t *abstract_surface,
+                                          cairo_surface_observer_callback_t func,
+                                          void *data);
+
+cairo_public cairo_status_t
+cairo_surface_observer_add_mask_callback (cairo_surface_t *abstract_surface,
+                                         cairo_surface_observer_callback_t func,
+                                         void *data);
+
+cairo_public cairo_status_t
+cairo_surface_observer_add_fill_callback (cairo_surface_t *abstract_surface,
+                                         cairo_surface_observer_callback_t func,
+                                         void *data);
+
+cairo_public cairo_status_t
+cairo_surface_observer_add_stroke_callback (cairo_surface_t *abstract_surface,
+                                           cairo_surface_observer_callback_t func,
+                                           void *data);
+
+cairo_public cairo_status_t
+cairo_surface_observer_add_glyphs_callback (cairo_surface_t *abstract_surface,
+                                           cairo_surface_observer_callback_t func,
+                                           void *data);
+
+cairo_public cairo_status_t
+cairo_surface_observer_add_flush_callback (cairo_surface_t *abstract_surface,
+                                          cairo_surface_observer_callback_t func,
+                                          void *data);
+
+cairo_public cairo_status_t
+cairo_surface_observer_add_finish_callback (cairo_surface_t *abstract_surface,
+                                           cairo_surface_observer_callback_t func,
+                                           void *data);
+
+cairo_public void
+cairo_surface_observer_print (cairo_surface_t *surface,
+                             cairo_write_func_t write_func,
+                             void *closure);
+cairo_public double
+cairo_surface_observer_elapsed (cairo_surface_t *surface);
+
+cairo_public void
+cairo_device_observer_print (cairo_device_t *device,
+                            cairo_write_func_t write_func,
+                            void *closure);
+
+cairo_public double
+cairo_device_observer_elapsed (cairo_device_t *device);
+
+cairo_public double
+cairo_device_observer_paint_elapsed (cairo_device_t *device);
+
+cairo_public double
+cairo_device_observer_mask_elapsed (cairo_device_t *device);
+
+cairo_public double
+cairo_device_observer_fill_elapsed (cairo_device_t *device);
+
+cairo_public double
+cairo_device_observer_stroke_elapsed (cairo_device_t *device);
+
+cairo_public double
+cairo_device_observer_glyphs_elapsed (cairo_device_t *device);
+
+cairo_public cairo_surface_t *
+cairo_surface_reference (cairo_surface_t *surface);
+
+cairo_public void
+cairo_surface_finish (cairo_surface_t *surface);
+
+cairo_public void
+cairo_surface_destroy (cairo_surface_t *surface);
+
+cairo_public cairo_device_t *
+cairo_surface_get_device (cairo_surface_t *surface);
+
+cairo_public unsigned int
+cairo_surface_get_reference_count (cairo_surface_t *surface);
+
+cairo_public cairo_status_t
+cairo_surface_status (cairo_surface_t *surface);
+
+/**
+ * cairo_surface_type_t:
+ * @CAIRO_SURFACE_TYPE_IMAGE: The surface is of type image
+ * @CAIRO_SURFACE_TYPE_PDF: The surface is of type pdf
+ * @CAIRO_SURFACE_TYPE_PS: The surface is of type ps
+ * @CAIRO_SURFACE_TYPE_XLIB: The surface is of type xlib
+ * @CAIRO_SURFACE_TYPE_XCB: The surface is of type xcb
+ * @CAIRO_SURFACE_TYPE_GLITZ: The surface is of type glitz
+ * @CAIRO_SURFACE_TYPE_QUARTZ: The surface is of type quartz
+ * @CAIRO_SURFACE_TYPE_WIN32: The surface is of type win32
+ * @CAIRO_SURFACE_TYPE_BEOS: The surface is of type beos
+ * @CAIRO_SURFACE_TYPE_DIRECTFB: The surface is of type directfb
+ * @CAIRO_SURFACE_TYPE_SVG: The surface is of type svg
+ * @CAIRO_SURFACE_TYPE_OS2: The surface is of type os2
+ * @CAIRO_SURFACE_TYPE_WIN32_PRINTING: The surface is a win32 printing surface
+ * @CAIRO_SURFACE_TYPE_QUARTZ_IMAGE: The surface is of type quartz_image
+ * @CAIRO_SURFACE_TYPE_SCRIPT: The surface is of type script, since 1.10
+ * @CAIRO_SURFACE_TYPE_QT: The surface is of type Qt, since 1.10
+ * @CAIRO_SURFACE_TYPE_RECORDING: The surface is of type recording, since 1.10
+ * @CAIRO_SURFACE_TYPE_VG: The surface is a OpenVG surface, since 1.10
+ * @CAIRO_SURFACE_TYPE_GL: The surface is of type OpenGL, since 1.10
+ * @CAIRO_SURFACE_TYPE_DRM: The surface is of type Direct Render Manager, since 1.10
+ * @CAIRO_SURFACE_TYPE_TEE: The surface is of type 'tee' (a multiplexing surface), since 1.10
+ * @CAIRO_SURFACE_TYPE_XML: The surface is of type XML (for debugging), since 1.10
+ * @CAIRO_SURFACE_TYPE_SKIA: The surface is of type Skia, since 1.10
+ * @CAIRO_SURFACE_TYPE_SUBSURFACE: The surface is a subsurface created with
+ *   cairo_surface_create_for_rectangle(), since 1.10
+ * @CAIRO_SURFACE_TYPE_MIME: The surface is a callback (mime) surface, since 1.12
+ *
+ * #cairo_surface_type_t is used to describe the type of a given
+ * surface. The surface types are also known as "backends" or "surface
+ * backends" within cairo.
+ *
+ * The type of a surface is determined by the function used to create
+ * it, which will generally be of the form
+ * <function>cairo_<emphasis>type</emphasis>_surface_create(<!-- -->)</function>,
+ * (though see cairo_surface_create_similar() as well).
+ *
+ * The surface type can be queried with cairo_surface_get_type()
+ *
+ * The various #cairo_surface_t functions can be used with surfaces of
+ * any type, but some backends also provide type-specific functions
+ * that must only be called with a surface of the appropriate
+ * type. These functions have names that begin with
+ * <literal>cairo_<emphasis>type</emphasis>_surface</literal> such as cairo_image_surface_get_width().
+ *
+ * The behavior of calling a type-specific function with a surface of
+ * the wrong type is undefined.
+ *
+ * New entries may be added in future versions.
+ *
+ * Since: 1.2
+ **/
+typedef enum _cairo_surface_type {
+    CAIRO_SURFACE_TYPE_IMAGE,
+    CAIRO_SURFACE_TYPE_PDF,
+    CAIRO_SURFACE_TYPE_PS,
+    CAIRO_SURFACE_TYPE_XLIB,
+    CAIRO_SURFACE_TYPE_XCB,
+    CAIRO_SURFACE_TYPE_GLITZ,
+    CAIRO_SURFACE_TYPE_QUARTZ,
+    CAIRO_SURFACE_TYPE_WIN32,
+    CAIRO_SURFACE_TYPE_BEOS,
+    CAIRO_SURFACE_TYPE_DIRECTFB,
+    CAIRO_SURFACE_TYPE_SVG,
+    CAIRO_SURFACE_TYPE_OS2,
+    CAIRO_SURFACE_TYPE_WIN32_PRINTING,
+    CAIRO_SURFACE_TYPE_QUARTZ_IMAGE,
+    CAIRO_SURFACE_TYPE_SCRIPT,
+    CAIRO_SURFACE_TYPE_QT,
+    CAIRO_SURFACE_TYPE_RECORDING,
+    CAIRO_SURFACE_TYPE_VG,
+    CAIRO_SURFACE_TYPE_GL,
+    CAIRO_SURFACE_TYPE_DRM,
+    CAIRO_SURFACE_TYPE_TEE,
+    CAIRO_SURFACE_TYPE_XML,
+    CAIRO_SURFACE_TYPE_SKIA,
+    CAIRO_SURFACE_TYPE_SUBSURFACE,
+    CAIRO_SURFACE_TYPE_MIME
+} cairo_surface_type_t;
+
+cairo_public cairo_surface_type_t
+cairo_surface_get_type (cairo_surface_t *surface);
+
+cairo_public cairo_content_t
+cairo_surface_get_content (cairo_surface_t *surface);
+
+#if CAIRO_HAS_PNG_FUNCTIONS
+
+cairo_public cairo_status_t
+cairo_surface_write_to_png (cairo_surface_t    *surface,
+                           const char          *filename);
+
+cairo_public cairo_status_t
+cairo_surface_write_to_png_stream (cairo_surface_t     *surface,
+                                  cairo_write_func_t   write_func,
+                                  void                 *closure);
+
+#endif
+
+cairo_public void *
+cairo_surface_get_user_data (cairo_surface_t            *surface,
+                            const cairo_user_data_key_t *key);
+
+cairo_public cairo_status_t
+cairo_surface_set_user_data (cairo_surface_t            *surface,
+                            const cairo_user_data_key_t *key,
+                            void                        *user_data,
+                            cairo_destroy_func_t        destroy);
+
+#define CAIRO_MIME_TYPE_JPEG "image/jpeg"
+#define CAIRO_MIME_TYPE_PNG "image/png"
+#define CAIRO_MIME_TYPE_JP2 "image/jp2"
+#define CAIRO_MIME_TYPE_URI "text/x-uri"
+#define CAIRO_MIME_TYPE_UNIQUE_ID "application/x-cairo.uuid"
+
+cairo_public void
+cairo_surface_get_mime_data (cairo_surface_t           *surface,
+                             const char                        *mime_type,
+                             const unsigned char       **data,
+                             unsigned long             *length);
+
+cairo_public 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_public void
+cairo_surface_get_font_options (cairo_surface_t      *surface,
+                               cairo_font_options_t *options);
+
+cairo_public void
+cairo_surface_flush (cairo_surface_t *surface);
+
+cairo_public void
+cairo_surface_mark_dirty (cairo_surface_t *surface);
+
+cairo_public void
+cairo_surface_mark_dirty_rectangle (cairo_surface_t *surface,
+                                   int              x,
+                                   int              y,
+                                   int              width,
+                                   int              height);
+
+cairo_public void
+cairo_surface_set_device_offset (cairo_surface_t *surface,
+                                double           x_offset,
+                                double           y_offset);
+
+cairo_public void
+cairo_surface_get_device_offset (cairo_surface_t *surface,
+                                double          *x_offset,
+                                double          *y_offset);
+
+cairo_public void
+cairo_surface_set_fallback_resolution (cairo_surface_t *surface,
+                                      double            x_pixels_per_inch,
+                                      double            y_pixels_per_inch);
+
+cairo_public void
+cairo_surface_get_fallback_resolution (cairo_surface_t *surface,
+                                      double           *x_pixels_per_inch,
+                                      double           *y_pixels_per_inch);
+
+cairo_public void
+cairo_surface_copy_page (cairo_surface_t *surface);
+
+cairo_public void
+cairo_surface_show_page (cairo_surface_t *surface);
+
+cairo_public cairo_bool_t
+cairo_surface_has_show_text_glyphs (cairo_surface_t *surface);
+
+/* Image-surface functions */
+
+cairo_public cairo_surface_t *
+cairo_image_surface_create (cairo_format_t     format,
+                           int                 width,
+                           int                 height);
+
+cairo_public int
+cairo_format_stride_for_width (cairo_format_t  format,
+                              int              width);
+
+cairo_public cairo_surface_t *
+cairo_image_surface_create_for_data (unsigned char            *data,
+                                    cairo_format_t             format,
+                                    int                        width,
+                                    int                        height,
+                                    int                        stride);
+
+cairo_public unsigned char *
+cairo_image_surface_get_data (cairo_surface_t *surface);
+
+cairo_public cairo_format_t
+cairo_image_surface_get_format (cairo_surface_t *surface);
+
+cairo_public int
+cairo_image_surface_get_width (cairo_surface_t *surface);
+
+cairo_public int
+cairo_image_surface_get_height (cairo_surface_t *surface);
+
+cairo_public int
+cairo_image_surface_get_stride (cairo_surface_t *surface);
+
+#if CAIRO_HAS_PNG_FUNCTIONS
+
+cairo_public cairo_surface_t *
+cairo_image_surface_create_from_png (const char        *filename);
+
+cairo_public cairo_surface_t *
+cairo_image_surface_create_from_png_stream (cairo_read_func_t  read_func,
+                                           void                *closure);
+
+#endif
+
+/* Recording-surface functions */
+
+cairo_public cairo_surface_t *
+cairo_recording_surface_create (cairo_content_t                 content,
+                                const cairo_rectangle_t *extents);
+
+cairo_public void
+cairo_recording_surface_ink_extents (cairo_surface_t *surface,
+                                     double *x0,
+                                     double *y0,
+                                     double *width,
+                                     double *height);
+
+cairo_public cairo_bool_t
+cairo_recording_surface_get_extents (cairo_surface_t *surface,
+                                    cairo_rectangle_t *extents);
+
+/* Mime-surface (callback) functions */
+
+typedef cairo_surface_t *(*cairo_mime_surface_acquire_t) (cairo_surface_t *mime_surface,
+                                                         void *callback_data,
+                                                         cairo_surface_t *target,
+                                                         const cairo_rectangle_int_t *sample_extents,
+                                                         cairo_rectangle_int_t *surface_extents);
+
+typedef void (*cairo_mime_surface_release_t) (cairo_surface_t *mime_surface,
+                                             void *callback_data,
+                                             cairo_surface_t *image_surface);
+
+typedef cairo_surface_t *(*cairo_mime_surface_snapshot_t) (cairo_surface_t *mime_surface,
+                                                          void *callback_data);
+typedef void (*cairo_mime_surface_destroy_t) (cairo_surface_t *mime_surface,
+                                             void *callback_data);
+
+cairo_public cairo_surface_t *
+cairo_mime_surface_create (void *data, cairo_content_t content, int width, int height);
+
+cairo_public void
+cairo_mime_surface_set_callback_data (cairo_surface_t *surface,
+                                     void *data);
+
+cairo_public void *
+cairo_mime_surface_get_callback_data (cairo_surface_t *surface);
+
+cairo_public void
+cairo_mime_surface_set_acquire (cairo_surface_t *surface,
+                               cairo_mime_surface_acquire_t acquire,
+                               cairo_mime_surface_release_t release);
+
+cairo_public void
+cairo_mime_surface_get_acquire (cairo_surface_t *surface,
+                               cairo_mime_surface_acquire_t *acquire,
+                               cairo_mime_surface_release_t *release);
+cairo_public void
+cairo_mime_surface_set_snapshot (cairo_surface_t *surface,
+                                cairo_mime_surface_snapshot_t snapshot);
+
+cairo_public cairo_mime_surface_snapshot_t
+cairo_mime_surface_get_snapshot (cairo_surface_t *surface);
+
+cairo_public void
+cairo_mime_surface_set_destroy (cairo_surface_t *surface,
+                               cairo_mime_surface_destroy_t destroy);
+
+cairo_public cairo_mime_surface_destroy_t
+cairo_mime_surface_get_destroy (cairo_surface_t *surface);
+
+/* Pattern creation functions */
+
+cairo_public cairo_pattern_t *
+cairo_pattern_create_rgb (double red, double green, double blue);
+
+cairo_public cairo_pattern_t *
+cairo_pattern_create_rgba (double red, double green, double blue,
+                          double alpha);
+
+cairo_public cairo_pattern_t *
+cairo_pattern_create_for_surface (cairo_surface_t *surface);
+
+cairo_public cairo_pattern_t *
+cairo_pattern_create_linear (double x0, double y0,
+                            double x1, double y1);
+
+cairo_public cairo_pattern_t *
+cairo_pattern_create_radial (double cx0, double cy0, double radius0,
+                            double cx1, double cy1, double radius1);
+
+cairo_public cairo_pattern_t *
+cairo_pattern_create_mesh (void);
+
+cairo_public cairo_pattern_t *
+cairo_pattern_reference (cairo_pattern_t *pattern);
+
+cairo_public void
+cairo_pattern_destroy (cairo_pattern_t *pattern);
+
+cairo_public unsigned int
+cairo_pattern_get_reference_count (cairo_pattern_t *pattern);
+
+cairo_public cairo_status_t
+cairo_pattern_status (cairo_pattern_t *pattern);
+
+cairo_public void *
+cairo_pattern_get_user_data (cairo_pattern_t            *pattern,
+                            const cairo_user_data_key_t *key);
+
+cairo_public cairo_status_t
+cairo_pattern_set_user_data (cairo_pattern_t            *pattern,
+                            const cairo_user_data_key_t *key,
+                            void                        *user_data,
+                            cairo_destroy_func_t         destroy);
+
+/**
+ * cairo_pattern_type_t:
+ * @CAIRO_PATTERN_TYPE_SOLID: The pattern is a solid (uniform)
+ * color. It may be opaque or translucent.
+ * @CAIRO_PATTERN_TYPE_SURFACE: The pattern is a based on a surface (an image).
+ * @CAIRO_PATTERN_TYPE_LINEAR: The pattern is a linear gradient.
+ * @CAIRO_PATTERN_TYPE_RADIAL: The pattern is a radial gradient.
+ * @CAIRO_PATTERN_TYPE_MESH: The pattern is a mesh.
+ *
+ * #cairo_pattern_type_t is used to describe the type of a given pattern.
+ *
+ * The type of a pattern is determined by the function used to create
+ * it. The cairo_pattern_create_rgb() and cairo_pattern_create_rgba()
+ * functions create SOLID patterns. The remaining
+ * cairo_pattern_create<!-- --> functions map to pattern types in obvious
+ * ways.
+ *
+ * The pattern type can be queried with cairo_pattern_get_type()
+ *
+ * Most #cairo_pattern_t functions can be called with a pattern of any
+ * type, (though trying to change the extend or filter for a solid
+ * pattern will have no effect). A notable exception is
+ * cairo_pattern_add_color_stop_rgb() and
+ * cairo_pattern_add_color_stop_rgba() which must only be called with
+ * gradient patterns (either LINEAR or RADIAL). Otherwise the pattern
+ * will be shutdown and put into an error state.
+ *
+ * New entries may be added in future versions.
+ *
+ * Since: 1.2
+ **/
+typedef enum _cairo_pattern_type {
+    CAIRO_PATTERN_TYPE_SOLID,
+    CAIRO_PATTERN_TYPE_SURFACE,
+    CAIRO_PATTERN_TYPE_LINEAR,
+    CAIRO_PATTERN_TYPE_RADIAL,
+    CAIRO_PATTERN_TYPE_MESH
+} cairo_pattern_type_t;
+
+cairo_public cairo_pattern_type_t
+cairo_pattern_get_type (cairo_pattern_t *pattern);
+
+cairo_public void
+cairo_pattern_add_color_stop_rgb (cairo_pattern_t *pattern,
+                                 double offset,
+                                 double red, double green, double blue);
+
+cairo_public void
+cairo_pattern_add_color_stop_rgba (cairo_pattern_t *pattern,
+                                  double offset,
+                                  double red, double green, double blue,
+                                  double alpha);
+
+cairo_public void
+cairo_mesh_pattern_begin_patch (cairo_pattern_t *pattern);
+
+cairo_public void
+cairo_mesh_pattern_end_patch (cairo_pattern_t *pattern);
+
+cairo_public void
+cairo_mesh_pattern_curve_to (cairo_pattern_t *pattern,
+                            double x1, double y1,
+                            double x2, double y2,
+                            double x3, double y3);
+
+cairo_public void
+cairo_mesh_pattern_line_to (cairo_pattern_t *pattern,
+                           double x, double y);
+
+cairo_public void
+cairo_mesh_pattern_move_to (cairo_pattern_t *pattern,
+                           double x, double y);
+
+cairo_public void
+cairo_mesh_pattern_set_control_point (cairo_pattern_t *pattern,
+                                     unsigned int point_num,
+                                     double x, double y);
+
+cairo_public void
+cairo_mesh_pattern_set_corner_color_rgb (cairo_pattern_t *pattern,
+                                        unsigned int corner_num,
+                                        double red, double green, double blue);
+
+cairo_public void
+cairo_mesh_pattern_set_corner_color_rgba (cairo_pattern_t *pattern,
+                                         unsigned int corner_num,
+                                         double red, double green, double blue,
+                                         double alpha);
+
+cairo_public void
+cairo_pattern_set_matrix (cairo_pattern_t      *pattern,
+                         const cairo_matrix_t *matrix);
+
+cairo_public void
+cairo_pattern_get_matrix (cairo_pattern_t *pattern,
+                         cairo_matrix_t  *matrix);
+
+/**
+ * cairo_extend_t:
+ * @CAIRO_EXTEND_NONE: pixels outside of the source pattern
+ *   are fully transparent
+ * @CAIRO_EXTEND_REPEAT: the pattern is tiled by repeating
+ * @CAIRO_EXTEND_REFLECT: the pattern is tiled by reflecting
+ *   at the edges (Implemented for surface patterns since 1.6)
+ * @CAIRO_EXTEND_PAD: pixels outside of the pattern copy
+ *   the closest pixel from the source (Since 1.2; but only
+ *   implemented for surface patterns since 1.6)
+ *
+ * #cairo_extend_t is used to describe how pattern color/alpha will be
+ * determined for areas "outside" the pattern's natural area, (for
+ * example, outside the surface bounds or outside the gradient
+ * geometry).
+ *
+ * Mesh patterns are not affected by the extend mode.
+ *
+ * The default extend mode is %CAIRO_EXTEND_NONE for surface patterns
+ * and %CAIRO_EXTEND_PAD for gradient patterns.
+ *
+ * New entries may be added in future versions.
+ **/
+typedef enum _cairo_extend {
+    CAIRO_EXTEND_NONE,
+    CAIRO_EXTEND_REPEAT,
+    CAIRO_EXTEND_REFLECT,
+    CAIRO_EXTEND_PAD
+} cairo_extend_t;
+
+cairo_public void
+cairo_pattern_set_extend (cairo_pattern_t *pattern, cairo_extend_t extend);
+
+cairo_public cairo_extend_t
+cairo_pattern_get_extend (cairo_pattern_t *pattern);
+
+/**
+ * cairo_filter_t:
+ * @CAIRO_FILTER_FAST: A high-performance filter, with quality similar
+ *     to %CAIRO_FILTER_NEAREST
+ * @CAIRO_FILTER_GOOD: A reasonable-performance filter, with quality
+ *     similar to %CAIRO_FILTER_BILINEAR
+ * @CAIRO_FILTER_BEST: The highest-quality available, performance may
+ *     not be suitable for interactive use.
+ * @CAIRO_FILTER_NEAREST: Nearest-neighbor filtering
+ * @CAIRO_FILTER_BILINEAR: Linear interpolation in two dimensions
+ * @CAIRO_FILTER_GAUSSIAN: This filter value is currently
+ *     unimplemented, and should not be used in current code.
+ *
+ * #cairo_filter_t is used to indicate what filtering should be
+ * applied when reading pixel values from patterns. See
+ * cairo_pattern_set_filter() for indicating the desired filter to be
+ * used with a particular pattern.
+ */
+typedef enum _cairo_filter {
+    CAIRO_FILTER_FAST,
+    CAIRO_FILTER_GOOD,
+    CAIRO_FILTER_BEST,
+    CAIRO_FILTER_NEAREST,
+    CAIRO_FILTER_BILINEAR,
+    CAIRO_FILTER_GAUSSIAN
+} cairo_filter_t;
+
+cairo_public void
+cairo_pattern_set_filter (cairo_pattern_t *pattern, cairo_filter_t filter);
+
+cairo_public cairo_filter_t
+cairo_pattern_get_filter (cairo_pattern_t *pattern);
+
+cairo_public cairo_status_t
+cairo_pattern_get_rgba (cairo_pattern_t *pattern,
+                       double *red, double *green,
+                       double *blue, double *alpha);
+
+cairo_public cairo_status_t
+cairo_pattern_get_surface (cairo_pattern_t *pattern,
+                          cairo_surface_t **surface);
+
+
+cairo_public cairo_status_t
+cairo_pattern_get_color_stop_rgba (cairo_pattern_t *pattern,
+                                  int index, double *offset,
+                                  double *red, double *green,
+                                  double *blue, double *alpha);
+
+cairo_public cairo_status_t
+cairo_pattern_get_color_stop_count (cairo_pattern_t *pattern,
+                                   int *count);
+
+cairo_public cairo_status_t
+cairo_pattern_get_linear_points (cairo_pattern_t *pattern,
+                                double *x0, double *y0,
+                                double *x1, double *y1);
+
+cairo_public cairo_status_t
+cairo_pattern_get_radial_circles (cairo_pattern_t *pattern,
+                                 double *x0, double *y0, double *r0,
+                                 double *x1, double *y1, double *r1);
+
+cairo_public cairo_status_t
+cairo_mesh_pattern_get_patch_count (cairo_pattern_t *pattern,
+                                   unsigned int *count);
+
+cairo_public cairo_path_t *
+cairo_mesh_pattern_get_path (cairo_pattern_t *pattern,
+                            unsigned int patch_num);
+
+cairo_public cairo_status_t
+cairo_mesh_pattern_get_corner_color_rgba (cairo_pattern_t *pattern,
+                                         unsigned int patch_num,
+                                         unsigned int corner_num,
+                                         double *red, double *green,
+                                         double *blue, double *alpha);
+
+cairo_public cairo_status_t
+cairo_mesh_pattern_get_control_point (cairo_pattern_t *pattern,
+                                     unsigned int patch_num,
+                                     unsigned int point_num,
+                                     double *x, double *y);
+
+/* Matrix functions */
+
+cairo_public void
+cairo_matrix_init (cairo_matrix_t *matrix,
+                  double  xx, double  yx,
+                  double  xy, double  yy,
+                  double  x0, double  y0);
+
+cairo_public void
+cairo_matrix_init_identity (cairo_matrix_t *matrix);
+
+cairo_public void
+cairo_matrix_init_translate (cairo_matrix_t *matrix,
+                            double tx, double ty);
+
+cairo_public void
+cairo_matrix_init_scale (cairo_matrix_t *matrix,
+                        double sx, double sy);
+
+cairo_public void
+cairo_matrix_init_rotate (cairo_matrix_t *matrix,
+                         double radians);
+
+cairo_public void
+cairo_matrix_translate (cairo_matrix_t *matrix, double tx, double ty);
+
+cairo_public void
+cairo_matrix_scale (cairo_matrix_t *matrix, double sx, double sy);
+
+cairo_public void
+cairo_matrix_rotate (cairo_matrix_t *matrix, double radians);
+
+cairo_public cairo_status_t
+cairo_matrix_invert (cairo_matrix_t *matrix);
+
+cairo_public void
+cairo_matrix_multiply (cairo_matrix_t      *result,
+                      const cairo_matrix_t *a,
+                      const cairo_matrix_t *b);
+
+cairo_public void
+cairo_matrix_transform_distance (const cairo_matrix_t *matrix,
+                                double *dx, double *dy);
+
+cairo_public void
+cairo_matrix_transform_point (const cairo_matrix_t *matrix,
+                             double *x, double *y);
+
+/* Region functions */
+
+/**
+ * cairo_region_t:
+ *
+ * A #cairo_region_t represents a set of integer-aligned rectangles.
+ *
+ * It allows set-theoretical operations like cairo_region_union() and
+ * cairo_region_intersect() to be performed on them.
+ *
+ * Memory management of #cairo_region_t is done with
+ * cairo_region_reference() and cairo_region_destroy().
+ *
+ * Since: 1.10
+ **/
+typedef struct _cairo_region cairo_region_t;
+
+typedef enum _cairo_region_overlap {
+    CAIRO_REGION_OVERLAP_IN,           /* completely inside region */
+    CAIRO_REGION_OVERLAP_OUT,          /* completely outside region */
+    CAIRO_REGION_OVERLAP_PART          /* partly inside region */
+} cairo_region_overlap_t;
+
+cairo_public cairo_region_t *
+cairo_region_create (void);
+
+cairo_public cairo_region_t *
+cairo_region_create_rectangle (const cairo_rectangle_int_t *rectangle);
+
+cairo_public cairo_region_t *
+cairo_region_create_rectangles (const cairo_rectangle_int_t *rects,
+                               int count);
+
+cairo_public cairo_region_t *
+cairo_region_copy (const cairo_region_t *original);
+
+cairo_public cairo_region_t *
+cairo_region_reference (cairo_region_t *region);
+
+cairo_public void
+cairo_region_destroy (cairo_region_t *region);
+
+cairo_public cairo_bool_t
+cairo_region_equal (const cairo_region_t *a, const cairo_region_t *b);
+
+cairo_public cairo_status_t
+cairo_region_status (const cairo_region_t *region);
+
+cairo_public void
+cairo_region_get_extents (const cairo_region_t        *region,
+                         cairo_rectangle_int_t *extents);
+
+cairo_public int
+cairo_region_num_rectangles (const cairo_region_t *region);
+
+cairo_public void
+cairo_region_get_rectangle (const cairo_region_t  *region,
+                           int                    nth,
+                           cairo_rectangle_int_t *rectangle);
+
+cairo_public cairo_bool_t
+cairo_region_is_empty (const cairo_region_t *region);
+
+cairo_public cairo_region_overlap_t
+cairo_region_contains_rectangle (const cairo_region_t *region,
+                                const cairo_rectangle_int_t *rectangle);
+
+cairo_public cairo_bool_t
+cairo_region_contains_point (const cairo_region_t *region, int x, int y);
+
+cairo_public void
+cairo_region_translate (cairo_region_t *region, int dx, int dy);
+
+cairo_public cairo_status_t
+cairo_region_subtract (cairo_region_t *dst, const cairo_region_t *other);
+
+cairo_public cairo_status_t
+cairo_region_subtract_rectangle (cairo_region_t *dst,
+                                const cairo_rectangle_int_t *rectangle);
+
+cairo_public cairo_status_t
+cairo_region_intersect (cairo_region_t *dst, const cairo_region_t *other);
+
+cairo_public cairo_status_t
+cairo_region_intersect_rectangle (cairo_region_t *dst,
+                                 const cairo_rectangle_int_t *rectangle);
+
+cairo_public cairo_status_t
+cairo_region_union (cairo_region_t *dst, const cairo_region_t *other);
+
+cairo_public cairo_status_t
+cairo_region_union_rectangle (cairo_region_t *dst,
+                             const cairo_rectangle_int_t *rectangle);
+
+cairo_public cairo_status_t
+cairo_region_xor (cairo_region_t *dst, const cairo_region_t *other);
+
+cairo_public cairo_status_t
+cairo_region_xor_rectangle (cairo_region_t *dst,
+                           const cairo_rectangle_int_t *rectangle);
+
+/* Functions to be used while debugging (not intended for use in production code) */
+cairo_public void
+cairo_debug_reset_static_data (void);
+
+/* TIZEN code modification */
+#ifndef TIZEN_ENABLE_FEATURE
+#define TIZEN_ENABLE_FEATURE
+#endif
+
+#ifdef TIZEN_ENABLE_FEATURE
+#define TIZEN_FIX_DASHED_LINE_STROKE /* Dashed line is displayed as unbroken line for certain scaling factor */
+#endif /* TIZEN_ENABLE_FEATURE */
+
+CAIRO_END_DECLS
+
+#endif /* CAIRO_H */
diff --git a/src/cairo.pc.in b/src/cairo.pc.in
new file mode 100644 (file)
index 0000000..b361edf
--- /dev/null
@@ -0,0 +1,13 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: cairo
+Description: Multi-platform 2D graphics library
+Version: @VERSION@
+
+@PKGCONFIG_REQUIRES@: @CAIRO_REQUIRES@
+Libs: -L${libdir} -lcairo
+Libs.private: @CAIRO_NONPKGCONFIG_LIBS@
+Cflags: -I${includedir}/cairo
diff --git a/src/cairoint.h b/src/cairoint.h
new file mode 100644 (file)
index 0000000..4b784a4
--- /dev/null
@@ -0,0 +1,1971 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *     Carl D. Worth <cworth@cworth.org>
+ */
+
+/*
+ * These definitions are solely for use by the implementation of cairo
+ * and constitute no kind of standard.  If you need any of these
+ * functions, please drop me a note.  Either the library needs new
+ * functionality, or there's a way to do what you need using the
+ * existing published interfaces. cworth@cworth.org
+ */
+
+#ifndef _CAIROINT_H_
+#define _CAIROINT_H_
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef _MSC_VER
+#define cairo_public __declspec(dllexport)
+#endif
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <stddef.h>
+
+#ifdef _MSC_VER
+#define _USE_MATH_DEFINES
+#endif
+#include <math.h>
+#include <limits.h>
+#include <stdio.h>
+
+#include "cairo.h"
+#include <pixman.h>
+
+#include "cairo-compiler-private.h"
+#include "cairo-error-private.h"
+
+#if CAIRO_HAS_PS_SURFACE || CAIRO_HAS_SCRIPT_SURFACE || CAIRO_HAS_XML_SURFACE
+#define CAIRO_HAS_DEFLATE_STREAM 1
+#endif
+
+#if CAIRO_HAS_PS_SURFACE  || \
+    CAIRO_HAS_PDF_SURFACE || \
+    CAIRO_HAS_SVG_SURFACE || \
+    CAIRO_HAS_WIN32_SURFACE
+#define CAIRO_HAS_FONT_SUBSET 1
+#endif
+
+#if CAIRO_HAS_PS_SURFACE || CAIRO_HAS_PDF_SURFACE || CAIRO_HAS_FONT_SUBSET
+#define CAIRO_HAS_PDF_OPERATORS 1
+#endif
+
+CAIRO_BEGIN_DECLS
+
+#if _WIN32 && !_WIN32_WCE /* Permissions on WinCE? No worries! */
+cairo_private FILE *
+_cairo_win32_tmpfile (void);
+#define tmpfile() _cairo_win32_tmpfile()
+#endif
+
+#undef MIN
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+
+#undef MAX
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#ifndef M_PI
+#define M_PI 3.14159265358979323846
+#endif
+
+#ifndef M_SQRT2
+#define M_SQRT2 1.41421356237309504880
+#endif
+
+#ifndef M_SQRT1_2
+#define M_SQRT1_2 0.707106781186547524400844362104849039
+#endif
+
+#undef  ARRAY_LENGTH
+#define ARRAY_LENGTH(__array) ((int) (sizeof (__array) / sizeof (__array[0])))
+
+#undef STRINGIFY
+#undef STRINGIFY_ARG
+#define STRINGIFY(macro_or_string)    STRINGIFY_ARG (macro_or_string)
+#define STRINGIFY_ARG(contents)       #contents
+
+#if defined (__GNUC__)
+#define cairo_container_of(ptr, type, member) ({ \
+    const __typeof__ (((type *) 0)->member) *mptr__ = (ptr); \
+    (type *) ((char *) mptr__ - offsetof (type, member)); \
+})
+#else
+#define cairo_container_of(ptr, type, member) \
+    ((type *)((char *) (ptr) - (char *) &((type *)0)->member))
+#endif
+
+
+#define ASSERT_NOT_REACHED             \
+do {                                   \
+    assert (!"reached");               \
+} while (0)
+#define COMPILE_TIME_ASSERT1(condition, line)          \
+    typedef int compile_time_assertion_at_line_##line##_failed [(condition)?1:-1]
+#define COMPILE_TIME_ASSERT0(condition, line)  COMPILE_TIME_ASSERT1(condition, line)
+#define COMPILE_TIME_ASSERT(condition)         COMPILE_TIME_ASSERT0(condition, __LINE__)
+
+#define CAIRO_ALPHA_IS_CLEAR(alpha) ((alpha) <= ((double)0x00ff / (double)0xffff))
+#define CAIRO_ALPHA_SHORT_IS_CLEAR(alpha) ((alpha) <= 0x00ff)
+
+#define CAIRO_ALPHA_IS_OPAQUE(alpha) ((alpha) >= ((double)0xff00 / (double)0xffff))
+#define CAIRO_ALPHA_SHORT_IS_OPAQUE(alpha) ((alpha) >= 0xff00)
+#define CAIRO_ALPHA_IS_ZERO(alpha) ((alpha) <= 0.0)
+
+#define CAIRO_COLOR_IS_CLEAR(color) CAIRO_ALPHA_SHORT_IS_CLEAR ((color)->alpha_short)
+#define CAIRO_COLOR_IS_OPAQUE(color) CAIRO_ALPHA_SHORT_IS_OPAQUE ((color)->alpha_short)
+
+/* 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)
+
+/* Return the number of 1 bits in mask.
+ *
+ * GCC 3.4 supports a "population count" builtin, which on many targets is
+ * implemented with a single instruction. There is a fallback definition
+ * in libgcc in case a target does not have one, which should be just as
+ * good as the open-coded solution below, (which is "HACKMEM 169").
+ */
+static inline int cairo_const
+_cairo_popcount (uint32_t mask)
+{
+#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)
+    return __builtin_popcount (mask);
+#else
+    register int y;
+
+    y = (mask >> 1) &033333333333;
+    y = mask - y - ((y >>1) & 033333333333);
+    return (((y + (y >> 3)) & 030707070707) % 077);
+#endif
+}
+
+static cairo_always_inline cairo_bool_t
+_cairo_is_little_endian (void)
+{
+    static const int i = 1;
+    return *((char *) &i) == 0x01;
+}
+
+#ifdef WORDS_BIGENDIAN
+#define CAIRO_BITSWAP8_IF_LITTLE_ENDIAN(c) (c)
+#else
+#define CAIRO_BITSWAP8_IF_LITTLE_ENDIAN(c) CAIRO_BITSWAP8(c)
+#endif
+
+#ifdef WORDS_BIGENDIAN
+
+#define cpu_to_be16(v) (v)
+#define be16_to_cpu(v) (v)
+#define cpu_to_be32(v) (v)
+#define be32_to_cpu(v) (v)
+
+#else
+
+static inline uint16_t cairo_const
+cpu_to_be16(uint16_t v)
+{
+    return (v << 8) | (v >> 8);
+}
+
+static inline uint16_t cairo_const
+be16_to_cpu(uint16_t v)
+{
+    return cpu_to_be16 (v);
+}
+
+static inline uint32_t cairo_const
+cpu_to_be32(uint32_t v)
+{
+    return (cpu_to_be16 (v) << 16) | cpu_to_be16 (v >> 16);
+}
+
+static inline uint32_t cairo_const
+be32_to_cpu(uint32_t v)
+{
+    return cpu_to_be32 (v);
+}
+
+#endif
+
+
+/* The glibc versions of ispace() and isdigit() are slow in UTF-8 locales.
+ */
+
+static inline int cairo_const
+_cairo_isspace (int c)
+{
+    return (c == 0x20 || (c >= 0x09 && c <= 0x0d));
+}
+
+static inline int cairo_const
+_cairo_isdigit (int c)
+{
+    return (c >= '0' && c <= '9');
+}
+
+#include "cairo-types-private.h"
+#include "cairo-cache-private.h"
+#include "cairo-reference-count-private.h"
+#include "cairo-spans-private.h"
+#include "cairo-surface-private.h"
+
+cairo_private void
+_cairo_box_from_doubles (cairo_box_t *box,
+                        double *x1, double *y1,
+                        double *x2, double *y2);
+
+cairo_private void
+_cairo_box_to_doubles (const cairo_box_t *box,
+                      double *x1, double *y1,
+                      double *x2, double *y2);
+
+cairo_private void
+_cairo_box_from_rectangle (cairo_box_t                 *box,
+                          const cairo_rectangle_int_t *rectangle);
+
+cairo_private void
+_cairo_box_round_to_rectangle (const cairo_box_t     *box,
+                              cairo_rectangle_int_t *rectangle);
+
+cairo_private void
+_cairo_box_add_curve_to (cairo_box_t         *extents,
+                        const cairo_point_t *a,
+                        const cairo_point_t *b,
+                        const cairo_point_t *c,
+                        const cairo_point_t *d);
+
+cairo_private void
+_cairo_boxes_get_extents (const cairo_box_t *boxes,
+                         int num_boxes,
+                         cairo_box_t *extents);
+
+cairo_private extern const cairo_rectangle_int_t _cairo_empty_rectangle;
+cairo_private extern const cairo_rectangle_int_t _cairo_unbounded_rectangle;
+
+static inline void
+_cairo_unbounded_rectangle_init (cairo_rectangle_int_t *rect)
+{
+    *rect = _cairo_unbounded_rectangle;
+}
+
+cairo_private_no_warn cairo_bool_t
+_cairo_rectangle_intersect (cairo_rectangle_int_t *dst,
+                           const cairo_rectangle_int_t *src);
+
+static inline cairo_bool_t
+_cairo_rectangle_intersects (const cairo_rectangle_int_t *dst,
+                            const cairo_rectangle_int_t *src)
+{
+    return !(src->x >= dst->x + (int) dst->width ||
+            src->x + (int) src->width <= dst->x ||
+            src->y >= dst->y + (int) dst->height ||
+            src->y + (int) src->height <= dst->y);
+}
+
+cairo_private void
+_cairo_rectangle_int_from_double (cairo_rectangle_int_t *recti,
+                                 const cairo_rectangle_t *rectf);
+
+/* Extends the dst rectangle to also contain src.
+ * If one of the rectangles is empty, the result is undefined
+ */
+cairo_private void
+_cairo_rectangle_union (cairo_rectangle_int_t *dst,
+                       const cairo_rectangle_int_t *src);
+
+cairo_private cairo_bool_t
+_cairo_box_intersects_line_segment (cairo_box_t *box,
+                                   cairo_line_t *line) cairo_pure;
+
+cairo_private cairo_bool_t
+_cairo_spline_intersects (const cairo_point_t *a,
+                         const cairo_point_t *b,
+                         const cairo_point_t *c,
+                         const cairo_point_t *d,
+                         const cairo_box_t *box) cairo_pure;
+
+typedef struct {
+    const cairo_user_data_key_t *key;
+    void *user_data;
+    cairo_destroy_func_t destroy;
+} cairo_user_data_slot_t;
+
+cairo_private void
+_cairo_user_data_array_init (cairo_user_data_array_t *array);
+
+cairo_private void
+_cairo_user_data_array_fini (cairo_user_data_array_t *array);
+
+cairo_private void *
+_cairo_user_data_array_get_data (cairo_user_data_array_t     *array,
+                                const cairo_user_data_key_t *key);
+
+cairo_private cairo_status_t
+_cairo_user_data_array_set_data (cairo_user_data_array_t     *array,
+                                const cairo_user_data_key_t *key,
+                                void                        *user_data,
+                                cairo_destroy_func_t         destroy);
+
+cairo_private cairo_status_t
+_cairo_user_data_array_copy (cairo_user_data_array_t           *dst,
+                            const cairo_user_data_array_t      *src);
+
+cairo_private void
+_cairo_user_data_array_foreach (cairo_user_data_array_t     *array,
+                               void (*func) (const void *key,
+                                             void *elt,
+                                             void *closure),
+                               void *closure);
+
+#define _CAIRO_HASH_INIT_VALUE 5381
+
+cairo_private unsigned long
+_cairo_hash_string (const char *c);
+
+cairo_private unsigned long
+_cairo_hash_bytes (unsigned long hash,
+                  const void *bytes,
+                  unsigned int length);
+
+#define _cairo_scaled_glyph_index(g) ((g)->hash_entry.hash)
+#define _cairo_scaled_glyph_set_index(g, i)  ((g)->hash_entry.hash = (i))
+
+#include "cairo-scaled-font-private.h"
+
+struct _cairo_font_face {
+    /* hash_entry must be first */
+    cairo_hash_entry_t hash_entry;
+    cairo_status_t status;
+    cairo_reference_count_t ref_count;
+    cairo_user_data_array_t user_data;
+    const cairo_font_face_backend_t *backend;
+};
+
+cairo_private void
+_cairo_default_context_reset_static_data (void);
+
+cairo_private void
+_cairo_toy_font_face_reset_static_data (void);
+
+cairo_private void
+_cairo_ft_font_reset_static_data (void);
+
+cairo_private void
+_cairo_win32_font_reset_static_data (void);
+
+/* the font backend interface */
+
+struct _cairo_unscaled_font_backend {
+    void (*destroy)                (void                            *unscaled_font);
+};
+
+/* #cairo_toy_font_face_t - simple family/slant/weight font faces used for
+ * the built-in font API
+ */
+
+typedef struct _cairo_toy_font_face {
+    cairo_font_face_t base;
+    const char *family;
+    cairo_bool_t owns_family;
+    cairo_font_slant_t slant;
+    cairo_font_weight_t weight;
+
+    cairo_font_face_t *impl_face; /* The non-toy font face this actually uses */
+} cairo_toy_font_face_t;
+
+typedef enum _cairo_scaled_glyph_info {
+    CAIRO_SCALED_GLYPH_INFO_METRICS     = (1 << 0),
+    CAIRO_SCALED_GLYPH_INFO_SURFACE     = (1 << 1),
+    CAIRO_SCALED_GLYPH_INFO_PATH        = (1 << 2),
+    CAIRO_SCALED_GLYPH_INFO_RECORDING_SURFACE = (1 << 3)
+} cairo_scaled_glyph_info_t;
+
+typedef struct _cairo_scaled_font_subset {
+    cairo_scaled_font_t *scaled_font;
+    unsigned int font_id;
+    unsigned int subset_id;
+
+    /* Index of glyphs array is subset_glyph_index.
+     * Value of glyphs array is scaled_font_glyph_index.
+     */
+    unsigned long *glyphs;
+    char          **utf8;
+    char          **glyph_names;
+    int           *to_latin_char;
+    unsigned long *latin_to_subset_glyph_index;
+    unsigned int num_glyphs;
+    cairo_bool_t is_composite;
+    cairo_bool_t is_scaled;
+    cairo_bool_t is_latin;
+} cairo_scaled_font_subset_t;
+
+struct _cairo_scaled_font_backend {
+    cairo_font_type_t type;
+
+    void
+    (*fini)            (void                   *scaled_font);
+
+    cairo_warn cairo_int_status_t
+    (*scaled_glyph_init)       (void                        *scaled_font,
+                                cairo_scaled_glyph_t        *scaled_glyph,
+                                cairo_scaled_glyph_info_t    info);
+
+    /* A backend only needs to implement this or ucs4_to_index(), not
+     * both. This allows the backend to do something more sophisticated
+     * then just converting characters one by one.
+     */
+    cairo_warn cairo_int_status_t
+    (*text_to_glyphs) (void                       *scaled_font,
+                      double                      x,
+                      double                      y,
+                      const char                 *utf8,
+                      int                         utf8_len,
+                      cairo_glyph_t             **glyphs,
+                      int                        *num_glyphs,
+                      cairo_text_cluster_t      **clusters,
+                      int                        *num_clusters,
+                      cairo_text_cluster_flags_t *cluster_flags);
+
+    unsigned long
+    (*ucs4_to_index)           (void                        *scaled_font,
+                                uint32_t                     ucs4);
+
+    /* Read data from a sfnt font table.
+     * @scaled_font: font
+     * @tag: 4 byte table name specifying the table to read.
+     * @offset: offset into the table
+     * @buffer: buffer to write data into. Caller must ensure there is sufficient space.
+     *          If NULL, return the size of the table in @length.
+     * @length: If @buffer is NULL, the size of the table will be returned in @length.
+     *          If @buffer is not null, @length specifies the number of bytes to read.
+     *
+     * If less than @length bytes are available to read this function
+     * returns CAIRO_INT_STATUS_UNSUPPORTED. Note that requesting more
+     * bytes than are available in the table may continue reading data
+     * from the following table and return success. If this is
+     * undesirable the caller should first query the table size. If an
+     * error occurs the output value of @length is undefined.
+     *
+     * Returns CAIRO_INT_STATUS_UNSUPPORTED if not a sfnt style font or table not found.
+     */
+    cairo_warn cairo_int_status_t
+    (*load_truetype_table)(void                        *scaled_font,
+                           unsigned long         tag,
+                           long                  offset,
+                           unsigned char        *buffer,
+                           unsigned long        *length);
+
+    /* ucs4 is set to -1 if the unicode character could not be found
+     * for the glyph */
+    cairo_warn cairo_int_status_t
+    (*index_to_ucs4)(void                       *scaled_font,
+                    unsigned long               index,
+                     uint32_t                   *ucs4);
+
+    cairo_warn cairo_bool_t
+    (*is_synthetic)(void                       *scaled_font);
+
+    /* For type 1 fonts, return the glyph name for a given glyph index.
+     * A glyph index and list of glyph names in the Type 1 fonts is provided.
+     * The function returns the index of the glyph in the list of glyph names.
+     * @scaled_font: font
+     * @glyph_names: the names of each glyph in the Type 1 font in the
+     *   order they appear in the CharStrings array
+     * @num_glyph_names: the number of names in the glyph_names array
+     * @glyph_index: the given glyph index
+     * @glyph_array_index: (index into glyph_names) the glyph name corresponding
+     *  to the glyph_index
+     */
+
+    cairo_warn cairo_int_status_t
+    (*index_to_glyph_name)(void                 *scaled_font,
+                          char                **glyph_names,
+                          int                   num_glyph_names,
+                          unsigned long         glyph_index,
+                          unsigned long        *glyph_array_index);
+
+    /* Read data from a PostScript font.
+     * @scaled_font: font
+     * @offset: offset into the table
+     * @buffer: buffer to write data into. Caller must ensure there is sufficient space.
+     *          If NULL, return the size of the table in @length.
+     * @length: If @buffer is NULL, the size of the table will be returned in @length.
+     *          If @buffer is not null, @length specifies the number of bytes to read.
+     *
+     * If less than @length bytes are available to read this function
+     * returns CAIRO_INT_STATUS_UNSUPPORTED. If an error occurs the
+     * output value of @length is undefined.
+     *
+     * Returns CAIRO_INT_STATUS_UNSUPPORTED if not a Type 1 font.
+     */
+    cairo_warn cairo_int_status_t
+    (*load_type1_data)    (void                        *scaled_font,
+                           long                  offset,
+                           unsigned char        *buffer,
+                           unsigned long        *length);
+};
+
+struct _cairo_font_face_backend {
+    cairo_font_type_t  type;
+
+    cairo_warn cairo_status_t
+    (*create_for_toy)  (cairo_toy_font_face_t  *toy_face,
+                       cairo_font_face_t      **font_face);
+
+    /* The destroy() function is allowed to resurrect the font face
+     * by re-referencing. This is needed for the FreeType backend.
+     */
+    void
+    (*destroy)     (void                       *font_face);
+
+    cairo_warn cairo_status_t
+    (*scaled_font_create) (void                                *font_face,
+                          const cairo_matrix_t         *font_matrix,
+                          const cairo_matrix_t         *ctm,
+                          const cairo_font_options_t   *options,
+                          cairo_scaled_font_t         **scaled_font);
+
+    cairo_font_face_t *
+    (*get_implementation) (void                                *font_face,
+                          const cairo_matrix_t         *font_matrix,
+                          const cairo_matrix_t         *ctm,
+                          const cairo_font_options_t   *options);
+};
+
+extern const cairo_private struct _cairo_font_face_backend _cairo_user_font_face_backend;
+
+/* concrete font backends */
+#if CAIRO_HAS_FT_FONT
+
+extern const cairo_private struct _cairo_font_face_backend _cairo_ft_font_face_backend;
+
+#endif
+
+#if CAIRO_HAS_WIN32_FONT
+
+extern const cairo_private struct _cairo_font_face_backend _cairo_win32_font_face_backend;
+
+#endif
+
+#if CAIRO_HAS_QUARTZ_FONT
+
+extern const cairo_private struct _cairo_font_face_backend _cairo_quartz_font_face_backend;
+
+#endif
+
+#define CAIRO_EXTEND_SURFACE_DEFAULT CAIRO_EXTEND_NONE
+#define CAIRO_EXTEND_GRADIENT_DEFAULT CAIRO_EXTEND_PAD
+#define CAIRO_FILTER_DEFAULT CAIRO_FILTER_GOOD
+
+extern const cairo_private cairo_solid_pattern_t _cairo_pattern_clear;
+extern const cairo_private cairo_solid_pattern_t _cairo_pattern_black;
+extern const cairo_private cairo_solid_pattern_t _cairo_pattern_white;
+
+struct _cairo_surface_attributes {
+    cairo_matrix_t matrix;
+    cairo_extend_t extend;
+    cairo_filter_t filter;
+    cairo_bool_t has_component_alpha;
+    int                   x_offset;
+    int                   y_offset;
+    void          *extra;
+};
+
+#define CAIRO_FONT_SLANT_DEFAULT   CAIRO_FONT_SLANT_NORMAL
+#define CAIRO_FONT_WEIGHT_DEFAULT  CAIRO_FONT_WEIGHT_NORMAL
+
+#define CAIRO_WIN32_FONT_FAMILY_DEFAULT "Arial"
+#define CAIRO_QUARTZ_FONT_FAMILY_DEFAULT  "Helvetica"
+#define CAIRO_FT_FONT_FAMILY_DEFAULT     ""
+#define CAIRO_USER_FONT_FAMILY_DEFAULT     "@cairo:"
+
+#if   CAIRO_HAS_WIN32_FONT
+
+#define CAIRO_FONT_FAMILY_DEFAULT CAIRO_WIN32_FONT_FAMILY_DEFAULT
+#define CAIRO_FONT_FACE_BACKEND_DEFAULT &_cairo_win32_font_face_backend
+
+#elif CAIRO_HAS_QUARTZ_FONT
+
+#define CAIRO_FONT_FAMILY_DEFAULT CAIRO_QUARTZ_FONT_FAMILY_DEFAULT
+#define CAIRO_FONT_FACE_BACKEND_DEFAULT &_cairo_quartz_font_face_backend
+
+#elif CAIRO_HAS_FT_FONT
+
+#define CAIRO_FONT_FAMILY_DEFAULT CAIRO_FT_FONT_FAMILY_DEFAULT
+#define CAIRO_FONT_FACE_BACKEND_DEFAULT &_cairo_ft_font_face_backend
+
+#else
+
+#define CAIRO_FONT_FAMILY_DEFAULT CAIRO_FT_FONT_FAMILY_DEFAULT
+#define CAIRO_FONT_FACE_BACKEND_DEFAULT &_cairo_user_font_face_backend
+
+#endif
+
+#define CAIRO_GSTATE_OPERATOR_DEFAULT  CAIRO_OPERATOR_OVER
+#define CAIRO_GSTATE_TOLERANCE_DEFAULT 0.1
+#define CAIRO_GSTATE_FILL_RULE_DEFAULT CAIRO_FILL_RULE_WINDING
+#define CAIRO_GSTATE_LINE_WIDTH_DEFAULT        2.0
+#define CAIRO_GSTATE_LINE_CAP_DEFAULT  CAIRO_LINE_CAP_BUTT
+#define CAIRO_GSTATE_LINE_JOIN_DEFAULT CAIRO_LINE_JOIN_MITER
+#define CAIRO_GSTATE_MITER_LIMIT_DEFAULT       10.0
+#define CAIRO_GSTATE_DEFAULT_FONT_SIZE  10.0
+
+#define CAIRO_SURFACE_RESOLUTION_DEFAULT 72.0
+#define CAIRO_SURFACE_FALLBACK_RESOLUTION_DEFAULT 300.0
+
+typedef struct _cairo_stroke_face {
+    cairo_point_t ccw;
+    cairo_point_t point;
+    cairo_point_t cw;
+    cairo_slope_t dev_vector;
+    cairo_point_double_t dev_slope;
+    cairo_point_double_t usr_vector;
+    double length;
+} cairo_stroke_face_t;
+
+/* cairo.c */
+
+static inline double cairo_const
+_cairo_restrict_value (double value, double min, double max)
+{
+    if (value < min)
+       return min;
+    else if (value > max)
+       return max;
+    else
+       return value;
+}
+
+/* C99 round() rounds to the nearest integral value with halfway cases rounded
+ * away from 0. _cairo_round rounds halfway cases toward positive infinity.
+ * This matches the rounding behaviour of _cairo_lround. */
+static inline double cairo_const
+_cairo_round (double r)
+{
+    return floor (r + .5);
+}
+
+#if DISABLE_SOME_FLOATING_POINT
+cairo_private int
+_cairo_lround (double d) cairo_const;
+#else
+static inline int cairo_const
+_cairo_lround (double r)
+{
+    return _cairo_round (r);
+}
+#endif
+
+cairo_private uint16_t
+_cairo_half_from_float (float f) cairo_const;
+
+cairo_private cairo_bool_t
+_cairo_operator_bounded_by_mask (cairo_operator_t op) cairo_const;
+
+cairo_private cairo_bool_t
+_cairo_operator_bounded_by_source (cairo_operator_t op) cairo_const;
+
+enum {
+    CAIRO_OPERATOR_BOUND_BY_MASK = 1 << 1,
+    CAIRO_OPERATOR_BOUND_BY_SOURCE = 1 << 2,
+};
+
+cairo_private uint32_t
+_cairo_operator_bounded_by_either (cairo_operator_t op) cairo_const;
+/* cairo-color.c */
+cairo_private const cairo_color_t *
+_cairo_stock_color (cairo_stock_t stock) cairo_pure;
+
+#define CAIRO_COLOR_WHITE       _cairo_stock_color (CAIRO_STOCK_WHITE)
+#define CAIRO_COLOR_BLACK       _cairo_stock_color (CAIRO_STOCK_BLACK)
+#define CAIRO_COLOR_TRANSPARENT _cairo_stock_color (CAIRO_STOCK_TRANSPARENT)
+
+cairo_private uint16_t
+_cairo_color_double_to_short (double d) cairo_const;
+
+cairo_private void
+_cairo_color_init_rgba (cairo_color_t *color,
+                       double red, double green, double blue,
+                       double alpha);
+
+cairo_private void
+_cairo_color_multiply_alpha (cairo_color_t *color,
+                            double         alpha);
+
+cairo_private void
+_cairo_color_get_rgba (cairo_color_t *color,
+                      double        *red,
+                      double        *green,
+                      double        *blue,
+                      double        *alpha);
+
+cairo_private void
+_cairo_color_get_rgba_premultiplied (cairo_color_t *color,
+                                    double        *red,
+                                    double        *green,
+                                    double        *blue,
+                                    double        *alpha);
+
+cairo_private cairo_bool_t
+_cairo_color_equal (const cairo_color_t *color_a,
+                    const cairo_color_t *color_b) cairo_pure;
+
+cairo_private cairo_bool_t
+_cairo_color_stop_equal (const cairo_color_stop_t *color_a,
+                        const cairo_color_stop_t *color_b) cairo_pure;
+
+cairo_private cairo_content_t
+_cairo_color_get_content (const cairo_color_t *color) cairo_pure;
+
+/* cairo-font-face.c */
+
+extern const cairo_private cairo_font_face_t _cairo_font_face_nil;
+
+cairo_private void
+_cairo_font_face_init (cairo_font_face_t               *font_face,
+                      const cairo_font_face_backend_t *backend);
+
+cairo_private cairo_status_t
+_cairo_font_face_set_error (cairo_font_face_t *font_face,
+                           cairo_status_t     status);
+
+cairo_private void
+_cairo_unscaled_font_init (cairo_unscaled_font_t               *font,
+                          const cairo_unscaled_font_backend_t *backend);
+
+cairo_private_no_warn cairo_unscaled_font_t *
+_cairo_unscaled_font_reference (cairo_unscaled_font_t *font);
+
+cairo_private void
+_cairo_unscaled_font_destroy (cairo_unscaled_font_t *font);
+
+/* cairo-font-face-twin.c */
+
+cairo_private cairo_font_face_t *
+_cairo_font_face_twin_create_fallback (void);
+
+cairo_private cairo_status_t
+_cairo_font_face_twin_create_for_toy (cairo_toy_font_face_t   *toy_face,
+                                     cairo_font_face_t      **font_face);
+
+/* cairo-font-face-twin-data.c */
+
+extern const cairo_private int8_t _cairo_twin_outlines[];
+extern const cairo_private uint16_t _cairo_twin_charmap[128];
+
+/* cairo-font-options.c */
+
+cairo_private void
+_cairo_font_options_init_default (cairo_font_options_t *options);
+
+cairo_private void
+_cairo_font_options_init_copy (cairo_font_options_t            *options,
+                              const cairo_font_options_t       *other);
+
+cairo_private void
+_cairo_font_options_set_lcd_filter (cairo_font_options_t   *options,
+                                  cairo_lcd_filter_t  lcd_filter);
+
+cairo_private cairo_lcd_filter_t
+_cairo_font_options_get_lcd_filter (const cairo_font_options_t *options);
+
+cairo_private void
+_cairo_font_options_set_round_glyph_positions (cairo_font_options_t   *options,
+                                              cairo_round_glyph_positions_t  round);
+
+cairo_private cairo_round_glyph_positions_t
+_cairo_font_options_get_round_glyph_positions (const cairo_font_options_t *options);
+
+/* cairo-hull.c */
+cairo_private cairo_status_t
+_cairo_hull_compute (cairo_pen_vertex_t *vertices, int *num_vertices);
+
+/* cairo-lzw.c */
+cairo_private unsigned char *
+_cairo_lzw_compress (unsigned char *data, unsigned long *size_in_out);
+
+/* cairo-misc.c */
+cairo_private cairo_status_t
+_cairo_validate_text_clusters (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   cluster_flags);
+
+cairo_private cairo_status_t
+_cairo_intern_string (const char **str_inout, int len);
+
+cairo_private void
+_cairo_intern_string_reset_static_data (void);
+
+/* cairo-path-fixed.c */
+cairo_private cairo_path_fixed_t *
+_cairo_path_fixed_create (void);
+
+cairo_private void
+_cairo_path_fixed_init (cairo_path_fixed_t *path);
+
+cairo_private cairo_status_t
+_cairo_path_fixed_init_copy (cairo_path_fixed_t *path,
+                            const cairo_path_fixed_t *other);
+
+cairo_private void
+_cairo_path_fixed_fini (cairo_path_fixed_t *path);
+
+cairo_private void
+_cairo_path_fixed_destroy (cairo_path_fixed_t *path);
+
+cairo_private cairo_status_t
+_cairo_path_fixed_move_to (cairo_path_fixed_t  *path,
+                          cairo_fixed_t        x,
+                          cairo_fixed_t        y);
+
+cairo_private void
+_cairo_path_fixed_new_sub_path (cairo_path_fixed_t *path);
+
+cairo_private cairo_status_t
+_cairo_path_fixed_rel_move_to (cairo_path_fixed_t *path,
+                              cairo_fixed_t       dx,
+                              cairo_fixed_t       dy);
+
+cairo_private cairo_status_t
+_cairo_path_fixed_line_to (cairo_path_fixed_t *path,
+                          cairo_fixed_t        x,
+                          cairo_fixed_t        y);
+
+cairo_private cairo_status_t
+_cairo_path_fixed_rel_line_to (cairo_path_fixed_t *path,
+                              cairo_fixed_t       dx,
+                              cairo_fixed_t       dy);
+
+cairo_private cairo_status_t
+_cairo_path_fixed_curve_to (cairo_path_fixed_t *path,
+                           cairo_fixed_t x0, cairo_fixed_t y0,
+                           cairo_fixed_t x1, cairo_fixed_t y1,
+                           cairo_fixed_t x2, cairo_fixed_t y2);
+
+cairo_private cairo_status_t
+_cairo_path_fixed_rel_curve_to (cairo_path_fixed_t *path,
+                               cairo_fixed_t dx0, cairo_fixed_t dy0,
+                               cairo_fixed_t dx1, cairo_fixed_t dy1,
+                               cairo_fixed_t dx2, cairo_fixed_t dy2);
+
+cairo_private cairo_status_t
+_cairo_path_fixed_close_path (cairo_path_fixed_t *path);
+
+cairo_private cairo_bool_t
+_cairo_path_fixed_get_current_point (cairo_path_fixed_t *path,
+                                    cairo_fixed_t      *x,
+                                    cairo_fixed_t      *y);
+
+typedef cairo_status_t
+(cairo_path_fixed_move_to_func_t) (void                 *closure,
+                                  const cairo_point_t *point);
+
+typedef cairo_status_t
+(cairo_path_fixed_line_to_func_t) (void                 *closure,
+                                  const cairo_point_t *point);
+
+typedef cairo_status_t
+(cairo_path_fixed_curve_to_func_t) (void         *closure,
+                                   const cairo_point_t *p0,
+                                   const cairo_point_t *p1,
+                                   const cairo_point_t *p2);
+
+typedef cairo_status_t
+(cairo_path_fixed_close_path_func_t) (void *closure);
+
+cairo_private cairo_status_t
+_cairo_path_fixed_interpret (const cairo_path_fixed_t    *path,
+                      cairo_path_fixed_move_to_func_t    *move_to,
+                      cairo_path_fixed_line_to_func_t    *line_to,
+                      cairo_path_fixed_curve_to_func_t   *curve_to,
+                      cairo_path_fixed_close_path_func_t *close_path,
+                      void                               *closure);
+
+cairo_private cairo_status_t
+_cairo_path_fixed_interpret_flat (const cairo_path_fixed_t *path,
+                      cairo_path_fixed_move_to_func_t    *move_to,
+                      cairo_path_fixed_line_to_func_t    *line_to,
+                      cairo_path_fixed_close_path_func_t *close_path,
+                      void                               *closure,
+                      double                             tolerance);
+
+
+cairo_private cairo_bool_t
+_cairo_path_bounder_extents (const cairo_path_fixed_t *path,
+                            cairo_box_t *box);
+
+cairo_private cairo_bool_t
+_cairo_path_fixed_extents (const cairo_path_fixed_t *path,
+                          cairo_box_t *box);
+
+cairo_private void
+_cairo_path_fixed_approximate_clip_extents (const cairo_path_fixed_t   *path,
+                                           cairo_rectangle_int_t *extents);
+
+cairo_private void
+_cairo_path_fixed_approximate_fill_extents (const cairo_path_fixed_t *path,
+                                           cairo_rectangle_int_t *extents);
+
+cairo_private void
+_cairo_path_fixed_fill_extents (const cairo_path_fixed_t       *path,
+                               cairo_fill_rule_t        fill_rule,
+                               double                   tolerance,
+                               cairo_rectangle_int_t   *extents);
+
+cairo_private void
+_cairo_path_fixed_approximate_stroke_extents (const cairo_path_fixed_t *path,
+                                             const cairo_stroke_style_t *style,
+                                             const cairo_matrix_t *ctm,
+                                             cairo_rectangle_int_t *extents);
+
+cairo_private cairo_status_t
+_cairo_path_fixed_stroke_extents (const cairo_path_fixed_t *path,
+                                 const cairo_stroke_style_t *style,
+                                 const cairo_matrix_t *ctm,
+                                 const cairo_matrix_t *ctm_inverse,
+                                 double tolerance,
+                                 cairo_rectangle_int_t *extents);
+
+cairo_private void
+_cairo_path_fixed_transform (cairo_path_fixed_t        *path,
+                            const cairo_matrix_t       *matrix);
+
+cairo_private cairo_bool_t
+_cairo_path_fixed_is_box (const cairo_path_fixed_t *path,
+                          cairo_box_t *box);
+
+cairo_private cairo_bool_t
+_cairo_path_fixed_is_rectangle (const cairo_path_fixed_t *path,
+                               cairo_box_t        *box);
+
+/* cairo-path-in-fill.c */
+cairo_private cairo_bool_t
+_cairo_path_fixed_in_fill (const cairo_path_fixed_t    *path,
+                          cairo_fill_rule_t     fill_rule,
+                          double                tolerance,
+                          double                x,
+                          double                y);
+
+/* cairo-path-fill.c */
+cairo_private cairo_status_t
+_cairo_path_fixed_fill_to_polygon (const cairo_path_fixed_t *path,
+                                  double              tolerance,
+                                  cairo_polygon_t      *polygon);
+
+cairo_private cairo_status_t
+_cairo_path_fixed_fill_rectilinear_to_polygon (const cairo_path_fixed_t *path,
+                                              cairo_antialias_t antialias,
+                                              cairo_polygon_t *polygon);
+
+cairo_private cairo_status_t
+_cairo_path_fixed_fill_rectilinear_to_boxes (const cairo_path_fixed_t *path,
+                                            cairo_fill_rule_t fill_rule,
+                                            cairo_antialias_t antialias,
+                                            cairo_boxes_t *boxes);
+
+cairo_private cairo_region_t *
+_cairo_path_fixed_fill_rectilinear_to_region (const cairo_path_fixed_t *path,
+                                             cairo_fill_rule_t  fill_rule,
+                                             const cairo_rectangle_int_t *extents);
+
+cairo_private cairo_status_t
+_cairo_path_fixed_fill_to_traps (const cairo_path_fixed_t   *path,
+                                cairo_fill_rule_t           fill_rule,
+                                double                      tolerance,
+                                cairo_traps_t              *traps);
+
+/* cairo-path-stroke.c */
+cairo_private cairo_status_t
+_cairo_path_fixed_stroke_to_polygon (const cairo_path_fixed_t  *path,
+                                    const cairo_stroke_style_t *stroke_style,
+                                    const cairo_matrix_t       *ctm,
+                                    const cairo_matrix_t       *ctm_inverse,
+                                    double              tolerance,
+                                    cairo_polygon_t    *polygon);
+
+cairo_private cairo_int_status_t
+_cairo_path_fixed_stroke_to_tristrip (const cairo_path_fixed_t *path,
+                                     const cairo_stroke_style_t*style,
+                                     const cairo_matrix_t      *ctm,
+                                     const cairo_matrix_t      *ctm_inverse,
+                                     double                     tolerance,
+                                     cairo_tristrip_t           *strip);
+
+cairo_private cairo_status_t
+_cairo_path_fixed_stroke_dashed_to_polygon (const cairo_path_fixed_t   *path,
+                                           const cairo_stroke_style_t  *stroke_style,
+                                           const cairo_matrix_t        *ctm,
+                                           const cairo_matrix_t        *ctm_inverse,
+                                           double               tolerance,
+                                           cairo_polygon_t     *polygon);
+
+cairo_private cairo_int_status_t
+_cairo_path_fixed_stroke_rectilinear_to_boxes (const cairo_path_fixed_t        *path,
+                                              const cairo_stroke_style_t       *stroke_style,
+                                              const cairo_matrix_t     *ctm,
+                                              cairo_antialias_t         antialias,
+                                              cairo_boxes_t            *boxes);
+
+cairo_private cairo_int_status_t
+_cairo_path_fixed_stroke_to_traps (const cairo_path_fixed_t    *path,
+                                  const cairo_stroke_style_t   *stroke_style,
+                                  const cairo_matrix_t *ctm,
+                                  const cairo_matrix_t *ctm_inverse,
+                                  double                tolerance,
+                                  cairo_traps_t        *traps);
+
+cairo_private cairo_status_t
+_cairo_path_fixed_stroke_to_shaper (cairo_path_fixed_t *path,
+                                  const cairo_stroke_style_t   *stroke_style,
+                                  const cairo_matrix_t *ctm,
+                                  const cairo_matrix_t *ctm_inverse,
+                                  double                tolerance,
+                                  cairo_status_t (*add_triangle) (void *closure,
+                                                                  const cairo_point_t triangle[3]),
+                                  cairo_status_t (*add_triangle_fan) (void *closure,
+                                                                      const cairo_point_t *midpt,
+                                                                      const cairo_point_t *points,
+                                                                      int npoints),
+                                  cairo_status_t (*add_quad) (void *closure,
+                                                              const cairo_point_t quad[4]),
+                                  void *closure);
+
+/* cairo-scaled-font.c */
+
+cairo_private void
+_cairo_scaled_font_freeze_cache (cairo_scaled_font_t *scaled_font);
+
+cairo_private void
+_cairo_scaled_font_thaw_cache (cairo_scaled_font_t *scaled_font);
+
+cairo_private void
+_cairo_scaled_font_reset_cache (cairo_scaled_font_t *scaled_font);
+
+cairo_private cairo_status_t
+_cairo_scaled_font_set_error (cairo_scaled_font_t *scaled_font,
+                             cairo_status_t status);
+
+cairo_private cairo_scaled_font_t *
+_cairo_scaled_font_create_in_error (cairo_status_t status);
+
+cairo_private void
+_cairo_scaled_font_reset_static_data (void);
+
+cairo_private cairo_status_t
+_cairo_scaled_font_register_placeholder_and_unlock_font_map (cairo_scaled_font_t *scaled_font);
+
+cairo_private void
+_cairo_scaled_font_unregister_placeholder_and_lock_font_map (cairo_scaled_font_t *scaled_font);
+
+cairo_private cairo_status_t
+_cairo_scaled_font_init (cairo_scaled_font_t               *scaled_font,
+                        cairo_font_face_t                 *font_face,
+                        const cairo_matrix_t              *font_matrix,
+                        const cairo_matrix_t              *ctm,
+                        const cairo_font_options_t        *options,
+                        const cairo_scaled_font_backend_t *backend);
+
+cairo_private cairo_status_t
+_cairo_scaled_font_set_metrics (cairo_scaled_font_t        *scaled_font,
+                               cairo_font_extents_t        *fs_metrics);
+
+/* This should only be called on an error path by a scaled_font constructor */
+cairo_private void
+_cairo_scaled_font_fini (cairo_scaled_font_t *scaled_font);
+
+cairo_private cairo_status_t
+_cairo_scaled_font_font_extents (cairo_scaled_font_t  *scaled_font,
+                                cairo_font_extents_t *extents);
+
+cairo_private cairo_status_t
+_cairo_scaled_font_glyph_device_extents (cairo_scaled_font_t    *scaled_font,
+                                        const cairo_glyph_t     *glyphs,
+                                        int                      num_glyphs,
+                                        cairo_rectangle_int_t   *extents,
+                                        cairo_bool_t            *overlap);
+
+cairo_private void
+_cairo_scaled_font_glyph_approximate_extents (cairo_scaled_font_t       *scaled_font,
+                                             const cairo_glyph_t        *glyphs,
+                                             int                      num_glyphs,
+                                             cairo_rectangle_int_t   *extents);
+
+cairo_private cairo_status_t
+_cairo_scaled_font_show_glyphs (cairo_scaled_font_t *scaled_font,
+                               cairo_operator_t     op,
+                               const cairo_pattern_t *source,
+                               cairo_surface_t     *surface,
+                               int                  source_x,
+                               int                  source_y,
+                               int                  dest_x,
+                               int                  dest_y,
+                               unsigned int         width,
+                               unsigned int         height,
+                               cairo_glyph_t       *glyphs,
+                               int                  num_glyphs,
+                               cairo_region_t      *clip_region);
+
+cairo_private cairo_status_t
+_cairo_scaled_font_glyph_path (cairo_scaled_font_t *scaled_font,
+                              const cairo_glyph_t *glyphs,
+                              int                  num_glyphs,
+                              cairo_path_fixed_t  *path);
+
+cairo_private void
+_cairo_scaled_glyph_set_metrics (cairo_scaled_glyph_t *scaled_glyph,
+                                cairo_scaled_font_t *scaled_font,
+                                cairo_text_extents_t *fs_metrics);
+
+cairo_private void
+_cairo_scaled_glyph_set_surface (cairo_scaled_glyph_t *scaled_glyph,
+                                cairo_scaled_font_t *scaled_font,
+                                cairo_image_surface_t *surface);
+
+cairo_private void
+_cairo_scaled_glyph_set_path (cairo_scaled_glyph_t *scaled_glyph,
+                             cairo_scaled_font_t *scaled_font,
+                             cairo_path_fixed_t *path);
+
+cairo_private void
+_cairo_scaled_glyph_set_recording_surface (cairo_scaled_glyph_t *scaled_glyph,
+                                           cairo_scaled_font_t *scaled_font,
+                                           cairo_surface_t *recording_surface);
+
+cairo_private cairo_int_status_t
+_cairo_scaled_glyph_lookup (cairo_scaled_font_t *scaled_font,
+                           unsigned long index,
+                           cairo_scaled_glyph_info_t info,
+                           cairo_scaled_glyph_t **scaled_glyph_ret);
+
+cairo_private double
+_cairo_scaled_font_get_max_scale (cairo_scaled_font_t *scaled_font);
+
+cairo_private void
+_cairo_scaled_font_map_destroy (void);
+
+/* cairo-stroke-style.c */
+
+cairo_private void
+_cairo_stroke_style_init (cairo_stroke_style_t *style);
+
+cairo_private cairo_status_t
+_cairo_stroke_style_init_copy (cairo_stroke_style_t *style,
+                              const cairo_stroke_style_t *other);
+
+cairo_private void
+_cairo_stroke_style_fini (cairo_stroke_style_t *style);
+
+cairo_private void
+_cairo_stroke_style_max_distance_from_path (const cairo_stroke_style_t *style,
+                                           const cairo_path_fixed_t *path,
+                                            const cairo_matrix_t *ctm,
+                                            double *dx, double *dy);
+
+cairo_private double
+_cairo_stroke_style_dash_period (const cairo_stroke_style_t *style);
+
+cairo_private double
+_cairo_stroke_style_dash_stroked (const cairo_stroke_style_t *style);
+
+cairo_private cairo_bool_t
+_cairo_stroke_style_dash_can_approximate (const cairo_stroke_style_t *style,
+                                         const cairo_matrix_t *ctm,
+                                         double tolerance);
+
+cairo_private void
+_cairo_stroke_style_dash_approximate (const cairo_stroke_style_t *style,
+                                     const cairo_matrix_t *ctm,
+                                     double tolerance,
+                                     double *dash_offset,
+                                     double *dashes,
+                                     unsigned int *num_dashes);
+
+
+/* cairo-surface.c */
+
+cairo_private cairo_surface_t *
+_cairo_surface_create_in_error (cairo_status_t status);
+
+cairo_private cairo_status_t
+_cairo_surface_copy_mime_data (cairo_surface_t *dst,
+                              cairo_surface_t *src);
+
+cairo_private_no_warn cairo_int_status_t
+_cairo_surface_set_error (cairo_surface_t      *surface,
+                         cairo_int_status_t     status);
+
+cairo_private void
+_cairo_surface_set_resolution (cairo_surface_t *surface,
+                               double x_res,
+                               double y_res);
+
+cairo_private cairo_surface_t *
+_cairo_surface_create_similar_scratch (cairo_surface_t *other,
+                                      cairo_content_t  content,
+                                      int              width,
+                                      int              height);
+
+cairo_private cairo_surface_t *
+_cairo_surface_create_for_rectangle_int (cairo_surface_t *target,
+                                        const cairo_rectangle_int_t *extents);
+
+cairo_private cairo_surface_t *
+_cairo_surface_create_similar_solid (cairo_surface_t       *other,
+                                    cairo_content_t         content,
+                                    int                     width,
+                                    int                     height,
+                                    const cairo_color_t    *color);
+
+cairo_private void
+_cairo_surface_init (cairo_surface_t                   *surface,
+                    const cairo_surface_backend_t      *backend,
+                    cairo_device_t                     *device,
+                    cairo_content_t                     content);
+
+cairo_private void
+_cairo_surface_set_font_options (cairo_surface_t       *surface,
+                                cairo_font_options_t  *options);
+
+cairo_private cairo_status_t
+_cairo_surface_paint (cairo_surface_t  *surface,
+                     cairo_operator_t   op,
+                     const cairo_pattern_t *source,
+                     const cairo_clip_t            *clip);
+
+cairo_private cairo_status_t
+_cairo_surface_mask (cairo_surface_t   *surface,
+                    cairo_operator_t    op,
+                    const cairo_pattern_t      *source,
+                    const cairo_pattern_t      *mask,
+                    const cairo_clip_t         *clip);
+
+cairo_private cairo_status_t
+_cairo_surface_fill_stroke (cairo_surface_t        *surface,
+                           cairo_operator_t         fill_op,
+                           const cairo_pattern_t   *fill_source,
+                           cairo_fill_rule_t        fill_rule,
+                           double                   fill_tolerance,
+                           cairo_antialias_t        fill_antialias,
+                           cairo_path_fixed_t      *path,
+                           cairo_operator_t         stroke_op,
+                           const cairo_pattern_t   *stroke_source,
+                           const cairo_stroke_style_t    *stroke_style,
+                           const cairo_matrix_t            *stroke_ctm,
+                           const cairo_matrix_t            *stroke_ctm_inverse,
+                           double                   stroke_tolerance,
+                           cairo_antialias_t        stroke_antialias,
+                           const cairo_clip_t      *clip);
+
+cairo_private cairo_status_t
+_cairo_surface_stroke (cairo_surface_t         *surface,
+                      cairo_operator_t          op,
+                      const cairo_pattern_t    *source,
+                      const cairo_path_fixed_t *path,
+                      const cairo_stroke_style_t       *style,
+                      const cairo_matrix_t             *ctm,
+                      const cairo_matrix_t             *ctm_inverse,
+                      double                    tolerance,
+                      cairo_antialias_t         antialias,
+                      const cairo_clip_t               *clip);
+
+cairo_private cairo_status_t
+_cairo_surface_fill (cairo_surface_t   *surface,
+                    cairo_operator_t    op,
+                    const cairo_pattern_t *source,
+                    const cairo_path_fixed_t   *path,
+                    cairo_fill_rule_t   fill_rule,
+                    double              tolerance,
+                    cairo_antialias_t   antialias,
+                    const cairo_clip_t *clip);
+
+cairo_private cairo_status_t
+_cairo_surface_show_text_glyphs (cairo_surface_t           *surface,
+                                cairo_operator_t            op,
+                                const cairo_pattern_t      *source,
+                                const char                 *utf8,
+                                int                         utf8_len,
+                                cairo_glyph_t              *glyphs,
+                                int                         num_glyphs,
+                                const cairo_text_cluster_t *clusters,
+                                int                         num_clusters,
+                                cairo_text_cluster_flags_t  cluster_flags,
+                                cairo_scaled_font_t        *scaled_font,
+                                const cairo_clip_t                 *clip);
+
+cairo_private cairo_status_t
+_cairo_surface_acquire_source_image (cairo_surface_t         *surface,
+                                    cairo_image_surface_t  **image_out,
+                                    void                   **image_extra);
+
+cairo_private void
+_cairo_surface_release_source_image (cairo_surface_t        *surface,
+                                    cairo_image_surface_t  *image,
+                                    void                   *image_extra);
+
+cairo_private cairo_surface_t *
+_cairo_surface_snapshot (cairo_surface_t *surface);
+
+cairo_private void
+_cairo_surface_attach_snapshot (cairo_surface_t *surface,
+                               cairo_surface_t *snapshot,
+                               cairo_surface_func_t detach_func);
+
+cairo_private cairo_surface_t *
+_cairo_surface_has_snapshot (cairo_surface_t *surface,
+                            const cairo_surface_backend_t *backend);
+
+cairo_private void
+_cairo_surface_detach_snapshot (cairo_surface_t *snapshot);
+
+cairo_private_no_warn cairo_bool_t
+_cairo_surface_get_extents (cairo_surface_t         *surface,
+                           cairo_rectangle_int_t   *extents);
+
+cairo_private void
+_cairo_surface_set_device_scale (cairo_surface_t *surface,
+                                double           sx,
+                                double           sy);
+
+cairo_private cairo_bool_t
+_cairo_surface_has_device_transform (cairo_surface_t *surface) cairo_pure;
+
+cairo_private void
+_cairo_surface_release_device_reference (cairo_surface_t *surface);
+
+/* cairo-image-surface.c */
+
+/* XXX: In cairo 1.2.0 we added a new %CAIRO_FORMAT_RGB16_565 but
+ * neglected to adjust this macro. The net effect is that it's
+ * impossible to externally create an image surface with this
+ * format. This is perhaps a good thing since we also neglected to fix
+ * up things like cairo_surface_write_to_png() for the new format
+ * (-Wswitch-enum will tell you where). Is it obvious that format was
+ * added in haste?
+ *
+ * The reason for the new format was to allow the xlib backend to be
+ * used on X servers with a 565 visual. So the new format did its job
+ * for that, even without being considered "valid" for the sake of
+ * things like cairo_image_surface_create().
+ *
+ * Since 1.2.0 we ran into the same situtation with X servers with BGR
+ * visuals. This time we invented #cairo_internal_format_t instead,
+ * (see it for more discussion).
+ *
+ * The punchline is that %CAIRO_FORMAT_VALID must not conside any
+ * internal format to be valid. Also we need to decide if the
+ * RGB16_565 should be moved to instead be an internal format. If so,
+ * this macro need not change for it. (We probably will need to leave
+ * an RGB16_565 value in the header files for the sake of code that
+ * might have that value in it.)
+ *
+ * If we do decide to start fully supporting RGB16_565 as an external
+ * format, then %CAIRO_FORMAT_VALID needs to be adjusted to include
+ * it. But that should not happen before all necessary code is fixed
+ * to support it (at least cairo_surface_write_to_png() and a few spots
+ * in cairo-xlib-surface.c--again see -Wswitch-enum).
+ */
+#define CAIRO_FORMAT_VALID(format) ((format) >= CAIRO_FORMAT_ARGB32 &&         \
+                                    (format) <= CAIRO_FORMAT_RGB30)
+
+/* pixman-required stride alignment in bytes. */
+#define CAIRO_STRIDE_ALIGNMENT (sizeof (uint32_t))
+#define CAIRO_STRIDE_FOR_WIDTH_BPP(w,bpp) \
+   ((((bpp)*(w)+7)/8 + CAIRO_STRIDE_ALIGNMENT-1) & -CAIRO_STRIDE_ALIGNMENT)
+
+#define CAIRO_CONTENT_VALID(content) ((content) &&                              \
+                                     (((content) & ~(CAIRO_CONTENT_COLOR |      \
+                                                     CAIRO_CONTENT_ALPHA |      \
+                                                     CAIRO_CONTENT_COLOR_ALPHA))\
+                                      == 0))
+
+cairo_private int
+_cairo_format_bits_per_pixel (cairo_format_t format) cairo_const;
+
+cairo_private cairo_format_t
+_cairo_format_from_content (cairo_content_t content) cairo_const;
+
+cairo_private cairo_format_t
+_cairo_format_from_pixman_format (pixman_format_code_t pixman_format);
+
+cairo_private cairo_content_t
+_cairo_content_from_format (cairo_format_t format) cairo_const;
+
+cairo_private cairo_content_t
+_cairo_content_from_pixman_format (pixman_format_code_t pixman_format);
+
+cairo_private cairo_surface_t *
+_cairo_image_surface_create_for_pixman_image (pixman_image_t           *pixman_image,
+                                             pixman_format_code_t       pixman_format);
+
+cairo_private pixman_format_code_t
+_cairo_format_to_pixman_format_code (cairo_format_t format);
+
+cairo_private cairo_bool_t
+_pixman_format_from_masks (cairo_format_masks_t *masks,
+                          pixman_format_code_t *format_ret);
+
+cairo_private cairo_bool_t
+_pixman_format_to_masks (pixman_format_code_t   pixman_format,
+                        cairo_format_masks_t   *masks);
+
+cairo_private void
+_cairo_image_reset_static_data (void);
+
+cairo_private cairo_surface_t *
+_cairo_image_surface_create_with_pixman_format (unsigned char          *data,
+                                               pixman_format_code_t     pixman_format,
+                                               int                      width,
+                                               int                      height,
+                                               int                      stride);
+
+cairo_private cairo_surface_t *
+_cairo_image_surface_create_with_content (cairo_content_t      content,
+                                         int                   width,
+                                         int                   height);
+
+cairo_private void
+_cairo_image_surface_assume_ownership_of_data (cairo_image_surface_t *surface);
+
+cairo_private cairo_image_surface_t *
+_cairo_image_surface_coerce (cairo_image_surface_t     *surface);
+
+cairo_private cairo_image_surface_t *
+_cairo_image_surface_coerce_to_format (cairo_image_surface_t   *surface,
+                                      cairo_format_t            format);
+
+cairo_private cairo_image_transparency_t
+_cairo_image_analyze_transparency (cairo_image_surface_t      *image);
+
+cairo_private cairo_image_color_t
+_cairo_image_analyze_color (cairo_image_surface_t      *image);
+
+/* cairo-pen.c */
+cairo_private cairo_status_t
+_cairo_pen_init (cairo_pen_t   *pen,
+                double          radius,
+                double          tolerance,
+                const cairo_matrix_t   *ctm);
+
+cairo_private void
+_cairo_pen_init_empty (cairo_pen_t *pen);
+
+cairo_private cairo_status_t
+_cairo_pen_init_copy (cairo_pen_t *pen, const cairo_pen_t *other);
+
+cairo_private void
+_cairo_pen_fini (cairo_pen_t *pen);
+
+cairo_private cairo_status_t
+_cairo_pen_add_points (cairo_pen_t *pen, cairo_point_t *point, int num_points);
+
+cairo_private int
+_cairo_pen_find_active_cw_vertex_index (const cairo_pen_t *pen,
+                                       const cairo_slope_t *slope);
+
+cairo_private int
+_cairo_pen_find_active_ccw_vertex_index (const cairo_pen_t *pen,
+                                        const cairo_slope_t *slope);
+
+/* cairo-polygon.c */
+cairo_private void
+_cairo_polygon_init (cairo_polygon_t   *polygon,
+                    const cairo_box_t *boxes,
+                    int                num_boxes);
+
+cairo_private void
+_cairo_polygon_init_with_clip (cairo_polygon_t *polygon,
+                              const cairo_clip_t *clip);
+
+cairo_private cairo_status_t
+_cairo_polygon_init_boxes (cairo_polygon_t *polygon,
+                          const cairo_boxes_t *boxes);
+
+cairo_private cairo_status_t
+_cairo_polygon_init_box_array (cairo_polygon_t *polygon,
+                              cairo_box_t *boxes,
+                              int num_boxes);
+
+cairo_private void
+_cairo_polygon_limit (cairo_polygon_t *polygon,
+                    const cairo_box_t *limits,
+                    int num_limits);
+
+cairo_private void
+_cairo_polygon_limit_to_clip (cairo_polygon_t *polygon,
+                             const cairo_clip_t *clip);
+
+cairo_private void
+_cairo_polygon_fini (cairo_polygon_t *polygon);
+
+cairo_private cairo_status_t
+_cairo_polygon_add_line (cairo_polygon_t *polygon,
+                        const cairo_line_t *line,
+                        int top, int bottom,
+                        int dir);
+
+cairo_private cairo_status_t
+_cairo_polygon_add_external_edge (void *polygon,
+                                 const cairo_point_t *p1,
+                                 const cairo_point_t *p2);
+
+cairo_private cairo_status_t
+_cairo_polygon_add_contour (cairo_polygon_t *polygon,
+                           const cairo_contour_t *contour);
+
+cairo_private void
+_cairo_polygon_translate (cairo_polygon_t *polygon, int dx, int dy);
+
+cairo_private cairo_status_t
+_cairo_polygon_reduce (cairo_polygon_t *polygon,
+                      cairo_fill_rule_t fill_rule);
+
+cairo_private cairo_status_t
+_cairo_polygon_intersect (cairo_polygon_t *a, int winding_a,
+                         cairo_polygon_t *b, int winding_b);
+
+cairo_private cairo_status_t
+_cairo_polygon_intersect_with_boxes (cairo_polygon_t *polygon,
+                                    cairo_fill_rule_t *winding,
+                                    cairo_box_t *boxes,
+                                    int num_boxes);
+
+static inline cairo_bool_t
+_cairo_polygon_is_empty (const cairo_polygon_t *polygon)
+{
+    return
+       polygon->num_edges == 0 ||
+       polygon->extents.p2.x <= polygon->extents.p1.x;
+}
+
+#define _cairo_polygon_status(P) ((cairo_polygon_t *) (P))->status
+
+/* cairo-spline.c */
+cairo_private cairo_bool_t
+_cairo_spline_init (cairo_spline_t *spline,
+                   cairo_spline_add_point_func_t add_point_func,
+                   void *closure,
+                   const cairo_point_t *a, const cairo_point_t *b,
+                   const cairo_point_t *c, const cairo_point_t *d);
+
+cairo_private cairo_status_t
+_cairo_spline_decompose (cairo_spline_t *spline, double tolerance);
+
+cairo_private cairo_status_t
+_cairo_spline_bound (cairo_spline_add_point_func_t add_point_func,
+                    void *closure,
+                    const cairo_point_t *p0, const cairo_point_t *p1,
+                    const cairo_point_t *p2, const cairo_point_t *p3);
+
+/* cairo-matrix.c */
+cairo_private void
+_cairo_matrix_get_affine (const cairo_matrix_t *matrix,
+                         double *xx, double *yx,
+                         double *xy, double *yy,
+                         double *x0, double *y0);
+
+cairo_private void
+_cairo_matrix_transform_bounding_box (const cairo_matrix_t *matrix,
+                                     double *x1, double *y1,
+                                     double *x2, double *y2,
+                                     cairo_bool_t *is_tight);
+
+cairo_private void
+_cairo_matrix_transform_bounding_box_fixed (const cairo_matrix_t *matrix,
+                                           cairo_box_t          *bbox,
+                                           cairo_bool_t         *is_tight);
+
+cairo_private cairo_bool_t
+_cairo_matrix_is_invertible (const cairo_matrix_t *matrix) cairo_pure;
+
+cairo_private cairo_bool_t
+_cairo_matrix_is_scale_0 (const cairo_matrix_t *matrix) cairo_pure;
+
+cairo_private double
+_cairo_matrix_compute_determinant (const cairo_matrix_t *matrix) cairo_pure;
+
+cairo_private cairo_status_t
+_cairo_matrix_compute_basis_scale_factors (const cairo_matrix_t *matrix,
+                                          double *sx, double *sy, int x_major);
+
+cairo_private cairo_bool_t
+_cairo_matrix_is_identity (const cairo_matrix_t *matrix) cairo_pure;
+
+cairo_private cairo_bool_t
+_cairo_matrix_is_translation (const cairo_matrix_t *matrix) cairo_pure;
+
+cairo_private cairo_bool_t
+_cairo_matrix_is_integer_translation(const cairo_matrix_t *matrix,
+                                    int *itx, int *ity);
+
+cairo_private cairo_bool_t
+_cairo_matrix_has_unity_scale (const cairo_matrix_t *matrix);
+
+#ifdef TIZEN_FIX_DASHED_LINE_STROKE
+/* Check whether matrix is uniform scale or not */
+cairo_private cairo_bool_t
+_cairo_matrix_has_uniform_scale (const cairo_matrix_t *matrix);
+#endif
+
+cairo_private cairo_bool_t
+_cairo_matrix_is_pixel_exact (const cairo_matrix_t *matrix) cairo_pure;
+
+cairo_private double
+_cairo_matrix_transformed_circle_major_axis (const cairo_matrix_t *matrix,
+                                            double radius) cairo_pure;
+
+cairo_private cairo_bool_t
+_cairo_matrix_is_pixman_translation (const cairo_matrix_t     *matrix,
+                                    cairo_filter_t            filter,
+                                    int                      *out_x_offset,
+                                    int                      *out_y_offset);
+
+cairo_private cairo_status_t
+_cairo_matrix_to_pixman_matrix_offset (const cairo_matrix_t    *matrix,
+                                      cairo_filter_t            filter,
+                                      double                    xc,
+                                      double                    yc,
+                                      pixman_transform_t       *out_transform,
+                                      int                      *out_x_offset,
+                                      int                      *out_y_offset);
+
+cairo_private cairo_status_t
+_cairo_bentley_ottmann_tessellate_rectilinear_polygon (cairo_traps_t    *traps,
+                                                      const cairo_polygon_t *polygon,
+                                                      cairo_fill_rule_t          fill_rule);
+
+cairo_private cairo_status_t
+_cairo_bentley_ottmann_tessellate_polygon (cairo_traps_t         *traps,
+                                          const cairo_polygon_t *polygon,
+                                          cairo_fill_rule_t      fill_rule);
+
+cairo_private cairo_status_t
+_cairo_bentley_ottmann_tessellate_traps (cairo_traps_t *traps,
+                                        cairo_fill_rule_t fill_rule);
+
+cairo_private cairo_status_t
+_cairo_bentley_ottmann_tessellate_rectangular_traps (cairo_traps_t *traps,
+                                                    cairo_fill_rule_t fill_rule);
+
+cairo_private cairo_status_t
+_cairo_bentley_ottmann_tessellate_boxes (const cairo_boxes_t *in,
+                                        cairo_fill_rule_t fill_rule,
+                                        cairo_boxes_t *out);
+
+cairo_private cairo_status_t
+_cairo_bentley_ottmann_tessellate_rectilinear_traps (cairo_traps_t *traps,
+                                                    cairo_fill_rule_t fill_rule);
+
+cairo_private cairo_status_t
+_cairo_bentley_ottmann_tessellate_rectilinear_polygon_to_boxes (const cairo_polygon_t *polygon,
+                                                               cairo_fill_rule_t fill_rule,
+                                                               cairo_boxes_t *boxes);
+
+cairo_private void
+_cairo_trapezoid_array_translate_and_scale (cairo_trapezoid_t *offset_traps,
+                                           cairo_trapezoid_t *src_traps,
+                                           int num_traps,
+                                           double tx, double ty,
+                                           double sx, double sy);
+
+#if CAIRO_HAS_DRM_SURFACE
+
+cairo_private void
+_cairo_drm_device_reset_static_data (void);
+
+#endif
+
+cairo_private void
+_cairo_clip_reset_static_data (void);
+
+cairo_private void
+_cairo_pattern_reset_static_data (void);
+
+/* cairo-unicode.c */
+
+cairo_private int
+_cairo_utf8_get_char_validated (const char *p,
+                               uint32_t   *unicode);
+
+cairo_private cairo_status_t
+_cairo_utf8_to_ucs4 (const char *str,
+                    int         len,
+                    uint32_t  **result,
+                    int        *items_written);
+
+cairo_private int
+_cairo_ucs4_to_utf8 (uint32_t    unicode,
+                    char       *utf8);
+
+#if CAIRO_HAS_WIN32_FONT || CAIRO_HAS_QUARTZ_FONT || CAIRO_HAS_PDF_OPERATORS
+# define CAIRO_HAS_UTF8_TO_UTF16 1
+#endif
+#if CAIRO_HAS_UTF8_TO_UTF16
+cairo_private cairo_status_t
+_cairo_utf8_to_utf16 (const char *str,
+                     int         len,
+                     uint16_t  **result,
+                     int        *items_written);
+#endif
+
+/* cairo-observer.c */
+
+cairo_private void
+_cairo_observers_notify (cairo_list_t *observers, void *arg);
+
+/* Avoid unnecessary PLT entries.  */
+slim_hidden_proto (cairo_clip_preserve);
+slim_hidden_proto (cairo_close_path);
+slim_hidden_proto (cairo_create);
+slim_hidden_proto (cairo_curve_to);
+slim_hidden_proto (cairo_destroy);
+slim_hidden_proto (cairo_fill_preserve);
+slim_hidden_proto (cairo_font_face_destroy);
+slim_hidden_proto (cairo_font_face_get_user_data);
+slim_hidden_proto_no_warn (cairo_font_face_reference);
+slim_hidden_proto (cairo_font_face_set_user_data);
+slim_hidden_proto (cairo_font_options_equal);
+slim_hidden_proto (cairo_font_options_hash);
+slim_hidden_proto (cairo_font_options_merge);
+slim_hidden_proto (cairo_font_options_set_antialias);
+slim_hidden_proto (cairo_font_options_set_hint_metrics);
+slim_hidden_proto (cairo_font_options_set_hint_style);
+slim_hidden_proto (cairo_font_options_set_subpixel_order);
+slim_hidden_proto (cairo_font_options_status);
+slim_hidden_proto (cairo_format_stride_for_width);
+slim_hidden_proto (cairo_get_current_point);
+slim_hidden_proto (cairo_get_line_width);
+slim_hidden_proto (cairo_get_matrix);
+slim_hidden_proto (cairo_get_scaled_font);
+slim_hidden_proto (cairo_get_target);
+slim_hidden_proto (cairo_get_tolerance);
+slim_hidden_proto (cairo_glyph_allocate);
+slim_hidden_proto (cairo_glyph_free);
+slim_hidden_proto (cairo_image_surface_create);
+slim_hidden_proto (cairo_image_surface_create_for_data);
+slim_hidden_proto (cairo_image_surface_get_data);
+slim_hidden_proto (cairo_image_surface_get_format);
+slim_hidden_proto (cairo_image_surface_get_height);
+slim_hidden_proto (cairo_image_surface_get_stride);
+slim_hidden_proto (cairo_image_surface_get_width);
+slim_hidden_proto (cairo_line_to);
+slim_hidden_proto (cairo_mask);
+slim_hidden_proto (cairo_matrix_init);
+slim_hidden_proto (cairo_matrix_init_identity);
+slim_hidden_proto (cairo_matrix_init_rotate);
+slim_hidden_proto (cairo_matrix_init_scale);
+slim_hidden_proto (cairo_matrix_init_translate);
+slim_hidden_proto (cairo_matrix_invert);
+slim_hidden_proto (cairo_matrix_multiply);
+slim_hidden_proto (cairo_matrix_scale);
+slim_hidden_proto (cairo_matrix_transform_distance);
+slim_hidden_proto (cairo_matrix_transform_point);
+slim_hidden_proto (cairo_matrix_translate);
+slim_hidden_proto (cairo_move_to);
+slim_hidden_proto (cairo_new_path);
+slim_hidden_proto (cairo_paint);
+slim_hidden_proto (cairo_pattern_add_color_stop_rgba);
+slim_hidden_proto (cairo_pattern_create_for_surface);
+slim_hidden_proto (cairo_pattern_create_rgb);
+slim_hidden_proto (cairo_pattern_create_rgba);
+slim_hidden_proto (cairo_pattern_destroy);
+slim_hidden_proto (cairo_pattern_get_extend);
+slim_hidden_proto (cairo_mesh_pattern_curve_to);
+slim_hidden_proto (cairo_mesh_pattern_get_control_point);
+slim_hidden_proto (cairo_mesh_pattern_get_corner_color_rgba);
+slim_hidden_proto (cairo_mesh_pattern_get_patch_count);
+slim_hidden_proto (cairo_mesh_pattern_get_path);
+slim_hidden_proto (cairo_mesh_pattern_line_to);
+slim_hidden_proto (cairo_mesh_pattern_move_to);
+slim_hidden_proto (cairo_mesh_pattern_set_corner_color_rgba);
+slim_hidden_proto_no_warn (cairo_pattern_reference);
+slim_hidden_proto (cairo_pattern_set_matrix);
+slim_hidden_proto (cairo_pop_group);
+slim_hidden_proto (cairo_push_group_with_content);
+slim_hidden_proto_no_warn (cairo_path_destroy);
+slim_hidden_proto (cairo_rel_line_to);
+slim_hidden_proto (cairo_restore);
+slim_hidden_proto (cairo_save);
+slim_hidden_proto (cairo_scale);
+slim_hidden_proto (cairo_scaled_font_create);
+slim_hidden_proto (cairo_scaled_font_destroy);
+slim_hidden_proto (cairo_scaled_font_extents);
+slim_hidden_proto (cairo_scaled_font_get_ctm);
+slim_hidden_proto (cairo_scaled_font_get_font_face);
+slim_hidden_proto (cairo_scaled_font_get_font_matrix);
+slim_hidden_proto (cairo_scaled_font_get_font_options);
+slim_hidden_proto (cairo_scaled_font_glyph_extents);
+slim_hidden_proto_no_warn (cairo_scaled_font_reference);
+slim_hidden_proto (cairo_scaled_font_status);
+slim_hidden_proto (cairo_scaled_font_get_user_data);
+slim_hidden_proto (cairo_scaled_font_set_user_data);
+slim_hidden_proto (cairo_scaled_font_text_to_glyphs);
+slim_hidden_proto (cairo_set_font_matrix);
+slim_hidden_proto (cairo_set_font_options);
+slim_hidden_proto (cairo_set_font_size);
+slim_hidden_proto (cairo_set_line_cap);
+slim_hidden_proto (cairo_set_line_join);
+slim_hidden_proto (cairo_set_line_width);
+slim_hidden_proto (cairo_set_matrix);
+slim_hidden_proto (cairo_set_operator);
+slim_hidden_proto (cairo_set_source);
+slim_hidden_proto (cairo_set_source_rgb);
+slim_hidden_proto (cairo_set_source_surface);
+slim_hidden_proto (cairo_set_tolerance);
+slim_hidden_proto (cairo_status);
+slim_hidden_proto (cairo_stroke);
+slim_hidden_proto (cairo_stroke_preserve);
+slim_hidden_proto (cairo_surface_copy_page);
+slim_hidden_proto (cairo_surface_create_similar_image);
+slim_hidden_proto (cairo_surface_destroy);
+slim_hidden_proto (cairo_surface_finish);
+slim_hidden_proto (cairo_surface_flush);
+slim_hidden_proto (cairo_surface_get_content);
+slim_hidden_proto (cairo_surface_get_device_offset);
+slim_hidden_proto (cairo_surface_get_font_options);
+slim_hidden_proto (cairo_surface_get_mime_data);
+slim_hidden_proto (cairo_surface_get_type);
+slim_hidden_proto (cairo_surface_has_show_text_glyphs);
+slim_hidden_proto (cairo_surface_map_to_image);
+slim_hidden_proto (cairo_surface_mark_dirty);
+slim_hidden_proto (cairo_surface_mark_dirty_rectangle);
+slim_hidden_proto_no_warn (cairo_surface_reference);
+slim_hidden_proto (cairo_surface_set_device_offset);
+slim_hidden_proto (cairo_surface_set_fallback_resolution);
+slim_hidden_proto (cairo_surface_set_mime_data);
+slim_hidden_proto (cairo_surface_show_page);
+slim_hidden_proto (cairo_surface_status);
+slim_hidden_proto (cairo_surface_unmap_image);
+slim_hidden_proto (cairo_text_cluster_allocate);
+slim_hidden_proto (cairo_text_cluster_free);
+slim_hidden_proto (cairo_toy_font_face_create);
+slim_hidden_proto (cairo_toy_font_face_get_slant);
+slim_hidden_proto (cairo_toy_font_face_get_weight);
+slim_hidden_proto (cairo_translate);
+slim_hidden_proto (cairo_transform);
+slim_hidden_proto (cairo_user_font_face_create);
+slim_hidden_proto (cairo_user_font_face_set_init_func);
+slim_hidden_proto (cairo_user_font_face_set_render_glyph_func);
+slim_hidden_proto (cairo_user_font_face_set_unicode_to_glyph_func);
+slim_hidden_proto (cairo_device_to_user);
+slim_hidden_proto (cairo_user_to_device);
+slim_hidden_proto (cairo_user_to_device_distance);
+slim_hidden_proto (cairo_version_string);
+slim_hidden_proto (cairo_region_create);
+slim_hidden_proto (cairo_region_create_rectangle);
+slim_hidden_proto (cairo_region_create_rectangles);
+slim_hidden_proto (cairo_region_copy);
+slim_hidden_proto (cairo_region_reference);
+slim_hidden_proto (cairo_region_destroy);
+slim_hidden_proto (cairo_region_equal);
+slim_hidden_proto (cairo_region_status);
+slim_hidden_proto (cairo_region_get_extents);
+slim_hidden_proto (cairo_region_num_rectangles);
+slim_hidden_proto (cairo_region_get_rectangle);
+slim_hidden_proto (cairo_region_is_empty);
+slim_hidden_proto (cairo_region_contains_rectangle);
+slim_hidden_proto (cairo_region_contains_point);
+slim_hidden_proto (cairo_region_translate);
+slim_hidden_proto (cairo_region_subtract);
+slim_hidden_proto (cairo_region_subtract_rectangle);
+slim_hidden_proto (cairo_region_intersect);
+slim_hidden_proto (cairo_region_intersect_rectangle);
+slim_hidden_proto (cairo_region_union);
+slim_hidden_proto (cairo_region_union_rectangle);
+slim_hidden_proto (cairo_region_xor);
+slim_hidden_proto (cairo_region_xor_rectangle);
+
+#if CAIRO_HAS_PNG_FUNCTIONS
+
+slim_hidden_proto (cairo_surface_write_to_png_stream);
+
+#endif
+
+cairo_private_no_warn cairo_filter_t
+_cairo_pattern_analyze_filter (const cairo_pattern_t   *pattern,
+                              double                   *pad_out);
+
+CAIRO_END_DECLS
+
+#include "cairo-mutex-private.h"
+#include "cairo-fixed-private.h"
+#include "cairo-wideint-private.h"
+#include "cairo-malloc-private.h"
+#include "cairo-hash-private.h"
+
+#if HAVE_VALGRIND
+#include <memcheck.h>
+
+#define VG(x) x
+
+cairo_private void
+_cairo_debug_check_image_surface_is_defined (const cairo_surface_t *surface);
+
+#else
+
+#define VG(x)
+#define _cairo_debug_check_image_surface_is_defined(X)
+
+#endif
+
+cairo_private void
+_cairo_debug_print_path (FILE *stream, cairo_path_fixed_t *path);
+
+cairo_private void
+_cairo_debug_print_polygon (FILE *stream, cairo_polygon_t *polygon);
+
+cairo_private void
+_cairo_debug_print_clip (FILE *stream, const cairo_clip_t *clip);
+
+#endif
diff --git a/src/check-def.sh b/src/check-def.sh
new file mode 100755 (executable)
index 0000000..9008c58
--- /dev/null
@@ -0,0 +1,48 @@
+#!/bin/sh
+
+LC_ALL=C
+export LC_ALL
+
+if which nm 2>/dev/null >/dev/null; then
+       :
+else
+       echo "'nm' not found; skipping test"
+       exit 0
+fi
+
+test -z "$srcdir" && srcdir=.
+test -z "$MAKE" && MAKE=make
+stat=0
+
+$MAKE check-has-hidden-symbols.i > /dev/null || exit 1
+if tail -1 check-has-hidden-symbols.i | grep CAIRO_HAS_HIDDEN_SYMBOLS >/dev/null; then
+       echo "Compiler doesn't support symbol visibility; skipping test"
+       exit 0
+fi
+
+if [ "`uname -s`" = "Linux" ]; then
+       get_cairo_syms='( objdump -t "$so" | grep "^[^ ]* [^l.*]*[.]"; objdump -t "$so" | grep "[.]hidden.*\\<cairo"; ) | sed "s/.* //"'
+else
+       get_cairo_syms='nm "$so" | grep " [BCDGINRSTVW] " | cut -d" " -f3'
+fi
+
+defs="cairo.def"
+$MAKE $defs > /dev/null
+for def in $defs; do
+       lib=`echo "$def" | sed 's/[.]def$//'`
+       lib=`echo "$lib" | sed 's@.*/@@'`
+       so=.libs/lib${lib}.so
+
+       test -f "$so" || continue
+
+       echo Checking that $so has the same symbol list as $def
+
+       {
+               echo EXPORTS
+               eval $get_cairo_syms | c++filt --no-params | grep -v '^_cairo_test_\|^_fini\|^_init\|^_save[fg]pr\|^_rest[fg]pr\|^_Z\|^__gnu' | sort -u
+               # cheat: copy the last line from the def file!
+               tail -n1 "$def"
+       } | diff "$def" - >&2 || stat=1
+done
+
+exit $stat
diff --git a/src/check-doc-syntax.sh b/src/check-doc-syntax.sh
new file mode 100755 (executable)
index 0000000..8390e5e
--- /dev/null
@@ -0,0 +1,73 @@
+#!/bin/sh
+
+LC_ALL=C
+export LC_ALL
+
+if grep --version 2>/dev/null | grep GNU >/dev/null; then
+       :
+else
+       echo "GNU grep not found; skipping test"
+       exit 0
+fi
+
+test -z "$srcdir" && srcdir=.
+stat=0
+
+echo Checking documentation for incorrect syntax
+
+cd "$srcdir"
+
+if test "x$SGML_DOCS" = x; then
+    FILES=$all_cairo_files
+    if test "x$FILES" = x; then
+        FILES=`find . -name 'cairo*.h' -or -name 'cairo*.c' -or -name 'cairo*.cpp'`
+    fi
+fi
+
+enum_regexp="\([^%@']\|^\)\<\(FALSE\|TRUE\|NULL\|CAIRO_[0-9A-Z_]*\)\($\|[^(A-Za-z0-9_]\)"
+if test "x$SGML_DOCS" = x; then
+       enum_regexp='^[^:]*:[/ ][*]\(\|[ \t].*\)'$enum_regexp\($\|[^:]\)
+fi
+if echo $FILES | xargs grep . /dev/null | sed -e '/<programlisting>/,/<\/programlisting>/d' | grep "$enum_regexp" | grep -v '#####'; then
+       stat=1
+       echo Error: some macros in the docs are not prefixed by percent sign.
+       echo Fix this by searching for the following regexp in the above files:
+       echo "  '$enum_regexp'"
+fi >&2
+
+type_regexp="\( .*[^#']\| \|^\)\<cairo[0-9a-z_]*_t\>\($\|[^:]$\|[^:].\)"
+if test "x$SGML_DOCS" = x; then
+       type_regexp='^[^:]*:[/ ][*]'$type_regexp
+else
+       type_regexp='\(.'$type_regexp'\)\|\('$type_regexp'.\)'
+fi
+
+if echo $FILES | xargs grep . /dev/null | sed -e '/<programlisting>/,/<\/programlisting>/d' | grep -v "@Title" | grep "$type_regexp" | grep -v '#####'; then
+       stat=1
+       echo Error: some type names in the docs are not prefixed by hash sign,
+       echo neither are the only token in the doc line followed by colon.
+       echo Fix this by searching for the following regexp in the above files:
+       echo "  '$type_regexp'"
+fi >&2
+
+func_regexp="\([^#']\|^\)\<\(cairo_[][<>/0-9a-z_]*\>[^][<>(]\)"
+if test "x$SGML_DOCS" = x; then
+       func_regexp='^[^:]*:[/ ][*]\(\|[ \t].*\)'$func_regexp
+fi
+
+# We need to filter out gtk-doc markup errors for program listings.
+if echo $FILES | xargs grep . /dev/null | sed -e '/<programlisting>/,/<\/programlisting>/d' | grep "$func_regexp" | grep -v '^[^:]*: [*] [a-z_0-9]*:$' | grep -v '#####'; then
+       stat=1
+       echo Error: some function names in the docs are not followed by parentheses.
+       echo Fix this by searching for the following regexp in the above files:
+       echo "  '$func_regexp'"
+fi >&2
+
+note_regexp='\<NOTE\>'
+if echo $FILES | xargs grep "$note_regexp" /dev/null; then
+       stat=1
+       echo Error: some source files contain the string 'NOTE'.
+       echo Be civil and replace it by 'Note' please.
+fi >&2
+
+exit $stat
diff --git a/src/check-has-hidden-symbols.c b/src/check-has-hidden-symbols.c
new file mode 100644 (file)
index 0000000..1204127
--- /dev/null
@@ -0,0 +1,3 @@
+#include "cairoint.h"
+
+CAIRO_HAS_HIDDEN_SYMBOLS
diff --git a/src/check-headers.sh b/src/check-headers.sh
new file mode 100755 (executable)
index 0000000..6123295
--- /dev/null
@@ -0,0 +1,27 @@
+#!/bin/sh
+
+LC_ALL=C
+export LC_ALL
+
+test -z "$srcdir" && srcdir=.
+stat=0
+
+echo Checking public headers for missing cairo_public decorators
+
+cd "$srcdir"
+FILES=$all_cairo_headers
+if test "x$FILES" = x; then
+       FILES=`find . -name 'cairo*.h' ! -name '*-private.h' ! -name 'cairoint.h'`
+fi
+
+grep -B 1 '^cairo_.*[  ]\+(' /dev/null $FILES |
+awk '
+/^--$/ { context=""; public=0; next; }
+/:cairo_.*[    ]+\(/ { if (!public) {print context; print; print "--";} next; }
+/-cairo_public.*[      ]/ {public=1;}
+{ context=$0; }
+' |
+sed 's/[.]h-/.h:/' |
+grep . >&2 && stat=1
+
+exit $stat
diff --git a/src/check-link.c b/src/check-link.c
new file mode 100644 (file)
index 0000000..66ca1b2
--- /dev/null
@@ -0,0 +1,24 @@
+#define CAIRO_VERSION_H 1
+
+#include <cairo.h>
+
+/* get the "real" version info instead of dummy cairo-version.h */
+#undef CAIRO_VERSION_H
+#include "../cairo-version.h"
+
+#include <stdio.h>
+
+int
+main (void)
+{
+  printf ("Check linking to the just built cairo library\n");
+  if (cairo_version () == CAIRO_VERSION) {
+    return 0;
+  } else {
+    fprintf (stderr,
+            "Error: linked to cairo version %s instead of %s\n",
+            cairo_version_string (),
+            CAIRO_VERSION_STRING);
+    return 1;
+  }
+}
diff --git a/src/check-plt.sh b/src/check-plt.sh
new file mode 100755 (executable)
index 0000000..5a9dae1
--- /dev/null
@@ -0,0 +1,28 @@
+#!/bin/sh
+
+LC_ALL=C
+export LC_ALL
+
+if which readelf 2>/dev/null >/dev/null; then
+       :
+else
+       echo "'readelf' not found; skipping test"
+       exit 0
+fi
+
+test -z "$srcdir" && srcdir=.
+test -z "$MAKE" && MAKE=make
+stat=0
+
+$MAKE check-has-hidden-symbols.i > /dev/null || exit 1
+if tail -1 check-has-hidden-symbols.i | grep CAIRO_HAS_HIDDEN_SYMBOLS >/dev/null; then
+       echo "Compiler doesn't support symbol visibility; skipping test"
+       exit 0
+fi
+
+for so in .libs/lib*.so; do
+       echo Checking "$so" for local PLT entries
+       readelf -W -r "$so" | grep 'JU\?MP_SLO' | grep 'cairo' >&2 && stat=1
+done
+
+exit $stat
diff --git a/src/check-preprocessor-syntax.sh b/src/check-preprocessor-syntax.sh
new file mode 100755 (executable)
index 0000000..c415415
--- /dev/null
@@ -0,0 +1,59 @@
+#!/bin/sh
+
+LC_ALL=C
+export LC_ALL
+
+test -z "$srcdir" && srcdir=.
+cd "$srcdir"
+stat=0
+
+
+HEADERS=$all_cairo_headers
+test "x$HEADERS" = x && HEADERS=`find . -name 'cairo*.h' ! -name 'cairo*-private.h' ! -name 'cairoint.h'`
+
+PRIVATE=$all_cairo_private
+test "x$PRIVATE" = x && PRIVATE=`find . -name 'cairo*-private.h' -or -name 'cairoint.h'`
+
+SOURCES=$all_cairo_sources
+test "x$SOURCES" = x && SOURCES=`find . -name 'cairo*.c' -or -name 'cairo*.cpp'`
+
+ALL="/dev/null $HEADERS $PRIVATE $SOURCES"
+
+echo 'Checking that public header files #include "cairo.h" first (or none)'
+
+for x in $HEADERS; do
+       grep '#.*\<include\>' "$x" /dev/null | head -n 1
+done |
+grep -v '"cairo[.]h"' |
+grep -v 'cairo[.]h:' |
+grep . >&2 && stat=1
+
+
+echo 'Checking that private header files #include "some cairo header" first (or none)'
+
+for x in $PRIVATE; do
+       grep '#.*\<include\>' "$x" /dev/null | head -n 1
+done |
+grep -v '"cairo.*[.]h"' |
+grep -v 'cairoint[.]h:' |
+grep . >&2 && stat=1
+
+
+echo 'Checking that source files #include "cairoint.h" first (or none)'
+
+for x in $SOURCES; do
+       grep '#.*\<include\>' "$x" /dev/null | head -n 1
+done |
+grep -v '"cairoint[.]h"' |
+grep . >&2 && stat=1
+
+
+echo 'Checking that there is no #include <cairo.*.h>'
+grep '#.*\<include\>.*<.*cairo' $ALL >&2 && stat=1
+
+
+echo 'Checking that feature conditionals are used with #if only (not #ifdef)'
+grep '#ifdef CAIRO_HAS_' $ALL && stat=1
+grep '#if.*defined[ ]*(CAIRO_HAS_' $ALL && stat=1
+
+exit $stat
diff --git a/src/drm/cairo-drm-bo.c b/src/drm/cairo-drm-bo.c
new file mode 100644 (file)
index 0000000..a5b59f2
--- /dev/null
@@ -0,0 +1,130 @@
+/* Cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Chris Wilson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ */
+
+#include "cairoint.h"
+
+#include "cairo-drm-private.h"
+#include "cairo-drm-ioctl-private.h"
+
+#include "cairo-error-private.h"
+
+#include <sys/ioctl.h>
+#include <errno.h>
+
+#define ERR_DEBUG(x) x
+
+struct drm_gem_close {
+       /** Handle of the object to be closed. */
+       uint32_t handle;
+       uint32_t pad;
+};
+
+struct drm_gem_flink {
+       /** Handle for the object being named */
+       uint32_t handle;
+
+       /** Returned global name */
+       uint32_t name;
+};
+
+struct drm_gem_open {
+       /** Name of object being opened */
+       uint32_t name;
+
+       /** Returned handle for the object */
+       uint32_t handle;
+
+       /** Returned size of the object */
+       uint64_t size;
+};
+
+#define DRM_IOCTL_GEM_CLOSE            DRM_IOW (0x09, struct drm_gem_close)
+#define DRM_IOCTL_GEM_FLINK            DRM_IOWR(0x0a, struct drm_gem_flink)
+#define DRM_IOCTL_GEM_OPEN             DRM_IOWR(0x0b, struct drm_gem_open)
+
+cairo_status_t
+_cairo_drm_bo_open_for_name (const cairo_drm_device_t *dev,
+                            cairo_drm_bo_t *bo,
+                            uint32_t name)
+{
+    struct drm_gem_open open;
+    int ret;
+
+    open.name = name;
+    open.handle = 0;
+    open.size = 0;
+    do {
+       ret = ioctl (dev->fd, DRM_IOCTL_GEM_OPEN, &open);
+    } while (ret == -1 && errno == EINTR);
+    if (ret == -1) {
+       ERR_DEBUG((fprintf (stderr, "Failed to open bo for name %d: %s\n",
+                           name, strerror (errno))));
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+    }
+
+    bo->name = name;
+    bo->size = open.size;
+    bo->handle = open.handle;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_status_t
+_cairo_drm_bo_flink (const cairo_drm_device_t *dev,
+                    cairo_drm_bo_t *bo)
+{
+    struct drm_gem_flink flink;
+    int ret;
+
+    memset (&flink, 0, sizeof (flink));
+    flink.handle = bo->handle;
+    ret = ioctl (dev->fd, DRM_IOCTL_GEM_FLINK, &flink);
+    if (ret == -1) {
+       ERR_DEBUG((fprintf (stderr, "Failed to flink bo: %s\n",
+                           strerror (errno))));
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+    }
+
+    bo->name = flink.name;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+void
+_cairo_drm_bo_close (const cairo_drm_device_t *dev,
+                    cairo_drm_bo_t *bo)
+{
+    struct drm_gem_close close;
+    int ret;
+
+    close.handle = bo->handle;
+    do {
+       ret = ioctl (dev->fd, DRM_IOCTL_GEM_CLOSE, &close);
+    } while (ret == -1 && errno == EINTR);
+}
diff --git a/src/drm/cairo-drm-gallium-surface.c b/src/drm/cairo-drm-gallium-surface.c
new file mode 100644 (file)
index 0000000..b489753
--- /dev/null
@@ -0,0 +1,821 @@
+/* Cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Chris Wilson
+ * Copyright © 2009 Eric Anholt
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Chris Wilson.
+ */
+
+#include "cairoint.h"
+
+#include "cairo-drm-private.h"
+#include "cairo-default-context-private.h"
+#include "cairo-error-private.h"
+
+#include <dlfcn.h>
+
+#include <state_tracker/drm_api.h>
+#include <pipe/p_format.h>
+#include <pipe/p_screen.h>
+#include <pipe/p_context.h>
+#include <pipe/p_state.h>
+
+#include <util/u_inlines.h>
+
+typedef struct _gallium_surface gallium_surface_t;
+typedef struct _gallium_device gallium_device_t;
+
+struct _gallium_device {
+    cairo_drm_device_t drm;
+
+    void *dlhandle;
+    struct drm_api *api;
+
+    struct pipe_screen *screen;
+    struct pipe_context *pipe;
+
+    int max_size;
+};
+
+struct _gallium_surface {
+    cairo_drm_surface_t drm;
+
+    enum pipe_format pipe_format;
+
+    struct pipe_resource *texture;
+    struct pipe_transfer *map_transfer;
+
+    cairo_surface_t *fallback;
+};
+
+static cairo_surface_t *
+gallium_surface_create_internal (gallium_device_t *device,
+                                enum pipe_format format,
+                                int width, int height);
+
+static inline gallium_device_t *
+gallium_device (gallium_surface_t *surface)
+{
+    return (gallium_device_t *) surface->drm.base.device;
+}
+
+static cairo_format_t
+_cairo_format_from_pipe_format (enum pipe_format format)
+{
+    switch ((int) format) {
+    case PIPE_FORMAT_A8_UNORM:
+       return CAIRO_FORMAT_A8;
+    case PIPE_FORMAT_A8R8G8B8_UNORM:
+       return CAIRO_FORMAT_ARGB32;
+    default:
+       return CAIRO_FORMAT_INVALID;
+    }
+}
+
+static enum pipe_format
+pipe_format_from_format (cairo_format_t format)
+{
+    switch ((int) format) {
+    case CAIRO_FORMAT_A8:
+       return PIPE_FORMAT_A8_UNORM;
+    case CAIRO_FORMAT_ARGB32:
+       return PIPE_FORMAT_A8R8G8B8_UNORM;
+    default:
+       return (enum pipe_format) -1;
+    }
+}
+
+static enum pipe_format
+pipe_format_from_content (cairo_content_t content)
+{
+    if (content == CAIRO_CONTENT_ALPHA)
+       return PIPE_FORMAT_A8_UNORM;
+    else
+       return PIPE_FORMAT_A8R8G8B8_UNORM;
+}
+
+static cairo_bool_t
+format_is_supported_destination (gallium_device_t *device,
+                                enum pipe_format format)
+{
+    if (format == (enum pipe_format) -1)
+       return FALSE;
+
+    return device->screen->is_format_supported (device->screen,
+                                               format,
+                                               0,
+                                               PIPE_BIND_RENDER_TARGET,
+                                               0);
+}
+
+#if 0
+static cairo_bool_t
+format_is_supported_source (gallium_device_t *device,
+                           enum pipe_format format)
+{
+    return device->screen->is_format_supported (device->screen,
+                                               format,
+                                               0,
+                                               PIPE_BIND_SAMPLER_VIEW,
+                                               0);
+}
+#endif
+
+static cairo_surface_t *
+gallium_surface_create_similar (void                   *abstract_src,
+                               cairo_content_t          content,
+                               int                      width,
+                               int                      height)
+{
+    gallium_surface_t *other = abstract_src;
+    gallium_device_t *device = gallium_device (other);
+    enum pipe_format pipe_format;
+    cairo_surface_t *surface = NULL;
+    cairo_status_t status;
+
+    status = cairo_device_acquire (&device->drm.base);
+    if (unlikely (status))
+       return _cairo_surface_create_in_error (status);
+
+    if (MAX (width, height) > device->max_size)
+       goto RELEASE;
+
+    if (content == other->drm.base.content)
+       pipe_format = other->pipe_format;
+    else
+       pipe_format = pipe_format_from_content (content);
+
+    if (! format_is_supported_destination (device, pipe_format))
+       goto RELEASE;
+
+    surface = gallium_surface_create_internal (device,
+                                              pipe_format,
+                                              width, height);
+
+RELEASE:
+    cairo_device_release (&device->drm.base);
+
+    return surface;
+}
+
+static cairo_status_t
+gallium_surface_finish (void *abstract_surface)
+{
+    gallium_surface_t *surface = abstract_surface;
+    gallium_device_t *device = gallium_device (surface);
+    cairo_status_t status;
+
+    status = cairo_device_acquire (&device->drm.base);
+    if (likely (status == CAIRO_STATUS_SUCCESS)) {
+       pipe_resource_reference (&surface->texture, NULL);
+       cairo_device_release (&device->drm.base);
+    }
+
+    return _cairo_drm_surface_finish (&surface->drm);
+}
+
+static cairo_surface_t *
+gallium_surface_map_to_image (gallium_surface_t *surface)
+{
+    gallium_device_t *device = gallium_device (surface);
+    cairo_status_t status;
+    void *ptr = NULL;
+
+    status = cairo_device_acquire (&device->drm.base);
+    if (unlikely (status))
+       return _cairo_surface_create_in_error (status);
+
+    surface->map_transfer =
+         pipe_get_transfer (device->pipe,
+                            surface->texture, 0, 0, 0,
+                            PIPE_TRANSFER_MAP_DIRECTLY |
+                            PIPE_TRANSFER_READ_WRITE,
+                            0, 0,
+                            surface->drm.width,
+                            surface->drm.height);
+    if (likely (surface->map_transfer != NULL))
+       ptr = device->pipe->transfer_map (device->pipe, surface->map_transfer);
+
+    cairo_device_release (&device->drm.base);
+
+    if (unlikely (ptr == NULL)) {
+       if (surface->map_transfer != NULL) {
+           device->pipe->transfer_destroy (device->pipe,
+                                           surface->map_transfer);
+           surface->map_transfer = NULL;
+       }
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+    }
+
+    return cairo_image_surface_create_for_data (ptr,
+                                               surface->drm.format,
+                                               surface->drm.width,
+                                               surface->drm.height,
+                                               surface->map_transfer->stride);
+}
+
+static cairo_status_t
+gallium_surface_acquire_source_image (void *abstract_surface,
+                                     cairo_image_surface_t **image_out,
+                                     void **image_extra)
+{
+    gallium_surface_t *surface = abstract_surface;
+    gallium_device_t *device = gallium_device (surface);
+    cairo_format_t format;
+    cairo_surface_t *image;
+    cairo_status_t status;
+    struct pipe_transfer *transfer;
+    void *ptr;
+
+    if (surface->fallback != NULL) {
+       *image_out = (cairo_image_surface_t *)
+           cairo_surface_reference (surface->fallback);
+       *image_extra = NULL;
+       return CAIRO_STATUS_SUCCESS;
+    }
+
+    if (unlikely (surface->drm.width == 0 || surface->drm.height == 0)) {
+       image = cairo_image_surface_create (surface->drm.format, 0, 0);
+       if (unlikely (image->status))
+           return image->status;
+
+       *image_out = (cairo_image_surface_t *) image;
+       *image_extra = NULL;
+       return CAIRO_STATUS_SUCCESS;
+    }
+
+    format = _cairo_format_from_pipe_format (surface->pipe_format);
+    if (format == CAIRO_FORMAT_INVALID)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    status = cairo_device_acquire (&device->drm.base);
+    if (unlikely (status))
+       return status;
+
+    transfer = pipe_get_transfer (device->pipe,
+                                 surface->texture, 0, 0, 0,
+                                 PIPE_TRANSFER_READ,
+                                 0, 0,
+                                 surface->drm.width,
+                                 surface->drm.height);
+    ptr = device->pipe->transfer_map (device->pipe, transfer);
+    cairo_device_release (&device->drm.base);
+
+    image = cairo_image_surface_create_for_data (ptr, format,
+                                                surface->drm.width,
+                                                surface->drm.height,
+                                                surface->drm.stride);
+    if (unlikely (image->status))
+       return image->status;
+
+    *image_out = (cairo_image_surface_t *) image;
+    *image_extra = transfer;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+gallium_surface_release_source_image (void *abstract_surface,
+                                     cairo_image_surface_t *image,
+                                     void *image_extra)
+{
+    cairo_surface_destroy (&image->base);
+
+    if (image_extra != NULL) {
+       gallium_device_t *device = gallium_device (abstract_surface);
+
+       device->pipe->transfer_unmap (device->pipe, image_extra);
+       device->pipe->transfer_destroy (device->pipe, image_extra);
+    }
+}
+
+static cairo_status_t
+gallium_surface_flush (void *abstract_surface)
+{
+    gallium_surface_t *surface = abstract_surface;
+    gallium_device_t *device = gallium_device (surface);
+    cairo_status_t status;
+
+    if (surface->fallback == NULL) {
+       device->pipe->flush (device->pipe,
+                            PIPE_FLUSH_RENDER_CACHE,
+                            NULL);
+       return CAIRO_STATUS_SUCCESS;
+    }
+
+    /* kill any outstanding maps */
+    cairo_surface_finish (surface->fallback);
+
+    status = cairo_device_acquire (&device->drm.base);
+    if (likely (status == CAIRO_STATUS_SUCCESS)) {
+       device->pipe->transfer_unmap (device->pipe,
+                                     surface->map_transfer);
+       device->pipe->transfer_destroy (device->pipe,
+                                       surface->map_transfer);
+       surface->map_transfer = NULL;
+       cairo_device_release (&device->drm.base);
+    }
+
+    status = cairo_surface_status (surface->fallback);
+    cairo_surface_destroy (surface->fallback);
+    surface->fallback = NULL;
+
+    return status;
+}
+
+static cairo_int_status_t
+gallium_surface_paint (void                    *abstract_surface,
+                         cairo_operator_t       op,
+                         const cairo_pattern_t *source,
+                         cairo_clip_t          *clip)
+{
+    gallium_surface_t *surface = abstract_surface;
+
+    if (surface->fallback == NULL) {
+       /* XXX insert magic */
+       surface->fallback = gallium_surface_map_to_image (surface);
+    }
+
+    return _cairo_surface_paint (surface->fallback, op, source, clip);
+}
+
+static cairo_int_status_t
+gallium_surface_mask (void                     *abstract_surface,
+                        cairo_operator_t        op,
+                        const cairo_pattern_t  *source,
+                        const cairo_pattern_t  *mask,
+                        cairo_clip_t           *clip)
+{
+    gallium_surface_t *surface = abstract_surface;
+
+    if (surface->fallback == NULL) {
+       /* XXX insert magic */
+       surface->fallback = gallium_surface_map_to_image (surface);
+    }
+
+    return _cairo_surface_mask (surface->fallback,
+                               op, source, mask,
+                               clip);
+}
+
+static cairo_int_status_t
+gallium_surface_stroke (void                           *abstract_surface,
+                          cairo_operator_t              op,
+                          const cairo_pattern_t        *source,
+                          cairo_path_fixed_t           *path,
+                          const cairo_stroke_style_t   *style,
+                          const cairo_matrix_t         *ctm,
+                          const cairo_matrix_t         *ctm_inverse,
+                          double                        tolerance,
+                          cairo_antialias_t             antialias,
+                          cairo_clip_t                 *clip)
+{
+    gallium_surface_t *surface = abstract_surface;
+
+    if (surface->fallback == NULL) {
+       /* XXX insert magic */
+       surface->fallback = gallium_surface_map_to_image (surface);
+    }
+
+    return _cairo_surface_stroke (surface->fallback,
+                                 op, source,
+                                 path, style,
+                                 ctm, ctm_inverse,
+                                 tolerance, antialias,
+                                 clip);
+}
+
+static cairo_int_status_t
+gallium_surface_fill (void                     *abstract_surface,
+                        cairo_operator_t        op,
+                        const cairo_pattern_t  *source,
+                        cairo_path_fixed_t     *path,
+                        cairo_fill_rule_t       fill_rule,
+                        double                  tolerance,
+                        cairo_antialias_t       antialias,
+                        cairo_clip_t           *clip)
+{
+    gallium_surface_t *surface = abstract_surface;
+
+    if (surface->fallback == NULL) {
+       /* XXX insert magic */
+       surface->fallback = gallium_surface_map_to_image (surface);
+    }
+
+    return _cairo_surface_fill (surface->fallback,
+                               op, source,
+                               path, fill_rule,
+                               tolerance, antialias,
+                               clip);
+}
+
+static cairo_int_status_t
+gallium_surface_glyphs (void                           *abstract_surface,
+                          cairo_operator_t              op,
+                          const cairo_pattern_t        *source,
+                          cairo_glyph_t                *glyphs,
+                          int                           num_glyphs,
+                          cairo_scaled_font_t          *scaled_font,
+                          cairo_clip_t                 *clip,
+                          int *num_remaining)
+{
+    gallium_surface_t *surface = abstract_surface;
+
+    *num_remaining = 0;
+
+    if (surface->fallback == NULL) {
+       /* XXX insert magic */
+       surface->fallback = gallium_surface_map_to_image (surface);
+    }
+
+    return _cairo_surface_show_text_glyphs (surface->fallback,
+                                           op, source,
+                                           NULL, 0,
+                                           glyphs, num_glyphs,
+                                           NULL, 0, 0,
+                                           scaled_font,
+                                           clip);
+}
+
+static const cairo_surface_backend_t gallium_surface_backend = {
+    CAIRO_SURFACE_TYPE_DRM,
+    _cairo_default_context_create,
+
+    gallium_surface_create_similar,
+    gallium_surface_finish,
+
+    gallium_surface_acquire_source_image,
+    gallium_surface_release_source_image,
+
+    NULL, //gallium_surface_acquire_dest_image,
+    NULL, //gallium_surface_release_dest_image,
+    NULL, //gallium_surface_clone_similar,
+    NULL, //gallium_surface_composite,
+    NULL, //gallium_surface_fill_rectangles,
+    NULL, //gallium_surface_composite_trapezoids,
+    NULL, //gallium_surface_create_span_renderer,
+    NULL, //gallium_surface_check_span_renderer,
+    NULL, /* copy_page */
+    NULL, /* show_page */
+    _cairo_drm_surface_get_extents,
+    NULL, /* old_show_glyphs */
+    _cairo_drm_surface_get_font_options,
+    gallium_surface_flush,
+    NULL, /* mark_dirty_rectangle */
+    NULL, //gallium_surface_scaled_font_fini,
+    NULL, //gallium_surface_scaled_glyph_fini,
+
+    gallium_surface_paint,
+    gallium_surface_mask,
+    gallium_surface_stroke,
+    gallium_surface_fill,
+    gallium_surface_glyphs,
+
+    NULL, /* snapshot */
+
+    NULL, /* is_similar */
+
+    NULL, /* reset */
+};
+
+static int
+gallium_format_stride_for_width (enum pipe_format format, int width)
+{
+    int stride;
+
+    stride = 1024; /* XXX fugly */
+    while (stride < width)
+       stride *= 2;
+
+    if (format == PIPE_FORMAT_A8R8G8B8_UNORM)
+       stride *= 4;
+
+    return stride;
+}
+
+static cairo_drm_bo_t *
+_gallium_fake_bo_create (uint32_t size, uint32_t name)
+{
+    cairo_drm_bo_t *bo;
+
+    /* XXX integrate with winsys handle */
+
+    bo = malloc (sizeof (cairo_drm_bo_t));
+
+    CAIRO_REFERENCE_COUNT_INIT (&bo->ref_count, 1);
+    bo->name = name;
+    bo->handle = 0;
+    bo->size = size;
+
+    return bo;
+}
+
+static void
+_gallium_fake_bo_release (void *dev, void *bo)
+{
+    free (bo);
+}
+
+static cairo_surface_t *
+gallium_surface_create_internal (gallium_device_t *device,
+                                enum pipe_format pipe_format,
+                                int width, int height)
+{
+    gallium_surface_t *surface;
+    struct pipe_resource template;
+    cairo_status_t status;
+    cairo_format_t format;
+    int stride, size;
+
+    surface = malloc (sizeof (gallium_surface_t));
+    if (unlikely (surface == NULL))
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+    format = _cairo_format_from_pipe_format (pipe_format);
+    _cairo_surface_init (&surface->drm.base,
+                        &gallium_surface_backend,
+                        &device->drm.base,
+                        _cairo_content_from_format (format));
+    _cairo_drm_surface_init (&surface->drm, format, width, height);
+
+    stride = gallium_format_stride_for_width (pipe_format, width);
+    size = stride * height;
+
+    surface->drm.stride = stride;
+    surface->drm.bo = _gallium_fake_bo_create (size, 0);
+
+    memset(&template, 0, sizeof(template));
+    template.target = PIPE_TEXTURE_2D;
+    template.format = pipe_format;
+    template.width0 = width;
+    template.height0 = height;
+    template.depth0 = 1;
+    template.last_level = 0;
+    template.bind = PIPE_BIND_RENDER_TARGET;
+    surface->texture = device->screen->resource_create (device->screen,
+                                                       &template);
+
+    if (unlikely (surface->texture == NULL)) {
+       status = _cairo_drm_surface_finish (&surface->drm);
+       free (surface);
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+    }
+
+    surface->pipe_format = pipe_format;
+    surface->texture = NULL;
+
+    return &surface->drm.base;
+}
+
+static cairo_surface_t *
+gallium_surface_create (cairo_drm_device_t *base_dev,
+                       cairo_format_t format,
+                       int width, int height)
+{
+    gallium_device_t *device = (gallium_device_t *) base_dev;
+    cairo_surface_t *surface;
+    enum pipe_format pipe_format;
+    cairo_status_t status;
+
+    status = cairo_device_acquire (&device->drm.base);
+
+    if (MAX (width, height) > device->max_size) {
+       surface = _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE));
+       goto RELEASE;
+    }
+
+    pipe_format = pipe_format_from_format (format);
+    if (! format_is_supported_destination (device, pipe_format)) {
+       surface = _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
+       goto RELEASE;
+    }
+
+    surface = gallium_surface_create_internal (device,
+                                              pipe_format,
+                                              width, height);
+
+RELEASE:
+    cairo_device_release (&device->drm.base);
+
+    return surface;
+}
+
+#if 0
+static cairo_surface_t *
+gallium_surface_create_for_name (cairo_drm_device_t *base_dev,
+                                unsigned int name,
+                                cairo_format_t format,
+                                int width, int height, int stride)
+{
+    gallium_device_t *device;
+    gallium_surface_t *surface;
+    cairo_status_t status;
+    cairo_content_t content;
+
+    surface = malloc (sizeof (gallium_surface_t));
+    if (unlikely (surface == NULL))
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+    switch (format) {
+    default:
+    case CAIRO_FORMAT_INVALID:
+    case CAIRO_FORMAT_A1:
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
+    case CAIRO_FORMAT_A8:
+       surface->pipe_format = PIPE_FORMAT_A8_UNORM;
+       break;
+    case CAIRO_FORMAT_RGB24:
+    case CAIRO_FORMAT_ARGB32:
+       surface->pipe_format = PIPE_FORMAT_A8R8G8B8_UNORM;
+       break;
+    }
+
+    status = cairo_device_acquire (&device->drm.base);
+
+    if (MAX (width, height) > device->max_size) {
+       cairo_device_release (&device->drm.base);
+       free (surface);
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE));
+    }
+
+    if (! format_is_supported_destination (device, surface->pipe_format)) {
+       cairo_device_release (&device->drm.base);
+       free (surface);
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
+    }
+
+    content = _cairo_content_from_format (format);
+    _cairo_surface_init (&surface->drm.base,
+                        &gallium_surface_backend,
+                        content);
+    _cairo_drm_surface_init (&surface->drm, base_dev);
+
+    surface->drm.bo = _gallium_fake_bo_create (height * stride, name);
+
+    surface->drm.width  = width;
+    surface->drm.height = height;
+    surface->drm.stride = stride;
+
+#if 0
+    /* XXX screen->create_from_handle */
+    surface->buffer = device->api->buffer_from_handle (device->api,
+                                                      device->screen,
+                                                      "cairo-gallium alien",
+                                                      name);
+    if (unlikely (surface->buffer == NULL)) {
+       status = _cairo_drm_surface_finish (&surface->drm);
+       cairo_device_release (&device->drm.base);
+       free (surface);
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+    }
+#endif
+
+    surface->texture = NULL;
+
+    surface->fallback = NULL;
+
+    cairo_device_release (&device->drm.base);
+
+    return &surface->drm.base;
+}
+
+static cairo_int_status_t
+gallium_surface_flink (void *abstract_surface)
+{
+    gallium_surface_t *surface = abstract_surface;
+    gallium_device_t *device;
+    cairo_status_t status = CAIRO_STATUS_SUCCESS;
+
+    status = cairo_device_acquire (&device->drm.base);
+    if (! device->api->global_handle_from_buffer (device->api,
+                                                 device->screen,
+                                                 surface->buffer,
+                                                 &surface->drm.bo->name))
+    {
+       status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+    }
+    cairo_device_release (&device->drm.base);
+
+    return status;
+}
+#endif
+
+static void
+gallium_device_destroy (void *abstract_device)
+{
+    gallium_device_t *device = abstract_device;
+
+    device->pipe->destroy (device->pipe);
+    device->screen->destroy (device->screen);
+    device->api->destroy (device->api);
+
+    dlclose (device->dlhandle);
+    free (device);
+}
+
+cairo_drm_device_t *
+_cairo_drm_gallium_device_create (int fd, dev_t dev, int vendor_id, int chip_id)
+{
+    gallium_device_t *device;
+    cairo_status_t status;
+    void *handle;
+    const char *libdir;
+    char buf[4096];
+    struct drm_api *(*ctor) (void);
+
+    /* XXX need search path + probe */
+    libdir = getenv ("CAIRO_GALLIUM_LIBDIR");
+    if (libdir == NULL)
+       libdir = "/usr/lib/dri";
+    buf[snprintf (buf, sizeof (buf)-1, "%s/i915_dri.so", libdir)] = '\0';
+
+    handle = dlopen (buf, RTLD_LAZY);
+    if (handle == NULL)
+       return NULL;
+
+    ctor = dlsym (handle, "drm_api_create");
+    if (ctor == NULL) {
+       dlclose (handle);
+       return NULL;
+    }
+
+    device = malloc (sizeof (gallium_device_t));
+    if (device == NULL) {
+       dlclose (handle);
+       return _cairo_drm_device_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+    }
+
+    device->dlhandle = handle;
+
+    device->drm.surface.create = gallium_surface_create;
+    device->drm.surface.create_for_name = NULL;
+    //device->drm.surface.create_for_name = gallium_surface_create_for_name;
+    device->drm.surface.enable_scan_out = NULL;
+    //device->drm.surface.flink = gallium_surface_flink;
+    device->drm.surface.flink = NULL;
+
+    device->drm.device.flush = NULL;
+    device->drm.device.throttle = NULL;
+    device->drm.device.destroy = gallium_device_destroy;
+
+    device->drm.bo.release = _gallium_fake_bo_release;
+
+    device->api = ctor ();
+    if (device->api == NULL) {
+       status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+       goto CLEANUP;
+    }
+
+    device->screen = device->api->create_screen (device->api, fd, NULL);
+    if (device->screen == NULL) {
+       status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+       goto CLEANUP_API;
+    }
+
+    device->max_size = 1 << device->screen->get_param (device->screen,
+                                                      PIPE_CAP_MAX_TEXTURE_2D_LEVELS);
+
+    device->pipe = device->screen->context_create (device->screen, device);
+    if (device->pipe == NULL) {
+       status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+       goto CLEANUP_SCREEN;
+    }
+
+    return _cairo_drm_device_init (&device->drm,
+                                  fd, dev,
+                                  0, 0,
+                                  device->max_size);
+
+CLEANUP_SCREEN:
+    device->screen->destroy (device->screen);
+CLEANUP_API:
+    device->api->destroy (device->api);
+CLEANUP:
+    free (device);
+    dlclose (handle);
+    return _cairo_drm_device_create_in_error (status);
+}
diff --git a/src/drm/cairo-drm-i915-glyphs.c b/src/drm/cairo-drm-i915-glyphs.c
new file mode 100644 (file)
index 0000000..9944f15
--- /dev/null
@@ -0,0 +1,563 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-composite-rectangles-private.h"
+#include "cairo-drm-i915-private.h"
+#include "cairo-error-private.h"
+#include "cairo-rtree-private.h"
+
+static void
+i915_emit_glyph_rectangle_zero (i915_device_t *device,
+                               i915_shader_t *shader,
+                               int x1, int y1,
+                               int x2, int y2,
+                               intel_glyph_t *glyph)
+{
+    float *v;
+
+    /* Each vertex is:
+     *   2 vertex coordinates
+     */
+
+    v = i915_add_rectangle (device);
+    *v++ = x2; *v++ = y2;
+    *v++ = x1; *v++ = y2;
+    *v++ = x1; *v++ = y1;
+}
+
+static void
+i915_emit_glyph_rectangle_constant (i915_device_t *device,
+                                   i915_shader_t *shader,
+                                   int x1, int y1,
+                                   int x2, int y2,
+                                   intel_glyph_t *glyph)
+{
+    float *v;
+
+    /* Each vertex is:
+     *   2 vertex coordinates
+     *   2 glyph texture coordinates
+     */
+
+    v = i915_add_rectangle (device);
+
+    /* bottom right */
+    *v++ = x2; *v++ = y2;
+    *v++ = glyph->texcoord[0];
+
+    /* bottom left */
+    *v++ = x1; *v++ = y2;
+    *v++ = glyph->texcoord[1];
+
+    /* top left */
+    *v++ = x1; *v++ = y1;
+    *v++ = glyph->texcoord[2];
+}
+
+static void
+i915_emit_glyph_rectangle_general (i915_device_t *device,
+                                  i915_shader_t *shader,
+                                  int x1, int y1,
+                                  int x2, int y2,
+                                  intel_glyph_t *glyph)
+{
+    double s, t;
+    float *v;
+
+    /* Each vertex is:
+     *   2 vertex coordinates
+     *   [0-2] source texture coordinates
+     *   2 glyph texture coordinates
+     */
+
+    v = i915_add_rectangle (device);
+
+    /* bottom right */
+    *v++ = x2; *v++ = y2;
+    s = x2, t = y2;
+    switch (shader->source.type.vertex) {
+    case VS_ZERO:
+    case VS_CONSTANT:
+       break;
+    case VS_LINEAR:
+       *v++ = i915_shader_linear_texcoord (&shader->source.linear, s, t);
+       break;
+    case VS_TEXTURE:
+       cairo_matrix_transform_point (&shader->source.base.matrix, &s, &t);
+       *v++ = s; *v++ = t;
+       break;
+    case VS_TEXTURE_16:
+       cairo_matrix_transform_point (&shader->source.base.matrix, &s, &t);
+       *v++ = texcoord_2d_16 (s, t);
+       break;
+    }
+    *v++ = glyph->texcoord[0];
+
+    /* bottom left */
+    *v++ = x1; *v++ = y2;
+    s = x1, t = y2;
+    switch (shader->source.type.vertex) {
+    case VS_ZERO:
+    case VS_CONSTANT:
+       break;
+    case VS_LINEAR:
+       *v++ = i915_shader_linear_texcoord (&shader->source.linear, s, t);
+       break;
+    case VS_TEXTURE:
+       cairo_matrix_transform_point (&shader->source.base.matrix, &s, &t);
+       *v++ = s; *v++ = t;
+       break;
+    case VS_TEXTURE_16:
+       cairo_matrix_transform_point (&shader->source.base.matrix, &s, &t);
+       *v++ = texcoord_2d_16 (s, t);
+       break;
+    }
+    *v++ = glyph->texcoord[1];
+
+    /* top left */
+    *v++ = x1; *v++ = y1;
+    s = x1, t = y2;
+    switch (shader->source.type.vertex) {
+    case VS_ZERO:
+    case VS_CONSTANT:
+       break;
+    case VS_LINEAR:
+       *v++ = i915_shader_linear_texcoord (&shader->source.linear, s, t);
+       break;
+    case VS_TEXTURE:
+       cairo_matrix_transform_point (&shader->source.base.matrix, &s, &t);
+       *v++ = s; *v++ = t;
+       break;
+    case VS_TEXTURE_16:
+       cairo_matrix_transform_point (&shader->source.base.matrix, &s, &t);
+       *v++ = texcoord_2d_16 (s, t);
+       break;
+    }
+    *v++ = glyph->texcoord[2];
+}
+
+typedef void
+(*i915_emit_glyph_rectangle_func_t) (i915_device_t *device,
+                                    i915_shader_t *shader,
+                                    int x1, int y1,
+                                    int x2, int y2,
+                                    intel_glyph_t *glyph);
+
+static cairo_status_t
+i915_surface_mask_internal (i915_surface_t *dst,
+                           cairo_operator_t             op,
+                           const cairo_pattern_t       *source,
+                           i915_surface_t *mask,
+                           cairo_clip_t                *clip,
+                           const cairo_composite_rectangles_t *extents)
+{
+    i915_device_t *device;
+    i915_shader_t shader;
+    cairo_region_t *clip_region = NULL;
+    cairo_status_t status;
+
+    i915_shader_init (&shader, dst, op, 1.);
+
+    status = i915_shader_acquire_pattern (&shader, &shader.source,
+                                         source, &extents->bounded);
+    if (unlikely (status))
+       return status;
+
+    shader.mask.type.vertex = VS_TEXTURE_16;
+    shader.mask.type.pattern = PATTERN_TEXTURE;
+    shader.mask.type.fragment = FS_TEXTURE;
+    shader.mask.base.content = mask->intel.drm.base.content;
+    shader.mask.base.texfmt = TEXCOORDFMT_2D_16;
+    shader.mask.base.n_samplers = 1;
+    shader.mask.base.sampler[0] =
+       (MIPFILTER_NONE << SS2_MIP_FILTER_SHIFT) |
+       i915_texture_filter (CAIRO_FILTER_NEAREST);
+    shader.mask.base.sampler[1] =
+       SS3_NORMALIZED_COORDS |
+       i915_texture_extend (CAIRO_EXTEND_NONE);
+
+    cairo_matrix_init_translate (&shader.mask.base.matrix,
+                                -extents->bounded.x,
+                                -extents->bounded.y);
+    cairo_matrix_scale (&shader.mask.base.matrix,
+                       1. / mask->intel.drm.width,
+                       1. / mask->intel.drm.height);
+
+    shader.mask.base.bo = intel_bo_reference (to_intel_bo (mask->intel.drm.bo));
+    shader.mask.base.offset[0] = 0;
+    shader.mask.base.map[0] = mask->map0;
+    shader.mask.base.map[1] = mask->map1;
+
+    if (clip != NULL) {
+       status = _cairo_clip_get_region (clip, &clip_region);
+
+       if (clip_region != NULL && cairo_region_num_rectangles (clip_region) == 1)
+           clip_region = NULL;
+
+       if (status == CAIRO_INT_STATUS_UNSUPPORTED)
+           i915_shader_set_clip (&shader, clip);
+    }
+
+    status = cairo_device_acquire (dst->intel.drm.base.device);
+    if (unlikely (status))
+       goto CLEANUP_SHADER;
+
+    device = i915_device (dst);
+
+    status = i915_shader_commit (&shader, device);
+    if (unlikely (status))
+       goto CLEANUP_DEVICE;
+
+    if (clip_region != NULL) {
+       unsigned int n, num_rectangles;
+
+       num_rectangles = cairo_region_num_rectangles (clip_region);
+       for (n = 0; n < num_rectangles; n++) {
+           cairo_rectangle_int_t rect;
+
+           cairo_region_get_rectangle (clip_region, n, &rect);
+
+           shader.add_rectangle (&shader,
+                                 rect.x, rect.y,
+                                 rect.x + rect.width, rect.y + rect.height);
+       }
+    } else {
+       shader.add_rectangle (&shader,
+                             extents->bounded.x, extents->bounded.y,
+                             extents->bounded.x + extents->bounded.width,
+                             extents->bounded.y + extents->bounded.height);
+    }
+
+    if (! extents->is_bounded)
+       status = i915_fixup_unbounded (dst, extents, clip);
+
+CLEANUP_DEVICE:
+    cairo_device_release (&device->intel.base.base);
+CLEANUP_SHADER:
+    i915_shader_fini (&shader);
+    return status;
+}
+
+cairo_int_status_t
+i915_surface_glyphs (void                      *abstract_surface,
+                    cairo_operator_t            op,
+                    const cairo_pattern_t      *source,
+                    cairo_glyph_t              *glyphs,
+                    int                         num_glyphs,
+                    cairo_scaled_font_t        *scaled_font,
+                    cairo_clip_t               *clip,
+                    int *num_remaining)
+{
+    i915_surface_t *surface = abstract_surface;
+    i915_surface_t *mask = NULL;
+    i915_device_t *device;
+    i915_shader_t shader;
+    cairo_composite_rectangles_t extents;
+    cairo_clip_t local_clip;
+    cairo_bool_t have_clip = FALSE;
+    cairo_bool_t overlap;
+    cairo_region_t *clip_region = NULL;
+    intel_bo_t *last_bo = NULL;
+    i915_emit_glyph_rectangle_func_t emit_func;
+    cairo_scaled_glyph_t *glyph_cache[64];
+    cairo_status_t status;
+    int mask_x = 0, mask_y = 0;
+    int i = 0;
+
+    *num_remaining = 0;
+    status = _cairo_composite_rectangles_init_for_glyphs (&extents,
+                                                         surface->intel.drm.width,
+                                                         surface->intel.drm.height,
+                                                         op, source,
+                                                         scaled_font,
+                                                         glyphs, num_glyphs,
+                                                         clip,
+                                                         &overlap);
+    if (unlikely (status))
+       return status;
+
+    if (_cairo_clip_contains_rectangle (clip, &extents.mask))
+       clip = NULL;
+
+    if (clip != NULL && extents.is_bounded) {
+       clip = _cairo_clip_init_copy (&local_clip, clip);
+       status = _cairo_clip_rectangle (clip, &extents.bounded);
+       if (unlikely (status))
+           return status;
+
+       have_clip = TRUE;
+    }
+
+    if (clip != NULL) {
+       status = _cairo_clip_get_region (clip, &clip_region);
+       if (unlikely (_cairo_status_is_error (status) ||
+                     status == CAIRO_INT_STATUS_NOTHING_TO_DO))
+       {
+           if (have_clip)
+               _cairo_clip_fini (&local_clip);
+           return status;
+       }
+    }
+
+    if (i915_surface_needs_tiling (surface)) {
+       ASSERT_NOT_REACHED;
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+    }
+
+    if (overlap || ! extents.is_bounded) {
+       cairo_format_t format;
+
+       format = CAIRO_FORMAT_A8;
+       if (scaled_font->options.antialias == CAIRO_ANTIALIAS_SUBPIXEL)
+           format = CAIRO_FORMAT_ARGB32;
+
+       mask = (i915_surface_t *)
+           i915_surface_create_internal (&i915_device (surface)->intel.base,
+                                         format,
+                                         extents.bounded.width,
+                                         extents.bounded.height,
+                                         I915_TILING_DEFAULT,
+                                         TRUE);
+       if (unlikely (mask->intel.drm.base.status))
+           return mask->intel.drm.base.status;
+
+       status = i915_surface_clear (mask);
+       if (unlikely (status)) {
+           cairo_surface_destroy (&mask->intel.drm.base);
+           return status;
+       }
+
+       i915_shader_init (&shader, mask, CAIRO_OPERATOR_ADD, 1.);
+
+       status = i915_shader_acquire_pattern (&shader, &shader.source,
+                                             &_cairo_pattern_white.base,
+                                             &extents.bounded);
+       if (unlikely (status)) {
+           cairo_surface_destroy (&mask->intel.drm.base);
+           return status;
+       }
+
+       mask_x = -extents.bounded.x;
+       mask_y = -extents.bounded.y;
+    } else {
+       i915_shader_init (&shader, surface, op, 1.);
+
+       status = i915_shader_acquire_pattern (&shader, &shader.source,
+                                             source, &extents.bounded);
+       if (unlikely (status))
+           return status;
+
+       if (clip != NULL) {
+           status = _cairo_clip_get_region (clip, &clip_region);
+
+           if (clip_region != NULL && cairo_region_num_rectangles (clip_region) == 1)
+               clip_region = NULL;
+
+           if (status == CAIRO_INT_STATUS_UNSUPPORTED)
+               i915_shader_set_clip (&shader, clip);
+       }
+    }
+
+    shader.mask.type.fragment = FS_TEXTURE;
+    shader.mask.base.content = CAIRO_CONTENT_ALPHA; /* XXX */
+    shader.mask.base.texfmt = TEXCOORDFMT_2D_16;
+    shader.mask.base.n_samplers = 1;
+    shader.mask.base.sampler[0] =
+       (MIPFILTER_NONE << SS2_MIP_FILTER_SHIFT) |
+       i915_texture_filter (CAIRO_FILTER_NEAREST);
+    shader.mask.base.sampler[1] =
+       SS3_NORMALIZED_COORDS |
+       i915_texture_extend (CAIRO_EXTEND_NONE);
+
+    switch (shader.source.type.vertex) {
+    case VS_ZERO:
+       emit_func = i915_emit_glyph_rectangle_zero;
+       break;
+    case VS_CONSTANT:
+       emit_func = i915_emit_glyph_rectangle_constant;
+       break;
+    default:
+    case VS_LINEAR:
+    case VS_TEXTURE:
+    case VS_TEXTURE_16:
+       emit_func = i915_emit_glyph_rectangle_general;
+       break;
+    }
+
+    status = cairo_device_acquire (surface->intel.drm.base.device);
+    if (unlikely (status))
+       goto CLEANUP_SHADER;
+
+    device = i915_device (surface);
+
+    _cairo_scaled_font_freeze_cache (scaled_font);
+    if (scaled_font->surface_private == NULL) {
+       scaled_font->surface_private = device;
+       scaled_font->surface_backend = surface->intel.drm.base.backend;
+       cairo_list_add (&scaled_font->link, &device->intel.fonts);
+    }
+
+    memset (glyph_cache, 0, sizeof (glyph_cache));
+
+    for (i = 0; i < num_glyphs; i++) {
+       cairo_scaled_glyph_t *scaled_glyph;
+       int x, y, x1, x2, y1, y2;
+       int cache_index = glyphs[i].index % ARRAY_LENGTH (glyph_cache);
+       intel_glyph_t *glyph;
+
+       scaled_glyph = glyph_cache[cache_index];
+       if (scaled_glyph == NULL ||
+           _cairo_scaled_glyph_index (scaled_glyph) != glyphs[i].index)
+       {
+           status = _cairo_scaled_glyph_lookup (scaled_font,
+                                                glyphs[i].index,
+                                                CAIRO_SCALED_GLYPH_INFO_METRICS,
+                                                &scaled_glyph);
+           if (unlikely (status))
+               goto FINISH;
+
+           glyph_cache[cache_index] = scaled_glyph;
+       }
+
+       if (unlikely (scaled_glyph->metrics.width  == 0 ||
+                     scaled_glyph->metrics.height == 0))
+       {
+           continue;
+       }
+
+       /* XXX glyph images are snapped to pixel locations */
+       x = _cairo_lround (glyphs[i].x);
+       y = _cairo_lround (glyphs[i].y);
+
+       x1 = x + _cairo_fixed_integer_floor (scaled_glyph->bbox.p1.x);
+       y1 = y + _cairo_fixed_integer_floor (scaled_glyph->bbox.p1.y);
+       x2 = x + _cairo_fixed_integer_ceil (scaled_glyph->bbox.p2.x);
+       y2 = y + _cairo_fixed_integer_ceil (scaled_glyph->bbox.p2.y);
+
+       if (x2 < extents.bounded.x ||
+           y2 < extents.bounded.y ||
+           x1 > extents.bounded.x + extents.bounded.width ||
+           y1 > extents.bounded.y + extents.bounded.height)
+       {
+           continue;
+       }
+
+       if (scaled_glyph->surface_private == NULL) {
+           status = intel_get_glyph (&device->intel, scaled_font, scaled_glyph);
+           if (unlikely (status == CAIRO_INT_STATUS_NOTHING_TO_DO)) {
+               status = CAIRO_STATUS_SUCCESS;
+               continue;
+           }
+           if (unlikely (status))
+               goto FINISH;
+       }
+
+       glyph = intel_glyph_pin (scaled_glyph->surface_private);
+       if (glyph->cache->buffer.bo != last_bo) {
+           intel_buffer_cache_t *cache = glyph->cache;
+
+           shader.mask.base.bo = cache->buffer.bo;
+           shader.mask.base.offset[0] = cache->buffer.offset;
+           shader.mask.base.map[0] = cache->buffer.map0;
+           shader.mask.base.map[1] = cache->buffer.map1;
+           shader.mask.base.content = CAIRO_CONTENT_ALPHA; /* XXX */
+
+           status = i915_shader_commit (&shader, device);
+           if (unlikely (status))
+               goto FINISH;
+
+           last_bo = cache->buffer.bo;
+       }
+
+       x2 = x1 + glyph->width;
+       y2 = y1 + glyph->height;
+
+       if (mask_x)
+           x1 += mask_x, x2 += mask_x;
+       if (mask_y)
+           y1 += mask_y, y2 += mask_y;
+
+       /* XXX clip glyph */
+       emit_func (device, &shader, x1, y1, x2, y2, glyph);
+    }
+
+    status = CAIRO_STATUS_SUCCESS;
+  FINISH:
+    _cairo_scaled_font_thaw_cache (scaled_font);
+    cairo_device_release (surface->intel.drm.base.device);
+  CLEANUP_SHADER:
+    i915_shader_fini (&shader);
+
+    if (unlikely (status == CAIRO_INT_STATUS_UNSUPPORTED)) {
+       cairo_path_fixed_t path;
+
+       _cairo_path_fixed_init (&path);
+       status = _cairo_scaled_font_glyph_path (scaled_font,
+                                               glyphs + i, num_glyphs - i,
+                                               &path);
+       if (mask_x | mask_y) {
+           _cairo_path_fixed_translate (&path,
+                                        _cairo_fixed_from_int (mask_x),
+                                        _cairo_fixed_from_int (mask_y));
+       }
+       if (likely (status == CAIRO_STATUS_SUCCESS)) {
+           status = surface->intel.drm.base.backend->fill (shader.target,
+                                                           shader.op,
+                                                           mask != NULL ? &_cairo_pattern_white.base : source,
+                                                           &path,
+                                                           CAIRO_FILL_RULE_WINDING,
+                                                           0,
+                                                           scaled_font->options.antialias,
+                                                           clip);
+       }
+       _cairo_path_fixed_fini (&path);
+    }
+
+    if (mask != NULL) {
+       if (likely (status == CAIRO_STATUS_SUCCESS)) {
+           status = i915_surface_mask_internal (surface, op, source, mask,
+                                                clip, &extents);
+       }
+       cairo_surface_finish (&mask->intel.drm.base);
+       cairo_surface_destroy (&mask->intel.drm.base);
+    }
+
+    if (have_clip)
+       _cairo_clip_fini (&local_clip);
+
+    return status;
+}
diff --git a/src/drm/cairo-drm-i915-private.h b/src/drm/cairo-drm-i915-private.h
new file mode 100644 (file)
index 0000000..1581cab
--- /dev/null
@@ -0,0 +1,1270 @@
+/*
+ * Copyright © 2006, 2009 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Authors:
+ *    Eric Anholt <eric@anholt.net>
+ *    Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#ifndef CAIRO_DRM_I915_PRIVATE_H
+#define CAIRO_DRM_I915_PRIVATE_H
+
+#include "cairo-types-private.h"
+
+#include "cairo-drm-private.h"
+#include "cairo-drm-intel-private.h"
+#include "cairo-drm-intel-command-private.h"
+#include "cairo-drm-intel-ioctl-private.h"
+#include "cairo-freelist-private.h"
+
+#include <setjmp.h>
+
+#define I915_VERBOSE 1
+
+#define I915_MAX_TEX_INDIRECT 4
+#define I915_MAX_TEX_INSN     32
+#define I915_MAX_ALU_INSN     64
+#define I915_MAX_DECL_INSN    27
+#define I915_MAX_TEMPORARY    16
+
+/* Each instruction is 3 dwords long, though most don't require all
+ * this space.  Maximum of 123 instructions.  Smaller maxes per insn
+ * type.
+ */
+#define _3DSTATE_PIXEL_SHADER_PROGRAM    (CMD_3D|(0x1d<<24)|(0x5<<16))
+
+#define REG_TYPE_R                 0 /* temporary regs, no need to
+                                     * dcl, must be written before
+                                     * read -- Preserved between
+                                     * phases.
+                                     */
+#define REG_TYPE_T                 1 /* Interpolated values, must be
+                                     * dcl'ed before use.
+                                     *
+                                     * 0..7: texture coord,
+                                     * 8: diffuse spec,
+                                     * 9: specular color,
+                                     * 10: fog parameter in w.
+                                     */
+#define REG_TYPE_CONST             2 /* Restriction: only one const
+                                     * can be referenced per
+                                     * instruction, though it may be
+                                     * selected for multiple inputs.
+                                     * Constants not initialized
+                                     * default to zero.
+                                     */
+#define REG_TYPE_S                 3 /* sampler */
+#define REG_TYPE_OC                4 /* output color (rgba) */
+#define REG_TYPE_OD                5 /* output depth (w), xyz are
+                                     * temporaries.  If not written,
+                                     * interpolated depth is used?
+                                     */
+#define REG_TYPE_U                 6 /* unpreserved temporaries */
+#define REG_TYPE_MASK              0x7
+#define REG_TYPE_SHIFT            4
+#define REG_NR_MASK                0xf
+
+/* REG_TYPE_T:
+ */
+#define T_TEX0     0
+#define T_TEX1     1
+#define T_TEX2     2
+#define T_TEX3     3
+#define T_TEX4     4
+#define T_TEX5     5
+#define T_TEX6     6
+#define T_TEX7     7
+#define T_DIFFUSE  8
+#define T_SPECULAR 9
+#define T_FOG_W    10          /* interpolated fog is in W coord */
+
+/* Arithmetic instructions */
+
+/* .replicate_swizzle == selection and replication of a particular
+ * scalar channel, ie., .xxxx, .yyyy, .zzzz or .wwww
+ */
+#define A0_NOP    (0x0<<24)            /* no operation */
+#define A0_ADD    (0x1<<24)            /* dst = src0 + src1 */
+#define A0_MOV    (0x2<<24)            /* dst = src0 */
+#define A0_MUL    (0x3<<24)            /* dst = src0 * src1 */
+#define A0_MAD    (0x4<<24)            /* dst = src0 * src1 + src2 */
+#define A0_DP2ADD (0x5<<24)            /* dst.xyzw = src0.xy dot src1.xy + src2.replicate_swizzle */
+#define A0_DP3    (0x6<<24)            /* dst.xyzw = src0.xyz dot src1.xyz */
+#define A0_DP4    (0x7<<24)            /* dst.xyzw = src0.xyzw dot src1.xyzw */
+#define A0_FRC    (0x8<<24)            /* dst = src0 - floor(src0) */
+#define A0_RCP    (0x9<<24)            /* dst.xyzw = 1/(src0.replicate_swizzle) */
+#define A0_RSQ    (0xa<<24)            /* dst.xyzw = 1/(sqrt(abs(src0.replicate_swizzle))) */
+#define A0_EXP    (0xb<<24)            /* dst.xyzw = exp2(src0.replicate_swizzle) */
+#define A0_LOG    (0xc<<24)            /* dst.xyzw = log2(abs(src0.replicate_swizzle)) */
+#define A0_CMP    (0xd<<24)            /* dst = (src0 >= 0.0) ? src1 : src2 */
+#define A0_MIN    (0xe<<24)            /* dst = (src0 < src1) ? src0 : src1 */
+#define A0_MAX    (0xf<<24)            /* dst = (src0 >= src1) ? src0 : src1 */
+#define A0_FLR    (0x10<<24)           /* dst = floor(src0) */
+#define A0_MOD    (0x11<<24)           /* dst = src0 fmod 1.0 */
+#define A0_TRC    (0x12<<24)           /* dst = int(src0) */
+#define A0_SGE    (0x13<<24)           /* dst = src0 >= src1 ? 1.0 : 0.0 */
+#define A0_SLT    (0x14<<24)           /* dst = src0 < src1 ? 1.0 : 0.0 */
+#define A0_DEST_SATURATE                 (1<<22)
+#define A0_DEST_TYPE_SHIFT                19
+/* Allow: R, OC, OD, U */
+#define A0_DEST_NR_SHIFT                 14
+/* Allow R: 0..15, OC,OD: 0..0, U: 0..2 */
+#define A0_DEST_CHANNEL_X                (1<<10)
+#define A0_DEST_CHANNEL_Y                (2<<10)
+#define A0_DEST_CHANNEL_Z                (4<<10)
+#define A0_DEST_CHANNEL_W                (8<<10)
+#define A0_DEST_CHANNEL_ALL              (0xf<<10)
+#define A0_DEST_CHANNEL_SHIFT            10
+#define A0_SRC0_TYPE_SHIFT               7
+#define A0_SRC0_NR_SHIFT                 2
+
+#define A0_DEST_CHANNEL_XY              (A0_DEST_CHANNEL_X|A0_DEST_CHANNEL_Y)
+#define A0_DEST_CHANNEL_XYZ             (A0_DEST_CHANNEL_XY|A0_DEST_CHANNEL_Z)
+
+#define SRC_X        0
+#define SRC_Y        1
+#define SRC_Z        2
+#define SRC_W        3
+#define SRC_ZERO     4
+#define SRC_ONE      5
+
+#define A1_SRC0_CHANNEL_X_NEGATE         (1<<31)
+#define A1_SRC0_CHANNEL_X_SHIFT          28
+#define A1_SRC0_CHANNEL_Y_NEGATE         (1<<27)
+#define A1_SRC0_CHANNEL_Y_SHIFT          24
+#define A1_SRC0_CHANNEL_Z_NEGATE         (1<<23)
+#define A1_SRC0_CHANNEL_Z_SHIFT          20
+#define A1_SRC0_CHANNEL_W_NEGATE         (1<<19)
+#define A1_SRC0_CHANNEL_W_SHIFT          16
+#define A1_SRC1_TYPE_SHIFT               13
+#define A1_SRC1_NR_SHIFT                 8
+#define A1_SRC1_CHANNEL_X_NEGATE         (1<<7)
+#define A1_SRC1_CHANNEL_X_SHIFT          4
+#define A1_SRC1_CHANNEL_Y_NEGATE         (1<<3)
+#define A1_SRC1_CHANNEL_Y_SHIFT          0
+
+#define A2_SRC1_CHANNEL_Z_NEGATE         (1<<31)
+#define A2_SRC1_CHANNEL_Z_SHIFT          28
+#define A2_SRC1_CHANNEL_W_NEGATE         (1<<27)
+#define A2_SRC1_CHANNEL_W_SHIFT          24
+#define A2_SRC2_TYPE_SHIFT               21
+#define A2_SRC2_NR_SHIFT                 16
+#define A2_SRC2_CHANNEL_X_NEGATE         (1<<15)
+#define A2_SRC2_CHANNEL_X_SHIFT          12
+#define A2_SRC2_CHANNEL_Y_NEGATE         (1<<11)
+#define A2_SRC2_CHANNEL_Y_SHIFT          8
+#define A2_SRC2_CHANNEL_Z_NEGATE         (1<<7)
+#define A2_SRC2_CHANNEL_Z_SHIFT          4
+#define A2_SRC2_CHANNEL_W_NEGATE         (1<<3)
+#define A2_SRC2_CHANNEL_W_SHIFT          0
+
+/* Texture instructions */
+#define T0_TEXLD     (0x15<<24)        /* Sample texture using predeclared
+                                * sampler and address, and output
+                                * filtered texel data to destination
+                                * register */
+#define T0_TEXLDP    (0x16<<24)        /* Same as texld but performs a
+                                * perspective divide of the texture
+                                * coordinate .xyz values by .w before
+                                * sampling. */
+#define T0_TEXLDB    (0x17<<24)        /* Same as texld but biases the
+                                * computed LOD by w.  Only S4.6 two's
+                                * comp is used.  This implies that a
+                                * float to fixed conversion is
+                                * done. */
+#define T0_TEXKILL   (0x18<<24)        /* Does not perform a sampling
+                                * operation.  Simply kills the pixel
+                                * if any channel of the address
+                                * register is < 0.0. */
+#define T0_DEST_TYPE_SHIFT                19
+/* Allow: R, OC, OD, U */
+/* Note: U (unpreserved) regs do not retain their values between
+ * phases (cannot be used for feedback)
+ *
+ * Note: oC and OD registers can only be used as the destination of a
+ * texture instruction once per phase (this is an implementation
+ * restriction).
+ */
+#define T0_DEST_NR_SHIFT                 14
+/* Allow R: 0..15, OC,OD: 0..0, U: 0..2 */
+#define T0_SAMPLER_NR_SHIFT              0 /* This field ignored for TEXKILL */
+#define T0_SAMPLER_NR_MASK               (0xf<<0)
+
+#define T1_ADDRESS_REG_TYPE_SHIFT        24 /* Reg to use as texture coord */
+/* Allow R, T, OC, OD -- R, OC, OD are 'dependent' reads, new program phase */
+#define T1_ADDRESS_REG_NR_SHIFT          17
+#define T2_MBZ                           0
+
+/* Declaration instructions */
+#define D0_DCL       (0x19<<24)        /* Declare a t (interpolated attrib)
+                                * register or an s (sampler)
+                                * register. */
+#define D0_SAMPLE_TYPE_SHIFT              22
+#define D0_SAMPLE_TYPE_2D                 (0x0<<22)
+#define D0_SAMPLE_TYPE_CUBE               (0x1<<22)
+#define D0_SAMPLE_TYPE_VOLUME             (0x2<<22)
+#define D0_SAMPLE_TYPE_MASK               (0x3<<22)
+
+#define D0_TYPE_SHIFT                19
+/* Allow: T, S */
+#define D0_NR_SHIFT                  14
+/* Allow T: 0..10, S: 0..15 */
+#define D0_CHANNEL_X                (1<<10)
+#define D0_CHANNEL_Y                (2<<10)
+#define D0_CHANNEL_Z                (4<<10)
+#define D0_CHANNEL_W                (8<<10)
+#define D0_CHANNEL_ALL              (0xf<<10)
+#define D0_CHANNEL_NONE             (0<<10)
+
+#define D0_CHANNEL_XY               (D0_CHANNEL_X|D0_CHANNEL_Y)
+#define D0_CHANNEL_XYZ              (D0_CHANNEL_XY|D0_CHANNEL_Z)
+
+/* I915 Errata: Do not allow (xz), (xw), (xzw) combinations for diffuse
+ * or specular declarations.
+ *
+ * For T dcls, only allow: (x), (xy), (xyz), (w), (xyzw)
+ *
+ * Must be zero for S (sampler) dcls
+ */
+#define D1_MBZ                          0
+#define D2_MBZ                          0
+
+
+/* MASK_* are the unshifted bitmasks of the destination mask in arithmetic
+ * operations
+ */
+#define MASK_X                 0x1
+#define MASK_Y                 0x2
+#define MASK_Z                 0x4
+#define MASK_W                 0x8
+#define MASK_XYZ               (MASK_X | MASK_Y | MASK_Z)
+#define MASK_XYZW              (MASK_XYZ | MASK_W)
+#define MASK_SATURATE          0x10
+
+/* Temporary, undeclared regs. Preserved between phases */
+#define FS_R0                  ((REG_TYPE_R << REG_TYPE_SHIFT) | 0)
+#define FS_R1                  ((REG_TYPE_R << REG_TYPE_SHIFT) | 1)
+#define FS_R2                  ((REG_TYPE_R << REG_TYPE_SHIFT) | 2)
+#define FS_R3                  ((REG_TYPE_R << REG_TYPE_SHIFT) | 3)
+
+/* Texture coordinate regs.  Must be declared. */
+#define FS_T0                  ((REG_TYPE_T << REG_TYPE_SHIFT) | 0)
+#define FS_T1                  ((REG_TYPE_T << REG_TYPE_SHIFT) | 1)
+#define FS_T2                  ((REG_TYPE_T << REG_TYPE_SHIFT) | 2)
+#define FS_T3                  ((REG_TYPE_T << REG_TYPE_SHIFT) | 3)
+#define FS_T4                  ((REG_TYPE_T << REG_TYPE_SHIFT) | 4)
+#define FS_T5                  ((REG_TYPE_T << REG_TYPE_SHIFT) | 5)
+#define FS_T6                  ((REG_TYPE_T << REG_TYPE_SHIFT) | 6)
+#define FS_T7                  ((REG_TYPE_T << REG_TYPE_SHIFT) | 7)
+#define FS_T8                  ((REG_TYPE_T << REG_TYPE_SHIFT) | 8)
+#define FS_T9                  ((REG_TYPE_T << REG_TYPE_SHIFT) | 9)
+#define FS_T10                 ((REG_TYPE_T << REG_TYPE_SHIFT) | 10)
+
+/* Constant values */
+#define FS_C0                  ((REG_TYPE_CONST << REG_TYPE_SHIFT) | 0)
+#define FS_C1                  ((REG_TYPE_CONST << REG_TYPE_SHIFT) | 1)
+#define FS_C2                  ((REG_TYPE_CONST << REG_TYPE_SHIFT) | 2)
+#define FS_C3                  ((REG_TYPE_CONST << REG_TYPE_SHIFT) | 3)
+#define FS_C4                  ((REG_TYPE_CONST << REG_TYPE_SHIFT) | 4)
+#define FS_C5                  ((REG_TYPE_CONST << REG_TYPE_SHIFT) | 5)
+#define FS_C6                  ((REG_TYPE_CONST << REG_TYPE_SHIFT) | 6)
+#define FS_C7                  ((REG_TYPE_CONST << REG_TYPE_SHIFT) | 7)
+
+/* Sampler regs */
+#define FS_S0                  ((REG_TYPE_S << REG_TYPE_SHIFT) | 0)
+#define FS_S1                  ((REG_TYPE_S << REG_TYPE_SHIFT) | 1)
+#define FS_S2                  ((REG_TYPE_S << REG_TYPE_SHIFT) | 2)
+#define FS_S3                  ((REG_TYPE_S << REG_TYPE_SHIFT) | 3)
+
+/* Output color */
+#define FS_OC                  ((REG_TYPE_OC << REG_TYPE_SHIFT) | 0)
+
+/* Output depth */
+#define FS_OD                  ((REG_TYPE_OD << REG_TYPE_SHIFT) | 0)
+
+/* Unpreserved temporary regs */
+#define FS_U0                  ((REG_TYPE_U << REG_TYPE_SHIFT) | 0)
+#define FS_U1                  ((REG_TYPE_U << REG_TYPE_SHIFT) | 1)
+#define FS_U2                  ((REG_TYPE_U << REG_TYPE_SHIFT) | 2)
+#define FS_U3                  ((REG_TYPE_U << REG_TYPE_SHIFT) | 3)
+
+#define X_CHANNEL_SHIFT (REG_TYPE_SHIFT + 3)
+#define Y_CHANNEL_SHIFT (X_CHANNEL_SHIFT + 4)
+#define Z_CHANNEL_SHIFT (Y_CHANNEL_SHIFT + 4)
+#define W_CHANNEL_SHIFT (Z_CHANNEL_SHIFT + 4)
+
+#define REG_CHANNEL_MASK 0xf
+
+#define REG_NR(reg)            ((reg) & REG_NR_MASK)
+#define REG_TYPE(reg)          (((reg) >> REG_TYPE_SHIFT) & REG_TYPE_MASK)
+#define REG_X(reg)             (((reg) >> X_CHANNEL_SHIFT) & REG_CHANNEL_MASK)
+#define REG_Y(reg)             (((reg) >> Y_CHANNEL_SHIFT) & REG_CHANNEL_MASK)
+#define REG_Z(reg)             (((reg) >> Z_CHANNEL_SHIFT) & REG_CHANNEL_MASK)
+#define REG_W(reg)             (((reg) >> W_CHANNEL_SHIFT) & REG_CHANNEL_MASK)
+
+enum i915_fs_channel {
+    X_CHANNEL_VAL = 0,
+    Y_CHANNEL_VAL,
+    Z_CHANNEL_VAL,
+    W_CHANNEL_VAL,
+    ZERO_CHANNEL_VAL,
+    ONE_CHANNEL_VAL,
+
+    NEG_X_CHANNEL_VAL = X_CHANNEL_VAL | 0x8,
+    NEG_Y_CHANNEL_VAL = Y_CHANNEL_VAL | 0x8,
+    NEG_Z_CHANNEL_VAL = Z_CHANNEL_VAL | 0x8,
+    NEG_W_CHANNEL_VAL = W_CHANNEL_VAL | 0x8,
+    NEG_ONE_CHANNEL_VAL = ONE_CHANNEL_VAL | 0x8
+};
+
+#define i915_fs_operand(reg, x, y, z, w) \
+    (reg) | \
+    (x##_CHANNEL_VAL << X_CHANNEL_SHIFT) | \
+    (y##_CHANNEL_VAL << Y_CHANNEL_SHIFT) | \
+    (z##_CHANNEL_VAL << Z_CHANNEL_SHIFT) | \
+    (w##_CHANNEL_VAL << W_CHANNEL_SHIFT)
+
+/**
+ * Construct an operand description for using a register with no swizzling
+ */
+#define i915_fs_operand_reg(reg)                                       \
+    i915_fs_operand(reg, X, Y, Z, W)
+
+#define i915_fs_operand_reg_negate(reg)                                        \
+    i915_fs_operand(reg, NEG_X, NEG_Y, NEG_Z, NEG_W)
+
+/**
+ * Returns an operand containing (0.0, 0.0, 0.0, 0.0).
+ */
+#define i915_fs_operand_zero() i915_fs_operand(FS_R0, ZERO, ZERO, ZERO, ZERO)
+
+/**
+ * Returns an unused operand
+ */
+#define i915_fs_operand_none() i915_fs_operand_zero()
+
+/**
+ * Returns an operand containing (1.0, 1.0, 1.0, 1.0).
+ */
+#define i915_fs_operand_one() i915_fs_operand(FS_R0, ONE, ONE, ONE, ONE)
+
+#define i915_get_hardware_channel_val(val, shift, negate) \
+    (((val & 0x7) << shift) | ((val & 0x8) ? negate : 0))
+
+/**
+ * Outputs a fragment shader command to declare a sampler or texture register.
+ */
+#define i915_fs_dcl(reg)                                               \
+do {                                                                   \
+    OUT_DWORD (D0_DCL | \
+              (REG_TYPE(reg) << D0_TYPE_SHIFT) | \
+              (REG_NR(reg) << D0_NR_SHIFT) | \
+               ((REG_TYPE(reg) != REG_TYPE_S) ? D0_CHANNEL_ALL : 0)); \
+    OUT_DWORD (0); \
+    OUT_DWORD (0); \
+} while (0)
+
+#define i915_fs_texld(dest_reg, sampler_reg, address_reg)              \
+do {                                                                   \
+    OUT_DWORD (T0_TEXLD | \
+               (REG_TYPE(dest_reg) << T0_DEST_TYPE_SHIFT) | \
+               (REG_NR(dest_reg) << T0_DEST_NR_SHIFT) | \
+               (REG_NR(sampler_reg) << T0_SAMPLER_NR_SHIFT)); \
+    OUT_DWORD((REG_TYPE(address_reg) << T1_ADDRESS_REG_TYPE_SHIFT) | \
+              (REG_NR(address_reg) << T1_ADDRESS_REG_NR_SHIFT)); \
+    OUT_DWORD (0); \
+} while (0)
+
+#define i915_fs_arith_masked(op, dest_reg, dest_mask, operand0, operand1, operand2)    \
+    _i915_fs_arith_masked(A0_##op, dest_reg, dest_mask, operand0, operand1, operand2)
+
+#define i915_fs_arith(op, dest_reg, operand0, operand1, operand2)      \
+    _i915_fs_arith(A0_##op, dest_reg, operand0, operand1, operand2)
+
+#define _i915_fs_arith_masked(cmd, dest_reg, dest_mask, operand0, operand1, operand2) \
+do { \
+    /* Set up destination register and write mask */ \
+    OUT_DWORD (cmd | \
+               (REG_TYPE(dest_reg) << A0_DEST_TYPE_SHIFT) | \
+              (REG_NR(dest_reg) << A0_DEST_NR_SHIFT) | \
+               (((dest_mask) & ~MASK_SATURATE) << A0_DEST_CHANNEL_SHIFT) | \
+               (((dest_mask) & MASK_SATURATE) ? A0_DEST_SATURATE : 0) | \
+               /* Set up operand 0 */ \
+              (REG_TYPE(operand0) << A0_SRC0_TYPE_SHIFT) | \
+              (REG_NR(operand0) << A0_SRC0_NR_SHIFT)); \
+    OUT_DWORD (i915_get_hardware_channel_val(REG_X(operand0), \
+                                             A1_SRC0_CHANNEL_X_SHIFT, \
+                                             A1_SRC0_CHANNEL_X_NEGATE) | \
+               i915_get_hardware_channel_val(REG_Y(operand0), \
+                                             A1_SRC0_CHANNEL_Y_SHIFT, \
+                                             A1_SRC0_CHANNEL_Y_NEGATE) | \
+               i915_get_hardware_channel_val(REG_Z(operand0), \
+                                             A1_SRC0_CHANNEL_Z_SHIFT, \
+                                             A1_SRC0_CHANNEL_Z_NEGATE) | \
+               i915_get_hardware_channel_val(REG_W(operand0), \
+                                             A1_SRC0_CHANNEL_W_SHIFT, \
+                                             A1_SRC0_CHANNEL_W_NEGATE) | \
+               /* Set up operand 1 */ \
+               (REG_TYPE(operand1) << A1_SRC1_TYPE_SHIFT) | \
+               (REG_NR(operand1) << A1_SRC1_NR_SHIFT) | \
+               i915_get_hardware_channel_val(REG_X(operand1), \
+                                             A1_SRC1_CHANNEL_X_SHIFT, \
+                                             A1_SRC1_CHANNEL_X_NEGATE) | \
+               i915_get_hardware_channel_val(REG_Y(operand1), \
+                                             A1_SRC1_CHANNEL_Y_SHIFT, \
+                                             A1_SRC1_CHANNEL_Y_NEGATE)); \
+    OUT_DWORD (i915_get_hardware_channel_val(REG_Z(operand1), \
+                                             A2_SRC1_CHANNEL_Z_SHIFT, \
+                                             A2_SRC1_CHANNEL_Z_NEGATE) | \
+              i915_get_hardware_channel_val(REG_W(operand1), \
+                                            A2_SRC1_CHANNEL_W_SHIFT, \
+                                            A2_SRC1_CHANNEL_W_NEGATE) | \
+               /* Set up operand 2 */ \
+               (REG_TYPE(operand2) << A2_SRC2_TYPE_SHIFT) | \
+               (REG_NR(operand2) << A2_SRC2_NR_SHIFT) | \
+               i915_get_hardware_channel_val(REG_X(operand2), \
+                                             A2_SRC2_CHANNEL_X_SHIFT, \
+                                             A2_SRC2_CHANNEL_X_NEGATE) | \
+               i915_get_hardware_channel_val(REG_Y(operand2), \
+                                             A2_SRC2_CHANNEL_Y_SHIFT, \
+                                             A2_SRC2_CHANNEL_Y_NEGATE) | \
+               i915_get_hardware_channel_val(REG_Z(operand2), \
+                                             A2_SRC2_CHANNEL_Z_SHIFT, \
+                                             A2_SRC2_CHANNEL_Z_NEGATE) | \
+               i915_get_hardware_channel_val(REG_W(operand2), \
+                                              A2_SRC2_CHANNEL_W_SHIFT, \
+                                              A2_SRC2_CHANNEL_W_NEGATE)); \
+} while (0)
+
+#define _i915_fs_arith(cmd, dest_reg, operand0, operand1, operand2) do {\
+    /* Set up destination register and write mask */ \
+    OUT_DWORD (cmd | \
+               (REG_TYPE(dest_reg) << A0_DEST_TYPE_SHIFT) | \
+              (REG_NR(dest_reg) << A0_DEST_NR_SHIFT) | \
+              (A0_DEST_CHANNEL_ALL) | \
+               /* Set up operand 0 */ \
+              (REG_TYPE(operand0) << A0_SRC0_TYPE_SHIFT) | \
+              (REG_NR(operand0) << A0_SRC0_NR_SHIFT)); \
+    OUT_DWORD (i915_get_hardware_channel_val(REG_X(operand0), \
+                                             A1_SRC0_CHANNEL_X_SHIFT, \
+                                             A1_SRC0_CHANNEL_X_NEGATE) | \
+               i915_get_hardware_channel_val(REG_Y(operand0), \
+                                             A1_SRC0_CHANNEL_Y_SHIFT, \
+                                             A1_SRC0_CHANNEL_Y_NEGATE) | \
+               i915_get_hardware_channel_val(REG_Z(operand0), \
+                                             A1_SRC0_CHANNEL_Z_SHIFT, \
+                                             A1_SRC0_CHANNEL_Z_NEGATE) | \
+               i915_get_hardware_channel_val(REG_W(operand0), \
+                                             A1_SRC0_CHANNEL_W_SHIFT, \
+                                             A1_SRC0_CHANNEL_W_NEGATE) | \
+               /* Set up operand 1 */ \
+               (REG_TYPE(operand1) << A1_SRC1_TYPE_SHIFT) | \
+               (REG_NR(operand1) << A1_SRC1_NR_SHIFT) | \
+               i915_get_hardware_channel_val(REG_X(operand1), \
+                                             A1_SRC1_CHANNEL_X_SHIFT, \
+                                             A1_SRC1_CHANNEL_X_NEGATE) | \
+               i915_get_hardware_channel_val(REG_Y(operand1), \
+                                             A1_SRC1_CHANNEL_Y_SHIFT, \
+                                             A1_SRC1_CHANNEL_Y_NEGATE)); \
+    OUT_DWORD (i915_get_hardware_channel_val(REG_Z(operand1), \
+                                             A2_SRC1_CHANNEL_Z_SHIFT, \
+                                             A2_SRC1_CHANNEL_Z_NEGATE) | \
+              i915_get_hardware_channel_val(REG_W(operand1), \
+                                            A2_SRC1_CHANNEL_W_SHIFT, \
+                                            A2_SRC1_CHANNEL_W_NEGATE) | \
+               /* Set up operand 2 */ \
+               (REG_TYPE(operand2) << A2_SRC2_TYPE_SHIFT) | \
+               (REG_NR(operand2) << A2_SRC2_NR_SHIFT) | \
+               i915_get_hardware_channel_val(REG_X(operand2), \
+                                             A2_SRC2_CHANNEL_X_SHIFT, \
+                                             A2_SRC2_CHANNEL_X_NEGATE) | \
+               i915_get_hardware_channel_val(REG_Y(operand2), \
+                                             A2_SRC2_CHANNEL_Y_SHIFT, \
+                                             A2_SRC2_CHANNEL_Y_NEGATE) | \
+               i915_get_hardware_channel_val(REG_Z(operand2), \
+                                             A2_SRC2_CHANNEL_Z_SHIFT, \
+                                             A2_SRC2_CHANNEL_Z_NEGATE) | \
+               i915_get_hardware_channel_val(REG_W(operand2), \
+                                              A2_SRC2_CHANNEL_W_SHIFT, \
+                                              A2_SRC2_CHANNEL_W_NEGATE)); \
+} while (0)
+
+#define i915_fs_mov(dest_reg, operand0)                                        \
+    i915_fs_arith(MOV, dest_reg, \
+                 operand0,                     \
+                 i915_fs_operand_none(),                       \
+                 i915_fs_operand_none())
+
+#define i915_fs_mov_masked(dest_reg, dest_mask, operand0)              \
+    i915_fs_arith_masked (MOV, dest_reg, dest_mask, \
+                         operand0, \
+                         i915_fs_operand_none(), \
+                         i915_fs_operand_none())
+
+
+#define i915_fs_frc(dest_reg, operand0)                                        \
+    i915_fs_arith (FRC, dest_reg, \
+                  operand0,                    \
+                  i915_fs_operand_none(),                      \
+                  i915_fs_operand_none())
+
+/** Add operand0 and operand1 and put the result in dest_reg */
+#define i915_fs_add(dest_reg, operand0, operand1)                      \
+    i915_fs_arith (ADD, dest_reg, \
+                  operand0, operand1,  \
+                  i915_fs_operand_none())
+
+/** Multiply operand0 and operand1 and put the result in dest_reg */
+#define i915_fs_mul(dest_reg, operand0, operand1)                      \
+    i915_fs_arith (MUL, dest_reg, \
+                  operand0, operand1,  \
+                  i915_fs_operand_none())
+
+/** Computes 1/sqrt(operand0.replicate_swizzle) puts the result in dest_reg */
+#define i915_fs_rsq(dest_reg, dest_mask, operand0)             \
+do {                                                                   \
+    if (dest_mask) {                                                   \
+       i915_fs_arith_masked (RSQ, dest_reg, dest_mask, \
+                             operand0,                 \
+                             i915_fs_operand_none (),                  \
+                             i915_fs_operand_none ());                 \
+    } else { \
+       i915_fs_arith (RSQ, dest_reg, \
+                      operand0, \
+                      i915_fs_operand_none (), \
+                      i915_fs_operand_none ()); \
+    } \
+} while (0)
+
+/** Puts the minimum of operand0 and operand1 in dest_reg */
+#define i915_fs_min(dest_reg, operand0, operand1)                      \
+    i915_fs_arith (MIN, dest_reg, \
+                  operand0, operand1, \
+                  i915_fs_operand_none())
+
+/** Puts the maximum of operand0 and operand1 in dest_reg */
+#define i915_fs_max(dest_reg, operand0, operand1)                      \
+    i915_fs_arith (MAX, dest_reg, \
+                  operand0, operand1, \
+                  i915_fs_operand_none())
+
+#define i915_fs_cmp(dest_reg, operand0, operand1, operand2)            \
+    i915_fs_arith (CMP, dest_reg, operand0, operand1, operand2)
+
+/** Perform operand0 * operand1 + operand2 and put the result in dest_reg */
+#define i915_fs_mad(dest_reg, dest_mask, op0, op1, op2)        \
+do {                                                                   \
+    if (dest_mask) {                                                   \
+       i915_fs_arith_masked (MAD, dest_reg, dest_mask, op0, op1, op2); \
+    } else { \
+       i915_fs_arith (MAD, dest_reg, op0, op1, op2); \
+    } \
+} while (0)
+
+#define i915_fs_dp2add(dest_reg, dest_mask, op0, op1, op2)     \
+do {                                                                   \
+    if (dest_mask) {                                                   \
+       i915_fs_arith_masked (DP2ADD, dest_reg, dest_mask, op0, op1, op2); \
+    } else { \
+       i915_fs_arith (DP2ADD, dest_reg, op0, op1, op2); \
+    } \
+} while (0)
+
+/**
+ * Perform a 3-component dot-product of operand0 and operand1 and put the
+ * resulting scalar in the channels of dest_reg specified by the dest_mask.
+ */
+#define i915_fs_dp3(dest_reg, dest_mask, op0, op1)     \
+do {                                                                   \
+    if (dest_mask) {                                                   \
+       i915_fs_arith_masked (DP3, dest_reg, dest_mask, \
+                             op0, op1,\
+                             i915_fs_operand_none());                  \
+    } else { \
+       i915_fs_arith (DP3, dest_reg, op0, op1,\
+                      i915_fs_operand_none());                 \
+    } \
+} while (0)
+
+static inline uint32_t cairo_const
+i915_fs_operand_pure_alpha (int pure)
+{
+    if (pure & (1 << 3))
+       return i915_fs_operand_one ();
+    else
+       return i915_fs_operand_zero ();
+}
+
+#define I915_TILING_DEFAULT I915_TILING_Y
+#define I915_BO_CACHE_BUCKETS 13 /* cache surfaces up to 16 MiB */
+
+typedef struct i915_surface i915_surface_t;
+typedef struct i915_device i915_device_t;
+typedef struct i915_shader i915_shader_t;
+
+typedef void (*i915_add_rectangle_func_t) (const i915_shader_t *shader,
+                                          int x, int y,
+                                          int w, int h);
+
+#define IMAGE_CACHE_WIDTH 1024
+#define IMAGE_CACHE_HEIGHT 1024
+
+typedef struct i915_image_private {
+    cairo_rtree_node_t node;
+    intel_buffer_cache_t *container;
+} i915_image_private_t;
+
+#define I915_BATCH_SIZE (128*1024)
+#define I915_VBO_SIZE (512*1024)
+#define I915_MAX_RELOCS 2048
+
+enum {
+    I915_DEBUG_EXEC = 0x1,
+    I915_DEBUG_SYNC = 0x2,
+    I915_DEBUG_BATCH = 0x4,
+    I915_DEBUG_BUFFER = 0x8,
+    I915_DEBUG_BUFFER_CACHE = 0x10,
+    I915_DEBUG_BUFFER_ALLOC = 0x20,
+    I915_DEBUG_GLYPHS = 0x40,
+    I915_DEBUG_MAP = 0x80,
+    I915_DEBUG_THROTTLE = 0x100,
+};
+
+struct i915_device {
+    intel_device_t intel;
+
+    cairo_bool_t debug;
+
+    i915_shader_t *shader; /* note: only valid during geometry emission */
+
+    struct i915_batch {
+       intel_bo_t *target_bo[I915_MAX_RELOCS];
+       size_t gtt_avail_size;
+       size_t est_gtt_size;
+       size_t total_gtt_size;
+
+       uint16_t fences;
+       uint16_t fences_avail;
+       uint16_t reloc_count;
+       uint16_t exec_count;
+       uint16_t used;
+
+       struct drm_i915_gem_exec_object2 exec[I915_MAX_RELOCS];
+       struct drm_i915_gem_relocation_entry reloc[I915_MAX_RELOCS];
+    } batch;
+
+    uint32_t vbo;
+    uint32_t vbo_offset;
+    uint32_t vbo_used;
+    uint32_t vbo_max_index;
+    uint32_t vertex_index;
+    uint32_t vertex_count;
+    uint32_t floats_per_vertex;
+    uint32_t rectangle_size;
+    intel_bo_t *last_vbo;
+    uint32_t last_vbo_offset;
+    uint32_t last_vbo_space;
+
+    i915_surface_t *current_target;
+    uint32_t current_size;
+    uint32_t current_diffuse;
+    uint32_t current_colorbuf;
+    uint32_t *current_source;
+    uint32_t *current_mask;
+    uint32_t *current_clip;
+    uint32_t current_program;
+    uint32_t current_texcoords;
+    uint32_t current_blend;
+    uint32_t current_constants[8*4];
+    uint32_t current_n_constants;
+    uint32_t current_samplers[2*4];
+    uint32_t current_maps[4*4];
+    uint32_t current_n_samplers;
+    uint32_t current_n_maps;
+    uint32_t last_source_fragment;
+    uint32_t clear_alpha;
+
+    cairo_list_t image_caches[2];
+
+    uint32_t batch_header[13];
+    uint32_t batch_base[I915_BATCH_SIZE / sizeof (uint32_t)];
+    uint8_t vbo_base[I915_VBO_SIZE];
+};
+
+enum {
+    CURRENT_SOURCE = 0x1,
+    CURRENT_MASK = 0x2,
+    CURRENT_CLIP = 0x4
+};
+
+typedef enum {
+    VS_ZERO,
+    VS_CONSTANT,
+    VS_LINEAR,
+    VS_TEXTURE,
+    VS_TEXTURE_16,
+} i915_vertex_shader_t;
+
+typedef enum {
+    FS_ZERO,
+    FS_ONE,
+    FS_PURE,
+    FS_CONSTANT,
+    FS_DIFFUSE,
+    FS_LINEAR,
+    FS_RADIAL,
+    FS_TEXTURE,
+    FS_YUV,
+    FS_SPANS,
+} i915_fragment_shader_t;
+
+#define FS_DETAILS_SHIFT 4
+
+typedef enum {
+    PATTERN_BASE,
+    PATTERN_CONSTANT,
+    PATTERN_LINEAR,
+    PATTERN_RADIAL,
+    PATTERN_TEXTURE,
+} i915_shader_channel_t;
+
+struct i915_surface {
+    intel_surface_t intel;
+
+    uint32_t map0, map1;
+    uint32_t colorbuf;
+
+    cairo_bool_t deferred_clear;
+    uint32_t offset;
+    uint32_t is_current_texture;
+
+    i915_image_private_t *cache;
+
+    intel_bo_t *stencil;
+    uint32_t stencil_stride;
+    uint32_t stencil_offset;
+};
+
+typedef enum {
+    NONE = 0,
+    YUV_I420,
+    /* XXX */
+    YUV_YV12,
+    YUV_YUY2,
+    YUV_UYVY,
+} i915_packed_pixel_t;
+
+/* read-only container */
+#define I915_PACKED_PIXEL_SURFACE_TYPE 0x1000
+typedef struct i915_packed_pixel_surface {
+    cairo_surface_t base;
+
+    i915_packed_pixel_t pixel;
+
+    i915_device_t *device;
+    intel_bo_t *bo;
+    uint32_t is_current_texture;
+
+    uint32_t offset[4];
+    uint32_t stride[4];
+    uint32_t width[4];
+    uint32_t height[4];
+    uint32_t map0[4], map1[4];
+} i915_packed_pixel_surface_t;
+
+struct i915_shader {
+    i915_device_t *device;
+    i915_surface_t *target;
+
+    cairo_operator_t op;
+    uint32_t blend;
+    float opacity;
+    cairo_content_t content;
+
+    cairo_bool_t committed;
+    cairo_bool_t need_combine;
+
+    i915_add_rectangle_func_t add_rectangle;
+
+    union i915_shader_channel {
+       struct {
+           i915_vertex_shader_t vertex;
+           i915_fragment_shader_t fragment;
+           i915_shader_channel_t pattern;
+       } type;
+       struct i915_shader_base {
+           i915_vertex_shader_t vertex;
+           i915_fragment_shader_t fragment;
+           i915_shader_channel_t pattern;
+           uint32_t texfmt;
+           cairo_content_t content;
+           uint32_t mode;
+           intel_bo_t *bo;
+           uint32_t n_samplers;
+           uint32_t offset[4];
+           uint32_t map[2*4];
+           uint32_t sampler[2];
+           cairo_matrix_t matrix;
+       } base;
+       struct i915_shader_solid {
+           struct i915_shader_base base;
+           cairo_color_t color;
+           int pure;
+       } solid;
+       struct i915_shader_linear {
+           struct i915_shader_base base;
+           struct {
+               float red, green, blue, alpha;
+           } color0, color1;
+           float dx, dy, offset;
+       } linear;
+       struct i915_shader_radial {
+           struct i915_shader_base base;
+           float constants[8];
+       } radial;
+       struct i915_shader_surface {
+           struct i915_shader_base base;
+           i915_packed_pixel_t pixel;
+       } surface;
+    } source, mask, clip, dst;
+
+    jmp_buf unwind;
+};
+
+enum i915_shader_linear_mode {
+    /* XXX REFLECT */
+    LINEAR_TEXTURE,
+    LINEAR_NONE,
+    LINEAR_REPEAT,
+    LINEAR_PAD,
+};
+
+enum i915_shader_radial_mode {
+    RADIAL_ONE,
+    RADIAL_TWO
+};
+
+typedef cairo_status_t
+(*i915_spans_func_t) (void                     *closure,
+                     cairo_span_renderer_t     *renderer,
+                     const cairo_rectangle_int_t       *extents);
+
+cairo_private cairo_status_t
+i915_clip_and_composite_spans (i915_surface_t          *dst,
+                              cairo_operator_t          op,
+                              const cairo_pattern_t    *pattern,
+                              cairo_antialias_t         antialias,
+                              i915_spans_func_t         draw_func,
+                              void                     *draw_closure,
+                              const cairo_composite_rectangles_t*extents,
+                              cairo_clip_t             *clip,
+                              double                    opacity);
+
+cairo_private cairo_surface_t *
+i915_surface_create_internal (cairo_drm_device_t *base_dev,
+                             cairo_format_t format,
+                             int width, int height,
+                             uint32_t tiling,
+                             cairo_bool_t gpu_target);
+
+cairo_private i915_surface_t *
+i915_surface_create_from_cacheable_image_internal (i915_device_t *device,
+                                                  cairo_image_surface_t *image);
+
+cairo_private void
+i915_surface_scaled_font_fini (cairo_scaled_font_t *scaled_font);
+
+cairo_private cairo_int_status_t
+i915_surface_glyphs (void                      *abstract_surface,
+                    cairo_operator_t            op,
+                    const cairo_pattern_t      *source,
+                    cairo_glyph_t              *glyphs,
+                    int                         num_glyphs,
+                    cairo_scaled_font_t        *scaled_font,
+                    cairo_clip_t               *clip,
+                    int *num_remaining);
+
+static inline int cairo_const
+i915_tiling_height (uint32_t tiling, int height)
+{
+    switch (tiling) {
+    default:
+    case I915_TILING_NONE: return (height + 1) & -2;
+    case I915_TILING_X: return (height + 7) & -8;
+    case I915_TILING_Y: return (height + 31) & -32;
+    }
+}
+
+static inline uint32_t cairo_const
+i915_tiling_stride (int format, uint32_t stride)
+{
+    uint32_t tile_width;
+
+    /* use 64B alignment so that the buffer may be used as a scanout */
+    if (format == I915_TILING_NONE)
+       return (stride + 63) & -64;
+
+    tile_width = 512;
+    /* XXX Currently the kernel enforces a tile_width of 512 for TILING_Y.
+
+       <jbarnes> the docs are a bit confused on that front
+       <jbarnes> once we enable it on 915 we'll find out what the tile width size should be in the fence setup
+       <jbarnes> it could be that 915 has y tiling but that the minimum width is 512 or something
+       <jbarnes> yeah it's probably 128 on 915 also
+       <jbarnes> it's just that we haven't tested
+       <jbarnes> but I wasn't thinking that the tile widths were the same
+       <jbarnes> only that in order to fence y tiles on 915 you needed pitch to be a multiple of 4 y tiles (or something like that)
+
+       tile_width = format == I915_TILING_Y ? 128 : 512;
+    */
+
+    /* needs a pot tile width */
+    while (tile_width < stride)
+       tile_width <<= 1;
+
+    return tile_width;
+}
+
+static inline uint32_t cairo_const
+i915_tiling_size (uint32_t tiling, uint32_t size)
+{
+    uint32_t fence;
+
+    if (tiling == I915_TILING_NONE)
+       return (size + 4095) & -4096;
+
+    fence = 1024 * 1024; /* 1 MiB */
+    while (fence < size)
+       fence <<= 1;
+
+    return fence;
+}
+
+static inline cairo_bool_t cairo_const
+i915_texture_filter_is_nearest (cairo_filter_t filter)
+{
+    switch (filter) {
+    case CAIRO_FILTER_BEST:
+    case CAIRO_FILTER_GOOD:
+    case CAIRO_FILTER_BILINEAR:
+    case CAIRO_FILTER_GAUSSIAN:
+       return FALSE;
+    default:
+    case CAIRO_FILTER_FAST:
+    case CAIRO_FILTER_NEAREST:
+       return TRUE;
+    }
+}
+
+static inline uint32_t cairo_const
+i915_texture_filter (cairo_filter_t filter)
+{
+    switch (filter) {
+    case CAIRO_FILTER_BEST:
+    case CAIRO_FILTER_GOOD:
+    case CAIRO_FILTER_BILINEAR:
+    case CAIRO_FILTER_GAUSSIAN:
+        return
+           (FILTER_LINEAR << SS2_MAG_FILTER_SHIFT) |
+           (FILTER_LINEAR << SS2_MIN_FILTER_SHIFT);
+    default:
+    case CAIRO_FILTER_FAST:
+    case CAIRO_FILTER_NEAREST:
+       return
+           (FILTER_NEAREST << SS2_MAG_FILTER_SHIFT) |
+           (FILTER_NEAREST << SS2_MIN_FILTER_SHIFT);
+    }
+}
+
+static inline uint32_t cairo_const
+i915_texture_extend (cairo_extend_t extend)
+{
+    switch (extend) {
+    default:
+    case CAIRO_EXTEND_NONE:
+       return
+           (TEXCOORDMODE_CLAMP_BORDER << SS3_TCX_ADDR_MODE_SHIFT) |
+           (TEXCOORDMODE_CLAMP_BORDER << SS3_TCY_ADDR_MODE_SHIFT);
+    case CAIRO_EXTEND_REPEAT:
+       return
+           (TEXCOORDMODE_WRAP << SS3_TCX_ADDR_MODE_SHIFT) |
+           (TEXCOORDMODE_WRAP << SS3_TCY_ADDR_MODE_SHIFT);
+    case CAIRO_EXTEND_PAD:
+       return
+           (TEXCOORDMODE_CLAMP_EDGE << SS3_TCX_ADDR_MODE_SHIFT) |
+           (TEXCOORDMODE_CLAMP_EDGE << SS3_TCY_ADDR_MODE_SHIFT);
+    case CAIRO_EXTEND_REFLECT:
+       return
+           (TEXCOORDMODE_MIRROR << SS3_TCX_ADDR_MODE_SHIFT) |
+           (TEXCOORDMODE_MIRROR << SS3_TCY_ADDR_MODE_SHIFT);
+    }
+}
+
+static inline uint32_t cairo_const
+BUF_tiling (uint32_t tiling)
+{
+    switch (tiling) {
+    default:
+    case I915_TILING_NONE: return 0;
+    case I915_TILING_X: return BUF_3D_TILED_SURFACE | BUF_3D_TILE_WALK_X;
+    case I915_TILING_Y: return BUF_3D_TILED_SURFACE | BUF_3D_TILE_WALK_Y;
+    }
+}
+
+#define OUT_DWORD(dword) i915_batch_emit_dword (device, dword)
+#define OUT_RELOC(surface, read, write) i915_batch_emit_reloc (device, to_intel_bo (surface->intel.drm.bo), surface->offset, read, write, FALSE)
+#define OUT_RELOC_FENCED(surface, read, write) i915_batch_emit_reloc (device, to_intel_bo (surface->intel.drm.bo), surface->offset, read, write, TRUE)
+
+#define FS_LOCALS                                                      \
+    uint32_t *_shader_start
+
+#define FS_BEGIN()                                                     \
+do {                                                                   \
+    _shader_start = BATCH_PTR (device);                                        \
+    OUT_DWORD (_3DSTATE_PIXEL_SHADER_PROGRAM);                         \
+} while (0)
+
+#define FS_END()                                                       \
+do {                                                                   \
+    *_shader_start |= BATCH_PTR (device) - _shader_start - 2;          \
+} while (0);
+
+static inline int32_t
+i915_batch_space (i915_device_t *device)
+{
+    /* leave room for RECTLIST(4) + MI_BUFFER_END + MI_NOOP */
+    return sizeof (device->batch_base) - (device->batch.used << 2) - 32;
+}
+
+static inline cairo_bool_t
+i915_check_aperture_size (const i915_device_t *device, int relocs, size_t est_size, size_t size)
+{
+    return device->batch.reloc_count + relocs < I915_MAX_RELOCS - 2 &&
+          device->batch.est_gtt_size + est_size <= device->batch.gtt_avail_size &&
+          device->batch.total_gtt_size + size <= device->intel.gtt_avail_size;
+}
+
+static inline cairo_bool_t
+i915_check_aperture (const i915_device_t *device, intel_bo_t **bo_array, int count)
+{
+    uint32_t relocs = 0, est_size = 0, size = 0;
+
+    while (count--) {
+       const intel_bo_t *bo = *bo_array++;
+       if (bo->exec == NULL) {
+           relocs++;
+           size += bo->base.size;
+           if (!bo->busy)
+               est_size += bo->base.size;
+       }
+    }
+
+    return i915_check_aperture_size (device, relocs, est_size, size);
+}
+
+static inline cairo_bool_t
+i915_check_aperture_and_fences (const i915_device_t *device, intel_bo_t **bo_array, int count)
+{
+    uint32_t relocs = 0, est_size = 0, size = 0;
+    uint32_t fences = 0;
+
+    while (count--) {
+       const intel_bo_t *bo = *bo_array++;
+       if (bo->exec == NULL) {
+           relocs++;
+           size += bo->base.size;
+           if (!bo->busy)
+               est_size += bo->base.size;
+           if (bo->tiling != I915_TILING_NONE)
+               fences++;
+       } else if (bo->tiling != I915_TILING_NONE) {
+           if ((bo->exec->flags & EXEC_OBJECT_NEEDS_FENCE) == 0)
+               fences++;
+       }
+    }
+
+    return i915_check_aperture_size (device, relocs, est_size, size) &&
+          device->batch.fences + fences <= device->batch.fences_avail;
+}
+
+#define BATCH_PTR(device) &(device)->batch_base[(device)->batch.used]
+static inline void
+i915_batch_emit_dword (i915_device_t *device, uint32_t dword)
+{
+    device->batch_base[device->batch.used++] = dword;
+}
+
+cairo_private void
+i915_batch_add_reloc (i915_device_t *device, uint32_t pos,
+                     intel_bo_t *bo,
+                     uint32_t offset,
+                     uint32_t read_domains,
+                     uint32_t write_domain,
+                     cairo_bool_t needs_fence);
+
+static inline void
+i915_batch_fill_reloc (i915_device_t *device, uint32_t pos,
+                      intel_bo_t *bo,
+                      uint32_t offset,
+                      uint32_t read_domains,
+                      uint32_t write_domain)
+{
+    i915_batch_add_reloc (device, pos,
+                         bo, offset,
+                         read_domains, write_domain,
+                         FALSE);
+    device->batch_base[pos] = bo->offset + offset;
+}
+
+static inline void
+i915_batch_emit_reloc (i915_device_t *device,
+                      intel_bo_t *bo,
+                      uint32_t offset,
+                      uint32_t read_domains,
+                      uint32_t write_domain,
+                      cairo_bool_t needs_fence)
+{
+    i915_batch_add_reloc (device, device->batch.used,
+                         bo, offset,
+                         read_domains, write_domain,
+                         needs_fence);
+    i915_batch_emit_dword (device, bo->offset + offset);
+}
+
+cairo_private void
+i915_vbo_flush (i915_device_t *device);
+
+cairo_private void
+i915_vbo_finish (i915_device_t *device);
+
+cairo_private  cairo_status_t
+i915_batch_flush (i915_device_t *device);
+
+static inline float *
+i915_add_rectangle (i915_device_t *device)
+{
+    float *vertices;
+    uint32_t size;
+
+    assert (device->floats_per_vertex);
+    assert (device->rectangle_size == 3*device->floats_per_vertex*sizeof(float));
+
+    size = device->rectangle_size;
+    if (unlikely (device->vbo_offset + size > I915_VBO_SIZE))
+       i915_vbo_finish (device);
+
+    vertices = (float *) (device->vbo_base + device->vbo_offset);
+    device->vbo_used = device->vbo_offset += size;
+    device->vertex_count += 3;
+    return vertices;
+}
+
+static inline i915_device_t *
+i915_device (i915_surface_t *surface)
+{
+    return (i915_device_t *) surface->intel.drm.base.device;
+}
+
+cairo_private cairo_status_t
+i915_surface_clear (i915_surface_t *dst);
+
+cairo_private void
+i915_set_dst (i915_device_t *device, i915_surface_t *dst);
+
+cairo_private void
+i915_shader_init (i915_shader_t *shader,
+                 i915_surface_t *dst,
+                 cairo_operator_t op,
+                 double opacity);
+
+cairo_private cairo_status_t
+i915_shader_acquire_pattern (i915_shader_t *shader,
+                            union i915_shader_channel *src,
+                            const cairo_pattern_t *pattern,
+                            const cairo_rectangle_int_t *extents);
+
+cairo_private void
+i915_shader_set_clip (i915_shader_t *shader,
+                     cairo_clip_t *clip);
+
+cairo_private int
+i915_shader_num_texcoords (const i915_shader_t *shader);
+
+static inline double cairo_const
+i915_shader_linear_texcoord (const struct i915_shader_linear *l,
+                            double src_x, double src_y)
+{
+    return l->dx * src_x + l->dy * src_y + l->offset;
+}
+
+cairo_private cairo_status_t
+i915_shader_commit (i915_shader_t *shader,
+                   i915_device_t *device);
+
+cairo_private void
+i915_shader_fini (i915_shader_t *shader);
+
+cairo_private cairo_status_t
+i915_fixup_unbounded (i915_surface_t *dst,
+                     const cairo_composite_rectangles_t *extents,
+                     cairo_clip_t *clip);
+
+static inline cairo_bool_t
+i915_surface_needs_tiling (i915_surface_t *dst)
+{
+    return dst->intel.drm.width > 2048 || dst->intel.drm.height > 2048;
+}
+
+cairo_private cairo_status_t
+i915_surface_copy_subimage (i915_device_t *device,
+                           i915_surface_t *src,
+                           const cairo_rectangle_int_t *extents,
+                           cairo_bool_t flush,
+                           i915_surface_t **clone_out);
+
+static inline uint32_t
+pack_float (float f)
+{
+    union {
+       float f;
+       uint32_t ui;
+    } t;
+    t.f = f;
+    return t.ui;
+}
+
+static inline cairo_status_t
+i915_surface_fallback_flush (i915_surface_t *surface)
+{
+    cairo_status_t status;
+
+    if (unlikely (surface->intel.drm.fallback != NULL))
+       return intel_surface_flush (&surface->intel);
+
+    status = CAIRO_STATUS_SUCCESS;
+    if (unlikely (surface->deferred_clear))
+       status = i915_surface_clear (surface);
+
+    return status;
+}
+
+#endif /* CAIRO_DRM_I915_PRIVATE_H */
diff --git a/src/drm/cairo-drm-i915-shader.c b/src/drm/cairo-drm-i915-shader.c
new file mode 100644 (file)
index 0000000..a1911d0
--- /dev/null
@@ -0,0 +1,2893 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * Contributor(s):
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-error-private.h"
+#include "cairo-drm-i915-private.h"
+#include "cairo-surface-offset-private.h"
+#include "cairo-surface-subsurface-private.h"
+#include "cairo-surface-snapshot-private.h"
+
+#if 0
+static cairo_status_t
+i915_packed_pixel_surface_finish (void *abstract_surface)
+{
+    i915_packed_pixel_surface_t *surface = abstract_surface;
+    i915_device_t *device;
+
+    device = i915_device_acquire (&surface->device->intel.base);
+
+    intel_bo_destroy (&device->intel, surface->bo);
+
+    if (surface->is_current_texture) {
+       if (surface->is_current_texture & CURRENT_SOURCE)
+           device->current_source = NULL;
+       if (surface->is_current_texture & CURRENT_MASK)
+           device->current_mask = NULL;
+       device->current_n_samplers = 0;
+    }
+
+    i915_device_release (device);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static const cairo_surface_backend_t i915_packed_pixel_surface_backend = {
+    I915_PACKED_PIXEL_SURFACE_TYPE,
+    i915_packed_pixel_surface_finish,
+};
+
+static cairo_surface_t *
+i915_packed_pixel_surface_create (i915_device_t *device,
+                                  i915_packed_pixel_t pixel,
+                                  const uint8_t *data,
+                                  uint32_t length,
+                                  uint32_t width, uint32_t height)
+{
+    i915_packed_pixel_surface_t *surface;
+    cairo_content_t content;
+    uint32_t tiling, size;
+    uint32_t stride, half_stride;
+    uint32_t i;
+
+    if (width > 2048 || height > 2048)
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE));
+
+    surface = malloc (sizeof (i915_packed_pixel_surface_t));
+    if (unlikely (surface == NULL))
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+    tiling = I915_TILING_NONE; /* XXX */
+    half_stride = stride = i915_tiling_stride (tiling, width/2);
+    if (stride < width)
+       stride *= 2 ;
+    height = i915_tiling_height (tiling, height);
+
+    switch (surface->pixel = pixel) {
+    case YUV_I420:
+       content = CAIRO_CONTENT_COLOR;
+
+       surface->offset[0] = 0;
+       surface->width[0] = width;
+       surface->height[0] = height;
+       surface->stride[0] = stride;
+       surface->map0[0] = MAPSURF_8BIT | MT_8BIT_I8 | MS3_tiling (tiling);
+       surface->map0[0] |= ((height - 1) << MS3_HEIGHT_SHIFT) |
+                           ((width - 1)  << MS3_WIDTH_SHIFT);
+       surface->map1[0] = (stride / 4 - 1) << MS4_PITCH_SHIFT;
+
+       surface->offset[1] = stride * height;
+       surface->width[1] = width / 2;
+       surface->height[1] = height / 2;
+       surface->stride[1] = half_stride;
+       surface->map0[1] = MAPSURF_8BIT | MT_8BIT_I8 | MS3_tiling (tiling);
+       surface->map0[1] |= ((height/2 - 1) << MS3_HEIGHT_SHIFT) |
+                           ((width/2 - 1)  << MS3_WIDTH_SHIFT);
+       surface->map1[1] = (half_stride / 4 - 1) << MS4_PITCH_SHIFT;
+
+       if (width < half_stride) {
+           surface->offset[2] = stride * height + half_stride / 2;
+           size = stride * height + half_stride * height / 2;
+       } else {
+           surface->offset[2] = stride * height + half_stride * height / 2;
+           size = stride * height + half_stride * height;
+       }
+       surface->width[2] = width / 2;
+       surface->height[2] = height / 2;
+       surface->stride[2] = half_stride;
+       surface->map0[2] = MAPSURF_8BIT | MT_8BIT_I8 | MS3_tiling (tiling);
+       surface->map0[2] |= ((height/2 - 1) << MS3_HEIGHT_SHIFT) |
+                           ((width/2 - 1)  << MS3_WIDTH_SHIFT);
+       surface->map1[2] = (half_stride / 4 - 1) << MS4_PITCH_SHIFT;
+       break;
+
+    case NONE:
+    case YUV_YV12:
+    case YUV_YUY2:
+    case YUV_UYVY:
+       ASSERT_NOT_REACHED;
+       break;
+    }
+
+    _cairo_surface_init (&surface->base,
+                        &i915_packed_pixel_surface_backend,
+                        content);
+
+    surface->bo = intel_bo_create (&device->intel, size, FALSE);
+    assert (surface->bo->tiling == I915_TILING_NONE);
+    if (unlikely (surface->bo == NULL)) {
+       free (surface);
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+    }
+
+    if (tiling == I915_TILING_NONE) {
+       intel_bo_t *bo = surface->bo;
+       uint32_t dst;
+       int uv;
+
+       dst = surface->offset[0];
+       if (width == stride) {
+           size = stride * height;
+           intel_bo_write (&device->intel, bo, dst, size, data);
+           data += size;
+       } else {
+           for (i = 0; i < height; i++) {
+               intel_bo_write (&device->intel, bo, dst, width, data);
+               dst += stride;
+               data += width;
+           }
+       }
+
+       for (uv = 1; uv <= 2; uv++) {
+           dst = surface->offset[uv];
+           if (width / 2 == half_stride) {
+               size = half_stride * height / 2;
+               intel_bo_write (&device->intel, bo, dst, size, data);
+               data += size;
+           } else {
+               size = width / 2;
+               for (i = 0; i < height / 2; i++) {
+                   intel_bo_write (&device->intel, bo, dst, size, data);
+                   dst += half_stride;
+                   data += size;
+               }
+           }
+       }
+    } else {
+       uint8_t *dst, *base;
+
+       base = intel_bo_map (&device->intel, surface->bo);
+
+       dst = base + surface->offset[0];
+       if (width == stride) {
+           size = stride * height;
+           memcpy (dst, data, size);
+           data += size;
+       } else {
+           for (i = 0; i < height; i++) {
+               memcpy (dst, data, width);
+               dst += stride;
+               data += width;
+           }
+       }
+
+       dst = base + surface->offset[1];
+       if (width / 2 == half_stride) {
+           size = half_stride * height / 2;
+           memcpy (dst, data, size);
+           data += size;
+       } else {
+           size = width / 2;
+           for (i = 0; i < height / 2; i++) {
+               memcpy (dst, data, size);
+               dst += half_stride;
+               data += size;
+           }
+       }
+
+       dst = base + surface->offset[2];
+       if (width / 2 == half_stride) {
+           size = half_stride * height / 2;
+           memcpy (dst, data, size);
+           data += size;
+       } else {
+           size = width / 2;
+           for (i = 0; i < height / 2; i++) {
+               memcpy (dst, data, size);
+               dst += half_stride;
+               data += size;
+           }
+       }
+    }
+
+    surface->device = device;
+    surface->is_current_texture = 0;
+
+    return &surface->base;
+}
+
+static cairo_int_status_t
+i915_clone_yuv (i915_surface_t *surface,
+                cairo_surface_t *source,
+                int width, int height,
+                cairo_surface_t **clone_out)
+{
+    const uint8_t *mime_data = NULL;
+    unsigned int mime_data_length;
+    cairo_surface_t *clone;
+
+    cairo_surface_get_mime_data (source, "video/x-raw-yuv/i420",
+                                &mime_data, &mime_data_length);
+    if (mime_data == NULL)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    clone =
+       i915_packed_pixel_surface_create ((i915_device_t *) surface->base.device,
+                                          YUV_I420,
+                                          mime_data, mime_data_length,
+                                          width, height);
+    if (clone == NULL)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+    if (unlikely (clone->status))
+       return clone->status;
+
+    *clone_out = clone;
+    return CAIRO_STATUS_SUCCESS;
+}
+#endif
+
+/* Max instruction count: 4 */
+static void
+i915_shader_linear_color (i915_device_t *device,
+                         enum i915_shader_linear_mode mode,
+                         int in, int c0, int c1, int out)
+{
+    int tmp = FS_U0;
+
+    switch (mode) {
+    case LINEAR_TEXTURE:
+       ASSERT_NOT_REACHED;
+    case LINEAR_NONE:
+       tmp = in;
+       break;
+
+    case LINEAR_REPEAT:
+       i915_fs_frc (tmp, i915_fs_operand (in, X, X, X, X));
+       break;
+#if 0
+    case LINEAR_REFLECT:
+       /* XXX needs an extra constant: C2 [0.5, 2.0, x, x] */
+       i915_fs_mul (tmp, in, 0.5);
+       i915_fs_frc (tmp, i915_fs_operand_reg (tmp));
+       i915_fs_mul (tmp, tmp, 2.0);
+       i915_fs_add (tmp, i915_fs_operand_one (),
+                    i915_fs_operand_reg_negate (tmp));
+       i915_fs_cmp (tmp,
+                    i915_fs_operand_reg (tmp),
+                    i915_fs_operand_reg (tmp),
+                    i915_fs_operand_reg_negate (tmp));
+       i915_fs_add (tmp, i915_fs_operand_one (),
+                    i915_fs_operand_reg_negate (tmp));
+#endif
+    case LINEAR_PAD:
+       i915_fs_max (tmp,
+                    i915_fs_operand_zero (),
+                    i915_fs_operand (in, X, X, X, X));
+       i915_fs_min (tmp,
+                    i915_fs_operand_one (),
+                    i915_fs_operand_reg (tmp));
+       break;
+    }
+
+    /* interpolate */
+    i915_fs_mad (out, 0,
+                i915_fs_operand (tmp, NEG_X, NEG_X, NEG_X, NEG_X),
+                i915_fs_operand_reg (c0),
+                i915_fs_operand_reg (c0));
+    i915_fs_mad (out, 0,
+                i915_fs_operand (tmp, X, X, X, X),
+                i915_fs_operand_reg (c1),
+                i915_fs_operand_reg (out));
+}
+
+static void
+i915_shader_radial_init (struct i915_shader_radial *r,
+                        const cairo_radial_pattern_t *radial)
+{
+    double dx, dy, dr, r1;
+
+    dx = radial->cd2.center.x - radial->cd1.center.x;
+    dy = radial->cd2.center.y - radial->cd1.center.y;
+    dr = radial->cd2.radius   - radial->cd1.radius;
+
+    r1 = radial->cd1.radius;
+
+    if (radial->cd2.center.x == radial->cd1.center.x &&
+       radial->cd2.center.y == radial->cd1.center.y)
+    {
+       /* XXX dr == 0, meaningless with anything other than PAD */
+       r->constants[0] = radial->cd1.center.x / dr;
+       r->constants[1] = radial->cd1.center.y / dr;
+       r->constants[2] = 1. / dr;
+       r->constants[3] = -r1 / dr;
+
+       r->constants[4] = 0;
+       r->constants[5] = 0;
+       r->constants[6] = 0;
+       r->constants[7] = 0;
+
+       r->base.mode = RADIAL_ONE;
+    } else {
+       r->constants[0] = -radial->cd1.center.x;
+       r->constants[1] = -radial->cd1.center.y;
+       r->constants[2] = r1;
+       r->constants[3] = -4 * (dx*dx + dy*dy - dr*dr);
+
+       r->constants[4] = -2 * dx;
+       r->constants[5] = -2 * dy;
+       r->constants[6] = -2 * r1 * dr;
+       r->constants[7] = 1 / (2 * (dx*dx + dy*dy - dr*dr));
+
+       r->base.mode = RADIAL_TWO;
+    }
+
+    r->base.matrix = radial->base.base.matrix;
+}
+
+/* Max instruction count: 10 */
+static void
+i915_shader_radial_coord (i915_device_t *device,
+                         enum i915_shader_radial_mode mode,
+                         int in, int g0, int g1, int out)
+{
+    switch (mode) {
+    case RADIAL_ONE:
+       /*
+          pdx = (x - c1x) / dr, pdy = (y - c1y) / dr;
+          r² = pdx*pdx + pdy*pdy
+          t = r²/sqrt(r²) - r1/dr;
+          */
+       i915_fs_mad (FS_U0, MASK_X | MASK_Y,
+                    i915_fs_operand (in, X, Y, ZERO, ZERO),
+                    i915_fs_operand (g0, Z, Z, ZERO, ZERO),
+                    i915_fs_operand (g0, NEG_X, NEG_Y, ZERO, ZERO));
+       i915_fs_dp2add (FS_U0, MASK_X,
+                       i915_fs_operand (FS_U0, X, Y, ZERO, ZERO),
+                       i915_fs_operand (FS_U0, X, Y, ZERO, ZERO),
+                       i915_fs_operand_zero ());
+       i915_fs_rsq (out, MASK_X, i915_fs_operand (FS_U0, X, X, X, X));
+       i915_fs_mad (out, MASK_X,
+                    i915_fs_operand (FS_U0, X, ZERO, ZERO, ZERO),
+                    i915_fs_operand (out, X, ZERO, ZERO, ZERO),
+                    i915_fs_operand (g0, W, ZERO, ZERO, ZERO));
+       break;
+
+    case RADIAL_TWO:
+       /*
+          pdx = x - c1x, pdy = y - c1y;
+          A = dx² + dy² - dr²
+          B = -2*(pdx*dx + pdy*dy + r1*dr);
+          C = pdx² + pdy² - r1²;
+          det = B*B - 4*A*C;
+          t = (-B + sqrt (det)) / (2 * A)
+          */
+
+       /* u0.x = pdx, u0.y = pdy, u[0].z = r1; */
+       i915_fs_add (FS_U0,
+                    i915_fs_operand (in, X, Y, ZERO, ZERO),
+                    i915_fs_operand (g0, X, Y, Z, ZERO));
+       /* u0.x = pdx, u0.y = pdy, u[0].z = r1, u[0].w = B; */
+       i915_fs_dp3 (FS_U0, MASK_W,
+                    i915_fs_operand (FS_U0, X, Y, ONE, ZERO),
+                    i915_fs_operand (g1, X, Y, Z, ZERO));
+       /* u1.x = pdx² + pdy² - r1²; [C] */
+       i915_fs_dp3 (FS_U1, MASK_X,
+                    i915_fs_operand (FS_U0, X, Y, Z, ZERO),
+                    i915_fs_operand (FS_U0, X, Y, NEG_Z, ZERO));
+       /* u1.x = C, u1.y = B, u1.z=-4*A; */
+       i915_fs_mov_masked (FS_U1, MASK_Y, i915_fs_operand (FS_U0, W, W, W, W));
+       i915_fs_mov_masked (FS_U1, MASK_Z, i915_fs_operand (g0, W, W, W, W));
+       /* u1.x = B² - 4*A*C */
+       i915_fs_dp2add (FS_U1, MASK_X,
+                       i915_fs_operand (FS_U1, X, Y, ZERO, ZERO),
+                       i915_fs_operand (FS_U1, Z, Y, ZERO, ZERO),
+                       i915_fs_operand_zero ());
+       /* out.x = -B + sqrt (B² - 4*A*C),
+        * out.y = -B - sqrt (B² - 4*A*C),
+        */
+       i915_fs_rsq (out, MASK_X, i915_fs_operand (FS_U1, X, X, X, X));
+       i915_fs_mad (out, MASK_X | MASK_Y,
+                    i915_fs_operand (out, X, X, ZERO, ZERO),
+                    i915_fs_operand (FS_U1, X, NEG_X, ZERO, ZERO),
+                    i915_fs_operand (FS_U0, NEG_W, NEG_W, ZERO, ZERO));
+       /* out.x = (-B + sqrt (B² - 4*A*C)) / (2 * A),
+        * out.y = (-B - sqrt (B² - 4*A*C)) / (2 * A)
+        */
+       i915_fs_mul (out,
+                    i915_fs_operand (out, X, Y, ZERO, ZERO),
+                    i915_fs_operand (g1, W, W, ZERO, ZERO));
+       /* if (A > 0)
+        *   out = (-B + sqrt (B² - 4*A*C)) / (2 * A),
+        * else
+        *   out = (-B - sqrt (B² - 4*A*C)) / (2 * A)
+        */
+       i915_fs_cmp (out,
+                    i915_fs_operand (g1, W, ZERO, ZERO, ZERO),
+                    i915_fs_operand (out, X, ZERO, ZERO, ZERO),
+                    i915_fs_operand (out, Y, ZERO, ZERO, ZERO));
+       break;
+    }
+}
+
+/* Max instruction count: 7 */
+static inline void
+i915_shader_yuv_color (i915_device_t *device,
+                      int y, int u, int v,
+                      int c0, int c1, int c2,
+                      int out)
+{
+    i915_fs_mov_masked (FS_U0, MASK_X, i915_fs_operand_reg (y));
+    i915_fs_mov_masked (FS_U0, MASK_Y, i915_fs_operand_reg (u));
+    i915_fs_mov_masked (FS_U0, MASK_Z, i915_fs_operand_reg (v));
+
+    i915_fs_add (FS_U0,
+                i915_fs_operand_reg (FS_U0),
+                i915_fs_operand_reg (c0));
+    i915_fs_dp3 (out, MASK_X,
+                i915_fs_operand_reg (FS_U0),
+                i915_fs_operand (c1, X, ZERO, Y, ZERO));
+    i915_fs_dp3 (out, MASK_Z,
+                i915_fs_operand_reg (FS_U0),
+                i915_fs_operand (c1, Z, W, ZERO, ZERO));
+    i915_fs_dp3 (out, MASK_Y,
+                i915_fs_operand_reg (FS_U0),
+                i915_fs_operand_reg (c2));
+}
+
+static inline uint32_t
+i915_shader_channel_key (const union i915_shader_channel *channel)
+{
+    return (channel->type.fragment & 0x0f) | (channel->base.mode << FS_DETAILS_SHIFT);
+}
+
+static uint32_t
+i915_shader_channel_get_num_tex_coords (const union i915_shader_channel *channel)
+{
+    switch (channel->type.fragment) {
+    default:
+    case FS_ZERO:
+    case FS_ONE:
+    case FS_CONSTANT:
+    case FS_PURE:
+    case FS_DIFFUSE:
+       return 0;
+
+    case FS_LINEAR:
+    case FS_RADIAL:
+    case FS_TEXTURE:
+    case FS_SPANS:
+    case FS_YUV:
+       return 1;
+    }
+}
+
+static uint32_t
+i915_shader_get_num_tex_coords (const i915_shader_t *shader)
+{
+    uint32_t num_tex_coords;
+
+    num_tex_coords = 0;
+
+    num_tex_coords += i915_shader_channel_get_num_tex_coords (&shader->source);
+    num_tex_coords += i915_shader_channel_get_num_tex_coords (&shader->mask);
+    num_tex_coords += i915_shader_channel_get_num_tex_coords (&shader->clip);
+    num_tex_coords += i915_shader_channel_get_num_tex_coords (&shader->dst);
+
+    return num_tex_coords;
+}
+
+#define i915_fs_operand_impure(reg, channel, pure) \
+    (reg | \
+     (((pure & (1 << 0)) ? channel##_CHANNEL_VAL : ZERO_CHANNEL_VAL) << X_CHANNEL_SHIFT) | \
+     (((pure & (1 << 1)) ? channel##_CHANNEL_VAL : ZERO_CHANNEL_VAL) << Y_CHANNEL_SHIFT) | \
+     (((pure & (1 << 2)) ? channel##_CHANNEL_VAL : ZERO_CHANNEL_VAL) << Z_CHANNEL_SHIFT) | \
+     (((pure & (1 << 3)) ? channel##_CHANNEL_VAL : ZERO_CHANNEL_VAL) << W_CHANNEL_SHIFT))
+
+#define i915_fs_operand_pure(pure) \
+    (FS_R0 | \
+     (((pure & (1 << 0)) ? ONE_CHANNEL_VAL : ZERO_CHANNEL_VAL) << X_CHANNEL_SHIFT) | \
+     (((pure & (1 << 1)) ? ONE_CHANNEL_VAL : ZERO_CHANNEL_VAL) << Y_CHANNEL_SHIFT) | \
+     (((pure & (1 << 2)) ? ONE_CHANNEL_VAL : ZERO_CHANNEL_VAL) << Z_CHANNEL_SHIFT) | \
+     (((pure & (1 << 3)) ? ONE_CHANNEL_VAL : ZERO_CHANNEL_VAL) << W_CHANNEL_SHIFT))
+
+static void
+i915_set_shader_program (i915_device_t *device,
+                        const i915_shader_t *shader)
+{
+    uint32_t num_tex_coords;
+    uint32_t num_samplers;
+    uint32_t n;
+    uint32_t texture_offset = 0;
+    uint32_t constant_offset = 0;
+    uint32_t sampler_offset = 0;
+    uint32_t source_reg;
+    uint32_t source_pure;
+    uint32_t mask_reg;
+    uint32_t out_reg;
+    uint32_t dest_reg;
+    FS_LOCALS;
+
+    n = (i915_shader_channel_key (&shader->source) <<  0) |
+       (i915_shader_channel_key (&shader->mask)   <<  8) |
+       (i915_shader_channel_key (&shader->clip)   << 16) |
+       (shader->op << 24) |
+       ((shader->opacity < 1.) << 30) |
+       (((shader->content & CAIRO_CONTENT_ALPHA) == CAIRO_CONTENT_ALPHA) << 31);
+    if (n == device->current_program)
+       return;
+    device->current_program = n;
+
+    FS_BEGIN ();
+
+    if (shader->source.type.fragment == FS_ZERO) {
+       if (shader->clip.type.fragment == FS_TEXTURE) {
+           /* XXX need_combine */
+           assert (shader->mask.type.fragment == (i915_fragment_shader_t) -1);
+           i915_fs_dcl (FS_T0);
+           i915_fs_texld (FS_U0, FS_S0, FS_T0);
+           if ((shader->content & CAIRO_CONTENT_COLOR) == 0)
+               i915_fs_mov (FS_OC, i915_fs_operand (FS_U0, W, W, W, W));
+           else
+               i915_fs_mov (FS_OC, i915_fs_operand (FS_U0, ZERO, ZERO, ZERO, W));
+       } else {
+           i915_fs_mov (FS_OC, i915_fs_operand_zero ());
+       }
+
+       FS_END ();
+       return;
+    }
+
+    num_tex_coords = i915_shader_get_num_tex_coords (shader);
+    for (n = 0; n < num_tex_coords; n++)
+       i915_fs_dcl (FS_T0 + n);
+
+    num_samplers =
+       shader->source.base.n_samplers +
+       shader->mask.base.n_samplers +
+       shader->clip.base.n_samplers +
+       shader->dst.base.n_samplers;
+    for (n = 0; n < num_samplers; n++)
+       i915_fs_dcl (FS_S0 + n);
+
+    source_reg = ~0;
+    source_pure = 0;
+    out_reg = FS_R0;
+    if (! shader->need_combine &&
+       shader->mask.type.fragment == (i915_fragment_shader_t) -1 &&
+       shader->clip.type.fragment != FS_TEXTURE &&
+       shader->content != CAIRO_CONTENT_ALPHA)
+    {
+       out_reg = FS_OC;
+    }
+
+    switch (shader->source.type.fragment) {
+    default:
+    case FS_ZERO:
+    case FS_SPANS:
+       ASSERT_NOT_REACHED;
+
+    case FS_PURE:
+       source_pure = shader->source.solid.pure;
+    case FS_ONE:
+       break;
+
+    case FS_CONSTANT:
+       source_reg = FS_C0;
+       constant_offset += 1;
+       break;
+
+    case FS_DIFFUSE:
+       i915_fs_dcl (FS_T8);
+       source_reg = FS_T8;
+       break;
+
+    case FS_LINEAR:
+       i915_shader_linear_color (device, shader->source.base.mode,
+                                 FS_T0, /* input */
+                                 FS_C0, FS_C1, /* colour ramp */
+                                 FS_U3); /* unpremultiplied output */
+       /* XXX can we defer premultiplication? */
+       i915_fs_mul (out_reg,
+                    i915_fs_operand_reg (FS_U3),
+                    i915_fs_operand (FS_U3, W, W, W, ONE));
+
+       constant_offset += 2;
+       texture_offset += 1;
+       source_reg = out_reg;
+       break;
+
+    case FS_RADIAL:
+       i915_shader_radial_coord (device, shader->source.base.mode,
+                                 FS_T0, /* input */
+                                 FS_C0, FS_C1, /* gradient constants */
+                                 FS_R0); /* coordinate */
+
+       i915_fs_texld (out_reg, FS_S0, FS_R0);
+       constant_offset += 2;
+       texture_offset += 1;
+       sampler_offset += 1;
+       source_reg = out_reg;
+       break;
+
+    case FS_TEXTURE:
+       i915_fs_texld (out_reg, FS_S0, FS_T0);
+       texture_offset += 1;
+       sampler_offset += 1;
+       source_reg = out_reg;
+       break;
+
+    case FS_YUV:
+       /* Load samplers to temporaries. */
+       i915_fs_texld (FS_R0, FS_S0, FS_T0);
+       i915_fs_texld (FS_R1, FS_S1, FS_T0);
+       i915_fs_texld (FS_R2, FS_S2, FS_T0);
+
+       i915_shader_yuv_color (device,
+                              FS_R0, FS_R1, FS_R2, /* y, u, v */
+                              FS_C0, FS_C1, FS_C2, /* coefficients */
+                              out_reg);
+
+       constant_offset += 3;
+       texture_offset += 1;
+       sampler_offset += 3;
+       source_reg = out_reg;
+       break;
+    }
+
+    mask_reg = ~0;
+    switch (shader->mask.type.fragment) {
+    case FS_PURE:
+    case FS_ZERO:
+    case FS_YUV:
+    case FS_DIFFUSE:
+       ASSERT_NOT_REACHED;
+    case FS_ONE:
+    default:
+       break;
+
+    case FS_SPANS:
+       mask_reg = FS_T0 + texture_offset;
+       texture_offset += 1;
+       break;
+
+    case FS_CONSTANT:
+       mask_reg = FS_C0 + constant_offset;
+       constant_offset += 1;
+       break;
+
+    case FS_LINEAR:
+       i915_shader_linear_color (device, shader->mask.base.mode,
+                                 FS_T0 + texture_offset, /* input */
+                                 FS_C0 + constant_offset,
+                                 FS_C0 + constant_offset + 1, /* colour ramp */
+                                 FS_R1); /* unpremultiplied output */
+       constant_offset += 2;
+       texture_offset += 1;
+       mask_reg = FS_R1;
+       break;
+
+    case FS_RADIAL:
+       i915_shader_radial_coord (device, shader->mask.base.mode,
+                                 FS_T0 + texture_offset, /* input */
+                                 FS_C0 + constant_offset,
+                                 FS_C0 + constant_offset + 1, /* gradient constants */
+                                 FS_R1); /* coordinate */
+
+       i915_fs_texld (FS_R1, FS_S0 + sampler_offset, FS_R1);
+       constant_offset += 2;
+       texture_offset += 1;
+       sampler_offset += 1;
+       mask_reg = FS_R1;
+       break;
+
+    case FS_TEXTURE:
+       i915_fs_texld (FS_R1, FS_S0 + sampler_offset, FS_T0 + texture_offset);
+       texture_offset += 1;
+       sampler_offset += 1;
+       mask_reg = FS_R1;
+       break;
+    }
+
+    if (mask_reg != ~0U) {
+       if (! shader->need_combine &&
+           shader->clip.type.fragment != FS_TEXTURE &&
+           (shader->content != CAIRO_CONTENT_ALPHA || source_reg == ~0U))
+       {
+           out_reg = FS_OC;
+       }
+       if (source_reg == ~0U) {
+           if (source_pure) {
+               if (shader->mask.type.fragment == FS_SPANS) {
+                   if (out_reg == FS_OC && shader->content == CAIRO_CONTENT_ALPHA) {
+                       if (source_pure & (1 << 3))
+                           i915_fs_mov (out_reg, i915_fs_operand (mask_reg, X, X, X, X));
+                       else
+                           i915_fs_mov (out_reg, i915_fs_operand_zero ());
+                   } else {
+                       i915_fs_mov (out_reg,
+                                    i915_fs_operand_impure (mask_reg, X, source_pure));
+                   }
+               } else {
+                   /* XXX ComponentAlpha
+                      i915_fs_mov (out_reg,
+                      i915_fs_operand_pure (mask_reg,
+                      shader->source.solid.pure));
+                      */
+                   if (out_reg == FS_OC && shader->content == CAIRO_CONTENT_ALPHA) {
+                       if (source_pure & (1 << 3))
+                           i915_fs_mov (out_reg, i915_fs_operand (mask_reg, W, W, W, W));
+                       else
+                           i915_fs_mov (out_reg, i915_fs_operand_zero ());
+                   } else {
+                       i915_fs_mov (out_reg,
+                                    i915_fs_operand_impure (mask_reg, W, source_pure));
+                   }
+               }
+               source_reg = out_reg;
+           } else if (shader->mask.type.fragment == FS_SPANS) {
+               i915_fs_mov (out_reg,
+                            i915_fs_operand (mask_reg, X, X, X, X));
+               source_reg = out_reg;
+           } else {
+               source_reg = mask_reg;
+           }
+       } else {
+           if (shader->mask.type.fragment == FS_SPANS) {
+                   if (out_reg == FS_OC && shader->content == CAIRO_CONTENT_ALPHA) {
+                       i915_fs_mul (out_reg,
+                                    i915_fs_operand (source_reg, W, W, W, W),
+                                    i915_fs_operand (mask_reg, X, X, X, X));
+                   } else {
+                       i915_fs_mul (out_reg,
+                                    i915_fs_operand_reg (source_reg),
+                                    i915_fs_operand (mask_reg, X, X, X, X));
+                   }
+           } else {
+               /* XXX ComponentAlpha
+               i915_fs_mul (FS_R0,
+                            i915_fs_operand_reg (source_reg),
+                            i915_fs_operand_reg (mask_reg));
+                */
+               if (out_reg == FS_OC && shader->content == CAIRO_CONTENT_ALPHA) {
+                   i915_fs_mul (out_reg,
+                                i915_fs_operand (source_reg, W, W, W, W),
+                                i915_fs_operand (mask_reg, W, W, W, W));
+               } else {
+                   i915_fs_mul (out_reg,
+                                i915_fs_operand_reg (source_reg),
+                                i915_fs_operand (mask_reg, W, W, W, W));
+               }
+           }
+
+           source_reg = out_reg;
+       }
+    }
+
+    if (shader->opacity < 1.) {
+       i915_fs_mul (source_reg,
+                    i915_fs_operand_reg (source_reg),
+                    i915_fs_operand_reg (FS_C0 + constant_offset));
+       constant_offset++;
+    }
+
+    /* need to preserve order of src, mask, clip, dst */
+    mask_reg = ~0;
+    if (shader->clip.type.fragment == FS_TEXTURE) {
+       i915_fs_texld (FS_R1, FS_S0 + sampler_offset, FS_T0 + texture_offset);
+       texture_offset += 1;
+       sampler_offset += 1;
+       mask_reg = FS_R1;
+    }
+
+    if (shader->need_combine) {
+       assert (shader->dst.type.fragment == FS_TEXTURE);
+
+       i915_fs_texld (FS_R2, FS_S0 + sampler_offset, FS_T0 + texture_offset);
+       texture_offset += 1;
+       sampler_offset += 1;
+       dest_reg = FS_R2;
+
+       switch (shader->op) {
+       case CAIRO_OPERATOR_CLEAR:
+       case CAIRO_OPERATOR_SOURCE:
+           ASSERT_NOT_REACHED;
+
+       case CAIRO_OPERATOR_OVER:
+           if (source_reg == ~0U) {
+               /* XXX shader->source.type.fragment == FS_PURE */
+               dest_reg = FS_OC;
+           } else {
+               i915_fs_add (FS_U0,
+                            i915_fs_operand (source_reg, NEG_W, NEG_W, NEG_W, NEG_W),
+                            i915_fs_operand_one ());
+               i915_fs_mul (FS_U0,
+                            i915_fs_operand_reg (FS_U0),
+                            dest_reg);
+               i915_fs_add (FS_R3,
+                            i915_fs_operand_reg (source_reg),
+                            i915_fs_operand_reg (FS_U0));
+               source_reg = FS_R3;
+           }
+           break;
+
+       case CAIRO_OPERATOR_IN:
+           if (source_reg == ~0U) {
+               /* XXX shader->source.type.fragment == FS_PURE */
+               source_reg = dest_reg;
+           } else {
+               i915_fs_mul (FS_R3,
+                            i915_fs_operand_reg (source_reg),
+                            dest_reg);
+               source_reg = FS_R3;
+           }
+           break;
+
+       case CAIRO_OPERATOR_OUT:
+           if (source_reg == ~0U) {
+               /* XXX shader->source.type.fragment == FS_PURE */
+               i915_fs_mov (FS_R3, i915_fs_operand_zero ());
+               source_reg = FS_R3;
+           } else {
+               i915_fs_add (FS_U0,
+                            i915_fs_operand (source_reg, NEG_W, NEG_W, NEG_W, NEG_W),
+                            i915_fs_operand_one ());
+               i915_fs_mul (FS_R3,
+                            i915_fs_operand_reg (FS_U0),
+                            dest_reg);
+               source_reg = FS_R3;
+           }
+           break;
+
+       case CAIRO_OPERATOR_ATOP:
+
+       case CAIRO_OPERATOR_DEST:
+       case CAIRO_OPERATOR_DEST_OVER:
+       case CAIRO_OPERATOR_DEST_IN:
+       case CAIRO_OPERATOR_DEST_OUT:
+       case CAIRO_OPERATOR_DEST_ATOP:
+
+       case CAIRO_OPERATOR_XOR:
+       case CAIRO_OPERATOR_ADD:
+       case CAIRO_OPERATOR_SATURATE:
+
+       case CAIRO_OPERATOR_MULTIPLY:
+       case CAIRO_OPERATOR_SCREEN:
+       case CAIRO_OPERATOR_OVERLAY:
+       case CAIRO_OPERATOR_DARKEN:
+       case CAIRO_OPERATOR_LIGHTEN:
+       case CAIRO_OPERATOR_COLOR_DODGE:
+       case CAIRO_OPERATOR_COLOR_BURN:
+       case CAIRO_OPERATOR_HARD_LIGHT:
+       case CAIRO_OPERATOR_SOFT_LIGHT:
+       case CAIRO_OPERATOR_DIFFERENCE:
+       case CAIRO_OPERATOR_EXCLUSION:
+       case CAIRO_OPERATOR_HSL_HUE:
+       case CAIRO_OPERATOR_HSL_SATURATION:
+       case CAIRO_OPERATOR_HSL_COLOR:
+       case CAIRO_OPERATOR_HSL_LUMINOSITY:
+           ASSERT_NOT_REACHED;
+           break;
+       }
+    }
+
+    if (shader->clip.type.fragment == FS_TEXTURE) {
+       assert (mask_reg != ~0U);
+
+       if (! shader->need_combine) {
+           /* (source IN clip) */
+           if (source_reg == ~0U) {
+               if (source_pure == 0) {
+                   source_reg = mask_reg;
+               } else {
+                   out_reg = FS_OC;
+                   if ((shader->content & CAIRO_CONTENT_COLOR) == 0) {
+                       if (source_pure & (1 << 3))
+                           i915_fs_mov (out_reg, i915_fs_operand (mask_reg, W, W, W, W));
+                       else
+                           i915_fs_mov (out_reg, i915_fs_operand_zero ());
+                   } else {
+                       i915_fs_mov (out_reg,
+                                    i915_fs_operand_impure (mask_reg, W, source_pure));
+                   }
+                   source_reg = out_reg;
+               }
+           } else if (mask_reg) {
+               out_reg = FS_OC;
+               if ((shader->content & CAIRO_CONTENT_COLOR) == 0) {
+                   i915_fs_mul (out_reg,
+                                i915_fs_operand (source_reg, W, W, W, W),
+                                i915_fs_operand (mask_reg, W, W, W, W));
+               } else {
+                   i915_fs_mul (out_reg,
+                                i915_fs_operand_reg (source_reg),
+                                i915_fs_operand (mask_reg, W, W, W, W));
+               }
+
+               source_reg = out_reg;
+           }
+       } else {
+           /* (source OP dest) LERP_clip dest */
+           if (source_reg == ~0U) {
+               if (source_pure == 0) {
+                   i915_fs_mov (FS_R3,
+                                i915_fs_operand (mask_reg, W, W, W, W));
+               } else {
+                   i915_fs_mov (FS_R3,
+                                i915_fs_operand_impure (mask_reg, W, source_pure));
+               }
+           } else {
+               i915_fs_mul (FS_R3,
+                            i915_fs_operand_reg (source_reg),
+                            i915_fs_operand (mask_reg, W, W, W, W));
+           }
+
+           i915_fs_add (mask_reg,
+                        i915_fs_operand_one (),
+                        i915_fs_operand (mask_reg, NEG_W, NEG_W, NEG_W, NEG_W));
+
+           if (dest_reg != FS_OC) {
+               if (dest_reg == ~0U) {
+                   assert (shader->dst.type.fragment == FS_TEXTURE);
+
+                   i915_fs_texld (FS_R2, FS_S0 + sampler_offset, FS_T0 + texture_offset);
+                   texture_offset += 1;
+                   sampler_offset += 1;
+                   dest_reg = FS_R2;
+               }
+
+               i915_fs_mul (FS_U1,
+                            i915_fs_operand_reg (dest_reg),
+                            i915_fs_operand_reg (mask_reg));
+               mask_reg = FS_U1;
+           }
+
+           source_reg = FS_OC;
+           if ((shader->content & CAIRO_CONTENT_COLOR) == 0) {
+               i915_fs_add (source_reg,
+                            i915_fs_operand (FS_R3, W, W, W, W),
+                            i915_fs_operand (mask_reg, W, W, W, W));
+           } else {
+               i915_fs_add (source_reg,
+                            i915_fs_operand_reg (FS_R3),
+                            i915_fs_operand_reg (mask_reg));
+           }
+       }
+    }
+
+    if (source_reg != FS_OC) {
+       if (source_reg == ~0U) {
+           if (source_pure) {
+               if ((shader->content & CAIRO_CONTENT_COLOR) == 0) {
+                   if (source_pure & (1 << 3))
+                       i915_fs_mov (FS_OC, i915_fs_operand_one ());
+                   else
+                       i915_fs_mov (FS_OC, i915_fs_operand_zero ());
+               } else
+                   i915_fs_mov (FS_OC, i915_fs_operand_pure (source_pure));
+           } else {
+               i915_fs_mov (FS_OC, i915_fs_operand_one ());
+           }
+       } else if ((shader->content & CAIRO_CONTENT_COLOR) == 0) {
+           i915_fs_mov (FS_OC, i915_fs_operand (source_reg, W, W, W, W));
+       } else {
+           i915_fs_mov (FS_OC, i915_fs_operand_reg (source_reg));
+       }
+    }
+
+    FS_END ();
+}
+
+static cairo_bool_t
+i915_shader_linear_init (struct i915_shader_linear *l,
+                        const cairo_linear_pattern_t *linear)
+{
+    double x0, y0, sf;
+    double dx, dy, offset;
+
+    dx = linear->pd2.x - linear->pd1.x;
+    dy = linear->pd2.y - linear->pd1.y;
+    sf = dx * dx + dy * dy;
+    if (sf <= 1e-5)
+       return FALSE;
+
+    dx /= sf;
+    dy /= sf;
+
+    x0 = linear->pd1.x;
+    y0 = linear->pd1.y;
+    offset = dx*x0 + dy*y0;
+
+    if (_cairo_matrix_is_identity (&linear->base.base.matrix)) {
+       l->dx = dx;
+       l->dy = dy;
+       l->offset = -offset;
+    } else {
+       cairo_matrix_t m;
+
+       cairo_matrix_init (&m, dx, 0, dy, 0, -offset, 0);
+       cairo_matrix_multiply (&m, &linear->base.base.matrix, &m);
+       l->dx = m.xx;
+       l->dy = m.xy;
+       l->offset = m.x0;
+    }
+
+    return TRUE;
+}
+
+static cairo_bool_t
+i915_shader_linear_contains_rectangle (struct i915_shader_linear *l,
+                                      const cairo_rectangle_int_t *extents)
+{
+    double v;
+
+    v = i915_shader_linear_texcoord (l,
+                                    extents->x,
+                                    extents->y);
+    if (v < 0.)
+       return FALSE;
+    if (v > 1.)
+       return FALSE;
+
+    v = i915_shader_linear_texcoord (l,
+                                    extents->x + extents->width,
+                                    extents->y);
+    if (v < 0.)
+       return FALSE;
+    if (v > 1.)
+       return FALSE;
+
+    v = i915_shader_linear_texcoord (l,
+                                    extents->x,
+                                    extents->y + extents->height);
+    if (v < 0.)
+       return FALSE;
+    if (v > 1.)
+       return FALSE;
+
+    v = i915_shader_linear_texcoord (l,
+                                    extents->x + extents->width,
+                                    extents->y + extents->height);
+    if (v < 0.)
+       return FALSE;
+    if (v > 1.)
+       return FALSE;
+
+    return TRUE;
+}
+
+#define is_pure(C,mask) (((mask) == 0) || (C) <= 0x00ff || (C) >= 0xff00)
+#define is_one(C,mask) (((mask) != 0) && (C) >= 0xff00)
+#define is_zero(C,mask) (((mask) != 0) && (C) <= 0x00ff)
+
+static cairo_status_t
+i915_shader_acquire_solid (i915_shader_t *shader,
+                          union i915_shader_channel *src,
+                          const cairo_solid_pattern_t *solid,
+                          const cairo_rectangle_int_t *extents)
+{
+    cairo_content_t content;
+
+    content = CAIRO_CONTENT_COLOR_ALPHA;
+    src->solid.color = solid->color;
+    if (content == 0 || solid->color.alpha_short <= 0x00ff)
+    {
+       src->base.content = CAIRO_CONTENT_ALPHA;
+       src->type.fragment = FS_ZERO;
+    }
+    else if ((((content & CAIRO_CONTENT_COLOR) == 0)  ||
+             (solid->color.red_short >= 0xff00 &&
+              solid->color.green_short >= 0xff00 &&
+              solid->color.blue_short >= 0xff00)) &&
+            ((content & CAIRO_CONTENT_ALPHA) == 0 ||
+             solid->color.alpha_short >= 0xff00))
+    {
+       src->base.content = CAIRO_CONTENT_ALPHA;
+       src->type.fragment = FS_ONE;
+    }
+    else if (is_pure (solid->color.red_short, content & CAIRO_CONTENT_COLOR) &&
+            is_pure (solid->color.green_short, content & CAIRO_CONTENT_COLOR) &&
+            is_pure (solid->color.blue_short, content & CAIRO_CONTENT_COLOR) &&
+            is_pure (solid->color.alpha_short, content & CAIRO_CONTENT_ALPHA))
+    {
+       src->solid.pure = 0;
+       src->solid.pure |= is_one (solid->color.red_short,   content & CAIRO_CONTENT_COLOR) << 0;
+       src->solid.pure |= is_one (solid->color.green_short, content & CAIRO_CONTENT_COLOR) << 1;
+       src->solid.pure |= is_one (solid->color.blue_short,  content & CAIRO_CONTENT_COLOR) << 2;
+       src->solid.pure |= (! is_zero (solid->color.alpha_short, content & CAIRO_CONTENT_ALPHA)) << 3;
+
+       if (src->solid.pure == 0) {
+           src->base.content = CAIRO_CONTENT_ALPHA;
+           src->type.fragment = FS_ZERO;
+       } else if (src->solid.pure == 0x7) {
+           src->base.content = CAIRO_CONTENT_ALPHA;
+           src->type.fragment = FS_ONE;
+       } else {
+           src->base.content = content;
+           src->type.fragment = FS_PURE;
+           src->base.mode = src->solid.pure;
+       }
+    }
+    else
+    {
+       src->base.content = content;
+       src->type.fragment = src == &shader->source ? FS_DIFFUSE : FS_CONSTANT;
+    }
+    src->type.vertex = src->type.fragment == FS_ZERO ? VS_ZERO : VS_CONSTANT;
+    src->type.pattern = PATTERN_CONSTANT;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+i915_shader_acquire_linear (i915_shader_t *shader,
+                           union i915_shader_channel *src,
+                           const cairo_linear_pattern_t *linear,
+                           const cairo_rectangle_int_t *extents)
+{
+    cairo_bool_t mode = LINEAR_TEXTURE;
+    cairo_status_t status;
+
+    if (i915_shader_linear_init (&src->linear, linear) &&
+       linear->base.n_stops == 2 &&
+       linear->base.stops[0].offset == 0.0 &&
+       linear->base.stops[1].offset == 1.0)
+    {
+       if (i915_shader_linear_contains_rectangle (&src->linear,
+                                                  extents))
+       {
+           /* XXX can also lerp if contained within offset range */
+           mode = LINEAR_NONE;
+       }
+       else switch (linear->base.base.extend) {
+       case CAIRO_EXTEND_REPEAT:
+           mode = LINEAR_REPEAT;
+           break;
+       case CAIRO_EXTEND_PAD:
+           mode = LINEAR_PAD;
+           break;
+       case CAIRO_EXTEND_NONE:
+           break;
+       case CAIRO_EXTEND_REFLECT:
+           break;
+       default:
+           ASSERT_NOT_REACHED;
+           break;
+       }
+    }
+
+    src->type.vertex = VS_LINEAR;
+    src->type.pattern = PATTERN_LINEAR;
+    src->base.texfmt = TEXCOORDFMT_1D;
+    src->base.content = CAIRO_CONTENT_COLOR_ALPHA;
+    src->base.mode = mode;
+    if (mode == LINEAR_TEXTURE) {
+       intel_buffer_t buffer;
+
+       status = intel_gradient_render ((intel_device_t *) shader->target->intel.drm.base.device,
+                                       &linear->base, &buffer);
+       if (unlikely (status))
+           return status;
+
+       src->type.fragment = FS_TEXTURE;
+       src->base.bo = intel_bo_reference (buffer.bo);
+       src->base.n_samplers = 1;
+       src->base.offset[0] = buffer.offset;
+       src->base.map[0] = buffer.map0;
+       src->base.map[1] = buffer.map1;
+       src->base.sampler[0] =
+           (MIPFILTER_NONE << SS2_MIP_FILTER_SHIFT) |
+           i915_texture_filter (CAIRO_FILTER_BILINEAR);
+       src->base.sampler[1] =
+           SS3_NORMALIZED_COORDS |
+           i915_texture_extend (linear->base.base.extend);
+    } else {
+       src->type.fragment = FS_LINEAR;
+       src->linear.color0.red   = linear->base.stops[0].color.red;
+       src->linear.color0.green = linear->base.stops[0].color.green;
+       src->linear.color0.blue  = linear->base.stops[0].color.blue;
+       src->linear.color0.alpha = linear->base.stops[0].color.alpha;
+
+       src->linear.color1.red   = linear->base.stops[1].color.red;
+       src->linear.color1.green = linear->base.stops[1].color.green;
+       src->linear.color1.blue  = linear->base.stops[1].color.blue;
+       src->linear.color1.alpha = linear->base.stops[1].color.alpha;
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+i915_shader_acquire_radial (i915_shader_t *shader,
+                           union i915_shader_channel *src,
+                           const cairo_radial_pattern_t *radial,
+                           const cairo_rectangle_int_t *extents)
+{
+    intel_buffer_t buffer;
+    cairo_status_t status;
+
+    status = intel_gradient_render ((intel_device_t *) shader->target->intel.drm.base.device,
+                                   &radial->base, &buffer);
+    if (unlikely (status))
+       return status;
+
+    i915_shader_radial_init (&src->radial, radial);
+
+    src->type.vertex = VS_TEXTURE;
+    src->type.fragment = FS_RADIAL;
+    src->type.pattern = PATTERN_RADIAL;
+    src->base.texfmt = TEXCOORDFMT_2D;
+
+    src->base.content = CAIRO_CONTENT_COLOR_ALPHA;
+    src->base.bo = intel_bo_reference (buffer.bo);
+    src->base.n_samplers = 1;
+    src->base.offset[0] = buffer.offset;
+    src->base.map[0] = buffer.map0;
+    src->base.map[1] = buffer.map1;
+    src->base.sampler[0] =
+       (MIPFILTER_NONE << SS2_MIP_FILTER_SHIFT) |
+       i915_texture_filter (CAIRO_FILTER_BILINEAR);
+    src->base.sampler[1] =
+       SS3_NORMALIZED_COORDS |
+       i915_texture_extend (radial->base.base.extend);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+i915_surface_clone (i915_device_t *device,
+                   cairo_image_surface_t *image,
+                   i915_surface_t **clone_out)
+{
+    i915_surface_t *clone;
+    cairo_status_t status;
+
+#if 0
+    clone =
+       i915_surface_create_from_cacheable_image_internal (device, image);
+    if (unlikely (clone->intel.drm.base.status))
+       return clone->intel.drm.base.status;
+#else
+    cairo_format_t format;
+
+    format = image->format;
+    if (format == CAIRO_FORMAT_A1)
+       format = CAIRO_FORMAT_A8;
+
+    clone = (i915_surface_t *)
+       i915_surface_create_internal (&device->intel.base,
+                                     format,
+                                     image->width,
+                                     image->height,
+                                     I915_TILING_DEFAULT,
+                                     FALSE);
+    if (unlikely (clone->intel.drm.base.status))
+       return clone->intel.drm.base.status;
+
+    status = intel_bo_put_image (&device->intel,
+                                to_intel_bo (clone->intel.drm.bo),
+                                image,
+                                0, 0,
+                                image->width, image->height,
+                                0, 0);
+
+    if (unlikely (status))
+       return status;
+#endif
+
+    *clone_out = clone;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+i915_surface_clone_subimage (i915_device_t *device,
+                            cairo_image_surface_t *image,
+                            const cairo_rectangle_int_t *extents,
+                            i915_surface_t **clone_out)
+{
+    i915_surface_t *clone;
+    cairo_status_t status;
+    cairo_format_t format;
+
+    format = image->format;
+    if (format == CAIRO_FORMAT_A1)
+       format = CAIRO_FORMAT_A8;
+
+    clone = (i915_surface_t *)
+       i915_surface_create_internal (&device->intel.base,
+                                     format,
+                                     extents->width,
+                                     extents->height,
+                                     I915_TILING_NONE,
+                                     FALSE);
+    if (unlikely (clone->intel.drm.base.status))
+       return clone->intel.drm.base.status;
+
+    status = intel_bo_put_image (&device->intel,
+                                to_intel_bo (clone->intel.drm.bo),
+                                image,
+                                extents->x, extents->y,
+                                extents->width, extents->height,
+                                0, 0);
+
+    if (unlikely (status))
+       return status;
+
+    *clone_out = clone;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+i915_surface_render_pattern (i915_device_t *device,
+                            const cairo_surface_pattern_t *pattern,
+                            const cairo_rectangle_int_t *extents,
+                            i915_surface_t **clone_out)
+{
+    i915_surface_t *clone;
+    cairo_surface_t *image;
+    cairo_status_t status;
+    void *ptr;
+
+    clone = (i915_surface_t *)
+       i915_surface_create_internal (&device->intel.base,
+                                     _cairo_format_from_content (pattern->surface->content),
+                                     extents->width,
+                                     extents->height,
+                                     I915_TILING_NONE,
+                                     FALSE);
+    if (unlikely (clone->intel.drm.base.status))
+       return clone->intel.drm.base.status;
+
+    ptr = intel_bo_map (&device->intel,
+                       to_intel_bo (clone->intel.drm.bo));
+    if (unlikely (ptr == NULL)) {
+       cairo_surface_destroy (&clone->intel.drm.base);
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+    }
+
+    image = cairo_image_surface_create_for_data (ptr,
+                                                clone->intel.drm.format,
+                                                clone->intel.drm.width,
+                                                clone->intel.drm.height,
+                                                clone->intel.drm.stride);
+    if (unlikely (image->status)) {
+       cairo_surface_destroy (&clone->intel.drm.base);
+       return image->status;
+    }
+
+    status = _cairo_surface_offset_paint (image,
+                                         extents->x, extents->y,
+                                         CAIRO_OPERATOR_SOURCE,
+                                         &pattern->base,
+                                         NULL);
+    cairo_surface_destroy (image);
+
+    if (unlikely (status)) {
+       cairo_surface_destroy (&clone->intel.drm.base);
+       return status;
+    }
+
+    *clone_out = clone;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+i915_shader_acquire_solid_surface (i915_shader_t *shader,
+                                  union i915_shader_channel *src,
+                                  cairo_surface_t *surface,
+                                  const cairo_rectangle_int_t *extents)
+{
+    cairo_surface_pattern_t pattern;
+    cairo_surface_t *pixel;
+    cairo_image_surface_t *image;
+    void *image_extra;
+    cairo_status_t status;
+    uint32_t argb;
+
+    status = _cairo_surface_acquire_source_image (surface, &image, &image_extra);
+    if (unlikely (status))
+       return status;
+
+    /* extract the pixel as argb32 */
+    pixel = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 1, 1);
+    _cairo_pattern_init_for_surface (&pattern, &image->base);
+    cairo_matrix_init_translate (&pattern.base.matrix, extents->x, extents->y);
+    pattern.base.filter = CAIRO_FILTER_NEAREST;
+    status = _cairo_surface_paint (pixel, CAIRO_OPERATOR_SOURCE, &pattern.base, NULL);
+    _cairo_pattern_fini (&pattern.base);
+
+    _cairo_surface_release_source_image (surface, image, image_extra);
+
+    if (unlikely (status)) {
+       cairo_surface_destroy (pixel);
+       return status;
+    }
+
+    image = (cairo_image_surface_t *) pixel;
+    argb = *(uint32_t *) image->data;
+    cairo_surface_destroy (pixel);
+
+    if (argb >> 24 == 0) {
+       _cairo_color_init_rgba (&src->solid.color, 0, 0, 0, 0);
+    } else {
+       uint8_t alpha = argb >> 24;
+
+       _cairo_color_init_rgba (&src->solid.color,
+                               ((((argb >> 16) & 0xff) * 255 + alpha / 2) / alpha) / 255.,
+                               ((((argb >>  8) & 0xff) * 255 + alpha / 2) / alpha) / 255.,
+                               ((((argb >>  0) & 0xff) * 255 + alpha / 2) / alpha) / 255.,
+                               alpha / 255.);
+    }
+
+    src->base.content = CAIRO_CONTENT_COLOR_ALPHA;
+    src->type.fragment = FS_CONSTANT;
+    src->type.vertex = VS_CONSTANT;
+    src->type.pattern = PATTERN_CONSTANT;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_filter_t
+sampled_area (const cairo_surface_pattern_t *pattern,
+             const cairo_rectangle_int_t *extents,
+             cairo_rectangle_int_t *sample)
+{
+    cairo_rectangle_int_t surface_extents;
+    cairo_filter_t filter;
+    double x1, x2, y1, y2;
+    double pad;
+
+    x1 = extents->x;
+    y1 = extents->y;
+    x2 = extents->x + (int) extents->width;
+    y2 = extents->y + (int) extents->height;
+
+    if (_cairo_matrix_is_translation (&pattern->base.matrix)) {
+       x1 += pattern->base.matrix.x0; x2 += pattern->base.matrix.x0;
+       y1 += pattern->base.matrix.y0; y2 += pattern->base.matrix.y0;
+    } else {
+       _cairo_matrix_transform_bounding_box (&pattern->base.matrix,
+                                             &x1, &y1, &x2, &y2,
+                                             NULL);
+    }
+
+    filter = _cairo_pattern_analyze_filter (&pattern->base, &pad);
+    sample->x = floor (x1 - pad);
+    sample->y = floor (y1 - pad);
+    sample->width  = ceil (x2 + pad) - sample->x;
+    sample->height = ceil (y2 + pad) - sample->y;
+
+    if (_cairo_surface_get_extents (pattern->surface, &surface_extents))
+       _cairo_rectangle_intersect (sample, &surface_extents);
+
+    return filter;
+}
+
+static cairo_status_t
+i915_shader_acquire_surface (i915_shader_t *shader,
+                            union i915_shader_channel *src,
+                            const cairo_surface_pattern_t *pattern,
+                            const cairo_rectangle_int_t *extents)
+{
+    int surface_width, surface_height;
+    cairo_surface_t *surface, *drm;
+    cairo_extend_t extend;
+    cairo_filter_t filter;
+    cairo_matrix_t m;
+    int src_x = 0, src_y = 0;
+    cairo_surface_t *free_me = NULL;
+    cairo_status_t status;
+    cairo_rectangle_int_t sample;
+
+    assert (src->type.fragment == (i915_fragment_shader_t) -1);
+    drm = surface = pattern->surface;
+
+    extend = pattern->base.extend;
+    src->base.matrix = pattern->base.matrix;
+    filter = sampled_area (pattern, extents, &sample);
+
+    if (surface->type == CAIRO_SURFACE_TYPE_DRM) {
+       if (surface->backend->type == CAIRO_SURFACE_TYPE_SUBSURFACE) {
+           drm = ((cairo_surface_subsurface_t *) surface)->target;
+       } else if (surface->backend->type == CAIRO_INTERNAL_SURFACE_TYPE_SNAPSHOT) {
+           drm = ((cairo_surface_snapshot_t *) surface)->target;
+       }
+    }
+
+    if (drm->type == CAIRO_SURFACE_TYPE_DRM) {
+       i915_surface_t *s = (i915_surface_t *) drm;
+
+       if (surface->backend->type == CAIRO_SURFACE_TYPE_SUBSURFACE) {
+           if (s->intel.drm.base.device == shader->target->intel.drm.base.device &&
+               s != shader->target)
+           {
+               cairo_surface_subsurface_t *sub = (cairo_surface_subsurface_t *) surface;
+               int x;
+
+               status = i915_surface_fallback_flush (s);
+               if (unlikely (status))
+                   return status;
+
+               /* XXX blt subimage and cache snapshot */
+
+               if (to_intel_bo (s->intel.drm.bo)->batch_write_domain) {
+                   /* XXX pipelined flush of RENDER/TEXTURE cache */
+               }
+
+               src->type.fragment = FS_TEXTURE;
+               src->surface.pixel = NONE;
+               surface_width  = sub->extents.width;
+               surface_height = sub->extents.height;
+
+               src->base.bo = intel_bo_reference (to_intel_bo (s->intel.drm.bo));
+               src->base.n_samplers = 1;
+
+               x = sub->extents.x;
+               if (s->intel.drm.format != CAIRO_FORMAT_A8)
+                   x *= 4;
+
+               /* XXX tiling restrictions upon offset? */
+               src->base.offset[0] = s->offset + sub->extents.y * s->intel.drm.stride + x;
+               src->base.map[0] = s->map0;
+               src->base.map[0] &= ~((2047 << MS3_HEIGHT_SHIFT) | (2047 << MS3_WIDTH_SHIFT));
+               src->base.map[0] |=
+                   ((sub->extents.height - 1) << MS3_HEIGHT_SHIFT) |
+                   ((sub->extents.width - 1)  << MS3_WIDTH_SHIFT);
+               src->base.map[1] = (s->intel.drm.stride / 4 - 1) << MS4_PITCH_SHIFT;
+           }
+       } else {
+           /* XXX if s == shader->dst allow if FILTER_NEAREST, EXTEND_NONE? */
+           if (s->intel.drm.base.device == shader->target->intel.drm.base.device) {
+               status = i915_surface_fallback_flush (s);
+               if (unlikely (status))
+                   return status;
+
+               if (s == shader->target || i915_surface_needs_tiling (s)) {
+                   status = i915_surface_copy_subimage (i915_device (shader->target),
+                                                        s, &sample, TRUE, &s);
+                   if (unlikely (status))
+                       return status;
+
+                   free_me = drm = &s->intel.drm.base;
+               }
+
+               src->type.fragment = FS_TEXTURE;
+               src->surface.pixel = NONE;
+
+               surface_width  = s->intel.drm.width;
+               surface_height = s->intel.drm.height;
+
+               src->base.bo = intel_bo_reference (to_intel_bo (s->intel.drm.bo));
+               src->base.n_samplers = 1;
+               src->base.offset[0] = s->offset;
+               src->base.map[0] = s->map0;
+               src->base.map[1] = s->map1;
+           }
+       }
+    }
+
+    if (src->type.fragment == (i915_fragment_shader_t) -1) {
+       i915_surface_t *s;
+
+       if (extents->width == 1 && extents->height == 1) {
+           return i915_shader_acquire_solid_surface (shader, src,
+                                                     surface, extents);
+       }
+
+       s = (i915_surface_t *)
+           _cairo_surface_has_snapshot (surface,
+                                        shader->target->intel.drm.base.backend);
+       if (s == NULL) {
+           cairo_status_t status;
+
+#if 0
+           /* XXX hackity hack hack */
+           status = i915_clone_yuv (surface, src,
+                                    image->width, image->height,
+                                    clone_out);
+#endif
+
+           if (sample.width > 2048 || sample.height > 2048) {
+               status = i915_surface_render_pattern (i915_device (shader->target),
+                                                     pattern, extents,
+                                                     &s);
+               if (unlikely (status))
+                   return status;
+
+               extend = CAIRO_EXTEND_NONE;
+               filter = CAIRO_FILTER_NEAREST;
+               cairo_matrix_init_translate (&src->base.matrix,
+                                            -extents->x, -extents->y);
+           } else {
+               cairo_image_surface_t *image;
+               void *image_extra;
+
+               status = _cairo_surface_acquire_source_image (surface, &image, &image_extra);
+               if (unlikely (status))
+                   return status;
+
+               if (image->width  < 2048 &&
+                   image->height < 2048 &&
+                   sample.width  >= image->width / 4 &&
+                   sample.height >= image->height /4)
+               {
+
+                   status = i915_surface_clone (i915_device (shader->target),
+                                                image, &s);
+
+                   if (likely (status == CAIRO_STATUS_SUCCESS)) {
+                       _cairo_surface_attach_snapshot (surface,
+                                                       &s->intel.drm.base,
+                                                       intel_surface_detach_snapshot);
+
+                       status = intel_snapshot_cache_insert (&i915_device (shader->target)->intel,
+                                                             &s->intel);
+                       if (unlikely (status)) {
+                           cairo_surface_finish (&s->intel.drm.base);
+                           cairo_surface_destroy (&s->intel.drm.base);
+                       }
+                   }
+               }
+               else
+               {
+                   status = i915_surface_clone_subimage (i915_device (shader->target),
+                                                         image, &sample, &s);
+                   src_x = -extents->x;
+                   src_y = -extents->y;
+               }
+
+               _cairo_surface_release_source_image (surface, image, image_extra);
+               if (unlikely (status))
+                   return status;
+           }
+
+           free_me = &s->intel.drm.base;
+       }
+
+       src->type.fragment = FS_TEXTURE;
+       src->surface.pixel = NONE;
+
+       src->base.bo = intel_bo_reference (to_intel_bo (s->intel.drm.bo));
+       src->base.n_samplers = 1;
+       src->base.offset[0] = s->offset;
+       src->base.map[0] = s->map0;
+       src->base.map[1] = s->map1;
+
+       drm = &s->intel.drm.base;
+
+       surface_width  = s->intel.drm.width;
+       surface_height = s->intel.drm.height;
+    }
+
+    /* XXX transform nx1 or 1xn surfaces to 1D */
+
+    src->type.pattern = PATTERN_TEXTURE;
+    if (extend != CAIRO_EXTEND_NONE &&
+       sample.x >= 0 && sample.y >= 0 &&
+       sample.x + sample.width  <= surface_width &&
+       sample.y + sample.height <= surface_height)
+    {
+       extend = CAIRO_EXTEND_NONE;
+    }
+    if (extend == CAIRO_EXTEND_NONE) {
+       src->type.vertex = VS_TEXTURE_16;
+       src->base.texfmt = TEXCOORDFMT_2D_16;
+    } else {
+       src->type.vertex = VS_TEXTURE;
+       src->base.texfmt = TEXCOORDFMT_2D;
+    }
+    src->base.content = drm->content;
+
+    src->base.sampler[0] =
+       (MIPFILTER_NONE << SS2_MIP_FILTER_SHIFT) |
+       i915_texture_filter (filter);
+    src->base.sampler[1] =
+       SS3_NORMALIZED_COORDS |
+       i915_texture_extend (extend);
+
+    /* tweak the src matrix to map from dst to texture coordinates */
+    if (src_x | src_y)
+       cairo_matrix_translate (&src->base.matrix, src_x, src_x);
+    cairo_matrix_init_scale (&m, 1. / surface_width, 1. / surface_height);
+    cairo_matrix_multiply (&src->base.matrix, &src->base.matrix, &m);
+
+    if (free_me != NULL)
+       cairo_surface_destroy (free_me);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_status_t
+i915_shader_acquire_pattern (i915_shader_t *shader,
+                            union i915_shader_channel *src,
+                            const cairo_pattern_t *pattern,
+                            const cairo_rectangle_int_t *extents)
+{
+    switch (pattern->type) {
+    case CAIRO_PATTERN_TYPE_SOLID:
+       return i915_shader_acquire_solid (shader, src,
+                                         (cairo_solid_pattern_t *) pattern,
+                                         extents);
+
+    case CAIRO_PATTERN_TYPE_LINEAR:
+       return i915_shader_acquire_linear (shader, src,
+                                          (cairo_linear_pattern_t *) pattern,
+                                          extents);
+
+    case CAIRO_PATTERN_TYPE_RADIAL:
+       return i915_shader_acquire_radial (shader, src,
+                                          (cairo_radial_pattern_t *) pattern,
+                                          extents);
+
+    case CAIRO_PATTERN_TYPE_SURFACE:
+       return i915_shader_acquire_surface (shader, src,
+                                           (cairo_surface_pattern_t *) pattern,
+                                           extents);
+
+    default:
+       ASSERT_NOT_REACHED;
+       return CAIRO_STATUS_SUCCESS;
+    }
+}
+
+static uint32_t
+i915_get_blend (cairo_operator_t op,
+               i915_surface_t *dst)
+{
+#define SBLEND(X) ((BLENDFACT_##X) << S6_CBUF_SRC_BLEND_FACT_SHIFT)
+#define DBLEND(X) ((BLENDFACT_##X) << S6_CBUF_DST_BLEND_FACT_SHIFT)
+    static const struct blendinfo {
+       cairo_bool_t dst_alpha;
+       uint32_t src_blend;
+       uint32_t dst_blend;
+       enum {
+           BOUNDED,
+           SIMPLE,
+           XRENDER,
+       } kind;
+    } i915_blend_op[] = {
+       {0, SBLEND (ZERO),          DBLEND (ZERO), BOUNDED}, /* Clear */
+       {0, SBLEND (ONE),           DBLEND (ZERO), BOUNDED}, /* Src */
+
+       {0, SBLEND (ONE),           DBLEND (INV_SRC_ALPHA), SIMPLE}, /* Over */
+       {1, SBLEND (DST_ALPHA),     DBLEND (ZERO), XRENDER}, /* In */
+       {1, SBLEND (INV_DST_ALPHA), DBLEND (ZERO), XRENDER}, /* Out */
+       {1, SBLEND (DST_ALPHA),     DBLEND (INV_SRC_ALPHA), SIMPLE}, /* Atop */
+
+       {0, SBLEND (ZERO),          DBLEND (ONE), SIMPLE}, /* Dst */
+       {1, SBLEND (INV_DST_ALPHA), DBLEND (ONE), SIMPLE}, /* OverReverse */
+       {0, SBLEND (ZERO),          DBLEND (SRC_ALPHA), XRENDER}, /* InReverse */
+       {0, SBLEND (ZERO),          DBLEND (INV_SRC_ALPHA), SIMPLE}, /* OutReverse */
+       {1, SBLEND (INV_DST_ALPHA), DBLEND (SRC_ALPHA), XRENDER}, /* AtopReverse */
+
+       {1, SBLEND (INV_DST_ALPHA), DBLEND (INV_SRC_ALPHA), SIMPLE}, /* Xor */
+       {0, SBLEND (ONE),           DBLEND (ONE), SIMPLE}, /* Add */
+       //{0, 0, SBLEND (SRC_ALPHA_SATURATE),       DBLEND (ONE), SIMPLE}, /* XXX Saturate */
+    };
+    uint32_t sblend, dblend;
+
+    if (op >= ARRAY_LENGTH (i915_blend_op))
+       return 0;
+
+    if (i915_blend_op[op].kind == BOUNDED)
+       return 0;
+
+    sblend = i915_blend_op[op].src_blend;
+    dblend = i915_blend_op[op].dst_blend;
+
+    /* If there's no dst alpha channel, adjust the blend op so that we'll treat
+     * it as always 1.
+     */
+    if ((dst->intel.drm.base.content & CAIRO_CONTENT_ALPHA) == 0 &&
+       i915_blend_op[op].dst_alpha)
+    {
+       if (sblend == SBLEND (DST_ALPHA))
+           sblend = SBLEND (ONE);
+       else if (sblend == SBLEND (INV_DST_ALPHA))
+           sblend = SBLEND (ZERO);
+    }
+
+    /* i915 engine reads 8bit color buffer into green channel in cases
+       like color buffer blending etc., and also writes back green channel.
+       So with dst_alpha blend we should use color factor. See spec on
+       "8-bit rendering" */
+    if (dst->intel.drm.format == CAIRO_FORMAT_A8 && i915_blend_op[op].dst_alpha) {
+       if (sblend == SBLEND (DST_ALPHA))
+           sblend = SBLEND (DST_COLR);
+       else if (sblend == SBLEND (INV_DST_ALPHA))
+           sblend = SBLEND (INV_DST_COLR);
+    }
+
+    return sblend | dblend;
+#undef SBLEND
+#undef DBLEND
+}
+
+static void
+i915_shader_channel_init (union i915_shader_channel *channel)
+{
+    channel->type.vertex = (i915_vertex_shader_t) -1;
+    channel->type.fragment = (i915_fragment_shader_t) -1;
+    channel->type.pattern = (i915_shader_channel_t) -1;
+    channel->base.texfmt = TEXCOORDFMT_NOT_PRESENT;
+    channel->base.bo = NULL;
+    channel->base.n_samplers = 0;
+    channel->base.mode = 0;
+}
+
+static void
+i915_shader_channel_fini (i915_device_t *device,
+                          union i915_shader_channel *channel)
+{
+    switch (channel->type.pattern) {
+    case PATTERN_TEXTURE:
+    case PATTERN_BASE:
+    case PATTERN_LINEAR:
+    case PATTERN_RADIAL:
+       if (channel->base.bo != NULL)
+           intel_bo_destroy (&device->intel, channel->base.bo);
+       break;
+
+    default:
+    case PATTERN_CONSTANT:
+       break;
+    }
+}
+
+static void
+i915_shader_channel_reset (i915_device_t *device,
+                          union i915_shader_channel *channel)
+{
+    i915_shader_channel_fini (device, channel);
+    i915_shader_channel_init (channel);
+}
+
+void
+i915_shader_init (i915_shader_t *shader,
+                 i915_surface_t *dst,
+                 cairo_operator_t op,
+                 double opacity)
+{
+    shader->committed = FALSE;
+    shader->device = i915_device (dst);
+    shader->target = dst;
+    shader->op = op;
+    shader->opacity = opacity;
+
+    shader->blend = i915_get_blend (op, dst);
+    shader->need_combine = FALSE;
+
+    shader->content = dst->intel.drm.base.content;
+
+    i915_shader_channel_init (&shader->source);
+    i915_shader_channel_init (&shader->mask);
+    i915_shader_channel_init (&shader->clip);
+    i915_shader_channel_init (&shader->dst);
+}
+
+static void
+i915_set_shader_samplers (i915_device_t *device,
+                         const i915_shader_t *shader)
+{
+    uint32_t n_samplers, n_maps, n;
+    uint32_t samplers[2*4];
+    uint32_t maps[4*4];
+    uint32_t mask, s, m;
+
+    n_maps =
+       shader->source.base.n_samplers +
+       shader->mask.base.n_samplers +
+       shader->clip.base.n_samplers +
+       shader->dst.base.n_samplers;
+    assert (n_maps <= 4);
+
+    if (n_maps == 0)
+       return;
+
+    n_samplers =
+       !! shader->source.base.bo +
+       !! shader->mask.base.bo +
+       !! shader->clip.base.bo +
+       !! shader->dst.base.bo;
+
+    mask  = (1 << n_maps) - 1;
+
+    /* We check for repeated setting of sample state mainly to catch
+     * continuation of text strings across multiple show-glyphs.
+     */
+    s = m = 0;
+    if (shader->source.base.bo != NULL) {
+       samplers[s++] = shader->source.base.sampler[0];
+       samplers[s++] = shader->source.base.sampler[1];
+       maps[m++] = shader->source.base.bo->base.handle;
+       for (n = 0; n < shader->source.base.n_samplers; n++) {
+           maps[m++] = shader->source.base.offset[n];
+           maps[m++] = shader->source.base.map[2*n+0];
+           maps[m++] = shader->source.base.map[2*n+1];
+       }
+    }
+    if (shader->mask.base.bo != NULL) {
+       samplers[s++] = shader->mask.base.sampler[0];
+       samplers[s++] = shader->mask.base.sampler[1];
+       maps[m++] = shader->mask.base.bo->base.handle;
+       for (n = 0; n < shader->mask.base.n_samplers; n++) {
+           maps[m++] = shader->mask.base.offset[n];
+           maps[m++] = shader->mask.base.map[2*n+0];
+           maps[m++] = shader->mask.base.map[2*n+1];
+       }
+    }
+    if (shader->clip.base.bo != NULL) {
+       samplers[s++] = shader->clip.base.sampler[0];
+       samplers[s++] = shader->clip.base.sampler[1];
+       maps[m++] = shader->clip.base.bo->base.handle;
+       for (n = 0; n < shader->clip.base.n_samplers; n++) {
+           maps[m++] = shader->clip.base.offset[n];
+           maps[m++] = shader->clip.base.map[2*n+0];
+           maps[m++] = shader->clip.base.map[2*n+1];
+       }
+    }
+    if (shader->dst.base.bo != NULL) {
+       samplers[s++] = shader->dst.base.sampler[0];
+       samplers[s++] = shader->dst.base.sampler[1];
+       maps[m++] = shader->dst.base.bo->base.handle;
+       for (n = 0; n < shader->dst.base.n_samplers; n++) {
+           maps[m++] = shader->dst.base.offset[n];
+           maps[m++] = shader->dst.base.map[2*n+0];
+           maps[m++] = shader->dst.base.map[2*n+1];
+       }
+    }
+
+    if (n_maps > device->current_n_maps ||
+       memcmp (device->current_maps,
+               maps,
+               m * sizeof (uint32_t)))
+    {
+       memcpy (device->current_maps, maps, m * sizeof (uint32_t));
+       device->current_n_maps = n_maps;
+
+       if (device->current_source != NULL)
+           *device->current_source = 0;
+       if (device->current_mask != NULL)
+           *device->current_mask = 0;
+       if (device->current_clip != NULL)
+           *device->current_clip = 0;
+
+#if 0
+       if (shader->source.type.pattern == PATTERN_TEXTURE) {
+           switch ((int) shader->source.surface.surface->type) {
+           case CAIRO_SURFACE_TYPE_DRM:
+               {
+                   i915_surface_t *surface =
+                       (i915_surface_t *) shader->source.surface.surface;
+                   device->current_source = &surface->is_current_texture;
+                   surface->is_current_texture |= CURRENT_SOURCE;
+                   break;
+               }
+
+           case I915_PACKED_PIXEL_SURFACE_TYPE:
+               {
+                   i915_packed_pixel_surface_t *surface =
+                       (i915_packed_pixel_surface_t *) shader->source.surface.surface;
+                   device->current_source = &surface->is_current_texture;
+                   surface->is_current_texture |= CURRENT_SOURCE;
+                   break;
+               }
+
+           default:
+               device->current_source = NULL;
+               break;
+           }
+       } else
+           device->current_source = NULL;
+
+       if (shader->mask.type.pattern == PATTERN_TEXTURE) {
+           switch ((int) shader->mask.surface.surface->type) {
+           case CAIRO_SURFACE_TYPE_DRM:
+               {
+                   i915_surface_t *surface =
+                       (i915_surface_t *) shader->mask.surface.surface;
+                   device->current_mask = &surface->is_current_texture;
+                   surface->is_current_texture |= CURRENT_MASK;
+                   break;
+               }
+
+           case I915_PACKED_PIXEL_SURFACE_TYPE:
+               {
+                   i915_packed_pixel_surface_t *surface =
+                       (i915_packed_pixel_surface_t *) shader->mask.surface.surface;
+                   device->current_mask = &surface->is_current_texture;
+                   surface->is_current_texture |= CURRENT_MASK;
+                   break;
+               }
+
+           default:
+               device->current_mask = NULL;
+               break;
+           }
+       } else
+           device->current_mask = NULL;
+#endif
+
+       OUT_DWORD (_3DSTATE_MAP_STATE | (3 * n_maps));
+       OUT_DWORD (mask);
+       for (n = 0; n < shader->source.base.n_samplers; n++) {
+           i915_batch_emit_reloc (device, shader->source.base.bo,
+                                  shader->source.base.offset[n],
+                                  I915_GEM_DOMAIN_SAMPLER, 0,
+                                  FALSE);
+           OUT_DWORD (shader->source.base.map[2*n+0]);
+           OUT_DWORD (shader->source.base.map[2*n+1]);
+       }
+       for (n = 0; n < shader->mask.base.n_samplers; n++) {
+           i915_batch_emit_reloc (device, shader->mask.base.bo,
+                                  shader->mask.base.offset[n],
+                                  I915_GEM_DOMAIN_SAMPLER, 0,
+                                  FALSE);
+           OUT_DWORD (shader->mask.base.map[2*n+0]);
+           OUT_DWORD (shader->mask.base.map[2*n+1]);
+       }
+       for (n = 0; n < shader->clip.base.n_samplers; n++) {
+           i915_batch_emit_reloc (device, shader->clip.base.bo,
+                                  shader->clip.base.offset[n],
+                                  I915_GEM_DOMAIN_SAMPLER, 0,
+                                  FALSE);
+           OUT_DWORD (shader->clip.base.map[2*n+0]);
+           OUT_DWORD (shader->clip.base.map[2*n+1]);
+       }
+       for (n = 0; n < shader->dst.base.n_samplers; n++) {
+           i915_batch_emit_reloc (device, shader->dst.base.bo,
+                                  shader->dst.base.offset[n],
+                                  I915_GEM_DOMAIN_SAMPLER, 0,
+                                  FALSE);
+           OUT_DWORD (shader->dst.base.map[2*n+0]);
+           OUT_DWORD (shader->dst.base.map[2*n+1]);
+       }
+    }
+
+    if (n_samplers > device->current_n_samplers ||
+       memcmp (device->current_samplers,
+               samplers,
+               s * sizeof (uint32_t)))
+    {
+       device->current_n_samplers = s;
+       memcpy (device->current_samplers, samplers, s * sizeof (uint32_t));
+
+       OUT_DWORD (_3DSTATE_SAMPLER_STATE | (3 * n_maps));
+       OUT_DWORD (mask);
+       s = 0;
+       for (n = 0; n < shader->source.base.n_samplers; n++) {
+           OUT_DWORD (shader->source.base.sampler[0]);
+           OUT_DWORD (shader->source.base.sampler[1] |
+                      (s << SS3_TEXTUREMAP_INDEX_SHIFT));
+           OUT_DWORD (0x0);
+           s++;
+       }
+       for (n = 0; n < shader->mask.base.n_samplers; n++) {
+           OUT_DWORD (shader->mask.base.sampler[0]);
+           OUT_DWORD (shader->mask.base.sampler[1] |
+                      (s << SS3_TEXTUREMAP_INDEX_SHIFT));
+           OUT_DWORD (0x0);
+           s++;
+       }
+       for (n = 0; n < shader->clip.base.n_samplers; n++) {
+           OUT_DWORD (shader->clip.base.sampler[0]);
+           OUT_DWORD (shader->clip.base.sampler[1] |
+                      (s << SS3_TEXTUREMAP_INDEX_SHIFT));
+           OUT_DWORD (0x0);
+           s++;
+       }
+       for (n = 0; n < shader->dst.base.n_samplers; n++) {
+           OUT_DWORD (shader->dst.base.sampler[0]);
+           OUT_DWORD (shader->dst.base.sampler[1] |
+                      (s << SS3_TEXTUREMAP_INDEX_SHIFT));
+           OUT_DWORD (0x0);
+           s++;
+       }
+    }
+}
+
+static uint32_t
+i915_shader_get_texcoords (const i915_shader_t *shader)
+{
+    uint32_t texcoords;
+    uint32_t tu;
+
+    texcoords = S2_TEXCOORD_NONE;
+    tu = 0;
+    if (shader->source.base.texfmt != TEXCOORDFMT_NOT_PRESENT) {
+       texcoords &= ~S2_TEXCOORD_FMT (tu, S2_TEXCOORD_FMT0_MASK);
+       texcoords |= S2_TEXCOORD_FMT (tu, shader->source.base.texfmt);
+       tu++;
+    }
+    if (shader->mask.base.texfmt != TEXCOORDFMT_NOT_PRESENT) {
+       texcoords &= ~S2_TEXCOORD_FMT (tu, S2_TEXCOORD_FMT0_MASK);
+       texcoords |= S2_TEXCOORD_FMT (tu, shader->mask.base.texfmt);
+       tu++;
+    }
+    if (shader->clip.base.texfmt != TEXCOORDFMT_NOT_PRESENT) {
+       texcoords &= ~S2_TEXCOORD_FMT (tu, S2_TEXCOORD_FMT0_MASK);
+       texcoords |= S2_TEXCOORD_FMT (tu, shader->clip.base.texfmt);
+       tu++;
+    }
+    if (shader->dst.base.texfmt != TEXCOORDFMT_NOT_PRESENT) {
+       texcoords &= ~S2_TEXCOORD_FMT (tu, S2_TEXCOORD_FMT0_MASK);
+       texcoords |= S2_TEXCOORD_FMT (tu, shader->dst.base.texfmt);
+       tu++;
+    }
+
+    return texcoords;
+}
+
+static void
+i915_set_shader_mode (i915_device_t *device,
+                     const i915_shader_t *shader)
+{
+    uint32_t texcoords;
+    uint32_t mask, cnt;
+
+    texcoords = i915_shader_get_texcoords (shader);
+
+    mask = cnt = 0;
+
+    if (device->current_texcoords != texcoords)
+       mask |= I1_LOAD_S (2), cnt++;
+
+    if (device->current_blend != shader->blend)
+       mask |= I1_LOAD_S (6), cnt++;
+
+    if (cnt == 0)
+       return;
+
+    OUT_DWORD (_3DSTATE_LOAD_STATE_IMMEDIATE_1 | mask | (cnt-1));
+
+    if (device->current_texcoords != texcoords) {
+       OUT_DWORD (texcoords);
+       device->current_texcoords = texcoords;
+    }
+
+    if (device->current_blend != shader->blend) {
+       if (shader->blend) {
+           OUT_DWORD (S6_CBUF_BLEND_ENABLE | S6_COLOR_WRITE_ENABLE |
+                      (BLENDFUNC_ADD << S6_CBUF_BLEND_FUNC_SHIFT) |
+                      shader->blend);
+       } else {
+           OUT_DWORD (S6_COLOR_WRITE_ENABLE);
+       }
+
+       device->current_blend = shader->blend;
+    }
+}
+
+static void
+i915_set_constants (i915_device_t *device,
+                   const uint32_t *constants,
+                   uint32_t n_constants)
+{
+    uint32_t n;
+
+    OUT_DWORD (_3DSTATE_PIXEL_SHADER_CONSTANTS | n_constants);
+    OUT_DWORD ((1 << (n_constants >> 2)) - 1);
+
+    for (n = 0; n < n_constants; n++)
+       OUT_DWORD (constants[n]);
+
+    device->current_n_constants = n_constants;
+    memcpy (device->current_constants, constants, n_constants*4);
+}
+
+static uint32_t
+pack_constants (const union i915_shader_channel *channel,
+               uint32_t *constants)
+{
+    uint32_t count = 0, n;
+
+    switch (channel->type.fragment) {
+    case FS_ZERO:
+    case FS_ONE:
+    case FS_PURE:
+    case FS_DIFFUSE:
+       break;
+
+    case FS_CONSTANT:
+       constants[count++] = pack_float (channel->solid.color.red);
+       constants[count++] = pack_float (channel->solid.color.green);
+       constants[count++] = pack_float (channel->solid.color.blue);
+       constants[count++] = pack_float (channel->solid.color.alpha);
+       break;
+
+    case FS_LINEAR:
+       constants[count++] = pack_float (channel->linear.color0.red);
+       constants[count++] = pack_float (channel->linear.color0.green);
+       constants[count++] = pack_float (channel->linear.color0.blue);
+       constants[count++] = pack_float (channel->linear.color0.alpha);
+
+       constants[count++] = pack_float (channel->linear.color1.red);
+       constants[count++] = pack_float (channel->linear.color1.green);
+       constants[count++] = pack_float (channel->linear.color1.blue);
+       constants[count++] = pack_float (channel->linear.color1.alpha);
+       break;
+
+    case FS_RADIAL:
+       for (n = 0; n < ARRAY_LENGTH (channel->radial.constants); n++)
+           constants[count++] = pack_float (channel->radial.constants[n]);
+       break;
+
+    case FS_TEXTURE:
+    case FS_YUV:
+    case FS_SPANS:
+       break;
+    }
+
+    return count;
+}
+
+static void
+i915_set_shader_constants (i915_device_t *device,
+                          const i915_shader_t *shader)
+{
+    uint32_t constants[4*4*3+4];
+    unsigned n_constants;
+
+    n_constants = 0;
+    if (shader->source.type.fragment == FS_DIFFUSE) {
+       uint32_t diffuse;
+
+       diffuse =
+           ((shader->source.solid.color.alpha_short >> 8) << 24) |
+           ((shader->source.solid.color.red_short   >> 8) << 16) |
+           ((shader->source.solid.color.green_short >> 8) << 8) |
+           ((shader->source.solid.color.blue_short  >> 8) << 0);
+
+       if (diffuse != device->current_diffuse) {
+           OUT_DWORD (_3DSTATE_DFLT_DIFFUSE_CMD);
+           OUT_DWORD (diffuse);
+           device->current_diffuse = diffuse;
+       }
+    } else {
+       n_constants += pack_constants (&shader->source, constants + n_constants);
+    }
+    n_constants += pack_constants (&shader->mask, constants + n_constants);
+
+    if (shader->opacity < 1.) {
+       constants[n_constants+0] =
+           constants[n_constants+1] =
+           constants[n_constants+2] =
+           constants[n_constants+3] = pack_float (shader->opacity);
+       n_constants += 4;
+    }
+
+    if (n_constants != 0 &&
+       (device->current_n_constants != n_constants ||
+        memcmp (device->current_constants, constants, n_constants*4)))
+    {
+       i915_set_constants (device, constants, n_constants);
+    }
+}
+
+static cairo_bool_t
+i915_shader_needs_update (const i915_shader_t *shader,
+                         const i915_device_t *device)
+{
+    uint32_t count, n;
+    uint32_t buf[64];
+
+    if (device->current_target != shader->target)
+       return TRUE;
+
+    count =
+       !! shader->source.base.bo +
+       !! shader->mask.base.bo +
+       !! shader->clip.base.bo +
+       !! shader->dst.base.bo;
+    if (count > device->current_n_samplers)
+       return TRUE;
+
+    count =
+       shader->source.base.n_samplers +
+       shader->mask.base.n_samplers +
+       shader->clip.base.n_samplers +
+       shader->dst.base.n_samplers;
+    if (count > device->current_n_maps)
+       return TRUE;
+
+    if (count) {
+       count = 0;
+       if (shader->source.base.bo != NULL) {
+           buf[count++] = shader->source.base.sampler[0];
+           buf[count++] = shader->source.base.sampler[1];
+       }
+       if (shader->mask.base.bo != NULL) {
+           buf[count++] = shader->mask.base.sampler[0];
+           buf[count++] = shader->mask.base.sampler[1];
+       }
+       if (shader->clip.base.bo != NULL) {
+           buf[count++] = shader->clip.base.sampler[0];
+           buf[count++] = shader->clip.base.sampler[1];
+       }
+       if (shader->dst.base.bo != NULL) {
+           buf[count++] = shader->dst.base.sampler[0];
+           buf[count++] = shader->dst.base.sampler[1];
+       }
+       if (memcmp (device->current_samplers, buf, count * sizeof (uint32_t)))
+           return TRUE;
+
+       count = 0;
+       if (shader->source.base.bo != NULL) {
+           buf[count++] = shader->source.base.bo->base.handle;
+           for (n = 0; n < shader->source.base.n_samplers; n++) {
+               buf[count++] = shader->source.base.offset[n];
+               buf[count++] = shader->source.base.map[2*n+0];
+               buf[count++] = shader->source.base.map[2*n+1];
+           }
+       }
+       if (shader->mask.base.bo != NULL) {
+           buf[count++] = shader->mask.base.bo->base.handle;
+           for (n = 0; n < shader->mask.base.n_samplers; n++) {
+               buf[count++] = shader->mask.base.offset[n];
+               buf[count++] = shader->mask.base.map[2*n+0];
+               buf[count++] = shader->mask.base.map[2*n+1];
+           }
+       }
+       if (shader->clip.base.bo != NULL) {
+           buf[count++] = shader->clip.base.bo->base.handle;
+           for (n = 0; n < shader->clip.base.n_samplers; n++) {
+               buf[count++] = shader->clip.base.offset[n];
+               buf[count++] = shader->clip.base.map[2*n+0];
+               buf[count++] = shader->clip.base.map[2*n+1];
+           }
+       }
+       if (shader->dst.base.bo != NULL) {
+           buf[count++] = shader->dst.base.bo->base.handle;
+           for (n = 0; n < shader->dst.base.n_samplers; n++) {
+               buf[count++] = shader->dst.base.offset[n];
+               buf[count++] = shader->dst.base.map[2*n+0];
+               buf[count++] = shader->dst.base.map[2*n+1];
+           }
+       }
+       if (memcmp (device->current_maps, buf, count * sizeof (uint32_t)))
+           return TRUE;
+    }
+
+    if (i915_shader_get_texcoords (shader) != device->current_texcoords)
+       return TRUE;
+    if (device->current_blend != shader->blend)
+       return TRUE;
+
+    count = 0;
+    if (shader->source.type.fragment == FS_DIFFUSE) {
+       uint32_t diffuse;
+
+       diffuse =
+           ((shader->source.solid.color.alpha_short >> 8) << 24) |
+           ((shader->source.solid.color.red_short   >> 8) << 16) |
+           ((shader->source.solid.color.green_short >> 8) << 8) |
+           ((shader->source.solid.color.blue_short  >> 8) << 0);
+
+       if (diffuse != device->current_diffuse)
+           return TRUE;
+    } else {
+       count += pack_constants (&shader->source, buf + count);
+    }
+    count += pack_constants (&shader->mask, buf + count);
+
+    if (count &&
+       (device->current_n_constants != count ||
+        memcmp (device->current_constants, buf, count*4)))
+    {
+       return TRUE;
+    }
+
+    n = (i915_shader_channel_key (&shader->source) <<  0) |
+       (i915_shader_channel_key (&shader->mask)   <<  8) |
+       (i915_shader_channel_key (&shader->clip)   << 16) |
+       (shader->op << 24) |
+       ((shader->opacity < 1.) << 30) |
+       (((shader->content & CAIRO_CONTENT_ALPHA) == CAIRO_CONTENT_ALPHA) << 31);
+    return n != device->current_program;
+}
+
+void
+i915_set_dst (i915_device_t *device, i915_surface_t *dst)
+{
+    uint32_t size;
+
+    if (device->current_target != dst) {
+       intel_bo_t *bo;
+
+       bo = to_intel_bo (dst->intel.drm.bo);
+       assert (bo != NULL);
+
+       OUT_DWORD (_3DSTATE_BUF_INFO_CMD);
+       OUT_DWORD (BUF_3D_ID_COLOR_BACK |
+                  BUF_tiling (bo->tiling) |
+                  BUF_3D_PITCH (dst->intel.drm.stride));
+       OUT_RELOC (dst, I915_GEM_DOMAIN_RENDER, I915_GEM_DOMAIN_RENDER);
+
+       device->current_target = dst;
+    }
+
+    if (dst->colorbuf != device->current_colorbuf) {
+       OUT_DWORD (_3DSTATE_DST_BUF_VARS_CMD);
+       OUT_DWORD (dst->colorbuf);
+       device->current_colorbuf = dst->colorbuf;
+    }
+
+    size = DRAW_YMAX (dst->intel.drm.height) | DRAW_XMAX (dst->intel.drm.width);
+    if (size != device->current_size) {
+       OUT_DWORD (_3DSTATE_DRAW_RECT_CMD);
+       OUT_DWORD (0); /* dither */
+       OUT_DWORD (0); /* top-left */
+       OUT_DWORD (size);
+       OUT_DWORD (0);  /* origin */
+       device->current_size = size;
+    }
+}
+
+static void
+i915_set_shader_target (i915_device_t *device,
+                       const i915_shader_t *shader)
+{
+    i915_set_dst (device, shader->target);
+}
+
+int
+i915_shader_num_texcoords (const i915_shader_t *shader)
+{
+    int cnt = 0;
+
+    switch (shader->source.base.texfmt) {
+    default:
+       ASSERT_NOT_REACHED;
+    case TEXCOORDFMT_NOT_PRESENT: break;
+    case TEXCOORDFMT_2D: cnt += 2; break;
+    case TEXCOORDFMT_3D: cnt += 3; break;
+    case TEXCOORDFMT_4D: cnt += 4; break;
+    case TEXCOORDFMT_1D: cnt += 1; break;
+    case TEXCOORDFMT_2D_16: cnt += 1; break;
+    }
+
+    switch (shader->mask.base.texfmt) {
+    default:
+       ASSERT_NOT_REACHED;
+    case TEXCOORDFMT_NOT_PRESENT: break;
+    case TEXCOORDFMT_2D: cnt += 2; break;
+    case TEXCOORDFMT_3D: cnt += 3; break;
+    case TEXCOORDFMT_4D: cnt += 4; break;
+    case TEXCOORDFMT_1D: cnt += 1; break;
+    case TEXCOORDFMT_2D_16: cnt += 1; break;
+    }
+
+    switch (shader->clip.base.texfmt) {
+    default:
+       ASSERT_NOT_REACHED;
+    case TEXCOORDFMT_NOT_PRESENT: break;
+    case TEXCOORDFMT_2D: cnt += 2; break;
+    case TEXCOORDFMT_3D: cnt += 3; break;
+    case TEXCOORDFMT_4D: cnt += 4; break;
+    case TEXCOORDFMT_1D: cnt += 1; break;
+    case TEXCOORDFMT_2D_16: cnt += 1; break;
+    }
+
+    switch (shader->dst.base.texfmt) {
+    default:
+       ASSERT_NOT_REACHED;
+    case TEXCOORDFMT_NOT_PRESENT: break;
+    case TEXCOORDFMT_2D: cnt += 2; break;
+    case TEXCOORDFMT_3D: cnt += 3; break;
+    case TEXCOORDFMT_4D: cnt += 4; break;
+    case TEXCOORDFMT_1D: cnt += 1; break;
+    case TEXCOORDFMT_2D_16: cnt += 1; break;
+    }
+
+    return cnt;
+}
+
+void
+i915_shader_fini (i915_shader_t *shader)
+{
+    i915_device_t *device = i915_device (shader->target);
+
+    i915_shader_channel_fini (device, &shader->source);
+    i915_shader_channel_fini (device, &shader->mask);
+    i915_shader_channel_fini (device, &shader->clip);
+}
+
+void
+i915_shader_set_clip (i915_shader_t *shader,
+                     cairo_clip_t *clip)
+{
+    cairo_surface_t *clip_surface;
+    int clip_x, clip_y;
+    union i915_shader_channel *channel;
+    i915_surface_t *s;
+
+    clip_surface = _cairo_clip_get_surface (clip, &shader->target->intel.drm.base, &clip_x, &clip_y);
+    assert (clip_surface->status == CAIRO_STATUS_SUCCESS);
+    assert (clip_surface->type == CAIRO_SURFACE_TYPE_DRM);
+
+    channel = &shader->clip;
+    channel->type.vertex = VS_TEXTURE_16;
+    channel->base.texfmt = TEXCOORDFMT_2D_16;
+    channel->base.content = CAIRO_CONTENT_ALPHA;
+
+    channel->type.fragment = FS_TEXTURE;
+    channel->surface.pixel = NONE;
+
+    s = (i915_surface_t *) clip_surface;
+    channel->base.bo = to_intel_bo (s->intel.drm.bo);
+    channel->base.n_samplers = 1;
+    channel->base.offset[0] = s->offset;
+    channel->base.map[0] = s->map0;
+    channel->base.map[1] = s->map1;
+
+    channel->base.sampler[0] =
+       (MIPFILTER_NONE << SS2_MIP_FILTER_SHIFT) |
+       i915_texture_filter (CAIRO_FILTER_NEAREST);
+    channel->base.sampler[1] =
+       SS3_NORMALIZED_COORDS |
+       i915_texture_extend (CAIRO_EXTEND_NONE);
+
+    cairo_matrix_init_scale (&shader->clip.base.matrix,
+                            1. / s->intel.drm.width,
+                            1. / s->intel.drm.height);
+    cairo_matrix_translate (&shader->clip.base.matrix,
+                           -clip_x, -clip_y);
+}
+
+static cairo_status_t
+i915_shader_check_aperture (i915_shader_t *shader,
+                           i915_device_t *device)
+{
+    cairo_status_t status;
+    intel_bo_t *bo_array[4];
+    uint32_t n = 0;
+
+    if (shader->target != device->current_target)
+       bo_array[n++] = to_intel_bo (shader->target->intel.drm.bo);
+
+    if (shader->source.base.bo != NULL)
+       bo_array[n++] = shader->source.base.bo;
+
+    if (shader->mask.base.bo != NULL)
+       bo_array[n++] = shader->mask.base.bo;
+
+    if (shader->clip.base.bo != NULL)
+       bo_array[n++] = shader->clip.base.bo;
+
+    if (n == 0 || i915_check_aperture (device, bo_array, n))
+       return CAIRO_STATUS_SUCCESS;
+
+    status = i915_batch_flush (device);
+    if (unlikely (status))
+       return status;
+
+    assert (i915_check_aperture (device, bo_array, n));
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+i915_shader_combine_mask (i915_shader_t *shader, i915_device_t *device)
+{
+    if (shader->mask.type.fragment == (i915_fragment_shader_t) -1 ||
+       shader->mask.type.fragment == FS_CONSTANT)
+    {
+       return;
+    }
+
+    if (shader->mask.type.fragment == FS_PURE) {
+       if (shader->mask.solid.pure & (1<<3)) {
+           shader->mask.type.fragment = FS_ONE;
+       } else {
+           shader->mask.type.fragment = FS_ZERO;
+       }
+    }
+
+    if (shader->mask.type.fragment == FS_ONE ||
+       (shader->mask.base.content & CAIRO_CONTENT_ALPHA) == 0)
+    {
+       i915_shader_channel_reset (device, &shader->mask);
+    }
+
+    if (shader->mask.type.fragment == FS_ZERO) {
+       i915_shader_channel_fini (device, &shader->source);
+
+       shader->source.type.fragment = FS_ZERO;
+       shader->source.type.vertex = VS_ZERO;
+       shader->source.base.texfmt = TEXCOORDFMT_NOT_PRESENT;
+       shader->source.base.mode = 0;
+       shader->source.base.n_samplers = 0;
+    }
+
+    if (shader->source.type.fragment == FS_ZERO) {
+       i915_shader_channel_reset (device, &shader->mask);
+       i915_shader_channel_reset (device, &shader->clip);
+    }
+}
+
+static void
+i915_shader_setup_dst (i915_shader_t *shader)
+{
+    union i915_shader_channel *channel;
+    i915_surface_t *s;
+
+    /* We need to manual blending if we have a clip surface and an unbounded op,
+     * or an extended blend mode.
+     */
+    if (shader->need_combine ||
+       (shader->op < CAIRO_OPERATOR_SATURATE &&
+        (shader->clip.type.fragment == (i915_fragment_shader_t) -1 ||
+         _cairo_operator_bounded_by_mask (shader->op))))
+    {
+       return;
+    }
+
+    shader->need_combine = TRUE;
+
+    channel = &shader->dst;
+    channel->type.vertex = VS_TEXTURE_16;
+    channel->base.texfmt = TEXCOORDFMT_2D_16;
+    channel->base.content = shader->content;
+
+    channel->type.fragment = FS_TEXTURE;
+    channel->surface.pixel = NONE;
+
+    s = shader->target;
+    channel->base.bo = to_intel_bo (s->intel.drm.bo);
+    channel->base.n_samplers = 1;
+    channel->base.offset[0] = s->offset;
+    channel->base.map[0] = s->map0;
+    channel->base.map[1] = s->map1;
+
+    channel->base.sampler[0] =
+       (MIPFILTER_NONE << SS2_MIP_FILTER_SHIFT) |
+       i915_texture_filter (CAIRO_FILTER_NEAREST);
+    channel->base.sampler[1] =
+       SS3_NORMALIZED_COORDS |
+       i915_texture_extend (CAIRO_EXTEND_NONE);
+
+    cairo_matrix_init_scale (&shader->dst.base.matrix,
+                            1. / s->intel.drm.width,
+                            1. / s->intel.drm.height);
+}
+
+static void
+i915_shader_combine_source (i915_shader_t *shader,
+                           i915_device_t *device)
+{
+    if (device->last_source_fragment == shader->source.type.fragment)
+       return;
+
+    if (device->last_source_fragment == FS_DIFFUSE) {
+       switch (shader->source.type.fragment) {
+       case FS_ONE:
+       case FS_PURE:
+       case FS_CONSTANT:
+       case FS_DIFFUSE:
+           shader->source.type.fragment = FS_DIFFUSE;
+           shader->source.base.mode = 0;
+           break;
+       case FS_ZERO:
+       case FS_LINEAR:
+       case FS_RADIAL:
+       case FS_TEXTURE:
+       case FS_YUV:
+       case FS_SPANS:
+       default:
+           break;
+       }
+    }
+
+    device->last_source_fragment = shader->source.type.fragment;
+}
+
+static inline float *
+i915_composite_vertex (float *v,
+                      const i915_shader_t *shader,
+                      double x, double y)
+{
+    double s, t;
+
+    /* Each vertex is:
+     *   2 vertex coordinates
+     *   [0-2] source texture coordinates
+     *   [0-2] mask texture coordinates
+     */
+
+    *v++ = x; *v++ = y;
+    switch (shader->source.type.vertex) {
+    case VS_ZERO:
+    case VS_CONSTANT:
+       break;
+    case VS_LINEAR:
+       *v++ = i915_shader_linear_texcoord (&shader->source.linear, x, y);
+       break;
+    case VS_TEXTURE:
+       s = x, t = y;
+       cairo_matrix_transform_point (&shader->source.base.matrix, &s, &t);
+       *v++ = s; *v++ = t;
+       break;
+    case VS_TEXTURE_16:
+       s = x, t = y;
+       cairo_matrix_transform_point (&shader->source.base.matrix, &s, &t);
+       *v++ = texcoord_2d_16 (s, t);
+       break;
+    }
+    switch (shader->mask.type.vertex) {
+    case VS_ZERO:
+    case VS_CONSTANT:
+       break;
+    case VS_LINEAR:
+       *v++ = i915_shader_linear_texcoord (&shader->mask.linear, x, y);
+       break;
+    case VS_TEXTURE:
+       s = x, t = y;
+       cairo_matrix_transform_point (&shader->mask.base.matrix, &s, &t);
+       *v++ = s; *v++ = t;
+       break;
+    case VS_TEXTURE_16:
+       s = x, t = y;
+       cairo_matrix_transform_point (&shader->mask.base.matrix, &s, &t);
+       *v++ = texcoord_2d_16 (s, t);
+       break;
+    }
+
+    return v;
+}
+
+static inline void
+i915_shader_add_rectangle_general (const i915_shader_t *shader,
+                                  int x, int y,
+                                  int w, int h)
+{
+    float *vertices;
+
+    vertices = i915_add_rectangle (shader->device);
+    vertices = i915_composite_vertex (vertices, shader, x + w, y + h);
+    vertices = i915_composite_vertex (vertices, shader, x, y + h);
+    vertices = i915_composite_vertex (vertices, shader, x, y);
+    /* XXX overflow! */
+}
+
+void
+i915_vbo_flush (i915_device_t *device)
+{
+    assert (device->floats_per_vertex);
+    assert (device->vertex_count);
+
+    if (device->vbo == 0) {
+       OUT_DWORD (_3DSTATE_LOAD_STATE_IMMEDIATE_1 |
+                  I1_LOAD_S (0) |
+                  I1_LOAD_S (1) |
+                  1);
+       device->vbo = device->batch.used++;
+       device->vbo_max_index = device->batch.used;
+       OUT_DWORD ((device->floats_per_vertex << S1_VERTEX_WIDTH_SHIFT) |
+                  (device->floats_per_vertex << S1_VERTEX_PITCH_SHIFT));
+    }
+
+    OUT_DWORD (PRIM3D_RECTLIST |
+              PRIM3D_INDIRECT_SEQUENTIAL |
+              device->vertex_count);
+    OUT_DWORD (device->vertex_index);
+
+    device->vertex_index += device->vertex_count;
+    device->vertex_count = 0;
+}
+
+cairo_status_t
+i915_shader_commit (i915_shader_t *shader,
+                   i915_device_t *device)
+{
+    unsigned floats_per_vertex;
+    cairo_status_t status;
+
+    assert (CAIRO_MUTEX_IS_LOCKED (device->intel.base.base.mutex));
+
+    if (! shader->committed) {
+       device->shader = shader;
+
+       i915_shader_combine_mask (shader, device);
+       i915_shader_combine_source (shader, device);
+       i915_shader_setup_dst (shader);
+
+       shader->add_rectangle = i915_shader_add_rectangle_general;
+
+       if ((status = setjmp (shader->unwind)))
+           return status;
+
+       shader->committed = TRUE;
+    }
+
+    if (i915_shader_needs_update (shader, device)) {
+       if (i915_batch_space (device) < 256) {
+           status = i915_batch_flush (device);
+           if (unlikely (status))
+               return status;
+       }
+
+       if (device->vertex_count)
+           i915_vbo_flush (device);
+
+       status = i915_shader_check_aperture (shader, device);
+       if (unlikely (status))
+           return status;
+
+  update_shader:
+       i915_set_shader_target (device, shader);
+       i915_set_shader_mode (device, shader);
+       i915_set_shader_samplers (device, shader);
+       i915_set_shader_constants (device, shader);
+       i915_set_shader_program (device, shader);
+    }
+
+    floats_per_vertex = 2 + i915_shader_num_texcoords (shader);
+    if (device->floats_per_vertex == floats_per_vertex)
+       return CAIRO_STATUS_SUCCESS;
+
+    if (i915_batch_space (device) < 8) {
+       status = i915_batch_flush (device);
+       if (unlikely (status))
+           return status;
+
+       goto update_shader;
+    }
+
+    if (device->vertex_count)
+       i915_vbo_flush (device);
+
+    if (device->vbo) {
+       device->batch_base[device->vbo_max_index] |= device->vertex_index;
+       OUT_DWORD (_3DSTATE_LOAD_STATE_IMMEDIATE_1 | I1_LOAD_S (1) | 0);
+       device->vbo_max_index = device->batch.used;
+       OUT_DWORD ((floats_per_vertex << S1_VERTEX_WIDTH_SHIFT) |
+                  (floats_per_vertex << S1_VERTEX_PITCH_SHIFT));
+    }
+
+    device->floats_per_vertex = floats_per_vertex;
+    device->rectangle_size = floats_per_vertex * 3 * sizeof (float);
+    device->vertex_index =
+       (device->vbo_used + 4*floats_per_vertex - 1) / (4 * floats_per_vertex);
+    device->vbo_offset = 4 * device->vertex_index * floats_per_vertex;
+
+    return CAIRO_STATUS_SUCCESS;
+}
diff --git a/src/drm/cairo-drm-i915-spans.c b/src/drm/cairo-drm-i915-spans.c
new file mode 100644 (file)
index 0000000..b3f4e0a
--- /dev/null
@@ -0,0 +1,799 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-composite-rectangles-private.h"
+#include "cairo-boxes-private.h"
+#include "cairo-error-private.h"
+#include "cairo-drm-i915-private.h"
+
+/* Operates in either immediate or retained mode.
+ * When given a clip region we record the sequence of vbo and then
+ * replay them for each clip rectangle, otherwise we simply emit
+ * the vbo straight into the command stream.
+ */
+
+typedef struct _i915_spans i915_spans_t;
+
+typedef float *
+(*i915_get_rectangle_func_t) (i915_spans_t *spans);
+
+typedef void
+(*i915_span_func_t) (i915_spans_t *spans,
+                    int x0, int x1, int y0, int y1,
+                    int alpha);
+
+struct _i915_spans {
+    cairo_span_renderer_t renderer;
+
+    i915_device_t *device;
+
+    int xmin, xmax;
+    cairo_bool_t is_bounded;
+    const cairo_rectangle_int_t *extents;
+
+    i915_get_rectangle_func_t get_rectangle;
+    i915_span_func_t span;
+    i915_shader_t shader;
+
+    cairo_region_t *clip_region;
+    cairo_bool_t need_clip_surface;
+
+    struct vbo {
+       struct vbo *next;
+       intel_bo_t *bo;
+       unsigned int count;
+    } head, *tail;
+
+    unsigned int vbo_offset;
+    float *vbo_base;
+};
+
+static float *
+i915_emit_rectangle (i915_spans_t *spans)
+{
+    return i915_add_rectangle (spans->device);
+}
+
+static float *
+i915_accumulate_rectangle (i915_spans_t *spans)
+{
+    float *vertices;
+    uint32_t size;
+
+    size = spans->device->rectangle_size;
+    if (unlikely (spans->vbo_offset + size > I915_VBO_SIZE)) {
+       struct vbo *vbo;
+
+       vbo = malloc (sizeof (struct vbo));
+       if (unlikely (vbo == NULL)) {
+           /* throw error! */
+       }
+
+       spans->tail->next = vbo;
+       spans->tail = vbo;
+
+       vbo->next = NULL;
+       vbo->bo = intel_bo_create (&spans->device->intel,
+                                  I915_VBO_SIZE, I915_VBO_SIZE,
+                                  FALSE, I915_TILING_NONE, 0);
+       vbo->count = 0;
+
+       spans->vbo_offset = 0;
+       spans->vbo_base = intel_bo_map (&spans->device->intel, vbo->bo);
+    }
+
+    vertices = spans->vbo_base + spans->vbo_offset;
+    spans->vbo_offset += size;
+    spans->tail->count += 3;
+
+    return vertices;
+}
+
+static void
+i915_span_zero (i915_spans_t *spans,
+               int x0, int x1, int y0, int y1,
+               int alpha)
+{
+    float *vertices;
+
+    vertices = spans->get_rectangle (spans);
+
+    *vertices++ = x1;
+    *vertices++ = y1;
+
+    *vertices++ = x0;
+    *vertices++ = y1;
+
+    *vertices++ = x0;
+    *vertices++ = y0;
+}
+
+static void
+i915_span_constant (i915_spans_t *spans,
+                   int x0, int x1, int y0, int y1,
+                   int alpha)
+{
+    float *vertices;
+    float a = alpha / 255.;
+
+    vertices = spans->get_rectangle (spans);
+
+    *vertices++ = x1;
+    *vertices++ = y1;
+    *vertices++ = a;
+
+    *vertices++ = x0;
+    *vertices++ = y1;
+    *vertices++ = a;
+
+    *vertices++ = x0;
+    *vertices++ = y0;
+    *vertices++ = a;
+}
+
+static void
+i915_span_linear (i915_spans_t *spans,
+                 int x0, int x1, int y0, int y1,
+                 int alpha)
+{
+    float *vertices;
+    float a = alpha / 255.;
+    double s, t;
+
+    vertices = spans->get_rectangle (spans);
+
+    *vertices++ = x1;
+    *vertices++ = y1;
+    s = x0, t = y0;
+    *vertices++ = i915_shader_linear_texcoord (&spans->shader.source.linear, s, t);
+    *vertices++ = a;
+
+    *vertices++ = x0;
+    *vertices++ = y1;
+    s = x1, t = y0;
+    *vertices++ = i915_shader_linear_texcoord (&spans->shader.source.linear, s, t);
+    *vertices++ = a;
+
+    *vertices++ = x0;
+    *vertices++ = y0;
+    s = x1, t = y1;
+    *vertices++ = i915_shader_linear_texcoord (&spans->shader.source.linear, s, t);
+    *vertices++ = a;
+}
+
+static void
+i915_span_texture (i915_spans_t *spans,
+                  int x0, int x1, int y0, int y1,
+                  int alpha)
+{
+    float *vertices;
+    float a = alpha / 255.;
+    double s, t;
+
+    vertices = spans->get_rectangle (spans);
+
+    *vertices++ = x1;
+    *vertices++ = y1;
+    s = x0, t = y0;
+    cairo_matrix_transform_point (&spans->shader.source.base.matrix, &s, &t);
+    *vertices++ = s; *vertices++ = t;
+    *vertices++ = a;
+
+    *vertices++ = x0;
+    *vertices++ = y1;
+    s = x1, t = y0;
+    cairo_matrix_transform_point (&spans->shader.source.base.matrix, &s, &t);
+    *vertices++ = s; *vertices++ = t;
+    *vertices++ = a;
+
+    *vertices++ = x0;
+    *vertices++ = y0;
+    s = x1, t = y1;
+    cairo_matrix_transform_point (&spans->shader.source.base.matrix, &s, &t);
+    *vertices++ = s; *vertices++ = t;
+    *vertices++ = a;
+}
+
+static void
+i915_span_texture16 (i915_spans_t *spans,
+                    int x0, int x1, int y0, int y1, int alpha)
+{
+    float *vertices;
+    float a = alpha / 255.;
+    double s, t;
+
+    vertices = spans->get_rectangle (spans);
+
+    *vertices++ = x1;
+    *vertices++ = y1;
+    s = x0, t = y0;
+    cairo_matrix_transform_point (&spans->shader.source.base.matrix, &s, &t);
+    *vertices++ = texcoord_2d_16 (s, t);
+    *vertices++ = a;
+
+    *vertices++ = x0;
+    *vertices++ = y1;
+    s = x1, t = y0;
+    cairo_matrix_transform_point (&spans->shader.source.base.matrix, &s, &t);
+    *vertices++ = texcoord_2d_16 (s, t);
+    *vertices++ = a;
+
+    *vertices++ = x0;
+    *vertices++ = y0;
+    s = x1, t = y1;
+    cairo_matrix_transform_point (&spans->shader.source.base.matrix, &s, &t);
+    *vertices++ = texcoord_2d_16 (s, t);
+    *vertices++ = a;
+}
+
+static void
+i915_span_generic (i915_spans_t *spans,
+                  int x0, int x1, int y0, int y1, int alpha)
+{
+    double s, t;
+    float *vertices;
+    float a = alpha / 255.;
+
+    /* Each vertex is:
+     *   2 vertex coordinates
+     *   [0-2] source texture coordinates
+     *   1 alpha value.
+     *   [0,2] clip mask coordinates
+     */
+
+    vertices = spans->get_rectangle (spans);
+
+    /* bottom right */
+    *vertices++ = x1; *vertices++ = y1;
+    s = x1, t = y1;
+    switch (spans->shader.source.type.vertex) {
+    case VS_ZERO:
+    case VS_CONSTANT:
+       break;
+    case VS_LINEAR:
+       *vertices++ = i915_shader_linear_texcoord (&spans->shader.source.linear, s, t);
+       break;
+    case VS_TEXTURE:
+       cairo_matrix_transform_point (&spans->shader.source.base.matrix, &s, &t);
+       *vertices++ = s; *vertices++ = t;
+       break;
+    case VS_TEXTURE_16:
+       cairo_matrix_transform_point (&spans->shader.source.base.matrix, &s, &t);
+       *vertices++ = texcoord_2d_16 (s, t);
+       break;
+    }
+    *vertices++ = a;
+    if (spans->need_clip_surface) {
+       s = x1, t = y1;
+       cairo_matrix_transform_point (&spans->shader.clip.base.matrix, &s, &t);
+       *vertices++ = texcoord_2d_16 (s, t);
+    }
+    if (spans->shader.need_combine) {
+       s = x1, t = y1;
+       cairo_matrix_transform_point (&spans->shader.dst.base.matrix, &s, &t);
+       *vertices++ = texcoord_2d_16 (s, t);
+    }
+
+    /* bottom left */
+    *vertices++ = x0; *vertices++ = y1;
+    s = x0, t = y1;
+    switch (spans->shader.source.type.vertex) {
+    case VS_ZERO:
+    case VS_CONSTANT:
+       break;
+    case VS_LINEAR:
+       *vertices++ = i915_shader_linear_texcoord (&spans->shader.source.linear, s, t);
+       break;
+    case VS_TEXTURE:
+       cairo_matrix_transform_point (&spans->shader.source.base.matrix, &s, &t);
+       *vertices++ = s; *vertices++ = t;
+       break;
+    case VS_TEXTURE_16:
+       cairo_matrix_transform_point (&spans->shader.source.base.matrix, &s, &t);
+       *vertices++ = texcoord_2d_16 (s, t);
+       break;
+    }
+    *vertices++ = a;
+    if (spans->need_clip_surface) {
+       s = x0, t = y1;
+       cairo_matrix_transform_point (&spans->shader.clip.base.matrix, &s, &t);
+       *vertices++ = texcoord_2d_16 (s, t);
+    }
+    if (spans->shader.need_combine) {
+       s = x0, t = y1;
+       cairo_matrix_transform_point (&spans->shader.dst.base.matrix, &s, &t);
+       *vertices++ = texcoord_2d_16 (s, t);
+    }
+
+    /* top left */
+    *vertices++ = x0; *vertices++ = y0;
+    s = x0, t = y0;
+    switch (spans->shader.source.type.vertex) {
+    case VS_ZERO:
+    case VS_CONSTANT:
+       break;
+    case VS_LINEAR:
+       *vertices++ = i915_shader_linear_texcoord (&spans->shader.source.linear, s, t);
+       break;
+    case VS_TEXTURE:
+       cairo_matrix_transform_point (&spans->shader.source.base.matrix, &s, &t);
+       *vertices++ = s; *vertices++ = t;
+       break;
+    case VS_TEXTURE_16:
+       cairo_matrix_transform_point (&spans->shader.source.base.matrix, &s, &t);
+       *vertices++ = texcoord_2d_16 (s, t);
+       break;
+    }
+    *vertices++ = a;
+    if (spans->need_clip_surface) {
+       s = x0, t = y0;
+       cairo_matrix_transform_point (&spans->shader.clip.base.matrix, &s, &t);
+       *vertices++ = texcoord_2d_16 (s, t);
+    }
+    if (spans->shader.need_combine) {
+       s = x0, t = y0;
+       cairo_matrix_transform_point (&spans->shader.dst.base.matrix, &s, &t);
+       *vertices++ = texcoord_2d_16 (s, t);
+    }
+}
+
+static cairo_status_t
+i915_zero_spans_mono (void *abstract_renderer,
+                     int y, int height,
+                     const cairo_half_open_span_t *half,
+                     unsigned num_spans)
+{
+    i915_spans_t *spans = abstract_renderer;
+    int x0, x1;
+
+    if (num_spans == 0)
+       return CAIRO_STATUS_SUCCESS;
+
+    do {
+       while (num_spans && half[0].coverage < 128)
+           half++, num_spans--;
+       if (num_spans == 0)
+           break;
+
+       x0 = x1 = half[0].x;
+       while (num_spans--) {
+           half++;
+
+           x1 = half[0].x;
+           if (half[0].coverage < 128)
+               break;
+       }
+
+       i915_span_zero (spans,
+                       x0, x1,
+                       y, y + height,
+                       0);
+    } while (num_spans);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+i915_zero_spans (void *abstract_renderer,
+                int y, int height,
+                const cairo_half_open_span_t *half,
+                unsigned num_spans)
+{
+    i915_spans_t *spans = abstract_renderer;
+    int x0, x1;
+
+    if (num_spans == 0)
+       return CAIRO_STATUS_SUCCESS;
+
+    do {
+       while (num_spans && half[0].coverage == 0)
+           half++, num_spans--;
+       if (num_spans == 0)
+           break;
+
+       x0 = x1 = half[0].x;
+       while (num_spans--) {
+           half++;
+
+           x1 = half[0].x;
+           if (half[0].coverage == 0)
+               break;
+       }
+
+       i915_span_zero (spans,
+                       x0, x1,
+                       y, y + height,
+                       0);
+    } while (num_spans);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+i915_bounded_spans_mono (void *abstract_renderer,
+                        int y, int height,
+                        const cairo_half_open_span_t *half,
+                        unsigned num_spans)
+{
+    i915_spans_t *spans = abstract_renderer;
+
+    if (num_spans == 0)
+       return CAIRO_STATUS_SUCCESS;
+
+    do {
+       if (half[0].coverage >= 128) {
+           spans->span (spans,
+                        half[0].x, half[1].x,
+                        y, y + height,
+                        255);
+       }
+       half++;
+    } while (--num_spans > 1);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+i915_bounded_spans (void *abstract_renderer,
+                   int y, int height,
+                   const cairo_half_open_span_t *half,
+                   unsigned num_spans)
+{
+    i915_spans_t *spans = abstract_renderer;
+
+    if (num_spans == 0)
+       return CAIRO_STATUS_SUCCESS;
+
+    do {
+       if (half[0].coverage) {
+           spans->span (spans,
+                        half[0].x, half[1].x,
+                        y, y + height,
+                        half[0].coverage);
+       }
+       half++;
+    } while (--num_spans > 1);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+i915_unbounded_spans (void *abstract_renderer,
+                     int y, int height,
+                     const cairo_half_open_span_t *half,
+                     unsigned num_spans)
+{
+    i915_spans_t *spans = abstract_renderer;
+
+    if (num_spans == 0) {
+       spans->span (spans,
+                    spans->xmin, spans->xmax,
+                    y, y + height,
+                    0);
+       return CAIRO_STATUS_SUCCESS;
+    }
+
+    if (half[0].x != spans->xmin) {
+       spans->span (spans,
+                    spans->xmin, half[0].x,
+                    y, y + height,
+                    0);
+    }
+
+    do {
+       spans->span (spans,
+                    half[0].x, half[1].x,
+                    y, y + height,
+                    half[0].coverage);
+       half++;
+    } while (--num_spans > 1);
+
+    if (half[0].x != spans->xmax) {
+       spans->span (spans,
+                    half[0].x, spans->xmax,
+                    y, y + height,
+                    0);
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+i915_unbounded_spans_mono (void *abstract_renderer,
+                          int y, int height,
+                          const cairo_half_open_span_t *half,
+                          unsigned num_spans)
+{
+    i915_spans_t *spans = abstract_renderer;
+
+    if (num_spans == 0) {
+       spans->span (spans,
+                    spans->xmin, spans->xmax,
+                    y, y + height,
+                    0);
+       return CAIRO_STATUS_SUCCESS;
+    }
+
+    if (half[0].x != spans->xmin) {
+       spans->span (spans,
+                    spans->xmin, half[0].x,
+                    y, y + height,
+                    0);
+    }
+
+    do {
+       int alpha = 0;
+       if (half[0].coverage >= 128)
+           alpha = 255;
+       spans->span (spans,
+                    half[0].x, half[1].x,
+                    y, y + height,
+                    alpha);
+       half++;
+    } while (--num_spans > 1);
+
+    if (half[0].x != spans->xmax) {
+       spans->span (spans,
+                    half[0].x, spans->xmax,
+                    y, y + height,
+                    0);
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+i915_spans_init (i915_spans_t *spans,
+                i915_surface_t *dst,
+                cairo_operator_t op,
+                const cairo_pattern_t *pattern,
+                cairo_antialias_t antialias,
+                cairo_clip_t *clip,
+                double opacity,
+                const cairo_composite_rectangles_t *extents)
+{
+    cairo_status_t status;
+
+    spans->device = (i915_device_t *) dst->intel.drm.base.device;
+
+    spans->is_bounded = extents->is_bounded;
+    if (extents->is_bounded) {
+       if (antialias == CAIRO_ANTIALIAS_NONE)
+           spans->renderer.render_rows = i915_bounded_spans_mono;
+       else
+           spans->renderer.render_rows = i915_bounded_spans;
+
+       spans->extents = &extents->bounded;
+    } else {
+       if (antialias == CAIRO_ANTIALIAS_NONE)
+           spans->renderer.render_rows = i915_unbounded_spans_mono;
+       else
+           spans->renderer.render_rows = i915_unbounded_spans;
+
+       spans->extents = &extents->unbounded;
+    }
+    spans->xmin = spans->extents->x;
+    spans->xmax = spans->extents->x + spans->extents->width;
+
+    spans->clip_region = NULL;
+    spans->need_clip_surface = FALSE;
+    if (clip != NULL) {
+       cairo_region_t *clip_region = NULL;
+
+       status = _cairo_clip_get_region (clip, &clip_region);
+       assert (status == CAIRO_STATUS_SUCCESS || status == CAIRO_INT_STATUS_UNSUPPORTED);
+
+       if (clip_region != NULL && cairo_region_num_rectangles (clip_region) == 1)
+           clip_region = NULL;
+
+       spans->clip_region = clip_region;
+       spans->need_clip_surface = status == CAIRO_INT_STATUS_UNSUPPORTED;
+    }
+
+    spans->head.next = NULL;
+    spans->head.bo = NULL;
+    spans->head.count = 0;
+    spans->tail = &spans->head;
+
+    if (spans->clip_region == NULL) {
+       spans->get_rectangle = i915_emit_rectangle;
+    } else {
+       assert (! extents->is_bounded);
+       spans->get_rectangle = i915_accumulate_rectangle;
+       spans->head.bo = intel_bo_create (&spans->device->intel,
+                                         I915_VBO_SIZE, I915_VBO_SIZE,
+                                         FALSE, I915_TILING_NONE, 0);
+       if (unlikely (spans->head.bo == NULL))
+           return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+       spans->vbo_base = intel_bo_map (&spans->device->intel, spans->head.bo);
+    }
+    spans->vbo_offset = 0;
+
+    i915_shader_init (&spans->shader, dst, op, opacity);
+    if (spans->need_clip_surface)
+       i915_shader_set_clip (&spans->shader, clip);
+
+    status = i915_shader_acquire_pattern (&spans->shader, &spans->shader.source,
+                                         pattern, &extents->bounded);
+    if (unlikely (status))
+       return status;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+i915_spans_fini (i915_spans_t *spans)
+{
+    i915_shader_fini (&spans->shader);
+
+    if (spans->head.bo != NULL) {
+       struct vbo *vbo, *next;
+
+       intel_bo_destroy (&spans->device->intel, spans->head.bo);
+       for (vbo = spans->head.next; vbo != NULL; vbo = next) {
+           next = vbo->next;
+           intel_bo_destroy (&spans->device->intel, vbo->bo);
+           free (vbo);
+       }
+    }
+}
+
+cairo_status_t
+i915_clip_and_composite_spans (i915_surface_t          *dst,
+                              cairo_operator_t          op,
+                              const cairo_pattern_t    *pattern,
+                              cairo_antialias_t         antialias,
+                              i915_spans_func_t         draw_func,
+                              void                     *draw_closure,
+                              const cairo_composite_rectangles_t*extents,
+                              cairo_clip_t             *clip,
+                              double opacity)
+{
+    i915_spans_t spans;
+    i915_device_t *device;
+    cairo_status_t status;
+    struct vbo *vbo;
+
+    if (i915_surface_needs_tiling (dst)) {
+       ASSERT_NOT_REACHED;
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+    }
+
+    if (op == CAIRO_OPERATOR_CLEAR) {
+       pattern = &_cairo_pattern_white.base;
+       op = CAIRO_OPERATOR_DEST_OUT;
+    }
+
+    status = i915_spans_init (&spans, dst, op, pattern, antialias, clip, opacity, extents);
+    if (unlikely (status))
+       return status;
+
+    spans.shader.mask.base.texfmt = TEXCOORDFMT_1D;
+    spans.shader.mask.base.content = CAIRO_CONTENT_ALPHA;
+    spans.shader.mask.type.fragment = FS_SPANS;
+
+    status = cairo_device_acquire (dst->intel.drm.base.device);
+    if (unlikely (status))
+       goto CLEANUP_SPANS;
+
+    if (dst->deferred_clear) {
+       status = i915_surface_clear (dst);
+       if (unlikely (status))
+           goto CLEANUP_SPANS;
+    }
+
+    device = i915_device (dst);
+    status = i915_shader_commit (&spans.shader, device);
+    if (unlikely (status))
+       goto CLEANUP_DEVICE;
+
+    if (! spans.shader.need_combine && ! spans.need_clip_surface) {
+       switch (spans.shader.source.type.vertex) {
+       case VS_ZERO:
+           spans.span = i915_span_zero;
+           if (extents->is_bounded) {
+               if (antialias == CAIRO_ANTIALIAS_NONE)
+                   spans.renderer.render_rows = i915_zero_spans_mono;
+               else
+                   spans.renderer.render_rows = i915_zero_spans;
+           }
+           break;
+       case VS_CONSTANT:
+           spans.span = i915_span_constant;
+           break;
+       case VS_LINEAR:
+           spans.span = i915_span_linear;
+           break;
+       case VS_TEXTURE:
+           spans.span = i915_span_texture;
+           break;
+       case VS_TEXTURE_16:
+           spans.span = i915_span_texture16;
+           break;
+       default:
+           spans.span = i915_span_generic;
+           break;
+       }
+    } else {
+       spans.span = i915_span_generic;
+    }
+
+    status = draw_func (draw_closure, &spans.renderer, spans.extents);
+    if (spans.clip_region != NULL && status == CAIRO_STATUS_SUCCESS) {
+       i915_vbo_finish (device);
+
+       OUT_DWORD (_3DSTATE_SCISSOR_ENABLE_CMD | ENABLE_SCISSOR_RECT);
+       for (vbo = &spans.head; vbo != NULL; vbo = vbo->next) {
+           int i, num_rectangles;
+
+           /* XXX require_space & batch_flush */
+
+           OUT_DWORD (_3DSTATE_LOAD_STATE_IMMEDIATE_1 | I1_LOAD_S (0) | I1_LOAD_S (1) | 1);
+           i915_batch_emit_reloc (device, vbo->bo, 0,
+                                  I915_GEM_DOMAIN_VERTEX, 0,
+                                  FALSE);
+           OUT_DWORD ((device->floats_per_vertex << S1_VERTEX_WIDTH_SHIFT) |
+                      (device->floats_per_vertex << S1_VERTEX_PITCH_SHIFT) |
+                      vbo->count);
+
+           num_rectangles = cairo_region_num_rectangles (spans.clip_region);
+           for (i = 0; i < num_rectangles; i++) {
+               cairo_rectangle_int_t rect;
+
+               cairo_region_get_rectangle (spans.clip_region, i, &rect);
+
+               OUT_DWORD (_3DSTATE_SCISSOR_RECT_0_CMD);
+               OUT_DWORD (SCISSOR_RECT_0_XMIN (rect.x) |
+                          SCISSOR_RECT_0_YMIN (rect.y));
+               OUT_DWORD (SCISSOR_RECT_0_XMAX (rect.x + rect.width) |
+                          SCISSOR_RECT_0_YMAX (rect.y + rect.height));
+
+               OUT_DWORD (PRIM3D_RECTLIST | PRIM3D_INDIRECT_SEQUENTIAL | vbo->count);
+               OUT_DWORD (0);
+           }
+       }
+       OUT_DWORD (_3DSTATE_SCISSOR_ENABLE_CMD | DISABLE_SCISSOR_RECT);
+    }
+
+CLEANUP_DEVICE:
+    cairo_device_release (dst->intel.drm.base.device);
+CLEANUP_SPANS:
+    i915_spans_fini (&spans);
+
+    return status;
+}
diff --git a/src/drm/cairo-drm-i915-surface.c b/src/drm/cairo-drm-i915-surface.c
new file mode 100644 (file)
index 0000000..2a55889
--- /dev/null
@@ -0,0 +1,2936 @@
+/* Cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Chris Wilson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * **************************************************************************
+ * This work was initially based upon xf86-video-intel/src/i915_render.c:
+ * Copyright © 2006 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Authors:
+ *    Wang Zhenyu <zhenyu.z.wang@intel.com>
+ *    Eric Anholt <eric@anholt.net>
+ *
+ * **************************************************************************
+ * and also upon libdrm/intel/intel_bufmgr_gem.c:
+ * Copyright © 2007 Red Hat Inc.
+ * Copyright © 2007 Intel Corporation
+ * Copyright 2006 Tungsten Graphics, Inc., Bismarck, ND., USA
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sub license, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ * Authors: Thomas Hellström <thomas-at-tungstengraphics-dot-com>
+ *          Keith Whitwell <keithw-at-tungstengraphics-dot-com>
+ *          Eric Anholt <eric@anholt.net>
+ *          Dave Airlie <airlied@linux.ie>
+ */
+
+/* XXX
+ *
+ * - Per thread context? Would it actually avoid many locks?
+ *
+ */
+
+#include "cairoint.h"
+
+#include "cairo-drm-private.h"
+#include "cairo-drm-ioctl-private.h"
+#include "cairo-drm-intel-private.h"
+#include "cairo-drm-intel-command-private.h"
+#include "cairo-drm-intel-ioctl-private.h"
+#include "cairo-drm-i915-private.h"
+
+#include "cairo-boxes-private.h"
+#include "cairo-cache-private.h"
+#include "cairo-composite-rectangles-private.h"
+#include "cairo-default-context-private.h"
+#include "cairo-error-private.h"
+#include "cairo-freelist-private.h"
+#include "cairo-list-private.h"
+#include "cairo-path-fixed-private.h"
+#include "cairo-region-private.h"
+#include "cairo-surface-offset-private.h"
+
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <errno.h>
+
+static const uint32_t i915_batch_setup[] = {
+    /* Disable line anti-aliasing */
+    _3DSTATE_AA_CMD,
+
+    /* Disable independent alpha blend */
+    _3DSTATE_INDEPENDENT_ALPHA_BLEND_CMD |
+       IAB_MODIFY_ENABLE |
+       IAB_MODIFY_FUNC | (BLENDFUNC_ADD << IAB_FUNC_SHIFT) |
+       IAB_MODIFY_SRC_FACTOR | (BLENDFACT_ONE << IAB_SRC_FACTOR_SHIFT) |
+       IAB_MODIFY_DST_FACTOR | (BLENDFACT_ZERO << IAB_DST_FACTOR_SHIFT),
+
+    /* Disable texture crossbar */
+    _3DSTATE_COORD_SET_BINDINGS |
+       CSB_TCB (0, 0) |
+       CSB_TCB (1, 1) |
+       CSB_TCB (2, 2) |
+       CSB_TCB (3, 3) |
+       CSB_TCB (4, 4) |
+       CSB_TCB (5, 5) |
+       CSB_TCB (6, 6) |
+       CSB_TCB (7, 7),
+
+    _3DSTATE_MODES_4_CMD | ENABLE_LOGIC_OP_FUNC | LOGIC_OP_FUNC (LOGICOP_COPY),
+
+    _3DSTATE_LOAD_STATE_IMMEDIATE_1 |
+       I1_LOAD_S (2) |
+       I1_LOAD_S (3) |
+       I1_LOAD_S (4) |
+       I1_LOAD_S (5) |
+       I1_LOAD_S (6) |
+       4,
+    S2_TEXCOORD_NONE,
+    0, /* Disable texture coordinate wrap-shortest */
+    (1 << S4_POINT_WIDTH_SHIFT) |
+       S4_LINE_WIDTH_ONE |
+       S4_FLATSHADE_ALPHA |
+       S4_FLATSHADE_FOG |
+       S4_FLATSHADE_SPECULAR |
+       S4_FLATSHADE_COLOR |
+       S4_CULLMODE_NONE |
+       S4_VFMT_XY,
+    0, /* Disable stencil buffer */
+    S6_COLOR_WRITE_ENABLE,
+
+    _3DSTATE_SCISSOR_ENABLE_CMD | DISABLE_SCISSOR_RECT,
+
+    /* disable indirect state */
+    _3DSTATE_LOAD_INDIRECT,
+    0,
+};
+
+static const cairo_surface_backend_t i915_surface_backend;
+
+static cairo_surface_t *
+i915_surface_create_from_cacheable_image (cairo_drm_device_t *base_dev,
+                                          cairo_surface_t *source);
+
+static cairo_status_t
+i915_bo_exec (i915_device_t *device, intel_bo_t *bo, uint32_t offset)
+{
+    struct drm_i915_gem_execbuffer2 execbuf;
+    int ret, cnt, i;
+
+    /* Add the batch buffer to the validation list.  */
+    cnt = device->batch.exec_count;
+    if (cnt > 0 && bo->base.handle == device->batch.exec[cnt-1].handle)
+       i = cnt - 1;
+    else
+       i = device->batch.exec_count++;
+    device->batch.exec[i].handle = bo->base.handle;
+    device->batch.exec[i].relocation_count = device->batch.reloc_count;
+    device->batch.exec[i].relocs_ptr = (uintptr_t) device->batch.reloc;
+    device->batch.exec[i].alignment = 0;
+    device->batch.exec[i].offset = 0;
+    device->batch.exec[i].flags = 0;
+    device->batch.exec[i].rsvd1 = 0;
+    device->batch.exec[i].rsvd2 = 0;
+
+    execbuf.buffers_ptr = (uintptr_t) device->batch.exec;
+    execbuf.buffer_count = device->batch.exec_count;
+    execbuf.batch_start_offset = offset;
+    execbuf.batch_len = (device->batch.used << 2) + sizeof (device->batch_header);
+    execbuf.DR1 = 0;
+    execbuf.DR4 = 0;
+    execbuf.num_cliprects = 0;
+    execbuf.cliprects_ptr = 0;
+    execbuf.flags = 0;
+    execbuf.rsvd1 = 0;
+    execbuf.rsvd2 = 0;
+
+    do {
+       ret = ioctl (device->intel.base.fd, DRM_IOCTL_I915_GEM_EXECBUFFER2, &execbuf);
+    } while (ret != 0 && errno == EINTR);
+
+    if (device->debug & I915_DEBUG_SYNC && ret == 0)
+       ret = ! intel_bo_wait (&device->intel, bo);
+
+    if (0 && ret) {
+       int n, m;
+
+       fprintf (stderr, "Batch submission failed: %d\n", errno);
+       fprintf (stderr, "   relocation entries: %d/%d\n",
+                device->batch.reloc_count, I915_MAX_RELOCS);
+       fprintf (stderr, "   gtt size: (%zd/%zd), (%zd/%zd)\n",
+                device->batch.est_gtt_size, device->batch.gtt_avail_size,
+                device->batch.total_gtt_size, device->intel.gtt_avail_size);
+
+       fprintf (stderr, "   buffers:\n");
+       for (n = 0; n < device->batch.exec_count; n++) {
+           fprintf (stderr, "  exec[%d] = %d, %d/%d bytes, gtt = %qx\n",
+                   n,
+                   device->batch.exec[n].handle,
+                   n == device->batch.exec_count - 1 ? bo->base.size : device->batch.target_bo[n]->base.size,
+                   n == device->batch.exec_count - 1 ? bo->full_size : device->batch.target_bo[n]->full_size,
+                   device->batch.exec[n].offset);
+       }
+       for (n = 0; n < device->batch.reloc_count; n++) {
+           for (m = 0; m < device->batch.exec_count; m++)
+               if (device->batch.exec[m].handle == device->batch.reloc[n].target_handle)
+                   break;
+
+           fprintf (stderr, "  reloc[%d] = %d @ %qx -> %qx + %qx\n", n,
+                    device->batch.reloc[n].target_handle,
+                    device->batch.reloc[n].offset,
+                    (unsigned long long) device->batch.exec[m].offset,
+                    (unsigned long long) device->batch.reloc[n].delta);
+
+           device->batch_base[(device->batch.reloc[n].offset - sizeof (device->batch_header)) / 4] =
+               device->batch.exec[m].offset + device->batch.reloc[n].delta;
+       }
+
+       intel_dump_batchbuffer (device->batch_header,
+                               execbuf.batch_len,
+                               device->intel.base.chip_id);
+    }
+    assert (ret == 0);
+
+    VG (VALGRIND_MAKE_MEM_DEFINED (device->batch.exec, sizeof (device->batch.exec[0]) * i));
+
+    bo->offset = device->batch.exec[i].offset;
+    bo->busy = TRUE;
+    if (bo->virtual)
+       intel_bo_unmap (bo);
+    bo->cpu = FALSE;
+
+    while (cnt--) {
+       intel_bo_t *bo = device->batch.target_bo[cnt];
+
+       bo->offset = device->batch.exec[cnt].offset;
+       bo->exec = NULL;
+       bo->busy = TRUE;
+       bo->batch_read_domains = 0;
+       bo->batch_write_domain = 0;
+       cairo_list_del (&bo->cache_list);
+
+       if (bo->virtual)
+           intel_bo_unmap (bo);
+       bo->cpu = FALSE;
+
+       intel_bo_destroy (&device->intel, bo);
+    }
+    assert (cairo_list_is_empty (&device->intel.bo_in_flight));
+
+    device->batch.exec_count = 0;
+    device->batch.reloc_count = 0;
+    device->batch.fences = 0;
+
+    device->batch.est_gtt_size = I915_BATCH_SIZE;
+    device->batch.total_gtt_size = I915_BATCH_SIZE;
+
+    return ret == 0 ? CAIRO_STATUS_SUCCESS : _cairo_error (CAIRO_STATUS_NO_MEMORY);
+}
+
+void
+i915_batch_add_reloc (i915_device_t *device,
+                     uint32_t pos,
+                     intel_bo_t *bo,
+                     uint32_t offset,
+                     uint32_t read_domains,
+                     uint32_t write_domain,
+                     cairo_bool_t needs_fence)
+{
+    int index;
+
+    assert (offset < bo->base.size);
+
+    if (bo->exec == NULL) {
+       device->batch.total_gtt_size += bo->base.size;
+
+       if (! bo->busy)
+           device->batch.est_gtt_size += bo->base.size;
+
+       assert (device->batch.exec_count < ARRAY_LENGTH (device->batch.exec));
+
+       index = device->batch.exec_count++;
+       device->batch.exec[index].handle = bo->base.handle;
+       device->batch.exec[index].relocation_count = 0;
+       device->batch.exec[index].relocs_ptr = 0;
+       device->batch.exec[index].alignment = 0;
+       device->batch.exec[index].offset = 0;
+       device->batch.exec[index].flags = 0;
+       device->batch.exec[index].rsvd1 = 0;
+       device->batch.exec[index].rsvd2 = 0;
+
+       device->batch.target_bo[index] = intel_bo_reference (bo);
+
+       bo->exec = &device->batch.exec[index];
+    }
+
+    if (bo->tiling != I915_TILING_NONE) {
+       uint32_t alignment;
+
+#if 0
+       /* We presume that we will want to use a fence with X tiled objects... */
+       if (needs_fence || bo->tiling == I915_TILING_X)
+           alignment = bo->full_size;
+       else
+           alignment = 2*((bo->stride + 4095) & -4096);
+#else
+       alignment = bo->full_size;
+#endif
+       if (bo->exec->alignment < alignment)
+           bo->exec->alignment = alignment;
+
+       if (needs_fence && (bo->exec->flags & EXEC_OBJECT_NEEDS_FENCE) == 0) {
+           bo->exec->flags |= EXEC_OBJECT_NEEDS_FENCE;
+           device->batch.fences++;
+
+           intel_bo_set_tiling (&device->intel, bo);
+       }
+    }
+
+    assert (device->batch.reloc_count < ARRAY_LENGTH (device->batch.reloc));
+
+    index = device->batch.reloc_count++;
+    device->batch.reloc[index].offset = (pos << 2) + sizeof (device->batch_header);
+    device->batch.reloc[index].delta = offset;
+    device->batch.reloc[index].target_handle = bo->base.handle;
+    device->batch.reloc[index].read_domains = read_domains;
+    device->batch.reloc[index].write_domain = write_domain;
+    device->batch.reloc[index].presumed_offset = bo->offset;
+
+    assert (write_domain == 0 || bo->batch_write_domain == 0 || bo->batch_write_domain == write_domain);
+    bo->batch_read_domains |= read_domains;
+    bo->batch_write_domain |= write_domain;
+}
+
+void
+i915_vbo_finish (i915_device_t *device)
+{
+    intel_bo_t *vbo;
+
+    assert (CAIRO_MUTEX_IS_LOCKED (device->intel.base.base.mutex));
+    assert (device->vbo_used);
+
+    if (device->vertex_count) {
+       if (device->vbo == 0) {
+           OUT_DWORD (_3DSTATE_LOAD_STATE_IMMEDIATE_1 |
+                      I1_LOAD_S (0) |
+                      I1_LOAD_S (1) |
+                      1);
+           device->vbo = device->batch.used++;
+           device->vbo_max_index = device->batch.used;
+           OUT_DWORD ((device->floats_per_vertex << S1_VERTEX_WIDTH_SHIFT) |
+                      (device->floats_per_vertex << S1_VERTEX_PITCH_SHIFT));
+       }
+
+       OUT_DWORD (PRIM3D_RECTLIST |
+                  PRIM3D_INDIRECT_SEQUENTIAL |
+                  device->vertex_count);
+       OUT_DWORD (device->vertex_index);
+    }
+
+    if (device->last_vbo != NULL) {
+       intel_bo_in_flight_add (&device->intel, device->last_vbo);
+       intel_bo_destroy (&device->intel, device->last_vbo);
+    }
+
+    device->batch_base[device->vbo_max_index] |= device->vertex_index + device->vertex_count;
+
+    /* will include a few bytes of inter-array padding */
+    vbo = intel_bo_create (&device->intel,
+                          device->vbo_used, device->vbo_used,
+                          FALSE, I915_TILING_NONE, 0);
+    i915_batch_fill_reloc (device, device->vbo, vbo, 0,
+                          I915_GEM_DOMAIN_VERTEX, 0);
+    intel_bo_write (&device->intel, vbo, 0, device->vbo_used, device->vbo_base);
+    device->last_vbo = vbo;
+    device->last_vbo_offset = (device->vbo_used+7)&-8;
+    device->last_vbo_space = vbo->base.size - device->last_vbo_offset;
+
+    device->vbo = 0;
+
+    device->vbo_used = device->vbo_offset = 0;
+    device->vertex_index = device->vertex_count = 0;
+
+    if (! i915_check_aperture_size (device, 1, I915_VBO_SIZE, I915_VBO_SIZE)) {
+       cairo_status_t status;
+
+       status = i915_batch_flush (device);
+       if (unlikely (status))
+           longjmp (device->shader->unwind, status);
+
+       status = i915_shader_commit (device->shader, device);
+       if (unlikely (status))
+           longjmp (device->shader->unwind, status);
+    }
+}
+
+/* XXX improve state tracker/difference and flush state on vertex emission */
+static void
+i915_device_reset (i915_device_t *device)
+{
+    if (device->current_source != NULL)
+       *device->current_source = 0;
+    if (device->current_mask != NULL)
+       *device->current_mask = 0;
+    if (device->current_clip != NULL)
+       *device->current_clip = 0;
+
+    device->current_target = NULL;
+    device->current_size = 0;
+    device->current_source = NULL;
+    device->current_mask = NULL;
+    device->current_clip = NULL;
+    device->current_texcoords = ~0;
+    device->current_blend = 0;
+    device->current_n_constants = 0;
+    device->current_n_samplers = 0;
+    device->current_n_maps = 0;
+    device->current_colorbuf = 0;
+    device->current_diffuse = 0;
+    device->current_program = ~0;
+    device->clear_alpha = ~0;
+
+    device->last_source_fragment = ~0;
+}
+
+static void
+i915_batch_cleanup (i915_device_t *device)
+{
+    int i;
+
+    for (i = 0; i < device->batch.exec_count; i++) {
+       intel_bo_t *bo = device->batch.target_bo[i];
+
+       bo->exec = NULL;
+       bo->batch_read_domains = 0;
+       bo->batch_write_domain = 0;
+       cairo_list_del (&bo->cache_list);
+
+       intel_bo_destroy (&device->intel, bo);
+    }
+
+    device->batch.exec_count = 0;
+    device->batch.reloc_count = 0;
+}
+
+static void
+i915_batch_vbo_finish (i915_device_t *device)
+{
+    assert (CAIRO_MUTEX_IS_LOCKED (device->intel.base.base.mutex));
+
+    if (device->vbo || i915_batch_space (device) < (int32_t) device->vbo_used) {
+       intel_bo_t *vbo;
+
+       if (device->vertex_count) {
+           if (device->vbo == 0) {
+               OUT_DWORD (_3DSTATE_LOAD_STATE_IMMEDIATE_1 |
+                          I1_LOAD_S (0) |
+                          I1_LOAD_S (1) |
+                          1);
+               device->vbo = device->batch.used++;
+               device->vbo_max_index = device->batch.used;
+               OUT_DWORD ((device->floats_per_vertex << S1_VERTEX_WIDTH_SHIFT) |
+                       (device->floats_per_vertex << S1_VERTEX_PITCH_SHIFT));
+           }
+
+           OUT_DWORD (PRIM3D_RECTLIST |
+                      PRIM3D_INDIRECT_SEQUENTIAL |
+                      device->vertex_count);
+           OUT_DWORD (device->vertex_index);
+       }
+
+       if (device->last_vbo != NULL)
+           intel_bo_destroy (&device->intel, device->last_vbo);
+
+       device->batch_base[device->vbo_max_index] |= device->vertex_index + device->vertex_count;
+
+       /* will include a few bytes of inter-array padding */
+       vbo = intel_bo_create (&device->intel,
+                              device->vbo_used, device->vbo_used,
+                              FALSE, I915_TILING_NONE, 0);
+       i915_batch_fill_reloc (device, device->vbo,
+                              vbo, 0,
+                              I915_GEM_DOMAIN_VERTEX, 0);
+       intel_bo_write (&device->intel, vbo, 0, device->vbo_used, device->vbo_base);
+       device->last_vbo = vbo;
+       device->last_vbo_offset = (device->vbo_used+7)&-8;
+       device->last_vbo_space = vbo->base.size - device->last_vbo_offset;
+
+       device->vbo = 0;
+    }
+    else
+    {
+       /* Only a single rectlist in this batch, and no active vertex buffer. */
+       OUT_DWORD (PRIM3D_RECTLIST | (device->vbo_used / 4 - 1));
+
+       memcpy (BATCH_PTR (device), device->vbo_base, device->vbo_used);
+       device->batch.used += device->vbo_used >> 2;
+    }
+
+    device->vbo_used = device->vbo_offset = 0;
+    device->vertex_index = device->vertex_count = 0;
+}
+
+cairo_status_t
+i915_batch_flush (i915_device_t *device)
+{
+    intel_bo_t *batch;
+    cairo_status_t status;
+    uint32_t length, offset;
+    int n;
+
+    assert (CAIRO_MUTEX_IS_LOCKED (device->intel.base.base.mutex));
+
+    if (device->vbo_used)
+       i915_batch_vbo_finish (device);
+
+    if (device->batch.used == 0)
+       return CAIRO_STATUS_SUCCESS;
+
+    i915_batch_emit_dword (device, MI_BATCH_BUFFER_END);
+    if ((device->batch.used & 1) != ((sizeof (device->batch_header)>>2) & 1))
+       i915_batch_emit_dword (device, MI_NOOP);
+
+    length = (device->batch.used << 2) + sizeof (device->batch_header);
+
+    /* NB: it is faster to copy the data then map/unmap the batch,
+     * presumably because we frequently only use a small part of the buffer.
+     */
+    batch = NULL;
+    if (device->last_vbo) {
+       if (length <= device->last_vbo_space) {
+           batch = device->last_vbo;
+           offset = device->last_vbo_offset;
+
+           /* fixup the relocations */
+           for (n = 0; n < device->batch.reloc_count; n++)
+               device->batch.reloc[n].offset += offset;
+       } else
+           intel_bo_destroy (&device->intel, device->last_vbo);
+       device->last_vbo = NULL;
+    }
+    if (batch == NULL) {
+       batch = intel_bo_create (&device->intel,
+                                length, length,
+                                FALSE, I915_TILING_NONE, 0);
+       if (unlikely (batch == NULL)) {
+           status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+           i915_batch_cleanup (device);
+           goto BAIL;
+       }
+
+       offset = 0;
+    }
+    intel_bo_write (&device->intel, batch, offset, length, device->batch_header);
+    status = i915_bo_exec (device, batch, offset);
+    intel_bo_destroy (&device->intel, batch);
+
+BAIL:
+    device->batch.used = 0;
+
+    intel_glyph_cache_unpin (&device->intel);
+    intel_snapshot_cache_thaw (&device->intel);
+
+    i915_device_reset (device);
+
+    return status;
+}
+
+#if 0
+static float *
+i915_add_rectangles (i915_device_t *device, int num_rects, int *count)
+{
+    float *vertices;
+    uint32_t size;
+    int cnt;
+
+    assert (device->floats_per_vertex);
+
+    size = device->rectangle_size;
+    if (unlikely (device->vbo_offset + size > I915_VBO_SIZE))
+       i915_vbo_finish (device);
+
+    vertices = (float *) (device->vbo_base + device->vbo_offset);
+    cnt = (I915_VBO_SIZE - device->vbo_offset) / size;
+    if (cnt > num_rects)
+       cnt = num_rects;
+    device->vbo_used = device->vbo_offset += size * cnt;
+    device->vertex_count += 3 * cnt;
+    *count = cnt;
+    return vertices;
+}
+#endif
+
+static cairo_surface_t *
+i915_surface_create_similar (void *abstract_other,
+                            cairo_content_t content,
+                            int width, int height)
+{
+    i915_surface_t *other;
+    cairo_format_t format;
+    uint32_t tiling = I915_TILING_DEFAULT;
+
+    other = abstract_other;
+    if (content == other->intel.drm.base.content)
+       format = other->intel.drm.format;
+    else
+       format = _cairo_format_from_content (content);
+
+    if (width * _cairo_format_bits_per_pixel (format) > 8 * 32*1024 || height > 64*1024)
+       return NULL;
+
+    /* we presume that a similar surface will be used for blitting */
+    if (i915_surface_needs_tiling (other))
+       tiling = I915_TILING_X;
+
+    return i915_surface_create_internal ((cairo_drm_device_t *) other->intel.drm.base.device,
+                                        format,
+                                        width, height,
+                                        tiling, TRUE);
+}
+
+static cairo_status_t
+i915_surface_finish (void *abstract_surface)
+{
+    i915_surface_t *surface = abstract_surface;
+    i915_device_t *device = i915_device (surface);
+
+    if (surface->stencil != NULL) {
+       intel_bo_in_flight_add (&device->intel, surface->stencil);
+       intel_bo_destroy (&device->intel, surface->stencil);
+    }
+
+    if (surface->is_current_texture) {
+       if (surface->is_current_texture & CURRENT_SOURCE)
+           device->current_source = NULL;
+       if (surface->is_current_texture & CURRENT_MASK)
+           device->current_mask = NULL;
+       if (surface->is_current_texture & CURRENT_CLIP)
+           device->current_clip = NULL;
+       device->current_n_samplers = 0;
+    }
+
+    if (surface == device->current_target)
+       device->current_target = NULL;
+
+    if (surface->cache != NULL) {
+       i915_image_private_t *node = surface->cache;
+       intel_buffer_cache_t *cache = node->container;
+
+       if (--cache->ref_count == 0) {
+           intel_bo_in_flight_add (&device->intel, cache->buffer.bo);
+           intel_bo_destroy (&device->intel, cache->buffer.bo);
+           _cairo_rtree_fini (&cache->rtree);
+           cairo_list_del (&cache->link);
+           free (cache);
+       } else {
+           node->node.state = CAIRO_RTREE_NODE_AVAILABLE;
+           cairo_list_move (&node->node.link, &cache->rtree.available);
+           _cairo_rtree_node_collapse (&cache->rtree, node->node.parent);
+       }
+    }
+
+    return intel_surface_finish (&surface->intel);
+}
+
+static cairo_status_t
+i915_surface_batch_flush (i915_surface_t *surface)
+{
+    cairo_status_t status;
+    intel_bo_t *bo;
+
+    assert (surface->intel.drm.fallback == NULL);
+
+    bo = to_intel_bo (surface->intel.drm.bo);
+    if (bo == NULL || bo->batch_write_domain == 0)
+       return CAIRO_STATUS_SUCCESS;
+
+    status = cairo_device_acquire (surface->intel.drm.base.device);
+    if (unlikely (status))
+       return status;
+
+    status = i915_batch_flush (i915_device (surface));
+    cairo_device_release (surface->intel.drm.base.device);
+
+    return status;
+}
+
+static cairo_status_t
+i915_surface_flush (void *abstract_surface)
+{
+    i915_surface_t *surface = abstract_surface;
+    cairo_status_t status;
+
+    if (surface->intel.drm.fallback == NULL) {
+       if (surface->intel.drm.base.finished) {
+           /* Forgo flushing on finish as the user cannot access the surface directly. */
+           return CAIRO_STATUS_SUCCESS;
+       }
+
+       if (surface->deferred_clear) {
+           status = i915_surface_clear (surface);
+           if (unlikely (status))
+               return status;
+       }
+
+       return i915_surface_batch_flush (surface);
+    }
+
+    return intel_surface_flush (abstract_surface);
+}
+
+/* rasterisation */
+
+static cairo_status_t
+_composite_boxes_spans (void                   *closure,
+                       cairo_span_renderer_t   *renderer,
+                       const cairo_rectangle_int_t     *extents)
+{
+    cairo_boxes_t *boxes = closure;
+    cairo_rectangular_scan_converter_t converter;
+    struct _cairo_boxes_chunk *chunk;
+    cairo_status_t status;
+    int i;
+
+    _cairo_rectangular_scan_converter_init (&converter, extents);
+    for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
+       cairo_box_t *box = chunk->base;
+       for (i = 0; i < chunk->count; i++) {
+           status = _cairo_rectangular_scan_converter_add_box (&converter, &box[i], 1);
+           if (unlikely (status))
+               goto CLEANUP;
+       }
+    }
+
+    status = converter.base.generate (&converter.base, renderer);
+
+CLEANUP:
+    converter.base.destroy (&converter.base);
+    return status;
+}
+
+cairo_status_t
+i915_fixup_unbounded (i915_surface_t *dst,
+                     const cairo_composite_rectangles_t *extents,
+                     cairo_clip_t *clip)
+{
+    i915_shader_t shader;
+    i915_device_t *device;
+    cairo_status_t status;
+
+    if (clip != NULL) {
+       cairo_region_t *clip_region = NULL;
+
+       status = _cairo_clip_get_region (clip, &clip_region);
+       assert (status == CAIRO_STATUS_SUCCESS || status == CAIRO_INT_STATUS_UNSUPPORTED);
+       assert (clip_region == NULL);
+
+       if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+           clip = NULL;
+    } else {
+       if (extents->bounded.width == extents->unbounded.width &&
+           extents->bounded.height == extents->unbounded.height)
+       {
+           return CAIRO_STATUS_SUCCESS;
+       }
+    }
+
+    if (clip != NULL) {
+       i915_shader_init (&shader, dst, CAIRO_OPERATOR_DEST_OVER, 1.);
+       i915_shader_set_clip (&shader, clip);
+       status = i915_shader_acquire_pattern (&shader,
+                                             &shader.source,
+                                             &_cairo_pattern_white.base,
+                                             &extents->unbounded);
+       assert (status == CAIRO_STATUS_SUCCESS);
+    } else {
+       i915_shader_init (&shader, dst, CAIRO_OPERATOR_CLEAR, 1.);
+       status = i915_shader_acquire_pattern (&shader,
+                                             &shader.source,
+                                             &_cairo_pattern_clear.base,
+                                             &extents->unbounded);
+       assert (status == CAIRO_STATUS_SUCCESS);
+    }
+
+    device = i915_device (dst);
+    status = cairo_device_acquire (&device->intel.base.base);
+    if (unlikely (status))
+       return status;
+
+    status = i915_shader_commit (&shader, device);
+    if (unlikely (status))
+       goto BAIL;
+
+    if (extents->bounded.width == 0 || extents->bounded.height == 0) {
+       shader.add_rectangle (&shader,
+                             extents->unbounded.x,
+                             extents->unbounded.y,
+                             extents->unbounded.width,
+                             extents->unbounded.height);
+    } else {
+       /* top */
+       if (extents->bounded.y != extents->unbounded.y) {
+           shader.add_rectangle (&shader,
+                                 extents->unbounded.x,
+                                 extents->unbounded.y,
+                                 extents->unbounded.width,
+                                 extents->bounded.y - extents->unbounded.y);
+       }
+
+       /* left */
+       if (extents->bounded.x != extents->unbounded.x) {
+           shader.add_rectangle (&shader,
+                                 extents->unbounded.x,
+                                 extents->bounded.y,
+                                 extents->bounded.x - extents->unbounded.x,
+                                 extents->bounded.height);
+       }
+
+       /* right */
+       if (extents->bounded.x + extents->bounded.width != extents->unbounded.x + extents->unbounded.width) {
+           shader.add_rectangle (&shader,
+                                 extents->bounded.x + extents->bounded.width,
+                                 extents->bounded.y,
+                                 extents->unbounded.x + extents->unbounded.width - (extents->bounded.x + extents->bounded.width),
+                                 extents->bounded.height);
+       }
+
+       /* bottom */
+       if (extents->bounded.y + extents->bounded.height != extents->unbounded.y + extents->unbounded.height) {
+           shader.add_rectangle (&shader,
+                                 extents->unbounded.x,
+                                 extents->bounded.y + extents->bounded.height,
+                                 extents->unbounded.width,
+                                 extents->unbounded.y + extents->unbounded.height - (extents->bounded.y + extents->bounded.height));
+       }
+    }
+
+    i915_shader_fini (&shader);
+  BAIL:
+    cairo_device_release (&device->intel.base.base);
+    return status;
+}
+
+static cairo_status_t
+i915_fixup_unbounded_boxes (i915_surface_t *dst,
+                           const cairo_composite_rectangles_t *extents,
+                           cairo_clip_t *clip,
+                           cairo_boxes_t *boxes)
+{
+    cairo_boxes_t clear;
+    cairo_box_t box;
+    cairo_region_t *clip_region = NULL;
+    cairo_status_t status;
+    struct _cairo_boxes_chunk *chunk;
+    int i;
+
+    if (boxes->num_boxes <= 1)
+       return i915_fixup_unbounded (dst, extents, clip);
+
+    _cairo_boxes_init (&clear);
+
+    box.p1.x = _cairo_fixed_from_int (extents->unbounded.x + extents->unbounded.width);
+    box.p1.y = _cairo_fixed_from_int (extents->unbounded.y);
+    box.p2.x = _cairo_fixed_from_int (extents->unbounded.x);
+    box.p2.y = _cairo_fixed_from_int (extents->unbounded.y + extents->unbounded.height);
+
+    if (clip != NULL) {
+       status = _cairo_clip_get_region (clip, &clip_region);
+       assert (status == CAIRO_STATUS_SUCCESS || status == CAIRO_INT_STATUS_UNSUPPORTED);
+       if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+           clip = NULL;
+    }
+
+    if (clip_region == NULL) {
+       cairo_boxes_t tmp;
+
+       _cairo_boxes_init (&tmp);
+
+       status = _cairo_boxes_add (&tmp, &box);
+       assert (status == CAIRO_STATUS_SUCCESS);
+
+       tmp.chunks.next = &boxes->chunks;
+       tmp.num_boxes += boxes->num_boxes;
+
+       status = _cairo_bentley_ottmann_tessellate_boxes (&tmp,
+                                                         CAIRO_FILL_RULE_WINDING,
+                                                         &clear);
+
+       tmp.chunks.next = NULL;
+    } else {
+       pixman_box32_t *pbox;
+
+       pbox = pixman_region32_rectangles (&clip_region->rgn, &i);
+       _cairo_boxes_limit (&clear, (cairo_box_t *) pbox, i);
+
+       status = _cairo_boxes_add (&clear, &box);
+       assert (status == CAIRO_STATUS_SUCCESS);
+
+       for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
+           for (i = 0; i < chunk->count; i++) {
+               status = _cairo_boxes_add (&clear, &chunk->base[i]);
+               if (unlikely (status)) {
+                   _cairo_boxes_fini (&clear);
+                   return status;
+               }
+           }
+       }
+
+       status = _cairo_bentley_ottmann_tessellate_boxes (&clear,
+                                                         CAIRO_FILL_RULE_WINDING,
+                                                         &clear);
+    }
+
+    if (likely (status == CAIRO_STATUS_SUCCESS && clear.num_boxes)) {
+       i915_shader_t shader;
+       i915_device_t *device;
+
+       if (clip != NULL) {
+           i915_shader_init (&shader, dst, CAIRO_OPERATOR_DEST_OVER, 1.);
+           i915_shader_set_clip (&shader, clip);
+           status = i915_shader_acquire_pattern (&shader,
+                                                 &shader.source,
+                                                 &_cairo_pattern_white.base,
+                                                 &extents->unbounded);
+           assert (status == CAIRO_STATUS_SUCCESS);
+       } else {
+           i915_shader_init (&shader, dst, CAIRO_OPERATOR_CLEAR, 1.);
+           status = i915_shader_acquire_pattern (&shader,
+                                                 &shader.source,
+                                                 &_cairo_pattern_clear.base,
+                                                 &extents->unbounded);
+           assert (status == CAIRO_STATUS_SUCCESS);
+       }
+
+       device = i915_device (dst);
+       status = cairo_device_acquire (&device->intel.base.base);
+       if (unlikely (status))
+           goto err_shader;
+
+       status = i915_shader_commit (&shader, device);
+       if (unlikely (status))
+           goto err_device;
+
+       for (chunk = &clear.chunks; chunk != NULL; chunk = chunk->next) {
+           for (i = 0; i < chunk->count; i++) {
+               int x1 = _cairo_fixed_integer_part (chunk->base[i].p1.x);
+               int y1 = _cairo_fixed_integer_part (chunk->base[i].p1.y);
+               int x2 = _cairo_fixed_integer_part (chunk->base[i].p2.x);
+               int y2 = _cairo_fixed_integer_part (chunk->base[i].p2.y);
+
+               shader.add_rectangle (&shader, x1, y1, x2 - x1, y2 - y1);
+           }
+       }
+err_device:
+       cairo_device_release (&device->intel.base.base);
+err_shader:
+       i915_shader_fini (&shader);
+    }
+
+    _cairo_boxes_fini (&clear);
+
+    return status;
+}
+
+static cairo_bool_t
+i915_can_blt (i915_surface_t *dst,
+             const cairo_pattern_t *pattern)
+{
+    const cairo_surface_pattern_t *spattern;
+    i915_surface_t *src;
+
+    spattern = (const cairo_surface_pattern_t *) pattern;
+    src = (i915_surface_t *) spattern->surface;
+
+    if (src->intel.drm.base.device != dst->intel.drm.base.device)
+       return FALSE;
+
+    if (! i915_surface_needs_tiling (dst))
+       return FALSE;
+
+    if (! _cairo_matrix_is_translation (&pattern->matrix))
+       return FALSE;
+
+    if (! (pattern->filter == CAIRO_FILTER_NEAREST ||
+          pattern->filter == CAIRO_FILTER_FAST))
+    {
+       if (! _cairo_fixed_is_integer (_cairo_fixed_from_double (pattern->matrix.x0)) ||
+           ! _cairo_fixed_is_integer (_cairo_fixed_from_double (pattern->matrix.y0)))
+       {
+           return FALSE;
+       }
+    }
+
+    return _cairo_format_bits_per_pixel (src->intel.drm.format) ==
+       _cairo_format_bits_per_pixel (dst->intel.drm.format);
+}
+
+static cairo_status_t
+i915_blt (i915_surface_t *src,
+         i915_surface_t *dst,
+         int src_x, int src_y,
+         int width, int height,
+         int dst_x, int dst_y,
+         cairo_bool_t flush)
+{
+    i915_device_t *device;
+    intel_bo_t *bo_array[2];
+    cairo_status_t status;
+    int br13, cmd;
+
+    bo_array[0] = to_intel_bo (dst->intel.drm.bo);
+    bo_array[1] = to_intel_bo (src->intel.drm.bo);
+
+    status = i915_surface_fallback_flush (src);
+    if (unlikely (status))
+       return status;
+
+    device = i915_device (dst);
+    status = cairo_device_acquire (&device->intel.base.base);
+    if (unlikely (status))
+       return status;
+
+    if (! i915_check_aperture_and_fences (device, bo_array, 2) ||
+       i915_batch_space (device) < 9)
+    {
+       status = i915_batch_flush (device);
+       if (unlikely (status))
+           goto CLEANUP;
+    }
+
+    cmd = XY_SRC_COPY_BLT_CMD;
+    br13 = (0xCC << 16) | dst->intel.drm.stride;
+    switch (dst->intel.drm.format) {
+    default:
+    case CAIRO_FORMAT_INVALID:
+    case CAIRO_FORMAT_A1:
+       ASSERT_NOT_REACHED;
+    case CAIRO_FORMAT_A8:
+       break;
+    case CAIRO_FORMAT_RGB16_565:
+       br13 |= BR13_565;
+       break;
+    case CAIRO_FORMAT_RGB24:
+    case CAIRO_FORMAT_ARGB32:
+       br13 |= BR13_8888;
+       cmd |= XY_BLT_WRITE_ALPHA | XY_BLT_WRITE_RGB;
+       break;
+    }
+
+    OUT_DWORD (cmd);
+    OUT_DWORD (br13);
+    OUT_DWORD ((dst_y << 16) | dst_x);
+    OUT_DWORD (((dst_y + height - 1) << 16) | (dst_x + width - 1));
+    OUT_RELOC_FENCED (dst, I915_GEM_DOMAIN_RENDER, I915_GEM_DOMAIN_RENDER);
+    OUT_DWORD ((src_y << 16) | src_x);
+    OUT_DWORD (src->intel.drm.stride);
+    OUT_RELOC_FENCED (src, I915_GEM_DOMAIN_RENDER, 0);
+    /* require explicit RenderCache flush for 2D -> 3D sampler? */
+    if (flush)
+       OUT_DWORD (MI_FLUSH);
+
+CLEANUP:
+    cairo_device_release (&device->intel.base.base);
+    return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_status_t
+i915_surface_copy_subimage (i915_device_t *device,
+                           i915_surface_t *src,
+                           const cairo_rectangle_int_t *extents,
+                           cairo_bool_t flush,
+                           i915_surface_t **clone_out)
+{
+    i915_surface_t *clone;
+    cairo_status_t status;
+
+    clone = (i915_surface_t *)
+       i915_surface_create_internal (&device->intel.base,
+                                     src->intel.drm.format,
+                                     extents->width,
+                                     extents->height,
+                                     I915_TILING_X, TRUE);
+    if (unlikely (clone->intel.drm.base.status))
+       return clone->intel.drm.base.status;
+
+    status = i915_blt (src, clone,
+                      extents->x, extents->y,
+                      extents->width, extents->height,
+                      0, 0,
+                      flush);
+
+    if (unlikely (status)) {
+       cairo_surface_destroy (&clone->intel.drm.base);
+       return status;
+    }
+
+    *clone_out = clone;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+i915_clear_boxes (i915_surface_t *dst,
+                 const cairo_boxes_t *boxes)
+{
+    i915_device_t *device = i915_device (dst);
+    const struct _cairo_boxes_chunk *chunk;
+    cairo_status_t status;
+    intel_bo_t *bo_array[1] = { to_intel_bo (dst->intel.drm.bo) };
+    int cmd, br13, clear = 0, i;
+
+    cmd = XY_COLOR_BLT_CMD;
+    br13 = (0xCC << 16) | dst->intel.drm.stride;
+    switch (dst->intel.drm.format) {
+    default:
+    case CAIRO_FORMAT_INVALID:
+    case CAIRO_FORMAT_A1:
+       ASSERT_NOT_REACHED;
+    case CAIRO_FORMAT_A8:
+       break;
+    case CAIRO_FORMAT_RGB16_565:
+       br13 |= BR13_565;
+       break;
+    case CAIRO_FORMAT_RGB24:
+       clear = 0xff000000;
+    case CAIRO_FORMAT_ARGB32:
+       br13 |= BR13_8888;
+       cmd |= XY_BLT_WRITE_ALPHA | XY_BLT_WRITE_RGB;
+       break;
+    }
+
+    status = cairo_device_acquire (&device->intel.base.base);
+    if (unlikely (status))
+       return status;
+
+    if (! i915_check_aperture_and_fences (device, bo_array, 1) ||
+       i915_batch_space (device) < 6 * boxes->num_boxes)
+    {
+       status = i915_batch_flush (device);
+       if (unlikely (status))
+           goto RELEASE;
+    }
+
+    if (device->vertex_count)
+       i915_vbo_flush (device);
+
+    for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
+       const cairo_box_t *box = chunk->base;
+       for (i = 0; i < chunk->count; i++) {
+           int x1 = _cairo_fixed_integer_round (box[i].p1.x);
+           int x2 = _cairo_fixed_integer_round (box[i].p2.x);
+           int y1 = _cairo_fixed_integer_round (box[i].p1.y);
+           int y2 = _cairo_fixed_integer_round (box[i].p2.y);
+
+           if (x2 <= x1 || y2 <= y1)
+               continue;
+
+           OUT_DWORD (cmd);
+           OUT_DWORD (br13);
+           OUT_DWORD ((y1 << 16) | x1);
+           OUT_DWORD (((y2 - 1) << 16) | (x2 - 1));
+           OUT_RELOC_FENCED (dst, I915_GEM_DOMAIN_RENDER, I915_GEM_DOMAIN_RENDER);
+           OUT_DWORD (clear);
+       }
+    }
+
+RELEASE:
+    cairo_device_release (&device->intel.base.base);
+    return status;
+}
+
+static cairo_status_t
+i915_surface_extract_X_from_Y (i915_device_t *device,
+                              i915_surface_t *src,
+                              const cairo_rectangle_int_t *extents,
+                              i915_surface_t **clone_out)
+{
+    i915_surface_t *clone;
+    i915_shader_t shader;
+    cairo_surface_pattern_t pattern;
+    cairo_rectangle_int_t rect;
+    cairo_status_t status;
+
+    status = i915_surface_fallback_flush (src);
+    if (unlikely (status))
+       return status;
+
+    clone = (i915_surface_t *)
+       i915_surface_create_internal (&device->intel.base,
+                                     src->intel.drm.format,
+                                     extents->width,
+                                     extents->height,
+                                     I915_TILING_X, TRUE);
+    if (unlikely (clone->intel.drm.base.status))
+       return clone->intel.drm.base.status;
+
+    i915_shader_init (&shader, clone, CAIRO_OPERATOR_SOURCE, 1.);
+
+    _cairo_pattern_init_for_surface (&pattern, &src->intel.drm.base);
+    pattern.base.filter = CAIRO_FILTER_NEAREST;
+    cairo_matrix_init_translate (&pattern.base.matrix, extents->x, extents->y);
+
+    rect.x = rect.y = 0;
+    rect.width = extents->width;
+    rect.height = extents->height;
+    status = i915_shader_acquire_pattern (&shader, &shader.source, &pattern.base, &rect);
+    _cairo_pattern_fini (&pattern.base);
+
+    if (unlikely (status))
+       goto err_shader;
+
+    status = cairo_device_acquire (&device->intel.base.base);
+    if (unlikely (status))
+       goto err_shader;
+
+    status = i915_shader_commit (&shader, device);
+    if (unlikely (status))
+       goto err_device;
+
+    shader.add_rectangle (&shader, 0, 0, extents->width, extents->height);
+
+    cairo_device_release (&device->intel.base.base);
+    i915_shader_fini (&shader);
+
+    *clone_out = clone;
+    return CAIRO_STATUS_SUCCESS;
+
+err_device:
+    cairo_device_release (&device->intel.base.base);
+err_shader:
+    i915_shader_fini (&shader);
+    cairo_surface_destroy (&clone->intel.drm.base);
+    return status;
+}
+
+static cairo_status_t
+i915_blt_boxes (i915_surface_t *dst,
+               const cairo_pattern_t *pattern,
+               const cairo_rectangle_int_t *extents,
+               const cairo_boxes_t *boxes)
+{
+    const cairo_surface_pattern_t *spattern;
+    i915_device_t *device;
+    i915_surface_t *src;
+    cairo_surface_t *free_me = NULL;
+    const struct _cairo_boxes_chunk *chunk;
+    cairo_status_t status;
+    int br13, cmd, tx, ty;
+    intel_bo_t *bo_array[2];
+    int i;
+
+    if (! i915_can_blt (dst, pattern))
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    spattern = (const cairo_surface_pattern_t *) pattern;
+    src = (i915_surface_t *) spattern->surface;
+
+    if (src->intel.drm.base.is_clear)
+       return i915_clear_boxes (dst, boxes);
+
+    if (pattern->extend != CAIRO_EXTEND_NONE &&
+       (extents->x + tx < 0 ||
+        extents->y + ty < 0 ||
+        extents->x + tx + extents->width  > src->intel.drm.width ||
+        extents->y + ty + extents->height > src->intel.drm.height))
+    {
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+    }
+
+    status = i915_surface_fallback_flush (src);
+    if (unlikely (status))
+       return status;
+
+    tx = _cairo_lround (pattern->matrix.x0);
+    ty = _cairo_lround (pattern->matrix.y0);
+
+    device = i915_device (dst);
+    if (to_intel_bo (src->intel.drm.bo)->tiling == I915_TILING_Y) {
+       cairo_rectangle_int_t extents;
+
+       _cairo_boxes_extents (boxes, &extents);
+       extents.x += tx;
+       extents.y += ty;
+
+       status = i915_surface_extract_X_from_Y (device, src, &extents, &src);
+       if (unlikely (status))
+           return status;
+
+       free_me = &src->intel.drm.base;
+       tx = -extents.x;
+       ty = -extents.y;
+    }
+
+    bo_array[0] = to_intel_bo (dst->intel.drm.bo);
+    bo_array[1] = to_intel_bo (src->intel.drm.bo);
+
+    status = cairo_device_acquire (&device->intel.base.base);
+    if (unlikely (status))
+       goto CLEANUP_SURFACE;
+
+    if (! i915_check_aperture_and_fences (device, bo_array, 2) ||
+       i915_batch_space (device) < 8 * boxes->num_boxes)
+    {
+       status = i915_batch_flush (device);
+       if (unlikely (status))
+           goto CLEANUP_DEVICE;
+    }
+
+    cmd = XY_SRC_COPY_BLT_CMD;
+    br13 = (0xCC << 16) | dst->intel.drm.stride;
+    switch (dst->intel.drm.format) {
+    default:
+    case CAIRO_FORMAT_INVALID:
+    case CAIRO_FORMAT_A1:
+       ASSERT_NOT_REACHED;
+    case CAIRO_FORMAT_A8:
+       break;
+    case CAIRO_FORMAT_RGB16_565:
+       br13 |= BR13_565;
+       break;
+    case CAIRO_FORMAT_RGB24:
+    case CAIRO_FORMAT_ARGB32:
+       br13 |= BR13_8888;
+       cmd |= XY_BLT_WRITE_ALPHA | XY_BLT_WRITE_RGB;
+       break;
+    }
+
+    for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
+       const cairo_box_t *box = chunk->base;
+       for (i = 0; i < chunk->count; i++) {
+           int x1 = _cairo_fixed_integer_round (box[i].p1.x);
+           int x2 = _cairo_fixed_integer_round (box[i].p2.x);
+           int y1 = _cairo_fixed_integer_round (box[i].p1.y);
+           int y2 = _cairo_fixed_integer_round (box[i].p2.y);
+
+           if (x1 + tx < 0)
+               x1 = -tx;
+           if (x2 + tx > src->intel.drm.width)
+               x2 = src->intel.drm.width - tx;
+
+           if (y1 + ty < 0)
+               y1 = -ty;
+           if (y2 + ty > src->intel.drm.height)
+               y2 = src->intel.drm.height - ty;
+
+           if (x2 <= x1 || y2 <= y1)
+               continue;
+           if (x2 < 0 || y2 < 0)
+               continue;
+           if (x1 >= dst->intel.drm.width || y2 >= dst->intel.drm.height)
+               continue;
+
+           OUT_DWORD (cmd);
+           OUT_DWORD (br13);
+           OUT_DWORD ((y1 << 16) | x1);
+           OUT_DWORD (((y2 - 1) << 16) | (x2 - 1));
+           OUT_RELOC_FENCED (dst, I915_GEM_DOMAIN_RENDER, I915_GEM_DOMAIN_RENDER);
+           OUT_DWORD (((y1 + ty) << 16) | (x1 + tx));
+           OUT_DWORD (src->intel.drm.stride);
+           OUT_RELOC_FENCED (src, I915_GEM_DOMAIN_RENDER, 0);
+       }
+    }
+
+    /* XXX fixup blank portions */
+
+CLEANUP_DEVICE:
+    cairo_device_release (&device->intel.base.base);
+CLEANUP_SURFACE:
+    cairo_surface_destroy (free_me);
+    return status;
+}
+
+static cairo_status_t
+_upload_image_inplace (i915_surface_t *surface,
+                      const cairo_pattern_t *source,
+                      const cairo_rectangle_int_t *extents,
+                      const cairo_boxes_t *boxes)
+{
+    i915_device_t *device;
+    const cairo_surface_pattern_t *pattern;
+    cairo_image_surface_t *image;
+    const struct _cairo_boxes_chunk *chunk;
+    intel_bo_t *bo;
+    int tx, ty, i;
+
+    if (source->type != CAIRO_PATTERN_TYPE_SURFACE)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    pattern = (const cairo_surface_pattern_t *) source;
+    if (pattern->surface->type != CAIRO_SURFACE_TYPE_IMAGE)
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    if (! _cairo_matrix_is_integer_translation (&source->matrix, &tx, &ty))
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    image = (cairo_image_surface_t *) pattern->surface;
+    if (source->extend != CAIRO_EXTEND_NONE &&
+       (extents->x + tx < 0 ||
+        extents->y + ty < 0 ||
+        extents->x + tx + extents->width  > image->width ||
+        extents->y + ty + extents->height > image->height))
+    {
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+    }
+
+    device = i915_device (surface);
+    bo = to_intel_bo (surface->intel.drm.bo);
+    if (bo->exec != NULL || ! intel_bo_is_inactive (&device->intel, bo)) {
+       intel_bo_t *new_bo;
+       cairo_bool_t need_clear = FALSE;
+
+       if (boxes->num_boxes != 1 ||
+           extents->width < surface->intel.drm.width ||
+           extents->height < surface->intel.drm.height)
+       {
+           if (! surface->intel.drm.base.is_clear)
+               return CAIRO_INT_STATUS_UNSUPPORTED;
+
+           need_clear = TRUE;
+       }
+
+       new_bo = intel_bo_create (&device->intel,
+                                 bo->full_size, bo->base.size,
+                                 FALSE, bo->tiling, bo->stride);
+       if (unlikely (new_bo == NULL))
+           return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+       intel_bo_in_flight_add (&device->intel, bo);
+       intel_bo_destroy (&device->intel, bo);
+
+       bo = new_bo;
+       surface->intel.drm.bo = &bo->base;
+
+       if (need_clear) {
+           memset (intel_bo_map (&device->intel, bo), 0,
+                   bo->stride * surface->intel.drm.height);
+       }
+    }
+
+    if (image->format == surface->intel.drm.format) {
+       for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
+           cairo_box_t *box = chunk->base;
+           for (i = 0; i < chunk->count; i++) {
+               int x1 = _cairo_fixed_integer_round (box[i].p1.x);
+               int x2 = _cairo_fixed_integer_round (box[i].p2.x);
+               int y1 = _cairo_fixed_integer_round (box[i].p1.y);
+               int y2 = _cairo_fixed_integer_round (box[i].p2.y);
+               cairo_status_t status;
+
+               if (x1 + tx < 0)
+                   x1 = -tx;
+               if (x2 + tx > image->width)
+                   x2 = image->width - tx;
+
+               if (y1 + ty < 0)
+                   y1 = -ty;
+               if (y2 + ty > image->height)
+                   y2 = image->height - ty;
+
+               if (x2 <= x1 || y2 <= y1)
+                   continue;
+               if (x2 < 0 || y2 < 0)
+                   continue;
+               if (x1 >= surface->intel.drm.width || y2 >= surface->intel.drm.height)
+                   continue;
+
+               status = intel_bo_put_image (&device->intel,
+                                            bo,
+                                            image,
+                                            x1 + tx, y1 + ty,
+                                            x2 - x1, y2 - y1,
+                                            x1, y1);
+               if (unlikely (status))
+                   return status;
+           }
+       }
+    } else {
+       pixman_image_t *dst;
+       void *ptr;
+
+       ptr = intel_bo_map (&device->intel, bo);
+       if (unlikely (ptr == NULL))
+           return _cairo_error (CAIRO_STATUS_DEVICE_ERROR);
+
+       dst = pixman_image_create_bits (_cairo_format_to_pixman_format_code (surface->intel.drm.format),
+                                       surface->intel.drm.width,
+                                       surface->intel.drm.height,
+                                       ptr,
+                                       surface->intel.drm.stride);
+       if (unlikely (dst == NULL))
+           return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+       for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
+           cairo_box_t *box = chunk->base;
+           for (i = 0; i < chunk->count; i++) {
+               int x1 = _cairo_fixed_integer_round (box[i].p1.x);
+               int x2 = _cairo_fixed_integer_round (box[i].p2.x);
+               int y1 = _cairo_fixed_integer_round (box[i].p1.y);
+               int y2 = _cairo_fixed_integer_round (box[i].p2.y);
+
+               if (x1 + tx < 0)
+                   x1 = -tx;
+               if (x2 + tx > image->width)
+                   x2 = image->width - tx;
+
+               if (y1 + ty < 0)
+                   y1 = -ty;
+               if (y2 + ty > image->height)
+                   y2 = image->height - ty;
+
+               if (x2 <= x1 || y2 <= y1)
+                   continue;
+               if (x2 < 0 || y2 < 0)
+                   continue;
+               if (x1 >= surface->intel.drm.width || y2 >= surface->intel.drm.height)
+                   continue;
+
+               pixman_image_composite32 (PIXMAN_OP_SRC,
+                                         image->pixman_image, NULL, dst,
+                                         x1 + tx, y1 + ty,
+                                         0, 0,
+                                         x1, y1,
+                                         x2 - x1, y2 - y1);
+           }
+       }
+
+       pixman_image_unref (dst);
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_composite_boxes (i915_surface_t *dst,
+                 cairo_operator_t op,
+                 const cairo_pattern_t *pattern,
+                 cairo_boxes_t *boxes,
+                 cairo_antialias_t antialias,
+                 cairo_clip_t *clip,
+                 double opacity,
+                 const cairo_composite_rectangles_t *extents)
+{
+    cairo_bool_t need_clip_surface = FALSE;
+    cairo_region_t *clip_region = NULL;
+    const struct _cairo_boxes_chunk *chunk;
+    cairo_status_t status;
+    i915_shader_t shader;
+    i915_device_t *device;
+    int i;
+
+    /* If the boxes are not pixel-aligned, we will need to compute a real mask */
+    if (antialias != CAIRO_ANTIALIAS_NONE) {
+       if (! boxes->is_pixel_aligned)
+           return CAIRO_INT_STATUS_UNSUPPORTED;
+    }
+
+    if (clip == NULL && op == CAIRO_OPERATOR_SOURCE && opacity == 1.) {
+       if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) {
+           status = i915_blt_boxes (dst, pattern, &extents->bounded, boxes);
+           if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
+               status = _upload_image_inplace (dst, pattern,
+                                               &extents->bounded, boxes);
+           }
+           if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+               return status;
+       }
+    }
+
+    if (i915_surface_needs_tiling (dst)) {
+       ASSERT_NOT_REACHED;
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+    }
+
+    i915_shader_init (&shader, dst, op, opacity);
+
+    status = i915_shader_acquire_pattern (&shader,
+                                         &shader.source,
+                                         pattern,
+                                         &extents->bounded);
+    if (unlikely (status))
+       return status;
+
+    if (clip != NULL) {
+       status = _cairo_clip_get_region (clip, &clip_region);
+       assert (status == CAIRO_STATUS_SUCCESS || status == CAIRO_INT_STATUS_UNSUPPORTED);
+       need_clip_surface = status == CAIRO_INT_STATUS_UNSUPPORTED;
+       if (need_clip_surface)
+           i915_shader_set_clip (&shader, clip);
+    }
+
+    device = i915_device (dst);
+    status = cairo_device_acquire (&device->intel.base.base);
+    if (unlikely (status))
+       goto err_shader;
+
+    status = i915_shader_commit (&shader, device);
+    if (unlikely (status))
+       goto err_device;
+
+    for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
+       cairo_box_t *box = chunk->base;
+       for (i = 0; i < chunk->count; i++) {
+           int x1 = _cairo_fixed_integer_round (box[i].p1.x);
+           int y1 = _cairo_fixed_integer_round (box[i].p1.y);
+           int x2 = _cairo_fixed_integer_round (box[i].p2.x);
+           int y2 = _cairo_fixed_integer_round (box[i].p2.y);
+
+           if (x2 > x1 && y2 > y1)
+               shader.add_rectangle (&shader, x1, y1, x2 - x1, y2 - y1);
+       }
+    }
+
+    if (! extents->is_bounded)
+       status = i915_fixup_unbounded_boxes (dst, extents, clip, boxes);
+
+  err_device:
+    cairo_device_release (&device->intel.base.base);
+  err_shader:
+    i915_shader_fini (&shader);
+
+    return status;
+}
+
+cairo_status_t
+i915_surface_clear (i915_surface_t *dst)
+{
+    i915_device_t *device;
+    cairo_status_t status;
+    intel_bo_t *bo_array[1] = { to_intel_bo (dst->intel.drm.bo) };
+
+    device = i915_device (dst);
+    status = cairo_device_acquire (&device->intel.base.base);
+    if (unlikely (status))
+       return status;
+
+    if (i915_surface_needs_tiling (dst)) {
+       int cmd, br13, clear = 0;
+
+       if (! i915_check_aperture_and_fences (device, bo_array, 1) ||
+           i915_batch_space (device) < 6)
+       {
+           status = i915_batch_flush (device);
+           if (unlikely (status)) {
+               cairo_device_release (&device->intel.base.base);
+               return status;
+           }
+       }
+
+       if (device->vertex_count)
+           i915_vbo_flush (device);
+
+       cmd = XY_COLOR_BLT_CMD;
+       br13 = (0xCC << 16) | dst->intel.drm.stride;
+       switch (dst->intel.drm.format) {
+       default:
+       case CAIRO_FORMAT_INVALID:
+       case CAIRO_FORMAT_A1:
+           ASSERT_NOT_REACHED;
+       case CAIRO_FORMAT_A8:
+           break;
+       case CAIRO_FORMAT_RGB16_565:
+           br13 |= BR13_565;
+           break;
+       case CAIRO_FORMAT_RGB24:
+           clear = 0xff000000;
+       case CAIRO_FORMAT_ARGB32:
+           br13 |= BR13_8888;
+           cmd |= XY_BLT_WRITE_ALPHA | XY_BLT_WRITE_RGB;
+           break;
+       }
+
+       OUT_DWORD (cmd);
+       OUT_DWORD (br13);
+       OUT_DWORD (0);
+       OUT_DWORD (((dst->intel.drm.height - 1) << 16) |
+                  (dst->intel.drm.width - 1));
+       OUT_RELOC_FENCED (dst,
+                         I915_GEM_DOMAIN_RENDER, I915_GEM_DOMAIN_RENDER);
+       OUT_DWORD (clear);
+    } else {
+       if (! i915_check_aperture (device, bo_array, 1) ||
+           i915_batch_space (device) < 24)
+       {
+           status = i915_batch_flush (device);
+           if (unlikely (status)) {
+               cairo_device_release (&device->intel.base.base);
+               return status;
+           }
+       }
+
+       if (device->vertex_count)
+           i915_vbo_flush (device);
+
+       i915_set_dst (device, dst);
+
+       /* set clear parameters */
+       if (device->clear_alpha != (dst->intel.drm.base.content & CAIRO_CONTENT_ALPHA)) {
+           device->clear_alpha = dst->intel.drm.base.content & CAIRO_CONTENT_ALPHA;
+           OUT_DWORD (_3DSTATE_CLEAR_PARAMETERS);
+           OUT_DWORD (CLEARPARAM_CLEAR_RECT | CLEARPARAM_WRITE_COLOR);
+           /* ZONE_INIT color */
+           if (device->clear_alpha) /* XXX depends on pixel format, 16bit needs replication, 8bit? */
+               OUT_DWORD (0x00000000);
+           else
+               OUT_DWORD (0xff000000);
+           OUT_DWORD (0); /* ZONE_INIT depth */
+           /* CLEAR_RECT color */
+           if (device->clear_alpha)
+               OUT_DWORD (0x00000000);
+           else
+               OUT_DWORD (0xff000000);
+           OUT_DWORD (0); /* CLEAR_RECT depth */
+           OUT_DWORD (0); /* CLEAR_RECT stencil */
+       }
+
+       OUT_DWORD (PRIM3D_CLEAR_RECT | 5);
+       OUT_DWORD (pack_float (dst->intel.drm.width));
+       OUT_DWORD (pack_float (dst->intel.drm.height));
+       OUT_DWORD (0);
+       OUT_DWORD (pack_float (dst->intel.drm.height));
+       OUT_DWORD (0);
+       OUT_DWORD (0);
+    }
+
+    cairo_device_release (&device->intel.base.base);
+
+    dst->deferred_clear = FALSE;
+    return status;
+}
+
+static cairo_status_t
+_clip_and_composite_boxes (i915_surface_t *dst,
+                          cairo_operator_t op,
+                          const cairo_pattern_t *src,
+                          cairo_boxes_t *boxes,
+                          cairo_antialias_t antialias,
+                          const cairo_composite_rectangles_t *extents,
+                          cairo_clip_t *clip,
+                          double opacity)
+{
+    cairo_status_t status;
+
+    if (boxes->num_boxes == 0) {
+       if (extents->is_bounded)
+           return CAIRO_STATUS_SUCCESS;
+
+       return i915_fixup_unbounded (dst, extents, clip);
+    }
+
+    if (clip == NULL &&
+       (op == CAIRO_OPERATOR_SOURCE || (op == CAIRO_OPERATOR_OVER && dst->intel.drm.base.is_clear)) &&
+       opacity == 1. &&
+       boxes->num_boxes == 1 &&
+       extents->bounded.width  == dst->intel.drm.width &&
+       extents->bounded.height == dst->intel.drm.height)
+    {
+       op = CAIRO_OPERATOR_SOURCE;
+       dst->deferred_clear = FALSE;
+
+       status = _upload_image_inplace (dst, src,
+                                       &extents->bounded, boxes);
+       if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+           return status;
+    }
+
+    if (dst->deferred_clear) {
+       status = i915_surface_clear (dst);
+       if (unlikely (status))
+           return status;
+    }
+
+    /* Use a fast path if the boxes are pixel aligned */
+    status = _composite_boxes (dst, op, src, boxes, antialias, clip, opacity, extents);
+    if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+       return status;
+
+    /* Otherwise render the boxes via an implicit mask and composite in the usual
+     * fashion.
+     */
+    return i915_clip_and_composite_spans (dst, op, src, antialias,
+                                         _composite_boxes_spans, boxes,
+                                         extents, clip, opacity);
+}
+
+static cairo_clip_path_t *
+_clip_get_solitary_path (cairo_clip_t *clip)
+{
+    cairo_clip_path_t *iter = clip->path;
+    cairo_clip_path_t *path = NULL;
+
+    do {
+       if ((iter->flags & CAIRO_CLIP_PATH_IS_BOX) == 0) {
+           if (path != NULL)
+               return FALSE;
+
+           path = iter;
+       }
+       iter = iter->prev;
+    } while (iter != NULL);
+
+    return path;
+}
+
+typedef struct {
+    cairo_polygon_t            polygon;
+    cairo_fill_rule_t           fill_rule;
+    cairo_antialias_t           antialias;
+} composite_polygon_info_t;
+
+static cairo_status_t
+_composite_polygon_spans (void                          *closure,
+                         cairo_span_renderer_t         *renderer,
+                         const cairo_rectangle_int_t   *extents)
+{
+    composite_polygon_info_t *info = closure;
+    cairo_botor_scan_converter_t converter;
+    cairo_status_t status;
+    cairo_box_t box;
+
+    box.p1.x = _cairo_fixed_from_int (extents->x);
+    box.p1.y = _cairo_fixed_from_int (extents->y);
+    box.p2.x = _cairo_fixed_from_int (extents->x + extents->width);
+    box.p2.y = _cairo_fixed_from_int (extents->y + extents->height);
+
+    _cairo_botor_scan_converter_init (&converter, &box, info->fill_rule);
+
+    status = converter.base.add_polygon (&converter.base, &info->polygon);
+    if (likely (status == CAIRO_STATUS_SUCCESS))
+       status = converter.base.generate (&converter.base, renderer);
+
+    converter.base.destroy (&converter.base);
+
+    return status;
+}
+
+static cairo_int_status_t
+i915_surface_fill_with_alpha (void                     *abstract_dst,
+                             cairo_operator_t           op,
+                             const cairo_pattern_t     *source,
+                             cairo_path_fixed_t        *path,
+                             cairo_fill_rule_t          fill_rule,
+                             double                     tolerance,
+                             cairo_antialias_t          antialias,
+                             cairo_clip_t              *clip,
+                             double                     opacity)
+{
+    i915_surface_t *dst = abstract_dst;
+    cairo_composite_rectangles_t extents;
+    composite_polygon_info_t info;
+    cairo_box_t boxes_stack[32], *clip_boxes = boxes_stack;
+    cairo_clip_t local_clip;
+    cairo_bool_t have_clip = FALSE;
+    int num_boxes = ARRAY_LENGTH (boxes_stack);
+    cairo_status_t status;
+
+    status = _cairo_composite_rectangles_init_for_fill (&extents,
+                                                       dst->intel.drm.width,
+                                                       dst->intel.drm.height,
+                                                       op, source, path,
+                                                       clip);
+    if (unlikely (status))
+       return status;
+
+    if (_cairo_clip_contains_extents (clip, &extents))
+       clip = NULL;
+
+    if (extents.is_bounded && clip != NULL) {
+       cairo_clip_path_t *clip_path;
+
+       if (((clip_path = _clip_get_solitary_path (clip)) != NULL) &&
+           _cairo_path_fixed_equal (&clip_path->path, path))
+       {
+           clip = NULL;
+       }
+    }
+
+    if (clip != NULL) {
+       clip = _cairo_clip_init_copy (&local_clip, clip);
+       have_clip = TRUE;
+    }
+
+    status = _cairo_clip_to_boxes (&clip, &extents, &clip_boxes, &num_boxes);
+    if (unlikely (status)) {
+       if (have_clip)
+           _cairo_clip_fini (&local_clip);
+
+       return status;
+    }
+
+    assert (! _cairo_path_fixed_fill_is_empty (path));
+
+    if (_cairo_path_fixed_fill_is_rectilinear (path)) {
+       cairo_boxes_t boxes;
+
+       _cairo_boxes_init (&boxes);
+       _cairo_boxes_limit (&boxes, clip_boxes, num_boxes);
+       status = _cairo_path_fixed_fill_rectilinear_to_boxes (path,
+                                                             fill_rule,
+                                                             &boxes);
+       if (likely (status == CAIRO_STATUS_SUCCESS)) {
+           status = _clip_and_composite_boxes (dst, op, source,
+                                               &boxes, antialias,
+                                               &extents, clip,
+                                               opacity);
+       }
+
+       _cairo_boxes_fini (&boxes);
+
+       if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+           goto CLEANUP_BOXES;
+    }
+
+    _cairo_polygon_init (&info.polygon, clip_boxes, num_boxes);
+
+    status = _cairo_path_fixed_fill_to_polygon (path, tolerance, &info.polygon);
+    if (unlikely (status))
+       goto CLEANUP_POLYGON;
+
+    if (extents.is_bounded) {
+       cairo_rectangle_int_t rect;
+
+       _cairo_box_round_to_rectangle (&info.polygon.extents, &rect);
+       if (! _cairo_rectangle_intersect (&extents.bounded, &rect))
+           goto CLEANUP_POLYGON;
+    }
+
+    if (info.polygon.num_edges == 0) {
+       if (! extents.is_bounded)
+           status = i915_fixup_unbounded (dst, &extents, clip);
+
+       goto CLEANUP_POLYGON;
+    }
+
+    info.fill_rule = fill_rule;
+    info.antialias = antialias;
+    status = i915_clip_and_composite_spans (dst, op, source, antialias,
+                                           _composite_polygon_spans, &info,
+                                           &extents, clip, opacity);
+
+CLEANUP_POLYGON:
+    _cairo_polygon_fini (&info.polygon);
+
+CLEANUP_BOXES:
+    if (clip_boxes != boxes_stack)
+       free (clip_boxes);
+
+    if (have_clip)
+       _cairo_clip_fini (&local_clip);
+
+    return status;
+}
+
+static cairo_int_status_t
+i915_surface_paint_with_alpha (void                    *abstract_dst,
+                              cairo_operator_t          op,
+                              const cairo_pattern_t    *source,
+                              cairo_clip_t             *clip,
+                              double                    opacity)
+{
+    i915_surface_t *dst = abstract_dst;
+    cairo_composite_rectangles_t extents;
+    cairo_clip_t local_clip;
+    cairo_bool_t have_clip = FALSE;
+    cairo_clip_path_t *clip_path;
+    cairo_boxes_t boxes;
+    int num_boxes = ARRAY_LENGTH (boxes.boxes_embedded);
+    cairo_box_t *clip_boxes = boxes.boxes_embedded;
+    cairo_status_t status;
+
+    status = _cairo_composite_rectangles_init_for_paint (&extents,
+                                                        dst->intel.drm.width,
+                                                        dst->intel.drm.height,
+                                                        op, source,
+                                                        clip);
+    if (unlikely (status))
+       return status;
+
+    if (_cairo_clip_contains_extents (clip, &extents))
+       clip = NULL;
+
+    if (clip != NULL) {
+       clip = _cairo_clip_init_copy (&local_clip, clip);
+       have_clip = TRUE;
+    }
+
+    status = _cairo_clip_to_boxes (&clip, &extents, &clip_boxes, &num_boxes);
+    if (unlikely (status)) {
+       if (have_clip)
+           _cairo_clip_fini (&local_clip);
+
+       return status;
+    }
+
+    /* If the clip cannot be reduced to a set of boxes, we will need to
+     * use a clipmask. Paint is special as it is the only operation that
+     * does not implicitly use a mask, so we may be able to reduce this
+     * operation to a fill...
+     */
+    if (clip != NULL &&
+       extents.is_bounded &&
+       (clip_path = _clip_get_solitary_path (clip)) != NULL)
+    {
+       status = i915_surface_fill_with_alpha (dst, op, source,
+                                              &clip_path->path,
+                                              clip_path->fill_rule,
+                                              clip_path->tolerance,
+                                              clip_path->antialias,
+                                              NULL, opacity);
+    }
+    else
+    {
+       _cairo_boxes_init_for_array (&boxes, clip_boxes, num_boxes);
+       status = _clip_and_composite_boxes (dst, op, source,
+                                           &boxes, CAIRO_ANTIALIAS_DEFAULT,
+                                           &extents, clip, opacity);
+    }
+    if (clip_boxes != boxes.boxes_embedded)
+       free (clip_boxes);
+
+    if (have_clip)
+       _cairo_clip_fini (&local_clip);
+
+    return status;
+}
+
+static cairo_int_status_t
+i915_surface_paint (void                       *abstract_dst,
+                   cairo_operator_t             op,
+                   const cairo_pattern_t       *source,
+                   cairo_clip_t                *clip)
+{
+    i915_surface_t *dst = abstract_dst;
+
+    /* XXX unsupported operators? use pixel shader blending, eventually */
+
+    if (op == CAIRO_OPERATOR_CLEAR && clip == NULL) {
+       dst->deferred_clear = TRUE;
+       return CAIRO_STATUS_SUCCESS;
+    }
+
+    return i915_surface_paint_with_alpha (dst, op, source, clip, 1.);
+}
+
+static cairo_int_status_t
+i915_surface_mask (void                                *abstract_dst,
+                  cairo_operator_t              op,
+                  const cairo_pattern_t        *source,
+                  const cairo_pattern_t        *mask,
+                  cairo_clip_t                 *clip)
+{
+    i915_surface_t *dst = abstract_dst;
+    i915_device_t *device;
+    cairo_composite_rectangles_t extents;
+    i915_shader_t shader;
+    cairo_clip_t local_clip;
+    cairo_region_t *clip_region = NULL;
+    cairo_bool_t need_clip_surface = FALSE;
+    cairo_bool_t have_clip = FALSE;
+    cairo_status_t status;
+
+    if (mask->type == CAIRO_PATTERN_TYPE_SOLID) {
+       const cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) mask;
+       return i915_surface_paint_with_alpha (dst, op, source, clip, solid->color.alpha);
+    }
+
+    status = _cairo_composite_rectangles_init_for_mask (&extents,
+                                                       dst->intel.drm.width,
+                                                       dst->intel.drm.height,
+                                                       op, source, mask, clip);
+    if (unlikely (status))
+       return status;
+
+    if (_cairo_clip_contains_extents (clip, &extents))
+       clip = NULL;
+
+    if (clip != NULL && extents.is_bounded) {
+       clip = _cairo_clip_init_copy (&local_clip, clip);
+       status = _cairo_clip_rectangle (clip, &extents.bounded);
+       if (unlikely (status)) {
+           _cairo_clip_fini (&local_clip);
+           return status;
+       }
+
+       have_clip = TRUE;
+    }
+
+    i915_shader_init (&shader, dst, op, 1.);
+
+    status = i915_shader_acquire_pattern (&shader,
+                                         &shader.source,
+                                         source,
+                                         &extents.bounded);
+    if (unlikely (status))
+       goto err_shader;
+
+    status = i915_shader_acquire_pattern (&shader,
+                                         &shader.mask,
+                                         mask,
+                                         &extents.bounded);
+    if (unlikely (status))
+       goto err_shader;
+
+    if (clip != NULL) {
+       status = _cairo_clip_get_region (clip, &clip_region);
+       if (unlikely (_cairo_status_is_error (status) ||
+                     status == CAIRO_INT_STATUS_NOTHING_TO_DO))
+       {
+           goto err_shader;
+       }
+
+       need_clip_surface = status == CAIRO_INT_STATUS_UNSUPPORTED;
+       if (need_clip_surface)
+           i915_shader_set_clip (&shader, clip);
+
+       if (clip_region != NULL) {
+           cairo_rectangle_int_t rect;
+           cairo_bool_t is_empty;
+
+           status = CAIRO_STATUS_SUCCESS;
+           cairo_region_get_extents (clip_region, &rect);
+           is_empty = ! _cairo_rectangle_intersect (&extents.unbounded, &rect);
+           if (unlikely (is_empty))
+               goto err_shader;
+
+           is_empty = ! _cairo_rectangle_intersect (&extents.bounded, &rect);
+           if (unlikely (is_empty && extents.is_bounded))
+               goto err_shader;
+
+           if (cairo_region_num_rectangles (clip_region) == 1)
+               clip_region = NULL;
+       }
+    }
+
+    if (i915_surface_needs_tiling (dst)) {
+       ASSERT_NOT_REACHED;
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+    }
+
+    device = i915_device (dst);
+    status = cairo_device_acquire (&device->intel.base.base);
+    if (unlikely (status))
+       goto err_shader;
+
+    if (dst->deferred_clear) {
+       status = i915_surface_clear (dst);
+       if (unlikely (status))
+           goto err_shader;
+    }
+
+    status = i915_shader_commit (&shader, device);
+    if (unlikely (status))
+       goto err_device;
+
+    if (clip_region != NULL) {
+       unsigned int n, num_rectangles;
+
+       num_rectangles = cairo_region_num_rectangles (clip_region);
+       for (n = 0; n < num_rectangles; n++) {
+           cairo_rectangle_int_t rect;
+
+           cairo_region_get_rectangle (clip_region, n, &rect);
+
+           shader.add_rectangle (&shader,
+                                 rect.x, rect.y,
+                                 rect.x + rect.width, rect.y + rect.height);
+       }
+    } else {
+       shader.add_rectangle (&shader,
+                             extents.bounded.x, extents.bounded.y,
+                             extents.bounded.x + extents.bounded.width,
+                             extents.bounded.y + extents.bounded.height);
+    }
+
+    if (! extents.is_bounded)
+       status = i915_fixup_unbounded (dst, &extents, clip);
+
+  err_device:
+    cairo_device_release (&device->intel.base.base);
+  err_shader:
+    i915_shader_fini (&shader);
+    if (have_clip)
+       _cairo_clip_fini (&local_clip);
+
+    return status;
+}
+
+static cairo_int_status_t
+i915_surface_stroke (void                      *abstract_dst,
+                    cairo_operator_t            op,
+                    const cairo_pattern_t      *source,
+                    cairo_path_fixed_t         *path,
+                    const cairo_stroke_style_t *stroke_style,
+                    const cairo_matrix_t       *ctm,
+                    const cairo_matrix_t       *ctm_inverse,
+                    double                      tolerance,
+                    cairo_antialias_t           antialias,
+                    cairo_clip_t               *clip)
+{
+    i915_surface_t *dst = abstract_dst;
+    cairo_composite_rectangles_t extents;
+    composite_polygon_info_t info;
+    cairo_box_t boxes_stack[32], *clip_boxes = boxes_stack;
+    int num_boxes = ARRAY_LENGTH (boxes_stack);
+    cairo_clip_t local_clip;
+    cairo_bool_t have_clip = FALSE;
+    cairo_status_t status;
+
+    status = _cairo_composite_rectangles_init_for_stroke (&extents,
+                                                         dst->intel.drm.width,
+                                                         dst->intel.drm.height,
+                                                         op, source,
+                                                         path, stroke_style, ctm,
+                                                         clip);
+    if (unlikely (status))
+       return status;
+
+    if (_cairo_clip_contains_extents (clip, &extents))
+       clip = NULL;
+
+    if (clip != NULL) {
+       clip = _cairo_clip_init_copy (&local_clip, clip);
+       have_clip = TRUE;
+    }
+
+    status = _cairo_clip_to_boxes (&clip, &extents, &clip_boxes, &num_boxes);
+    if (unlikely (status)) {
+       if (have_clip)
+           _cairo_clip_fini (&local_clip);
+
+       return status;
+    }
+
+    if (_cairo_path_fixed_stroke_is_rectilinear (path)) {
+       cairo_boxes_t boxes;
+
+       _cairo_boxes_init (&boxes);
+       _cairo_boxes_limit (&boxes, clip_boxes, num_boxes);
+       status = _cairo_path_fixed_stroke_rectilinear_to_boxes (path,
+                                                               stroke_style,
+                                                               ctm,
+                                                               &boxes);
+       if (likely (status == CAIRO_STATUS_SUCCESS)) {
+           status = _clip_and_composite_boxes (dst, op, source,
+                                               &boxes, antialias,
+                                               &extents, clip, 1.);
+       }
+
+       _cairo_boxes_fini (&boxes);
+
+       if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+           goto CLEANUP_BOXES;
+    }
+
+    _cairo_polygon_init (&info.polygon, clip_boxes, num_boxes);
+
+    status = _cairo_path_fixed_stroke_to_polygon (path,
+                                                 stroke_style,
+                                                 ctm, ctm_inverse,
+                                                 tolerance,
+                                                 &info.polygon);
+    if (unlikely (status))
+       goto CLEANUP_POLYGON;
+
+    if (extents.is_bounded) {
+       cairo_rectangle_int_t rect;
+
+       _cairo_box_round_to_rectangle (&info.polygon.extents, &rect);
+       if (! _cairo_rectangle_intersect (&extents.bounded, &rect))
+           goto CLEANUP_POLYGON;
+    }
+
+    if (info.polygon.num_edges == 0) {
+       if (! extents.is_bounded)
+           status = i915_fixup_unbounded (dst, &extents, clip);
+
+       goto CLEANUP_POLYGON;
+    }
+
+    info.fill_rule = CAIRO_FILL_RULE_WINDING;
+    info.antialias = antialias;
+    status = i915_clip_and_composite_spans (dst, op, source, antialias,
+                                           _composite_polygon_spans, &info,
+                                           &extents, clip, 1.);
+
+CLEANUP_POLYGON:
+    _cairo_polygon_fini (&info.polygon);
+
+CLEANUP_BOXES:
+    if (clip_boxes != boxes_stack)
+       free (clip_boxes);
+
+    if (have_clip)
+       _cairo_clip_fini (&local_clip);
+
+    return status;
+}
+
+static cairo_int_status_t
+i915_surface_fill (void                        *abstract_dst,
+                  cairo_operator_t      op,
+                  const cairo_pattern_t*source,
+                  cairo_path_fixed_t   *path,
+                  cairo_fill_rule_t     fill_rule,
+                  double                tolerance,
+                  cairo_antialias_t     antialias,
+                  cairo_clip_t         *clip)
+{
+    return i915_surface_fill_with_alpha (abstract_dst, op, source, path, fill_rule, tolerance, antialias, clip, 1.);
+}
+
+static const cairo_surface_backend_t i915_surface_backend = {
+    CAIRO_SURFACE_TYPE_DRM,
+    _cairo_default_context_create,
+
+    i915_surface_create_similar,
+    i915_surface_finish,
+    intel_surface_acquire_source_image,
+    intel_surface_release_source_image,
+
+    NULL, NULL, NULL,
+    NULL, /* composite */
+    NULL, /* fill */
+    NULL, /* trapezoids */
+    NULL, /* span */
+    NULL, /* check-span */
+
+    NULL, /* copy_page */
+    NULL, /* show_page */
+    _cairo_drm_surface_get_extents,
+    NULL, /* old-glyphs */
+    _cairo_drm_surface_get_font_options,
+
+    i915_surface_flush,
+    NULL, /* mark_dirty */
+    intel_scaled_font_fini,
+    intel_scaled_glyph_fini,
+
+    i915_surface_paint,
+    i915_surface_mask,
+    i915_surface_stroke,
+    i915_surface_fill,
+    i915_surface_glyphs,
+};
+
+static void
+i915_surface_init (i915_surface_t *surface,
+                  cairo_drm_device_t *device,
+                  cairo_format_t format,
+                  int width, int height)
+{
+    intel_surface_init (&surface->intel, &i915_surface_backend, device,
+                       format, width, height);
+
+    switch (format) {
+    default:
+    case CAIRO_FORMAT_INVALID:
+    case CAIRO_FORMAT_A1:
+       ASSERT_NOT_REACHED;
+    case CAIRO_FORMAT_ARGB32:
+       surface->map0 = MAPSURF_32BIT | MT_32BIT_ARGB8888;
+       surface->colorbuf = COLR_BUF_ARGB8888 | DEPTH_FRMT_24_FIXED_8_OTHER;
+       break;
+    case CAIRO_FORMAT_RGB24:
+       surface->map0 = MAPSURF_32BIT | MT_32BIT_XRGB8888;
+       surface->colorbuf = COLR_BUF_ARGB8888 | DEPTH_FRMT_24_FIXED_8_OTHER;
+       break;
+    case CAIRO_FORMAT_RGB16_565:
+       surface->map0 = MAPSURF_16BIT | MT_16BIT_RGB565;
+       surface->colorbuf = COLR_BUF_RGB565;
+       break;
+    case CAIRO_FORMAT_A8:
+       surface->map0 = MAPSURF_8BIT | MT_8BIT_A8;
+       surface->colorbuf = COLR_BUF_8BIT | DEPTH_FRMT_24_FIXED_8_OTHER;
+       break;
+    }
+    surface->colorbuf |= DSTORG_HORT_BIAS (0x8) | DSTORG_VERT_BIAS (0x8);
+    surface->map0 |= ((height - 1) << MS3_HEIGHT_SHIFT) |
+                    ((width  - 1) << MS3_WIDTH_SHIFT);
+    surface->map1 = 0;
+
+    surface->is_current_texture = 0;
+    surface->deferred_clear = FALSE;
+
+    surface->offset = 0;
+
+    surface->stencil  = NULL;
+    surface->cache = NULL;
+}
+
+cairo_surface_t *
+i915_surface_create_internal (cairo_drm_device_t *base_dev,
+                             cairo_format_t format,
+                             int width, int height,
+                             uint32_t tiling,
+                             cairo_bool_t gpu_target)
+{
+    i915_surface_t *surface;
+    cairo_status_t status_ignored;
+
+    surface = malloc (sizeof (i915_surface_t));
+    if (unlikely (surface == NULL))
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+    i915_surface_init (surface, base_dev, format, width, height);
+
+    if (width && height) {
+       uint32_t size, stride;
+       intel_bo_t *bo;
+
+       width = (width + 3) & -4;
+       stride = cairo_format_stride_for_width (surface->intel.drm.format, width);
+       /* check for tiny surfaces for which tiling is irrelevant */
+       if (height * stride <= 4096)
+           tiling = I915_TILING_NONE;
+       if (tiling != I915_TILING_NONE && stride <= 512)
+           tiling = I915_TILING_NONE;
+       if (tiling != I915_TILING_NONE) {
+           if (height <= 8)
+               tiling = I915_TILING_NONE;
+           else if (height <= 16)
+               tiling = I915_TILING_X;
+       }
+       /* large surfaces we need to blt, so force TILING_X */
+       if (height > 2048)
+           tiling = I915_TILING_X;
+       /* but there is a maximum limit to the tiling pitch */
+       if (tiling != I915_TILING_NONE && stride > 8192)
+           tiling = I915_TILING_NONE;
+
+       stride = i915_tiling_stride (tiling, stride);
+       assert (stride >= (uint32_t) cairo_format_stride_for_width (surface->intel.drm.format, width));
+       assert (tiling == I915_TILING_NONE || stride <= 8192);
+       height = i915_tiling_height (tiling, height);
+       if (height > 64*1024) {
+           free (surface);
+           cairo_device_destroy (&base_dev->base);
+           return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE));
+       }
+
+       size = stride * height;
+       bo = intel_bo_create (to_intel_device (&base_dev->base),
+                             i915_tiling_size (tiling, size), size,
+                             gpu_target, tiling, stride);
+       if (bo == NULL) {
+           status_ignored = _cairo_drm_surface_finish (&surface->intel.drm);
+           free (surface);
+           return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+       }
+       assert (bo->base.size >= size);
+
+       surface->intel.drm.bo = &bo->base;
+       surface->intel.drm.stride = stride;
+
+       surface->map0 |= MS3_tiling (tiling);
+       surface->map1 = (stride/4 - 1) << MS4_PITCH_SHIFT;
+    }
+
+    return &surface->intel.drm.base;
+}
+
+static cairo_surface_t *
+i915_surface_create (cairo_drm_device_t *base_dev,
+                    cairo_format_t format,
+                    int width, int height)
+{
+    switch (format) {
+    case CAIRO_FORMAT_ARGB32:
+    case CAIRO_FORMAT_RGB16_565:
+    case CAIRO_FORMAT_RGB24:
+    case CAIRO_FORMAT_A8:
+       break;
+    case CAIRO_FORMAT_INVALID:
+    default:
+    case CAIRO_FORMAT_A1:
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
+    }
+
+    return i915_surface_create_internal (base_dev, format, width, height,
+                                        I915_TILING_DEFAULT, TRUE);
+}
+
+static cairo_surface_t *
+i915_surface_create_for_name (cairo_drm_device_t *base_dev,
+                             unsigned int name,
+                             cairo_format_t format,
+                             int width, int height, int stride)
+{
+    i915_surface_t *surface;
+
+    /* Vol I, p134: size restrictions for textures */
+    /* Vol I, p129: destination surface stride must be a multiple of 32 bytes */
+    if (stride < cairo_format_stride_for_width (format, (width + 3) & -4) ||
+       stride & 31)
+    {
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_STRIDE));
+    }
+
+    switch (format) {
+    default:
+    case CAIRO_FORMAT_INVALID:
+    case CAIRO_FORMAT_A1:
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
+    case CAIRO_FORMAT_ARGB32:
+    case CAIRO_FORMAT_RGB16_565:
+    case CAIRO_FORMAT_RGB24:
+    case CAIRO_FORMAT_A8:
+       break;
+    }
+
+    surface = malloc (sizeof (i915_surface_t));
+    if (unlikely (surface == NULL))
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+    i915_surface_init (surface, base_dev, format, width, height);
+
+    if (width && height) {
+       surface->intel.drm.stride = stride;
+       surface->map1 = (surface->intel.drm.stride/4 - 1) << MS4_PITCH_SHIFT;
+
+       surface->intel.drm.bo =
+           &intel_bo_create_for_name (to_intel_device (&base_dev->base),
+                                      name)->base;
+       if (unlikely (surface->intel.drm.bo == NULL)) {
+           free (surface);
+           return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+       }
+       to_intel_bo (surface->intel.drm.bo)->stride = stride;
+
+       surface->map0 |= MS3_tiling (to_intel_bo (surface->intel.drm.bo)->tiling);
+    }
+
+    return &surface->intel.drm.base;
+}
+
+static cairo_status_t
+i915_buffer_cache_init (intel_buffer_cache_t *cache,
+                       i915_device_t *device,
+                       cairo_format_t format,
+                       int width, int height)
+{
+    const uint32_t tiling = I915_TILING_DEFAULT;
+    uint32_t stride, size;
+
+    assert ((width & 3) == 0);
+    assert ((height & 1) == 0);
+    cache->buffer.width = width;
+    cache->buffer.height = height;
+
+    switch (format) {
+    case CAIRO_FORMAT_INVALID:
+    case CAIRO_FORMAT_A1:
+    case CAIRO_FORMAT_RGB24:
+    case CAIRO_FORMAT_RGB16_565:
+       ASSERT_NOT_REACHED;
+    case CAIRO_FORMAT_ARGB32:
+       cache->buffer.map0 = MAPSURF_32BIT | MT_32BIT_ARGB8888;
+       stride = width * 4;
+       break;
+    case CAIRO_FORMAT_A8:
+       cache->buffer.map0 = MAPSURF_8BIT | MT_8BIT_I8;
+       stride = width;
+       break;
+    }
+    assert ((stride & 7) == 0);
+    assert (i915_tiling_stride (tiling, stride) == stride);
+    assert (i915_tiling_height (tiling, height) == height);
+
+    size = height * stride;
+    assert (i915_tiling_size (tiling, size) == size);
+    cache->buffer.bo = intel_bo_create (&device->intel, size, size, FALSE, tiling, stride);
+    if (unlikely (cache->buffer.bo == NULL))
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    cache->buffer.stride = cache->buffer.bo->stride;
+
+    cache->buffer.map0 |= ((height - 1) << MS3_HEIGHT_SHIFT) |
+                         ((width  - 1) << MS3_WIDTH_SHIFT);
+    cache->buffer.map0 |= MS3_tiling (tiling);
+    cache->buffer.map1 = ((stride / 4) - 1) << MS4_PITCH_SHIFT;
+
+    cache->ref_count = 0;
+    cairo_list_init (&cache->link);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+i915_surface_t *
+i915_surface_create_from_cacheable_image_internal (i915_device_t *device,
+                                                  cairo_image_surface_t *image)
+{
+    i915_surface_t *surface;
+    cairo_status_t status;
+    cairo_list_t *caches;
+    intel_buffer_cache_t *cache;
+    cairo_rtree_node_t *node;
+    cairo_format_t format;
+    int width, height, bpp;
+
+    format = image->format;
+    if (format == CAIRO_FORMAT_A1)
+       format = CAIRO_FORMAT_A8;
+
+    width = image->width;
+    height = image->height;
+    if (width > IMAGE_CACHE_WIDTH/2 || height > IMAGE_CACHE_HEIGHT/2) {
+       surface = (i915_surface_t *)
+           i915_surface_create_internal (&device->intel.base,
+                                         format,
+                                         width, height,
+                                         I915_TILING_NONE, FALSE);
+       if (unlikely (surface->intel.drm.base.status))
+           return surface;
+
+       status = intel_bo_put_image (&device->intel,
+                                    to_intel_bo (surface->intel.drm.bo),
+                                    image,
+                                    0, 0,
+                                    width, height,
+                                    0, 0);
+
+       if (unlikely (status)) {
+           cairo_surface_destroy (&surface->intel.drm.base);
+           return (i915_surface_t *) _cairo_surface_create_in_error (status);
+       }
+
+       return surface;
+    }
+
+    status = cairo_device_acquire (&device->intel.base.base);
+    if (unlikely (status))
+       return (i915_surface_t *) _cairo_surface_create_in_error (status);
+
+    switch (image->format) {
+    case CAIRO_FORMAT_ARGB32:
+    case CAIRO_FORMAT_RGB24:
+    case CAIRO_FORMAT_RGB16_565:
+       caches = &device->image_caches[0];
+       format = CAIRO_FORMAT_ARGB32;
+       bpp = 4;
+       break;
+    case CAIRO_FORMAT_A8:
+    case CAIRO_FORMAT_A1:
+       caches = &device->image_caches[1];
+       format = CAIRO_FORMAT_A8;
+       bpp = 1;
+       break;
+    case CAIRO_FORMAT_INVALID:
+    default:
+       ASSERT_NOT_REACHED;
+       status = _cairo_error (CAIRO_STATUS_INVALID_FORMAT);
+       goto CLEANUP_DEVICE;
+    }
+
+    node = NULL;
+    cairo_list_foreach_entry (cache, intel_buffer_cache_t, caches, link) {
+       if (! intel_bo_is_inactive (&device->intel, cache->buffer.bo))
+           continue;
+
+       status = _cairo_rtree_insert (&cache->rtree, width, height, &node);
+       if (unlikely (_cairo_status_is_error (status)))
+           goto CLEANUP_DEVICE;
+       if (status == CAIRO_STATUS_SUCCESS)
+           break;
+    }
+    if (node == NULL) {
+       cache = malloc (sizeof (intel_buffer_cache_t));
+       if (unlikely (cache == NULL)) {
+           status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+           goto CLEANUP_DEVICE;
+       }
+
+       status = i915_buffer_cache_init (cache, device, format,
+                                        IMAGE_CACHE_WIDTH,
+                                        IMAGE_CACHE_HEIGHT);
+       if (unlikely (status)) {
+           free (cache);
+           goto CLEANUP_DEVICE;
+       }
+
+       _cairo_rtree_init (&cache->rtree,
+                          IMAGE_CACHE_WIDTH,
+                          IMAGE_CACHE_HEIGHT,
+                          4,
+                          sizeof (i915_image_private_t));
+
+       status = _cairo_rtree_insert (&cache->rtree, width, height, &node);
+       assert (status == CAIRO_STATUS_SUCCESS);
+
+       cairo_list_init (&cache->link);
+    }
+    cairo_list_move (&cache->link, caches);
+    ((i915_image_private_t *) node)->container = cache;
+
+    status = intel_bo_put_image (&device->intel,
+                                cache->buffer.bo,
+                                image,
+                                0, 0,
+                                width, height,
+                                node->x, node->y);
+    if (unlikely (status))
+       goto CLEANUP_CACHE;
+
+    surface = malloc (sizeof (i915_surface_t));
+    if (unlikely (surface == NULL)) {
+       status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+       goto CLEANUP_CACHE;
+    }
+
+    i915_surface_init (surface, &device->intel.base,
+                      format, width, height);
+
+    surface->intel.drm.stride = cache->buffer.stride;
+
+    surface->map0 |= MS3_tiling (cache->buffer.bo->tiling);
+    surface->map1 = (surface->intel.drm.stride/4 - 1) << MS4_PITCH_SHIFT;
+
+    surface->intel.drm.bo = &intel_bo_reference (cache->buffer.bo)->base;
+    surface->offset = node->y * cache->buffer.stride + bpp * node->x;
+
+    surface->cache = (i915_image_private_t *) node;
+    cache->ref_count++;
+
+    cairo_device_release (&device->intel.base.base);
+
+    return surface;
+
+CLEANUP_CACHE:
+    _cairo_rtree_node_destroy (&cache->rtree, node);
+    if (cache->ref_count == 0) {
+       intel_bo_destroy (&device->intel, cache->buffer.bo);
+       _cairo_rtree_fini (&cache->rtree);
+       cairo_list_del (&cache->link);
+       free (cache);
+    }
+CLEANUP_DEVICE:
+    cairo_device_release (&device->intel.base.base);
+    return (i915_surface_t *) _cairo_surface_create_in_error (status);
+}
+
+static cairo_surface_t *
+i915_surface_create_from_cacheable_image (cairo_drm_device_t *device,
+                                         cairo_surface_t *source)
+{
+    i915_surface_t *surface;
+    cairo_image_surface_t *image;
+    void *image_extra;
+    cairo_status_t status;
+
+    status = _cairo_surface_acquire_source_image (source, &image, &image_extra);
+    if (unlikely (status))
+       return _cairo_surface_create_in_error (status);
+
+    surface = i915_surface_create_from_cacheable_image_internal ((i915_device_t *) device, image);
+
+    _cairo_surface_release_source_image (source, image, image_extra);
+
+    return &surface->intel.drm.base;
+}
+
+static cairo_status_t
+i915_surface_enable_scan_out (void *abstract_surface)
+{
+    i915_surface_t *surface = abstract_surface;
+    intel_bo_t *bo;
+    cairo_status_t status;
+
+    if (unlikely (surface->intel.drm.bo == NULL))
+       return _cairo_error (CAIRO_STATUS_INVALID_SIZE);
+
+    bo = to_intel_bo (surface->intel.drm.bo);
+    if (bo->tiling == I915_TILING_Y) {
+       status = i915_surface_batch_flush (surface);
+       if (unlikely (status))
+           return status;
+
+       bo->tiling = I915_TILING_X;
+       surface->map0 &= ~MS3_tiling (I915_TILING_Y);
+       surface->map0 |= MS3_tiling (I915_TILING_X);
+    }
+
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+i915_device_flush (cairo_drm_device_t *device)
+{
+    cairo_status_t status;
+
+    if (unlikely (device->base.finished))
+       return CAIRO_STATUS_SUCCESS;
+
+    status = cairo_device_acquire (&device->base);
+    if (likely (status == CAIRO_STATUS_SUCCESS)) {
+       status = i915_batch_flush ((i915_device_t *) device);
+       cairo_device_release (&device->base);
+    }
+
+    return status;
+}
+
+static cairo_int_status_t
+i915_device_throttle (cairo_drm_device_t *device)
+{
+    cairo_status_t status;
+
+    status = cairo_device_acquire (&device->base);
+    if (unlikely (status))
+       return status;
+
+    status = i915_batch_flush ((i915_device_t *) device);
+    intel_throttle ((intel_device_t *) device);
+
+    cairo_device_release (&device->base);
+
+    return status;
+}
+
+static void
+i915_device_destroy (void *data)
+{
+    i915_device_t *device = data;
+
+    if (device->last_vbo)
+       intel_bo_destroy (&device->intel, device->last_vbo);
+
+    i915_batch_cleanup (device);
+
+    intel_device_fini (&device->intel);
+    free (device);
+}
+
+COMPILE_TIME_ASSERT (sizeof (i915_batch_setup) == sizeof (((i915_device_t *)0)->batch_header));
+COMPILE_TIME_ASSERT (offsetof (i915_device_t, batch_base) == offsetof (i915_device_t, batch_header) + sizeof (i915_batch_setup));
+
+cairo_drm_device_t *
+_cairo_drm_i915_device_create (int fd, dev_t dev_id, int vendor_id, int chip_id)
+{
+    i915_device_t *device;
+    cairo_status_t status;
+    uint64_t gtt_size;
+    int n;
+
+    if (! intel_info (fd, &gtt_size))
+       return NULL;
+
+    device = malloc (sizeof (i915_device_t));
+    if (device == NULL)
+       return (cairo_drm_device_t *) _cairo_device_create_in_error (CAIRO_STATUS_NO_MEMORY);
+
+    status = intel_device_init (&device->intel, fd);
+    if (unlikely (status)) {
+       free (device);
+       return (cairo_drm_device_t *) _cairo_device_create_in_error (status);
+    }
+
+    device->debug = 0;
+    if (getenv ("CAIRO_DEBUG_DRM") != NULL)
+       device->debug = I915_DEBUG_SYNC;
+
+    n = intel_get (fd, I915_PARAM_NUM_FENCES_AVAIL);
+    if (n == 0)
+       n = 8;
+    device->batch.fences_avail = n - 2; /* conservative */
+
+    device->batch.gtt_avail_size = device->intel.gtt_avail_size / 4;
+    device->batch.est_gtt_size = I915_BATCH_SIZE;
+    device->batch.total_gtt_size = I915_BATCH_SIZE;
+    device->batch.exec_count = 0;
+    device->batch.reloc_count = 0;
+    device->batch.used = 0;
+    device->batch.fences = 0;
+
+    memcpy (device->batch_header, i915_batch_setup, sizeof (i915_batch_setup));
+    device->vbo = 0;
+    device->vbo_offset = 0;
+    device->vbo_used = 0;
+    device->vertex_index = 0;
+    device->vertex_count = 0;
+    device->last_vbo = NULL;
+
+    for (n = 0; n < ARRAY_LENGTH (device->image_caches); n++)
+       cairo_list_init (&device->image_caches[n]);
+
+    device->intel.base.surface.create = i915_surface_create;
+    device->intel.base.surface.create_for_name = i915_surface_create_for_name;
+    device->intel.base.surface.create_from_cacheable_image = i915_surface_create_from_cacheable_image;
+
+    device->intel.base.surface.flink = _cairo_drm_surface_flink;
+    device->intel.base.surface.enable_scan_out = i915_surface_enable_scan_out;
+    device->intel.base.surface.map_to_image = intel_surface_map_to_image;
+
+    device->intel.base.device.flush = i915_device_flush;
+    device->intel.base.device.throttle = i915_device_throttle;
+    device->intel.base.device.destroy = i915_device_destroy;
+
+    device->floats_per_vertex = 0;
+    device->current_source = NULL;
+    device->current_mask = NULL;
+    device->current_clip = NULL;
+
+    i915_device_reset (device);
+
+    return _cairo_drm_device_init (&device->intel.base,
+                                  fd, dev_id, vendor_id, chip_id,
+                                  16*1024);
+}
diff --git a/src/drm/cairo-drm-i965-glyphs.c b/src/drm/cairo-drm-i965-glyphs.c
new file mode 100644 (file)
index 0000000..c66a63d
--- /dev/null
@@ -0,0 +1,504 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-composite-rectangles-private.h"
+#include "cairo-drm-i965-private.h"
+#include "cairo-error-private.h"
+#include "cairo-rtree-private.h"
+
+typedef struct _i965_glyphs i965_glyphs_t;
+
+typedef float *
+(*i965_get_rectangle_func_t) (i965_glyphs_t *glyphs);
+
+struct _i965_glyphs {
+    i965_get_rectangle_func_t get_rectangle;
+    i965_shader_t shader;
+
+    struct i965_vbo head, *tail;
+
+    unsigned int vbo_offset;
+    float *vbo_base;
+};
+
+static float *
+i965_glyphs_emit_rectangle (i965_glyphs_t *glyphs)
+{
+    return i965_add_rectangle (glyphs->shader.device);
+}
+
+static float *
+i965_glyphs_accumulate_rectangle (i965_glyphs_t *glyphs)
+{
+    float *vertices;
+    uint32_t size;
+
+    size = glyphs->shader.device->rectangle_size;
+    if (unlikely (glyphs->vbo_offset + size > I965_VERTEX_SIZE)) {
+       struct i965_vbo *vbo;
+
+       vbo = malloc (sizeof (struct i965_vbo));
+       if (unlikely (vbo == NULL)) {
+           /* throw error! */
+       }
+
+       glyphs->tail->next = vbo;
+       glyphs->tail = vbo;
+
+       vbo->next = NULL;
+       vbo->bo = intel_bo_create (&glyphs->shader.device->intel,
+                                  I965_VERTEX_SIZE, I965_VERTEX_SIZE,
+                                  FALSE, I915_TILING_NONE, 0);
+       vbo->count = 0;
+
+       glyphs->vbo_offset = 0;
+       glyphs->vbo_base = intel_bo_map (&glyphs->shader.device->intel, vbo->bo);
+    }
+
+    vertices = glyphs->vbo_base + glyphs->vbo_offset;
+    glyphs->vbo_offset += size;
+    glyphs->tail->count += 3;
+
+    return vertices;
+}
+
+static void
+i965_add_glyph_rectangle (i965_glyphs_t *glyphs,
+                         int x1, int y1,
+                         int x2, int y2,
+                         intel_glyph_t *glyph)
+{
+    float *v;
+
+    /* Each vertex is:
+     *   2 vertex coordinates
+     *   1 glyph texture coordinate
+     */
+
+    v = glyphs->get_rectangle (glyphs);
+
+    /* bottom right */
+    *v++ = x2; *v++ = y2;
+    *v++ = glyph->texcoord[0];
+
+    /* bottom left */
+    *v++ = x1; *v++ = y2;
+    *v++ = glyph->texcoord[1];
+
+    /* top left */
+    *v++ = x1; *v++ = y1;
+    *v++ = glyph->texcoord[2];
+}
+
+static cairo_status_t
+i965_surface_mask_internal (i965_surface_t *dst,
+                           cairo_operator_t             op,
+                           const cairo_pattern_t       *source,
+                           i965_surface_t *mask,
+                           cairo_clip_t                *clip,
+                           const cairo_composite_rectangles_t *extents)
+{
+    i965_device_t *device;
+    i965_shader_t shader;
+    cairo_region_t *clip_region = NULL;
+    cairo_status_t status;
+
+    i965_shader_init (&shader, dst, op);
+
+    status = i965_shader_acquire_pattern (&shader, &shader.source,
+                                         source, &extents->bounded);
+    if (unlikely (status))
+       return status;
+
+    shader.mask.type.vertex = VS_NONE;
+    shader.mask.type.fragment = FS_SURFACE;
+    shader.mask.base.content = mask->intel.drm.base.content;
+    shader.mask.base.filter = i965_filter (CAIRO_FILTER_NEAREST);
+    shader.mask.base.extend = i965_extend (CAIRO_EXTEND_NONE);
+
+    cairo_matrix_init_translate (&shader.mask.base.matrix,
+                                -extents->bounded.x,
+                                -extents->bounded.y);
+    cairo_matrix_scale (&shader.mask.base.matrix,
+                       1. / mask->intel.drm.width,
+                       1. / mask->intel.drm.height);
+
+    shader.mask.base.bo = to_intel_bo (mask->intel.drm.bo);
+    shader.mask.base.format = mask->intel.drm.format;
+    shader.mask.base.width = mask->intel.drm.width;
+    shader.mask.base.height = mask->intel.drm.height;
+    shader.mask.base.stride = mask->intel.drm.stride;
+
+    if (clip != NULL) {
+       status = _cairo_clip_get_region (clip, &clip_region);
+       assert (status == CAIRO_STATUS_SUCCESS || status == CAIRO_INT_STATUS_UNSUPPORTED);
+
+       if (clip_region != NULL && cairo_region_num_rectangles (clip_region) == 1)
+           clip_region = NULL;
+
+       if (status == CAIRO_INT_STATUS_UNSUPPORTED)
+           i965_shader_set_clip (&shader, clip);
+    }
+
+    status = cairo_device_acquire (dst->intel.drm.base.device);
+    if (unlikely (status))
+       goto CLEANUP_SHADER;
+
+    device = i965_device (dst);
+
+    status = i965_shader_commit (&shader, device);
+    if (unlikely (status))
+       goto CLEANUP_DEVICE;
+
+    if (clip_region != NULL) {
+       unsigned int n, num_rectangles;
+
+       num_rectangles = cairo_region_num_rectangles (clip_region);
+       for (n = 0; n < num_rectangles; n++) {
+           cairo_rectangle_int_t rect;
+
+           cairo_region_get_rectangle (clip_region, n, &rect);
+
+           i965_shader_add_rectangle (&shader,
+                                      rect.x, rect.y,
+                                      rect.width, rect.height);
+       }
+    } else {
+       i965_shader_add_rectangle (&shader,
+                                  extents->bounded.x,
+                                  extents->bounded.y,
+                                  extents->bounded.width,
+                                  extents->bounded.height);
+    }
+
+    if (! extents->is_bounded)
+       status = i965_fixup_unbounded (dst, extents, clip);
+
+  CLEANUP_DEVICE:
+    cairo_device_release (&device->intel.base.base);
+  CLEANUP_SHADER:
+    i965_shader_fini (&shader);
+    return status;
+}
+
+cairo_int_status_t
+i965_surface_glyphs (void                      *abstract_surface,
+                    cairo_operator_t            op,
+                    const cairo_pattern_t      *source,
+                    cairo_glyph_t              *g,
+                    int                         num_glyphs,
+                    cairo_scaled_font_t        *scaled_font,
+                    cairo_clip_t               *clip,
+                    int *num_remaining)
+{
+    i965_surface_t *surface = abstract_surface;
+    i965_surface_t *mask = NULL;
+    i965_device_t *device;
+    i965_glyphs_t glyphs;
+    cairo_composite_rectangles_t extents;
+    cairo_clip_t local_clip;
+    cairo_bool_t have_clip = FALSE;
+    cairo_bool_t overlap;
+    cairo_region_t *clip_region = NULL;
+    intel_bo_t *last_bo = NULL;
+    cairo_scaled_glyph_t *glyph_cache[64];
+    cairo_status_t status;
+    int mask_x = 0, mask_y = 0;
+    int i = 0;
+
+    *num_remaining = 0;
+    status = _cairo_composite_rectangles_init_for_glyphs (&extents,
+                                                         surface->intel.drm.width,
+                                                         surface->intel.drm.height,
+                                                         op, source,
+                                                         scaled_font,
+                                                         g, num_glyphs,
+                                                         clip,
+                                                         &overlap);
+    if (unlikely (status))
+       return status;
+
+    if (clip != NULL && _cairo_clip_contains_rectangle (clip, &extents.mask))
+       clip = NULL;
+
+    if (clip != NULL && extents.is_bounded) {
+       clip = _cairo_clip_init_copy (&local_clip, clip);
+       status = _cairo_clip_rectangle (clip, &extents.bounded);
+       if (unlikely (status))
+           return status;
+
+       have_clip = TRUE;
+    }
+
+    if (overlap || ! extents.is_bounded) {
+       cairo_format_t format;
+
+       format = CAIRO_FORMAT_A8;
+       if (scaled_font->options.antialias == CAIRO_ANTIALIAS_SUBPIXEL)
+           format = CAIRO_FORMAT_ARGB32;
+
+       mask = (i965_surface_t *)
+           i965_surface_create_internal (&i965_device (surface)->intel.base,
+                                         format,
+                                         extents.bounded.width,
+                                         extents.bounded.height,
+                                         I965_TILING_DEFAULT,
+                                         TRUE);
+       if (unlikely (mask->intel.drm.base.status))
+           return mask->intel.drm.base.status;
+
+       status = _cairo_surface_paint (&mask->intel.drm.base,
+                                      CAIRO_OPERATOR_CLEAR,
+                                      &_cairo_pattern_clear.base,
+                                      NULL);
+       if (unlikely (status)) {
+           cairo_surface_destroy (&mask->intel.drm.base);
+           return status;
+       }
+
+       i965_shader_init (&glyphs.shader, mask, CAIRO_OPERATOR_ADD);
+
+       status = i965_shader_acquire_pattern (&glyphs.shader, &glyphs.shader.source,
+                                             &_cairo_pattern_white.base,
+                                             &extents.bounded);
+       if (unlikely (status)) {
+           cairo_surface_destroy (&mask->intel.drm.base);
+           return status;
+       }
+
+       mask_x = -extents.bounded.x;
+       mask_y = -extents.bounded.y;
+    } else {
+       i965_shader_init (&glyphs.shader, surface, op);
+
+       status = i965_shader_acquire_pattern (&glyphs.shader, &glyphs.shader.source,
+                                             source, &extents.bounded);
+       if (unlikely (status))
+           return status;
+
+       if (clip != NULL) {
+           status = _cairo_clip_get_region (clip, &clip_region);
+           assert (status == CAIRO_STATUS_SUCCESS || status == CAIRO_INT_STATUS_UNSUPPORTED);
+
+           if (status == CAIRO_INT_STATUS_UNSUPPORTED)
+               i965_shader_set_clip (&glyphs.shader, clip);
+       }
+    }
+
+    glyphs.head.next = NULL;
+    glyphs.head.bo = NULL;
+    glyphs.head.count = 0;
+    glyphs.tail = &glyphs.head;
+
+    device = i965_device (surface);
+    if (mask != NULL || clip_region == NULL) {
+       glyphs.get_rectangle = i965_glyphs_emit_rectangle;
+    } else {
+       glyphs.get_rectangle = i965_glyphs_accumulate_rectangle;
+       glyphs.head.bo = intel_bo_create (&device->intel,
+                                         I965_VERTEX_SIZE, I965_VERTEX_SIZE,
+                                         FALSE, I915_TILING_NONE, 0);
+       if (unlikely (glyphs.head.bo == NULL))
+           return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+       glyphs.vbo_base = intel_bo_map (&device->intel, glyphs.head.bo);
+    }
+    glyphs.vbo_offset = 0;
+
+    status = cairo_device_acquire (&device->intel.base.base);
+    if (unlikely (status))
+       goto CLEANUP_GLYPHS;
+
+    _cairo_scaled_font_freeze_cache (scaled_font);
+    //private = _cairo_scaled_font_get_device (scaled_font, device);
+    if (scaled_font->surface_private == NULL) {
+       scaled_font->surface_private = device;
+       scaled_font->surface_backend = surface->intel.drm.base.backend;
+       cairo_list_add (&scaled_font->link, &device->intel.fonts);
+    }
+
+    memset (glyph_cache, 0, sizeof (glyph_cache));
+
+    for (i = 0; i < num_glyphs; i++) {
+       cairo_scaled_glyph_t *scaled_glyph;
+       int x, y, x1, x2, y1, y2;
+       int cache_index = g[i].index % ARRAY_LENGTH (glyph_cache);
+       intel_glyph_t *glyph;
+
+       scaled_glyph = glyph_cache[cache_index];
+       if (scaled_glyph == NULL ||
+           _cairo_scaled_glyph_index (scaled_glyph) != g[i].index)
+       {
+           status = _cairo_scaled_glyph_lookup (scaled_font,
+                                                g[i].index,
+                                                CAIRO_SCALED_GLYPH_INFO_METRICS,
+                                                &scaled_glyph);
+           if (unlikely (status))
+               goto FINISH;
+
+           glyph_cache[cache_index] = scaled_glyph;
+       }
+
+       if (unlikely (scaled_glyph->metrics.width  == 0 ||
+                     scaled_glyph->metrics.height == 0))
+       {
+           continue;
+       }
+
+       /* XXX glyph images are snapped to pixel locations */
+       x = _cairo_lround (g[i].x);
+       y = _cairo_lround (g[i].y);
+
+       x1 = x + _cairo_fixed_integer_floor (scaled_glyph->bbox.p1.x);
+       y1 = y + _cairo_fixed_integer_floor (scaled_glyph->bbox.p1.y);
+       x2 = x + _cairo_fixed_integer_ceil (scaled_glyph->bbox.p2.x);
+       y2 = y + _cairo_fixed_integer_ceil (scaled_glyph->bbox.p2.y);
+
+       if (x2 < extents.bounded.x ||
+           y2 < extents.bounded.y ||
+           x1 > extents.bounded.x + extents.bounded.width ||
+           y1 > extents.bounded.y + extents.bounded.height)
+       {
+           continue;
+       }
+
+       if (scaled_glyph->surface_private == NULL) {
+           status = intel_get_glyph (&device->intel, scaled_font, scaled_glyph);
+           if (unlikely (status == CAIRO_INT_STATUS_NOTHING_TO_DO)) {
+               status = CAIRO_STATUS_SUCCESS;
+               continue;
+           }
+           if (unlikely (status))
+               goto FINISH;
+       }
+       glyph = intel_glyph_pin (scaled_glyph->surface_private);
+
+       if (glyph->cache->buffer.bo != last_bo) {
+           intel_buffer_cache_t *cache = glyph->cache;
+
+           glyphs.shader.mask.type.vertex   = VS_GLYPHS;
+           glyphs.shader.mask.type.fragment = FS_GLYPHS;
+           glyphs.shader.mask.type.pattern  = PATTERN_BASE;
+
+           glyphs.shader.mask.base.bo = cache->buffer.bo;
+           glyphs.shader.mask.base.format = cache->buffer.format;
+           glyphs.shader.mask.base.width  = cache->buffer.width;
+           glyphs.shader.mask.base.height = cache->buffer.height;
+           glyphs.shader.mask.base.stride = cache->buffer.stride;
+           glyphs.shader.mask.base.filter = i965_filter (CAIRO_FILTER_NEAREST);
+           glyphs.shader.mask.base.extend = i965_extend (CAIRO_EXTEND_NONE);
+           glyphs.shader.mask.base.content = CAIRO_CONTENT_ALPHA; /* XXX */
+
+           glyphs.shader.committed = FALSE;
+           status = i965_shader_commit (&glyphs.shader, device);
+           if (unlikely (status))
+               goto FINISH;
+
+           last_bo = cache->buffer.bo;
+       }
+
+       x2 = x1 + glyph->width;
+       y2 = y1 + glyph->height;
+
+       if (mask_x)
+           x1 += mask_x, x2 += mask_x;
+       if (mask_y)
+           y1 += mask_y, y2 += mask_y;
+
+       i965_add_glyph_rectangle (&glyphs, x1, y1, x2, y2, glyph);
+    }
+
+    if (mask != NULL && clip_region != NULL)
+       i965_clipped_vertices (device, &glyphs.head, clip_region);
+
+    status = CAIRO_STATUS_SUCCESS;
+  FINISH:
+    _cairo_scaled_font_thaw_cache (scaled_font);
+    cairo_device_release (surface->intel.drm.base.device);
+  CLEANUP_GLYPHS:
+    i965_shader_fini (&glyphs.shader);
+
+    if (glyphs.head.bo != NULL) {
+       struct i965_vbo *vbo, *next;
+
+       intel_bo_destroy (&device->intel, glyphs.head.bo);
+       for (vbo = glyphs.head.next; vbo != NULL; vbo = next) {
+           next = vbo->next;
+           intel_bo_destroy (&device->intel, vbo->bo);
+           free (vbo);
+       }
+    }
+
+    if (unlikely (status == CAIRO_INT_STATUS_UNSUPPORTED)) {
+       cairo_path_fixed_t path;
+
+       _cairo_path_fixed_init (&path);
+       status = _cairo_scaled_font_glyph_path (scaled_font,
+                                               g + i, num_glyphs - i,
+                                               &path);
+       if (mask_x | mask_y) {
+           _cairo_path_fixed_translate (&path,
+                                        _cairo_fixed_from_int (mask_x),
+                                        _cairo_fixed_from_int (mask_y));
+       }
+       if (likely (status == CAIRO_STATUS_SUCCESS)) {
+           status = surface->intel.drm.base.backend->fill (glyphs.shader.target,
+                                                           glyphs.shader.op,
+                                                           mask != NULL ? &_cairo_pattern_white.base : source,
+                                                           &path,
+                                                           CAIRO_FILL_RULE_WINDING,
+                                                           0,
+                                                           scaled_font->options.antialias,
+                                                           clip);
+       }
+       _cairo_path_fixed_fini (&path);
+    }
+
+    if (mask != NULL) {
+       if (likely (status == CAIRO_STATUS_SUCCESS)) {
+           status = i965_surface_mask_internal (surface, op, source, mask,
+                                                clip, &extents);
+       }
+       cairo_surface_finish (&mask->intel.drm.base);
+       cairo_surface_destroy (&mask->intel.drm.base);
+    }
+
+    if (have_clip)
+       _cairo_clip_fini (&local_clip);
+
+    return status;
+}
diff --git a/src/drm/cairo-drm-i965-private.h b/src/drm/cairo-drm-i965-private.h
new file mode 100644 (file)
index 0000000..79568a6
--- /dev/null
@@ -0,0 +1,737 @@
+#ifndef CAIRO_DRM_I965_PRIVATE_H
+#define CAIRO_DRM_I965_PRIVATE_H
+
+#include "cairo-drm-intel-private.h"
+
+#include "cairo-hash-private.h"
+#include "cairo-freelist-private.h"
+
+#include "cairo-drm-intel-brw-defines.h"
+
+#include <setjmp.h>
+
+#define BRW_MI_GLOBAL_SNAPSHOT_RESET   (1 << 3)
+
+/*
+ * New regs for broadwater -- we need to split this file up sensibly somehow.
+ */
+#define BRW_3D(Pipeline,Opcode,Subopcode) ((3 << 29) | \
+                                          ((Pipeline) << 27) | \
+                                          ((Opcode) << 24) | \
+                                          ((Subopcode) << 16))
+
+#define BRW_URB_FENCE                          BRW_3D(0, 0, 0)
+#define BRW_CS_URB_STATE                       BRW_3D(0, 0, 1)
+#define BRW_CONSTANT_BUFFER                    BRW_3D(0, 0, 2)
+#define BRW_STATE_PREFETCH                     BRW_3D(0, 0, 3)
+
+#define BRW_STATE_BASE_ADDRESS                 BRW_3D(0, 1, 1)
+#define BRW_STATE_SIP                          BRW_3D(0, 1, 2)
+#define BRW_PIPELINE_SELECT                    BRW_3D(0, 1, 4)
+
+#define NEW_PIPELINE_SELECT                    BRW_3D(1, 1, 4)
+
+#define BRW_MEDIA_STATE_POINTERS               BRW_3D(2, 0, 0)
+#define BRW_MEDIA_OBJECT                       BRW_3D(2, 1, 0)
+
+#define BRW_3DSTATE_PIPELINED_POINTERS         BRW_3D(3, 0, 0)
+#define BRW_3DSTATE_BINDING_TABLE_POINTERS     BRW_3D(3, 0, 1)
+#define BRW_3DSTATE_VERTEX_BUFFERS             BRW_3D(3, 0, 8)
+#define BRW_3DSTATE_VERTEX_ELEMENTS            BRW_3D(3, 0, 9)
+#define BRW_3DSTATE_INDEX_BUFFER               BRW_3D(3, 0, 0xa)
+#define BRW_3DSTATE_VF_STATISTICS              BRW_3D(3, 0, 0xb)
+
+#define BRW_3DSTATE_DRAWING_RECTANGLE          BRW_3D(3, 1, 0)
+#define BRW_3DSTATE_CONSTANT_COLOR             BRW_3D(3, 1, 1)
+#define BRW_3DSTATE_SAMPLER_PALETTE_LOAD       BRW_3D(3, 1, 2)
+#define BRW_3DSTATE_CHROMA_KEY                 BRW_3D(3, 1, 4)
+#define BRW_3DSTATE_DEPTH_BUFFER               BRW_3D(3, 1, 5)
+#define BRW_3DSTATE_POLY_STIPPLE_OFFSET                BRW_3D(3, 1, 6)
+#define BRW_3DSTATE_POLY_STIPPLE_PATTERN       BRW_3D(3, 1, 7)
+#define BRW_3DSTATE_LINE_STIPPLE               BRW_3D(3, 1, 8)
+#define BRW_3DSTATE_GLOBAL_DEPTH_OFFSET_CLAMP  BRW_3D(3, 1, 9)
+/* These two are BLC and CTG only, not BW or CL */
+#define BRW_3DSTATE_AA_LINE_PARAMS             BRW_3D(3, 1, 0xa)
+#define BRW_3DSTATE_GS_SVB_INDEX               BRW_3D(3, 1, 0xb)
+
+#define BRW_PIPE_CONTROL                       BRW_3D(3, 2, 0)
+
+#define BRW_3DPRIMITIVE                                BRW_3D(3, 3, 0)
+
+#define PIPELINE_SELECT_3D             0
+#define PIPELINE_SELECT_MEDIA          1
+
+#define UF0_CS_REALLOC                 (1 << 13)
+#define UF0_VFE_REALLOC                        (1 << 12)
+#define UF0_SF_REALLOC                 (1 << 11)
+#define UF0_CLIP_REALLOC               (1 << 10)
+#define UF0_GS_REALLOC                 (1 << 9)
+#define UF0_VS_REALLOC                 (1 << 8)
+#define UF1_CLIP_FENCE_SHIFT           20
+#define UF1_GS_FENCE_SHIFT             10
+#define UF1_VS_FENCE_SHIFT             0
+#define UF2_CS_FENCE_SHIFT             20
+#define UF2_VFE_FENCE_SHIFT            10
+#define UF2_SF_FENCE_SHIFT             0
+
+/* for BRW_STATE_BASE_ADDRESS */
+#define BASE_ADDRESS_MODIFY            (1 << 0)
+
+/* for BRW_3DSTATE_PIPELINED_POINTERS */
+#define BRW_GS_DISABLE                0
+#define BRW_GS_ENABLE                 1
+#define BRW_CLIP_DISABLE              0
+#define BRW_CLIP_ENABLE                       1
+
+/* for BRW_PIPE_CONTROL */
+#define BRW_PIPE_CONTROL_NOWRITE       (0 << 14)
+#define BRW_PIPE_CONTROL_WRITE_QWORD   (1 << 14)
+#define BRW_PIPE_CONTROL_WRITE_DEPTH   (2 << 14)
+#define BRW_PIPE_CONTROL_WRITE_TIME    (3 << 14)
+#define BRW_PIPE_CONTROL_DEPTH_STALL   (1 << 13)
+#define BRW_PIPE_CONTROL_WC_FLUSH      (1 << 12)
+#define BRW_PIPE_CONTROL_IS_FLUSH      (1 << 11)
+#define BRW_PIPE_CONTROL_NOTIFY_ENABLE (1 << 8)
+#define BRW_PIPE_CONTROL_GLOBAL_GTT    (1 << 2)
+#define BRW_PIPE_CONTROL_LOCAL_PGTT    (0 << 2)
+
+/* VERTEX_BUFFER_STATE Structure */
+#define VB0_BUFFER_INDEX_SHIFT         27
+#define VB0_VERTEXDATA                 (0 << 26)
+#define VB0_INSTANCEDATA               (1 << 26)
+#define VB0_BUFFER_PITCH_SHIFT         0
+
+/* VERTEX_ELEMENT_STATE Structure */
+#define VE0_VERTEX_BUFFER_INDEX_SHIFT  27
+#define VE0_VALID                      (1 << 26)
+#define VE0_FORMAT_SHIFT               16
+#define VE0_OFFSET_SHIFT               0
+#define VE1_VFCOMPONENT_0_SHIFT                28
+#define VE1_VFCOMPONENT_1_SHIFT                24
+#define VE1_VFCOMPONENT_2_SHIFT                20
+#define VE1_VFCOMPONENT_3_SHIFT                16
+#define VE1_DESTINATION_ELEMENT_OFFSET_SHIFT   0
+
+/* 3DPRIMITIVE bits */
+#define BRW_3DPRIMITIVE_VERTEX_SEQUENTIAL (0 << 15)
+#define BRW_3DPRIMITIVE_VERTEX_RANDOM    (1 << 15)
+/* Primitive types are in brw_defines.h */
+#define BRW_3DPRIMITIVE_TOPOLOGY_SHIFT   10
+
+#define BRW_SVG_CTL                   0x7400
+
+#define BRW_SVG_CTL_GS_BA             (0 << 8)
+#define BRW_SVG_CTL_SS_BA             (1 << 8)
+#define BRW_SVG_CTL_IO_BA             (2 << 8)
+#define BRW_SVG_CTL_GS_AUB            (3 << 8)
+#define BRW_SVG_CTL_IO_AUB            (4 << 8)
+#define BRW_SVG_CTL_SIP                       (5 << 8)
+
+#define BRW_SVG_RDATA                 0x7404
+#define BRW_SVG_WORK_CTL              0x7408
+
+#define BRW_VF_CTL                    0x7500
+
+#define BRW_VF_CTL_SNAPSHOT_COMPLETE              (1 << 31)
+#define BRW_VF_CTL_SNAPSHOT_MUX_SELECT_THREADID           (0 << 8)
+#define BRW_VF_CTL_SNAPSHOT_MUX_SELECT_VF_DEBUG           (1 << 8)
+#define BRW_VF_CTL_SNAPSHOT_TYPE_VERTEX_SEQUENCE   (0 << 4)
+#define BRW_VF_CTL_SNAPSHOT_TYPE_VERTEX_INDEX     (1 << 4)
+#define BRW_VF_CTL_SKIP_INITIAL_PRIMITIVES        (1 << 3)
+#define BRW_VF_CTL_MAX_PRIMITIVES_LIMIT_ENABLE    (1 << 2)
+#define BRW_VF_CTL_VERTEX_RANGE_LIMIT_ENABLE      (1 << 1)
+#define BRW_VF_CTL_SNAPSHOT_ENABLE                (1 << 0)
+
+#define BRW_VF_STRG_VAL                       0x7504
+#define BRW_VF_STR_VL_OVR             0x7508
+#define BRW_VF_VC_OVR                 0x750c
+#define BRW_VF_STR_PSKIP              0x7510
+#define BRW_VF_MAX_PRIM                       0x7514
+#define BRW_VF_RDATA                  0x7518
+
+#define BRW_VS_CTL                    0x7600
+#define BRW_VS_CTL_SNAPSHOT_COMPLETE              (1 << 31)
+#define BRW_VS_CTL_SNAPSHOT_MUX_VERTEX_0          (0 << 8)
+#define BRW_VS_CTL_SNAPSHOT_MUX_VERTEX_1          (1 << 8)
+#define BRW_VS_CTL_SNAPSHOT_MUX_VALID_COUNT       (2 << 8)
+#define BRW_VS_CTL_SNAPSHOT_MUX_VS_KERNEL_POINTER  (3 << 8)
+#define BRW_VS_CTL_SNAPSHOT_ALL_THREADS                   (1 << 2)
+#define BRW_VS_CTL_THREAD_SNAPSHOT_ENABLE         (1 << 1)
+#define BRW_VS_CTL_SNAPSHOT_ENABLE                (1 << 0)
+
+#define BRW_VS_STRG_VAL                       0x7604
+#define BRW_VS_RDATA                  0x7608
+
+#define BRW_SF_CTL                    0x7b00
+#define BRW_SF_CTL_SNAPSHOT_COMPLETE              (1 << 31)
+#define BRW_SF_CTL_SNAPSHOT_MUX_VERTEX_0_FF_ID    (0 << 8)
+#define BRW_SF_CTL_SNAPSHOT_MUX_VERTEX_0_REL_COUNT (1 << 8)
+#define BRW_SF_CTL_SNAPSHOT_MUX_VERTEX_1_FF_ID    (2 << 8)
+#define BRW_SF_CTL_SNAPSHOT_MUX_VERTEX_1_REL_COUNT (3 << 8)
+#define BRW_SF_CTL_SNAPSHOT_MUX_VERTEX_2_FF_ID    (4 << 8)
+#define BRW_SF_CTL_SNAPSHOT_MUX_VERTEX_2_REL_COUNT (5 << 8)
+#define BRW_SF_CTL_SNAPSHOT_MUX_VERTEX_COUNT      (6 << 8)
+#define BRW_SF_CTL_SNAPSHOT_MUX_SF_KERNEL_POINTER  (7 << 8)
+#define BRW_SF_CTL_MIN_MAX_PRIMITIVE_RANGE_ENABLE  (1 << 4)
+#define BRW_SF_CTL_DEBUG_CLIP_RECTANGLE_ENABLE    (1 << 3)
+#define BRW_SF_CTL_SNAPSHOT_ALL_THREADS                   (1 << 2)
+#define BRW_SF_CTL_THREAD_SNAPSHOT_ENABLE         (1 << 1)
+#define BRW_SF_CTL_SNAPSHOT_ENABLE                (1 << 0)
+
+#define BRW_SF_STRG_VAL                       0x7b04
+#define BRW_SF_RDATA                  0x7b18
+
+#define BRW_WIZ_CTL                   0x7c00
+#define BRW_WIZ_CTL_SNAPSHOT_COMPLETE             (1 << 31)
+#define BRW_WIZ_CTL_SUBSPAN_INSTANCE_SHIFT        16
+#define BRW_WIZ_CTL_SNAPSHOT_MUX_WIZ_KERNEL_POINTER   (0 << 8)
+#define BRW_WIZ_CTL_SNAPSHOT_MUX_SUBSPAN_INSTANCE     (1 << 8)
+#define BRW_WIZ_CTL_SNAPSHOT_MUX_PRIMITIVE_SEQUENCE   (2 << 8)
+#define BRW_WIZ_CTL_SINGLE_SUBSPAN_DISPATCH          (1 << 6)
+#define BRW_WIZ_CTL_IGNORE_COLOR_SCOREBOARD_STALLS    (1 << 5)
+#define BRW_WIZ_CTL_ENABLE_SUBSPAN_INSTANCE_COMPARE   (1 << 4)
+#define BRW_WIZ_CTL_USE_UPSTREAM_SNAPSHOT_FLAG       (1 << 3)
+#define BRW_WIZ_CTL_SNAPSHOT_ALL_THREADS             (1 << 2)
+#define BRW_WIZ_CTL_THREAD_SNAPSHOT_ENABLE           (1 << 1)
+#define BRW_WIZ_CTL_SNAPSHOT_ENABLE                  (1 << 0)
+
+#define BRW_WIZ_STRG_VAL                             0x7c04
+#define BRW_WIZ_RDATA                                0x7c18
+
+#define BRW_TS_CTL                    0x7e00
+#define BRW_TS_CTL_SNAPSHOT_COMPLETE              (1 << 31)
+#define BRW_TS_CTL_SNAPSHOT_MESSAGE_ERROR         (0 << 8)
+#define BRW_TS_CTL_SNAPSHOT_INTERFACE_DESCRIPTOR   (3 << 8)
+#define BRW_TS_CTL_SNAPSHOT_ALL_CHILD_THREADS     (1 << 2)
+#define BRW_TS_CTL_SNAPSHOT_ALL_ROOT_THREADS      (1 << 1)
+#define BRW_TS_CTL_SNAPSHOT_ENABLE                (1 << 0)
+
+#define BRW_TS_STRG_VAL                       0x7e04
+#define BRW_TS_RDATA                  0x7e08
+
+#define BRW_TD_CTL                    0x8000
+#define BRW_TD_CTL_MUX_SHIFT          8
+#define BRW_TD_CTL_EXTERNAL_HALT_R0_DEBUG_MATCH           (1 << 7)
+#define BRW_TD_CTL_FORCE_EXTERNAL_HALT            (1 << 6)
+#define BRW_TD_CTL_EXCEPTION_MASK_OVERRIDE        (1 << 5)
+#define BRW_TD_CTL_FORCE_THREAD_BREAKPOINT_ENABLE  (1 << 4)
+#define BRW_TD_CTL_BREAKPOINT_ENABLE              (1 << 2)
+#define BRW_TD_CTL2                   0x8004
+#define BRW_TD_CTL2_ILLEGAL_OPCODE_EXCEPTION_OVERRIDE (1 << 28)
+#define BRW_TD_CTL2_MASKSTACK_EXCEPTION_OVERRIDE      (1 << 26)
+#define BRW_TD_CTL2_SOFTWARE_EXCEPTION_OVERRIDE              (1 << 25)
+#define BRW_TD_CTL2_ACTIVE_THREAD_LIMIT_SHIFT        16
+#define BRW_TD_CTL2_ACTIVE_THREAD_LIMIT_ENABLE       (1 << 8)
+#define BRW_TD_CTL2_THREAD_SPAWNER_EXECUTION_MASK_ENABLE (1 << 7)
+#define BRW_TD_CTL2_WIZ_EXECUTION_MASK_ENABLE        (1 << 6)
+#define BRW_TD_CTL2_SF_EXECUTION_MASK_ENABLE         (1 << 5)
+#define BRW_TD_CTL2_CLIPPER_EXECUTION_MASK_ENABLE     (1 << 4)
+#define BRW_TD_CTL2_GS_EXECUTION_MASK_ENABLE         (1 << 3)
+#define BRW_TD_CTL2_VS_EXECUTION_MASK_ENABLE         (1 << 0)
+#define BRW_TD_VF_VS_EMSK             0x8008
+#define BRW_TD_GS_EMSK                0x800c
+#define BRW_TD_CLIP_EMSK              0x8010
+#define BRW_TD_SF_EMSK                0x8014
+#define BRW_TD_WIZ_EMSK                       0x8018
+#define BRW_TD_0_6_EHTRG_VAL          0x801c
+#define BRW_TD_0_7_EHTRG_VAL          0x8020
+#define BRW_TD_0_6_EHTRG_MSK           0x8024
+#define BRW_TD_0_7_EHTRG_MSK          0x8028
+#define BRW_TD_RDATA                  0x802c
+#define BRW_TD_TS_EMSK                0x8030
+
+#define BRW_EU_CTL                    0x8800
+#define BRW_EU_CTL_SELECT_SHIFT               16
+#define BRW_EU_CTL_DATA_MUX_SHIFT      8
+#define BRW_EU_ATT_0                  0x8810
+#define BRW_EU_ATT_1                  0x8814
+#define BRW_EU_ATT_DATA_0             0x8820
+#define BRW_EU_ATT_DATA_1             0x8824
+#define BRW_EU_ATT_CLR_0              0x8830
+#define BRW_EU_ATT_CLR_1              0x8834
+#define BRW_EU_RDATA                  0x8840
+
+typedef struct i965_device i965_device_t;
+typedef struct i965_surface i965_surface_t;
+typedef struct i965_shader i965_shader_t;
+typedef struct i965_stream i965_stream_t;
+
+struct i965_sf_state {
+    cairo_hash_entry_t entry;
+    uint32_t offset;
+};
+
+cairo_private cairo_bool_t
+i965_sf_state_equal (const void *, const void *);
+
+struct i965_cc_state {
+    cairo_hash_entry_t entry;
+    uint32_t offset;
+};
+
+cairo_private cairo_bool_t
+i965_cc_state_equal (const void *, const void *);
+
+struct i965_wm_kernel {
+    cairo_hash_entry_t entry;
+    uint32_t offset;
+};
+
+struct i965_wm_state {
+    cairo_hash_entry_t entry;
+    uint32_t kernel;
+    uint32_t sampler;
+    uint32_t offset;
+};
+
+cairo_private cairo_bool_t
+i965_wm_state_equal (const void *, const void *);
+
+struct i965_wm_binding {
+    cairo_hash_entry_t entry;
+    uint32_t table[4];
+    int size;
+    uint32_t offset;
+};
+
+cairo_private cairo_bool_t
+i965_wm_binding_equal (const void *, const void *);
+
+struct i965_sampler {
+    cairo_hash_entry_t entry;
+    uint32_t offset;
+};
+
+struct i965_vbo {
+    struct i965_vbo *next;
+    intel_bo_t *bo;
+    unsigned int count;
+};
+
+struct i965_surface {
+    intel_surface_t intel;
+
+    uint32_t stream;
+    uint32_t offset;
+};
+
+struct i965_pending_relocation {
+    uint32_t offset;
+    uint32_t read_domains;
+    uint32_t write_domain;
+    uint32_t delta;
+};
+
+struct i965_stream {
+    uint32_t used;
+    uint32_t committed;
+    uint32_t size;
+    uint8_t *data;
+    uint32_t serial;
+
+    int num_pending_relocations;
+    int max_pending_relocations;
+    struct i965_pending_relocation *pending_relocations;
+
+    int num_relocations;
+    int max_relocations;
+    struct drm_i915_gem_relocation_entry *relocations;
+};
+
+#define I965_BATCH_SIZE (16 * 4096)
+#define I965_GENERAL_SIZE (16 * 4096)
+#define I965_SURFACE_SIZE (32 * 4096)
+#define I965_VERTEX_SIZE (128 * 4096)
+
+#define I965_TILING_DEFAULT I915_TILING_Y
+
+
+struct i965_device {
+    intel_device_t intel;
+
+    cairo_bool_t is_g4x;
+
+    i965_shader_t *shader; /* note: only valid during geometry emission */
+
+    /* track state changes */
+    struct i965_sf_state sf_state;
+    struct i965_cc_state cc_state;
+    struct i965_wm_state wm_state;
+    struct i965_wm_binding wm_binding;
+
+    i965_surface_t *target;
+    uint32_t target_offset;
+
+    intel_bo_t *source;
+    uint32_t source_offset;
+
+    intel_bo_t *mask;
+    uint32_t mask_offset;
+
+    intel_bo_t *clip;
+    uint32_t clip_offset;
+
+    uint32_t draw_rectangle;
+
+    uint32_t vs_offset;
+    uint32_t border_color_offset;
+    cairo_hash_table_t *sf_states;
+    cairo_hash_table_t *cc_states;
+    cairo_hash_table_t *wm_kernels;
+    cairo_hash_table_t *wm_states;
+    cairo_hash_table_t *wm_bindings;
+    cairo_hash_table_t *samplers;
+    intel_bo_t *general_state;
+
+    cairo_freelist_t sf_freelist;
+    cairo_freelist_t cc_freelist;
+    cairo_freelist_t wm_kernel_freelist;
+    cairo_freelist_t wm_state_freelist;
+    cairo_freelist_t wm_binding_freelist;
+    cairo_freelist_t sampler_freelist;
+
+    uint32_t vertex_type;
+    uint32_t vertex_size;
+    uint32_t rectangle_size;
+    uint32_t last_vertex_size;
+
+    float *constants; /* 4 x matrix + 2 x source */
+    unsigned constants_size;
+    cairo_bool_t have_urb_fences;
+
+    i965_stream_t batch;
+    uint8_t batch_base[I965_BATCH_SIZE];
+    struct drm_i915_gem_relocation_entry batch_relocations[2048];
+
+    i965_stream_t surface;
+    uint8_t surface_base[I965_SURFACE_SIZE];
+    struct i965_pending_relocation surface_pending_relocations[1];
+    struct drm_i915_gem_relocation_entry surface_relocations[1024];
+
+    i965_stream_t general;
+    uint8_t general_base[I965_GENERAL_SIZE];
+    struct i965_pending_relocation general_pending_relocations[1];
+
+    i965_stream_t vertex;
+    uint8_t vertex_base[I965_VERTEX_SIZE];
+    struct i965_pending_relocation vertex_pending_relocations[512];
+
+    struct {
+       size_t gtt_size;
+
+       intel_bo_t *bo[1024];
+       int count;
+
+       struct drm_i915_gem_exec_object2 exec[1024];
+    } exec;
+    cairo_list_t flush;
+};
+
+typedef enum {
+    VS_NONE = 0,
+    VS_GLYPHS,
+    VS_SPANS,
+} i965_vertex_shader_t;
+
+typedef enum {
+    FS_NONE = 0,
+    FS_CONSTANT,
+    FS_LINEAR,
+    FS_RADIAL,
+    FS_SURFACE,
+    FS_GLYPHS,
+    FS_SPANS,
+} i965_fragment_shader_t;
+
+typedef enum {
+    PATTERN_BASE,
+    PATTERN_SOLID,
+    PATTERN_LINEAR,
+    PATTERN_RADIAL,
+    PATTERN_SURFACE,
+} i965_shader_channel_t;
+#define PATTERN_NONE (i965_shader_channel_t)-1
+
+struct i965_shader {
+    i965_device_t *device;
+    i965_surface_t *target;
+
+    cairo_operator_t op;
+
+    cairo_bool_t committed;
+    cairo_bool_t need_combine;
+
+    float constants[4*8 + 2*8]; /* 4 x matrix + 2 x source */
+    unsigned constants_size;
+
+    union i965_shader_channel {
+       struct {
+           i965_vertex_shader_t vertex;
+           i965_fragment_shader_t fragment;
+           i965_shader_channel_t pattern;
+       } type;
+       struct i965_shader_base {
+           i965_vertex_shader_t vertex;
+           i965_fragment_shader_t fragment;
+           i965_shader_channel_t pattern;
+
+           uint32_t mode;
+
+           float constants[8];
+           unsigned constants_size;
+
+           intel_bo_t *bo;
+           cairo_format_t format;
+           cairo_content_t content;
+           int width, height, stride;
+           int filter, extend;
+           cairo_matrix_t matrix;
+           cairo_bool_t has_component_alpha;
+       } base;
+       struct i965_shader_solid {
+           struct i965_shader_base base;
+       } solid;
+       struct i965_shader_linear {
+           struct i965_shader_base base;
+       } linear;
+       struct i965_shader_radial {
+           struct i965_shader_base base;
+       } radial;
+       struct i965_shader_surface {
+           struct i965_shader_base base;
+           cairo_surface_t *surface;
+       } surface;
+    } source, mask, clip, dst;
+
+    jmp_buf unwind;
+};
+
+enum i965_shader_linear_mode {
+    /* XXX REFLECT */
+    LINEAR_TEXTURE,
+    LINEAR_NONE,
+    LINEAR_REPEAT,
+    LINEAR_PAD,
+};
+
+enum i965_shader_radial_mode {
+    RADIAL_ONE,
+    RADIAL_TWO
+};
+
+typedef cairo_status_t
+(*i965_spans_func_t) (void                     *closure,
+                     cairo_span_renderer_t     *renderer,
+                     const cairo_rectangle_int_t       *extents);
+
+static inline i965_device_t *
+i965_device (i965_surface_t *surface)
+{
+    return (i965_device_t *) surface->intel.drm.base.device;
+}
+
+cairo_private void
+i965_emit_relocation (i965_device_t *device,
+                     i965_stream_t *stream,
+                     intel_bo_t *target,
+                     uint32_t target_offset,
+                     uint32_t read_domains,
+                     uint32_t write_domain,
+                     uint32_t offset);
+
+static cairo_always_inline uint32_t
+i965_stream_emit (i965_stream_t *stream, const void *data, size_t size)
+{
+    uint32_t offset;
+
+    offset = stream->used;
+    assert (offset + size <= stream->size);
+    memcpy (stream->data + offset, data, size);
+    stream->used += size;
+
+    return offset;
+}
+
+static cairo_always_inline void
+i965_stream_align (i965_stream_t *stream, uint32_t size)
+{
+    stream->used = (stream->used + size - 1) & -size;
+}
+
+static cairo_always_inline void *
+i965_stream_alloc (i965_stream_t *stream, uint32_t align, uint32_t size)
+{
+    void *ptr;
+
+    if (align)
+       i965_stream_align (stream, align);
+
+    assert (stream->used + size <= stream->size);
+    ptr = stream->data + stream->used;
+    stream->used += size;
+
+    return ptr;
+}
+
+static cairo_always_inline uint32_t
+i965_stream_offsetof (i965_stream_t *stream, const void *ptr)
+{
+    return (char *) ptr - (char *) stream->data;
+}
+
+cairo_private void
+i965_stream_commit (i965_device_t *device,
+                   i965_stream_t *stream);
+
+cairo_private void
+i965_general_state_reset (i965_device_t *device);
+
+static inline void
+i965_batch_emit_dword (i965_device_t *device, uint32_t dword)
+{
+    *(uint32_t *) (device->batch.data + device->batch.used) = dword;
+    device->batch.used += 4;
+}
+
+#define OUT_BATCH(dword) i965_batch_emit_dword(device, dword)
+
+cairo_private void
+i965_clipped_vertices (i965_device_t *device,
+                      struct i965_vbo *vbo,
+                      cairo_region_t *clip_region);
+
+cairo_private void
+i965_flush_vertices (i965_device_t *device);
+
+cairo_private void
+i965_finish_vertices (i965_device_t *device);
+
+static inline float *
+i965_add_rectangle (i965_device_t *device)
+{
+    float *vertices;
+    uint32_t size;
+
+    size = device->rectangle_size;
+    if (unlikely (device->vertex.used + size > device->vertex.size))
+       i965_finish_vertices (device);
+
+    vertices = (float *) (device->vertex.data + device->vertex.used);
+    device->vertex.used += size;
+
+    return vertices;
+}
+
+static inline void
+i965_shader_add_rectangle (const i965_shader_t *shader,
+                          int x, int y,
+                          int w, int h)
+{
+    float *v;
+
+    v= i965_add_rectangle (shader->device);
+
+    /* bottom-right */
+    *v++ = x + w;
+    *v++ = y + h;
+
+    /* bottom-left */
+    *v++ = x;
+    *v++ = y + h;
+
+    /* top-left */
+    *v++ = x;
+    *v++ = y;
+}
+
+cairo_private cairo_surface_t *
+i965_surface_create_internal (cairo_drm_device_t *base_dev,
+                             cairo_format_t format,
+                             int width, int height,
+                             uint32_t tiling,
+                             cairo_bool_t gpu_target);
+
+cairo_private cairo_status_t
+i965_clip_and_composite_spans (i965_surface_t          *dst,
+                              cairo_operator_t          op,
+                              const cairo_pattern_t    *pattern,
+                              cairo_antialias_t         antialias,
+                              i965_spans_func_t         draw_func,
+                              void                     *draw_closure,
+                              const cairo_composite_rectangles_t*extents,
+                              cairo_clip_t             *clip);
+
+cairo_private cairo_int_status_t
+i965_surface_glyphs (void                      *abstract_surface,
+                    cairo_operator_t            op,
+                    const cairo_pattern_t      *source,
+                    cairo_glyph_t              *glyphs,
+                    int                         num_glyphs,
+                    cairo_scaled_font_t        *scaled_font,
+                    cairo_clip_t               *clip,
+                    int *num_remaining);
+
+cairo_private void
+i965_shader_init (i965_shader_t *shader,
+                 i965_surface_t *dst,
+                 cairo_operator_t op);
+
+cairo_private cairo_status_t
+i965_shader_acquire_pattern (i965_shader_t *shader,
+                            union i965_shader_channel *src,
+                            const cairo_pattern_t *pattern,
+                            const cairo_rectangle_int_t *extents);
+
+cairo_private void
+i965_shader_set_clip (i965_shader_t *shader,
+                     cairo_clip_t *clip);
+
+cairo_private cairo_status_t
+i965_shader_commit (i965_shader_t *shader,
+                   i965_device_t *device);
+
+cairo_private void
+i965_shader_fini (i965_shader_t *shader);
+
+cairo_private cairo_status_t
+i965_device_flush (i965_device_t *device);
+
+cairo_private cairo_status_t
+i965_fixup_unbounded (i965_surface_t *dst,
+                     const cairo_composite_rectangles_t *extents,
+                     cairo_clip_t *clip);
+
+static inline int
+i965_filter (cairo_filter_t filter)
+{
+    switch (filter) {
+    default:
+    case CAIRO_FILTER_FAST:
+    case CAIRO_FILTER_NEAREST:
+       return BRW_MAPFILTER_NEAREST;
+
+    case CAIRO_FILTER_GOOD:
+    case CAIRO_FILTER_BEST:
+    case CAIRO_FILTER_BILINEAR:
+    case CAIRO_FILTER_GAUSSIAN:
+       return BRW_MAPFILTER_LINEAR;
+    }
+}
+
+static inline int
+i965_extend (cairo_extend_t extend)
+{
+    switch (extend) {
+    default:
+    case CAIRO_EXTEND_NONE:
+       return BRW_TEXCOORDMODE_CLAMP_BORDER;
+    case CAIRO_EXTEND_REPEAT:
+       return BRW_TEXCOORDMODE_WRAP;
+    case CAIRO_EXTEND_PAD:
+       return BRW_TEXCOORDMODE_CLAMP;
+    case CAIRO_EXTEND_REFLECT:
+       return BRW_TEXCOORDMODE_MIRROR;
+    }
+}
+
+#endif /* CAIRO_DRM_I965_PRIVATE_H */
diff --git a/src/drm/cairo-drm-i965-shader.c b/src/drm/cairo-drm-i965-shader.c
new file mode 100644 (file)
index 0000000..f88e5a4
--- /dev/null
@@ -0,0 +1,2825 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Kristian Høgsberg
+ * Copyright © 2009 Chris Wilson
+ * Copyright © 2009 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * Contributor(s):
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ *      Kristian Høgsberg <krh@bitplanet.net>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-error-private.h"
+#include "cairo-drm-i965-private.h"
+#include "cairo-surface-subsurface-private.h"
+#include "cairo-surface-snapshot-private.h"
+
+#include "cairo-drm-intel-brw-eu.h"
+
+/* Theory of shaders:
+ *
+ * 3 types of rectangular inputs:
+ *  (a) standard composite: x,y, use source, mask matrices to compute texcoords
+ *  (b) spans: x,y, alpha, use source matrix
+ *  (c) glyphs: x,y, s,t, use source matrix
+ *
+ * 5 types of pixel shaders:
+ *  (a) Solid colour
+ *  (b) Linear gradient (via 1D texture, with precomputed tex)
+ *  (c) Radial gradient (per-pixel s computation, 1D texture)
+ *  (d) Spans (mask only): apply opacity
+ *  (e) Texture (includes glyphs).
+ *
+ *  Clip masks are limited to 2D textures only.
+ */
+
+/* XXX dual source blending for LERP + ComponentAlpha!!! */
+
+#define BRW_GRF_BLOCKS(nreg)    ((nreg + 15) / 16 - 1)
+
+#define SF_KERNEL_NUM_GRF  1
+#define SF_MAX_THREADS    24
+
+#define PS_MAX_THREADS_CTG 50
+#define PS_MAX_THREADS_BRW 32
+
+#define URB_CS_ENTRY_SIZE     3 /* We need 4 matrices + 2 sources */
+#define URB_CS_ENTRIES       4 /* 4x sets of CONSTANT_BUFFER */
+
+#define URB_VS_ENTRY_SIZE     1
+#define URB_VS_ENTRIES       8
+
+#define URB_GS_ENTRY_SIZE     0
+#define URB_GS_ENTRIES       0
+
+#define URB_CLIP_ENTRY_SIZE   0
+#define URB_CLIP_ENTRIES      0
+
+#define URB_SF_ENTRY_SIZE     1
+#define URB_SF_ENTRIES       (SF_MAX_THREADS + 1)
+
+static void
+i965_pipelined_flush (i965_device_t *device)
+{
+    intel_bo_t *bo, *next;
+
+    if (device->batch.used == 0)
+       return;
+
+    OUT_BATCH (BRW_PIPE_CONTROL |
+              BRW_PIPE_CONTROL_NOWRITE |
+              BRW_PIPE_CONTROL_WC_FLUSH |
+              2);
+    OUT_BATCH(0);   /* Destination address */ 
+    OUT_BATCH(0);   /* Immediate data low DW */ 
+    OUT_BATCH(0);   /* Immediate data high DW */ 
+
+    cairo_list_foreach_entry_safe (bo, next, intel_bo_t, &device->flush, link) {
+       bo->batch_write_domain = 0;
+       cairo_list_init (&bo->link);
+    }
+    cairo_list_init (&device->flush);
+}
+
+static cairo_status_t
+i965_shader_acquire_solid (i965_shader_t *shader,
+                          union i965_shader_channel *src,
+                          const cairo_solid_pattern_t *solid,
+                          const cairo_rectangle_int_t *extents)
+{
+    src->type.fragment = FS_CONSTANT;
+    src->type.vertex = VS_NONE;
+    src->type.pattern = PATTERN_SOLID;
+
+    src->base.content = _cairo_color_get_content (&solid->color);
+    src->base.constants[0] = solid->color.red   * solid->color.alpha;
+    src->base.constants[1] = solid->color.green * solid->color.alpha;
+    src->base.constants[2] = solid->color.blue  * solid->color.alpha;
+    src->base.constants[3] = solid->color.alpha;
+    src->base.constants_size = 4;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+i965_shader_acquire_linear (i965_shader_t *shader,
+                           union i965_shader_channel *src,
+                           const cairo_linear_pattern_t *linear,
+                           const cairo_rectangle_int_t *extents)
+{
+    intel_buffer_t buffer;
+    cairo_status_t status;
+    double x0, y0, sf;
+    double dx, dy, offset;
+
+    status = intel_gradient_render (&i965_device (shader->target)->intel,
+                                   &linear->base, &buffer);
+    if (unlikely (status))
+       return status;
+
+    src->type.vertex = VS_NONE;
+    src->type.pattern = PATTERN_LINEAR;
+    src->type.fragment = FS_LINEAR;
+    src->base.bo = buffer.bo;
+    src->base.content = CAIRO_CONTENT_COLOR_ALPHA;
+    src->base.format = buffer.format;
+    src->base.width  = buffer.width;
+    src->base.height = buffer.height;
+    src->base.stride = buffer.stride;
+    src->base.filter = i965_filter (CAIRO_FILTER_BILINEAR);
+    src->base.extend = i965_extend (linear->base.base.extend);
+
+    dx = linear->pd2.x - linear->pd1.x;
+    dy = linear->pd2.y - linear->pd1.y;
+    sf = 1. / (dx * dx + dy * dy);
+    dx *= sf;
+    dy *= sf;
+
+    x0 = linear->pd1.x;
+    y0 = linear->pd1.y;
+    offset = dx*x0 + dy*y0;
+
+    if (_cairo_matrix_is_identity (&linear->base.base.matrix)) {
+       src->base.matrix.xx = dx;
+       src->base.matrix.xy = dy;
+       src->base.matrix.x0 = -offset;
+    } else {
+       cairo_matrix_t m;
+
+       cairo_matrix_init (&m, dx, 0, dy, 0, -offset, 0);
+       cairo_matrix_multiply (&src->base.matrix, &linear->base.base.matrix, &m);
+    }
+    src->base.matrix.yx = 0.;
+    src->base.matrix.yy = 1.;
+    src->base.matrix.y0 = 0.;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+i965_shader_acquire_radial (i965_shader_t *shader,
+                           union i965_shader_channel *src,
+                           const cairo_radial_pattern_t *radial,
+                           const cairo_rectangle_int_t *extents)
+{
+    intel_buffer_t buffer;
+    cairo_status_t status;
+    double dx, dy, dr, r1;
+
+    status = intel_gradient_render (&i965_device (shader->target)->intel,
+                                   &radial->base, &buffer);
+    if (unlikely (status))
+       return status;
+
+    src->type.vertex = VS_NONE;
+    src->type.pattern = PATTERN_RADIAL;
+    src->type.fragment = FS_RADIAL;
+    src->base.bo = buffer.bo;
+    src->base.content = CAIRO_CONTENT_COLOR_ALPHA;
+    src->base.format = buffer.format;
+    src->base.width  = buffer.width;
+    src->base.height = buffer.height;
+    src->base.stride = buffer.stride;
+    src->base.filter = i965_filter (CAIRO_FILTER_BILINEAR);
+    src->base.extend = i965_extend (radial->base.base.extend);
+
+    dx = radial->cd2.center.x - radial->cd1.center.x;
+    dy = radial->cd2.center.y - radial->cd1.center.y;
+    dr = radial->cd2.radius   - radial->cd1.radius;
+
+    r1 = radial->cd1.radius;
+
+    if (FALSE && (radial->cd2.center.x == radial->cd1.center.x &&
+                 radial->cd2.center.y == radial->cd1.center.y))
+    {
+       /* XXX dr == 0, meaningless with anything other than PAD */
+       src->base.constants[0] = radial->cd1.center.x / dr;
+       src->base.constants[1] = radial->cd1.center.y / dr;
+       src->base.constants[2] = 1. / dr;
+       src->base.constants[3] = -r1 / dr;
+
+       src->base.constants_size = 4;
+       src->base.mode = RADIAL_ONE;
+    } else {
+       src->base.constants[0] = -radial->cd1.center.x;
+       src->base.constants[1] = -radial->cd1.center.y;
+       src->base.constants[2] = r1;
+       src->base.constants[3] = -4 * (dx*dx + dy*dy - dr*dr);
+
+       src->base.constants[4] = -2 * dx;
+       src->base.constants[5] = -2 * dy;
+       src->base.constants[6] = -2 * r1 * dr;
+       src->base.constants[7] = 1 / (2 * (dx*dx + dy*dy - dr*dr));
+
+       src->base.constants_size = 8;
+       src->base.mode = RADIAL_TWO;
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+i965_surface_clone (i965_device_t *device,
+                   cairo_image_surface_t *image,
+                   i965_surface_t **clone_out)
+{
+    i965_surface_t *clone;
+    cairo_status_t status;
+
+    clone = (i965_surface_t *)
+       i965_surface_create_internal (&device->intel.base,
+                                     image->base.content,
+                                     image->width,
+                                     image->height,
+                                     I965_TILING_DEFAULT,
+                                     FALSE);
+    if (unlikely (clone->intel.drm.base.status))
+       return clone->intel.drm.base.status;
+
+    status = intel_bo_put_image (&device->intel,
+                                to_intel_bo (clone->intel.drm.bo),
+                                image,
+                                0, 0,
+                                image->width, image->height,
+                                0, 0);
+
+    if (unlikely (status)) {
+       cairo_surface_destroy (&clone->intel.drm.base);
+       return status;
+    }
+
+    status = intel_snapshot_cache_insert (&device->intel, &clone->intel);
+    if (unlikely (status)) {
+       cairo_surface_destroy (&clone->intel.drm.base);
+       return status;
+    }
+
+    _cairo_surface_attach_snapshot (&image->base,
+                                   &clone->intel.drm.base,
+                                   intel_surface_detach_snapshot);
+
+    *clone_out = clone;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+i965_surface_clone_subimage (i965_device_t *device,
+                            cairo_image_surface_t *image,
+                            const cairo_rectangle_int_t *extents,
+                            i965_surface_t **clone_out)
+{
+    i965_surface_t *clone;
+    cairo_status_t status;
+
+    clone = (i965_surface_t *)
+       i965_surface_create_internal (&device->intel.base,
+                                     image->base.content,
+                                     extents->width,
+                                     extents->height,
+                                     I965_TILING_DEFAULT,
+                                     FALSE);
+    if (unlikely (clone->intel.drm.base.status))
+       return clone->intel.drm.base.status;
+
+    status = intel_bo_put_image (to_intel_device (clone->intel.drm.base.device),
+                                to_intel_bo (clone->intel.drm.bo),
+                                image,
+                                extents->x, extents->y,
+                                extents->width, extents->height,
+                                0, 0);
+    if (unlikely (status))
+       return status;
+
+    *clone_out = clone;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+i965_shader_acquire_solid_surface (i965_shader_t *shader,
+                                  union i965_shader_channel *src,
+                                  cairo_surface_t *surface,
+                                  const cairo_rectangle_int_t *extents)
+{
+    cairo_image_surface_t *image;
+    void *image_extra;
+    cairo_status_t status;
+    uint32_t argb;
+
+    status = _cairo_surface_acquire_source_image (surface, &image, &image_extra);
+    if (unlikely (status))
+       return status;
+
+    if (image->format != CAIRO_FORMAT_ARGB32) {
+       cairo_surface_t *pixel;
+       cairo_surface_pattern_t pattern;
+
+       /* extract the pixel as argb32 */
+       pixel = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 1, 1);
+       _cairo_pattern_init_for_surface (&pattern, &image->base);
+       cairo_matrix_init_translate (&pattern.base.matrix, extents->x, extents->y);
+       pattern.base.filter = CAIRO_FILTER_NEAREST;
+       status = _cairo_surface_paint (pixel, CAIRO_OPERATOR_SOURCE, &pattern.base, NULL);
+       _cairo_pattern_fini (&pattern.base);
+
+       if (unlikely (status)) {
+           _cairo_surface_release_source_image (surface, image, image_extra);
+           cairo_surface_destroy (pixel);
+           return status;
+       }
+
+       argb = *(uint32_t *) ((cairo_image_surface_t *) pixel)->data;
+       cairo_surface_destroy (pixel);
+    } else {
+       argb = ((uint32_t *) (image->data + extents->y * image->stride))[extents->x];
+    }
+
+    _cairo_surface_release_source_image (surface, image, image_extra);
+
+    if (argb >> 24 == 0)
+       argb = 0;
+
+    src->base.constants[0] = ((argb >> 16) & 0xff) / 255.;
+    src->base.constants[1] = ((argb >>  8) & 0xff) / 255.;
+    src->base.constants[2] = ((argb >>  0) & 0xff) / 255.;
+    src->base.constants[3] = ((argb >> 24) & 0xff) / 255.;
+    src->base.constants_size = 4;
+
+    src->base.content  = CAIRO_CONTENT_COLOR_ALPHA;
+    if (CAIRO_ALPHA_IS_OPAQUE(src->base.constants[3]))
+       src->base.content &= ~CAIRO_CONTENT_ALPHA;
+    src->type.fragment = FS_CONSTANT;
+    src->type.vertex   = VS_NONE;
+    src->type.pattern  = PATTERN_SOLID;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+i965_shader_acquire_surface (i965_shader_t *shader,
+                            union i965_shader_channel *src,
+                            const cairo_surface_pattern_t *pattern,
+                            const cairo_rectangle_int_t *extents)
+{
+    cairo_surface_t *surface, *drm;
+    cairo_matrix_t m;
+    cairo_status_t status;
+    int src_x = 0, src_y = 0;
+
+    assert (src->type.fragment == FS_NONE);
+    drm = surface = pattern->surface;
+
+    if (surface->type == CAIRO_SURFACE_TYPE_DRM) {
+       if (surface->backend->type == CAIRO_SURFACE_TYPE_SUBSURFACE) {
+           drm = ((cairo_surface_subsurface_t *) surface)->target;
+       } else if (surface->backend->type == CAIRO_INTERNAL_SURFACE_TYPE_SNAPSHOT) {
+           drm = ((cairo_surface_snapshot_t *) surface)->target;
+       }
+    }
+
+    src->type.pattern = PATTERN_SURFACE;
+    src->surface.surface = NULL;
+    if (drm->type == CAIRO_SURFACE_TYPE_DRM) {
+       i965_surface_t *s = (i965_surface_t *) drm;
+
+       if (surface->backend->type == CAIRO_SURFACE_TYPE_SUBSURFACE) {
+           if (s->intel.drm.base.device == shader->target->intel.drm.base.device) {
+               cairo_surface_subsurface_t *sub = (cairo_surface_subsurface_t *) surface;
+               if (s != shader->target) {
+                   int x;
+
+                   if (s->intel.drm.fallback != NULL) {
+                       status = intel_surface_flush (s);
+                       if (unlikely (status))
+                           return status;
+                   }
+
+                   if (to_intel_bo (s->intel.drm.bo)->batch_write_domain)
+                       i965_pipelined_flush (i965_device (s));
+
+                   src->type.fragment = FS_SURFACE;
+
+                   src->base.bo = to_intel_bo (s->intel.drm.bo);
+                   src->base.format = s->intel.drm.format;
+                   src->base.content = s->intel.drm.base.content;
+                   src->base.width = sub->extents.width;
+                   src->base.height = sub->extents.height;
+                   src->base.stride = s->intel.drm.stride;
+
+                   x = sub->extents.x;
+                   if (s->intel.drm.format != CAIRO_FORMAT_A8)
+                       x *= 4;
+
+                   /* XXX tiling restrictions upon offset? */
+                   //src->base.offset[0] = s->offset + sub->extents.y * s->intel.drm.stride + x;
+               } else {
+                   i965_surface_t *clone;
+                   cairo_surface_pattern_t pattern;
+
+                   clone = (i965_surface_t *)
+                       i965_surface_create_internal ((cairo_drm_device_t *) s->intel.drm.base.device,
+                                                     s->intel.drm.base.content,
+                                                     sub->extents.width,
+                                                     sub->extents.height,
+                                                     I965_TILING_DEFAULT,
+                                                     TRUE);
+                   if (unlikely (clone->intel.drm.base.status))
+                       return clone->intel.drm.base.status;
+
+                   _cairo_pattern_init_for_surface (&pattern, &s->intel.drm.base);
+                   pattern.base.filter = CAIRO_FILTER_NEAREST;
+                   cairo_matrix_init_translate (&pattern.base.matrix,
+                                                sub->extents.x, sub->extents.y);
+
+                   status = _cairo_surface_paint (&clone->intel.drm.base,
+                                                  CAIRO_OPERATOR_SOURCE,
+                                                  &pattern.base,
+                                                  NULL);
+
+                   _cairo_pattern_fini (&pattern.base);
+
+                   if (unlikely (status)) {
+                       cairo_surface_destroy (&clone->intel.drm.base);
+                       return status;
+                   }
+
+                   i965_pipelined_flush (i965_device (s));
+                   src->type.fragment = FS_SURFACE;
+
+                   src->base.bo = to_intel_bo (clone->intel.drm.bo);
+                   src->base.format = clone->intel.drm.format;
+                   src->base.content = clone->intel.drm.base.content;
+                   src->base.width = clone->intel.drm.width;
+                   src->base.height = clone->intel.drm.height;
+                   src->base.stride = clone->intel.drm.stride;
+
+                   src->surface.surface = &clone->intel.drm.base;
+               }
+
+               src_x = sub->extents.x;
+               src_y = sub->extents.y;
+           }
+       } else {
+           if (s->intel.drm.base.device == shader->target->intel.drm.base.device) {
+               if (s != shader->target) {
+                   if (s->intel.drm.fallback != NULL) {
+                       status = intel_surface_flush (s);
+                       if (unlikely (status))
+                           return status;
+                   }
+
+                   if (to_intel_bo (s->intel.drm.bo)->batch_write_domain)
+                       i965_pipelined_flush (i965_device (s));
+
+                   src->type.fragment = FS_SURFACE;
+
+                   src->base.bo = to_intel_bo (s->intel.drm.bo);
+                   src->base.format = s->intel.drm.format;
+                   src->base.content = s->intel.drm.base.content;
+                   src->base.width = s->intel.drm.width;
+                   src->base.height = s->intel.drm.height;
+                   src->base.stride = s->intel.drm.stride;
+               } else {
+                   i965_surface_t *clone;
+                   cairo_surface_pattern_t pattern;
+
+                   clone = (i965_surface_t *)
+                       i965_surface_create_internal ((cairo_drm_device_t *) s->intel.drm.base.device,
+                                                     s->intel.drm.base.content,
+                                                     s->intel.drm.width,
+                                                     s->intel.drm.height,
+                                                     I965_TILING_DEFAULT,
+                                                     TRUE);
+                   if (unlikely (clone->intel.drm.base.status))
+                       return clone->intel.drm.base.status;
+
+                   _cairo_pattern_init_for_surface (&pattern, &s->intel.drm.base);
+                   pattern.base.filter = CAIRO_FILTER_NEAREST;
+                   status = _cairo_surface_paint (&clone->intel.drm.base,
+                                                  CAIRO_OPERATOR_SOURCE,
+                                                  &pattern.base,
+                                                  NULL);
+
+                   _cairo_pattern_fini (&pattern.base);
+
+                   if (unlikely (status)) {
+                       cairo_surface_destroy (&clone->intel.drm.base);
+                       return status;
+                   }
+
+                   i965_pipelined_flush (i965_device (s));
+                   src->type.fragment = FS_SURFACE;
+
+                   src->base.bo = to_intel_bo (clone->intel.drm.bo);
+                   src->base.format = clone->intel.drm.format;
+                   src->base.content = clone->intel.drm.base.content;
+                   src->base.width = clone->intel.drm.width;
+                   src->base.height = clone->intel.drm.height;
+                   src->base.stride = clone->intel.drm.stride;
+
+                   src->surface.surface = &clone->intel.drm.base;
+               }
+           }
+       }
+    }
+
+    if (src->type.fragment == FS_NONE) {
+       i965_surface_t *s;
+
+       if (extents->width == 1 && extents->height == 1) {
+           return i965_shader_acquire_solid_surface (shader, src,
+                                                     surface, extents);
+       }
+
+       s = (i965_surface_t *)
+           _cairo_surface_has_snapshot (surface,
+                                        shader->target->intel.drm.base.backend);
+       if (s != NULL) {
+           i965_device_t *device = i965_device (shader->target);
+           intel_bo_t *bo = to_intel_bo (s->intel.drm.bo);
+
+           if (bo->purgeable &&
+               ! intel_bo_madvise (&device->intel, bo, I915_MADV_WILLNEED))
+           {
+               _cairo_surface_detach_snapshot (&s->intel.drm.base);
+               s = NULL;
+           }
+
+           if (s != NULL)
+               cairo_surface_reference (&s->intel.drm.base);
+       }
+
+       if (s == NULL) {
+           cairo_image_surface_t *image;
+           void *image_extra;
+           cairo_status_t status;
+
+           status = _cairo_surface_acquire_source_image (surface, &image, &image_extra);
+           if (unlikely (status))
+               return status;
+
+           if (image->width < 8192 && image->height < 8192) {
+               status = i965_surface_clone (i965_device (shader->target), image, &s);
+           } else {
+               status = i965_surface_clone_subimage (i965_device (shader->target),
+                                                     image, extents, &s);
+               src_x = -extents->x;
+               src_y = -extents->y;
+           }
+
+           _cairo_surface_release_source_image (surface, image, image_extra);
+
+           if (unlikely (status))
+               return status;
+
+           /* XXX? */
+           //intel_bo_mark_purgeable (to_intel_bo (s->intel.drm.bo), TRUE);
+       }
+
+       src->type.fragment = FS_SURFACE;
+
+       src->base.bo = to_intel_bo (s->intel.drm.bo);
+       src->base.content = s->intel.drm.base.content;
+       src->base.format = s->intel.drm.format;
+       src->base.width  = s->intel.drm.width;
+       src->base.height = s->intel.drm.height;
+       src->base.stride = s->intel.drm.stride;
+
+       src->surface.surface = &s->intel.drm.base;
+
+       drm = &s->intel.drm.base;
+    }
+
+    /* XXX transform nx1 or 1xn surfaces to 1D? */
+
+    src->type.vertex = VS_NONE;
+
+    src->base.extend = i965_extend (pattern->base.extend);
+    if (pattern->base.extend == CAIRO_EXTEND_NONE &&
+       extents->x >= 0 && extents->y >= 0 &&
+       extents->x + extents->width  <= src->base.width &&
+       extents->y + extents->height <= src->base.height)
+    {
+       /* Convert a wholly contained NONE to a REFLECT as the contiguous sampler
+        * cannot not handle CLAMP_BORDER textures.
+        */
+       src->base.extend = i965_extend (CAIRO_EXTEND_REFLECT);
+       /* XXX also need to check |u,v| < 3 */
+    }
+
+    src->base.filter = i965_filter (pattern->base.filter);
+    if (_cairo_matrix_is_pixel_exact (&pattern->base.matrix))
+       src->base.filter = i965_filter (CAIRO_FILTER_NEAREST);
+
+    /* tweak the src matrix to map from dst to texture coordinates */
+    src->base.matrix = pattern->base.matrix;
+    if (src_x | src_y)
+       cairo_matrix_translate (&src->base.matrix, src_x, src_x);
+    cairo_matrix_init_scale (&m, 1. / src->base.width, 1. / src->base.height);
+    cairo_matrix_multiply (&src->base.matrix, &src->base.matrix, &m);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_status_t
+i965_shader_acquire_pattern (i965_shader_t *shader,
+                            union i965_shader_channel *src,
+                            const cairo_pattern_t *pattern,
+                            const cairo_rectangle_int_t *extents)
+{
+    switch (pattern->type) {
+    case CAIRO_PATTERN_TYPE_SOLID:
+       return i965_shader_acquire_solid (shader, src,
+                                         (cairo_solid_pattern_t *) pattern,
+                                         extents);
+
+    case CAIRO_PATTERN_TYPE_LINEAR:
+       return i965_shader_acquire_linear (shader, src,
+                                          (cairo_linear_pattern_t *) pattern,
+                                          extents);
+
+    case CAIRO_PATTERN_TYPE_RADIAL:
+       return i965_shader_acquire_radial (shader, src,
+                                          (cairo_radial_pattern_t *) pattern,
+                                          extents);
+
+    case CAIRO_PATTERN_TYPE_SURFACE:
+       return i965_shader_acquire_surface (shader, src,
+                                           (cairo_surface_pattern_t *) pattern,
+                                           extents);
+
+    default:
+       ASSERT_NOT_REACHED;
+       return CAIRO_STATUS_SUCCESS;
+    }
+}
+
+static void
+i965_shader_channel_init (union i965_shader_channel *channel)
+{
+    channel->type.vertex = VS_NONE;
+    channel->type.fragment = FS_NONE;
+    channel->type.pattern = PATTERN_NONE;
+
+    channel->base.mode = 0;
+    channel->base.bo = NULL;
+    channel->base.filter = i965_extend (CAIRO_FILTER_NEAREST);
+    channel->base.extend = i965_extend (CAIRO_EXTEND_NONE);
+    channel->base.has_component_alpha = 0;
+    channel->base.constants_size = 0;
+}
+
+void
+i965_shader_init (i965_shader_t *shader,
+                 i965_surface_t *dst,
+                 cairo_operator_t op)
+{
+    shader->committed = FALSE;
+    shader->device = i965_device (dst);
+    shader->target = dst;
+    shader->op = op;
+    shader->constants_size = 0;
+
+    shader->need_combine = FALSE;
+
+    i965_shader_channel_init (&shader->source);
+    i965_shader_channel_init (&shader->mask);
+    i965_shader_channel_init (&shader->clip);
+    i965_shader_channel_init (&shader->dst);
+}
+
+void
+i965_shader_fini (i965_shader_t *shader)
+{
+    if (shader->source.type.pattern == PATTERN_SURFACE)
+       cairo_surface_destroy (shader->source.surface.surface);
+    if (shader->mask.type.pattern == PATTERN_SURFACE)
+       cairo_surface_destroy (shader->mask.surface.surface);
+    if (shader->clip.type.pattern == PATTERN_SURFACE)
+       cairo_surface_destroy (shader->clip.surface.surface);
+    if (shader->dst.type.pattern == PATTERN_SURFACE)
+       cairo_surface_destroy (shader->dst.surface.surface);
+}
+
+void
+i965_shader_set_clip (i965_shader_t *shader,
+                     cairo_clip_t *clip)
+{
+    cairo_surface_t *clip_surface;
+    int clip_x, clip_y;
+    union i965_shader_channel *channel;
+    i965_surface_t *s;
+
+    clip_surface = _cairo_clip_get_surface (clip, &shader->target->intel.drm.base, &clip_x, &clip_y);
+    assert (clip_surface->status == CAIRO_STATUS_SUCCESS);
+    assert (clip_surface->type == CAIRO_SURFACE_TYPE_DRM);
+    s = (i965_surface_t *) clip_surface;
+
+    if (to_intel_bo (s->intel.drm.bo)->batch_write_domain)
+       i965_pipelined_flush (i965_device (s));
+
+    channel = &shader->clip;
+    channel->type.pattern = PATTERN_BASE;
+    channel->type.vertex  = VS_NONE;
+    channel->type.fragment = FS_SURFACE;
+
+    channel->base.bo = to_intel_bo (s->intel.drm.bo);
+    channel->base.content = CAIRO_CONTENT_ALPHA;
+    channel->base.format = CAIRO_FORMAT_A8;
+    channel->base.width  = s->intel.drm.width;
+    channel->base.height = s->intel.drm.height;
+    channel->base.stride = s->intel.drm.stride;
+
+    channel->base.extend = i965_extend (CAIRO_EXTEND_NONE);
+    channel->base.filter = i965_filter (CAIRO_FILTER_NEAREST);
+
+    cairo_matrix_init_scale (&shader->clip.base.matrix,
+                            1. / s->intel.drm.width,
+                            1. / s->intel.drm.height);
+
+    cairo_matrix_translate (&shader->clip.base.matrix,
+                           -clip_x, -clip_y);
+}
+
+static cairo_bool_t
+i965_shader_check_aperture (i965_shader_t *shader,
+                           i965_device_t *device)
+{
+    uint32_t size = device->exec.gtt_size;
+
+    if (shader->target != device->target) {
+       const intel_bo_t *bo = to_intel_bo (shader->target->intel.drm.bo);
+       if (bo->exec == NULL)
+           size += bo->base.size;
+    }
+
+    if (shader->source.base.bo != NULL && shader->source.base.bo != device->source) {
+       const intel_bo_t *bo = to_intel_bo (shader->target->intel.drm.bo);
+       if (bo->exec == NULL)
+           size += bo->base.size;
+    }
+
+    if (shader->mask.base.bo != NULL && shader->mask.base.bo != device->mask) {
+       const intel_bo_t *bo = to_intel_bo (shader->target->intel.drm.bo);
+       if (bo->exec == NULL)
+           size += bo->base.size;
+    }
+
+    if (shader->clip.base.bo != NULL && shader->clip.base.bo != device->clip) {
+       const intel_bo_t *bo = to_intel_bo (shader->target->intel.drm.bo);
+       if (bo->exec == NULL)
+           size += bo->base.size;
+    }
+
+    return size <= device->intel.gtt_avail_size;
+}
+
+static cairo_status_t
+i965_shader_setup_dst (i965_shader_t *shader)
+{
+    union i965_shader_channel *channel;
+    i965_surface_t *s, *clone;
+
+    /* We need to manual blending if we have a clip surface and an unbounded op,
+     * or an extended blend mode.
+     */
+    if (shader->need_combine ||
+       (shader->op < CAIRO_OPERATOR_SATURATE &&
+        (shader->clip.type.fragment == FS_NONE ||
+         _cairo_operator_bounded_by_mask (shader->op))))
+    {
+       return CAIRO_STATUS_SUCCESS;
+    }
+
+    shader->need_combine = TRUE;
+
+    s = shader->target;
+
+    /* we need to allocate a new render target and use the original as a source */
+    clone = (i965_surface_t *)
+       i965_surface_create_internal ((cairo_drm_device_t *) s->intel.drm.base.device,
+                                     s->intel.drm.base.content,
+                                     s->intel.drm.width,
+                                     s->intel.drm.height,
+                                     I965_TILING_DEFAULT,
+                                     TRUE);
+    if (unlikely (clone->intel.drm.base.status))
+       return clone->intel.drm.base.status;
+
+    if (to_intel_bo (s->intel.drm.bo)->batch_write_domain)
+       i965_pipelined_flush (i965_device (s));
+
+    channel = &shader->dst;
+
+    channel->type.vertex = VS_NONE;
+    channel->type.fragment = FS_SURFACE;
+    channel->type.pattern = PATTERN_SURFACE;
+
+    /* swap buffer objects */
+    channel->base.bo = to_intel_bo (s->intel.drm.bo);
+    s->intel.drm.bo = ((cairo_drm_surface_t *) clone)->bo;
+    ((cairo_drm_surface_t *) clone)->bo = &channel->base.bo->base;
+
+    channel->base.content = s->intel.drm.base.content;
+    channel->base.format  = s->intel.drm.format;
+    channel->base.width   = s->intel.drm.width;
+    channel->base.height  = s->intel.drm.height;
+    channel->base.stride  = s->intel.drm.stride;
+
+    channel->base.filter = i965_filter (CAIRO_FILTER_NEAREST);
+    channel->base.extend = i965_extend (CAIRO_EXTEND_NONE);
+
+    cairo_matrix_init_scale (&channel->base.matrix,
+                            1. / s->intel.drm.width,
+                            1. / s->intel.drm.height);
+
+    channel->surface.surface = &clone->intel.drm.base;
+
+    s->intel.drm.base.content = clone->intel.drm.base.content;
+    s->intel.drm.format = clone->intel.drm.format;
+    assert (s->intel.drm.width == clone->intel.drm.width);
+    assert (s->intel.drm.height == clone->intel.drm.height);
+    s->intel.drm.stride = clone->intel.drm.stride;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static inline void
+constant_add_float (i965_shader_t *shader, float v)
+{
+    shader->constants[shader->constants_size++] = v;
+}
+
+static inline void
+i965_shader_copy_channel_constants (i965_shader_t *shader,
+                                   const union i965_shader_channel *channel)
+{
+    if (channel->base.constants_size) {
+       assert (shader->constants_size + channel->base.constants_size < ARRAY_LENGTH (shader->constants));
+
+       memcpy (shader->constants + shader->constants_size,
+               channel->base.constants,
+               sizeof (float) * channel->base.constants_size);
+       shader->constants_size += channel->base.constants_size;
+    }
+}
+
+static void
+i965_shader_setup_channel_constants (i965_shader_t *shader,
+                                    const union i965_shader_channel *channel)
+{
+    switch (channel->type.fragment) {
+    case FS_NONE:
+    case FS_CONSTANT:
+       /* no plane equations */
+       break;
+
+    case FS_LINEAR:
+       constant_add_float (shader, channel->base.matrix.xx);
+       constant_add_float (shader, channel->base.matrix.xy);
+       constant_add_float (shader, 0);
+       constant_add_float (shader, channel->base.matrix.x0);
+       break;
+
+    case FS_RADIAL:
+    case FS_SURFACE:
+       constant_add_float (shader, channel->base.matrix.xx);
+       constant_add_float (shader, channel->base.matrix.xy);
+       constant_add_float (shader, 0);
+       constant_add_float (shader, channel->base.matrix.x0);
+
+       constant_add_float (shader, channel->base.matrix.yx);
+       constant_add_float (shader, channel->base.matrix.yy);
+       constant_add_float (shader, 0);
+       constant_add_float (shader, channel->base.matrix.y0);
+       break;
+
+    case FS_SPANS:
+    case FS_GLYPHS:
+       /* use pue from SF */
+       break;
+    }
+
+    i965_shader_copy_channel_constants (shader, channel);
+}
+
+static void
+i965_shader_setup_constants (i965_shader_t *shader)
+{
+    i965_shader_setup_channel_constants (shader, &shader->source);
+    i965_shader_setup_channel_constants (shader, &shader->mask);
+    i965_shader_setup_channel_constants (shader, &shader->clip);
+    i965_shader_setup_channel_constants (shader, &shader->dst);
+    assert (shader->constants_size < ARRAY_LENGTH (shader->constants));
+}
+
+/**
+ * Highest-valued BLENDFACTOR used in i965_blend_op.
+ *
+ * This leaves out BRW_BLENDFACTOR_INV_DST_COLOR,
+ * BRW_BLENDFACTOR_INV_CONST_{COLOR,ALPHA},
+ * BRW_BLENDFACTOR_INV_SRC1_{COLOR,ALPHA}
+ */
+#define BRW_BLENDFACTOR_COUNT (BRW_BLENDFACTOR_INV_DST_ALPHA + 1)
+
+static void
+i965_shader_get_blend_cntl (const i965_shader_t *shader,
+                           uint32_t *sblend, uint32_t *dblend)
+{
+    static const struct blendinfo {
+       cairo_bool_t dst_alpha;
+       cairo_bool_t src_alpha;
+       uint32_t src_blend;
+       uint32_t dst_blend;
+    } i965_blend_op[] = {
+       /* CAIRO_OPERATOR_CLEAR treat as SOURCE with transparent */
+       {0, 0, BRW_BLENDFACTOR_ONE,          BRW_BLENDFACTOR_ZERO},
+       /* CAIRO_OPERATOR_SOURCE */
+       {0, 0, BRW_BLENDFACTOR_ONE,           BRW_BLENDFACTOR_ZERO},
+       /* CAIRO_OPERATOR_OVER */
+       {0, 1, BRW_BLENDFACTOR_ONE,           BRW_BLENDFACTOR_INV_SRC_ALPHA},
+       /* CAIRO_OPERATOR_IN */
+       {1, 0, BRW_BLENDFACTOR_DST_ALPHA,     BRW_BLENDFACTOR_ZERO},
+       /* CAIRO_OPERATOR_OUT */
+       {1, 0, BRW_BLENDFACTOR_INV_DST_ALPHA, BRW_BLENDFACTOR_ZERO},
+       /* CAIRO_OPERATOR_ATOP */
+       {1, 1, BRW_BLENDFACTOR_DST_ALPHA,     BRW_BLENDFACTOR_INV_SRC_ALPHA},
+
+       /* CAIRO_OPERATOR_DEST */
+       {0, 0, BRW_BLENDFACTOR_ZERO,          BRW_BLENDFACTOR_ONE},
+       /* CAIRO_OPERATOR_DEST_OVER */
+       {1, 0, BRW_BLENDFACTOR_INV_DST_ALPHA, BRW_BLENDFACTOR_ONE},
+       /* CAIRO_OPERATOR_DEST_IN */
+       {0, 1, BRW_BLENDFACTOR_ZERO,          BRW_BLENDFACTOR_SRC_ALPHA},
+       /* CAIRO_OPERATOR_DEST_OUT */
+       {0, 1, BRW_BLENDFACTOR_ZERO,          BRW_BLENDFACTOR_INV_SRC_ALPHA},
+       /* CAIRO_OPERATOR_DEST_ATOP */
+       {1, 1, BRW_BLENDFACTOR_INV_DST_ALPHA, BRW_BLENDFACTOR_SRC_ALPHA},
+       /* CAIRO_OPERATOR_XOR */
+       {1, 1, BRW_BLENDFACTOR_INV_DST_ALPHA, BRW_BLENDFACTOR_INV_SRC_ALPHA},
+       /* CAIRO_OPERATOR_ADD */
+       {0, 0, BRW_BLENDFACTOR_ONE,           BRW_BLENDFACTOR_ONE},
+    };
+    const struct blendinfo *op = &i965_blend_op[shader->op];
+
+    *sblend = op->src_blend;
+    *dblend = op->dst_blend;
+
+    /* If there's no dst alpha channel, adjust the blend op so that we'll treat
+     * it as always 1.
+     */
+    if (shader->target->intel.drm.base.content == CAIRO_CONTENT_COLOR &&
+       op->dst_alpha)
+    {
+       if (*sblend == BRW_BLENDFACTOR_DST_ALPHA)
+           *sblend = BRW_BLENDFACTOR_ONE;
+       else if (*sblend == BRW_BLENDFACTOR_INV_DST_ALPHA)
+           *sblend = BRW_BLENDFACTOR_ZERO;
+    }
+}
+
+static void
+emit_wm_subpans_to_pixels (struct brw_compile *compile,
+                          int tmp)
+{
+    /* Inputs:
+     * R1.5 x/y of upper-left pixel of subspan 3
+     * R1.4 x/y of upper-left pixel of subspan 2
+     * R1.3 x/y of upper-left pixel of subspan 1
+     * R1.2 x/y of upper-left pixel of subspan 0
+     *
+     * Outputs:
+     * M1,2: u
+     * M3,4: v
+     *
+     * upper left, upper right, lower left, lower right.
+     */
+
+    /* compute pixel locations for each subspan */
+    brw_set_compression_control (compile, BRW_COMPRESSION_NONE);
+    brw_ADD (compile,
+            brw_vec8_grf (tmp),
+            brw_reg (BRW_GENERAL_REGISTER_FILE, 1, 4,
+                     BRW_REGISTER_TYPE_UW,
+                     BRW_VERTICAL_STRIDE_2,
+                     BRW_WIDTH_4,
+                     BRW_HORIZONTAL_STRIDE_0,
+                     BRW_SWIZZLE_NOOP,
+                     WRITEMASK_XYZW),
+            brw_imm_vf4 (VF_ZERO, VF_ONE, VF_ZERO, VF_ONE));
+    brw_ADD (compile,
+            brw_vec8_grf (tmp+1),
+            brw_reg (BRW_GENERAL_REGISTER_FILE, 1, 8,
+                     BRW_REGISTER_TYPE_UW,
+                     BRW_VERTICAL_STRIDE_2,
+                     BRW_WIDTH_4,
+                     BRW_HORIZONTAL_STRIDE_0,
+                     BRW_SWIZZLE_NOOP,
+                     WRITEMASK_XYZW),
+            brw_imm_vf4 (VF_ZERO, VF_ONE, VF_ZERO, VF_ONE));
+    brw_ADD (compile,
+            brw_vec8_grf (tmp+2),
+            brw_reg (BRW_GENERAL_REGISTER_FILE, 1, 5,
+                     BRW_REGISTER_TYPE_UW,
+                     BRW_VERTICAL_STRIDE_2,
+                     BRW_WIDTH_4,
+                     BRW_HORIZONTAL_STRIDE_0,
+                     BRW_SWIZZLE_NOOP,
+                     WRITEMASK_XYZW),
+            brw_imm_vf4 (VF_ZERO, VF_ZERO, VF_ONE, VF_ONE));
+    brw_ADD (compile,
+            brw_vec8_grf (tmp+3),
+            brw_reg (BRW_GENERAL_REGISTER_FILE, 1, 9,
+                     BRW_REGISTER_TYPE_UW,
+                     BRW_VERTICAL_STRIDE_2,
+                     BRW_WIDTH_4,
+                     BRW_HORIZONTAL_STRIDE_0,
+                     BRW_SWIZZLE_NOOP,
+                     WRITEMASK_XYZW),
+            brw_imm_vf4 (VF_ZERO, VF_ZERO, VF_ONE, VF_ONE));
+    brw_set_compression_control (compile, BRW_COMPRESSION_COMPRESSED);
+}
+
+static void
+emit_wm_affine (struct brw_compile *compile,
+               int tmp, int reg, int msg)
+{
+    emit_wm_subpans_to_pixels (compile, tmp);
+
+    brw_LINE (compile,
+             brw_null_reg (),
+             brw_vec1_grf (reg, 0),
+             brw_vec8_grf (tmp));
+    brw_MAC (compile,
+            brw_message_reg (msg + 1),
+            brw_vec1_grf (reg, 1),
+            brw_vec8_grf (tmp+2));
+
+    brw_LINE (compile,
+             brw_null_reg (),
+             brw_vec1_grf (reg, 4),
+             brw_vec8_grf (tmp));
+    brw_MAC (compile,
+            brw_message_reg (msg + 3),
+            brw_vec1_grf (reg, 5),
+            brw_vec8_grf (tmp+2));
+}
+
+static void
+emit_wm_glyph (struct brw_compile *compile,
+              int tmp, int vue, int msg)
+{
+    emit_wm_subpans_to_pixels (compile, tmp);
+
+    brw_MUL (compile,
+            brw_null_reg (),
+            brw_vec8_grf (tmp),
+            brw_imm_f (1./1024));
+    brw_ADD (compile,
+            brw_message_reg (msg + 1),
+            brw_acc_reg (),
+            brw_vec1_grf (vue, 0));
+
+    brw_MUL (compile,
+            brw_null_reg (),
+            brw_vec8_grf (tmp + 2),
+            brw_imm_f (1./1024));
+    brw_ADD (compile,
+            brw_message_reg (msg + 3),
+            brw_acc_reg (),
+            brw_vec1_grf (vue, 1));
+}
+
+static void
+emit_wm_load_constant (struct brw_compile *compile,
+                      int reg,
+                      struct brw_reg *result)
+{
+    int n;
+
+    for (n = 0; n < 4; n++) {
+       result[n] = result[n+4] = brw_reg (BRW_GENERAL_REGISTER_FILE, reg, n,
+                                          BRW_REGISTER_TYPE_F,
+                                          BRW_VERTICAL_STRIDE_0,
+                                          BRW_WIDTH_1,
+                                          BRW_HORIZONTAL_STRIDE_0,
+                                          BRW_SWIZZLE_XXXX,
+                                          WRITEMASK_XYZW);
+    }
+}
+
+static void
+emit_wm_load_opacity (struct brw_compile *compile,
+                     int reg,
+                     struct brw_reg *result)
+{
+    result[0] = result[1] = result[2] = result[3] =
+       result[4] = result[5] = result[6] = result[7] =
+       brw_reg (BRW_GENERAL_REGISTER_FILE, reg, 0,
+                BRW_REGISTER_TYPE_F,
+                BRW_VERTICAL_STRIDE_0,
+                BRW_WIDTH_1,
+                BRW_HORIZONTAL_STRIDE_1,
+                BRW_SWIZZLE_XXXX,
+                WRITEMASK_XYZW);
+}
+
+static void
+emit_wm_load_linear (struct brw_compile *compile,
+                    int tmp, int reg, int msg)
+{
+    emit_wm_subpans_to_pixels (compile, tmp);
+
+    brw_LINE (compile,
+             brw_null_reg(),
+             brw_vec1_grf (reg, 0),
+             brw_vec8_grf (tmp));
+    brw_MAC (compile,
+            brw_message_reg(msg + 1),
+            brw_vec1_grf (reg, 1),
+            brw_vec8_grf (tmp + 2));
+}
+
+static void
+emit_wm_load_radial (struct brw_compile *compile,
+                    int reg, int msg)
+
+{
+    struct brw_reg c1x = brw_vec1_grf (reg, 0);
+    struct brw_reg c1y = brw_vec1_grf (reg, 1);
+    struct brw_reg minus_r_sq = brw_vec1_grf (reg, 3);
+    struct brw_reg cdx = brw_vec1_grf (reg, 4);
+    struct brw_reg cdy = brw_vec1_grf (reg, 5);
+    struct brw_reg neg_4a = brw_vec1_grf (reg + 1, 0);
+    struct brw_reg inv_2a = brw_vec1_grf (reg + 1, 1);
+
+    struct brw_reg tmp_x = brw_uw16_grf (30, 0);
+    struct brw_reg tmp_y = brw_uw16_grf (28, 0);
+    struct brw_reg det = brw_vec8_grf (22);
+    struct brw_reg b = brw_vec8_grf (20);
+    struct brw_reg c = brw_vec8_grf (18);
+    struct brw_reg pdx = brw_vec8_grf (16);
+    struct brw_reg pdy = brw_vec8_grf (14);
+    struct brw_reg t = brw_message_reg (msg + 1);
+
+    /* cdx = (c₂x - c₁x)
+     * cdy = (c₂y - c₁y)
+     *  dr =  r₂-r₁
+     * pdx =  px - c₁x
+     * pdy =  py - c₁y
+     *
+     * A = cdx² + cdy² - dr²
+     * B = -2·(pdx·cdx + pdy·cdy + r₁·dr)
+     * C = pdx² + pdy² - r₁²
+     *
+     * t = (-2·B ± ⎷(B² - 4·A·C)) / 2·A
+     */
+
+    brw_ADD (compile, pdx, vec8 (tmp_x), negate (c1x));
+    brw_ADD (compile, pdy, vec8 (tmp_y), negate (c1y));
+
+    brw_LINE (compile, brw_null_reg (), cdx, pdx);
+    brw_MAC (compile, b, cdy, pdy);
+
+    brw_MUL (compile, brw_null_reg (), pdx, pdx);
+    brw_MAC (compile, c, pdy, pdy);
+    brw_ADD (compile, c, c, minus_r_sq);
+
+    brw_MUL (compile, brw_null_reg (), b, b);
+    brw_MAC (compile, det, neg_4a, c);
+
+    /* XXX use rsqrt like i915?, it's faster and we need to mac anyway */
+    brw_math (compile,
+             det,
+             BRW_MATH_FUNCTION_SQRT,
+             BRW_MATH_SATURATE_NONE,
+             2,
+             det,
+             BRW_MATH_DATA_VECTOR,
+             BRW_MATH_PRECISION_FULL);
+
+    /* XXX cmp, +- */
+
+    brw_ADD (compile, det, negate (det), negate (b));
+    brw_ADD (compile, det, det, negate (b));
+    brw_MUL (compile, t, det, inv_2a);
+}
+
+static int
+emit_wm_sample (struct brw_compile *compile,
+               union i965_shader_channel *channel,
+               int sampler,
+               int msg_base, int msg_len,
+               int dst,
+               struct brw_reg *result)
+{
+    int response_len, mask;
+
+    if (channel->base.content == CAIRO_CONTENT_ALPHA) {
+       mask = 0x7000;
+       response_len = 2;
+       result[0] = result[1] = result[2] = result[3] = brw_vec8_grf (dst);
+       result[4] = result[5] = result[6] = result[7] = brw_vec8_grf (dst + 1);
+    } else {
+       mask = 0;
+       response_len = 8;
+       result[0] = brw_vec8_grf (dst + 0);
+       result[1] = brw_vec8_grf (dst + 2);
+       result[2] = brw_vec8_grf (dst + 4);
+       result[3] = brw_vec8_grf (dst + 6);
+       result[4] = brw_vec8_grf (dst + 1);
+       result[5] = brw_vec8_grf (dst + 3);
+       result[6] = brw_vec8_grf (dst + 5);
+       result[7] = brw_vec8_grf (dst + 7);
+    }
+
+    brw_set_compression_control (compile, BRW_COMPRESSION_NONE);
+
+    brw_set_mask_control (compile, BRW_MASK_DISABLE);
+    brw_MOV (compile,
+            get_element_ud (brw_vec8_grf (0), 2),
+            brw_imm_ud (mask));
+    brw_set_mask_control (compile, BRW_MASK_ENABLE);
+
+    brw_SAMPLE (compile,
+               brw_uw16_grf (dst, 0),
+               msg_base,
+               brw_uw8_grf (0, 0),
+               sampler + 1, /* binding table */
+               sampler,
+               WRITEMASK_XYZW,
+               BRW_SAMPLER_MESSAGE_SIMD16_SAMPLE,
+               response_len,
+               msg_len,
+               0 /* eot */);
+
+    brw_set_compression_control (compile, BRW_COMPRESSION_COMPRESSED);
+
+    return response_len;
+}
+
+#define MAX_MSG_REGISTER 16
+
+static void
+emit_wm_load_channel (struct brw_compile *compile,
+                     union i965_shader_channel *channel,
+                     int *vue,
+                     int *cue,
+                     int *msg,
+                     int *sampler,
+                     int *grf,
+                     struct brw_reg *result)
+{
+    switch (channel->type.fragment) {
+    case FS_NONE:
+       break;
+
+    case FS_CONSTANT:
+       emit_wm_load_constant (compile, *cue, result);
+       *cue += 1;
+       break;
+
+    case FS_RADIAL:
+       emit_wm_load_radial (compile, *cue, *msg);
+       *cue += 2;
+
+       if (*msg + 3 > MAX_MSG_REGISTER)
+           *msg = 1;
+
+       *grf += emit_wm_sample (compile, channel, *sampler, *msg, 3, *grf, result);
+       *sampler += 1;
+       *msg += 3;
+       break;
+
+    case FS_LINEAR:
+       emit_wm_load_linear (compile, *grf, *cue, *msg);
+       *cue += 1;
+
+       if (*msg + 3 > MAX_MSG_REGISTER)
+           *msg = 1;
+
+       *grf += emit_wm_sample (compile, channel, *sampler, *msg, 3, *grf, result);
+       *sampler += 1;
+       *msg += 3;
+       break;
+
+    case FS_SURFACE:
+       emit_wm_affine (compile, *grf, *cue, *msg);
+       *cue += 2;
+
+       if (*msg + 5 > MAX_MSG_REGISTER)
+           *msg = 1;
+
+       *grf += emit_wm_sample (compile, channel, *sampler, *msg, 5, *grf, result);
+       *sampler += 1;
+       *msg += 5;
+       break;
+
+    case FS_SPANS:
+       emit_wm_load_opacity (compile, *vue, result);
+       *vue += 1;
+       break;
+
+    case FS_GLYPHS:
+       emit_wm_glyph (compile, *grf, *vue, *msg);
+       *vue += 1;
+
+       if (*msg + 5 > MAX_MSG_REGISTER)
+           *msg = 1;
+
+       *grf += emit_wm_sample (compile, channel, *sampler, *msg, 5, *grf, result);
+       *sampler += 1;
+       *msg += 5;
+       break;
+    }
+}
+
+static unsigned long
+i965_wm_kernel_hash (const i965_shader_t *shader)
+{
+    unsigned long hash;
+
+    hash =
+       (shader->source.type.fragment & 0xff) |
+       (shader->mask.type.fragment & 0xff) << 8 |
+       (shader->clip.type.fragment & 0xff) << 16;
+    if (shader->need_combine)
+       hash |= (1 + shader->op) << 24;
+
+    return hash;
+}
+
+static void
+i965_wm_kernel_init (struct i965_wm_kernel *key,
+                    const i965_shader_t *shader)
+{
+    key->entry.hash = i965_wm_kernel_hash (shader);
+}
+
+static uint32_t
+i965_shader_const_urb_length (i965_shader_t *shader)
+{
+    const int lengths[] = { 0, 1, 1, 4, 2, 0, 0 };
+    int count = 0; /* 128-bit/16-byte increments */
+
+    count += lengths[shader->source.type.fragment];
+    count += lengths[shader->mask.type.fragment];
+    count += lengths[shader->clip.type.fragment];
+    count += lengths[shader->dst.type.fragment];
+
+    return (count + 1) / 2; /* 256-bit/32-byte increments */
+}
+
+static uint32_t
+i965_shader_pue_length (i965_shader_t *shader)
+{
+    return 1 + (shader->mask.type.vertex != VS_NONE);
+}
+
+static uint32_t
+create_wm_kernel (i965_device_t *device,
+                 i965_shader_t *shader,
+                 int *num_reg)
+{
+    struct brw_compile compile;
+    struct brw_reg source[8], mask[8], clip[8], dst[8];
+    const uint32_t *program;
+    uint32_t size;
+    int msg, cue, vue, grf, sampler;
+    int i;
+
+    struct i965_wm_kernel key, *cache;
+    cairo_status_t status;
+    uint32_t offset;
+
+    i965_wm_kernel_init (&key, shader);
+    cache = _cairo_hash_table_lookup (device->wm_kernels, &key.entry);
+    if (cache != NULL)
+       return cache->offset;
+
+    brw_compile_init (&compile, device->is_g4x);
+
+    if (key.entry.hash == FS_CONSTANT &&
+       to_intel_bo (shader->target->intel.drm.bo)->tiling)
+    {
+       struct brw_instruction *insn;
+
+       assert (i965_shader_const_urb_length (shader) == 1);
+       brw_MOV (&compile, brw_message4_reg (2), brw_vec4_grf (2, 0));
+       grf = 3;
+
+       brw_push_insn_state (&compile);
+       brw_set_mask_control (&compile, BRW_MASK_DISABLE); /* ? */
+       brw_MOV (&compile,
+                retype (brw_message_reg (1), BRW_REGISTER_TYPE_UD),
+                retype (brw_vec8_grf (1), BRW_REGISTER_TYPE_UD));
+       brw_pop_insn_state (&compile);
+
+       insn = brw_next_instruction (&compile, BRW_OPCODE_SEND);
+       insn->header.predicate_control = 0;
+       insn->header.compression_control = BRW_COMPRESSION_NONE;
+       insn->header.destreg__conditonalmod = 0;
+
+       brw_instruction_set_destination (insn,
+                                        retype (vec16 (brw_acc_reg ()),
+                                                BRW_REGISTER_TYPE_UW));
+
+       brw_instruction_set_source0 (insn,
+                                    retype (brw_vec8_grf (0),
+                                            BRW_REGISTER_TYPE_UW));
+
+       brw_instruction_set_dp_write_message (insn,
+                                             0,
+                                             BRW_DATAPORT_RENDER_TARGET_WRITE_SIMD16_SINGLE_SOURCE_REPLICATED, /* msg_control */
+                                             BRW_DATAPORT_WRITE_MESSAGE_RENDER_TARGET_WRITE, /* msg_type */
+                                             3,
+                                             1,        /* pixel scoreboard */
+                                             0,
+                                             TRUE);
+    }
+    else
+    {
+       msg = 1;
+       cue = 2;
+       vue = cue + i965_shader_const_urb_length (shader);
+       grf = vue + i965_shader_pue_length (shader);
+       sampler = 0;
+
+       brw_set_compression_control (&compile, BRW_COMPRESSION_COMPRESSED);
+       emit_wm_load_channel (&compile, &shader->source,
+                             &vue, &cue, &msg, &sampler, &grf,
+                             source);
+       emit_wm_load_channel (&compile, &shader->mask,
+                             &vue, &cue, &msg, &sampler, &grf,
+                             mask);
+       emit_wm_load_channel (&compile, &shader->clip,
+                             &vue, &cue, &msg, &sampler, &grf,
+                             clip);
+       emit_wm_load_channel (&compile, &shader->dst,
+                             &vue, &cue, &msg, &sampler, &grf,
+                             dst);
+       brw_set_compression_control (&compile, BRW_COMPRESSION_NONE);
+
+       if (shader->need_combine) {
+           if (shader->mask.type.fragment != FS_NONE &&
+               shader->clip.type.fragment != FS_NONE)
+           {
+               for (i = 0; i < 8; i++)
+                   brw_MUL (&compile, mask[i], mask[i], clip[i]);
+           }
+
+           /* XXX LERP ! */
+           for (i = 0; i < 8; i++)
+               brw_MOV (&compile, brw_message_reg (2 + i), source[i]);
+       } else {
+           if (shader->mask.type.fragment != FS_NONE) {
+               if (shader->clip.type.fragment != FS_NONE) {
+                   for (i = 0; i < 8; i++)
+                       brw_MUL (&compile, mask[i], mask[i], clip[i]);
+               }
+
+               for (i = 0; i < 8; i++)
+                   brw_MUL (&compile, brw_message_reg (2 + i), source[i], mask[i]);
+           } else {
+               if (shader->clip.type.fragment != FS_NONE) {
+                   for (i = 0; i < 8; i++)
+                       brw_MUL (&compile, brw_message_reg (2 + i), source[i], clip[i]);
+               } else {
+                   for (i = 0; i < 8; i++)
+                       brw_MOV (&compile, brw_message_reg (2 + i), source[i]);
+               }
+           }
+       }
+
+       brw_push_insn_state (&compile);
+       brw_set_mask_control (&compile, BRW_MASK_DISABLE); /* ? */
+       brw_MOV (&compile,
+                retype (brw_message_reg (1), BRW_REGISTER_TYPE_UD),
+                retype (brw_vec8_grf (1), BRW_REGISTER_TYPE_UD));
+       brw_pop_insn_state (&compile);
+
+       brw_fb_WRITE (&compile,
+                     retype (vec16 (brw_acc_reg ()), BRW_REGISTER_TYPE_UW),
+                     0,                /* base reg */
+                     retype (brw_vec8_grf (0), BRW_REGISTER_TYPE_UW),
+                     0,                /* binding table index */
+                     2 + 8,    /* msg length */
+                     0,                /* response length */
+                     TRUE);    /* EOT */
+    }
+
+    program = brw_get_program (&compile, &size);
+    *num_reg = grf;
+
+    i965_stream_align (&device->general, 64);
+    offset = i965_stream_emit (&device->general, program, size);
+
+    cache = _cairo_freelist_alloc (&device->wm_kernel_freelist);
+    if (likely (cache != NULL)) {
+       i965_wm_kernel_init (cache, shader);
+       cache->offset = offset;
+       status = _cairo_hash_table_insert (device->wm_kernels, &cache->entry);
+       if (unlikely (status))
+           _cairo_freelist_free (&device->wm_kernel_freelist, cache);
+    }
+
+    return offset;
+}
+
+static uint32_t
+create_sf_kernel (i965_device_t *device,
+                 i965_shader_t *shader)
+{
+    struct brw_compile compile;
+    const uint32_t *program;
+    uint32_t size;
+    int msg_len;
+
+    brw_compile_init (&compile, device->is_g4x);
+
+    switch (shader->mask.type.vertex) {
+    default:
+    case VS_NONE:
+       /* use curb plane eq in WM */
+       msg_len = 1;
+       break;
+
+    case VS_SPANS:
+       /* just a constant opacity */
+       brw_MOV (&compile,
+                brw_message4_reg (1),
+                brw_vec4_grf (3, 0));
+       msg_len = 2;
+       break;
+
+    case VS_GLYPHS:
+       /* an offset+sf into the glyph cache */
+       brw_MOV (&compile,
+                brw_acc_reg (),
+                brw_vec2_grf (3, 0));
+       brw_MAC (&compile,
+                brw_message4_reg (1),
+                negate (brw_vec2_grf (1, 4)),
+                brw_imm_f (1./1024));
+       msg_len = 2;
+       break;
+    }
+
+    brw_urb_WRITE (&compile,
+                  brw_null_reg (),
+                  0,
+                  brw_vec8_grf (0), /* r0, will be copied to m0 */
+                  0,   /* allocate */
+                  1,   /* used */
+                  msg_len,
+                  0,   /* response len */
+                  1,   /* eot */
+                  1,   /* writes complete */
+                  0,   /* offset */
+                  BRW_URB_SWIZZLE_NONE);
+
+    program = brw_get_program (&compile, &size);
+
+    i965_stream_align (&device->general, 64);
+    return i965_stream_emit (&device->general, program, size);
+}
+
+static uint32_t
+i965_sf_kernel (const i965_shader_t *shader)
+{
+    return shader->mask.type.vertex;
+}
+
+static void
+i965_sf_state_init (struct i965_sf_state *key,
+                   const i965_shader_t *shader)
+{
+    key->entry.hash = i965_sf_kernel (shader);
+}
+
+cairo_bool_t
+i965_sf_state_equal (const void *A, const void *B)
+{
+    const cairo_hash_entry_t *a = A, *b = B;
+    return a->hash == b->hash;
+}
+
+/**
+ * Sets up the SF state pointing at an SF kernel.
+ *
+ * The SF kernel does coord interp: for each attribute,
+ * calculate dA/dx and dA/dy.  Hand these interpolation coefficients
+ * back to SF which then hands pixels off to WM.
+ */
+static uint32_t
+gen4_create_sf_state (i965_device_t *device,
+                     i965_shader_t *shader)
+{
+    struct brw_sf_unit_state *state;
+    struct i965_sf_state key, *cache;
+    cairo_status_t status;
+    uint32_t offset;
+
+    i965_sf_state_init (&key, shader);
+    if (i965_sf_state_equal (&key, &device->sf_state))
+       return device->sf_state.offset;
+
+    cache = _cairo_hash_table_lookup (device->sf_states, &key.entry);
+    if (cache != NULL) {
+       offset = cache->offset;
+       goto DONE;
+    }
+
+    offset = create_sf_kernel (device, shader);
+
+    state = i965_stream_alloc (&device->general, 32, sizeof (*state));
+    memset (state, 0, sizeof (*state));
+
+    state->thread0.grf_reg_count = BRW_GRF_BLOCKS (3);
+    assert ((offset & 63) == 0);
+    state->thread0.kernel_start_pointer = offset >> 6;
+    state->sf1.single_program_flow = 1;
+    state->thread3.urb_entry_read_length = 1; /* 1 URB per vertex */
+    state->thread3.urb_entry_read_offset = 1;
+    state->thread3.dispatch_grf_start_reg = 3;
+    state->thread4.max_threads = SF_MAX_THREADS - 1;
+    state->thread4.urb_entry_allocation_size = URB_SF_ENTRY_SIZE - 1;
+    state->thread4.nr_urb_entries = URB_SF_ENTRIES;
+    state->sf6.dest_org_vbias = 0x8;
+    state->sf6.dest_org_hbias = 0x8;
+
+    offset = i965_stream_offsetof (&device->general, state);
+
+    cache = _cairo_freelist_alloc (&device->sf_freelist);
+    if (likely (cache != NULL)) {
+       i965_sf_state_init (cache, shader);
+       cache->offset = offset;
+       status = _cairo_hash_table_insert (device->sf_states, &cache->entry);
+       if (unlikely (status))
+           _cairo_freelist_free (&device->sf_freelist, cache);
+    }
+
+  DONE:
+    i965_sf_state_init (&device->sf_state, shader);
+    device->sf_state.offset = offset;
+
+    return offset;
+}
+
+static unsigned long
+i965_shader_sampler_hash (const i965_shader_t *shader)
+{
+    unsigned long hash = 0;
+    unsigned int offset = 0;
+
+    if (shader->source.base.bo != NULL) {
+       hash |= (shader->source.base.filter << offset) |
+               (shader->source.base.extend << (offset + 4));
+       offset += 8;
+    }
+
+    if (shader->mask.base.bo != NULL) {
+       hash |= (shader->mask.base.filter << offset) |
+               (shader->mask.base.extend << (offset + 4));
+       offset += 8;
+    }
+
+    if (shader->clip.base.bo != NULL) {
+       hash |= (shader->clip.base.filter << offset) |
+               (shader->clip.base.extend << (offset + 4));
+       offset += 8;
+    }
+
+    if (shader->dst.base.bo != NULL) {
+       hash |= (shader->dst.base.filter << offset) |
+               (shader->dst.base.extend << (offset + 4));
+       offset += 8;
+    }
+
+    return hash;
+}
+
+static void
+i965_sampler_init (struct i965_sampler *key,
+                  const i965_shader_t *shader)
+{
+    key->entry.hash = i965_shader_sampler_hash (shader);
+}
+
+static void
+emit_sampler_channel (i965_device_t *device,
+                     const union i965_shader_channel *channel,
+                     uint32_t border_color)
+{
+    struct brw_sampler_state *state;
+
+    state = i965_stream_alloc (&device->general, 0, sizeof (*state));
+    memset (state, 0, sizeof (*state));
+
+    state->ss0.lod_preclamp = 1; /* GL mode */
+
+    state->ss0.border_color_mode = BRW_BORDER_COLOR_MODE_LEGACY;
+
+    state->ss0.min_filter = channel->base.filter;
+    state->ss0.mag_filter = channel->base.filter;
+
+    state->ss1.r_wrap_mode = channel->base.extend;
+    state->ss1.s_wrap_mode = channel->base.extend;
+    state->ss1.t_wrap_mode = channel->base.extend;
+
+    assert ((border_color & 31) == 0);
+    state->ss2.border_color_pointer = border_color >> 5;
+}
+
+static uint32_t
+emit_sampler_state_table (i965_device_t *device,
+                         i965_shader_t *shader)
+{
+    struct i965_sampler key, *cache;
+    cairo_status_t status;
+    uint32_t offset;
+
+    if (device->border_color_offset == (uint32_t) -1) {
+       struct brw_sampler_legacy_border_color *border_color;
+
+       border_color = i965_stream_alloc (&device->general, 32,
+                                         sizeof (*border_color));
+       border_color->color[0] = 0; /* R */
+       border_color->color[1] = 0; /* G */
+       border_color->color[2] = 0; /* B */
+       border_color->color[3] = 0; /* A */
+
+       device->border_color_offset = i965_stream_offsetof (&device->general,
+                                                           border_color);
+    } else {
+       i965_sampler_init (&key, shader);
+       cache = _cairo_hash_table_lookup (device->samplers, &key.entry);
+       if (cache != NULL)
+           return cache->offset;
+    }
+
+    i965_stream_align (&device->general, 32);
+    offset = device->general.used;
+    if (shader->source.base.bo != NULL) {
+       emit_sampler_channel (device,
+                             &shader->source,
+                             device->border_color_offset);
+    }
+    if (shader->mask.base.bo != NULL) {
+       emit_sampler_channel (device,
+                             &shader->mask,
+                             device->border_color_offset);
+    }
+    if (shader->clip.base.bo != NULL) {
+       emit_sampler_channel (device,
+                             &shader->clip,
+                             device->border_color_offset);
+    }
+    if (shader->dst.base.bo != NULL) {
+       emit_sampler_channel (device,
+                             &shader->dst,
+                             device->border_color_offset);
+    }
+
+    cache = _cairo_freelist_alloc (&device->sampler_freelist);
+    if (likely (cache != NULL)) {
+       i965_sampler_init (cache, shader);
+       cache->offset = offset;
+       status = _cairo_hash_table_insert (device->samplers, &cache->entry);
+       if (unlikely (status))
+           _cairo_freelist_free (&device->sampler_freelist, cache);
+    }
+
+    return offset;
+}
+
+static void
+i965_cc_state_init (struct i965_cc_state *key,
+                   const i965_shader_t *shader)
+{
+    uint32_t src_blend, dst_blend;
+
+    if (shader->need_combine)
+       src_blend = dst_blend = 0;
+    else
+       i965_shader_get_blend_cntl (shader, &src_blend, &dst_blend);
+
+    key->entry.hash = src_blend | ((dst_blend & 0xffff) << 16);
+}
+
+cairo_bool_t
+i965_cc_state_equal (const void *A, const void *B)
+{
+    const cairo_hash_entry_t *a = A, *b = B;
+    return a->hash == b->hash;
+}
+
+static uint32_t
+cc_state_emit (i965_device_t *device, i965_shader_t *shader)
+{
+    struct brw_cc_unit_state *state;
+    struct i965_cc_state key, *cache;
+    cairo_status_t status;
+    uint32_t src_blend, dst_blend;
+    uint32_t offset;
+
+    i965_cc_state_init (&key, shader);
+    if (i965_cc_state_equal (&key, &device->cc_state))
+       return device->cc_state.offset;
+
+    cache = _cairo_hash_table_lookup (device->cc_states, &key.entry);
+    if (cache != NULL) {
+       offset = cache->offset;
+       goto DONE;
+    }
+
+    if (shader->need_combine)
+       src_blend = dst_blend = 0;
+    else
+       i965_shader_get_blend_cntl (shader, &src_blend, &dst_blend);
+
+    state = i965_stream_alloc (&device->general, 64, sizeof (*state));
+    memset (state, 0, sizeof (*state));
+
+    /* XXX Note errata, need to flush render cache when blend_enable 0 -> 1 */
+    /* XXX 2 source blend */
+    state->cc3.blend_enable = ! shader->need_combine;
+    state->cc5.ia_blend_function = BRW_BLENDFUNCTION_ADD;
+    state->cc5.ia_src_blend_factor  = src_blend;
+    state->cc5.ia_dest_blend_factor = dst_blend;
+    state->cc6.blend_function = BRW_BLENDFUNCTION_ADD;
+    state->cc6.clamp_post_alpha_blend = 1;
+    state->cc6.clamp_pre_alpha_blend  = 1;
+    state->cc6.src_blend_factor  = src_blend;
+    state->cc6.dest_blend_factor = dst_blend;
+
+    offset = i965_stream_offsetof (&device->general, state);
+
+    cache = _cairo_freelist_alloc (&device->cc_freelist);
+    if (likely (cache != NULL)) {
+       i965_cc_state_init (cache, shader);
+       cache->offset = offset;
+       status = _cairo_hash_table_insert (device->cc_states, &cache->entry);
+       if (unlikely (status))
+           _cairo_freelist_free (&device->cc_freelist, cache);
+    }
+
+  DONE:
+    i965_cc_state_init (&device->cc_state, shader);
+    device->cc_state.offset = offset;
+
+    return offset;
+}
+
+static void
+i965_wm_state_init (struct i965_wm_state *key,
+                   const i965_shader_t *shader)
+{
+    key->kernel = i965_wm_kernel_hash (shader);
+    key->sampler = i965_shader_sampler_hash (shader);
+
+    key->entry.hash = key->kernel ^ ((key->sampler) << 16 | (key->sampler >> 16));
+}
+
+cairo_bool_t
+i965_wm_state_equal (const void *A, const void *B)
+{
+    const struct i965_wm_state *a = A, *b = B;
+
+    if (a->entry.hash != b->entry.hash)
+       return FALSE;
+
+    return a->kernel == b->kernel && a->sampler == b->sampler;
+}
+
+static int
+i965_shader_binding_table_count (i965_shader_t *shader)
+{
+    int count;
+
+    count = 1;
+    if (shader->source.type.fragment != FS_CONSTANT)
+       count++;
+    switch (shader->mask.type.fragment) {
+    case FS_NONE:
+    case FS_CONSTANT:
+    case FS_SPANS:
+       break;
+    case FS_LINEAR:
+    case FS_RADIAL:
+    case FS_SURFACE:
+    case FS_GLYPHS:
+       count++;
+    }
+    if (shader->clip.type.fragment == FS_SURFACE)
+       count++;
+    if (shader->dst.type.fragment == FS_SURFACE)
+       count++;
+
+    return count;
+}
+
+static uint32_t
+gen4_create_wm_state (i965_device_t *device,
+                     i965_shader_t *shader)
+{
+    struct brw_wm_unit_state *state;
+    uint32_t sampler;
+    uint32_t kernel;
+
+    struct i965_wm_state key, *cache;
+    cairo_status_t status;
+    int num_reg;
+
+    i965_wm_state_init (&key, shader);
+    if (i965_wm_state_equal (&key, &device->wm_state))
+       return device->wm_state.offset;
+
+    cache = _cairo_hash_table_lookup (device->wm_states, &key.entry);
+    if (cache != NULL) {
+       device->wm_state = *cache;
+       return cache->offset;
+    }
+
+    kernel = create_wm_kernel (device, shader, &num_reg);
+    sampler = emit_sampler_state_table (device, shader);
+
+    state = i965_stream_alloc (&device->general, 32, sizeof (*state));
+    memset (state, 0, sizeof (*state));
+    state->thread0.grf_reg_count = BRW_GRF_BLOCKS (num_reg);
+    assert ((kernel & 63) == 0);
+    state->thread0.kernel_start_pointer = kernel >> 6;
+
+    state->thread3.dispatch_grf_start_reg = 2;
+
+    state->wm4.sampler_count = 1; /* 1-4 samplers used */
+    assert ((sampler & 31) == 0);
+    state->wm4.sampler_state_pointer = sampler >> 5;
+    if (device->is_g4x)
+       state->wm5.max_threads = PS_MAX_THREADS_CTG - 1;
+    else
+       state->wm5.max_threads = PS_MAX_THREADS_BRW - 1;
+    state->wm5.thread_dispatch_enable = 1;
+
+    if (device->is_g4x) {
+       /* XXX contiguous 32 pixel dispatch */
+    }
+    state->wm5.enable_16_pix = 1;
+    /* 8 pixel dispatch and friends */
+    //state->wm5.early_depth_test = 1;
+
+    state->thread1.binding_table_entry_count = i965_shader_binding_table_count(shader);
+    state->thread3.urb_entry_read_length = i965_shader_pue_length (shader);
+    state->thread3.const_urb_entry_read_length = i965_shader_const_urb_length (shader);
+
+    key.offset = i965_stream_offsetof (&device->general, state);
+
+    cache = _cairo_freelist_alloc (&device->wm_state_freelist);
+    if (likely (cache != NULL)) {
+       *cache = key;
+       status = _cairo_hash_table_insert (device->wm_states, &cache->entry);
+       if (unlikely (status))
+           _cairo_freelist_free (&device->wm_state_freelist, cache);
+    }
+
+    device->wm_state = key;
+    return key.offset;
+}
+
+static uint32_t
+vs_unit_state_emit (i965_device_t *device)
+{
+    if (device->vs_offset == (uint32_t) -1) {
+       struct brw_vs_unit_state *state;
+
+       /* Set up the vertex shader to be disabled (passthrough) */
+       state = i965_stream_alloc (&device->general, 32, sizeof (*state));
+       memset (state, 0, sizeof (*state));
+
+       state->thread4.nr_urb_entries = URB_VS_ENTRIES;
+       state->thread4.urb_entry_allocation_size = URB_VS_ENTRY_SIZE - 1;
+       state->vs6.vert_cache_disable = 1;
+
+       device->vs_offset = i965_stream_offsetof (&device->general, state);
+    }
+
+    return device->vs_offset;
+}
+
+static uint32_t
+i965_get_card_format (cairo_format_t format)
+{
+    switch (format) {
+    case CAIRO_FORMAT_ARGB32:
+       return BRW_SURFACEFORMAT_B8G8R8A8_UNORM;
+    case CAIRO_FORMAT_RGB24:
+       return BRW_SURFACEFORMAT_B8G8R8X8_UNORM;
+    case CAIRO_FORMAT_RGB16_565:
+       return BRW_SURFACEFORMAT_B5G6R5_UNORM;
+    case CAIRO_FORMAT_A8:
+       return BRW_SURFACEFORMAT_A8_UNORM;
+    case CAIRO_FORMAT_A1:
+    case CAIRO_FORMAT_INVALID:
+    default:
+       ASSERT_NOT_REACHED;
+       return 0;
+    }
+}
+
+static uint32_t
+i965_get_dest_format (cairo_format_t format)
+{
+    switch (format) {
+    case CAIRO_FORMAT_ARGB32:
+    case CAIRO_FORMAT_RGB24:
+        return BRW_SURFACEFORMAT_B8G8R8A8_UNORM;
+    case CAIRO_FORMAT_RGB16_565:
+        return BRW_SURFACEFORMAT_B5G6R5_UNORM;
+    case CAIRO_FORMAT_A8:
+        return BRW_SURFACEFORMAT_A8_UNORM;
+    case CAIRO_FORMAT_A1:
+    case CAIRO_FORMAT_INVALID:
+    default:
+       ASSERT_NOT_REACHED;
+       return 0;
+    }
+}
+
+/* XXX silly inline due to compiler bug... */
+static inline void
+i965_stream_add_pending_relocation (i965_stream_t *stream,
+                                   uint32_t target_offset,
+                                   uint32_t read_domains,
+                                   uint32_t write_domain,
+                                   uint32_t delta)
+{
+    int n;
+
+    n = stream->num_pending_relocations++;
+    assert (n < stream->max_pending_relocations);
+
+    stream->pending_relocations[n].offset = target_offset;
+    stream->pending_relocations[n].read_domains = read_domains;
+    stream->pending_relocations[n].write_domain = write_domain;
+    stream->pending_relocations[n].delta = delta;
+}
+
+static uint32_t
+emit_surface_state (i965_device_t *device,
+                   cairo_bool_t is_target,
+                   intel_bo_t *bo,
+                   cairo_format_t format,
+                   int width, int height, int stride,
+                   int type)
+{
+    struct brw_surface_state *state;
+    uint32_t write_domain, read_domains;
+    uint32_t offset;
+
+    state = i965_stream_alloc (&device->surface, 32, sizeof (*state));
+    memset (state, 0, sizeof (*state));
+
+    state->ss0.surface_type = type;
+    if (is_target)
+       state->ss0.surface_format = i965_get_dest_format (format);
+    else
+       state->ss0.surface_format = i965_get_card_format (format);
+
+    state->ss0.data_return_format = BRW_SURFACERETURNFORMAT_FLOAT32;
+    state->ss0.color_blend = 1;
+    if (is_target && device->is_g4x)
+       state->ss0.render_cache_read_mode = 1;
+
+    state->ss1.base_addr = bo->offset;
+
+    state->ss2.height = height - 1;
+    state->ss2.width  = width  - 1;
+    state->ss3.pitch  = stride - 1;
+    state->ss3.tile_walk = bo->tiling == I915_TILING_Y;
+    state->ss3.tiled_surface = bo->tiling != I915_TILING_NONE;
+
+    if (is_target) {
+       read_domains = I915_GEM_DOMAIN_RENDER;
+       write_domain = I915_GEM_DOMAIN_RENDER;
+    } else {
+       read_domains = I915_GEM_DOMAIN_SAMPLER;
+       write_domain = 0;
+    }
+
+    offset = i965_stream_offsetof (&device->surface, state);
+    i965_emit_relocation (device, &device->surface,
+                         bo, 0,
+                         read_domains, write_domain,
+                         offset + offsetof (struct brw_surface_state, ss1.base_addr));
+    return offset;
+}
+
+static uint32_t
+emit_surface_state_for_shader (i965_device_t *device,
+                              const union i965_shader_channel *channel)
+{
+    int type = BRW_SURFACE_2D;
+
+    assert (channel->type.fragment != FS_NONE);
+    assert (channel->type.fragment != FS_CONSTANT);
+
+    if (channel->type.fragment != FS_SURFACE)
+       type = BRW_SURFACE_1D;
+
+    return emit_surface_state (device, FALSE,
+                              channel->base.bo,
+                              channel->base.format,
+                              channel->base.width,
+                              channel->base.height,
+                              channel->base.stride,
+                              type);
+}
+
+cairo_bool_t
+i965_wm_binding_equal (const void *A,
+                      const void *B)
+{
+    const struct i965_wm_binding *a = A, *b = B;
+
+    if (a->entry.hash != b->entry.hash)
+       return FALSE;
+
+    if (a->size != b->size)
+       return FALSE;
+
+    return memcmp (a->table, b->table, sizeof (uint32_t) * a->size) == 0;
+}
+
+static void
+i965_wm_binding_init (struct i965_wm_binding *state,
+                     const uint32_t *table,
+                     int size)
+{
+    int n;
+
+    state->entry.hash = size;
+    state->size = size;
+
+    for (n = 0; n < size; n++) {
+       state->table[n] = table[n];
+       state->entry.hash ^= (table[n] << (8 * n)) |
+                            (table[n] >> (32 - (8*n)));
+    }
+}
+
+static uint32_t
+emit_binding_table (i965_device_t *device,
+                   i965_shader_t *shader)
+{
+    intel_bo_t *bo;
+    struct i965_wm_binding key, *cache;
+    uint32_t *table;
+    int n = 0;
+
+    table = i965_stream_alloc (&device->surface, 32, 5 * sizeof (uint32_t));
+    if (shader->target->stream != device->surface.serial) {
+       shader->target->stream = device->surface.serial;
+       shader->target->offset = emit_surface_state (device,
+                                                    TRUE,
+                                                    to_intel_bo (shader->target->intel.drm.bo),
+                                                    shader->target->intel.drm.format,
+                                                    shader->target->intel.drm.width,
+                                                    shader->target->intel.drm.height,
+                                                    shader->target->intel.drm.stride,
+                                                    BRW_SURFACE_2D);
+    }
+    table[n++] = shader->target->offset;
+
+    bo = shader->source.base.bo;
+    if (bo != NULL) {
+       if (bo->opaque0 != device->surface.serial) {
+           bo->opaque0 = device->surface.serial;
+           bo->opaque1 = emit_surface_state_for_shader (device, &shader->source);
+       }
+       table[n++] = bo->opaque1;
+    }
+
+    bo = shader->mask.base.bo;
+    if (bo != NULL) {
+       if (bo->opaque0 != device->surface.serial) {
+           bo->opaque0 = device->surface.serial;
+           bo->opaque1 = emit_surface_state_for_shader (device, &shader->mask);
+       }
+       table[n++] = bo->opaque1;
+    }
+
+    bo = shader->clip.base.bo;
+    if (bo != NULL) {
+       if (bo->opaque0 != device->surface.serial) {
+           bo->opaque0 = device->surface.serial;
+           bo->opaque1 = emit_surface_state_for_shader (device, &shader->clip);
+       }
+       table[n++] = bo->opaque1;
+    }
+
+    bo = shader->dst.base.bo;
+    if (bo != NULL) {
+       if (bo->opaque0 != device->surface.serial) {
+           bo->opaque0 = device->surface.serial;
+           bo->opaque1 = emit_surface_state_for_shader (device, &shader->dst);
+       }
+       table[n++] = bo->opaque1;
+    }
+
+    i965_wm_binding_init (&key, table, n);
+    key.offset = i965_stream_offsetof (&device->surface, table);
+
+    if (i965_wm_binding_equal (&key, &device->wm_binding)) {
+       device->surface.used = key.offset;
+       return device->wm_binding.offset;
+    }
+
+    cache = _cairo_hash_table_lookup (device->wm_bindings, &key.entry);
+    if (cache != NULL) {
+       device->surface.used = key.offset;
+       key.offset = cache->offset;
+    }
+
+    device->wm_binding = key;
+    return key.offset;
+}
+
+static void
+i965_emit_invariants (i965_device_t *device)
+{
+    OUT_BATCH (BRW_CS_URB_STATE | 0);
+    OUT_BATCH (((URB_CS_ENTRY_SIZE-1) << 4) | (URB_CS_ENTRIES << 0));
+}
+
+static void
+i965_emit_urb_fences (i965_device_t *device)
+{
+    int urb_vs_start, urb_vs_size;
+    int urb_gs_start, urb_gs_size;
+    int urb_clip_start, urb_clip_size;
+    int urb_sf_start, urb_sf_size;
+    int urb_cs_start, urb_cs_size;
+
+    if (device->have_urb_fences)
+       return;
+
+    /* URB fence */
+    urb_vs_start = 0;
+    urb_vs_size = URB_VS_ENTRIES * URB_VS_ENTRY_SIZE;
+    urb_gs_start = urb_vs_start + urb_vs_size;
+    urb_gs_size = URB_GS_ENTRIES * URB_GS_ENTRY_SIZE;
+    urb_clip_start = urb_gs_start + urb_gs_size;
+    urb_clip_size = URB_CLIP_ENTRIES * URB_CLIP_ENTRY_SIZE;
+    urb_sf_start = urb_clip_start + urb_clip_size;
+    urb_sf_size = URB_SF_ENTRIES * URB_SF_ENTRY_SIZE;
+    urb_cs_start = urb_sf_start + urb_sf_size;
+    urb_cs_size = URB_CS_ENTRIES * URB_CS_ENTRY_SIZE;
+
+    /* erratum: URB_FENCE must not cross a 64-byte cache-line */
+    while ((device->batch.used & 63) > 64-12)
+       OUT_BATCH (MI_NOOP);
+    OUT_BATCH (BRW_URB_FENCE |
+              UF0_CS_REALLOC |
+              UF0_SF_REALLOC |
+              UF0_CLIP_REALLOC |
+              UF0_GS_REALLOC |
+              UF0_VS_REALLOC |
+              1);
+    OUT_BATCH (((urb_clip_start + urb_clip_size) << UF1_CLIP_FENCE_SHIFT) |
+              ((urb_gs_start + urb_gs_size) << UF1_GS_FENCE_SHIFT) |
+              ((urb_vs_start + urb_vs_size) << UF1_VS_FENCE_SHIFT));
+    OUT_BATCH (((urb_cs_start + urb_cs_size) << UF2_CS_FENCE_SHIFT) |
+              ((urb_sf_start + urb_sf_size) << UF2_SF_FENCE_SHIFT));
+
+    device->have_urb_fences = TRUE;
+    device->constants_size = 0;
+}
+
+static void
+i965_emit_base (i965_device_t *device)
+{
+    OUT_BATCH (BRW_STATE_BASE_ADDRESS | 4);
+    if (likely (device->general.num_pending_relocations == 0)) {
+       i965_stream_add_pending_relocation (&device->general,
+                                           device->batch.used,
+                                           I915_GEM_DOMAIN_INSTRUCTION, 0,
+                                           BASE_ADDRESS_MODIFY);
+    }
+    OUT_BATCH (0); /* pending relocation */
+
+    if (likely (device->surface.num_pending_relocations == 0)) {
+       i965_stream_add_pending_relocation (&device->surface,
+                                           device->batch.used,
+                                           I915_GEM_DOMAIN_INSTRUCTION, 0,
+                                           BASE_ADDRESS_MODIFY);
+    }
+    OUT_BATCH (0); /* pending relocation */
+
+    OUT_BATCH (0 | BASE_ADDRESS_MODIFY);
+    /* general state max addr, disabled */
+    OUT_BATCH (0x10000000 | BASE_ADDRESS_MODIFY);
+    /* media object state max addr, disabled */
+    OUT_BATCH (0x10000000 | BASE_ADDRESS_MODIFY);
+}
+
+static void
+i965_emit_vertex_element (i965_device_t *device,
+                         i965_shader_t *shader)
+{
+    uint32_t offset;
+    uint32_t type;
+    int nelem;
+
+    type = 0;
+    nelem = 1;
+    if (shader->mask.type.vertex == VS_SPANS ||
+       shader->mask.type.vertex == VS_GLYPHS)
+    {
+       type = shader->mask.type.vertex;
+       nelem++;
+    }
+
+    if (type == device->vertex_type)
+       return;
+    device->vertex_type = type;
+
+    offset = 0;
+
+    OUT_BATCH (BRW_3DSTATE_VERTEX_ELEMENTS | ((2 * nelem) - 1));
+    OUT_BATCH ((0 << VE0_VERTEX_BUFFER_INDEX_SHIFT) |
+              VE0_VALID |
+              (BRW_SURFACEFORMAT_R32G32_FLOAT  << VE0_FORMAT_SHIFT) |
+              (offset                          << VE0_OFFSET_SHIFT));
+    OUT_BATCH ((BRW_VFCOMPONENT_STORE_SRC      << VE1_VFCOMPONENT_0_SHIFT) |
+              (BRW_VFCOMPONENT_STORE_SRC       << VE1_VFCOMPONENT_1_SHIFT) |
+              (BRW_VFCOMPONENT_STORE_0         << VE1_VFCOMPONENT_2_SHIFT) |
+              (BRW_VFCOMPONENT_STORE_1_FLT     << VE1_VFCOMPONENT_3_SHIFT) |
+              (4 << VE1_DESTINATION_ELEMENT_OFFSET_SHIFT));
+    offset += 8;
+
+    assert (shader->source.type.vertex == VS_NONE);
+    switch (shader->mask.type.vertex) {
+    default:
+    case VS_NONE:
+       break;
+
+    case VS_SPANS:
+       OUT_BATCH((0 << VE0_VERTEX_BUFFER_INDEX_SHIFT) |
+                 VE0_VALID |
+                 (BRW_SURFACEFORMAT_R32_FLOAT << VE0_FORMAT_SHIFT) |
+                 (offset                       << VE0_OFFSET_SHIFT));
+       OUT_BATCH((BRW_VFCOMPONENT_STORE_SRC    << VE1_VFCOMPONENT_0_SHIFT) |
+                 (BRW_VFCOMPONENT_NOSTORE      << VE1_VFCOMPONENT_1_SHIFT) |
+                 (BRW_VFCOMPONENT_NOSTORE      << VE1_VFCOMPONENT_2_SHIFT) |
+                 (BRW_VFCOMPONENT_NOSTORE      << VE1_VFCOMPONENT_3_SHIFT) |
+                 (8 << VE1_DESTINATION_ELEMENT_OFFSET_SHIFT));
+
+       offset += 4;
+       break;
+
+    case VS_GLYPHS:
+       OUT_BATCH((0 << VE0_VERTEX_BUFFER_INDEX_SHIFT) |
+                 VE0_VALID |
+                 (BRW_SURFACEFORMAT_R16G16_FLOAT << VE0_FORMAT_SHIFT) |
+                 (offset                       << VE0_OFFSET_SHIFT));
+       OUT_BATCH((BRW_VFCOMPONENT_STORE_SRC    << VE1_VFCOMPONENT_0_SHIFT) |
+                 (BRW_VFCOMPONENT_STORE_SRC    << VE1_VFCOMPONENT_1_SHIFT) |
+                 (BRW_VFCOMPONENT_NOSTORE      << VE1_VFCOMPONENT_2_SHIFT) |
+                 (BRW_VFCOMPONENT_NOSTORE      << VE1_VFCOMPONENT_3_SHIFT) |
+                 (8 << VE1_DESTINATION_ELEMENT_OFFSET_SHIFT));
+
+       offset += 4;
+       break;
+    }
+    assert (shader->clip.type.vertex == VS_NONE);
+    assert (shader->dst.type.vertex == VS_NONE);
+
+    device->vertex_size = offset;
+    i965_stream_align (&device->vertex, device->vertex_size);
+    device->vertex.committed = device->vertex.used;
+
+    device->rectangle_size = 3 * offset;
+}
+
+static cairo_bool_t
+i965_shader_needs_surface_update (const i965_shader_t *shader,
+                                 const i965_device_t *device)
+{
+    return device->target != shader->target || shader->target->stream == 0 ||
+       (shader->source.base.bo != NULL && device->source != shader->source.base.bo) ||
+       (shader->mask.base.bo != NULL && device->mask != shader->mask.base.bo) ||
+       (shader->clip.base.bo != NULL && device->clip != shader->clip.base.bo);
+}
+
+static cairo_bool_t
+i965_shader_needs_constants_update (const i965_shader_t *shader,
+                                   const i965_device_t *device)
+{
+    if (shader->constants_size == 0)
+       return FALSE;
+
+    if (device->constants_size != shader->constants_size)
+       return TRUE;
+
+    return memcmp (device->constants,
+                  shader->constants,
+                  sizeof (float) * shader->constants_size);
+}
+
+static cairo_bool_t
+i965_shader_needs_state_update (const i965_shader_t *shader,
+                               const i965_device_t *device)
+{
+    union {
+       struct i965_sf_state sf;
+       struct i965_wm_state wm;
+       struct i965_cc_state cc;
+    } state;
+
+    i965_sf_state_init (&state.sf, shader);
+    if (! i965_sf_state_equal (&state.sf, &device->sf_state))
+       return TRUE;
+
+    i965_wm_state_init (&state.wm, shader);
+    if (! i965_wm_state_equal (&state.wm, &device->wm_state))
+       return TRUE;
+
+    i965_cc_state_init (&state.cc, shader);
+    if (! i965_cc_state_equal (&state.cc, &device->cc_state))
+       return TRUE;
+
+    return FALSE;
+}
+
+static void
+i965_emit_composite (i965_device_t *device,
+                    i965_shader_t *shader)
+{
+    uint32_t draw_rectangle;
+
+    if (i965_shader_needs_surface_update (shader, device)) {
+       uint32_t offset;
+
+       offset = emit_binding_table (device, shader);
+
+       /* Only the PS uses the binding table */
+       OUT_BATCH (BRW_3DSTATE_BINDING_TABLE_POINTERS | 4);
+       OUT_BATCH (0); /* vs */
+       OUT_BATCH (0); /* gs */
+       OUT_BATCH (0); /* clip */
+       OUT_BATCH (0); /* sf */
+       OUT_BATCH (offset);
+
+       device->target = shader->target;
+       device->source = shader->source.base.bo;
+       device->mask = shader->mask.base.bo;
+       device->clip = shader->clip.base.bo;
+    }
+
+    /* The drawing rectangle clipping is always on.  Set it to values that
+     * shouldn't do any clipping.
+     */
+    draw_rectangle = DRAW_YMAX (shader->target->intel.drm.height) |
+                    DRAW_XMAX (shader->target->intel.drm.width);
+    if (draw_rectangle != device->draw_rectangle) {
+       OUT_BATCH (BRW_3DSTATE_DRAWING_RECTANGLE | 2);
+       OUT_BATCH (0x00000000); /* ymin, xmin */
+       OUT_BATCH (draw_rectangle);
+       OUT_BATCH (0x00000000); /* yorigin, xorigin */
+       device->draw_rectangle = draw_rectangle;
+    }
+
+    /* skip the depth buffer */
+    /* skip the polygon stipple */
+    /* skip the polygon stipple offset */
+    /* skip the line stipple */
+
+    /* Set the pointers to the 3d pipeline state */
+    if (i965_shader_needs_state_update (shader, device)) {
+       OUT_BATCH (BRW_3DSTATE_PIPELINED_POINTERS | 5);
+       OUT_BATCH (vs_unit_state_emit (device));
+       OUT_BATCH (BRW_GS_DISABLE);
+       OUT_BATCH (BRW_CLIP_DISABLE);
+       OUT_BATCH (gen4_create_sf_state (device, shader));
+       OUT_BATCH (gen4_create_wm_state (device, shader));
+       OUT_BATCH (cc_state_emit (device, shader));
+
+       /* Once the units are initialized, we need to setup the fences */
+       i965_emit_urb_fences (device);
+    }
+
+    if (i965_shader_needs_constants_update (shader, device)) {
+       uint32_t size = (sizeof (float) * shader->constants_size + 63) & -64;
+
+       /* XXX reuse clear/black/white
+        * ht!
+       */
+
+       /* XXX CONSTANT_BUFFER Address Offset Disable? INSTPM? */
+
+       assert (size <= 64 * URB_CS_ENTRY_SIZE);
+       assert (((sizeof (float) * shader->constants_size + 31) & -32) == 32 * i965_shader_const_urb_length (shader));
+
+       device->constants = i965_stream_alloc (&device->surface, 64, size);
+       memcpy (device->constants, shader->constants, size);
+       device->constants_size = shader->constants_size;
+
+       OUT_BATCH (BRW_CONSTANT_BUFFER | (1 << 8));
+       OUT_BATCH (i965_stream_offsetof (&device->surface, device->constants) + size / 64 - 1);
+    }
+
+    i965_emit_vertex_element (device, shader);
+}
+
+void
+i965_flush_vertices (i965_device_t *device)
+{
+    int vertex_count, vertex_start;
+
+    if (device->vertex.used == device->vertex.committed)
+       return;
+
+    assert (device->vertex.used > device->vertex.committed);
+
+    vertex_start = device->vertex.committed / device->vertex_size;
+    vertex_count =
+       (device->vertex.used - device->vertex.committed) / device->vertex_size;
+
+    assert (vertex_count);
+
+    if (device->vertex_size != device->last_vertex_size) {
+       i965_stream_add_pending_relocation (&device->vertex,
+                                           device->batch.used + 8,
+                                           I915_GEM_DOMAIN_VERTEX, 0,
+                                           0);
+
+       OUT_BATCH (BRW_3DSTATE_VERTEX_BUFFERS | 3);
+       OUT_BATCH ((0 << VB0_BUFFER_INDEX_SHIFT) |
+                  VB0_VERTEXDATA |
+                  (device->vertex_size << VB0_BUFFER_PITCH_SHIFT));
+       OUT_BATCH (0); /* pending relocation */
+       OUT_BATCH (0);
+       OUT_BATCH (0);
+       device->last_vertex_size = device->vertex_size;
+    }
+
+    OUT_BATCH (BRW_3DPRIMITIVE |
+              BRW_3DPRIMITIVE_VERTEX_SEQUENTIAL |
+              (_3DPRIM_RECTLIST << BRW_3DPRIMITIVE_TOPOLOGY_SHIFT) |
+              (0 << 9) |
+              4);
+    OUT_BATCH (vertex_count);  /* vertex count per instance */
+    OUT_BATCH (vertex_start);  /* start vertex offset */
+    OUT_BATCH (1); /* single instance */
+    OUT_BATCH (0);
+    OUT_BATCH (0);
+
+    device->vertex.committed = device->vertex.used;
+}
+
+void
+i965_finish_vertices (i965_device_t *device)
+{
+    cairo_status_t status;
+
+    i965_flush_vertices (device);
+
+    i965_stream_commit (device, &device->vertex);
+
+    if (! i965_shader_check_aperture (device->shader, device)) {
+       status = i965_device_flush (device);
+       if (unlikely (status))
+           longjmp (device->shader->unwind, status);
+
+       status = i965_shader_commit (device->shader, device);
+       assert (status == CAIRO_STATUS_SUCCESS);
+    }
+
+    device->last_vertex_size = 0;
+}
+
+static cairo_bool_t
+i965_shader_needs_update (const i965_shader_t *shader,
+                         const i965_device_t *device)
+{
+    if (i965_shader_needs_surface_update (shader, device))
+       return TRUE;
+
+    if (i965_shader_needs_constants_update (shader, device))
+       return TRUE;
+
+    return i965_shader_needs_state_update (shader, device);
+}
+
+static void
+i965_shader_reduce (i965_shader_t *shader,
+                   const i965_device_t *device)
+{
+    if (shader->op == CAIRO_OPERATOR_OVER &&
+       (i965_wm_kernel_hash (shader) & ~0xff) == 0 &&
+       (shader->source.base.content & CAIRO_CONTENT_ALPHA) == 0)
+    {
+       shader->op = CAIRO_OPERATOR_SOURCE;
+    }
+}
+
+cairo_status_t
+i965_shader_commit (i965_shader_t *shader,
+                   i965_device_t *device)
+{
+    cairo_status_t status;
+
+    if (! shader->committed) {
+       device->shader = shader;
+
+       status = i965_shader_setup_dst (shader);
+       if (unlikely (status))
+           return status;
+
+       i965_shader_setup_constants (shader);
+       i965_shader_reduce (shader, device);
+
+       if ((status = setjmp (shader->unwind)))
+           return status;
+
+       shader->committed = TRUE;
+    }
+
+    if (! i965_shader_needs_update (shader, device))
+       return CAIRO_STATUS_SUCCESS;
+
+    /* XXX too many guestimates about likely maximum sizes */
+recheck:
+    if (device->batch.used + 128 > device->batch.size ||
+       ! i965_shader_check_aperture (shader, device))
+    {
+       status = i965_device_flush (device);
+       if (unlikely (status))
+           longjmp (shader->unwind, status);
+    }
+
+    i965_flush_vertices (device);
+
+    if (unlikely (device->surface.used + 128 > device->surface.size ||
+                 device->surface.num_relocations + 4 > device->surface.max_relocations))
+    {
+       i965_stream_commit (device, &device->surface);
+       goto recheck;
+    }
+
+    if (unlikely (device->general.used + 512 > device->general.size)) {
+       i965_stream_commit (device, &device->general);
+       i965_general_state_reset (device);
+       goto recheck;
+    }
+
+    if (unlikely (device->batch.used == 0))
+       i965_emit_invariants (device);
+
+    if (unlikely (device->surface.num_pending_relocations == 0 ||
+                 device->general.num_pending_relocations == 0))
+    {
+       i965_emit_base (device);
+    }
+
+    i965_emit_composite (device, shader);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+void
+i965_clipped_vertices (i965_device_t *device,
+                      struct i965_vbo *vbo,
+                      cairo_region_t *clip_region)
+{
+    int i, num_rectangles, size;
+    cairo_status_t status;
+
+    if (vbo->count == 0)
+       return;
+
+    num_rectangles = cairo_region_num_rectangles (clip_region);
+    assert (num_rectangles);
+
+    if (vbo->next ||
+       vbo->count * device->vertex_size + device->vertex.used > device->vertex.size)
+    {
+       i965_finish_vertices (device);
+
+       size = device->rectangle_size;
+       do {
+           for (i = 0; i < num_rectangles; i++) {
+               cairo_rectangle_int_t rect;
+
+               cairo_region_get_rectangle (clip_region, i, &rect);
+
+               if (unlikely (device->vertex.used + size > device->vertex.size ||
+                             device->batch.used + 64 > device->batch.size ||
+                             ! i965_shader_check_aperture (device->shader, device)))
+               {
+                   status = i965_device_flush (device);
+                   if (unlikely (status))
+                       longjmp (device->shader->unwind, status);
+
+                   status = i965_shader_commit (device->shader, device);
+                   assert (status == CAIRO_STATUS_SUCCESS);
+               }
+
+               i965_emit_relocation (device, &device->batch,
+                                     vbo->bo, 0,
+                                     I915_GEM_DOMAIN_VERTEX, 0,
+                                     device->batch.used + 8);
+
+               OUT_BATCH (BRW_3DSTATE_VERTEX_BUFFERS | 3);
+               OUT_BATCH ((0 << VB0_BUFFER_INDEX_SHIFT) |
+                          VB0_VERTEXDATA |
+                          (device->vertex_size << VB0_BUFFER_PITCH_SHIFT));
+               OUT_BATCH (vbo->bo->offset);
+               OUT_BATCH (0);
+               OUT_BATCH (0);
+
+               /* XXX scissor? */
+               OUT_BATCH (BRW_3DSTATE_DRAWING_RECTANGLE | 2);
+               OUT_BATCH (DRAW_YMIN (rect.y) | DRAW_XMIN (rect.x));
+               OUT_BATCH (DRAW_YMAX (rect.y + rect.height) |
+                          DRAW_XMAX (rect.x + rect.width));
+               OUT_BATCH (0x00000000); /* yorigin, xorigin */
+
+               OUT_BATCH (BRW_3DPRIMITIVE |
+                          BRW_3DPRIMITIVE_VERTEX_SEQUENTIAL |
+                          (_3DPRIM_RECTLIST << BRW_3DPRIMITIVE_TOPOLOGY_SHIFT) |
+                          (0 << 9) |
+                          4);
+               OUT_BATCH (vbo->count);  /* vertex count per instance */
+               OUT_BATCH (0);  /* start vertex offset */
+               OUT_BATCH (1); /* single instance */
+               OUT_BATCH (0);
+               OUT_BATCH (0);
+           }
+       } while ((vbo = vbo->next) != NULL);
+       assert (device->last_vertex_size == 0);
+    } else {
+       int vertex_start, vertex_count;
+       void *ptr;
+
+       vertex_start = device->vertex.committed / device->vertex_size;
+       vertex_count = vbo->count;
+
+       size = vertex_count * device->vertex_size;
+       ptr = intel_bo_map (&device->intel, vbo->bo);
+       memcpy (device->vertex.data + device->vertex.used, ptr, size);
+       device->vertex.committed = device->vertex.used += size;
+
+       for (i = 0; i < num_rectangles; i++) {
+           cairo_rectangle_int_t rect;
+
+           cairo_region_get_rectangle (clip_region, i, &rect);
+
+           /* XXX scissor? */
+           OUT_BATCH (BRW_3DSTATE_DRAWING_RECTANGLE | 2);
+           OUT_BATCH (DRAW_YMIN (rect.y) | DRAW_XMIN (rect.x));
+           OUT_BATCH (DRAW_YMAX (rect.y + rect.height) |
+                      DRAW_XMAX (rect.x + rect.width));
+           OUT_BATCH (0x00000000);     /* yorigin, xorigin */
+
+           OUT_BATCH (BRW_3DPRIMITIVE |
+                      BRW_3DPRIMITIVE_VERTEX_SEQUENTIAL |
+                      (_3DPRIM_RECTLIST << BRW_3DPRIMITIVE_TOPOLOGY_SHIFT) |
+                      (0 << 9) |
+                      4);
+           OUT_BATCH (vertex_count);  /* vertex count per instance */
+           OUT_BATCH (vertex_start);  /* start vertex offset */
+           OUT_BATCH (1); /* single instance */
+           OUT_BATCH (0);
+           OUT_BATCH (0);
+       }
+    }
+
+    device->draw_rectangle = 0;
+}
diff --git a/src/drm/cairo-drm-i965-spans.c b/src/drm/cairo-drm-i965-spans.c
new file mode 100644 (file)
index 0000000..5cba7ce
--- /dev/null
@@ -0,0 +1,407 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-composite-rectangles-private.h"
+#include "cairo-boxes-private.h"
+#include "cairo-error-private.h"
+#include "cairo-drm-i965-private.h"
+
+/* Operates in either immediate or retained mode.
+ * When given a clip region we record the sequence of vbo and then
+ * replay them for each clip rectangle, otherwise we simply emit
+ * the vbo straight into the command stream.
+ */
+
+typedef struct _i965_spans i965_spans_t;
+
+typedef float *
+(*i965_get_rectangle_func_t) (i965_spans_t *spans);
+
+struct _i965_spans {
+    cairo_span_renderer_t renderer;
+
+    i965_device_t *device;
+
+    int xmin, xmax;
+    cairo_bool_t is_bounded;
+    const cairo_rectangle_int_t *extents;
+
+    i965_get_rectangle_func_t get_rectangle;
+    i965_shader_t shader;
+
+    cairo_region_t *clip_region;
+
+    struct i965_vbo head, *tail;
+
+    unsigned int vbo_offset;
+    float *vbo_base;
+};
+
+static float *
+i965_spans_emit_rectangle (i965_spans_t *spans)
+{
+    return i965_add_rectangle (spans->device);
+}
+
+static float *
+i965_spans_accumulate_rectangle (i965_spans_t *spans)
+{
+    float *vertices;
+    uint32_t size;
+
+    size = spans->device->rectangle_size;
+    if (unlikely (spans->vbo_offset + size > I965_VERTEX_SIZE)) {
+       struct i965_vbo *vbo;
+
+       vbo = malloc (sizeof (struct i965_vbo));
+       if (unlikely (vbo == NULL)) {
+           /* throw error! */
+       }
+
+       spans->tail->next = vbo;
+       spans->tail = vbo;
+
+       vbo->next = NULL;
+       vbo->bo = intel_bo_create (&spans->device->intel,
+                                  I965_VERTEX_SIZE, I965_VERTEX_SIZE,
+                                  FALSE, I915_TILING_NONE, 0);
+       vbo->count = 0;
+
+       spans->vbo_offset = 0;
+       spans->vbo_base = intel_bo_map (&spans->device->intel, vbo->bo);
+    }
+
+    vertices = spans->vbo_base + spans->vbo_offset;
+    spans->vbo_offset += size;
+    spans->tail->count += 3;
+
+    return vertices;
+}
+
+static void
+i965_span_rectangle (i965_spans_t *spans,
+                    int x0, int x1, int y0, int y1,
+                    int alpha)
+{
+    float *vertices;
+    float a = alpha / 255.;
+
+    vertices = spans->get_rectangle (spans);
+
+    *vertices++ = x1;
+    *vertices++ = y1;
+    *vertices++ = a;
+
+    *vertices++ = x0;
+    *vertices++ = y1;
+    *vertices++ = a;
+
+    *vertices++ = x0;
+    *vertices++ = y0;
+    *vertices++ = a;
+}
+
+static cairo_status_t
+i965_bounded_spans_mono (void *abstract_renderer,
+                        int y, int height,
+                        const cairo_half_open_span_t *half,
+                        unsigned num_spans)
+{
+    i965_spans_t *spans = abstract_renderer;
+
+    if (num_spans == 0)
+       return CAIRO_STATUS_SUCCESS;
+
+    do {
+       if (half[0].coverage >= 128) {
+           i965_span_rectangle (spans,
+                                half[0].x, half[1].x,
+                                y, y + height,
+                                255);
+       }
+       half++;
+    } while (--num_spans > 1);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+i965_bounded_spans (void *abstract_renderer,
+                   int y, int height,
+                   const cairo_half_open_span_t *half,
+                   unsigned num_spans)
+{
+    i965_spans_t *spans = abstract_renderer;
+
+    if (num_spans == 0)
+       return CAIRO_STATUS_SUCCESS;
+
+    do {
+       if (half[0].coverage) {
+           i965_span_rectangle (spans,
+                                half[0].x, half[1].x,
+                                y, y + height,
+                                half[0].coverage);
+       }
+       half++;
+    } while (--num_spans > 1);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+i965_unbounded_spans (void *abstract_renderer,
+                     int y, int height,
+                     const cairo_half_open_span_t *half,
+                     unsigned num_spans)
+{
+    i965_spans_t *spans = abstract_renderer;
+
+    if (num_spans == 0) {
+       i965_span_rectangle (spans,
+                            spans->xmin, spans->xmax,
+                            y, y + height,
+                            0);
+       return CAIRO_STATUS_SUCCESS;
+    }
+
+    if (half[0].x != spans->xmin) {
+       i965_span_rectangle (spans,
+                            spans->xmin, half[0].x,
+                            y, y + height,
+                            0);
+    }
+
+    do {
+       i965_span_rectangle (spans,
+                            half[0].x, half[1].x,
+                            y, y + height,
+                            half[0].coverage);
+       half++;
+    } while (--num_spans > 1);
+
+    if (half[0].x != spans->xmax) {
+       i965_span_rectangle (spans,
+                            half[0].x, spans->xmax,
+                            y, y + height,
+                            0);
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+i965_unbounded_spans_mono (void *abstract_renderer,
+                          int y, int height,
+                          const cairo_half_open_span_t *half,
+                          unsigned num_spans)
+{
+    i965_spans_t *spans = abstract_renderer;
+
+    if (num_spans == 0) {
+       i965_span_rectangle (spans,
+                            spans->xmin, spans->xmax,
+                            y, y + height,
+                            0);
+       return CAIRO_STATUS_SUCCESS;
+    }
+
+    if (half[0].x != spans->xmin) {
+       i965_span_rectangle (spans,
+                            spans->xmin, half[0].x,
+                            y, y + height,
+                            0);
+    }
+
+    do {
+       int alpha = 0;
+       if (half[0].coverage >= 128)
+           alpha = 255;
+       i965_span_rectangle (spans,
+                            half[0].x, half[1].x,
+                            y, y + height,
+                            alpha);
+       half++;
+    } while (--num_spans > 1);
+
+    if (half[0].x != spans->xmax) {
+       i965_span_rectangle (spans,
+                            half[0].x, spans->xmax,
+                            y, y + height,
+                            0);
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+i965_spans_init (i965_spans_t *spans,
+                i965_surface_t *dst,
+                cairo_operator_t op,
+                const cairo_pattern_t *pattern,
+                cairo_antialias_t antialias,
+                cairo_clip_t *clip,
+                const cairo_composite_rectangles_t *extents)
+{
+    cairo_status_t status;
+
+    spans->device = i965_device (dst);
+    i965_shader_init (&spans->shader, dst, op);
+
+    spans->is_bounded = extents->is_bounded;
+    if (extents->is_bounded) {
+       if (antialias == CAIRO_ANTIALIAS_NONE)
+           spans->renderer.render_rows = i965_bounded_spans_mono;
+       else
+           spans->renderer.render_rows = i965_bounded_spans;
+
+       spans->extents = &extents->bounded;
+    } else {
+       if (antialias == CAIRO_ANTIALIAS_NONE)
+           spans->renderer.render_rows = i965_unbounded_spans_mono;
+       else
+           spans->renderer.render_rows = i965_unbounded_spans;
+
+       spans->extents = &extents->unbounded;
+    }
+    spans->xmin = spans->extents->x;
+    spans->xmax = spans->extents->x + spans->extents->width;
+
+    spans->clip_region = NULL;
+    if (clip != NULL) {
+       cairo_region_t *clip_region = NULL;
+
+       status = _cairo_clip_get_region (clip, &clip_region);
+       assert (status == CAIRO_STATUS_SUCCESS || status == CAIRO_INT_STATUS_UNSUPPORTED);
+
+       if (clip_region != NULL && cairo_region_num_rectangles (clip_region) == 1)
+           clip_region = NULL;
+
+       spans->clip_region = clip_region;
+       if (status == CAIRO_INT_STATUS_UNSUPPORTED)
+           i965_shader_set_clip (&spans->shader, clip);
+    }
+
+    spans->head.next  = NULL;
+    spans->head.bo    = NULL;
+    spans->head.count = 0;
+    spans->tail = &spans->head;
+
+    if (spans->clip_region == NULL) {
+       spans->get_rectangle = i965_spans_emit_rectangle;
+    } else {
+       spans->get_rectangle = i965_spans_accumulate_rectangle;
+       spans->head.bo = intel_bo_create (&spans->device->intel,
+                                         I965_VERTEX_SIZE, I965_VERTEX_SIZE,
+                                         FALSE, I915_TILING_NONE, 0);
+       if (unlikely (spans->head.bo == NULL))
+           return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+       spans->vbo_base = intel_bo_map (&spans->device->intel, spans->head.bo);
+    }
+    spans->vbo_offset = 0;
+
+    return i965_shader_acquire_pattern (&spans->shader,
+                                       &spans->shader.source,
+                                       pattern, &extents->bounded);
+}
+
+static void
+i965_spans_fini (i965_spans_t *spans)
+{
+    i965_shader_fini (&spans->shader);
+
+    if (spans->head.bo != NULL) {
+       struct i965_vbo *vbo, *next;
+
+       intel_bo_destroy (&spans->device->intel, spans->head.bo);
+       for (vbo = spans->head.next; vbo != NULL; vbo = next) {
+           next = vbo->next;
+           intel_bo_destroy (&spans->device->intel, vbo->bo);
+           free (vbo);
+       }
+    }
+}
+
+cairo_status_t
+i965_clip_and_composite_spans (i965_surface_t          *dst,
+                              cairo_operator_t          op,
+                              const cairo_pattern_t    *pattern,
+                              cairo_antialias_t         antialias,
+                              i965_spans_func_t         draw_func,
+                              void                     *draw_closure,
+                              const cairo_composite_rectangles_t*extents,
+                              cairo_clip_t             *clip)
+{
+    i965_spans_t spans;
+    i965_device_t *device;
+    cairo_status_t status;
+
+    if (op == CAIRO_OPERATOR_CLEAR) {
+       pattern = &_cairo_pattern_white.base;
+       op = CAIRO_OPERATOR_DEST_OUT;
+    }
+
+    status = i965_spans_init (&spans, dst, op, pattern, antialias, clip, extents);
+    if (unlikely (status))
+       return status;
+
+    spans.shader.mask.base.content  = CAIRO_CONTENT_ALPHA;
+    spans.shader.mask.type.fragment = FS_SPANS;
+    spans.shader.mask.type.vertex   = VS_SPANS;
+    spans.shader.mask.type.pattern  = PATTERN_BASE;
+
+    status = cairo_device_acquire (dst->intel.drm.base.device);
+    if (unlikely (status))
+       goto CLEANUP_SPANS;
+
+    device = i965_device (dst);
+    status = i965_shader_commit (&spans.shader, device);
+    if (unlikely (status))
+       goto CLEANUP_DEVICE;
+
+    status = draw_func (draw_closure, &spans.renderer, spans.extents);
+    if (spans.clip_region != NULL && status == CAIRO_STATUS_SUCCESS)
+       i965_clipped_vertices (device, &spans.head, spans.clip_region);
+
+  CLEANUP_DEVICE:
+    cairo_device_release (dst->intel.drm.base.device);
+  CLEANUP_SPANS:
+    i965_spans_fini (&spans);
+
+    return status;
+}
diff --git a/src/drm/cairo-drm-i965-surface.c b/src/drm/cairo-drm-i965-surface.c
new file mode 100644 (file)
index 0000000..71061a0
--- /dev/null
@@ -0,0 +1,1922 @@
+/* Cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Kristian Høgsberg
+ * Copyright © 2009 Chris Wilson
+ * Copyright © 2009 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Kristian Høgsberg.
+ *
+ * Based on the xf86-intel-driver i965 render acceleration code,
+ * authored by:
+ *    Wang Zhenyu <zhenyu.z.wang@intel.com>
+ *    Eric Anholt <eric@anholt.net>
+ *    Carl Worth <cworth@redhat.com>
+ *    Keith Packard <keithp@keithp.com>
+ */
+
+/* XXX
+ *
+ * FIXME: Use brw_PLN for [DevCTG-B+]
+ *
+ */
+
+#include "cairoint.h"
+
+#include "cairo-drm-private.h"
+#include "cairo-drm-ioctl-private.h"
+#include "cairo-drm-intel-private.h"
+#include "cairo-drm-intel-command-private.h"
+#include "cairo-drm-intel-ioctl-private.h"
+#include "cairo-drm-i965-private.h"
+
+#include "cairo-boxes-private.h"
+#include "cairo-composite-rectangles-private.h"
+#include "cairo-default-context-private.h"
+#include "cairo-error-private.h"
+#include "cairo-region-private.h"
+#include "cairo-surface-offset-private.h"
+
+#include <sys/ioctl.h>
+#include <errno.h>
+
+#define I965_MAX_SIZE 8192
+
+static const cairo_surface_backend_t i965_surface_backend;
+
+static void
+i965_stream_init (i965_stream_t *stream,
+                 uint8_t *data, uint32_t size,
+                 struct i965_pending_relocation *pending, int max_pending,
+                 struct drm_i915_gem_relocation_entry *relocations, int max_relocations)
+
+{
+    stream->used = stream->committed = 0;
+    stream->data = data;
+    stream->size = size;
+    stream->serial = 1;
+
+    stream->num_pending_relocations = 0;
+    stream->max_pending_relocations = max_pending;
+    stream->pending_relocations = pending;
+
+    stream->num_relocations = 0;
+    stream->max_relocations = max_relocations;
+    stream->relocations = relocations;
+}
+
+static void
+i965_add_relocation (i965_device_t *device,
+                    intel_bo_t *bo,
+                    uint32_t read_domains,
+                    uint32_t write_domain)
+{
+    if (bo->exec == NULL) {
+       int i;
+
+       device->exec.gtt_size += bo->base.size;
+
+       i = device->exec.count++;
+       assert (i < ARRAY_LENGTH (device->exec.exec));
+
+       device->exec.exec[i].handle = bo->base.handle;
+       device->exec.exec[i].relocation_count = 0;
+       device->exec.exec[i].relocs_ptr = 0;
+       device->exec.exec[i].alignment  = 0;
+       device->exec.exec[i].offset = 0;
+       device->exec.exec[i].flags  = 0;
+       device->exec.exec[i].rsvd1  = 0;
+       device->exec.exec[i].rsvd2  = 0;
+
+       device->exec.bo[i] = intel_bo_reference (bo);
+       bo->exec = &device->exec.exec[i];
+    }
+
+    if (cairo_list_is_empty (&bo->link))
+       cairo_list_add_tail (&device->flush, &bo->link);
+
+    assert (write_domain == 0 || bo->batch_write_domain == 0 || bo->batch_write_domain == write_domain);
+    bo->batch_read_domains |= read_domains;
+    bo->batch_write_domain |= write_domain;
+}
+
+void
+i965_emit_relocation (i965_device_t *device,
+                     i965_stream_t *stream,
+                     intel_bo_t *target,
+                     uint32_t target_offset,
+                     uint32_t read_domains,
+                     uint32_t write_domain,
+                     uint32_t offset)
+{
+    int n;
+
+    assert (target_offset < target->base.size);
+
+    i965_add_relocation (device, target, read_domains, write_domain);
+
+    n = stream->num_relocations++;
+    assert (n < stream->max_relocations);
+
+    stream->relocations[n].offset = offset;
+    stream->relocations[n].delta  = target_offset;
+    stream->relocations[n].target_handle   = target->base.handle;
+    stream->relocations[n].read_domains    = read_domains;
+    stream->relocations[n].write_domain    = write_domain;
+    stream->relocations[n].presumed_offset = target->offset;
+}
+
+static void
+i965_stream_reset (i965_stream_t *stream)
+{
+    stream->used = stream->committed = 0;
+    stream->num_relocations = 0;
+    stream->num_pending_relocations = 0;
+    if (++stream->serial == 0)
+       stream->serial = 1;
+}
+
+void
+i965_stream_commit (i965_device_t *device,
+                   i965_stream_t *stream)
+{
+    intel_bo_t *bo;
+    int n;
+
+    assert (stream->used);
+
+    bo = intel_bo_create (&device->intel,
+                         stream->used, stream->used,
+                         FALSE, I915_TILING_NONE, 0);
+
+    /* apply pending relocations */
+    for (n = 0; n < stream->num_pending_relocations; n++) {
+       struct i965_pending_relocation *p = &stream->pending_relocations[n];
+
+       i965_emit_relocation (device, &device->batch, bo,
+                             p->delta,
+                             p->read_domains,
+                             p->write_domain,
+                             p->offset);
+       if (bo->offset)
+           *(uint32_t *) (device->batch.data + p->offset) = bo->offset + p->delta;
+    }
+
+    intel_bo_write (&device->intel, bo, 0, stream->used, stream->data);
+
+    if (stream->num_relocations) {
+       assert (bo->exec != NULL);
+       bo->exec->relocs_ptr = (uintptr_t) stream->relocations;
+       bo->exec->relocation_count = stream->num_relocations;
+    }
+
+    intel_bo_destroy (&device->intel, bo);
+
+    i965_stream_reset (stream);
+}
+
+static void
+sf_states_pluck (void *entry, void *closure)
+{
+    i965_device_t *device = closure;
+
+    _cairo_hash_table_remove (device->sf_states, entry);
+    _cairo_freelist_free (&device->sf_freelist, entry);
+}
+
+static void
+cc_offsets_pluck (void *entry, void *closure)
+{
+    i965_device_t *device = closure;
+
+    _cairo_hash_table_remove (device->cc_states, entry);
+    _cairo_freelist_free (&device->cc_freelist, entry);
+}
+
+static void
+wm_kernels_pluck (void *entry, void *closure)
+{
+    i965_device_t *device = closure;
+
+    _cairo_hash_table_remove (device->wm_kernels, entry);
+    _cairo_freelist_free (&device->wm_kernel_freelist, entry);
+}
+
+static void
+wm_states_pluck (void *entry, void *closure)
+{
+    i965_device_t *device = closure;
+
+    _cairo_hash_table_remove (device->wm_states, entry);
+    _cairo_freelist_free (&device->wm_state_freelist, entry);
+}
+
+static void
+wm_bindings_pluck (void *entry, void *closure)
+{
+    i965_device_t *device = closure;
+
+    _cairo_hash_table_remove (device->wm_bindings, entry);
+    _cairo_freelist_free (&device->wm_binding_freelist, entry);
+}
+
+static void
+samplers_pluck (void *entry, void *closure)
+{
+    i965_device_t *device = closure;
+
+    _cairo_hash_table_remove (device->samplers, entry);
+    _cairo_freelist_free (&device->sampler_freelist, entry);
+}
+
+void
+i965_general_state_reset (i965_device_t *device)
+{
+    _cairo_hash_table_foreach (device->sf_states,
+                              sf_states_pluck,
+                              device);
+
+    _cairo_hash_table_foreach (device->cc_states,
+                              cc_offsets_pluck,
+                              device);
+
+    _cairo_hash_table_foreach (device->wm_kernels,
+                              wm_kernels_pluck,
+                              device);
+
+    _cairo_hash_table_foreach (device->wm_states,
+                              wm_states_pluck,
+                              device);
+
+    _cairo_hash_table_foreach (device->wm_bindings,
+                              wm_bindings_pluck,
+                              device);
+
+    _cairo_hash_table_foreach (device->samplers,
+                              samplers_pluck,
+                              device);
+
+    device->vs_offset = (uint32_t) -1;
+    device->border_color_offset = (uint32_t) -1;
+
+    if (device->general_state != NULL) {
+       intel_bo_destroy (&device->intel, device->general_state);
+       device->general_state = NULL;
+    }
+}
+
+static void
+i965_device_reset (i965_device_t *device)
+{
+    device->exec.count = 0;
+    device->exec.gtt_size = I965_VERTEX_SIZE +
+                           I965_SURFACE_SIZE +
+                           I965_GENERAL_SIZE +
+                           I965_BATCH_SIZE;
+
+    device->sf_state.entry.hash = (uint32_t) -1;
+    device->wm_state.entry.hash = (uint32_t) -1;
+    device->wm_binding.entry.hash = (uint32_t) -1;
+    device->cc_state.entry.hash = (uint32_t) -1;
+
+    device->target = NULL;
+    device->source = NULL;
+    device->mask = NULL;
+    device->clip = NULL;
+
+    device->draw_rectangle = (uint32_t) -1;
+
+    device->vertex_type = (uint32_t) -1;
+    device->vertex_size = 0;
+    device->rectangle_size   = 0;
+    device->last_vertex_size = 0;
+
+    device->constants = NULL;
+    device->constants_size = 0;
+
+    device->have_urb_fences = FALSE;
+}
+
+static cairo_status_t
+i965_exec (i965_device_t *device, uint32_t offset)
+{
+    struct drm_i915_gem_execbuffer2 execbuf;
+    cairo_status_t status = CAIRO_STATUS_SUCCESS;
+    int ret, i;
+
+    execbuf.buffers_ptr = (uintptr_t) device->exec.exec;
+    execbuf.buffer_count = device->exec.count;
+    execbuf.batch_start_offset = offset;
+    execbuf.batch_len = device->batch.used;
+    execbuf.DR1 = 0;
+    execbuf.DR4 = 0;
+    execbuf.num_cliprects = 0;
+    execbuf.cliprects_ptr = 0;
+    execbuf.flags = I915_GEM_3D_PIPELINE;
+    execbuf.rsvd1 = 0;
+    execbuf.rsvd2 = 0;
+
+#if 0
+    printf ("exec: offset=%d, length=%d, buffers=%d\n",
+           offset, device->batch.used, device->exec.count);
+    intel_dump_batchbuffer ((uint32_t *) device->batch.data,
+                           device->batch.used,
+                           device->intel.base.chip_id);
+#endif
+
+    ret = 0;
+    do {
+       ret = ioctl (device->intel.base.fd, DRM_IOCTL_I915_GEM_EXECBUFFER2, &execbuf);
+    } while (ret != 0 && errno == EINTR);
+    if (unlikely (ret)) {
+       if (errno == ENOMEM)
+           status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+       else
+           status = _cairo_error (CAIRO_STATUS_DEVICE_ERROR);
+
+       fprintf (stderr, "Batch submission failed: %d\n", errno);
+       fprintf (stderr, "   gtt size: %zd/%zd\n",
+                device->exec.gtt_size, device->intel.gtt_avail_size);
+
+       fprintf (stderr, "   %d buffers:\n",
+                device->exec.count);
+       for (i = 0; i < device->exec.count; i++) {
+           fprintf (stderr, "     exec[%d] = %d\n",
+                    i, device->exec.bo[i]->base.size);
+       }
+
+       intel_dump_batchbuffer ((uint32_t *) device->batch.data,
+                               device->batch.used,
+                               device->intel.base.chip_id);
+    }
+
+    /* XXX any write target within the batch should now be in error */
+    for (i = 0; i < device->exec.count; i++) {
+       intel_bo_t *bo = device->exec.bo[i];
+       cairo_bool_t ret;
+
+       bo->offset = device->exec.exec[i].offset;
+       bo->exec = NULL;
+       bo->batch_read_domains = 0;
+       bo->batch_write_domain = 0;
+
+       if (bo->virtual)
+           intel_bo_unmap (bo);
+       bo->cpu = FALSE;
+
+       if (bo->purgeable)
+           ret = intel_bo_madvise (&device->intel, bo, I915_MADV_DONTNEED);
+           /* ignore immediate notification of purging */
+
+       cairo_list_del (&bo->cache_list);
+       cairo_list_init (&bo->link);
+       intel_bo_destroy (&device->intel, bo);
+    }
+    cairo_list_init (&device->flush);
+
+    device->exec.count = 0;
+
+    return status;
+}
+
+static inline uint32_t
+next_bo_size (uint32_t v)
+{
+    v = (v + 8191) / 8192;
+
+    v--;
+    v |= v >> 1;
+    v |= v >> 2;
+    v |= v >> 4;
+    v |= v >> 8;
+    v |= v >> 16;
+    v++;
+
+    return v * 8192;
+}
+
+static void
+_copy_to_bo_and_apply_relocations (i965_device_t *device,
+                                  intel_bo_t *bo,
+                                  i965_stream_t *stream,
+                                  uint32_t offset)
+{
+    int n;
+
+    intel_bo_write (&device->intel, bo,
+                   offset, stream->used,
+                   stream->data);
+
+    for (n = 0; n < stream->num_pending_relocations; n++) {
+       struct i965_pending_relocation *p = &stream->pending_relocations[n];
+
+       i965_emit_relocation (device, &device->batch, bo,
+                             p->delta + offset,
+                             p->read_domains,
+                             p->write_domain,
+                             p->offset);
+
+       if (bo->offset) {
+           *(uint32_t *) (device->batch.data + p->offset) =
+               bo->offset + p->delta + offset;
+       }
+    }
+}
+
+cairo_status_t
+i965_device_flush (i965_device_t *device)
+{
+    cairo_status_t status;
+    uint32_t aligned, max;
+    intel_bo_t *bo;
+    int n;
+
+    if (device->batch.used == 0)
+       return CAIRO_STATUS_SUCCESS;
+
+    i965_flush_vertices (device);
+
+    OUT_BATCH (MI_BATCH_BUFFER_END);
+    /* Emit a padding dword if we aren't going to be quad-word aligned. */
+    if (device->batch.used & 4)
+       OUT_BATCH (MI_NOOP);
+
+#if 0
+    printf ("device flush: vertex=%d, constant=%d, surface=%d, general=%d, batch=%d\n",
+           device->vertex.used,
+           device->constant.used,
+           device->surface.used,
+           device->general.used,
+           device->batch.used);
+#endif
+
+    /* can we pack the surface state into the tail of the general state? */
+    if (device->general.used == device->general.committed) {
+       if (device->general.used) {
+           assert (device->general.num_pending_relocations == 1);
+           assert (device->general_state != NULL);
+           i965_emit_relocation (device, &device->batch,
+                                 device->general_state,
+                                 device->general.pending_relocations[0].delta,
+                                 device->general.pending_relocations[0].read_domains,
+                                 device->general.pending_relocations[0].write_domain,
+                                 device->general.pending_relocations[0].offset);
+
+           if (device->general_state->offset) {
+               *(uint32_t *) (device->batch.data +
+                              device->general.pending_relocations[0].offset) =
+                   device->general_state->offset +
+                   device->general.pending_relocations[0].delta;
+           }
+       }
+    } else {
+       assert (device->general.num_pending_relocations == 1);
+       if (device->general_state != NULL) {
+           intel_bo_destroy (&device->intel, device->general_state);
+           device->general_state = NULL;
+       }
+
+       bo = intel_bo_create (&device->intel,
+                             device->general.used,
+                             device->general.used,
+                             FALSE, I915_TILING_NONE, 0);
+       if (unlikely (bo == NULL))
+           return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+       aligned = (device->general.used + 31) & -32;
+       if (device->surface.used &&
+           aligned + device->surface.used <= bo->base.size)
+       {
+           _copy_to_bo_and_apply_relocations (device, bo, &device->general, 0);
+           _copy_to_bo_and_apply_relocations (device, bo, &device->surface, aligned);
+
+           if (device->surface.num_relocations) {
+               for (n = 0; n < device->surface.num_relocations; n++)
+                   device->surface.relocations[n].offset += aligned;
+
+               assert (bo->exec != NULL);
+               bo->exec->relocs_ptr = (uintptr_t) device->surface.relocations;
+               bo->exec->relocation_count = device->surface.num_relocations;
+           }
+
+           i965_stream_reset (&device->surface);
+       }
+       else
+       {
+           _copy_to_bo_and_apply_relocations (device, bo, &device->general, 0);
+       }
+
+       /* Note we don't reset the general state, just mark what data we've committed. */
+       device->general.committed = device->general.used;
+       device->general_state = bo;
+    }
+    device->general.num_pending_relocations = 0;
+
+    /* Combine vertex+constant+surface+batch streams? */
+    max = aligned = device->vertex.used;
+    if (device->surface.used) {
+       aligned = (aligned + 63) & -64;
+       aligned += device->surface.used;
+       if (device->surface.used > max)
+           max = device->surface.used;
+    }
+    aligned = (aligned + 63) & -64;
+    aligned += device->batch.used;
+    if (device->batch.used > max)
+       max = device->batch.used;
+    if (aligned <= next_bo_size (max)) {
+       int batch_num_relocations;
+
+       if (aligned <= 8192)
+           max = aligned;
+
+       bo = intel_bo_create (&device->intel,
+                             max, max,
+                             FALSE, I915_TILING_NONE, 0);
+       if (unlikely (bo == NULL))
+           return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+       assert (aligned <= bo->base.size);
+
+       if (device->vertex.used)
+           _copy_to_bo_and_apply_relocations (device, bo, &device->vertex, 0);
+
+       aligned = device->vertex.used;
+
+       batch_num_relocations = device->batch.num_relocations;
+       if (device->surface.used) {
+           aligned = (aligned + 63) & -64;
+           _copy_to_bo_and_apply_relocations (device, bo, &device->surface, aligned);
+
+           batch_num_relocations = device->batch.num_relocations;
+           if (device->surface.num_relocations) {
+               assert (device->batch.num_relocations + device->surface.num_relocations < device->batch.max_relocations);
+
+               memcpy (device->batch.relocations + device->batch.num_relocations,
+                       device->surface.relocations,
+                       sizeof (device->surface.relocations[0]) * device->surface.num_relocations);
+
+               for (n = 0; n < device->surface.num_relocations; n++)
+                   device->batch.relocations[device->batch.num_relocations + n].offset += aligned;
+
+               device->batch.num_relocations += device->surface.num_relocations;
+           }
+
+           aligned += device->surface.used;
+       }
+
+       aligned = (aligned + 63) & -64;
+       intel_bo_write (&device->intel, bo,
+                       aligned, device->batch.used,
+                       device->batch.data);
+
+       for (n = 0; n < batch_num_relocations; n++)
+           device->batch.relocations[n].offset += aligned;
+
+       if (device->exec.bo[device->exec.count-1] == bo) {
+           assert (bo->exec == &device->exec.exec[device->exec.count-1]);
+
+           bo->exec->relocation_count = device->batch.num_relocations;
+           bo->exec->relocs_ptr = (uintptr_t) device->batch.relocations;
+           intel_bo_destroy (&device->intel, bo);
+       } else {
+           assert (bo->exec ==  NULL);
+
+           n = device->exec.count++;
+           device->exec.exec[n].handle = bo->base.handle;
+           device->exec.exec[n].relocation_count = device->batch.num_relocations;
+           device->exec.exec[n].relocs_ptr = (uintptr_t) device->batch.relocations;
+           device->exec.exec[n].alignment = 0;
+           device->exec.exec[n].offset = 0;
+           device->exec.exec[n].flags = 0;
+           device->exec.exec[n].rsvd1 = 0;
+           device->exec.exec[n].rsvd2 = 0;
+
+           /* transfer ownership to the exec */
+           device->exec.bo[n] = bo;
+       }
+    } else {
+       i965_stream_commit (device, &device->vertex);
+       if (device->surface.used)
+           i965_stream_commit (device, &device->surface);
+
+       bo = intel_bo_create (&device->intel,
+                             device->batch.used, device->batch.used,
+                             FALSE, I915_TILING_NONE, 0);
+       if (unlikely (bo == NULL))
+           return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+       intel_bo_write (&device->intel, bo,
+                       0, device->batch.used,
+                       device->batch.data);
+
+       n = device->exec.count++;
+       device->exec.exec[n].handle = bo->base.handle;
+       device->exec.exec[n].relocation_count = device->batch.num_relocations;
+       device->exec.exec[n].relocs_ptr = (uintptr_t) device->batch.relocations;
+       device->exec.exec[n].alignment = 0;
+       device->exec.exec[n].offset = 0;
+       device->exec.exec[n].flags = 0;
+       device->exec.exec[n].rsvd1 = 0;
+       device->exec.exec[n].rsvd2 = 0;
+
+       /* transfer ownership to the exec */
+       device->exec.bo[n] = bo;
+       aligned = 0;
+    }
+
+    status = i965_exec (device, aligned);
+
+    i965_stream_reset (&device->vertex);
+    i965_stream_reset (&device->surface);
+    i965_stream_reset (&device->batch);
+
+    intel_glyph_cache_unpin (&device->intel);
+    intel_snapshot_cache_thaw (&device->intel);
+
+    i965_device_reset (device);
+
+    return status;
+}
+
+static cairo_surface_t *
+i965_surface_create_similar (void *abstract_other,
+                            cairo_content_t content,
+                            int width, int height)
+{
+    i965_surface_t *other;
+    cairo_format_t format;
+
+    if (width > 8192 || height > 8192)
+       return NULL;
+
+    other = abstract_other;
+    if (content == other->intel.drm.base.content)
+       format = other->intel.drm.format;
+    else
+       format = _cairo_format_from_content (content);
+
+    return i965_surface_create_internal ((cairo_drm_device_t *) other->intel.drm.base.device,
+                                        format,
+                                        width, height,
+                                        I965_TILING_DEFAULT, TRUE);
+}
+
+static cairo_status_t
+i965_surface_finish (void *abstract_surface)
+{
+    i965_surface_t *surface = abstract_surface;
+
+    return intel_surface_finish (&surface->intel);
+}
+
+static cairo_status_t
+i965_surface_flush (void *abstract_surface)
+{
+    i965_surface_t *surface = abstract_surface;
+    cairo_status_t status = CAIRO_STATUS_SUCCESS;
+
+    if (surface->intel.drm.fallback != NULL)
+       return intel_surface_flush (abstract_surface);
+
+    /* Forgo flushing on finish as the user cannot access the surface directly. */
+    if (! surface->intel.drm.base.finished &&
+       to_intel_bo (surface->intel.drm.bo)->exec != NULL)
+    {
+       status = cairo_device_acquire (surface->intel.drm.base.device);
+       if (likely (status == CAIRO_STATUS_SUCCESS)) {
+           i965_device_t *device;
+
+           device = i965_device (surface);
+           status = i965_device_flush (device);
+           cairo_device_release (&device->intel.base.base);
+       }
+    }
+
+    return status;
+}
+
+/* rasterisation */
+
+static cairo_status_t
+_composite_boxes_spans (void                           *closure,
+                       cairo_span_renderer_t           *renderer,
+                       const cairo_rectangle_int_t     *extents)
+{
+    cairo_boxes_t *boxes = closure;
+    cairo_rectangular_scan_converter_t converter;
+    struct _cairo_boxes_chunk *chunk;
+    cairo_status_t status;
+
+    _cairo_rectangular_scan_converter_init (&converter, extents);
+    for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
+       cairo_box_t *box = chunk->base;
+       int i;
+
+       for (i = 0; i < chunk->count; i++) {
+           status = _cairo_rectangular_scan_converter_add_box (&converter, &box[i], 1);
+           if (unlikely (status))
+               goto CLEANUP;
+       }
+    }
+
+    status = converter.base.generate (&converter.base, renderer);
+
+  CLEANUP:
+    converter.base.destroy (&converter.base);
+    return status;
+}
+
+cairo_status_t
+i965_fixup_unbounded (i965_surface_t *dst,
+                     const cairo_composite_rectangles_t *extents,
+                     cairo_clip_t *clip)
+{
+    i965_shader_t shader;
+    i965_device_t *device;
+    cairo_status_t status;
+
+    i965_shader_init (&shader, dst, CAIRO_OPERATOR_CLEAR);
+
+    if (clip != NULL) {
+       cairo_region_t *clip_region = NULL;
+
+       status = _cairo_clip_get_region (clip, &clip_region);
+       assert (status == CAIRO_STATUS_SUCCESS || CAIRO_INT_STATUS_UNSUPPORTED);
+       assert (clip_region == NULL);
+
+       if (status == CAIRO_INT_STATUS_UNSUPPORTED)
+           i965_shader_set_clip (&shader, clip);
+    } else {
+       if (extents->bounded.width  == extents->unbounded.width &&
+           extents->bounded.height == extents->unbounded.height)
+       {
+           return CAIRO_STATUS_SUCCESS;
+       }
+    }
+
+    status = i965_shader_acquire_pattern (&shader,
+                                         &shader.source,
+                                         &_cairo_pattern_clear.base,
+                                         &extents->unbounded);
+    if (unlikely (status)) {
+       i965_shader_fini (&shader);
+       return status;
+    }
+
+    device = i965_device (dst);
+    status = cairo_device_acquire (&device->intel.base.base);
+    if (unlikely (status))
+       return status;
+
+    status = i965_shader_commit (&shader, device);
+    if (unlikely (status)) {
+       goto BAIL;
+    }
+
+    if (extents->bounded.width == 0 || extents->bounded.height == 0) {
+       i965_shader_add_rectangle (&shader,
+                                  extents->unbounded.x,
+                                  extents->unbounded.y,
+                                  extents->unbounded.width,
+                                  extents->unbounded.height);
+    } else { /* top */
+       if (extents->bounded.y != extents->unbounded.y) {
+           cairo_rectangle_int_t rect;
+
+           rect.x = extents->unbounded.x;
+           rect.y = extents->unbounded.y;
+           rect.width  = extents->unbounded.width;
+           rect.height = extents->bounded.y - rect.y;
+
+           i965_shader_add_rectangle (&shader,
+                                      rect.x, rect.y,
+                                      rect.width, rect.height);
+       }
+
+       /* left */
+       if (extents->bounded.x != extents->unbounded.x) {
+           cairo_rectangle_int_t rect;
+
+           rect.x = extents->unbounded.x;
+           rect.y = extents->bounded.y;
+           rect.width  = extents->bounded.x - extents->unbounded.x;
+           rect.height = extents->bounded.height;
+
+           i965_shader_add_rectangle (&shader,
+                                      rect.x, rect.y,
+                                      rect.width, rect.height);
+       }
+
+       /* right */
+       if (extents->bounded.x + extents->bounded.width != extents->unbounded.x + extents->unbounded.width) {
+           cairo_rectangle_int_t rect;
+
+           rect.x = extents->bounded.x + extents->bounded.width;
+           rect.y = extents->bounded.y;
+           rect.width  = extents->unbounded.x + extents->unbounded.width - rect.x;
+           rect.height = extents->bounded.height;
+
+           i965_shader_add_rectangle (&shader,
+                                      rect.x, rect.y,
+                                      rect.width, rect.height);
+       }
+
+       /* bottom */
+       if (extents->bounded.y + extents->bounded.height != extents->unbounded.y + extents->unbounded.height) {
+           cairo_rectangle_int_t rect;
+
+           rect.x = extents->unbounded.x;
+           rect.y = extents->bounded.y + extents->bounded.height;
+           rect.width  = extents->unbounded.width;
+           rect.height = extents->unbounded.y + extents->unbounded.height - rect.y;
+
+           i965_shader_add_rectangle (&shader,
+                                      rect.x, rect.y,
+                                      rect.width, rect.height);
+       }
+    }
+
+    i965_shader_fini (&shader);
+  BAIL:
+    cairo_device_release (&device->intel.base.base);
+    return status;
+}
+
+static cairo_status_t
+i965_fixup_unbounded_boxes (i965_surface_t *dst,
+                           const cairo_composite_rectangles_t *extents,
+                           cairo_clip_t *clip,
+                           cairo_boxes_t *boxes)
+{
+    cairo_boxes_t clear;
+    cairo_box_t box;
+    cairo_region_t *clip_region = NULL;
+    cairo_status_t status;
+    struct _cairo_boxes_chunk *chunk;
+    i965_shader_t shader;
+    int i;
+
+    if (boxes->num_boxes <= 1)
+       return i965_fixup_unbounded (dst, extents, clip);
+
+    i965_shader_init (&shader, dst, CAIRO_OPERATOR_CLEAR);
+    if (clip != NULL) {
+       status = _cairo_clip_get_region (clip, &clip_region);
+       assert (status == CAIRO_STATUS_SUCCESS || CAIRO_INT_STATUS_UNSUPPORTED);
+       if (status == CAIRO_INT_STATUS_UNSUPPORTED)
+           i965_shader_set_clip (&shader, clip);
+    }
+
+    status = i965_shader_acquire_pattern (&shader,
+                                         &shader.source,
+                                         &_cairo_pattern_clear.base,
+                                         &extents->unbounded);
+    if (unlikely (status)) {
+       i965_shader_fini (&shader);
+       return status;
+    }
+
+    _cairo_boxes_init (&clear);
+
+    box.p1.x = _cairo_fixed_from_int (extents->unbounded.x + extents->unbounded.width);
+    box.p1.y = _cairo_fixed_from_int (extents->unbounded.y);
+    box.p2.x = _cairo_fixed_from_int (extents->unbounded.x);
+    box.p2.y = _cairo_fixed_from_int (extents->unbounded.y + extents->unbounded.height);
+
+    if (clip_region == NULL) {
+       cairo_boxes_t tmp;
+
+       _cairo_boxes_init (&tmp);
+
+       status = _cairo_boxes_add (&tmp, &box);
+       assert (status == CAIRO_STATUS_SUCCESS);
+
+       tmp.chunks.next = &boxes->chunks;
+       tmp.num_boxes += boxes->num_boxes;
+
+       status = _cairo_bentley_ottmann_tessellate_boxes (&tmp,
+                                                         CAIRO_FILL_RULE_WINDING,
+                                                         &clear);
+
+       tmp.chunks.next = NULL;
+    } else {
+       pixman_box32_t *pbox;
+
+       pbox = pixman_region32_rectangles (&clip_region->rgn, &i);
+       _cairo_boxes_limit (&clear, (cairo_box_t *) pbox, i);
+
+       status = _cairo_boxes_add (&clear, &box);
+       assert (status == CAIRO_STATUS_SUCCESS);
+
+       for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
+           for (i = 0; i < chunk->count; i++) {
+               status = _cairo_boxes_add (&clear, &chunk->base[i]);
+               if (unlikely (status)) {
+                   _cairo_boxes_fini (&clear);
+                   return status;
+               }
+           }
+       }
+
+       status = _cairo_bentley_ottmann_tessellate_boxes (&clear,
+                                                         CAIRO_FILL_RULE_WINDING,
+                                                         &clear);
+    }
+
+    if (likely (status == CAIRO_STATUS_SUCCESS && clear.num_boxes)) {
+       i965_device_t *device;
+
+       device = i965_device (dst);
+       status = cairo_device_acquire (&device->intel.base.base);
+       if (unlikely (status))
+           goto err_shader;
+
+       status = i965_shader_commit (&shader, device);
+       if (unlikely (status))
+           goto err_device;
+
+       for (chunk = &clear.chunks; chunk != NULL; chunk = chunk->next) {
+           for (i = 0; i < chunk->count; i++) {
+               int x1 = _cairo_fixed_integer_part (chunk->base[i].p1.x);
+               int y1 = _cairo_fixed_integer_part (chunk->base[i].p1.y);
+               int x2 = _cairo_fixed_integer_part (chunk->base[i].p2.x);
+               int y2 = _cairo_fixed_integer_part (chunk->base[i].p2.y);
+
+               i965_shader_add_rectangle (&shader, x1, y1, x2 - x1, y2 - y1);
+           }
+       }
+
+err_device:
+       cairo_device_release (&device->intel.base.base);
+err_shader:
+       i965_shader_fini (&shader);
+    }
+
+    _cairo_boxes_fini (&clear);
+
+    return status;
+}
+
+static cairo_status_t
+_composite_boxes (i965_surface_t *dst,
+                 cairo_operator_t op,
+                 const cairo_pattern_t *pattern,
+                 cairo_boxes_t *boxes,
+                 cairo_antialias_t antialias,
+                 cairo_clip_t *clip,
+                 const cairo_composite_rectangles_t *extents)
+{
+    cairo_bool_t need_clip_surface = FALSE;
+    cairo_region_t *clip_region = NULL;
+    const struct _cairo_boxes_chunk *chunk;
+    cairo_status_t status;
+    i965_shader_t shader;
+    i965_device_t *device;
+    int i;
+
+    /* If the boxes are not pixel-aligned, we will need to compute a real mask */
+    if (antialias != CAIRO_ANTIALIAS_NONE) {
+       if (! boxes->is_pixel_aligned)
+           return CAIRO_INT_STATUS_UNSUPPORTED;
+    }
+
+    i965_shader_init (&shader, dst, op);
+
+    status = i965_shader_acquire_pattern (&shader,
+                                         &shader.source,
+                                         pattern,
+                                         &extents->bounded);
+    if (unlikely (status))
+       return status;
+
+    if (clip != NULL) {
+       status = _cairo_clip_get_region (clip, &clip_region);
+       assert (status == CAIRO_STATUS_SUCCESS || CAIRO_INT_STATUS_UNSUPPORTED);
+       need_clip_surface = status == CAIRO_INT_STATUS_UNSUPPORTED;
+       if (need_clip_surface)
+           i965_shader_set_clip (&shader, clip);
+    }
+
+    device = i965_device (dst);
+    status = cairo_device_acquire (&device->intel.base.base);
+    if (unlikely (status))
+       goto err_shader;
+
+    status = i965_shader_commit (&shader, i965_device (dst));
+    if (unlikely (status))
+       goto err_device;
+
+    for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
+       cairo_box_t *box = chunk->base;
+       for (i = 0; i < chunk->count; i++) {
+           int x1 = _cairo_fixed_integer_round (box[i].p1.x);
+           int y1 = _cairo_fixed_integer_round (box[i].p1.y);
+           int x2 = _cairo_fixed_integer_round (box[i].p2.x);
+           int y2 = _cairo_fixed_integer_round (box[i].p2.y);
+
+           if (x2 > x1 && y2 > y1)
+               i965_shader_add_rectangle (&shader, x1, y1, x2 - x1, y2 - y1);
+       }
+    }
+
+    if (! extents->is_bounded)
+       status = i965_fixup_unbounded_boxes (dst, extents, clip, boxes);
+
+  err_device:
+    cairo_device_release (&device->intel.base.base);
+  err_shader:
+    i965_shader_fini (&shader);
+
+    return status;
+}
+
+static cairo_status_t
+_clip_and_composite_boxes (i965_surface_t *dst,
+                          cairo_operator_t op,
+                          const cairo_pattern_t *src,
+                          cairo_boxes_t *boxes,
+                          cairo_antialias_t antialias,
+                          const cairo_composite_rectangles_t *extents,
+                          cairo_clip_t *clip)
+{
+    cairo_status_t status;
+
+    if (boxes->num_boxes == 0) {
+       if (extents->is_bounded)
+           return CAIRO_STATUS_SUCCESS;
+
+       return i965_fixup_unbounded (dst, extents, clip);
+    }
+
+    /* Use a fast path if the boxes are pixel aligned */
+    status = _composite_boxes (dst, op, src, boxes, antialias, clip, extents);
+    if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+       return status;
+
+    /* Otherwise render the boxes via an implicit mask and composite in the usual
+     * fashion.
+     */
+    return i965_clip_and_composite_spans (dst, op, src, antialias,
+                                         _composite_boxes_spans, boxes,
+                                         extents, clip);
+}
+
+static cairo_int_status_t
+i965_surface_paint (void                       *abstract_dst,
+                   cairo_operator_t             op,
+                   const cairo_pattern_t       *source,
+                   cairo_clip_t                *clip)
+{
+    i965_surface_t *dst = abstract_dst;
+    cairo_composite_rectangles_t extents;
+    cairo_boxes_t boxes;
+    cairo_box_t *clip_boxes = boxes.boxes_embedded;
+    cairo_clip_t local_clip;
+    cairo_bool_t have_clip = FALSE;
+    int num_boxes = ARRAY_LENGTH (boxes.boxes_embedded);
+    cairo_status_t status;
+
+    /* XXX unsupported operators? use pixel shader blending, eventually */
+
+    status = _cairo_composite_rectangles_init_for_paint (&extents,
+                                                        dst->intel.drm.width,
+                                                        dst->intel.drm.height,
+                                                        op, source,
+                                                        clip);
+    if (unlikely (status))
+       return status;
+
+    if (clip != NULL && _cairo_clip_contains_extents (clip, &extents))
+       clip = NULL;
+
+    if (clip != NULL) {
+       clip = _cairo_clip_init_copy (&local_clip, clip);
+       have_clip = TRUE;
+    }
+
+    status = _cairo_clip_to_boxes (&clip, &extents, &clip_boxes, &num_boxes);
+    if (unlikely (status)) {
+       if (have_clip)
+           _cairo_clip_fini (&local_clip);
+
+       return status;
+    }
+
+    _cairo_boxes_init_for_array (&boxes, clip_boxes, num_boxes);
+    status = _clip_and_composite_boxes (dst, op, source,
+                                       &boxes, CAIRO_ANTIALIAS_DEFAULT,
+                                       &extents, clip);
+    if (clip_boxes != boxes.boxes_embedded)
+       free (clip_boxes);
+
+    if (have_clip)
+       _cairo_clip_fini (&local_clip);
+
+    return status;
+}
+
+static cairo_int_status_t
+i965_surface_mask (void                                *abstract_dst,
+                  cairo_operator_t              op,
+                  const cairo_pattern_t        *source,
+                  const cairo_pattern_t        *mask,
+                  cairo_clip_t                 *clip)
+{
+    i965_surface_t *dst = abstract_dst;
+    cairo_composite_rectangles_t extents;
+    i965_shader_t shader;
+    i965_device_t *device;
+    cairo_clip_t local_clip;
+    cairo_region_t *clip_region = NULL;
+    cairo_bool_t need_clip_surface = FALSE;
+    cairo_bool_t have_clip = FALSE;
+    cairo_status_t status;
+
+    status = _cairo_composite_rectangles_init_for_mask (&extents,
+                                                       dst->intel.drm.width,
+                                                       dst->intel.drm.height,
+                                                       op, source, mask, clip);
+    if (unlikely (status))
+       return status;
+
+    if (clip != NULL && _cairo_clip_contains_extents (clip, &extents))
+       clip = NULL;
+
+    if (clip != NULL && extents.is_bounded) {
+       clip = _cairo_clip_init_copy (&local_clip, clip);
+       status = _cairo_clip_rectangle (clip, &extents.bounded);
+       if (unlikely (status)) {
+           _cairo_clip_fini (&local_clip);
+           return status;
+       }
+
+       have_clip = TRUE;
+    }
+
+    i965_shader_init (&shader, dst, op);
+
+    status = i965_shader_acquire_pattern (&shader,
+                                         &shader.source,
+                                         source,
+                                         &extents.bounded);
+    if (unlikely (status))
+       goto err_shader;
+
+    status = i965_shader_acquire_pattern (&shader,
+                                         &shader.mask,
+                                         mask,
+                                         &extents.bounded);
+    if (unlikely (status))
+       goto err_shader;
+
+    if (clip != NULL) {
+       status = _cairo_clip_get_region (clip, &clip_region);
+       assert (status == CAIRO_STATUS_SUCCESS || CAIRO_INT_STATUS_UNSUPPORTED);
+       need_clip_surface = status == CAIRO_INT_STATUS_UNSUPPORTED;
+       if (need_clip_surface)
+           i965_shader_set_clip (&shader, clip);
+    }
+
+    device = i965_device (dst);
+    status = cairo_device_acquire (&device->intel.base.base);
+    if (unlikely (status))
+       goto err_shader;
+
+    status = i965_shader_commit (&shader, device);
+    if (unlikely (status))
+       goto err_device;
+
+    if (clip_region != NULL) {
+       unsigned int n, num_rectangles;
+
+       num_rectangles = cairo_region_num_rectangles (clip_region);
+       for (n = 0; n < num_rectangles; n++) {
+           cairo_rectangle_int_t rect;
+
+           cairo_region_get_rectangle (clip_region, n, &rect);
+
+           i965_shader_add_rectangle (&shader,
+                                      rect.x, rect.y,
+                                      rect.width, rect.height);
+       }
+    } else {
+       i965_shader_add_rectangle (&shader,
+                                  extents.bounded.x,
+                                  extents.bounded.y,
+                                  extents.bounded.width,
+                                  extents.bounded.height);
+    }
+
+    if (! extents.is_bounded)
+       status = i965_fixup_unbounded (dst, &extents, clip);
+
+  err_device:
+    cairo_device_release (&device->intel.base.base);
+  err_shader:
+    i965_shader_fini (&shader);
+    if (have_clip)
+       _cairo_clip_fini (&local_clip);
+
+    return status;
+}
+
+typedef struct {
+    cairo_polygon_t            polygon;
+    cairo_fill_rule_t           fill_rule;
+    cairo_antialias_t           antialias;
+} composite_polygon_info_t;
+
+static cairo_status_t
+_composite_polygon_spans (void                          *closure,
+                         cairo_span_renderer_t         *renderer,
+                         const cairo_rectangle_int_t   *extents)
+{
+    composite_polygon_info_t *info = closure;
+    cairo_botor_scan_converter_t converter;
+    cairo_status_t status;
+    cairo_box_t box;
+
+    box.p1.x = _cairo_fixed_from_int (extents->x);
+    box.p1.y = _cairo_fixed_from_int (extents->y);
+    box.p2.x = _cairo_fixed_from_int (extents->x + extents->width);
+    box.p2.y = _cairo_fixed_from_int (extents->y + extents->height);
+
+    _cairo_botor_scan_converter_init (&converter, &box, info->fill_rule);
+
+    status = converter.base.add_polygon (&converter.base, &info->polygon);
+    if (likely (status == CAIRO_STATUS_SUCCESS))
+       status = converter.base.generate (&converter.base, renderer);
+
+    converter.base.destroy (&converter.base);
+
+    return status;
+}
+
+static cairo_int_status_t
+i965_surface_stroke (void                      *abstract_dst,
+                    cairo_operator_t            op,
+                    const cairo_pattern_t      *source,
+                    cairo_path_fixed_t         *path,
+                    const cairo_stroke_style_t *stroke_style,
+                    const cairo_matrix_t       *ctm,
+                    const cairo_matrix_t       *ctm_inverse,
+                    double                      tolerance,
+                    cairo_antialias_t           antialias,
+                    cairo_clip_t               *clip)
+{
+    i965_surface_t *dst = abstract_dst;
+    cairo_composite_rectangles_t extents;
+    composite_polygon_info_t info;
+    cairo_box_t boxes_stack[32], *clip_boxes = boxes_stack;
+    int num_boxes = ARRAY_LENGTH (boxes_stack);
+    cairo_clip_t local_clip;
+    cairo_bool_t have_clip = FALSE;
+    cairo_status_t status;
+
+    status = _cairo_composite_rectangles_init_for_stroke (&extents,
+                                                         dst->intel.drm.width,
+                                                         dst->intel.drm.height,
+                                                         op, source,
+                                                         path, stroke_style, ctm,
+                                                         clip);
+    if (unlikely (status))
+       return status;
+
+    if (clip != NULL && _cairo_clip_contains_extents (clip, &extents))
+       clip = NULL;
+
+    if (clip != NULL) {
+       clip = _cairo_clip_init_copy (&local_clip, clip);
+       have_clip = TRUE;
+    }
+
+    status = _cairo_clip_to_boxes (&clip, &extents, &clip_boxes, &num_boxes);
+    if (unlikely (status)) {
+       if (have_clip)
+           _cairo_clip_fini (&local_clip);
+
+       return status;
+    }
+
+    if (_cairo_path_fixed_stroke_is_rectilinear (path)) {
+       cairo_boxes_t boxes;
+
+       _cairo_boxes_init (&boxes);
+       _cairo_boxes_limit (&boxes, clip_boxes, num_boxes);
+       status = _cairo_path_fixed_stroke_rectilinear_to_boxes (path,
+                                                               stroke_style,
+                                                               ctm,
+                                                               &boxes);
+       if (likely (status == CAIRO_STATUS_SUCCESS)) {
+           status = _clip_and_composite_boxes (dst, op, source,
+                                               &boxes, antialias,
+                                               &extents, clip);
+       }
+
+       _cairo_boxes_fini (&boxes);
+
+       if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+           goto CLEANUP_BOXES;
+    }
+
+    _cairo_polygon_init (&info.polygon, clip_boxes, num_boxes);
+
+    status = _cairo_path_fixed_stroke_to_polygon (path,
+                                                 stroke_style,
+                                                 ctm, ctm_inverse,
+                                                 tolerance,
+                                                 &info.polygon);
+    if (unlikely (status))
+       goto CLEANUP_POLYGON;
+
+    if (extents.is_bounded) {
+       cairo_rectangle_int_t rect;
+
+       _cairo_box_round_to_rectangle (&info.polygon.extents, &rect);
+       if (! _cairo_rectangle_intersect (&extents.bounded, &rect))
+           goto CLEANUP_POLYGON;
+    }
+
+    if (info.polygon.num_edges == 0) {
+       if (! extents.is_bounded)
+           status = i965_fixup_unbounded (dst, &extents, clip);
+    } else {
+       info.fill_rule = CAIRO_FILL_RULE_WINDING;
+       info.antialias = antialias;
+       status = i965_clip_and_composite_spans (dst, op, source, antialias,
+                                               _composite_polygon_spans, &info,
+                                               &extents, clip);
+    }
+
+CLEANUP_POLYGON:
+    _cairo_polygon_fini (&info.polygon);
+
+CLEANUP_BOXES:
+    if (clip_boxes != boxes_stack)
+       free (clip_boxes);
+
+    if (have_clip)
+       _cairo_clip_fini (&local_clip);
+
+    return status;
+}
+
+static cairo_int_status_t
+i965_surface_fill (void                        *abstract_dst,
+                  cairo_operator_t      op,
+                  const cairo_pattern_t*source,
+                  cairo_path_fixed_t   *path,
+                  cairo_fill_rule_t     fill_rule,
+                  double                tolerance,
+                  cairo_antialias_t     antialias,
+                  cairo_clip_t         *clip)
+{
+    i965_surface_t *dst = abstract_dst;
+    cairo_composite_rectangles_t extents;
+    composite_polygon_info_t info;
+    cairo_box_t boxes_stack[32], *clip_boxes = boxes_stack;
+    cairo_clip_t local_clip;
+    cairo_bool_t have_clip = FALSE;
+    int num_boxes = ARRAY_LENGTH (boxes_stack);
+    cairo_status_t status;
+
+    status = _cairo_composite_rectangles_init_for_fill (&extents,
+                                                       dst->intel.drm.width,
+                                                       dst->intel.drm.height,
+                                                       op, source, path,
+                                                       clip);
+    if (unlikely (status))
+       return status;
+
+    if (clip != NULL && _cairo_clip_contains_extents (clip, &extents))
+       clip = NULL;
+
+    if (clip != NULL) {
+       clip = _cairo_clip_init_copy (&local_clip, clip);
+       have_clip = TRUE;
+    }
+
+    status = _cairo_clip_to_boxes (&clip, &extents, &clip_boxes, &num_boxes);
+    if (unlikely (status)) {
+       if (have_clip)
+           _cairo_clip_fini (&local_clip);
+
+       return status;
+    }
+
+    assert (! _cairo_path_fixed_fill_is_empty (path));
+
+    if (_cairo_path_fixed_fill_is_rectilinear (path)) {
+       cairo_boxes_t boxes;
+
+       _cairo_boxes_init (&boxes);
+       _cairo_boxes_limit (&boxes, clip_boxes, num_boxes);
+       status = _cairo_path_fixed_fill_rectilinear_to_boxes (path,
+                                                             fill_rule,
+                                                             &boxes);
+       if (likely (status == CAIRO_STATUS_SUCCESS)) {
+           status = _clip_and_composite_boxes (dst, op, source,
+                                               &boxes, antialias,
+                                               &extents, clip);
+       }
+
+       _cairo_boxes_fini (&boxes);
+
+       if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+           goto CLEANUP_BOXES;
+    }
+
+    _cairo_polygon_init (&info.polygon, clip_boxes, num_boxes);
+
+    status = _cairo_path_fixed_fill_to_polygon (path, tolerance, &info.polygon);
+    if (unlikely (status))
+       goto CLEANUP_POLYGON;
+
+    if (extents.is_bounded) {
+       cairo_rectangle_int_t rect;
+
+       _cairo_box_round_to_rectangle (&info.polygon.extents, &rect);
+       if (! _cairo_rectangle_intersect (&extents.bounded, &rect))
+           goto CLEANUP_POLYGON;
+    }
+
+    if (info.polygon.num_edges == 0) {
+       if (! extents.is_bounded)
+           status = i965_fixup_unbounded (dst, &extents, clip);
+    } else {
+       info.fill_rule = fill_rule;
+       info.antialias = antialias;
+       status = i965_clip_and_composite_spans (dst, op, source, antialias,
+                                               _composite_polygon_spans, &info,
+                                               &extents, clip);
+    }
+
+CLEANUP_POLYGON:
+    _cairo_polygon_fini (&info.polygon);
+
+CLEANUP_BOXES:
+    if (clip_boxes != boxes_stack)
+       free (clip_boxes);
+
+    if (have_clip)
+       _cairo_clip_fini (&local_clip);
+
+    return status;
+}
+
+static const cairo_surface_backend_t i965_surface_backend = {
+    CAIRO_SURFACE_TYPE_DRM,
+    _cairo_default_context_create,
+
+    i965_surface_create_similar,
+    i965_surface_finish,
+    intel_surface_acquire_source_image,
+    intel_surface_release_source_image,
+
+    NULL, NULL, NULL,
+    NULL, /* composite */
+    NULL, /* fill */
+    NULL, /* trapezoids */
+    NULL, /* span */
+    NULL, /* check-span */
+
+    NULL, /* copy_page */
+    NULL, /* show_page */
+    _cairo_drm_surface_get_extents,
+    NULL, /* old-glyphs */
+    _cairo_drm_surface_get_font_options,
+
+    i965_surface_flush,
+    NULL, /* mark_dirty */
+    intel_scaled_font_fini,
+    intel_scaled_glyph_fini,
+
+    i965_surface_paint,
+    i965_surface_mask,
+    i965_surface_stroke,
+    i965_surface_fill,
+    i965_surface_glyphs,
+};
+
+static void
+i965_surface_init (i965_surface_t *surface,
+                  cairo_drm_device_t *device,
+                  cairo_format_t format,
+                  int width, int height)
+{
+    intel_surface_init (&surface->intel, &i965_surface_backend, device,
+                       format, width, height);
+    surface->stream = 0;
+}
+
+static inline int cairo_const
+i965_tiling_stride (uint32_t tiling, int stride)
+{
+    if (tiling == I915_TILING_NONE)
+       return stride;
+
+    return (stride + 127) & -128;
+}
+
+static inline int cairo_const
+i965_tiling_height (uint32_t tiling, int height)
+{
+    switch (tiling) {
+    default:
+    case I915_TILING_NONE: return (height + 1) & -2;
+    case I915_TILING_X: return (height + 7) & -8;
+    case I915_TILING_Y: return (height + 31) & -32;
+    }
+}
+
+cairo_surface_t *
+i965_surface_create_internal (cairo_drm_device_t *base_dev,
+                             cairo_format_t format,
+                             int width, int height,
+                             uint32_t tiling,
+                             cairo_bool_t gpu_target)
+{
+    i965_surface_t *surface;
+    cairo_status_t status_ignored;
+
+    surface = malloc (sizeof (i965_surface_t));
+    if (unlikely (surface == NULL))
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+    i965_surface_init (surface, base_dev, format, width, height);
+
+    if (width && height) {
+       uint32_t size, stride;
+       intel_bo_t *bo;
+
+       width = (width + 3) & -4;
+       stride = cairo_format_stride_for_width (surface->intel.drm.format, width);
+       stride = (stride + 63) & ~63;
+       stride = i965_tiling_stride (tiling, stride);
+       surface->intel.drm.stride = stride;
+
+       height = i965_tiling_height (tiling, height);
+       assert (height <= I965_MAX_SIZE);
+
+       size = stride * height;
+       bo = intel_bo_create (to_intel_device (&base_dev->base),
+                             size, size,
+                             gpu_target, tiling, stride);
+       if (bo == NULL) {
+           status_ignored = _cairo_drm_surface_finish (&surface->intel.drm);
+           free (surface);
+           return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+       }
+
+       bo->tiling = tiling;
+       bo->stride = stride;
+       surface->intel.drm.bo = &bo->base;
+
+       assert (bo->base.size >= (size_t) stride*height);
+    }
+
+    return &surface->intel.drm.base;
+}
+
+static cairo_surface_t *
+i965_surface_create (cairo_drm_device_t *device,
+                    cairo_format_t format, int width, int height)
+{
+    switch (format) {
+    case CAIRO_FORMAT_ARGB32:
+    case CAIRO_FORMAT_RGB16_565:
+    case CAIRO_FORMAT_RGB24:
+    case CAIRO_FORMAT_A8:
+       break;
+    case CAIRO_FORMAT_INVALID:
+    default:
+    case CAIRO_FORMAT_A1:
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
+    }
+
+    return i965_surface_create_internal (device, format, width, height,
+                                        I965_TILING_DEFAULT, TRUE);
+}
+
+static cairo_surface_t *
+i965_surface_create_for_name (cairo_drm_device_t *base_dev,
+                             unsigned int name,
+                             cairo_format_t format,
+                             int width, int height, int stride)
+{
+    i965_device_t *device;
+    i965_surface_t *surface;
+    cairo_status_t status_ignored;
+    int min_stride;
+
+    min_stride = cairo_format_stride_for_width (format, (width + 3) & -4);
+    if (stride < min_stride || stride & 63)
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_STRIDE));
+
+    if (format == CAIRO_FORMAT_A1)
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
+
+    switch (format) {
+    case CAIRO_FORMAT_ARGB32:
+    case CAIRO_FORMAT_RGB16_565:
+    case CAIRO_FORMAT_RGB24:
+    case CAIRO_FORMAT_A8:
+       break;
+    case CAIRO_FORMAT_INVALID:
+    default:
+    case CAIRO_FORMAT_A1:
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
+    }
+
+    surface = malloc (sizeof (i965_surface_t));
+    if (unlikely (surface == NULL))
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+    i965_surface_init (surface, base_dev, format, width, height);
+
+    device = (i965_device_t *) base_dev;
+    surface->intel.drm.bo = &intel_bo_create_for_name (&device->intel, name)->base;
+    if (unlikely (surface->intel.drm.bo == NULL)) {
+       status_ignored = _cairo_drm_surface_finish (&surface->intel.drm);
+       free (surface);
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+    }
+
+    surface->intel.drm.stride = stride;
+
+    return &surface->intel.drm.base;
+}
+
+static cairo_status_t
+i965_surface_enable_scan_out (void *abstract_surface)
+{
+    i965_surface_t *surface = abstract_surface;
+    intel_bo_t *bo;
+
+    if (unlikely (surface->intel.drm.bo == NULL))
+       return _cairo_error (CAIRO_STATUS_INVALID_SIZE);
+
+    bo = to_intel_bo (surface->intel.drm.bo);
+    if (bo->tiling != I915_TILING_X) {
+       i965_device_t *device = i965_device (surface);
+       cairo_surface_pattern_t pattern;
+       cairo_surface_t *clone;
+       cairo_status_t status;
+
+       clone = i965_surface_create_internal (&device->intel.base,
+                                             surface->intel.drm.base.content,
+                                             surface->intel.drm.width,
+                                             surface->intel.drm.height,
+                                             I915_TILING_X,
+                                             TRUE);
+       if (unlikely (clone->status))
+           return clone->status;
+
+       /* 2D blit? */
+       _cairo_pattern_init_for_surface (&pattern, &surface->intel.drm.base);
+       pattern.base.filter = CAIRO_FILTER_NEAREST;
+
+       status = _cairo_surface_paint (clone,
+                                      CAIRO_OPERATOR_SOURCE,
+                                      &pattern.base,
+                                      NULL);
+
+       _cairo_pattern_fini (&pattern.base);
+
+       if (unlikely (status)) {
+           cairo_surface_destroy (clone);
+           return status;
+       }
+
+       /* swap buffer objects */
+       surface->intel.drm.bo = ((cairo_drm_surface_t *) clone)->bo;
+       ((cairo_drm_surface_t *) clone)->bo = &bo->base;
+       bo = to_intel_bo (surface->intel.drm.bo);
+
+       cairo_surface_destroy (clone);
+    }
+
+    if (unlikely (bo->tiling == I915_TILING_Y))
+       return _cairo_error (CAIRO_STATUS_INVALID_FORMAT); /* XXX */
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_i965_device_flush (cairo_drm_device_t *device)
+{
+    cairo_status_t status;
+
+    if (unlikely (device->base.finished))
+       return CAIRO_STATUS_SUCCESS;
+
+    status = cairo_device_acquire (&device->base);
+    if (likely (status == CAIRO_STATUS_SUCCESS))
+       status = i965_device_flush ((i965_device_t *) device);
+
+    cairo_device_release (&device->base);
+
+    return status;
+}
+
+static cairo_int_status_t
+_i965_device_throttle (cairo_drm_device_t *device)
+{
+    cairo_status_t status;
+
+    status = cairo_device_acquire (&device->base);
+    if (unlikely (status))
+       return status;
+
+    status = i965_device_flush ((i965_device_t *) device);
+    intel_throttle ((intel_device_t *) device);
+
+    cairo_device_release (&device->base);
+
+    return status;
+}
+
+static void
+_i965_device_destroy (void *base)
+{
+    i965_device_t *device = base;
+
+    i965_device_reset (device);
+    i965_general_state_reset (device);
+
+    _cairo_hash_table_destroy (device->sf_states);
+    _cairo_hash_table_destroy (device->samplers);
+    _cairo_hash_table_destroy (device->cc_states);
+    _cairo_hash_table_destroy (device->wm_kernels);
+    _cairo_hash_table_destroy (device->wm_states);
+    _cairo_hash_table_destroy (device->wm_bindings);
+
+    _cairo_freelist_fini (&device->sf_freelist);
+    _cairo_freelist_fini (&device->cc_freelist);
+    _cairo_freelist_fini (&device->wm_kernel_freelist);
+    _cairo_freelist_fini (&device->wm_state_freelist);
+    _cairo_freelist_fini (&device->wm_binding_freelist);
+    _cairo_freelist_fini (&device->sampler_freelist);
+
+    intel_device_fini (&device->intel);
+    free (device);
+}
+
+static cairo_bool_t
+hash_equal (const void *A, const void *B)
+{
+    const cairo_hash_entry_t *a = A, *b = B;
+    return a->hash == b->hash;
+}
+
+cairo_drm_device_t *
+_cairo_drm_i965_device_create (int fd, dev_t dev, int vendor_id, int chip_id)
+{
+    i965_device_t *device;
+    uint64_t gtt_size;
+    cairo_status_t status;
+
+    if (! intel_info (fd, &gtt_size))
+       return  NULL;
+
+    device = malloc (sizeof (i965_device_t));
+    if (unlikely (device == NULL))
+       return (cairo_drm_device_t *) _cairo_device_create_in_error (CAIRO_STATUS_NO_MEMORY);
+
+    status = intel_device_init (&device->intel, fd);
+    if (unlikely (status))
+       goto CLEANUP;
+
+    device->is_g4x = IS_G4X (chip_id);
+    //device->is_g5x = IS_G5X (chip_id);
+
+    device->intel.base.surface.create = i965_surface_create;
+    device->intel.base.surface.create_for_name = i965_surface_create_for_name;
+    device->intel.base.surface.create_from_cacheable_image = NULL;
+    device->intel.base.surface.enable_scan_out = i965_surface_enable_scan_out;
+
+    device->intel.base.device.flush = _i965_device_flush;
+    device->intel.base.device.throttle = _i965_device_throttle;
+    device->intel.base.device.destroy = _i965_device_destroy;
+
+    device->sf_states = _cairo_hash_table_create (i965_sf_state_equal);
+    if (unlikely (device->sf_states == NULL))
+       goto CLEANUP_INTEL;
+
+    _cairo_freelist_init (&device->sf_freelist,
+                         sizeof (struct i965_sf_state));
+
+
+    device->cc_states = _cairo_hash_table_create (i965_cc_state_equal);
+    if (unlikely (device->cc_states == NULL))
+       goto CLEANUP_SF;
+
+    _cairo_freelist_init (&device->cc_freelist,
+                         sizeof (struct i965_cc_state));
+
+
+    device->wm_kernels = _cairo_hash_table_create (hash_equal);
+    if (unlikely (device->wm_kernels == NULL))
+       goto CLEANUP_CC;
+
+    _cairo_freelist_init (&device->wm_kernel_freelist,
+                         sizeof (struct i965_wm_kernel));
+
+    device->wm_states = _cairo_hash_table_create (i965_wm_state_equal);
+    if (unlikely (device->wm_states == NULL))
+       goto CLEANUP_WM_KERNEL;
+
+    _cairo_freelist_init (&device->wm_state_freelist,
+                         sizeof (struct i965_wm_state));
+
+
+    device->wm_bindings = _cairo_hash_table_create (i965_wm_binding_equal);
+    if (unlikely (device->wm_bindings == NULL))
+       goto CLEANUP_WM_STATE;
+
+    _cairo_freelist_init (&device->wm_binding_freelist,
+                         sizeof (struct i965_wm_binding));
+
+    device->samplers = _cairo_hash_table_create (hash_equal);
+    if (unlikely (device->samplers == NULL))
+       goto CLEANUP_WM_BINDING;
+
+    _cairo_freelist_init (&device->sampler_freelist,
+                         sizeof (struct i965_sampler));
+
+    i965_stream_init (&device->batch,
+                     device->batch_base, sizeof (device->batch_base),
+                     NULL, 0,
+                     device->batch_relocations,
+                     ARRAY_LENGTH (device->batch_relocations));
+
+    i965_stream_init (&device->surface,
+                     device->surface_base, sizeof (device->surface_base),
+                     device->surface_pending_relocations,
+                     ARRAY_LENGTH (device->surface_pending_relocations),
+                     device->surface_relocations,
+                     ARRAY_LENGTH (device->surface_relocations));
+
+    i965_stream_init (&device->general,
+                     device->general_base, sizeof (device->general_base),
+                     device->general_pending_relocations,
+                     ARRAY_LENGTH (device->general_pending_relocations),
+                     NULL, 0);
+
+    i965_stream_init (&device->vertex,
+                     device->vertex_base, sizeof (device->vertex_base),
+                     device->vertex_pending_relocations,
+                     ARRAY_LENGTH (device->vertex_pending_relocations),
+                     NULL, 0);
+
+    cairo_list_init (&device->flush);
+    i965_device_reset (device);
+    device->vs_offset = (uint32_t) -1;
+    device->border_color_offset = (uint32_t) -1;
+    device->general_state = NULL;
+
+    return _cairo_drm_device_init (&device->intel.base,
+                                  fd, dev, vendor_id, chip_id,
+                                  I965_MAX_SIZE);
+
+  CLEANUP_WM_BINDING:
+    _cairo_hash_table_destroy (device->wm_bindings);
+  CLEANUP_WM_STATE:
+    _cairo_hash_table_destroy (device->wm_states);
+  CLEANUP_WM_KERNEL:
+    _cairo_hash_table_destroy (device->wm_kernels);
+  CLEANUP_CC:
+    _cairo_hash_table_destroy (device->cc_states);
+  CLEANUP_SF:
+    _cairo_hash_table_destroy (device->sf_states);
+  CLEANUP_INTEL:
+    intel_device_fini (&device->intel);
+  CLEANUP:
+    free (device);
+    return (cairo_drm_device_t *) _cairo_device_create_in_error (status);
+}
diff --git a/src/drm/cairo-drm-intel-brw-defines.h b/src/drm/cairo-drm-intel-brw-defines.h
new file mode 100644 (file)
index 0000000..b2be36f
--- /dev/null
@@ -0,0 +1,824 @@
+/**************************************************************************
+ *
+ * Copyright 2005 Tungsten Graphics, Inc., Cedar Park, Texas.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sub license, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
+ * IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ **************************************************************************/
+
+#ifndef CAIRO_DRM_INTEL_BRW_DEFINES_H
+#define CAIRO_DRM_INTEL_BRW_DEFINES_H
+
+/* 3D state: */
+#define _3DOP_3DSTATE_PIPELINED       0x0
+#define _3DOP_3DSTATE_NONPIPELINED    0x1
+#define _3DOP_3DCONTROL               0x2
+#define _3DOP_3DPRIMITIVE             0x3
+
+#define _3DSTATE_PIPELINED_POINTERS       0x00
+#define _3DSTATE_BINDING_TABLE_POINTERS   0x01
+#define _3DSTATE_VERTEX_BUFFERS           0x08
+#define _3DSTATE_VERTEX_ELEMENTS          0x09
+#define _3DSTATE_INDEX_BUFFER             0x0A
+#define _3DSTATE_VF_STATISTICS            0x0B
+#define _3DSTATE_DRAWING_RECTANGLE            0x00
+#define _3DSTATE_CONSTANT_COLOR               0x01
+#define _3DSTATE_SAMPLER_PALETTE_LOAD         0x02
+#define _3DSTATE_CHROMA_KEY                   0x04
+#define _3DSTATE_DEPTH_BUFFER                 0x05
+#define _3DSTATE_POLY_STIPPLE_OFFSET          0x06
+#define _3DSTATE_POLY_STIPPLE_PATTERN         0x07
+#define _3DSTATE_LINE_STIPPLE                 0x08
+#define _3DSTATE_GLOBAL_DEPTH_OFFSET_CLAMP    0x09
+#define _3DCONTROL    0x00
+#define _3DPRIMITIVE  0x00
+
+#define PIPE_CONTROL_NOWRITE          0x00
+#define PIPE_CONTROL_WRITEIMMEDIATE   0x01
+#define PIPE_CONTROL_WRITEDEPTH       0x02
+#define PIPE_CONTROL_WRITETIMESTAMP   0x03
+
+#define PIPE_CONTROL_GTTWRITE_PROCESS_LOCAL 0x00
+#define PIPE_CONTROL_GTTWRITE_GLOBAL        0x01
+
+#define BRW_3D(Pipeline,Opcode,Subopcode) ((3 << 29) | \
+                                          ((Pipeline) << 27) | \
+                                          ((Opcode) << 24) | \
+                                          ((Subopcode) << 16))
+
+#define BRW_PIPE_CONTROL                       BRW_3D(3, 2, 0)
+#define BRW_PIPE_CONTROL_NOWRITE       (0 << 14)
+#define BRW_PIPE_CONTROL_WRITE_QWORD   (1 << 14)
+#define BRW_PIPE_CONTROL_WRITE_DEPTH   (2 << 14)
+#define BRW_PIPE_CONTROL_WRITE_TIME    (3 << 14)
+#define BRW_PIPE_CONTROL_DEPTH_STALL   (1 << 13)
+#define BRW_PIPE_CONTROL_WC_FLUSH      (1 << 12)
+#define BRW_PIPE_CONTROL_IS_FLUSH      (1 << 11)
+#define BRW_PIPE_CONTROL_NOTIFY_ENABLE (1 << 8)
+#define BRW_PIPE_CONTROL_GLOBAL_GTT    (1 << 2)
+#define BRW_PIPE_CONTROL_LOCAL_PGTT    (0 << 2)
+
+#define _3DPRIM_POINTLIST         0x01
+#define _3DPRIM_LINELIST          0x02
+#define _3DPRIM_LINESTRIP         0x03
+#define _3DPRIM_TRILIST           0x04
+#define _3DPRIM_TRISTRIP          0x05
+#define _3DPRIM_TRIFAN            0x06
+#define _3DPRIM_QUADLIST          0x07
+#define _3DPRIM_QUADSTRIP         0x08
+#define _3DPRIM_LINELIST_ADJ      0x09
+#define _3DPRIM_LINESTRIP_ADJ     0x0A
+#define _3DPRIM_TRILIST_ADJ       0x0B
+#define _3DPRIM_TRISTRIP_ADJ      0x0C
+#define _3DPRIM_TRISTRIP_REVERSE  0x0D
+#define _3DPRIM_POLYGON           0x0E
+#define _3DPRIM_RECTLIST          0x0F
+#define _3DPRIM_LINELOOP          0x10
+#define _3DPRIM_POINTLIST_BF      0x11
+#define _3DPRIM_LINESTRIP_CONT    0x12
+#define _3DPRIM_LINESTRIP_BF      0x13
+#define _3DPRIM_LINESTRIP_CONT_BF 0x14
+#define _3DPRIM_TRIFAN_NOSTIPPLE  0x15
+
+#define _3DPRIM_VERTEXBUFFER_ACCESS_SEQUENTIAL 0
+#define _3DPRIM_VERTEXBUFFER_ACCESS_RANDOM     1
+
+#define BRW_ANISORATIO_2     0
+#define BRW_ANISORATIO_4     1
+#define BRW_ANISORATIO_6     2
+#define BRW_ANISORATIO_8     3
+#define BRW_ANISORATIO_10    4
+#define BRW_ANISORATIO_12    5
+#define BRW_ANISORATIO_14    6
+#define BRW_ANISORATIO_16    7
+
+#define BRW_BLENDFACTOR_ONE                 0x1
+#define BRW_BLENDFACTOR_SRC_COLOR           0x2
+#define BRW_BLENDFACTOR_SRC_ALPHA           0x3
+#define BRW_BLENDFACTOR_DST_ALPHA           0x4
+#define BRW_BLENDFACTOR_DST_COLOR           0x5
+#define BRW_BLENDFACTOR_SRC_ALPHA_SATURATE  0x6
+#define BRW_BLENDFACTOR_CONST_COLOR         0x7
+#define BRW_BLENDFACTOR_CONST_ALPHA         0x8
+#define BRW_BLENDFACTOR_SRC1_COLOR          0x9
+#define BRW_BLENDFACTOR_SRC1_ALPHA          0x0A
+#define BRW_BLENDFACTOR_ZERO                0x11
+#define BRW_BLENDFACTOR_INV_SRC_COLOR       0x12
+#define BRW_BLENDFACTOR_INV_SRC_ALPHA       0x13
+#define BRW_BLENDFACTOR_INV_DST_ALPHA       0x14
+#define BRW_BLENDFACTOR_INV_DST_COLOR       0x15
+#define BRW_BLENDFACTOR_INV_CONST_COLOR     0x17
+#define BRW_BLENDFACTOR_INV_CONST_ALPHA     0x18
+#define BRW_BLENDFACTOR_INV_SRC1_COLOR      0x19
+#define BRW_BLENDFACTOR_INV_SRC1_ALPHA      0x1A
+
+#define BRW_BLENDFUNCTION_ADD               0
+#define BRW_BLENDFUNCTION_SUBTRACT          1
+#define BRW_BLENDFUNCTION_REVERSE_SUBTRACT  2
+#define BRW_BLENDFUNCTION_MIN               3
+#define BRW_BLENDFUNCTION_MAX               4
+
+#define BRW_ALPHATEST_FORMAT_UNORM8         0
+#define BRW_ALPHATEST_FORMAT_FLOAT32        1
+
+#define BRW_CHROMAKEY_KILL_ON_ANY_MATCH  0
+#define BRW_CHROMAKEY_REPLACE_BLACK      1
+
+#define BRW_CLIP_API_OGL     0
+#define BRW_CLIP_API_DX      1
+
+#define BRW_CLIPMODE_NORMAL              0
+#define BRW_CLIPMODE_CLIP_ALL            1
+#define BRW_CLIPMODE_CLIP_NON_REJECTED   2
+#define BRW_CLIPMODE_REJECT_ALL          3
+#define BRW_CLIPMODE_ACCEPT_ALL          4
+
+#define BRW_CLIP_NDCSPACE     0
+#define BRW_CLIP_SCREENSPACE  1
+
+#define BRW_COMPAREFUNCTION_ALWAYS       0
+#define BRW_COMPAREFUNCTION_NEVER        1
+#define BRW_COMPAREFUNCTION_LESS         2
+#define BRW_COMPAREFUNCTION_EQUAL        3
+#define BRW_COMPAREFUNCTION_LEQUAL       4
+#define BRW_COMPAREFUNCTION_GREATER      5
+#define BRW_COMPAREFUNCTION_NOTEQUAL     6
+#define BRW_COMPAREFUNCTION_GEQUAL       7
+
+#define BRW_COVERAGE_PIXELS_HALF     0
+#define BRW_COVERAGE_PIXELS_1        1
+#define BRW_COVERAGE_PIXELS_2        2
+#define BRW_COVERAGE_PIXELS_4        3
+
+#define BRW_CULLMODE_BOTH        0
+#define BRW_CULLMODE_NONE        1
+#define BRW_CULLMODE_FRONT       2
+#define BRW_CULLMODE_BACK        3
+
+#define BRW_DEFAULTCOLOR_R8G8B8A8_UNORM      0
+#define BRW_DEFAULTCOLOR_R32G32B32A32_FLOAT  1
+
+#define BRW_DEPTHFORMAT_D32_FLOAT_S8X24_UINT     0
+#define BRW_DEPTHFORMAT_D32_FLOAT                1
+#define BRW_DEPTHFORMAT_D24_UNORM_S8_UINT        2
+#define BRW_DEPTHFORMAT_D16_UNORM                5
+
+#define BRW_FLOATING_POINT_IEEE_754        0
+#define BRW_FLOATING_POINT_NON_IEEE_754    1
+
+#define BRW_FRONTWINDING_CW      0
+#define BRW_FRONTWINDING_CCW     1
+
+#define BRW_INDEX_BYTE     0
+#define BRW_INDEX_WORD     1
+#define BRW_INDEX_DWORD    2
+
+#define BRW_LOGICOPFUNCTION_CLEAR            0
+#define BRW_LOGICOPFUNCTION_NOR              1
+#define BRW_LOGICOPFUNCTION_AND_INVERTED     2
+#define BRW_LOGICOPFUNCTION_COPY_INVERTED    3
+#define BRW_LOGICOPFUNCTION_AND_REVERSE      4
+#define BRW_LOGICOPFUNCTION_INVERT           5
+#define BRW_LOGICOPFUNCTION_XOR              6
+#define BRW_LOGICOPFUNCTION_NAND             7
+#define BRW_LOGICOPFUNCTION_AND              8
+#define BRW_LOGICOPFUNCTION_EQUIV            9
+#define BRW_LOGICOPFUNCTION_NOOP             10
+#define BRW_LOGICOPFUNCTION_OR_INVERTED      11
+#define BRW_LOGICOPFUNCTION_COPY             12
+#define BRW_LOGICOPFUNCTION_OR_REVERSE       13
+#define BRW_LOGICOPFUNCTION_OR               14
+#define BRW_LOGICOPFUNCTION_SET              15
+
+#define BRW_MAPFILTER_NEAREST        0x0
+#define BRW_MAPFILTER_LINEAR         0x1
+#define BRW_MAPFILTER_ANISOTROPIC    0x2
+
+#define BRW_MIPFILTER_NONE        0
+#define BRW_MIPFILTER_NEAREST     1
+#define BRW_MIPFILTER_LINEAR      3
+
+#define BRW_POLYGON_FRONT_FACING     0
+#define BRW_POLYGON_BACK_FACING      1
+
+#define BRW_PREFILTER_ALWAYS     0x0
+#define BRW_PREFILTER_NEVER      0x1
+#define BRW_PREFILTER_LESS       0x2
+#define BRW_PREFILTER_EQUAL      0x3
+#define BRW_PREFILTER_LEQUAL     0x4
+#define BRW_PREFILTER_GREATER    0x5
+#define BRW_PREFILTER_NOTEQUAL   0x6
+#define BRW_PREFILTER_GEQUAL     0x7
+
+#define BRW_PROVOKING_VERTEX_0    0
+#define BRW_PROVOKING_VERTEX_1    1
+#define BRW_PROVOKING_VERTEX_2    2
+
+#define BRW_RASTRULE_UPPER_LEFT  0
+#define BRW_RASTRULE_UPPER_RIGHT 1
+
+#define BRW_RENDERTARGET_CLAMPRANGE_UNORM    0
+#define BRW_RENDERTARGET_CLAMPRANGE_SNORM    1
+#define BRW_RENDERTARGET_CLAMPRANGE_FORMAT   2
+
+#define BRW_STENCILOP_KEEP               0
+#define BRW_STENCILOP_ZERO               1
+#define BRW_STENCILOP_REPLACE            2
+#define BRW_STENCILOP_INCRSAT            3
+#define BRW_STENCILOP_DECRSAT            4
+#define BRW_STENCILOP_INCR               5
+#define BRW_STENCILOP_DECR               6
+#define BRW_STENCILOP_INVERT             7
+
+#define BRW_SURFACE_MIPMAPLAYOUT_BELOW   0
+#define BRW_SURFACE_MIPMAPLAYOUT_RIGHT   1
+
+#define BRW_SURFACEFORMAT_R32G32B32A32_FLOAT             0x000 
+#define BRW_SURFACEFORMAT_R32G32B32A32_SINT              0x001 
+#define BRW_SURFACEFORMAT_R32G32B32A32_UINT              0x002 
+#define BRW_SURFACEFORMAT_R32G32B32A32_UNORM             0x003 
+#define BRW_SURFACEFORMAT_R32G32B32A32_SNORM             0x004 
+#define BRW_SURFACEFORMAT_R64G64_FLOAT                   0x005 
+#define BRW_SURFACEFORMAT_R32G32B32X32_FLOAT             0x006 
+#define BRW_SURFACEFORMAT_R32G32B32A32_SSCALED           0x007
+#define BRW_SURFACEFORMAT_R32G32B32A32_USCALED           0x008
+#define BRW_SURFACEFORMAT_R32G32B32_FLOAT                0x040 
+#define BRW_SURFACEFORMAT_R32G32B32_SINT                 0x041 
+#define BRW_SURFACEFORMAT_R32G32B32_UINT                 0x042 
+#define BRW_SURFACEFORMAT_R32G32B32_UNORM                0x043 
+#define BRW_SURFACEFORMAT_R32G32B32_SNORM                0x044 
+#define BRW_SURFACEFORMAT_R32G32B32_SSCALED              0x045 
+#define BRW_SURFACEFORMAT_R32G32B32_USCALED              0x046 
+#define BRW_SURFACEFORMAT_R16G16B16A16_UNORM             0x080 
+#define BRW_SURFACEFORMAT_R16G16B16A16_SNORM             0x081 
+#define BRW_SURFACEFORMAT_R16G16B16A16_SINT              0x082 
+#define BRW_SURFACEFORMAT_R16G16B16A16_UINT              0x083 
+#define BRW_SURFACEFORMAT_R16G16B16A16_FLOAT             0x084 
+#define BRW_SURFACEFORMAT_R32G32_FLOAT                   0x085 
+#define BRW_SURFACEFORMAT_R32G32_SINT                    0x086 
+#define BRW_SURFACEFORMAT_R32G32_UINT                    0x087 
+#define BRW_SURFACEFORMAT_R32_FLOAT_X8X24_TYPELESS       0x088 
+#define BRW_SURFACEFORMAT_X32_TYPELESS_G8X24_UINT        0x089 
+#define BRW_SURFACEFORMAT_L32A32_FLOAT                   0x08A 
+#define BRW_SURFACEFORMAT_R32G32_UNORM                   0x08B 
+#define BRW_SURFACEFORMAT_R32G32_SNORM                   0x08C 
+#define BRW_SURFACEFORMAT_R64_FLOAT                      0x08D 
+#define BRW_SURFACEFORMAT_R16G16B16X16_UNORM             0x08E 
+#define BRW_SURFACEFORMAT_R16G16B16X16_FLOAT             0x08F 
+#define BRW_SURFACEFORMAT_A32X32_FLOAT                   0x090 
+#define BRW_SURFACEFORMAT_L32X32_FLOAT                   0x091 
+#define BRW_SURFACEFORMAT_I32X32_FLOAT                   0x092 
+#define BRW_SURFACEFORMAT_R16G16B16A16_SSCALED           0x093
+#define BRW_SURFACEFORMAT_R16G16B16A16_USCALED           0x094
+#define BRW_SURFACEFORMAT_R32G32_SSCALED                 0x095
+#define BRW_SURFACEFORMAT_R32G32_USCALED                 0x096
+#define BRW_SURFACEFORMAT_B8G8R8A8_UNORM                 0x0C0 
+#define BRW_SURFACEFORMAT_B8G8R8A8_UNORM_SRGB            0x0C1 
+#define BRW_SURFACEFORMAT_R10G10B10A2_UNORM              0x0C2 
+#define BRW_SURFACEFORMAT_R10G10B10A2_UNORM_SRGB         0x0C3 
+#define BRW_SURFACEFORMAT_R10G10B10A2_UINT               0x0C4 
+#define BRW_SURFACEFORMAT_R10G10B10_SNORM_A2_UNORM       0x0C5 
+#define BRW_SURFACEFORMAT_R8G8B8A8_UNORM                 0x0C7 
+#define BRW_SURFACEFORMAT_R8G8B8A8_UNORM_SRGB            0x0C8 
+#define BRW_SURFACEFORMAT_R8G8B8A8_SNORM                 0x0C9 
+#define BRW_SURFACEFORMAT_R8G8B8A8_SINT                  0x0CA 
+#define BRW_SURFACEFORMAT_R8G8B8A8_UINT                  0x0CB 
+#define BRW_SURFACEFORMAT_R16G16_UNORM                   0x0CC 
+#define BRW_SURFACEFORMAT_R16G16_SNORM                   0x0CD 
+#define BRW_SURFACEFORMAT_R16G16_SINT                    0x0CE 
+#define BRW_SURFACEFORMAT_R16G16_UINT                    0x0CF 
+#define BRW_SURFACEFORMAT_R16G16_FLOAT                   0x0D0 
+#define BRW_SURFACEFORMAT_B10G10R10A2_UNORM              0x0D1 
+#define BRW_SURFACEFORMAT_B10G10R10A2_UNORM_SRGB         0x0D2 
+#define BRW_SURFACEFORMAT_R11G11B10_FLOAT                0x0D3 
+#define BRW_SURFACEFORMAT_R32_SINT                       0x0D6 
+#define BRW_SURFACEFORMAT_R32_UINT                       0x0D7 
+#define BRW_SURFACEFORMAT_R32_FLOAT                      0x0D8 
+#define BRW_SURFACEFORMAT_R24_UNORM_X8_TYPELESS          0x0D9 
+#define BRW_SURFACEFORMAT_X24_TYPELESS_G8_UINT           0x0DA 
+#define BRW_SURFACEFORMAT_L16A16_UNORM                   0x0DF 
+#define BRW_SURFACEFORMAT_I24X8_UNORM                    0x0E0 
+#define BRW_SURFACEFORMAT_L24X8_UNORM                    0x0E1 
+#define BRW_SURFACEFORMAT_A24X8_UNORM                    0x0E2 
+#define BRW_SURFACEFORMAT_I32_FLOAT                      0x0E3 
+#define BRW_SURFACEFORMAT_L32_FLOAT                      0x0E4 
+#define BRW_SURFACEFORMAT_A32_FLOAT                      0x0E5 
+#define BRW_SURFACEFORMAT_B8G8R8X8_UNORM                 0x0E9 
+#define BRW_SURFACEFORMAT_B8G8R8X8_UNORM_SRGB            0x0EA 
+#define BRW_SURFACEFORMAT_R8G8B8X8_UNORM                 0x0EB 
+#define BRW_SURFACEFORMAT_R8G8B8X8_UNORM_SRGB            0x0EC 
+#define BRW_SURFACEFORMAT_R9G9B9E5_SHAREDEXP             0x0ED 
+#define BRW_SURFACEFORMAT_B10G10R10X2_UNORM              0x0EE 
+#define BRW_SURFACEFORMAT_L16A16_FLOAT                   0x0F0 
+#define BRW_SURFACEFORMAT_R32_UNORM                      0x0F1 
+#define BRW_SURFACEFORMAT_R32_SNORM                      0x0F2 
+#define BRW_SURFACEFORMAT_R10G10B10X2_USCALED            0x0F3
+#define BRW_SURFACEFORMAT_R8G8B8A8_SSCALED               0x0F4
+#define BRW_SURFACEFORMAT_R8G8B8A8_USCALED               0x0F5
+#define BRW_SURFACEFORMAT_R16G16_SSCALED                 0x0F6
+#define BRW_SURFACEFORMAT_R16G16_USCALED                 0x0F7
+#define BRW_SURFACEFORMAT_R32_SSCALED                    0x0F8
+#define BRW_SURFACEFORMAT_R32_USCALED                    0x0F9
+#define BRW_SURFACEFORMAT_B5G6R5_UNORM                   0x100 
+#define BRW_SURFACEFORMAT_B5G6R5_UNORM_SRGB              0x101 
+#define BRW_SURFACEFORMAT_B5G5R5A1_UNORM                 0x102 
+#define BRW_SURFACEFORMAT_B5G5R5A1_UNORM_SRGB            0x103 
+#define BRW_SURFACEFORMAT_B4G4R4A4_UNORM                 0x104 
+#define BRW_SURFACEFORMAT_B4G4R4A4_UNORM_SRGB            0x105 
+#define BRW_SURFACEFORMAT_R8G8_UNORM                     0x106 
+#define BRW_SURFACEFORMAT_R8G8_SNORM                     0x107 
+#define BRW_SURFACEFORMAT_R8G8_SINT                      0x108 
+#define BRW_SURFACEFORMAT_R8G8_UINT                      0x109 
+#define BRW_SURFACEFORMAT_R16_UNORM                      0x10A 
+#define BRW_SURFACEFORMAT_R16_SNORM                      0x10B 
+#define BRW_SURFACEFORMAT_R16_SINT                       0x10C 
+#define BRW_SURFACEFORMAT_R16_UINT                       0x10D 
+#define BRW_SURFACEFORMAT_R16_FLOAT                      0x10E 
+#define BRW_SURFACEFORMAT_I16_UNORM                      0x111 
+#define BRW_SURFACEFORMAT_L16_UNORM                      0x112 
+#define BRW_SURFACEFORMAT_A16_UNORM                      0x113 
+#define BRW_SURFACEFORMAT_L8A8_UNORM                     0x114 
+#define BRW_SURFACEFORMAT_I16_FLOAT                      0x115
+#define BRW_SURFACEFORMAT_L16_FLOAT                      0x116
+#define BRW_SURFACEFORMAT_A16_FLOAT                      0x117 
+#define BRW_SURFACEFORMAT_R5G5_SNORM_B6_UNORM            0x119 
+#define BRW_SURFACEFORMAT_B5G5R5X1_UNORM                 0x11A 
+#define BRW_SURFACEFORMAT_B5G5R5X1_UNORM_SRGB            0x11B
+#define BRW_SURFACEFORMAT_R8G8_SSCALED                   0x11C
+#define BRW_SURFACEFORMAT_R8G8_USCALED                   0x11D
+#define BRW_SURFACEFORMAT_R16_SSCALED                    0x11E
+#define BRW_SURFACEFORMAT_R16_USCALED                    0x11F
+#define BRW_SURFACEFORMAT_R8_UNORM                       0x140 
+#define BRW_SURFACEFORMAT_R8_SNORM                       0x141 
+#define BRW_SURFACEFORMAT_R8_SINT                        0x142 
+#define BRW_SURFACEFORMAT_R8_UINT                        0x143 
+#define BRW_SURFACEFORMAT_A8_UNORM                       0x144 
+#define BRW_SURFACEFORMAT_I8_UNORM                       0x145 
+#define BRW_SURFACEFORMAT_L8_UNORM                       0x146 
+#define BRW_SURFACEFORMAT_P4A4_UNORM                     0x147 
+#define BRW_SURFACEFORMAT_A4P4_UNORM                     0x148
+#define BRW_SURFACEFORMAT_R8_SSCALED                     0x149
+#define BRW_SURFACEFORMAT_R8_USCALED                     0x14A
+#define BRW_SURFACEFORMAT_R1_UINT                        0x181 
+#define BRW_SURFACEFORMAT_YCRCB_NORMAL                   0x182 
+#define BRW_SURFACEFORMAT_YCRCB_SWAPUVY                  0x183 
+#define BRW_SURFACEFORMAT_BC1_UNORM                      0x186 
+#define BRW_SURFACEFORMAT_BC2_UNORM                      0x187 
+#define BRW_SURFACEFORMAT_BC3_UNORM                      0x188 
+#define BRW_SURFACEFORMAT_BC4_UNORM                      0x189 
+#define BRW_SURFACEFORMAT_BC5_UNORM                      0x18A 
+#define BRW_SURFACEFORMAT_BC1_UNORM_SRGB                 0x18B 
+#define BRW_SURFACEFORMAT_BC2_UNORM_SRGB                 0x18C 
+#define BRW_SURFACEFORMAT_BC3_UNORM_SRGB                 0x18D 
+#define BRW_SURFACEFORMAT_MONO8                          0x18E 
+#define BRW_SURFACEFORMAT_YCRCB_SWAPUV                   0x18F 
+#define BRW_SURFACEFORMAT_YCRCB_SWAPY                    0x190 
+#define BRW_SURFACEFORMAT_DXT1_RGB                       0x191 
+#define BRW_SURFACEFORMAT_FXT1                           0x192 
+#define BRW_SURFACEFORMAT_R8G8B8_UNORM                   0x193 
+#define BRW_SURFACEFORMAT_R8G8B8_SNORM                   0x194 
+#define BRW_SURFACEFORMAT_R8G8B8_SSCALED                 0x195 
+#define BRW_SURFACEFORMAT_R8G8B8_USCALED                 0x196 
+#define BRW_SURFACEFORMAT_R64G64B64A64_FLOAT             0x197 
+#define BRW_SURFACEFORMAT_R64G64B64_FLOAT                0x198 
+#define BRW_SURFACEFORMAT_BC4_SNORM                      0x199 
+#define BRW_SURFACEFORMAT_BC5_SNORM                      0x19A 
+#define BRW_SURFACEFORMAT_R16G16B16_UNORM                0x19C 
+#define BRW_SURFACEFORMAT_R16G16B16_SNORM                0x19D 
+#define BRW_SURFACEFORMAT_R16G16B16_SSCALED              0x19E 
+#define BRW_SURFACEFORMAT_R16G16B16_USCALED              0x19F
+
+#define BRW_SURFACERETURNFORMAT_FLOAT32  0
+#define BRW_SURFACERETURNFORMAT_S1       1
+
+#define BRW_SURFACE_1D      0
+#define BRW_SURFACE_2D      1
+#define BRW_SURFACE_3D      2
+#define BRW_SURFACE_CUBE    3
+#define BRW_SURFACE_BUFFER  4
+#define BRW_SURFACE_NULL    7
+
+#define BRW_BORDER_COLOR_MODE_DEFAULT  0
+#define BRW_BORDER_COLOR_MODE_LEGACY   1
+
+#define BRW_TEXCOORDMODE_WRAP            0
+#define BRW_TEXCOORDMODE_MIRROR          1
+#define BRW_TEXCOORDMODE_CLAMP           2
+#define BRW_TEXCOORDMODE_CUBE            3
+#define BRW_TEXCOORDMODE_CLAMP_BORDER    4
+#define BRW_TEXCOORDMODE_MIRROR_ONCE     5
+
+#define BRW_THREAD_PRIORITY_NORMAL   0
+#define BRW_THREAD_PRIORITY_HIGH     1
+
+#define BRW_TILEWALK_XMAJOR                 0
+#define BRW_TILEWALK_YMAJOR                 1
+
+#define BRW_VERTEX_SUBPIXEL_PRECISION_8BITS  0
+#define BRW_VERTEX_SUBPIXEL_PRECISION_4BITS  1
+
+#define BRW_VERTEXBUFFER_ACCESS_VERTEXDATA     0
+#define BRW_VERTEXBUFFER_ACCESS_INSTANCEDATA   1
+
+#define BRW_VFCOMPONENT_NOSTORE      0
+#define BRW_VFCOMPONENT_STORE_SRC    1
+#define BRW_VFCOMPONENT_STORE_0      2
+#define BRW_VFCOMPONENT_STORE_1_FLT  3
+#define BRW_VFCOMPONENT_STORE_1_INT  4
+#define BRW_VFCOMPONENT_STORE_VID    5
+#define BRW_VFCOMPONENT_STORE_IID    6
+#define BRW_VFCOMPONENT_STORE_PID    7
+
+
+
+/* Execution Unit (EU) defines */
+
+#define BRW_ALIGN_1   0
+#define BRW_ALIGN_16  1
+
+#define BRW_ADDRESS_DIRECT                        0
+#define BRW_ADDRESS_REGISTER_INDIRECT_REGISTER    1
+
+#define BRW_CHANNEL_X     0
+#define BRW_CHANNEL_Y     1
+#define BRW_CHANNEL_Z     2
+#define BRW_CHANNEL_W     3
+
+#define BRW_COMPRESSION_NONE          0
+#define BRW_COMPRESSION_2NDHALF       1
+#define BRW_COMPRESSION_COMPRESSED    2
+
+#define BRW_CONDITIONAL_NONE  0
+#define BRW_CONDITIONAL_Z     1
+#define BRW_CONDITIONAL_NZ    2
+#define BRW_CONDITIONAL_EQ    1        /* Z */
+#define BRW_CONDITIONAL_NEQ   2        /* NZ */
+#define BRW_CONDITIONAL_G     3
+#define BRW_CONDITIONAL_GE    4
+#define BRW_CONDITIONAL_L     5
+#define BRW_CONDITIONAL_LE    6
+#define BRW_CONDITIONAL_C     7
+#define BRW_CONDITIONAL_O     8
+
+#define BRW_DEBUG_NONE        0
+#define BRW_DEBUG_BREAKPOINT  1
+
+#define BRW_DEPENDENCY_NORMAL         0
+#define BRW_DEPENDENCY_NOTCLEARED     1
+#define BRW_DEPENDENCY_NOTCHECKED     2
+#define BRW_DEPENDENCY_DISABLE        3
+
+#define BRW_EXECUTE_1     0
+#define BRW_EXECUTE_2     1
+#define BRW_EXECUTE_4     2
+#define BRW_EXECUTE_8     3
+#define BRW_EXECUTE_16    4
+#define BRW_EXECUTE_32    5
+
+#define BRW_HORIZONTAL_STRIDE_0   0
+#define BRW_HORIZONTAL_STRIDE_1   1
+#define BRW_HORIZONTAL_STRIDE_2   2
+#define BRW_HORIZONTAL_STRIDE_4   3
+
+#define BRW_INSTRUCTION_NORMAL    0
+#define BRW_INSTRUCTION_SATURATE  1
+
+#define BRW_MASK_ENABLE   0
+#define BRW_MASK_DISABLE  1
+
+#define BRW_OPCODE_MOV        1
+#define BRW_OPCODE_SEL        2
+#define BRW_OPCODE_NOT        4
+#define BRW_OPCODE_AND        5
+#define BRW_OPCODE_OR         6
+#define BRW_OPCODE_XOR        7
+#define BRW_OPCODE_SHR        8
+#define BRW_OPCODE_SHL        9
+#define BRW_OPCODE_RSR        10
+#define BRW_OPCODE_RSL        11
+#define BRW_OPCODE_ASR        12
+#define BRW_OPCODE_CMP        16
+#define BRW_OPCODE_JMPI       32
+#define BRW_OPCODE_IF         34
+#define BRW_OPCODE_IFF        35
+#define BRW_OPCODE_ELSE       36
+#define BRW_OPCODE_ENDIF      37
+#define BRW_OPCODE_DO         38
+#define BRW_OPCODE_WHILE      39
+#define BRW_OPCODE_BREAK      40
+#define BRW_OPCODE_CONTINUE   41
+#define BRW_OPCODE_HALT       42
+#define BRW_OPCODE_MSAVE      44
+#define BRW_OPCODE_MRESTORE   45
+#define BRW_OPCODE_PUSH       46
+#define BRW_OPCODE_POP        47
+#define BRW_OPCODE_WAIT       48
+#define BRW_OPCODE_SEND       49
+#define BRW_OPCODE_ADD        64
+#define BRW_OPCODE_MUL        65
+#define BRW_OPCODE_AVG        66
+#define BRW_OPCODE_FRC        67
+#define BRW_OPCODE_RNDU       68
+#define BRW_OPCODE_RNDD       69
+#define BRW_OPCODE_RNDE       70
+#define BRW_OPCODE_RNDZ       71
+#define BRW_OPCODE_MAC        72
+#define BRW_OPCODE_MACH       73
+#define BRW_OPCODE_LZD        74
+#define BRW_OPCODE_SAD2       80
+#define BRW_OPCODE_SADA2      81
+#define BRW_OPCODE_DP4        84
+#define BRW_OPCODE_DPH        85
+#define BRW_OPCODE_DP3        86
+#define BRW_OPCODE_DP2        87
+#define BRW_OPCODE_DPA2       88
+#define BRW_OPCODE_LINE       89
+#define BRW_OPCODE_NOP        126
+
+#define BRW_PREDICATE_NONE             0
+#define BRW_PREDICATE_NORMAL           1
+#define BRW_PREDICATE_ALIGN1_ANYV             2
+#define BRW_PREDICATE_ALIGN1_ALLV             3
+#define BRW_PREDICATE_ALIGN1_ANY2H            4
+#define BRW_PREDICATE_ALIGN1_ALL2H            5
+#define BRW_PREDICATE_ALIGN1_ANY4H            6
+#define BRW_PREDICATE_ALIGN1_ALL4H            7
+#define BRW_PREDICATE_ALIGN1_ANY8H            8
+#define BRW_PREDICATE_ALIGN1_ALL8H            9
+#define BRW_PREDICATE_ALIGN1_ANY16H           10
+#define BRW_PREDICATE_ALIGN1_ALL16H           11
+#define BRW_PREDICATE_ALIGN16_REPLICATE_X     2
+#define BRW_PREDICATE_ALIGN16_REPLICATE_Y     3
+#define BRW_PREDICATE_ALIGN16_REPLICATE_Z     4
+#define BRW_PREDICATE_ALIGN16_REPLICATE_W     5
+#define BRW_PREDICATE_ALIGN16_ANY4H           6
+#define BRW_PREDICATE_ALIGN16_ALL4H           7
+
+#define BRW_ARCHITECTURE_REGISTER_FILE    0
+#define BRW_GENERAL_REGISTER_FILE         1
+#define BRW_MESSAGE_REGISTER_FILE         2
+#define BRW_IMMEDIATE_VALUE               3
+
+#define BRW_REGISTER_TYPE_UD  0
+#define BRW_REGISTER_TYPE_D   1
+#define BRW_REGISTER_TYPE_UW  2
+#define BRW_REGISTER_TYPE_W   3
+#define BRW_REGISTER_TYPE_UB  4
+#define BRW_REGISTER_TYPE_B   5
+#define BRW_REGISTER_TYPE_VF  5        /* packed float vector, immediates only? */
+#define BRW_REGISTER_TYPE_HF  6
+#define BRW_REGISTER_TYPE_V   6        /* packed int vector, immediates only, uword dest only */
+#define BRW_REGISTER_TYPE_F   7
+
+#define BRW_ARF_NULL                  0x00
+#define BRW_ARF_ADDRESS               0x10
+#define BRW_ARF_ACCUMULATOR           0x20   
+#define BRW_ARF_FLAG                  0x30
+#define BRW_ARF_MASK                  0x40
+#define BRW_ARF_MASK_STACK            0x50
+#define BRW_ARF_MASK_STACK_DEPTH      0x60
+#define BRW_ARF_STATE                 0x70
+#define BRW_ARF_CONTROL               0x80
+#define BRW_ARF_NOTIFICATION_COUNT    0x90
+#define BRW_ARF_IP                    0xA0
+
+#define BRW_AMASK   0
+#define BRW_IMASK   1
+#define BRW_LMASK   2
+#define BRW_CMASK   3
+
+
+
+#define BRW_THREAD_NORMAL     0
+#define BRW_THREAD_ATOMIC     1
+#define BRW_THREAD_SWITCH     2
+
+#define BRW_VERTICAL_STRIDE_0                 0
+#define BRW_VERTICAL_STRIDE_1                 1
+#define BRW_VERTICAL_STRIDE_2                 2
+#define BRW_VERTICAL_STRIDE_4                 3
+#define BRW_VERTICAL_STRIDE_8                 4
+#define BRW_VERTICAL_STRIDE_16                5
+#define BRW_VERTICAL_STRIDE_32                6
+#define BRW_VERTICAL_STRIDE_64                7
+#define BRW_VERTICAL_STRIDE_128               8
+#define BRW_VERTICAL_STRIDE_256               9
+#define BRW_VERTICAL_STRIDE_ONE_DIMENSIONAL   0xF
+
+#define BRW_WIDTH_1       0
+#define BRW_WIDTH_2       1
+#define BRW_WIDTH_4       2
+#define BRW_WIDTH_8       3
+#define BRW_WIDTH_16      4
+
+#define BRW_STATELESS_BUFFER_BOUNDARY_1K      0
+#define BRW_STATELESS_BUFFER_BOUNDARY_2K      1
+#define BRW_STATELESS_BUFFER_BOUNDARY_4K      2
+#define BRW_STATELESS_BUFFER_BOUNDARY_8K      3
+#define BRW_STATELESS_BUFFER_BOUNDARY_16K     4
+#define BRW_STATELESS_BUFFER_BOUNDARY_32K     5
+#define BRW_STATELESS_BUFFER_BOUNDARY_64K     6
+#define BRW_STATELESS_BUFFER_BOUNDARY_128K    7
+#define BRW_STATELESS_BUFFER_BOUNDARY_256K    8
+#define BRW_STATELESS_BUFFER_BOUNDARY_512K    9
+#define BRW_STATELESS_BUFFER_BOUNDARY_1M      10
+#define BRW_STATELESS_BUFFER_BOUNDARY_2M      11
+
+#define BRW_POLYGON_FACING_FRONT      0
+#define BRW_POLYGON_FACING_BACK       1
+
+#define BRW_MESSAGE_TARGET_NULL               0
+#define BRW_MESSAGE_TARGET_MATH               1
+#define BRW_MESSAGE_TARGET_SAMPLER            2
+#define BRW_MESSAGE_TARGET_GATEWAY            3
+#define BRW_MESSAGE_TARGET_DATAPORT_READ      4
+#define BRW_MESSAGE_TARGET_DATAPORT_WRITE     5
+#define BRW_MESSAGE_TARGET_URB                6
+#define BRW_MESSAGE_TARGET_THREAD_SPAWNER     7
+
+#define BRW_SAMPLER_RETURN_FORMAT_FLOAT32     0
+#define BRW_SAMPLER_RETURN_FORMAT_UINT32      2
+#define BRW_SAMPLER_RETURN_FORMAT_SINT32      3
+
+#define BRW_SAMPLER_MESSAGE_SIMD8_SAMPLE              0
+#define BRW_SAMPLER_MESSAGE_SIMD16_SAMPLE             0
+#define BRW_SAMPLER_MESSAGE_SIMD16_SAMPLE_BIAS        0
+#define BRW_SAMPLER_MESSAGE_SIMD8_KILLPIX             1
+#define BRW_SAMPLER_MESSAGE_SIMD4X2_SAMPLE_LOD        1
+#define BRW_SAMPLER_MESSAGE_SIMD16_SAMPLE_LOD         1
+#define BRW_SAMPLER_MESSAGE_SIMD4X2_SAMPLE_GRADIENTS  2
+#define BRW_SAMPLER_MESSAGE_SIMD8_SAMPLE_GRADIENTS    2
+#define BRW_SAMPLER_MESSAGE_SIMD4X2_SAMPLE_COMPARE    0
+#define BRW_SAMPLER_MESSAGE_SIMD16_SAMPLE_COMPARE     2
+#define BRW_SAMPLER_MESSAGE_SIMD4X2_RESINFO           2
+#define BRW_SAMPLER_MESSAGE_SIMD8_RESINFO             2
+#define BRW_SAMPLER_MESSAGE_SIMD16_RESINFO            2
+#define BRW_SAMPLER_MESSAGE_SIMD4X2_LD                3
+#define BRW_SAMPLER_MESSAGE_SIMD8_LD                  3
+#define BRW_SAMPLER_MESSAGE_SIMD16_LD                 3
+
+#define BRW_DATAPORT_OWORD_BLOCK_1_OWORDLOW   0
+#define BRW_DATAPORT_OWORD_BLOCK_1_OWORDHIGH  1
+#define BRW_DATAPORT_OWORD_BLOCK_2_OWORDS     2
+#define BRW_DATAPORT_OWORD_BLOCK_4_OWORDS     3
+#define BRW_DATAPORT_OWORD_BLOCK_8_OWORDS     4
+
+#define BRW_DATAPORT_OWORD_DUAL_BLOCK_1OWORD     0
+#define BRW_DATAPORT_OWORD_DUAL_BLOCK_4OWORDS    2
+
+#define BRW_DATAPORT_DWORD_SCATTERED_BLOCK_8DWORDS   2
+#define BRW_DATAPORT_DWORD_SCATTERED_BLOCK_16DWORDS  3
+
+#define BRW_DATAPORT_READ_MESSAGE_OWORD_BLOCK_READ          0
+#define BRW_DATAPORT_READ_MESSAGE_OWORD_DUAL_BLOCK_READ     1
+#define BRW_DATAPORT_READ_MESSAGE_DWORD_BLOCK_READ          2
+#define BRW_DATAPORT_READ_MESSAGE_DWORD_SCATTERED_READ      3
+
+#define BRW_DATAPORT_READ_TARGET_DATA_CACHE      0
+#define BRW_DATAPORT_READ_TARGET_RENDER_CACHE    1
+#define BRW_DATAPORT_READ_TARGET_SAMPLER_CACHE   2
+
+#define BRW_DATAPORT_RENDER_TARGET_WRITE_SIMD16_SINGLE_SOURCE                0
+#define BRW_DATAPORT_RENDER_TARGET_WRITE_SIMD16_SINGLE_SOURCE_REPLICATED     1
+#define BRW_DATAPORT_RENDER_TARGET_WRITE_SIMD8_DUAL_SOURCE_SUBSPAN01         2
+#define BRW_DATAPORT_RENDER_TARGET_WRITE_SIMD8_DUAL_SOURCE_SUBSPAN23         3
+#define BRW_DATAPORT_RENDER_TARGET_WRITE_SIMD8_SINGLE_SOURCE_SUBSPAN01       4
+
+#define BRW_DATAPORT_WRITE_MESSAGE_OWORD_BLOCK_WRITE                0
+#define BRW_DATAPORT_WRITE_MESSAGE_OWORD_DUAL_BLOCK_WRITE           1
+#define BRW_DATAPORT_WRITE_MESSAGE_DWORD_BLOCK_WRITE                2
+#define BRW_DATAPORT_WRITE_MESSAGE_DWORD_SCATTERED_WRITE            3
+#define BRW_DATAPORT_WRITE_MESSAGE_RENDER_TARGET_WRITE              4
+#define BRW_DATAPORT_WRITE_MESSAGE_STREAMED_VERTEX_BUFFER_WRITE     5
+#define BRW_DATAPORT_WRITE_MESSAGE_FLUSH_RENDER_CACHE               7
+
+#define BRW_MATH_FUNCTION_INV                              1
+#define BRW_MATH_FUNCTION_LOG                              2
+#define BRW_MATH_FUNCTION_EXP                              3
+#define BRW_MATH_FUNCTION_SQRT                             4
+#define BRW_MATH_FUNCTION_RSQ                              5
+#define BRW_MATH_FUNCTION_SIN                              6 /* was 7 */
+#define BRW_MATH_FUNCTION_COS                              7 /* was 8 */
+#define BRW_MATH_FUNCTION_SINCOS                           8 /* was 6 */
+#define BRW_MATH_FUNCTION_TAN                              9
+#define BRW_MATH_FUNCTION_POW                              10
+#define BRW_MATH_FUNCTION_INT_DIV_QUOTIENT_AND_REMAINDER   11
+#define BRW_MATH_FUNCTION_INT_DIV_QUOTIENT                 12
+#define BRW_MATH_FUNCTION_INT_DIV_REMAINDER                13
+
+#define BRW_MATH_INTEGER_UNSIGNED     0
+#define BRW_MATH_INTEGER_SIGNED       1
+
+#define BRW_MATH_PRECISION_FULL        0
+#define BRW_MATH_PRECISION_PARTIAL     1
+
+#define BRW_MATH_SATURATE_NONE         0
+#define BRW_MATH_SATURATE_SATURATE     1
+
+#define BRW_MATH_DATA_VECTOR  0
+#define BRW_MATH_DATA_SCALAR  1
+
+#define BRW_URB_OPCODE_WRITE  0
+
+#define BRW_URB_SWIZZLE_NONE          0
+#define BRW_URB_SWIZZLE_INTERLEAVE    1
+#define BRW_URB_SWIZZLE_TRANSPOSE     2
+
+#define BRW_SCRATCH_SPACE_SIZE_1K     0
+#define BRW_SCRATCH_SPACE_SIZE_2K     1
+#define BRW_SCRATCH_SPACE_SIZE_4K     2
+#define BRW_SCRATCH_SPACE_SIZE_8K     3
+#define BRW_SCRATCH_SPACE_SIZE_16K    4
+#define BRW_SCRATCH_SPACE_SIZE_32K    5
+#define BRW_SCRATCH_SPACE_SIZE_64K    6
+#define BRW_SCRATCH_SPACE_SIZE_128K   7
+#define BRW_SCRATCH_SPACE_SIZE_256K   8
+#define BRW_SCRATCH_SPACE_SIZE_512K   9
+#define BRW_SCRATCH_SPACE_SIZE_1M     10
+#define BRW_SCRATCH_SPACE_SIZE_2M     11
+
+
+
+
+#define CMD_URB_FENCE                 0x6000
+#define CMD_CONST_BUFFER_STATE        0x6001
+#define CMD_CONST_BUFFER              0x6002
+
+#define CMD_STATE_BASE_ADDRESS        0x6101
+#define CMD_STATE_INSN_POINTER        0x6102
+#define CMD_PIPELINE_SELECT           0x6104
+
+#define CMD_PIPELINED_STATE_POINTERS  0x7800
+#define CMD_BINDING_TABLE_PTRS        0x7801
+#define CMD_VERTEX_BUFFER             0x7808
+#define CMD_VERTEX_ELEMENT            0x7809
+#define CMD_INDEX_BUFFER              0x780a
+#define CMD_VF_STATISTICS             0x780b
+
+#define CMD_DRAW_RECT                 0x7900
+#define CMD_BLEND_CONSTANT_COLOR      0x7901
+#define CMD_CHROMA_KEY                0x7904
+#define CMD_DEPTH_BUFFER              0x7905
+#define CMD_POLY_STIPPLE_OFFSET       0x7906
+#define CMD_POLY_STIPPLE_PATTERN      0x7907
+#define CMD_LINE_STIPPLE_PATTERN      0x7908
+#define CMD_GLOBAL_DEPTH_OFFSET_CLAMP 0x7908
+
+#define CMD_PIPE_CONTROL              0x7a00
+
+#define CMD_3D_PRIM                   0x7b00
+
+#define CMD_MI_FLUSH                  0x0200
+
+
+/* Various values from the R0 vertex header:
+ */
+#define R02_PRIM_END    0x1
+#define R02_PRIM_START  0x2
+
+/* media pipeline */
+
+#define BRW_VFE_MODE_GENERIC           0x0
+#define BRW_VFE_MODE_VLD_MPEG2         0x1
+#define BRW_VFE_MODE_IS                        0x2
+#define BRW_VFE_MODE_AVC_MC            0x4
+#define BRW_VFE_MODE_AVC_IT            0x7
+#define BRW_VFE_MODE_VC1_IT            0xB
+
+#define BRW_VFE_DEBUG_COUNTER_FREE     0
+#define BRW_VFE_DEBUG_COUNTER_FROZEN   1
+#define BRW_VFE_DEBUG_COUNTER_ONCE     2
+#define BRW_VFE_DEBUG_COUNTER_ALWAYS   3
+
+/* VLD_STATE */
+#define BRW_MPEG_TOP_FIELD             1
+#define BRW_MPEG_BOTTOM_FIELD          2
+#define BRW_MPEG_FRAME                 3
+#define BRW_MPEG_QSCALE_LINEAR         0
+#define BRW_MPEG_QSCALE_NONLINEAR      1
+#define BRW_MPEG_ZIGZAG_SCAN           0
+#define BRW_MPEG_ALTER_VERTICAL_SCAN   1
+#define BRW_MPEG_I_PICTURE             1
+#define BRW_MPEG_P_PICTURE             2
+#define BRW_MPEG_B_PICTURE             3
+
+#endif
diff --git a/src/drm/cairo-drm-intel-brw-eu-emit.c b/src/drm/cairo-drm-intel-brw-eu-emit.c
new file mode 100644 (file)
index 0000000..05bac0a
--- /dev/null
@@ -0,0 +1,1089 @@
+/*
+   Copyright (C) Intel Corp.  2006.  All Rights Reserved.
+   Intel funded Tungsten Graphics (http://www.tungstengraphics.com) to
+   develop this 3D driver.
+
+   Permission is hereby granted, free of charge, to any person obtaining
+   a copy of this software and associated documentation files (the
+   "Software"), to deal in the Software without restriction, including
+   without limitation the rights to use, copy, modify, merge, publish,
+   distribute, sublicense, and/or sell copies of the Software, and to
+   permit persons to whom the Software is furnished to do so, subject to
+   the following conditions:
+
+   The above copyright notice and this permission notice (including the
+   next paragraph) shall be included in all copies or substantial
+   portions of the Software.
+
+   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+   IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
+   LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+   OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+   WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+ **********************************************************************/
+/*
+ * Authors:
+ *   Keith Whitwell <keith@tungstengraphics.com>
+ */
+
+#include "cairoint.h"
+#include "cairo-drm-intel-brw-eu.h"
+
+#include <string.h>
+
+/***********************************************************************
+ * Internal helper for constructing instructions
+ */
+
+static void guess_execution_size( struct brw_instruction *insn,
+                                 struct brw_reg reg )
+{
+    if (reg.width == BRW_WIDTH_8 &&
+       insn->header.compression_control == BRW_COMPRESSION_COMPRESSED)
+       insn->header.execution_size = BRW_EXECUTE_16;
+    else
+       insn->header.execution_size = reg.width;        /* note - definitions are compatible */
+}
+
+
+void
+brw_instruction_set_destination (struct brw_instruction *insn,
+                                struct brw_reg dest)
+{
+    insn->bits1.da1.dest_reg_file = dest.file;
+    insn->bits1.da1.dest_reg_type = dest.type;
+    insn->bits1.da1.dest_address_mode = dest.address_mode;
+
+    if (dest.address_mode == BRW_ADDRESS_DIRECT) {
+       insn->bits1.da1.dest_reg_nr = dest.nr;
+
+       if (insn->header.access_mode == BRW_ALIGN_1) {
+           insn->bits1.da1.dest_subreg_nr = dest.subnr;
+           if (dest.hstride == BRW_HORIZONTAL_STRIDE_0)
+               dest.hstride = BRW_HORIZONTAL_STRIDE_1;
+           insn->bits1.da1.dest_horiz_stride = dest.hstride;
+       } else {
+           insn->bits1.da16.dest_subreg_nr = dest.subnr / 16;
+           insn->bits1.da16.dest_writemask = dest.dw1.bits.writemask;
+       }
+    } else {
+       insn->bits1.ia1.dest_subreg_nr = dest.subnr;
+
+       /* These are different sizes in align1 vs align16:
+       */
+       if (insn->header.access_mode == BRW_ALIGN_1) {
+           insn->bits1.ia1.dest_indirect_offset = dest.dw1.bits.indirect_offset;
+           if (dest.hstride == BRW_HORIZONTAL_STRIDE_0)
+               dest.hstride = BRW_HORIZONTAL_STRIDE_1;
+           insn->bits1.ia1.dest_horiz_stride = dest.hstride;
+       } else {
+           insn->bits1.ia16.dest_indirect_offset = dest.dw1.bits.indirect_offset;
+       }
+    }
+
+    /* NEW: Set the execution size based on dest.width and
+     * insn->compression_control:
+     */
+    guess_execution_size(insn, dest);
+}
+
+void
+brw_instruction_set_source0 (struct brw_instruction *insn,
+                            struct brw_reg reg)
+{
+    assert(reg.file != BRW_MESSAGE_REGISTER_FILE);
+
+    insn->bits1.da1.src0_reg_file = reg.file;
+    insn->bits1.da1.src0_reg_type = reg.type;
+    insn->bits2.da1.src0_abs = reg.abs;
+    insn->bits2.da1.src0_negate = reg.negate;
+    insn->bits2.da1.src0_address_mode = reg.address_mode;
+
+    if (reg.file == BRW_IMMEDIATE_VALUE) {
+       insn->bits3.ud = reg.dw1.ud;
+
+       /* Required to set some fields in src1 as well:
+       */
+       insn->bits1.da1.src1_reg_file = 0; /* arf */
+       insn->bits1.da1.src1_reg_type = reg.type;
+    } else {
+       if (reg.address_mode == BRW_ADDRESS_DIRECT) {
+           if (insn->header.access_mode == BRW_ALIGN_1) {
+               insn->bits2.da1.src0_subreg_nr = reg.subnr;
+               insn->bits2.da1.src0_reg_nr = reg.nr;
+           } else {
+               insn->bits2.da16.src0_subreg_nr = reg.subnr / 16;
+               insn->bits2.da16.src0_reg_nr = reg.nr;
+           }
+       } else {
+           insn->bits2.ia1.src0_subreg_nr = reg.subnr;
+
+           if (insn->header.access_mode == BRW_ALIGN_1) {
+               insn->bits2.ia1.src0_indirect_offset = reg.dw1.bits.indirect_offset;
+           } else {
+               insn->bits2.ia16.src0_subreg_nr = reg.dw1.bits.indirect_offset;
+           }
+       }
+
+       if (insn->header.access_mode == BRW_ALIGN_1) {
+           if (reg.width == BRW_WIDTH_1 &&
+               insn->header.execution_size == BRW_EXECUTE_1) {
+               insn->bits2.da1.src0_horiz_stride = BRW_HORIZONTAL_STRIDE_0;
+               insn->bits2.da1.src0_width = BRW_WIDTH_1;
+               insn->bits2.da1.src0_vert_stride = BRW_VERTICAL_STRIDE_0;
+           } else {
+               insn->bits2.da1.src0_horiz_stride = reg.hstride;
+               insn->bits2.da1.src0_width = reg.width;
+               insn->bits2.da1.src0_vert_stride = reg.vstride;
+           }
+       } else {
+           insn->bits2.da16.src0_swz_x = BRW_GET_SWZ(reg.dw1.bits.swizzle, BRW_CHANNEL_X);
+           insn->bits2.da16.src0_swz_y = BRW_GET_SWZ(reg.dw1.bits.swizzle, BRW_CHANNEL_Y);
+           insn->bits2.da16.src0_swz_z = BRW_GET_SWZ(reg.dw1.bits.swizzle, BRW_CHANNEL_Z);
+           insn->bits2.da16.src0_swz_w = BRW_GET_SWZ(reg.dw1.bits.swizzle, BRW_CHANNEL_W);
+
+           /* This is an oddity of the fact we're using the same
+            * descriptions for registers in align_16 as align_1:
+            */
+           if (reg.vstride == BRW_VERTICAL_STRIDE_8)
+               insn->bits2.da16.src0_vert_stride = BRW_VERTICAL_STRIDE_4;
+           else
+               insn->bits2.da16.src0_vert_stride = reg.vstride;
+       }
+    }
+}
+
+
+void brw_set_src1( struct brw_instruction *insn,
+                  struct brw_reg reg )
+{
+    assert(reg.file != BRW_MESSAGE_REGISTER_FILE);
+
+    insn->bits1.da1.src1_reg_file = reg.file;
+    insn->bits1.da1.src1_reg_type = reg.type;
+    insn->bits3.da1.src1_abs = reg.abs;
+    insn->bits3.da1.src1_negate = reg.negate;
+
+    /* Only src1 can be immediate in two-argument instructions.
+    */
+    assert(insn->bits1.da1.src0_reg_file != BRW_IMMEDIATE_VALUE);
+
+    if (reg.file == BRW_IMMEDIATE_VALUE) {
+       insn->bits3.ud = reg.dw1.ud;
+    }
+    else {
+       /* This is a hardware restriction, which may or may not be lifted
+        * in the future:
+        */
+       assert (reg.address_mode == BRW_ADDRESS_DIRECT);
+       //assert (reg.file == BRW_GENERAL_REGISTER_FILE);
+
+       if (insn->header.access_mode == BRW_ALIGN_1) {
+           insn->bits3.da1.src1_subreg_nr = reg.subnr;
+           insn->bits3.da1.src1_reg_nr = reg.nr;
+       }
+       else {
+           insn->bits3.da16.src1_subreg_nr = reg.subnr / 16;
+           insn->bits3.da16.src1_reg_nr = reg.nr;
+       }
+
+       if (insn->header.access_mode == BRW_ALIGN_1) {
+           if (reg.width == BRW_WIDTH_1 &&
+               insn->header.execution_size == BRW_EXECUTE_1) {
+               insn->bits3.da1.src1_horiz_stride = BRW_HORIZONTAL_STRIDE_0;
+               insn->bits3.da1.src1_width = BRW_WIDTH_1;
+               insn->bits3.da1.src1_vert_stride = BRW_VERTICAL_STRIDE_0;
+           }
+           else {
+               insn->bits3.da1.src1_horiz_stride = reg.hstride;
+               insn->bits3.da1.src1_width = reg.width;
+               insn->bits3.da1.src1_vert_stride = reg.vstride;
+           }
+       }
+       else {
+           insn->bits3.da16.src1_swz_x = BRW_GET_SWZ(reg.dw1.bits.swizzle, BRW_CHANNEL_X);
+           insn->bits3.da16.src1_swz_y = BRW_GET_SWZ(reg.dw1.bits.swizzle, BRW_CHANNEL_Y);
+           insn->bits3.da16.src1_swz_z = BRW_GET_SWZ(reg.dw1.bits.swizzle, BRW_CHANNEL_Z);
+           insn->bits3.da16.src1_swz_w = BRW_GET_SWZ(reg.dw1.bits.swizzle, BRW_CHANNEL_W);
+
+           /* This is an oddity of the fact we're using the same
+            * descriptions for registers in align_16 as align_1:
+            */
+           if (reg.vstride == BRW_VERTICAL_STRIDE_8)
+               insn->bits3.da16.src1_vert_stride = BRW_VERTICAL_STRIDE_4;
+           else
+               insn->bits3.da16.src1_vert_stride = reg.vstride;
+       }
+    }
+}
+
+
+
+static void brw_set_math_message( struct brw_instruction *insn,
+                                 uint32_t msg_length,
+                                 uint32_t response_length,
+                                 uint32_t function,
+                                 uint32_t integer_type,
+                                 int low_precision,
+                                 int saturate,
+                                 uint32_t dataType )
+{
+    brw_set_src1 (insn, brw_imm_d (0));
+
+    insn->bits3.math.function = function;
+    insn->bits3.math.int_type = integer_type;
+    insn->bits3.math.precision = low_precision;
+    insn->bits3.math.saturate = saturate;
+    insn->bits3.math.data_type = dataType;
+    insn->bits3.math.response_length = response_length;
+    insn->bits3.math.msg_length = msg_length;
+    insn->bits3.math.msg_target = BRW_MESSAGE_TARGET_MATH;
+    insn->bits3.math.end_of_thread = 0;
+}
+
+static void brw_set_urb_message( struct brw_instruction *insn,
+                                int allocate,
+                                int used,
+                                uint32_t msg_length,
+                                uint32_t response_length,
+                                int end_of_thread,
+                                int complete,
+                                uint32_t offset,
+                                uint32_t swizzle_control )
+{
+    brw_set_src1 (insn, brw_imm_d (0));
+
+    insn->bits3.urb.opcode = 0;        /* ? */
+    insn->bits3.urb.offset = offset;
+    insn->bits3.urb.swizzle_control = swizzle_control;
+    insn->bits3.urb.allocate = allocate;
+    insn->bits3.urb.used = used;       /* ? */
+    insn->bits3.urb.complete = complete;
+    insn->bits3.urb.response_length = response_length;
+    insn->bits3.urb.msg_length = msg_length;
+    insn->bits3.urb.msg_target = BRW_MESSAGE_TARGET_URB;
+    insn->bits3.urb.end_of_thread = end_of_thread;
+}
+
+void
+brw_instruction_set_dp_write_message (struct brw_instruction *insn,
+                                     uint32_t binding_table_index,
+                                     uint32_t msg_control,
+                                     uint32_t msg_type,
+                                     uint32_t msg_length,
+                                     uint32_t pixel_scoreboard_clear,
+                                     uint32_t response_length,
+                                     uint32_t end_of_thread)
+{
+    brw_set_src1 (insn, brw_imm_d (0));
+
+    insn->bits3.dp_write.binding_table_index = binding_table_index;
+    insn->bits3.dp_write.msg_control = msg_control;
+    insn->bits3.dp_write.pixel_scoreboard_clear = pixel_scoreboard_clear;
+    insn->bits3.dp_write.msg_type = msg_type;
+    insn->bits3.dp_write.send_commit_msg = 0;
+    insn->bits3.dp_write.response_length = response_length;
+    insn->bits3.dp_write.msg_length = msg_length;
+    insn->bits3.dp_write.msg_target = BRW_MESSAGE_TARGET_DATAPORT_WRITE;
+    insn->bits3.urb.end_of_thread = end_of_thread;
+}
+
+static void brw_set_dp_read_message( struct brw_instruction *insn,
+                                    uint32_t binding_table_index,
+                                    uint32_t msg_control,
+                                    uint32_t msg_type,
+                                    uint32_t target_cache,
+                                    uint32_t msg_length,
+                                    uint32_t response_length,
+                                    uint32_t end_of_thread )
+{
+    brw_set_src1 (insn, brw_imm_d (0));
+
+    insn->bits3.dp_read.binding_table_index = binding_table_index;
+    insn->bits3.dp_read.msg_control = msg_control;
+    insn->bits3.dp_read.msg_type = msg_type;
+    insn->bits3.dp_read.target_cache = target_cache;
+    insn->bits3.dp_read.response_length = response_length;
+    insn->bits3.dp_read.msg_length = msg_length;
+    insn->bits3.dp_read.msg_target = BRW_MESSAGE_TARGET_DATAPORT_READ;
+    insn->bits3.dp_read.end_of_thread = end_of_thread;
+}
+
+static void
+brw_set_sampler_message (struct brw_instruction *insn,
+                        cairo_bool_t is_g4x,
+                        uint32_t binding_table_index,
+                        uint32_t sampler,
+                        uint32_t msg_type,
+                        uint32_t response_length,
+                        uint32_t msg_length,
+                        cairo_bool_t eot)
+{
+    brw_set_src1 (insn, brw_imm_d (0));
+
+    if (is_g4x) {
+       /* XXX presume the driver is sane! */
+       insn->bits3.sampler_g4x.binding_table_index = binding_table_index;
+       insn->bits3.sampler_g4x.sampler = sampler;
+       insn->bits3.sampler_g4x.msg_type = msg_type;
+       insn->bits3.sampler_g4x.response_length = response_length;
+       insn->bits3.sampler_g4x.msg_length = msg_length;
+       insn->bits3.sampler_g4x.end_of_thread = eot;
+       insn->bits3.sampler_g4x.msg_target = BRW_MESSAGE_TARGET_SAMPLER;
+    } else {
+       insn->bits3.sampler.binding_table_index = binding_table_index;
+       insn->bits3.sampler.sampler = sampler;
+       insn->bits3.sampler.msg_type = msg_type;
+       insn->bits3.sampler.return_format = BRW_SAMPLER_RETURN_FORMAT_FLOAT32;
+       insn->bits3.sampler.response_length = response_length;
+       insn->bits3.sampler.msg_length = msg_length;
+       insn->bits3.sampler.end_of_thread = eot;
+       insn->bits3.sampler.msg_target = BRW_MESSAGE_TARGET_SAMPLER;
+    }
+}
+
+struct brw_instruction *
+brw_next_instruction (struct brw_compile *p,
+                     uint32_t opcode)
+{
+    struct brw_instruction *insn;
+
+    assert(p->nr_insn + 1 < BRW_EU_MAX_INSN);
+
+    insn = &p->store[p->nr_insn++];
+    memcpy(insn, p->current, sizeof(*insn));
+
+    /* Reset this one-shot flag: */
+    if (p->current->header.destreg__conditonalmod) {
+       p->current->header.destreg__conditonalmod = 0;
+       p->current->header.predicate_control = BRW_PREDICATE_NORMAL;
+    }
+
+    insn->header.opcode = opcode;
+    return insn;
+}
+
+static struct brw_instruction *brw_alu1( struct brw_compile *p,
+                                        uint32_t opcode,
+                                        struct brw_reg dest,
+                                        struct brw_reg src )
+{
+    struct brw_instruction *insn = brw_next_instruction(p, opcode);
+    brw_instruction_set_destination(insn, dest);
+    brw_instruction_set_source0(insn, src);
+    return insn;
+}
+
+static struct brw_instruction *brw_alu2(struct brw_compile *p,
+                                       uint32_t opcode,
+                                       struct brw_reg dest,
+                                       struct brw_reg src0,
+                                       struct brw_reg src1 )
+{
+    struct brw_instruction *insn = brw_next_instruction(p, opcode);
+    brw_instruction_set_destination(insn, dest);
+    brw_instruction_set_source0(insn, src0);
+    brw_set_src1(insn, src1);
+    return insn;
+}
+
+
+/***********************************************************************
+ * Convenience routines.
+ */
+#define ALU1(OP)                                       \
+    struct brw_instruction *brw_##OP(struct brw_compile *p,                    \
+                                    struct brw_reg dest,                       \
+                                    struct brw_reg src0)                       \
+{                                                      \
+    return brw_alu1(p, BRW_OPCODE_##OP, dest, src0);   \
+}
+
+#define ALU2(OP)                                       \
+    struct brw_instruction *brw_##OP(struct brw_compile *p,                    \
+                                    struct brw_reg dest,                       \
+                                    struct brw_reg src0,                       \
+                                    struct brw_reg src1)                       \
+{                                                      \
+    return brw_alu2(p, BRW_OPCODE_##OP, dest, src0, src1);     \
+}
+
+
+    ALU1(MOV)
+    ALU2(SEL)
+    ALU1(NOT)
+    ALU2(AND)
+    ALU2(OR)
+    ALU2(XOR)
+    ALU2(SHR)
+    ALU2(SHL)
+    ALU2(RSR)
+    ALU2(RSL)
+    ALU2(ASR)
+    ALU2(ADD)
+    ALU2(MUL)
+    ALU1(FRC)
+    ALU1(RNDD)
+    ALU1(RNDZ)
+    ALU2(MAC)
+    ALU2(MACH)
+    ALU1(LZD)
+    ALU2(DP4)
+    ALU2(DPH)
+    ALU2(DP3)
+    ALU2(DP2)
+ALU2(LINE)
+
+
+
+
+void brw_NOP(struct brw_compile *p)
+{
+    struct brw_instruction *insn = brw_next_instruction(p, BRW_OPCODE_NOP);
+    brw_instruction_set_destination(insn, retype(brw_vec4_grf(0,0), BRW_REGISTER_TYPE_UD));
+    brw_instruction_set_source0(insn, retype(brw_vec4_grf(0,0), BRW_REGISTER_TYPE_UD));
+    brw_set_src1(insn, brw_imm_ud(0x0));
+}
+
+
+
+
+
+/***********************************************************************
+ * Comparisons, if/else/endif
+ */
+
+struct brw_instruction *brw_JMPI(struct brw_compile *p,
+                                struct brw_reg dest,
+                                struct brw_reg src0,
+                                struct brw_reg src1)
+{
+    struct brw_instruction *insn = brw_alu2(p, BRW_OPCODE_JMPI, dest, src0, src1);
+
+    p->current->header.predicate_control = BRW_PREDICATE_NONE;
+
+    return insn;
+}
+
+/* EU takes the value from the flag register and pushes it onto some
+ * sort of a stack (presumably merging with any flag value already on
+ * the stack).  Within an if block, the flags at the top of the stack
+ * control execution on each channel of the unit, eg. on each of the
+ * 16 pixel values in our wm programs.
+ *
+ * When the matching 'else' instruction is reached (presumably by
+ * countdown of the instruction count patched in by our ELSE/ENDIF
+ * functions), the relevent flags are inverted.
+ *
+ * When the matching 'endif' instruction is reached, the flags are
+ * popped off.  If the stack is now empty, normal execution resumes.
+ *
+ * No attempt is made to deal with stack overflow (14 elements?).
+ */
+struct brw_instruction *brw_IF(struct brw_compile *p, uint32_t execute_size)
+{
+    struct brw_instruction *insn;
+
+    if (p->single_program_flow) {
+       assert(execute_size == BRW_EXECUTE_1);
+
+       insn = brw_next_instruction(p, BRW_OPCODE_ADD);
+       insn->header.predicate_inverse = 1;
+    } else {
+       insn = brw_next_instruction(p, BRW_OPCODE_IF);
+    }
+
+    /* Override the defaults for this instruction:
+    */
+    brw_instruction_set_destination (insn, brw_ip_reg ());
+    brw_instruction_set_source0 (insn, brw_ip_reg ());
+    brw_set_src1 (insn, brw_imm_d (0));
+
+    insn->header.execution_size = execute_size;
+    insn->header.compression_control = BRW_COMPRESSION_NONE;
+    insn->header.predicate_control = BRW_PREDICATE_NORMAL;
+    insn->header.mask_control = BRW_MASK_ENABLE;
+    if (!p->single_program_flow)
+       insn->header.thread_control = BRW_THREAD_SWITCH;
+
+    p->current->header.predicate_control = BRW_PREDICATE_NONE;
+
+    return insn;
+}
+
+
+struct brw_instruction *brw_ELSE(struct brw_compile *p,
+                                struct brw_instruction *if_insn)
+{
+    struct brw_instruction *insn;
+
+    if (p->single_program_flow) {
+       insn = brw_next_instruction(p, BRW_OPCODE_ADD);
+    } else {
+       insn = brw_next_instruction(p, BRW_OPCODE_ELSE);
+    }
+
+    brw_instruction_set_destination (insn, brw_ip_reg ());
+    brw_instruction_set_source0 (insn, brw_ip_reg ());
+    brw_set_src1 (insn, brw_imm_d (0));
+
+    insn->header.compression_control = BRW_COMPRESSION_NONE;
+    insn->header.execution_size = if_insn->header.execution_size;
+    insn->header.mask_control = BRW_MASK_ENABLE;
+    if (!p->single_program_flow)
+       insn->header.thread_control = BRW_THREAD_SWITCH;
+
+    /* Patch the if instruction to point at this instruction.
+    */
+    if (p->single_program_flow) {
+       assert(if_insn->header.opcode == BRW_OPCODE_ADD);
+
+       if_insn->bits3.ud = (insn - if_insn + 1) * 16;
+    } else {
+       assert(if_insn->header.opcode == BRW_OPCODE_IF);
+
+       if_insn->bits3.if_else.jump_count = insn - if_insn;
+       if_insn->bits3.if_else.pop_count = 1;
+       if_insn->bits3.if_else.pad0 = 0;
+    }
+
+    return insn;
+}
+
+void brw_ENDIF(struct brw_compile *p,
+              struct brw_instruction *patch_insn)
+{
+    if (p->single_program_flow) {
+       /* In single program flow mode, there's no need to execute an ENDIF,
+        * since we don't need to do any stack operations, and if we're executing
+        * currently, we want to just continue executing.
+        */
+       struct brw_instruction *next = &p->store[p->nr_insn];
+
+       assert(patch_insn->header.opcode == BRW_OPCODE_ADD);
+
+       patch_insn->bits3.ud = (next - patch_insn) * 16;
+    } else {
+       struct brw_instruction *insn = brw_next_instruction(p, BRW_OPCODE_ENDIF);
+
+       brw_instruction_set_destination(insn, retype(brw_vec4_grf(0,0), BRW_REGISTER_TYPE_UD));
+       brw_instruction_set_source0(insn, retype(brw_vec4_grf(0,0), BRW_REGISTER_TYPE_UD));
+       brw_set_src1 (insn, brw_imm_d (0));
+
+       insn->header.compression_control = BRW_COMPRESSION_NONE;
+       insn->header.execution_size = patch_insn->header.execution_size;
+       insn->header.mask_control = BRW_MASK_ENABLE;
+       insn->header.thread_control = BRW_THREAD_SWITCH;
+
+       assert(patch_insn->bits3.if_else.jump_count == 0);
+
+       /* Patch the if or else instructions to point at this or the next
+        * instruction respectively.
+        */
+       if (patch_insn->header.opcode == BRW_OPCODE_IF) {
+           /* Automagically turn it into an IFF:
+           */
+           patch_insn->header.opcode = BRW_OPCODE_IFF;
+           patch_insn->bits3.if_else.jump_count = insn - patch_insn + 1;
+           patch_insn->bits3.if_else.pop_count = 0;
+           patch_insn->bits3.if_else.pad0 = 0;
+       } else if (patch_insn->header.opcode == BRW_OPCODE_ELSE) {
+           patch_insn->bits3.if_else.jump_count = insn - patch_insn + 1;
+           patch_insn->bits3.if_else.pop_count = 1;
+           patch_insn->bits3.if_else.pad0 = 0;
+       } else {
+           assert(0);
+       }
+
+       /* Also pop item off the stack in the endif instruction:
+       */
+       insn->bits3.if_else.jump_count = 0;
+       insn->bits3.if_else.pop_count = 1;
+       insn->bits3.if_else.pad0 = 0;
+    }
+}
+
+struct brw_instruction *brw_BREAK(struct brw_compile *p)
+{
+    struct brw_instruction *insn;
+    insn = brw_next_instruction(p, BRW_OPCODE_BREAK);
+    brw_instruction_set_destination(insn, brw_ip_reg());
+    brw_instruction_set_source0(insn, brw_ip_reg());
+    brw_set_src1(insn, brw_imm_d (0));
+    insn->header.compression_control = BRW_COMPRESSION_NONE;
+    insn->header.execution_size = BRW_EXECUTE_8;
+    /* insn->header.mask_control = BRW_MASK_DISABLE; */
+    insn->bits3.if_else.pad0 = 0;
+    return insn;
+}
+
+struct brw_instruction *brw_CONT(struct brw_compile *p)
+{
+    struct brw_instruction *insn;
+    insn = brw_next_instruction(p, BRW_OPCODE_CONTINUE);
+    brw_instruction_set_destination(insn, brw_ip_reg());
+    brw_instruction_set_source0(insn, brw_ip_reg());
+    brw_set_src1 (insn, brw_imm_d (0));
+    insn->header.compression_control = BRW_COMPRESSION_NONE;
+    insn->header.execution_size = BRW_EXECUTE_8;
+    /* insn->header.mask_control = BRW_MASK_DISABLE; */
+    insn->bits3.if_else.pad0 = 0;
+    return insn;
+}
+
+/* DO/WHILE loop:
+*/
+struct brw_instruction *brw_DO(struct brw_compile *p, uint32_t execute_size)
+{
+    if (p->single_program_flow) {
+       return &p->store[p->nr_insn];
+    } else {
+       struct brw_instruction *insn = brw_next_instruction(p, BRW_OPCODE_DO);
+
+       /* Override the defaults for this instruction:
+       */
+       brw_instruction_set_destination(insn, brw_null_reg());
+       brw_instruction_set_source0(insn, brw_null_reg());
+       brw_set_src1(insn, brw_null_reg());
+
+       insn->header.compression_control = BRW_COMPRESSION_NONE;
+       insn->header.execution_size = execute_size;
+       insn->header.predicate_control = BRW_PREDICATE_NONE;
+       /* insn->header.mask_control = BRW_MASK_ENABLE; */
+       /* insn->header.mask_control = BRW_MASK_DISABLE; */
+
+       return insn;
+    }
+}
+
+
+
+struct brw_instruction *brw_WHILE(struct brw_compile *p,
+                                 struct brw_instruction *do_insn)
+{
+    struct brw_instruction *insn;
+
+    if (p->single_program_flow)
+       insn = brw_next_instruction(p, BRW_OPCODE_ADD);
+    else
+       insn = brw_next_instruction(p, BRW_OPCODE_WHILE);
+
+    brw_instruction_set_destination(insn, brw_ip_reg());
+    brw_instruction_set_source0(insn, brw_ip_reg());
+    brw_set_src1 (insn, brw_imm_d (0));
+
+    insn->header.compression_control = BRW_COMPRESSION_NONE;
+
+    if (p->single_program_flow) {
+       insn->header.execution_size = BRW_EXECUTE_1;
+
+       insn->bits3.d = (do_insn - insn) * 16;
+    } else {
+       insn->header.execution_size = do_insn->header.execution_size;
+
+       assert(do_insn->header.opcode == BRW_OPCODE_DO);
+       insn->bits3.if_else.jump_count = do_insn - insn + 1;
+       insn->bits3.if_else.pop_count = 0;
+       insn->bits3.if_else.pad0 = 0;
+    }
+
+    /*    insn->header.mask_control = BRW_MASK_ENABLE; */
+
+    /* insn->header.mask_control = BRW_MASK_DISABLE; */
+    p->current->header.predicate_control = BRW_PREDICATE_NONE;
+    return insn;
+}
+
+
+/* FORWARD JUMPS:
+*/
+void brw_land_fwd_jump(struct brw_compile *p,
+                      struct brw_instruction *jmp_insn)
+{
+    struct brw_instruction *landing = &p->store[p->nr_insn];
+
+    assert(jmp_insn->header.opcode == BRW_OPCODE_JMPI);
+    assert(jmp_insn->bits1.da1.src1_reg_file = BRW_IMMEDIATE_VALUE);
+
+    jmp_insn->bits3.ud = (landing - jmp_insn) - 1;
+}
+
+
+
+/* To integrate with the above, it makes sense that the comparison
+ * instruction should populate the flag register.  It might be simpler
+ * just to use the flag reg for most WM tasks?
+ */
+void brw_CMP(struct brw_compile *p,
+            struct brw_reg dest,
+            uint32_t conditional,
+            struct brw_reg src0,
+            struct brw_reg src1)
+{
+    struct brw_instruction *insn = brw_next_instruction(p, BRW_OPCODE_CMP);
+
+    insn->header.destreg__conditonalmod = conditional;
+    brw_instruction_set_destination(insn, dest);
+    brw_instruction_set_source0(insn, src0);
+    brw_set_src1(insn, src1);
+
+    /*    guess_execution_size(insn, src0); */
+
+
+    /* Make it so that future instructions will use the computed flag
+     * value until brw_set_predicate_control_flag_value() is called
+     * again.
+     */
+    if (dest.file == BRW_ARCHITECTURE_REGISTER_FILE &&
+       dest.nr == 0) {
+       p->current->header.predicate_control = BRW_PREDICATE_NORMAL;
+       p->flag_value = 0xff;
+    }
+}
+
+
+
+/***********************************************************************
+ * Helpers for the various SEND message types:
+ */
+
+/* Invert 8 values
+*/
+void brw_math( struct brw_compile *p,
+              struct brw_reg dest,
+              uint32_t function,
+              uint32_t saturate,
+              uint32_t msg_reg_nr,
+              struct brw_reg src,
+              uint32_t data_type,
+              uint32_t precision )
+{
+    struct brw_instruction *insn = brw_next_instruction(p, BRW_OPCODE_SEND);
+    uint32_t msg_length = (function == BRW_MATH_FUNCTION_POW) ? 2 : 1;
+    uint32_t response_length = (function == BRW_MATH_FUNCTION_SINCOS) ? 2 : 1;
+
+    /* Example code doesn't set predicate_control for send
+     * instructions.
+     */
+    insn->header.predicate_control = 0;
+    insn->header.destreg__conditonalmod = msg_reg_nr;
+
+    response_length = 1;
+
+    brw_instruction_set_destination(insn, dest);
+    brw_instruction_set_source0(insn, src);
+    brw_set_math_message(insn,
+                        msg_length, response_length,
+                        function,
+                        BRW_MATH_INTEGER_UNSIGNED,
+                        precision,
+                        saturate,
+                        data_type);
+}
+
+/* Use 2 send instructions to invert 16 elements
+*/
+void brw_math_16( struct brw_compile *p,
+                 struct brw_reg dest,
+                 uint32_t function,
+                 uint32_t saturate,
+                 uint32_t msg_reg_nr,
+                 struct brw_reg src,
+                 uint32_t precision )
+{
+    struct brw_instruction *insn;
+    uint32_t msg_length = (function == BRW_MATH_FUNCTION_POW) ? 2 : 1;
+    uint32_t response_length = (function == BRW_MATH_FUNCTION_SINCOS) ? 2 : 1;
+
+    /* First instruction:
+    */
+    brw_push_insn_state(p);
+    brw_set_predicate_control_flag_value(p, 0xff);
+    brw_set_compression_control(p, BRW_COMPRESSION_NONE);
+
+    insn = brw_next_instruction(p, BRW_OPCODE_SEND);
+    insn->header.destreg__conditonalmod = msg_reg_nr;
+
+    brw_instruction_set_destination(insn, dest);
+    brw_instruction_set_source0(insn, src);
+    brw_set_math_message(insn,
+                        msg_length, response_length,
+                        function,
+                        BRW_MATH_INTEGER_UNSIGNED,
+                        precision,
+                        saturate,
+                        BRW_MATH_DATA_VECTOR);
+
+    /* Second instruction:
+    */
+    insn = brw_next_instruction(p, BRW_OPCODE_SEND);
+    insn->header.compression_control = BRW_COMPRESSION_2NDHALF;
+    insn->header.destreg__conditonalmod = msg_reg_nr+1;
+
+    brw_instruction_set_destination(insn, offset(dest,1));
+    brw_instruction_set_source0(insn, src);
+    brw_set_math_message(insn,
+                        msg_length, response_length,
+                        function,
+                        BRW_MATH_INTEGER_UNSIGNED,
+                        precision,
+                        saturate,
+                        BRW_MATH_DATA_VECTOR);
+
+    brw_pop_insn_state(p);
+}
+
+
+
+
+void brw_dp_WRITE_16( struct brw_compile *p,
+                     struct brw_reg src,
+                     uint32_t msg_reg_nr,
+                     uint32_t scratch_offset )
+{
+    {
+       brw_push_insn_state(p);
+       brw_set_mask_control(p, BRW_MASK_DISABLE);
+       brw_set_compression_control(p, BRW_COMPRESSION_NONE);
+
+       brw_MOV (p,
+               retype (brw_vec1_grf (0, 2), BRW_REGISTER_TYPE_D),
+               brw_imm_d (scratch_offset));
+
+       brw_pop_insn_state(p);
+    }
+
+    {
+       uint32_t msg_length = 3;
+       struct brw_reg dest = retype(brw_null_reg(), BRW_REGISTER_TYPE_UW);
+       struct brw_instruction *insn = brw_next_instruction(p, BRW_OPCODE_SEND);
+
+       insn->header.predicate_control = 0; /* XXX */
+       insn->header.compression_control = BRW_COMPRESSION_NONE;
+       insn->header.destreg__conditonalmod = msg_reg_nr;
+
+       brw_instruction_set_destination(insn, dest);
+       brw_instruction_set_source0(insn, src);
+
+       brw_instruction_set_dp_write_message(insn,
+                                            255, /* bti */
+                                            BRW_DATAPORT_OWORD_BLOCK_4_OWORDS, /* msg_control */
+                                            BRW_DATAPORT_WRITE_MESSAGE_OWORD_BLOCK_WRITE, /* msg_type */
+                                            msg_length,
+                                            0, /* pixel scoreboard */
+                                            0, /* response_length */
+                                            0); /* eot */
+    }
+
+}
+
+
+void brw_dp_READ_16( struct brw_compile *p,
+                    struct brw_reg dest,
+                    uint32_t msg_reg_nr,
+                    uint32_t scratch_offset )
+{
+    {
+       brw_push_insn_state(p);
+       brw_set_compression_control(p, BRW_COMPRESSION_NONE);
+       brw_set_mask_control(p, BRW_MASK_DISABLE);
+
+       brw_MOV (p,
+               retype (brw_vec1_grf (0, 2), BRW_REGISTER_TYPE_D),
+               brw_imm_d (scratch_offset));
+
+       brw_pop_insn_state(p);
+    }
+
+    {
+       struct brw_instruction *insn = brw_next_instruction(p, BRW_OPCODE_SEND);
+
+       insn->header.predicate_control = 0; /* XXX */
+       insn->header.compression_control = BRW_COMPRESSION_NONE;
+       insn->header.destreg__conditonalmod = msg_reg_nr;
+
+       brw_instruction_set_destination(insn, dest);    /* UW? */
+       brw_instruction_set_source0(insn, retype(brw_vec8_grf(0), BRW_REGISTER_TYPE_UW));
+
+       brw_set_dp_read_message(insn,
+                               255, /* bti */
+                               3,  /* msg_control */
+                               BRW_DATAPORT_READ_MESSAGE_OWORD_BLOCK_READ, /* msg_type */
+                               1, /* target cache */
+                               1, /* msg_length */
+                               2, /* response_length */
+                               0); /* eot */
+    }
+}
+
+
+void brw_fb_WRITE(struct brw_compile *p,
+                 struct brw_reg dest,
+                 uint32_t msg_reg_nr,
+                 struct brw_reg src0,
+                 uint32_t binding_table_index,
+                 uint32_t msg_length,
+                 uint32_t response_length,
+                 int eot)
+{
+    struct brw_instruction *insn = brw_next_instruction(p, BRW_OPCODE_SEND);
+
+    insn->header.predicate_control = 0; /* XXX */
+    insn->header.compression_control = BRW_COMPRESSION_NONE;
+    insn->header.destreg__conditonalmod = msg_reg_nr;
+
+    brw_instruction_set_destination(insn, dest);
+    brw_instruction_set_source0(insn, src0);
+    brw_instruction_set_dp_write_message(insn,
+                                        binding_table_index,
+                                        BRW_DATAPORT_RENDER_TARGET_WRITE_SIMD16_SINGLE_SOURCE, /* msg_control */
+                                        BRW_DATAPORT_WRITE_MESSAGE_RENDER_TARGET_WRITE, /* msg_type */
+                                        msg_length,
+                                        1,     /* pixel scoreboard */
+                                        response_length,
+                                        eot);
+}
+
+
+
+void brw_SAMPLE (struct brw_compile *p,
+                struct brw_reg dest,
+                uint32_t msg_reg_nr,
+                struct brw_reg src0,
+                uint32_t binding_table_index,
+                uint32_t sampler,
+                uint32_t writemask,
+                uint32_t msg_type,
+                uint32_t response_length,
+                uint32_t msg_length,
+                cairo_bool_t eot)
+{
+    int need_stall = 0;
+
+    if(writemask == 0) {
+       /*       printf("%s: zero writemask??\n", __FUNCTION__); */
+       return;
+    }
+
+    /* Hardware doesn't do destination dependency checking on send
+     * instructions properly.  Add a workaround which generates the
+     * dependency by other means.  In practice it seems like this bug
+     * only crops up for texture samples, and only where registers are
+     * written by the send and then written again later without being
+     * read in between.  Luckily for us, we already track that
+     * information and use it to modify the writemask for the
+     * instruction, so that is a guide for whether a workaround is
+     * needed.
+     */
+    if (writemask != WRITEMASK_XYZW) {
+       uint32_t dst_offset = 0;
+       uint32_t i, newmask = 0, len = 0;
+
+       for (i = 0; i < 4; i++) {
+           if (writemask & (1<<i))
+               break;
+           dst_offset += 2;
+       }
+       for (; i < 4; i++) {
+           if (!(writemask & (1<<i)))
+               break;
+           newmask |= 1<<i;
+           len++;
+       }
+
+       if (newmask != writemask) {
+           need_stall = 1;
+           /*   printf("need stall %x %x\n", newmask , writemask); */
+       }
+       else {
+           struct brw_reg m1 = brw_message_reg(msg_reg_nr);
+
+           newmask = ~newmask & WRITEMASK_XYZW;
+
+           brw_push_insn_state(p);
+
+           brw_set_compression_control(p, BRW_COMPRESSION_NONE);
+           brw_set_mask_control(p, BRW_MASK_DISABLE);
+
+           brw_MOV(p, m1, brw_vec8_grf(0));
+           brw_MOV(p, get_element_ud(m1, 2), brw_imm_ud(newmask << 12));
+
+           brw_pop_insn_state(p);
+
+           src0 = retype(brw_null_reg(), BRW_REGISTER_TYPE_UW);
+           dest = offset(dest, dst_offset);
+           response_length = len * 2;
+       }
+    }
+
+    {
+       struct brw_instruction *insn = brw_next_instruction(p, BRW_OPCODE_SEND);
+
+       insn->header.predicate_control = 0; /* XXX */
+       insn->header.compression_control = BRW_COMPRESSION_NONE;
+       insn->header.destreg__conditonalmod = msg_reg_nr;
+
+       brw_instruction_set_destination(insn, dest);
+       brw_instruction_set_source0(insn, src0);
+       brw_set_sampler_message (insn, p->is_g4x,
+                                binding_table_index,
+                                sampler,
+                                msg_type,
+                                response_length,
+                                msg_length,
+                                eot);
+    }
+
+    if (need_stall)
+    {
+       struct brw_reg reg = vec8(offset(dest, response_length-1));
+
+       /*  mov (8) r9.0<1>:f    r9.0<8;8,1>:f    { Align1 }
+       */
+       brw_push_insn_state(p);
+       brw_set_compression_control(p, 0);
+       brw_MOV(p, reg, reg);
+       brw_pop_insn_state(p);
+    }
+}
+
+/* All these variables are pretty confusing - we might be better off
+ * using bitmasks and macros for this, in the old style.  Or perhaps
+ * just having the caller instantiate the fields in dword3 itself.
+ */
+void brw_urb_WRITE(struct brw_compile *p,
+                  struct brw_reg dest,
+                  uint32_t msg_reg_nr,
+                  struct brw_reg src0,
+                  int allocate,
+                  int used,
+                  uint32_t msg_length,
+                  uint32_t response_length,
+                  int eot,
+                  int writes_complete,
+                  uint32_t offset,
+                  uint32_t swizzle)
+{
+    struct brw_instruction *insn = brw_next_instruction(p, BRW_OPCODE_SEND);
+
+    assert(msg_length < 16);
+
+    brw_instruction_set_destination (insn, dest);
+    brw_instruction_set_source0 (insn, src0);
+    brw_set_src1 (insn, brw_imm_d (0));
+
+    insn->header.destreg__conditonalmod = msg_reg_nr;
+
+    brw_set_urb_message (insn,
+                        allocate,
+                        used,
+                        msg_length,
+                        response_length,
+                        eot,
+                        writes_complete,
+                        offset,
+                        swizzle);
+}
diff --git a/src/drm/cairo-drm-intel-brw-eu-util.c b/src/drm/cairo-drm-intel-brw-eu-util.c
new file mode 100644 (file)
index 0000000..592235b
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+   Copyright (C) Intel Corp.  2006.  All Rights Reserved.
+   Intel funded Tungsten Graphics (http://www.tungstengraphics.com) to
+   develop this 3D driver.
+
+   Permission is hereby granted, free of charge, to any person obtaining
+   a copy of this software and associated documentation files (the
+   "Software"), to deal in the Software without restriction, including
+   without limitation the rights to use, copy, modify, merge, publish,
+   distribute, sublicense, and/or sell copies of the Software, and to
+   permit persons to whom the Software is furnished to do so, subject to
+   the following conditions:
+
+   The above copyright notice and this permission notice (including the
+   next paragraph) shall be included in all copies or substantial
+   portions of the Software.
+
+   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+   IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
+   LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+   OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+   WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+ **********************************************************************/
+/*
+ * Authors:
+ *   Keith Whitwell <keith@tungstengraphics.com>
+ */
+
+
+#include "cairoint.h"
+#include "cairo-drm-intel-brw-eu.h"
+
+
+void brw_math_invert( struct brw_compile *p,
+                     struct brw_reg dst,
+                     struct brw_reg src)
+{
+    brw_math( p,
+             dst,
+             BRW_MATH_FUNCTION_INV,
+             BRW_MATH_SATURATE_NONE,
+             0,
+             src,
+             BRW_MATH_PRECISION_FULL,
+             BRW_MATH_DATA_VECTOR );
+}
+
+
+
+void brw_copy4(struct brw_compile *p,
+              struct brw_reg dst,
+              struct brw_reg src,
+              uint32_t count)
+{
+    uint32_t i;
+
+    dst = vec4(dst);
+    src = vec4(src);
+
+    for (i = 0; i < count; i++)
+    {
+       uint32_t delta = i*32;
+       brw_MOV(p, byte_offset(dst, delta),    byte_offset(src, delta));
+       brw_MOV(p, byte_offset(dst, delta+16), byte_offset(src, delta+16));
+    }
+}
+
+
+void brw_copy8(struct brw_compile *p,
+              struct brw_reg dst,
+              struct brw_reg src,
+              uint32_t count)
+{
+    uint32_t i;
+
+    dst = vec8(dst);
+    src = vec8(src);
+
+    for (i = 0; i < count; i++)
+    {
+       uint32_t delta = i*32;
+       brw_MOV(p, byte_offset(dst, delta),    byte_offset(src, delta));
+    }
+}
+
+
+void brw_copy_indirect_to_indirect(struct brw_compile *p,
+                                  struct brw_indirect dst_ptr,
+                                  struct brw_indirect src_ptr,
+                                  uint32_t count)
+{
+    uint32_t i;
+
+    for (i = 0; i < count; i++)
+    {
+       uint32_t delta = i*32;
+       brw_MOV(p, deref_4f(dst_ptr, delta),    deref_4f(src_ptr, delta));
+       brw_MOV(p, deref_4f(dst_ptr, delta+16), deref_4f(src_ptr, delta+16));
+    }
+}
+
+
+void brw_copy_from_indirect(struct brw_compile *p,
+                           struct brw_reg dst,
+                           struct brw_indirect ptr,
+                           uint32_t count)
+{
+    uint32_t i;
+
+    dst = vec4(dst);
+
+    for (i = 0; i < count; i++)
+    {
+       uint32_t delta = i*32;
+       brw_MOV(p, byte_offset(dst, delta),    deref_4f(ptr, delta));
+       brw_MOV(p, byte_offset(dst, delta+16), deref_4f(ptr, delta+16));
+    }
+}
diff --git a/src/drm/cairo-drm-intel-brw-eu.c b/src/drm/cairo-drm-intel-brw-eu.c
new file mode 100644 (file)
index 0000000..51c3de4
--- /dev/null
@@ -0,0 +1,250 @@
+/*
+   Copyright (C) Intel Corp.  2006.  All Rights Reserved.
+   Intel funded Tungsten Graphics (http://www.tungstengraphics.com) to
+   develop this 3D driver.
+
+   Permission is hereby granted, free of charge, to any person obtaining
+   a copy of this software and associated documentation files (the
+   "Software"), to deal in the Software without restriction, including
+   without limitation the rights to use, copy, modify, merge, publish,
+   distribute, sublicense, and/or sell copies of the Software, and to
+   permit persons to whom the Software is furnished to do so, subject to
+   the following conditions:
+
+   The above copyright notice and this permission notice (including the
+   next paragraph) shall be included in all copies or substantial
+   portions of the Software.
+
+   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+   IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
+   LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+   OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+   WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+ **********************************************************************/
+/*
+ * Authors:
+ *   Keith Whitwell <keith@tungstengraphics.com>
+ */
+
+#include "cairoint.h"
+#include "cairo-drm-intel-brw-eu.h"
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
+
+/* How does predicate control work when execution_size != 8?  Do I
+ * need to test/set for 0xffff when execution_size is 16?
+ */
+void brw_set_predicate_control_flag_value( struct brw_compile *p, uint32_t value )
+{
+    p->current->header.predicate_control = BRW_PREDICATE_NONE;
+
+    if (value != 0xff) {
+       if (value != p->flag_value) {
+           brw_push_insn_state(p);
+           brw_MOV(p, brw_flag_reg(), brw_imm_uw(value));
+           p->flag_value = value;
+           brw_pop_insn_state(p);
+       }
+
+       p->current->header.predicate_control = BRW_PREDICATE_NORMAL;
+    }
+}
+
+void brw_set_predicate_control( struct brw_compile *p, uint32_t pc )
+{
+    p->current->header.predicate_control = pc;
+}
+
+void brw_set_conditionalmod( struct brw_compile *p, uint32_t conditional )
+{
+    p->current->header.destreg__conditonalmod = conditional;
+}
+
+void brw_set_access_mode( struct brw_compile *p, uint32_t access_mode )
+{
+    p->current->header.access_mode = access_mode;
+}
+
+void brw_set_compression_control( struct brw_compile *p, int compression_control )
+{
+    p->current->header.compression_control = compression_control;
+}
+
+void brw_set_mask_control( struct brw_compile *p, uint32_t value )
+{
+    p->current->header.mask_control = value;
+}
+
+void brw_set_saturate( struct brw_compile *p, uint32_t value )
+{
+    p->current->header.saturate = value;
+}
+
+void brw_push_insn_state( struct brw_compile *p )
+{
+    assert(p->current != &p->stack[BRW_EU_MAX_INSN_STACK-1]);
+    memcpy(p->current+1, p->current, sizeof(struct brw_instruction));
+    p->current++;
+}
+
+void brw_pop_insn_state( struct brw_compile *p )
+{
+    assert(p->current != p->stack);
+    p->current--;
+}
+
+/************************************************************************/
+void
+brw_compile_init (struct brw_compile *p,
+                 cairo_bool_t is_g4x)
+{
+    p->nr_insn = 0;
+    p->current = p->stack;
+    memset (p->current, 0, sizeof (p->current[0]));
+
+    p->is_g4x = is_g4x;
+
+    /* Some defaults?  */
+    brw_set_mask_control (p, BRW_MASK_ENABLE); /* what does this do? */
+    brw_set_saturate (p, 0);
+    brw_set_compression_control (p, BRW_COMPRESSION_NONE);
+    brw_set_predicate_control_flag_value (p, 0xff);
+}
+
+const uint32_t *
+brw_get_program (struct brw_compile *p,
+                uint32_t *sz)
+{
+    *sz = p->nr_insn * sizeof (struct brw_instruction);
+    return (const uint32_t *)p->store;
+}
+
+
+
+/**
+ * Subroutine calls require special attention.
+ * Mesa instructions may be expanded into multiple hardware instructions
+ * so the prog_instruction::BranchTarget field can't be used as an index
+ * into the hardware instructions.
+ *
+ * The BranchTarget field isn't needed, however.  Mesa's GLSL compiler
+ * emits CAL and BGNSUB instructions with labels that can be used to map
+ * subroutine calls to actual subroutine code blocks.
+ *
+ * The structures and function here implement patching of CAL instructions
+ * so they jump to the right subroutine code...
+ */
+
+
+/**
+ * For each OPCODE_BGNSUB we create one of these.
+ */
+struct brw_glsl_label
+{
+    const char *name; /**< the label string */
+    uint32_t position;  /**< the position of the brw instruction for this label */
+    struct brw_glsl_label *next;  /**< next in linked list */
+};
+
+
+/**
+ * For each OPCODE_CAL we create one of these.
+ */
+struct brw_glsl_call
+{
+    uint32_t call_inst_pos;  /**< location of the CAL instruction */
+    const char *sub_name;  /**< name of subroutine to call */
+    struct brw_glsl_call *next;  /**< next in linked list */
+};
+
+
+/**
+ * Called for each OPCODE_BGNSUB.
+ */
+    void
+brw_save_label(struct brw_compile *c, const char *name, uint32_t position)
+{
+    struct brw_glsl_label *label = calloc(1, sizeof *label);
+    label->name = name;
+    label->position = position;
+    label->next = c->first_label;
+    c->first_label = label;
+}
+
+
+/**
+ * Called for each OPCODE_CAL.
+ */
+    void
+brw_save_call(struct brw_compile *c, const char *name, uint32_t call_pos)
+{
+    struct brw_glsl_call *call = calloc(1, sizeof *call);
+    call->call_inst_pos = call_pos;
+    call->sub_name = name;
+    call->next = c->first_call;
+    c->first_call = call;
+}
+
+
+/**
+ * Lookup a label, return label's position/offset.
+ */
+    static uint32_t
+brw_lookup_label(struct brw_compile *c, const char *name)
+{
+    const struct brw_glsl_label *label;
+    for (label = c->first_label; label; label = label->next) {
+       if (strcmp(name, label->name) == 0) {
+           return label->position;
+       }
+    }
+    abort();  /* should never happen */
+    return ~0;
+}
+
+
+/**
+ * When we're done generating code, this function is called to resolve
+ * subroutine calls.
+ */
+    void
+brw_resolve_cals(struct brw_compile *c)
+{
+    const struct brw_glsl_call *call;
+
+    for (call = c->first_call; call; call = call->next) {
+       const uint32_t sub_loc = brw_lookup_label(c, call->sub_name);
+       struct brw_instruction *brw_call_inst = &c->store[call->call_inst_pos];
+       struct brw_instruction *brw_sub_inst = &c->store[sub_loc];
+       int32_t offset = brw_sub_inst - brw_call_inst;
+
+       /* patch brw_inst1 to point to brw_inst2 */
+       brw_set_src1(brw_call_inst, brw_imm_d(offset * 16));
+    }
+
+    /* free linked list of calls */
+    {
+       struct brw_glsl_call *call, *next;
+       for (call = c->first_call; call; call = next) {
+           next = call->next;
+           free(call);
+       }
+       c->first_call = NULL;
+    }
+
+    /* free linked list of labels */
+    {
+       struct brw_glsl_label *label, *next;
+       for (label = c->first_label; label; label = next) {
+           next = label->next;
+           free(label);
+       }
+       c->first_label = NULL;
+    }
+}
diff --git a/src/drm/cairo-drm-intel-brw-eu.h b/src/drm/cairo-drm-intel-brw-eu.h
new file mode 100644 (file)
index 0000000..197a371
--- /dev/null
@@ -0,0 +1,1043 @@
+/*
+   Copyright (C) Intel Corp.  2006.  All Rights Reserved.
+   Intel funded Tungsten Graphics (http://www.tungstengraphics.com) to
+   develop this 3D driver.
+
+   Permission is hereby granted, free of charge, to any person obtaining
+   a copy of this software and associated documentation files (the
+   "Software"), to deal in the Software without restriction, including
+   without limitation the rights to use, copy, modify, merge, publish,
+   distribute, sublicense, and/or sell copies of the Software, and to
+   permit persons to whom the Software is furnished to do so, subject to
+   the following conditions:
+
+   The above copyright notice and this permission notice (including the
+   next paragraph) shall be included in all copies or substantial
+   portions of the Software.
+
+   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+   IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
+   LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+   OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+   WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+ **********************************************************************/
+/*
+ * Authors:
+ *   Keith Whitwell <keith@tungstengraphics.com>
+ */
+
+#ifndef CAIRO_DRM_INTEL_BRW_EU_H
+#define CAIRO_DRM_INTEL_BRW_EU_H
+
+#include "cairo-drm-intel-brw-structs.h"
+#include "cairo-drm-intel-brw-defines.h"
+
+#include <assert.h>
+
+
+/**
+ * Writemask values, 1 bit per component.
+ */
+#define WRITEMASK_X     0x1
+#define WRITEMASK_Y     0x2
+#define WRITEMASK_Z     0x4
+#define WRITEMASK_W     0x8
+#define WRITEMASK_XY    (WRITEMASK_X | WRITEMASK_Y)
+#define WRITEMASK_XZ    (WRITEMASK_X | WRITEMASK_Z)
+#define WRITEMASK_YZ    (WRITEMASK_Y | WRITEMASK_Z)
+#define WRITEMASK_XYZ   (WRITEMASK_X | WRITEMASK_Y | WRITEMASK_Z)
+#define WRITEMASK_XW    (WRITEMASK_X | WRITEMASK_W)
+#define WRITEMASK_YW    (WRITEMASK_Y | WRITEMASK_W)
+#define WRITEMASK_XYW   (WRITEMASK_X | WRITEMASK_Y | WRITEMASK_W)
+#define WRITEMASK_ZW    (WRITEMASK_Z | WRITEMASK_W)
+#define WRITEMASK_XZW   (WRITEMASK_X | WRITEMASK_Z | WRITEMASK_W)
+#define WRITEMASK_YZW   (WRITEMASK_Y | WRITEMASK_Z | WRITEMASK_W)
+#define WRITEMASK_XYZW  (WRITEMASK_X | WRITEMASK_Y | WRITEMASK_Z | WRITEMASK_W)
+
+#define BRW_SWIZZLE4(a,b,c,d) (((a)<<0) | ((b)<<2) | ((c)<<4) | ((d)<<6))
+#define BRW_GET_SWZ(swz, idx) (((swz) >> ((idx)*2)) & 0x3)
+
+#define BRW_SWIZZLE_NOOP      BRW_SWIZZLE4 (0,1,2,3)
+#define BRW_SWIZZLE_XYZW      BRW_SWIZZLE4 (0,1,2,3)
+#define BRW_SWIZZLE_XXXX      BRW_SWIZZLE4 (0,0,0,0)
+#define BRW_SWIZZLE_XYXY      BRW_SWIZZLE4 (0,1,0,1)
+
+#define REG_SIZE (8*4)
+
+/* These aren't hardware structs, just something useful for us to pass around:
+ *
+ * Align1 operation has a lot of control over input ranges.  Used in
+ * WM programs to implement shaders decomposed into "channel serial"
+ * or "structure of array" form:
+ */
+struct brw_reg {
+   uint32_t type:4;
+   uint32_t file:2;
+   uint32_t nr:8;
+   uint32_t subnr:5;           /* :1 in align16 */
+   uint32_t negate:1;          /* source only */
+   uint32_t abs:1;             /* source only */
+   uint32_t vstride:4;         /* source only */
+   uint32_t width:3;           /* src only, align1 only */
+   uint32_t hstride:2;         /* align1 only */
+   uint32_t address_mode:1;    /* relative addressing, hopefully! */
+   uint32_t pad0:1;
+
+   union {
+      struct {
+        uint32_t swizzle:8;            /* src only, align16 only */
+        uint32_t writemask:4;          /* dest only, align16 only */
+        int32_t  indirect_offset:10;   /* relative addressing offset */
+        uint32_t pad1:10;              /* two dwords total */
+      } bits;
+
+      float f;
+      int32_t   d;
+      uint32_t ud;
+   } dw1;
+};
+
+struct brw_indirect {
+   uint32_t addr_subnr:4;
+   int32_t addr_offset:10;
+   uint32_t pad:18;
+};
+
+struct brw_glsl_label;
+struct brw_glsl_call;
+
+#define BRW_EU_MAX_INSN_STACK 5
+#define BRW_EU_MAX_INSN 200
+
+struct brw_compile {
+   struct brw_instruction store[BRW_EU_MAX_INSN];
+   uint32_t nr_insn;
+
+   cairo_bool_t is_g4x;
+
+   /* Allow clients to push/pop instruction state:
+    */
+   struct brw_instruction stack[BRW_EU_MAX_INSN_STACK];
+   struct brw_instruction *current;
+
+   uint32_t flag_value;
+   int single_program_flow;
+   struct brw_context *brw;
+
+   struct brw_glsl_label *first_label;  /**< linked list of labels */
+   struct brw_glsl_call *first_call;    /**< linked list of CALs */
+};
+
+cairo_private void
+brw_save_label (struct brw_compile *c,
+               const char *name,
+               uint32_t position);
+
+cairo_private void
+brw_save_call (struct brw_compile *c,
+              const char *name,
+              uint32_t call_pos);
+
+cairo_private void
+brw_resolve_cals (struct brw_compile *c);
+
+static cairo_always_inline int
+type_sz (uint32_t type)
+{
+   switch (type) {
+   case BRW_REGISTER_TYPE_UD:
+   case BRW_REGISTER_TYPE_D:
+   case BRW_REGISTER_TYPE_F:
+      return 4;
+   case BRW_REGISTER_TYPE_HF:
+   case BRW_REGISTER_TYPE_UW:
+   case BRW_REGISTER_TYPE_W:
+      return 2;
+   case BRW_REGISTER_TYPE_UB:
+   case BRW_REGISTER_TYPE_B:
+      return 1;
+   default:
+      return 0;
+   }
+}
+
+/**
+ * Construct a brw_reg.
+ * \param file  one of the BRW_x_REGISTER_FILE values
+ * \param nr  register number/index
+ * \param subnr  register sub number
+ * \param type  one of BRW_REGISTER_TYPE_x
+ * \param vstride  one of BRW_VERTICAL_STRIDE_x
+ * \param width  one of BRW_WIDTH_x
+ * \param hstride  one of BRW_HORIZONTAL_STRIDE_x
+ * \param swizzle  one of BRW_SWIZZLE_x
+ * \param writemask  WRITEMASK_X/Y/Z/W bitfield
+ */
+static cairo_always_inline struct brw_reg
+brw_reg (uint32_t file,
+        uint32_t nr,
+        uint32_t subnr,
+        uint32_t type,
+        uint32_t vstride,
+        uint32_t width,
+        uint32_t hstride,
+        uint32_t swizzle,
+        uint32_t writemask)
+{
+   struct brw_reg reg;
+
+   if (type == BRW_GENERAL_REGISTER_FILE)
+      assert(nr < 128);
+   else if (type == BRW_MESSAGE_REGISTER_FILE)
+      assert(nr < 9);
+   else if (type == BRW_ARCHITECTURE_REGISTER_FILE)
+      assert(nr <= BRW_ARF_IP);
+
+   reg.type = type;
+   reg.file = file;
+   reg.nr = nr;
+   reg.subnr = subnr * type_sz(type);
+   reg.negate = 0;
+   reg.abs = 0;
+   reg.vstride = vstride;
+   reg.width = width;
+   reg.hstride = hstride;
+   reg.address_mode = BRW_ADDRESS_DIRECT;
+   reg.pad0 = 0;
+
+   /* Could do better: If the reg is r5.3<0;1,0>, we probably want to
+    * set swizzle and writemask to W, as the lower bits of subnr will
+    * be lost when converted to align16.  This is probably too much to
+    * keep track of as you'd want it adjusted by suboffset(), etc.
+    * Perhaps fix up when converting to align16?
+    */
+   reg.dw1.bits.swizzle = swizzle;
+   reg.dw1.bits.writemask = writemask;
+   reg.dw1.bits.indirect_offset = 0;
+   reg.dw1.bits.pad1 = 0;
+
+   return reg;
+}
+
+/** Construct float[16] register */
+static cairo_always_inline struct brw_reg
+brw_vec16_reg (uint32_t file,
+              uint32_t nr,
+              uint32_t subnr)
+{
+    return brw_reg (file, nr, subnr,
+                   BRW_REGISTER_TYPE_F,
+                   BRW_VERTICAL_STRIDE_16,
+                   BRW_WIDTH_16,
+                   BRW_HORIZONTAL_STRIDE_1,
+                   BRW_SWIZZLE_XYZW,
+                   WRITEMASK_XYZW);
+}
+
+/** Construct float[8] register */
+static cairo_always_inline struct brw_reg
+brw_vec8_reg (uint32_t file,
+             uint32_t nr,
+             uint32_t subnr)
+{
+    return brw_reg (file, nr, subnr,
+                   BRW_REGISTER_TYPE_F,
+                   BRW_VERTICAL_STRIDE_8,
+                   BRW_WIDTH_8,
+                   BRW_HORIZONTAL_STRIDE_1,
+                   BRW_SWIZZLE_XYZW,
+                   WRITEMASK_XYZW);
+}
+
+/** Construct float[4] register */
+static cairo_always_inline struct brw_reg
+brw_vec4_reg (uint32_t file,
+             uint32_t nr,
+             uint32_t subnr)
+{
+   return brw_reg (file, nr, subnr,
+                  BRW_REGISTER_TYPE_F,
+                  BRW_VERTICAL_STRIDE_4,
+                  BRW_WIDTH_4,
+                  BRW_HORIZONTAL_STRIDE_1,
+                  BRW_SWIZZLE_XYZW,
+                  WRITEMASK_XYZW);
+}
+
+/** Construct float[2] register */
+static cairo_always_inline struct brw_reg
+brw_vec2_reg (uint32_t file,
+             uint32_t nr,
+             uint32_t subnr)
+{
+   return brw_reg (file, nr, subnr,
+                  BRW_REGISTER_TYPE_F,
+                  BRW_VERTICAL_STRIDE_2,
+                  BRW_WIDTH_2,
+                  BRW_HORIZONTAL_STRIDE_1,
+                  BRW_SWIZZLE_XYXY,
+                  WRITEMASK_XY);
+}
+
+/** Construct float[1] register */
+static cairo_always_inline struct brw_reg
+brw_vec1_reg (uint32_t file,
+             uint32_t nr,
+             uint32_t subnr)
+{
+   return brw_reg (file, nr, subnr,
+                  BRW_REGISTER_TYPE_F,
+                  BRW_VERTICAL_STRIDE_0,
+                  BRW_WIDTH_1,
+                  BRW_HORIZONTAL_STRIDE_0,
+                  BRW_SWIZZLE_XXXX,
+                  WRITEMASK_X);
+}
+
+static cairo_always_inline struct brw_reg
+retype (struct brw_reg reg,
+       uint32_t type)
+{
+   reg.type = type;
+   return reg;
+}
+
+static cairo_always_inline struct brw_reg
+suboffset (struct brw_reg reg,
+          uint32_t delta)
+{
+   reg.subnr += delta * type_sz (reg.type);
+   return reg;
+}
+
+static cairo_always_inline struct brw_reg
+offset (struct brw_reg reg,
+       uint32_t delta)
+{
+   reg.nr += delta;
+   return reg;
+}
+
+static cairo_always_inline struct brw_reg
+byte_offset (struct brw_reg reg,
+            uint32_t bytes)
+{
+   uint32_t newoffset = reg.nr * REG_SIZE + reg.subnr + bytes;
+   reg.nr = newoffset / REG_SIZE;
+   reg.subnr = newoffset % REG_SIZE;
+   return reg;
+}
+
+/** Construct unsigned word[16] register */
+static cairo_always_inline struct brw_reg
+brw_uw16_reg (uint32_t file,
+             uint32_t nr,
+             uint32_t subnr)
+{
+   return suboffset (retype (brw_vec16_reg (file, nr, 0), BRW_REGISTER_TYPE_UW), subnr);
+}
+
+/** Construct unsigned word[8] register */
+static cairo_always_inline struct brw_reg
+brw_uw8_reg (uint32_t file,
+            uint32_t nr,
+            uint32_t subnr)
+{
+   return suboffset (retype (brw_vec8_reg (file, nr, 0), BRW_REGISTER_TYPE_UW), subnr);
+}
+
+/** Construct unsigned word[2] register */
+static cairo_always_inline struct brw_reg
+brw_uw2_reg (uint32_t file,
+            uint32_t nr,
+            uint32_t subnr)
+{
+   return suboffset (retype (brw_vec2_reg (file, nr, 0), BRW_REGISTER_TYPE_UW), subnr);
+}
+
+/** Construct unsigned word[1] register */
+static cairo_always_inline struct brw_reg
+brw_uw1_reg (uint32_t file,
+            uint32_t nr,
+            uint32_t subnr)
+{
+   return suboffset (retype (brw_vec1_reg (file, nr, 0), BRW_REGISTER_TYPE_UW), subnr);
+}
+
+static cairo_always_inline struct brw_reg
+brw_imm_reg (uint32_t type)
+{
+   return brw_reg (BRW_IMMEDIATE_VALUE,
+                  0,
+                  0,
+                  type,
+                  BRW_VERTICAL_STRIDE_0,
+                  BRW_WIDTH_1,
+                  BRW_HORIZONTAL_STRIDE_0,
+                  0,
+                  0);
+}
+
+/** Construct float immediate register */
+static cairo_always_inline struct brw_reg brw_imm_f( float f )
+{
+   struct brw_reg imm = brw_imm_reg(BRW_REGISTER_TYPE_F);
+   imm.dw1.f = f;
+   return imm;
+}
+
+/** Construct integer immediate register */
+static cairo_always_inline struct brw_reg brw_imm_d( int32_t d )
+{
+   struct brw_reg imm = brw_imm_reg(BRW_REGISTER_TYPE_D);
+   imm.dw1.d = d;
+   return imm;
+}
+
+/** Construct uint immediate register */
+static cairo_always_inline struct brw_reg brw_imm_ud( uint32_t ud )
+{
+   struct brw_reg imm = brw_imm_reg(BRW_REGISTER_TYPE_UD);
+   imm.dw1.ud = ud;
+   return imm;
+}
+
+/** Construct ushort immediate register */
+static cairo_always_inline struct brw_reg brw_imm_uw( uint16_t uw )
+{
+   struct brw_reg imm = brw_imm_reg(BRW_REGISTER_TYPE_UW);
+   imm.dw1.ud = uw | (uw << 16);
+   return imm;
+}
+
+/** Construct short immediate register */
+static cairo_always_inline struct brw_reg brw_imm_w( int16_t w )
+{
+   struct brw_reg imm = brw_imm_reg(BRW_REGISTER_TYPE_W);
+   imm.dw1.d = w | (w << 16);
+   return imm;
+}
+
+/* brw_imm_b and brw_imm_ub aren't supported by hardware - the type
+ * numbers alias with _V and _VF below:
+ */
+
+/** Construct vector of eight signed half-byte values */
+static cairo_always_inline
+struct brw_reg brw_imm_v (uint32_t v)
+{
+   struct brw_reg imm = brw_imm_reg(BRW_REGISTER_TYPE_V);
+   imm.vstride = BRW_VERTICAL_STRIDE_0;
+   imm.width = BRW_WIDTH_8;
+   imm.hstride = BRW_HORIZONTAL_STRIDE_1;
+   imm.dw1.ud = v;
+   return imm;
+}
+
+/** Construct vector of four 8-bit float values */
+static cairo_always_inline struct brw_reg
+brw_imm_vf (uint32_t v)
+{
+   struct brw_reg imm = brw_imm_reg(BRW_REGISTER_TYPE_VF);
+   imm.vstride = BRW_VERTICAL_STRIDE_0;
+   imm.width = BRW_WIDTH_4;
+   imm.hstride = BRW_HORIZONTAL_STRIDE_1;
+   imm.dw1.ud = v;
+   return imm;
+}
+
+#define VF_ZERO 0x0
+#define VF_ONE  0x30
+#define VF_NEG  (1<<7)
+
+static cairo_always_inline struct brw_reg
+brw_imm_vf4 (uint32_t v0,
+            uint32_t v1,
+            uint32_t v2,
+            uint32_t v3)
+{
+   struct brw_reg imm = brw_imm_reg(BRW_REGISTER_TYPE_VF);
+   imm.vstride = BRW_VERTICAL_STRIDE_0;
+   imm.width = BRW_WIDTH_4;
+   imm.hstride = BRW_HORIZONTAL_STRIDE_1;
+   imm.dw1.ud = ((v0 << 0) |
+                (v1 << 8) |
+                (v2 << 16) |
+                (v3 << 24));
+   return imm;
+}
+
+static cairo_always_inline struct brw_reg
+brw_address (struct brw_reg reg)
+{
+   return brw_imm_uw (reg.nr * REG_SIZE + reg.subnr);
+}
+
+/** Construct float[1] general-purpose register */
+static cairo_always_inline struct brw_reg
+brw_vec1_grf (uint32_t nr, uint32_t subnr)
+{
+   return brw_vec1_reg (BRW_GENERAL_REGISTER_FILE, nr, subnr);
+}
+
+/** Construct float[2] general-purpose register */
+static cairo_always_inline struct brw_reg
+brw_vec2_grf (uint32_t nr, uint32_t subnr)
+{
+   return brw_vec2_reg (BRW_GENERAL_REGISTER_FILE, nr, subnr);
+}
+
+/** Construct float[4] general-purpose register */
+static cairo_always_inline struct brw_reg
+brw_vec4_grf (uint32_t nr, uint32_t subnr)
+{
+   return brw_vec4_reg (BRW_GENERAL_REGISTER_FILE, nr, subnr);
+}
+
+/** Construct float[8] general-purpose register */
+static cairo_always_inline struct brw_reg
+brw_vec8_grf (uint32_t nr)
+{
+   return brw_vec8_reg (BRW_GENERAL_REGISTER_FILE, nr, 0);
+}
+
+static cairo_always_inline struct brw_reg
+brw_uw8_grf (uint32_t nr, uint32_t subnr)
+{
+   return brw_uw8_reg (BRW_GENERAL_REGISTER_FILE, nr, subnr);
+}
+
+static cairo_always_inline struct brw_reg
+brw_uw16_grf (uint32_t nr, uint32_t subnr)
+{
+   return brw_uw16_reg (BRW_GENERAL_REGISTER_FILE, nr, subnr);
+}
+
+/** Construct null register (usually used for setting condition codes) */
+static cairo_always_inline struct brw_reg
+brw_null_reg (void)
+{
+   return brw_vec8_reg (BRW_ARCHITECTURE_REGISTER_FILE,
+                       BRW_ARF_NULL,
+                       0);
+}
+
+static cairo_always_inline struct brw_reg
+brw_address_reg (uint32_t subnr)
+{
+   return brw_uw1_reg (BRW_ARCHITECTURE_REGISTER_FILE,
+                      BRW_ARF_ADDRESS,
+                      subnr);
+}
+
+/* If/else instructions break in align16 mode if writemask & swizzle
+ * aren't xyzw.  This goes against the convention for other scalar
+ * regs:
+ */
+static cairo_always_inline struct brw_reg
+brw_ip_reg (void)
+{
+   return brw_reg (BRW_ARCHITECTURE_REGISTER_FILE,
+                  BRW_ARF_IP,
+                  0,
+                  BRW_REGISTER_TYPE_UD,
+                  BRW_VERTICAL_STRIDE_4, /* ? */
+                  BRW_WIDTH_1,
+                  BRW_HORIZONTAL_STRIDE_0,
+                  BRW_SWIZZLE_XYZW,
+                  WRITEMASK_XYZW);
+}
+
+static cairo_always_inline struct brw_reg
+brw_acc_reg (void)
+{
+   return brw_vec8_reg (BRW_ARCHITECTURE_REGISTER_FILE,
+                       BRW_ARF_ACCUMULATOR,
+                       0);
+}
+
+static cairo_always_inline struct brw_reg
+brw_flag_reg (void)
+{
+   return brw_uw1_reg (BRW_ARCHITECTURE_REGISTER_FILE,
+                      BRW_ARF_FLAG,
+                      0);
+}
+
+static cairo_always_inline struct brw_reg
+brw_mask_reg (uint32_t subnr)
+{
+   return brw_uw1_reg (BRW_ARCHITECTURE_REGISTER_FILE,
+                      BRW_ARF_MASK,
+                      subnr);
+}
+
+static cairo_always_inline struct brw_reg
+brw_message4_reg (uint32_t nr)
+{
+    return brw_vec4_reg (BRW_MESSAGE_REGISTER_FILE,
+                        nr,
+                        0);
+}
+
+static cairo_always_inline struct brw_reg
+brw_message_reg (uint32_t nr)
+{
+   return brw_vec8_reg (BRW_MESSAGE_REGISTER_FILE,
+                       nr,
+                       0);
+}
+
+/* This is almost always called with a numeric constant argument, so
+ * make things easy to evaluate at compile time:
+ */
+static cairo_always_inline uint32_t
+cvt (uint32_t val)
+{
+   switch (val) {
+   case 0: return 0;
+   case 1: return 1;
+   case 2: return 2;
+   case 4: return 3;
+   case 8: return 4;
+   case 16: return 5;
+   case 32: return 6;
+   }
+   return 0;
+}
+
+static cairo_always_inline struct brw_reg
+stride (struct brw_reg reg,
+       uint32_t vstride,
+       uint32_t width,
+       uint32_t hstride)
+{
+   reg.vstride = cvt (vstride);
+   reg.width   = cvt (width) - 1;
+   reg.hstride = cvt (hstride);
+   return reg;
+}
+
+static cairo_always_inline struct brw_reg
+vec16 (struct brw_reg reg)
+{
+   return stride (reg, 16,16,1);
+}
+
+static cairo_always_inline struct brw_reg
+vec8 (struct brw_reg reg)
+{
+   return stride (reg, 8,8,1);
+}
+
+static cairo_always_inline struct brw_reg
+vec4 (struct brw_reg reg)
+{
+   return stride (reg, 4,4,1);
+}
+
+static cairo_always_inline struct brw_reg
+vec2 (struct brw_reg reg)
+{
+   return stride (reg, 2,2,1);
+}
+
+static cairo_always_inline struct brw_reg
+vec1 (struct brw_reg reg)
+{
+   return stride (reg, 0,1,0);
+}
+
+static cairo_always_inline struct brw_reg
+get_element (struct brw_reg reg, uint32_t elt)
+{
+   return vec1 (suboffset (reg, elt));
+}
+
+static cairo_always_inline struct brw_reg
+get_element_ud (struct brw_reg reg, uint32_t elt)
+{
+   return vec1 (suboffset (retype (reg, BRW_REGISTER_TYPE_UD), elt));
+}
+
+static cairo_always_inline struct brw_reg
+brw_swizzle (struct brw_reg reg,
+            uint32_t x,
+            uint32_t y,
+            uint32_t z,
+            uint32_t w)
+{
+    reg.dw1.bits.swizzle = BRW_SWIZZLE4 (BRW_GET_SWZ (reg.dw1.bits.swizzle, x),
+                                        BRW_GET_SWZ (reg.dw1.bits.swizzle, y),
+                                        BRW_GET_SWZ (reg.dw1.bits.swizzle, z),
+                                        BRW_GET_SWZ (reg.dw1.bits.swizzle, w));
+   return reg;
+}
+
+static cairo_always_inline struct brw_reg
+brw_swizzle1 (struct brw_reg reg,
+             uint32_t x)
+{
+   return brw_swizzle (reg, x, x, x, x);
+}
+
+static cairo_always_inline struct brw_reg
+brw_writemask (struct brw_reg reg,
+              uint32_t mask)
+{
+   reg.dw1.bits.writemask &= mask;
+   return reg;
+}
+
+static cairo_always_inline struct brw_reg
+brw_set_writemask (struct brw_reg reg,
+                  uint32_t mask)
+{
+   reg.dw1.bits.writemask = mask;
+   return reg;
+}
+
+static cairo_always_inline struct brw_reg
+negate (struct brw_reg reg)
+{
+   reg.negate ^= 1;
+   return reg;
+}
+
+static cairo_always_inline struct brw_reg
+brw_abs (struct brw_reg reg)
+{
+   reg.abs = 1;
+   return reg;
+}
+
+static cairo_always_inline struct brw_reg
+brw_vec4_indirect (uint32_t subnr,
+                  int32_t offset)
+{
+   struct brw_reg reg = brw_vec4_grf (0, 0);
+   reg.subnr = subnr;
+   reg.address_mode = BRW_ADDRESS_REGISTER_INDIRECT_REGISTER;
+   reg.dw1.bits.indirect_offset = offset;
+   return reg;
+}
+
+static cairo_always_inline struct brw_reg
+brw_vec1_indirect (uint32_t subnr,
+                  int32_t offset)
+{
+   struct brw_reg reg = brw_vec1_grf (0, 0);
+   reg.subnr = subnr;
+   reg.address_mode = BRW_ADDRESS_REGISTER_INDIRECT_REGISTER;
+   reg.dw1.bits.indirect_offset = offset;
+   return reg;
+}
+
+static cairo_always_inline struct brw_reg
+deref_4f (struct brw_indirect ptr, int32_t offset)
+{
+   return brw_vec4_indirect (ptr.addr_subnr, ptr.addr_offset + offset);
+}
+
+static cairo_always_inline struct brw_reg
+deref_1f(struct brw_indirect ptr, int32_t offset)
+{
+   return brw_vec1_indirect (ptr.addr_subnr, ptr.addr_offset + offset);
+}
+
+static cairo_always_inline struct brw_reg
+deref_4b(struct brw_indirect ptr, int32_t offset)
+{
+   return retype (deref_4f (ptr, offset), BRW_REGISTER_TYPE_B);
+}
+
+static cairo_always_inline struct brw_reg
+deref_1uw(struct brw_indirect ptr, int32_t offset)
+{
+   return retype (deref_1f (ptr, offset), BRW_REGISTER_TYPE_UW);
+}
+
+static cairo_always_inline struct brw_reg
+deref_1d (struct brw_indirect ptr, int32_t offset)
+{
+   return retype (deref_1f (ptr, offset), BRW_REGISTER_TYPE_D);
+}
+
+static cairo_always_inline struct brw_reg
+deref_1ud (struct brw_indirect ptr, int32_t offset)
+{
+   return retype (deref_1f (ptr, offset), BRW_REGISTER_TYPE_UD);
+}
+
+static cairo_always_inline struct brw_reg
+get_addr_reg (struct brw_indirect ptr)
+{
+   return brw_address_reg (ptr.addr_subnr);
+}
+
+static cairo_always_inline struct brw_indirect
+brw_indirect_offset (struct brw_indirect ptr, int32_t offset)
+{
+   ptr.addr_offset += offset;
+   return ptr;
+}
+
+static cairo_always_inline struct brw_indirect
+brw_indirect (uint32_t addr_subnr, int32_t offset)
+{
+   struct brw_indirect ptr;
+   ptr.addr_subnr = addr_subnr;
+   ptr.addr_offset = offset;
+   ptr.pad = 0;
+   return ptr;
+}
+
+static cairo_always_inline struct brw_instruction *
+current_insn (struct brw_compile *p)
+{
+   return &p->store[p->nr_insn];
+}
+
+cairo_private void brw_pop_insn_state (struct brw_compile *p);
+cairo_private void brw_push_insn_state (struct brw_compile *p);
+cairo_private void brw_set_mask_control (struct brw_compile *p, uint32_t value);
+cairo_private void brw_set_saturate (struct brw_compile *p, uint32_t value);
+cairo_private void brw_set_access_mode (struct brw_compile *p, uint32_t access_mode);
+cairo_private void brw_set_compression_control (struct brw_compile *p, int control);
+cairo_private void brw_set_predicate_control_flag_value (struct brw_compile *p, uint32_t value);
+cairo_private void brw_set_predicate_control (struct brw_compile *p, uint32_t pc);
+cairo_private void brw_set_conditionalmod (struct brw_compile *p, uint32_t conditional);
+
+cairo_private void
+brw_compile_init (struct brw_compile *p,
+                 cairo_bool_t is_g4x);
+cairo_private const uint32_t *brw_get_program (struct brw_compile *p, uint32_t *sz);
+
+/* Helpers for regular instructions:
+ */
+#define ALU1(OP)                                       \
+cairo_private_no_warn struct brw_instruction * \
+brw_##OP(struct brw_compile *p,        \
+        struct brw_reg dest,                   \
+        struct brw_reg src0);
+
+#define ALU2(OP)                                       \
+cairo_private_no_warn struct brw_instruction * \
+brw_##OP(struct brw_compile *p,        \
+        struct brw_reg dest,                   \
+        struct brw_reg src0,                   \
+        struct brw_reg src1);
+
+ALU1(MOV)
+ALU2(SEL)
+ALU1(NOT)
+ALU2(AND)
+ALU2(OR)
+ALU2(XOR)
+ALU2(SHR)
+ALU2(SHL)
+ALU2(RSR)
+ALU2(RSL)
+ALU2(ASR)
+ALU2(JMPI)
+ALU2(ADD)
+ALU2(MUL)
+ALU1(FRC)
+ALU1(RNDD)
+ALU1(RNDZ)
+ALU2(MAC)
+ALU2(MACH)
+ALU1(LZD)
+ALU2(DP4)
+ALU2(DPH)
+ALU2(DP3)
+ALU2(DP2)
+ALU2(LINE)
+
+#undef ALU1
+#undef ALU2
+
+/* Helpers for SEND instruction: */
+cairo_private void
+brw_urb_WRITE (struct brw_compile *p,
+              struct brw_reg dest,
+              uint32_t msg_reg_nr,
+              struct brw_reg src0,
+              int allocate,
+              int used,
+              uint32_t msg_length,
+              uint32_t response_length,
+              int eot,
+              int writes_complete,
+              uint32_t offset,
+              uint32_t swizzle);
+
+cairo_private void
+brw_fb_WRITE (struct brw_compile *p,
+             struct brw_reg dest,
+             uint32_t msg_reg_nr,
+             struct brw_reg src0,
+             uint32_t binding_table_index,
+             uint32_t msg_length,
+             uint32_t response_length,
+             int eot);
+
+cairo_private void
+brw_SAMPLE (struct brw_compile *p,
+           struct brw_reg dest,
+           uint32_t msg_reg_nr,
+           struct brw_reg src0,
+           uint32_t binding_table_index,
+           uint32_t sampler,
+           uint32_t writemask,
+           uint32_t msg_type,
+           uint32_t response_length,
+           uint32_t msg_length,
+           cairo_bool_t eot);
+
+cairo_private void
+brw_math_16 (struct brw_compile *p,
+            struct brw_reg dest,
+            uint32_t function,
+            uint32_t saturate,
+            uint32_t msg_reg_nr,
+            struct brw_reg src,
+            uint32_t precision);
+
+cairo_private void
+brw_math (struct brw_compile *p,
+         struct brw_reg dest,
+         uint32_t function,
+         uint32_t saturate,
+         uint32_t msg_reg_nr,
+         struct brw_reg src,
+         uint32_t data_type,
+         uint32_t precision);
+
+cairo_private void
+brw_dp_READ_16 (struct brw_compile *p,
+               struct brw_reg dest,
+               uint32_t msg_reg_nr,
+               uint32_t scratch_offset);
+
+cairo_private void
+brw_dp_WRITE_16 (struct brw_compile *p,
+                struct brw_reg src,
+                uint32_t msg_reg_nr,
+                uint32_t scratch_offset);
+
+/* If/else/endif.  Works by manipulating the execution flags on each
+ * channel.
+ */
+cairo_private struct brw_instruction *
+brw_IF (struct brw_compile *p,
+       uint32_t execute_size);
+
+cairo_private struct brw_instruction *
+brw_ELSE (struct brw_compile *p,
+         struct brw_instruction *if_insn);
+
+cairo_private void
+brw_ENDIF (struct brw_compile *p,
+          struct brw_instruction *if_or_else_insn);
+
+
+/* DO/WHILE loops: */
+cairo_private struct brw_instruction *
+brw_DO (struct brw_compile *p,
+       uint32_t execute_size);
+
+cairo_private struct brw_instruction *
+brw_WHILE (struct brw_compile *p,
+          struct brw_instruction *patch_insn);
+
+cairo_private struct brw_instruction *
+brw_BREAK (struct brw_compile *p);
+
+cairo_private struct brw_instruction *
+brw_CONT (struct brw_compile *p);
+
+/* Forward jumps: */
+cairo_private void
+brw_land_fwd_jump (struct brw_compile *p,
+                 struct brw_instruction *jmp_insn);
+
+cairo_private void
+brw_NOP (struct brw_compile *p);
+
+/* Special case: there is never a destination, execution size will be
+ * taken from src0:
+ */
+cairo_private void
+brw_CMP (struct brw_compile *p,
+        struct brw_reg dest,
+        uint32_t conditional,
+        struct brw_reg src0,
+        struct brw_reg src1);
+
+cairo_private void
+brw_print_reg (struct brw_reg reg);
+
+cairo_private struct brw_instruction *
+brw_next_instruction (struct brw_compile *p,
+                     uint32_t opcode);
+
+cairo_private void
+brw_instruction_set_destination (struct brw_instruction *insn,
+                                struct brw_reg dest);
+
+cairo_private void
+brw_instruction_set_source0 (struct brw_instruction *insn,
+                            struct brw_reg reg);
+
+cairo_private void
+brw_instruction_set_dp_write_message (struct brw_instruction *insn,
+                                     uint32_t binding_table_index,
+                                     uint32_t msg_control,
+                                     uint32_t msg_type,
+                                     uint32_t msg_length,
+                                     uint32_t pixel_scoreboard_clear,
+                                     uint32_t response_length,
+                                     uint32_t end_of_thread);
+
+/***********************************************************************
+ * brw_eu_util.c:
+ */
+
+cairo_private void
+brw_copy_indirect_to_indirect (struct brw_compile *p,
+                              struct brw_indirect dst_ptr,
+                              struct brw_indirect src_ptr,
+                              uint32_t count);
+
+cairo_private void
+brw_copy_from_indirect (struct brw_compile *p,
+                      struct brw_reg dst,
+                      struct brw_indirect ptr,
+                      uint32_t count);
+
+cairo_private void
+brw_copy4 (struct brw_compile *p,
+          struct brw_reg dst,
+          struct brw_reg src,
+          uint32_t count);
+
+cairo_private void
+brw_copy8 (struct brw_compile *p,
+          struct brw_reg dst,
+          struct brw_reg src,
+          uint32_t count);
+
+cairo_private void
+brw_math_invert (struct brw_compile *p,
+                struct brw_reg dst,
+                struct brw_reg src);
+
+cairo_private void
+brw_set_src1 (struct brw_instruction *insn,
+             struct brw_reg reg);
+
+#endif
diff --git a/src/drm/cairo-drm-intel-brw-structs.h b/src/drm/cairo-drm-intel-brw-structs.h
new file mode 100644 (file)
index 0000000..f42483e
--- /dev/null
@@ -0,0 +1,1328 @@
+/**************************************************************************
+ *
+ * Copyright 2005 Tungsten Graphics, Inc., Cedar Park, Texas.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sub license, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
+ * IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ **************************************************************************/
+
+#ifndef CAIRO_DRM_INTEL_BRW_STRUCTS_H
+#define CAIRO_DRM_INTEL_BRW_STRUCTS_H
+
+#include "cairo-types-private.h"
+
+/* Command packets:
+*/
+struct header {
+    unsigned int length:16;
+    unsigned int opcode:16;
+};
+
+union header_union {
+    struct header bits;
+    unsigned int dword;
+};
+
+struct brw_3d_control {
+    struct {
+       unsigned int length:8;
+       unsigned int notify_enable:1;
+       unsigned int pad:3;
+       unsigned int wc_flush_enable:1;
+       unsigned int depth_stall_enable:1;
+       unsigned int operation:2;
+       unsigned int opcode:16;
+    } header;
+
+    struct {
+       unsigned int pad:2;
+       unsigned int dest_addr_type:1;
+       unsigned int dest_addr:29;
+    } dest;
+
+    unsigned int dword2;
+    unsigned int dword3;
+};
+
+
+struct brw_3d_primitive {
+    struct {
+       unsigned int length:8;
+       unsigned int pad:2;
+       unsigned int topology:5;
+       unsigned int indexed:1;
+       unsigned int opcode:16;
+    } header;
+
+    unsigned int verts_per_instance;
+    unsigned int start_vert_location;
+    unsigned int instance_count;
+    unsigned int start_instance_location;
+    unsigned int base_vert_location;
+};
+
+/* These seem to be passed around as function args, so it works out
+ * better to keep them as #defines:
+ */
+#define BRW_FLUSH_READ_CACHE           0x1
+#define BRW_FLUSH_STATE_CACHE          0x2
+#define BRW_INHIBIT_FLUSH_RENDER_CACHE 0x4
+#define BRW_FLUSH_SNAPSHOT_COUNTERS    0x8
+
+struct brw_mi_flush {
+    unsigned int flags:4;
+    unsigned int pad:12;
+    unsigned int opcode:16;
+};
+
+struct brw_vf_statistics {
+    unsigned int statistics_enable:1;
+    unsigned int pad:15;
+    unsigned int opcode:16;
+};
+
+
+struct brw_binding_table_pointers {
+    struct header header;
+    unsigned int vs;
+    unsigned int gs;
+    unsigned int clp;
+    unsigned int sf;
+    unsigned int wm;
+};
+
+struct brw_blend_constant_color {
+    struct header header;
+    float blend_constant_color[4];
+};
+
+struct brw_depthbuffer {
+    union header_union header;
+
+    union {
+       struct {
+           unsigned int pitch:18;
+           unsigned int format:3;
+           unsigned int pad:4;
+           unsigned int depth_offset_disable:1;
+           unsigned int tile_walk:1;
+           unsigned int tiled_surface:1;
+           unsigned int pad2:1;
+           unsigned int surface_type:3;
+       } bits;
+       unsigned int dword;
+    } dword1;
+
+    unsigned int dword2_base_addr;
+
+    union {
+       struct {
+           unsigned int pad:1;
+           unsigned int mipmap_layout:1;
+           unsigned int lod:4;
+           unsigned int width:13;
+           unsigned int height:13;
+       } bits;
+       unsigned int dword;
+    } dword3;
+
+    union {
+       struct {
+           unsigned int pad:12;
+           unsigned int min_array_element:9;
+           unsigned int depth:11;
+       } bits;
+       unsigned int dword;
+    } dword4;
+};
+
+struct brw_drawrect {
+    struct header header;
+    unsigned int xmin:16;
+    unsigned int ymin:16;
+    unsigned int xmax:16;
+    unsigned int ymax:16;
+    unsigned int xorg:16;
+    unsigned int yorg:16;
+};
+
+struct brw_global_depth_offset_clamp {
+    struct header header;
+    float depth_offset_clamp;
+};
+
+struct brw_indexbuffer {
+    union {
+       struct {
+           unsigned int length:8;
+           unsigned int index_format:2;
+           unsigned int cut_index_enable:1;
+           unsigned int pad:5;
+           unsigned int opcode:16;
+       } bits;
+       unsigned int dword;
+    } header;
+    unsigned int buffer_start;
+    unsigned int buffer_end;
+};
+
+
+struct brw_line_stipple {
+    struct header header;
+
+    struct {
+       unsigned int pattern:16;
+       unsigned int pad:16;
+    } bits0;
+
+    struct {
+       unsigned int repeat_count:9;
+       unsigned int pad:7;
+       unsigned int inverse_repeat_count:16;
+    } bits1;
+};
+
+struct brw_pipelined_state_pointers {
+    struct header header;
+
+    struct {
+       unsigned int pad:5;
+       unsigned int offset:27;
+    } vs;
+
+    struct {
+       unsigned int enable:1;
+       unsigned int pad:4;
+       unsigned int offset:27;
+    } gs;
+
+    struct {
+       unsigned int enable:1;
+       unsigned int pad:4;
+       unsigned int offset:27;
+    } clp;
+
+    struct {
+       unsigned int pad:5;
+       unsigned int offset:27;
+    } sf;
+
+    struct {
+       unsigned int pad:5;
+       unsigned int offset:27;
+    } wm;
+
+    struct {
+       unsigned int pad:6;
+       unsigned int offset:26;
+    } cc;
+};
+
+struct brw_polygon_stipple_offset {
+    struct header header;
+
+    struct {
+       unsigned int y_offset:5;
+       unsigned int pad:3;
+       unsigned int x_offset:5;
+       unsigned int pad0:19;
+    } bits0;
+};
+
+struct brw_polygon_stipple {
+    struct header header;
+    unsigned int stipple[32];
+};
+
+struct brw_pipeline_select {
+    struct {
+       unsigned int pipeline_select:1;
+       unsigned int pad:15;
+       unsigned int opcode:16;
+    } header;
+};
+
+struct brw_pipe_control {
+    struct {
+       unsigned int length:8;
+       unsigned int notify_enable:1;
+       unsigned int pad:2;
+       unsigned int instruction_state_cache_flush_enable:1;
+       unsigned int write_cache_flush_enable:1;
+       unsigned int depth_stall_enable:1;
+       unsigned int post_sync_operation:2;
+
+       unsigned int opcode:16;
+    } header;
+
+    struct {
+       unsigned int pad:2;
+       unsigned int dest_addr_type:1;
+       unsigned int dest_addr:29;
+    } bits1;
+
+    unsigned int data0;
+    unsigned int data1;
+};
+
+
+struct brw_urb_fence {
+    struct {
+       unsigned int length:8;
+       unsigned int vs_realloc:1;
+       unsigned int gs_realloc:1;
+       unsigned int clp_realloc:1;
+       unsigned int sf_realloc:1;
+       unsigned int vfe_realloc:1;
+       unsigned int cs_realloc:1;
+       unsigned int pad:2;
+       unsigned int opcode:16;
+    } header;
+
+    struct {
+       unsigned int vs_fence:10;
+       unsigned int gs_fence:10;
+       unsigned int clp_fence:10;
+       unsigned int pad:2;
+    } bits0;
+
+    struct {
+       unsigned int sf_fence:10;
+       unsigned int vf_fence:10;
+       unsigned int cs_fence:10;
+       unsigned int pad:2;
+    } bits1;
+};
+
+struct brw_constant_buffer_state {
+    struct header header;
+
+    struct {
+       unsigned int nr_urb_entries:3;
+       unsigned int pad:1;
+       unsigned int urb_entry_size:5;
+       unsigned int pad0:23;
+    } bits0;
+};
+
+struct brw_constant_buffer {
+    struct {
+       unsigned int length:8;
+       unsigned int valid:1;
+       unsigned int pad:7;
+       unsigned int opcode:16;
+    } header;
+
+    struct {
+       unsigned int buffer_length:6;
+       unsigned int buffer_address:26;
+    } bits0;
+};
+
+struct brw_state_base_address {
+    struct header header;
+
+    struct {
+       unsigned int modify_enable:1;
+       unsigned int pad:4;
+       unsigned int general_state_address:27;
+    } bits0;
+
+    struct {
+       unsigned int modify_enable:1;
+       unsigned int pad:4;
+       unsigned int surface_state_address:27;
+    } bits1;
+
+    struct {
+       unsigned int modify_enable:1;
+       unsigned int pad:4;
+       unsigned int indirect_object_state_address:27;
+    } bits2;
+
+    struct {
+       unsigned int modify_enable:1;
+       unsigned int pad:11;
+       unsigned int general_state_upper_bound:20;
+    } bits3;
+
+    struct {
+       unsigned int modify_enable:1;
+       unsigned int pad:11;
+       unsigned int indirect_object_state_upper_bound:20;
+    } bits4;
+};
+
+struct brw_state_prefetch {
+    struct header header;
+
+    struct {
+       unsigned int prefetch_count:3;
+       unsigned int pad:3;
+       unsigned int prefetch_pointer:26;
+    } bits0;
+};
+
+struct brw_system_instruction_pointer {
+    struct header header;
+
+    struct {
+       unsigned int pad:4;
+       unsigned int system_instruction_pointer:28;
+    } bits0;
+};
+
+
+/* State structs for the various fixed function units:
+*/
+
+struct thread0 {
+    unsigned int pad0:1;
+    unsigned int grf_reg_count:3;
+    unsigned int pad1:2;
+    unsigned int kernel_start_pointer:26;
+};
+
+struct thread1 {
+    unsigned int ext_halt_exception_enable:1;
+    unsigned int sw_exception_enable:1;
+    unsigned int mask_stack_exception_enable:1;
+    unsigned int timeout_exception_enable:1;
+    unsigned int illegal_op_exception_enable:1;
+    unsigned int pad0:3;
+    unsigned int depth_coef_urb_read_offset:6; /* WM only */
+    unsigned int pad1:2;
+    unsigned int floating_point_mode:1;
+    unsigned int thread_priority:1;
+    unsigned int binding_table_entry_count:8;
+    unsigned int pad3:5;
+    unsigned int single_program_flow:1;
+};
+
+struct thread2 {
+    unsigned int per_thread_scratch_space:4;
+    unsigned int pad0:6;
+    unsigned int scratch_space_base_pointer:22;
+};
+
+struct thread3 {
+    unsigned int dispatch_grf_start_reg:4;
+    unsigned int urb_entry_read_offset:6;
+    unsigned int pad0:1;
+    unsigned int urb_entry_read_length:6;
+    unsigned int pad1:1;
+    unsigned int const_urb_entry_read_offset:6;
+    unsigned int pad2:1;
+    unsigned int const_urb_entry_read_length:6;
+    unsigned int pad3:1;
+};
+
+struct brw_clip_unit_state {
+    struct thread0 thread0;
+    struct thread1 thread1;
+    struct thread2 thread2;
+    struct thread3 thread3;
+
+    struct {
+       unsigned int pad0:9;
+       unsigned int gs_output_stats:1; /* not always */
+       unsigned int stats_enable:1;
+       unsigned int nr_urb_entries:7;
+       unsigned int pad1:1;
+       unsigned int urb_entry_allocation_size:5;
+       unsigned int pad2:1;
+       unsigned int max_threads:6;     /* may be less */
+       unsigned int pad3:1;
+    } thread4;
+
+    struct {
+       unsigned int pad0:13;
+       unsigned int clip_mode:3;
+       unsigned int userclip_enable_flags:8;
+       unsigned int userclip_must_clip:1;
+       unsigned int pad1:1;
+       unsigned int guard_band_enable:1;
+       unsigned int viewport_z_clip_enable:1;
+       unsigned int viewport_xy_clip_enable:1;
+       unsigned int vertex_position_space:1;
+       unsigned int api_mode:1;
+       unsigned int pad2:1;
+    } clip5;
+
+    struct {
+       unsigned int pad0:5;
+       unsigned int clipper_viewport_state_ptr:27;
+    } clip6;
+
+    float viewport_xmin;
+    float viewport_xmax;
+    float viewport_ymin;
+    float viewport_ymax;
+};
+
+struct brw_cc_unit_state {
+    struct {
+       unsigned int pad0:3;
+       unsigned int bf_stencil_pass_depth_pass_op:3;
+       unsigned int bf_stencil_pass_depth_fail_op:3;
+       unsigned int bf_stencil_fail_op:3;
+       unsigned int bf_stencil_func:3;
+       unsigned int bf_stencil_enable:1;
+       unsigned int pad1:2;
+       unsigned int stencil_write_enable:1;
+       unsigned int stencil_pass_depth_pass_op:3;
+       unsigned int stencil_pass_depth_fail_op:3;
+       unsigned int stencil_fail_op:3;
+       unsigned int stencil_func:3;
+       unsigned int stencil_enable:1;
+    } cc0;
+
+    struct {
+       unsigned int bf_stencil_ref:8;
+       unsigned int stencil_write_mask:8;
+       unsigned int stencil_test_mask:8;
+       unsigned int stencil_ref:8;
+    } cc1;
+
+    struct {
+       unsigned int logicop_enable:1;
+       unsigned int pad0:10;
+       unsigned int depth_write_enable:1;
+       unsigned int depth_test_function:3;
+       unsigned int depth_test:1;
+       unsigned int bf_stencil_write_mask:8;
+       unsigned int bf_stencil_test_mask:8;
+    } cc2;
+
+    struct {
+       unsigned int pad0:8;
+       unsigned int alpha_test_func:3;
+       unsigned int alpha_test:1;
+       unsigned int blend_enable:1;
+       unsigned int ia_blend_enable:1;
+       unsigned int pad1:1;
+       unsigned int alpha_test_format:1;
+       unsigned int pad2:16;
+    } cc3;
+
+    struct {
+       unsigned int pad0:5;
+       unsigned int cc_viewport_state_offset:27;
+    } cc4;
+
+    struct {
+       unsigned int pad0:2;
+       unsigned int ia_dest_blend_factor:5;
+       unsigned int ia_src_blend_factor:5;
+       unsigned int ia_blend_function:3;
+       unsigned int statistics_enable:1;
+       unsigned int logicop_func:4;
+       unsigned int pad1:11;
+       unsigned int dither_enable:1;
+    } cc5;
+
+    struct {
+       unsigned int clamp_post_alpha_blend:1;
+       unsigned int clamp_pre_alpha_blend:1;
+       unsigned int clamp_range:2;
+       unsigned int pad0:11;
+       unsigned int y_dither_offset:2;
+       unsigned int x_dither_offset:2;
+       unsigned int dest_blend_factor:5;
+       unsigned int src_blend_factor:5;
+       unsigned int blend_function:3;
+    } cc6;
+
+    struct {
+       union {
+           float f;
+           unsigned char ub[4];
+       } alpha_ref;
+    } cc7;
+};
+
+struct brw_sf_unit_state {
+    struct thread0 thread0;
+    struct {
+       unsigned int pad0:7;
+       unsigned int sw_exception_enable:1;
+       unsigned int pad1:3;
+       unsigned int mask_stack_exception_enable:1;
+       unsigned int pad2:1;
+       unsigned int illegal_op_exception_enable:1;
+       unsigned int pad3:2;
+       unsigned int floating_point_mode:1;
+       unsigned int thread_priority:1;
+       unsigned int binding_table_entry_count:8;
+       unsigned int pad4:5;
+       unsigned int single_program_flow:1;
+    } sf1;
+
+    struct thread2 thread2;
+    struct thread3 thread3;
+
+    struct {
+       unsigned int pad0:10;
+       unsigned int stats_enable:1;
+       unsigned int nr_urb_entries:7;
+       unsigned int pad1:1;
+       unsigned int urb_entry_allocation_size:5;
+       unsigned int pad2:1;
+       unsigned int max_threads:6;
+       unsigned int pad3:1;
+    } thread4;
+
+    struct {
+       unsigned int front_winding:1;
+       unsigned int viewport_transform:1;
+       unsigned int pad0:3;
+       unsigned int sf_viewport_state_offset:27;
+    } sf5;
+
+    struct {
+       unsigned int pad0:9;
+       unsigned int dest_org_vbias:4;
+       unsigned int dest_org_hbias:4;
+       unsigned int scissor:1;
+       unsigned int disable_2x2_trifilter:1;
+       unsigned int disable_zero_pix_trifilter:1;
+       unsigned int point_rast_rule:2;
+       unsigned int line_endcap_aa_region_width:2;
+       unsigned int line_width:4;
+       unsigned int fast_scissor_disable:1;
+       unsigned int cull_mode:2;
+       unsigned int aa_enable:1;
+    } sf6;
+
+    struct {
+       unsigned int point_size:11;
+       unsigned int use_point_size_state:1;
+       unsigned int subpixel_precision:1;
+       unsigned int sprite_point:1;
+       unsigned int pad0:11;
+       unsigned int trifan_pv:2;
+       unsigned int linestrip_pv:2;
+       unsigned int tristrip_pv:2;
+       unsigned int line_last_pixel_enable:1;
+    } sf7;
+};
+
+struct brw_gs_unit_state {
+    struct thread0 thread0;
+    struct thread1 thread1;
+    struct thread2 thread2;
+    struct thread3 thread3;
+
+    struct {
+       unsigned int pad0:10;
+       unsigned int stats_enable:1;
+       unsigned int nr_urb_entries:7;
+       unsigned int pad1:1;
+       unsigned int urb_entry_allocation_size:5;
+       unsigned int pad2:1;
+       unsigned int max_threads:1;
+       unsigned int pad3:6;
+    } thread4;
+
+    struct {
+       unsigned int sampler_count:3;
+       unsigned int pad0:2;
+       unsigned int sampler_state_pointer:27;
+    } gs5;
+
+    struct {
+       unsigned int max_vp_index:4;
+       unsigned int pad0:26;
+       unsigned int reorder_enable:1;
+       unsigned int pad1:1;
+    } gs6;
+};
+
+struct brw_vs_unit_state {
+    struct thread0 thread0;
+    struct thread1 thread1;
+    struct thread2 thread2;
+    struct thread3 thread3;
+
+    struct {
+       unsigned int pad0:10;
+       unsigned int stats_enable:1;
+       unsigned int nr_urb_entries:7;
+       unsigned int pad1:1;
+       unsigned int urb_entry_allocation_size:5;
+       unsigned int pad2:1;
+       unsigned int max_threads:4;
+       unsigned int pad3:3;
+    } thread4;
+
+    struct {
+       unsigned int sampler_count:3;
+       unsigned int pad0:2;
+       unsigned int sampler_state_pointer:27;
+    } vs5;
+
+    struct {
+       unsigned int vs_enable:1;
+       unsigned int vert_cache_disable:1;
+       unsigned int pad0:30;
+    } vs6;
+};
+
+struct brw_wm_unit_state {
+    struct thread0 thread0;
+    struct thread1 thread1;
+    struct thread2 thread2;
+    struct thread3 thread3;
+
+    struct {
+       unsigned int stats_enable:1;
+       unsigned int pad0:1;
+       unsigned int sampler_count:3;
+       unsigned int sampler_state_pointer:27;
+    } wm4;
+
+    struct {
+       unsigned int enable_8_pix:1;
+       unsigned int enable_16_pix:1;
+       unsigned int enable_32_pix:1;
+       unsigned int pad0:7;
+       unsigned int legacy_global_depth_bias:1;
+       unsigned int line_stipple:1;
+       unsigned int depth_offset:1;
+       unsigned int polygon_stipple:1;
+       unsigned int line_aa_region_width:2;
+       unsigned int line_endcap_aa_region_width:2;
+       unsigned int early_depth_test:1;
+       unsigned int thread_dispatch_enable:1;
+       unsigned int program_uses_depth:1;
+       unsigned int program_computes_depth:1;
+       unsigned int program_uses_killpixel:1;
+       unsigned int legacy_line_rast: 1;
+       unsigned int transposed_urb_read:1;
+       unsigned int max_threads:7;
+    } wm5;
+
+    float global_depth_offset_constant;
+    float global_depth_offset_scale;
+};
+
+/* The hardware supports two different modes for border color. The
+ * default (OpenGL) mode uses floating-point color channels, while the
+ * legacy mode uses 4 bytes.
+ *
+ * More significantly, the legacy mode respects the components of the
+ * border color for channels not present in the source, (whereas the
+ * default mode will ignore the border color's alpha channel and use
+ * alpha==1 for an RGB source, for example).
+ *
+ * The legacy mode matches the semantics specified by the Render
+ * extension.
+ */
+struct brw_sampler_default_border_color {
+    float color[4];
+};
+
+struct brw_sampler_legacy_border_color {
+    uint8_t color[4];
+};
+
+struct brw_sampler_state {
+    struct {
+       unsigned int shadow_function:3;
+       unsigned int lod_bias:11;
+       unsigned int min_filter:3;
+       unsigned int mag_filter:3;
+       unsigned int mip_filter:2;
+       unsigned int base_level:5;
+       unsigned int pad:1;
+       unsigned int lod_preclamp:1;
+       unsigned int border_color_mode:1;
+       unsigned int pad0:1;
+       unsigned int disable:1;
+    } ss0;
+
+    struct {
+       unsigned int r_wrap_mode:3;
+       unsigned int t_wrap_mode:3;
+       unsigned int s_wrap_mode:3;
+       unsigned int pad:3;
+       unsigned int max_lod:10;
+       unsigned int min_lod:10;
+    } ss1;
+
+    struct {
+       unsigned int pad:5;
+       unsigned int border_color_pointer:27;
+    } ss2;
+
+    struct {
+       unsigned int pad:19;
+       unsigned int max_aniso:3;
+       unsigned int chroma_key_mode:1;
+       unsigned int chroma_key_index:2;
+       unsigned int chroma_key_enable:1;
+       unsigned int monochrome_filter_width:3;
+       unsigned int monochrome_filter_height:3;
+    } ss3;
+};
+
+struct brw_clipper_viewport {
+    float xmin;
+    float xmax;
+    float ymin;
+    float ymax;
+};
+
+struct brw_cc_viewport {
+    float min_depth;
+    float max_depth;
+};
+
+struct brw_sf_viewport {
+    struct {
+       float m00;
+       float m11;
+       float m22;
+       float m30;
+       float m31;
+       float m32;
+    } viewport;
+
+    struct {
+       short xmin;
+       short ymin;
+       short xmax;
+       short ymax;
+    } scissor;
+};
+
+/* Documented in the subsystem/shared-functions/sampler chapter...
+*/
+struct brw_surface_state {
+    struct {
+       unsigned int cube_pos_z:1;
+       unsigned int cube_neg_z:1;
+       unsigned int cube_pos_y:1;
+       unsigned int cube_neg_y:1;
+       unsigned int cube_pos_x:1;
+       unsigned int cube_neg_x:1;
+       unsigned int pad:3;
+       unsigned int render_cache_read_mode:1;
+       unsigned int mipmap_layout_mode:1;
+       unsigned int vert_line_stride_ofs:1;
+       unsigned int vert_line_stride:1;
+       unsigned int color_blend:1;
+       unsigned int writedisable_blue:1;
+       unsigned int writedisable_green:1;
+       unsigned int writedisable_red:1;
+       unsigned int writedisable_alpha:1;
+       unsigned int surface_format:9;
+       unsigned int data_return_format:1;
+       unsigned int pad0:1;
+       unsigned int surface_type:3;
+    } ss0;
+
+    struct {
+       unsigned int base_addr;
+    } ss1;
+
+    struct {
+       unsigned int render_target_rotation:2;
+       unsigned int mip_count:4;
+       unsigned int width:13;
+       unsigned int height:13;
+    } ss2;
+
+    struct {
+       unsigned int tile_walk:1;
+       unsigned int tiled_surface:1;
+       unsigned int pad:1;
+       unsigned int pitch:18;
+       unsigned int depth:11;
+    } ss3;
+
+    struct {
+       unsigned int pad:19;
+       unsigned int min_array_elt:9;
+       unsigned int min_lod:4;
+    } ss4;
+
+    struct {
+       unsigned int pad:20;
+       unsigned int y_offset:4;
+       unsigned int pad2:1;
+       unsigned int x_offset:7;
+    } ss5;
+};
+
+struct brw_vertex_buffer_state {
+    struct {
+       unsigned int pitch:11;
+       unsigned int pad:15;
+       unsigned int access_type:1;
+       unsigned int vb_index:5;
+    } vb0;
+
+    unsigned int start_addr;
+    unsigned int max_index;
+#if 1
+    unsigned int instance_data_step_rate; /* not included for sequential/random vertices? */
+#endif
+};
+
+#define BRW_VBP_MAX 17
+
+struct brw_vb_array_state {
+    struct header header;
+    struct brw_vertex_buffer_state vb[BRW_VBP_MAX];
+};
+
+struct brw_vertex_element_state {
+    struct {
+       unsigned int src_offset:11;
+       unsigned int pad:5;
+       unsigned int src_format:9;
+       unsigned int pad0:1;
+       unsigned int valid:1;
+       unsigned int vertex_buffer_index:5;
+    } ve0;
+
+    struct {
+       unsigned int dst_offset:8;
+       unsigned int pad:8;
+       unsigned int vfcomponent3:4;
+       unsigned int vfcomponent2:4;
+       unsigned int vfcomponent1:4;
+       unsigned int vfcomponent0:4;
+    } ve1;
+};
+
+#define BRW_VEP_MAX 18
+
+struct brw_vertex_element_packet {
+    struct header header;
+    struct brw_vertex_element_state ve[BRW_VEP_MAX];
+};
+
+struct brw_urb_immediate {
+    unsigned int opcode:4;
+    unsigned int offset:6;
+    unsigned int swizzle_control:2;
+    unsigned int pad:1;
+    unsigned int allocate:1;
+    unsigned int used:1;
+    unsigned int complete:1;
+    unsigned int response_length:4;
+    unsigned int msg_length:4;
+    unsigned int msg_target:4;
+    unsigned int pad1:3;
+    unsigned int end_of_thread:1;
+};
+
+/* Instruction format for the execution units: */
+
+struct brw_instruction {
+    struct {
+       unsigned int opcode:7;
+       unsigned int pad:1;
+       unsigned int access_mode:1;
+       unsigned int mask_control:1;
+       unsigned int dependency_control:2;
+       unsigned int compression_control:2;
+       unsigned int thread_control:2;
+       unsigned int predicate_control:4;
+       unsigned int predicate_inverse:1;
+       unsigned int execution_size:3;
+       unsigned int destreg__conditonalmod:4; /* destreg - send, conditionalmod - others */
+       unsigned int pad0:2;
+       unsigned int debug_control:1;
+       unsigned int saturate:1;
+    } header;
+
+    union {
+       struct {
+           unsigned int dest_reg_file:2;
+           unsigned int dest_reg_type:3;
+           unsigned int src0_reg_file:2;
+           unsigned int src0_reg_type:3;
+           unsigned int src1_reg_file:2;
+           unsigned int src1_reg_type:3;
+           unsigned int pad:1;
+           unsigned int dest_subreg_nr:5;
+           unsigned int dest_reg_nr:8;
+           unsigned int dest_horiz_stride:2;
+           unsigned int dest_address_mode:1;
+       } da1;
+
+       struct {
+           unsigned int dest_reg_file:2;
+           unsigned int dest_reg_type:3;
+           unsigned int src0_reg_file:2;
+           unsigned int src0_reg_type:3;
+           unsigned int pad:6;
+           int dest_indirect_offset:10;        /* offset against the deref'd address reg */
+           unsigned int dest_subreg_nr:3; /* subnr for the address reg a0.x */
+           unsigned int dest_horiz_stride:2;
+           unsigned int dest_address_mode:1;
+       } ia1;
+
+       struct {
+           unsigned int dest_reg_file:2;
+           unsigned int dest_reg_type:3;
+           unsigned int src0_reg_file:2;
+           unsigned int src0_reg_type:3;
+           unsigned int src1_reg_file:2;
+           unsigned int src1_reg_type:3;
+           unsigned int pad0:1;
+           unsigned int dest_writemask:4;
+           unsigned int dest_subreg_nr:1;
+           unsigned int dest_reg_nr:8;
+           unsigned int pad1:2;
+           unsigned int dest_address_mode:1;
+       } da16;
+
+       struct {
+           unsigned int dest_reg_file:2;
+           unsigned int dest_reg_type:3;
+           unsigned int src0_reg_file:2;
+           unsigned int src0_reg_type:3;
+           unsigned int pad0:6;
+           unsigned int dest_writemask:4;
+           int dest_indirect_offset:6;
+           unsigned int dest_subreg_nr:3;
+           unsigned int pad1:2;
+           unsigned int dest_address_mode:1;
+       } ia16;
+    } bits1;
+
+
+    union {
+       struct {
+           unsigned int src0_subreg_nr:5;
+           unsigned int src0_reg_nr:8;
+           unsigned int src0_abs:1;
+           unsigned int src0_negate:1;
+           unsigned int src0_address_mode:1;
+           unsigned int src0_horiz_stride:2;
+           unsigned int src0_width:3;
+           unsigned int src0_vert_stride:4;
+           unsigned int flag_reg_nr:1;
+           unsigned int pad:6;
+       } da1;
+
+       struct {
+           int src0_indirect_offset:10;
+           unsigned int src0_subreg_nr:3;
+           unsigned int src0_abs:1;
+           unsigned int src0_negate:1;
+           unsigned int src0_address_mode:1;
+           unsigned int src0_horiz_stride:2;
+           unsigned int src0_width:3;
+           unsigned int src0_vert_stride:4;
+           unsigned int flag_reg_nr:1;
+           unsigned int pad:6;
+       } ia1;
+
+       struct {
+           unsigned int src0_swz_x:2;
+           unsigned int src0_swz_y:2;
+           unsigned int src0_subreg_nr:1;
+           unsigned int src0_reg_nr:8;
+           unsigned int src0_abs:1;
+           unsigned int src0_negate:1;
+           unsigned int src0_address_mode:1;
+           unsigned int src0_swz_z:2;
+           unsigned int src0_swz_w:2;
+           unsigned int pad0:1;
+           unsigned int src0_vert_stride:4;
+           unsigned int flag_reg_nr:1;
+           unsigned int pad1:6;
+       } da16;
+
+       struct {
+           unsigned int src0_swz_x:2;
+           unsigned int src0_swz_y:2;
+           int src0_indirect_offset:6;
+           unsigned int src0_subreg_nr:3;
+           unsigned int src0_abs:1;
+           unsigned int src0_negate:1;
+           unsigned int src0_address_mode:1;
+           unsigned int src0_swz_z:2;
+           unsigned int src0_swz_w:2;
+           unsigned int pad0:1;
+           unsigned int src0_vert_stride:4;
+           unsigned int flag_reg_nr:1;
+           unsigned int pad1:6;
+       } ia16;
+
+    } bits2;
+
+    union {
+       struct {
+           unsigned int src1_subreg_nr:5;
+           unsigned int src1_reg_nr:8;
+           unsigned int src1_abs:1;
+           unsigned int src1_negate:1;
+           unsigned int pad:1;
+           unsigned int src1_horiz_stride:2;
+           unsigned int src1_width:3;
+           unsigned int src1_vert_stride:4;
+           unsigned int pad0:7;
+       } da1;
+
+       struct {
+           unsigned int src1_swz_x:2;
+           unsigned int src1_swz_y:2;
+           unsigned int src1_subreg_nr:1;
+           unsigned int src1_reg_nr:8;
+           unsigned int src1_abs:1;
+           unsigned int src1_negate:1;
+           unsigned int pad0:1;
+           unsigned int src1_swz_z:2;
+           unsigned int src1_swz_w:2;
+           unsigned int pad1:1;
+           unsigned int src1_vert_stride:4;
+           unsigned int pad2:7;
+       } da16;
+
+       struct {
+           int src1_indirect_offset:10;
+           unsigned int src1_subreg_nr:3;
+           unsigned int src1_abs:1;
+           unsigned int src1_negate:1;
+           unsigned int pad0:1;
+           unsigned int src1_horiz_stride:2;
+           unsigned int src1_width:3;
+           unsigned int src1_vert_stride:4;
+           unsigned int flag_reg_nr:1;
+           unsigned int pad1:6;
+       } ia1;
+
+       struct {
+           unsigned int src1_swz_x:2;
+           unsigned int src1_swz_y:2;
+           int src1_indirect_offset:6;
+           unsigned int src1_subreg_nr:3;
+           unsigned int src1_abs:1;
+           unsigned int src1_negate:1;
+           unsigned int pad0:1;
+           unsigned int src1_swz_z:2;
+           unsigned int src1_swz_w:2;
+           unsigned int pad1:1;
+           unsigned int src1_vert_stride:4;
+           unsigned int flag_reg_nr:1;
+           unsigned int pad2:6;
+       } ia16;
+
+       struct {
+           int jump_count:16;  /* note: signed */
+           unsigned int pop_count:4;
+           unsigned int pad0:12;
+       } if_else;
+
+       struct {
+           unsigned int function:4;
+           unsigned int int_type:1;
+           unsigned int precision:1;
+           unsigned int saturate:1;
+           unsigned int data_type:1;
+           unsigned int pad0:8;
+           unsigned int response_length:4;
+           unsigned int msg_length:4;
+           unsigned int msg_target:4;
+           unsigned int pad1:3;
+           unsigned int end_of_thread:1;
+       } math;
+
+       struct {
+           unsigned int binding_table_index:8;
+           unsigned int sampler:4;
+           unsigned int return_format:2;
+           unsigned int msg_type:2;
+           unsigned int response_length:4;
+           unsigned int msg_length:4;
+           unsigned int msg_target:4;
+           unsigned int pad1:3;
+           unsigned int end_of_thread:1;
+       } sampler;
+
+       struct {
+           uint32_t binding_table_index:8;
+           uint32_t sampler:4;
+           uint32_t msg_type:4;
+           uint32_t response_length:4;
+           uint32_t msg_length:4;
+           uint32_t msg_target:4;
+           uint32_t pad1:3;
+           uint32_t end_of_thread:1;
+       } sampler_g4x;
+
+       struct brw_urb_immediate urb;
+
+       struct {
+           unsigned int binding_table_index:8;
+           unsigned int msg_control:4;
+           unsigned int msg_type:2;
+           unsigned int target_cache:2;
+           unsigned int response_length:4;
+           unsigned int msg_length:4;
+           unsigned int msg_target:4;
+           unsigned int pad1:3;
+           unsigned int end_of_thread:1;
+       } dp_read;
+
+       struct {
+           unsigned int binding_table_index:8;
+           unsigned int msg_control:3;
+           unsigned int pixel_scoreboard_clear:1;
+           unsigned int msg_type:3;
+           unsigned int send_commit_msg:1;
+           unsigned int response_length:4;
+           unsigned int msg_length:4;
+           unsigned int msg_target:4;
+           unsigned int pad1:3;
+           unsigned int end_of_thread:1;
+       } dp_write;
+
+       struct {
+           unsigned int pad:16;
+           unsigned int response_length:4;
+           unsigned int msg_length:4;
+           unsigned int msg_target:4;
+           unsigned int pad1:3;
+           unsigned int end_of_thread:1;
+       } generic;
+
+       uint32_t ud;
+       int32_t d;
+    } bits3;
+};
+
+/* media pipeline */
+
+struct brw_vfe_state {
+    struct {
+       unsigned int per_thread_scratch_space:4;
+       unsigned int pad3:3;
+       unsigned int extend_vfe_state_present:1;
+       unsigned int pad2:2;
+       unsigned int scratch_base:22;
+    } vfe0;
+
+    struct {
+       unsigned int debug_counter_control:2;
+       unsigned int children_present:1;
+       unsigned int vfe_mode:4;
+       unsigned int pad2:2;
+       unsigned int num_urb_entries:7;
+       unsigned int urb_entry_alloc_size:9;
+       unsigned int max_threads:7;
+    } vfe1;
+
+    struct {
+       unsigned int pad4:4;
+       unsigned int interface_descriptor_base:28;
+    } vfe2;
+};
+
+struct brw_vld_state {
+    struct {
+       unsigned int pad6:6;
+       unsigned int scan_order:1;
+       unsigned int intra_vlc_format:1;
+       unsigned int quantizer_scale_type:1;
+       unsigned int concealment_motion_vector:1;
+       unsigned int frame_predict_frame_dct:1;
+       unsigned int top_field_first:1;
+       unsigned int picture_structure:2;
+       unsigned int intra_dc_precision:2;
+       unsigned int f_code_0_0:4;
+       unsigned int f_code_0_1:4;
+       unsigned int f_code_1_0:4;
+       unsigned int f_code_1_1:4;
+    } vld0;
+
+    struct {
+       unsigned int pad2:9;
+       unsigned int picture_coding_type:2;
+       unsigned int pad:21;
+    } vld1;
+
+    struct {
+       unsigned int index_0:4;
+       unsigned int index_1:4;
+       unsigned int index_2:4;
+       unsigned int index_3:4;
+       unsigned int index_4:4;
+       unsigned int index_5:4;
+       unsigned int index_6:4;
+       unsigned int index_7:4;
+    } desc_remap_table0;
+
+    struct {
+       unsigned int index_8:4;
+       unsigned int index_9:4;
+       unsigned int index_10:4;
+       unsigned int index_11:4;
+       unsigned int index_12:4;
+       unsigned int index_13:4;
+       unsigned int index_14:4;
+       unsigned int index_15:4;
+    } desc_remap_table1;
+};
+
+struct brw_interface_descriptor {
+    struct {
+       unsigned int grf_reg_blocks:4;
+       unsigned int pad:2;
+       unsigned int kernel_start_pointer:26;
+    } desc0;
+
+    struct {
+       unsigned int pad:7;
+       unsigned int software_exception:1;
+       unsigned int pad2:3;
+       unsigned int maskstack_exception:1;
+       unsigned int pad3:1;
+       unsigned int illegal_opcode_exception:1;
+       unsigned int pad4:2;
+       unsigned int floating_point_mode:1;
+       unsigned int thread_priority:1;
+       unsigned int single_program_flow:1;
+       unsigned int pad5:1;
+       unsigned int const_urb_entry_read_offset:6;
+       unsigned int const_urb_entry_read_len:6;
+    } desc1;
+
+    struct {
+       unsigned int pad:2;
+       unsigned int sampler_count:3;
+       unsigned int sampler_state_pointer:27;
+    } desc2;
+
+    struct {
+       unsigned int binding_table_entry_count:5;
+       unsigned int binding_table_pointer:27;
+    } desc3;
+};
+
+#endif
diff --git a/src/drm/cairo-drm-intel-command-private.h b/src/drm/cairo-drm-intel-command-private.h
new file mode 100644 (file)
index 0000000..a93ac12
--- /dev/null
@@ -0,0 +1,909 @@
+/**************************************************************************
+ *
+ * Copyright 2003 Tungsten Graphics, Inc., Cedar Park, Texas.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sub license, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
+ * IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ **************************************************************************/
+
+#ifndef CAIRO_DRM_INTEL_COMMAND_PRIVATE_H
+#define CAIRO_DRM_INTEL_COMMAND_PRIVATE_H
+
+#include "cairo-types-private.h"
+
+#define CMD_MI                         (0x0 << 29)
+#define CMD_misc                       (0x1 << 29)
+#define CMD_2D                         (0x2 << 29)
+#define CMD_3D                         (0x3 << 29)
+/* 4-7 reserved */
+
+#define MI_NOOP                                (CMD_MI | 0)
+/* Batch */
+#define MI_BATCH_BUFFER                        (CMD_MI | (0x30 << 23) | 1)
+#define MI_BATCH_BUFFER_START          (CMD_MI | (0x31 << 23))
+#define MI_BATCH_BUFFER_END            (CMD_MI | (0x0a << 23))
+#define MI_BATCH_NON_SECURE            (1)
+#define MI_BATCH_NON_SECURE_I965       (1 << 8)
+/* Flush */
+#define MI_FLUSH                       (CMD_MI | (0x04 << 23))
+#define MI_WRITE_DIRTY_STATE           (1<<4)
+#define MI_END_SCENE                   (1<<3)
+#define MI_GLOBAL_SNAPSHOT_COUNT_RESET (1<<3)
+#define MI_INHIBIT_RENDER_CACHE_FLUSH  (1<<2)
+#define MI_STATE_INSTRUCTION_CACHE_FLUSH (1<<1)
+#define MI_INVALIDATE_MAP_CACHE                (1<<0)
+
+#define PRIM3D                         (CMD_3D | (0x1f<<24))
+#define PRIM3D_TRILIST                 (PRIM3D | (0x0<<18))
+#define PRIM3D_TRISTRIP                        (PRIM3D | (0x1<<18))
+#define PRIM3D_TRISTRIP_RVRSE          (PRIM3D | (0x2<<18))
+#define PRIM3D_TRIFAN                  (PRIM3D | (0x3<<18))
+#define PRIM3D_POLY                    (PRIM3D | (0x4<<18))
+#define PRIM3D_LINELIST                        (PRIM3D | (0x5<<18))
+#define PRIM3D_LINESTRIP               (PRIM3D | (0x6<<18))
+#define PRIM3D_RECTLIST                        (PRIM3D | (0x7<<18))
+#define PRIM3D_POINTLIST               (PRIM3D | (0x8<<18))
+#define PRIM3D_DIB                     (PRIM3D | (0x9<<18))
+#define PRIM3D_CLEAR_RECT              (PRIM3D | (0xa<<18))
+#define PRIM3D_ZONE_INIT               (PRIM3D | (0xd<<18))
+#define PRIM3D_MASK                    (0x1f<<18)
+#define PRIM3D_INDIRECT_SEQUENTIAL     ((1<<23) | (0<<17))
+#define PRIM3D_INDIRECT_ELTS           ((1<<23) | (1<<17))
+
+/* p137 */
+#define _3DSTATE_AA_CMD                        (CMD_3D | (0x06<<24))
+#define AA_LINE_ECAAR_WIDTH_ENABLE     (1<<16)
+#define AA_LINE_ECAAR_WIDTH_0_5                0
+#define AA_LINE_ECAAR_WIDTH_1_0                (1<<14)
+#define AA_LINE_ECAAR_WIDTH_2_0                (2<<14)
+#define AA_LINE_ECAAR_WIDTH_4_0                (3<<14)
+#define AA_LINE_REGION_WIDTH_ENABLE    (1<<8)
+#define AA_LINE_REGION_WIDTH_0_5       0
+#define AA_LINE_REGION_WIDTH_1_0       (1<<6)
+#define AA_LINE_REGION_WIDTH_2_0       (2<<6)
+#define AA_LINE_REGION_WIDTH_4_0       (3<<6)
+
+/* 3DSTATE_BACKFACE_STENCIL_OPS, p138*/
+#define _3DSTATE_BACKFACE_STENCIL_OPS    (CMD_3D | (0x8<<24))
+#define BFO_ENABLE_STENCIL_REF          (1<<23)
+#define BFO_STENCIL_REF_SHIFT           15
+#define BFO_STENCIL_REF_MASK            (0xff<<15)
+#define BFO_ENABLE_STENCIL_FUNCS        (1<<14)
+#define BFO_STENCIL_TEST_SHIFT          11
+#define BFO_STENCIL_TEST_MASK           (0x7<<11)
+#define BFO_STENCIL_FAIL_SHIFT          8
+#define BFO_STENCIL_FAIL_MASK           (0x7<<8)
+#define BFO_STENCIL_PASS_Z_FAIL_SHIFT   5
+#define BFO_STENCIL_PASS_Z_FAIL_MASK    (0x7<<5)
+#define BFO_STENCIL_PASS_Z_PASS_SHIFT   2
+#define BFO_STENCIL_PASS_Z_PASS_MASK    (0x7<<2)
+#define BFO_ENABLE_STENCIL_TWO_SIDE     (1<<1)
+#define BFO_STENCIL_TWO_SIDE            (1<<0)
+
+/* 3DSTATE_BACKFACE_STENCIL_MASKS, p140 */
+#define _3DSTATE_BACKFACE_STENCIL_MASKS    (CMD_3D | (0x9<<24))
+#define BFM_ENABLE_STENCIL_TEST_MASK      (1<<17)
+#define BFM_ENABLE_STENCIL_WRITE_MASK     (1<<16)
+#define BFM_STENCIL_TEST_MASK_SHIFT       8
+#define BFM_STENCIL_TEST_MASK_MASK        (0xff<<8)
+#define BFM_STENCIL_WRITE_MASK_SHIFT      0
+#define BFM_STENCIL_WRITE_MASK_MASK       (0xff<<0)
+
+/* 3DSTATE_BIN_CONTROL p141 */
+
+/* p143 */
+#define _3DSTATE_BUF_INFO_CMD  (CMD_3D | (0x1d<<24) | (0x8e<<16) | 1)
+/* Dword 1 */
+#define BUF_3D_ID_COLOR_BACK   (0x3<<24)
+#define BUF_3D_ID_DEPTH                (0x7<<24)
+#define BUF_3D_USE_FENCE       (1<<23)
+#define BUF_3D_TILED_SURFACE   (1<<22)
+#define BUF_3D_TILE_WALK_X     0
+#define BUF_3D_TILE_WALK_Y     (1<<21)
+#define BUF_3D_PITCH(x)         (x)
+/* Dword 2 */
+#define BUF_3D_ADDR(x)         ((x) & ~0x3)
+
+/* 3DSTATE_CHROMA_KEY */
+
+/* 3DSTATE_CLEAR_PARAMETERS, p150 */
+#define _3DSTATE_CLEAR_PARAMETERS   (CMD_3D | (0x1d<<24) | (0x9c<<16) | 5)
+/* Dword 1 */
+#define CLEARPARAM_CLEAR_RECT      (1 << 16)
+#define CLEARPARAM_ZONE_INIT       (0 << 16)
+#define CLEARPARAM_WRITE_COLOR     (1 << 2)
+#define CLEARPARAM_WRITE_DEPTH     (1 << 1)
+#define CLEARPARAM_WRITE_STENCIL    (1 << 0)
+
+/* 3DSTATE_CONSTANT_BLEND_COLOR, p153 */
+#define _3DSTATE_CONST_BLEND_COLOR_CMD (CMD_3D | (0x1d<<24) | (0x88<<16))
+
+/* 3DSTATE_COORD_SET_BINDINGS, p154 */
+#define _3DSTATE_COORD_SET_BINDINGS      (CMD_3D | (0x16<<24))
+#define CSB_TCB(iunit, eunit)           ((eunit)<<(iunit*3))
+
+/* p156 */
+#define _3DSTATE_DFLT_DIFFUSE_CMD      (CMD_3D | (0x1d<<24) | (0x99<<16))
+
+/* p157 */
+#define _3DSTATE_DFLT_SPEC_CMD         (CMD_3D | (0x1d<<24) | (0x9a<<16))
+
+/* p158 */
+#define _3DSTATE_DFLT_Z_CMD            (CMD_3D | (0x1d<<24) | (0x98<<16))
+
+/* 3DSTATE_DEPTH_OFFSET_SCALE, p159 */
+#define _3DSTATE_DEPTH_OFFSET_SCALE       (CMD_3D | (0x1d<<24) | (0x97<<16))
+/* scale in dword 1 */
+
+/* The depth subrectangle is not supported, but must be disabled. */
+/* 3DSTATE_DEPTH_SUBRECT_DISABLE, p160 */
+#define _3DSTATE_DEPTH_SUBRECT_DISABLE (CMD_3D | (0x1c<<24) | (0x11<<19) | (1 << 1) | (0 << 0))
+
+/* p161 */
+#define _3DSTATE_DST_BUF_VARS_CMD      (CMD_3D | (0x1d<<24) | (0x85<<16))
+/* Dword 1 */
+#define TEX_DEFAULT_COLOR_OGL           (0<<30)
+#define TEX_DEFAULT_COLOR_D3D           (1<<30)
+#define ZR_EARLY_DEPTH                  (1<<29)
+#define LOD_PRECLAMP_OGL                (1<<28)
+#define LOD_PRECLAMP_D3D                (0<<28)
+#define DITHER_FULL_ALWAYS              (0<<26)
+#define DITHER_FULL_ON_FB_BLEND         (1<<26)
+#define DITHER_CLAMPED_ALWAYS           (2<<26)
+#define LINEAR_GAMMA_BLEND_32BPP        (1<<25)
+#define DEBUG_DISABLE_ENH_DITHER        (1<<24)
+#define DSTORG_HORT_BIAS(x)            ((x)<<20)
+#define DSTORG_VERT_BIAS(x)            ((x)<<16)
+#define COLOR_4_2_2_CHNL_WRT_ALL       0
+#define COLOR_4_2_2_CHNL_WRT_Y         (1<<12)
+#define COLOR_4_2_2_CHNL_WRT_CR                (2<<12)
+#define COLOR_4_2_2_CHNL_WRT_CB                (3<<12)
+#define COLOR_4_2_2_CHNL_WRT_CRCB      (4<<12)
+#define COLR_BUF_8BIT                  0
+#define COLR_BUF_RGB555                        (1<<8)
+#define COLR_BUF_RGB565                        (2<<8)
+#define COLR_BUF_ARGB8888              (3<<8)
+#define COLR_BUF_ARGB4444              (8<<8)
+#define COLR_BUF_ARGB1555              (9<<8)
+#define COLR_BUF_ARGB2AAA              (0xa<<8)
+#define DEPTH_FRMT_16_FIXED            0
+#define DEPTH_FRMT_16_FLOAT            (1<<2)
+#define DEPTH_FRMT_24_FIXED_8_OTHER    (2<<2)
+#define VERT_LINE_STRIDE_1             (1<<1)
+#define VERT_LINE_STRIDE_0             (0<<1)
+#define VERT_LINE_STRIDE_OFS_1         1
+#define VERT_LINE_STRIDE_OFS_0         0
+
+/* p166 */
+#define _3DSTATE_DRAW_RECT_CMD         (CMD_3D|(0x1d<<24)|(0x80<<16)|3)
+/* Dword 1 */
+#define DRAW_RECT_DIS_DEPTH_OFS                (1<<30)
+#define DRAW_DITHER_OFS_X(x)           ((x)<<26)
+#define DRAW_DITHER_OFS_Y(x)           ((x)<<24)
+/* Dword 2 */
+#define DRAW_YMIN(x)                   ((x)<<16)
+#define DRAW_XMIN(x)                   (x)
+/* Dword 3 */
+#define DRAW_YMAX(x)                   ((x-1)<<16)
+#define DRAW_XMAX(x)                   (x-1)
+/* Dword 4 */
+#define DRAW_YORG(x)                   ((x)<<16)
+#define DRAW_XORG(x)                   (x)
+
+/* 3DSTATE_FILTER_COEFFICIENTS_4X4, p170 */
+
+/* 3DSTATE_FILTER_COEFFICIENTS_6X5, p172 */
+
+/* _3DSTATE_FOG_COLOR, p173 */
+#define _3DSTATE_FOG_COLOR_CMD         (CMD_3D|(0x15<<24))
+#define FOG_COLOR_RED(x)               ((x)<<16)
+#define FOG_COLOR_GREEN(x)             ((x)<<8)
+#define FOG_COLOR_BLUE(x)              (x)
+
+/* _3DSTATE_FOG_MODE, p174 */
+#define _3DSTATE_FOG_MODE_CMD          (CMD_3D|(0x1d<<24)|(0x89<<16)|2)
+/* Dword 1 */
+#define FMC1_FOGFUNC_MODIFY_ENABLE     (1<<31)
+#define FMC1_FOGFUNC_VERTEX            (0<<28)
+#define FMC1_FOGFUNC_PIXEL_EXP         (1<<28)
+#define FMC1_FOGFUNC_PIXEL_EXP2                (2<<28)
+#define FMC1_FOGFUNC_PIXEL_LINEAR      (3<<28)
+#define FMC1_FOGFUNC_MASK              (3<<28)
+#define FMC1_FOGINDEX_MODIFY_ENABLE     (1<<27)
+#define FMC1_FOGINDEX_Z                        (0<<25)
+#define FMC1_FOGINDEX_W                        (1<<25)
+#define FMC1_C1_C2_MODIFY_ENABLE       (1<<24)
+#define FMC1_DENSITY_MODIFY_ENABLE     (1<<23)
+#define FMC1_C1_ONE                    (1<<13)
+#define FMC1_C1_MASK                   (0xffff<<4)
+/* Dword 2 */
+#define FMC2_C2_ONE                    (1<<16)
+/* Dword 3 */
+#define FMC3_D_ONE                     (1<<16)
+
+/* _3DSTATE_INDEPENDENT_ALPHA_BLEND, p177 */
+#define _3DSTATE_INDEPENDENT_ALPHA_BLEND_CMD   (CMD_3D|(0x0b<<24))
+#define IAB_MODIFY_ENABLE              (1<<23)
+#define IAB_ENABLE                     (1<<22)
+#define IAB_MODIFY_FUNC                        (1<<21)
+#define IAB_FUNC_SHIFT                 16
+#define IAB_MODIFY_SRC_FACTOR          (1<<11)
+#define IAB_SRC_FACTOR_SHIFT           6
+#define IAB_SRC_FACTOR_MASK            (BLENDFACT_MASK<<6)
+#define IAB_MODIFY_DST_FACTOR          (1<<5)
+#define IAB_DST_FACTOR_SHIFT           0
+#define IAB_DST_FACTOR_MASK            (BLENDFACT_MASK<<0)
+
+#define BLENDFACT_ZERO                 0x01
+#define BLENDFACT_ONE                  0x02
+#define BLENDFACT_SRC_COLR             0x03
+#define BLENDFACT_INV_SRC_COLR         0x04
+#define BLENDFACT_SRC_ALPHA            0x05
+#define BLENDFACT_INV_SRC_ALPHA                0x06
+#define BLENDFACT_DST_ALPHA            0x07
+#define BLENDFACT_INV_DST_ALPHA                0x08
+#define BLENDFACT_DST_COLR             0x09
+#define BLENDFACT_INV_DST_COLR         0x0a
+#define BLENDFACT_SRC_ALPHA_SATURATE   0x0b
+#define BLENDFACT_CONST_COLOR          0x0c
+#define BLENDFACT_INV_CONST_COLOR      0x0d
+#define BLENDFACT_CONST_ALPHA          0x0e
+#define BLENDFACT_INV_CONST_ALPHA      0x0f
+#define BLENDFACT_MASK                 0x0f
+
+#define BLENDFUNC_ADD                  0x0
+#define BLENDFUNC_SUBTRACT             0x1
+#define BLENDFUNC_REVERSE_SUBTRACT     0x2
+#define BLENDFUNC_MIN                  0x3
+#define BLENDFUNC_MAX                  0x4
+#define BLENDFUNC_MASK                 0x7
+
+/* 3DSTATE_LOAD_INDIRECT, p180 */
+
+#define _3DSTATE_LOAD_INDIRECT         (CMD_3D|(0x1d<<24)|(0x7<<16))
+#define LI0_STATE_STATIC_INDIRECT       (0x01<<8)
+#define LI0_STATE_DYNAMIC_INDIRECT      (0x02<<8)
+#define LI0_STATE_SAMPLER               (0x04<<8)
+#define LI0_STATE_MAP                   (0x08<<8)
+#define LI0_STATE_PROGRAM               (0x10<<8)
+#define LI0_STATE_CONSTANTS             (0x20<<8)
+
+#define SIS0_BUFFER_ADDRESS(x)          ((x)&~0x3)
+#define SIS0_FORCE_LOAD                 (1<<1)
+#define SIS0_BUFFER_VALID               (1<<0)
+#define SIS1_BUFFER_LENGTH(x)           ((x)&0xff)
+
+#define DIS0_BUFFER_ADDRESS(x)          ((x)&~0x3)
+#define DIS0_BUFFER_RESET               (1<<1)
+#define DIS0_BUFFER_VALID               (1<<0)
+
+#define SSB0_BUFFER_ADDRESS(x)          ((x)&~0x3)
+#define SSB0_FORCE_LOAD                 (1<<1)
+#define SSB0_BUFFER_VALID               (1<<0)
+#define SSB1_BUFFER_LENGTH(x)           ((x)&0xff)
+
+#define MSB0_BUFFER_ADDRESS(x)          ((x)&~0x3)
+#define MSB0_FORCE_LOAD                 (1<<1)
+#define MSB0_BUFFER_VALID               (1<<0)
+#define MSB1_BUFFER_LENGTH(x)           ((x)&0xff)
+
+#define PSP0_BUFFER_ADDRESS(x)          ((x)&~0x3)
+#define PSP0_FORCE_LOAD                 (1<<1)
+#define PSP0_BUFFER_VALID               (1<<0)
+#define PSP1_BUFFER_LENGTH(x)           ((x)&0xff)
+
+#define PSC0_BUFFER_ADDRESS(x)          ((x)&~0x3)
+#define PSC0_FORCE_LOAD                 (1<<1)
+#define PSC0_BUFFER_VALID               (1<<0)
+#define PSC1_BUFFER_LENGTH(x)           ((x)&0xff)
+
+/* _3DSTATE_RASTERIZATION_RULES */
+#define _3DSTATE_RASTER_RULES_CMD      (CMD_3D|(0x07<<24))
+#define ENABLE_POINT_RASTER_RULE       (1<<15)
+#define OGL_POINT_RASTER_RULE          (1<<13)
+#define ENABLE_TEXKILL_3D_4D            (1<<10)
+#define TEXKILL_3D                      (0<<9)
+#define TEXKILL_4D                      (1<<9)
+#define ENABLE_LINE_STRIP_PROVOKE_VRTX (1<<8)
+#define ENABLE_TRI_FAN_PROVOKE_VRTX    (1<<5)
+#define LINE_STRIP_PROVOKE_VRTX(x)     ((x)<<6)
+#define TRI_FAN_PROVOKE_VRTX(x)                ((x)<<3)
+
+/* _3DSTATE_SCISSOR_ENABLE, p256 */
+#define _3DSTATE_SCISSOR_ENABLE_CMD    (CMD_3D|(0x1c<<24)|(0x10<<19))
+#define ENABLE_SCISSOR_RECT            ((1<<1) | 1)
+#define DISABLE_SCISSOR_RECT           (1<<1)
+
+/* _3DSTATE_SCISSOR_RECTANGLE_0, p257 */
+#define _3DSTATE_SCISSOR_RECT_0_CMD    (CMD_3D|(0x1d<<24)|(0x81<<16)|1)
+/* Dword 1 */
+#define SCISSOR_RECT_0_YMIN(x)         ((x)<<16)
+#define SCISSOR_RECT_0_XMIN(x)         (x)
+/* Dword 2 */
+#define SCISSOR_RECT_0_YMAX(x)         ((x)<<16)
+#define SCISSOR_RECT_0_XMAX(x)         (x)
+
+/* p189 */
+#define _3DSTATE_LOAD_STATE_IMMEDIATE_1   (CMD_3D | (0x1d<<24) | (0x04<<16))
+#define I1_LOAD_S(n)                      (1<<(4+n))
+
+#define S0_VB_OFFSET_MASK              0xffffffc
+#define S0_AUTO_CACHE_INV_DISABLE      (1<<0)
+
+#define S1_VERTEX_WIDTH_SHIFT          24
+#define S1_VERTEX_WIDTH_MASK           (0x3f<<24)
+#define S1_VERTEX_PITCH_SHIFT          16
+#define S1_VERTEX_PITCH_MASK           (0x3f<<16)
+
+#define TEXCOORDFMT_2D                 0x0
+#define TEXCOORDFMT_3D                 0x1
+#define TEXCOORDFMT_4D                 0x2
+#define TEXCOORDFMT_1D                 0x3
+#define TEXCOORDFMT_2D_16              0x4
+#define TEXCOORDFMT_4D_16              0x5
+#define TEXCOORDFMT_NOT_PRESENT        0xf
+#define S2_TEXCOORD_FMT0_MASK            0xf
+#define S2_TEXCOORD_FMT1_SHIFT           4
+#define S2_TEXCOORD_FMT(unit, type)    ((type)<<(unit*4))
+#define S2_TEXCOORD_NONE               (~0U)
+
+#define TEXCOORD_WRAP_SHORTEST_TCX     8
+#define TEXCOORD_WRAP_SHORTEST_TCY     4
+#define TEXCOORD_WRAP_SHORTEST_TCZ     2
+#define TEXCOORD_PERSPECTIVE_DISABLE   1
+
+#define S3_WRAP_SHORTEST_TCX(unit)     (TEXCOORD_WRAP_SHORTEST_TCX << ((unit) * 4))
+#define S3_WRAP_SHORTEST_TCY(unit)     (TEXCOORD_WRAP_SHORTEST_TCY << ((unit) * 4))
+#define S3_WRAP_SHORTEST_TCZ(unit)     (TEXCOORD_WRAP_SHORTEST_TCZ << ((unit) * 4))
+#define S3_PERSPECTIVE_DISABLE(unit)   (TEXCOORD_PERSPECTIVE_DISABLE << ((unit) * 4))
+
+/* S3 not interesting */
+
+#define S4_POINT_WIDTH_SHIFT           23
+#define S4_POINT_WIDTH_MASK            (0x1ff<<23)
+#define S4_LINE_WIDTH_SHIFT            19
+#define S4_LINE_WIDTH_ONE              (0x2<<19)
+#define S4_LINE_WIDTH_MASK             (0xf<<19)
+#define S4_FLATSHADE_ALPHA             (1<<18)
+#define S4_FLATSHADE_FOG               (1<<17)
+#define S4_FLATSHADE_SPECULAR          (1<<16)
+#define S4_FLATSHADE_COLOR             (1<<15)
+#define S4_CULLMODE_BOTH              (0<<13)
+#define S4_CULLMODE_NONE              (1<<13)
+#define S4_CULLMODE_CW                (2<<13)
+#define S4_CULLMODE_CCW                       (3<<13)
+#define S4_CULLMODE_MASK              (3<<13)
+#define S4_VFMT_POINT_WIDTH            (1<<12)
+#define S4_VFMT_SPEC_FOG               (1<<11)
+#define S4_VFMT_COLOR                  (1<<10)
+#define S4_VFMT_DEPTH_OFFSET           (1<<9)
+#define S4_VFMT_XYZ                   (1<<6)
+#define S4_VFMT_XYZW                  (2<<6)
+#define S4_VFMT_XY                    (3<<6)
+#define S4_VFMT_XYW                   (4<<6)
+#define S4_VFMT_XYZW_MASK              (7<<6)
+#define S4_FORCE_DEFAULT_DIFFUSE       (1<<5)
+#define S4_FORCE_DEFAULT_SPECULAR      (1<<4)
+#define S4_LOCAL_DEPTH_OFFSET_ENABLE   (1<<3)
+#define S4_VFMT_FOG_PARAM              (1<<2)
+#define S4_SPRITE_POINT_ENABLE         (1<<1)
+#define S4_LINE_ANTIALIAS_ENABLE       (1<<0)
+
+#define S4_VFMT_MASK (S4_VFMT_POINT_WIDTH   |  \
+                     S4_VFMT_SPEC_FOG      |   \
+                     S4_VFMT_COLOR         |   \
+                     S4_VFMT_DEPTH_OFFSET  |   \
+                     S4_VFMT_XYZW_MASK     |   \
+                     S4_VFMT_FOG_PARAM)
+
+#define S5_WRITEDISABLE_ALPHA          (1<<31)
+#define S5_WRITEDISABLE_RED            (1<<30)
+#define S5_WRITEDISABLE_GREEN          (1<<29)
+#define S5_WRITEDISABLE_BLUE           (1<<28)
+#define S5_WRITEDISABLE_MASK           (0xf<<28)
+#define S5_FORCE_DEFAULT_POINT_SIZE    (1<<27)
+#define S5_LAST_PIXEL_ENABLE           (1<<26)
+#define S5_GLOBAL_DEPTH_OFFSET_ENABLE  (1<<25)
+#define S5_FOG_ENABLE                  (1<<24)
+#define S5_STENCIL_REF_SHIFT           16
+#define S5_STENCIL_REF_MASK            (0xff<<16)
+#define S5_STENCIL_TEST_FUNC_SHIFT     13
+#define S5_STENCIL_TEST_FUNC_MASK      (0x7<<13)
+#define S5_STENCIL_FAIL_SHIFT          10
+#define S5_STENCIL_FAIL_MASK           (0x7<<10)
+#define S5_STENCIL_PASS_Z_FAIL_SHIFT   7
+#define S5_STENCIL_PASS_Z_FAIL_MASK    (0x7<<7)
+#define S5_STENCIL_PASS_Z_PASS_SHIFT   4
+#define S5_STENCIL_PASS_Z_PASS_MASK    (0x7<<4)
+#define S5_STENCIL_WRITE_ENABLE        (1<<3)
+#define S5_STENCIL_TEST_ENABLE         (1<<2)
+#define S5_COLOR_DITHER_ENABLE         (1<<1)
+#define S5_LOGICOP_ENABLE              (1<<0)
+
+#define COMPAREFUNC_ALWAYS             0
+#define COMPAREFUNC_NEVER              0x1
+#define COMPAREFUNC_LESS               0x2
+#define COMPAREFUNC_EQUAL              0x3
+#define COMPAREFUNC_LEQUAL             0x4
+#define COMPAREFUNC_GREATER            0x5
+#define COMPAREFUNC_NOTEQUAL           0x6
+#define COMPAREFUNC_GEQUAL             0x7
+
+#define STENCILOP_KEEP                 0
+#define STENCILOP_ZERO                 0x1
+#define STENCILOP_REPLACE              0x2
+#define STENCILOP_INCRSAT              0x3
+#define STENCILOP_DECRSAT              0x4
+#define STENCILOP_INCR                 0x5
+#define STENCILOP_DECR                 0x6
+#define STENCILOP_INVERT               0x7
+
+#define S6_ALPHA_TEST_ENABLE           (1<<31)
+#define S6_ALPHA_TEST_FUNC_SHIFT       28
+#define S6_ALPHA_TEST_FUNC_MASK        (0x7<<28)
+#define S6_ALPHA_REF_SHIFT             20
+#define S6_ALPHA_REF_MASK              (0xff<<20)
+#define S6_DEPTH_TEST_ENABLE           (1<<19)
+#define S6_DEPTH_TEST_FUNC_SHIFT       16
+#define S6_DEPTH_TEST_FUNC_MASK        (0x7<<16)
+#define S6_CBUF_BLEND_ENABLE           (1<<15)
+#define S6_CBUF_BLEND_FUNC_SHIFT       12
+#define S6_CBUF_BLEND_FUNC_MASK        (0x7<<12)
+#define S6_CBUF_SRC_BLEND_FACT_SHIFT   8
+#define S6_CBUF_SRC_BLEND_FACT_MASK    (0xf<<8)
+#define S6_CBUF_DST_BLEND_FACT_SHIFT   4
+#define S6_CBUF_DST_BLEND_FACT_MASK    (0xf<<4)
+#define S6_DEPTH_WRITE_ENABLE          (1<<3)
+#define S6_COLOR_WRITE_ENABLE          (1<<2)
+#define S6_TRISTRIP_PV_SHIFT           0
+#define S6_TRISTRIP_PV_MASK            (0x3<<0)
+
+#define S7_DEPTH_OFFSET_CONST_MASK     ~0
+
+/* 3DSTATE_MAP_DEINTERLACER_PARAMETERS */
+/* 3DSTATE_MAP_PALETTE_LOAD_32, p206 */
+
+/* _3DSTATE_MODES_4, p218 */
+#define _3DSTATE_MODES_4_CMD           (CMD_3D|(0x0d<<24))
+#define ENABLE_LOGIC_OP_FUNC           (1<<23)
+#define LOGIC_OP_FUNC(x)               ((x)<<18)
+#define LOGICOP_MASK                   (0xf<<18)
+#define LOGICOP_COPY                   0xc
+#define MODE4_ENABLE_STENCIL_TEST_MASK ((1<<17)|(0xff00))
+#define ENABLE_STENCIL_TEST_MASK       (1<<17)
+#define STENCIL_TEST_MASK(x)           ((x)<<8)
+#define MODE4_ENABLE_STENCIL_WRITE_MASK        ((1<<16)|(0x00ff))
+#define ENABLE_STENCIL_WRITE_MASK      (1<<16)
+#define STENCIL_WRITE_MASK(x)          ((x)&0xff)
+
+/* _3DSTATE_MODES_5, p220 */
+#define _3DSTATE_MODES_5_CMD           (CMD_3D|(0x0c<<24))
+#define PIPELINE_FLUSH_RENDER_CACHE    (1<<18)
+#define PIPELINE_FLUSH_TEXTURE_CACHE   (1<<16)
+
+/* p221 */
+#define _3DSTATE_PIXEL_SHADER_CONSTANTS  (CMD_3D|(0x1d<<24)|(0x6<<16))
+#define PS1_REG(n)                      (1<<(n))
+#define PS2_CONST_X(n)                  (n)
+#define PS3_CONST_Y(n)                  (n)
+#define PS4_CONST_Z(n)                  (n)
+#define PS5_CONST_W(n)                  (n)
+
+/* p222 */
+
+#define I915_MAX_TEX_INDIRECT 4
+#define I915_MAX_TEX_INSN     32
+#define I915_MAX_ALU_INSN     64
+#define I915_MAX_DECL_INSN    27
+#define I915_MAX_TEMPORARY    16
+
+/* Each instruction is 3 dwords long, though most don't require all
+ * this space.  Maximum of 123 instructions.  Smaller maxes per insn
+ * type.
+ */
+#define _3DSTATE_PIXEL_SHADER_PROGRAM    (CMD_3D|(0x1d<<24)|(0x5<<16))
+
+#define REG_TYPE_R                 0 /* temporary regs, no need to
+                                     * dcl, must be written before
+                                     * read -- Preserved between
+                                     * phases.
+                                     */
+#define REG_TYPE_T                 1 /* Interpolated values, must be
+                                     * dcl'ed before use.
+                                     *
+                                     * 0..7: texture coord,
+                                     * 8: diffuse spec,
+                                     * 9: specular color,
+                                     * 10: fog parameter in w.
+                                     */
+#define REG_TYPE_CONST             2 /* Restriction: only one const
+                                     * can be referenced per
+                                     * instruction, though it may be
+                                     * selected for multiple inputs.
+                                     * Constants not initialized
+                                     * default to zero.
+                                     */
+#define REG_TYPE_S                 3 /* sampler */
+#define REG_TYPE_OC                4 /* output color (rgba) */
+#define REG_TYPE_OD                5 /* output depth (w), xyz are
+                                     * temporaries.  If not written,
+                                     * interpolated depth is used?
+                                     */
+#define REG_TYPE_U                 6 /* unpreserved temporaries */
+#define REG_TYPE_MASK              0x7
+#define REG_NR_MASK                0xf
+
+/* REG_TYPE_T:
+ */
+#define T_TEX0     0
+#define T_TEX1     1
+#define T_TEX2     2
+#define T_TEX3     3
+#define T_TEX4     4
+#define T_TEX5     5
+#define T_TEX6     6
+#define T_TEX7     7
+#define T_DIFFUSE  8
+#define T_SPECULAR 9
+#define T_FOG_W    10          /* interpolated fog is in W coord */
+
+/* Arithmetic instructions */
+
+/* .replicate_swizzle == selection and replication of a particular
+ * scalar channel, ie., .xxxx, .yyyy, .zzzz or .wwww
+ */
+#define A0_NOP    (0x0<<24)            /* no operation */
+#define A0_ADD    (0x1<<24)            /* dst = src0 + src1 */
+#define A0_MOV    (0x2<<24)            /* dst = src0 */
+#define A0_MUL    (0x3<<24)            /* dst = src0 * src1 */
+#define A0_MAD    (0x4<<24)            /* dst = src0 * src1 + src2 */
+#define A0_DP2ADD (0x5<<24)            /* dst.xyzw = src0.xy dot src1.xy + src2.replicate_swizzle */
+#define A0_DP3    (0x6<<24)            /* dst.xyzw = src0.xyz dot src1.xyz */
+#define A0_DP4    (0x7<<24)            /* dst.xyzw = src0.xyzw dot src1.xyzw */
+#define A0_FRC    (0x8<<24)            /* dst = src0 - floor(src0) */
+#define A0_RCP    (0x9<<24)            /* dst.xyzw = 1/(src0.replicate_swizzle) */
+#define A0_RSQ    (0xa<<24)            /* dst.xyzw = 1/(sqrt(abs(src0.replicate_swizzle))) */
+#define A0_EXP    (0xb<<24)            /* dst.xyzw = exp2(src0.replicate_swizzle) */
+#define A0_LOG    (0xc<<24)            /* dst.xyzw = log2(abs(src0.replicate_swizzle)) */
+#define A0_CMP    (0xd<<24)            /* dst = (src0 >= 0.0) ? src1 : src2 */
+#define A0_MIN    (0xe<<24)            /* dst = (src0 < src1) ? src0 : src1 */
+#define A0_MAX    (0xf<<24)            /* dst = (src0 >= src1) ? src0 : src1 */
+#define A0_FLR    (0x10<<24)           /* dst = floor(src0) */
+#define A0_MOD    (0x11<<24)           /* dst = src0 fmod 1.0 */
+#define A0_TRC    (0x12<<24)           /* dst = int(src0) */
+#define A0_SGE    (0x13<<24)           /* dst = src0 >= src1 ? 1.0 : 0.0 */
+#define A0_SLT    (0x14<<24)           /* dst = src0 < src1 ? 1.0 : 0.0 */
+#define A0_DEST_SATURATE                 (1<<22)
+#define A0_DEST_TYPE_SHIFT                19
+/* Allow: R, OC, OD, U */
+#define A0_DEST_NR_SHIFT                 14
+/* Allow R: 0..15, OC,OD: 0..0, U: 0..2 */
+#define A0_DEST_CHANNEL_X                (1<<10)
+#define A0_DEST_CHANNEL_Y                (2<<10)
+#define A0_DEST_CHANNEL_Z                (4<<10)
+#define A0_DEST_CHANNEL_W                (8<<10)
+#define A0_DEST_CHANNEL_ALL              (0xf<<10)
+#define A0_DEST_CHANNEL_SHIFT            10
+#define A0_SRC0_TYPE_SHIFT               7
+#define A0_SRC0_NR_SHIFT                 2
+
+#define A0_DEST_CHANNEL_XY              (A0_DEST_CHANNEL_X|A0_DEST_CHANNEL_Y)
+#define A0_DEST_CHANNEL_XYZ             (A0_DEST_CHANNEL_XY|A0_DEST_CHANNEL_Z)
+
+#define SRC_X        0
+#define SRC_Y        1
+#define SRC_Z        2
+#define SRC_W        3
+#define SRC_ZERO     4
+#define SRC_ONE      5
+
+#define A1_SRC0_CHANNEL_X_NEGATE         (1<<31)
+#define A1_SRC0_CHANNEL_X_SHIFT          28
+#define A1_SRC0_CHANNEL_Y_NEGATE         (1<<27)
+#define A1_SRC0_CHANNEL_Y_SHIFT          24
+#define A1_SRC0_CHANNEL_Z_NEGATE         (1<<23)
+#define A1_SRC0_CHANNEL_Z_SHIFT          20
+#define A1_SRC0_CHANNEL_W_NEGATE         (1<<19)
+#define A1_SRC0_CHANNEL_W_SHIFT          16
+#define A1_SRC1_TYPE_SHIFT               13
+#define A1_SRC1_NR_SHIFT                 8
+#define A1_SRC1_CHANNEL_X_NEGATE         (1<<7)
+#define A1_SRC1_CHANNEL_X_SHIFT          4
+#define A1_SRC1_CHANNEL_Y_NEGATE         (1<<3)
+#define A1_SRC1_CHANNEL_Y_SHIFT          0
+
+#define A2_SRC1_CHANNEL_Z_NEGATE         (1<<31)
+#define A2_SRC1_CHANNEL_Z_SHIFT          28
+#define A2_SRC1_CHANNEL_W_NEGATE         (1<<27)
+#define A2_SRC1_CHANNEL_W_SHIFT          24
+#define A2_SRC2_TYPE_SHIFT               21
+#define A2_SRC2_NR_SHIFT                 16
+#define A2_SRC2_CHANNEL_X_NEGATE         (1<<15)
+#define A2_SRC2_CHANNEL_X_SHIFT          12
+#define A2_SRC2_CHANNEL_Y_NEGATE         (1<<11)
+#define A2_SRC2_CHANNEL_Y_SHIFT          8
+#define A2_SRC2_CHANNEL_Z_NEGATE         (1<<7)
+#define A2_SRC2_CHANNEL_Z_SHIFT          4
+#define A2_SRC2_CHANNEL_W_NEGATE         (1<<3)
+#define A2_SRC2_CHANNEL_W_SHIFT          0
+
+/* Texture instructions */
+#define T0_TEXLD     (0x15<<24)        /* Sample texture using predeclared
+                                * sampler and address, and output
+                                * filtered texel data to destination
+                                * register */
+#define T0_TEXLDP    (0x16<<24)        /* Same as texld but performs a
+                                * perspective divide of the texture
+                                * coordinate .xyz values by .w before
+                                * sampling. */
+#define T0_TEXLDB    (0x17<<24)        /* Same as texld but biases the
+                                * computed LOD by w.  Only S4.6 two's
+                                * comp is used.  This implies that a
+                                * float to fixed conversion is
+                                * done. */
+#define T0_TEXKILL   (0x18<<24)        /* Does not perform a sampling
+                                * operation.  Simply kills the pixel
+                                * if any channel of the address
+                                * register is < 0.0. */
+#define T0_DEST_TYPE_SHIFT                19
+/* Allow: R, OC, OD, U */
+/* Note: U (unpreserved) regs do not retain their values between
+ * phases (cannot be used for feedback)
+ *
+ * Note: oC and OD registers can only be used as the destination of a
+ * texture instruction once per phase (this is an implementation
+ * restriction).
+ */
+#define T0_DEST_NR_SHIFT                 14
+/* Allow R: 0..15, OC,OD: 0..0, U: 0..2 */
+#define T0_SAMPLER_NR_SHIFT              0 /* This field ignored for TEXKILL */
+#define T0_SAMPLER_NR_MASK               (0xf<<0)
+
+#define T1_ADDRESS_REG_TYPE_SHIFT        24 /* Reg to use as texture coord */
+/* Allow R, T, OC, OD -- R, OC, OD are 'dependent' reads, new program phase */
+#define T1_ADDRESS_REG_NR_SHIFT          17
+#define T2_MBZ                           0
+
+/* Declaration instructions */
+#define D0_DCL       (0x19<<24)        /* Declare a t (interpolated attrib)
+                                * register or an s (sampler)
+                                * register. */
+#define D0_SAMPLE_TYPE_SHIFT              22
+#define D0_SAMPLE_TYPE_2D                 (0x0<<22)
+#define D0_SAMPLE_TYPE_CUBE               (0x1<<22)
+#define D0_SAMPLE_TYPE_VOLUME             (0x2<<22)
+#define D0_SAMPLE_TYPE_MASK               (0x3<<22)
+
+#define D0_TYPE_SHIFT                19
+/* Allow: T, S */
+#define D0_NR_SHIFT                  14
+/* Allow T: 0..10, S: 0..15 */
+#define D0_CHANNEL_X                (1<<10)
+#define D0_CHANNEL_Y                (2<<10)
+#define D0_CHANNEL_Z                (4<<10)
+#define D0_CHANNEL_W                (8<<10)
+#define D0_CHANNEL_ALL              (0xf<<10)
+#define D0_CHANNEL_NONE             (0<<10)
+
+#define D0_CHANNEL_XY               (D0_CHANNEL_X|D0_CHANNEL_Y)
+#define D0_CHANNEL_XYZ              (D0_CHANNEL_XY|D0_CHANNEL_Z)
+
+/* I915 Errata: Do not allow (xz), (xw), (xzw) combinations for diffuse
+ * or specular declarations.
+ *
+ * For T dcls, only allow: (x), (xy), (xyz), (w), (xyzw)
+ *
+ * Must be zero for S (sampler) dcls
+ */
+#define D1_MBZ                          0
+#define D2_MBZ                          0
+
+/* p207.
+ * The DWORD count is 3 times the number of bits set in MS1_MAPMASK_MASK
+ */
+#define _3DSTATE_MAP_STATE               (CMD_3D|(0x1d<<24)|(0x0<<16))
+
+#define MS1_MAPMASK_SHIFT               0
+#define MS1_MAPMASK_MASK                (0x8fff<<0)
+
+#define MS2_UNTRUSTED_SURFACE           (1<<31)
+#define MS2_ADDRESS_MASK                0xfffffffc
+#define MS2_VERTICAL_LINE_STRIDE        (1<<1)
+#define MS2_VERTICAL_OFFSET             (1<<1)
+
+#define MS3_HEIGHT_SHIFT              21
+#define MS3_WIDTH_SHIFT               10
+#define MS3_PALETTE_SELECT            (1<<9)
+#define MS3_MAPSURF_FORMAT_SHIFT      7
+#define MS3_MAPSURF_FORMAT_MASK       (0x7<<7)
+#define    MAPSURF_8BIT                           (1<<7)
+#define    MAPSURF_16BIT                  (2<<7)
+#define    MAPSURF_32BIT                  (3<<7)
+#define    MAPSURF_422                    (5<<7)
+#define    MAPSURF_COMPRESSED             (6<<7)
+#define    MAPSURF_4BIT_INDEXED                   (7<<7)
+#define MS3_MT_FORMAT_MASK         (0x7 << 3)
+#define MS3_MT_FORMAT_SHIFT        3
+#define    MT_4BIT_IDX_ARGB8888                   (7<<3) /* SURFACE_4BIT_INDEXED */
+#define    MT_8BIT_I8                     (0<<3) /* SURFACE_8BIT */
+#define    MT_8BIT_L8                     (1<<3)
+#define    MT_8BIT_A8                     (4<<3)
+#define    MT_8BIT_MONO8                  (5<<3)
+#define    MT_16BIT_RGB565                (0<<3) /* SURFACE_16BIT */
+#define    MT_16BIT_ARGB1555              (1<<3)
+#define    MT_16BIT_ARGB4444              (2<<3)
+#define    MT_16BIT_AY88                  (3<<3)
+#define    MT_16BIT_88DVDU                (5<<3)
+#define    MT_16BIT_BUMP_655LDVDU         (6<<3)
+#define    MT_16BIT_I16                           (7<<3)
+#define    MT_16BIT_L16                           (8<<3)
+#define    MT_16BIT_A16                           (9<<3)
+#define    MT_32BIT_ARGB8888              (0<<3) /* SURFACE_32BIT */
+#define    MT_32BIT_ABGR8888              (1<<3)
+#define    MT_32BIT_XRGB8888              (2<<3)
+#define    MT_32BIT_XBGR8888              (3<<3)
+#define    MT_32BIT_QWVU8888              (4<<3)
+#define    MT_32BIT_AXVU8888              (5<<3)
+#define    MT_32BIT_LXVU8888              (6<<3)
+#define    MT_32BIT_XLVU8888              (7<<3)
+#define    MT_32BIT_ARGB2101010                   (8<<3)
+#define    MT_32BIT_ABGR2101010                   (9<<3)
+#define    MT_32BIT_AWVU2101010                   (0xA<<3)
+#define    MT_32BIT_GR1616                (0xB<<3)
+#define    MT_32BIT_VU1616                (0xC<<3)
+#define    MT_32BIT_xI824                 (0xD<<3)
+#define    MT_32BIT_xA824                 (0xE<<3)
+#define    MT_32BIT_xL824                 (0xF<<3)
+#define    MT_422_YCRCB_SWAPY             (0<<3) /* SURFACE_422 */
+#define    MT_422_YCRCB_NORMAL            (1<<3)
+#define    MT_422_YCRCB_SWAPUV            (2<<3)
+#define    MT_422_YCRCB_SWAPUVY                   (3<<3)
+#define    MT_COMPRESS_DXT1               (0<<3) /* SURFACE_COMPRESSED */
+#define    MT_COMPRESS_DXT2_3             (1<<3)
+#define    MT_COMPRESS_DXT4_5             (2<<3)
+#define    MT_COMPRESS_FXT1               (3<<3)
+#define    MT_COMPRESS_DXT1_RGB                   (4<<3)
+#define MS3_USE_FENCE_REGS              (1<<2)
+#define MS3_TILED_SURFACE             (1<<1)
+#define MS3_TILE_WALK                 (1<<0)
+
+/* The pitch is the pitch measured in DWORDS, minus 1 */
+#define MS4_PITCH_SHIFT                 21
+#define MS4_CUBE_FACE_ENA_NEGX          (1<<20)
+#define MS4_CUBE_FACE_ENA_POSX          (1<<19)
+#define MS4_CUBE_FACE_ENA_NEGY          (1<<18)
+#define MS4_CUBE_FACE_ENA_POSY          (1<<17)
+#define MS4_CUBE_FACE_ENA_NEGZ          (1<<16)
+#define MS4_CUBE_FACE_ENA_POSZ          (1<<15)
+#define MS4_CUBE_FACE_ENA_MASK          (0x3f<<15)
+#define MS4_MAX_LOD_SHIFT              9
+#define MS4_MAX_LOD_MASK               (0x3f<<9)
+#define MS4_MIP_LAYOUT_LEGACY           (0<<8)
+#define MS4_MIP_LAYOUT_BELOW_LPT        (0<<8)
+#define MS4_MIP_LAYOUT_RIGHT_LPT        (1<<8)
+#define MS4_VOLUME_DEPTH_SHIFT          0
+#define MS4_VOLUME_DEPTH_MASK           (0xff<<0)
+
+/* p244.
+ * The DWORD count is 3 times the number of bits set in SS1_MAPMASK_MASK.
+ */
+#define _3DSTATE_SAMPLER_STATE         (CMD_3D|(0x1d<<24)|(0x1<<16))
+
+#define SS1_MAPMASK_SHIFT               0
+#define SS1_MAPMASK_MASK                (0x8fff<<0)
+
+#define SS2_REVERSE_GAMMA_ENABLE        (1<<31)
+#define SS2_PACKED_TO_PLANAR_ENABLE     (1<<30)
+#define SS2_COLORSPACE_CONVERSION       (1<<29)
+#define SS2_CHROMAKEY_SHIFT             27
+#define SS2_BASE_MIP_LEVEL_SHIFT        22
+#define SS2_BASE_MIP_LEVEL_MASK         (0x1f<<22)
+#define SS2_MIP_FILTER_SHIFT            20
+#define SS2_MIP_FILTER_MASK             (0x3<<20)
+#define   MIPFILTER_NONE       0
+#define   MIPFILTER_NEAREST    1
+#define   MIPFILTER_LINEAR     3
+#define SS2_MAG_FILTER_SHIFT          17
+#define SS2_MAG_FILTER_MASK           (0x7<<17)
+#define   FILTER_NEAREST       0
+#define   FILTER_LINEAR                1
+#define   FILTER_ANISOTROPIC   2
+#define   FILTER_4X4_1         3
+#define   FILTER_4X4_2         4
+#define   FILTER_4X4_FLAT      5
+#define   FILTER_6X5_MONO      6 /* XXX - check */
+#define SS2_MIN_FILTER_SHIFT          14
+#define SS2_MIN_FILTER_MASK           (0x7<<14)
+#define SS2_LOD_BIAS_SHIFT            5
+#define SS2_LOD_BIAS_ONE              (0x10<<5)
+#define SS2_LOD_BIAS_MASK             (0x1ff<<5)
+/* Shadow requires:
+ *  MT_X8{I,L,A}24 or MT_{I,L,A}16 texture format
+ *  FILTER_4X4_x  MIN and MAG filters
+ */
+#define SS2_SHADOW_ENABLE             (1<<4)
+#define SS2_MAX_ANISO_MASK            (1<<3)
+#define SS2_MAX_ANISO_2               (0<<3)
+#define SS2_MAX_ANISO_4               (1<<3)
+#define SS2_SHADOW_FUNC_SHIFT         0
+#define SS2_SHADOW_FUNC_MASK          (0x7<<0)
+/* SS2_SHADOW_FUNC values: see COMPAREFUNC_* */
+
+#define SS3_MIN_LOD_SHIFT            24
+#define SS3_MIN_LOD_ONE              (0x10<<24)
+#define SS3_MIN_LOD_MASK             (0xff<<24)
+#define SS3_KILL_PIXEL_ENABLE        (1<<17)
+#define SS3_TCX_ADDR_MODE_SHIFT      12
+#define SS3_TCX_ADDR_MODE_MASK       (0x7<<12)
+#define   TEXCOORDMODE_WRAP            0
+#define   TEXCOORDMODE_MIRROR          1
+#define   TEXCOORDMODE_CLAMP_EDGE      2
+#define   TEXCOORDMODE_CUBE            3
+#define   TEXCOORDMODE_CLAMP_BORDER    4
+#define   TEXCOORDMODE_MIRROR_ONCE      5
+#define SS3_TCY_ADDR_MODE_SHIFT      9
+#define SS3_TCY_ADDR_MODE_MASK       (0x7<<9)
+#define SS3_TCZ_ADDR_MODE_SHIFT      6
+#define SS3_TCZ_ADDR_MODE_MASK       (0x7<<6)
+#define SS3_NORMALIZED_COORDS        (1<<5)
+#define SS3_TEXTUREMAP_INDEX_SHIFT   1
+#define SS3_TEXTUREMAP_INDEX_MASK    (0xf<<1)
+#define SS3_DEINTERLACER_ENABLE      (1<<0)
+
+#define SS4_BORDER_COLOR_MASK        (~0)
+
+/* 3DSTATE_SPAN_STIPPLE, p258
+ */
+#define _3DSTATE_STIPPLE           ((0x3<<29)|(0x1d<<24)|(0x83<<16))
+#define ST1_ENABLE               (1<<16)
+#define ST1_MASK                 (0xffff)
+
+#define FLUSH_MAP_CACHE    (1<<0)
+#define FLUSH_RENDER_CACHE (1<<1)
+
+/* BLT commands */
+#define COLOR_BLT_CMD                  (CMD_2D | (0x40 << 22) | 3)
+#define XY_COLOR_BLT_CMD               (CMD_2D | (0x50 << 22) | 4)
+#define XY_SETUP_CLIP_BLT_CMD          (CMD_2D | (0x03 << 22) | 1)
+#define XY_SRC_COPY_BLT_CMD             (CMD_2D | (0x53 << 22) | 6)
+#define SRC_COPY_BLT_CMD               (CMD_2D | (0x43 << 22) | 4)
+
+#define XY_MONO_PAT_BLT_CMD            (CMD_2D  | (0x52<<22)|0x7)
+#define XY_MONO_PAT_VERT_SEED          ((1<<10) | (1<<9)|(1<<8))
+#define XY_MONO_PAT_HORT_SEED          ((1<<14) | (1<<13)|(1<<12))
+#define XY_MONO_SRC_BLT_CMD            (CMD_2D  | (0x54<<22)|(0x6))
+
+#define XY_SETUP_BLT_CMD               (CMD_2D | (0x01 << 22) | 6)
+#define XY_TEXT_IMMEDIATE_BLIT_CMD     (CMD_2D | (0x31 << 22))
+#define XY_TEXT_BYTE_PACKED            (1 << 16)
+
+/* BR00 */
+#define XY_BLT_WRITE_ALPHA     (1 << 21)
+#define XY_BLT_WRITE_RGB       (1 << 20)
+#define XY_SRC_TILED           (1 << 15)
+#define XY_DST_TILED           (1 << 11)
+
+/* BR13 */
+#define BR13_565               (0x1 << 24)
+#define BR13_8888              (0x3 << 24)
+
+#endif /* CAIRO_DRM_INTEL_COMMAND_PRIVATE_H */
diff --git a/src/drm/cairo-drm-intel-debug.c b/src/drm/cairo-drm-intel-debug.c
new file mode 100644 (file)
index 0000000..7068c93
--- /dev/null
@@ -0,0 +1,1209 @@
+/**************************************************************************
+ *
+ * Copyright 2003 Tungsten Graphics, Inc., Cedar Park, Texas.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sub license, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
+ * IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ **************************************************************************/
+
+#include "cairoint.h"
+#include "cairo-drm-intel-private.h"
+
+struct debug_stream {
+    unsigned offset;   /* current gtt offset */
+    const char *ptr;           /* pointer to gtt offset zero */
+    const char *end;           /* pointer to gtt offset zero */
+};
+
+static cairo_bool_t
+debug (struct debug_stream *stream, const char *name, uint32_t len)
+{
+    uint32_t i;
+    const uint32_t *ptr = (uint32_t *)(stream->ptr + stream->offset);
+
+    if (len == 0) {
+       fprintf (stderr, "Error - zero length packet (0x%08x)\n", stream->ptr[0]);
+       ASSERT_NOT_REACHED;
+       return FALSE;
+    }
+
+    fprintf (stderr, "%04x:  ", stream->offset);
+
+    fprintf (stderr, "%s (%d dwords):\n", name, len);
+    for (i = 0; i < len; i++)
+       fprintf (stderr, "\t0x%08x\n",  ptr[i]);
+    fprintf (stderr, "\n");
+
+    stream->offset += len * sizeof(uint32_t);
+    return TRUE;
+}
+
+
+static const char *
+get_prim_name (uint32_t val)
+{
+    switch (val & PRIM3D_MASK) {
+    case PRIM3D_TRILIST: return "TRILIST";
+    case PRIM3D_TRISTRIP: return "TRISTRIP";
+    case PRIM3D_TRISTRIP_RVRSE: return "TRISTRIP_RVRSE";
+    case PRIM3D_TRIFAN: return "TRIFAN";
+    case PRIM3D_POLY: return "POLY";
+    case PRIM3D_LINELIST: return "LINELIST";
+    case PRIM3D_LINESTRIP: return "LINESTRIP";
+    case PRIM3D_RECTLIST: return "RECTLIST";
+    case PRIM3D_POINTLIST: return "POINTLIST";
+    case PRIM3D_DIB: return "DIB";
+    case PRIM3D_CLEAR_RECT: return "CLEAR_RECT";
+    case PRIM3D_ZONE_INIT: return "ZONE_INIT";
+    default: return "????";
+    }
+}
+
+static cairo_bool_t
+debug_prim (struct debug_stream *stream,
+           const char *name,
+           cairo_bool_t dump_floats,
+           uint32_t len)
+{
+    uint32_t *ptr = (uint32_t *)(stream->ptr + stream->offset);
+    const char *prim = get_prim_name( ptr[0] );
+    uint32_t i;
+
+    fprintf (stderr, "%04x:  ", stream->offset);
+    fprintf (stderr, "%s %s (%d dwords):\n", name, prim, len);
+    fprintf (stderr, "\t0x%08x\n",  ptr[0]);
+    for (i = 1; i < len; i++) {
+       if (dump_floats)
+           fprintf (stderr, "\t0x%08x // %f\n",  ptr[i], *(float *)&ptr[i]);
+       else
+           fprintf (stderr, "\t0x%08x\n",  ptr[i]);
+    }
+
+    fprintf (stderr, "\n");
+
+    stream->offset += len * sizeof(uint32_t);
+    return TRUE;
+}
+
+static const char *opcodes[] = {
+    "NOP",
+    "ADD",
+    "MOV",
+    "MUL",
+    "MAD",
+    "DP2ADD",
+    "DP3",
+    "DP4",
+    "FRC",
+    "RCP",
+    "RSQ",
+    "EXP",
+    "LOG",
+    "CMP",
+    "MIN",
+    "MAX",
+    "FLR",
+    "MOD",
+    "TRC",
+    "SGE",
+    "SLT",
+    "TEXLD",
+    "TEXLDP",
+    "TEXLDB",
+    "TEXKILL",
+    "DCL",
+    "0x1a",
+    "0x1b",
+    "0x1c",
+    "0x1d",
+    "0x1e",
+    "0x1f",
+};
+
+static const int args[] = {
+    0,                           /* 0 nop */
+    2,                           /* 1 add */
+    1,                           /* 2 mov */
+    2,                           /* 3 m ul */
+    3,                           /* 4 mad */
+    3,                           /* 5 dp2add */
+    2,                           /* 6 dp3 */
+    2,                           /* 7 dp4 */
+    1,                           /* 8 frc */
+    1,                           /* 9 rcp */
+    1,                           /* a rsq */
+    1,                           /* b exp */
+    1,                           /* c log */
+    3,                           /* d cmp */
+    2,                           /* e min */
+    2,                           /* f max */
+    1,                           /* 10 flr */
+    1,                           /* 11 mod */
+    1,                           /* 12 trc */
+    2,                           /* 13 sge */
+    2,                           /* 14 slt */
+    1,
+    1,
+    1,
+    1,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+};
+
+static const char *regname[] = {
+    "R",
+    "T",
+    "CONST",
+    "S",
+    "OC",
+    "OD",
+    "U",
+    "UNKNOWN",
+};
+
+static void
+print_reg_type_nr(uint32_t type, uint32_t nr)
+{
+    switch (type) {
+    case REG_TYPE_T:
+       switch (nr) {
+       case T_DIFFUSE:
+           fprintf (stderr, "T_DIFFUSE");
+           return;
+       case T_SPECULAR:
+           fprintf (stderr, "T_SPECULAR");
+           return;
+       case T_FOG_W:
+           fprintf (stderr, "T_FOG_W");
+           return;
+       default:
+           fprintf (stderr, "T_TEX%d", nr);
+           return;
+       }
+    case REG_TYPE_OC:
+       if (nr == 0) {
+           fprintf (stderr, "oC");
+           return;
+       }
+       break;
+    case REG_TYPE_OD:
+       if (nr == 0) {
+           fprintf (stderr, "oD");
+           return;
+       }
+       break;
+    default:
+       break;
+    }
+
+    fprintf (stderr, "%s[%d]", regname[type], nr);
+}
+
+#define REG_SWIZZLE_MASK 0x7777
+#define REG_NEGATE_MASK 0x8888
+
+#define REG_SWIZZLE_XYZW ((SRC_X << A2_SRC2_CHANNEL_X_SHIFT) | \
+                         (SRC_Y << A2_SRC2_CHANNEL_Y_SHIFT) |  \
+                         (SRC_Z << A2_SRC2_CHANNEL_Z_SHIFT) |  \
+                         (SRC_W << A2_SRC2_CHANNEL_W_SHIFT))
+
+static void
+print_reg_neg_swizzle(uint32_t reg)
+{
+    int i;
+
+    if ((reg & REG_SWIZZLE_MASK) == REG_SWIZZLE_XYZW &&
+       (reg & REG_NEGATE_MASK) == 0)
+       return;
+
+    fprintf (stderr, ".");
+
+    for (i = 12; i >= 0; i -= 4) {
+       if (reg & (8 << i))
+           fprintf (stderr, "-");
+
+       switch ((reg >> i) & 0x7) {
+       case 0:
+           fprintf (stderr, "x");
+           break;
+       case 1:
+           fprintf (stderr, "y");
+           break;
+       case 2:
+           fprintf (stderr, "z");
+           break;
+       case 3:
+           fprintf (stderr, "w");
+           break;
+       case 4:
+           fprintf (stderr, "0");
+           break;
+       case 5:
+           fprintf (stderr, "1");
+           break;
+       default:
+           fprintf (stderr, "?");
+           break;
+       }
+    }
+}
+
+static void
+print_src_reg(uint32_t dword)
+{
+    uint32_t nr = (dword >> A2_SRC2_NR_SHIFT) & REG_NR_MASK;
+    uint32_t type = (dword >> A2_SRC2_TYPE_SHIFT) & REG_TYPE_MASK;
+    print_reg_type_nr(type, nr);
+    print_reg_neg_swizzle(dword);
+}
+
+static void
+print_dest_reg(uint32_t dword)
+{
+    uint32_t nr = (dword >> A0_DEST_NR_SHIFT) & REG_NR_MASK;
+    uint32_t type = (dword >> A0_DEST_TYPE_SHIFT) & REG_TYPE_MASK;
+    print_reg_type_nr(type, nr);
+    if ((dword & A0_DEST_CHANNEL_ALL) == A0_DEST_CHANNEL_ALL)
+       return;
+    fprintf (stderr, ".");
+    if (dword & A0_DEST_CHANNEL_X)
+       fprintf (stderr, "x");
+    if (dword & A0_DEST_CHANNEL_Y)
+       fprintf (stderr, "y");
+    if (dword & A0_DEST_CHANNEL_Z)
+       fprintf (stderr, "z");
+    if (dword & A0_DEST_CHANNEL_W)
+       fprintf (stderr, "w");
+}
+
+#define GET_SRC0_REG(r0, r1) ((r0<<14)|(r1>>A1_SRC0_CHANNEL_W_SHIFT))
+#define GET_SRC1_REG(r0, r1) ((r0<<8)|(r1>>A2_SRC1_CHANNEL_W_SHIFT))
+#define GET_SRC2_REG(r)      (r)
+
+static void
+print_arith_op(uint32_t opcode, const uint32_t * program)
+{
+    if (opcode != A0_NOP) {
+       print_dest_reg(program[0]);
+       if (program[0] & A0_DEST_SATURATE)
+           fprintf (stderr, " = SATURATE ");
+       else
+           fprintf (stderr, " = ");
+    }
+
+    fprintf (stderr, "%s ", opcodes[opcode]);
+
+    print_src_reg(GET_SRC0_REG(program[0], program[1]));
+    if (args[opcode] == 1) {
+       fprintf (stderr, "\n");
+       return;
+    }
+
+    fprintf (stderr, ", ");
+    print_src_reg(GET_SRC1_REG(program[1], program[2]));
+    if (args[opcode] == 2) {
+       fprintf (stderr, "\n");
+       return;
+    }
+
+    fprintf (stderr, ", ");
+    print_src_reg(GET_SRC2_REG(program[2]));
+    fprintf (stderr, "\n");
+    return;
+}
+
+static void
+print_tex_op(uint32_t opcode, const uint32_t * program)
+{
+    print_dest_reg(program[0] | A0_DEST_CHANNEL_ALL);
+    fprintf (stderr, " = ");
+
+    fprintf (stderr, "%s ", opcodes[opcode]);
+
+    fprintf (stderr, "S[%d],", program[0] & T0_SAMPLER_NR_MASK);
+
+    print_reg_type_nr((program[1] >> T1_ADDRESS_REG_TYPE_SHIFT) &
+                     REG_TYPE_MASK,
+                     (program[1] >> T1_ADDRESS_REG_NR_SHIFT) & REG_NR_MASK);
+    fprintf (stderr, "\n");
+}
+
+static void
+print_dcl_op(uint32_t opcode, const uint32_t * program)
+{
+    fprintf (stderr, "%s ", opcodes[opcode]);
+    print_dest_reg(program[0] | A0_DEST_CHANNEL_ALL);
+    fprintf (stderr, "\n");
+}
+
+static void
+i915_disassemble_program (const uint32_t * program, uint32_t sz)
+{
+    uint32_t size = program[0] & 0x1ff;
+    uint32_t i;
+
+    fprintf (stderr, "\tPROGRAM\n");
+
+    assert(size + 2 == sz);
+
+    program++;
+    for (i = 1; i < sz; i += 3, program += 3) {
+       uint32_t opcode = program[0] & (0x1f << 24);
+
+       fprintf (stderr, "\t\t");
+
+       if ((int) opcode >= A0_NOP && opcode <= A0_SLT)
+           print_arith_op(opcode >> 24, program);
+       else if (opcode >= T0_TEXLD && opcode <= T0_TEXKILL)
+           print_tex_op(opcode >> 24, program);
+       else if (opcode == D0_DCL)
+           print_dcl_op(opcode >> 24, program);
+       else
+           fprintf (stderr, "Unknown opcode 0x%x\n", opcode);
+    }
+
+    fprintf (stderr, "\tEND-PROGRAM\n\n");
+}
+
+static cairo_bool_t
+debug_program (struct debug_stream *stream, const char *name, uint32_t len)
+{
+    uint32_t *ptr = (uint32_t *)(stream->ptr + stream->offset);
+
+    if (len == 0) {
+       fprintf (stderr, "Error - zero length packet (0x%08x)\n", stream->ptr[0]);
+       ASSERT_NOT_REACHED;
+       return FALSE;
+    }
+
+    fprintf (stderr, "%04x:  ", stream->offset);
+    fprintf (stderr, "%s (%d dwords):\n", name, len);
+    i915_disassemble_program (ptr, len);
+
+    stream->offset += len * sizeof(uint32_t);
+    return TRUE;
+}
+
+static cairo_bool_t
+debug_chain (struct debug_stream *stream, const char *name, uint32_t len)
+{
+    uint32_t *ptr = (uint32_t *)(stream->ptr + stream->offset);
+    uint32_t old_offset = stream->offset + len * sizeof(uint32_t);
+    uint32_t i;
+
+    fprintf (stderr, "%s (%d dwords):\n", name, len);
+    for (i = 0; i < len; i++)
+       fprintf (stderr, "\t0x%08x\n",  ptr[i]);
+
+    stream->offset = ptr[1] & ~0x3;
+
+    if (stream->offset < old_offset)
+       fprintf (stderr, "\n... skipping backwards from 0x%x --> 0x%x ...\n\n",
+                old_offset, stream->offset );
+    else
+       fprintf (stderr, "\n... skipping from 0x%x --> 0x%x ...\n\n",
+                old_offset, stream->offset );
+
+    return TRUE;
+}
+
+static cairo_bool_t
+debug_variable_length_prim (struct debug_stream *stream)
+{
+    uint32_t *ptr = (uint32_t *)(stream->ptr + stream->offset);
+    const char *prim = get_prim_name( ptr[0] );
+    uint32_t i, len;
+
+    uint16_t *idx = (uint16_t *)(ptr+1);
+    for (i = 0; idx[i] != 0xffff; i++)
+       ;
+
+    len = 1+(i+2)/2;
+
+    fprintf (stderr, "%04x:  ", stream->offset);
+    fprintf (stderr, "3DPRIM, %s variable length %d indicies (%d dwords):\n", prim, i, len);
+    for (i = 0; i < len; i++)
+       fprintf (stderr, "\t0x%08x\n",  ptr[i]);
+    fprintf (stderr, "\n");
+
+    stream->offset += len * sizeof(uint32_t);
+    return TRUE;
+}
+
+#define BITS(dw, hi, lo, ...)                                  \
+    do {                                                       \
+       unsigned himask = 0xffffffffU >> (31 - (hi));           \
+       fprintf (stderr, "\t\t ");                              \
+       fprintf (stderr, __VA_ARGS__);                          \
+       fprintf (stderr, ": 0x%x\n", ((dw) & himask) >> (lo));  \
+    } while (0)
+
+#define MBZ(dw, hi, lo) do {                           \
+    unsigned x = (dw) >> (lo);                         \
+    unsigned lomask = (1 << (lo)) - 1;                 \
+    unsigned himask;                                   \
+    himask = (1UL << (hi)) - 1;                                \
+    assert ((x & himask & ~lomask) == 0);              \
+} while (0)
+
+#define FLAG(dw, bit, ... )                                    \
+    do {                                                       \
+       if (((dw) >> (bit)) & 1) {                              \
+           fprintf (stderr, "\t\t ");                          \
+           fprintf (stderr, __VA_ARGS__);                      \
+           fprintf (stderr, "\n");                             \
+       }                                                       \
+    } while (0)
+
+static cairo_bool_t
+debug_load_immediate (struct debug_stream *stream,
+                     const char *name,
+                     uint32_t len)
+{
+    uint32_t *ptr = (uint32_t *)(stream->ptr + stream->offset);
+    uint32_t bits = (ptr[0] >> 4) & 0xff;
+    uint32_t j = 0;
+
+    fprintf (stderr, "%04x:  ", stream->offset);
+    fprintf (stderr, "%s (%d dwords, flags: %x):\n", name, len, bits);
+    fprintf (stderr, "\t0x%08x\n",  ptr[j++]);
+
+    if (bits & (1<<0)) {
+       fprintf (stderr, "\t  LIS0: 0x%08x\n", ptr[j]);
+       fprintf (stderr, "\t vb address: 0x%08x\n", (ptr[j] & ~0x3));
+       BITS (ptr[j], 0, 0, "vb invalidate disable");
+       j++;
+    }
+    if (bits & (1<<1)) {
+       fprintf (stderr, "\t  LIS1: 0x%08x\n", ptr[j]);
+       BITS (ptr[j], 29, 24, "vb dword width");
+       BITS (ptr[j], 21, 16, "vb dword pitch");
+       BITS (ptr[j], 15, 0, "vb max index");
+       j++;
+    }
+    if (bits & (1<<2)) {
+       int i;
+       fprintf (stderr, "\t  LIS2: 0x%08x\n", ptr[j]);
+       for (i = 0; i < 8; i++) {
+           unsigned tc = (ptr[j] >> (i * 4)) & 0xf;
+           if (tc != 0xf)
+               BITS (tc, 3, 0, "tex coord %d", i);
+       }
+       j++;
+    }
+    if (bits & (1<<3)) {
+       fprintf (stderr, "\t  LIS3: 0x%08x\n", ptr[j]);
+       j++;
+    }
+    if (bits & (1<<4)) {
+       fprintf (stderr, "\t  LIS4: 0x%08x\n", ptr[j]);
+       BITS (ptr[j], 31, 23, "point width");
+       BITS (ptr[j], 22, 19, "line width");
+       FLAG (ptr[j], 18, "alpha flatshade");
+       FLAG (ptr[j], 17, "fog flatshade");
+       FLAG (ptr[j], 16, "spec flatshade");
+       FLAG (ptr[j], 15, "rgb flatshade");
+       BITS (ptr[j], 14, 13, "cull mode");
+       FLAG (ptr[j], 12, "vfmt: point width");
+       FLAG (ptr[j], 11, "vfmt: specular/fog");
+       FLAG (ptr[j], 10, "vfmt: rgba");
+       FLAG (ptr[j], 9, "vfmt: depth offset");
+       BITS (ptr[j], 8, 6, "vfmt: position (2==xyzw)");
+       FLAG (ptr[j], 5, "force dflt diffuse");
+       FLAG (ptr[j], 4, "force dflt specular");
+       FLAG (ptr[j], 3, "local depth offset enable");
+       FLAG (ptr[j], 2, "vfmt: fp32 fog coord");
+       FLAG (ptr[j], 1, "sprite point");
+       FLAG (ptr[j], 0, "antialiasing");
+       j++;
+    }
+    if (bits & (1<<5)) {
+       fprintf (stderr, "\t  LIS5: 0x%08x\n", ptr[j]);
+       BITS (ptr[j], 31, 28, "rgba write disables");
+       FLAG (ptr[j], 27,     "force dflt point width");
+       FLAG (ptr[j], 26,     "last pixel enable");
+       FLAG (ptr[j], 25,     "global z offset enable");
+       FLAG (ptr[j], 24,     "fog enable");
+       BITS (ptr[j], 23, 16, "stencil ref");
+       BITS (ptr[j], 15, 13, "stencil test");
+       BITS (ptr[j], 12, 10, "stencil fail op");
+       BITS (ptr[j], 9, 7,   "stencil pass z fail op");
+       BITS (ptr[j], 6, 4,   "stencil pass z pass op");
+       FLAG (ptr[j], 3,      "stencil write enable");
+       FLAG (ptr[j], 2,      "stencil test enable");
+       FLAG (ptr[j], 1,      "color dither enable");
+       FLAG (ptr[j], 0,      "logiop enable");
+       j++;
+    }
+    if (bits & (1<<6)) {
+       fprintf (stderr, "\t  LIS6: 0x%08x\n", ptr[j]);
+       FLAG (ptr[j], 31,      "alpha test enable");
+       BITS (ptr[j], 30, 28,  "alpha func");
+       BITS (ptr[j], 27, 20,  "alpha ref");
+       FLAG (ptr[j], 19,      "depth test enable");
+       BITS (ptr[j], 18, 16,  "depth func");
+       FLAG (ptr[j], 15,      "blend enable");
+       BITS (ptr[j], 14, 12,  "blend func");
+       BITS (ptr[j], 11, 8,   "blend src factor");
+       BITS (ptr[j], 7,  4,   "blend dst factor");
+       FLAG (ptr[j], 3,       "depth write enable");
+       FLAG (ptr[j], 2,       "color write enable");
+       BITS (ptr[j], 1,  0,   "provoking vertex");
+       j++;
+    }
+
+    fprintf (stderr, "\n");
+
+    assert(j == len);
+
+    stream->offset += len * sizeof(uint32_t);
+    return TRUE;
+}
+
+static cairo_bool_t
+debug_load_indirect (struct debug_stream *stream,
+                    const char *name,
+                    uint32_t len)
+{
+    uint32_t *ptr = (uint32_t *)(stream->ptr + stream->offset);
+    uint32_t bits = (ptr[0] >> 8) & 0x3f;
+    uint32_t i, j = 0;
+
+    fprintf (stderr, "%04x:  ", stream->offset);
+    fprintf (stderr, "%s (%d dwords):\n", name, len);
+    fprintf (stderr, "\t0x%08x\n",  ptr[j++]);
+
+    for (i = 0; i < 6; i++) {
+       if (bits & (1<<i)) {
+           switch (1<<(8+i)) {
+           case LI0_STATE_STATIC_INDIRECT:
+               fprintf (stderr, "        STATIC: 0x%08x | %x\n", ptr[j]&~3, ptr[j]&3); j++;
+               fprintf (stderr, "                0x%08x\n", ptr[j++]);
+               break;
+           case LI0_STATE_DYNAMIC_INDIRECT:
+               fprintf (stderr, "       DYNAMIC: 0x%08x | %x\n", ptr[j]&~3, ptr[j]&3); j++;
+               break;
+           case LI0_STATE_SAMPLER:
+               fprintf (stderr, "       SAMPLER: 0x%08x | %x\n", ptr[j]&~3, ptr[j]&3); j++;
+               fprintf (stderr, "                0x%08x\n", ptr[j++]);
+               break;
+           case LI0_STATE_MAP:
+               fprintf (stderr, "           MAP: 0x%08x | %x\n", ptr[j]&~3, ptr[j]&3); j++;
+               fprintf (stderr, "                0x%08x\n", ptr[j++]);
+               break;
+           case LI0_STATE_PROGRAM:
+               fprintf (stderr, "       PROGRAM: 0x%08x | %x\n", ptr[j]&~3, ptr[j]&3); j++;
+               fprintf (stderr, "                0x%08x\n", ptr[j++]);
+               break;
+           case LI0_STATE_CONSTANTS:
+               fprintf (stderr, "     CONSTANTS: 0x%08x | %x\n", ptr[j]&~3, ptr[j]&3); j++;
+               fprintf (stderr, "                0x%08x\n", ptr[j++]);
+               break;
+           default:
+               ASSERT_NOT_REACHED;
+               break;
+           }
+       }
+    }
+
+    if (bits == 0) {
+       fprintf (stderr, "\t  DUMMY: 0x%08x\n", ptr[j++]);
+    }
+
+    fprintf (stderr, "\n");
+
+    assert(j == len);
+    stream->offset += len * sizeof(uint32_t);
+    return TRUE;
+}
+
+static void
+BR13 (struct debug_stream *stream,
+      uint32_t val)
+{
+    fprintf (stderr, "\t0x%08x\n",  val);
+    FLAG (val, 30, "clipping enable");
+    BITS (val, 25, 24, "color depth (3==32bpp)");
+    BITS (val, 23, 16, "raster op");
+    BITS (val, 15, 0,  "dest pitch");
+}
+
+static void
+BR2223 (struct debug_stream *stream,
+       uint32_t val22, uint32_t val23)
+{
+    union { uint32_t val; short field[2]; } BR22, BR23;
+
+    BR22.val = val22;
+    BR23.val = val23;
+
+    fprintf (stderr, "\t0x%08x\n",  val22);
+    BITS (val22, 31, 16, "dest y1");
+    BITS (val22, 15, 0,  "dest x1");
+
+    fprintf (stderr, "\t0x%08x\n",  val23);
+    BITS (val23, 31, 16, "dest y2");
+    BITS (val23, 15, 0,  "dest x2");
+
+    /* The blit engine may produce unexpected results when these aren't met */
+    assert(BR22.field[0] < BR23.field[0]);
+    assert(BR22.field[1] < BR23.field[1]);
+}
+
+static void
+BR09 (struct debug_stream *stream,
+      uint32_t val)
+{
+    fprintf (stderr, "\t0x%08x -- dest address\n",  val);
+}
+
+static void
+BR26 (struct debug_stream *stream,
+      uint32_t val)
+{
+    fprintf (stderr, "\t0x%08x\n",  val);
+    BITS (val, 31, 16, "src y1");
+    BITS (val, 15, 0,  "src x1");
+}
+
+static void
+BR11 (struct debug_stream *stream,
+      uint32_t val)
+{
+    fprintf (stderr, "\t0x%08x\n",  val);
+    BITS (val, 15, 0,  "src pitch");
+}
+
+static void
+BR12 (struct debug_stream *stream,
+      uint32_t val)
+{
+    fprintf (stderr, "\t0x%08x -- src address\n",  val);
+}
+
+static void
+BR16 (struct debug_stream *stream,
+      uint32_t val)
+{
+    fprintf (stderr, "\t0x%08x -- color\n",  val);
+}
+
+static cairo_bool_t
+debug_copy_blit (struct debug_stream *stream,
+                const char *name,
+                uint32_t len)
+{
+    uint32_t *ptr = (uint32_t *)(stream->ptr + stream->offset);
+    uint32_t j = 0;
+
+    fprintf (stderr, "%04x:  ", stream->offset);
+    fprintf (stderr, "%s (%d dwords):\n", name, len);
+    fprintf (stderr, "\t0x%08x\n",  ptr[j++]);
+
+    BR13(stream, ptr[j++]);
+    BR2223(stream, ptr[j], ptr[j+1]);
+    j += 2;
+    BR09(stream, ptr[j++]);
+    BR26(stream, ptr[j++]);
+    BR11(stream, ptr[j++]);
+    BR12(stream, ptr[j++]);
+
+    stream->offset += len * sizeof(uint32_t);
+    assert(j == len);
+    return TRUE;
+}
+
+static cairo_bool_t
+debug_color_blit (struct debug_stream *stream,
+                 const char *name,
+                 uint32_t len)
+{
+    uint32_t *ptr = (uint32_t *)(stream->ptr + stream->offset);
+    uint32_t j = 0;
+
+    fprintf (stderr, "%04x:  ", stream->offset);
+    fprintf (stderr, "%s (%d dwords):\n", name, len);
+    fprintf (stderr, "\t0x%08x\n",  ptr[j++]);
+
+    BR13(stream, ptr[j++]);
+    BR2223(stream, ptr[j], ptr[j+1]);
+    j += 2;
+    BR09(stream, ptr[j++]);
+    BR16(stream, ptr[j++]);
+
+    stream->offset += len * sizeof(uint32_t);
+    assert(j == len);
+    return TRUE;
+}
+
+static cairo_bool_t
+debug_modes4 (struct debug_stream *stream,
+             const char *name,
+             uint32_t len)
+{
+    uint32_t *ptr = (uint32_t *)(stream->ptr + stream->offset);
+    uint32_t j = 0;
+
+    fprintf (stderr, "%04x:  ", stream->offset);
+    fprintf (stderr, "%s (%d dwords):\n", name, len);
+    fprintf (stderr, "\t0x%08x\n",  ptr[j]);
+    BITS (ptr[j], 21, 18, "logicop func");
+    FLAG (ptr[j], 17, "stencil test mask modify-enable");
+    FLAG (ptr[j], 16, "stencil write mask modify-enable");
+    BITS (ptr[j], 15, 8, "stencil test mask");
+    BITS (ptr[j], 7, 0,  "stencil write mask");
+    fprintf (stderr, "\n");
+    j++;
+
+    stream->offset += len * sizeof(uint32_t);
+    assert(j == len);
+    return TRUE;
+}
+
+static cairo_bool_t
+debug_map_state (struct debug_stream *stream,
+                const char *name,
+                uint32_t len)
+{
+    uint32_t *ptr = (uint32_t *)(stream->ptr + stream->offset);
+    uint32_t j = 0;
+
+    fprintf (stderr, "%04x:  ", stream->offset);
+    fprintf (stderr, "%s (%d dwords):\n", name, len);
+    fprintf (stderr, "\t0x%08x\n",  ptr[j++]);
+
+    {
+       fprintf (stderr, "\t0x%08x\n",  ptr[j]);
+       BITS (ptr[j], 15, 0,   "map mask");
+       j++;
+    }
+
+    while (j < len) {
+       {
+           fprintf (stderr, "\t  TMn.0: 0x%08x\n", ptr[j]);
+           fprintf (stderr, "\t map address: 0x%08x\n", (ptr[j] & ~0x3));
+           FLAG (ptr[j], 1, "vertical line stride");
+           FLAG (ptr[j], 0, "vertical line stride offset");
+           j++;
+       }
+
+       {
+           fprintf (stderr, "\t  TMn.1: 0x%08x\n", ptr[j]);
+           BITS (ptr[j], 31, 21, "height");
+           BITS (ptr[j], 20, 10, "width");
+           BITS (ptr[j], 9, 7, "surface format");
+           BITS (ptr[j], 6, 3, "texel format");
+           FLAG (ptr[j], 2, "use fence regs");
+           FLAG (ptr[j], 1, "tiled surface");
+           FLAG (ptr[j], 0, "tile walk ymajor");
+           j++;
+       }
+       {
+           fprintf (stderr, "\t  TMn.2: 0x%08x\n", ptr[j]);
+           BITS (ptr[j], 31, 21, "dword pitch");
+           BITS (ptr[j], 20, 15, "cube face enables");
+           BITS (ptr[j], 14, 9, "max lod");
+           FLAG (ptr[j], 8,     "mip layout right");
+           BITS (ptr[j], 7, 0, "depth");
+           j++;
+       }
+    }
+
+    stream->offset += len * sizeof(uint32_t);
+    assert(j == len);
+    return TRUE;
+}
+
+static cairo_bool_t
+debug_sampler_state (struct debug_stream *stream,
+                    const char *name,
+                    uint32_t len)
+{
+    uint32_t *ptr = (uint32_t *)(stream->ptr + stream->offset);
+    uint32_t j = 0;
+
+    fprintf (stderr, "%04x:  ", stream->offset);
+    fprintf (stderr, "%s (%d dwords):\n", name, len);
+    fprintf (stderr, "\t0x%08x\n",  ptr[j++]);
+
+    {
+       fprintf (stderr, "\t0x%08x\n",  ptr[j]);
+       BITS (ptr[j], 15, 0,   "sampler mask");
+       j++;
+    }
+
+    while (j < len) {
+       {
+           fprintf (stderr, "\t  TSn.0: 0x%08x\n", ptr[j]);
+           FLAG (ptr[j], 31, "reverse gamma");
+           FLAG (ptr[j], 30, "planar to packed");
+           FLAG (ptr[j], 29, "yuv->rgb");
+           BITS (ptr[j], 28, 27, "chromakey index");
+           BITS (ptr[j], 26, 22, "base mip level");
+           BITS (ptr[j], 21, 20, "mip mode filter");
+           BITS (ptr[j], 19, 17, "mag mode filter");
+           BITS (ptr[j], 16, 14, "min mode filter");
+           BITS (ptr[j], 13, 5,  "lod bias (s4.4)");
+           FLAG (ptr[j], 4,      "shadow enable");
+           FLAG (ptr[j], 3,      "max-aniso-4");
+           BITS (ptr[j], 2, 0,   "shadow func");
+           j++;
+       }
+
+       {
+           fprintf (stderr, "\t  TSn.1: 0x%08x\n", ptr[j]);
+           BITS (ptr[j], 31, 24, "min lod");
+           MBZ( ptr[j], 23, 18 );
+           FLAG (ptr[j], 17,     "kill pixel enable");
+           FLAG (ptr[j], 16,     "keyed tex filter mode");
+           FLAG (ptr[j], 15,     "chromakey enable");
+           BITS (ptr[j], 14, 12, "tcx wrap mode");
+           BITS (ptr[j], 11, 9,  "tcy wrap mode");
+           BITS (ptr[j], 8,  6,  "tcz wrap mode");
+           FLAG (ptr[j], 5,      "normalized coords");
+           BITS (ptr[j], 4,  1,  "map (surface) index");
+           FLAG (ptr[j], 0,      "EAST deinterlacer enable");
+           j++;
+       }
+       {
+           fprintf (stderr, "\t  TSn.2: 0x%08x  (default color)\n", ptr[j]);
+           j++;
+       }
+    }
+
+    stream->offset += len * sizeof(uint32_t);
+    assert(j == len);
+    return TRUE;
+}
+
+static cairo_bool_t
+debug_dest_vars (struct debug_stream *stream,
+                const char *name,
+                uint32_t len)
+{
+    uint32_t *ptr = (uint32_t *)(stream->ptr + stream->offset);
+    uint32_t j = 0;
+
+    fprintf (stderr, "%04x:  ", stream->offset);
+    fprintf (stderr, "%s (%d dwords):\n", name, len);
+    fprintf (stderr, "\t0x%08x\n",  ptr[j++]);
+
+    {
+       fprintf (stderr, "\t0x%08x\n",  ptr[j]);
+       FLAG (ptr[j], 31,     "early classic ztest");
+       FLAG (ptr[j], 30,     "opengl tex default color");
+       FLAG (ptr[j], 29,     "bypass iz");
+       FLAG (ptr[j], 28,     "lod preclamp");
+       BITS (ptr[j], 27, 26, "dither pattern");
+       FLAG (ptr[j], 25,     "linear gamma blend");
+       FLAG (ptr[j], 24,     "debug dither");
+       BITS (ptr[j], 23, 20, "dstorg x");
+       BITS (ptr[j], 19, 16, "dstorg y");
+       MBZ (ptr[j], 15, 15 );
+       BITS (ptr[j], 14, 12, "422 write select");
+       BITS (ptr[j], 11, 8,  "cbuf format");
+       BITS (ptr[j], 3, 2,   "zbuf format");
+       FLAG (ptr[j], 1,      "vert line stride");
+       FLAG (ptr[j], 1,      "vert line stride offset");
+       j++;
+    }
+
+    stream->offset += len * sizeof(uint32_t);
+    assert(j == len);
+    return TRUE;
+}
+
+static cairo_bool_t debug_buf_info( struct debug_stream *stream,
+                                   const char *name,
+                                   uint32_t len )
+{
+    uint32_t *ptr = (uint32_t *)(stream->ptr + stream->offset);
+    uint32_t j = 0;
+
+    fprintf (stderr, "%04x:  ", stream->offset);
+    fprintf (stderr, "%s (%d dwords):\n", name, len);
+    fprintf (stderr, "\t0x%08x\n",  ptr[j++]);
+
+    {
+       fprintf (stderr, "\t0x%08x\n",  ptr[j]);
+       BITS (ptr[j], 28, 28, "aux buffer id");
+       BITS (ptr[j], 27, 24, "buffer id (7=depth, 3=back)");
+       FLAG (ptr[j], 23,     "use fence regs");
+       FLAG (ptr[j], 22,     "tiled surface");
+       FLAG (ptr[j], 21,     "tile walk ymajor");
+       MBZ (ptr[j], 20, 14);
+       BITS (ptr[j], 13, 2,  "dword pitch");
+       MBZ (ptr[j], 2,  0);
+       j++;
+    }
+
+    fprintf (stderr, "\t0x%08x -- buffer base address\n",  ptr[j++]);
+
+    stream->offset += len * sizeof(uint32_t);
+    assert(j == len);
+    return TRUE;
+}
+
+static cairo_bool_t
+decode_3d_i915 (struct debug_stream *stream)
+{
+    uint32_t *ptr = (uint32_t *)(stream->ptr + stream->offset);
+    uint32_t cmd = *ptr;
+
+    switch ((cmd >> 24) & 0x1f) {
+    case 0x6:
+       return debug (stream, "3DSTATE_ANTI_ALIASING", 1);
+    case 0x7:
+       return debug (stream, "3DSTATE_RASTERIZATION_RULES", 1);
+    case 0x8:
+       return debug (stream, "3DSTATE_BACKFACE_STENCIL_OPS", 1);
+    case 0x9:
+       return debug (stream, "3DSTATE_BACKFACE_STENCIL_MASKS", 1);
+    case 0xb:
+       return debug (stream, "3DSTATE_INDEPENDENT_ALPHA_BLEND", 1);
+    case 0xc:
+       return debug (stream, "3DSTATE_MODES5", 1);
+    case 0xd:
+       return debug_modes4(stream, "3DSTATE_MODES4", 1);
+    case 0x15:
+       return debug (stream, "3DSTATE_FOG_COLOR", 1);
+    case 0x16:
+       return debug (stream, "3DSTATE_COORD_SET_BINDINGS", 1);
+    case 0x1c:
+       /* 3DState16NP */
+       switch((cmd >> 19) & 0x1f) {
+       case 0x10:
+           return debug (stream, "3DSTATE_SCISSOR_ENABLE", 1);
+       case 0x11:
+           return debug (stream, "3DSTATE_DEPTH_SUBRECTANGLE_DISABLE", 1);
+       default:
+           break;
+       }
+       break;
+    case 0x1d:
+       /* 3DStateMW */
+       switch ((cmd >> 16) & 0xff) {
+       case 0x0:
+           return debug_map_state(stream, "3DSTATE_MAP_STATE", (cmd & 0x1f) + 2);
+       case 0x1:
+           return debug_sampler_state(stream, "3DSTATE_SAMPLER_STATE", (cmd & 0x1f) + 2);
+       case 0x4:
+           return debug_load_immediate(stream, "3DSTATE_LOAD_STATE_IMMEDIATE", (cmd & 0xf) + 2);
+       case 0x5:
+           return debug_program(stream, "3DSTATE_PIXEL_SHADER_PROGRAM", (cmd & 0x1ff) + 2);
+       case 0x6:
+           return debug (stream, "3DSTATE_PIXEL_SHADER_CONSTANTS", (cmd & 0xff) + 2);
+       case 0x7:
+           return debug_load_indirect(stream, "3DSTATE_LOAD_INDIRECT", (cmd & 0xff) + 2);
+       case 0x80:
+           return debug (stream, "3DSTATE_DRAWING_RECTANGLE", (cmd & 0xffff) + 2);
+       case 0x81:
+           return debug (stream, "3DSTATE_SCISSOR_RECTANGLE", (cmd & 0xffff) + 2);
+       case 0x83:
+           return debug (stream, "3DSTATE_SPAN_STIPPLE", (cmd & 0xffff) + 2);
+       case 0x85:
+           return debug_dest_vars(stream, "3DSTATE_DEST_BUFFER_VARS", (cmd & 0xffff) + 2);
+       case 0x88:
+           return debug (stream, "3DSTATE_CONSTANT_BLEND_COLOR", (cmd & 0xffff) + 2);
+       case 0x89:
+           return debug (stream, "3DSTATE_FOG_MODE", (cmd & 0xffff) + 2);
+       case 0x8e:
+           return debug_buf_info(stream, "3DSTATE_BUFFER_INFO", (cmd & 0xffff) + 2);
+       case 0x97:
+           return debug (stream, "3DSTATE_DEPTH_OFFSET_SCALE", (cmd & 0xffff) + 2);
+       case 0x98:
+           return debug (stream, "3DSTATE_DEFAULT_Z", (cmd & 0xffff) + 2);
+       case 0x99:
+           return debug (stream, "3DSTATE_DEFAULT_DIFFUSE", (cmd & 0xffff) + 2);
+       case 0x9a:
+           return debug (stream, "3DSTATE_DEFAULT_SPECULAR", (cmd & 0xffff) + 2);
+       case 0x9c:
+           return debug (stream, "3DSTATE_CLEAR_PARAMETERS", (cmd & 0xffff) + 2);
+       default:
+           ASSERT_NOT_REACHED;
+           return 0;
+       }
+       break;
+    case 0x1e:
+       if (cmd & (1 << 23))
+           return debug (stream, "???", (cmd & 0xffff) + 1);
+       else
+           return debug (stream, "", 1);
+       break;
+    case 0x1f:
+       if ((cmd & (1 << 23)) == 0) {
+           return debug_prim (stream, "3DPRIM (inline)", 1, (cmd & 0x1ffff) + 2);
+       } else if (cmd & (1 << 17)) {
+           if ((cmd & 0xffff) == 0)
+               return debug_variable_length_prim (stream);
+           else
+               return debug_prim (stream, "3DPRIM (indexed)", 0, (((cmd & 0xffff) + 1) / 2) + 1);
+       } else
+           return debug_prim (stream, "3DPRIM  (indirect sequential)", 0, 2);
+       break;
+    default:
+       return debug (stream, "", 0);
+    }
+
+    return FALSE;
+}
+
+static cairo_bool_t
+decode_3d_i965 (struct debug_stream *stream)
+{
+    const uint32_t *data = (uint32_t *) (stream->ptr + stream->offset);
+    const uint32_t opcode = (data[0] & 0xffff0000) >> 16;
+    unsigned int idx;
+    const struct {
+       uint32_t opcode;
+       int min_len;
+       int max_len;
+       const char *name;
+    } opcodes_3d[] = {
+       { 0x6000, 3, 3, "URB_FENCE" },
+       { 0x6001, 2, 2, "CS_URB_STATE" },
+       { 0x6002, 2, 2, "CONSTANT_BUFFER" },
+       { 0x6101, 6, 6, "STATE_BASE_ADDRESS" },
+       { 0x6102, 2, 2 , "STATE_SIP" },
+       { 0x6104, 1, 1, "3DSTATE_PIPELINE_SELECT" },
+       { 0x680b, 1, 1, "3DSTATE_VF_STATISTICS" },
+       { 0x6904, 1, 1, "3DSTATE_PIPELINE_SELECT" },
+       { 0x7800, 7, 7, "3DSTATE_PIPELINED_POINTERS" },
+       { 0x7801, 6, 6, "3DSTATE_BINDING_TABLE_POINTERS" },
+       { 0x780b, 1, 1, "3DSTATE_VF_STATISTICS" },
+       { 0x7808, 5, 257, "3DSTATE_VERTEX_BUFFERS" },
+       { 0x7809, 3, 256, "3DSTATE_VERTEX_ELEMENTS" },
+       { 0x780a, 3, 3, "3DSTATE_INDEX_BUFFER" },
+       { 0x7900, 4, 4, "3DSTATE_DRAWING_RECTANGLE" },
+       { 0x7901, 5, 5, "3DSTATE_CONSTANT_COLOR" },
+       { 0x7905, 5, 7, "3DSTATE_DEPTH_BUFFER" },
+       { 0x7906, 2, 2, "3DSTATE_POLY_STIPPLE_OFFSET" },
+       { 0x7907, 33, 33, "3DSTATE_POLY_STIPPLE_PATTERN" },
+       { 0x7908, 3, 3, "3DSTATE_LINE_STIPPLE" },
+       { 0x7909, 2, 2, "3DSTATE_GLOBAL_DEPTH_OFFSET_CLAMP" },
+       { 0x790a, 3, 3, "3DSTATE_AA_LINE_PARAMETERS" },
+       { 0x7b00, 6, 6, "3DPRIMITIVE" },
+    }, *opcode_3d;
+
+    for (idx = 0; idx < ARRAY_LENGTH (opcodes_3d); idx++) {
+       opcode_3d = &opcodes_3d[idx];
+       if (opcode == opcode_3d->opcode) {
+           unsigned int len = 1;
+           if (opcode_3d->max_len != 1)
+               len = (data[0] & 0x000000ff) + 2;
+           return debug (stream, opcode_3d->name, len);
+       }
+    }
+
+    return FALSE;
+}
+
+static cairo_bool_t
+decode_3d_i830 (struct debug_stream *stream)
+{
+    ASSERT_NOT_REACHED;
+    return FALSE;
+}
+
+static cairo_bool_t
+i915_debug_packet (struct debug_stream *stream,
+                  int devid)
+{
+    uint32_t *ptr = (uint32_t *)(stream->ptr + stream->offset);
+    uint32_t cmd = *ptr;
+
+    switch (((cmd >> 29) & 0x7)) {
+    case 0x0:
+       switch ((cmd >> 23) & 0x3f) {
+       case 0x0:
+           return debug (stream, "MI_NOOP", 1);
+       case 0x3:
+           return debug (stream, "MI_WAIT_FOR_EVENT", 1);
+       case 0x4:
+           return debug (stream, "MI_FLUSH", 1);
+       case 0xA:
+           debug (stream, "MI_BATCH_BUFFER_END", 1);
+           return FALSE;
+       case 0x22:
+           return debug (stream, "MI_LOAD_REGISTER_IMM", 3);
+       case 0x31:
+           return debug_chain(stream, "MI_BATCH_BUFFER_START", 2);
+       default:
+           break;
+       }
+       break;
+    case 0x1:
+       break;
+    case 0x2:
+       switch ((cmd >> 22) & 0xff) {
+       case 0x50:
+           return debug_color_blit(stream, "XY_COLOR_BLT", (cmd & 0xff) + 2);
+       case 0x53:
+           return debug_copy_blit(stream, "XY_SRC_COPY_BLT", (cmd & 0xff) + 2);
+       default:
+           return debug (stream, "blit command", (cmd & 0xff) + 2);
+       }
+       break;
+    case 0x3:
+       if (IS_965(devid))
+           return decode_3d_i965 (stream);
+       else if (IS_9XX(devid))
+           return decode_3d_i915 (stream);
+       else
+           return decode_3d_i830 (stream);
+    default:
+       break;
+    }
+
+    fprintf (stderr, "Bogus cmd: %x [%x]\n", (cmd >> 29) & 7, cmd);
+    ASSERT_NOT_REACHED;
+    return 0;
+}
+
+void
+intel_dump_batchbuffer (const void *batch,
+                       uint32_t length,
+                       int devid)
+{
+    struct debug_stream stream;
+    cairo_bool_t done = FALSE;
+
+    fprintf (stderr, "\nBATCH: (%d dwords)\n", length / 4);
+
+    stream.offset = 0;
+    stream.ptr = batch;
+
+    while (! done && stream.offset < length) {
+       if (! i915_debug_packet (&stream, devid))
+           break;
+
+       assert (stream.offset <= length);
+    }
+
+    fprintf (stderr, "END-BATCH\n\n");
+    fflush (stderr);
+}
diff --git a/src/drm/cairo-drm-intel-ioctl-private.h b/src/drm/cairo-drm-intel-ioctl-private.h
new file mode 100644 (file)
index 0000000..29fe88a
--- /dev/null
@@ -0,0 +1,442 @@
+/* Cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Chris Wilson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ */
+
+#ifndef CAIRO_DRM_INTEL_IOCTL_PRIVATE_H
+#define CAIRO_DRM_INTEL_IOCTL_PRIVATE_H
+
+#include "cairo-drm-intel-command-private.h"
+
+#define I915_PARAM_IRQ_ACTIVE            1
+#define I915_PARAM_ALLOW_BATCHBUFFER     2
+#define I915_PARAM_LAST_DISPATCH         3
+#define I915_PARAM_CHIPSET_ID            4
+#define I915_PARAM_HAS_GEM               5
+#define I915_PARAM_NUM_FENCES_AVAIL      6
+#define I915_PARAM_HAS_OVERLAY           7
+#define I915_PARAM_HAS_PAGEFLIPPING     8
+#define I915_PARAM_HAS_EXECBUF2          9
+
+struct intel_getparam {
+       int param;
+       int *value;
+};
+
+
+/** @{
+ * Intel memory domains
+ *
+ * Most of these just align with the various caches in
+ * the system and are used to flush and invalidate as
+ * objects end up cached in different domains.
+ */
+/** CPU cache */
+#define I915_GEM_DOMAIN_CPU            0x00000001
+/** Render cache, used by 2D and 3D drawing */
+#define I915_GEM_DOMAIN_RENDER         0x00000002
+/** Sampler cache, used by texture engine */
+#define I915_GEM_DOMAIN_SAMPLER                0x00000004
+/** Command queue, used to load batch buffers */
+#define I915_GEM_DOMAIN_COMMAND                0x00000008
+/** Instruction cache, used by shader programs */
+#define I915_GEM_DOMAIN_INSTRUCTION    0x00000010
+/** Vertex address cache */
+#define I915_GEM_DOMAIN_VERTEX         0x00000020
+/** GTT domain - aperture and scanout */
+#define I915_GEM_DOMAIN_GTT            0x00000040
+/** @} */
+
+#define I915_TILING_NONE       0
+#define I915_TILING_X          1
+#define I915_TILING_Y          2
+
+#define I915_BIT_6_SWIZZLE_NONE                0
+#define I915_BIT_6_SWIZZLE_9           1
+#define I915_BIT_6_SWIZZLE_9_10                2
+#define I915_BIT_6_SWIZZLE_9_11                3
+#define I915_BIT_6_SWIZZLE_9_10_11     4
+
+#define DRM_I915_GEM_EXECBUFFER        0x14
+#define DRM_I915_GEM_BUSY      0x17
+#define DRM_I915_GEM_THROTTLE  0x18
+#define DRM_I915_GEM_CREATE    0x1b
+#define DRM_I915_GEM_PREAD     0x1c
+#define DRM_I915_GEM_PWRITE    0x1d
+#define DRM_I915_GEM_MMAP      0x1e
+#define DRM_I915_GEM_SET_DOMAIN        0x1f
+#define DRM_I915_GEM_SET_TILING        0x21
+#define DRM_I915_GEM_GET_TILING        0x22
+#define DRM_I915_GEM_GET_APERTURE 0x23
+#define DRM_I915_GEM_MMAP_GTT  0x24
+
+struct drm_i915_gem_create {
+       /**
+        * Requested size for the object.
+        *
+        * The (page-aligned) allocated size for the object will be returned.
+        */
+       uint64_t size;
+       /**
+        * Returned handle for the object.
+        *
+        * Object handles are nonzero.
+        */
+       uint32_t handle;
+       uint32_t pad;
+};
+
+struct drm_i915_gem_pread {
+       /** Handle for the object being read. */
+       uint32_t handle;
+       uint32_t pad;
+       /** Offset into the object to read from */
+       uint64_t offset;
+       /** Length of data to read */
+       uint64_t size;
+       /**
+        * Pointer to write the data into.
+        *
+        * This is a fixed-size type for 32/64 compatibility.
+        */
+       uint64_t data_ptr;
+};
+
+struct drm_i915_gem_pwrite {
+       /** Handle for the object being written to. */
+       uint32_t handle;
+       uint32_t pad;
+       /** Offset into the object to write to */
+       uint64_t offset;
+       /** Length of data to write */
+       uint64_t size;
+       /**
+        * Pointer to read the data from.
+        *
+        * This is a fixed-size type for 32/64 compatibility.
+        */
+       uint64_t data_ptr;
+};
+
+struct drm_i915_gem_mmap {
+       /** Handle for the object being mapped. */
+       uint32_t handle;
+       uint32_t pad;
+       /** Offset in the object to map. */
+       uint64_t offset;
+       /**
+        * Length of data to map.
+        *
+        * The value will be page-aligned.
+        */
+       uint64_t size;
+       /**
+        * Returned pointer the data was mapped at.
+        *
+        * This is a fixed-size type for 32/64 compatibility.
+        */
+       uint64_t addr_ptr;
+};
+
+struct drm_i915_gem_mmap_gtt {
+       /** Handle for the object being mapped. */
+       uint32_t handle;
+       uint32_t pad;
+       /**
+        * Fake offset to use for subsequent mmap call
+        *
+        * This is a fixed-size type for 32/64 compatibility.
+        */
+       uint64_t offset;
+};
+
+struct drm_i915_gem_set_domain {
+       /** Handle for the object */
+       uint32_t handle;
+
+       /** New read domains */
+       uint32_t read_domains;
+
+       /** New write domain */
+       uint32_t write_domain;
+};
+
+struct drm_i915_gem_relocation_entry {
+       /**
+        * Handle of the buffer being pointed to by this relocation entry.
+        *
+        * It's appealing to make this be an index into the mm_validate_entry
+        * list to refer to the buffer, but this allows the driver to create
+        * a relocation list for state buffers and not re-write it per
+        * exec using the buffer.
+        */
+       uint32_t target_handle;
+
+       /**
+        * Value to be added to the offset of the target buffer to make up
+        * the relocation entry.
+        */
+       uint32_t delta;
+
+       /** Offset in the buffer the relocation entry will be written into */
+       uint64_t offset;
+
+       /**
+        * Offset value of the target buffer that the relocation entry was last
+        * written as.
+        *
+        * If the buffer has the same offset as last time, we can skip syncing
+        * and writing the relocation.  This value is written back out by
+        * the execbuffer ioctl when the relocation is written.
+        */
+       uint64_t presumed_offset;
+
+       /**
+        * Target memory domains read by this operation.
+        */
+       uint32_t read_domains;
+
+       /**
+        * Target memory domains written by this operation.
+        *
+        * Note that only one domain may be written by the whole
+        * execbuffer operation, so that where there are conflicts,
+        * the application will get -EINVAL back.
+        */
+       uint32_t write_domain;
+};
+
+struct drm_i915_gem_exec_object {
+       /**
+        * User's handle for a buffer to be bound into the GTT for this
+        * operation.
+        */
+       uint32_t handle;
+
+       /** Number of relocations to be performed on this buffer */
+       uint32_t relocation_count;
+       /**
+        * Pointer to array of struct drm_i915_gem_relocation_entry containing
+        * the relocations to be performed in this buffer.
+        */
+       uint64_t relocs_ptr;
+
+       /** Required alignment in graphics aperture */
+       uint64_t alignment;
+
+       /**
+        * Returned value of the updated offset of the object, for future
+        * presumed_offset writes.
+        */
+       uint64_t offset;
+};
+
+struct drm_i915_gem_execbuffer {
+       /**
+        * List of buffers to be validated with their relocations to be
+        * performend on them.
+        *
+        * This is a pointer to an array of struct drm_i915_gem_validate_entry.
+        *
+        * These buffers must be listed in an order such that all relocations
+        * a buffer is performing refer to buffers that have already appeared
+        * in the validate list.
+        */
+       uint64_t buffers_ptr;
+       uint32_t buffer_count;
+
+       /** Offset in the batchbuffer to start execution from. */
+       uint32_t batch_start_offset;
+       /** Bytes used in batchbuffer from batch_start_offset */
+       uint32_t batch_len;
+       uint32_t DR1;
+       uint32_t DR4;
+       uint32_t num_cliprects;
+       /** This is a struct drm_clip_rect *cliprects */
+       uint64_t cliprects_ptr;
+};
+
+struct drm_i915_gem_busy {
+       /** Handle of the buffer to check for busy */
+       uint32_t handle;
+
+       /** Return busy status (1 if busy, 0 if idle) */
+       uint32_t busy;
+};
+
+struct drm_i915_gem_set_tiling {
+       /** Handle of the buffer to have its tiling state updated */
+       uint32_t handle;
+
+       /**
+        * Tiling mode for the object (I915_TILING_NONE, I915_TILING_X,
+        * I915_TILING_Y).
+        *
+        * This value is to be set on request, and will be updated by the
+        * kernel on successful return with the actual chosen tiling layout.
+        *
+        * The tiling mode may be demoted to I915_TILING_NONE when the system
+        * has bit 6 swizzling that can't be managed correctly by GEM.
+        *
+        * Buffer contents become undefined when changing tiling_mode.
+        */
+       uint32_t tiling_mode;
+
+       /**
+        * Stride in bytes for the object when in I915_TILING_X or
+        * I915_TILING_Y.
+        */
+       uint32_t stride;
+
+       /**
+        * Returned address bit 6 swizzling required for CPU access through
+        * mmap mapping.
+        */
+       uint32_t swizzle_mode;
+};
+
+struct drm_i915_gem_get_tiling {
+       /** Handle of the buffer to get tiling state for. */
+       uint32_t handle;
+
+       /**
+        * Current tiling mode for the object (I915_TILING_NONE, I915_TILING_X,
+        * I915_TILING_Y).
+        */
+       uint32_t tiling_mode;
+
+       /**
+        * Returned address bit 6 swizzling required for CPU access through
+        * mmap mapping.
+        */
+       uint32_t swizzle_mode;
+};
+
+struct drm_i915_gem_get_aperture {
+       /** Total size of the aperture used by i915_gem_execbuffer, in bytes */
+       uint64_t aper_size;
+
+       /**
+        * Available space in the aperture used by i915_gem_execbuffer, in
+        * bytes
+        */
+       uint64_t aper_available_size;
+};
+
+#define DRM_I915_GETPARAM      0x06
+
+#define DRM_IOCTL_I915_GETPARAM         DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GETPARAM, struct intel_getparam)
+#define DRM_IOCTL_I915_GEM_EXECBUFFER  DRM_IOW(DRM_COMMAND_BASE + DRM_I915_GEM_EXECBUFFER, struct drm_i915_gem_execbuffer)
+#define DRM_IOCTL_I915_GEM_BUSY                DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_BUSY, struct drm_i915_gem_busy)
+#define DRM_IOCTL_I915_GEM_THROTTLE    DRM_IO ( DRM_COMMAND_BASE + DRM_I915_GEM_THROTTLE)
+#define DRM_IOCTL_I915_GEM_CREATE      DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_CREATE, struct drm_i915_gem_create)
+#define DRM_IOCTL_I915_GEM_PREAD       DRM_IOW (DRM_COMMAND_BASE + DRM_I915_GEM_PREAD, struct drm_i915_gem_pread)
+#define DRM_IOCTL_I915_GEM_PWRITE      DRM_IOW (DRM_COMMAND_BASE + DRM_I915_GEM_PWRITE, struct drm_i915_gem_pwrite)
+#define DRM_IOCTL_I915_GEM_MMAP                DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_MMAP, struct drm_i915_gem_mmap)
+#define DRM_IOCTL_I915_GEM_MMAP_GTT    DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_MMAP_GTT, struct drm_i915_gem_mmap_gtt)
+#define DRM_IOCTL_I915_GEM_SET_DOMAIN  DRM_IOW (DRM_COMMAND_BASE + DRM_I915_GEM_SET_DOMAIN, struct drm_i915_gem_set_domain)
+#define DRM_IOCTL_I915_GEM_SET_TILING  DRM_IOWR (DRM_COMMAND_BASE + DRM_I915_GEM_SET_TILING, struct drm_i915_gem_set_tiling)
+#define DRM_IOCTL_I915_GEM_GET_TILING  DRM_IOWR (DRM_COMMAND_BASE + DRM_I915_GEM_GET_TILING, struct drm_i915_gem_get_tiling)
+#define DRM_IOCTL_I915_GEM_GET_APERTURE        DRM_IOR  (DRM_COMMAND_BASE + DRM_I915_GEM_GET_APERTURE, struct drm_i915_gem_get_aperture)
+
+#define I915_MADV_WILLNEED     0
+#define I915_MADV_DONTNEED     1
+
+struct drm_i915_gem_madvise {
+       uint32_t handle;
+       uint32_t madv;
+       uint32_t retained;
+};
+#define DRM_I915_GEM_MADVISE   0x26
+#define DRM_IOCTL_I915_GEM_MADVISE     DRM_IOWR (DRM_COMMAND_BASE + DRM_I915_GEM_MADVISE, struct drm_i915_gem_madvise)
+
+
+/* XXX execbuffer2 */
+struct drm_i915_gem_exec_object2 {
+       /**
+        * User's handle for a buffer to be bound into the GTT for this
+        * operation.
+        */
+       uint32_t handle;
+
+       /** Number of relocations to be performed on this buffer */
+       uint32_t relocation_count;
+       /**
+        * Pointer to array of struct drm_i915_gem_relocation_entry containing
+        * the relocations to be performed in this buffer.
+        */
+       uint64_t relocs_ptr;
+
+       /** Required alignment in graphics aperture */
+       uint64_t alignment;
+
+       /**
+        * Returned value of the updated offset of the object, for future
+        * presumed_offset writes.
+        */
+       uint64_t offset;
+
+#define EXEC_OBJECT_NEEDS_FENCE (1<<0)
+       uint64_t flags;
+       uint64_t rsvd1;
+       uint64_t rsvd2;
+};
+
+struct drm_i915_gem_execbuffer2 {
+       /**
+        * List of gem_exec_object2 structs
+        */
+       uint64_t buffers_ptr;
+       uint32_t buffer_count;
+
+       /** Offset in the batchbuffer to start execution from. */
+       uint32_t batch_start_offset;
+       /** Bytes used in batchbuffer from batch_start_offset */
+       uint32_t batch_len;
+       uint32_t DR1;
+       uint32_t DR4;
+       uint32_t num_cliprects;
+       /** This is a struct drm_clip_rect *cliprects */
+       uint64_t cliprects_ptr;
+       uint64_t flags;
+       uint64_t rsvd1;
+       uint64_t rsvd2;
+};
+
+#define I915_GEM_3D_PIPELINE 0x1
+#define I915_GEM_MEDIA_PIPELINE 0x2
+#define DRM_I915_GEM_EXECBUFFER2       0x29
+#define DRM_IOCTL_I915_GEM_EXECBUFFER2 DRM_IOW(DRM_COMMAND_BASE + DRM_I915_GEM_EXECBUFFER2, struct drm_i915_gem_execbuffer2)
+
+struct drm_i915_gem_real_size {
+       uint32_t handle;
+       uint64_t size;
+};
+#define DRM_I915_GEM_REAL_SIZE 0x2a
+#define DRM_IOCTL_I915_GEM_REAL_SIZE   DRM_IOWR (DRM_COMMAND_BASE + DRM_I915_GEM_REAL_SIZE, struct drm_i915_gem_real_size)
+
+#endif /* CAIRO_DRM_INTEL_IOCTL_PRIVATE_H */
diff --git a/src/drm/cairo-drm-intel-private.h b/src/drm/cairo-drm-intel-private.h
new file mode 100644 (file)
index 0000000..7fe3623
--- /dev/null
@@ -0,0 +1,518 @@
+/* Cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Chris Wilson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ */
+
+#ifndef CAIRO_DRM_INTEL_PRIVATE_H
+#define CAIRO_DRM_INTEL_PRIVATE_H
+
+#include "cairoint.h"
+#include "cairo-cache-private.h"
+#include "cairo-compiler-private.h"
+#include "cairo-drm-private.h"
+#include "cairo-freelist-private.h"
+#include "cairo-list-private.h"
+#include "cairo-mutex-private.h"
+#include "cairo-rtree-private.h"
+#include "cairo-types-private.h"
+
+#include "cairo-drm-intel-ioctl-private.h"
+
+#define INTEL_TILING_DEFAULT I915_TILING_Y
+
+#define INTEL_BO_CACHE_BUCKETS 12 /* cache surfaces up to 16 MiB */
+
+#define INTEL_GLYPH_CACHE_WIDTH 1024
+#define INTEL_GLYPH_CACHE_HEIGHT 1024
+#define INTEL_GLYPH_CACHE_MIN_SIZE 1
+#define INTEL_GLYPH_CACHE_MAX_SIZE 128
+
+typedef struct _intel_bo {
+    cairo_drm_bo_t base;
+
+    cairo_list_t link;
+    cairo_list_t cache_list;
+
+    uint32_t offset;
+    uint32_t batch_read_domains;
+    uint32_t batch_write_domain;
+
+    uint32_t opaque0;
+    uint32_t opaque1;
+
+    uint32_t full_size;
+    uint16_t stride;
+    uint16_t _stride;
+    uint32_t tiling :4;
+    uint32_t _tiling :4;
+    uint32_t purgeable :1;
+    uint32_t busy :1;
+    uint32_t cpu :1;
+
+    struct drm_i915_gem_exec_object2 *exec;
+    void *virtual;
+} intel_bo_t;
+
+#define INTEL_BATCH_SIZE (64*1024)
+#define INTEL_VERTEX_BUFFER_SIZE (512*1024)
+#define INTEL_MAX_RELOCS 2048
+
+static inline void
+intel_bo_mark_purgeable (intel_bo_t *bo)
+{
+    if (bo->base.name == 0)
+       bo->purgeable = 1;
+}
+
+typedef struct _intel_vertex_buffer intel_vertex_buffer_t;
+
+typedef void (*intel_vertex_buffer_new_func_t) (intel_vertex_buffer_t *vertex_buffer);
+typedef void (*intel_vertex_buffer_start_rectangles_func_t) (intel_vertex_buffer_t *vertex_buffer,
+                                                            uint32_t floats_per_vertex);
+typedef void (*intel_vertex_buffer_flush_func_t) (intel_vertex_buffer_t *vertex_buffer);
+typedef void (*intel_vertex_buffer_finish_func_t) (intel_vertex_buffer_t *vertex_buffer);
+
+struct _intel_vertex_buffer {
+    uint32_t vbo_batch; /* reloc position in batch, 0 -> not yet allocated */
+    uint32_t vbo_offset;
+    uint32_t vbo_used;
+
+    uint32_t vertex_index;
+    uint32_t vertex_count;
+
+    uint32_t floats_per_vertex;
+    uint32_t rectangle_size;
+
+    intel_bo_t *last_vbo;
+    uint32_t last_vbo_offset;
+    uint32_t last_vbo_space;
+
+    intel_vertex_buffer_new_func_t new;
+    intel_vertex_buffer_start_rectangles_func_t start_rectangles;
+    intel_vertex_buffer_flush_func_t flush;
+    intel_vertex_buffer_finish_func_t finish;
+
+    uint32_t base[INTEL_VERTEX_BUFFER_SIZE / sizeof (uint32_t)];
+};
+
+typedef struct _intel_batch intel_batch_t;
+
+typedef void (*intel_batch_commit_func_t) (intel_batch_t *batch);
+typedef void (*intel_batch_reset_func_t) (intel_batch_t *batch);
+
+struct _intel_batch {
+    size_t gtt_size;
+    size_t gtt_avail_size;
+
+    intel_batch_commit_func_t commit;
+    intel_batch_reset_func_t reset;
+
+    uint16_t exec_count;
+    uint16_t reloc_count;
+    uint16_t used;
+    uint16_t header;
+
+    intel_bo_t *target_bo[INTEL_MAX_RELOCS];
+    struct drm_i915_gem_exec_object2 exec[INTEL_MAX_RELOCS];
+    struct drm_i915_gem_relocation_entry reloc[INTEL_MAX_RELOCS];
+
+    uint32_t base[INTEL_BATCH_SIZE / sizeof (uint32_t)];
+
+    intel_vertex_buffer_t vertex_buffer;
+};
+
+typedef struct _intel_buffer {
+    intel_bo_t *bo;
+    uint32_t offset;
+    cairo_format_t format;
+    uint32_t map0, map1;
+    uint32_t width;
+    uint32_t height;
+    uint32_t stride;
+} intel_buffer_t;
+
+typedef struct _intel_buffer_cache {
+    int ref_count;
+    intel_buffer_t buffer;
+    cairo_rtree_t rtree;
+    cairo_list_t link;
+} intel_buffer_cache_t;
+
+typedef struct _intel_glyph {
+    cairo_rtree_node_t node;
+    intel_buffer_cache_t *cache;
+    void **owner;
+    float texcoord[3];
+    int width, height;
+} intel_glyph_t;
+
+typedef struct _intel_gradient_cache {
+    cairo_pattern_union_t pattern;
+    intel_buffer_t buffer;
+} intel_gradient_cache_t;
+#define GRADIENT_CACHE_SIZE 16
+
+typedef struct _intel_surface {
+    cairo_drm_surface_t drm;
+
+    cairo_cache_entry_t snapshot_cache_entry;
+} intel_surface_t;
+
+typedef void (*intel_reset_context_func_t) (void *device);
+
+typedef struct _intel_device {
+    cairo_drm_device_t base;
+
+    size_t gtt_max_size;
+    size_t gtt_avail_size;
+
+    cairo_freepool_t bo_pool;
+    cairo_list_t bo_in_flight;
+
+    cairo_mutex_t mutex;
+    intel_batch_t batch;
+
+    intel_buffer_cache_t glyph_cache[2];
+    cairo_list_t fonts;
+
+    struct {
+       intel_gradient_cache_t cache[GRADIENT_CACHE_SIZE];
+       unsigned int size;
+    } gradient_cache;
+
+    cairo_cache_t snapshot_cache;
+    size_t snapshot_cache_max_size;
+
+    intel_reset_context_func_t reset_context;
+
+    cairo_status_t (*flush) (struct _intel_device *);
+} intel_device_t;
+
+static inline intel_device_t *
+to_intel_device (cairo_device_t *base)
+{
+    return (intel_device_t *) base;
+}
+
+static inline intel_bo_t *
+to_intel_bo (cairo_drm_bo_t *base)
+{
+    return (intel_bo_t *) base;
+}
+
+static inline intel_bo_t *
+intel_bo_reference (intel_bo_t *bo)
+{
+    return to_intel_bo (cairo_drm_bo_reference (&bo->base));
+}
+
+cairo_private cairo_bool_t
+intel_bo_madvise (intel_device_t *device, intel_bo_t *bo, int madv);
+
+static cairo_always_inline void
+intel_bo_destroy (intel_device_t *device, intel_bo_t *bo)
+{
+    cairo_drm_bo_destroy (&device->base.base, &bo->base);
+}
+
+static inline void
+intel_bo_in_flight_add (intel_device_t *device,
+                       intel_bo_t *bo)
+{
+    if (bo->base.name == 0 && bo->exec != NULL && cairo_list_is_empty (&bo->cache_list))
+       cairo_list_add (&bo->cache_list, &device->bo_in_flight);
+}
+
+cairo_private int
+intel_get (int fd, int param);
+
+cairo_private cairo_bool_t
+intel_info (int fd, uint64_t *gtt_size);
+
+cairo_private cairo_status_t
+intel_device_init (intel_device_t *device, int fd);
+
+cairo_private void
+intel_device_fini (intel_device_t *dev);
+
+cairo_private intel_bo_t *
+intel_bo_create (intel_device_t *dev,
+                uint32_t max_size,
+                uint32_t real_size,
+                cairo_bool_t gpu_target,
+                uint32_t tiling,
+                uint32_t stride);
+
+cairo_private intel_bo_t *
+intel_bo_create_for_name (intel_device_t *dev, uint32_t name);
+
+cairo_private void
+intel_bo_set_tiling (const intel_device_t *dev,
+                    intel_bo_t *bo);
+
+cairo_private cairo_bool_t
+intel_bo_is_inactive (const intel_device_t *device,
+                     intel_bo_t *bo);
+
+cairo_private cairo_bool_t
+intel_bo_wait (const intel_device_t *device, const intel_bo_t *bo);
+
+cairo_private void
+intel_bo_write (const intel_device_t *dev,
+               intel_bo_t *bo,
+               unsigned long offset,
+               unsigned long size,
+               const void *data);
+
+cairo_private void
+intel_bo_read (const intel_device_t *dev,
+              intel_bo_t *bo,
+              unsigned long offset,
+              unsigned long size,
+              void *data);
+
+cairo_private void *
+intel_bo_map (const intel_device_t *dev, intel_bo_t *bo);
+
+cairo_private void
+intel_bo_unmap (intel_bo_t *bo);
+
+cairo_private cairo_status_t
+intel_bo_init (const intel_device_t *dev,
+              intel_bo_t *bo,
+              uint32_t size,
+              uint32_t initial_domain);
+
+cairo_private cairo_status_t
+intel_bo_init_for_name (const intel_device_t *dev,
+                       intel_bo_t *bo,
+                       uint32_t size,
+                       uint32_t name);
+
+cairo_private cairo_surface_t *
+intel_bo_get_image (const intel_device_t *device,
+                   intel_bo_t *bo,
+                   const cairo_drm_surface_t *surface);
+
+cairo_private cairo_status_t
+intel_bo_put_image (intel_device_t *dev,
+                   intel_bo_t *bo,
+                   cairo_image_surface_t *src,
+                   int src_x, int src_y,
+                   int width, int height,
+                   int dst_x, int dst_y);
+
+cairo_private void
+intel_surface_init (intel_surface_t *surface,
+                   const cairo_surface_backend_t *backend,
+                   cairo_drm_device_t *device,
+                   cairo_format_t format,
+                   int width, int height);
+
+cairo_private cairo_status_t
+intel_buffer_cache_init (intel_buffer_cache_t *cache,
+                       intel_device_t *device,
+                       cairo_format_t format,
+                       int width, int height);
+
+cairo_private cairo_status_t
+intel_gradient_render (intel_device_t *device,
+                      const cairo_gradient_pattern_t *pattern,
+                      intel_buffer_t *buffer);
+
+cairo_private cairo_int_status_t
+intel_get_glyph (intel_device_t *device,
+                cairo_scaled_font_t *scaled_font,
+                cairo_scaled_glyph_t *scaled_glyph);
+
+cairo_private void
+intel_scaled_glyph_fini (cairo_scaled_glyph_t *scaled_glyph,
+                        cairo_scaled_font_t  *scaled_font);
+
+cairo_private void
+intel_scaled_font_fini (cairo_scaled_font_t *scaled_font);
+
+cairo_private void
+intel_glyph_cache_unpin (intel_device_t *device);
+
+static inline intel_glyph_t *
+intel_glyph_pin (intel_glyph_t *glyph)
+{
+    cairo_rtree_node_t *node = &glyph->node;
+    if (unlikely (node->pinned == 0))
+       return _cairo_rtree_pin (&glyph->cache->rtree, node);
+    return glyph;
+}
+
+cairo_private cairo_status_t
+intel_snapshot_cache_insert (intel_device_t *device,
+                            intel_surface_t *surface);
+
+cairo_private void
+intel_surface_detach_snapshot (cairo_surface_t *abstract_surface);
+
+cairo_private void
+intel_snapshot_cache_thaw (intel_device_t *device);
+
+cairo_private void
+intel_throttle (intel_device_t *device);
+
+cairo_private cairo_status_t
+intel_surface_acquire_source_image (void *abstract_surface,
+                                   cairo_image_surface_t **image_out,
+                                   void **image_extra);
+
+cairo_private void
+intel_surface_release_source_image (void *abstract_surface,
+                                   cairo_image_surface_t *image,
+                                   void *image_extra);
+cairo_private cairo_surface_t *
+intel_surface_map_to_image (void *abstract_surface);
+
+cairo_private cairo_status_t
+intel_surface_flush (void *abstract_surface);
+
+cairo_private cairo_status_t
+intel_surface_finish (void *abstract_surface);
+
+cairo_private void
+intel_dump_batchbuffer (const void *batch,
+                       uint32_t length,
+                       int devid);
+
+static inline uint32_t cairo_const
+MS3_tiling (uint32_t tiling)
+{
+    switch (tiling) {
+    default:
+    case I915_TILING_NONE: return 0;
+    case I915_TILING_X: return MS3_TILED_SURFACE;
+    case I915_TILING_Y: return MS3_TILED_SURFACE | MS3_TILE_WALK;
+    }
+}
+
+static inline float cairo_const
+texcoord_2d_16 (double x, double y)
+{
+    union {
+       uint32_t ui;
+       float f;
+    } u;
+    u.ui = (_cairo_half_from_float (y) << 16) | _cairo_half_from_float (x);
+    return u.f;
+}
+
+#define PCI_CHIP_I810                  0x7121
+#define PCI_CHIP_I810_DC100            0x7123
+#define PCI_CHIP_I810_E                        0x7125
+#define PCI_CHIP_I815                  0x1132
+
+#define PCI_CHIP_I830_M                        0x3577
+#define PCI_CHIP_845_G                 0x2562
+#define PCI_CHIP_I855_GM               0x3582
+#define PCI_CHIP_I865_G                        0x2572
+
+#define PCI_CHIP_I915_G                        0x2582
+#define PCI_CHIP_E7221_G               0x258A
+#define PCI_CHIP_I915_GM               0x2592
+#define PCI_CHIP_I945_G                        0x2772
+#define PCI_CHIP_I945_GM               0x27A2
+#define PCI_CHIP_I945_GME              0x27AE
+
+#define PCI_CHIP_Q35_G                 0x29B2
+#define PCI_CHIP_G33_G                 0x29C2
+#define PCI_CHIP_Q33_G                 0x29D2
+
+#define PCI_CHIP_IGD_GM                        0xA011
+#define PCI_CHIP_IGD_G                 0xA001
+
+#define IS_IGDGM(devid)        (devid == PCI_CHIP_IGD_GM)
+#define IS_IGDG(devid) (devid == PCI_CHIP_IGD_G)
+#define IS_IGD(devid) (IS_IGDG(devid) || IS_IGDGM(devid))
+
+#define PCI_CHIP_I965_G                        0x29A2
+#define PCI_CHIP_I965_Q                        0x2992
+#define PCI_CHIP_I965_G_1              0x2982
+#define PCI_CHIP_I946_GZ               0x2972
+#define PCI_CHIP_I965_GM                0x2A02
+#define PCI_CHIP_I965_GME               0x2A12
+
+#define PCI_CHIP_GM45_GM                0x2A42
+
+#define PCI_CHIP_IGD_E_G                0x2E02
+#define PCI_CHIP_Q45_G                  0x2E12
+#define PCI_CHIP_G45_G                  0x2E22
+#define PCI_CHIP_G41_G                  0x2E32
+
+#define PCI_CHIP_ILD_G                  0x0042
+#define PCI_CHIP_ILM_G                  0x0046
+
+#define IS_MOBILE(devid)       (devid == PCI_CHIP_I855_GM || \
+                                devid == PCI_CHIP_I915_GM || \
+                                devid == PCI_CHIP_I945_GM || \
+                                devid == PCI_CHIP_I945_GME || \
+                                devid == PCI_CHIP_I965_GM || \
+                                devid == PCI_CHIP_I965_GME || \
+                                devid == PCI_CHIP_GM45_GM || IS_IGD(devid))
+
+#define IS_G45(devid)           (devid == PCI_CHIP_IGD_E_G || \
+                                 devid == PCI_CHIP_Q45_G || \
+                                 devid == PCI_CHIP_G45_G || \
+                                 devid == PCI_CHIP_G41_G)
+#define IS_GM45(devid)          (devid == PCI_CHIP_GM45_GM)
+#define IS_G4X(devid)          (IS_G45(devid) || IS_GM45(devid))
+
+#define IS_ILD(devid)           (devid == PCI_CHIP_ILD_G)
+#define IS_ILM(devid)           (devid == PCI_CHIP_ILM_G)
+#define IS_IRONLAKE(devid)      (IS_ILD(devid) || IS_ILM(devid))
+
+#define IS_915(devid)          (devid == PCI_CHIP_I915_G || \
+                                devid == PCI_CHIP_E7221_G || \
+                                devid == PCI_CHIP_I915_GM)
+
+#define IS_945(devid)          (devid == PCI_CHIP_I945_G || \
+                                devid == PCI_CHIP_I945_GM || \
+                                devid == PCI_CHIP_I945_GME || \
+                                devid == PCI_CHIP_G33_G || \
+                                devid == PCI_CHIP_Q33_G || \
+                                devid == PCI_CHIP_Q35_G || IS_IGD(devid))
+
+#define IS_965(devid)          (devid == PCI_CHIP_I965_G || \
+                                devid == PCI_CHIP_I965_Q || \
+                                devid == PCI_CHIP_I965_G_1 || \
+                                devid == PCI_CHIP_I965_GM || \
+                                devid == PCI_CHIP_I965_GME || \
+                                devid == PCI_CHIP_I946_GZ || \
+                                IS_G4X(devid) || \
+                                IS_IRONLAKE(devid))
+
+#define IS_9XX(devid)          (IS_915(devid) || \
+                                IS_945(devid) || \
+                                IS_965(devid))
+
+
+#endif /* CAIRO_DRM_INTEL_PRIVATE_H */
diff --git a/src/drm/cairo-drm-intel-surface.c b/src/drm/cairo-drm-intel-surface.c
new file mode 100644 (file)
index 0000000..f28c750
--- /dev/null
@@ -0,0 +1,445 @@
+/* Cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Chris Wilson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ */
+
+#include "cairoint.h"
+
+#include "cairo-drm-private.h"
+#include "cairo-drm-intel-private.h"
+
+#include "cairo-default-context-private.h"
+#include "cairo-error-private.h"
+
+/* Basic generic/stub surface for intel chipsets */
+
+#define MAX_SIZE 2048
+
+static cairo_surface_t *
+intel_surface_create_similar (void                     *abstract_surface,
+                             cairo_content_t    content,
+                             int                        width,
+                             int                        height)
+{
+    return cairo_image_surface_create (_cairo_format_from_content (content),
+                                      width, height);
+}
+
+cairo_status_t
+intel_surface_finish (void *abstract_surface)
+{
+    intel_surface_t *surface = abstract_surface;
+
+    intel_bo_in_flight_add (to_intel_device (surface->drm.base.device),
+                           to_intel_bo (surface->drm.bo));
+    return _cairo_drm_surface_finish (&surface->drm);
+}
+
+static void
+surface_finish_and_destroy (cairo_surface_t *surface)
+{
+    cairo_surface_finish (surface);
+    cairo_surface_destroy (surface);
+}
+
+cairo_status_t
+intel_surface_acquire_source_image (void *abstract_surface,
+                                   cairo_image_surface_t **image_out,
+                                   void **image_extra)
+{
+    intel_surface_t *surface = abstract_surface;
+    cairo_surface_t *image;
+    cairo_status_t status;
+    void *ptr;
+
+    if (surface->drm.fallback != NULL) {
+       image = surface->drm.fallback;
+       goto DONE;
+    }
+
+    image = _cairo_surface_has_snapshot (&surface->drm.base,
+                                        &_cairo_image_surface_backend);
+    if (image != NULL)
+       goto DONE;
+
+    if (surface->drm.base.backend->flush != NULL) {
+       status = surface->drm.base.backend->flush (surface);
+       if (unlikely (status))
+           return status;
+    }
+
+    ptr = intel_bo_map (to_intel_device (surface->drm.base.device),
+                       to_intel_bo (surface->drm.bo));
+    if (unlikely (ptr == NULL))
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    image = cairo_image_surface_create_for_data (ptr,
+                                                surface->drm.format,
+                                                surface->drm.width,
+                                                surface->drm.height,
+                                                surface->drm.stride);
+    if (unlikely (image->status))
+       return image->status;
+
+    _cairo_surface_attach_snapshot (&surface->drm.base, image, surface_finish_and_destroy);
+
+DONE:
+    *image_out = (cairo_image_surface_t *) cairo_surface_reference (image);
+    *image_extra = NULL;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+void
+intel_surface_release_source_image (void *abstract_surface,
+                                   cairo_image_surface_t *image,
+                                   void *image_extra)
+{
+    cairo_surface_destroy (&image->base);
+}
+
+cairo_surface_t *
+intel_surface_map_to_image (void *abstract_surface)
+{
+    intel_surface_t *surface = abstract_surface;
+
+    if (surface->drm.fallback == NULL) {
+       cairo_surface_t *image;
+       cairo_status_t status;
+       void *ptr;
+
+       if (surface->drm.base.backend->flush != NULL) {
+           status = surface->drm.base.backend->flush (surface);
+           if (unlikely (status))
+               return _cairo_surface_create_in_error (status);
+       }
+
+       ptr = intel_bo_map (to_intel_device (surface->drm.base.device),
+                           to_intel_bo (surface->drm.bo));
+       if (unlikely (ptr == NULL))
+           return _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY);
+
+       image = cairo_image_surface_create_for_data (ptr,
+                                                    surface->drm.format,
+                                                    surface->drm.width,
+                                                    surface->drm.height,
+                                                    surface->drm.stride);
+       if (unlikely (image->status))
+           return image;
+
+       surface->drm.fallback = image;
+    }
+
+    return surface->drm.fallback;
+}
+
+cairo_status_t
+intel_surface_flush (void *abstract_surface)
+{
+    intel_surface_t *surface = abstract_surface;
+    cairo_status_t status;
+
+    if (surface->drm.fallback == NULL)
+       return CAIRO_STATUS_SUCCESS;
+
+    /* kill any outstanding maps */
+    cairo_surface_finish (surface->drm.fallback);
+
+    status = cairo_surface_status (surface->drm.fallback);
+    cairo_surface_destroy (surface->drm.fallback);
+    surface->drm.fallback = NULL;
+
+    return status;
+}
+
+static cairo_int_status_t
+intel_surface_paint (void *abstract_surface,
+                    cairo_operator_t            op,
+                    const cairo_pattern_t      *source,
+                    cairo_clip_t               *clip)
+{
+    return _cairo_surface_paint (intel_surface_map_to_image (abstract_surface),
+                                op, source, clip);
+}
+
+static cairo_int_status_t
+intel_surface_mask (void                       *abstract_surface,
+                   cairo_operator_t             op,
+                   const cairo_pattern_t       *source,
+                   const cairo_pattern_t       *mask,
+                   cairo_clip_t                *clip)
+{
+    return _cairo_surface_mask (intel_surface_map_to_image (abstract_surface),
+                               op, source, mask, clip);
+}
+
+static cairo_int_status_t
+intel_surface_stroke (void                     *abstract_surface,
+                     cairo_operator_t           op,
+                     const cairo_pattern_t     *source,
+                     cairo_path_fixed_t        *path,
+                     const cairo_stroke_style_t        *stroke_style,
+                     const cairo_matrix_t              *ctm,
+                     const cairo_matrix_t              *ctm_inverse,
+                     double                     tolerance,
+                     cairo_antialias_t          antialias,
+                     cairo_clip_t              *clip)
+{
+    return _cairo_surface_stroke (intel_surface_map_to_image (abstract_surface),
+                                 op, source, path, stroke_style, ctm, ctm_inverse,
+                                 tolerance, antialias, clip);
+}
+
+static cairo_int_status_t
+intel_surface_fill (void                       *abstract_surface,
+                   cairo_operator_t             op,
+                   const cairo_pattern_t       *source,
+                   cairo_path_fixed_t          *path,
+                   cairo_fill_rule_t            fill_rule,
+                   double                       tolerance,
+                   cairo_antialias_t            antialias,
+                   cairo_clip_t                *clip)
+{
+    return _cairo_surface_fill (intel_surface_map_to_image (abstract_surface),
+                               op, source, path, fill_rule,
+                               tolerance, antialias, clip);
+}
+
+static cairo_int_status_t
+intel_surface_glyphs (void                     *abstract_surface,
+                     cairo_operator_t           op,
+                     const cairo_pattern_t     *source,
+                     cairo_glyph_t             *glyphs,
+                     int                        num_glyphs,
+                     cairo_scaled_font_t       *scaled_font,
+                     cairo_clip_t              *clip,
+                     int *num_remaining)
+{
+    *num_remaining = 0;
+    return _cairo_surface_show_text_glyphs (intel_surface_map_to_image (abstract_surface),
+                                           op, source,
+                                           NULL, 0,
+                                           glyphs, num_glyphs,
+                                           NULL, 0, 0,
+                                           scaled_font, clip);
+}
+
+static const cairo_surface_backend_t intel_surface_backend = {
+    CAIRO_SURFACE_TYPE_DRM,
+    _cairo_default_context_create,
+
+    intel_surface_create_similar,
+    intel_surface_finish,
+    intel_surface_acquire_source_image,
+    intel_surface_release_source_image,
+    NULL, NULL, NULL,
+    NULL, /* composite */
+    NULL, /* fill */
+    NULL, /* trapezoids */
+    NULL, /* span */
+    NULL, /* check-span */
+
+    NULL, /* copy_page */
+    NULL, /* show_page */
+    _cairo_drm_surface_get_extents,
+    NULL, /* old-glyphs */
+    _cairo_drm_surface_get_font_options,
+
+    intel_surface_flush,
+    NULL, /* mark dirty */
+    NULL, NULL, /* font/glyph fini */
+
+    intel_surface_paint,
+    intel_surface_mask,
+    intel_surface_stroke,
+    intel_surface_fill,
+    intel_surface_glyphs,
+};
+
+void
+intel_surface_init (intel_surface_t *surface,
+                   const cairo_surface_backend_t *backend,
+                   cairo_drm_device_t *device,
+                   cairo_format_t format,
+                   int width, int height)
+{
+    _cairo_surface_init (&surface->drm.base,
+                        backend,
+                        &device->base,
+                        _cairo_content_from_format (format));
+    _cairo_drm_surface_init (&surface->drm, format, width, height);
+
+    surface->snapshot_cache_entry.hash = 0;
+}
+
+static cairo_surface_t *
+intel_surface_create (cairo_drm_device_t *device,
+                     cairo_format_t format,
+                     int width, int height)
+{
+    intel_surface_t *surface;
+    cairo_status_t status;
+
+    surface = malloc (sizeof (intel_surface_t));
+    if (unlikely (surface == NULL))
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+    intel_surface_init (surface, &intel_surface_backend, device,
+                       format, width, height);
+
+    if (width && height) {
+       /* Vol I, p134: size restrictions for textures */
+       width  = (width  + 3) & -4;
+       height = (height + 1) & -2;
+       surface->drm.stride =
+           cairo_format_stride_for_width (surface->drm.format, width);
+       surface->drm.bo = &intel_bo_create (to_intel_device (&device->base),
+                                           surface->drm.stride * height,
+                                           surface->drm.stride * height,
+                                           TRUE, I915_TILING_NONE, surface->drm.stride)->base;
+       if (surface->drm.bo == NULL) {
+           status = _cairo_drm_surface_finish (&surface->drm);
+           free (surface);
+           return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+       }
+    }
+
+    return &surface->drm.base;
+}
+
+static cairo_surface_t *
+intel_surface_create_for_name (cairo_drm_device_t *device,
+                              unsigned int name,
+                              cairo_format_t format,
+                              int width, int height, int stride)
+{
+    intel_surface_t *surface;
+    cairo_status_t status;
+
+    switch (format) {
+    default:
+    case CAIRO_FORMAT_INVALID:
+    case CAIRO_FORMAT_A1:
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
+    case CAIRO_FORMAT_ARGB32:
+    case CAIRO_FORMAT_RGB16_565:
+    case CAIRO_FORMAT_RGB24:
+    case CAIRO_FORMAT_A8:
+       break;
+    }
+
+    if (stride < cairo_format_stride_for_width (format, width))
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_STRIDE));
+
+    surface = malloc (sizeof (intel_surface_t));
+    if (unlikely (surface == NULL))
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+    intel_surface_init (surface, &intel_surface_backend,
+                       device, format, width, height);
+
+    if (width && height) {
+       surface->drm.stride = stride;
+
+       surface->drm.bo = &intel_bo_create_for_name (to_intel_device (&device->base),
+                                                     name)->base;
+       if (unlikely (surface->drm.bo == NULL)) {
+           status = _cairo_drm_surface_finish (&surface->drm);
+           free (surface);
+           return _cairo_surface_create_in_error (_cairo_error
+                                                  (CAIRO_STATUS_NO_MEMORY));
+       }
+    }
+
+    return &surface->drm.base;
+}
+
+static cairo_status_t
+intel_surface_enable_scan_out (void *abstract_surface)
+{
+    intel_surface_t *surface = abstract_surface;
+
+    if (unlikely (surface->drm.bo == NULL))
+       return _cairo_error (CAIRO_STATUS_INVALID_SIZE);
+
+    to_intel_bo (surface->drm.bo)->tiling = I915_TILING_X;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+intel_device_throttle (cairo_drm_device_t *device)
+{
+    intel_throttle (to_intel_device (&device->base));
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+intel_device_destroy (void *data)
+{
+    intel_device_t *device = data;
+
+    intel_device_fini (device);
+
+    free (data);
+}
+
+cairo_drm_device_t *
+_cairo_drm_intel_device_create (int fd, dev_t dev, int vendor_id, int chip_id)
+{
+    intel_device_t *device;
+    cairo_status_t status;
+
+    if (! intel_info (fd, NULL))
+       return NULL;
+
+    device = malloc (sizeof (intel_device_t));
+    if (unlikely (device == NULL))
+       return (cairo_drm_device_t *) _cairo_device_create_in_error (CAIRO_STATUS_NO_MEMORY);
+
+    status = intel_device_init (device, fd);
+    if (unlikely (status)) {
+       free (device);
+       return (cairo_drm_device_t *) _cairo_device_create_in_error (status);
+    }
+
+    device->base.surface.create = intel_surface_create;
+    device->base.surface.create_for_name = intel_surface_create_for_name;
+    device->base.surface.create_from_cacheable_image = NULL;
+    device->base.surface.flink = _cairo_drm_surface_flink;
+    device->base.surface.enable_scan_out = intel_surface_enable_scan_out;
+
+    device->base.surface.map_to_image = intel_surface_map_to_image;
+
+    device->base.device.flush = NULL;
+    device->base.device.throttle = intel_device_throttle;
+    device->base.device.destroy = intel_device_destroy;
+
+    return _cairo_drm_device_init (&device->base,
+                                  fd, dev,
+                                  vendor_id, chip_id,
+                                  MAX_SIZE);
+}
diff --git a/src/drm/cairo-drm-intel.c b/src/drm/cairo-drm-intel.c
new file mode 100644 (file)
index 0000000..d45155e
--- /dev/null
@@ -0,0 +1,1389 @@
+/* Cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Chris Wilson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ */
+
+#include "cairoint.h"
+
+#include "cairo-drm-private.h"
+#include "cairo-drm-ioctl-private.h"
+#include "cairo-drm-intel-private.h"
+#include "cairo-drm-intel-ioctl-private.h"
+
+#include "cairo-error-private.h"
+#include "cairo-freelist-private.h"
+
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <errno.h>
+
+#define GLYPH_CACHE_WIDTH 1024
+#define GLYPH_CACHE_HEIGHT 1024
+#define GLYPH_CACHE_MIN_SIZE 1
+#define GLYPH_CACHE_MAX_SIZE 128
+
+#define IMAGE_CACHE_WIDTH 1024
+#define IMAGE_CACHE_HEIGHT 1024
+
+int
+intel_get (int fd, int param)
+{
+    struct intel_getparam gp;
+    int value;
+
+    gp.param = param;
+    gp.value = &value;
+    if (ioctl (fd, DRM_IOCTL_I915_GETPARAM, &gp) < 0)
+       return 0;
+
+    VG (VALGRIND_MAKE_MEM_DEFINED (&value, sizeof (value)));
+
+    return value;
+}
+
+cairo_bool_t
+intel_info (int fd, uint64_t *gtt_size)
+{
+    struct drm_i915_gem_get_aperture info;
+
+    if (! intel_get (fd, I915_PARAM_HAS_GEM))
+       return FALSE;
+
+    if (! intel_get (fd, I915_PARAM_HAS_EXECBUF2))
+       return FALSE;
+
+    if (ioctl (fd, DRM_IOCTL_I915_GEM_GET_APERTURE, &info) < 0)
+       return FALSE;
+
+    VG (VALGRIND_MAKE_MEM_DEFINED (&info, sizeof (info)));
+
+    if (gtt_size != NULL)
+       *gtt_size = info.aper_size;
+
+    return TRUE;
+}
+
+void
+intel_bo_write (const intel_device_t *device,
+               intel_bo_t *bo,
+               unsigned long offset,
+               unsigned long size,
+               const void *data)
+{
+    struct drm_i915_gem_pwrite pwrite;
+    int ret;
+
+    assert (bo->tiling == I915_TILING_NONE);
+    assert (size);
+    assert (offset < bo->base.size);
+    assert (size+offset <= bo->base.size);
+
+    intel_bo_set_tiling (device, bo);
+
+    assert (bo->_tiling == I915_TILING_NONE);
+
+    memset (&pwrite, 0, sizeof (pwrite));
+    pwrite.handle = bo->base.handle;
+    pwrite.offset = offset;
+    pwrite.size = size;
+    pwrite.data_ptr = (uint64_t) (uintptr_t) data;
+    do {
+       ret = ioctl (device->base.fd, DRM_IOCTL_I915_GEM_PWRITE, &pwrite);
+    } while (ret == -1 && errno == EINTR);
+    assert (ret == 0);
+
+    bo->busy = FALSE;
+}
+
+void
+intel_bo_read (const intel_device_t *device,
+              intel_bo_t *bo,
+              unsigned long offset,
+              unsigned long size,
+              void *data)
+{
+    struct drm_i915_gem_pread pread;
+    int ret;
+
+    assert (bo->tiling == I915_TILING_NONE);
+    assert (size);
+    assert (offset < bo->base.size);
+    assert (size+offset <= bo->base.size);
+
+    intel_bo_set_tiling (device, bo);
+
+    assert (bo->_tiling == I915_TILING_NONE);
+
+    memset (&pread, 0, sizeof (pread));
+    pread.handle = bo->base.handle;
+    pread.offset = offset;
+    pread.size = size;
+    pread.data_ptr = (uint64_t) (uintptr_t) data;
+    do {
+       ret = ioctl (device->base.fd, DRM_IOCTL_I915_GEM_PREAD, &pread);
+    } while (ret == -1 && errno == EINTR);
+    assert (ret == 0);
+
+    bo->cpu = TRUE;
+    bo->busy = FALSE;
+}
+
+void *
+intel_bo_map (const intel_device_t *device, intel_bo_t *bo)
+{
+    struct drm_i915_gem_set_domain set_domain;
+    uint32_t domain;
+    int ret;
+
+    intel_bo_set_tiling (device, bo);
+
+    if (bo->virtual != NULL)
+       return bo->virtual;
+
+    if (bo->cpu && bo->tiling == I915_TILING_NONE) {
+       struct drm_i915_gem_mmap mmap_arg;
+
+       mmap_arg.handle = bo->base.handle;
+       mmap_arg.offset = 0;
+       mmap_arg.size = bo->base.size;
+       mmap_arg.addr_ptr = 0;
+
+       do {
+           ret = ioctl (device->base.fd, DRM_IOCTL_I915_GEM_MMAP, &mmap_arg);
+       } while (ret == -1 && errno == EINTR);
+       if (unlikely (ret != 0)) {
+           _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+           return NULL;
+       }
+
+       bo->virtual = (void *) (uintptr_t) mmap_arg.addr_ptr;
+       domain = I915_GEM_DOMAIN_CPU;
+    } else {
+       struct drm_i915_gem_mmap_gtt mmap_arg;
+       void *ptr;
+
+       /* Get the fake offset back... */
+       mmap_arg.handle = bo->base.handle;
+       do {
+           ret = ioctl (device->base.fd,
+                        DRM_IOCTL_I915_GEM_MMAP_GTT, &mmap_arg);
+       } while (ret == -1 && errno == EINTR);
+       if (unlikely (ret != 0)) {
+           _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+           return NULL;
+       }
+
+       /* and mmap it */
+       ptr = mmap (0, bo->base.size, PROT_READ | PROT_WRITE,
+                   MAP_SHARED, device->base.fd,
+                   mmap_arg.offset);
+       if (unlikely (ptr == MAP_FAILED)) {
+           _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+           return NULL;
+       }
+
+       bo->virtual = ptr;
+       domain = I915_GEM_DOMAIN_GTT;
+    }
+
+    VG (VALGRIND_MAKE_MEM_DEFINED (bo->virtual, bo->base.size));
+
+    set_domain.handle = bo->base.handle;
+    set_domain.read_domains = domain;
+    set_domain.write_domain = domain;
+
+    do {
+       ret = ioctl (device->base.fd,
+                    DRM_IOCTL_I915_GEM_SET_DOMAIN,
+                    &set_domain);
+    } while (ret == -1 && errno == EINTR);
+
+    if (ret != 0) {
+       intel_bo_unmap (bo);
+       _cairo_error_throw (CAIRO_STATUS_DEVICE_ERROR);
+       return NULL;
+    }
+
+    bo->busy = FALSE;
+    return bo->virtual;
+}
+
+void
+intel_bo_unmap (intel_bo_t *bo)
+{
+    munmap (bo->virtual, bo->base.size);
+    bo->virtual = NULL;
+}
+
+cairo_bool_t
+intel_bo_is_inactive (const intel_device_t *device, intel_bo_t *bo)
+{
+    struct drm_i915_gem_busy busy;
+
+    if (! bo->busy)
+       return TRUE;
+
+    /* Is this buffer busy for our intended usage pattern? */
+    busy.handle = bo->base.handle;
+    busy.busy = 1;
+    ioctl (device->base.fd, DRM_IOCTL_I915_GEM_BUSY, &busy);
+
+    bo->busy = busy.busy;
+    return ! busy.busy;
+}
+
+cairo_bool_t
+intel_bo_wait (const intel_device_t *device, const intel_bo_t *bo)
+{
+    struct drm_i915_gem_set_domain set_domain;
+    int ret;
+
+    set_domain.handle = bo->base.handle;
+    set_domain.read_domains = I915_GEM_DOMAIN_GTT;
+    set_domain.write_domain = 0;
+
+    do {
+       ret = ioctl (device->base.fd, DRM_IOCTL_I915_GEM_SET_DOMAIN, &set_domain);
+    } while (ret == -1 && errno == EINTR);
+
+    return ret == 0;
+}
+
+static inline int
+pot (int v)
+{
+    v--;
+    v |= v >> 1;
+    v |= v >> 2;
+    v |= v >> 4;
+    v |= v >> 8;
+    v |= v >> 16;
+    v++;
+    return v;
+}
+
+cairo_bool_t
+intel_bo_madvise (intel_device_t *device,
+                 intel_bo_t *bo,
+                 int advice)
+{
+    struct drm_i915_gem_madvise madv;
+
+    madv.handle = bo->base.handle;
+    madv.madv = advice;
+    madv.retained = TRUE;
+    ioctl (device->base.fd, DRM_IOCTL_I915_GEM_MADVISE, &madv);
+    return madv.retained;
+}
+
+static void
+intel_bo_set_real_size (intel_device_t *device,
+                       intel_bo_t *bo,
+                       size_t size)
+{
+    struct drm_i915_gem_real_size arg;
+    int ret;
+
+    return;
+
+    if (size == bo->base.size)
+       return;
+
+    arg.handle = bo->base.handle;
+    arg.size = size;
+    do {
+       ret = ioctl (device->base.fd, DRM_IOCTL_I915_GEM_REAL_SIZE, &arg);
+    } while (ret == -1 && errno == EINTR);
+
+    if (ret == 0) {
+       if (size > bo->base.size) {
+           assert (bo->exec == NULL);
+           bo->cpu = TRUE;
+           bo->busy = FALSE;
+       }
+
+       bo->base.size = size;
+    }
+}
+
+intel_bo_t *
+intel_bo_create (intel_device_t *device,
+                uint32_t max_size,
+                uint32_t real_size,
+                cairo_bool_t gpu_target,
+                uint32_t tiling,
+                uint32_t stride)
+{
+    intel_bo_t *bo;
+    uint32_t cache_size;
+    struct drm_i915_gem_create create;
+    int bucket;
+    int ret;
+
+    max_size = (max_size + 4095) & -4096;
+    real_size = (real_size + 4095) & -4096;
+    cache_size = pot (max_size);
+    bucket = ffs (cache_size / 4096) - 1;
+    if (bucket >= INTEL_BO_CACHE_BUCKETS)
+       cache_size = max_size;
+
+    if (gpu_target) {
+       intel_bo_t *first = NULL;
+
+       cairo_list_foreach_entry (bo, intel_bo_t,
+                                 &device->bo_in_flight,
+                                 cache_list)
+       {
+           assert (bo->exec != NULL);
+           if (tiling && bo->_tiling &&
+               (bo->_tiling != tiling || bo->_stride != stride))
+           {
+               continue;
+           }
+
+           if (real_size <= bo->base.size) {
+               if (real_size >= bo->base.size/2) {
+                   cairo_list_del (&bo->cache_list);
+                   bo = intel_bo_reference (bo);
+                   goto DONE;
+               }
+
+               if (first == NULL)
+                   first = bo;
+           }
+       }
+
+       if (first != NULL) {
+           cairo_list_del (&first->cache_list);
+           bo = intel_bo_reference (first);
+           goto DONE;
+       }
+    }
+
+    /* no cached buffer available, allocate fresh */
+    bo = _cairo_freepool_alloc (&device->bo_pool);
+    if (unlikely (bo == NULL)) {
+       _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+       return bo;
+    }
+
+    cairo_list_init (&bo->cache_list);
+
+    bo->base.name = 0;
+
+    bo->offset = 0;
+    bo->virtual = NULL;
+    bo->cpu = TRUE;
+
+    bo->_tiling = I915_TILING_NONE;
+    bo->_stride = 0;
+    bo->purgeable = 0;
+    bo->busy = FALSE;
+
+    bo->opaque0 = 0;
+    bo->opaque1 = 0;
+
+    bo->exec = NULL;
+    bo->batch_read_domains = 0;
+    bo->batch_write_domain = 0;
+    cairo_list_init (&bo->link);
+
+    create.size = cache_size;
+    create.handle = 0;
+    ret = ioctl (device->base.fd, DRM_IOCTL_I915_GEM_CREATE, &create);
+    if (unlikely (ret != 0)) {
+       _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+       _cairo_freepool_free (&device->bo_pool, bo);
+       return NULL;
+    }
+
+    bo->base.handle = create.handle;
+    bo->full_size = bo->base.size = create.size;
+
+    intel_bo_set_real_size (device, bo, real_size);
+    CAIRO_REFERENCE_COUNT_INIT (&bo->base.ref_count, 1);
+DONE:
+    bo->tiling = tiling;
+    bo->stride = stride;
+    return bo;
+}
+
+intel_bo_t *
+intel_bo_create_for_name (intel_device_t *device, uint32_t name)
+{
+    struct drm_i915_gem_get_tiling get_tiling;
+    cairo_status_t status;
+    intel_bo_t *bo;
+    int ret;
+
+    bo = _cairo_freepool_alloc (&device->bo_pool);
+    if (unlikely (bo == NULL)) {
+       _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+       return NULL;
+    }
+
+    status = _cairo_drm_bo_open_for_name (&device->base, &bo->base, name);
+    if (unlikely (status))
+       goto FAIL;
+
+    CAIRO_REFERENCE_COUNT_INIT (&bo->base.ref_count, 1);
+    cairo_list_init (&bo->cache_list);
+
+    bo->full_size = bo->base.size;
+    bo->offset = 0;
+    bo->virtual = NULL;
+    bo->purgeable = 0;
+    bo->busy = TRUE;
+    bo->cpu = FALSE;
+
+    bo->opaque0 = 0;
+    bo->opaque1 = 0;
+
+    bo->exec = NULL;
+    bo->batch_read_domains = 0;
+    bo->batch_write_domain = 0;
+    cairo_list_init (&bo->link);
+
+    memset (&get_tiling, 0, sizeof (get_tiling));
+    get_tiling.handle = bo->base.handle;
+
+    ret = ioctl (device->base.fd, DRM_IOCTL_I915_GEM_GET_TILING, &get_tiling);
+    if (unlikely (ret != 0)) {
+       _cairo_error_throw (CAIRO_STATUS_DEVICE_ERROR);
+       _cairo_drm_bo_close (&device->base, &bo->base);
+       goto FAIL;
+    }
+
+    bo->_tiling = bo->tiling = get_tiling.tiling_mode;
+    // bo->stride = get_tiling.stride; /* XXX not available from get_tiling */
+
+    return bo;
+
+FAIL:
+    _cairo_freepool_free (&device->bo_pool, bo);
+    return NULL;
+}
+
+static void
+intel_bo_release (void *_dev, void *_bo)
+{
+    intel_device_t *device = _dev;
+    intel_bo_t *bo = _bo;
+
+    if (bo->virtual != NULL)
+       intel_bo_unmap (bo);
+
+    assert (bo->exec == NULL);
+    assert (cairo_list_is_empty (&bo->cache_list));
+
+    _cairo_drm_bo_close (&device->base, &bo->base);
+    _cairo_freepool_free (&device->bo_pool, bo);
+}
+
+void
+intel_bo_set_tiling (const intel_device_t *device,
+                    intel_bo_t *bo)
+{
+    struct drm_i915_gem_set_tiling set_tiling;
+    int ret;
+
+    if (bo->tiling == bo->_tiling &&
+       (bo->tiling == I915_TILING_NONE || bo->stride == bo->_stride))
+       return;
+
+    do {
+       set_tiling.handle = bo->base.handle;
+       set_tiling.tiling_mode = bo->tiling;
+       set_tiling.stride = bo->stride;
+
+       ret = ioctl (device->base.fd, DRM_IOCTL_I915_GEM_SET_TILING, &set_tiling);
+    } while (ret == -1 && errno == EINTR);
+
+    assert (ret == 0);
+    bo->_tiling = bo->tiling;
+    bo->_stride = bo->stride;
+}
+
+cairo_surface_t *
+intel_bo_get_image (const intel_device_t *device,
+                   intel_bo_t *bo,
+                   const cairo_drm_surface_t *surface)
+{
+    cairo_image_surface_t *image;
+    uint8_t *dst;
+    int size, row;
+
+    image = (cairo_image_surface_t *)
+       cairo_image_surface_create (surface->format,
+                                   surface->width,
+                                   surface->height);
+    if (unlikely (image->base.status))
+       return &image->base;
+
+    intel_bo_set_tiling (device, bo);
+
+    if (bo->tiling == I915_TILING_NONE && image->stride == surface->stride) {
+       size = surface->stride * surface->height;
+       intel_bo_read (device, bo, 0, size, image->data);
+    } else {
+       const uint8_t *src;
+
+       src = intel_bo_map (device, bo);
+       if (unlikely (src == NULL))
+           return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+       size = surface->width;
+       if (surface->format != CAIRO_FORMAT_A8)
+           size *= 4;
+
+       row = surface->height;
+       dst = image->data;
+       while (row--) {
+           memcpy (dst, src, size);
+           dst += image->stride;
+           src += surface->stride;
+       }
+    }
+
+    return &image->base;
+}
+
+static cairo_status_t
+_intel_bo_put_a1_image (intel_device_t *device,
+                       intel_bo_t *bo,
+                       cairo_image_surface_t *src,
+                       int src_x, int src_y,
+                       int width, int height,
+                       int dst_x, int dst_y)
+{
+    uint8_t buf[CAIRO_STACK_BUFFER_SIZE];
+    uint8_t *a8 = buf;
+    uint8_t *data;
+    int x;
+
+    data = src->data + src_y * src->stride;
+
+    if (bo->tiling == I915_TILING_NONE && width == bo->stride) {
+       uint8_t *p;
+       int size;
+
+       size = bo->stride * height;
+       if (size > (int) sizeof (buf)) {
+           a8 = _cairo_malloc_ab (bo->stride, height);
+           if (a8 == NULL)
+               return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+       }
+
+       p = a8;
+       while (height--) {
+           for (x = 0; x < width; x++) {
+               int i = src_x + x;
+               int byte = i / 8;
+               int bit = i % 8;
+               p[x] = data[byte] & (1 << bit) ? 0xff : 0x00;
+           }
+
+           data += src->stride;
+           p += bo->stride;
+       }
+
+       intel_bo_write (device, bo,
+                       dst_y * bo->stride + dst_x, /* XXX  bo_offset */
+                       size, a8);
+    } else {
+       uint8_t *dst;
+
+       if (width > (int) sizeof (buf)) {
+           a8 = malloc (width);
+           if (a8 == NULL)
+               return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+       }
+
+       dst = intel_bo_map (device, bo);
+       if (dst == NULL) {
+           if (a8 != buf)
+               free (a8);
+           return _cairo_error (CAIRO_STATUS_DEVICE_ERROR);
+       }
+
+       dst += dst_y * bo->stride + dst_x; /* XXX  bo_offset */
+       while (height--) {
+           for (x = 0; x < width; x++) {
+               int i = src_x + x;
+               int byte = i / 8;
+               int bit = i % 8;
+               a8[x] = data[byte] & (1 << bit) ? 0xff : 0x00;
+           }
+
+           memcpy (dst, a8, width);
+           dst  += bo->stride;
+           data += src->stride;
+       }
+    }
+
+    if (a8 != buf)
+       free (a8);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_status_t
+intel_bo_put_image (intel_device_t *device,
+                   intel_bo_t *bo,
+                   cairo_image_surface_t *src,
+                   int src_x, int src_y,
+                   int width, int height,
+                   int dst_x, int dst_y)
+{
+    uint8_t *data;
+    int size;
+    int offset;
+
+    intel_bo_set_tiling (device, bo);
+
+    offset = dst_y * bo->stride;
+    data = src->data + src_y * src->stride;
+    switch (src->format) {
+    case CAIRO_FORMAT_ARGB32:
+    case CAIRO_FORMAT_RGB24:
+       offset += 4 * dst_x;
+       data   += 4 * src_x;
+       size    = 4 * width;
+       break;
+    case CAIRO_FORMAT_RGB16_565:
+       offset += 2 * dst_x;
+       data   += 2 * src_x;
+       size    = 2 * width;
+       break;
+    case CAIRO_FORMAT_A8:
+       offset += dst_x;
+       data   += src_x;
+       size    = width;
+       break;
+    case CAIRO_FORMAT_A1:
+       return _intel_bo_put_a1_image (device, bo, src,
+                                      src_x, src_y,
+                                      width, height,
+                                      dst_x, dst_y);
+    default:
+    case CAIRO_FORMAT_INVALID:
+       return _cairo_error (CAIRO_STATUS_INVALID_FORMAT);
+    }
+
+    if (bo->tiling == I915_TILING_NONE && src->stride == bo->stride) {
+       intel_bo_write (device, bo, offset, bo->stride * height, data);
+    } else {
+       uint8_t *dst;
+
+       dst = intel_bo_map (device, bo);
+       if (unlikely (dst == NULL))
+           return _cairo_error (CAIRO_STATUS_DEVICE_ERROR);
+
+       dst += offset;
+       while (height--) {
+           memcpy (dst, data, size);
+           dst  += bo->stride;
+           data += src->stride;
+       }
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_bool_t
+_intel_snapshot_cache_entry_can_remove (const void *closure)
+{
+    return TRUE;
+}
+
+static void
+_intel_snapshot_cache_entry_destroy (void *closure)
+{
+    intel_surface_t *surface = cairo_container_of (closure,
+                                                  intel_surface_t,
+                                                  snapshot_cache_entry);
+
+    surface->snapshot_cache_entry.hash = 0;
+}
+
+cairo_status_t
+intel_device_init (intel_device_t *device, int fd)
+{
+    struct drm_i915_gem_get_aperture aperture;
+    cairo_status_t status;
+    size_t size;
+    int ret;
+    int n;
+
+    ret = ioctl (fd, DRM_IOCTL_I915_GEM_GET_APERTURE, &aperture);
+    if (ret != 0)
+       return _cairo_error (CAIRO_STATUS_DEVICE_ERROR);
+
+    CAIRO_MUTEX_INIT (device->mutex);
+
+    device->gtt_max_size = aperture.aper_size;
+    device->gtt_avail_size = aperture.aper_available_size;
+    device->gtt_avail_size -= device->gtt_avail_size >> 5;
+
+    size = aperture.aper_size / 8;
+    device->snapshot_cache_max_size = size / 4;
+    status = _cairo_cache_init (&device->snapshot_cache,
+                               NULL,
+                               _intel_snapshot_cache_entry_can_remove,
+                               _intel_snapshot_cache_entry_destroy,
+                               size);
+    if (unlikely (status))
+       return status;
+
+    for (n = 0; n < ARRAY_LENGTH (device->glyph_cache); n++) {
+       device->glyph_cache[n].buffer.bo = NULL;
+       cairo_list_init (&device->glyph_cache[n].rtree.pinned);
+    }
+    cairo_list_init (&device->fonts);
+
+    device->gradient_cache.size = 0;
+
+    device->base.bo.release = intel_bo_release;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_intel_gradient_cache_fini (intel_device_t *device)
+{
+    unsigned int n;
+
+    for (n = 0; n < device->gradient_cache.size; n++) {
+       _cairo_pattern_fini (&device->gradient_cache.cache[n].pattern.base);
+       if (device->gradient_cache.cache[n].buffer.bo != NULL)
+           cairo_drm_bo_destroy (&device->base.base,
+                                 &device->gradient_cache.cache[n].buffer.bo->base);
+    }
+}
+
+static void
+_intel_glyph_cache_fini (intel_device_t *device, intel_buffer_cache_t *cache)
+{
+    if (cache->buffer.bo == NULL)
+       return;
+
+    intel_bo_destroy (device, cache->buffer.bo);
+    _cairo_rtree_fini (&cache->rtree);
+}
+
+void
+intel_device_fini (intel_device_t *device)
+{
+    cairo_scaled_font_t *scaled_font, *next_scaled_font;
+    int n;
+
+    cairo_list_foreach_entry_safe (scaled_font,
+                                  next_scaled_font,
+                                  cairo_scaled_font_t,
+                                  &device->fonts,
+                                  link)
+    {
+       _cairo_scaled_font_revoke_ownership (scaled_font);
+    }
+
+    for (n = 0; n < ARRAY_LENGTH (device->glyph_cache); n++)
+       _intel_glyph_cache_fini (device, &device->glyph_cache[n]);
+
+    _cairo_cache_fini (&device->snapshot_cache);
+
+    _intel_gradient_cache_fini (device);
+    _cairo_freepool_fini (&device->bo_pool);
+
+    _cairo_drm_device_fini (&device->base);
+}
+
+void
+intel_throttle (intel_device_t *device)
+{
+    ioctl (device->base.fd, DRM_IOCTL_I915_GEM_THROTTLE);
+}
+
+void
+intel_glyph_cache_unpin (intel_device_t *device)
+{
+    int n;
+
+    for (n = 0; n < ARRAY_LENGTH (device->glyph_cache); n++)
+       _cairo_rtree_unpin (&device->glyph_cache[n].rtree);
+}
+
+static cairo_status_t
+intel_glyph_cache_add_glyph (intel_device_t *device,
+                            intel_buffer_cache_t *cache,
+                            cairo_scaled_glyph_t  *scaled_glyph)
+{
+    cairo_image_surface_t *glyph_surface = scaled_glyph->surface;
+    intel_glyph_t *glyph;
+    cairo_rtree_node_t *node = NULL;
+    double sf_x, sf_y;
+    cairo_status_t status;
+    uint8_t *dst, *src;
+    int width, height;
+
+    width = glyph_surface->width;
+    if (width < GLYPH_CACHE_MIN_SIZE)
+       width = GLYPH_CACHE_MIN_SIZE;
+    height = glyph_surface->height;
+    if (height < GLYPH_CACHE_MIN_SIZE)
+       height = GLYPH_CACHE_MIN_SIZE;
+
+    /* search for an available slot */
+    status = _cairo_rtree_insert (&cache->rtree, width, height, &node);
+    /* search for an unpinned slot */
+    if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
+       status = _cairo_rtree_evict_random (&cache->rtree, width, height, &node);
+       if (status == CAIRO_STATUS_SUCCESS)
+           status = _cairo_rtree_node_insert (&cache->rtree, node, width, height, &node);
+    }
+    if (unlikely (status))
+       return status;
+
+    /* XXX streaming upload? */
+
+    height = glyph_surface->height;
+    src = glyph_surface->data;
+    dst = cache->buffer.bo->virtual;
+    if (dst == NULL) {
+       dst = intel_bo_map (device, cache->buffer.bo);
+       if (unlikely (dst == NULL))
+           return _cairo_error (CAIRO_STATUS_DEVICE_ERROR);
+    }
+
+    dst += node->y * cache->buffer.stride;
+    switch (glyph_surface->format) {
+    case CAIRO_FORMAT_A1: {
+       uint8_t buf[CAIRO_STACK_BUFFER_SIZE];
+       uint8_t *a8 = buf;
+       int x;
+
+       if (width > (int) sizeof (buf)) {
+           a8 = malloc (width);
+           if (unlikely (a8 == NULL))
+               return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+       }
+
+       dst += node->x;
+       width = glyph_surface->width;
+       while (height--) {
+           for (x = 0; x < width; x++)
+               a8[x] = src[x>>3] & (1 << (x&7)) ? 0xff : 0x00;
+
+           memcpy (dst, a8, width);
+           dst += cache->buffer.stride;
+           src += glyph_surface->stride;
+       }
+
+       if (a8 != buf)
+           free (a8);
+       break;
+    }
+
+    case CAIRO_FORMAT_A8:
+       dst  += node->x;
+       width = glyph_surface->width;
+       while (height--) {
+           memcpy (dst, src, width);
+           dst += cache->buffer.stride;
+           src += glyph_surface->stride;
+       }
+       break;
+
+    case CAIRO_FORMAT_ARGB32:
+       dst  += 4*node->x;
+       width = 4*glyph_surface->width;
+       while (height--) {
+           memcpy (dst, src, width);
+           dst += cache->buffer.stride;
+           src += glyph_surface->stride;
+       }
+       break;
+    default:
+    case CAIRO_FORMAT_RGB16_565:
+    case CAIRO_FORMAT_RGB24:
+    case CAIRO_FORMAT_INVALID:
+       ASSERT_NOT_REACHED;
+       return _cairo_error (CAIRO_STATUS_INVALID_FORMAT);
+    }
+
+    scaled_glyph->surface_private = node;
+
+    glyph= (intel_glyph_t *) node;
+    glyph->node.owner = &scaled_glyph->surface_private;
+    glyph->cache = cache;
+
+    /* compute tex coords: bottom-right, bottom-left, top-left */
+    sf_x = 1. / cache->buffer.width;
+    sf_y = 1. / cache->buffer.height;
+    glyph->texcoord[0] =
+       texcoord_2d_16 (sf_x * (node->x + glyph_surface->width),
+                       sf_y * (node->y + glyph_surface->height));
+    glyph->texcoord[1] =
+       texcoord_2d_16 (sf_x * node->x,
+                       sf_y * (node->y + glyph_surface->height));
+    glyph->texcoord[2] =
+       texcoord_2d_16 (sf_x * node->x,
+                       sf_y * node->y);
+
+    glyph->width  = glyph_surface->width;
+    glyph->height = glyph_surface->height;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+void
+intel_scaled_glyph_fini (cairo_scaled_glyph_t *scaled_glyph,
+                        cairo_scaled_font_t  *scaled_font)
+{
+    intel_glyph_t *glyph;
+
+    glyph = scaled_glyph->surface_private;
+    if (glyph != NULL) {
+       /* XXX thread-safety? Probably ok due to the frozen scaled-font. */
+       glyph->node.owner = NULL;
+       if (! glyph->node.pinned)
+           _cairo_rtree_node_remove (&glyph->cache->rtree, &glyph->node);
+    }
+}
+
+void
+intel_scaled_font_fini (cairo_scaled_font_t *scaled_font)
+{
+    cairo_list_del (&scaled_font->link);
+}
+
+static cairo_status_t
+intel_get_glyph_cache (intel_device_t *device,
+                      cairo_format_t format,
+                      intel_buffer_cache_t **out)
+{
+    intel_buffer_cache_t *cache;
+    cairo_status_t status;
+
+    switch (format) {
+    case CAIRO_FORMAT_ARGB32:
+       cache = &device->glyph_cache[0];
+       format = CAIRO_FORMAT_ARGB32;
+       break;
+    case CAIRO_FORMAT_A8:
+    case CAIRO_FORMAT_A1:
+       cache = &device->glyph_cache[1];
+       format = CAIRO_FORMAT_A8;
+       break;
+    default:
+    case CAIRO_FORMAT_RGB16_565:
+    case CAIRO_FORMAT_RGB24:
+    case CAIRO_FORMAT_INVALID:
+       ASSERT_NOT_REACHED;
+       return _cairo_error (CAIRO_STATUS_INVALID_FORMAT);
+    }
+
+    if (unlikely (cache->buffer.bo == NULL)) {
+       status = intel_buffer_cache_init (cache, device, format,
+                                        INTEL_GLYPH_CACHE_WIDTH,
+                                        INTEL_GLYPH_CACHE_HEIGHT);
+       if (unlikely (status))
+           return status;
+
+       _cairo_rtree_init (&cache->rtree,
+                          INTEL_GLYPH_CACHE_WIDTH,
+                          INTEL_GLYPH_CACHE_HEIGHT,
+                          0, sizeof (intel_glyph_t));
+    }
+
+    *out = cache;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_int_status_t
+intel_get_glyph (intel_device_t *device,
+                cairo_scaled_font_t *scaled_font,
+                cairo_scaled_glyph_t *scaled_glyph)
+{
+    cairo_bool_t own_surface = FALSE;
+    intel_buffer_cache_t *cache;
+    cairo_status_t status;
+
+    if (scaled_glyph->surface == NULL) {
+       status =
+           scaled_font->backend->scaled_glyph_init (scaled_font,
+                                                    scaled_glyph,
+                                                    CAIRO_SCALED_GLYPH_INFO_SURFACE);
+       if (unlikely (status))
+           return status;
+
+       if (unlikely (scaled_glyph->surface == NULL))
+           return CAIRO_INT_STATUS_UNSUPPORTED;
+
+       own_surface = TRUE;
+    }
+
+    if (unlikely (scaled_glyph->surface->width == 0 ||
+                 scaled_glyph->surface->height == 0))
+    {
+       return CAIRO_INT_STATUS_NOTHING_TO_DO;
+    }
+
+    if (unlikely (scaled_glyph->surface->width  > GLYPH_CACHE_MAX_SIZE ||
+                 scaled_glyph->surface->height > GLYPH_CACHE_MAX_SIZE))
+    {
+       return CAIRO_INT_STATUS_UNSUPPORTED;
+    }
+
+    status = intel_get_glyph_cache (device,
+                                   scaled_glyph->surface->format,
+                                   &cache);
+    if (unlikely (status))
+       return status;
+
+    status = intel_glyph_cache_add_glyph (device, cache, scaled_glyph);
+    if (unlikely (_cairo_status_is_error (status)))
+       return status;
+
+    if (unlikely (status == CAIRO_INT_STATUS_UNSUPPORTED)) {
+       /* no room, replace entire cache */
+
+       assert (cache->buffer.bo->exec != NULL);
+
+       _cairo_rtree_reset (&cache->rtree);
+       intel_bo_destroy (device, cache->buffer.bo);
+       cache->buffer.bo = NULL;
+
+       status = intel_buffer_cache_init (cache, device,
+                                         scaled_glyph->surface->format,
+                                         GLYPH_CACHE_WIDTH,
+                                         GLYPH_CACHE_HEIGHT);
+       if (unlikely (status))
+           return status;
+
+       status = intel_glyph_cache_add_glyph (device, cache, scaled_glyph);
+       if (unlikely (status))
+           return status;
+    }
+
+    if (own_surface) {
+       /* and release the copy of the image from system memory */
+       cairo_surface_destroy (&scaled_glyph->surface->base);
+       scaled_glyph->surface = NULL;
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_status_t
+intel_buffer_cache_init (intel_buffer_cache_t *cache,
+                       intel_device_t *device,
+                       cairo_format_t format,
+                       int width, int height)
+{
+    const uint32_t tiling = I915_TILING_Y;
+    uint32_t stride, size;
+
+    assert ((width & 3) == 0);
+    assert ((height & 1) == 0);
+    cache->buffer.format = format;
+    cache->buffer.width = width;
+    cache->buffer.height = height;
+
+    switch (format) {
+    default:
+    case CAIRO_FORMAT_A1:
+    case CAIRO_FORMAT_RGB16_565:
+    case CAIRO_FORMAT_RGB24:
+    case CAIRO_FORMAT_INVALID:
+       ASSERT_NOT_REACHED;
+       return _cairo_error (CAIRO_STATUS_INVALID_FORMAT);
+    case CAIRO_FORMAT_ARGB32:
+       cache->buffer.map0 = MAPSURF_32BIT | MT_32BIT_ARGB8888;
+       stride = width * 4;
+       break;
+    case CAIRO_FORMAT_A8:
+       cache->buffer.map0 = MAPSURF_8BIT | MT_8BIT_I8;
+       stride = width;
+       break;
+    }
+
+    size = height * stride;
+    cache->buffer.bo = intel_bo_create (device,
+                                       size, size,
+                                       FALSE, tiling, stride);
+    if (unlikely (cache->buffer.bo == NULL))
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    cache->buffer.stride = stride;
+
+    cache->buffer.offset = 0;
+    cache->buffer.map0 |= MS3_tiling (tiling);
+    cache->buffer.map0 |= ((height - 1) << MS3_HEIGHT_SHIFT) |
+                         ((width - 1)  << MS3_WIDTH_SHIFT);
+    cache->buffer.map1 = ((stride / 4) - 1) << MS4_PITCH_SHIFT;
+
+    cache->ref_count = 0;
+    cairo_list_init (&cache->link);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_status_t
+intel_snapshot_cache_insert (intel_device_t *device,
+                            intel_surface_t *surface)
+{
+    cairo_status_t status;
+
+    surface->snapshot_cache_entry.size = surface->drm.bo->size;
+    if (surface->snapshot_cache_entry.size >
+       device->snapshot_cache_max_size)
+    {
+       return CAIRO_STATUS_SUCCESS;
+    }
+
+    if (device->snapshot_cache.freeze_count == 0)
+       _cairo_cache_freeze (&device->snapshot_cache);
+
+    surface->snapshot_cache_entry.hash = (unsigned long) surface;
+    status = _cairo_cache_insert (&device->snapshot_cache,
+                                 &surface->snapshot_cache_entry);
+    if (unlikely (status)) {
+       surface->snapshot_cache_entry.hash = 0;
+       return status;
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+void
+intel_surface_detach_snapshot (cairo_surface_t *abstract_surface)
+{
+    intel_surface_t *surface = (intel_surface_t *) abstract_surface;
+
+    if (surface->snapshot_cache_entry.hash) {
+       intel_device_t *device;
+
+       device = (intel_device_t *) surface->drm.base.device;
+       _cairo_cache_remove (&device->snapshot_cache,
+                            &surface->snapshot_cache_entry);
+       assert (surface->snapshot_cache_entry.hash == 0);
+    }
+}
+
+void
+intel_snapshot_cache_thaw (intel_device_t *device)
+{
+    if (device->snapshot_cache.freeze_count)
+       _cairo_cache_thaw (&device->snapshot_cache);
+}
+
+static cairo_bool_t
+_gradient_color_stops_equal (const cairo_gradient_pattern_t *a,
+                            const cairo_gradient_pattern_t *b)
+{
+    unsigned int n;
+
+    if (a->n_stops != b->n_stops)
+       return FALSE;
+
+    for (n = 0; n < a->n_stops; n++) {
+       if (_cairo_fixed_from_double (a->stops[n].offset) !=
+           _cairo_fixed_from_double (b->stops[n].offset))
+       {
+           return FALSE;
+       }
+
+       if (! _cairo_color_stop_equal (&a->stops[n].color, &b->stops[n].color))
+           return FALSE;
+    }
+
+    return TRUE;
+}
+
+static uint32_t
+hars_petruska_f54_1_random (void)
+{
+#define rol(x,k) ((x << k) | (x >> (32-k)))
+    static uint32_t x;
+    return x = (x ^ rol (x, 5) ^ rol (x, 24)) + 0x37798849;
+#undef rol
+}
+
+static int
+intel_gradient_sample_width (const cairo_gradient_pattern_t *gradient)
+{
+    unsigned int n;
+    int width;
+
+    width = 8;
+    for (n = 1; n < gradient->n_stops; n++) {
+       double dx = gradient->stops[n].offset - gradient->stops[n-1].offset;
+       double delta, max;
+       int ramp;
+
+       if (dx == 0)
+           continue;
+
+       max = gradient->stops[n].color.red -
+             gradient->stops[n-1].color.red;
+
+       delta = gradient->stops[n].color.green -
+               gradient->stops[n-1].color.green;
+       if (delta > max)
+           max = delta;
+
+       delta = gradient->stops[n].color.blue -
+               gradient->stops[n-1].color.blue;
+       if (delta > max)
+           max = delta;
+
+       delta = gradient->stops[n].color.alpha -
+               gradient->stops[n-1].color.alpha;
+       if (delta > max)
+           max = delta;
+
+       ramp = 128 * max / dx;
+       if (ramp > width)
+           width = ramp;
+    }
+
+    width = (width + 7) & -8;
+    return MIN (width, 1024);
+}
+
+cairo_status_t
+intel_gradient_render (intel_device_t *device,
+                      const cairo_gradient_pattern_t *pattern,
+                      intel_buffer_t *buffer)
+{
+    pixman_image_t *gradient, *image;
+    pixman_gradient_stop_t pixman_stops_stack[32];
+    pixman_gradient_stop_t *pixman_stops;
+    pixman_point_fixed_t p1, p2;
+    int width;
+    unsigned int i;
+    cairo_status_t status;
+
+    for (i = 0; i < device->gradient_cache.size; i++) {
+       if (_gradient_color_stops_equal (pattern,
+                                        &device->gradient_cache.cache[i].pattern.gradient.base)) {
+           *buffer = device->gradient_cache.cache[i].buffer;
+           return CAIRO_STATUS_SUCCESS;
+       }
+    }
+
+    pixman_stops = pixman_stops_stack;
+    if (unlikely (pattern->n_stops > ARRAY_LENGTH (pixman_stops_stack))) {
+       pixman_stops = _cairo_malloc_ab (pattern->n_stops,
+                                        sizeof (pixman_gradient_stop_t));
+       if (unlikely (pixman_stops == NULL))
+           return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+    }
+
+    for (i = 0; i < pattern->n_stops; i++) {
+       pixman_stops[i].x = _cairo_fixed_16_16_from_double (pattern->stops[i].offset);
+       pixman_stops[i].color.red   = pattern->stops[i].color.red_short;
+       pixman_stops[i].color.green = pattern->stops[i].color.green_short;
+       pixman_stops[i].color.blue  = pattern->stops[i].color.blue_short;
+       pixman_stops[i].color.alpha = pattern->stops[i].color.alpha_short;
+    }
+
+    width = intel_gradient_sample_width (pattern);
+
+    p1.x = 0;
+    p1.y = 0;
+    p2.x = width << 16;
+    p2.y = 0;
+
+    gradient = pixman_image_create_linear_gradient (&p1, &p2,
+                                                   pixman_stops,
+                                                   pattern->n_stops);
+    if (pixman_stops != pixman_stops_stack)
+       free (pixman_stops);
+
+    if (unlikely (gradient == NULL))
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    pixman_image_set_filter (gradient, PIXMAN_FILTER_BILINEAR, NULL, 0);
+    pixman_image_set_repeat (gradient, PIXMAN_REPEAT_PAD);
+
+    image = pixman_image_create_bits (PIXMAN_a8r8g8b8, width, 1, NULL, 0);
+    if (unlikely (image == NULL)) {
+       pixman_image_unref (gradient);
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+    }
+
+    pixman_image_composite32 (PIXMAN_OP_SRC,
+                              gradient, NULL, image,
+                              0, 0,
+                              0, 0,
+                              0, 0,
+                              width, 1);
+
+    pixman_image_unref (gradient);
+
+    buffer->bo = intel_bo_create (device,
+                                 4*width, 4*width,
+                                 FALSE, I915_TILING_NONE, 4*width);
+    if (unlikely (buffer->bo == NULL)) {
+       pixman_image_unref (image);
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+    }
+
+    intel_bo_write (device, buffer->bo, 0, 4*width, pixman_image_get_data (image));
+    pixman_image_unref (image);
+
+    buffer->offset = 0;
+    buffer->width  = width;
+    buffer->height = 1;
+    buffer->stride = 4*width;
+    buffer->format = CAIRO_FORMAT_ARGB32;
+    buffer->map0 = MAPSURF_32BIT | MT_32BIT_ARGB8888;
+    buffer->map0 |= ((width - 1) << MS3_WIDTH_SHIFT);
+    buffer->map1 = (width - 1) << MS4_PITCH_SHIFT;
+
+    if (device->gradient_cache.size < GRADIENT_CACHE_SIZE) {
+       i = device->gradient_cache.size++;
+    } else {
+       i = hars_petruska_f54_1_random () % GRADIENT_CACHE_SIZE;
+       _cairo_pattern_fini (&device->gradient_cache.cache[i].pattern.base);
+       intel_bo_destroy (device, device->gradient_cache.cache[i].buffer.bo);
+    }
+
+    status = _cairo_pattern_init_copy (&device->gradient_cache.cache[i].pattern.base,
+                                      &pattern->base);
+    if (unlikely (status)) {
+       intel_bo_destroy (device, buffer->bo);
+       /* Ensure the cache is correctly initialised for i965_device_destroy */
+       _cairo_pattern_init_solid (&device->gradient_cache.cache[i].pattern.solid,
+                                  CAIRO_COLOR_TRANSPARENT);
+       return status;
+    }
+
+    device->gradient_cache.cache[i].buffer = *buffer;
+    return CAIRO_STATUS_SUCCESS;
+}
diff --git a/src/drm/cairo-drm-ioctl-private.h b/src/drm/cairo-drm-ioctl-private.h
new file mode 100644 (file)
index 0000000..4294de2
--- /dev/null
@@ -0,0 +1,12 @@
+#ifndef CAIRO_DRM_IOCTL_PRIVATE_H
+#define CAIRO_DRM_IOCTL_PRIVATE_H
+
+#define DRM_IOCTL_BASE                 'd'
+#define DRM_IO(nr)                     _IO(DRM_IOCTL_BASE,nr)
+#define DRM_IOR(nr,type)               _IOR(DRM_IOCTL_BASE,nr,type)
+#define DRM_IOW(nr,type)               _IOW(DRM_IOCTL_BASE,nr,type)
+#define DRM_IOWR(nr,type)              _IOWR(DRM_IOCTL_BASE,nr,type)
+
+#define DRM_COMMAND_BASE                0x40
+
+#endif /* CAIRO_DRM_IOCTL_PRIVATE_H */
diff --git a/src/drm/cairo-drm-private.h b/src/drm/cairo-drm-private.h
new file mode 100644 (file)
index 0000000..2db7f38
--- /dev/null
@@ -0,0 +1,238 @@
+/* Cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Chris Wilson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Chris Wilson.
+ *
+ * Contributors(s):
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#ifndef CAIRO_DRM_PRIVATE_H
+#define CAIRO_DRM_PRIVATE_H
+
+#include "cairo-drm.h"
+
+#include "cairo-device-private.h"
+#include "cairo-reference-count-private.h"
+#include "cairo-surface-private.h"
+
+#include <sys/types.h> /* dev_t */
+
+typedef struct _cairo_drm_device cairo_drm_device_t;
+
+typedef cairo_drm_device_t *
+(*cairo_drm_device_create_func_t) (int fd,
+                                  dev_t dev,
+                                  int vendor_id,
+                                  int chip_id);
+
+typedef cairo_int_status_t
+(*cairo_drm_device_flush_func_t) (cairo_drm_device_t *device);
+
+typedef cairo_int_status_t
+(*cairo_drm_device_throttle_func_t) (cairo_drm_device_t *device);
+
+typedef void
+(*cairo_drm_device_destroy_func_t) (void *data);
+
+typedef cairo_surface_t *
+(*cairo_drm_surface_create_func_t) (cairo_drm_device_t *device,
+                                   cairo_format_t format,
+                                   int width, int height);
+
+typedef cairo_surface_t *
+(*cairo_drm_surface_create_for_name_func_t) (cairo_drm_device_t *device,
+                                            unsigned int name,
+                                            cairo_format_t format,
+                                            int width, int height, int stride);
+
+typedef cairo_surface_t *
+(*cairo_drm_surface_create_from_cacheable_image_func_t)
+    (cairo_drm_device_t *device, cairo_surface_t *image);
+
+typedef cairo_int_status_t
+(*cairo_drm_surface_flink_func_t) (void *surface);
+
+typedef cairo_status_t
+(*cairo_drm_surface_enable_scan_out_func_t) (void *surface);
+
+typedef cairo_surface_t *
+(*cairo_drm_surface_map_to_image_func_t) (void *surface);
+
+typedef struct _cairo_drm_bo_backend {
+    void (*release) (void *device, void *bo);
+} cairo_drm_bo_backend_t;
+
+typedef struct _cairo_drm_device_backend {
+    cairo_drm_device_flush_func_t flush;
+    cairo_drm_device_throttle_func_t throttle;
+    cairo_drm_device_destroy_func_t destroy;
+} cairo_drm_device_backend_t;
+
+typedef struct _cairo_drm_surface_backend {
+    cairo_drm_surface_create_func_t create;
+    cairo_drm_surface_create_for_name_func_t create_for_name;
+    cairo_drm_surface_create_from_cacheable_image_func_t create_from_cacheable_image;
+    cairo_drm_surface_flink_func_t flink;
+    cairo_drm_surface_enable_scan_out_func_t enable_scan_out;
+    cairo_drm_surface_map_to_image_func_t map_to_image;
+} cairo_drm_surface_backend_t;
+
+typedef struct _cairo_drm_bo {
+    cairo_reference_count_t ref_count;
+    uint32_t name;
+    uint32_t handle;
+    uint32_t size;
+} cairo_drm_bo_t;
+
+struct _cairo_drm_device {
+    cairo_device_t base;
+
+    int vendor_id;
+    int chip_id;
+    dev_t id;
+    int fd;
+
+    int max_surface_size;
+
+    cairo_drm_bo_backend_t bo;
+    cairo_drm_surface_backend_t surface;
+    cairo_drm_device_backend_t device;
+
+    cairo_drm_device_t *next, *prev;
+};
+
+typedef struct _cairo_drm_surface {
+    cairo_surface_t base;
+
+    cairo_drm_bo_t *bo;
+
+    cairo_format_t format;
+    int width, height, stride;
+
+    cairo_surface_t *fallback;
+    uint32_t map_count;
+} cairo_drm_surface_t;
+
+static inline cairo_drm_bo_t *
+cairo_drm_bo_reference (cairo_drm_bo_t *bo)
+{
+    _cairo_reference_count_inc (&bo->ref_count);
+    return bo;
+}
+
+static cairo_always_inline void
+cairo_drm_bo_destroy (cairo_device_t *abstract_device,
+                     cairo_drm_bo_t *bo)
+{
+    if (_cairo_reference_count_dec_and_test (&bo->ref_count)) {
+       cairo_drm_device_t *device = (cairo_drm_device_t *) abstract_device;
+       device->bo.release (device, bo);
+    }
+}
+
+cairo_private cairo_status_t
+_cairo_drm_bo_open_for_name (const cairo_drm_device_t *dev,
+                            cairo_drm_bo_t *bo,
+                            uint32_t name);
+
+cairo_private cairo_status_t
+_cairo_drm_bo_flink (const cairo_drm_device_t *dev,
+                    cairo_drm_bo_t *bo);
+
+cairo_private void
+_cairo_drm_bo_close (const cairo_drm_device_t *dev,
+                    cairo_drm_bo_t *bo);
+
+cairo_private void
+_cairo_drm_surface_init (cairo_drm_surface_t *surface,
+                        cairo_format_t format,
+                        int width, int height);
+
+cairo_private cairo_status_t
+_cairo_drm_surface_finish (cairo_drm_surface_t *surface);
+
+cairo_private void
+_cairo_drm_surface_get_font_options (void                  *abstract_surface,
+                                    cairo_font_options_t  *options);
+
+cairo_private cairo_bool_t
+_cairo_drm_surface_get_extents (void *abstract_surface,
+                               cairo_rectangle_int_t *rectangle);
+
+cairo_private cairo_int_status_t
+_cairo_drm_surface_flink (void *abstract_surface);
+
+static inline cairo_drm_device_t *
+_cairo_drm_device_create_in_error (cairo_status_t status)
+{
+    return (cairo_drm_device_t *) _cairo_device_create_in_error (CAIRO_STATUS_NO_MEMORY);
+}
+
+cairo_private cairo_drm_device_t *
+_cairo_drm_device_init (cairo_drm_device_t *device,
+                       int fd,
+                       dev_t devid,
+                       int vendor_id,
+                       int chip_id,
+                       int max_surface_size);
+
+cairo_private void
+_cairo_drm_device_fini (cairo_drm_device_t *device);
+
+/* h/w specific backends */
+
+cairo_private cairo_drm_device_t *
+_cairo_drm_intel_device_create (int fd, dev_t dev, int vendor_id, int chip_id);
+
+cairo_private cairo_drm_device_t *
+_cairo_drm_i915_device_create (int fd, dev_t dev, int vendor_id, int chip_id);
+
+cairo_private cairo_drm_device_t *
+_cairo_drm_i965_device_create (int fd, dev_t dev, int vendor_id, int chip_id);
+
+cairo_private cairo_drm_device_t *
+_cairo_drm_radeon_device_create (int fd, dev_t dev, int vendor_id, int chip_id);
+
+#if CAIRO_HAS_GALLIUM_SURFACE
+cairo_private cairo_drm_device_t *
+_cairo_drm_gallium_device_create (int fd, dev_t dev, int vendor_id, int chip_id);
+#endif
+
+slim_hidden_proto (cairo_drm_device_default);
+slim_hidden_proto (cairo_drm_device_get);
+slim_hidden_proto (cairo_drm_device_get_for_fd);
+
+slim_hidden_proto (cairo_drm_surface_create_for_name);
+
+cairo_private cairo_bool_t
+_cairo_drm_size_is_valid (cairo_device_t *abstract_device,
+                         int width, int height);
+
+#endif /* CAIRO_DRM_PRIVATE_H */
diff --git a/src/drm/cairo-drm-radeon-private.h b/src/drm/cairo-drm-radeon-private.h
new file mode 100644 (file)
index 0000000..546126c
--- /dev/null
@@ -0,0 +1,107 @@
+/* Cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Chris Wilson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ */
+
+#ifndef CAIRO_DRM_RADEON_PRIVATE_H
+#define CAIRO_DRM_RADEON_PRIVATE_H
+
+#include "cairo-compiler-private.h"
+#include "cairo-types-private.h"
+#include "cairo-drm-private.h"
+#include "cairo-freelist-private.h"
+
+#define RADEON_GEM_DOMAIN_CPU          0x1
+#define RADEON_GEM_DOMAIN_GTT          0x2
+#define RADEON_GEM_DOMAIN_VRAM         0x4
+
+typedef struct _radeon_bo {
+    cairo_drm_bo_t base;
+
+    void *virtual;
+
+    cairo_bool_t in_batch;
+    uint32_t read_domains;
+    uint32_t write_domain;
+} radeon_bo_t;
+
+typedef struct _radeon_device {
+    cairo_drm_device_t base;
+    cairo_freepool_t bo_pool;
+
+    uint64_t vram_limit;
+    uint64_t gart_limit;
+} radeon_device_t;
+
+cairo_private cairo_status_t
+radeon_device_init (radeon_device_t *device, int fd);
+
+cairo_private void
+radeon_device_fini (radeon_device_t *device);
+
+cairo_private cairo_bool_t
+radeon_info (int fd,
+             uint64_t *gart_size,
+            uint64_t *vram_size);
+
+cairo_private void
+radeon_bo_write (const radeon_device_t *dev,
+                radeon_bo_t *bo,
+                unsigned long offset,
+                unsigned long size,
+                const void *data);
+
+cairo_private void
+radeon_bo_read (const radeon_device_t *dev,
+               radeon_bo_t *bo,
+               unsigned long offset,
+               unsigned long size,
+               void *data);
+
+cairo_private void
+radeon_bo_wait (const radeon_device_t *dev, radeon_bo_t *bo);
+
+cairo_private void *
+radeon_bo_map (const radeon_device_t *dev, radeon_bo_t *bo);
+
+cairo_private void
+radeon_bo_unmap (radeon_bo_t *bo);
+
+cairo_private cairo_drm_bo_t *
+radeon_bo_create (radeon_device_t *dev,
+                 uint32_t size,
+                 uint32_t initial_domain);
+
+cairo_private cairo_drm_bo_t *
+radeon_bo_create_for_name (radeon_device_t *dev, uint32_t name);
+
+cairo_private cairo_surface_t *
+radeon_bo_get_image (const radeon_device_t *device,
+                    radeon_bo_t *bo,
+                    const cairo_drm_surface_t *surface);
+
+#endif /* CAIRO_DRM_RADEON_PRIVATE_H */
diff --git a/src/drm/cairo-drm-radeon-surface.c b/src/drm/cairo-drm-radeon-surface.c
new file mode 100644 (file)
index 0000000..9335d7b
--- /dev/null
@@ -0,0 +1,441 @@
+/* Cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Chris Wilson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ */
+
+#include "cairoint.h"
+
+#include "cairo-drm-private.h"
+#include "cairo-drm-radeon-private.h"
+
+#include "cairo-default-context-private.h"
+#include "cairo-error-private.h"
+
+/* Basic stub surface for radeon chipsets */
+
+#define MAX_SIZE 2048
+
+typedef struct _radeon_surface {
+    cairo_drm_surface_t base;
+} radeon_surface_t;
+
+static inline radeon_device_t *
+to_radeon_device (cairo_device_t *device)
+{
+    return (radeon_device_t *) device;
+}
+
+static inline radeon_bo_t *
+to_radeon_bo (cairo_drm_bo_t *bo)
+{
+    return (radeon_bo_t *) bo;
+}
+
+static cairo_surface_t *
+radeon_surface_create_similar (void                    *abstract_surface,
+                             cairo_content_t            content,
+                             int                        width,
+                             int                        height)
+{
+    return cairo_image_surface_create (_cairo_format_from_content (content),
+                                      width, height);
+}
+
+static cairo_status_t
+radeon_surface_finish (void *abstract_surface)
+{
+    radeon_surface_t *surface = abstract_surface;
+
+    return _cairo_drm_surface_finish (&surface->base);
+}
+
+static cairo_status_t
+radeon_surface_acquire_source_image (void *abstract_surface,
+                                    cairo_image_surface_t **image_out,
+                                    void **image_extra)
+{
+    radeon_surface_t *surface = abstract_surface;
+    cairo_surface_t *image;
+    cairo_status_t status;
+
+    /* XXX batch flush */
+
+    if (surface->base.fallback != NULL) {
+       image = surface->base.fallback;
+       goto DONE;
+    }
+
+    image = _cairo_surface_has_snapshot (&surface->base.base,
+                                        &_cairo_image_surface_backend);
+    if (image != NULL)
+       goto DONE;
+
+    if (surface->base.base.backend->flush != NULL) {
+       status = surface->base.base.backend->flush (surface);
+       if (unlikely (status))
+           return status;
+    }
+
+    image = radeon_bo_get_image (to_radeon_device (surface->base.base.device),
+                               to_radeon_bo (surface->base.bo),
+                               &surface->base);
+    status = image->status;
+    if (unlikely (status))
+       return status;
+
+    _cairo_surface_attach_snapshot (&surface->base.base, image, cairo_surface_destroy);
+
+DONE:
+    *image_out = (cairo_image_surface_t *) cairo_surface_reference (image);
+    *image_extra = NULL;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+radeon_surface_release_source_image (void *abstract_surface,
+                                    cairo_image_surface_t *image,
+                                    void *image_extra)
+{
+    cairo_surface_destroy (&image->base);
+}
+
+static cairo_surface_t *
+radeon_surface_map_to_image (radeon_surface_t *surface)
+{
+    if (surface->base.fallback == NULL) {
+       cairo_surface_t *image;
+       cairo_status_t status;
+       void *ptr;
+
+       if (surface->base.base.backend->flush != NULL) {
+           status = surface->base.base.backend->flush (surface);
+           if (unlikely (status))
+               return _cairo_surface_create_in_error (status);
+       }
+
+       ptr = radeon_bo_map (to_radeon_device (surface->base.base.device),
+                           to_radeon_bo (surface->base.bo));
+       if (unlikely (ptr == NULL))
+           return _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY);
+
+       image = cairo_image_surface_create_for_data (ptr,
+                                                    surface->base.format,
+                                                    surface->base.width,
+                                                    surface->base.height,
+                                                    surface->base.stride);
+       if (unlikely (image->status)) {
+           radeon_bo_unmap (to_radeon_bo (surface->base.bo));
+           return image;
+       }
+
+       surface->base.fallback = image;
+    }
+
+    return surface->base.fallback;
+}
+
+static cairo_status_t
+radeon_surface_flush (void *abstract_surface)
+{
+    radeon_surface_t *surface = abstract_surface;
+    cairo_status_t status;
+
+    if (surface->base.fallback == NULL)
+       return CAIRO_STATUS_SUCCESS;
+
+    /* kill any outstanding maps */
+    cairo_surface_finish (surface->base.fallback);
+
+    status = cairo_surface_status (surface->base.fallback);
+    cairo_surface_destroy (surface->base.fallback);
+    surface->base.fallback = NULL;
+
+    radeon_bo_unmap (to_radeon_bo (surface->base.bo));
+
+    return status;
+}
+
+static cairo_int_status_t
+radeon_surface_paint (void *abstract_surface,
+                    cairo_operator_t            op,
+                    const cairo_pattern_t      *source,
+                    cairo_clip_t               *clip)
+{
+    return _cairo_surface_paint (radeon_surface_map_to_image (abstract_surface),
+                                op, source, clip);
+}
+
+static cairo_int_status_t
+radeon_surface_mask (void                      *abstract_surface,
+                   cairo_operator_t             op,
+                   const cairo_pattern_t       *source,
+                   const cairo_pattern_t       *mask,
+                   cairo_clip_t                *clip)
+{
+    return _cairo_surface_mask (radeon_surface_map_to_image (abstract_surface),
+                               op, source, mask, clip);
+}
+
+static cairo_int_status_t
+radeon_surface_stroke (void                    *abstract_surface,
+                     cairo_operator_t           op,
+                     const cairo_pattern_t     *source,
+                     cairo_path_fixed_t        *path,
+                     const cairo_stroke_style_t        *stroke_style,
+                     const cairo_matrix_t              *ctm,
+                     const cairo_matrix_t              *ctm_inverse,
+                     double                     tolerance,
+                     cairo_antialias_t          antialias,
+                     cairo_clip_t              *clip)
+{
+    return _cairo_surface_stroke (radeon_surface_map_to_image (abstract_surface),
+                                 op, source, path, stroke_style, ctm, ctm_inverse,
+                                 tolerance, antialias, clip);
+}
+
+static cairo_int_status_t
+radeon_surface_fill (void                      *abstract_surface,
+                   cairo_operator_t             op,
+                   const cairo_pattern_t       *source,
+                   cairo_path_fixed_t          *path,
+                   cairo_fill_rule_t            fill_rule,
+                   double                       tolerance,
+                   cairo_antialias_t            antialias,
+                   cairo_clip_t                *clip)
+{
+    return _cairo_surface_fill (radeon_surface_map_to_image (abstract_surface),
+                               op, source, path, fill_rule,
+                               tolerance, antialias, clip);
+}
+
+static cairo_int_status_t
+radeon_surface_glyphs (void                    *abstract_surface,
+                     cairo_operator_t           op,
+                     const cairo_pattern_t     *source,
+                     cairo_glyph_t             *glyphs,
+                     int                        num_glyphs,
+                     cairo_scaled_font_t       *scaled_font,
+                     cairo_clip_t              *clip,
+                     int *num_remaining)
+{
+    *num_remaining = 0;
+    return _cairo_surface_show_text_glyphs (radeon_surface_map_to_image (abstract_surface),
+                                           op, source,
+                                           NULL, 0,
+                                           glyphs, num_glyphs,
+                                           NULL, 0, 0,
+                                           scaled_font, clip);
+}
+
+static const cairo_surface_backend_t radeon_surface_backend = {
+    CAIRO_SURFACE_TYPE_DRM,
+    _cairo_default_context_create,
+
+    radeon_surface_create_similar,
+    radeon_surface_finish,
+    radeon_surface_acquire_source_image,
+    radeon_surface_release_source_image,
+    NULL, NULL, NULL,
+    NULL, /* composite */
+    NULL, /* fill */
+    NULL, /* trapezoids */
+    NULL, /* span */
+    NULL, /* check-span */
+
+    NULL, /* copy_page */
+    NULL, /* show_page */
+    _cairo_drm_surface_get_extents,
+    NULL, /* old-glyphs */
+    _cairo_drm_surface_get_font_options,
+
+    radeon_surface_flush,
+    NULL, /* mark dirty */
+    NULL, NULL, /* font/glyph fini */
+
+    radeon_surface_paint,
+    radeon_surface_mask,
+    radeon_surface_stroke,
+    radeon_surface_fill,
+    radeon_surface_glyphs,
+};
+
+static void
+radeon_surface_init (radeon_surface_t *surface,
+                    cairo_drm_device_t *device,
+                    cairo_format_t format,
+                    int width, int height)
+{
+    _cairo_surface_init (&surface->base.base,
+                        &radeon_surface_backend,
+                        &device->base,
+                        _cairo_content_from_format (format));
+    _cairo_drm_surface_init (&surface->base, format, width, height);
+}
+
+static cairo_surface_t *
+radeon_surface_create_internal (cairo_drm_device_t *device,
+                               cairo_format_t format,
+                               int width, int height)
+{
+    radeon_surface_t *surface;
+    cairo_status_t status;
+
+    surface = malloc (sizeof (radeon_surface_t));
+    if (unlikely (surface == NULL))
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+    radeon_surface_init (surface, device, format, width, height);
+
+    if (width && height) {
+       surface->base.stride =
+           cairo_format_stride_for_width (surface->base.format, width);
+
+       surface->base.bo = radeon_bo_create (to_radeon_device (&device->base),
+                                            surface->base.stride * height,
+                                            RADEON_GEM_DOMAIN_GTT);
+
+       if (unlikely (surface->base.bo == NULL)) {
+           status = _cairo_drm_surface_finish (&surface->base);
+           free (surface);
+           return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+       }
+    }
+
+    return &surface->base.base;
+}
+
+static cairo_surface_t *
+radeon_surface_create (cairo_drm_device_t *device,
+                      cairo_format_t format,
+                      int width, int height)
+{
+    switch (format) {
+    default:
+    case CAIRO_FORMAT_INVALID:
+    case CAIRO_FORMAT_A1:
+    case CAIRO_FORMAT_RGB16_565:
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
+    case CAIRO_FORMAT_ARGB32:
+    case CAIRO_FORMAT_RGB24:
+    case CAIRO_FORMAT_A8:
+       break;
+    }
+
+    return radeon_surface_create_internal (device, format, width, height);
+}
+
+static cairo_surface_t *
+radeon_surface_create_for_name (cairo_drm_device_t *device,
+                             unsigned int name,
+                             cairo_format_t format,
+                             int width, int height, int stride)
+{
+    radeon_surface_t *surface;
+    cairo_status_t status;
+
+    switch (format) {
+    default:
+    case CAIRO_FORMAT_INVALID:
+    case CAIRO_FORMAT_A1:
+    case CAIRO_FORMAT_RGB16_565:
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
+    case CAIRO_FORMAT_ARGB32:
+    case CAIRO_FORMAT_RGB24:
+    case CAIRO_FORMAT_A8:
+       break;
+    }
+
+    if (stride < cairo_format_stride_for_width (format, width))
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_STRIDE));
+
+    surface = malloc (sizeof (radeon_surface_t));
+    if (unlikely (surface == NULL))
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+    radeon_surface_init (surface, device, format, width, height);
+
+    if (width && height) {
+       surface->base.stride = stride;
+
+       surface->base.bo = radeon_bo_create_for_name (to_radeon_device (&device->base),
+                                                     name);
+
+       if (unlikely (surface->base.bo == NULL)) {
+           status = _cairo_drm_surface_finish (&surface->base);
+           free (surface);
+           return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+       }
+    }
+
+    return &surface->base.base;
+}
+
+static void
+radeon_device_destroy (void *data)
+{
+    radeon_device_t *device = data;
+
+    radeon_device_fini (device);
+
+    free (data);
+}
+
+cairo_drm_device_t *
+_cairo_drm_radeon_device_create (int fd, dev_t dev, int vendor_id, int chip_id)
+{
+    radeon_device_t *device;
+    uint64_t gart_size, vram_size;
+    cairo_status_t status;
+
+    if (! radeon_info (fd, &gart_size, &vram_size))
+       return NULL;
+
+    device = malloc (sizeof (radeon_device_t));
+    if (device == NULL)
+       return _cairo_drm_device_create_in_error (CAIRO_STATUS_NO_MEMORY);
+
+    status = radeon_device_init (device, fd);
+    if (unlikely (status)) {
+       free (device);
+       return _cairo_drm_device_create_in_error (status);
+    }
+
+    device->base.surface.create = radeon_surface_create;
+    device->base.surface.create_for_name = radeon_surface_create_for_name;
+    device->base.surface.create_from_cacheable_image = NULL;
+    device->base.surface.flink = _cairo_drm_surface_flink;
+    device->base.surface.enable_scan_out = NULL;
+
+    device->base.device.flush = NULL;
+    device->base.device.throttle = NULL;
+    device->base.device.destroy = radeon_device_destroy;
+
+    device->vram_limit = vram_size;
+    device->gart_limit = gart_size;
+
+    return _cairo_drm_device_init (&device->base, fd, dev, vendor_id, chip_id, MAX_SIZE);
+}
diff --git a/src/drm/cairo-drm-radeon.c b/src/drm/cairo-drm-radeon.c
new file mode 100644 (file)
index 0000000..a6d2208
--- /dev/null
@@ -0,0 +1,451 @@
+/* Cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Chris Wilson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ */
+
+#include "cairoint.h"
+
+#include "cairo-drm-private.h"
+#include "cairo-drm-radeon-private.h"
+#include "cairo-drm-ioctl-private.h"
+
+#include "cairo-error-private.h"
+
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <errno.h>
+
+#define DRM_RADEON_GEM_INFO            0x1c
+#define DRM_RADEON_GEM_CREATE          0x1d
+#define DRM_RADEON_GEM_MMAP            0x1e
+#define DRM_RADEON_GEM_PREAD           0x21
+#define DRM_RADEON_GEM_PWRITE          0x22
+#define DRM_RADEON_GEM_SET_DOMAIN      0x23
+#define DRM_RADEON_GEM_WAIT_IDLE       0x24
+#define DRM_RADEON_CS                  0x26
+#define DRM_RADEON_INFO                        0x27
+
+#define DRM_IOCTL_RADEON_GEM_INFO   DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_GEM_INFO, struct drm_radeon_gem_info)
+#define DRM_IOCTL_RADEON_GEM_CREATE   DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_GEM_CREATE, struct drm_radeon_gem_create)
+#define DRM_IOCTL_RADEON_GEM_WAIT_IDLE   DRM_IOW(DRM_COMMAND_BASE + DRM_RADEON_GEM_WAIT_IDLE, struct drm_radeon_gem_wait_idle)
+#define DRM_IOCTL_RADEON_GEM_MMAP   DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_GEM_MMAP, struct drm_radeon_gem_mmap)
+#define DRM_IOCTL_RADEON_GEM_PREAD   DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_GEM_PREAD, struct drm_radeon_gem_pread)
+#define DRM_IOCTL_RADEON_GEM_PWRITE   DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_GEM_PWRITE, struct drm_radeon_gem_pwrite)
+#define DRM_IOCTL_RADEON_GEM_SET_DOMAIN  DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_GEM_SET_DOMAIN, struct drm_radeon_gem_set_domain)
+//#define DRM_IOCTL_RADEON_CS DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_CS, struct drm_radeon_cs)
+
+struct drm_radeon_gem_info {
+    uint64_t   gart_size;
+    uint64_t   vram_size;
+    uint64_t   vram_visible;
+};
+
+#define RADEON_GEM_NO_BACKING_STORE 1
+
+struct drm_radeon_gem_create {
+    uint64_t   size;
+    uint64_t   alignment;
+    uint32_t   handle;
+    uint32_t   initial_domain;
+    uint32_t   flags;
+};
+
+struct drm_radeon_gem_mmap {
+    uint32_t   handle;
+    uint32_t   pad;
+    uint64_t   offset;
+    uint64_t   size;
+    uint64_t   addr_ptr;
+};
+
+struct drm_radeon_gem_set_domain {
+    uint32_t   handle;
+    uint32_t   read_domains;
+    uint32_t   write_domain;
+};
+
+struct drm_radeon_gem_wait_idle {
+    uint32_t   handle;
+    uint32_t   pad;
+};
+
+struct drm_radeon_gem_busy {
+    uint32_t   handle;
+    uint32_t   busy;
+};
+
+struct drm_radeon_gem_pread {
+    /** Handle for the object being read. */
+    uint32_t handle;
+    uint32_t pad;
+    /** Offset into the object to read from */
+    uint64_t offset;
+    /** Length of data to read */
+    uint64_t size;
+    /** Pointer to write the data into. */
+    /* void *, but pointers are not 32/64 compatible */
+    uint64_t data_ptr;
+};
+
+struct drm_radeon_gem_pwrite {
+    /** Handle for the object being written to. */
+    uint32_t handle;
+    uint32_t pad;
+    /** Offset into the object to write to */
+    uint64_t offset;
+    /** Length of data to write */
+    uint64_t size;
+    /** Pointer to read the data from. */
+    /* void *, but pointers are not 32/64 compatible */
+    uint64_t data_ptr;
+};
+
+#define RADEON_CHUNK_ID_RELOCS 0x01
+#define RADEON_CHUNK_ID_IB     0x02
+
+struct drm_radeon_cs_chunk {
+    uint32_t           chunk_id;
+    uint32_t           length_dw;
+    uint64_t           chunk_data;
+};
+
+struct drm_radeon_cs_reloc {
+    uint32_t           handle;
+    uint32_t           read_domains;
+    uint32_t           write_domain;
+    uint32_t           flags;
+};
+
+struct drm_radeon_cs {
+    uint32_t           num_chunks;
+    uint32_t           cs_id;
+    /* this points to uint64_t * which point to cs chunks */
+    uint64_t           chunks;
+    /* updates to the limits after this CS ioctl */
+    uint64_t           gart_limit;
+    uint64_t           vram_limit;
+};
+
+#define RADEON_INFO_DEVICE_ID          0x00
+#define RADEON_INFO_NUM_GB_PIPES       0x01
+
+struct drm_radeon_info {
+    uint32_t           request;
+    uint32_t           pad;
+    uint64_t           value;
+};
+
+
+cairo_bool_t
+radeon_info (int fd,
+             uint64_t *gart_size,
+            uint64_t *vram_size)
+{
+    struct drm_radeon_gem_info info;
+    int ret;
+
+    ret = ioctl (fd, DRM_IOCTL_RADEON_GEM_INFO, &info);
+    if (ret == -1)
+       return FALSE;
+
+    if (gart_size != NULL)
+       *gart_size = info.gart_size;
+
+    if (vram_size != NULL)
+       *vram_size = info.vram_size;
+
+    return TRUE;
+}
+
+void
+radeon_bo_write (const radeon_device_t *device,
+                radeon_bo_t *bo,
+                unsigned long offset,
+                unsigned long size,
+                const void *data)
+{
+    struct drm_radeon_gem_pwrite pwrite;
+    int ret;
+
+    memset (&pwrite, 0, sizeof (pwrite));
+    pwrite.handle = bo->base.handle;
+    pwrite.offset = offset;
+    pwrite.size = size;
+    pwrite.data_ptr = (uint64_t) (uintptr_t) data;
+    do {
+       ret = ioctl (device->base.fd, DRM_IOCTL_RADEON_GEM_PWRITE, &pwrite);
+    } while (ret == -1 && errno == EINTR);
+
+    /* XXX temporary workaround */
+    if (ret == -1 && errno == ENOSYS) {
+       uint8_t *ptr;
+
+       ptr = radeon_bo_map (device, bo);
+       if (ptr != NULL) {
+           memcpy (ptr + offset, data, size);
+           radeon_bo_unmap (bo);
+       }
+    }
+}
+
+void
+radeon_bo_read (const radeon_device_t *device,
+               radeon_bo_t *bo,
+               unsigned long offset,
+               unsigned long size,
+               void *data)
+{
+    struct drm_radeon_gem_pread pread;
+    int ret;
+
+    memset (&pread, 0, sizeof (pread));
+    pread.handle = bo->base.handle;
+    pread.offset = offset;
+    pread.size = size;
+    pread.data_ptr = (uint64_t) (uintptr_t) data;
+    do {
+       ret = ioctl (device->base.fd, DRM_IOCTL_RADEON_GEM_PREAD, &pread);
+    } while (ret == -1 && errno == EINTR);
+
+    /* XXX temporary workaround */
+    if (ret == -1 && errno == ENOSYS) {
+       uint8_t *ptr;
+
+       ptr = radeon_bo_map (device, bo);
+       if (ptr != NULL) {
+           memcpy (data, ptr + offset, size);
+           radeon_bo_unmap (bo);
+       }
+    }
+
+    VG (VALGRIND_MAKE_MEM_DEFINED (data, size));
+}
+
+void
+radeon_bo_wait (const radeon_device_t *device, radeon_bo_t *bo)
+{
+    struct drm_radeon_gem_wait_idle wait;
+    int ret;
+
+    wait.handle = bo->base.handle;
+    do {
+       ret = ioctl (device->base.fd, DRM_IOCTL_RADEON_GEM_WAIT_IDLE, &wait);
+    } while (ret == -1 && (errno == EINTR || errno == EBUSY));
+}
+
+void *
+radeon_bo_map (const radeon_device_t *device, radeon_bo_t *bo)
+{
+    struct drm_radeon_gem_mmap mmap_arg;
+    void *ptr;
+    int ret;
+
+    assert (bo->virtual == NULL);
+
+    memset (&mmap_arg, 0, sizeof (mmap_arg));
+    mmap_arg.handle = bo->base.handle;
+    mmap_arg.offset = 0;
+    mmap_arg.size = bo->base.size;
+
+    do {
+       ret = ioctl (device->base.fd, DRM_IOCTL_RADEON_GEM_MMAP, &mmap_arg);
+    } while (ret == -1 && errno == EINTR);
+    if (unlikely (ret != 0)) {
+       _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+       return NULL;
+    }
+
+    VG (VALGRIND_MAKE_MEM_DEFINED (&mmap_arg, sizeof (mmap_arg)));
+
+    /* and mmap it */
+    ptr = mmap (0, bo->base.size, PROT_READ | PROT_WRITE,
+               MAP_SHARED, device->base.fd,
+               mmap_arg.addr_ptr);
+    if (unlikely (ptr == MAP_FAILED)) {
+       _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+       return NULL;
+    }
+
+    bo->virtual = ptr;
+
+    /* XXX set_domain? */
+    return bo->virtual;
+}
+
+void
+radeon_bo_unmap (radeon_bo_t *bo)
+{
+    assert (bo->virtual != NULL);
+
+    munmap (bo->virtual, bo->base.size);
+    bo->virtual = NULL;
+}
+
+cairo_drm_bo_t *
+radeon_bo_create (radeon_device_t *device,
+                 uint32_t size,
+                 uint32_t initial_domain)
+{
+    struct drm_radeon_gem_create create;
+    radeon_bo_t *bo;
+    int ret;
+
+    bo = _cairo_freepool_alloc (&device->bo_pool);
+    if (unlikely (bo == NULL))
+       return NULL;
+
+    create.size = size;
+    create.alignment = 0;
+    create.initial_domain = initial_domain;
+    create.flags = 0;
+    create.handle = 0;
+
+    do {
+       ret = ioctl (device->base.fd, DRM_IOCTL_RADEON_GEM_CREATE, &create);
+    } while (ret == -1 && errno == EINTR);
+    if (ret == -1) {
+       _cairo_freepool_free (&device->bo_pool, bo);
+       return NULL;
+    }
+
+    bo->base.handle = create.handle;
+    bo->base.size = size;
+
+    bo->virtual = NULL;
+
+    bo->in_batch = FALSE;
+    bo->read_domains = 0;
+    bo->write_domain = 0;
+
+    CAIRO_REFERENCE_COUNT_INIT (&bo->base.ref_count, 1);
+    return &bo->base;
+}
+
+cairo_drm_bo_t *
+radeon_bo_create_for_name (radeon_device_t *device,
+                          uint32_t name)
+{
+    radeon_bo_t *bo;
+    cairo_status_t status;
+
+    bo = _cairo_freepool_alloc (&device->bo_pool);
+    if (unlikely (bo == NULL))
+       return NULL;
+
+    status = _cairo_drm_bo_open_for_name (&device->base, &bo->base, name);
+    if (unlikely (status)) {
+       _cairo_freepool_free (&device->bo_pool, bo);
+       return NULL;
+    }
+
+    bo->virtual = NULL;
+
+    bo->in_batch = FALSE;
+    bo->read_domains = 0;
+    bo->write_domain = 0;
+
+    CAIRO_REFERENCE_COUNT_INIT (&bo->base.ref_count, 1);
+    return &bo->base;
+}
+
+static void
+radeon_bo_release (void *_dev, void *_bo)
+{
+    radeon_device_t *device = _dev;
+    radeon_bo_t *bo = _bo;
+
+    _cairo_drm_bo_close (&device->base, &bo->base);
+    _cairo_freepool_free (&device->bo_pool, bo);
+}
+
+cairo_surface_t *
+radeon_bo_get_image (const radeon_device_t *device,
+                    radeon_bo_t *bo,
+                    const cairo_drm_surface_t *surface)
+{
+    cairo_image_surface_t *image;
+    uint8_t *dst;
+    int size, row;
+
+    image = (cairo_image_surface_t *)
+       cairo_image_surface_create (surface->format,
+                                   surface->width,
+                                   surface->height);
+    if (unlikely (image->base.status))
+       return &image->base;
+
+    if (image->stride == surface->stride) {
+       size = surface->stride * surface->height;
+       radeon_bo_read (device, bo, 0, size, image->data);
+    } else {
+       int offset;
+
+       size = surface->width;
+       if (surface->format != CAIRO_FORMAT_A8)
+           size *= 4;
+
+       offset = 0;
+       row = surface->height;
+       dst = image->data;
+       while (row--) {
+           radeon_bo_read (device, bo, offset, size, dst);
+           offset += surface->stride;
+           dst += image->stride;
+       }
+    }
+
+    return &image->base;
+}
+
+static void
+_radeon_device_init_bo_cache (radeon_device_t *device)
+{
+    _cairo_freepool_init (&device->bo_pool, sizeof (radeon_bo_t));
+}
+
+cairo_status_t
+radeon_device_init (radeon_device_t *device, int fd)
+{
+    _radeon_device_init_bo_cache (device);
+
+    device->base.bo.release = radeon_bo_release;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_radeon_bo_cache_fini (radeon_device_t *device)
+{
+    _cairo_freepool_fini (&device->bo_pool);
+}
+
+void
+radeon_device_fini (radeon_device_t *device)
+{
+    _radeon_bo_cache_fini (device);
+    _cairo_drm_device_fini (&device->base);
+}
diff --git a/src/drm/cairo-drm-surface.c b/src/drm/cairo-drm-surface.c
new file mode 100644 (file)
index 0000000..a8a8f32
--- /dev/null
@@ -0,0 +1,368 @@
+/* Cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Chris Wilson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Chris Wilson.
+ */
+
+#include "cairoint.h"
+
+#include "cairo-drm-private.h"
+
+#include "cairo-error-private.h"
+
+void
+_cairo_drm_surface_init (cairo_drm_surface_t *surface,
+                        cairo_format_t format,
+                        int width, int height)
+{
+    surface->bo = NULL;
+    surface->format = format;
+    surface->width  = width;
+    surface->height = height;
+    surface->stride = 0;
+
+    surface->fallback = NULL;
+    surface->map_count = 0;
+}
+
+cairo_status_t
+_cairo_drm_surface_finish (cairo_drm_surface_t *surface)
+{
+    assert (surface->fallback == NULL);
+
+    if (surface->bo != NULL)
+       cairo_drm_bo_destroy (surface->base.device, surface->bo);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+void
+_cairo_drm_surface_get_font_options (void                  *abstract_surface,
+                                    cairo_font_options_t  *options)
+{
+    _cairo_font_options_init_default (options);
+
+    cairo_font_options_set_hint_metrics (options, CAIRO_HINT_METRICS_ON);
+}
+
+cairo_bool_t
+_cairo_drm_surface_get_extents (void *abstract_surface,
+                               cairo_rectangle_int_t *rectangle)
+{
+    cairo_drm_surface_t *surface = abstract_surface;
+
+    rectangle->x = 0;
+    rectangle->y = 0;
+    rectangle->width  = surface->width;
+    rectangle->height = surface->height;
+
+    return TRUE;
+}
+
+cairo_surface_t *
+cairo_drm_surface_create (cairo_device_t *abstract_device,
+                         cairo_format_t format,
+                         int width, int height)
+{
+    cairo_drm_device_t *device = (cairo_drm_device_t *) abstract_device;
+    cairo_surface_t *surface;
+
+    if (device != NULL && device->base.status)
+    {
+       surface = _cairo_surface_create_in_error (device->base.status);
+    }
+    else if (device == NULL ||
+            device->surface.create == NULL ||
+            width == 0 || width > device->max_surface_size ||
+            height == 0 || height > device->max_surface_size)
+    {
+       surface = cairo_image_surface_create (format, width, height);
+    }
+    else if (device->base.finished)
+    {
+       surface = _cairo_surface_create_in_error (CAIRO_STATUS_SURFACE_FINISHED);
+    }
+    else
+    {
+       surface = device->surface.create (device, format, width, height);
+       if (surface->status == CAIRO_STATUS_INVALID_SIZE)
+           surface = cairo_image_surface_create (format, width, height);
+    }
+
+    return surface;
+}
+
+cairo_surface_t *
+cairo_drm_surface_create_for_name (cairo_device_t *abstract_device,
+                                  unsigned int name,
+                                  cairo_format_t format,
+                                  int width, int height, int stride)
+{
+    cairo_drm_device_t *device = (cairo_drm_device_t *) abstract_device;
+    cairo_surface_t *surface;
+
+    if (! CAIRO_FORMAT_VALID (format))
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
+
+    if (device != NULL && device->base.status)
+    {
+       surface = _cairo_surface_create_in_error (device->base.status);
+    }
+    else if (device == NULL || device->surface.create_for_name == NULL)
+    {
+       /* XXX invalid device! */
+       surface = _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
+    }
+    else if (width == 0 || width > device->max_surface_size ||
+            height == 0 || height > device->max_surface_size)
+    {
+       surface = _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE));
+    }
+    else if (device->base.finished)
+    {
+       surface = _cairo_surface_create_in_error (CAIRO_STATUS_SURFACE_FINISHED);
+    }
+    else
+    {
+       surface = device->surface.create_for_name (device,
+                                                    name, format,
+                                                    width, height, stride);
+    }
+
+    return surface;
+}
+slim_hidden_def (cairo_drm_surface_create_for_name);
+
+cairo_surface_t *
+cairo_drm_surface_create_from_cacheable_image (cairo_device_t *abstract_device,
+                                              cairo_surface_t *surface)
+{
+    cairo_drm_device_t *device = (cairo_drm_device_t *) abstract_device;
+
+    if (surface->status) {
+       surface = _cairo_surface_create_in_error (surface->status);
+    } else if (device != NULL && device->base.status) {
+       surface = _cairo_surface_create_in_error (device->base.status);
+    } else if (device == NULL || device->surface.create_from_cacheable_image == NULL) {
+       /* XXX invalid device! */
+       surface = _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
+    } else if (device->base.finished) {
+       surface = _cairo_surface_create_in_error (CAIRO_STATUS_SURFACE_FINISHED);
+    } else {
+       surface = device->surface.create_from_cacheable_image (device, surface);
+    }
+
+    return surface;
+}
+
+static cairo_drm_surface_t *
+_cairo_surface_as_drm (cairo_surface_t *abstract_surface)
+{
+    if (unlikely (abstract_surface->status))
+       return NULL;
+
+    if (abstract_surface->type != CAIRO_SURFACE_TYPE_DRM)
+       return NULL;
+
+    return (cairo_drm_surface_t *) abstract_surface;
+}
+
+cairo_status_t
+cairo_drm_surface_enable_scan_out (cairo_surface_t *abstract_surface)
+{
+    cairo_drm_surface_t *surface;
+    cairo_drm_device_t *device;
+
+    surface = _cairo_surface_as_drm (abstract_surface);
+    if (unlikely (surface == NULL))
+       return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+    if (unlikely (surface->base.finished))
+       return _cairo_error (CAIRO_STATUS_SURFACE_FINISHED);
+
+    device = (cairo_drm_device_t *) surface->base.device;
+    if (device->surface.enable_scan_out == NULL)
+       return CAIRO_STATUS_SUCCESS;
+
+    if (unlikely (device->base.finished))
+       return _cairo_error (CAIRO_STATUS_SURFACE_FINISHED);
+
+    return device->surface.enable_scan_out (abstract_surface);
+}
+
+unsigned int
+cairo_drm_surface_get_handle (cairo_surface_t *abstract_surface)
+{
+    cairo_drm_surface_t *surface;
+
+    surface = _cairo_surface_as_drm (abstract_surface);
+    if (surface == NULL) {
+       _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+       return 0;
+    }
+
+    return surface->bo->handle;
+}
+
+cairo_int_status_t
+_cairo_drm_surface_flink (void *abstract_surface)
+{
+    cairo_drm_surface_t *surface = abstract_surface;
+
+    return _cairo_drm_bo_flink ((cairo_drm_device_t *) surface->base.device,
+                               surface->bo);
+}
+
+unsigned int
+cairo_drm_surface_get_name (cairo_surface_t *abstract_surface)
+{
+    cairo_drm_surface_t *surface;
+    cairo_drm_device_t *device;
+    cairo_status_t status;
+
+    surface = _cairo_surface_as_drm (abstract_surface);
+    if (surface == NULL) {
+       _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+       return 0;
+    }
+
+    if (surface->bo->name)
+       return surface->bo->name;
+
+    device = (cairo_drm_device_t *) surface->base.device;
+    if (device->surface.flink == NULL)
+       return 0;
+
+    status = device->surface.flink (abstract_surface);
+    if (status) {
+       if (_cairo_status_is_error (status))
+           status = _cairo_surface_set_error (abstract_surface, status);
+
+       return 0;
+    }
+
+    return surface->bo->name;
+}
+
+cairo_format_t
+cairo_drm_surface_get_format (cairo_surface_t *abstract_surface)
+{
+    cairo_drm_surface_t *surface;
+
+    surface = _cairo_surface_as_drm (abstract_surface);
+    if (surface == NULL)
+       return cairo_image_surface_get_format (abstract_surface);
+
+    return surface->format;
+}
+
+int
+cairo_drm_surface_get_width (cairo_surface_t *abstract_surface)
+{
+    cairo_drm_surface_t *surface;
+
+    surface = _cairo_surface_as_drm (abstract_surface);
+    if (surface == NULL)
+       return cairo_image_surface_get_width (abstract_surface);
+
+    return surface->width;
+}
+
+int
+cairo_drm_surface_get_height (cairo_surface_t *abstract_surface)
+{
+    cairo_drm_surface_t *surface;
+
+    surface = _cairo_surface_as_drm (abstract_surface);
+    if (surface == NULL)
+       return cairo_image_surface_get_height (abstract_surface);
+
+    return surface->height;
+}
+
+int
+cairo_drm_surface_get_stride (cairo_surface_t *abstract_surface)
+{
+    cairo_drm_surface_t *surface;
+
+    surface = _cairo_surface_as_drm (abstract_surface);
+    if (surface == NULL)
+       return cairo_image_surface_get_stride (abstract_surface);
+
+    return surface->stride;
+}
+
+/* XXX drm or general surface layer? naming? */
+cairo_surface_t *
+cairo_drm_surface_map_to_image (cairo_surface_t *abstract_surface)
+{
+    cairo_drm_surface_t *surface;
+    cairo_drm_device_t *device;
+    cairo_status_t status;
+
+    if (unlikely (abstract_surface->status))
+       return _cairo_surface_create_in_error (abstract_surface->status);
+
+    surface = _cairo_surface_as_drm (abstract_surface);
+    if (surface == NULL) {
+       if (_cairo_surface_is_image (abstract_surface))
+           return cairo_surface_reference (abstract_surface);
+
+       status = _cairo_surface_set_error (abstract_surface,
+                                          CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+       return _cairo_surface_create_in_error (status);
+    }
+
+    surface->map_count++;
+    device = (cairo_drm_device_t *) surface->base.device;
+    return cairo_surface_reference (device->surface.map_to_image (surface));
+}
+
+void
+cairo_drm_surface_unmap (cairo_surface_t *abstract_surface,
+                        cairo_surface_t *image)
+{
+    cairo_drm_surface_t *surface;
+
+    surface = _cairo_surface_as_drm (abstract_surface);
+    if (surface == NULL) {
+       if (_cairo_surface_is_image (abstract_surface))
+           cairo_surface_destroy (image);
+       else
+           _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+       return;
+    }
+
+    /* XXX assert image belongs to drm */
+    //assert (image == drm->fallback);
+    cairo_surface_destroy (image);
+
+    assert (surface->map_count > 0);
+    if (--surface->map_count == 0)
+       cairo_surface_flush (&surface->base);
+}
diff --git a/src/drm/cairo-drm.c b/src/drm/cairo-drm.c
new file mode 100644 (file)
index 0000000..051b79e
--- /dev/null
@@ -0,0 +1,387 @@
+/* Cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Chris Wilson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Chris Wilson.
+ */
+
+#include "cairoint.h"
+
+#include "cairo-drm-private.h"
+
+#include "cairo-device-private.h"
+#include "cairo-error-private.h"
+
+#define LIBUDEV_I_KNOW_THE_API_IS_SUBJECT_TO_CHANGE
+#include <libudev.h>
+#include <fcntl.h>
+#include <unistd.h> /* open(), close() */
+
+static cairo_drm_device_t *_cairo_drm_known_devices;
+static cairo_drm_device_t *_cairo_drm_default_device;
+
+static const char *
+get_udev_property(struct udev_device *device, const char *name)
+{
+    struct udev_list_entry *entry;
+
+    udev_list_entry_foreach (entry,
+                            udev_device_get_properties_list_entry (device))
+    {
+       if (strcmp (udev_list_entry_get_name (entry), name) == 0)
+           return udev_list_entry_get_value (entry);
+    }
+
+    return NULL;
+}
+
+static void
+_device_flush (void *abstract_device)
+{
+    cairo_drm_device_t *device = abstract_device;
+
+    device->device.flush (device);
+}
+
+static void
+_device_finish (void *abstract_device)
+{
+    cairo_drm_device_t *device = abstract_device;
+
+    CAIRO_MUTEX_LOCK (_cairo_drm_device_mutex);
+    if (device->prev != NULL)
+       device->prev->next = device->next;
+    else
+       _cairo_drm_known_devices = device->next;
+    if (device->next != NULL)
+       device->next->prev = device->prev;
+
+    CAIRO_MUTEX_UNLOCK (_cairo_drm_device_mutex);
+
+    if (_cairo_atomic_ptr_cmpxchg (&_cairo_drm_default_device,
+                                  device, NULL))
+    {
+       cairo_device_destroy (&device->base);
+    }
+}
+
+static void
+_device_destroy (void *abstract_device)
+{
+    cairo_drm_device_t *device = abstract_device;
+
+    device->device.destroy (device);
+}
+
+static const cairo_device_backend_t _cairo_drm_device_backend = {
+    CAIRO_DEVICE_TYPE_DRM,
+
+    NULL, NULL, /* lock, unlock */
+
+    _device_flush,
+    _device_finish,
+    _device_destroy,
+};
+
+cairo_drm_device_t *
+_cairo_drm_device_init (cairo_drm_device_t *dev,
+                       int fd,
+                       dev_t devid,
+                       int vendor_id,
+                       int chip_id,
+                       int max_surface_size)
+{
+    assert (CAIRO_MUTEX_IS_LOCKED (_cairo_drm_device_mutex));
+
+    _cairo_device_init (&dev->base, &_cairo_drm_device_backend);
+
+    dev->id = devid;
+    dev->vendor_id = vendor_id;
+    dev->chip_id = chip_id;
+    dev->fd = fd;
+
+    dev->max_surface_size = max_surface_size;
+
+    dev->prev = NULL;
+    dev->next = _cairo_drm_known_devices;
+    if (_cairo_drm_known_devices != NULL)
+       _cairo_drm_known_devices->prev = dev;
+    _cairo_drm_known_devices = dev;
+
+    if (_cairo_drm_default_device == NULL)
+       _cairo_drm_default_device = (cairo_drm_device_t *) cairo_device_reference (&dev->base);
+
+    return dev;
+}
+
+cairo_device_t *
+cairo_drm_device_get (struct udev_device *device)
+{
+    static const struct dri_driver_entry {
+       uint32_t vendor_id;
+       uint32_t chip_id;
+       cairo_drm_device_create_func_t create_func;
+    } driver_map[] = {
+       { 0x8086, 0x29a2, _cairo_drm_i965_device_create }, /* I965_G */
+       { 0x8086, 0x2982, _cairo_drm_i965_device_create }, /* G35_G */
+       { 0x8086, 0x2992, _cairo_drm_i965_device_create }, /* I965_Q */
+       { 0x8086, 0x2972, _cairo_drm_i965_device_create }, /* I946_GZ */
+       { 0x8086, 0x2a02, _cairo_drm_i965_device_create }, /* I965_GM */
+       { 0x8086, 0x2a12, _cairo_drm_i965_device_create }, /* I965_GME */
+       { 0x8086, 0x2e02, _cairo_drm_i965_device_create }, /* IGD_E_G */
+       { 0x8086, 0x2e22, _cairo_drm_i965_device_create }, /* G45_G */
+       { 0x8086, 0x2e12, _cairo_drm_i965_device_create }, /* Q45_G */
+       { 0x8086, 0x2e32, _cairo_drm_i965_device_create }, /* G41_G */
+       { 0x8086, 0x2a42, _cairo_drm_i965_device_create }, /* GM45_GM */
+
+       { 0x8086, 0x2582, _cairo_drm_i915_device_create }, /* I915_G */
+       { 0x8086, 0x2592, _cairo_drm_i915_device_create }, /* I915_GM */
+       { 0x8086, 0x258a, _cairo_drm_i915_device_create }, /* E7221_G */
+       { 0x8086, 0x2772, _cairo_drm_i915_device_create }, /* I945_G */
+       { 0x8086, 0x27a2, _cairo_drm_i915_device_create }, /* I945_GM */
+       { 0x8086, 0x27ae, _cairo_drm_i915_device_create }, /* I945_GME */
+       { 0x8086, 0x29c2, _cairo_drm_i915_device_create }, /* G33_G */
+       { 0x8086, 0x29b2, _cairo_drm_i915_device_create }, /* Q35_G */
+       { 0x8086, 0x29d2, _cairo_drm_i915_device_create }, /* Q33_G */
+       { 0x8086, 0xa011, _cairo_drm_i915_device_create }, /* IGD_GM */
+       { 0x8086, 0xa001, _cairo_drm_i915_device_create }, /* IGD_G */
+
+       /* XXX i830 */
+
+       { 0x8086, ~0, _cairo_drm_intel_device_create },
+
+       { 0x1002, ~0, _cairo_drm_radeon_device_create },
+#if CAIRO_HAS_GALLIUM_SURFACE
+       { ~0, ~0, _cairo_drm_gallium_device_create },
+#endif
+    };
+
+    cairo_drm_device_t *dev;
+    dev_t devid;
+    struct udev_device *parent;
+    const char *pci_id;
+    uint32_t vendor_id, chip_id;
+    const char *path;
+    int i, fd;
+
+    devid = udev_device_get_devnum (device);
+
+    CAIRO_MUTEX_LOCK (_cairo_drm_device_mutex);
+    for (dev = _cairo_drm_known_devices; dev != NULL; dev = dev->next) {
+       if (dev->id == devid) {
+           dev = (cairo_drm_device_t *) cairo_device_reference (&dev->base);
+           goto DONE;
+       }
+    }
+
+    parent = udev_device_get_parent (device);
+    pci_id = get_udev_property (parent, "PCI_ID");
+    if (pci_id == NULL || sscanf (pci_id, "%x:%x", &vendor_id, &chip_id) != 2) {
+       dev = (cairo_drm_device_t *)
+           _cairo_device_create_in_error (CAIRO_STATUS_DEVICE_ERROR);
+       goto DONE;
+    }
+
+#if CAIRO_HAS_GALLIUM_SURFACE
+    if (getenv ("CAIRO_GALLIUM_FORCE"))
+    {
+       i = ARRAY_LENGTH (driver_map) - 1;
+    }
+    else
+#endif
+    {
+       for (i = 0; i < ARRAY_LENGTH (driver_map); i++) {
+           if (driver_map[i].vendor_id == ~0U)
+               break;
+
+           if (driver_map[i].vendor_id == vendor_id &&
+               (driver_map[i].chip_id == ~0U || driver_map[i].chip_id == chip_id))
+               break;
+       }
+
+       if (i == ARRAY_LENGTH (driver_map)) {
+           dev = (cairo_drm_device_t *)
+               _cairo_device_create_in_error (CAIRO_STATUS_DEVICE_ERROR);
+           goto DONE;
+       }
+    }
+
+    path = udev_device_get_devnode (device);
+    if (path == NULL)
+       path = "/dev/dri/card0"; /* XXX buggy udev? */
+
+    fd = open (path, O_RDWR);
+    if (fd == -1) {
+       /* XXX more likely to be a permissions issue... */
+       _cairo_error_throw (CAIRO_STATUS_FILE_NOT_FOUND);
+       goto DONE;
+    }
+
+    dev = driver_map[i].create_func (fd, devid, vendor_id, chip_id);
+    if (dev == NULL)
+       close (fd);
+
+  DONE:
+    CAIRO_MUTEX_UNLOCK (_cairo_drm_device_mutex);
+
+    return &dev->base;
+}
+slim_hidden_def (cairo_drm_device_get);
+
+cairo_device_t *
+cairo_drm_device_get_for_fd (int fd)
+{
+    struct stat st;
+    struct udev *udev;
+    struct udev_device *device;
+    cairo_device_t *dev = NULL;
+
+    if (fstat (fd, &st) < 0 || ! S_ISCHR (st.st_mode)) {
+       //_cairo_error_throw (CAIRO_STATUS_INVALID_DEVICE);
+       return _cairo_device_create_in_error (CAIRO_STATUS_NO_MEMORY);
+    }
+
+    udev = udev_new ();
+
+    device = udev_device_new_from_devnum (udev, 'c', st.st_rdev);
+    if (device != NULL) {
+       dev = cairo_drm_device_get (device);
+       udev_device_unref (device);
+    }
+
+    udev_unref (udev);
+
+    return dev;
+}
+slim_hidden_def (cairo_drm_device_get_for_fd);
+
+cairo_device_t *
+cairo_drm_device_default (void)
+{
+    struct udev *udev;
+    struct udev_enumerate *e;
+    struct udev_list_entry *entry;
+    cairo_device_t *dev;
+
+    /* optimistic atomic pointer read */
+    dev = &_cairo_drm_default_device->base;
+    if (dev != NULL)
+       return dev;
+
+    udev = udev_new();
+    if (udev == NULL)
+       return _cairo_device_create_in_error (CAIRO_STATUS_NO_MEMORY);
+
+    e = udev_enumerate_new (udev);
+    udev_enumerate_add_match_subsystem (e, "drm");
+    udev_enumerate_scan_devices (e);
+    udev_list_entry_foreach (entry, udev_enumerate_get_list_entry (e)) {
+       struct udev_device *device;
+
+       device =
+           udev_device_new_from_syspath (udev,
+                   udev_list_entry_get_name (entry));
+
+       dev = cairo_drm_device_get (device);
+
+       udev_device_unref (device);
+
+       if (dev != NULL) {
+           if (((cairo_drm_device_t *) dev)->fd == -1) {
+               /* try again, we may find a usable card */
+               cairo_device_destroy (dev);
+               dev = NULL;
+           } else
+               break;
+       }
+    }
+    udev_enumerate_unref (e);
+    udev_unref (udev);
+
+    cairo_device_destroy (dev); /* owned by _cairo_drm_default_device */
+    return dev;
+}
+slim_hidden_def (cairo_drm_device_default);
+
+void
+_cairo_drm_device_reset_static_data (void)
+{
+    if (_cairo_drm_default_device != NULL) {
+       cairo_device_t *device = &_cairo_drm_default_device->base;
+       _cairo_drm_default_device = NULL;
+       cairo_device_destroy (device);
+    }
+}
+
+int
+cairo_drm_device_get_fd (cairo_device_t *abstract_device)
+{
+    cairo_drm_device_t *device = (cairo_drm_device_t *) abstract_device;
+
+    if (device->base.status)
+       return -1;
+
+    return device->fd;
+}
+
+void
+_cairo_drm_device_fini (cairo_drm_device_t *device)
+{
+    if (device->fd != -1)
+       close (device->fd);
+}
+
+void
+cairo_drm_device_throttle (cairo_device_t *abstract_device)
+{
+    cairo_drm_device_t *device = (cairo_drm_device_t *) abstract_device;
+    cairo_status_t status;
+
+    if (unlikely (device->base.status))
+       return;
+
+    if (device->device.throttle == NULL)
+       return;
+
+    status = device->device.throttle (device);
+    if (unlikely (status))
+       _cairo_status_set_error (&device->base.status, status);
+}
+
+cairo_bool_t
+_cairo_drm_size_is_valid (cairo_device_t *abstract_device,
+                         int width, int height)
+{
+    cairo_drm_device_t *device = (cairo_drm_device_t *) abstract_device;
+
+    if (unlikely (device->base.status))
+       return FALSE;
+
+    return width  <= device->max_surface_size &&
+          height <= device->max_surface_size;
+}
diff --git a/src/skia/cairo-skia-context.cpp b/src/skia/cairo-skia-context.cpp
new file mode 100644 (file)
index 0000000..e5d4828
--- /dev/null
@@ -0,0 +1,1741 @@
+/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ * Copyright © 2005 Red Hat, Inc.
+ * Copyright © 2010 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *     Carl D. Worth <cworth@cworth.org>
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-private.h"
+#include "cairo-error-private.h"
+#include "cairo-arc-private.h"
+#include "cairo-backend-private.h"
+#include "cairo-default-context-private.h"
+#include "cairo-freed-pool-private.h"
+#include "cairo-gstate-private.h"
+#include "cairo-path-private.h"
+#include "cairo-pattern-private.h"
+#include "cairo-skia-private.h"
+#include "cairo-surface-backend-private.h"
+
+#include <SkShader.h>
+#include <SkColorShader.h>
+#include <SkGradientShader.h>
+#include <SkDashPathEffect.h>
+
+#if !defined(INFINITY)
+#define INFINITY HUGE_VAL
+#endif
+
+#if (CAIRO_FIXED_BITS == 32) && (CAIRO_FIXED_FRAC_BITS == 16) && defined(SK_SCALAR_IS_FIXED)
+# define CAIRO_FIXED_TO_SK_SCALAR(x)  (x)
+#elif defined(SK_SCALAR_IS_FIXED)
+/* This can be done better, but this will do for now */
+# define CAIRO_FIXED_TO_SK_SCALAR(x)  SkFloatToScalar(_cairo_fixed_to_double(x))
+#else
+# define CAIRO_FIXED_TO_SK_SCALAR(x)  SkFloatToScalar(_cairo_fixed_to_double(x))
+#endif
+
+#define UNSUPPORTED
+
+
+static freed_pool_t context_pool;
+
+static void
+_cairo_skia_context_destroy (void *abstract_cr)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    cr->path->reset ();
+    cr->paint->reset ();
+
+    delete cr->canvas;
+
+    cairo_surface_destroy (&cr->target->image.base);
+    cairo_surface_destroy (&cr->original->image.base);
+
+    if (cr->source != NULL) {
+       if (cr->source_image != NULL) {
+           _cairo_surface_release_source_image (cr->source, cr->source_image, cr->source_extra);
+           cr->source_image = NULL;
+       }
+       cairo_surface_destroy (cr->source);
+       cr->source = NULL;
+    }
+
+    _cairo_fini (&cr->base);
+
+    _freed_pool_put (&context_pool, cr);
+}
+
+static cairo_surface_t *
+_cairo_skia_context_get_original_target (void *abstract_cr)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    return &cr->original->image.base;
+}
+
+static cairo_surface_t *
+_cairo_skia_context_get_current_target (void *abstract_cr)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    return &cr->target->image.base;
+}
+
+static cairo_status_t
+_cairo_skia_context_save (void *abstract_cr)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    cr->canvas->save ();
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_restore (void *abstract_cr)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    cr->canvas->restore ();
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_push_group (void *abstract_cr, cairo_content_t content)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+    cairo_surface_t *group_surface;
+    cairo_status_t status;
+    int width, height;
+
+    //clip = _cairo_gstate_get_clip (cr->gstate);
+    width = cr->target->image.width;
+    height = cr->target->image.height;
+    group_surface = cr->target->image.base.backend->create_similar (&cr->target->image.base,
+                                                                   content, width, height);
+
+#if 0
+    /* Set device offsets on the new surface so that logically it appears at
+     * the same location on the parent surface -- when we pop_group this,
+     * the source pattern will get fixed up for the appropriate target surface
+     * device offsets, so we want to set our own surface offsets from /that/,
+     * and not from the device origin. */
+    cairo_surface_set_device_offset (group_surface,
+                                    parent_surface->device_transform.x0 - extents.x,
+                                    parent_surface->device_transform.y0 - extents.y);
+
+    /* If we have a current path, we need to adjust it to compensate for
+     * the device offset just applied. */
+    _cairo_path_fixed_transform (cr->path,
+                                &group_surface->device_transform);
+#endif
+
+    status = _cairo_skia_context_save (cr);
+    if (unlikely (status)) {
+       cairo_surface_destroy (group_surface);
+       return status;
+    }
+
+    cairo_surface_destroy (&cr->target->image.base);
+    cr->target = (cairo_skia_surface_t *) group_surface;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_pattern_t *
+_cairo_skia_context_pop_group (void *abstract_cr)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+    cairo_surface_t *group_surface;
+    cairo_pattern_t *group_pattern;
+    cairo_status_t status;
+
+    group_surface = cairo_surface_reference (&cr->target->image.base);
+
+    status = _cairo_skia_context_restore (cr);
+    if (unlikely (status)) {
+       group_pattern = _cairo_pattern_create_in_error (status);
+       goto done;
+    }
+
+    group_pattern = cairo_pattern_create_for_surface (group_surface);
+    status = group_pattern->status;
+    if (unlikely (status))
+        goto done;
+
+#if 0
+    _cairo_gstate_get_matrix (cr->gstate, &group_matrix);
+    /* Transform by group_matrix centered around device_transform so that when
+     * we call _cairo_gstate_copy_transformed_pattern the result is a pattern
+     * with a matrix equivalent to the device_transform of group_surface. */
+    if (_cairo_surface_has_device_transform (group_surface)) {
+       cairo_pattern_set_matrix (group_pattern, &group_surface->device_transform);
+       _cairo_pattern_transform (group_pattern, &group_matrix);
+       _cairo_pattern_transform (group_pattern, &group_surface->device_transform_inverse);
+    } else {
+       cairo_pattern_set_matrix (group_pattern, &group_matrix);
+    }
+
+    /* If we have a current path, we need to adjust it to compensate for
+     * the device offset just removed. */
+    _cairo_path_fixed_transform (cr->path,
+                                &group_surface->device_transform_inverse);
+#endif
+
+done:
+    cairo_surface_destroy (group_surface);
+
+    return group_pattern;
+}
+
+static inline cairo_surface_t *
+surface_from_pattern (const cairo_pattern_t *pattern)
+{
+    return (reinterpret_cast <const cairo_surface_pattern_t *> (pattern))->surface;
+}
+
+static inline bool
+surface_to_sk_bitmap (cairo_surface_t *surface, SkBitmap& bitmap)
+{
+    cairo_image_surface_t *img = (cairo_image_surface_t *) surface;
+    SkBitmap::Config config;
+    bool opaque;
+
+    if (unlikely (! format_to_sk_config (img->format, config, opaque)))
+       return false;
+
+    bitmap.reset ();
+    bitmap.setConfig (config, img->width, img->height, img->stride);
+    bitmap.setIsOpaque (opaque);
+    bitmap.setPixels (img->data);
+
+    return true;
+}
+
+static inline SkMatrix
+matrix_to_sk (const cairo_matrix_t& mat)
+{
+    SkMatrix skm;
+
+    skm.reset ();
+    skm.set (SkMatrix::kMScaleX, SkFloatToScalar (mat.xx));
+    skm.set (SkMatrix::kMSkewX,  SkFloatToScalar (mat.xy));
+    skm.set (SkMatrix::kMTransX, SkFloatToScalar (mat.x0));
+    skm.set (SkMatrix::kMSkewY,  SkFloatToScalar (mat.yx));
+    skm.set (SkMatrix::kMScaleY, SkFloatToScalar (mat.yy));
+    skm.set (SkMatrix::kMTransY, SkFloatToScalar (mat.y0));
+
+    /*
+    skm[6] = SkFloatToScalar (0.0);
+    skm[7] = SkFloatToScalar (0.0);
+    skm[8] = SkFloatToScalar (1.0); -- this isn't right, it wants a magic value in there that it'll set itself.  It wants Sk_Fract1 (2.30), not Sk_Scalar1
+    */
+
+    return skm;
+}
+
+static inline SkMatrix
+matrix_inverse_to_sk (const cairo_matrix_t& mat)
+{
+    cairo_matrix_t inv = mat;
+    cairo_status_t status = cairo_matrix_invert (&inv);
+    assert (status == CAIRO_STATUS_SUCCESS);
+    return matrix_to_sk (inv);
+}
+
+static SkShader::TileMode
+extend_to_sk (cairo_extend_t extend)
+{
+    static const SkShader::TileMode modeMap[] = {
+       SkShader::kClamp_TileMode,  // NONE behaves like PAD, because noone wants NONE
+       SkShader::kRepeat_TileMode,
+       SkShader::kMirror_TileMode,
+       SkShader::kClamp_TileMode
+    };
+
+    return modeMap[extend];
+}
+
+static inline SkColor
+color_to_sk (const cairo_color_t& c)
+{
+    /* Need unpremultiplied 1-byte values */
+    return SkColorSetARGB ((U8CPU) (c.alpha * 255),
+                          (U8CPU) (c.red * 255),
+                          (U8CPU) (c.green * 255),
+                          (U8CPU) (c.blue * 255));
+}
+
+static inline SkColor
+color_stop_to_sk (const cairo_color_stop_t& c)
+{
+    /* Need unpremultiplied 1-byte values */
+    return SkColorSetARGB ((U8CPU) (c.alpha * 255),
+                          (U8CPU) (c.red * 255),
+                          (U8CPU) (c.green * 255),
+                          (U8CPU) (c.blue * 255));
+}
+
+static SkShader*
+source_to_sk_shader (cairo_skia_context_t *cr,
+                    const cairo_pattern_t *pattern)
+{
+    SkShader *shader = NULL;
+
+    if (pattern->type == CAIRO_PATTERN_TYPE_SOLID) {
+       cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) pattern;
+       return new SkColorShader (color_to_sk (solid->color));
+    } else if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) {
+       cairo_surface_t *surface = surface_from_pattern (pattern);
+
+       cr->source = cairo_surface_reference (surface);
+
+       if (surface->type == CAIRO_SURFACE_TYPE_SKIA) {
+           cairo_skia_surface_t *esurf = (cairo_skia_surface_t *) surface;
+
+           shader = SkShader::CreateBitmapShader (*esurf->bitmap,
+                                                  extend_to_sk (pattern->extend),
+                                                  extend_to_sk (pattern->extend));
+       } else {
+           SkBitmap bitmap;
+
+           if (! _cairo_surface_is_image (surface)) {
+               cairo_status_t status;
+
+               status = _cairo_surface_acquire_source_image (surface,
+                                                             &cr->source_image,
+                                                             &cr->source_extra);
+               if (status)
+                   return NULL;
+
+               surface = &cr->source_image->base;
+           }
+
+           if (unlikely (! surface_to_sk_bitmap (surface, bitmap)))
+               return NULL;
+
+           shader = SkShader::CreateBitmapShader (bitmap,
+                                                  extend_to_sk (pattern->extend),
+                                                  extend_to_sk (pattern->extend));
+       }
+    } else if (pattern->type == CAIRO_PATTERN_TYPE_LINEAR
+              /* || pattern->type == CAIRO_PATTERN_TYPE_RADIAL */)
+    {
+       cairo_gradient_pattern_t *gradient = (cairo_gradient_pattern_t *) pattern;
+       SkColor colors_stack[10];
+       SkScalar pos_stack[10];
+       SkColor *colors = colors_stack;
+       SkScalar *pos = pos_stack;
+
+       if (gradient->n_stops > 10) {
+           colors = new SkColor[gradient->n_stops];
+           pos = new SkScalar[gradient->n_stops];
+       }
+
+       for (unsigned int i = 0; i < gradient->n_stops; i++) {
+           pos[i] = CAIRO_FIXED_TO_SK_SCALAR (gradient->stops[i].offset);
+           colors[i] = color_stop_to_sk (gradient->stops[i].color);
+       }
+
+       if (pattern->type == CAIRO_PATTERN_TYPE_LINEAR) {
+           cairo_linear_pattern_t *linear = (cairo_linear_pattern_t *) gradient;
+           SkPoint points[2];
+
+           points[0].set (SkFloatToScalar (linear->pd1.x),
+                          SkFloatToScalar (linear->pd1.y));
+           points[1].set (SkFloatToScalar (linear->pd2.x),
+                          SkFloatToScalar (linear->pd2.y));
+           shader = SkGradientShader::CreateLinear (points, colors, pos, gradient->n_stops,
+                                                    extend_to_sk (pattern->extend));
+       } else {
+           // XXX todo -- implement real radial shaders in Skia
+       }
+
+       if (gradient->n_stops > 10) {
+           delete [] colors;
+           delete [] pos;
+       }
+    }
+
+    if (shader && ! _cairo_matrix_is_identity (&pattern->matrix))
+       shader->setLocalMatrix (matrix_inverse_to_sk (pattern->matrix));
+
+    return shader;
+}
+
+static inline bool
+pattern_filter_to_sk (const cairo_pattern_t *pattern)
+{
+    switch (pattern->filter) {
+    case CAIRO_FILTER_GOOD:
+    case CAIRO_FILTER_BEST:
+    case CAIRO_FILTER_BILINEAR:
+    case CAIRO_FILTER_GAUSSIAN:
+       return true;
+    default:
+    case CAIRO_FILTER_FAST:
+    case CAIRO_FILTER_NEAREST:
+       return false;
+    }
+}
+
+static inline bool
+pattern_to_sk_color (const cairo_pattern_t *pattern, SkColor& color)
+{
+    if (pattern->type != CAIRO_PATTERN_TYPE_SOLID)
+       return false;
+
+    color = color_to_sk (((cairo_solid_pattern_t *) pattern)->color);
+    return true;
+}
+
+static cairo_status_t
+_cairo_skia_context_set_source (void *abstract_cr,
+                               cairo_pattern_t *source)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+    SkColor color;
+
+    if (cr->source != NULL) {
+       if (cr->source_image != NULL) {
+           _cairo_surface_release_source_image (cr->source, cr->source_image, cr->source_extra);
+           cr->source_image = NULL;
+       }
+       cairo_surface_destroy (cr->source);
+       cr->source = NULL;
+    }
+
+    if (pattern_to_sk_color (source, color)) {
+       cr->paint->setColor (color);
+    } else {
+       SkShader *shader = source_to_sk_shader (cr, source);
+       if (shader == NULL) {
+           UNSUPPORTED;
+           return CAIRO_STATUS_SUCCESS;
+       }
+
+       cr->paint->setShader (shader);
+       shader->unref ();
+
+       cr->paint->setFilterBitmap (pattern_filter_to_sk (source));
+    }
+
+    /* XXX change notification */
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_set_source_rgba (void *abstract_cr, double red, double green, double blue, double alpha)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    /* Need unpremultiplied 1-byte values */
+    cr->paint->setARGB ((U8CPU) (alpha * 255),
+                       (U8CPU) (red * 255),
+                       (U8CPU) (green * 255),
+                       (U8CPU) (blue * 255));
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_set_source_surface (void *abstract_cr,
+                                       cairo_surface_t *surface,
+                                       double     x,
+                                       double     y)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+    cairo_pattern_t *pattern;
+    cairo_matrix_t matrix;
+    cairo_status_t status;
+
+    if (surface->type == CAIRO_SURFACE_TYPE_SKIA) {
+       cairo_skia_surface_t *esurf = (cairo_skia_surface_t *) surface;
+       SkShader *shader;
+
+       shader = SkShader::CreateBitmapShader (*esurf->bitmap,
+                                              SkShader::kClamp_TileMode, /* XXX */
+                                              SkShader::kClamp_TileMode);
+
+       cr->paint->setShader (shader);
+       shader->unref ();
+
+       cr->paint->setFilterBitmap (true);
+
+       return CAIRO_STATUS_SUCCESS;
+    }
+
+    pattern = cairo_pattern_create_for_surface (surface);
+    if (unlikely (pattern->status))
+       return pattern->status;
+
+    cairo_matrix_init_translate (&matrix, -x, -y);
+    cairo_pattern_set_matrix (pattern, &matrix);
+
+    status = _cairo_skia_context_set_source (cr, pattern);
+    cairo_pattern_destroy (pattern);
+
+    return status;
+}
+
+static cairo_pattern_t *
+_cairo_skia_context_get_source (void *abstract_cr)
+{
+    //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    UNSUPPORTED;
+    return NULL;
+}
+
+static cairo_status_t
+_cairo_skia_context_set_tolerance (void *abstract_cr,
+                                  double tolerance)
+{
+    //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    /* XXX ignored */
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static inline SkXfermode::Mode
+operator_to_sk (cairo_operator_t op)
+{
+    static const SkXfermode::Mode modeMap[] = {
+       SkXfermode::kClear_Mode,
+
+       SkXfermode::kSrc_Mode,
+       SkXfermode::kSrcOver_Mode,
+       SkXfermode::kSrcIn_Mode,
+       SkXfermode::kSrcOut_Mode,
+       SkXfermode::kSrcATop_Mode,
+
+       SkXfermode::kDst_Mode,
+       SkXfermode::kDstOver_Mode,
+       SkXfermode::kDstIn_Mode,
+       SkXfermode::kDstOut_Mode,
+       SkXfermode::kDstATop_Mode,
+
+       SkXfermode::kXor_Mode,
+       SkXfermode::kPlus_Mode, // XXX Add?
+       SkXfermode::kPlus_Mode, // XXX SATURATE
+
+       SkXfermode::kPlus_Mode,
+       SkXfermode::kMultiply_Mode,
+       SkXfermode::kScreen_Mode,
+       SkXfermode::kOverlay_Mode,
+       SkXfermode::kDarken_Mode,
+       SkXfermode::kLighten_Mode,
+       SkXfermode::kColorDodge_Mode,
+       SkXfermode::kColorBurn_Mode,
+       SkXfermode::kHardLight_Mode,
+       SkXfermode::kSoftLight_Mode,
+       SkXfermode::kDifference_Mode,
+       SkXfermode::kExclusion_Mode,
+
+       SkXfermode::kSrcOver_Mode, // XXX: CAIRO_OPERATOR_HSL_HUE
+       SkXfermode::kSrcOver_Mode, // XXX: CAIRO_OPERATOR_HSL_SATURATION,
+       SkXfermode::kSrcOver_Mode, // XXX: CAIRO_OPERATOR_HSL_COLOR,
+       SkXfermode::kSrcOver_Mode, // XXX: CAIRO_OPERATOR_HSL_LUMINOSITY
+    };
+
+    return modeMap[op];
+}
+
+static cairo_status_t
+_cairo_skia_context_set_operator (void *abstract_cr, cairo_operator_t op)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    cr->paint->setXfermodeMode (operator_to_sk (op));
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_set_opacity (void *abstract_cr, double opacity)
+{
+    //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    /* XXX */
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_set_antialias (void *abstract_cr, cairo_antialias_t antialias)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    cr->paint->setAntiAlias (antialias != CAIRO_ANTIALIAS_NONE);
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_set_fill_rule (void *abstract_cr,
+                                  cairo_fill_rule_t fill_rule)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    cr->path->setFillType (fill_rule == CAIRO_FILL_RULE_WINDING ? SkPath::kWinding_FillType : SkPath::kEvenOdd_FillType);
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_set_line_width (void *abstract_cr,
+                                   double line_width)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    cr->paint->setStrokeWidth (SkFloatToScalar (line_width));
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_set_line_cap (void *abstract_cr,
+                                 cairo_line_cap_t line_cap)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+    static const SkPaint::Cap map[] = {
+       SkPaint::kButt_Cap,
+       SkPaint::kRound_Cap,
+       SkPaint::kSquare_Cap
+    };
+    cr->paint->setStrokeCap (map[line_cap]);
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_set_line_join (void *abstract_cr,
+                                  cairo_line_join_t line_join)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+    static const SkPaint::Join map[] = {
+       SkPaint::kMiter_Join,
+       SkPaint::kRound_Join,
+       SkPaint::kBevel_Join
+    };
+    cr->paint->setStrokeJoin (map[line_join]);
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_set_dash (void *abstract_cr,
+                             const double *dashes,
+                             int             num_dashes,
+                             double          offset)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+    SkScalar intervals_static[20];
+    SkScalar *intervals = intervals_static;
+
+    if (num_dashes == 0) {
+       cr->paint->setPathEffect (NULL);
+       return CAIRO_STATUS_SUCCESS;
+    }
+
+    int loop = 0;
+    if ((num_dashes & 1) != 0) {
+       loop = 1;
+       num_dashes <<= 1;
+    }
+
+    if (num_dashes > 20)
+       intervals = new SkScalar[num_dashes];
+
+    int i = 0;
+    do {
+       for (int j = 0; i < num_dashes; j++)
+           intervals[i++] = SkFloatToScalar (dashes[j]);
+    } while (loop--);
+
+    SkDashPathEffect *dash = new SkDashPathEffect (intervals, num_dashes, SkFloatToScalar (offset));
+
+    cr->paint->setPathEffect (dash);
+    dash->unref ();
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_set_miter_limit (void *abstract_cr, double limit)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    cr->paint->setStrokeMiter (SkFloatToScalar (limit));
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_antialias_t
+_cairo_skia_context_get_antialias (void *abstract_cr)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    return cr->paint->isAntiAlias () ? CAIRO_ANTIALIAS_DEFAULT : CAIRO_ANTIALIAS_NONE;
+}
+
+static void
+_cairo_skia_context_get_dash (void *abstract_cr,
+                             double *dashes,
+                             int *num_dashes,
+                             double *offset)
+{
+    //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    *num_dashes = 0;
+    /* XXX */
+}
+
+static cairo_fill_rule_t
+_cairo_skia_context_get_fill_rule (void *abstract_cr)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+    SkPath::FillType ft;
+
+    ft = cr->path->getFillType ();
+    if (ft == SkPath::kWinding_FillType)
+       return CAIRO_FILL_RULE_WINDING;
+    if (ft == SkPath::kEvenOdd_FillType)
+       return CAIRO_FILL_RULE_EVEN_ODD;;
+
+    UNSUPPORTED;
+    return CAIRO_FILL_RULE_WINDING;
+}
+
+static double
+_cairo_skia_context_get_line_width (void *abstract_cr)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    return /* ScalarToFloat */ cr->paint->getStrokeWidth ();
+}
+
+static cairo_line_cap_t
+_cairo_skia_context_get_line_cap (void *abstract_cr)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+    static const cairo_line_cap_t map[] = {
+       CAIRO_LINE_CAP_BUTT,
+       CAIRO_LINE_CAP_ROUND,
+       CAIRO_LINE_CAP_SQUARE
+    };
+    return map[cr->paint->getStrokeCap ()];
+}
+
+static cairo_line_join_t
+_cairo_skia_context_get_line_join (void *abstract_cr)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+    static const cairo_line_join_t map[] = {
+       CAIRO_LINE_JOIN_MITER,
+       CAIRO_LINE_JOIN_ROUND,
+       CAIRO_LINE_JOIN_BEVEL
+    };
+    return map[cr->paint->getStrokeJoin ()];
+}
+
+static double
+_cairo_skia_context_get_miter_limit (void *abstract_cr)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    return /* SkScalarToFloat */ cr->paint->getStrokeMiter ();
+}
+
+static cairo_operator_t
+_cairo_skia_context_get_operator (void *abstract_cr)
+{
+    //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    UNSUPPORTED;
+    //cr->paint->getXfermode ();
+    return CAIRO_OPERATOR_OVER;
+}
+
+static double
+_cairo_skia_context_get_opacity (void *abstract_cr)
+{
+    //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    UNSUPPORTED;
+    return 1.;
+}
+
+static double
+_cairo_skia_context_get_tolerance (void *abstract_cr)
+{
+    //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    /* XXX */
+    return CAIRO_GSTATE_TOLERANCE_DEFAULT;
+}
+
+
+/* Current tranformation matrix */
+
+static cairo_status_t
+_cairo_skia_context_translate (void *abstract_cr,
+                              double tx,
+                              double ty)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    cairo_matrix_translate (&cr->matrix, tx, ty);
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_scale (void *abstract_cr,
+                          double sx,
+                             double sy)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    cairo_matrix_scale (&cr->matrix, sx, sy);
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_rotate (void *abstract_cr,
+                           double theta)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    cairo_matrix_rotate (&cr->matrix, theta);
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_transform (void *abstract_cr,
+                              const cairo_matrix_t *matrix)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    cairo_matrix_multiply (&cr->matrix, &cr->matrix, matrix);
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_set_matrix (void *abstract_cr,
+                               const cairo_matrix_t *matrix)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    cr->matrix = *matrix;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_set_identity_matrix (void *abstract_cr)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    cairo_matrix_init_identity (&cr->matrix);
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_skia_context_get_matrix (void *abstract_cr,
+                               cairo_matrix_t *matrix)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    *matrix = cr->matrix;
+}
+
+static void
+_cairo_skia_context_user_to_device (void *abstract_cr,
+                                   double *x,
+                                   double *y)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    cairo_matrix_transform_point (&cr->matrix, x, y);
+}
+
+static void
+_cairo_skia_context_user_to_device_distance (void *abstract_cr,
+                                            double *dx,
+                                            double *dy)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    cairo_matrix_transform_distance (&cr->matrix, dx, dy);
+}
+
+static void
+_cairo_skia_context_device_to_user (void *abstract_cr,
+                                   double *x,
+                                   double *y)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+    cairo_matrix_t inverse;
+    cairo_status_t status;
+
+    inverse = cr->matrix;
+    status = cairo_matrix_invert (&inverse);
+    assert (CAIRO_STATUS_SUCCESS == status);
+
+    cairo_matrix_transform_point (&inverse, x, y);
+}
+
+static void
+_cairo_skia_context_device_to_user_distance (void *abstract_cr,
+                                            double *dx,
+                                            double *dy)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+    cairo_matrix_t inverse;
+    cairo_status_t status;
+
+    inverse = cr->matrix;
+    status = cairo_matrix_invert (&inverse);
+    assert (CAIRO_STATUS_SUCCESS == status);
+
+    cairo_matrix_transform_distance (&inverse, dx, dy);
+}
+
+/* Path constructor */
+
+static cairo_status_t
+_cairo_skia_context_new_path (void *abstract_cr)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    cr->path->reset ();
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_new_sub_path (void *abstract_cr)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    cr->path->rMoveTo (0, 0); /* XXX */
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+user_to_device_point (cairo_skia_context_t *cr, double *x, double *y)
+{
+    cairo_matrix_transform_point (&cr->matrix, x, y);
+    cairo_matrix_transform_point (&cr->target->image.base.device_transform, x, y);
+}
+
+static void
+user_to_device_distance (cairo_skia_context_t *cr, double *dx, double *dy)
+{
+    cairo_matrix_transform_distance (&cr->matrix, dx, dy);
+    cairo_matrix_transform_distance (&cr->target->image.base.device_transform, dx, dy);
+}
+
+static cairo_status_t
+_cairo_skia_context_move_to (void *abstract_cr, double x, double y)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    user_to_device_point (cr, &x, &y);
+    cr->path->moveTo (SkFloatToScalar (x), SkFloatToScalar (y));
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_line_to (void *abstract_cr, double x, double y)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    user_to_device_point (cr, &x, &y);
+    cr->path->lineTo (SkFloatToScalar (x), SkFloatToScalar (y));
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_curve_to (void *abstract_cr,
+                             double x1, double y1,
+                             double x2, double y2,
+                             double x3, double y3)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    user_to_device_point (cr, &x1, &y1);
+    user_to_device_point (cr, &x2, &y2);
+    user_to_device_point (cr, &x3, &y3);
+    cr->path->cubicTo (SkFloatToScalar (x1), SkFloatToScalar (y1),
+                      SkFloatToScalar (x2), SkFloatToScalar (y2),
+                      SkFloatToScalar (x3), SkFloatToScalar (y3));
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_arc_to (void *abstract_cr,
+                           double x1, double y1,
+                           double x2, double y2,
+                           double radius)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+#if 0
+    user_to_device_point (cr, &x1, &y1);
+    user_to_device_point (cr, &x2, &y2);
+    user_to_device_distance (cr, &radius, &radius);
+#endif
+
+    cr->path->arcTo (SkFloatToScalar (x1), SkFloatToScalar (y1),
+                    SkFloatToScalar (x2), SkFloatToScalar (y2),
+                    SkFloatToScalar (radius));
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_rel_move_to (void *abstract_cr, double dx, double dy)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    user_to_device_distance (cr, &dx, &dy);
+    cr->path->rMoveTo (SkFloatToScalar (dx), SkFloatToScalar (dy));
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_rel_line_to (void *abstract_cr, double dx, double dy)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    user_to_device_distance (cr, &dx, &dy);
+    cr->path->rLineTo (SkFloatToScalar (dx), SkFloatToScalar (dy));
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_rel_curve_to (void *abstract_cr,
+                                 double dx1, double dy1,
+                                 double dx2, double dy2,
+                                 double dx3, double dy3)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    user_to_device_distance (cr, &dx1, &dy1);
+    user_to_device_distance (cr, &dx2, &dy2);
+    user_to_device_distance (cr, &dx3, &dy3);
+    cr->path->rCubicTo (SkFloatToScalar (dx1), SkFloatToScalar (dy1),
+                       SkFloatToScalar (dx2), SkFloatToScalar (dy2),
+                       SkFloatToScalar (dx3), SkFloatToScalar (dy3));
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_rel_arc_to (void *abstract_cr,
+                               double dx1, double dy1,
+                               double dx2, double dy2,
+                               double radius)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+#if 0
+    user_to_device_point (cr, &x1, &y1);
+    user_to_device_point (cr, &x2, &y2);
+    user_to_device_distance (cr, &radius, &radius);
+#endif
+
+    cr->path->arcTo (SkFloatToScalar (dx1), SkFloatToScalar (dy1),
+                    SkFloatToScalar (dx2), SkFloatToScalar (dy2),
+                    SkFloatToScalar (radius));
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_close_path (void *abstract_cr)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    cr->path->close ();
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_rectangle (void *abstract_cr,
+                              double x, double y,
+                              double width, double height)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+    double x1, y1, x2, y2;
+
+    /* XXX assume no rotation! */
+    x1 = x, y1 = y;
+    user_to_device_point (cr, &x1, &y1);
+
+    x2 = x + width, y2 = y + height;
+    user_to_device_point (cr, &x2, &y2);
+
+    cr->path->addRect (SkFloatToScalar (x1), SkFloatToScalar (y1),
+                      SkFloatToScalar (x2), SkFloatToScalar (y2));
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_arc (void *abstract_cr,
+                        double xc, double yc, double radius,
+                        double angle1, double angle2,
+                        cairo_bool_t forward)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+    cairo_status_t status;
+
+    /* XXX cr->path->arc() */
+
+    /* Do nothing, successfully, if radius is <= 0 */
+    if (radius <= 0.0) {
+       status = _cairo_skia_context_line_to (cr, xc, yc);
+       if (unlikely (status))
+           return status;
+
+       status = _cairo_skia_context_line_to (cr, xc, yc);
+       if (unlikely (status))
+           return status;
+
+       return CAIRO_STATUS_SUCCESS;
+    }
+
+    status = _cairo_skia_context_line_to (cr,
+                                         xc + radius * cos (angle1),
+                                         yc + radius * sin (angle1));
+
+    if (unlikely (status))
+       return status;
+
+    if (forward)
+       _cairo_arc_path (&cr->base, xc, yc, radius, angle1, angle2);
+    else
+       _cairo_arc_path_negative (&cr->base, xc, yc, radius, angle1, angle2);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_skia_context_path_extents (void *abstract_cr,
+                                 double *x1,
+                                 double *y1,
+                                 double *x2,
+                                 double *y2)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+    SkRect rect;
+
+    rect = cr->path->getBounds ();
+
+    UNSUPPORTED;
+    /* XXX transform SkScalar rect to user */
+}
+
+static cairo_bool_t
+_cairo_skia_context_has_current_point (void *abstract_cr)
+{
+    //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    return TRUE;
+}
+
+static cairo_bool_t
+_cairo_skia_context_get_current_point (void *abstract_cr,
+                                      double *x,
+                                      double *y)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+    SkPoint pt;
+
+    cr->path->getLastPt (&pt);
+    //*x = SkScalarToFloat (pt.x);
+    //*y = SkScalarToFloat (pt.y);
+    //_cairo_gstate_backend_to_user (cr->gstate, x, y);
+
+    return TRUE;
+}
+
+static cairo_path_t *
+_cairo_skia_context_copy_path (void *abstract_cr)
+{
+    //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    /* XXX iterate */
+    UNSUPPORTED;
+    return NULL;
+}
+
+static cairo_path_t *
+_cairo_skia_context_copy_path_flat (void *abstract_cr)
+{
+    //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    /* XXX iterate and decompose */
+    UNSUPPORTED;
+    return NULL;
+}
+
+static cairo_status_t
+_cairo_skia_context_append_path (void *abstract_cr,
+                                const cairo_path_t *path)
+{
+    //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+   // return _cairo_path_append_to_context (path, cr);
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_stroke_to_path (void *abstract_cr)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    cr->paint->setStyle (SkPaint::kStroke_Style);
+    cr->paint->getFillPath (*cr->path, cr->path);
+    return CAIRO_STATUS_SUCCESS;
+}
+
+
+static cairo_status_t
+_cairo_skia_context_paint (void *abstract_cr)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+#if 0
+    if (cr->source != NULL) {
+       SkBitmap bitmap;
+       SkMatrix bitmapMatrix;
+
+       if (cr->source->type == CAIRO_SURFACE_TYPE_SKIA) {
+           cairo_skia_surface_t *esurf = (cairo_skia_surface_t *) cr->source->type;
+
+           bitmap = *esurf->bitmap;
+       } else {
+           surface_to_sk_bitmap (&cr->source_image->base, bitmap);
+       }
+
+       // XXX pattern->matrix, pattern->filter, pattern->extend
+       cr->canvas->drawBitmapMatrix (bitmap, bitmapMatrix, cr->paint);
+    } else {
+       cr->canvas->drawPaint (*cr->paint);
+    }
+#else
+    cr->canvas->drawPaint (*cr->paint);
+#endif
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_paint_with_alpha (void *abstract_cr,
+                                     double alpha)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    if (CAIRO_ALPHA_IS_OPAQUE (alpha))
+       return _cairo_skia_context_paint (cr);
+
+    /*XXX */
+    return _cairo_skia_context_paint (cr);
+}
+
+static cairo_status_t
+_cairo_skia_context_mask (void *abstract_cr,
+                         cairo_pattern_t *mask)
+{
+    //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    /* XXX */
+    //UNSUPPORTED;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_stroke_preserve (void *abstract_cr)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    cr->paint->setStyle (SkPaint::kStroke_Style);
+
+    /* XXX pen transformation? */
+    //assert (_cairo_matrix_is_identity (&cr->matrix));
+    cr->canvas->drawPath (*cr->path, *cr->paint);
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_stroke (void *abstract_cr)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+    cairo_status_t status;
+
+    status = _cairo_skia_context_stroke_preserve (cr);
+    if (unlikely (status))
+       return status;
+
+    return _cairo_skia_context_new_path (cr);
+}
+
+static cairo_status_t
+_cairo_skia_context_in_stroke (void *abstract_cr,
+                              double x, double y,
+                              cairo_bool_t *inside)
+{
+    //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    UNSUPPORTED;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_stroke_extents (void *abstract_cr,
+                                   double *x1, double *y1, double *x2, double *y2)
+{
+    //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    UNSUPPORTED;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_fill_preserve (void *abstract_cr)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    cr->paint->setStyle (SkPaint::kFill_Style);
+    cr->canvas->drawPath (*cr->path, *cr->paint);
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_fill (void *abstract_cr)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+    cairo_status_t status;
+
+    status = _cairo_skia_context_fill_preserve (cr);
+    if (unlikely (status))
+       return status;
+
+    return _cairo_skia_context_new_path (cr);
+}
+
+static cairo_status_t
+_cairo_skia_context_in_fill (void *abstract_cr,
+                               double x, double y,
+                               cairo_bool_t *inside)
+{
+    //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    UNSUPPORTED;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_fill_extents (void *abstract_cr,
+                                    double *x1, double *y1, double *x2, double *y2)
+{
+    //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    UNSUPPORTED;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_clip_preserve (void *abstract_cr)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    cr->canvas->clipPath (*cr->path);
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_clip (void *abstract_cr)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+    cairo_status_t status;
+
+    status = _cairo_skia_context_clip_preserve (cr);
+    if (unlikely (status))
+       return status;
+
+    return _cairo_skia_context_new_path (cr);
+}
+
+static cairo_status_t
+_cairo_skia_context_in_clip (void *abstract_cr,
+                            double x, double y,
+                            cairo_bool_t *inside)
+{
+    //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_reset_clip (void *abstract_cr)
+{
+    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+    SkRegion rgn(SkIRect::MakeWH (cr->target->bitmap->width (),
+                                 cr->target->bitmap->height ()));
+
+    cr->canvas->setClipRegion(rgn);
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_clip_extents (void *abstract_cr,
+                                 double *x1, double *y1,
+                                 double *x2, double *y2)
+{
+    //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    UNSUPPORTED;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_rectangle_list_t *
+_cairo_skia_context_copy_clip_rectangle_list (void *abstract_cr)
+{
+    //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    UNSUPPORTED;
+    return NULL;
+}
+
+static cairo_status_t
+_cairo_skia_context_copy_page (void *abstract_cr)
+{
+    //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    UNSUPPORTED;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_show_page (void *abstract_cr)
+{
+    //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+    UNSUPPORTED;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_set_font_face (void *abstract_cr,
+                                  cairo_font_face_t *font_face)
+{
+   // cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    //return _cairo_gstate_set_font_face (cr->gstate, font_face);
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_font_face_t *
+_cairo_skia_context_get_font_face (void *abstract_cr)
+{
+    //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    UNSUPPORTED;
+    return NULL;
+}
+
+static cairo_status_t
+_cairo_skia_context_font_extents (void *abstract_cr,
+                                 cairo_font_extents_t *extents)
+{
+    //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    UNSUPPORTED;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_set_font_size (void *abstract_cr,
+                                  double size)
+{
+    //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    UNSUPPORTED;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_set_font_matrix (void *abstract_cr,
+                                    const cairo_matrix_t *matrix)
+{
+    //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    UNSUPPORTED;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_skia_context_get_font_matrix (void *abstract_cr,
+                                    cairo_matrix_t *matrix)
+{
+    //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    UNSUPPORTED;
+}
+
+static cairo_status_t
+_cairo_skia_context_set_font_options (void *abstract_cr,
+                                     const cairo_font_options_t *options)
+{
+    //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    UNSUPPORTED;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_skia_context_get_font_options (void *abstract_cr,
+                                     cairo_font_options_t *options)
+{
+    //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    UNSUPPORTED;
+}
+
+static cairo_status_t
+_cairo_skia_context_set_scaled_font (void *abstract_cr,
+                                       cairo_scaled_font_t *scaled_font)
+{
+    //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    UNSUPPORTED;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_scaled_font_t *
+_cairo_skia_context_get_scaled_font (void *abstract_cr)
+{
+    //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    UNSUPPORTED;
+    return _cairo_scaled_font_create_in_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+}
+
+static cairo_status_t
+_cairo_skia_context_glyphs (void *abstract_cr,
+                           const cairo_glyph_t *glyphs,
+                           int num_glyphs,
+                           cairo_glyph_text_info_t *info)
+{
+    //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    /* XXX */
+    //UNSUPPORTED;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_glyph_path (void *abstract_cr,
+                               const cairo_glyph_t *glyphs,
+                               int num_glyphs)
+{
+    //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    UNSUPPORTED;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_glyph_extents (void                *abstract_cr,
+                                  const cairo_glyph_t    *glyphs,
+                                  int                    num_glyphs,
+                                  cairo_text_extents_t   *extents)
+{
+    //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+    UNSUPPORTED;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static const cairo_backend_t _cairo_skia_context_backend = {
+    CAIRO_TYPE_SKIA,
+    _cairo_skia_context_destroy,
+
+    _cairo_skia_context_get_original_target,
+    _cairo_skia_context_get_current_target,
+
+    _cairo_skia_context_save,
+    _cairo_skia_context_restore,
+
+    _cairo_skia_context_push_group,
+    _cairo_skia_context_pop_group,
+
+    _cairo_skia_context_set_source_rgba,
+    _cairo_skia_context_set_source_surface,
+    _cairo_skia_context_set_source,
+    _cairo_skia_context_get_source,
+
+    _cairo_skia_context_set_antialias,
+    _cairo_skia_context_set_dash,
+    _cairo_skia_context_set_fill_rule,
+    _cairo_skia_context_set_line_cap,
+    _cairo_skia_context_set_line_join,
+    _cairo_skia_context_set_line_width,
+    _cairo_skia_context_set_miter_limit,
+    _cairo_skia_context_set_opacity,
+    _cairo_skia_context_set_operator,
+    _cairo_skia_context_set_tolerance,
+    _cairo_skia_context_get_antialias,
+    _cairo_skia_context_get_dash,
+    _cairo_skia_context_get_fill_rule,
+    _cairo_skia_context_get_line_cap,
+    _cairo_skia_context_get_line_join,
+    _cairo_skia_context_get_line_width,
+    _cairo_skia_context_get_miter_limit,
+    _cairo_skia_context_get_opacity,
+    _cairo_skia_context_get_operator,
+    _cairo_skia_context_get_tolerance,
+
+    _cairo_skia_context_translate,
+    _cairo_skia_context_scale,
+    _cairo_skia_context_rotate,
+    _cairo_skia_context_transform,
+    _cairo_skia_context_set_matrix,
+    _cairo_skia_context_set_identity_matrix,
+    _cairo_skia_context_get_matrix,
+    _cairo_skia_context_user_to_device,
+    _cairo_skia_context_user_to_device_distance,
+    _cairo_skia_context_device_to_user,
+    _cairo_skia_context_device_to_user_distance,
+
+    _cairo_skia_context_new_path,
+    _cairo_skia_context_new_sub_path,
+    _cairo_skia_context_move_to,
+    _cairo_skia_context_rel_move_to,
+    _cairo_skia_context_line_to,
+    _cairo_skia_context_rel_line_to,
+    _cairo_skia_context_curve_to,
+    _cairo_skia_context_rel_curve_to,
+    _cairo_skia_context_arc_to,
+    _cairo_skia_context_rel_arc_to,
+    _cairo_skia_context_close_path,
+    _cairo_skia_context_arc,
+    _cairo_skia_context_rectangle,
+    _cairo_skia_context_path_extents,
+    _cairo_skia_context_has_current_point,
+    _cairo_skia_context_get_current_point,
+    _cairo_skia_context_copy_path,
+    _cairo_skia_context_copy_path_flat,
+    _cairo_skia_context_append_path,
+
+    _cairo_skia_stroke_to_path,
+
+    _cairo_skia_context_clip,
+    _cairo_skia_context_clip_preserve,
+    _cairo_skia_context_in_clip,
+    _cairo_skia_context_clip_extents,
+    _cairo_skia_context_reset_clip,
+    _cairo_skia_context_copy_clip_rectangle_list,
+
+    _cairo_skia_context_paint,
+    _cairo_skia_context_paint_with_alpha,
+    _cairo_skia_context_mask,
+
+    _cairo_skia_context_stroke,
+    _cairo_skia_context_stroke_preserve,
+    _cairo_skia_context_in_stroke,
+    _cairo_skia_context_stroke_extents,
+
+    _cairo_skia_context_fill,
+    _cairo_skia_context_fill_preserve,
+    _cairo_skia_context_in_fill,
+    _cairo_skia_context_fill_extents,
+
+    _cairo_skia_context_set_font_face,
+    _cairo_skia_context_get_font_face,
+    _cairo_skia_context_set_font_size,
+    _cairo_skia_context_set_font_matrix,
+    _cairo_skia_context_get_font_matrix,
+    _cairo_skia_context_set_font_options,
+    _cairo_skia_context_get_font_options,
+    _cairo_skia_context_set_scaled_font,
+    _cairo_skia_context_get_scaled_font,
+    _cairo_skia_context_font_extents,
+
+    _cairo_skia_context_glyphs,
+    _cairo_skia_context_glyph_path,
+    _cairo_skia_context_glyph_extents,
+
+    _cairo_skia_context_copy_page,
+    _cairo_skia_context_show_page,
+};
+
+cairo_t *
+_cairo_skia_context_create (void *target)
+{
+    cairo_skia_surface_t *surface = (cairo_skia_surface_t *) target;
+    cairo_skia_context_t *cr;
+
+    cr = (cairo_skia_context_t *) _freed_pool_get (&context_pool);
+    if (unlikely (cr == NULL)) {
+           cr = new cairo_skia_context_t;
+           if (unlikely (cr == NULL))
+               return _cairo_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+           cr->path = new SkPath;
+           cr->paint = new SkPaint;
+    }
+
+    _cairo_init (&cr->base, &_cairo_skia_context_backend);
+
+    cr->source = NULL;
+    cr->source_image = NULL;
+
+    cr->paint->setStrokeWidth (SkFloatToScalar (2.0));
+
+    cr->target = (cairo_skia_surface_t *) cairo_surface_reference ((cairo_surface_t *) target);
+    cr->original = (cairo_skia_surface_t *) cairo_surface_reference ((cairo_surface_t *) target);
+    cr->canvas = new SkCanvas (*surface->bitmap);
+    cr->canvas->save ();
+
+    cairo_matrix_init_identity (&cr->matrix);
+
+    return &cr->base;
+}
+
+#if 0
+void
+_cairo_skia_context_set_SkPaint (cairo_t *cr, SkPaint paint)
+{
+    *cr->paint = paint;
+}
+
+void
+_cairo_skia_context_set_SkPath (cairo_t *cr, SkPath path)
+{
+    *cr->path = path;
+}
+#endif
diff --git a/src/skia/cairo-skia-private.h b/src/skia/cairo-skia-private.h
new file mode 100644 (file)
index 0000000..cbd8c88
--- /dev/null
@@ -0,0 +1,110 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ *     Carl D. Worth <cworth@redhat.com>
+ */
+
+#ifndef CAIRO_SKIA_CONTEXT_PRIVATE_H
+#define CAIRO_SKIA_CONTEXT_PRIVATE_H
+
+#include "cairo-private.h"
+#include "cairo-image-surface-private.h"
+
+#include <SkBitmap.h>
+#include <SkCanvas.h>
+#include <SkPaint.h>
+#include <SkPath.h>
+
+typedef struct _cairo_skia_context cairo_skia_context_t;
+typedef struct _cairo_skia_surface cairo_skia_surface_t;
+
+struct _cairo_skia_context {
+    cairo_t base;
+
+    cairo_skia_surface_t *original;
+    cairo_skia_surface_t *target;
+
+    cairo_matrix_t matrix;
+
+    SkCanvas *canvas;
+    SkPaint *paint;
+    SkPath *path;
+
+    cairo_surface_t *source;
+    cairo_image_surface_t *source_image;
+    void *source_extra;
+};
+
+struct _cairo_skia_surface {
+    cairo_image_surface_t image;
+
+    SkBitmap *bitmap;
+};
+
+static inline bool
+format_to_sk_config (cairo_format_t format,
+                    SkBitmap::Config& config,
+                    bool& opaque)
+{
+    opaque = false;
+
+    switch (format) {
+    case CAIRO_FORMAT_ARGB32:
+       config = SkBitmap::kARGB_8888_Config;
+       break;
+    case CAIRO_FORMAT_RGB24:
+       config = SkBitmap::kARGB_8888_Config;
+       opaque = true;
+       break;
+    case CAIRO_FORMAT_RGB16_565:
+       config = SkBitmap::kRGB_565_Config;
+       opaque = true;
+       break;
+    case CAIRO_FORMAT_A8:
+       config = SkBitmap::kA8_Config;
+       break;
+    case CAIRO_FORMAT_A1:
+       config = SkBitmap::kA1_Config;
+       break;
+    case CAIRO_FORMAT_RGB30:
+    case CAIRO_FORMAT_INVALID:
+    default:
+       return false;
+    }
+
+    return true;
+}
+
+cairo_private cairo_t *
+_cairo_skia_context_create (void *target);
+
+#endif /* CAIRO_SKIA_CONTEXT_PRIVATE_H */
diff --git a/src/skia/cairo-skia-surface.cpp b/src/skia/cairo-skia-surface.cpp
new file mode 100644 (file)
index 0000000..de94f3a
--- /dev/null
@@ -0,0 +1,328 @@
+/* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2007 Mozilla Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Mozilla Corporation.
+ *
+ * Contributor(s):
+ *     Vladimir Vukicevic <vladimir@mozilla.com>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-skia.h"
+#include "cairo-skia-private.h"
+
+#include "cairo-composite-rectangles-private.h"
+#include "cairo-error-private.h"
+#include "cairo-surface-backend-private.h"
+#include "cairo-surface-fallback-private.h"
+
+static cairo_skia_surface_t *
+_cairo_skia_surface_create_internal (SkBitmap::Config config,
+                                    bool opaque,
+                                    unsigned char *data,
+                                    int width,
+                                    int height,
+                                    int stride);
+
+static cairo_surface_t *
+_cairo_skia_surface_create_similar (void *asurface,
+                                   cairo_content_t content,
+                                   int width,
+                                   int height)
+{
+    cairo_skia_surface_t *surface = (cairo_skia_surface_t *) asurface;
+    SkBitmap::Config config;
+    bool opaque;
+
+    if (content == surface->image.base.content)
+    {
+       config = surface->bitmap->getConfig ();
+       opaque = surface->bitmap->isOpaque ();
+    }
+    else if (! format_to_sk_config (_cairo_format_from_content (content),
+                                   config, opaque))
+    {
+       return NULL;
+    }
+
+    return &_cairo_skia_surface_create_internal (config, opaque,
+                                                NULL,
+                                                width, height,
+                                                0)->image.base;
+}
+
+static cairo_status_t
+_cairo_skia_surface_finish (void *asurface)
+{
+    cairo_skia_surface_t *surface = (cairo_skia_surface_t *) asurface;
+
+    cairo_surface_finish (&surface->image.base);
+    delete surface->bitmap;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_surface_t *
+_cairo_skia_surface_map_to_image (void *asurface,
+                                 const cairo_rectangle_int_t *extents)
+{
+    cairo_skia_surface_t *surface = (cairo_skia_surface_t *) asurface;
+
+    surface->bitmap->lockPixels ();
+
+    if (extents->width < surface->image.width ||
+       extents->height < surface->image.height)
+    {
+       return _cairo_surface_create_for_rectangle_int (&surface->image.base,
+                                                       extents);
+    }
+
+    return cairo_surface_reference (&surface->image.base);
+}
+
+static cairo_int_status_t
+_cairo_skia_surface_unmap_image (void *asurface,
+                                cairo_image_surface_t *image)
+{
+    cairo_skia_surface_t *surface = (cairo_skia_surface_t *) asurface;
+
+    surface->bitmap->unlockPixels ();
+    return CAIRO_INT_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_surface_acquire_source_image (void *asurface,
+                                         cairo_image_surface_t **image_out,
+                                         void **image_extra)
+{
+    cairo_skia_surface_t *surface = (cairo_skia_surface_t *) asurface;
+
+    surface->bitmap->lockPixels ();
+
+    *image_out = &surface->image;
+    *image_extra = NULL;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_skia_surface_release_source_image (void *asurface,
+                                         cairo_image_surface_t *image,
+                                         void *image_extra)
+{
+    cairo_skia_surface_t *surface = (cairo_skia_surface_t *) asurface;
+
+    surface->bitmap->unlockPixels ();
+}
+
+static cairo_bool_t
+_cairo_skia_surface_get_extents (void *asurface,
+                                 cairo_rectangle_int_t *extents)
+{
+    cairo_skia_surface_t *surface = (cairo_skia_surface_t *) asurface;
+    extents->x = extents->y = 0;
+    extents->width  = surface->image.width;
+    extents->height = surface->image.height;
+    return TRUE;
+}
+
+static void
+_cairo_skia_surface_get_font_options (void                  *abstract_surface,
+                                      cairo_font_options_t  *options)
+{
+    _cairo_font_options_init_default (options);
+
+    cairo_font_options_set_hint_metrics (options, CAIRO_HINT_METRICS_ON);
+    _cairo_font_options_set_round_glyph_positions (options, CAIRO_ROUND_GLYPH_POS_ON);
+}
+
+static const struct _cairo_surface_backend
+cairo_skia_surface_backend = {
+    CAIRO_SURFACE_TYPE_SKIA,
+    _cairo_skia_surface_finish,
+
+    _cairo_skia_context_create,
+
+    _cairo_skia_surface_create_similar,
+    NULL, //_cairo_skia_surface_create_similar_image,
+    _cairo_skia_surface_map_to_image,
+    _cairo_skia_surface_unmap_image,
+
+    _cairo_skia_surface_acquire_source_image,
+    _cairo_skia_surface_release_source_image,
+    NULL, /* snapshot */
+
+    NULL, /* copy_page */
+    NULL, /* show_page */
+
+    _cairo_skia_surface_get_extents,
+    _cairo_skia_surface_get_font_options,
+
+    NULL, /* flush */
+    NULL, /* mark_dirty_rectangle */
+
+    /* XXX native surface functions? */
+    _cairo_surface_fallback_paint,
+    _cairo_surface_fallback_mask,
+    _cairo_surface_fallback_stroke,
+    _cairo_surface_fallback_fill,
+    NULL, /* fill/stroke */
+    _cairo_surface_fallback_glyphs
+};
+
+/*
+ * Surface constructors
+ */
+
+static inline pixman_format_code_t
+sk_config_to_pixman_format_code (SkBitmap::Config config,
+                                bool opaque)
+{
+    switch (config) {
+    case SkBitmap::kARGB_8888_Config:
+       return opaque ? PIXMAN_x8r8g8b8 : PIXMAN_a8r8g8b8;
+
+    case SkBitmap::kA8_Config:
+       return PIXMAN_a8;
+
+    case SkBitmap::kA1_Config:
+       return PIXMAN_a1;
+    case SkBitmap::kRGB_565_Config:
+       return PIXMAN_r5g6b5;
+    case SkBitmap::kARGB_4444_Config:
+       return PIXMAN_a4r4g4b4;
+
+    case SkBitmap::kNo_Config:
+    case SkBitmap::kIndex8_Config:
+    case SkBitmap::kRLE_Index8_Config:
+    case SkBitmap::kConfigCount:
+    default:
+       ASSERT_NOT_REACHED;
+       return (pixman_format_code_t) -1;
+    }
+}
+
+static cairo_skia_surface_t *
+_cairo_skia_surface_create_internal (SkBitmap::Config config,
+                                    bool opaque,
+                                    unsigned char *data,
+                                    int width,
+                                    int height,
+                                    int stride)
+{
+    cairo_skia_surface_t *surface;
+    pixman_image_t *pixman_image;
+    pixman_format_code_t pixman_format;
+
+    surface = (cairo_skia_surface_t *) malloc (sizeof (cairo_skia_surface_t));
+    if (unlikely (surface == NULL))
+       return (cairo_skia_surface_t *) _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+    pixman_format = sk_config_to_pixman_format_code (config, opaque);
+    pixman_image = pixman_image_create_bits (pixman_format,
+                                            width, height,
+                                            (uint32_t *) data, stride);
+    if (unlikely (pixman_image == NULL))
+       return (cairo_skia_surface_t *) _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+    _cairo_surface_init (&surface->image.base,
+                        &cairo_skia_surface_backend,
+                        NULL, /* device */
+                        _cairo_content_from_pixman_format (pixman_format));
+
+    _cairo_image_surface_init (&surface->image, pixman_image, pixman_format);
+
+    surface->bitmap = new SkBitmap;
+    surface->bitmap->setConfig (config, width, height, surface->image.stride);
+    surface->bitmap->setIsOpaque (opaque);
+    surface->bitmap->setPixels (surface->image.data);
+
+    surface->image.base.is_clear = data == NULL;
+
+    return surface;
+}
+
+cairo_surface_t *
+cairo_skia_surface_create (cairo_format_t format,
+                          int width,
+                          int height)
+{
+    SkBitmap::Config config;
+    bool opaque;
+
+    if (! CAIRO_FORMAT_VALID (format) ||
+       ! format_to_sk_config (format, config, opaque))
+    {
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
+    }
+
+    return &_cairo_skia_surface_create_internal (config, opaque, NULL, width, height, 0)->image.base;
+}
+
+cairo_surface_t *
+cairo_skia_surface_create_for_data (unsigned char *data,
+                                   cairo_format_t format,
+                                   int width,
+                                   int height,
+                                   int stride)
+{
+    SkBitmap::Config config;
+    bool opaque;
+
+    if (! CAIRO_FORMAT_VALID (format) ||
+       ! format_to_sk_config (format, config, opaque))
+    {
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
+    }
+
+    return &_cairo_skia_surface_create_internal (config, opaque, data, width, height, stride)->image.base;
+}
+
+/***
+
+Todo:
+
+*** Skia:
+
+- mask()
+
+*** Sk:
+
+High:
+- antialiased clipping?
+
+Medium:
+- implement clip path reset (to avoid restore/save)
+- implement complex radial patterns (2 centers and 2 radii)
+
+Low:
+- implement EXTEND_NONE
+
+***/
diff --git a/src/test-base-compositor-surface.c b/src/test-base-compositor-surface.c
new file mode 100644 (file)
index 0000000..21032b0
--- /dev/null
@@ -0,0 +1,900 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ * Copyright © 2005 Red Hat, Inc.
+ * Copyright © 2011 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *     Carl D. Worth <cworth@cworth.org>
+ *      Joonas Pihlaja <jpihlaja@cc.helsinki.fi>
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+
+#include "test-compositor-surface-private.h"
+
+#include "cairo-clip-private.h"
+#include "cairo-composite-rectangles-private.h"
+#include "cairo-compositor-private.h"
+#include "cairo-error-private.h"
+#include "cairo-image-surface-private.h"
+#include "cairo-region-private.h"
+#include "cairo-traps-private.h"
+
+/* The intention is that this is a surface that just works, and most
+ * important of all does not try to be clever!
+ */
+
+typedef cairo_int_status_t
+(*draw_func_t) (cairo_image_surface_t          *dst,
+               void                            *closure,
+               cairo_operator_t                 op,
+               const cairo_pattern_t           *pattern,
+               int                              dst_x,
+               int                              dst_y,
+               const cairo_rectangle_int_t     *extents);
+
+static pixman_op_t
+_pixman_operator (cairo_operator_t op)
+{
+    switch ((int) op) {
+    case CAIRO_OPERATOR_CLEAR:
+       return PIXMAN_OP_CLEAR;
+
+    case CAIRO_OPERATOR_SOURCE:
+       return PIXMAN_OP_SRC;
+    case CAIRO_OPERATOR_OVER:
+       return PIXMAN_OP_OVER;
+    case CAIRO_OPERATOR_IN:
+       return PIXMAN_OP_IN;
+    case CAIRO_OPERATOR_OUT:
+       return PIXMAN_OP_OUT;
+    case CAIRO_OPERATOR_ATOP:
+       return PIXMAN_OP_ATOP;
+
+    case CAIRO_OPERATOR_DEST:
+       return PIXMAN_OP_DST;
+    case CAIRO_OPERATOR_DEST_OVER:
+       return PIXMAN_OP_OVER_REVERSE;
+    case CAIRO_OPERATOR_DEST_IN:
+       return PIXMAN_OP_IN_REVERSE;
+    case CAIRO_OPERATOR_DEST_OUT:
+       return PIXMAN_OP_OUT_REVERSE;
+    case CAIRO_OPERATOR_DEST_ATOP:
+       return PIXMAN_OP_ATOP_REVERSE;
+
+    case CAIRO_OPERATOR_XOR:
+       return PIXMAN_OP_XOR;
+    case CAIRO_OPERATOR_ADD:
+       return PIXMAN_OP_ADD;
+    case CAIRO_OPERATOR_SATURATE:
+       return PIXMAN_OP_SATURATE;
+
+    case CAIRO_OPERATOR_MULTIPLY:
+       return PIXMAN_OP_MULTIPLY;
+    case CAIRO_OPERATOR_SCREEN:
+       return PIXMAN_OP_SCREEN;
+    case CAIRO_OPERATOR_OVERLAY:
+       return PIXMAN_OP_OVERLAY;
+    case CAIRO_OPERATOR_DARKEN:
+       return PIXMAN_OP_DARKEN;
+    case CAIRO_OPERATOR_LIGHTEN:
+       return PIXMAN_OP_LIGHTEN;
+    case CAIRO_OPERATOR_COLOR_DODGE:
+       return PIXMAN_OP_COLOR_DODGE;
+    case CAIRO_OPERATOR_COLOR_BURN:
+       return PIXMAN_OP_COLOR_BURN;
+    case CAIRO_OPERATOR_HARD_LIGHT:
+       return PIXMAN_OP_HARD_LIGHT;
+    case CAIRO_OPERATOR_SOFT_LIGHT:
+       return PIXMAN_OP_SOFT_LIGHT;
+    case CAIRO_OPERATOR_DIFFERENCE:
+       return PIXMAN_OP_DIFFERENCE;
+    case CAIRO_OPERATOR_EXCLUSION:
+       return PIXMAN_OP_EXCLUSION;
+    case CAIRO_OPERATOR_HSL_HUE:
+       return PIXMAN_OP_HSL_HUE;
+    case CAIRO_OPERATOR_HSL_SATURATION:
+       return PIXMAN_OP_HSL_SATURATION;
+    case CAIRO_OPERATOR_HSL_COLOR:
+       return PIXMAN_OP_HSL_COLOR;
+    case CAIRO_OPERATOR_HSL_LUMINOSITY:
+       return PIXMAN_OP_HSL_LUMINOSITY;
+
+    default:
+       ASSERT_NOT_REACHED;
+       return PIXMAN_OP_OVER;
+    }
+}
+
+static void
+_pixman_image_add_boxes (pixman_image_t *image,
+                        int dst_x, int dst_y,
+                        cairo_box_t *box,
+                        int count)
+{
+    while (count--) {
+       pixman_trapezoid_t trap;
+
+       trap.top = _cairo_fixed_to_16_16 (box->p1.y);
+       trap.bottom = _cairo_fixed_to_16_16 (box->p2.y);
+
+       trap.left.p1.x = _cairo_fixed_to_16_16 (box->p1.x);
+       trap.left.p1.y = _cairo_fixed_to_16_16 (box->p1.y);
+       trap.left.p2.x = _cairo_fixed_to_16_16 (box->p1.x);
+       trap.left.p2.y = _cairo_fixed_to_16_16 (box->p2.y);
+
+       trap.right.p1.x = _cairo_fixed_to_16_16 (box->p2.x);
+       trap.right.p1.y = _cairo_fixed_to_16_16 (box->p1.y);
+       trap.right.p2.x = _cairo_fixed_to_16_16 (box->p2.x);
+       trap.right.p2.y = _cairo_fixed_to_16_16 (box->p2.y);
+
+       pixman_rasterize_trapezoid (image, &trap, -dst_x, -dst_y);
+       box++;
+    }
+}
+
+static cairo_status_t
+combine_in_boxes (cairo_image_surface_t *dst,
+                 cairo_box_t *box, int count,
+                 const cairo_rectangle_int_t *extents)
+{
+    pixman_image_t *mask;
+
+    mask = pixman_image_create_bits (PIXMAN_a8,
+                                    extents->width, extents->height,
+                                    NULL, 0);
+    if (unlikely (mask == NULL))
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    _pixman_image_add_boxes (mask, extents->x, extents->y, box, count);
+    pixman_image_composite32 (PIXMAN_OP_IN,
+                              mask, NULL, dst->pixman_image,
+                             0, 0,
+                              0, 0,
+                              0, 0,
+                              extents->width, extents->height);
+    pixman_image_unref (mask);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_image_surface_t *
+get_clip_surface (cairo_image_surface_t        *dst,
+                 const cairo_composite_rectangles_t *extents,
+                 int *clip_x,
+                 int *clip_y)
+{
+    cairo_image_surface_t *surface;
+    cairo_int_status_t status;
+
+    surface = (cairo_image_surface_t *)
+       _cairo_surface_create_similar_solid (&dst->base,
+                                            CAIRO_CONTENT_ALPHA,
+                                            extents->unbounded.width,
+                                            extents->unbounded.height,
+                                            CAIRO_COLOR_WHITE);
+    if (unlikely (surface->base.status))
+       return surface;
+
+    if (extents->clip->boxes) {
+       status = combine_in_boxes (surface,
+                                  extents->clip->boxes,
+                                  extents->clip->num_boxes,
+                                  &extents->unbounded);
+       if (unlikely (status))
+           goto error;
+    }
+
+    if (extents->clip->path) {
+       status = _cairo_clip_combine_with_surface (extents->clip,
+                                                  &surface->base,
+                                                  extents->unbounded.x,
+                                                  extents->unbounded.y);
+       if (unlikely (status))
+           goto error;
+    }
+
+    *clip_x = extents->unbounded.x;
+    *clip_y = extents->unbounded.y;
+    return surface;
+
+error:
+    cairo_surface_destroy (&surface->base);
+    return (cairo_image_surface_t *)_cairo_surface_create_in_error (status);
+}
+
+static cairo_image_surface_t *
+create_composite_mask (cairo_image_surface_t   *dst,
+                      void                     *draw_closure,
+                      draw_func_t               draw_func,
+                      const cairo_composite_rectangles_t *extents)
+{
+    cairo_image_surface_t *surface;
+    cairo_int_status_t status;
+
+    surface = (cairo_image_surface_t *)
+       _cairo_surface_create_similar_solid (&dst->base,
+                                            CAIRO_CONTENT_ALPHA,
+                                            extents->bounded.width,
+                                            extents->bounded.height,
+                                            CAIRO_COLOR_TRANSPARENT);
+    if (unlikely (surface->base.status))
+       return surface;
+
+    status = draw_func (surface, draw_closure,
+                       CAIRO_OPERATOR_ADD, &_cairo_pattern_white.base,
+                       extents->bounded.x, extents->bounded.y,
+                       &extents->bounded);
+    if (unlikely (status))
+       goto error;
+
+    if (extents->clip->boxes) {
+       status = combine_in_boxes (surface,
+                                  extents->clip->boxes,
+                                  extents->clip->num_boxes,
+                                  &extents->bounded);
+       if (unlikely (status))
+           goto error;
+    }
+
+    if (extents->clip->path) {
+       status = _cairo_clip_combine_with_surface (extents->clip,
+                                                  &surface->base,
+                                                  extents->bounded.x,
+                                                  extents->bounded.y);
+       if (unlikely (status))
+           goto error;
+    }
+
+    return surface;
+
+error:
+    cairo_surface_destroy (&surface->base);
+    return (cairo_image_surface_t *)_cairo_surface_create_in_error (status);
+}
+
+/* Handles compositing with a clip surface when the operator allows
+ * us to combine the clip with the mask
+ */
+static cairo_status_t
+clip_and_composite_with_mask (const cairo_composite_rectangles_t*extents,
+                             draw_func_t                draw_func,
+                             void                      *draw_closure)
+{
+    cairo_image_surface_t *dst = (cairo_image_surface_t *)extents->surface;
+    cairo_image_surface_t *mask;
+    pixman_image_t *src;
+    cairo_status_t status = CAIRO_STATUS_SUCCESS;
+    int src_x, src_y;
+
+    mask = create_composite_mask (dst, draw_closure, draw_func, extents);
+    if (unlikely (mask->base.status))
+       return mask->base.status;
+
+    src = _pixman_image_for_pattern (dst,
+                                    &extents->source_pattern.base, FALSE,
+                                    &extents->bounded,
+                                    &extents->source_sample_area,
+                                    &src_x, &src_y);
+    if (unlikely (src == NULL)) {
+       status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+       goto error;
+    }
+
+    pixman_image_composite32 (_pixman_operator (extents->op),
+                             src, mask->pixman_image, dst->pixman_image,
+                             extents->bounded.x + src_x,
+                             extents->bounded.y + src_y,
+                             0, 0,
+                             extents->bounded.x,      extents->bounded.y,
+                             extents->bounded.width,  extents->bounded.height);
+
+    pixman_image_unref (src);
+error:
+    cairo_surface_destroy (&mask->base);
+    return status;
+}
+
+/* Handles compositing with a clip surface when we have to do the operation
+ * in two pieces and combine them together.
+ */
+static cairo_status_t
+clip_and_composite_combine (const cairo_composite_rectangles_t*extents,
+                           draw_func_t                  draw_func,
+                           void                        *draw_closure)
+{
+    cairo_image_surface_t *dst = (cairo_image_surface_t *)extents->surface;
+    cairo_image_surface_t *tmp, *clip;
+    int clip_x, clip_y;
+    cairo_status_t status;
+
+    tmp = (cairo_image_surface_t *)
+       _cairo_surface_create_similar_solid (&dst->base, dst->base.content,
+                                            extents->bounded.width,
+                                            extents->bounded.height,
+                                            CAIRO_COLOR_TRANSPARENT);
+    if (unlikely (tmp->base.status))
+       return tmp->base.status;
+
+    pixman_image_composite32 (PIXMAN_OP_SRC,
+                             dst->pixman_image, NULL, tmp->pixman_image,
+                             extents->bounded.x,      extents->bounded.y,
+                             0, 0,
+                             0, 0,
+                             extents->bounded.width,  extents->bounded.height);
+
+    status = draw_func (tmp, draw_closure,
+                       extents->op, &extents->source_pattern.base,
+                       extents->bounded.x, extents->bounded.y,
+                       &extents->bounded);
+    if (unlikely (status))
+       goto error;
+
+    clip = (cairo_image_surface_t *)
+       get_clip_surface (dst, extents, &clip_x, &clip_y);
+    if (unlikely (clip->base.status))
+       goto error;
+
+    pixman_image_composite32 (PIXMAN_OP_OUT_REVERSE,
+                             clip->pixman_image, NULL, dst->pixman_image,
+                             extents->bounded.x - clip_x, extents->bounded.y - clip_y,
+                             0,      0,
+                             extents->bounded.x, extents->bounded.y,
+                             extents->bounded.width, extents->bounded.height);
+    pixman_image_composite32 (PIXMAN_OP_ADD,
+                             tmp->pixman_image, clip->pixman_image, dst->pixman_image,
+                             0,  0,
+                             extents->bounded.x - clip_x, extents->bounded.y - clip_y,
+                             extents->bounded.x, extents->bounded.y,
+                             extents->bounded.width, extents->bounded.height);
+
+    cairo_surface_destroy (&clip->base);
+
+ error:
+    cairo_surface_destroy (&tmp->base);
+
+    return status;
+}
+
+/* Handles compositing for %CAIRO_OPERATOR_SOURCE, which is special; it's
+ * defined as (src IN mask IN clip) ADD (dst OUT (mask IN clip))
+ */
+static cairo_status_t
+clip_and_composite_source (const cairo_composite_rectangles_t  *extents,
+                          draw_func_t                           draw_func,
+                          void                                 *draw_closure)
+{
+    cairo_image_surface_t *dst = (cairo_image_surface_t *)extents->surface;
+    cairo_image_surface_t *mask;
+    pixman_image_t *src;
+    int src_x, src_y;
+    cairo_status_t status = CAIRO_STATUS_SUCCESS;
+
+    mask = create_composite_mask (dst, draw_closure, draw_func, extents);
+    if (unlikely (mask->base.status))
+       return mask->base.status;
+
+    pixman_image_composite32 (PIXMAN_OP_OUT_REVERSE,
+                             mask->pixman_image, NULL, dst->pixman_image,
+                             0,      0,
+                             0,      0,
+                             extents->bounded.x, extents->bounded.y,
+                             extents->bounded.width, extents->bounded.height);
+
+    src = _pixman_image_for_pattern (dst,
+                                    &extents->source_pattern.base, FALSE,
+                                    &extents->bounded,
+                                    &extents->source_sample_area,
+                                    &src_x, &src_y);
+    if (unlikely (src == NULL)) {
+       status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+       goto error;
+    }
+
+    pixman_image_composite32 (PIXMAN_OP_ADD,
+                             src, mask->pixman_image, dst->pixman_image,
+                             extents->bounded.x + src_x,  extents->bounded.y + src_y,
+                             0, 0,
+                             extents->bounded.x, extents->bounded.y,
+                             extents->bounded.width, extents->bounded.height);
+
+    pixman_image_unref (src);
+
+error:
+    cairo_surface_destroy (&mask->base);
+    return status;
+}
+
+static cairo_status_t
+fixup_unbounded (const cairo_composite_rectangles_t *extents)
+{
+    cairo_image_surface_t *dst = (cairo_image_surface_t *)extents->surface;
+    pixman_image_t *mask;
+    int mask_x, mask_y;
+
+    if (! _cairo_clip_is_region (extents->clip)) {
+       cairo_image_surface_t *clip;
+
+       clip = get_clip_surface (dst, extents, &mask_x, &mask_y);
+       if (unlikely (clip->base.status))
+           return clip->base.status;
+
+       mask = pixman_image_ref (clip->pixman_image);
+       cairo_surface_destroy (&clip->base);
+    } else {
+       mask_x = mask_y = 0;
+       mask = _pixman_image_for_color (CAIRO_COLOR_WHITE);
+       if (unlikely (mask == NULL))
+           return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+    }
+
+    /* top */
+    if (extents->bounded.y != extents->unbounded.y) {
+       int x = extents->unbounded.x;
+       int y = extents->unbounded.y;
+       int width = extents->unbounded.width;
+       int height = extents->bounded.y - y;
+
+       pixman_image_composite32 (PIXMAN_OP_OUT_REVERSE,
+                                 mask, NULL, dst->pixman_image,
+                                 x - mask_x, y - mask_y,
+                                 0, 0,
+                                 x, y,
+                                 width, height);
+    }
+
+    /* left */
+    if (extents->bounded.x != extents->unbounded.x) {
+       int x = extents->unbounded.x;
+       int y = extents->bounded.y;
+       int width = extents->bounded.x - x;
+       int height = extents->bounded.height;
+
+       pixman_image_composite32 (PIXMAN_OP_OUT_REVERSE,
+                                 mask, NULL, dst->pixman_image,
+                                 x - mask_x, y - mask_y,
+                                 0, 0,
+                                 x, y,
+                                 width, height);
+    }
+
+    /* right */
+    if (extents->bounded.x + extents->bounded.width != extents->unbounded.x + extents->unbounded.width) {
+       int x = extents->bounded.x + extents->bounded.width;
+       int y = extents->bounded.y;
+       int width = extents->unbounded.x + extents->unbounded.width - x;
+       int height = extents->bounded.height;
+
+       pixman_image_composite32 (PIXMAN_OP_OUT_REVERSE,
+                                 mask, NULL, dst->pixman_image,
+                                 x - mask_x, y - mask_y,
+                                 0, 0,
+                                 x, y,
+                                 width, height);
+    }
+
+    /* bottom */
+    if (extents->bounded.y + extents->bounded.height != extents->unbounded.y + extents->unbounded.height) {
+       int x = extents->unbounded.x;
+       int y = extents->bounded.y + extents->bounded.height;
+       int width = extents->unbounded.width;
+       int height = extents->unbounded.y + extents->unbounded.height - y;
+
+       pixman_image_composite32 (PIXMAN_OP_OUT_REVERSE,
+                                 mask, NULL, dst->pixman_image,
+                                 x - mask_x, y - mask_y,
+                                 0, 0,
+                                 x, y,
+                                 width, height);
+    }
+
+    pixman_image_unref (mask);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+set_clip_region (cairo_composite_rectangles_t *extents)
+{
+    cairo_image_surface_t *dst = (cairo_image_surface_t *) extents->surface;
+    cairo_region_t *region = _cairo_clip_get_region (extents->clip);
+    pixman_region32_t *rgn = region ? &region->rgn : NULL;
+    if (! pixman_image_set_clip_region32 (dst->pixman_image, rgn))
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+clip_and_composite (cairo_composite_rectangles_t *extents,
+                   draw_func_t          draw_func,
+                   void                *draw_closure)
+{
+    cairo_status_t status;
+
+    status = set_clip_region (extents);
+    if (unlikely (status))
+       return status;
+
+    if (extents->op == CAIRO_OPERATOR_SOURCE) {
+       status = clip_and_composite_source (extents, draw_func, draw_closure);
+    } else {
+       if (extents->op == CAIRO_OPERATOR_CLEAR) {
+           extents->source_pattern.solid = _cairo_pattern_white;
+           extents->op = CAIRO_OPERATOR_DEST_OUT;
+       }
+       if (! _cairo_clip_is_region (extents->clip)) {
+           if (extents->is_bounded)
+               status = clip_and_composite_with_mask (extents, draw_func, draw_closure);
+           else
+               status = clip_and_composite_combine (extents, draw_func, draw_closure);
+       } else {
+           status = draw_func ((cairo_image_surface_t *) extents->surface,
+                               draw_closure,
+                               extents->op,
+                               &extents->source_pattern.base,
+                               0, 0,
+                               &extents->bounded);
+       }
+    }
+
+    if (status == CAIRO_STATUS_SUCCESS && ! extents->is_bounded)
+       status = fixup_unbounded (extents);
+
+    return status;
+}
+
+/* high-level compositor interface */
+
+static cairo_int_status_t
+composite_paint (cairo_image_surface_t         *dst,
+                void                           *closure,
+                cairo_operator_t                op,
+                const cairo_pattern_t          *pattern,
+                int                             dst_x,
+                int                             dst_y,
+                const cairo_rectangle_int_t    *extents)
+{
+    cairo_rectangle_int_t sample;
+    pixman_image_t *src;
+    int src_x, src_y;
+
+    _cairo_pattern_sampled_area (pattern, extents, &sample);
+    src = _pixman_image_for_pattern (dst,
+                                    pattern, FALSE,
+                                    extents, &sample,
+                                    &src_x, &src_y);
+    if (unlikely (src == NULL))
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    pixman_image_composite32 (_pixman_operator (op),
+                             src, NULL, dst->pixman_image,
+                             extents->x + src_x, extents->y + src_y,
+                             0, 0,
+                             extents->x - dst_x, extents->y - dst_y,
+                             extents->width, extents->height);
+
+    pixman_image_unref (src);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+base_compositor_paint (const cairo_compositor_t *_compositor,
+                      cairo_composite_rectangles_t *extents)
+{
+    return clip_and_composite (extents, composite_paint, NULL);
+}
+
+static cairo_int_status_t
+composite_mask (cairo_image_surface_t          *dst,
+               void                            *closure,
+               cairo_operator_t                 op,
+               const cairo_pattern_t           *pattern,
+               int                              dst_x,
+               int                              dst_y,
+               const cairo_rectangle_int_t      *extents)
+{
+    cairo_rectangle_int_t sample;
+    pixman_image_t *src, *mask;
+    int src_x, src_y;
+    int mask_x, mask_y;
+
+    _cairo_pattern_sampled_area (pattern, extents, &sample);
+    src = _pixman_image_for_pattern (dst, pattern, FALSE,
+                                    extents, &sample,
+                                    &src_x, &src_y);
+    if (unlikely (src == NULL))
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    _cairo_pattern_sampled_area (closure, extents, &sample);
+    mask = _pixman_image_for_pattern (dst, closure, TRUE,
+                                     extents, &sample,
+                                     &mask_x, &mask_y);
+    if (unlikely (mask == NULL)) {
+       pixman_image_unref (src);
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+    }
+
+    pixman_image_composite32 (_pixman_operator (op),
+                             src, mask, dst->pixman_image,
+                             extents->x + src_x, extents->y + src_y,
+                             extents->x + mask_x, extents->y + mask_y,
+                             extents->x - dst_x, extents->y - dst_y,
+                             extents->width, extents->height);
+
+    pixman_image_unref (mask);
+    pixman_image_unref (src);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+base_compositor_mask (const cairo_compositor_t *_compositor,
+                     cairo_composite_rectangles_t *extents)
+{
+    return clip_and_composite (extents, composite_mask, &extents->mask_pattern.base);
+}
+
+typedef struct {
+    cairo_traps_t traps;
+    cairo_antialias_t antialias;
+} composite_traps_info_t;
+
+static cairo_int_status_t
+composite_traps (cairo_image_surface_t *dst,
+                void                   *closure,
+                cairo_operator_t        op,
+                const cairo_pattern_t  *pattern,
+                int                     dst_x,
+                int                     dst_y,
+                const cairo_rectangle_int_t *extents)
+{
+    composite_traps_info_t *info = closure;
+    cairo_rectangle_int_t sample;
+    pixman_image_t *src, *mask;
+    int src_x, src_y;
+
+    _cairo_pattern_sampled_area (pattern, extents, &sample);
+    src = _pixman_image_for_pattern (dst, pattern, FALSE,
+                                    extents, &sample,
+                                    &src_x, &src_y);
+    if (unlikely (src == NULL))
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    mask = pixman_image_create_bits (info->antialias == CAIRO_ANTIALIAS_NONE ? PIXMAN_a1 : PIXMAN_a8,
+                                    extents->width, extents->height,
+                                    NULL, 0);
+    if (unlikely (mask == NULL)) {
+       pixman_image_unref (src);
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+    }
+
+    _pixman_image_add_traps (mask, extents->x, extents->y, &info->traps);
+    pixman_image_composite32 (_pixman_operator (op),
+                              src, mask, dst->pixman_image,
+                              extents->x + src_x - dst_x, extents->y + src_y - dst_y,
+                              0, 0,
+                              extents->x - dst_x, extents->y - dst_y,
+                              extents->width, extents->height);
+
+    pixman_image_unref (mask);
+    pixman_image_unref (src);
+
+    return  CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+trim_extents_to_traps (cairo_composite_rectangles_t *extents,
+                      cairo_traps_t *traps)
+{
+    cairo_box_t box;
+
+    /* X trims the affected area to the extents of the trapezoids, so
+     * we need to compensate when fixing up the unbounded area.
+    */
+    _cairo_traps_extents (traps, &box);
+    return _cairo_composite_rectangles_intersect_mask_extents (extents, &box);
+}
+
+static cairo_int_status_t
+base_compositor_stroke (const cairo_compositor_t *_compositor,
+                       cairo_composite_rectangles_t *extents,
+                       const cairo_path_fixed_t *path,
+                       const cairo_stroke_style_t *style,
+                       const cairo_matrix_t    *ctm,
+                       const cairo_matrix_t    *ctm_inverse,
+                       double                   tolerance,
+                       cairo_antialias_t        antialias)
+{
+    composite_traps_info_t info;
+    cairo_int_status_t status;
+
+    info.antialias = antialias;
+    _cairo_traps_init_with_clip (&info.traps, extents->clip);
+    status = _cairo_path_fixed_stroke_to_traps (path, style,
+                                               ctm, ctm_inverse,
+                                               tolerance,
+                                               &info.traps);
+    if (likely (status == CAIRO_INT_STATUS_SUCCESS))
+       status = trim_extents_to_traps (extents, &info.traps);
+    if (likely (status == CAIRO_INT_STATUS_SUCCESS))
+       status = clip_and_composite (extents, composite_traps, &info);
+    _cairo_traps_fini (&info.traps);
+
+    return status;
+}
+
+static cairo_int_status_t
+base_compositor_fill (const cairo_compositor_t *_compositor,
+                     cairo_composite_rectangles_t *extents,
+                     const cairo_path_fixed_t  *path,
+                     cairo_fill_rule_t          fill_rule,
+                     double                     tolerance,
+                     cairo_antialias_t          antialias)
+{
+    composite_traps_info_t info;
+    cairo_int_status_t status;
+
+    info.antialias = antialias;
+    _cairo_traps_init_with_clip (&info.traps, extents->clip);
+    status = _cairo_path_fixed_fill_to_traps (path,
+                                             fill_rule, tolerance,
+                                             &info.traps);
+    if (likely (status == CAIRO_INT_STATUS_SUCCESS))
+       status = trim_extents_to_traps (extents, &info.traps);
+    if (likely (status == CAIRO_INT_STATUS_SUCCESS))
+       status = clip_and_composite (extents, composite_traps, &info);
+    _cairo_traps_fini (&info.traps);
+
+    return status;
+}
+
+static cairo_int_status_t
+composite_glyphs (cairo_image_surface_t        *dst,
+                 void                   *closure,
+                 cairo_operator_t       op,
+                 const cairo_pattern_t *pattern,
+                 int                    dst_x,
+                 int                    dst_y,
+                 const cairo_rectangle_int_t *extents)
+{
+    cairo_composite_glyphs_info_t *info = closure;
+    pixman_image_t *mask;
+    cairo_status_t status;
+    int i;
+
+    mask = pixman_image_create_bits (PIXMAN_a8,
+                                    extents->width, extents->height,
+                                    NULL, 0);
+    if (unlikely (mask == NULL))
+       return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    status = CAIRO_STATUS_SUCCESS;
+    _cairo_scaled_font_freeze_cache (info->font);
+    for (i = 0; i < info->num_glyphs; i++) {
+       cairo_image_surface_t *glyph_surface;
+       cairo_scaled_glyph_t *scaled_glyph;
+       unsigned long glyph_index = info->glyphs[i].index;
+       int x, y;
+
+       status = _cairo_scaled_glyph_lookup (info->font, glyph_index,
+                                            CAIRO_SCALED_GLYPH_INFO_SURFACE,
+                                            &scaled_glyph);
+
+       if (unlikely (status))
+           break;
+
+       glyph_surface = scaled_glyph->surface;
+       if (glyph_surface->width && glyph_surface->height) {
+           /* round glyph locations to the nearest pixel */
+           /* XXX: FRAGILE: We're ignoring device_transform scaling here. A bug? */
+           x = _cairo_lround (info->glyphs[i].x -
+                              glyph_surface->base.device_transform.x0);
+           y = _cairo_lround (info->glyphs[i].y -
+                              glyph_surface->base.device_transform.y0);
+
+           pixman_image_composite32 (PIXMAN_OP_ADD,
+                                     glyph_surface->pixman_image, NULL, mask,
+                                     0, 0,
+                                      0, 0,
+                                      x - extents->x, y - extents->y,
+                                     glyph_surface->width,
+                                     glyph_surface->height);
+       }
+    }
+    _cairo_scaled_font_thaw_cache (info->font);
+
+    if (status == CAIRO_STATUS_SUCCESS) {
+       cairo_rectangle_int_t sample;
+       pixman_image_t *src;
+       int src_x, src_y;
+
+       _cairo_pattern_sampled_area (pattern, extents, &sample);
+       src = _pixman_image_for_pattern (dst, pattern, FALSE,
+                                        extents, &sample,
+                                        &src_x, &src_y);
+       if (src != NULL) {
+           dst_x = extents->x - dst_x;
+           dst_y = extents->y - dst_y;
+           pixman_image_composite32 (_pixman_operator (op),
+                                     src, mask, dst->pixman_image,
+                                     src_x + dst_x,  src_y + dst_y,
+                                     0, 0,
+                                     dst_x, dst_y,
+                                     extents->width, extents->height);
+           pixman_image_unref (src);
+       } else
+           status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+    }
+    pixman_image_unref (mask);
+
+    return status;
+}
+
+static cairo_int_status_t
+base_compositor_glyphs (const cairo_compositor_t       *_compositor,
+                       cairo_composite_rectangles_t    *extents,
+                       cairo_scaled_font_t             *scaled_font,
+                       cairo_glyph_t                   *glyphs,
+                       int                              num_glyphs,
+                       cairo_bool_t                     overlap)
+{
+    cairo_composite_glyphs_info_t info;
+
+    info.font = scaled_font;
+    info.glyphs = glyphs;
+    info.num_glyphs = num_glyphs;
+
+    return clip_and_composite (extents, composite_glyphs, &info);
+}
+
+static const cairo_compositor_t base_compositor = {
+    &__cairo_no_compositor,
+
+    base_compositor_paint,
+    base_compositor_mask,
+    base_compositor_stroke,
+    base_compositor_fill,
+    base_compositor_glyphs,
+};
+
+cairo_surface_t *
+_cairo_test_base_compositor_surface_create (cairo_content_t    content,
+                                           int         width,
+                                           int         height)
+{
+    return test_compositor_surface_create (&base_compositor,
+                                          content, width, height);
+}
diff --git a/src/test-compositor-surface-private.h b/src/test-compositor-surface-private.h
new file mode 100644 (file)
index 0000000..491f241
--- /dev/null
@@ -0,0 +1,56 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2011 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Intel Corporation
+ *
+ * Contributor(s):
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#ifndef TEST_COMPOSITOR_SURFACE_PRIVATE_H
+#define TEST_COMPOSITOR_SURFACE_PRIVATE_H
+
+#include "cairo.h"
+
+#include "test-compositor-surface.h"
+
+#include "cairo-compiler-private.h"
+#include "cairo-compositor-private.h"
+
+CAIRO_BEGIN_DECLS
+
+cairo_private cairo_surface_t *
+test_compositor_surface_create (const cairo_compositor_t *compositor,
+                               cairo_content_t content,
+                               int             width,
+                               int             height);
+
+CAIRO_END_DECLS
+
+#endif /* TEST_COMPOSITOR_SURFACE_PRIVATE H */
diff --git a/src/test-compositor-surface.c b/src/test-compositor-surface.c
new file mode 100644 (file)
index 0000000..356c16c
--- /dev/null
@@ -0,0 +1,259 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2011 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Intel Corporation
+ *
+ * Contributor(s):
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+
+#include "test-compositor-surface-private.h"
+
+#include "cairo-compositor-private.h"
+#include "cairo-default-context-private.h"
+#include "cairo-error-private.h"
+#include "cairo-image-surface-private.h"
+#include "cairo-surface-backend-private.h"
+
+typedef struct _test_compositor_surface {
+    cairo_image_surface_t base;
+} test_compositor_surface_t;
+
+static const cairo_surface_backend_t test_compositor_surface_backend;
+
+cairo_surface_t *
+test_compositor_surface_create (const cairo_compositor_t *compositor,
+                               cairo_content_t content,
+                               int             width,
+                               int             height)
+{
+    test_compositor_surface_t *surface;
+    pixman_image_t *pixman_image;
+    pixman_format_code_t pixman_format;
+
+    switch (content) {
+    case CAIRO_CONTENT_ALPHA:
+       pixman_format = PIXMAN_a8;
+       break;
+    case CAIRO_CONTENT_COLOR:
+       pixman_format = PIXMAN_x8r8g8b8;
+       break;
+    case CAIRO_CONTENT_COLOR_ALPHA:
+       pixman_format = PIXMAN_a8r8g8b8;
+       break;
+    default:
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_CONTENT));
+    }
+
+    pixman_image = pixman_image_create_bits (pixman_format, width, height,
+                                            NULL, 0);
+    if (unlikely (pixman_image == NULL))
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+    surface = malloc (sizeof (test_compositor_surface_t));
+    if (unlikely (surface == NULL)) {
+       pixman_image_unref (pixman_image);
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+    }
+
+    _cairo_surface_init (&surface->base.base,
+                        &test_compositor_surface_backend,
+                        NULL, /* device */
+                        content);
+    _cairo_image_surface_init (&surface->base, pixman_image, pixman_format);
+
+    surface->base.compositor = compositor;
+
+    return &surface->base.base;
+}
+
+static cairo_surface_t *
+test_compositor_surface_create_similar (void           *abstract_surface,
+                                       cairo_content_t  content,
+                                       int              width,
+                                       int              height)
+{
+    test_compositor_surface_t *surface = abstract_surface;
+
+    return test_compositor_surface_create (surface->base.compositor,
+                                          content, width, height);
+}
+
+static cairo_int_status_t
+test_compositor_surface_paint (void                    *_surface,
+                              cairo_operator_t          op,
+                              const cairo_pattern_t    *source,
+                              const cairo_clip_t       *clip)
+{
+    test_compositor_surface_t *surface = _surface;
+    return _cairo_compositor_paint (surface->base.compositor,
+                                   _surface, op, source,
+                                   clip);
+}
+
+static cairo_int_status_t
+test_compositor_surface_mask (void                     *_surface,
+                             cairo_operator_t           op,
+                             const cairo_pattern_t     *source,
+                             const cairo_pattern_t     *mask,
+                             const cairo_clip_t        *clip)
+{
+    test_compositor_surface_t *surface = _surface;
+    return _cairo_compositor_mask (surface->base.compositor,
+                                  _surface, op, source, mask,
+                                   clip);
+}
+
+static cairo_int_status_t
+test_compositor_surface_stroke (void                           *_surface,
+                               cairo_operator_t                 op,
+                               const cairo_pattern_t           *source,
+                               const cairo_path_fixed_t        *path,
+                               const cairo_stroke_style_t      *style,
+                               const cairo_matrix_t            *ctm,
+                               const cairo_matrix_t            *ctm_inverse,
+                               double                           tolerance,
+                               cairo_antialias_t                antialias,
+                               const cairo_clip_t              *clip)
+{
+    test_compositor_surface_t *surface = _surface;
+    return _cairo_compositor_stroke (surface->base.compositor,
+                                    _surface, op, source,
+                                    path, style, ctm, ctm_inverse,
+                                    tolerance, antialias,
+                                    clip);
+}
+
+static cairo_int_status_t
+test_compositor_surface_fill (void                     *_surface,
+                             cairo_operator_t           op,
+                             const cairo_pattern_t     *source,
+                             const cairo_path_fixed_t  *path,
+                             cairo_fill_rule_t          fill_rule,
+                             double                     tolerance,
+                             cairo_antialias_t          antialias,
+                             const cairo_clip_t        *clip)
+{
+    test_compositor_surface_t *surface = _surface;
+    return _cairo_compositor_fill (surface->base.compositor,
+                                  _surface, op, source,
+                                  path, fill_rule, tolerance, antialias,
+                                  clip);
+}
+
+static cairo_int_status_t
+test_compositor_surface_glyphs (void                   *_surface,
+                               cairo_operator_t         op,
+                               const cairo_pattern_t   *source,
+                               cairo_glyph_t           *glyphs,
+                               int                      num_glyphs,
+                               cairo_scaled_font_t     *scaled_font,
+                               const cairo_clip_t      *clip)
+{
+    test_compositor_surface_t *surface = _surface;
+    return _cairo_compositor_glyphs (surface->base.compositor,
+                                    _surface, op, source,
+                                    glyphs, num_glyphs, scaled_font,
+                                    clip);
+}
+
+static const cairo_surface_backend_t test_compositor_surface_backend = {
+    CAIRO_SURFACE_TYPE_IMAGE,
+    _cairo_image_surface_finish,
+    _cairo_default_context_create,
+
+    test_compositor_surface_create_similar,
+    NULL, /* create similar image */
+    _cairo_image_surface_map_to_image,
+    _cairo_image_surface_unmap_image,
+
+    _cairo_image_surface_acquire_source_image,
+    _cairo_image_surface_release_source_image,
+    _cairo_image_surface_snapshot,
+
+    NULL, /* copy_page */
+    NULL, /* show_page */
+
+    _cairo_image_surface_get_extents,
+    _cairo_image_surface_get_font_options,
+
+    NULL, /* flush */
+    NULL, /* mark_dirty_rectangle */
+
+    test_compositor_surface_paint,
+    test_compositor_surface_mask,
+    test_compositor_surface_stroke,
+    test_compositor_surface_fill,
+    NULL, /* fill/stroke */
+    test_compositor_surface_glyphs,
+};
+
+static const cairo_compositor_t *
+get_fallback_compositor (void)
+{
+    return &_cairo_fallback_compositor;
+}
+
+cairo_surface_t *
+_cairo_test_fallback_compositor_surface_create (cairo_content_t        content,
+                                               int             width,
+                                               int             height)
+{
+    return test_compositor_surface_create (get_fallback_compositor(),
+                                          content, width, height);
+}
+
+cairo_surface_t *
+_cairo_test_mask_compositor_surface_create (cairo_content_t    content,
+                                               int             width,
+                                               int             height)
+{
+    return test_compositor_surface_create (_cairo_image_mask_compositor_get(),
+                                          content, width, height);
+}
+
+cairo_surface_t *
+_cairo_test_traps_compositor_surface_create (cairo_content_t   content,
+                                            int                width,
+                                            int                height)
+{
+    return test_compositor_surface_create (_cairo_image_traps_compositor_get(),
+                                          content, width, height);
+}
+
+cairo_surface_t *
+_cairo_test_spans_compositor_surface_create (cairo_content_t   content,
+                                            int                width,
+                                            int                height)
+{
+    return test_compositor_surface_create (_cairo_image_spans_compositor_get(),
+                                          content, width, height);
+}
diff --git a/src/test-compositor-surface.h b/src/test-compositor-surface.h
new file mode 100644 (file)
index 0000000..8d8af2d
--- /dev/null
@@ -0,0 +1,71 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2011 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Intel Corporation
+ *
+ * Contributor(s):
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#ifndef TEST_COMPOSITOR_SURFACE_H
+#define TEST_COMPOSITOR_SURFACE_H
+
+#include "cairo.h"
+
+CAIRO_BEGIN_DECLS
+
+cairo_surface_t *
+_cairo_test_fallback_compositor_surface_create (cairo_content_t        content,
+                                               int             width,
+                                               int             height);
+
+
+cairo_surface_t *
+_cairo_test_mask_compositor_surface_create (cairo_content_t    content,
+                                            int                width,
+                                            int                height);
+
+cairo_surface_t *
+_cairo_test_traps_compositor_surface_create (cairo_content_t   content,
+                                            int                width,
+                                            int                height);
+
+cairo_surface_t *
+_cairo_test_spans_compositor_surface_create (cairo_content_t   content,
+                                            int                width,
+                                            int                height);
+
+cairo_surface_t *
+_cairo_test_base_compositor_surface_create (cairo_content_t    content,
+                                           int                 width,
+                                           int                 height);
+
+CAIRO_END_DECLS
+
+#endif /* TEST_COMPOSITOR_SURFACE_H */
diff --git a/src/test-null-compositor-surface.c b/src/test-null-compositor-surface.c
new file mode 100644 (file)
index 0000000..df2eda0
--- /dev/null
@@ -0,0 +1,472 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2011 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Intel Corporation
+ *
+ * Contributor(s):
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+
+#include "cairoint.h"
+
+#include "test-null-compositor-surface.h"
+
+#include "cairo-compositor-private.h"
+#include "cairo-default-context-private.h"
+#include "cairo-error-private.h"
+#include "cairo-image-surface-private.h"
+#include "cairo-surface-backend-private.h"
+#include "cairo-spans-compositor-private.h"
+#include "cairo-spans-private.h"
+
+typedef struct _test_compositor_surface {
+    cairo_image_surface_t base;
+} test_compositor_surface_t;
+
+static const cairo_surface_backend_t test_compositor_surface_backend;
+
+static cairo_surface_t *
+test_compositor_surface_create (const cairo_compositor_t *compositor,
+                               cairo_content_t content,
+                               int             width,
+                               int             height)
+{
+    test_compositor_surface_t *surface;
+    pixman_image_t *pixman_image;
+    pixman_format_code_t pixman_format;
+
+    switch (content) {
+    case CAIRO_CONTENT_ALPHA:
+       pixman_format = PIXMAN_a8;
+       break;
+    case CAIRO_CONTENT_COLOR:
+       pixman_format = PIXMAN_x8r8g8b8;
+       break;
+    case CAIRO_CONTENT_COLOR_ALPHA:
+       pixman_format = PIXMAN_a8r8g8b8;
+       break;
+    default:
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_CONTENT));
+    }
+
+    pixman_image = pixman_image_create_bits (pixman_format, width, height,
+                                            NULL, 0);
+    if (unlikely (pixman_image == NULL))
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+    surface = malloc (sizeof (test_compositor_surface_t));
+    if (unlikely (surface == NULL)) {
+       pixman_image_unref (pixman_image);
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+    }
+
+    _cairo_surface_init (&surface->base.base,
+                        &test_compositor_surface_backend,
+                        NULL, /* device */
+                        content);
+    _cairo_image_surface_init (&surface->base, pixman_image, pixman_format);
+
+    surface->base.compositor = compositor;
+
+    return &surface->base.base;
+}
+
+static cairo_surface_t *
+test_compositor_surface_create_similar (void           *abstract_surface,
+                                       cairo_content_t  content,
+                                       int              width,
+                                       int              height)
+{
+    test_compositor_surface_t *surface = abstract_surface;
+
+    return test_compositor_surface_create (surface->base.compositor,
+                                          content, width, height);
+}
+
+static cairo_int_status_t
+test_compositor_surface_paint (void                    *_surface,
+                              cairo_operator_t          op,
+                              const cairo_pattern_t    *source,
+                              const cairo_clip_t       *clip)
+{
+    test_compositor_surface_t *surface = _surface;
+    return _cairo_compositor_paint (surface->base.compositor,
+                                   _surface, op, source,
+                                   clip);
+}
+
+static cairo_int_status_t
+test_compositor_surface_mask (void                     *_surface,
+                             cairo_operator_t           op,
+                             const cairo_pattern_t     *source,
+                             const cairo_pattern_t     *mask,
+                             const cairo_clip_t        *clip)
+{
+    test_compositor_surface_t *surface = _surface;
+    return _cairo_compositor_mask (surface->base.compositor,
+                                  _surface, op, source, mask,
+                                   clip);
+}
+
+static cairo_int_status_t
+test_compositor_surface_stroke (void                           *_surface,
+                               cairo_operator_t                 op,
+                               const cairo_pattern_t           *source,
+                               const cairo_path_fixed_t        *path,
+                               const cairo_stroke_style_t      *style,
+                               const cairo_matrix_t            *ctm,
+                               const cairo_matrix_t            *ctm_inverse,
+                               double                           tolerance,
+                               cairo_antialias_t                antialias,
+                               const cairo_clip_t              *clip)
+{
+    test_compositor_surface_t *surface = _surface;
+    return _cairo_compositor_stroke (surface->base.compositor,
+                                    _surface, op, source,
+                                    path, style, ctm, ctm_inverse,
+                                    tolerance, antialias,
+                                    clip);
+}
+
+static cairo_int_status_t
+test_compositor_surface_fill (void                     *_surface,
+                             cairo_operator_t           op,
+                             const cairo_pattern_t     *source,
+                             const cairo_path_fixed_t  *path,
+                             cairo_fill_rule_t          fill_rule,
+                             double                     tolerance,
+                             cairo_antialias_t          antialias,
+                             const cairo_clip_t        *clip)
+{
+    test_compositor_surface_t *surface = _surface;
+    return _cairo_compositor_fill (surface->base.compositor,
+                                  _surface, op, source,
+                                  path, fill_rule, tolerance, antialias,
+                                  clip);
+}
+
+static cairo_int_status_t
+test_compositor_surface_glyphs (void                   *_surface,
+                               cairo_operator_t         op,
+                               const cairo_pattern_t   *source,
+                               cairo_glyph_t           *glyphs,
+                               int                      num_glyphs,
+                               cairo_scaled_font_t     *scaled_font,
+                               const cairo_clip_t      *clip)
+{
+    test_compositor_surface_t *surface = _surface;
+    return _cairo_compositor_glyphs (surface->base.compositor,
+                                    _surface, op, source,
+                                    glyphs, num_glyphs, scaled_font,
+                                    clip);
+}
+
+static const cairo_surface_backend_t test_compositor_surface_backend = {
+    CAIRO_SURFACE_TYPE_IMAGE,
+    _cairo_image_surface_finish,
+    _cairo_default_context_create,
+
+    test_compositor_surface_create_similar,
+    NULL, /* create similar image */
+    _cairo_image_surface_map_to_image,
+    _cairo_image_surface_unmap_image,
+
+    _cairo_image_surface_acquire_source_image,
+    _cairo_image_surface_release_source_image,
+    NULL, /* snapshot */
+
+    NULL, /* copy_page */
+    NULL, /* show_page */
+
+    _cairo_image_surface_get_extents,
+    _cairo_image_surface_get_font_options,
+
+    NULL, /* flush */
+    NULL, /* mark_dirty_rectangle */
+
+    test_compositor_surface_paint,
+    test_compositor_surface_mask,
+    test_compositor_surface_stroke,
+    test_compositor_surface_fill,
+    NULL, /* fill/stroke */
+    test_compositor_surface_glyphs,
+};
+
+static cairo_int_status_t
+acquire (void *abstract_dst)
+{
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+release (void *abstract_dst)
+{
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+set_clip_region (void *_surface,
+                cairo_region_t *region)
+{
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_surface_t *
+pattern_to_surface (cairo_surface_t *dst,
+                   const cairo_pattern_t *pattern,
+                   cairo_bool_t is_mask,
+                   const cairo_rectangle_int_t *extents,
+                   const cairo_rectangle_int_t *sample,
+                   int *src_x, int *src_y)
+{
+    return cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 0, 0);
+}
+
+static cairo_int_status_t
+fill_boxes (void               *_dst,
+           cairo_operator_t     op,
+           const cairo_color_t *color,
+           cairo_boxes_t       *boxes)
+{
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+draw_image_boxes (void *_dst,
+                 cairo_image_surface_t *image,
+                 cairo_boxes_t *boxes,
+                 int dx, int dy)
+{
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+composite (void                        *_dst,
+          cairo_operator_t     op,
+          cairo_surface_t      *abstract_src,
+          cairo_surface_t      *abstract_mask,
+          int                  src_x,
+          int                  src_y,
+          int                  mask_x,
+          int                  mask_y,
+          int                  dst_x,
+          int                  dst_y,
+          unsigned int         width,
+          unsigned int         height)
+{
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+lerp (void                     *_dst,
+      cairo_surface_t          *abstract_src,
+      cairo_surface_t          *abstract_mask,
+      int                      src_x,
+      int                      src_y,
+      int                      mask_x,
+      int                      mask_y,
+      int                      dst_x,
+      int                      dst_y,
+      unsigned int             width,
+      unsigned int             height)
+{
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+composite_boxes (void                  *_dst,
+                cairo_operator_t       op,
+                cairo_surface_t        *abstract_src,
+                cairo_surface_t        *abstract_mask,
+                int                    src_x,
+                int                    src_y,
+                int                    mask_x,
+                int                    mask_y,
+                int                    dst_x,
+                int                    dst_y,
+                cairo_boxes_t          *boxes,
+                const cairo_rectangle_int_t  *extents)
+{
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+composite_traps (void                  *_dst,
+                cairo_operator_t       op,
+                cairo_surface_t        *abstract_src,
+                int                    src_x,
+                int                    src_y,
+                int                    dst_x,
+                int                    dst_y,
+                const cairo_rectangle_int_t *extents,
+                cairo_antialias_t      antialias,
+                cairo_traps_t          *traps)
+{
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+check_composite_glyphs (const cairo_composite_rectangles_t *extents,
+                       cairo_scaled_font_t *scaled_font,
+                       cairo_glyph_t *glyphs,
+                       int *num_glyphs)
+{
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+composite_glyphs (void                         *_dst,
+                 cairo_operator_t               op,
+                 cairo_surface_t               *_src,
+                 int                            src_x,
+                 int                            src_y,
+                 int                            dst_x,
+                 int                            dst_y,
+                 cairo_composite_glyphs_info_t *info)
+{
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+spans (void *abstract_renderer,
+       int y, int height,
+       const cairo_half_open_span_t *spans,
+       unsigned num_spans)
+{
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+finish_spans (void *abstract_renderer)
+{
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+span_renderer_init (cairo_abstract_span_renderer_t     *_r,
+                   const cairo_composite_rectangles_t *composite,
+                   cairo_bool_t                         needs_clip)
+{
+    cairo_span_renderer_t *r = (cairo_span_renderer_t *)_r;
+    r->render_rows = spans;
+    r->finish = finish_spans;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+span_renderer_fini (cairo_abstract_span_renderer_t *_r,
+                   cairo_int_status_t status)
+{
+}
+
+static const cairo_compositor_t *
+no_fallback_compositor_get (void)
+{
+    return &__cairo_no_compositor;
+}
+
+static const cairo_compositor_t *
+no_traps_compositor_get (void)
+{
+    static cairo_traps_compositor_t compositor;
+
+    if (compositor.base.delegate == NULL) {
+       _cairo_traps_compositor_init (&compositor,
+                                     no_fallback_compositor_get ());
+
+       compositor.acquire = acquire;
+       compositor.release = release;
+       compositor.set_clip_region = set_clip_region;
+       compositor.pattern_to_surface = pattern_to_surface;
+       compositor.draw_image_boxes = draw_image_boxes;
+       //compositor.copy_boxes = copy_boxes;
+       compositor.fill_boxes = fill_boxes;
+       //compositor.check_composite = check_composite;
+       compositor.composite = composite;
+       compositor.lerp = lerp;
+       //compositor.check_composite_boxes = check_composite_boxes;
+       compositor.composite_boxes = composite_boxes;
+       //compositor.check_composite_traps = check_composite_traps;
+       compositor.composite_traps = composite_traps;
+       compositor.check_composite_glyphs = check_composite_glyphs;
+       compositor.composite_glyphs = composite_glyphs;
+    }
+
+    return &compositor.base;
+}
+
+static const cairo_compositor_t *
+no_spans_compositor_get (void)
+{
+    static cairo_spans_compositor_t compositor;
+
+    if (compositor.base.delegate == NULL) {
+       _cairo_spans_compositor_init (&compositor,
+                                     no_traps_compositor_get());
+
+       //compositor.acquire = acquire;
+       //compositor.release = release;
+       compositor.fill_boxes = fill_boxes;
+       //compositor.check_composite_boxes = check_composite_boxes;
+       compositor.composite_boxes = composite_boxes;
+       //compositor.check_span_renderer = check_span_renderer;
+       compositor.renderer_init = span_renderer_init;
+       compositor.renderer_fini = span_renderer_fini;
+    }
+
+    return &compositor.base;
+}
+
+cairo_surface_t *
+_cairo_test_no_fallback_compositor_surface_create (cairo_content_t     content,
+                                                  int          width,
+                                                  int          height)
+{
+    return test_compositor_surface_create (no_fallback_compositor_get(),
+                                          content, width, height);
+}
+
+cairo_surface_t *
+_cairo_test_no_traps_compositor_surface_create (cairo_content_t        content,
+                                               int             width,
+                                               int             height)
+{
+    return test_compositor_surface_create (no_traps_compositor_get(),
+                                          content, width, height);
+}
+
+cairo_surface_t *
+_cairo_test_no_spans_compositor_surface_create (cairo_content_t        content,
+                                            int                width,
+                                            int                height)
+{
+    return test_compositor_surface_create (no_spans_compositor_get(),
+                                          content, width, height);
+}
diff --git a/src/test-null-compositor-surface.h b/src/test-null-compositor-surface.h
new file mode 100644 (file)
index 0000000..52d864b
--- /dev/null
@@ -0,0 +1,60 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2011 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Intel Corporation
+ *
+ * Contributor(s):
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#ifndef TEST_NULL_COMPOSITOR_SURFACE_H
+#define TEST_NULL_COMPOSITOR_SURFACE_H
+
+#include "cairo.h"
+
+CAIRO_BEGIN_DECLS
+
+cairo_surface_t *
+_cairo_test_no_fallback_compositor_surface_create (cairo_content_t     content,
+                                                  int                  width,
+                                                  int                  height);
+
+cairo_surface_t *
+_cairo_test_no_traps_compositor_surface_create (cairo_content_t        content,
+                                               int             width,
+                                               int             height);
+
+cairo_surface_t *
+_cairo_test_no_spans_compositor_surface_create (cairo_content_t        content,
+                                               int             width,
+                                               int             height);
+
+CAIRO_END_DECLS
+
+#endif /* TEST_NULL_COMPOSITOR_SURFACE_H */
diff --git a/src/test-paginated-surface.c b/src/test-paginated-surface.c
new file mode 100644 (file)
index 0000000..e3c8a91
--- /dev/null
@@ -0,0 +1,285 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2005 Red Hat, Inc
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ *     Carl Worth <cworth@cworth.org>
+ */
+
+/* This isn't a "real" surface, but just something to be used by the
+ * test suite to help exercise the paginated-surface paths in cairo.
+ *
+ * The defining feature of this backend is that it uses a paginated
+ * surface to record all operations, and then replays everything to an
+ * image surface.
+ *
+ * It's possible that this code might serve as a good starting point
+ * for someone working on bringing up a new paginated-surface-based
+ * backend.
+ */
+
+#include "cairoint.h"
+
+#include "test-paginated-surface.h"
+
+#include "cairo-default-context-private.h"
+#include "cairo-error-private.h"
+#include "cairo-paginated-private.h"
+#include "cairo-surface-backend-private.h"
+
+typedef struct _test_paginated_surface {
+    cairo_surface_t base;
+    cairo_surface_t *target;
+    cairo_paginated_mode_t paginated_mode;
+} test_paginated_surface_t;
+
+static const cairo_surface_backend_t test_paginated_surface_backend;
+static const cairo_paginated_surface_backend_t test_paginated_surface_paginated_backend;
+
+cairo_surface_t *
+_cairo_test_paginated_surface_create (cairo_surface_t *target)
+{
+    cairo_status_t status;
+    cairo_surface_t *paginated;
+    test_paginated_surface_t *surface;
+
+    status = cairo_surface_status (target);
+    if (unlikely (status))
+       return _cairo_surface_create_in_error (status);
+
+    surface = malloc (sizeof (test_paginated_surface_t));
+    if (unlikely (surface == NULL))
+       return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+    _cairo_surface_init (&surface->base,
+                        &test_paginated_surface_backend,
+                        NULL, /* device */
+                        target->content);
+
+    surface->target = cairo_surface_reference (target);
+
+    paginated =  _cairo_paginated_surface_create (&surface->base,
+                                                 target->content,
+                                                 &test_paginated_surface_paginated_backend);
+    status = paginated->status;
+    if (status == CAIRO_STATUS_SUCCESS) {
+       /* paginated keeps the only reference to surface now, drop ours */
+       cairo_surface_destroy (&surface->base);
+       return paginated;
+    }
+
+    cairo_surface_destroy (target);
+    free (surface);
+    return _cairo_surface_create_in_error (status);
+}
+
+static cairo_status_t
+_test_paginated_surface_finish (void *abstract_surface)
+{
+    test_paginated_surface_t *surface = abstract_surface;
+
+    cairo_surface_destroy (surface->target);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_bool_t
+_test_paginated_surface_get_extents (void                      *abstract_surface,
+                                    cairo_rectangle_int_t      *rectangle)
+{
+    test_paginated_surface_t *surface = abstract_surface;
+
+    return _cairo_surface_get_extents (surface->target, rectangle);
+}
+
+static cairo_int_status_t
+_test_paginated_surface_paint (void            *abstract_surface,
+                              cairo_operator_t  op,
+                              const cairo_pattern_t    *source,
+                              const cairo_clip_t       *clip)
+{
+    test_paginated_surface_t *surface = abstract_surface;
+
+    if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
+       return CAIRO_STATUS_SUCCESS;
+
+    return _cairo_surface_paint (surface->target, op, source, clip);
+}
+
+static cairo_int_status_t
+_test_paginated_surface_mask (void             *abstract_surface,
+                             cairo_operator_t   op,
+                             const cairo_pattern_t     *source,
+                             const cairo_pattern_t     *mask,
+                             const cairo_clip_t        *clip)
+{
+    test_paginated_surface_t *surface = abstract_surface;
+
+    if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
+       return CAIRO_STATUS_SUCCESS;
+
+    return _cairo_surface_mask (surface->target,
+                               op, source, mask, clip);
+}
+
+static cairo_int_status_t
+_test_paginated_surface_stroke (void                           *abstract_surface,
+                               cairo_operator_t                 op,
+                               const cairo_pattern_t           *source,
+                               const cairo_path_fixed_t                *path,
+                               const cairo_stroke_style_t              *style,
+                               const cairo_matrix_t                    *ctm,
+                               const cairo_matrix_t                    *ctm_inverse,
+                               double                           tolerance,
+                               cairo_antialias_t                antialias,
+                               const cairo_clip_t              *clip)
+{
+    test_paginated_surface_t *surface = abstract_surface;
+
+    if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
+       return CAIRO_STATUS_SUCCESS;
+
+    return _cairo_surface_stroke (surface->target, op, source,
+                                 path, style,
+                                 ctm, ctm_inverse,
+                                 tolerance, antialias,
+                                 clip);
+}
+
+static cairo_int_status_t
+_test_paginated_surface_fill (void                             *abstract_surface,
+                             cairo_operator_t                   op,
+                             const cairo_pattern_t             *source,
+                             const cairo_path_fixed_t          *path,
+                             cairo_fill_rule_t                  fill_rule,
+                             double                             tolerance,
+                             cairo_antialias_t                  antialias,
+                             const cairo_clip_t                *clip)
+{
+    test_paginated_surface_t *surface = abstract_surface;
+
+    if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
+       return CAIRO_STATUS_SUCCESS;
+
+    return _cairo_surface_fill (surface->target, op, source,
+                               path, fill_rule,
+                               tolerance, antialias,
+                               clip);
+}
+
+static cairo_bool_t
+_test_paginated_surface_has_show_text_glyphs (void *abstract_surface)
+{
+    test_paginated_surface_t *surface = abstract_surface;
+
+    return cairo_surface_has_show_text_glyphs (surface->target);
+}
+
+static cairo_int_status_t
+_test_paginated_surface_show_text_glyphs (void                     *abstract_surface,
+                                         cairo_operator_t           op,
+                                         const cairo_pattern_t     *source,
+                                         const char                *utf8,
+                                         int                        utf8_len,
+                                         cairo_glyph_t             *glyphs,
+                                         int                        num_glyphs,
+                                         const cairo_text_cluster_t *clusters,
+                                         int                        num_clusters,
+                                         cairo_text_cluster_flags_t cluster_flags,
+                                         cairo_scaled_font_t       *scaled_font,
+                                         const cairo_clip_t        *clip)
+{
+    test_paginated_surface_t *surface = abstract_surface;
+
+    if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
+       return CAIRO_STATUS_SUCCESS;
+
+    return _cairo_surface_show_text_glyphs (surface->target, op, source,
+                                           utf8, utf8_len,
+                                           glyphs, num_glyphs,
+                                           clusters, num_clusters,
+                                           cluster_flags,
+                                           scaled_font,
+                                           clip);
+}
+
+
+static void
+_test_paginated_surface_set_paginated_mode (void                       *abstract_surface,
+                                           cairo_paginated_mode_t       mode)
+{
+    test_paginated_surface_t *surface = abstract_surface;
+
+    surface->paginated_mode = mode;
+}
+
+static const cairo_surface_backend_t test_paginated_surface_backend = {
+    CAIRO_INTERNAL_SURFACE_TYPE_TEST_PAGINATED,
+    _test_paginated_surface_finish,
+    _cairo_default_context_create,
+
+    /* Since we are a paginated user, we get to regard most of the
+     * surface backend interface as historical cruft and ignore it. */
+
+    NULL, /* create_similar */
+    NULL, /* create similar image */
+    NULL, /* map to image */
+    NULL, /* unmap image */
+
+    NULL, /* acquire_source_image */
+    NULL, /* release_source_image */
+    NULL, /* snapshot */
+
+    NULL, /* copy_page */
+    NULL, /* show_page */
+
+    _test_paginated_surface_get_extents,
+    NULL, /* get_font_options */
+
+    NULL, /* flush */
+    NULL, /* mark_dirty_rectangle */
+
+    /* Here is the more "modern" section of the surface backend
+     * interface which is mostly just drawing functions */
+
+    _test_paginated_surface_paint,
+    _test_paginated_surface_mask,
+    _test_paginated_surface_stroke,
+    _test_paginated_surface_fill,
+    NULL, /* fill-stroke */
+    NULL, /* replaced by show_text_glyphs */
+    _test_paginated_surface_has_show_text_glyphs,
+    _test_paginated_surface_show_text_glyphs
+};
+
+static const cairo_paginated_surface_backend_t test_paginated_surface_paginated_backend = {
+    NULL, /* start_page */
+    _test_paginated_surface_set_paginated_mode
+};
diff --git a/src/test-paginated-surface.h b/src/test-paginated-surface.h
new file mode 100644 (file)
index 0000000..2bd98aa
--- /dev/null
@@ -0,0 +1,48 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2005 Red Hat, Inc
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ *     Carl Worth <cworth@cworth.org>
+ */
+
+#ifndef TEST_PAGINATED_SURFACE_H
+#define TEST_PAGINATED_SURFACE_H
+
+#include "cairo.h"
+
+CAIRO_BEGIN_DECLS
+
+cairo_surface_t *
+_cairo_test_paginated_surface_create (cairo_surface_t *target);
+
+CAIRO_END_DECLS
+
+#endif /* TEST_PAGINATED_SURFACE_H */
diff --git a/test/.gitignore b/test/.gitignore
new file mode 100644 (file)
index 0000000..dba5877
--- /dev/null
@@ -0,0 +1,40 @@
+TAGS
+tags
+.deps
+.libs
+output
+Makefile
+Makefile.in
+ref.hash
+ref.list
+any2ppm
+.any2ppm
+.any2ppm.pid
+.any2ppm.errors
+cairo-test-constructors.c
+cairo-test-trace
+cairo-test-suite
+pdf2png
+ps2png
+svg2png
+valgrind-log
+*.out.*
+*.pass.*
+*.fail.*
+*.diff.png
+*.manifest
+*.gcda
+*.gcno
+*.exe
+*.exe.so
+*.obj
+*.ilk
+*.pdb
+*.la
+*.lo
+*.log
+*.suo
+*.o
+*~
+.*.sw?
+make-cairo-test-constructors
diff --git a/test/.valgrind-suppressions b/test/.valgrind-suppressions
new file mode 100644 (file)
index 0000000..f481a2f
--- /dev/null
@@ -0,0 +1,577 @@
+{
+   in dl.so
+   Memcheck:Cond
+   fun:_dl_relocate_object
+ }
+{
+   bugs in libpng/libz
+   Memcheck:Cond
+   obj:/usr/lib/libz.so.1.2.2.2
+   obj:/usr/lib/libz.so.1.2.2.2
+   fun:deflate
+ }
+{
+   bugs in libpng/libz
+   Memcheck:Cond
+   obj:/usr/lib/libz.so.1.2.3
+   obj:/usr/lib/libz.so.1.2.3
+   fun:deflate
+ }
+{
+   bugs in libpng/libz
+   Memcheck:Cond
+   obj:/usr/lib/libz.so.1.2.3.3
+ }
+{
+   bugs in libpng/libz
+   Memcheck:Value8
+   obj:/usr/lib/libz.so.1.2.3.3
+ }
+{
+   cairo's write_png triggers apparent bugs in libpng/libz
+   Memcheck:Cond
+   obj:/usr/lib/libz.so.1.2.2.2
+   obj:/usr/lib/libz.so.1.2.2.2
+   fun:deflate
+   fun:png_write_finish_row
+   fun:png_write_filtered_row
+   fun:png_write_find_filter
+   fun:png_write_row
+   fun:png_write_image
+   fun:write_png
+ }
+{
+   cairo's write_png_argb32 triggers apparent bugs in libpng/libz
+   Memcheck:Cond
+   obj:/usr/lib/libz.so.1.2.3
+   obj:/usr/lib/libz.so.1.2.3
+   fun:deflate
+   obj:/usr/lib/libpng12.so.0.1.2.8
+   obj:/usr/lib/libpng12.so.0.1.2.8
+   obj:/usr/lib/libpng12.so.0.1.2.8
+   fun:png_write_row
+   fun:png_write_image
+   fun:write_png
+}
+{
+   cairo's write_png_argb32 triggers apparent bugs in libpng/libz
+   Memcheck:Cond
+   obj:/usr/lib/libz.so.1.2.2.2
+   obj:/usr/lib/libz.so.1.2.2.2
+   fun:deflate
+   fun:png_write_finish_row
+   fun:png_write_filtered_row
+   fun:png_write_find_filter
+   fun:png_write_row
+   fun:png_write_image
+   fun:write_png_argb32
+ }
+{
+   cairo's _cairo_pdf_surface_paint triggers apparent bugs in libz
+   Memcheck:Cond
+   obj:/usr/lib/libz.so.1.2.3
+   obj:/usr/lib/libz.so.1.2.3
+   fun:deflate
+   fun:compress2
+   fun:compress
+   fun:compress_dup
+   fun:emit_image
+   fun:emit_surface_pattern
+   fun:emit_pattern
+   fun:_cairo_pdf_surface_paint
+}
+{
+   cairo's _cairo_pdf_surface_paint triggers apparent bugs in libz
+   Memcheck:Cond
+   obj:/usr/lib/libz.so.1.2.3
+   obj:/usr/lib/libz.so.1.2.3
+   fun:deflate
+   fun:compress2
+   fun:compress
+   fun:compress_dup
+   fun:emit_pattern
+   fun:_cairo_pdf_surface_paint
+}
+{
+   cairo's _cairo_pdf_surface_paint triggers apparent bugs in libz
+   Memcheck:Cond
+   fun:deflate_slow
+   fun:deflate
+}
+{
+   cairo's _cairo_pdf_surface_paint triggers apparent bugs in libz
+   Memcheck:Value4
+   fun:deflate_slow
+   fun:deflate
+}
+{
+   cairo's _cairo_pdf_surface_paint triggers apparent bugs in libz
+   Memcheck:Value4
+   fun:compress_block
+   fun:_tr_flush_block
+   fun:deflate_slow
+   fun:deflate
+}
+{
+   cairo's _cairo_pdf_surface_paint triggers apparent bugs in libz
+   Memcheck:Value4
+   fun:crc32
+   obj:/usr/lib/libpng12.so.0.15.0
+   fun:png_write_chunk_data
+   fun:png_write_chunk
+}
+{
+   cairo's _cairo_pdf_surface_paint triggers apparent bugs in libz
+   Memcheck:Value4
+   fun:base64_write_func
+   fun:stream_write_func
+   obj:/usr/lib/libpng12.so.0.15.0
+   fun:png_write_chunk_data
+   fun:png_write_chunk
+}
+{
+   pthread initialization strstr bug
+   Memcheck:Cond
+   fun:strstr
+   fun:__pthread_initialize_minimal
+   obj:/lib/libpthread-2.3.5.so
+   obj:/lib/libpthread-2.3.5.so
+   fun:call_init
+   fun:_dl_init
+   obj:/lib/ld-2.3.5.so
+}
+{
+   Pixman reads padding bytes that are never initialized
+   Memcheck:Cond
+   fun:fbBltOne
+   fun:fbCompositeSolidMask_nx1xn
+   fun:_cairo_pixman_composite
+   fun:_cairo_image_surface_composite
+   fun:_cairo_surface_composite
+   fun:_cairo_ft_scaled_font_show_glyphs
+   fun:_cairo_scaled_font_show_glyphs
+   fun:_cairo_gstate_show_glyphs_draw_func
+   fun:_cairo_gstate_clip_and_composite
+   fun:_cairo_gstate_show_glyphs
+   fun:cairo_show_text
+   fun:draw
+}
+{
+   XXX: I have no idea what might be causing this
+   Memcheck:Free
+   fun:free
+   fun:free_mem
+   fun:__libc_freeres
+   fun:_vgw_freeres
+   fun:exit
+   fun:__libc_start_main
+}
+{
+   XrmGetStringDatabase is fairly obnoxious about leaving reachable memory around
+   Memcheck:Leak
+   fun:malloc
+   fun:_XlcAddCT
+   fun:_XlcInitCTInfo
+   fun:initialize
+   fun:initialize
+   fun:_XlcCreateLC
+   fun:_XlcUtf8Loader
+   fun:_XOpenLC
+   fun:_XrmInitParseInfo
+   fun:NewDatabase
+   fun:XrmGetStringDatabase
+}
+{
+   XrmGetStringDatabase is fairly obnoxious about leaving reachable memory around
+   Memcheck:Leak
+   fun:malloc
+   fun:PutEntry
+   fun:GetDatabase
+   fun:XrmGetStringDatabase
+}
+{
+   XrmGetStringDatabase is fairly obnoxious about leaving reachable memory around
+   Memcheck:Leak
+   fun:malloc
+   fun:initialize
+   fun:_XlcCreateLC
+   fun:_XlcDefaultLoader
+   fun:_XOpenLC
+   fun:_XrmInitParseInfo
+   fun:NewDatabase
+   fun:XrmGetStringDatabase
+}
+{
+   XrmGetStringDatabase is fairly obnoxious about leaving reachable memory around
+   Memcheck:Leak
+   fun:malloc
+   fun:add_codeset
+   fun:initialize
+   fun:_XlcCreateLC
+   fun:_XlcDefaultLoader
+   fun:_XOpenLC
+   fun:_XrmInitParseInfo
+   fun:NewDatabase
+   fun:XrmGetStringDatabase
+}
+{
+   XrmInitialize is fairly obnoxious about leaving reachable memory around
+   Memcheck:Leak
+   fun:malloc
+   fun:_XrmInternalStringToQuark
+}
+{
+   XrmInitialize is fairly obnoxious about leaving reachable memory around
+   Memcheck:Leak
+   fun:malloc
+   obj:/usr/lib/libX11.so.6.2.0
+   fun:_XrmInternalStringToQuark
+   fun:XrmInitialize
+}
+{
+   XrmInitialize is fairly obnoxious about leaving reachable memory around
+   Memcheck:Leak
+   fun:malloc
+   fun:permalloc
+   fun:_XrmInternalStringToQuark
+}
+{
+   XrmGetStringDatabase is fairly obnoxious about leaving reachable memory around
+   Memcheck:Leak
+   fun:malloc
+   fun:_XlcSetConverter
+   fun:_XlcDefaultLoader
+   fun:_XOpenLC
+   fun:_XrmInitParseInfo
+   fun:NewDatabase
+   fun:XrmGetStringDatabase
+}
+{
+   XrmGetStringDatabase is fairly obnoxious about leaving reachable memory around
+   Memcheck:Leak
+   fun:malloc
+   fun:_XlcCreateDefaultCharSet
+   fun:_XlcAddCT
+   fun:_XlcInitCTInfo
+   fun:initialize
+   fun:initialize
+   fun:_XlcCreateLC
+   fun:_XlcUtf8Loader
+   fun:_XOpenLC
+   fun:_XrmInitParseInfo
+   fun:NewDatabase
+   fun:XrmGetStringDatabase
+}
+{
+   XrmGetStringDatabase is fairly obnoxious about leaving reachable memory around
+   Memcheck:Leak
+   fun:realloc
+   fun:add_codeset
+   fun:initialize
+   fun:_XlcCreateLC
+   fun:_XlcDefaultLoader
+   fun:_XOpenLC
+   fun:_XrmInitParseInfo
+   fun:NewDatabase
+   fun:XrmGetStringDatabase
+}
+{
+   XrmGetStringDatabase is fairly obnoxious about leaving reachable memory around
+   Memcheck:Leak
+   fun:realloc
+   fun:add_codeset
+   fun:load_generic
+   fun:initialize
+   fun:_XlcCreateLC
+   fun:_XlcDefaultLoader
+   fun:_XOpenLC
+}
+{
+   XrmGetStringDatabase is fairly obnoxious about leaving reachable memory around
+   Memcheck:Leak
+   fun:malloc
+   fun:add_codeset
+   fun:load_generic
+   fun:initialize
+   fun:_XlcCreateLC
+   fun:_XlcDefaultLoader
+   fun:_XOpenLC
+}
+{
+   XrmGetStringDatabase is fairly obnoxious about leaving reachable memory around
+   Memcheck:Leak
+   fun:malloc
+   fun:load_generic
+   fun:initialize
+   fun:_XlcCreateLC
+   fun:_XlcDefaultLoader
+   fun:_XOpenLC
+}
+{
+   XrmGetStringDatabase is fairly obnoxious about leaving reachable memory around
+   Memcheck:Leak
+   fun:malloc
+   fun:_XlcAddCharSet
+   fun:_XlcAddCT
+   fun:_XlcInitCTInfo
+   fun:initialize
+   fun:initialize
+   fun:_XlcCreateLC
+   fun:_XlcUtf8Loader
+   fun:_XOpenLC
+   fun:_XrmInitParseInfo
+   fun:NewDatabase
+   fun:XrmGetStringDatabase
+}
+{
+   XrmGetStringDatabase is fairly obnoxious about leaving reachable memory around
+   Memcheck:Leak
+   fun:malloc
+   fun:_XlcCreateLocaleDataBase
+   fun:initialize
+   fun:initialize
+   fun:_XlcCreateLC
+   fun:_XlcUtf8Loader
+   fun:_XOpenLC
+   fun:_XrmInitParseInfo
+   fun:NewDatabase
+   fun:XrmGetStringDatabase
+}
+{
+   XrmGetStringDatabase is fairly obnoxious about leaving reachable memory around
+   Memcheck:Leak
+   fun:malloc
+   fun:_XlcSetConverter
+   fun:_XlcInitCTInfo
+   fun:initialize
+   fun:initialize
+   fun:_XlcCreateLC
+   fun:_XlcUtf8Loader
+   fun:_XOpenLC
+   fun:_XrmInitParseInfo
+   fun:NewDatabase
+   fun:XrmGetStringDatabase
+}
+{
+   pthread initialization seems to leave some memory possibly lost
+   Memcheck:Leak
+   fun:calloc
+   obj:/lib/ld-2.3.6.so
+   obj:/lib/ld-2.3.6.so
+   fun:_dl_allocate_tls
+   fun:__pthread_initialize_minimal
+   obj:/usr/lib/debug/libpthread-0.10.so
+   obj:/usr/lib/debug/libpthread-0.10.so
+   obj:/lib/ld-2.3.6.so
+   obj:/lib/ld-2.3.6.so
+   obj:/lib/ld-2.3.6.so
+}
+{
+   pthread initialization seems to leave some memory still reachable
+   Memcheck:Leak
+   fun:calloc
+   fun:_dl_tls_setup
+   fun:__pthread_initialize_minimal
+   obj:/usr/lib/debug/libpthread-0.10.so
+   obj:/usr/lib/debug/libpthread-0.10.so
+   obj:/lib/ld-2.3.6.so
+   obj:/lib/ld-2.3.6.so
+   obj:/lib/ld-2.3.6.so
+}
+{
+   pthread initialization seems to leave some memory possibly lost
+   Memcheck:Leak
+   fun:memalign
+   obj:/lib/ld-2.3.6.so
+   fun:_dl_allocate_tls
+   fun:__pthread_initialize_minimal
+   obj:/usr/lib/debug/libpthread-0.10.so
+   obj:/usr/lib/debug/libpthread-0.10.so
+   obj:/lib/ld-2.3.6.so
+   obj:/lib/ld-2.3.6.so
+   obj:/lib/ld-2.3.6.so
+}
+{
+   XrmGet*Database is fairly obnoxious about leaving reachable memory around
+   Memcheck:Leak
+   fun:malloc
+   fun:_XlcAddCT
+   fun:_XlcInitCTInfo
+   fun:initialize
+   fun:initialize
+   fun:_XlcCreateLC
+   fun:_XlcUtf8Loader
+   fun:_XOpenLC
+   fun:_XrmInitParseInfo
+   fun:NewDatabase
+}
+{
+   XrmGet*Database is fairly obnoxious about leaving reachable memory around
+   Memcheck:Leak
+   fun:malloc
+   fun:PutEntry
+   fun:GetDatabase
+}
+{
+   XrmGet*Database is fairly obnoxious about leaving reachable memory around
+   Memcheck:Leak
+   fun:malloc
+   fun:initialize
+   fun:_XlcCreateLC
+   fun:_XlcDefaultLoader
+   fun:_XOpenLC
+   fun:_XrmInitParseInfo
+   fun:NewDatabase
+}
+{
+   XrmGet*Database is fairly obnoxious about leaving reachable memory around
+   Memcheck:Leak
+   fun:malloc
+   fun:add_codeset
+   fun:initialize
+   fun:_XlcCreateLC
+   fun:_XlcDefaultLoader
+   fun:_XOpenLC
+   fun:_XrmInitParseInfo
+   fun:NewDatabase
+}
+{
+   XrmGet*Database is fairly obnoxious about leaving reachable memory around
+   Memcheck:Leak
+   fun:malloc
+   fun:_XlcAddCharSet
+   fun:_XlcAddCT
+   fun:_XlcInitCTInfo
+   fun:initialize
+   fun:initialize
+   fun:_XlcCreateLC
+   fun:_XlcUtf8Loader
+   fun:_XOpenLC
+   fun:_XrmInitParseInfo
+   fun:NewDatabase
+}
+{
+   XrmGet*Database is fairly obnoxious about leaving reachable memory around
+   Memcheck:Leak
+   fun:realloc
+   fun:add_codeset
+   fun:initialize
+   fun:_XlcCreateLC
+   fun:_XlcDefaultLoader
+   fun:_XOpenLC
+   fun:_XrmInitParseInfo
+   fun:NewDatabase
+}
+{
+   XrmGet*Database is fairly obnoxious about leaving reachable memory around
+   Memcheck:Leak
+   fun:malloc
+   fun:_XlcCreateDefaultCharSet
+   fun:_XlcAddCT
+   fun:_XlcInitCTInfo
+   fun:initialize
+   fun:initialize
+   fun:_XlcCreateLC
+   fun:_XlcUtf8Loader
+   fun:_XOpenLC
+   fun:_XrmInitParseInfo
+   fun:NewDatabase
+}
+{
+   XrmGet*Database is fairly obnoxious about leaving reachable memory around
+   Memcheck:Leak
+   fun:malloc
+   fun:_XlcCreateDefaultCharSet
+   fun:_XlcAddCT
+   fun:_XlcInitCTInfo
+   fun:initialize
+   fun:initialize
+   fun:_XlcCreateLC
+   fun:_XlcUtf8Loader
+   fun:_XOpenLC
+   fun:_XrmInitParseInfo
+   fun:NewDatabase
+}
+{
+   XrmGet*Database is fairly obnoxious about leaving reachable memory around
+   Memcheck:Leak
+   fun:malloc
+   fun:_XlcAddCT
+   fun:_XlcInitCTInfo
+   fun:initialize
+   fun:initialize
+   fun:_XlcCreateLC
+   fun:_XlcUtf8Loader
+   fun:_XOpenLC
+   fun:_XrmInitParseInfo
+   fun:NewDatabase
+}
+{
+   XrmGet*Database is fairly obnoxious about leaving reachable memory around
+   Memcheck:Leak
+   fun:malloc
+   fun:_XlcCreateLocaleDataBase
+   fun:initialize
+   fun:initialize
+   fun:_XlcCreateLC
+   fun:_XlcUtf8Loader
+   fun:_XOpenLC
+   fun:_XrmInitParseInfo
+   fun:NewDatabase
+}
+{
+   XrmGet*Database is fairly obnoxious about leaving reachable memory around
+   Memcheck:Leak
+   fun:malloc
+   fun:_XlcCreateDefaultCharSet
+   fun:_XlcAddCT
+   fun:_XlcInitCTInfo
+   fun:initialize
+   fun:initialize
+   fun:_XlcCreateLC
+   fun:_XlcUtf8Loader
+   fun:_XOpenLC
+   fun:_XrmInitParseInfo
+   fun:NewDatabase
+}
+{
+   XrmGet*Database is fairly obnoxious about leaving reachable memory around
+   Memcheck:Leak
+   fun:malloc
+   fun:_XlcSetConverter
+   fun:_XlcInitCTInfo
+   fun:initialize
+   fun:initialize
+   fun:_XlcCreateLC
+   fun:_XlcUtf8Loader
+   fun:_XOpenLC
+   fun:_XrmInitParseInfo
+   fun:NewDatabase
+}
+{
+   XrmGet*Database is fairly obnoxious about leaving reachable memory around
+   Memcheck:Leak
+   fun:malloc
+   fun:_XlcSetConverter
+   fun:_XlcDefaultLoader
+   fun:_XOpenLC
+   fun:_XrmInitParseInfo
+   fun:NewDatabase
+}
+{
+   XrmGet*Database is fairly obnoxious about leaving reachable memory around
+   Memcheck:Leak
+   fun:malloc
+   fun:_XlcSetConverter
+   fun:_XlcAddUtf8Converters
+   fun:_XlcDefaultLoader
+   fun:_XOpenLC
+   fun:_XrmInitParseInfo
+   fun:NewDatabase
+}
+{
+   Xau chooses not to free its static data...
+   Memcheck:Leak
+   fun:malloc
+   fun:XauFileName
+}
diff --git a/test/6x13.pcf b/test/6x13.pcf
new file mode 100644 (file)
index 0000000..1325ae6
Binary files /dev/null and b/test/6x13.pcf differ
diff --git a/test/COPYING b/test/COPYING
new file mode 100644 (file)
index 0000000..3db71fb
--- /dev/null
@@ -0,0 +1,26 @@
+Cairo is free software.
+
+These tests are mainly available under a liberal MIT license to simplify
+any use of the code for reference purposes. Please check the opening comment
+of each file for copyright and licensing information.
+
+The test suite also bundles some fonts for use by the test suite. The
+fonts included, their licenses, and why we use them in the test suite
+are as follows:
+
+       Font            License         Distinguishing feature
+       --------        -------------   ----------------------
+       6x13.pcf        Public Domain   Bitmap font
+
+
+The test suite also bundles some images for use by the test suite. The
+images included, their licenses, and why we use them in the test suite
+are as follows:
+
+       Image           License         Distinguishing feature
+       -------------   -------------   -------------------------
+       romedalen.jpg   Public Domain   Bitmap image (image/jpeg)
+       romedalen.png   Public Domain   Bitmap image (image/png)
+
+The kind contributors of the bundled files are (in alphabetical order):
+Øyvind Kolås <pippin@freedesktop.org> Author of the original romedalen shot.
diff --git a/test/Makefile.am b/test/Makefile.am
new file mode 100644 (file)
index 0000000..0a36780
--- /dev/null
@@ -0,0 +1,465 @@
+include $(top_srcdir)/build/Makefile.am.common
+
+include $(top_srcdir)/test/Makefile.sources
+
+SUBDIRS=pdiff .
+
+# Then we have a collection of tests that are only run if certain
+# features are compiled into cairo
+if HAVE_REAL_PTHREAD
+test_sources += $(pthread_test_sources)
+endif
+
+if CAIRO_HAS_FT_FONT
+if CAIRO_HAS_FC_FONT
+test_sources += $(ft_font_test_sources)
+endif
+endif
+
+if CAIRO_HAS_GL_SURFACE
+test_sources += $(gl_surface_test_sources)
+endif
+
+# Need to add quartz-surface-source
+if CAIRO_HAS_QUARTZ_SURFACE
+test_sources += $(quartz_surface_test_sources)
+endif
+
+if CAIRO_HAS_PDF_SURFACE
+test_sources += $(pdf_surface_test_sources)
+endif
+
+if CAIRO_HAS_PS_SURFACE
+test_sources += $(ps_surface_test_sources)
+endif
+
+if CAIRO_HAS_SVG_SURFACE
+test_sources += $(svg_surface_test_sources)
+endif
+
+if CAIRO_HAS_TEST_SURFACES
+test_sources += $(test_fallback16_surface_test_sources)
+endif
+
+if CAIRO_HAS_XCB_SURFACE
+test_sources += $(xcb_surface_test_sources)
+endif
+
+if CAIRO_HAS_XLIB_SURFACE
+test_sources += $(xlib_surface_test_sources)
+endif
+
+if CAIRO_HAS_XLIB_XRENDER_SURFACE
+test_sources += $(xlib_xrender_surface_test_sources)
+endif
+
+if CAIRO_HAS_MULTI_PAGE_SURFACES
+test_sources += $(multi_page_surface_test_sources)
+endif
+
+# Include fallback-resolution (once!) if we have any of the vector surfaces
+if BUILD_ANY2PPM
+if CAIRO_HAS_SVG_SURFACE
+test = $(fallback_resolution_test_sources)
+endif
+if CAIRO_HAS_PDF_SURFACE
+test = $(fallback_resolution_test_sources)
+endif
+if CAIRO_HAS_PS_SURFACE
+test = $(fallback_resolution_test_sources)
+endif
+endif
+test_sources += $(test)
+
+noinst_PROGRAMS = cairo-test-suite$(EXEEXT) # always build
+
+TESTS += cairo-test-suite$(EXEEXT)
+
+cairo-test-constructors.c: Makefile $(test_sources) make-cairo-test-constructors.sh
+       (cd $(srcdir) && sh ./make-cairo-test-constructors.sh $(test_sources)) > $@
+
+cairo_test_suite_SOURCES =             \
+       $(cairo_test_suite_sources)     \
+       $(cairo_test_suite_headers)     \
+       $(test_sources)                 \
+       cairo-test-constructors.c
+cairo_test_suite_CFLAGS = $(AM_CFLAGS) $(real_pthread_CFLAGS) 
+cairo_test_suite_LDADD =                                       \
+       $(real_pthread_LIBS)                                    \
+       $(top_builddir)/test/pdiff/libpdiff.la                  \
+        $(top_builddir)/boilerplate/libcairoboilerplate.la     \
+       $(top_builddir)/src/libcairo.la                         \
+       $(CAIRO_LDADD)
+cairo_test_suite_DEPENDENCIES = \
+       $(top_builddir)/test/pdiff/libpdiff.la                  \
+        $(top_builddir)/boilerplate/libcairoboilerplate.la     \
+       $(top_builddir)/src/libcairo.la
+if BUILD_ANY2PPM
+cairo_test_suite_DEPENDENCIES += \
+       any2ppm$(EXEEXT)
+endif
+
+if HAVE_SHM
+EXTRA_PROGRAMS += cairo-test-trace
+cairo_test_trace_SOURCES =             \
+       cairo-test-trace.c              \
+       buffer-diff.c                   \
+       buffer-diff.h
+cairo_test_trace_CFLAGS = $(AM_CFLAGS) $(real_pthread_CFLAGS) 
+cairo_test_trace_LDADD =               \
+       $(real_pthread_LIBS)                                    \
+       $(top_builddir)/test/pdiff/libpdiff.la                  \
+       $(top_builddir)/util/cairo-script/libcairo-script-interpreter.la \
+        $(top_builddir)/boilerplate/libcairoboilerplate.la     \
+       $(top_builddir)/src/libcairo.la                         \
+       $(top_builddir)/util/cairo-missing/libcairo-missing.la  \
+       $(CAIRO_LDADD) \
+       $(SHM_LIBS)
+cairo_test_trace_DEPENDENCIES = \
+       $(top_builddir)/test/pdiff/libpdiff.la                  \
+       $(top_builddir)/util/cairo-script/libcairo-script-interpreter.la \
+        $(top_builddir)/boilerplate/libcairoboilerplate.la     \
+       $(top_builddir)/src/libcairo.la                         \
+       $(top_builddir)/util/cairo-missing/libcairo-missing.la  \
+       $(NULL)
+endif
+
+BUILT_SOURCES += cairo-test-constructors.c
+EXTRA_DIST += $(BUILT_SOURCES) $(noinst_SCRIPTS) COPYING make-cairo-test-constructors.sh run-cairo-test-suite.sh generate_refs.sh tiger.inc
+CLEANFILES += $(BUILT_SOURCES)
+
+EXTRA_DIST +=          \
+6x13.pcf               \
+index.html             \
+jp2.jp2                        \
+jpeg.jpg               \
+png.png                        \
+romedalen.jpg          \
+romedalen.png          \
+scarab.jpg             \
+surface-source.c       \
+testtable.js           \
+reference
+
+# Any test for which the code committed to CVS is expected to fail
+# should be listed here.
+#
+# This way, we can avoid being bothered by reports of bugs we are
+# aware of, but users can still report when tests start behaving in
+# unexpected ways on their system.
+#
+# Of course, before any "release" of cairo we should eliminate
+# everything from this list by fixing the bugs. (We don't necessarily
+# have to be that strict for "snapshots" though.)
+#
+# Analysis of XFAIL errors:
+# alpha-similar         - discrepancy between backends in applying color
+#                         components of a pure alpha surface
+# big-line              - range overflow of fixed-point
+# big-trap              - range overflow of fixed-point
+# degenerate-dash       - needs path editing in PS to convert degenerate
+#                         end-caps into the shapes as expected by cairo
+#                         (Or maybe PS is the correct behaviour?)
+# degenerate-path       - undefined behaviour in PS, needs path editing to
+#                         convert degenerate segments into circles/rectangles
+#                         as expected by cairo
+# device-offset-scale   - complication of pre-multiplying device_offset
+#                         into the pattern_matrix and then requiring futher
+#                         manipulation for SVG
+# extend-pad            - lacks implementation in pixman and consequently used
+#                         as an excuse for lack of support in other backends
+# fallback-resolution   - The essential problem here is that the recording-surface
+#                         has recorded a sequence of operations with one device
+#                         transformation, and we attempt to replay it with
+#                         another (basically a scale-factor for the falback
+#                         resolution). Carl begun to look at this with his
+#                         chain-of-bugs, but the can of worms is much bigger.
+#                         It appears to be a design flaw in the recording-surface
+#                         that may spread further...
+#                         My solution would be to lock Behad and Adrian in a
+#                         room, with Carl as a moderator and not let them out
+#                         until they have come up with an interface and
+#                         semantics that actually work. :-)
+# in-fill-empty-trapezoid The cairo_in_fill () function can sometimes
+#                         produce false positives when the tessellator
+#                         produces empty trapezoids and the query
+#                         point lands exactly on a trapezoid edge.
+# long-lines            - range overflow of fixed-point
+# scale-offset-image    - loss of precision converting a cairo matrix to
+# scale-offset-similar    pixman's fixed point format.
+# self-copy-overlap     - vector surfaces take snapshot of patterns in contrast
+#                         to the raster backends which don't. One solution
+#                         would be to clone overlapping areas of dst/source, so
+#                         patterns were effectively snapshotted across all
+#                         backends.
+# self-intersecting     - incremental trapezoidation of strokes can generate
+#                         overlapping traps. Needs self-intersection analysis
+#                         on cairo_traps_t after strokes.
+#                         Test case should also be expanded to hit special-case
+#                         tessellators as well.
+# surface-pattern-big...- Strange, unexplained results for SVG/PS.
+XFAIL_TESTS =                                  \
+alpha-similar$(EXEEXT)                         \
+big-line$(EXEEXT)                              \
+big-trap$(EXEEXT)                              \
+degenerate-dash$(EXEEXT)                       \
+degenerate-path$(EXEEXT)                       \
+device-offset-scale$(EXEEXT)                   \
+extend-pad$(EXEEXT)                            \
+fallback-resolution$(EXEEXT)                   \
+in-fill-empty-trapezoid$(EXEEXT)               \
+long-lines$(EXEEXT)                            \
+self-copy-overlap$(EXEEXT)                     \
+self-intersecting$(EXEEXT)                     \
+surface-pattern-big-scale-down$(EXEEXT)                \
+$(NULL)
+
+# Any test that doesn't generate a log file goes here
+NOLOG_TESTS =                  \
+fallback-resolution            \
+font-options                   \
+multi-page                     \
+pdf-features                   \
+png                            \
+ps-eps                          \
+ps-features                    \
+svg-clip                       \
+svg-surface                    \
+toy-font-face                  \
+user-data
+
+# A target to summarise the failures
+check-summary:
+       @FAILED_TESTS=""; \
+       for t in $(test_sources:.c=.log); do \
+           if grep -e '\<FAIL\>' $$t >/dev/null 2>&1; then \
+               FAILED_TESTS="$$FAILED_TESTS $${t%.log}"; \
+           fi; \
+       done; \
+       if test -n "$$FAILED_TESTS"; then \
+           echo "Failed tests:"; \
+           surfaces=""; \
+           for t in $$FAILED_TESTS; do \
+               echo -n "     $$t: "; \
+               grep -e '\<FAIL\>' $$t.log | sed -e 's/.*TARGET: \([^ ]*\).*/\1/' | sort | uniq | tr '\n' ' '; \
+               echo; \
+               for s in `grep -e '\<FAIL\>' $$t.log | sed -e 's/.*TARGET: \([^ ]*\).*/\1/' | sort | uniq`; do \
+                   ss=`echo $$s | tr '-' '_'`; \
+                   tt=`echo $$t | tr '-' '_'`; \
+                   eval $$ss=\""$${!ss} $$tt"\"; \
+                   echo $$surfaces | grep $$ss >/dev/null || surfaces="$$surfaces $$ss"; \
+               done; \
+           done; \
+           echo -n "Failures per surface - "; \
+           first=""; \
+           for s in $$surfaces; do \
+               ss=`echo $$s | tr '_' '-'`; \
+               test -n "$$first" && echo -n ", "; \
+               cnt=`echo $${!s} | wc -w`; \
+               echo -n "$$ss: $$cnt"; \
+               first="false"; \
+           done; \
+           echo "."; \
+           for s in $$surfaces; do \
+               ss=`echo $$s | tr '_' '-'`; \
+               cnt=`echo $${!s} | wc -w`; \
+               echo -n "       $$ss [$$cnt]: "; \
+               echo $${!s} | tr '_' '-'; \
+           done; \
+       fi
+
+AM_CPPFLAGS =                                  \
+       -I$(srcdir)                             \
+       -I$(srcdir)/pdiff                       \
+       -I$(top_srcdir)/boilerplate             \
+       -I$(top_srcdir)/util/cairo-missing      \
+       -I$(top_srcdir)/util/cairo-script       \
+       -I$(top_srcdir)/src                     \
+       -I$(top_builddir)/src                   \
+       $(CAIRO_CFLAGS)
+AM_LDFLAGS = $(CAIRO_LDFLAGS)
+
+$(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)/test/pdiff/libpdiff.la:
+       cd $(top_builddir)/test/pdiff && $(MAKE) $(AM_MAKEFLAGS) libpdiff.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
+
+EXTRA_PROGRAMS += imagediff png-flatten
+
+imagediff_SOURCES = \
+       imagediff.c     \
+       buffer-diff.c   \
+       buffer-diff.h
+imagediff_LDADD = \
+       $(top_builddir)/test/pdiff/libpdiff.la \
+       $(top_builddir)/src/libcairo.la
+
+png_flatten_SOURCES = png-flatten.c
+png_flatten_LDADD = $(top_builddir)/src/libcairo.la \
+                   $(CAIRO_LDADD)
+
+if BUILD_ANY2PPM
+check_PROGRAMS += any2ppm
+any2ppm_CFLAGS = $(AM_CFLAGS) $(POPPLER_CFLAGS) $(LIBRSVG_CFLAGS) $(LIBSPECTRE_CFLAGS)
+# add LDADD, so poppler/librsvg uses "our" cairo
+any2ppm_LDFLAGS = $(AM_LDFLAGS) $(CAIRO_TEST_UNDEFINED_LDFLAGS)
+any2ppm_LDADD = \
+               $(top_builddir)/util/cairo-script/libcairo-script-interpreter.la \
+               $(top_builddir)/src/libcairo.la \
+               $(CAIRO_LDADD) \
+               $(CAIROBOILERPLATE_LIBS) \
+               $(POPPLER_LIBS) \
+               $(LIBRSVG_LIBS) \
+               $(LIBSPECTRE_LIBS)
+endif
+
+if CAIRO_CAN_TEST_PDF_SURFACE
+check_PROGRAMS += pdf2png
+pdf2png_CFLAGS = $(AM_CFLAGS) $(POPPLER_CFLAGS)
+# add LDADD, so poppler uses "our" cairo
+pdf2png_LDFLAGS = $(AM_LDFLAGS) $(CAIRO_TEST_UNDEFINED_LDFLAGS)
+pdf2png_LDADD  = $(top_builddir)/src/libcairo.la \
+                $(CAIRO_LDADD) \
+                $(POPPLER_LIBS)
+endif
+
+if CAIRO_CAN_TEST_SVG_SURFACE
+check_PROGRAMS += svg2png
+svg2png_CFLAGS = $(AM_CFLAGS) $(LIBRSVG_CFLAGS)
+# add LDADD, so librsvg uses "our" cairo
+svg2png_LDFLAGS = $(AM_LDFLAGS) $(CAIRO_TEST_UNDEFINED_LDFLAGS)
+svg2png_LDADD  = $(top_builddir)/src/libcairo.la \
+                $(CAIRO_LDADD) \
+                $(LIBRSVG_LIBS)
+endif
+
+if CAIRO_HAS_SPECTRE
+check_PROGRAMS += ps2png
+ps2png_CFLAGS = $(AM_CFLAGS) $(LIBSPECTRE_CFLAGS)
+# add LDADD, so ps2png uses "our" cairo
+ps2png_LDFLAGS = $(AM_LDFLAGS) $(CAIRO_TEST_UNDEFINED_LDFLAGS)
+ps2png_LDADD  = $(top_builddir)/src/libcairo.la \
+               $(CAIRO_LDADD) \
+               $(LIBSPECTRE_LIBS)
+endif
+
+EXTRA_PROGRAMS += $(TESTS)
+
+# Do a funny transition of CAIRO_TEST_TARGET through TARGETS such that
+# one can limit tested targets both through CAIRO_TEST_TARGET env var
+# and TARGETS make var on the command line.  Same for the rest.
+TARGETS = $(CAIRO_TEST_TARGET)
+TARGETS_EXCLUDE = $(CAIRO_TEST_TARGET_EXCLUDE)
+NUM_THREADS = $(CAIRO_TEST_NUM_THREADS)
+MODE = $(CAIRO_TEST_MODE)
+
+# Same about ENV vs CAIRO_TEST_ENV.  ENV is used with "make run" only
+ENV = $(CAIRO_TEST_ENV)
+
+TESTS_ENVIRONMENT = CAIRO_TEST_MODE="$(MODE)" CAIRO_TEST_TARGET="$(TARGETS)" CAIRO_TEST_TARGET_EXCLUDE="$(TARGETS_EXCLUDE)" CAIRO_TEST_NUM_THREADS="$(NUM_THREADS)" $(ENV)
+
+EXTRA_VALGRIND_FLAGS = $(CAIRO_EXTRA_VALGRIND_FLAGS)
+VALGRIND_FLAGS = \
+       --tool=memcheck --suppressions=$(srcdir)/.valgrind-suppressions \
+       --track-origins=yes \
+       --leak-check=yes --show-reachable=yes \
+       $(EXTRA_VALGRIND_FLAGS)
+
+CLEANFILES +=                                  \
+       valgrind-log                            \
+       ref.hash                                \
+       ref.list                                \
+       png-test.png                            \
+       png.out.png                             \
+       create-for-stream.pdf                   \
+       create-for-stream.ps                    \
+       create-for-stream.svg                   \
+       svg-surface-source.out.svg              \
+       pdf-surface-source.out.pdf              \
+       ps-surface-source.out.ps                \
+       pdf-features.pdf                        \
+       pdf-mime-data.out*                      \
+       ps-features.ps                          \
+       svg-clip.svg                            \
+       svg-surface.svg                         \
+       multi-page.pdf                          \
+       multi-page.ps                           \
+       $(NULL)
+
+# This used to be a simple 'echo ${RM} *.ps *.pdf *.svg *.etc', but
+# most systems cannot handle all of our clean files together.
+# Then it became a fancy find using many GNU extensions, but then the ugly
+# reality of portability was raised and it became....
+clean-local:
+       rm -rf output
+       -${FIND} . -name '*.log'      -print | ${XARGS} ${RM}
+       -${FIND} . -name '*.[is]'     -print | ${XARGS} ${RM}
+clean-caches:
+       -${FIND} output -name '*.fail.*' -print | ${XARGS} ${RM}
+       -${FIND} output -name '*.pass.*' -print | ${XARGS} ${RM}
+
+# The following definitions both should work.
+#FAILED_TESTS = `grep -l '\<FAIL\>' $(test_sources:.c=.log) 2>/dev/null | sed -e 's/[.]log$$//' | xargs echo`
+FAILED_TESTS = `grep -l '\<FAIL\>' $(test_sources:.c=.log) 2>/dev/null | tr '\n' ' ' | sed -e 's/[.]log  */ /g; s/^ //; s/ $$//'`
+
+recheck = check CAIRO_TESTS="$(FAILED_TESTS)"
+
+# Re-checks all failed tests, i.e. tests with a log file that has a failure
+recheck:
+       @echo Re-checking failed tests
+       @$(MAKE) $(AM_MAKEFLAGS) $(recheck)
+
+# Checks tests.
+# Target doesn't fail if tests fail.
+test:
+       @$(MAKE) $(AM_MAKEFLAGS) check
+
+# Re-checks tests.
+# Target doesn't fail if tests fail.
+retest:
+       @CAIRO_TESTS="$(FAILED_TESTS)"; \
+       $(MAKE) $(AM_MAKEFLAGS) check
+
+# Run tests under a tool specified by TOOL.  For example, make run TOOL=gdb
+run:
+       $(MAKE) $(AM_MAKEFLAGS) check TESTS_ENVIRONMENT='$(TESTS_ENVIRONMENT) $(top_builddir)/libtool --mode=execute env $(TOOL)'
+
+# Check tests under valgrind.  Saves log to valgrind-log
+check-valgrind:
+       $(MAKE) $(AM_MAKEFLAGS) check TESTS_ENVIRONMENT='$(TESTS_ENVIRONMENT) CAIRO_TEST_MODE="$(MODE),foreground CAIRO_TEST_TIMEOUT=0" $(top_builddir)/libtool --mode=execute valgrind $(VALGRIND_FLAGS)' 2>&1 | tee valgrind-log
+
+#%.log: %.c cairo-test-suite
+#-./cairo-test-suite $(<:.c=)
+
+NOLOG_TESTS_LOG = $(NOLOG_TESTS:=.log)
+
+$(NOLOG_TESTS_LOG):
+       @echo dummy > $@
+
+# Identify identical reference images
+check-ref-dups:
+       @LANG=C; \
+       ( cd "$(srcdir)" && sha1sum *.ref.png | sort ) > ref.hash; \
+       join ref.hash ref.hash | grep -v -E '( .*.ref.png).*\1' | cut -d' ' -f 1-2 | sort -u
+
+results.tar:
+       @tar cf $@ index.html testtable.js *.log output/*.log; \
+       for i in output/*.fail.png ; do \
+               testname=$${i#output/} ; \
+               testname=$${testname%%.*} ; \
+               tar uf $@ $${testname}*.ref.png $${i%fail.png}out.png $${i%fail.png}diff.png ; \
+       done
+
+results.tar.gz: results.tar
+       gzip -c $< > $@
+
+.PHONY: check-valgrind test recheck retest check-ref-dups
+
+EXTRA_DIST += Makefile.win32
diff --git a/test/Makefile.sources b/test/Makefile.sources
new file mode 100644 (file)
index 0000000..48a8f12
--- /dev/null
@@ -0,0 +1,412 @@
+test_sources = \
+       a1-bug.c                                        \
+       a1-clip.c                                       \
+       a1-fill.c                                       \
+       a1-image-sample.c                               \
+       a1-mask.c                                       \
+       a1-mask-sample.c                                \
+       a1-sample.c                                     \
+       a1-traps-sample.c                               \
+       a1-rasterisation.c                              \
+       a8-clear.c                                      \
+       a8-mask.c                                       \
+       aliasing.c                                      \
+       alpha-similar.c                                 \
+       arc-infinite-loop.c                             \
+       arc-looping-dash.c                              \
+       api-special-cases.c                             \
+       big-line.c                                      \
+       big-empty-box.c                                 \
+       big-empty-triangle.c                            \
+       big-little-box.c                                \
+       big-little-triangle.c                           \
+       big-trap.c                                      \
+       bilevel-image.c                                 \
+       bug-40410.c                                     \
+       bug-bo-rectangular.c                            \
+       bug-bo-ricotz.c                                 \
+       bug-extents.c                                   \
+       bug-seams.c                                     \
+       caps.c                                          \
+       checkerboard.c                                  \
+       caps-joins.c                                    \
+       caps-joins-alpha.c                              \
+       caps-joins-curve.c                              \
+       caps-tails-curve.c                              \
+       caps-sub-paths.c                                \
+       clear.c                                         \
+       clear-source.c                                  \
+       clip-all.c                                      \
+       clip-contexts.c                                 \
+       clip-complex-shape.c                            \
+       clip-disjoint.c                                 \
+       clip-disjoint-hatching.c                        \
+       clip-device-offset.c                            \
+       clip-double-free.c                              \
+       clip-draw-unbounded.c                           \
+       clip-empty.c                                    \
+       clip-empty-group.c                              \
+       clip-empty-save.c                               \
+       clip-fill.c                                     \
+       clip-fill-no-op.c                               \
+       clip-fill-rule.c                                \
+       clip-fill-rule-pixel-aligned.c                  \
+       clip-group-shapes.c                             \
+       clip-image.c                                    \
+       clip-intersect.c                                \
+       clip-mixed-antialias.c                          \
+       clip-nesting.c                                  \
+       clip-operator.c                                 \
+       clip-push-group.c                               \
+       clip-polygons.c                                 \
+       clip-rectilinear.c                              \
+       clip-shape.c                                    \
+       clip-stroke.c                                   \
+       clip-stroke-no-op.c                             \
+       clip-text.c                                     \
+       clip-twice.c                                    \
+       clip-twice-rectangle.c                          \
+       clip-unbounded.c                                \
+       clip-zero.c                                     \
+       clipped-group.c                                 \
+       clipped-surface.c                               \
+       close-path.c                                    \
+       close-path-current-point.c                      \
+       composite-integer-translate-source.c            \
+       composite-integer-translate-over.c              \
+       composite-integer-translate-over-repeat.c       \
+       copy-path.c                                     \
+       coverage.c                                      \
+       create-for-stream.c                             \
+       create-from-png.c                               \
+       create-from-png-stream.c                        \
+       culled-glyphs.c                                 \
+       curve-to-as-line-to.c                           \
+       dash-caps-joins.c                               \
+       dash-curve.c                                    \
+       dash-infinite-loop.c                            \
+       dash-no-dash.c                                  \
+       dash-offset.c                                   \
+       dash-offset-negative.c                          \
+       dash-scale.c                                    \
+       dash-state.c                                    \
+       dash-zero-length.c                              \
+       degenerate-arc.c                                \
+       degenerate-arcs.c                               \
+       degenerate-curve-to.c                           \
+       degenerate-dash.c                               \
+       degenerate-linear-gradient.c                    \
+       degenerate-path.c                               \
+       degenerate-pen.c                                \
+       degenerate-radial-gradient.c                    \
+       degenerate-rel-curve-to.c                       \
+       drunkard-tails.c                                \
+       device-offset.c                                 \
+       device-offset-fractional.c                      \
+       device-offset-positive.c                        \
+       device-offset-scale.c                           \
+       error-setters.c                                 \
+       extend-pad.c                                    \
+       extend-pad-border.c                             \
+       extend-pad-similar.c                            \
+       extend-reflect.c                                \
+       extend-reflect-similar.c                        \
+       extend-repeat.c                                 \
+       extend-repeat-similar.c                         \
+       extended-blend.c                                \
+       fill-alpha.c                                    \
+       fill-alpha-pattern.c                            \
+       fill-and-stroke.c                               \
+       fill-and-stroke-alpha.c                         \
+       fill-and-stroke-alpha-add.c                     \
+       fill-degenerate-sort-order.c                    \
+       fill-empty.c                                    \
+       fill-image.c                                    \
+       fill-missed-stop.c                              \
+       fill-rule.c                                     \
+       filter-bilinear-extents.c                       \
+       filter-nearest-offset.c                         \
+       filter-nearest-transformed.c                    \
+       finer-grained-fallbacks.c                       \
+       font-face-get-type.c                            \
+       font-matrix-translation.c                       \
+       font-options.c                                  \
+       glyph-cache-pressure.c                          \
+       get-and-set.c                                   \
+       get-clip.c                                      \
+       get-group-target.c                              \
+       get-path-extents.c                              \
+       gradient-alpha.c                                \
+       gradient-constant-alpha.c                       \
+       gradient-zero-stops.c                           \
+       gradient-zero-stops-mask.c                      \
+       group-clip.c                                    \
+       group-paint.c                                   \
+       group-state.c                                   \
+       group-unaligned.c                               \
+       half-coverage.c                                 \
+       halo.c                                          \
+       hatchings.c                                     \
+       horizontal-clip.c                               \
+       huge-linear.c                                   \
+       huge-radial.c                                   \
+       image-surface-source.c                          \
+       image-bug-710072.c                              \
+       implicit-close.c                                \
+       infinite-join.c                                 \
+       in-fill-empty-trapezoid.c                       \
+       in-fill-trapezoid.c                             \
+       invalid-matrix.c                                \
+       inverse-text.c                                  \
+       joins.c                                         \
+       joins-loop.c                                    \
+       joins-star.c                                    \
+       joins-retrace.c                                 \
+       large-clip.c                                    \
+       large-font.c                                    \
+       large-source.c                                  \
+       large-source-roi.c                              \
+       large-twin-antialias-mixed.c                    \
+       leaky-dash.c                                    \
+       leaky-dashed-rectangle.c                        \
+       leaky-dashed-stroke.c                           \
+       leaky-polygon.c                                 \
+       line-width.c                                    \
+       line-width-large-overlap.c                      \
+       line-width-overlap.c                            \
+       line-width-scale.c                              \
+       line-width-tolerance.c                          \
+       line-width-zero.c                               \
+       linear-gradient.c                               \
+       linear-gradient-extend.c                        \
+       linear-gradient-large.c                         \
+       linear-gradient-one-stop.c                      \
+       linear-gradient-reflect.c                       \
+       linear-gradient-subset.c                        \
+       linear-step-function.c                          \
+       linear-uniform.c                                \
+       long-dashed-lines.c                             \
+       long-lines.c                                    \
+       map-to-image.c                                  \
+       mask.c                                          \
+       mask-alpha.c                                    \
+       mask-ctm.c                                      \
+       mask-glyphs.c                                   \
+       mask-surface-ctm.c                              \
+       mask-transformed-image.c                        \
+       mask-transformed-similar.c                      \
+       mesh-pattern.c                                  \
+       mesh-pattern-accuracy.c                         \
+       mesh-pattern-conical.c                          \
+       mesh-pattern-control-points.c                   \
+       mesh-pattern-fold.c                             \
+       mesh-pattern-overlap.c                          \
+       mesh-pattern-transformed.c                      \
+       mime-data.c                                     \
+       mime-surface.c                                  \
+       mime-surface-api.c                              \
+       miter-precision.c                               \
+       move-to-show-surface.c                          \
+       new-sub-path.c                                  \
+       nil-surface.c                                   \
+       operator.c                                      \
+       operator-alpha.c                                \
+       operator-alpha-alpha.c                          \
+       operator-clear.c                                \
+       operator-source.c                               \
+       over-above-source.c                             \
+       over-around-source.c                            \
+       over-below-source.c                             \
+       over-between-source.c                           \
+       overlapping-boxes.c                             \
+       overlapping-glyphs.c                            \
+       overlapping-dash-caps.c                         \
+       paint.c                                         \
+       paint-clip-fill.c                               \
+       paint-repeat.c                                  \
+       paint-source-alpha.c                            \
+       paint-with-alpha.c                              \
+       partial-clip-text.c                             \
+       partial-coverage.c                              \
+       path-append.c                                   \
+       path-stroke-twice.c                             \
+       path-precision.c                                \
+       pattern-get-type.c                              \
+       pattern-getters.c                               \
+       pixman-rotate.c                                 \
+       png.c                                           \
+       push-group.c                                    \
+       push-group-color.c                              \
+       push-group-path-offset.c                        \
+       radial-gradient.c                               \
+       radial-gradient-extend.c                        \
+       radial-outer-focus.c                            \
+       random-clips.c                  \
+       random-intersections-eo.c                       \
+       random-intersections-nonzero.c                  \
+       random-intersections-curves-eo.c                \
+       random-intersections-curves-nz.c                \
+       record.c                                        \
+       record-extend.c                                 \
+       record-mesh.c                                   \
+       recording-surface-pattern.c                     \
+       recording-surface-extend.c                      \
+       rectangle-rounding-error.c                      \
+       rectilinear-fill.c                              \
+       rectilinear-grid.c                              \
+       rectilinear-miter-limit.c                       \
+       rectilinear-dash.c                              \
+       rectilinear-stroke.c                            \
+       reflected-stroke.c                              \
+       rel-path.c                                      \
+       rgb24-ignore-alpha.c                            \
+       rotate-image-surface-paint.c                    \
+       rotated-clip.c                                  \
+       rounded-rectangle-fill.c                        \
+       rounded-rectangle-stroke.c                      \
+       scale-down-source-surface-paint.c               \
+       scale-offset-image.c                            \
+       scale-offset-similar.c                          \
+       scale-source-surface-paint.c                    \
+       scaled-font-zero-matrix.c                       \
+       stroke-ctm-caps.c                               \
+       stroke-image.c                                  \
+       stroke-open-box.c                               \
+       select-font-face.c                              \
+       select-font-no-show-text.c                      \
+       self-copy.c                                     \
+       self-copy-overlap.c                             \
+       self-intersecting.c                             \
+       set-source.c                                    \
+       show-glyphs-advance.c                           \
+       show-glyphs-many.c                              \
+       show-text-current-point.c                       \
+       shape-general-convex.c                          \
+       shape-sierpinski.c                              \
+       skew-extreme.c                                  \
+       smask.c                                         \
+       smask-fill.c                                    \
+       smask-image-mask.c                              \
+       smask-mask.c                                    \
+       smask-paint.c                                   \
+       smask-stroke.c                                  \
+       smask-text.c                                    \
+       solid-pattern-cache-stress.c                    \
+       source-clip.c                                   \
+       source-clip-scale.c                             \
+       source-surface-scale-paint.c                    \
+       spline-decomposition.c                          \
+       stroke-pattern.c                                \
+       subsurface.c                                    \
+       subsurface-image-repeat.c                       \
+       subsurface-repeat.c                             \
+       subsurface-reflect.c                            \
+       subsurface-pad.c                                \
+       subsurface-modify-child.c                       \
+       subsurface-modify-parent.c                      \
+       subsurface-outside-target.c                     \
+       subsurface-scale.c                              \
+       subsurface-similar-repeat.c                     \
+       surface-finish-twice.c                          \
+       surface-pattern.c                               \
+       surface-pattern-big-scale-down.c                \
+       surface-pattern-operator.c                      \
+       surface-pattern-scale-down.c                    \
+       surface-pattern-scale-down-extend.c             \
+       surface-pattern-scale-up.c                      \
+       text-antialias.c                                \
+       text-cache-crash.c                              \
+       text-glyph-range.c                              \
+       text-pattern.c                                  \
+       text-rotate.c                                   \
+       text-transform.c                                \
+       text-zero-len.c                                 \
+       tighten-bounds.c                                \
+       tiger.c                                         \
+       toy-font-face.c                                 \
+       transforms.c                                    \
+       translate-show-surface.c                        \
+       trap-clip.c                                     \
+       twin.c                                          \
+       twin-antialias-gray.c                           \
+       twin-antialias-mixed.c                          \
+       twin-antialias-none.c                           \
+       twin-antialias-subpixel.c                       \
+       unaligned-box.c                                 \
+       unantialiased-shapes.c                          \
+       unbounded-operator.c                            \
+       unclosed-strokes.c                              \
+       user-data.c                                     \
+       user-font.c                                     \
+       user-font-mask.c                                \
+       user-font-proxy.c                               \
+       user-font-rescale.c                             \
+       world-map.c                                     \
+       white-in-noop.c                                 \
+       xcb-huge-image-shm.c                            \
+       xcb-stress-cache.c                              \
+       xcb-snapshot-assert.c                           \
+       xcomposite-projection.c                         \
+       xlib-expose-event.c                             \
+       zero-alpha.c                                    \
+       zero-mask.c
+
+pthread_test_sources =                                 \
+       pthread-same-source.c                           \
+       pthread-show-text.c                             \
+       pthread-similar.c                               \
+       $(NULL)
+
+ft_font_test_sources = \
+       bitmap-font.c \
+       ft-font-create-for-ft-face.c \
+       ft-show-glyphs-positioning.c \
+       ft-show-glyphs-table.c \
+       ft-text-vertical-layout-type1.c \
+       ft-text-vertical-layout-type3.c \
+       ft-text-antialias-none.c
+
+gl_surface_test_sources = \
+       gl-surface-source.c
+
+quartz_surface_test_sources = quartz-surface-source.c
+
+pdf_surface_test_sources = \
+       pdf-features.c \
+       pdf-mime-data.c \
+       pdf-surface-source.c
+
+ps_surface_test_sources = \
+       ps-eps.c \
+       ps-features.c \
+       ps-surface-source.c
+
+svg_surface_test_sources = \
+       svg-surface.c \
+       svg-clip.c \
+       svg-surface-source.c
+
+xcb_surface_test_sources = \
+       xcb-surface-source.c
+
+xlib_surface_test_sources = \
+       xlib-surface.c \
+       xlib-surface-source.c
+
+xlib_xrender_surface_test_sources = get-xrender-format.c
+
+multi_page_surface_test_sources = multi-page.c
+
+fallback_resolution_test_sources = fallback-resolution.c
+
+cairo_test_suite_headers = \
+       buffer-diff.h \
+       cairo-test.h \
+       cairo-test-private.h \
+       world-map.h \
+       $(NULL)
+
+cairo_test_suite_sources = \
+       buffer-diff.c \
+       cairo-test.c \
+       cairo-test-runner.c
diff --git a/test/Makefile.win32 b/test/Makefile.win32
new file mode 100644 (file)
index 0000000..5235769
--- /dev/null
@@ -0,0 +1,35 @@
+top_srcdir = ..
+include $(top_srcdir)/build/Makefile.win32.common
+include $(top_srcdir)/test/Makefile.sources
+
+CFLAGS += -I$(top_srcdir)/boilerplate -I./pdiff
+
+TEST_LIBS = ./pdiff/$(CFG)/pdiff.lib $(top_builddir)/src/$(CFG)/cairo-static.lib $(top_builddir)/boilerplate/$(CFG)/boiler.lib
+
+all: inform $(CFG)/cairo-test-suite.exe
+
+cairo-test-constructors.c: Makefile.sources Makefile.win32 $(test_sources) make-cairo-test-constructors.sh
+       sh ./make-cairo-test-constructors.sh $(test_sources) > $@
+
+SOURCES = $(cairo_test_suite_sources) $(test_sources) cairo-test-constructors.c
+
+OBJECTS = $(patsubst %.c, $(CFG)/%-static.obj, $(SOURCES))
+
+$(CFG)/cairo-test-suite.exe: $(OBJECTS) $(TEST_LIBS)
+       @$(LD) $(CAIRO_LDFLAGS) -OUT:$@ $(OBJECTS) $(TEST_LIBS) $(CAIRO_LIBS)
+
+./pdiff/$(CFG)/pdiff.lib:
+       $(MAKE) -C pdiff -f Makefile.win32
+
+$(top_builddir)/src/$(CFG)/cairo-static.lib:
+       $(MAKE) -C $(top_srcdir)/src -f Makefile.win32
+
+$(top_builddir)/boilerplate/$(CFG)/boiler.lib:
+       $(MAKE) -C $(top_srcdir)/boilerplate -f Makefile.win32
+
+.PHONY: check test
+
+check: inform $(CFG)/cairo-test-suite.exe
+       ./$(CFG)/cairo-test-suite.exe
+
+test: inform check
diff --git a/test/README b/test/README
new file mode 100644 (file)
index 0000000..c6f8401
--- /dev/null
@@ -0,0 +1,306 @@
+Regression test suite for cairo.
+
+How to use cairo's test suite
+=============================
+Using this test should be as simple as running:
+
+       make test
+
+assuming that the cairo distribution in the directory above has been
+configured and built. The test suite here goes through some effort to
+run against the locally compiled library rather than any installed
+version, but those efforts may fall short depending on the level of your
+libtool madness.
+
+The results of the test suite run are summarized in an index.html
+file, which, when viewed in a web browser makes it quite easy to
+visually see any failed renderings alongside the corresponding
+reference image, (and a diff image as well).
+
+The test suite needs to be run before any code is committed and before
+any release. See below for hints and rules governing the use of the suite.
+
+The test suite is built as a single binary, which allows you to choose
+individual or categories of tests to run. For example, if you want to
+run all text related tests you can use:
+    ./cairo-test-suite text
+Or if you want to check the current status of known failures:
+    ./cairo-test-suite XFAIL
+This binary should be backwards-compatible with all library versions,
+allowing you to compare current versus past behaviour for any test.
+
+Tailoring tests running
+-----------------------
+There are some mechanisms to limit the tests run during "make test".
+These come very handy when doing development, but should not be used
+to circumvent the "pass" requirements listed below.
+
+To limit the backends that the tests are run against, use the
+TARGETS make variable, that can also be passed to make.
+It should contain a (space-, comma-, etc-separated) list of backends to test.
+To limit the tests run, use the CAIRO_TESTS environment variable, which
+should be a space-separated list of tests to run.  For example:
+
+  CAIRO_TESTS="zero-alpha" make test TARGETS=image,ps
+
+Another very handy mechanism when trying to fix bugs is:
+
+  make retest
+
+This will re-run the test suite, but only on tests that failed on the
+last run. So this is a much faster way of checking if changes actually
+fix bugs rather than running the entire test suite again.
+
+The test suite first compares the output from the current run against the
+previous in order to skip more expensive image comparisons . If you think
+this is interfering with the results, you can clear the cached results using:
+
+  make clean-caches
+
+Running tests under modified enviroments or tools
+-------------------------------------------------
+To run tests under a tool like gdb, one can use the run target and
+the TOOL variable.  For example:
+
+  CAIRO_TESTS=user-font make run TOOL=gdb TARGETS=pdf
+
+If you want to run under valgrind, there is a specific target for that
+that also sets a bunch of useful valgrind options.  Try:
+
+  CAIRO_TESTS=user-font make check-valgrind
+
+You can run tests under a modified environment you can use the ENV
+make variable.  However, that environment will also affect the libtool
+wrapper of the tests.  To only affect the actual test binaries, pass
+such environment as TOOL:
+
+  CAIRO_TESTS=user-font make run TOOL="LD_PRELOAD=/path/to/something.so"
+
+Getting the elusive zero failures
+---------------------------------
+It's generally been very difficult to achieve a test run with zero
+failures. The difficulties stem from the various versions of the many
+libraries that the test suite depends on, (it depends on a lot more
+than cairo itself), as well as fonts and other system-specific
+settings. If your system differs significantly from the system on
+which the reference images were generated, then you will likely see
+the test suite reporting "failures", (even if cairo is working just
+fine).
+
+We are constantly working to reduce the number of variables that need
+to be tweaked to get a clean run, (for example, by bundling fonts with
+the test suite itself), and also working to more carefully document
+the software configuration used to generate the reference images.
+
+Here are some of the relevant details:
+
+  * Your system must have a copy of the DejaVu font, the sha1sum of
+    the version used are listed in [...].  These are
+      "DejaVu Sans" (DejaVuSans.ttf) [682718dd37c3f6a4767386d3b2b27fba9b219818];
+      "DejaVu Sans Mono" (DejaVuSansMono.ttf) [1cc58255d6f9df2b08e760ca38937a2957153484];
+      "DejaVu Serif" (DejaVuSerif.ttf) [020e72e92cb2f66107dc18c70ac4214df4f13014];
+      [the DejaVu fonts can be installed from the ttf-dejavu Debian package]
+    and also
+      "Nimbus Sans L" (n019003l.pfb)
+      [which can be found in the gsfonts Debian package].
+
+  * Currently, you must be using a build of cairo using freetype
+    (cairo-ft) as the default font backend. Otherwise all tests
+    involving text are likely to fail.
+
+  * To test the pdf backend, you will want the very latest version of
+    poppler as made available via git:
+
+       git clone git://anongit.freedesktop.org/git/poppler/poppler
+
+    As of this writing, no released version of poppler contains all
+    the fixes you will need to avoid false negatives from the test
+    suite.
+
+  * To test the ps backend, you will need ghostscript version 9.04.
+
+  * Testing the xlib backend is problematic since many X server
+    drivers have bugs that are exercised by the test suite. (Or, if
+    not actual bugs, differ slightly in their output in such a way
+    that the test suite will report errors.) This can be quite handy
+    if you want to debug an X server driver, but since most people
+    don't want to do that, another option is to run against a headless
+    X server that uses only software for all rendering. One such X
+    server is Xvfb which can be started like this:
+
+       Xvfb -screen 0 1680x1024x24 -ac -nolisten tcp :2
+
+    after which the test suite can be run against it like so:
+
+       DISPLAY=:2 make test
+
+    We have been using Xvfb for testing cairo releases and ensuring
+    that all tests behave as expected with this X server.
+
+What if I can't make my system match?
+-------------------------------------
+For one reason or another, you may be unable to get a clean run of the
+test suite even if cairo is working properly, (for example, you might
+be on a system without freetype). In this case, it's still useful to
+be able to determine if code changes you make to cairo result in any
+regressions to the test suite. But it's hard to notice regressions if
+there are many failures both before and after your changes.
+
+For this scenario, you can capture the output of a run of the test
+suite before your changes, and then use the CAIRO_REF_DIR environment
+variable to use that output as the reference images for a run after
+your changes. The process looks like this:
+
+        # Before code change there may be failures we don't care about
+        make test
+
+        # Let's save those output images
+        mkdir /some/directory/
+        cp test/*-out.png /some/directory/
+
+        # hack, hack, hack
+
+        # Now to see if nothing changed:
+        CAIRO_REF_DIR=/some/directory/ make test
+
+Best practices for cairo developers
+===================================
+If we all follow the guidelines below, then both the test suite and
+cairo itself will stay much healthier, and we'll all have a lot more
+fun hacking on cairo.
+
+Before committing
+-----------------
+All tests should return a result of PASS or XFAIL. The XFAIL results
+indicate known bugs. The final message should be one of the following:
+
+       All XX tests behaved as expected (YY expected failures)
+       All XX tests passed
+
+If any tests have a status of FAIL, then the new code has caused a
+regression error which should be fixed before the code is committed.
+
+When a new bug is found
+-----------------------
+A new test case should be added by imitating the style of an existing
+test. This means adding the following files:
+
+       new-bug.c
+       new-bug-ref.png
+
+Where new-bug.c is a minimal program to demonstrate the bug, following
+the style of existing tests. The new-bug-ref.png image should contain
+the desired result of new-bug.c if the bug were fixed.
+
+Makefile.am should be edited, adding new-bug.c to both the TESTS and
+XFAIL_TESTS lists and new-bug-ref.png to EXTRA_DIST. Add new-bug to
+.gitignore, and last but not least, don't forget to "git add" the new
+files.
+
+When a new feature is added
+---------------------------
+It's important for the regression suite to keep pace with development
+of the library. So a new test should be added for each new feature.
+The work involved is similar the work described above for new bugs.
+The only distinction is that the test is expected to pass so it
+should not be added to the XFAIL_TESTS list.
+
+While working on a test
+-----------------------
+Before a bugfix or feature is ready, it may be useful to compare
+output from different builds. For convenience, you can set
+CAIRO_REF_DIR to point at a previous test directory, relative
+to the current test directory, and any previous output will be
+used by preference as reference images.
+
+When a bug is fixed
+-------------------
+The fix should be verified by running the test suite which should
+result in an "unexpected pass" for the test of interest. Rejoice as
+appropriate, then remove the relevant file name from the XFAIL_TESTS
+variable in Makefile.am.
+
+Before releasing
+----------------
+All tests should return a result of PASS for all supported (those enabled by
+default) backends, meaning all known bugs are fixed, resulting in the happy
+message:
+
+       All XX tests passed
+
+Some notes on limitations in poppler
+====================================
+One of the difficulties of our current test infrastructure is that we
+rely on external tools to convert cairo's vector output (PDF,
+PostScript, and SVG), into an image that can be used for the image
+comparison. This means that any bugs in that conversion tool will
+result in false negatives in the test suite.
+
+We've identified several such bugs in the poppler library which is
+used to convert PDF to an image. This is particularly discouraging
+because 1) poppler is free software that will be used by *many* cairo
+users, and 2) poppler calls into cairo for its rendering so it should
+be able to do a 100% faithful conversion.
+
+So we have an interest in ensuring that these poppler bugs get fixed
+sooner rather than later. As such, we're trying to be good citizens by
+reporting all such poppler bugs that we identify to the poppler
+bugzilla. Here's a tracking bug explaining the situation:
+
+       Poppler does not yet handle everything in the cairo test suite
+       https://bugs.freedesktop.org/show_bug.cgi?id=12143
+
+Here's the rule: If a cairo-pdf test reports a failure, but viewing
+the resulting PDF file with acroread suggests that the PDF itself is
+correct, then there's likely a bug in poppler. In this case, we can
+simply report the poppler bug, (making it block 12143 above), post the
+PDF result from the test suite, and list the bug in this file. Once
+we've done this, we can capture poppler's buggy output as a
+pdf-specific reference image so that the test suite will regard the
+test as passing, (and we'll ensure there is no regression).
+
+Once the poppler bug gets fixed, the test suite will start reporting a
+false negative again, and this will be easy to fix by simply removing
+the pdf-specific reference image.
+
+Here are the reported poppler bugs and the tests they affect:
+
+Poppler doesn't correctly handle gradients with transparency
+https://bugs.freedesktop.org/show_bug.cgi?id=12144
+--------------------------------------------------
+fill-alpha-pattern
+gradient-alpha
+gradient-constant-alpha
+linear-gradient
+linear-gradient-reflect
+radial-gradient
+trap-clip
+
+Poppler should paint images with CAIRO_EXTEND_PAD
+https://bugs.freedesktop.org/show_bug.cgi?id=14578
+--------------------------------------------------
+paint-source-alpha
+paint-with-alpha
+rotate-image-surface-paint
+scale-source-surface-paint
+
+Incorrect clipping of group object (regression?)
+https://bugs.freedesktop.org/show_bug.cgi?id=14580
+--------------------------------------------------
+push-group
+
+spurious horizontal stripes in color gradients
+https://bugs.freedesktop.org/show_bug.cgi?id=10942
+--------------------------------------------------
+smask 
+smask-fill 
+smask-image-mask 
+smask-mask 
+smask-paint 
+smask-stroke 
+smask-text
+
+Ghostscript does not correctly render small miters
+http://bugs.ghostscript.com/show_bug.cgi?id=690098
+--------------------------------------------------
+miter-precision
diff --git a/test/a1-bug.c b/test/a1-bug.c
new file mode 100644 (file)
index 0000000..9166ff5
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "cairo-test.h"
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    static const struct point {
+       double x;
+       double y;
+    } xy[] = {
+       { 627.016212, 221.749777 },
+       { 756.120787, 221.749777 },
+       { 756.120787, 557.602766 },
+       { 626.952721, 557.602766 },
+       { 626.548456, 493.315729 },
+    };
+    unsigned int i;
+
+    cairo_set_source_rgb (cr, 0, 0, 0);
+    cairo_paint (cr);
+
+    for (i = 0; i < ARRAY_LENGTH (xy); i++)
+       cairo_line_to (cr, xy[i].x, xy[i].y);
+
+    cairo_set_source_rgb (cr, 1, 0, 0);
+    cairo_fill_preserve (cr);
+
+    cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
+    cairo_set_source_rgb (cr, 0, 1, 0);
+    cairo_fill (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (a1_bug,
+           "Check the fidelity of the rasterisation.",
+           "a1 raster", /* keywords */
+           "target=raster", /* requirements */
+           1000, 800,
+           NULL, draw)
diff --git a/test/a1-clip.c b/test/a1-clip.c
new file mode 100644 (file)
index 0000000..0e84cd3
--- /dev/null
@@ -0,0 +1,175 @@
+/*
+ * Copyright © 2008 Red Hat, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ */
+
+#include "cairo-test.h"
+
+#define POINTS 10
+#define STEP   (1.0 / POINTS)
+#define PAD    1
+#define WIDTH  (PAD + POINTS * 2 + PAD)
+#define HEIGHT (WIDTH)
+
+static cairo_test_status_t
+paint (cairo_t *cr, int width, int height)
+{
+    int i, j;
+
+    /* Fill background white */
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_paint (cr);
+
+    /* Draw in black */
+    cairo_set_source_rgb (cr, 0, 0, 0);
+
+    cairo_translate (cr, PAD, PAD);
+    cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
+
+    for (i = 0; i < POINTS; i++)
+       for (j = 0; j < POINTS; j++) {
+           cairo_save (cr);
+           cairo_rectangle (cr, 2 * i + i * STEP, 2 * j + j * STEP, 1, 1);
+           cairo_clip (cr);
+           cairo_paint (cr);
+           cairo_restore (cr);
+       }
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+static cairo_test_status_t
+fill_equal (cairo_t *cr, int width, int height)
+{
+    int i, j;
+
+    /* Fill background white */
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_paint (cr);
+
+    /* Draw in black */
+    cairo_set_source_rgb (cr, 0, 0, 0);
+
+    cairo_translate (cr, PAD, PAD);
+    cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
+
+    for (i = 0; i < POINTS; i++)
+       for (j = 0; j < POINTS; j++) {
+           cairo_save (cr);
+           cairo_rectangle (cr, 2 * i + i * STEP, 2 * j + j * STEP, 1, 1);
+           cairo_clip_preserve (cr);
+           cairo_fill (cr);
+           cairo_restore (cr);
+       }
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+static cairo_test_status_t
+fill (cairo_t *cr, int width, int height)
+{
+    int i, j;
+
+    /* Fill background white */
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_paint (cr);
+
+    /* Draw in black */
+    cairo_set_source_rgb (cr, 0, 0, 0);
+
+    cairo_translate (cr, PAD, PAD);
+    cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
+
+    for (i = 0; i < POINTS; i++)
+       for (j = 0; j < POINTS; j++) {
+           cairo_save (cr);
+           cairo_rectangle (cr, 2 * i + i * STEP, 2 * j + j * STEP, 1, 1);
+           cairo_clip (cr);
+           cairo_rectangle (cr, 2 * i, 2 * j, 2, 2);
+           cairo_fill (cr);
+           cairo_restore (cr);
+       }
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+static cairo_test_status_t
+stroke (cairo_t *cr, int width, int height)
+{
+    int i, j;
+
+    /* Fill background white */
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_paint (cr);
+
+    /* Draw in black */
+    cairo_set_source_rgb (cr, 0, 0, 0);
+
+    cairo_translate (cr, PAD, PAD);
+    cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
+
+    cairo_set_line_width (cr, 2);
+    cairo_set_line_cap (cr, CAIRO_LINE_CAP_BUTT);
+
+    for (i = 0; i < POINTS; i++)
+       for (j = 0; j < POINTS; j++) {
+           cairo_save (cr);
+           cairo_rectangle (cr, 2 * i + i * STEP, 2 * j + j * STEP, 1, 1);
+           cairo_clip (cr);
+           cairo_move_to (cr, 2 * i, 2 * j + 1);
+           cairo_line_to (cr, 2 * i + 2, 2 * j + 1);
+           cairo_stroke (cr);
+           cairo_restore (cr);
+       }
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (a1_clip_paint,
+           "Test sample position when drawing trapezoids with ANTIALIAS_NONE",
+           "alpha, clip", /* keywords */
+           "target=raster", /* requirements */
+           WIDTH, HEIGHT,
+           NULL, paint)
+
+CAIRO_TEST (a1_clip_fill,
+           "Test sample position when drawing trapezoids with ANTIALIAS_NONE",
+           "alpha, clip", /* keywords */
+           "target=raster", /* requirements */
+           WIDTH, HEIGHT,
+           NULL, fill)
+
+CAIRO_TEST (a1_clip_fill_equal,
+           "Test sample position when drawing trapezoids with ANTIALIAS_NONE",
+           "alpha, clip", /* keywords */
+           "target=raster", /* requirements */
+           WIDTH, HEIGHT,
+           NULL, fill_equal)
+
+CAIRO_TEST (a1_clip_stroke,
+           "Test sample position when drawing trapezoids with ANTIALIAS_NONE",
+           "alpha, clip", /* keywords */
+           "target=raster", /* requirements */
+           WIDTH, HEIGHT,
+           NULL, stroke)
diff --git a/test/a1-fill.c b/test/a1-fill.c
new file mode 100644 (file)
index 0000000..8c8b8c8
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * Copyright © 2011 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+/* Exercise https://bugs.freedesktop.org/show_bug.cgi?id=31604 */
+
+#include "cairo-test.h"
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_surface_t *a1;
+    cairo_t *cr2;
+
+    a1 = cairo_image_surface_create (CAIRO_FORMAT_A1, 100, 100);
+    cr2 = cairo_create (a1);
+    cairo_surface_destroy (a1);
+
+    cairo_set_operator (cr2, CAIRO_OPERATOR_SOURCE);
+    cairo_rectangle (cr2, 10, 10, 80, 80);
+    cairo_set_source_rgb (cr2, 1, 1, 1);
+    cairo_fill (cr2);
+    cairo_rectangle (cr2, 20, 20, 60, 60);
+    cairo_set_source_rgb (cr2, 0, 0, 0);
+    cairo_fill (cr2);
+
+    a1 = cairo_surface_reference (cairo_get_target (cr2));
+    cairo_destroy (cr2);
+
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_paint (cr);
+
+    cairo_set_source_rgb (cr, 1, 0, 0);
+    cairo_mask_surface (cr, a1, 0, 0);
+    cairo_surface_destroy (a1);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (a1_fill,
+           "Test filling of an a1-surface and use as mask",
+           "a1, alpha, fill, mask", /* keywords */
+           "target=raster", /* requirements */
+           100, 100,
+           NULL, draw)
diff --git a/test/a1-image-sample.c b/test/a1-image-sample.c
new file mode 100644 (file)
index 0000000..3c349af
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * Copyright © 2008 Red Hat, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ */
+
+#include "cairo-test.h"
+
+#define POINTS 10
+#define STEP   (1.0 / POINTS)
+#define PAD    1
+#define WIDTH  (PAD + POINTS * 2 + PAD)
+#define HEIGHT (WIDTH)
+
+/* A single, black pixel */
+static const uint32_t black_pixel = 0xff000000;
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    int i, j;
+    cairo_surface_t *surface;
+
+    surface = cairo_image_surface_create_for_data ((unsigned char *) &black_pixel,
+                                                  CAIRO_FORMAT_ARGB32,
+                                                  1, 1, 4);
+
+    /* Fill background white */
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_paint (cr);
+
+    cairo_translate (cr, PAD, PAD);
+
+    for (i = 0; i < POINTS; i++)
+       for (j = 0; j < POINTS; j++) {
+           cairo_set_source_surface (cr, surface,
+                                     2 * i + i * STEP, 2 * j + j * STEP);
+           cairo_pattern_set_filter (cairo_get_source (cr),
+                                     CAIRO_FILTER_NEAREST);
+           cairo_paint (cr);
+       }
+
+    cairo_surface_destroy (surface);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (a1_image_sample,
+           "Test sample position when drawing images with FILTER_NEAREST",
+           "image, alpha", /* keywords */
+           "target=raster", /* requirements */
+           WIDTH, HEIGHT,
+           NULL, draw)
diff --git a/test/a1-mask-sample.c b/test/a1-mask-sample.c
new file mode 100644 (file)
index 0000000..4214e8f
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * Copyright © 2008 Red Hat, Inc.
+ * Copyright © 2010 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ *         Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+
+#define POINTS 10
+#define STEP   (1.0 / POINTS)
+#define PAD    1
+#define WIDTH  (PAD + POINTS * 2 + PAD)
+#define HEIGHT (WIDTH)
+
+/* A single, opaque pixel */
+static const uint32_t black_pixel = 0xffffffff;
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_surface_t *surface;
+    cairo_pattern_t *mask;
+    int i, j;
+
+    surface = cairo_image_surface_create_for_data ((unsigned char *) &black_pixel,
+                                                  CAIRO_FORMAT_A8,
+                                                  1, 1, 4);
+    mask = cairo_pattern_create_for_surface (surface);
+    cairo_pattern_set_filter (mask, CAIRO_FILTER_NEAREST);
+    cairo_surface_destroy (surface);
+
+    /* Fill background white */
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_paint (cr);
+
+    cairo_translate (cr, PAD, PAD);
+
+    cairo_set_source_rgb (cr, 0, 0, 0);
+    for (i = 0; i < POINTS; i++) {
+       for (j = 0; j < POINTS; j++) {
+           cairo_matrix_t m;
+
+           cairo_matrix_init_translate (&m,
+                                        -(2 * i + i * STEP),
+                                        -(2 * j + j * STEP));
+           cairo_pattern_set_matrix (mask, &m);
+           cairo_mask (cr, mask);
+       }
+    }
+
+    cairo_pattern_destroy (mask);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (a1_mask_sample,
+           "Test sample position when masking with FILTER_NEAREST",
+           "image, alpha", /* keywords */
+           "target=raster", /* requirements */
+           WIDTH, HEIGHT,
+           NULL, draw)
diff --git a/test/a1-mask.c b/test/a1-mask.c
new file mode 100644 (file)
index 0000000..c52aa9d
--- /dev/null
@@ -0,0 +1,202 @@
+/*
+ * Copyright © Jeff Muizelaar
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * JEFF MUIZELAAR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Authors: Jeff Muizelaar <jeff@infidigm.net>
+ *         Carl Worth <cworth@cworth.org>
+ *         Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+
+#define MASK_WIDTH 10
+#define MASK_HEIGHT 8
+
+#ifdef WORDS_BIGENDIAN
+#define MASK 0x28, 0x55
+#else
+#define MASK 0x14, 0xAA
+#endif
+static unsigned char mask[(MASK_WIDTH + 7) / 8 * MASK_HEIGHT] = {
+    MASK,
+    MASK,
+    MASK,
+    MASK,
+    MASK,
+    MASK,
+    MASK,
+    MASK,
+};
+
+static cairo_test_status_t
+check_status (const cairo_test_context_t *ctx,
+             cairo_status_t status,
+             cairo_status_t expected)
+{
+    if (status == expected)
+       return CAIRO_TEST_SUCCESS;
+
+    cairo_test_log (ctx,
+                   "Error: Expected status value %d (%s), received %d (%s)\n",
+                   expected,
+                   cairo_status_to_string (expected),
+                   status,
+                   cairo_status_to_string (status));
+    return CAIRO_TEST_FAILURE;
+}
+
+static cairo_test_status_t
+test_surface_with_width_and_stride (const cairo_test_context_t *ctx,
+                                   int width, int stride,
+                                   cairo_status_t expected)
+{
+    cairo_test_status_t status;
+    cairo_surface_t *surface;
+    cairo_t *cr;
+    int len;
+    unsigned char *data;
+
+    cairo_test_log (ctx,
+                   "Creating surface with width %d and stride %d\n",
+                   width, stride);
+
+    len = stride;
+    if (len < 0)
+       len = -len;
+    data = xmalloc (len);
+
+    surface = cairo_image_surface_create_for_data (data, CAIRO_FORMAT_A1,
+                                                  width, 1, stride);
+    cr = cairo_create (surface);
+
+    cairo_paint (cr);
+
+    status = check_status (ctx, cairo_surface_status (surface), expected);
+    if (status)
+       goto BAIL;
+
+    status = check_status (ctx, cairo_status (cr), expected);
+    if (status)
+       goto BAIL;
+
+  BAIL:
+    cairo_destroy (cr);
+    cairo_surface_destroy (surface);
+    free (data);
+    return status;
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int dst_width, int dst_height)
+{
+    unsigned char *mask_aligned;
+    cairo_surface_t *surface;
+
+    surface = cairo_image_surface_create (CAIRO_FORMAT_A1,
+                                         MASK_WIDTH,
+                                         MASK_HEIGHT);
+
+    mask_aligned = cairo_image_surface_get_data (surface);
+    if (mask_aligned != NULL) {
+       int stride = cairo_image_surface_get_stride (surface), row;
+       const unsigned char *src = mask;
+       unsigned char *dst = mask_aligned;
+       for (row = 0; row < MASK_HEIGHT; row++) {
+           memcpy (dst, src, (MASK_WIDTH + 7) / 8);
+           src += (MASK_WIDTH + 7) / 8;
+           dst += stride;
+       }
+    }
+    cairo_surface_mark_dirty (surface);
+
+    /* Paint background blue */
+    cairo_set_source_rgb (cr, 0, 0, 1); /* blue */
+    cairo_paint (cr);
+
+    /* Then paint red through our mask */
+    cairo_set_source_rgb (cr, 1, 0, 0); /* red */
+    cairo_mask_surface (cr, surface, 0, 0);
+    cairo_surface_destroy (surface);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+static cairo_test_status_t
+preamble (cairo_test_context_t *ctx)
+{
+    cairo_test_status_t status = CAIRO_TEST_SUCCESS;
+    int test_width;
+
+    /* first check the API strictness */
+    for (test_width = 0; test_width < 40; test_width++) {
+       int test_stride = (test_width + 7) / 8;
+       int stride = cairo_format_stride_for_width (CAIRO_FORMAT_A1,
+                                                   test_width);
+       cairo_status_t expected;
+
+       /* First create a surface using the width as the stride,
+        * (most of these should fail).
+        */
+       expected = (stride == test_stride) ?
+           CAIRO_STATUS_SUCCESS : CAIRO_STATUS_INVALID_STRIDE;
+
+       status = test_surface_with_width_and_stride (ctx,
+                                                    test_width,
+                                                    test_stride,
+                                                    expected);
+       if (status)
+           return status;
+
+       status = test_surface_with_width_and_stride (ctx,
+                                                    test_width,
+                                                    -test_stride,
+                                                    expected);
+       if (status)
+           return status;
+
+
+       /* Then create a surface using the correct stride,
+        * (should always succeed).
+        */
+       status = test_surface_with_width_and_stride (ctx,
+                                                    test_width,
+                                                    stride,
+                                                    CAIRO_STATUS_SUCCESS);
+       if (status)
+           return status;
+
+       status = test_surface_with_width_and_stride (ctx,
+                                                    test_width,
+                                                    -stride,
+                                                    CAIRO_STATUS_SUCCESS);
+       if (status)
+           return status;
+    }
+
+    return status;
+}
+
+CAIRO_TEST (a1_mask,
+            "test masks of CAIRO_FORMAT_A1",
+           "alpha, mask", /* keywords */
+           NULL, /* requirements */
+           MASK_WIDTH, MASK_HEIGHT,
+           preamble, draw)
diff --git a/test/a1-rasterisation.c b/test/a1-rasterisation.c
new file mode 100644 (file)
index 0000000..b59090a
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2010 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+
+/*
+ * Test the fidelity of the rasterisation, paying careful attention to rounding.
+ */
+
+#include "../src/cairo-fixed-type-private.h"
+#define PRECISION (int)(1 << CAIRO_FIXED_FRAC_BITS)
+
+#define WIDTH ((PRECISION/2+1)*3)
+#define HEIGHT ((PRECISION/2+1)*3)
+
+#define SUBPIXEL(v) ((v)/(double)(PRECISION/2))
+
+static cairo_test_status_t
+rectangles (cairo_t *cr, int width, int height)
+{
+    int x, y;
+
+    cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
+    cairo_paint (cr);
+
+    cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
+    cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
+
+    for (x = 0; x < WIDTH; x += 3) {
+       for (y = 0; y < HEIGHT; y += 3) {
+           cairo_rectangle (cr, x + SUBPIXEL (y/3) - .5, y + SUBPIXEL (x/3) - .5, .5, .5);
+       }
+    }
+    cairo_fill (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+static cairo_test_status_t
+triangles (cairo_t *cr, int width, int height)
+{
+    int x, y;
+
+    cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
+    cairo_paint (cr);
+
+    cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
+    cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
+
+    for (x = 0; x < WIDTH; x += 3) {
+       for (y = 0; y < HEIGHT; y += 3) {
+           /* a rectangle with a diagonal to force tessellation */
+           cairo_move_to (cr, x + SUBPIXEL (y/3) - .5, y + SUBPIXEL (x/3) - .5);
+           cairo_rel_line_to (cr, .5, .5);
+           cairo_rel_line_to (cr, 0, -.5);
+           cairo_rel_line_to (cr, -.5, 0);
+           cairo_rel_line_to (cr, 0, .5);
+           cairo_rel_line_to (cr, .5, 0);
+       }
+    }
+    cairo_fill (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (a1_rasterisation_rectangles,
+           "Check the fidelity of the rasterisation.",
+           "rasterisation", /* keywords */
+           "target=raster", /* requirements */
+           WIDTH, HEIGHT,
+           NULL, rectangles)
+
+CAIRO_TEST (a1_rasterisation_triangles,
+           "Check the fidelity of the rasterisation.",
+           "rasterisation", /* keywords */
+           "target=raster", /* requirements */
+           WIDTH, HEIGHT,
+           NULL, triangles)
diff --git a/test/a1-sample.c b/test/a1-sample.c
new file mode 100644 (file)
index 0000000..977dfc8
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * Copyright © 2011 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+
+#define WIDTH  (256) //CAIRO_FIXED_ONE
+#define HEIGHT (WIDTH)
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    int i, j;
+
+    /* Fill background white */
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_paint (cr);
+
+    cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
+    cairo_set_source_rgb (cr, 0, 0, 0);
+
+    /* Only the single rectangle that covers the centre pixel should be filled*/
+    for (i = 0; i < 256; i++)
+       for (j = 0; j < 256; j++) {
+           cairo_rectangle (cr, i + i/256., j + j/256., 1/256., 1/256.);
+           cairo_fill (cr);
+       }
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (a1_sample,
+           "Tests unantialiased rendering of a quantum box",
+           " alpha", /* keywords */
+           "target=raster", /* requirements */
+           WIDTH, HEIGHT,
+           NULL, draw)
diff --git a/test/a1-traps-sample.c b/test/a1-traps-sample.c
new file mode 100644 (file)
index 0000000..5bb97bb
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Copyright © 2008 Red Hat, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ */
+
+#include "cairo-test.h"
+
+#define POINTS 10
+#define STEP   (1.0 / POINTS)
+#define PAD    1
+#define WIDTH  (PAD + POINTS * 2 + PAD)
+#define HEIGHT (WIDTH)
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    int i, j;
+
+    /* Fill background white */
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_paint (cr);
+
+    /* Draw in black */
+    cairo_set_source_rgb (cr, 0, 0, 0);
+
+    cairo_translate (cr, PAD, PAD);
+    cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
+
+    for (i = 0; i < POINTS; i++)
+       for (j = 0; j < POINTS; j++) {
+           cairo_rectangle (cr, 2 * i + i * STEP, 2 * j + j * STEP, 1, 1);
+           cairo_fill (cr);
+       }
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (a1_traps_sample,
+           "Test sample position when drawing trapezoids with ANTIALIAS_NONE",
+           "alpha, traps", /* keywords */
+           "target=raster", /* requirements */
+           WIDTH, HEIGHT,
+           NULL, draw)
diff --git a/test/a8-clear.c b/test/a8-clear.c
new file mode 100644 (file)
index 0000000..1459f83
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Copyright © 2010 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ *
+ * Based on a bug snippet by Jeremy Moles <jeremy@emperorlinux.com>
+ */
+
+#include "cairo-test.h"
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_pattern_t *mask;
+
+    cairo_set_source_rgb (cr, 1, 0, 0);
+    cairo_paint (cr);
+
+    cairo_push_group_with_content (cr, CAIRO_CONTENT_ALPHA);
+    {
+       cairo_set_source_rgb (cr, 1, 1, 1);
+       cairo_paint (cr);
+
+       cairo_move_to (cr, 0, 0);
+       cairo_line_to (cr, width, height);
+       cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
+       cairo_set_line_width (cr, 10);
+       cairo_stroke (cr);
+    }
+    mask = cairo_pop_group (cr);
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_mask (cr, mask);
+    cairo_pattern_destroy (mask);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (a8_clear,
+           "Test clear on an a8 surface",
+           "a8, clear", /* keywords */
+           NULL, /* requirements */
+           40, 40,
+           NULL, draw)
+
diff --git a/test/a8-mask.c b/test/a8-mask.c
new file mode 100644 (file)
index 0000000..ec70812
--- /dev/null
@@ -0,0 +1,201 @@
+/*
+ * Copyright © Jeff Muizelaar
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * JEFF MUIZELAAR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Authors: Jeff Muizelaar <jeff@infidigm.net>
+ *         Carl Worth <cworth@cworth.org>
+ */
+
+#include "cairo-test.h"
+
+#define MASK_WIDTH 8
+#define MASK_HEIGHT 8
+
+static unsigned char mask[MASK_WIDTH * MASK_HEIGHT] = {
+    0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0x0, 0x0,
+    0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0x0, 0x0,
+    0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0x0, 0x0,
+    0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0x0, 0x0,
+    0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0x0, 0x0,
+    0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0x0, 0x0,
+    0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0x0, 0x0,
+    0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0x0, 0x0,
+};
+
+static cairo_test_status_t
+check_status (const cairo_test_context_t *ctx,
+             cairo_status_t status,
+             cairo_status_t expected)
+{
+    if (status == expected)
+       return CAIRO_TEST_SUCCESS;
+
+    cairo_test_log (ctx,
+                   "Error: Expected status value %d (%s), received %d (%s)\n",
+                   expected,
+                   cairo_status_to_string (expected),
+                   status,
+                   cairo_status_to_string (status));
+    return CAIRO_TEST_FAILURE;
+}
+
+static cairo_test_status_t
+test_surface_with_width_and_stride (const cairo_test_context_t *ctx,
+                                   int width, int stride,
+                                   cairo_status_t expected)
+{
+    cairo_test_status_t status;
+    cairo_surface_t *surface;
+    cairo_t *cr;
+    int len;
+    unsigned char *data;
+
+    cairo_test_log (ctx,
+                   "Creating surface with width %d and stride %d\n",
+                   width, stride);
+
+    len = stride;
+    if (len < 0)
+       len = -len;
+    data = xmalloc (len);
+
+    surface = cairo_image_surface_create_for_data (data, CAIRO_FORMAT_A8,
+                                                  width, 1, stride);
+    cr = cairo_create (surface);
+
+    cairo_paint (cr);
+
+    status = check_status (ctx, cairo_surface_status (surface), expected);
+    if (status)
+       goto BAIL;
+
+    status = check_status (ctx, cairo_status (cr), expected);
+    if (status)
+       goto BAIL;
+
+  BAIL:
+    cairo_destroy (cr);
+    cairo_surface_destroy (surface);
+    free (data);
+    return status;
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int dst_width, int dst_height)
+{
+    int stride, row;
+    unsigned char *src, *dst, *mask_aligned;
+    cairo_surface_t *surface;
+
+    /* Now test actually drawing through our mask data, allocating and
+     * copying with the proper stride. */
+    stride = cairo_format_stride_for_width (CAIRO_FORMAT_A8,
+                                           MASK_WIDTH);
+
+    mask_aligned = xmalloc (stride * MASK_HEIGHT);
+
+    src = mask;
+    dst = mask_aligned;
+    for (row = 0; row < MASK_HEIGHT; row++) {
+       memcpy (dst, src, MASK_WIDTH);
+       src += MASK_WIDTH;
+       dst += stride;
+    }
+
+    surface = cairo_image_surface_create_for_data (mask_aligned,
+                                                  CAIRO_FORMAT_A8,
+                                                  MASK_WIDTH,
+                                                  MASK_HEIGHT,
+                                                  stride);
+
+    /* Paint background blue */
+    cairo_set_source_rgb (cr, 0, 0, 1); /* blue */
+    cairo_paint (cr);
+
+    /* Then paint red through our mask */
+    cairo_set_source_rgb (cr, 1, 0, 0); /* red */
+    cairo_mask_surface (cr, surface, 0, 0);
+    cairo_surface_destroy (surface);
+
+    free (mask_aligned);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+static cairo_test_status_t
+preamble (cairo_test_context_t *ctx)
+{
+    cairo_test_status_t status = CAIRO_TEST_SUCCESS;
+    int test_width;
+
+    for (test_width = 0; test_width < 40; test_width++) {
+       int stride = cairo_format_stride_for_width (CAIRO_FORMAT_A8,
+                                               test_width);
+       cairo_status_t expected;
+
+       /* First create a surface using the width as the stride,
+        * (most of these should fail).
+        */
+       expected = (stride == test_width) ?
+           CAIRO_STATUS_SUCCESS : CAIRO_STATUS_INVALID_STRIDE;
+
+       status = test_surface_with_width_and_stride (ctx,
+                                                    test_width,
+                                                    test_width,
+                                                    expected);
+       if (status)
+           return status;
+
+       status = test_surface_with_width_and_stride (ctx,
+                                                    test_width,
+                                                    -test_width,
+                                                    expected);
+       if (status)
+           return status;
+
+
+       /* Then create a surface using the correct stride,
+        * (should always succeed).
+        */
+       status = test_surface_with_width_and_stride (ctx,
+                                                    test_width,
+                                                    stride,
+                                                    CAIRO_STATUS_SUCCESS);
+       if (status)
+           return status;
+
+       status = test_surface_with_width_and_stride (ctx,
+                                                    test_width,
+                                                    -stride,
+                                                    CAIRO_STATUS_SUCCESS);
+       if (status)
+           return status;
+    }
+
+    return status;
+}
+
+CAIRO_TEST (a8_mask,
+           "test masks of CAIRO_FORMAT_A8",
+           "alpha, mask", /* keywords */
+           NULL, /* requirements */
+           8, 8,
+           preamble, draw)
diff --git a/test/aliasing.c b/test/aliasing.c
new file mode 100644 (file)
index 0000000..73c1f1a
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2010 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+
+/* A fun little test to explore color fringing in various experimental
+ * subpixel rasterisation techniques.
+ */
+
+#define WIDTH 60
+#define HEIGHT 40
+
+static const  struct color {
+    double red, green, blue;
+} color[] = {
+    { 1, 1, 1 },
+    { 0, 0, 0 },
+    { 1, 0, 0 },
+    { 0, 1, 0 },
+    { 0, 0, 1 },
+    { 1, 1, 0 },
+    { 0, 1, 1 },
+    { 1, 0, 1 },
+    { .5, .5, .5 },
+};
+
+#define NUM_COLORS ARRAY_LENGTH (color)
+
+static void
+object (cairo_t *cr, const struct color *fg, const struct color *bg)
+{
+    cairo_set_source_rgb (cr, bg->red, bg->green, bg->blue);
+    cairo_rectangle (cr, 0, 0, WIDTH, HEIGHT);
+    cairo_fill (cr);
+
+    cairo_set_source_rgb (cr, fg->red, fg->green, fg->blue);
+    cairo_save (cr);
+    cairo_scale (cr, WIDTH, HEIGHT);
+    cairo_arc (cr, .5, .5, .5 - 4. / MAX (WIDTH, HEIGHT), 0, 2 * M_PI);
+    cairo_fill (cr);
+    cairo_arc (cr, .5, .5, .5 - 2. / MAX (WIDTH, HEIGHT), 0, 2 * M_PI);
+    cairo_restore (cr);
+    cairo_set_line_width (cr, 1.);
+    cairo_stroke (cr);
+
+    cairo_set_source_rgb (cr, bg->red, bg->green, bg->blue);
+    cairo_set_line_width (cr, 4.);
+    cairo_move_to (cr, 4, HEIGHT-4);
+    cairo_line_to (cr, WIDTH-12, 4);
+    cairo_move_to (cr, 12, HEIGHT-4);
+    cairo_line_to (cr, WIDTH-4, 4);
+    cairo_stroke (cr);
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    unsigned int i, j;
+
+    for (i = 0; i < NUM_COLORS; i++) {
+       for (j = 0; j < NUM_COLORS; j++) {
+           cairo_save (cr);
+           cairo_translate (cr, i * WIDTH, j * HEIGHT);
+           object (cr, &color[i], &color[j]);
+           cairo_restore (cr);
+       }
+    }
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (aliasing,
+           "Check for subpixel aliasing and color fringing",
+           "rasterisation", /* keywords */
+           "target=raster", /* requirements */
+           NUM_COLORS * WIDTH, NUM_COLORS * HEIGHT,
+           NULL, draw)
diff --git a/test/alpha-similar.c b/test/alpha-similar.c
new file mode 100644 (file)
index 0000000..a9411d1
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2008 Chris Wilson
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Chris Wilson not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Chris Wilson makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * CHRIS WILSON DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL CHRIS WILSON BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+
+static cairo_surface_t *
+create_source (cairo_surface_t *target, int width, int height)
+{
+    cairo_surface_t *similar;
+    cairo_t *cr;
+
+    similar = cairo_surface_create_similar (target,
+                                           CAIRO_CONTENT_ALPHA,
+                                           width, height);
+    cr = cairo_create (similar);
+    cairo_surface_destroy (similar);
+
+    cairo_set_source_rgba (cr, 1, 0, 0, .5);
+    cairo_paint (cr);
+
+    similar = cairo_surface_reference (cairo_get_target (cr));
+    cairo_destroy (cr);
+
+    return similar;
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_surface_t *source;
+
+    source = create_source (cairo_get_target (cr), width, height);
+    cairo_set_source_surface (cr, source, 0, 0);
+    cairo_surface_destroy (source);
+
+    cairo_paint (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (alpha_similar,
+           "Tests creation of similar alpha surfaces"
+           "\nApplication of a pure-alpha similar source is inconsistent across backends.",
+           "alpha, similar", /* keywords */
+           NULL, /* requirements */
+           10, 10,
+           NULL, draw)
+
diff --git a/test/any2ppm.c b/test/any2ppm.c
new file mode 100644 (file)
index 0000000..40c304b
--- /dev/null
@@ -0,0 +1,875 @@
+/*
+ * Copyright © 2008 Chris Wilson
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Chris Wilson not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Chris Wilson makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * CHRIS WILSON DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL CHRIS WILSON BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ *
+ * Contributor(s):
+ *     Carlos Garcia Campos <carlosgc@gnome.org>
+ *
+ * Adapted from pdf2png.c:
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Kristian Høgsberg <krh@redhat.com>
+ */
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <cairo.h>
+#include <cairo-script-interpreter.h>
+
+#if CAIRO_CAN_TEST_PDF_SURFACE
+#include <poppler.h>
+#endif
+
+#if CAIRO_CAN_TEST_SVG_SURFACE
+#include <librsvg/rsvg.h>
+#include <librsvg/rsvg-cairo.h>
+#endif
+
+#if CAIRO_HAS_SPECTRE
+#include <libspectre/spectre.h>
+#endif
+
+#if HAVE_UNISTD_H && HAVE_FCNTL_H && HAVE_SIGNAL_H && HAVE_SYS_STAT_H && HAVE_SYS_SOCKET_H && HAVE_SYS_POLL_H && HAVE_SYS_UN_H
+#include <fcntl.h>
+#include <signal.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/poll.h>
+#include <sys/un.h>
+#include <errno.h>
+
+#define SOCKET_PATH "./.any2ppm"
+#define TIMEOUT 60000 /* 60 seconds */
+
+#define CAN_RUN_AS_DAEMON 1
+#endif
+
+#define ARRAY_LENGTH(A) (sizeof (A) / sizeof (A[0]))
+
+static int
+_writen (int fd, char *buf, int len)
+{
+    while (len) {
+       int ret;
+
+       ret = write (fd, buf, len);
+       if (ret == -1) {
+           int err = errno;
+           switch (err) {
+           case EINTR:
+           case EAGAIN:
+               continue;
+           default:
+               return 0;
+           }
+       }
+       len -= ret;
+       buf += ret;
+    }
+
+    return 1;
+}
+
+static int
+_write (int fd,
+       char *buf, int maxlen, int buflen,
+       const unsigned char *src, int srclen)
+{
+    if (buflen < 0)
+       return buflen;
+
+    while (srclen) {
+       int len;
+
+       len = buflen + srclen;
+       if (len > maxlen)
+           len = maxlen;
+       len -= buflen;
+
+       memcpy (buf + buflen, src, len);
+       buflen += len;
+       srclen -= len;
+       src += len;
+
+       if (buflen == maxlen) {
+           if (! _writen (fd, buf, buflen))
+               return -1;
+
+           buflen = 0;
+       }
+    }
+
+    return buflen;
+}
+
+static const char *
+write_ppm (cairo_surface_t *surface, int fd)
+{
+    char buf[4096];
+    cairo_format_t format;
+    const char *format_str;
+    const unsigned char *data;
+    int len;
+    int width, height, stride;
+    int i, j;
+
+    data = cairo_image_surface_get_data (surface);
+    height = cairo_image_surface_get_height (surface);
+    width = cairo_image_surface_get_width (surface);
+    stride = cairo_image_surface_get_stride (surface);
+    format = cairo_image_surface_get_format (surface);
+    if (format == CAIRO_FORMAT_ARGB32) {
+       /* see if we can convert to a standard ppm type and trim a few bytes */
+       const unsigned char *alpha = data;
+       for (j = height; j--; alpha += stride) {
+           for (i = 0; i < width; i++) {
+               if ((*(unsigned int *) (alpha+4*i) & 0xff000000) != 0xff000000)
+                   goto done;
+           }
+       }
+       format = CAIRO_FORMAT_RGB24;
+ done: ;
+    }
+
+    switch (format) {
+    case CAIRO_FORMAT_ARGB32:
+       /* XXX need true alpha for svg */
+       format_str = "P7";
+       break;
+    case CAIRO_FORMAT_RGB24:
+       format_str = "P6";
+       break;
+    case CAIRO_FORMAT_A8:
+       format_str = "P5";
+       break;
+    case CAIRO_FORMAT_A1:
+    case CAIRO_FORMAT_RGB16_565:
+    case CAIRO_FORMAT_INVALID:
+    default:
+       return "unhandled image format";
+    }
+
+    len = sprintf (buf, "%s %d %d 255\n", format_str, width, height);
+    for (j = 0; j < height; j++) {
+       const unsigned int *row = (unsigned int *) (data + stride * j);
+
+       switch ((int) format) {
+       case CAIRO_FORMAT_ARGB32:
+           len = _write (fd,
+                         buf, sizeof (buf), len,
+                         (unsigned char *) row, 4 * width);
+           break;
+       case CAIRO_FORMAT_RGB24:
+           for (i = 0; i < width; i++) {
+               unsigned char rgb[3];
+               unsigned int p = *row++;
+               rgb[0] = (p & 0xff0000) >> 16;
+               rgb[1] = (p & 0x00ff00) >> 8;
+               rgb[2] = (p & 0x0000ff) >> 0;
+               len = _write (fd,
+                             buf, sizeof (buf), len,
+                             rgb, 3);
+           }
+           break;
+       case CAIRO_FORMAT_A8:
+           len = _write (fd,
+                         buf, sizeof (buf), len,
+                         (unsigned char *) row, width);
+           break;
+       }
+       if (len < 0)
+           return "write failed";
+    }
+
+    if (len && ! _writen (fd, buf, len))
+       return "write failed";
+
+    return NULL;
+}
+
+static cairo_surface_t *
+_create_image (void *closure,
+              cairo_content_t content,
+              double width, double height,
+              long uid)
+{
+    cairo_surface_t **out = closure;
+    cairo_format_t format;
+    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;
+    }
+    *out = cairo_image_surface_create (format, width, height);
+    return cairo_surface_reference (*out);
+}
+
+#if CAIRO_HAS_INTERPRETER
+static const char *
+_cairo_script_render_page (const char *filename,
+                          cairo_surface_t **surface_out)
+{
+    cairo_script_interpreter_t *csi;
+    cairo_surface_t *surface = NULL;
+    cairo_status_t status;
+    const cairo_script_interpreter_hooks_t hooks = {
+       .closure = &surface,
+       .surface_create = _create_image,
+    };
+
+    csi = cairo_script_interpreter_create ();
+    cairo_script_interpreter_install_hooks (csi, &hooks);
+    status = cairo_script_interpreter_run (csi, filename);
+    if (status) {
+       cairo_surface_destroy (surface);
+       surface = NULL;
+    }
+    status = cairo_script_interpreter_destroy (csi);
+    if (surface == NULL)
+       return "cairo-script interpreter failed";
+
+    if (status == CAIRO_STATUS_SUCCESS)
+       status = cairo_surface_status (surface);
+    if (status) {
+       cairo_surface_destroy (surface);
+       return cairo_status_to_string (status);
+    }
+
+    *surface_out = surface;
+    return NULL;
+}
+
+static const char *
+cs_convert (char **argv, int fd)
+{
+    const char *err;
+    cairo_surface_t *surface = NULL; /* silence compiler warning */
+
+    err = _cairo_script_render_page (argv[0], &surface);
+    if (err != NULL)
+       return err;
+
+    err = write_ppm (surface, fd);
+    cairo_surface_destroy (surface);
+
+    return err;
+}
+#else
+static const char *
+cs_convert (char **argv, int fd)
+{
+    return "compiled without CairoScript support.";
+}
+#endif
+
+#if CAIRO_CAN_TEST_PDF_SURFACE
+/* adapted from pdf2png.c */
+static const char *
+_poppler_render_page (const char *filename,
+                     const char *page_label,
+                     cairo_surface_t **surface_out)
+{
+    PopplerDocument *document;
+    PopplerPage *page;
+    double width, height;
+    GError *error = NULL;
+    gchar *absolute, *uri;
+    cairo_surface_t *surface;
+    cairo_t *cr;
+    cairo_status_t status;
+
+    if (g_path_is_absolute (filename)) {
+       absolute = g_strdup (filename);
+    } else {
+       gchar *dir = g_get_current_dir ();
+       absolute = g_build_filename (dir, filename, (gchar *) 0);
+       g_free (dir);
+    }
+
+    uri = g_filename_to_uri (absolute, NULL, &error);
+    g_free (absolute);
+    if (uri == NULL)
+       return error->message; /* XXX g_error_free (error) */
+
+    document = poppler_document_new_from_file (uri, NULL, &error);
+    g_free (uri);
+    if (document == NULL)
+       return error->message; /* XXX g_error_free (error) */
+
+    page = poppler_document_get_page_by_label (document, page_label);
+    g_object_unref (document);
+    if (page == NULL)
+       return "page not found";
+
+    poppler_page_get_size (page, &width, &height);
+
+    surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, height);
+    cr = cairo_create (surface);
+
+    cairo_set_source_rgb (cr, 1., 1., 1.);
+    cairo_paint (cr);
+    cairo_push_group_with_content (cr, CAIRO_CONTENT_COLOR_ALPHA);
+
+    poppler_page_render (page, cr);
+    g_object_unref (page);
+
+    cairo_pop_group_to_source (cr);
+    cairo_paint (cr);
+
+    status = cairo_status (cr);
+    cairo_destroy (cr);
+
+    if (status) {
+       cairo_surface_destroy (surface);
+       return  cairo_status_to_string (status);
+    }
+
+    *surface_out = surface;
+    return NULL;
+}
+
+static const char *
+pdf_convert (char **argv, int fd)
+{
+    const char *err;
+    cairo_surface_t *surface = NULL; /* silence compiler warning */
+
+    err = _poppler_render_page (argv[0], argv[1], &surface);
+    if (err != NULL)
+       return err;
+
+    err = write_ppm (surface, fd);
+    cairo_surface_destroy (surface);
+
+    return err;
+}
+#else
+static const char *
+pdf_convert (char **argv, int fd)
+{
+    return "compiled without PDF support.";
+}
+#endif
+
+#if CAIRO_CAN_TEST_SVG_SURFACE
+static const char *
+_rsvg_render_page (const char *filename,
+                  cairo_surface_t **surface_out)
+{
+    RsvgHandle *handle;
+    RsvgDimensionData dimensions;
+    GError *error = NULL;
+    cairo_surface_t *surface;
+    cairo_t *cr;
+    cairo_status_t status;
+
+    handle = rsvg_handle_new_from_file (filename, &error);
+    if (handle == NULL)
+       return error->message; /* XXX g_error_free */
+
+    rsvg_handle_get_dimensions (handle, &dimensions);
+    surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
+                                         dimensions.width,
+                                         dimensions.height);
+    cr = cairo_create (surface);
+
+    rsvg_handle_render_cairo (handle, cr);
+    g_object_unref (handle);
+
+    status = cairo_status (cr);
+    cairo_destroy (cr);
+
+    if (status) {
+       cairo_surface_destroy (surface);
+       return  cairo_status_to_string (status);
+    }
+
+    *surface_out = surface;
+    return NULL;
+}
+
+static const char *
+svg_convert (char **argv, int fd)
+{
+    const char *err;
+    cairo_surface_t *surface = NULL; /* silence compiler warning */
+
+    err = _rsvg_render_page (argv[0], &surface);
+    if (err != NULL)
+       return err;
+
+    err = write_ppm (surface, fd);
+    cairo_surface_destroy (surface);
+
+    return err;
+}
+#else
+static const char *
+svg_convert (char **argv, int fd)
+{
+    return "compiled without SVG support.";
+}
+#endif
+
+#if CAIRO_HAS_SPECTRE
+static const char *
+_spectre_render_page (const char *filename,
+                     const char *page_label,
+                     cairo_surface_t **surface_out)
+{
+    static const cairo_user_data_key_t key;
+
+    SpectreDocument *document;
+    SpectreStatus status;
+    int width, height, stride;
+    unsigned char *pixels;
+    cairo_surface_t *surface;
+
+    document = spectre_document_new ();
+    spectre_document_load (document, filename);
+    status = spectre_document_status (document);
+    if (status) {
+       spectre_document_free (document);
+       return spectre_status_to_string (status);
+    }
+
+    if (page_label) {
+       SpectrePage *page;
+       SpectreRenderContext *rc;
+
+       page = spectre_document_get_page_by_label (document, page_label);
+       spectre_document_free (document);
+       if (page == NULL)
+           return "page not found";
+
+       spectre_page_get_size (page, &width, &height);
+       rc = spectre_render_context_new ();
+       spectre_render_context_set_page_size (rc, width, height);
+       spectre_page_render (page, rc, &pixels, &stride);
+       spectre_render_context_free (rc);
+       status = spectre_page_status (page);
+       spectre_page_free (page);
+       if (status) {
+           free (pixels);
+           return spectre_status_to_string (status);
+       }
+    } else {
+       spectre_document_get_page_size (document, &width, &height);
+       spectre_document_render (document, &pixels, &stride);
+       spectre_document_free (document);
+    }
+
+    surface = cairo_image_surface_create_for_data (pixels,
+                                                  CAIRO_FORMAT_RGB24,
+                                                  width, height,
+                                                  stride);
+    cairo_surface_set_user_data (surface, &key,
+                                pixels, (cairo_destroy_func_t) free);
+    *surface_out = surface;
+    return NULL;
+}
+
+static const char *
+ps_convert (char **argv, int fd)
+{
+    const char *err;
+    cairo_surface_t *surface = NULL; /* silence compiler warning */
+
+    err = _spectre_render_page (argv[0], argv[1], &surface);
+    if (err != NULL)
+       return err;
+
+    err = write_ppm (surface, fd);
+    cairo_surface_destroy (surface);
+
+    return err;
+}
+#else
+static const char *
+ps_convert (char **argv, int fd)
+{
+    return "compiled without PostScript support.";
+}
+#endif
+
+static const char *
+convert (char **argv, int fd)
+{
+    static const struct converter {
+       const char *type;
+       const char *(*func) (char **, int);
+    } converters[] = {
+       { "cs", cs_convert },
+       { "pdf", pdf_convert },
+       { "ps", ps_convert },
+       { "svg", svg_convert },
+       { NULL, NULL }
+    };
+    const struct converter *converter = converters;
+    char *type;
+
+    type = strrchr (argv[0], '.');
+    if (type == NULL)
+       return "no file extension";
+    type++;
+
+    while (converter->type) {
+       if (strcmp (type, converter->type) == 0)
+           return converter->func (argv, fd);
+       converter++;
+    }
+    return "no converter";
+}
+
+#if CAN_RUN_AS_DAEMON
+static int
+_getline (int fd, char **linep, size_t *lenp)
+{
+    char *line;
+    size_t len, i;
+    ssize_t ret;
+
+    line = *linep;
+    if (line == NULL) {
+       line = malloc (1024);
+       if (line == NULL)
+           return -1;
+       line[0] = '\0';
+       len = 1024;
+    } else
+       len = *lenp;
+
+    /* XXX simple, but ugly! */
+    i = 0;
+    do {
+       if (i == len - 1) {
+           char *nline;
+
+           nline = realloc (line, len + 1024);
+           if (nline == NULL)
+               goto out;
+
+           line = nline;
+           len += 1024;
+       }
+
+       ret = read (fd, line + i, 1);
+       if (ret == -1 || ret == 0)
+           goto out;
+    } while (line[i++] != '\n');
+
+out:
+    line[i] = '\0';
+    *linep = line;
+    *lenp = len;
+    return i-1;
+}
+
+static int
+split_line (char *line, char *argv[], int max_argc)
+{
+    int i = 0;
+
+    max_argc--; /* leave one spare for the trailing NULL */
+
+    argv[i++] = line;
+    while (i < max_argc && (line = strchr (line, ' ')) != NULL) {
+       *line++ = '\0';
+       argv[i++] = line;
+    }
+
+    /* chomp the newline */
+    line = strchr (argv[i-1], '\n');
+    if (line != NULL)
+       *line = '\0';
+
+    argv[i] = NULL;
+
+    return i;
+}
+
+static int
+any2ppm_daemon_exists (void)
+{
+    struct stat st;
+    int fd;
+    char buf[80];
+    int pid;
+    int ret;
+
+    if (stat (SOCKET_PATH, &st) < 0)
+       return 0;
+
+    fd = open (SOCKET_PATH ".pid", O_RDONLY);
+    if (fd < 0)
+       return 0;
+
+    pid = 0;
+    ret = read (fd, buf, sizeof (buf) - 1);
+    if (ret > 0) {
+       buf[ret] = '\0';
+       pid = atoi (buf);
+    }
+    close (fd);
+
+    return pid > 0 && kill (pid, 0) == 0;
+}
+
+static int
+write_pid_file (void)
+{
+    int fd;
+    char buf[80];
+    int ret;
+
+    fd = open (SOCKET_PATH ".pid", O_CREAT | O_TRUNC | O_WRONLY, 0666);
+    if (fd < 0)
+       return 0;
+
+    ret = sprintf (buf, "%d\n", getpid ());
+    ret = write (fd, buf, ret) == ret;
+    close (fd);
+
+    return ret;
+}
+
+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 ()) {         /* refork to yield session leadership. */
+    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 const char *
+any2ppm_daemon (void)
+{
+    int timeout = TIMEOUT;
+    struct pollfd pfd;
+    int sk, fd;
+    long flags;
+    struct sockaddr_un addr;
+    char *line = NULL;
+    size_t len = 0;
+
+#ifdef SIGPIPE
+    signal (SIGPIPE, SIG_IGN);
+#endif
+
+    /* XXX racy! */
+    if (getenv ("ANY2PPM_FORCE") == NULL && any2ppm_daemon_exists ())
+       return "any2ppm daemon already running";
+
+    unlink (SOCKET_PATH);
+
+    sk = socket (PF_UNIX, SOCK_STREAM, 0);
+    if (sk == -1)
+       return "unable to create socket";
+
+    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 "unable to bind socket";
+    }
+
+    flags = fcntl (sk, F_GETFL);
+    if (flags == -1 || fcntl (sk, F_SETFL, flags | O_NONBLOCK) == -1) {
+       close (sk);
+       return "unable to set socket to non-blocking";
+    }
+
+    if (listen (sk, 5) == -1) {
+       close (sk);
+       return "unable to listen on socket";
+    }
+
+    /* ready for client connection - detach from parent/terminal */
+    if (getenv ("ANY2PPM_NODAEMON") == NULL && daemonize () == -1) {
+       close (sk);
+       return "unable to detach from parent";
+    }
+
+    if (! write_pid_file ()) {
+       close (sk);
+       return "unable to write pid file";
+    }
+
+    if (getenv ("ANY2PPM_TIMEOUT") != NULL) {
+       timeout = atoi (getenv ("ANY2PPM_TIMEOUT"));
+       if (timeout == 0)
+           timeout = -1;
+       if (timeout > 0)
+           timeout *= 1000; /* convert env (in seconds) to milliseconds */
+    }
+
+    pfd.fd = sk;
+    pfd.events = POLLIN;
+    pfd.revents = 0; /* valgrind */
+    while (poll (&pfd, 1, timeout) > 0) {
+       while ((fd = accept (sk, NULL, NULL)) != -1) {
+           if (_getline (fd, &line, &len) != -1) {
+               char *argv[10];
+
+               if (split_line (line, argv, ARRAY_LENGTH (argv)) > 0) {
+                   const char *err;
+
+                   err = convert (argv, fd);
+                   if (err != NULL) {
+                       FILE *file = fopen (".any2ppm.errors", "a");
+                       if (file != NULL) {
+                           fprintf (file,
+                                    "Failed to convert '%s': %s\n",
+                                    argv[0], err);
+                           fclose (file);
+                       }
+                   }
+               }
+           }
+           close (fd);
+       }
+    }
+    close (sk);
+    unlink (SOCKET_PATH);
+    unlink (SOCKET_PATH ".pid");
+
+    free (line);
+    return NULL;
+}
+#else
+static const char *
+any2ppm_daemon (void)
+{
+    return "daemon not compiled in.";
+}
+#endif
+
+int
+main (int argc, char **argv)
+{
+    const char *err;
+
+#if CAIRO_CAN_TEST_PDF_SURFACE || CAIRO_CAN_TEST_SVG_SURFACE
+    g_type_init ();
+#endif
+
+#if CAIRO_CAN_TEST_SVG_SURFACE
+    rsvg_init ();
+    rsvg_set_default_dpi (72.0);
+#endif
+
+    if (argc == 1)
+       err = any2ppm_daemon ();
+    else
+       err = convert (argv + 1, 1);
+    if (err != NULL) {
+       fprintf (stderr, "Failed to run converter: %s\n", err);
+       return EXIT_FAILURE;
+    }
+
+    return EXIT_SUCCESS;
+}
diff --git a/test/api-special-cases.c b/test/api-special-cases.c
new file mode 100644 (file)
index 0000000..dbe9eb2
--- /dev/null
@@ -0,0 +1,847 @@
+/*
+ * Copyright © 2010 Red Hat Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Benjamin Otte <otte@redhat.com>
+ */
+
+/*
+ * WHAT THIS TEST DOES
+ *
+ * This test tests that for all public APIs Cairo behaves correct, consistent
+ * and most of all doesn't crash. It does this by calling all APIs that take
+ * surfaces and calling them on specially prepared surfaces that should fail 
+ * when called on this function.
+ *
+ * ADDING NEW FUNCTIONS
+ *
+ * You need (for adding the function cairo_surface_foo):
+ * 1) A surface_test_func_t named test_cairo_surface_foo that gets passed the
+ *    prepared surface and has the job of calling the function and checking
+ *    the return value (if one exists) for correctness. The top of this file
+ *    contains all these shim functions.
+ * 2) Knowledge if the function behaves like a setter or like a getter. A 
+ *    setter should set an error status on the surface, a getter does not
+ *    modify the function.
+ * 3) Knowledge if the function only works for a specific surface type and for
+ *    which one.
+ * 4) An entry in the tests array using the TEST() macro. It takes as arguments:
+ *    - The function name
+ *    - TRUE if the function modifies the surface, FALSE otherwise
+ *    - the surface type for which the function is valid or -1 if it is valid
+ *      for all surface types.
+ *
+ * FIXING FAILURES
+ *
+ * The test will dump failures notices into the api-special-cases.log file (when 
+ * it doesn't crash). These should be pretty self-explanatory. Usually it is 
+ * enough to just add a new check to the function it complained about.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <assert.h>
+#include <limits.h>
+
+#include "cairo-test.h"
+
+#if CAIRO_HAS_GL_SURFACE
+#include <cairo-gl.h>
+#endif
+#if CAIRO_HAS_OS2_SURFACE
+#include <cairo-os2.h>
+#endif
+#if CAIRO_HAS_PDF_SURFACE
+#include <cairo-pdf.h>
+#endif
+#if CAIRO_HAS_PS_SURFACE
+#include <cairo-ps.h>
+#endif
+#if CAIRO_HAS_QUARTZ_SURFACE
+#define Cursor QuartzCursor
+#include <cairo-quartz.h>
+#undef Cursor
+#endif
+#if CAIRO_HAS_SVG_SURFACE
+#include <cairo-svg.h>
+#endif
+#if CAIRO_HAS_TEE_SURFACE
+#include <cairo-tee.h>
+#endif
+#if CAIRO_HAS_XCB_SURFACE
+#include <cairo-xcb.h>
+#endif
+#if CAIRO_HAS_XLIB_SURFACE
+#define Cursor XCursor
+#include <cairo-xlib.h>
+#undef Cursor
+#endif
+
+#define surface_has_type(surface,type) (cairo_surface_get_type (surface) == (type))
+
+typedef cairo_test_status_t (* surface_test_func_t) (cairo_surface_t *surface);
+
+static cairo_test_status_t
+test_cairo_surface_create_similar (cairo_surface_t *surface)
+{
+    cairo_surface_t *similar;
+    
+    similar = cairo_surface_create_similar (surface, CAIRO_CONTENT_ALPHA, 100, 100);
+    
+    cairo_surface_destroy (similar);
+    return CAIRO_TEST_SUCCESS;
+}
+
+static cairo_test_status_t
+test_cairo_surface_create_for_rectangle (cairo_surface_t *surface)
+{
+    cairo_surface_t *similar;
+    
+    similar = cairo_surface_create_for_rectangle (surface, 1, 1, 8, 8);
+    
+    cairo_surface_destroy (similar);
+    return CAIRO_TEST_SUCCESS;
+}
+
+static cairo_test_status_t
+test_cairo_surface_reference (cairo_surface_t *surface)
+{
+    cairo_surface_destroy (cairo_surface_reference (surface));
+    return CAIRO_TEST_SUCCESS;
+}
+
+static cairo_test_status_t
+test_cairo_surface_finish (cairo_surface_t *surface)
+{
+    cairo_surface_finish (surface);
+    return CAIRO_TEST_SUCCESS;
+}
+
+static cairo_test_status_t
+test_cairo_surface_get_device (cairo_surface_t *surface)
+{
+    /* cairo_device_t *device = */cairo_surface_get_device (surface);
+    return CAIRO_TEST_SUCCESS;
+}
+
+static cairo_test_status_t
+test_cairo_surface_get_reference_count (cairo_surface_t *surface)
+{
+    unsigned int refcount = cairo_surface_get_reference_count (surface);
+    if (refcount > 0)
+        return CAIRO_TEST_SUCCESS;
+    /* inert error surfaces have a refcount of 0 */
+    return cairo_surface_status (surface) ? CAIRO_TEST_SUCCESS : CAIRO_TEST_ERROR;
+}
+
+static cairo_test_status_t
+test_cairo_surface_status (cairo_surface_t *surface)
+{
+    cairo_status_t status = cairo_surface_status (surface);
+    return status < CAIRO_STATUS_LAST_STATUS ? CAIRO_TEST_SUCCESS : CAIRO_TEST_ERROR;
+}
+
+static cairo_test_status_t
+test_cairo_surface_get_type (cairo_surface_t *surface)
+{
+    /* cairo_surface_type_t type = */cairo_surface_get_type (surface);
+    return CAIRO_TEST_SUCCESS;
+}
+
+static cairo_test_status_t
+test_cairo_surface_get_content (cairo_surface_t *surface)
+{
+    cairo_content_t content = cairo_surface_get_content (surface);
+
+    switch (content) {
+    case CAIRO_CONTENT_COLOR:
+    case CAIRO_CONTENT_ALPHA:
+    case CAIRO_CONTENT_COLOR_ALPHA:
+        return CAIRO_TEST_SUCCESS;
+    default:
+        return CAIRO_TEST_ERROR;
+    }
+}
+
+static cairo_test_status_t
+test_cairo_surface_set_user_data (cairo_surface_t *surface)
+{
+    static cairo_user_data_key_t key;
+    cairo_status_t status;
+
+    status = cairo_surface_set_user_data (surface, &key, &key, NULL);
+    if (status == CAIRO_STATUS_NO_MEMORY)
+        return CAIRO_TEST_NO_MEMORY;
+    else if (status)
+        return CAIRO_TEST_SUCCESS;
+
+    if (cairo_surface_get_user_data (surface, &key) != &key)
+        return CAIRO_TEST_ERROR;
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+static cairo_test_status_t
+test_cairo_surface_set_mime_data (cairo_surface_t *surface)
+{
+    const char *mimetype = "text/x-uri";
+    const char *data = "http://www.cairographics.org";
+    cairo_status_t status;
+
+    status = cairo_surface_set_mime_data (surface,
+                                          mimetype,
+                                          (const unsigned char *) data,
+                                         strlen (data),
+                                          NULL, NULL);
+    return status ? CAIRO_TEST_SUCCESS : CAIRO_TEST_ERROR;
+}
+
+static cairo_test_status_t
+test_cairo_surface_get_mime_data (cairo_surface_t *surface)
+{
+    const char *mimetype = "text/x-uri";
+    const unsigned char *data;
+    unsigned long length;
+
+    cairo_surface_get_mime_data (surface, mimetype, &data, &length);
+    return data == NULL && length == 0 ? CAIRO_TEST_SUCCESS : CAIRO_TEST_ERROR;
+}
+
+static cairo_test_status_t
+test_cairo_surface_get_font_options (cairo_surface_t *surface)
+{
+    cairo_font_options_t *options;
+    cairo_status_t status;
+
+    options = cairo_font_options_create ();
+    if (likely (!cairo_font_options_status (options)))
+        cairo_surface_get_font_options (surface, options);
+    status = cairo_font_options_status (options);
+    cairo_font_options_destroy (options);
+    return status ? CAIRO_TEST_ERROR : CAIRO_TEST_SUCCESS;
+}
+
+static cairo_test_status_t
+test_cairo_surface_flush (cairo_surface_t *surface)
+{
+    cairo_surface_flush (surface);
+    return CAIRO_TEST_SUCCESS;
+}
+
+static cairo_test_status_t
+test_cairo_surface_mark_dirty (cairo_surface_t *surface)
+{
+    cairo_surface_mark_dirty (surface);
+    return CAIRO_TEST_SUCCESS;
+}
+
+static cairo_test_status_t
+test_cairo_surface_mark_dirty_rectangle (cairo_surface_t *surface)
+{
+    cairo_surface_mark_dirty_rectangle (surface, 1, 1, 8, 8);
+    return CAIRO_TEST_SUCCESS;
+}
+
+static cairo_test_status_t
+test_cairo_surface_set_device_offset (cairo_surface_t *surface)
+{
+    cairo_surface_set_device_offset (surface, 5, 5);
+    return CAIRO_TEST_SUCCESS;
+}
+
+static cairo_test_status_t
+test_cairo_surface_get_device_offset (cairo_surface_t *surface)
+{
+    double x, y;
+
+    cairo_surface_get_device_offset (surface, &x, &y);
+    return CAIRO_TEST_SUCCESS;
+}
+
+static cairo_test_status_t
+test_cairo_surface_set_fallback_resolution (cairo_surface_t *surface)
+{
+    cairo_surface_set_fallback_resolution (surface, 42, 42);
+    return CAIRO_TEST_SUCCESS;
+}
+
+static cairo_test_status_t
+test_cairo_surface_get_fallback_resolution (cairo_surface_t *surface)
+{
+    double x, y;
+
+    cairo_surface_get_fallback_resolution (surface, &x, &y);
+    return CAIRO_TEST_SUCCESS;
+}
+
+static cairo_test_status_t
+test_cairo_surface_copy_page (cairo_surface_t *surface)
+{
+    cairo_surface_copy_page (surface);
+    return CAIRO_TEST_SUCCESS;
+}
+
+static cairo_test_status_t
+test_cairo_surface_show_page (cairo_surface_t *surface)
+{
+    cairo_surface_show_page (surface);
+    return CAIRO_TEST_SUCCESS;
+}
+
+static cairo_test_status_t
+test_cairo_surface_has_show_text_glyphs (cairo_surface_t *surface)
+{
+    cairo_surface_has_show_text_glyphs (surface);
+    return CAIRO_TEST_SUCCESS;
+}
+
+static cairo_test_status_t
+test_cairo_image_surface_get_data (cairo_surface_t *surface)
+{
+    unsigned char *data = cairo_image_surface_get_data (surface);
+    return data == NULL || surface_has_type (surface, CAIRO_SURFACE_TYPE_IMAGE) ? CAIRO_TEST_SUCCESS : CAIRO_TEST_ERROR;
+}
+
+static cairo_test_status_t
+test_cairo_image_surface_get_format (cairo_surface_t *surface)
+{
+    cairo_format_t format = cairo_image_surface_get_format (surface);
+    return format == CAIRO_FORMAT_INVALID || surface_has_type (surface, CAIRO_SURFACE_TYPE_IMAGE) ? CAIRO_TEST_SUCCESS : CAIRO_TEST_ERROR;
+}
+
+static cairo_test_status_t
+test_cairo_image_surface_get_width (cairo_surface_t *surface)
+{
+    unsigned int width = cairo_image_surface_get_width (surface);
+    return width == 0 || surface_has_type (surface, CAIRO_SURFACE_TYPE_IMAGE) ? CAIRO_TEST_SUCCESS : CAIRO_TEST_ERROR;
+}
+
+static cairo_test_status_t
+test_cairo_image_surface_get_height (cairo_surface_t *surface)
+{
+    unsigned int height = cairo_image_surface_get_height (surface);
+    return height == 0 || surface_has_type (surface, CAIRO_SURFACE_TYPE_IMAGE) ? CAIRO_TEST_SUCCESS : CAIRO_TEST_ERROR;
+}
+
+static cairo_test_status_t
+test_cairo_image_surface_get_stride (cairo_surface_t *surface)
+{
+    unsigned int stride = cairo_image_surface_get_stride (surface);
+    return stride == 0 || surface_has_type (surface, CAIRO_SURFACE_TYPE_IMAGE) ? CAIRO_TEST_SUCCESS : CAIRO_TEST_ERROR;
+}
+
+#if CAIRO_HAS_PNG_FUNCTIONS
+
+static cairo_test_status_t
+test_cairo_surface_write_to_png (cairo_surface_t *surface)
+{
+    cairo_status_t status;
+
+    status = cairo_surface_write_to_png (surface, "/this/file/will/definitely/not/exist.png");
+    
+    return status ? CAIRO_TEST_SUCCESS : CAIRO_TEST_ERROR;
+}
+
+static cairo_status_t
+write_func_that_always_fails (void *closure, const unsigned char *data, unsigned int length)
+{
+    return CAIRO_STATUS_WRITE_ERROR;
+}
+
+static cairo_test_status_t
+test_cairo_surface_write_to_png_stream (cairo_surface_t *surface)
+{
+    cairo_status_t status;
+
+    status = cairo_surface_write_to_png_stream (surface,
+                                                write_func_that_always_fails,
+                                                NULL);
+    
+    return status && status != CAIRO_STATUS_WRITE_ERROR ? CAIRO_TEST_SUCCESS : CAIRO_TEST_ERROR;
+}
+
+#endif /* CAIRO_HAS_PNG_FUNCTIONS */
+
+static cairo_test_status_t
+test_cairo_recording_surface_ink_extents (cairo_surface_t *surface)
+{
+    double x, y, w, h;
+
+    cairo_recording_surface_ink_extents (surface, &x, &y, &w, &h);
+    return x == 0 && y == 0 && w == 0 && h == 0 ? CAIRO_TEST_SUCCESS : CAIRO_TEST_ERROR;
+}
+
+#if CAIRO_HAS_TEE_SURFACE
+
+static cairo_test_status_t
+test_cairo_tee_surface_add (cairo_surface_t *surface)
+{
+    cairo_surface_t *image = cairo_image_surface_create (CAIRO_FORMAT_A8, 10, 10);
+
+    cairo_tee_surface_add (surface, image);
+    cairo_surface_destroy (image);
+    return CAIRO_TEST_SUCCESS;
+}
+
+static cairo_test_status_t
+test_cairo_tee_surface_remove (cairo_surface_t *surface)
+{
+    cairo_surface_t *image = cairo_image_surface_create (CAIRO_FORMAT_A8, 10, 10);
+
+    cairo_tee_surface_remove (surface, image);
+    cairo_surface_destroy (image);
+    return CAIRO_TEST_SUCCESS;
+}
+
+static cairo_test_status_t
+test_cairo_tee_surface_index (cairo_surface_t *surface)
+{
+    cairo_surface_t *master;
+    cairo_status_t status;
+
+    master = cairo_tee_surface_index (surface, 0);
+    status = cairo_surface_status (master);
+    cairo_surface_destroy (master);
+    return status ? CAIRO_TEST_SUCCESS : CAIRO_TEST_ERROR;
+}
+
+#endif /* CAIRO_HAS_TEE_SURFACE */
+
+#if CAIRO_HAS_GL_SURFACE
+
+static cairo_test_status_t
+test_cairo_gl_surface_set_size (cairo_surface_t *surface)
+{
+    cairo_gl_surface_set_size (surface, 5, 5);
+    return CAIRO_TEST_SUCCESS;
+}
+
+static cairo_test_status_t
+test_cairo_gl_surface_get_width (cairo_surface_t *surface)
+{
+    unsigned int width = cairo_gl_surface_get_width (surface);
+    return width == 0 || surface_has_type (surface, CAIRO_SURFACE_TYPE_GL) ? CAIRO_TEST_SUCCESS : CAIRO_TEST_ERROR;
+}
+
+static cairo_test_status_t
+test_cairo_gl_surface_get_height (cairo_surface_t *surface)
+{
+    unsigned int height = cairo_gl_surface_get_height (surface);
+    return height == 0 || surface_has_type (surface, CAIRO_SURFACE_TYPE_GL) ? CAIRO_TEST_SUCCESS : CAIRO_TEST_ERROR;
+}
+
+static cairo_test_status_t
+test_cairo_gl_surface_swapbuffers (cairo_surface_t *surface)
+{
+    cairo_gl_surface_swapbuffers (surface);
+    return CAIRO_TEST_SUCCESS;
+}
+
+#endif /* CAIRO_HAS_GL_SURFACE */
+
+#if CAIRO_HAS_PDF_SURFACE
+
+static cairo_test_status_t
+test_cairo_pdf_surface_restrict_to_version (cairo_surface_t *surface)
+{
+    cairo_pdf_surface_restrict_to_version (surface, CAIRO_PDF_VERSION_1_4);
+    return CAIRO_TEST_SUCCESS;
+}
+
+static cairo_test_status_t
+test_cairo_pdf_surface_set_size (cairo_surface_t *surface)
+{
+    cairo_pdf_surface_set_size (surface, 5, 5);
+    return CAIRO_TEST_SUCCESS;
+}
+
+#endif /* CAIRO_HAS_PDF_SURFACE */
+
+#if CAIRO_HAS_PS_SURFACE
+
+static cairo_test_status_t
+test_cairo_ps_surface_restrict_to_level (cairo_surface_t *surface)
+{
+    cairo_ps_surface_restrict_to_level (surface, CAIRO_PS_LEVEL_2);
+    return CAIRO_TEST_SUCCESS;
+}
+
+static cairo_test_status_t
+test_cairo_ps_surface_set_eps (cairo_surface_t *surface)
+{
+    cairo_ps_surface_set_eps (surface, TRUE);
+    return CAIRO_TEST_SUCCESS;
+}
+
+static cairo_test_status_t
+test_cairo_ps_surface_get_eps (cairo_surface_t *surface)
+{
+    cairo_bool_t eps = cairo_ps_surface_get_eps (surface);
+    return eps ? CAIRO_TEST_ERROR : CAIRO_TEST_SUCCESS;
+}
+
+static cairo_test_status_t
+test_cairo_ps_surface_set_size (cairo_surface_t *surface)
+{
+    cairo_ps_surface_set_size (surface, 5, 5);
+    return CAIRO_TEST_SUCCESS;
+}
+
+static cairo_test_status_t
+test_cairo_ps_surface_dsc_comment (cairo_surface_t *surface)
+{
+    cairo_ps_surface_dsc_comment (surface, "54, 74, 90, 2010");
+    return CAIRO_TEST_SUCCESS;
+}
+
+static cairo_test_status_t
+test_cairo_ps_surface_dsc_begin_setup (cairo_surface_t *surface)
+{
+    cairo_ps_surface_dsc_begin_setup (surface);
+    return CAIRO_TEST_SUCCESS;
+}
+
+static cairo_test_status_t
+test_cairo_ps_surface_dsc_begin_page_setup (cairo_surface_t *surface)
+{
+    cairo_ps_surface_dsc_begin_page_setup (surface);
+    return CAIRO_TEST_SUCCESS;
+}
+
+#endif /* CAIRO_HAS_PS_SURFACE */
+
+#if CAIRO_HAS_QUARTZ_SURFACE
+
+static cairo_test_status_t
+test_cairo_quartz_surface_get_cg_context (cairo_surface_t *surface)
+{
+    CGContextRef context = cairo_quartz_surface_get_cg_context (surface);
+    return context == NULL || surface_has_type (surface, CAIRO_SURFACE_TYPE_QUARTZ) ? CAIRO_TEST_SUCCESS : CAIRO_TEST_ERROR;
+}
+
+#endif /* CAIRO_HAS_QUARTZ_SURFACE */
+
+#if CAIRO_HAS_SVG_SURFACE
+
+static cairo_test_status_t
+test_cairo_svg_surface_restrict_to_version (cairo_surface_t *surface)
+{
+    cairo_svg_surface_restrict_to_version (surface, CAIRO_SVG_VERSION_1_1);
+    return CAIRO_TEST_SUCCESS;
+}
+
+#endif /* CAIRO_HAS_SVG_SURFACE */
+
+#if CAIRO_HAS_XCB_SURFACE
+
+static cairo_test_status_t
+test_cairo_xcb_surface_set_size (cairo_surface_t *surface)
+{
+    cairo_xcb_surface_set_size (surface, 5, 5);
+    return CAIRO_TEST_SUCCESS;
+}
+
+#endif
+
+#if CAIRO_HAS_XLIB_SURFACE
+
+static cairo_test_status_t
+test_cairo_xlib_surface_set_size (cairo_surface_t *surface)
+{
+    cairo_xlib_surface_set_size (surface, 5, 5);
+    return CAIRO_TEST_SUCCESS;
+}
+
+static cairo_test_status_t
+test_cairo_xlib_surface_set_drawable (cairo_surface_t *surface)
+{
+    cairo_xlib_surface_set_drawable (surface, 0, 5, 5);
+    return CAIRO_TEST_SUCCESS;
+}
+
+static cairo_test_status_t
+test_cairo_xlib_surface_get_display (cairo_surface_t *surface)
+{
+    Display *display = cairo_xlib_surface_get_display (surface);
+    return display == NULL || surface_has_type (surface, CAIRO_SURFACE_TYPE_XLIB) ? CAIRO_TEST_SUCCESS : CAIRO_TEST_ERROR;
+}
+
+static cairo_test_status_t
+test_cairo_xlib_surface_get_screen (cairo_surface_t *surface)
+{
+    Screen *screen = cairo_xlib_surface_get_screen (surface);
+    return screen == NULL || surface_has_type (surface, CAIRO_SURFACE_TYPE_XLIB) ? CAIRO_TEST_SUCCESS : CAIRO_TEST_ERROR;
+}
+
+static cairo_test_status_t
+test_cairo_xlib_surface_get_visual (cairo_surface_t *surface)
+{
+    Visual *visual = cairo_xlib_surface_get_visual (surface);
+    return visual == NULL || surface_has_type (surface, CAIRO_SURFACE_TYPE_XLIB) ? CAIRO_TEST_SUCCESS : CAIRO_TEST_ERROR;
+}
+
+static cairo_test_status_t
+test_cairo_xlib_surface_get_drawable (cairo_surface_t *surface)
+{
+    Drawable drawable = cairo_xlib_surface_get_drawable (surface);
+    return drawable == 0 || surface_has_type (surface, CAIRO_SURFACE_TYPE_XLIB) ? CAIRO_TEST_SUCCESS : CAIRO_TEST_ERROR;
+}
+
+static cairo_test_status_t
+test_cairo_xlib_surface_get_depth (cairo_surface_t *surface)
+{
+    int depth = cairo_xlib_surface_get_depth (surface);
+    return depth == 0 || surface_has_type (surface, CAIRO_SURFACE_TYPE_XLIB) ? CAIRO_TEST_SUCCESS : CAIRO_TEST_ERROR;
+}
+
+static cairo_test_status_t
+test_cairo_xlib_surface_get_width (cairo_surface_t *surface)
+{
+    int width = cairo_xlib_surface_get_width (surface);
+    return width == 0 || surface_has_type (surface, CAIRO_SURFACE_TYPE_XLIB) ? CAIRO_TEST_SUCCESS : CAIRO_TEST_ERROR;
+}
+
+static cairo_test_status_t
+test_cairo_xlib_surface_get_height (cairo_surface_t *surface)
+{
+    int height = cairo_xlib_surface_get_height (surface);
+    return height == 0 || surface_has_type (surface, CAIRO_SURFACE_TYPE_XLIB) ? CAIRO_TEST_SUCCESS : CAIRO_TEST_ERROR;
+}
+
+#endif
+
+
+#define TEST(name, surface_type, sets_status) { #name, test_ ## name, surface_type, sets_status }
+
+struct {
+    const char *name;
+    surface_test_func_t func;
+    int surface_type; /* cairo_surface_type_t or -1 */
+    cairo_bool_t modifies_surface;
+} tests[] = {
+    TEST (cairo_surface_create_similar, -1, FALSE),
+    TEST (cairo_surface_create_for_rectangle, -1, FALSE),
+    TEST (cairo_surface_reference, -1, FALSE),
+    TEST (cairo_surface_finish, -1, TRUE),
+    TEST (cairo_surface_get_device, -1, FALSE),
+    TEST (cairo_surface_get_reference_count, -1, FALSE),
+    TEST (cairo_surface_status, -1, FALSE),
+    TEST (cairo_surface_get_type, -1, FALSE),
+    TEST (cairo_surface_get_content, -1, FALSE),
+    TEST (cairo_surface_set_user_data, -1, FALSE),
+    TEST (cairo_surface_set_mime_data, -1, TRUE),
+    TEST (cairo_surface_get_mime_data, -1, FALSE),
+    TEST (cairo_surface_get_font_options, -1, FALSE),
+    TEST (cairo_surface_flush, -1, TRUE),
+    TEST (cairo_surface_mark_dirty, -1, TRUE),
+    TEST (cairo_surface_mark_dirty_rectangle, -1, TRUE),
+    TEST (cairo_surface_set_device_offset, -1, TRUE),
+    TEST (cairo_surface_get_device_offset, -1, FALSE),
+    TEST (cairo_surface_set_fallback_resolution, -1, TRUE),
+    TEST (cairo_surface_get_fallback_resolution, -1, FALSE),
+    TEST (cairo_surface_copy_page, -1, TRUE),
+    TEST (cairo_surface_show_page, -1, TRUE),
+    TEST (cairo_surface_has_show_text_glyphs, -1, FALSE),
+    TEST (cairo_image_surface_get_data, CAIRO_SURFACE_TYPE_IMAGE, FALSE),
+    TEST (cairo_image_surface_get_format, CAIRO_SURFACE_TYPE_IMAGE, FALSE),
+    TEST (cairo_image_surface_get_width, CAIRO_SURFACE_TYPE_IMAGE, FALSE),
+    TEST (cairo_image_surface_get_height, CAIRO_SURFACE_TYPE_IMAGE, FALSE),
+    TEST (cairo_image_surface_get_stride, CAIRO_SURFACE_TYPE_IMAGE, FALSE),
+#if CAIRO_HAS_PNG_FUNCTIONS
+    TEST (cairo_surface_write_to_png, -1, FALSE),
+    TEST (cairo_surface_write_to_png_stream, -1, FALSE),
+#endif
+    TEST (cairo_recording_surface_ink_extents, CAIRO_SURFACE_TYPE_RECORDING, FALSE),
+#if CAIRO_HAS_TEE_SURFACE
+    TEST (cairo_tee_surface_add, CAIRO_SURFACE_TYPE_TEE, TRUE),
+    TEST (cairo_tee_surface_remove, CAIRO_SURFACE_TYPE_TEE, TRUE),
+    TEST (cairo_tee_surface_index, CAIRO_SURFACE_TYPE_TEE, FALSE),
+#endif
+#if CAIRO_HAS_GL_SURFACE
+    TEST (cairo_gl_surface_set_size, CAIRO_SURFACE_TYPE_GL, TRUE),
+    TEST (cairo_gl_surface_get_width, CAIRO_SURFACE_TYPE_GL, FALSE),
+    TEST (cairo_gl_surface_get_height, CAIRO_SURFACE_TYPE_GL, FALSE),
+    TEST (cairo_gl_surface_swapbuffers, CAIRO_SURFACE_TYPE_GL, TRUE),
+#endif
+#if CAIRO_HAS_PDF_SURFACE
+    TEST (cairo_pdf_surface_restrict_to_version, CAIRO_SURFACE_TYPE_PDF, TRUE),
+    TEST (cairo_pdf_surface_set_size, CAIRO_SURFACE_TYPE_PDF, TRUE),
+#endif
+#if CAIRO_HAS_PS_SURFACE
+    TEST (cairo_ps_surface_restrict_to_level, CAIRO_SURFACE_TYPE_PS, TRUE),
+    TEST (cairo_ps_surface_set_eps, CAIRO_SURFACE_TYPE_PS, TRUE),
+    TEST (cairo_ps_surface_get_eps, CAIRO_SURFACE_TYPE_PS, FALSE),
+    TEST (cairo_ps_surface_set_size, CAIRO_SURFACE_TYPE_PS, TRUE),
+    TEST (cairo_ps_surface_dsc_comment, CAIRO_SURFACE_TYPE_PS, TRUE),
+    TEST (cairo_ps_surface_dsc_begin_setup, CAIRO_SURFACE_TYPE_PS, TRUE),
+    TEST (cairo_ps_surface_dsc_begin_page_setup, CAIRO_SURFACE_TYPE_PS, TRUE),
+#endif
+#if CAIRO_HAS_QUARTZ_SURFACE
+    TEST (cairo_quartz_surface_get_cg_context, CAIRO_SURFACE_TYPE_QUARTZ, FALSE),
+#endif
+#if CAIRO_HAS_SVG_SURFACE
+    TEST (cairo_svg_surface_restrict_to_version, CAIRO_SURFACE_TYPE_SVG, TRUE),
+#endif
+#if CAIRO_HAS_XCB_SURFACE
+    TEST (cairo_xcb_surface_set_size, CAIRO_SURFACE_TYPE_XCB, TRUE),
+#endif
+#if CAIRO_HAS_XLIB_SURFACE
+    TEST (cairo_xlib_surface_set_size, CAIRO_SURFACE_TYPE_XLIB, TRUE),
+    TEST (cairo_xlib_surface_set_drawable, CAIRO_SURFACE_TYPE_XLIB, TRUE),
+    TEST (cairo_xlib_surface_get_display, CAIRO_SURFACE_TYPE_XLIB, FALSE),
+    TEST (cairo_xlib_surface_get_drawable, CAIRO_SURFACE_TYPE_XLIB, FALSE),
+    TEST (cairo_xlib_surface_get_screen, CAIRO_SURFACE_TYPE_XLIB, FALSE),
+    TEST (cairo_xlib_surface_get_visual, CAIRO_SURFACE_TYPE_XLIB, FALSE),
+    TEST (cairo_xlib_surface_get_depth, CAIRO_SURFACE_TYPE_XLIB, FALSE),
+    TEST (cairo_xlib_surface_get_width, CAIRO_SURFACE_TYPE_XLIB, FALSE),
+    TEST (cairo_xlib_surface_get_height, CAIRO_SURFACE_TYPE_XLIB, FALSE),
+#endif
+};
+
+static cairo_test_status_t
+preamble (cairo_test_context_t *ctx)
+{
+    cairo_surface_t *surface;
+    cairo_test_status_t test_status;
+    cairo_status_t status_before, status_after;
+    unsigned int i;
+
+    /* Test an error surface */
+    for (i = 0; i < ARRAY_LENGTH (tests); i++) {
+        surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, INT_MAX, INT_MAX);
+        status_before = cairo_surface_status (surface);
+        assert (status_before);
+
+        test_status = tests[i].func (surface);
+
+        status_after = cairo_surface_status (surface);
+        cairo_surface_destroy (surface);
+
+        if (test_status != CAIRO_TEST_SUCCESS) {
+            cairo_test_log (ctx,
+                            "Failed test %s with %d\n",
+                            tests[i].name, (int) test_status);
+            return test_status;
+        }
+
+        if (status_before != status_after) {
+            cairo_test_log (ctx,
+                            "Failed test %s: Modified surface status from %u (%s) to %u (%s)\n",
+                            tests[i].name,
+                            status_before, cairo_status_to_string (status_before),
+                            status_after, cairo_status_to_string (status_after));
+            return CAIRO_TEST_ERROR;
+        }
+    }
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    const cairo_test_context_t *ctx = cairo_test_get_context (cr);
+    cairo_surface_t *similar, *target;
+    cairo_test_status_t test_status;
+    cairo_status_t status;
+    unsigned int i;
+
+    target = cairo_get_target (cr);
+
+    /* Test a finished similar surface */
+    for (i = 0; i < ARRAY_LENGTH (tests); i++) {
+        similar = cairo_surface_create_similar (target,
+                                                cairo_surface_get_content (target),
+                                                10, 10);
+        cairo_surface_finish (similar);
+        test_status = tests[i].func (similar);
+        status = cairo_surface_status (similar);
+        cairo_surface_destroy (similar);
+
+        if (test_status != CAIRO_TEST_SUCCESS) {
+            cairo_test_log (ctx,
+                            "Failed test %s with %d\n",
+                            tests[i].name, (int) test_status);
+            return test_status;
+        }
+
+        if (tests[i].modifies_surface &&
+            strcmp (tests[i].name, "cairo_surface_finish") &&
+            strcmp (tests[i].name, "cairo_surface_flush") &&
+            status != CAIRO_STATUS_SURFACE_FINISHED) {
+            cairo_test_log (ctx,
+                            "Failed test %s: Finished surface not set into error state\n",
+                            tests[i].name);
+            return CAIRO_TEST_ERROR;
+        }
+    }
+
+    /* Test a normal surface for functions that have the wrong type */
+    for (i = 0; i < ARRAY_LENGTH (tests); i++) {
+        cairo_status_t desired_status;
+
+        if (tests[i].surface_type == -1)
+            continue;
+        similar = cairo_surface_create_similar (target,
+                                                cairo_surface_get_content (target),
+                                                10, 10);
+        if (cairo_surface_get_type (similar) == (cairo_surface_type_t) tests[i].surface_type) {
+            cairo_surface_destroy (similar);
+            continue;
+        }
+
+        test_status = tests[i].func (similar);
+        status = cairo_surface_status (similar);
+        cairo_surface_destroy (similar);
+
+        if (test_status != CAIRO_TEST_SUCCESS) {
+            cairo_test_log (ctx,
+                            "Failed test %s with %d\n",
+                            tests[i].name, (int) test_status);
+            return test_status;
+        }
+
+        desired_status = tests[i].modifies_surface ? CAIRO_STATUS_SURFACE_TYPE_MISMATCH : CAIRO_STATUS_SUCCESS;
+        if (status != desired_status) {
+            cairo_test_log (ctx,
+                            "Failed test %s: Surface status should be %u (%s), but is %u (%s)\n",
+                            tests[i].name,
+                            desired_status, cairo_status_to_string (desired_status),
+                            status, cairo_status_to_string (status));
+            return CAIRO_TEST_ERROR;
+        }
+    }
+
+    /* 565-compatible gray background */
+    cairo_set_source_rgb (cr, 0.51613, 0.55555, 0.51613);
+    cairo_paint (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (api_special_cases,
+           "Check surface functions properly handle wrong surface arguments",
+           "api", /* keywords */
+           NULL, /* requirements */
+           10, 10,
+           preamble, draw)
diff --git a/test/arc-infinite-loop.c b/test/arc-infinite-loop.c
new file mode 100644 (file)
index 0000000..8b469ea
--- /dev/null
@@ -0,0 +1,61 @@
+/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/*
+ * Copyright 2010 Andrea Canciani
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Andrea Canciani <ranma42@gmail.com>
+ */
+
+#include "cairo-test.h"
+#include <float.h>
+
+#define SIZE 8
+
+/*
+  cairo_arc can hang in an infinite loop if given huge (so big that
+  adding/subtracting 4*M_PI to them doesn't change the value because
+  of floating point rounding).
+
+  The purpose of this test is to check that cairo doesn't hang or crash.
+*/
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_paint (cr);
+
+    /* Check if the code that guarantees start <= end hangs */
+    cairo_arc (cr, 0, 0, 1, 1024 / DBL_EPSILON * M_PI, 0);
+
+    /* Check if the code that handles huge angles hangs */
+    cairo_arc (cr, 0, 0, 1, 0, 1024 / DBL_EPSILON * M_PI);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (arc_infinite_loop,
+           "Test cairo_arc with huge angles",
+           "arc", /* keywords */
+           NULL, /* requirements */
+           SIZE, SIZE,
+           NULL, draw)
diff --git a/test/arc-looping-dash.c b/test/arc-looping-dash.c
new file mode 100644 (file)
index 0000000..ff5556a
--- /dev/null
@@ -0,0 +1,79 @@
+/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/*
+ * Copyright 2010 Andrea Canciani
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Andrea Canciani <ranma42@gmail.com>
+ */
+
+#include "cairo-test.h"
+
+#define SIZE 32
+
+/*
+  When cairo_arc is used to draw an arc of more than 2pi radians
+  (i.e. a circle "looping over itself"), various different behaviors
+  are possible:
+
+  - draw exactly a circle (an arc of 2pi radians)
+
+  - draw an arc such that the current point is the expected one and
+    that does at least a complete circle (an arc of [2pi, 4pi)
+    radians)
+
+  - draw an arc with the original number of loops
+
+  This test produces different results for each of these three cases.
+*/
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    double dashes[] = { 0.3, 7 };
+
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_paint (cr);
+
+    cairo_save (cr);
+
+    cairo_translate (cr, SIZE * .5, SIZE * .5);
+    cairo_scale (cr, SIZE * 3 / 8., SIZE * 3 / 8.);
+
+    cairo_arc (cr, 0, 0, 1, 0, 11 * M_PI);
+
+    cairo_set_line_width (cr, 8. / SIZE);
+
+    cairo_set_source_rgb (cr, 0, 0, 0);
+    cairo_set_dash (cr, dashes, 2, 0);
+    cairo_stroke (cr);
+
+    cairo_restore (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (arc_looping_dash,
+           "Test cairo_arc for angles describing more than a complete circle",
+           "arc", /* keywords */
+           NULL, /* requirements */
+           SIZE, SIZE,
+           NULL, draw)
diff --git a/test/big-empty-box.c b/test/big-empty-box.c
new file mode 100644 (file)
index 0000000..4ea91a1
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Copyright © 2011 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+/*
+ * The mystery of the disappearing box, similar to big-little-box.
+ *
+ * The issue is that we failed to tighten the initial approximated bounds
+ * after tessellating the path.
+ */
+
+#include "cairo-test.h"
+
+#define SIZE 60
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_set_source_rgb (cr, 1, 0, 1);
+    cairo_paint (cr);
+
+    /* Set an unbounded operator so that we can see how accurate the bounded
+     * extents were.
+     */
+    cairo_set_operator (cr, CAIRO_OPERATOR_IN);
+    cairo_set_source_rgb (cr, 1, 1, 1);
+
+    /* Wind several boxes together that reduce to nothing */
+    cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD);
+    cairo_rectangle (cr, 0, 0, SIZE, SIZE);
+    cairo_rectangle (cr, 0, 0, SIZE, SIZE);
+    cairo_fill (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (big_empty_box,
+           "Tests that we tighten the bounds after tessellation.",
+           "fill", /* keywords */
+           NULL, /* requirements */
+           SIZE, SIZE,
+           NULL, draw)
diff --git a/test/big-empty-triangle.c b/test/big-empty-triangle.c
new file mode 100644 (file)
index 0000000..4c02c87
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * Copyright © 2011 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+/*
+ * A variation on
+ *
+ * https://bugzilla.mozilla.org/show_bug.cgi?id=668921
+ *
+ * The issue is that we failed to tighten the initial approximated bounds
+ * after tessellating the path.
+ */
+
+#include "cairo-test.h"
+
+#define SIZE 60
+
+static void
+triangle (cairo_t *cr, double x, double y, double h)
+{
+    cairo_move_to (cr, x, y);
+    cairo_line_to (cr, x+h/2, y+h);
+    cairo_line_to (cr, x+h, y);
+    cairo_close_path (cr);
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_set_source_rgb (cr, 1, 0, 1);
+    cairo_paint (cr);
+
+    /* Set an unbounded operator so that we can see how accurate the bounded
+     * extents were.
+     */
+    cairo_set_operator (cr, CAIRO_OPERATOR_IN);
+    cairo_set_source_rgb (cr, 1, 1, 1);
+
+    /* Wind several triangles together that reduce to nothing */
+    cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD);
+    triangle (cr, 0, 0, SIZE);
+    triangle (cr, 0, 0, SIZE);
+    cairo_fill (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (big_empty_triangle,
+           "Tests that we tighten the bounds after tessellation.",
+           "fill", /* keywords */
+           NULL, /* requirements */
+           SIZE, SIZE,
+           NULL, draw)
diff --git a/test/big-line.c b/test/big-line.c
new file mode 100644 (file)
index 0000000..6260fba
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright © 2008 Novell, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Novell, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Novell, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * NOVELL, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL NOVELL, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Larry Ewing <lewing@novell.com>
+ */
+
+#include "cairo-test.h"
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_paint (cr);
+
+    cairo_set_source_rgb (cr, 1, 1, 0);
+    cairo_move_to (cr, 50, 50);
+    cairo_rel_line_to (cr, 50000, 50000);
+    cairo_stroke (cr);
+
+    cairo_set_source_rgb (cr, 1, 0, 0);
+    cairo_move_to (cr, 50, 50);
+    cairo_rel_line_to (cr, -50000, 50000);
+    cairo_stroke (cr);
+
+    cairo_set_source_rgb (cr, 0, 1, 0);
+    cairo_move_to (cr, 50, 50);
+    cairo_rel_line_to (cr, 50000, -50000);
+    cairo_stroke (cr);
+
+    cairo_set_source_rgb (cr, 0, 0, 1);
+    cairo_move_to (cr, 50, 50);
+    cairo_rel_line_to (cr, -50000, -50000);
+    cairo_stroke (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (big_line,
+           "Test drawing of simple lines with positive and negative coordinates > 2^16",
+           "stroke, line", /* keywords */
+           NULL, /* requirements */
+           100, 100,
+           NULL, draw)
diff --git a/test/big-little-box.c b/test/big-little-box.c
new file mode 100644 (file)
index 0000000..1787ee5
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * Copyright © 2011 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+/*
+ * This attempts to exercise the bug found in
+ *
+ * https://bugzilla.mozilla.org/show_bug.cgi?id=668921
+ *
+ * and also identified by Taekyun Kim.
+ *
+ * The issue is that we failed to tighten the initial approximated bounds
+ * after tessellating the path.
+ */
+
+#include "cairo-test.h"
+
+#define SIZE 60
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_set_source_rgb (cr, 1, 0, 1);
+    cairo_paint (cr);
+
+    /* Set an unbounded operator so that we can see how accurate the bounded
+     * extents were.
+     */
+    cairo_set_operator (cr, CAIRO_OPERATOR_IN);
+    cairo_set_source_rgb (cr, 1, 1, 1);
+
+    /* Wind several boxes together that reduce to just one */
+    cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD);
+    cairo_rectangle (cr, 0, 0, SIZE, SIZE);
+    cairo_rectangle (cr, 0, 0, SIZE, SIZE);
+    cairo_rectangle (cr, SIZE/2 - 20, SIZE/2 - 20, 40, 40);
+    cairo_fill (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (big_little_box,
+           "Tests that we tighten the bounds after tessellation.",
+           "fill", /* keywords */
+           NULL, /* requirements */
+           SIZE, SIZE,
+           NULL, draw)
diff --git a/test/big-little-triangle.c b/test/big-little-triangle.c
new file mode 100644 (file)
index 0000000..27eb232
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * Copyright © 2011 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+/*
+ * A variation on
+ *
+ * https://bugzilla.mozilla.org/show_bug.cgi?id=668921
+ *
+ * The issue is that we failed to tighten the initial approximated bounds
+ * after tessellating the path.
+ */
+
+#include "cairo-test.h"
+
+#define SIZE 60
+
+static void
+triangle (cairo_t *cr, double x, double y, double h)
+{
+    cairo_move_to (cr, x, y);
+    cairo_line_to (cr, x+h/2, y+h);
+    cairo_line_to (cr, x+h, y);
+    cairo_close_path (cr);
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_set_source_rgb (cr, 1, 0, 1);
+    cairo_paint (cr);
+
+    /* Set an unbounded operator so that we can see how accurate the bounded
+     * extents were.
+     */
+    cairo_set_operator (cr, CAIRO_OPERATOR_IN);
+    cairo_set_source_rgb (cr, 1, 1, 1);
+
+    /* Wind several triangles together that reduce to just one */
+    cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD);
+    triangle (cr, 0, 0, SIZE);
+    triangle (cr, 0, 0, SIZE);
+    triangle (cr, SIZE/2-20, SIZE/2 - 20, 40);
+    cairo_fill (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (big_little_triangle,
+           "Tests that we tighten the bounds after tessellation.",
+           "fill", /* keywords */
+           NULL, /* requirements */
+           SIZE, SIZE,
+           NULL, draw)
diff --git a/test/big-trap.c b/test/big-trap.c
new file mode 100644 (file)
index 0000000..507e0e5
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * Copyright © 2006 Mozilla Corporation
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Mozilla Corporation not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Mozilla Corporation makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * MOZILLA CORPORATION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL MOZILLA CORPORATION BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Vladimir Vukicevic <vladimir@pobox.com>
+ */
+
+#include "cairo-test.h"
+
+/* This test was originally written to exercise a bug in pixman in
+ * which it would scribble all over memory when given a particular
+ * (and bogus) trapezoid. However, a recent change to
+ * _cairo_fixed_from_double changed the details of the bogus trapezoid
+ * (it overflows in a different way now), so the bug is being masked.
+ *
+ * According to Vladimir, (http://lists.freedesktop.org/archives/cairo/2006-November/008482.html):
+ *
+ *     Before the change, the two trapezoids that were generated were:
+ *
+ *     Trap[0]: T: 0x80000000 B: 0x80000003
+ *        L: [(0x000a0000, 0x80000000) (0x00080000, 0x00080000)]
+ *        R: [(0x01360000, 0x80000000) (0x01380000, 0x00080000)]
+ *     Trap[1]: T: 0x80000003 B: 0x00080000
+ *        L: [(0x000a0000, 0x80000000) (0x00080000, 0x00080000)]
+ *        R: [(0x01360000, 0x80000000) (0x01380000, 0x00080000)]
+ *
+ *     After the change, the L/R coordinates are identical for both traps, but
+ *     the top and bottom change:
+ *
+ *     Trap[0]: t: 0x80000000 b: 0xfda80003
+ *        l: [(0x000a0000, 0x80000000) (0x00080000, 0x00080000)]
+ *        r: [(0x01360000, 0x80000000) (0x01380000, 0x00080000)]
+ *     Trap[1]: t: 0xfda80003 b: 0x00080000
+ *        l: [(0x000a0000, 0x80000000) (0x00080000, 0x00080000)]
+ *        r: [(0x01360000, 0x80000000) (0x01380000, 0x00080000)]
+ *
+ * I think the fix we want here is to rewrite this test to call
+ * directly into pixman with the trapezoid of interest, (which will
+ * require adding a new way to configure cairo for "testing" which
+ * will prevent the hiding of internal library symbols.
+ */
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_set_source_rgb (cr, 1,1,1);
+    cairo_paint (cr);
+
+    cairo_set_source_rgb (cr, 0,0,0);
+
+    /* Note that without the clip, this doesn't crash... */
+    cairo_new_path (cr);
+    cairo_rectangle (cr, 0, 0, width, height);
+    cairo_clip (cr);
+
+    cairo_new_path (cr);
+    cairo_line_to (cr, 8.0, 8.0);
+    cairo_line_to (cr, 312.0, 8.0);
+    cairo_line_to (cr, 310.0, 31378756.2666666666);
+    cairo_line_to (cr, 10.0, 31378756.2666666666);
+    cairo_line_to (cr, 8.0, 8.0);
+    cairo_fill (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (big_trap,
+           "Test oversize trapezoid with a clip region"
+           "\nTest needs to be adjusted to trigger the original bug",
+           "trap", /* keywords */
+           NULL, /* requirements */
+           100, 100,
+           NULL, draw)
diff --git a/test/bilevel-image.c b/test/bilevel-image.c
new file mode 100644 (file)
index 0000000..4feff0e
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Copyright © 2008 Chris Wilson
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Chris Wilson not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Chris Wilson makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * CHRIS WILSON DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL CHRIS WILSON BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+
+#define RGBx 0xffff0000, 0xff00ff00, 0xff0000ff, 0x00000000
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    uint32_t data[] = {
+       RGBx, RGBx, RGBx,
+       RGBx, RGBx, RGBx,
+       RGBx, RGBx, RGBx,
+       RGBx, RGBx, RGBx,
+    };
+    cairo_surface_t *mask;
+
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_paint (cr);
+
+    mask = cairo_image_surface_create_for_data ((unsigned char *) data,
+                                               CAIRO_FORMAT_ARGB32, 12, 4, 48);
+
+    cairo_set_source_surface (cr, mask, 0, 0);
+    cairo_pattern_set_filter (cairo_get_source (cr), CAIRO_FILTER_NEAREST);
+
+    cairo_paint (cr);
+
+    cairo_surface_finish (mask); /* data goes out of scope */
+    cairo_surface_destroy (mask);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (bilevel_image,
+           "Test that PS can embed an RGB image with a bilevel alpha channel.",
+           "alpha, ps", /* keywords */
+           NULL, /* requirements */
+           12, 4,
+           NULL, draw)
diff --git a/test/bitmap-font.c b/test/bitmap-font.c
new file mode 100644 (file)
index 0000000..62e51ea
--- /dev/null
@@ -0,0 +1,217 @@
+/*
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ */
+
+#include "cairo-test.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <cairo-ft.h>
+#include <fontconfig/fontconfig.h>
+#include <fontconfig/fcfreetype.h>
+
+#define FONT "6x13.pcf"
+#define TEXT_SIZE 13
+
+static cairo_bool_t
+font_extents_equal (const cairo_font_extents_t *A,
+                   const cairo_font_extents_t *B)
+{
+    return
+       CAIRO_TEST_DOUBLE_EQUALS (A->ascent,  B->ascent)  &&
+       CAIRO_TEST_DOUBLE_EQUALS (A->descent, B->descent) &&
+       CAIRO_TEST_DOUBLE_EQUALS (A->height,  B->height)  &&
+       CAIRO_TEST_DOUBLE_EQUALS (A->max_x_advance, B->max_x_advance) &&
+       CAIRO_TEST_DOUBLE_EQUALS (A->max_y_advance, B->max_y_advance);
+}
+
+static cairo_test_status_t
+check_font_extents (const cairo_test_context_t *ctx, cairo_t *cr, const char *comment)
+{
+    cairo_font_extents_t font_extents, ref_font_extents = {11, 2, 13, 6, 0};
+    cairo_status_t status;
+
+    memset (&font_extents, 0xff, sizeof (cairo_font_extents_t));
+    cairo_font_extents (cr, &font_extents);
+
+    status = cairo_status (cr);
+    if (status)
+       return cairo_test_status_from_status (ctx, status);
+
+    if (! font_extents_equal (&font_extents, &ref_font_extents)) {
+       cairo_test_log (ctx, "Error: %s: cairo_font_extents(); extents (%g, %g, %g, %g, %g)\n",
+                       comment,
+                       font_extents.ascent, font_extents.descent,
+                       font_extents.height,
+                       font_extents.max_x_advance, font_extents.max_y_advance);
+       return CAIRO_TEST_FAILURE;
+    }
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    const cairo_test_context_t *ctx = cairo_test_get_context (cr);
+    FcPattern *pattern;
+    cairo_font_face_t *font_face;
+    cairo_font_extents_t font_extents;
+    cairo_font_options_t *font_options;
+    cairo_status_t status;
+    char *filename;
+    int face_count;
+    struct stat stat_buf;
+
+    xasprintf (&filename, "%s/%s", ctx->srcdir, FONT);
+
+    if (stat (filename, &stat_buf) || ! S_ISREG (stat_buf.st_mode)) {
+       cairo_test_log (ctx, "Error finding font: %s: file not found?\n", filename);
+       return CAIRO_TEST_FAILURE;
+    }
+
+    pattern = FcFreeTypeQuery ((unsigned char *)filename, 0, NULL, &face_count);
+    free (filename);
+    if (! pattern) {
+       cairo_test_log (ctx, "FcFreeTypeQuery failed.\n");
+       return cairo_test_status_from_status (ctx, CAIRO_STATUS_NO_MEMORY);
+    }
+
+    font_face = cairo_ft_font_face_create_for_pattern (pattern);
+    FcPatternDestroy (pattern);
+
+    status = cairo_font_face_status (font_face);
+    if (status) {
+       cairo_test_log (ctx, "Error creating font face for %s: %s\n",
+                       filename,
+                       cairo_status_to_string (status));
+       return cairo_test_status_from_status (ctx, status);
+    }
+
+    if (cairo_font_face_get_type (font_face) != CAIRO_FONT_TYPE_FT) {
+       cairo_test_log (ctx, "Unexpected value from cairo_font_face_get_type: %d (expected %d)\n",
+                       cairo_font_face_get_type (font_face), CAIRO_FONT_TYPE_FT);
+       cairo_font_face_destroy (font_face);
+       return CAIRO_TEST_FAILURE;
+    }
+
+    cairo_set_font_face (cr, font_face);
+    cairo_font_face_destroy (font_face);
+
+    font_options = cairo_font_options_create ();
+
+#define CHECK_FONT_EXTENTS(comment) do {\
+    cairo_test_status_t test_status; \
+    test_status = check_font_extents (ctx, cr, (comment)); \
+    if (test_status != CAIRO_TEST_SUCCESS) { \
+       cairo_font_options_destroy (font_options); \
+       return test_status; \
+    } \
+} while (0)
+
+    cairo_font_extents (cr, &font_extents);
+    CHECK_FONT_EXTENTS ("default");
+
+    cairo_font_options_set_hint_metrics (font_options, CAIRO_HINT_METRICS_ON);
+    cairo_set_font_options (cr, font_options);
+
+    CHECK_FONT_EXTENTS ("HINT_METRICS_ON");
+
+    cairo_move_to (cr, 1, font_extents.ascent - 1);
+    cairo_set_source_rgb (cr, 0.0, 0.0, 1.0); /* blue */
+
+    cairo_font_options_set_hint_style (font_options, CAIRO_HINT_STYLE_NONE);
+    cairo_set_font_options (cr, font_options);
+    CHECK_FONT_EXTENTS ("HINT_METRICS_ON HINT_STYLE_NONE");
+    cairo_show_text (cr, "the ");
+
+    cairo_font_options_set_hint_style (font_options, CAIRO_HINT_STYLE_SLIGHT);
+    cairo_set_font_options (cr, font_options);
+    CHECK_FONT_EXTENTS ("HINT_METRICS_ON HINT_STYLE_SLIGHT");
+    cairo_show_text (cr, "quick ");
+
+    cairo_font_options_set_hint_style (font_options, CAIRO_HINT_STYLE_MEDIUM);
+    cairo_set_font_options (cr, font_options);
+    CHECK_FONT_EXTENTS ("HINT_METRICS_ON HINT_STYLE_MEDIUM");
+    cairo_show_text (cr, "brown");
+
+    cairo_font_options_set_hint_style (font_options, CAIRO_HINT_STYLE_FULL);
+    cairo_set_font_options (cr, font_options);
+    CHECK_FONT_EXTENTS ("HINT_METRICS_ON HINT_STYLE_FULL");
+    cairo_show_text (cr, " fox");
+
+    /* Switch from show_text to text_path/fill to exercise bug #7889 */
+    cairo_text_path (cr, " jumps over a lazy dog");
+    cairo_fill (cr);
+
+    /* And test it rotated as well for the sake of bug #7888 */
+
+    cairo_translate (cr, width, height);
+    cairo_rotate (cr, M_PI);
+
+    cairo_font_options_set_hint_style (font_options, CAIRO_HINT_STYLE_DEFAULT);
+    cairo_font_options_set_hint_metrics (font_options, CAIRO_HINT_METRICS_OFF);
+    cairo_set_font_options (cr, font_options);
+    CHECK_FONT_EXTENTS ("HINT_METRICS_OFF");
+
+    cairo_move_to (cr, 1, font_extents.height - font_extents.descent - 1);
+
+    cairo_font_options_set_hint_style (font_options, CAIRO_HINT_STYLE_NONE);
+    cairo_set_font_options (cr, font_options);
+    CHECK_FONT_EXTENTS ("HINT_METRICS_OFF HINT_STYLE_NONE");
+    cairo_show_text (cr, "the ");
+
+    cairo_font_options_set_hint_style (font_options, CAIRO_HINT_STYLE_SLIGHT);
+    cairo_set_font_options (cr, font_options);
+    CHECK_FONT_EXTENTS ("HINT_METRICS_OFF HINT_STYLE_SLIGHT");
+    cairo_show_text (cr, "quick");
+
+    cairo_font_options_set_hint_style (font_options, CAIRO_HINT_STYLE_MEDIUM);
+    cairo_set_font_options (cr, font_options);
+    CHECK_FONT_EXTENTS ("HINT_METRICS_OFF HINT_STYLE_MEDIUM");
+    cairo_show_text (cr, " brown");
+
+    cairo_font_options_set_hint_style (font_options, CAIRO_HINT_STYLE_FULL);
+    cairo_set_font_options (cr, font_options);
+    CHECK_FONT_EXTENTS ("HINT_METRICS_OFF HINT_STYLE_FULL");
+    cairo_show_text (cr, " fox");
+
+    cairo_text_path (cr, " jumps over");
+    cairo_text_path (cr, " a lazy dog");
+    cairo_fill (cr);
+
+    cairo_font_options_destroy (font_options);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (bitmap_font,
+           "Test drawing with a font consisting only of bitmaps"
+           "\nThe PDF and PS backends embed a slightly distorted font for the rotated case.",
+           "text", /* keywords */
+           "ft", /* requirements */
+           246 + 1, 2 * TEXT_SIZE,
+           NULL, draw)
diff --git a/test/buffer-diff.c b/test/buffer-diff.c
new file mode 100644 (file)
index 0000000..087ac45
--- /dev/null
@@ -0,0 +1,262 @@
+/* imagediff - Compare two images
+ *
+ * Copyright © 2004 Richard D. Worth
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of Richard Worth
+ * not be used in advertising or publicity pertaining to distribution
+ * of the software without specific, written prior permission.
+ * Richard Worth makes no representations about the suitability of this
+ * software for any purpose.  It is provided "as is" without express
+ * or implied warranty.
+ *
+ * RICHARD WORTH DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
+ * NO EVENT SHALL RICHARD WORTH BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+ * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+ * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Richard D. Worth <richard@theworths.org> */
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <errno.h>
+#include <string.h>
+#include <pixman.h>
+
+#include "cairo-test.h"
+
+#include "pdiff.h"
+#include "buffer-diff.h"
+
+/* Don't allow any differences greater than this value, even if pdiff
+ * claims that the images are identical */
+#define PERCEPTUAL_DIFF_THRESHOLD 25
+
+/* Compare two buffers, returning the number of pixels that are
+ * different and the maximum difference of any single color channel in
+ * result_ret.
+ *
+ * This function should be rewritten to compare all formats supported by
+ * cairo_format_t instead of taking a mask as a parameter.
+ */
+static void
+buffer_diff_core (const unsigned char *_buf_a, int stride_a,
+                 const unsigned char *_buf_b, int stride_b,
+                 unsigned char *_buf_diff, int stride_diff,
+                 int           width,
+                 int           height,
+                 uint32_t mask,
+                 buffer_diff_result_t *result_ret)
+{
+    const uint32_t *buf_a = (const uint32_t*) _buf_a;
+    const uint32_t *buf_b = (const uint32_t*) _buf_b;
+    uint32_t *buf_diff = (uint32_t*) _buf_diff;
+    int x, y;
+    buffer_diff_result_t result = {0, 0};
+
+    stride_a /= sizeof (uint32_t);
+    stride_b /= sizeof (uint32_t);
+    stride_diff /= sizeof (uint32_t);
+    for (y = 0; y < height; y++) {
+       const uint32_t *row_a = buf_a + y * stride_a;
+       const uint32_t *row_b = buf_b + y * stride_b;
+       uint32_t *row = buf_diff + y * stride_diff;
+
+       for (x = 0; x < width; x++) {
+           /* check if the pixels are the same */
+           if ((row_a[x] & mask) != (row_b[x] & mask)) {
+               int channel;
+               uint32_t diff_pixel = 0;
+
+               /* calculate a difference value for all 4 channels */
+               for (channel = 0; channel < 4; channel++) {
+                   int value_a = (row_a[x] >> (channel*8)) & 0xff;
+                   int value_b = (row_b[x] >> (channel*8)) & 0xff;
+                   unsigned int diff;
+                   diff = abs (value_a - value_b);
+                   if (diff > result.max_diff)
+                       result.max_diff = diff;
+                   diff *= 4;  /* emphasize */
+                   if (diff)
+                       diff += 128; /* make sure it's visible */
+                   if (diff > 255)
+                       diff = 255;
+                   diff_pixel |= diff << (channel*8);
+               }
+
+               result.pixels_changed++;
+               if ((diff_pixel & 0x00ffffff) == 0) {
+                   /* alpha only difference, convert to luminance */
+                   uint8_t alpha = diff_pixel >> 24;
+                   diff_pixel = alpha * 0x010101;
+               }
+               row[x] = diff_pixel;
+           } else {
+               row[x] = 0;
+           }
+           row[x] |= 0xff000000; /* Set ALPHA to 100% (opaque) */
+       }
+    }
+
+    *result_ret = result;
+}
+
+/* Compares two image surfaces
+ *
+ * Provides number of pixels changed and maximum single-channel
+ * difference in result.
+ *
+ * Also fills in a "diff" surface intended to visually show where the
+ * images differ.
+ */
+static void
+compare_surfaces (const cairo_test_context_t  *ctx,
+                 cairo_surface_t       *surface_a,
+                 cairo_surface_t       *surface_b,
+                 cairo_surface_t       *surface_diff,
+                 buffer_diff_result_t  *result)
+{
+    /* These default values were taken straight from the
+     * perceptualdiff program. We'll probably want to tune these as
+     * necessary. */
+    double gamma = 2.2;
+    double luminance = 100.0;
+    double field_of_view = 45.0;
+    int discernible_pixels_changed;
+
+    /* First, we run cairo's old buffer_diff algorithm which looks for
+     * pixel-perfect images, (we do this first since the test suite
+     * runs about 3x slower if we run pdiff_compare first).
+     */
+    buffer_diff_core (cairo_image_surface_get_data (surface_a),
+                     cairo_image_surface_get_stride (surface_a),
+                     cairo_image_surface_get_data (surface_b),
+                     cairo_image_surface_get_stride (surface_b),
+                     cairo_image_surface_get_data (surface_diff),
+                     cairo_image_surface_get_stride (surface_diff),
+                     cairo_image_surface_get_width (surface_a),
+                     cairo_image_surface_get_height (surface_a),
+                     cairo_surface_get_content (surface_a) & CAIRO_CONTENT_ALPHA ?  0xffffffff : 0x00ffffff,
+                     result);
+    if (result->pixels_changed == 0)
+       return;
+
+    cairo_test_log (ctx,
+                   "%d pixels differ (with maximum difference of %d) from reference image\n",
+                   result->pixels_changed, result->max_diff);
+
+    /* Then, if there are any different pixels, we give the pdiff code
+     * a crack at the images. If it decides that there are no visually
+     * discernible differences in any pixels, then we accept this
+     * result as good enough.
+     *
+     * Only let pdiff have a crack at the comparison if the max difference
+     * is lower than a threshold, otherwise some problems could be masked.
+     */
+    if (result->max_diff < PERCEPTUAL_DIFF_THRESHOLD) {
+        discernible_pixels_changed = pdiff_compare (surface_a, surface_b,
+                                                    gamma, luminance, field_of_view);
+        if (discernible_pixels_changed == 0) {
+            result->pixels_changed = 0;
+            cairo_test_log (ctx,
+                           "But perceptual diff finds no visually discernible difference.\n"
+                            "Accepting result.\n");
+        }
+    }
+}
+
+void
+buffer_diff_noalpha (const unsigned char *buf_a,
+                    const unsigned char *buf_b,
+                    unsigned char *buf_diff,
+                    int           width,
+                    int           height,
+                    int           stride,
+                    buffer_diff_result_t *result)
+{
+    buffer_diff_core(buf_a, stride,
+                    buf_b, stride,
+                    buf_diff, stride,
+                    width, height,
+                    0x00ffffff,
+                    result);
+}
+
+static cairo_bool_t
+same_size (cairo_surface_t *a, cairo_surface_t *b)
+{
+    unsigned int width_a, height_a;
+    unsigned int width_b, height_b;
+
+    width_a = cairo_image_surface_get_width (a);
+    height_a = cairo_image_surface_get_height (a);
+
+    width_b = cairo_image_surface_get_width (b);
+    height_b = cairo_image_surface_get_height (b);
+
+    return width_a == width_b && height_a == height_b;
+}
+
+/* Image comparison code courtesy of Richard Worth <richard@theworths.org>
+ * Returns number of pixels changed, (or -1 on error).
+ * Also saves a "diff" image intended to visually show where the
+ * images differ.
+ *
+ * The return value simply indicates whether a check was successfully
+ * made, (as opposed to a file-not-found condition or similar). It
+ * does not indicate anything about how much the images differ. For
+ * that, see result.
+ *
+ * One failure mode is if the two images provided do not have the same
+ * dimensions. In this case, this function will return
+ * CAIRO_STATUS_SURFACE_TYPE_MISMATCH (which is a bit of an abuse, but
+ * oh well).
+ */
+cairo_status_t
+image_diff (const cairo_test_context_t *ctx,
+           cairo_surface_t *surface_a,
+           cairo_surface_t *surface_b,
+           cairo_surface_t *surface_diff,
+           buffer_diff_result_t *result)
+{
+    if (cairo_surface_status (surface_a))
+       return cairo_surface_status (surface_a);
+
+    if (cairo_surface_status (surface_b))
+       return cairo_surface_status (surface_b);
+
+    if (cairo_surface_status (surface_diff))
+       return cairo_surface_status (surface_diff);
+
+    if (! same_size (surface_a, surface_b) ||
+       ! same_size (surface_a, surface_diff))
+    {
+       cairo_test_log (ctx, "Error: Image size mismatch\n");
+       return CAIRO_STATUS_SURFACE_TYPE_MISMATCH;
+    }
+
+    compare_surfaces (ctx, surface_a, surface_b, surface_diff, result);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_bool_t
+image_diff_is_failure (const buffer_diff_result_t *result,
+                       unsigned int                tolerance)
+{
+  return result->pixels_changed &&
+         result->max_diff > tolerance;
+}
diff --git a/test/buffer-diff.h b/test/buffer-diff.h
new file mode 100644 (file)
index 0000000..2cbb895
--- /dev/null
@@ -0,0 +1,73 @@
+/* imagediff - Compare two images
+ *
+ * Copyright © 2004 Richard D. Worth
+ * Copyright © 2006 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of the authors
+ * not be used in advertising or publicity pertaining to distribution
+ * of the software without specific, written prior permission.
+ * The authors make no representations about the suitability of this
+ * software for any purpose.  It is provided "as is" without express
+ * or implied warranty.
+ *
+ * THE AUTHORS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
+ * NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+ * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+ * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Authors: Richard D. Worth <richard@theworths.org>
+ *         Carl Worth <cworth@cworth.org>
+ */
+
+#ifndef BUFFER_DIFF_H
+#define BUFFER_DIFF_H
+
+#include "cairo-test.h"
+
+typedef struct _buffer_diff_result {
+    unsigned int pixels_changed;
+    unsigned int max_diff;
+} buffer_diff_result_t;
+
+/* Compares two image buffers ignoring the alpha channel.
+ *
+ * Provides number of pixels changed and maximum single-channel
+ * difference in result.
+ *
+ * Also fills in a "diff" buffer intended to visually show where the
+ * images differ.
+ */
+void
+buffer_diff_noalpha (const unsigned char *buf_a,
+                    const unsigned char *buf_b,
+                    unsigned char *buf_diff,
+                    int            width,
+                    int            height,
+                    int            stride,
+                    buffer_diff_result_t *result);
+
+/* The central algorithm to compare two images, and return the differences
+ * in the surface_diff.
+ *
+ * Provides number of pixels changed and maximum single-channel
+ * difference in result.
+ */
+cairo_status_t
+image_diff (const cairo_test_context_t *ctx,
+           cairo_surface_t *surface_a,
+           cairo_surface_t *surface_b,
+           cairo_surface_t *surface_diff,
+           buffer_diff_result_t *result);
+
+cairo_bool_t
+image_diff_is_failure (const buffer_diff_result_t *result,
+                       unsigned int                tolerance);
+
+#endif
diff --git a/test/bug-40410.c b/test/bug-40410.c
new file mode 100644 (file)
index 0000000..2d6e512
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * Copyright © 2011 Krzysztof Kosiński
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Krzysztof Kosiński <tweenk.pl@gmail.com>
+ */
+
+#include "cairo-test.h"
+
+#define WIDTH  300
+#define HEIGHT 100
+
+/*
+ * The bug appears to be triggered if:
+ * 1. There is more than one subpath
+ * 2. All subpaths are axis-aligned rectangles
+ * 3. Only one of the subpaths is within surface bounds
+ *
+ * Tweaking any of the coordinates so that there is at least one
+ * non-axis-aligned segment or removing the second subpath (the one that is
+ * outside the surface bounds) causes the bug to disappear.
+ */
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_set_source_rgb (cr, 1,1,1);
+    cairo_paint (cr);
+
+    cairo_move_to (cr, 10.3, 10.6);
+    cairo_line_to (cr, 10.3, 150.2);
+    cairo_line_to (cr, 290.1, 150.2);
+    cairo_line_to (cr, 290.1, 10.6);
+    cairo_close_path (cr);
+
+    cairo_move_to (cr, 10.3, 180.7);
+    cairo_line_to (cr, 10.3, 230.2);
+    cairo_line_to (cr, 290.1, 230.2);
+    cairo_line_to (cr, 290.1, 180.7);
+    cairo_close_path (cr);
+
+    cairo_set_source_rgb (cr, 1, 0, 0);
+    cairo_fill (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (bug_40410,
+           "Exercises a bug found in 1.10.2 (and never again!)",
+           "fill", /* keywords */
+           "target=raster", /* requirements */
+           WIDTH, HEIGHT,
+           NULL, draw)
diff --git a/test/bug-bo-rectangular.c b/test/bug-bo-rectangular.c
new file mode 100644 (file)
index 0000000..08e2e49
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2010 Red Hat
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Intel not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Intel makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Benjamin Otte <otte@gnome.com>
+ */
+
+#include "cairo-test.h"
+
+static void
+rect (cairo_t *cr, int x1, int y1, int x2, int y2)
+{
+    cairo_rectangle (cr, x1, y1, x2 - x1, y2 - y1);
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_paint (cr);
+
+    cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD);
+
+    cairo_scale (cr, 1./256, 1./256);
+
+    rect (cr, 0, 0, 29696, 7680);
+    rect (cr, 0, 0, -15360, 15360);
+    cairo_clip (cr);
+
+    cairo_set_source_rgb (cr, 1, 0.5, 0);
+    cairo_paint (cr);
+
+    rect (cr, 9984, 0, 2969, 3840);
+    rect (cr, 0, 3840, 9472, 7680);
+    cairo_clip (cr);
+
+    rect (cr, 0, 3840, 3584, 7680);
+    cairo_set_source_rgb (cr, 1, 0, 0.5);
+    cairo_fill (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (bug_bo_rectangular,
+           "Tests a bug found by Benjamin Otte in the rectangular tessellator",
+           "tessellator", /* keywords */
+           NULL, /* requirements */
+           300, 300,
+           NULL, draw)
diff --git a/test/bug-bo-ricotz.c b/test/bug-bo-ricotz.c
new file mode 100644 (file)
index 0000000..9524d20
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * Copyright © 2011 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+
+/* An assertion failure found by Rico Tzschichholz */
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_paint (cr);
+    cairo_set_source_rgb (cr, 0, 0, 0);
+
+    cairo_rectangle (cr, 10, 55,165, 1);
+    cairo_rectangle (cr, 174, 55,1, 413);
+    cairo_rectangle (cr, 10, 56, 1, 413);
+    cairo_rectangle (cr, 10, 469, 165, 1);
+    cairo_clip (cr);
+
+    cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD);
+
+    cairo_move_to (cr, 10, 57);
+    cairo_curve_to (cr, 10, 55.894531, 10.894531, 55, 12, 55);
+    cairo_line_to (cr, 173, 55);
+    cairo_curve_to (cr, 174.105469, 55, 175, 55.894531, 175, 57);
+    cairo_line_to (cr, 175, 468);
+    cairo_curve_to (cr, 175, 469.105469, 174.105469, 470, 173, 470);
+    cairo_line_to (cr, 12, 470);
+    cairo_curve_to (cr, 10.894531, 470, 10, 469.105469, 10, 468);
+
+    cairo_move_to (cr, 11, 57);
+    cairo_curve_to (cr, 11, 56.449219, 11.449219, 56, 12, 56);
+    cairo_line_to (cr, 173, 56);
+    cairo_curve_to (cr, 173.550781, 56, 174, 56.449219, 174, 57);
+    cairo_line_to (cr, 174, 468);
+    cairo_curve_to (cr, 174, 468.550781, 173.550781, 469, 173, 469);
+    cairo_line_to (cr, 12, 469);
+    cairo_curve_to (cr, 11.449219, 469, 11, 468.550781, 11, 468);
+
+    cairo_fill (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (bug_bo_ricotz,
+           "Exercises a bug discovered by Rico Tzschichholz",
+           "clip, fill", /* keywords */
+           NULL, /* requirements */
+           649, 480,
+           NULL, draw)
diff --git a/test/bug-extents.c b/test/bug-extents.c
new file mode 100644 (file)
index 0000000..4edb4a5
--- /dev/null
@@ -0,0 +1,59 @@
+/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/*
+ * Copyright 2010 Andrea Canciani
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Andrea Canciani <ranma42@gmail.com>
+ */
+
+#include "cairo-test.h"
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_paint (cr);
+
+    cairo_translate (cr, 0, -25);
+
+    cairo_move_to  (cr,   50, 200);
+    cairo_curve_to (cr,   50, 150, 100,  50, 150,  50);
+    cairo_curve_to (cr,  200,  50, 250, 250, 200, 250);
+    cairo_curve_to (cr,  150, 250, 200,  50,  50, 100);
+    cairo_curve_to (cr, -100, 150, 200, 150, 200, 200);
+    cairo_curve_to (cr,  200, 250,  50, 250,  50, 200);
+
+    cairo_set_source_rgb (cr, 0, 0, 0);
+    cairo_fill_preserve (cr);
+
+    cairo_set_source_rgb (cr, 1, 0, 0);
+    cairo_stroke (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (bug_extents,
+           "Tests a bug in the computation of approximate extents",
+           "extents", /* keywords */
+           NULL, /* requirements */
+           250, 250,
+           NULL, draw)
diff --git a/test/bug-seams.c b/test/bug-seams.c
new file mode 100644 (file)
index 0000000..01e84ff
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ * Copyright 2010 Soeren Sandmann Pedersen
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Soeren Sandmann <sandmann@daimi.au.dk>
+ */
+
+/* Exercises a case of seam appearing between two polygons in the image
+ * backend but not in xlib [using pixman].
+ *
+ * The test case draws two abutting quads both individually on the
+ * leftm combining them with operator ADD, and in one go on the right.
+ * Both methods should show no signs of seaming at the common edge
+ * between the two quads, but the individually drawn ones have a
+ * slight seam.
+ *
+ * The cause of the seam is that there are slight differences in the
+ * output of the analytical coverage rasterization and the
+ * supersampling rasterization methods, both employed by
+ * cairo-tor-scan-converter.  When drawn individually, the scan
+ * converter gets a partial view of the geometry at a time, so it ends
+ * up making different decisions about which scanlines it rasterizes
+ * with which method, compared to when the geometry is draw all at
+ * once.  Though both methods produce seamless results individually
+ * (where applicable), they don't produce bit-exact identical results,
+ * and hence we get seaming where they meet.
+ */
+
+#include "cairo-test.h"
+
+static void
+draw_quad (cairo_t *cr,
+          double x1, double y1, double x2, double y2,
+          double x3, double y3, double x4, double y4)
+{
+    cairo_move_to (cr, x1, y1);
+    cairo_line_to (cr, x2, y2);
+    cairo_line_to (cr, x3, y3);
+    cairo_line_to (cr, x4, y4);
+    cairo_close_path (cr);
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_set_source_rgb (cr, 0, 0, 0);
+    cairo_paint (cr);
+
+    cairo_scale (cr, 20, 20);
+    cairo_translate (cr, 5, 1);
+
+    /* On the left side, we have two quads drawn one at a time and
+     * combined with OPERATOR_ADD.  This should be seamless, but
+     * isn't. */
+    cairo_push_group (cr);
+    cairo_set_operator (cr, CAIRO_OPERATOR_ADD);
+
+    cairo_set_source_rgb (cr, 0, 0.6, 0);
+    draw_quad (cr,
+              1.50, 1.50,
+              2.64, 1.63,
+              1.75, 2.75,
+              0.55, 2.63);
+    cairo_fill (cr);
+    draw_quad (cr,
+              0.55, 2.63,
+              1.75, 2.75,
+              0.98, 4.11,
+              -0.35, 4.05);
+    cairo_fill (cr);
+
+    cairo_pop_group_to_source (cr);
+    cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
+    cairo_paint (cr);
+
+    /* On the right side, we have the same two quads drawn both at the
+     * same time.  This is seamless. */
+    cairo_translate (cr, 10, 0);
+
+    cairo_set_source_rgb (cr, 0, 0.6, 0);
+    draw_quad (cr,
+              1.50, 1.50,
+              2.64, 1.63,
+              1.75, 2.75,
+              0.55, 2.63);
+    draw_quad (cr,
+              0.55, 2.63,
+              1.75, 2.75,
+              0.98, 4.11,
+              -0.35, 4.05);
+    cairo_fill (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (bug_seams,
+           "Check the fidelity of the rasterisation.",
+           "raster", /* keywords */
+           "target=raster", /* requirements */
+           500, 300,
+           NULL, draw)
diff --git a/test/cairo-test-private.h b/test/cairo-test-private.h
new file mode 100644 (file)
index 0000000..bd0c0ac
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * Copyright © 2004 Red Hat, Inc.
+ * Copyright © 2008 Chris Wilson
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ *         Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#ifndef _CAIRO_TEST_PRIVATE_H_
+#define _CAIRO_TEST_PRIVATE_H_
+
+#include "cairo-test.h"
+
+/* For communication between the core components of cairo-test and not
+ * for the tests themselves.
+ */
+
+CAIRO_BEGIN_DECLS
+
+typedef enum {
+    DIRECT,
+    SIMILAR
+} cairo_test_similar_t;
+
+cairo_test_similar_t
+cairo_test_target_has_similar (const cairo_test_context_t *ctx,
+                              const cairo_boilerplate_target_t *target);
+
+cairo_test_status_t
+_cairo_test_context_run (cairo_test_context_t *ctx);
+
+cairo_test_status_t
+_cairo_test_context_run_for_target (cairo_test_context_t *ctx,
+                                   const cairo_boilerplate_target_t *target,
+                                   cairo_bool_t similar,
+                                   int dev_offset);
+
+void
+_cairo_test_context_init_for_test (cairo_test_context_t *ctx,
+                                  const cairo_test_context_t *parent,
+                                  const cairo_test_t *test);
+
+void
+cairo_test_init (cairo_test_context_t *ctx,
+                const char *test_name,
+                const char *output);
+
+cairo_test_status_t
+cairo_test (const cairo_test_t *test);
+
+void
+cairo_test_fini (cairo_test_context_t *ctx);
+
+void
+_cairo_test_runner_register_tests (void);
+
+CAIRO_END_DECLS
+
+#endif /* _CAIRO_TEST_PRIVATE_H_ */
diff --git a/test/cairo-test-runner.c b/test/cairo-test-runner.c
new file mode 100644 (file)
index 0000000..28e4b5d
--- /dev/null
@@ -0,0 +1,1103 @@
+/*
+ * Copyright © 2008 Chris Wilson
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Chris Wilson not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Chris Wilson makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * CHRIS WILSON DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL CHRIS WILSON BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test-private.h"
+#include "cairo-boilerplate-getopt.h"
+
+/* get the "real" version info instead of dummy cairo-version.h */
+#undef CAIRO_VERSION_H
+#undef CAIRO_VERSION_MAJOR
+#undef CAIRO_VERSION_MINOR
+#undef CAIRO_VERSION_MICRO
+#include "../cairo-version.h"
+
+#include <pixman.h> /* for version information */
+
+/* Coregraphics doesn't seem to like being forked and reports:
+ * "The process has forked and you cannot use this CoreFoundation functionality safely. You MUST exec()."
+ * so we don't for on OS X */
+#define SHOULD_FORK HAVE_FORK && HAVE_WAITPID && !__APPLE__
+#if SHOULD_FORK
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if HAVE_SIGNAL_H
+#include <signal.h>
+#endif
+#include <sys/types.h>
+#include <sys/wait.h>
+#endif
+#if HAVE_LIBGEN_H
+#include <libgen.h>
+#endif
+
+#if HAVE_VALGRIND
+#include <valgrind.h>
+#else
+#define RUNNING_ON_VALGRIND 0
+#endif
+
+#ifdef _MSC_VER
+#include <crtdbg.h>
+#endif
+
+typedef struct _cairo_test_list {
+    const cairo_test_t *test;
+    struct _cairo_test_list *next;
+} cairo_test_list_t;
+
+typedef struct _cairo_test_runner {
+    cairo_test_context_t base;
+
+    unsigned int num_device_offsets;
+
+    cairo_bool_t passed;
+    int num_passed;
+    int num_skipped;
+    int num_failed;
+    int num_xfailed;
+    int num_error;
+    int num_crashed;
+
+    cairo_test_list_t *crashes_preamble;
+    cairo_test_list_t *errors_preamble;
+    cairo_test_list_t *fails_preamble;
+
+    cairo_test_list_t **crashes_per_target;
+    cairo_test_list_t **errors_per_target;
+    cairo_test_list_t **fails_per_target;
+
+    int *num_failed_per_target;
+    int *num_error_per_target;
+    int *num_crashed_per_target;
+
+    cairo_bool_t foreground;
+    cairo_bool_t exit_on_failure;
+    cairo_bool_t list_only;
+    cairo_bool_t full_test;
+    cairo_bool_t keyword_match;
+    cairo_bool_t slow;
+    cairo_bool_t force_pass;
+} cairo_test_runner_t;
+
+typedef enum {
+    GE,
+    GT
+} cairo_test_compare_op_t;
+
+static cairo_test_t *tests;
+
+static void CAIRO_BOILERPLATE_PRINTF_FORMAT(2,3)
+_log (cairo_test_context_t *ctx,
+      const char *fmt,
+      ...)
+{
+    va_list ap;
+
+    va_start (ap, fmt);
+    vprintf (fmt, ap);
+    va_end (ap);
+
+    va_start (ap, fmt);
+    cairo_test_logv (ctx, fmt, ap);
+    va_end (ap);
+}
+
+static void
+_tests_reverse (void)
+{
+    cairo_test_t *list, *next;
+
+    for (list = tests, tests = NULL; list != NULL; list = next) {
+       next = list->next;
+       list->next = tests;
+       tests = list;
+    }
+}
+
+static cairo_test_list_t *
+_list_prepend (cairo_test_list_t *head, const cairo_test_t *test)
+{
+    cairo_test_list_t *list;
+
+    list = xmalloc (sizeof (cairo_test_list_t));
+    list->test = test;
+    list->next = head;
+    head = list;
+
+    return head;
+}
+
+static cairo_test_list_t *
+_list_reverse (cairo_test_list_t *head)
+{
+    cairo_test_list_t *list, *next;
+
+    for (list = head, head = NULL; list != NULL; list = next) {
+       next = list->next;
+       list->next = head;
+       head = list;
+    }
+
+    return head;
+}
+
+static void
+_list_free (cairo_test_list_t *list)
+{
+    while (list != NULL) {
+       cairo_test_list_t *next = list->next;
+       free (list);
+       list = next;
+    }
+}
+
+static cairo_bool_t
+is_running_under_debugger (void)
+{
+#if HAVE_UNISTD_H && HAVE_LIBGEN_H && __linux__
+    char buf[1024];
+
+    sprintf (buf, "/proc/%d/exe", getppid ());
+    if (readlink (buf, buf, sizeof (buf)) != -1 &&
+       strncmp (basename (buf), "gdb", 3) == 0)
+    {
+       return TRUE;
+    }
+#endif
+
+    if (RUNNING_ON_VALGRIND)
+       return TRUE;
+
+    return FALSE;
+}
+
+#if SHOULD_FORK
+static cairo_test_status_t
+_cairo_test_wait (pid_t pid)
+{
+    int exitcode;
+
+    if (waitpid (pid, &exitcode, 0) != pid)
+       return CAIRO_TEST_CRASHED;
+
+    if (WIFSIGNALED (exitcode)) {
+       switch (WTERMSIG (exitcode)) {
+       case SIGINT:
+#if HAVE_RAISE
+           raise (SIGINT);
+#endif
+           return CAIRO_TEST_UNTESTED;
+       default:
+           return CAIRO_TEST_CRASHED;
+       }
+    }
+
+    return WEXITSTATUS (exitcode);
+}
+#endif
+
+static cairo_test_status_t
+_cairo_test_runner_preamble (cairo_test_runner_t *runner,
+                            cairo_test_context_t *ctx)
+{
+#if SHOULD_FORK
+    if (! runner->foreground) {
+       pid_t pid;
+
+       switch ((pid = fork ())) {
+       case -1: /* error */
+           return CAIRO_TEST_UNTESTED;
+
+       case 0: /* child */
+           exit (ctx->test->preamble (ctx));
+
+       default:
+           return _cairo_test_wait (pid);
+       }
+    }
+#endif
+    return ctx->test->preamble (ctx);
+}
+
+static cairo_test_status_t
+_cairo_test_runner_draw (cairo_test_runner_t *runner,
+                        cairo_test_context_t *ctx,
+                        const cairo_boilerplate_target_t *target,
+                        cairo_bool_t similar,
+                        int device_offset)
+{
+#if SHOULD_FORK
+    if (! runner->foreground) {
+       pid_t pid;
+
+       switch ((pid = fork ())) {
+       case -1: /* error */
+           return CAIRO_TEST_UNTESTED;
+
+       case 0: /* child */
+           exit (_cairo_test_context_run_for_target (ctx, target,
+                                                     similar, device_offset));
+
+       default:
+           return _cairo_test_wait (pid);
+       }
+    }
+#endif
+    return _cairo_test_context_run_for_target (ctx, target,
+                                              similar, device_offset);
+}
+
+static void
+append_argv (int *argc, char ***argv, const char *str)
+{
+    int old_argc;
+    char **old_argv;
+    cairo_bool_t doit;
+    const char *s, *t;
+    int olen;
+    int len;
+    int i;
+    int args_to_add = 0;
+
+    if (str == NULL)
+       return;
+
+    old_argc = *argc;
+    old_argv = *argv;
+
+    doit = FALSE;
+    do {
+       if (doit)
+           *argv = xmalloc (olen);
+
+       olen = sizeof (char *) * (args_to_add + *argc);
+       for (i = 0; i < old_argc; i++) {
+           len = strlen (old_argv[i]) + 1;
+           if (doit) {
+               (*argv)[i] = (char *) *argv + olen;
+               memcpy ((*argv)[i], old_argv[i], len);
+           }
+           olen += len;
+       }
+
+       s = str;
+       while ((t = strpbrk (s, " \t,:;")) != NULL) {
+           if (t - s) {
+               len = t - s;
+               if (doit) {
+                   (*argv)[i] = (char *) *argv + olen;
+                   memcpy ((*argv)[i], s, len);
+                   (*argv)[i][len] = '\0';
+               } else {
+                   olen += sizeof (char *);
+               }
+               args_to_add++;
+               olen += len + 1;
+               i++;
+           }
+           s = t + 1;
+       }
+       if (*s != '\0') {
+           len = strlen (s) + 1;
+           if (doit) {
+               (*argv)[i] = (char *) *argv + olen;
+               memcpy ((*argv)[i], s, len);
+           } else {
+               olen += sizeof (char *);
+           }
+           args_to_add++;
+           olen += len;
+           i++;
+       }
+    } while (doit++ == FALSE);
+    *argc = i;
+}
+
+static void
+usage (const char *argv0)
+{
+    fprintf (stderr,
+            "Usage: %s [-afkxsl] [test-names|keywords ...]\n"
+            "       %s -l\n"
+            "\n"
+            "Run the cairo conformance test suite over the given tests (all by default)\n"
+            "The command-line arguments are interpreted as follows:\n"
+            "\n"
+            "  -a      all; run the full set of tests. By default the test suite\n"
+            "          skips similar surface and device offset testing.\n"
+            "  -f      foreground; do not fork\n"
+            "  -k      match tests by keyword\n"
+            "  -s      include slow, long running tests\n"
+            "  -x      exit on first failure\n"
+            "  -l      list only; just list selected test case names without executing\n"
+            "\n"
+            "If test names are given they are used as matches either to a specific\n"
+            "test case or to a keyword, so a command such as\n"
+            "\"cairo-test-suite -k text\" can be used to run all text test cases, and\n"
+            "\"cairo-test-suite text-transform\" to run the individual case.\n",
+            argv0, argv0);
+}
+
+static void
+_parse_cmdline (cairo_test_runner_t *runner, int *argc, char **argv[])
+{
+    int c;
+
+    while (1) {
+       c = _cairo_getopt (*argc, *argv, ":afkxsl");
+       if (c == -1)
+           break;
+
+       switch (c) {
+       case 'a':
+           runner->full_test = TRUE;
+           break;
+       case 's':
+           runner->slow = TRUE;
+           break;
+       case 'l':
+           runner->list_only = TRUE;
+           break;
+       case 'f':
+           runner->foreground = TRUE;
+           break;
+       case 'x':
+           runner->exit_on_failure = TRUE;
+           break;
+       case 'k':
+           runner->keyword_match = TRUE;
+           break;
+       default:
+           fprintf (stderr, "Internal error: unhandled option: %c\n", c);
+           /* fall-through */
+       case '?':
+           usage ((*argv)[0]);
+           exit (1);
+       }
+    }
+
+    *argc -= optind;
+    *argv += optind;
+}
+
+static void
+_runner_init (cairo_test_runner_t *runner)
+{
+    cairo_test_init (&runner->base, "cairo-test-suite", ".");
+
+    runner->passed = TRUE;
+
+    runner->fails_preamble = NULL;
+    runner->crashes_preamble = NULL;
+    runner->errors_preamble = NULL;
+
+    runner->fails_per_target = xcalloc (sizeof (cairo_test_list_t *),
+                                       runner->base.num_targets);
+    runner->crashes_per_target = xcalloc (sizeof (cairo_test_list_t *),
+                                         runner->base.num_targets);
+    runner->errors_per_target = xcalloc (sizeof (cairo_test_list_t *),
+                                         runner->base.num_targets);
+    runner->num_failed_per_target = xcalloc (sizeof (int),
+                                            runner->base.num_targets);
+    runner->num_error_per_target = xcalloc (sizeof (int),
+                                            runner->base.num_targets);
+    runner->num_crashed_per_target = xcalloc (sizeof (int),
+                                             runner->base.num_targets);
+}
+
+static void
+_runner_print_versions (cairo_test_runner_t *runner)
+{
+    _log (&runner->base,
+        "Compiled against cairo %s, running on %s.\n",
+        CAIRO_VERSION_STRING, cairo_version_string ());
+    _log (&runner->base,
+        "Compiled against pixman %s, running on %s.\n",
+        PIXMAN_VERSION_STRING, pixman_version_string ());
+
+    fflush (runner->base.log_file);
+}
+
+static void
+_runner_print_summary (cairo_test_runner_t *runner)
+{
+    _log (&runner->base,
+         "%d Passed, %d Failed [%d crashed, %d expected], %d Skipped\n",
+         runner->num_passed,
+
+         runner->num_failed + runner->num_crashed + runner->num_xfailed,
+         runner->num_crashed,
+         runner->num_xfailed,
+
+         runner->num_skipped);
+}
+
+static void
+_runner_print_details (cairo_test_runner_t *runner)
+{
+    cairo_test_list_t *list;
+    unsigned int n;
+
+    if (runner->crashes_preamble) {
+       int count = 0;
+
+       for (list = runner->crashes_preamble; list != NULL; list = list->next)
+           count++;
+
+       _log (&runner->base, "Preamble: %d crashed! -", count);
+
+       for (list = runner->crashes_preamble; list != NULL; list = list->next) {
+           char *name = cairo_test_get_name (list->test);
+           _log (&runner->base, " %s", name);
+           free (name);
+       }
+       _log (&runner->base, "\n");
+    }
+    if (runner->errors_preamble) {
+       int count = 0;
+
+       for (list = runner->errors_preamble; list != NULL; list = list->next)
+           count++;
+
+       _log (&runner->base, "Preamble: %d error -", count);
+
+       for (list = runner->errors_preamble; list != NULL; list = list->next) {
+           char *name = cairo_test_get_name (list->test);
+           _log (&runner->base, " %s", name);
+           free (name);
+       }
+       _log (&runner->base, "\n");
+    }
+    if (runner->fails_preamble) {
+       int count = 0;
+
+       for (list = runner->fails_preamble; list != NULL; list = list->next)
+           count++;
+
+       _log (&runner->base, "Preamble: %d failed -", count);
+
+       for (list = runner->fails_preamble; list != NULL; list = list->next) {
+           char *name = cairo_test_get_name (list->test);
+           _log (&runner->base, " %s", name);
+           free (name);
+       }
+       _log (&runner->base, "\n");
+    }
+
+    for (n = 0; n < runner->base.num_targets; n++) {
+       const cairo_boilerplate_target_t *target;
+
+       target = runner->base.targets_to_test[n];
+       if (runner->num_crashed_per_target[n]) {
+           _log (&runner->base, "%s (%s): %d crashed! -",
+                 target->name,
+                 cairo_boilerplate_content_name (target->content),
+                 runner->num_crashed_per_target[n]);
+
+           for (list = runner->crashes_per_target[n];
+                list != NULL;
+                list = list->next)
+           {
+               char *name = cairo_test_get_name (list->test);
+               _log (&runner->base, " %s", name);
+               free (name);
+           }
+           _log (&runner->base, "\n");
+       }
+       if (runner->num_error_per_target[n]) {
+           _log (&runner->base, "%s (%s): %d error -",
+                 target->name,
+                 cairo_boilerplate_content_name (target->content),
+                 runner->num_error_per_target[n]);
+
+           for (list = runner->errors_per_target[n];
+                list != NULL;
+                list = list->next)
+           {
+               char *name = cairo_test_get_name (list->test);
+               _log (&runner->base, " %s", name);
+               free (name);
+           }
+           _log (&runner->base, "\n");
+       }
+
+       if (runner->num_failed_per_target[n]) {
+           _log (&runner->base, "%s (%s): %d failed -",
+                 target->name,
+                 cairo_boilerplate_content_name (target->content),
+                 runner->num_failed_per_target[n]);
+
+           for (list = runner->fails_per_target[n];
+                list != NULL;
+                list = list->next)
+           {
+               char *name = cairo_test_get_name (list->test);
+               _log (&runner->base, " %s", name);
+               free (name);
+           }
+           _log (&runner->base, "\n");
+       }
+    }
+}
+
+static void
+_runner_print_results (cairo_test_runner_t *runner)
+{
+    _runner_print_summary (runner);
+    _runner_print_details (runner);
+
+    if (! runner->passed && ! runner->num_crashed) {
+       _log (&runner->base,
+"\n"
+"Note: These failures may be due to external factors.\n"
+"Please read test/README -- \"Getting the elusive zero failures\".\n");
+    }
+}
+
+static cairo_test_status_t
+_runner_fini (cairo_test_runner_t *runner)
+{
+    unsigned int n;
+
+    _list_free (runner->crashes_preamble);
+    _list_free (runner->errors_preamble);
+    _list_free (runner->fails_preamble);
+
+    for (n = 0; n < runner->base.num_targets; n++) {
+       _list_free (runner->crashes_per_target[n]);
+       _list_free (runner->errors_per_target[n]);
+       _list_free (runner->fails_per_target[n]);
+    }
+    free (runner->crashes_per_target);
+    free (runner->errors_per_target);
+    free (runner->fails_per_target);
+
+    free (runner->num_crashed_per_target);
+    free (runner->num_error_per_target);
+    free (runner->num_failed_per_target);
+
+    cairo_test_fini (&runner->base);
+
+    if (runner->force_pass)
+       return CAIRO_TEST_SUCCESS;
+
+    return runner->num_failed + runner->num_crashed ?
+       CAIRO_TEST_FAILURE :
+       runner->num_passed + runner->num_xfailed ?
+       CAIRO_TEST_SUCCESS : CAIRO_TEST_UNTESTED;
+}
+
+static cairo_bool_t
+_version_compare (int a, cairo_test_compare_op_t op, int b)
+{
+    switch (op) {
+    case GT: return a > b;
+    case GE: return a >= b;
+    default: return FALSE;
+    }
+}
+
+
+static cairo_bool_t
+_get_required_version (const char *str,
+                      cairo_test_compare_op_t *op,
+                      int *major,
+                      int *minor,
+                      int *micro)
+{
+    while (*str == ' ')
+       str++;
+
+    if (strncmp (str, ">=", 2) == 0) {
+       *op = GE;
+       str += 2;
+    } else if (strncmp (str, ">", 1) == 0) {
+       *op = GT;
+       str += 1;
+    } else
+       return FALSE;
+
+    while (*str == ' ')
+       str++;
+
+    if (sscanf (str, "%d.%d.%d", major, minor, micro) != 3) {
+       *micro = 0;
+       if (sscanf (str, "%d.%d", major, minor) != 2)
+           return FALSE;
+    }
+
+    return TRUE;
+}
+
+static cairo_bool_t
+_has_required_cairo_version (const char *str)
+{
+    cairo_test_compare_op_t op;
+    int major, minor, micro;
+
+    if (! _get_required_version (str + 5 /* advance over "cairo" */,
+                                &op, &major, &minor, &micro))
+    {
+       fprintf (stderr, "unrecognised cairo version requirement '%s'\n", str);
+       return FALSE;
+    }
+
+    return _version_compare (cairo_version (),
+                            op,
+                            CAIRO_VERSION_ENCODE (major, minor, micro));
+}
+
+static cairo_bool_t
+_has_required_ghostscript_version (const char *str)
+{
+#if ! CAIRO_CAN_TEST_PS_SURFACE
+    return TRUE;
+#endif
+
+    str += 2; /* advance over "gs" */
+
+    return TRUE;
+}
+
+static cairo_bool_t
+_has_required_poppler_version (const char *str)
+{
+#if ! CAIRO_CAN_TEST_PDF_SURFACE
+    return TRUE;
+#endif
+
+    str += 7; /* advance over "poppler" */
+
+    return TRUE;
+}
+
+static cairo_bool_t
+_has_required_rsvg_version (const char *str)
+{
+#if ! CAIRO_CAN_TEST_SVG_SURFACE
+    return TRUE;
+#endif
+
+    str += 4; /* advance over "rsvg" */
+
+    return TRUE;
+}
+
+int
+main (int argc, char **argv)
+{
+    cairo_test_runner_t runner;
+    cairo_test_t *test;
+    cairo_test_status_t *target_status;
+    unsigned int n, m;
+    char targets[4096];
+    int len;
+    char *cairo_tests_env;
+
+#ifdef _MSC_VER
+    /* We don't want an assert dialog, we want stderr */
+    _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE);
+    _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR);
+#endif
+
+    _cairo_test_runner_register_tests ();
+    _tests_reverse ();
+
+    memset (&runner, 0, sizeof (runner));
+    runner.num_device_offsets = 1;
+
+    if (is_running_under_debugger ())
+       runner.foreground = TRUE;
+
+    if (getenv ("CAIRO_TEST_MODE")) {
+       const char *env = getenv ("CAIRO_TEST_MODE");
+
+       if (strstr (env, "full")) {
+           runner.full_test = TRUE;
+       }
+       if (strstr (env, "foreground")) {
+           runner.foreground = TRUE;
+       }
+       if (strstr (env, "exit-on-failure")) {
+           runner.exit_on_failure = TRUE;
+       }
+    }
+
+    if (getenv ("CAIRO_TEST_FORCE_PASS")) {
+       const char *env = getenv ("CAIRO_TEST_FORCE_PASS");
+
+       runner.force_pass = atoi (env);
+    }
+
+    _parse_cmdline (&runner, &argc, &argv);
+
+    cairo_tests_env = getenv("CAIRO_TESTS");
+    append_argv (&argc, &argv, cairo_tests_env);
+
+    if (runner.full_test) {
+       runner.num_device_offsets = 2;
+    }
+
+    target_status = NULL; /* silence the compiler */
+    if (! runner.list_only) {
+       _runner_init (&runner);
+       _runner_print_versions (&runner);
+       target_status = xmalloc (sizeof (cairo_test_status_t) *
+                                runner.base.num_targets);
+    }
+
+    for (test = tests; test != NULL; test = test->next) {
+       cairo_test_context_t ctx;
+       cairo_test_status_t status;
+       cairo_bool_t failed = FALSE, xfailed = FALSE, error = FALSE, crashed = FALSE, skipped = TRUE;
+       cairo_bool_t in_preamble = FALSE;
+       char *name = cairo_test_get_name (test);
+       int i;
+
+       /* check for restricted runs */
+       if (argc) {
+           cairo_bool_t found = FALSE;
+           const char *keywords = test->keywords;
+
+           for (i = 0; i < argc; i++) {
+               const char *match = argv[i];
+               cairo_bool_t invert = match[0] == '!';
+               if (invert)
+                   match++;
+
+               if (runner.keyword_match) {
+                   if (keywords != NULL && strstr (keywords, match) != NULL) {
+                       found = ! invert;
+                       break;
+                   } else if (invert) {
+                       found = TRUE;
+                   }
+               } else {
+                   /* exact match on test name */
+                   if (strcmp (name, match) == 0) {
+                       found = ! invert;
+                       break;
+                   } else if (invert) {
+                       found = TRUE;
+                   }
+               }
+           }
+
+           if (! found) {
+               free (name);
+               continue;
+           }
+       }
+
+       /* check to see if external requirements match */
+       if (test->requirements != NULL) {
+           const char *requirements = test->requirements;
+           const char *str;
+
+           str = strstr (requirements, "slow");
+           if (str != NULL && ! runner.slow) {
+               if (runner.list_only)
+                   goto TEST_NEXT;
+               else
+                   goto TEST_SKIPPED;
+           }
+
+           str = strstr (requirements, "cairo");
+           if (str != NULL && ! _has_required_cairo_version (str)) {
+               if (runner.list_only)
+                   goto TEST_NEXT;
+               else
+                   goto TEST_SKIPPED;
+           }
+
+           str = strstr (requirements, "gs");
+           if (str != NULL && ! _has_required_ghostscript_version (str)) {
+               if (runner.list_only)
+                   goto TEST_NEXT;
+               else
+                   goto TEST_SKIPPED;
+           }
+
+           str = strstr (requirements, "poppler");
+           if (str != NULL && ! _has_required_poppler_version (str)) {
+               if (runner.list_only)
+                   goto TEST_NEXT;
+               else
+                   goto TEST_SKIPPED;
+           }
+
+           str = strstr (requirements, "rsvg");
+           if (str != NULL && ! _has_required_rsvg_version (str)) {
+               if (runner.list_only)
+                   goto TEST_NEXT;
+               else
+                   goto TEST_SKIPPED;
+           }
+       }
+
+       if (runner.list_only) {
+           printf ("%s ", name);
+           goto TEST_NEXT;
+       }
+
+       _cairo_test_context_init_for_test (&ctx, &runner.base, test);
+       memset (target_status, 0,
+               sizeof (cairo_test_status_t) * ctx.num_targets);
+
+       if (ctx.test->preamble != NULL) {
+           status = _cairo_test_runner_preamble (&runner, &ctx);
+           switch (status) {
+           case CAIRO_TEST_SUCCESS:
+               in_preamble = TRUE;
+               skipped = FALSE;
+               break;
+
+           case CAIRO_TEST_XFAILURE:
+               in_preamble = TRUE;
+               xfailed = TRUE;
+               goto TEST_DONE;
+
+           case CAIRO_TEST_NEW:
+           case CAIRO_TEST_FAILURE:
+               runner.fails_preamble = _list_prepend (runner.fails_preamble,
+                                                      test);
+               in_preamble = TRUE;
+               failed = TRUE;
+               goto TEST_DONE;
+
+           case CAIRO_TEST_ERROR:
+               runner.errors_preamble = _list_prepend (runner.errors_preamble,
+                                                        test);
+               in_preamble = TRUE;
+               failed = TRUE;
+               goto TEST_DONE;
+
+           case CAIRO_TEST_NO_MEMORY:
+           case CAIRO_TEST_CRASHED:
+               runner.crashes_preamble = _list_prepend (runner.crashes_preamble,
+                                                        test);
+               in_preamble = TRUE;
+               failed = TRUE;
+               goto TEST_DONE;
+
+           case CAIRO_TEST_UNTESTED:
+               goto TEST_DONE;
+           }
+       }
+
+       if (ctx.test->draw == NULL)
+           goto TEST_DONE;
+
+       for (n = 0; n < ctx.num_targets; n++) {
+           const cairo_boilerplate_target_t *target;
+           cairo_bool_t target_failed = FALSE,
+                        target_xfailed = FALSE,
+                        target_error = FALSE,
+                        target_crashed = FALSE,
+                        target_skipped = TRUE;
+           cairo_test_similar_t has_similar;
+
+           target = ctx.targets_to_test[n];
+
+           has_similar = runner.full_test ?
+                         cairo_test_target_has_similar (&ctx, target) :
+                         DIRECT;
+           for (m = 0; m < runner.num_device_offsets; m++) {
+               int dev_offset = m * 25;
+               cairo_test_similar_t similar;
+
+               for (similar = DIRECT; similar <= has_similar; similar++) {
+                   status = _cairo_test_runner_draw (&runner, &ctx, target,
+                                                     similar, dev_offset);
+                   switch (status) {
+                   case CAIRO_TEST_SUCCESS:
+                       target_skipped = FALSE;
+                       break;
+                   case CAIRO_TEST_XFAILURE:
+                       target_xfailed = TRUE;
+                       break;
+                   case CAIRO_TEST_NEW:
+                   case CAIRO_TEST_FAILURE:
+                       target_failed = TRUE;
+                       break;
+                   case CAIRO_TEST_ERROR:
+                       target_error = TRUE;
+                       break;
+                   case CAIRO_TEST_NO_MEMORY:
+                   case CAIRO_TEST_CRASHED:
+                       target_crashed = TRUE;
+                       break;
+                   case CAIRO_TEST_UNTESTED:
+                       break;
+                   }
+               }
+           }
+
+           if (target_crashed) {
+               target_status[n] = CAIRO_TEST_CRASHED;
+               runner.num_crashed_per_target[n]++;
+               runner.crashes_per_target[n] = _list_prepend (runner.crashes_per_target[n],
+                                                             test);
+               crashed = TRUE;
+           } else if (target_error) {
+               target_status[n] = CAIRO_TEST_ERROR;
+               runner.num_error_per_target[n]++;
+               runner.errors_per_target[n] = _list_prepend (runner.errors_per_target[n],
+                                                            test);
+
+               error = TRUE;
+           } else if (target_failed) {
+               target_status[n] = CAIRO_TEST_FAILURE;
+               runner.num_failed_per_target[n]++;
+               runner.fails_per_target[n] = _list_prepend (runner.fails_per_target[n],
+                                                           test);
+
+               failed = TRUE;
+           } else if (target_xfailed) {
+               target_status[n] = CAIRO_TEST_XFAILURE;
+               xfailed = TRUE;
+           } else if (target_skipped) {
+               target_status[n] = CAIRO_TEST_UNTESTED;
+           } else {
+               target_status[n] = CAIRO_TEST_SUCCESS;
+               skipped = FALSE;
+           }
+       }
+
+  TEST_DONE:
+       cairo_test_fini (&ctx);
+  TEST_SKIPPED:
+       targets[0] = '\0';
+       if (crashed) {
+           if (! in_preamble) {
+               len = 0;
+               for (n = 0 ; n < runner.base.num_targets; n++) {
+                   if (target_status[n] == CAIRO_TEST_CRASHED) {
+                       if (strstr (targets,
+                                   runner.base.targets_to_test[n]->name) == NULL)
+                       {
+                           len += snprintf (targets + len, sizeof (targets) - len,
+                                            "%s, ",
+                                            runner.base.targets_to_test[n]->name);
+                       }
+                   }
+               }
+               targets[len-2] = '\0';
+               _log (&runner.base, "\n%s: CRASH! (%s)\n", name, targets);
+           } else {
+               _log (&runner.base, "\n%s: CRASH!\n", name);
+           }
+           runner.num_crashed++;
+           runner.passed = FALSE;
+       } else if (error) {
+           if (! in_preamble) {
+               len = 0;
+               for (n = 0 ; n < runner.base.num_targets; n++) {
+                   if (target_status[n] == CAIRO_TEST_ERROR) {
+                       if (strstr (targets,
+                                   runner.base.targets_to_test[n]->name) == NULL)
+                       {
+                           len += snprintf (targets + len,
+                                            sizeof (targets) - len,
+                                            "%s, ",
+                                            runner.base.targets_to_test[n]->name);
+                       }
+                   }
+               }
+               targets[len-2] = '\0';
+               _log (&runner.base, "%s: ERROR (%s)\n", name, targets);
+           } else {
+               _log (&runner.base, "%s: ERROR\n", name);
+           }
+           runner.num_error++;
+           runner.passed = FALSE;
+       } else if (failed) {
+           if (! in_preamble) {
+               len = 0;
+               for (n = 0 ; n < runner.base.num_targets; n++) {
+                   if (target_status[n] == CAIRO_TEST_FAILURE) {
+                       if (strstr (targets,
+                                   runner.base.targets_to_test[n]->name) == NULL)
+                       {
+                           len += snprintf (targets + len,
+                                            sizeof (targets) - len,
+                                            "%s, ",
+                                            runner.base.targets_to_test[n]->name);
+                       }
+                   }
+               }
+               targets[len-2] = '\0';
+               _log (&runner.base, "%s: FAIL (%s)\n", name, targets);
+           } else {
+               _log (&runner.base, "%s: FAIL\n", name);
+           }
+           runner.num_failed++;
+           runner.passed = FALSE;
+       } else if (xfailed) {
+           _log (&runner.base, "%s: XFAIL\n", name);
+           runner.num_xfailed++;
+       } else if (skipped) {
+           _log (&runner.base, "%s: UNTESTED\n", name);
+           runner.num_skipped++;
+       } else {
+           _log (&runner.base, "%s: PASS\n", name);
+           runner.num_passed++;
+       }
+       fflush (runner.base.log_file);
+
+  TEST_NEXT:
+       free (name);
+       if (runner.exit_on_failure && ! runner.passed)
+           break;
+
+    }
+
+    if (cairo_tests_env)
+       free(argv);
+
+    if (runner.list_only) {
+       printf ("\n");
+       return CAIRO_TEST_SUCCESS;
+    }
+
+    for (n = 0 ; n < runner.base.num_targets; n++) {
+       runner.crashes_per_target[n] = _list_reverse (runner.crashes_per_target[n]);
+       runner.errors_per_target[n] = _list_reverse (runner.errors_per_target[n]);
+       runner.fails_per_target[n] = _list_reverse (runner.fails_per_target[n]);
+    }
+
+    _runner_print_results (&runner);
+
+    free (target_status);
+    return _runner_fini (&runner);
+}
+
+void
+cairo_test_register (cairo_test_t *test)
+{
+    test->next = tests;
+    tests = test;
+}
diff --git a/test/cairo-test-trace.c b/test/cairo-test-trace.c
new file mode 100644 (file)
index 0000000..fb1bd42
--- /dev/null
@@ -0,0 +1,1778 @@
+/*
+ * Copyright © 2009 Chris Wilson
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * the authors not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. The authors make no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE AUTHORS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Authors: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+/*
+ * The basic idea is that we feed the trace to multiple backends in parallel
+ * and compare the output at the end of each context (based on the premise
+ * that contexts demarcate expose events, or their logical equivalents) with
+ * that of the image[1] backend. Each backend is executed in a separate
+ * process, for robustness and to isolate the global cairo state, with the
+ * image data residing in shared memory and synchronising over a socket.
+ *
+ * [1] Should be reference implementation, currently the image backend is
+ *     considered to be the reference for all other backends.
+ */
+
+/* XXX Can't directly compare fills using spans versus trapezoidation,
+ *     i.e. xlib vs image. Gah, kinda renders this whole scheme moot.
+ *     How about reference platforms?
+ *     E.g. accelerated xlib driver vs Xvfb?
+ *
+ *     boilerplate->create_reference_surface()?
+ *     boilerplate->reference->create_surface()?
+ *     So for each backend spawn two processes, a reference and xlib
+ *     (obviously minimising the number of reference processes when possible)
+ */
+
+/*
+ * XXX Handle show-page as well as cairo_destroy()? Though arguably that is
+ *     only relevant for paginated backends which is currently outside the
+ *     scope of this test.
+ */
+
+#define _GNU_SOURCE 1  /* getline() */
+
+#include "cairo-test.h"
+#include "buffer-diff.h"
+
+#include "cairo-boilerplate-getopt.h"
+#include <cairo-script-interpreter.h>
+#include "cairo-missing.h"
+
+#if CAIRO_HAS_SCRIPT_SURFACE
+#include <cairo-script.h>
+#endif
+
+/* For basename */
+#ifdef HAVE_LIBGEN_H
+#include <libgen.h>
+#endif
+#include <ctype.h> /* isspace() */
+
+#include <sys/types.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/mman.h>
+#include <sys/poll.h>
+#include <sys/un.h>
+#include <errno.h>
+#include <assert.h>
+#if CAIRO_HAS_REAL_PTHREAD
+#include <pthread.h>
+#endif
+
+#if HAVE_FCFINI
+#include <fontconfig/fontconfig.h>
+#endif
+
+#define DEBUG 0
+
+#define ignore_image_differences 0 /* XXX make me a cmdline option! */
+#define write_results 1
+#define write_traces 1
+
+#define DATA_SIZE (256 << 20)
+#define SHM_PATH_XXX "/.shmem-cairo-trace"
+
+typedef struct _test_trace {
+    /* Options from command-line */
+    cairo_bool_t list_only;
+    char **names;
+    unsigned int num_names;
+    char **exclude_names;
+    unsigned int num_exclude_names;
+
+    /* Stuff used internally */
+    const cairo_boilerplate_target_t **targets;
+    int num_targets;
+} test_trace_t;
+
+typedef struct _test_runner {
+    const char *name;
+    cairo_surface_t *surface;
+    void *closure;
+    uint8_t *base;
+    const char *trace;
+    pid_t pid;
+    int sk;
+    cairo_bool_t is_recording;
+
+    cairo_script_interpreter_t *csi;
+    struct context_closure {
+       struct context_closure *next;
+       unsigned long id;
+       unsigned long start_line;
+       unsigned long end_line;
+       cairo_t *context;
+       cairo_surface_t *surface;
+    } *contexts;
+
+    unsigned long context_id;
+} test_runner_t;
+
+struct slave {
+    pid_t pid;
+    int fd;
+    unsigned long image_serial;
+    unsigned long image_ready;
+    unsigned long start_line;
+    unsigned long end_line;
+    cairo_surface_t *image;
+    long width, height;
+    cairo_surface_t *difference;
+    buffer_diff_result_t result;
+    const cairo_boilerplate_target_t *target;
+    const struct slave *reference;
+    cairo_bool_t is_recording;
+};
+
+struct request_image {
+    unsigned long id;
+    unsigned long start_line;
+    unsigned long end_line;
+    cairo_format_t format;
+    long width;
+    long height;
+    long stride;
+};
+
+struct surface_tag {
+    long width, height;
+};
+static const cairo_user_data_key_t surface_tag;
+
+#define TARGET_NAME(T)  ((T) ? (T)->name : "recording")
+
+#if CAIRO_HAS_REAL_PTHREAD
+#define tr_die(t) t->is_recording ? pthread_exit(NULL) : exit(1)
+#else
+#define tr_die(t) exit(1)
+#endif
+
+static cairo_bool_t
+writen (int fd, const void *ptr, int len)
+{
+#if 0
+    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 cairo_bool_t
+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 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
+send_recording_surface (test_runner_t *tr,
+                       int width, int height,
+                       struct context_closure *closure)
+{
+#if CAIRO_HAS_REAL_PTHREAD
+    const struct request_image rq = {
+       closure->id,
+       closure->start_line,
+       closure->end_line,
+       -1,
+       width, height,
+       (long) closure->surface,
+    };
+    unsigned long offset;
+    unsigned long serial;
+
+    if (DEBUG > 1) {
+       printf ("send-recording-surface: %lu [%lu, %lu]\n",
+               closure->id,
+               closure->start_line,
+               closure->end_line);
+    }
+    writen (tr->sk, &rq, sizeof (rq));
+    readn (tr->sk, &offset, sizeof (offset));
+
+    /* signal completion */
+    writen (tr->sk, &closure->id, sizeof (closure->id));
+
+    /* wait for image check */
+    serial = 0;
+    readn (tr->sk, &serial, sizeof (serial));
+    if (DEBUG > 1) {
+       printf ("send-recording-surface: serial: %lu\n", serial);
+    }
+    if (serial != closure->id)
+       pthread_exit (NULL);
+#else
+    exit (1);
+#endif
+}
+
+static void *
+request_image (test_runner_t *tr,
+              struct context_closure *closure,
+              cairo_format_t format,
+              int width, int height, int stride)
+{
+    const struct request_image rq = {
+       closure->id,
+       closure->start_line,
+       closure->end_line,
+       format, width, height, stride
+    };
+    unsigned long offset = -1;
+
+    assert (format != (cairo_format_t) -1);
+
+    writen (tr->sk, &rq, sizeof (rq));
+    readn (tr->sk, &offset, sizeof (offset));
+    if (offset == (unsigned long) -1)
+       return NULL;
+
+    return tr->base + offset;
+}
+
+static void
+send_surface (test_runner_t *tr,
+             struct context_closure *closure)
+{
+    cairo_surface_t *source = closure->surface;
+    cairo_surface_t *image;
+    cairo_format_t format = (cairo_format_t) -1;
+    cairo_t *cr;
+    int width, height, stride;
+    void *data;
+    unsigned long serial;
+
+    if (DEBUG > 1) {
+       printf ("send-surface: '%s', is-recording? %d\n",
+               tr->name, tr->is_recording);
+    }
+
+    if (cairo_surface_get_type (source) == CAIRO_SURFACE_TYPE_IMAGE) {
+       width = cairo_image_surface_get_width (source);
+       height = cairo_image_surface_get_height (source);
+       format = cairo_image_surface_get_format (source);
+    } else {
+       struct surface_tag *tag;
+
+       tag = cairo_surface_get_user_data (source, &surface_tag);
+       if (tag != NULL) {
+           width = tag->width;
+           height = tag->height;
+       } else {
+           double x0, x1, y0, y1;
+
+           /* presumably created using cairo_surface_create_similar() */
+           cr = cairo_create (source);
+           cairo_clip_extents (cr, &x0, &y0, &x1, &y1);
+           cairo_destroy (cr);
+
+           tag = xmalloc (sizeof (*tag));
+           width = tag->width = x1 - x0;
+           height = tag->height = y1 - y0;
+
+           if (cairo_surface_set_user_data (source, &surface_tag, tag, free))
+               tr_die (tr);
+       }
+    }
+
+    if (tr->is_recording) {
+       send_recording_surface (tr, width, height, closure);
+       return;
+    }
+
+    if (format == (cairo_format_t) -1)
+       format = format_for_content (cairo_surface_get_content (source));
+
+    stride = cairo_format_stride_for_width (format, width);
+
+    data = request_image (tr, closure, format, width, height, stride);
+    if (data == NULL)
+       tr_die (tr);
+
+    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 (tr->sk, &closure->id, sizeof (closure->id));
+
+    /* wait for image check */
+    serial = 0;
+    readn (tr->sk, &serial, sizeof (serial));
+    if (serial != closure->id)
+       tr_die (tr);
+}
+
+static cairo_surface_t *
+_surface_create (void *closure,
+                cairo_content_t content,
+                double width, double height,
+                long uid)
+{
+    test_runner_t *tr = closure;
+    cairo_surface_t *surface;
+
+    surface = cairo_surface_create_similar (tr->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))
+           tr_die (tr);
+    }
+
+    return surface;
+}
+
+static cairo_t *
+_context_create (void *closure, cairo_surface_t *surface)
+{
+    test_runner_t *tr = closure;
+    struct context_closure *l;
+
+    if (DEBUG) {
+       fprintf (stderr, "%s: starting context %lu on line %d\n",
+                tr->name ? tr->name : "recording" ,
+                tr->context_id + 1,
+                cairo_script_interpreter_get_line_number (tr->csi));
+    }
+
+    l = xmalloc (sizeof (*l));
+    l->next = tr->contexts;
+    l->start_line = cairo_script_interpreter_get_line_number (tr->csi);
+    l->end_line = l->start_line;
+    l->context = cairo_create (surface);
+    l->surface = cairo_surface_reference (surface);
+    l->id = ++tr->context_id;
+    if (l->id == 0)
+       l->id = ++tr->context_id;
+    tr->contexts = l;
+
+    return l->context;
+}
+
+static void
+_context_destroy (void *closure, void *ptr)
+{
+    test_runner_t *tr = closure;
+    struct context_closure *l, **prev = &tr->contexts;
+
+    while ((l = *prev) != NULL) {
+       if (l->context == ptr) {
+           if (DEBUG) {
+               fprintf (stderr, "%s: context %lu complete on line %d\n",
+                        tr->name ? tr->name : "recording" ,
+                        tr->context_id,
+                        cairo_script_interpreter_get_line_number (tr->csi));
+           }
+           l->end_line =
+               cairo_script_interpreter_get_line_number (tr->csi);
+           if (cairo_surface_status (l->surface) == CAIRO_STATUS_SUCCESS) {
+               send_surface (tr, l);
+            } else {
+               fprintf (stderr, "%s: error during replay, line %lu: %s!\n",
+                        tr->name,
+                        l->end_line,
+                        cairo_status_to_string (cairo_surface_status (l->surface)));
+               tr_die (tr);
+           }
+
+            cairo_surface_destroy (l->surface);
+            *prev = l->next;
+            free (l);
+            return;
+        }
+        prev = &l->next;
+    }
+}
+
+static void
+execute (test_runner_t *tr)
+{
+    const cairo_script_interpreter_hooks_t hooks = {
+       .closure = tr,
+       .surface_create = _surface_create,
+       .context_create = _context_create,
+       .context_destroy = _context_destroy,
+    };
+    pid_t ack;
+
+    tr->csi = cairo_script_interpreter_create ();
+    cairo_script_interpreter_install_hooks (tr->csi, &hooks);
+
+    ack = -1;
+    readn (tr->sk, &ack, sizeof (ack));
+    if (ack != tr->pid)
+       tr_die (tr);
+
+    cairo_script_interpreter_run (tr->csi, tr->trace);
+
+    cairo_script_interpreter_finish (tr->csi);
+    if (cairo_script_interpreter_destroy (tr->csi))
+       tr_die (tr);
+}
+
+static int
+spawn_socket (const char *socket_path, pid_t pid)
+{
+    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;
+
+    if (! writen (sk, &pid, sizeof (pid)))
+       return -1;
+
+    return sk;
+}
+
+static void *
+spawn_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
+spawn_target (const char *socket_path,
+             const char *shm_path,
+             const cairo_boilerplate_target_t *target,
+             const char *trace)
+{
+    test_runner_t tr;
+    pid_t pid;
+
+    if (DEBUG)
+       printf ("Spawning slave '%s' for %s\n", target->name, trace);
+
+    pid = fork ();
+    if (pid != 0)
+       return pid;
+
+    tr.is_recording = FALSE;
+    tr.pid = getpid ();
+
+    tr.sk = spawn_socket (socket_path, tr.pid);
+    if (tr.sk == -1) {
+       fprintf (stderr, "%s: Failed to open socket.\n",
+                target->name);
+       exit (-1);
+    }
+
+    tr.base = spawn_shm (shm_path);
+    if (tr.base == MAP_FAILED) {
+       fprintf (stderr, "%s: Failed to map shared memory segment.\n",
+                target->name);
+       exit (-1);
+    }
+
+    tr.name = target->name;
+    tr.contexts = NULL;
+    tr.context_id = 0;
+    tr.trace = trace;
+
+    tr.surface = target->create_surface (NULL,
+                                        target->content,
+                                        1, 1,
+                                        1, 1,
+                                        CAIRO_BOILERPLATE_MODE_TEST,
+                                        0,
+                                        &tr.closure);
+    if (tr.surface == NULL) {
+       fprintf (stderr,
+                "%s:  Failed to create target surface.\n",
+                target->name);
+       exit (-1);
+    }
+
+    execute (&tr);
+
+    cairo_surface_destroy (tr.surface);
+
+    if (target->cleanup)
+       target->cleanup (tr.closure);
+
+    close (tr.sk);
+    munmap (tr.base, DATA_SIZE);
+
+    exit (0);
+}
+
+#if CAIRO_HAS_REAL_PTHREAD
+static void
+cleanup_recorder (void *arg)
+{
+    test_runner_t *tr = arg;
+
+    cairo_surface_finish (tr->surface);
+    cairo_surface_destroy (tr->surface);
+
+    close (tr->sk);
+    free (tr);
+}
+
+static void *
+record (void *arg)
+{
+    test_runner_t *tr = arg;
+
+    pthread_cleanup_push (cleanup_recorder, tr);
+    execute (tr);
+    pthread_cleanup_pop (TRUE);
+
+    return NULL;
+}
+
+/* The recorder is special:
+ * 1. It doesn't generate an image, but keeps an in-memory trace to
+ *    reconstruct any surface.
+ * 2. Runs in the same process, but separate thread.
+ */
+static pid_t
+spawn_recorder (const char *socket_path, const char *trace, test_runner_t **out)
+{
+    test_runner_t *tr;
+    pthread_t id;
+    pthread_attr_t attr;
+    pid_t pid = getpid ();
+
+    if (DEBUG)
+       printf ("Spawning recorder for %s\n", trace);
+
+    tr = malloc (sizeof (*tr));
+    if (tr == NULL)
+       return -1;
+
+    tr->is_recording = TRUE;
+    tr->pid = pid;
+    tr->sk = spawn_socket (socket_path, tr->pid);
+    if (tr->sk == -1) {
+       free (tr);
+       return -1;
+    }
+
+    tr->base = NULL;
+    tr->name = NULL;
+    tr->contexts = NULL;
+    tr->context_id = 0;
+    tr->trace = trace;
+
+    tr->surface = cairo_recording_surface_create (CAIRO_CONTENT_COLOR_ALPHA,
+                                                 NULL);
+    if (tr->surface == NULL) {
+       cleanup_recorder (tr);
+       return -1;
+    }
+
+    pthread_attr_init (&attr);
+    pthread_attr_setdetachstate (&attr, TRUE);
+    if (pthread_create (&id, &attr, record, tr) < 0) {
+       pthread_attr_destroy (&attr);
+       cleanup_recorder (tr);
+       return -1;
+    }
+    pthread_attr_destroy (&attr);
+
+
+    *out = tr;
+    return pid;
+}
+#endif
+
+/* XXX imagediff - is the extra expense worth it? */
+static cairo_bool_t
+matches_reference (struct slave *slave)
+{
+    cairo_surface_t *a, *b;
+
+    a = slave->image;
+    b = slave->reference->image;
+
+    if (a == b)
+       return TRUE;
+
+    if (a == NULL || b == NULL)
+       return FALSE;
+
+    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;
+
+    if (FALSE && cairo_surface_get_content (a) & CAIRO_CONTENT_COLOR) {
+       cairo_surface_t *diff;
+       int width, height, stride, size;
+       unsigned char *data;
+       cairo_status_t status;
+
+       width = cairo_image_surface_get_width (a);
+       height = cairo_image_surface_get_height (a);
+       stride = cairo_image_surface_get_stride (a);
+       size = height * stride * 4;
+       data = malloc (size);
+       if (data == NULL)
+           return FALSE;
+
+       diff = cairo_image_surface_create_for_data (data,
+                                                   cairo_image_surface_get_format (a),
+                                                   width, height, stride);
+       cairo_surface_set_user_data (diff, (cairo_user_data_key_t *) diff,
+                                    data, free);
+
+       status = image_diff (NULL, a, b, diff, &slave->result);
+       if (status) {
+           cairo_surface_destroy (diff);
+           return FALSE;
+       }
+
+       if (image_diff_is_failure (&slave->result, slave->target->error_tolerance)) {
+           slave->difference = diff;
+           return FALSE;
+       } else {
+           cairo_surface_destroy (diff);
+           return TRUE;
+       }
+    } else {
+       int width, height, stride;
+       const uint8_t *aa, *bb;
+       int x, y;
+
+       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 > slave->target->error_tolerance)
+                               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 > slave->target->error_tolerance)
+                               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 > slave->target->error_tolerance)
+                           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_RGB30:
+       case CAIRO_FORMAT_RGB16_565:
+       case CAIRO_FORMAT_INVALID:
+           assert (0);
+       }
+
+       return TRUE;
+    }
+}
+
+static cairo_bool_t
+check_images (struct slave *slaves, int num_slaves)
+{
+    int n;
+
+    if (ignore_image_differences)
+       return TRUE;
+
+    for (n = 0; n < num_slaves; n++) {
+       if (slaves[n].reference == NULL)
+           continue;
+
+       if (! matches_reference (&slaves[n]))
+           return FALSE;
+    }
+
+    return TRUE;
+}
+
+static void
+write_images (const char *trace, struct slave *slave, int num_slaves)
+{
+    while (num_slaves--) {
+       if (slave->image != NULL && ! slave->is_recording) {
+           char *filename;
+
+           xasprintf (&filename, "%s-%s-fail.png",
+                      trace, slave->target->name);
+           cairo_surface_write_to_png (slave->image, filename);
+           free (filename);
+
+           if (slave->difference) {
+               xasprintf (&filename, "%s-%s-diff.png",
+                          trace, slave->target->name);
+               cairo_surface_write_to_png (slave->difference, filename);
+               free (filename);
+           }
+       }
+
+       slave++;
+    }
+}
+
+static void
+write_result (const char *trace, struct slave *slave)
+{
+    static int index;
+    char *filename;
+
+    xasprintf (&filename, "%s-%s-pass-%d-%d-%d.png",
+              trace, slave->target->name, ++index,
+              slave->start_line, slave->end_line);
+    cairo_surface_write_to_png (slave->image, filename);
+    free (filename);
+}
+
+static void
+write_trace (const char *trace, const char *id, struct slave *slave)
+{
+#if CAIRO_HAS_SCRIPT_SURFACE
+    cairo_device_t *script;
+    char *filename;
+
+    assert (slave->is_recording);
+
+    xasprintf (&filename, "%s-%s.trace", trace, id);
+
+    script = cairo_script_create (filename);
+    cairo_script_from_recording_surface (script, slave->image);
+    cairo_device_destroy (script);
+
+    free (filename);
+#endif
+}
+
+static void
+dump_traces (test_runner_t *tr,
+            const char *trace,
+            const char *target,
+            const char *fail)
+{
+#if CAIRO_HAS_SCRIPT_SURFACE
+    struct context_closure *c;
+
+    for (c = tr->contexts; c; c = c->next) {
+       cairo_device_t *script;
+       char *filename;
+
+       xasprintf (&filename, "%s-%s-%s.%lu.trace",
+                  trace, target, fail, c->start_line);
+
+       script = cairo_script_create (filename);
+       cairo_script_from_recording_surface (script, c->surface);
+       cairo_device_destroy (script);
+
+       free (filename);
+    }
+#endif
+}
+
+static unsigned long
+allocate_image_for_slave (uint8_t *base,
+                         unsigned long offset,
+                         struct slave *slave)
+{
+    struct request_image rq;
+    int size;
+    uint8_t *data;
+
+    assert (slave->image == NULL);
+
+    readn (slave->fd, &rq, sizeof (rq));
+    slave->image_serial = rq.id;
+    slave->start_line = rq.start_line;
+    slave->end_line = rq.end_line;
+
+    slave->width = rq.width;
+    slave->height = rq.height;
+
+    if (DEBUG > 1) {
+       printf ("allocate-image-for-slave: %s %lu [%lu, %lu] %ldx%ld stride=%lu => %lu, is-recording? %d\n",
+               TARGET_NAME (slave->target),
+               slave->image_serial,
+               slave->start_line,
+               slave->end_line,
+               slave->width,
+               slave->height,
+               rq.stride,
+               offset,
+               slave->is_recording);
+    }
+
+    if (slave->is_recording) {
+       /* special communication with recording-surface thread */
+       slave->image = cairo_surface_reference ((cairo_surface_t *) rq.stride);
+    } else {
+       size = rq.height * rq.stride;
+       size = (size + 4095) & -4096;
+       data = base + offset;
+       offset += size;
+       assert (offset <= DATA_SIZE);
+
+       slave->image = cairo_image_surface_create_for_data (data, rq.format,
+                                                           rq.width, rq.height,
+                                                           rq.stride);
+    }
+
+    return offset;
+}
+
+struct error_info {
+    unsigned long context_id;
+    unsigned long start_line;
+    unsigned long end_line;
+};
+
+static cairo_bool_t
+test_run (void *base,
+         int sk,
+         const char *trace,
+         struct slave *slaves,
+         int num_slaves,
+         struct error_info *error)
+{
+    struct pollfd *pfd;
+    int npfd, cnt, n, i;
+    int completion, err = 0;
+    cairo_bool_t ret = FALSE;
+    unsigned long image;
+
+    if (DEBUG) {
+       printf ("Running trace '%s' over %d slaves\n",
+               trace, num_slaves);
+    }
+
+    pfd = xcalloc (num_slaves+1, sizeof (*pfd));
+
+    pfd[0].fd = sk;
+    pfd[0].events = POLLIN;
+    npfd = 1;
+
+    completion = 0;
+    image = 0;
+    while ((cnt = poll (pfd, npfd, -1)) > 0) {
+       if (pfd[0].revents) {
+           int fd;
+
+           while ((fd = accept (sk, NULL, NULL)) != -1) {
+               pid_t pid;
+
+               readn (fd, &pid, sizeof (pid));
+               for (n = 0; n < num_slaves; n++) {
+                   if (slaves[n].pid == pid) {
+                       slaves[n].fd = fd;
+                       break;
+                   }
+               }
+               if (n == num_slaves) {
+                   if (DEBUG)
+                       printf ("unknown slave pid\n");
+                   goto out;
+               }
+
+               pfd[npfd].fd = fd;
+               pfd[npfd].events = POLLIN;
+               npfd++;
+
+               if (! writen (fd, &pid, sizeof (pid)))
+                   goto out;
+           }
+           cnt--;
+       }
+
+       for (n = 1; n < npfd && cnt; n++) {
+           if (! pfd[n].revents)
+               continue;
+
+           if (pfd[n].revents & POLLHUP) {
+               pfd[n].events = pfd[n].revents = 0;
+               completion++;
+               continue;
+           }
+
+           for (i = 0; i < num_slaves; i++) {
+               if (slaves[i].fd == pfd[n].fd) {
+                   /* Communication with the slave is done in three phases,
+                    * and we do each pass synchronously.
+                    *
+                    * 1. The slave requests an image buffer, which we
+                    * allocate and then return to the slave the offset into
+                    * the shared memory segment.
+                    *
+                    * 2. The slave indicates that it has finished writing
+                    * into the shared image buffer. The slave now waits
+                    * for the server to collate all the image data - thereby
+                    * throttling the slaves.
+                    *
+                    * 3. After all slaves have finished writing their images,
+                    * we compare them all against the reference image and,
+                    * if satisfied, send an acknowledgement to all slaves.
+                    */
+                   if (slaves[i].image_serial == 0) {
+                       unsigned long offset;
+
+                       image =
+                           allocate_image_for_slave (base,
+                                                     offset = image,
+                                                     &slaves[i]);
+                       if (! writen (pfd[n].fd, &offset, sizeof (offset))) {
+                           pfd[n].events = pfd[n].revents = 0;
+                           err = 1;
+                           completion++;
+                           continue;
+                       }
+                   } else {
+                       readn (pfd[n].fd,
+                              &slaves[i].image_ready,
+                              sizeof (slaves[i].image_ready));
+                       if (DEBUG) {
+                           printf ("slave '%s' reports completion on %lu (expecting %lu)\n",
+                                   TARGET_NAME (slaves[i].target),
+                                   slaves[i].image_ready,
+                                   slaves[i].image_serial);
+                       }
+                       if (slaves[i].image_ready != slaves[i].image_serial) {
+                           pfd[n].events = pfd[n].revents = 0;
+                           err = 1;
+                           completion++;
+                           continue;
+                       }
+
+                       /* Can anyone spell 'P·E·D·A·N·T'? */
+                       if (! slaves[i].is_recording)
+                           cairo_surface_mark_dirty (slaves[i].image);
+                       completion++;
+                   }
+
+                   break;
+               }
+           }
+
+           cnt--;
+       }
+
+       if (completion >= num_slaves) {
+           if (err) {
+               if (DEBUG > 1)
+                   printf ("error detected\n");
+               goto out;
+           }
+
+           if (DEBUG > 1) {
+               printf ("all saves report completion\n");
+           }
+           if (slaves[0].end_line >= slaves[0].start_line &&
+               ! check_images (slaves, num_slaves)) {
+               error->context_id = slaves[0].image_serial;
+               error->start_line = slaves[0].start_line;
+               error->end_line = slaves[0].end_line;
+
+               if (DEBUG) {
+                   printf ("check_images failed: %lu, [%lu, %lu]\n",
+                           slaves[0].image_serial,
+                           slaves[0].start_line,
+                           slaves[0].end_line);
+               }
+
+               write_images (trace, slaves, num_slaves);
+
+               if (slaves[0].is_recording)
+                   write_trace (trace, "fail", &slaves[0]);
+
+               goto out;
+           }
+
+           if (write_results) write_result (trace, &slaves[1]);
+           if (write_traces && slaves[0].is_recording) {
+               char buf[80];
+               snprintf (buf, sizeof (buf), "%d", slaves[0].image_serial);
+               write_trace (trace, buf, &slaves[0]);
+           }
+
+           /* ack */
+           for (i = 0; i < num_slaves; i++) {
+               cairo_surface_destroy (slaves[i].image);
+               slaves[i].image = NULL;
+
+               if (DEBUG > 1) {
+                   printf ("sending continuation to '%s'\n",
+                           TARGET_NAME (slaves[i].target));
+               }
+               if (! writen (slaves[i].fd,
+                             &slaves[i].image_serial,
+                             sizeof (slaves[i].image_serial)))
+               {
+                   goto out;
+               }
+
+               slaves[i].image_serial = 0;
+               slaves[i].image_ready = 0;
+           }
+
+           completion = 0;
+           image = 0;
+       }
+    }
+done:
+    ret = TRUE;
+
+out:
+    if (DEBUG) {
+       printf ("run complete: %d\n", ret);
+    }
+
+    for (n = 0; n < num_slaves; n++) {
+       if (slaves[n].fd != -1)
+           close (slaves[n].fd);
+
+       if (slaves[n].image == NULL)
+           continue;
+
+       cairo_surface_destroy (slaves[n].image);
+       slaves[n].image = NULL;
+
+       cairo_surface_destroy (slaves[n].difference);
+       slaves[n].difference = NULL;
+
+       slaves[n].image_serial = 0;
+       slaves[n].image_ready = 0;
+    }
+
+    free (pfd);
+
+    return ret;
+}
+
+static int
+server_socket (const char *socket_path)
+{
+    long flags;
+    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 (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
+server_shm (const char *shm_path)
+{
+    int fd;
+
+    fd = shm_open (shm_path, O_RDWR | O_EXCL | O_CREAT, 0777);
+    if (fd == -1)
+       return -1;
+
+    if (ftruncate (fd, DATA_SIZE) == -1) {
+       close (fd);
+       return -1;
+    }
+
+    return fd;
+}
+
+static cairo_bool_t
+_test_trace (test_trace_t *test,
+            const char *trace,
+            const char *name,
+            struct error_info *error)
+{
+    const char *shm_path = SHM_PATH_XXX;
+    const cairo_boilerplate_target_t *target, *image;
+    struct slave *slaves, *s;
+    test_runner_t *recorder = NULL;
+    pid_t slave;
+    char socket_dir[] = "/tmp/cairo-test-trace.XXXXXX";
+    char *socket_path;
+    int sk, fd;
+    int i, num_slaves;
+    void *base;
+    cairo_bool_t ret = FALSE;
+
+    if (DEBUG)
+       printf ("setting up trace '%s'\n", trace);
+
+    /* create a socket to control the test runners */
+    if (mkdtemp (socket_dir) == NULL) {
+       fprintf (stderr, "Unable to create temporary name for socket\n");
+       return FALSE;
+    }
+
+    xasprintf (&socket_path, "%s/socket", socket_dir);
+    sk = server_socket (socket_path);
+    if (sk == -1) {
+       fprintf (stderr, "Unable to create socket for server\n");
+       goto cleanup_paths;
+    }
+
+    /* allocate some shared memory */
+    fd = server_shm (shm_path);
+    if (fd == -1) {
+       fprintf (stderr, "Unable to create shared memory '%s': %s\n",
+                shm_path, strerror (errno));
+       goto cleanup_sk;
+    }
+
+    image = cairo_boilerplate_get_image_target (CAIRO_CONTENT_COLOR_ALPHA);
+    assert (image != NULL);
+
+    s = slaves = xcalloc (2*test->num_targets + 1, sizeof (struct slave));
+
+#if CAIRO_HAS_REAL_PTHREAD
+    /* set-up a recording-surface to reconstruct errors */
+    slave = spawn_recorder (socket_path, trace, &recorder);
+    if (slave < 0) {
+        fprintf (stderr, "Unable to create recording surface\n");
+        goto cleanup_sk;
+    }
+
+    s->pid = slave;
+    s->is_recording = TRUE;
+    s->target = NULL;
+    s->fd = -1;
+    s->reference = NULL;
+    s++;
+#endif
+
+    /* spawn slave processes to run the trace */
+    for (i = 0; i < test->num_targets; i++) {
+       const cairo_boilerplate_target_t *reference;
+       struct slave *master;
+
+       target = test->targets[i];
+
+       if (DEBUG)
+           printf ("setting up target[%d]? '%s' (image? %d, measurable? %d)\n",
+                   i, target->name, target == image, target->is_measurable);
+
+       if (target == image || ! target->is_measurable)
+           continue;
+
+       /* find a matching slave to use as a reference for this target */
+       if (target->reference_target != NULL) {
+           reference =
+               cairo_boilerplate_get_target_by_name (target->reference_target,
+                                                     target->content);
+           assert (reference != NULL);
+       } else {
+           reference = image;
+       }
+       for (master = slaves; master < s; master++) {
+           if (master->target == reference)
+               break;
+       }
+
+       if (master == s) {
+           /* no match found, spawn a slave to render the reference image */
+           slave = spawn_target (socket_path, shm_path, reference, trace);
+           if (slave < 0)
+               continue;
+
+           s->pid = slave;
+           s->target = reference;
+           s->fd = -1;
+           s->reference = NULL;
+           s++;
+       }
+
+       slave = spawn_target (socket_path, shm_path, target, trace);
+       if (slave < 0)
+           continue;
+
+       s->pid = slave;
+       s->target = target;
+       s->fd = -1;
+       s->reference = master;
+       s++;
+    }
+    num_slaves = s - slaves;
+    if (num_slaves == 1) {
+       fprintf (stderr, "No targets to test\n");
+       goto cleanup;
+    }
+
+    base = mmap (NULL, DATA_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+    if (base == MAP_FAILED) {
+       fprintf (stderr, "Unable to mmap shared memory\n");
+       goto cleanup;
+    }
+    ret = test_run (base, sk, name, slaves, num_slaves, error);
+    munmap (base, DATA_SIZE);
+
+cleanup:
+    close (fd);
+    while (s-- > slaves) {
+       int status;
+
+       if (s->fd != -1)
+           close (s->fd);
+
+       cairo_surface_destroy (s->image);
+       cairo_surface_destroy (s->difference);
+
+       if (s->is_recording) /* in-process */
+           continue;
+
+       kill (s->pid, SIGKILL);
+       waitpid (s->pid, &status, 0);
+       if (WIFSIGNALED (status) && WTERMSIG(status) != SIGKILL) {
+           fprintf (stderr, "%s crashed\n", s->target->name);
+           if (recorder)
+               dump_traces (recorder, trace, s->target->name, "crash");
+       }
+    }
+    free (slaves);
+    shm_unlink (shm_path);
+cleanup_sk:
+    close (sk);
+
+cleanup_paths:
+    remove (socket_path);
+    remove (socket_dir);
+
+    free (socket_path);
+    return ret;
+}
+
+static void
+test_trace (test_trace_t *test, const char *trace)
+{
+    char *trace_cpy, *name, *dot;
+
+    trace_cpy = xstrdup (trace);
+    name = basename (trace_cpy);
+    dot = strchr (name, '.');
+    if (dot)
+       *dot = '\0';
+
+    if (test->list_only) {
+       printf ("%s\n", name);
+    } else {
+       struct error_info error = {0};
+       cairo_bool_t ret;
+
+       printf ("%s: ", name);
+       fflush (stdout);
+
+       ret = _test_trace (test, trace, name, &error);
+       if (ret) {
+           printf ("PASS\n");
+       } else {
+           if (error.context_id) {
+               printf ("FAIL (context %lu, lines [%lu, %lu])\n",
+                       error.context_id,
+                       error.start_line,
+                       error.end_line);
+           } else {
+               printf ("FAIL\n");
+           }
+       }
+    }
+
+    free (trace_cpy);
+}
+
+static cairo_bool_t
+read_excludes (test_trace_t *test, const char *filename)
+{
+    FILE *file;
+    char *line = NULL;
+    size_t line_size = 0;
+    char *s, *t;
+
+    file = fopen (filename, "r");
+    if (file == NULL)
+       return FALSE;
+
+    while (getline (&line, &line_size, file) != -1) {
+       /* terminate the line at a comment marker '#' */
+       s = strchr (line, '#');
+       if (s)
+           *s = '\0';
+
+       /* whitespace delimits */
+       s = line;
+       while (*s != '\0' && isspace (*s))
+           s++;
+
+       t = s;
+       while (*t != '\0' && ! isspace (*t))
+           t++;
+
+       if (s != t) {
+           int i = test->num_exclude_names;
+           test->exclude_names = xrealloc (test->exclude_names,
+                                           sizeof (char *) * (i+1));
+           test->exclude_names[i] = strndup (s, t-s);
+           test->num_exclude_names++;
+       }
+    }
+    free (line);
+
+    fclose (file);
+
+    return TRUE;
+}
+
+static void
+usage (const char *argv0)
+{
+    fprintf (stderr,
+"Usage: %s [-x exclude-file] [test-names ... | traces ...]\n"
+"       %s -l\n"
+"\n"
+"Run the cairo test suite over the given traces (all by default).\n"
+"The command-line arguments are interpreted as follows:\n"
+"\n"
+"  -x   exclude; specify a file to read a list of traces to exclude\n"
+"  -l  list only; just list selected test case names without executing\n"
+"\n"
+"If test names are given they are used as sub-string matches so a command\n"
+"such as \"cairo-test-trace firefox\" can be used to run all firefox traces.\n"
+"Alternatively, you can specify a list of filenames to execute.\n",
+            argv0, argv0);
+}
+
+static void
+parse_options (test_trace_t *test, int argc, char *argv[])
+{
+    int c;
+
+    test->list_only = FALSE;
+    test->names = NULL;
+    test->num_names = 0;
+    test->exclude_names = NULL;
+    test->num_exclude_names = 0;
+
+    while (1) {
+       c = _cairo_getopt (argc, argv, "x:l");
+       if (c == -1)
+           break;
+
+       switch (c) {
+       case 'l':
+           test->list_only = TRUE;
+           break;
+       case 'x':
+           if (! read_excludes (test, optarg)) {
+               fprintf (stderr, "Invalid argument for -x (not readable file): %s\n",
+                        optarg);
+               exit (1);
+           }
+           break;
+       default:
+           fprintf (stderr, "Internal error: unhandled option: %c\n", c);
+           /* fall-through */
+       case '?':
+           usage (argv[0]);
+           exit (1);
+       }
+    }
+
+    if (optind < argc) {
+       test->names = &argv[optind];
+       test->num_names = argc - optind;
+    }
+}
+
+static void
+test_reset (test_trace_t *test)
+{
+    /* XXX leaking fonts again via recording-surface? */
+#if 0
+    cairo_debug_reset_static_data ();
+#if HAVE_FCFINI
+    FcFini ();
+#endif
+#endif
+}
+
+static void
+test_fini (test_trace_t *test)
+{
+    test_reset (test);
+
+    cairo_boilerplate_free_targets (test->targets);
+    free (test->exclude_names);
+}
+
+static cairo_bool_t
+test_has_filenames (test_trace_t *test)
+{
+    unsigned int i;
+
+    if (test->num_names == 0)
+       return FALSE;
+
+    for (i = 0; i < test->num_names; i++)
+       if (access (test->names[i], R_OK) == 0)
+           return TRUE;
+
+    return FALSE;
+}
+
+static cairo_bool_t
+test_can_run (test_trace_t *test, const char *name)
+{
+    unsigned int i;
+    char *copy, *dot;
+    cairo_bool_t ret;
+
+    if (test->num_names == 0 && test->num_exclude_names == 0)
+       return TRUE;
+
+    copy = xstrdup (name);
+    dot = strrchr (copy, '.');
+    if (dot != NULL)
+       *dot = '\0';
+
+    if (test->num_names) {
+       ret = TRUE;
+       for (i = 0; i < test->num_names; i++)
+           if (strstr (copy, test->names[i]))
+               goto check_exclude;
+
+       ret = FALSE;
+       goto done;
+    }
+
+check_exclude:
+    if (test->num_exclude_names) {
+       ret = FALSE;
+       for (i = 0; i < test->num_exclude_names; i++)
+           if (strstr (copy, test->exclude_names[i]))
+               goto done;
+
+       ret = TRUE;
+       goto done;
+    }
+
+done:
+    free (copy);
+
+    return ret;
+}
+
+static void
+warn_no_traces (const char *message, const char *trace_dir)
+{
+    fprintf (stderr,
+"Error: %s '%s'.\n"
+"Have you cloned the cairo-traces repository and uncompressed the traces?\n"
+"  git clone git://anongit.freedesktop.org/cairo-traces\n"
+"  cd cairo-traces && make\n"
+"Or set the env.var CAIRO_TRACE_DIR to point to your traces?\n",
+           message, trace_dir);
+}
+
+static void
+interrupt (int sig)
+{
+    shm_unlink (SHM_PATH_XXX);
+
+    signal (sig, SIG_DFL);
+    raise (sig);
+}
+
+int
+main (int argc, char *argv[])
+{
+    test_trace_t test;
+    const char *trace_dir = "cairo-traces";
+    unsigned int n;
+
+    signal (SIGPIPE, SIG_IGN);
+    signal (SIGINT, interrupt);
+
+    parse_options (&test, argc, argv);
+
+    shm_unlink (SHM_PATH_XXX);
+
+    if (getenv ("CAIRO_TRACE_DIR") != NULL)
+       trace_dir = getenv ("CAIRO_TRACE_DIR");
+
+    test.targets = cairo_boilerplate_get_targets (&test.num_targets, NULL);
+
+    if (test_has_filenames (&test)) {
+       for (n = 0; n < test.num_names; n++) {
+           if (access (test.names[n], R_OK) == 0) {
+               test_trace (&test, test.names[n]);
+               test_reset (&test);
+           }
+       }
+    } else {
+       DIR *dir;
+       struct dirent *de;
+       int num_traces = 0;
+
+       dir = opendir (trace_dir);
+       if (dir == NULL) {
+           warn_no_traces ("Failed to open directory", trace_dir);
+           test_fini (&test);
+           return 1;
+       }
+
+       while ((de = readdir (dir)) != NULL) {
+           char *trace;
+           const char *dot;
+
+           dot = strrchr (de->d_name, '.');
+           if (dot == NULL)
+               continue;
+           if (strcmp (dot, ".trace"))
+               continue;
+
+           num_traces++;
+           if (! test_can_run (&test, de->d_name))
+               continue;
+
+           xasprintf (&trace, "%s/%s", trace_dir, de->d_name);
+           test_trace (&test, trace);
+           test_reset (&test);
+
+           free (trace);
+
+       }
+       closedir (dir);
+
+       if (num_traces == 0) {
+           warn_no_traces ("Found no traces in", trace_dir);
+           test_fini (&test);
+           return 1;
+       }
+    }
+
+    test_fini (&test);
+
+    return 0;
+}
+
+void
+cairo_test_logv (const cairo_test_context_t *ctx,
+                const char *fmt, va_list va)
+{
+#if 0
+    vfprintf (stderr, fmt, va);
+#endif
+}
+
+void
+cairo_test_log (const cairo_test_context_t *ctx, const char *fmt, ...)
+{
+#if 0
+    va_list va;
+
+    va_start (va, fmt);
+    vfprintf (stderr, fmt, va);
+    va_end (va);
+#endif
+}
diff --git a/test/cairo-test.c b/test/cairo-test.c
new file mode 100644 (file)
index 0000000..b2d6b3f
--- /dev/null
@@ -0,0 +1,2070 @@
+/*
+ * Copyright © 2004 Red Hat, Inc.
+ * Copyright © 2008 Chris Wilson
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ *         Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#define _GNU_SOURCE 1  /* for feenableexcept() et al */
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <ctype.h>
+#if HAVE_FEENABLEEXCEPT
+#include <fenv.h>
+#endif
+#include <assert.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <errno.h>
+#include <string.h>
+#if HAVE_FCFINI
+#include <fontconfig/fontconfig.h>
+#endif
+#if CAIRO_HAS_REAL_PTHREAD
+#include <pthread.h>
+#endif
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+
+#if HAVE_VALGRIND
+#include <valgrind.h>
+#else
+#define RUNNING_ON_VALGRIND 0
+#endif
+
+#if HAVE_MEMFAULT
+#include <memfault.h>
+#define MF(x) x
+#else
+#define MF(x)
+#endif
+
+#include "cairo-test-private.h"
+
+#include "buffer-diff.h"
+
+#ifdef _MSC_VER
+#include <crtdbg.h>
+#define F_OK 0
+#endif
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+#ifndef TRUE
+#define TRUE !FALSE
+#endif
+
+#ifndef ARRAY_SIZE
+#define ARRAY_SIZE(A) (sizeof(A) / sizeof (A[0]))
+#endif
+
+#if ! HAVE_ALARM
+#define alarm(X);
+#endif
+
+static const cairo_user_data_key_t _cairo_test_context_key;
+
+static void
+_xunlink (const cairo_test_context_t *ctx, const char *pathname);
+
+static const char *fail_face = "", *xfail_face="", *normal_face = "";
+static cairo_bool_t print_fail_on_stdout;
+static int cairo_test_timeout = 60;
+
+#define NUM_DEVICE_OFFSETS 2
+
+static char *
+_cairo_test_fixup_name (const char *original)
+{
+    int len = strlen (original);
+    char *name, *s;
+
+    name = xmalloc (len + 1);
+    s = memcpy (name, original, len + 1);
+    while ((s = strchr (s, '_')) != NULL)
+       *s++ = '-';
+
+    return name;
+}
+
+char *
+cairo_test_get_name (const cairo_test_t *test)
+{
+    return _cairo_test_fixup_name (test->name);
+}
+
+static void
+_cairo_test_init (cairo_test_context_t *ctx,
+                 const cairo_test_context_t *parent,
+                 const cairo_test_t *test,
+                 const char *test_name,
+                 const char *output)
+{
+    char *log_name;
+
+    MF (MEMFAULT_DISABLE_FAULTS ());
+
+#if HAVE_FEENABLEEXCEPT
+    feenableexcept (FE_DIVBYZERO | FE_INVALID | FE_OVERFLOW);
+#endif
+
+    ctx->test = test;
+    ctx->test_name = _cairo_test_fixup_name (test_name);
+    ctx->output = output;
+
+    ctx->malloc_failure = 0;
+#if HAVE_MEMFAULT
+    if (getenv ("CAIRO_TEST_MALLOC_FAILURE"))
+       ctx->malloc_failure = atoi (getenv ("CAIRO_TEST_MALLOC_FAILURE"));
+    if (ctx->malloc_failure && ! RUNNING_ON_MEMFAULT ())
+       ctx->malloc_failure = 0;
+#endif
+
+    ctx->timeout = cairo_test_timeout;
+    if (getenv ("CAIRO_TEST_TIMEOUT"))
+       ctx->timeout = atoi (getenv ("CAIRO_TEST_TIMEOUT"));
+
+    xasprintf (&log_name, "%s/%s%s", ctx->output, ctx->test_name, CAIRO_TEST_LOG_SUFFIX);
+    _xunlink (NULL, log_name);
+
+    ctx->log_file = fopen (log_name, "a");
+    if (ctx->log_file == NULL) {
+       fprintf (stderr, "Error opening log file: %s\n", log_name);
+       ctx->log_file = stderr;
+    }
+    free (log_name);
+
+    ctx->ref_name = NULL;
+    ctx->ref_image = NULL;
+    ctx->ref_image_flattened = NULL;
+
+    ctx->thread = 0;
+
+    if (parent != NULL) {
+       ctx->targets_to_test = parent->targets_to_test;
+       ctx->num_targets = parent->num_targets;
+       ctx->limited_targets = parent->limited_targets;
+       ctx->own_targets = FALSE;
+
+       ctx->srcdir = parent->srcdir;
+       ctx->refdir = parent->refdir;
+    } else {
+       int tmp_num_targets;
+       cairo_bool_t tmp_limited_targets;
+
+       ctx->targets_to_test = cairo_boilerplate_get_targets (&tmp_num_targets, &tmp_limited_targets);
+       ctx->num_targets = tmp_num_targets;
+       ctx->limited_targets = tmp_limited_targets;
+       ctx->own_targets = TRUE;
+
+       ctx->srcdir = getenv ("srcdir");
+       if (ctx->srcdir == NULL)
+           ctx->srcdir = ".";
+
+       ctx->refdir = getenv ("CAIRO_REF_DIR");
+    }
+
+#ifdef HAVE_UNISTD_H
+    if (*fail_face == '\0' && isatty (2)) {
+       fail_face = "\033[41;37;1m";
+       xfail_face = "\033[43;37;1m";
+       normal_face = "\033[m";
+       if (isatty (1))
+           print_fail_on_stdout = FALSE;
+    }
+#endif
+
+    printf ("\nTESTING %s\n", ctx->test_name);
+}
+
+void
+_cairo_test_context_init_for_test (cairo_test_context_t *ctx,
+                                  const cairo_test_context_t *parent,
+                                  const cairo_test_t *test)
+{
+    _cairo_test_init (ctx, parent, test, test->name, CAIRO_TEST_OUTPUT_DIR);
+}
+
+void
+cairo_test_init (cairo_test_context_t *ctx,
+                const char *test_name,
+                const char *output)
+{
+    _cairo_test_init (ctx, NULL, NULL, test_name, output);
+}
+
+static void
+cairo_test_init_thread (cairo_test_context_t *ctx,
+                       cairo_test_context_t *master,
+                       int thread)
+{
+    MF (MEMFAULT_DISABLE_FAULTS ());
+
+    *ctx = *master;
+    ctx->thread = thread;
+}
+
+void
+cairo_test_fini (cairo_test_context_t *ctx)
+{
+    if (ctx->thread != 0)
+       return;
+
+    if (ctx->log_file == NULL)
+       return;
+
+    if (ctx->log_file != stderr)
+       fclose (ctx->log_file);
+    ctx->log_file = NULL;
+
+    if (ctx->ref_name != NULL)
+       free (ctx->ref_name);
+    cairo_surface_destroy (ctx->ref_image);
+    cairo_surface_destroy (ctx->ref_image_flattened);
+
+    if (ctx->test_name != NULL)
+       free ((char *) ctx->test_name);
+
+    if (ctx->own_targets)
+       cairo_boilerplate_free_targets (ctx->targets_to_test);
+
+    cairo_boilerplate_fini ();
+
+    cairo_debug_reset_static_data ();
+#if HAVE_FCFINI
+    FcFini ();
+#endif
+}
+
+void
+cairo_test_logv (const cairo_test_context_t *ctx,
+               const char *fmt, va_list va)
+{
+    FILE *file = ctx && ctx->log_file ? ctx->log_file : stderr;
+    vfprintf (file, fmt, va);
+}
+
+void
+cairo_test_log (const cairo_test_context_t *ctx, const char *fmt, ...)
+{
+    va_list va;
+    FILE *file = ctx && ctx->log_file ? ctx->log_file : stderr;
+
+    va_start (va, fmt);
+    vfprintf (file, fmt, va);
+    va_end (va);
+}
+
+void
+cairo_test_log_path (const cairo_test_context_t *ctx,
+                    const cairo_path_t *path)
+{
+  int i;
+
+  for (i = 0; i < path->num_data; i += path->data[i].header.length) {
+    cairo_path_data_t *data = &path->data[i];
+    switch (data->header.type) {
+    case CAIRO_PATH_MOVE_TO:
+       cairo_test_log (ctx,
+                       "    cairo_move_to (cr, %g, %g);\n",
+                       data[1].point.x, data[1].point.y);
+        break;
+    case CAIRO_PATH_LINE_TO:
+       cairo_test_log (ctx,
+                       "    cairo_line_to (cr, %g, %g);\n",
+                       data[1].point.x, data[1].point.y);
+       break;
+    case CAIRO_PATH_CURVE_TO:
+       cairo_test_log (ctx,
+                       "    cairo_curve_to (cr, %g, %g, %g, %g, %g, %g);\n",
+                       data[1].point.x, data[1].point.y,
+                       data[2].point.x, data[2].point.y,
+                       data[3].point.x, data[3].point.y);
+       break;
+    case CAIRO_PATH_CLOSE_PATH:
+       cairo_test_log (ctx,
+                       "    cairo_close_path (cr);\n\n");
+       break;
+    default:
+       assert (0);
+    }
+  }
+}
+
+static void
+_xunlink (const cairo_test_context_t *ctx, const char *pathname)
+{
+    if (unlink (pathname) < 0 && errno != ENOENT) {
+       cairo_test_log (ctx, "Error: Cannot remove %s: %s\n",
+                       pathname, strerror (errno));
+       exit (1);
+    }
+}
+
+char *
+cairo_test_reference_filename (const cairo_test_context_t *ctx,
+                              const char *base_name,
+                              const char *test_name,
+                              const char *target_name,
+                              const char *base_target_name,
+                              const char *format,
+                              const char *suffix,
+                              const char *extension)
+{
+    char *ref_name = NULL;
+
+    /* First look for a previous build for comparison. */
+    if (ctx->refdir != NULL) {
+       xasprintf (&ref_name, "%s/%s%s%s",
+                  ctx->refdir,
+                  base_name,
+                  suffix,
+                  extension);
+       if (access (ref_name, F_OK) != 0)
+           free (ref_name);
+       else
+           goto done;
+    }
+
+    if (target_name != NULL) {
+       /* Next look for a target/format-specific reference image. */
+       xasprintf (&ref_name, "%s/reference/%s.%s.%s%s%s",
+                  ctx->srcdir,
+                  test_name,
+                  target_name,
+                  format,
+                  suffix,
+                  extension);
+       if (access (ref_name, F_OK) != 0)
+           free (ref_name);
+       else
+           goto done;
+
+       /* Next, look for target-specific reference image. */
+       xasprintf (&ref_name, "%s/reference/%s.%s%s%s",
+                  ctx->srcdir,
+                  test_name,
+                  target_name,
+                  suffix,
+                  extension);
+       if (access (ref_name, F_OK) != 0)
+           free (ref_name);
+       else
+           goto done;
+    }
+
+    if (base_target_name != NULL) {
+       /* Next look for a base/format-specific reference image. */
+       xasprintf (&ref_name, "%s/reference/%s.%s.%s%s%s",
+                  ctx->srcdir,
+                  test_name,
+                  base_target_name,
+                  format,
+                  suffix,
+                  extension);
+       if (access (ref_name, F_OK) != 0)
+           free (ref_name);
+       else
+           goto done;
+
+       /* Next, look for base-specific reference image. */
+       xasprintf (&ref_name, "%s/reference/%s.%s%s%s",
+                  ctx->srcdir,
+                  test_name,
+                  base_target_name,
+                  suffix,
+                  extension);
+       if (access (ref_name, F_OK) != 0)
+           free (ref_name);
+       else
+           goto done;
+    }
+
+    /* Next, look for format-specific reference image. */
+    xasprintf (&ref_name, "%s/reference/%s.%s%s%s",
+              ctx->srcdir,
+              test_name,
+              format,
+              suffix,
+              extension);
+    if (access (ref_name, F_OK) != 0)
+       free (ref_name);
+    else
+       goto done;
+
+    /* Finally, look for the standard reference image. */
+    xasprintf (&ref_name, "%s/reference/%s%s%s", ctx->srcdir,
+              test_name,
+              suffix,
+              extension);
+    if (access (ref_name, F_OK) != 0)
+       free (ref_name);
+    else
+       goto done;
+
+    ref_name = NULL;
+
+done:
+    return ref_name;
+}
+
+static cairo_bool_t
+_cairo_test_mkdir (const char *path)
+{
+#if ! HAVE_MKDIR
+    return FALSE;
+#elif HAVE_MKDIR == 1
+    if (mkdir (path) == 0)
+       return TRUE;
+#elif HAVE_MKDIR == 2
+    if (mkdir (path, 0770) == 0)
+       return TRUE;
+#else
+#error Bad value for HAVE_MKDIR
+#endif
+
+    return errno == EEXIST;
+}
+
+cairo_test_similar_t
+cairo_test_target_has_similar (const cairo_test_context_t *ctx,
+                              const cairo_boilerplate_target_t *target)
+{
+    cairo_surface_t *surface;
+    cairo_test_similar_t has_similar;
+    cairo_t * cr;
+    cairo_surface_t *similar;
+    cairo_status_t status;
+    void *closure;
+    char *path;
+
+    /* ignore image intermediate targets */
+    if (target->expected_type == CAIRO_SURFACE_TYPE_IMAGE)
+       return DIRECT;
+
+    if (getenv ("CAIRO_TEST_IGNORE_SIMILAR"))
+       return DIRECT;
+
+    xasprintf (&path, "%s/%s",
+              _cairo_test_mkdir (ctx->output) ? ctx->output : ".",
+              ctx->test_name);
+
+    has_similar = DIRECT;
+    do {
+       do {
+           surface = (target->create_surface) (path,
+                                               target->content,
+                                               ctx->test->width,
+                                               ctx->test->height,
+                                               ctx->test->width + 25 * NUM_DEVICE_OFFSETS,
+                                               ctx->test->height + 25 * NUM_DEVICE_OFFSETS,
+                                               CAIRO_BOILERPLATE_MODE_TEST,
+                                               0,
+                                               &closure);
+           if (surface == NULL)
+               goto out;
+       } while (cairo_test_malloc_failure (ctx, cairo_surface_status (surface)));
+
+       if (cairo_surface_status (surface))
+           goto out;
+
+       cr = cairo_create (surface);
+       cairo_push_group_with_content (cr,
+                                      cairo_boilerplate_content (target->content));
+       similar = cairo_get_group_target (cr);
+       status = cairo_surface_status (similar);
+
+       if (cairo_surface_get_type (similar) == cairo_surface_get_type (surface))
+           has_similar = SIMILAR;
+       else
+           has_similar = DIRECT;
+
+       cairo_destroy (cr);
+       cairo_surface_destroy (surface);
+
+       if (target->cleanup)
+           target->cleanup (closure);
+    } while (! has_similar && cairo_test_malloc_failure (ctx, status));
+out:
+    free (path);
+
+    return has_similar;
+}
+
+static cairo_surface_t *
+_cairo_test_flatten_reference_image (cairo_test_context_t *ctx,
+                                    cairo_bool_t flatten)
+{
+    cairo_surface_t *surface;
+    cairo_t *cr;
+
+    if (! flatten)
+       return ctx->ref_image;
+
+    if (ctx->ref_image_flattened != NULL)
+       return ctx->ref_image_flattened;
+
+    surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
+                                         cairo_image_surface_get_width (ctx->ref_image),
+                                         cairo_image_surface_get_height (ctx->ref_image));
+    cr = cairo_create (surface);
+    cairo_surface_destroy (surface);
+
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_paint (cr);
+
+    cairo_set_source_surface (cr, ctx->ref_image, 0, 0);
+    cairo_paint (cr);
+
+    surface = cairo_surface_reference (cairo_get_target (cr));
+    cairo_destroy (cr);
+
+    if (cairo_surface_status (surface) == CAIRO_STATUS_SUCCESS)
+       ctx->ref_image_flattened = surface;
+    return surface;
+}
+
+cairo_surface_t *
+cairo_test_get_reference_image (cairo_test_context_t *ctx,
+                               const char *filename,
+                               cairo_bool_t flatten)
+{
+    cairo_surface_t *surface;
+    int len;
+
+    if (ctx->ref_name != NULL) {
+       if (strcmp (ctx->ref_name, filename) == 0)
+           return _cairo_test_flatten_reference_image (ctx, flatten);
+
+       cairo_surface_destroy (ctx->ref_image);
+       ctx->ref_image = NULL;
+
+       cairo_surface_destroy (ctx->ref_image_flattened);
+       ctx->ref_image_flattened = NULL;
+
+       free (ctx->ref_name);
+       ctx->ref_name = NULL;
+    }
+
+    surface = cairo_image_surface_create_from_png (filename);
+    if (cairo_surface_status (surface))
+       return surface;
+
+    len = strlen (filename);
+    ctx->ref_name = xmalloc (len + 1);
+    memcpy (ctx->ref_name, filename, len + 1);
+
+    ctx->ref_image = surface;
+    return _cairo_test_flatten_reference_image (ctx, flatten);
+}
+
+static cairo_bool_t
+cairo_test_file_is_older (const char *filename,
+                         char **ref_filenames,
+                         int num_ref_filenames)
+{
+#if HAVE_SYS_STAT_H
+    struct stat st;
+
+    if (stat (filename, &st) < 0)
+       return FALSE;
+
+    while (num_ref_filenames--) {
+       struct stat ref;
+       char *ref_filename = *ref_filenames++;
+
+       if (ref_filename == NULL)
+           continue;
+
+       if (stat (ref_filename++, &ref) < 0)
+           continue;
+
+       if (st.st_mtime <= ref.st_mtime)
+           return TRUE;
+    }
+#endif
+
+    return FALSE;
+}
+
+static cairo_bool_t
+cairo_test_files_equal (const char *test_filename,
+                       const char *pass_filename)
+{
+    FILE *test, *pass;
+    int t, p;
+
+    if (test_filename == NULL || pass_filename == NULL)
+       return FALSE;
+
+    test = fopen (test_filename, "rb");
+    if (test == NULL)
+       return FALSE;
+
+    pass = fopen (pass_filename, "rb");
+    if (pass == NULL) {
+       fclose (test);
+       return FALSE;
+    }
+
+    /* as simple as it gets */
+    do {
+       t = getc (test);
+       p = getc (pass);
+       if (t != p)
+           break;
+    } while (t != EOF && p != EOF);
+
+    fclose (pass);
+    fclose (test);
+
+    return t == p; /* both EOF */
+}
+
+static cairo_bool_t
+cairo_test_copy_file (const char *src_filename,
+                     const char *dst_filename)
+{
+    FILE *src, *dst;
+    int c;
+
+#if HAVE_LINK
+    if (link (src_filename, dst_filename) == 0)
+       return TRUE;
+
+    unlink (dst_filename);
+#endif
+
+    src = fopen (src_filename, "rb");
+    if (src == NULL)
+       return FALSE;
+
+    dst = fopen (dst_filename, "wb");
+    if (dst == NULL) {
+       fclose (src);
+       return FALSE;
+    }
+
+    /* as simple as it gets */
+    while ((c = getc (src)) != EOF)
+       putc (c, dst);
+
+    fclose (src);
+    fclose (dst);
+
+    return TRUE;
+}
+
+static cairo_test_status_t
+cairo_test_for_target (cairo_test_context_t             *ctx,
+                      const cairo_boilerplate_target_t  *target,
+                      int                                dev_offset,
+                      cairo_bool_t                       similar)
+{
+    cairo_test_status_t status;
+    cairo_surface_t *surface = NULL;
+    cairo_t *cr;
+    const char *empty_str = "";
+    char *offset_str, *thread_str;
+    char *base_name, *base_path;
+    char *out_png_path;
+    char *ref_path = NULL, *ref_png_path, *cmp_png_path = NULL;
+    char *new_path = NULL, *new_png_path;
+    char *xfail_path = NULL, *xfail_png_path;
+    char *base_ref_png_path;
+    char *base_new_png_path;
+    char *base_xfail_png_path;
+    char *diff_png_path;
+    char *test_filename = NULL, *pass_filename = NULL, *fail_filename = NULL;
+    cairo_test_status_t ret;
+    cairo_content_t expected_content;
+    cairo_font_options_t *font_options;
+    const char *format;
+    cairo_bool_t have_output = FALSE;
+    cairo_bool_t have_result = FALSE;
+    void *closure;
+    double width, height;
+    cairo_bool_t have_output_dir;
+#if HAVE_MEMFAULT
+    int malloc_failure_iterations = ctx->malloc_failure;
+    int last_fault_count = 0;
+#endif
+
+    /* Get the strings ready that we'll need. */
+    format = cairo_boilerplate_content_name (target->content);
+    if (dev_offset)
+       xasprintf (&offset_str, ".%d", dev_offset);
+    else
+       offset_str = (char *) empty_str;
+    if (ctx->thread)
+       xasprintf (&thread_str, ".thread%d", ctx->thread);
+    else
+       thread_str = (char *) empty_str;
+
+    xasprintf (&base_name, "%s.%s.%s%s%s%s",
+              ctx->test_name,
+              target->name,
+              format,
+              similar ? ".similar" : "",
+              offset_str,
+              thread_str);
+
+    if (offset_str != empty_str)
+      free (offset_str);
+    if (thread_str != empty_str)
+      free (thread_str);
+
+
+    ref_png_path = cairo_test_reference_filename (ctx,
+                                                 base_name,
+                                                 ctx->test_name,
+                                                 target->name,
+                                                 target->basename,
+                                                 format,
+                                                 CAIRO_TEST_REF_SUFFIX,
+                                                 CAIRO_TEST_PNG_EXTENSION);
+    new_png_path = cairo_test_reference_filename (ctx,
+                                                 base_name,
+                                                 ctx->test_name,
+                                                 target->name,
+                                                 target->basename,
+                                                 format,
+                                                 CAIRO_TEST_NEW_SUFFIX,
+                                                 CAIRO_TEST_PNG_EXTENSION);
+    xfail_png_path = cairo_test_reference_filename (ctx,
+                                                   base_name,
+                                                   ctx->test_name,
+                                                   target->name,
+                                                   target->basename,
+                                                   format,
+                                                   CAIRO_TEST_XFAIL_SUFFIX,
+                                                   CAIRO_TEST_PNG_EXTENSION);
+
+    base_ref_png_path = cairo_test_reference_filename (ctx,
+                                                 base_name,
+                                                 ctx->test_name,
+                                                 NULL, NULL,
+                                                 format,
+                                                 CAIRO_TEST_REF_SUFFIX,
+                                                 CAIRO_TEST_PNG_EXTENSION);
+    base_new_png_path = cairo_test_reference_filename (ctx,
+                                                 base_name,
+                                                 ctx->test_name,
+                                                 NULL, NULL,
+                                                 format,
+                                                 CAIRO_TEST_NEW_SUFFIX,
+                                                 CAIRO_TEST_PNG_EXTENSION);
+    base_xfail_png_path = cairo_test_reference_filename (ctx,
+                                                   base_name,
+                                                   ctx->test_name,
+                                                   NULL, NULL,
+                                                   format,
+                                                   CAIRO_TEST_XFAIL_SUFFIX,
+                                                   CAIRO_TEST_PNG_EXTENSION);
+
+    if (target->file_extension != NULL) {
+       ref_path = cairo_test_reference_filename (ctx,
+                                                 base_name,
+                                                 ctx->test_name,
+                                                 target->name,
+                                                 target->basename,
+                                                 format,
+                                                 CAIRO_TEST_REF_SUFFIX,
+                                                 target->file_extension);
+       new_path = cairo_test_reference_filename (ctx,
+                                                 base_name,
+                                                 ctx->test_name,
+                                                 target->name,
+                                                 target->basename,
+                                                 format,
+                                                 CAIRO_TEST_NEW_SUFFIX,
+                                                 target->file_extension);
+       xfail_path = cairo_test_reference_filename (ctx,
+                                                   base_name,
+                                                   ctx->test_name,
+                                                   target->name,
+                                                   target->basename,
+                                                   format,
+                                                   CAIRO_TEST_XFAIL_SUFFIX,
+                                                   target->file_extension);
+    }
+
+    have_output_dir = _cairo_test_mkdir (ctx->output);
+    xasprintf (&base_path, "%s/%s",
+              have_output_dir ? ctx->output : ".",
+              base_name);
+    xasprintf (&out_png_path, "%s" CAIRO_TEST_OUT_PNG, base_path);
+    xasprintf (&diff_png_path, "%s" CAIRO_TEST_DIFF_PNG, base_path);
+
+    if (ctx->test->requirements != NULL) {
+       const char *required;
+
+       required = target->is_vector ? "target=raster" : "target=vector";
+       if (strstr (ctx->test->requirements, required) != NULL) {
+           cairo_test_log (ctx, "Error: Skipping for %s target %s\n",
+                           target->is_vector ? "vector" : "raster",
+                           target->name);
+           ret = CAIRO_TEST_UNTESTED;
+           goto UNWIND_STRINGS;
+       }
+
+       required = target->is_recording ? "target=!recording" : "target=recording";
+       if (strstr (ctx->test->requirements, required) != NULL) {
+           cairo_test_log (ctx, "Error: Skipping for %s target %s\n",
+                           target->is_recording ? "recording" : "non-recording",
+                           target->name);
+           ret = CAIRO_TEST_UNTESTED;
+           goto UNWIND_STRINGS;
+       }
+    }
+
+    width = ctx->test->width;
+    height = ctx->test->height;
+    if (width && height) {
+       width += dev_offset;
+       height += dev_offset;
+    }
+
+#if HAVE_MEMFAULT
+REPEAT:
+    MEMFAULT_CLEAR_FAULTS ();
+    MEMFAULT_RESET_LEAKS ();
+    ctx->last_fault_count = 0;
+    last_fault_count = MEMFAULT_COUNT_FAULTS ();
+
+    /* Pre-initialise fontconfig so that the configuration is loaded without
+     * malloc failures (our primary goal is to test cairo fault tolerance).
+     */
+#if HAVE_FCINIT
+    FcInit ();
+#endif
+
+    MEMFAULT_ENABLE_FAULTS ();
+#endif
+    have_output = FALSE;
+    have_result = FALSE;
+
+    /* Run the actual drawing code. */
+    ret = CAIRO_TEST_SUCCESS;
+    surface = (target->create_surface) (base_path,
+                                       target->content,
+                                       width, height,
+                                       ctx->test->width + 25 * NUM_DEVICE_OFFSETS,
+                                       ctx->test->height + 25 * NUM_DEVICE_OFFSETS,
+                                       CAIRO_BOILERPLATE_MODE_TEST,
+                                       ctx->thread,
+                                       &closure);
+    if (surface == NULL) {
+       cairo_test_log (ctx, "Error: Failed to set %s target\n", target->name);
+       ret = CAIRO_TEST_UNTESTED;
+       goto UNWIND_STRINGS;
+    }
+
+#if HAVE_MEMFAULT
+    if (ctx->malloc_failure &&
+       MEMFAULT_COUNT_FAULTS () - last_fault_count > 0 &&
+       cairo_surface_status (surface) == CAIRO_STATUS_NO_MEMORY)
+    {
+       goto REPEAT;
+    }
+#endif
+
+    if (cairo_surface_status (surface)) {
+       MF (MEMFAULT_PRINT_FAULTS ());
+       cairo_test_log (ctx, "Error: Created an error surface: %s\n",
+                       cairo_status_to_string (cairo_surface_status (surface)));
+       ret = CAIRO_TEST_FAILURE;
+       goto UNWIND_STRINGS;
+    }
+
+    /* Check that we created a surface of the expected type. */
+    if (cairo_surface_get_type (surface) != target->expected_type) {
+       MF (MEMFAULT_PRINT_FAULTS ());
+       cairo_test_log (ctx, "Error: Created surface is of type %d (expected %d)\n",
+                       cairo_surface_get_type (surface), target->expected_type);
+       ret = CAIRO_TEST_UNTESTED;
+       goto UNWIND_SURFACE;
+    }
+
+    /* Check that we created a surface of the expected content,
+     * (ignore the artificial CAIRO_TEST_CONTENT_COLOR_ALPHA_FLATTENED value).
+     */
+    expected_content = cairo_boilerplate_content (target->content);
+
+    if (cairo_surface_get_content (surface) != expected_content) {
+       MF (MEMFAULT_PRINT_FAULTS ());
+       cairo_test_log (ctx, "Error: Created surface has content %d (expected %d)\n",
+                       cairo_surface_get_content (surface), expected_content);
+       ret = CAIRO_TEST_FAILURE;
+       goto UNWIND_SURFACE;
+    }
+
+    if (cairo_surface_set_user_data (surface,
+                                    &cairo_boilerplate_output_basename_key,
+                                    base_path,
+                                    NULL))
+    {
+#if HAVE_MEMFAULT
+       cairo_surface_destroy (surface);
+
+       if (target->cleanup)
+           target->cleanup (closure);
+
+       goto REPEAT;
+#else
+       ret = CAIRO_TEST_FAILURE;
+       goto UNWIND_SURFACE;
+#endif
+    }
+
+    cairo_surface_set_device_offset (surface, dev_offset, dev_offset);
+
+    cr = cairo_create (surface);
+    if (cairo_set_user_data (cr, &_cairo_test_context_key, (void*) ctx, NULL)) {
+#if HAVE_MEMFAULT
+       cairo_destroy (cr);
+       cairo_surface_destroy (surface);
+
+       if (target->cleanup)
+           target->cleanup (closure);
+
+       goto REPEAT;
+#else
+       ret = CAIRO_TEST_FAILURE;
+       goto UNWIND_CAIRO;
+#endif
+    }
+
+    if (similar)
+       cairo_push_group_with_content (cr, expected_content);
+
+    /* Clear to transparent (or black) depending on whether the target
+     * surface supports alpha. */
+    cairo_save (cr);
+    cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
+    cairo_paint (cr);
+    cairo_restore (cr);
+
+    /* Set all components of font_options to avoid backend differences
+     * and reduce number of needed reference images. */
+    font_options = cairo_font_options_create ();
+    cairo_font_options_set_hint_style (font_options, CAIRO_HINT_STYLE_NONE);
+    cairo_font_options_set_hint_metrics (font_options, CAIRO_HINT_METRICS_ON);
+    cairo_font_options_set_antialias (font_options, CAIRO_ANTIALIAS_GRAY);
+    cairo_set_font_options (cr, font_options);
+    cairo_font_options_destroy (font_options);
+
+    cairo_save (cr);
+    alarm (ctx->timeout);
+    status = (ctx->test->draw) (cr, ctx->test->width, ctx->test->height);
+    alarm (0);
+    cairo_restore (cr);
+
+    if (similar) {
+       cairo_pop_group_to_source (cr);
+       cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+       cairo_paint (cr);
+    }
+
+#if HAVE_MEMFAULT
+    MEMFAULT_DISABLE_FAULTS ();
+
+    /* repeat test after malloc failure injection */
+    if (ctx->malloc_failure &&
+       MEMFAULT_COUNT_FAULTS () - last_fault_count > 0 &&
+       (status == CAIRO_TEST_NO_MEMORY ||
+        cairo_status (cr) == CAIRO_STATUS_NO_MEMORY ||
+        cairo_surface_status (surface) == CAIRO_STATUS_NO_MEMORY))
+    {
+       cairo_destroy (cr);
+       cairo_surface_destroy (surface);
+       if (target->cleanup)
+           target->cleanup (closure);
+       if (ctx->thread == 0) {
+           cairo_debug_reset_static_data ();
+#if HAVE_FCFINI
+           FcFini ();
+#endif
+           if (MEMFAULT_COUNT_LEAKS () > 0) {
+               MEMFAULT_PRINT_FAULTS ();
+               MEMFAULT_PRINT_LEAKS ();
+           }
+       }
+
+       goto REPEAT;
+    }
+#endif
+
+    /* Then, check all the different ways it could fail. */
+    if (status) {
+       cairo_test_log (ctx, "Error: Function under test failed\n");
+       ret = status;
+       goto UNWIND_CAIRO;
+    }
+
+#if HAVE_MEMFAULT
+    if (MEMFAULT_COUNT_FAULTS () - last_fault_count > 0 &&
+       MEMFAULT_HAS_FAULTS ())
+    {
+       VALGRIND_PRINTF ("Unreported memfaults...");
+       MEMFAULT_PRINT_FAULTS ();
+    }
+#endif
+
+    if (target->finish_surface != NULL) {
+#if HAVE_MEMFAULT
+       /* We need to re-enable faults as most recording-surface processing
+        * is done during cairo_surface_finish().
+        */
+       MEMFAULT_CLEAR_FAULTS ();
+       last_fault_count = MEMFAULT_COUNT_FAULTS ();
+       MEMFAULT_ENABLE_FAULTS ();
+#endif
+
+       /* also check for infinite loops whilst replaying */
+       alarm (ctx->timeout);
+       status = target->finish_surface (surface);
+       alarm (0);
+
+#if HAVE_MEMFAULT
+       MEMFAULT_DISABLE_FAULTS ();
+
+       if (ctx->malloc_failure &&
+           MEMFAULT_COUNT_FAULTS () - last_fault_count > 0 &&
+           status == CAIRO_STATUS_NO_MEMORY)
+       {
+           cairo_destroy (cr);
+           cairo_surface_destroy (surface);
+           if (target->cleanup)
+               target->cleanup (closure);
+           if (ctx->thread == 0) {
+               cairo_debug_reset_static_data ();
+#if HAVE_FCFINI
+               FcFini ();
+#endif
+               if (MEMFAULT_COUNT_LEAKS () > 0) {
+                   MEMFAULT_PRINT_FAULTS ();
+                   MEMFAULT_PRINT_LEAKS ();
+               }
+           }
+
+           goto REPEAT;
+       }
+#endif
+       if (status) {
+           cairo_test_log (ctx, "Error: Failed to finish surface: %s\n",
+                           cairo_status_to_string (status));
+           ret = CAIRO_TEST_FAILURE;
+           goto UNWIND_CAIRO;
+       }
+    }
+
+    /* Skip image check for tests with no image (width,height == 0,0) */
+    if (ctx->test->width != 0 && ctx->test->height != 0) {
+       cairo_surface_t *ref_image;
+       cairo_surface_t *test_image;
+       cairo_surface_t *diff_image;
+       buffer_diff_result_t result;
+       cairo_status_t diff_status;
+
+       if (ref_png_path == NULL) {
+           cairo_test_log (ctx, "Error: Cannot find reference image for %s\n",
+                           base_name);
+
+           /* we may be running this test to generate reference images */
+           _xunlink (ctx, out_png_path);
+           /* be more generous as we may need to use external renderers */
+           alarm (4 * ctx->timeout);
+           test_image = target->get_image_surface (surface, 0,
+                                                   ctx->test->width,
+                                                   ctx->test->height);
+           alarm (0);
+           diff_status = cairo_surface_write_to_png (test_image, out_png_path);
+           cairo_surface_destroy (test_image);
+           if (diff_status) {
+               if (cairo_surface_status (test_image) == CAIRO_STATUS_INVALID_STATUS)
+                   ret = CAIRO_TEST_CRASHED;
+               else
+                   ret = CAIRO_TEST_FAILURE;
+               cairo_test_log (ctx,
+                               "Error: Failed to write output image: %s\n",
+                               cairo_status_to_string (diff_status));
+           }
+           have_output = TRUE;
+
+           ret = CAIRO_TEST_XFAILURE;
+           goto UNWIND_CAIRO;
+       }
+
+       if (target->file_extension != NULL) { /* compare vector surfaces */
+           char *filenames[] = {
+               ref_png_path,
+               ref_path,
+               new_png_path,
+               new_path,
+               xfail_png_path,
+               xfail_path,
+               base_ref_png_path,
+               base_new_png_path,
+               base_xfail_png_path,
+           };
+
+           xasprintf (&test_filename, "%s.out%s",
+                      base_path, target->file_extension);
+           xasprintf (&pass_filename, "%s.pass%s",
+                      base_path, target->file_extension);
+           xasprintf (&fail_filename, "%s.fail%s",
+                      base_path, target->file_extension);
+
+           if (cairo_test_file_is_older (pass_filename,
+                                         filenames,
+                                         ARRAY_SIZE (filenames)))
+           {
+               _xunlink (ctx, pass_filename);
+           }
+           if (cairo_test_file_is_older (fail_filename,
+                                         filenames,
+                                         ARRAY_SIZE (filenames)))
+           {
+               _xunlink (ctx, fail_filename);
+           }
+
+           if (cairo_test_files_equal (out_png_path, ref_path)) {
+               cairo_test_log (ctx, "Vector surface matches reference.\n");
+               have_output = FALSE;
+               ret = CAIRO_TEST_SUCCESS;
+               goto UNWIND_CAIRO;
+           }
+           if (cairo_test_files_equal (out_png_path, new_path)) {
+               cairo_test_log (ctx, "Vector surface matches current failure.\n");
+               have_output = FALSE;
+               ret = CAIRO_TEST_NEW;
+               goto UNWIND_CAIRO;
+           }
+           if (cairo_test_files_equal (out_png_path, xfail_path)) {
+               cairo_test_log (ctx, "Vector surface matches known failure.\n");
+               have_output = FALSE;
+               ret = CAIRO_TEST_XFAILURE;
+               goto UNWIND_CAIRO;
+           }
+
+           if (cairo_test_files_equal (test_filename, pass_filename)) {
+               /* identical output as last known PASS */
+               cairo_test_log (ctx, "Vector surface matches last pass.\n");
+               have_output = TRUE;
+               ret = CAIRO_TEST_SUCCESS;
+               goto UNWIND_CAIRO;
+           }
+           if (cairo_test_files_equal (test_filename, fail_filename)) {
+               /* identical output as last known FAIL, fail */
+               cairo_test_log (ctx, "Vector surface matches last fail.\n");
+               have_result = TRUE; /* presume these were kept around as well */
+               have_output = TRUE;
+               ret = CAIRO_TEST_FAILURE;
+               goto UNWIND_CAIRO;
+           }
+       }
+
+       /* be more generous as we may need to use external renderers */
+       alarm (4 * ctx->timeout);
+       test_image = target->get_image_surface (surface, 0,
+                                               ctx->test->width,
+                                               ctx->test->height);
+       alarm (0);
+       if (cairo_surface_status (test_image)) {
+           cairo_test_log (ctx, "Error: Failed to extract image: %s\n",
+                           cairo_status_to_string (cairo_surface_status (test_image)));
+           if (cairo_surface_status (test_image) == CAIRO_STATUS_INVALID_STATUS)
+               ret = CAIRO_TEST_CRASHED;
+           else
+               ret = CAIRO_TEST_FAILURE;
+           cairo_surface_destroy (test_image);
+           goto UNWIND_CAIRO;
+       }
+
+       _xunlink (ctx, out_png_path);
+       diff_status = cairo_surface_write_to_png (test_image, out_png_path);
+       if (diff_status) {
+           cairo_test_log (ctx, "Error: Failed to write output image: %s\n",
+                           cairo_status_to_string (diff_status));
+           cairo_surface_destroy (test_image);
+           ret = CAIRO_TEST_FAILURE;
+           goto UNWIND_CAIRO;
+       }
+       have_output = TRUE;
+
+       /* binary compare png files (no decompression) */
+       if (target->file_extension == NULL) {
+           char *filenames[] = {
+               ref_png_path,
+               new_png_path,
+               xfail_png_path,
+               base_ref_png_path,
+               base_new_png_path,
+               base_xfail_png_path,
+           };
+
+           xasprintf (&test_filename, "%s", out_png_path);
+           xasprintf (&pass_filename, "%s.pass.png", base_path);
+           xasprintf (&fail_filename, "%s.fail.png", base_path);
+
+           if (cairo_test_file_is_older (pass_filename,
+                                         filenames,
+                                         ARRAY_SIZE (filenames)))
+           {
+               _xunlink (ctx, pass_filename);
+           }
+           if (cairo_test_file_is_older (fail_filename,
+                                         filenames,
+                                         ARRAY_SIZE (filenames)))
+           {
+               _xunlink (ctx, fail_filename);
+           }
+
+           if (cairo_test_files_equal (test_filename, pass_filename)) {
+               cairo_test_log (ctx, "PNG file exactly matches last pass.\n");
+                have_result = TRUE;
+               cairo_surface_destroy (test_image);
+               ret = CAIRO_TEST_SUCCESS;
+               goto UNWIND_CAIRO;
+           }
+           if (cairo_test_files_equal (out_png_path, ref_png_path)) {
+               cairo_test_log (ctx, "PNG file exactly matches reference image.\n");
+                have_result = TRUE;
+               cairo_surface_destroy (test_image);
+               ret = CAIRO_TEST_SUCCESS;
+               goto UNWIND_CAIRO;
+           }
+           if (cairo_test_files_equal (out_png_path, new_png_path)) {
+               cairo_test_log (ctx, "PNG file exactly matches current failure image.\n");
+                have_result = TRUE;
+               cairo_surface_destroy (test_image);
+               ret = CAIRO_TEST_NEW;
+               goto UNWIND_CAIRO;
+           }
+           if (cairo_test_files_equal (out_png_path, xfail_png_path)) {
+               cairo_test_log (ctx, "PNG file exactly matches known failure image.\n");
+                have_result = TRUE;
+               cairo_surface_destroy (test_image);
+               ret = CAIRO_TEST_XFAILURE;
+               goto UNWIND_CAIRO;
+           }
+           if (cairo_test_files_equal (test_filename, fail_filename)) {
+               cairo_test_log (ctx, "PNG file exactly matches last fail.\n");
+               have_result = TRUE; /* presume these were kept around as well */
+               cairo_surface_destroy (test_image);
+               ret = CAIRO_TEST_FAILURE;
+               goto UNWIND_CAIRO;
+           }
+       } else {
+           if (cairo_test_files_equal (out_png_path, ref_png_path)) {
+               cairo_test_log (ctx, "PNG file exactly matches reference image.\n");
+               have_result = TRUE;
+               cairo_surface_destroy (test_image);
+               ret = CAIRO_TEST_SUCCESS;
+               goto UNWIND_CAIRO;
+           }
+           if (cairo_test_files_equal (out_png_path, new_png_path)) {
+               cairo_test_log (ctx, "PNG file exactly matches current failure image.\n");
+               have_result = TRUE;
+               cairo_surface_destroy (test_image);
+               ret = CAIRO_TEST_NEW;
+               goto UNWIND_CAIRO;
+           }
+           if (cairo_test_files_equal (out_png_path, xfail_png_path)) {
+               cairo_test_log (ctx, "PNG file exactly matches known failure image.\n");
+               have_result = TRUE;
+               cairo_surface_destroy (test_image);
+               ret = CAIRO_TEST_XFAILURE;
+               goto UNWIND_CAIRO;
+           }
+       }
+
+       if (cairo_test_files_equal (out_png_path, base_ref_png_path)) {
+           cairo_test_log (ctx, "PNG file exactly reference image.\n");
+           have_result = TRUE;
+           cairo_surface_destroy (test_image);
+           ret = CAIRO_TEST_SUCCESS;
+           goto UNWIND_CAIRO;
+       }
+       if (cairo_test_files_equal (out_png_path, base_new_png_path)) {
+           cairo_test_log (ctx, "PNG file exactly current failure image.\n");
+           have_result = TRUE;
+           cairo_surface_destroy (test_image);
+           ret = CAIRO_TEST_NEW;
+           goto UNWIND_CAIRO;
+       }
+       if (cairo_test_files_equal (out_png_path, base_xfail_png_path)) {
+           cairo_test_log (ctx, "PNG file exactly known failure image.\n");
+           have_result = TRUE;
+           cairo_surface_destroy (test_image);
+           ret = CAIRO_TEST_XFAILURE;
+           goto UNWIND_CAIRO;
+       }
+
+       /* first compare against the ideal reference */
+       ref_image = cairo_test_get_reference_image (ctx, base_ref_png_path,
+                                                   target->content == CAIRO_TEST_CONTENT_COLOR_ALPHA_FLATTENED);
+       if (cairo_surface_status (ref_image)) {
+           cairo_test_log (ctx, "Error: Cannot open reference image for %s: %s\n",
+                           base_ref_png_path,
+                           cairo_status_to_string (cairo_surface_status (ref_image)));
+           cairo_surface_destroy (test_image);
+           ret = CAIRO_TEST_FAILURE;
+           goto UNWIND_CAIRO;
+       }
+
+       diff_image = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
+                                                ctx->test->width,
+                                                ctx->test->height);
+
+       cmp_png_path = base_ref_png_path;
+       diff_status = image_diff (ctx,
+                                 test_image, ref_image, diff_image,
+                                 &result);
+       _xunlink (ctx, diff_png_path);
+       if (diff_status ||
+            image_diff_is_failure (&result, target->error_tolerance))
+       {
+           /* that failed, so check against the specific backend */
+           ref_image = cairo_test_get_reference_image (ctx, ref_png_path,
+                                                       target->content == CAIRO_TEST_CONTENT_COLOR_ALPHA_FLATTENED);
+           if (cairo_surface_status (ref_image)) {
+               cairo_test_log (ctx, "Error: Cannot open reference image for %s: %s\n",
+                               ref_png_path,
+                               cairo_status_to_string (cairo_surface_status (ref_image)));
+               cairo_surface_destroy (test_image);
+               ret = CAIRO_TEST_FAILURE;
+               goto UNWIND_CAIRO;
+           }
+
+           cmp_png_path = ref_png_path;
+           diff_status = image_diff (ctx,
+                                     test_image, ref_image,
+                                     diff_image,
+                                     &result);
+           if (diff_status)
+           {
+               cairo_test_log (ctx, "Error: Failed to compare images: %s\n",
+                               cairo_status_to_string (diff_status));
+               ret = CAIRO_TEST_FAILURE;
+           }
+           else if (image_diff_is_failure (&result, target->error_tolerance))
+           {
+               ret = CAIRO_TEST_FAILURE;
+
+               diff_status = cairo_surface_write_to_png (diff_image,
+                                                         diff_png_path);
+               if (diff_status) {
+                   cairo_test_log (ctx, "Error: Failed to write differences image: %s\n",
+                                   cairo_status_to_string (diff_status));
+               } else {
+                   have_result = TRUE;
+               }
+
+               cairo_test_copy_file (test_filename, fail_filename);
+           }
+           else
+           { /* success */
+               cairo_test_copy_file (test_filename, pass_filename);
+           }
+       }
+       else
+       { /* success */
+           cairo_test_copy_file (test_filename, pass_filename);
+       }
+
+       /* If failed, compare against the current image output,
+        * and attempt to detect systematic failures.
+        */
+       if (ret == CAIRO_TEST_FAILURE) {
+           char *image_out_path;
+
+           image_out_path =
+               cairo_test_reference_filename (ctx,
+                                              base_name,
+                                              ctx->test_name,
+                                              "image",
+                                              "image",
+                                              format,
+                                              CAIRO_TEST_OUT_SUFFIX,
+                                              CAIRO_TEST_PNG_EXTENSION);
+           if (image_out_path != NULL) {
+               if (cairo_test_files_equal (out_png_path,
+                                           image_out_path))
+               {
+                   ret = CAIRO_TEST_XFAILURE;
+               }
+               else
+               {
+                   ref_image =
+                       cairo_image_surface_create_from_png (image_out_path);
+                   if (cairo_surface_status (ref_image) == CAIRO_STATUS_SUCCESS)
+                   {
+                       diff_status = image_diff (ctx,
+                                                 test_image, ref_image,
+                                                 diff_image,
+                                                 &result);
+                       if (diff_status == CAIRO_STATUS_SUCCESS &&
+                           !image_diff_is_failure (&result, target->error_tolerance))
+                       {
+                           ret = CAIRO_TEST_XFAILURE;
+                       }
+
+                       cairo_surface_destroy (ref_image);
+                   }
+               }
+
+               free (image_out_path);
+           }
+       }
+
+       cairo_surface_destroy (test_image);
+       cairo_surface_destroy (diff_image);
+    }
+
+    if (cairo_status (cr) != CAIRO_STATUS_SUCCESS) {
+       cairo_test_log (ctx, "Error: Function under test left cairo status in an error state: %s\n",
+                       cairo_status_to_string (cairo_status (cr)));
+       ret = CAIRO_TEST_ERROR;
+       goto UNWIND_CAIRO;
+    }
+
+UNWIND_CAIRO:
+    if (test_filename != NULL) {
+       free (test_filename);
+       test_filename = NULL;
+    }
+    if (fail_filename != NULL) {
+       free (fail_filename);
+       fail_filename = NULL;
+    }
+    if (pass_filename != NULL) {
+       free (pass_filename);
+       pass_filename = NULL;
+    }
+
+#if HAVE_MEMFAULT
+    if (ret == CAIRO_TEST_FAILURE)
+       MEMFAULT_PRINT_FAULTS ();
+#endif
+    cairo_destroy (cr);
+UNWIND_SURFACE:
+    cairo_surface_destroy (surface);
+
+    if (target->cleanup)
+       target->cleanup (closure);
+
+#if HAVE_MEMFAULT
+    if (ctx->thread == 0) {
+       cairo_debug_reset_static_data ();
+
+#if HAVE_FCFINI
+       FcFini ();
+#endif
+
+       if (MEMFAULT_COUNT_LEAKS () > 0) {
+           if (ret != CAIRO_TEST_FAILURE)
+               MEMFAULT_PRINT_FAULTS ();
+           MEMFAULT_PRINT_LEAKS ();
+       }
+    }
+
+    if (ret == CAIRO_TEST_SUCCESS && --malloc_failure_iterations > 0)
+       goto REPEAT;
+#endif
+
+    if (ctx->thread == 0) {
+       if (have_output)
+           cairo_test_log (ctx, "OUTPUT: %s\n", out_png_path);
+
+       if (have_result) {
+           if (cmp_png_path == NULL) {
+               /* XXX presume we matched the normal ref last time */
+               cmp_png_path = ref_png_path;
+           }
+           cairo_test_log (ctx,
+                           "REFERENCE: %s\nDIFFERENCE: %s\n",
+                           cmp_png_path, diff_png_path);
+       }
+    }
+
+UNWIND_STRINGS:
+    if (out_png_path)
+      free (out_png_path);
+    if (ref_png_path)
+      free (ref_png_path);
+    if (base_ref_png_path)
+      free (base_ref_png_path);
+    if (ref_path)
+      free (ref_path);
+    if (new_png_path)
+      free (new_png_path);
+    if (base_new_png_path)
+      free (base_new_png_path);
+    if (new_path)
+      free (new_path);
+    if (xfail_png_path)
+      free (xfail_png_path);
+    if (base_xfail_png_path)
+      free (base_xfail_png_path);
+    if (xfail_path)
+      free (xfail_path);
+    if (diff_png_path)
+      free (diff_png_path);
+    if (base_path)
+      free (base_path);
+    if (base_name)
+      free (base_name);
+
+    return ret;
+}
+
+#if defined(HAVE_SIGNAL_H) && defined(HAVE_SETJMP_H)
+#include <signal.h>
+#include <setjmp.h>
+/* Used to catch crashes in a test, so that we report it as such and
+ * continue testing, although one crasher may already have corrupted memory in
+ * an nonrecoverable fashion. */
+static jmp_buf jmpbuf;
+
+static void
+segfault_handler (int signal)
+{
+    longjmp (jmpbuf, signal);
+}
+#endif
+
+cairo_test_status_t
+_cairo_test_context_run_for_target (cairo_test_context_t *ctx,
+                                   const cairo_boilerplate_target_t *target,
+                                   cairo_bool_t similar,
+                                   int dev_offset)
+{
+    cairo_test_status_t status;
+
+    if (target->get_image_surface == NULL)
+       return CAIRO_TEST_UNTESTED;
+
+    if (similar && ! cairo_test_target_has_similar (ctx, target))
+       return CAIRO_TEST_UNTESTED;
+
+    cairo_test_log (ctx,
+                   "Testing %s with %s%s target (dev offset %d)\n",
+                   ctx->test_name,
+                   similar ? " (similar) " : "",
+                   target->name,
+                   dev_offset);
+
+    if (ctx->thread == 0) {
+       printf ("%s.%s.%s [%d]%s:\t", ctx->test_name, target->name,
+               cairo_boilerplate_content_name (target->content),
+               dev_offset,
+               similar ? " (similar)": "");
+       fflush (stdout);
+    }
+
+#if defined(HAVE_SIGNAL_H) && defined(HAVE_SETJMP_H)
+    if (ctx->thread == 0 && ! RUNNING_ON_VALGRIND) {
+       void (* volatile old_segfault_handler)(int);
+       void (* volatile old_segfpe_handler)(int);
+       void (* volatile old_sigpipe_handler)(int);
+       void (* volatile old_sigabrt_handler)(int);
+       void (* volatile old_sigalrm_handler)(int);
+
+       /* Set up a checkpoint to get back to in case of segfaults. */
+#ifdef SIGSEGV
+       old_segfault_handler = signal (SIGSEGV, segfault_handler);
+#endif
+#ifdef SIGFPE
+       old_segfpe_handler = signal (SIGFPE, segfault_handler);
+#endif
+#ifdef SIGPIPE
+       old_sigpipe_handler = signal (SIGPIPE, segfault_handler);
+#endif
+#ifdef SIGABRT
+       old_sigabrt_handler = signal (SIGABRT, segfault_handler);
+#endif
+#ifdef SIGALRM
+       old_sigalrm_handler = signal (SIGALRM, segfault_handler);
+#endif
+       if (0 == setjmp (jmpbuf))
+           status = cairo_test_for_target (ctx, target, dev_offset, similar);
+       else
+           status = CAIRO_TEST_CRASHED;
+#ifdef SIGSEGV
+       signal (SIGSEGV, old_segfault_handler);
+#endif
+#ifdef SIGFPE
+       signal (SIGFPE, old_segfpe_handler);
+#endif
+#ifdef SIGPIPE
+       signal (SIGPIPE, old_sigpipe_handler);
+#endif
+#ifdef SIGABRT
+       signal (SIGABRT, old_sigabrt_handler);
+#endif
+#ifdef SIGALRM
+       signal (SIGALRM, old_sigalrm_handler);
+#endif
+    } else {
+       status = cairo_test_for_target (ctx, target, dev_offset, similar);
+    }
+#else
+    status = cairo_test_for_target (ctx, target, dev_offset, similar);
+#endif
+
+    if (ctx->thread == 0) {
+       cairo_test_log (ctx,
+               "TEST: %s TARGET: %s FORMAT: %s OFFSET: %d SIMILAR: %d RESULT: ",
+                       ctx->test_name, target->name,
+                       cairo_boilerplate_content_name (target->content),
+                       dev_offset, similar);
+       switch (status) {
+       case CAIRO_TEST_SUCCESS:
+           printf ("PASS\n");
+           cairo_test_log (ctx, "PASS\n");
+           break;
+
+       case CAIRO_TEST_UNTESTED:
+           printf ("UNTESTED\n");
+           cairo_test_log (ctx, "UNTESTED\n");
+           break;
+
+       default:
+       case CAIRO_TEST_CRASHED:
+           if (print_fail_on_stdout && ctx->thread == 0) {
+               printf ("!!!CRASHED!!!\n");
+           } else {
+               /* eat the test name */
+               printf ("\r");
+               fflush (stdout);
+           }
+           cairo_test_log (ctx, "CRASHED\n");
+           fprintf (stderr, "%s.%s.%s [%d]%s:\t%s!!!CRASHED!!!%s\n",
+                    ctx->test_name, target->name,
+                    cairo_boilerplate_content_name (target->content), dev_offset, similar ? " (similar)" : "",
+                    fail_face, normal_face);
+           break;
+
+       case CAIRO_TEST_ERROR:
+           if (print_fail_on_stdout && ctx->thread == 0) {
+               printf ("!!!ERROR!!!\n");
+           } else {
+               /* eat the test name */
+               printf ("\r");
+               fflush (stdout);
+           }
+           cairo_test_log (ctx, "ERROR\n");
+           fprintf (stderr, "%s.%s.%s [%d]%s:\t%s!!!ERROR!!!%s\n",
+                    ctx->test_name, target->name,
+                    cairo_boilerplate_content_name (target->content), dev_offset, similar ? " (similar)" : "",
+                    fail_face, normal_face);
+           break;
+
+       case CAIRO_TEST_XFAILURE:
+           if (print_fail_on_stdout && ctx->thread == 0) {
+               printf ("XFAIL\n");
+           } else {
+               /* eat the test name */
+               printf ("\r");
+               fflush (stdout);
+           }
+           fprintf (stderr, "%s.%s.%s [%d]%s:\t%sXFAIL%s\n",
+                    ctx->test_name, target->name,
+                    cairo_boilerplate_content_name (target->content), dev_offset, similar ? " (similar)" : "",
+                    xfail_face, normal_face);
+           cairo_test_log (ctx, "XFAIL\n");
+           break;
+
+       case CAIRO_TEST_NEW:
+           if (print_fail_on_stdout && ctx->thread == 0) {
+               printf ("NEW\n");
+           } else {
+               /* eat the test name */
+               printf ("\r");
+               fflush (stdout);
+           }
+           fprintf (stderr, "%s.%s.%s [%d]%s:\t%sNEW%s\n",
+                    ctx->test_name, target->name,
+                    cairo_boilerplate_content_name (target->content), dev_offset, similar ? " (similar)" : "",
+                    fail_face, normal_face);
+           cairo_test_log (ctx, "NEW\n");
+           break;
+
+       case CAIRO_TEST_NO_MEMORY:
+       case CAIRO_TEST_FAILURE:
+           if (print_fail_on_stdout && ctx->thread == 0) {
+               printf ("FAIL\n");
+           } else {
+               /* eat the test name */
+               printf ("\r");
+               fflush (stdout);
+           }
+           fprintf (stderr, "%s.%s.%s [%d]%s:\t%sFAIL%s\n",
+                    ctx->test_name, target->name,
+                    cairo_boilerplate_content_name (target->content), dev_offset, similar ? " (similar)" : "",
+                    fail_face, normal_face);
+           cairo_test_log (ctx, "FAIL\n");
+           break;
+       }
+       fflush (stdout);
+    } else {
+#if HAVE_FLOCKFILE && HAVE_FUNLOCKFILE
+       flockfile (stdout);
+#endif
+       printf ("%s.%s.%s %d [%d]:\t",
+               ctx->test_name, target->name,
+               cairo_boilerplate_content_name (target->content),
+               ctx->thread,
+               dev_offset);
+       switch (status) {
+       case CAIRO_TEST_SUCCESS:
+           printf ("PASS\n");
+           break;
+       case CAIRO_TEST_UNTESTED:
+           printf ("UNTESTED\n");
+           break;
+       default:
+       case CAIRO_TEST_CRASHED:
+           printf ("!!!CRASHED!!!\n");
+           break;
+       case CAIRO_TEST_ERROR:
+           printf ("!!!ERRORED!!!\n");
+           break;
+       case CAIRO_TEST_XFAILURE:
+           printf ("XFAIL\n");
+           break;
+       case CAIRO_TEST_NEW:
+           printf ("NEW\n");
+           break;
+       case CAIRO_TEST_NO_MEMORY:
+       case CAIRO_TEST_FAILURE:
+           printf ("FAIL\n");
+           break;
+       }
+
+       fflush (stdout);
+#if  HAVE_FLOCKFILE && HAVE_FUNLOCKFILE
+       funlockfile (stdout);
+#endif
+    }
+
+    return status;
+}
+
+cairo_test_status_t
+_cairo_test_context_run (cairo_test_context_t *ctx)
+{
+    size_t i, j;
+    cairo_test_status_t ret;
+
+    ret = CAIRO_TEST_UNTESTED;
+    if (ctx->test->preamble != NULL)
+       ret = ctx->test->preamble (ctx);
+
+    if (ctx->test->draw == NULL)
+       return ret;
+
+    /* The intended logic here is that we return overall SUCCESS
+     * iff. there is at least one tested backend and that all tested
+     * backends return SUCCESS, OR, there's backends were manually
+     * limited, and none were tested.
+     * In other words:
+     *
+     *  if      backends limited and no backend tested
+     *          -> SUCCESS
+     * else if any backend not SUCCESS
+     *         -> FAILURE
+     * else if all backends UNTESTED
+     *         -> FAILURE
+     * else    (== some backend SUCCESS)
+     *         -> SUCCESS
+     *
+     * Also, on a crash, run no further tests.
+     */
+    for (i = 0; i < ctx->num_targets && ret != CAIRO_TEST_CRASHED; i++) {
+       const cairo_boilerplate_target_t *target = ctx->targets_to_test[(i + ctx->thread) % ctx->num_targets];
+
+       for (j = 0; j < NUM_DEVICE_OFFSETS; j++) {
+           int dev_offset = ((j + ctx->thread) % NUM_DEVICE_OFFSETS) * 25;
+           cairo_test_similar_t similar, has_similar;
+
+           has_similar = cairo_test_target_has_similar (ctx, target);
+           for (similar = DIRECT; similar <= has_similar; similar++) {
+               cairo_status_t status;
+
+               status = _cairo_test_context_run_for_target (ctx,
+                                                            target,
+                                                            similar,
+                                                            dev_offset);
+               if (ret == CAIRO_TEST_UNTESTED)
+                   ret = status;
+           }
+       }
+    }
+
+    return ret;
+}
+
+#if CAIRO_HAS_REAL_PTHREAD
+typedef struct _cairo_test_thread {
+    pthread_t thread;
+    cairo_test_context_t *ctx;
+    size_t id;
+} cairo_test_thread_t;
+
+static void *
+cairo_test_run_threaded (void *closure)
+{
+    cairo_test_thread_t *arg = closure;
+    cairo_test_context_t ctx;
+    cairo_test_status_t ret;
+
+    cairo_test_init_thread (&ctx, arg->ctx, arg->id);
+
+    ret = _cairo_test_context_run (&ctx);
+
+    cairo_test_fini (&ctx);
+
+    return (void *) ret;
+}
+#endif
+
+
+static cairo_test_status_t
+cairo_test_expecting (const cairo_test_t *test)
+{
+    cairo_test_context_t ctx;
+    cairo_test_status_t ret = CAIRO_TEST_SUCCESS;
+    size_t num_threads;
+
+    _cairo_test_init (&ctx, NULL, test, test->name, CAIRO_TEST_OUTPUT_DIR);
+    printf ("%s\n", test->description);
+
+#if CAIRO_HAS_REAL_PTHREAD
+    num_threads = 0;
+    if (getenv ("CAIRO_TEST_NUM_THREADS"))
+       num_threads = atoi (getenv ("CAIRO_TEST_NUM_THREADS"));
+    if (num_threads > 1) {
+       cairo_test_thread_t *threads;
+       size_t n;
+
+       threads = xmalloc (sizeof (cairo_test_thread_t) * num_threads);
+       for (n = 0; n < num_threads; n++) {
+           threads[n].ctx = &ctx;
+           threads[n].id = n + 1;
+           pthread_create (&threads[n].thread, NULL,
+                   cairo_test_run_threaded, &threads[n]);
+       }
+       for (n = 0; n < num_threads; n++) {
+           void *tmp;
+           pthread_join (threads[n].thread, &tmp);
+           if (ret == CAIRO_TEST_SUCCESS)
+               ret = (cairo_test_status_t) tmp;
+       }
+       free (threads);
+    }
+
+    if (ret == CAIRO_TEST_SUCCESS)
+#endif
+       ret = _cairo_test_context_run (&ctx);
+
+    if (ret != CAIRO_TEST_SUCCESS)
+        printf ("Check %s%s out for more information.\n", ctx.test_name, CAIRO_TEST_LOG_SUFFIX);
+
+    cairo_test_fini (&ctx);
+
+    return ret;
+}
+
+cairo_test_status_t
+cairo_test (const cairo_test_t *test)
+{
+#ifdef _MSC_VER
+    /* We don't want an assert dialog, we want stderr */
+    _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE);
+    _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR);
+#endif
+
+    return cairo_test_expecting (test);
+}
+
+const cairo_test_context_t *
+cairo_test_get_context (cairo_t *cr)
+{
+    return cairo_get_user_data (cr, &_cairo_test_context_key);
+}
+
+cairo_surface_t *
+cairo_test_create_surface_from_png (const cairo_test_context_t *ctx,
+                                   const char *filename)
+{
+    cairo_surface_t *image;
+    cairo_status_t status;
+
+    image = cairo_image_surface_create_from_png (filename);
+    status = cairo_surface_status (image);
+    if (status == CAIRO_STATUS_FILE_NOT_FOUND) {
+        /* expect not found when running with srcdir != builddir
+         * such as when 'make distcheck' is run
+         */
+       if (ctx->srcdir) {
+           char *srcdir_filename;
+           xasprintf (&srcdir_filename, "%s/%s", ctx->srcdir, filename);
+           cairo_surface_destroy (image);
+           image = cairo_image_surface_create_from_png (srcdir_filename);
+           free (srcdir_filename);
+       }
+    }
+
+    return image;
+}
+
+cairo_pattern_t *
+cairo_test_create_pattern_from_png (const cairo_test_context_t *ctx,
+                                   const char *filename)
+{
+    cairo_surface_t *image;
+    cairo_pattern_t *pattern;
+
+    image = cairo_test_create_surface_from_png (ctx, filename);
+
+    pattern = cairo_pattern_create_for_surface (image);
+
+    cairo_pattern_set_extend (pattern, CAIRO_EXTEND_REPEAT);
+
+    cairo_surface_destroy (image);
+
+    return pattern;
+}
+
+static cairo_surface_t *
+_draw_check (int width, int height)
+{
+    cairo_surface_t *surface;
+    cairo_t *cr;
+
+    surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, 12, 12);
+    cr = cairo_create (surface);
+    cairo_surface_destroy (surface);
+
+    cairo_set_source_rgb (cr, 0.75, 0.75, 0.75); /* light gray */
+    cairo_paint (cr);
+
+    cairo_set_source_rgb (cr, 0.25, 0.25, 0.25); /* dark gray */
+    cairo_rectangle (cr, width / 2,  0, width / 2, height / 2);
+    cairo_rectangle (cr, 0, height / 2, width / 2, height / 2);
+    cairo_fill (cr);
+
+    surface = cairo_surface_reference (cairo_get_target (cr));
+    cairo_destroy (cr);
+
+    return surface;
+}
+
+void
+cairo_test_paint_checkered (cairo_t *cr)
+{
+    cairo_surface_t *check;
+
+    check = _draw_check (12, 12);
+
+    cairo_save (cr);
+    cairo_set_source_surface (cr, check, 0, 0);
+    cairo_surface_destroy (check);
+
+    cairo_pattern_set_filter (cairo_get_source (cr), CAIRO_FILTER_NEAREST);
+    cairo_pattern_set_extend (cairo_get_source (cr), CAIRO_EXTEND_REPEAT);
+    cairo_paint (cr);
+
+    cairo_restore (cr);
+}
+
+cairo_bool_t
+cairo_test_is_target_enabled (const cairo_test_context_t *ctx,
+                             const char *target)
+{
+    size_t i;
+
+    for (i = 0; i < ctx->num_targets; i++) {
+       const cairo_boilerplate_target_t *t = ctx->targets_to_test[i];
+       if (strcmp (t->name, target) == 0) {
+           /* XXX ask the target whether is it possible to run?
+            * e.g. the xlib backend could check whether it is able to connect
+            * to the Display.
+            */
+           return t->get_image_surface != NULL;
+       }
+    }
+
+    return FALSE;
+}
+
+cairo_bool_t
+cairo_test_malloc_failure (const cairo_test_context_t *ctx,
+                          cairo_status_t status)
+{
+    if (! ctx->malloc_failure)
+       return FALSE;
+
+    if (status != CAIRO_STATUS_NO_MEMORY)
+       return FALSE;
+
+#if HAVE_MEMFAULT
+    {
+       int n_faults;
+
+       /* prevent infinite loops... */
+       n_faults = MEMFAULT_COUNT_FAULTS ();
+       if (n_faults == ctx->last_fault_count)
+           return FALSE;
+
+       ((cairo_test_context_t *) ctx)->last_fault_count = n_faults;
+    }
+#endif
+
+    return TRUE;
+}
+
+cairo_test_status_t
+cairo_test_status_from_status (const cairo_test_context_t *ctx,
+                              cairo_status_t status)
+{
+    if (status == CAIRO_STATUS_SUCCESS)
+       return CAIRO_TEST_SUCCESS;
+
+    if (cairo_test_malloc_failure (ctx, status))
+       return CAIRO_TEST_NO_MEMORY;
+
+    return CAIRO_TEST_FAILURE;
+}
diff --git a/test/cairo-test.h b/test/cairo-test.h
new file mode 100644 (file)
index 0000000..dd4fcf9
--- /dev/null
@@ -0,0 +1,310 @@
+/*
+ * Copyright © 2004 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ */
+
+#ifndef _CAIRO_TEST_H_
+#define _CAIRO_TEST_H_
+
+#include "cairo-boilerplate.h"
+
+#include <stdarg.h>
+
+CAIRO_BEGIN_DECLS
+
+#if   HAVE_STDINT_H
+# include <stdint.h>
+#elif HAVE_INTTYPES_H
+# include <inttypes.h>
+#elif HAVE_SYS_INT_TYPES_H
+# include <sys/int_types.h>
+#elif defined(_MSC_VER)
+typedef __int8 int8_t;
+typedef unsigned __int8 uint8_t;
+typedef __int16 int16_t;
+typedef unsigned __int16 uint16_t;
+typedef __int32 int32_t;
+typedef unsigned __int32 uint32_t;
+typedef __int64 int64_t;
+typedef unsigned __int64 uint64_t;
+# ifndef HAVE_UINT64_T
+#  define HAVE_UINT64_T 1
+# endif
+#else
+#error Cannot find definitions for fixed-width integral types (uint8_t, uint32_t, \etc.)
+#endif
+
+#ifdef _MSC_VER
+#define _USE_MATH_DEFINES
+
+#include <float.h>
+#define isnan(x) _isnan(x)
+
+#endif
+
+#include <math.h>
+
+static inline double
+cairo_test_NaN (void)
+{
+#ifdef _MSC_VER
+    /* MSVC strtod("NaN", NULL) returns 0.0 */
+    union {
+       uint32_t i[2];
+       double d;
+    } nan = {{0xffffffff, 0x7fffffff}};
+    return nan.d;
+#else
+    return strtod("NaN", NULL);
+#endif
+}
+
+#ifndef MIN
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#endif
+
+#ifndef MAX
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+#endif
+
+#ifndef ARRAY_LENGTH
+#define ARRAY_LENGTH(__array) ((int) (sizeof (__array) / sizeof (__array[0])))
+#endif
+
+#define CAIRO_TEST_OUTPUT_DIR "output"
+
+#define CAIRO_TEST_LOG_SUFFIX ".log"
+
+#define CAIRO_TEST_FONT_FAMILY "DejaVu"
+
+/* What is a fail and what isn't?
+ * When running the test suite we want to detect unexpected output. This
+ * can be caused by a change we have made to cairo itself, or a change
+ * in our environment. To capture this we classify the expected output into 3
+ * classes:
+ *
+ *   REF  -- Perfect output.
+ *           Might be different for each backend, due to slight implementation
+ *           differences.
+ *
+ *   NEW  -- A new failure. We have uncovered a bug within cairo and have
+ *           recorded the current failure (along with the expected output
+ *           if possible!) so we can detect any changes in our attempt to
+ *           fix the bug.
+ *
+ *  XFAIL -- An external failure. We believe the cairo output is perfect,
+ *           but an external renderer is causing gross failure.
+ *           (We also use this to capture current WONTFIX issues within cairo,
+ *           such as overflow in internal coordinates, so as not to distract
+ *           us when regression testing.)
+ *
+ *  If no REF is given for a test, then it is assumed to be XFAIL.
+ */
+#define CAIRO_TEST_REF_SUFFIX ".ref"
+#define CAIRO_TEST_XFAIL_SUFFIX ".xfail"
+#define CAIRO_TEST_NEW_SUFFIX ".new"
+
+#define CAIRO_TEST_OUT_SUFFIX ".out"
+#define CAIRO_TEST_DIFF_SUFFIX ".diff"
+
+#define CAIRO_TEST_PNG_EXTENSION ".png"
+#define CAIRO_TEST_OUT_PNG CAIRO_TEST_OUT_SUFFIX CAIRO_TEST_PNG_EXTENSION
+#define CAIRO_TEST_REF_PNG CAIRO_TEST_REF_SUFFIX CAIRO_TEST_PNG_EXTENSION
+#define CAIRO_TEST_DIFF_PNG CAIRO_TEST_DIFF_SUFFIX CAIRO_TEST_PNG_EXTENSION
+
+typedef enum cairo_test_status {
+    CAIRO_TEST_SUCCESS = 0,
+    CAIRO_TEST_NO_MEMORY,
+    CAIRO_TEST_FAILURE,
+    CAIRO_TEST_NEW,
+    CAIRO_TEST_XFAILURE,
+    CAIRO_TEST_ERROR,
+    CAIRO_TEST_CRASHED,
+    CAIRO_TEST_UNTESTED = 77 /* match automake's skipped exit status */
+} cairo_test_status_t;
+
+typedef struct _cairo_test_context cairo_test_context_t;
+typedef struct _cairo_test cairo_test_t;
+
+typedef cairo_test_status_t
+(cairo_test_preamble_function_t) (cairo_test_context_t *ctx);
+
+typedef cairo_test_status_t
+(cairo_test_draw_function_t) (cairo_t *cr, int width, int height);
+
+struct _cairo_test {
+    struct _cairo_test *next;
+    const char *name;
+    const char *description;
+    const char *keywords;
+    const char *requirements;
+    double width;
+    double height;
+    cairo_test_preamble_function_t *preamble;
+    cairo_test_draw_function_t *draw;
+};
+
+/* The standard test interface which works by examining result image.
+ *
+ * CAIRO_TEST() constructs a test which will be called once before (the
+ * preamble callback), and then once for each testable backend (the draw
+ * callback). The following checks will be performed for each backend:
+ *
+ * 1) If preamble() returns CAIRO_TEST_UNTESTED, the test is skipped.
+ *
+ * 2) If preamble() does not return CAIRO_TEST_SUCCESS, the test fails.
+ *
+ * 3) If draw() does not return CAIRO_TEST_SUCCESS then this backend
+ *    fails.
+ *
+ * 4) Otherwise, if cairo_status(cr) indicates an error then this
+ *    backend fails.
+ *
+ * 5) Otherwise, if the image size is 0, then this backend passes.
+ *
+ * 6) Otherwise, if every channel of every pixel exactly matches the
+ *    reference image then this backend passes. If not, this backend
+ *    fails.
+ *
+ * The overall test result is PASS if and only if there is at least
+ * one backend that is tested and if all tested backend pass according
+ * to the four criteria above.
+ */
+#define CAIRO_TEST(name, description, keywords, requirements, width, height, preamble, draw) \
+void _register_##name (void); \
+void _register_##name (void) { \
+    static cairo_test_t test = { \
+       NULL, #name, description, \
+       keywords, requirements, \
+       width, height, \
+       preamble, draw \
+    }; \
+    cairo_test_register (&test); \
+}
+
+void
+cairo_test_register (cairo_test_t *test);
+
+/* The full context for the test.
+ * For ordinary tests (using the CAIRO_TEST()->draw interface) the context
+ * is passed to the draw routine via user_data on the cairo_t.
+ * The reason why the context is not passed as an explicit parameter is that
+ * it is rarely required by the test itself and by removing the parameter
+ * we can keep the draw routines simple and serve as example code.
+ *
+ * In contrast, for the preamble phase the context is passed as the only
+ * parameter.
+ */
+struct _cairo_test_context {
+    const cairo_test_t *test;
+    const char *test_name;
+
+    FILE *log_file;
+    const char *output;
+    const char *srcdir; /* directory containing sources and input data */
+    const char *refdir; /* directory containing reference images */
+
+    char *ref_name; /* cache of the current reference image */
+    cairo_surface_t *ref_image;
+    cairo_surface_t *ref_image_flattened;
+
+    size_t num_targets;
+    cairo_bool_t limited_targets;
+    const cairo_boilerplate_target_t **targets_to_test;
+    cairo_bool_t own_targets;
+
+    int malloc_failure;
+    int last_fault_count;
+
+    int timeout;
+
+    int thread;
+};
+
+/* Retrieve the test context from the cairo_t, used for logging, paths etc */
+const cairo_test_context_t *
+cairo_test_get_context (cairo_t *cr);
+
+
+/* Print a message to the log file, ala printf. */
+void
+cairo_test_log (const cairo_test_context_t *ctx,
+               const char *fmt, ...) CAIRO_BOILERPLATE_PRINTF_FORMAT(2, 3);
+void
+cairo_test_logv (const cairo_test_context_t *ctx,
+               const char *fmt, va_list ap) CAIRO_BOILERPLATE_PRINTF_FORMAT(2, 0);
+
+void
+cairo_test_log_path (const cairo_test_context_t *ctx,
+                    const cairo_path_t *path);
+
+/* Helper functions that take care of finding source images even when
+ * building in a non-srcdir manner, (i.e. the tests will be run in a
+ * directory that is different from the one where the source image
+ * exists). */
+cairo_surface_t *
+cairo_test_create_surface_from_png (const cairo_test_context_t *ctx,
+                                   const char *filename);
+
+cairo_pattern_t *
+cairo_test_create_pattern_from_png (const cairo_test_context_t *ctx,
+                                   const char *filename);
+
+void
+cairo_test_paint_checkered (cairo_t *cr);
+
+#define CAIRO_TEST_DOUBLE_EQUALS(a,b)  (fabs((a)-(b)) < 0.00001)
+
+cairo_bool_t
+cairo_test_is_target_enabled (const cairo_test_context_t *ctx,
+                             const char *target);
+
+char *
+cairo_test_get_name (const cairo_test_t *test);
+
+cairo_bool_t
+cairo_test_malloc_failure (const cairo_test_context_t *ctx,
+                          cairo_status_t status);
+
+cairo_test_status_t
+cairo_test_status_from_status (const cairo_test_context_t *ctx,
+                              cairo_status_t status);
+
+char *
+cairo_test_reference_filename (const cairo_test_context_t *ctx,
+                              const char *base_name,
+                              const char *test_name,
+                              const char *target_name,
+                              const char *base_target_name,
+                              const char *format,
+                              const char *suffix,
+                              const char *extension);
+
+cairo_surface_t *
+cairo_test_get_reference_image (cairo_test_context_t *ctx,
+                               const char *filename,
+                               cairo_bool_t flatten);
+
+CAIRO_END_DECLS
+
+#endif
diff --git a/test/caps-joins-alpha.c b/test/caps-joins-alpha.c
new file mode 100644 (file)
index 0000000..bbff44e
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * Copyright © 2005 Red Hat, Inc.
+ * Copyright © 2006 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ */
+
+#include "cairo-test.h"
+
+#define LINE_WIDTH     10.
+#define SIZE           (5 * LINE_WIDTH)
+#define PAD            (2 * LINE_WIDTH)
+
+static void
+make_path (cairo_t *cr)
+{
+    cairo_move_to (cr, 0., 0.);
+    cairo_rel_line_to (cr, 0., SIZE);
+    cairo_rel_line_to (cr, SIZE, 0.);
+    cairo_close_path (cr);
+
+    cairo_move_to (cr, 2 * LINE_WIDTH, 0.);
+    cairo_rel_line_to (cr, 3 * LINE_WIDTH, 0.);
+    cairo_rel_line_to (cr, 0., 3 * LINE_WIDTH);
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    /* First draw a checkered background */
+    cairo_test_paint_checkered (cr);
+
+    /* Then draw the original caps-joins test but with a bit of alphs thrown in. */
+    cairo_set_line_width (cr, LINE_WIDTH);
+
+    cairo_set_source_rgba (cr, 1.0, 0.0, 0.0, 0.5); /* 50% red */
+    cairo_translate (cr, PAD, PAD);
+
+    make_path (cr);
+    cairo_set_line_cap (cr, CAIRO_LINE_CAP_BUTT);
+    cairo_set_line_join (cr, CAIRO_LINE_JOIN_BEVEL);
+    cairo_stroke (cr);
+
+    cairo_set_source_rgba (cr, 0.0, 1.0, 0.0, 0.5); /* 50% green */
+    cairo_translate (cr, SIZE + PAD, 0.);
+
+    make_path (cr);
+    cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
+    cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND);
+    cairo_stroke (cr);
+
+    cairo_set_source_rgba (cr, 0.0, 0.0, 1.0, 0.5); /* 50% blue */
+
+    cairo_translate (cr, SIZE + PAD, 0.);
+
+    make_path (cr);
+    cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE);
+    cairo_set_line_join (cr, CAIRO_LINE_JOIN_MITER);
+    cairo_stroke (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (caps_joins_alpha,
+           "Test caps and joins with some source alpha",
+           "stroke", /* keywords */
+           NULL, /* requirements */
+           3 * (PAD + SIZE) + PAD,
+           PAD + SIZE + PAD,
+           NULL, draw)
diff --git a/test/caps-joins-curve.c b/test/caps-joins-curve.c
new file mode 100644 (file)
index 0000000..1b2fc7f
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * Copyright © 2008 Chris Wilson
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Chris Wilson not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Chris Wilson makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * CHRIS WILSON DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL CHRIS WILSON BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+
+#define LINE_WIDTH     10.
+#define SIZE           (5 * LINE_WIDTH)
+#define PAD            (3 * LINE_WIDTH)
+
+static void
+make_path (cairo_t *cr)
+{
+    cairo_move_to (cr, 0, 0);
+    cairo_rel_curve_to (cr,
+                       -SIZE/4, SIZE/3,
+                       -SIZE/4, SIZE/3,
+                       0, SIZE);
+    cairo_rel_curve_to (cr,
+                       SIZE/3, -SIZE/4,
+                       SIZE/3, -SIZE/4,
+                       SIZE, 0);
+    cairo_close_path (cr);
+
+    cairo_move_to (cr, 5 * LINE_WIDTH, 3 * LINE_WIDTH);
+    cairo_rel_curve_to (cr,
+                       0, -3 * LINE_WIDTH,
+                       0, -3 * LINE_WIDTH,
+                       -3 * LINE_WIDTH, -3 * LINE_WIDTH);
+}
+
+static void
+draw_caps_joins (cairo_t *cr)
+{
+    cairo_save (cr);
+
+    cairo_translate (cr, PAD, PAD);
+
+    make_path (cr);
+    cairo_set_line_cap (cr, CAIRO_LINE_CAP_BUTT);
+    cairo_set_line_join (cr, CAIRO_LINE_JOIN_BEVEL);
+    cairo_stroke (cr);
+
+    cairo_translate (cr, SIZE + PAD, 0.);
+
+    make_path (cr);
+    cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
+    cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND);
+    cairo_stroke (cr);
+
+    cairo_translate (cr, SIZE + PAD, 0.);
+
+    make_path (cr);
+    cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE);
+    cairo_set_line_join (cr, CAIRO_LINE_JOIN_MITER);
+    cairo_stroke (cr);
+
+    cairo_restore (cr);
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    /* We draw in the default black, so paint white first. */
+    cairo_save (cr);
+    cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); /* white */
+    cairo_paint (cr);
+    cairo_restore (cr);
+
+    cairo_set_line_width (cr, LINE_WIDTH);
+
+    draw_caps_joins (cr);
+
+    /* and reflect to generate the opposite vertex ordering */
+    cairo_translate (cr, 0, height);
+    cairo_scale (cr, 1, -1);
+
+    draw_caps_joins (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (caps_joins_curve,
+           "Test caps and joins on curves",
+           "stroke cap join", /* keywords */
+           NULL, /* requirements */
+           3 * (PAD + SIZE) + PAD,
+           2 * (PAD + SIZE) + PAD,
+           NULL, draw)
+
diff --git a/test/caps-joins.c b/test/caps-joins.c
new file mode 100644 (file)
index 0000000..9d1c2a8
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ */
+
+#include "cairo-test.h"
+
+#define LINE_WIDTH     10.
+#define SIZE           (5 * LINE_WIDTH)
+#define PAD            (2 * LINE_WIDTH)
+
+static void
+make_path (cairo_t *cr)
+{
+    cairo_move_to (cr, 0., 0.);
+    cairo_rel_line_to (cr, 0., SIZE);
+    cairo_rel_line_to (cr, SIZE, 0.);
+    cairo_close_path (cr);
+
+    cairo_move_to (cr, 5 * LINE_WIDTH, 3 * LINE_WIDTH);
+    cairo_rel_line_to (cr, 0., -3 * LINE_WIDTH);
+    cairo_rel_line_to (cr, -3 * LINE_WIDTH, 0.);
+}
+
+static void
+draw_caps_joins (cairo_t *cr)
+{
+    cairo_save (cr);
+
+    cairo_translate (cr, PAD, PAD);
+
+    make_path (cr);
+    cairo_set_line_cap (cr, CAIRO_LINE_CAP_BUTT);
+    cairo_set_line_join (cr, CAIRO_LINE_JOIN_BEVEL);
+    cairo_stroke (cr);
+
+    cairo_translate (cr, SIZE + PAD, 0.);
+
+    make_path (cr);
+    cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
+    cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND);
+    cairo_stroke (cr);
+
+    cairo_translate (cr, SIZE + PAD, 0.);
+
+    make_path (cr);
+    cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE);
+    cairo_set_line_join (cr, CAIRO_LINE_JOIN_MITER);
+    cairo_stroke (cr);
+
+    cairo_restore (cr);
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    /* We draw in the default black, so paint white first. */
+    cairo_save (cr);
+    cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); /* white */
+    cairo_paint (cr);
+    cairo_restore (cr);
+
+    cairo_set_line_width (cr, LINE_WIDTH);
+
+    draw_caps_joins (cr);
+
+    /* and reflect to generate the opposite vertex ordering */
+    cairo_translate (cr, 0, height);
+    cairo_scale (cr, 1, -1);
+
+    draw_caps_joins (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (caps_joins,
+           "Test caps and joins",
+           "stroke", /* keywords */
+           NULL, /* requirements */
+           3 * (PAD + SIZE) + PAD,
+           2 * (PAD + SIZE) + PAD,
+           NULL, draw)
+
diff --git a/test/caps-sub-paths.c b/test/caps-sub-paths.c
new file mode 100644 (file)
index 0000000..2310eb7
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ */
+
+#include "cairo-test.h"
+
+/* Test case for bug #4205:
+
+   https://bugs.freedesktop.org/show_bug.cgi?id=4205
+*/
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    /* We draw in the default black, so paint white first. */
+    cairo_save (cr);
+    cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); /* white */
+    cairo_paint (cr);
+    cairo_restore (cr);
+
+    cairo_set_line_width (cr, 4);
+    cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
+
+    cairo_move_to (cr,  4,  4);
+    cairo_line_to (cr,  4, 16);
+
+    cairo_move_to (cr, 10,  4);
+    cairo_line_to (cr, 10, 16);
+
+    cairo_move_to (cr, 16,  4);
+    cairo_line_to (cr, 16, 16);
+
+    cairo_stroke (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (caps_sub_paths,
+           "Test that sub-paths receive caps.",
+           "stroke", /* keywords */
+           NULL, /* requirements */
+           20, 20,
+           NULL, draw)
+
diff --git a/test/caps-tails-curve.c b/test/caps-tails-curve.c
new file mode 100644 (file)
index 0000000..0dc8b31
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ * Copyright © 2011 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+
+#define LINE_WIDTH     30.
+#define SIZE           (2 * LINE_WIDTH)
+#define PAD            (2 * LINE_WIDTH)
+
+static void
+make_path (cairo_t *cr, double theta)
+{
+    double line_width = cairo_get_line_width (cr) / 4;
+
+    cairo_move_to (cr, 0, 0);
+    cairo_rel_curve_to (cr,
+                       SIZE/3, -SIZE/4,
+                       SIZE/3, -SIZE/4,
+                       SIZE, 0);
+
+    cairo_rel_line_to (cr,
+                      cos (theta) * line_width,
+                      sin (theta) * line_width);
+}
+
+static void
+draw_joins (cairo_t *cr, double theta)
+{
+    make_path (cr, theta);
+    cairo_set_line_join (cr, CAIRO_LINE_JOIN_BEVEL);
+    cairo_stroke (cr);
+
+    cairo_translate (cr, SIZE + PAD, 0.);
+
+    make_path (cr, theta);
+    cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND);
+    cairo_stroke (cr);
+
+    cairo_translate (cr, SIZE + PAD, 0.);
+
+    make_path (cr, theta);
+    cairo_set_line_join (cr, CAIRO_LINE_JOIN_MITER);
+    cairo_stroke (cr);
+
+    cairo_translate (cr, SIZE + PAD, 0.);
+}
+
+static void
+draw_caps_joins (cairo_t *cr, double theta)
+{
+    cairo_translate (cr, PAD, 0);
+    cairo_set_line_cap (cr, CAIRO_LINE_CAP_BUTT);
+    draw_joins (cr, theta);
+
+    cairo_translate (cr, PAD, 0);
+    cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
+    draw_joins (cr, theta);
+
+    cairo_translate (cr, PAD, 0);
+    cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE);
+    draw_joins (cr, theta);
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    const double theta[] = {
+       -M_PI/2, -M_PI/4, 0, M_PI/8, M_PI/3, M_PI
+    };
+    unsigned int t;
+
+    /* We draw in the default black, so paint white first. */
+    cairo_save (cr);
+    cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); /* white */
+    cairo_paint (cr);
+    cairo_restore (cr);
+
+    cairo_set_line_width (cr, LINE_WIDTH);
+
+    for (t = 0; t < sizeof(theta)/sizeof (theta[0]); t++) {
+       cairo_save (cr);
+       cairo_translate (cr, 0, t * (SIZE + PAD) + PAD);
+       draw_caps_joins (cr, theta[t]);
+       cairo_restore (cr);
+
+       cairo_save (cr);
+       /* and reflect to generate the opposite vertex ordering */
+       cairo_translate (cr, 0, height - t * (SIZE + PAD) - PAD);
+       cairo_scale (cr, 1, -1);
+       draw_caps_joins (cr, theta[t]);
+       cairo_restore (cr);
+    }
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (caps_tails_curve,
+           "Test caps and joins on short tail segments",
+           "stroke cap join", /* keywords */
+           NULL, /* requirements */
+           9 * (PAD + SIZE) + 4*PAD,
+           12 * (PAD + SIZE) + PAD,
+           NULL, draw)
+
diff --git a/test/caps.c b/test/caps.c
new file mode 100644 (file)
index 0000000..7f56117
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * Copyright © 2008 Chris Wilson
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Chris Wilson not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Chris Wilson makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * CHRIS WILSON DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL CHRIS WILSON BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+
+#define LINE_WIDTH     10.
+#define SIZE           (5 * LINE_WIDTH)
+#define PAD            (2 * LINE_WIDTH)
+
+static void
+make_path (cairo_t *cr)
+{
+    int i;
+
+    cairo_save (cr);
+    for (i = 0; i <= 3; i++) {
+       cairo_new_sub_path (cr);
+       cairo_move_to (cr, -SIZE / 2, 0.);
+       cairo_line_to (cr,  SIZE / 2, 0.);
+       cairo_rotate (cr, M_PI / 4.);
+    }
+    cairo_restore (cr);
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_save (cr);
+    cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); /* white */
+    cairo_paint (cr);
+    cairo_restore (cr);
+
+    cairo_set_line_width (cr, LINE_WIDTH);
+    cairo_translate (cr, PAD + SIZE / 2., PAD + SIZE / 2.);
+
+    cairo_set_line_cap (cr, CAIRO_LINE_CAP_BUTT);
+    cairo_set_line_join (cr, CAIRO_LINE_JOIN_BEVEL);
+    make_path (cr);
+    cairo_stroke (cr);
+
+    cairo_translate (cr, 0, SIZE + PAD);
+
+    cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
+    cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND);
+    make_path (cr);
+    cairo_stroke (cr);
+
+    cairo_translate (cr, 0, SIZE + PAD);
+
+    cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE);
+    cairo_set_line_join (cr, CAIRO_LINE_JOIN_MITER);
+    make_path (cr);
+    cairo_stroke (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (caps,
+           "Test caps",
+           "stroke caps", /* keywords */
+           NULL, /* requirements */
+           PAD + SIZE + PAD,
+           3 * (PAD + SIZE) + PAD,
+           NULL, draw)
+
diff --git a/test/checkerboard.c b/test/checkerboard.c
new file mode 100644 (file)
index 0000000..aeb2efe
--- /dev/null
@@ -0,0 +1,48 @@
+/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/*
+ * Copyright 2011 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+/* Test the basic background used extensively in the test suite. */
+
+#include "cairo-test.h"
+
+#define HEIGHT 32
+#define WIDTH 32
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_test_paint_checkered (cr);
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (checkerboard,
+           "Tests the checkerboard background",
+           "paint", /* keywords */
+           NULL, /* requirements */
+           WIDTH, HEIGHT,
+           NULL, draw)
+
diff --git a/test/clear-source.c b/test/clear-source.c
new file mode 100644 (file)
index 0000000..5410932
--- /dev/null
@@ -0,0 +1,169 @@
+/*
+ * Copyright 2009 Benjamin Otte
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Benjamin Otte <otte@gnome.org>
+ */
+
+#include "cairo-test.h"
+
+typedef enum {
+  CLEAR,
+  CLEARED,
+  PAINTED
+} surface_type_t;
+
+#define SIZE 10
+#define SPACE 5
+
+static cairo_surface_t *
+create_surface (cairo_t *target, cairo_content_t content, surface_type_t type)
+{
+    cairo_surface_t *surface;
+    cairo_t *cr;
+
+    surface = cairo_surface_create_similar (cairo_get_target (target),
+                                           content,
+                                           SIZE, SIZE);
+
+    if (type == CLEAR)
+       return surface;
+
+    cr = cairo_create (surface);
+    cairo_surface_destroy (surface);
+
+    cairo_set_source_rgb (cr, 0.75, 0, 0);
+    cairo_paint (cr);
+
+    if (type == PAINTED)
+       goto DONE;
+
+    cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
+    cairo_paint (cr);
+
+DONE:
+    surface = cairo_surface_reference (cairo_get_target (cr));
+    cairo_destroy (cr);
+
+    return surface;
+}
+
+static void
+paint (cairo_t *cr, cairo_surface_t *surface)
+{
+    cairo_set_source_surface (cr, surface, 0, 0);
+    cairo_paint (cr);
+}
+
+static void
+fill (cairo_t *cr, cairo_surface_t *surface)
+{
+    cairo_set_source_surface (cr, surface, 0, 0);
+    cairo_rectangle (cr, -SPACE, -SPACE, SIZE + 2 * SPACE, SIZE + 2 * SPACE);
+    cairo_fill (cr);
+}
+
+static void
+stroke (cairo_t *cr, cairo_surface_t *surface)
+{
+    cairo_set_source_surface (cr, surface, 0, 0);
+    cairo_set_line_width (cr, 2.0);
+    cairo_rectangle (cr, 1, 1, SIZE - 2, SIZE - 2);
+    cairo_stroke (cr);
+}
+
+static void
+mask (cairo_t *cr, cairo_surface_t *surface)
+{
+    cairo_set_source_rgb (cr, 0, 0, 0.75);
+    cairo_mask_surface (cr, surface, 0, 0);
+}
+
+static void
+mask_self (cairo_t *cr, cairo_surface_t *surface)
+{
+    cairo_set_source_surface (cr, surface, 0, 0);
+    cairo_mask_surface (cr, surface, 0, 0);
+}
+
+static void
+glyphs (cairo_t *cr, cairo_surface_t *surface)
+{
+    cairo_set_source_surface (cr, surface, 0, 0);
+    cairo_select_font_face (cr,
+                           "@cairo:",
+                           CAIRO_FONT_SLANT_NORMAL,
+                           CAIRO_FONT_WEIGHT_NORMAL);
+    cairo_set_font_size (cr, 16);
+    cairo_translate (cr, 0, SIZE);
+    cairo_show_text (cr, "C");
+}
+
+typedef void (* operation_t) (cairo_t *cr, cairo_surface_t *surface);
+static operation_t operations[] = {
+    paint,
+    fill,
+    stroke,
+    mask,
+    mask_self,
+    glyphs
+};
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_content_t contents[] = { CAIRO_CONTENT_COLOR_ALPHA, CAIRO_CONTENT_COLOR, CAIRO_CONTENT_ALPHA };
+    unsigned int content, type, ops;
+
+    cairo_set_source_rgb (cr, 0.5, 0.5, 0.5);
+    cairo_paint (cr);
+    cairo_translate (cr, SPACE, SPACE);
+
+    for (type = 0; type <= PAINTED; type++) {
+       for (content = 0; content < ARRAY_LENGTH (contents); content++) {
+           cairo_surface_t *surface;
+
+           surface = create_surface (cr, contents[content], type);
+
+            cairo_save (cr);
+            for (ops = 0; ops < ARRAY_LENGTH (operations); ops++) {
+                cairo_save (cr);
+                operations[ops] (cr, surface);
+                cairo_restore (cr);
+                cairo_translate (cr, 0, SIZE + SPACE);
+            }
+            cairo_restore (cr);
+            cairo_translate (cr, SIZE + SPACE, 0);
+
+           cairo_surface_destroy (surface);
+        }
+    }
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (clear_source,
+           "Check painting with cleared surfaces works as expected",
+           NULL, /* keywords */
+           NULL, /* requirements */
+           (SIZE + SPACE) * 9 + SPACE, ARRAY_LENGTH (operations) * (SIZE + SPACE) + SPACE,
+           NULL, draw)
diff --git a/test/clear.c b/test/clear.c
new file mode 100644 (file)
index 0000000..696993d
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * Copyright © 2009 Chris Wilson
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Chris Wilson not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Chris Wilson makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * CHRIS WILSON DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL CHRIS WILSON BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_text_extents_t extents;
+
+    cairo_set_source_rgb (cr, 0, 0, 1);
+    cairo_paint (cr);
+
+    cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
+
+    cairo_translate (cr, 2, 2);
+    cairo_save (cr);
+    cairo_rectangle (cr, 0, 0, 20, 20);
+    cairo_clip (cr);
+    cairo_rectangle (cr, 5, 5, 10, 10);
+    cairo_fill (cr);
+    cairo_restore (cr);
+
+    cairo_translate (cr, 20, 0);
+    cairo_save (cr);
+    cairo_rectangle (cr, 0, 0, 20, 20);
+    cairo_clip (cr);
+    cairo_arc (cr, 10, 10, 8, 0, 2*M_PI);
+    cairo_fill (cr);
+    cairo_restore (cr);
+
+    cairo_translate (cr, 0, 20);
+    cairo_save (cr);
+    cairo_rectangle (cr, 0, 0, 20, 20);
+    cairo_clip (cr);
+    cairo_text_extents (cr, "Cairo", &extents);
+    cairo_move_to (cr,
+                  10 - (extents.width/2. + extents.x_bearing),
+                  10 - (extents.height/2. + extents.y_bearing));
+    cairo_text_path (cr, "Cairo");
+    cairo_fill (cr);
+    cairo_restore (cr);
+
+    cairo_translate (cr, -20, 0);
+    cairo_save (cr);
+    cairo_rectangle (cr, 0, 0, 20, 20);
+    cairo_clip (cr);
+    cairo_move_to (cr, 10, 2);
+    cairo_line_to (cr, 18, 18);
+    cairo_line_to (cr, 2, 18);
+    cairo_close_path (cr);
+    cairo_fill (cr);
+    cairo_restore (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (clear,
+           "Test masked clears",
+           "paint, clear", /* keywords */
+           NULL, /* requirements */
+           44, 44,
+           NULL, draw)
+
diff --git a/test/clip-all.c b/test/clip-all.c
new file mode 100644 (file)
index 0000000..dc216c9
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * Copyright © 2005 Novell, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Novell, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Novell, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * NOVELL, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL NOVELL, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Radek Doulík <rodo@novell.com>
+ */
+
+#include "cairo-test.h"
+
+#define SIZE 10
+#define CLIP_SIZE 2
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_rectangle (cr, 0, 0, SIZE, SIZE);
+    cairo_set_source_rgb (cr, 0, 0, 1);
+    cairo_fill (cr);
+
+    cairo_reset_clip (cr);
+    cairo_rectangle (cr, CLIP_SIZE, CLIP_SIZE, CLIP_SIZE, CLIP_SIZE);
+    cairo_clip (cr);
+    cairo_rectangle (cr, 3*CLIP_SIZE, 3*CLIP_SIZE, CLIP_SIZE, CLIP_SIZE);
+    cairo_clip (cr);
+
+    cairo_translate (cr, .5, .5);
+
+    cairo_reset_clip (cr);
+    cairo_rectangle (cr, CLIP_SIZE, CLIP_SIZE, CLIP_SIZE, CLIP_SIZE);
+    cairo_clip (cr);
+    cairo_rectangle (cr, 3*CLIP_SIZE, 3*CLIP_SIZE, CLIP_SIZE, CLIP_SIZE);
+    cairo_clip (cr);
+
+    cairo_rectangle (cr, 0, 0, SIZE, SIZE);
+    cairo_set_source_rgb (cr, 1, 1, 0);
+    cairo_fill (cr);
+
+    /* https://bugs.freedesktop.org/show_bug.cgi?id=13084 */
+    cairo_select_font_face (cr,
+                           CAIRO_TEST_FONT_FAMILY " Sans",
+                           CAIRO_FONT_SLANT_NORMAL,
+                           CAIRO_FONT_WEIGHT_NORMAL);
+
+    cairo_move_to (cr, 0., SIZE);
+    cairo_show_text (cr, "cairo");
+
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (clip_all,
+           "Test clipping with everything clipped out",
+           "clip", /* keywords */
+           NULL, /* requirements */
+           SIZE, SIZE,
+           NULL, draw)
+
diff --git a/test/clip-complex-shape.c b/test/clip-complex-shape.c
new file mode 100644 (file)
index 0000000..d7d5301
--- /dev/null
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2011 SCore Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Taekyun Kim <podain77@gmail.com>
+ */
+
+#include "cairo-test.h"
+
+static void
+rounded_rectangle(cairo_t *cr,
+                 double x, double y,
+                 double width, double height,
+                 double radius)
+{
+    cairo_move_to   (cr, x, y + radius);
+    cairo_line_to   (cr, x, y + height - radius);
+    cairo_curve_to  (cr, x, y + height - radius/2.0,
+                         x + radius/2.0, y + height,
+                         x + radius, y + height);
+    cairo_line_to   (cr, x + width - radius, y + height);
+    cairo_curve_to  (cr, x + width - radius/2.0, y + height,
+                         x + width, y + height - radius/2.0,
+                         x + width, y + height - radius);
+    cairo_line_to   (cr, x + width, y + radius);
+    cairo_curve_to  (cr, x + width, y + radius/2.0,
+                         x + width - radius/2.0, y,
+                         x + width - radius, y);
+    cairo_line_to   (cr, x + radius, y);
+    cairo_curve_to  (cr, x + radius/2.0, y, x, y + radius/2.0, x, y + radius);
+    cairo_close_path(cr);
+}
+
+static void
+background (cairo_t *cr)
+{
+    cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
+    cairo_set_source_rgba(cr, 1.0, 1.0, 1.0, 1.0);
+    cairo_paint(cr);
+}
+
+static void
+foreground (cairo_t *cr)
+{
+    cairo_set_source_rgba(cr, 1.0, 0.0, 0.0, 1.0);
+    cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
+    cairo_rectangle(cr, 20, 20, 60, 60);
+    cairo_fill(cr);
+}
+
+static cairo_test_status_t
+clip_eo_mono (cairo_t *cr, int width, int height)
+{
+
+    background (cr);
+
+    cairo_set_antialias(cr, CAIRO_ANTIALIAS_NONE);
+    cairo_set_fill_rule(cr, CAIRO_FILL_RULE_EVEN_ODD);
+    rounded_rectangle(cr, 0, 0, 40, 100, 10);
+    rounded_rectangle(cr, 60, 0, 40, 100, 10);
+    cairo_clip(cr);
+
+    foreground (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+static cairo_test_status_t
+clip_eo_aa (cairo_t *cr, int width, int height)
+{
+    background (cr);
+
+    cairo_set_antialias(cr, CAIRO_ANTIALIAS_DEFAULT);
+    cairo_set_fill_rule(cr, CAIRO_FILL_RULE_EVEN_ODD);
+    rounded_rectangle(cr, 0, 0, 40, 100, 10);
+    rounded_rectangle(cr, 60, 0, 40, 100, 10);
+    cairo_clip(cr);
+
+    foreground (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (clip_complex_shape_eo_mono,
+           "Test clipping against a complex shape",
+           "clip", /* keywords */
+           NULL, /* requirements */
+           100, 100,
+           NULL, clip_eo_mono)
+CAIRO_TEST (clip_complex_shape_eo_aa,
+           "Test clipping against a complex shape",
+           "clip", /* keywords */
+           NULL, /* requirements */
+           100, 100,
+           NULL, clip_eo_aa)
diff --git a/test/clip-contexts.c b/test/clip-contexts.c
new file mode 100644 (file)
index 0000000..39d2004
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * Copyright © 2010 Intel Corporation
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Intel Corporation not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Intel Corporation makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * INTEL CORPORATION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL INTEL CORPORATION BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+
+/*
+ * Jeff Muizelaar found a bug on Quartz with cairo-surface-clipper, which was
+ * the topmost clip path from two different contexts and finding them equally
+ * incorrectly concluding that the operation was a no-op.
+ */
+
+#define SIZE 10
+#define CLIP_SIZE 2
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_t *cr2;
+
+    /* opaque background */
+    cairo_set_source_rgb (cr, 0, 0, 0);
+    cairo_paint (cr);
+
+    /* first create an empty, non-overlappiny clip */
+    cr2 = cairo_create (cairo_get_target (cr));
+    cairo_rectangle (cr2, 0, 0, SIZE/2-2, SIZE/2-2);
+    cairo_clip (cr2);
+
+    cairo_rectangle (cr2, SIZE/2+2, SIZE/2+2, SIZE/2-2, SIZE/2-2);
+    cairo_clip (cr2);
+
+    /* and apply the clip onto the surface, empty nothing should be painted */
+    cairo_set_source_rgba (cr2, 1, 0, 0, .5);
+    cairo_paint (cr2);
+
+    /* switch back to the original, and set only the last clip */
+    cairo_rectangle (cr, SIZE/2+2, SIZE/2+2, SIZE/2-2, SIZE/2-2);
+    cairo_clip (cr);
+
+    cairo_set_source_rgba (cr, 0, 0, 1, .5);
+    cairo_paint (cr);
+
+    cairo_destroy (cr2);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (clip_contexts,
+           "Test clipping with 2 separate contexts",
+           "clip", /* keywords */
+           NULL, /* requirements */
+           SIZE, SIZE,
+           NULL, draw)
diff --git a/test/clip-device-offset.c b/test/clip-device-offset.c
new file mode 100644 (file)
index 0000000..f725b8e
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * Copyright © 2009 Benjamin Otte
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Authors: Benjamin Otte <otte@gnome.org>
+ */
+
+#include "cairo-test.h"
+
+#define WIDTH 50
+#define HEIGHT 50
+
+static cairo_pattern_t *
+create_green_source (void)
+{
+  cairo_surface_t *image;
+  cairo_pattern_t *pattern;
+  cairo_t *cr;
+
+  image = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, WIDTH, HEIGHT);
+  cr = cairo_create (image);
+  cairo_surface_destroy (image);
+
+  cairo_set_source_rgb (cr, 0, 1, 0);
+  cairo_paint (cr);
+
+  pattern = cairo_pattern_create_for_surface (cairo_get_target (cr));
+  cairo_destroy (cr);
+
+  return pattern;
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+  cairo_pattern_t *source;
+  double old_x, old_y;
+
+  cairo_surface_get_device_offset (cairo_get_group_target (cr), &old_x, &old_y);
+  cairo_surface_set_device_offset (cairo_get_group_target (cr), old_x+5, old_y+5);
+
+  source = create_green_source ();
+  cairo_set_source (cr, source);
+  cairo_pattern_destroy (source);
+
+  cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+  cairo_rectangle (cr, 0, 0, WIDTH, HEIGHT);
+  cairo_clip (cr);
+  cairo_paint (cr);
+
+  cairo_surface_set_device_offset (cairo_get_group_target (cr), old_x, old_y);
+
+  return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (clip_device_offset,
+           "Test clipping on surfaces with device offsets",
+           "clip", /* keywords */
+           NULL, /* requirements */
+           WIDTH+10, HEIGHT+10,
+           NULL, draw)
diff --git a/test/clip-disjoint-hatching.c b/test/clip-disjoint-hatching.c
new file mode 100644 (file)
index 0000000..8626e80
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ * Copyright © 2011 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+
+#define STEP   5
+#define WIDTH  100
+#define HEIGHT 100
+
+static void hatching (cairo_t *cr)
+{
+    int i;
+
+    cairo_rectangle (cr, 0, 0, WIDTH, HEIGHT);
+    cairo_clip (cr);
+
+    cairo_translate (cr, WIDTH/2, HEIGHT/2);
+    cairo_rotate (cr, M_PI/4);
+    cairo_translate (cr, -WIDTH/2, -HEIGHT/2);
+
+    for (i = 0; i < WIDTH; i += STEP) {
+       cairo_rectangle (cr, i, -2, 1, HEIGHT+4);
+       cairo_rectangle (cr, -2, i, WIDTH+4, 1);
+    }
+}
+
+static void background (cairo_t *cr)
+{
+    cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+    cairo_set_source_rgb (cr, 1,1,1);
+    cairo_paint (cr);
+}
+
+static void clip_to_grid (cairo_t *cr)
+{
+    int i, j;
+
+    for (j = 0; j < HEIGHT; j += 2*STEP) {
+       for (i = 0; i < WIDTH; i += 2*STEP)
+           cairo_rectangle (cr, i, j, STEP, STEP);
+
+       j += 2*STEP;
+       for (i = 0; i < WIDTH; i += 2*STEP)
+           cairo_rectangle (cr, i+STEP/2, j, STEP, STEP);
+    }
+
+    cairo_clip (cr);
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    background (cr);
+
+    cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
+
+    cairo_save (cr); {
+       clip_to_grid (cr);
+       hatching (cr);
+       cairo_set_source_rgb (cr, 1, 0, 0);
+       cairo_fill (cr);
+    } cairo_restore (cr);
+
+    cairo_translate (cr, 0.25, HEIGHT+.25);
+
+    cairo_save (cr); {
+       clip_to_grid (cr);
+       hatching (cr);
+       cairo_set_source_rgb (cr, 0, 0, 1);
+       cairo_fill (cr);
+    } cairo_restore (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (clip_disjoint_hatching,
+           "Test drawing through through an array of clips",
+           "clip", /* keywords */
+           "target=raster", /* requirements */
+           WIDTH, 2*HEIGHT,
+           NULL, draw)
diff --git a/test/clip-disjoint.c b/test/clip-disjoint.c
new file mode 100644 (file)
index 0000000..28bb963
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * Copyright © 2008 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Soren Sandmann <sandmann@redhat.com>
+ */
+
+#include "cairo-test.h"
+
+#define WIDTH 300
+#define HEIGHT 300
+
+typedef struct {
+    double x, y;
+} point_t;
+
+static void
+paint_curve (cairo_t *cr)
+{
+    const point_t points[] = {
+       { 100, 320 }, { 110, -80 },
+       { 180, 60 }, { 300, 170 },
+       { 300, -40 }
+    };
+    unsigned i;
+
+    cairo_set_line_width (cr, 2);
+    cairo_move_to (cr, points[0].x, points[0].y);
+
+    for (i = 1; i < ARRAY_LENGTH (points) - 2; i += 3) {
+       cairo_curve_to (cr,
+                       points[i].x, points[i].y,
+                       points[i + 1].x, points[i + 1].y,
+                       points[i + 2].x, points[i + 2].y);
+    }
+    cairo_set_line_width (cr, 5);
+    cairo_stroke (cr);
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    /* Fill window with light blue */
+    cairo_set_source_rgba (cr, 0.8, 0.8, 1.9, 1.0);
+    cairo_paint (cr);
+
+    /* Paint curve in green */
+    cairo_set_source_rgba (cr, 0.6, 0.8, 0.6, 1.0);
+    paint_curve (cr);
+
+    /* Make clip region */
+    cairo_rectangle (cr, 228, 131, 50, 13);
+    cairo_rectangle (cr, 20, 99, 200, 75);
+    cairo_clip_preserve (cr);
+
+    /* Fill clip region with red */
+    cairo_set_source_rgba (cr, 1.0, 0.5, 0.5, 0.8);
+    cairo_fill (cr);
+
+    /* Paint curve again, this time in blue */
+    cairo_set_source_rgba (cr, 0, 0, 1.0, 1.0);
+    paint_curve (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (clip_disjoint,
+           "Tests stroking through two disjoint clips.",
+           "clip, stroke", /* keywords */
+           NULL, /* requirements */
+           WIDTH, HEIGHT,
+           NULL, draw)
diff --git a/test/clip-double-free.c b/test/clip-double-free.c
new file mode 100644 (file)
index 0000000..74b9a6e
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * Copyright © 2011 Uli Schlachter
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Uli Schlachter <psychon@znc.in>
+ */
+
+/*
+ * This test wants to hit the following double free:
+ *
+ * ==10517== Invalid free() / delete / delete[]
+ * ==10517==    at 0x4C268FE: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
+ * ==10517==    by 0x87FBE80: _cairo_clip_destroy (cairo-clip.c:136)
+ * ==10517==    by 0x87FE520: _cairo_clip_intersect_boxes.part.1 (cairo-clip-private.h:92)
+ * ==10517==    by 0x87FE79F: _cairo_clip_intersect_rectilinear_path (cairo-clip-boxes.c:266)
+ * ==10517==    by 0x87FC29B: _cairo_clip_intersect_path.part.3 (cairo-clip.c:242)
+ * ==10517==    by 0x8809C3A: _cairo_gstate_clip (cairo-gstate.c:1518)
+ * ==10517==    by 0x8802E40: _cairo_default_context_clip (cairo-default-context.c:1048)
+ * ==10517==    by 0x87FA2C6: cairo_clip (cairo.c:2380)
+ * ==10517==  Address 0x18d44cb0 is 0 bytes inside a block of size 32 free'd
+ * ==10517==    at 0x4C268FE: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
+ * ==10517==    by 0x87FE506: _cairo_clip_intersect_boxes.part.1 (cairo-clip-boxes.c:295)
+ * ==10517==    by 0x87FE79F: _cairo_clip_intersect_rectilinear_path (cairo-clip-boxes.c:266)
+ * ==10517==    by 0x87FC29B: _cairo_clip_intersect_path.part.3 (cairo-clip.c:242)
+ * ==10517==    by 0x8809C3A: _cairo_gstate_clip (cairo-gstate.c:1518)
+ * ==10517==    by 0x8802E40: _cairo_default_context_clip (cairo-default-context.c:1048)
+ * ==10517==    by 0x87FA2C6: cairo_clip (cairo.c:2380)
+ *
+ * _cairo_clip_intersect_boxes() is called with clip->num_boxes != 0. It then
+ * calls _cairo_boxes_init_for_array (&clip_boxes, clip->boxes, clip->num_boxes)
+ * and free (clip->boxes), stealing the clip's boxes, but leaving a dangling
+ * pointer behind.
+ * Because this code already intersected the existing boxes and the new ones, we
+ * now have num_boxes == 0. This means that _cairo_clip_set_all_clipped() gets
+ * called and tries to free the clip's boxes again.
+ */
+
+#include "cairo-test.h"
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD);
+
+    /* To hit this bug, we first need a clip with
+     * clip->boxes != clip->embedded_boxes.
+     */
+    cairo_rectangle (cr, 0, 0, 2, 2);
+    cairo_rectangle (cr, 0, 0, 1, 1);
+    cairo_clip (cr);
+
+    /* Then we have to intersect this with a rectilinear path which results in
+     * all clipped. This path must consist of at least two boxes or we will hit
+     * a different code path.
+     */
+    cairo_rectangle (cr, 10, 10, 2, 2);
+    cairo_rectangle (cr, 10, 10, 1, 1);
+    cairo_clip (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (clip_double_free,
+           "Test a double free bug in the clipping code",
+           "clip", /* keywords */
+           NULL, /* requirements */
+           0, 0,
+           NULL, draw)
diff --git a/test/clip-draw-unbounded.c b/test/clip-draw-unbounded.c
new file mode 100644 (file)
index 0000000..6b9263b
--- /dev/null
@@ -0,0 +1,184 @@
+/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/*
+ * Copyright 2009 Chris Wilson
+ * Copyright 2010 Andrea Canciani
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+
+#define WIDTH 60
+#define HEIGHT 60
+
+static void
+stroke (cairo_t *cr)
+{
+    cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
+    cairo_set_source_rgb (cr, 1, 0, 0);
+    cairo_paint (cr);
+
+    cairo_set_operator (cr, CAIRO_OPERATOR_IN);
+    cairo_set_source_rgb (cr, 0, 0.7, 0);
+    cairo_arc (cr, 10, 10, 7.5, 0, 2 * M_PI);
+    cairo_move_to (cr, 0, 20);
+    cairo_line_to (cr, 20, 0);
+    cairo_stroke (cr);
+}
+
+static void
+fill (cairo_t *cr)
+{
+    cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
+    cairo_set_source_rgb (cr, 1, 0, 0);
+    cairo_paint (cr);
+
+    cairo_set_operator (cr, CAIRO_OPERATOR_IN);
+    cairo_set_source_rgb (cr, 0, 0.7, 0);
+    cairo_new_sub_path (cr);
+    cairo_arc (cr, 10, 10, 8.5, 0, 2 * M_PI);
+    cairo_new_sub_path (cr);
+    cairo_arc_negative (cr, 10, 10, 6.5, 2 * M_PI, 0);
+
+    cairo_move_to (cr, -1, 19);
+    cairo_line_to (cr,  1, 21);
+    cairo_line_to (cr, 21,  1);
+    cairo_line_to (cr, 19, -1);
+    cairo_line_to (cr, -1, 19);
+
+    cairo_fill (cr);
+}
+
+static void
+clip_simple (cairo_t *cr)
+{
+    cairo_rectangle (cr, 0, 0, 20, 20);
+    cairo_clip (cr);
+}
+
+static void
+clip_unaligned (cairo_t *cr)
+{
+    cairo_rectangle (cr, 0.5, 0.5, 20, 20);
+    cairo_clip (cr);
+}
+
+static void
+clip_aligned (cairo_t *cr)
+{
+    cairo_fill_rule_t orig_rule;
+
+    orig_rule = cairo_get_fill_rule (cr);
+    cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD);
+    cairo_rectangle (cr, 0, 0, 20, 20);
+    cairo_rectangle (cr, 3, 3, 10, 10);
+    cairo_rectangle (cr, 7, 7, 10, 10);
+    cairo_clip (cr);
+    cairo_set_fill_rule (cr, orig_rule);
+}
+
+static void
+clip_mask (cairo_t *cr)
+{
+    cairo_arc (cr, 10, 10, 10, 0, 2 * M_PI);
+    cairo_new_sub_path (cr);
+    cairo_arc_negative (cr, 10, 10, 5, 2 * M_PI, 0);
+    cairo_new_sub_path (cr);
+    cairo_arc (cr, 10, 10, 2, 0, 2 * M_PI);
+    cairo_clip (cr);
+}
+
+static void (* const clip_funcs[])(cairo_t *cr) = {
+    clip_simple,
+    clip_unaligned,
+    clip_aligned,
+    clip_mask
+};
+
+static double translations[][2] = {
+    { 10, 10 },
+    { WIDTH, 0 },
+    { -WIDTH, HEIGHT },
+    { WIDTH, 0 }
+};
+
+static cairo_test_status_t
+draw (cairo_t *cr, void (*shapes)(cairo_t *))
+{
+    unsigned int i;
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_paint (cr);
+
+    for (i = 0; i < ARRAY_LENGTH (clip_funcs); i++) {
+       cairo_translate (cr, translations[i][0], translations[i][1]);
+
+       cairo_save (cr);
+       cairo_scale (cr, 2, 2);
+       clip_funcs[i] (cr);
+       shapes (cr);
+       cairo_restore (cr);
+    }
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+static cairo_test_status_t
+draw_stroke (cairo_t *cr, int width, int height)
+{
+    return draw (cr, stroke);
+}
+
+static cairo_test_status_t
+draw_fill_nz (cairo_t *cr, int width, int height)
+{
+    cairo_set_fill_rule (cr, CAIRO_FILL_RULE_WINDING);
+    return draw (cr, fill);
+}
+
+static cairo_test_status_t
+draw_fill_eo (cairo_t *cr, int width, int height)
+{
+    cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD);
+    return draw (cr, fill);
+}
+
+CAIRO_TEST (clip_stroke_unbounded,
+           "Tests unbounded stroke through complex clips.",
+           "clip, stroke, unbounded", /* keywords */
+           NULL, /* requirements */
+           2 * WIDTH, 2 * HEIGHT,
+           NULL, draw_stroke)
+
+CAIRO_TEST (clip_fill_nz_unbounded,
+           "Tests unbounded fill through complex clips (with winding fill rule).",
+           "clip, fill, unbounded", /* keywords */
+           NULL, /* requirements */
+           2 * WIDTH, 2 * HEIGHT,
+           NULL, draw_fill_nz)
+
+CAIRO_TEST (clip_fill_eo_unbounded,
+           "Tests unbounded fill through complex clips (with even-odd fill rule).",
+           "clip, fill, unbounded", /* keywords */
+           NULL, /* requirements */
+           2 * WIDTH, 2 * HEIGHT,
+           NULL, draw_fill_eo)
diff --git a/test/clip-empty-group.c b/test/clip-empty-group.c
new file mode 100644 (file)
index 0000000..20e95c2
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2010 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+
+/* Test the handling of cairo_push_group() with everything clipped. */
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_paint (cr); /* opaque background */
+
+    cairo_rectangle (cr,  20, 20, 0, 0);
+    cairo_clip (cr);
+
+    cairo_push_group (cr); /* => 0x0 group */
+    cairo_reset_clip (cr);
+
+    cairo_set_source_rgb (cr, 1, 0, 0);
+    cairo_paint (cr);
+
+    cairo_rectangle (cr, 0, 0, width, height);
+    cairo_set_source_rgba (cr, 0, 1, 0, .5);
+    cairo_fill (cr);
+
+    cairo_move_to (cr, 0, 20);
+    cairo_line_to (cr, width, 20);
+    cairo_set_source_rgb (cr, 0, 0, 1);
+    cairo_stroke (cr);
+
+    cairo_pop_group_to_source (cr);
+    cairo_reset_clip (cr);
+    cairo_paint (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (clip_empty_group,
+           "Test handling of groups with everything clipped",
+           "clip group", /* keywords */
+           NULL, /* requirements */
+           40, 40,
+           NULL, draw)
diff --git a/test/clip-empty-save.c b/test/clip-empty-save.c
new file mode 100644 (file)
index 0000000..35de3cb
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * Copyright © 2007 Chris Wilson
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Chris Wilson not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Chris Wilson makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * CHRIS WILSON DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL CHRIS WILSON BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+
+#define SIZE 10
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_set_source_rgb (cr, 0, 0, 1);
+    cairo_paint (cr);
+
+    cairo_reset_clip (cr);
+    cairo_clip (cr);
+
+    cairo_save (cr);
+
+    cairo_translate (cr, .5, .5);
+
+    cairo_set_source_rgb (cr, 0, 1, 0);
+    cairo_rectangle (cr, 0, 0, SIZE, SIZE);
+    cairo_fill_preserve (cr);
+    cairo_set_source_rgb (cr, 1, 0, 0);
+    cairo_stroke (cr);
+
+    /* https://bugs.freedesktop.org/show_bug.cgi?id=13084 */
+    cairo_select_font_face (cr,
+                           CAIRO_TEST_FONT_FAMILY " Sans",
+                           CAIRO_FONT_SLANT_NORMAL,
+                           CAIRO_FONT_WEIGHT_NORMAL);
+
+    cairo_move_to (cr, 0., SIZE);
+    cairo_show_text (cr, "cairo");
+
+    cairo_restore (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (clip_empty_save,
+           "Test clipping with an empty clip path",
+           "clip", /* keywords */
+           NULL, /* requirements */
+           SIZE, SIZE,
+           NULL, draw)
diff --git a/test/clip-empty.c b/test/clip-empty.c
new file mode 100644 (file)
index 0000000..858b69a
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Copyright © 2007 Chris Wilson
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Chris Wilson not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Chris Wilson makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * CHRIS WILSON DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL CHRIS WILSON BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+
+#define SIZE 10
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_set_source_rgb (cr, 0, 0, 1);
+    cairo_paint (cr);
+
+    cairo_reset_clip (cr);
+    cairo_clip (cr);
+
+    cairo_translate (cr, .5, .5);
+
+    cairo_set_source_rgb (cr, 0, 1, 0);
+    cairo_rectangle (cr, 0, 0, SIZE, SIZE);
+    cairo_fill_preserve (cr);
+    cairo_set_source_rgb (cr, 1, 0, 0);
+    cairo_stroke (cr);
+
+    /* https://bugs.freedesktop.org/show_bug.cgi?id=13084 */
+    cairo_select_font_face (cr,
+                           CAIRO_TEST_FONT_FAMILY " Sans",
+                           CAIRO_FONT_SLANT_NORMAL,
+                           CAIRO_FONT_WEIGHT_NORMAL);
+
+    cairo_move_to (cr, 0., SIZE);
+    cairo_show_text (cr, "cairo");
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (clip_empty,
+           "Test clipping with an empty clip path",
+           "clip", /* keywords */
+           NULL, /* requirements */
+           SIZE, SIZE,
+           NULL, draw)
diff --git a/test/clip-fill-no-op.c b/test/clip-fill-no-op.c
new file mode 100644 (file)
index 0000000..b686b6e
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2009 Benjamin Otte
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Benjamin Otte <otte@gnome.org>
+ */
+
+#include "cairo-test.h"
+
+#define WIDTH 50
+#define HEIGHT 50
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    /* Neutral gray background */
+    cairo_set_source_rgb (cr, 0.5, 0.5, 0.5);
+    cairo_paint (cr);
+
+    /* remove this clip operation and everything works */
+    cairo_rectangle (cr, 10, 10, 30, 30);
+    cairo_clip (cr);
+
+    /* remove this no-op and everything works */
+    cairo_fill (cr);
+
+    /* make the y coordinates integers and everything works */
+    cairo_move_to (cr, 20, 20.101562);
+    cairo_line_to (cr, 30, 20.101562);
+
+    /* This clip operation should fail to work. But with cairo 1.9, if all the 
+     * 3 cases above happen, the clip will not work and the paint will happen.
+     */
+    cairo_save (cr); {
+       cairo_set_source_rgba (cr, 1, 0.5, 0.5, 1);
+       cairo_clip_preserve (cr);
+       cairo_paint (cr);
+    } cairo_restore (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (clip_fill_no_op,
+           "Exercises a bug found by Benjamin Otte whereby a no-op clip is nullified by a stroke",
+           "clip, fill", /* keywords */
+           NULL, /* requirements */
+           WIDTH, HEIGHT,
+           NULL, draw)
diff --git a/test/clip-fill-rule-pixel-aligned.c b/test/clip-fill-rule-pixel-aligned.c
new file mode 100644 (file)
index 0000000..210afcb
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * Copyright © 2004 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ */
+
+#include "cairo-test.h"
+
+#define PAD 1
+#define SIZE 5
+
+static void
+pixel_aligned_path (cairo_t *cr)
+{
+    cairo_save (cr);
+    {
+       cairo_scale (cr, SIZE, SIZE);
+       cairo_move_to     (cr,  1,  0);
+       cairo_rel_line_to (cr,  1,  0);
+       cairo_rel_line_to (cr,  0,  3);
+       cairo_rel_line_to (cr,  1,  0);
+       cairo_rel_line_to (cr,  0, -1);
+       cairo_rel_line_to (cr, -3,  0);
+       cairo_rel_line_to (cr,  0, -1);
+       cairo_rel_line_to (cr,  4,  0);
+       cairo_rel_line_to (cr,  0,  3);
+       cairo_rel_line_to (cr, -3,  0);
+       cairo_rel_line_to (cr,  0, -4);
+       cairo_close_path (cr);
+    }
+    cairo_restore (cr);
+}
+
+/* Use clipping to draw the same path twice, once with each fill rule */
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_set_source_rgb (cr, 1, 0, 0); /* red */
+
+    cairo_translate (cr, PAD, PAD);
+
+    cairo_save (cr);
+    {
+       pixel_aligned_path (cr);
+       cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD);
+       cairo_clip (cr);
+       cairo_paint (cr);
+    }
+    cairo_restore (cr);
+
+    cairo_translate (cr, SIZE*4 + PAD, 0);
+
+    cairo_save (cr);
+    {
+       pixel_aligned_path (cr);
+       cairo_set_fill_rule (cr, CAIRO_FILL_RULE_WINDING);
+       cairo_clip (cr);
+       cairo_paint (cr);
+    }
+    cairo_restore (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (clip_fill_rule_pixel_aligned,
+           "Tests interaction of clipping and cairo_set_fill_rule with a pixel-aligned path",
+           "clip", /* keywords */
+           NULL, /* requirements */
+           PAD + (SIZE*4) + PAD + (SIZE*4) + PAD,
+           PAD + (SIZE*4) + PAD,
+           NULL, draw)
diff --git a/test/clip-fill-rule.c b/test/clip-fill-rule.c
new file mode 100644 (file)
index 0000000..31c3ab7
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * Copyright © 2004 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ */
+
+#include "cairo-test.h"
+
+#define STAR_SIZE 20
+
+static void
+star_path (cairo_t *cr)
+{
+    cairo_move_to (cr, 10, 0);
+    cairo_rel_line_to (cr, 6, 20);
+    cairo_rel_line_to (cr, -16, -12);
+    cairo_rel_line_to (cr, 20, 0);
+    cairo_rel_line_to (cr, -16, 12);
+}
+
+/* Use clipping to draw the same path twice, once with each fill rule */
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_set_source_rgb (cr, 1, 0, 0);
+
+    cairo_translate (cr, 1, 1);
+    cairo_save (cr);
+    {
+       star_path (cr);
+       cairo_set_fill_rule (cr, CAIRO_FILL_RULE_WINDING);
+       cairo_clip (cr);
+       cairo_paint (cr);
+    }
+    cairo_restore (cr);
+
+    cairo_translate (cr, STAR_SIZE + 1, 0);
+    cairo_save (cr);
+    {
+       star_path (cr);
+       cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD);
+       cairo_clip (cr);
+       cairo_paint (cr);
+    }
+    cairo_restore (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+static cairo_test_status_t
+a1_draw (cairo_t *cr, int width, int height)
+{
+    cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
+    return draw (cr, width, height);
+}
+
+CAIRO_TEST (clip_fill_rule,
+           "Tests interaction of clipping with cairo_set_fill_rule",
+           "clip", /* keywords */
+           NULL, /* requirements */
+           STAR_SIZE * 2 + 2, STAR_SIZE + 2,
+           NULL, draw)
+CAIRO_TEST (a1_clip_fill_rule,
+           "Tests interaction of clipping with cairo_set_fill_rule",
+           "clip", /* keywords */
+           "target=raster", /* requirements */
+           STAR_SIZE * 2 + 2, STAR_SIZE + 2,
+           NULL, a1_draw)
diff --git a/test/clip-fill.c b/test/clip-fill.c
new file mode 100644 (file)
index 0000000..331af79
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2009 Chris Wilson
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Chris Wilson not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Chris Wilson makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * CHRIS WILSON DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL CHRIS WILSON BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+
+#define WIDTH 20
+#define HEIGHT 20
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_paint (cr);
+
+    /* aligned-clip */
+    cairo_save (cr);
+    cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD);
+    cairo_rectangle (cr, 0, 0, 20, 20);
+    cairo_rectangle (cr, 3, 3, 10, 10);
+    cairo_rectangle (cr, 7, 7, 10, 10);
+    cairo_clip (cr);
+
+    cairo_set_source_rgb (cr, 0.7, 0, 0);
+    cairo_paint (cr);
+    cairo_set_source_rgb (cr, 0, 0.7, 0);
+    cairo_arc (cr, 10, 10, 8, 0, 2 * M_PI);
+    cairo_fill (cr);
+    cairo_restore (cr);
+
+    cairo_translate (cr, WIDTH, 0);
+
+    /* force a clip-mask */
+    cairo_save (cr);
+    cairo_arc (cr, 10, 10, 10, 0, 2 * M_PI);
+    cairo_new_sub_path (cr);
+    cairo_arc_negative (cr, 10, 10, 5, 2 * M_PI, 0);
+    cairo_new_sub_path (cr);
+    cairo_arc (cr, 10, 10, 2, 0, 2 * M_PI);
+    cairo_clip (cr);
+
+    cairo_set_source_rgb (cr, 0, 0, 0.7);
+    cairo_paint (cr);
+    cairo_set_source_rgb (cr, 0, 0.7, 0);
+    cairo_arc (cr, 10, 10, 7.5, 0, 2 * M_PI);
+    cairo_fill (cr);
+    cairo_restore (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (clip_fill,
+           "Tests filling through complex clips.",
+           "clip, fill", /* keywords */
+           NULL, /* requirements */
+           2 * WIDTH, HEIGHT,
+           NULL, draw)
diff --git a/test/clip-group-shapes.c b/test/clip-group-shapes.c
new file mode 100644 (file)
index 0000000..88bb9b3
--- /dev/null
@@ -0,0 +1,188 @@
+/*
+ * Copyright (c) 2010 M Joonas Pihlaja
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: M Joonas Pihlaja <jpihlaja@cc.helsinki.fi>
+ */
+#include "cairo-test.h"
+
+/* Tests specific clipping fast paths and their interaction with
+ * groups:  It shouldn't matter if the clip is set before or after
+ * pushing a group.
+ *
+ * There's some overlap with the following tests, but they test for
+ * different things:
+ *
+ *  group-clip.c (tests preserving paths), clipped-group.c (tests
+ *  clipping the same thing different ways), clip-push-group (tests
+ *  for a specific bug).
+ */
+
+#define GENERATE_REF 0
+
+/* For determining whether we establish the clip path before or after
+ * pushing a group. */
+enum {
+    CLIP_OUTSIDE_GROUP,
+    CLIP_INSIDE_GROUP
+};
+
+typedef void (*clipper_t)(cairo_t *cr, int w, int h);
+
+static cairo_test_status_t
+clip_and_paint (cairo_t *cr,
+                int w, int h,
+                clipper_t do_clip,
+                int clip_where)
+{
+    cairo_save (cr); {
+        if (GENERATE_REF) {
+            do_clip (cr, w, h);
+            cairo_paint (cr);
+        } else {
+            if (clip_where == CLIP_OUTSIDE_GROUP)
+                do_clip (cr, w, h);
+            cairo_push_group (cr); {
+                if (clip_where == CLIP_INSIDE_GROUP)
+                    do_clip (cr, w, h);
+                cairo_paint (cr);
+            }
+            cairo_pop_group_to_source (cr);
+            if (clip_where == CLIP_OUTSIDE_GROUP)
+               cairo_reset_clip (cr);
+            cairo_paint (cr);
+        }
+    }
+    cairo_restore (cr);
+    return CAIRO_TEST_SUCCESS;
+}
+
+static cairo_test_status_t
+run_clip_test (cairo_t *cr, int w, int h, clipper_t do_clip)
+{
+    cairo_set_source_rgb (cr, 1,1,1);
+    cairo_paint (cr);
+    cairo_set_source_rgb (cr, 1,0,0);
+
+    /* Left. */
+    clip_and_paint (cr, w/2, h, do_clip, CLIP_OUTSIDE_GROUP);
+
+    /* Right */
+    cairo_translate(cr, w/2, 0);
+    clip_and_paint (cr, w/2, h, do_clip, CLIP_INSIDE_GROUP);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+static void
+clip_aligned_rectangles (cairo_t *cr, int w, int h)
+{
+    int x1 = 0.2 * w;
+    int y1 = 0.2 * h;
+    int x2 = 0.8 * w;
+    int y2 = 0.8 * h;
+
+    cairo_rectangle (cr, x1, y1, w, h);
+    cairo_clip (cr);
+
+    cairo_rectangle (cr, x2, y2, -w, -h);
+    cairo_clip (cr);
+}
+
+static void
+clip_unaligned_rectangles (cairo_t *cr, int w, int h)
+{
+    /* This clip stresses the antialiased edges produced by an
+     * unaligned rectangular clip. The edges should be produced by
+     * compositing red on white with alpha = 0.5 on the sides, and with
+     * alpha = 0.25 in the corners. */
+    int x1 = 0.2 * w;
+    int y1 = 0.2 * h;
+    int x2 = 0.8 * w;
+    int y2 = 0.8 * h;
+
+    cairo_rectangle (cr, x1+0.5, y1+0.5, w, h);
+    cairo_clip (cr);
+
+    cairo_rectangle (cr, x2+0.5, y2+0.5, -w, -h);
+    w = x2 - x1;
+    h = y2 - y1;
+    cairo_rectangle (cr, x2, y1+1, -w+1, h-1);
+    cairo_clip (cr);
+}
+
+static void
+clip_circles (cairo_t *cr, int w, int h)
+{
+    int x1 = 0.5 * w;
+    int y1 = 0.5 * h;
+    int x2 = 0.75 * w;
+    int y2 = 0.75 * h;
+    int r = 0.4*MIN(w,h);
+
+    cairo_arc (cr, x1, y1, r, 0, 6.28);
+    cairo_close_path (cr);
+    cairo_clip (cr);
+
+    cairo_arc (cr, x2, y2, r, 0, 6.28);
+    cairo_close_path (cr);
+    cairo_clip (cr);
+}
+
+static cairo_test_status_t
+draw_aligned_rectangles (cairo_t *cr, int width, int height)
+{
+    return run_clip_test (cr, width, height, clip_aligned_rectangles);
+}
+
+static cairo_test_status_t
+draw_unaligned_rectangles (cairo_t *cr, int width, int height)
+{
+    return run_clip_test (cr, width, height, clip_unaligned_rectangles);
+}
+
+static cairo_test_status_t
+draw_circles (cairo_t *cr, int width, int height)
+{
+    return run_clip_test (cr, width, height, clip_circles);
+}
+
+CAIRO_TEST (clip_group_shapes_aligned_rectangles,
+           "Test clip and group interaction with aligned rectangle clips",
+           "clip", /* keywords */
+           NULL, /* requirements */
+           200, 100,
+           NULL, draw_aligned_rectangles)
+
+CAIRO_TEST (clip_group_shapes_unaligned_rectangles,
+           "Test clip and group interaction with unaligned rectangle clips",
+           "clip", /* keywords */
+           "target=raster", /* requirements */
+           200, 100,
+           NULL, draw_unaligned_rectangles)
+
+CAIRO_TEST (clip_group_shapes_circles,
+           "Test clip and group interaction with circular clips",
+           "clip", /* keywords */
+           NULL, /* requirements */
+           200, 100,
+           NULL, draw_circles)
diff --git a/test/clip-image.c b/test/clip-image.c
new file mode 100644 (file)
index 0000000..68ed142
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2009 Chris Wilson
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Chris Wilson not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Chris Wilson makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * CHRIS WILSON DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL CHRIS WILSON BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+
+#define WIDTH 20
+#define HEIGHT 20
+
+static const char *png_filename = "romedalen.png";
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    const cairo_test_context_t *ctx = cairo_test_get_context (cr);
+    cairo_surface_t *image;
+
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_paint (cr);
+
+    image = cairo_test_create_surface_from_png (ctx, png_filename);
+    cairo_set_source_surface (cr, image, 0, 0);
+    cairo_surface_destroy (image);
+
+    /* simple clip */
+    cairo_save (cr);
+    cairo_rectangle (cr, 2, 2, 16, 16);
+    cairo_clip (cr);
+    cairo_paint (cr);
+    cairo_restore (cr);
+
+    cairo_translate (cr, WIDTH, 0);
+
+    /* unaligned clip */
+    cairo_save (cr);
+    cairo_rectangle (cr, 2.5, 2.5, 15, 15);
+    cairo_clip (cr);
+    cairo_paint (cr);
+    cairo_restore (cr);
+
+    cairo_translate (cr, -WIDTH, HEIGHT);
+
+    /* aligned-clip */
+    cairo_save (cr);
+    cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD);
+    cairo_rectangle (cr, 0, 0, 20, 20);
+    cairo_rectangle (cr, 3, 3, 10, 10);
+    cairo_rectangle (cr, 7, 7, 10, 10);
+    cairo_clip (cr);
+    cairo_paint (cr);
+    cairo_restore (cr);
+
+    cairo_translate (cr, WIDTH, 0);
+
+    /* force a clip-mask */
+    cairo_save (cr);
+    cairo_arc (cr, 10, 10, 10, 0, 2 * M_PI);
+    cairo_new_sub_path (cr);
+    cairo_arc_negative (cr, 10, 10, 5, 2 * M_PI, 0);
+    cairo_new_sub_path (cr);
+    cairo_arc (cr, 10, 10, 2, 0, 2 * M_PI);
+    cairo_clip (cr);
+    cairo_paint (cr);
+    cairo_restore (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (clip_image,
+           "Tests painting an image through complex clips.",
+           "clip, paint", /* keywords */
+           NULL, /* requirements */
+           2 * WIDTH, 2 * HEIGHT,
+           NULL, draw)
diff --git a/test/clip-intersect.c b/test/clip-intersect.c
new file mode 100644 (file)
index 0000000..295fbc9
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2009 Chris Wilson
+ * Copyright 2011 Intel Corporation
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Chris Wilson not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Chris Wilson makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * CHRIS WILSON DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL CHRIS WILSON BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+
+#define WIDTH 20
+#define HEIGHT 20
+
+static void clip_mask (cairo_t *cr)
+{
+    cairo_move_to (cr, 10, 0);
+    cairo_line_to (cr, 0, 10);
+    cairo_line_to (cr, 10, 20);
+    cairo_line_to (cr, 20, 10);
+    cairo_clip (cr);
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_paint (cr);
+
+    clip_mask (cr);
+    cairo_set_source_rgb (cr, 0, 1, 0);
+    cairo_paint (cr);
+    cairo_reset_clip (cr);
+
+    cairo_set_source_rgb (cr, 1, 0, 0);
+
+    cairo_rectangle (cr, 0, 0, 4, 4);
+    cairo_clip (cr);
+    clip_mask (cr);
+    cairo_paint (cr);
+    cairo_reset_clip (cr);
+
+    cairo_rectangle (cr, 20, 0, -4, 4);
+    cairo_clip (cr);
+    clip_mask (cr);
+    cairo_paint (cr);
+    cairo_reset_clip (cr);
+
+    cairo_rectangle (cr, 20, 20, -4, -4);
+    cairo_clip (cr);
+    clip_mask (cr);
+    cairo_paint (cr);
+    cairo_reset_clip (cr);
+
+    cairo_rectangle (cr, 0, 20, 4, -4);
+    cairo_clip (cr);
+    clip_mask (cr);
+    cairo_paint (cr);
+    cairo_reset_clip (cr);
+
+    cairo_set_source_rgb (cr, 0, 0, 1);
+
+    cairo_rectangle (cr, 8, 8, 4, 4);
+    cairo_clip (cr);
+    clip_mask (cr);
+    cairo_paint (cr);
+    cairo_reset_clip (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (clip_intersect,
+           "Tests intersection of a simple clip with a clip-mask",
+           "clip, paint", /* keywords */
+           NULL, /* requirements */
+           WIDTH, HEIGHT,
+           NULL, draw)
diff --git a/test/clip-mixed-antialias.c b/test/clip-mixed-antialias.c
new file mode 100644 (file)
index 0000000..23a55ff
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+ * Copyright © 2011 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+
+#define WIDTH  200
+#define HEIGHT 200
+
+/* This is an example from the wild, as silly as it is
+n 52 0 429 709 rectangle
+clip+
+//ANTIALIAS_NONE set-antialias
+n 8 0 m 952 0 l 956.417969 0 960 3.582031 960 8 c 960 1807 l 960 1811.417969 956.417969 1815 952 1815 c 8 1815 l 3.582031 1815 0 1811.417969 0 1807 c 0 8 l 0 3.582031 3.582031 0 8 0 c h
+clip+
+//EVEN_ODD set-fill-rule
+n 0 0 m 480 0 l 480 708 l 0 708 l h 8 1 m 952 1 l 955.867188 1 959 4.132812 959 8 c 959 1807 l 959 1810.867188 955.867188 1814 952 1814 c 8 1814 l 4.132812 1814 1 1810.867188 1 1807 c 1 8 l 1 4.132812 4.132812 1 8 1 c h
+clip+
+//WINDING set-fill-rule
+//ANTIALIAS_DEFAULT set-antialias
+n 960 0 m 52.5 907.5 l 52.5 1815 l 960 1815 l h
+clip+
+//ANTIALIAS_NONE set-antialias
+n 960 0 m 52.5 0 l 52.5 907.5 l 960 1815 l h
+clip+
+*/
+
+static void background (cairo_t *cr)
+{
+    cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+    cairo_set_source_rgb (cr, 1,1,1);
+    cairo_paint (cr);
+    cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
+}
+
+static void clip_0 (cairo_t *cr)
+{
+    cairo_rectangle (cr, 5, 5, 190, 190);
+    cairo_clip (cr);
+}
+
+static void clip_1 (cairo_t *cr)
+{
+    cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
+
+    cairo_arc (cr, 100, 100, 125, 0, 2*M_PI);
+    cairo_clip (cr);
+
+    cairo_set_antialias (cr, CAIRO_ANTIALIAS_DEFAULT);
+}
+
+static void
+rounded_rectangle (cairo_t *cr, int x, int y, int w, int h, int r)
+{
+    cairo_new_sub_path (cr);
+    cairo_arc (cr, x + r, y + r, r, M_PI, 3 * M_PI / 2);
+    cairo_arc (cr, x + w - r, y + r, r, 3 *M_PI / 2, 2 * M_PI);
+    cairo_arc (cr, x + w - r, y + h - r, r, 0, M_PI / 2);
+    cairo_arc (cr, x + r, y + h - r, r, M_PI / 2, M_PI);
+    cairo_close_path (cr);
+}
+
+static void clip_2 (cairo_t *cr)
+{
+    cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD);
+
+    rounded_rectangle (cr, 50, 50, 100, 100, 15);
+    rounded_rectangle (cr, 60, 60, 80, 80, 5);
+    cairo_clip (cr);
+
+    cairo_set_fill_rule (cr, CAIRO_FILL_RULE_WINDING);
+}
+
+static void clip_3 (cairo_t *cr)
+{
+    cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
+
+    cairo_rectangle (cr, 40.25, 60.25, 120, 80);
+    cairo_rectangle (cr, 60.25, 40.25, 80, 120);
+    cairo_clip (cr);
+
+    cairo_set_antialias (cr, CAIRO_ANTIALIAS_DEFAULT);
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    background (cr);
+
+    clip_0 (cr);
+    clip_1 (cr);
+    clip_2 (cr);
+    clip_3 (cr);
+
+    cairo_set_source_rgb (cr, 0, 0, 0);
+    cairo_paint (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (clip_mixed_antialias,
+           "Test drawing through through an mixture of clips",
+           "clip", /* keywords */
+           "target=raster", /* requirements */
+           WIDTH, HEIGHT,
+           NULL, draw)
diff --git a/test/clip-nesting.c b/test/clip-nesting.c
new file mode 100644 (file)
index 0000000..ce03b4b
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Owen Taylor <otaylor@redhat.com>
+ */
+
+#include <math.h>
+#include "cairo-test.h"
+#include <stdio.h>
+
+#define SIZE 100
+#define BORDER 10
+#define LINE_WIDTH 20
+
+static void
+_propagate_status (cairo_t *dst, cairo_t *src)
+{
+    cairo_path_t path;
+
+    path.status = cairo_status (src);
+    if (path.status) {
+       path.num_data = 0;
+       path.data = NULL;
+       cairo_append_path (dst, &path);
+    }
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_surface_t *target_surface;
+    cairo_t *cr2, *cr3;
+
+    target_surface = cairo_get_group_target (cr);
+
+    cr2 = cairo_create (target_surface);
+
+    /* Draw a diagonal line and clip to it */
+
+    cairo_move_to (cr2, BORDER,                     BORDER);
+    cairo_line_to (cr2, BORDER + LINE_WIDTH,        BORDER);
+    cairo_line_to (cr2, SIZE - BORDER,              SIZE - BORDER);
+    cairo_line_to (cr2, SIZE - BORDER - LINE_WIDTH, SIZE - BORDER);
+
+    cairo_clip (cr2);
+    cairo_set_source_rgb (cr2, 0, 0, 1); /* Blue */
+    cairo_paint (cr2);
+
+    /* Clipping affects this cairo_t */
+
+    cairo_set_source_rgb (cr2, 1, 1, 1); /* White */
+    cairo_rectangle (cr2,
+                    SIZE / 2 - LINE_WIDTH / 2, BORDER,
+                    LINE_WIDTH,                SIZE - 2 * BORDER);
+    cairo_fill (cr2);
+
+    /* But doesn't affect another cairo_t that we create temporarily for
+     * the same surface
+     */
+    cr3 = cairo_create (target_surface);
+    cairo_set_source_rgb (cr3, 1, 1, 1); /* White */
+    cairo_rectangle (cr3,
+                    SIZE - BORDER - LINE_WIDTH, BORDER,
+                    LINE_WIDTH,                 SIZE - 2 * BORDER);
+    cairo_fill (cr3);
+
+    _propagate_status (cr, cr3);
+    cairo_destroy (cr3);
+
+    _propagate_status (cr, cr2);
+    cairo_destroy (cr2);
+
+    /* And doesn't affect anything after this cairo_t is destroyed */
+
+    cairo_set_source_rgb (cr, 1, 1, 1); /* White */
+    cairo_rectangle (cr,
+                    BORDER,     BORDER,
+                    LINE_WIDTH, SIZE - 2 * BORDER);
+    cairo_fill (cr);
+
+    return CAIRO_TEST_SUCCESS;
+
+}
+
+CAIRO_TEST (clip_nesting,
+           "Test clipping with multiple contexts for the same surface",
+           "clip", /* keywords */
+           NULL, /* requirements */
+           SIZE, SIZE,
+           NULL, draw)
diff --git a/test/clip-operator.c b/test/clip-operator.c
new file mode 100644 (file)
index 0000000..aaa3445
--- /dev/null
@@ -0,0 +1,188 @@
+/*
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Kristian Høgsberg <krh@redhat.com>
+ */
+
+#include <math.h>
+#include "cairo-test.h"
+#include <stdio.h>
+
+#define WIDTH 16
+#define HEIGHT 16
+#define PAD 2
+
+static void
+draw_mask (cairo_t *cr, int x, int y)
+{
+    cairo_surface_t *mask_surface;
+    cairo_t *cr2;
+
+    double width = (int)(0.9 * WIDTH);
+    double height = (int)(0.9 * HEIGHT);
+    x += 0.05 * WIDTH;
+    y += 0.05 * HEIGHT;
+
+    mask_surface = cairo_surface_create_similar (cairo_get_group_target (cr),
+                                                CAIRO_CONTENT_ALPHA,
+                                                width, height);
+    cr2 = cairo_create (mask_surface);
+    cairo_surface_destroy (mask_surface);
+
+    cairo_save (cr2);
+    cairo_set_source_rgba (cr2, 0, 0, 0, 0); /* transparent */
+    cairo_set_operator (cr2, CAIRO_OPERATOR_SOURCE);
+    cairo_paint (cr2);
+    cairo_restore (cr2);
+
+    cairo_set_source_rgb (cr2, 1, 1, 1); /* white */
+
+    cairo_arc (cr2, 0.5 * width, 0.5 * height, 0.45 * height, 0, 2 * M_PI);
+    cairo_fill (cr2);
+
+    cairo_mask_surface (cr, cairo_get_target (cr2), x, y);
+    cairo_destroy (cr2);
+}
+
+static void
+draw_glyphs (cairo_t *cr, int x, int y)
+{
+    cairo_text_extents_t extents;
+
+    cairo_set_font_size (cr, 0.8 * HEIGHT);
+
+    cairo_text_extents (cr, "FG", &extents);
+    cairo_move_to (cr,
+                  x + floor ((WIDTH - extents.width) / 2 + 0.5) - extents.x_bearing,
+                  y + floor ((HEIGHT - extents.height) / 2 + 0.5) - extents.y_bearing);
+    cairo_show_text (cr, "FG");
+}
+
+static void
+draw_polygon (cairo_t *cr, int x, int y)
+{
+    double width = (int)(0.9 * WIDTH);
+    double height = (int)(0.9 * HEIGHT);
+    x += 0.05 * WIDTH;
+    y += 0.05 * HEIGHT;
+
+    cairo_new_path (cr);
+    cairo_move_to (cr, x, y);
+    cairo_line_to (cr, x, y + height);
+    cairo_line_to (cr, x + width / 2, y + 3 * height / 4);
+    cairo_line_to (cr, x + width, y + height);
+    cairo_line_to (cr, x + width, y);
+    cairo_line_to (cr, x + width / 2, y + height / 4);
+    cairo_close_path (cr);
+    cairo_fill (cr);
+}
+
+static void
+draw_rects (cairo_t *cr, int x, int y)
+{
+    double block_width = (int)(0.33 * WIDTH + 0.5);
+    double block_height = (int)(0.33 * HEIGHT + 0.5);
+    int i, j;
+
+    for (i = 0; i < 3; i++)
+       for (j = 0; j < 3; j++)
+           if ((i + j) % 2 == 0)
+               cairo_rectangle (cr,
+                                x + block_width * i, y + block_height * j,
+                                block_width,         block_height);
+
+    cairo_fill (cr);
+}
+
+static void (* const draw_funcs[])(cairo_t *cr, int x, int y) = {
+    draw_mask,
+    draw_glyphs,
+    draw_polygon,
+    draw_rects
+};
+
+#define N_OPERATORS (1 + CAIRO_OPERATOR_SATURATE - CAIRO_OPERATOR_CLEAR)
+
+#define IMAGE_WIDTH (N_OPERATORS * (WIDTH + PAD) + PAD)
+#define IMAGE_HEIGHT (ARRAY_LENGTH (draw_funcs) * (HEIGHT + PAD) + PAD)
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    const cairo_test_context_t *ctx = cairo_test_get_context (cr);
+    size_t j, x, y;
+    cairo_operator_t op;
+    cairo_pattern_t *pattern;
+
+    cairo_select_font_face (cr, CAIRO_TEST_FONT_FAMILY " Sans",
+                           CAIRO_FONT_SLANT_NORMAL,
+                           CAIRO_FONT_WEIGHT_NORMAL);
+    cairo_set_font_size (cr, 0.9 * HEIGHT);
+
+    for (j = 0; j < ARRAY_LENGTH (draw_funcs); j++) {
+       for (op = CAIRO_OPERATOR_CLEAR; op < N_OPERATORS; op++) {
+           x = op * (WIDTH + PAD) + PAD;
+           y = j * (HEIGHT + PAD) + PAD;
+
+           cairo_save (cr);
+
+           pattern = cairo_pattern_create_linear (x + WIDTH, y,
+                                                  x,         y + HEIGHT);
+           cairo_pattern_add_color_stop_rgba (pattern, 0.2,
+                                              0.0, 0.0, 1.0, 1.0); /* Solid blue */
+           cairo_pattern_add_color_stop_rgba (pattern, 0.8,
+                                              0.0, 0.0, 1.0, 0.0); /* Transparent blue */
+           cairo_set_source (cr, pattern);
+           cairo_pattern_destroy (pattern);
+
+           cairo_rectangle (cr, x, y, WIDTH, HEIGHT);
+           cairo_fill (cr);
+
+           cairo_set_operator (cr, op);
+           cairo_set_source_rgb (cr, 1.0, 0.0, 0.0);
+
+           cairo_move_to (cr, x, y);
+           cairo_line_to (cr, x + WIDTH, y);
+           cairo_line_to (cr, x, y + HEIGHT);
+           cairo_clip (cr);
+
+           draw_funcs[j] (cr, x, y);
+           if (cairo_status (cr))
+               cairo_test_log (ctx, "%d %d HERE!\n", op, (int)j);
+
+           cairo_restore (cr);
+       }
+    }
+
+    if (cairo_status (cr) != CAIRO_STATUS_SUCCESS)
+       cairo_test_log (ctx, "%d %d .HERE!\n", op, (int)j);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (clip_operator,
+           "Surface clipping with different operators",
+           "clip", /* keywords */
+           NULL, /* requirements */
+           IMAGE_WIDTH, IMAGE_HEIGHT,
+           NULL, draw)
+
diff --git a/test/clip-polygons.c b/test/clip-polygons.c
new file mode 100644 (file)
index 0000000..83eb4df
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ * Copyright © 2011 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+
+#define STEP   5
+#define WIDTH  100
+#define HEIGHT 100
+
+static void diamond (cairo_t *cr)
+{
+    cairo_move_to (cr, WIDTH/2, 0);
+    cairo_line_to (cr, WIDTH, HEIGHT/2);
+    cairo_line_to (cr, WIDTH/2, HEIGHT);
+    cairo_line_to (cr, 0, HEIGHT/2);
+    cairo_close_path (cr);
+}
+
+static void background (cairo_t *cr)
+{
+    cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+    cairo_set_source_rgb (cr, 1,1,1);
+    cairo_paint (cr);
+    cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
+    cairo_set_source_rgb (cr, 0,0,0);
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    background (cr);
+
+    /* completely overlapping diamonds */
+    cairo_save (cr);
+    diamond (cr);
+    cairo_clip (cr);
+    diamond (cr);
+    cairo_clip (cr);
+    cairo_paint (cr);
+    cairo_restore (cr);
+
+    cairo_translate (cr, WIDTH, 0);
+
+    /* partial overlap */
+    cairo_save (cr);
+    cairo_translate (cr, -WIDTH/4, 0);
+    diamond (cr);
+    cairo_clip (cr);
+    cairo_translate (cr, WIDTH/2, 0);
+    diamond (cr);
+    cairo_clip (cr);
+    cairo_paint (cr);
+    cairo_restore (cr);
+
+    cairo_translate (cr, WIDTH, 0);
+
+    /* no overlap, but the bounding boxes must */
+    cairo_save (cr);
+    cairo_translate (cr, -WIDTH/2 + 2, -2);
+    diamond (cr);
+    cairo_clip (cr);
+    cairo_translate (cr, WIDTH - 4, 4);
+    diamond (cr);
+    cairo_clip (cr);
+    cairo_paint (cr);
+    cairo_restore (cr);
+
+    cairo_translate (cr, WIDTH, 0);
+
+    /* completely disjoint */
+    cairo_save (cr);
+    cairo_translate (cr, -WIDTH/2 - 1, 0);
+    diamond (cr);
+    cairo_clip (cr);
+    cairo_translate (cr, WIDTH + 2, 0);
+    diamond (cr);
+    cairo_clip (cr);
+    cairo_paint (cr);
+    cairo_restore (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (clip_polygons,
+           "Test drawing through through an intersection of polygons",
+           "clip", /* keywords */
+           "target=raster", /* requirements */
+           4*WIDTH, HEIGHT,
+           NULL, draw)
diff --git a/test/clip-push-group.c b/test/clip-push-group.c
new file mode 100644 (file)
index 0000000..4effb0a
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * Copyright © 2006 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ */
+
+/* A test for the crash described here:
+ *
+ *     http://lists.freedesktop.org/archives/cairo/2006-August/007698.html
+ *
+ * The triggering condition for this bug should be setting a
+ * surface-based clip and then calling cairo_push_group.
+ */
+
+#include "cairo-test.h"
+
+#define SIZE 10
+#define PAD 2
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    /* An overly complex way of drawing a blue circle onto a red
+     * background, to trigger the bug. */
+    cairo_set_source_rgb (cr, 1, 0, 0); /* red */
+    cairo_paint (cr);
+
+    cairo_arc (cr,
+              SIZE / 2, SIZE / 2,
+              SIZE / 2 - PAD,
+              0, 2 * M_PI);
+    cairo_clip (cr);
+
+    cairo_push_group (cr);
+    cairo_set_source_rgb (cr, 0, 0, 1); /* blue */
+    cairo_paint (cr);
+    cairo_pop_group_to_source (cr);
+    cairo_paint (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (clip_push_group,
+           "Test that push_group doesn't crash after setting a surface-based clip",
+           "clip", /* keywords */
+           NULL, /* requirements */
+           SIZE, SIZE,
+           NULL, draw)
diff --git a/test/clip-rectilinear.c b/test/clip-rectilinear.c
new file mode 100644 (file)
index 0000000..98db4a2
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2011 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+
+#define SIZE 120
+
+static void L(cairo_t *cr, int w, int h)
+{
+       cairo_move_to (cr, 0, 0);
+       cairo_line_to (cr, 0, h);
+       cairo_line_to (cr, w, h);
+       cairo_line_to (cr, w, h/2);
+       cairo_line_to (cr, w/2, h/2);
+       cairo_line_to (cr, w/2, 0);
+       cairo_close_path (cr);
+}
+
+static void LL(cairo_t *cr, int w, int h)
+{
+    cairo_save (cr);
+
+    /* aligned */
+    cairo_rectangle (cr, 0, 0, w, h);
+    cairo_clip (cr);
+    L (cr, w, h);
+    cairo_clip (cr);
+    cairo_paint (cr);
+    cairo_reset_clip (cr);
+
+    /* unaligned */
+    cairo_translate (cr, w+.25, .25);
+    cairo_rectangle (cr, 0, 0, w, h);
+    cairo_clip (cr);
+    L (cr, w, h);
+    cairo_clip (cr);
+    cairo_paint (cr);
+    cairo_reset_clip (cr);
+
+    cairo_restore (cr);
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    int w = SIZE/2, h = SIZE/2;
+
+    cairo_paint (cr); /* opaque background */
+
+    cairo_set_source_rgb (cr, 1, 0, 0);
+    LL (cr, w, h);
+
+    cairo_translate (cr, 0, h);
+    cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
+
+    cairo_set_source_rgb (cr, 0, 0, 1);
+    LL (cr, w, h);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (clip_rectilinear,
+           "Test handling of rectilinear clipping",
+           "clip", /* keywords */
+           NULL, /* requirements */
+           SIZE, SIZE,
+           NULL, draw)
diff --git a/test/clip-shape.c b/test/clip-shape.c
new file mode 100644 (file)
index 0000000..6a74a6e
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2010 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+/* Adapted from a bug report by <cairouser@yahoo.com> */
+
+#include "cairo-test.h"
+
+static const struct xy {
+   double x;
+   double y;
+} gp[] = {
+    { 100, 250 },
+    { 100, 100 },
+    { 150, 230 },
+    { 239, 100 },
+    { 239, 250 },
+};
+
+static const double vp[3] = { 100, 144, 238.5 };
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    int i;
+
+    cairo_paint (cr); /* opaque background */
+
+    for (i = 0; i < 5; ++i)
+       cairo_line_to (cr, gp[i].x, gp[i].y);
+    cairo_close_path (cr);
+
+    cairo_set_source_rgb (cr, 1, 0, 0);
+    cairo_set_line_width (cr, 1.5);
+    cairo_stroke_preserve (cr);
+    cairo_clip (cr);
+
+    for (i = 1; i < 3; ++i) {
+       double x1 = vp[i - 1];
+       double x2 = vp[i];
+
+       cairo_move_to (cr, x1, 0);
+       cairo_line_to (cr, x1, height);
+       cairo_line_to (cr, x2, height);
+       cairo_line_to (cr, x2, 0);
+       cairo_close_path (cr);
+
+       if (i & 1)
+           cairo_set_source_rgb (cr, 0, 1, 0);
+       else
+           cairo_set_source_rgb (cr, 1, 1, 0);
+
+       cairo_fill (cr);
+    }
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (clip_shape,
+           "Test handling of clipping with a non-aligned shape",
+           "clip", /* keywords */
+           NULL, /* requirements */
+           400, 300,
+           NULL, draw)
diff --git a/test/clip-stroke-no-op.c b/test/clip-stroke-no-op.c
new file mode 100644 (file)
index 0000000..8eb9171
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2009 Benjamin Otte
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Benjamin Otte <otte@gnome.org>
+ */
+
+#include "cairo-test.h"
+
+#define WIDTH 50
+#define HEIGHT 50
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    /* Neutral gray background */
+    cairo_set_source_rgb (cr, 0.5, 0.5, 0.5);
+    cairo_paint (cr);
+
+    /* remove this clip operation and everything works */
+    cairo_rectangle (cr, 10, 10, 30, 30);
+    cairo_clip (cr);
+
+    /* remove this no-op and everything works */
+    cairo_stroke (cr);
+
+    /* make the y coordinates integers and everything works */
+    cairo_move_to (cr, 20, 20.101562);
+    cairo_line_to (cr, 30, 20.101562);
+
+    /* This clip operation should fail to work. But with cairo 1.9, if all the 
+     * 3 cases above happen, the clip will not work and the paint will happen.
+     */
+    cairo_save (cr); {
+       cairo_set_source_rgba (cr, 1, 0.5, 0.5, 1);
+       cairo_clip_preserve (cr);
+       cairo_paint (cr);
+    } cairo_restore (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (clip_stroke_no_op,
+           "Exercises a bug found by Benjamin Otte whereby a no-op clip is nullified by a stroke",
+           "clip, stroke", /* keywords */
+           NULL, /* requirements */
+           WIDTH, HEIGHT,
+           NULL, draw)
diff --git a/test/clip-stroke.c b/test/clip-stroke.c
new file mode 100644 (file)
index 0000000..9714dd0
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+ * Copyright 2009 Chris Wilson
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Chris Wilson not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Chris Wilson makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * CHRIS WILSON DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL CHRIS WILSON BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+
+#define WIDTH 40
+#define HEIGHT 40
+
+static void
+shapes (cairo_t *cr)
+{
+    cairo_set_source_rgb (cr, 1, 0, 0);
+    cairo_paint (cr);
+
+    cairo_set_source_rgb (cr, 0, 0.7, 0);
+    cairo_arc (cr, 10, 10, 7.5, 0, 2 * M_PI);
+    cairo_stroke (cr);
+
+    cairo_set_source_rgb (cr, 0, 0.7, 0.7);
+    cairo_arc (cr, 10, 10, 25, 0, 2 * M_PI);
+    cairo_stroke (cr);
+    cairo_rectangle (cr, -5, -5, 30, 30);
+    cairo_stroke (cr);
+
+    cairo_set_source_rgb (cr, 0.7, 0.7, 0);
+    cairo_save (cr);
+    cairo_translate (cr, 10, 10);
+    cairo_rotate (cr, M_PI/4);
+    cairo_translate (cr, -10, -10);
+    cairo_rectangle (cr, -5, -5, 30, 30);
+    cairo_stroke (cr);
+    cairo_restore (cr);
+
+    cairo_set_source_rgb (cr, 0.7, 0.0, 0.7);
+    cairo_move_to (cr, 15, -10);
+    cairo_line_to (cr, 30, 10);
+    cairo_line_to (cr, 15, 30);
+    cairo_stroke (cr);
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_paint (cr);
+
+    cairo_translate (cr, 10, 10);
+
+    /* simple clip */
+    cairo_save (cr);
+    cairo_rectangle (cr, 0, 0, 20, 20);
+    cairo_clip (cr);
+    shapes (cr);
+    cairo_restore (cr);
+
+    cairo_translate (cr, WIDTH, 0);
+
+    /* unaligned clip */
+    cairo_save (cr);
+    cairo_rectangle (cr, 0.5, 0.5, 20, 20);
+    cairo_clip (cr);
+    shapes (cr);
+    cairo_restore (cr);
+
+    cairo_translate (cr, -WIDTH, HEIGHT);
+
+    /* aligned-clip */
+    cairo_save (cr);
+    cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD);
+    cairo_rectangle (cr, 0, 0, 20, 20);
+    cairo_rectangle (cr, 3, 3, 10, 10);
+    cairo_rectangle (cr, 7, 7, 10, 10);
+    cairo_clip (cr);
+    shapes (cr);
+    cairo_restore (cr);
+
+    cairo_translate (cr, WIDTH, 0);
+
+    /* force a clip-mask */
+    cairo_save (cr);
+    cairo_arc (cr, 10, 10, 10, 0, 2 * M_PI);
+    cairo_new_sub_path (cr);
+    cairo_arc_negative (cr, 10, 10, 5, 2 * M_PI, 0);
+    cairo_new_sub_path (cr);
+    cairo_arc (cr, 10, 10, 2, 0, 2 * M_PI);
+    cairo_clip (cr);
+    shapes (cr);
+    cairo_restore (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (clip_stroke,
+           "Tests stroke through complex clips.",
+           "clip, stroke", /* keywords */
+           NULL, /* requirements */
+           2 * WIDTH, 2* HEIGHT,
+           NULL, draw)
+
diff --git a/test/clip-text.c b/test/clip-text.c
new file mode 100644 (file)
index 0000000..ed8e107
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2009 Chris Wilson
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Chris Wilson not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Chris Wilson makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * CHRIS WILSON DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL CHRIS WILSON BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+
+#define WIDTH 20
+#define HEIGHT 20
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    const char *cairo = "Cairo";
+    cairo_text_extents_t extents;
+    double x0, y0;
+
+    cairo_text_extents (cr, cairo, &extents);
+    x0 = WIDTH/2. - (extents.width/2. + extents.x_bearing);
+    y0 = HEIGHT/2. - (extents.height/2. + extents.y_bearing);
+
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_paint (cr);
+
+    /* aligned-clip */
+    cairo_save (cr);
+    cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD);
+    cairo_rectangle (cr, 0, 0, 20, 20);
+    cairo_rectangle (cr, 3, 3, 10, 10);
+    cairo_rectangle (cr, 7, 7, 10, 10);
+    cairo_clip (cr);
+
+    cairo_set_source_rgb (cr, 0.7, 0, 0);
+    cairo_paint (cr);
+    cairo_set_source_rgb (cr, 0, 0, 0);
+
+    cairo_move_to (cr, x0, y0);
+    cairo_show_text (cr, cairo);
+    cairo_restore (cr);
+
+    cairo_translate (cr, WIDTH, 0);
+
+    /* force a clip-mask */
+    cairo_save (cr);
+    cairo_arc (cr, 10, 10, 10, 0, 2 * M_PI);
+    cairo_new_sub_path (cr);
+    cairo_arc_negative (cr, 10, 10, 5, 2 * M_PI, 0);
+    cairo_new_sub_path (cr);
+    cairo_arc (cr, 10, 10, 2, 0, 2 * M_PI);
+    cairo_clip (cr);
+
+    cairo_set_source_rgb (cr, 0, 0, 0.7);
+    cairo_paint (cr);
+    cairo_set_source_rgb (cr, 0, 0, 0);
+
+    cairo_move_to (cr, x0, y0);
+    cairo_show_text (cr, cairo);
+    cairo_restore (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (clip_text,
+           "Tests drawing text through complex clips.",
+           "clip, text", /* keywords */
+           NULL, /* requirements */
+           2 * WIDTH, HEIGHT,
+           NULL, draw)
diff --git a/test/clip-twice-rectangle.c b/test/clip-twice-rectangle.c
new file mode 100644 (file)
index 0000000..28f16ec
--- /dev/null
@@ -0,0 +1,70 @@
+/*\r
+ * Copyright © 2010 Mozilla Corporation\r
+ * Copyright © 2010 Intel Corporation\r
+ *\r
+ * Permission is hereby granted, free of charge, to any person\r
+ * obtaining a copy of this software and associated documentation\r
+ * files (the "Software"), to deal in the Software without\r
+ * restriction, including without limitation the rights to use, copy,\r
+ * modify, merge, publish, distribute, sublicense, and/or sell copies\r
+ * of the Software, and to permit persons to whom the Software is\r
+ * furnished to do so, subject to the following conditions:\r
+ *\r
+ * The above copyright notice and this permission notice shall be\r
+ * included in all copies or substantial portions of the Software.\r
+ *\r
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
+ * SOFTWARE.\r
+ *\r
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>\r
+ */\r
+\r
+#include "cairo-test.h"\r
+\r
+static cairo_test_status_t\r
+draw (cairo_t *cr, int width, int height)\r
+{\r
+    cairo_surface_t *mask;\r
+    cairo_t *cr2;\r
+\r
+    cairo_set_source_rgb (cr, 0, 1, 0);\r
+    cairo_paint (cr);\r
+\r
+    /* clip twice, note that the intersection is smaller then the extents */\r
+    cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD);\r
+    cairo_rectangle (cr, 10, 10, 80, 80);\r
+    cairo_rectangle (cr, 20, 20, 60, 60);\r
+    cairo_clip (cr);\r
+\r
+    cairo_rectangle (cr, 0, 40, 40, 30);\r
+    cairo_clip (cr);\r
+\r
+    /* and exercise the bug found by Jeff Muizelaar */\r
+    mask = cairo_surface_create_similar (cairo_get_target (cr),\r
+                                        CAIRO_CONTENT_ALPHA,\r
+                                        width-20, height-20);\r
+    cr2 = cairo_create (mask);\r
+    cairo_surface_destroy (mask);\r
+\r
+    cairo_set_source_rgba (cr2, 1, 1, 1, 1);\r
+    cairo_paint (cr2);\r
+\r
+    cairo_set_source_rgb (cr, 1, 0, 0);\r
+    cairo_mask_surface (cr, cairo_get_target (cr2), 0, 0);\r
+    cairo_destroy (cr2);\r
+\r
+    return CAIRO_TEST_SUCCESS;\r
+}\r
+\r
+CAIRO_TEST (clip_twice_rectangle,\r
+           "Tests clipping twice using rectangles",\r
+           "clip", /* keywords */\r
+           NULL, /* requirements */\r
+           100, 100,\r
+           NULL, draw)\r
diff --git a/test/clip-twice.c b/test/clip-twice.c
new file mode 100644 (file)
index 0000000..641b551
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Kristian Høgsberg <krh@redhat.com>
+ */
+
+#include "cairo-test.h"
+
+#define WIDTH 64
+#define HEIGHT 64
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_new_path (cr);
+    cairo_arc (cr, WIDTH / 2, HEIGHT / 2, WIDTH / 3, 0, 2 * M_PI);
+    cairo_clip (cr);
+
+    cairo_new_path (cr);
+    cairo_move_to (cr, 0, 0);
+    cairo_line_to (cr, WIDTH / 4, HEIGHT / 2);
+    cairo_line_to (cr, 0, HEIGHT);
+    cairo_line_to (cr, WIDTH, HEIGHT);
+    cairo_line_to (cr, 3 * WIDTH / 4, HEIGHT / 2);
+    cairo_line_to (cr, WIDTH, 0);
+    cairo_close_path (cr);
+    cairo_clip (cr);
+
+    cairo_set_source_rgb (cr, 0, 0, 0.6);
+
+    cairo_new_path (cr);
+    cairo_move_to (cr, 0, 0);
+    cairo_line_to (cr, 0, HEIGHT);
+    cairo_line_to (cr, WIDTH / 2, 3 * HEIGHT / 4);
+    cairo_line_to (cr, WIDTH, HEIGHT);
+    cairo_line_to (cr, WIDTH, 0);
+    cairo_line_to (cr, WIDTH / 2, HEIGHT / 4);
+    cairo_close_path (cr);
+    cairo_fill (cr);
+
+    cairo_new_path (cr);
+    cairo_arc (cr, WIDTH / 2, HEIGHT / 2, WIDTH / 5, 0, 2 * M_PI);
+    cairo_clip (cr);
+    cairo_set_source_rgb (cr, 1, 1, 0);
+    cairo_paint (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (clip_twice,
+           "Verifies that the clip mask is updated correctly when it constructed by setting the clip path twice.",
+           "clip", /* keywords */
+           NULL, /* requirements */
+           WIDTH, HEIGHT,
+           NULL, draw)
diff --git a/test/clip-unbounded.c b/test/clip-unbounded.c
new file mode 100644 (file)
index 0000000..cd1c602
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * Copyright © 2009 Chris Wilson
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Chris Wilson not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Chris Wilson makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * CHRIS WILSON DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL CHRIS WILSON BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+
+#define SIZE 10
+
+static cairo_surface_t *
+create_source (cairo_surface_t *target)
+{
+    cairo_surface_t *similar;
+    cairo_t *cr;
+
+    similar = cairo_surface_create_similar (target,
+                                           CAIRO_CONTENT_COLOR, SIZE/2, SIZE);
+    cr = cairo_create (similar);
+    cairo_surface_destroy (similar);
+
+    cairo_set_source_rgb (cr, 1, 0, 0);
+    cairo_paint (cr);
+
+    similar = cairo_surface_reference (cairo_get_target (cr));
+    cairo_destroy (cr);
+
+    return similar;
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_surface_t *source;
+
+    cairo_set_source_rgb (cr, 0, 0, 1);
+    cairo_paint (cr);
+
+    cairo_rectangle (cr, 0, 0, SIZE/2, SIZE);
+    cairo_clip (cr);
+
+    /* Draw a source rectangle outside the image, the effect should be to
+     * clear only within the clip region.
+     */
+    source = create_source (cairo_get_target (cr));
+    cairo_set_source_surface (cr, source, SIZE/2, 0);
+    cairo_surface_destroy (source);
+
+    cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+    cairo_paint (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (clip_unbounded,
+           "Test handling of an unbounded fill outside the clip region",
+           "clip", /* keywords */
+           NULL, /* requirements */
+           SIZE, SIZE,
+           NULL, draw)
+
diff --git a/test/clip-zero.c b/test/clip-zero.c
new file mode 100644 (file)
index 0000000..5afd1b5
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * Copyright © 2007 Mozilla Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Vladimir Vukicevic <vladimir@pobox.com>
+ */
+
+#include "cairo-test.h"
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_pattern_t *pat;
+    cairo_surface_t *surf;
+
+    cairo_new_path (cr);
+    cairo_rectangle (cr, 0, 0, 0, 0);
+    cairo_clip (cr);
+
+    cairo_push_group (cr);
+
+    cairo_set_source_rgb (cr, 1, 0, 0);
+
+    cairo_new_path (cr);
+    cairo_rectangle (cr, -10, 10, 20, 20);
+    cairo_fill_preserve (cr);
+    cairo_stroke_preserve (cr);
+    cairo_paint (cr);
+
+    cairo_select_font_face (cr, "sans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
+    cairo_show_text (cr, "ABC");
+
+    cairo_mask (cr, cairo_get_source (cr));
+
+    surf = cairo_surface_create_similar (cairo_get_group_target (cr), CAIRO_CONTENT_COLOR_ALPHA, 0, 0);
+    pat = cairo_pattern_create_for_surface (surf);
+    cairo_surface_destroy (surf);
+
+    cairo_mask (cr, pat);
+    cairo_pattern_destroy (pat);
+
+    cairo_pop_group_to_source (cr);
+    cairo_paint (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (clip_zero,
+           "Verifies that 0x0 surfaces or clips don't cause problems.",
+           "clip", /* keywords */
+           NULL, /* requirements */
+           0, 0,
+           NULL, draw)
diff --git a/test/clipped-group.c b/test/clipped-group.c
new file mode 100644 (file)
index 0000000..a66f357
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * Copyright © 2008 Mozilla Corporation
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Mozilla Corporation not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Mozilla Corporation makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * MOZILLA CORPORATION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL MOZILLA CORPORATION BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Jeff Muizelaar
+ */
+
+#include "cairo-test.h"
+
+#define WIDTH 60
+#define HEIGHT 70
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    /* fill with black so we don't need an rgb test case */
+    cairo_set_source_rgb (cr, 0, 0, 0);
+    cairo_paint (cr);
+
+    /* setting a scale will ensure that the device offset is transformed */
+    cairo_scale (cr, 2.1, 2.8);
+    cairo_set_source_rgb (cr, 1, .5,.4);
+
+    /* all rectangles should look the same */
+
+    /* plain rectangle */
+    cairo_rectangle (cr, 4, 4, 8, 8);
+    cairo_fill (cr);
+
+    cairo_translate (cr, 10, 0);
+
+    /* clipped rectangle */
+    cairo_save (cr);
+    cairo_rectangle (cr, 3, 3, 9, 9);
+    cairo_clip (cr);
+    cairo_rectangle (cr, 4, 4, 8, 8);
+    cairo_fill (cr);
+    cairo_restore (cr);
+
+    cairo_translate (cr, 0, 10);
+
+    /* clipped and grouped rectangle */
+    cairo_save (cr);
+    cairo_rectangle (cr, 3, 3, 9, 9);
+    cairo_clip (cr);
+    cairo_push_group (cr);
+    cairo_rectangle (cr, 4, 4, 8, 8);
+    cairo_fill (cr);
+    cairo_pop_group_to_source (cr);
+    cairo_paint (cr);
+    cairo_restore (cr);
+
+    cairo_translate (cr, -10, 0);
+
+    /* grouped rectangle */
+    cairo_push_group (cr);
+    cairo_rectangle (cr, 4, 4, 8, 8);
+    cairo_fill (cr);
+    cairo_pop_group_to_source (cr);
+    cairo_paint (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (clipped_group,
+           "Test that a clipped group ends up in the right place",
+           "clip", /* keywords */
+           NULL, /* requirements */
+           WIDTH, HEIGHT,
+           NULL, draw)
diff --git a/test/clipped-surface.c b/test/clipped-surface.c
new file mode 100644 (file)
index 0000000..a10652d
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2008 Chris Wilson
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Chris Wilson not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Chris Wilson makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * CHRIS WILSON DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL CHRIS WILSON BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+/*
+ * Michael Natterer (mitch) reported a bad regression with post-1.8 trunk
+ * with artifacts drawn whilst repainting exposed areas.
+ */
+
+#include "cairo-test.h"
+
+static const char png_filename[] = "romedalen.png";
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    const cairo_test_context_t *ctx = cairo_test_get_context (cr);
+    cairo_surface_t *image;
+
+    image = cairo_test_create_surface_from_png (ctx, png_filename);
+
+    cairo_set_source_rgb (cr, 1, 0, 0);
+    cairo_paint (cr);
+
+    cairo_rectangle (cr, 20, 20, 10, 10);
+    cairo_clip (cr);
+
+    cairo_set_source_surface (cr, image, 10, 10);
+    cairo_surface_destroy (image);
+
+    cairo_rectangle (cr, 10, 10, 20, 20);
+    cairo_fill (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (clipped_surface,
+           "Tests application of a clip to a source surface",
+           "clip", /* keywords */
+           NULL, /* requirements */
+           40, 40,
+           NULL, draw)
diff --git a/test/clipped-trapezoids-ref.png b/test/clipped-trapezoids-ref.png
new file mode 100644 (file)
index 0000000..3fd300c
Binary files /dev/null and b/test/clipped-trapezoids-ref.png differ
diff --git a/test/clipped-trapezoids.c b/test/clipped-trapezoids.c
new file mode 100644 (file)
index 0000000..1f96daa
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2008 Chris Wilson
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Chris Wilson not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Chris Wilson makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * CHRIS WILSON DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL CHRIS WILSON BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    double dash[2] = { 8, 4 };
+    double radius;
+
+    radius = width;
+    if (height > radius)
+       radius = height;
+
+    /* fill the background using a big circle */
+    cairo_arc (cr, 0, 0, 4 * radius, 0, 2 * M_PI);
+    cairo_fill (cr);
+
+    /* a rotated square - overlapping the corners */
+    cairo_save (cr);
+    cairo_save (cr);
+    cairo_translate (cr, width/2, height/2);
+    cairo_rotate (cr, M_PI/4);
+    cairo_scale (cr, M_SQRT2, M_SQRT2);
+    cairo_rectangle (cr, -width/2, -height/2, width, height);
+    cairo_restore (cr);
+    cairo_set_source_rgba (cr, 0, 1, 0, .5);
+    cairo_set_line_width (cr, radius/2);
+    cairo_stroke (cr);
+    cairo_restore (cr);
+
+    /* and put some circles in the corners */
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_new_sub_path (cr);
+    cairo_arc (cr, 0, 0, radius/4, 0, 2 * M_PI);
+    cairo_new_sub_path (cr);
+    cairo_arc (cr, width, 0, radius/4, 0, 2 * M_PI);
+    cairo_new_sub_path (cr);
+    cairo_arc (cr, width, height, radius/4, 0, 2 * M_PI);
+    cairo_new_sub_path (cr);
+    cairo_arc (cr, 0, height, radius/4, 0, 2 * M_PI);
+    cairo_fill (cr);
+
+    /* a couple of pixel-aligned lines */
+    cairo_set_source_rgb (cr, 0, 0, 1);
+    cairo_move_to (cr, width/2, -height);
+    cairo_rel_line_to (cr, 0, 3*height);
+    cairo_move_to (cr, -width, height/2);
+    cairo_rel_line_to (cr, 3*width, 0);
+    cairo_stroke (cr);
+
+    /* a couple of dashed diagonals */
+    cairo_save (cr);
+    cairo_set_source_rgb (cr, 1, 0, 0);
+    cairo_set_dash (cr, dash, 2, 0);
+    cairo_set_line_width (cr, 4.);
+    cairo_move_to (cr, -width, -height);
+    cairo_line_to (cr, width+width, height+height);
+    cairo_move_to (cr, width+width, -height);
+    cairo_line_to (cr, -width, height+height);
+    cairo_stroke (cr);
+    cairo_restore (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (clipped_trapezoids,
+           "Tests clipping of trapezoids larger than the surface",
+           "clip", /* keywords */
+           NULL, /* requirements */
+           40, 40,
+           NULL, draw)
diff --git a/test/close-path-current-point.c b/test/close-path-current-point.c
new file mode 100644 (file)
index 0000000..35f8d42
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * Copyright © 2009 Nis Martensen
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without fee,
+ * provided that the above copyright notice appear in all copies and
+ * that both that copyright notice and this permission notice appear in
+ * supporting documentation, and that the name of the copyright holder
+ * not be used in advertising or publicity pertaining to distribution of
+ * the software without specific, written prior permission. The
+ * copyright holder makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without
+ * express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDER DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
+ * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Nis Martensen <nis.martensen@web.de>
+ */
+
+#include "cairo-test.h"
+
+#define SIZE 20
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    /* We draw in the default black, so paint white first. */
+    cairo_save (cr);
+    cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); /* white */
+    cairo_paint (cr);
+    cairo_restore (cr);
+
+    /* subpath starts with cairo_move_to */
+    cairo_new_sub_path (cr);
+    cairo_move_to (cr, SIZE, SIZE);
+    cairo_rel_line_to (cr, SIZE, 0);
+    cairo_rel_line_to (cr, 0, SIZE);
+    cairo_close_path (cr);
+    cairo_rel_line_to (cr, 0.5 * SIZE, SIZE);
+
+    /* subpath starts with cairo_line_to */
+    cairo_new_sub_path (cr);
+    cairo_line_to (cr, SIZE, 3 * SIZE);
+    cairo_rel_line_to (cr, SIZE, 0);
+    cairo_rel_line_to (cr, 0, SIZE);
+    cairo_close_path (cr);
+    cairo_rel_line_to (cr, 0, SIZE);
+
+    /* subpath starts with cairo_curve_to */
+    cairo_new_sub_path (cr);
+    cairo_curve_to (cr,
+                   SIZE, 5 * SIZE,
+                   1.5 * SIZE, 6 * SIZE,
+                   2 * SIZE, 5 * SIZE);
+    cairo_rel_line_to (cr, 0, SIZE);
+    cairo_close_path (cr);
+    cairo_rel_line_to (cr, -0.5 * SIZE, SIZE);
+
+    /* subpath starts with cairo_arc */
+    cairo_new_sub_path (cr);
+    cairo_arc (cr,
+              1.5 * SIZE, 7 * SIZE,
+              0.5 * SIZE,
+              M_PI, 2 * M_PI);
+    cairo_rel_line_to (cr, 0, SIZE);
+    cairo_close_path (cr);
+    cairo_rel_line_to (cr, -0.7 * SIZE, 0.7 * SIZE);
+
+    /* subpath starts with cairo_arc_negative */
+    cairo_new_sub_path (cr);
+    cairo_arc_negative (cr,
+                       1.5 * SIZE, 9 * SIZE,
+                       0.5 * SIZE,
+                       M_PI, 2 * M_PI);
+    cairo_rel_line_to (cr, 0, SIZE);
+    cairo_close_path (cr);
+    cairo_rel_line_to (cr, -0.8 * SIZE, 0.3 * SIZE);
+
+    cairo_stroke (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (close_path_current_point,
+           "Test some corner cases related to cairo path operations and the current point",
+           "path", /* keywords */
+           NULL, /* requirements */
+           3 * SIZE, 11 * SIZE,
+           NULL, draw)
diff --git a/test/close-path.c b/test/close-path.c
new file mode 100644 (file)
index 0000000..41b237e
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * Copyright © 2006 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ */
+
+#include <stdlib.h>
+#include "cairo-test.h"
+
+static cairo_test_draw_function_t draw;
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_path_t *path;
+
+    /* We draw in the default black, so paint white first. */
+    cairo_save (cr);
+    cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); /* white */
+    cairo_paint (cr);
+    cairo_restore (cr);
+
+    /* This curious approach for drawing a circle (starting with a
+     * closed arc) exercises a bug in which the "last move point" was
+     * not being set so the close_path closes to (0,0). */
+    cairo_arc (cr, 8, 8, 4, 0, M_PI);
+    cairo_close_path (cr);
+    cairo_arc (cr, 8, 8, 4, M_PI, 2 * M_PI);
+
+    cairo_fill (cr);
+
+    cairo_translate (cr, 16, 0);
+
+    /* Here a curve immediately after a close_to will begin from (0,0)
+     * when the path is obtained with cairo_copy_path_flat. */
+    cairo_move_to (cr, 8, 4);
+    cairo_arc_negative (cr, 8, 8, 4, 3 * M_PI / 2.0, M_PI / 2.0);
+    cairo_close_path (cr);
+    cairo_curve_to (cr,
+                   12, 4,
+                   12, 12,
+                   8, 12);
+
+    path = cairo_copy_path_flat (cr);
+    cairo_new_path (cr);
+    cairo_append_path (cr, path);
+    cairo_path_destroy (path);
+
+    cairo_fill (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (close_path,
+           "Test some corner cases related to cairo_close_path",
+           "path", /* keywords */
+           NULL, /* requirements */
+           32, 16,
+           NULL, draw)
diff --git a/test/composite-integer-translate-over-repeat.c b/test/composite-integer-translate-over-repeat.c
new file mode 100644 (file)
index 0000000..9a60d9a
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * Copyright © 2007 Mozilla Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Vladimir Vukicevic <vladimir@pobox.com>
+ */
+
+#include <math.h>
+#include "cairo-test.h"
+#include <stdio.h>
+
+#define SIZE 100
+#define SIZE2 20
+#define OFFSET 10
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_surface_t *image;
+    cairo_pattern_t *pat;
+    cairo_t *cr2;
+
+    image = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, SIZE2, SIZE2);
+    cr2 = cairo_create (image);
+    cairo_surface_destroy (image);
+
+    cairo_set_source_rgba (cr2, 1, 0, 0, 1);
+    cairo_rectangle (cr2, 0, 0, SIZE2/2, SIZE2/2);
+    cairo_fill (cr2);
+    cairo_set_source_rgba (cr2, 0, 1, 0, 1);
+    cairo_rectangle (cr2, SIZE2/2, 0, SIZE2/2, SIZE2/2);
+    cairo_fill (cr2);
+    cairo_set_source_rgba (cr2, 0, 0, 1, 1);
+    cairo_rectangle (cr2, 0, SIZE2/2, SIZE2/2, SIZE2/2);
+    cairo_fill (cr2);
+    cairo_set_source_rgba (cr2, 1, 1, 0, 1);
+    cairo_rectangle (cr2, SIZE2/2, SIZE2/2, SIZE2/2, SIZE2/2);
+    cairo_fill (cr2);
+
+    pat = cairo_pattern_create_for_surface (cairo_get_target (cr2));
+    cairo_destroy (cr2);
+
+    cairo_pattern_set_extend (pat, CAIRO_EXTEND_REPEAT);
+
+    cairo_set_source_rgba (cr, 0, 0, 0, 1);
+    cairo_rectangle (cr, 0, 0, SIZE, SIZE);
+    cairo_fill (cr);
+
+    cairo_translate (cr, OFFSET, OFFSET);
+    cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
+    cairo_set_source (cr, pat);
+    cairo_rectangle (cr, 0, 0, SIZE - OFFSET, SIZE - OFFSET);
+    cairo_fill (cr);
+
+    cairo_pattern_destroy (pat);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (composite_integer_translate_over_repeat,
+           "Test simple compositing: integer-translation 32->32 OVER, with repeat",
+           "composite", /* keywords */
+           NULL, /* requirements */
+           SIZE, SIZE,
+           NULL, draw)
diff --git a/test/composite-integer-translate-over.c b/test/composite-integer-translate-over.c
new file mode 100644 (file)
index 0000000..8c6b9c2
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Copyright © 2006 Mozilla Corporation
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Mozilla Corporation not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Mozilla Corporation makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * MOZILLA CORPORATION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL MOZILLA CORPORATION BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Vladimir Vukicevic <vladimir@pobox.com>
+ */
+#include <math.h>
+#include "cairo-test.h"
+#include <stdio.h>
+
+#define SIZE 100
+#define OFFSET 10
+static const char *png_filename = "romedalen.png";
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    const cairo_test_context_t *ctx = cairo_test_get_context (cr);
+    cairo_surface_t *image;
+
+    image = cairo_test_create_surface_from_png (ctx, png_filename);
+
+    cairo_set_source_rgba (cr, 0, 0, 0, 1);
+    cairo_rectangle (cr, 0, 0, SIZE, SIZE);
+    cairo_fill (cr);
+
+    cairo_translate (cr, OFFSET, OFFSET);
+    cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
+    cairo_set_source_surface (cr, image, 0, 0);
+    cairo_rectangle (cr, 0, 0, (SIZE-OFFSET), (SIZE-OFFSET));
+    cairo_fill (cr);
+
+    cairo_surface_destroy (image);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (composite_integer_translate_over,
+           "Test simple compositing: integer-translation 32->32 OVER",
+           "composite", /* keywords */
+           NULL, /* requirements */
+           SIZE, SIZE,
+           NULL, draw)
diff --git a/test/composite-integer-translate-source.c b/test/composite-integer-translate-source.c
new file mode 100644 (file)
index 0000000..a05133c
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Copyright © 2007 Mozilla Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Vladimir Vukicevic <vladimir@pobox.com>
+ */
+
+#include <math.h>
+#include "cairo-test.h"
+#include <stdio.h>
+
+#define SIZE 100
+#define OFFSET 10
+
+static const char *png_filename = "romedalen.png";
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    const cairo_test_context_t *ctx = cairo_test_get_context (cr);
+    cairo_surface_t *image;
+
+    image = cairo_test_create_surface_from_png (ctx, png_filename);
+
+    cairo_set_source_rgba (cr, 0, 0, 0, 1);
+    cairo_rectangle (cr, 0, 0, SIZE, SIZE);
+    cairo_fill (cr);
+
+    cairo_translate (cr, OFFSET, OFFSET);
+    cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+    cairo_set_source_surface (cr, image, 0, 0);
+    cairo_rectangle (cr, 0, 0, SIZE - OFFSET, SIZE - OFFSET);
+    cairo_fill (cr);
+
+    cairo_surface_destroy (image);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (composite_integer_translate_source,
+           "Test simple compositing: integer-translation 32->32 SOURCE",
+           "composite", /* keywords */
+           NULL, /* requirements */
+           SIZE, SIZE,
+           NULL, draw)
diff --git a/test/copy-path.c b/test/copy-path.c
new file mode 100644 (file)
index 0000000..c5f9398
--- /dev/null
@@ -0,0 +1,314 @@
+/*
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ */
+
+#include <stdlib.h>
+#include "cairo-test.h"
+
+static void
+scale_by_two (double *x, double *y)
+{
+    *x = *x * 2.0;
+    *y = *y * 2.0;
+}
+
+typedef void (*munge_func_t) (double *x, double *y);
+
+static void
+munge_and_set_path (cairo_t     *cr,
+                   cairo_path_t *path,
+                   munge_func_t  munge)
+{
+    int i;
+    cairo_path_data_t *p;
+    double x1, y1, x2, y2, x3, y3;
+
+    if (path->status) {
+       cairo_append_path (cr, path);
+       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:
+           x1 = p[1].point.x; y1 = p[1].point.y;
+           (munge) (&x1, &y1);
+           cairo_move_to (cr, x1, y1);
+           break;
+       case CAIRO_PATH_LINE_TO:
+           x1 = p[1].point.x; y1 = p[1].point.y;
+           (munge) (&x1, &y1);
+           cairo_line_to (cr, x1, y1);
+           break;
+       case CAIRO_PATH_CURVE_TO:
+           x1 = p[1].point.x; y1 = p[1].point.y;
+           x2 = p[2].point.x; y2 = p[2].point.y;
+           x3 = p[3].point.x; y3 = p[3].point.y;
+           (munge) (&x1, &y1);
+           (munge) (&x2, &y2);
+           (munge) (&x3, &y3);
+           cairo_curve_to (cr,
+                           x1, y1,
+                           x2, y2,
+                           x3, y3);
+           break;
+       case CAIRO_PATH_CLOSE_PATH:
+           cairo_close_path (cr);
+           break;
+       }
+    }
+}
+
+static void
+make_path (cairo_t *cr)
+{
+    cairo_rectangle (cr, 0, 0, 5, 5);
+    cairo_move_to (cr, 15, 2.5);
+    cairo_arc (cr, 12.5, 2.5, 2.5, 0, 2 * M_PI);
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    const cairo_test_context_t *ctx = cairo_test_get_context (cr);
+    cairo_path_t *path;
+    cairo_t *cr_error;
+
+    /* Ensure that calling cairo_copy_path on an in-error cairo_t will
+     * propagate the error. */
+    cr_error = cairo_create (NULL);
+    path = cairo_copy_path (cr_error);
+    if (path->status != CAIRO_STATUS_NULL_POINTER) {
+       cairo_test_log (ctx,
+                       "Error: cairo_copy_path returned status of %s rather than propagating %s\n",
+                       cairo_status_to_string (path->status),
+                       cairo_status_to_string (CAIRO_STATUS_NULL_POINTER));
+       cairo_path_destroy (path);
+       cairo_destroy (cr_error);
+       return CAIRO_TEST_FAILURE;
+    }
+    cairo_path_destroy (path);
+
+    path = cairo_copy_path_flat (cr_error);
+    if (path->status != CAIRO_STATUS_NULL_POINTER) {
+       cairo_test_log (ctx,
+                       "Error: cairo_copy_path_flat returned status of %s rather than propagating %s\n",
+                       cairo_status_to_string (path->status),
+                       cairo_status_to_string (CAIRO_STATUS_NULL_POINTER));
+       cairo_path_destroy (path);
+       cairo_destroy (cr_error);
+       return CAIRO_TEST_FAILURE;
+    }
+    cairo_path_destroy (path);
+
+    cairo_destroy (cr_error);
+
+    /* first check that we can copy an empty path */
+    cairo_new_path (cr);
+    path = cairo_copy_path (cr);
+    if (path->status != CAIRO_STATUS_SUCCESS) {
+       cairo_status_t status = path->status;
+       cairo_test_log (ctx,
+                       "Error: cairo_copy_path returned status of %s\n",
+                       cairo_status_to_string (status));
+       cairo_path_destroy (path);
+       return cairo_test_status_from_status (ctx, status);
+    }
+    if (path->num_data != 0) {
+       cairo_test_log (ctx,
+                       "Error: cairo_copy_path did not copy an empty path, returned path contains %d elements\n",
+                       path->num_data);
+       cairo_path_destroy (path);
+       return CAIRO_TEST_FAILURE;
+    }
+    cairo_append_path (cr, path);
+    cairo_path_destroy (path);
+    if (cairo_status (cr) != CAIRO_STATUS_SUCCESS) {
+       cairo_test_log (ctx,
+                       "Error: cairo_append_path failed with a copy of an empty path, returned status of %s\n",
+                       cairo_status_to_string (cairo_status (cr)));
+       return cairo_test_status_from_status (ctx, cairo_status (cr));
+    }
+
+    /* We draw in the default black, so paint white first. */
+    cairo_save (cr);
+    cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); /* white */
+    cairo_paint (cr);
+    cairo_restore (cr);
+
+    /* copy path, munge, and fill */
+    cairo_translate (cr, 5, 5);
+    make_path (cr);
+    path = cairo_copy_path (cr);
+
+    cairo_new_path (cr);
+    munge_and_set_path (cr, path, scale_by_two);
+    cairo_path_destroy (path);
+    cairo_fill (cr);
+
+    /* copy flattened path, munge, and fill */
+    cairo_translate (cr, 0, 15);
+    make_path (cr);
+    path = cairo_copy_path_flat (cr);
+
+    cairo_new_path (cr);
+    munge_and_set_path (cr, path, scale_by_two);
+    cairo_path_destroy (path);
+    cairo_fill (cr);
+
+    /* append two copies of path, and fill */
+    cairo_translate (cr, 0, 15);
+    cairo_scale (cr, 2.0, 2.0);
+    make_path (cr);
+    path = cairo_copy_path (cr);
+
+    cairo_new_path (cr);
+    cairo_append_path (cr, path);
+    cairo_translate (cr, 2.5, 2.5);
+    cairo_append_path (cr, path);
+
+    cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD);
+    cairo_fill (cr);
+
+    cairo_path_destroy (path);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+static cairo_test_status_t
+preamble (cairo_test_context_t *ctx)
+{
+    cairo_t *cr;
+    cairo_path_data_t data;
+    cairo_path_t path;
+    cairo_surface_t *surface;
+    cairo_status_t status;
+
+    surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 1, 1);
+    status = cairo_surface_status (surface);
+    if (status) {
+       cairo_surface_destroy (surface);
+       return cairo_test_status_from_status (ctx, status);
+    }
+
+    /* Test a few error cases for cairo_append_path_data */
+#define CAIRO_CREATE() do {\
+    cr = cairo_create (surface); \
+    status = cairo_status (cr); \
+    if (status) { \
+       cairo_destroy (cr); \
+       cairo_surface_destroy (surface); \
+       return cairo_test_status_from_status (ctx, status); \
+    } \
+} while (0)
+    CAIRO_CREATE ();
+    cairo_append_path (cr, NULL);
+    status = cairo_status (cr);
+    cairo_destroy (cr);
+    if (status != CAIRO_STATUS_NULL_POINTER) {
+       cairo_surface_destroy (surface);
+       return cairo_test_status_from_status (ctx, status);
+    }
+
+    CAIRO_CREATE ();
+    path.status = -1;
+    cairo_append_path (cr, &path);
+    status = cairo_status (cr);
+    cairo_destroy (cr);
+    if (status != CAIRO_STATUS_INVALID_STATUS) {
+       cairo_surface_destroy (surface);
+       return cairo_test_status_from_status (ctx, status);
+    }
+
+    CAIRO_CREATE ();
+    path.status = CAIRO_STATUS_NO_MEMORY;
+    cairo_append_path (cr, &path);
+    status = cairo_status (cr);
+    cairo_destroy (cr);
+    if (status != CAIRO_STATUS_NO_MEMORY) {
+       cairo_surface_destroy (surface);
+       return cairo_test_status_from_status (ctx, status);
+    }
+
+    CAIRO_CREATE ();
+    path.data = NULL;
+    path.num_data = 0;
+    path.status = CAIRO_STATUS_SUCCESS;
+    cairo_append_path (cr, &path);
+    status = cairo_status (cr);
+    cairo_destroy (cr);
+    if (status != CAIRO_STATUS_SUCCESS) {
+       cairo_surface_destroy (surface);
+       return cairo_test_status_from_status (ctx, status);
+    }
+
+    CAIRO_CREATE ();
+    path.data = NULL;
+    path.num_data = 1;
+    path.status = CAIRO_STATUS_SUCCESS;
+    cairo_append_path (cr, &path);
+    status = cairo_status (cr);
+    cairo_destroy (cr);
+    if (status != CAIRO_STATUS_NULL_POINTER) {
+       cairo_surface_destroy (surface);
+       return cairo_test_status_from_status (ctx, status);
+    }
+
+    CAIRO_CREATE ();
+    /* Intentionally insert bogus header.length value (otherwise would be 2) */
+    data.header.type = CAIRO_PATH_MOVE_TO;
+    data.header.length = 1;
+    path.data = &data;
+    path.num_data = 1;
+    cairo_append_path (cr, &path);
+    status = cairo_status (cr);
+    cairo_destroy (cr);
+    if (status != CAIRO_STATUS_INVALID_PATH_DATA) {
+       cairo_surface_destroy (surface);
+       return cairo_test_status_from_status (ctx, status);
+    }
+
+    /* And test the degnerate case */
+    CAIRO_CREATE ();
+    path.num_data = 0;
+    cairo_append_path (cr, &path);
+    status = cairo_status (cr);
+    cairo_destroy (cr);
+    if (status != CAIRO_STATUS_SUCCESS) {
+       cairo_surface_destroy (surface);
+       return cairo_test_status_from_status (ctx, status);
+    }
+
+    cairo_surface_destroy (surface);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (copy_path,
+           "Tests calls to path_data functions: cairo_copy_path, cairo_copy_path_flat, and cairo_append_path",
+           "path", /* keywords */
+           NULL, /* requirements */
+           45, 53,
+           preamble, draw)
diff --git a/test/coverage.c b/test/coverage.c
new file mode 100644 (file)
index 0000000..2c037eb
--- /dev/null
@@ -0,0 +1,398 @@
+/*
+ * Copyright 2010 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+
+/* Test the fidelity of the rasterisation, because Cairo is my favourite
+ * driver test suite.
+ */
+
+#define GENERATE_REFERENCE 0
+
+#define WIDTH 256
+#define HEIGHT 40
+
+#include "../src/cairo-fixed-type-private.h"
+#define PRECISION (1 << CAIRO_FIXED_FRAC_BITS)
+
+/* XXX beware multithreading! */
+static uint32_t state;
+
+static uint32_t
+hars_petruska_f54_1_random (void)
+{
+#define rol(x,k) ((x << k) | (x >> (32-k)))
+    return state = (state ^ rol (state, 5) ^ rol (state, 24)) + 0x37798849;
+#undef rol
+}
+
+static double
+random_offset (int range, int precise)
+{
+    double x = hars_petruska_f54_1_random() / (double) UINT32_MAX * range / WIDTH;
+    if (precise)
+       x = floor (x * PRECISION) / PRECISION;
+    return x;
+}
+
+static cairo_test_status_t
+rectangles (cairo_t *cr, int width, int height)
+{
+    int x, y, channel;
+
+    state = 0x12345678;
+
+    cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
+    cairo_paint (cr);
+
+#if GENERATE_REFERENCE
+    for (x = 0; x < WIDTH; x++) {
+       cairo_set_source_rgba (cr, 1, 1, 1, x * x * 1.0 / (WIDTH * WIDTH));
+       cairo_rectangle (cr, x, 0, 1, HEIGHT);
+       cairo_fill (cr);
+    }
+#else
+    cairo_set_operator (cr, CAIRO_OPERATOR_ADD);
+    for (channel = 0; channel < 3; channel++) {
+       switch (channel) {
+       default:
+       case 0: cairo_set_source_rgb (cr, 1.0, 0.0, 0.0); break;
+       case 1: cairo_set_source_rgb (cr, 0.0, 1.0, 0.0); break;
+       case 2: cairo_set_source_rgb (cr, 0.0, 0.0, 1.0); break;
+       }
+
+       for (x = 0; x < WIDTH; x++) {
+           for (y = 0; y < HEIGHT; y++) {
+               double dx = random_offset (WIDTH - x, TRUE);
+               double dy = random_offset (WIDTH - x, TRUE);
+               cairo_rectangle (cr, x + dx, y + dy, x / (double) WIDTH, x / (double) WIDTH);
+           }
+       }
+       cairo_fill (cr);
+    }
+#endif
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+static cairo_test_status_t
+intersecting_quads (cairo_t *cr, int width, int height)
+{
+    int x, y, channel;
+
+    state = 0x12345678;
+
+    cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
+    cairo_paint (cr);
+
+#if GENERATE_REFERENCE
+    for (x = 0; x < WIDTH; x++) {
+       cairo_set_source_rgba (cr, 1, 1, 1, x * x * 0.5 / (WIDTH * WIDTH));
+       cairo_rectangle (cr, x, 0, 1, HEIGHT);
+       cairo_fill (cr);
+    }
+#else
+    cairo_set_operator (cr, CAIRO_OPERATOR_ADD);
+    for (channel = 0; channel < 3; channel++) {
+       switch (channel) {
+       default:
+       case 0: cairo_set_source_rgb (cr, 1.0, 0.0, 0.0); break;
+       case 1: cairo_set_source_rgb (cr, 0.0, 1.0, 0.0); break;
+       case 2: cairo_set_source_rgb (cr, 0.0, 0.0, 1.0); break;
+       }
+
+       for (x = 0; x < WIDTH; x++) {
+           double step = x / (double) WIDTH;
+           for (y = 0; y < HEIGHT; y++) {
+               double dx = random_offset (WIDTH - x, TRUE);
+               double dy = random_offset (WIDTH - x, TRUE);
+               cairo_move_to (cr, x + dx, y + dy);
+               cairo_rel_line_to (cr, step, step);
+               cairo_rel_line_to (cr, 0, -step);
+               cairo_rel_line_to (cr, -step, step);
+               cairo_close_path (cr);
+           }
+       }
+       cairo_fill (cr);
+    }
+#endif
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+static cairo_test_status_t
+intersecting_triangles (cairo_t *cr, int width, int height)
+{
+    int x, y, channel;
+
+    state = 0x12345678;
+
+    cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
+    cairo_paint (cr);
+
+#if GENERATE_REFERENCE
+    for (x = 0; x < WIDTH; x++) {
+       cairo_set_source_rgba (cr, 1, 1, 1, x * 0.75 / WIDTH);
+       cairo_rectangle (cr, x, 0, 1, HEIGHT);
+       cairo_fill (cr);
+    }
+#else
+    cairo_set_operator (cr, CAIRO_OPERATOR_ADD);
+    for (channel = 0; channel < 3; channel++) {
+       switch (channel) {
+       default:
+       case 0: cairo_set_source_rgb (cr, 1.0, 0.0, 0.0); break;
+       case 1: cairo_set_source_rgb (cr, 0.0, 1.0, 0.0); break;
+       case 2: cairo_set_source_rgb (cr, 0.0, 0.0, 1.0); break;
+       }
+
+       for (x = 0; x < WIDTH; x++) {
+           double step = x / (double) WIDTH;
+           for (y = 0; y < HEIGHT; y++) {
+               double dx = random_offset (WIDTH - x, TRUE);
+               double dy = random_offset (WIDTH - x, TRUE);
+
+               /* left */
+               cairo_move_to (cr, x + dx, y + dy);
+               cairo_rel_line_to (cr, 0, step);
+               cairo_rel_line_to (cr, step, 0);
+               cairo_close_path (cr);
+
+               /* right, mirrored */
+               cairo_move_to (cr, x + dx + step, y + dy + step);
+               cairo_rel_line_to (cr, 0, -step);
+               cairo_rel_line_to (cr, -step, step);
+               cairo_close_path (cr);
+           }
+       }
+       cairo_fill (cr);
+    }
+#endif
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+static cairo_test_status_t
+triangles (cairo_t *cr, int width, int height)
+{
+    int x, y, channel;
+
+    state = 0x12345678;
+
+    cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
+    cairo_paint (cr);
+
+#if GENERATE_REFERENCE
+    for (x = 0; x < WIDTH; x++) {
+       cairo_set_source_rgba (cr, 1, 1, 1, x * x * 0.5 / (WIDTH * WIDTH));
+       cairo_rectangle (cr, x, 0, 1, HEIGHT);
+       cairo_fill (cr);
+    }
+#else
+    cairo_set_operator (cr, CAIRO_OPERATOR_ADD);
+    for (channel = 0; channel < 3; channel++) {
+       switch (channel) {
+       default:
+       case 0: cairo_set_source_rgb (cr, 1.0, 0.0, 0.0); break;
+       case 1: cairo_set_source_rgb (cr, 0.0, 1.0, 0.0); break;
+       case 2: cairo_set_source_rgb (cr, 0.0, 0.0, 1.0); break;
+       }
+
+       for (x = 0; x < WIDTH; x++) {
+           for (y = 0; y < HEIGHT; y++) {
+               double dx = random_offset (WIDTH - x, TRUE);
+               double dy = random_offset (WIDTH - x, TRUE);
+               cairo_move_to (cr, x + dx, y + dy);
+               cairo_rel_line_to (cr, x / (double) WIDTH, 0);
+               cairo_rel_line_to (cr, 0, x / (double) WIDTH);
+               cairo_close_path (cr);
+           }
+       }
+       cairo_fill (cr);
+    }
+#endif
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+static cairo_test_status_t
+column_triangles (cairo_t *cr, int width, int height)
+{
+    int x, y, i, channel;
+
+    state = 0x12345678;
+
+    cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
+    cairo_paint (cr);
+
+#if GENERATE_REFERENCE
+    for (x = 0; x < WIDTH; x++) {
+       cairo_set_source_rgba (cr, 1, 1, 1, x * 0.5 / WIDTH);
+       cairo_rectangle (cr, x, 0, 1, HEIGHT);
+       cairo_fill (cr);
+    }
+#else
+    cairo_set_operator (cr, CAIRO_OPERATOR_ADD);
+    for (channel = 0; channel < 3; channel++) {
+       switch (channel) {
+       default:
+       case 0: cairo_set_source_rgb (cr, 1.0, 0.0, 0.0); break;
+       case 1: cairo_set_source_rgb (cr, 0.0, 1.0, 0.0); break;
+       case 2: cairo_set_source_rgb (cr, 0.0, 0.0, 1.0); break;
+       }
+
+       for (x = 0; x < WIDTH; x++) {
+           double step = x / (double) (2 * WIDTH);
+           for (y = 0; y < HEIGHT; y++) {
+               for (i = 0; i < PRECISION; i++) {
+                   double dy = random_offset (WIDTH - x, FALSE);
+
+                   /*
+                    * We want to test some sharing of edges to further
+                    * stress the rasterisers, so instead of using one
+                    * tall triangle, it is split into two, with vertical
+                    * edges on either side that may co-align with their
+                    * neighbours:
+                    *
+                    *  s ---  .      ---
+                    *  t  |   |\      |
+                    *  e  |   | \     |
+                    *  p ---  ....    |  2 * step = x / WIDTH
+                    *          \ |    |
+                    *           \|    |
+                    *            .   ---
+                    *        |---|
+                    *     1 / PRECISION
+                    *
+                    * Each column contains two triangles of width one quantum and
+                    * total height of (x / WIDTH), thus the total area covered by all
+                    * columns in each pixel is .5 * (x / WIDTH).
+                    */
+
+                   cairo_move_to (cr, x + i / (double) PRECISION, y + dy);
+                   cairo_rel_line_to (cr, 0, step);
+                   cairo_rel_line_to (cr, 1 / (double) PRECISION, step);
+                   cairo_rel_line_to (cr, 0, -step);
+                   cairo_close_path (cr);
+               }
+               cairo_fill (cr); /* do these per-pixel due to the extra volume of edges */
+           }
+       }
+    }
+#endif
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+static cairo_test_status_t
+row_triangles (cairo_t *cr, int width, int height)
+{
+    int x, y, i, channel;
+
+    state = 0x12345678;
+
+    cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
+    cairo_paint (cr);
+
+#if GENERATE_REFERENCE
+    for (x = 0; x < WIDTH; x++) {
+       cairo_set_source_rgba (cr, 1, 1, 1, x * 0.5 / WIDTH);
+       cairo_rectangle (cr, x, 0, 1, HEIGHT);
+       cairo_fill (cr);
+    }
+#else
+    cairo_set_operator (cr, CAIRO_OPERATOR_ADD);
+    for (channel = 0; channel < 3; channel++) {
+       switch (channel) {
+       default:
+       case 0: cairo_set_source_rgb (cr, 1.0, 0.0, 0.0); break;
+       case 1: cairo_set_source_rgb (cr, 0.0, 1.0, 0.0); break;
+       case 2: cairo_set_source_rgb (cr, 0.0, 0.0, 1.0); break;
+       }
+
+       for (x = 0; x < WIDTH; x++) {
+           double step = x / (double) (2 * WIDTH);
+           for (y = 0; y < HEIGHT; y++) {
+               for (i = 0; i < PRECISION; i++) {
+                   double dx = random_offset (WIDTH - x, FALSE);
+
+                   /* See column_triangles() for a transposed description
+                    * of this geometry.
+                    */
+
+                   cairo_move_to (cr, x + dx, y + i / (double) PRECISION);
+                   cairo_rel_line_to (cr,  step, 0);
+                   cairo_rel_line_to (cr,  step, 1 / (double) PRECISION);
+                   cairo_rel_line_to (cr, -step, 0);
+                   cairo_close_path (cr);
+               }
+               cairo_fill (cr); /* do these per-pixel due to the extra volume of edges */
+           }
+       }
+    }
+#endif
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (coverage_rectangles,
+           "Check the fidelity of the rasterisation.",
+           NULL, /* keywords */
+           "target=raster slow", /* requirements */
+           WIDTH, HEIGHT,
+           NULL, rectangles)
+
+CAIRO_TEST (coverage_intersecting_quads,
+           "Check the fidelity of the rasterisation.",
+           NULL, /* keywords */
+           "target=raster slow", /* requirements */
+           WIDTH, HEIGHT,
+           NULL, intersecting_quads)
+
+CAIRO_TEST (coverage_intersecting_triangles,
+           "Check the fidelity of the rasterisation.",
+           NULL, /* keywords */
+           "target=raster slow", /* requirements */
+           WIDTH, HEIGHT,
+           NULL, intersecting_triangles)
+CAIRO_TEST (coverage_row_triangles,
+           "Check the fidelity of the rasterisation.",
+           NULL, /* keywords */
+           "target=raster slow", /* requirements */
+           WIDTH, HEIGHT,
+           NULL, row_triangles)
+CAIRO_TEST (coverage_column_triangles,
+           "Check the fidelity of the rasterisation.",
+           NULL, /* keywords */
+           "target=raster slow", /* requirements */
+           WIDTH, HEIGHT,
+           NULL, column_triangles)
+CAIRO_TEST (coverage_triangles,
+           "Check the fidelity of the rasterisation.",
+           NULL, /* keywords */
+           "target=raster slow", /* requirements */
+           WIDTH, HEIGHT,
+           NULL, triangles)
diff --git a/test/create-for-stream.c b/test/create-for-stream.c
new file mode 100644 (file)
index 0000000..3dde378
--- /dev/null
@@ -0,0 +1,303 @@
+/*
+ * Copyright © 2006 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Kristian Høgsberg <krh@redhat.com>
+ */
+
+#include "cairo-test.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+#if CAIRO_HAS_PS_SURFACE
+#include <cairo-ps.h>
+#endif
+
+#if CAIRO_HAS_PDF_SURFACE
+#include <cairo-pdf.h>
+#endif
+
+#if CAIRO_HAS_SVG_SURFACE
+#include <cairo-svg.h>
+#endif
+
+#include "cairo-test.h"
+
+/* The main test suite doesn't test the *_create_for_stream
+ * constructors for the PDF, PS and SVG surface, so we do that here.
+ * We draw to an in-memory buffer using the stream constructor and
+ * compare the output to the contents of a file written using the
+ * file constructor.
+ */
+
+#define MAX_OUTPUT_SIZE 4096
+
+#define WIDTH_IN_INCHES  3
+#define HEIGHT_IN_INCHES 3
+#define WIDTH_IN_POINTS  (WIDTH_IN_INCHES  * 72.0)
+#define HEIGHT_IN_POINTS (HEIGHT_IN_INCHES * 72.0)
+
+#define BASENAME "create-for-stream.out"
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    /* Just draw a rectangle. */
+
+    cairo_rectangle (cr, width / 10., height /10.,
+                    width - 2 * width / 10.,
+                    height - 2 * height /10.);
+    cairo_fill (cr);
+
+    cairo_show_page (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+static void
+draw_to (cairo_surface_t *surface)
+{
+    cairo_t *cr;
+
+    cr = cairo_create (surface);
+
+    draw (cr, WIDTH_IN_POINTS, HEIGHT_IN_POINTS);
+
+    cairo_destroy (cr);
+}
+
+typedef struct _write_closure {
+    const cairo_test_context_t *ctx;
+    char buffer[MAX_OUTPUT_SIZE];
+    size_t index;
+    cairo_test_status_t status;
+} write_closure_t;
+
+static cairo_status_t
+bad_write (void                *closure,
+          const unsigned char  *data,
+          unsigned int  length)
+{
+    return CAIRO_STATUS_WRITE_ERROR;
+}
+
+static cairo_status_t
+test_write (void               *closure,
+           const unsigned char *data,
+           unsigned int         length)
+{
+    write_closure_t *wc = closure;
+
+    if (wc->index + length >= sizeof wc->buffer) {
+       cairo_test_log (wc->ctx, "Error: out of bounds in write callback\n");
+       wc->status = CAIRO_TEST_FAILURE;
+       return CAIRO_STATUS_SUCCESS;
+    }
+
+    memcpy (&wc->buffer[wc->index], data, length);
+    wc->index += length;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+
+typedef cairo_surface_t *
+(*file_constructor_t) (const char             *filename,
+                      double                   width_in_points,
+                      double                   height_in_points);
+
+typedef cairo_surface_t *
+(*stream_constructor_t) (cairo_write_func_t    write_func,
+                        void                  *closure,
+                        double                 width_in_points,
+                        double                 height_in_points);
+
+static cairo_test_status_t
+test_surface (const cairo_test_context_t *ctx,
+             const char                 *backend,
+             const char                 *filename,
+             file_constructor_t         file_constructor,
+             stream_constructor_t       stream_constructor)
+{
+    cairo_surface_t *surface;
+    write_closure_t wc;
+    char file_contents[MAX_OUTPUT_SIZE];
+    cairo_status_t status;
+    FILE *fp;
+
+    /* test propagation of user errors */
+    surface = stream_constructor (bad_write, &wc,
+                                 WIDTH_IN_POINTS, HEIGHT_IN_POINTS);
+
+    status = cairo_surface_status (surface);
+    if (status) {
+       cairo_test_log (ctx,
+                       "%s: Failed to create surface for stream.\n",
+                       backend);
+       return CAIRO_TEST_FAILURE;
+    }
+
+    draw_to (surface);
+
+    cairo_surface_finish (surface);
+    status = cairo_surface_status (surface);
+    cairo_surface_destroy (surface);
+
+    if (status != CAIRO_STATUS_WRITE_ERROR) {
+       cairo_test_log (ctx,
+                       "%s: Error: expected \"write error\", but received \"%s\".\n",
+                       backend, cairo_status_to_string (status));
+       return CAIRO_TEST_FAILURE;
+    }
+
+    /* construct the real surface */
+    wc.ctx = ctx;
+    wc.status = CAIRO_TEST_SUCCESS;
+    wc.index = 0;
+
+    surface = stream_constructor (test_write, &wc,
+                                 WIDTH_IN_POINTS, HEIGHT_IN_POINTS);
+
+    status = cairo_surface_status (surface);
+    if (status) {
+       cairo_test_log (ctx,
+                       "%s: Failed to create surface for stream.\n", backend);
+       return CAIRO_TEST_FAILURE;
+    }
+
+    draw_to (surface);
+
+    cairo_surface_destroy (surface);
+
+    if (wc.status != CAIRO_TEST_SUCCESS) {
+       /* Error already reported. */
+       return wc.status;
+    }
+
+    surface = file_constructor (filename,
+                               WIDTH_IN_POINTS, HEIGHT_IN_POINTS);
+
+    status = cairo_surface_status (surface);
+    if (status) {
+       cairo_test_log (ctx, "%s: Failed to create surface for file %s: %s.\n",
+                       backend, filename, cairo_status_to_string (status));
+       return CAIRO_TEST_FAILURE;
+    }
+
+    draw_to (surface);
+
+    cairo_surface_destroy (surface);
+
+    fp = fopen (filename, "r");
+    if (fp == NULL) {
+       cairo_test_log (ctx, "%s: Failed to open %s for reading: %s.\n",
+                       backend, filename, strerror (errno));
+       return CAIRO_TEST_FAILURE;
+    }
+
+    if (fread (file_contents, 1, wc.index, fp) != wc.index) {
+       cairo_test_log (ctx, "%s: Failed to read %s: %s.\n",
+                       backend, filename, strerror (errno));
+       fclose (fp);
+       return CAIRO_TEST_FAILURE;
+    }
+
+    if (memcmp (file_contents, wc.buffer, wc.index) != 0) {
+       cairo_test_log (ctx, "%s: Stream based output differ from file output for %s.\n",
+                       backend, filename);
+       fclose (fp);
+       return CAIRO_TEST_FAILURE;
+    }
+
+    fclose (fp);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+static cairo_test_status_t
+preamble (cairo_test_context_t *ctx)
+{
+    cairo_test_status_t status = CAIRO_TEST_UNTESTED;
+    cairo_test_status_t test_status;
+
+#if CAIRO_HAS_PS_SURFACE
+    if (cairo_test_is_target_enabled (ctx, "ps2") ||
+       cairo_test_is_target_enabled (ctx, "ps3"))
+    {
+       if (status == CAIRO_TEST_UNTESTED)
+           status = CAIRO_TEST_SUCCESS;
+
+       test_status = test_surface (ctx, "ps", BASENAME ".ps",
+                                   cairo_ps_surface_create,
+                                   cairo_ps_surface_create_for_stream);
+       cairo_test_log (ctx, "TEST: %s TARGET: %s RESULT: %s\n",
+                       ctx->test->name, "ps",
+                       test_status ? "FAIL" : "PASS");
+       if (status == CAIRO_TEST_SUCCESS)
+           status = test_status;
+    }
+#endif
+
+#if CAIRO_HAS_PDF_SURFACE
+    if (cairo_test_is_target_enabled (ctx, "pdf")) {
+       if (status == CAIRO_TEST_UNTESTED)
+           status = CAIRO_TEST_SUCCESS;
+
+       test_status = test_surface (ctx, "pdf", BASENAME ".pdf",
+                                   cairo_pdf_surface_create,
+                                   cairo_pdf_surface_create_for_stream);
+       cairo_test_log (ctx, "TEST: %s TARGET: %s RESULT: %s\n",
+                       ctx->test->name, "pdf",
+                       test_status ? "FAIL" : "PASS");
+       if (status == CAIRO_TEST_SUCCESS)
+           status = test_status;
+    }
+#endif
+
+#if CAIRO_HAS_SVG_SURFACE
+    if (cairo_test_is_target_enabled (ctx, "svg11") ||
+       cairo_test_is_target_enabled (ctx, "svg12"))
+    {
+       if (status == CAIRO_TEST_UNTESTED)
+           status = CAIRO_TEST_SUCCESS;
+
+       test_status = test_surface (ctx, "svg", BASENAME ".svg",
+                                   cairo_svg_surface_create,
+                                   cairo_svg_surface_create_for_stream);
+       cairo_test_log (ctx, "TEST: %s TARGET: %s RESULT: %s\n",
+                       ctx->test->name, "svg",
+                       test_status ? "FAIL" : "PASS");
+       if (status == CAIRO_TEST_SUCCESS)
+           status = test_status;
+    }
+#endif
+
+    return status;
+}
+
+CAIRO_TEST (create_for_stream,
+           "Checks creating vector surfaces with user defined I/O\n",
+           "stream", /* keywords */
+           "target=vector", /* requirements */
+           0, 0,
+           preamble, NULL)
diff --git a/test/create-from-png-stream.c b/test/create-from-png-stream.c
new file mode 100644 (file)
index 0000000..2e9eeee
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Carl Worth <cworth@cworth.org>
+ */
+
+#include "cairo-test.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+
+#define WIDTH 2
+#define HEIGHT 2
+
+static cairo_status_t
+read_png_from_file (void *closure, unsigned char *data, unsigned int length)
+{
+    FILE *file = closure;
+    size_t bytes_read;
+
+    bytes_read = fread (data, 1, length, file);
+    if (bytes_read != length)
+       return CAIRO_STATUS_READ_ERROR;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    const cairo_test_context_t *ctx = cairo_test_get_context (cr);
+    char *filename;
+    FILE *file;
+    cairo_surface_t *surface;
+    cairo_status_t status;
+
+    xasprintf (&filename, "%s/reference/%s", ctx->srcdir,
+              "create-from-png-stream.ref.png");
+
+    file = fopen (filename, "rb");
+    if (file == NULL) {
+       cairo_test_status_t ret;
+
+       ret = CAIRO_TEST_FAILURE;
+       if (errno == ENOMEM)
+           ret = cairo_test_status_from_status (ctx, CAIRO_STATUS_NO_MEMORY);
+
+       if (ret != CAIRO_TEST_NO_MEMORY)
+           cairo_test_log (ctx, "Error: failed to open file: %s\n", filename);
+
+       free (filename);
+       return ret;
+    }
+
+    surface = cairo_image_surface_create_from_png_stream (read_png_from_file,
+                                                         file);
+
+    fclose (file);
+
+    status = cairo_surface_status (surface);
+    if (status) {
+       cairo_test_status_t ret;
+
+       cairo_surface_destroy (surface);
+
+       ret = cairo_test_status_from_status (ctx, status);
+       if (ret != CAIRO_TEST_NO_MEMORY) {
+           cairo_test_log (ctx,
+                           "Error: failed to create surface from PNG: %s - %s\n",
+                           filename,
+                           cairo_status_to_string (status));
+       }
+
+       free (filename);
+
+       return ret;
+    }
+
+    free (filename);
+
+    /* Pretend we modify the surface data (which detaches the PNG mime data) */
+    cairo_surface_flush (surface);
+    cairo_surface_mark_dirty (surface);
+
+    cairo_set_source_surface (cr, surface, 0, 0);
+    cairo_pattern_set_filter (cairo_get_source (cr), CAIRO_FILTER_NEAREST);
+    cairo_paint (cr);
+
+    cairo_surface_destroy (surface);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (create_from_png_stream,
+           "Tests the creation of an image surface from a PNG using a FILE *",
+           "png", /* keywords */
+           NULL, /* requirements */
+           WIDTH, HEIGHT,
+           NULL, draw)
diff --git a/test/create-from-png.c b/test/create-from-png.c
new file mode 100644 (file)
index 0000000..f620956
--- /dev/null
@@ -0,0 +1,315 @@
+/*
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Carl Worth <cworth@cworth.org>
+ */
+
+#include "cairo-test.h"
+
+#include <stdlib.h>
+
+#define WIDTH 2
+#define HEIGHT 2
+
+static cairo_status_t
+no_memory_error (void *closure, unsigned char *data, unsigned int size)
+{
+    return CAIRO_STATUS_NO_MEMORY;
+}
+
+static cairo_status_t
+read_error (void *closure, unsigned char *data, unsigned int size)
+{
+    return CAIRO_STATUS_READ_ERROR;
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    const cairo_test_context_t *ctx = cairo_test_get_context (cr);
+    char *filename;
+    cairo_surface_t *surface;
+
+    xasprintf (&filename, "%s/reference/%s",
+              ctx->srcdir, "create-from-png.ref.png");
+
+    surface = cairo_image_surface_create_from_png (filename);
+    if (cairo_surface_status (surface)) {
+       cairo_test_status_t result;
+
+       result = cairo_test_status_from_status (ctx,
+                                               cairo_surface_status (surface));
+       if (result == CAIRO_TEST_FAILURE) {
+           cairo_test_log (ctx, "Error reading PNG image %s: %s\n",
+                           filename,
+                           cairo_status_to_string (cairo_surface_status (surface)));
+       }
+
+       free (filename);
+       return result;
+    }
+
+    /* Pretend we modify the surface data (which detaches the PNG mime data) */
+    cairo_surface_flush (surface);
+    cairo_surface_mark_dirty (surface);
+
+    cairo_set_source_surface (cr, surface, 0, 0);
+    cairo_pattern_set_filter (cairo_get_source (cr), CAIRO_FILTER_NEAREST);
+    cairo_paint (cr);
+
+    cairo_surface_destroy (surface);
+
+    free (filename);
+    return CAIRO_TEST_SUCCESS;
+}
+
+static cairo_test_status_t
+preamble (cairo_test_context_t *ctx)
+{
+    char *filename;
+    char *path;
+    cairo_surface_t *surface;
+    cairo_status_t status;
+    cairo_test_status_t result = CAIRO_TEST_SUCCESS;
+
+    surface = cairo_image_surface_create_from_png ("___THIS_FILE_DOES_NOT_EXIST___");
+    if (cairo_surface_status (surface) != CAIRO_STATUS_FILE_NOT_FOUND) {
+       result = cairo_test_status_from_status (ctx,
+                                               cairo_surface_status (surface));
+       if (result == CAIRO_TEST_FAILURE) {
+           cairo_test_log (ctx, "Error: expected \"file not found\", but got: %s\n",
+                           cairo_status_to_string (cairo_surface_status (surface)));
+       }
+    }
+    cairo_surface_destroy (surface);
+    if (result != CAIRO_TEST_SUCCESS)
+       return result;
+
+    surface = cairo_image_surface_create_from_png_stream (no_memory_error, NULL);
+    if (cairo_surface_status (surface) != CAIRO_STATUS_NO_MEMORY) {
+       result = cairo_test_status_from_status (ctx,
+                                               cairo_surface_status (surface));
+       if (result == CAIRO_TEST_FAILURE) {
+           cairo_test_log (ctx, "Error: expected \"out of memory\", but got: %s\n",
+                           cairo_status_to_string (cairo_surface_status (surface)));
+       }
+    }
+    cairo_surface_destroy (surface);
+    if (result != CAIRO_TEST_SUCCESS)
+       return result;
+
+    surface = cairo_image_surface_create_from_png_stream (read_error, NULL);
+    if (cairo_surface_status (surface) != CAIRO_STATUS_READ_ERROR) {
+       result = cairo_test_status_from_status (ctx,
+                                               cairo_surface_status (surface));
+       if (result == CAIRO_TEST_FAILURE) {
+           cairo_test_log (ctx, "Error: expected \"read error\", but got: %s\n",
+                           cairo_status_to_string (cairo_surface_status (surface)));
+       }
+    }
+    cairo_surface_destroy (surface);
+    if (result != CAIRO_TEST_SUCCESS)
+       return result;
+
+    /* cheekily test error propagation from the user write funcs as well ... */
+    xasprintf (&path, "%s/reference", ctx->srcdir);
+    xasprintf (&filename, "%s/%s", path, "create-from-png.ref.png");
+
+    surface = cairo_image_surface_create_from_png (filename);
+    if (cairo_surface_status (surface)) {
+       result = cairo_test_status_from_status (ctx,
+                                               cairo_surface_status (surface));
+       if (result == CAIRO_TEST_FAILURE) {
+           cairo_test_log (ctx, "Error reading PNG image %s: %s\n",
+                           filename,
+                           cairo_status_to_string (cairo_surface_status (surface)));
+       }
+    } else {
+       status = cairo_surface_write_to_png_stream (surface,
+                                              (cairo_write_func_t) no_memory_error,
+                                              NULL);
+       if (status != CAIRO_STATUS_NO_MEMORY) {
+           result = cairo_test_status_from_status (ctx, status);
+           if (result == CAIRO_TEST_FAILURE) {
+               cairo_test_log (ctx, "Error: expected \"out of memory\", but got: %s\n",
+                               cairo_status_to_string (status));
+           }
+       }
+
+       status = cairo_surface_write_to_png_stream (surface,
+                                                   (cairo_write_func_t) read_error,
+                                                   NULL);
+       if (status != CAIRO_STATUS_READ_ERROR) {
+           result = cairo_test_status_from_status (ctx, status);
+           if (result == CAIRO_TEST_FAILURE) {
+               cairo_test_log (ctx, "Error: expected \"read error\", but got: %s\n",
+                               cairo_status_to_string (status));
+           }
+       }
+
+       /* and check that error has not propagated to the surface */
+       if (cairo_surface_status (surface)) {
+           result = cairo_test_status_from_status (ctx,
+                                                   cairo_surface_status (surface));
+           if (result == CAIRO_TEST_FAILURE) {
+               cairo_test_log (ctx, "Error: user write error propagated to surface: %s",
+                               cairo_status_to_string (cairo_surface_status (surface)));
+           }
+       }
+    }
+    cairo_surface_destroy (surface);
+    free (filename);
+    if (result != CAIRO_TEST_SUCCESS)
+       return result;
+
+    /* check that loading alpha/opaque PNGs generate the correct surfaces */
+    xasprintf (&filename, "%s/%s", path, "create-from-png.alpha.ref.png");
+    surface = cairo_image_surface_create_from_png (filename);
+    if (cairo_surface_status (surface)) {
+       result = cairo_test_status_from_status (ctx,
+                                               cairo_surface_status (surface));
+       if (result == CAIRO_TEST_FAILURE) {
+           cairo_test_log (ctx, "Error reading PNG image %s: %s\n",
+                           filename,
+                           cairo_status_to_string (cairo_surface_status (surface)));
+       }
+    } else if (cairo_image_surface_get_format (surface) != CAIRO_FORMAT_ARGB32) {
+       cairo_test_log (ctx, "Error reading PNG image %s: did not create an ARGB32 image\n",
+                       filename);
+       result = CAIRO_TEST_FAILURE;
+    }
+    free (filename);
+    cairo_surface_destroy (surface);
+    if (result != CAIRO_TEST_SUCCESS)
+       return result;
+
+    xasprintf (&filename, "%s/%s", path, "create-from-png.ref.png");
+    surface = cairo_image_surface_create_from_png (filename);
+    if (cairo_surface_status (surface)) {
+       result = cairo_test_status_from_status (ctx,
+                                               cairo_surface_status (surface));
+       if (result == CAIRO_TEST_FAILURE) {
+           cairo_test_log (ctx, "Error reading PNG image %s: %s\n",
+                           filename,
+                           cairo_status_to_string (cairo_surface_status (surface)));
+       }
+    } else if (cairo_image_surface_get_format (surface) != CAIRO_FORMAT_RGB24) {
+       cairo_test_log (ctx, "Error reading PNG image %s: did not create an RGB24 image\n",
+                       filename);
+       result = CAIRO_TEST_FAILURE;
+    }
+    free (filename);
+    cairo_surface_destroy (surface);
+    if (result != CAIRO_TEST_SUCCESS)
+       return result;
+
+    /* check paletted PNGs */
+    xasprintf (&filename, "%s/%s", path, "create-from-png.indexed-alpha.ref.png");
+    surface = cairo_image_surface_create_from_png (filename);
+    if (cairo_surface_status (surface)) {
+       result = cairo_test_status_from_status (ctx,
+                                               cairo_surface_status (surface));
+       if (result == CAIRO_TEST_FAILURE) {
+           cairo_test_log (ctx, "Error reading PNG image %s: %s\n",
+                           filename,
+                           cairo_status_to_string (cairo_surface_status (surface)));
+       }
+    } else if (cairo_image_surface_get_format (surface) != CAIRO_FORMAT_ARGB32) {
+       cairo_test_log (ctx, "Error reading PNG image %s: did not create an ARGB32 image\n",
+                       filename);
+       result = CAIRO_TEST_FAILURE;
+    }
+    free (filename);
+    cairo_surface_destroy (surface);
+    if (result != CAIRO_TEST_SUCCESS)
+       return result;
+
+    xasprintf (&filename, "%s/%s", path, "create-from-png.indexed.ref.png");
+    surface = cairo_image_surface_create_from_png (filename);
+    if (cairo_surface_status (surface)) {
+       result = cairo_test_status_from_status (ctx,
+                                               cairo_surface_status (surface));
+       if (result == CAIRO_TEST_FAILURE) {
+           cairo_test_log (ctx, "Error reading PNG image %s: %s\n",
+                           filename,
+                           cairo_status_to_string (cairo_surface_status (surface)));
+       }
+    } else if (cairo_image_surface_get_format (surface) != CAIRO_FORMAT_RGB24) {
+       cairo_test_log (ctx, "Error reading PNG image %s: did not create an RGB24 image\n",
+                       filename);
+       result = CAIRO_TEST_FAILURE;
+    }
+    free (filename);
+    cairo_surface_destroy (surface);
+    if (result != CAIRO_TEST_SUCCESS)
+       return result;
+
+    /* check grayscale PNGs */
+    xasprintf (&filename, "%s/%s", path, "create-from-png.gray-alpha.ref.png");
+    surface = cairo_image_surface_create_from_png (filename);
+    if (cairo_surface_status (surface)) {
+       result = cairo_test_status_from_status (ctx,
+                                               cairo_surface_status (surface));
+       if (result == CAIRO_TEST_FAILURE) {
+           cairo_test_log (ctx, "Error reading PNG image %s: %s\n",
+                           filename,
+                           cairo_status_to_string (cairo_surface_status (surface)));
+       }
+    } else if (cairo_image_surface_get_format (surface) != CAIRO_FORMAT_ARGB32) {
+       cairo_test_log (ctx, "Error reading PNG image %s: did not create an ARGB32 image\n",
+                       filename);
+       result = CAIRO_TEST_FAILURE;
+    }
+    free (filename);
+    cairo_surface_destroy (surface);
+    if (result != CAIRO_TEST_SUCCESS)
+       return result;
+
+    xasprintf (&filename, "%s/%s", path, "create-from-png.gray.ref.png");
+    surface = cairo_image_surface_create_from_png (filename);
+    if (cairo_surface_status (surface)) {
+       result = cairo_test_status_from_status (ctx,
+                                               cairo_surface_status (surface));
+       if (result == CAIRO_TEST_FAILURE) {
+           cairo_test_log (ctx, "Error reading PNG image %s: %s\n",
+                           filename,
+                           cairo_status_to_string (cairo_surface_status (surface)));
+       }
+    } else if (cairo_image_surface_get_format (surface) != CAIRO_FORMAT_RGB24) {
+       cairo_test_log (ctx, "Error reading PNG image %s: did not create an RGB24 image\n",
+                       filename);
+       result = CAIRO_TEST_FAILURE;
+    }
+    free (filename);
+    cairo_surface_destroy (surface);
+
+    free (path);
+
+    return result;
+}
+
+CAIRO_TEST (create_from_png,
+           "Tests the creation of an image surface from a PNG file",
+           "png", /* keywords */
+           NULL, /* requirements */
+           WIDTH, HEIGHT,
+           preamble, draw)
diff --git a/test/culled-glyphs.c b/test/culled-glyphs.c
new file mode 100644 (file)
index 0000000..28bee7e
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2008 Chris Wilson
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Chris Wilson not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Chris Wilson makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * CHRIS WILSON DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL CHRIS WILSON BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    const char *text =
+"This needs to be a very long string, wider than the surface, and yet wider."
+"Ideally it should overflow the stack buffers, but do you really want to read "
+"a message that long. No. So we compromise with around 300 glyphs that is "
+"long enough to trigger the conditions as stated in "
+"http://lists.cairographics.org/archives/cairo/2008-December/015976.html. "
+"Happy now?";
+    cairo_text_extents_t extents;
+
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_paint (cr);
+    cairo_set_source_rgb (cr, 0, 0, 0);
+
+    cairo_set_font_size (cr, 16);
+    cairo_text_extents (cr, text, &extents);
+    cairo_move_to (cr, -extents.width/2, 18);
+    cairo_show_text (cr, text);
+
+    /* XXX we should exercise cairo_show_text_glyphs() as well,
+     * and CAIRO_TEXT_CLUSTER_BACKWARDS
+     */
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (culled_glyphs,
+           "Tests culling of glyphs and text clusters",
+           "glyphs", /* keywords */
+           NULL, /* requirements */
+           20, 20,
+           NULL, draw)
+
diff --git a/test/curve-to-as-line-to.c b/test/curve-to-as-line-to.c
new file mode 100644 (file)
index 0000000..07eb005
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ */
+
+#include "cairo-test.h"
+
+#define SIZE 30
+
+/* At one point, an optimization was proposed for cairo in which a
+ * curve_to would be optimized as a line_to. The initial (buggy)
+ * implementation verified that the slopes of several segments of the
+ * spline's control polygon were identical, but left open the
+ * possibility of an anti-parallel slope for one segment.
+ *
+ * For example, given a spline with collinear control points (A,B,C,D)
+ * positioned as follows:
+ *
+ *     C--A--B--D
+ *
+ * The code verified identical slopes for AB, CD, and AD. The missing
+ * check for the BC segment allowed it to be anti-parallel to the
+ * others as above, and hence invalid to replace this spline with the
+ * AD line segment.
+ */
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); /* white */
+    cairo_paint (cr);
+
+    cairo_set_line_width (cr, 1.0);
+    cairo_set_line_cap (cr, CAIRO_LINE_CAP_BUTT);
+    cairo_set_line_join (cr, CAIRO_LINE_JOIN_BEVEL);
+    cairo_set_source_rgb (cr, 0.0, 0.0, 0.0); /* black */
+
+    cairo_translate (cr, 0, 1.0);
+
+    /* The CABD spline as described above. We ensure that the spline
+     * folds over on itself outside the bounds of the image to avoid
+     * the reference image having the curved portion of that fold,
+     * (which would just be harder to match in all the backends than
+     * we really want). */
+    cairo_move_to (cr,
+                    10.5, 0.5);
+    cairo_curve_to (cr,
+                    11.5, 0.5,
+                   -25.0, 0.5,
+                    31.0, 0.5);
+
+    cairo_stroke (cr);
+
+    cairo_translate (cr, 0, 2.0);
+
+    /* A reflected version: DBAC */
+    cairo_move_to (cr,
+                   19.5, 0.5);
+
+    cairo_curve_to (cr,
+                   18.5, 0.5,
+                   55.0, 0.5,
+                   -1.0, 0.5);
+
+    cairo_stroke (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (curve_to_as_line_to,
+           "Test optimization treating curve_to as line_to",
+           "path", /* keywords */
+           NULL, /* requirements */
+           30,
+           5,
+           NULL, draw)
diff --git a/test/dash-caps-joins.c b/test/dash-caps-joins.c
new file mode 100644 (file)
index 0000000..ace27e9
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ */
+
+/* Test case for bug #4409:
+ *
+ *     Dashes are missing initial caps
+ *     https://bugs.freedesktop.org/show_bug.cgi?id=4409
+ */
+
+#include "cairo-test.h"
+
+#define LINE_WIDTH     10.
+#define SIZE           (5 * LINE_WIDTH)
+#define PAD            (2 * LINE_WIDTH)
+
+static void
+make_path (cairo_t *cr)
+{
+    cairo_move_to (cr, 0., 0.);
+    cairo_rel_line_to (cr, 0., SIZE);
+    cairo_rel_line_to (cr, SIZE, 0.);
+    cairo_close_path (cr);
+
+    cairo_move_to (cr, 2 * LINE_WIDTH, 0.);
+    cairo_rel_line_to (cr, 3 * LINE_WIDTH, 0.);
+    cairo_rel_line_to (cr, 0., 3 * LINE_WIDTH);
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    double dash[] = {LINE_WIDTH, 1.5 * LINE_WIDTH};
+    double dash_offset = -2 * LINE_WIDTH;
+    int i;
+
+    /* We draw in the default black, so paint white first. */
+    cairo_save (cr);
+    cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); /* white */
+    cairo_paint (cr);
+    cairo_restore (cr);
+
+    for (i=0; i<2; i++) {
+       cairo_save (cr);
+       cairo_set_line_width (cr, LINE_WIDTH);
+       cairo_set_dash (cr, dash, ARRAY_LENGTH (dash), dash_offset);
+
+       cairo_translate (cr, PAD, PAD);
+
+       make_path (cr);
+       cairo_set_line_cap (cr, CAIRO_LINE_CAP_BUTT);
+       cairo_set_line_join (cr, CAIRO_LINE_JOIN_BEVEL);
+       cairo_stroke (cr);
+
+       cairo_translate (cr, SIZE + PAD, 0.);
+
+       make_path (cr);
+       cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
+       cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND);
+       cairo_stroke (cr);
+
+       cairo_translate (cr, SIZE + PAD, 0.);
+
+       make_path (cr);
+       cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE);
+       cairo_set_line_join (cr, CAIRO_LINE_JOIN_MITER);
+       cairo_stroke (cr);
+
+       cairo_restore (cr);
+       cairo_translate (cr, 0., SIZE + PAD);
+       dash_offset = 0;
+    }
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (dash_caps_joins,
+           "Test caps and joins when dashing",
+           "dash, stroke", /* keywords */
+           NULL, /* requirements */
+           3 * (PAD + SIZE) + PAD,
+           PAD + SIZE + PAD + SIZE + PAD,
+           NULL, draw)
diff --git a/test/dash-curve.c b/test/dash-curve.c
new file mode 100644 (file)
index 0000000..c319980
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * Copyright © 2007 Jeff Smith
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Jeff Smith not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Jeff Smith makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * JEFF SMITH DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL JEFF SMITH BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Jeff Smith <whydoubt@yahoo.com>
+ */
+
+#include "cairo-test.h"
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    double dashes[2] = {20, 20};
+    int a=0, b=0, c=0;
+
+    cairo_set_source_rgb (cr, 0, 0, 0);
+    cairo_paint (cr);
+
+    for (a=0; a<4; a++)
+    for (b=0; b<5; b++)
+    for (c=0; c<5; c++) {
+       cairo_move_to (cr, ((b*5)+c)*60+10, a*60+10);
+       cairo_rel_curve_to (cr,
+                           0,      b*10,
+                           0,      b*10,
+                           c*10,   b*10);
+
+       cairo_set_source_rgb (cr, 1, 1, 1);
+       cairo_set_line_width (cr, 8);
+       cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
+       cairo_set_dash (cr, dashes, 2, a*10);
+       cairo_stroke_preserve (cr);
+
+       cairo_set_source_rgb (cr, 0, 0.5, 1);
+       cairo_set_line_width (cr, 2);
+       cairo_set_line_cap (cr, CAIRO_LINE_CAP_BUTT);
+       cairo_set_dash (cr, 0, 0, 0);
+       cairo_stroke (cr);
+    }
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (dash_curve,
+           "Tries to explore the state space of the dashing code along curves",
+           "dash, stroke", /* keywords */
+           NULL, /* requirements */
+           25*60, 4*60,
+           NULL, draw)
diff --git a/test/dash-infinite-loop.c b/test/dash-infinite-loop.c
new file mode 100644 (file)
index 0000000..a3d7544
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * Copyright © 2009 M Joonas Pihlaja
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: M Joonas Pihlaja <jpihlaja@cc.helsinki.fi>
+ */
+
+#include "cairo-test.h"
+
+/* When faced with very small dash lengths the stroker is liable to
+ * get stuck in an infinite loop when advancing the dash offset.  This
+ * test attempts to hit each of the locations in the stroker code
+ * where the dash offset is advanced in a loop.
+ *
+ * Reported to the cairo mailing list by Hans Breuer.
+ * http://lists.cairographics.org/archives/cairo/2009-June/017506.html
+ */
+
+#define EPS 1e-30
+/* This should be comfortably smaller than the unit epsilon of the
+ * floating point type used to advance the dashing, yet not small
+ * enough that it underflows to zero.  1e-30 works to foil up to 80
+ * bit extended precision arithmetic.  We want to avoid zero dash
+ * lengths because those trigger special processing in the stroker. */
+
+static void
+do_dash (cairo_t *cr, double dx, double dy, double offset)
+{
+    /* Set the dash pattern to be predominantly ON so that we can
+     * create a reference image by just ignoring the dashing. */
+    static double dash[] = { EPS, EPS/512 };
+    cairo_set_dash (cr, dash, 2, offset);
+    cairo_move_to (cr, 10, 10);
+    cairo_rel_line_to (cr, dx, dy);
+    cairo_stroke (cr);
+    cairo_translate (cr, dx, dy);
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    (void)width; (void)height;
+
+    cairo_set_source_rgb (cr, 1,1,1);
+    cairo_paint (cr);
+    cairo_set_source_rgb (cr, 0,0,0);
+
+    cairo_set_line_width (cr, 10);
+
+    /* The following calls will wedge in various places that try
+     * to advance the dashing in a loop inside the stroker. */
+    do_dash (cr, 30, 30, 0); /* _cairo_stroker_line_to_dashed */
+    do_dash (cr, 30,  0, 0); /* _cairo_rectilinear_stroker_line_to_dashed */
+    do_dash (cr, 30, 30, 1); /* _cairo_stroker_dash_start */
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (dash_infinite_loop,
+            "Test dashing with extremely small dash lengths.",
+            "dash",
+            NULL,
+            100, 100,
+            NULL, draw);
diff --git a/test/dash-no-dash.c b/test/dash-no-dash.c
new file mode 100644 (file)
index 0000000..e03094a
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * Copyright © 2006 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ */
+
+#include "cairo-test.h"
+#include <stdlib.h>
+
+#define PAD            1
+#define LINE_WIDTH     2
+#define HEIGHT         (PAD + 4 * (LINE_WIDTH + PAD))
+#define WIDTH          16
+
+static void
+line (cairo_t *cr)
+{
+    cairo_move_to (cr, PAD, 0.0);
+    cairo_line_to (cr, WIDTH - PAD, 0.0);
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    double dash = 2.0;
+
+    /* We draw in black, so paint white first. */
+    cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); /* white */
+    cairo_paint (cr);
+
+    cairo_set_source_rgb (cr, 0, 0, 0); /* black */
+
+    cairo_translate (cr, 0.0, PAD + LINE_WIDTH / 2);
+
+    /* First draw a solid line... */
+    line (cr);
+    cairo_stroke (cr);
+
+    cairo_translate (cr, 0.0, LINE_WIDTH + PAD);
+
+    /* then a dashed line... */
+    cairo_set_dash (cr, &dash, 1, 0.0);
+    line (cr);
+    cairo_stroke (cr);
+
+    cairo_translate (cr, 0.0, LINE_WIDTH + PAD);
+
+    /* back to solid... */
+    cairo_set_dash (cr, NULL, 0, 0.0);
+    line (cr);
+    cairo_stroke (cr);
+
+    cairo_translate (cr, 0.0, LINE_WIDTH + PAD);
+
+    /* and finally, back to dashed. */
+    cairo_set_dash (cr, &dash, 1, 0.0);
+    line (cr);
+    cairo_stroke (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (dash_no_dash,
+           "Tests that we can actually turn dashing on and off again",
+           "dash, stroke", /* keywords */
+           NULL, /* requirements */
+           WIDTH, HEIGHT,
+           NULL, draw)
diff --git a/test/dash-offset-negative.c b/test/dash-offset-negative.c
new file mode 100644 (file)
index 0000000..8766cc5
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Owen Taylor <otaylor@redhat.com>
+ */
+
+#include "cairo-test.h"
+
+#define IMAGE_WIDTH 19
+#define IMAGE_HEIGHT 19
+
+/* Basic test of dashed strokes, including a test for the negative
+ * dash offset bug:
+ *
+ *     https://bugs.freedesktop.org/show_bug.cgi?id=2729
+ */
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    double dashes[] = { 1 };
+
+    /* We draw in the default black, so paint white first. */
+    cairo_save (cr);
+    cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); /* white */
+    cairo_paint (cr);
+    cairo_restore (cr);
+
+    cairo_set_line_width (cr, 2);
+
+    /* Basic 1-1 dash pattern */
+    cairo_set_dash (cr, dashes, 1, 0.);
+
+    cairo_move_to (cr,  1, 2);
+    cairo_line_to (cr, 18, 2);
+    cairo_stroke (cr);
+
+    /* Adjust path by 0.5. Ideally this would give a constant 50%
+     * gray, (but does not due to the location of the regular sample
+     * grid points. */
+    cairo_move_to (cr, 1.5, 5);
+    cairo_line_to (cr, 18., 5);
+    cairo_stroke (cr);
+
+    /* Offset dash by 0.5, rather than the path */
+    cairo_set_dash (cr, dashes, 1, 0.5);
+
+    cairo_move_to (cr,  1, 8);
+    cairo_line_to (cr, 18, 8);
+    cairo_stroke (cr);
+
+    /* Now, similar tests with negative dash offsets. */
+
+    /* Basic 1-1 dash pattern dashing */
+    cairo_set_dash (cr, dashes, 1, -4);
+
+    cairo_move_to (cr,  1, 11);
+    cairo_line_to (cr, 18, 11);
+    cairo_stroke (cr);
+
+    /* Adjust path by 0.5 */
+    cairo_move_to (cr, 1.5, 14);
+    cairo_line_to (cr, 18., 14);
+    cairo_stroke (cr);
+
+    /* Offset dash by 0.5 */
+    cairo_set_dash (cr, dashes, 1, -3.5);
+
+    cairo_move_to (cr,  1, 17);
+    cairo_line_to (cr, 18, 17);
+    cairo_stroke (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (dash_offset_negative,
+           "Tests cairo_set_dash with a negative offset",
+           "dash, stroke", /* keywords */
+           NULL, /* requirements */
+           IMAGE_WIDTH, IMAGE_HEIGHT,
+           NULL, draw)
diff --git a/test/dash-offset.c b/test/dash-offset.c
new file mode 100644 (file)
index 0000000..0e53687
--- /dev/null
@@ -0,0 +1,83 @@
+/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/*
+ * Copyright 2009 Andrea Canciani
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Andrea Canciani <ranma42@gmail.com>
+ */
+
+#include "cairo-test.h"
+
+/* Lengths of the dashes of the dash patterns */
+static const double dashes[] = { 2, 2, 4, 4 };
+/* Dash offset in userspace units
+ * They always grow by 2, so the dash pattern is
+ * should be shifted by the same amount each time */
+static const double frac_offset[] = { 0, 2, 4, 6 };
+/* Dash offset relative to the whole dash pattern
+ * This corresponds to the non-inverted part only if
+ * the dash pattern has odd length, so the expected result
+ * is the same for every int_offset if the pattern has
+ * even lenght, and inverted each time (or shifted by half
+ * period, which is the same) if the pattern has odd length. */
+static const double int_offset[] = { -2, -1, 0, 1, 2 };
+
+#define PAD 6
+#define STROKE_LENGTH 32
+#define IMAGE_WIDTH (PAD + (STROKE_LENGTH + PAD) * ARRAY_LENGTH (dashes))
+#define IMAGE_HEIGHT (PAD + PAD * ARRAY_LENGTH (int_offset) + PAD * ARRAY_LENGTH (frac_offset) * ARRAY_LENGTH (int_offset))
+
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    double total;
+    size_t i, j, k;
+
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_paint (cr);
+
+    cairo_set_source_rgb (cr, 0, 0, 0);
+    cairo_set_line_width (cr, 2);
+
+    total = 0.0;
+    for (k = 0; k < ARRAY_LENGTH (dashes); ++k) {
+       total += dashes[k];
+       for (i = 0; i < ARRAY_LENGTH (frac_offset); ++i) {
+           for (j = 0; j < ARRAY_LENGTH (int_offset); ++j) {
+               cairo_set_dash (cr, dashes, k + 1, frac_offset[i] + total * int_offset[j]);
+               cairo_move_to (cr, (STROKE_LENGTH + PAD) * k + PAD, PAD * (i + j + ARRAY_LENGTH (frac_offset) * j + 1));
+               cairo_line_to (cr, (STROKE_LENGTH + PAD) * (k + 1), PAD * (i + j + ARRAY_LENGTH (frac_offset) * j + 1));
+               cairo_stroke (cr);
+           }
+       }
+    }
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (dash_offset,
+           "Tests dashes of different length with various offsets",
+           "stroke, dash", /* keywords */
+           NULL, /* requirements */
+           IMAGE_WIDTH, IMAGE_HEIGHT,
+           NULL, draw)
diff --git a/test/dash-scale.c b/test/dash-scale.c
new file mode 100644 (file)
index 0000000..fd51702
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ */
+
+#include "cairo-test.h"
+
+#define LINE_WIDTH     8.
+#define SIZE           (5 * LINE_WIDTH)
+#define PAD            (2 * LINE_WIDTH)
+
+static void
+make_path (cairo_t *cr)
+{
+    cairo_move_to (cr, 0., 0.);
+    cairo_rel_line_to (cr, 0., SIZE);
+    cairo_rel_line_to (cr, SIZE, 0.);
+    cairo_close_path (cr);
+
+    cairo_move_to (cr, 2 * LINE_WIDTH, 0.);
+    cairo_rel_line_to (cr, 3 * LINE_WIDTH, 0.);
+    cairo_rel_line_to (cr, 0., 3 * LINE_WIDTH);
+}
+
+static void
+draw_three_shapes (cairo_t *cr)
+{
+    cairo_save (cr);
+
+    make_path (cr);
+    cairo_set_line_cap (cr, CAIRO_LINE_CAP_BUTT);
+    cairo_set_line_join (cr, CAIRO_LINE_JOIN_BEVEL);
+    cairo_stroke (cr);
+
+    cairo_translate (cr, SIZE + PAD, 0.);
+
+    make_path (cr);
+    cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
+    cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND);
+    cairo_stroke (cr);
+
+    cairo_translate (cr, SIZE + PAD, 0.);
+
+    make_path (cr);
+    cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE);
+    cairo_set_line_join (cr, CAIRO_LINE_JOIN_MITER);
+    cairo_stroke (cr);
+
+    cairo_restore (cr);
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    double dash[] = {1.5 * LINE_WIDTH};
+
+    /* We draw in the default black, so paint white first. */
+    cairo_save (cr);
+    cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); /* white */
+    cairo_paint (cr);
+    cairo_restore (cr);
+
+    cairo_translate (cr, PAD, PAD);
+
+    cairo_set_dash (cr, dash, ARRAY_LENGTH (dash), - 2 * LINE_WIDTH);
+    cairo_set_line_width (cr, LINE_WIDTH);
+    draw_three_shapes (cr);
+
+    cairo_translate (cr, 0, SIZE + 2 * PAD);
+
+    cairo_save (cr);
+    {
+       cairo_set_dash (cr, dash, ARRAY_LENGTH (dash), - 2 * LINE_WIDTH);
+       cairo_set_line_width (cr, LINE_WIDTH);
+       cairo_scale (cr, 1, 2);
+       draw_three_shapes (cr);
+    }
+    cairo_restore (cr);
+
+    cairo_translate (cr, 0, 2 * (SIZE + PAD));
+
+    cairo_save (cr);
+    {
+       cairo_scale (cr, 1, 2);
+       cairo_set_dash (cr, dash, ARRAY_LENGTH (dash), - 2 * LINE_WIDTH);
+       cairo_set_line_width (cr, LINE_WIDTH);
+       draw_three_shapes (cr);
+    }
+    cairo_restore (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (dash_scale,
+           "Test interactions of cairo_set_dash and cairo_scale, (in particular with a non-uniformly scaled pen)",
+           "dash, stroke, transform", /* keywords */
+           NULL, /* requirements */
+           3 * (PAD + SIZE) + PAD,
+           PAD + 5 * SIZE + 2 * (2 * PAD) + PAD,
+           NULL, draw)
diff --git a/test/dash-state.c b/test/dash-state.c
new file mode 100644 (file)
index 0000000..5276aea
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Copyright © 2007 Jeff Smith
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Jeff Smith not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Jeff Smith makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * JEFF SMITH DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL JEFF SMITH BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Jeff Smith <whydoubt@yahoo.com>
+ */
+
+#include "cairo-test.h"
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    double dashes[2] = {20, 20};
+    int a=0, b=0, c=0;
+
+    cairo_set_source_rgb (cr, 0, 0, 0);
+    cairo_paint (cr);
+
+    for (a=0; a<4; a++)
+    for (b=0; b<5; b++)
+    for (c=0; c<5; c++) {
+       cairo_move_to (cr, ((b*5)+c)*60+10, a*60+10);
+       cairo_rel_line_to (cr, 0, b*10);
+       cairo_rel_line_to (cr, c*10, 0);
+
+       cairo_set_source_rgb (cr, 1, 1, 1);
+       cairo_set_line_width (cr, 8);
+       cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
+       cairo_set_dash (cr, dashes, 2, a*10);
+       cairo_stroke_preserve (cr);
+
+       cairo_set_source_rgb (cr, 0, 0.5, 1);
+       cairo_set_line_width (cr, 2);
+       cairo_set_line_cap (cr, CAIRO_LINE_CAP_BUTT);
+       cairo_set_dash (cr, 0, 0, 0);
+       cairo_stroke (cr);
+    }
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (dash_state,
+           "Tries to explore the state space of the dashing code",
+           "dash, stroke", /* keywords */
+           NULL, /* requirements */
+           25*60, 4*60,
+           NULL, draw)
diff --git a/test/dash-zero-length.c b/test/dash-zero-length.c
new file mode 100644 (file)
index 0000000..5ab944b
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * Copyright © 2006 Jeff Muizelaar
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Jeff Muizelaar. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Jeff Muizelaar. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * JEFF MUIZELAAR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL JEFF MUIZELAAR BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Jeff Muizelaar <jeff@infidigm.net>
+ */
+
+#include "cairo-test.h"
+
+#define IMAGE_WIDTH 19
+#define IMAGE_HEIGHT 61
+
+/* A test of the two extremes of dashing: a solid line
+ * and an invisible one. Also test that capping works
+ * on invisible lines.
+ */
+
+static void
+draw_dash (cairo_t *cr, double *dash, int num_dashes)
+{
+    cairo_set_dash (cr, dash, num_dashes, 0.0);
+    cairo_move_to (cr,  1, 2);
+    cairo_line_to (cr, 18, 2);
+    cairo_stroke (cr);
+    cairo_translate (cr, 0, 3);
+}
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    static double solid_line[] = { 4, 0 };
+    static double invisible_line[] = { 0, 4 };
+    static double dotted_line[] = { 0, 6 };
+    static double zero_1_of_3[] = { 0, 2, 3 };
+    static double zero_2_of_3[] = { 1, 0, 3 };
+    static double zero_3_of_3[] = { 1, 2, 0 };
+    static double zero_1_of_4[] = { 0, 2, 3, 4 };
+    static double zero_2_of_4[] = { 1, 0, 3, 4 };
+    static double zero_3_of_4[] = { 1, 2, 0, 4 };
+    static double zero_4_of_4[] = { 1, 2, 3, 0 };
+    static double zero_1_2_of_4[] = { 0, 0, 3, 4 };
+    static double zero_1_3_of_4[] = { 0, 2, 0, 4 };
+/* Clearly it would be nice to draw this one as well, but it seems to trigger a bug in ghostscript. */
+#if BUG_FIXED_IN_GHOSTSCRIPT
+    static double zero_1_4_of_4[] = { 0, 2, 3, 0 };
+#endif
+    static double zero_2_3_of_4[] = { 1, 0, 0, 4 };
+    static double zero_2_4_of_4[] = { 1, 0, 3, 0 };
+    static double zero_3_4_of_4[] = { 1, 2, 0, 0 };
+    static double zero_1_2_3_of_4[] = { 0, 0, 0, 4 };
+    static double zero_1_2_4_of_4[] = { 0, 0, 3, 0 };
+    static double zero_1_3_4_of_4[] = { 0, 2, 0, 0 };
+    static double zero_2_3_4_of_4[] = { 1, 0, 0, 0 };
+
+    cairo_set_source_rgb (cr, 1, 0, 0);
+    cairo_set_line_width (cr, 2);
+
+    draw_dash (cr, solid_line, 2);
+    draw_dash (cr, invisible_line, 2);
+
+    cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
+    draw_dash (cr, dotted_line, 2);
+    cairo_set_line_cap (cr, CAIRO_LINE_CAP_BUTT);
+
+    draw_dash (cr, zero_1_of_3, 3);
+    draw_dash (cr, zero_2_of_3, 3);
+    draw_dash (cr, zero_3_of_3, 3);
+    draw_dash (cr, zero_1_of_4, 4);
+    draw_dash (cr, zero_2_of_4, 4);
+    draw_dash (cr, zero_3_of_4, 4);
+    draw_dash (cr, zero_4_of_4, 4);
+    draw_dash (cr, zero_1_2_of_4, 4);
+    draw_dash (cr, zero_1_3_of_4, 4);
+#if BUG_FIXED_IN_GHOSTSCRIPT
+    draw_dash (cr, zero_1_4_of_4, 4);
+#endif
+    draw_dash (cr, zero_2_3_of_4, 4);
+    draw_dash (cr, zero_2_4_of_4, 4);
+    draw_dash (cr, zero_3_4_of_4, 4);
+    draw_dash (cr, zero_1_2_3_of_4, 4);
+    draw_dash (cr, zero_1_2_4_of_4, 4);
+    draw_dash (cr, zero_1_3_4_of_4, 4);
+    draw_dash (cr, zero_2_3_4_of_4, 4);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (dash_zero_length,
+           "Tests cairo_set_dash with zero length",
+           "dash, stroke", /* keywords */
+           NULL, /* requirements */
+           IMAGE_WIDTH, IMAGE_HEIGHT,
+           NULL, draw)
diff --git a/test/degenerate-arc.c b/test/degenerate-arc.c
new file mode 100644 (file)
index 0000000..ab65774
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * Copyright © 2008 Chris Wilson
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Chris Wilson not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Chris Wilson makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * CHRIS WILSON DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL CHRIS WILSON BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+/* This test case exercises a "Potential division by zero in cairo_arc"
+ * reported by Luiz Americo Pereira Camara <luizmed@oi.com.br>,
+ * http://lists.cairographics.org/archives/cairo/2008-May/014054.html.
+ */
+
+#include "cairo-test.h"
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    int n;
+
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_paint (cr);
+
+    cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
+
+    cairo_set_line_width (cr, 5);
+    cairo_set_source_rgb (cr, 0, 1, 0);
+    for (n = 0; n < 8; n++) {
+       double theta = n * 2 * M_PI / 8;
+       cairo_new_sub_path (cr);
+       cairo_arc (cr, 20, 20, 15, theta, theta);
+       cairo_close_path (cr);
+    }
+    cairo_stroke (cr);
+
+    cairo_set_line_width (cr, 2);
+    cairo_set_source_rgb (cr, 0, 0, 1);
+    for (n = 0; n < 8; n++) {
+       double theta = n * 2 * M_PI / 8;
+       cairo_move_to (cr, 20, 20);
+       cairo_arc (cr, 20, 20, 15, theta, theta);
+    }
+    cairo_stroke (cr);
+
+    cairo_set_source_rgb (cr, 1, 0, 0);
+    cairo_arc (cr, 20, 20, 2, 0, 2*M_PI);
+    cairo_fill (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (degenerate_arc,
+           "Tests the behaviour of degenerate arcs",
+           "degenerate", /* keywords */
+           NULL, /* requirements */
+           40, 40,
+           NULL, draw)
diff --git a/test/degenerate-arcs.c b/test/degenerate-arcs.c
new file mode 100644 (file)
index 0000000..2470828
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2010 Intel Corporation
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Chris Wilson not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Chris Wilson makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * CHRIS WILSON DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL CHRIS WILSON BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+
+#define WIDTH 20
+#define HEIGHT 20
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_paint (cr);
+
+    cairo_set_source_rgb (cr, 0.3, 0.4, 0.5);
+
+    /* This should be equivalent to a simple rectangle, such as may be
+     * constructed for a rounded-rectangle with corner radii of 0...
+     */
+    cairo_arc (cr, 5, 5, 0, M_PI, 3*M_PI/2);
+    cairo_arc (cr, 15, 5, 0, 3*M_PI/2, 2*M_PI);
+    cairo_arc (cr, 15, 15, 0, 0, M_PI/2);
+    cairo_arc (cr, 5, 15, 0, M_PI/2, M_PI);
+
+    cairo_fill (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (degenerate_arcs,
+           "Tests path construction using a series of degenerate (radius=0) arcs",
+           "arc, fill", /* keywords */
+           NULL, /* requirements */
+           WIDTH, HEIGHT,
+           NULL, draw)
diff --git a/test/degenerate-curve-to.c b/test/degenerate-curve-to.c
new file mode 100644 (file)
index 0000000..a081a3b
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ * Copyright © 2005 Red Hat, Inc.
+ * Copyright © 2009 Chris Wilson
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ *         Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+
+#define SIZE 30
+
+/* Another attempt at avoiding unnecessary splines was made, where
+ * a curve-to that ended on the same point as it began were discarded.
+ */
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); /* white */
+    cairo_paint (cr);
+
+    cairo_set_line_width (cr, 1.0);
+    cairo_set_line_cap (cr, CAIRO_LINE_CAP_BUTT);
+    cairo_set_line_join (cr, CAIRO_LINE_JOIN_BEVEL);
+    cairo_set_source_rgb (cr, 0.0, 0.0, 0.0); /* black */
+
+    /* entirely degenerate */
+    cairo_move_to (cr,
+                   2.5, 2.5);
+    cairo_curve_to (cr,
+                   2.5, 2.5,
+                   2.5, 2.5,
+                   2.5, 2.5);
+    cairo_stroke (cr);
+
+    /* horizontal */
+    cairo_move_to (cr,
+                    5.5, 2.5);
+    cairo_curve_to (cr,
+                   22.0, 2.5,
+                   -0.5, 2.5,
+                    5.5, 2.5);
+    cairo_stroke (cr);
+
+    /* vertical */
+    cairo_move_to (cr,
+                   7.5,  0.0);
+    cairo_curve_to (cr,
+                   7.5, 11.0,
+                   7.5,  0.0,
+                   7.5,  0.0);
+    cairo_stroke (cr);
+
+    cairo_translate (cr, 15, 0);
+
+    /* horizontal/vertical */
+    cairo_move_to (cr,
+                    5.5,  0.5);
+    cairo_curve_to (cr,
+                   -0.5,  0.5,
+                    5.5, 10.5,
+                    5.5,  0.5);
+
+    cairo_translate (cr, 10, 0);
+
+    /* vertical/horizontal */
+    cairo_move_to (cr,
+                    5.5,  0.0);
+    cairo_curve_to (cr,
+                    5.5, 11.0,
+                   10.5,  0.0,
+                    5.5,  0.0);
+    cairo_stroke (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (degenerate_curve_to,
+           "Test optimization treating degenerate curve_to as line_to",
+           "path", /* keywords */
+           NULL, /* requirements */
+           40,
+           5,
+           NULL, draw)
diff --git a/test/degenerate-dash.c b/test/degenerate-dash.c
new file mode 100644 (file)
index 0000000..c13792f
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * Copyright © 2008 Chris Wilson
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Chris Wilson not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Chris Wilson makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * CHRIS WILSON DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL CHRIS WILSON BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ *
+ * Based on an original test case by M Joonas Pihlaja.
+ */
+
+#include "cairo-test.h"
+
+#define PAD 5
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    const double dashes[] = { 25, 25 };
+    cairo_line_join_t joins[] = {
+       CAIRO_LINE_JOIN_ROUND,
+       CAIRO_LINE_JOIN_MITER,
+       CAIRO_LINE_JOIN_BEVEL
+    };
+    cairo_line_cap_t caps[] = {
+       CAIRO_LINE_CAP_ROUND,
+       CAIRO_LINE_CAP_SQUARE,
+       CAIRO_LINE_CAP_BUTT,
+    };
+    int i, j;
+
+    cairo_set_source_rgb (cr, 1,1,1);
+    cairo_paint (cr);
+
+    cairo_set_source_rgb (cr, 1, 0, 0);
+
+    cairo_set_dash (cr, dashes, 2, 0.);
+    cairo_set_line_width (cr, 10);
+
+    cairo_translate (cr, 5 + PAD, 5 + PAD);
+
+    for (i = 0; i < ARRAY_LENGTH (joins); i++) {
+       cairo_set_line_join (cr, joins[i]);
+       cairo_save (cr);
+
+       for (j = 0; j < ARRAY_LENGTH (caps); j++) {
+           cairo_set_line_cap (cr, caps[j]);
+
+           cairo_move_to (cr,  0, 0);
+           cairo_line_to (cr, 50, 0);
+           cairo_line_to (cr, 50,50);
+           cairo_stroke (cr);
+
+           cairo_translate (cr, 75, 0);
+       }
+       cairo_restore (cr);
+
+       cairo_translate (cr, 0, 75);
+    }
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (degenerate_dash,
+           "Tests the behaviour of dashed segments that end on a off-on transition",
+           "dash, degenerate", /* keywords */
+           NULL, /* requirementts */
+           210 + 2*PAD, 210 + 2*PAD,
+           NULL, draw)
diff --git a/test/degenerate-linear-gradient.c b/test/degenerate-linear-gradient.c
new file mode 100644 (file)
index 0000000..0d174fc
--- /dev/null
@@ -0,0 +1,81 @@
+/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/*
+ * Copyright 2010 Andrea Canciani
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Andrea Canciani <ranma42@gmail.com>
+ */
+
+#include "cairo-test.h"
+
+#define NUM_EXTEND 4
+#define HEIGHT 16
+#define WIDTH 16
+#define PAD 3
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_pattern_t *pattern;
+    unsigned int j;
+
+    cairo_extend_t extend[NUM_EXTEND] = {
+       CAIRO_EXTEND_NONE,
+       CAIRO_EXTEND_REPEAT,
+       CAIRO_EXTEND_REFLECT,
+       CAIRO_EXTEND_PAD
+    };
+
+    cairo_test_paint_checkered (cr);
+
+    pattern = cairo_pattern_create_linear (WIDTH/2, HEIGHT/2, WIDTH/2, HEIGHT/2);
+
+    cairo_pattern_add_color_stop_rgba (pattern, 0, 1, 0, 0, 1);
+    cairo_pattern_add_color_stop_rgba (pattern, sqrt (1.0 / 2.0), 0, 1, 0, 0);
+    cairo_pattern_add_color_stop_rgba (pattern, 1, 0, 0, 1, 0.5);
+
+    cairo_translate (cr, PAD, PAD);
+
+    for (j = 0; j < NUM_EXTEND; j++) {
+       cairo_reset_clip (cr);
+       cairo_rectangle (cr, 0, 0, WIDTH, HEIGHT);
+       cairo_clip (cr);
+       
+       cairo_pattern_set_extend (pattern, extend[j]);
+       
+       cairo_set_source (cr, pattern);
+       cairo_paint (cr);
+       
+       cairo_translate (cr, WIDTH+PAD, 0);
+    }
+
+    cairo_pattern_destroy (pattern);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (degenerate_linear_gradient,
+           "Tests degenerate linear gradients",
+           "linear, pattern, extend", /* keywords */
+           NULL, /* requirements */
+           (WIDTH+PAD) * NUM_EXTEND + PAD, 1*(HEIGHT + PAD) + PAD,
+           NULL, draw)
diff --git a/test/degenerate-path.c b/test/degenerate-path.c
new file mode 100644 (file)
index 0000000..c690730
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+ * Copyright © 2006 Jeff Muizelaar
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Jeff Muizelaar not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Jeff Muizelaar makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * JEFF MUIZELAAR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL JEFF MUIZELAAR BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Jeff Muizelaar <jeff@infidigm.net>
+ */
+
+#include "cairo-test.h"
+
+#define PAD 3.0
+#define LINE_WIDTH 6.0
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    const cairo_line_cap_t cap[] = { CAIRO_LINE_CAP_ROUND, CAIRO_LINE_CAP_SQUARE, CAIRO_LINE_CAP_BUTT };
+    size_t i;
+    double dash[] = {2, 2};
+    double dash_long[] = {6, 6};
+
+    cairo_set_source_rgb (cr, 1, 0, 0);
+
+    for (i = 0; i < ARRAY_LENGTH (cap); i++) {
+       cairo_save (cr);
+
+       cairo_set_line_cap (cr, cap[i]);
+
+       /* simple degenerate paths */
+       cairo_set_line_width (cr, LINE_WIDTH);
+       cairo_move_to (cr, LINE_WIDTH, LINE_WIDTH);
+       cairo_line_to (cr, LINE_WIDTH, LINE_WIDTH);
+       cairo_stroke (cr);
+
+       cairo_translate (cr, 0, 3*PAD);
+       cairo_move_to (cr, LINE_WIDTH, LINE_WIDTH);
+       cairo_close_path (cr);
+       cairo_stroke (cr);
+
+       /* degenerate paths starting with dash on */
+       cairo_set_dash (cr, dash, 2, 0.);
+
+       cairo_translate (cr, 0, 3*PAD);
+       cairo_move_to (cr, LINE_WIDTH, LINE_WIDTH);
+       cairo_line_to (cr, LINE_WIDTH, LINE_WIDTH);
+       cairo_stroke (cr);
+
+       cairo_translate (cr, 0, 3*PAD);
+       cairo_move_to (cr, LINE_WIDTH, LINE_WIDTH);
+       cairo_close_path (cr);
+       cairo_stroke (cr);
+
+       /* degenerate paths starting with dash off */
+       /* these should not draw anything */
+       cairo_set_dash (cr, dash, 2, 2.);
+
+       cairo_translate (cr, 0, 3*PAD);
+       cairo_move_to (cr, LINE_WIDTH, LINE_WIDTH);
+       cairo_line_to (cr, LINE_WIDTH, LINE_WIDTH);
+       cairo_stroke (cr);
+
+       cairo_translate (cr, 0, 3*PAD);
+       cairo_move_to (cr, LINE_WIDTH, LINE_WIDTH);
+       cairo_close_path (cr);
+       cairo_stroke (cr);
+
+       /* this should draw a single degenerate sub-path
+        * at the end of the path */
+       cairo_set_dash (cr, dash_long, 2, 6.);
+
+       cairo_translate (cr, 0, 3*PAD);
+       cairo_move_to (cr, LINE_WIDTH + 6.0, LINE_WIDTH);
+       cairo_line_to (cr, LINE_WIDTH, LINE_WIDTH);
+       cairo_stroke (cr);
+
+       /* this should draw a single degenerate sub-path
+        * at the end of the path. The difference between this
+        * and the above is that this ends with a degenerate sub-path*/
+       cairo_set_dash (cr, dash_long, 2, 6.);
+
+       cairo_translate (cr, 0, 3*PAD);
+       cairo_move_to (cr, LINE_WIDTH + 6.0, LINE_WIDTH);
+       cairo_line_to (cr, LINE_WIDTH, LINE_WIDTH);
+       cairo_line_to (cr, LINE_WIDTH, LINE_WIDTH);
+       cairo_stroke (cr);
+
+       cairo_restore (cr);
+
+       cairo_translate (cr, PAD+LINE_WIDTH+PAD, 0);
+    }
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (degenerate_path,
+           "Tests the behaviour of degenerate paths with different cap types",
+           "degenerate", /* keywords */
+           NULL, /* requirements */
+           3*(PAD+LINE_WIDTH+PAD), 8*(LINE_WIDTH+PAD) + PAD,
+           NULL, draw)
diff --git a/test/degenerate-pen.c b/test/degenerate-pen.c
new file mode 100644 (file)
index 0000000..ec8bd19
--- /dev/null
@@ -0,0 +1,105 @@
+/*
+ * Copyright © 2007 Red Hat, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ */
+
+#include "cairo-test.h"
+
+#define SIZE   20
+#define PAD    5
+#define WIDTH  (PAD + 3 * (PAD + SIZE) + PAD)
+#define HEIGHT (PAD + SIZE + PAD)
+
+/* We're demonstrating here a bug originally reported by Benjamin Otte
+ * on the cairo mailing list here, (after he ran into this problem
+ * with various flash animations):
+ *
+ *     [cairo] Assertion `i < pen->num_vertices' failed in 1.4.10
+ *     http://lists.cairographics.org/archives/cairo/2007-August/011282.html
+ *
+ * The problem shows up with an extreme transformation matrix that
+ * collapses the pen to a single line, (which means that
+ * _cairo_slope_compare cannot handle adjacent vertices in the pen
+ * since they have parallel slope).
+ *
+ * This test case tests degenerate pens in several directions and uses
+ * round caps to force the stroking code to attempt to walk around the
+ * pen doing slope comparisons.
+ */
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_paint (cr);
+
+    cairo_set_source_rgb (cr, 0, 0, 0);
+    cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND);
+
+    cairo_translate (cr, PAD, PAD);
+
+    /* First compress the pen to a vertical line. */
+    cairo_rectangle (cr, 0, 0, SIZE, SIZE);
+    cairo_curve_to (cr, SIZE / 2, 0, SIZE, SIZE / 2, SIZE, SIZE);
+    cairo_save (cr);
+    {
+       cairo_scale (cr, 0.000001, 1.0);
+       cairo_stroke (cr);
+    }
+    cairo_restore (cr);
+
+    cairo_translate (cr, PAD + SIZE, 0);
+
+    /* Then compress the pen to a horizontal line. */
+    cairo_rectangle (cr, 0, 0, SIZE, SIZE);
+    cairo_curve_to (cr, SIZE / 2, 0, SIZE, SIZE / 2, SIZE, SIZE);
+    cairo_save (cr);
+    {
+       cairo_scale (cr, 1.0, 0.000001);
+       cairo_stroke (cr);
+    }
+    cairo_restore (cr);
+
+    cairo_translate (cr, PAD + SIZE, 0);
+
+    /* Finally a line at an angle. */
+    cairo_rectangle (cr, 0, 0, SIZE, SIZE);
+    cairo_curve_to (cr, SIZE / 2, 0, SIZE, SIZE / 2, SIZE, SIZE);
+    cairo_save (cr);
+    {
+       cairo_rotate (cr, M_PI / 4.0);
+       cairo_scale (cr, 0.000001, 1.0);
+       cairo_stroke (cr);
+    }
+    cairo_restore (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (degenerate_pen,
+           "Test round joins with a pen that's transformed to a line",
+           "degenerate", /* keywords */
+           NULL, /* requirements */
+           WIDTH, HEIGHT,
+           NULL, draw)
diff --git a/test/degenerate-radial-gradient.c b/test/degenerate-radial-gradient.c
new file mode 100644 (file)
index 0000000..1466645
--- /dev/null
@@ -0,0 +1,93 @@
+/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/*
+ * Copyright 2010 Andrea Canciani
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Andrea Canciani <ranma42@gmail.com>
+ */
+
+#include "cairo-test.h"
+
+#define NUM_EXTEND 4
+#define HEIGHT 32
+#define WIDTH 32
+#define PAD 6
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_pattern_t *pattern;
+    unsigned int i, j;
+
+    cairo_extend_t extend[NUM_EXTEND] = {
+       CAIRO_EXTEND_NONE,
+       CAIRO_EXTEND_REPEAT,
+       CAIRO_EXTEND_REFLECT,
+       CAIRO_EXTEND_PAD
+    };
+
+    cairo_test_paint_checkered (cr);
+
+    cairo_translate (cr, PAD, PAD);
+
+    for (i = 0; i < 3; i++) {
+        cairo_save (cr);
+       
+       for (j = 0; j < NUM_EXTEND; j++) {
+           cairo_reset_clip (cr);
+           cairo_rectangle (cr, 0, 0, WIDTH, HEIGHT);
+           cairo_clip (cr);
+
+           if (i == 0)
+               pattern = cairo_pattern_create_radial (WIDTH/2, HEIGHT/2, 0, WIDTH/2, HEIGHT/2, 0);
+           else if (i == 1)
+               pattern = cairo_pattern_create_radial (WIDTH/2, HEIGHT/2, 2*PAD, WIDTH/2, HEIGHT/2, 2*PAD);
+           else if (i == 2)
+               pattern = cairo_pattern_create_radial (PAD, PAD, 0, WIDTH-PAD, HEIGHT-PAD, 0);
+
+           cairo_pattern_add_color_stop_rgba (pattern, 0, 1, 0, 0, 1);
+           cairo_pattern_add_color_stop_rgba (pattern, sqrt (1.0 / 2.0), 0, 1, 0, 0);
+           cairo_pattern_add_color_stop_rgba (pattern, 1, 0, 0, 1, 0.5);
+
+           cairo_pattern_set_extend (pattern, extend[j]);
+
+           cairo_set_source (cr, pattern);
+           cairo_paint (cr);
+
+           cairo_pattern_destroy (pattern);
+
+           cairo_translate (cr, WIDTH+PAD, 0);
+       }
+
+       cairo_restore (cr);
+       cairo_translate (cr, 0, HEIGHT+PAD);
+    }
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (degenerate_radial_gradient,
+           "Tests degenerate radial gradients",
+           "radial, pattern, extend", /* keywords */
+           NULL, /* requirements */
+           (WIDTH+PAD) * NUM_EXTEND + PAD, 3*(HEIGHT + PAD) + PAD,
+           NULL, draw)
diff --git a/test/degenerate-rel-curve-to.c b/test/degenerate-rel-curve-to.c
new file mode 100644 (file)
index 0000000..d7c0523
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * Copyright © 2005 Red Hat, Inc.
+ * Copyright © 2009 Chris Wilson
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ *         Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+
+#define SIZE 30
+
+/* Another attempt at avoiding unnecessary splines was made, where
+ * a curve-to that ended on the same point as it began were discarded.
+ * The same bug was made to rel-curve-to as well, hence this test.
+ */
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); /* white */
+    cairo_paint (cr);
+
+    cairo_set_line_width (cr, 1.0);
+    cairo_set_line_cap (cr, CAIRO_LINE_CAP_BUTT);
+    cairo_set_line_join (cr, CAIRO_LINE_JOIN_BEVEL);
+    cairo_set_source_rgb (cr, 0.0, 0.0, 0.0); /* black */
+
+    /* entirely degenerate */
+    cairo_move_to (cr, 2.5, 2.5);
+    cairo_rel_curve_to (cr,
+                       0., 0.,
+                       0., 0.,
+                       0., 0.);
+    cairo_stroke (cr);
+
+    /* horizontal */
+    cairo_move_to (cr, 5.5, 2.5);
+    cairo_rel_curve_to (cr,
+                        10., 0.,
+                       -10., 0.,
+                        0., 0.);
+    cairo_stroke (cr);
+
+    /* vertical */
+    cairo_move_to (cr, 5.5, 2.5);
+    cairo_rel_curve_to (cr,
+                       0.,  10.,
+                       0., -10.,
+                       0.,   0.);
+    cairo_stroke (cr);
+
+    cairo_translate (cr, 15, 0);
+
+    /* horizontal/vertical */
+    cairo_move_to (cr, 5.5, 0.5);
+    cairo_rel_curve_to (cr,
+                       -6.,  0.,
+                        0., 10.,
+                        0.,  0.);
+
+    cairo_translate (cr, 10, 0);
+
+    /* vertical/horizontal */
+    cairo_move_to (cr, 5.5, 0.0);
+    cairo_rel_curve_to (cr,
+                       0., 11.,
+                       5.,  0.,
+                       0.,  0.);
+    cairo_stroke (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (degenerate_rel_curve_to,
+           "Test optimization treating degenerate rel-curve-to as line-to",
+           "path", /* keywords */
+           NULL, /* requirements */
+           40,
+           5,
+           NULL, draw)
diff --git a/test/device-offset-fractional.c b/test/device-offset-fractional.c
new file mode 100644 (file)
index 0000000..adc2116
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * Copyright © 2006 Red Hat, Inc.
+ * Copyright © 2008 Chris Wilson
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ *         Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+
+#define SIZE 10
+#define PAD 4
+#define COUNT 4
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_surface_t *surface;
+    cairo_t *cr2;
+    int i,j;
+
+    /* Fill the background */
+    cairo_set_source_rgb (cr, 1, 1, 1); /* white */
+    cairo_paint (cr);
+
+    surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, SIZE, SIZE);
+    cr2 = cairo_create (surface);
+    cairo_surface_destroy (surface);
+
+    cairo_set_source_rgb (cr2, 0, 0, 1); /* blue */
+    cairo_paint (cr2);
+
+    cairo_set_source_surface (cr, cairo_get_target (cr2), 0, 0);
+    cairo_destroy (cr2);
+
+    for (i = 0; i < COUNT; i++) {
+       for (j = 0; j < COUNT; j++) {
+           cairo_surface_set_device_offset (surface,
+                                            -i*(SIZE+PAD+.5)-PAD,
+                                            -j*(SIZE+PAD+.5)-PAD);
+           cairo_paint (cr);
+       }
+    }
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (device_offset_fractional,
+           "Test using image surfaces with fractional device-offsets as sources.",
+           "device-offset", /* keywords */
+           NULL, /* requirements */
+           COUNT*(SIZE+PAD+.5)+PAD, COUNT*(SIZE+PAD+.5)+PAD,
+           NULL, draw)
diff --git a/test/device-offset-positive.c b/test/device-offset-positive.c
new file mode 100644 (file)
index 0000000..5afe973
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * Copyright © 2006 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ */
+
+#include "cairo-test.h"
+#include <stddef.h>
+
+#define SIZE 10
+#define PAD 2
+
+static void
+draw_square (cairo_t *cr)
+{
+    cairo_rectangle (cr,
+                    PAD, PAD,
+                    SIZE - 2 * PAD,
+                    SIZE - 2 * PAD);
+    cairo_fill (cr);
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_surface_t *surface, *target;
+    cairo_t *cr2;
+
+    /* First draw a shape in blue on the original destination. */
+    cairo_set_source_rgb (cr, 0, 0, 1); /* blue */
+    draw_square (cr);
+
+    /* Then, create an offset surface and repeat the drawing in red. */
+    target = cairo_get_group_target (cr);
+    surface = cairo_surface_create_similar (target,
+                                           cairo_surface_get_content (target),
+                                           SIZE / 2, SIZE / 2);
+    cr2 = cairo_create (surface);
+    cairo_surface_destroy (surface);
+
+    cairo_set_source_rgb (cr2, 1, 0, 0); /* red */
+    draw_square (cr2);
+
+    /* Finally, copy the offset surface to the original destination.
+     * The final result should be a blue square with the upper-left
+     * quarter red. */
+    surface = cairo_surface_reference (cairo_get_target (cr2));
+    cairo_destroy (cr2);
+    cairo_surface_set_device_offset (surface, + SIZE / 2, + SIZE / 2);
+    cairo_set_source_surface (cr, surface, SIZE / 2, SIZE / 2);
+    cairo_surface_destroy (surface);
+
+    cairo_paint (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (device_offset_positive,
+           "Simple test using a surface with a positive device-offset as a source.",
+           "device-offset", /* keywords */
+           NULL, /* requirements */
+           SIZE, SIZE,
+           NULL, draw)
diff --git a/test/device-offset-scale.c b/test/device-offset-scale.c
new file mode 100644 (file)
index 0000000..3df3d38
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * Copyright © 2008 Mozilla Corporation
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Mozilla Corporation not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Mozilla Corporation makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * MOZILLA CORPORATION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL MOZILLA CORPORATION BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Authors: Michael Ventnor, Jeff Muizelaar
+ */
+
+#include "cairo-test.h"
+
+#define WIDTH 20
+#define HEIGHT 20
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_surface_t *second;
+    cairo_t *second_cr;
+
+    /* fill with black so we don't need an rgb test case */
+    cairo_set_source_rgb (cr, 0, 0, 0);
+    cairo_paint (cr);
+
+    cairo_scale (cr, 0.5, 0.5);
+
+    /* draw the first rectangle */
+    cairo_set_source_rgb (cr, 0, 0, 0.4);
+    cairo_rectangle (cr, 6, 6, 10, 10);
+    cairo_fill (cr);
+
+    /* adjust the offset so that the second rectangle will fit on the surface */
+    second = cairo_image_surface_create (CAIRO_FORMAT_A8, 10, 10);
+    cairo_surface_set_device_offset (second, -6, -6);
+
+    /* draw the second rectangle:
+     * this rectangle should end up in the same place as the rectangle above
+     * independent of the device offset of the surface it is painted on*/
+    second_cr = cairo_create (second);
+    cairo_surface_destroy (second);
+    cairo_rectangle (second_cr, 6, 6, 10, 10);
+    cairo_fill (second_cr);
+
+    /* paint the second rectangle on top of the first rectangle */
+    cairo_set_source_rgb (cr, 0.5, 0.5, 0);
+    cairo_mask_surface (cr, cairo_get_target (second_cr), 0, 0);
+    cairo_destroy (second_cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (device_offset_scale,
+           "Test that the device-offset transform is transformed by the ctm.",
+           "device-offset", /* keywords */
+           NULL, /* requirements */
+           WIDTH, HEIGHT,
+           NULL, draw)
diff --git a/test/device-offset.c b/test/device-offset.c
new file mode 100644 (file)
index 0000000..1bf5283
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * Copyright © 2006 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ */
+
+#include "cairo-test.h"
+#include <stddef.h>
+
+#define SIZE 10
+#define PAD 2
+
+static void
+draw_square (cairo_t *cr)
+{
+    cairo_rectangle (cr,
+                    PAD, PAD,
+                    SIZE - 2 * PAD,
+                    SIZE - 2 * PAD);
+    cairo_fill (cr);
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_surface_t *surface, *target;
+    cairo_t *cr2;
+
+    /* First draw a shape in blue on the original destination. */
+    cairo_set_source_rgb (cr, 0, 0, 1); /* blue */
+    draw_square (cr);
+
+    /* Then, create an offset surface and repeat the drawing in red. */
+    target = cairo_get_group_target (cr);
+    surface = cairo_surface_create_similar (target,
+                                           cairo_surface_get_content (target),
+                                           SIZE / 2, SIZE / 2);
+    cairo_surface_set_device_offset (surface, - SIZE / 2, - SIZE / 2);
+    cr2 = cairo_create (surface);
+    cairo_surface_destroy (surface);
+
+    cairo_set_source_rgb (cr2, 1, 0, 0); /* red */
+    draw_square (cr2);
+
+
+    /* Finally, copy the offset surface to the original destination.
+    * The final result should be a blue square with the lower-right
+    * quarter red. */
+    cairo_set_source_surface (cr, cairo_get_target (cr2), 0, 0);
+    cairo_destroy (cr2);
+
+    cairo_paint (cr);
+
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (device_offset,
+           "Simple test using a surface with a negative device-offset as a source.",
+           "device-offset", /* keywords */
+           NULL, /* requirements */
+           SIZE, SIZE,
+           NULL, draw)
diff --git a/test/drunkard-tails.c b/test/drunkard-tails.c
new file mode 100644 (file)
index 0000000..3d3e09a
--- /dev/null
@@ -0,0 +1,135 @@
+/*
+ * Copyright © 2011 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+
+#define LINE_WIDTH     10.
+#define SIZE           (5 * LINE_WIDTH)
+#define PAD            (3 * LINE_WIDTH)
+
+static uint32_t state;
+
+static double
+uniform_random (double minval, double maxval)
+{
+    static uint32_t const poly = 0x9a795537U;
+    uint32_t n = 32;
+    while (n-->0)
+       state = 2*state < state ? (2*state ^ poly) : 2*state;
+    return minval + state * (maxval - minval) / 4294967296.0;
+}
+
+static void
+make_path (cairo_t *cr)
+{
+    int i;
+
+    state = 0xdeadbeef;
+
+    cairo_move_to (cr, SIZE/2, SIZE/2);
+    for (i = 0; i < 200; i++) {
+       double theta = uniform_random (-M_PI, M_PI);
+       cairo_rel_line_to (cr,
+                          cos (theta) * LINE_WIDTH / 4,
+                          sin (theta) * LINE_WIDTH / 4);
+    }
+}
+
+static void
+draw_caps_joins (cairo_t *cr)
+{
+    cairo_save (cr);
+
+    cairo_translate (cr, PAD, PAD);
+
+    cairo_reset_clip (cr);
+    cairo_rectangle (cr, 0, 0, SIZE, SIZE);
+    cairo_clip (cr);
+
+    make_path (cr);
+    cairo_set_line_cap (cr, CAIRO_LINE_CAP_BUTT);
+    cairo_set_line_join (cr, CAIRO_LINE_JOIN_BEVEL);
+    cairo_stroke (cr);
+
+    cairo_translate (cr, SIZE + PAD, 0.);
+
+    cairo_reset_clip (cr);
+    cairo_rectangle (cr, 0, 0, SIZE, SIZE);
+    cairo_clip (cr);
+
+    make_path (cr);
+    cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
+    cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND);
+    cairo_stroke (cr);
+
+    cairo_translate (cr, SIZE + PAD, 0.);
+
+    cairo_reset_clip (cr);
+    cairo_rectangle (cr, 0, 0, SIZE, SIZE);
+    cairo_clip (cr);
+
+    make_path (cr);
+    cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE);
+    cairo_set_line_join (cr, CAIRO_LINE_JOIN_MITER);
+    cairo_stroke (cr);
+
+    cairo_restore (cr);
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    /* We draw in the default black, so paint white first. */
+    cairo_save (cr);
+    cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); /* white */
+    cairo_paint (cr);
+    cairo_restore (cr);
+
+    cairo_set_line_width (cr, LINE_WIDTH);
+
+    draw_caps_joins (cr);
+
+    cairo_save (cr);
+    /* and reflect to generate the opposite vertex ordering */
+    cairo_translate (cr, 0, height);
+    cairo_scale (cr, 1, -1);
+
+    draw_caps_joins (cr);
+    cairo_restore (cr);
+
+    cairo_translate (cr, 0, SIZE + PAD);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (drunkard_tails,
+           "Test caps and joins on short tail segments",
+           "stroke cap join", /* keywords */
+           NULL, /* requirements */
+           3 * (PAD + SIZE) + PAD,
+           2 * (PAD + SIZE) + PAD,
+           NULL, draw)
+
diff --git a/test/error-setters.c b/test/error-setters.c
new file mode 100644 (file)
index 0000000..ff65ad4
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ * Copyright © 2010 Red Hat Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Benjamin Otte <otte@redhat.com>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <limits.h>
+
+#include "cairo-test.h"
+
+#if CAIRO_HAS_GL_SURFACE
+#include <cairo-gl.h>
+#endif
+#if CAIRO_HAS_OS2_SURFACE
+#include <cairo-os2.h>
+#endif
+#if CAIRO_HAS_PDF_SURFACE
+#include <cairo-pdf.h>
+#endif
+#if CAIRO_HAS_PS_SURFACE
+#include <cairo-ps.h>
+#endif
+#if CAIRO_HAS_XCB_SURFACE
+#include <cairo-xcb.h>
+#endif
+#if CAIRO_HAS_XLIB_SURFACE
+#include <cairo-xlib.h>
+#endif
+
+static cairo_test_status_t
+preamble (cairo_test_context_t *ctx)
+{
+    cairo_surface_t *surface;
+
+    /* get the error surface */
+    surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, INT_MAX, INT_MAX);
+
+#if CAIRO_HAS_GL_SURFACE
+    cairo_gl_surface_set_size (surface, 0, 0);
+    cairo_gl_surface_swapbuffers (surface);
+#endif
+
+#if CAIRO_HAS_OS2_SURFACE
+    cairo_os2_surface_set_hwnd (surface, 0);
+    cairo_os2_surface_set_size (surface, 0, 0, 0);
+    cairo_os2_surface_set_manual_window_refresh (surface, FALSE);
+#endif
+
+#if CAIRO_HAS_PDF_SURFACE
+    cairo_pdf_surface_restrict_to_version (surface, CAIRO_PDF_VERSION_1_4);
+    cairo_pdf_surface_set_size (surface, 0, 0);
+#endif
+
+#if CAIRO_HAS_PS_SURFACE
+    cairo_ps_surface_set_eps (surface, FALSE);
+    cairo_ps_surface_set_size (surface, 0, 0);
+    cairo_ps_surface_restrict_to_level (surface, CAIRO_PS_LEVEL_2);
+    cairo_ps_surface_dsc_comment (surface, NULL);
+    cairo_ps_surface_dsc_begin_setup (surface);
+    cairo_ps_surface_dsc_begin_page_setup (surface);
+#endif
+
+#if CAIRO_HAS_XCB_SURFACE
+    cairo_xcb_surface_set_size (surface, 0, 0);
+#endif
+
+#if CAIRO_HAS_XLIB_SURFACE
+    cairo_xlib_surface_set_size (surface, 0, 0);
+    cairo_xlib_surface_set_drawable (surface, 0, 0, 0);
+#endif
+
+    cairo_surface_set_mime_data (surface, NULL, NULL, 0, NULL, 0);
+    cairo_surface_set_device_offset (surface, 0, 0);
+    cairo_surface_set_fallback_resolution (surface, 0, 0);
+
+    cairo_surface_destroy (surface);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (error_setters,
+           "Check setters properly error out on read-only error surfaces",
+           NULL, /* keywords */
+           NULL, /* requirements */
+           0, 0,
+           preamble, NULL)
diff --git a/test/extend-pad-border.c b/test/extend-pad-border.c
new file mode 100644 (file)
index 0000000..de8105c
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * Copyright © 2007 Red Hat, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Behdad Esfahbod <behdad@behdad.org>
+ */
+
+#include <math.h>
+#include "cairo-test.h"
+#include <stdio.h>
+
+#define SIZE 90
+
+/* Check the border-pixels of an EXTEND_PAD image pattern */
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_surface_t *surface;
+    cairo_t * cr_surface;
+    int surface_size = (SIZE - 30) / 10;
+
+    cairo_set_source_rgba (cr, 0, 0, 0, 1);
+    cairo_rectangle (cr, 0, 0, SIZE, SIZE);
+    cairo_fill (cr);
+
+    /* Create an image surface with my favorite four colors in each
+     * quadrant. */
+    surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24,
+                                         surface_size, surface_size);
+    cr_surface = cairo_create (surface);
+    cairo_surface_destroy (surface);
+
+    cairo_set_source_rgb (cr_surface, 1, 1, 1);
+    cairo_rectangle (cr_surface,
+                    0, 0,
+                    surface_size / 2, surface_size / 2);
+    cairo_fill (cr_surface);
+    cairo_set_source_rgb (cr_surface, 1, 0, 0);
+    cairo_rectangle (cr_surface,
+                    surface_size / 2, 0,
+                    surface_size / 2, surface_size / 2);
+    cairo_fill (cr_surface);
+    cairo_set_source_rgb (cr_surface, 0, 1, 0);
+    cairo_rectangle (cr_surface,
+                    0, surface_size / 2,
+                    surface_size / 2, surface_size / 2);
+    cairo_fill (cr_surface);
+    cairo_set_source_rgb (cr_surface, 0, 0, 1);
+    cairo_rectangle (cr_surface,
+                    surface_size / 2, surface_size / 2,
+                    surface_size / 2, surface_size / 2);
+    cairo_fill (cr_surface);
+
+    cairo_scale (cr, 10, 10);
+    cairo_set_source_surface (cr, cairo_get_target (cr_surface), 1.5, 1.5);
+    cairo_destroy (cr_surface);
+
+    /* Using EXTEND_REFLECT makes this test pass for image and xlib backends */
+    /*cairo_pattern_set_extend (cairo_get_source (cr), CAIRO_EXTEND_REFLECT);*/
+
+    cairo_pattern_set_extend (cairo_get_source (cr), CAIRO_EXTEND_PAD);
+    cairo_rectangle (cr, 1.5, 1.5, 6, 6);
+    cairo_clip (cr);
+
+    cairo_paint (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (extend_pad_border,
+           "Test CAIRO_EXTEND_PAD for surface patterns",
+           "extend", /* keywords */
+           NULL, /* requirements */
+           SIZE, SIZE,
+           NULL, draw)
diff --git a/test/extend-pad-similar.c b/test/extend-pad-similar.c
new file mode 100644 (file)
index 0000000..357252a
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * Copyright © 2007 Red Hat, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Behdad Esfahbod <behdad@behdad.org>
+ */
+
+#include <math.h>
+#include "cairo-test.h"
+#include <stdio.h>
+
+#define SIZE 90
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_surface_t *surface;
+    cairo_t *cr_surface;
+
+    /* Create a 4-pixel similar surface with my favorite four colors in each
+     * quadrant. */
+    surface = cairo_surface_create_similar (cairo_get_group_target (cr),
+                                           CAIRO_CONTENT_COLOR, 2, 2);
+    cr_surface = cairo_create (surface);
+    cairo_surface_destroy (surface);
+
+    /* upper-left = white */
+    cairo_set_source_rgb (cr_surface, 1, 1, 1);
+    cairo_rectangle (cr_surface, 0, 0, 1, 1);
+    cairo_fill (cr_surface);
+
+    /* upper-right = red */
+    cairo_set_source_rgb (cr_surface, 1, 0, 0);
+    cairo_rectangle (cr_surface, 1, 0, 1, 1);
+    cairo_fill (cr_surface);
+
+    /* lower-left = green */
+    cairo_set_source_rgb (cr_surface, 0, 1, 0);
+    cairo_rectangle (cr_surface, 0, 1, 1, 1);
+    cairo_fill (cr_surface);
+
+    /* lower-right = blue */
+    cairo_set_source_rgb (cr_surface, 0, 0, 1);
+    cairo_rectangle (cr_surface, 1, 1, 1, 1);
+    cairo_fill (cr_surface);
+
+    /* Now use extend pad to cover the entire surface with those 4 colors */
+    cairo_set_source_surface (cr, cairo_get_target (cr_surface),
+                             width/2  - 1,
+                             height/2 - 1);
+    cairo_destroy (cr_surface);
+    cairo_pattern_set_extend (cairo_get_source (cr), CAIRO_EXTEND_PAD);
+    cairo_paint (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (extend_pad_similar,
+           "Test CAIRO_EXTEND_PAD for similar surface patterns",
+           "extend", /* keywords */
+           NULL, /* requirements */
+           SIZE, SIZE,
+           NULL, draw)
diff --git a/test/extend-pad.c b/test/extend-pad.c
new file mode 100644 (file)
index 0000000..15a2079
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * Copyright © 2007 Red Hat, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Behdad Esfahbod <behdad@behdad.org>
+ */
+
+#include <math.h>
+#include "cairo-test.h"
+#include <stdio.h>
+
+#define SIZE 90
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_surface_t *surface;
+    cairo_t *cr_surface;
+
+    /* Create a 4-pixel image surface with my favorite four colors in each
+     * quadrant. */
+    surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, 2, 2);
+    cr_surface = cairo_create (surface);
+    cairo_surface_destroy (surface);
+
+    /* upper-left = white */
+    cairo_set_source_rgb (cr_surface, 1, 1, 1);
+    cairo_rectangle (cr_surface, 0, 0, 1, 1);
+    cairo_fill (cr_surface);
+
+    /* upper-right = red */
+    cairo_set_source_rgb (cr_surface, 1, 0, 0);
+    cairo_rectangle (cr_surface, 1, 0, 1, 1);
+    cairo_fill (cr_surface);
+
+    /* lower-left = green */
+    cairo_set_source_rgb (cr_surface, 0, 1, 0);
+    cairo_rectangle (cr_surface, 0, 1, 1, 1);
+    cairo_fill (cr_surface);
+
+    /* lower-right = blue */
+    cairo_set_source_rgb (cr_surface, 0, 0, 1);
+    cairo_rectangle (cr_surface, 1, 1, 1, 1);
+    cairo_fill (cr_surface);
+
+    /* Now use extend pad to cover the entire surface with those 4 colors */
+    cairo_set_source_surface (cr, cairo_get_target (cr_surface),
+                             width/2  - 1,
+                             height/2 - 1);
+    cairo_destroy (cr_surface);
+    cairo_pattern_set_extend (cairo_get_source (cr), CAIRO_EXTEND_PAD);
+    cairo_paint (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (extend_pad,
+           "Test CAIRO_EXTEND_PAD for surface patterns",
+           "extend", /* keywords */
+           NULL, /* requirements */
+           SIZE, SIZE,
+           NULL, draw)
diff --git a/test/extend-reflect-similar.c b/test/extend-reflect-similar.c
new file mode 100644 (file)
index 0000000..fa376a7
--- /dev/null
@@ -0,0 +1,51 @@
+#include "cairo-test.h"
+
+static const char *png_filename = "romedalen.png";
+
+static cairo_surface_t *
+clone_similar_surface (cairo_surface_t * target, cairo_surface_t *surface)
+{
+    cairo_t *cr;
+    cairo_surface_t *similar;
+
+    similar = cairo_surface_create_similar (target,
+                                     cairo_surface_get_content (surface),
+                                     cairo_image_surface_get_width (surface),
+                                     cairo_image_surface_get_height (surface));
+    cr = cairo_create (similar);
+    cairo_surface_destroy (similar);
+    cairo_set_source_surface (cr, surface, 0, 0);
+    cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+    cairo_paint (cr);
+    similar = cairo_surface_reference (cairo_get_target (cr));
+    cairo_destroy (cr);
+
+    return similar;
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    const cairo_test_context_t *ctx = cairo_test_get_context (cr);
+    cairo_surface_t *surface;
+    cairo_surface_t *similar;
+
+    surface = cairo_test_create_surface_from_png (ctx, png_filename);
+    similar = clone_similar_surface (cairo_get_group_target (cr), surface);
+    cairo_set_source_surface (cr, similar, 32, 32);
+    cairo_pattern_set_extend (cairo_get_source (cr), CAIRO_EXTEND_REFLECT);
+
+    cairo_paint (cr);
+
+    cairo_surface_destroy (similar);
+    cairo_surface_destroy (surface);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (extend_reflect_similar,
+           "Test CAIRO_EXTEND_REFLECT for surface patterns",
+           "extend", /* keywords */
+           NULL, /* requirements */
+           256 + 32*2, 192 + 32*2,
+           NULL, draw)
diff --git a/test/extend-reflect.c b/test/extend-reflect.c
new file mode 100644 (file)
index 0000000..16b08c1
--- /dev/null
@@ -0,0 +1,29 @@
+#include <math.h>
+#include "cairo-test.h"
+#include <stdio.h>
+
+static const char *png_filename = "romedalen.png";
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    const cairo_test_context_t *ctx = cairo_test_get_context (cr);
+    cairo_surface_t *surface;
+
+    surface = cairo_test_create_surface_from_png (ctx, png_filename);
+    cairo_set_source_surface (cr, surface, 32, 32);
+    cairo_pattern_set_extend (cairo_get_source (cr), CAIRO_EXTEND_REFLECT);
+
+    cairo_paint (cr);
+
+    cairo_surface_destroy (surface);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (extend_reflect,
+           "Test CAIRO_EXTEND_REFLECT for surface patterns",
+           "extend", /* keywords */
+           NULL, /* requirements */
+           256 + 32*2, 192 + 32*2,
+           NULL, draw)
diff --git a/test/extend-repeat-similar.c b/test/extend-repeat-similar.c
new file mode 100644 (file)
index 0000000..08cfd29
--- /dev/null
@@ -0,0 +1,51 @@
+#include "cairo-test.h"
+
+static const char *png_filename = "romedalen.png";
+
+static cairo_surface_t *
+clone_similar_surface (cairo_surface_t * target, cairo_surface_t *surface)
+{
+    cairo_t *cr;
+    cairo_surface_t *similar;
+
+    similar = cairo_surface_create_similar (target,
+                                     cairo_surface_get_content (surface),
+                                     cairo_image_surface_get_width (surface),
+                                     cairo_image_surface_get_height (surface));
+    cr = cairo_create (similar);
+    cairo_surface_destroy (similar);
+    cairo_set_source_surface (cr, surface, 0, 0);
+    cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+    cairo_paint (cr);
+    similar = cairo_surface_reference (cairo_get_target (cr));
+    cairo_destroy (cr);
+
+    return similar;
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    const cairo_test_context_t *ctx = cairo_test_get_context (cr);
+    cairo_surface_t *surface;
+    cairo_surface_t *similar;
+
+    surface = cairo_test_create_surface_from_png (ctx, png_filename);
+    similar = clone_similar_surface (cairo_get_group_target (cr), surface);
+    cairo_set_source_surface (cr, similar, 32, 32);
+    cairo_pattern_set_extend (cairo_get_source (cr), CAIRO_EXTEND_REPEAT);
+
+    cairo_paint (cr);
+
+    cairo_surface_destroy (similar);
+    cairo_surface_destroy (surface);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (extend_repeat_similar,
+           "Test CAIRO_EXTEND_REPEAT for surface patterns",
+           "extend", /* keywords */
+           NULL, /* requirements */
+           256 + 32*2, 192 + 32*2,
+           NULL, draw)
diff --git a/test/extend-repeat.c b/test/extend-repeat.c
new file mode 100644 (file)
index 0000000..e6c0bcc
--- /dev/null
@@ -0,0 +1,27 @@
+#include "cairo-test.h"
+
+static const char *png_filename = "romedalen.png";
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    const cairo_test_context_t *ctx = cairo_test_get_context (cr);
+    cairo_surface_t *surface;
+
+    surface = cairo_test_create_surface_from_png (ctx, png_filename);
+    cairo_set_source_surface (cr, surface, 32, 32);
+    cairo_pattern_set_extend (cairo_get_source (cr), CAIRO_EXTEND_REPEAT);
+
+    cairo_paint (cr);
+
+    cairo_surface_destroy (surface);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (extend_repeat,
+           "Test CAIRO_EXTEND_REPEAT for surface patterns",
+           "extend", /* keywords */
+           NULL, /* requirements */
+           256 + 32*2, 192 + 32*2,
+           NULL, draw)
diff --git a/test/extended-blend.c b/test/extended-blend.c
new file mode 100644 (file)
index 0000000..a45ddc9
--- /dev/null
@@ -0,0 +1,256 @@
+/*
+ * Copyright © 2005 Red Hat, Inc.
+ * Copyright © 2007 Emmanuel Pacaud
+ * Copyright © 2008 Benjamin Otte
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Authors: Owen Taylor <otaylor@redhat.com>
+ *          Kristian Høgsberg <krh@redhat.com>
+ *          Emmanuel Pacaud <emmanuel.pacaud@lapp.in2p3.fr>
+ */
+
+#include <math.h>
+#include "cairo-test.h"
+#include <stdio.h>
+
+#define STEPS 16
+#define START_OPERATOR CAIRO_OPERATOR_MULTIPLY
+#define STOP_OPERATOR  CAIRO_OPERATOR_HSL_LUMINOSITY
+
+#define SIZE 5
+#define COUNT 4
+#define FULL_WIDTH  ((STEPS + 1) * COUNT - 1)
+#define FULL_HEIGHT ((COUNT + STOP_OPERATOR - START_OPERATOR) / COUNT) * (STEPS + 1)
+
+static void
+set_solid_pattern (cairo_t *cr,
+                  int step,
+                  cairo_bool_t bg,
+                  cairo_bool_t alpha)
+{
+    double c, a;
+
+    a = ((double) step) / (STEPS - 1);
+    if (alpha) {
+       c = 1;
+    } else {
+       c = a;
+       a = 1;
+    }
+
+    if (bg) /* draw a yellow background fading in using discrete steps */
+       cairo_set_source_rgba (cr, c, c, 0, a);
+    else /* draw a teal foreground pattern fading in using discrete steps */
+       cairo_set_source_rgba (cr, 0, c, c, a);
+}
+
+/* expects a STEP*STEP pixel rectangle */
+static void
+do_blend_solid (cairo_t *cr, cairo_operator_t op, cairo_bool_t alpha)
+{
+    int x;
+
+    cairo_save (cr);
+    cairo_scale (cr, SIZE, SIZE);
+
+    /* not using CAIRO_OPERATOR_SOURCE here, it triggers a librsvg bug */
+    cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
+    for (x = 0; x < STEPS; x++) {
+       /* draw the background using discrete steps */
+       set_solid_pattern (cr, x, TRUE, alpha);
+       cairo_rectangle (cr, x, 0, 1, STEPS);
+       cairo_fill (cr);
+    }
+
+    cairo_set_operator (cr, op);
+    for (x = 0; x < STEPS; x++) {
+       /* draw an orthogonal foreground pattern using discrete steps */
+       set_solid_pattern (cr, x, FALSE, alpha);
+       cairo_rectangle (cr, 0, x, STEPS, 1);
+       cairo_fill (cr);
+    }
+
+    cairo_restore (cr);
+}
+
+static void
+create_patterns (cairo_t *cr,
+                cairo_surface_t **bg,
+                cairo_surface_t **fg,
+                cairo_bool_t alpha)
+{
+    cairo_t *bgcr, *fgcr;
+
+    *bg = cairo_surface_create_similar (cairo_get_target (cr),
+                                       CAIRO_CONTENT_COLOR_ALPHA,
+                                       SIZE * STEPS,
+                                       SIZE * STEPS);
+    *fg = cairo_surface_create_similar (cairo_get_target (cr),
+                                       CAIRO_CONTENT_COLOR_ALPHA,
+                                       SIZE * STEPS,
+                                       SIZE * STEPS);
+
+    bgcr = cairo_create (*bg);
+    fgcr = cairo_create (*fg);
+
+    do_blend_solid (bgcr, CAIRO_OPERATOR_DEST, alpha);
+    do_blend_solid (fgcr, CAIRO_OPERATOR_SOURCE, alpha);
+
+    cairo_destroy (bgcr);
+    cairo_destroy (fgcr);
+}
+
+/* expects a STEP*STEP pixel rectangle */
+static void
+do_blend (cairo_t *cr, cairo_operator_t op, cairo_bool_t alpha)
+{
+    cairo_surface_t *bg, *fg;
+
+    create_patterns (cr, &bg, &fg, alpha);
+
+    /* not using CAIRO_OPERATOR_SOURCE here, it triggers a librsvg bug */
+    cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
+    cairo_set_source_surface (cr, bg, 0, 0);
+    cairo_paint (cr);
+
+    cairo_set_operator (cr, op);
+    cairo_set_source_surface (cr, fg, 0, 0);
+    cairo_paint (cr);
+
+    cairo_surface_destroy (fg);
+    cairo_surface_destroy (bg);
+}
+
+static void
+do_blend_mask (cairo_t *cr, cairo_operator_t op, cairo_bool_t alpha)
+{
+    cairo_surface_t *bg, *fg;
+
+    create_patterns (cr, &bg, &fg, alpha);
+
+    /* not using CAIRO_OPERATOR_SOURCE here, it triggers a librsvg bug */
+    cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
+    cairo_set_source_surface (cr, bg, 0, 0);
+    cairo_paint (cr);
+
+    cairo_set_operator (cr, op);
+    cairo_set_source_surface (cr, fg, 0, 0);
+    cairo_paint_with_alpha (cr, .5);
+
+    cairo_surface_destroy (fg);
+    cairo_surface_destroy (bg);
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, cairo_bool_t alpha,
+      void (*blend)(cairo_t *, cairo_operator_t, cairo_bool_t))
+{
+    size_t i = 0;
+    cairo_operator_t op;
+
+    for (op = START_OPERATOR; op <= STOP_OPERATOR; op++, i++) {
+       cairo_save (cr);
+       cairo_translate (cr,
+               SIZE * (STEPS + 1) * (i % COUNT),
+               SIZE * (STEPS + 1) * (i / COUNT));
+       blend (cr, op, alpha);
+       cairo_restore (cr);
+    }
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+static cairo_test_status_t
+draw_extended_blend (cairo_t *cr, int width, int height)
+{
+    return draw (cr, FALSE, do_blend);
+}
+
+static cairo_test_status_t
+draw_extended_blend_alpha (cairo_t *cr, int width, int height)
+{
+    return draw (cr, TRUE, do_blend);
+}
+
+static cairo_test_status_t
+draw_extended_blend_solid (cairo_t *cr, int width, int height)
+{
+    return draw (cr, FALSE, do_blend_solid);
+}
+
+static cairo_test_status_t
+draw_extended_blend_solid_alpha (cairo_t *cr, int width, int height)
+{
+    return draw (cr, TRUE, do_blend_solid);
+}
+
+static cairo_test_status_t
+draw_extended_blend_mask (cairo_t *cr, int width, int height)
+{
+    return draw (cr, FALSE, do_blend_mask);
+}
+static cairo_test_status_t
+draw_extended_blend_alpha_mask (cairo_t *cr, int width, int height)
+{
+    return draw (cr, TRUE, do_blend_mask);
+}
+
+CAIRO_TEST (extended_blend,
+           "Tests extended blend modes without alpha",
+           "operator", /* keywords */
+           NULL, /* requirements */
+           FULL_WIDTH * SIZE, FULL_HEIGHT * SIZE,
+           NULL, draw_extended_blend)
+
+CAIRO_TEST (extended_blend_alpha,
+           "Tests extended blend modes with alpha",
+           "operator", /* keywords */
+           NULL, /* requirements */
+           FULL_WIDTH * SIZE, FULL_HEIGHT * SIZE,
+           NULL, draw_extended_blend_alpha)
+
+CAIRO_TEST (extended_blend_mask,
+           "Tests extended blend modes with an alpha mask",
+           "operator,mask", /* keywords */
+           NULL, /* requirements */
+           FULL_WIDTH * SIZE, FULL_HEIGHT * SIZE,
+           NULL, draw_extended_blend_mask)
+CAIRO_TEST (extended_blend_alpha_mask,
+           "Tests extended blend modes with an alpha mask",
+           "operator,mask", /* keywords */
+           NULL, /* requirements */
+           FULL_WIDTH * SIZE, FULL_HEIGHT * SIZE,
+           NULL, draw_extended_blend_alpha_mask)
+
+
+CAIRO_TEST (extended_blend_solid,
+           "Tests extended blend modes on solid patterns without alpha",
+           "operator", /* keywords */
+           NULL, /* requirements */
+           FULL_WIDTH * SIZE, FULL_HEIGHT * SIZE,
+           NULL, draw_extended_blend_solid)
+
+CAIRO_TEST (extended_blend_solid_alpha,
+           "Tests extended blend modes on solid patterns with alpha",
+           "operator", /* keywords */
+           NULL, /* requirements */
+           FULL_WIDTH * SIZE, FULL_HEIGHT * SIZE,
+           NULL, draw_extended_blend_solid_alpha)
diff --git a/test/fallback-resolution.c b/test/fallback-resolution.c
new file mode 100644 (file)
index 0000000..5447a56
--- /dev/null
@@ -0,0 +1,513 @@
+/*
+ * Copyright © 2006 Red Hat, Inc.
+ * Copyright © 2008 Chris Wilson
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ *         Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <cairo.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#include <errno.h>
+#endif
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+
+#include "cairo-test.h"
+#include "buffer-diff.h"
+
+/* This test exists to test cairo_surface_set_fallback_resolution
+ *
+ * <behdad> one more thing.
+ *          if you can somehow incorporate cairo_show_page stuff in the
+ *          test suite.  such that fallback-resolution can actually be
+ *          automated..
+ *          if we could get a callback on surface when that function is
+ *          called, we could do cool stuff like making other backends
+ *          draw a long strip of images, one for each page...
+ */
+
+#define INCHES_TO_POINTS(in) ((in) * 72.0)
+#define SIZE INCHES_TO_POINTS(2)
+
+/* cairo_set_tolerance() is not respected by the PS/PDF backends currently */
+#define SET_TOLERANCE 0
+
+#define GENERATE_REFERENCE 0
+
+static void
+draw (cairo_t *cr, double width, double height)
+{
+    const char *text = "cairo";
+    cairo_text_extents_t extents;
+    const double dash[2] = { 8, 16 };
+    cairo_pattern_t *pattern;
+
+    cairo_save (cr);
+
+    cairo_new_path (cr);
+
+    cairo_set_line_width (cr, .05 * SIZE / 2.0);
+
+    cairo_arc (cr, SIZE / 2.0, SIZE / 2.0,
+              0.875 * SIZE / 2.0,
+              0, 2.0 * M_PI);
+    cairo_stroke (cr);
+
+    /* use dashes to demonstrate bugs:
+     *  https://bugs.freedesktop.org/show_bug.cgi?id=9189
+     *  https://bugs.freedesktop.org/show_bug.cgi?id=17223
+     */
+    cairo_save (cr);
+    cairo_set_dash (cr, dash, 2, 0);
+    cairo_arc (cr, SIZE / 2.0, SIZE / 2.0,
+              0.75 * SIZE / 2.0,
+              0, 2.0 * M_PI);
+    cairo_stroke (cr);
+    cairo_restore (cr);
+
+    cairo_save (cr);
+    cairo_rectangle (cr, 0, 0, SIZE/2, SIZE);
+    cairo_clip (cr);
+    cairo_arc (cr, SIZE / 2.0, SIZE / 2.0,
+              0.6 * SIZE / 2.0,
+              0, 2.0 * M_PI);
+    cairo_fill (cr);
+    cairo_restore (cr);
+
+    /* use a pattern to exercise bug:
+     *   https://bugs.launchpad.net/inkscape/+bug/234546
+     */
+    cairo_save (cr);
+    cairo_rectangle (cr, SIZE/2, 0, SIZE/2, SIZE);
+    cairo_clip (cr);
+    pattern = cairo_pattern_create_linear (SIZE/2, 0, SIZE, 0);
+    cairo_pattern_add_color_stop_rgba (pattern, 0, 0, 0, 0, 1.);
+    cairo_pattern_add_color_stop_rgba (pattern, 1, 0, 0, 0, 0.);
+    cairo_set_source (cr, pattern);
+    cairo_pattern_destroy (pattern);
+    cairo_arc (cr, SIZE / 2.0, SIZE / 2.0,
+              0.6 * SIZE / 2.0,
+              0, 2.0 * M_PI);
+    cairo_fill (cr);
+    cairo_restore (cr);
+
+    cairo_set_source_rgb (cr, 1, 1, 1); /* white */
+    cairo_set_font_size (cr, .25 * SIZE / 2.0);
+    cairo_text_extents (cr, text, &extents);
+    cairo_move_to (cr, (SIZE-extents.width)/2.0-extents.x_bearing,
+                      (SIZE-extents.height)/2.0-extents.y_bearing);
+    cairo_show_text (cr, text);
+
+    cairo_restore (cr);
+}
+
+static void
+_xunlink (const cairo_test_context_t *ctx, const char *pathname)
+{
+    if (unlink (pathname) < 0 && errno != ENOENT) {
+       cairo_test_log (ctx, "Error: Cannot remove %s: %s\n",
+                       pathname, strerror (errno));
+       exit (1);
+    }
+}
+
+static cairo_bool_t
+check_result (cairo_test_context_t *ctx,
+             const cairo_boilerplate_target_t *target,
+             const char *test_name,
+             const char *base_name,
+             cairo_surface_t *surface)
+{
+    const char *format;
+    char *ref_name;
+    char *png_name;
+    char *diff_name;
+    cairo_surface_t *test_image, *ref_image, *diff_image;
+    buffer_diff_result_t result;
+    cairo_status_t status;
+    cairo_bool_t ret;
+
+    /* XXX log target, OUTPUT, REFERENCE, DIFFERENCE for index.html */
+
+    if (target->finish_surface != NULL) {
+       status = target->finish_surface (surface);
+       if (status) {
+           cairo_test_log (ctx, "Error: Failed to finish surface: %s\n",
+                   cairo_status_to_string (status));
+           cairo_surface_destroy (surface);
+           return FALSE;
+       }
+    }
+
+    xasprintf (&png_name,  "%s.out.png", base_name);
+    xasprintf (&diff_name, "%s.diff.png", base_name);
+
+    test_image = target->get_image_surface (surface, 0, SIZE, SIZE);
+    if (cairo_surface_status (test_image)) {
+       cairo_test_log (ctx, "Error: Failed to extract page: %s\n",
+                       cairo_status_to_string (cairo_surface_status (test_image)));
+       cairo_surface_destroy (test_image);
+       free (png_name);
+       free (diff_name);
+       return FALSE;
+    }
+
+    _xunlink (ctx, png_name);
+    status = cairo_surface_write_to_png (test_image, png_name);
+    if (status) {
+       cairo_test_log (ctx, "Error: Failed to write output image: %s\n",
+               cairo_status_to_string (status));
+       cairo_surface_destroy (test_image);
+       free (png_name);
+       free (diff_name);
+       return FALSE;
+    }
+
+    format = cairo_boilerplate_content_name (target->content);
+    ref_name = cairo_test_reference_filename (ctx,
+                                             base_name,
+                                             test_name,
+                                             target->name,
+                                             target->basename,
+                                             format,
+                                             CAIRO_TEST_REF_SUFFIX,
+                                             CAIRO_TEST_PNG_EXTENSION);
+    if (ref_name == NULL) {
+       cairo_test_log (ctx, "Error: Cannot find reference image for %s\n",
+                       base_name);
+       cairo_surface_destroy (test_image);
+       free (png_name);
+       free (diff_name);
+       return FALSE;
+    }
+
+
+    ref_image = cairo_test_get_reference_image (ctx, ref_name,
+           target->content == CAIRO_TEST_CONTENT_COLOR_ALPHA_FLATTENED);
+    if (cairo_surface_status (ref_image)) {
+       cairo_test_log (ctx, "Error: Cannot open reference image for %s: %s\n",
+                       ref_name,
+               cairo_status_to_string (cairo_surface_status (ref_image)));
+       cairo_surface_destroy (ref_image);
+       cairo_surface_destroy (test_image);
+       free (png_name);
+       free (diff_name);
+       free (ref_name);
+       return FALSE;
+    }
+
+    diff_image = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
+           SIZE, SIZE);
+
+    ret = TRUE;
+    status = image_diff (ctx,
+           test_image, ref_image, diff_image,
+           &result);
+    _xunlink (ctx, diff_name);
+    if (status) {
+       cairo_test_log (ctx, "Error: Failed to compare images: %s\n",
+                       cairo_status_to_string (status));
+       ret = FALSE;
+    } else if (image_diff_is_failure (&result, target->error_tolerance))
+    {
+       ret = FALSE;
+
+       status = cairo_surface_write_to_png (diff_image, diff_name);
+       if (status) {
+           cairo_test_log (ctx, "Error: Failed to write differences image: %s\n",
+                   cairo_status_to_string (status));
+       }
+    }
+
+    cairo_surface_destroy (test_image);
+    cairo_surface_destroy (diff_image);
+    free (png_name);
+    free (diff_name);
+    free (ref_name);
+
+    return ret;
+}
+
+#if GENERATE_REFERENCE
+static void
+generate_reference (double ppi_x, double ppi_y, const char *filename)
+{
+    cairo_surface_t *surface, *target;
+    cairo_t *cr;
+    cairo_status_t status;
+
+    surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24,
+                                         SIZE*ppi_x/72, SIZE*ppi_y/72);
+    cr = cairo_create (surface);
+    cairo_surface_destroy (surface);
+
+#if SET_TOLERANCE
+    cairo_set_tolerance (cr, 3.0);
+#endif
+
+    cairo_save (cr); {
+       cairo_set_source_rgb (cr, 1, 1, 1);
+       cairo_paint (cr);
+    } cairo_restore (cr);
+
+    cairo_scale (cr, ppi_x/72., ppi_y/72.);
+    draw (cr, SIZE, SIZE);
+
+    surface = cairo_surface_reference (cairo_get_target (cr));
+    cairo_destroy (cr);
+
+    target = cairo_image_surface_create (CAIRO_FORMAT_RGB24, SIZE, SIZE);
+    cr = cairo_create (target);
+    cairo_scale (cr, 72./ppi_x, 72./ppi_y);
+    cairo_set_source_surface (cr, surface, 0, 0);
+    cairo_paint (cr);
+
+    status = cairo_surface_write_to_png (cairo_get_target (cr), filename);
+    cairo_destroy (cr);
+
+    if (status) {
+       fprintf (stderr, "Failed to generate reference image '%s': %s\n",
+                filename, cairo_status_to_string (status));
+       exit (1);
+    }
+}
+#endif
+
+static cairo_bool_t
+_cairo_test_mkdir (const char *path)
+{
+#if ! HAVE_MKDIR
+    return FALSE;
+#elif HAVE_MKDIR == 1
+    if (mkdir (path) == 0)
+       return TRUE;
+#elif HAVE_MKDIR == 2
+    if (mkdir (path, 0770) == 0)
+       return TRUE;
+#else
+#error Bad value for HAVE_MKDIR
+#endif
+
+    return errno == EEXIST;
+}
+
+static cairo_test_status_t
+preamble (cairo_test_context_t *ctx)
+{
+    cairo_t *cr;
+    cairo_test_status_t ret = CAIRO_TEST_UNTESTED;
+    struct {
+       double x, y;
+    } ppi[] = {
+       { 576, 576 },
+       { 576, 72 },
+
+       { 288, 288 },
+       { 288, 72 },
+
+       { 144, 144 },
+       { 144, 72 },
+
+       { 72, 576 },
+       { 72, 288 },
+       { 72, 144 },
+       { 72, 72 },
+    };
+    unsigned int i;
+    int n, num_ppi;
+    const char *path = _cairo_test_mkdir (CAIRO_TEST_OUTPUT_DIR) ? CAIRO_TEST_OUTPUT_DIR : ".";
+
+    num_ppi = ARRAY_LENGTH (ppi);
+
+#if GENERATE_REFERENCE
+    for (n = 0; n < num_ppi; n++) {
+       char *ref_name;
+       xasprintf (&ref_name, "fallback-resolution.ppi%gx%g.ref.png",
+                  ppi[n].x, ppi[n].y);
+       generate_reference (ppi[n].x, ppi[n].y, ref_name);
+       free (ref_name);
+    }
+#endif
+
+    for (i = 0; i < ctx->num_targets; i++) {
+       const cairo_boilerplate_target_t *target = ctx->targets_to_test[i];
+       cairo_surface_t *surface = NULL;
+       char *base_name;
+       void *closure;
+       const char *format;
+       cairo_status_t status;
+
+       if (! target->is_vector)
+           continue;
+
+       if (! cairo_test_is_target_enabled (ctx, target->name))
+           continue;
+
+       format = cairo_boilerplate_content_name (target->content);
+       xasprintf (&base_name, "%s/fallback-resolution.%s.%s",
+                  path, target->name,
+                  format);
+
+       surface = (target->create_surface) (base_name,
+                                           target->content,
+                                           SIZE, SIZE,
+                                           SIZE, SIZE,
+                                           CAIRO_BOILERPLATE_MODE_TEST,
+                                           0,
+                                           &closure);
+
+       if (surface == NULL) {
+           free (base_name);
+           continue;
+       }
+
+       if (ret == CAIRO_TEST_UNTESTED)
+           ret = CAIRO_TEST_SUCCESS;
+
+       cairo_surface_destroy (surface);
+       if (target->cleanup)
+           target->cleanup (closure);
+       free (base_name);
+
+       /* we need to recreate the surface for each resolution as we include
+        * SVG in testing which does not support the paginated interface.
+        */
+       for (n = 0; n < num_ppi; n++) {
+           char *test_name;
+           cairo_bool_t pass;
+
+           xasprintf (&test_name, "fallback-resolution.ppi%gx%g",
+                      ppi[n].x, ppi[n].y);
+           xasprintf (&base_name, "%s/%s.%s.%s",
+                      path, test_name,
+                      target->name,
+                      format);
+
+           surface = (target->create_surface) (base_name,
+                                               target->content,
+                                               SIZE + 25, SIZE + 25,
+                                               SIZE + 25, SIZE + 25,
+                                               CAIRO_BOILERPLATE_MODE_TEST,
+                                               0,
+                                               &closure);
+           if (surface == NULL || cairo_surface_status (surface)) {
+               cairo_test_log (ctx, "Failed to generate surface: %s.%s\n",
+                               target->name,
+                               format);
+               free (base_name);
+               free (test_name);
+               ret = CAIRO_TEST_FAILURE;
+               continue;
+           }
+
+           cairo_test_log (ctx,
+                           "Testing fallback-resolution %gx%g with %s target\n",
+                           ppi[n].x, ppi[n].y, target->name);
+           printf ("%s:\t", base_name);
+           fflush (stdout);
+
+           if (target->force_fallbacks != NULL)
+               target->force_fallbacks (surface, ppi[n].x, ppi[n].y);
+           cr = cairo_create (surface);
+#if SET_TOLERANCE
+           cairo_set_tolerance (cr, 3.0);
+#endif
+
+           cairo_surface_set_device_offset (surface, 25, 25);
+
+           cairo_save (cr); {
+               cairo_set_source_rgb (cr, 1, 1, 1);
+               cairo_paint (cr);
+           } cairo_restore (cr);
+
+           /* First draw the top half in a conventional way. */
+           cairo_save (cr); {
+               cairo_rectangle (cr, 0, 0, SIZE, SIZE / 2.0);
+               cairo_clip (cr);
+
+               draw (cr, SIZE, SIZE);
+           } cairo_restore (cr);
+
+           /* Then draw the bottom half in a separate group,
+            * (exposing a bug in 1.6.4 with the group not being
+            * rendered with the correct fallback resolution). */
+           cairo_save (cr); {
+               cairo_rectangle (cr, 0, SIZE / 2.0, SIZE, SIZE / 2.0);
+               cairo_clip (cr);
+
+               cairo_push_group (cr); {
+                   draw (cr, SIZE, SIZE);
+               } cairo_pop_group_to_source (cr);
+
+               cairo_paint (cr);
+           } cairo_restore (cr);
+
+           status = cairo_status (cr);
+           cairo_destroy (cr);
+
+           pass = FALSE;
+           if (status) {
+               cairo_test_log (ctx, "Error: Failed to create target surface: %s\n",
+                               cairo_status_to_string (status));
+               ret = CAIRO_TEST_FAILURE;
+           } else {
+               /* extract the image and compare it to our reference */
+               if (! check_result (ctx, target, test_name, base_name, surface))
+                   ret = CAIRO_TEST_FAILURE;
+               else
+                   pass = TRUE;
+           }
+           cairo_surface_destroy (surface);
+           if (target->cleanup)
+               target->cleanup (closure);
+
+           free (base_name);
+           free (test_name);
+
+           if (pass) {
+               printf ("PASS\n");
+           } else {
+               printf ("FAIL\n");
+           }
+           fflush (stdout);
+       }
+    }
+
+    return ret;
+}
+
+CAIRO_TEST (fallback_resolution,
+           "Check handling of fallback resolutions",
+           "fallback", /* keywords */
+           NULL, /* requirements */
+           0, 0,
+           preamble, NULL)
diff --git a/test/fill-alpha-pattern.c b/test/fill-alpha-pattern.c
new file mode 100644 (file)
index 0000000..3698779
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * Copyright © 2008 Chris Wilson
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Chris Wilson not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Chris Wilson makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * CHRIS WILSON DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL CHRIS WILSON BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+
+#define SIZE 60 /* needs to be big to check large area effects (dithering) */
+#define PAD 2
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    const double alpha = 1./3;
+    cairo_pattern_t *pattern;
+    int n;
+
+    /* draw a simple pattern behind */
+    pattern = cairo_pattern_create_linear (0, 0, width, height);
+    cairo_pattern_add_color_stop_rgb (pattern, 0, 1, 1, 0);
+    cairo_pattern_add_color_stop_rgb (pattern, 1, 1, 1, 1);
+    cairo_set_source (cr, pattern);
+    cairo_pattern_destroy (pattern);
+    cairo_paint (cr);
+
+    /* square */
+    cairo_rectangle (cr, PAD, PAD, SIZE, SIZE);
+    cairo_set_source_rgba (cr, 1, 0, 0, alpha);
+    cairo_fill (cr);
+
+    /* circle */
+    cairo_translate (cr, SIZE + 2 * PAD, 0);
+    cairo_arc (cr, PAD + SIZE / 2., PAD + SIZE / 2., SIZE / 2., 0, 2 * M_PI);
+    cairo_set_source_rgba (cr, 0, 1, 0, alpha);
+    cairo_fill (cr);
+
+    /* triangle */
+    cairo_translate (cr, 0, SIZE + 2 * PAD);
+    cairo_move_to (cr, PAD + SIZE / 2, PAD);
+    cairo_line_to (cr, PAD + SIZE, PAD + SIZE);
+    cairo_line_to (cr, PAD, PAD + SIZE);
+    cairo_set_source_rgba (cr, 0, 0, 1, alpha);
+    cairo_fill (cr);
+
+    /* star */
+    cairo_translate (cr, -(SIZE + 2 * PAD) + SIZE/2., SIZE/2.);
+    for (n = 0; n < 5; n++) {
+       cairo_line_to (cr,
+                      SIZE/2 * cos (2*n * 2*M_PI / 10),
+                      SIZE/2 * sin (2*n * 2*M_PI / 10));
+
+       cairo_line_to (cr,
+                      SIZE/4 * cos ((2*n+1)*2*M_PI / 10),
+                      SIZE/4 * sin ((2*n+1)*2*M_PI / 10));
+    }
+    cairo_set_source_rgba (cr, 0, 0, 0, alpha);
+    cairo_fill (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (fill_alpha_pattern,
+           "Tests using set_rgba();fill() over a linear gradient",
+           "fill, alpha", /* keywords */
+           NULL, /* requirements */
+           2*SIZE + 4*PAD, 2*SIZE + 4*PAD,
+           NULL, draw)
diff --git a/test/fill-alpha.c b/test/fill-alpha.c
new file mode 100644 (file)
index 0000000..6c2948d
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * Copyright © 2008 Chris Wilson
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Chris Wilson not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Chris Wilson makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * CHRIS WILSON DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL CHRIS WILSON BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+
+#define SIZE 60 /* needs to be big to check large area effects (dithering) */
+#define PAD 2
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    const double alpha = 1./3;
+    int n;
+
+    /* flatten to white */
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_paint (cr);
+
+    /* square */
+    cairo_rectangle (cr, PAD, PAD, SIZE, SIZE);
+    cairo_set_source_rgba (cr, 1, 0, 0, alpha);
+    cairo_fill (cr);
+
+    /* circle */
+    cairo_translate (cr, SIZE + 2 * PAD, 0);
+    cairo_arc (cr, PAD + SIZE / 2., PAD + SIZE / 2., SIZE / 2., 0, 2 * M_PI);
+    cairo_set_source_rgba (cr, 0, 1, 0, alpha);
+    cairo_fill (cr);
+
+    /* triangle */
+    cairo_translate (cr, 0, SIZE + 2 * PAD);
+    cairo_move_to (cr, PAD + SIZE / 2, PAD);
+    cairo_line_to (cr, PAD + SIZE, PAD + SIZE);
+    cairo_line_to (cr, PAD, PAD + SIZE);
+    cairo_set_source_rgba (cr, 0, 0, 1, alpha);
+    cairo_fill (cr);
+
+    /* star */
+    cairo_translate (cr, -(SIZE + 2 * PAD) + SIZE/2., SIZE/2.);
+    for (n = 0; n < 5; n++) {
+       cairo_line_to (cr,
+                      SIZE/2 * cos (2*n * 2*M_PI / 10),
+                      SIZE/2 * sin (2*n * 2*M_PI / 10));
+
+       cairo_line_to (cr,
+                      SIZE/4 * cos ((2*n+1)*2*M_PI / 10),
+                      SIZE/4 * sin ((2*n+1)*2*M_PI / 10));
+    }
+    cairo_set_source_rgba (cr, 0, 0, 0, alpha);
+    cairo_fill (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (fill_alpha,
+           "Tests using set_rgba();fill()",
+           "fill, alpha", /* keywords */
+           NULL, /* requirements */
+           2*SIZE + 4*PAD, 2*SIZE + 4*PAD,
+           NULL, draw)
diff --git a/test/fill-and-stroke-alpha-add.c b/test/fill-and-stroke-alpha-add.c
new file mode 100644 (file)
index 0000000..55cd4c6
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ * Copyright © 2005 Red Hat, Inc.
+ * Copyright © 2006 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ */
+
+#include "cairo-test.h"
+
+#define PAD 2
+#define SIZE 10
+
+typedef void (*path_func_t) (cairo_t *cr);
+
+static void
+rectangle (cairo_t *cr)
+{
+    cairo_rectangle (cr, PAD, PAD, SIZE, SIZE);
+}
+
+static void
+circle (cairo_t *cr)
+{
+    cairo_arc (cr,
+              PAD + SIZE / 2, PAD + SIZE / 2,
+              SIZE / 2,
+              0, 2 * M_PI);
+}
+
+/* Given a path-generating function and two possibly translucent
+ * patterns, fill and stroke the path with the patterns (to an
+ * offscreen group), then blend the result into the destination.
+ */
+static void
+fill_and_stroke (cairo_t               *cr,
+                path_func_t             path_func,
+                cairo_pattern_t        *fill_pattern,
+                cairo_pattern_t        *stroke_pattern)
+{
+    cairo_push_group (cr);
+    {
+       (path_func) (cr);
+       cairo_set_source (cr, fill_pattern);
+       cairo_fill_preserve (cr);
+
+       /* Use DEST_OUT to subtract stroke from fill. */
+       cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
+       cairo_set_operator (cr, CAIRO_OPERATOR_DEST_OUT);
+       cairo_stroke_preserve (cr);
+
+       /* Then use ADD to draw the stroke without a seam. */
+       cairo_set_source (cr, stroke_pattern);
+       cairo_set_operator (cr, CAIRO_OPERATOR_ADD);
+       cairo_stroke (cr);
+    }
+    cairo_pop_group_to_source (cr);
+    cairo_paint (cr);
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_pattern_t *blue;
+    cairo_pattern_t *red;
+
+    blue = cairo_pattern_create_rgba (0.0, 0.0, 1.0, 0.8);
+    red = cairo_pattern_create_rgba (1.0, 0.0, 0.0, 0.2);
+
+    cairo_test_paint_checkered (cr);
+
+    fill_and_stroke (cr, rectangle, blue, red);
+
+    cairo_translate (cr, SIZE + 2 * PAD, 0);
+
+    fill_and_stroke (cr, circle, red, blue);
+
+    cairo_pattern_destroy (blue);
+    cairo_pattern_destroy (red);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (fill_and_stroke_alpha_add,
+           "Use a group to fill/stroke a path (each with different alpha) using DEST_OUT and ADD to combine",
+           "fill-and-stroke, fill, stroke", /* keywords */
+           NULL, /* requirements */
+           2 * SIZE + 4 * PAD, SIZE + 2 * PAD,
+           NULL, draw)
diff --git a/test/fill-and-stroke-alpha.c b/test/fill-and-stroke-alpha.c
new file mode 100644 (file)
index 0000000..c6d7c00
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * Copyright © 2005 Red Hat, Inc.
+ * Copyright © 2006 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ */
+
+#include "cairo-test.h"
+
+#define PAD 2
+#define SIZE 10
+
+typedef void (*path_func_t) (cairo_t *cr);
+
+static void
+rectangle (cairo_t *cr)
+{
+    cairo_rectangle (cr, PAD, PAD, SIZE, SIZE);
+}
+
+static void
+circle (cairo_t *cr)
+{
+    cairo_arc (cr,
+              PAD + SIZE / 2, PAD + SIZE / 2,
+              SIZE / 2,
+              0, 2 * M_PI);
+}
+
+/* Given a path-generating function and two opaque patterns, fill and
+ * stroke the path with the patterns (to an offscreen group), then
+ * blend the result into the destination with the given alpha
+ * value.
+ */
+static void
+fill_and_stroke_alpha (cairo_t         *cr,
+                      path_func_t       path_func,
+                      cairo_pattern_t  *fill_pattern,
+                      cairo_pattern_t  *stroke_pattern,
+                      double            alpha)
+{
+    cairo_push_group (cr);
+    {
+       (path_func) (cr);
+       cairo_set_source (cr, fill_pattern);
+       cairo_fill_preserve (cr);
+       cairo_set_source (cr, stroke_pattern);
+       cairo_stroke (cr);
+    }
+    cairo_pop_group_to_source (cr);
+    cairo_paint_with_alpha (cr, alpha);
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_pattern_t *blue;
+    cairo_pattern_t *red;
+
+    blue = cairo_pattern_create_rgb (0.0, 0.0, 1.0);
+    red = cairo_pattern_create_rgb (1.0, 0.0, 0.0);
+
+    cairo_test_paint_checkered (cr);
+
+    fill_and_stroke_alpha (cr, rectangle, blue, red, 0.5);
+
+    cairo_translate (cr, SIZE + 2 * PAD, 0);
+
+    fill_and_stroke_alpha (cr, circle, red, blue, 0.5);
+
+    cairo_pattern_destroy (blue);
+    cairo_pattern_destroy (red);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (fill_and_stroke_alpha,
+           "Use a group to fill/stroke a path then blend the result with alpha onto the destination",
+           "fill-and-stroke, fill, stroke", /* keywords */
+           NULL, /* requirements */
+           2 * SIZE + 4 * PAD, SIZE + 2 * PAD,
+           NULL, draw)
diff --git a/test/fill-and-stroke.c b/test/fill-and-stroke.c
new file mode 100644 (file)
index 0000000..c983c32
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ */
+
+#include "cairo-test.h"
+
+#define PAD 2
+#define SIZE 10
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_rectangle (cr, PAD, PAD, SIZE, SIZE);
+    cairo_set_source_rgb (cr, 0, 0, 1);
+    cairo_fill_preserve (cr);
+    cairo_set_source_rgb (cr, 1, 0, 0);
+    cairo_stroke (cr);
+
+    cairo_translate (cr, SIZE + 2 * PAD, 0);
+
+    cairo_arc (cr,
+              PAD + SIZE / 2, PAD + SIZE / 2,
+              SIZE / 2,
+              0, 2 * M_PI);
+    cairo_fill_preserve (cr);
+    cairo_set_source_rgb (cr, 0, 0, 1);
+    cairo_stroke (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (fill_and_stroke,
+           "Tests using cairo_fill_preserve/cairo_stroke to fill/stroke the same path",
+           "fill-and-stroke, fill, stroke", /* keywords */
+           NULL, /* requirements */
+           2 * SIZE + 4 * PAD, SIZE + 2 * PAD,
+           NULL, draw)
diff --git a/test/fill-degenerate-sort-order.c b/test/fill-degenerate-sort-order.c
new file mode 100644 (file)
index 0000000..d934810
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * Copyright © 2006 M Joonas Pihlaja
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: M Joonas Pihlaja <jpihlaja@cc.helsinki.fi>
+ */
+
+/* Bug history
+ *
+ * 2006-12-05  M Joonas Pihlaja <jpihlaja@cc.helsinki.fi>
+ *
+ *   There's currently a regression bug in the tessellation code from
+ *   switching to the "new tessellator".  The bug is caused by
+ *   confusion in the comparator used to order events when there are
+ *   degenerate edges.
+ */
+
+#include "cairo-test.h"
+
+/* Derived from zrusin's "another" polygon in the performance suite. */
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_set_source_rgb (cr, 1, 0, 0);
+
+    /* The polygon uses (43,103) as its "base point".  Closed
+     * subpaths are simulated by going from the base point to the
+     * subpath's first point, doing the subpath, and returning to the
+     * base point.  The moving to and from the base point causes
+     * degenerate edges which shouldn't result in anything visible. */
+    cairo_move_to (cr, 43, 103);
+
+    /* First subpath. */
+    cairo_line_to (cr, 91, 101);
+    cairo_line_to (cr, 0, 112);
+    cairo_line_to (cr, 60, 0);
+    cairo_line_to (cr, 91, 101);
+
+    cairo_line_to (cr, 43, 103);
+
+    /* Second subpath. */
+    cairo_line_to (cr, 176, 110);
+    cairo_line_to (cr, 116, 100);
+    cairo_line_to (cr, 176, 0);
+    cairo_line_to (cr, 176, 110);
+
+    cairo_close_path (cr);
+    cairo_fill (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (fill_degenerate_sort_order,
+           "Tests the tessellator's event comparator with degenerate input",
+           "degenerate, fill", /* keywords */
+           NULL, /* requirements */
+           190, 120,
+           NULL, draw)
diff --git a/test/fill-empty.c b/test/fill-empty.c
new file mode 100644 (file)
index 0000000..0594e57
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright © 2009 Chris Wilson
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Chris Wilson not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Chris Wilson makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * CHRIS WILSON DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL CHRIS WILSON BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+
+#define SIZE 10
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_set_source_rgb (cr, 0, 0, 1);
+    cairo_paint (cr);
+
+    cairo_set_source_rgb (cr, 1, 0, 0);
+
+    /* first drawn an ordinary empty path */
+    cairo_save (cr);
+    cairo_rectangle (cr, 0, 0, SIZE, SIZE/2);
+    cairo_clip (cr);
+    cairo_fill (cr);
+    cairo_restore (cr);
+
+    /* and then an unbounded empty path */
+    cairo_save (cr);
+    cairo_rectangle (cr, 0, SIZE/2, SIZE, SIZE/2);
+    cairo_clip (cr);
+    cairo_set_operator (cr, CAIRO_OPERATOR_DEST_IN);
+    cairo_fill (cr);
+    cairo_restore (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (fill_empty,
+           "Test filling with an empty path",
+           "fill", /* keywords */
+           NULL, /* requirements */
+           SIZE, SIZE,
+           NULL, draw)
+
diff --git a/test/fill-image.c b/test/fill-image.c
new file mode 100644 (file)
index 0000000..24ee031
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * Copyright © 2006 Mozilla Corporation
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Mozilla Corporation not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Mozilla Corporation makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * MOZILLA CORPORATION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL MOZILLA CORPORATION BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Vladimir Vukicevic <vladimir@pobox.com>
+ */
+
+#include "cairo-test.h"
+
+#define PAD 10
+#define SIZE 100
+#define IMAGE_SIZE (SIZE-PAD*2)
+#define LINE_WIDTH 10
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_surface_t *image;
+    cairo_t *cr_image;
+
+    cairo_set_source_rgb (cr, 0, 0, 0);
+    cairo_paint (cr);
+
+    image = cairo_image_surface_create (CAIRO_FORMAT_RGB24,
+                                       IMAGE_SIZE, IMAGE_SIZE);
+    cr_image = cairo_create (image);
+    cairo_surface_destroy (image);
+
+    /* Create the image */
+    cairo_set_source_rgb (cr_image, 0, 0, 0);
+    cairo_paint (cr_image);
+
+    cairo_set_source_rgb (cr_image, 0, 1, 0);
+    cairo_new_sub_path (cr_image);
+    cairo_arc (cr_image, IMAGE_SIZE/2, IMAGE_SIZE/2, IMAGE_SIZE/2 - LINE_WIDTH, 0, M_PI * 2.0);
+    cairo_close_path (cr_image);
+    cairo_new_sub_path (cr_image);
+    cairo_arc_negative (cr_image, IMAGE_SIZE/2, IMAGE_SIZE/2, IMAGE_SIZE/2, 0, -M_PI * 2.0);
+    cairo_close_path (cr_image);
+    cairo_fill (cr_image);
+
+    /* Now stroke^Wfill with it */
+    cairo_translate (cr, PAD, PAD);
+
+    cairo_set_source_surface (cr, cairo_get_target (cr_image), 0, 0);
+    cairo_destroy (cr_image);
+
+    cairo_new_sub_path (cr);
+    cairo_arc (cr, IMAGE_SIZE/2, IMAGE_SIZE/2, IMAGE_SIZE/2 - LINE_WIDTH, 0, M_PI * 2.0);
+    cairo_close_path (cr);
+    cairo_new_sub_path (cr);
+    cairo_arc_negative (cr, IMAGE_SIZE/2, IMAGE_SIZE/2, IMAGE_SIZE/2, 0, -M_PI * 2.0);
+    cairo_close_path (cr);
+
+    cairo_fill (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (fill_image,
+           "Test filling with an image source, with a non-identity CTM",
+           "fill, image, transform", /* keywords */
+           NULL, /* requirements */
+           SIZE, SIZE,
+           NULL, draw)
diff --git a/test/fill-missed-stop.c b/test/fill-missed-stop.c
new file mode 100644 (file)
index 0000000..5589337
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * Copyright © 2006 M Joonas Pihlaja
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: M Joonas Pihlaja <jpihlaja@cc.helsinki.fi>
+ */
+
+/* Bug history
+ *
+ * 2006-12-05  M Joonas Pihlaja <jpihlaja@cc.helsinki.fi>
+ *
+ *  The tessellator has a regression where a trapezoid may continue
+ *  below the end of a polygon edge (i.e. the bottom of the trapezoid
+ *  is miscomputed.)  This can only happen if the right edge of a
+ *  trapezoid stops earlier than the left edge and there is no start
+ *  event at the end point of the right edge.
+ */
+
+#include "cairo-test.h"
+
+#define SIZE 50
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_set_source_rgb (cr, 1, 0, 0);
+
+    cairo_translate (cr, 1, 1);
+
+    /* What it should look like, with # marking the filled areas:
+     *
+     * |\    |\
+     * |#\   |#\
+     * |##\__|##\
+     *     \#|
+     *      \|
+     *
+     * What it looke like with the bug, when the rightmost edge's end
+     * is missed:
+     *
+     * |\    |\
+     * |#\   |#\
+     * |##\__|##\
+     *     \#####|
+     *      \####|
+     */
+
+    cairo_move_to (cr, 0, 0);
+    cairo_line_to (cr, SIZE/2, SIZE);
+    cairo_line_to (cr, SIZE/2, 0);
+    cairo_line_to (cr, SIZE, SIZE/2);
+    cairo_line_to (cr, 0, SIZE/2);
+    cairo_close_path (cr);
+    cairo_fill (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (fill_missed_stop,
+           "Tests that the tessellator doesn't miss stop events when generating trapezoids",
+           "fill", /* keywords */
+           NULL, /* requirements */
+           SIZE+3, SIZE+3,
+           NULL, draw)
diff --git a/test/fill-rule.c b/test/fill-rule.c
new file mode 100644 (file)
index 0000000..66f19b7
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ * Copyright © 2004 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ */
+
+/* Bug history
+ *
+ * 2004-10-27  Carl Worth  <cworth@cworth.org>
+ *
+ *   There's currently a regression bug in the tessellation code. This
+ *   causes each of these simple star shapes to be filled incorrectly.
+ *
+ *   It looks like right now we can get this test to pass by doing:
+ *
+ *       cvs update -r 1.16 src/cairo_traps.c
+ *
+ *   But we don't want to revert that change permanently since it
+ *   really does correct some bugs. It must be that the old version of
+ *   the code is masking some other bugs in the tessellation code. My
+ *   current plan is to back this revision up for the next snapshot,
+ *   but not to list the test as an expected failure since I'm
+ *   planning on doing the new tessellator which should fix this
+ *   problem.
+ *
+ * 2005-01-11 Carl Worth <cworth@cworth.org>
+ *
+ *   Keith committed some fixes that fix the original size-20
+ *   star_path:
+ *
+ *     * src/cairo_wideint.c: (_cairo_int32x32_64_mul),
+ *     (_cairo_int64x64_128_mul):
+ *     * src/cairo_wideint.h:
+ *     int32x32_64_mul and int64x64_128_mul are different from their
+ *     unsigned compatriots
+ *
+ * 2005-01-12 Carl Worth <cworth@cworth.org>
+ *
+ *   Going back to the SVG test suite, however, the original star
+ *   shape is still broken. Adding both shapes now as little_star_path
+ *   and big_star_path.
+ *
+ */
+
+#include "cairo-test.h"
+
+#define LITTLE_STAR_SIZE 20
+#define BIG_STAR_SIZE    80
+
+/* The SVG start trimmed down, but still showing the bug (originally) */
+static void
+little_star_path (cairo_t *cr)
+{
+    cairo_move_to (cr, 10, 0);
+    cairo_rel_line_to (cr, 6, 20);
+    cairo_rel_line_to (cr, -16, -12);
+    cairo_rel_line_to (cr, 20, 0);
+    cairo_rel_line_to (cr, -16, 12);
+}
+
+/* The star shape from the SVG test suite. This was is still buggy even after
+   we got little_star_path working. */
+static void
+big_star_path (cairo_t *cr)
+{
+    cairo_move_to (cr, 40, 0);
+    cairo_rel_line_to (cr, 25, 80);
+    cairo_rel_line_to (cr, -65, -50);
+    cairo_rel_line_to (cr, 80, 0);
+    cairo_rel_line_to (cr, -65, 50);
+    cairo_close_path (cr);
+}
+
+/* Fill the same path twice, once with each fill rule */
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_set_source_rgb (cr, 1, 0, 0);
+
+    cairo_translate (cr, 1, 1);
+    little_star_path (cr);
+    cairo_set_fill_rule (cr, CAIRO_FILL_RULE_WINDING);
+    cairo_fill (cr);
+
+    cairo_translate (cr, LITTLE_STAR_SIZE + 1, 0);
+    little_star_path (cr);
+    cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD);
+    cairo_fill (cr);
+
+    cairo_translate (cr, -(LITTLE_STAR_SIZE + 1), LITTLE_STAR_SIZE + 1);
+    big_star_path (cr);
+    cairo_set_fill_rule (cr, CAIRO_FILL_RULE_WINDING);
+    cairo_fill (cr);
+
+    cairo_translate (cr, BIG_STAR_SIZE + 1, 0);
+    big_star_path (cr);
+    cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD);
+    cairo_fill (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (fill_rule,
+           "Tests cairo_set_fill_rule with some star shapes",
+           "fill, path", /* keywords */
+           NULL, /* requirements */
+           BIG_STAR_SIZE * 2 + 3, BIG_STAR_SIZE + LITTLE_STAR_SIZE + 3,
+           NULL, draw)
diff --git a/test/filter-bilinear-extents.c b/test/filter-bilinear-extents.c
new file mode 100644 (file)
index 0000000..79d36f6
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * Copyright © 2008 Red Hat, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Authors: Carl D. Worth <cworth@cworth.org>
+ *         Owen Taylor <otaylor@redhat.com>
+ */
+
+#include "cairo-test.h"
+
+/* This test exercises code that computes the extents of a surface
+ * pattern with CAIRO_FILTER_BILINEAR, (where the filtering
+ * effectively increases the extents of the pattern).
+ *
+ * The original bug was reported by Owen Taylor here:
+ *
+ *     bad clipping with EXTEND_NONE
+ *     http://bugs.freedesktop.org/show_bug.cgi?id=15349
+ */
+
+#define SCALE  10
+#define PAD    3
+#define WIDTH  (PAD + 3 * SCALE + PAD)
+#define HEIGHT WIDTH
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_surface_t *image;
+    cairo_t *cr2;
+
+    image = cairo_image_surface_create (CAIRO_FORMAT_RGB24, 2, 2);
+
+    /* Fill with an opaque background to avoid a separate rgb24 ref image */
+    cairo_set_source_rgb (cr, 0, 0, 0);
+    cairo_paint (cr);
+
+    /* First check handling of pattern extents > surface extents */
+    cairo_save (cr);
+    cairo_scale (cr, width/2., height/2.);
+
+    /* Create a solid black source to merge with the background */
+    cr2 = cairo_create (image);
+    cairo_set_source_rgb (cr2, 0, 0 ,0);
+    cairo_paint (cr2);
+    cairo_set_source_surface (cr, cairo_get_target (cr2), 0, 0);
+    cairo_destroy (cr2);
+    cairo_pattern_set_filter (cairo_get_source (cr), CAIRO_FILTER_BILINEAR);
+    cairo_paint (cr);
+    cairo_restore (cr);
+
+    /* Then scale to smaller so we can see the full bilinear extents */
+    cairo_save (cr);
+    cairo_translate (cr, PAD, PAD);
+    cairo_scale (cr, SCALE, SCALE);
+    cairo_translate (cr, 0.5, 0.5);
+
+    /* Create a 2x2 blue+red checkerboard source */
+    cr2 = cairo_create (image);
+    cairo_set_source_rgb (cr2, 1, 0 ,0); /* red */
+    cairo_paint (cr2);
+    cairo_set_source_rgb (cr2, 0, 0, 1); /* blue */
+    cairo_rectangle (cr2, 0, 1, 1, 1);
+    cairo_rectangle (cr2, 1, 0, 1, 1);
+    cairo_fill (cr2);
+    cairo_set_source_surface (cr, cairo_get_target (cr2), 0, 0);
+    cairo_destroy (cr2);
+
+    cairo_pattern_set_filter (cairo_get_source (cr), CAIRO_FILTER_BILINEAR);
+    cairo_paint (cr);
+    cairo_restore (cr);
+
+    cairo_surface_destroy (image);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (filter_bilinear_extents,
+           "Test that pattern extents are properly computed for CAIRO_FILTER_BILINEAR",
+           "extents", /* keywords */
+           NULL, /* requirements */
+           WIDTH, HEIGHT,
+           NULL, draw)
diff --git a/test/filter-nearest-offset.c b/test/filter-nearest-offset.c
new file mode 100644 (file)
index 0000000..4df6097
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ */
+
+#include "cairo-test.h"
+
+#define STAMP_WIDTH  4
+#define STAMP_HEIGHT 4
+#define PAD          1
+
+#define STEPS       10
+
+#define IMAGE_WIDTH  (PAD + STEPS * (STAMP_WIDTH  + PAD) + PAD)
+#define IMAGE_HEIGHT (PAD + STEPS * (STAMP_HEIGHT + PAD) + PAD)
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_surface_t *surface;
+    uint32_t data[STAMP_WIDTH * STAMP_HEIGHT] = {
+       0xffffffff, 0xffffffff,         0xffff0000, 0xffff0000,
+       0xffffffff, 0xffffffff,         0xffff0000, 0xffff0000,
+
+       0xff00ff00, 0xff00ff00,         0xff0000ff, 0xff0000ff,
+       0xff00ff00, 0xff00ff00,         0xff0000ff, 0xff0000ff
+    };
+    int i, j;
+
+    /* fill with off-white to avoid a separate rgb24 ref image */
+    cairo_save (cr);
+    cairo_set_source_rgb (cr, .7, .7, .7);
+    cairo_paint (cr);
+    cairo_restore (cr);
+
+    /* Draw reference lines where the jump should be. */
+    cairo_move_to (cr, PAD + STEPS / 2 * (STAMP_WIDTH + PAD), 0);
+    cairo_rel_line_to (cr, 0, IMAGE_HEIGHT);
+    cairo_move_to (cr, 0, PAD + STEPS / 2 * (STAMP_HEIGHT + PAD));
+    cairo_rel_line_to (cr, IMAGE_WIDTH, 0);
+    cairo_set_line_width (cr, 2.0);
+    cairo_stroke (cr);
+
+    surface = cairo_image_surface_create_for_data ((unsigned char *) data,
+                                                  CAIRO_FORMAT_RGB24,
+                                                  STAMP_WIDTH,
+                                                  STAMP_HEIGHT,
+                                                  STAMP_WIDTH * 4);
+
+    for (j=0; j < STEPS; j++) {
+       double j_step;
+
+       for (i=0; i < STEPS; i++) {
+           double i_step;
+
+#define GENERATE_REFERENCE_IMAGE 0
+#if GENERATE_REFERENCE_IMAGE
+           i_step = i >= STEPS / 2 ? 1 : 0;
+           j_step = j >= STEPS / 2 ? 1 : 0;
+#else
+           i_step = i * 1.0 / STEPS;
+           j_step = j * 1.0 / STEPS;
+#endif
+
+           cairo_save (cr);
+
+           cairo_set_source_surface (cr, surface,
+                                     PAD + i * (STAMP_WIDTH  + PAD) + i_step,
+                                     PAD + j * (STAMP_HEIGHT + PAD) + j_step);
+           cairo_pattern_set_filter (cairo_get_source (cr), CAIRO_FILTER_NEAREST);
+           cairo_paint (cr);
+
+           cairo_restore (cr);
+       }
+    }
+
+    cairo_surface_finish (surface); /* data goes out of scope */
+    cairo_surface_destroy (surface);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (filter_nearest_offset,
+           "Test sampling offset of CAIRO_FILTER_NEAREST"
+           "\nwrong sampling location for nearest-neighbor filter in libpixman and Render",
+           "filter", /* keywords */
+           NULL, /* requirements */
+           IMAGE_WIDTH, IMAGE_HEIGHT,
+           NULL, draw)
diff --git a/test/filter-nearest-transformed.c b/test/filter-nearest-transformed.c
new file mode 100644 (file)
index 0000000..ba56f7c
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ * Copyright © 2008 Chris Wilson
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+
+/*
+ * We wish to check the optimization away of non-fractional translations
+ * for NEAREST surface patterns under a few transformations.
+ */
+
+static const char png_filename[] = "romedalen.png";
+
+/* A single, black pixel */
+static const uint32_t black_pixel = 0xff000000;
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    const cairo_test_context_t *ctx = cairo_test_get_context (cr);
+    unsigned int i, j, k;
+    cairo_surface_t *surface;
+    cairo_pattern_t *pattern;
+    const cairo_matrix_t transform[] = {
+       {  1, 0, 0,  1,  0, 0 },
+       { -1, 0, 0,  1,  8, 0 },
+       {  1, 0, 0, -1,  0, 8 },
+       { -1, 0, 0, -1,  8, 8 },
+    };
+    const cairo_matrix_t ctx_transform[] = {
+       {  1, 0, 0,  1,   0,  0 },
+       { -1, 0, 0,  1,  14,  0 },
+       {  1, 0, 0, -1,   0, 14 },
+       { -1, 0, 0, -1,  14, 14 },
+    };
+    const double colour[][3] = {
+       {0, 0, 0},
+       {1, 0, 0},
+       {0, 1, 0},
+       {0, 0, 1},
+    };
+    cairo_matrix_t m;
+
+    surface = cairo_image_surface_create_for_data ((uint8_t *) &black_pixel,
+                                                  CAIRO_FORMAT_ARGB32,
+                                                  1, 1, 4);
+    pattern = cairo_pattern_create_for_surface (surface);
+    cairo_surface_destroy (surface);
+
+    cairo_pattern_set_filter (pattern, CAIRO_FILTER_NEAREST);
+
+    surface = cairo_test_create_surface_from_png (ctx, png_filename);
+
+    /* Fill background white */
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_paint (cr);
+
+    cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
+
+    for (k = 0; k < ARRAY_LENGTH (transform); k++) {
+       /* draw a "large" section from an image */
+       cairo_save (cr); {
+           cairo_set_matrix(cr, &ctx_transform[k]);
+           cairo_rectangle (cr, 0, 0, 7, 7);
+           cairo_clip (cr);
+
+           cairo_set_source_surface (cr, surface,
+                                     -cairo_image_surface_get_width (surface)/2.,
+                                     -cairo_image_surface_get_height (surface)/2.);
+           cairo_pattern_set_filter (cairo_get_source (cr), CAIRO_FILTER_NEAREST);
+           cairo_paint (cr);
+       } cairo_restore (cr);
+
+       cairo_set_source_rgb (cr, colour[k][0], colour[k][1], colour[k][2]);
+       for (j = 4; j <= 6; j++) {
+           for (i = 4; i <= 6; i++) {
+               cairo_matrix_init_translate (&m,
+                                            -(2*(i-4) + .1*i),
+                                            -(2*(j-4) + .1*j));
+               cairo_matrix_multiply (&m, &m, &transform[k]);
+               cairo_pattern_set_matrix (pattern, &m);
+               cairo_mask (cr, pattern);
+           }
+       }
+    }
+
+    cairo_pattern_destroy (pattern);
+    cairo_surface_destroy (surface);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (filter_nearest_transformed,
+           "Test sample position when drawing transformed images with FILTER_NEAREST",
+           "filter, nearest", /* keywords */
+           NULL,
+           14, 14,
+           NULL, draw)
diff --git a/test/finer-grained-fallbacks.c b/test/finer-grained-fallbacks.c
new file mode 100644 (file)
index 0000000..fa16f72
--- /dev/null
@@ -0,0 +1,180 @@
+/*
+ * Copyright © 2008 Adrian Johnson
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Adrian Johnson <ajohnson@redneon.com>
+ */
+
+#include "cairo-test.h"
+
+#define CIRCLE_SIZE 10
+#define PAD 2
+#define WIDTH (CIRCLE_SIZE*6.5 + PAD)
+#define HEIGHT (CIRCLE_SIZE*7.0 + PAD)
+
+static void
+draw_circle (cairo_t *cr, double x, double y)
+{
+    cairo_save (cr);
+    cairo_translate (cr, x, y);
+    cairo_arc (cr, 0, 0, CIRCLE_SIZE / 2, 0., 2. * M_PI);
+    cairo_fill (cr);
+    cairo_restore (cr);
+}
+
+static void
+draw_image_circle (cairo_t *cr, cairo_surface_t *source, double x, double y)
+{
+    cairo_save (cr);
+
+    cairo_set_source_surface (cr, source, x, y);
+    cairo_pattern_set_extend (cairo_get_source (cr), CAIRO_EXTEND_REFLECT);
+    cairo_rectangle (cr, x, y, CIRCLE_SIZE, CIRCLE_SIZE);
+    cairo_fill (cr);
+
+    cairo_restore (cr);
+}
+
+static void
+draw_circles (cairo_t *cr)
+{
+    draw_circle (cr, 0,               -CIRCLE_SIZE*0.1);
+    draw_circle (cr, CIRCLE_SIZE*0.4,  CIRCLE_SIZE*0.25);
+
+    draw_circle (cr, CIRCLE_SIZE*2, 0);
+    draw_circle (cr, CIRCLE_SIZE*4, 0);
+    draw_circle (cr, CIRCLE_SIZE*6, 0);
+}
+
+static void
+draw_image_circles (cairo_t *cr, cairo_surface_t *source)
+{
+    draw_image_circle (cr, source, 0,               -CIRCLE_SIZE*0.1);
+    draw_image_circle (cr, source, CIRCLE_SIZE*0.4,  CIRCLE_SIZE*0.25);
+
+    draw_image_circle (cr, source, CIRCLE_SIZE*2, 0);
+    draw_image_circle (cr, source, CIRCLE_SIZE*4, 0);
+    draw_image_circle (cr, source, CIRCLE_SIZE*6, 0);
+}
+
+/* For each of circle and fallback_circle we draw:
+ *  - two overlapping
+ *  - one isolated
+ *  - one off the page
+ *  - one overlapping the edge of the page.
+ *
+ * We also draw a circle and fallback_circle overlapping each other.
+ *
+ * Circles are drawn in green. An opaque color and CAIRO_OPERATOR_OVER
+ * is used to ensure they will be emitted as a vectors in PS/PDF.
+ *
+ * Fallback circles are drawn in red. CAIRO_OPERATOR_ADD is used to
+ * ensure they will be emitted as a fallback image in PS/PDF.
+ *
+ * In order to trigger a fallback for SVG, we need to use a surface with
+ * REFLECT.
+ */
+static cairo_surface_t *
+surface_create (cairo_t *target)
+{
+    cairo_surface_t *surface;
+    cairo_t *cr;
+
+    surface = cairo_surface_create_similar (cairo_get_target (target),
+                                           CAIRO_CONTENT_COLOR_ALPHA,
+                                           CIRCLE_SIZE, CIRCLE_SIZE);
+    cr = cairo_create (surface);
+    cairo_surface_destroy (surface);
+
+    cairo_set_source_rgb (cr, 1.0, 0.0, 0.0);
+    draw_circle (cr, CIRCLE_SIZE/2, CIRCLE_SIZE/2);
+
+    surface = cairo_surface_reference (cairo_get_target (cr));
+    cairo_destroy (cr);
+
+    return surface;
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_surface_t *surface;
+
+    cairo_translate (cr, PAD, PAD);
+
+    cairo_save (cr);
+
+    /* Draw overlapping circle and fallback circle */
+    cairo_set_source_rgb (cr, 0.0, 1.0, 0.0);
+    cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
+    draw_circle (cr, CIRCLE_SIZE*0.5,  CIRCLE_SIZE*1.5);
+
+    cairo_set_source_rgb (cr, 1.0, 0.0, 0.0);
+    cairo_set_operator (cr, CAIRO_OPERATOR_ADD);
+    draw_circle (cr, CIRCLE_SIZE*0.75, CIRCLE_SIZE*1.75);
+
+    /* Draw circles */
+    cairo_set_source_rgb (cr, 0.0, 1.0, 0.0);
+    cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
+    cairo_translate (cr, CIRCLE_SIZE*2.5, CIRCLE_SIZE*0.6);
+    draw_circles (cr);
+
+    /* Draw fallback circles */
+    cairo_set_source_rgb (cr, 1.0, 0.0, 0.0);
+    cairo_set_operator (cr, CAIRO_OPERATOR_ADD);
+    cairo_translate (cr, 0, CIRCLE_SIZE*2);
+    draw_circles (cr);
+
+    cairo_restore (cr);
+    cairo_translate (cr, 0, CIRCLE_SIZE * 3.5);
+
+    /* Draw using fallback surface */
+    surface = surface_create (cr);
+
+    cairo_set_source_rgb (cr, 0.0, 1.0, 0.0);
+    cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
+    draw_circle (cr, CIRCLE_SIZE*0.5,  CIRCLE_SIZE*1.5);
+
+    cairo_set_operator (cr, CAIRO_OPERATOR_ADD);
+    draw_image_circle (cr, surface, CIRCLE_SIZE/4, CIRCLE_SIZE + CIRCLE_SIZE/4);
+
+    /* Draw circles */
+    cairo_set_source_rgb (cr, 0.0, 1.0, 0.0);
+    cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
+    cairo_translate (cr, CIRCLE_SIZE*2.5, CIRCLE_SIZE*0.6);
+    draw_circles (cr);
+
+    cairo_set_operator (cr, CAIRO_OPERATOR_ADD);
+    cairo_translate (cr, -CIRCLE_SIZE/2, CIRCLE_SIZE*1.5);
+    draw_image_circles (cr, surface);
+
+    cairo_surface_destroy (surface);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (finer_grained_fallbacks,
+           "Test that multiple PS/PDF fallback images in various locations are correct",
+           "fallbacks", /* keywords */
+           NULL, /* requirements */
+           WIDTH, HEIGHT,
+           NULL, draw)
diff --git a/test/font-face-get-type.c b/test/font-face-get-type.c
new file mode 100644 (file)
index 0000000..afbb739
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * Copyright © 2006 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ */
+
+#include "cairo-test.h"
+
+static cairo_test_status_t
+preamble (cairo_test_context_t *ctx)
+{
+    cairo_test_status_t status = CAIRO_TEST_SUCCESS;
+    cairo_surface_t *surface;
+    cairo_t *cr;
+    cairo_font_face_t *font_face;
+    cairo_scaled_font_t *scaled_font;
+
+    cairo_test_log (ctx, "Creating cairo context and obtaining a font face\n");
+
+    surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 1, 1);
+    cr = cairo_create (surface);
+
+    cairo_select_font_face (cr, CAIRO_TEST_FONT_FAMILY " Sans",
+                           CAIRO_FONT_SLANT_NORMAL,
+                           CAIRO_FONT_WEIGHT_NORMAL);
+
+    cairo_test_log (ctx, "Testing return value of cairo_font_face_get_type\n");
+
+    font_face = cairo_get_font_face (cr);
+
+    if (cairo_font_face_get_type (font_face) != CAIRO_FONT_TYPE_TOY) {
+       cairo_test_log (ctx, "Unexpected value %d from cairo_font_face_get_type (expected %d)\n",
+                       cairo_font_face_get_type (font_face), CAIRO_FONT_TYPE_TOY);
+       status = CAIRO_TEST_FAILURE;
+       goto done;
+    }
+
+    cairo_test_log (ctx, "Testing return value of cairo_get_scaled_font\n");
+
+    scaled_font = cairo_get_scaled_font (cr);
+
+    if (cairo_scaled_font_get_font_face (scaled_font) != font_face) {
+       cairo_test_log (ctx, "Font face returned from the scaled font is different from that returned by the context\n");
+       status = CAIRO_TEST_FAILURE;
+       goto done;
+    }
+
+done:
+    cairo_destroy (cr);
+    cairo_surface_destroy (surface);
+
+    return status;
+}
+
+CAIRO_TEST (font_face_get_type,
+           "Check the returned type from cairo_select_font_face.",
+           "font", /* keywords */
+           NULL, /* requirements */
+           0, 0,
+           preamble, NULL)
diff --git a/test/font-matrix-translation.c b/test/font-matrix-translation.c
new file mode 100644 (file)
index 0000000..465ac7c
--- /dev/null
@@ -0,0 +1,141 @@
+/*
+ * Copyright © 2006 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ */
+
+#include "cairo-test.h"
+
+#define TEXT_SIZE 12
+#define PAD 4
+#define TEXT "text"
+
+static cairo_bool_t
+text_extents_equal (const cairo_text_extents_t *A,
+                   const cairo_text_extents_t *B)
+{
+    return A->x_bearing == B->x_bearing &&
+          A->y_bearing == B->y_bearing &&
+          A->width     == B->width     &&
+          A->height    == B->height    &&
+          A->x_advance == B->x_advance &&
+          A->y_advance == B->y_advance;
+}
+
+static cairo_test_status_t
+box_text (const cairo_test_context_t *ctx, cairo_t *cr,
+         const char *utf8,
+         double x, double y)
+{
+    double line_width;
+    cairo_text_extents_t extents = {0}, scaled_extents = {0};
+    cairo_scaled_font_t *scaled_font;
+    cairo_status_t status;
+
+    cairo_save (cr);
+
+    cairo_text_extents (cr, utf8, &extents);
+
+    scaled_font = cairo_get_scaled_font (cr);
+    cairo_scaled_font_text_extents (scaled_font, TEXT, &scaled_extents);
+    status = cairo_scaled_font_status (scaled_font);
+    if (status)
+       return cairo_test_status_from_status (ctx, status);
+
+    if (! text_extents_equal (&extents, &scaled_extents)) {
+        cairo_test_log (ctx,
+                       "Error: extents differ when they shouldn't:\n"
+                       "cairo_text_extents(); extents (%g, %g, %g, %g, %g, %g)\n"
+                       "cairo_scaled_font_text_extents(); extents (%g, %g, %g, %g, %g, %g)\n",
+                       extents.x_bearing, extents.y_bearing,
+                       extents.width, extents.height,
+                       extents.x_advance, extents.y_advance,
+                       scaled_extents.x_bearing, scaled_extents.y_bearing,
+                       scaled_extents.width, scaled_extents.height,
+                       scaled_extents.x_advance, scaled_extents.y_advance);
+        return CAIRO_TEST_FAILURE;
+    }
+
+    line_width = cairo_get_line_width (cr);
+    cairo_rectangle (cr,
+                    x + extents.x_bearing - line_width / 2,
+                    y + extents.y_bearing - line_width / 2,
+                    extents.width  + line_width,
+                    extents.height + line_width);
+    cairo_stroke (cr);
+
+    cairo_move_to (cr, x, y);
+    cairo_show_text (cr, utf8);
+
+    cairo_restore (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    const cairo_test_context_t *ctx = cairo_test_get_context (cr);
+    cairo_test_status_t status;
+    cairo_text_extents_t extents;
+    cairo_matrix_t matrix;
+
+    cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); /* white */
+    cairo_paint (cr);
+
+    cairo_select_font_face (cr, CAIRO_TEST_FONT_FAMILY " Sans",
+                           CAIRO_FONT_SLANT_NORMAL,
+                           CAIRO_FONT_WEIGHT_NORMAL);
+    cairo_set_font_size (cr, TEXT_SIZE);
+
+    cairo_translate (cr, PAD, PAD);
+    cairo_set_line_width (cr, 1.0);
+
+    cairo_text_extents (cr, TEXT, &extents);
+
+    /* Draw text and bounding box */
+    cairo_set_source_rgb (cr, 0, 0, 0); /* black */
+    status = box_text (ctx, cr, TEXT, 0, - extents.y_bearing);
+    if (status)
+       return status;
+
+    /* Then draw again with the same coordinates, but with a font
+     * matrix to position the text below and shifted a bit to the
+     * right. */
+    cairo_matrix_init_translate (&matrix, TEXT_SIZE / 2, TEXT_SIZE + PAD);
+    cairo_matrix_scale (&matrix, TEXT_SIZE, TEXT_SIZE);
+    cairo_set_font_matrix (cr, &matrix);
+
+    cairo_set_source_rgb (cr, 0, 0, 1); /* blue */
+    status = box_text (ctx, cr, TEXT, 0, - extents.y_bearing);
+    if (status)
+       return status;
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (font_matrix_translation,
+           "Test that translation in a font matrix can be used to offset a string",
+           "font", /* keywords */
+           NULL, /* requirements */
+           38, 34,
+           NULL, draw)
diff --git a/test/font-options.c b/test/font-options.c
new file mode 100644 (file)
index 0000000..873a5c3
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * Copyright © 2008 Chris Wilson
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Chris Wilson not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Chris Wilson makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * CHRIS WILSON DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL CHRIS WILSON BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+
+#include <assert.h>
+
+static cairo_test_status_t
+preamble (cairo_test_context_t *ctx)
+{
+    cairo_font_options_t *default_options;
+    cairo_font_options_t *nil_options;
+    cairo_surface_t *surface;
+    cairo_matrix_t identity;
+    cairo_t *cr;
+    cairo_scaled_font_t *scaled_font;
+
+    /* first check NULL handling of cairo_font_options_t */
+    default_options = cairo_font_options_create ();
+    assert (cairo_font_options_status (default_options) == CAIRO_STATUS_SUCCESS);
+    nil_options = cairo_font_options_copy (NULL);
+    assert (cairo_font_options_status (nil_options) == CAIRO_STATUS_NO_MEMORY);
+
+    assert (cairo_font_options_equal (default_options, default_options));
+    assert (! cairo_font_options_equal (default_options, nil_options));
+    assert (! cairo_font_options_equal (NULL, nil_options));
+    assert (! cairo_font_options_equal (nil_options, nil_options));
+    assert (! cairo_font_options_equal (default_options, NULL));
+    assert (! cairo_font_options_equal (NULL, default_options));
+
+    assert (cairo_font_options_hash (default_options) == cairo_font_options_hash (nil_options));
+    assert (cairo_font_options_hash (NULL) == cairo_font_options_hash (nil_options));
+    assert (cairo_font_options_hash (default_options) == cairo_font_options_hash (NULL));
+
+    cairo_font_options_merge (NULL, NULL);
+    cairo_font_options_merge (default_options, NULL);
+    cairo_font_options_merge (default_options, nil_options);
+
+    cairo_font_options_set_antialias (NULL, CAIRO_ANTIALIAS_DEFAULT);
+    cairo_font_options_get_antialias (NULL);
+    assert (cairo_font_options_get_antialias (default_options) == CAIRO_ANTIALIAS_DEFAULT);
+
+    cairo_font_options_set_subpixel_order (NULL, CAIRO_SUBPIXEL_ORDER_DEFAULT);
+    cairo_font_options_get_subpixel_order (NULL);
+    assert (cairo_font_options_get_subpixel_order (default_options) == CAIRO_SUBPIXEL_ORDER_DEFAULT);
+
+    cairo_font_options_set_hint_style (NULL, CAIRO_HINT_STYLE_DEFAULT);
+    cairo_font_options_get_hint_style (NULL);
+    assert (cairo_font_options_get_hint_style (default_options) == CAIRO_HINT_STYLE_DEFAULT);
+
+    cairo_font_options_set_hint_metrics (NULL, CAIRO_HINT_METRICS_DEFAULT);
+    cairo_font_options_get_hint_metrics (NULL);
+    assert (cairo_font_options_get_hint_metrics (default_options) == CAIRO_HINT_METRICS_DEFAULT);
+
+    cairo_font_options_destroy (NULL);
+    cairo_font_options_destroy (default_options);
+    cairo_font_options_destroy (nil_options);
+
+
+    /* Now try creating fonts with NULLs */
+    surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, 0, 0);
+    cr = cairo_create (surface);
+    cairo_surface_destroy (surface);
+
+    cairo_matrix_init_identity (&identity);
+    scaled_font = cairo_scaled_font_create (cairo_get_font_face (cr),
+                                           &identity, &identity,
+                                           NULL);
+    assert (cairo_scaled_font_status (scaled_font) == CAIRO_STATUS_NULL_POINTER);
+    cairo_scaled_font_get_font_options (scaled_font, NULL);
+    cairo_scaled_font_destroy (scaled_font);
+
+    assert (cairo_status (cr) == CAIRO_STATUS_SUCCESS);
+    cairo_get_font_options (cr, NULL);
+    cairo_set_font_options (cr, NULL);
+    assert (cairo_status (cr) == CAIRO_STATUS_NULL_POINTER);
+
+    cairo_destroy (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (font_options,
+           "Check setters and getters on cairo_font_options_t.",
+           "font, api", /* keywords */
+           NULL, /* requirements */
+           0, 0,
+           preamble, NULL)
diff --git a/test/ft-font-create-for-ft-face.c b/test/ft-font-create-for-ft-face.c
new file mode 100644 (file)
index 0000000..52c838d
--- /dev/null
@@ -0,0 +1,228 @@
+/*
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ */
+
+#include "cairo-test.h"
+#include <cairo-ft.h>
+
+static void
+_stress_font_cache (FT_Face ft_face, cairo_t *cr, int lvl);
+
+static cairo_font_face_t *
+_load_font (FT_Face ft_face, int flags, cairo_t *cr, int lvl)
+{
+    cairo_font_face_t *font_face;
+    cairo_font_extents_t font_extents;
+
+    _stress_font_cache (ft_face, cr, lvl+1);
+
+    font_face = cairo_ft_font_face_create_for_ft_face (ft_face, flags);
+
+    cairo_set_font_face (cr, font_face);
+    cairo_font_extents (cr, &font_extents);
+
+    _stress_font_cache (ft_face, cr, lvl+1);
+
+    return font_face;
+}
+
+static void
+_stress_font_cache (FT_Face ft_face, cairo_t *cr, int lvl)
+{
+#define A _load_font (ft_face, 0, cr, lvl)
+#define B _load_font (ft_face, FT_LOAD_NO_BITMAP, cr, lvl)
+#define C _load_font (ft_face, FT_LOAD_NO_RECURSE, cr, lvl)
+#define D _load_font (ft_face, FT_LOAD_FORCE_AUTOHINT, cr, lvl)
+
+    cairo_font_face_t *font_face[4];
+
+    while (lvl++ < 5) {
+       font_face[0] = A; font_face[1] = A;
+       font_face[2] = A; font_face[3] = A;
+       cairo_font_face_destroy (font_face[0]);
+       cairo_font_face_destroy (font_face[1]);
+       cairo_font_face_destroy (font_face[2]);
+       cairo_font_face_destroy (font_face[3]);
+
+       font_face[0] = A; font_face[1] = B;
+       font_face[2] = C; font_face[3] = D;
+       cairo_font_face_destroy (font_face[0]);
+       cairo_font_face_destroy (font_face[1]);
+       cairo_font_face_destroy (font_face[2]);
+       cairo_font_face_destroy (font_face[3]);
+
+       font_face[0] = A; font_face[1] = B;
+       font_face[2] = C; font_face[3] = D;
+       cairo_font_face_destroy (font_face[3]);
+       cairo_font_face_destroy (font_face[2]);
+       cairo_font_face_destroy (font_face[1]);
+       cairo_font_face_destroy (font_face[0]);
+
+       font_face[0] = A;
+       font_face[1] = A;
+       cairo_font_face_destroy (font_face[0]);
+       font_face[2] = A;
+       cairo_font_face_destroy (font_face[1]);
+       font_face[3] = A;
+       cairo_font_face_destroy (font_face[2]);
+       cairo_font_face_destroy (font_face[3]);
+
+       font_face[0] = A;
+       font_face[1] = B;
+       cairo_font_face_destroy (font_face[0]);
+       font_face[2] = C;
+       cairo_font_face_destroy (font_face[1]);
+       font_face[3] = D;
+       cairo_font_face_destroy (font_face[2]);
+       cairo_font_face_destroy (font_face[3]);
+    }
+
+#undef A
+#undef B
+#undef C
+#undef D
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    const cairo_test_context_t *ctx = cairo_test_get_context (cr);
+    FcPattern *pattern, *resolved;
+    FcResult result;
+    cairo_font_face_t *font_face;
+    cairo_scaled_font_t *scaled_font;
+    cairo_font_options_t *font_options;
+    cairo_font_extents_t font_extents;
+    cairo_matrix_t font_matrix, ctm;
+    FT_Face ft_face;
+
+    /* We're trying here to get our hands on _some_ FT_Face but we do
+     * not at all care which one. So we start with an empty pattern
+     * and do the minimal substitution on it in order to get a valid
+     * pattern.
+     *
+     * Do not use this in production code! */
+    pattern = FcPatternCreate ();
+    if (! pattern) {
+       cairo_test_log (ctx, "FcPatternCreate failed.\n");
+       return cairo_test_status_from_status (ctx, CAIRO_STATUS_NO_MEMORY);
+    }
+
+    FcConfigSubstitute (NULL, pattern, FcMatchPattern);
+    FcDefaultSubstitute (pattern);
+    resolved = FcFontMatch (NULL, pattern, &result);
+    if (! resolved) {
+       FcPatternDestroy (pattern);
+       cairo_test_log (ctx, "FcFontMatch failed.\n");
+       return cairo_test_status_from_status (ctx, CAIRO_STATUS_NO_MEMORY);
+    }
+
+    font_face = cairo_ft_font_face_create_for_pattern (resolved);
+    if (cairo_font_face_status (font_face)) {
+       FcPatternDestroy (resolved);
+       FcPatternDestroy (pattern);
+       return cairo_test_status_from_status (ctx, cairo_font_face_status (font_face));
+    }
+
+    if (cairo_font_face_get_type (font_face) != CAIRO_FONT_TYPE_FT) {
+       cairo_test_log (ctx, "Unexpected value from cairo_font_face_get_type: %d (expected %d)\n",
+                       cairo_font_face_get_type (font_face), CAIRO_FONT_TYPE_FT);
+       cairo_font_face_destroy (font_face);
+       FcPatternDestroy (resolved);
+       FcPatternDestroy (pattern);
+       return CAIRO_TEST_FAILURE;
+    }
+
+    cairo_matrix_init_identity (&font_matrix);
+
+    cairo_get_matrix (cr, &ctm);
+
+    font_options = cairo_font_options_create ();
+
+    cairo_get_font_options (cr, font_options);
+
+    scaled_font = cairo_scaled_font_create (font_face,
+                                           &font_matrix,
+                                           &ctm,
+                                           font_options);
+
+    cairo_font_options_destroy (font_options);
+    cairo_font_face_destroy (font_face);
+    FcPatternDestroy (pattern);
+    FcPatternDestroy (resolved);
+
+    if (cairo_scaled_font_status (scaled_font)) {
+       return cairo_test_status_from_status (ctx,
+                                             cairo_scaled_font_status (scaled_font));
+    }
+
+    if (cairo_scaled_font_get_type (scaled_font) != CAIRO_FONT_TYPE_FT) {
+       cairo_test_log (ctx, "Unexpected value from cairo_scaled_font_get_type: %d (expected %d)\n",
+                       cairo_scaled_font_get_type (scaled_font), CAIRO_FONT_TYPE_FT);
+       cairo_scaled_font_destroy (scaled_font);
+       return CAIRO_TEST_FAILURE;
+    }
+
+    ft_face = cairo_ft_scaled_font_lock_face (scaled_font);
+    if (ft_face == NULL) {
+       cairo_test_log (ctx, "Failed to get an ft_face with cairo_ft_scaled_font_lock_face\n");
+       cairo_scaled_font_destroy (scaled_font);
+       return CAIRO_TEST_FAILURE;
+    }
+
+    /* phew, that was a lot of work. But at least we didn't ever have
+     * to call freetype directly, nor did we have to make many (any?)
+     * assumptions about the current system.
+     *
+     * Now, on to the simple thing we actually want to test.
+     */
+
+    cairo_save (cr);
+
+    /* First we want to test caching behaviour */
+    _stress_font_cache (ft_face, cr, 0);
+
+    /* Set the font_face and force cairo to actually use it for
+     * something. */
+    font_face = cairo_ft_font_face_create_for_ft_face (ft_face, 0);
+    cairo_set_font_face (cr, font_face);
+    cairo_font_extents (cr, &font_extents);
+
+    cairo_restore (cr);
+
+    /* Finally, even more cleanup */
+    cairo_font_face_destroy (font_face);
+    cairo_ft_scaled_font_unlock_face (scaled_font);
+    cairo_scaled_font_destroy (scaled_font);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (ft_font_create_for_ft_face,
+           "Simple test to verify that cairo_ft_font_create_for_ft_face doesn't crash.",
+           "ft, font", /* keywords */
+           NULL, /* requirements */
+           0, 0,
+           NULL, draw)
+
diff --git a/test/ft-show-glyphs-positioning.c b/test/ft-show-glyphs-positioning.c
new file mode 100644 (file)
index 0000000..449f8eb
--- /dev/null
@@ -0,0 +1,191 @@
+/*
+ * Copyright © 2008 Adrian Johnson
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Adrian Johnson <ajohnson@redneon.com>
+ */
+
+#include "cairo-test.h"
+#include <cairo-ft.h>
+
+#define TEXT_SIZE 12
+
+typedef struct {
+    cairo_glyph_t glyph_list[100];
+    int num_glyphs;
+    double x;
+    double y;
+} glyph_array_t;
+
+static void
+glyph_array_init (glyph_array_t *glyphs, double x, double y)
+{
+    glyphs->num_glyphs = 0;
+    glyphs->x = x;
+    glyphs->y = y;
+}
+
+static void
+glyph_array_rel_move_to (glyph_array_t *glyphs, double x, double y)
+{
+    glyphs->x += x;
+    glyphs->y += y;
+}
+
+static void
+glyph_array_show (glyph_array_t *glyphs, cairo_t *cr)
+{
+    cairo_show_glyphs (cr, glyphs->glyph_list, glyphs->num_glyphs);
+}
+
+#define DOUBLE_FROM_26_6(t) ((double)(t) / 64.0)
+
+static cairo_status_t
+glyph_array_add_text(glyph_array_t *glyphs, cairo_t *cr, const char *s, double spacing)
+{
+    cairo_scaled_font_t *scaled_font;
+    cairo_status_t status;
+    FT_Face face;
+    unsigned long charcode;
+    unsigned int index;
+    cairo_text_extents_t extents;
+    const char *p;
+    FT_Vector kerning;
+    double kern_x;
+    int first = TRUE;
+
+    scaled_font = cairo_get_scaled_font (cr);
+    status = cairo_scaled_font_status (scaled_font);
+    if (status)
+       return status;
+
+    face = cairo_ft_scaled_font_lock_face (scaled_font);
+    if (face == NULL)
+       return CAIRO_STATUS_FONT_TYPE_MISMATCH;
+
+    p = s;
+    while (*p)
+    {
+        charcode = *p;
+        index = FT_Get_Char_Index (face, charcode);
+        glyphs->glyph_list[glyphs->num_glyphs].index = index;
+        if (first) {
+            first = FALSE;
+            glyphs->glyph_list[glyphs->num_glyphs].x = glyphs->x;
+            glyphs->glyph_list[glyphs->num_glyphs].y = glyphs->y;
+        } else {
+            cairo_glyph_extents (cr, &glyphs->glyph_list[glyphs->num_glyphs - 1], 1, &extents);
+            FT_Get_Kerning (face,
+                            glyphs->glyph_list[glyphs->num_glyphs - 1].index,
+                            glyphs->glyph_list[glyphs->num_glyphs].index,
+                            FT_KERNING_UNSCALED,
+                            &kerning);
+            kern_x = DOUBLE_FROM_26_6(kerning.x);
+            glyphs->glyph_list[glyphs->num_glyphs].x =
+               glyphs->glyph_list[glyphs->num_glyphs - 1].x + extents.x_advance + kern_x + spacing;
+            glyphs->glyph_list[glyphs->num_glyphs].y =
+               glyphs->glyph_list[glyphs->num_glyphs - 1].y + extents.y_advance;
+       }
+
+       cairo_glyph_extents (cr, &glyphs->glyph_list[glyphs->num_glyphs], 1, &extents);
+       glyphs->x = glyphs->glyph_list[glyphs->num_glyphs].x + extents.x_advance + spacing;
+       glyphs->y = glyphs->glyph_list[glyphs->num_glyphs].y + extents.y_advance;
+       p++;
+        glyphs->num_glyphs++;
+    }
+
+    cairo_ft_scaled_font_unlock_face (scaled_font);
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    const cairo_test_context_t *ctx = cairo_test_get_context (cr);
+    glyph_array_t glyphs;
+    cairo_font_options_t *font_options;
+    cairo_status_t status;
+
+    /* paint white so we don't need separate ref images for
+     * RGB24 and ARGB32 */
+    cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
+    cairo_paint (cr);
+
+    cairo_select_font_face (cr, CAIRO_TEST_FONT_FAMILY " Sans",
+                           CAIRO_FONT_SLANT_NORMAL,
+                           CAIRO_FONT_WEIGHT_NORMAL);
+    cairo_set_font_size (cr, TEXT_SIZE);
+
+    font_options = cairo_font_options_create ();
+    cairo_get_font_options (cr, font_options);
+    cairo_font_options_set_hint_metrics (font_options, CAIRO_HINT_METRICS_OFF);
+    cairo_set_font_options (cr, font_options);
+    cairo_font_options_destroy (font_options);
+
+    cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
+
+    glyph_array_init (&glyphs, 1, TEXT_SIZE);
+
+    status = glyph_array_add_text(&glyphs, cr, "AWAY again", 0.0);
+    if (status)
+       return cairo_test_status_from_status (ctx, status);
+
+    glyph_array_rel_move_to (&glyphs, TEXT_SIZE*1, 0.0);
+    status = glyph_array_add_text(&glyphs, cr, "character space", TEXT_SIZE*0.3);
+    if (status)
+       return cairo_test_status_from_status (ctx, status);
+
+    glyph_array_show (&glyphs, cr);
+
+
+    glyph_array_init (&glyphs, 1, TEXT_SIZE*2 + 4);
+
+    status = glyph_array_add_text(&glyphs, cr, "Increasing", 0.0);
+    if (status)
+       return cairo_test_status_from_status (ctx, status);
+
+    glyph_array_rel_move_to (&glyphs, TEXT_SIZE*0.5, 0.0);
+    status = glyph_array_add_text(&glyphs, cr, "space", 0.0);
+    if (status)
+       return cairo_test_status_from_status (ctx, status);
+
+    glyph_array_rel_move_to (&glyphs, TEXT_SIZE*1.0, 0.0);
+    status = glyph_array_add_text(&glyphs, cr, "between", 0.0);
+    if (status)
+       return cairo_test_status_from_status (ctx, status);
+
+    glyph_array_rel_move_to (&glyphs, TEXT_SIZE*1.5, 0.0);
+    status = glyph_array_add_text(&glyphs, cr, "words", 0.0);
+    if (status)
+       return cairo_test_status_from_status (ctx, status);
+
+    glyph_array_show (&glyphs, cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (ft_show_glyphs_positioning,
+           "Test that the PS/PDF glyph positioning optimizations are correct",
+           "ft, text", /* keywords */
+           NULL, /* requirements */
+           235, (TEXT_SIZE + 4)*2,
+           NULL, draw)
diff --git a/test/ft-show-glyphs-table.c b/test/ft-show-glyphs-table.c
new file mode 100644 (file)
index 0000000..344392f
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ * Copyright © 2008 Red Hat, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Authors: Eugeniy Meshcheryakov <eugen@debian.org>
+ *         Adrian Johnson <ajohnson@redneon.com>
+ *         Carl Worth <cworth@cworth.org>
+ */
+
+#include "cairo-test.h"
+#include <cairo-ft.h>
+
+#define TEXT_SIZE      20
+#define PAD            10
+#define GRID_SIZE      30
+#define GRID_ROWS      10
+#define GRID_COLS      4
+#define NUM_GLYPHS     (GRID_ROWS * GRID_COLS)
+#define WIDTH          (PAD + GRID_COLS * GRID_SIZE + PAD)
+#define HEIGHT         (PAD + GRID_ROWS * GRID_SIZE + PAD)
+
+/* This test was originally inspired by this bug report:
+ *
+ * Error when creating pdf charts for new FreeSerifItalic.ttf
+ * http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=%23474136
+ *
+ * The original assertion failure was fairly boring, but the later
+ * glyph mispositiing was quite interesting. And it turns out that the
+ * _cairo_pdf_operators_show_glyphs code is fairly convoluted with a
+ * code path that wasn't being exercised at all by the test suite.
+ *
+ * So this is an attempt to exercise that code path. Apparently laying
+ * glyphs out vertically in a table like this, (so that there's a
+ * large change in Y position from one glyph to the next), exercises
+ * the code well.
+ */
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_font_options_t *font_options;
+    cairo_scaled_font_t *scaled_font;
+    FT_Face face;
+    FT_ULong charcode;
+    FT_UInt idx;
+    int i = 0;
+    cairo_glyph_t glyphs[NUM_GLYPHS];
+
+    /* paint white so we don't need separate ref images for
+     * RGB24 and ARGB32 */
+    cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
+    cairo_paint (cr);
+
+    cairo_select_font_face (cr, CAIRO_TEST_FONT_FAMILY " Sans",
+                           CAIRO_FONT_SLANT_NORMAL,
+                           CAIRO_FONT_WEIGHT_NORMAL);
+    cairo_set_font_size (cr, TEXT_SIZE);
+
+    font_options = cairo_font_options_create ();
+    cairo_get_font_options (cr, font_options);
+    cairo_font_options_set_hint_metrics (font_options, CAIRO_HINT_METRICS_OFF);
+    cairo_set_font_options (cr, font_options);
+    cairo_font_options_destroy (font_options);
+
+    cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
+
+    scaled_font = cairo_get_scaled_font (cr);
+    face = cairo_ft_scaled_font_lock_face (scaled_font);
+    {
+       charcode = FT_Get_First_Char(face, &idx);
+       while (idx && (i < NUM_GLYPHS)) {
+           glyphs[i] = (cairo_glyph_t) {idx, PAD + GRID_SIZE * (i/GRID_ROWS), PAD + TEXT_SIZE + GRID_SIZE * (i%GRID_ROWS)};
+           i++;
+           charcode = FT_Get_Next_Char(face, charcode, &idx);
+       }
+    }
+    cairo_ft_scaled_font_unlock_face (scaled_font);
+
+    cairo_show_glyphs(cr, glyphs, i);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (ft_show_glyphs_table,
+           "Test cairo_show_glyphs with cairo-ft backend and glyphs laid out in a table",
+           "ft, text", /* keywords */
+           NULL, /* requirements */
+           WIDTH, HEIGHT,
+           NULL, draw)
+
diff --git a/test/ft-text-antialias-none.c b/test/ft-text-antialias-none.c
new file mode 100644 (file)
index 0000000..64eea64
--- /dev/null
@@ -0,0 +1,146 @@
+/*
+ * Copyright © 2006 Jinghua Luo
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * JINGHUA LUO DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Jinghua Luo <sunmoon1997@gmail.com>
+ * Derived from:
+ *  text-antialias-none.c,
+ *  ft-font-create-for-ft-face.c.
+ * Original Author: Carl D. Worth <cworth@cworth.org>
+ */
+#include "cairo-test.h"
+#include <cairo-ft.h>
+
+#define WIDTH  40
+#define HEIGHT 30
+#define TEXT_SIZE 12
+
+static cairo_status_t
+create_scaled_font (cairo_t * cr,
+                   cairo_scaled_font_t **out)
+{
+    FcPattern *pattern, *resolved;
+    FcResult result;
+    cairo_font_face_t *font_face;
+    cairo_scaled_font_t *scaled_font;
+    cairo_font_options_t *font_options;
+    cairo_matrix_t font_matrix, ctm;
+    cairo_status_t status;
+    double pixel_size;
+
+    font_options = cairo_font_options_create ();
+
+    cairo_get_font_options (cr, font_options);
+
+    pattern = FcPatternCreate ();
+    if (pattern == NULL)
+       return CAIRO_STATUS_NO_MEMORY;
+
+    FcPatternAddString (pattern, FC_FAMILY, (FcChar8 *) CAIRO_TEST_FONT_FAMILY " Sans");
+    FcPatternAddDouble (pattern, FC_SIZE, TEXT_SIZE);
+    FcConfigSubstitute (NULL, pattern, FcMatchPattern);
+
+    cairo_ft_font_options_substitute (font_options, pattern);
+
+    FcDefaultSubstitute (pattern);
+    resolved = FcFontMatch (NULL, pattern, &result);
+    if (resolved == NULL) {
+       FcPatternDestroy (pattern);
+       return CAIRO_STATUS_NO_MEMORY;
+    }
+
+    /* turn antialiasing off */
+    FcPatternDel (resolved, FC_ANTIALIAS);
+    FcPatternAddBool (resolved, FC_ANTIALIAS, FcFalse);
+
+    FcPatternGetDouble (resolved, FC_PIXEL_SIZE, 0, &pixel_size);
+
+    font_face = cairo_ft_font_face_create_for_pattern (resolved);
+
+    cairo_matrix_init_identity (&font_matrix);
+    cairo_matrix_scale (&font_matrix, pixel_size, pixel_size);
+
+    cairo_get_matrix (cr, &ctm);
+
+    scaled_font = cairo_scaled_font_create (font_face,
+                                           &font_matrix,
+                                           &ctm,
+                                           font_options);
+
+    cairo_font_options_destroy (font_options);
+    cairo_font_face_destroy (font_face);
+    FcPatternDestroy (pattern);
+    FcPatternDestroy (resolved);
+
+    status = cairo_scaled_font_status (scaled_font);
+    if (status) {
+       cairo_scaled_font_destroy (scaled_font);
+       return status;
+    }
+
+    *out = scaled_font;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_text_extents_t extents;
+    cairo_scaled_font_t *scaled_font;
+    cairo_status_t status;
+    const char black[] = "black", blue[] = "blue";
+
+    /* We draw in the default black, so paint white first. */
+    cairo_save (cr);
+    cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); /* white */
+    cairo_paint (cr);
+    cairo_restore (cr);
+
+    status = create_scaled_font (cr, &scaled_font);
+    if (status) {
+       return cairo_test_status_from_status (cairo_test_get_context (cr),
+                                             status);
+    }
+
+    cairo_set_scaled_font (cr, scaled_font);
+
+    cairo_set_source_rgb (cr, 0, 0, 0); /* black */
+    cairo_text_extents (cr, black, &extents);
+    cairo_move_to (cr, -extents.x_bearing, -extents.y_bearing);
+    cairo_show_text (cr, black);
+    cairo_translate (cr, 0, -extents.y_bearing + 1);
+
+    cairo_set_source_rgb (cr, 0, 0, 1); /* blue */
+    cairo_text_extents (cr, blue, &extents);
+    cairo_move_to (cr, -extents.x_bearing, -extents.y_bearing);
+    cairo_show_text (cr, blue);
+
+    cairo_scaled_font_destroy (scaled_font);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (ft_text_antialias_none,
+           "Tests text rendering with no antialiasing",
+           "ft, text", /* keywords */
+           "target=raster", /* requirements */
+           WIDTH, HEIGHT,
+           NULL, draw)
diff --git a/test/ft-text-vertical-layout-type1.c b/test/ft-text-vertical-layout-type1.c
new file mode 100644 (file)
index 0000000..c6f26a6
--- /dev/null
@@ -0,0 +1,167 @@
+/*
+ * Copyright © 2006 Jinghua Luo
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * JINGHUA LUO DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Jinghua Luo <sunmoon1997@gmail.com>
+ * Derived from:
+ *  text-antialias-none.c,
+ *  ft-font-create-for-ft-face.c.
+ * Original Author: Carl D. Worth <cworth@cworth.org>
+ */
+#include "cairo-test.h"
+#include <cairo-ft.h>
+
+#define WIDTH  80
+#define HEIGHT 240
+#define TEXT_SIZE 30
+
+static cairo_status_t
+create_scaled_font (cairo_t * cr,
+                   cairo_scaled_font_t **out)
+{
+    FcPattern *pattern, *resolved;
+    FcResult result;
+    cairo_font_face_t *font_face;
+    cairo_scaled_font_t *scaled_font;
+    cairo_font_options_t *font_options;
+    cairo_matrix_t font_matrix, ctm;
+    cairo_status_t status;
+    double pixel_size;
+
+    font_options = cairo_font_options_create ();
+
+    cairo_get_font_options (cr, font_options);
+
+    pattern = FcPatternCreate ();
+    if (pattern == NULL)
+       return CAIRO_STATUS_NO_MEMORY;
+
+    FcPatternAddString (pattern, FC_FAMILY, (FcChar8 *)"Nimbus Sans L");
+    FcPatternAddDouble (pattern, FC_PIXEL_SIZE, TEXT_SIZE);
+    FcConfigSubstitute (NULL, pattern, FcMatchPattern);
+
+    cairo_ft_font_options_substitute (font_options, pattern);
+
+    FcDefaultSubstitute (pattern);
+    resolved = FcFontMatch (NULL, pattern, &result);
+    if (resolved == NULL) {
+       FcPatternDestroy (pattern);
+       return CAIRO_STATUS_NO_MEMORY;
+    }
+
+    /* set layout to vertical */
+    FcPatternDel (resolved, FC_VERTICAL_LAYOUT);
+    FcPatternAddBool (resolved, FC_VERTICAL_LAYOUT, FcTrue);
+
+    FcPatternGetDouble (resolved, FC_PIXEL_SIZE, 0, &pixel_size);
+
+    font_face = cairo_ft_font_face_create_for_pattern (resolved);
+
+    cairo_matrix_init_translate (&font_matrix, 10, 30);
+    cairo_matrix_rotate (&font_matrix, M_PI_2/3);
+    cairo_matrix_scale (&font_matrix, pixel_size, pixel_size);
+
+    cairo_get_matrix (cr, &ctm);
+
+    scaled_font = cairo_scaled_font_create (font_face,
+                                           &font_matrix,
+                                           &ctm,
+                                           font_options);
+
+    cairo_font_options_destroy (font_options);
+    cairo_font_face_destroy (font_face);
+    FcPatternDestroy (pattern);
+    FcPatternDestroy (resolved);
+
+    status = cairo_scaled_font_status (scaled_font);
+    if (status) {
+       cairo_scaled_font_destroy (scaled_font);
+       return status;
+    }
+
+    *out = scaled_font;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_text_extents_t extents;
+    cairo_scaled_font_t *scaled_font;
+    cairo_status_t status;
+    const char text[] = "i-W";
+    double line_width, x, y;
+
+    line_width = cairo_get_line_width (cr);
+
+    /* We draw in the default black, so paint white first. */
+    cairo_save (cr);
+    cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); /* white */
+    cairo_paint (cr);
+    cairo_restore (cr);
+
+    status = create_scaled_font (cr, &scaled_font);
+    if (status) {
+       return cairo_test_status_from_status (cairo_test_get_context (cr),
+                                             status);
+    }
+
+    cairo_set_scaled_font (cr, scaled_font);
+    cairo_scaled_font_destroy (scaled_font);
+
+    cairo_set_line_width (cr, 1.0);
+    cairo_set_source_rgb (cr, 0, 0, 0); /* black */
+    cairo_text_extents (cr, text, &extents);
+    x = width  - (extents.width  + extents.x_bearing) - 5;
+    y = height - (extents.height + extents.y_bearing) - 5;
+    cairo_move_to (cr, x, y);
+    cairo_show_text (cr, text);
+    cairo_rectangle (cr,
+                    x + extents.x_bearing - line_width / 2,
+                    y + extents.y_bearing - line_width / 2,
+                    extents.width  + line_width,
+                    extents.height + line_width);
+    cairo_stroke (cr);
+
+    cairo_set_source_rgb (cr, 0, 0, 1); /* blue */
+    cairo_text_extents (cr, text, &extents);
+    x = -extents.x_bearing + 5;
+    y = -extents.y_bearing + 5;
+    cairo_move_to (cr, x, y);
+    cairo_text_path (cr, text);
+    cairo_fill (cr);
+    cairo_rectangle (cr,
+                    x + extents.x_bearing - line_width / 2,
+                    y + extents.y_bearing - line_width / 2,
+                    extents.width  + line_width,
+                    extents.height + line_width);
+    cairo_stroke (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (ft_text_vertical_layout_type1,
+           "Tests text rendering for vertical layout with Type1 fonts"
+           "\nCan fail if an incorrect font is loaded---need to bundle the desired font",
+           "ft, fc, text", /* keywords */
+           NULL, /* requirements */
+           WIDTH, HEIGHT,
+           NULL, draw)
diff --git a/test/ft-text-vertical-layout-type3.c b/test/ft-text-vertical-layout-type3.c
new file mode 100644 (file)
index 0000000..0cea9dd
--- /dev/null
@@ -0,0 +1,166 @@
+/*
+ * Copyright © 2006 Jinghua Luo
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * JINGHUA LUO DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Jinghua Luo <sunmoon1997@gmail.com>
+ * Derived from:
+ *  text-antialias-none.c,
+ *  ft-font-create-for-ft-face.c.
+ * Original Author: Carl D. Worth <cworth@cworth.org>
+ */
+#include "cairo-test.h"
+#include <cairo-ft.h>
+
+#define WIDTH  80
+#define HEIGHT 200
+#define TEXT_SIZE 30
+
+static cairo_status_t
+create_scaled_font (cairo_t * cr,
+                   cairo_scaled_font_t **out)
+{
+    FcPattern *pattern, *resolved;
+    FcResult result;
+    cairo_font_face_t *font_face;
+    cairo_scaled_font_t *scaled_font;
+    cairo_font_options_t *font_options;
+    cairo_matrix_t font_matrix, ctm;
+    cairo_status_t status;
+    double pixel_size;
+
+    font_options = cairo_font_options_create ();
+
+    cairo_get_font_options (cr, font_options);
+
+    pattern = FcPatternCreate ();
+    if (pattern == NULL)
+       return CAIRO_STATUS_NO_MEMORY;
+
+    FcPatternAddString (pattern, FC_FAMILY, (FcChar8 *)CAIRO_TEST_FONT_FAMILY " Sans");
+    FcPatternAddDouble (pattern, FC_PIXEL_SIZE, TEXT_SIZE);
+    FcConfigSubstitute (NULL, pattern, FcMatchPattern);
+
+    cairo_ft_font_options_substitute (font_options, pattern);
+
+    FcDefaultSubstitute (pattern);
+    resolved = FcFontMatch (NULL, pattern, &result);
+    if (resolved == NULL) {
+       FcPatternDestroy (pattern);
+       return CAIRO_STATUS_NO_MEMORY;
+    }
+
+    /* set layout to vertical */
+    FcPatternDel (resolved, FC_VERTICAL_LAYOUT);
+    FcPatternAddBool (resolved, FC_VERTICAL_LAYOUT, FcTrue);
+
+    FcPatternGetDouble (resolved, FC_PIXEL_SIZE, 0, &pixel_size);
+
+    font_face = cairo_ft_font_face_create_for_pattern (resolved);
+
+    cairo_matrix_init_translate (&font_matrix, 10, 30);
+    cairo_matrix_rotate (&font_matrix, M_PI_2/3);
+    cairo_matrix_scale (&font_matrix, pixel_size, pixel_size);
+
+    cairo_get_matrix (cr, &ctm);
+
+    scaled_font = cairo_scaled_font_create (font_face,
+                                           &font_matrix,
+                                           &ctm,
+                                           font_options);
+
+    cairo_font_options_destroy (font_options);
+    cairo_font_face_destroy (font_face);
+    FcPatternDestroy (pattern);
+    FcPatternDestroy (resolved);
+
+    status = cairo_scaled_font_status (scaled_font);
+    if (status) {
+       cairo_scaled_font_destroy (scaled_font);
+       return status;
+    }
+
+    *out = scaled_font;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_text_extents_t extents;
+    cairo_scaled_font_t *scaled_font;
+    cairo_status_t status;
+    const char text[] = "i-W";
+    double line_width, x, y;
+
+    line_width = cairo_get_line_width (cr);
+
+    /* We draw in the default black, so paint white first. */
+    cairo_save (cr);
+    cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); /* white */
+    cairo_paint (cr);
+    cairo_restore (cr);
+
+    status = create_scaled_font (cr, &scaled_font);
+    if (status) {
+       return cairo_test_status_from_status (cairo_test_get_context (cr),
+                                             status);
+    }
+
+    cairo_set_scaled_font (cr, scaled_font);
+    cairo_scaled_font_destroy (scaled_font);
+
+    cairo_set_line_width (cr, 1.0);
+    cairo_set_source_rgb (cr, 0, 0, 0); /* black */
+    cairo_text_extents (cr, text, &extents);
+    x = width  - (extents.width  + extents.x_bearing) - 5;
+    y = height - (extents.height + extents.y_bearing) - 5;
+    cairo_move_to (cr, x, y);
+    cairo_show_text (cr, text);
+    cairo_rectangle (cr,
+                    x + extents.x_bearing - line_width / 2,
+                    y + extents.y_bearing - line_width / 2,
+                    extents.width  + line_width,
+                    extents.height + line_width);
+    cairo_stroke (cr);
+
+    cairo_set_source_rgb (cr, 0, 0, 1); /* blue */
+    cairo_text_extents (cr, text, &extents);
+    x = -extents.x_bearing + 5;
+    y = -extents.y_bearing + 5;
+    cairo_move_to (cr, x, y);
+    cairo_text_path (cr, text);
+    cairo_fill (cr);
+    cairo_rectangle (cr,
+                    x + extents.x_bearing - line_width / 2,
+                    y + extents.y_bearing - line_width / 2,
+                    extents.width  + line_width,
+                    extents.height + line_width);
+    cairo_stroke (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (ft_text_vertical_layout_type3,
+           "Tests text rendering for vertical layout with TrueType fonts",
+           "ft, fc, text", /* keywords */
+           NULL, /* requirements */
+           WIDTH, HEIGHT,
+           NULL, draw)
diff --git a/test/generate_refs.sh b/test/generate_refs.sh
new file mode 100755 (executable)
index 0000000..e22aa25
--- /dev/null
@@ -0,0 +1,12 @@
+#!/bin/bash
+
+# A very simple script. But sufficient, hopefully, for our current purposes.
+
+cat <<EOF
+# Note REFERENCE_IMAGES must be in lexicographical order.
+# Use generate_refs.sh on a git checkout with updated images.
+REFERENCE_IMAGES = \\
+EOF
+
+git ls-files 'reference/*.ref.png' '*.xfail.png' | sed 's/\(.*\)/      \1 \\/'
+echo ' $(NULL)'
diff --git a/test/get-and-set.c b/test/get-and-set.c
new file mode 100644 (file)
index 0000000..6a395d6
--- /dev/null
@@ -0,0 +1,159 @@
+/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/*
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ */
+
+#include "cairo-test.h"
+
+typedef struct {
+    cairo_operator_t op;
+    double tolerance;
+    cairo_fill_rule_t fill_rule;
+    double line_width;
+    cairo_line_cap_t line_cap;
+    cairo_line_join_t line_join;
+    double miter_limit;
+    cairo_matrix_t matrix;
+    double dash[5];
+    double dash_offset;
+} settings_t;
+
+/* Two sets of settings, no defaults */
+static const settings_t settings[] = {
+    {
+       CAIRO_OPERATOR_IN,
+       2.0,
+       CAIRO_FILL_RULE_EVEN_ODD,
+       7.7,
+       CAIRO_LINE_CAP_SQUARE,
+       CAIRO_LINE_JOIN_ROUND,
+       3.14,
+       {2.0, 0.0, 0.0, 2.0, 5.0, 5.0},
+       {0.1, 0.2, 0.3, 0.4, 0.5},
+       2.0
+    },
+    {
+       CAIRO_OPERATOR_ATOP,
+       5.25,
+       CAIRO_FILL_RULE_WINDING,
+       2.17,
+       CAIRO_LINE_CAP_ROUND,
+       CAIRO_LINE_JOIN_BEVEL,
+       1000.0,
+       {-3.0, 1.0, 1.0, -3.0, -4, -4},
+       {1.0, 2.0, 3.0, 4.0, 5.0},
+       3.0
+    }
+};
+
+static void
+settings_set (cairo_t *cr, const settings_t *settings)
+{
+    cairo_set_operator (cr, settings->op);
+    cairo_set_tolerance (cr, settings->tolerance);
+    cairo_set_fill_rule (cr, settings->fill_rule);
+    cairo_set_line_width (cr, settings->line_width);
+    cairo_set_line_cap (cr, settings->line_cap);
+    cairo_set_line_join (cr, settings->line_join);
+    cairo_set_miter_limit (cr, settings->miter_limit);
+    cairo_set_matrix (cr, &settings->matrix);
+    cairo_set_dash (cr, settings->dash, 5, settings->dash_offset);
+}
+
+static int
+settings_get (cairo_t *cr, settings_t *settings)
+{
+    int count;
+
+    settings->op = cairo_get_operator (cr);
+    settings->tolerance = cairo_get_tolerance (cr);
+    settings->fill_rule = cairo_get_fill_rule (cr);
+    settings->line_width = cairo_get_line_width (cr);
+    settings->line_cap = cairo_get_line_cap (cr);
+    settings->line_join = cairo_get_line_join (cr);
+    settings->miter_limit = cairo_get_miter_limit (cr);
+    cairo_get_matrix (cr, &settings->matrix);
+
+    count = cairo_get_dash_count (cr);
+    if (count != 5)
+       return -1;
+
+    cairo_get_dash (cr, settings->dash, &settings->dash_offset);
+
+    return 0;
+}
+
+static int
+settings_equal (const settings_t *a, const settings_t *b)
+{
+    return (a->op == b->op &&
+           a->tolerance == b->tolerance &&
+           a->fill_rule == b->fill_rule &&
+           a->line_width == b->line_width &&
+           a->line_cap == b->line_cap &&
+           a->line_join == b->line_join &&
+           a->miter_limit == b->miter_limit &&
+           a->matrix.xx == b->matrix.xx &&
+           a->matrix.xy == b->matrix.xy &&
+           a->matrix.x0 == b->matrix.x0 &&
+           a->matrix.yx == b->matrix.yx &&
+           a->matrix.yy == b->matrix.yy &&
+           a->matrix.y0 == b->matrix.y0 &&
+           memcmp(a->dash, b->dash, sizeof(a->dash)) == 0 &&
+           a->dash_offset == b->dash_offset);
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    settings_t check;
+
+    settings_set (cr, &settings[0]);
+
+    cairo_save (cr);
+    {
+       settings_set (cr, &settings[1]);
+       if (settings_get (cr, &check))
+           return CAIRO_TEST_FAILURE;
+
+       if (!settings_equal (&settings[1], &check))
+           return CAIRO_TEST_FAILURE;
+    }
+    cairo_restore (cr);
+
+    if (settings_get (cr, &check))
+       return CAIRO_TEST_FAILURE;
+
+    if (!settings_equal (&settings[0], &check))
+       return CAIRO_TEST_FAILURE;
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (get_and_set,
+           "Tests calls to the most trivial cairo_get and cairo_set functions",
+           "api", /* keywords */
+           NULL, /* requirements */
+           0, 0,
+           NULL, draw)
diff --git a/test/get-clip.c b/test/get-clip.c
new file mode 100644 (file)
index 0000000..f97db3f
--- /dev/null
@@ -0,0 +1,264 @@
+/*
+ * Copyright © 2006 Novell, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Novell, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Novell, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * NOVELL, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Robert O'Callahan <rocallahan@novell.com>
+ */
+
+#include "cairo-test.h"
+#include <stddef.h>
+
+static cairo_bool_t
+check_count (const cairo_test_context_t *ctx,
+            const char *message,
+             cairo_rectangle_list_t *list, int expected)
+{
+    if (list->status != CAIRO_STATUS_SUCCESS) {
+        cairo_test_log (ctx, "Error: %s; cairo_copy_clip_rectangle_list failed with \"%s\"\n",
+                        message, cairo_status_to_string(list->status));
+        return 0;
+    }
+
+    if (list->num_rectangles == expected)
+        return 1;
+    cairo_test_log (ctx, "Error: %s; expected %d rectangles, got %d\n", message,
+                    expected, list->num_rectangles);
+    return 0;
+}
+
+static cairo_bool_t
+check_unrepresentable (const cairo_test_context_t *ctx, const char *message, cairo_rectangle_list_t *list)
+{
+    if (list->status != CAIRO_STATUS_CLIP_NOT_REPRESENTABLE) {
+        cairo_test_log (ctx, "Error: %s; cairo_copy_clip_rectangle_list got unexpected result \"%s\"\n"
+                        " (we expected CAIRO_STATUS_CLIP_NOT_REPRESENTABLE)",
+                        message, cairo_status_to_string(list->status));
+        return 0;
+    }
+    return 1;
+}
+
+static cairo_bool_t
+check_rectangles_contain (const cairo_test_context_t *ctx,
+                         const char *message,
+                          cairo_rectangle_list_t *list,
+                          double x, double y, double width, double height)
+{
+    int i;
+
+    for (i = 0; i < list->num_rectangles; ++i) {
+        if (list->rectangles[i].x == x && list->rectangles[i].y == y &&
+            list->rectangles[i].width == width && list->rectangles[i].height == height)
+            return 1;
+    }
+    cairo_test_log (ctx, "Error: %s; rectangle list does not contain rectangle %f,%f,%f,%f\n",
+                    message, x, y, width, height);
+    return 0;
+}
+
+static cairo_bool_t
+check_clip_extents (const cairo_test_context_t *ctx,
+                   const char *message, cairo_t *cr,
+                    double x, double y, double width, double height)
+{
+    double ext_x1, ext_y1, ext_x2, ext_y2;
+    cairo_clip_extents (cr, &ext_x1, &ext_y1, &ext_x2, &ext_y2);
+    if (ext_x1 == x && ext_y1 == y && ext_x2 == x + width && ext_y2 == y + height)
+        return 1;
+    if (width == 0.0 && height == 0.0 && ext_x1 == ext_x2 && ext_y1 == ext_y2)
+        return 1;
+    cairo_test_log (ctx, "Error: %s; clip extents %f,%f,%f,%f should be %f,%f,%f,%f\n",
+                    message, ext_x1, ext_y1, ext_x2 - ext_x1, ext_y2 - ext_y1,
+                    x, y, width, height);
+    return 0;
+}
+
+#define SIZE 100
+
+static cairo_test_status_t
+preamble (cairo_test_context_t *ctx)
+{
+    cairo_surface_t        *surface;
+    cairo_t                *cr;
+    cairo_rectangle_list_t *rectangle_list;
+    const char             *phase;
+    cairo_bool_t            completed = 0;
+    cairo_status_t          status;
+
+    surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, SIZE, SIZE);
+    cr = cairo_create (surface);
+    cairo_surface_destroy (surface);
+
+
+    /* first, test basic stuff. This should not be clipped, it should
+       return the surface rectangle. */
+    phase = "No clip set";
+    rectangle_list = cairo_copy_clip_rectangle_list (cr);
+    if (! check_count (ctx, phase, rectangle_list, 1) ||
+        ! check_clip_extents (ctx, phase, cr, 0, 0, SIZE, SIZE) ||
+        ! check_rectangles_contain (ctx, phase, rectangle_list, 0, 0, SIZE, SIZE))
+    {
+       goto FAIL;
+    }
+    cairo_rectangle_list_destroy (rectangle_list);
+
+    /* We should get the same results after applying a clip that contains the
+       existing clip. */
+    phase = "Clip beyond surface extents";
+    cairo_save (cr);
+    cairo_rectangle (cr, -10, -10, SIZE + 20 , SIZE + 20);
+    cairo_clip (cr);
+    rectangle_list = cairo_copy_clip_rectangle_list (cr);
+    if (! check_count (ctx, phase, rectangle_list, 1) ||
+        ! check_clip_extents (ctx, phase, cr, 0, 0, SIZE, SIZE) ||
+        ! check_rectangles_contain (ctx, phase, rectangle_list, 0, 0, SIZE, SIZE))
+    {
+       goto FAIL;
+    }
+    cairo_rectangle_list_destroy (rectangle_list);
+    cairo_restore (cr);
+
+    /* Test simple clip rect. */
+    phase = "Simple clip rect";
+    cairo_save (cr);
+    cairo_rectangle (cr, 10, 10, 80, 80);
+    cairo_clip (cr);
+    rectangle_list = cairo_copy_clip_rectangle_list (cr);
+    if (! check_count (ctx, phase, rectangle_list, 1) ||
+        ! check_clip_extents (ctx, phase, cr, 10, 10, 80, 80) ||
+        ! check_rectangles_contain (ctx, phase, rectangle_list, 10, 10, 80, 80))
+    {
+       goto FAIL;
+    }
+    cairo_rectangle_list_destroy (rectangle_list);
+    cairo_restore (cr);
+
+    /* Test everything clipped out. */
+    phase = "All clipped out";
+    cairo_save (cr);
+    cairo_clip (cr);
+    rectangle_list = cairo_copy_clip_rectangle_list (cr);
+    if (! check_count (ctx, phase, rectangle_list, 0) ||
+        ! check_clip_extents (ctx, phase, cr, 0, 0, 0, 0))
+    {
+       goto FAIL;
+    }
+    cairo_rectangle_list_destroy (rectangle_list);
+    cairo_restore (cr);
+
+    /* test two clip rects */
+    phase = "Two clip rects";
+    cairo_save (cr);
+    cairo_rectangle (cr, 10, 10, 10, 10);
+    cairo_rectangle (cr, 20, 20, 10, 10);
+    cairo_clip (cr);
+    cairo_rectangle (cr, 15, 15, 10, 10);
+    cairo_clip (cr);
+    rectangle_list = cairo_copy_clip_rectangle_list (cr);
+    if (! check_count (ctx, phase, rectangle_list, 2) ||
+        ! check_clip_extents (ctx, phase, cr, 15, 15, 10, 10) ||
+        ! check_rectangles_contain (ctx, phase, rectangle_list, 15, 15, 5, 5) ||
+        ! check_rectangles_contain (ctx, phase, rectangle_list, 20, 20, 5, 5))
+    {
+       goto FAIL;
+    }
+    cairo_rectangle_list_destroy (rectangle_list);
+    cairo_restore (cr);
+
+    /* test non-rectangular clip */
+    phase = "Nonrectangular clip";
+    cairo_save (cr);
+    cairo_move_to (cr, 0, 0);
+    cairo_line_to (cr, 100, 100);
+    cairo_line_to (cr, 100, 0);
+    cairo_close_path (cr);
+    cairo_clip (cr);
+    rectangle_list = cairo_copy_clip_rectangle_list (cr);
+     /* can't get this in one tight user-space rectangle */
+    if (! check_unrepresentable (ctx, phase, rectangle_list) ||
+        ! check_clip_extents (ctx, phase, cr, 0, 0, 100, 100))
+    {
+       goto FAIL;
+    }
+    cairo_rectangle_list_destroy (rectangle_list);
+    cairo_restore (cr);
+
+    phase = "User space, simple scale, getting clip with same transform";
+    cairo_save (cr);
+    cairo_scale (cr, 2, 2);
+    cairo_rectangle (cr, 5, 5, 40, 40);
+    cairo_clip (cr);
+    rectangle_list = cairo_copy_clip_rectangle_list (cr);
+    if (! check_count (ctx, phase, rectangle_list, 1) ||
+        ! check_clip_extents (ctx, phase, cr, 5, 5, 40, 40) ||
+        ! check_rectangles_contain (ctx, phase, rectangle_list, 5, 5, 40, 40))
+    {
+       goto FAIL;
+    }
+    cairo_rectangle_list_destroy (rectangle_list);
+    cairo_restore (cr);
+
+    phase = "User space, simple scale, getting clip with no transform";
+    cairo_save (cr);
+    cairo_save (cr);
+    cairo_scale (cr, 2, 2);
+    cairo_rectangle (cr, 5, 5, 40, 40);
+    cairo_restore (cr);
+    cairo_clip (cr);
+    rectangle_list = cairo_copy_clip_rectangle_list (cr);
+    if (! check_count (ctx, phase, rectangle_list, 1) ||
+        ! check_clip_extents (ctx, phase, cr, 10, 10, 80, 80) ||
+        ! check_rectangles_contain (ctx, phase, rectangle_list, 10, 10, 80, 80))
+    {
+       goto FAIL;
+    }
+    cairo_rectangle_list_destroy (rectangle_list);
+    cairo_restore (cr);
+
+    phase = "User space, rotation, getting clip with no transform";
+    cairo_save (cr);
+    cairo_save (cr);
+    cairo_rotate (cr, 12);
+    cairo_rectangle (cr, 5, 5, 40, 40);
+    cairo_restore (cr);
+    cairo_clip (cr);
+    rectangle_list = cairo_copy_clip_rectangle_list (cr);
+    if (! check_unrepresentable (ctx, phase, rectangle_list))
+       goto FAIL;
+
+    completed = 1;
+FAIL:
+    cairo_rectangle_list_destroy (rectangle_list);
+    status = cairo_status (cr);
+    cairo_destroy (cr);
+
+    if (!completed)
+        return CAIRO_TEST_FAILURE;
+
+    return cairo_test_status_from_status (ctx, status);
+}
+
+CAIRO_TEST (get_clip,
+           "Test cairo_copy_clip_rectangle_list and cairo_clip_extents",
+           "clip, extents", /* keywords */
+           NULL, /* requirements */
+           0, 0,
+           preamble, NULL)
diff --git a/test/get-group-target.c b/test/get-group-target.c
new file mode 100644 (file)
index 0000000..5aff19b
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * Copyright © 2006 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ */
+
+#include "cairo-test.h"
+
+#define SIZE 8
+#define PAD 2
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_surface_t *group;
+    double x, y;
+
+    /* First paint background in blue. */
+    cairo_set_source_rgb (cr, 0.0, 0.0, 1.0);
+    cairo_paint (cr);
+
+    /* Then clip so that the group surface ends up smaller than the
+     * original surface. */
+    cairo_rectangle (cr, PAD, PAD, width - 2 * PAD, height - 2 * PAD);
+    cairo_clip (cr);
+
+    /* Paint the clipped region in red (which should all be overwritten later). */
+    cairo_set_source_rgb (cr, 1.0, 0.0, 0.0);
+    cairo_paint (cr);
+
+    /* Redirect to a new group and get that surface. */
+    cairo_push_group (cr);
+    group = cairo_get_group_target (cr);
+
+    /* Then paint in green what we query the group surface size to be. */
+    cairo_set_source_rgb (cr, 0.0, 1.0, 0.0);
+    cairo_surface_get_device_offset (group, &x, &y);
+    /* Or rather, we calculate the group surface size based on the
+     * only thing we can query which is the device offset. Ideally,
+     * the size would always be the minimal (width - 2 * PAD, height -
+     * 2 * PAD) based on the clip. But currently, group targets are
+     * created oversized for paginated surfaces, so we only subtract
+     * anything from the size if there is a non-zero device offfset.
+     *
+     * The calculation below might also be less confusing if the sign
+     * convention on the device offset were reversed, but it is what
+     * it is. Oh well. */
+    cairo_rectangle (cr,
+                    -x, -y,
+                    width + 2 * x,
+                    height + 2 * y);
+    cairo_fill (cr);
+
+    /* Finish up the group painting. */
+    cairo_pop_group_to_source (cr);
+    cairo_paint (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (get_group_target,
+           "Test of both cairo_get_group_target and cairo_surface_get_device_offset",
+           "api", /* keywords */
+           NULL, /* requirements */
+           SIZE, SIZE,
+           NULL, draw)
diff --git a/test/get-path-extents.c b/test/get-path-extents.c
new file mode 100644 (file)
index 0000000..4123ddb
--- /dev/null
@@ -0,0 +1,423 @@
+/*
+ * Copyright © 2006 Novell, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Novell, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Novell, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * NOVELL, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Robert O'Callahan <rocallahan@novell.com>
+ */
+
+#include "cairo-test.h"
+#include <stddef.h>
+#include <math.h>
+
+enum ExtentsType { FILL, STROKE, PATH };
+
+enum Relation { EQUALS, APPROX_EQUALS, CONTAINS };
+
+static cairo_bool_t
+check_extents (const cairo_test_context_t *ctx,
+              const char *message, cairo_t *cr, enum ExtentsType type,
+               enum Relation relation,
+               double x, double y, double width, double height)
+{
+    double ext_x1, ext_y1, ext_x2, ext_y2;
+    const char *type_string;
+    const char *relation_string;
+
+    switch (type) {
+    default:
+    case FILL:
+        type_string = "fill";
+        cairo_fill_extents (cr, &ext_x1, &ext_y1, &ext_x2, &ext_y2);
+        break;
+    case STROKE:
+        type_string = "stroke";
+        cairo_stroke_extents (cr, &ext_x1, &ext_y1, &ext_x2, &ext_y2);
+        break;
+    case PATH:
+        type_string = "path";
+        cairo_path_extents (cr, &ext_x1, &ext_y1, &ext_x2, &ext_y2);
+        break;
+    }
+
+    /* ignore results after an error occurs */
+    if (cairo_status (cr))
+       return 1;
+
+    switch (relation) {
+    default:
+    case EQUALS:
+        relation_string = "equal";
+        if (ext_x1 == x && ext_y1 == y && ext_x2 == x + width && ext_y2 == y + height)
+            return 1;
+        break;
+    case APPROX_EQUALS:
+        relation_string = "approx. equal";
+        if (fabs (ext_x1 - x) < 1. &&
+           fabs (ext_y1 - y) < 1. &&
+           fabs (ext_x2 - (x + width))  < 1. &&
+           fabs (ext_y2 - (y + height)) < 1.)
+       {
+            return 1;
+       }
+        break;
+    case CONTAINS:
+        relation_string = "contain";
+        if (width == 0 || height == 0) {
+            /* odd test that doesn't really test anything... */
+            return 1;
+        }
+        if (ext_x1 <= x && ext_y1 <= y && ext_x2 >= x + width && ext_y2 >= y + height)
+            return 1;
+        break;
+    }
+
+    cairo_test_log (ctx, "Error: %s; %s extents (%g, %g) x (%g, %g) should %s (%g, %g) x (%g, %g)\n",
+                    message, type_string,
+                    ext_x1, ext_y1, ext_x2 - ext_x1, ext_y2 - ext_y1,
+                    relation_string,
+                    x, y, width, height);
+    return 0;
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    const cairo_test_context_t *ctx = cairo_test_get_context (cr);
+    cairo_surface_t *surface;
+    cairo_t         *cr2;
+    const char      *phase;
+    const char      string[] = "The quick brown fox jumps over the lazy dog.";
+    cairo_text_extents_t extents, scaled_font_extents;
+    cairo_status_t   status;
+    int              errors = 0;
+
+    surface = cairo_surface_create_similar (cairo_get_group_target (cr),
+                                            CAIRO_CONTENT_COLOR, 100, 100);
+    /* don't use cr accidentally */
+    cr = NULL;
+    cr2 = cairo_create (surface);
+    cairo_surface_destroy (surface);
+
+    cairo_set_line_width (cr2, 10);
+    cairo_set_line_join (cr2, CAIRO_LINE_JOIN_MITER);
+    cairo_set_miter_limit (cr2, 100);
+
+    phase = "No path";
+    errors += !check_extents (ctx, phase, cr2, FILL, EQUALS, 0, 0, 0, 0);
+    errors += !check_extents (ctx, phase, cr2, STROKE, EQUALS, 0, 0, 0, 0);
+    errors += !check_extents (ctx, phase, cr2, PATH, EQUALS, 0, 0, 0, 0);
+
+    cairo_save (cr2);
+
+    cairo_new_path (cr2);
+    cairo_move_to (cr2, 200, 400);
+    cairo_rel_line_to (cr2, 0., 0.);
+    phase = "Degenerate line";
+    errors += !check_extents (ctx, phase, cr2, FILL, EQUALS, 0, 0, 0, 0);
+    errors += !check_extents (ctx, phase, cr2, STROKE, EQUALS, 0, 0, 0, 0);
+    errors += !check_extents (ctx, phase, cr2, PATH, EQUALS, 200, 400, 0, 0);
+
+    cairo_new_path (cr2);
+    cairo_move_to (cr2, 200, 400);
+    cairo_rel_curve_to (cr2, 0., 0., 0., 0., 0., 0.);
+    phase = "Degenerate curve";
+    errors += !check_extents (ctx, phase, cr2, FILL, EQUALS, 0, 0, 0, 0);
+    errors += !check_extents (ctx, phase, cr2, STROKE, EQUALS, 0, 0, 0, 0);
+    errors += !check_extents (ctx, phase, cr2, PATH, EQUALS, 200, 400, 0, 0);
+
+    cairo_new_path (cr2);
+    cairo_arc (cr2, 200, 400, 0., 0, 2 * M_PI);
+    phase = "Degenerate arc (R=0)";
+    errors += !check_extents (ctx, phase, cr2, FILL, EQUALS, 0, 0, 0, 0);
+    errors += !check_extents (ctx, phase, cr2, STROKE, EQUALS, 0, 0, 0, 0);
+    errors += !check_extents (ctx, phase, cr2, PATH, EQUALS, 200, 400, 0, 0);
+
+    cairo_new_path (cr2);
+    cairo_arc_negative (cr2, 200, 400, 0., 0, 2 * M_PI);
+    phase = "Degenerate negative arc (R=0)";
+    errors += !check_extents (ctx, phase, cr2, FILL, EQUALS, 0, 0, 0, 0);
+    errors += !check_extents (ctx, phase, cr2, STROKE, EQUALS, 0, 0, 0, 0);
+    errors += !check_extents (ctx, phase, cr2, PATH, EQUALS, 200, 400, 0, 0);
+
+    cairo_new_path (cr2);
+    cairo_arc (cr2, 200, 400, 10., 0, 0);
+    phase = "Degenerate arc (Θ=0)";
+    errors += !check_extents (ctx, phase, cr2, FILL, EQUALS, 0, 0, 0, 0);
+    errors += !check_extents (ctx, phase, cr2, STROKE, EQUALS, 0, 0, 0, 0);
+    errors += !check_extents (ctx, phase, cr2, PATH, EQUALS, 210, 400, 0, 0);
+
+    cairo_new_path (cr2);
+    cairo_arc_negative (cr2, 200, 400, 10., 0, 0);
+    phase = "Degenerate negative arc (Θ=0)";
+    errors += !check_extents (ctx, phase, cr2, FILL, EQUALS, 0, 0, 0, 0);
+    errors += !check_extents (ctx, phase, cr2, STROKE, EQUALS, 0, 0, 0, 0);
+    errors += !check_extents (ctx, phase, cr2, PATH, EQUALS, 210, 400, 0, 0);
+
+    cairo_new_path (cr2);
+    cairo_restore (cr2);
+
+    /* Test that with CAIRO_LINE_CAP_ROUND, we get "dots" from
+     * cairo_move_to; cairo_rel_line_to(0,0) */
+    cairo_save (cr2);
+
+    cairo_set_line_cap (cr2, CAIRO_LINE_CAP_ROUND);
+    cairo_set_line_width (cr2, 20);
+
+    cairo_move_to (cr2, 200, 400);
+    cairo_rel_line_to (cr2, 0, 0);
+    phase = "Single 'dot'";
+    errors += !check_extents (ctx, phase, cr2, FILL, EQUALS, 0, 0, 0, 0);
+    errors += !check_extents (ctx, phase, cr2, STROKE, EQUALS, 190, 390, 20, 20);
+    errors += !check_extents (ctx, phase, cr2, PATH, EQUALS, 200, 400, 0, 0);
+
+    /* Add another dot without starting a new path */
+    cairo_move_to (cr2, 100, 500);
+    cairo_rel_line_to (cr2, 0, 0);
+    phase = "Multiple 'dots'";
+    errors += !check_extents (ctx, phase, cr2, FILL, EQUALS, 0, 0, 0, 0);
+    errors += !check_extents (ctx, phase, cr2, STROKE, EQUALS, 90, 390, 120, 120);
+    errors += !check_extents (ctx, phase, cr2, PATH, EQUALS, 100, 400, 100, 100);
+
+    cairo_new_path (cr2);
+
+    cairo_restore (cr2);
+
+    /* http://bugs.freedesktop.org/show_bug.cgi?id=7965 */
+    phase = "A horizontal, open path";
+    cairo_save (cr2);
+    cairo_set_line_cap (cr2, CAIRO_LINE_CAP_ROUND);
+    cairo_set_line_join (cr2, CAIRO_LINE_JOIN_ROUND);
+    cairo_move_to (cr2, 0, 180);
+    cairo_line_to (cr2, 750, 180);
+    errors += !check_extents (ctx, phase, cr2, FILL, EQUALS, 0, 0, 0, 0);
+    errors += !check_extents (ctx, phase, cr2, STROKE, EQUALS, -5, 175, 760, 10);
+    errors += !check_extents (ctx, phase, cr2, PATH, EQUALS, 0, 180, 750, 0);
+    cairo_new_path (cr2);
+    cairo_restore (cr2);
+
+    phase = "A vertical, open path";
+    cairo_save (cr2);
+    cairo_set_line_cap (cr2, CAIRO_LINE_CAP_ROUND);
+    cairo_set_line_join (cr2, CAIRO_LINE_JOIN_ROUND);
+    cairo_new_path (cr2);
+    cairo_move_to (cr2, 180, 0);
+    cairo_line_to (cr2, 180, 750);
+    errors += !check_extents (ctx, phase, cr2, FILL, EQUALS, 0, 0, 0, 0);
+    errors += !check_extents (ctx, phase, cr2, STROKE, EQUALS, 175, -5, 10, 760);
+    errors += !check_extents (ctx, phase, cr2, PATH, EQUALS, 180, 0, 0, 750);
+    cairo_new_path (cr2);
+    cairo_restore (cr2);
+
+    phase = "A degenerate open path";
+    cairo_save (cr2);
+    cairo_set_line_cap (cr2, CAIRO_LINE_CAP_ROUND);
+    cairo_set_line_join (cr2, CAIRO_LINE_JOIN_ROUND);
+    cairo_new_path (cr2);
+    cairo_move_to (cr2, 180, 0);
+    cairo_line_to (cr2, 180, 0);
+    errors += !check_extents (ctx, phase, cr2, FILL, EQUALS, 0, 0, 0, 0);
+    errors += !check_extents (ctx, phase, cr2, STROKE, EQUALS, 175, -5, 10, 10);
+    errors += !check_extents (ctx, phase, cr2, PATH, EQUALS, 180, 0, 0, 0);
+    cairo_new_path (cr2);
+    cairo_restore (cr2);
+
+    phase = "Simple rect";
+    cairo_save (cr2);
+    cairo_rectangle (cr2, 10, 10, 80, 80);
+    errors += !check_extents (ctx, phase, cr2, FILL, EQUALS, 10, 10, 80, 80);
+    errors += !check_extents (ctx, phase, cr2, STROKE, EQUALS, 5, 5, 90, 90);
+    errors += !check_extents (ctx, phase, cr2, PATH, EQUALS, 10, 10, 80, 80);
+    cairo_new_path (cr2);
+    cairo_restore (cr2);
+
+    phase = "Two rects";
+    cairo_save (cr2);
+    cairo_rectangle (cr2, 10, 10, 10, 10);
+    cairo_rectangle (cr2, 20, 20, 10, 10);
+    errors += !check_extents (ctx, phase, cr2, FILL, EQUALS, 10, 10, 20, 20);
+    errors += !check_extents (ctx, phase, cr2, STROKE, EQUALS, 5, 5, 30, 30);
+    errors += !check_extents (ctx, phase, cr2, PATH, EQUALS, 10, 10, 20, 20);
+    cairo_new_path (cr2);
+    cairo_restore (cr2);
+
+    phase = "Triangle";
+    cairo_save (cr2);
+    cairo_move_to (cr2, 10, 10);
+    cairo_line_to (cr2, 90, 90);
+    cairo_line_to (cr2, 90, 10);
+    cairo_close_path (cr2);
+    /* miter joins protrude 5*(1+sqrt(2)) above the top-left corner and to
+       the right of the bottom-right corner */
+    errors += !check_extents (ctx, phase, cr2, FILL, EQUALS, 10, 10, 80, 80);
+    errors += !check_extents (ctx, phase, cr2, STROKE, CONTAINS, 0, 5, 95, 95);
+    errors += !check_extents (ctx, phase, cr2, PATH, CONTAINS, 10, 10, 80, 80);
+    cairo_new_path (cr2);
+    cairo_restore (cr2);
+
+    cairo_save (cr2);
+
+    cairo_set_line_width (cr2, 4);
+
+    cairo_rectangle (cr2, 10, 10, 30, 30);
+    cairo_rectangle (cr2, 25, 10, 15, 30);
+
+    cairo_set_fill_rule (cr2, CAIRO_FILL_RULE_EVEN_ODD);
+    phase = "EVEN_ODD overlapping rectangles";
+    errors += !check_extents (ctx, phase, cr2, FILL, EQUALS, 10, 10, 15, 30);
+    errors += !check_extents (ctx, phase, cr2, STROKE, EQUALS, 8, 8, 34, 34);
+    errors += !check_extents (ctx, phase, cr2, PATH, EQUALS, 10, 10, 30, 30);
+
+    /* Test other fill rule with the same path. */
+
+    cairo_set_fill_rule (cr2, CAIRO_FILL_RULE_WINDING);
+    phase = "WINDING overlapping rectangles";
+    errors += !check_extents (ctx, phase, cr2, FILL, EQUALS, 10, 10, 30, 30);
+    errors += !check_extents (ctx, phase, cr2, STROKE, EQUALS, 8, 8, 34, 34);
+    errors += !check_extents (ctx, phase, cr2, PATH, EQUALS, 10, 10, 30, 30);
+
+    /* Now, change the direction of the second rectangle and test both
+     * fill rules again. */
+    cairo_new_path (cr2);
+    cairo_rectangle (cr2, 10, 10, 30, 30);
+    cairo_rectangle (cr2, 25, 40, 15, -30);
+
+    cairo_set_fill_rule (cr2, CAIRO_FILL_RULE_EVEN_ODD);
+    phase = "EVEN_ODD overlapping rectangles";
+    errors += !check_extents (ctx, phase, cr2, FILL, EQUALS, 10, 10, 15, 30);
+    errors += !check_extents (ctx, phase, cr2, STROKE, EQUALS, 8, 8, 34, 34);
+    errors += !check_extents (ctx, phase, cr2, PATH, EQUALS, 10, 10, 30, 30);
+
+    /* Test other fill rule with the same path. */
+
+    cairo_set_fill_rule (cr2, CAIRO_FILL_RULE_WINDING);
+    phase = "WINDING overlapping rectangles";
+    errors += !check_extents (ctx, phase, cr2, FILL, EQUALS, 10, 10, 15, 30);
+    errors += !check_extents (ctx, phase, cr2, STROKE, EQUALS, 8, 8, 34, 34);
+    errors += !check_extents (ctx, phase, cr2, PATH, EQUALS, 10, 10, 30, 30);
+
+    cairo_new_path (cr2);
+
+    cairo_restore (cr2);
+
+    /* http://bugs.freedesktop.org/show_bug.cgi?id=7245 */
+    phase = "Arc";
+    cairo_save (cr2);
+    cairo_arc (cr2, 250.0, 250.0, 157.0, 5.147, 3.432);
+    cairo_set_line_width (cr2, 154.0);
+    errors += !check_extents (ctx, phase, cr2, STROKE, APPROX_EQUALS, 16, 38, 468, 446);
+    cairo_new_path (cr2);
+    cairo_restore (cr2);
+
+    phase = "Text";
+    cairo_save (cr2);
+    cairo_select_font_face (cr2, CAIRO_TEST_FONT_FAMILY " Sans",
+                           CAIRO_FONT_SLANT_NORMAL,
+                           CAIRO_FONT_WEIGHT_NORMAL);
+    cairo_set_font_size (cr2, 12);
+    cairo_text_extents (cr2, string, &extents);
+    /* double check that the two methods of measuring the text agree... */
+    cairo_scaled_font_text_extents (cairo_get_scaled_font (cr2),
+                                   string,
+                                   &scaled_font_extents);
+    if (memcmp (&extents, &scaled_font_extents, sizeof (extents))) {
+       cairo_test_log (ctx, "Error: cairo_text_extents() does not match cairo_scaled_font_text_extents() - font extents (%f, %f) x (%f, %f) should be (%f, %f) x (%f, %f)\n",
+                       scaled_font_extents.x_bearing,
+                       scaled_font_extents.y_bearing,
+                       scaled_font_extents.width,
+                       scaled_font_extents.height,
+                       extents.x_bearing,
+                       extents.y_bearing,
+                       extents.width,
+                       extents.height);
+       errors++;
+    }
+
+    cairo_move_to (cr2, -extents.x_bearing, -extents.y_bearing);
+    cairo_text_path (cr2, string);
+    cairo_set_line_width (cr2, 2.0);
+    /* XXX: We'd like to be able to use EQUALS here, but currently
+     * when hinting is enabled freetype returns integer extents. See
+     * http://cairographics.org/todo */
+    errors += !check_extents (ctx, phase, cr2, FILL, APPROX_EQUALS,
+                             0, 0, extents.width, extents.height);
+    errors += !check_extents (ctx, phase, cr2, STROKE, APPROX_EQUALS,
+                             -1, -1, extents.width+2, extents.height+2);
+    errors += !check_extents (ctx, phase, cr2, PATH, APPROX_EQUALS,
+                             0, 0, extents.width, extents.height);
+    cairo_new_path (cr2);
+    cairo_restore (cr2);
+
+    phase = "User space, simple scale, getting extents with same transform";
+    cairo_save (cr2);
+    cairo_scale (cr2, 2, 2);
+    cairo_rectangle (cr2, 5, 5, 40, 40);
+    errors += !check_extents (ctx, phase, cr2, FILL, EQUALS, 5, 5, 40, 40);
+    errors += !check_extents (ctx, phase, cr2, STROKE, EQUALS, 0, 0, 50, 50);
+    errors += !check_extents (ctx, phase, cr2, PATH, EQUALS, 5, 5, 40, 40);
+    cairo_new_path (cr2);
+    cairo_restore (cr2);
+
+    phase = "User space, simple scale, getting extents with no transform";
+    cairo_save (cr2);
+    cairo_save (cr2);
+    cairo_scale (cr2, 2, 2);
+    cairo_rectangle (cr2, 5, 5, 40, 40);
+    cairo_restore (cr2);
+    errors += !check_extents (ctx, phase, cr2, FILL, EQUALS, 10, 10, 80, 80);
+    errors += !check_extents (ctx, phase, cr2, STROKE, EQUALS, 5, 5, 90, 90);
+    errors += !check_extents (ctx, phase, cr2, PATH, EQUALS, 10, 10, 80, 80);
+    cairo_new_path (cr2);
+    cairo_restore (cr2);
+
+    phase = "User space, rotation, getting extents with transform";
+    cairo_save (cr2);
+    cairo_rectangle (cr2, -50, -50, 50, 50);
+    cairo_rotate (cr2, -M_PI/4);
+    /* the path in user space is now (nearly) the square rotated by
+       45 degrees about the origin. Thus its x1 and x2 are both nearly 0.
+       This should show any bugs where we just transform device-space
+       x1,y1 and x2,y2 to get the extents. */
+    /* The largest axis-aligned square inside the rotated path has
+       side lengths 50*sqrt(2), so a bit over 35 on either side of
+       the axes. With the stroke width added to the rotated path,
+       the largest axis-aligned square is a bit over 38 on either side of
+       the axes. */
+    errors += !check_extents (ctx, phase, cr2, FILL, CONTAINS, -35, -35, 35, 35);
+    errors += !check_extents (ctx, phase, cr2, STROKE, CONTAINS, -38, -38, 38, 38);
+    errors += !check_extents (ctx, phase, cr2, PATH, CONTAINS, -35, -35, 35, 35);
+    cairo_new_path (cr2);
+    cairo_restore (cr2);
+
+    status = cairo_status (cr2);
+    cairo_destroy (cr2);
+
+    if (status)
+       return cairo_test_status_from_status (ctx, status);
+
+    return errors == 0 ? CAIRO_TEST_SUCCESS : CAIRO_TEST_FAILURE;
+}
+
+CAIRO_TEST (get_path_extents,
+           "Test cairo_fill_extents and cairo_stroke_extents",
+           "extents, path", /* keywords */
+           NULL, /* requirements */
+           0, 0,
+           NULL, draw)
diff --git a/test/get-xrender-format.c b/test/get-xrender-format.c
new file mode 100644 (file)
index 0000000..8e99eb7
--- /dev/null
@@ -0,0 +1,132 @@
+/*
+ * Copyright © 2008 Red Hat, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ */
+
+#include "cairo-test.h"
+
+#include "cairo-xlib.h"
+#include "cairo-xlib-xrender.h"
+
+#include "cairo-boilerplate-xlib.h"
+
+static cairo_test_status_t
+preamble (cairo_test_context_t *ctx)
+{
+    Display *dpy;
+    XRenderPictFormat *orig_format, *format;
+    cairo_surface_t *surface;
+    Pixmap pixmap;
+    int screen;
+    cairo_test_status_t result;
+
+    result = CAIRO_TEST_UNTESTED;
+
+    if (! cairo_test_is_target_enabled (ctx, "xlib"))
+       goto CLEANUP_TEST;
+
+    dpy = XOpenDisplay (NULL);
+    if (! dpy) {
+       cairo_test_log (ctx, "Error: Cannot open display: %s, skipping.\n",
+                       XDisplayName (NULL));
+       goto CLEANUP_TEST;
+    }
+
+    result = CAIRO_TEST_FAILURE;
+
+    screen = DefaultScreen (dpy);
+
+    cairo_test_log (ctx, "Testing with image surface.\n");
+
+    surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 1, 1);
+
+    format = cairo_xlib_surface_get_xrender_format (surface);
+    if (format != NULL) {
+       cairo_test_log (ctx, "Error: expected NULL for image surface\n");
+       goto CLEANUP_SURFACE;
+    }
+
+    cairo_surface_destroy (surface);
+
+    cairo_test_log (ctx, "Testing with non-xrender xlib surface.\n");
+
+    pixmap = XCreatePixmap (dpy, DefaultRootWindow (dpy),
+                           1, 1, DefaultDepth (dpy, screen));
+    surface = cairo_xlib_surface_create (dpy, pixmap,
+                                        DefaultVisual (dpy, screen),
+                                        1, 1);
+    orig_format = XRenderFindVisualFormat (dpy, DefaultVisual (dpy, screen));
+    format = cairo_xlib_surface_get_xrender_format (surface);
+    if (format != orig_format) {
+       cairo_test_log (ctx, "Error: did not receive the same format as XRenderFindVisualFormat\n");
+       goto CLEANUP_PIXMAP;
+    }
+    cairo_surface_destroy (surface);
+    XFreePixmap (dpy, pixmap);
+
+    cairo_test_log (ctx, "Testing with xlib xrender surface.\n");
+
+    orig_format = XRenderFindStandardFormat (dpy, PictStandardARGB32);
+    pixmap = XCreatePixmap (dpy, DefaultRootWindow (dpy),
+                           1, 1, 32);
+    surface = cairo_xlib_surface_create_with_xrender_format (dpy,
+                                                            pixmap,
+                                                            DefaultScreenOfDisplay (dpy),
+                                                            orig_format,
+                                                            1, 1);
+    format = cairo_xlib_surface_get_xrender_format (surface);
+    if (format != orig_format) {
+       cairo_test_log (ctx, "Error: did not receive the same format originally set\n");
+       goto CLEANUP_PIXMAP;
+    }
+
+    cairo_test_log (ctx, "Testing without the X Render extension.\n");
+
+    cairo_boilerplate_xlib_surface_disable_render (surface);
+
+    format = cairo_xlib_surface_get_xrender_format (surface);
+    if (format != NULL) {
+       cairo_test_log (ctx, "Error: did not receive a NULL format as expected\n");
+       goto CLEANUP_PIXMAP;
+    }
+
+    result = CAIRO_TEST_SUCCESS;
+
+  CLEANUP_PIXMAP:
+    XFreePixmap (dpy, pixmap);
+  CLEANUP_SURFACE:
+    cairo_surface_destroy (surface);
+
+    XCloseDisplay (dpy);
+
+  CLEANUP_TEST:
+    return result;
+}
+
+CAIRO_TEST (get_xrender_format,
+           "Check XRender specific API",
+           "xrender, api", /* keywords */
+           NULL, /* requirements */
+           0, 0,
+           preamble, NULL)
diff --git a/test/gl-surface-source.c b/test/gl-surface-source.c
new file mode 100644 (file)
index 0000000..09d4d9c
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * Copyright © 2008 Chris Wilson
+ * Copyright © 2010 Intel Corporation
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Chris Wilson not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Chris Wilson makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * CHRIS WILSON DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL CHRIS WILSON BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+#include <cairo-gl.h>
+
+#include "surface-source.c"
+
+struct closure {
+    Display *dpy;
+    GLXContext ctx;
+};
+
+static void
+cleanup (void *data)
+{
+    struct closure *arg = data;
+
+    glXDestroyContext (arg->dpy, arg->ctx);
+    XCloseDisplay (arg->dpy);
+
+    free (arg);
+}
+
+static cairo_surface_t *
+create_source_surface (int size)
+{
+    int rgba_attribs[] = {
+       GLX_RGBA,
+       GLX_RED_SIZE, 1,
+       GLX_GREEN_SIZE, 1,
+       GLX_BLUE_SIZE, 1,
+       GLX_ALPHA_SIZE, 1,
+       GLX_DOUBLEBUFFER,
+       None
+    };
+    XVisualInfo *visinfo;
+    GLXContext ctx;
+    struct closure *arg;
+    cairo_device_t *device;
+    cairo_surface_t *surface;
+    Display *dpy;
+
+    dpy = XOpenDisplay (NULL);
+    if (dpy == NULL)
+       return NULL;
+
+    visinfo = glXChooseVisual (dpy, DefaultScreen (dpy), rgba_attribs);
+    if (visinfo == NULL) {
+       XCloseDisplay (dpy);
+       return NULL;
+    }
+
+    ctx = glXCreateContext (dpy, visinfo, NULL, True);
+    XFree (visinfo);
+
+    if (ctx == NULL) {
+       XCloseDisplay (dpy);
+       return NULL;
+    }
+
+    arg = xmalloc (sizeof (struct closure));
+    arg->dpy = dpy;
+    arg->ctx = ctx;
+    device = cairo_glx_device_create (dpy, ctx);
+    if (cairo_device_set_user_data (device,
+                                   (cairo_user_data_key_t *) cleanup,
+                                   arg,
+                                   cleanup))
+    {
+       cleanup (arg);
+       return NULL;
+    }
+
+    surface = cairo_gl_surface_create (device,
+                                      CAIRO_CONTENT_COLOR_ALPHA,
+                                      size, size);
+    cairo_device_destroy (device);
+
+    return surface;
+}
+
+CAIRO_TEST (gl_surface_source,
+           "Test using a GL surface as the source",
+           "source", /* keywords */
+           NULL, /* requirements */
+           SIZE, SIZE,
+           preamble, draw)
diff --git a/test/glyph-cache-pressure.c b/test/glyph-cache-pressure.c
new file mode 100644 (file)
index 0000000..770b39f
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ */
+
+#include "cairo-test.h"
+#include "cairo-boilerplate-scaled-font.h"
+
+#define TEXT_SIZE 12
+
+/* Bug history
+ *
+ * 2006-06-22  Carl Worth  <cworth@cworth.org>
+ *
+ *   This is a test case to demonstrate the following bug in the xlib backend:
+ *
+ *     Some characters aren't displayed when using xlib (cache usage missing freeze/thaw)
+ *     https://bugs.freedesktop.org/show_bug.cgi?id=6955
+ *
+ *   We replicate this bug by using the cairo_scaled_font_set_max_glyphs_per_font
+ *   function to artifically induce cache pressure. (This function was added
+ *   for this very purpose.)
+ *
+ * 2006-06-22  Carl Worth  <cworth@cworth.org>
+ *
+ *   Bug was simple enough to solve by just adding a freeze/thaw pair
+ *   around the scaled_font's glyph cache in
+ *   _cairo_xlib_surface_show_glyphs, (I went ahead and added
+ *   _cairo_sacled_font_freeze/thaw_cache functions for this).
+ */
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    /* We draw in the default black, so paint white first. */
+    cairo_save (cr);
+    cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); /* white */
+    cairo_paint (cr);
+    cairo_restore (cr);
+
+    cairo_select_font_face (cr, CAIRO_TEST_FONT_FAMILY " Sans",
+                           CAIRO_FONT_SLANT_NORMAL,
+                           CAIRO_FONT_WEIGHT_NORMAL);
+    cairo_set_font_size (cr, TEXT_SIZE);
+
+    cairo_set_source_rgb (cr, 0, 0, 0); /* black */
+
+    cairo_boilerplate_scaled_font_set_max_glyphs_cached (cairo_get_scaled_font (cr), 1);
+
+    cairo_move_to (cr, 1, TEXT_SIZE);
+    cairo_show_text (cr, "the five boxing wizards jump quickly");
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (glyph_cache_pressure,
+           "Ensure that all backends behave well under artificial glyph cache pressure",
+           "stress", /* keywords */
+           NULL, /* requirements */
+           223, TEXT_SIZE + 4,
+           NULL, draw)
diff --git a/test/gradient-alpha.c b/test/gradient-alpha.c
new file mode 100644 (file)
index 0000000..24cd324
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ */
+
+#include "cairo-test.h"
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_pattern_t *gradient;
+
+    gradient = cairo_pattern_create_linear (0, -height,
+                                           0, height);
+    cairo_pattern_add_color_stop_rgba (gradient, 0.0,
+                                      1.0, 0.0, 0.0,
+                                      1.0);
+    cairo_pattern_add_color_stop_rgba (gradient, 1.0,
+                                      0.0, 0.0, 1.0,
+                                      0.5);
+
+    cairo_set_source (cr, gradient);
+
+    cairo_paint (cr);
+
+    cairo_pattern_destroy (gradient);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (gradient_alpha,
+           "Tests drawing of a gradient with various alpha values in the color stops",
+           "gradient, alpha", /* keywords */
+           NULL, /* requirements */
+           10, 10,
+           NULL, draw)
diff --git a/test/gradient-constant-alpha.c b/test/gradient-constant-alpha.c
new file mode 100644 (file)
index 0000000..7640b6e
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Copyright © 2005 Red Hat, Inc.
+ * Copyright © 2008 Chris Wilson
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ *         Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_pattern_t *gradient;
+
+    gradient = cairo_pattern_create_linear (0, 0,
+                                           0, height);
+    cairo_pattern_add_color_stop_rgba (gradient, 0.0,
+                                      1.0, 0.0, 0.0,
+                                      0.5);
+    cairo_pattern_add_color_stop_rgba (gradient, 0.0,
+                                      0.0, 1.0, 0.0,
+                                      0.5);
+    cairo_pattern_add_color_stop_rgba (gradient, 1.0,
+                                      0.0, 0.0, 1.0,
+                                      0.5);
+
+    cairo_set_source (cr, gradient);
+
+    cairo_paint (cr);
+
+    cairo_pattern_destroy (gradient);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (gradient_constant_alpha,
+           "Tests drawing of a gradient with constant alpha values in the color stops",
+           "gradient, alpha", /* keywords */
+           NULL,
+           10, 10,
+           NULL, draw)
diff --git a/test/gradient-zero-stops-mask.c b/test/gradient-zero-stops-mask.c
new file mode 100644 (file)
index 0000000..b2a10ec
--- /dev/null
@@ -0,0 +1,59 @@
+/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/*
+ * Copyright © 2007 Brian Ewins
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Brian Ewins <Brian.Ewins@gmail.com>
+ * Contributor(s):
+ *      Andrea Canciani <ranma42@gmail.com>
+ */
+
+#include "cairo-test.h"
+
+/* This test case is designed to exercise the opaque test for
+ * gradients with no stop.
+ */
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_pattern_t *pat;
+
+    cairo_set_source_rgb (cr, 1., 0., 0.);
+
+    pat = cairo_pattern_create_linear (0., 0., 1., 1.);
+    cairo_mask (cr, pat);
+    cairo_pattern_destroy (pat);
+
+    pat = cairo_pattern_create_radial (0., 0., 0., 1., 1., 1.);
+    cairo_mask (cr, pat);
+    cairo_pattern_destroy (pat);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (gradient_zero_stops_mask,
+           "Verifies that gradients with no stops are considered clear.",
+           "gradient", /* keywords */
+           NULL, /* requirements */
+           2, 2,
+           NULL, draw)
diff --git a/test/gradient-zero-stops.c b/test/gradient-zero-stops.c
new file mode 100644 (file)
index 0000000..57a91c5
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * Copyright © 2007 Brian Ewins
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Brian Ewins <Brian.Ewins@gmail.com>
+ */
+
+#include "cairo-test.h"
+
+/* This test case is designed to exercise the following bug:
+ *
+ *      Crash when trying to paint gradient with no stops
+ *      https://bugzilla.mozilla.org/show_bug.cgi?id=407104
+ */
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_pattern_t *pat;
+
+    pat = cairo_pattern_create_linear (0., 0., 1., 1.);
+    cairo_set_source (cr, pat);
+    cairo_paint (cr);
+    cairo_pattern_destroy (pat);
+
+    pat = cairo_pattern_create_radial (0., 0., 0., 1., 1., 1.);
+    cairo_set_source (cr, pat);
+    cairo_paint (cr);
+    cairo_pattern_destroy (pat);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (gradient_zero_stops,
+           "Verifies that gradients with no stops don't cause problems.",
+           "gradient", /* keywords */
+           NULL, /* requirements */
+           2, 2,
+           NULL, draw)
diff --git a/test/group-clip.c b/test/group-clip.c
new file mode 100644 (file)
index 0000000..b99d861
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Copyright © Chris Wilson, 2008
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Chris Wilson not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * CHRIS WILSON DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL CHRIS WILSON BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Authors: Chris Wilson <chris@chris-wilson.co.uk>
+ *          Larry Ewing <lewing@novell.com>
+ */
+
+#include "cairo-test.h"
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_paint (cr);
+
+    cairo_rectangle (cr, 25, 25, width, height);
+    cairo_clip_preserve (cr);
+    cairo_push_group (cr);
+    cairo_set_source_rgb (cr, 0, 0, 1);
+    cairo_fill (cr);
+    cairo_rectangle (cr, 0, 0, width, height);
+    cairo_pop_group_to_source (cr);
+    cairo_paint (cr);
+
+    cairo_reset_clip (cr);
+    cairo_clip_preserve (cr);
+    cairo_set_source_rgba (cr, 1, 0, 0, .5);
+    cairo_paint (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (group_clip,
+           "test preserving paths across groups",
+           "group", /* keywords */
+           NULL, /* requirements */
+           40 + 25, 40 + 25,
+           NULL, draw)
diff --git a/test/group-paint.c b/test/group-paint.c
new file mode 100644 (file)
index 0000000..3854485
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright © Chris Wilson, 2008
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Chris Wilson not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * CHRIS WILSON DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL CHRIS WILSON BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Authors: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_push_group (cr);
+
+    cairo_set_source_rgb (cr, 0, 0, 1);
+    cairo_paint (cr);
+
+    cairo_pop_group_to_source (cr);
+    cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+    cairo_paint (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (group_paint,
+           "test push_group(); pop_group_to_source(); set_operator(SOURCE); paint();",
+           "group", /* keywords */
+           NULL, /* requirements */
+           10, 10,
+           NULL, draw)
diff --git a/test/group-state.c b/test/group-state.c
new file mode 100644 (file)
index 0000000..a96b2e3
--- /dev/null
@@ -0,0 +1,96 @@
+/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/*
+ * Copyright 2011 Andrea Canciani
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Andrea Canciani <ranma42@gmail.com>
+ */
+
+#include "cairo-test.h"
+
+#define CHECK_STATUS(status)                                           \
+    do {                                                               \
+       if (cairo_status (cr) != (status)) {                            \
+           cairo_test_log (ctx, "Expected status: %s\n",               \
+                           cairo_status_to_string (status));           \
+           cairo_test_log (ctx, "Actual status: %s\n",                 \
+                           cairo_status_to_string (cairo_status (cr))); \
+           result = CAIRO_TEST_FAILURE;                                \
+       }                                                               \
+    } while (0)
+
+static void
+reinit_cairo (cairo_t **cr)
+{
+    if (*cr)
+       cairo_destroy (*cr);
+
+    *cr = cairo_create (cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 1, 1));
+    cairo_surface_destroy (cairo_get_target (*cr));
+}
+
+static cairo_test_status_t
+preamble (cairo_test_context_t *ctx)
+{
+    cairo_t *cr;
+    cairo_test_status_t result = CAIRO_TEST_SUCCESS;
+
+    cr = NULL;
+
+    reinit_cairo (&cr);
+
+    /* cairo_restore() must fail with CAIRO_STATUS_INVALID_RESTORE if
+     * no matching cairo_save() call has been performed. */
+    cairo_test_log (ctx, "Checking save(); push(); restore();\n");
+    cairo_save (cr);
+    CHECK_STATUS (CAIRO_STATUS_SUCCESS);
+    cairo_push_group (cr);
+    CHECK_STATUS (CAIRO_STATUS_SUCCESS);
+    cairo_restore (cr);
+    CHECK_STATUS (CAIRO_STATUS_INVALID_RESTORE);
+
+
+    reinit_cairo (&cr);
+
+    /* cairo_restore() must fail with CAIRO_STATUS_INVALID_RESTORE if
+     * no matching cairo_save() call has been performed. */
+    cairo_test_log (ctx, "Checking push(); save(); pop();\n");
+    cairo_push_group (cr);
+    CHECK_STATUS (CAIRO_STATUS_SUCCESS);
+    cairo_save (cr);
+    CHECK_STATUS (CAIRO_STATUS_SUCCESS);
+    cairo_pop_group_to_source (cr);
+    CHECK_STATUS (CAIRO_STATUS_INVALID_POP_GROUP);
+
+
+    cairo_destroy (cr);
+
+    return result;
+}
+
+CAIRO_TEST (group_state,
+           "Tests the interaction between state (cairo_save, cairo_restore) "
+           "and group (cairo_push_group/cairo_pop_group) API",
+           "api", /* keywords */
+           NULL, /* requirements */
+           0, 0,
+           preamble, NULL)
diff --git a/test/group-unaligned.c b/test/group-unaligned.c
new file mode 100644 (file)
index 0000000..9124a03
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * Copyright © Chris Wilson, 2008
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Chris Wilson not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * CHRIS WILSON DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL CHRIS WILSON BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Authors: Chris Wilson <chris@chris-wilson.co.uk>
+ *          Stefan Röllin <stefan.roellin@gmx.ch>
+ */
+
+#include "cairo-test.h"
+
+static void
+circle (cairo_t* cr, double xc, double yc, double radius)
+{
+    cairo_arc (cr, xc, yc, radius, 0.0, 2*M_PI);
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_paint (cr);
+
+    cairo_set_source_rgb (cr, 1, 0, 0);
+    circle (cr, 12.5, 12.5, 10.);
+    cairo_fill (cr);
+
+    cairo_push_group (cr);
+    cairo_set_source_rgb (cr, 0, 0, 1);
+    circle (cr, 12.5, 12.5, 10.);
+    cairo_fill (cr);
+    cairo_pop_group_to_source (cr);
+    cairo_paint (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (group_unaligned,
+           "test non-integer sized sub-surface",
+           "group", /* keywords */
+           NULL, /* requirements */
+           35.5, 35.5,
+           NULL, draw)
diff --git a/test/half-coverage.c b/test/half-coverage.c
new file mode 100644 (file)
index 0000000..38cafda
--- /dev/null
@@ -0,0 +1,155 @@
+/*
+ * Copyright 2010 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+
+/* Test the fidelity of the rasterisation, because Cairo is my favourite
+ * driver test suite.
+ */
+
+#define SIZE 256
+#define WIDTH 2
+#define HEIGHT 10
+
+static cairo_test_status_t
+rectangles (cairo_t *cr, int width, int height)
+{
+    int i;
+
+    cairo_set_source_rgb (cr, 1.0, 0.0, 0.0);
+    cairo_paint (cr);
+    cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
+
+    for (i = 1; i <= SIZE; i++) {
+       int x, y;
+
+       cairo_save (cr);
+       cairo_rectangle (cr, 0, 0, WIDTH, HEIGHT);
+       cairo_clip (cr);
+
+       cairo_scale (cr, 1./SIZE, 1./SIZE);
+       for (x = -i; x < SIZE*WIDTH; x += 2*i) {
+           for (y = -i; y < SIZE*HEIGHT; y += 2*i) {
+               /* Add a little tile composed of two non-overlapping squares
+                *   +--+
+                *   |  |
+                *   |__|__
+                *      |  |
+                *      |  |
+                *      +--+
+                */
+               cairo_rectangle (cr, x, y, i, i);
+               cairo_rectangle (cr, x+i, y+i, i, i);
+           }
+       }
+       cairo_fill (cr);
+       cairo_restore (cr);
+
+       cairo_translate (cr, WIDTH, 0);
+    }
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+static cairo_test_status_t
+triangles (cairo_t *cr, int width, int height)
+{
+    int i;
+
+    cairo_set_source_rgb (cr, 1.0, 0.0, 0.0);
+    cairo_paint (cr);
+    cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
+
+    for (i = 1; i <= SIZE; i++) {
+       int x, y;
+
+       cairo_save (cr);
+       cairo_rectangle (cr, 0, 0, WIDTH, HEIGHT);
+       cairo_clip (cr);
+
+       cairo_scale (cr, 1./SIZE, 1./SIZE);
+       for (x = -i; x < SIZE*WIDTH; x += 2*i) {
+           for (y = -i; y < SIZE*HEIGHT; y += 2*i) {
+               /* Add a tile composed of four non-overlapping
+                * triangles.  The plus and minus signs inside the
+                * triangles denote the orientation of the triangle's
+                * edges: + for clockwise and - for anticlockwise.
+                *
+                *   +-----+
+                *    \-|+/
+                *     \|/
+                *     /|\
+                *    /-|-\
+                *   +-----+
+                */
+
+               /* top left triangle */
+               cairo_move_to (cr, x, y);
+               cairo_line_to (cr, x+i, y+i);
+               cairo_line_to (cr, x+i, y);
+               cairo_close_path (cr);
+
+               /* top right triangle */
+               cairo_move_to (cr, x+i, y);
+               cairo_line_to (cr, x+2*i, y);
+               cairo_line_to (cr, x+i, y+i);
+               cairo_close_path (cr);
+
+               /* bottom left triangle */
+               cairo_move_to (cr, x+i, y+i);
+               cairo_line_to (cr, x, y+2*i);
+               cairo_line_to (cr, x+i, y+2*i);
+               cairo_close_path (cr);
+
+               /* bottom right triangle */
+               cairo_move_to (cr, x+i, y+i);
+               cairo_line_to (cr, x+i, y+2*i);
+               cairo_line_to (cr, x+2*i, y+2*i);
+               cairo_close_path (cr);
+           }
+       }
+       cairo_fill (cr);
+       cairo_restore (cr);
+
+       cairo_translate (cr, WIDTH, 0);
+    }
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (half_coverage_rectangles,
+           "Check the fidelity of the rasterisation.",
+           NULL, /* keywords */
+           "target=raster slow", /* requirements */
+           WIDTH * SIZE, HEIGHT,
+           NULL, rectangles)
+
+CAIRO_TEST (half_coverage_triangles,
+           "Check the fidelity of the rasterisation.",
+           NULL, /* keywords */
+           "target=raster slow", /* requirements */
+           WIDTH * SIZE, HEIGHT,
+           NULL, triangles)
diff --git a/test/halo.c b/test/halo.c
new file mode 100644 (file)
index 0000000..30064e5
--- /dev/null
@@ -0,0 +1,158 @@
+/*
+ * Copyright 2010 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+
+/* Try to replicate the misbehaviour of show_glyphs() versus glyph_path()
+ * in the PDF backend reported by Ian Britten.
+ */
+
+static void
+halo_around_path (cairo_t *cr, const char *str)
+{
+    cairo_text_path (cr, str);
+
+    cairo_set_source_rgb (cr, 0, .5, 1);
+    cairo_stroke_preserve (cr);
+    cairo_set_source_rgb (cr, 1, .5, 0);
+    cairo_fill (cr);
+}
+
+static void
+halo_around_text (cairo_t *cr, const char *str)
+{
+    double x, y;
+
+    cairo_get_current_point (cr, &x, &y);
+    cairo_text_path (cr, str);
+
+    cairo_set_source_rgb (cr, 0, .5, 1);
+    cairo_stroke(cr);
+
+    cairo_set_source_rgb (cr, 1, .5, 0);
+    cairo_move_to (cr, x, y);
+    cairo_show_text  (cr, str);
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    const char *string = "0123456789";
+    cairo_text_extents_t extents;
+
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_paint (cr);
+
+    cairo_text_extents (cr, string, &extents);
+
+    cairo_set_font_size (cr, 12);
+    cairo_set_line_width (cr, 3);
+    cairo_move_to (cr, 9, 4 + extents.height);
+    halo_around_path (cr, string);
+
+    cairo_move_to (cr, 109, 4 + extents.height);
+    halo_around_text (cr, string);
+
+    cairo_set_font_size (cr, 6);
+    cairo_set_line_width (cr, 3);
+    cairo_move_to (cr, 19 + extents.width, 20 + extents.height);
+    halo_around_path (cr, "0");
+
+    cairo_move_to (cr, 119 + extents.width, 20 + extents.height);
+    halo_around_text (cr, "0");
+
+    cairo_set_font_size (cr, 64);
+    cairo_set_line_width (cr, 10);
+    cairo_move_to (cr, 8, 70);
+    halo_around_path (cr, "6");
+    cairo_move_to (cr, 32, 90);
+    halo_around_path (cr, "7");
+
+    cairo_move_to (cr, 108, 70);
+    halo_around_text (cr, "6");
+    cairo_move_to (cr, 132, 90);
+    halo_around_text (cr, "7");
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+static cairo_test_status_t
+draw_transform (cairo_t *cr, int width, int height)
+{
+    const char *string = "0123456789";
+    cairo_text_extents_t extents;
+
+    cairo_translate (cr, 50, 50);
+    cairo_scale (cr, M_SQRT2, M_SQRT2);
+
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_paint (cr);
+
+    cairo_text_extents (cr, string, &extents);
+
+    cairo_set_line_width (cr, 3);
+    cairo_move_to (cr, 9, 4 + extents.height);
+    halo_around_path (cr, string);
+
+    cairo_move_to (cr, 109, 4 + extents.height);
+    halo_around_text (cr, string);
+
+    cairo_set_font_size (cr, 6);
+    cairo_set_line_width (cr, 3);
+    cairo_move_to (cr, 19 + extents.width, 20 + extents.height);
+    halo_around_path (cr, "0");
+
+    cairo_move_to (cr, 119 + extents.width, 20 + extents.height);
+    halo_around_text (cr, "0");
+
+    cairo_set_font_size (cr, 64);
+    cairo_set_line_width (cr, 10);
+    cairo_move_to (cr, 8, 70);
+    halo_around_path (cr, "6");
+    cairo_move_to (cr, 32, 90);
+    halo_around_path (cr, "7");
+
+    cairo_move_to (cr, 108, 70);
+    halo_around_text (cr, "6");
+    cairo_move_to (cr, 132, 90);
+    halo_around_text (cr, "7");
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (halo,
+           "Check the show_glyphs() vs glyph_path()",
+           "text", /* keywords */
+           NULL, /* requirements */
+           200, 100,
+           NULL, draw)
+
+CAIRO_TEST (halo_transform,
+           "Check the show_glyphs() vs glyph_path()",
+           "text", /* keywords */
+           NULL, /* requirements */
+           400, 200,
+           NULL, draw_transform)
diff --git a/test/hatchings.c b/test/hatchings.c
new file mode 100644 (file)
index 0000000..ca3f2c6
--- /dev/null
@@ -0,0 +1,153 @@
+/*
+ * Copyright © 2011 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+
+#define STEP   5
+#define WIDTH  100
+#define HEIGHT 100
+
+static void hatching (cairo_t *cr)
+{
+    int i;
+
+    cairo_rectangle (cr, 0, 0, WIDTH, HEIGHT);
+    cairo_clip (cr);
+
+    for (i = 0; i < WIDTH; i += STEP) {
+       cairo_rectangle (cr, i-1, -2, 2, HEIGHT+4);
+       cairo_rectangle (cr, -2, i-1, WIDTH+4, 2);
+    }
+}
+
+static void background (cairo_t *cr)
+{
+    cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+    cairo_set_source_rgb (cr, 1,1,1);
+    cairo_paint (cr);
+}
+
+static void clip_to_quadrant (cairo_t *cr)
+{
+    cairo_rectangle (cr, 0, 0, WIDTH, HEIGHT);
+    cairo_clip (cr);
+}
+
+static void draw_hatching (cairo_t *cr, void (*func) (cairo_t *))
+{
+    cairo_save (cr); {
+       clip_to_quadrant (cr);
+       hatching (cr);
+       func (cr);
+    } cairo_restore (cr);
+
+    cairo_translate (cr, WIDTH, 0);
+
+    cairo_save (cr); {
+       clip_to_quadrant (cr);
+       cairo_translate (cr, 0.25, 0.25);
+       hatching (cr);
+       func (cr);
+    } cairo_restore (cr);
+
+    cairo_translate (cr, WIDTH, 0);
+
+    cairo_save (cr); {
+       clip_to_quadrant (cr);
+       cairo_translate (cr, WIDTH/2, HEIGHT/2);
+       cairo_rotate (cr, M_PI/4);
+       cairo_translate (cr, -WIDTH/2, -HEIGHT/2);
+       hatching (cr);
+       func (cr);
+    } cairo_restore (cr);
+
+    cairo_translate (cr, WIDTH, 0);
+}
+
+static void do_clip (cairo_t *cr)
+{
+    cairo_clip (cr);
+    cairo_paint (cr);
+}
+
+static void do_clip_alpha (cairo_t *cr)
+{
+    cairo_clip (cr);
+    cairo_paint_with_alpha (cr, .5);
+}
+
+static void hatchings (cairo_t *cr, void (*func) (cairo_t *))
+{
+    cairo_save (cr); {
+       cairo_set_source_rgb(cr, 1, 0, 0);
+       cairo_set_antialias (cr, CAIRO_ANTIALIAS_DEFAULT);
+       draw_hatching (cr, func);
+       cairo_set_source_rgb(cr, 0, 0, 1);
+       cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
+       draw_hatching (cr, func);
+    } cairo_restore (cr);
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    background (cr);
+
+
+    /* aligned, misaligned, diagonal; mono repeat
+     * x fill
+     * x clip; paint
+     * x clip; paint-alpha
+     * repeated, for over/source
+     */
+
+    cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
+
+    hatchings (cr, cairo_fill);
+    cairo_translate (cr, 0, HEIGHT);
+    hatchings (cr, do_clip);
+    cairo_translate (cr, 0, HEIGHT);
+    hatchings (cr, do_clip_alpha);
+    cairo_translate (cr, 0, HEIGHT);
+
+    cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+
+    hatchings (cr, cairo_fill);
+    cairo_translate (cr, 0, HEIGHT);
+    hatchings (cr, do_clip);
+    cairo_translate (cr, 0, HEIGHT);
+    hatchings (cr, do_clip_alpha);
+    cairo_translate (cr, 0, HEIGHT);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (hatchings,
+           "Test drawing through various aligned/unaliged clips",
+           "clip, alpha", /* keywords */
+           "target=raster", /* requirements */
+           6*WIDTH, 6*HEIGHT,
+           NULL, draw)
diff --git a/test/horizontal-clip.c b/test/horizontal-clip.c
new file mode 100644 (file)
index 0000000..93127a7
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2011 Intel Corporation
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Chris Wilson not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Chris Wilson makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * CHRIS WILSON DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL CHRIS WILSON BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+/* Exercises a bug spotted by Andrea Canciani where the polygon clipping
+ * code was hopeless broken with horizontal edges.
+ */
+
+#include "cairo-test.h"
+
+#define WIDTH 16
+#define HEIGHT 26
+
+#define BUGY 1
+#define BUGX (4 * BUGY * WIDTH * 256)
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_paint (cr);
+    cairo_set_source_rgb (cr, 0, 0, 0);
+
+    cairo_move_to (cr,       - BUGX, 6 - BUGY);
+    cairo_line_to (cr,       + BUGX, 6 + BUGY);
+    cairo_line_to (cr, WIDTH + BUGX, 2 - BUGY);
+    cairo_line_to (cr, WIDTH - BUGX, 2 + BUGY);
+    cairo_fill (cr);
+
+    cairo_move_to (cr, WIDTH + BUGX, 8  - BUGY);
+    cairo_line_to (cr, WIDTH - BUGX, 8  + BUGY);
+    cairo_line_to (cr,       - BUGX, 12 - BUGY);
+    cairo_line_to (cr,       + BUGX, 12 + BUGY);
+    cairo_fill (cr);
+
+    cairo_move_to (cr,       - BUGX, 14 - BUGY);
+    cairo_line_to (cr,       + BUGX, 14 + BUGY);
+    cairo_line_to (cr, WIDTH + BUGX, 18 - BUGY);
+    cairo_line_to (cr, WIDTH - BUGX, 18 + BUGY);
+    cairo_fill (cr);
+
+    cairo_move_to (cr, WIDTH + BUGX, 24 - BUGY);
+    cairo_line_to (cr, WIDTH - BUGX, 24 + BUGY);
+    cairo_line_to (cr,       - BUGX, 20 - BUGY);
+    cairo_line_to (cr,       + BUGX, 20 + BUGY);
+    cairo_fill (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (horizontal_clip,
+           "Tests intersection of a nearly horizontal lines with a clipped polygon",
+           "clip, fill", /* keywords */
+           NULL, /* requirements */
+           WIDTH, HEIGHT,
+           NULL, draw)
diff --git a/test/huge-linear.c b/test/huge-linear.c
new file mode 100644 (file)
index 0000000..f84b4ea
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * Copyright © 2006 Benjamin Otte
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Benjamin Otte <otte@gnome.org>
+ */
+
+#include "cairo-test.h"
+
+/* set this to 0.1 to make this test work */
+#define FACTOR 1.e6
+
+/* XXX poppler-cairo doesn't handle gradients very well... */
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_pattern_t *pattern;
+    cairo_matrix_t mat = {
+       0, -4.5254285714285709 * FACTOR,
+       -2.6398333333333333 * FACTOR, 0,
+       0, 0
+    };
+
+    pattern = cairo_pattern_create_linear (-16384 * FACTOR, 0,
+                                           16384 * FACTOR, 0);
+    cairo_pattern_add_color_stop_rgba (pattern,
+                                      0, 0.376471, 0.533333, 0.27451, 1);
+    cairo_pattern_add_color_stop_rgba (pattern, 1, 1, 1, 1, 1);
+    cairo_pattern_set_matrix (pattern, &mat);
+
+    cairo_scale (cr, 0.05, 0.05);
+    cairo_translate (cr, 6000, 3500);
+
+    cairo_set_source (cr, pattern);
+    cairo_rectangle (cr, -6000, -3500, 12000, 7000);
+    cairo_pattern_destroy (pattern);
+    cairo_fill (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (huge_linear,
+           "Test huge linear patterns",
+           "gradient, linear", /* keywords */
+           NULL, /* requirements */
+           600, 350,
+           NULL, draw)
diff --git a/test/huge-radial.c b/test/huge-radial.c
new file mode 100644 (file)
index 0000000..21524b7
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * Copyright © 2006 Benjamin Otte
+ * Copyright © 2009 Chris Wilson
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Benjamin Otte <otte@gnome.org>
+ *         Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+
+/* set this to 0.1 to make this test work */
+#define FACTOR 1.e6
+
+/* XXX poppler-cairo doesn't handle gradients very well... */
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_pattern_t *pattern;
+    cairo_matrix_t mat = {
+       0, -4.5254285714285709 * FACTOR,
+       -2.6398333333333333 * FACTOR, 0,
+       0, 0
+    };
+
+    pattern = cairo_pattern_create_radial (0, 0, 0,
+                                          0, 0, 16384 * FACTOR);
+    cairo_pattern_add_color_stop_rgba (pattern,
+                                      0, 0.376471, 0.533333, 0.27451, 1);
+    cairo_pattern_add_color_stop_rgba (pattern, 1, 1, 1, 1, 1);
+    cairo_pattern_set_matrix (pattern, &mat);
+
+    cairo_scale (cr, 0.05, 0.05);
+    cairo_translate (cr, 6000, 3500);
+
+    cairo_set_source (cr, pattern);
+    cairo_rectangle (cr, -6000, -3500, 12000, 7000);
+    cairo_pattern_destroy (pattern);
+    cairo_fill (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (huge_radial,
+           "Test huge radial patterns",
+           "gradient, radial", /* keywords */
+           NULL, /* requirements */
+           600, 350,
+           NULL, draw)
diff --git a/test/image-bug-710072.c b/test/image-bug-710072.c
new file mode 100644 (file)
index 0000000..0c9cd8a
--- /dev/null
@@ -0,0 +1,80 @@
+/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/*
+ * Copyright 2011 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+/*
+ * This test case aims to reproduce the misbehaviour exhibited in
+ * https://bugs.launchpad.net/ubuntu/+source/cairo/+bug/710072
+ * i.e. out of bounds rendering with the rectangular span compositor.
+ */
+
+#include "cairo-test.h"
+
+static cairo_test_status_t
+draw_aligned (cairo_t *cr, int width, int height)
+{
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_paint (cr);
+
+    cairo_rectangle (cr, -10, -10, 20, 20);
+    cairo_rectangle (cr, 5, 5, 20, 20);
+    cairo_set_source_rgb (cr, 0, 0, 0);
+    cairo_fill_preserve (cr);
+    cairo_set_source_rgba (cr, 1, 0, 0, .5);
+    cairo_stroke (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+static cairo_test_status_t
+draw_unaligned (cairo_t *cr, int width, int height)
+{
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_paint (cr);
+
+    cairo_rectangle (cr, -10.5, -10.5, 20, 20);
+    cairo_rectangle (cr, 5.5, 5.5, 20, 20);
+    cairo_set_source_rgb (cr, 0, 0, 0);
+    cairo_fill_preserve (cr);
+    cairo_set_source_rgba (cr, 1, 0, 0, .5);
+    cairo_stroke (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (image_bug_710072_aligned,
+           "Tests a bug where we may compute spans greater than bounded extents",
+           "extents,fill,stroke", /* keywords */
+           NULL, /* requirements */
+           15, 15,
+           NULL, draw_aligned)
+
+CAIRO_TEST (image_bug_710072_unaligned,
+           "Tests a bug where we may compute spans greater than bounded extents",
+           "extents,fill,stroke", /* keywords */
+           NULL, /* requirements */
+           15, 15,
+           NULL, draw_unaligned)
diff --git a/test/image-surface-source.c b/test/image-surface-source.c
new file mode 100644 (file)
index 0000000..c7c1fdc
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright © 2008 Chris Wilson
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Chris Wilson not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Chris Wilson makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * CHRIS WILSON DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL CHRIS WILSON BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+
+#include "surface-source.c"
+
+static cairo_surface_t *
+create_source_surface (int size)
+{
+    return cairo_image_surface_create (CAIRO_FORMAT_ARGB32, size, size);
+}
+
+CAIRO_TEST (image_surface_source,
+           "Test using a image surface as the source",
+           "source", /* keywords */
+           NULL, /* requirements */
+           SIZE, SIZE,
+           preamble, draw)
diff --git a/test/imagediff.c b/test/imagediff.c
new file mode 100644 (file)
index 0000000..6ebbcfc
--- /dev/null
@@ -0,0 +1,303 @@
+/* imagediff - Compare two images
+ *
+ * Copyright © 2004 Richard D. Worth
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of Richard Worth
+ * not be used in advertising or publicity pertaining to distribution
+ * of the software without specific, written prior permission.
+ * Richard Worth makes no representations about the suitability of this
+ * software for any purpose.  It is provided "as is" without express
+ * or implied warranty.
+ *
+ * RICHARD WORTH DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
+ * NO EVENT SHALL RICHARD WORTH BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+ * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+ * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Richard D. Worth <richard@theworths.org> */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <errno.h>
+
+#include "buffer-diff.h"
+
+static void
+_xunlink (const char *pathname)
+{
+    if (unlink (pathname) < 0 && errno != ENOENT) {
+       fprintf (stderr, "  Error: Cannot remove %s: %s\n",
+                       pathname, strerror (errno));
+       exit (1);
+    }
+}
+
+void
+cairo_test_logv (const cairo_test_context_t *ctx,
+                const char *fmt, va_list va)
+{
+    vfprintf (stderr, fmt, va);
+}
+
+void
+cairo_test_log (const cairo_test_context_t *ctx, const char *fmt, ...)
+{
+    va_list va;
+
+    va_start (va, fmt);
+    vfprintf (stderr, fmt, va);
+    va_end (va);
+}
+
+/* Flatten an ARGB surface by blending it over white. The resulting
+ * surface, (still in ARGB32 format, but with only alpha==1.0
+ * everywhere) is returned in the same surface pointer.
+ *
+ * The original surface will be destroyed.
+ *
+ * The (x,y) value specify an origin of interest for the original
+ * image. The flattened image will be generated only from the box
+ * extending from (x,y) to (width,height).
+ */
+static void
+flatten_surface (cairo_surface_t **surface, int x, int y)
+{
+    cairo_surface_t *flat;
+    cairo_t *cr;
+
+    flat = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
+                                      cairo_image_surface_get_width (*surface) - x,
+                                      cairo_image_surface_get_height (*surface) - y);
+    cairo_surface_set_device_offset (flat, -x, -y);
+
+    cr = cairo_create (flat);
+    cairo_surface_destroy (flat);
+
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_paint (cr);
+
+    cairo_set_source_surface (cr, *surface, 0, 0);
+    cairo_surface_destroy (*surface);
+    cairo_paint (cr);
+
+    *surface = cairo_surface_reference (cairo_get_target (cr));
+    cairo_destroy (cr);
+}
+
+/* Given an image surface, create a new surface that has the same
+ * contents as the sub-surface with its origin at x,y.
+ *
+ * The original surface will be destroyed.
+ */
+static void
+extract_sub_surface (cairo_surface_t **surface, int x, int y)
+{
+    cairo_surface_t *sub;
+    cairo_t *cr;
+
+    sub = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
+                                     cairo_image_surface_get_width (*surface) - x,
+                                     cairo_image_surface_get_height (*surface) - y);
+
+    /* We don't use a device offset like flatten_surface. That's not
+     * for any important reason, (the results should be
+     * identical). This style just seemed more natural to me this
+     * time, so I'm leaving both here so I can look at both to see
+     * which I like better. */
+    cr = cairo_create (sub);
+    cairo_surface_destroy (sub);
+
+    cairo_set_source_surface (cr, *surface, -x, -y);
+    cairo_surface_destroy (*surface);
+    cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+    cairo_paint (cr);
+
+    *surface = cairo_surface_reference (cairo_get_target (cr));
+    cairo_destroy (cr);
+}
+
+static cairo_status_t
+stdio_write_func (void *closure, const unsigned char *data, unsigned int length)
+{
+    FILE *file = closure;
+
+    if (fwrite (data, 1, length, file) != length)
+       return CAIRO_STATUS_WRITE_ERROR;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+write_png (cairo_surface_t *surface, const char *filename)
+{
+    cairo_status_t status;
+    FILE *png_file;
+
+    if (filename != NULL) {
+       png_file = fopen (filename, "wb");
+       if (png_file == NULL) {
+           switch (errno) {
+           case ENOMEM:
+               return CAIRO_STATUS_NO_MEMORY;
+           default:
+               return CAIRO_STATUS_WRITE_ERROR;
+           }
+       }
+    } else
+       png_file = stdout;
+
+    status = cairo_surface_write_to_png_stream (surface,
+                                               stdio_write_func,
+                                               png_file);
+
+    if (png_file != stdout)
+       fclose (png_file);
+
+    return status;
+}
+
+static cairo_status_t
+png_diff (const char *filename_a,
+         const char *filename_b,
+         const char *filename_diff,
+         int           ax,
+         int           ay,
+         int           bx,
+         int           by,
+         buffer_diff_result_t *result)
+{
+    cairo_surface_t *surface_a;
+    cairo_surface_t *surface_b;
+    cairo_surface_t *surface_diff;
+    cairo_status_t status;
+
+    surface_a = cairo_image_surface_create_from_png (filename_a);
+    status = cairo_surface_status (surface_a);
+    if (status) {
+       fprintf (stderr, "Error: Failed to create surface from %s: %s\n",
+                filename_a, cairo_status_to_string (status));
+       return status;
+    }
+
+    surface_b = cairo_image_surface_create_from_png (filename_b);
+    status = cairo_surface_status (surface_b);
+    if (status) {
+       fprintf (stderr, "Error: Failed to create surface from %s: %s\n",
+                filename_b, cairo_status_to_string (status));
+       cairo_surface_destroy (surface_a);
+       return status;
+    }
+
+    if (ax || ay) {
+       extract_sub_surface (&surface_a, ax, ay);
+       ax = ay = 0;
+    }
+
+    if (bx || by) {
+       extract_sub_surface (&surface_b, bx, by);
+       bx = by = 0;
+    }
+
+    status = cairo_surface_status (surface_a);
+    if (status) {
+       fprintf (stderr, "Error: Failed to extract surface from %s: %s\n",
+                filename_a, cairo_status_to_string (status));
+       cairo_surface_destroy (surface_a);
+       cairo_surface_destroy (surface_b);
+       return status;
+    }
+    status = cairo_surface_status (surface_b);
+    if (status) {
+       fprintf (stderr, "Error: Failed to extract surface from %s: %s\n",
+                filename_b, cairo_status_to_string (status));
+       cairo_surface_destroy (surface_a);
+       cairo_surface_destroy (surface_b);
+       return status;
+    }
+
+    surface_diff = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
+                                              cairo_image_surface_get_width (surface_a),
+                                              cairo_image_surface_get_height (surface_a));
+    status = cairo_surface_status (surface_diff);
+    if (status) {
+       fprintf (stderr,
+                "Error: Failed to allocate surface to hold differences\n");
+       cairo_surface_destroy (surface_a);
+       cairo_surface_destroy (surface_b);
+       return CAIRO_STATUS_NO_MEMORY;
+    }
+
+    status = image_diff (NULL,
+                        surface_a, surface_b, surface_diff,
+                        result);
+
+    if (filename_diff)
+       _xunlink (filename_diff);
+
+    if (status == CAIRO_STATUS_SUCCESS &&
+       result->pixels_changed)
+    {
+       status = write_png (surface_diff, filename_diff);
+    }
+
+    cairo_surface_destroy (surface_a);
+    cairo_surface_destroy (surface_b);
+    cairo_surface_destroy (surface_diff);
+
+    return status;
+}
+
+int
+main (int argc, char *argv[])
+{
+    buffer_diff_result_t result;
+    cairo_status_t status;
+
+    unsigned int ax, ay, bx, by;
+
+    if (argc != 3 && argc != 7) {
+       fprintf (stderr, "Usage: %s image1.png image2.png [ax ay bx by]\n", argv[0]);
+       fprintf (stderr, "Computes an output image designed to present a \"visual diff\" such that even\n");
+       fprintf (stderr, "small errors in single pixels are readily apparent in the output.\n");
+       fprintf (stderr, "The output image is written on stdout.\n");
+       exit (1);
+    }
+
+    if (argc == 7) {
+       ax = strtoul (argv[3], NULL, 0);
+       ay = strtoul (argv[4], NULL, 0);
+       bx = strtoul (argv[5], NULL, 0);
+       by = strtoul (argv[6], NULL, 0);
+    } else {
+       ax = ay = bx = by = 0;
+    }
+
+    status = png_diff (argv[1], argv[2], NULL, ax, ay, bx, by, &result);
+
+    if (status) {
+       fprintf (stderr, "Error comparing images: %s\n",
+                cairo_status_to_string (status));
+       return 1;
+    }
+
+    if (result.pixels_changed)
+       fprintf (stderr, "Total pixels changed: %d with a maximum channel difference of %d.\n",
+                result.pixels_changed,
+                result.max_diff);
+
+    return (result.pixels_changed != 0);
+}
diff --git a/test/implicit-close.c b/test/implicit-close.c
new file mode 100644 (file)
index 0000000..2529bb0
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright © 2009 Chris Wilson
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Chris Wilson not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Chris Wilson makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * CHRIS WILSON DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL CHRIS WILSON BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+
+#define SIZE 40
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_paint (cr);
+
+    cairo_move_to (cr, SIZE,    0);
+    cairo_rel_line_to (cr, 0, SIZE);
+    cairo_rel_line_to (cr, -SIZE,    0);
+
+    cairo_set_source_rgb (cr, 0, 0, 0);
+    cairo_fill_preserve (cr);
+
+    cairo_set_source_rgb (cr, 1, 0, 0);
+    cairo_stroke (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (implicit_close,
+           "Test implicitly closing paths",
+           "fill", /* keywords */
+           NULL, /* requirements */
+           SIZE, SIZE,
+           NULL, draw)
diff --git a/test/in-fill-empty-trapezoid.c b/test/in-fill-empty-trapezoid.c
new file mode 100644 (file)
index 0000000..2b345cb
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ * Copyright © 2006 M Joonas Pihlaja
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: M Joonas Pihlaja <jpihlaja@cc.helsinki.fi>
+ */
+
+/* Bug history
+ *
+ * 2006-12-05  M Joonas Pihlaja <jpihlaja@cc.helsinki.fi>
+ *
+ *  The cairo_in_fill () function can sometimes produce false
+ *  positives when the tessellator produces empty trapezoids
+ *  and the query point lands exactly on a trapezoid edge.
+ */
+
+#include "cairo-test.h"
+
+static cairo_test_status_t
+preamble (cairo_test_context_t *ctx)
+{
+    int x,y;
+    int width = 10;
+    int height = 10;
+    cairo_surface_t *surf;
+    cairo_t *cr;
+    int false_positive_count = 0;
+    cairo_status_t status;
+    cairo_test_status_t ret;
+
+    surf = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
+    cr = cairo_create (surf);
+    cairo_surface_destroy (surf);
+
+    /* Empty horizontal trapezoid. */
+    cairo_move_to (cr, 0, height/3);
+    cairo_line_to (cr, width, height/3);
+    cairo_close_path (cr);
+
+    /* Empty non-horizontal trapezoid #1. */
+    cairo_move_to (cr, 0, 0);
+    cairo_line_to (cr, width, height/2);
+    cairo_close_path (cr);
+
+    /* Empty non-horizontal trapezoid #2 intersecting #1. */
+    cairo_move_to (cr, 0, height/2);
+    cairo_line_to (cr, width, 0);
+    cairo_close_path (cr);
+
+    status = cairo_status (cr);
+
+    /* Point sample the tessellated path. */
+    for (y = 0; y < height; y++) {
+       for (x = 0; x < width; x++) {
+           if (cairo_in_fill (cr, x, y)) {
+               false_positive_count++;
+           }
+       }
+    }
+    cairo_destroy (cr);
+
+    /* Check that everything went well. */
+    ret = CAIRO_TEST_SUCCESS;
+    if (CAIRO_STATUS_SUCCESS != status) {
+       cairo_test_log (ctx, "Failed to create a test surface and path: %s\n",
+                       cairo_status_to_string (status));
+       ret = CAIRO_TEST_XFAILURE;
+    }
+
+    if (0 != false_positive_count) {
+       cairo_test_log (ctx, "Point sampling found %d false positives "
+                       "from cairo_in_fill()\n",
+                       false_positive_count);
+       ret = CAIRO_TEST_XFAILURE;
+    }
+
+    return ret;
+}
+
+CAIRO_TEST (in_fill_empty_trapezoid,
+           "Test that the tessellator isn't producing obviously empty trapezoids",
+           "in, trap", /* keywords */
+           NULL, /* requirements */
+           0, 0,
+           preamble, NULL)
diff --git a/test/in-fill-trapezoid.c b/test/in-fill-trapezoid.c
new file mode 100644 (file)
index 0000000..84b323a
--- /dev/null
@@ -0,0 +1,279 @@
+/*
+ * Copyright © 2008 Chris Wilson
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+
+static cairo_test_status_t
+preamble (cairo_test_context_t *ctx)
+{
+    cairo_test_status_t ret = CAIRO_TEST_SUCCESS;
+    cairo_surface_t *surface;
+    cairo_t *cr;
+
+    surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 0, 0);
+    cr = cairo_create (surface);
+    cairo_surface_destroy (surface);
+
+    cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD);
+
+    /* simple rectangle */
+    cairo_new_path (cr);
+    cairo_rectangle (cr, -10, -10, 20, 20);
+    if (! cairo_in_fill (cr, 0, 0)) {
+       cairo_test_log (ctx, "Error: Failed to find point inside rectangle\n");
+       ret = CAIRO_TEST_FAILURE;
+    }
+
+    /* rectangular boundary tests */
+    if (! cairo_in_fill (cr, -10, -10)) {
+       cairo_test_log (ctx, "Error: Failed to find top-left vertex inside rectangle\n");
+       ret = CAIRO_TEST_FAILURE;
+    }
+    if (! cairo_in_fill (cr, -10, 10)) {
+       cairo_test_log (ctx, "Error: Failed to find bottom-left vertex inside rectangle\n");
+       ret = CAIRO_TEST_FAILURE;
+    }
+    if (! cairo_in_fill (cr, 10, -10)) {
+       cairo_test_log (ctx, "Error: Failed to find top-right vertex inside rectangle\n");
+       ret = CAIRO_TEST_FAILURE;
+    }
+    if (! cairo_in_fill (cr, 10, 10)) {
+       cairo_test_log (ctx, "Error: Failed to find bottom-right vertex inside rectangle\n");
+       ret = CAIRO_TEST_FAILURE;
+    }
+    if (! cairo_in_fill (cr, -10, 0)) {
+       cairo_test_log (ctx, "Error: Failed to find left edge inside rectangle\n");
+       ret = CAIRO_TEST_FAILURE;
+    }
+    if (! cairo_in_fill (cr, 0, -10)) {
+       cairo_test_log (ctx, "Error: Failed to find top edge inside rectangle\n");
+       ret = CAIRO_TEST_FAILURE;
+    }
+    if (! cairo_in_fill (cr, 10, 0)) {
+       cairo_test_log (ctx, "Error: Failed to find right edge inside rectangle\n");
+       ret = CAIRO_TEST_FAILURE;
+    }
+    if (! cairo_in_fill (cr, 0, 10)) {
+       cairo_test_log (ctx, "Error: Failed to find bottom edge inside rectangle\n");
+       ret = CAIRO_TEST_FAILURE;
+    }
+
+    /* simple circle */
+    cairo_new_path (cr);
+    cairo_arc (cr, 0, 0, 10, 0, 2 * M_PI);
+    if (! cairo_in_fill (cr, 0, 0)) {
+       cairo_test_log (ctx, "Error: Failed to find point inside circle [even-odd]\n");
+       ret = CAIRO_TEST_FAILURE;
+    }
+
+    /* holey rectangle */
+    cairo_new_path (cr);
+    cairo_rectangle (cr, -10, -10, 20, 20);
+    cairo_rectangle (cr, -5, -5, 10, 10);
+    if (cairo_in_fill (cr, 0, 0)) {
+       cairo_test_log (ctx, "Error: Found an unexpected point inside rectangular eo-hole\n");
+       ret = CAIRO_TEST_FAILURE;
+    }
+
+    /* holey circle */
+    cairo_new_path (cr);
+    cairo_arc (cr, 0, 0, 10, 0, 2 * M_PI);
+    cairo_arc (cr, 0, 0, 5, 0, 2 * M_PI);
+    if (cairo_in_fill (cr, 0, 0)) {
+       cairo_test_log (ctx, "Error: Found an unexpected point inside circular eo-hole\n");
+       ret = CAIRO_TEST_FAILURE;
+    }
+
+
+    cairo_set_fill_rule (cr, CAIRO_FILL_RULE_WINDING);
+
+    /* simple rectangle */
+    cairo_new_path (cr);
+    cairo_rectangle (cr, -10, -10, 20, 20);
+    if (! cairo_in_fill (cr, 0, 0)) {
+       cairo_test_log (ctx, "Error: Failed to find point inside rectangle\n");
+       ret = CAIRO_TEST_FAILURE;
+    }
+
+    /* simple circle */
+    cairo_new_path (cr);
+    cairo_arc (cr, 0, 0, 10, 0, 2 * M_PI);
+    if (! cairo_in_fill (cr, 0, 0)) {
+       cairo_test_log (ctx, "Error: Failed to find point inside circle [nonzero]\n");
+       ret = CAIRO_TEST_FAILURE;
+    }
+
+    /* overlapping circle/rectangle */
+    cairo_new_path (cr);
+    cairo_rectangle (cr, -10, -10, 20, 20);
+    cairo_new_sub_path (cr);
+    cairo_arc (cr, 0, 0, 10, 0, 2 * M_PI);
+    if (! cairo_in_fill (cr, 0, 0)) {
+       cairo_test_log (ctx, "Error: Failed to find point inside circle+rectangle\n");
+       ret = CAIRO_TEST_FAILURE;
+    }
+
+    /* holey rectangle */
+    cairo_new_path (cr);
+    cairo_rectangle (cr, -10, -10, 20, 20);
+    cairo_rectangle (cr, 5, -5, -10, 10);
+    if (cairo_in_fill (cr, 0, 0)) {
+       cairo_test_log (ctx, "Error: Found an unexpected point inside rectangular non-zero-hole\n");
+       ret = CAIRO_TEST_FAILURE;
+    }
+
+    /* holey circle */
+    cairo_new_path (cr);
+    cairo_arc (cr, 0, 0, 10, 0, 2 * M_PI);
+    cairo_arc_negative (cr, 0, 0, 5, 0, -2 * M_PI);
+    if (cairo_in_fill (cr, 0, 0)) {
+       cairo_test_log (ctx, "Error: Found an unexpected point inside circular non-zero-hole\n");
+       ret = CAIRO_TEST_FAILURE;
+    }
+
+    /* not a holey circle */
+    cairo_new_path (cr);
+    cairo_arc (cr, 0, 0, 10, 0, 2 * M_PI);
+    cairo_arc (cr, 0, 0, 5, 0, 2 * M_PI);
+    if (! cairo_in_fill (cr, 0, 0)) {
+       cairo_test_log (ctx, "Error: Failed to find point inside two circles\n");
+       ret = CAIRO_TEST_FAILURE;
+    }
+
+    /* check off-centre */
+    cairo_new_path (cr);
+    cairo_arc (cr, 7.5, 0, 10, 0, 2 * M_PI);
+    cairo_arc_negative (cr, 7.5, 0, 5, 0, -2 * M_PI);
+    if (cairo_in_fill (cr, 7.5, 0)) {
+       cairo_test_log (ctx, "Error: Found an unexpected point inside off-centre-x circular non-zero-hole\n");
+       ret = CAIRO_TEST_FAILURE;
+    }
+    cairo_new_path (cr);
+    cairo_arc (cr, 0, 7.5, 10, 0, 2 * M_PI);
+    cairo_arc_negative (cr, 0, 7.5, 5, 0, -2 * M_PI);
+    if (cairo_in_fill (cr, 0, 7.5)) {
+       cairo_test_log (ctx, "Error: Found an unexpected point inside off-centre-y circular non-zero-hole\n");
+       ret = CAIRO_TEST_FAILURE;
+    }
+    cairo_new_path (cr);
+    cairo_arc (cr, 15, 0, 10, 0, 2 * M_PI);
+    if (! cairo_in_fill (cr, 15, 0)) {
+       cairo_test_log (ctx, "Error: Failed to find point inside off-centre-x circle\n");
+       ret = CAIRO_TEST_FAILURE;
+    }
+    cairo_new_path (cr);
+    cairo_arc (cr, 0, 15, 10, 0, 2 * M_PI);
+    if (! cairo_in_fill (cr, 0, 15)) {
+       cairo_test_log (ctx, "Error: Failed to find point inside off-centre-y circle\n");
+       ret = CAIRO_TEST_FAILURE;
+    }
+
+    /* simple rectangle */
+    cairo_new_path (cr);
+    cairo_rectangle (cr, 10, 0, 5, 5);
+    if (cairo_in_fill (cr, 0, 0)) {
+       cairo_test_log (ctx, "Error: Found an unexpected point outside rectangle\n");
+       ret = CAIRO_TEST_FAILURE;
+    }
+    if (cairo_in_fill (cr, 20, 20)) {
+       cairo_test_log (ctx, "Error: Found an unexpected point outside rectangle\n");
+       ret = CAIRO_TEST_FAILURE;
+    }
+    if (! cairo_in_fill (cr, 12.5, 2.5)) {
+       cairo_test_log (ctx, "Error: Failed to find point inside rectangle\n");
+       ret = CAIRO_TEST_FAILURE;
+    }
+
+    /* off-centre triangle */
+    cairo_new_path (cr);
+    cairo_move_to (cr, 10, 0);
+    cairo_line_to (cr, 15, 5);
+    cairo_line_to (cr, 5, 5);
+    cairo_close_path (cr);
+    if (cairo_in_fill (cr, 0, 0) ||
+       cairo_in_fill (cr, 5, 0) ||
+       cairo_in_fill (cr, 15, 0) ||
+       cairo_in_fill (cr, 20, 0) ||
+       cairo_in_fill (cr, 0, 10) ||
+       cairo_in_fill (cr, 10, 10) ||
+       cairo_in_fill (cr, 20, 10) ||
+       cairo_in_fill (cr, 7, 2.5) ||
+       cairo_in_fill (cr, 13, 2.5))
+    {
+       cairo_test_log (ctx,
+                       "Error: Found an unexpected point outside triangle\n"
+                       "\t(0, 0) -> %s\n"
+                       "\t(5, 0) -> %s\n"
+                       "\t(15, 0) -> %s\n"
+                       "\t(20, 0) -> %s\n"
+                       "\t(0, 10) -> %s\n"
+                       "\t(10, 10) -> %s\n"
+                       "\t(20, 10) -> %s\n"
+                       "\t(7, 2.5) -> %s\n"
+                       "\t(13, 2.5) -> %s\n",
+                       cairo_in_fill (cr, 0, 0) ? "inside" : "outside",
+                       cairo_in_fill (cr, 5, 0) ? "inside" : "outside",
+                       cairo_in_fill (cr, 15, 0) ? "inside" : "outside",
+                       cairo_in_fill (cr, 20, 0) ? "inside" : "outside",
+                       cairo_in_fill (cr, 0, 10) ? "inside" : "outside",
+                       cairo_in_fill (cr, 10, 10) ? "inside" : "outside",
+                       cairo_in_fill (cr, 20, 10) ? "inside" : "outside",
+                       cairo_in_fill (cr, 7, 2.5) ? "inside" : "outside",
+                       cairo_in_fill (cr, 13, 2.5) ? "inside" : "outside");
+       ret = CAIRO_TEST_FAILURE;
+    }
+    if (! cairo_in_fill (cr, 7.5, 2.5) ||
+       ! cairo_in_fill (cr, 12.5, 2.5) ||
+       ! cairo_in_fill (cr, 10, 5))
+    {
+       cairo_test_log (ctx,
+                       "Error: Failed to find point on triangle edge\n"
+                       "\t(7.5, 2.5) -> %s\n"
+                       "\t(12.5, 2.5) -> %s\n"
+                       "\t(10, 5) -> %s\n",
+                       cairo_in_fill (cr, 7.5, 2.5) ? "inside" : "outside",
+                       cairo_in_fill (cr, 12.5, 2.5) ? "inside" : "outside",
+                       cairo_in_fill (cr, 10, 5) ? "inside" : "outside");
+       ret = CAIRO_TEST_FAILURE;
+    }
+    if (! cairo_in_fill (cr, 8, 2.5) ||
+       ! cairo_in_fill (cr, 12, 2.5))
+    {
+       cairo_test_log (ctx, "Error: Failed to find point inside triangle\n");
+       ret = CAIRO_TEST_FAILURE;
+    }
+
+    cairo_destroy (cr);
+
+    return ret;
+}
+
+CAIRO_TEST (in_fill_trapezoid,
+           "Test cairo_in_fill",
+           "in, trap", /* keywords */
+           NULL, /* requirements */
+           0, 0,
+           preamble, NULL)
diff --git a/test/index.html b/test/index.html
new file mode 100644 (file)
index 0000000..54c65cd
--- /dev/null
@@ -0,0 +1,42 @@
+<html><head>
+<title>Cairo Test Results</title>
+<style type="text/css">
+#HcolHeader { text-align: center; }
+
+img { max-width: 15em; min-width: 3em; min-height: 3em; margin: 3px; }
+a img { border: solid 1px #FFF; }
+
+td { vertical-align: top; }
+span { cursor: pointer; }
+
+td.PASS { background-color: #0B0; min-width: 1em; }
+span.PASS { color: #0B0; }
+
+td.NEW { background-color: #B70; }
+span.NEW { color: #B70; }
+
+td.FAIL { background-color: #B00; }
+span.FAIL { color: #D00; }
+
+td.XFAIL { background-color: #BB0; }
+span.XFAIL { color: #BB0; }
+
+td.UNTESTED { background-color: #555; }
+span.UNTESTED { color: #555; }
+
+td.CRASHED { background-color: #F00; color: #FF0; }
+span.CRASHED { color: #F00; }
+
+.test { }
+.target { }
+.format { }
+.offset { }
+.similar { }
+
+</style>
+<script language="JavaScript" src="testtable.js"></script>
+</head>
+<body oncontextmenu='ignoreEvent (event)' onmouseup='noDrag (event)'>
+<table id="testTable" border="1"></table>
+</body>
+</html>
diff --git a/test/infinite-join.c b/test/infinite-join.c
new file mode 100644 (file)
index 0000000..a573f56
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * Copyright © 2006 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of the
+ * copyright holders not be used in advertising or publicity
+ * pertaining to distribution of the software without specific,
+ * written prior permission. The copyright holders make no
+ * representations about the suitability of this software for any
+ * purpose.  It is provided "as is" without express or implied
+ * warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ * SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ */
+
+/* Test case for bug #8379:
+ *
+ *     infinite loop when stroking
+ *     https://bugs.freedesktop.org/show_bug.cgi?id=8379
+ */
+
+#include "cairo-test.h"
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    /* Paint white, then draw in black. */
+    cairo_set_source_rgb (cr, 1, 1, 1); /* white */
+    cairo_paint (cr);
+    cairo_set_source_rgb (cr, 0, 0, 0); /* black */
+
+    cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND);
+
+    /* scaling 2 times causes a slight rounding error in the ctm.
+     * Without that, the bug doesn't happen. */
+    cairo_scale (cr, 20 / 100., 20 / 100.);
+    cairo_scale (cr, 1. / 20, 1. / 20);
+
+    cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND);
+    cairo_set_line_width (cr, 20);
+
+    cairo_translate (cr, -18300, -13200);
+
+    cairo_new_path (cr);
+    cairo_move_to (cr, 18928, 13843);
+    cairo_line_to (cr, 18500, 13843);
+    cairo_line_to (cr, 18500, 13400);
+    cairo_line_to (cr, 18928, 13400);
+    cairo_line_to (cr, 18928, 13843);
+    cairo_stroke (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (infinite_join,
+           "Test case for infinite loop when stroking with round joins",
+           "stroke", /* keywords */
+           NULL,
+           8, 8,
+           NULL, draw)
diff --git a/test/invalid-matrix.c b/test/invalid-matrix.c
new file mode 100644 (file)
index 0000000..6cfaedf
--- /dev/null
@@ -0,0 +1,378 @@
+/*
+ * Copyright © 2007 Red Hat, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Carl Worth <cworth@cworth.org>
+ */
+
+#define _ISOC99_SOURCE /* for INFINITY */
+#define _GNU_SOURCE 1  /* for fedisableeexcept() et al */
+
+#include "cairo-test.h"
+
+#if !defined(INFINITY)
+#define INFINITY HUGE_VAL
+#endif
+
+#if HAVE_FENV_H
+# include <fenv.h>
+#endif
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    const cairo_test_context_t *ctx = cairo_test_get_context (cr);
+    cairo_status_t status;
+    cairo_surface_t *target;
+    cairo_font_face_t *font_face;
+    cairo_font_options_t *font_options;
+    cairo_scaled_font_t *scaled_font;
+    cairo_pattern_t *pattern;
+    cairo_t *cr2;
+    cairo_matrix_t identity, bogus, inf, invalid = {
+       4.0, 4.0,
+       4.0, 4.0,
+       4.0, 4.0
+    };
+
+#define CHECK_STATUS(status, function_name)                                            \
+if ((status) == CAIRO_STATUS_SUCCESS) {                                                        \
+    cairo_test_log (ctx, "Error: %s with invalid matrix passed\n",                             \
+                   (function_name));                                                   \
+    return CAIRO_TEST_FAILURE;                                                         \
+} else if ((status) != CAIRO_STATUS_INVALID_MATRIX) {                                  \
+    cairo_test_log (ctx, "Error: %s with invalid matrix returned unexpected status "   \
+                   "(%d): %s\n",                                                       \
+                   (function_name),                                                    \
+                   status,                                                             \
+                   cairo_status_to_string (status));                                   \
+    return CAIRO_TEST_FAILURE;                                                         \
+}
+
+    /* clear floating point exceptions (added by cairo_test_init()) */
+#if HAVE_FEDISABLEEXCEPT
+    fedisableexcept (FE_INVALID);
+#endif
+
+    /* create a bogus matrix and check results of attempted inversion */
+    bogus.x0 = bogus.xy = bogus.xx = cairo_test_NaN ();
+    bogus.y0 = bogus.yx = bogus.yy = bogus.xx;
+    status = cairo_matrix_invert (&bogus);
+    CHECK_STATUS (status, "cairo_matrix_invert(NaN)");
+
+    inf.x0 = inf.xy = inf.xx = INFINITY;
+    inf.y0 = inf.yx = inf.yy = inf.xx;
+    status = cairo_matrix_invert (&inf);
+    CHECK_STATUS (status, "cairo_matrix_invert(infinity)");
+
+    /* test cairo_matrix_invert with invalid matrix */
+    status = cairo_matrix_invert (&invalid);
+    CHECK_STATUS (status, "cairo_matrix_invert(invalid)");
+
+
+    cairo_matrix_init_identity (&identity);
+
+    target = cairo_get_group_target (cr);
+
+    /* test cairo_transform with invalid matrix */
+    cr2 = cairo_create (target);
+    cairo_transform (cr2, &invalid);
+
+    status = cairo_status (cr2);
+    cairo_destroy (cr2);
+    CHECK_STATUS (status, "cairo_transform(invalid)");
+
+    /* test cairo_transform with bogus matrix */
+    cr2 = cairo_create (target);
+    cairo_transform (cr2, &bogus);
+
+    status = cairo_status (cr2);
+    cairo_destroy (cr2);
+    CHECK_STATUS (status, "cairo_transform(NaN)");
+
+    /* test cairo_transform with ∞ matrix */
+    cr2 = cairo_create (target);
+    cairo_transform (cr2, &inf);
+
+    status = cairo_status (cr2);
+    cairo_destroy (cr2);
+    CHECK_STATUS (status, "cairo_transform(infinity)");
+
+
+    /* test cairo_set_matrix with invalid matrix */
+    cr2 = cairo_create (target);
+    cairo_set_matrix (cr2, &invalid);
+
+    status = cairo_status (cr2);
+    cairo_destroy (cr2);
+    CHECK_STATUS (status, "cairo_set_matrix(invalid)");
+
+    /* test cairo_set_matrix with bogus matrix */
+    cr2 = cairo_create (target);
+    cairo_set_matrix (cr2, &bogus);
+
+    status = cairo_status (cr2);
+    cairo_destroy (cr2);
+    CHECK_STATUS (status, "cairo_set_matrix(NaN)");
+
+    /* test cairo_set_matrix with ∞ matrix */
+    cr2 = cairo_create (target);
+    cairo_set_matrix (cr2, &inf);
+
+    status = cairo_status (cr2);
+    cairo_destroy (cr2);
+    CHECK_STATUS (status, "cairo_set_matrix(infinity)");
+
+
+    /* test cairo_set_font_matrix with invalid matrix */
+    cr2 = cairo_create (target);
+    cairo_set_font_matrix (cr2, &invalid);
+
+    /* draw some text to force the font to be resolved */
+    cairo_show_text (cr2, "hello");
+
+    status = cairo_status (cr2);
+    cairo_destroy (cr2);
+    CHECK_STATUS (status, "cairo_set_font_matrix(invalid)");
+
+    /* test cairo_set_font_matrix with bogus matrix */
+    cr2 = cairo_create (target);
+    cairo_set_font_matrix (cr2, &bogus);
+
+    /* draw some text to force the font to be resolved */
+    cairo_show_text (cr2, "hello");
+
+    status = cairo_status (cr2);
+    cairo_destroy (cr2);
+    CHECK_STATUS (status, "cairo_set_font_matrix(NaN)");
+
+    /* test cairo_set_font_matrix with ∞ matrix */
+    cr2 = cairo_create (target);
+    cairo_set_font_matrix (cr2, &inf);
+
+    /* draw some text to force the font to be resolved */
+    cairo_show_text (cr2, "hello");
+
+    status = cairo_status (cr2);
+    cairo_destroy (cr2);
+    CHECK_STATUS (status, "cairo_set_font_matrix(infinity)");
+
+
+    /* test cairo_scaled_font_create with invalid matrix */
+    cr2 = cairo_create (target);
+    font_face = cairo_get_font_face (cr2);
+    font_options = cairo_font_options_create ();
+    cairo_get_font_options (cr, font_options);
+    scaled_font = cairo_scaled_font_create (font_face,
+                                           &invalid,
+                                           &identity,
+                                           font_options);
+    status = cairo_scaled_font_status (scaled_font);
+    CHECK_STATUS (status, "cairo_scaled_font_create(invalid)");
+
+    cairo_scaled_font_destroy (scaled_font);
+
+    scaled_font = cairo_scaled_font_create (font_face,
+                                           &identity,
+                                           &invalid,
+                                           font_options);
+    status = cairo_scaled_font_status (scaled_font);
+    CHECK_STATUS (status, "cairo_scaled_font_create(invalid)");
+
+    cairo_scaled_font_destroy (scaled_font);
+    cairo_font_options_destroy (font_options);
+    cairo_destroy (cr2);
+
+    /* test cairo_scaled_font_create with bogus matrix */
+    cr2 = cairo_create (target);
+    font_face = cairo_get_font_face (cr2);
+    font_options = cairo_font_options_create ();
+    cairo_get_font_options (cr, font_options);
+    scaled_font = cairo_scaled_font_create (font_face,
+                                           &bogus,
+                                           &identity,
+                                           font_options);
+    status = cairo_scaled_font_status (scaled_font);
+    CHECK_STATUS (status, "cairo_scaled_font_create(NaN)");
+
+    cairo_scaled_font_destroy (scaled_font);
+
+    scaled_font = cairo_scaled_font_create (font_face,
+                                           &identity,
+                                           &bogus,
+                                           font_options);
+    status = cairo_scaled_font_status (scaled_font);
+    CHECK_STATUS (status, "cairo_scaled_font_create(NaN)");
+
+    cairo_scaled_font_destroy (scaled_font);
+    cairo_font_options_destroy (font_options);
+    cairo_destroy (cr2);
+
+    /* test cairo_scaled_font_create with ∞ matrix */
+    cr2 = cairo_create (target);
+    font_face = cairo_get_font_face (cr2);
+    font_options = cairo_font_options_create ();
+    cairo_get_font_options (cr, font_options);
+    scaled_font = cairo_scaled_font_create (font_face,
+                                           &inf,
+                                           &identity,
+                                           font_options);
+    status = cairo_scaled_font_status (scaled_font);
+    CHECK_STATUS (status, "cairo_scaled_font_create(infinity)");
+
+    cairo_scaled_font_destroy (scaled_font);
+
+    scaled_font = cairo_scaled_font_create (font_face,
+                                           &identity,
+                                           &inf,
+                                           font_options);
+    status = cairo_scaled_font_status (scaled_font);
+    CHECK_STATUS (status, "cairo_scaled_font_create(infinity)");
+
+    cairo_scaled_font_destroy (scaled_font);
+    cairo_font_options_destroy (font_options);
+    cairo_destroy (cr2);
+
+
+    /* test cairo_pattern_set_matrix with invalid matrix */
+    pattern = cairo_pattern_create_rgb (1.0, 1.0, 1.0);
+    cairo_pattern_set_matrix (pattern, &invalid);
+    status = cairo_pattern_status (pattern);
+    CHECK_STATUS (status, "cairo_pattern_set_matrix(invalid)");
+    cairo_pattern_destroy (pattern);
+
+    /* test cairo_pattern_set_matrix with bogus matrix */
+    pattern = cairo_pattern_create_rgb (1.0, 1.0, 1.0);
+    cairo_pattern_set_matrix (pattern, &bogus);
+    status = cairo_pattern_status (pattern);
+    CHECK_STATUS (status, "cairo_pattern_set_matrix(NaN)");
+    cairo_pattern_destroy (pattern);
+
+    /* test cairo_pattern_set_matrix with ∞ matrix */
+    pattern = cairo_pattern_create_rgb (1.0, 1.0, 1.0);
+    cairo_pattern_set_matrix (pattern, &inf);
+    status = cairo_pattern_status (pattern);
+    CHECK_STATUS (status, "cairo_pattern_set_matrix(infinity)");
+    cairo_pattern_destroy (pattern);
+
+
+    /* test invalid transformations */
+    cr2 = cairo_create (target);
+    cairo_translate (cr2, bogus.xx, bogus.yy);
+    CHECK_STATUS (status, "cairo_translate(NaN, NaN)");
+    cairo_destroy (cr2);
+
+    cr2 = cairo_create (target);
+    cairo_translate (cr2, 0, bogus.yy);
+    CHECK_STATUS (status, "cairo_translate(0, NaN)");
+    cairo_destroy (cr2);
+
+    cr2 = cairo_create (target);
+    cairo_translate (cr2, bogus.xx, 0);
+    CHECK_STATUS (status, "cairo_translate(NaN, 0)");
+    cairo_destroy (cr2);
+
+    cr2 = cairo_create (target);
+    cairo_translate (cr2, inf.xx, inf.yy);
+    CHECK_STATUS (status, "cairo_translate(∞, ∞)");
+    cairo_destroy (cr2);
+
+    cr2 = cairo_create (target);
+    cairo_translate (cr2, 0, inf.yy);
+    CHECK_STATUS (status, "cairo_translate(0, ∞)");
+    cairo_destroy (cr2);
+
+    cr2 = cairo_create (target);
+    cairo_translate (cr2, inf.xx, 0);
+    CHECK_STATUS (status, "cairo_translate(∞, 0)");
+    cairo_destroy (cr2);
+
+
+    cr2 = cairo_create (target);
+    cairo_scale (cr2, bogus.xx, bogus.yy);
+    CHECK_STATUS (status, "cairo_scale(NaN, NaN)");
+    cairo_destroy (cr2);
+
+    cr2 = cairo_create (target);
+    cairo_scale (cr2, 1, bogus.yy);
+    CHECK_STATUS (status, "cairo_scale(1, NaN)");
+    cairo_destroy (cr2);
+
+    cr2 = cairo_create (target);
+    cairo_scale (cr2, bogus.xx, 1);
+    CHECK_STATUS (status, "cairo_scale(NaN, 1)");
+    cairo_destroy (cr2);
+
+    cr2 = cairo_create (target);
+    cairo_scale (cr2, inf.xx, inf.yy);
+    CHECK_STATUS (status, "cairo_scale(∞, ∞)");
+    cairo_destroy (cr2);
+
+    cr2 = cairo_create (target);
+    cairo_scale (cr2, 1, inf.yy);
+    CHECK_STATUS (status, "cairo_scale(1, ∞)");
+    cairo_destroy (cr2);
+
+    cr2 = cairo_create (target);
+    cairo_scale (cr2, inf.xx, 1);
+    CHECK_STATUS (status, "cairo_scale(∞, 1)");
+    cairo_destroy (cr2);
+
+    cr2 = cairo_create (target);
+    cairo_scale (cr2, bogus.xx, bogus.yy);
+    CHECK_STATUS (status, "cairo_scale(0, 0)");
+    cairo_destroy (cr2);
+
+    cr2 = cairo_create (target);
+    cairo_scale (cr2, 1, bogus.yy);
+    CHECK_STATUS (status, "cairo_scale(1, 0)");
+    cairo_destroy (cr2);
+
+    cr2 = cairo_create (target);
+    cairo_scale (cr2, bogus.xx, 1);
+    CHECK_STATUS (status, "cairo_scale(0, 1)");
+    cairo_destroy (cr2);
+
+
+    cr2 = cairo_create (target);
+    cairo_rotate (cr2, bogus.xx);
+    CHECK_STATUS (status, "cairo_rotate(NaN)");
+    cairo_destroy (cr2);
+
+    cr2 = cairo_create (target);
+    cairo_rotate (cr2, inf.xx);
+    CHECK_STATUS (status, "cairo_rotate(∞)");
+    cairo_destroy (cr2);
+
+#if HAVE_FECLEAREXCEPT
+    feclearexcept (FE_INVALID);
+#endif
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (invalid_matrix,
+           "Test that all relevant public functions return CAIRO_STATUS_INVALID_MATRIX as appropriate",
+           "api, matrix", /* keywords */
+           NULL, /* requirements */
+           0, 0,
+           NULL, draw)
diff --git a/test/inverse-text.c b/test/inverse-text.c
new file mode 100644 (file)
index 0000000..c8d7aea
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * Copyright © 2006 Red Hat, Inc.
+ * Copyright © 2010 Intel Corporation
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ *         Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+
+#define TEXT_SIZE 12
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    /* This is just the inverse of select-font-face.c */
+    cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
+    cairo_paint (cr);
+
+    cairo_set_source_rgb (cr, 1, 1, 1);
+
+    cairo_set_font_size (cr, TEXT_SIZE);
+    cairo_move_to (cr, 0, TEXT_SIZE);
+
+    cairo_select_font_face (cr, CAIRO_TEST_FONT_FAMILY " Serif",
+                           CAIRO_FONT_SLANT_NORMAL,
+                           CAIRO_FONT_WEIGHT_NORMAL);
+    cairo_show_text (cr, "i-am-serif");
+
+    cairo_select_font_face (cr, CAIRO_TEST_FONT_FAMILY " Sans",
+                           CAIRO_FONT_SLANT_NORMAL,
+                           CAIRO_FONT_WEIGHT_NORMAL);
+    cairo_show_text (cr, " i-am-sans");
+
+    cairo_select_font_face (cr, CAIRO_TEST_FONT_FAMILY " Sans Mono",
+                           CAIRO_FONT_SLANT_NORMAL,
+                           CAIRO_FONT_WEIGHT_NORMAL);
+    cairo_show_text (cr, " i-am-mono");
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (inverse_text,
+           "Tests rendering of inverse text (white-on-black)",
+           "font, text", /* keywords */
+           NULL, /* requirements */
+           192, TEXT_SIZE + 4,
+           NULL, draw)
diff --git a/test/joins-loop.c b/test/joins-loop.c
new file mode 100644 (file)
index 0000000..f0b7bc1
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * Copyright © 2011 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+
+#define LINE_WIDTH     10.
+#define SIZE           (8 * LINE_WIDTH)
+#define PAD            (1 * LINE_WIDTH)
+
+
+static void
+make_path (cairo_t *cr)
+{
+    cairo_move_to (cr, 0, 0);
+    cairo_rel_curve_to (cr,
+                       SIZE, 0,
+                       0, SIZE,
+                       SIZE, SIZE);
+    cairo_rel_line_to (cr, -SIZE, 0);
+    cairo_rel_curve_to (cr,
+                       SIZE, 0,
+                       0, -SIZE,
+                       SIZE, -SIZE);
+    cairo_close_path (cr);
+}
+
+static void
+draw_joins (cairo_t *cr)
+{
+    cairo_save (cr);
+    cairo_translate (cr, PAD, PAD);
+
+    make_path (cr);
+    cairo_set_line_join (cr, CAIRO_LINE_JOIN_BEVEL);
+    cairo_stroke (cr);
+    cairo_translate (cr, SIZE + PAD, 0.);
+
+    make_path (cr);
+    cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND);
+    cairo_stroke (cr);
+    cairo_translate (cr, SIZE + PAD, 0.);
+
+    make_path (cr);
+    cairo_set_line_join (cr, CAIRO_LINE_JOIN_MITER);
+    cairo_stroke (cr);
+    cairo_translate (cr, SIZE + PAD, 0.);
+
+    cairo_restore (cr);
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
+    cairo_paint (cr);
+
+    cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
+    cairo_set_line_width (cr, LINE_WIDTH);
+
+    draw_joins (cr);
+
+    /* and reflect to generate the opposite vertex ordering */
+    cairo_translate (cr, 0, height);
+    cairo_scale (cr, 1, -1);
+
+    draw_joins (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (joins_loop,
+           "A loopy concave shape",
+           "stroke", /* keywords */
+           NULL, /* requirements */
+           3*(SIZE+PAD)+PAD, 2*(SIZE+PAD)+2*PAD,
+           NULL, draw)
+
diff --git a/test/joins-retrace.c b/test/joins-retrace.c
new file mode 100644 (file)
index 0000000..9a2cf70
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ * Copyright © 2011 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+
+#define LINE_WIDTH     10.
+#define SIZE           (8 * LINE_WIDTH)
+#define PAD            (1 * LINE_WIDTH)
+
+
+static void
+make_path (cairo_t *cr)
+{
+    cairo_move_to (cr, 0, SIZE/2 + LINE_WIDTH);
+    cairo_rel_line_to (cr, SIZE, 0);
+    cairo_rel_line_to (cr, -SIZE, 0);
+    cairo_close_path (cr);
+
+    cairo_move_to (cr, 3*SIZE/4, 0);
+    cairo_rel_line_to (cr, -SIZE/2, SIZE);
+    cairo_rel_line_to (cr, SIZE/2, -SIZE);
+    cairo_close_path (cr);
+
+    cairo_move_to (cr, 0, SIZE/2-LINE_WIDTH);
+    cairo_rel_curve_to (cr,
+                       SIZE/2, -2*LINE_WIDTH,
+                       SIZE/2, 2*LINE_WIDTH,
+                       SIZE, 0);
+    cairo_rel_curve_to (cr,
+                       -SIZE/2, 2*LINE_WIDTH,
+                       -SIZE/2, -2*LINE_WIDTH,
+                       -SIZE, 0);
+    cairo_close_path (cr);
+}
+
+static void
+draw_joins (cairo_t *cr)
+{
+    cairo_save (cr);
+    cairo_translate (cr, PAD, PAD);
+
+    make_path (cr);
+    cairo_set_line_join (cr, CAIRO_LINE_JOIN_BEVEL);
+    cairo_stroke (cr);
+    cairo_translate (cr, SIZE + PAD, 0.);
+
+    make_path (cr);
+    cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND);
+    cairo_stroke (cr);
+    cairo_translate (cr, SIZE + PAD, 0.);
+
+    make_path (cr);
+    cairo_set_line_join (cr, CAIRO_LINE_JOIN_MITER);
+    cairo_stroke (cr);
+    cairo_translate (cr, SIZE + PAD, 0.);
+
+    cairo_restore (cr);
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
+    cairo_paint (cr);
+
+    cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
+    cairo_set_line_width (cr, LINE_WIDTH);
+
+    draw_joins (cr);
+
+    /* and reflect to generate the opposite vertex ordering */
+    cairo_translate (cr, 0, height);
+    cairo_scale (cr, 1, -1);
+
+    draw_joins (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (joins_retrace,
+           "A shape that repeats upon itself",
+           "stroke", /* keywords */
+           NULL, /* requirements */
+           3*(SIZE+PAD)+PAD, 2*(SIZE+PAD)+2*PAD,
+           NULL, draw)
+
diff --git a/test/joins-star.c b/test/joins-star.c
new file mode 100644 (file)
index 0000000..17c1b4e
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * Copyright © 2011 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+
+#define LINE_WIDTH     10.
+#define SIZE           (8 * LINE_WIDTH)
+#define PAD            (1 * LINE_WIDTH)
+
+
+static void
+make_path (cairo_t *cr)
+{
+#define PROTRUSION 20
+    cairo_move_to (cr, SIZE/2-PROTRUSION, LINE_WIDTH);
+    cairo_line_to (cr, SIZE-LINE_WIDTH, SIZE/2+PROTRUSION);
+    cairo_line_to (cr, SIZE-LINE_WIDTH, SIZE/2-PROTRUSION);
+    cairo_line_to (cr, SIZE/2-PROTRUSION, SIZE-LINE_WIDTH);
+    cairo_line_to (cr, SIZE/2+PROTRUSION, SIZE-LINE_WIDTH);
+    cairo_line_to (cr, LINE_WIDTH, SIZE/2-PROTRUSION);
+    cairo_line_to (cr, LINE_WIDTH, SIZE/2+PROTRUSION);
+    cairo_line_to (cr, SIZE/2+PROTRUSION, LINE_WIDTH);
+    cairo_close_path (cr);
+}
+
+static void
+draw_joins (cairo_t *cr)
+{
+    cairo_save (cr);
+    cairo_translate (cr, PAD, PAD);
+
+    make_path (cr);
+    cairo_set_line_join (cr, CAIRO_LINE_JOIN_BEVEL);
+    cairo_stroke (cr);
+    cairo_translate (cr, SIZE + PAD, 0.);
+
+    make_path (cr);
+    cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND);
+    cairo_stroke (cr);
+    cairo_translate (cr, SIZE + PAD, 0.);
+
+    make_path (cr);
+    cairo_set_line_join (cr, CAIRO_LINE_JOIN_MITER);
+    cairo_stroke (cr);
+    cairo_translate (cr, SIZE + PAD, 0.);
+
+    cairo_restore (cr);
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
+    cairo_paint (cr);
+
+    cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
+    cairo_set_line_width (cr, LINE_WIDTH);
+
+    draw_joins (cr);
+
+    /* and reflect to generate the opposite vertex ordering */
+    cairo_translate (cr, 0, height);
+    cairo_scale (cr, 1, -1);
+
+    draw_joins (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (joins_star,
+           "A nice and simple concave shape",
+           "stroke", /* keywords */
+           NULL, /* requirements */
+           3*(SIZE+PAD)+PAD, 2*(SIZE+PAD)+PAD,
+           NULL, draw)
+
diff --git a/test/joins.c b/test/joins.c
new file mode 100644 (file)
index 0000000..66847dc
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ * Copyright © 2008 Chris Wilson
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Chris Wilson not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Chris Wilson makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * CHRIS WILSON DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL CHRIS WILSON BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+
+#define LINE_WIDTH     12.
+#define SIZE           (5 * LINE_WIDTH)
+#define PAD            (2 * LINE_WIDTH)
+
+static void
+make_path (cairo_t *cr)
+{
+    const struct {
+       double x, y;
+    } scales[] = {
+       {  1,  1 },
+       { -1,  1 },
+       {  1, -1 },
+       { -1, -1 },
+    };
+    unsigned int i, j;
+
+    for (j = 0; j < ARRAY_LENGTH (scales); j++) {
+       cairo_save (cr);
+       /* include reflections to flip the orientation of the join */
+       cairo_scale (cr, scales[j].x, scales[j].y);
+       for (i = 0; i < 3; i++) {
+           cairo_new_sub_path (cr);
+           cairo_move_to (cr, 0, -9*LINE_WIDTH/4 - 2);
+           cairo_line_to (cr,  0, -2*LINE_WIDTH - 2);
+           cairo_line_to (cr,  LINE_WIDTH/4, -2*LINE_WIDTH - 2);
+           cairo_rotate (cr, M_PI / 4.);
+       }
+       cairo_restore (cr);
+    }
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_line_join_t join;
+
+    cairo_save (cr);
+    cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); /* white */
+    cairo_paint (cr);
+    cairo_restore (cr);
+
+    cairo_set_line_width (cr, LINE_WIDTH);
+    cairo_translate (cr, PAD + SIZE / 2., PAD + SIZE / 2.);
+
+    for (join = CAIRO_LINE_JOIN_MITER; join <= CAIRO_LINE_JOIN_BEVEL; join++) {
+       cairo_save (cr);
+
+       cairo_set_line_join (cr, join);
+
+       cairo_set_line_cap (cr, CAIRO_LINE_CAP_BUTT);
+       make_path (cr);
+       cairo_stroke (cr);
+
+       cairo_translate (cr, 0, SIZE + PAD);
+
+       cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
+       make_path (cr);
+       cairo_stroke (cr);
+
+       cairo_translate (cr, 0, SIZE + PAD);
+
+       cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE);
+       make_path (cr);
+       cairo_stroke (cr);
+
+       cairo_restore (cr);
+
+       cairo_translate (cr, SIZE + PAD, 0);
+    }
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (joins,
+           "Test joins",
+           "stroke joins", /* keywords */
+           NULL, /* requirements */
+           3 * (PAD + SIZE) + PAD,
+           3 * (PAD + SIZE) + PAD,
+           NULL, draw)
+
diff --git a/test/jp2.jp2 b/test/jp2.jp2
new file mode 100644 (file)
index 0000000..fe8dd6e
Binary files /dev/null and b/test/jp2.jp2 differ
diff --git a/test/jpeg.jpg b/test/jpeg.jpg
new file mode 100644 (file)
index 0000000..a1bac1c
Binary files /dev/null and b/test/jpeg.jpg differ
diff --git a/test/large-clip.c b/test/large-clip.c
new file mode 100644 (file)
index 0000000..d2380de
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright © 2008 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Soren Sandmann <sandmann@redhat.com>
+ */
+
+#include "cairo-test.h"
+
+#define SIZE 100
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_set_source_rgb (cr, 0, 0, 1);
+    cairo_paint (cr);
+
+    cairo_rectangle (cr, 0, 0, 65536 + 25, 65536 + 25);
+    cairo_clip (cr);
+
+    cairo_set_source_rgb (cr, 1, 0, 0);
+    cairo_paint (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (large_clip,
+           "Incorrect clipping when the clip rectangle doesn't fit in 16 bits signed",
+           "clip, stress", /* keywords */
+           NULL, /* requirements */
+           SIZE, SIZE,
+           NULL, draw)
diff --git a/test/large-font.c b/test/large-font.c
new file mode 100644 (file)
index 0000000..0a13944
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * Copyright © 2008 Red Hat, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ */
+
+/* Bug history:
+ *
+ * 2008-05-23: Caolan McNamara noticed a bug in OpenOffice.org where,
+ *             when using a very large font, space would be left for a
+ *            glyph but it would actually be rendered in the wrong
+ *            place. He wrote a minimal test case and posted the bug
+ *            here:
+ *
+ *                     corrupt glyph positions with large font
+ *                     https://bugzilla.redhat.com/show_bug.cgi?id=448104
+ *
+ * 2008-05-23: Carl Worth wrote this test for the cairo test suite to
+ *             exercise the bug.
+ */
+
+#include "cairo-test.h"
+
+#define WIDTH  800
+#define HEIGHT 800
+#define TEXT_SIZE 10000
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    /* paint white so we don't need separate ref images for
+     * RGB24 and ARGB32 */
+    cairo_set_source_rgb (cr, 1., 1., 1.);
+    cairo_paint (cr);
+
+    cairo_select_font_face (cr, CAIRO_TEST_FONT_FAMILY " Sans",
+                           CAIRO_FONT_SLANT_NORMAL,
+                           CAIRO_FONT_WEIGHT_NORMAL);
+    cairo_set_font_size (cr, TEXT_SIZE);
+
+    cairo_set_source_rgb (cr, 0, 0, 0);
+    cairo_move_to (cr, -TEXT_SIZE / 2, TEXT_SIZE / 2);
+    cairo_show_text (cr, "xW");
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (large_font,
+           "Draws a very large font to exercise a glyph-positioning bug",
+           "stress, font", /* keywords */
+           NULL, /* requirements */
+           WIDTH, HEIGHT,
+           NULL, draw)
diff --git a/test/large-source-roi.c b/test/large-source-roi.c
new file mode 100644 (file)
index 0000000..dfd3554
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * Copyright © 2009 Joonas Pihlaja
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting documentation, and
+ * that the name of the copyright holders not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission.  The copyright holders make no representations
+ * about the suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ */
+
+#include "cairo-test.h"
+
+/* This test attempts to trigger failures in those clone_similar
+ * backend methods that have size restrictions. */
+
+static cairo_surface_t *
+create_large_source (int width, int height)
+{
+    cairo_surface_t *surface;
+    cairo_t *cr;
+
+    surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, height);
+    cr = cairo_create (surface);
+    cairo_surface_destroy (surface);
+
+    cairo_set_source_rgb (cr, 1,0,0); /* red */
+    cairo_paint (cr);
+    surface = cairo_surface_reference (cairo_get_target (cr));
+    cairo_destroy (cr);
+
+    return surface;
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_surface_t *source;
+    /* Since 1cc750ed92a936d84b47cac696aaffd226e1c02e pixman will not
+     * paint on the source surface if source_width > 30582. */
+    double source_width = 30000.0;
+
+    cairo_set_source_rgb (cr, 1,1,1);
+    cairo_paint (cr);
+
+    /* Create an excessively wide source image, all red. */
+    source = create_large_source (source_width, height);
+
+    /* Set a transform so that the source is scaled down to fit in the
+     * destination horizontally and then paint the entire source to
+     * the context. */
+    cairo_scale (cr, width/source_width, 1.0);
+    cairo_set_source_surface (cr, source, 0, 0);
+    cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+    cairo_paint (cr);
+
+    cairo_surface_destroy (source);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (large_source_roi,
+           "Uses a all of a large source image.",
+           "stress, source", /* keywords */
+           NULL, /* requirements */
+           7, 7,
+           NULL, draw)
diff --git a/test/large-source.c b/test/large-source.c
new file mode 100644 (file)
index 0000000..2b6b84f
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * Copyright © Chris Wilson, 2008
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Chris Wilson not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Chris Wilson makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * CHRIS WILSON DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL CHRIS WILSON BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Authors: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+
+/* This is a test case for the following bug:
+ *
+ *     crafted gif file will crash firefox
+ *     [XError: 'BadAlloc (insufficient resources for operation)']
+ *     https://bugzilla.mozilla.org/show_bug.cgi?id=424333
+ */
+
+#ifdef WORDS_BIGENDIAN
+#define RED_MASK 0xA0
+#define GREEN_MASK 0xA
+#else
+#define RED_MASK 0x5
+#define GREEN_MASK 0x50
+#endif
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_surface_t *surface;
+    unsigned char *data;
+
+    cairo_set_source_rgb (cr, 0, 0, 1); /* blue */
+    cairo_paint (cr);
+
+    surface = cairo_image_surface_create (CAIRO_FORMAT_A1, 32000, 20);
+    data = cairo_image_surface_get_data (surface);
+    if (data != NULL) {
+       int stride = cairo_image_surface_get_stride (surface);
+       int width  = cairo_image_surface_get_width  (surface);
+       int height = cairo_image_surface_get_height (surface);
+       int x, y;
+
+       for (y = 0; y < height; y++) {
+           for (x = 0; x < (width + 7) / 8; x++)
+               data[x] = RED_MASK;
+           data += stride;
+       }
+        cairo_surface_mark_dirty (surface);
+    }
+
+    cairo_set_source_rgb (cr, 1, 0, 0); /* red */
+    cairo_mask_surface (cr, surface, 0, 0);
+    cairo_surface_destroy (surface);
+
+    surface = cairo_image_surface_create (CAIRO_FORMAT_A1, 20, 32000);
+    data = cairo_image_surface_get_data (surface);
+    if (data != NULL) {
+       int stride = cairo_image_surface_get_stride (surface);
+       int width  = cairo_image_surface_get_width  (surface);
+       int height = cairo_image_surface_get_height (surface);
+       int x, y;
+
+       for (y = 0; y < height; y++) {
+           for (x = 0; x < (width + 7) / 8; x++)
+               data[x] = GREEN_MASK;
+           data += stride;
+       }
+        cairo_surface_mark_dirty (surface);
+    }
+
+    cairo_set_source_rgb (cr, 0, 1, 0); /* green */
+    cairo_mask_surface (cr, surface, 0, 0);
+    cairo_surface_destroy (surface);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (large_source,
+           "Exercises mozilla bug 424333 - handling of massive images",
+           "stress, source", /* keywords */
+           NULL, /* requirements */
+           20, 20,
+           NULL, draw)
diff --git a/test/large-twin-antialias-mixed.c b/test/large-twin-antialias-mixed.c
new file mode 100644 (file)
index 0000000..9626a9d
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2009 Chris Wilson
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Chris Wilson not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Chris Wilson makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * CHRIS WILSON DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL CHRIS WILSON BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+
+static cairo_scaled_font_t *
+create_twin (cairo_t *cr, cairo_antialias_t antialias)
+{
+    cairo_font_options_t *options;
+
+    cairo_select_font_face (cr,
+                           "@cairo:",
+                           CAIRO_FONT_SLANT_NORMAL,
+                           CAIRO_FONT_WEIGHT_NORMAL);
+
+    options = cairo_font_options_create ();
+    cairo_font_options_set_antialias (options, antialias);
+    cairo_set_font_options (cr, options);
+    cairo_font_options_destroy (options);
+
+    return cairo_scaled_font_reference (cairo_get_scaled_font (cr));
+}
+
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_scaled_font_t *subpixel, *gray, *none;
+
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_paint (cr);
+    cairo_set_source_rgb (cr, 0, 0, 0);
+
+    cairo_set_font_size (cr, 64);
+    subpixel = create_twin (cr, CAIRO_ANTIALIAS_SUBPIXEL);
+    gray = create_twin (cr, CAIRO_ANTIALIAS_GRAY);
+    none = create_twin (cr, CAIRO_ANTIALIAS_NONE);
+
+    cairo_move_to (cr, 4, 64);
+    cairo_set_scaled_font (cr, subpixel);
+    cairo_show_text (cr, "Is cairo's");
+    cairo_set_scaled_font (cr, gray);
+    cairo_show_text (cr, " twin");
+    cairo_set_scaled_font (cr, none);
+    cairo_show_text (cr, " giza?");
+
+    cairo_move_to (cr, 4, 128+16);
+    cairo_set_scaled_font (cr, gray);
+    cairo_show_text (cr, "Is cairo's");
+    cairo_set_scaled_font (cr, none);
+    cairo_show_text (cr, " twin");
+    cairo_set_scaled_font (cr, subpixel);
+    cairo_show_text (cr, " giza?");
+
+    cairo_move_to (cr, 4, 192+32);
+    cairo_set_scaled_font (cr, none);
+    cairo_show_text (cr, "Is cairo's");
+    cairo_set_scaled_font (cr, gray);
+    cairo_show_text (cr, " twin");
+    cairo_set_scaled_font (cr, subpixel);
+    cairo_show_text (cr, " giza?");
+
+    cairo_scaled_font_destroy (none);
+    cairo_scaled_font_destroy (gray);
+    cairo_scaled_font_destroy (subpixel);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (large_twin_antialias_mixed,
+           "Tests the internal font (with intermixed antialiasing)",
+           "twin, font", /* keywords */
+           "target=raster", /* requirements */
+           524, 240,
+           NULL, draw)
diff --git a/test/leaky-dash.c b/test/leaky-dash.c
new file mode 100644 (file)
index 0000000..12e91ac
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ */
+
+/* Test case for bug #4863:
+ *
+ *     stroking problems with wide dashed lines
+ *     https://bugs.freedesktop.org/show_bug.cgi?id=4863
+ */
+
+#include "cairo-test.h"
+
+#define WIDTH 71
+#define HEIGHT 28
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    double dash[2];
+
+    cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); /* white */
+    cairo_paint (cr);
+    cairo_set_source_rgb (cr, 0.0, 0.0, 0.0); /* black */
+
+    cairo_set_line_width (cr, 2);
+    dash[0] = 8.0;
+    dash[1] = 2.0;
+
+    cairo_rectangle (cr, 2.0, 2.0, 67.0, 24.0);
+
+    cairo_set_dash (cr, dash, 2, 9.0);
+    cairo_stroke (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (leaky_dash,
+           "Exercises bug #4863 in which a dashed stroke leaks into half the rectangle being filled"
+           "\nknown bug (#4863) which has existed since the 1.0 release",
+           "dash, stroke", /* keywords */
+           NULL, /* requirements */
+           WIDTH, HEIGHT,
+           NULL, draw)
diff --git a/test/leaky-dashed-rectangle.c b/test/leaky-dashed-rectangle.c
new file mode 100644 (file)
index 0000000..25f5674
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * Copyright © 2008 Chris Wilson
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Chris Wilson not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Chris Wilson makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * CHRIS WILSON DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL CHRIS WILSON BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ *         Franz Schmid <Franz.Schmid@altmuehlnet.de>
+ */
+
+/* Test case for bug reported by Franz Schmid <Franz.Schmid@altmuehlnet.de>
+ * http://lists.cairographics.org/archives/cairo/2008-April/013912.html
+ *
+ * See also: http://bugs.freedesktop.org/show_bug.cgi?id=17177
+ */
+
+#include "cairo-test.h"
+
+#define WIDTH 60
+#define HEIGHT 60
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    const double dash[2] = {4, 2};
+
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_paint (cr);
+
+    cairo_set_source_rgb (cr, 0., 0., 0);
+
+    cairo_translate (cr, 0.5, .5);
+    cairo_set_line_width (cr, 1); /* This is vital to reproduce the bug. */
+
+    /* First check simple rectangles */
+    cairo_set_source_rgb (cr, 0., 0., 0);
+    cairo_rectangle (cr, -WIDTH/4, -HEIGHT/4, WIDTH, HEIGHT);
+    cairo_stroke (cr);
+    cairo_rectangle (cr, WIDTH+WIDTH/4, -HEIGHT/4, -WIDTH, HEIGHT);
+    cairo_stroke (cr);
+    cairo_rectangle (cr, -WIDTH/4, HEIGHT+HEIGHT/4, WIDTH, -HEIGHT);
+    cairo_stroke (cr);
+    cairo_rectangle (cr, WIDTH+WIDTH/4, HEIGHT+HEIGHT/4, -WIDTH, -HEIGHT);
+    cairo_stroke (cr);
+
+    cairo_set_dash (cr, dash, 2, 0);
+
+    /* And now dashed. */
+    cairo_set_source_rgb (cr, 1., 0., 0);
+    cairo_rectangle (cr, -WIDTH/4, -HEIGHT/4, WIDTH, HEIGHT);
+    cairo_stroke (cr);
+    cairo_set_source_rgb (cr, 0., 1., 0);
+    cairo_rectangle (cr, WIDTH+WIDTH/4, -HEIGHT/4, -WIDTH, HEIGHT);
+    cairo_stroke (cr);
+    cairo_set_source_rgb (cr, 0., 0., 1);
+    cairo_rectangle (cr, -WIDTH/4, HEIGHT+HEIGHT/4, WIDTH, -HEIGHT);
+    cairo_stroke (cr);
+    cairo_set_source_rgb (cr, 1., 1., 0);
+    cairo_rectangle (cr, WIDTH+WIDTH/4, HEIGHT+HEIGHT/4, -WIDTH, -HEIGHT);
+    cairo_stroke (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (leaky_dashed_rectangle,
+           "Exercises bug in which a dashed stroke leaks in from outside the surface",
+           "dash, stroke", /* keywords */
+           NULL, /* requirements */
+           WIDTH, HEIGHT,
+           NULL, draw)
diff --git a/test/leaky-dashed-stroke.c b/test/leaky-dashed-stroke.c
new file mode 100644 (file)
index 0000000..457d7ce
--- /dev/null
@@ -0,0 +1,166 @@
+/*
+ * Copyright © 2008 Chris Wilson
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Chris Wilson not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Chris Wilson makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * CHRIS WILSON DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL CHRIS WILSON BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+/* Extracted from a test case reported by Jeff Muizelaar found whilst running
+ * firefox http://people.mozilla.com/~jmuizelaar/BerlinDistricts-check.svg
+ */
+
+#include "cairo-test.h"
+
+#define WIDTH 205
+#define HEIGHT 260
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    const double dash[2] = {.5, .5};
+
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_paint (cr);
+
+    cairo_set_source_rgb (cr, 1., 0., 0);
+
+    /* By adjusting the miter limit, we can see variations on the artifact.
+     * cairo_set_miter_limit (cr, 4.);
+     */
+
+    cairo_translate (cr, -720, -484);
+    cairo_scale (cr, 2.5, 2.5);
+
+    cairo_set_dash (cr, dash, 2, 0);
+
+    cairo_move_to (cr, 293.622, 330);
+    cairo_line_to (cr, 293.703, 337.028);
+    cairo_line_to (cr, 297.45, 336.851);
+    cairo_line_to (cr, 308.88, 342.609);
+    cairo_line_to (cr, 309.736, 346.107);
+    cairo_line_to (cr, 312.972, 348.128);
+    cairo_line_to (cr, 312.977, 353.478);
+    cairo_line_to (cr, 322.486, 359.355);
+    cairo_line_to (cr, 320.831, 363.642);
+    cairo_line_to (cr, 315.175, 367.171);
+    cairo_line_to (cr, 308.987, 365.715);
+    cairo_line_to (cr, 301.3, 365.964);
+    cairo_line_to (cr, 304.712, 368.852);
+    cairo_line_to (cr, 305.349, 373.022);
+    cairo_line_to (cr, 303.211, 376.551);
+    cairo_line_to (cr, 304.915, 382.855);
+    cairo_line_to (cr, 323.715, 400.475);
+    cairo_line_to (cr, 355.323, 424.072);
+    cairo_line_to (cr, 443.078, 426.534);
+    cairo_line_to (cr, 455.26, 400.603);
+    cairo_line_to (cr, 471.924, 392.604);
+    cairo_line_to (cr, 478.556, 390.797);
+    cairo_line_to (cr, 477.715, 386);
+    cairo_line_to (cr, 456.807, 376.507);
+    cairo_line_to (cr, 449.134, 368.722);
+    cairo_line_to (cr, 449.147, 365.847);
+    cairo_line_to (cr, 439.981, 361.692);
+    cairo_line_to (cr, 439.994, 358.603);
+    cairo_line_to (cr, 454.645, 336.128);
+    cairo_line_to (cr, 434.995, 324.005);
+    cairo_line_to (cr, 423.884, 319.354);
+    cairo_line_to (cr, 421.098, 312.569);
+    cairo_line_to (cr, 424.291, 305.997);
+    cairo_line_to (cr, 431.308, 305.069);
+    cairo_line_to (cr, 437.257, 296.882);
+    cairo_line_to (cr, 448.544, 296.808);
+    cairo_line_to (cr, 452.113, 290.651);
+    cairo_line_to (cr, 448.469, 285.483);
+    cairo_line_to (cr, 442.903, 282.877);
+    cairo_line_to (cr, 447.798, 281.124);
+    cairo_line_to (cr, 454.622, 274.911);
+    cairo_line_to (cr, 449.491, 269.978);
+    cairo_line_to (cr, 443.666, 253.148);
+    cairo_line_to (cr, 445.741, 250.834);
+    cairo_line_to (cr, 441.87, 247.131);
+    cairo_line_to (cr, 436.932, 246.203);
+    cairo_line_to (cr, 430.5, 251.252);
+    cairo_line_to (cr, 427.483, 250.751);
+    cairo_line_to (cr, 427.26, 253.572);
+    cairo_line_to (cr, 423.621, 255.539);
+    cairo_line_to (cr, 423.824, 257.933);
+    cairo_line_to (cr, 425.239, 259.582);
+    cairo_line_to (cr, 422.385, 261.443);
+    cairo_line_to (cr, 421.665, 260.53);
+    cairo_line_to (cr, 419.238, 262.819);
+    cairo_line_to (cr, 418.731, 257.849);
+    cairo_line_to (cr, 419.72, 255.227);
+    cairo_line_to (cr, 418.786, 250.258);
+    cairo_line_to (cr, 405.685, 235.254);
+    cairo_line_to (cr, 427.167, 215.127);
+    cairo_line_to (cr, 413.852, 196.281);
+    cairo_line_to (cr, 420.177, 192.379);
+    cairo_line_to (cr, 419.885, 185.701);
+    cairo_line_to (cr, 413.401, 185.428);
+    cairo_line_to (cr, 407.985, 186.863);
+    cairo_line_to (cr, 397.11, 189.112);
+    cairo_line_to (cr, 390.505, 186.664);
+    cairo_line_to (cr, 388.527, 183.694);
+    cairo_line_to (cr, 336.503, 221.048);
+    cairo_line_to (cr, 367.028, 241.656);
+    cairo_line_to (cr, 365.103, 244.117);
+    cairo_line_to (cr, 364.886, 246.792);
+    cairo_line_to (cr, 361.467, 247.119);
+    cairo_line_to (cr, 360.396, 245.525);
+    cairo_line_to (cr, 356.336, 245.638);
+    cairo_line_to (cr, 353.344, 242.122);
+    cairo_line_to (cr, 347.149, 242.876);
+    cairo_line_to (cr, 341.809, 256.652);
+    cairo_line_to (cr, 342.232, 268.72);
+    cairo_line_to (cr, 329.579, 269.095);
+    cairo_line_to (cr, 327.001, 271.009);
+    cairo_line_to (cr, 325.579, 275.598);
+    cairo_line_to (cr, 318.941, 277.313);
+    cairo_line_to (cr, 306.048, 277.231);
+    cairo_line_to (cr, 304.071, 276.27);
+    cairo_line_to (cr, 301.153, 277.175);
+    cairo_line_to (cr, 293.52, 277.529);
+    cairo_line_to (cr, 290.682, 281.947);
+    cairo_line_to (cr, 293.911, 286.63);
+    cairo_line_to (cr, 302.417, 290.547);
+    cairo_line_to (cr, 303.521, 294.73);
+    cairo_line_to (cr, 307.787, 298.088);
+    cairo_line_to (cr, 311.718, 299.126);
+    cairo_line_to (cr, 313.255, 302.146);
+    cairo_line_to (cr, 314.6, 306.206);
+    cairo_line_to (cr, 322.603, 308.96);
+    cairo_line_to (cr, 321.718, 314.477);
+    cairo_line_to (cr, 319.596, 320.341);
+    cairo_line_to (cr, 300.689, 323.69);
+    cairo_line_to (cr, 301.232, 326.789);
+    cairo_line_to (cr, 293.622, 330);
+    cairo_close_path (cr);
+
+    cairo_stroke (cr);
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (leaky_dashed_stroke,
+           "Exercises bug in which a dashed stroke leaks in from outside the surface",
+           "dash, stroke", /* keywords */
+           NULL, /* requirements */
+           WIDTH, HEIGHT,
+           NULL, draw)
diff --git a/test/leaky-polygon.c b/test/leaky-polygon.c
new file mode 100644 (file)
index 0000000..0ea7094
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ */
+
+/* Bug history
+ *
+ * 2005-01-07 Carl Worth <cworth@cworth.org>
+ *
+ *   Bug reported:
+ *
+ *     From: Chris <fltk@functionalfuture.com>
+ *     Subject: [cairo] Render to image buffer artifacts
+ *     To: cairo@cairographics.org
+ *     Date: Fri, 07 Jan 2005 02:22:28 -0500
+ *
+ *     I've attached the code and image that shows this off.  Scaling at
+ *     different levels seems to change the corruption.
+ *
+ *     For some reason there are artifacts in the alpha channel.  I don't know
+ *     if that's the only place, but the alpha channel looks bad.
+ *
+ *     If you run the code and parse the attached image, directing stdout to a
+ *     file, you can see in the lower left corner there are alpha values where
+ *     it should be transparent.
+ *     [...]
+ *
+ * 2005-01-11 Carl Worth <cworth@cworth.org>
+ *
+ *   I trimmed the original test case down to the code that appears here.
+ *
+ */
+
+#include "cairo-test.h"
+
+#define WIDTH 21
+#define HEIGHT 21
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    /* We draw in the default black, so paint white first. */
+    cairo_save (cr);
+    cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); /* white */
+    cairo_paint (cr);
+    cairo_restore (cr);
+
+    cairo_scale (cr, 1.0/(1<<16), 1.0/(1<<16));
+
+    cairo_move_to (cr, 131072,39321);
+    cairo_line_to (cr, 1103072,1288088);
+    cairo_line_to (cr, 1179648,1294990);
+    cairo_close_path (cr);
+
+    cairo_fill (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (leaky_polygon,
+           "Exercises a corner case in the trapezoid rasterization in which pixels outside the trapezoids received a non-zero alpha",
+           "fill, trap", /* keywords */
+           NULL, /* requirements */
+           WIDTH, HEIGHT,
+           NULL, draw)
diff --git a/test/line-width-large-overlap.c b/test/line-width-large-overlap.c
new file mode 100644 (file)
index 0000000..767734f
--- /dev/null
@@ -0,0 +1,149 @@
+/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/*
+ * Copyright 2011 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Benjamin Otte <otte@redhat.com>
+ */
+
+/*
+ * Test case taken from the WebKit test suite, failure originally reported
+ * by Zan Dobersek <zandobersek@gmail.com>. WebKit test is
+ * LayoutTests/canvas/philip/tests/2d.path.rect.selfintersect.html
+ */
+
+#include "cairo-test.h"
+
+#include <math.h>
+
+#define LINE_WIDTH 120
+#define SIZE 100
+#define RECT_SIZE 10
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    /* fill with green so RGB and RGBA tests can share the ref image */
+    cairo_set_source_rgb (cr, 0, 1, 0);
+    cairo_paint (cr);
+
+    /* red to see eventual bugs immediately */
+    cairo_set_source_rgb (cr, 1, 0, 0);
+
+    /* big line width */
+    cairo_set_line_width (cr, LINE_WIDTH);
+
+    /* rectangle that is smaller than the line width in center of image */
+    cairo_rectangle (cr,
+                     (SIZE - RECT_SIZE) / 2,
+                     (SIZE - RECT_SIZE) / 2,
+                     RECT_SIZE,
+                     RECT_SIZE);
+
+    cairo_stroke (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+/* and again slightly offset to trigger another path */
+static cairo_test_status_t
+draw_offset (cairo_t *cr, int width, int height)
+{
+    cairo_translate (cr, .5, .5);
+    return draw (cr, width, height);
+}
+
+static cairo_test_status_t
+draw_rotated (cairo_t *cr, int width, int height)
+{
+    cairo_translate (cr, SIZE/2, SIZE/2);
+    cairo_rotate (cr, M_PI/4);
+    cairo_translate (cr, -SIZE/2, -SIZE/2);
+
+    return draw (cr, width, height);
+}
+
+static cairo_test_status_t
+draw_flipped (cairo_t *cr, int width, int height)
+{
+    cairo_translate (cr, SIZE/2, SIZE/2);
+    cairo_scale (cr, -1, 1);
+    cairo_translate (cr, -SIZE/2, -SIZE/2);
+
+    return draw (cr, width, height);
+}
+
+static cairo_test_status_t
+draw_flopped (cairo_t *cr, int width, int height)
+{
+    cairo_translate (cr, SIZE/2, SIZE/2);
+    cairo_scale (cr, 1, -1);
+    cairo_translate (cr, -SIZE/2, -SIZE/2);
+
+    return draw (cr, width, height);
+}
+
+static cairo_test_status_t
+draw_dashed (cairo_t *cr, int width, int height)
+{
+    const double dashes[] = { 4 };
+    cairo_set_dash (cr, dashes, 1, 0);
+    cairo_set_line_cap (cr, CAIRO_LINE_CAP_BUTT);
+    return draw (cr, width, height);
+}
+
+CAIRO_TEST (line_width_large_overlap,
+           "Test overlapping lines due to large line width",
+           "stroke", /* keywords */
+           NULL, /* requirements */
+           SIZE, SIZE,
+           NULL, draw)
+CAIRO_TEST (line_width_large_overlap_offset,
+           "Test overlapping lines due to large line width",
+           "stroke", /* keywords */
+           NULL, /* requirements */
+           SIZE, SIZE,
+           NULL, draw_offset)
+CAIRO_TEST (line_width_large_overlap_rotated,
+           "Test overlapping lines due to large line width",
+           "stroke", /* keywords */
+           NULL, /* requirements */
+           SIZE, SIZE,
+           NULL, draw_rotated)
+CAIRO_TEST (line_width_large_overlap_flipped,
+           "Test overlapping lines due to large line width",
+           "stroke", /* keywords */
+           NULL, /* requirements */
+           SIZE, SIZE,
+           NULL, draw_flipped)
+CAIRO_TEST (line_width_large_overlap_flopped,
+           "Test overlapping lines due to large line width",
+           "stroke", /* keywords */
+           NULL, /* requirements */
+           SIZE, SIZE,
+           NULL, draw_flopped)
+CAIRO_TEST (line_width_large_overlap_dashed,
+           "Test overlapping lines due to large line width",
+           "stroke", /* keywords */
+           NULL, /* requirements */
+           SIZE, SIZE,
+           NULL, draw_dashed)
diff --git a/test/line-width-overlap.c b/test/line-width-overlap.c
new file mode 100644 (file)
index 0000000..ac8c234
--- /dev/null
@@ -0,0 +1,149 @@
+/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/*
+ * Copyright 2011 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Benjamin Otte <otte@redhat.com>
+ */
+
+/*
+ * Test case taken from the WebKit test suite, failure originally reported
+ * by Zan Dobersek <zandobersek@gmail.com>. WebKit test is
+ * LayoutTests/canvas/philip/tests/2d.path.rect.selfintersect.html
+ */
+
+#include "cairo-test.h"
+
+#include <math.h>
+
+#define LINE_WIDTH 60
+#define SIZE 100
+#define RECT_SIZE 10
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    /* fill with green so RGB and RGBA tests can share the ref image */
+    cairo_set_source_rgb (cr, 0, 1, 0);
+    cairo_paint (cr);
+
+    /* red to see eventual bugs immediately */
+    cairo_set_source_rgb (cr, 1, 0, 0);
+
+    /* big line width */
+    cairo_set_line_width (cr, LINE_WIDTH);
+
+    /* rectangle that is smaller than the line width in center of image */
+    cairo_rectangle (cr,
+                     (SIZE - RECT_SIZE) / 2,
+                     (SIZE - RECT_SIZE) / 2,
+                     RECT_SIZE,
+                     RECT_SIZE);
+
+    cairo_stroke (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+/* and again slightly offset to trigger another path */
+static cairo_test_status_t
+draw_offset (cairo_t *cr, int width, int height)
+{
+    cairo_translate (cr, .5, .5);
+    return draw (cr, width, height);
+}
+
+static cairo_test_status_t
+draw_rotated (cairo_t *cr, int width, int height)
+{
+    cairo_translate (cr, SIZE/2, SIZE/2);
+    cairo_rotate (cr, M_PI/4);
+    cairo_translate (cr, -SIZE/2, -SIZE/2);
+
+    return draw (cr, width, height);
+}
+
+static cairo_test_status_t
+draw_flipped (cairo_t *cr, int width, int height)
+{
+    cairo_translate (cr, SIZE/2, SIZE/2);
+    cairo_scale (cr, -1, 1);
+    cairo_translate (cr, -SIZE/2, -SIZE/2);
+
+    return draw (cr, width, height);
+}
+
+static cairo_test_status_t
+draw_flopped (cairo_t *cr, int width, int height)
+{
+    cairo_translate (cr, SIZE/2, SIZE/2);
+    cairo_scale (cr, 1, -1);
+    cairo_translate (cr, -SIZE/2, -SIZE/2);
+
+    return draw (cr, width, height);
+}
+
+static cairo_test_status_t
+draw_dashed (cairo_t *cr, int width, int height)
+{
+    const double dashes[] = { 4 };
+    cairo_set_dash (cr, dashes, 1, 0);
+    cairo_set_line_cap (cr, CAIRO_LINE_CAP_BUTT);
+    return draw (cr, width, height);
+}
+
+CAIRO_TEST (line_width_overlap,
+           "Test overlapping lines due to large line width",
+           "stroke", /* keywords */
+           NULL, /* requirements */
+           SIZE, SIZE,
+           NULL, draw)
+CAIRO_TEST (line_width_overlap_offset,
+           "Test overlapping lines due to large line width",
+           "stroke", /* keywords */
+           NULL, /* requirements */
+           SIZE, SIZE,
+           NULL, draw_offset)
+CAIRO_TEST (line_width_overlap_rotated,
+           "Test overlapping lines due to large line width",
+           "stroke", /* keywords */
+           NULL, /* requirements */
+           SIZE, SIZE,
+           NULL, draw_rotated)
+CAIRO_TEST (line_width_overlap_flipped,
+           "Test overlapping lines due to large line width",
+           "stroke", /* keywords */
+           NULL, /* requirements */
+           SIZE, SIZE,
+           NULL, draw_flipped)
+CAIRO_TEST (line_width_overlap_flopped,
+           "Test overlapping lines due to large line width",
+           "stroke", /* keywords */
+           NULL, /* requirements */
+           SIZE, SIZE,
+           NULL, draw_flopped)
+CAIRO_TEST (line_width_overlap_dashed,
+           "Test overlapping lines due to large line width",
+           "stroke", /* keywords */
+           NULL, /* requirements */
+           SIZE, SIZE,
+           NULL, draw_dashed)
diff --git a/test/line-width-scale.c b/test/line-width-scale.c
new file mode 100644 (file)
index 0000000..037b887
--- /dev/null
@@ -0,0 +1,178 @@
+/*
+ * Copyright © 2006 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ */
+
+#include "cairo-test.h"
+
+/* This test exercises the various interactions between
+ * cairo_set_line_width and cairo_scale. Specifically it shows how
+ * separate transformations can affect the pen for stroking compared
+ * to the path itself.
+ *
+ * This was inspired by an image by Maxim Shemanarev demonstrating the
+ * flexible-pipeline nature of his Antigrain Geometry project:
+ *
+ *     http://antigrain.com/tips/line_alignment/conv_order.gif
+ *
+ * It also uncovered some behavior in cairo that I found surprising.
+ * Namely, cairo_set_line_width was not transforming the width
+ * according the the current CTM, but instead delaying that
+ * transformation until the time of cairo_stroke.
+ *
+ * This delayed behavior was released in cairo 1.0 so we're going to
+ * document this as the way cairo_set_line_width works rather than
+ * considering this a bug.
+ */
+
+#define LINE_WIDTH 13
+#define SPLINE 50.0
+#define XSCALE  0.5
+#define YSCALE  2.0
+#define WIDTH (XSCALE * SPLINE * 6.0)
+#define HEIGHT (YSCALE * SPLINE * 2.0)
+
+static void
+spline_path (cairo_t *cr)
+{
+    cairo_save (cr);
+    {
+       cairo_move_to (cr,
+                      - SPLINE, 0);
+       cairo_curve_to (cr,
+                       - SPLINE / 4, - SPLINE,
+                         SPLINE / 4,   SPLINE,
+                         SPLINE, 0);
+    }
+    cairo_restore (cr);
+}
+
+/* If we scale before setting the line width or creating the path,
+ * then obviously both will be scaled. */
+static void
+scale_then_set_line_width_and_stroke (cairo_t *cr)
+{
+    cairo_scale (cr, XSCALE, YSCALE);
+    cairo_set_line_width (cr, LINE_WIDTH);
+    spline_path (cr);
+    cairo_stroke (cr);
+}
+
+/* This is used to verify the results of
+ * scale_then_set_line_width_and_stroke.
+ *
+ * It uses save/restore pairs to isolate the scaling of the path and
+ * line_width and ensures that both are scaled.
+ */
+static void
+scale_path_and_line_width (cairo_t *cr)
+{
+    cairo_save (cr);
+    {
+       cairo_scale (cr, XSCALE, YSCALE);
+       spline_path (cr);
+    }
+    cairo_restore (cr);
+
+    cairo_save (cr);
+    {
+       cairo_scale (cr, XSCALE, YSCALE);
+       cairo_set_line_width (cr, LINE_WIDTH);
+       cairo_stroke (cr);
+    }
+    cairo_restore (cr);
+}
+
+/* This is the case that was surprising.
+ *
+ * Setting the line width before scaling doesn't change anything. The
+ * line width will be interpreted under the CTM in effect at the time
+ * of cairo_stroke, so the line width will be scaled as well as the
+ * path here.
+ */
+static void
+set_line_width_then_scale_and_stroke (cairo_t *cr)
+{
+    cairo_set_line_width (cr, LINE_WIDTH);
+    cairo_scale (cr, XSCALE, YSCALE);
+    spline_path (cr);
+    cairo_stroke (cr);
+}
+
+/* Here then is the way to achieve the alternate result.
+ *
+ * This uses save/restore pairs to isolate the scaling of the path and
+ * line_width and ensures that the path is scaled while the line width
+ * is not.
+ */
+static void
+scale_path_not_line_width (cairo_t *cr)
+{
+    cairo_save (cr);
+    {
+       cairo_scale (cr, XSCALE, YSCALE);
+       spline_path (cr);
+    }
+    cairo_restore (cr);
+
+    cairo_save (cr);
+    {
+       cairo_set_line_width (cr, LINE_WIDTH);
+       cairo_stroke (cr);
+    }
+    cairo_restore (cr);
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    int i;
+    void (* const figures[4]) (cairo_t *cr) = {
+       scale_then_set_line_width_and_stroke,
+       scale_path_and_line_width,
+       set_line_width_then_scale_and_stroke,
+       scale_path_not_line_width
+    };
+
+    cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); /* white */
+    cairo_paint (cr);
+    cairo_set_source_rgb (cr, 0.0, 0.0, 0.0); /* black */
+
+    for (i = 0; i < 4; i++) {
+       cairo_save (cr);
+       cairo_translate (cr,
+                        WIDTH/4  + (i % 2) * WIDTH/2,
+                        HEIGHT/4 + (i / 2) * HEIGHT/2);
+       (figures[i]) (cr);
+       cairo_restore (cr);
+    }
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (line_width_scale,
+           "Tests interaction of cairo_set_line_width with cairo_scale",
+           "stroke", /* keywords */
+           NULL, /* requirements */
+           WIDTH, HEIGHT,
+           NULL, draw)
diff --git a/test/line-width-tolerance.c b/test/line-width-tolerance.c
new file mode 100644 (file)
index 0000000..5a581ca
--- /dev/null
@@ -0,0 +1,66 @@
+/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/*
+ * Copyright 2011 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Benjamin Otte <otte@redhat.com>
+ */
+
+/*
+ * Test case taken from the WebKit test suite, failure originally reported
+ * by Zan Dobersek <zandobersek@gmail.com> at
+ * https://bugs.webkit.org/show_bug.cgi?id=54471
+ */
+
+#include "cairo-test.h"
+
+#include <math.h>
+
+#define RADIUS 50
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    /* fill with green so RGB and RGBA tests can share the ref image */
+    cairo_set_source_rgb (cr, 0, 1, 0);
+    cairo_paint (cr);
+
+    /* red to see eventual bugs immediately */
+    cairo_set_source_rgb (cr, 1, 0, 0);
+
+    /* stroke 3/4 of a circle where the last quarter would be this
+     * reference image. Keep just a 1 pixel border. Use a huge line
+     * width (twice the circle's radius to get it filled completely).
+     */
+    cairo_set_line_width (cr, 2 * RADIUS);
+    cairo_arc (cr, 1, RADIUS - 1, RADIUS, 0, - M_PI / 2.0);
+    cairo_stroke (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (line_width_tolerance,
+           "Test interaction of line width and tolerance when stroking arcs",
+           "stroke", /* keywords */
+           NULL, /* requirements */
+           RADIUS, RADIUS,
+           NULL, draw)
diff --git a/test/line-width-zero.c b/test/line-width-zero.c
new file mode 100644 (file)
index 0000000..c7ac8a0
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * Copyright © 2007 Red Hat, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Carl Worth <cworth@cworth.org>
+ */
+
+#include "cairo-test.h"
+
+/* This is a test case for the following bug:
+ *
+ *     Crash in cairo_stroke_extents whe line width is 0 and line cap is ROUND
+ *     (_cairo_pen_find_active_cw_vertex_index)
+ *     https://bugs.freedesktop.org/show_bug.cgi?id=10231
+ */
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    double x1, y1, x2, y2;
+
+    cairo_move_to (cr, 0.0, 0.0);
+    cairo_line_to (cr, 100.0, 100.0);
+    cairo_set_line_width (cr, 0.0);
+
+    cairo_set_line_cap (cr, CAIRO_LINE_CAP_BUTT);
+    cairo_set_line_join (cr, CAIRO_LINE_JOIN_MITER);
+    cairo_stroke_extents (cr, &x1, &y1, &x2, &y2);
+    cairo_in_stroke (cr, 50, 50);
+    cairo_stroke_preserve (cr);
+
+    cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
+    cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND);
+    cairo_stroke_extents (cr, &x1, &y1, &x2, &y2);
+    cairo_in_stroke (cr, 50, 50);
+    cairo_stroke_preserve (cr);
+
+    cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE);
+    cairo_set_line_join (cr, CAIRO_LINE_JOIN_BEVEL);
+    cairo_stroke_extents (cr, &x1, &y1, &x2, &y2);
+    cairo_in_stroke (cr, 50, 50);
+    cairo_stroke (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (line_width_zero,
+           "Test all stroke operations and all cap,join styles with line width of zero",
+           "stroke", /* keywords */
+           NULL, /* requirements */
+           0, 0,
+           NULL, draw)
diff --git a/test/line-width.c b/test/line-width.c
new file mode 100644 (file)
index 0000000..283e41f
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * Copyright © 2004 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ */
+
+#include "cairo-test.h"
+
+#define LINES 5
+#define LINE_LENGTH 10
+#define IMAGE_WIDTH 2 * LINE_LENGTH + 6
+#define IMAGE_HEIGHT ((LINES+4)*LINES)/2 + 2
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    int i;
+
+    /* We draw in black, so paint white first. */
+    cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); /* white */
+    cairo_paint (cr);
+
+    cairo_set_source_rgb (cr, 0, 0, 0);
+    cairo_translate (cr, 2, 2);
+
+    for (i=0; i < LINES; i++) {
+       cairo_set_line_width (cr, i+1);
+       cairo_move_to (cr, 0, 0);
+       cairo_rel_line_to (cr, LINE_LENGTH, 0);
+       cairo_stroke (cr);
+       cairo_move_to (cr, LINE_LENGTH + 2, 0.5);
+       cairo_rel_line_to (cr, LINE_LENGTH, 0);
+       cairo_stroke (cr);
+       cairo_translate (cr, 0, i+3);
+    }
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+static cairo_test_status_t
+draw_a1 (cairo_t *cr, int width, int height)
+{
+    cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
+    return draw (cr, width, height);
+}
+
+CAIRO_TEST (line_width,
+           "Tests cairo_set_line_width",
+           "stroke", /* keywords */
+           NULL, /* requirements */
+           IMAGE_WIDTH, IMAGE_HEIGHT,
+           NULL, draw)
+CAIRO_TEST (a1_line_width,
+           "Tests cairo_set_line_width",
+           "stroke", /* keywords */
+           NULL, /* requirements */
+           IMAGE_WIDTH, IMAGE_HEIGHT,
+           NULL, draw_a1)
diff --git a/test/linear-gradient-extend.c b/test/linear-gradient-extend.c
new file mode 100644 (file)
index 0000000..b1328b4
--- /dev/null
@@ -0,0 +1,92 @@
+/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/*
+ * Copyright 2010 Andrea Canciani
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Andrea Canciani <ranma42@gmail.com>
+ */
+
+#include "cairo-test.h"
+
+#define NUM_EXTEND 4
+#define HEIGHT 16
+#define WIDTH 16
+#define PAD 3
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_pattern_t *pattern;
+    unsigned int i, j;
+
+    cairo_extend_t extend[NUM_EXTEND] = {
+       CAIRO_EXTEND_NONE,
+       CAIRO_EXTEND_REPEAT,
+       CAIRO_EXTEND_REFLECT,
+       CAIRO_EXTEND_PAD
+    };
+
+    cairo_test_paint_checkered (cr);
+
+    pattern = cairo_pattern_create_linear (0, 2*PAD, 0, HEIGHT - 2*PAD);
+
+    cairo_pattern_add_color_stop_rgb (pattern, 0, 0, 0, 1);
+    cairo_pattern_add_color_stop_rgb (pattern, 1, 0, 0, 1);
+
+    cairo_translate (cr, PAD, PAD);
+
+    for (i = 0; i < 2; i++) {
+        cairo_save (cr);
+       
+       for (j = 0; j < NUM_EXTEND; j++) {
+           cairo_pattern_set_extend (pattern, extend[j]);
+
+           cairo_reset_clip (cr);
+           cairo_rectangle (cr, 0, 0, WIDTH, HEIGHT);
+           cairo_clip (cr);
+
+           if (i & 1) {
+               cairo_set_source_rgb (cr, 0, 1, 0);
+               cairo_mask (cr, pattern);
+           } else {
+               cairo_set_source (cr, pattern);
+               cairo_paint (cr);
+           }
+
+           cairo_translate (cr, WIDTH+PAD, 0);
+       }
+
+       cairo_restore (cr);
+       cairo_translate (cr, 0, HEIGHT+PAD);
+    }
+
+    cairo_pattern_destroy (pattern);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (linear_gradient_extend,
+           "Tests gradient to solid reduction of linear gradients",
+           "linear, pattern, extend", /* keywords */
+           NULL, /* requirements */
+           (WIDTH+PAD) * NUM_EXTEND + PAD, 2*(HEIGHT + PAD) + PAD,
+           NULL, draw)
diff --git a/test/linear-gradient-large.c b/test/linear-gradient-large.c
new file mode 100644 (file)
index 0000000..5646aa4
--- /dev/null
@@ -0,0 +1,68 @@
+/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/*
+ * Copyright 2010 Krzysztof Kosiński
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Krzysztof Kosiński <tweenk.pl@gmail.com>
+ */
+
+#include "cairo-test.h"
+
+/* originally reported in https://bugs.freedesktop.org/show_bug.cgi?id=29470 */
+
+#define OFFSET 50
+#define SIZE 1000
+
+static void mark_point(cairo_t *ct, double x, double y)
+{
+    cairo_rectangle(ct, x-2, y-2, 4, 4);
+    cairo_set_source_rgb(ct, 1,0,0);
+    cairo_fill(ct);
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_pattern_t *gr = cairo_pattern_create_linear (SIZE - OFFSET, OFFSET,
+                                                       OFFSET, SIZE - OFFSET);
+
+    cairo_pattern_add_color_stop_rgb (gr, 0.0, 1, 1, 1);
+    cairo_pattern_add_color_stop_rgb (gr, 0.0, 0, 0, 0);
+    cairo_pattern_add_color_stop_rgb (gr, 1.0, 0, 0, 0);
+    cairo_pattern_add_color_stop_rgb (gr, 1.0, 1, 1, 1);
+
+    cairo_set_source (cr, gr);
+    cairo_pattern_destroy (gr);
+    cairo_paint (cr);
+
+    mark_point(cr, SIZE - OFFSET, OFFSET);
+    mark_point(cr, OFFSET, SIZE - OFFSET);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (linear_gradient_large,
+           "Tests that large linear gradients get rendered at the correct place",
+           "linear, pattern", /* keywords */
+           NULL, /* requirements */
+           SIZE, SIZE,
+           NULL, draw)
diff --git a/test/linear-gradient-one-stop.c b/test/linear-gradient-one-stop.c
new file mode 100644 (file)
index 0000000..09c4b9d
--- /dev/null
@@ -0,0 +1,90 @@
+/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/*
+ * Copyright 2010 Andrea Canciani
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Andrea Canciani <ranma42@gmail.com>
+ */
+
+#include "cairo-test.h"
+
+#define NUM_EXTEND 4
+#define HEIGHT 16
+#define WIDTH 16
+#define PAD 3
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_pattern_t *pattern;
+    unsigned int i, j;
+
+    cairo_extend_t extend[NUM_EXTEND] = {
+       CAIRO_EXTEND_NONE,
+       CAIRO_EXTEND_REPEAT,
+       CAIRO_EXTEND_REFLECT,
+       CAIRO_EXTEND_PAD
+    };
+
+    cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+
+    cairo_translate (cr, PAD, PAD);
+
+    for (i = 0; i < 3; i++) {
+        cairo_save (cr);
+       
+       for (j = 0; j < NUM_EXTEND; j++) {
+           cairo_reset_clip (cr);
+           cairo_rectangle (cr, 0, 0, WIDTH, HEIGHT);
+           cairo_clip (cr);
+
+           if (i == 0)
+               pattern = cairo_pattern_create_linear (0, 2*PAD, 0, HEIGHT - 2*PAD);
+           else if (i == 1)
+               pattern = cairo_pattern_create_linear (2*PAD, 2*PAD, HEIGHT - 2*PAD, HEIGHT - 2*PAD);
+           else if (i == 2)
+               pattern = cairo_pattern_create_linear (2*PAD, 0, HEIGHT - 2*PAD, 0);
+
+           cairo_pattern_add_color_stop_rgb (pattern, 0.25, 0, 0, 1);
+           cairo_pattern_set_extend (pattern, extend[j]);
+
+           cairo_set_source (cr, pattern);
+           cairo_paint (cr);
+
+           cairo_pattern_destroy (pattern);
+
+           cairo_translate (cr, WIDTH+PAD, 0);
+       }
+
+       cairo_restore (cr);
+       cairo_translate (cr, 0, HEIGHT+PAD);
+    }
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (linear_gradient_one_stop,
+           "Tests linear gradients with a single stop",
+           "gradient,linear,", /* keywords */
+           NULL, /* requirements */
+           (WIDTH+PAD) * NUM_EXTEND + PAD, 3*(HEIGHT + PAD) + PAD,
+           NULL, draw)
diff --git a/test/linear-gradient-reflect.c b/test/linear-gradient-reflect.c
new file mode 100644 (file)
index 0000000..8adb587
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * Copyright © 2007 Tim Rowley
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Tim Rowley
+ */
+
+#include "cairo-test.h"
+#include "stdio.h"
+
+#define WIDTH  50
+#define HEIGHT 50
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_pattern_t *pattern;
+
+    cairo_set_source_rgb (cr, 0.5, 0.5, 0.5);
+    cairo_paint (cr);
+
+    cairo_save (cr);
+
+    pattern = cairo_pattern_create_linear (0, 0, 10.0, 0);
+
+    cairo_pattern_add_color_stop_rgb (pattern, 0.0,
+                                     0.0, 0.0, 1.0);
+    cairo_pattern_add_color_stop_rgb (pattern, 1.0,
+                                     1.0, 0.0, 0.0);
+    cairo_pattern_set_extend (pattern, CAIRO_EXTEND_REFLECT);
+
+    cairo_set_source (cr, pattern);
+    cairo_pattern_destroy (pattern);
+    cairo_rectangle (cr, 0.0, 0.0, WIDTH, HEIGHT);
+    cairo_fill (cr);
+
+    cairo_restore (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (linear_gradient_reflect,
+           "Tests the drawing of linear gradient with reflect",
+           "gradient", /* keywords */
+           NULL, /* requirements */
+           WIDTH, HEIGHT,
+           NULL, draw)
+
diff --git a/test/linear-gradient-subset.c b/test/linear-gradient-subset.c
new file mode 100644 (file)
index 0000000..ce05e6e
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Owen Taylor <otaylor@redhat.com>
+ */
+
+#include "cairo-test.h"
+#include "stdio.h"
+
+/* The test matrix is
+ *
+ * A) Horizontal   B) 5°          C) 45°          D) Vertical
+ * 1) Rotated 0°   2) Rotated 45° C) Rotated 90°
+ * a) 2 stop       b) 3 stop
+ *
+ *  A1a   B1a  C1a  D1a
+ *  A2a   B2a  C2a  D2a
+ *  A3a   B3a  C3a  D3a
+ *  A1b   B1b  C1b  D1b
+ *  A2b   B2b  C2b  D2b
+ *  A3b   B3b  C3b  D3b
+ */
+
+static const double gradient_angles[] = { 0, 45, 90 };
+#define N_GRADIENT_ANGLES 3
+static const double rotate_angles[] = { 0, 45, 90 };
+#define N_ROTATE_ANGLES 3
+static const int n_stops[] = { 2, 3 };
+#define N_N_STOPS 2
+
+#define UNIT_SIZE 6
+#define UNIT_SIZE 6
+#define PAD 1
+
+#define WIDTH  N_GRADIENT_ANGLES * UNIT_SIZE + (N_GRADIENT_ANGLES + 1) * PAD
+#define HEIGHT N_N_STOPS * N_ROTATE_ANGLES * UNIT_SIZE + (N_N_STOPS * N_ROTATE_ANGLES + 1) * PAD
+
+static void
+draw_unit (cairo_t *cr,
+          double   gradient_angle,
+          double   rotate_angle,
+          int      n_stops)
+{
+    cairo_pattern_t *pattern;
+
+    cairo_rectangle (cr, 0, 0, 1, 1);
+    cairo_clip (cr);
+    cairo_new_path(cr);
+
+    cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
+    cairo_rectangle (cr, 0, 0, 1, 1);
+    cairo_fill (cr);
+
+    cairo_translate (cr, 0.5, 0.5);
+    cairo_scale (cr, 1 / 1.5, 1 / 1.5);
+    cairo_rotate (cr, rotate_angle);
+
+    pattern = cairo_pattern_create_linear (-0.5 * cos (gradient_angle),  -0.5 * sin (gradient_angle),
+                                           0.5 * cos (gradient_angle),   0.5 * sin (gradient_angle));
+
+    if (n_stops == 2) {
+       cairo_pattern_add_color_stop_rgb (pattern, 0.2, 0.3, 0.3, 0.3);
+       cairo_pattern_add_color_stop_rgb (pattern, 0.8, 1.0, 1.0, 1.0);
+    } else {
+       cairo_pattern_add_color_stop_rgb (pattern, 0.2, 1.0, 0.0, 0.0);
+       cairo_pattern_add_color_stop_rgb (pattern, 0.5, 1.0, 1.0, 1.0);
+       cairo_pattern_add_color_stop_rgb (pattern, 0.8, 0.0, 0.0, 1.0);
+    }
+
+    cairo_set_source (cr, pattern);
+    cairo_pattern_destroy (pattern);
+    cairo_rectangle (cr, -0.5, -0.5, 1, 1);
+    cairo_fill (cr);
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    int i, j, k;
+
+    cairo_set_source_rgb (cr, 0.5, 0.5, 0.5);
+    cairo_paint (cr);
+
+    for (i = 0; i < N_GRADIENT_ANGLES; i++)
+       for (j = 0; j < N_ROTATE_ANGLES; j++)
+         for (k = 0; k < N_N_STOPS; k++) {
+               cairo_save (cr);
+               cairo_translate (cr,
+                                PAD + (PAD + UNIT_SIZE) * i,
+                                PAD + (PAD + UNIT_SIZE) * (N_ROTATE_ANGLES * k + j));
+               cairo_scale (cr, UNIT_SIZE, UNIT_SIZE);
+
+               draw_unit (cr,
+                          gradient_angles[i] * M_PI / 180.,
+                          rotate_angles[j] * M_PI / 180.,
+                          n_stops[k]);
+               cairo_restore (cr);
+           }
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (linear_gradient_subset,
+           "Tests the drawing of linear gradients",
+           "gradient", /* keywords */
+           NULL, /* requirements */
+           WIDTH, HEIGHT,
+           NULL, draw)
diff --git a/test/linear-gradient.c b/test/linear-gradient.c
new file mode 100644 (file)
index 0000000..054d6ab
--- /dev/null
@@ -0,0 +1,132 @@
+/*
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Owen Taylor <otaylor@redhat.com>
+ */
+
+#include "cairo-test.h"
+#include "stdio.h"
+
+/* The test matrix is
+ *
+ * A) Horizontal   B) 5°          C) 45°          D) Vertical
+ * 1) Rotated 0°   2) Rotated 45° C) Rotated 90°
+ * a) 2 stop       b) 3 stop
+ *
+ *  A1a   B1a  C1a  D1a
+ *  A2a   B2a  C2a  D2a
+ *  A3a   B3a  C3a  D3a
+ *  A1b   B1b  C1b  D1b
+ *  A2b   B2b  C2b  D2b
+ *  A3b   B3b  C3b  D3b
+ */
+
+static const double gradient_angles[] = { 0, 45, 90 };
+#define N_GRADIENT_ANGLES 3
+static const double rotate_angles[] = { 0, 45, 90 };
+#define N_ROTATE_ANGLES 3
+static const int n_stops[] = { 2, 3 };
+#define N_N_STOPS 2
+
+#define UNIT_SIZE 6
+#define UNIT_SIZE 6
+#define PAD 1
+
+#define WIDTH  N_GRADIENT_ANGLES * UNIT_SIZE + (N_GRADIENT_ANGLES + 1) * PAD
+#define HEIGHT N_N_STOPS * N_ROTATE_ANGLES * UNIT_SIZE + (N_N_STOPS * N_ROTATE_ANGLES + 1) * PAD
+
+static void
+draw_unit (cairo_t *cr,
+          double   gradient_angle,
+          double   rotate_angle,
+          int      n_stops)
+{
+    cairo_pattern_t *pattern;
+
+    cairo_rectangle (cr, 0, 0, 1, 1);
+    cairo_clip (cr);
+    cairo_new_path(cr);
+
+    cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
+    cairo_rectangle (cr, 0, 0, 1, 1);
+    cairo_fill (cr);
+
+    cairo_translate (cr, 0.5, 0.5);
+    cairo_scale (cr, 1 / 1.5, 1 / 1.5);
+    cairo_rotate (cr, rotate_angle);
+
+    pattern = cairo_pattern_create_linear (-0.5 * cos (gradient_angle),  -0.5 * sin (gradient_angle),
+                                           0.5 * cos (gradient_angle),   0.5 * sin (gradient_angle));
+
+    if (n_stops == 2) {
+       cairo_pattern_add_color_stop_rgb (pattern, 0.,
+                                         0.3, 0.3, 0.3);
+       cairo_pattern_add_color_stop_rgb (pattern, 1.,
+                                         1.0, 1.0, 1.0);
+    } else {
+       cairo_pattern_add_color_stop_rgb (pattern, 0.,
+                                         1.0, 0.0, 0.0);
+       cairo_pattern_add_color_stop_rgb (pattern, 0.5,
+                                         1.0, 1.0, 1.0);
+       cairo_pattern_add_color_stop_rgb (pattern, 1.,
+                                         0.0, 0.0, 1.0);
+    }
+
+    cairo_set_source (cr, pattern);
+    cairo_pattern_destroy (pattern);
+    cairo_rectangle (cr, -0.5, -0.5, 1, 1);
+    cairo_fill (cr);
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    int i, j, k;
+
+    cairo_set_source_rgb (cr, 0.5, 0.5, 0.5);
+    cairo_paint (cr);
+
+    for (i = 0; i < N_GRADIENT_ANGLES; i++)
+       for (j = 0; j < N_ROTATE_ANGLES; j++)
+         for (k = 0; k < N_N_STOPS; k++) {
+               cairo_save (cr);
+               cairo_translate (cr,
+                                PAD + (PAD + UNIT_SIZE) * i,
+                                PAD + (PAD + UNIT_SIZE) * (N_ROTATE_ANGLES * k + j));
+               cairo_scale (cr, UNIT_SIZE, UNIT_SIZE);
+
+               draw_unit (cr,
+                          gradient_angles[i] * M_PI / 180.,
+                          rotate_angles[j] * M_PI / 180.,
+                          n_stops[k]);
+               cairo_restore (cr);
+           }
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (linear_gradient,
+           "Tests the drawing of linear gradients",
+           "gradient", /* keywords */
+           NULL, /* requirements */
+           WIDTH, HEIGHT,
+           NULL, draw)
diff --git a/test/linear-step-function.c b/test/linear-step-function.c
new file mode 100644 (file)
index 0000000..da7e8ca
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * Copyright © 2010 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_pattern_t *pattern;
+
+    cairo_set_source_rgb (cr, 0, 0, 0);
+    cairo_paint (cr);
+
+    pattern = cairo_pattern_create_linear (width/2, 0, width/2, 0);
+    cairo_pattern_add_color_stop_rgb (pattern, 0, 1, 0, 0);
+    cairo_pattern_add_color_stop_rgb (pattern, 1, 0, 0, 1);
+    cairo_set_source (cr, pattern);
+
+    cairo_pattern_set_extend (pattern, CAIRO_EXTEND_NONE); /* nothing */
+    cairo_rectangle (cr, 0, 0, width, height/2);
+    cairo_fill (cr);
+
+    cairo_pattern_set_extend (pattern, CAIRO_EXTEND_PAD); /* step */
+    cairo_rectangle (cr, 0, height/2, width, height/2);
+    cairo_fill (cr);
+
+    cairo_pattern_destroy (pattern);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (linear_step_function,
+           "Tests creating a step function using a linear gradient",
+           "gradient, linear", /* keywords */
+           NULL, /* requirements */
+           40, 40,
+           NULL, draw)
diff --git a/test/linear-uniform.c b/test/linear-uniform.c
new file mode 100644 (file)
index 0000000..2f65535
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Copyright © 2010 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_pattern_t *pattern;
+
+    cairo_set_source_rgb (cr, 0, 0, 0);
+    cairo_paint (cr);
+
+    /* with alpha */
+    pattern = cairo_pattern_create_linear (0, 0, 0, height);
+    cairo_pattern_add_color_stop_rgba (pattern, 0, 1, 1, 1, .5);
+    cairo_pattern_add_color_stop_rgba (pattern, 1, 1, 1, 1, .5);
+    cairo_set_source (cr, pattern);
+    cairo_pattern_destroy (pattern);
+    cairo_rectangle (cr, 0, 0, width/2, height);
+    cairo_fill (cr);
+
+    /* without alpha */
+    pattern = cairo_pattern_create_linear (0, 0, 0, height);
+    cairo_pattern_add_color_stop_rgb (pattern, 0, 1, 1, 1);
+    cairo_pattern_add_color_stop_rgb (pattern, 1, 1, 1, 1);
+    cairo_set_source (cr, pattern);
+    cairo_pattern_destroy (pattern);
+    cairo_rectangle (cr, width/2, 0, width/2, height);
+    cairo_fill (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (linear_uniform,
+           "Tests handling of \"solid\" linear gradients",
+           "gradient, linear", /* keywords */
+           NULL, /* requirements */
+           40, 40,
+           NULL, draw)
diff --git a/test/long-dashed-lines.c b/test/long-dashed-lines.c
new file mode 100644 (file)
index 0000000..215bd91
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * Copyright © 2008 Chris Wilson
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * the author not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. The author makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THE AUTHOR. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    int i;
+    double dashes[] = {6, 3};
+
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_paint (cr);
+
+    /* partially visible rectangle... */
+    cairo_rectangle (cr, -0.5, -0.5, 61, 61);
+
+    /* rectangles with intersecting segments... */
+    cairo_save (cr);
+    cairo_translate (cr, 30, 30);
+    for (i = 0; i < 4; i++) {
+       cairo_rotate (cr, M_PI / 4);
+       cairo_rectangle (cr, -37, -15, 74, 30);
+    }
+    cairo_restore (cr);
+
+    /* completely invisible rectangle */
+    cairo_rectangle (cr, -5, -5, 70, 70);
+
+    cairo_set_dash (cr, dashes, ARRAY_LENGTH (dashes), 0.);
+    cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
+    cairo_set_line_join (cr, CAIRO_LINE_JOIN_BEVEL);
+    cairo_set_source_rgb (cr, 0, 0, 0);
+    cairo_stroke (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (long_dashed_lines,
+           "Exercises _cairo_box_intersects_line_segment()",
+           "dash, stroke, stress", /* keywords */
+           NULL, /* requirements */
+           60, 60,
+           NULL, draw)
diff --git a/test/long-lines.c b/test/long-lines.c
new file mode 100644 (file)
index 0000000..79fc98f
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Authors: Carl D. Worth <cworth@cworth.org>
+ *         Emmanuel Pacaud <emmanuel.pacaud@lapp.in2p3.fr>
+ */
+
+#include "cairo-test.h"
+
+#define LINE_WIDTH     1.
+#define SIZE           10
+#define LINE_NBR       6
+
+struct {
+    double length;
+    double red, green, blue;
+} lines[LINE_NBR] = {
+    {       100.0, 1.0, 0.0, 0.0 },
+    {     10000.0, 0.0, 1.0, 0.0 },
+    {    100000.0, 0.0, 0.0, 1.0 },
+    {   1000000.0, 1.0, 1.0, 0.0 },
+    {  10000000.0, 0.0, 1.0, 1.0 },
+    { 100000000.0, 1.0, 0.0, 1.0 }
+};
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    double pos;
+    int i;
+
+    /* We draw in the default black, so paint white first. */
+    cairo_save (cr);
+    cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); /* white */
+    cairo_paint (cr);
+    cairo_restore (cr);
+
+    cairo_set_line_width (cr, LINE_WIDTH);
+
+    pos = SIZE + .5;
+    for (i = 0; i < LINE_NBR; i++) {
+       cairo_move_to (cr, pos, -lines[i].length);
+       cairo_line_to (cr, pos, +lines[i].length);
+       cairo_set_source_rgb (cr, lines[i].red, lines[i].green, lines[i].blue);
+       cairo_stroke (cr);
+       pos += SIZE;
+    }
+
+    /* This should display a perfect vertically centered black line */
+    cairo_move_to (cr, 0.5, -1e100);
+    cairo_line_to (cr, pos,  1e100);
+    cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
+    cairo_stroke (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (long_lines,
+           "Test long lines"
+           "\nLong lines are not drawn due to the limitations of the internal 16.16 fixed-point coordinates",
+           "stroke, stress", /* keywords */
+           NULL, /* requirements */
+           SIZE * (LINE_NBR + 1), SIZE * (LINE_NBR + 1),
+           NULL, draw)
+
diff --git a/test/make-cairo-test-constructors.sh b/test/make-cairo-test-constructors.sh
new file mode 100644 (file)
index 0000000..cb1391e
--- /dev/null
@@ -0,0 +1,29 @@
+#! /bin/sh
+
+if test $# -eq 0; then
+    echo "$0: no input files." >&2
+    exit 0
+fi
+
+cat <<HERE
+/* WARNING: Autogenerated file - see $0! */
+
+#include "cairo-test-private.h"
+
+void _cairo_test_runner_register_tests (void);
+
+HERE
+
+cat "$@" |  sed '/^CAIRO_TEST/!d; s/CAIRO_TEST.*(\(.*\),.*/extern void _register_\1 (void);/'
+cat <<HERE
+
+void
+_cairo_test_runner_register_tests (void)
+{
+HERE
+
+cat "$@" |  sed '/^CAIRO_TEST/!d; s/CAIRO_TEST.*(\(.*\),.*/    _register_\1 ();/'
+
+echo "}"
+
+
diff --git a/test/map-to-image.c b/test/map-to-image.c
new file mode 100644 (file)
index 0000000..0262245
--- /dev/null
@@ -0,0 +1,156 @@
+/*
+ * Copyright © 2011 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+
+#define WIDTH  3
+#define HEIGHT 3
+
+/* A single, black pixel */
+static const uint32_t black_pixel_argb = 0xff000000;
+static const uint32_t black_pixel      = 0x00000000;
+
+static cairo_bool_t
+set_pixel_black(uint8_t *data, int stride,
+         cairo_format_t format, int x, int y)
+{
+    switch (format) {
+    case CAIRO_FORMAT_ARGB32:
+    case CAIRO_FORMAT_RGB24:
+       *(uint32_t *)(data + y * stride + 4*x) = black_pixel_argb;
+       break;
+    case CAIRO_FORMAT_RGB16_565:
+       *(uint16_t *)(data + y * stride + 2*x) = black_pixel;
+       break;
+    case CAIRO_FORMAT_RGB30:
+    case CAIRO_FORMAT_A8:
+    case CAIRO_FORMAT_A1:
+    case CAIRO_FORMAT_INVALID:
+    default:
+       return FALSE;
+    }
+    return TRUE;
+}
+
+static cairo_test_status_t
+all (cairo_t *cr, int width, int height)
+{
+    cairo_surface_t *surface;
+    uint8_t *data;
+    int stride;
+    cairo_format_t format;
+    int i, j;
+
+    /* Fill background white */
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_paint (cr);
+
+    surface = cairo_surface_map_to_image (cairo_get_target (cr), NULL);
+    cairo_surface_flush (surface);
+    format = cairo_image_surface_get_format (surface);
+    stride = cairo_image_surface_get_stride (surface);
+    data = cairo_image_surface_get_data (surface);
+    if (data) {
+       for (j = 0; j < HEIGHT; j++)
+           for (i = 0; i < WIDTH; i++)
+               if (! set_pixel_black (data, stride, format, i, j))
+                   return CAIRO_TEST_FAILURE;
+    }
+    cairo_surface_mark_dirty (surface);
+    cairo_surface_unmap_image (cairo_get_target (cr), surface);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+static cairo_test_status_t
+bit (cairo_t *cr, int width, int height)
+{
+    cairo_surface_t *surface;
+    cairo_rectangle_int_t extents;
+    cairo_format_t format;
+    uint8_t *data;
+
+    extents.x = extents.y = extents.width = extents.height = 1;
+
+    /* Fill background white */
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_paint (cr);
+
+    surface = cairo_surface_map_to_image (cairo_get_target (cr), &extents);
+    cairo_surface_flush (surface);
+    data = cairo_image_surface_get_data (surface);
+    format = cairo_image_surface_get_format (surface);
+    if (data) {
+       if (! set_pixel_black (data, 0, format, 0, 0))
+           return CAIRO_TEST_FAILURE;
+    }
+    cairo_surface_mark_dirty (surface);
+    cairo_surface_unmap_image (cairo_get_target (cr), surface);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+static cairo_test_status_t
+fill (cairo_t *cr, int width, int height)
+{
+    cairo_surface_t *surface;
+    cairo_rectangle_int_t extents;
+    cairo_t *cr2;
+
+    extents.x = extents.y = extents.width = extents.height = 1;
+
+    /* Fill background white */
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_paint (cr);
+
+    surface = cairo_surface_map_to_image (cairo_get_target (cr), &extents);
+    cr2 = cairo_create (surface);
+    cairo_set_source_rgb (cr2, 1, 0, 0);
+    cairo_paint (cr2);
+    cairo_destroy (cr2);
+    cairo_surface_unmap_image (cairo_get_target (cr), surface);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (map_all_to_image,
+           "Test maping a surface to an image and modifying it externally",
+           "image", /* keywords */
+           "target=raster", /* requirements */
+           WIDTH, HEIGHT,
+           NULL, all)
+CAIRO_TEST (map_bit_to_image,
+           "Test maping a surface to an image and modifying it externally",
+           "image", /* keywords */
+           "target=raster", /* requirements */
+           WIDTH, HEIGHT,
+           NULL, bit)
+CAIRO_TEST (map_to_image_fill,
+           "Test maping a surface to an image and modifying it externally",
+           "image", /* keywords */
+           "target=raster", /* requirements */
+           WIDTH, HEIGHT,
+           NULL, fill)
diff --git a/test/mask-alpha.c b/test/mask-alpha.c
new file mode 100644 (file)
index 0000000..d66e0ac
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * Copyright © 2007 Adrian Johnson
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Adrian Johnson <ajohnson@redneon.com>
+ */
+
+#include "cairo-test.h"
+
+#define SIZE 40
+#define PAD 2
+#define WIDTH (PAD + SIZE + PAD)
+#define HEIGHT WIDTH
+
+/* This test is designed to test that PDF viewers use the correct
+ * alpha values in an Alpha SMasks. Some viewers use the color values
+ * instead of the alpha. The test draws a triangle and rectangle in a
+ * group then draws the group using cairo_mask(). The mask consists of
+ * a circle with the rgba (0.4, 0.4, 0.4, 0.8) and the background rgba
+ * (0.8, 0.8, 0.8, 0.4).
+ */
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_pattern_t *pattern;
+
+    cairo_translate (cr, PAD, PAD);
+
+    /* mask */
+    cairo_push_group (cr);
+    cairo_set_source_rgba (cr, 0.8, 0.8, 0.8, 0.4);
+    cairo_paint (cr);
+    cairo_arc (cr, SIZE / 2, SIZE / 2, SIZE / 6, 0., 2. * M_PI);
+    cairo_set_source_rgba (cr, 0.4, 0.4, 0.4, 0.8);
+    cairo_fill (cr);
+    pattern = cairo_pop_group (cr);
+
+    /* source */
+    cairo_push_group (cr);
+    cairo_rectangle (cr, 0.3 * SIZE, 0.2 * SIZE, 0.5 * SIZE, 0.5 * SIZE);
+    cairo_set_source_rgb (cr, 0, 0, 1);
+    cairo_fill (cr);
+    cairo_move_to     (cr,   0.0,          0.8 * SIZE);
+    cairo_rel_line_to (cr,   0.7 * SIZE,   0.0);
+    cairo_rel_line_to (cr, -0.375 * SIZE, -0.6 * SIZE);
+    cairo_close_path (cr);
+    cairo_set_source_rgb (cr, 0, 1, 0);
+    cairo_fill (cr);
+    cairo_pop_group_to_source (cr);
+
+    cairo_mask (cr, pattern);
+    cairo_pattern_destroy (pattern);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (mask_alpha,
+           "A simple test painting a group through a circle mask",
+           "mask, alpha", /* keywords */
+           NULL, /* requirements */
+           WIDTH, HEIGHT,
+           NULL, draw)
diff --git a/test/mask-ctm.c b/test/mask-ctm.c
new file mode 100644 (file)
index 0000000..205ab06
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ */
+
+#include "cairo-test.h"
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_surface_t *mask_surface;
+    cairo_pattern_t *mask;
+    uint32_t data[] = {
+       0x80000000, 0x80000000,
+       0x80000000, 0x80000000,
+    };
+    cairo_matrix_t matrix;
+
+    mask_surface = cairo_image_surface_create_for_data ((unsigned char *) data,
+                                                       CAIRO_FORMAT_ARGB32, 2, 2, 8);
+    mask = cairo_pattern_create_for_surface (mask_surface);
+
+    cairo_set_source_rgb (cr, 1.0, 0, 0);
+
+    /* We can translate with the CTM, with the pattern matrix, or with
+     * both. */
+
+    /* 1. CTM alone. */
+    cairo_save (cr);
+    {
+       cairo_translate (cr, 2, 2);
+       cairo_mask (cr, mask);
+    }
+    cairo_restore (cr);
+
+    /* 2. Pattern matrix alone. */
+    cairo_matrix_init_translate (&matrix, -4, -4);
+    cairo_pattern_set_matrix (mask, &matrix);
+
+    cairo_mask (cr, mask);
+
+    /* 3. CTM + pattern matrix */
+    cairo_translate (cr, 2, 2);
+    cairo_mask (cr, mask);
+
+    cairo_pattern_destroy (mask);
+
+    cairo_surface_finish (mask_surface); /* data goes out of scope */
+    cairo_surface_destroy (mask_surface);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (mask_ctm,
+           "Test that cairo_mask is affected properly by the CTM",
+           "mask", /* keywords */
+           NULL, /* requirements */
+           10, 10,
+           NULL, draw)
+
diff --git a/test/mask-glyphs.c b/test/mask-glyphs.c
new file mode 100644 (file)
index 0000000..9ac0272
--- /dev/null
@@ -0,0 +1,189 @@
+/*
+ * Copyright 2009 Chris Wilson
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Chris Wilson not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Chris Wilson makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * CHRIS WILSON DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL CHRIS WILSON BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+
+#include <assert.h>
+
+static const char *png_filename = "romedalen.png";
+
+#define WIDTH 800
+#define HEIGHT 600
+
+static cairo_status_t
+_image_to_glyphs (cairo_surface_t *image,
+                 int channel,
+                 int level,
+                 cairo_scaled_font_t *scaled_font,
+                 double tx, double ty,
+                 cairo_glyph_t *glyphs,
+                 int *num_glyphs)
+{
+    int width, height, stride;
+    const unsigned char *data;
+    cairo_format_t format;
+    int x, y, z, n;
+
+    width = cairo_image_surface_get_width (image);
+    height = cairo_image_surface_get_height (image);
+    stride = cairo_image_surface_get_stride (image);
+    format = cairo_image_surface_get_format (image);
+    data = cairo_image_surface_get_data (image);
+
+    n = 0;
+    for (y = 0; y < height; y++) {
+       const uint32_t *row = (uint32_t *) (data + y * stride);
+
+       for (x = 0; x < width; x++) {
+           z = (row[x] >> channel) & 0xff;
+           if (z == level) {
+               double xx, yy, zz;
+               char c = n % 26 + 'a';
+               int count = 1;
+               cairo_glyph_t *glyphs_p = &glyphs[n];
+               cairo_status_t status;
+
+               xx = 4 * (x - width/2.) + width/2.;
+               yy = 4 * (y - height/2.) + height/2.;
+
+               zz = z / 1000.;
+               xx = xx + zz*(width/2. - xx);
+               yy = yy + zz*(height/2. - yy);
+
+               cairo_scaled_font_text_to_glyphs (scaled_font,
+                                                 tx + xx, ty + yy,
+                                                 &c, 1,
+                                                 &glyphs_p, &count,
+                                                 NULL, NULL,
+                                                 NULL);
+               status = cairo_scaled_font_status (scaled_font);
+               if (status)
+                   return status;
+
+               assert (glyphs_p == &glyphs[n]);
+               assert (count == 1);
+               n++;
+           }
+       }
+    }
+
+    *num_glyphs = n;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_render_image (cairo_t *cr,
+              int width, int height,
+              cairo_surface_t *image)
+{
+    int ww, hh;
+    cairo_glyph_t *glyphs;
+    cairo_pattern_t *mask;
+    cairo_scaled_font_t *scaled_font;
+    double tx, ty;
+    const struct {
+       int shift;
+       double red;
+       double green;
+       double blue;
+    } channel[3] = {
+       {  0, 0.9, 0.3, 0.4 },
+       {  8, 0.4, 0.9, 0.3 },
+       { 16, 0.3, 0.4, 0.9 },
+    };
+    unsigned int n, i;
+
+    ww = cairo_image_surface_get_width (image);
+    hh = cairo_image_surface_get_height (image);
+
+    glyphs = cairo_glyph_allocate (ww * hh);
+    if (glyphs == NULL)
+       return CAIRO_STATUS_NO_MEMORY;
+
+    tx = (width - ww) / 2.;
+    ty = (height - hh) / 2.;
+
+    cairo_set_font_size (cr, 5);
+    scaled_font = cairo_get_scaled_font (cr);
+
+    for (i = 0; i < ARRAY_LENGTH (channel); i++) {
+       cairo_push_group_with_content (cr, CAIRO_CONTENT_ALPHA);
+       for (n = 0; n < 256; n++) {
+           cairo_status_t status;
+           int num_glyphs;
+
+           status = _image_to_glyphs (image, channel[i].shift, n,
+                                      scaled_font,
+                                      tx, ty, glyphs, &num_glyphs);
+           if (status) {
+               cairo_glyph_free (glyphs);
+               return status;
+           }
+
+           cairo_set_source_rgba (cr,
+                                  0, 0, 0,
+                                  .15 + .85 * n / 255.);
+           cairo_show_glyphs (cr, glyphs, num_glyphs);
+       }
+       mask = cairo_pop_group (cr);
+       cairo_set_source_rgb (cr,
+                             channel[i].red,
+                             channel[i].green,
+                             channel[i].blue);
+       cairo_mask (cr, mask);
+       cairo_pattern_destroy (mask);
+    }
+
+    cairo_glyph_free (glyphs);
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    const cairo_test_context_t *ctx = cairo_test_get_context (cr);
+    cairo_surface_t *image;
+    cairo_status_t status;
+
+    cairo_set_source_rgb (cr, 0, 0, 0);
+    cairo_paint (cr);
+
+    image = cairo_test_create_surface_from_png (ctx, png_filename);
+    status = cairo_surface_status (image);
+    if (status)
+       return cairo_test_status_from_status (ctx, status);
+
+    status = _render_image (cr, width, height, image);
+    cairo_surface_destroy (image);
+
+    return cairo_test_status_from_status (ctx, status);
+}
+
+CAIRO_TEST (mask_glyphs,
+           "Creates a mask using a distorted array of overlapping glyphs",
+           "mask, glyphs", /* keywords */
+           "slow", /* requirements */
+           WIDTH, HEIGHT,
+           NULL, draw)
diff --git a/test/mask-surface-ctm.c b/test/mask-surface-ctm.c
new file mode 100644 (file)
index 0000000..064de3c
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ */
+
+#include "cairo-test.h"
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_surface_t *mask;
+    uint32_t data[] = {
+       0x80000000, 0x80000000,
+       0x80000000, 0x80000000,
+    };
+
+    mask = cairo_image_surface_create_for_data ((unsigned char *) data,
+                                               CAIRO_FORMAT_ARGB32, 2, 2, 8);
+
+    cairo_set_source_rgb (cr, 1.0, 0, 0);
+
+    /* We can translate with the CTM, with the mask_surface offset, or
+     * with both. */
+
+    /* 1. CTM alone. */
+    cairo_save (cr);
+    {
+       cairo_translate (cr, 2, 2);
+       cairo_mask_surface (cr, mask, 0, 0);
+    }
+    cairo_restore (cr);
+
+    /* 2. Offset alone. */
+    cairo_mask_surface (cr, mask, 4, 4);
+
+    /* 3. CTM + offset */
+    cairo_translate (cr, 2, 2);
+    cairo_mask_surface (cr, mask, 4, 4);
+
+    cairo_surface_finish (mask); /* data goes out of scope */
+    cairo_surface_destroy (mask);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (mask_surface_ctm,
+           "Test that cairo_mask_surface is affected properly by the CTM",
+           "mask", /* keywords */
+           NULL, /* requirements */
+           10, 10,
+           NULL, draw)
diff --git a/test/mask-transformed-image.c b/test/mask-transformed-image.c
new file mode 100644 (file)
index 0000000..13c8fe7
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2008 Kai-Uwe Behrmann
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Kai-Uwe Behrmann not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Kai-Uwe Behrmann makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * KAI_UWE BEHRMANN DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL KAI_UWE BEHRMANN BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Kai-Uwe Behrmann <ku.b@gmx.de>
+ *         Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+
+static const char png_filename[] = "romedalen.png";
+
+static cairo_surface_t *
+create_mask (cairo_t *dst, int width, int height)
+{
+    cairo_surface_t *mask;
+    cairo_t *cr;
+
+    mask = cairo_image_surface_create (CAIRO_FORMAT_A8, width, height);
+    cr = cairo_create (mask);
+    cairo_surface_destroy (mask);
+
+    cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
+    cairo_rectangle (cr, width/4, height/4, width/2, height/2);
+    cairo_fill (cr);
+
+    mask = cairo_surface_reference (cairo_get_target (cr));
+    cairo_destroy (cr);
+
+    return mask;
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    const cairo_test_context_t *ctx = cairo_test_get_context (cr);
+    cairo_surface_t *image, *mask;
+
+    image = cairo_test_create_surface_from_png (ctx, png_filename);
+    mask = create_mask (cr, 40, 40);
+
+    /* opaque background */
+    cairo_paint (cr);
+
+    /* center */
+    cairo_translate (cr,
+                    (width - cairo_image_surface_get_width (image)) / 2.,
+                    (height - cairo_image_surface_get_height (image)) / 2.);
+
+    /* rotate 30 degree around the center */
+    cairo_translate (cr, width/2., height/2.);
+    cairo_rotate (cr, -30 * 2 * M_PI / 360);
+    cairo_translate (cr, -width/2., -height/2.);
+
+    /* place the image on our surface */
+    cairo_set_source_surface (cr, image, 0, 0);
+
+    /* reset the drawing matrix */
+    cairo_identity_matrix (cr);
+
+    /* fill nicely */
+    cairo_scale (cr, width / 40., height / 40.);
+
+    /* apply the mask */
+    cairo_mask_surface (cr, mask, 0, 0);
+
+    cairo_surface_destroy (mask);
+    cairo_surface_destroy (image);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (mask_transformed_image,
+           "Test that cairo_mask() is affected properly by the CTM and not the image",
+           "mask", /* keywords */
+           NULL, /* requirements */
+           80, 80,
+           NULL, draw)
diff --git a/test/mask-transformed-similar.c b/test/mask-transformed-similar.c
new file mode 100644 (file)
index 0000000..c37deb2
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2008 Kai-Uwe Behrmann
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Kai-Uwe Behrmann not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Kai-Uwe Behrmann makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * KAI_UWE BEHRMANN DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL KAI_UWE BEHRMANN BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Kai-Uwe Behrmann <ku.b@gmx.de>
+ *         Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+
+static const char png_filename[] = "romedalen.png";
+
+static cairo_surface_t *
+create_mask (cairo_t *dst, int width, int height)
+{
+    cairo_surface_t *mask;
+    cairo_t *cr;
+
+    mask = cairo_surface_create_similar (cairo_get_target (dst),
+                                        CAIRO_CONTENT_ALPHA,
+                                        width, height);
+    cr = cairo_create (mask);
+    cairo_surface_destroy (mask);
+
+    cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
+    cairo_rectangle (cr, width/4, height/4, width/2, height/2);
+    cairo_fill (cr);
+
+    mask = cairo_surface_reference (cairo_get_target (cr));
+    cairo_destroy (cr);
+
+    return mask;
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    const cairo_test_context_t *ctx = cairo_test_get_context (cr);
+    cairo_surface_t *image, *mask;
+
+    image = cairo_test_create_surface_from_png (ctx, png_filename);
+    mask = create_mask (cr, 40, 40);
+
+    /* opaque background */
+    cairo_paint (cr);
+
+    /* center */
+    cairo_translate (cr,
+                    (width - cairo_image_surface_get_width (image)) / 2.,
+                    (height - cairo_image_surface_get_height (image)) / 2.);
+
+    /* rotate 30 degree around the center */
+    cairo_translate (cr, width/2., height/2.);
+    cairo_rotate (cr, -30 * 2 * M_PI / 360);
+    cairo_translate (cr, -width/2., -height/2.);
+
+    /* place the image on our surface */
+    cairo_set_source_surface (cr, image, 0, 0);
+
+    /* reset the drawing matrix */
+    cairo_identity_matrix (cr);
+
+    /* fill nicely */
+    cairo_scale (cr, width / 40., height / 40.);
+
+    /* apply the mask */
+    cairo_mask_surface (cr, mask, 0, 0);
+
+    cairo_surface_destroy (mask);
+    cairo_surface_destroy (image);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (mask_transformed_similar,
+           "Test that cairo_mask() is affected properly by the CTM and not the image",
+           "mask", /* keywords */
+           NULL, /* requirements */
+           80, 80,
+           NULL, draw)
diff --git a/test/mask.c b/test/mask.c
new file mode 100644 (file)
index 0000000..cc722d3
--- /dev/null
@@ -0,0 +1,246 @@
+/*
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Authors: Owen Taylor <otaylor@redhat.com>
+ *          Kristian Høgsberg <krh@redhat.com>
+ */
+
+#include <math.h>
+#include "cairo-test.h"
+#include <stdio.h>
+
+#define WIDTH 16
+#define HEIGHT 16
+#define PAD 2
+
+static const char *png_filename = "romedalen.png";
+static cairo_surface_t *image;
+
+static void
+set_solid_pattern (const cairo_test_context_t *ctx, cairo_t *cr, int x, int y)
+{
+    cairo_set_source_rgb (cr, 0, 0, 0.6);
+}
+
+static void
+set_translucent_pattern (const cairo_test_context_t *ctx, cairo_t *cr, int x, int y)
+{
+    cairo_set_source_rgba (cr, 0, 0, 0.6, 0.5);
+}
+
+static void
+set_gradient_pattern (const cairo_test_context_t *ctx, cairo_t *cr, int x, int y)
+{
+    cairo_pattern_t *pattern;
+
+    pattern =
+       cairo_pattern_create_linear (x, y, x + WIDTH, y + HEIGHT);
+    cairo_pattern_add_color_stop_rgba (pattern, 0, 1, 1, 1, 1);
+    cairo_pattern_add_color_stop_rgba (pattern, 1, 0, 0, 0.4, 1);
+    cairo_set_source (cr, pattern);
+    cairo_pattern_destroy (pattern);
+}
+
+static void
+set_image_pattern (const cairo_test_context_t *ctx, cairo_t *cr, int x, int y)
+{
+    cairo_pattern_t *pattern;
+
+    if (image == NULL || cairo_surface_status (image)) {
+       cairo_surface_destroy (image);
+       image = cairo_test_create_surface_from_png (ctx, png_filename);
+    }
+
+    pattern = cairo_pattern_create_for_surface (image);
+    cairo_pattern_set_extend (pattern, CAIRO_EXTEND_REPEAT);
+    cairo_set_source (cr, pattern);
+    cairo_pattern_destroy (pattern);
+}
+
+static void
+mask_polygon (cairo_t *cr, int x, int y)
+{
+    cairo_surface_t *mask_surface;
+    cairo_t *cr2;
+
+    mask_surface = cairo_surface_create_similar (cairo_get_group_target (cr),
+                                                CAIRO_CONTENT_ALPHA,
+                                                WIDTH, HEIGHT);
+    cr2 = cairo_create (mask_surface);
+    cairo_surface_destroy (mask_surface);
+
+    cairo_save (cr2);
+    cairo_set_operator (cr2, CAIRO_OPERATOR_CLEAR);
+    cairo_paint (cr2);
+    cairo_restore (cr2);
+
+    cairo_set_source_rgb (cr2, 1, 1, 1); /* white */
+
+    cairo_new_path (cr2);
+    cairo_move_to (cr2, 0, 0);
+    cairo_line_to (cr2, 0, HEIGHT);
+    cairo_line_to (cr2, WIDTH / 2, 3 * HEIGHT / 4);
+    cairo_line_to (cr2, WIDTH, HEIGHT);
+    cairo_line_to (cr2, WIDTH, 0);
+    cairo_line_to (cr2, WIDTH / 2, HEIGHT / 4);
+    cairo_close_path (cr2);
+    cairo_fill (cr2);
+
+    cairo_mask_surface (cr, cairo_get_target (cr2), x, y);
+    cairo_destroy (cr2);
+}
+
+static void
+mask_alpha (cairo_t *cr, int x, int y)
+{
+    cairo_paint_with_alpha (cr, 0.75);
+}
+
+static void
+mask_gradient (cairo_t *cr, int x, int y)
+{
+    cairo_pattern_t *pattern;
+
+    pattern = cairo_pattern_create_linear (x, y,
+                                          x + WIDTH, y + HEIGHT);
+
+    cairo_pattern_add_color_stop_rgba (pattern,
+                                      0,
+                                      1, 1, 1, 1);
+    cairo_pattern_add_color_stop_rgba (pattern,
+                                      1,
+                                      1, 1, 1, 0);
+
+    cairo_mask (cr, pattern);
+
+    cairo_pattern_destroy (pattern);
+}
+
+static void
+clip_none (cairo_t *cr, int x, int y)
+{
+}
+
+static void
+clip_rects (cairo_t *cr, int x, int y)
+{
+    int height = HEIGHT / 3;
+
+    cairo_new_path (cr);
+    cairo_rectangle (cr, x, y, WIDTH, height);
+    cairo_rectangle (cr, x, y + 2 * height, WIDTH, height);
+    cairo_clip (cr);
+}
+
+static void
+clip_circle (cairo_t *cr, int x, int y)
+{
+    cairo_new_path (cr);
+    cairo_arc (cr, x + WIDTH / 2, y + HEIGHT / 2,
+              WIDTH / 2, 0, 2 * M_PI);
+    cairo_clip (cr);
+    cairo_new_path (cr);
+}
+
+static void (* const pattern_funcs[])(const cairo_test_context_t *ctx, cairo_t *cr, int x, int y) = {
+    set_solid_pattern,
+    set_translucent_pattern,
+    set_gradient_pattern,
+    set_image_pattern,
+};
+
+static void (* const mask_funcs[])(cairo_t *cr, int x, int y) = {
+    mask_alpha,
+    mask_gradient,
+    mask_polygon,
+};
+
+static void (* const clip_funcs[])(cairo_t *cr, int x, int y) = {
+    clip_none,
+    clip_rects,
+    clip_circle,
+};
+
+#define IMAGE_WIDTH (ARRAY_LENGTH (pattern_funcs) * (WIDTH + PAD) + PAD)
+#define IMAGE_HEIGHT (ARRAY_LENGTH (mask_funcs) * ARRAY_LENGTH (clip_funcs) * (HEIGHT + PAD) + PAD)
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    const cairo_test_context_t *ctx = cairo_test_get_context (cr);
+    cairo_surface_t *tmp_surface;
+    size_t i, j, k;
+    cairo_t *cr2;
+
+    /* Some of our drawing is unbounded, so we draw each test to
+     * a temporary surface and copy over.
+     */
+    tmp_surface = cairo_surface_create_similar (cairo_get_group_target (cr),
+                                               CAIRO_CONTENT_COLOR_ALPHA,
+                                               IMAGE_WIDTH, IMAGE_HEIGHT);
+    cr2 = cairo_create (tmp_surface);
+    cairo_surface_destroy (tmp_surface);
+
+    for (k = 0; k < ARRAY_LENGTH (clip_funcs); k++) {
+       for (j = 0; j < ARRAY_LENGTH (mask_funcs); j++) {
+           for (i = 0; i < ARRAY_LENGTH (pattern_funcs); i++) {
+               int x = i * (WIDTH + PAD) + PAD;
+               int y = (ARRAY_LENGTH (mask_funcs) * k + j) * (HEIGHT + PAD) + PAD;
+
+               /* Clear intermediate surface we are going to be drawing onto */
+               cairo_save (cr2);
+               cairo_set_operator (cr2, CAIRO_OPERATOR_CLEAR);
+               cairo_paint (cr2);
+               cairo_restore (cr2);
+
+               /* draw */
+               cairo_save (cr2);
+
+               clip_funcs[k] (cr2, x, y);
+               pattern_funcs[i] (ctx, cr2, x, y);
+               mask_funcs[j] (cr2, x, y);
+
+               cairo_restore (cr2);
+
+               /* Copy back to the main pixmap */
+               cairo_set_source_surface (cr, cairo_get_target (cr2), 0, 0);
+               cairo_rectangle (cr, x, y, WIDTH, HEIGHT);
+               cairo_fill (cr);
+           }
+       }
+    }
+
+    cairo_destroy (cr2);
+
+    cairo_surface_destroy (image);
+    image = NULL;
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (mask,
+           "Tests of cairo_mask",
+           "mask", /* keywords */
+           NULL, /* requirements */
+           IMAGE_WIDTH, IMAGE_HEIGHT,
+           NULL, draw)
+
diff --git a/test/mesh-pattern-accuracy.c b/test/mesh-pattern-accuracy.c
new file mode 100644 (file)
index 0000000..92e1523
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * Copyright © 2009 Adrian Johnson
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Adrian Johnson <ajohnson@redneon.com>
+ */
+
+#include "cairo-test.h"
+#include <float.h>
+
+#define SIZE 256
+
+/* This test is designed to test the accuracy of the rendering of mesh
+ * patterns.
+ *
+ * Color accuracy is tested by a square patch covering the whole
+ * surface with black and white corners.
+ *
+ * Extents accuracy is checked by a small red square patch at the
+ * center of the surface which should measure 2x2 pixels.
+ */
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_pattern_t *pattern;
+    double offset;
+
+    cairo_test_paint_checkered (cr);
+
+    pattern = cairo_pattern_create_mesh ();
+
+    cairo_mesh_pattern_begin_patch (pattern);
+
+    cairo_mesh_pattern_move_to (pattern, 0, 0);
+    cairo_mesh_pattern_line_to (pattern, 1, 0);
+    cairo_mesh_pattern_line_to (pattern, 1, 1);
+    cairo_mesh_pattern_line_to (pattern, 0, 1);
+
+    cairo_mesh_pattern_set_corner_color_rgb (pattern, 0, 0, 0, 0);
+    cairo_mesh_pattern_set_corner_color_rgb (pattern, 1, 1, 1, 1);
+    cairo_mesh_pattern_set_corner_color_rgb (pattern, 2, 0, 0, 0);
+    cairo_mesh_pattern_set_corner_color_rgb (pattern, 3, 1, 1, 1);
+
+    cairo_mesh_pattern_end_patch (pattern);
+
+    cairo_mesh_pattern_begin_patch (pattern);
+
+    /* A small 1x1 red patch, that should be rendered as a 2x2 red
+     * square in the center of the image */
+
+    offset = 0.5 / SIZE;
+
+    cairo_mesh_pattern_move_to (pattern, 0.5 + offset, 0.5 + offset);
+    cairo_mesh_pattern_line_to (pattern, 0.5 + offset, 0.5 - offset);
+    cairo_mesh_pattern_line_to (pattern, 0.5 - offset, 0.5 - offset);
+    cairo_mesh_pattern_line_to (pattern, 0.5 - offset, 0.5 + offset);
+
+    cairo_mesh_pattern_set_corner_color_rgb (pattern, 0, 1, 0, 0);
+    cairo_mesh_pattern_set_corner_color_rgb (pattern, 1, 1, 0, 0);
+    cairo_mesh_pattern_set_corner_color_rgb (pattern, 2, 1, 0, 0);
+    cairo_mesh_pattern_set_corner_color_rgb (pattern, 3, 1, 0, 0);
+
+    cairo_mesh_pattern_end_patch (pattern);
+
+    cairo_scale (cr, SIZE, SIZE);
+
+    cairo_set_source (cr, pattern);
+    cairo_paint (cr);
+    cairo_pattern_destroy (pattern);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (mesh_pattern_accuracy,
+           "Paint mesh pattern",
+           "mesh,pattern", /* keywords */
+           NULL, /* requirements */
+           SIZE, SIZE,
+           NULL, draw)
diff --git a/test/mesh-pattern-conical.c b/test/mesh-pattern-conical.c
new file mode 100644 (file)
index 0000000..8138884
--- /dev/null
@@ -0,0 +1,135 @@
+/*
+ * Copyright © 2009 Adrian Johnson
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Adrian Johnson <ajohnson@redneon.com>
+ */
+
+#include "cairo-test.h"
+#include <math.h>
+
+#define PAT_WIDTH  100
+#define PAT_HEIGHT 100
+#define SIZE PAT_WIDTH
+#define PAD 2
+#define WIDTH (PAD + SIZE + PAD)
+#define HEIGHT WIDTH
+
+
+/*
+ * This test is designed to paint a mesh pattern which contains 8
+ * circular sectors approximating a conical gradient.
+ */
+
+#define CENTER_X 50
+#define CENTER_Y 50
+#define RADIUS   50
+
+static void
+sector_patch (cairo_pattern_t *pattern,
+             double angle_A,
+             double A_r, double A_g, double A_b,
+             double angle_B,
+             double B_r, double B_g, double B_b)
+{
+    double r_sin_A, r_cos_A;
+    double r_sin_B, r_cos_B;
+    double h;
+
+    r_sin_A = RADIUS * sin (angle_A);
+    r_cos_A = RADIUS * cos (angle_A);
+    r_sin_B = RADIUS * sin (angle_B);
+    r_cos_B = RADIUS * cos (angle_B);
+
+    h = 4.0/3.0 * tan ((angle_B - angle_A) / 4.0);
+
+    cairo_mesh_pattern_begin_patch (pattern);
+
+    cairo_mesh_pattern_move_to (pattern, CENTER_X, CENTER_Y);
+    cairo_mesh_pattern_line_to (pattern,
+                               CENTER_X + r_cos_A,
+                               CENTER_Y + r_sin_A);
+
+    cairo_mesh_pattern_curve_to (pattern,
+                                CENTER_X + r_cos_A - h * r_sin_A,
+                                CENTER_Y + r_sin_A + h * r_cos_A,
+                                CENTER_X + r_cos_B + h * r_sin_B,
+                                CENTER_Y + r_sin_B - h * r_cos_B,
+                                CENTER_X + r_cos_B,
+                                CENTER_Y + r_sin_B);
+
+    cairo_mesh_pattern_set_corner_color_rgb (pattern, 0, 1, 1, 1);
+    cairo_mesh_pattern_set_corner_color_rgb (pattern, 1, A_r, A_g, A_b);
+    cairo_mesh_pattern_set_corner_color_rgb (pattern, 2, B_r, B_g, B_b);
+
+    cairo_mesh_pattern_end_patch (pattern);
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_pattern_t *pattern;
+
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_paint (cr);
+
+    cairo_translate (cr, PAD, PAD);
+
+    pattern = cairo_pattern_create_mesh ();
+    sector_patch (pattern,
+                 0,         1, 0, 0,
+                 M_PI/4,    1, 1, 0);
+    sector_patch (pattern,
+                 M_PI/4,    0, 1, 0,
+                 M_PI/2,    0, 1, 1);
+    sector_patch (pattern,
+                 M_PI/2,    0, 0, 1,
+                 3*M_PI/4,  1, 0, 1);
+    sector_patch (pattern,
+                 3*M_PI/4,  1, 0, 0,
+                 M_PI,      1, 1, 0);
+    sector_patch (pattern,
+                 -M_PI,     1, 1, 0,
+                 -3*M_PI/4, 0, 1, 0);
+    sector_patch (pattern,
+                 -3*M_PI/4, 0, 1, 0,
+                 -M_PI/2,   0, 1, 1);
+    sector_patch (pattern,
+                 -M_PI/2,   0, 1, 1,
+                 -M_PI/4,   0, 0, 1);
+    sector_patch (pattern,
+                 -M_PI/4,   0, 0, 1,
+                 0,         1, 0, 0);
+
+    cairo_set_source (cr, pattern);
+    cairo_paint (cr);
+    cairo_pattern_destroy (pattern);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (mesh_pattern_conical,
+           "Paint a conical pattern using a mesh pattern",
+           "conical,mesh,pattern", /* keywords */
+           NULL, /* requirements */
+           WIDTH, HEIGHT,
+           NULL, draw)
diff --git a/test/mesh-pattern-control-points.c b/test/mesh-pattern-control-points.c
new file mode 100644 (file)
index 0000000..4d59495
--- /dev/null
@@ -0,0 +1,114 @@
+/*
+ * Copyright © 2009 Adrian Johnson
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Adrian Johnson <ajohnson@redneon.com>
+ */
+
+#include "cairo-test.h"
+
+#define SIZE 90
+#define PAD 10
+#define WIDTH (PAD + 2 * (SIZE + PAD))
+#define HEIGHT (PAD + SIZE + PAD)
+
+
+/*
+ * This test is designed to paint a two mesh patches. One with default
+ * control points and one with a control point at a no default
+ * location.  The control points of both of them are drawn as squares
+ * to make them visible.
+ */
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_pattern_t *pattern;
+    unsigned int i, j;
+    unsigned int num_patches;
+    double x, y;
+
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_paint (cr);
+
+    cairo_translate (cr, PAD, PAD);
+
+    pattern = cairo_pattern_create_mesh ();
+    cairo_mesh_pattern_begin_patch (pattern);
+
+    cairo_mesh_pattern_move_to (pattern,    0,    0);
+    cairo_mesh_pattern_line_to (pattern, SIZE,    0);
+    cairo_mesh_pattern_line_to (pattern, SIZE, SIZE);
+    cairo_mesh_pattern_line_to (pattern,    0, SIZE);
+
+    cairo_mesh_pattern_set_corner_color_rgb (pattern, 0, 1, 0, 0);
+    cairo_mesh_pattern_set_corner_color_rgb (pattern, 1, 0, 1, 0);
+    cairo_mesh_pattern_set_corner_color_rgb (pattern, 2, 0, 0, 1);
+    cairo_mesh_pattern_set_corner_color_rgb (pattern, 3, 1, 1, 0);
+
+    cairo_mesh_pattern_set_control_point (pattern, 0, SIZE * .7, SIZE * .7);
+    cairo_mesh_pattern_set_control_point (pattern, 1, SIZE * .9, SIZE * .7);
+    cairo_mesh_pattern_set_control_point (pattern, 2, SIZE * .9, SIZE * .9);
+    cairo_mesh_pattern_set_control_point (pattern, 3, SIZE * .7, SIZE * .9);
+
+    cairo_mesh_pattern_end_patch (pattern);
+
+    cairo_mesh_pattern_begin_patch (pattern);
+
+    cairo_mesh_pattern_move_to (pattern,   SIZE + PAD,    0);
+    cairo_mesh_pattern_line_to (pattern, 2*SIZE + PAD,    0);
+    cairo_mesh_pattern_line_to (pattern, 2*SIZE + PAD, SIZE);
+    cairo_mesh_pattern_line_to (pattern,   SIZE + PAD, SIZE);
+
+    cairo_mesh_pattern_set_corner_color_rgb (pattern, 0, 1, 0, 0);
+    cairo_mesh_pattern_set_corner_color_rgb (pattern, 1, 0, 1, 0);
+    cairo_mesh_pattern_set_corner_color_rgb (pattern, 2, 0, 0, 1);
+    cairo_mesh_pattern_set_corner_color_rgb (pattern, 3, 1, 1, 0);
+
+    cairo_mesh_pattern_end_patch (pattern);
+
+    cairo_set_source (cr, pattern);
+    cairo_paint (cr);
+
+    /* mark the location of the control points */
+    cairo_set_source_rgb (cr, 0, 0, 0);
+    cairo_mesh_pattern_get_patch_count (pattern, &num_patches);
+    for (i = 0; i < num_patches; i++) {
+       for (j = 0; j < 4; j++) {
+           cairo_mesh_pattern_get_control_point (pattern, i, j, &x, &y);
+           cairo_rectangle (cr, x - 5, y - 5, 10, 10);
+           cairo_fill (cr);
+       }
+    }
+
+    cairo_pattern_destroy (pattern);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+
+CAIRO_TEST (mesh_pattern_control_points,
+           "Paint mesh pattern with non default control points",
+           "mesh,pattern", /* keywords */
+           NULL, /* requirements */
+           WIDTH, HEIGHT,
+           NULL, draw)
diff --git a/test/mesh-pattern-fold.c b/test/mesh-pattern-fold.c
new file mode 100644 (file)
index 0000000..4872bc0
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * Copyright © 2009 Adrian Johnson
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Adrian Johnson <ajohnson@redneon.com>
+ */
+
+#include "cairo-test.h"
+
+#define SIZE 100
+#define PAD 15
+#define WIDTH (5*SIZE)
+#define HEIGHT (5*SIZE)
+
+
+/* This test is designed to paint a mesh pattern which folds along
+ * both parameters. */
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_pattern_t *pattern;
+
+    cairo_test_paint_checkered (cr);
+
+    pattern = cairo_pattern_create_mesh ();
+
+    cairo_mesh_pattern_begin_patch (pattern);
+
+    cairo_mesh_pattern_move_to (pattern, 1, 1);
+
+    cairo_mesh_pattern_curve_to (pattern, 6, 0, -1, 0, 4, 1);
+    cairo_mesh_pattern_curve_to (pattern, 5, 6, 5, -1, 4, 4);
+    cairo_mesh_pattern_curve_to (pattern, -1, 3, 6, 3, 1, 4);
+    cairo_mesh_pattern_curve_to (pattern, 2, -1, 2, 6, 1, 1);
+
+    cairo_mesh_pattern_set_control_point (pattern, 0, 2, 3);
+    cairo_mesh_pattern_set_control_point (pattern, 1, 3, 3);
+    cairo_mesh_pattern_set_control_point (pattern, 2, 3, 2);
+    cairo_mesh_pattern_set_control_point (pattern, 3, 2, 2);
+
+    cairo_mesh_pattern_set_corner_color_rgb (pattern, 0, 1, 0, 0);
+    cairo_mesh_pattern_set_corner_color_rgb (pattern, 1, 0, 0, 1);
+    cairo_mesh_pattern_set_corner_color_rgb (pattern, 2, 0, 0, 0);
+    cairo_mesh_pattern_set_corner_color_rgb (pattern, 3, 0, 1, 0);
+
+    cairo_mesh_pattern_end_patch (pattern);
+
+    cairo_scale (cr, SIZE, SIZE);
+
+    cairo_set_source (cr, pattern);
+    cairo_paint (cr);
+    cairo_pattern_destroy (pattern);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (mesh_pattern_fold,
+           "Paint a mesh pattern with complex folds",
+           "mesh,pattern", /* keywords */
+           NULL, /* requirements */
+           WIDTH, HEIGHT,
+           NULL, draw)
diff --git a/test/mesh-pattern-overlap.c b/test/mesh-pattern-overlap.c
new file mode 100644 (file)
index 0000000..5ecbf9f
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * Copyright © 2009 Adrian Johnson
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Adrian Johnson <ajohnson@redneon.com>
+ */
+
+#include "cairo-test.h"
+
+#define SIZE 100
+#define PAD 15
+#define WIDTH (PAD + SIZE + PAD)
+#define HEIGHT WIDTH
+
+
+/* This test is designed to paint a mesh pattern with a simple
+ * fold. */
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_pattern_t *pattern;
+
+    cairo_test_paint_checkered (cr);
+
+    cairo_translate (cr, PAD, PAD);
+
+    pattern = cairo_pattern_create_mesh ();
+
+    cairo_mesh_pattern_begin_patch (pattern);
+
+    cairo_mesh_pattern_move_to (pattern, 0, 0);
+    cairo_mesh_pattern_curve_to (pattern,  30, -30, 60,  30, 100, 0);
+    cairo_mesh_pattern_curve_to (pattern, 130, 140, 60, -40, 100, 100);
+    cairo_mesh_pattern_curve_to (pattern,  60,  70, 30, 130,   0, 100);
+    cairo_mesh_pattern_curve_to (pattern, -30, -40, 30, 140,   0, 0);
+
+    cairo_mesh_pattern_set_corner_color_rgb (pattern, 0, 1, 0, 0);
+    cairo_mesh_pattern_set_corner_color_rgb (pattern, 1, 0, 1, 0);
+    cairo_mesh_pattern_set_corner_color_rgb (pattern, 2, 0, 0, 1);
+    cairo_mesh_pattern_set_corner_color_rgb (pattern, 3, 1, 1, 0);
+
+    cairo_mesh_pattern_end_patch (pattern);
+
+    cairo_set_source (cr, pattern);
+    cairo_paint (cr);
+    cairo_pattern_destroy (pattern);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (mesh_pattern_overlap,
+           "Paint a mesh pattern with a simple fold",
+           "mesh,pattern", /* keywords */
+           NULL, /* requirements */
+           WIDTH, HEIGHT,
+           NULL, draw)
diff --git a/test/mesh-pattern-transformed.c b/test/mesh-pattern-transformed.c
new file mode 100644 (file)
index 0000000..a5c20e7
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ * Copyright © 2009 Adrian Johnson
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Adrian Johnson <ajohnson@redneon.com>
+ */
+
+#include "cairo-test.h"
+
+#define PAT_WIDTH  170
+#define PAT_HEIGHT 170
+#define SIZE PAT_WIDTH
+#define PAD 10
+#define WIDTH 190
+#define HEIGHT 140
+
+
+/* This test is designed to paint a mesh pattern containing two
+ * overlapping patches transformed in different ways. */
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_pattern_t *pattern;
+
+    cairo_test_paint_checkered (cr);
+
+    cairo_translate (cr, PAD, PAD);
+
+    pattern = cairo_pattern_create_mesh ();
+
+    cairo_mesh_pattern_begin_patch (pattern);
+
+    cairo_mesh_pattern_move_to (pattern, 0, 0);
+    cairo_mesh_pattern_curve_to (pattern, 30, -30,  60,  30, 100, 0);
+    cairo_mesh_pattern_curve_to (pattern, 60,  30, 130,  60, 100, 100);
+    cairo_mesh_pattern_curve_to (pattern, 60,  70,  30, 130,   0, 100);
+    cairo_mesh_pattern_curve_to (pattern, 30,  70, -30,  30,   0, 0);
+
+    cairo_mesh_pattern_set_corner_color_rgb (pattern, 0, 1, 0, 0);
+    cairo_mesh_pattern_set_corner_color_rgb (pattern, 1, 0, 1, 0);
+    cairo_mesh_pattern_set_corner_color_rgb (pattern, 2, 0, 0, 1);
+    cairo_mesh_pattern_set_corner_color_rgb (pattern, 3, 1, 1, 0);
+
+    cairo_mesh_pattern_end_patch (pattern);
+
+    cairo_mesh_pattern_begin_patch (pattern);
+
+    cairo_mesh_pattern_move_to (pattern, 50, 50);
+    cairo_mesh_pattern_curve_to (pattern, 80, 20, 110, 80, 150, 50);
+
+    cairo_mesh_pattern_curve_to (pattern, 110, 80, 180, 110, 150, 150);
+
+    cairo_mesh_pattern_curve_to (pattern, 110, 120, 80, 180, 50, 150);
+
+    cairo_mesh_pattern_curve_to (pattern, 80, 120, 20, 80, 50, 50);
+
+    cairo_mesh_pattern_set_corner_color_rgba (pattern, 0, 1, 0, 0, 0.3);
+    cairo_mesh_pattern_set_corner_color_rgb  (pattern, 1, 0, 1, 0);
+    cairo_mesh_pattern_set_corner_color_rgba (pattern, 2, 0, 0, 1, 0.3);
+    cairo_mesh_pattern_set_corner_color_rgb  (pattern, 3, 1, 1, 0);
+
+    cairo_mesh_pattern_end_patch (pattern);
+
+    cairo_scale (cr, .5, .5);
+
+    cairo_set_source (cr, pattern);
+    cairo_paint (cr);
+
+    cairo_translate (cr, PAT_WIDTH, PAT_HEIGHT);
+    cairo_translate (cr, PAT_WIDTH/2, PAT_HEIGHT/2);
+    cairo_rotate (cr, M_PI/4);
+    cairo_translate (cr, -PAT_WIDTH, -PAT_HEIGHT);
+    cairo_set_source (cr, pattern);
+    cairo_paint (cr);
+
+    cairo_pattern_destroy (pattern);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (mesh_pattern_transformed,
+           "Paint mesh pattern with a transformation",
+           "mesh,pattern", /* keywords */
+           NULL, /* requirements */
+           WIDTH, HEIGHT,
+           NULL, draw)
+
diff --git a/test/mesh-pattern.c b/test/mesh-pattern.c
new file mode 100644 (file)
index 0000000..68f1830
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * Copyright © 2009 Adrian Johnson
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Adrian Johnson <ajohnson@redneon.com>
+ */
+
+#include "cairo-test.h"
+
+#define PAT_WIDTH  170
+#define PAT_HEIGHT 170
+#define SIZE PAT_WIDTH
+#define PAD 2
+#define WIDTH (PAD + SIZE + PAD)
+#define HEIGHT WIDTH
+
+
+/* This test is designed to paint a mesh pattern. The mesh contains
+ * two overlapping patches */
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_pattern_t *pattern;
+
+    cairo_test_paint_checkered (cr);
+
+    cairo_translate (cr, PAD, PAD);
+    cairo_translate (cr, 10, 10);
+
+    pattern = cairo_pattern_create_mesh ();
+
+    cairo_mesh_pattern_begin_patch (pattern);
+
+    cairo_mesh_pattern_move_to (pattern, 0, 0);
+    cairo_mesh_pattern_curve_to (pattern, 30, -30,  60,  30, 100, 0);
+    cairo_mesh_pattern_curve_to (pattern, 60,  30, 130,  60, 100, 100);
+    cairo_mesh_pattern_curve_to (pattern, 60,  70,  30, 130,   0, 100);
+    cairo_mesh_pattern_curve_to (pattern, 30,  70, -30,  30,   0, 0);
+
+    cairo_mesh_pattern_set_corner_color_rgb (pattern, 0, 1, 0, 0);
+    cairo_mesh_pattern_set_corner_color_rgb (pattern, 1, 0, 1, 0);
+    cairo_mesh_pattern_set_corner_color_rgb (pattern, 2, 0, 0, 1);
+    cairo_mesh_pattern_set_corner_color_rgb (pattern, 3, 1, 1, 0);
+
+    cairo_mesh_pattern_end_patch (pattern);
+
+    cairo_mesh_pattern_begin_patch (pattern);
+
+    cairo_mesh_pattern_move_to (pattern, 50, 50);
+    cairo_mesh_pattern_curve_to (pattern,  80,  20, 110,  80, 150, 50);
+    cairo_mesh_pattern_curve_to (pattern, 110,  80, 180, 110, 150, 150);
+    cairo_mesh_pattern_curve_to (pattern, 110, 120,  80, 180,  50, 150);
+    cairo_mesh_pattern_curve_to (pattern,  80, 120,  20,  80,  50, 50);
+
+    cairo_mesh_pattern_set_corner_color_rgba (pattern, 0, 1, 0, 0, 0.3);
+    cairo_mesh_pattern_set_corner_color_rgb  (pattern, 1, 0, 1, 0);
+    cairo_mesh_pattern_set_corner_color_rgba (pattern, 2, 0, 0, 1, 0.3);
+    cairo_mesh_pattern_set_corner_color_rgb  (pattern, 3, 1, 1, 0);
+
+    cairo_mesh_pattern_end_patch (pattern);
+
+    cairo_set_source (cr, pattern);
+    cairo_paint (cr);
+    cairo_pattern_destroy (pattern);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (mesh_pattern,
+           "Paint mesh pattern",
+           "mesh,pattern", /* keywords */
+           NULL, /* requirements */
+           WIDTH, HEIGHT,
+           NULL, draw)
diff --git a/test/mime-data.c b/test/mime-data.c
new file mode 100644 (file)
index 0000000..b1074cd
--- /dev/null
@@ -0,0 +1,146 @@
+/*
+ * Copyright © 2008 Chris Wilson
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Chris Wilson not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Chris Wilson makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * CHRIS WILSON DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL CHRIS WILSON BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+
+#include <stdio.h>
+#include <errno.h>
+
+/* Basic test to exercise the new mime-data embedding. */
+
+static cairo_status_t
+read_file (const cairo_test_context_t *ctx,
+          const char *filename,
+          unsigned char **data_out,
+          unsigned int *length_out)
+{
+    FILE *file;
+    unsigned char *buf;
+    unsigned int len;
+
+    file = fopen (filename, "rb");
+    if (file == NULL) {
+       char path[4096];
+
+       if (errno == ENOMEM)
+           return CAIRO_STATUS_NO_MEMORY;
+
+       /* try again with srcdir */
+       snprintf (path, sizeof (path),
+                 "%s/%s", ctx->srcdir, filename);
+       file = fopen (path, "rb");
+    }
+    if (file == NULL) {
+       switch (errno) {
+       case ENOMEM:
+           return CAIRO_STATUS_NO_MEMORY;
+       default:
+           return CAIRO_STATUS_FILE_NOT_FOUND;
+       }
+    }
+
+    fseek (file, 0, SEEK_END);
+    len = ftell (file);
+    fseek (file, 0, SEEK_SET);
+
+    buf = xmalloc (len);
+    *length_out = fread (buf, 1, len, file);
+    fclose (file);
+    if (*length_out != len) {
+       free (buf);
+       return CAIRO_STATUS_READ_ERROR;
+    }
+
+    *data_out = buf;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_test_status_t
+paint_file (cairo_t *cr,
+           const char *filename, const char *mime_type,
+           int x, int y)
+{
+    const cairo_test_context_t *ctx = cairo_test_get_context (cr);
+    cairo_surface_t *image;
+    unsigned char *mime_data;
+    unsigned int mime_length;
+    cairo_status_t status;
+
+    /* Deliberately use a non-matching MIME images, so that we can identify
+     * when the MIME representation is used in preference to the plain image
+     * surface.
+     */
+    status = read_file (ctx, filename, &mime_data, &mime_length);
+    if (status)
+       return cairo_test_status_from_status (ctx, status);
+
+    image = cairo_image_surface_create (CAIRO_FORMAT_RGB24, 200, 50);
+
+    status = cairo_surface_set_mime_data (image, mime_type,
+                                         mime_data, mime_length,
+                                         free, mime_data);
+    if (status) {
+       cairo_surface_destroy (image);
+       free (mime_data);
+       return cairo_test_status_from_status (ctx, status);
+    }
+
+    cairo_set_source_surface (cr, image, x, y);
+    cairo_surface_destroy (image);
+
+    cairo_paint (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    const char jpg_filename[] = "jpeg.jpg";
+    const char png_filename[] = "png.png";
+    const char jp2_filename[] = "jp2.jp2";
+    cairo_test_status_t status;
+
+    status = paint_file (cr, jpg_filename, CAIRO_MIME_TYPE_JPEG, 0, 0);
+    if (status)
+       return status;
+
+    status = paint_file (cr, png_filename, CAIRO_MIME_TYPE_PNG, 0, 50);
+    if (status)
+       return status;
+
+    status = paint_file (cr, jp2_filename, CAIRO_MIME_TYPE_JP2, 0, 100);
+    if (status)
+       return status;
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (mime_data,
+           "Check that the mime-data embedding works",
+           "jpeg, api", /* keywords */
+           NULL, /* requirements */
+           200, 150,
+           NULL, draw)
diff --git a/test/mime-surface-api.c b/test/mime-surface-api.c
new file mode 100644 (file)
index 0000000..ce12653
--- /dev/null
@@ -0,0 +1,151 @@
+/*
+ * Copyright © 2011 Uli Schlachter
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Uli Schlachter <psychon@znc.in>
+ */
+
+#include "cairo-test.h"
+
+static void
+mime_data_destroy_func (void *data)
+{
+    cairo_bool_t *called = data;
+    *called = TRUE;
+}
+
+static cairo_test_status_t
+check_mime_data (cairo_test_context_t *ctx, cairo_surface_t *surface,
+                const char *mimetype, const unsigned char *data,
+                unsigned long length)
+{
+    const unsigned char *data_ret;
+    unsigned long length_ret;
+
+    cairo_surface_get_mime_data (surface, mimetype, &data_ret, &length_ret);
+    if (data_ret != data || length_ret != length) {
+       cairo_test_log (ctx,
+                       "Surface has mime data %p with length %lu, "
+                       "but expected %p with length %lu\n",
+                       data_ret, length_ret, data, length);
+       return CAIRO_TEST_ERROR;
+    }
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+static cairo_test_status_t
+set_and_check_mime_data (cairo_test_context_t *ctx, cairo_surface_t *surface,
+                        const char *mimetype, const unsigned char *data,
+                        unsigned long length, cairo_bool_t *destroy_called)
+{
+    cairo_status_t status;
+
+    status = cairo_surface_set_mime_data (surface, mimetype,
+                                         data, length,
+                                         mime_data_destroy_func,
+                                         destroy_called);
+    if (status) {
+       cairo_test_log (ctx, "Could not set mime data to %s: %s\n",
+                       data, cairo_status_to_string(status));
+       return CAIRO_TEST_ERROR;
+    }
+
+    return check_mime_data (ctx, surface, mimetype, data, length);
+}
+
+static cairo_test_status_t
+preamble (cairo_test_context_t *ctx)
+{
+    const char *mimetype = "text/x-uri";
+    const char *data1 = "http://www.cairographics.org";
+    const char *data2 = "http://cairographics.org/examples/";
+    cairo_bool_t destroy1_called = FALSE;
+    cairo_bool_t destroy2_called = FALSE;
+    cairo_surface_t *surface;
+    cairo_test_status_t test_status = CAIRO_TEST_SUCCESS;
+
+    surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 0, 0);
+    if (cairo_surface_status (surface)) {
+       cairo_test_log (ctx, "Could not create image surface\n");
+       test_status = CAIRO_TEST_ERROR;
+       goto out;
+    }
+
+    test_status = check_mime_data (ctx, surface, mimetype, NULL, 0);
+    if (test_status)
+       goto out;
+
+    test_status = set_and_check_mime_data (ctx, surface, mimetype,
+                                          (const unsigned char *) data1,
+                                          strlen (data1),
+                                          &destroy1_called);
+    if (test_status)
+       goto out;
+
+    if (destroy1_called) {
+       cairo_test_log (ctx, "MIME data 1 destroyed too early\n");
+       test_status = CAIRO_TEST_ERROR;
+       goto out;
+    }
+
+    test_status = set_and_check_mime_data (ctx, surface, mimetype,
+                                          (const unsigned char *) data2,
+                                          strlen (data2),
+                                          &destroy2_called);
+    if (test_status)
+       goto out;
+
+    if (!destroy1_called) {
+       cairo_test_log (ctx, "MIME data 1 destroy callback not called\n");
+       test_status = CAIRO_TEST_ERROR;
+       goto out;
+    }
+    if (destroy2_called) {
+       cairo_test_log (ctx, "MIME data 2 destroyed too early\n");
+       test_status = CAIRO_TEST_ERROR;
+       goto out;
+    }
+
+    test_status = set_and_check_mime_data (ctx, surface, mimetype,
+                                          NULL, 0, NULL);
+    if (test_status)
+       goto out;
+
+    if (!destroy2_called) {
+       cairo_test_log (ctx, "MIME data destroy callback not called\n");
+       test_status = CAIRO_TEST_ERROR;
+       goto out;
+    }
+
+out:
+    cairo_surface_destroy (surface);
+
+    return test_status;
+}
+
+CAIRO_TEST (mime_surface_api,
+           "Check the mime data API",
+           "api", /* keywords */
+           NULL, /* requirements */
+           0, 0,
+           preamble, NULL)
diff --git a/test/mime-surface.c b/test/mime-surface.c
new file mode 100644 (file)
index 0000000..d59112f
--- /dev/null
@@ -0,0 +1,174 @@
+/*
+ * Copyright © 2011 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+
+#include <stdio.h>
+#include <errno.h>
+
+/* Basic test to exercise the new mime-surface callback. */
+
+#define WIDTH 200
+#define HEIGHT 80
+
+/* Lazy way of determining PNG dimensions... */
+static void
+png_dimensions (const char *filename,
+               cairo_content_t *content, int *width, int *height)
+{
+    cairo_surface_t *surface;
+
+    surface = cairo_image_surface_create_from_png (filename);
+    *content = cairo_surface_get_content (surface);
+    *width = cairo_image_surface_get_width (surface);
+    *height = cairo_image_surface_get_height (surface);
+    cairo_surface_destroy (surface);
+}
+
+static cairo_surface_t *
+png_acquire (cairo_surface_t *mime_surface, void *closure,
+            cairo_surface_t *target, const cairo_rectangle_int_t *roi,
+            cairo_rectangle_int_t *extents)
+{
+    cairo_surface_t *image;
+
+    image = cairo_image_surface_create_from_png (closure);
+    extents->x = extents->y = 0;
+    extents->width = cairo_image_surface_get_width (image);
+    extents->height = cairo_image_surface_get_height (image);
+    return image;
+}
+
+static cairo_surface_t *
+red_acquire (cairo_surface_t *mime_surface, void *closure,
+            cairo_surface_t *target, const cairo_rectangle_int_t *roi,
+            cairo_rectangle_int_t *extents)
+{
+    cairo_surface_t *image;
+    cairo_t *cr;
+
+    image = cairo_surface_create_similar_image (target,
+                                               CAIRO_FORMAT_RGB24,
+                                               roi->width, roi->height);
+    cr = cairo_create (image);
+    cairo_set_source_rgb (cr, 1, 0, 0);
+    cairo_paint (cr);
+    cairo_destroy (cr);
+
+    *extents = *roi;
+    return image;
+}
+
+static void
+release (cairo_surface_t *mime_surface, void *closure, cairo_surface_t *image)
+{
+    cairo_surface_destroy (image);
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    const char *png_filename = "png.png";
+    cairo_surface_t *png, *red;
+    cairo_content_t content;
+    int png_width, png_height;
+    int i, j;
+
+    png_dimensions (png_filename, &content, &png_width, &png_height);
+
+    png = cairo_mime_surface_create ((void*)png_filename, content, png_width, png_height);
+    cairo_mime_surface_set_acquire (png, png_acquire, release);
+
+    red = cairo_mime_surface_create (NULL, CAIRO_CONTENT_COLOR, WIDTH, HEIGHT);
+    cairo_mime_surface_set_acquire (red, red_acquire, release);
+
+    cairo_set_source_rgb (cr, 0, 0, 1);
+    cairo_paint (cr);
+
+    cairo_translate (cr, 0, (HEIGHT-png_height)/2);
+    for (i = 0; i < 4; i++) {
+       for (j = 0; j < 4; j++) {
+           cairo_surface_t *source;
+           if ((i ^ j) & 1)
+               source = red;
+           else
+               source = png;
+           cairo_set_source_surface (cr, source, 0, 0);
+           cairo_rectangle (cr, i * WIDTH/4, j * png_height/4, WIDTH/4, png_height/4);
+           cairo_fill (cr);
+       }
+    }
+
+    cairo_surface_destroy (red);
+    cairo_surface_destroy (png);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+static cairo_test_status_t
+check_status (const cairo_test_context_t *ctx,
+             cairo_status_t status,
+             cairo_status_t expected)
+{
+    if (status == expected)
+       return CAIRO_TEST_SUCCESS;
+
+    cairo_test_log (ctx,
+                   "Error: Expected status value %d (%s), received %d (%s)\n",
+                   expected,
+                   cairo_status_to_string (expected),
+                   status,
+                   cairo_status_to_string (status));
+    return CAIRO_TEST_FAILURE;
+}
+
+static cairo_test_status_t
+preamble (cairo_test_context_t *ctx)
+{
+    cairo_surface_t *mime;
+    cairo_status_t status;
+    cairo_t *cr;
+
+    /* drawing to a mime-surface is verboten */
+
+    mime = cairo_mime_surface_create (NULL, CAIRO_CONTENT_COLOR, 0, 0);
+    cr = cairo_create (mime);
+    cairo_surface_destroy (mime);
+    status = cairo_status (cr);
+    cairo_destroy (cr);
+    status = check_status (ctx, status, CAIRO_STATUS_WRITE_ERROR);
+    if (status)
+       return status;
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (mime_surface,
+           "Check that the mime-surface embedding works",
+           "api", /* keywords */
+           NULL, /* requirements */
+           WIDTH, HEIGHT,
+           preamble, draw)
diff --git a/test/miter-precision.c b/test/miter-precision.c
new file mode 100644 (file)
index 0000000..3bcdde0
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * Copyright © 2007 Keith Packard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Keith Packard
+ *
+ * Contributor(s):
+ *      Keith Packard <keithp@keithp.com>
+ */
+#include "cairo-test.h"
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    double  xscale, yscale;
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_paint (cr);
+
+    cairo_set_source_rgb (cr, 0, 0, 0);
+    cairo_set_miter_limit (cr, 100000);
+    for (xscale = 1; xscale <= 1000; xscale += 999)
+       for (yscale = 1; yscale <= 1000; yscale += 999)
+       {
+           double  max_scale = xscale > yscale ? xscale : yscale;
+           cairo_save (cr);
+           if (xscale > 1)
+               cairo_translate (cr, 50, 0);
+           if (yscale > 1)
+               cairo_translate (cr, 0, 50);
+           cairo_scale (cr, xscale, yscale);
+           cairo_set_line_width (cr, 10.0 / max_scale);
+           cairo_move_to (cr, 10.0 / xscale, 10.0 / yscale);
+           cairo_line_to (cr, 40.0 / xscale, 10.0 / yscale);
+           cairo_line_to (cr, 10.0 / xscale, 30.0 / yscale);
+           cairo_stroke (cr);
+           cairo_restore (cr);
+       }
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (miter_precision,
+           "test how cairo deals with small miters"
+           "\ncurrent code draws inappropriate bevels at times",
+           "stoke, stress", /* keywords */
+           NULL, /* requirements */
+           120, 100,
+           NULL, draw)
diff --git a/test/move-to-show-surface.c b/test/move-to-show-surface.c
new file mode 100644 (file)
index 0000000..a52b468
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * Copyright © 2004 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ */
+
+/* Bug history
+ *
+ * 2004-10-25  Carl Worth  <cworth@cworth.org>
+ *
+ *   It looks like cairo_show_surface has no effect if it follows a
+ *   call to cairo_move_to to any coordinate other than 0,0. A little
+ *   bit of poking around suggests this isn't a regression, (at least
+ *   not since the last pixman snapshot).
+ *
+ * 2005-04-02  Carl Worth <cworth@cworth.org>
+ *
+ *   Status: RESOLVED
+ *
+ *   Inside cairo_show_surface the current point was being used as
+ *   both source and destination offsets. After fixing that to use 0,0
+ *   as the source offset and the current point as the destination
+ *   offset, the bug seems to be gone.
+ *
+ */
+
+#include "cairo-test.h"
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_surface_t *surface;
+    uint32_t colors[4] = {
+       0xffffffff, 0xffff0000,
+       0xff00ff00, 0xff0000ff
+    };
+    int i;
+
+    for (i=0; i < 4; i++) {
+       surface = cairo_image_surface_create_for_data ((unsigned char *) &colors[i],
+                                                      CAIRO_FORMAT_RGB24,
+                                                      1, 1, 4);
+       cairo_set_source_surface (cr, surface,
+                                 i % 2, i / 2);
+       cairo_paint (cr);
+
+       cairo_surface_finish (surface); /* colors will go out of scope */
+       cairo_surface_destroy (surface);
+    }
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (move_to_show_surface,
+           "Tests calls to cairo_show_surface after cairo_move_to",
+           "transform", /* keywords */
+           NULL, /* requirements */
+           2, 2,
+           NULL, draw)
diff --git a/test/multi-page.c b/test/multi-page.c
new file mode 100644 (file)
index 0000000..0b73902
--- /dev/null
@@ -0,0 +1,194 @@
+/*
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ */
+
+#include <stdio.h>
+
+#include <cairo.h>
+
+#if CAIRO_HAS_PS_SURFACE
+#include <cairo-ps.h>
+#endif
+
+#if CAIRO_HAS_PDF_SURFACE
+#include <cairo-pdf.h>
+#endif
+
+#include "cairo-test.h"
+
+/* The PostScript and PDF backends are now integrated into the main
+ * test suite, so we are getting good verification of most things
+ * there.
+ *
+ * One thing that isn't supported there yet is multi-page output. So,
+ * for now we have this one-off test. There's no automatic
+ * verififcation here yet, but you can manually view the output to
+ * make sure it looks happy.
+ */
+
+#define WIDTH_IN_INCHES  3
+#define HEIGHT_IN_INCHES 3
+#define WIDTH_IN_POINTS  (WIDTH_IN_INCHES  * 72.0)
+#define HEIGHT_IN_POINTS (HEIGHT_IN_INCHES * 72.0)
+
+static void
+draw_smiley (cairo_t *cr, double width, double height, double smile_ratio)
+{
+#define STROKE_WIDTH .04
+    double size;
+
+    double theta = M_PI / 4 * smile_ratio;
+    double dx = sqrt (0.005) * cos (theta);
+    double dy = sqrt (0.005) * sin (theta);
+
+    cairo_save (cr);
+
+    if (width > height)
+       size = height;
+    else
+       size = width;
+
+    cairo_translate (cr, (width - size) / 2.0, (height - size) / 2.0);
+    cairo_scale (cr, size, size);
+
+    /* Fill face */
+    cairo_arc (cr, 0.5, 0.5, 0.5 - STROKE_WIDTH, 0, 2 * M_PI);
+    cairo_set_source_rgb (cr, 1, 1, 0);
+    cairo_fill_preserve (cr);
+
+    cairo_set_source_rgb (cr, 0, 0, 0);
+
+    /* Stroke face */
+    cairo_set_line_width (cr, STROKE_WIDTH / 2.0);
+    cairo_stroke (cr);
+
+    /* Eyes */
+    cairo_set_line_width (cr, STROKE_WIDTH);
+    cairo_arc (cr, 0.3, 0.4, STROKE_WIDTH, 0, 2 * M_PI);
+    cairo_fill (cr);
+    cairo_arc (cr, 0.7, 0.4, STROKE_WIDTH, 0, 2 * M_PI);
+    cairo_fill (cr);
+
+    /* Mouth */
+    cairo_move_to (cr,
+                  0.35 - dx, 0.75 - dy);
+    cairo_curve_to (cr,
+                   0.35 + dx, 0.75 + dy,
+                   0.65 - dx, 0.75 + dy,
+                   0.65 + dx, 0.75 - dy);
+    cairo_stroke (cr);
+
+    cairo_restore (cr);
+}
+
+static void
+draw_some_pages (cairo_surface_t *surface)
+{
+    cairo_t *cr;
+    int i;
+
+    cr = cairo_create (surface);
+
+#define NUM_FRAMES 5
+    for (i=0; i < NUM_FRAMES; i++) {
+       draw_smiley (cr, WIDTH_IN_POINTS, HEIGHT_IN_POINTS,
+                    (double) i / (NUM_FRAMES - 1));
+
+       /* Duplicate the last frame onto another page. (This is just a
+        * way to sneak cairo_copy_page into the test).
+        */
+       if (i == (NUM_FRAMES - 1))
+           cairo_copy_page (cr);
+
+       cairo_show_page (cr);
+    }
+
+    cairo_destroy (cr);
+}
+
+static cairo_test_status_t
+preamble (cairo_test_context_t *ctx)
+{
+    cairo_surface_t *surface;
+    cairo_status_t status;
+    const char *filename;
+    cairo_test_status_t result = CAIRO_TEST_UNTESTED;
+
+#if CAIRO_HAS_PS_SURFACE
+    if (cairo_test_is_target_enabled (ctx, "ps2") ||
+        cairo_test_is_target_enabled (ctx, "ps3"))
+    {
+       if (result == CAIRO_TEST_UNTESTED)
+           result = CAIRO_TEST_SUCCESS;
+
+       filename = "multi-page.out.ps";
+       surface = cairo_ps_surface_create (filename,
+                                          WIDTH_IN_POINTS, HEIGHT_IN_POINTS);
+       status = cairo_surface_status (surface);
+       if (status) {
+           cairo_test_log (ctx, "Failed to create ps surface for file %s: %s\n",
+                           filename, cairo_status_to_string (status));
+           result = CAIRO_TEST_FAILURE;
+       }
+
+       draw_some_pages (surface);
+
+       cairo_surface_destroy (surface);
+
+       printf ("multi-page: Please check %s to ensure it looks happy.\n", filename);
+    }
+#endif
+
+#if CAIRO_HAS_PDF_SURFACE
+    if (cairo_test_is_target_enabled (ctx, "pdf")) {
+       if (result == CAIRO_TEST_UNTESTED)
+           result = CAIRO_TEST_SUCCESS;
+
+       filename = "multi-page.out.pdf";
+       surface = cairo_pdf_surface_create (filename,
+                                           WIDTH_IN_POINTS, HEIGHT_IN_POINTS);
+       status = cairo_surface_status (surface);
+       if (status) {
+           cairo_test_log (ctx, "Failed to create pdf surface for file %s: %s\n",
+                           filename, cairo_status_to_string (status));
+           result = CAIRO_TEST_FAILURE;
+       }
+
+       draw_some_pages (surface);
+
+       cairo_surface_destroy (surface);
+
+       printf ("multi-page: Please check %s to ensure it looks happy.\n", filename);
+    }
+#endif
+
+    return result;
+}
+
+CAIRO_TEST (multi_page,
+           "Check the paginated surfaces handle multiple pages.",
+           "paginated", /* keywords */
+           "target=vector", /* requirements */
+           0, 0,
+           preamble, NULL)
diff --git a/test/new-sub-path.c b/test/new-sub-path.c
new file mode 100644 (file)
index 0000000..f8f6bbe
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ */
+
+#include "cairo-test.h"
+
+#define SIZE 10
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_set_source_rgb (cr, 0.0, 0.0, 1.0); /* blue */
+
+    /* Test cairo_new_sub_path followed by several different
+     * path-modification functions in turn...
+     */
+
+    /* ... cairo_move_to */
+    cairo_new_sub_path (cr);
+    cairo_move_to (cr, SIZE, SIZE);
+    cairo_line_to (cr, SIZE, 2 * SIZE);
+
+    /* ... cairo_line_to */
+    cairo_new_sub_path (cr);
+    cairo_line_to (cr, 2 * SIZE, 1.5 * SIZE);
+    cairo_line_to (cr, 3 * SIZE, 1.5 * SIZE);
+
+    /* ... cairo_curve_to */
+    cairo_new_sub_path (cr);
+    cairo_curve_to (cr,
+                   4.0 * SIZE, 1.5 * SIZE,
+                   4.5 * SIZE, 1.0 * SIZE,
+                   5.0 * SIZE, 1.5 * SIZE);
+
+    /* ... cairo_arc */
+    cairo_new_sub_path (cr);
+    cairo_arc (cr,
+              6.5 * SIZE, 1.5 * SIZE,
+              0.5 * SIZE,
+              0.0, 2.0 * M_PI);
+
+    cairo_stroke (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (new_sub_path,
+           "Test the cairo_new_sub_path call",
+           "path", /* keywords */
+           NULL, /* requirements */
+           8 * SIZE,
+           3 * SIZE,
+           NULL, draw)
diff --git a/test/nil-surface.c b/test/nil-surface.c
new file mode 100644 (file)
index 0000000..30a1f97
--- /dev/null
@@ -0,0 +1,170 @@
+/*
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ */
+
+#include "cairo-test.h"
+#include <stddef.h>
+
+/* Test to verify fixes for the following similar bugs:
+ *
+ *     https://bugs.freedesktop.org/show_bug.cgi?id=4088
+ *     https://bugs.freedesktop.org/show_bug.cgi?id=3915
+ *     https://bugs.freedesktop.org/show_bug.cgi?id=9906
+ */
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    const cairo_test_context_t *ctx = cairo_test_get_context (cr);
+    cairo_surface_t *surface;
+    cairo_pattern_t *pattern;
+    cairo_t *cr2;
+
+    /*
+     * 1. Test file-not-found from surface->pattern->cairo_t
+     */
+
+    /* Make a custom context to not interfere with the one passed in. */
+    cr2 = cairo_create (cairo_get_target (cr));
+
+    /* First, let's make a nil surface. */
+    surface = cairo_image_surface_create_from_png ("___THIS_FILE_DOES_NOT_EXIST___");
+
+    /* Let the error propagate into a nil pattern. */
+    pattern = cairo_pattern_create_for_surface (surface);
+
+    /* Then let it propagate into the cairo_t. */
+    cairo_set_source (cr2, pattern);
+    cairo_paint (cr2);
+
+    cairo_pattern_destroy (pattern);
+    cairo_surface_destroy (surface);
+
+    /* Check that the error made it all that way. */
+    if (cairo_status (cr2) != CAIRO_STATUS_FILE_NOT_FOUND) {
+       cairo_test_log (ctx, "Error: Received status of \"%s\" rather than expected \"%s\"\n",
+                       cairo_status_to_string (cairo_status (cr2)),
+                       cairo_status_to_string (CAIRO_STATUS_FILE_NOT_FOUND));
+       cairo_destroy (cr2);
+       return CAIRO_TEST_FAILURE;
+    }
+
+    cairo_destroy (cr2);
+
+    /*
+     * 2. Test NULL pointer pattern->cairo_t
+     */
+    cr2 = cairo_create (cairo_get_target (cr));
+
+    /* First, trigger the NULL pointer status. */
+    pattern = cairo_pattern_create_for_surface (NULL);
+
+    /* Then let it propagate into the cairo_t. */
+    cairo_set_source (cr2, pattern);
+    cairo_paint (cr2);
+
+    cairo_pattern_destroy (pattern);
+
+    /* Check that the error made it all that way. */
+    if (cairo_status (cr2) != CAIRO_STATUS_NULL_POINTER) {
+       cairo_test_log (ctx, "Error: Received status of \"%s\" rather than expected \"%s\"\n",
+                       cairo_status_to_string (cairo_status (cr2)),
+                       cairo_status_to_string (CAIRO_STATUS_NULL_POINTER));
+       cairo_destroy (cr2);
+       return CAIRO_TEST_FAILURE;
+    }
+
+    cairo_destroy (cr2);
+
+    /*
+     * 3. Test that cairo_surface_finish can accept NULL or a nil
+     *    surface without crashing.
+     */
+
+    cairo_surface_finish (NULL);
+
+    surface = cairo_image_surface_create_from_png ("___THIS_FILE_DOES_NOT_EXIST___");
+    cairo_surface_finish (surface);
+    cairo_surface_destroy (surface);
+
+    /*
+     * 4. OK, we're straying from the original name, but it's still a
+     * similar kind of testing of error paths. Here we're making sure
+     * we can still call a cairo_get_* function after triggering an
+     * INVALID_RESTORE error.
+     */
+    cr2 = cairo_create (cairo_get_target (cr));
+
+    /* Trigger invalid restore. */
+    cairo_restore (cr2);
+    if (cairo_status (cr2) != CAIRO_STATUS_INVALID_RESTORE) {
+       cairo_test_log (ctx, "Error: Received status of \"%s\" rather than expected \"%s\"\n",
+                       cairo_status_to_string (cairo_status (cr2)),
+                       cairo_status_to_string (CAIRO_STATUS_INVALID_RESTORE));
+       cairo_destroy (cr2);
+       return CAIRO_TEST_FAILURE;
+    }
+
+    /* Test that we can still call cairo_get_fill_rule without crashing. */
+    cairo_get_fill_rule (cr2);
+
+    cairo_destroy (cr2);
+
+    /*
+     * 5. Create a cairo_t for the NULL surface.
+     */
+    cr2 = cairo_create (NULL);
+
+    if (cairo_status (cr2) != CAIRO_STATUS_NULL_POINTER) {
+       cairo_test_log (ctx, "Error: Received status of \"%s\" rather than expected \"%s\"\n",
+                       cairo_status_to_string (cairo_status (cr2)),
+                       cairo_status_to_string (CAIRO_STATUS_NULL_POINTER));
+       cairo_destroy (cr2);
+       return CAIRO_TEST_FAILURE;
+    }
+
+    /* Test that get_target returns something valid */
+    if (cairo_get_target (cr2) == NULL) {
+       cairo_test_log (ctx, "Error: cairo_get_target() returned NULL\n");
+       cairo_destroy (cr2);
+       return CAIRO_TEST_FAILURE;
+    }
+
+    /* Test that push_group doesn't crash */
+    cairo_push_group (cr2);
+    cairo_stroke (cr2);
+    pattern = cairo_pop_group (cr2);
+    cairo_pattern_destroy (pattern);
+
+    cairo_destroy (cr2);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (nil_surface,
+           "Test that nil surfaces do not make cairo crash.",
+           "api", /* keywords */
+           NULL, /* requirements */
+           1, 1,
+           NULL, draw)
diff --git a/test/operator-alpha-alpha.c b/test/operator-alpha-alpha.c
new file mode 100644 (file)
index 0000000..2cef858
--- /dev/null
@@ -0,0 +1,166 @@
+/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright 2002 University of Southern California
+ * Copyright 2005 Red Hat, Inc.
+ * Copyright 2007 Emmanuel Pacaud
+ * Copyright 2008 Benjamin Otte
+ * Copyright 2008 Chris Wilson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *      Owen Taylor <otaylor@redhat.com>
+ *      Kristian Høgsberg <krh@redhat.com>
+ *      Emmanuel Pacaud <emmanuel.pacaud@lapp.in2p3.fr>
+ *      Chris Wilson <chris@chris-wilson.co.uk>
+ *      Andrea Canciani <ranma42@gmail.com>
+ */
+
+#include "cairo-test.h"
+
+#define STEPS 16
+#define START_OPERATOR CAIRO_OPERATOR_CLEAR
+#define STOP_OPERATOR  CAIRO_OPERATOR_HSL_LUMINOSITY
+
+#define SIZE 3
+#define COUNT 6
+#define FULL_WIDTH  ((STEPS + 1) * COUNT - 1)
+#define FULL_HEIGHT ((COUNT + STOP_OPERATOR - START_OPERATOR) / COUNT) * (STEPS + 1)
+
+static void
+create_patterns (cairo_t *bg, cairo_t *fg)
+{
+    int x;
+
+    for (x = 0; x < STEPS; x++) {
+       double i = (double) x / (STEPS - 1);
+       cairo_set_source_rgba (bg, 0, 0, 0, i);
+       cairo_rectangle (bg, x, 0, 1, STEPS);
+       cairo_fill (bg);
+
+       cairo_set_source_rgba (fg, 0, 0, 0, i);
+       cairo_rectangle (fg, 0, x, STEPS, 1);
+       cairo_fill (fg);
+    }
+}
+
+/* expects a STEP*STEP pixel rectangle */
+static void
+do_composite (cairo_t *cr, cairo_operator_t op, cairo_surface_t *bg, cairo_surface_t *fg)
+{
+    cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+    cairo_set_source_surface (cr, bg, 0, 0);
+    cairo_paint (cr);
+
+    cairo_set_operator (cr, op);
+    cairo_set_source_surface (cr, fg, 0, 0);
+    cairo_paint (cr);
+}
+
+static void
+subdraw (cairo_t *cr, int width, int height)
+{
+    size_t i = 0;
+    cairo_operator_t op;
+    cairo_t *bgcr, *fgcr;
+    cairo_surface_t *bg, *fg;
+
+    bg = cairo_surface_create_similar (cairo_get_target (cr),
+           CAIRO_CONTENT_ALPHA, SIZE * STEPS, SIZE * STEPS);
+    fg = cairo_surface_create_similar (cairo_get_target (cr),
+           CAIRO_CONTENT_ALPHA, SIZE * STEPS, SIZE * STEPS);
+    bgcr = cairo_create (bg);
+    fgcr = cairo_create (fg);
+    cairo_scale (bgcr, SIZE, SIZE);
+    cairo_scale (fgcr, SIZE, SIZE);
+    create_patterns (bgcr, fgcr);
+    cairo_destroy (bgcr);
+    cairo_destroy (fgcr);
+
+    for (op = START_OPERATOR; op <= STOP_OPERATOR; op++, i++) {
+       cairo_save (cr);
+       cairo_translate (cr,
+               SIZE * (STEPS + 1) * (i % COUNT),
+               SIZE * (STEPS + 1) * (i / COUNT));
+       cairo_rectangle (cr, 0, 0, SIZE * (STEPS + 1), SIZE * (STEPS+1));
+       cairo_clip (cr);
+       do_composite (cr, op, bg, fg);
+       cairo_restore (cr);
+    }
+
+    cairo_surface_destroy (fg);
+    cairo_surface_destroy (bg);
+}
+
+
+static cairo_surface_t *
+create_source (cairo_surface_t *target, int width, int height)
+{
+    cairo_surface_t *similar;
+    cairo_t *cr;
+
+    similar = cairo_surface_create_similar (target,
+                                           CAIRO_CONTENT_ALPHA,
+                                           width, height);
+    cr = cairo_create (similar);
+    cairo_surface_destroy (similar);
+
+    subdraw (cr, width, height);
+
+    similar = cairo_surface_reference (cairo_get_target (cr));
+    cairo_destroy (cr);
+
+    return similar;
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_surface_t *source;
+
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_paint (cr);
+
+    source = create_source (cairo_get_target (cr), width, height);
+    cairo_set_source_surface (cr, source, 0, 0);
+    cairo_surface_destroy (source);
+
+    cairo_paint (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (operator_alpha_alpha,
+           "Tests result of compositing pure-alpha surfaces"
+           "\nCompositing of pure-alpha sources is inconsistent across backends.",
+           "alpha, similar, operator", /* keywords */
+           NULL, /* requirements */
+           FULL_WIDTH * SIZE, FULL_HEIGHT * SIZE,
+           NULL, draw)
diff --git a/test/operator-alpha.c b/test/operator-alpha.c
new file mode 100644 (file)
index 0000000..1ce4ecd
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * Copyright © 2008 Chris Wilson
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Chris Wilson not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Chris Wilson makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * CHRIS WILSON DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL CHRIS WILSON BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+
+#define N_OPERATORS (CAIRO_OPERATOR_SATURATE + 1)
+#define SIZE 10
+#define PAD 3
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    unsigned int n;
+
+    cairo_translate (cr, PAD, PAD);
+
+    for (n = 0; n < N_OPERATORS; n++) {
+       cairo_reset_clip (cr);
+       cairo_rectangle (cr, 0, 0, SIZE, SIZE);
+       cairo_clip (cr);
+
+       cairo_set_source_rgb (cr, 1, 0, 0);
+       cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
+       cairo_rectangle (cr, 0, 0, SIZE-PAD, SIZE-PAD);
+       cairo_fill (cr);
+
+       cairo_set_source_rgba (cr, 0, 0, 1, .33);
+       cairo_set_operator (cr, n);
+       cairo_rectangle (cr, PAD, PAD, SIZE-PAD, SIZE-PAD);
+       cairo_fill (cr);
+
+       cairo_translate (cr, SIZE+PAD, 0);
+    }
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (operator_alpha,
+           "Tests using set_operator() with an non-opaque source",
+           "operator, alpha", /* keywords */
+           NULL, /* requirements */
+           (SIZE+PAD) * N_OPERATORS + PAD, SIZE + 2*PAD,
+           NULL, draw)
diff --git a/test/operator-clear.c b/test/operator-clear.c
new file mode 100644 (file)
index 0000000..aac39f3
--- /dev/null
@@ -0,0 +1,196 @@
+/*
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Authors: Kristian Høgsberg <krh@redhat.com>
+ *          Owen Taylor <otaylor@redhat.com>
+ */
+
+#include <math.h>
+#include "cairo-test.h"
+#include <stdio.h>
+
+#define WIDTH 16
+#define HEIGHT 16
+#define PAD 2
+
+static void
+set_solid_pattern (cairo_t *cr, int x, int y)
+{
+    cairo_set_source_rgb (cr, 1.0, 0, 0.0);
+}
+
+static void
+set_gradient_pattern (cairo_t *cr, int x, int y)
+{
+    cairo_pattern_t *pattern;
+
+    pattern = cairo_pattern_create_linear (x, y, x + WIDTH, y + HEIGHT);
+    cairo_pattern_add_color_stop_rgba (pattern, 0.2, 1, 0, 0, 1);
+    cairo_pattern_add_color_stop_rgba (pattern, 0.8, 1, 0, 0, 0.0);
+    cairo_set_source (cr, pattern);
+    cairo_pattern_destroy (pattern);
+}
+
+static void
+draw_mask (cairo_t *cr, int x, int y)
+{
+    cairo_surface_t *mask_surface;
+    cairo_t *cr2;
+
+    double width = (int)(0.9 * WIDTH);
+    double height = (int)(0.9 * HEIGHT);
+    x += 0.05 * WIDTH;
+    y += 0.05 * HEIGHT;
+
+    mask_surface = cairo_surface_create_similar (cairo_get_group_target (cr),
+                                                CAIRO_CONTENT_ALPHA,
+                                                width, height);
+    cr2 = cairo_create (mask_surface);
+    cairo_surface_destroy (mask_surface);
+
+    cairo_set_source_rgb (cr2, 1, 1, 1); /* white */
+
+    cairo_arc (cr2, 0.5 * width, 0.5 * height, 0.45 * height, 0, 2 * M_PI);
+    cairo_fill (cr2);
+
+    cairo_mask_surface (cr, cairo_get_target (cr2), x, y);
+    cairo_destroy (cr2);
+}
+
+static void
+draw_glyphs (cairo_t *cr, int x, int y)
+{
+    cairo_text_extents_t extents;
+
+    cairo_set_font_size (cr, 0.8 * HEIGHT);
+
+    cairo_text_extents (cr, "FG", &extents);
+    cairo_move_to (cr,
+                  x + floor ((WIDTH - extents.width) / 2 + 0.5) - extents.x_bearing,
+                  y + floor ((HEIGHT - extents.height) / 2 + 0.5) - extents.y_bearing);
+    cairo_show_text (cr, "FG");
+}
+
+static void
+draw_polygon (cairo_t *cr, int x, int y)
+{
+    double width = (int)(0.9 * WIDTH);
+    double height = (int)(0.9 * HEIGHT);
+    x += 0.05 * WIDTH;
+    y += 0.05 * HEIGHT;
+
+    cairo_new_path (cr);
+    cairo_move_to (cr, x, y);
+    cairo_line_to (cr, x, y + height);
+    cairo_line_to (cr, x + width / 2, y + 3 * height / 4);
+    cairo_line_to (cr, x + width, y + height);
+    cairo_line_to (cr, x + width, y);
+    cairo_line_to (cr, x + width / 2, y + height / 4);
+    cairo_close_path (cr);
+    cairo_fill (cr);
+}
+
+static void
+draw_rects (cairo_t *cr, int x, int y)
+{
+    double block_width = (int)(0.33 * WIDTH + 0.5);
+    double block_height = (int)(0.33 * HEIGHT + 0.5);
+    int i, j;
+
+    for (i = 0; i < 3; i++)
+       for (j = 0; j < 3; j++)
+           if ((i + j) % 2 == 0)
+               cairo_rectangle (cr,
+                                x + block_width * i, y + block_height * j,
+                                block_width,         block_height);
+
+    cairo_fill (cr);
+}
+
+static void (* const pattern_funcs[])(cairo_t *cr, int x, int y) = {
+    set_solid_pattern,
+    set_gradient_pattern,
+};
+
+static void (* const draw_funcs[])(cairo_t *cr, int x, int y) = {
+    draw_mask,
+    draw_glyphs,
+    draw_polygon,
+    draw_rects
+};
+
+#define IMAGE_WIDTH (ARRAY_LENGTH (pattern_funcs) * (WIDTH + PAD) + PAD)
+#define IMAGE_HEIGHT (ARRAY_LENGTH (draw_funcs) * (HEIGHT + PAD) + PAD)
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    const cairo_test_context_t *ctx = cairo_test_get_context (cr);
+    size_t i, j, x, y;
+    cairo_pattern_t *pattern;
+
+    cairo_select_font_face (cr, CAIRO_TEST_FONT_FAMILY " Sans",
+                           CAIRO_FONT_SLANT_NORMAL,
+                           CAIRO_FONT_WEIGHT_NORMAL);
+
+    for (j = 0; j < ARRAY_LENGTH (draw_funcs); j++) {
+       for (i = 0; i < ARRAY_LENGTH (pattern_funcs); i++) {
+           x = i * (WIDTH + PAD) + PAD;
+           y = j * (HEIGHT + PAD) + PAD;
+
+           cairo_save (cr);
+
+           pattern = cairo_pattern_create_linear (x + WIDTH, y,
+                                                  x,         y + HEIGHT);
+           cairo_pattern_add_color_stop_rgba (pattern, 0.2,
+                                              0.0, 0.0, 1.0, 1.0); /* Solid blue */
+           cairo_pattern_add_color_stop_rgba (pattern, 0.8,
+                                              0.0, 0.0, 1.0, 0.0); /* Transparent blue */
+           cairo_set_source (cr, pattern);
+           cairo_pattern_destroy (pattern);
+
+           cairo_rectangle (cr, x, y, WIDTH, HEIGHT);
+           cairo_fill_preserve (cr);
+           cairo_clip (cr);
+
+           cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
+           pattern_funcs[i] (cr, x, y);
+           draw_funcs[j] (cr, x, y);
+           if (cairo_status (cr))
+               cairo_test_log (ctx, "%d %d HERE!\n", (int)i, (int)j);
+
+           cairo_restore (cr);
+       }
+    }
+
+    if (cairo_status (cr) != CAIRO_STATUS_SUCCESS)
+       cairo_test_log (ctx, "%d %d .HERE!\n", (int)i, (int)j);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (operator_clear,
+           "Test of CAIRO_OPERATOR_CLEAR",
+           "operator", /* keywords */
+           NULL, /* requirements */
+           IMAGE_WIDTH, IMAGE_HEIGHT,
+           NULL, draw)
diff --git a/test/operator-source.c b/test/operator-source.c
new file mode 100644 (file)
index 0000000..3dee570
--- /dev/null
@@ -0,0 +1,250 @@
+/*
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Authors: Kristian Høgsberg <krh@redhat.com>
+ *          Owen Taylor <otaylor@redhat.com>
+ *          Uli Schlachter <psychon@znc.in>
+ */
+
+#include <math.h>
+#include "cairo-test.h"
+#include <stdio.h>
+
+#define WIDTH 16
+#define HEIGHT 16
+#define PAD 2
+
+static void
+set_solid_pattern (cairo_t *cr, int x, int y)
+{
+    cairo_set_source_rgb (cr, 1.0, 0, 0.0);
+}
+
+static void
+set_translucent_pattern (cairo_t *cr, int x, int y)
+{
+    cairo_set_source_rgba (cr, 1, 0, 0, 0.5);
+}
+
+static void
+set_gradient_pattern (cairo_t *cr, int x, int y)
+{
+    cairo_pattern_t *pattern;
+
+    pattern = cairo_pattern_create_linear (x, y, x + WIDTH, y + HEIGHT);
+    cairo_pattern_add_color_stop_rgba (pattern, 0.2, 1, 0, 0, 1);
+    cairo_pattern_add_color_stop_rgba (pattern, 0.8, 1, 0, 0, 0.0);
+    cairo_set_source (cr, pattern);
+    cairo_pattern_destroy (pattern);
+}
+
+static void
+set_surface_pattern (cairo_t *cr, int x, int y)
+{
+    cairo_surface_t *source_surface;
+    cairo_t *cr2;
+
+    double width = (int)(0.6 * WIDTH);
+    double height = (int)(0.6 * HEIGHT);
+    x += 0.2 * WIDTH;
+    y += 0.2 * HEIGHT;
+
+    source_surface = cairo_surface_create_similar (cairo_get_group_target (cr),
+                                                  CAIRO_CONTENT_COLOR_ALPHA,
+                                                  width, height);
+    cr2 = cairo_create (source_surface);
+    cairo_surface_destroy (source_surface);
+
+    cairo_set_source_rgb (cr2, 1, 0, 0); /* red */
+    cairo_paint (cr2);
+
+    cairo_set_source_rgb (cr2, 1, 1, 1); /* white */
+
+    cairo_arc (cr2, 0.5 * width, 0.5 * height, 0.5 * height, 0, 2 * M_PI);
+    cairo_fill (cr2);
+
+    cairo_set_source_surface (cr, cairo_get_target (cr2), x, y);
+    cairo_destroy (cr2);
+}
+
+static void
+draw_mask (cairo_t *cr, int x, int y)
+{
+    cairo_surface_t *mask_surface;
+    cairo_t *cr2;
+
+    double width = (int)(0.9 * WIDTH);
+    double height = (int)(0.9 * HEIGHT);
+    x += 0.05 * WIDTH;
+    y += 0.05 * HEIGHT;
+
+    mask_surface = cairo_surface_create_similar (cairo_get_group_target (cr),
+                                                CAIRO_CONTENT_ALPHA,
+                                                width, height);
+    cr2 = cairo_create (mask_surface);
+    cairo_surface_destroy (mask_surface);
+
+    cairo_set_source_rgb (cr2, 1, 1, 1); /* white */
+
+    cairo_arc (cr2, 0.5 * width, 0.5 * height, 0.45 * height, 0, 2 * M_PI);
+    cairo_fill (cr2);
+
+    cairo_mask_surface (cr, cairo_get_target (cr2), x, y);
+    cairo_destroy (cr2);
+}
+
+static void
+draw_glyphs (cairo_t *cr, int x, int y)
+{
+    cairo_text_extents_t extents;
+
+    cairo_set_font_size (cr, 0.8 * HEIGHT);
+
+    cairo_text_extents (cr, "FG", &extents);
+    cairo_move_to (cr,
+                  x + floor ((WIDTH - extents.width) / 2 + 0.5) - extents.x_bearing,
+                  y + floor ((HEIGHT - extents.height) / 2 + 0.5) - extents.y_bearing);
+    cairo_show_text (cr, "FG");
+}
+
+static void
+draw_polygon (cairo_t *cr, int x, int y)
+{
+    double width = (int)(0.9 * WIDTH);
+    double height = (int)(0.9 * HEIGHT);
+    x += 0.05 * WIDTH;
+    y += 0.05 * HEIGHT;
+
+    cairo_new_path (cr);
+    cairo_move_to (cr, x, y);
+    cairo_line_to (cr, x, y + height);
+    cairo_line_to (cr, x + width / 2, y + 3 * height / 4);
+    cairo_line_to (cr, x + width, y + height);
+    cairo_line_to (cr, x + width, y);
+    cairo_line_to (cr, x + width / 2, y + height / 4);
+    cairo_close_path (cr);
+    cairo_fill (cr);
+}
+
+static void
+draw_rects (cairo_t *cr, int x, int y, double offset)
+{
+    double block_width = (int)(0.33 * WIDTH + 0.5) - offset/3;
+    double block_height = (int)(0.33 * HEIGHT + 0.5) - offset/3;
+    int i, j;
+
+    x += offset/2;
+    y += offset/2;
+
+    for (i = 0; i < 3; i++)
+       for (j = 0; j < 3; j++)
+           if ((i + j) % 2 == 0)
+               cairo_rectangle (cr,
+                                x + block_width * i, y + block_height * j,
+                                block_width,         block_height);
+
+    cairo_fill (cr);
+}
+
+static void
+draw_aligned_rects (cairo_t *cr, int x, int y)
+{
+    draw_rects (cr, x, y, 0);
+}
+
+static void
+draw_unaligned_rects (cairo_t *cr, int x, int y)
+{
+    draw_rects (cr, x, y, 2.1);
+}
+
+static void (* const pattern_funcs[])(cairo_t *cr, int x, int y) = {
+    set_solid_pattern,
+    set_translucent_pattern,
+    set_gradient_pattern,
+    set_surface_pattern,
+};
+
+static void (* const draw_funcs[])(cairo_t *cr, int x, int y) = {
+    draw_mask,
+    draw_glyphs,
+    draw_polygon,
+    draw_aligned_rects,
+    draw_unaligned_rects
+};
+
+#define IMAGE_WIDTH (ARRAY_LENGTH (pattern_funcs) * (WIDTH + PAD) + PAD)
+#define IMAGE_HEIGHT (ARRAY_LENGTH (draw_funcs) * (HEIGHT + PAD) + PAD)
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    const cairo_test_context_t *ctx = cairo_test_get_context (cr);
+    size_t i, j, x, y;
+    cairo_pattern_t *pattern;
+
+    cairo_select_font_face (cr, CAIRO_TEST_FONT_FAMILY " Sans",
+                           CAIRO_FONT_SLANT_NORMAL,
+                           CAIRO_FONT_WEIGHT_NORMAL);
+
+    for (j = 0; j < ARRAY_LENGTH (draw_funcs); j++) {
+       for (i = 0; i < ARRAY_LENGTH (pattern_funcs); i++) {
+           x = i * (WIDTH + PAD) + PAD;
+           y = j * (HEIGHT + PAD) + PAD;
+
+           cairo_save (cr);
+
+           pattern = cairo_pattern_create_linear (x + WIDTH, y,
+                                                  x,         y + HEIGHT);
+           cairo_pattern_add_color_stop_rgba (pattern, 0.2,
+                                              0.0, 0.0, 1.0, 1.0); /* Solid blue */
+           cairo_pattern_add_color_stop_rgba (pattern, 0.8,
+                                              0.0, 0.0, 1.0, 0.0); /* Transparent blue */
+           cairo_set_source (cr, pattern);
+           cairo_pattern_destroy (pattern);
+
+           cairo_rectangle (cr, x, y, WIDTH, HEIGHT);
+           cairo_fill_preserve (cr);
+           cairo_clip (cr);
+
+           cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+           pattern_funcs[i] (cr, x, y);
+           draw_funcs[j] (cr, x, y);
+           if (cairo_status (cr))
+               cairo_test_log (ctx, "%d %d HERE!\n", (int)i, (int)j);
+
+           cairo_restore (cr);
+       }
+    }
+
+    if (cairo_status (cr) != CAIRO_STATUS_SUCCESS)
+       cairo_test_log (ctx, "%d %d .HERE!\n", (int)i, (int)j);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (operator_source,
+           "Test of CAIRO_OPERATOR_SOURCE",
+           "operator", /* keywords */
+           NULL, /* requirements */
+           IMAGE_WIDTH, IMAGE_HEIGHT,
+           NULL, draw)
diff --git a/test/operator.c b/test/operator.c
new file mode 100644 (file)
index 0000000..666fcaf
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * Copyright © 2008 Chris Wilson
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Chris Wilson not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Chris Wilson makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * CHRIS WILSON DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL CHRIS WILSON BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+
+#define N_OPERATORS (CAIRO_OPERATOR_SATURATE + 1)
+#define SIZE 10
+#define PAD 3
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    unsigned int n;
+
+    cairo_translate (cr, PAD, PAD);
+
+    for (n = 0; n < N_OPERATORS; n++) {
+       cairo_reset_clip (cr);
+       cairo_rectangle (cr, 0, 0, SIZE, SIZE);
+       cairo_clip (cr);
+
+       cairo_set_source_rgb (cr, 1, 0, 0);
+       cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
+       cairo_rectangle (cr, 0, 0, SIZE-PAD, SIZE-PAD);
+       cairo_fill (cr);
+
+       cairo_set_source_rgb (cr, 0, 0, 1);
+       cairo_set_operator (cr, n);
+       cairo_rectangle (cr, PAD, PAD, SIZE-PAD, SIZE-PAD);
+       cairo_fill (cr);
+
+       cairo_translate (cr, SIZE+PAD, 0);
+    }
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (operator,
+           "Tests using set_operator()",
+           "operator", /* keywords */
+           NULL, /* requirements */
+           (SIZE+PAD) * N_OPERATORS + PAD, SIZE + 2*PAD,
+           NULL, draw)
diff --git a/test/over-above-source.c b/test/over-above-source.c
new file mode 100644 (file)
index 0000000..7191c8d
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * Copyright © 2007 Red Hat, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ */
+
+#include "cairo-test.h"
+
+#define SIZE 40
+#define PAD 2
+#define WIDTH (PAD + SIZE + PAD)
+#define HEIGHT WIDTH
+
+/* This test is designed to explore the interactions of "native" and
+ * "fallback" objects. For the ps surface, OVER with non-1.0 opacity
+ * will be a fallback while SOURCE will be native. For the pdf
+ * surface, it's the reverse where OVER is native while SOURCE is a
+ * fallback. */
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_translate (cr, PAD, PAD);
+
+    /* A red triangle with SOURCE */
+    cairo_move_to     (cr,  SIZE / 2, SIZE / 2);
+    cairo_rel_line_to (cr,  SIZE / 2, 0);
+    cairo_rel_line_to (cr, -SIZE / 2, SIZE / 2);
+    cairo_close_path (cr);
+
+    cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+    cairo_set_source_rgba (cr, 1., 0., 0., 0.5); /* 50% red */
+
+    cairo_fill (cr);
+
+    /* A green circle with OVER */
+    cairo_arc (cr, SIZE / 2, SIZE / 2, SIZE / 4, 0., 2. * M_PI);
+
+    cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
+    cairo_set_source_rgba (cr, 0., 1., 0., 0.5); /* 50% green */
+
+    cairo_fill (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (over_above_source,
+           "A simple test drawing a circle with OVER after a triangle drawn with SOURCE",
+           "operator", /* keywords */
+           NULL, /* requirements */
+           WIDTH, HEIGHT,
+           NULL, draw)
diff --git a/test/over-around-source.c b/test/over-around-source.c
new file mode 100644 (file)
index 0000000..531eda8
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * Copyright © 2007 Red Hat, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ */
+
+#include "cairo-test.h"
+
+#define SIZE 40
+#define PAD 2
+#define WIDTH (PAD + SIZE + PAD)
+#define HEIGHT WIDTH
+
+/* This test is designed to explore the interactions of "native" and
+ * "fallback" objects. For the ps surface, OVER with non-1.0 opacity
+ * will be a fallback while SOURCE will be native. For the pdf
+ * surface, it's the reverse where OVER is native while SOURCE is a
+ * fallback. */
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_translate (cr, PAD, PAD);
+
+    /* A green triangle with OVER */
+    cairo_move_to     (cr,  SIZE / 5, SIZE / 5);
+    cairo_rel_line_to (cr,  SIZE / 2, 0);
+    cairo_rel_line_to (cr, -SIZE / 2, SIZE / 2);
+    cairo_close_path (cr);
+
+    cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
+    cairo_set_source_rgba (cr, 0., 1., 0., 0.5); /* 50% green */
+
+    cairo_fill (cr);
+
+    /* A red circle with SOURCE */
+    cairo_arc (cr, SIZE / 2, SIZE / 2, SIZE / 4, 0., 2. * M_PI);
+
+    cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+    cairo_set_source_rgba (cr, 1., 0., 0., 0.5); /* 50% red */
+
+    cairo_fill (cr);
+
+    /* Another green triangle with OVER */
+    cairo_move_to     (cr,  SIZE / 2, SIZE / 2);
+    cairo_rel_line_to (cr,  SIZE / 2, 0);
+    cairo_rel_line_to (cr, -SIZE / 2, SIZE / 2);
+    cairo_close_path (cr);
+
+    cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
+    cairo_set_source_rgba (cr, 0., 1., 0., 0.5); /* 50% green */
+
+    cairo_fill (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (over_around_source,
+           "A simple test drawing a triangle with SOURCE between two circles drawn with OVER",
+           "operator", /* keywords */
+           NULL, /* requirements */
+           WIDTH, HEIGHT,
+           NULL, draw)
diff --git a/test/over-below-source.c b/test/over-below-source.c
new file mode 100644 (file)
index 0000000..7fe5bd8
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * Copyright © 2007 Red Hat, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ */
+
+#include "cairo-test.h"
+
+#define SIZE 40
+#define PAD 2
+#define WIDTH (PAD + SIZE + PAD)
+#define HEIGHT WIDTH
+
+/* This test is designed to explore the interactions of "native" and
+ * "fallback" objects. For the ps surface, OVER with non-1.0 opacity
+ * will be a fallback while SOURCE will be native. For the pdf
+ * surface, it's the reverse where OVER is native while SOURCE is a
+ * fallback. */
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_translate (cr, PAD, PAD);
+
+    /* A green circle with OVER */
+    cairo_arc (cr, SIZE / 2, SIZE / 2, SIZE / 4, 0., 2. * M_PI);
+
+    cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
+    cairo_set_source_rgba (cr, 0., 1., 0., 0.5); /* 50% green */
+
+    cairo_fill (cr);
+
+    /* A red triangle with SOURCE */
+    cairo_move_to (cr, SIZE / 2, SIZE / 2);
+    cairo_line_to (cr, SIZE,     SIZE / 2);
+    cairo_line_to (cr, SIZE / 2, SIZE);
+    cairo_close_path (cr);
+
+    cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+    cairo_set_source_rgba (cr, 1., 0., 0., 0.5); /* 50% red */
+
+    cairo_fill (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (over_below_source,
+           "A simple test drawing a circle with OVER before a triangle drawn with SOURCE",
+           "operator", /* keywords */
+           NULL, /* requirements */
+           WIDTH, HEIGHT,
+           NULL, draw)
diff --git a/test/over-between-source.c b/test/over-between-source.c
new file mode 100644 (file)
index 0000000..ffb6572
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * Copyright © 2007 Red Hat, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ */
+
+#include "cairo-test.h"
+
+#define SIZE 40
+#define PAD 2
+#define WIDTH (PAD + SIZE + PAD)
+#define HEIGHT WIDTH
+
+/* This test is designed to explore the interactions of "native" and
+ * "fallback" objects. For the ps surface, OVER with non-1.0 opacity
+ * will be a fallback while SOURCE will be native. For the pdf
+ * surface, it's the reverse where OVER is native while SOURCE is a
+ * fallback. */
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_translate (cr, PAD, PAD);
+
+    /* A red triangle with SOURCE */
+    cairo_move_to     (cr,  SIZE / 5, SIZE / 5);
+    cairo_rel_line_to (cr,  SIZE / 2, 0);
+    cairo_rel_line_to (cr, -SIZE / 2, SIZE / 2);
+    cairo_close_path (cr);
+
+    cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+    cairo_set_source_rgba (cr, 1., 0., 0., 0.5); /* 50% red */
+
+    cairo_fill (cr);
+
+    /* A green circle with OVER */
+    cairo_arc (cr, SIZE / 2, SIZE / 2, SIZE / 4, 0., 2. * M_PI);
+
+    cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
+    cairo_set_source_rgba (cr, 0., 1., 0., 0.5); /* 50% green */
+
+    cairo_fill (cr);
+
+    /* Another red triangle with SOURCE */
+    cairo_move_to     (cr,  SIZE / 2, SIZE / 2);
+    cairo_rel_line_to (cr,  SIZE / 2, 0);
+    cairo_rel_line_to (cr, -SIZE / 2, SIZE / 2);
+    cairo_close_path (cr);
+
+    cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+    cairo_set_source_rgba (cr, 1., 0., 0., 0.5); /* 50% red */
+
+    cairo_fill (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (over_between_source,
+           "A simple test drawing a circle with OVER between two triangles drawn with SOURCE",
+           "operator", /* keywords */
+           NULL, /* requirements */
+           WIDTH, HEIGHT,
+           NULL, draw)
diff --git a/test/overlapping-boxes.c b/test/overlapping-boxes.c
new file mode 100644 (file)
index 0000000..92211ee
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ * Copyright © 2011 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+/* Not strictly overlapping, but it does highlight the error in
+ * an optimisation of fill-box handling that I frequently am
+ * tempted to write.
+ */
+
+#include "cairo-test.h"
+
+#define WIDTH          (20)
+#define HEIGHT         (20)
+
+static void
+border (cairo_t *cr)
+{
+    cairo_rectangle (cr, 1, 1, 8, 8);
+    cairo_rectangle (cr, 1.25, 1.25, 7.5, 7.5);
+    cairo_rectangle (cr, 1.75, 1.75, 6.5, 6.5);
+    cairo_rectangle (cr, 2, 2, 6, 6);
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD);
+
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_paint (cr);
+
+    border (cr);
+    cairo_set_source_rgb (cr, 1, 0, 0);
+    cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
+    cairo_fill (cr);
+
+    cairo_translate (cr, 10, 0);
+
+    border (cr);
+    cairo_set_source_rgb (cr, 0, 0, 1);
+    cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+    cairo_fill (cr);
+
+    cairo_translate (cr, 0, 10);
+
+    cairo_rectangle (cr, 0, 0, 10, 10);
+    cairo_clip (cr);
+
+    border (cr);
+    cairo_set_source_rgb (cr, 0, 1, 0);
+    cairo_set_operator (cr, CAIRO_OPERATOR_IN);
+    cairo_fill (cr);
+
+    cairo_reset_clip (cr);
+
+    cairo_translate (cr, -10, 0);
+
+    cairo_rectangle (cr, 0, 0, 10, 10);
+    cairo_clip (cr);
+
+    border (cr);
+    cairo_set_source_rgb (cr, 0, 0, 1);
+    cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
+    cairo_fill (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (overlapping_boxes,
+           "A sub-pixel double border to highlight the danger in an easy optimisation",
+           "fill", /* keywords */
+           NULL, /* requirements */
+           WIDTH, HEIGHT,
+           NULL, draw)
diff --git a/test/overlapping-dash-caps.c b/test/overlapping-dash-caps.c
new file mode 100644 (file)
index 0000000..f7ba248
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * Copyright © 2011 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+
+#define SIZE 100
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    double dashes1[] = {20, 10};
+    double dashes2[] = {10, 1};
+
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_paint (cr);
+
+    cairo_set_line_width (cr, 15);
+
+    cairo_set_dash (cr, dashes1, 2, 0);
+    cairo_new_sub_path (cr);
+    cairo_arc (cr, SIZE/2, SIZE/2, SIZE/2-10, 0, 2*M_PI);
+
+    cairo_set_source_rgba (cr, 1, 0, 0, 0.5);
+    cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
+    cairo_stroke (cr);
+
+    cairo_set_dash (cr, dashes2, 2, 0);
+    cairo_new_sub_path (cr);
+    cairo_arc (cr, SIZE/2, SIZE/2, SIZE/4-5, 0, 2*M_PI);
+
+    cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE);
+    cairo_set_source_rgba (cr, 0, 1, 0, 0.5);
+    cairo_stroke (cr);
+
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (overlapping_dash_caps,
+           "Test intersections between neighbouring dash segments",
+           "overlap, dash", /* keywords */
+           NULL, /* requirements */
+           SIZE, SIZE,
+           NULL, draw)
diff --git a/test/overlapping-glyphs.c b/test/overlapping-glyphs.c
new file mode 100644 (file)
index 0000000..93067ab
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ * Copyright © 2009 Chris Wilson
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Chris Wilson not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Chris Wilson makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * CHRIS WILSON DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL CHRIS WILSON BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+
+#include <assert.h>
+
+#define TEXT_SIZE 12
+#define HEIGHT (TEXT_SIZE + 4)
+#define WIDTH 50
+
+#define MAX_GLYPHS 80
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_glyph_t glyphs_stack[MAX_GLYPHS], *glyphs;
+    const char *cairo = "Cairo";
+    const char *giza = "Giza";
+    cairo_text_extents_t cairo_extents;
+    cairo_text_extents_t giza_extents;
+    int count, num_glyphs;
+    double x0, y0;
+
+    /* We draw in the default black, so paint white first. */
+    cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); /* white */
+    cairo_paint (cr);
+
+    cairo_select_font_face (cr, CAIRO_TEST_FONT_FAMILY " Sans",
+                           CAIRO_FONT_SLANT_NORMAL,
+                           CAIRO_FONT_WEIGHT_NORMAL);
+    cairo_set_font_size (cr, TEXT_SIZE);
+
+    /* We want to overlap two strings, so compute overlapping glyphs.  */
+
+    cairo_text_extents (cr, cairo, &cairo_extents);
+    cairo_text_extents (cr, giza, &giza_extents);
+
+    x0 = WIDTH/2. - (cairo_extents.width/2. + cairo_extents.x_bearing);
+    y0 = HEIGHT/2. - (cairo_extents.height/2. + cairo_extents.y_bearing);
+    glyphs = glyphs_stack;
+    count = MAX_GLYPHS;
+    cairo_scaled_font_text_to_glyphs (cairo_get_scaled_font (cr),
+                                     x0, y0,
+                                     cairo, strlen (cairo),
+                                     &glyphs, &count,
+                                     NULL, NULL,
+                                     NULL);
+    assert (glyphs == glyphs_stack);
+    num_glyphs = count;
+
+    x0 = WIDTH/2. - (giza_extents.width/2. + giza_extents.x_bearing);
+    y0 = HEIGHT/2. - (giza_extents.height/2. + giza_extents.y_bearing);
+    glyphs = glyphs_stack + count;
+    count = MAX_GLYPHS - count;
+    cairo_scaled_font_text_to_glyphs (cairo_get_scaled_font (cr),
+                                     x0, y0,
+                                     giza, strlen (giza),
+                                     &glyphs, &count,
+                                     NULL, NULL,
+                                     NULL);
+    assert (glyphs == glyphs_stack + num_glyphs);
+    glyphs = glyphs_stack;
+    num_glyphs += count;
+
+    cairo_set_source_rgba (cr, 0, 0, 0, .5); /* translucent black, gray! */
+    cairo_show_glyphs (cr, glyphs, num_glyphs);
+
+    /* and compare with filling */
+    cairo_translate (cr, 0, HEIGHT);
+    cairo_glyph_path (cr, glyphs, num_glyphs);
+    cairo_fill (cr);
+
+    /* switch to using an unbounded operator for added complexity */
+    cairo_set_operator (cr, CAIRO_OPERATOR_IN);
+
+    cairo_translate (cr, WIDTH, -HEIGHT);
+    cairo_save (cr);
+    cairo_rectangle (cr, 0, 0, WIDTH, HEIGHT);
+    cairo_clip (cr);
+    cairo_show_glyphs (cr, glyphs, num_glyphs);
+    cairo_restore (cr);
+
+    cairo_translate (cr, 0, HEIGHT);
+    cairo_save (cr);
+    cairo_rectangle (cr, 0, 0, WIDTH, HEIGHT);
+    cairo_clip (cr);
+    cairo_glyph_path (cr, glyphs, num_glyphs);
+    cairo_fill (cr);
+    cairo_restore (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (overlapping_glyphs,
+           "Test handing of overlapping glyphs",
+           "text, glyphs", /* keywords */
+           NULL, /* requirements */
+           2 * WIDTH, 2 * HEIGHT,
+           NULL, draw)
+
diff --git a/test/paint-clip-fill.c b/test/paint-clip-fill.c
new file mode 100644 (file)
index 0000000..5a9e24f
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2011 SCore Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Taekyun Kim <podain77@gmail.com>
+ */
+
+#include "cairo-test.h"
+
+static void
+rounded_rectangle(cairo_t *cr,
+                 double x, double y,
+                 double width, double height,
+                 double radius)
+{
+    cairo_move_to   (cr, x, y + radius);
+    cairo_line_to   (cr, x, y + height - radius);
+    cairo_curve_to  (cr, x, y + height - radius/2.0,
+                         x + radius/2.0, y + height,
+                         x + radius, y + height);
+    cairo_line_to   (cr, x + width - radius, y + height);
+    cairo_curve_to  (cr, x + width - radius/2.0, y + height,
+                         x + width, y + height - radius/2.0,
+                         x + width, y + height - radius);
+    cairo_line_to   (cr, x + width, y + radius);
+    cairo_curve_to  (cr, x + width, y + radius/2.0,
+                         x + width - radius/2.0, y,
+                         x + width - radius, y);
+    cairo_line_to   (cr, x + radius, y);
+    cairo_curve_to  (cr, x + radius/2.0, y, x, y + radius/2.0, x, y + radius);
+    cairo_close_path(cr);
+}
+
+static cairo_test_status_t
+draw_mono (cairo_t *cr, int width, int height)
+{
+    cairo_set_antialias(cr, CAIRO_ANTIALIAS_NONE);
+    cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
+    cairo_set_source_rgba(cr, 1.0, 1.0, 1.0, 1.0);
+    cairo_paint(cr);
+
+    cairo_rectangle(cr, 20, 20, 60, 60);
+    cairo_clip(cr);
+
+    rounded_rectangle(cr, 0, 0, 100, 100, 10);
+    cairo_clip(cr);
+
+    cairo_set_source_rgba(cr, 1.0, 0.0, 0.0, 1.0);
+    cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
+    cairo_paint(cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+static cairo_test_status_t
+draw_aa (cairo_t *cr, int width, int height)
+{
+    cairo_set_antialias(cr, CAIRO_ANTIALIAS_DEFAULT);
+    cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
+    cairo_set_source_rgba(cr, 1.0, 1.0, 1.0, 1.0);
+    cairo_paint(cr);
+
+    cairo_rectangle(cr, 20, 20, 60, 60);
+    cairo_clip(cr);
+
+    rounded_rectangle(cr, 0, 0, 100, 100, 10);
+    cairo_clip(cr);
+
+    cairo_set_source_rgba(cr, 1.0, 0.0, 0.0, 1.0);
+    cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
+    cairo_paint(cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (paint_clip_fill_mono,
+           "Test reduction of a paint with a clip",
+           "paint, clip", /* keywords */
+           NULL, /* requirements */
+           100, 100,
+           NULL, draw_mono)
+CAIRO_TEST (paint_clip_fill_aa,
+           "Test reduction of a paint with a clip",
+           "paint, clip", /* keywords */
+           NULL, /* requirements */
+           100, 100,
+           NULL, draw_aa)
diff --git a/test/paint-repeat.c b/test/paint-repeat.c
new file mode 100644 (file)
index 0000000..c48d84c
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ */
+
+#include "cairo-test.h"
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_surface_t *surface;
+    uint32_t data[16] = {
+       0xffffffff, 0xffffffff,         0xffff0000, 0xffff0000,
+       0xffffffff, 0xffffffff,         0xffff0000, 0xffff0000,
+
+       0xff00ff00, 0xff00ff00,         0xff0000ff, 0xff0000ff,
+       0xff00ff00, 0xff00ff00,         0xff0000ff, 0xff0000ff
+    };
+
+    surface = cairo_image_surface_create_for_data ((unsigned char *) data,
+                                                  CAIRO_FORMAT_RGB24, 4, 4, 16);
+
+    /* We use a non-zero offset larger than the source surface size to
+     * stress cairo out a bit more. */
+    cairo_set_source_surface (cr, surface, 10, 10);
+    cairo_pattern_set_filter (cairo_get_source (cr), CAIRO_FILTER_NEAREST);
+    cairo_pattern_set_extend (cairo_get_source (cr), CAIRO_EXTEND_REPEAT);
+    cairo_paint (cr);
+
+    cairo_surface_finish (surface); /* data will go out of scope */
+    cairo_surface_destroy (surface);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (paint_repeat,
+           "Test calls to cairo_paint with a repeating source surface pattern",
+           "paint", /* keywords */
+           NULL, /* requirements */
+           8, 8,
+           NULL, draw)
diff --git a/test/paint-source-alpha.c b/test/paint-source-alpha.c
new file mode 100644 (file)
index 0000000..cb2d488
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Copyright © 2005 Red Hat, Inc.
+ * Copyright © 2006 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ */
+
+#include "cairo-test.h"
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_surface_t *surface;
+    uint32_t data[16] = {
+       0x80808080, 0x80808080,         0x80800000, 0x80800000,
+       0x80808080, 0x80808080,         0x80800000, 0x80800000,
+
+       0x80008000, 0x80008000,         0x80000080, 0x80000080,
+       0x80008000, 0x80008000,         0x80000080, 0x80000080
+    };
+
+    surface = cairo_image_surface_create_for_data ((unsigned char *) data,
+                                                  CAIRO_FORMAT_ARGB32, 4, 4, 16);
+
+    cairo_test_paint_checkered (cr);
+
+    cairo_scale (cr, 4, 4);
+
+    cairo_set_source_surface (cr, surface, 2 , 2);
+    cairo_pattern_set_filter (cairo_get_source (cr), CAIRO_FILTER_NEAREST);
+    cairo_paint (cr);
+
+    cairo_surface_finish (surface); /* data will go out of scope */
+    cairo_surface_destroy (surface);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (paint_source_alpha,
+           "Simple test of cairo_paint with a source surface with non-opaque alpha",
+           "paint, alpha", /* keywords */
+           NULL, /* requirements */
+           32, 32,
+           NULL, draw)
diff --git a/test/paint-with-alpha.c b/test/paint-with-alpha.c
new file mode 100644 (file)
index 0000000..8daa935
--- /dev/null
@@ -0,0 +1,148 @@
+/*
+ * Copyright © 2005 Red Hat, Inc.
+ * Copyright © 2006 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ */
+
+#include "cairo-test.h"
+
+static uint32_t data[16] = {
+    0xffffffff, 0xffffffff,            0xffff0000, 0xffff0000,
+    0xffffffff, 0xffffffff,            0xffff0000, 0xffff0000,
+
+    0xff00ff00, 0xff00ff00,            0xff0000ff, 0xff0000ff,
+    0xff00ff00, 0xff00ff00,            0xff0000ff, 0xff0000ff
+};
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_surface_t *surface;
+
+    surface = cairo_image_surface_create_for_data ((unsigned char *) data,
+                                                  CAIRO_FORMAT_RGB24, 4, 4, 16);
+
+    cairo_test_paint_checkered (cr);
+
+    cairo_scale (cr, 4, 4);
+
+    cairo_set_source_surface (cr, surface, 2 , 2);
+    cairo_pattern_set_filter (cairo_get_source (cr), CAIRO_FILTER_NEAREST);
+    cairo_paint_with_alpha (cr, 0.5);
+
+    cairo_surface_finish (surface); /* data will go out of scope */
+    cairo_surface_destroy (surface);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+static cairo_test_status_t
+draw_solid_clip (cairo_t *cr, int width, int height)
+{
+    cairo_test_paint_checkered (cr);
+
+    cairo_rectangle (cr, 2.5, 2.5, 27, 27);
+    cairo_clip (cr);
+
+    cairo_set_source_rgb (cr, 1., 0.,0.);
+    cairo_paint_with_alpha (cr, 0.5);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+static cairo_test_status_t
+draw_clip (cairo_t *cr, int width, int height)
+{
+    cairo_surface_t *surface;
+
+    surface = cairo_image_surface_create_for_data ((unsigned char *) data,
+                                                  CAIRO_FORMAT_RGB24, 4, 4, 16);
+
+    cairo_test_paint_checkered (cr);
+
+    cairo_rectangle (cr, 10.5, 10.5, 11, 11);
+    cairo_clip (cr);
+
+    cairo_scale (cr, 4, 4);
+
+    cairo_set_source_surface (cr, surface, 2 , 2);
+    cairo_pattern_set_filter (cairo_get_source (cr), CAIRO_FILTER_NEAREST);
+    cairo_paint_with_alpha (cr, 0.5);
+
+    cairo_surface_finish (surface); /* data will go out of scope */
+    cairo_surface_destroy (surface);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+static cairo_test_status_t
+draw_clip_mask (cairo_t *cr, int width, int height)
+{
+    cairo_surface_t *surface;
+
+    surface = cairo_image_surface_create_for_data ((unsigned char *) data,
+                                                  CAIRO_FORMAT_RGB24, 4, 4, 16);
+
+    cairo_test_paint_checkered (cr);
+
+    cairo_move_to (cr, 16, 5);
+    cairo_line_to (cr, 5, 16);
+    cairo_line_to (cr, 16, 27);
+    cairo_line_to (cr, 27, 16);
+    cairo_clip (cr);
+
+    cairo_scale (cr, 4, 4);
+
+    cairo_set_source_surface (cr, surface, 2 , 2);
+    cairo_pattern_set_filter (cairo_get_source (cr), CAIRO_FILTER_NEAREST);
+    cairo_paint_with_alpha (cr, 0.5);
+
+    cairo_surface_finish (surface); /* data will go out of scope */
+    cairo_surface_destroy (surface);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (paint_with_alpha,
+           "Simple test of cairo_paint_with_alpha",
+           "paint, alpha", /* keywords */
+           NULL, /* requirements */
+           32, 32,
+           NULL, draw)
+CAIRO_TEST (paint_with_alpha_solid_clip,
+           "Simple test of cairo_paint_with_alpha+unaligned clip",
+           "paint, alpha, clip", /* keywords */
+           NULL, /* requirements */
+           32, 32,
+           NULL, draw_solid_clip)
+CAIRO_TEST (paint_with_alpha_clip,
+           "Simple test of cairo_paint_with_alpha+unaligned clip",
+           "paint, alpha, clip", /* keywords */
+           NULL, /* requirements */
+           32, 32,
+           NULL, draw_clip)
+CAIRO_TEST (paint_with_alpha_clip_mask,
+           "Simple test of cairo_paint_with_alpha+unaligned clip",
+           "paint, alpha, clip", /* keywords */
+           NULL, /* requirements */
+           32, 32,
+           NULL, draw_clip_mask)
diff --git a/test/paint.c b/test/paint.c
new file mode 100644 (file)
index 0000000..c5fd648
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ */
+
+#include "cairo-test.h"
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_set_source_rgb (cr, 0, 0, 1);
+    cairo_paint (cr);
+
+    cairo_translate (cr, 2, 2);
+    cairo_scale (cr, 0.5, 0.5);
+
+    cairo_set_source_rgb (cr, 1, 0, 0);
+    cairo_paint (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (paint,
+           "Test calls to cairo_paint",
+           "paint", /* keywords */
+           NULL, /* requirements */
+           8, 8,
+           NULL, draw)
diff --git a/test/partial-clip-text.c b/test/partial-clip-text.c
new file mode 100644 (file)
index 0000000..4d8bae0
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ * Copyright 2010 Igor Nikitin
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Igor Nikitin <igor_nikitin@valentina-db.com>
+ */
+
+#include "cairo-test.h"
+
+#define HEIGHT 15
+#define WIDTH 40
+
+static void background (cairo_t *cr)
+{
+     cairo_set_source_rgb( cr, 0, 0, 0 );
+     cairo_paint (cr);
+}
+
+static void text (cairo_t *cr)
+{
+     cairo_move_to (cr, 0, 12);
+     cairo_set_source_rgb (cr, 1, 1, 1);
+     cairo_show_text (cr, "CAIRO");
+}
+
+static cairo_test_status_t
+top (cairo_t *cr, int width, int height)
+{
+     background (cr);
+
+     cairo_rectangle (cr, 0, 0, WIDTH, 5);
+     cairo_clip (cr);
+
+     text (cr);
+
+     return CAIRO_TEST_SUCCESS;
+}
+
+static cairo_test_status_t
+bottom (cairo_t *cr, int width, int height)
+{
+     background (cr);
+
+     cairo_rectangle (cr, 0, HEIGHT-5, WIDTH, 5);
+     cairo_clip (cr);
+
+     text (cr);
+
+     return CAIRO_TEST_SUCCESS;
+}
+
+static cairo_test_status_t
+left (cairo_t *cr, int width, int height)
+{
+     background (cr);
+
+     cairo_rectangle (cr, 0, 0, 10, HEIGHT);
+     cairo_clip (cr);
+
+     text (cr);
+
+     return CAIRO_TEST_SUCCESS;
+}
+
+static cairo_test_status_t
+right (cairo_t *cr, int width, int height)
+{
+     background (cr);
+
+     cairo_rectangle (cr, WIDTH-10, 0, 10, HEIGHT);
+     cairo_clip (cr);
+
+     text (cr);
+
+     return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (partial_clip_text_top,
+           "Tests drawing text through a single, partial clip.",
+           "clip, text", /* keywords */
+           NULL, /* requirements */
+           WIDTH, HEIGHT,
+           NULL, top)
+CAIRO_TEST (partial_clip_text_bottom,
+           "Tests drawing text through a single, partial clip.",
+           "clip, text", /* keywords */
+           NULL, /* requirements */
+           WIDTH, HEIGHT,
+           NULL, bottom)
+CAIRO_TEST (partial_clip_text_left,
+           "Tests drawing text through a single, partial clip.",
+           "clip, text", /* keywords */
+           NULL, /* requirements */
+           WIDTH, HEIGHT,
+           NULL, left)
+CAIRO_TEST (partial_clip_text_right,
+           "Tests drawing text through a single, partial clip.",
+           "clip, text", /* keywords */
+           NULL, /* requirements */
+           WIDTH, HEIGHT,
+           NULL, right)
diff --git a/test/partial-coverage.c b/test/partial-coverage.c
new file mode 100644 (file)
index 0000000..0b5cbdc
--- /dev/null
@@ -0,0 +1,680 @@
+/*
+ * Copyright 2010 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+
+/* Test the sampling stratagems of the rasterisers by creating pixels
+ * containing minute holes and seeing how close to the expected
+ * coverage each rasteriser approaches.
+ */
+
+#define SIZE 64
+
+#include "../src/cairo-fixed-type-private.h"
+#define SAMPLE (1 << CAIRO_FIXED_FRAC_BITS)
+
+static uint32_t state;
+
+static uint32_t
+hars_petruska_f54_1_random (void)
+{
+#define rol(x,k) ((x << k) | (x >> (32-k)))
+    return state = (state ^ rol (state, 5) ^ rol (state, 24)) + 0x37798849;
+#undef rol
+}
+
+static double
+uniform_random (void)
+{
+    return hars_petruska_f54_1_random() / (double) UINT32_MAX;
+}
+
+/* coverage is given in [0,sample] */
+static void
+compute_occupancy (uint8_t *occupancy, int coverage, int sample)
+{
+    int i, c;
+
+    if (coverage < sample/2) {
+       memset (occupancy, 0, sample);
+       if (coverage == 0)
+           return;
+
+       for (i = c = 0; i < sample; i++) {
+           if ((sample - i) * uniform_random() < coverage - c) {
+               occupancy[i] = 0xff;
+               if (++c == coverage)
+                   return;
+           }
+       }
+    } else {
+       coverage = sample - coverage;
+       memset (occupancy, 0xff, sample);
+       if (coverage == 0)
+           return;
+
+       for (i = c = 0; i < sample; i++) {
+           if ((sample - i) * uniform_random() < coverage - c) {
+               occupancy[i] = 0;
+               if (++c == coverage)
+                   return;
+           }
+       }
+    }
+}
+
+static cairo_test_status_t
+reference (cairo_t *cr, int width, int height)
+{
+    int i;
+
+    cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
+    cairo_paint (cr);
+
+    for (i = 0; i < SIZE*SIZE; i++) {
+       cairo_set_source_rgba (cr, 1., 1., 1.,
+                              i / (double) (SIZE * SIZE));
+       cairo_rectangle (cr, i % SIZE, i / SIZE, 1, 1);
+       cairo_fill (cr);
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_test_status_t
+three_quarter_reference (cairo_t *cr, int width, int height)
+{
+    int i;
+
+    cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
+    cairo_paint (cr);
+
+    for (i = 0; i < SIZE*SIZE; i++) {
+       cairo_set_source_rgba (cr, 1., 1., 1.,
+                              .75 * i / (double) (SIZE * SIZE));
+       cairo_rectangle (cr, i % SIZE, i / SIZE, 1, 1);
+       cairo_fill (cr);
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_test_status_t
+half_reference (cairo_t *cr, int width, int height)
+{
+    int i;
+
+    cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
+    cairo_paint (cr);
+
+    for (i = 0; i < SIZE*SIZE; i++) {
+       cairo_set_source_rgba (cr, 1., 1., 1.,
+                              .5 * i / (double) (SIZE * SIZE));
+       cairo_rectangle (cr, i % SIZE, i / SIZE, 1, 1);
+       cairo_fill (cr);
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_test_status_t
+rectangles (cairo_t *cr, int width, int height)
+{
+    uint8_t *occupancy;
+    int i, j, channel;
+
+    state = 0x12345678;
+    occupancy = xmalloc (SAMPLE*SAMPLE);
+
+    cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
+    cairo_paint (cr);
+
+    cairo_set_operator (cr, CAIRO_OPERATOR_ADD);
+    for (channel = 0; channel < 3; channel++) {
+       switch (channel) {
+       default:
+       case 0: cairo_set_source_rgb (cr, 1.0, 0.0, 0.0); break;
+       case 1: cairo_set_source_rgb (cr, 0.0, 1.0, 0.0); break;
+       case 2: cairo_set_source_rgb (cr, 0.0, 0.0, 1.0); break;
+       }
+
+       for (i = 0; i < SIZE*SIZE; i++) {
+           int xs, ys;
+
+           compute_occupancy (occupancy, SAMPLE*SAMPLE * i / (SIZE * SIZE), SAMPLE*SAMPLE);
+
+           xs = i % SIZE * SAMPLE;
+           ys = i / SIZE * SAMPLE;
+           for (j = 0; j < SAMPLE*SAMPLE; j++) {
+               if (occupancy[j]) {
+                   cairo_rectangle (cr,
+                                    (j % SAMPLE + xs) / (double) SAMPLE,
+                                    (j / SAMPLE + ys) / (double) SAMPLE,
+                                    1 / (double) SAMPLE,
+                                    1 / (double) SAMPLE);
+               }
+           }
+           cairo_fill (cr);
+       }
+    }
+
+    free (occupancy);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+static cairo_test_status_t
+intersecting_quads (cairo_t *cr, int width, int height)
+{
+    uint8_t *occupancy;
+    int i, j, channel;
+
+    state = 0x12345678;
+    occupancy = xmalloc (SAMPLE*SAMPLE);
+
+    cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
+    cairo_paint (cr);
+
+    cairo_set_operator (cr, CAIRO_OPERATOR_ADD);
+    for (channel = 0; channel < 3; channel++) {
+       switch (channel) {
+       default:
+       case 0: cairo_set_source_rgb (cr, 1.0, 0.0, 0.0); break;
+       case 1: cairo_set_source_rgb (cr, 0.0, 1.0, 0.0); break;
+       case 2: cairo_set_source_rgb (cr, 0.0, 0.0, 1.0); break;
+       }
+
+       for (i = 0; i < SIZE*SIZE; i++) {
+           int xs, ys;
+
+           compute_occupancy (occupancy, SAMPLE*SAMPLE * i / (SIZE * SIZE), SAMPLE*SAMPLE);
+
+           xs = i % SIZE * SAMPLE;
+           ys = i / SIZE * SAMPLE;
+           for (j = 0; j < SAMPLE*SAMPLE; j++) {
+               if (occupancy[j]) {
+                   cairo_move_to (cr,
+                                    (j % SAMPLE + xs) / (double) SAMPLE,
+                                    (j / SAMPLE + ys) / (double) SAMPLE);
+                   cairo_rel_line_to (cr, 1 / (double) SAMPLE, 1 / (double) SAMPLE);
+                   cairo_rel_line_to (cr, 0, -1 / (double) SAMPLE);
+                   cairo_rel_line_to (cr, -1 / (double) SAMPLE, 1 / (double) SAMPLE);
+                   cairo_close_path (cr);
+               }
+           }
+           cairo_fill (cr);
+       }
+    }
+
+    free (occupancy);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+static cairo_test_status_t
+half_triangles (cairo_t *cr, int width, int height)
+{
+    uint8_t *occupancy;
+    int i, j, channel;
+
+    state = 0x12345678;
+    occupancy = xmalloc (SAMPLE*SAMPLE);
+
+    cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
+    cairo_paint (cr);
+
+    cairo_set_operator (cr, CAIRO_OPERATOR_ADD);
+    for (channel = 0; channel < 3; channel++) {
+       switch (channel) {
+       default:
+       case 0: cairo_set_source_rgb (cr, 1.0, 0.0, 0.0); break;
+       case 1: cairo_set_source_rgb (cr, 0.0, 1.0, 0.0); break;
+       case 2: cairo_set_source_rgb (cr, 0.0, 0.0, 1.0); break;
+       }
+
+       for (i = 0; i < SIZE*SIZE; i++) {
+           int xs, ys;
+
+           compute_occupancy (occupancy, SAMPLE*SAMPLE * i / (SIZE * SIZE), SAMPLE*SAMPLE);
+
+           xs = i % SIZE * SAMPLE;
+           ys = i / SIZE * SAMPLE;
+           for (j = 0; j < SAMPLE*SAMPLE; j++) {
+               if (occupancy[j]) {
+                   int x = j % SAMPLE + xs;
+                   int y = j / SAMPLE + ys;
+                   cairo_move_to (cr, x / (double) SAMPLE, y / (double) SAMPLE);
+                   cairo_line_to (cr, (x+1) / (double) SAMPLE, (y+1) / (double) SAMPLE);
+                   cairo_line_to (cr, (x+1) / (double) SAMPLE, y / (double) SAMPLE);
+                   cairo_close_path (cr);
+               }
+           }
+           cairo_fill (cr);
+       }
+    }
+
+    free (occupancy);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+static cairo_test_status_t
+overlap_half_triangles (cairo_t *cr, int width, int height)
+{
+    uint8_t *occupancy;
+    int i, j, channel;
+
+    state = 0x12345678;
+    occupancy = xmalloc (SAMPLE*SAMPLE);
+
+    cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
+    cairo_paint (cr);
+
+    cairo_set_operator (cr, CAIRO_OPERATOR_ADD);
+    for (channel = 0; channel < 3; channel++) {
+       switch (channel) {
+       default:
+       case 0: cairo_set_source_rgb (cr, 1.0, 0.0, 0.0); break;
+       case 1: cairo_set_source_rgb (cr, 0.0, 1.0, 0.0); break;
+       case 2: cairo_set_source_rgb (cr, 0.0, 0.0, 1.0); break;
+       }
+
+       for (i = 0; i < SIZE*SIZE; i++) {
+           int xs, ys;
+
+           compute_occupancy (occupancy, SAMPLE/2*SAMPLE/2 * i / (SIZE * SIZE), SAMPLE/2*SAMPLE/2);
+
+           xs = i % SIZE * SAMPLE;
+           ys = i / SIZE * SAMPLE;
+           for (j = 0; j < SAMPLE/2*SAMPLE/2; j++) {
+               if (occupancy[j]) {
+                   int x = 2 * (j % (SAMPLE/2)) + xs;
+                   int y = 2 * (j / (SAMPLE/2)) + ys;
+
+                   /* Add a 4-tile composed of two overlapping triangles.
+                    *   .__.__.
+                    *   |\   /|
+                    *   | \ / |
+                    *   .  x  |
+                    *   | / \ |
+                    *   |/   \|
+                    *   .     .
+                    *
+                    * Coverage should be computable as 50% (due to counter-winding).
+                    */
+
+                   cairo_move_to (cr, (x) / (double) SAMPLE,   (y) / (double) SAMPLE);
+                   cairo_line_to (cr, (x) / (double) SAMPLE,   (y+2) / (double) SAMPLE);
+                   cairo_line_to (cr, (x+2) / (double) SAMPLE, (y) / (double) SAMPLE);
+                   cairo_close_path (cr);
+
+                   cairo_move_to (cr, (x) / (double) SAMPLE,   (y) / (double) SAMPLE);
+                   cairo_line_to (cr, (x+2) / (double) SAMPLE, (y) / (double) SAMPLE);
+                   cairo_line_to (cr, (x+2) / (double) SAMPLE, (y+2) / (double) SAMPLE);
+                   cairo_close_path (cr);
+               }
+           }
+           cairo_fill (cr);
+       }
+    }
+
+    free (occupancy);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+static cairo_test_status_t
+overlap_half_triangles_eo (cairo_t *cr, int width, int height)
+{
+    uint8_t *occupancy;
+    int i, j, channel;
+
+    state = 0x12345678;
+    occupancy = xmalloc (SAMPLE*SAMPLE);
+
+    cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
+    cairo_paint (cr);
+
+    cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD);
+
+    cairo_set_operator (cr, CAIRO_OPERATOR_ADD);
+    for (channel = 0; channel < 3; channel++) {
+       switch (channel) {
+       default:
+       case 0: cairo_set_source_rgb (cr, 1.0, 0.0, 0.0); break;
+       case 1: cairo_set_source_rgb (cr, 0.0, 1.0, 0.0); break;
+       case 2: cairo_set_source_rgb (cr, 0.0, 0.0, 1.0); break;
+       }
+
+       for (i = 0; i < SIZE*SIZE; i++) {
+           int xs, ys;
+
+           compute_occupancy (occupancy, SAMPLE/2*SAMPLE/2 * i / (SIZE * SIZE), SAMPLE/2*SAMPLE/2);
+
+           xs = i % SIZE * SAMPLE;
+           ys = i / SIZE * SAMPLE;
+           for (j = 0; j < SAMPLE/2*SAMPLE/2; j++) {
+               if (occupancy[j]) {
+                   int x = 2 * (j % (SAMPLE/2)) + xs;
+                   int y = 2 * (j / (SAMPLE/2)) + ys;
+
+                   /* Add a 4-tile composed of two overlapping triangles.
+                    *   .__.__.
+                    *   |\   /|
+                    *   | \ / |
+                    *   .  x  |
+                    *   | / \ |
+                    *   |/   \|
+                    *   .     .
+                    *
+                    * Coverage should be computable as 50%, due to even-odd fill rule.
+                    */
+
+                   cairo_move_to (cr, (x) / (double) SAMPLE,   (y) / (double) SAMPLE);
+                   cairo_line_to (cr, (x) / (double) SAMPLE,   (y+2) / (double) SAMPLE);
+                   cairo_line_to (cr, (x+2) / (double) SAMPLE, (y) / (double) SAMPLE);
+                   cairo_close_path (cr);
+
+                   cairo_move_to (cr, (x) / (double) SAMPLE,   (y) / (double) SAMPLE);
+                   cairo_line_to (cr, (x+2) / (double) SAMPLE, (y+2) / (double) SAMPLE);
+                   cairo_line_to (cr, (x+2) / (double) SAMPLE, (y) / (double) SAMPLE);
+                   cairo_close_path (cr);
+               }
+           }
+           cairo_fill (cr);
+       }
+    }
+
+    free (occupancy);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+static cairo_test_status_t
+overlap_three_quarter_triangles (cairo_t *cr, int width, int height)
+{
+    uint8_t *occupancy;
+    int i, j, channel;
+
+    state = 0x12345678;
+    occupancy = xmalloc (SAMPLE*SAMPLE);
+
+    cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
+    cairo_paint (cr);
+
+    cairo_set_operator (cr, CAIRO_OPERATOR_ADD);
+    for (channel = 0; channel < 3; channel++) {
+       switch (channel) {
+       default:
+       case 0: cairo_set_source_rgb (cr, 1.0, 0.0, 0.0); break;
+       case 1: cairo_set_source_rgb (cr, 0.0, 1.0, 0.0); break;
+       case 2: cairo_set_source_rgb (cr, 0.0, 0.0, 1.0); break;
+       }
+
+       for (i = 0; i < SIZE*SIZE; i++) {
+           int xs, ys;
+
+           compute_occupancy (occupancy, SAMPLE/2*SAMPLE/2 * i / (SIZE * SIZE), SAMPLE/2*SAMPLE/2);
+
+           xs = i % SIZE * SAMPLE;
+           ys = i / SIZE * SAMPLE;
+           for (j = 0; j < SAMPLE/2*SAMPLE/2; j++) {
+               if (occupancy[j]) {
+                   int x = 2 * (j % (SAMPLE/2)) + xs;
+                   int y = 2 * (j / (SAMPLE/2)) + ys;
+
+                   /* Add a 4-tile composed of two overlapping triangles.
+                    *   .__.__.
+                    *   |\   /|
+                    *   | \ / |
+                    *   .  x  |
+                    *   | / \ |
+                    *   |/   \|
+                    *   .     .
+                    *
+                    * Coverage should be computable as 75%.
+                    */
+
+                   cairo_move_to (cr, (x) / (double) SAMPLE,   (y) / (double) SAMPLE);
+                   cairo_line_to (cr, (x) / (double) SAMPLE,   (y+2) / (double) SAMPLE);
+                   cairo_line_to (cr, (x+2) / (double) SAMPLE, (y) / (double) SAMPLE);
+                   cairo_close_path (cr);
+
+                   cairo_move_to (cr, (x) / (double) SAMPLE,   (y) / (double) SAMPLE);
+                   cairo_line_to (cr, (x+2) / (double) SAMPLE, (y+2) / (double) SAMPLE);
+                   cairo_line_to (cr, (x+2) / (double) SAMPLE, (y) / (double) SAMPLE);
+                   cairo_close_path (cr);
+               }
+           }
+           cairo_fill (cr);
+       }
+    }
+
+    free (occupancy);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+static cairo_test_status_t
+triangles (cairo_t *cr, int width, int height)
+{
+    uint8_t *occupancy;
+    int i, j, channel;
+
+    state = 0x12345678;
+    occupancy = xmalloc (SAMPLE*SAMPLE);
+
+    cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
+    cairo_paint (cr);
+
+    cairo_set_operator (cr, CAIRO_OPERATOR_ADD);
+    for (channel = 0; channel < 3; channel++) {
+       switch (channel) {
+       default:
+       case 0: cairo_set_source_rgb (cr, 1.0, 0.0, 0.0); break;
+       case 1: cairo_set_source_rgb (cr, 0.0, 1.0, 0.0); break;
+       case 2: cairo_set_source_rgb (cr, 0.0, 0.0, 1.0); break;
+       }
+
+       for (i = 0; i < SIZE*SIZE; i++) {
+           int xs, ys;
+
+           compute_occupancy (occupancy, SAMPLE*SAMPLE * i / (SIZE * SIZE), SAMPLE*SAMPLE);
+
+           xs = i % SIZE * SAMPLE;
+           ys = i / SIZE * SAMPLE;
+           for (j = 0; j < SAMPLE*SAMPLE; j++) {
+               if (occupancy[j]) {
+                   /* Add a tile composed of two non-overlapping triangles.
+                    *   .__.
+                    *   | /|
+                    *   |/ |
+                    *   .--.
+                    */
+                   int x = j % SAMPLE + xs;
+                   int y = j / SAMPLE + ys;
+
+                   /* top-left triangle */
+                   cairo_move_to (cr, (x) / (double) SAMPLE,   (y) / (double) SAMPLE);
+                   cairo_line_to (cr, (x+1) / (double) SAMPLE, (y) / (double) SAMPLE);
+                   cairo_line_to (cr, (x) / (double) SAMPLE,   (y+1) / (double) SAMPLE);
+                   cairo_close_path (cr);
+
+                   /* bottom-right triangle */
+                   cairo_move_to (cr, (x) / (double) SAMPLE,   (y+1) / (double) SAMPLE);
+                   cairo_line_to (cr, (x+1) / (double) SAMPLE, (y+1) / (double) SAMPLE);
+                   cairo_line_to (cr, (x+1) / (double) SAMPLE, (y) / (double) SAMPLE);
+                   cairo_close_path (cr);
+               }
+           }
+           cairo_fill (cr);
+       }
+    }
+
+    free (occupancy);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+static cairo_test_status_t
+intersecting_triangles (cairo_t *cr, int width, int height)
+{
+    uint8_t *occupancy;
+    int i, j, channel;
+
+    state = 0x12345678;
+    occupancy = xmalloc (SAMPLE*SAMPLE);
+
+    cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
+    cairo_paint (cr);
+
+    cairo_set_operator (cr, CAIRO_OPERATOR_ADD);
+    for (channel = 0; channel < 3; channel++) {
+       switch (channel) {
+       default:
+       case 0: cairo_set_source_rgb (cr, 1.0, 0.0, 0.0); break;
+       case 1: cairo_set_source_rgb (cr, 0.0, 1.0, 0.0); break;
+       case 2: cairo_set_source_rgb (cr, 0.0, 0.0, 1.0); break;
+       }
+
+       for (i = 0; i < SIZE*SIZE; i++) {
+           int xs, ys;
+
+           compute_occupancy (occupancy, SAMPLE*SAMPLE * i / (SIZE * SIZE), SAMPLE*SAMPLE);
+
+           xs = i % SIZE * SAMPLE;
+           ys = i / SIZE * SAMPLE;
+           for (j = 0; j < SAMPLE*SAMPLE; j++) {
+               if (occupancy[j]) {
+                   /* Add 2 overlapping tiles in a single cell, each composed
+                    * of two non-overlapping triangles.
+                    *   .--.   .--.
+                    *   | /|   |\ |
+                    *   |/ | + | \|
+                    *   .--.   .--.
+                    */
+                   int x = j % SAMPLE + xs;
+                   int y = j / SAMPLE + ys;
+
+                   /* first pair of triangles, diagonal bottom-left to top-right */
+                   cairo_move_to (cr, (x) / (double) SAMPLE,   (y) / (double) SAMPLE);
+                   cairo_line_to (cr, (x+1) / (double) SAMPLE, (y) / (double) SAMPLE);
+                   cairo_line_to (cr, (x) / (double) SAMPLE,   (y+1) / (double) SAMPLE);
+                   cairo_close_path (cr);
+                   cairo_move_to (cr, (x) / (double) SAMPLE,   (y+1) / (double) SAMPLE);
+                   cairo_line_to (cr, (x+1) / (double) SAMPLE, (y+1) / (double) SAMPLE);
+                   cairo_line_to (cr, (x+1) / (double) SAMPLE, (y) / (double) SAMPLE);
+                   cairo_close_path (cr);
+
+                   /* second pair of triangles, diagonal top-left to bottom-right */
+                   cairo_move_to (cr, (x) / (double) SAMPLE,   (y) / (double) SAMPLE);
+                   cairo_line_to (cr, (x+1) / (double) SAMPLE, (y+1) / (double) SAMPLE);
+                   cairo_line_to (cr, (x+1) / (double) SAMPLE,   (y) / (double) SAMPLE);
+                   cairo_close_path (cr);
+                   cairo_move_to (cr, (x) / (double) SAMPLE,   (y) / (double) SAMPLE);
+                   cairo_line_to (cr, (x+1) / (double) SAMPLE, (y+1) / (double) SAMPLE);
+                   cairo_line_to (cr, (x) / (double) SAMPLE, (y+1) / (double) SAMPLE);
+                   cairo_close_path (cr);
+               }
+           }
+           cairo_fill (cr);
+       }
+    }
+
+    free (occupancy);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (partial_coverage_rectangles,
+           "Check the fidelity of the rasterisation.",
+           "coverage raster", /* keywords */
+           "target=raster slow", /* requirements */
+           SIZE, SIZE,
+           NULL, rectangles)
+
+CAIRO_TEST (partial_coverage_intersecting_quads,
+           "Check the fidelity of the rasterisation.",
+           "coverage raster", /* keywords */
+           "target=raster slow", /* requirements */
+           SIZE, SIZE,
+           NULL, intersecting_quads)
+
+CAIRO_TEST (partial_coverage_intersecting_triangles,
+           "Check the fidelity of the rasterisation.",
+           "coverage raster", /* keywords */
+           "target=raster slow", /* requirements */
+           SIZE, SIZE,
+           NULL, intersecting_triangles)
+CAIRO_TEST (partial_coverage_triangles,
+           "Check the fidelity of the rasterisation.",
+           "coverage raster", /* keywords */
+           "target=raster slow", /* requirements */
+           SIZE, SIZE,
+           NULL, triangles)
+CAIRO_TEST (partial_coverage_overlap_three_quarter_triangles,
+           "Check the fidelity of the rasterisation.",
+           "coverage raster", /* keywords */
+           "target=raster slow", /* requirements */
+           SIZE, SIZE,
+           NULL, overlap_three_quarter_triangles)
+CAIRO_TEST (partial_coverage_overlap_half_triangles_eo,
+           "Check the fidelity of the rasterisation.",
+           "coverage raster", /* keywords */
+           "target=raster slow", /* requirements */
+           SIZE, SIZE,
+           NULL, overlap_half_triangles_eo)
+CAIRO_TEST (partial_coverage_overlap_half_triangles,
+           "Check the fidelity of the rasterisation.",
+           "coverage raster", /* keywords */
+           "target=raster slow", /* requirements */
+           SIZE, SIZE,
+           NULL, overlap_half_triangles)
+CAIRO_TEST (partial_coverage_half_triangles,
+           "Check the fidelity of the rasterisation.",
+           "coverage raster", /* keywords */
+           "target=raster slow", /* requirements */
+           SIZE, SIZE,
+           NULL, half_triangles)
+
+CAIRO_TEST (partial_coverage_reference,
+           "Check the fidelity of this test.",
+           "coverage raster", /* keywords */
+           "target=raster", /* requirements */
+           SIZE, SIZE,
+           NULL, reference)
+CAIRO_TEST (partial_coverage_three_quarter_reference,
+           "Check the fidelity of this test.",
+           "coverage raster", /* keywords */
+           "target=raster", /* requirements */
+           SIZE, SIZE,
+           NULL, three_quarter_reference)
+CAIRO_TEST (partial_coverage_half_reference,
+           "Check the fidelity of this test.",
+           "coverage raster", /* keywords */
+           "target=raster", /* requirements */
+           SIZE, SIZE,
+           NULL, half_reference)
diff --git a/test/pass-through.c b/test/pass-through.c
new file mode 100644 (file)
index 0000000..96f91ba
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2008 Chris Wilson
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Chris Wilson not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Chris Wilson makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * CHRIS WILSON DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL CHRIS WILSON BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+
+static cairo_test_draw_function_t draw;
+
+static const cairo_test_t test = {
+    "pass-through",
+    "Tests pixel values",
+    16, 256,
+    draw
+};
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    int n;
+
+    for (n = 0; n < 256; n++) {
+       cairo_set_source_rgba (cr, 1, 1, 1, n / 255.);
+       cairo_rectangle (cr, 0, n, 2, 1);
+       cairo_fill (cr);
+    }
+    for (n = 0; n < 256; n++) {
+       cairo_set_source_rgb (cr, n / 255., n / 255., n / 255.);
+       cairo_rectangle (cr, 2, n, 2, 1);
+       cairo_fill (cr);
+    }
+
+    cairo_translate (cr, 4, 0);
+
+    for (n = 0; n < 256; n++) {
+       cairo_set_source_rgba (cr, 1, 0, 0, n / 255.);
+       cairo_rectangle (cr, 0, n, 2, 1);
+       cairo_fill (cr);
+    }
+    for (n = 0; n < 256; n++) {
+       cairo_set_source_rgb (cr, n / 255., 0, 0);
+       cairo_rectangle (cr, 2, n, 2, 1);
+       cairo_fill (cr);
+    }
+
+    cairo_translate (cr, 4, 0);
+
+    for (n = 0; n < 256; n++) {
+       cairo_set_source_rgba (cr, 0, 1, 0, n / 255.);
+       cairo_rectangle (cr, 0, n, 2, 1);
+       cairo_fill (cr);
+    }
+    for (n = 0; n < 256; n++) {
+       cairo_set_source_rgb (cr, 0, n / 255., 0);
+       cairo_rectangle (cr, 2, n, 2, 1);
+       cairo_fill (cr);
+    }
+
+    cairo_translate (cr, 4, 0);
+
+    for (n = 0; n < 256; n++) {
+       cairo_set_source_rgba (cr, 0, 0, 1, n / 255.);
+       cairo_rectangle (cr, 0, n, 2, 1);
+       cairo_fill (cr);
+    }
+    for (n = 0; n < 256; n++) {
+       cairo_set_source_rgb (cr, 0, 0, n / 255.);
+       cairo_rectangle (cr, 2, n, 2, 1);
+       cairo_fill (cr);
+    }
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+int
+main (void)
+{
+    return cairo_test (&test);
+}
diff --git a/test/path-append.c b/test/path-append.c
new file mode 100644 (file)
index 0000000..bcd282d
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * Copyright © 2009 Jeff Muizelaar
+ * Copyright © 2009 Chris Wilson
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting documentation, and
+ * that the name of the copyright holders not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission.  The copyright holders make no representations
+ * about the suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ */
+
+#include <stdlib.h>
+#include "cairo-test.h"
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_matrix_t m;
+    int xoffset = 50;
+    int yoffset = 50;
+
+    cairo_surface_t *shadow;
+    cairo_t *shadow_cr;
+    cairo_path_t *path;
+
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_paint (cr);
+
+    cairo_translate (cr, 130, 130);
+    cairo_rotate (cr, .5);//2*M_PI*angle/360);
+    cairo_rectangle (cr, 0, 0, 50, 100);
+    cairo_get_matrix (cr, &m);
+
+    shadow = cairo_surface_create_similar (cairo_get_target (cr),
+                                          CAIRO_CONTENT_COLOR_ALPHA,
+                                          600 - xoffset,
+                                          600 - yoffset);
+    cairo_surface_set_device_offset (shadow, xoffset, yoffset);
+    shadow_cr = cairo_create (shadow);
+    cairo_surface_destroy (shadow);
+
+    cairo_set_source_rgb (shadow_cr, 0, 1, 0);
+    cairo_set_matrix (shadow_cr, &m);
+
+    path = cairo_copy_path (cr);
+    cairo_new_path (shadow_cr);
+    cairo_append_path (shadow_cr, path);
+    cairo_fill (shadow_cr);
+    cairo_path_destroy (path);
+
+    cairo_identity_matrix (cr);
+    cairo_translate (cr, 10, 50);
+    cairo_set_source_surface (cr, cairo_get_target (shadow_cr), 0, 0);
+    cairo_paint (cr);
+    cairo_set_matrix (cr, &m);
+    cairo_set_source_rgb (cr, 1, 0, 0);
+    cairo_fill (cr);
+
+    cairo_destroy (shadow_cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (path_append,
+           "Test appending path to a context, in particular to exercise a regression in 005436",
+           "path", /* keywords */
+           NULL, /* requirements */
+           600, 600,
+           NULL, draw)
diff --git a/test/path-precision.c b/test/path-precision.c
new file mode 100644 (file)
index 0000000..3a7fb11
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * Copyright © 2008 Chris Wilson
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Chris Wilson not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Chris Wilson makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * CHRIS WILSON DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL CHRIS WILSON BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ *
+ * Based on an example by Dirk "krit" Schulze found during WebKit integration.
+ */
+
+#include "cairo-test.h"
+
+/* we know that this is an inherent limitation in cairo */
+#define FAIL CAIRO_TEST_XFAILURE
+
+/* Test the idempotency of path construction and copying */
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_path_data_t path_data[] = {
+       { { CAIRO_PATH_MOVE_TO, 2 }, },
+       { { 95.000000, 40.000000 }, },
+
+       { { CAIRO_PATH_LINE_TO, 2 }, },
+       { { 94.960533, 41.255810 }, },
+
+       { { CAIRO_PATH_LINE_TO, 2 }, },
+       { { 94.842293, 42.50666 }, },
+
+       { { CAIRO_PATH_LINE_TO, 2 }, },
+       { { 94.645744, 43.747627 }, },
+
+       { { CAIRO_PATH_LINE_TO, 2 }, },
+       { { 94.371666, 44.973797 }, },
+    };
+    const cairo_test_context_t *ctx = cairo_test_get_context (cr);
+    cairo_path_t path, *path_copy;
+    int i, j, n;
+    cairo_test_status_t result = CAIRO_TEST_SUCCESS;
+
+    path.status = CAIRO_STATUS_SUCCESS;
+    path.num_data = ARRAY_LENGTH (path_data);
+    path.data = path_data;
+
+    cairo_new_path (cr);
+    cairo_append_path (cr, &path);
+    path_copy = cairo_copy_path (cr);
+
+    if (path_copy->status)
+       return cairo_test_status_from_status (ctx, path_copy->status);
+
+    for (i = j = n = 0;
+        i < path.num_data && j < path_copy->num_data;
+        i += path.data[i].header.length,
+        j += path_copy->data[j].header.length,
+        n++)
+    {
+       const cairo_path_data_t *src, *dst;
+
+       src = &path.data[i];
+       dst = &path_copy->data[j];
+
+       if (src->header.type != dst->header.type) {
+           cairo_test_log (ctx,
+                           "Paths differ in header type after %d operations.\n"
+                           "Expected path operation %d, found %d.\n",
+                           n, src->header.type, dst->header.type);
+           result = FAIL;
+           break;
+       }
+
+       if (memcmp (&src[1].point, &dst[1].point, sizeof (src->point))) {
+           cairo_test_log (ctx,
+                           "Paths differ in coordinates after %d operations.\n"
+                           "Expected point (%f, %f), found (%f, %f).\n",
+                           n,
+                           src[1].point.x, src[1].point.y,
+                           dst[1].point.x, dst[1].point.y);
+           result = FAIL;
+           break;
+       }
+    }
+
+    cairo_path_destroy (path_copy);
+    return result;
+}
+
+CAIRO_TEST (path_precision,
+           "Check that the path append/copy is idempotent.",
+           "api", /* keywords */
+           NULL, /* requirements */
+           0, 0,
+           NULL, draw)
diff --git a/test/path-stroke-twice.c b/test/path-stroke-twice.c
new file mode 100644 (file)
index 0000000..10bbbf8
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2010 Chris Wilson
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Chris Wilson not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Chris Wilson makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * CHRIS WILSON DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL CHRIS WILSON BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+
+/* Exercises a bug found by alois@astro.ch:
+ *   http://bugs.freedesktop.org/show_bug.cgi?id=26010
+ *   cairo_line_to optimizes away path segment
+ */
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_paint (cr);
+
+    cairo_set_source_rgb (cr, 0, 0, 0);
+    cairo_move_to (cr, 10, 10);
+    cairo_line_to (cr, 10, 20);
+    cairo_line_to (cr, 20, 30);
+    cairo_line_to (cr, 10, 20);
+    cairo_stroke (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (path_stroke_twice,
+           "Tests stroking of a path containing a segment drawn twice",
+           "path, stroke", /* keywords */
+           NULL, /* requirements */
+           40, 40,
+           NULL, draw)
diff --git a/test/pattern-get-type.c b/test/pattern-get-type.c
new file mode 100644 (file)
index 0000000..c807b14
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * Copyright © 2006 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ */
+
+#include "cairo-test.h"
+
+static cairo_test_status_t
+preamble (cairo_test_context_t *Ctx)
+{
+    cairo_surface_t *surface;
+    cairo_pattern_t *solid_rgb, *solid_rgba, *surface_pattern, *linear, *radial, *mesh;
+    cairo_test_status_t result = CAIRO_TEST_SUCCESS;
+
+    solid_rgb = cairo_pattern_create_rgb (0.0, 0.1, 0.2);
+    solid_rgba = cairo_pattern_create_rgba (0.3, 0.4, 0.5, 0.6);
+    surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
+                                         1, 1);
+    surface_pattern = cairo_pattern_create_for_surface (surface);
+    linear = cairo_pattern_create_linear (0.0, 0.0, 10.0, 10.0);
+    radial = cairo_pattern_create_radial (10.0, 10.0, 0.1,
+                                         10.0, 10.0, 1.0);
+    mesh = cairo_pattern_create_mesh ();
+
+    if (cairo_pattern_get_type (solid_rgb) != CAIRO_PATTERN_TYPE_SOLID)
+       result = CAIRO_TEST_FAILURE;
+
+    if (cairo_pattern_get_type (solid_rgba) != CAIRO_PATTERN_TYPE_SOLID)
+       result = CAIRO_TEST_FAILURE;
+
+    if (cairo_pattern_get_type (surface_pattern) != CAIRO_PATTERN_TYPE_SURFACE)
+       result = CAIRO_TEST_FAILURE;
+
+    if (cairo_pattern_get_type (linear) != CAIRO_PATTERN_TYPE_LINEAR)
+       result = CAIRO_TEST_FAILURE;
+
+    if (cairo_pattern_get_type (radial) != CAIRO_PATTERN_TYPE_RADIAL)
+       result = CAIRO_TEST_FAILURE;
+
+    if (cairo_pattern_get_type (mesh) != CAIRO_PATTERN_TYPE_MESH)
+       result = CAIRO_TEST_FAILURE;
+
+    cairo_pattern_destroy (solid_rgb);
+    cairo_pattern_destroy (solid_rgba);
+    cairo_pattern_destroy (surface_pattern);
+    cairo_surface_destroy (surface);
+    cairo_pattern_destroy (linear);
+    cairo_pattern_destroy (radial);
+    cairo_pattern_destroy (mesh);
+
+    return result;
+}
+
+CAIRO_TEST (pattern_get_type,
+           "Creating patterns of all types",
+           "pattern, api", /* keywords */
+           NULL, /* requirements */
+           0, 0,
+           preamble, NULL)
diff --git a/test/pattern-getters.c b/test/pattern-getters.c
new file mode 100644 (file)
index 0000000..28bc1f9
--- /dev/null
@@ -0,0 +1,279 @@
+/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/*
+ * Copyright © 2005 Mozilla Corporation, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Mozilla Corporation not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Mozilla Corporation makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * MOZILLA CORPORATION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL MOZILLA CORPORATION BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Vladimir Vukicevic <vladimir@pobox.com>
+ */
+
+#include <stdlib.h>
+#include "cairo-test.h"
+
+#define CHECK_SUCCESS do { \
+    if (status) { \
+       cairo_pattern_destroy (pat); \
+       return cairo_test_status_from_status (ctx, status); \
+    } \
+} while (0)
+
+static int
+double_buf_equal (const cairo_test_context_t *ctx, double *a, double *b, int nc)
+{
+    int i;
+    for (i = 0; i < nc; i++) {
+       if (!CAIRO_TEST_DOUBLE_EQUALS(a[i],b[i])) {
+           cairo_test_log (ctx, "Error: doubles not equal: %g, %g\n",
+                           a[i], b[i]);
+           return 0;
+       }
+    }
+    return 1;
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    const cairo_test_context_t *ctx = cairo_test_get_context (cr);
+    cairo_status_t status;
+    cairo_pattern_t *pat;
+
+    /* Test pattern_get_rgba */
+    {
+       double r, g, b, a;
+       pat = cairo_pattern_create_rgba (0.2, 0.3, 0.4, 0.5);
+
+       status = cairo_pattern_get_rgba (pat, &r, &g, &b, &a);
+       CHECK_SUCCESS;
+
+       if (!CAIRO_TEST_DOUBLE_EQUALS(r,0.2) ||
+           !CAIRO_TEST_DOUBLE_EQUALS(g,0.3) ||
+           !CAIRO_TEST_DOUBLE_EQUALS(b,0.4) ||
+           !CAIRO_TEST_DOUBLE_EQUALS(a,0.5)) {
+           cairo_test_log (ctx, "Error: cairo_pattern_get_rgba returned unexepcted results: %g, %g, %g, %g\n",
+                           r, g, b, a);
+           cairo_pattern_destroy (pat);
+           return CAIRO_TEST_FAILURE;
+       }
+
+       cairo_pattern_destroy (pat);
+    }
+
+    /* Test pattern_get_surface */
+    {
+       cairo_surface_t *surf;
+
+       pat = cairo_pattern_create_for_surface (cairo_get_target (cr));
+
+       status = cairo_pattern_get_surface (pat, &surf);
+       CHECK_SUCCESS;
+
+       if (surf != cairo_get_target (cr)) {
+           cairo_test_log (ctx, "Error: cairo_pattern_get_resurface returned wrong surface\n");
+           cairo_pattern_destroy (pat);
+           return CAIRO_TEST_FAILURE;
+       }
+
+       cairo_pattern_destroy (pat);
+    }
+
+    /* Test get_color_stops & linear_get_points */
+    {
+       int i;
+       double x0, y0, x1, y1;
+       double expected_values[15] = { 0.0, 0.2, 0.4, 0.2, 1.0,
+                                      0.5, 0.4, 0.5, 0.2, 0.5,
+                                      1.0, 0.2, 0.4, 0.5, 0.2 };
+       double new_buf[15];
+
+       pat = cairo_pattern_create_linear (1.0, 2.0, 3.0, 4.0);
+
+       for (i = 0; i < 3; i++) {
+           cairo_pattern_add_color_stop_rgba (pat,
+                                              expected_values[i*5+0],
+                                              expected_values[i*5+1],
+                                              expected_values[i*5+2],
+                                              expected_values[i*5+3],
+                                              expected_values[i*5+4]);
+       }
+
+       status = cairo_pattern_get_linear_points (pat, &x0, &y0, &x1, &y1);
+       CHECK_SUCCESS;
+
+       if (!CAIRO_TEST_DOUBLE_EQUALS(x0,1.0) ||
+           !CAIRO_TEST_DOUBLE_EQUALS(y0,2.0) ||
+           !CAIRO_TEST_DOUBLE_EQUALS(x1,3.0) ||
+           !CAIRO_TEST_DOUBLE_EQUALS(y1,4.0))
+       {
+           cairo_pattern_destroy (pat);
+           return CAIRO_TEST_FAILURE;
+       }
+
+       status = cairo_pattern_get_color_stop_count (pat, &i);
+       CHECK_SUCCESS;
+
+       if (i != 3) {
+           cairo_pattern_destroy (pat);
+           return CAIRO_TEST_FAILURE;
+       }
+
+       for (i = 0; i < 3; i++) {
+           status = cairo_pattern_get_color_stop_rgba (pat, i,
+                                                       &new_buf[i*5+0],
+                                                       &new_buf[i*5+1],
+                                                       &new_buf[i*5+2],
+                                                       &new_buf[i*5+3],
+                                                       &new_buf[i*5+4]);
+           CHECK_SUCCESS;
+       }
+
+       status = cairo_pattern_get_color_stop_rgba (pat, 5, NULL, NULL, NULL, NULL, NULL);
+       if (status != CAIRO_STATUS_INVALID_INDEX) {
+           cairo_pattern_destroy (pat);
+           return CAIRO_TEST_FAILURE;
+       }
+
+       if (!double_buf_equal (ctx, new_buf, expected_values,
+                              ARRAY_LENGTH (expected_values)) != 0)
+       {
+           cairo_pattern_destroy (pat);
+           return CAIRO_TEST_FAILURE;
+       }
+
+       cairo_pattern_destroy (pat);
+    }
+
+    /* Test radial_get_circles */
+    {
+       double a, b, c, d, e, f;
+       pat = cairo_pattern_create_radial (1, 2, 3,
+                                          4, 5, 6);
+
+       status = cairo_pattern_get_radial_circles (pat, &a, &b, &c, &d, &e, &f);
+       CHECK_SUCCESS;
+
+       if (!CAIRO_TEST_DOUBLE_EQUALS(a,1.0) ||
+           !CAIRO_TEST_DOUBLE_EQUALS(b,2.0) ||
+           !CAIRO_TEST_DOUBLE_EQUALS(c,3.0) ||
+           !CAIRO_TEST_DOUBLE_EQUALS(d,4.0) ||
+           !CAIRO_TEST_DOUBLE_EQUALS(e,5.0) ||
+           !CAIRO_TEST_DOUBLE_EQUALS(f,6.0))
+       {
+           cairo_pattern_destroy (pat);
+           return CAIRO_TEST_FAILURE;
+       }
+
+       cairo_pattern_destroy (pat);
+    }
+
+    /* Test mesh getters */
+    {
+       unsigned int count;
+       int i;
+       pat = cairo_pattern_create_mesh ();
+
+       status = cairo_mesh_pattern_get_patch_count (pat, &count);
+       CHECK_SUCCESS;
+
+       if (count != 0) {
+           cairo_pattern_destroy (pat);
+           return CAIRO_TEST_FAILURE;
+       }
+
+       cairo_mesh_pattern_begin_patch (pat);
+       cairo_mesh_pattern_move_to (pat, 0, 0);
+       cairo_mesh_pattern_line_to (pat, 0, 3);
+       cairo_mesh_pattern_line_to (pat, 3, 3);
+       cairo_mesh_pattern_line_to (pat, 3, 0);
+
+       status = cairo_mesh_pattern_get_patch_count (pat, &count);
+       CHECK_SUCCESS;
+
+       if (count != 0) {
+           cairo_pattern_destroy (pat);
+           return CAIRO_TEST_FAILURE;
+       }
+
+       cairo_mesh_pattern_end_patch (pat);
+
+       status = cairo_mesh_pattern_get_patch_count (pat, &count);
+       CHECK_SUCCESS;
+
+       if (count != 1) {
+           cairo_pattern_destroy (pat);
+           return CAIRO_TEST_FAILURE;
+       }
+
+       for (i = 0; i < 4; i++) {
+           double cp_x[4] = { 1, 1, 2, 2 };
+           double cp_y[4] = { 1, 2, 2, 1 };
+           double x, y;
+
+           status = cairo_mesh_pattern_get_control_point (pat, 0, i, &x, &y);
+           CHECK_SUCCESS;
+
+           if (!CAIRO_TEST_DOUBLE_EQUALS(x,cp_x[i]) ||
+               !CAIRO_TEST_DOUBLE_EQUALS(y,cp_y[i]))
+           {
+               cairo_pattern_destroy (pat);
+               return CAIRO_TEST_FAILURE;
+           }
+       }
+
+       cairo_mesh_pattern_begin_patch (pat);
+       cairo_mesh_pattern_move_to (pat, 0, 0);
+       cairo_mesh_pattern_line_to (pat, 1, 0);
+       cairo_mesh_pattern_line_to (pat, 1, 1);
+       cairo_mesh_pattern_set_corner_color_rgb (pat, 0, 1, 1, 1);
+       cairo_mesh_pattern_end_patch (pat);
+
+       for (i = 0; i < 4; i++) {
+           double corner_color[4] = { 1, 0, 0, 1 };
+           double a, r, g, b;
+
+           status = cairo_mesh_pattern_get_corner_color_rgba (pat, 1, i,
+                                                              &r, &g, &b, &a);
+           CHECK_SUCCESS;
+
+           if (!CAIRO_TEST_DOUBLE_EQUALS(a,corner_color[i]) ||
+               !CAIRO_TEST_DOUBLE_EQUALS(r,corner_color[i]) ||
+               !CAIRO_TEST_DOUBLE_EQUALS(g,corner_color[i]) ||
+               !CAIRO_TEST_DOUBLE_EQUALS(b,corner_color[i]))
+           {
+               cairo_pattern_destroy (pat);
+               return CAIRO_TEST_FAILURE;
+           }
+       }
+
+       cairo_pattern_destroy (pat);
+    }
+
+    cairo_set_source_rgb (cr, 0, 1, 0);
+    cairo_paint (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (pattern_getters,
+           "Tests calls to pattern getter functions",
+           "pattern, api", /* keywords */
+           NULL, /* requirements */
+           1, 1,
+           NULL, draw)
diff --git a/test/pdf-features.c b/test/pdf-features.c
new file mode 100644 (file)
index 0000000..f8850c4
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+ * Copyright © 2006 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ */
+
+#include <stdio.h>
+#include <cairo.h>
+#include <cairo-pdf.h>
+
+#include "cairo-test.h"
+
+/* This test exists to test the various features of cairo-pdf.h.
+ *
+ * Currently, this test exercises the following function calls:
+ *
+ *     cairo_pdf_surface_set_size
+ */
+
+#define INCHES_TO_POINTS(in) ((in) * 72.0)
+#define MM_TO_POINTS(mm) ((mm) / 25.4 * 72.0)
+#define TEXT_SIZE 12
+
+static struct {
+    const char *page_size;
+    const char *page_size_alias;
+    const char *orientation;
+    double width_in_points;
+    double height_in_points;
+} pages[] = {
+    {"na_letter_8.5x11in", "letter", "portrait",
+     INCHES_TO_POINTS(8.5), INCHES_TO_POINTS(11)},
+    {"na_letter_8.5x11in", "letter", "landscape",
+     INCHES_TO_POINTS(11), INCHES_TO_POINTS(8.5)},
+    {"iso_a4_210x297mm", "a4", "portrait",
+     MM_TO_POINTS(210), MM_TO_POINTS(297)},
+    {"iso_a4_210x297mm", "a4", "landscape",
+     MM_TO_POINTS(297), MM_TO_POINTS(210)},
+    {"iso_a5_148x210mm", "a5", "portrait",
+     MM_TO_POINTS(148), MM_TO_POINTS(210)},
+    {"iso_a5_148x210mm", "a5", "landscape",
+     MM_TO_POINTS(210), MM_TO_POINTS(148)},
+    {"iso_a6_105x148mm", "a6", "portrait",
+     MM_TO_POINTS(105), MM_TO_POINTS(148)},
+    {"iso_a6_105x148mm", "a6", "landscape",
+     MM_TO_POINTS(148), MM_TO_POINTS(105)},
+    {"iso_a7_74x105mm", "a7", "portrait",
+     MM_TO_POINTS(74), MM_TO_POINTS(105)},
+    {"iso_a7_74x105mm", "a7", "landscape",
+     MM_TO_POINTS(105), MM_TO_POINTS(74)},
+    {"iso_a8_52x74mm", "a8", "portrait",
+     MM_TO_POINTS(52), MM_TO_POINTS(74)},
+    {"iso_a8_52x74mm", "a8", "landscape",
+     MM_TO_POINTS(74), MM_TO_POINTS(52)},
+    {"iso_a9_37x52mm", "a9", "portrait",
+     MM_TO_POINTS(37), MM_TO_POINTS(52)},
+    {"iso_a9_37x52mm", "a9", "landscape",
+     MM_TO_POINTS(52), MM_TO_POINTS(37)},
+    {"iso_a10_26x37mm", "a10", "portrait",
+     MM_TO_POINTS(26), MM_TO_POINTS(37)},
+    {"iso_a10_26x37mm", "a10", "landscape",
+     MM_TO_POINTS(37), MM_TO_POINTS(26)}
+};
+
+static cairo_test_status_t
+preamble (cairo_test_context_t *ctx)
+{
+    const char *filename = "pdf-features.out.pdf";
+    cairo_surface_t *surface;
+    cairo_t *cr;
+    cairo_status_t status;
+    size_t i;
+
+    if (! cairo_test_is_target_enabled (ctx, "pdf"))
+       return CAIRO_TEST_UNTESTED;
+
+    /* The initial size passed here is the default size that will be
+     * inheritable by each page. That is, any page for which this
+     * initial size applies will not have its own /MediaBox entry in
+     * its dictionary. */
+    surface = cairo_pdf_surface_create (filename,
+                                       INCHES_TO_POINTS(8.5),
+                                       INCHES_TO_POINTS(11));
+
+    cr = cairo_create (surface);
+
+    cairo_select_font_face (cr, CAIRO_TEST_FONT_FAMILY " Sans",
+                           CAIRO_FONT_SLANT_NORMAL,
+                           CAIRO_FONT_WEIGHT_NORMAL);
+    cairo_set_font_size (cr, TEXT_SIZE);
+
+    for (i = 0; i < ARRAY_LENGTH (pages); i++) {
+       cairo_pdf_surface_set_size (surface,
+                                  pages[i].width_in_points,
+                                  pages[i].height_in_points);
+
+       cairo_move_to (cr, TEXT_SIZE, TEXT_SIZE);
+       cairo_show_text (cr, pages[i].page_size);
+       cairo_show_text (cr, " - ");
+       cairo_show_text (cr, pages[i].orientation);
+       cairo_show_page (cr);
+    }
+
+    status = cairo_status (cr);
+
+    cairo_destroy (cr);
+    cairo_surface_destroy (surface);
+
+    if (status) {
+       cairo_test_log (ctx, "Failed to create pdf surface for file %s: %s\n",
+                       filename, cairo_status_to_string (status));
+       return CAIRO_TEST_FAILURE;
+    }
+
+    printf ("pdf-features: Please check %s to ensure it looks/prints correctly.\n", filename);
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (pdf_features,
+           "Check PDF specific API",
+           "pdf", /* keywords */
+           NULL, /* requirements */
+           0, 0,
+           preamble, NULL)
diff --git a/test/pdf-mime-data.c b/test/pdf-mime-data.c
new file mode 100644 (file)
index 0000000..00cde9d
--- /dev/null
@@ -0,0 +1,177 @@
+/*
+ * Copyright © 2008 Adrian Johnson
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Adrian Johnson <ajohnson@redneon.com>
+ */
+
+#include "cairo-test.h"
+
+#include <stdio.h>
+#include <errno.h>
+#include <cairo.h>
+#include <cairo-pdf.h>
+
+/* This test checks that the mime data is correctly used by the PDF
+ * surface when embedding images..
+ */
+
+/* Both a *.png and *.jpg file with this name is required since we
+ * are not using a jpeg library */
+#define IMAGE_FILE "romedalen"
+
+
+static cairo_test_status_t
+read_file (const cairo_test_context_t *ctx,
+          const char *file,
+          unsigned char **data,
+          unsigned int *len)
+{
+    FILE *fp;
+
+    fp = fopen (file, "rb");
+    if (file == NULL) {
+       char filename[4096];
+
+       /* try again with srcdir */
+       snprintf (filename, sizeof (filename),
+                 "%s/%s", ctx->srcdir, file);
+       fp = fopen (filename, "rb");
+    }
+    if (fp == NULL) {
+       switch (errno) {
+       case ENOMEM:
+           return CAIRO_TEST_NO_MEMORY;
+       default:
+           return CAIRO_TEST_FAILURE;
+       }
+    }
+
+    fseek (fp, 0, SEEK_END);
+    *len = ftell(fp);
+    fseek (fp, 0, SEEK_SET);
+    *data = malloc (*len);
+    if (*data == NULL)
+       return CAIRO_TEST_NO_MEMORY;
+
+    if (fread(*data, *len, 1, fp) != 1)
+       return CAIRO_TEST_FAILURE;
+
+    fclose(fp);
+    return CAIRO_TEST_SUCCESS;
+}
+
+static cairo_test_status_t
+preamble (cairo_test_context_t *ctx)
+{
+    const char *filename = "pdf-mime-data.out.pdf";
+    cairo_surface_t *image;
+    cairo_surface_t *surface;
+    cairo_t *cr;
+    cairo_status_t status, status2;
+    cairo_test_status_t test_status;
+    int width, height;
+    unsigned char *data;
+    unsigned char *out_data;
+    unsigned int len, out_len;
+    char command[4096];
+    int exit_status;
+
+    if (! cairo_test_is_target_enabled (ctx, "pdf"))
+       return CAIRO_TEST_UNTESTED;
+
+    image = cairo_image_surface_create_from_png (IMAGE_FILE ".png");
+    test_status = read_file (ctx, IMAGE_FILE ".jpg", &data, &len);
+    if (test_status) {
+       cairo_test_log (ctx, "Could not read input jpeg file %s\n", IMAGE_FILE ".jpg");
+       return test_status;
+    }
+
+    cairo_surface_set_mime_data (image, CAIRO_MIME_TYPE_JPEG,
+                                data, len,
+                                free, data);
+    width = cairo_image_surface_get_width (image);
+    height = cairo_image_surface_get_height (image);
+
+    surface = cairo_pdf_surface_create (filename, width + 20, height + 20);
+    cr = cairo_create (surface);
+    cairo_translate (cr, 10, 10);
+    cairo_set_source_surface (cr, image, 0, 0);
+    cairo_paint (cr);
+    status = cairo_status (cr);
+    cairo_destroy (cr);
+    cairo_surface_finish (surface);
+    status2 = cairo_surface_status (surface);
+    if (status != CAIRO_STATUS_SUCCESS)
+       status = status2;
+    cairo_surface_destroy (surface);
+    cairo_surface_destroy (image);
+
+    if (status) {
+       cairo_test_log (ctx, "Failed to create pdf surface for file %s: %s\n",
+                       filename, cairo_status_to_string (status));
+       return CAIRO_TEST_FAILURE;
+    }
+
+    printf ("pdf-mime-data: Please check %s to ensure it looks/prints correctly.\n", filename);
+
+    sprintf (command, "pdfimages -j %s pdf-mime-data.out", filename);
+    exit_status = system (command);
+    if (exit_status) {
+       cairo_test_log (ctx, "pdfimages failed with exit status %d\n", exit_status);
+       return CAIRO_TEST_FAILURE;
+    }
+
+    test_status = read_file (ctx, IMAGE_FILE ".jpg", &data, &len);
+    if (test_status) {
+       cairo_test_log (ctx, "Could not read input jpeg file %s\n", IMAGE_FILE ".jpg");
+       return test_status;
+    }
+
+    test_status = read_file (ctx, "pdf-mime-data.out-000.jpg", &out_data, &out_len);
+    if (test_status) {
+       free (data);
+       cairo_test_log (ctx,
+                       "Could not read input jpeg file %s\n",
+                       "pdf-mime-data.out-000.jpg");
+       return test_status;
+    }
+
+    if (len != out_len || memcmp(data, out_data, len) != 0) {
+       free (data);
+       free (out_data);
+       cairo_test_log (ctx, "output mime data does not match source mime data\n");
+       return CAIRO_TEST_FAILURE;
+    }
+
+    free (data);
+    free (out_data);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (pdf_mime_data,
+           "Check mime data correctly used by PDF surface",
+           "pdf, mime-data", /* keywords */
+           NULL, /* requirements */
+           0, 0,
+           preamble, NULL)
diff --git a/test/pdf-surface-source.c b/test/pdf-surface-source.c
new file mode 100644 (file)
index 0000000..078af3a
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Copyright © 2008 Chris Wilson
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Chris Wilson not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Chris Wilson makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * CHRIS WILSON DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL CHRIS WILSON BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+#include <cairo-pdf.h>
+
+#include "surface-source.c"
+
+static cairo_surface_t *
+create_source_surface (int size)
+{
+    cairo_surface_t *surface;
+
+    surface = cairo_pdf_surface_create ("pdf-surface-source.out.pdf", size, size);
+    cairo_surface_set_fallback_resolution (surface, 72., 72.);
+
+    return surface;
+}
+
+CAIRO_TEST (pdf_surface_source,
+           "Test using a PDF surface as the source",
+           "source", /* keywords */
+           NULL, /* requirements */
+           SIZE, SIZE,
+           preamble, draw)
diff --git a/test/pdf2png.c b/test/pdf2png.c
new file mode 100644 (file)
index 0000000..06fa05b
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Kristian Høgsberg <krh@redhat.com>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <poppler.h>
+
+#define FAIL(msg)                                                      \
+    do { fprintf (stderr, "FAIL: %s\n", msg); exit (-1); } while (0)
+
+#define PIXELS_PER_POINT 1
+
+int main (int argc, char *argv[])
+{
+    PopplerDocument *document;
+    PopplerPage *page;
+    double width, height;
+    const char *filename = argv[1];
+    const char *output_filename = argv[2];
+    const char *page_label = argv[3];
+    gchar *absolute, *uri;
+    cairo_surface_t *surface;
+    cairo_t *cr;
+    cairo_status_t status;
+    GError *error = NULL;
+
+    if (argc != 4)
+       FAIL ("usage: pdf2png input_file.pdf output_file.png page");
+
+    g_type_init ();
+
+    if (g_path_is_absolute(filename)) {
+       absolute = g_strdup (filename);
+    } else {
+       gchar *dir = g_get_current_dir ();
+       absolute = g_build_filename (dir, filename, (gchar *) 0);
+       g_free (dir);
+    }
+
+    uri = g_filename_to_uri (absolute, NULL, &error);
+    g_free (absolute);
+    if (uri == NULL)
+       FAIL (error->message);
+
+    document = poppler_document_new_from_file (uri, NULL, &error);
+    if (document == NULL)
+       FAIL (error->message);
+
+    page = poppler_document_get_page_by_label (document, page_label);
+    if (page == NULL)
+       FAIL ("page not found");
+
+    poppler_page_get_size (page, &width, &height);
+
+    surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, height);
+    cr = cairo_create (surface);
+    cairo_surface_destroy (surface);
+
+    cairo_set_source_rgb (cr, 1,1,1);
+    cairo_paint (cr);
+    cairo_push_group_with_content (cr, CAIRO_CONTENT_COLOR_ALPHA);
+
+    poppler_page_render (page, cr);
+    g_object_unref (page);
+
+    cairo_pop_group_to_source (cr);
+    cairo_paint (cr);
+
+    status = cairo_surface_write_to_png (cairo_get_target (cr),
+                                        output_filename);
+    cairo_destroy (cr);
+
+    if (status)
+       FAIL (cairo_status_to_string (status));
+
+    return 0;
+}
diff --git a/test/pdiff/.gitignore b/test/pdiff/.gitignore
new file mode 100644 (file)
index 0000000..f44ed65
--- /dev/null
@@ -0,0 +1,3 @@
+TAGS
+tags
+perceptualdiff
diff --git a/test/pdiff/CMakeLists.txt b/test/pdiff/CMakeLists.txt
new file mode 100644 (file)
index 0000000..6e4fa7a
--- /dev/null
@@ -0,0 +1,55 @@
+PROJECT (PerceptualDiff)
+SET(DIFF_SRC PerceptualDiff.cpp LPyramid.cpp RGBAImage.cpp
+CompareArgs.cpp Metric.cpp)
+
+ADD_EXECUTABLE (perceptualdiff ${DIFF_SRC})
+
+# look for libtiff
+FIND_PATH(TIFF_INCLUDE_DIR tiff.h
+  /usr/local/include
+  /usr/include
+  /opt/local/include
+)
+
+FIND_LIBRARY(TIFF_LIBRARY tiff
+  /usr/lib
+  /usr/local/lib
+  /opt/local/lib
+)
+
+IF(TIFF_INCLUDE_DIR)
+  IF(TIFF_LIBRARY)
+    SET( TIFF_FOUND "YES" )
+    SET( TIFF_LIBRARIES ${TIFF_LIBRARY} )
+  ENDIF(TIFF_LIBRARY)
+ENDIF(TIFF_INCLUDE_DIR)
+
+IF(TIFF_FOUND)
+  INCLUDE_DIRECTORIES(${TIFF_INCLUDE_DIR})
+  TARGET_LINK_LIBRARIES(perceptualdiff ${TIFF_LIBRARY})
+ENDIF(TIFF_FOUND)
+
+# look for libpng
+FIND_PATH(PNG_INCLUDE_DIR png.h
+  /usr/local/include
+  /usr/include
+  /opt/local/include
+)
+
+FIND_LIBRARY(PNG_LIBRARY png
+  /usr/lib
+  /usr/local/lib
+  /opt/local/lib
+)
+
+IF(PNG_INCLUDE_DIR)
+  IF(PNG_LIBRARY)
+    SET( PNG_FOUND "YES" )
+    SET( PNG_LIBRARIES ${PNG_LIBRARY} )
+  ENDIF(PNG_LIBRARY)
+ENDIF(PNG_INCLUDE_DIR)
+
+IF(PNG_FOUND)
+  INCLUDE_DIRECTORIES(${PNG_INCLUDE_DIR})
+  TARGET_LINK_LIBRARIES(perceptualdiff ${PNG_LIBRARY})
+ENDIF(PNG_FOUND)
\ No newline at end of file
diff --git a/test/pdiff/Makefile.am b/test/pdiff/Makefile.am
new file mode 100644 (file)
index 0000000..73098da
--- /dev/null
@@ -0,0 +1,19 @@
+include $(top_srcdir)/build/Makefile.am.common
+
+EXTRA_PROGRAMS += perceptualdiff
+EXTRA_DIST += gpl.txt
+
+noinst_LTLIBRARIES = libpdiff.la
+libpdiff_la_SOURCES =          \
+       pdiff.h                 \
+       lpyramid.c              \
+       lpyramid.h              \
+       pdiff.c
+
+perceptualdiff_SOURCES =       \
+       args.c                  \
+       args.h                  \
+       perceptualdiff.c
+
+AM_CPPFLAGS = -I$(top_srcdir)/src -I$(top_builddir)/src $(CAIRO_CFLAGS)
+LDADD = libpdiff.la $(top_builddir)/src/libcairo.la
diff --git a/test/pdiff/Makefile.win32 b/test/pdiff/Makefile.win32
new file mode 100644 (file)
index 0000000..3d64676
--- /dev/null
@@ -0,0 +1,14 @@
+top_srcdir = ../..
+include $(top_srcdir)/build/Makefile.win32.common
+
+SOURCES = \
+       lpyramid.c \
+       pdiff.c \
+       $(NULL)
+
+OBJECTS = $(patsubst %.c, $(CFG)/%-static.obj, $(SOURCES))
+
+all: $(CFG)/pdiff.lib
+
+$(CFG)/pdiff.lib: $(OBJECTS)
+       @$(AR) $(CAIRO_ARFLAGS) -OUT:$@ $(OBJECTS)
diff --git a/test/pdiff/README.txt b/test/pdiff/README.txt
new file mode 100644 (file)
index 0000000..922ddaf
--- /dev/null
@@ -0,0 +1,45 @@
+pdiff - a program that compares two images using
+a perceptually based image metric.
+Copyright (C) 2006 Yangli Hector Yee
+yeehector@users.sourceforge.net
+http://pdiff.sourceforge.net/
+
+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 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 in the file gpl.txt.
+
+Build Instructions
+1. Download cross platform make from http://www.cmake.org
+2. Download libtiff from http://www.libtiff.org. Download libpng from http://www.libpng.org
+3. Edit CMakeLists.txt to tell it where to find your tiff library
+4. Type cmake .
+5. Type make . (or on Windows systems cmake makes a Visual Studio
+Project file)
+6. To specify the install directory, use make install DESTDIR="/home/me/mydist"
+
+Usage
+
+pdiff image1.(tif | png) image2.(tif | png) [options]
+-verbose : Turns on verbose mode
+-fov deg: field of view, deg, in degrees. Usually between 10.0 to 85.0. 
+This controls how much of the screen the oberserver is seeing. Front row of 
+a theatre has a field of view of around 25 degrees. Back row has a field of
+ view of around 60 degrees.
+-threshold p : Sets the number of pixels, p, to reject. For example if p is
+ 100, then the test fails if 100 or more pixels are perceptably different.
+-gamma g : The gamma to use to convert to RGB linear space. Default is 2.2
+-luminance l: The luminance of the display the observer is seeing. Default
+ is 100 candela per meter squared
+
+Credits
+
+Hector Yee, project administrator and originator - hectorgon.blogspot.com
+Scott Corley, for png file IO code
+Mick Weiss, Linux build and release & QA
+Carl Worth, Rewrite as library, depend on cairo, and port to C
\ No newline at end of file
diff --git a/test/pdiff/args.c b/test/pdiff/args.c
new file mode 100644 (file)
index 0000000..ac3aa83
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+  Comapre Args
+  Copyright (C) 2006 Yangli Hector Yee
+
+  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 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, write to the Free Software Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+*/
+
+#include "args.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static const char* copyright =
+"PerceptualDiff version 1.0, Copyright (C) 2006 Yangli Hector Yee\n\
+PerceptualDiff comes with ABSOLUTELY NO WARRANTY;\n\
+This is free software, and you are welcome\n\
+to redistribute it under certain conditions;\n\
+See the GPL page for details: http://www.gnu.org/copyleft/gpl.html\n\n";
+
+static const char *usage =
+"PeceptualDiff image1.tif image2.tif\n\n\
+   Compares image1.tif and image2.tif using a perceptually based image metric\n\
+   Options:\n\
+\t-verbose       : Turns on verbose mode\n\
+\t-fov deg       : Field of view in degrees (0.1 to 89.9)\n\
+\t-threshold p  : #pixels p below which differences are ignored\n\
+\t-gamma g       : Value to convert rgb into linear space (default 2.2)\n\
+\t-luminance l   : White luminance (default 100.0 cdm^-2)\n\
+\n\
+\n Note: Input files can also be in the PNG format\
+\n";
+
+void
+args_init (args_t *args)
+{
+    args->surface_a = NULL;
+    args->surface_b = NULL;
+    args->Verbose = false;
+    args->FieldOfView = 45.0f;
+    args->Gamma = 2.2f;
+    args->ThresholdPixels = 100;
+    args->Luminance = 100.0f;
+}
+
+void
+args_fini (args_t *args)
+{
+    cairo_surface_destroy (args->surface_a);
+    cairo_surface_destroy (args->surface_b);
+}
+
+bool
+args_parse (args_t *args, int argc, char **argv)
+{
+    int i;
+    if (argc < 3) {
+       fprintf (stderr, "%s", copyright);
+       fprintf (stderr, "%s", usage);
+       return false;
+    }
+    for (i = 0; i < argc; i++) {
+       if (i == 1) {
+           args->surface_a = cairo_image_surface_create_from_png (argv[1]);
+           if (cairo_surface_status (args->surface_a))
+           {
+               fprintf (stderr, "FAIL: Cannot open %s: %s\n",
+                        argv[1], cairo_status_to_string (cairo_surface_status (args->surface_a)));
+               return false;
+           }
+       } else if (i == 2) {
+           args->surface_b = cairo_image_surface_create_from_png (argv[2]);
+           if (cairo_surface_status (args->surface_b))
+           {
+               fprintf (stderr, "FAIL: Cannot open %s: %s\n",
+                        argv[2], cairo_status_to_string (cairo_surface_status (args->surface_b)));
+               return false;
+           }
+       } else {
+           if (strstr(argv[i], "-fov")) {
+               if (i + 1 < argc) {
+                   args->FieldOfView = (float) atof(argv[i + 1]);
+               }
+           } else if (strstr(argv[i], "-verbose")) {
+               args->Verbose = true;
+           } else      if (strstr(argv[i], "-threshold")) {
+               if (i + 1 < argc) {
+                   args->ThresholdPixels = atoi(argv[i + 1]);
+               }
+           } else      if (strstr(argv[i], "-gamma")) {
+               if (i + 1 < argc) {
+                   args->Gamma = (float) atof(argv[i + 1]);
+               }
+           }else       if (strstr(argv[i], "-luminance")) {
+               if (i + 1 < argc) {
+                   args->Luminance = (float) atof(argv[i + 1]);
+               }
+           }
+       }
+    } /* i */
+    return true;
+}
+
+void
+args_print (args_t *args)
+{
+    printf("Field of view is %f degrees\n", args->FieldOfView);
+    printf("Threshold pixels is %d pixels\n", args->ThresholdPixels);
+    printf("The Gamma is %f\n", args->Gamma);
+    printf("The Display's luminance is %f candela per meter squared\n", args->Luminance);
+}
diff --git a/test/pdiff/args.h b/test/pdiff/args.h
new file mode 100644 (file)
index 0000000..5020239
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+  Comapre Args
+  Copyright (C) 2006 Yangli Hector Yee
+
+  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 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, write to the Free Software Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+*/
+
+#ifndef _ARGS_H
+#define _ARGS_H
+
+#include "pdiff.h"
+
+/* Args to pass into the comparison function */
+typedef struct _args
+{
+    cairo_surface_t    *surface_a;             /* Image A */
+    cairo_surface_t    *surface_b;             /* Image B */
+    bool               Verbose;                /* Print lots of text or not */
+    float              FieldOfView;            /* Field of view in degrees */
+    float              Gamma;                  /* The gamma to convert to linear color space */
+    float              Luminance;              /* the display's luminance */
+    unsigned int       ThresholdPixels;        /* How many pixels different to ignore */
+} args_t;
+
+void
+args_init (args_t *args);
+
+void
+args_fini (args_t *args);
+
+bool
+args_parse (args_t *args, int argc, char **argv);
+
+void
+args_print (args_t *args);
+
+#endif
diff --git a/test/pdiff/gpl.txt b/test/pdiff/gpl.txt
new file mode 100644 (file)
index 0000000..f90922e
--- /dev/null
@@ -0,0 +1,340 @@
+                   GNU GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                           Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.)  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
+this service 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 make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  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.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                   GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute 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 and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+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
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the 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 a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                           NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, 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.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE 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.
+
+                    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
+convey 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 2 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, write to the Free Software
+    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision 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, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This 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.
diff --git a/test/pdiff/lpyramid.c b/test/pdiff/lpyramid.c
new file mode 100644 (file)
index 0000000..aa57ca2
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+  Laplacian Pyramid
+  Copyright (C) 2006 Yangli Hector Yee
+
+  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 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, write to the Free Software Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+*/
+
+#include "lpyramid.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+struct _lpyramid {
+    /* Successively blurred versions of the original image */
+    float *levels[MAX_PYR_LEVELS];
+
+    int width;
+    int height;
+};
+
+static void
+convolve (lpyramid_t *pyramid, float *a, const float *b)
+/* convolves image b with the filter kernel and stores it in a */
+{
+    int y,x,i,j;
+    const float Kernel[] = {0.05f, 0.25f, 0.4f, 0.25f, 0.05f};
+    int width = pyramid->width;
+    int height = pyramid->height;
+
+    for (y=0; y<height; y++) {
+       for (x=0; x<width; x++) {
+           float sum = 0.f;
+           for (j=-2; j<=2; j++) {
+               float sum_i = 0.f;
+               int ny=y+j;
+               if (ny<0) ny=-ny;
+               if (ny>=height) ny=2*height - ny - 1;
+               ny *= width;
+               for (i=-2; i<=2; i++) {
+                   int nx=x+i;
+                   if (nx<0) nx=-nx;
+                   if (nx>=width) nx=2*width - nx - 1;
+                   sum_i += Kernel[i+2] * b[ny + nx];
+               }
+               sum += sum_i * Kernel[j+2];
+           }
+           *a++ = sum;
+       }
+    }
+}
+
+/*
+ * Construction/Destruction
+ */
+
+lpyramid_t *
+lpyramid_create (float *image, int width, int height)
+{
+    lpyramid_t *pyramid;
+    int i;
+
+    pyramid = malloc (sizeof (lpyramid_t));
+    if (pyramid == NULL) {
+       fprintf (stderr, "Out of memory.\n");
+       exit (1);
+    }
+    pyramid->width = width;
+    pyramid->height = height;
+
+    /* Make the Laplacian pyramid by successively
+     * copying the earlier levels and blurring them */
+    for (i=0; i<MAX_PYR_LEVELS; i++) {
+       pyramid->levels[i] = malloc (width * height * sizeof (float));
+       if (pyramid->levels[i] == NULL) {
+           fprintf (stderr, "Out of memory.\n");
+           exit (1);
+       }
+       if (i == 0) {
+           memcpy (pyramid->levels[i], image, width * height * sizeof (float));
+       } else {
+           convolve(pyramid, pyramid->levels[i], pyramid->levels[i - 1]);
+       }
+    }
+
+    return pyramid;
+}
+
+void
+lpyramid_destroy (lpyramid_t *pyramid)
+{
+    int i;
+
+    for (i=0; i<MAX_PYR_LEVELS; i++)
+       free (pyramid->levels[i]);
+
+    free (pyramid);
+}
+
+float
+lpyramid_get_value (lpyramid_t *pyramid, int x, int y, int level)
+{
+    int index = x + y * pyramid->width;
+    int l = level;
+    if (l > MAX_PYR_LEVELS)
+       l = MAX_PYR_LEVELS;
+    return pyramid->levels[level][index];
+}
diff --git a/test/pdiff/lpyramid.h b/test/pdiff/lpyramid.h
new file mode 100644 (file)
index 0000000..e47a66e
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+  Laplacian Pyramid
+  Copyright (C) 2006 Yangli Hector Yee
+
+  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 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, write to the Free Software Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+*/
+#ifndef _LPYRAMID_H
+#define _LPYRAMID_H
+
+#define MAX_PYR_LEVELS 8
+
+typedef struct _lpyramid lpyramid_t;
+
+lpyramid_t *
+lpyramid_create (float *image, int width, int height);
+
+void
+lpyramid_destroy (lpyramid_t *pyramid);
+
+float
+lpyramid_get_value (lpyramid_t *pyramid, int x, int y, int level);
+
+#endif /* _LPYRAMID_H */
diff --git a/test/pdiff/pdiff.c b/test/pdiff/pdiff.c
new file mode 100644 (file)
index 0000000..eb5f156
--- /dev/null
@@ -0,0 +1,420 @@
+/*
+  Metric
+  Copyright (C) 2006 Yangli Hector Yee
+
+  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 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, write to the Free Software Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+*/
+
+#define _GNU_SOURCE
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "lpyramid.h"
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#if   HAVE_STDINT_H
+# include <stdint.h>
+#elif HAVE_INTTYPES_H
+# include <inttypes.h>
+#elif HAVE_SYS_INT_TYPES_H
+# include <sys/int_types.h>
+#elif defined(_MSC_VER)
+  typedef __int8 int8_t;
+  typedef unsigned __int8 uint8_t;
+  typedef __int16 int16_t;
+  typedef unsigned __int16 uint16_t;
+  typedef __int32 int32_t;
+  typedef unsigned __int32 uint32_t;
+  typedef __int64 int64_t;
+  typedef unsigned __int64 uint64_t;
+# ifndef HAVE_UINT64_T
+#  define HAVE_UINT64_T 1
+# endif
+# ifndef INT16_MIN
+#  define INT16_MIN    (-32767-1)
+# endif
+# ifndef INT16_MAX
+#  define INT16_MAX    (32767)
+# endif
+# ifndef UINT16_MAX
+#  define UINT16_MAX   (65535)
+# endif
+#else
+#error Cannot find definitions for fixed-width integral types (uint8_t, uint32_t, etc.)
+#endif
+
+#include "pdiff.h"
+
+#ifndef M_PI
+#define M_PI 3.14159265f
+#endif
+
+#ifndef __USE_ISOC99
+#define expf   exp
+#define powf   pow
+#define fabsf  fabs
+#define sqrtf  sqrt
+#define log10f log10
+#endif
+
+/*
+ * Given the adaptation luminance, this function returns the
+ * threshold of visibility in cd per m^2
+ * TVI means Threshold vs Intensity function
+ * This version comes from Ward Larson Siggraph 1997
+ */
+static float
+tvi (float adaptation_luminance)
+{
+    /* returns the threshold luminance given the adaptation luminance
+       units are candelas per meter squared
+    */
+    float log_a, r, result;
+    log_a = log10f(adaptation_luminance);
+
+    if (log_a < -3.94f) {
+       r = -2.86f;
+    } else if (log_a < -1.44f) {
+       r = powf(0.405f * log_a + 1.6f , 2.18f) - 2.86f;
+    } else if (log_a < -0.0184f) {
+       r = log_a - 0.395f;
+    } else if (log_a < 1.9f) {
+       r = powf(0.249f * log_a + 0.65f, 2.7f) - 0.72f;
+    } else {
+       r = log_a - 1.255f;
+    }
+
+    result = powf(10.0f , r);
+
+    return result;
+}
+
+/* computes the contrast sensitivity function (Barten SPIE 1989)
+ * given the cycles per degree (cpd) and luminance (lum)
+ */
+static float
+csf (float cpd, float lum)
+{
+    float a, b, result;
+
+    a = 440.0f * powf((1.0f + 0.7f / lum), -0.2f);
+    b = 0.3f * powf((1.0f + 100.0f / lum), 0.15f);
+
+    result = a * cpd * expf(-b * cpd) * sqrtf(1.0f + 0.06f * expf(b * cpd));
+
+    return result;
+}
+
+/*
+ * Visual Masking Function
+ * from Daly 1993
+ */
+static float
+mask (float contrast)
+{
+    float a, b, result;
+    a = powf(392.498f * contrast,  0.7f);
+    b = powf(0.0153f * a, 4.0f);
+    result = powf(1.0f + b, 0.25f);
+
+    return result;
+}
+
+/* convert Adobe RGB (1998) with reference white D65 to XYZ */
+static void
+AdobeRGBToXYZ (float r, float g, float b, float *x, float *y, float *z)
+{
+    /* matrix is from http://www.brucelindbloom.com/ */
+    *x = r * 0.576700f + g * 0.185556f + b * 0.188212f;
+    *y = r * 0.297361f + g * 0.627355f + b * 0.0752847f;
+    *z = r * 0.0270328f + g * 0.0706879f + b * 0.991248f;
+}
+
+static void
+XYZToLAB (float x, float y, float z, float *L, float *A, float *B)
+{
+    static float xw = -1;
+    static float yw;
+    static float zw;
+    const float epsilon  = 216.0f / 24389.0f;
+    const float kappa = 24389.0f / 27.0f;
+    float f[3];
+    float r[3];
+    int i;
+
+    /* reference white */
+    if (xw < 0) {
+       AdobeRGBToXYZ(1, 1, 1, &xw, &yw, &zw);
+    }
+    r[0] = x / xw;
+    r[1] = y / yw;
+    r[2] = z / zw;
+    for (i = 0; i < 3; i++) {
+       if (r[i] > epsilon) {
+           f[i] = powf(r[i], 1.0f / 3.0f);
+       } else {
+           f[i] = (kappa * r[i] + 16.0f) / 116.0f;
+       }
+    }
+    *L = 116.0f * f[1] - 16.0f;
+    *A = 500.0f * (f[0] - f[1]);
+    *B = 200.0f * (f[1] - f[2]);
+}
+
+static uint32_t
+_get_pixel (const uint32_t *data, int i)
+{
+    return data[i];
+}
+
+static unsigned char
+_get_red (const uint32_t *data, int i)
+{
+    uint32_t pixel;
+    uint8_t alpha;
+
+    pixel = _get_pixel (data, i);
+    alpha = (pixel & 0xff000000) >> 24;
+    if (alpha == 0)
+       return 0;
+    else
+       return (((pixel & 0x00ff0000) >> 16) * 255 + alpha / 2) / alpha;
+}
+
+static unsigned char
+_get_green (const uint32_t *data, int i)
+{
+    uint32_t pixel;
+    uint8_t alpha;
+
+    pixel = _get_pixel (data, i);
+    alpha = (pixel & 0xff000000) >> 24;
+    if (alpha == 0)
+       return 0;
+    else
+       return (((pixel & 0x0000ff00) >> 8) * 255 + alpha / 2) / alpha;
+}
+
+static unsigned char
+_get_blue (const uint32_t *data, int i)
+{
+    uint32_t pixel;
+    uint8_t alpha;
+
+    pixel = _get_pixel (data, i);
+    alpha = (pixel & 0xff000000) >> 24;
+    if (alpha == 0)
+       return 0;
+    else
+       return (((pixel & 0x000000ff) >> 0) * 255 + alpha / 2) / alpha;
+}
+
+static void *
+xmalloc (size_t size)
+{
+    void *buf;
+
+    buf = malloc (size);
+    if (buf == NULL) {
+       fprintf (stderr, "Out of memory.\n");
+       exit (1);
+    }
+
+    return buf;
+}
+
+int
+pdiff_compare (cairo_surface_t *surface_a,
+              cairo_surface_t *surface_b,
+              double gamma,
+              double luminance,
+              double field_of_view)
+{
+    unsigned int dim = (cairo_image_surface_get_width (surface_a)
+                       * cairo_image_surface_get_height (surface_a));
+    unsigned int i;
+
+    /* assuming colorspaces are in Adobe RGB (1998) convert to XYZ */
+    float *aX;
+    float *aY;
+    float *aZ;
+    float *bX;
+    float *bY;
+    float *bZ;
+    float *aLum;
+    float *bLum;
+
+    float *aA;
+    float *bA;
+    float *aB;
+    float *bB;
+
+    unsigned int x, y, w, h;
+
+    lpyramid_t *la, *lb;
+
+    float num_one_degree_pixels, pixels_per_degree, num_pixels;
+    unsigned int adaptation_level;
+
+    float cpd[MAX_PYR_LEVELS];
+    float F_freq[MAX_PYR_LEVELS - 2];
+    float csf_max;
+    const uint32_t *data_a, *data_b;
+
+    unsigned int pixels_failed;
+
+    w = cairo_image_surface_get_width (surface_a);
+    h = cairo_image_surface_get_height (surface_a);
+    if (w < 3 || h < 3) /* too small for the Laplacian convolution */
+       return -1;
+
+    aX = xmalloc (dim * sizeof (float));
+    aY = xmalloc (dim * sizeof (float));
+    aZ = xmalloc (dim * sizeof (float));
+    bX = xmalloc (dim * sizeof (float));
+    bY = xmalloc (dim * sizeof (float));
+    bZ = xmalloc (dim * sizeof (float));
+    aLum = xmalloc (dim * sizeof (float));
+    bLum = xmalloc (dim * sizeof (float));
+
+    aA = xmalloc (dim * sizeof (float));
+    bA = xmalloc (dim * sizeof (float));
+    aB = xmalloc (dim * sizeof (float));
+    bB = xmalloc (dim * sizeof (float));
+
+    data_a = (uint32_t *) cairo_image_surface_get_data (surface_a);
+    data_b = (uint32_t *) cairo_image_surface_get_data (surface_b);
+    for (y = 0; y < h; y++) {
+       for (x = 0; x < w; x++) {
+           float r, g, b, l;
+           i = x + y * w;
+           r = powf(_get_red (data_a, i) / 255.0f, gamma);
+           g = powf(_get_green (data_a, i) / 255.0f, gamma);
+           b = powf(_get_blue (data_a, i) / 255.0f, gamma);
+
+           AdobeRGBToXYZ(r,g,b,&aX[i],&aY[i],&aZ[i]);
+           XYZToLAB(aX[i], aY[i], aZ[i], &l, &aA[i], &aB[i]);
+           r = powf(_get_red (data_b, i) / 255.0f, gamma);
+           g = powf(_get_green (data_b, i) / 255.0f, gamma);
+           b = powf(_get_blue (data_b, i) / 255.0f, gamma);
+
+           AdobeRGBToXYZ(r,g,b,&bX[i],&bY[i],&bZ[i]);
+           XYZToLAB(bX[i], bY[i], bZ[i], &l, &bA[i], &bB[i]);
+           aLum[i] = aY[i] * luminance;
+           bLum[i] = bY[i] * luminance;
+       }
+    }
+
+    la = lpyramid_create (aLum, w, h);
+    lb = lpyramid_create (bLum, w, h);
+
+    num_one_degree_pixels = (float) (2 * tan(field_of_view * 0.5 * M_PI / 180) * 180 / M_PI);
+    pixels_per_degree = w / num_one_degree_pixels;
+
+    num_pixels = 1;
+    adaptation_level = 0;
+    for (i = 0; i < MAX_PYR_LEVELS; i++) {
+       adaptation_level = i;
+       if (num_pixels > num_one_degree_pixels) break;
+       num_pixels *= 2;
+    }
+
+    cpd[0] = 0.5f * pixels_per_degree;
+    for (i = 1; i < MAX_PYR_LEVELS; i++) cpd[i] = 0.5f * cpd[i - 1];
+    csf_max = csf(3.248f, 100.0f);
+
+    for (i = 0; i < MAX_PYR_LEVELS - 2; i++) F_freq[i] = csf_max / csf( cpd[i], 100.0f);
+
+    pixels_failed = 0;
+    for (y = 0; y < h; y++) {
+       for (x = 0; x < w; x++) {
+           int index = x + y * w;
+           float contrast[MAX_PYR_LEVELS - 2];
+           float F_mask[MAX_PYR_LEVELS - 2];
+           float factor;
+           float delta;
+           float adapt;
+           bool pass;
+           float sum_contrast = 0;
+           for (i = 0; i < MAX_PYR_LEVELS - 2; i++) {
+               float n1 = fabsf(lpyramid_get_value (la,x,y,i) - lpyramid_get_value (la,x,y,i + 1));
+               float n2 = fabsf(lpyramid_get_value (lb,x,y,i) - lpyramid_get_value (lb,x,y,i + 1));
+               float numerator = (n1 > n2) ? n1 : n2;
+               float d1 = fabsf(lpyramid_get_value(la,x,y,i+2));
+               float d2 = fabsf(lpyramid_get_value(lb,x,y,i+2));
+               float denominator = (d1 > d2) ? d1 : d2;
+               if (denominator < 1e-5f) denominator = 1e-5f;
+               contrast[i] = numerator / denominator;
+               sum_contrast += contrast[i];
+           }
+           if (sum_contrast < 1e-5) sum_contrast = 1e-5f;
+           adapt = lpyramid_get_value(la,x,y,adaptation_level) + lpyramid_get_value(lb,x,y,adaptation_level);
+           adapt *= 0.5f;
+           if (adapt < 1e-5) adapt = 1e-5f;
+           for (i = 0; i < MAX_PYR_LEVELS - 2; i++) {
+               F_mask[i] = mask(contrast[i] * csf(cpd[i], adapt));
+           }
+           factor = 0;
+           for (i = 0; i < MAX_PYR_LEVELS - 2; i++) {
+               factor += contrast[i] * F_freq[i] * F_mask[i] / sum_contrast;
+           }
+           if (factor < 1) factor = 1;
+           if (factor > 10) factor = 10;
+           delta = fabsf(lpyramid_get_value(la,x,y,0) - lpyramid_get_value(lb,x,y,0));
+           pass = true;
+           /* pure luminance test */
+           if (delta > factor * tvi(adapt)) {
+               pass = false;
+           } else {
+               /* CIE delta E test with modifications */
+               float color_scale = 1.0f;
+               float da = aA[index] - bA[index];
+               float db = aB[index] - bB[index];
+               float delta_e;
+               /* ramp down the color test in scotopic regions */
+               if (adapt < 10.0f) {
+                   color_scale = 1.0f - (10.0f - color_scale) / 10.0f;
+                   color_scale = color_scale * color_scale;
+               }
+               da = da * da;
+               db = db * db;
+               delta_e = (da + db) * color_scale;
+               if (delta_e > factor) {
+                   pass = false;
+               }
+           }
+           if (!pass)
+               pixels_failed++;
+       }
+    }
+
+    free (aX);
+    free (aY);
+    free (aZ);
+    free (bX);
+    free (bY);
+    free (bZ);
+    free (aLum);
+    free (bLum);
+    lpyramid_destroy (la);
+    lpyramid_destroy (lb);
+    free (aA);
+    free (bA);
+    free (aB);
+    free (bB);
+
+    return pixels_failed;
+}
diff --git a/test/pdiff/pdiff.h b/test/pdiff/pdiff.h
new file mode 100644 (file)
index 0000000..30fec06
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+  Copyright (C) 2006 Yangli Hector Yee
+  Copyright (C) 2006 Red Hat, Inc.
+
+  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 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, write to the Free Software Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+*/
+
+#ifndef _PDIFF_H
+#define _PDIFF_H
+
+#include <cairo.h>
+
+typedef int bool;
+#ifndef true
+#define true 1
+#endif
+#ifndef false
+#define false 0
+#endif
+
+/* Image comparison metric using Yee's method (and a cairo interface)
+ * References: A Perceptual Metric for Production Testing, Hector Yee, Journal of Graphics Tools 2004
+ */
+int
+pdiff_compare (cairo_surface_t *surface_a,
+              cairo_surface_t *surface_b,
+              double gamma,
+              double luminance,
+              double field_of_view);
+
+#endif
diff --git a/test/pdiff/perceptualdiff.c b/test/pdiff/perceptualdiff.c
new file mode 100644 (file)
index 0000000..a3a6e80
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+  PerceptualDiff - a program that compares two images using a perceptual metric
+  based on the paper :
+  A perceptual metric for production testing. Journal of graphics tools, 9(4):33-40, 2004, Hector Yee
+  Copyright (C) 2006 Yangli Hector Yee
+
+  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 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, write to the Free Software Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+*/
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <math.h>
+#include "lpyramid.h"
+#include "args.h"
+#include "pdiff.h"
+
+static bool Yee_Compare(args_t *args)
+{
+    int width_a, height_a, stride_a;
+    unsigned char *data_a, *row_a;
+    uint32_t *pixel_a;
+    int width_b, height_b, stride_b;
+    unsigned char *data_b, *row_b;
+    uint32_t *pixel_b;
+    unsigned int x, y, dim, pixels_failed;
+    bool identical = true;
+
+    width_a = cairo_image_surface_get_width (args->surface_a);
+    height_a = cairo_image_surface_get_height (args->surface_a);
+    stride_a = cairo_image_surface_get_stride (args->surface_a);
+    data_a = cairo_image_surface_get_data (args->surface_a);
+
+    width_b = cairo_image_surface_get_width (args->surface_b);
+    height_b = cairo_image_surface_get_height (args->surface_b);
+    stride_b = cairo_image_surface_get_stride (args->surface_b);
+    data_b = cairo_image_surface_get_data (args->surface_b);
+
+    if ((width_a != width_b) || (height_a != height_b)) {
+       printf ("FAIL: Image dimensions do not match\n");
+       return false;
+    }
+
+    identical = true;
+
+    for (y = 0; y < height_a; y++) {
+       row_a = data_a + y * stride_a;
+       row_b = data_b + y * stride_b;
+       pixel_a = (uint32_t *) row_a;
+       pixel_b = (uint32_t *) row_b;
+       for (x = 0; x < width_a; x++) {
+           if (*pixel_a != *pixel_b) {
+               identical = false;
+           }
+           pixel_a++;
+           pixel_b++;
+       }
+    }
+    if (identical) {
+       printf ("PASS: Images are binary identical\n");
+       return true;
+    }
+
+    pixels_failed = pdiff_compare (args->surface_a, args->surface_b,
+                                  args->Gamma, args->Luminance,
+                                  args->FieldOfView);
+
+    if (pixels_failed < args->ThresholdPixels) {
+       printf ("PASS: Images are perceptually indistinguishable\n");
+       return true;
+    }
+
+    printf("FAIL: Images are visibly different\n"
+          "%d pixels are different\n", pixels_failed);
+
+    return false;
+}
+
+int main(int argc, char **argv)
+{
+    args_t args;
+
+    args_init (&args);
+
+    if (!args_parse (&args, argc, argv)) {
+       return -1;
+    } else {
+       if (args.Verbose)
+           args_print (&args);
+    }
+    return ! Yee_Compare(&args);
+}
diff --git a/test/pixman-rotate.c b/test/pixman-rotate.c
new file mode 100644 (file)
index 0000000..f412ce6
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * Copyright © 2007 Red Hat, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Kristian Høgsberg <krh@redhat.com>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+#include <cairo.h>
+
+#include "cairo-test.h"
+
+#define WIDTH  32
+#define HEIGHT WIDTH
+
+#define IMAGE_WIDTH    (3 * WIDTH)
+#define IMAGE_HEIGHT   IMAGE_WIDTH
+
+/* Draw the word cairo at NUM_TEXT different angles */
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_surface_t *stamp;
+    cairo_t *cr2;
+
+    /* Draw a translucent rectangle for reference where the rotated
+     * image should be. */
+    cairo_new_path (cr);
+    cairo_rectangle (cr, WIDTH, HEIGHT, WIDTH, HEIGHT);
+    cairo_set_source_rgba (cr, 1, 1, 0, 0.3);
+    cairo_fill (cr);
+
+#if 1 /* Set to 0 to generate reference image */
+    cairo_translate (cr, 2 * WIDTH, 2 * HEIGHT);
+    cairo_rotate (cr, M_PI);
+#else
+    cairo_translate (cr, WIDTH, HEIGHT);
+#endif
+
+    stamp = cairo_surface_create_similar (cairo_get_group_target (cr),
+                                         CAIRO_CONTENT_COLOR_ALPHA,
+                                         WIDTH, HEIGHT);
+    cr2 = cairo_create (stamp);
+    cairo_surface_destroy (stamp);
+    {
+       cairo_new_path (cr2);
+       cairo_rectangle (cr2, WIDTH / 4, HEIGHT / 4, WIDTH / 2, HEIGHT / 2);
+       cairo_set_source_rgba (cr2, 1, 0, 0, 0.8);
+       cairo_fill (cr2);
+
+       cairo_rectangle (cr2, 0, 0, WIDTH, HEIGHT);
+       cairo_set_line_width (cr2, 2);
+       cairo_set_source_rgb (cr2, 0, 0, 0);
+       cairo_stroke (cr2);
+    }
+    cairo_set_source_surface (cr, cairo_get_target (cr2), 0, 0);
+    cairo_destroy (cr2);
+
+    cairo_paint (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (pixman_rotate,
+           "Exposes pixman off-by-one error when rotating",
+           "image, transform", /* keywords */
+           NULL, /* requirements */
+           IMAGE_WIDTH, IMAGE_HEIGHT,
+           NULL, draw)
diff --git a/test/png-flatten.c b/test/png-flatten.c
new file mode 100644 (file)
index 0000000..2ce804e
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Carl Worth <cworth@cworth.org>
+ */
+
+#include <stdio.h>
+
+#include <cairo.h>
+
+int
+main (int argc, char *argv[])
+{
+    cairo_t *cr;
+    cairo_surface_t *argb, *rgb24;
+    cairo_status_t status;
+    const char *input, *output;
+
+    if (argc != 3) {
+       fprintf (stderr, "usage: %s input.png output.png", argv[0]);
+       fprintf (stderr, "Loads a PNG image (potentially with alpha) and writes out a flattened (no alpha)\nPNG image by first blending over white.\n");
+       return 1;
+    }
+
+    input = argv[1];
+    output = argv[2];
+
+    argb = cairo_image_surface_create_from_png (input);
+    status = cairo_surface_status (argb);
+    if (status) {
+       fprintf (stderr, "%s: Error: Failed to load %s: %s\n",
+                argv[0], input, cairo_status_to_string (status));
+       return 1;
+    }
+
+    rgb24 = cairo_image_surface_create (CAIRO_FORMAT_RGB24,
+                                       cairo_image_surface_get_width (argb),
+                                       cairo_image_surface_get_height (argb));
+
+    cr = cairo_create (rgb24);
+
+    cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); /* white */
+    cairo_paint (cr);
+
+    cairo_set_source_surface (cr, argb, 0, 0);
+    cairo_paint (cr);
+
+    cairo_destroy (cr);
+
+    status = cairo_surface_write_to_png (rgb24, output);
+    if (status) {
+       fprintf (stderr, "%s: Error: Failed to write %s: %s\n",
+                argv[0], output, cairo_status_to_string (status));
+       return 1;
+    }
+
+    return 0;
+}
diff --git a/test/png.c b/test/png.c
new file mode 100644 (file)
index 0000000..e90ac66
--- /dev/null
@@ -0,0 +1,164 @@
+/*
+ * Copyright © 2008 Chris Wilson
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Chris Wilson not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Chris Wilson makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * CHRIS WILSON DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL CHRIS WILSON BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+
+#include <assert.h>
+
+/* Test the idempotency of write_png->read_png */
+
+#define RGB_MASK 0x00ffffff
+
+static cairo_bool_t
+image_surface_equals (cairo_surface_t *A, cairo_surface_t *B)
+{
+    if (cairo_image_surface_get_format (A) !=
+       cairo_image_surface_get_format (B))
+       return 0;
+
+    if (cairo_image_surface_get_width (A) !=
+       cairo_image_surface_get_width (B))
+       return 0;
+
+    if (cairo_image_surface_get_height (A) !=
+       cairo_image_surface_get_height (B))
+       return 0;
+
+    return 1;
+}
+
+static const char *
+format_to_string (cairo_format_t format)
+{
+    switch (format) {
+    case CAIRO_FORMAT_A1:     return "a1";
+    case CAIRO_FORMAT_A8:     return "a8";
+    case CAIRO_FORMAT_RGB16_565:  return "rgb16";
+    case CAIRO_FORMAT_RGB24:  return "rgb24";
+    case CAIRO_FORMAT_ARGB32: return "argb32";
+    case CAIRO_FORMAT_INVALID:
+    default: return "???";
+    }
+}
+
+static void
+print_surface (const cairo_test_context_t *ctx, cairo_surface_t *surface)
+{
+    cairo_test_log (ctx,
+                   "%s (%dx%d)\n",
+                   format_to_string (cairo_image_surface_get_format (surface)),
+                   cairo_image_surface_get_width (surface),
+                   cairo_image_surface_get_height (surface));
+}
+
+static cairo_test_status_t
+preamble (cairo_test_context_t *ctx)
+{
+    const char *filename = "png.out.png";
+    cairo_surface_t *surface0, *surface1;
+    cairo_status_t status;
+    uint32_t argb32 = 0xdeadbede;
+
+    surface0 = cairo_image_surface_create_for_data ((unsigned char *) &argb32,
+                                                   CAIRO_FORMAT_ARGB32,
+                                                   1, 1, 4);
+    status = cairo_surface_write_to_png (surface0, filename);
+    if (status) {
+       cairo_test_log (ctx, "Error writing '%s': %s\n",
+                       filename, cairo_status_to_string (status));
+
+       cairo_surface_destroy (surface0);
+       return cairo_test_status_from_status (ctx, status);
+    }
+    surface1 = cairo_image_surface_create_from_png (filename);
+    status = cairo_surface_status (surface1);
+    if (status) {
+       cairo_test_log (ctx, "Error reading '%s': %s\n",
+                       filename, cairo_status_to_string (status));
+
+       cairo_surface_destroy (surface1);
+       cairo_surface_destroy (surface0);
+       return cairo_test_status_from_status (ctx, status);
+    }
+
+    if (! image_surface_equals (surface0, surface1)) {
+       cairo_test_log (ctx, "Error surface mismatch.\n");
+       cairo_test_log (ctx, "to png: "); print_surface (ctx, surface0);
+       cairo_test_log (ctx, "from png: "); print_surface (ctx, surface1);
+
+       cairo_surface_destroy (surface0);
+       cairo_surface_destroy (surface1);
+       return CAIRO_TEST_FAILURE;
+    }
+    assert (*(uint32_t *) cairo_image_surface_get_data (surface1) == argb32);
+
+    cairo_surface_destroy (surface0);
+    cairo_surface_destroy (surface1);
+
+    surface0 = cairo_image_surface_create_for_data ((unsigned char *) &argb32,
+                                                   CAIRO_FORMAT_RGB24,
+                                                   1, 1, 4);
+    status = cairo_surface_write_to_png (surface0, filename);
+    if (status) {
+       cairo_test_log (ctx, "Error writing '%s': %s\n",
+                       filename, cairo_status_to_string (status));
+       cairo_surface_destroy (surface0);
+       return cairo_test_status_from_status (ctx, status);
+    }
+    surface1 = cairo_image_surface_create_from_png (filename);
+    status = cairo_surface_status (surface1);
+    if (status) {
+       cairo_test_log (ctx, "Error reading '%s': %s\n",
+                       filename, cairo_status_to_string (status));
+
+       cairo_surface_destroy (surface1);
+       cairo_surface_destroy (surface0);
+       return cairo_test_status_from_status (ctx, status);
+    }
+
+    if (! image_surface_equals (surface0, surface1)) {
+       cairo_test_log (ctx, "Error surface mismatch.\n");
+       cairo_test_log (ctx, "to png: "); print_surface (ctx, surface0);
+       cairo_test_log (ctx, "from png: "); print_surface (ctx, surface1);
+
+       cairo_surface_destroy (surface0);
+       cairo_surface_destroy (surface1);
+       return CAIRO_TEST_FAILURE;
+    }
+    assert ((*(uint32_t *) cairo_image_surface_get_data (surface1) & RGB_MASK)
+           == (argb32 & RGB_MASK));
+
+    cairo_surface_destroy (surface0);
+    cairo_surface_destroy (surface1);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (png,
+           "Check that the png export/import is idempotent.",
+           "png, api", /* keywords */
+           NULL, /* requirements */
+           0, 0,
+           preamble, NULL)
diff --git a/test/png.png b/test/png.png
new file mode 100644 (file)
index 0000000..56c428e
Binary files /dev/null and b/test/png.png differ
diff --git a/test/ps-eps.c b/test/ps-eps.c
new file mode 100644 (file)
index 0000000..33802f3
--- /dev/null
@@ -0,0 +1,369 @@
+/*
+ * Copyright © 2006 Red Hat, Inc.
+ * Copyright © 2009 Adrian Johnson
+ * Copyright © 2008 Chris Wilson
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ *         Adrian Johnson <ajohnson@redneon.com>
+ *         Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <cairo.h>
+#include <cairo-ps.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#include <errno.h>
+#endif
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+
+#include "cairo-test.h"
+#include "buffer-diff.h"
+
+/* Test EPS output.
+ */
+
+#define WIDTH 595
+#define HEIGHT 842
+
+/* Reference Bounding Box */
+#define LLX  95
+#define LLY 687
+#define URX 155
+#define URY 747
+
+static void
+_xunlink (const cairo_test_context_t *ctx, const char *pathname)
+{
+    if (unlink (pathname) < 0 && errno != ENOENT) {
+       cairo_test_log (ctx, "Error: Cannot remove %s: %s\n",
+                       pathname, strerror (errno));
+       exit (1);
+    }
+}
+
+static cairo_bool_t
+check_result (cairo_test_context_t *ctx,
+             const cairo_boilerplate_target_t *target,
+             const char *test_name,
+             const char *base_name,
+             cairo_surface_t *surface)
+{
+    const char *format;
+    char *ref_name;
+    char *png_name;
+    char *diff_name;
+    cairo_surface_t *test_image, *ref_image, *diff_image;
+    buffer_diff_result_t result;
+    cairo_status_t status;
+    cairo_bool_t ret;
+
+    /* XXX log target, OUTPUT, REFERENCE, DIFFERENCE for index.html */
+
+    if (target->finish_surface != NULL) {
+       status = target->finish_surface (surface);
+       if (status) {
+           cairo_test_log (ctx, "Error: Failed to finish surface: %s\n",
+                   cairo_status_to_string (status));
+           cairo_surface_destroy (surface);
+           return FALSE;
+       }
+    }
+
+    xasprintf (&png_name,  "%s.out.png", base_name);
+    xasprintf (&diff_name, "%s.diff.png", base_name);
+
+    test_image = target->get_image_surface (surface, 0, WIDTH, HEIGHT);
+    if (cairo_surface_status (test_image)) {
+       cairo_test_log (ctx, "Error: Failed to extract page: %s\n",
+                       cairo_status_to_string (cairo_surface_status (test_image)));
+       cairo_surface_destroy (test_image);
+       free (png_name);
+       free (diff_name);
+       return FALSE;
+    }
+
+    _xunlink (ctx, png_name);
+    status = cairo_surface_write_to_png (test_image, png_name);
+    if (status) {
+       cairo_test_log (ctx, "Error: Failed to write output image: %s\n",
+               cairo_status_to_string (status));
+       cairo_surface_destroy (test_image);
+       free (png_name);
+       free (diff_name);
+       return FALSE;
+    }
+
+    format = cairo_boilerplate_content_name (target->content);
+    ref_name = cairo_test_reference_filename (ctx,
+                                             base_name,
+                                             test_name,
+                                             target->name,
+                                             target->basename,
+                                             format,
+                                             CAIRO_TEST_REF_SUFFIX,
+                                             CAIRO_TEST_PNG_EXTENSION);
+    if (ref_name == NULL) {
+       cairo_test_log (ctx, "Error: Cannot find reference image for %s\n",
+                       base_name);
+       cairo_surface_destroy (test_image);
+       free (png_name);
+       free (diff_name);
+       return FALSE;
+    }
+
+    ref_image = cairo_test_get_reference_image (ctx, ref_name,
+           target->content == CAIRO_TEST_CONTENT_COLOR_ALPHA_FLATTENED);
+    if (cairo_surface_status (ref_image)) {
+       cairo_test_log (ctx, "Error: Cannot open reference image for %s: %s\n",
+                       ref_name,
+               cairo_status_to_string (cairo_surface_status (ref_image)));
+       cairo_surface_destroy (ref_image);
+       cairo_surface_destroy (test_image);
+       free (png_name);
+       free (diff_name);
+       free (ref_name);
+       return FALSE;
+    }
+
+    diff_image = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
+           WIDTH, HEIGHT);
+
+    ret = TRUE;
+    status = image_diff (ctx,
+           test_image, ref_image, diff_image,
+           &result);
+    _xunlink (ctx, diff_name);
+    if (status) {
+       cairo_test_log (ctx, "Error: Failed to compare images: %s\n",
+                       cairo_status_to_string (status));
+       ret = FALSE;
+    } else if (image_diff_is_failure (&result, target->error_tolerance))
+    {
+       ret = FALSE;
+
+       status = cairo_surface_write_to_png (diff_image, diff_name);
+       if (status) {
+           cairo_test_log (ctx, "Error: Failed to write differences image: %s\n",
+                   cairo_status_to_string (status));
+       }
+    }
+
+    cairo_surface_destroy (test_image);
+    cairo_surface_destroy (diff_image);
+    free (png_name);
+    free (diff_name);
+    free (ref_name);
+
+    return ret;
+}
+
+
+#define DOCUMENT_BBOX  "%%BoundingBox:"
+#define PAGE_BBOX      "%%PageBoundingBox:"
+
+static cairo_bool_t
+check_bbox (cairo_test_context_t *ctx,
+           const char *base_name)
+{
+    char *filename;
+    FILE *f;
+    char buf[256];
+    cairo_bool_t bbox_pass, page_bbox_pass;
+    int llx, lly, urx, ury;
+    int ret;
+
+    xasprintf (&filename,  "%s.out.ps", base_name);
+    f = fopen (filename, "r");
+    if (!f) {
+       cairo_test_log (ctx, "Error: Cannot open EPS output: %s\n",
+                       base_name);
+       free (filename);
+       return FALSE;
+    }
+
+    bbox_pass = FALSE;
+    page_bbox_pass = FALSE;
+    while (!feof(f)) {
+       fgets (buf, sizeof(buf), f);
+
+       if (strncmp (buf, DOCUMENT_BBOX, strlen (DOCUMENT_BBOX)) == 0) {
+           ret = sscanf (buf+strlen (DOCUMENT_BBOX), "%d %d %d %d", &llx, &lly, &urx, &ury);
+           if (ret == 4 && llx == LLX && lly == LLY && urx == URX && ury == URY)
+               bbox_pass = TRUE;
+       }
+
+       if (strncmp (buf, PAGE_BBOX, strlen (PAGE_BBOX)) == 0) {
+           ret = sscanf (buf+strlen (PAGE_BBOX), "%d %d %d %d", &llx, &lly, &urx, &ury);
+           if (ret == 4 && llx == LLX && lly == LLY && urx == URX && ury == URY)
+               page_bbox_pass = TRUE;
+       }
+    }
+    fclose (f);
+
+    if (!bbox_pass || !page_bbox_pass) {
+       cairo_test_log (ctx, "Error: EPS Bounding Box does not match reference Bounding Box\n");
+       return FALSE;
+    }
+
+    free (filename);
+
+    return TRUE;
+}
+
+static cairo_bool_t
+_cairo_test_mkdir (const char *path)
+{
+#if ! HAVE_MKDIR
+    return FALSE;
+#elif HAVE_MKDIR == 1
+    if (mkdir (path) == 0)
+       return TRUE;
+#elif HAVE_MKDIR == 2
+    if (mkdir (path, 0770) == 0)
+       return TRUE;
+#else
+#error Bad value for HAVE_MKDIR
+#endif
+
+    return errno == EEXIST;
+}
+
+static cairo_test_status_t
+preamble (cairo_test_context_t *ctx)
+{
+    cairo_t *cr;
+    cairo_test_status_t ret = CAIRO_TEST_UNTESTED;
+    const char *path = _cairo_test_mkdir (CAIRO_TEST_OUTPUT_DIR) ? CAIRO_TEST_OUTPUT_DIR : ".";
+    unsigned int i;
+
+    for (i = 0; i < ctx->num_targets; i++) {
+       const cairo_boilerplate_target_t *target = ctx->targets_to_test[i];
+       cairo_surface_t *surface = NULL;
+       char *base_name;
+       void *closure;
+       const char *format;
+       cairo_status_t status;
+       cairo_bool_t pass;
+       char *test_name;
+
+       if (! cairo_test_is_target_enabled (ctx, target->name))
+           continue;
+
+       format = cairo_boilerplate_content_name (target->content);
+       xasprintf (&test_name, "ps-eps");
+       xasprintf (&base_name, "%s/ps-eps.%s.%s",
+                  path, target->name, format);
+
+       surface = (target->create_surface) (base_name,
+                                           target->content,
+                                           WIDTH, HEIGHT,
+                                           WIDTH, HEIGHT,
+                                           CAIRO_BOILERPLATE_MODE_TEST,
+                                           0,
+                                           &closure);
+
+       if (surface == NULL) {
+           free (base_name);
+           free (test_name);
+           continue;
+       }
+
+       cairo_ps_surface_set_eps (surface, TRUE);
+       if (!cairo_ps_surface_get_eps (surface)) {
+           cairo_surface_destroy (surface);
+           if (target->cleanup)
+               target->cleanup (closure);
+
+           free (base_name);
+           free (test_name);
+           continue;
+       }
+       
+       cairo_test_log (ctx,
+                       "Testing ps-eps with %s target\n",
+                       target->name);
+       printf ("%s:\t", base_name);
+       fflush (stdout);
+
+       cairo_surface_set_device_offset (surface, 25, 25);
+       cr = cairo_create (surface);
+
+       cairo_new_sub_path (cr);
+       cairo_arc (cr, 100, 100, 25, 0, 2*M_PI);
+       cairo_set_line_width (cr, 10);
+       cairo_stroke (cr);
+
+       cairo_show_page (cr);
+
+       status = cairo_status (cr);
+       cairo_destroy (cr);
+
+       if (status) {
+           cairo_test_log (ctx, "Error: Failed to create target surface: %s\n",
+                           cairo_status_to_string (status));
+           pass = FALSE;
+       } else {
+           pass = TRUE;
+           /* extract the image and compare it to our reference */
+           if (! check_result (ctx, target, test_name, base_name, surface))
+               pass = FALSE;
+
+           /* check the bounding box of the EPS file and compare it to our reference */
+           if (! check_bbox (ctx, base_name))
+               pass = FALSE;
+       }
+       cairo_surface_destroy (surface);
+       if (target->cleanup)
+           target->cleanup (closure);
+
+       free (base_name);
+       free (test_name);
+
+       if (pass) {
+           printf ("PASS\n");
+           ret = CAIRO_TEST_SUCCESS;
+       } else {
+           printf ("FAIL\n");
+           ret = CAIRO_TEST_FAILURE;
+       }
+       fflush (stdout);
+    }
+
+    return ret;
+}
+
+CAIRO_TEST (ps_eps,
+           "Check EPS output from PS surface",
+           "ps, api", /* keywords */
+           NULL, /* requirements */
+           0, 0,
+           preamble, NULL)
diff --git a/test/ps-features.c b/test/ps-features.c
new file mode 100644 (file)
index 0000000..e3cf9b4
--- /dev/null
@@ -0,0 +1,163 @@
+/*
+ * Copyright © 2006 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ */
+
+#include <stdio.h>
+#include <cairo.h>
+#include <cairo-ps.h>
+
+#include "cairo-test.h"
+
+/* This test exists to test the various features of cairo-ps.h.
+ *
+ * Currently, this test exercises the following function calls:
+ *
+ *     cairo_ps_surface_set_size
+ *     cairo_ps_surface_dsc_comment
+ *     cairo_ps_surface_dsc_begin_setup
+ *     cairo_ps_surface_dsc_begin_page_setup
+ */
+
+#define INCHES_TO_POINTS(in) ((in) * 72.0)
+#define MM_TO_POINTS(mm) ((mm) / 25.4 * 72.0)
+#define TEXT_SIZE 12
+
+static struct {
+    const char *page_size;
+    const char *page_size_alias;
+    const char *orientation;
+    double width_in_points;
+    double height_in_points;
+} pages[] = {
+    {"na_letter_8.5x11in", "letter", "portrait",
+     INCHES_TO_POINTS(8.5), INCHES_TO_POINTS(11)},
+    {"na_letter_8.5x11in", "letter", "landscape",
+     INCHES_TO_POINTS(11), INCHES_TO_POINTS(8.5)},
+    {"iso_a4_210x297mm", "a4", "portrait",
+     MM_TO_POINTS(210), MM_TO_POINTS(297)},
+    {"iso_a4_210x297mm", "a4", "landscape",
+     MM_TO_POINTS(297), MM_TO_POINTS(210)},
+    {"iso_a5_148x210mm", "a5", "portrait",
+     MM_TO_POINTS(148), MM_TO_POINTS(210)},
+    {"iso_a5_148x210mm", "a5", "landscape",
+     MM_TO_POINTS(210), MM_TO_POINTS(148)},
+    {"iso_a6_105x148mm", "a6", "portrait",
+     MM_TO_POINTS(105), MM_TO_POINTS(148)},
+    {"iso_a6_105x148mm", "a6", "landscape",
+     MM_TO_POINTS(148), MM_TO_POINTS(105)},
+    {"iso_a7_74x105mm", "a7", "portrait",
+     MM_TO_POINTS(74), MM_TO_POINTS(105)},
+    {"iso_a7_74x105mm", "a7", "landscape",
+     MM_TO_POINTS(105), MM_TO_POINTS(74)},
+    {"iso_a8_52x74mm", "a8", "portrait",
+     MM_TO_POINTS(52), MM_TO_POINTS(74)},
+    {"iso_a8_52x74mm", "a8", "landscape",
+     MM_TO_POINTS(74), MM_TO_POINTS(52)},
+    {"iso_a9_37x52mm", "a9", "portrait",
+     MM_TO_POINTS(37), MM_TO_POINTS(52)},
+    {"iso_a9_37x52mm", "a9", "landscape",
+     MM_TO_POINTS(52), MM_TO_POINTS(37)},
+    {"iso_a10_26x37mm", "a10", "portrait",
+     MM_TO_POINTS(26), MM_TO_POINTS(37)},
+    {"iso_a10_26x37mm", "a10", "landscape",
+     MM_TO_POINTS(37), MM_TO_POINTS(26)}
+};
+
+static cairo_test_status_t
+preamble (cairo_test_context_t *ctx)
+{
+    cairo_surface_t *surface;
+    cairo_t *cr;
+    cairo_status_t status;
+    const char *filename;
+    size_t i;
+    char dsc[255];
+
+    if (! (cairo_test_is_target_enabled (ctx, "ps2") ||
+          cairo_test_is_target_enabled (ctx, "ps3")))
+    {
+       return CAIRO_TEST_UNTESTED;
+    }
+
+    filename = "ps-features.out.ps";
+
+    /* We demonstrate that the initial size doesn't matter (we're
+     * passing 0,0), if we use cairo_ps_surface_set_size on the first
+     * page. */
+    surface = cairo_ps_surface_create (filename, 0, 0);
+
+    cairo_ps_surface_dsc_comment (surface, "%%Title: ps-features");
+    cairo_ps_surface_dsc_comment (surface, "%%Copyright: Copyright (C) 2006 Red Hat, Inc.");
+
+    cairo_ps_surface_dsc_begin_setup (surface);
+    cairo_ps_surface_dsc_comment (surface, "%%IncludeFeature: *PageSize letter");
+    cairo_ps_surface_dsc_comment (surface, "%%IncludeFeature: *MediaColor White");
+
+    cr = cairo_create (surface);
+
+    cairo_select_font_face (cr, CAIRO_TEST_FONT_FAMILY " Sans",
+                           CAIRO_FONT_SLANT_NORMAL,
+                           CAIRO_FONT_WEIGHT_NORMAL);
+    cairo_set_font_size (cr, TEXT_SIZE);
+
+    for (i = 0; i < ARRAY_LENGTH (pages); i++) {
+       cairo_ps_surface_set_size (surface,
+                                  pages[i].width_in_points,
+                                  pages[i].height_in_points);
+       cairo_ps_surface_dsc_begin_page_setup (surface);
+       snprintf (dsc, 255, "%%IncludeFeature: *PageSize %s", pages[i].page_size_alias);
+       cairo_ps_surface_dsc_comment (surface, dsc);
+       if (i % 2) {
+           snprintf (dsc, 255, "%%IncludeFeature: *MediaType Glossy");
+           cairo_ps_surface_dsc_comment (surface, dsc);
+       }
+
+       cairo_move_to (cr, TEXT_SIZE, TEXT_SIZE);
+       cairo_show_text (cr, pages[i].page_size);
+       cairo_show_text (cr, " - ");
+       cairo_show_text (cr, pages[i].orientation);
+       cairo_show_page (cr);
+    }
+
+    status = cairo_status (cr);
+
+    cairo_destroy (cr);
+    cairo_surface_destroy (surface);
+
+    if (status) {
+       cairo_test_log (ctx, "Failed to create ps surface for file %s: %s\n",
+                       filename, cairo_status_to_string (status));
+       return CAIRO_TEST_FAILURE;
+    }
+
+    printf ("ps-features: Please check %s to ensure it looks/prints correctly.\n", filename);
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (ps_features,
+           "Check PS specific API",
+           "ps, api", /* keywords */
+           NULL, /* requirements */
+           0, 0,
+           preamble, NULL)
diff --git a/test/ps-surface-source.c b/test/ps-surface-source.c
new file mode 100644 (file)
index 0000000..37f57ee
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Copyright © 2008 Chris Wilson
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Chris Wilson not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Chris Wilson makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * CHRIS WILSON DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL CHRIS WILSON BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+#include <cairo-ps.h>
+
+#include "surface-source.c"
+
+static cairo_surface_t *
+create_source_surface (int size)
+{
+    cairo_surface_t *surface;
+
+    surface = cairo_ps_surface_create ("ps-surface-source.out.ps", size, size);
+    cairo_surface_set_fallback_resolution (surface, 72., 72.);
+
+    return surface;
+}
+
+CAIRO_TEST (ps_surface_source,
+           "Test using a PS surface as the source",
+           "source", /* keywords */
+           NULL, /* requirements */
+           SIZE, SIZE,
+           preamble, draw)
diff --git a/test/ps2png.c b/test/ps2png.c
new file mode 100644 (file)
index 0000000..cf98aed
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ * Copyright © 2008 Carlos Garcia Campos
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Carlos Garcia Campos <carlosgc@gnome.org>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <cairo.h>
+#include <libspectre/spectre.h>
+
+#define FAIL(msg)                                                        \
+    do { fprintf (stderr, "FAIL: %s\n", msg); exit (-1); } while (0)
+
+static const char *
+_spectre_render_page (const char *filename,
+                     const char *page_label,
+                     cairo_surface_t **surface_out)
+{
+    static const cairo_user_data_key_t key;
+
+    SpectreDocument *document;
+    SpectreStatus status;
+    int width, height, stride;
+    unsigned char *pixels;
+    cairo_surface_t *surface;
+
+    document = spectre_document_new ();
+    spectre_document_load (document, filename);
+    status = spectre_document_status (document);
+    if (status) {
+       spectre_document_free (document);
+       return spectre_status_to_string (status);
+    }
+
+    if (page_label) {
+       SpectrePage *page;
+       SpectreRenderContext *rc;
+
+       page = spectre_document_get_page_by_label (document, page_label);
+       spectre_document_free (document);
+       if (page == NULL)
+           return "page not found";
+
+       spectre_page_get_size (page, &width, &height);
+       rc = spectre_render_context_new ();
+       spectre_render_context_set_page_size (rc, width, height);
+       spectre_page_render (page, rc, &pixels, &stride);
+       spectre_render_context_free (rc);
+       status = spectre_page_status (page);
+       spectre_page_free (page);
+       if (status) {
+           free (pixels);
+           return spectre_status_to_string (status);
+       }
+    } else {
+       spectre_document_get_page_size (document, &width, &height);
+       spectre_document_render (document, &pixels, &stride);
+       spectre_document_free (document);
+    }
+
+    surface = cairo_image_surface_create_for_data (pixels,
+                                                  CAIRO_FORMAT_RGB24,
+                                                  width, height,
+                                                  stride);
+    cairo_surface_set_user_data (surface, &key,
+                                pixels, (cairo_destroy_func_t) free);
+    *surface_out = surface;
+    return NULL;
+}
+
+int main
+(int argc, char *argv[])
+{
+    const char *err;
+    cairo_surface_t *surface = NULL; /* silence compiler warning */
+    cairo_status_t status;
+
+    if (argc < 3 || argc > 4)
+        FAIL ("usage: ps2png input_file.ps output_file.png [page]");
+
+    err = _spectre_render_page (argv[1], argv[3], &surface);
+    if (err != NULL)
+        FAIL (err);
+
+    status = cairo_surface_write_to_png (surface, argv[2]);
+    cairo_surface_destroy (surface);
+
+    if (status != CAIRO_STATUS_SUCCESS)
+        FAIL (cairo_status_to_string (status));
+
+    return 0;
+}
diff --git a/test/pthread-same-source.c b/test/pthread-same-source.c
new file mode 100644 (file)
index 0000000..d0eda4a
--- /dev/null
@@ -0,0 +1,173 @@
+/*
+ * Copyright 2009 Benjamin Otte
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Benjamin Otte <otte@gnome.org>
+ */
+
+#include "cairo-test.h"
+#include <pthread.h>
+
+#define N_THREADS 8
+
+#define WIDTH 64
+#define HEIGHT 8
+
+typedef struct {
+  cairo_surface_t *target;
+  cairo_surface_t *source;
+  int id;
+} thread_data_t;
+
+static void *
+draw_thread (void *arg)
+{
+    thread_data_t *thread_data = arg;
+    cairo_surface_t *surface;
+    cairo_pattern_t *pattern;
+    cairo_matrix_t pattern_matrix = { 2, 0, 0, 2, 0, 0 };
+    cairo_t *cr;
+    int x, y;
+
+    cr = cairo_create (thread_data->target);
+    cairo_surface_destroy (thread_data->target);
+
+    pattern = cairo_pattern_create_for_surface (thread_data->source);
+    cairo_surface_destroy (thread_data->source);
+    cairo_pattern_set_extend (pattern, thread_data->id % 4);
+    cairo_pattern_set_filter (pattern, thread_data->id >= 4 ? CAIRO_FILTER_BILINEAR : CAIRO_FILTER_NEAREST);
+    cairo_pattern_set_matrix (pattern, &pattern_matrix);
+
+    for (y = 0; y < HEIGHT; y++) {
+        for (x = 0; x < WIDTH; x++) {
+            cairo_save (cr);
+            cairo_translate (cr, 4 * x + 1, 4 * y + 1);
+            cairo_rectangle (cr, 0, 0, 2, 2);
+            cairo_set_source (cr, pattern);
+            cairo_fill (cr);
+            cairo_restore (cr);
+        }
+    }
+    cairo_pattern_destroy (pattern);
+
+    surface = cairo_surface_reference (cairo_get_target (cr));
+    cairo_destroy (cr);
+
+    return surface;
+}
+
+static cairo_surface_t *
+create_source (cairo_surface_t *similar)
+{
+    cairo_surface_t *source;
+    cairo_t *cr;
+    double colors[4][3] = {
+      { 0.75, 0,    0    },
+      { 0,    0.75, 0    },
+      { 0,    0,    0.75 },
+      { 0.75, 0.75, 0    }
+    };
+    int i;
+
+    source = cairo_surface_create_similar (similar,
+                                           CAIRO_CONTENT_COLOR_ALPHA,
+                                           2, 2);
+
+    cr = cairo_create (source);
+    cairo_surface_destroy (source);
+
+    for (i = 0; i < 4; i++) {
+      cairo_set_source_rgb (cr, colors[i][0], colors[i][1], colors[i][2]);
+      cairo_rectangle (cr, i % 2, i / 2, 1, 1);
+      cairo_fill (cr);
+    }
+
+    source = cairo_surface_reference (cairo_get_target (cr));
+    cairo_destroy (cr);
+
+    return source;
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    pthread_t threads[N_THREADS];
+    thread_data_t thread_data[N_THREADS];
+    cairo_test_status_t test_status = CAIRO_TEST_SUCCESS;
+    cairo_surface_t *source;
+    cairo_status_t status;
+    int i;
+
+    source = create_source (cairo_get_target (cr));
+    status = cairo_surface_status (source);
+    if (status) {
+       cairo_surface_destroy (source);
+       return cairo_test_status_from_status (cairo_test_get_context (cr),
+                                             status);
+    }
+
+    for (i = 0; i < N_THREADS; i++) {
+        thread_data[i].target = cairo_surface_create_similar (cairo_get_target (cr),
+                                                              CAIRO_CONTENT_COLOR_ALPHA,
+                                                              4 * WIDTH, 4 * HEIGHT);
+        thread_data[i].source = cairo_surface_reference (source);
+        thread_data[i].id = i;
+        if (pthread_create (&threads[i], NULL, draw_thread, &thread_data[i]) != 0) {
+           threads[i] = pthread_self (); /* to indicate error */
+            cairo_surface_destroy (thread_data[i].target);
+            cairo_surface_destroy (thread_data[i].source);
+            test_status = CAIRO_TEST_FAILURE;
+           break;
+        }
+    }
+
+    cairo_surface_destroy (source);
+
+    cairo_set_source_rgb (cr, 0.5, 0.5, 0.5);
+    cairo_paint (cr);
+
+    for (i = 0; i < N_THREADS; i++) {
+       void *surface;
+
+        if (pthread_equal (threads[i], pthread_self ()))
+            break;
+
+        if (pthread_join (threads[i], &surface) == 0) {
+           cairo_set_source_surface (cr, surface, 0, 0);
+           cairo_surface_destroy (surface);
+           cairo_paint (cr);
+
+           cairo_translate (cr, 0, 4 * HEIGHT);
+       } else {
+            test_status = CAIRO_TEST_FAILURE;
+       }
+    }
+
+    return test_status;
+}
+
+CAIRO_TEST (pthread_same_source,
+           "Use the same source for drawing in different threads",
+           "threads", /* keywords */
+           NULL, /* requirements */
+           4 * WIDTH, 4 * HEIGHT * N_THREADS,
+           NULL, draw)
diff --git a/test/pthread-show-text.c b/test/pthread-show-text.c
new file mode 100644 (file)
index 0000000..0e070b7
--- /dev/null
@@ -0,0 +1,142 @@
+/*
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ */
+
+/* Test case for bug #4299:
+
+   Assertion fails in "cairo-font.c" when using multithreads
+   https://bugs.freedesktop.org/show_bug.cgi?id=4299
+*/
+
+#include "cairo-test.h"
+
+#include <string.h>
+#include <stdlib.h>
+#include <pthread.h>
+
+#define N_THREADS 8
+#define NUM_ITERATIONS 40
+
+#define WIDTH 400
+#define HEIGHT 42
+
+typedef struct {
+  cairo_surface_t *target;
+  int id;
+} thread_data_t;
+
+static void *
+draw_thread (void *arg)
+{
+    const char *text = "Hello world. ";
+    thread_data_t *thread_data = arg;
+    cairo_surface_t *surface;
+    cairo_font_extents_t extents;
+    cairo_t *cr;
+    int i;
+
+    cr = cairo_create (thread_data->target);
+    cairo_surface_destroy (thread_data->target);
+
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_paint (cr);
+    cairo_set_source_rgb (cr, 0, 0, 0);
+
+    cairo_select_font_face (cr, "serif",
+                           CAIRO_FONT_SLANT_NORMAL,
+                           CAIRO_FONT_WEIGHT_NORMAL);
+    cairo_set_font_size (cr, NUM_ITERATIONS);
+    cairo_font_extents (cr, &extents);
+
+    cairo_move_to (cr, 1, HEIGHT - extents.descent - 1);
+
+    for (i = 0; i < NUM_ITERATIONS; i++) {
+       char buf[2];
+
+        cairo_select_font_face (cr, "serif",
+                               CAIRO_FONT_SLANT_NORMAL,
+                               CAIRO_FONT_WEIGHT_NORMAL);
+       cairo_set_font_size (cr, i);
+
+       buf[0] = text[i%strlen(text)];
+       buf[1] = '\0';
+       cairo_show_text (cr, buf);
+    }
+
+    surface = cairo_surface_reference (cairo_get_target (cr));
+    cairo_destroy (cr);
+
+    return surface;
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    pthread_t threads[N_THREADS];
+    thread_data_t thread_data[N_THREADS];
+    cairo_test_status_t test_status = CAIRO_TEST_SUCCESS;
+    int i;
+
+    for (i = 0; i < N_THREADS; i++) {
+        thread_data[i].target = cairo_surface_create_similar (cairo_get_target (cr),
+                                                             cairo_surface_get_content (cairo_get_target (cr)),
+                                                             WIDTH, HEIGHT);
+        thread_data[i].id = i;
+        if (pthread_create (&threads[i], NULL, draw_thread, &thread_data[i]) != 0) {
+           threads[i] = pthread_self (); /* to indicate error */
+            cairo_surface_destroy (thread_data[i].target);
+            test_status = CAIRO_TEST_FAILURE;
+           break;
+        }
+    }
+
+    cairo_set_source_rgb (cr, 0.5, 0.5, 0.5);
+    cairo_paint (cr);
+
+    for (i = 0; i < N_THREADS; i++) {
+       void *surface;
+
+        if (pthread_equal (threads[i], pthread_self ()))
+            break;
+
+        if (pthread_join (threads[i], &surface) == 0) {
+           cairo_set_source_surface (cr, surface, 0, 0);
+           cairo_surface_destroy (surface);
+           cairo_paint (cr);
+
+           cairo_translate (cr, 0, HEIGHT);
+       } else {
+            test_status = CAIRO_TEST_FAILURE;
+       }
+    }
+
+    return test_status;
+}
+
+CAIRO_TEST (pthread_show_text,
+           "Concurrent stress test of the cairo_show_text().",
+           "thread, text", /* keywords */
+           NULL, /* requirements */
+           WIDTH, HEIGHT * N_THREADS,
+           NULL, draw)
diff --git a/test/pthread-similar.c b/test/pthread-similar.c
new file mode 100644 (file)
index 0000000..a5f5e3b
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2009 Benjamin Otte
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Benjamin Otte <otte@gnome.org>
+ */
+
+#include "cairo-test.h"
+#include <pthread.h>
+
+#define N_THREADS 8
+
+#define WIDTH 64
+#define HEIGHT 8
+
+static void *
+draw_thread (void *arg)
+{
+    cairo_surface_t *surface = arg;
+    cairo_t *cr;
+    int x, y;
+
+    cr = cairo_create (surface);
+    cairo_surface_destroy (surface);
+
+    for (y = 0; y < HEIGHT; y++) {
+        for (x = 0; x < WIDTH; x++) {
+            cairo_rectangle (cr, x, y, 1, 1);
+            cairo_set_source_rgba (cr, 0, 0.75, 0.75, (double) x / WIDTH);
+            cairo_fill (cr);
+        }
+    }
+
+    surface = cairo_surface_reference (cairo_get_target (cr));
+    cairo_destroy (cr);
+
+    return surface;
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    pthread_t threads[N_THREADS];
+    cairo_test_status_t test_status = CAIRO_TEST_SUCCESS;
+    int i;
+
+    for (i = 0; i < N_THREADS; i++) {
+       cairo_surface_t *surface;
+
+        surface = cairo_surface_create_similar (cairo_get_target (cr),
+                                               CAIRO_CONTENT_COLOR,
+                                               WIDTH, HEIGHT);
+        if (pthread_create (&threads[i], NULL, draw_thread, surface) != 0) {
+           threads[i] = pthread_self ();
+            test_status = cairo_test_status_from_status (cairo_test_get_context (cr),
+                                                        cairo_surface_status (surface));
+            cairo_surface_destroy (surface);
+           break;
+        }
+    }
+
+    for (i = 0; i < N_THREADS; i++) {
+       void *surface;
+
+        if (pthread_equal (threads[i], pthread_self ()))
+            break;
+
+        if (pthread_join (threads[i], &surface) == 0) {
+           cairo_set_source_surface (cr, surface, 0, 0);
+           cairo_surface_destroy (surface);
+           cairo_paint (cr);
+
+           cairo_translate (cr, 0, HEIGHT);
+       } else {
+            test_status = CAIRO_TEST_FAILURE;
+       }
+    }
+
+    return test_status;
+}
+
+CAIRO_TEST (pthread_similar,
+           "Draw lots of 1x1 rectangles on similar surfaces in lots of threads",
+           "threads", /* keywords */
+           NULL, /* requirements */
+           WIDTH, HEIGHT * N_THREADS,
+           NULL, draw)
diff --git a/test/push-group-color.c b/test/push-group-color.c
new file mode 100644 (file)
index 0000000..1bc5bca
--- /dev/null
@@ -0,0 +1,141 @@
+/*
+ * Copyright © 2005 Mozilla Corporation
+ * Copyright © 2009 Intel Corporation
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Mozilla Corporation not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Mozilla Corporation makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * MOZILLA CORPORATION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL MOZILLA CORPORATION BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Vladimir Vukicevic <vladimir@pobox.com>
+ *         Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+
+#define UNIT_SIZE 100
+#define PAD 5
+#define INNER_PAD 10
+
+#define WIDTH (UNIT_SIZE + PAD) + PAD
+#define HEIGHT (UNIT_SIZE + PAD) + PAD
+
+static cairo_pattern_t *
+argb32_source (void)
+{
+    cairo_surface_t *surface;
+    cairo_pattern_t *pattern;
+    cairo_t *cr;
+
+    surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 16, 16);
+    cr = cairo_create (surface);
+    cairo_surface_destroy (surface);
+
+    cairo_set_source_rgb (cr, 1, 0, 0);
+    cairo_rectangle (cr, 8, 0, 8, 8);
+    cairo_fill (cr);
+
+    cairo_set_source_rgb (cr, 0, 1, 0);
+    cairo_rectangle (cr, 0, 8, 8, 8);
+    cairo_fill (cr);
+
+    cairo_set_source_rgb (cr, 0, 0, 1);
+    cairo_rectangle (cr, 8, 8, 8, 8);
+    cairo_fill (cr);
+
+    pattern = cairo_pattern_create_for_surface (cairo_get_target (cr));
+    cairo_destroy (cr);
+
+    return pattern;
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_pattern_t *gradient, *image;
+
+    cairo_set_source_rgb (cr, 1,1,1);
+    cairo_paint (cr);
+
+    cairo_translate (cr, PAD, PAD);
+
+    /* clip to the unit size */
+    cairo_rectangle (cr, 0, 0,
+                    UNIT_SIZE, UNIT_SIZE);
+    cairo_clip (cr);
+
+    cairo_rectangle (cr, 0, 0,
+                    UNIT_SIZE, UNIT_SIZE);
+    cairo_set_source_rgba (cr, 0, 0, 0, 1);
+    cairo_set_line_width (cr, 2);
+    cairo_stroke (cr);
+
+    /* start a group */
+    cairo_push_group_with_content (cr, CAIRO_CONTENT_COLOR);
+
+    /* draw a gradient background */
+    cairo_save (cr);
+    cairo_translate (cr, INNER_PAD, INNER_PAD);
+    cairo_new_path (cr);
+    cairo_rectangle (cr, 0, 0,
+                    UNIT_SIZE - (INNER_PAD*2), UNIT_SIZE - (INNER_PAD*2));
+    gradient = cairo_pattern_create_linear (UNIT_SIZE - (INNER_PAD*2), 0,
+                                            UNIT_SIZE - (INNER_PAD*2), UNIT_SIZE - (INNER_PAD*2));
+    cairo_pattern_add_color_stop_rgba (gradient, 0.0, 0.3, 0.3, 0.3, 1.0);
+    cairo_pattern_add_color_stop_rgba (gradient, 1.0, 1.0, 1.0, 1.0, 1.0);
+    cairo_set_source (cr, gradient);
+    cairo_pattern_destroy (gradient);
+    cairo_fill (cr);
+    cairo_restore (cr);
+
+    /* draw diamond */
+    cairo_move_to (cr, UNIT_SIZE / 2, 0);
+    cairo_line_to (cr, UNIT_SIZE    , UNIT_SIZE / 2);
+    cairo_line_to (cr, UNIT_SIZE / 2, UNIT_SIZE);
+    cairo_line_to (cr, 0            , UNIT_SIZE / 2);
+    cairo_close_path (cr);
+    cairo_set_source_rgba (cr, 0, 0, 1, 1);
+    cairo_fill (cr);
+
+    /* draw circle */
+    cairo_arc (cr,
+              UNIT_SIZE / 2, UNIT_SIZE / 2,
+              UNIT_SIZE / 3.5,
+              0, M_PI * 2);
+    cairo_set_source_rgba (cr, 1, 0, 0, 1);
+    cairo_fill (cr);
+
+    /* and put the image on top */
+    cairo_translate (cr, UNIT_SIZE/2 - 8, UNIT_SIZE/2 - 8);
+    image = argb32_source ();
+    cairo_set_source (cr, image);
+    cairo_pattern_destroy (image);
+    cairo_paint (cr);
+
+    cairo_pop_group_to_source (cr);
+    cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+    cairo_paint (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (push_group_color,
+           "Verify that cairo_push_group_with_content works.",
+           "group", /* keywords */
+           NULL, /* requirements */
+           WIDTH, HEIGHT,
+           NULL, draw)
diff --git a/test/push-group-path-offset.c b/test/push-group-path-offset.c
new file mode 100644 (file)
index 0000000..6b73ca4
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2010 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Benjamin Otte <otte@gnome.org>
+ */
+
+#include "cairo-test.h"
+
+#define CLIP_OFFSET 15
+#define CLIP_SIZE 20
+
+#define WIDTH 50
+#define HEIGHT 50
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    /* Neutral gray background */
+    cairo_set_source_rgb (cr, 0.51613, 0.55555, 0.51613);
+    cairo_paint (cr);
+
+    /* the rest uses CAIRO_OPERATOR_SOURCE so we see better when something goes wrong */
+    cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+
+    /* add a rectangle */
+    cairo_rectangle (cr, CLIP_OFFSET, CLIP_OFFSET, CLIP_SIZE, CLIP_SIZE);
+
+    /* clip to the rectangle */
+    cairo_clip_preserve (cr);
+
+    /* push a group. We now have a device offset. */
+    cairo_push_group (cr);
+
+    /* push a group again. This is where the bug used to happen. */
+    cairo_push_group (cr);
+
+    /* draw something */
+    cairo_set_source_rgb (cr, 1, 0, 0);
+    cairo_fill_preserve (cr);
+
+    /* make sure the stuff we drew ends up on the output */
+    cairo_pop_group_to_source (cr);
+    cairo_fill_preserve (cr);
+
+    cairo_pop_group_to_source (cr);
+    cairo_fill_preserve (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (push_group_path_offset,
+           "Exercises a bug in Cairo 1.9 where existing paths applied the target's"
+            " device offset twice when cairo_push_group() was called.",
+           "group, path", /* keywords */
+           NULL, /* requirements */
+           WIDTH, HEIGHT,
+           NULL, draw)
diff --git a/test/push-group.c b/test/push-group.c
new file mode 100644 (file)
index 0000000..8e6ce19
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ * Copyright © 2005 Mozilla Corporation
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Mozilla Corporation not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Mozilla Corporation makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * MOZILLA CORPORATION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL MOZILLA CORPORATION BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Vladimir Vukicevic <vladimir@pobox.com>
+ */
+
+#include "cairo-test.h"
+
+#define UNIT_SIZE 100
+#define PAD 5
+#define INNER_PAD 10
+
+#define WIDTH (UNIT_SIZE + PAD) + PAD
+#define HEIGHT (UNIT_SIZE + PAD) + PAD
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_pattern_t *gradient;
+    int i, j;
+
+    gradient = cairo_pattern_create_linear (UNIT_SIZE - (INNER_PAD*2), 0,
+                                            UNIT_SIZE - (INNER_PAD*2), UNIT_SIZE - (INNER_PAD*2));
+    cairo_pattern_add_color_stop_rgba (gradient, 0.0, 0.3, 0.3, 0.3, 1.0);
+    cairo_pattern_add_color_stop_rgba (gradient, 1.0, 1.0, 1.0, 1.0, 1.0);
+
+    for (j = 0; j < 1; j++) {
+        for (i = 0; i < 1; i++) {
+            double x = (i * UNIT_SIZE) + (i + 1) * PAD;
+            double y = (j * UNIT_SIZE) + (j + 1) * PAD;
+
+            cairo_save (cr);
+
+            cairo_translate (cr, x, y);
+
+            /* draw a gradient background */
+            cairo_save (cr);
+            cairo_translate (cr, INNER_PAD, INNER_PAD);
+            cairo_new_path (cr);
+            cairo_rectangle (cr, 0, 0,
+                             UNIT_SIZE - (INNER_PAD*2), UNIT_SIZE - (INNER_PAD*2));
+            cairo_set_source (cr, gradient);
+            cairo_fill (cr);
+            cairo_restore (cr);
+
+            /* clip to the unit size */
+            cairo_rectangle (cr, 0, 0,
+                             UNIT_SIZE, UNIT_SIZE);
+            cairo_clip (cr);
+
+            cairo_rectangle (cr, 0, 0,
+                             UNIT_SIZE, UNIT_SIZE);
+            cairo_set_source_rgba (cr, 0, 0, 0, 1);
+            cairo_set_line_width (cr, 2);
+            cairo_stroke (cr);
+
+            /* start a group */
+            cairo_push_group (cr);
+
+            /* draw diamond */
+            cairo_move_to (cr, UNIT_SIZE / 2, 0);
+            cairo_line_to (cr, UNIT_SIZE    , UNIT_SIZE / 2);
+            cairo_line_to (cr, UNIT_SIZE / 2, UNIT_SIZE);
+            cairo_line_to (cr, 0            , UNIT_SIZE / 2);
+            cairo_close_path (cr);
+            cairo_set_source_rgba (cr, 0, 0, 1, 1);
+            cairo_fill (cr);
+
+            /* draw circle */
+            cairo_arc (cr,
+                       UNIT_SIZE / 2, UNIT_SIZE / 2,
+                       UNIT_SIZE / 3.5,
+                       0, M_PI * 2);
+            cairo_set_source_rgba (cr, 1, 0, 0, 1);
+            cairo_fill (cr);
+
+            cairo_pop_group_to_source (cr);
+            cairo_paint_with_alpha (cr, 0.5);
+
+            cairo_restore (cr);
+        }
+    }
+
+    cairo_pattern_destroy (gradient);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (push_group,
+           "Verify that cairo_push_group works.",
+           "group", /* keywords */
+           NULL, /* requirements */
+           WIDTH, HEIGHT,
+           NULL, draw)
diff --git a/test/quartz-surface-source.c b/test/quartz-surface-source.c
new file mode 100644 (file)
index 0000000..b0c86d0
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright © 2008 Chris Wilson
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Chris Wilson not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Chris Wilson makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * CHRIS WILSON DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL CHRIS WILSON BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+#include "cairo-quartz.h"
+
+#include "surface-source.c"
+
+static cairo_surface_t *
+create_source_surface (int size)
+{
+    return cairo_quartz_surface_create (CAIRO_FORMAT_ARGB32, size, size);
+}
+
+CAIRO_TEST (quartz_surface_source,
+           "Test using a Quartz surface as the source",
+           "source", /* keywords */
+           NULL, /* requirements */
+           SIZE, SIZE,
+           preamble, draw)
diff --git a/test/radial-gradient-extend.c b/test/radial-gradient-extend.c
new file mode 100644 (file)
index 0000000..c32a2c3
--- /dev/null
@@ -0,0 +1,92 @@
+/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/*
+ * Copyright 2010 Andrea Canciani
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Andrea Canciani <ranma42@gmail.com>
+ */
+
+#include "cairo-test.h"
+
+#define NUM_EXTEND 4
+#define HEIGHT 16
+#define WIDTH 16
+#define PAD 3
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_pattern_t *pattern;
+    unsigned int i, j;
+
+    cairo_extend_t extend[NUM_EXTEND] = {
+       CAIRO_EXTEND_NONE,
+       CAIRO_EXTEND_REPEAT,
+       CAIRO_EXTEND_REFLECT,
+       CAIRO_EXTEND_PAD
+    };
+
+    cairo_test_paint_checkered (cr);
+
+    pattern = cairo_pattern_create_radial (WIDTH / 2, HEIGHT / 2, 0, WIDTH / 2, HEIGHT / 2, 2 * PAD);
+
+    cairo_pattern_add_color_stop_rgb (pattern, 0, 0, 0, 1);
+    cairo_pattern_add_color_stop_rgb (pattern, 1, 0, 0, 1);
+
+    cairo_translate (cr, PAD, PAD);
+
+    for (i = 0; i < 2; i++) {
+        cairo_save (cr);
+       
+       for (j = 0; j < NUM_EXTEND; j++) {
+           cairo_pattern_set_extend (pattern, extend[j]);
+
+           cairo_reset_clip (cr);
+           cairo_rectangle (cr, 0, 0, WIDTH, HEIGHT);
+           cairo_clip (cr);
+
+           if (i & 1) {
+               cairo_set_source_rgb (cr, 0, 1, 0);
+               cairo_mask (cr, pattern);
+           } else {
+               cairo_set_source (cr, pattern);
+               cairo_paint (cr);
+           }
+
+           cairo_translate (cr, WIDTH+PAD, 0);
+       }
+
+       cairo_restore (cr);
+       cairo_translate (cr, 0, HEIGHT+PAD);
+    }
+
+    cairo_pattern_destroy (pattern);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (radial_gradient_extend,
+           "Tests gradient to solid reduction of radial gradients",
+           "radial, pattern, extend", /* keywords */
+           NULL, /* requirements */
+           (WIDTH+PAD) * NUM_EXTEND + PAD, 2*(HEIGHT + PAD) + PAD,
+           NULL, draw)
diff --git a/test/radial-gradient.c b/test/radial-gradient.c
new file mode 100644 (file)
index 0000000..026876b
--- /dev/null
@@ -0,0 +1,256 @@
+/*
+ * Copyright © 2005, 2007 Red Hat, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ */
+
+#include "cairo-test.h"
+
+#define NUM_GRADIENTS 7
+#define NUM_EXTEND 4
+#define SIZE 120
+#define WIDTH (SIZE * NUM_GRADIENTS)
+#define HEIGHT (SIZE * NUM_EXTEND)
+
+typedef void (*composite_t)(cairo_t *cr, cairo_pattern_t *pattern);
+typedef void (*add_stops_t)(cairo_pattern_t *pattern);
+
+/*
+ * We want to test all the possible relative positions of the start
+ * and end circle:
+ *
+ *  - The start circle can be smaller/equal/bigger than the end
+ *    circle. A radial gradient can be classified in one of these
+ *    three cases depending on the sign of dr.
+ *
+ *  - The smaller circle can be completely inside/internally
+ *    tangent/outside (at least in part) of the bigger circle. This
+ *    classification is the same as the one which can be computed by
+ *    examining the sign of a = (dx^2 + dy^2 - dr^2).
+ *
+ *  - If the two circles have the same size, neither can be inside or
+ *    internally tangent
+ *
+ * This test draws radial gradients whose circles always have the same
+ * centers (0, 0) and (1, 0), but with different radiuses. From left
+ * to right:
+ *
+ * - Small start circle completely inside the end circle
+ *     0.25 -> 1.75; dr =  1.5 > 0; a = 1 - 1.50^2 < 0
+ *
+ * - Small start circle internally tangent to the end circle
+ *     0.50 -> 1.50; dr =  1.0 > 0; a = 1 - 1.00^2 = 0
+ *
+ * - Small start circle outside of the end circle
+ *     0.50 -> 1.00; dr =  0.5 > 0; a = 1 - 0.50^2 > 0
+ *
+ * - Start circle with the same size as the end circle
+ *     1.00 -> 1.00; dr =  0.0 = 0; a = 1 - 0.00^2 > 0
+ *
+ * - Small end circle outside of the start circle
+ *     1.00 -> 0.50; dr = -0.5 > 0; a = 1 - 0.50^2 > 0
+ *
+ * - Small end circle internally tangent to the start circle
+ *     1.50 -> 0.50; dr = -1.0 > 0; a = 1 - 1.00^2 = 0
+ *
+ * - Small end circle completely inside the start circle
+ *     1.75 -> 0.25; dr = -1.5 > 0; a = 1 - 1.50^2 < 0
+ *
+ */
+
+static const double radiuses[NUM_GRADIENTS] = {
+    0.25,
+    0.50,
+    0.50,
+    1.00,
+    1.00,
+    1.50,
+    1.75
+};
+
+static cairo_pattern_t *
+create_pattern (int index)
+{
+    double x0, x1, radius0, radius1, left, right, center;
+
+    x0 = 0;
+    x1 = 1;
+    radius0 = radiuses[index];
+    radius1 = radiuses[NUM_GRADIENTS - index - 1];
+
+    /* center the gradient */
+    left = MIN (x0 - radius0, x1 - radius1);
+    right = MAX (x0 + radius0, x1 + radius1);
+    center = (left + right) * 0.5;
+    x0 -= center;
+    x1 -= center;
+
+    /* scale to make it fit within a 1x1 rect centered in (0,0) */
+    x0 *= 0.25;
+    x1 *= 0.25;
+    radius0 *= 0.25;
+    radius1 *= 0.25;
+
+    return cairo_pattern_create_radial (x0, 0, radius0, x1, 0, radius1);
+}
+
+static void
+pattern_add_stops (cairo_pattern_t *pattern)
+{
+    cairo_pattern_add_color_stop_rgba (pattern, 0.0,        1, 0, 0, 0.75);
+    cairo_pattern_add_color_stop_rgba (pattern, sqrt (0.5), 0, 1, 0, 0);
+    cairo_pattern_add_color_stop_rgba (pattern, 1.0,        0, 0, 1, 1);
+}
+
+static void
+pattern_add_single_stop (cairo_pattern_t *pattern)
+{
+    cairo_pattern_add_color_stop_rgba (pattern, 0.25, 1, 0, 0, 1);
+}
+
+
+static cairo_test_status_t
+draw (cairo_t *cr, add_stops_t add_stops, composite_t composite)
+{
+    int i, j;
+    cairo_extend_t extend[NUM_EXTEND] = {
+       CAIRO_EXTEND_NONE,
+       CAIRO_EXTEND_REPEAT,
+       CAIRO_EXTEND_REFLECT,
+       CAIRO_EXTEND_PAD
+    };
+
+    cairo_scale (cr, SIZE, SIZE);
+    cairo_translate (cr, 0.5, 0.5);
+
+    for (j = 0; j < NUM_EXTEND; j++) {
+       cairo_save (cr);
+       for (i = 0; i < NUM_GRADIENTS; i++) {
+           cairo_pattern_t *pattern;
+
+           pattern = create_pattern (i);
+           add_stops (pattern);
+           cairo_pattern_set_extend (pattern, extend[j]);
+
+           cairo_save (cr);
+           cairo_rectangle (cr, -0.5, -0.5, 1, 1);
+           cairo_clip (cr);
+           composite (cr, pattern);
+           cairo_restore (cr);
+           cairo_pattern_destroy (pattern);
+
+           cairo_translate (cr, 1, 0);
+       }
+       cairo_restore (cr);
+       cairo_translate (cr, 0, 1);
+    }
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+
+static void
+composite_simple (cairo_t *cr, cairo_pattern_t *pattern)
+{
+    cairo_set_source (cr, pattern);
+    cairo_paint (cr);
+}
+
+static void
+composite_mask (cairo_t *cr, cairo_pattern_t *pattern)
+{
+    cairo_set_source_rgb (cr, 1, 0, 1);
+    cairo_mask (cr, pattern);
+}
+
+
+static cairo_test_status_t
+draw_simple (cairo_t *cr, int width, int height)
+{
+    cairo_test_paint_checkered (cr);
+    return draw (cr, pattern_add_stops, composite_simple);
+}
+
+static cairo_test_status_t
+draw_mask (cairo_t *cr, int width, int height)
+{
+    cairo_test_paint_checkered (cr);
+    return draw (cr, pattern_add_stops, composite_mask);
+}
+
+static cairo_test_status_t
+draw_source (cairo_t *cr, int width, int height)
+{
+    cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+    return draw (cr, pattern_add_stops, composite_simple);
+}
+
+
+static cairo_test_status_t
+draw_mask_source (cairo_t *cr, int width, int height)
+{
+    cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+    return draw (cr, pattern_add_stops, composite_mask);
+}
+
+static cairo_test_status_t
+draw_one_stop (cairo_t *cr, int width, int height)
+{
+    cairo_test_paint_checkered (cr);
+    return draw (cr, pattern_add_single_stop, composite_simple);
+}
+
+CAIRO_TEST (radial_gradient,
+           "Simple test of radial gradients",
+           "gradient", /* keywords */
+           NULL, /* requirements */
+           WIDTH, HEIGHT,
+           NULL, draw_simple)
+
+CAIRO_TEST (radial_gradient_mask,
+           "Simple test of radial gradients using a MASK",
+           "gradient,mask", /* keywords */
+           NULL, /* requirements */
+           WIDTH, HEIGHT,
+           NULL, draw_mask)
+
+CAIRO_TEST (radial_gradient_source,
+           "Simple test of radial gradients using the SOURCE operator",
+           "gradient,source", /* keywords */
+           NULL, /* requirements */
+           WIDTH, HEIGHT,
+           NULL, draw_source)
+
+CAIRO_TEST (radial_gradient_mask_source,
+           "Simple test of radial gradients using a MASK with a SOURCE operator",
+           "gradient,mask,source", /* keywords */
+           NULL, /* requirements */
+           WIDTH, HEIGHT,
+           NULL, draw_mask_source)
+
+CAIRO_TEST (radial_gradient_one_stop,
+           "Tests radial gradients with a single stop",
+           "gradient,radial", /* keywords */
+           NULL, /* requirements */
+           WIDTH, HEIGHT,
+           NULL, draw_one_stop)
diff --git a/test/radial-outer-focus.c b/test/radial-outer-focus.c
new file mode 100644 (file)
index 0000000..e038947
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Krzysztof Kosi\u0144ski <tweenk.pl@gmail.com>
+ */
+
+/* Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=40918 */
+
+#include "cairo-test.h"
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_pattern_t *radial;
+    double angle;
+    int i, j;
+
+    cairo_set_source_rgb (cr, 0, 0, 1);
+    cairo_paint (cr);
+
+    angle = 0.0;
+
+    for (i = 0; i < 4; i++) {
+       for (j = 0; j < 4; j++) {
+           cairo_save (cr);
+           cairo_rectangle (cr, 100*i, 100*j, 100, 100);
+           cairo_clip (cr);
+
+           radial = cairo_pattern_create_radial (cos (angle), sin (angle), 0,
+                                                 0, 0, 1);
+           cairo_pattern_add_color_stop_rgb (radial, 0.0, 1, 0, 0);
+           cairo_pattern_add_color_stop_rgb (radial, 1.0, 0, 1, 0);
+
+           cairo_translate (cr, 100*i+50, 100*j+50);
+           cairo_scale (cr, 50, -50);
+           cairo_set_source (cr, radial);
+           cairo_pattern_destroy (radial);
+
+           cairo_paint(cr);
+           cairo_restore (cr);
+
+           angle += M_PI/17;
+       }
+    }
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (radial_outer_focus,
+           "Exercises the condition of rendering a radial gradial on its outer focus",
+           "radial", /* keywords */
+           NULL, /* requirements */
+           400, 400,
+           NULL, draw)
diff --git a/test/random-clips.c b/test/random-clips.c
new file mode 100644 (file)
index 0000000..31d7d6d
--- /dev/null
@@ -0,0 +1,232 @@
+/*
+ * Copyright © 2006 M Joonas Pihlaja
+ * Copyright © 2011 Chris Wilson
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Authors:
+ *   M Joonas Pihlaja <jpihlaja@cc.helsinki.fi>
+ *   Chris Wilson <chris@chris-wilson.co.uk>
+ */
+#include "cairo-test.h"
+
+#define SIZE 512
+#define STEP (512+2)
+#define NUM_SEGMENTS 128
+
+static uint32_t state;
+
+static double
+uniform_random (double minval, double maxval)
+{
+    static uint32_t const poly = 0x9a795537U;
+    uint32_t n = 32;
+    while (n-->0)
+       state = 2*state < state ? (2*state ^ poly) : 2*state;
+    return minval + state * (maxval - minval) / 4294967296.0;
+}
+
+static void nz_path (cairo_t *cr)
+{
+    int i;
+
+    state = 0xc0ffee;
+
+    cairo_move_to (cr, 0, 0);
+    for (i = 0; i < NUM_SEGMENTS; i++) {
+       double x = uniform_random (0, SIZE);
+       double y = uniform_random (0, SIZE);
+       cairo_line_to (cr, x, y);
+    }
+    cairo_close_path (cr);
+}
+
+static void region_path (cairo_t *cr)
+{
+    int i;
+
+    state = 0xc0ffee;
+
+    for (i = 0; i < NUM_SEGMENTS; i++) {
+       int x = uniform_random (0, SIZE);
+       int y = uniform_random (0, SIZE);
+       int w = uniform_random (0, 40);
+       int h = uniform_random (0, 40);
+       cairo_rectangle (cr, x, y, w, h);
+    }
+}
+
+static void rectangle_path (cairo_t *cr)
+{
+    int i;
+
+    state = 0xc0ffee;
+
+    for (i = 0; i < NUM_SEGMENTS; i++) {
+       double x = uniform_random (0, SIZE);
+       double y = uniform_random (0, SIZE);
+       double w = uniform_random (0, 40);
+       double h = uniform_random (0, 40);
+       cairo_rectangle (cr, x, y, w, h);
+    }
+}
+
+static void arc_path (cairo_t *cr)
+{
+    int i;
+
+    state = 0xc0ffee;
+
+    for (i = 0; i < NUM_SEGMENTS; i++) {
+       double x = uniform_random (0, SIZE);
+       double y = uniform_random (0, SIZE);
+       double r = uniform_random (0, 20);
+       cairo_new_sub_path (cr);
+       cairo_arc (cr, x, y, r, 0, 2*M_PI);
+    }
+}
+
+
+static void nz_fill_stroke (cairo_t *cr)
+{
+    nz_path (cr);
+
+    cairo_set_source_rgb (cr, 1, 0, 0);
+    cairo_fill_preserve (cr);
+    cairo_set_source_rgb (cr, 0, 1, 0);
+    cairo_set_line_width (cr, 1.0);
+    cairo_stroke (cr);
+}
+
+static void clip_to_quadrant (cairo_t *cr)
+{
+    cairo_rectangle (cr, 0, 0, SIZE, SIZE);
+    cairo_clip (cr);
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_set_source_rgb (cr, 0, 0, 0);
+    cairo_paint (cr);
+
+    cairo_set_fill_rule (cr, CAIRO_FILL_RULE_WINDING);
+
+    state = 0xc0ffee;
+    cairo_translate (cr, 1, 1);
+
+    /* no clipping */
+    cairo_save (cr); {
+       clip_to_quadrant (cr);
+
+       nz_fill_stroke (cr);
+    } cairo_restore (cr);
+
+    cairo_translate (cr, STEP, 0);
+
+    /* random clipping */
+    cairo_save (cr); {
+       clip_to_quadrant (cr);
+
+       nz_path (cr);
+       cairo_clip (cr);
+
+       nz_fill_stroke (cr);
+
+       cairo_set_source_rgba (cr, 1, 1, 1, 0.5);
+       cairo_paint (cr);
+    } cairo_restore (cr);
+
+    cairo_translate (cr, STEP, 0);
+
+    /* regional clipping */
+    cairo_save (cr); {
+       clip_to_quadrant (cr);
+
+       region_path (cr);
+       cairo_clip (cr);
+
+       nz_fill_stroke (cr);
+
+       cairo_set_source_rgba (cr, 1, 1, 1, 0.5);
+       cairo_paint (cr);
+    } cairo_restore (cr);
+
+    cairo_translate (cr, -2*STEP, STEP);
+
+    /* rectangular clipping */
+    cairo_save (cr); {
+       clip_to_quadrant (cr);
+
+       rectangle_path (cr);
+       cairo_clip (cr);
+
+       nz_fill_stroke (cr);
+
+       cairo_set_source_rgba (cr, 1, 1, 1, 0.5);
+       cairo_paint (cr);
+    } cairo_restore (cr);
+
+    cairo_translate (cr, STEP, 0);
+
+    /* circular clipping */
+    cairo_save (cr); {
+       clip_to_quadrant (cr);
+
+       arc_path (cr);
+       cairo_clip (cr);
+
+       nz_fill_stroke (cr);
+
+       cairo_set_source_rgba (cr, 1, 1, 1, 0.5);
+       cairo_paint (cr);
+    } cairo_restore (cr);
+
+    cairo_translate (cr, STEP, 0);
+
+    /* all-of-the-above clipping */
+    cairo_save (cr); {
+       clip_to_quadrant (cr);
+
+       nz_path (cr);
+       cairo_clip (cr);
+       region_path (cr);
+       cairo_clip (cr);
+       rectangle_path (cr);
+       cairo_clip (cr);
+       arc_path (cr);
+       cairo_clip (cr);
+
+       nz_fill_stroke (cr);
+
+       cairo_set_source_rgba (cr, 1, 1, 1, 0.5);
+       cairo_paint (cr);
+    } cairo_restore (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (random_clip,
+           "Tests the clip generation and intersection computation",
+           "trap, clip", /* keywords */
+           NULL, /* requirements */
+           3*STEP+2, 2*STEP+2,
+           NULL, draw)
diff --git a/test/random-intersections-curves-eo.c b/test/random-intersections-curves-eo.c
new file mode 100644 (file)
index 0000000..84e9a75
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * Copyright © 2006 M Joonas Pihlaja
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: M Joonas Pihlaja <jpihlaja@cc.helsinki.fi>
+ */
+#include "cairo-test.h"
+
+#define SIZE 512
+#define NUM_SEGMENTS 128
+
+static uint32_t state;
+
+static double
+uniform_random (double minval, double maxval)
+{
+    static uint32_t const poly = 0x9a795537U;
+    uint32_t n = 32;
+    while (n-->0)
+       state = 2*state < state ? (2*state ^ poly) : 2*state;
+    return minval + state * (maxval - minval) / 4294967296.0;
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    int i;
+
+    cairo_set_source_rgb (cr, 0, 0, 0);
+    cairo_paint (cr);
+
+    state = 0x12345678;
+    cairo_translate (cr, 1, 1);
+    cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD);
+
+    cairo_move_to (cr, 0, 0);
+    for (i = 0; i < NUM_SEGMENTS; i++) {
+        double y3 = uniform_random (0, SIZE);
+        double x3 = uniform_random (0, SIZE);
+        double y2 = uniform_random (-SIZE, SIZE);
+        double x2 = uniform_random (-SIZE, SIZE);
+        double y1 = uniform_random (-SIZE, SIZE);
+        double x1 = uniform_random (-SIZE, SIZE);
+        cairo_curve_to (cr,
+                        x1, y1,
+                        x2, y2,
+                        x3, y3);
+    }
+    cairo_close_path (cr);
+
+    cairo_set_source_rgb (cr, 1, 0, 0);
+    cairo_fill_preserve (cr);
+    cairo_set_source_rgb (cr, 0, 1, 0);
+    cairo_set_line_width (cr, 0.5);
+    cairo_stroke (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (random_intersections_curves_eo,
+           "Tests the tessellator trapezoid generation and intersection computation",
+           "trap", /* keywords */
+           NULL, /* requirements */
+           SIZE+3, SIZE+3,
+           NULL, draw)
diff --git a/test/random-intersections-curves-nz.c b/test/random-intersections-curves-nz.c
new file mode 100644 (file)
index 0000000..5265e4a
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * Copyright © 2006 M Joonas Pihlaja
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: M Joonas Pihlaja <jpihlaja@cc.helsinki.fi>
+ */
+#include "cairo-test.h"
+
+#define SIZE 512
+#define NUM_SEGMENTS 128
+
+static uint32_t state;
+
+static double
+uniform_random (double minval, double maxval)
+{
+    static uint32_t const poly = 0x9a795537U;
+    uint32_t n = 32;
+    while (n-->0)
+       state = 2*state < state ? (2*state ^ poly) : 2*state;
+    return minval + state * (maxval - minval) / 4294967296.0;
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    int i;
+
+    cairo_set_source_rgb (cr, 0, 0, 0);
+    cairo_paint (cr);
+
+    state = 0x12345678;
+    cairo_translate (cr, 1, 1);
+    cairo_set_fill_rule (cr, CAIRO_FILL_RULE_WINDING);
+
+    cairo_move_to (cr, 0, 0);
+    for (i = 0; i < NUM_SEGMENTS; i++) {
+        double y3 = uniform_random (0, SIZE);
+        double x3 = uniform_random (0, SIZE);
+        double y2 = uniform_random (-SIZE, SIZE);
+        double x2 = uniform_random (-SIZE, SIZE);
+        double y1 = uniform_random (-SIZE, SIZE);
+        double x1 = uniform_random (-SIZE, SIZE);
+        cairo_curve_to (cr,
+                        x1, y1,
+                        x2, y2,
+                        x3, y3);
+    }
+    cairo_close_path (cr);
+
+    cairo_set_source_rgb (cr, 1, 0, 0);
+    cairo_fill_preserve (cr);
+    cairo_set_source_rgb (cr, 0, 1, 0);
+    cairo_set_line_width (cr, 0.5);
+    cairo_stroke (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (random_intersections_curves_nz,
+           "Tests the tessellator trapezoid generation and intersection computation",
+           "trap", /* keywords */
+           NULL, /* requirements */
+           SIZE+3, SIZE+3,
+           NULL, draw)
+
diff --git a/test/random-intersections-eo.c b/test/random-intersections-eo.c
new file mode 100644 (file)
index 0000000..d35894f
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * Copyright © 2006 M Joonas Pihlaja
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: M Joonas Pihlaja <jpihlaja@cc.helsinki.fi>
+ */
+#include "cairo-test.h"
+
+#define SIZE 512
+#define NUM_SEGMENTS 128
+
+static uint32_t state;
+
+static double
+uniform_random (double minval, double maxval)
+{
+    static uint32_t const poly = 0x9a795537U;
+    uint32_t n = 32;
+    while (n-->0)
+       state = 2*state < state ? (2*state ^ poly) : 2*state;
+    return minval + state * (maxval - minval) / 4294967296.0;
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    int i;
+
+    cairo_set_source_rgb (cr, 0, 0, 0);
+    cairo_paint (cr);
+
+    state = 0x12345678;
+    cairo_translate (cr, 1, 1);
+    cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD);
+
+    cairo_move_to (cr, 0, 0);
+    for (i = 0; i < NUM_SEGMENTS; i++) {
+       double x = uniform_random (0, width);
+       double y = uniform_random (0, height);
+       cairo_line_to (cr, x, y);
+    }
+    cairo_close_path (cr);
+
+    cairo_set_source_rgb (cr, 1, 0, 0);
+    cairo_fill_preserve (cr);
+    cairo_set_source_rgb (cr, 0, 1, 0);
+    cairo_set_line_width (cr, 0.5);
+    cairo_stroke (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (random_intersections_eo,
+           "Tests the tessellator trapezoid generation and intersection computation",
+           "trap", /* keywords */
+           NULL, /* requirements */
+           SIZE+3, SIZE+3,
+           NULL, draw)
+
diff --git a/test/random-intersections-nonzero.c b/test/random-intersections-nonzero.c
new file mode 100644 (file)
index 0000000..cad047e
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * Copyright © 2006 M Joonas Pihlaja
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: M Joonas Pihlaja <jpihlaja@cc.helsinki.fi>
+ */
+#include "cairo-test.h"
+
+#define SIZE 512
+#define NUM_SEGMENTS 128
+
+static uint32_t state;
+
+static double
+uniform_random (double minval, double maxval)
+{
+    static uint32_t const poly = 0x9a795537U;
+    uint32_t n = 32;
+    while (n-->0)
+       state = 2*state < state ? (2*state ^ poly) : 2*state;
+    return minval + state * (maxval - minval) / 4294967296.0;
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    int i;
+
+    cairo_set_source_rgb (cr, 0, 0, 0);
+    cairo_paint (cr);
+
+    state = 0x12345678;
+    cairo_translate (cr, 1, 1);
+    cairo_set_fill_rule (cr, CAIRO_FILL_RULE_WINDING);
+
+    cairo_move_to (cr, 0, 0);
+    for (i = 0; i < NUM_SEGMENTS; i++) {
+       double x = uniform_random (0, width);
+       double y = uniform_random (0, height);
+       cairo_line_to (cr, x, y);
+    }
+    cairo_close_path (cr);
+
+    cairo_set_source_rgb (cr, 1, 0, 0);
+    cairo_fill_preserve (cr);
+    cairo_set_source_rgb (cr, 0, 1, 0);
+    cairo_set_line_width (cr, 0.5);
+    cairo_stroke (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (random_intersections_nonzero,
+           "Tests the tessellator trapezoid generation and intersection computation",
+           "trap", /* keywords */
+           NULL, /* requirements */
+           SIZE+3, SIZE+3,
+           NULL, draw)
+
+
diff --git a/test/record-extend.c b/test/record-extend.c
new file mode 100644 (file)
index 0000000..5ecef44
--- /dev/null
@@ -0,0 +1,288 @@
+/*
+ * Copyright © 2007 Red Hat, Inc.
+ * Copyright © 2011 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Authors:
+ *     Behdad Esfahbod <behdad@behdad.org>
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include <math.h>
+#include "cairo-test.h"
+#include <stdio.h>
+
+#define SIZE 90
+
+/* This is written using clip+paint to exercise a bug that once was in the
+ * recording surface.
+ */
+
+static cairo_surface_t *
+source (cairo_surface_t *surface)
+{
+    cairo_t *cr;
+
+    /* Create a 4-pixel image surface with my favorite four colors in each
+     * quadrant. */
+    cr = cairo_create (surface);
+    cairo_surface_destroy (surface);
+
+    /* upper-left = white */
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_rectangle (cr, 0, 0, 1, 1);
+    cairo_fill (cr);
+
+    /* upper-right = red */
+    cairo_set_source_rgb (cr, 1, 0, 0);
+    cairo_rectangle (cr, 1, 0, 1, 1);
+    cairo_fill (cr);
+
+    /* lower-left = green */
+    cairo_set_source_rgb (cr, 0, 1, 0);
+    cairo_rectangle (cr, 0, 1, 1, 1);
+    cairo_fill (cr);
+
+    /* lower-right = blue */
+    cairo_set_source_rgb (cr, 0, 0, 1);
+    cairo_rectangle (cr, 1, 1, 1, 1);
+    cairo_fill (cr);
+
+    surface = cairo_surface_reference (cairo_get_target (cr));
+    cairo_destroy (cr);
+
+    return surface;
+}
+
+static cairo_surface_t *
+image (cairo_t *cr)
+{
+    return source (cairo_image_surface_create (CAIRO_FORMAT_RGB24, 2, 2));
+}
+
+static cairo_surface_t *
+similar (cairo_t *cr)
+{
+    return source (cairo_surface_create_similar (cairo_get_target (cr),
+                                                CAIRO_CONTENT_COLOR, 2, 2));
+}
+
+static cairo_t *
+extend (cairo_t *cr, cairo_surface_t *(*surface)(cairo_t *), cairo_extend_t mode)
+{
+    cairo_surface_t *s;
+
+    cairo_set_source_rgb (cr, 0, 1, 1);
+    cairo_paint (cr);
+
+    /* Now use extend modes to cover most of the surface with those 4 colors */
+    s = surface (cr);
+    cairo_set_source_surface (cr, s, SIZE/2 - 1, SIZE/2 - 1);
+    cairo_surface_destroy (s);
+
+    cairo_pattern_set_extend (cairo_get_source (cr), mode);
+
+    cairo_rectangle (cr, 10, 10, SIZE-20, SIZE-20);
+    cairo_clip (cr);
+    cairo_paint (cr);
+
+    return cr;
+}
+
+static cairo_t *
+extend_none (cairo_t *cr,
+            cairo_surface_t *(*pattern)(cairo_t *))
+{
+    return extend (cr, pattern, CAIRO_EXTEND_NONE);
+}
+
+static cairo_t *
+extend_pad (cairo_t *cr,
+           cairo_surface_t *(*pattern)(cairo_t *))
+{
+    return extend (cr, pattern, CAIRO_EXTEND_PAD);
+}
+
+static cairo_t *
+extend_repeat (cairo_t *cr,
+              cairo_surface_t *(*pattern)(cairo_t *))
+{
+    return extend (cr, pattern, CAIRO_EXTEND_REPEAT);
+}
+
+static cairo_t *
+extend_reflect (cairo_t *cr,
+              cairo_surface_t *(*pattern)(cairo_t *))
+{
+    return extend (cr, pattern, CAIRO_EXTEND_REFLECT);
+}
+
+static cairo_t *
+record_create (cairo_t *target)
+{
+    cairo_surface_t *surface;
+    cairo_t *cr;
+
+    surface = cairo_recording_surface_create (cairo_surface_get_content (cairo_get_target (target)), NULL);
+    cr = cairo_create (surface);
+    cairo_surface_destroy (surface);
+
+    return cr;
+}
+
+static cairo_surface_t *
+record_get (cairo_t *target)
+{
+    cairo_surface_t *surface;
+
+    surface = cairo_surface_reference (cairo_get_target (target));
+    cairo_destroy (target);
+
+    return surface;
+}
+
+static cairo_test_status_t
+record_replay (cairo_t *cr,
+              cairo_t *(*func)(cairo_t *,
+                               cairo_surface_t *(*pattern)(cairo_t *)),
+              cairo_surface_t *(*pattern)(cairo_t *),
+              int width, int height)
+{
+    cairo_surface_t *surface;
+    int x, y;
+
+    surface = record_get (func (record_create (cr), pattern));
+
+    cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+    cairo_set_source_surface (cr, surface, 0, 0);
+    cairo_surface_destroy (surface);
+    cairo_pattern_set_extend (cairo_get_source (cr), CAIRO_EXTEND_NONE);
+
+    for (y = 0; y < height; y += 2) {
+       for (x = 0; x < width; x += 2) {
+           cairo_rectangle (cr, x, y, 2, 2);
+           cairo_clip (cr);
+           cairo_paint (cr);
+           cairo_reset_clip (cr);
+       }
+    }
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+static cairo_test_status_t
+record_extend_none (cairo_t *cr, int width, int height)
+{
+    return record_replay (cr, extend_none, image, width, height);
+}
+
+static cairo_test_status_t
+record_extend_pad (cairo_t *cr, int width, int height)
+{
+    return record_replay (cr, extend_pad, image, width, height);
+}
+
+static cairo_test_status_t
+record_extend_repeat (cairo_t *cr, int width, int height)
+{
+    return record_replay (cr, extend_repeat, image, width, height);
+}
+
+static cairo_test_status_t
+record_extend_reflect (cairo_t *cr, int width, int height)
+{
+    return record_replay (cr, extend_reflect, image, width, height);
+}
+
+static cairo_test_status_t
+record_extend_none_similar (cairo_t *cr, int width, int height)
+{
+    return record_replay (cr, extend_none, similar, width, height);
+}
+
+static cairo_test_status_t
+record_extend_pad_similar (cairo_t *cr, int width, int height)
+{
+    return record_replay (cr, extend_pad, similar, width, height);
+}
+
+static cairo_test_status_t
+record_extend_repeat_similar (cairo_t *cr, int width, int height)
+{
+    return record_replay (cr, extend_repeat, similar, width, height);
+}
+
+static cairo_test_status_t
+record_extend_reflect_similar (cairo_t *cr, int width, int height)
+{
+    return record_replay (cr, extend_reflect, similar, width, height);
+}
+
+CAIRO_TEST (record_extend_none,
+           "Test CAIRO_EXTEND_NONE for recorded surface patterns",
+           "record, extend", /* keywords */
+           NULL, /* requirements */
+           SIZE, SIZE,
+           NULL, record_extend_none)
+CAIRO_TEST (record_extend_pad,
+           "Test CAIRO_EXTEND_PAD for recorded surface patterns",
+           "record, extend", /* keywords */
+           NULL, /* requirements */
+           SIZE, SIZE,
+           NULL, record_extend_pad)
+CAIRO_TEST (record_extend_repeat,
+           "Test CAIRO_EXTEND_REPEAT for recorded surface patterns",
+           "record, extend", /* keywords */
+           NULL, /* requirements */
+           SIZE, SIZE,
+           NULL, record_extend_repeat)
+CAIRO_TEST (record_extend_reflect,
+           "Test CAIRO_EXTEND_REFLECT for recorded surface patterns",
+           "record, extend", /* keywords */
+           NULL, /* requirements */
+           SIZE, SIZE,
+           NULL, record_extend_reflect)
+
+CAIRO_TEST (record_extend_none_similar,
+           "Test CAIRO_EXTEND_NONE for recorded surface patterns",
+           "record, extend", /* keywords */
+           NULL, /* requirements */
+           SIZE, SIZE,
+           NULL, record_extend_none_similar)
+CAIRO_TEST (record_extend_pad_similar,
+           "Test CAIRO_EXTEND_PAD for recorded surface patterns",
+           "record, extend", /* keywords */
+           NULL, /* requirements */
+           SIZE, SIZE,
+           NULL, record_extend_pad_similar)
+CAIRO_TEST (record_extend_repeat_similar,
+           "Test CAIRO_EXTEND_REPEAT for recorded surface patterns",
+           "record, extend", /* keywords */
+           NULL, /* requirements */
+           SIZE, SIZE,
+           NULL, record_extend_repeat_similar)
+CAIRO_TEST (record_extend_reflect_similar,
+           "Test CAIRO_EXTEND_REFLECT for recorded surface patterns",
+           "record, extend", /* keywords */
+           NULL, /* requirements */
+           SIZE, SIZE,
+           NULL, record_extend_reflect_similar)
diff --git a/test/record-mesh.c b/test/record-mesh.c
new file mode 100644 (file)
index 0000000..2581ce7
--- /dev/null
@@ -0,0 +1,166 @@
+/*
+ * Copyright © 2007 Red Hat, Inc.
+ * Copyright © 2009 Adrian Johnson
+ * Copyright © 2011 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Authors:
+ *     Behdad Esfahbod <behdad@behdad.org>
+ *     Adrian Johnson <ajohnson@redneon.com>
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include <math.h>
+#include "cairo-test.h"
+#include <stdio.h>
+
+#define PAT_WIDTH  170
+#define PAT_HEIGHT 170
+#define SIZE PAT_WIDTH
+#define PAD 2
+#define WIDTH (PAD + SIZE + PAD)
+#define HEIGHT WIDTH
+
+/* This test is designed to paint a mesh pattern. The mesh contains
+ * two overlapping patches */
+
+static cairo_pattern_t *
+mesh (void)
+{
+    cairo_pattern_t *pattern;
+
+    pattern = cairo_pattern_create_mesh ();
+
+    cairo_mesh_pattern_begin_patch (pattern);
+
+    cairo_mesh_pattern_move_to (pattern, 0, 0);
+    cairo_mesh_pattern_curve_to (pattern, 30, -30,  60,  30, 100, 0);
+    cairo_mesh_pattern_curve_to (pattern, 60,  30, 130,  60, 100, 100);
+    cairo_mesh_pattern_curve_to (pattern, 60,  70,  30, 130,   0, 100);
+    cairo_mesh_pattern_curve_to (pattern, 30,  70, -30,  30,   0, 0);
+
+    cairo_mesh_pattern_set_corner_color_rgb (pattern, 0, 1, 0, 0);
+    cairo_mesh_pattern_set_corner_color_rgb (pattern, 1, 0, 1, 0);
+    cairo_mesh_pattern_set_corner_color_rgb (pattern, 2, 0, 0, 1);
+    cairo_mesh_pattern_set_corner_color_rgb (pattern, 3, 1, 1, 0);
+
+    cairo_mesh_pattern_end_patch (pattern);
+
+    cairo_mesh_pattern_begin_patch (pattern);
+
+    cairo_mesh_pattern_move_to (pattern, 50, 50);
+    cairo_mesh_pattern_curve_to (pattern,  80,  20, 110,  80, 150, 50);
+    cairo_mesh_pattern_curve_to (pattern, 110,  80, 180, 110, 150, 150);
+    cairo_mesh_pattern_curve_to (pattern, 110, 120,  80, 180,  50, 150);
+    cairo_mesh_pattern_curve_to (pattern,  80, 120,  20,  80,  50, 50);
+
+    cairo_mesh_pattern_set_corner_color_rgba (pattern, 0, 1, 0, 0, 0.3);
+    cairo_mesh_pattern_set_corner_color_rgb  (pattern, 1, 0, 1, 0);
+    cairo_mesh_pattern_set_corner_color_rgba (pattern, 2, 0, 0, 1, 0.3);
+    cairo_mesh_pattern_set_corner_color_rgb  (pattern, 3, 1, 1, 0);
+
+    cairo_mesh_pattern_end_patch (pattern);
+
+    return pattern;
+}
+
+static cairo_t *
+draw (cairo_t *cr)
+{
+    cairo_pattern_t *source;
+
+    cairo_set_source_rgb (cr, 0, 1, 1);
+    cairo_paint (cr);
+
+    source = mesh ();
+    cairo_set_source (cr, source);
+    cairo_pattern_destroy (source);
+
+    cairo_rectangle (cr, 10, 10, SIZE-20, SIZE-20);
+    cairo_clip (cr);
+    cairo_paint (cr);
+
+    return cr;
+}
+
+static cairo_t *
+record_create (cairo_t *target)
+{
+    cairo_surface_t *surface;
+    cairo_t *cr;
+
+    surface = cairo_recording_surface_create (cairo_surface_get_content (cairo_get_target (target)), NULL);
+    cr = cairo_create (surface);
+    cairo_surface_destroy (surface);
+
+    return cr;
+}
+
+static cairo_surface_t *
+record_get (cairo_t *target)
+{
+    cairo_surface_t *surface;
+
+    surface = cairo_surface_reference (cairo_get_target (target));
+    cairo_destroy (target);
+
+    return surface;
+}
+
+static cairo_test_status_t
+record_replay (cairo_t *cr, cairo_t *(*func)(cairo_t *), int width, int height)
+{
+    cairo_surface_t *surface;
+    int x, y;
+
+    surface = record_get (func (record_create (cr)));
+
+    cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+    cairo_set_source_surface (cr, surface, 0, 0);
+    cairo_surface_destroy (surface);
+    cairo_pattern_set_extend (cairo_get_source (cr), CAIRO_EXTEND_NONE);
+
+    for (y = 0; y < height; y += 2) {
+       for (x = 0; x < width; x += 2) {
+           cairo_rectangle (cr, x, y, 2, 2);
+           cairo_clip (cr);
+           cairo_paint (cr);
+           cairo_reset_clip (cr);
+       }
+    }
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+static cairo_test_status_t
+record_mesh (cairo_t *cr, int width, int height)
+{
+    return record_replay (cr, draw, width, height);
+}
+
+CAIRO_TEST (record_mesh,
+           "Paint mesh pattern through a recording surface",
+           "record,mesh,pattern", /* keywords */
+           NULL, /* requirements */
+           WIDTH, HEIGHT,
+           NULL, record_mesh)
+
diff --git a/test/record.c b/test/record.c
new file mode 100644 (file)
index 0000000..2f30653
--- /dev/null
@@ -0,0 +1,485 @@
+/*
+ * Copyright © 2005 Red Hat, Inc.
+ * Copyright © 2011 Intel Corporation
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Authors:
+ *     Carl D. Worth <cworth@cworth.org>
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+
+#define TEXT_SIZE 12
+#define SIZE 60 /* needs to be big to check large area effects (dithering) */
+#define PAD 2
+
+#define TT_SIZE 100
+#define TT_PAD 5
+#define TT_FONT_SIZE 32.0
+
+static uint32_t data[16] = {
+    0xffffffff, 0xffffffff,            0xffff0000, 0xffff0000,
+    0xffffffff, 0xffffffff,            0xffff0000, 0xffff0000,
+
+    0xff00ff00, 0xff00ff00,            0xff0000ff, 0xff0000ff,
+    0xff00ff00, 0xff00ff00,            0xff0000ff, 0xff0000ff
+};
+
+static const char *png_filename = "romedalen.png";
+
+static cairo_t *
+paint (cairo_t *cr)
+{
+    cairo_set_source_rgb (cr, 0, 0, 1);
+    cairo_paint (cr);
+
+    cairo_translate (cr, 2, 2);
+    cairo_scale (cr, 0.5, 0.5);
+
+    cairo_set_source_rgb (cr, 1, 0, 0);
+    cairo_paint (cr);
+
+    return cr;
+}
+
+static cairo_t *
+paint_alpha (cairo_t *cr)
+{
+    cairo_surface_t *surface;
+
+    surface = cairo_image_surface_create_for_data ((unsigned char *) data,
+                                                  CAIRO_FORMAT_RGB24, 4, 4, 16);
+
+    cairo_test_paint_checkered (cr);
+
+    cairo_scale (cr, 4, 4);
+
+    cairo_set_source_surface (cr, surface, 2 , 2);
+    cairo_pattern_set_filter (cairo_get_source (cr), CAIRO_FILTER_NEAREST);
+    cairo_paint_with_alpha (cr, 0.5);
+
+    cairo_surface_finish (surface); /* data will go out of scope */
+    cairo_surface_destroy (surface);
+
+    return cr;
+}
+
+static cairo_t *
+paint_alpha_solid_clip (cairo_t *cr)
+{
+    cairo_test_paint_checkered (cr);
+
+    cairo_rectangle (cr, 2.5, 2.5, 27, 27);
+    cairo_clip (cr);
+
+    cairo_set_source_rgb (cr, 1., 0.,0.);
+    cairo_paint_with_alpha (cr, 0.5);
+
+    return cr;
+}
+
+static cairo_t *
+paint_alpha_clip (cairo_t *cr)
+{
+    cairo_surface_t *surface;
+
+    surface = cairo_image_surface_create_for_data ((unsigned char *) data,
+                                                  CAIRO_FORMAT_RGB24, 4, 4, 16);
+
+    cairo_test_paint_checkered (cr);
+
+    cairo_rectangle (cr, 10.5, 10.5, 11, 11);
+    cairo_clip (cr);
+
+    cairo_scale (cr, 4, 4);
+
+    cairo_set_source_surface (cr, surface, 2 , 2);
+    cairo_pattern_set_filter (cairo_get_source (cr), CAIRO_FILTER_NEAREST);
+    cairo_paint_with_alpha (cr, 0.5);
+
+    cairo_surface_finish (surface); /* data will go out of scope */
+    cairo_surface_destroy (surface);
+
+    return cr;
+}
+
+static cairo_t *
+paint_alpha_clip_mask (cairo_t *cr)
+{
+    cairo_surface_t *surface;
+
+    surface = cairo_image_surface_create_for_data ((unsigned char *) data,
+                                                  CAIRO_FORMAT_RGB24, 4, 4, 16);
+
+    cairo_test_paint_checkered (cr);
+
+    cairo_move_to (cr, 16, 5);
+    cairo_line_to (cr, 5, 16);
+    cairo_line_to (cr, 16, 27);
+    cairo_line_to (cr, 27, 16);
+    cairo_clip (cr);
+
+    cairo_scale (cr, 4, 4);
+
+    cairo_set_source_surface (cr, surface, 2 , 2);
+    cairo_pattern_set_filter (cairo_get_source (cr), CAIRO_FILTER_NEAREST);
+    cairo_paint_with_alpha (cr, 0.5);
+
+    cairo_surface_finish (surface); /* data will go out of scope */
+    cairo_surface_destroy (surface);
+
+    return cr;
+}
+
+static cairo_t *
+select_font_face (cairo_t *cr)
+{
+    /* We draw in the default black, so paint white first. */
+    cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); /* white */
+    cairo_paint (cr);
+
+    cairo_set_source_rgb (cr, 0, 0, 0); /* black */
+
+    cairo_set_font_size (cr, TEXT_SIZE);
+    cairo_move_to (cr, 0, TEXT_SIZE);
+
+    cairo_select_font_face (cr, CAIRO_TEST_FONT_FAMILY " Serif",
+                           CAIRO_FONT_SLANT_NORMAL,
+                           CAIRO_FONT_WEIGHT_NORMAL);
+    cairo_show_text (cr, "i-am-serif");
+
+    cairo_select_font_face (cr, CAIRO_TEST_FONT_FAMILY " Sans",
+                           CAIRO_FONT_SLANT_NORMAL,
+                           CAIRO_FONT_WEIGHT_NORMAL);
+    cairo_show_text (cr, " i-am-sans");
+
+    cairo_select_font_face (cr, CAIRO_TEST_FONT_FAMILY " Sans Mono",
+                           CAIRO_FONT_SLANT_NORMAL,
+                           CAIRO_FONT_WEIGHT_NORMAL);
+    cairo_show_text (cr, " i-am-mono");
+
+    return cr;
+}
+
+static cairo_t *
+fill_alpha (cairo_t *cr)
+{
+    const double alpha = 1./3;
+    int n;
+
+    /* flatten to white */
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_paint (cr);
+
+    /* square */
+    cairo_rectangle (cr, PAD, PAD, SIZE, SIZE);
+    cairo_set_source_rgba (cr, 1, 0, 0, alpha);
+    cairo_fill (cr);
+
+    /* circle */
+    cairo_translate (cr, SIZE + 2 * PAD, 0);
+    cairo_arc (cr, PAD + SIZE / 2., PAD + SIZE / 2., SIZE / 2., 0, 2 * M_PI);
+    cairo_set_source_rgba (cr, 0, 1, 0, alpha);
+    cairo_fill (cr);
+
+    /* triangle */
+    cairo_translate (cr, 0, SIZE + 2 * PAD);
+    cairo_move_to (cr, PAD + SIZE / 2, PAD);
+    cairo_line_to (cr, PAD + SIZE, PAD + SIZE);
+    cairo_line_to (cr, PAD, PAD + SIZE);
+    cairo_set_source_rgba (cr, 0, 0, 1, alpha);
+    cairo_fill (cr);
+
+    /* star */
+    cairo_translate (cr, -(SIZE + 2 * PAD) + SIZE/2., SIZE/2.);
+    for (n = 0; n < 5; n++) {
+       cairo_line_to (cr,
+                      SIZE/2 * cos (2*n * 2*M_PI / 10),
+                      SIZE/2 * sin (2*n * 2*M_PI / 10));
+
+       cairo_line_to (cr,
+                      SIZE/4 * cos ((2*n+1)*2*M_PI / 10),
+                      SIZE/4 * sin ((2*n+1)*2*M_PI / 10));
+    }
+    cairo_set_source_rgba (cr, 0, 0, 0, alpha);
+    cairo_fill (cr);
+
+    return cr;
+}
+
+static cairo_t *
+self_intersecting (cairo_t *cr)
+{
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_paint (cr);
+
+    cairo_translate (cr, 1.0, 1.0);
+
+    cairo_set_source_rgb (cr, 1, 0, 0); /* red */
+
+    /* First draw the desired shape with a fill */
+    cairo_rectangle (cr, 0.5, 0.5,  4.0, 4.0);
+    cairo_rectangle (cr, 3.5, 3.5,  4.0, 4.0);
+    cairo_rectangle (cr, 3.5, 1.5, -2.0, 2.0);
+    cairo_rectangle (cr, 6.5, 4.5, -2.0, 2.0);
+
+    cairo_fill (cr);
+
+    /* Then try the same thing with a stroke */
+    cairo_translate (cr, 0, 10);
+    cairo_move_to (cr, 1.0, 1.0);
+    cairo_rel_line_to (cr,  3.0,  0.0);
+    cairo_rel_line_to (cr,  0.0,  6.0);
+    cairo_rel_line_to (cr,  3.0,  0.0);
+    cairo_rel_line_to (cr,  0.0, -3.0);
+    cairo_rel_line_to (cr, -6.0,  0.0);
+    cairo_close_path (cr);
+
+    cairo_set_line_width (cr, 1.0);
+    cairo_stroke (cr);
+
+    return cr;
+}
+
+static void
+draw_text_transform (cairo_t *cr)
+{
+    cairo_matrix_t tm;
+
+    /* skew */
+    cairo_matrix_init (&tm, 1, 0,
+                       -0.25, 1,
+                       0, 0);
+    cairo_matrix_scale (&tm, TT_FONT_SIZE, TT_FONT_SIZE);
+    cairo_set_font_matrix (cr, &tm);
+
+    cairo_new_path (cr);
+    cairo_move_to (cr, 50, TT_SIZE-TT_PAD);
+    cairo_show_text (cr, "A");
+
+    /* rotate and scale */
+    cairo_matrix_init_rotate (&tm, M_PI / 2);
+    cairo_matrix_scale (&tm, TT_FONT_SIZE, TT_FONT_SIZE * 2.0);
+    cairo_set_font_matrix (cr, &tm);
+
+    cairo_new_path (cr);
+    cairo_move_to (cr, TT_PAD, TT_PAD + 25);
+    cairo_show_text (cr, "A");
+
+    cairo_matrix_init_rotate (&tm, M_PI / 2);
+    cairo_matrix_scale (&tm, TT_FONT_SIZE * 2.0, TT_FONT_SIZE);
+    cairo_set_font_matrix (cr, &tm);
+
+    cairo_new_path (cr);
+    cairo_move_to (cr, TT_PAD, TT_PAD + 50);
+    cairo_show_text (cr, "A");
+}
+
+static cairo_t *
+text_transform (cairo_t *cr)
+{
+    const cairo_test_context_t *ctx = cairo_test_get_context (cr);
+    cairo_pattern_t *pattern;
+
+    cairo_set_source_rgb (cr, 1., 1., 1.);
+    cairo_paint (cr);
+
+    cairo_set_source_rgb (cr, 0., 0., 0.);
+
+    cairo_select_font_face (cr, CAIRO_TEST_FONT_FAMILY " Sans",
+                           CAIRO_FONT_SLANT_NORMAL,
+                           CAIRO_FONT_WEIGHT_NORMAL);
+
+    draw_text_transform (cr);
+
+    cairo_translate (cr, SIZE, SIZE);
+    cairo_rotate (cr, M_PI);
+
+    pattern = cairo_test_create_pattern_from_png (ctx, png_filename);
+    cairo_pattern_set_extend (pattern, CAIRO_EXTEND_REPEAT);
+    cairo_set_source (cr, pattern);
+    cairo_pattern_destroy (pattern);
+
+    draw_text_transform (cr);
+
+    return cr;
+}
+
+/* And here begins the recording and replaying... */
+
+static cairo_t *
+record_create (cairo_t *target)
+{
+    cairo_surface_t *surface;
+    cairo_t *cr;
+
+    surface = cairo_recording_surface_create (cairo_surface_get_content (cairo_get_target (target)), NULL);
+    cr = cairo_create (surface);
+    cairo_surface_destroy (surface);
+
+    return cr;
+}
+
+static cairo_surface_t *
+record_get (cairo_t *target)
+{
+    cairo_surface_t *surface;
+
+    surface = cairo_surface_reference (cairo_get_target (target));
+    cairo_destroy (target);
+
+    return surface;
+}
+
+static cairo_test_status_t
+record_replay (cairo_t *cr, cairo_t *(*func)(cairo_t *), int width, int height)
+{
+    cairo_surface_t *surface;
+    int x, y;
+
+    surface = record_get (func (record_create (cr)));
+
+    cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+    cairo_set_source_surface (cr, surface, 0, 0);
+    cairo_surface_destroy (surface);
+    cairo_pattern_set_extend (cairo_get_source (cr), CAIRO_EXTEND_NONE);
+
+    for (y = 0; y < height; y += 2) {
+       for (x = 0; x < width; x += 2) {
+           cairo_rectangle (cr, x, y, 2, 2);
+           cairo_clip (cr);
+           cairo_paint (cr);
+           cairo_reset_clip (cr);
+       }
+    }
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+static cairo_test_status_t
+record_paint (cairo_t *cr, int width, int height)
+{
+    return record_replay (cr, paint, width, height);
+}
+
+static cairo_test_status_t
+record_paint_alpha (cairo_t *cr, int width, int height)
+{
+    return record_replay (cr, paint_alpha, width, height);
+}
+
+static cairo_test_status_t
+record_paint_alpha_solid_clip (cairo_t *cr, int width, int height)
+{
+    return record_replay (cr, paint_alpha_solid_clip, width, height);
+}
+
+static cairo_test_status_t
+record_paint_alpha_clip (cairo_t *cr, int width, int height)
+{
+    return record_replay (cr, paint_alpha_clip, width, height);
+}
+
+static cairo_test_status_t
+record_paint_alpha_clip_mask (cairo_t *cr, int width, int height)
+{
+    return record_replay (cr, paint_alpha_clip_mask, width, height);
+}
+
+static cairo_test_status_t
+record_fill_alpha (cairo_t *cr, int width, int height)
+{
+    return record_replay (cr, fill_alpha, width, height);
+}
+
+static cairo_test_status_t
+record_self_intersecting (cairo_t *cr, int width, int height)
+{
+    return record_replay (cr, self_intersecting, width, height);
+}
+
+static cairo_test_status_t
+record_select_font_face (cairo_t *cr, int width, int height)
+{
+    return record_replay (cr, select_font_face, width, height);
+}
+
+static cairo_test_status_t
+record_text_transform (cairo_t *cr, int width, int height)
+{
+    return record_replay (cr, text_transform, width, height);
+}
+
+CAIRO_TEST (record_paint,
+           "Test replayed calls to cairo_paint",
+           "paint,record", /* keywords */
+           NULL, /* requirements */
+           8, 8,
+           NULL, record_paint)
+CAIRO_TEST (record_paint_alpha,
+           "Simple test of cairo_paint_with_alpha",
+           "record, paint, alpha", /* keywords */
+           NULL, /* requirements */
+           32, 32,
+           NULL, record_paint_alpha)
+CAIRO_TEST (record_paint_alpha_solid_clip,
+           "Simple test of cairo_paint_with_alpha+unaligned clip",
+           "record, paint, alpha, clip", /* keywords */
+           NULL, /* requirements */
+           32, 32,
+           NULL, record_paint_alpha_solid_clip)
+CAIRO_TEST (record_paint_alpha_clip,
+           "Simple test of cairo_paint_with_alpha+unaligned clip",
+           "record, paint, alpha, clip", /* keywords */
+           NULL, /* requirements */
+           32, 32,
+           NULL, record_paint_alpha_clip)
+CAIRO_TEST (record_paint_alpha_clip_mask,
+           "Simple test of cairo_paint_with_alpha+triangular clip",
+           "record, paint, alpha, clip", /* keywords */
+           NULL, /* requirements */
+           32, 32,
+           NULL, record_paint_alpha_clip_mask)
+CAIRO_TEST (record_fill_alpha,
+           "Tests using set_rgba();fill()",
+           "record,fill, alpha", /* keywords */
+           NULL, /* requirements */
+           2*SIZE + 4*PAD, 2*SIZE + 4*PAD,
+           NULL, record_fill_alpha)
+CAIRO_TEST (record_select_font_face,
+           "Tests using cairo_select_font_face to draw text in different faces",
+           "record, font", /* keywords */
+           NULL, /* requirements */
+           192, TEXT_SIZE + 4,
+           NULL, record_select_font_face)
+CAIRO_TEST (record_self_intersecting,
+           "Test strokes of self-intersecting paths",
+           "record, stroke, trap", /* keywords */
+           NULL, /* requirements */
+           10, 20,
+           NULL, record_self_intersecting)
+CAIRO_TEST (record_text_transform,
+           "Test various applications of the font matrix",
+           "record, text, transform", /* keywords */
+           NULL, /* requirements */
+           TT_SIZE, TT_SIZE,
+           NULL, record_text_transform)
diff --git a/test/recording-surface-extend.c b/test/recording-surface-extend.c
new file mode 100644 (file)
index 0000000..0566545
--- /dev/null
@@ -0,0 +1,174 @@
+/*
+ * Copyright © 2007 Adrian Johnson
+ * Copyright © 2011 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Authors:
+ *     Adrian Johnson <ajohnson@redneon.com>
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+
+#define PAT_WIDTH  120
+#define PAT_HEIGHT 120
+#define SIZE (PAT_WIDTH*2)
+#define PAD 2
+#define WIDTH (PAD + SIZE + PAD)
+#define HEIGHT WIDTH
+
+
+/* This test is designed to test painting a recording surface pattern with
+ * CAIRO_EXTEND_NONE and a non identity pattern matrix.
+ */
+static cairo_pattern_t *create_pattern (cairo_t *target)
+{
+    cairo_surface_t *surface;
+    cairo_pattern_t *pattern;
+    cairo_t *cr;
+
+    surface = cairo_surface_create_similar (cairo_get_group_target (target),
+                                           CAIRO_CONTENT_COLOR_ALPHA,
+                                           PAT_WIDTH, PAT_HEIGHT);
+    cr = cairo_create (surface);
+    cairo_surface_destroy (surface);
+
+    cairo_set_source_rgba (cr, 1, 0, 1, 0.5);
+    cairo_rectangle (cr, PAT_WIDTH/6.0, PAT_HEIGHT/6.0, PAT_WIDTH/4.0, PAT_HEIGHT/4.0);
+    cairo_fill (cr);
+
+    cairo_set_source_rgba (cr, 0, 1, 1, 0.5);
+    cairo_rectangle (cr, PAT_WIDTH/2.0, PAT_HEIGHT/2.0, PAT_WIDTH/4.0, PAT_HEIGHT/4.0);
+    cairo_fill (cr);
+
+    cairo_set_line_width (cr, 1);
+    cairo_move_to (cr, PAT_WIDTH/6.0, 0);
+    cairo_line_to (cr, 0, 0);
+    cairo_line_to (cr, 0, PAT_HEIGHT/6.0);
+    cairo_set_source_rgb (cr, 1, 0, 0);
+    cairo_stroke (cr);
+    cairo_move_to (cr, PAT_WIDTH/6.0, PAT_HEIGHT);
+    cairo_line_to (cr, 0, PAT_HEIGHT);
+    cairo_line_to (cr, 0, 5*PAT_HEIGHT/6.0);
+    cairo_set_source_rgb (cr, 0, 1, 0);
+    cairo_stroke (cr);
+    cairo_move_to (cr, 5*PAT_WIDTH/6.0, 0);
+    cairo_line_to (cr, PAT_WIDTH, 0);
+    cairo_line_to (cr, PAT_WIDTH, PAT_HEIGHT/6.0);
+    cairo_set_source_rgb (cr, 0, 0, 1);
+    cairo_stroke (cr);
+    cairo_move_to (cr, 5*PAT_WIDTH/6.0, PAT_HEIGHT);
+    cairo_line_to (cr, PAT_WIDTH, PAT_HEIGHT);
+    cairo_line_to (cr, PAT_WIDTH, 5*PAT_HEIGHT/6.0);
+    cairo_set_source_rgb (cr, 1, 1, 0);
+    cairo_stroke (cr);
+
+    cairo_set_source_rgb (cr, 0.5, 0.5, 0.5);
+    cairo_set_line_width (cr, PAT_WIDTH/10.0);
+
+    cairo_move_to (cr, 0,         PAT_HEIGHT/4.0);
+    cairo_line_to (cr, PAT_WIDTH, PAT_HEIGHT/4.0);
+    cairo_stroke (cr);
+
+    cairo_move_to (cr, PAT_WIDTH/4.0,         0);
+    cairo_line_to (cr, PAT_WIDTH/4.0, PAT_WIDTH);
+    cairo_stroke (cr);
+
+    pattern = cairo_pattern_create_for_surface (cairo_get_target (cr));
+    cairo_destroy (cr);
+
+    return pattern;
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, cairo_extend_t extend)
+{
+    cairo_pattern_t *pattern;
+    cairo_matrix_t   mat;
+
+    cairo_translate (cr, PAD, PAD);
+
+    pattern = create_pattern (cr);
+
+    cairo_matrix_init_identity (&mat);
+    cairo_matrix_scale (&mat, 2, 1.5);
+    cairo_matrix_rotate (&mat, 1);
+    cairo_matrix_translate (&mat, -PAT_WIDTH/4.0, -PAT_WIDTH/2.0);
+    cairo_pattern_set_matrix (pattern, &mat);
+    cairo_pattern_set_extend (pattern, extend);
+
+    cairo_set_source (cr, pattern);
+    cairo_paint (cr);
+
+    cairo_pattern_destroy (pattern);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+static cairo_test_status_t
+none (cairo_t *cr, int width, int height)
+{
+    return draw (cr, CAIRO_EXTEND_NONE);
+}
+
+static cairo_test_status_t
+repeat (cairo_t *cr, int width, int height)
+{
+    return draw (cr, CAIRO_EXTEND_REPEAT);
+}
+
+static cairo_test_status_t
+reflect (cairo_t *cr, int width, int height)
+{
+    return draw (cr, CAIRO_EXTEND_REFLECT);
+}
+
+static cairo_test_status_t
+pad (cairo_t *cr, int width, int height)
+{
+    return draw (cr, CAIRO_EXTEND_PAD);
+}
+
+CAIRO_TEST (recording_surface_extend_none,
+           "Paint recording surface pattern with extend modes",
+           "recording, extend", /* keywords */
+           NULL, /* requirements */
+           WIDTH, HEIGHT,
+           NULL, none)
+CAIRO_TEST (recording_surface_extend_repeat,
+           "Paint recording surface pattern with extend modes",
+           "recording, extend", /* keywords */
+           NULL, /* requirements */
+           WIDTH, HEIGHT,
+           NULL, repeat)
+CAIRO_TEST (recording_surface_extend_reflect,
+           "Paint recording surface pattern with extend modes",
+           "recording, extend", /* keywords */
+           NULL, /* requirements */
+           WIDTH, HEIGHT,
+           NULL, reflect)
+CAIRO_TEST (recording_surface_extend_pad,
+           "Paint recording surface pattern with extend modes",
+           "recording, extend", /* keywords */
+           NULL, /* requirements */
+           WIDTH, HEIGHT,
+           NULL, pad)
diff --git a/test/recording-surface-pattern.c b/test/recording-surface-pattern.c
new file mode 100644 (file)
index 0000000..5a5fee2
--- /dev/null
@@ -0,0 +1,162 @@
+/*
+ * Copyright © 2007 Adrian Johnson
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Adrian Johnson <ajohnson@redneon.com>
+ */
+
+#include "cairo-test.h"
+
+#define PAT_WIDTH  120
+#define PAT_HEIGHT 120
+#define SIZE (PAT_WIDTH*2)
+#define PAD 2
+#define WIDTH (PAD + SIZE + PAD)
+#define HEIGHT WIDTH
+
+
+/* This test is designed to test painting a recording surface pattern with
+ * CAIRO_EXTEND_NONE and a non identity pattern matrix.
+ */
+static cairo_pattern_t *create_pattern (cairo_t *target)
+{
+    cairo_surface_t *surface;
+    cairo_pattern_t *pattern;
+    cairo_t *cr;
+
+    surface = cairo_surface_create_similar (cairo_get_group_target (target),
+                                           CAIRO_CONTENT_COLOR_ALPHA,
+                                           PAT_WIDTH, PAT_HEIGHT);
+    cr = cairo_create (surface);
+    cairo_surface_destroy (surface);
+
+    cairo_set_source_rgba (cr, 1, 0, 1, 0.5);
+    cairo_rectangle (cr, PAT_WIDTH/6.0, PAT_HEIGHT/6.0, PAT_WIDTH/4.0, PAT_HEIGHT/4.0);
+    cairo_fill (cr);
+
+    cairo_set_source_rgba (cr, 0, 1, 1, 0.5);
+    cairo_rectangle (cr, PAT_WIDTH/2.0, PAT_HEIGHT/2.0, PAT_WIDTH/4.0, PAT_HEIGHT/4.0);
+    cairo_fill (cr);
+
+    cairo_set_line_width (cr, 1);
+    cairo_move_to (cr, PAT_WIDTH/6.0, 0);
+    cairo_line_to (cr, 0, 0);
+    cairo_line_to (cr, 0, PAT_HEIGHT/6.0);
+    cairo_set_source_rgb (cr, 1, 0, 0);
+    cairo_stroke (cr);
+    cairo_move_to (cr, PAT_WIDTH/6.0, PAT_HEIGHT);
+    cairo_line_to (cr, 0, PAT_HEIGHT);
+    cairo_line_to (cr, 0, 5*PAT_HEIGHT/6.0);
+    cairo_set_source_rgb (cr, 0, 1, 0);
+    cairo_stroke (cr);
+    cairo_move_to (cr, 5*PAT_WIDTH/6.0, 0);
+    cairo_line_to (cr, PAT_WIDTH, 0);
+    cairo_line_to (cr, PAT_WIDTH, PAT_HEIGHT/6.0);
+    cairo_set_source_rgb (cr, 0, 0, 1);
+    cairo_stroke (cr);
+    cairo_move_to (cr, 5*PAT_WIDTH/6.0, PAT_HEIGHT);
+    cairo_line_to (cr, PAT_WIDTH, PAT_HEIGHT);
+    cairo_line_to (cr, PAT_WIDTH, 5*PAT_HEIGHT/6.0);
+    cairo_set_source_rgb (cr, 1, 1, 0);
+    cairo_stroke (cr);
+
+    cairo_set_source_rgb (cr, 0.5, 0.5, 0.5);
+    cairo_set_line_width (cr, PAT_WIDTH/10.0);
+
+    cairo_move_to (cr, 0,         PAT_HEIGHT/4.0);
+    cairo_line_to (cr, PAT_WIDTH, PAT_HEIGHT/4.0);
+    cairo_stroke (cr);
+
+    cairo_move_to (cr, PAT_WIDTH/4.0,         0);
+    cairo_line_to (cr, PAT_WIDTH/4.0, PAT_WIDTH);
+    cairo_stroke (cr);
+
+    pattern = cairo_pattern_create_for_surface (cairo_get_target (cr));
+    cairo_destroy (cr);
+
+    return pattern;
+}
+
+static cairo_test_status_t
+over (cairo_t *cr, int width, int height)
+{
+    cairo_pattern_t *pattern;
+    cairo_matrix_t   mat;
+
+    cairo_translate (cr, PAD, PAD);
+
+    pattern = create_pattern (cr);
+
+    cairo_matrix_init_identity (&mat);
+    cairo_matrix_scale (&mat, 2, 1.5);
+    cairo_matrix_rotate (&mat, 1);
+    cairo_matrix_translate (&mat, -PAT_WIDTH/4.0, -PAT_WIDTH/2.0);
+    cairo_pattern_set_matrix (pattern, &mat);
+    cairo_pattern_set_extend (pattern, CAIRO_EXTEND_NONE);
+
+    cairo_set_source (cr, pattern);
+    cairo_paint (cr);
+
+    cairo_pattern_destroy (pattern);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+static cairo_test_status_t
+source (cairo_t *cr, int width, int height)
+{
+    cairo_pattern_t *pattern;
+    cairo_matrix_t   mat;
+
+    cairo_translate (cr, PAD, PAD);
+
+    pattern = create_pattern (cr);
+
+    cairo_matrix_init_identity (&mat);
+    cairo_matrix_scale (&mat, 2, 1.5);
+    cairo_matrix_rotate (&mat, 1);
+    cairo_matrix_translate (&mat, -PAT_WIDTH/4.0, -PAT_WIDTH/2.0);
+    cairo_pattern_set_matrix (pattern, &mat);
+    cairo_pattern_set_extend (pattern, CAIRO_EXTEND_NONE);
+
+    cairo_set_source (cr, pattern);
+    cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+    cairo_paint (cr);
+
+    cairo_pattern_destroy (pattern);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (recording_surface_over,
+           "Paint recording surface pattern with non identity pattern matrix",
+           "recording", /* keywords */
+           NULL, /* requirements */
+           WIDTH, HEIGHT,
+           NULL, over)
+
+CAIRO_TEST (recording_surface_source,
+           "Paint recording surface pattern with non identity pattern matrix",
+           "recording", /* keywords */
+           NULL, /* requirements */
+           WIDTH, HEIGHT,
+           NULL, source)
diff --git a/test/rectangle-rounding-error.c b/test/rectangle-rounding-error.c
new file mode 100644 (file)
index 0000000..6ad99ae
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Copyright © 2005 Bertram Felgenhauer
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * the author not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. The author makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THE AUTHOR. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Bertram Felgenhauer <int-e@gmx.de>
+ */
+
+#include "cairo-test.h"
+
+/* Test case for:
+ *
+ *      https://bugs.freedesktop.org/show_bug.cgi?id=4137
+ */
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_paint (cr);
+
+    cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
+    cairo_set_source_rgb (cr, 0, 0, 0);
+
+    cairo_translate(cr, -300, -300);
+    cairo_scale(cr, 677.0/26, 677.0/26);
+    cairo_translate(cr, 1, 1);
+
+    /* this should draw a seamless 2x2 rectangle */
+    cairo_rectangle(cr, 11, 11, 1, 1);
+    cairo_rectangle(cr, 11, 12, 1, 1);
+    cairo_rectangle(cr, 12, 11, 1, 1);
+    cairo_rectangle(cr, 12, 12, 1, 1);
+
+    cairo_set_source_rgb(cr, 0, 0, 0);
+    cairo_fill(cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (rectangle_rounding_error,
+           "This demonstrates (or not) a rounding error that causes a gap between "
+           "two neighbouring rectangles.",
+           "trap", /* keywords */
+           "target=raster", /* requirements */
+           76, 76,
+           NULL, draw)
diff --git a/test/rectilinear-dash.c b/test/rectilinear-dash.c
new file mode 100644 (file)
index 0000000..fe998bb
--- /dev/null
@@ -0,0 +1,176 @@
+/*
+ * Copyright © 2006 Red Hat, Inc.
+ * Copyright © 2008 Chris Wilson
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ *         Chris Wilson <chris@chris-wilson.co.uk>
+ *
+ * Based on the original test/rectilinear-stroke.c by Carl D. Worth.
+ */
+
+#include "cairo-test.h"
+
+#define SIZE 50
+
+static void
+draw_dashes (cairo_t *cr)
+{
+    const double dash_square[4] = {4, 2, 2, 2};
+    const double dash_butt[4] = {5, 1, 3, 1};
+
+    cairo_save (cr);
+
+    cairo_set_dash (cr, dash_square, 4, 0);
+
+    cairo_set_line_width (cr, 1.0);
+    cairo_translate (cr, 1, 1);
+
+    /* Draw everything first with square caps. */
+    cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE);
+
+    /* Draw horizontal and vertical segments, each in both
+     * directions. */
+    cairo_move_to     (cr,  4.5,  0.5);
+    cairo_rel_line_to (cr,  2.0,  0.0);
+
+    cairo_move_to     (cr, 10.5,  4.5);
+    cairo_rel_line_to (cr,  0.0,  2.0);
+
+    cairo_move_to     (cr,  6.5, 10.5);
+    cairo_rel_line_to (cr, -2.0,  0.0);
+
+    cairo_move_to     (cr,  0.5,  6.5);
+    cairo_rel_line_to (cr,  0.0, -2.0);
+
+    /* Draw right angle turns in four directions. */
+    cairo_move_to     (cr,  0.5,  2.5);
+    cairo_rel_line_to (cr,  0.0, -2.0);
+    cairo_rel_line_to (cr,  2.0,  0.0);
+
+    cairo_move_to     (cr,  8.5,  0.5);
+    cairo_rel_line_to (cr,  2.0,  0.0);
+    cairo_rel_line_to (cr,  0.0,  2.0);
+
+    cairo_move_to     (cr, 10.5,  8.5);
+    cairo_rel_line_to (cr,  0.0,  2.0);
+    cairo_rel_line_to (cr, -2.0,  0.0);
+
+    cairo_move_to     (cr,  2.5, 10.5);
+    cairo_rel_line_to (cr, -2.0,  0.0);
+    cairo_rel_line_to (cr,  0.0, -2.0);
+
+    cairo_stroke (cr);
+
+    /* Draw a closed-path rectangle */
+    cairo_rectangle (cr, 0.5, 12.5, 10.0, 10.0);
+    cairo_set_dash (cr, dash_square, 4, 2);
+    cairo_stroke (cr);
+
+    cairo_translate (cr, 12, 0);
+
+    /* Now draw the same results, but with butt caps. */
+    cairo_set_line_cap (cr, CAIRO_LINE_CAP_BUTT);
+    cairo_set_dash (cr, dash_butt, 4, 0.0);
+
+    /* Draw horizontal and vertical segments, each in both
+     * directions. */
+    cairo_move_to     (cr,  4.0,  0.5);
+    cairo_rel_line_to (cr,  3.0,  0.0);
+
+    cairo_move_to     (cr, 10.5,  4.0);
+    cairo_rel_line_to (cr,  0.0,  3.0);
+
+    cairo_move_to     (cr,  7.0, 10.5);
+    cairo_rel_line_to (cr, -3.0,  0.0);
+
+    cairo_move_to     (cr,  0.5,  7.0);
+    cairo_rel_line_to (cr,  0.0, -3.0);
+
+    /* Draw right angle turns in four directions. */
+    cairo_move_to     (cr,  0.5,  3.0);
+    cairo_rel_line_to (cr,  0.0, -2.5);
+    cairo_rel_line_to (cr,  2.5,  0.0);
+
+    cairo_move_to     (cr,  8.0,  0.5);
+    cairo_rel_line_to (cr,  2.5,  0.0);
+    cairo_rel_line_to (cr,  0.0,  2.5);
+
+    cairo_move_to     (cr, 10.5,  8.0);
+    cairo_rel_line_to (cr,  0.0,  2.5);
+    cairo_rel_line_to (cr, -2.5,  0.0);
+
+    cairo_move_to     (cr,  3.0, 10.5);
+    cairo_rel_line_to (cr, -2.5,  0.0);
+    cairo_rel_line_to (cr,  0.0, -2.5);
+
+    cairo_stroke (cr);
+
+    /* Draw a closed-path rectangle */
+    cairo_set_dash (cr, dash_butt, 4, 2.5);
+    cairo_rectangle (cr, 0.5, 12.5, 10.0, 10.0);
+    cairo_stroke (cr);
+
+    cairo_restore (cr);
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    /* Paint background white, then draw in black. */
+    cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
+    cairo_paint (cr);
+
+    cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
+    draw_dashes (cr);
+
+    cairo_save (cr);
+    cairo_set_source_rgb (cr, 1.0, 0.0, 0.0);
+    cairo_translate (cr, 0, height);
+    cairo_scale (cr, 1, -1);
+    draw_dashes (cr);
+    cairo_restore (cr);
+
+    cairo_save (cr);
+    cairo_set_source_rgb (cr, 0.0, 1.0, 0.0);
+    cairo_translate (cr, width, 0);
+    cairo_scale (cr, -1, 1);
+    draw_dashes (cr);
+    cairo_restore (cr);
+
+    cairo_save (cr);
+    cairo_set_source_rgb (cr, 0.0, 0.0, 1.0);
+    cairo_translate (cr, width, height);
+    cairo_scale (cr, -1, -1);
+    draw_dashes (cr);
+    cairo_restore (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (rectilinear_dash,
+           "Test dashed rectilinear stroke operations (covering only whole pixels)",
+           "stroke dash", /* keywords */
+           NULL, /* requirements */
+           SIZE, SIZE,
+           NULL, draw)
+
diff --git a/test/rectilinear-fill.c b/test/rectilinear-fill.c
new file mode 100644 (file)
index 0000000..3678379
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * Copyright © 2008 Chris Wilson
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+
+#define SIZE 24
+
+static void
+draw_rectangles (cairo_t *cr)
+{
+    cairo_save (cr);
+
+    /* test constructing single rectangles */
+    cairo_rectangle (cr, 0, 0, SIZE/2, 2);
+    cairo_fill (cr);
+
+    cairo_rectangle (cr, 0, 5, SIZE/2, -2);
+    cairo_fill (cr);
+
+    cairo_rectangle (cr, SIZE/2, 6, -SIZE/2, 2);
+    cairo_fill (cr);
+
+    cairo_rectangle (cr, SIZE/2, 11, -SIZE/2, -2);
+    cairo_fill (cr);
+
+    /* test constructing multiple rectangles */
+    cairo_translate (cr, 0, 12);
+    cairo_rectangle (cr, 0, 0, SIZE/2, 2);
+    cairo_rectangle (cr, 0, 5, SIZE/2, -2);
+    cairo_rectangle (cr, SIZE/2, 6, -SIZE/2, 2);
+    cairo_rectangle (cr, SIZE/2, 11, -SIZE/2, -2);
+    cairo_fill (cr);
+
+    cairo_restore (cr);
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    /* Paint background white, then draw in black. */
+    cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); /* white */
+    cairo_paint (cr);
+    cairo_set_source_rgb (cr, 0.0, 0.0, 0.0); /* black */
+
+    draw_rectangles (cr);
+
+    /* and check using cw winding */
+    cairo_translate (cr, SIZE, SIZE);
+    cairo_scale (cr, -1, 1);
+
+    draw_rectangles (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (rectilinear_fill,
+           "Test rectilinear fill operations (covering only whole pixels)",
+           "fill, rectilinear", /* keywords */
+           NULL, /* requirements */
+           SIZE, 2 * SIZE,
+           NULL, draw)
diff --git a/test/rectilinear-grid.c b/test/rectilinear-grid.c
new file mode 100644 (file)
index 0000000..6d37c6b
--- /dev/null
@@ -0,0 +1,92 @@
+/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/*
+ * Copyright 2010 Andrea Canciani
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Andrea Canciani <ranma42@gmail.com>
+ */
+
+#include "cairo-test.h"
+
+#define SIZE 52
+#define OFFSET 5
+#define DISTANCE 10.25
+
+/*
+  This test checks that boxes not aligned to pixels are drawn
+  correctly.
+
+  In particular the corners of the boxes are drawn incorrectly by
+  cairo-image in cairo 1.10.0, because overlapping boxes are passed to
+  a span converter which assumes disjoint boxes as input.
+
+  This results in corners to be drawn with the wrong shade.
+*/
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    int i;
+
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_paint (cr);
+
+    cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
+    cairo_set_source_rgb (cr, 0, 0, 0);
+    cairo_set_line_width (cr, 4);
+    cairo_translate (cr, 2*OFFSET, 2*OFFSET);
+
+    for (i = 0; i < 4; i++) {
+       double x = i * DISTANCE;
+
+       cairo_move_to (cr, x, -OFFSET-0.75);
+       cairo_line_to (cr, x, SIZE-3*OFFSET-0.25);
+
+       cairo_move_to (cr, -OFFSET-0.75, x);
+       cairo_line_to (cr, SIZE-3*OFFSET-0.25, x);
+    }
+
+    cairo_stroke (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+static cairo_test_status_t
+aligned (cairo_t *cr, int width, int height)
+{
+    cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
+    return draw (cr, width, height);
+}
+
+CAIRO_TEST (rectilinear_grid,
+           "Test rectilinear rasterizer (covering partial pixels)",
+           "rectilinear", /* keywords */
+           NULL, /* requirements */
+           SIZE, SIZE,
+           NULL, draw)
+
+CAIRO_TEST (a1_rectilinear_grid,
+           "Test rectilinear rasterizer (covering whole pixels)",
+           "rectilinear", /* keywords */
+           "target=raster", /* requirements */
+           SIZE, SIZE,
+           NULL, aligned)
diff --git a/test/rectilinear-miter-limit.c b/test/rectilinear-miter-limit.c
new file mode 100644 (file)
index 0000000..581d98b
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * Copyright © 2008 Red Hat, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ */
+
+#include "cairo-test.h"
+
+#define LINE_WIDTH  10
+#define PAD        2
+#define WIDTH      (PAD + LINE_WIDTH + PAD)
+#define HEIGHT     (WIDTH)
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_translate (cr, PAD, PAD);
+
+    /* Paint background white, then draw in black. */
+    cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); /* white */
+    cairo_paint (cr);
+    cairo_set_source_rgb (cr, 0.0, 0.0, 0.0); /* black */
+
+    cairo_set_line_cap (cr, CAIRO_LINE_CAP_BUTT);
+    cairo_set_line_width (cr, LINE_WIDTH);
+
+    /* The default miter limit value of 10.0 guarantees that
+     * right-angle turns, (in fact, any angle greater than 11
+     * degrees), gets a miter rather than a bevel join. The
+     * rectilinear stroke optimization was originally written in a
+     * buggy way that did not respect the miter limit, (that is,
+     * inappropriately drawing miter joins when the miter limit would
+     * turn them into bevels). So we draw here with a miter limit of
+     * 1.0 to force all miter joins into bevels. */
+    cairo_set_line_join (cr, CAIRO_LINE_JOIN_MITER);
+    cairo_set_miter_limit (cr, 1.0);
+
+    cairo_move_to (cr, LINE_WIDTH / 2.0, LINE_WIDTH);
+    cairo_rel_line_to (cr, 0, - LINE_WIDTH / 2.0);
+    cairo_rel_line_to (cr, LINE_WIDTH / 2.0, 0);
+
+    cairo_stroke (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (rectilinear_miter_limit,
+           "Test that the rectilinear stroke optimization doesn't break cairo_set_miter_limit",
+           "miter, stroke, stress", /* keywords */
+           NULL, /* requirements */
+           WIDTH, HEIGHT,
+           NULL, draw)
diff --git a/test/rectilinear-stroke.c b/test/rectilinear-stroke.c
new file mode 100644 (file)
index 0000000..e05ff8c
--- /dev/null
@@ -0,0 +1,139 @@
+/*
+ * Copyright © 2006 Red Hat, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ */
+
+#include "cairo-test.h"
+
+#define SIZE 25
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    /* Paint background white, then draw in black. */
+    cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); /* white */
+    cairo_paint (cr);
+    cairo_set_source_rgb (cr, 0.0, 0.0, 0.0); /* black */
+
+    cairo_set_line_width (cr, 1.0);
+    cairo_translate (cr, 1, 1);
+
+    /* Draw everything first with square caps. */
+    cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE);
+
+    /* Draw horizontal and vertical segments, each in both
+     * directions. */
+    cairo_move_to     (cr,  4.5,  0.5);
+    cairo_rel_line_to (cr,  2.0,  0.0);
+
+    cairo_move_to     (cr, 10.5,  4.5);
+    cairo_rel_line_to (cr,  0.0,  2.0);
+
+    cairo_move_to     (cr,  6.5, 10.5);
+    cairo_rel_line_to (cr, -2.0,  0.0);
+
+    cairo_move_to     (cr,  0.5,  6.5);
+    cairo_rel_line_to (cr,  0.0, -2.0);
+
+    /* Draw right angle turns in four directions. */
+    cairo_move_to     (cr,  0.5,  2.5);
+    cairo_rel_line_to (cr,  0.0, -2.0);
+    cairo_rel_line_to (cr,  2.0,  0.0);
+
+    cairo_move_to     (cr,  8.5,  0.5);
+    cairo_rel_line_to (cr,  2.0,  0.0);
+    cairo_rel_line_to (cr,  0.0,  2.0);
+
+    cairo_move_to     (cr, 10.5,  8.5);
+    cairo_rel_line_to (cr,  0.0,  2.0);
+    cairo_rel_line_to (cr, -2.0,  0.0);
+
+    cairo_move_to     (cr,  2.5, 10.5);
+    cairo_rel_line_to (cr, -2.0,  0.0);
+    cairo_rel_line_to (cr,  0.0, -2.0);
+
+    /* Draw a closed-path rectangle */
+    cairo_rectangle   (cr, 0.5, 12.5, 10.0, 10.0);
+
+    cairo_stroke (cr);
+
+    cairo_translate (cr, 12, 0);
+
+    /* Now draw the same results, but with butt caps. */
+    cairo_set_line_cap (cr, CAIRO_LINE_CAP_BUTT);
+
+    /* Draw horizontal and vertical segments, each in both
+     * directions. */
+    cairo_move_to     (cr,  4.0,  0.5);
+    cairo_rel_line_to (cr,  3.0,  0.0);
+
+    cairo_move_to     (cr, 10.5,  4.0);
+    cairo_rel_line_to (cr,  0.0,  3.0);
+
+    cairo_move_to     (cr,  7.0, 10.5);
+    cairo_rel_line_to (cr, -3.0,  0.0);
+
+    cairo_move_to     (cr,  0.5,  7.0);
+    cairo_rel_line_to (cr,  0.0, -3.0);
+
+    /* Draw right angle turns in four directions. */
+    cairo_move_to     (cr,  0.5,  3.0);
+    cairo_rel_line_to (cr,  0.0, -2.5);
+    cairo_rel_line_to (cr,  2.5,  0.0);
+
+    cairo_move_to     (cr,  8.0,  0.5);
+    cairo_rel_line_to (cr,  2.5,  0.0);
+    cairo_rel_line_to (cr,  0.0,  2.5);
+
+    cairo_move_to     (cr, 10.5,  8.0);
+    cairo_rel_line_to (cr,  0.0,  2.5);
+    cairo_rel_line_to (cr, -2.5,  0.0);
+
+    cairo_move_to     (cr,  3.0, 10.5);
+    cairo_rel_line_to (cr, -2.5,  0.0);
+    cairo_rel_line_to (cr,  0.0, -2.5);
+
+    /* Draw a closed-path rectangle */
+    cairo_rectangle   (cr, 0.5, 12.5, 10.0, 10.0);
+
+    /* Draw a path that is rectilinear initially, but not completely */
+    /* We draw this out of the target window.  The bug that caused this
+     * addition was leaks if part of the path was rectilinear but not
+     * completely */
+    cairo_move_to     (cr,  3.0, 30.5);
+    cairo_rel_line_to (cr, -2.5,  0.0);
+    cairo_rel_line_to (cr, +2.5, +2.5);
+
+    cairo_stroke (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (rectilinear_stroke,
+           "Test rectilinear stroke operations (covering only whole pixels)",
+           "stroke", /* keywords */
+           NULL, /* requirements */
+           SIZE, SIZE,
+           NULL, draw)
+
diff --git a/test/reference/a1-bug.base.argb32.ref.png b/test/reference/a1-bug.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..4b08e06
Binary files /dev/null and b/test/reference/a1-bug.base.argb32.ref.png differ
diff --git a/test/reference/a1-bug.base.rgb24.ref.png b/test/reference/a1-bug.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..4b08e06
Binary files /dev/null and b/test/reference/a1-bug.base.rgb24.ref.png differ
diff --git a/test/reference/a1-bug.image16.ref.png b/test/reference/a1-bug.image16.ref.png
new file mode 100644 (file)
index 0000000..48ce4b6
Binary files /dev/null and b/test/reference/a1-bug.image16.ref.png differ
diff --git a/test/reference/a1-bug.quartz.xfail.png b/test/reference/a1-bug.quartz.xfail.png
new file mode 100644 (file)
index 0000000..4ed3793
Binary files /dev/null and b/test/reference/a1-bug.quartz.xfail.png differ
diff --git a/test/reference/a1-bug.ref.png b/test/reference/a1-bug.ref.png
new file mode 100644 (file)
index 0000000..9c6a36e
Binary files /dev/null and b/test/reference/a1-bug.ref.png differ
diff --git a/test/reference/a1-bug.traps.argb32.ref.png b/test/reference/a1-bug.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..4b08e06
Binary files /dev/null and b/test/reference/a1-bug.traps.argb32.ref.png differ
diff --git a/test/reference/a1-bug.traps.rgb24.ref.png b/test/reference/a1-bug.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..4b08e06
Binary files /dev/null and b/test/reference/a1-bug.traps.rgb24.ref.png differ
diff --git a/test/reference/a1-clip-fill-equal.base.argb32.ref.png b/test/reference/a1-clip-fill-equal.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..384ba4a
Binary files /dev/null and b/test/reference/a1-clip-fill-equal.base.argb32.ref.png differ
diff --git a/test/reference/a1-clip-fill-equal.base.rgb24.ref.png b/test/reference/a1-clip-fill-equal.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..384ba4a
Binary files /dev/null and b/test/reference/a1-clip-fill-equal.base.rgb24.ref.png differ
diff --git a/test/reference/a1-clip-fill-equal.ref.png b/test/reference/a1-clip-fill-equal.ref.png
new file mode 100644 (file)
index 0000000..384ba4a
Binary files /dev/null and b/test/reference/a1-clip-fill-equal.ref.png differ
diff --git a/test/reference/a1-clip-fill-equal.traps.argb32.ref.png b/test/reference/a1-clip-fill-equal.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..384ba4a
Binary files /dev/null and b/test/reference/a1-clip-fill-equal.traps.argb32.ref.png differ
diff --git a/test/reference/a1-clip-fill-equal.traps.rgb24.ref.png b/test/reference/a1-clip-fill-equal.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..384ba4a
Binary files /dev/null and b/test/reference/a1-clip-fill-equal.traps.rgb24.ref.png differ
diff --git a/test/reference/a1-clip-fill-rule.argb32.ref.png b/test/reference/a1-clip-fill-rule.argb32.ref.png
new file mode 100644 (file)
index 0000000..c3ba9dd
Binary files /dev/null and b/test/reference/a1-clip-fill-rule.argb32.ref.png differ
diff --git a/test/reference/a1-clip-fill-rule.base.argb32.ref.png b/test/reference/a1-clip-fill-rule.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..c3ba9dd
Binary files /dev/null and b/test/reference/a1-clip-fill-rule.base.argb32.ref.png differ
diff --git a/test/reference/a1-clip-fill-rule.base.rgb24.ref.png b/test/reference/a1-clip-fill-rule.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..6fe9346
Binary files /dev/null and b/test/reference/a1-clip-fill-rule.base.rgb24.ref.png differ
diff --git a/test/reference/a1-clip-fill-rule.rgb24.ref.png b/test/reference/a1-clip-fill-rule.rgb24.ref.png
new file mode 100644 (file)
index 0000000..6fe9346
Binary files /dev/null and b/test/reference/a1-clip-fill-rule.rgb24.ref.png differ
diff --git a/test/reference/a1-clip-fill-rule.traps.argb32.ref.png b/test/reference/a1-clip-fill-rule.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..c3ba9dd
Binary files /dev/null and b/test/reference/a1-clip-fill-rule.traps.argb32.ref.png differ
diff --git a/test/reference/a1-clip-fill-rule.traps.rgb24.ref.png b/test/reference/a1-clip-fill-rule.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..6fe9346
Binary files /dev/null and b/test/reference/a1-clip-fill-rule.traps.rgb24.ref.png differ
diff --git a/test/reference/a1-clip-fill.base.argb32.ref.png b/test/reference/a1-clip-fill.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..384ba4a
Binary files /dev/null and b/test/reference/a1-clip-fill.base.argb32.ref.png differ
diff --git a/test/reference/a1-clip-fill.base.rgb24.ref.png b/test/reference/a1-clip-fill.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..384ba4a
Binary files /dev/null and b/test/reference/a1-clip-fill.base.rgb24.ref.png differ
diff --git a/test/reference/a1-clip-fill.ref.png b/test/reference/a1-clip-fill.ref.png
new file mode 100644 (file)
index 0000000..384ba4a
Binary files /dev/null and b/test/reference/a1-clip-fill.ref.png differ
diff --git a/test/reference/a1-clip-fill.traps.argb32.ref.png b/test/reference/a1-clip-fill.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..384ba4a
Binary files /dev/null and b/test/reference/a1-clip-fill.traps.argb32.ref.png differ
diff --git a/test/reference/a1-clip-fill.traps.rgb24.ref.png b/test/reference/a1-clip-fill.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..384ba4a
Binary files /dev/null and b/test/reference/a1-clip-fill.traps.rgb24.ref.png differ
diff --git a/test/reference/a1-clip-paint.base.argb32.ref.png b/test/reference/a1-clip-paint.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..384ba4a
Binary files /dev/null and b/test/reference/a1-clip-paint.base.argb32.ref.png differ
diff --git a/test/reference/a1-clip-paint.base.rgb24.ref.png b/test/reference/a1-clip-paint.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..384ba4a
Binary files /dev/null and b/test/reference/a1-clip-paint.base.rgb24.ref.png differ
diff --git a/test/reference/a1-clip-paint.ref.png b/test/reference/a1-clip-paint.ref.png
new file mode 100644 (file)
index 0000000..384ba4a
Binary files /dev/null and b/test/reference/a1-clip-paint.ref.png differ
diff --git a/test/reference/a1-clip-paint.traps.argb32.ref.png b/test/reference/a1-clip-paint.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..384ba4a
Binary files /dev/null and b/test/reference/a1-clip-paint.traps.argb32.ref.png differ
diff --git a/test/reference/a1-clip-paint.traps.rgb24.ref.png b/test/reference/a1-clip-paint.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..384ba4a
Binary files /dev/null and b/test/reference/a1-clip-paint.traps.rgb24.ref.png differ
diff --git a/test/reference/a1-clip-stroke.base.argb32.ref.png b/test/reference/a1-clip-stroke.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..384ba4a
Binary files /dev/null and b/test/reference/a1-clip-stroke.base.argb32.ref.png differ
diff --git a/test/reference/a1-clip-stroke.base.rgb24.ref.png b/test/reference/a1-clip-stroke.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..384ba4a
Binary files /dev/null and b/test/reference/a1-clip-stroke.base.rgb24.ref.png differ
diff --git a/test/reference/a1-clip-stroke.ref.png b/test/reference/a1-clip-stroke.ref.png
new file mode 100644 (file)
index 0000000..384ba4a
Binary files /dev/null and b/test/reference/a1-clip-stroke.ref.png differ
diff --git a/test/reference/a1-clip-stroke.traps.argb32.ref.png b/test/reference/a1-clip-stroke.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..384ba4a
Binary files /dev/null and b/test/reference/a1-clip-stroke.traps.argb32.ref.png differ
diff --git a/test/reference/a1-clip-stroke.traps.rgb24.ref.png b/test/reference/a1-clip-stroke.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..384ba4a
Binary files /dev/null and b/test/reference/a1-clip-stroke.traps.rgb24.ref.png differ
diff --git a/test/reference/a1-fill.base.ref.png b/test/reference/a1-fill.base.ref.png
new file mode 100644 (file)
index 0000000..e40202b
Binary files /dev/null and b/test/reference/a1-fill.base.ref.png differ
diff --git a/test/reference/a1-fill.ref.png b/test/reference/a1-fill.ref.png
new file mode 100644 (file)
index 0000000..e40202b
Binary files /dev/null and b/test/reference/a1-fill.ref.png differ
diff --git a/test/reference/a1-fill.traps.ref.png b/test/reference/a1-fill.traps.ref.png
new file mode 100644 (file)
index 0000000..e40202b
Binary files /dev/null and b/test/reference/a1-fill.traps.ref.png differ
diff --git a/test/reference/a1-image-sample.base.argb32.ref.png b/test/reference/a1-image-sample.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..b8fd457
Binary files /dev/null and b/test/reference/a1-image-sample.base.argb32.ref.png differ
diff --git a/test/reference/a1-image-sample.base.rgb24.ref.png b/test/reference/a1-image-sample.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..b8fd457
Binary files /dev/null and b/test/reference/a1-image-sample.base.rgb24.ref.png differ
diff --git a/test/reference/a1-image-sample.gl.xfail.png b/test/reference/a1-image-sample.gl.xfail.png
new file mode 100644 (file)
index 0000000..384ba4a
Binary files /dev/null and b/test/reference/a1-image-sample.gl.xfail.png differ
diff --git a/test/reference/a1-image-sample.ref.png b/test/reference/a1-image-sample.ref.png
new file mode 100644 (file)
index 0000000..b4e81eb
Binary files /dev/null and b/test/reference/a1-image-sample.ref.png differ
diff --git a/test/reference/a1-image-sample.traps.argb32.ref.png b/test/reference/a1-image-sample.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..b8fd457
Binary files /dev/null and b/test/reference/a1-image-sample.traps.argb32.ref.png differ
diff --git a/test/reference/a1-image-sample.traps.rgb24.ref.png b/test/reference/a1-image-sample.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..b8fd457
Binary files /dev/null and b/test/reference/a1-image-sample.traps.rgb24.ref.png differ
diff --git a/test/reference/a1-line-width.base.argb32.ref.png b/test/reference/a1-line-width.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..35d9cad
Binary files /dev/null and b/test/reference/a1-line-width.base.argb32.ref.png differ
diff --git a/test/reference/a1-line-width.base.rgb24.ref.png b/test/reference/a1-line-width.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..35d9cad
Binary files /dev/null and b/test/reference/a1-line-width.base.rgb24.ref.png differ
diff --git a/test/reference/a1-line-width.pdf.ref.png b/test/reference/a1-line-width.pdf.ref.png
new file mode 100644 (file)
index 0000000..41a06e7
Binary files /dev/null and b/test/reference/a1-line-width.pdf.ref.png differ
diff --git a/test/reference/a1-line-width.ps.ref.png b/test/reference/a1-line-width.ps.ref.png
new file mode 100644 (file)
index 0000000..c52f8d8
Binary files /dev/null and b/test/reference/a1-line-width.ps.ref.png differ
diff --git a/test/reference/a1-line-width.ref.png b/test/reference/a1-line-width.ref.png
new file mode 100644 (file)
index 0000000..35d9cad
Binary files /dev/null and b/test/reference/a1-line-width.ref.png differ
diff --git a/test/reference/a1-line-width.traps.argb32.ref.png b/test/reference/a1-line-width.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..35d9cad
Binary files /dev/null and b/test/reference/a1-line-width.traps.argb32.ref.png differ
diff --git a/test/reference/a1-line-width.traps.rgb24.ref.png b/test/reference/a1-line-width.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..35d9cad
Binary files /dev/null and b/test/reference/a1-line-width.traps.rgb24.ref.png differ
diff --git a/test/reference/a1-mask-sample.base.argb32.ref.png b/test/reference/a1-mask-sample.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..b8fd457
Binary files /dev/null and b/test/reference/a1-mask-sample.base.argb32.ref.png differ
diff --git a/test/reference/a1-mask-sample.base.rgb24.ref.png b/test/reference/a1-mask-sample.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..b8fd457
Binary files /dev/null and b/test/reference/a1-mask-sample.base.rgb24.ref.png differ
diff --git a/test/reference/a1-mask-sample.ref.png b/test/reference/a1-mask-sample.ref.png
new file mode 100644 (file)
index 0000000..b4e81eb
Binary files /dev/null and b/test/reference/a1-mask-sample.ref.png differ
diff --git a/test/reference/a1-mask-sample.traps.argb32.ref.png b/test/reference/a1-mask-sample.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..b8fd457
Binary files /dev/null and b/test/reference/a1-mask-sample.traps.argb32.ref.png differ
diff --git a/test/reference/a1-mask-sample.traps.rgb24.ref.png b/test/reference/a1-mask-sample.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..b8fd457
Binary files /dev/null and b/test/reference/a1-mask-sample.traps.rgb24.ref.png differ
diff --git a/test/reference/a1-mask.base.argb32.ref.png b/test/reference/a1-mask.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..864fc10
Binary files /dev/null and b/test/reference/a1-mask.base.argb32.ref.png differ
diff --git a/test/reference/a1-mask.base.rgb24.ref.png b/test/reference/a1-mask.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..864fc10
Binary files /dev/null and b/test/reference/a1-mask.base.rgb24.ref.png differ
diff --git a/test/reference/a1-mask.pdf.ref.png b/test/reference/a1-mask.pdf.ref.png
new file mode 100644 (file)
index 0000000..864fc10
Binary files /dev/null and b/test/reference/a1-mask.pdf.ref.png differ
diff --git a/test/reference/a1-mask.ps.ref.png b/test/reference/a1-mask.ps.ref.png
new file mode 100644 (file)
index 0000000..864fc10
Binary files /dev/null and b/test/reference/a1-mask.ps.ref.png differ
diff --git a/test/reference/a1-mask.ref.png b/test/reference/a1-mask.ref.png
new file mode 100644 (file)
index 0000000..ac4d97a
Binary files /dev/null and b/test/reference/a1-mask.ref.png differ
diff --git a/test/reference/a1-mask.traps.argb32.ref.png b/test/reference/a1-mask.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..864fc10
Binary files /dev/null and b/test/reference/a1-mask.traps.argb32.ref.png differ
diff --git a/test/reference/a1-mask.traps.rgb24.ref.png b/test/reference/a1-mask.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..864fc10
Binary files /dev/null and b/test/reference/a1-mask.traps.rgb24.ref.png differ
diff --git a/test/reference/a1-rasterisation-rectangles.base.argb32.ref.png b/test/reference/a1-rasterisation-rectangles.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..784cf87
Binary files /dev/null and b/test/reference/a1-rasterisation-rectangles.base.argb32.ref.png differ
diff --git a/test/reference/a1-rasterisation-rectangles.base.rgb24.ref.png b/test/reference/a1-rasterisation-rectangles.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..784cf87
Binary files /dev/null and b/test/reference/a1-rasterisation-rectangles.base.rgb24.ref.png differ
diff --git a/test/reference/a1-rasterisation-rectangles.quartz.xfail.png b/test/reference/a1-rasterisation-rectangles.quartz.xfail.png
new file mode 100644 (file)
index 0000000..f8f3bf8
Binary files /dev/null and b/test/reference/a1-rasterisation-rectangles.quartz.xfail.png differ
diff --git a/test/reference/a1-rasterisation-rectangles.ref.png b/test/reference/a1-rasterisation-rectangles.ref.png
new file mode 100644 (file)
index 0000000..784cf87
Binary files /dev/null and b/test/reference/a1-rasterisation-rectangles.ref.png differ
diff --git a/test/reference/a1-rasterisation-rectangles.traps.argb32.ref.png b/test/reference/a1-rasterisation-rectangles.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..784cf87
Binary files /dev/null and b/test/reference/a1-rasterisation-rectangles.traps.argb32.ref.png differ
diff --git a/test/reference/a1-rasterisation-rectangles.traps.rgb24.ref.png b/test/reference/a1-rasterisation-rectangles.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..784cf87
Binary files /dev/null and b/test/reference/a1-rasterisation-rectangles.traps.rgb24.ref.png differ
diff --git a/test/reference/a1-rasterisation-triangles.base.argb32.ref.png b/test/reference/a1-rasterisation-triangles.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..784cf87
Binary files /dev/null and b/test/reference/a1-rasterisation-triangles.base.argb32.ref.png differ
diff --git a/test/reference/a1-rasterisation-triangles.base.rgb24.ref.png b/test/reference/a1-rasterisation-triangles.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..784cf87
Binary files /dev/null and b/test/reference/a1-rasterisation-triangles.base.rgb24.ref.png differ
diff --git a/test/reference/a1-rasterisation-triangles.quartz.xfail.png b/test/reference/a1-rasterisation-triangles.quartz.xfail.png
new file mode 100644 (file)
index 0000000..f8f3bf8
Binary files /dev/null and b/test/reference/a1-rasterisation-triangles.quartz.xfail.png differ
diff --git a/test/reference/a1-rasterisation-triangles.ref.png b/test/reference/a1-rasterisation-triangles.ref.png
new file mode 100644 (file)
index 0000000..784cf87
Binary files /dev/null and b/test/reference/a1-rasterisation-triangles.ref.png differ
diff --git a/test/reference/a1-rasterisation-triangles.traps.argb32.ref.png b/test/reference/a1-rasterisation-triangles.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..784cf87
Binary files /dev/null and b/test/reference/a1-rasterisation-triangles.traps.argb32.ref.png differ
diff --git a/test/reference/a1-rasterisation-triangles.traps.rgb24.ref.png b/test/reference/a1-rasterisation-triangles.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..784cf87
Binary files /dev/null and b/test/reference/a1-rasterisation-triangles.traps.rgb24.ref.png differ
diff --git a/test/reference/a1-rectilinear-grid.base.argb32.ref.png b/test/reference/a1-rectilinear-grid.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..2dfb85e
Binary files /dev/null and b/test/reference/a1-rectilinear-grid.base.argb32.ref.png differ
diff --git a/test/reference/a1-rectilinear-grid.base.rgb24.ref.png b/test/reference/a1-rectilinear-grid.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..2dfb85e
Binary files /dev/null and b/test/reference/a1-rectilinear-grid.base.rgb24.ref.png differ
diff --git a/test/reference/a1-rectilinear-grid.ref.png b/test/reference/a1-rectilinear-grid.ref.png
new file mode 100644 (file)
index 0000000..2dfb85e
Binary files /dev/null and b/test/reference/a1-rectilinear-grid.ref.png differ
diff --git a/test/reference/a1-rectilinear-grid.traps.argb32.ref.png b/test/reference/a1-rectilinear-grid.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..2dfb85e
Binary files /dev/null and b/test/reference/a1-rectilinear-grid.traps.argb32.ref.png differ
diff --git a/test/reference/a1-rectilinear-grid.traps.rgb24.ref.png b/test/reference/a1-rectilinear-grid.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..2dfb85e
Binary files /dev/null and b/test/reference/a1-rectilinear-grid.traps.rgb24.ref.png differ
diff --git a/test/reference/a1-sample.base.argb32.ref.png b/test/reference/a1-sample.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..4c6131c
Binary files /dev/null and b/test/reference/a1-sample.base.argb32.ref.png differ
diff --git a/test/reference/a1-sample.base.rgb24.ref.png b/test/reference/a1-sample.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..4c6131c
Binary files /dev/null and b/test/reference/a1-sample.base.rgb24.ref.png differ
diff --git a/test/reference/a1-sample.ref.png b/test/reference/a1-sample.ref.png
new file mode 100644 (file)
index 0000000..4c6131c
Binary files /dev/null and b/test/reference/a1-sample.ref.png differ
diff --git a/test/reference/a1-sample.traps.argb32.ref.png b/test/reference/a1-sample.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..4c6131c
Binary files /dev/null and b/test/reference/a1-sample.traps.argb32.ref.png differ
diff --git a/test/reference/a1-sample.traps.rgb24.ref.png b/test/reference/a1-sample.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..4c6131c
Binary files /dev/null and b/test/reference/a1-sample.traps.rgb24.ref.png differ
diff --git a/test/reference/a1-tiger.base.argb32.ref.png b/test/reference/a1-tiger.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..3847282
Binary files /dev/null and b/test/reference/a1-tiger.base.argb32.ref.png differ
diff --git a/test/reference/a1-tiger.base.rgb24.ref.png b/test/reference/a1-tiger.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..3847282
Binary files /dev/null and b/test/reference/a1-tiger.base.rgb24.ref.png differ
diff --git a/test/reference/a1-tiger.ref.png b/test/reference/a1-tiger.ref.png
new file mode 100644 (file)
index 0000000..e207d7d
Binary files /dev/null and b/test/reference/a1-tiger.ref.png differ
diff --git a/test/reference/a1-tiger.traps.argb32.ref.png b/test/reference/a1-tiger.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..3847282
Binary files /dev/null and b/test/reference/a1-tiger.traps.argb32.ref.png differ
diff --git a/test/reference/a1-tiger.traps.rgb24.ref.png b/test/reference/a1-tiger.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..3847282
Binary files /dev/null and b/test/reference/a1-tiger.traps.rgb24.ref.png differ
diff --git a/test/reference/a1-traps-sample.base.argb32.ref.png b/test/reference/a1-traps-sample.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..384ba4a
Binary files /dev/null and b/test/reference/a1-traps-sample.base.argb32.ref.png differ
diff --git a/test/reference/a1-traps-sample.base.rgb24.ref.png b/test/reference/a1-traps-sample.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..384ba4a
Binary files /dev/null and b/test/reference/a1-traps-sample.base.rgb24.ref.png differ
diff --git a/test/reference/a1-traps-sample.quartz.xfail.png b/test/reference/a1-traps-sample.quartz.xfail.png
new file mode 100644 (file)
index 0000000..c89f4fe
Binary files /dev/null and b/test/reference/a1-traps-sample.quartz.xfail.png differ
diff --git a/test/reference/a1-traps-sample.ref.png b/test/reference/a1-traps-sample.ref.png
new file mode 100644 (file)
index 0000000..384ba4a
Binary files /dev/null and b/test/reference/a1-traps-sample.ref.png differ
diff --git a/test/reference/a1-traps-sample.traps.argb32.ref.png b/test/reference/a1-traps-sample.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..384ba4a
Binary files /dev/null and b/test/reference/a1-traps-sample.traps.argb32.ref.png differ
diff --git a/test/reference/a1-traps-sample.traps.rgb24.ref.png b/test/reference/a1-traps-sample.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..384ba4a
Binary files /dev/null and b/test/reference/a1-traps-sample.traps.rgb24.ref.png differ
diff --git a/test/reference/a8-clear.base.argb32.ref.png b/test/reference/a8-clear.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..5201080
Binary files /dev/null and b/test/reference/a8-clear.base.argb32.ref.png differ
diff --git a/test/reference/a8-clear.base.rgb24.ref.png b/test/reference/a8-clear.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..5201080
Binary files /dev/null and b/test/reference/a8-clear.base.rgb24.ref.png differ
diff --git a/test/reference/a8-clear.quartz.ref.png b/test/reference/a8-clear.quartz.ref.png
new file mode 100644 (file)
index 0000000..5b7c67f
Binary files /dev/null and b/test/reference/a8-clear.quartz.ref.png differ
diff --git a/test/reference/a8-clear.ref.png b/test/reference/a8-clear.ref.png
new file mode 100644 (file)
index 0000000..23260c4
Binary files /dev/null and b/test/reference/a8-clear.ref.png differ
diff --git a/test/reference/a8-clear.traps.argb32.ref.png b/test/reference/a8-clear.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..5201080
Binary files /dev/null and b/test/reference/a8-clear.traps.argb32.ref.png differ
diff --git a/test/reference/a8-clear.traps.rgb24.ref.png b/test/reference/a8-clear.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..5201080
Binary files /dev/null and b/test/reference/a8-clear.traps.rgb24.ref.png differ
diff --git a/test/reference/a8-mask.base.argb32.ref.png b/test/reference/a8-mask.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..e0503d9
Binary files /dev/null and b/test/reference/a8-mask.base.argb32.ref.png differ
diff --git a/test/reference/a8-mask.base.rgb24.ref.png b/test/reference/a8-mask.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..e0503d9
Binary files /dev/null and b/test/reference/a8-mask.base.rgb24.ref.png differ
diff --git a/test/reference/a8-mask.ref.png b/test/reference/a8-mask.ref.png
new file mode 100644 (file)
index 0000000..3855615
Binary files /dev/null and b/test/reference/a8-mask.ref.png differ
diff --git a/test/reference/a8-mask.traps.argb32.ref.png b/test/reference/a8-mask.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..e0503d9
Binary files /dev/null and b/test/reference/a8-mask.traps.argb32.ref.png differ
diff --git a/test/reference/a8-mask.traps.rgb24.ref.png b/test/reference/a8-mask.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..e0503d9
Binary files /dev/null and b/test/reference/a8-mask.traps.rgb24.ref.png differ
diff --git a/test/reference/aliasing.base.argb32.ref.png b/test/reference/aliasing.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..a88dbe2
Binary files /dev/null and b/test/reference/aliasing.base.argb32.ref.png differ
diff --git a/test/reference/aliasing.base.rgb24.ref.png b/test/reference/aliasing.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..a88dbe2
Binary files /dev/null and b/test/reference/aliasing.base.rgb24.ref.png differ
diff --git a/test/reference/aliasing.image16.ref.png b/test/reference/aliasing.image16.ref.png
new file mode 100644 (file)
index 0000000..c763333
Binary files /dev/null and b/test/reference/aliasing.image16.ref.png differ
diff --git a/test/reference/aliasing.quartz.ref.png b/test/reference/aliasing.quartz.ref.png
new file mode 100644 (file)
index 0000000..f4b6e22
Binary files /dev/null and b/test/reference/aliasing.quartz.ref.png differ
diff --git a/test/reference/aliasing.ref.png b/test/reference/aliasing.ref.png
new file mode 100644 (file)
index 0000000..16f563e
Binary files /dev/null and b/test/reference/aliasing.ref.png differ
diff --git a/test/reference/aliasing.traps.argb32.ref.png b/test/reference/aliasing.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..a88dbe2
Binary files /dev/null and b/test/reference/aliasing.traps.argb32.ref.png differ
diff --git a/test/reference/aliasing.traps.rgb24.ref.png b/test/reference/aliasing.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..a88dbe2
Binary files /dev/null and b/test/reference/aliasing.traps.rgb24.ref.png differ
diff --git a/test/reference/alpha-similar.base.argb32.ref.png b/test/reference/alpha-similar.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..9e1bfaa
Binary files /dev/null and b/test/reference/alpha-similar.base.argb32.ref.png differ
diff --git a/test/reference/alpha-similar.base.rgb24.ref.png b/test/reference/alpha-similar.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..a1f5280
Binary files /dev/null and b/test/reference/alpha-similar.base.rgb24.ref.png differ
diff --git a/test/reference/alpha-similar.gl.argb32.xfail.png b/test/reference/alpha-similar.gl.argb32.xfail.png
new file mode 100644 (file)
index 0000000..579aae1
Binary files /dev/null and b/test/reference/alpha-similar.gl.argb32.xfail.png differ
diff --git a/test/reference/alpha-similar.gl.rgb24.xfail.png b/test/reference/alpha-similar.gl.rgb24.xfail.png
new file mode 100644 (file)
index 0000000..86366d2
Binary files /dev/null and b/test/reference/alpha-similar.gl.rgb24.xfail.png differ
diff --git a/test/reference/alpha-similar.pdf.argb32.xfail.png b/test/reference/alpha-similar.pdf.argb32.xfail.png
new file mode 100644 (file)
index 0000000..75aa600
Binary files /dev/null and b/test/reference/alpha-similar.pdf.argb32.xfail.png differ
diff --git a/test/reference/alpha-similar.pdf.rgb24.xfail.png b/test/reference/alpha-similar.pdf.rgb24.xfail.png
new file mode 100644 (file)
index 0000000..86366d2
Binary files /dev/null and b/test/reference/alpha-similar.pdf.rgb24.xfail.png differ
diff --git a/test/reference/alpha-similar.ps.argb32.xfail.png b/test/reference/alpha-similar.ps.argb32.xfail.png
new file mode 100644 (file)
index 0000000..75aa600
Binary files /dev/null and b/test/reference/alpha-similar.ps.argb32.xfail.png differ
diff --git a/test/reference/alpha-similar.ps.rgb24.xfail.png b/test/reference/alpha-similar.ps.rgb24.xfail.png
new file mode 100644 (file)
index 0000000..15a6aa1
Binary files /dev/null and b/test/reference/alpha-similar.ps.rgb24.xfail.png differ
diff --git a/test/reference/alpha-similar.ref.png b/test/reference/alpha-similar.ref.png
new file mode 100644 (file)
index 0000000..9e1bfaa
Binary files /dev/null and b/test/reference/alpha-similar.ref.png differ
diff --git a/test/reference/alpha-similar.rgb24.ref.png b/test/reference/alpha-similar.rgb24.ref.png
new file mode 100644 (file)
index 0000000..4085d60
Binary files /dev/null and b/test/reference/alpha-similar.rgb24.ref.png differ
diff --git a/test/reference/alpha-similar.svg.argb32.xfail.png b/test/reference/alpha-similar.svg.argb32.xfail.png
new file mode 100644 (file)
index 0000000..2ade632
Binary files /dev/null and b/test/reference/alpha-similar.svg.argb32.xfail.png differ
diff --git a/test/reference/alpha-similar.svg.rgb24.xfail.png b/test/reference/alpha-similar.svg.rgb24.xfail.png
new file mode 100644 (file)
index 0000000..c236898
Binary files /dev/null and b/test/reference/alpha-similar.svg.rgb24.xfail.png differ
diff --git a/test/reference/alpha-similar.traps.argb32.ref.png b/test/reference/alpha-similar.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..9e1bfaa
Binary files /dev/null and b/test/reference/alpha-similar.traps.argb32.ref.png differ
diff --git a/test/reference/alpha-similar.traps.rgb24.ref.png b/test/reference/alpha-similar.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..a1f5280
Binary files /dev/null and b/test/reference/alpha-similar.traps.rgb24.ref.png differ
diff --git a/test/reference/api-special-cases.base.argb32.ref.png b/test/reference/api-special-cases.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..56b88a9
Binary files /dev/null and b/test/reference/api-special-cases.base.argb32.ref.png differ
diff --git a/test/reference/api-special-cases.base.rgb24.ref.png b/test/reference/api-special-cases.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..56b88a9
Binary files /dev/null and b/test/reference/api-special-cases.base.rgb24.ref.png differ
diff --git a/test/reference/api-special-cases.ref.png b/test/reference/api-special-cases.ref.png
new file mode 100644 (file)
index 0000000..56b88a9
Binary files /dev/null and b/test/reference/api-special-cases.ref.png differ
diff --git a/test/reference/api-special-cases.traps.argb32.ref.png b/test/reference/api-special-cases.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..56b88a9
Binary files /dev/null and b/test/reference/api-special-cases.traps.argb32.ref.png differ
diff --git a/test/reference/api-special-cases.traps.rgb24.ref.png b/test/reference/api-special-cases.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..56b88a9
Binary files /dev/null and b/test/reference/api-special-cases.traps.rgb24.ref.png differ
diff --git a/test/reference/arc-infinite-loop.base.argb32.ref.png b/test/reference/arc-infinite-loop.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..82d645f
Binary files /dev/null and b/test/reference/arc-infinite-loop.base.argb32.ref.png differ
diff --git a/test/reference/arc-infinite-loop.base.rgb24.ref.png b/test/reference/arc-infinite-loop.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..82d645f
Binary files /dev/null and b/test/reference/arc-infinite-loop.base.rgb24.ref.png differ
diff --git a/test/reference/arc-infinite-loop.ref.png b/test/reference/arc-infinite-loop.ref.png
new file mode 100644 (file)
index 0000000..82d645f
Binary files /dev/null and b/test/reference/arc-infinite-loop.ref.png differ
diff --git a/test/reference/arc-infinite-loop.traps.argb32.ref.png b/test/reference/arc-infinite-loop.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..82d645f
Binary files /dev/null and b/test/reference/arc-infinite-loop.traps.argb32.ref.png differ
diff --git a/test/reference/arc-infinite-loop.traps.rgb24.ref.png b/test/reference/arc-infinite-loop.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..82d645f
Binary files /dev/null and b/test/reference/arc-infinite-loop.traps.rgb24.ref.png differ
diff --git a/test/reference/arc-looping-dash.base.argb32.ref.png b/test/reference/arc-looping-dash.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..9b717b8
Binary files /dev/null and b/test/reference/arc-looping-dash.base.argb32.ref.png differ
diff --git a/test/reference/arc-looping-dash.base.rgb24.ref.png b/test/reference/arc-looping-dash.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..9b717b8
Binary files /dev/null and b/test/reference/arc-looping-dash.base.rgb24.ref.png differ
diff --git a/test/reference/arc-looping-dash.image16.ref.png b/test/reference/arc-looping-dash.image16.ref.png
new file mode 100644 (file)
index 0000000..addc93c
Binary files /dev/null and b/test/reference/arc-looping-dash.image16.ref.png differ
diff --git a/test/reference/arc-looping-dash.ps.ref.png b/test/reference/arc-looping-dash.ps.ref.png
new file mode 100644 (file)
index 0000000..ab19b19
Binary files /dev/null and b/test/reference/arc-looping-dash.ps.ref.png differ
diff --git a/test/reference/arc-looping-dash.quartz.ref.png b/test/reference/arc-looping-dash.quartz.ref.png
new file mode 100644 (file)
index 0000000..70304ca
Binary files /dev/null and b/test/reference/arc-looping-dash.quartz.ref.png differ
diff --git a/test/reference/arc-looping-dash.ref.png b/test/reference/arc-looping-dash.ref.png
new file mode 100644 (file)
index 0000000..27acfc3
Binary files /dev/null and b/test/reference/arc-looping-dash.ref.png differ
diff --git a/test/reference/arc-looping-dash.traps.argb32.ref.png b/test/reference/arc-looping-dash.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..9b717b8
Binary files /dev/null and b/test/reference/arc-looping-dash.traps.argb32.ref.png differ
diff --git a/test/reference/arc-looping-dash.traps.rgb24.ref.png b/test/reference/arc-looping-dash.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..9b717b8
Binary files /dev/null and b/test/reference/arc-looping-dash.traps.rgb24.ref.png differ
diff --git a/test/reference/big-empty-box.argb32.ref.png b/test/reference/big-empty-box.argb32.ref.png
new file mode 100644 (file)
index 0000000..a88d3b6
Binary files /dev/null and b/test/reference/big-empty-box.argb32.ref.png differ
diff --git a/test/reference/big-empty-box.base.argb32.ref.png b/test/reference/big-empty-box.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..a88d3b6
Binary files /dev/null and b/test/reference/big-empty-box.base.argb32.ref.png differ
diff --git a/test/reference/big-empty-box.base.rgb24.ref.png b/test/reference/big-empty-box.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..6c2ca32
Binary files /dev/null and b/test/reference/big-empty-box.base.rgb24.ref.png differ
diff --git a/test/reference/big-empty-box.rgb24.ref.png b/test/reference/big-empty-box.rgb24.ref.png
new file mode 100644 (file)
index 0000000..6c2ca32
Binary files /dev/null and b/test/reference/big-empty-box.rgb24.ref.png differ
diff --git a/test/reference/big-empty-box.traps.argb32.ref.png b/test/reference/big-empty-box.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..a88d3b6
Binary files /dev/null and b/test/reference/big-empty-box.traps.argb32.ref.png differ
diff --git a/test/reference/big-empty-box.traps.rgb24.ref.png b/test/reference/big-empty-box.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..6c2ca32
Binary files /dev/null and b/test/reference/big-empty-box.traps.rgb24.ref.png differ
diff --git a/test/reference/big-empty-triangle.argb32.ref.png b/test/reference/big-empty-triangle.argb32.ref.png
new file mode 100644 (file)
index 0000000..a88d3b6
Binary files /dev/null and b/test/reference/big-empty-triangle.argb32.ref.png differ
diff --git a/test/reference/big-empty-triangle.base.argb32.ref.png b/test/reference/big-empty-triangle.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..a88d3b6
Binary files /dev/null and b/test/reference/big-empty-triangle.base.argb32.ref.png differ
diff --git a/test/reference/big-empty-triangle.base.rgb24.ref.png b/test/reference/big-empty-triangle.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..6c2ca32
Binary files /dev/null and b/test/reference/big-empty-triangle.base.rgb24.ref.png differ
diff --git a/test/reference/big-empty-triangle.rgb24.ref.png b/test/reference/big-empty-triangle.rgb24.ref.png
new file mode 100644 (file)
index 0000000..6c2ca32
Binary files /dev/null and b/test/reference/big-empty-triangle.rgb24.ref.png differ
diff --git a/test/reference/big-empty-triangle.traps.argb32.ref.png b/test/reference/big-empty-triangle.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..a88d3b6
Binary files /dev/null and b/test/reference/big-empty-triangle.traps.argb32.ref.png differ
diff --git a/test/reference/big-empty-triangle.traps.rgb24.ref.png b/test/reference/big-empty-triangle.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..6c2ca32
Binary files /dev/null and b/test/reference/big-empty-triangle.traps.rgb24.ref.png differ
diff --git a/test/reference/big-line.base.argb32.ref.png b/test/reference/big-line.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..35d2e36
Binary files /dev/null and b/test/reference/big-line.base.argb32.ref.png differ
diff --git a/test/reference/big-line.base.rgb24.ref.png b/test/reference/big-line.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..35d2e36
Binary files /dev/null and b/test/reference/big-line.base.rgb24.ref.png differ
diff --git a/test/reference/big-line.image16.ref.png b/test/reference/big-line.image16.ref.png
new file mode 100644 (file)
index 0000000..47e33b8
Binary files /dev/null and b/test/reference/big-line.image16.ref.png differ
diff --git a/test/reference/big-line.ps.ref.png b/test/reference/big-line.ps.ref.png
new file mode 100644 (file)
index 0000000..7b7e875
Binary files /dev/null and b/test/reference/big-line.ps.ref.png differ
diff --git a/test/reference/big-line.quartz.ref.png b/test/reference/big-line.quartz.ref.png
new file mode 100644 (file)
index 0000000..a6f7240
Binary files /dev/null and b/test/reference/big-line.quartz.ref.png differ
diff --git a/test/reference/big-line.ref.png b/test/reference/big-line.ref.png
new file mode 100644 (file)
index 0000000..d86725d
Binary files /dev/null and b/test/reference/big-line.ref.png differ
diff --git a/test/reference/big-line.traps.argb32.ref.png b/test/reference/big-line.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..35d2e36
Binary files /dev/null and b/test/reference/big-line.traps.argb32.ref.png differ
diff --git a/test/reference/big-line.traps.rgb24.ref.png b/test/reference/big-line.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..35d2e36
Binary files /dev/null and b/test/reference/big-line.traps.rgb24.ref.png differ
diff --git a/test/reference/big-little-box.argb32.ref.png b/test/reference/big-little-box.argb32.ref.png
new file mode 100644 (file)
index 0000000..928c5e6
Binary files /dev/null and b/test/reference/big-little-box.argb32.ref.png differ
diff --git a/test/reference/big-little-box.base.argb32.ref.png b/test/reference/big-little-box.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..928c5e6
Binary files /dev/null and b/test/reference/big-little-box.base.argb32.ref.png differ
diff --git a/test/reference/big-little-box.base.rgb24.ref.png b/test/reference/big-little-box.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..c069d6f
Binary files /dev/null and b/test/reference/big-little-box.base.rgb24.ref.png differ
diff --git a/test/reference/big-little-box.rgb24.ref.png b/test/reference/big-little-box.rgb24.ref.png
new file mode 100644 (file)
index 0000000..c069d6f
Binary files /dev/null and b/test/reference/big-little-box.rgb24.ref.png differ
diff --git a/test/reference/big-little-box.traps.argb32.ref.png b/test/reference/big-little-box.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..928c5e6
Binary files /dev/null and b/test/reference/big-little-box.traps.argb32.ref.png differ
diff --git a/test/reference/big-little-box.traps.rgb24.ref.png b/test/reference/big-little-box.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..c069d6f
Binary files /dev/null and b/test/reference/big-little-box.traps.rgb24.ref.png differ
diff --git a/test/reference/big-little-triangle.argb32.ref.png b/test/reference/big-little-triangle.argb32.ref.png
new file mode 100644 (file)
index 0000000..a5c8155
Binary files /dev/null and b/test/reference/big-little-triangle.argb32.ref.png differ
diff --git a/test/reference/big-little-triangle.base.argb32.ref.png b/test/reference/big-little-triangle.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..5308cce
Binary files /dev/null and b/test/reference/big-little-triangle.base.argb32.ref.png differ
diff --git a/test/reference/big-little-triangle.base.rgb24.ref.png b/test/reference/big-little-triangle.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..9e4773b
Binary files /dev/null and b/test/reference/big-little-triangle.base.rgb24.ref.png differ
diff --git a/test/reference/big-little-triangle.rgb24.ref.png b/test/reference/big-little-triangle.rgb24.ref.png
new file mode 100644 (file)
index 0000000..30b1178
Binary files /dev/null and b/test/reference/big-little-triangle.rgb24.ref.png differ
diff --git a/test/reference/big-little-triangle.traps.argb32.ref.png b/test/reference/big-little-triangle.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..5308cce
Binary files /dev/null and b/test/reference/big-little-triangle.traps.argb32.ref.png differ
diff --git a/test/reference/big-little-triangle.traps.rgb24.ref.png b/test/reference/big-little-triangle.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..9e4773b
Binary files /dev/null and b/test/reference/big-little-triangle.traps.rgb24.ref.png differ
diff --git a/test/reference/big-trap.base.argb32.ref.png b/test/reference/big-trap.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..c0975c9
Binary files /dev/null and b/test/reference/big-trap.base.argb32.ref.png differ
diff --git a/test/reference/big-trap.base.rgb24.ref.png b/test/reference/big-trap.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..c0975c9
Binary files /dev/null and b/test/reference/big-trap.base.rgb24.ref.png differ
diff --git a/test/reference/big-trap.traps.argb32.ref.png b/test/reference/big-trap.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..c0975c9
Binary files /dev/null and b/test/reference/big-trap.traps.argb32.ref.png differ
diff --git a/test/reference/big-trap.traps.rgb24.ref.png b/test/reference/big-trap.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..c0975c9
Binary files /dev/null and b/test/reference/big-trap.traps.rgb24.ref.png differ
diff --git a/test/reference/bilevel-image.base.argb32.ref.png b/test/reference/bilevel-image.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..3fb25c2
Binary files /dev/null and b/test/reference/bilevel-image.base.argb32.ref.png differ
diff --git a/test/reference/bilevel-image.base.rgb24.ref.png b/test/reference/bilevel-image.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..3fb25c2
Binary files /dev/null and b/test/reference/bilevel-image.base.rgb24.ref.png differ
diff --git a/test/reference/bilevel-image.ref.png b/test/reference/bilevel-image.ref.png
new file mode 100644 (file)
index 0000000..cae76d6
Binary files /dev/null and b/test/reference/bilevel-image.ref.png differ
diff --git a/test/reference/bilevel-image.traps.argb32.ref.png b/test/reference/bilevel-image.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..3fb25c2
Binary files /dev/null and b/test/reference/bilevel-image.traps.argb32.ref.png differ
diff --git a/test/reference/bilevel-image.traps.rgb24.ref.png b/test/reference/bilevel-image.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..3fb25c2
Binary files /dev/null and b/test/reference/bilevel-image.traps.rgb24.ref.png differ
diff --git a/test/reference/bitmap-font.base.argb32.ref.png b/test/reference/bitmap-font.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..bc2bc52
Binary files /dev/null and b/test/reference/bitmap-font.base.argb32.ref.png differ
diff --git a/test/reference/bitmap-font.base.rgb24.ref.png b/test/reference/bitmap-font.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..285d742
Binary files /dev/null and b/test/reference/bitmap-font.base.rgb24.ref.png differ
diff --git a/test/reference/bitmap-font.ref.png b/test/reference/bitmap-font.ref.png
new file mode 100644 (file)
index 0000000..0718bf9
Binary files /dev/null and b/test/reference/bitmap-font.ref.png differ
diff --git a/test/reference/bitmap-font.rgb24.ref.png b/test/reference/bitmap-font.rgb24.ref.png
new file mode 100644 (file)
index 0000000..8569064
Binary files /dev/null and b/test/reference/bitmap-font.rgb24.ref.png differ
diff --git a/test/reference/bitmap-font.traps.argb32.ref.png b/test/reference/bitmap-font.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..bc2bc52
Binary files /dev/null and b/test/reference/bitmap-font.traps.argb32.ref.png differ
diff --git a/test/reference/bitmap-font.traps.rgb24.ref.png b/test/reference/bitmap-font.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..285d742
Binary files /dev/null and b/test/reference/bitmap-font.traps.rgb24.ref.png differ
diff --git a/test/reference/bug-40410.base.argb32.ref.png b/test/reference/bug-40410.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..a315935
Binary files /dev/null and b/test/reference/bug-40410.base.argb32.ref.png differ
diff --git a/test/reference/bug-40410.base.rgb24.ref.png b/test/reference/bug-40410.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..a315935
Binary files /dev/null and b/test/reference/bug-40410.base.rgb24.ref.png differ
diff --git a/test/reference/bug-40410.ref.png b/test/reference/bug-40410.ref.png
new file mode 100644 (file)
index 0000000..a315935
Binary files /dev/null and b/test/reference/bug-40410.ref.png differ
diff --git a/test/reference/bug-40410.traps.argb32.ref.png b/test/reference/bug-40410.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..a315935
Binary files /dev/null and b/test/reference/bug-40410.traps.argb32.ref.png differ
diff --git a/test/reference/bug-40410.traps.rgb24.ref.png b/test/reference/bug-40410.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..a315935
Binary files /dev/null and b/test/reference/bug-40410.traps.rgb24.ref.png differ
diff --git a/test/reference/bug-bo-rectangular.base.argb32.ref.png b/test/reference/bug-bo-rectangular.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..ffa4ede
Binary files /dev/null and b/test/reference/bug-bo-rectangular.base.argb32.ref.png differ
diff --git a/test/reference/bug-bo-rectangular.base.rgb24.ref.png b/test/reference/bug-bo-rectangular.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..ffa4ede
Binary files /dev/null and b/test/reference/bug-bo-rectangular.base.rgb24.ref.png differ
diff --git a/test/reference/bug-bo-rectangular.image16.ref.png b/test/reference/bug-bo-rectangular.image16.ref.png
new file mode 100644 (file)
index 0000000..d468d59
Binary files /dev/null and b/test/reference/bug-bo-rectangular.image16.ref.png differ
diff --git a/test/reference/bug-bo-rectangular.ps.xfail.png b/test/reference/bug-bo-rectangular.ps.xfail.png
new file mode 100644 (file)
index 0000000..44b8c03
Binary files /dev/null and b/test/reference/bug-bo-rectangular.ps.xfail.png differ
diff --git a/test/reference/bug-bo-rectangular.ref.png b/test/reference/bug-bo-rectangular.ref.png
new file mode 100644 (file)
index 0000000..ffa4ede
Binary files /dev/null and b/test/reference/bug-bo-rectangular.ref.png differ
diff --git a/test/reference/bug-bo-rectangular.traps.argb32.ref.png b/test/reference/bug-bo-rectangular.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..ffa4ede
Binary files /dev/null and b/test/reference/bug-bo-rectangular.traps.argb32.ref.png differ
diff --git a/test/reference/bug-bo-rectangular.traps.rgb24.ref.png b/test/reference/bug-bo-rectangular.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..ffa4ede
Binary files /dev/null and b/test/reference/bug-bo-rectangular.traps.rgb24.ref.png differ
diff --git a/test/reference/bug-bo-ricotz.base.ref.png b/test/reference/bug-bo-ricotz.base.ref.png
new file mode 100644 (file)
index 0000000..ff7a552
Binary files /dev/null and b/test/reference/bug-bo-ricotz.base.ref.png differ
diff --git a/test/reference/bug-bo-ricotz.ref.png b/test/reference/bug-bo-ricotz.ref.png
new file mode 100644 (file)
index 0000000..51c7ccb
Binary files /dev/null and b/test/reference/bug-bo-ricotz.ref.png differ
diff --git a/test/reference/bug-bo-ricotz.traps.ref.png b/test/reference/bug-bo-ricotz.traps.ref.png
new file mode 100644 (file)
index 0000000..ff7a552
Binary files /dev/null and b/test/reference/bug-bo-ricotz.traps.ref.png differ
diff --git a/test/reference/bug-extents.base.argb32.ref.png b/test/reference/bug-extents.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..e07f8aa
Binary files /dev/null and b/test/reference/bug-extents.base.argb32.ref.png differ
diff --git a/test/reference/bug-extents.base.rgb24.ref.png b/test/reference/bug-extents.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..e07f8aa
Binary files /dev/null and b/test/reference/bug-extents.base.rgb24.ref.png differ
diff --git a/test/reference/bug-extents.image16.ref.png b/test/reference/bug-extents.image16.ref.png
new file mode 100644 (file)
index 0000000..8eb3d4b
Binary files /dev/null and b/test/reference/bug-extents.image16.ref.png differ
diff --git a/test/reference/bug-extents.ps.ref.png b/test/reference/bug-extents.ps.ref.png
new file mode 100644 (file)
index 0000000..2d55405
Binary files /dev/null and b/test/reference/bug-extents.ps.ref.png differ
diff --git a/test/reference/bug-extents.quartz.ref.png b/test/reference/bug-extents.quartz.ref.png
new file mode 100644 (file)
index 0000000..2973448
Binary files /dev/null and b/test/reference/bug-extents.quartz.ref.png differ
diff --git a/test/reference/bug-extents.ref.png b/test/reference/bug-extents.ref.png
new file mode 100644 (file)
index 0000000..47d6d36
Binary files /dev/null and b/test/reference/bug-extents.ref.png differ
diff --git a/test/reference/bug-extents.traps.argb32.ref.png b/test/reference/bug-extents.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..e07f8aa
Binary files /dev/null and b/test/reference/bug-extents.traps.argb32.ref.png differ
diff --git a/test/reference/bug-extents.traps.rgb24.ref.png b/test/reference/bug-extents.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..e07f8aa
Binary files /dev/null and b/test/reference/bug-extents.traps.rgb24.ref.png differ
diff --git a/test/reference/bug-seams.base.argb32.ref.png b/test/reference/bug-seams.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..ac80651
Binary files /dev/null and b/test/reference/bug-seams.base.argb32.ref.png differ
diff --git a/test/reference/bug-seams.base.rgb24.ref.png b/test/reference/bug-seams.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..ac80651
Binary files /dev/null and b/test/reference/bug-seams.base.rgb24.ref.png differ
diff --git a/test/reference/bug-seams.ref.png b/test/reference/bug-seams.ref.png
new file mode 100644 (file)
index 0000000..0665e06
Binary files /dev/null and b/test/reference/bug-seams.ref.png differ
diff --git a/test/reference/bug-seams.traps.argb32.ref.png b/test/reference/bug-seams.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..ac80651
Binary files /dev/null and b/test/reference/bug-seams.traps.argb32.ref.png differ
diff --git a/test/reference/bug-seams.traps.rgb24.ref.png b/test/reference/bug-seams.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..ac80651
Binary files /dev/null and b/test/reference/bug-seams.traps.rgb24.ref.png differ
diff --git a/test/reference/bug-seams.xlib-fallback.ref.png b/test/reference/bug-seams.xlib-fallback.ref.png
new file mode 100644 (file)
index 0000000..e81fc6c
Binary files /dev/null and b/test/reference/bug-seams.xlib-fallback.ref.png differ
diff --git a/test/reference/caps-joins-alpha.base.argb32.ref.png b/test/reference/caps-joins-alpha.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..288a500
Binary files /dev/null and b/test/reference/caps-joins-alpha.base.argb32.ref.png differ
diff --git a/test/reference/caps-joins-alpha.base.rgb24.ref.png b/test/reference/caps-joins-alpha.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..288a500
Binary files /dev/null and b/test/reference/caps-joins-alpha.base.rgb24.ref.png differ
diff --git a/test/reference/caps-joins-alpha.image16.ref.png b/test/reference/caps-joins-alpha.image16.ref.png
new file mode 100644 (file)
index 0000000..ddefea8
Binary files /dev/null and b/test/reference/caps-joins-alpha.image16.ref.png differ
diff --git a/test/reference/caps-joins-alpha.quartz.ref.png b/test/reference/caps-joins-alpha.quartz.ref.png
new file mode 100644 (file)
index 0000000..190c1e9
Binary files /dev/null and b/test/reference/caps-joins-alpha.quartz.ref.png differ
diff --git a/test/reference/caps-joins-alpha.ref.png b/test/reference/caps-joins-alpha.ref.png
new file mode 100644 (file)
index 0000000..338c809
Binary files /dev/null and b/test/reference/caps-joins-alpha.ref.png differ
diff --git a/test/reference/caps-joins-alpha.traps.argb32.ref.png b/test/reference/caps-joins-alpha.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..288a500
Binary files /dev/null and b/test/reference/caps-joins-alpha.traps.argb32.ref.png differ
diff --git a/test/reference/caps-joins-alpha.traps.rgb24.ref.png b/test/reference/caps-joins-alpha.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..288a500
Binary files /dev/null and b/test/reference/caps-joins-alpha.traps.rgb24.ref.png differ
diff --git a/test/reference/caps-joins-curve.base.argb32.ref.png b/test/reference/caps-joins-curve.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..13f877c
Binary files /dev/null and b/test/reference/caps-joins-curve.base.argb32.ref.png differ
diff --git a/test/reference/caps-joins-curve.base.rgb24.ref.png b/test/reference/caps-joins-curve.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..13f877c
Binary files /dev/null and b/test/reference/caps-joins-curve.base.rgb24.ref.png differ
diff --git a/test/reference/caps-joins-curve.image16.ref.png b/test/reference/caps-joins-curve.image16.ref.png
new file mode 100644 (file)
index 0000000..060b3dc
Binary files /dev/null and b/test/reference/caps-joins-curve.image16.ref.png differ
diff --git a/test/reference/caps-joins-curve.ps.ref.png b/test/reference/caps-joins-curve.ps.ref.png
new file mode 100644 (file)
index 0000000..7fbb826
Binary files /dev/null and b/test/reference/caps-joins-curve.ps.ref.png differ
diff --git a/test/reference/caps-joins-curve.quartz.ref.png b/test/reference/caps-joins-curve.quartz.ref.png
new file mode 100644 (file)
index 0000000..ec3d307
Binary files /dev/null and b/test/reference/caps-joins-curve.quartz.ref.png differ
diff --git a/test/reference/caps-joins-curve.ref.png b/test/reference/caps-joins-curve.ref.png
new file mode 100644 (file)
index 0000000..f4398d7
Binary files /dev/null and b/test/reference/caps-joins-curve.ref.png differ
diff --git a/test/reference/caps-joins-curve.traps.argb32.ref.png b/test/reference/caps-joins-curve.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..13f877c
Binary files /dev/null and b/test/reference/caps-joins-curve.traps.argb32.ref.png differ
diff --git a/test/reference/caps-joins-curve.traps.rgb24.ref.png b/test/reference/caps-joins-curve.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..13f877c
Binary files /dev/null and b/test/reference/caps-joins-curve.traps.rgb24.ref.png differ
diff --git a/test/reference/caps-joins.base.argb32.ref.png b/test/reference/caps-joins.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..831cba4
Binary files /dev/null and b/test/reference/caps-joins.base.argb32.ref.png differ
diff --git a/test/reference/caps-joins.base.rgb24.ref.png b/test/reference/caps-joins.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..831cba4
Binary files /dev/null and b/test/reference/caps-joins.base.rgb24.ref.png differ
diff --git a/test/reference/caps-joins.image16.ref.png b/test/reference/caps-joins.image16.ref.png
new file mode 100644 (file)
index 0000000..0c452f2
Binary files /dev/null and b/test/reference/caps-joins.image16.ref.png differ
diff --git a/test/reference/caps-joins.ps.ref.png b/test/reference/caps-joins.ps.ref.png
new file mode 100644 (file)
index 0000000..f6c85ce
Binary files /dev/null and b/test/reference/caps-joins.ps.ref.png differ
diff --git a/test/reference/caps-joins.ref.png b/test/reference/caps-joins.ref.png
new file mode 100644 (file)
index 0000000..b9b5ad4
Binary files /dev/null and b/test/reference/caps-joins.ref.png differ
diff --git a/test/reference/caps-joins.traps.argb32.ref.png b/test/reference/caps-joins.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..831cba4
Binary files /dev/null and b/test/reference/caps-joins.traps.argb32.ref.png differ
diff --git a/test/reference/caps-joins.traps.rgb24.ref.png b/test/reference/caps-joins.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..831cba4
Binary files /dev/null and b/test/reference/caps-joins.traps.rgb24.ref.png differ
diff --git a/test/reference/caps-sub-paths.base.argb32.ref.png b/test/reference/caps-sub-paths.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..1ffb2d9
Binary files /dev/null and b/test/reference/caps-sub-paths.base.argb32.ref.png differ
diff --git a/test/reference/caps-sub-paths.base.rgb24.ref.png b/test/reference/caps-sub-paths.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..1ffb2d9
Binary files /dev/null and b/test/reference/caps-sub-paths.base.rgb24.ref.png differ
diff --git a/test/reference/caps-sub-paths.image16.ref.png b/test/reference/caps-sub-paths.image16.ref.png
new file mode 100644 (file)
index 0000000..c0cc4d7
Binary files /dev/null and b/test/reference/caps-sub-paths.image16.ref.png differ
diff --git a/test/reference/caps-sub-paths.ps.ref.png b/test/reference/caps-sub-paths.ps.ref.png
new file mode 100644 (file)
index 0000000..197b443
Binary files /dev/null and b/test/reference/caps-sub-paths.ps.ref.png differ
diff --git a/test/reference/caps-sub-paths.ref.png b/test/reference/caps-sub-paths.ref.png
new file mode 100644 (file)
index 0000000..1829fc5
Binary files /dev/null and b/test/reference/caps-sub-paths.ref.png differ
diff --git a/test/reference/caps-sub-paths.traps.argb32.ref.png b/test/reference/caps-sub-paths.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..1ffb2d9
Binary files /dev/null and b/test/reference/caps-sub-paths.traps.argb32.ref.png differ
diff --git a/test/reference/caps-sub-paths.traps.rgb24.ref.png b/test/reference/caps-sub-paths.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..1ffb2d9
Binary files /dev/null and b/test/reference/caps-sub-paths.traps.rgb24.ref.png differ
diff --git a/test/reference/caps-tails-curve.base.argb32.ref.png b/test/reference/caps-tails-curve.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..ccb8d65
Binary files /dev/null and b/test/reference/caps-tails-curve.base.argb32.ref.png differ
diff --git a/test/reference/caps-tails-curve.base.rgb24.ref.png b/test/reference/caps-tails-curve.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..ccb8d65
Binary files /dev/null and b/test/reference/caps-tails-curve.base.rgb24.ref.png differ
diff --git a/test/reference/caps-tails-curve.ps.ref.png b/test/reference/caps-tails-curve.ps.ref.png
new file mode 100644 (file)
index 0000000..fca77c3
Binary files /dev/null and b/test/reference/caps-tails-curve.ps.ref.png differ
diff --git a/test/reference/caps-tails-curve.ref.png b/test/reference/caps-tails-curve.ref.png
new file mode 100644 (file)
index 0000000..6ed8905
Binary files /dev/null and b/test/reference/caps-tails-curve.ref.png differ
diff --git a/test/reference/caps-tails-curve.traps.argb32.ref.png b/test/reference/caps-tails-curve.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..ccb8d65
Binary files /dev/null and b/test/reference/caps-tails-curve.traps.argb32.ref.png differ
diff --git a/test/reference/caps-tails-curve.traps.rgb24.ref.png b/test/reference/caps-tails-curve.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..ccb8d65
Binary files /dev/null and b/test/reference/caps-tails-curve.traps.rgb24.ref.png differ
diff --git a/test/reference/caps.base.argb32.ref.png b/test/reference/caps.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..a46e036
Binary files /dev/null and b/test/reference/caps.base.argb32.ref.png differ
diff --git a/test/reference/caps.base.rgb24.ref.png b/test/reference/caps.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..a46e036
Binary files /dev/null and b/test/reference/caps.base.rgb24.ref.png differ
diff --git a/test/reference/caps.image16.ref.png b/test/reference/caps.image16.ref.png
new file mode 100644 (file)
index 0000000..a33a5ef
Binary files /dev/null and b/test/reference/caps.image16.ref.png differ
diff --git a/test/reference/caps.ps.ref.png b/test/reference/caps.ps.ref.png
new file mode 100644 (file)
index 0000000..c91b8aa
Binary files /dev/null and b/test/reference/caps.ps.ref.png differ
diff --git a/test/reference/caps.ref.png b/test/reference/caps.ref.png
new file mode 100644 (file)
index 0000000..37ed2bb
Binary files /dev/null and b/test/reference/caps.ref.png differ
diff --git a/test/reference/caps.traps.argb32.ref.png b/test/reference/caps.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..a46e036
Binary files /dev/null and b/test/reference/caps.traps.argb32.ref.png differ
diff --git a/test/reference/caps.traps.rgb24.ref.png b/test/reference/caps.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..a46e036
Binary files /dev/null and b/test/reference/caps.traps.rgb24.ref.png differ
diff --git a/test/reference/checkerboard.base.argb32.ref.png b/test/reference/checkerboard.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..1444bc4
Binary files /dev/null and b/test/reference/checkerboard.base.argb32.ref.png differ
diff --git a/test/reference/checkerboard.base.rgb24.ref.png b/test/reference/checkerboard.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..1444bc4
Binary files /dev/null and b/test/reference/checkerboard.base.rgb24.ref.png differ
diff --git a/test/reference/checkerboard.ref.png b/test/reference/checkerboard.ref.png
new file mode 100644 (file)
index 0000000..1444bc4
Binary files /dev/null and b/test/reference/checkerboard.ref.png differ
diff --git a/test/reference/checkerboard.traps.argb32.ref.png b/test/reference/checkerboard.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..1444bc4
Binary files /dev/null and b/test/reference/checkerboard.traps.argb32.ref.png differ
diff --git a/test/reference/checkerboard.traps.rgb24.ref.png b/test/reference/checkerboard.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..1444bc4
Binary files /dev/null and b/test/reference/checkerboard.traps.rgb24.ref.png differ
diff --git a/test/reference/clear-source.base.argb32.ref.png b/test/reference/clear-source.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..b7e8025
Binary files /dev/null and b/test/reference/clear-source.base.argb32.ref.png differ
diff --git a/test/reference/clear-source.base.rgb24.ref.png b/test/reference/clear-source.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..b7e8025
Binary files /dev/null and b/test/reference/clear-source.base.rgb24.ref.png differ
diff --git a/test/reference/clear-source.image16.ref.png b/test/reference/clear-source.image16.ref.png
new file mode 100644 (file)
index 0000000..4055b95
Binary files /dev/null and b/test/reference/clear-source.image16.ref.png differ
diff --git a/test/reference/clear-source.pdf.xfail.png b/test/reference/clear-source.pdf.xfail.png
new file mode 100644 (file)
index 0000000..8e1bdd7
Binary files /dev/null and b/test/reference/clear-source.pdf.xfail.png differ
diff --git a/test/reference/clear-source.ps.xfail.png b/test/reference/clear-source.ps.xfail.png
new file mode 100644 (file)
index 0000000..b515751
Binary files /dev/null and b/test/reference/clear-source.ps.xfail.png differ
diff --git a/test/reference/clear-source.ref.png b/test/reference/clear-source.ref.png
new file mode 100644 (file)
index 0000000..b7e8025
Binary files /dev/null and b/test/reference/clear-source.ref.png differ
diff --git a/test/reference/clear-source.traps.argb32.ref.png b/test/reference/clear-source.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..b7e8025
Binary files /dev/null and b/test/reference/clear-source.traps.argb32.ref.png differ
diff --git a/test/reference/clear-source.traps.rgb24.ref.png b/test/reference/clear-source.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..b7e8025
Binary files /dev/null and b/test/reference/clear-source.traps.rgb24.ref.png differ
diff --git a/test/reference/clear.argb32.ref.png b/test/reference/clear.argb32.ref.png
new file mode 100644 (file)
index 0000000..2e756e2
Binary files /dev/null and b/test/reference/clear.argb32.ref.png differ
diff --git a/test/reference/clear.base.argb32.ref.png b/test/reference/clear.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..1caca6d
Binary files /dev/null and b/test/reference/clear.base.argb32.ref.png differ
diff --git a/test/reference/clear.base.rgb24.ref.png b/test/reference/clear.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..7d789b0
Binary files /dev/null and b/test/reference/clear.base.rgb24.ref.png differ
diff --git a/test/reference/clear.pdf.argb32.ref.png b/test/reference/clear.pdf.argb32.ref.png
new file mode 100644 (file)
index 0000000..0960f48
Binary files /dev/null and b/test/reference/clear.pdf.argb32.ref.png differ
diff --git a/test/reference/clear.ps.argb32.ref.png b/test/reference/clear.ps.argb32.ref.png
new file mode 100644 (file)
index 0000000..0960f48
Binary files /dev/null and b/test/reference/clear.ps.argb32.ref.png differ
diff --git a/test/reference/clear.quartz.argb32.ref.png b/test/reference/clear.quartz.argb32.ref.png
new file mode 100644 (file)
index 0000000..12e6043
Binary files /dev/null and b/test/reference/clear.quartz.argb32.ref.png differ
diff --git a/test/reference/clear.quartz.rgb24.ref.png b/test/reference/clear.quartz.rgb24.ref.png
new file mode 100644 (file)
index 0000000..6ea4490
Binary files /dev/null and b/test/reference/clear.quartz.rgb24.ref.png differ
diff --git a/test/reference/clear.rgb24.ref.png b/test/reference/clear.rgb24.ref.png
new file mode 100644 (file)
index 0000000..dc1e509
Binary files /dev/null and b/test/reference/clear.rgb24.ref.png differ
diff --git a/test/reference/clear.svg12.argb32.xfail.png b/test/reference/clear.svg12.argb32.xfail.png
new file mode 100644 (file)
index 0000000..cb25bcb
Binary files /dev/null and b/test/reference/clear.svg12.argb32.xfail.png differ
diff --git a/test/reference/clear.svg12.rgb24.xfail.png b/test/reference/clear.svg12.rgb24.xfail.png
new file mode 100644 (file)
index 0000000..cb25bcb
Binary files /dev/null and b/test/reference/clear.svg12.rgb24.xfail.png differ
diff --git a/test/reference/clear.traps.argb32.ref.png b/test/reference/clear.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..1caca6d
Binary files /dev/null and b/test/reference/clear.traps.argb32.ref.png differ
diff --git a/test/reference/clear.traps.rgb24.ref.png b/test/reference/clear.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..7d789b0
Binary files /dev/null and b/test/reference/clear.traps.rgb24.ref.png differ
diff --git a/test/reference/clip-all.base.argb32.ref.png b/test/reference/clip-all.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..1ed070c
Binary files /dev/null and b/test/reference/clip-all.base.argb32.ref.png differ
diff --git a/test/reference/clip-all.base.rgb24.ref.png b/test/reference/clip-all.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..1ed070c
Binary files /dev/null and b/test/reference/clip-all.base.rgb24.ref.png differ
diff --git a/test/reference/clip-all.ref.png b/test/reference/clip-all.ref.png
new file mode 100644 (file)
index 0000000..6c14df5
Binary files /dev/null and b/test/reference/clip-all.ref.png differ
diff --git a/test/reference/clip-all.traps.argb32.ref.png b/test/reference/clip-all.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..1ed070c
Binary files /dev/null and b/test/reference/clip-all.traps.argb32.ref.png differ
diff --git a/test/reference/clip-all.traps.rgb24.ref.png b/test/reference/clip-all.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..1ed070c
Binary files /dev/null and b/test/reference/clip-all.traps.rgb24.ref.png differ
diff --git a/test/reference/clip-complex-shape-eo-aa.base.argb32.ref.png b/test/reference/clip-complex-shape-eo-aa.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..bafbb8a
Binary files /dev/null and b/test/reference/clip-complex-shape-eo-aa.base.argb32.ref.png differ
diff --git a/test/reference/clip-complex-shape-eo-aa.base.rgb24.ref.png b/test/reference/clip-complex-shape-eo-aa.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..bafbb8a
Binary files /dev/null and b/test/reference/clip-complex-shape-eo-aa.base.rgb24.ref.png differ
diff --git a/test/reference/clip-complex-shape-eo-aa.ref.png b/test/reference/clip-complex-shape-eo-aa.ref.png
new file mode 100644 (file)
index 0000000..d575aa9
Binary files /dev/null and b/test/reference/clip-complex-shape-eo-aa.ref.png differ
diff --git a/test/reference/clip-complex-shape-eo-aa.traps.argb32.ref.png b/test/reference/clip-complex-shape-eo-aa.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..bafbb8a
Binary files /dev/null and b/test/reference/clip-complex-shape-eo-aa.traps.argb32.ref.png differ
diff --git a/test/reference/clip-complex-shape-eo-aa.traps.rgb24.ref.png b/test/reference/clip-complex-shape-eo-aa.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..bafbb8a
Binary files /dev/null and b/test/reference/clip-complex-shape-eo-aa.traps.rgb24.ref.png differ
diff --git a/test/reference/clip-complex-shape-eo-mono.base.argb32.ref.png b/test/reference/clip-complex-shape-eo-mono.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..bafbb8a
Binary files /dev/null and b/test/reference/clip-complex-shape-eo-mono.base.argb32.ref.png differ
diff --git a/test/reference/clip-complex-shape-eo-mono.base.rgb24.ref.png b/test/reference/clip-complex-shape-eo-mono.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..bafbb8a
Binary files /dev/null and b/test/reference/clip-complex-shape-eo-mono.base.rgb24.ref.png differ
diff --git a/test/reference/clip-complex-shape-eo-mono.ref.png b/test/reference/clip-complex-shape-eo-mono.ref.png
new file mode 100644 (file)
index 0000000..d575aa9
Binary files /dev/null and b/test/reference/clip-complex-shape-eo-mono.ref.png differ
diff --git a/test/reference/clip-complex-shape-eo-mono.traps.argb32.ref.png b/test/reference/clip-complex-shape-eo-mono.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..bafbb8a
Binary files /dev/null and b/test/reference/clip-complex-shape-eo-mono.traps.argb32.ref.png differ
diff --git a/test/reference/clip-complex-shape-eo-mono.traps.rgb24.ref.png b/test/reference/clip-complex-shape-eo-mono.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..bafbb8a
Binary files /dev/null and b/test/reference/clip-complex-shape-eo-mono.traps.rgb24.ref.png differ
diff --git a/test/reference/clip-contexts.base.argb32.ref.png b/test/reference/clip-contexts.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..0d575a6
Binary files /dev/null and b/test/reference/clip-contexts.base.argb32.ref.png differ
diff --git a/test/reference/clip-contexts.base.rgb24.ref.png b/test/reference/clip-contexts.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..0d575a6
Binary files /dev/null and b/test/reference/clip-contexts.base.rgb24.ref.png differ
diff --git a/test/reference/clip-contexts.ref.png b/test/reference/clip-contexts.ref.png
new file mode 100644 (file)
index 0000000..0d575a6
Binary files /dev/null and b/test/reference/clip-contexts.ref.png differ
diff --git a/test/reference/clip-contexts.traps.argb32.ref.png b/test/reference/clip-contexts.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..0d575a6
Binary files /dev/null and b/test/reference/clip-contexts.traps.argb32.ref.png differ
diff --git a/test/reference/clip-contexts.traps.rgb24.ref.png b/test/reference/clip-contexts.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..0d575a6
Binary files /dev/null and b/test/reference/clip-contexts.traps.rgb24.ref.png differ
diff --git a/test/reference/clip-device-offset.argb32.ref.png b/test/reference/clip-device-offset.argb32.ref.png
new file mode 100644 (file)
index 0000000..06be985
Binary files /dev/null and b/test/reference/clip-device-offset.argb32.ref.png differ
diff --git a/test/reference/clip-device-offset.base.argb32.ref.png b/test/reference/clip-device-offset.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..06be985
Binary files /dev/null and b/test/reference/clip-device-offset.base.argb32.ref.png differ
diff --git a/test/reference/clip-device-offset.base.rgb24.ref.png b/test/reference/clip-device-offset.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..2419384
Binary files /dev/null and b/test/reference/clip-device-offset.base.rgb24.ref.png differ
diff --git a/test/reference/clip-device-offset.rgb24.ref.png b/test/reference/clip-device-offset.rgb24.ref.png
new file mode 100644 (file)
index 0000000..2419384
Binary files /dev/null and b/test/reference/clip-device-offset.rgb24.ref.png differ
diff --git a/test/reference/clip-device-offset.traps.argb32.ref.png b/test/reference/clip-device-offset.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..06be985
Binary files /dev/null and b/test/reference/clip-device-offset.traps.argb32.ref.png differ
diff --git a/test/reference/clip-device-offset.traps.rgb24.ref.png b/test/reference/clip-device-offset.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..2419384
Binary files /dev/null and b/test/reference/clip-device-offset.traps.rgb24.ref.png differ
diff --git a/test/reference/clip-disjoint-hatching.base.argb32.ref.png b/test/reference/clip-disjoint-hatching.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..dbac90b
Binary files /dev/null and b/test/reference/clip-disjoint-hatching.base.argb32.ref.png differ
diff --git a/test/reference/clip-disjoint-hatching.base.rgb24.ref.png b/test/reference/clip-disjoint-hatching.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..dbac90b
Binary files /dev/null and b/test/reference/clip-disjoint-hatching.base.rgb24.ref.png differ
diff --git a/test/reference/clip-disjoint-hatching.ref.png b/test/reference/clip-disjoint-hatching.ref.png
new file mode 100644 (file)
index 0000000..115875b
Binary files /dev/null and b/test/reference/clip-disjoint-hatching.ref.png differ
diff --git a/test/reference/clip-disjoint-hatching.traps.argb32.ref.png b/test/reference/clip-disjoint-hatching.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..6226d93
Binary files /dev/null and b/test/reference/clip-disjoint-hatching.traps.argb32.ref.png differ
diff --git a/test/reference/clip-disjoint-hatching.traps.rgb24.ref.png b/test/reference/clip-disjoint-hatching.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..6226d93
Binary files /dev/null and b/test/reference/clip-disjoint-hatching.traps.rgb24.ref.png differ
diff --git a/test/reference/clip-disjoint.base.argb32.ref.png b/test/reference/clip-disjoint.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..74ae9d8
Binary files /dev/null and b/test/reference/clip-disjoint.base.argb32.ref.png differ
diff --git a/test/reference/clip-disjoint.base.rgb24.ref.png b/test/reference/clip-disjoint.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..74ae9d8
Binary files /dev/null and b/test/reference/clip-disjoint.base.rgb24.ref.png differ
diff --git a/test/reference/clip-disjoint.image16.ref.png b/test/reference/clip-disjoint.image16.ref.png
new file mode 100644 (file)
index 0000000..79aaf71
Binary files /dev/null and b/test/reference/clip-disjoint.image16.ref.png differ
diff --git a/test/reference/clip-disjoint.ps.ref.png b/test/reference/clip-disjoint.ps.ref.png
new file mode 100644 (file)
index 0000000..5410d0a
Binary files /dev/null and b/test/reference/clip-disjoint.ps.ref.png differ
diff --git a/test/reference/clip-disjoint.quartz.ref.png b/test/reference/clip-disjoint.quartz.ref.png
new file mode 100644 (file)
index 0000000..1006815
Binary files /dev/null and b/test/reference/clip-disjoint.quartz.ref.png differ
diff --git a/test/reference/clip-disjoint.ref.png b/test/reference/clip-disjoint.ref.png
new file mode 100644 (file)
index 0000000..a4c9989
Binary files /dev/null and b/test/reference/clip-disjoint.ref.png differ
diff --git a/test/reference/clip-disjoint.traps.argb32.ref.png b/test/reference/clip-disjoint.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..74ae9d8
Binary files /dev/null and b/test/reference/clip-disjoint.traps.argb32.ref.png differ
diff --git a/test/reference/clip-disjoint.traps.rgb24.ref.png b/test/reference/clip-disjoint.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..74ae9d8
Binary files /dev/null and b/test/reference/clip-disjoint.traps.rgb24.ref.png differ
diff --git a/test/reference/clip-empty-group.base.argb32.ref.png b/test/reference/clip-empty-group.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..a59ca47
Binary files /dev/null and b/test/reference/clip-empty-group.base.argb32.ref.png differ
diff --git a/test/reference/clip-empty-group.base.rgb24.ref.png b/test/reference/clip-empty-group.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..a59ca47
Binary files /dev/null and b/test/reference/clip-empty-group.base.rgb24.ref.png differ
diff --git a/test/reference/clip-empty-group.ref.png b/test/reference/clip-empty-group.ref.png
new file mode 100644 (file)
index 0000000..a59ca47
Binary files /dev/null and b/test/reference/clip-empty-group.ref.png differ
diff --git a/test/reference/clip-empty-group.traps.argb32.ref.png b/test/reference/clip-empty-group.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..a59ca47
Binary files /dev/null and b/test/reference/clip-empty-group.traps.argb32.ref.png differ
diff --git a/test/reference/clip-empty-group.traps.rgb24.ref.png b/test/reference/clip-empty-group.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..a59ca47
Binary files /dev/null and b/test/reference/clip-empty-group.traps.rgb24.ref.png differ
diff --git a/test/reference/clip-empty-save.base.argb32.ref.png b/test/reference/clip-empty-save.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..1ed070c
Binary files /dev/null and b/test/reference/clip-empty-save.base.argb32.ref.png differ
diff --git a/test/reference/clip-empty-save.base.rgb24.ref.png b/test/reference/clip-empty-save.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..1ed070c
Binary files /dev/null and b/test/reference/clip-empty-save.base.rgb24.ref.png differ
diff --git a/test/reference/clip-empty-save.ref.png b/test/reference/clip-empty-save.ref.png
new file mode 100644 (file)
index 0000000..6c14df5
Binary files /dev/null and b/test/reference/clip-empty-save.ref.png differ
diff --git a/test/reference/clip-empty-save.traps.argb32.ref.png b/test/reference/clip-empty-save.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..1ed070c
Binary files /dev/null and b/test/reference/clip-empty-save.traps.argb32.ref.png differ
diff --git a/test/reference/clip-empty-save.traps.rgb24.ref.png b/test/reference/clip-empty-save.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..1ed070c
Binary files /dev/null and b/test/reference/clip-empty-save.traps.rgb24.ref.png differ
diff --git a/test/reference/clip-empty.base.argb32.ref.png b/test/reference/clip-empty.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..1ed070c
Binary files /dev/null and b/test/reference/clip-empty.base.argb32.ref.png differ
diff --git a/test/reference/clip-empty.base.rgb24.ref.png b/test/reference/clip-empty.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..1ed070c
Binary files /dev/null and b/test/reference/clip-empty.base.rgb24.ref.png differ
diff --git a/test/reference/clip-empty.ref.png b/test/reference/clip-empty.ref.png
new file mode 100644 (file)
index 0000000..6c14df5
Binary files /dev/null and b/test/reference/clip-empty.ref.png differ
diff --git a/test/reference/clip-empty.traps.argb32.ref.png b/test/reference/clip-empty.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..1ed070c
Binary files /dev/null and b/test/reference/clip-empty.traps.argb32.ref.png differ
diff --git a/test/reference/clip-empty.traps.rgb24.ref.png b/test/reference/clip-empty.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..1ed070c
Binary files /dev/null and b/test/reference/clip-empty.traps.rgb24.ref.png differ
diff --git a/test/reference/clip-fill-eo-unbounded.argb32.ref.png b/test/reference/clip-fill-eo-unbounded.argb32.ref.png
new file mode 100644 (file)
index 0000000..66af094
Binary files /dev/null and b/test/reference/clip-fill-eo-unbounded.argb32.ref.png differ
diff --git a/test/reference/clip-fill-eo-unbounded.base.argb32.ref.png b/test/reference/clip-fill-eo-unbounded.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..19b9f09
Binary files /dev/null and b/test/reference/clip-fill-eo-unbounded.base.argb32.ref.png differ
diff --git a/test/reference/clip-fill-eo-unbounded.base.rgb24.ref.png b/test/reference/clip-fill-eo-unbounded.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..1ad0b17
Binary files /dev/null and b/test/reference/clip-fill-eo-unbounded.base.rgb24.ref.png differ
diff --git a/test/reference/clip-fill-eo-unbounded.image16.ref.png b/test/reference/clip-fill-eo-unbounded.image16.ref.png
new file mode 100644 (file)
index 0000000..e0e66ff
Binary files /dev/null and b/test/reference/clip-fill-eo-unbounded.image16.ref.png differ
diff --git a/test/reference/clip-fill-eo-unbounded.quartz.argb32.ref.png b/test/reference/clip-fill-eo-unbounded.quartz.argb32.ref.png
new file mode 100644 (file)
index 0000000..3423000
Binary files /dev/null and b/test/reference/clip-fill-eo-unbounded.quartz.argb32.ref.png differ
diff --git a/test/reference/clip-fill-eo-unbounded.quartz.rgb24.ref.png b/test/reference/clip-fill-eo-unbounded.quartz.rgb24.ref.png
new file mode 100644 (file)
index 0000000..1612801
Binary files /dev/null and b/test/reference/clip-fill-eo-unbounded.quartz.rgb24.ref.png differ
diff --git a/test/reference/clip-fill-eo-unbounded.rgb24.ref.png b/test/reference/clip-fill-eo-unbounded.rgb24.ref.png
new file mode 100644 (file)
index 0000000..272e0e8
Binary files /dev/null and b/test/reference/clip-fill-eo-unbounded.rgb24.ref.png differ
diff --git a/test/reference/clip-fill-eo-unbounded.svg12.rgb24.xfail.png b/test/reference/clip-fill-eo-unbounded.svg12.rgb24.xfail.png
new file mode 100644 (file)
index 0000000..f949de1
Binary files /dev/null and b/test/reference/clip-fill-eo-unbounded.svg12.rgb24.xfail.png differ
diff --git a/test/reference/clip-fill-eo-unbounded.traps.argb32.ref.png b/test/reference/clip-fill-eo-unbounded.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..19b9f09
Binary files /dev/null and b/test/reference/clip-fill-eo-unbounded.traps.argb32.ref.png differ
diff --git a/test/reference/clip-fill-eo-unbounded.traps.rgb24.ref.png b/test/reference/clip-fill-eo-unbounded.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..1ad0b17
Binary files /dev/null and b/test/reference/clip-fill-eo-unbounded.traps.rgb24.ref.png differ
diff --git a/test/reference/clip-fill-eo-unbounded.xlib-fallback.rgb24.ref.png b/test/reference/clip-fill-eo-unbounded.xlib-fallback.rgb24.ref.png
new file mode 100644 (file)
index 0000000..ec25896
Binary files /dev/null and b/test/reference/clip-fill-eo-unbounded.xlib-fallback.rgb24.ref.png differ
diff --git a/test/reference/clip-fill-no-op.base.argb32.ref.png b/test/reference/clip-fill-no-op.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..2256461
Binary files /dev/null and b/test/reference/clip-fill-no-op.base.argb32.ref.png differ
diff --git a/test/reference/clip-fill-no-op.base.rgb24.ref.png b/test/reference/clip-fill-no-op.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..2256461
Binary files /dev/null and b/test/reference/clip-fill-no-op.base.rgb24.ref.png differ
diff --git a/test/reference/clip-fill-no-op.image16.ref.png b/test/reference/clip-fill-no-op.image16.ref.png
new file mode 100644 (file)
index 0000000..cf0c74a
Binary files /dev/null and b/test/reference/clip-fill-no-op.image16.ref.png differ
diff --git a/test/reference/clip-fill-no-op.ref.png b/test/reference/clip-fill-no-op.ref.png
new file mode 100644 (file)
index 0000000..2256461
Binary files /dev/null and b/test/reference/clip-fill-no-op.ref.png differ
diff --git a/test/reference/clip-fill-no-op.traps.argb32.ref.png b/test/reference/clip-fill-no-op.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..2256461
Binary files /dev/null and b/test/reference/clip-fill-no-op.traps.argb32.ref.png differ
diff --git a/test/reference/clip-fill-no-op.traps.rgb24.ref.png b/test/reference/clip-fill-no-op.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..2256461
Binary files /dev/null and b/test/reference/clip-fill-no-op.traps.rgb24.ref.png differ
diff --git a/test/reference/clip-fill-nz-unbounded.argb32.ref.png b/test/reference/clip-fill-nz-unbounded.argb32.ref.png
new file mode 100644 (file)
index 0000000..66af094
Binary files /dev/null and b/test/reference/clip-fill-nz-unbounded.argb32.ref.png differ
diff --git a/test/reference/clip-fill-nz-unbounded.base.argb32.ref.png b/test/reference/clip-fill-nz-unbounded.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..19b9f09
Binary files /dev/null and b/test/reference/clip-fill-nz-unbounded.base.argb32.ref.png differ
diff --git a/test/reference/clip-fill-nz-unbounded.base.rgb24.ref.png b/test/reference/clip-fill-nz-unbounded.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..1ad0b17
Binary files /dev/null and b/test/reference/clip-fill-nz-unbounded.base.rgb24.ref.png differ
diff --git a/test/reference/clip-fill-nz-unbounded.image16.ref.png b/test/reference/clip-fill-nz-unbounded.image16.ref.png
new file mode 100644 (file)
index 0000000..e0e66ff
Binary files /dev/null and b/test/reference/clip-fill-nz-unbounded.image16.ref.png differ
diff --git a/test/reference/clip-fill-nz-unbounded.quartz.argb32.ref.png b/test/reference/clip-fill-nz-unbounded.quartz.argb32.ref.png
new file mode 100644 (file)
index 0000000..3423000
Binary files /dev/null and b/test/reference/clip-fill-nz-unbounded.quartz.argb32.ref.png differ
diff --git a/test/reference/clip-fill-nz-unbounded.quartz.rgb24.ref.png b/test/reference/clip-fill-nz-unbounded.quartz.rgb24.ref.png
new file mode 100644 (file)
index 0000000..1612801
Binary files /dev/null and b/test/reference/clip-fill-nz-unbounded.quartz.rgb24.ref.png differ
diff --git a/test/reference/clip-fill-nz-unbounded.rgb24.ref.png b/test/reference/clip-fill-nz-unbounded.rgb24.ref.png
new file mode 100644 (file)
index 0000000..272e0e8
Binary files /dev/null and b/test/reference/clip-fill-nz-unbounded.rgb24.ref.png differ
diff --git a/test/reference/clip-fill-nz-unbounded.svg12.rgb24.xfail.png b/test/reference/clip-fill-nz-unbounded.svg12.rgb24.xfail.png
new file mode 100644 (file)
index 0000000..f949de1
Binary files /dev/null and b/test/reference/clip-fill-nz-unbounded.svg12.rgb24.xfail.png differ
diff --git a/test/reference/clip-fill-nz-unbounded.traps.argb32.ref.png b/test/reference/clip-fill-nz-unbounded.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..19b9f09
Binary files /dev/null and b/test/reference/clip-fill-nz-unbounded.traps.argb32.ref.png differ
diff --git a/test/reference/clip-fill-nz-unbounded.traps.rgb24.ref.png b/test/reference/clip-fill-nz-unbounded.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..1ad0b17
Binary files /dev/null and b/test/reference/clip-fill-nz-unbounded.traps.rgb24.ref.png differ
diff --git a/test/reference/clip-fill-nz-unbounded.xlib-fallback.rgb24.ref.png b/test/reference/clip-fill-nz-unbounded.xlib-fallback.rgb24.ref.png
new file mode 100644 (file)
index 0000000..ec25896
Binary files /dev/null and b/test/reference/clip-fill-nz-unbounded.xlib-fallback.rgb24.ref.png differ
diff --git a/test/reference/clip-fill-rule-pixel-aligned.base.argb32.ref.png b/test/reference/clip-fill-rule-pixel-aligned.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..9b82c4b
Binary files /dev/null and b/test/reference/clip-fill-rule-pixel-aligned.base.argb32.ref.png differ
diff --git a/test/reference/clip-fill-rule-pixel-aligned.base.rgb24.ref.png b/test/reference/clip-fill-rule-pixel-aligned.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..0b4f068
Binary files /dev/null and b/test/reference/clip-fill-rule-pixel-aligned.base.rgb24.ref.png differ
diff --git a/test/reference/clip-fill-rule-pixel-aligned.ref.png b/test/reference/clip-fill-rule-pixel-aligned.ref.png
new file mode 100644 (file)
index 0000000..66eb685
Binary files /dev/null and b/test/reference/clip-fill-rule-pixel-aligned.ref.png differ
diff --git a/test/reference/clip-fill-rule-pixel-aligned.rgb24.ref.png b/test/reference/clip-fill-rule-pixel-aligned.rgb24.ref.png
new file mode 100644 (file)
index 0000000..1970c47
Binary files /dev/null and b/test/reference/clip-fill-rule-pixel-aligned.rgb24.ref.png differ
diff --git a/test/reference/clip-fill-rule-pixel-aligned.traps.argb32.ref.png b/test/reference/clip-fill-rule-pixel-aligned.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..9b82c4b
Binary files /dev/null and b/test/reference/clip-fill-rule-pixel-aligned.traps.argb32.ref.png differ
diff --git a/test/reference/clip-fill-rule-pixel-aligned.traps.rgb24.ref.png b/test/reference/clip-fill-rule-pixel-aligned.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..0b4f068
Binary files /dev/null and b/test/reference/clip-fill-rule-pixel-aligned.traps.rgb24.ref.png differ
diff --git a/test/reference/clip-fill-rule.argb32.ref.png b/test/reference/clip-fill-rule.argb32.ref.png
new file mode 100644 (file)
index 0000000..0af1530
Binary files /dev/null and b/test/reference/clip-fill-rule.argb32.ref.png differ
diff --git a/test/reference/clip-fill-rule.base.argb32.ref.png b/test/reference/clip-fill-rule.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..6b083a7
Binary files /dev/null and b/test/reference/clip-fill-rule.base.argb32.ref.png differ
diff --git a/test/reference/clip-fill-rule.base.rgb24.ref.png b/test/reference/clip-fill-rule.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..d21472d
Binary files /dev/null and b/test/reference/clip-fill-rule.base.rgb24.ref.png differ
diff --git a/test/reference/clip-fill-rule.image16.ref.png b/test/reference/clip-fill-rule.image16.ref.png
new file mode 100644 (file)
index 0000000..101449d
Binary files /dev/null and b/test/reference/clip-fill-rule.image16.ref.png differ
diff --git a/test/reference/clip-fill-rule.pdf.argb32.ref.png b/test/reference/clip-fill-rule.pdf.argb32.ref.png
new file mode 100644 (file)
index 0000000..0d9938e
Binary files /dev/null and b/test/reference/clip-fill-rule.pdf.argb32.ref.png differ
diff --git a/test/reference/clip-fill-rule.ps.argb32.ref.png b/test/reference/clip-fill-rule.ps.argb32.ref.png
new file mode 100644 (file)
index 0000000..1b89434
Binary files /dev/null and b/test/reference/clip-fill-rule.ps.argb32.ref.png differ
diff --git a/test/reference/clip-fill-rule.ps.rgb24.ref.png b/test/reference/clip-fill-rule.ps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..1b061b7
Binary files /dev/null and b/test/reference/clip-fill-rule.ps.rgb24.ref.png differ
diff --git a/test/reference/clip-fill-rule.quartz.rgb24.ref.png b/test/reference/clip-fill-rule.quartz.rgb24.ref.png
new file mode 100644 (file)
index 0000000..c95f290
Binary files /dev/null and b/test/reference/clip-fill-rule.quartz.rgb24.ref.png differ
diff --git a/test/reference/clip-fill-rule.rgb24.ref.png b/test/reference/clip-fill-rule.rgb24.ref.png
new file mode 100644 (file)
index 0000000..7001dbe
Binary files /dev/null and b/test/reference/clip-fill-rule.rgb24.ref.png differ
diff --git a/test/reference/clip-fill-rule.test-paginated.rgb24.ref.png b/test/reference/clip-fill-rule.test-paginated.rgb24.ref.png
new file mode 100644 (file)
index 0000000..d21472d
Binary files /dev/null and b/test/reference/clip-fill-rule.test-paginated.rgb24.ref.png differ
diff --git a/test/reference/clip-fill-rule.traps.argb32.ref.png b/test/reference/clip-fill-rule.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..6b083a7
Binary files /dev/null and b/test/reference/clip-fill-rule.traps.argb32.ref.png differ
diff --git a/test/reference/clip-fill-rule.traps.rgb24.ref.png b/test/reference/clip-fill-rule.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..d21472d
Binary files /dev/null and b/test/reference/clip-fill-rule.traps.rgb24.ref.png differ
diff --git a/test/reference/clip-fill.base.argb32.ref.png b/test/reference/clip-fill.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..d6e84a3
Binary files /dev/null and b/test/reference/clip-fill.base.argb32.ref.png differ
diff --git a/test/reference/clip-fill.base.rgb24.ref.png b/test/reference/clip-fill.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..d6e84a3
Binary files /dev/null and b/test/reference/clip-fill.base.rgb24.ref.png differ
diff --git a/test/reference/clip-fill.image16.ref.png b/test/reference/clip-fill.image16.ref.png
new file mode 100644 (file)
index 0000000..24595ba
Binary files /dev/null and b/test/reference/clip-fill.image16.ref.png differ
diff --git a/test/reference/clip-fill.ps.xfail.png b/test/reference/clip-fill.ps.xfail.png
new file mode 100644 (file)
index 0000000..d0aeaf1
Binary files /dev/null and b/test/reference/clip-fill.ps.xfail.png differ
diff --git a/test/reference/clip-fill.quartz.ref.png b/test/reference/clip-fill.quartz.ref.png
new file mode 100644 (file)
index 0000000..4f235b4
Binary files /dev/null and b/test/reference/clip-fill.quartz.ref.png differ
diff --git a/test/reference/clip-fill.ref.png b/test/reference/clip-fill.ref.png
new file mode 100644 (file)
index 0000000..7f894e9
Binary files /dev/null and b/test/reference/clip-fill.ref.png differ
diff --git a/test/reference/clip-fill.traps.argb32.ref.png b/test/reference/clip-fill.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..d6e84a3
Binary files /dev/null and b/test/reference/clip-fill.traps.argb32.ref.png differ
diff --git a/test/reference/clip-fill.traps.rgb24.ref.png b/test/reference/clip-fill.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..d6e84a3
Binary files /dev/null and b/test/reference/clip-fill.traps.rgb24.ref.png differ
diff --git a/test/reference/clip-fill.xlib-fallback.ref.png b/test/reference/clip-fill.xlib-fallback.ref.png
new file mode 100644 (file)
index 0000000..064b0cf
Binary files /dev/null and b/test/reference/clip-fill.xlib-fallback.ref.png differ
diff --git a/test/reference/clip-group-shapes-aligned-rectangles.base.argb32.ref.png b/test/reference/clip-group-shapes-aligned-rectangles.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..cba7507
Binary files /dev/null and b/test/reference/clip-group-shapes-aligned-rectangles.base.argb32.ref.png differ
diff --git a/test/reference/clip-group-shapes-aligned-rectangles.base.rgb24.ref.png b/test/reference/clip-group-shapes-aligned-rectangles.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..cba7507
Binary files /dev/null and b/test/reference/clip-group-shapes-aligned-rectangles.base.rgb24.ref.png differ
diff --git a/test/reference/clip-group-shapes-aligned-rectangles.ref.png b/test/reference/clip-group-shapes-aligned-rectangles.ref.png
new file mode 100644 (file)
index 0000000..cba7507
Binary files /dev/null and b/test/reference/clip-group-shapes-aligned-rectangles.ref.png differ
diff --git a/test/reference/clip-group-shapes-aligned-rectangles.traps.argb32.ref.png b/test/reference/clip-group-shapes-aligned-rectangles.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..cba7507
Binary files /dev/null and b/test/reference/clip-group-shapes-aligned-rectangles.traps.argb32.ref.png differ
diff --git a/test/reference/clip-group-shapes-aligned-rectangles.traps.rgb24.ref.png b/test/reference/clip-group-shapes-aligned-rectangles.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..cba7507
Binary files /dev/null and b/test/reference/clip-group-shapes-aligned-rectangles.traps.rgb24.ref.png differ
diff --git a/test/reference/clip-group-shapes-circles.base.argb32.ref.png b/test/reference/clip-group-shapes-circles.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..1a67714
Binary files /dev/null and b/test/reference/clip-group-shapes-circles.base.argb32.ref.png differ
diff --git a/test/reference/clip-group-shapes-circles.base.rgb24.ref.png b/test/reference/clip-group-shapes-circles.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..1a67714
Binary files /dev/null and b/test/reference/clip-group-shapes-circles.base.rgb24.ref.png differ
diff --git a/test/reference/clip-group-shapes-circles.ps.ref.png b/test/reference/clip-group-shapes-circles.ps.ref.png
new file mode 100644 (file)
index 0000000..be6203f
Binary files /dev/null and b/test/reference/clip-group-shapes-circles.ps.ref.png differ
diff --git a/test/reference/clip-group-shapes-circles.quartz.ref.png b/test/reference/clip-group-shapes-circles.quartz.ref.png
new file mode 100644 (file)
index 0000000..c2ac9ea
Binary files /dev/null and b/test/reference/clip-group-shapes-circles.quartz.ref.png differ
diff --git a/test/reference/clip-group-shapes-circles.ref.png b/test/reference/clip-group-shapes-circles.ref.png
new file mode 100644 (file)
index 0000000..f9710c2
Binary files /dev/null and b/test/reference/clip-group-shapes-circles.ref.png differ
diff --git a/test/reference/clip-group-shapes-circles.traps.argb32.ref.png b/test/reference/clip-group-shapes-circles.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..3b81146
Binary files /dev/null and b/test/reference/clip-group-shapes-circles.traps.argb32.ref.png differ
diff --git a/test/reference/clip-group-shapes-circles.traps.rgb24.ref.png b/test/reference/clip-group-shapes-circles.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..3b81146
Binary files /dev/null and b/test/reference/clip-group-shapes-circles.traps.rgb24.ref.png differ
diff --git a/test/reference/clip-group-shapes-unaligned-rectangles.base.argb32.ref.png b/test/reference/clip-group-shapes-unaligned-rectangles.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..877e78b
Binary files /dev/null and b/test/reference/clip-group-shapes-unaligned-rectangles.base.argb32.ref.png differ
diff --git a/test/reference/clip-group-shapes-unaligned-rectangles.base.rgb24.ref.png b/test/reference/clip-group-shapes-unaligned-rectangles.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..877e78b
Binary files /dev/null and b/test/reference/clip-group-shapes-unaligned-rectangles.base.rgb24.ref.png differ
diff --git a/test/reference/clip-group-shapes-unaligned-rectangles.ref.png b/test/reference/clip-group-shapes-unaligned-rectangles.ref.png
new file mode 100644 (file)
index 0000000..2ad4118
Binary files /dev/null and b/test/reference/clip-group-shapes-unaligned-rectangles.ref.png differ
diff --git a/test/reference/clip-group-shapes-unaligned-rectangles.traps.argb32.ref.png b/test/reference/clip-group-shapes-unaligned-rectangles.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..877e78b
Binary files /dev/null and b/test/reference/clip-group-shapes-unaligned-rectangles.traps.argb32.ref.png differ
diff --git a/test/reference/clip-group-shapes-unaligned-rectangles.traps.rgb24.ref.png b/test/reference/clip-group-shapes-unaligned-rectangles.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..877e78b
Binary files /dev/null and b/test/reference/clip-group-shapes-unaligned-rectangles.traps.rgb24.ref.png differ
diff --git a/test/reference/clip-image.base.argb32.ref.png b/test/reference/clip-image.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..062c721
Binary files /dev/null and b/test/reference/clip-image.base.argb32.ref.png differ
diff --git a/test/reference/clip-image.base.rgb24.ref.png b/test/reference/clip-image.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..062c721
Binary files /dev/null and b/test/reference/clip-image.base.rgb24.ref.png differ
diff --git a/test/reference/clip-image.image16.ref.png b/test/reference/clip-image.image16.ref.png
new file mode 100644 (file)
index 0000000..770891a
Binary files /dev/null and b/test/reference/clip-image.image16.ref.png differ
diff --git a/test/reference/clip-image.ps.ref.png b/test/reference/clip-image.ps.ref.png
new file mode 100644 (file)
index 0000000..b242249
Binary files /dev/null and b/test/reference/clip-image.ps.ref.png differ
diff --git a/test/reference/clip-image.ref.png b/test/reference/clip-image.ref.png
new file mode 100644 (file)
index 0000000..bf8ee72
Binary files /dev/null and b/test/reference/clip-image.ref.png differ
diff --git a/test/reference/clip-image.traps.argb32.ref.png b/test/reference/clip-image.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..062c721
Binary files /dev/null and b/test/reference/clip-image.traps.argb32.ref.png differ
diff --git a/test/reference/clip-image.traps.rgb24.ref.png b/test/reference/clip-image.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..062c721
Binary files /dev/null and b/test/reference/clip-image.traps.rgb24.ref.png differ
diff --git a/test/reference/clip-intersect.base.argb32.ref.png b/test/reference/clip-intersect.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..2e97b54
Binary files /dev/null and b/test/reference/clip-intersect.base.argb32.ref.png differ
diff --git a/test/reference/clip-intersect.base.rgb24.ref.png b/test/reference/clip-intersect.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..2e97b54
Binary files /dev/null and b/test/reference/clip-intersect.base.rgb24.ref.png differ
diff --git a/test/reference/clip-intersect.ref.png b/test/reference/clip-intersect.ref.png
new file mode 100644 (file)
index 0000000..96f9b08
Binary files /dev/null and b/test/reference/clip-intersect.ref.png differ
diff --git a/test/reference/clip-intersect.traps.argb32.ref.png b/test/reference/clip-intersect.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..2e97b54
Binary files /dev/null and b/test/reference/clip-intersect.traps.argb32.ref.png differ
diff --git a/test/reference/clip-intersect.traps.rgb24.ref.png b/test/reference/clip-intersect.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..2e97b54
Binary files /dev/null and b/test/reference/clip-intersect.traps.rgb24.ref.png differ
diff --git a/test/reference/clip-mixed-antialias.base.argb32.ref.png b/test/reference/clip-mixed-antialias.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..982530c
Binary files /dev/null and b/test/reference/clip-mixed-antialias.base.argb32.ref.png differ
diff --git a/test/reference/clip-mixed-antialias.base.rgb24.ref.png b/test/reference/clip-mixed-antialias.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..982530c
Binary files /dev/null and b/test/reference/clip-mixed-antialias.base.rgb24.ref.png differ
diff --git a/test/reference/clip-mixed-antialias.ref.png b/test/reference/clip-mixed-antialias.ref.png
new file mode 100644 (file)
index 0000000..108cb46
Binary files /dev/null and b/test/reference/clip-mixed-antialias.ref.png differ
diff --git a/test/reference/clip-mixed-antialias.traps.argb32.ref.png b/test/reference/clip-mixed-antialias.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..982530c
Binary files /dev/null and b/test/reference/clip-mixed-antialias.traps.argb32.ref.png differ
diff --git a/test/reference/clip-mixed-antialias.traps.rgb24.ref.png b/test/reference/clip-mixed-antialias.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..982530c
Binary files /dev/null and b/test/reference/clip-mixed-antialias.traps.rgb24.ref.png differ
diff --git a/test/reference/clip-nesting.argb32.ref.png b/test/reference/clip-nesting.argb32.ref.png
new file mode 100644 (file)
index 0000000..927a7d4
Binary files /dev/null and b/test/reference/clip-nesting.argb32.ref.png differ
diff --git a/test/reference/clip-nesting.base.argb32.ref.png b/test/reference/clip-nesting.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..01168df
Binary files /dev/null and b/test/reference/clip-nesting.base.argb32.ref.png differ
diff --git a/test/reference/clip-nesting.base.rgb24.ref.png b/test/reference/clip-nesting.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..d087ab6
Binary files /dev/null and b/test/reference/clip-nesting.base.rgb24.ref.png differ
diff --git a/test/reference/clip-nesting.pdf.argb32.ref.png b/test/reference/clip-nesting.pdf.argb32.ref.png
new file mode 100644 (file)
index 0000000..78ae6e0
Binary files /dev/null and b/test/reference/clip-nesting.pdf.argb32.ref.png differ
diff --git a/test/reference/clip-nesting.ps.argb32.ref.png b/test/reference/clip-nesting.ps.argb32.ref.png
new file mode 100644 (file)
index 0000000..8a0239b
Binary files /dev/null and b/test/reference/clip-nesting.ps.argb32.ref.png differ
diff --git a/test/reference/clip-nesting.ps.rgb24.ref.png b/test/reference/clip-nesting.ps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..f9e1ac9
Binary files /dev/null and b/test/reference/clip-nesting.ps.rgb24.ref.png differ
diff --git a/test/reference/clip-nesting.quartz.argb32.ref.png b/test/reference/clip-nesting.quartz.argb32.ref.png
new file mode 100644 (file)
index 0000000..7bc187c
Binary files /dev/null and b/test/reference/clip-nesting.quartz.argb32.ref.png differ
diff --git a/test/reference/clip-nesting.quartz.rgb24.ref.png b/test/reference/clip-nesting.quartz.rgb24.ref.png
new file mode 100644 (file)
index 0000000..926f5f5
Binary files /dev/null and b/test/reference/clip-nesting.quartz.rgb24.ref.png differ
diff --git a/test/reference/clip-nesting.rgb24.ref.png b/test/reference/clip-nesting.rgb24.ref.png
new file mode 100644 (file)
index 0000000..432318d
Binary files /dev/null and b/test/reference/clip-nesting.rgb24.ref.png differ
diff --git a/test/reference/clip-nesting.test-paginated.rgb24.ref.png b/test/reference/clip-nesting.test-paginated.rgb24.ref.png
new file mode 100644 (file)
index 0000000..d087ab6
Binary files /dev/null and b/test/reference/clip-nesting.test-paginated.rgb24.ref.png differ
diff --git a/test/reference/clip-nesting.traps.argb32.ref.png b/test/reference/clip-nesting.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..01168df
Binary files /dev/null and b/test/reference/clip-nesting.traps.argb32.ref.png differ
diff --git a/test/reference/clip-nesting.traps.rgb24.ref.png b/test/reference/clip-nesting.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..d087ab6
Binary files /dev/null and b/test/reference/clip-nesting.traps.rgb24.ref.png differ
diff --git a/test/reference/clip-operator.argb32.ref.png b/test/reference/clip-operator.argb32.ref.png
new file mode 100644 (file)
index 0000000..47c16de
Binary files /dev/null and b/test/reference/clip-operator.argb32.ref.png differ
diff --git a/test/reference/clip-operator.base.argb32.ref.png b/test/reference/clip-operator.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..63a7baa
Binary files /dev/null and b/test/reference/clip-operator.base.argb32.ref.png differ
diff --git a/test/reference/clip-operator.base.rgb24.ref.png b/test/reference/clip-operator.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..d3d84f2
Binary files /dev/null and b/test/reference/clip-operator.base.rgb24.ref.png differ
diff --git a/test/reference/clip-operator.gl.argb32.ref.png b/test/reference/clip-operator.gl.argb32.ref.png
new file mode 100644 (file)
index 0000000..92d8b75
Binary files /dev/null and b/test/reference/clip-operator.gl.argb32.ref.png differ
diff --git a/test/reference/clip-operator.image16.ref.png b/test/reference/clip-operator.image16.ref.png
new file mode 100644 (file)
index 0000000..ab8cd9b
Binary files /dev/null and b/test/reference/clip-operator.image16.ref.png differ
diff --git a/test/reference/clip-operator.pdf.argb32.ref.png b/test/reference/clip-operator.pdf.argb32.ref.png
new file mode 100644 (file)
index 0000000..7f8c93e
Binary files /dev/null and b/test/reference/clip-operator.pdf.argb32.ref.png differ
diff --git a/test/reference/clip-operator.pdf.rgb24.ref.png b/test/reference/clip-operator.pdf.rgb24.ref.png
new file mode 100644 (file)
index 0000000..fc4f431
Binary files /dev/null and b/test/reference/clip-operator.pdf.rgb24.ref.png differ
diff --git a/test/reference/clip-operator.ps2.rgb24.ref.png b/test/reference/clip-operator.ps2.rgb24.ref.png
new file mode 100644 (file)
index 0000000..5245299
Binary files /dev/null and b/test/reference/clip-operator.ps2.rgb24.ref.png differ
diff --git a/test/reference/clip-operator.ps3.argb32.ref.png b/test/reference/clip-operator.ps3.argb32.ref.png
new file mode 100644 (file)
index 0000000..cd207d9
Binary files /dev/null and b/test/reference/clip-operator.ps3.argb32.ref.png differ
diff --git a/test/reference/clip-operator.ps3.ref.png b/test/reference/clip-operator.ps3.ref.png
new file mode 100644 (file)
index 0000000..dee12ca
Binary files /dev/null and b/test/reference/clip-operator.ps3.ref.png differ
diff --git a/test/reference/clip-operator.ps3.rgb24.ref.png b/test/reference/clip-operator.ps3.rgb24.ref.png
new file mode 100644 (file)
index 0000000..5245299
Binary files /dev/null and b/test/reference/clip-operator.ps3.rgb24.ref.png differ
diff --git a/test/reference/clip-operator.quartz.argb32.ref.png b/test/reference/clip-operator.quartz.argb32.ref.png
new file mode 100644 (file)
index 0000000..ecf6ee2
Binary files /dev/null and b/test/reference/clip-operator.quartz.argb32.ref.png differ
diff --git a/test/reference/clip-operator.quartz.rgb24.ref.png b/test/reference/clip-operator.quartz.rgb24.ref.png
new file mode 100644 (file)
index 0000000..67c628f
Binary files /dev/null and b/test/reference/clip-operator.quartz.rgb24.ref.png differ
diff --git a/test/reference/clip-operator.rgb24.ref.png b/test/reference/clip-operator.rgb24.ref.png
new file mode 100644 (file)
index 0000000..8ef57e1
Binary files /dev/null and b/test/reference/clip-operator.rgb24.ref.png differ
diff --git a/test/reference/clip-operator.svg12.argb32.xfail.png b/test/reference/clip-operator.svg12.argb32.xfail.png
new file mode 100644 (file)
index 0000000..a1b8072
Binary files /dev/null and b/test/reference/clip-operator.svg12.argb32.xfail.png differ
diff --git a/test/reference/clip-operator.svg12.rgb24.xfail.png b/test/reference/clip-operator.svg12.rgb24.xfail.png
new file mode 100644 (file)
index 0000000..9522770
Binary files /dev/null and b/test/reference/clip-operator.svg12.rgb24.xfail.png differ
diff --git a/test/reference/clip-operator.test-paginated.argb32.ref.png b/test/reference/clip-operator.test-paginated.argb32.ref.png
new file mode 100644 (file)
index 0000000..0203b1d
Binary files /dev/null and b/test/reference/clip-operator.test-paginated.argb32.ref.png differ
diff --git a/test/reference/clip-operator.traps.argb32.ref.png b/test/reference/clip-operator.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..1a562cd
Binary files /dev/null and b/test/reference/clip-operator.traps.argb32.ref.png differ
diff --git a/test/reference/clip-operator.traps.rgb24.ref.png b/test/reference/clip-operator.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..8933505
Binary files /dev/null and b/test/reference/clip-operator.traps.rgb24.ref.png differ
diff --git a/test/reference/clip-operator.xlib-fallback.ref.png b/test/reference/clip-operator.xlib-fallback.ref.png
new file mode 100644 (file)
index 0000000..9ef8637
Binary files /dev/null and b/test/reference/clip-operator.xlib-fallback.ref.png differ
diff --git a/test/reference/clip-polygons.base.ref.png b/test/reference/clip-polygons.base.ref.png
new file mode 100644 (file)
index 0000000..e1f294c
Binary files /dev/null and b/test/reference/clip-polygons.base.ref.png differ
diff --git a/test/reference/clip-polygons.ref.png b/test/reference/clip-polygons.ref.png
new file mode 100644 (file)
index 0000000..df93d88
Binary files /dev/null and b/test/reference/clip-polygons.ref.png differ
diff --git a/test/reference/clip-polygons.traps.ref.png b/test/reference/clip-polygons.traps.ref.png
new file mode 100644 (file)
index 0000000..a8c5734
Binary files /dev/null and b/test/reference/clip-polygons.traps.ref.png differ
diff --git a/test/reference/clip-push-group.base.argb32.ref.png b/test/reference/clip-push-group.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..de6ac63
Binary files /dev/null and b/test/reference/clip-push-group.base.argb32.ref.png differ
diff --git a/test/reference/clip-push-group.base.rgb24.ref.png b/test/reference/clip-push-group.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..de6ac63
Binary files /dev/null and b/test/reference/clip-push-group.base.rgb24.ref.png differ
diff --git a/test/reference/clip-push-group.image16.ref.png b/test/reference/clip-push-group.image16.ref.png
new file mode 100644 (file)
index 0000000..24f4424
Binary files /dev/null and b/test/reference/clip-push-group.image16.ref.png differ
diff --git a/test/reference/clip-push-group.pdf.ref.png b/test/reference/clip-push-group.pdf.ref.png
new file mode 100644 (file)
index 0000000..37b58c5
Binary files /dev/null and b/test/reference/clip-push-group.pdf.ref.png differ
diff --git a/test/reference/clip-push-group.ps.ref.png b/test/reference/clip-push-group.ps.ref.png
new file mode 100644 (file)
index 0000000..7af9fe5
Binary files /dev/null and b/test/reference/clip-push-group.ps.ref.png differ
diff --git a/test/reference/clip-push-group.quartz.ref.png b/test/reference/clip-push-group.quartz.ref.png
new file mode 100644 (file)
index 0000000..22e1525
Binary files /dev/null and b/test/reference/clip-push-group.quartz.ref.png differ
diff --git a/test/reference/clip-push-group.ref.png b/test/reference/clip-push-group.ref.png
new file mode 100644 (file)
index 0000000..f57e7d2
Binary files /dev/null and b/test/reference/clip-push-group.ref.png differ
diff --git a/test/reference/clip-push-group.svg.ref.png b/test/reference/clip-push-group.svg.ref.png
new file mode 100644 (file)
index 0000000..291b473
Binary files /dev/null and b/test/reference/clip-push-group.svg.ref.png differ
diff --git a/test/reference/clip-push-group.traps.argb32.ref.png b/test/reference/clip-push-group.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..de6ac63
Binary files /dev/null and b/test/reference/clip-push-group.traps.argb32.ref.png differ
diff --git a/test/reference/clip-push-group.traps.rgb24.ref.png b/test/reference/clip-push-group.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..de6ac63
Binary files /dev/null and b/test/reference/clip-push-group.traps.rgb24.ref.png differ
diff --git a/test/reference/clip-rectilinear.base.ref.png b/test/reference/clip-rectilinear.base.ref.png
new file mode 100644 (file)
index 0000000..2a27bec
Binary files /dev/null and b/test/reference/clip-rectilinear.base.ref.png differ
diff --git a/test/reference/clip-rectilinear.ref.png b/test/reference/clip-rectilinear.ref.png
new file mode 100644 (file)
index 0000000..9d910db
Binary files /dev/null and b/test/reference/clip-rectilinear.ref.png differ
diff --git a/test/reference/clip-rectilinear.traps.ref.png b/test/reference/clip-rectilinear.traps.ref.png
new file mode 100644 (file)
index 0000000..2a27bec
Binary files /dev/null and b/test/reference/clip-rectilinear.traps.ref.png differ
diff --git a/test/reference/clip-rotate-image-surface-paint.base.argb32.ref.png b/test/reference/clip-rotate-image-surface-paint.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..1093804
Binary files /dev/null and b/test/reference/clip-rotate-image-surface-paint.base.argb32.ref.png differ
diff --git a/test/reference/clip-rotate-image-surface-paint.base.rgb24.ref.png b/test/reference/clip-rotate-image-surface-paint.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..1093804
Binary files /dev/null and b/test/reference/clip-rotate-image-surface-paint.base.rgb24.ref.png differ
diff --git a/test/reference/clip-rotate-image-surface-paint.ref.png b/test/reference/clip-rotate-image-surface-paint.ref.png
new file mode 100644 (file)
index 0000000..1093804
Binary files /dev/null and b/test/reference/clip-rotate-image-surface-paint.ref.png differ
diff --git a/test/reference/clip-rotate-image-surface-paint.traps.argb32.ref.png b/test/reference/clip-rotate-image-surface-paint.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..1093804
Binary files /dev/null and b/test/reference/clip-rotate-image-surface-paint.traps.argb32.ref.png differ
diff --git a/test/reference/clip-rotate-image-surface-paint.traps.rgb24.ref.png b/test/reference/clip-rotate-image-surface-paint.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..1093804
Binary files /dev/null and b/test/reference/clip-rotate-image-surface-paint.traps.rgb24.ref.png differ
diff --git a/test/reference/clip-shape.base.argb32.ref.png b/test/reference/clip-shape.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..8757979
Binary files /dev/null and b/test/reference/clip-shape.base.argb32.ref.png differ
diff --git a/test/reference/clip-shape.base.rgb24.ref.png b/test/reference/clip-shape.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..8757979
Binary files /dev/null and b/test/reference/clip-shape.base.rgb24.ref.png differ
diff --git a/test/reference/clip-shape.image16.ref.png b/test/reference/clip-shape.image16.ref.png
new file mode 100644 (file)
index 0000000..0ef06b2
Binary files /dev/null and b/test/reference/clip-shape.image16.ref.png differ
diff --git a/test/reference/clip-shape.ps.ref.png b/test/reference/clip-shape.ps.ref.png
new file mode 100644 (file)
index 0000000..1125fb5
Binary files /dev/null and b/test/reference/clip-shape.ps.ref.png differ
diff --git a/test/reference/clip-shape.quartz.ref.png b/test/reference/clip-shape.quartz.ref.png
new file mode 100644 (file)
index 0000000..2923406
Binary files /dev/null and b/test/reference/clip-shape.quartz.ref.png differ
diff --git a/test/reference/clip-shape.ref.png b/test/reference/clip-shape.ref.png
new file mode 100644 (file)
index 0000000..892a4aa
Binary files /dev/null and b/test/reference/clip-shape.ref.png differ
diff --git a/test/reference/clip-shape.traps.argb32.ref.png b/test/reference/clip-shape.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..362f24f
Binary files /dev/null and b/test/reference/clip-shape.traps.argb32.ref.png differ
diff --git a/test/reference/clip-shape.traps.rgb24.ref.png b/test/reference/clip-shape.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..362f24f
Binary files /dev/null and b/test/reference/clip-shape.traps.rgb24.ref.png differ
diff --git a/test/reference/clip-shape.xlib-fallback.ref.png b/test/reference/clip-shape.xlib-fallback.ref.png
new file mode 100644 (file)
index 0000000..e9aa947
Binary files /dev/null and b/test/reference/clip-shape.xlib-fallback.ref.png differ
diff --git a/test/reference/clip-stroke-no-op.base.argb32.ref.png b/test/reference/clip-stroke-no-op.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..2256461
Binary files /dev/null and b/test/reference/clip-stroke-no-op.base.argb32.ref.png differ
diff --git a/test/reference/clip-stroke-no-op.base.rgb24.ref.png b/test/reference/clip-stroke-no-op.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..2256461
Binary files /dev/null and b/test/reference/clip-stroke-no-op.base.rgb24.ref.png differ
diff --git a/test/reference/clip-stroke-no-op.image16.ref.png b/test/reference/clip-stroke-no-op.image16.ref.png
new file mode 100644 (file)
index 0000000..cf0c74a
Binary files /dev/null and b/test/reference/clip-stroke-no-op.image16.ref.png differ
diff --git a/test/reference/clip-stroke-no-op.ref.png b/test/reference/clip-stroke-no-op.ref.png
new file mode 100644 (file)
index 0000000..2256461
Binary files /dev/null and b/test/reference/clip-stroke-no-op.ref.png differ
diff --git a/test/reference/clip-stroke-no-op.traps.argb32.ref.png b/test/reference/clip-stroke-no-op.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..2256461
Binary files /dev/null and b/test/reference/clip-stroke-no-op.traps.argb32.ref.png differ
diff --git a/test/reference/clip-stroke-no-op.traps.rgb24.ref.png b/test/reference/clip-stroke-no-op.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..2256461
Binary files /dev/null and b/test/reference/clip-stroke-no-op.traps.rgb24.ref.png differ
diff --git a/test/reference/clip-stroke-unbounded.argb32.ref.png b/test/reference/clip-stroke-unbounded.argb32.ref.png
new file mode 100644 (file)
index 0000000..df0ee64
Binary files /dev/null and b/test/reference/clip-stroke-unbounded.argb32.ref.png differ
diff --git a/test/reference/clip-stroke-unbounded.base.argb32.ref.png b/test/reference/clip-stroke-unbounded.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..aea8cba
Binary files /dev/null and b/test/reference/clip-stroke-unbounded.base.argb32.ref.png differ
diff --git a/test/reference/clip-stroke-unbounded.base.rgb24.ref.png b/test/reference/clip-stroke-unbounded.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..70cddac
Binary files /dev/null and b/test/reference/clip-stroke-unbounded.base.rgb24.ref.png differ
diff --git a/test/reference/clip-stroke-unbounded.image16.rgb24.ref.png b/test/reference/clip-stroke-unbounded.image16.rgb24.ref.png
new file mode 100644 (file)
index 0000000..2dfd48b
Binary files /dev/null and b/test/reference/clip-stroke-unbounded.image16.rgb24.ref.png differ
diff --git a/test/reference/clip-stroke-unbounded.quartz.argb32.ref.png b/test/reference/clip-stroke-unbounded.quartz.argb32.ref.png
new file mode 100644 (file)
index 0000000..8bd5b36
Binary files /dev/null and b/test/reference/clip-stroke-unbounded.quartz.argb32.ref.png differ
diff --git a/test/reference/clip-stroke-unbounded.quartz.rgb24.ref.png b/test/reference/clip-stroke-unbounded.quartz.rgb24.ref.png
new file mode 100644 (file)
index 0000000..5349f84
Binary files /dev/null and b/test/reference/clip-stroke-unbounded.quartz.rgb24.ref.png differ
diff --git a/test/reference/clip-stroke-unbounded.rgb24.ref.png b/test/reference/clip-stroke-unbounded.rgb24.ref.png
new file mode 100644 (file)
index 0000000..7e53e69
Binary files /dev/null and b/test/reference/clip-stroke-unbounded.rgb24.ref.png differ
diff --git a/test/reference/clip-stroke-unbounded.svg12.rgb24.xfail.png b/test/reference/clip-stroke-unbounded.svg12.rgb24.xfail.png
new file mode 100644 (file)
index 0000000..c35fc83
Binary files /dev/null and b/test/reference/clip-stroke-unbounded.svg12.rgb24.xfail.png differ
diff --git a/test/reference/clip-stroke-unbounded.traps.argb32.ref.png b/test/reference/clip-stroke-unbounded.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..aea8cba
Binary files /dev/null and b/test/reference/clip-stroke-unbounded.traps.argb32.ref.png differ
diff --git a/test/reference/clip-stroke-unbounded.traps.rgb24.ref.png b/test/reference/clip-stroke-unbounded.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..70cddac
Binary files /dev/null and b/test/reference/clip-stroke-unbounded.traps.rgb24.ref.png differ
diff --git a/test/reference/clip-stroke-unbounded.xlib-fallback.rgb24.ref.png b/test/reference/clip-stroke-unbounded.xlib-fallback.rgb24.ref.png
new file mode 100644 (file)
index 0000000..20ebfe4
Binary files /dev/null and b/test/reference/clip-stroke-unbounded.xlib-fallback.rgb24.ref.png differ
diff --git a/test/reference/clip-stroke.base.argb32.ref.png b/test/reference/clip-stroke.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..0a6342d
Binary files /dev/null and b/test/reference/clip-stroke.base.argb32.ref.png differ
diff --git a/test/reference/clip-stroke.base.rgb24.ref.png b/test/reference/clip-stroke.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..0a6342d
Binary files /dev/null and b/test/reference/clip-stroke.base.rgb24.ref.png differ
diff --git a/test/reference/clip-stroke.image16.ref.png b/test/reference/clip-stroke.image16.ref.png
new file mode 100644 (file)
index 0000000..ad62af4
Binary files /dev/null and b/test/reference/clip-stroke.image16.ref.png differ
diff --git a/test/reference/clip-stroke.ps.xfail.png b/test/reference/clip-stroke.ps.xfail.png
new file mode 100644 (file)
index 0000000..cc67b08
Binary files /dev/null and b/test/reference/clip-stroke.ps.xfail.png differ
diff --git a/test/reference/clip-stroke.quartz.ref.png b/test/reference/clip-stroke.quartz.ref.png
new file mode 100644 (file)
index 0000000..994e317
Binary files /dev/null and b/test/reference/clip-stroke.quartz.ref.png differ
diff --git a/test/reference/clip-stroke.ref.png b/test/reference/clip-stroke.ref.png
new file mode 100644 (file)
index 0000000..c40fe63
Binary files /dev/null and b/test/reference/clip-stroke.ref.png differ
diff --git a/test/reference/clip-stroke.traps.argb32.ref.png b/test/reference/clip-stroke.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..36b113a
Binary files /dev/null and b/test/reference/clip-stroke.traps.argb32.ref.png differ
diff --git a/test/reference/clip-stroke.traps.rgb24.ref.png b/test/reference/clip-stroke.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..36b113a
Binary files /dev/null and b/test/reference/clip-stroke.traps.rgb24.ref.png differ
diff --git a/test/reference/clip-stroke.xlib-fallback.ref.png b/test/reference/clip-stroke.xlib-fallback.ref.png
new file mode 100644 (file)
index 0000000..cef5000
Binary files /dev/null and b/test/reference/clip-stroke.xlib-fallback.ref.png differ
diff --git a/test/reference/clip-text.base.argb32.ref.png b/test/reference/clip-text.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..eed421f
Binary files /dev/null and b/test/reference/clip-text.base.argb32.ref.png differ
diff --git a/test/reference/clip-text.base.rgb24.ref.png b/test/reference/clip-text.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..eed421f
Binary files /dev/null and b/test/reference/clip-text.base.rgb24.ref.png differ
diff --git a/test/reference/clip-text.image16.ref.png b/test/reference/clip-text.image16.ref.png
new file mode 100644 (file)
index 0000000..2122954
Binary files /dev/null and b/test/reference/clip-text.image16.ref.png differ
diff --git a/test/reference/clip-text.ps.xfail.png b/test/reference/clip-text.ps.xfail.png
new file mode 100644 (file)
index 0000000..b50217d
Binary files /dev/null and b/test/reference/clip-text.ps.xfail.png differ
diff --git a/test/reference/clip-text.quartz.ref.png b/test/reference/clip-text.quartz.ref.png
new file mode 100644 (file)
index 0000000..d251bfa
Binary files /dev/null and b/test/reference/clip-text.quartz.ref.png differ
diff --git a/test/reference/clip-text.ref.png b/test/reference/clip-text.ref.png
new file mode 100644 (file)
index 0000000..d65a8f6
Binary files /dev/null and b/test/reference/clip-text.ref.png differ
diff --git a/test/reference/clip-text.svg.ref.png b/test/reference/clip-text.svg.ref.png
new file mode 100644 (file)
index 0000000..a113b14
Binary files /dev/null and b/test/reference/clip-text.svg.ref.png differ
diff --git a/test/reference/clip-text.traps.argb32.ref.png b/test/reference/clip-text.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..eed421f
Binary files /dev/null and b/test/reference/clip-text.traps.argb32.ref.png differ
diff --git a/test/reference/clip-text.traps.rgb24.ref.png b/test/reference/clip-text.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..eed421f
Binary files /dev/null and b/test/reference/clip-text.traps.rgb24.ref.png differ
diff --git a/test/reference/clip-twice-rectangle.base.argb32.ref.png b/test/reference/clip-twice-rectangle.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..d0e65ea
Binary files /dev/null and b/test/reference/clip-twice-rectangle.base.argb32.ref.png differ
diff --git a/test/reference/clip-twice-rectangle.base.rgb24.ref.png b/test/reference/clip-twice-rectangle.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..d0e65ea
Binary files /dev/null and b/test/reference/clip-twice-rectangle.base.rgb24.ref.png differ
diff --git a/test/reference/clip-twice-rectangle.ref.png b/test/reference/clip-twice-rectangle.ref.png
new file mode 100644 (file)
index 0000000..d0e65ea
Binary files /dev/null and b/test/reference/clip-twice-rectangle.ref.png differ
diff --git a/test/reference/clip-twice-rectangle.traps.argb32.ref.png b/test/reference/clip-twice-rectangle.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..d0e65ea
Binary files /dev/null and b/test/reference/clip-twice-rectangle.traps.argb32.ref.png differ
diff --git a/test/reference/clip-twice-rectangle.traps.rgb24.ref.png b/test/reference/clip-twice-rectangle.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..d0e65ea
Binary files /dev/null and b/test/reference/clip-twice-rectangle.traps.rgb24.ref.png differ
diff --git a/test/reference/clip-twice.argb32.ref.png b/test/reference/clip-twice.argb32.ref.png
new file mode 100644 (file)
index 0000000..e0bdf32
Binary files /dev/null and b/test/reference/clip-twice.argb32.ref.png differ
diff --git a/test/reference/clip-twice.base.argb32.ref.png b/test/reference/clip-twice.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..ba62180
Binary files /dev/null and b/test/reference/clip-twice.base.argb32.ref.png differ
diff --git a/test/reference/clip-twice.base.rgb24.ref.png b/test/reference/clip-twice.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..9cbdc4d
Binary files /dev/null and b/test/reference/clip-twice.base.rgb24.ref.png differ
diff --git a/test/reference/clip-twice.image16.ref.png b/test/reference/clip-twice.image16.ref.png
new file mode 100644 (file)
index 0000000..d5a3f45
Binary files /dev/null and b/test/reference/clip-twice.image16.ref.png differ
diff --git a/test/reference/clip-twice.pdf.argb32.ref.png b/test/reference/clip-twice.pdf.argb32.ref.png
new file mode 100644 (file)
index 0000000..2a7541f
Binary files /dev/null and b/test/reference/clip-twice.pdf.argb32.ref.png differ
diff --git a/test/reference/clip-twice.ps.argb32.ref.png b/test/reference/clip-twice.ps.argb32.ref.png
new file mode 100644 (file)
index 0000000..5d29d17
Binary files /dev/null and b/test/reference/clip-twice.ps.argb32.ref.png differ
diff --git a/test/reference/clip-twice.ps.rgb24.ref.png b/test/reference/clip-twice.ps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..85eb890
Binary files /dev/null and b/test/reference/clip-twice.ps.rgb24.ref.png differ
diff --git a/test/reference/clip-twice.quartz.argb32.ref.png b/test/reference/clip-twice.quartz.argb32.ref.png
new file mode 100644 (file)
index 0000000..04b588d
Binary files /dev/null and b/test/reference/clip-twice.quartz.argb32.ref.png differ
diff --git a/test/reference/clip-twice.quartz.rgb24.ref.png b/test/reference/clip-twice.quartz.rgb24.ref.png
new file mode 100644 (file)
index 0000000..eb4f62c
Binary files /dev/null and b/test/reference/clip-twice.quartz.rgb24.ref.png differ
diff --git a/test/reference/clip-twice.rgb24.ref.png b/test/reference/clip-twice.rgb24.ref.png
new file mode 100644 (file)
index 0000000..97cb680
Binary files /dev/null and b/test/reference/clip-twice.rgb24.ref.png differ
diff --git a/test/reference/clip-twice.test-paginated.argb32.ref.png b/test/reference/clip-twice.test-paginated.argb32.ref.png
new file mode 100644 (file)
index 0000000..ffd59aa
Binary files /dev/null and b/test/reference/clip-twice.test-paginated.argb32.ref.png differ
diff --git a/test/reference/clip-twice.test-paginated.rgb24.ref.png b/test/reference/clip-twice.test-paginated.rgb24.ref.png
new file mode 100644 (file)
index 0000000..e3d0ae4
Binary files /dev/null and b/test/reference/clip-twice.test-paginated.rgb24.ref.png differ
diff --git a/test/reference/clip-twice.traps.argb32.ref.png b/test/reference/clip-twice.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..057fc31
Binary files /dev/null and b/test/reference/clip-twice.traps.argb32.ref.png differ
diff --git a/test/reference/clip-twice.traps.rgb24.ref.png b/test/reference/clip-twice.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..6002d34
Binary files /dev/null and b/test/reference/clip-twice.traps.rgb24.ref.png differ
diff --git a/test/reference/clip-unbounded.base.argb32.ref.png b/test/reference/clip-unbounded.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..0b65905
Binary files /dev/null and b/test/reference/clip-unbounded.base.argb32.ref.png differ
diff --git a/test/reference/clip-unbounded.base.rgb24.ref.png b/test/reference/clip-unbounded.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..2baf9f4
Binary files /dev/null and b/test/reference/clip-unbounded.base.rgb24.ref.png differ
diff --git a/test/reference/clip-unbounded.pdf.argb32.xfail.png b/test/reference/clip-unbounded.pdf.argb32.xfail.png
new file mode 100644 (file)
index 0000000..1ed070c
Binary files /dev/null and b/test/reference/clip-unbounded.pdf.argb32.xfail.png differ
diff --git a/test/reference/clip-unbounded.pdf.rgb24.xfail.png b/test/reference/clip-unbounded.pdf.rgb24.xfail.png
new file mode 100644 (file)
index 0000000..1ed070c
Binary files /dev/null and b/test/reference/clip-unbounded.pdf.rgb24.xfail.png differ
diff --git a/test/reference/clip-unbounded.ref.png b/test/reference/clip-unbounded.ref.png
new file mode 100644 (file)
index 0000000..0b65905
Binary files /dev/null and b/test/reference/clip-unbounded.ref.png differ
diff --git a/test/reference/clip-unbounded.rgb24.ref.png b/test/reference/clip-unbounded.rgb24.ref.png
new file mode 100644 (file)
index 0000000..2baf9f4
Binary files /dev/null and b/test/reference/clip-unbounded.rgb24.ref.png differ
diff --git a/test/reference/clip-unbounded.svg12.rgb24.xfail.png b/test/reference/clip-unbounded.svg12.rgb24.xfail.png
new file mode 100644 (file)
index 0000000..0b65905
Binary files /dev/null and b/test/reference/clip-unbounded.svg12.rgb24.xfail.png differ
diff --git a/test/reference/clip-unbounded.traps.argb32.ref.png b/test/reference/clip-unbounded.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..0b65905
Binary files /dev/null and b/test/reference/clip-unbounded.traps.argb32.ref.png differ
diff --git a/test/reference/clip-unbounded.traps.rgb24.ref.png b/test/reference/clip-unbounded.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..2baf9f4
Binary files /dev/null and b/test/reference/clip-unbounded.traps.rgb24.ref.png differ
diff --git a/test/reference/clipped-group.base.argb32.ref.png b/test/reference/clipped-group.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..9855e61
Binary files /dev/null and b/test/reference/clipped-group.base.argb32.ref.png differ
diff --git a/test/reference/clipped-group.base.rgb24.ref.png b/test/reference/clipped-group.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..9855e61
Binary files /dev/null and b/test/reference/clipped-group.base.rgb24.ref.png differ
diff --git a/test/reference/clipped-group.image16.ref.png b/test/reference/clipped-group.image16.ref.png
new file mode 100644 (file)
index 0000000..bf419f6
Binary files /dev/null and b/test/reference/clipped-group.image16.ref.png differ
diff --git a/test/reference/clipped-group.pdf.ref.png b/test/reference/clipped-group.pdf.ref.png
new file mode 100644 (file)
index 0000000..23db5a4
Binary files /dev/null and b/test/reference/clipped-group.pdf.ref.png differ
diff --git a/test/reference/clipped-group.ps2.ref.png b/test/reference/clipped-group.ps2.ref.png
new file mode 100644 (file)
index 0000000..7a0f45c
Binary files /dev/null and b/test/reference/clipped-group.ps2.ref.png differ
diff --git a/test/reference/clipped-group.ps3.ref.png b/test/reference/clipped-group.ps3.ref.png
new file mode 100644 (file)
index 0000000..7a0f45c
Binary files /dev/null and b/test/reference/clipped-group.ps3.ref.png differ
diff --git a/test/reference/clipped-group.quartz.ref.png b/test/reference/clipped-group.quartz.ref.png
new file mode 100644 (file)
index 0000000..10e22f7
Binary files /dev/null and b/test/reference/clipped-group.quartz.ref.png differ
diff --git a/test/reference/clipped-group.ref.png b/test/reference/clipped-group.ref.png
new file mode 100644 (file)
index 0000000..fe9b8dc
Binary files /dev/null and b/test/reference/clipped-group.ref.png differ
diff --git a/test/reference/clipped-group.svg.ref.png b/test/reference/clipped-group.svg.ref.png
new file mode 100644 (file)
index 0000000..196aec0
Binary files /dev/null and b/test/reference/clipped-group.svg.ref.png differ
diff --git a/test/reference/clipped-group.traps.argb32.ref.png b/test/reference/clipped-group.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..b25c9f4
Binary files /dev/null and b/test/reference/clipped-group.traps.argb32.ref.png differ
diff --git a/test/reference/clipped-group.traps.rgb24.ref.png b/test/reference/clipped-group.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..b25c9f4
Binary files /dev/null and b/test/reference/clipped-group.traps.rgb24.ref.png differ
diff --git a/test/reference/clipped-group.xlib-fallback.ref.png b/test/reference/clipped-group.xlib-fallback.ref.png
new file mode 100644 (file)
index 0000000..e0a5dc0
Binary files /dev/null and b/test/reference/clipped-group.xlib-fallback.ref.png differ
diff --git a/test/reference/clipped-surface.base.argb32.ref.png b/test/reference/clipped-surface.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..8e8b008
Binary files /dev/null and b/test/reference/clipped-surface.base.argb32.ref.png differ
diff --git a/test/reference/clipped-surface.base.rgb24.ref.png b/test/reference/clipped-surface.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..8e8b008
Binary files /dev/null and b/test/reference/clipped-surface.base.rgb24.ref.png differ
diff --git a/test/reference/clipped-surface.image16.ref.png b/test/reference/clipped-surface.image16.ref.png
new file mode 100644 (file)
index 0000000..e9ad572
Binary files /dev/null and b/test/reference/clipped-surface.image16.ref.png differ
diff --git a/test/reference/clipped-surface.ref.png b/test/reference/clipped-surface.ref.png
new file mode 100644 (file)
index 0000000..7fed5a3
Binary files /dev/null and b/test/reference/clipped-surface.ref.png differ
diff --git a/test/reference/clipped-surface.traps.argb32.ref.png b/test/reference/clipped-surface.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..8e8b008
Binary files /dev/null and b/test/reference/clipped-surface.traps.argb32.ref.png differ
diff --git a/test/reference/clipped-surface.traps.rgb24.ref.png b/test/reference/clipped-surface.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..8e8b008
Binary files /dev/null and b/test/reference/clipped-surface.traps.rgb24.ref.png differ
diff --git a/test/reference/clipped-trapezoids.ref.png b/test/reference/clipped-trapezoids.ref.png
new file mode 100644 (file)
index 0000000..975a692
Binary files /dev/null and b/test/reference/clipped-trapezoids.ref.png differ
diff --git a/test/reference/close-path-current-point.base.argb32.ref.png b/test/reference/close-path-current-point.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..f07aeba
Binary files /dev/null and b/test/reference/close-path-current-point.base.argb32.ref.png differ
diff --git a/test/reference/close-path-current-point.base.rgb24.ref.png b/test/reference/close-path-current-point.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..f07aeba
Binary files /dev/null and b/test/reference/close-path-current-point.base.rgb24.ref.png differ
diff --git a/test/reference/close-path-current-point.image16.ref.png b/test/reference/close-path-current-point.image16.ref.png
new file mode 100644 (file)
index 0000000..6aacf3e
Binary files /dev/null and b/test/reference/close-path-current-point.image16.ref.png differ
diff --git a/test/reference/close-path-current-point.ps.ref.png b/test/reference/close-path-current-point.ps.ref.png
new file mode 100644 (file)
index 0000000..1442f01
Binary files /dev/null and b/test/reference/close-path-current-point.ps.ref.png differ
diff --git a/test/reference/close-path-current-point.ref.png b/test/reference/close-path-current-point.ref.png
new file mode 100644 (file)
index 0000000..9d3fd24
Binary files /dev/null and b/test/reference/close-path-current-point.ref.png differ
diff --git a/test/reference/close-path-current-point.traps.argb32.ref.png b/test/reference/close-path-current-point.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..f07aeba
Binary files /dev/null and b/test/reference/close-path-current-point.traps.argb32.ref.png differ
diff --git a/test/reference/close-path-current-point.traps.rgb24.ref.png b/test/reference/close-path-current-point.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..f07aeba
Binary files /dev/null and b/test/reference/close-path-current-point.traps.rgb24.ref.png differ
diff --git a/test/reference/close-path.base.argb32.ref.png b/test/reference/close-path.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..b53fab2
Binary files /dev/null and b/test/reference/close-path.base.argb32.ref.png differ
diff --git a/test/reference/close-path.base.rgb24.ref.png b/test/reference/close-path.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..b53fab2
Binary files /dev/null and b/test/reference/close-path.base.rgb24.ref.png differ
diff --git a/test/reference/close-path.ps2.ref.png b/test/reference/close-path.ps2.ref.png
new file mode 100644 (file)
index 0000000..e43821c
Binary files /dev/null and b/test/reference/close-path.ps2.ref.png differ
diff --git a/test/reference/close-path.ps3.ref.png b/test/reference/close-path.ps3.ref.png
new file mode 100644 (file)
index 0000000..e43821c
Binary files /dev/null and b/test/reference/close-path.ps3.ref.png differ
diff --git a/test/reference/close-path.ref.png b/test/reference/close-path.ref.png
new file mode 100644 (file)
index 0000000..117c1ae
Binary files /dev/null and b/test/reference/close-path.ref.png differ
diff --git a/test/reference/close-path.traps.argb32.ref.png b/test/reference/close-path.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..b53fab2
Binary files /dev/null and b/test/reference/close-path.traps.argb32.ref.png differ
diff --git a/test/reference/close-path.traps.rgb24.ref.png b/test/reference/close-path.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..b53fab2
Binary files /dev/null and b/test/reference/close-path.traps.rgb24.ref.png differ
diff --git a/test/reference/composite-integer-translate-over-repeat.base.argb32.ref.png b/test/reference/composite-integer-translate-over-repeat.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..c425749
Binary files /dev/null and b/test/reference/composite-integer-translate-over-repeat.base.argb32.ref.png differ
diff --git a/test/reference/composite-integer-translate-over-repeat.base.rgb24.ref.png b/test/reference/composite-integer-translate-over-repeat.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..c425749
Binary files /dev/null and b/test/reference/composite-integer-translate-over-repeat.base.rgb24.ref.png differ
diff --git a/test/reference/composite-integer-translate-over-repeat.ps2.ref.png b/test/reference/composite-integer-translate-over-repeat.ps2.ref.png
new file mode 100644 (file)
index 0000000..f0e7b84
Binary files /dev/null and b/test/reference/composite-integer-translate-over-repeat.ps2.ref.png differ
diff --git a/test/reference/composite-integer-translate-over-repeat.ps3.ref.png b/test/reference/composite-integer-translate-over-repeat.ps3.ref.png
new file mode 100644 (file)
index 0000000..f0e7b84
Binary files /dev/null and b/test/reference/composite-integer-translate-over-repeat.ps3.ref.png differ
diff --git a/test/reference/composite-integer-translate-over-repeat.ref.png b/test/reference/composite-integer-translate-over-repeat.ref.png
new file mode 100644 (file)
index 0000000..c04db26
Binary files /dev/null and b/test/reference/composite-integer-translate-over-repeat.ref.png differ
diff --git a/test/reference/composite-integer-translate-over-repeat.traps.argb32.ref.png b/test/reference/composite-integer-translate-over-repeat.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..c425749
Binary files /dev/null and b/test/reference/composite-integer-translate-over-repeat.traps.argb32.ref.png differ
diff --git a/test/reference/composite-integer-translate-over-repeat.traps.rgb24.ref.png b/test/reference/composite-integer-translate-over-repeat.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..c425749
Binary files /dev/null and b/test/reference/composite-integer-translate-over-repeat.traps.rgb24.ref.png differ
diff --git a/test/reference/composite-integer-translate-over.base.argb32.ref.png b/test/reference/composite-integer-translate-over.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..7ac3ddf
Binary files /dev/null and b/test/reference/composite-integer-translate-over.base.argb32.ref.png differ
diff --git a/test/reference/composite-integer-translate-over.base.rgb24.ref.png b/test/reference/composite-integer-translate-over.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..7ac3ddf
Binary files /dev/null and b/test/reference/composite-integer-translate-over.base.rgb24.ref.png differ
diff --git a/test/reference/composite-integer-translate-over.image16.ref.png b/test/reference/composite-integer-translate-over.image16.ref.png
new file mode 100644 (file)
index 0000000..acb0917
Binary files /dev/null and b/test/reference/composite-integer-translate-over.image16.ref.png differ
diff --git a/test/reference/composite-integer-translate-over.ps2.ref.png b/test/reference/composite-integer-translate-over.ps2.ref.png
new file mode 100644 (file)
index 0000000..8c8cc0d
Binary files /dev/null and b/test/reference/composite-integer-translate-over.ps2.ref.png differ
diff --git a/test/reference/composite-integer-translate-over.ps3.ref.png b/test/reference/composite-integer-translate-over.ps3.ref.png
new file mode 100644 (file)
index 0000000..8c8cc0d
Binary files /dev/null and b/test/reference/composite-integer-translate-over.ps3.ref.png differ
diff --git a/test/reference/composite-integer-translate-over.ref.png b/test/reference/composite-integer-translate-over.ref.png
new file mode 100644 (file)
index 0000000..630d993
Binary files /dev/null and b/test/reference/composite-integer-translate-over.ref.png differ
diff --git a/test/reference/composite-integer-translate-over.traps.argb32.ref.png b/test/reference/composite-integer-translate-over.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..7ac3ddf
Binary files /dev/null and b/test/reference/composite-integer-translate-over.traps.argb32.ref.png differ
diff --git a/test/reference/composite-integer-translate-over.traps.rgb24.ref.png b/test/reference/composite-integer-translate-over.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..7ac3ddf
Binary files /dev/null and b/test/reference/composite-integer-translate-over.traps.rgb24.ref.png differ
diff --git a/test/reference/composite-integer-translate-source.base.argb32.ref.png b/test/reference/composite-integer-translate-source.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..7ac3ddf
Binary files /dev/null and b/test/reference/composite-integer-translate-source.base.argb32.ref.png differ
diff --git a/test/reference/composite-integer-translate-source.base.rgb24.ref.png b/test/reference/composite-integer-translate-source.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..7ac3ddf
Binary files /dev/null and b/test/reference/composite-integer-translate-source.base.rgb24.ref.png differ
diff --git a/test/reference/composite-integer-translate-source.image16.ref.png b/test/reference/composite-integer-translate-source.image16.ref.png
new file mode 100644 (file)
index 0000000..acb0917
Binary files /dev/null and b/test/reference/composite-integer-translate-source.image16.ref.png differ
diff --git a/test/reference/composite-integer-translate-source.ps2.ref.png b/test/reference/composite-integer-translate-source.ps2.ref.png
new file mode 100644 (file)
index 0000000..8c8cc0d
Binary files /dev/null and b/test/reference/composite-integer-translate-source.ps2.ref.png differ
diff --git a/test/reference/composite-integer-translate-source.ps3.ref.png b/test/reference/composite-integer-translate-source.ps3.ref.png
new file mode 100644 (file)
index 0000000..8c8cc0d
Binary files /dev/null and b/test/reference/composite-integer-translate-source.ps3.ref.png differ
diff --git a/test/reference/composite-integer-translate-source.ref.png b/test/reference/composite-integer-translate-source.ref.png
new file mode 100644 (file)
index 0000000..da9a398
Binary files /dev/null and b/test/reference/composite-integer-translate-source.ref.png differ
diff --git a/test/reference/composite-integer-translate-source.svg12.argb32.xfail.png b/test/reference/composite-integer-translate-source.svg12.argb32.xfail.png
new file mode 100644 (file)
index 0000000..c4f3197
Binary files /dev/null and b/test/reference/composite-integer-translate-source.svg12.argb32.xfail.png differ
diff --git a/test/reference/composite-integer-translate-source.svg12.rgb24.xfail.png b/test/reference/composite-integer-translate-source.svg12.rgb24.xfail.png
new file mode 100644 (file)
index 0000000..c4f3197
Binary files /dev/null and b/test/reference/composite-integer-translate-source.svg12.rgb24.xfail.png differ
diff --git a/test/reference/composite-integer-translate-source.traps.argb32.ref.png b/test/reference/composite-integer-translate-source.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..7ac3ddf
Binary files /dev/null and b/test/reference/composite-integer-translate-source.traps.argb32.ref.png differ
diff --git a/test/reference/composite-integer-translate-source.traps.rgb24.ref.png b/test/reference/composite-integer-translate-source.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..7ac3ddf
Binary files /dev/null and b/test/reference/composite-integer-translate-source.traps.rgb24.ref.png differ
diff --git a/test/reference/copy-path.base.argb32.ref.png b/test/reference/copy-path.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..6ca6f48
Binary files /dev/null and b/test/reference/copy-path.base.argb32.ref.png differ
diff --git a/test/reference/copy-path.base.rgb24.ref.png b/test/reference/copy-path.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..6ca6f48
Binary files /dev/null and b/test/reference/copy-path.base.rgb24.ref.png differ
diff --git a/test/reference/copy-path.image16.ref.png b/test/reference/copy-path.image16.ref.png
new file mode 100644 (file)
index 0000000..1b3cabc
Binary files /dev/null and b/test/reference/copy-path.image16.ref.png differ
diff --git a/test/reference/copy-path.ps.ref.png b/test/reference/copy-path.ps.ref.png
new file mode 100644 (file)
index 0000000..41423a0
Binary files /dev/null and b/test/reference/copy-path.ps.ref.png differ
diff --git a/test/reference/copy-path.ref.png b/test/reference/copy-path.ref.png
new file mode 100644 (file)
index 0000000..588d56c
Binary files /dev/null and b/test/reference/copy-path.ref.png differ
diff --git a/test/reference/copy-path.traps.argb32.ref.png b/test/reference/copy-path.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..6ca6f48
Binary files /dev/null and b/test/reference/copy-path.traps.argb32.ref.png differ
diff --git a/test/reference/copy-path.traps.rgb24.ref.png b/test/reference/copy-path.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..6ca6f48
Binary files /dev/null and b/test/reference/copy-path.traps.rgb24.ref.png differ
diff --git a/test/reference/coverage-column-triangles.ref.png b/test/reference/coverage-column-triangles.ref.png
new file mode 100644 (file)
index 0000000..aa61031
Binary files /dev/null and b/test/reference/coverage-column-triangles.ref.png differ
diff --git a/test/reference/coverage-column-triangles.xlib.xfail.png b/test/reference/coverage-column-triangles.xlib.xfail.png
new file mode 100644 (file)
index 0000000..f433b7a
Binary files /dev/null and b/test/reference/coverage-column-triangles.xlib.xfail.png differ
diff --git a/test/reference/coverage-intersecting-quads.ref.png b/test/reference/coverage-intersecting-quads.ref.png
new file mode 100644 (file)
index 0000000..f56cb55
Binary files /dev/null and b/test/reference/coverage-intersecting-quads.ref.png differ
diff --git a/test/reference/coverage-intersecting-quads.xlib.xfail.png b/test/reference/coverage-intersecting-quads.xlib.xfail.png
new file mode 100644 (file)
index 0000000..d6b8c2e
Binary files /dev/null and b/test/reference/coverage-intersecting-quads.xlib.xfail.png differ
diff --git a/test/reference/coverage-intersecting-triangles.ref.png b/test/reference/coverage-intersecting-triangles.ref.png
new file mode 100644 (file)
index 0000000..40a48c1
Binary files /dev/null and b/test/reference/coverage-intersecting-triangles.ref.png differ
diff --git a/test/reference/coverage-intersecting-triangles.xlib.xfail.png b/test/reference/coverage-intersecting-triangles.xlib.xfail.png
new file mode 100644 (file)
index 0000000..e64cd47
Binary files /dev/null and b/test/reference/coverage-intersecting-triangles.xlib.xfail.png differ
diff --git a/test/reference/coverage-rectangles.ref.png b/test/reference/coverage-rectangles.ref.png
new file mode 100644 (file)
index 0000000..cc1d31c
Binary files /dev/null and b/test/reference/coverage-rectangles.ref.png differ
diff --git a/test/reference/coverage-rectangles.xlib.xfail.png b/test/reference/coverage-rectangles.xlib.xfail.png
new file mode 100644 (file)
index 0000000..622c2d7
Binary files /dev/null and b/test/reference/coverage-rectangles.xlib.xfail.png differ
diff --git a/test/reference/coverage-row-triangles.ref.png b/test/reference/coverage-row-triangles.ref.png
new file mode 100644 (file)
index 0000000..aa61031
Binary files /dev/null and b/test/reference/coverage-row-triangles.ref.png differ
diff --git a/test/reference/coverage-row-triangles.xlib.xfail.png b/test/reference/coverage-row-triangles.xlib.xfail.png
new file mode 100644 (file)
index 0000000..f8582fb
Binary files /dev/null and b/test/reference/coverage-row-triangles.xlib.xfail.png differ
diff --git a/test/reference/coverage-triangles.ref.png b/test/reference/coverage-triangles.ref.png
new file mode 100644 (file)
index 0000000..f56cb55
Binary files /dev/null and b/test/reference/coverage-triangles.ref.png differ
diff --git a/test/reference/coverage-triangles.xlib.xfail.png b/test/reference/coverage-triangles.xlib.xfail.png
new file mode 100644 (file)
index 0000000..133fd33
Binary files /dev/null and b/test/reference/coverage-triangles.xlib.xfail.png differ
diff --git a/test/reference/create-from-png-stream.base.argb32.ref.png b/test/reference/create-from-png-stream.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..0a145d9
Binary files /dev/null and b/test/reference/create-from-png-stream.base.argb32.ref.png differ
diff --git a/test/reference/create-from-png-stream.base.rgb24.ref.png b/test/reference/create-from-png-stream.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..0a145d9
Binary files /dev/null and b/test/reference/create-from-png-stream.base.rgb24.ref.png differ
diff --git a/test/reference/create-from-png-stream.ref.png b/test/reference/create-from-png-stream.ref.png
new file mode 100644 (file)
index 0000000..765adc4
Binary files /dev/null and b/test/reference/create-from-png-stream.ref.png differ
diff --git a/test/reference/create-from-png-stream.traps.argb32.ref.png b/test/reference/create-from-png-stream.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..0a145d9
Binary files /dev/null and b/test/reference/create-from-png-stream.traps.argb32.ref.png differ
diff --git a/test/reference/create-from-png-stream.traps.rgb24.ref.png b/test/reference/create-from-png-stream.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..0a145d9
Binary files /dev/null and b/test/reference/create-from-png-stream.traps.rgb24.ref.png differ
diff --git a/test/reference/create-from-png.alpha.ref.png b/test/reference/create-from-png.alpha.ref.png
new file mode 100644 (file)
index 0000000..a5175a1
Binary files /dev/null and b/test/reference/create-from-png.alpha.ref.png differ
diff --git a/test/reference/create-from-png.base.argb32.ref.png b/test/reference/create-from-png.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..0a145d9
Binary files /dev/null and b/test/reference/create-from-png.base.argb32.ref.png differ
diff --git a/test/reference/create-from-png.base.rgb24.ref.png b/test/reference/create-from-png.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..0a145d9
Binary files /dev/null and b/test/reference/create-from-png.base.rgb24.ref.png differ
diff --git a/test/reference/create-from-png.gray-alpha.ref.png b/test/reference/create-from-png.gray-alpha.ref.png
new file mode 100644 (file)
index 0000000..f5d47dc
Binary files /dev/null and b/test/reference/create-from-png.gray-alpha.ref.png differ
diff --git a/test/reference/create-from-png.gray.ref.png b/test/reference/create-from-png.gray.ref.png
new file mode 100644 (file)
index 0000000..12dc90b
Binary files /dev/null and b/test/reference/create-from-png.gray.ref.png differ
diff --git a/test/reference/create-from-png.indexed-alpha.ref.png b/test/reference/create-from-png.indexed-alpha.ref.png
new file mode 100644 (file)
index 0000000..9f32c69
Binary files /dev/null and b/test/reference/create-from-png.indexed-alpha.ref.png differ
diff --git a/test/reference/create-from-png.indexed.ref.png b/test/reference/create-from-png.indexed.ref.png
new file mode 100644 (file)
index 0000000..6b1d713
Binary files /dev/null and b/test/reference/create-from-png.indexed.ref.png differ
diff --git a/test/reference/create-from-png.ref.png b/test/reference/create-from-png.ref.png
new file mode 100644 (file)
index 0000000..5753560
Binary files /dev/null and b/test/reference/create-from-png.ref.png differ
diff --git a/test/reference/culled-glyphs.base.argb32.ref.png b/test/reference/culled-glyphs.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..08aad33
Binary files /dev/null and b/test/reference/culled-glyphs.base.argb32.ref.png differ
diff --git a/test/reference/culled-glyphs.base.rgb24.ref.png b/test/reference/culled-glyphs.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..08aad33
Binary files /dev/null and b/test/reference/culled-glyphs.base.rgb24.ref.png differ
diff --git a/test/reference/culled-glyphs.image16.ref.png b/test/reference/culled-glyphs.image16.ref.png
new file mode 100644 (file)
index 0000000..724cb8f
Binary files /dev/null and b/test/reference/culled-glyphs.image16.ref.png differ
diff --git a/test/reference/culled-glyphs.ps.ref.png b/test/reference/culled-glyphs.ps.ref.png
new file mode 100644 (file)
index 0000000..f34fb95
Binary files /dev/null and b/test/reference/culled-glyphs.ps.ref.png differ
diff --git a/test/reference/culled-glyphs.quartz.ref.png b/test/reference/culled-glyphs.quartz.ref.png
new file mode 100644 (file)
index 0000000..1aa2342
Binary files /dev/null and b/test/reference/culled-glyphs.quartz.ref.png differ
diff --git a/test/reference/culled-glyphs.ref.png b/test/reference/culled-glyphs.ref.png
new file mode 100644 (file)
index 0000000..753ed46
Binary files /dev/null and b/test/reference/culled-glyphs.ref.png differ
diff --git a/test/reference/culled-glyphs.traps.argb32.ref.png b/test/reference/culled-glyphs.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..08aad33
Binary files /dev/null and b/test/reference/culled-glyphs.traps.argb32.ref.png differ
diff --git a/test/reference/culled-glyphs.traps.rgb24.ref.png b/test/reference/culled-glyphs.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..08aad33
Binary files /dev/null and b/test/reference/culled-glyphs.traps.rgb24.ref.png differ
diff --git a/test/reference/curve-to-as-line-to.base.argb32.ref.png b/test/reference/curve-to-as-line-to.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..d3cf32e
Binary files /dev/null and b/test/reference/curve-to-as-line-to.base.argb32.ref.png differ
diff --git a/test/reference/curve-to-as-line-to.base.rgb24.ref.png b/test/reference/curve-to-as-line-to.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..d3cf32e
Binary files /dev/null and b/test/reference/curve-to-as-line-to.base.rgb24.ref.png differ
diff --git a/test/reference/curve-to-as-line-to.ps.xfail.png b/test/reference/curve-to-as-line-to.ps.xfail.png
new file mode 100644 (file)
index 0000000..3f31058
Binary files /dev/null and b/test/reference/curve-to-as-line-to.ps.xfail.png differ
diff --git a/test/reference/curve-to-as-line-to.ref.png b/test/reference/curve-to-as-line-to.ref.png
new file mode 100644 (file)
index 0000000..15589db
Binary files /dev/null and b/test/reference/curve-to-as-line-to.ref.png differ
diff --git a/test/reference/curve-to-as-line-to.traps.argb32.ref.png b/test/reference/curve-to-as-line-to.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..d3cf32e
Binary files /dev/null and b/test/reference/curve-to-as-line-to.traps.argb32.ref.png differ
diff --git a/test/reference/curve-to-as-line-to.traps.rgb24.ref.png b/test/reference/curve-to-as-line-to.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..d3cf32e
Binary files /dev/null and b/test/reference/curve-to-as-line-to.traps.rgb24.ref.png differ
diff --git a/test/reference/dash-caps-joins.base.argb32.ref.png b/test/reference/dash-caps-joins.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..94f72e3
Binary files /dev/null and b/test/reference/dash-caps-joins.base.argb32.ref.png differ
diff --git a/test/reference/dash-caps-joins.base.rgb24.ref.png b/test/reference/dash-caps-joins.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..94f72e3
Binary files /dev/null and b/test/reference/dash-caps-joins.base.rgb24.ref.png differ
diff --git a/test/reference/dash-caps-joins.image16.ref.png b/test/reference/dash-caps-joins.image16.ref.png
new file mode 100644 (file)
index 0000000..1f8d048
Binary files /dev/null and b/test/reference/dash-caps-joins.image16.ref.png differ
diff --git a/test/reference/dash-caps-joins.ps.ref.png b/test/reference/dash-caps-joins.ps.ref.png
new file mode 100644 (file)
index 0000000..466bc62
Binary files /dev/null and b/test/reference/dash-caps-joins.ps.ref.png differ
diff --git a/test/reference/dash-caps-joins.quartz.xfail.png b/test/reference/dash-caps-joins.quartz.xfail.png
new file mode 100644 (file)
index 0000000..0dc5433
Binary files /dev/null and b/test/reference/dash-caps-joins.quartz.xfail.png differ
diff --git a/test/reference/dash-caps-joins.ref.png b/test/reference/dash-caps-joins.ref.png
new file mode 100644 (file)
index 0000000..db8822a
Binary files /dev/null and b/test/reference/dash-caps-joins.ref.png differ
diff --git a/test/reference/dash-caps-joins.traps.argb32.ref.png b/test/reference/dash-caps-joins.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..94f72e3
Binary files /dev/null and b/test/reference/dash-caps-joins.traps.argb32.ref.png differ
diff --git a/test/reference/dash-caps-joins.traps.rgb24.ref.png b/test/reference/dash-caps-joins.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..94f72e3
Binary files /dev/null and b/test/reference/dash-caps-joins.traps.rgb24.ref.png differ
diff --git a/test/reference/dash-curve.base.argb32.ref.png b/test/reference/dash-curve.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..53976b7
Binary files /dev/null and b/test/reference/dash-curve.base.argb32.ref.png differ
diff --git a/test/reference/dash-curve.base.rgb24.ref.png b/test/reference/dash-curve.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..53976b7
Binary files /dev/null and b/test/reference/dash-curve.base.rgb24.ref.png differ
diff --git a/test/reference/dash-curve.image16.ref.png b/test/reference/dash-curve.image16.ref.png
new file mode 100644 (file)
index 0000000..d89ceda
Binary files /dev/null and b/test/reference/dash-curve.image16.ref.png differ
diff --git a/test/reference/dash-curve.ps2.ref.png b/test/reference/dash-curve.ps2.ref.png
new file mode 100644 (file)
index 0000000..ffb402f
Binary files /dev/null and b/test/reference/dash-curve.ps2.ref.png differ
diff --git a/test/reference/dash-curve.ps3.ref.png b/test/reference/dash-curve.ps3.ref.png
new file mode 100644 (file)
index 0000000..ffb402f
Binary files /dev/null and b/test/reference/dash-curve.ps3.ref.png differ
diff --git a/test/reference/dash-curve.quartz.xfail.png b/test/reference/dash-curve.quartz.xfail.png
new file mode 100644 (file)
index 0000000..b68d7a7
Binary files /dev/null and b/test/reference/dash-curve.quartz.xfail.png differ
diff --git a/test/reference/dash-curve.ref.png b/test/reference/dash-curve.ref.png
new file mode 100644 (file)
index 0000000..b98c118
Binary files /dev/null and b/test/reference/dash-curve.ref.png differ
diff --git a/test/reference/dash-curve.traps.argb32.ref.png b/test/reference/dash-curve.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..53976b7
Binary files /dev/null and b/test/reference/dash-curve.traps.argb32.ref.png differ
diff --git a/test/reference/dash-curve.traps.rgb24.ref.png b/test/reference/dash-curve.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..53976b7
Binary files /dev/null and b/test/reference/dash-curve.traps.rgb24.ref.png differ
diff --git a/test/reference/dash-infinite-loop.base.argb32.ref.png b/test/reference/dash-infinite-loop.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..a12aa94
Binary files /dev/null and b/test/reference/dash-infinite-loop.base.argb32.ref.png differ
diff --git a/test/reference/dash-infinite-loop.base.rgb24.ref.png b/test/reference/dash-infinite-loop.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..a12aa94
Binary files /dev/null and b/test/reference/dash-infinite-loop.base.rgb24.ref.png differ
diff --git a/test/reference/dash-infinite-loop.ps.ref.png b/test/reference/dash-infinite-loop.ps.ref.png
new file mode 100644 (file)
index 0000000..bab313a
Binary files /dev/null and b/test/reference/dash-infinite-loop.ps.ref.png differ
diff --git a/test/reference/dash-infinite-loop.ref.png b/test/reference/dash-infinite-loop.ref.png
new file mode 100644 (file)
index 0000000..4a44e22
Binary files /dev/null and b/test/reference/dash-infinite-loop.ref.png differ
diff --git a/test/reference/dash-infinite-loop.traps.argb32.ref.png b/test/reference/dash-infinite-loop.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..a12aa94
Binary files /dev/null and b/test/reference/dash-infinite-loop.traps.argb32.ref.png differ
diff --git a/test/reference/dash-infinite-loop.traps.rgb24.ref.png b/test/reference/dash-infinite-loop.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..a12aa94
Binary files /dev/null and b/test/reference/dash-infinite-loop.traps.rgb24.ref.png differ
diff --git a/test/reference/dash-no-dash.base.argb32.ref.png b/test/reference/dash-no-dash.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..c150a4f
Binary files /dev/null and b/test/reference/dash-no-dash.base.argb32.ref.png differ
diff --git a/test/reference/dash-no-dash.base.rgb24.ref.png b/test/reference/dash-no-dash.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..c150a4f
Binary files /dev/null and b/test/reference/dash-no-dash.base.rgb24.ref.png differ
diff --git a/test/reference/dash-no-dash.ref.png b/test/reference/dash-no-dash.ref.png
new file mode 100644 (file)
index 0000000..9afd045
Binary files /dev/null and b/test/reference/dash-no-dash.ref.png differ
diff --git a/test/reference/dash-no-dash.traps.argb32.ref.png b/test/reference/dash-no-dash.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..c150a4f
Binary files /dev/null and b/test/reference/dash-no-dash.traps.argb32.ref.png differ
diff --git a/test/reference/dash-no-dash.traps.rgb24.ref.png b/test/reference/dash-no-dash.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..c150a4f
Binary files /dev/null and b/test/reference/dash-no-dash.traps.rgb24.ref.png differ
diff --git a/test/reference/dash-offset-negative.base.argb32.ref.png b/test/reference/dash-offset-negative.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..77b9291
Binary files /dev/null and b/test/reference/dash-offset-negative.base.argb32.ref.png differ
diff --git a/test/reference/dash-offset-negative.base.rgb24.ref.png b/test/reference/dash-offset-negative.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..77b9291
Binary files /dev/null and b/test/reference/dash-offset-negative.base.rgb24.ref.png differ
diff --git a/test/reference/dash-offset-negative.pdf.ref.png b/test/reference/dash-offset-negative.pdf.ref.png
new file mode 100644 (file)
index 0000000..df22d08
Binary files /dev/null and b/test/reference/dash-offset-negative.pdf.ref.png differ
diff --git a/test/reference/dash-offset-negative.ref.png b/test/reference/dash-offset-negative.ref.png
new file mode 100644 (file)
index 0000000..f2b8007
Binary files /dev/null and b/test/reference/dash-offset-negative.ref.png differ
diff --git a/test/reference/dash-offset-negative.traps.argb32.ref.png b/test/reference/dash-offset-negative.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..77b9291
Binary files /dev/null and b/test/reference/dash-offset-negative.traps.argb32.ref.png differ
diff --git a/test/reference/dash-offset-negative.traps.rgb24.ref.png b/test/reference/dash-offset-negative.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..77b9291
Binary files /dev/null and b/test/reference/dash-offset-negative.traps.rgb24.ref.png differ
diff --git a/test/reference/dash-offset.base.argb32.ref.png b/test/reference/dash-offset.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..52600c4
Binary files /dev/null and b/test/reference/dash-offset.base.argb32.ref.png differ
diff --git a/test/reference/dash-offset.base.rgb24.ref.png b/test/reference/dash-offset.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..52600c4
Binary files /dev/null and b/test/reference/dash-offset.base.rgb24.ref.png differ
diff --git a/test/reference/dash-offset.ref.png b/test/reference/dash-offset.ref.png
new file mode 100644 (file)
index 0000000..52600c4
Binary files /dev/null and b/test/reference/dash-offset.ref.png differ
diff --git a/test/reference/dash-offset.traps.argb32.ref.png b/test/reference/dash-offset.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..52600c4
Binary files /dev/null and b/test/reference/dash-offset.traps.argb32.ref.png differ
diff --git a/test/reference/dash-offset.traps.rgb24.ref.png b/test/reference/dash-offset.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..52600c4
Binary files /dev/null and b/test/reference/dash-offset.traps.rgb24.ref.png differ
diff --git a/test/reference/dash-scale.base.argb32.ref.png b/test/reference/dash-scale.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..46fe785
Binary files /dev/null and b/test/reference/dash-scale.base.argb32.ref.png differ
diff --git a/test/reference/dash-scale.base.rgb24.ref.png b/test/reference/dash-scale.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..46fe785
Binary files /dev/null and b/test/reference/dash-scale.base.rgb24.ref.png differ
diff --git a/test/reference/dash-scale.image16.ref.png b/test/reference/dash-scale.image16.ref.png
new file mode 100644 (file)
index 0000000..2b4fca5
Binary files /dev/null and b/test/reference/dash-scale.image16.ref.png differ
diff --git a/test/reference/dash-scale.ps.ref.png b/test/reference/dash-scale.ps.ref.png
new file mode 100644 (file)
index 0000000..f8c2527
Binary files /dev/null and b/test/reference/dash-scale.ps.ref.png differ
diff --git a/test/reference/dash-scale.quartz.ref.png b/test/reference/dash-scale.quartz.ref.png
new file mode 100644 (file)
index 0000000..62ccdf5
Binary files /dev/null and b/test/reference/dash-scale.quartz.ref.png differ
diff --git a/test/reference/dash-scale.ref.png b/test/reference/dash-scale.ref.png
new file mode 100644 (file)
index 0000000..e2a2538
Binary files /dev/null and b/test/reference/dash-scale.ref.png differ
diff --git a/test/reference/dash-scale.traps.argb32.ref.png b/test/reference/dash-scale.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..46fe785
Binary files /dev/null and b/test/reference/dash-scale.traps.argb32.ref.png differ
diff --git a/test/reference/dash-scale.traps.rgb24.ref.png b/test/reference/dash-scale.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..46fe785
Binary files /dev/null and b/test/reference/dash-scale.traps.rgb24.ref.png differ
diff --git a/test/reference/dash-state.base.argb32.ref.png b/test/reference/dash-state.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..04c2eb0
Binary files /dev/null and b/test/reference/dash-state.base.argb32.ref.png differ
diff --git a/test/reference/dash-state.base.rgb24.ref.png b/test/reference/dash-state.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..04c2eb0
Binary files /dev/null and b/test/reference/dash-state.base.rgb24.ref.png differ
diff --git a/test/reference/dash-state.image16.ref.png b/test/reference/dash-state.image16.ref.png
new file mode 100644 (file)
index 0000000..07c77da
Binary files /dev/null and b/test/reference/dash-state.image16.ref.png differ
diff --git a/test/reference/dash-state.ps2.ref.png b/test/reference/dash-state.ps2.ref.png
new file mode 100644 (file)
index 0000000..88e208c
Binary files /dev/null and b/test/reference/dash-state.ps2.ref.png differ
diff --git a/test/reference/dash-state.ps3.ref.png b/test/reference/dash-state.ps3.ref.png
new file mode 100644 (file)
index 0000000..88e208c
Binary files /dev/null and b/test/reference/dash-state.ps3.ref.png differ
diff --git a/test/reference/dash-state.quartz.xfail.png b/test/reference/dash-state.quartz.xfail.png
new file mode 100644 (file)
index 0000000..993498c
Binary files /dev/null and b/test/reference/dash-state.quartz.xfail.png differ
diff --git a/test/reference/dash-state.ref.png b/test/reference/dash-state.ref.png
new file mode 100644 (file)
index 0000000..06c8bbc
Binary files /dev/null and b/test/reference/dash-state.ref.png differ
diff --git a/test/reference/dash-state.traps.argb32.ref.png b/test/reference/dash-state.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..04c2eb0
Binary files /dev/null and b/test/reference/dash-state.traps.argb32.ref.png differ
diff --git a/test/reference/dash-state.traps.rgb24.ref.png b/test/reference/dash-state.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..04c2eb0
Binary files /dev/null and b/test/reference/dash-state.traps.rgb24.ref.png differ
diff --git a/test/reference/dash-zero-length.base.argb32.ref.png b/test/reference/dash-zero-length.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..3ed0c85
Binary files /dev/null and b/test/reference/dash-zero-length.base.argb32.ref.png differ
diff --git a/test/reference/dash-zero-length.base.rgb24.ref.png b/test/reference/dash-zero-length.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..0aae369
Binary files /dev/null and b/test/reference/dash-zero-length.base.rgb24.ref.png differ
diff --git a/test/reference/dash-zero-length.ps2.ref.png b/test/reference/dash-zero-length.ps2.ref.png
new file mode 100644 (file)
index 0000000..68fd3fb
Binary files /dev/null and b/test/reference/dash-zero-length.ps2.ref.png differ
diff --git a/test/reference/dash-zero-length.ps2.rgb24.ref.png b/test/reference/dash-zero-length.ps2.rgb24.ref.png
new file mode 100644 (file)
index 0000000..8a4a40f
Binary files /dev/null and b/test/reference/dash-zero-length.ps2.rgb24.ref.png differ
diff --git a/test/reference/dash-zero-length.ps3.ref.png b/test/reference/dash-zero-length.ps3.ref.png
new file mode 100644 (file)
index 0000000..68fd3fb
Binary files /dev/null and b/test/reference/dash-zero-length.ps3.ref.png differ
diff --git a/test/reference/dash-zero-length.ps3.rgb24.ref.png b/test/reference/dash-zero-length.ps3.rgb24.ref.png
new file mode 100644 (file)
index 0000000..8a4a40f
Binary files /dev/null and b/test/reference/dash-zero-length.ps3.rgb24.ref.png differ
diff --git a/test/reference/dash-zero-length.ref.png b/test/reference/dash-zero-length.ref.png
new file mode 100644 (file)
index 0000000..9ddd40f
Binary files /dev/null and b/test/reference/dash-zero-length.ref.png differ
diff --git a/test/reference/dash-zero-length.rgb24.ref.png b/test/reference/dash-zero-length.rgb24.ref.png
new file mode 100644 (file)
index 0000000..74d5df2
Binary files /dev/null and b/test/reference/dash-zero-length.rgb24.ref.png differ
diff --git a/test/reference/dash-zero-length.traps.argb32.ref.png b/test/reference/dash-zero-length.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..3ed0c85
Binary files /dev/null and b/test/reference/dash-zero-length.traps.argb32.ref.png differ
diff --git a/test/reference/dash-zero-length.traps.rgb24.ref.png b/test/reference/dash-zero-length.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..0aae369
Binary files /dev/null and b/test/reference/dash-zero-length.traps.rgb24.ref.png differ
diff --git a/test/reference/degenerate-arc.base.argb32.ref.png b/test/reference/degenerate-arc.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..73d41af
Binary files /dev/null and b/test/reference/degenerate-arc.base.argb32.ref.png differ
diff --git a/test/reference/degenerate-arc.base.rgb24.ref.png b/test/reference/degenerate-arc.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..73d41af
Binary files /dev/null and b/test/reference/degenerate-arc.base.rgb24.ref.png differ
diff --git a/test/reference/degenerate-arc.image16.ref.png b/test/reference/degenerate-arc.image16.ref.png
new file mode 100644 (file)
index 0000000..690e4a4
Binary files /dev/null and b/test/reference/degenerate-arc.image16.ref.png differ
diff --git a/test/reference/degenerate-arc.ps2.ref.png b/test/reference/degenerate-arc.ps2.ref.png
new file mode 100644 (file)
index 0000000..f6d9134
Binary files /dev/null and b/test/reference/degenerate-arc.ps2.ref.png differ
diff --git a/test/reference/degenerate-arc.ps3.ref.png b/test/reference/degenerate-arc.ps3.ref.png
new file mode 100644 (file)
index 0000000..f6d9134
Binary files /dev/null and b/test/reference/degenerate-arc.ps3.ref.png differ
diff --git a/test/reference/degenerate-arc.quartz.ref.png b/test/reference/degenerate-arc.quartz.ref.png
new file mode 100644 (file)
index 0000000..a43f6c9
Binary files /dev/null and b/test/reference/degenerate-arc.quartz.ref.png differ
diff --git a/test/reference/degenerate-arc.ref.png b/test/reference/degenerate-arc.ref.png
new file mode 100644 (file)
index 0000000..6bcfb8b
Binary files /dev/null and b/test/reference/degenerate-arc.ref.png differ
diff --git a/test/reference/degenerate-arc.traps.argb32.ref.png b/test/reference/degenerate-arc.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..73d41af
Binary files /dev/null and b/test/reference/degenerate-arc.traps.argb32.ref.png differ
diff --git a/test/reference/degenerate-arc.traps.rgb24.ref.png b/test/reference/degenerate-arc.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..73d41af
Binary files /dev/null and b/test/reference/degenerate-arc.traps.rgb24.ref.png differ
diff --git a/test/reference/degenerate-arcs.base.argb32.ref.png b/test/reference/degenerate-arcs.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..fc1869d
Binary files /dev/null and b/test/reference/degenerate-arcs.base.argb32.ref.png differ
diff --git a/test/reference/degenerate-arcs.base.rgb24.ref.png b/test/reference/degenerate-arcs.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..fc1869d
Binary files /dev/null and b/test/reference/degenerate-arcs.base.rgb24.ref.png differ
diff --git a/test/reference/degenerate-arcs.image16.ref.png b/test/reference/degenerate-arcs.image16.ref.png
new file mode 100644 (file)
index 0000000..595cb7b
Binary files /dev/null and b/test/reference/degenerate-arcs.image16.ref.png differ
diff --git a/test/reference/degenerate-arcs.ref.png b/test/reference/degenerate-arcs.ref.png
new file mode 100644 (file)
index 0000000..fc1869d
Binary files /dev/null and b/test/reference/degenerate-arcs.ref.png differ
diff --git a/test/reference/degenerate-arcs.traps.argb32.ref.png b/test/reference/degenerate-arcs.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..fc1869d
Binary files /dev/null and b/test/reference/degenerate-arcs.traps.argb32.ref.png differ
diff --git a/test/reference/degenerate-arcs.traps.rgb24.ref.png b/test/reference/degenerate-arcs.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..fc1869d
Binary files /dev/null and b/test/reference/degenerate-arcs.traps.rgb24.ref.png differ
diff --git a/test/reference/degenerate-curve-to.base.argb32.ref.png b/test/reference/degenerate-curve-to.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..fc26b00
Binary files /dev/null and b/test/reference/degenerate-curve-to.base.argb32.ref.png differ
diff --git a/test/reference/degenerate-curve-to.base.rgb24.ref.png b/test/reference/degenerate-curve-to.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..fc26b00
Binary files /dev/null and b/test/reference/degenerate-curve-to.base.rgb24.ref.png differ
diff --git a/test/reference/degenerate-curve-to.image16.ref.png b/test/reference/degenerate-curve-to.image16.ref.png
new file mode 100644 (file)
index 0000000..8036d0f
Binary files /dev/null and b/test/reference/degenerate-curve-to.image16.ref.png differ
diff --git a/test/reference/degenerate-curve-to.ps.xfail.png b/test/reference/degenerate-curve-to.ps.xfail.png
new file mode 100644 (file)
index 0000000..1c07965
Binary files /dev/null and b/test/reference/degenerate-curve-to.ps.xfail.png differ
diff --git a/test/reference/degenerate-curve-to.quartz.ref.png b/test/reference/degenerate-curve-to.quartz.ref.png
new file mode 100644 (file)
index 0000000..0448343
Binary files /dev/null and b/test/reference/degenerate-curve-to.quartz.ref.png differ
diff --git a/test/reference/degenerate-curve-to.ref.png b/test/reference/degenerate-curve-to.ref.png
new file mode 100644 (file)
index 0000000..57a36c3
Binary files /dev/null and b/test/reference/degenerate-curve-to.ref.png differ
diff --git a/test/reference/degenerate-curve-to.traps.argb32.ref.png b/test/reference/degenerate-curve-to.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..fc26b00
Binary files /dev/null and b/test/reference/degenerate-curve-to.traps.argb32.ref.png differ
diff --git a/test/reference/degenerate-curve-to.traps.rgb24.ref.png b/test/reference/degenerate-curve-to.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..fc26b00
Binary files /dev/null and b/test/reference/degenerate-curve-to.traps.rgb24.ref.png differ
diff --git a/test/reference/degenerate-dash.base.argb32.ref.png b/test/reference/degenerate-dash.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..4f9a5a0
Binary files /dev/null and b/test/reference/degenerate-dash.base.argb32.ref.png differ
diff --git a/test/reference/degenerate-dash.base.rgb24.ref.png b/test/reference/degenerate-dash.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..4f9a5a0
Binary files /dev/null and b/test/reference/degenerate-dash.base.rgb24.ref.png differ
diff --git a/test/reference/degenerate-dash.ps.xfail.png b/test/reference/degenerate-dash.ps.xfail.png
new file mode 100644 (file)
index 0000000..d5f8884
Binary files /dev/null and b/test/reference/degenerate-dash.ps.xfail.png differ
diff --git a/test/reference/degenerate-dash.quartz.xfail.png b/test/reference/degenerate-dash.quartz.xfail.png
new file mode 100644 (file)
index 0000000..594437c
Binary files /dev/null and b/test/reference/degenerate-dash.quartz.xfail.png differ
diff --git a/test/reference/degenerate-dash.ref.png b/test/reference/degenerate-dash.ref.png
new file mode 100644 (file)
index 0000000..523eb1a
Binary files /dev/null and b/test/reference/degenerate-dash.ref.png differ
diff --git a/test/reference/degenerate-dash.traps.argb32.ref.png b/test/reference/degenerate-dash.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..4f9a5a0
Binary files /dev/null and b/test/reference/degenerate-dash.traps.argb32.ref.png differ
diff --git a/test/reference/degenerate-dash.traps.rgb24.ref.png b/test/reference/degenerate-dash.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..4f9a5a0
Binary files /dev/null and b/test/reference/degenerate-dash.traps.rgb24.ref.png differ
diff --git a/test/reference/degenerate-linear-gradient.base.argb32.ref.png b/test/reference/degenerate-linear-gradient.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..ceed48a
Binary files /dev/null and b/test/reference/degenerate-linear-gradient.base.argb32.ref.png differ
diff --git a/test/reference/degenerate-linear-gradient.base.rgb24.ref.png b/test/reference/degenerate-linear-gradient.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..ceed48a
Binary files /dev/null and b/test/reference/degenerate-linear-gradient.base.rgb24.ref.png differ
diff --git a/test/reference/degenerate-linear-gradient.ref.png b/test/reference/degenerate-linear-gradient.ref.png
new file mode 100644 (file)
index 0000000..1de7ca6
Binary files /dev/null and b/test/reference/degenerate-linear-gradient.ref.png differ
diff --git a/test/reference/degenerate-linear-gradient.traps.argb32.ref.png b/test/reference/degenerate-linear-gradient.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..ceed48a
Binary files /dev/null and b/test/reference/degenerate-linear-gradient.traps.argb32.ref.png differ
diff --git a/test/reference/degenerate-linear-gradient.traps.rgb24.ref.png b/test/reference/degenerate-linear-gradient.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..ceed48a
Binary files /dev/null and b/test/reference/degenerate-linear-gradient.traps.rgb24.ref.png differ
diff --git a/test/reference/degenerate-path.argb32.ref.png b/test/reference/degenerate-path.argb32.ref.png
new file mode 100644 (file)
index 0000000..5078b8a
Binary files /dev/null and b/test/reference/degenerate-path.argb32.ref.png differ
diff --git a/test/reference/degenerate-path.base.argb32.ref.png b/test/reference/degenerate-path.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..42363ea
Binary files /dev/null and b/test/reference/degenerate-path.base.argb32.ref.png differ
diff --git a/test/reference/degenerate-path.base.rgb24.ref.png b/test/reference/degenerate-path.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..45497d7
Binary files /dev/null and b/test/reference/degenerate-path.base.rgb24.ref.png differ
diff --git a/test/reference/degenerate-path.ps.argb32.xfail.png b/test/reference/degenerate-path.ps.argb32.xfail.png
new file mode 100644 (file)
index 0000000..33d713c
Binary files /dev/null and b/test/reference/degenerate-path.ps.argb32.xfail.png differ
diff --git a/test/reference/degenerate-path.ps.rgb24.xfail.png b/test/reference/degenerate-path.ps.rgb24.xfail.png
new file mode 100644 (file)
index 0000000..e73f314
Binary files /dev/null and b/test/reference/degenerate-path.ps.rgb24.xfail.png differ
diff --git a/test/reference/degenerate-path.quartz.argb32.xfail.png b/test/reference/degenerate-path.quartz.argb32.xfail.png
new file mode 100644 (file)
index 0000000..d655e27
Binary files /dev/null and b/test/reference/degenerate-path.quartz.argb32.xfail.png differ
diff --git a/test/reference/degenerate-path.quartz.rgb24.xfail.png b/test/reference/degenerate-path.quartz.rgb24.xfail.png
new file mode 100644 (file)
index 0000000..5256951
Binary files /dev/null and b/test/reference/degenerate-path.quartz.rgb24.xfail.png differ
diff --git a/test/reference/degenerate-path.rgb24.ref.png b/test/reference/degenerate-path.rgb24.ref.png
new file mode 100644 (file)
index 0000000..d25513a
Binary files /dev/null and b/test/reference/degenerate-path.rgb24.ref.png differ
diff --git a/test/reference/degenerate-path.traps.argb32.ref.png b/test/reference/degenerate-path.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..42363ea
Binary files /dev/null and b/test/reference/degenerate-path.traps.argb32.ref.png differ
diff --git a/test/reference/degenerate-path.traps.rgb24.ref.png b/test/reference/degenerate-path.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..45497d7
Binary files /dev/null and b/test/reference/degenerate-path.traps.rgb24.ref.png differ
diff --git a/test/reference/degenerate-pen.base.argb32.ref.png b/test/reference/degenerate-pen.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..103b858
Binary files /dev/null and b/test/reference/degenerate-pen.base.argb32.ref.png differ
diff --git a/test/reference/degenerate-pen.base.rgb24.ref.png b/test/reference/degenerate-pen.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..103b858
Binary files /dev/null and b/test/reference/degenerate-pen.base.rgb24.ref.png differ
diff --git a/test/reference/degenerate-pen.image16.ref.png b/test/reference/degenerate-pen.image16.ref.png
new file mode 100644 (file)
index 0000000..5501127
Binary files /dev/null and b/test/reference/degenerate-pen.image16.ref.png differ
diff --git a/test/reference/degenerate-pen.ps.ref.png b/test/reference/degenerate-pen.ps.ref.png
new file mode 100644 (file)
index 0000000..34d173b
Binary files /dev/null and b/test/reference/degenerate-pen.ps.ref.png differ
diff --git a/test/reference/degenerate-pen.ref.png b/test/reference/degenerate-pen.ref.png
new file mode 100644 (file)
index 0000000..0a28436
Binary files /dev/null and b/test/reference/degenerate-pen.ref.png differ
diff --git a/test/reference/degenerate-pen.traps.argb32.ref.png b/test/reference/degenerate-pen.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..103b858
Binary files /dev/null and b/test/reference/degenerate-pen.traps.argb32.ref.png differ
diff --git a/test/reference/degenerate-pen.traps.rgb24.ref.png b/test/reference/degenerate-pen.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..103b858
Binary files /dev/null and b/test/reference/degenerate-pen.traps.rgb24.ref.png differ
diff --git a/test/reference/degenerate-radial-gradient.base.argb32.ref.png b/test/reference/degenerate-radial-gradient.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..9a5213b
Binary files /dev/null and b/test/reference/degenerate-radial-gradient.base.argb32.ref.png differ
diff --git a/test/reference/degenerate-radial-gradient.base.rgb24.ref.png b/test/reference/degenerate-radial-gradient.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..9a5213b
Binary files /dev/null and b/test/reference/degenerate-radial-gradient.base.rgb24.ref.png differ
diff --git a/test/reference/degenerate-radial-gradient.ref.png b/test/reference/degenerate-radial-gradient.ref.png
new file mode 100644 (file)
index 0000000..9a5213b
Binary files /dev/null and b/test/reference/degenerate-radial-gradient.ref.png differ
diff --git a/test/reference/degenerate-radial-gradient.traps.argb32.ref.png b/test/reference/degenerate-radial-gradient.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..9a5213b
Binary files /dev/null and b/test/reference/degenerate-radial-gradient.traps.argb32.ref.png differ
diff --git a/test/reference/degenerate-radial-gradient.traps.rgb24.ref.png b/test/reference/degenerate-radial-gradient.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..9a5213b
Binary files /dev/null and b/test/reference/degenerate-radial-gradient.traps.rgb24.ref.png differ
diff --git a/test/reference/degenerate-rel-curve-to.base.argb32.ref.png b/test/reference/degenerate-rel-curve-to.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..73ad25c
Binary files /dev/null and b/test/reference/degenerate-rel-curve-to.base.argb32.ref.png differ
diff --git a/test/reference/degenerate-rel-curve-to.base.rgb24.ref.png b/test/reference/degenerate-rel-curve-to.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..73ad25c
Binary files /dev/null and b/test/reference/degenerate-rel-curve-to.base.rgb24.ref.png differ
diff --git a/test/reference/degenerate-rel-curve-to.image16.ref.png b/test/reference/degenerate-rel-curve-to.image16.ref.png
new file mode 100644 (file)
index 0000000..ece8943
Binary files /dev/null and b/test/reference/degenerate-rel-curve-to.image16.ref.png differ
diff --git a/test/reference/degenerate-rel-curve-to.ps.ref.png b/test/reference/degenerate-rel-curve-to.ps.ref.png
new file mode 100644 (file)
index 0000000..98a1fc9
Binary files /dev/null and b/test/reference/degenerate-rel-curve-to.ps.ref.png differ
diff --git a/test/reference/degenerate-rel-curve-to.quartz.ref.png b/test/reference/degenerate-rel-curve-to.quartz.ref.png
new file mode 100644 (file)
index 0000000..2d21e04
Binary files /dev/null and b/test/reference/degenerate-rel-curve-to.quartz.ref.png differ
diff --git a/test/reference/degenerate-rel-curve-to.ref.png b/test/reference/degenerate-rel-curve-to.ref.png
new file mode 100644 (file)
index 0000000..31b0eb4
Binary files /dev/null and b/test/reference/degenerate-rel-curve-to.ref.png differ
diff --git a/test/reference/degenerate-rel-curve-to.traps.argb32.ref.png b/test/reference/degenerate-rel-curve-to.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..73ad25c
Binary files /dev/null and b/test/reference/degenerate-rel-curve-to.traps.argb32.ref.png differ
diff --git a/test/reference/degenerate-rel-curve-to.traps.rgb24.ref.png b/test/reference/degenerate-rel-curve-to.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..73ad25c
Binary files /dev/null and b/test/reference/degenerate-rel-curve-to.traps.rgb24.ref.png differ
diff --git a/test/reference/device-offset-fractional.base.argb32.ref.png b/test/reference/device-offset-fractional.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..b1eef5f
Binary files /dev/null and b/test/reference/device-offset-fractional.base.argb32.ref.png differ
diff --git a/test/reference/device-offset-fractional.base.rgb24.ref.png b/test/reference/device-offset-fractional.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..b1eef5f
Binary files /dev/null and b/test/reference/device-offset-fractional.base.rgb24.ref.png differ
diff --git a/test/reference/device-offset-fractional.gl.xfail.png b/test/reference/device-offset-fractional.gl.xfail.png
new file mode 100644 (file)
index 0000000..96b0a6a
Binary files /dev/null and b/test/reference/device-offset-fractional.gl.xfail.png differ
diff --git a/test/reference/device-offset-fractional.pdf.xfail.png b/test/reference/device-offset-fractional.pdf.xfail.png
new file mode 100644 (file)
index 0000000..50bbd34
Binary files /dev/null and b/test/reference/device-offset-fractional.pdf.xfail.png differ
diff --git a/test/reference/device-offset-fractional.ps2.ref.png b/test/reference/device-offset-fractional.ps2.ref.png
new file mode 100644 (file)
index 0000000..5b44082
Binary files /dev/null and b/test/reference/device-offset-fractional.ps2.ref.png differ
diff --git a/test/reference/device-offset-fractional.ps3.ref.png b/test/reference/device-offset-fractional.ps3.ref.png
new file mode 100644 (file)
index 0000000..5b44082
Binary files /dev/null and b/test/reference/device-offset-fractional.ps3.ref.png differ
diff --git a/test/reference/device-offset-fractional.ref.png b/test/reference/device-offset-fractional.ref.png
new file mode 100644 (file)
index 0000000..9250d33
Binary files /dev/null and b/test/reference/device-offset-fractional.ref.png differ
diff --git a/test/reference/device-offset-fractional.traps.argb32.ref.png b/test/reference/device-offset-fractional.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..b1eef5f
Binary files /dev/null and b/test/reference/device-offset-fractional.traps.argb32.ref.png differ
diff --git a/test/reference/device-offset-fractional.traps.rgb24.ref.png b/test/reference/device-offset-fractional.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..b1eef5f
Binary files /dev/null and b/test/reference/device-offset-fractional.traps.rgb24.ref.png differ
diff --git a/test/reference/device-offset-positive.base.argb32.ref.png b/test/reference/device-offset-positive.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..1115bca
Binary files /dev/null and b/test/reference/device-offset-positive.base.argb32.ref.png differ
diff --git a/test/reference/device-offset-positive.base.rgb24.ref.png b/test/reference/device-offset-positive.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..dcdd332
Binary files /dev/null and b/test/reference/device-offset-positive.base.rgb24.ref.png differ
diff --git a/test/reference/device-offset-positive.ref.png b/test/reference/device-offset-positive.ref.png
new file mode 100644 (file)
index 0000000..bdf63af
Binary files /dev/null and b/test/reference/device-offset-positive.ref.png differ
diff --git a/test/reference/device-offset-positive.rgb24.ref.png b/test/reference/device-offset-positive.rgb24.ref.png
new file mode 100644 (file)
index 0000000..3281f49
Binary files /dev/null and b/test/reference/device-offset-positive.rgb24.ref.png differ
diff --git a/test/reference/device-offset-positive.traps.argb32.ref.png b/test/reference/device-offset-positive.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..1115bca
Binary files /dev/null and b/test/reference/device-offset-positive.traps.argb32.ref.png differ
diff --git a/test/reference/device-offset-positive.traps.rgb24.ref.png b/test/reference/device-offset-positive.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..dcdd332
Binary files /dev/null and b/test/reference/device-offset-positive.traps.rgb24.ref.png differ
diff --git a/test/reference/device-offset-scale.base.argb32.ref.png b/test/reference/device-offset-scale.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..fdffbf3
Binary files /dev/null and b/test/reference/device-offset-scale.base.argb32.ref.png differ
diff --git a/test/reference/device-offset-scale.base.rgb24.ref.png b/test/reference/device-offset-scale.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..fdffbf3
Binary files /dev/null and b/test/reference/device-offset-scale.base.rgb24.ref.png differ
diff --git a/test/reference/device-offset-scale.ref.png b/test/reference/device-offset-scale.ref.png
new file mode 100644 (file)
index 0000000..66b2973
Binary files /dev/null and b/test/reference/device-offset-scale.ref.png differ
diff --git a/test/reference/device-offset-scale.svg.xfail.png b/test/reference/device-offset-scale.svg.xfail.png
new file mode 100644 (file)
index 0000000..58a82d6
Binary files /dev/null and b/test/reference/device-offset-scale.svg.xfail.png differ
diff --git a/test/reference/device-offset-scale.traps.argb32.ref.png b/test/reference/device-offset-scale.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..fdffbf3
Binary files /dev/null and b/test/reference/device-offset-scale.traps.argb32.ref.png differ
diff --git a/test/reference/device-offset-scale.traps.rgb24.ref.png b/test/reference/device-offset-scale.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..fdffbf3
Binary files /dev/null and b/test/reference/device-offset-scale.traps.rgb24.ref.png differ
diff --git a/test/reference/device-offset.base.argb32.ref.png b/test/reference/device-offset.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..43ced46
Binary files /dev/null and b/test/reference/device-offset.base.argb32.ref.png differ
diff --git a/test/reference/device-offset.base.rgb24.ref.png b/test/reference/device-offset.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..f19acba
Binary files /dev/null and b/test/reference/device-offset.base.rgb24.ref.png differ
diff --git a/test/reference/device-offset.ref.png b/test/reference/device-offset.ref.png
new file mode 100644 (file)
index 0000000..22cbfb4
Binary files /dev/null and b/test/reference/device-offset.ref.png differ
diff --git a/test/reference/device-offset.rgb24.ref.png b/test/reference/device-offset.rgb24.ref.png
new file mode 100644 (file)
index 0000000..634388f
Binary files /dev/null and b/test/reference/device-offset.rgb24.ref.png differ
diff --git a/test/reference/device-offset.traps.argb32.ref.png b/test/reference/device-offset.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..43ced46
Binary files /dev/null and b/test/reference/device-offset.traps.argb32.ref.png differ
diff --git a/test/reference/device-offset.traps.rgb24.ref.png b/test/reference/device-offset.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..f19acba
Binary files /dev/null and b/test/reference/device-offset.traps.rgb24.ref.png differ
diff --git a/test/reference/drunkard-tails.base.argb32.ref.png b/test/reference/drunkard-tails.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..71cb9af
Binary files /dev/null and b/test/reference/drunkard-tails.base.argb32.ref.png differ
diff --git a/test/reference/drunkard-tails.base.rgb24.ref.png b/test/reference/drunkard-tails.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..71cb9af
Binary files /dev/null and b/test/reference/drunkard-tails.base.rgb24.ref.png differ
diff --git a/test/reference/drunkard-tails.ps.ref.png b/test/reference/drunkard-tails.ps.ref.png
new file mode 100644 (file)
index 0000000..f68c8b5
Binary files /dev/null and b/test/reference/drunkard-tails.ps.ref.png differ
diff --git a/test/reference/drunkard-tails.ref.png b/test/reference/drunkard-tails.ref.png
new file mode 100644 (file)
index 0000000..9cd2314
Binary files /dev/null and b/test/reference/drunkard-tails.ref.png differ
diff --git a/test/reference/drunkard-tails.traps.argb32.ref.png b/test/reference/drunkard-tails.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..71cb9af
Binary files /dev/null and b/test/reference/drunkard-tails.traps.argb32.ref.png differ
diff --git a/test/reference/drunkard-tails.traps.rgb24.ref.png b/test/reference/drunkard-tails.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..71cb9af
Binary files /dev/null and b/test/reference/drunkard-tails.traps.rgb24.ref.png differ
diff --git a/test/reference/extend-pad-border.base.argb32.ref.png b/test/reference/extend-pad-border.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..f4fc524
Binary files /dev/null and b/test/reference/extend-pad-border.base.argb32.ref.png differ
diff --git a/test/reference/extend-pad-border.base.rgb24.ref.png b/test/reference/extend-pad-border.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..f4fc524
Binary files /dev/null and b/test/reference/extend-pad-border.base.rgb24.ref.png differ
diff --git a/test/reference/extend-pad-border.image16.ref.png b/test/reference/extend-pad-border.image16.ref.png
new file mode 100644 (file)
index 0000000..2a1efd4
Binary files /dev/null and b/test/reference/extend-pad-border.image16.ref.png differ
diff --git a/test/reference/extend-pad-border.pdf.ref.png b/test/reference/extend-pad-border.pdf.ref.png
new file mode 100644 (file)
index 0000000..f4fc524
Binary files /dev/null and b/test/reference/extend-pad-border.pdf.ref.png differ
diff --git a/test/reference/extend-pad-border.ps.ref.png b/test/reference/extend-pad-border.ps.ref.png
new file mode 100644 (file)
index 0000000..b1f4c40
Binary files /dev/null and b/test/reference/extend-pad-border.ps.ref.png differ
diff --git a/test/reference/extend-pad-border.quartz.ref.png b/test/reference/extend-pad-border.quartz.ref.png
new file mode 100644 (file)
index 0000000..4ad67a1
Binary files /dev/null and b/test/reference/extend-pad-border.quartz.ref.png differ
diff --git a/test/reference/extend-pad-border.ref.png b/test/reference/extend-pad-border.ref.png
new file mode 100644 (file)
index 0000000..9292f8b
Binary files /dev/null and b/test/reference/extend-pad-border.ref.png differ
diff --git a/test/reference/extend-pad-border.svg.xfail.png b/test/reference/extend-pad-border.svg.xfail.png
new file mode 100644 (file)
index 0000000..0fde36d
Binary files /dev/null and b/test/reference/extend-pad-border.svg.xfail.png differ
diff --git a/test/reference/extend-pad-border.traps.argb32.ref.png b/test/reference/extend-pad-border.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..f4fc524
Binary files /dev/null and b/test/reference/extend-pad-border.traps.argb32.ref.png differ
diff --git a/test/reference/extend-pad-border.traps.rgb24.ref.png b/test/reference/extend-pad-border.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..f4fc524
Binary files /dev/null and b/test/reference/extend-pad-border.traps.rgb24.ref.png differ
diff --git a/test/reference/extend-pad-similar.base.argb32.ref.png b/test/reference/extend-pad-similar.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..9345221
Binary files /dev/null and b/test/reference/extend-pad-similar.base.argb32.ref.png differ
diff --git a/test/reference/extend-pad-similar.base.rgb24.ref.png b/test/reference/extend-pad-similar.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..9345221
Binary files /dev/null and b/test/reference/extend-pad-similar.base.rgb24.ref.png differ
diff --git a/test/reference/extend-pad-similar.quartz.xfail.png b/test/reference/extend-pad-similar.quartz.xfail.png
new file mode 100644 (file)
index 0000000..a2cf353
Binary files /dev/null and b/test/reference/extend-pad-similar.quartz.xfail.png differ
diff --git a/test/reference/extend-pad-similar.ref.png b/test/reference/extend-pad-similar.ref.png
new file mode 100644 (file)
index 0000000..82da7b6
Binary files /dev/null and b/test/reference/extend-pad-similar.ref.png differ
diff --git a/test/reference/extend-pad-similar.svg.xfail.png b/test/reference/extend-pad-similar.svg.xfail.png
new file mode 100644 (file)
index 0000000..a2cf353
Binary files /dev/null and b/test/reference/extend-pad-similar.svg.xfail.png differ
diff --git a/test/reference/extend-pad-similar.traps.argb32.ref.png b/test/reference/extend-pad-similar.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..9345221
Binary files /dev/null and b/test/reference/extend-pad-similar.traps.argb32.ref.png differ
diff --git a/test/reference/extend-pad-similar.traps.rgb24.ref.png b/test/reference/extend-pad-similar.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..9345221
Binary files /dev/null and b/test/reference/extend-pad-similar.traps.rgb24.ref.png differ
diff --git a/test/reference/extend-pad.base.argb32.ref.png b/test/reference/extend-pad.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..9345221
Binary files /dev/null and b/test/reference/extend-pad.base.argb32.ref.png differ
diff --git a/test/reference/extend-pad.base.rgb24.ref.png b/test/reference/extend-pad.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..9345221
Binary files /dev/null and b/test/reference/extend-pad.base.rgb24.ref.png differ
diff --git a/test/reference/extend-pad.ps.ref.png b/test/reference/extend-pad.ps.ref.png
new file mode 100644 (file)
index 0000000..a249ee2
Binary files /dev/null and b/test/reference/extend-pad.ps.ref.png differ
diff --git a/test/reference/extend-pad.quartz.xfail.png b/test/reference/extend-pad.quartz.xfail.png
new file mode 100644 (file)
index 0000000..a2cf353
Binary files /dev/null and b/test/reference/extend-pad.quartz.xfail.png differ
diff --git a/test/reference/extend-pad.ref.png b/test/reference/extend-pad.ref.png
new file mode 100644 (file)
index 0000000..82da7b6
Binary files /dev/null and b/test/reference/extend-pad.ref.png differ
diff --git a/test/reference/extend-pad.svg.xfail.png b/test/reference/extend-pad.svg.xfail.png
new file mode 100644 (file)
index 0000000..a2cf353
Binary files /dev/null and b/test/reference/extend-pad.svg.xfail.png differ
diff --git a/test/reference/extend-pad.traps.argb32.ref.png b/test/reference/extend-pad.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..9345221
Binary files /dev/null and b/test/reference/extend-pad.traps.argb32.ref.png differ
diff --git a/test/reference/extend-pad.traps.rgb24.ref.png b/test/reference/extend-pad.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..9345221
Binary files /dev/null and b/test/reference/extend-pad.traps.rgb24.ref.png differ
diff --git a/test/reference/extend-reflect-similar.base.argb32.ref.png b/test/reference/extend-reflect-similar.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..5fc3448
Binary files /dev/null and b/test/reference/extend-reflect-similar.base.argb32.ref.png differ
diff --git a/test/reference/extend-reflect-similar.base.rgb24.ref.png b/test/reference/extend-reflect-similar.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..5fc3448
Binary files /dev/null and b/test/reference/extend-reflect-similar.base.rgb24.ref.png differ
diff --git a/test/reference/extend-reflect-similar.image16.ref.png b/test/reference/extend-reflect-similar.image16.ref.png
new file mode 100644 (file)
index 0000000..27c6594
Binary files /dev/null and b/test/reference/extend-reflect-similar.image16.ref.png differ
diff --git a/test/reference/extend-reflect-similar.ps2.ref.png b/test/reference/extend-reflect-similar.ps2.ref.png
new file mode 100644 (file)
index 0000000..acaee1b
Binary files /dev/null and b/test/reference/extend-reflect-similar.ps2.ref.png differ
diff --git a/test/reference/extend-reflect-similar.ps3.ref.png b/test/reference/extend-reflect-similar.ps3.ref.png
new file mode 100644 (file)
index 0000000..acaee1b
Binary files /dev/null and b/test/reference/extend-reflect-similar.ps3.ref.png differ
diff --git a/test/reference/extend-reflect-similar.ref.png b/test/reference/extend-reflect-similar.ref.png
new file mode 100644 (file)
index 0000000..93a8b00
Binary files /dev/null and b/test/reference/extend-reflect-similar.ref.png differ
diff --git a/test/reference/extend-reflect-similar.traps.argb32.ref.png b/test/reference/extend-reflect-similar.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..5fc3448
Binary files /dev/null and b/test/reference/extend-reflect-similar.traps.argb32.ref.png differ
diff --git a/test/reference/extend-reflect-similar.traps.rgb24.ref.png b/test/reference/extend-reflect-similar.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..5fc3448
Binary files /dev/null and b/test/reference/extend-reflect-similar.traps.rgb24.ref.png differ
diff --git a/test/reference/extend-reflect.base.argb32.ref.png b/test/reference/extend-reflect.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..5fc3448
Binary files /dev/null and b/test/reference/extend-reflect.base.argb32.ref.png differ
diff --git a/test/reference/extend-reflect.base.rgb24.ref.png b/test/reference/extend-reflect.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..5fc3448
Binary files /dev/null and b/test/reference/extend-reflect.base.rgb24.ref.png differ
diff --git a/test/reference/extend-reflect.image16.ref.png b/test/reference/extend-reflect.image16.ref.png
new file mode 100644 (file)
index 0000000..27c6594
Binary files /dev/null and b/test/reference/extend-reflect.image16.ref.png differ
diff --git a/test/reference/extend-reflect.ps2.ref.png b/test/reference/extend-reflect.ps2.ref.png
new file mode 100644 (file)
index 0000000..acaee1b
Binary files /dev/null and b/test/reference/extend-reflect.ps2.ref.png differ
diff --git a/test/reference/extend-reflect.ps3.ref.png b/test/reference/extend-reflect.ps3.ref.png
new file mode 100644 (file)
index 0000000..acaee1b
Binary files /dev/null and b/test/reference/extend-reflect.ps3.ref.png differ
diff --git a/test/reference/extend-reflect.ref.png b/test/reference/extend-reflect.ref.png
new file mode 100644 (file)
index 0000000..93a8b00
Binary files /dev/null and b/test/reference/extend-reflect.ref.png differ
diff --git a/test/reference/extend-reflect.traps.argb32.ref.png b/test/reference/extend-reflect.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..5fc3448
Binary files /dev/null and b/test/reference/extend-reflect.traps.argb32.ref.png differ
diff --git a/test/reference/extend-reflect.traps.rgb24.ref.png b/test/reference/extend-reflect.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..5fc3448
Binary files /dev/null and b/test/reference/extend-reflect.traps.rgb24.ref.png differ
diff --git a/test/reference/extend-repeat-similar.base.argb32.ref.png b/test/reference/extend-repeat-similar.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..81f0503
Binary files /dev/null and b/test/reference/extend-repeat-similar.base.argb32.ref.png differ
diff --git a/test/reference/extend-repeat-similar.base.rgb24.ref.png b/test/reference/extend-repeat-similar.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..81f0503
Binary files /dev/null and b/test/reference/extend-repeat-similar.base.rgb24.ref.png differ
diff --git a/test/reference/extend-repeat-similar.image16.ref.png b/test/reference/extend-repeat-similar.image16.ref.png
new file mode 100644 (file)
index 0000000..cdc742a
Binary files /dev/null and b/test/reference/extend-repeat-similar.image16.ref.png differ
diff --git a/test/reference/extend-repeat-similar.ps2.ref.png b/test/reference/extend-repeat-similar.ps2.ref.png
new file mode 100644 (file)
index 0000000..8218211
Binary files /dev/null and b/test/reference/extend-repeat-similar.ps2.ref.png differ
diff --git a/test/reference/extend-repeat-similar.ps3.ref.png b/test/reference/extend-repeat-similar.ps3.ref.png
new file mode 100644 (file)
index 0000000..8218211
Binary files /dev/null and b/test/reference/extend-repeat-similar.ps3.ref.png differ
diff --git a/test/reference/extend-repeat-similar.ref.png b/test/reference/extend-repeat-similar.ref.png
new file mode 100644 (file)
index 0000000..ee2527f
Binary files /dev/null and b/test/reference/extend-repeat-similar.ref.png differ
diff --git a/test/reference/extend-repeat-similar.traps.argb32.ref.png b/test/reference/extend-repeat-similar.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..81f0503
Binary files /dev/null and b/test/reference/extend-repeat-similar.traps.argb32.ref.png differ
diff --git a/test/reference/extend-repeat-similar.traps.rgb24.ref.png b/test/reference/extend-repeat-similar.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..81f0503
Binary files /dev/null and b/test/reference/extend-repeat-similar.traps.rgb24.ref.png differ
diff --git a/test/reference/extend-repeat.base.argb32.ref.png b/test/reference/extend-repeat.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..81f0503
Binary files /dev/null and b/test/reference/extend-repeat.base.argb32.ref.png differ
diff --git a/test/reference/extend-repeat.base.rgb24.ref.png b/test/reference/extend-repeat.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..81f0503
Binary files /dev/null and b/test/reference/extend-repeat.base.rgb24.ref.png differ
diff --git a/test/reference/extend-repeat.image16.ref.png b/test/reference/extend-repeat.image16.ref.png
new file mode 100644 (file)
index 0000000..cdc742a
Binary files /dev/null and b/test/reference/extend-repeat.image16.ref.png differ
diff --git a/test/reference/extend-repeat.ps2.ref.png b/test/reference/extend-repeat.ps2.ref.png
new file mode 100644 (file)
index 0000000..8218211
Binary files /dev/null and b/test/reference/extend-repeat.ps2.ref.png differ
diff --git a/test/reference/extend-repeat.ps3.ref.png b/test/reference/extend-repeat.ps3.ref.png
new file mode 100644 (file)
index 0000000..8218211
Binary files /dev/null and b/test/reference/extend-repeat.ps3.ref.png differ
diff --git a/test/reference/extend-repeat.ref.png b/test/reference/extend-repeat.ref.png
new file mode 100644 (file)
index 0000000..ee2527f
Binary files /dev/null and b/test/reference/extend-repeat.ref.png differ
diff --git a/test/reference/extend-repeat.traps.argb32.ref.png b/test/reference/extend-repeat.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..81f0503
Binary files /dev/null and b/test/reference/extend-repeat.traps.argb32.ref.png differ
diff --git a/test/reference/extend-repeat.traps.rgb24.ref.png b/test/reference/extend-repeat.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..81f0503
Binary files /dev/null and b/test/reference/extend-repeat.traps.rgb24.ref.png differ
diff --git a/test/reference/extended-blend-alpha-mask.argb32.ref.png b/test/reference/extended-blend-alpha-mask.argb32.ref.png
new file mode 100644 (file)
index 0000000..a1dd2a9
Binary files /dev/null and b/test/reference/extended-blend-alpha-mask.argb32.ref.png differ
diff --git a/test/reference/extended-blend-alpha-mask.base.argb32.ref.png b/test/reference/extended-blend-alpha-mask.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..a1dd2a9
Binary files /dev/null and b/test/reference/extended-blend-alpha-mask.base.argb32.ref.png differ
diff --git a/test/reference/extended-blend-alpha-mask.base.rgb24.ref.png b/test/reference/extended-blend-alpha-mask.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..b5f12f1
Binary files /dev/null and b/test/reference/extended-blend-alpha-mask.base.rgb24.ref.png differ
diff --git a/test/reference/extended-blend-alpha-mask.rgb24.ref.png b/test/reference/extended-blend-alpha-mask.rgb24.ref.png
new file mode 100644 (file)
index 0000000..b5f12f1
Binary files /dev/null and b/test/reference/extended-blend-alpha-mask.rgb24.ref.png differ
diff --git a/test/reference/extended-blend-alpha-mask.traps.argb32.ref.png b/test/reference/extended-blend-alpha-mask.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..a1dd2a9
Binary files /dev/null and b/test/reference/extended-blend-alpha-mask.traps.argb32.ref.png differ
diff --git a/test/reference/extended-blend-alpha-mask.traps.rgb24.ref.png b/test/reference/extended-blend-alpha-mask.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..b5f12f1
Binary files /dev/null and b/test/reference/extended-blend-alpha-mask.traps.rgb24.ref.png differ
diff --git a/test/reference/extended-blend-alpha.argb32.ref.png b/test/reference/extended-blend-alpha.argb32.ref.png
new file mode 100644 (file)
index 0000000..4d56a21
Binary files /dev/null and b/test/reference/extended-blend-alpha.argb32.ref.png differ
diff --git a/test/reference/extended-blend-alpha.base.argb32.ref.png b/test/reference/extended-blend-alpha.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..4d56a21
Binary files /dev/null and b/test/reference/extended-blend-alpha.base.argb32.ref.png differ
diff --git a/test/reference/extended-blend-alpha.base.rgb24.ref.png b/test/reference/extended-blend-alpha.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..86d0e2d
Binary files /dev/null and b/test/reference/extended-blend-alpha.base.rgb24.ref.png differ
diff --git a/test/reference/extended-blend-alpha.image16.ref.png b/test/reference/extended-blend-alpha.image16.ref.png
new file mode 100644 (file)
index 0000000..df96469
Binary files /dev/null and b/test/reference/extended-blend-alpha.image16.ref.png differ
diff --git a/test/reference/extended-blend-alpha.quartz.argb32.ref.png b/test/reference/extended-blend-alpha.quartz.argb32.ref.png
new file mode 100644 (file)
index 0000000..e5701a6
Binary files /dev/null and b/test/reference/extended-blend-alpha.quartz.argb32.ref.png differ
diff --git a/test/reference/extended-blend-alpha.quartz.rgb24.ref.png b/test/reference/extended-blend-alpha.quartz.rgb24.ref.png
new file mode 100644 (file)
index 0000000..477d346
Binary files /dev/null and b/test/reference/extended-blend-alpha.quartz.rgb24.ref.png differ
diff --git a/test/reference/extended-blend-alpha.rgb24.ref.png b/test/reference/extended-blend-alpha.rgb24.ref.png
new file mode 100644 (file)
index 0000000..86d0e2d
Binary files /dev/null and b/test/reference/extended-blend-alpha.rgb24.ref.png differ
diff --git a/test/reference/extended-blend-alpha.svg12.argb32.xfail.png b/test/reference/extended-blend-alpha.svg12.argb32.xfail.png
new file mode 100644 (file)
index 0000000..cc34416
Binary files /dev/null and b/test/reference/extended-blend-alpha.svg12.argb32.xfail.png differ
diff --git a/test/reference/extended-blend-alpha.svg12.rgb24.xfail.png b/test/reference/extended-blend-alpha.svg12.rgb24.xfail.png
new file mode 100644 (file)
index 0000000..f80569e
Binary files /dev/null and b/test/reference/extended-blend-alpha.svg12.rgb24.xfail.png differ
diff --git a/test/reference/extended-blend-alpha.traps.argb32.ref.png b/test/reference/extended-blend-alpha.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..4d56a21
Binary files /dev/null and b/test/reference/extended-blend-alpha.traps.argb32.ref.png differ
diff --git a/test/reference/extended-blend-alpha.traps.rgb24.ref.png b/test/reference/extended-blend-alpha.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..86d0e2d
Binary files /dev/null and b/test/reference/extended-blend-alpha.traps.rgb24.ref.png differ
diff --git a/test/reference/extended-blend-mask.argb32.ref.png b/test/reference/extended-blend-mask.argb32.ref.png
new file mode 100644 (file)
index 0000000..5fa78e4
Binary files /dev/null and b/test/reference/extended-blend-mask.argb32.ref.png differ
diff --git a/test/reference/extended-blend-mask.base.argb32.ref.png b/test/reference/extended-blend-mask.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..5fa78e4
Binary files /dev/null and b/test/reference/extended-blend-mask.base.argb32.ref.png differ
diff --git a/test/reference/extended-blend-mask.base.rgb24.ref.png b/test/reference/extended-blend-mask.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..1c85474
Binary files /dev/null and b/test/reference/extended-blend-mask.base.rgb24.ref.png differ
diff --git a/test/reference/extended-blend-mask.rgb24.ref.png b/test/reference/extended-blend-mask.rgb24.ref.png
new file mode 100644 (file)
index 0000000..1c85474
Binary files /dev/null and b/test/reference/extended-blend-mask.rgb24.ref.png differ
diff --git a/test/reference/extended-blend-mask.traps.argb32.ref.png b/test/reference/extended-blend-mask.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..5fa78e4
Binary files /dev/null and b/test/reference/extended-blend-mask.traps.argb32.ref.png differ
diff --git a/test/reference/extended-blend-mask.traps.rgb24.ref.png b/test/reference/extended-blend-mask.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..1c85474
Binary files /dev/null and b/test/reference/extended-blend-mask.traps.rgb24.ref.png differ
diff --git a/test/reference/extended-blend-solid-alpha.argb32.ref.png b/test/reference/extended-blend-solid-alpha.argb32.ref.png
new file mode 100644 (file)
index 0000000..4d56a21
Binary files /dev/null and b/test/reference/extended-blend-solid-alpha.argb32.ref.png differ
diff --git a/test/reference/extended-blend-solid-alpha.base.argb32.ref.png b/test/reference/extended-blend-solid-alpha.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..4d56a21
Binary files /dev/null and b/test/reference/extended-blend-solid-alpha.base.argb32.ref.png differ
diff --git a/test/reference/extended-blend-solid-alpha.base.rgb24.ref.png b/test/reference/extended-blend-solid-alpha.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..86d0e2d
Binary files /dev/null and b/test/reference/extended-blend-solid-alpha.base.rgb24.ref.png differ
diff --git a/test/reference/extended-blend-solid-alpha.image16.ref.png b/test/reference/extended-blend-solid-alpha.image16.ref.png
new file mode 100644 (file)
index 0000000..df96469
Binary files /dev/null and b/test/reference/extended-blend-solid-alpha.image16.ref.png differ
diff --git a/test/reference/extended-blend-solid-alpha.rgb24.ref.png b/test/reference/extended-blend-solid-alpha.rgb24.ref.png
new file mode 100644 (file)
index 0000000..86d0e2d
Binary files /dev/null and b/test/reference/extended-blend-solid-alpha.rgb24.ref.png differ
diff --git a/test/reference/extended-blend-solid-alpha.traps.argb32.ref.png b/test/reference/extended-blend-solid-alpha.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..4d56a21
Binary files /dev/null and b/test/reference/extended-blend-solid-alpha.traps.argb32.ref.png differ
diff --git a/test/reference/extended-blend-solid-alpha.traps.rgb24.ref.png b/test/reference/extended-blend-solid-alpha.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..86d0e2d
Binary files /dev/null and b/test/reference/extended-blend-solid-alpha.traps.rgb24.ref.png differ
diff --git a/test/reference/extended-blend-solid.argb32.ref.png b/test/reference/extended-blend-solid.argb32.ref.png
new file mode 100644 (file)
index 0000000..083fe87
Binary files /dev/null and b/test/reference/extended-blend-solid.argb32.ref.png differ
diff --git a/test/reference/extended-blend-solid.base.argb32.ref.png b/test/reference/extended-blend-solid.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..902ef88
Binary files /dev/null and b/test/reference/extended-blend-solid.base.argb32.ref.png differ
diff --git a/test/reference/extended-blend-solid.base.rgb24.ref.png b/test/reference/extended-blend-solid.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..4580e0d
Binary files /dev/null and b/test/reference/extended-blend-solid.base.rgb24.ref.png differ
diff --git a/test/reference/extended-blend-solid.image16.ref.png b/test/reference/extended-blend-solid.image16.ref.png
new file mode 100644 (file)
index 0000000..2052ea7
Binary files /dev/null and b/test/reference/extended-blend-solid.image16.ref.png differ
diff --git a/test/reference/extended-blend-solid.rgb24.ref.png b/test/reference/extended-blend-solid.rgb24.ref.png
new file mode 100644 (file)
index 0000000..4580e0d
Binary files /dev/null and b/test/reference/extended-blend-solid.rgb24.ref.png differ
diff --git a/test/reference/extended-blend-solid.traps.argb32.ref.png b/test/reference/extended-blend-solid.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..902ef88
Binary files /dev/null and b/test/reference/extended-blend-solid.traps.argb32.ref.png differ
diff --git a/test/reference/extended-blend-solid.traps.rgb24.ref.png b/test/reference/extended-blend-solid.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..4580e0d
Binary files /dev/null and b/test/reference/extended-blend-solid.traps.rgb24.ref.png differ
diff --git a/test/reference/extended-blend.argb32.ref.png b/test/reference/extended-blend.argb32.ref.png
new file mode 100644 (file)
index 0000000..083fe87
Binary files /dev/null and b/test/reference/extended-blend.argb32.ref.png differ
diff --git a/test/reference/extended-blend.base.argb32.ref.png b/test/reference/extended-blend.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..902ef88
Binary files /dev/null and b/test/reference/extended-blend.base.argb32.ref.png differ
diff --git a/test/reference/extended-blend.base.rgb24.ref.png b/test/reference/extended-blend.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..4580e0d
Binary files /dev/null and b/test/reference/extended-blend.base.rgb24.ref.png differ
diff --git a/test/reference/extended-blend.image16.ref.png b/test/reference/extended-blend.image16.ref.png
new file mode 100644 (file)
index 0000000..2052ea7
Binary files /dev/null and b/test/reference/extended-blend.image16.ref.png differ
diff --git a/test/reference/extended-blend.quartz.argb32.ref.png b/test/reference/extended-blend.quartz.argb32.ref.png
new file mode 100644 (file)
index 0000000..173c6e2
Binary files /dev/null and b/test/reference/extended-blend.quartz.argb32.ref.png differ
diff --git a/test/reference/extended-blend.quartz.rgb24.ref.png b/test/reference/extended-blend.quartz.rgb24.ref.png
new file mode 100644 (file)
index 0000000..56a1214
Binary files /dev/null and b/test/reference/extended-blend.quartz.rgb24.ref.png differ
diff --git a/test/reference/extended-blend.rgb24.ref.png b/test/reference/extended-blend.rgb24.ref.png
new file mode 100644 (file)
index 0000000..4580e0d
Binary files /dev/null and b/test/reference/extended-blend.rgb24.ref.png differ
diff --git a/test/reference/extended-blend.svg12.argb32.xfail.png b/test/reference/extended-blend.svg12.argb32.xfail.png
new file mode 100644 (file)
index 0000000..93297a5
Binary files /dev/null and b/test/reference/extended-blend.svg12.argb32.xfail.png differ
diff --git a/test/reference/extended-blend.svg12.rgb24.xfail.png b/test/reference/extended-blend.svg12.rgb24.xfail.png
new file mode 100644 (file)
index 0000000..8db02c5
Binary files /dev/null and b/test/reference/extended-blend.svg12.rgb24.xfail.png differ
diff --git a/test/reference/extended-blend.traps.argb32.ref.png b/test/reference/extended-blend.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..902ef88
Binary files /dev/null and b/test/reference/extended-blend.traps.argb32.ref.png differ
diff --git a/test/reference/extended-blend.traps.rgb24.ref.png b/test/reference/extended-blend.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..4580e0d
Binary files /dev/null and b/test/reference/extended-blend.traps.rgb24.ref.png differ
diff --git a/test/reference/fallback-resolution.ppi144x144.ps.ref.png b/test/reference/fallback-resolution.ppi144x144.ps.ref.png
new file mode 100644 (file)
index 0000000..0922f03
Binary files /dev/null and b/test/reference/fallback-resolution.ppi144x144.ps.ref.png differ
diff --git a/test/reference/fallback-resolution.ppi144x144.ref.png b/test/reference/fallback-resolution.ppi144x144.ref.png
new file mode 100644 (file)
index 0000000..cbe93ca
Binary files /dev/null and b/test/reference/fallback-resolution.ppi144x144.ref.png differ
diff --git a/test/reference/fallback-resolution.ppi144x72.ps.ref.png b/test/reference/fallback-resolution.ppi144x72.ps.ref.png
new file mode 100644 (file)
index 0000000..2f8d82d
Binary files /dev/null and b/test/reference/fallback-resolution.ppi144x72.ps.ref.png differ
diff --git a/test/reference/fallback-resolution.ppi144x72.ref.png b/test/reference/fallback-resolution.ppi144x72.ref.png
new file mode 100644 (file)
index 0000000..4c16ca1
Binary files /dev/null and b/test/reference/fallback-resolution.ppi144x72.ref.png differ
diff --git a/test/reference/fallback-resolution.ppi288x288.pdf.ref.png b/test/reference/fallback-resolution.ppi288x288.pdf.ref.png
new file mode 100644 (file)
index 0000000..181e110
Binary files /dev/null and b/test/reference/fallback-resolution.ppi288x288.pdf.ref.png differ
diff --git a/test/reference/fallback-resolution.ppi288x288.ps.ref.png b/test/reference/fallback-resolution.ppi288x288.ps.ref.png
new file mode 100644 (file)
index 0000000..99bccef
Binary files /dev/null and b/test/reference/fallback-resolution.ppi288x288.ps.ref.png differ
diff --git a/test/reference/fallback-resolution.ppi288x288.svg.ref.png b/test/reference/fallback-resolution.ppi288x288.svg.ref.png
new file mode 100644 (file)
index 0000000..e71ff81
Binary files /dev/null and b/test/reference/fallback-resolution.ppi288x288.svg.ref.png differ
diff --git a/test/reference/fallback-resolution.ppi288x72.ps.ref.png b/test/reference/fallback-resolution.ppi288x72.ps.ref.png
new file mode 100644 (file)
index 0000000..89b9c51
Binary files /dev/null and b/test/reference/fallback-resolution.ppi288x72.ps.ref.png differ
diff --git a/test/reference/fallback-resolution.ppi288x72.ref.png b/test/reference/fallback-resolution.ppi288x72.ref.png
new file mode 100644 (file)
index 0000000..b213587
Binary files /dev/null and b/test/reference/fallback-resolution.ppi288x72.ref.png differ
diff --git a/test/reference/fallback-resolution.ppi576x576.pdf.ref.png b/test/reference/fallback-resolution.ppi576x576.pdf.ref.png
new file mode 100644 (file)
index 0000000..5b37619
Binary files /dev/null and b/test/reference/fallback-resolution.ppi576x576.pdf.ref.png differ
diff --git a/test/reference/fallback-resolution.ppi576x576.ps.ref.png b/test/reference/fallback-resolution.ppi576x576.ps.ref.png
new file mode 100644 (file)
index 0000000..9dc4735
Binary files /dev/null and b/test/reference/fallback-resolution.ppi576x576.ps.ref.png differ
diff --git a/test/reference/fallback-resolution.ppi576x576.svg.ref.png b/test/reference/fallback-resolution.ppi576x576.svg.ref.png
new file mode 100644 (file)
index 0000000..b5a97e3
Binary files /dev/null and b/test/reference/fallback-resolution.ppi576x576.svg.ref.png differ
diff --git a/test/reference/fallback-resolution.ppi576x72.ps.ref.png b/test/reference/fallback-resolution.ppi576x72.ps.ref.png
new file mode 100644 (file)
index 0000000..9ac6be4
Binary files /dev/null and b/test/reference/fallback-resolution.ppi576x72.ps.ref.png differ
diff --git a/test/reference/fallback-resolution.ppi576x72.ref.png b/test/reference/fallback-resolution.ppi576x72.ref.png
new file mode 100644 (file)
index 0000000..b63e66e
Binary files /dev/null and b/test/reference/fallback-resolution.ppi576x72.ref.png differ
diff --git a/test/reference/fallback-resolution.ppi72x144.ps.ref.png b/test/reference/fallback-resolution.ppi72x144.ps.ref.png
new file mode 100644 (file)
index 0000000..50b5a99
Binary files /dev/null and b/test/reference/fallback-resolution.ppi72x144.ps.ref.png differ
diff --git a/test/reference/fallback-resolution.ppi72x144.ref.png b/test/reference/fallback-resolution.ppi72x144.ref.png
new file mode 100644 (file)
index 0000000..9d232c0
Binary files /dev/null and b/test/reference/fallback-resolution.ppi72x144.ref.png differ
diff --git a/test/reference/fallback-resolution.ppi72x288.ps.ref.png b/test/reference/fallback-resolution.ppi72x288.ps.ref.png
new file mode 100644 (file)
index 0000000..b4ff82d
Binary files /dev/null and b/test/reference/fallback-resolution.ppi72x288.ps.ref.png differ
diff --git a/test/reference/fallback-resolution.ppi72x288.ref.png b/test/reference/fallback-resolution.ppi72x288.ref.png
new file mode 100644 (file)
index 0000000..fdbc382
Binary files /dev/null and b/test/reference/fallback-resolution.ppi72x288.ref.png differ
diff --git a/test/reference/fallback-resolution.ppi72x576.ps.ref.png b/test/reference/fallback-resolution.ppi72x576.ps.ref.png
new file mode 100644 (file)
index 0000000..6a3ddcb
Binary files /dev/null and b/test/reference/fallback-resolution.ppi72x576.ps.ref.png differ
diff --git a/test/reference/fallback-resolution.ppi72x576.ref.png b/test/reference/fallback-resolution.ppi72x576.ref.png
new file mode 100644 (file)
index 0000000..c943f08
Binary files /dev/null and b/test/reference/fallback-resolution.ppi72x576.ref.png differ
diff --git a/test/reference/fallback-resolution.ppi72x72.ref.png b/test/reference/fallback-resolution.ppi72x72.ref.png
new file mode 100644 (file)
index 0000000..5d1df2c
Binary files /dev/null and b/test/reference/fallback-resolution.ppi72x72.ref.png differ
diff --git a/test/reference/fill-alpha-pattern.base.argb32.ref.png b/test/reference/fill-alpha-pattern.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..4dafb83
Binary files /dev/null and b/test/reference/fill-alpha-pattern.base.argb32.ref.png differ
diff --git a/test/reference/fill-alpha-pattern.base.rgb24.ref.png b/test/reference/fill-alpha-pattern.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..4dafb83
Binary files /dev/null and b/test/reference/fill-alpha-pattern.base.rgb24.ref.png differ
diff --git a/test/reference/fill-alpha-pattern.image16.ref.png b/test/reference/fill-alpha-pattern.image16.ref.png
new file mode 100644 (file)
index 0000000..f323c10
Binary files /dev/null and b/test/reference/fill-alpha-pattern.image16.ref.png differ
diff --git a/test/reference/fill-alpha-pattern.pdf.ref.png b/test/reference/fill-alpha-pattern.pdf.ref.png
new file mode 100644 (file)
index 0000000..ed7f404
Binary files /dev/null and b/test/reference/fill-alpha-pattern.pdf.ref.png differ
diff --git a/test/reference/fill-alpha-pattern.ps3.argb32.ref.png b/test/reference/fill-alpha-pattern.ps3.argb32.ref.png
new file mode 100644 (file)
index 0000000..28689a3
Binary files /dev/null and b/test/reference/fill-alpha-pattern.ps3.argb32.ref.png differ
diff --git a/test/reference/fill-alpha-pattern.quartz.ref.png b/test/reference/fill-alpha-pattern.quartz.ref.png
new file mode 100644 (file)
index 0000000..b612e7a
Binary files /dev/null and b/test/reference/fill-alpha-pattern.quartz.ref.png differ
diff --git a/test/reference/fill-alpha-pattern.ref.png b/test/reference/fill-alpha-pattern.ref.png
new file mode 100644 (file)
index 0000000..13f2a72
Binary files /dev/null and b/test/reference/fill-alpha-pattern.ref.png differ
diff --git a/test/reference/fill-alpha-pattern.traps.argb32.ref.png b/test/reference/fill-alpha-pattern.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..4dafb83
Binary files /dev/null and b/test/reference/fill-alpha-pattern.traps.argb32.ref.png differ
diff --git a/test/reference/fill-alpha-pattern.traps.rgb24.ref.png b/test/reference/fill-alpha-pattern.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..4dafb83
Binary files /dev/null and b/test/reference/fill-alpha-pattern.traps.rgb24.ref.png differ
diff --git a/test/reference/fill-alpha.base.argb32.ref.png b/test/reference/fill-alpha.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..85df919
Binary files /dev/null and b/test/reference/fill-alpha.base.argb32.ref.png differ
diff --git a/test/reference/fill-alpha.base.rgb24.ref.png b/test/reference/fill-alpha.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..85df919
Binary files /dev/null and b/test/reference/fill-alpha.base.rgb24.ref.png differ
diff --git a/test/reference/fill-alpha.image16.ref.png b/test/reference/fill-alpha.image16.ref.png
new file mode 100644 (file)
index 0000000..08252b6
Binary files /dev/null and b/test/reference/fill-alpha.image16.ref.png differ
diff --git a/test/reference/fill-alpha.ps.argb32.ref.png b/test/reference/fill-alpha.ps.argb32.ref.png
new file mode 100644 (file)
index 0000000..8d70d53
Binary files /dev/null and b/test/reference/fill-alpha.ps.argb32.ref.png differ
diff --git a/test/reference/fill-alpha.quartz.ref.png b/test/reference/fill-alpha.quartz.ref.png
new file mode 100644 (file)
index 0000000..81cee81
Binary files /dev/null and b/test/reference/fill-alpha.quartz.ref.png differ
diff --git a/test/reference/fill-alpha.ref.png b/test/reference/fill-alpha.ref.png
new file mode 100644 (file)
index 0000000..7ab149d
Binary files /dev/null and b/test/reference/fill-alpha.ref.png differ
diff --git a/test/reference/fill-alpha.traps.argb32.ref.png b/test/reference/fill-alpha.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..85df919
Binary files /dev/null and b/test/reference/fill-alpha.traps.argb32.ref.png differ
diff --git a/test/reference/fill-alpha.traps.rgb24.ref.png b/test/reference/fill-alpha.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..85df919
Binary files /dev/null and b/test/reference/fill-alpha.traps.rgb24.ref.png differ
diff --git a/test/reference/fill-and-stroke-alpha-add.base.argb32.ref.png b/test/reference/fill-and-stroke-alpha-add.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..71d2b22
Binary files /dev/null and b/test/reference/fill-and-stroke-alpha-add.base.argb32.ref.png differ
diff --git a/test/reference/fill-and-stroke-alpha-add.base.rgb24.ref.png b/test/reference/fill-and-stroke-alpha-add.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..71d2b22
Binary files /dev/null and b/test/reference/fill-and-stroke-alpha-add.base.rgb24.ref.png differ
diff --git a/test/reference/fill-and-stroke-alpha-add.image16.ref.png b/test/reference/fill-and-stroke-alpha-add.image16.ref.png
new file mode 100644 (file)
index 0000000..3162c81
Binary files /dev/null and b/test/reference/fill-and-stroke-alpha-add.image16.ref.png differ
diff --git a/test/reference/fill-and-stroke-alpha-add.quartz.ref.png b/test/reference/fill-and-stroke-alpha-add.quartz.ref.png
new file mode 100644 (file)
index 0000000..1d89752
Binary files /dev/null and b/test/reference/fill-and-stroke-alpha-add.quartz.ref.png differ
diff --git a/test/reference/fill-and-stroke-alpha-add.ref.png b/test/reference/fill-and-stroke-alpha-add.ref.png
new file mode 100644 (file)
index 0000000..f2731b9
Binary files /dev/null and b/test/reference/fill-and-stroke-alpha-add.ref.png differ
diff --git a/test/reference/fill-and-stroke-alpha-add.svg12.xfail.png b/test/reference/fill-and-stroke-alpha-add.svg12.xfail.png
new file mode 100644 (file)
index 0000000..c1d7d6f
Binary files /dev/null and b/test/reference/fill-and-stroke-alpha-add.svg12.xfail.png differ
diff --git a/test/reference/fill-and-stroke-alpha-add.traps.argb32.ref.png b/test/reference/fill-and-stroke-alpha-add.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..71d2b22
Binary files /dev/null and b/test/reference/fill-and-stroke-alpha-add.traps.argb32.ref.png differ
diff --git a/test/reference/fill-and-stroke-alpha-add.traps.rgb24.ref.png b/test/reference/fill-and-stroke-alpha-add.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..71d2b22
Binary files /dev/null and b/test/reference/fill-and-stroke-alpha-add.traps.rgb24.ref.png differ
diff --git a/test/reference/fill-and-stroke-alpha.base.argb32.ref.png b/test/reference/fill-and-stroke-alpha.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..c85b933
Binary files /dev/null and b/test/reference/fill-and-stroke-alpha.base.argb32.ref.png differ
diff --git a/test/reference/fill-and-stroke-alpha.base.rgb24.ref.png b/test/reference/fill-and-stroke-alpha.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..c85b933
Binary files /dev/null and b/test/reference/fill-and-stroke-alpha.base.rgb24.ref.png differ
diff --git a/test/reference/fill-and-stroke-alpha.image16.ref.png b/test/reference/fill-and-stroke-alpha.image16.ref.png
new file mode 100644 (file)
index 0000000..cde5bd9
Binary files /dev/null and b/test/reference/fill-and-stroke-alpha.image16.ref.png differ
diff --git a/test/reference/fill-and-stroke-alpha.ref.png b/test/reference/fill-and-stroke-alpha.ref.png
new file mode 100644 (file)
index 0000000..70f4763
Binary files /dev/null and b/test/reference/fill-and-stroke-alpha.ref.png differ
diff --git a/test/reference/fill-and-stroke-alpha.traps.argb32.ref.png b/test/reference/fill-and-stroke-alpha.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..c85b933
Binary files /dev/null and b/test/reference/fill-and-stroke-alpha.traps.argb32.ref.png differ
diff --git a/test/reference/fill-and-stroke-alpha.traps.rgb24.ref.png b/test/reference/fill-and-stroke-alpha.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..c85b933
Binary files /dev/null and b/test/reference/fill-and-stroke-alpha.traps.rgb24.ref.png differ
diff --git a/test/reference/fill-and-stroke.argb32.ref.png b/test/reference/fill-and-stroke.argb32.ref.png
new file mode 100644 (file)
index 0000000..f1aa633
Binary files /dev/null and b/test/reference/fill-and-stroke.argb32.ref.png differ
diff --git a/test/reference/fill-and-stroke.base.argb32.ref.png b/test/reference/fill-and-stroke.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..3f32060
Binary files /dev/null and b/test/reference/fill-and-stroke.base.argb32.ref.png differ
diff --git a/test/reference/fill-and-stroke.base.rgb24.ref.png b/test/reference/fill-and-stroke.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..2797921
Binary files /dev/null and b/test/reference/fill-and-stroke.base.rgb24.ref.png differ
diff --git a/test/reference/fill-and-stroke.image16.ref.png b/test/reference/fill-and-stroke.image16.ref.png
new file mode 100644 (file)
index 0000000..f562509
Binary files /dev/null and b/test/reference/fill-and-stroke.image16.ref.png differ
diff --git a/test/reference/fill-and-stroke.ps.argb32.ref.png b/test/reference/fill-and-stroke.ps.argb32.ref.png
new file mode 100644 (file)
index 0000000..8cf8d9c
Binary files /dev/null and b/test/reference/fill-and-stroke.ps.argb32.ref.png differ
diff --git a/test/reference/fill-and-stroke.ps.rgb24.ref.png b/test/reference/fill-and-stroke.ps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..fceda26
Binary files /dev/null and b/test/reference/fill-and-stroke.ps.rgb24.ref.png differ
diff --git a/test/reference/fill-and-stroke.quartz.argb32.ref.png b/test/reference/fill-and-stroke.quartz.argb32.ref.png
new file mode 100644 (file)
index 0000000..9440719
Binary files /dev/null and b/test/reference/fill-and-stroke.quartz.argb32.ref.png differ
diff --git a/test/reference/fill-and-stroke.quartz.rgb24.ref.png b/test/reference/fill-and-stroke.quartz.rgb24.ref.png
new file mode 100644 (file)
index 0000000..5ba2197
Binary files /dev/null and b/test/reference/fill-and-stroke.quartz.rgb24.ref.png differ
diff --git a/test/reference/fill-and-stroke.rgb24.ref.png b/test/reference/fill-and-stroke.rgb24.ref.png
new file mode 100644 (file)
index 0000000..7081d72
Binary files /dev/null and b/test/reference/fill-and-stroke.rgb24.ref.png differ
diff --git a/test/reference/fill-and-stroke.traps.argb32.ref.png b/test/reference/fill-and-stroke.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..3f32060
Binary files /dev/null and b/test/reference/fill-and-stroke.traps.argb32.ref.png differ
diff --git a/test/reference/fill-and-stroke.traps.rgb24.ref.png b/test/reference/fill-and-stroke.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..2797921
Binary files /dev/null and b/test/reference/fill-and-stroke.traps.rgb24.ref.png differ
diff --git a/test/reference/fill-degenerate-sort-order.argb32.ref.png b/test/reference/fill-degenerate-sort-order.argb32.ref.png
new file mode 100644 (file)
index 0000000..57e61db
Binary files /dev/null and b/test/reference/fill-degenerate-sort-order.argb32.ref.png differ
diff --git a/test/reference/fill-degenerate-sort-order.base.argb32.ref.png b/test/reference/fill-degenerate-sort-order.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..860ee1b
Binary files /dev/null and b/test/reference/fill-degenerate-sort-order.base.argb32.ref.png differ
diff --git a/test/reference/fill-degenerate-sort-order.base.rgb24.ref.png b/test/reference/fill-degenerate-sort-order.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..18b08fc
Binary files /dev/null and b/test/reference/fill-degenerate-sort-order.base.rgb24.ref.png differ
diff --git a/test/reference/fill-degenerate-sort-order.image16.ref.png b/test/reference/fill-degenerate-sort-order.image16.ref.png
new file mode 100644 (file)
index 0000000..6dc2078
Binary files /dev/null and b/test/reference/fill-degenerate-sort-order.image16.ref.png differ
diff --git a/test/reference/fill-degenerate-sort-order.ps.argb32.xfail.png b/test/reference/fill-degenerate-sort-order.ps.argb32.xfail.png
new file mode 100644 (file)
index 0000000..79ea630
Binary files /dev/null and b/test/reference/fill-degenerate-sort-order.ps.argb32.xfail.png differ
diff --git a/test/reference/fill-degenerate-sort-order.ps.rgb24.xfail.png b/test/reference/fill-degenerate-sort-order.ps.rgb24.xfail.png
new file mode 100644 (file)
index 0000000..b4c45f9
Binary files /dev/null and b/test/reference/fill-degenerate-sort-order.ps.rgb24.xfail.png differ
diff --git a/test/reference/fill-degenerate-sort-order.quartz.argb32.ref.png b/test/reference/fill-degenerate-sort-order.quartz.argb32.ref.png
new file mode 100644 (file)
index 0000000..a8b9f15
Binary files /dev/null and b/test/reference/fill-degenerate-sort-order.quartz.argb32.ref.png differ
diff --git a/test/reference/fill-degenerate-sort-order.quartz.rgb24.ref.png b/test/reference/fill-degenerate-sort-order.quartz.rgb24.ref.png
new file mode 100644 (file)
index 0000000..703467c
Binary files /dev/null and b/test/reference/fill-degenerate-sort-order.quartz.rgb24.ref.png differ
diff --git a/test/reference/fill-degenerate-sort-order.rgb24.ref.png b/test/reference/fill-degenerate-sort-order.rgb24.ref.png
new file mode 100644 (file)
index 0000000..5e807b1
Binary files /dev/null and b/test/reference/fill-degenerate-sort-order.rgb24.ref.png differ
diff --git a/test/reference/fill-degenerate-sort-order.traps.argb32.ref.png b/test/reference/fill-degenerate-sort-order.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..860ee1b
Binary files /dev/null and b/test/reference/fill-degenerate-sort-order.traps.argb32.ref.png differ
diff --git a/test/reference/fill-degenerate-sort-order.traps.rgb24.ref.png b/test/reference/fill-degenerate-sort-order.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..18b08fc
Binary files /dev/null and b/test/reference/fill-degenerate-sort-order.traps.rgb24.ref.png differ
diff --git a/test/reference/fill-empty.argb32.ref.png b/test/reference/fill-empty.argb32.ref.png
new file mode 100644 (file)
index 0000000..8c26f7e
Binary files /dev/null and b/test/reference/fill-empty.argb32.ref.png differ
diff --git a/test/reference/fill-empty.base.argb32.ref.png b/test/reference/fill-empty.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..8c26f7e
Binary files /dev/null and b/test/reference/fill-empty.base.argb32.ref.png differ
diff --git a/test/reference/fill-empty.base.rgb24.ref.png b/test/reference/fill-empty.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..dc7a8a0
Binary files /dev/null and b/test/reference/fill-empty.base.rgb24.ref.png differ
diff --git a/test/reference/fill-empty.rgb24.ref.png b/test/reference/fill-empty.rgb24.ref.png
new file mode 100644 (file)
index 0000000..dc7a8a0
Binary files /dev/null and b/test/reference/fill-empty.rgb24.ref.png differ
diff --git a/test/reference/fill-empty.svg12.rgb24.xfail.png b/test/reference/fill-empty.svg12.rgb24.xfail.png
new file mode 100644 (file)
index 0000000..8c26f7e
Binary files /dev/null and b/test/reference/fill-empty.svg12.rgb24.xfail.png differ
diff --git a/test/reference/fill-empty.traps.argb32.ref.png b/test/reference/fill-empty.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..8c26f7e
Binary files /dev/null and b/test/reference/fill-empty.traps.argb32.ref.png differ
diff --git a/test/reference/fill-empty.traps.rgb24.ref.png b/test/reference/fill-empty.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..dc7a8a0
Binary files /dev/null and b/test/reference/fill-empty.traps.rgb24.ref.png differ
diff --git a/test/reference/fill-image.base.argb32.ref.png b/test/reference/fill-image.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..b94ed91
Binary files /dev/null and b/test/reference/fill-image.base.argb32.ref.png differ
diff --git a/test/reference/fill-image.base.rgb24.ref.png b/test/reference/fill-image.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..b94ed91
Binary files /dev/null and b/test/reference/fill-image.base.rgb24.ref.png differ
diff --git a/test/reference/fill-image.image16.ref.png b/test/reference/fill-image.image16.ref.png
new file mode 100644 (file)
index 0000000..3a9b7c7
Binary files /dev/null and b/test/reference/fill-image.image16.ref.png differ
diff --git a/test/reference/fill-image.ps.ref.png b/test/reference/fill-image.ps.ref.png
new file mode 100644 (file)
index 0000000..9713701
Binary files /dev/null and b/test/reference/fill-image.ps.ref.png differ
diff --git a/test/reference/fill-image.quartz.ref.png b/test/reference/fill-image.quartz.ref.png
new file mode 100644 (file)
index 0000000..bb205a7
Binary files /dev/null and b/test/reference/fill-image.quartz.ref.png differ
diff --git a/test/reference/fill-image.ref.png b/test/reference/fill-image.ref.png
new file mode 100644 (file)
index 0000000..899c159
Binary files /dev/null and b/test/reference/fill-image.ref.png differ
diff --git a/test/reference/fill-image.traps.argb32.ref.png b/test/reference/fill-image.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..b94ed91
Binary files /dev/null and b/test/reference/fill-image.traps.argb32.ref.png differ
diff --git a/test/reference/fill-image.traps.rgb24.ref.png b/test/reference/fill-image.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..b94ed91
Binary files /dev/null and b/test/reference/fill-image.traps.rgb24.ref.png differ
diff --git a/test/reference/fill-missed-stop.base.argb32.ref.png b/test/reference/fill-missed-stop.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..223fb36
Binary files /dev/null and b/test/reference/fill-missed-stop.base.argb32.ref.png differ
diff --git a/test/reference/fill-missed-stop.base.rgb24.ref.png b/test/reference/fill-missed-stop.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..f56b4b2
Binary files /dev/null and b/test/reference/fill-missed-stop.base.rgb24.ref.png differ
diff --git a/test/reference/fill-missed-stop.pdf.argb32.ref.png b/test/reference/fill-missed-stop.pdf.argb32.ref.png
new file mode 100644 (file)
index 0000000..7d56e3e
Binary files /dev/null and b/test/reference/fill-missed-stop.pdf.argb32.ref.png differ
diff --git a/test/reference/fill-missed-stop.ps2.argb32.ref.png b/test/reference/fill-missed-stop.ps2.argb32.ref.png
new file mode 100644 (file)
index 0000000..b94a708
Binary files /dev/null and b/test/reference/fill-missed-stop.ps2.argb32.ref.png differ
diff --git a/test/reference/fill-missed-stop.ps2.rgb24.ref.png b/test/reference/fill-missed-stop.ps2.rgb24.ref.png
new file mode 100644 (file)
index 0000000..fd54c7b
Binary files /dev/null and b/test/reference/fill-missed-stop.ps2.rgb24.ref.png differ
diff --git a/test/reference/fill-missed-stop.ps3.argb32.ref.png b/test/reference/fill-missed-stop.ps3.argb32.ref.png
new file mode 100644 (file)
index 0000000..b94a708
Binary files /dev/null and b/test/reference/fill-missed-stop.ps3.argb32.ref.png differ
diff --git a/test/reference/fill-missed-stop.ps3.rgb24.ref.png b/test/reference/fill-missed-stop.ps3.rgb24.ref.png
new file mode 100644 (file)
index 0000000..fd54c7b
Binary files /dev/null and b/test/reference/fill-missed-stop.ps3.rgb24.ref.png differ
diff --git a/test/reference/fill-missed-stop.ref.png b/test/reference/fill-missed-stop.ref.png
new file mode 100644 (file)
index 0000000..7c5a4ed
Binary files /dev/null and b/test/reference/fill-missed-stop.ref.png differ
diff --git a/test/reference/fill-missed-stop.rgb24.ref.png b/test/reference/fill-missed-stop.rgb24.ref.png
new file mode 100644 (file)
index 0000000..3f9d2ad
Binary files /dev/null and b/test/reference/fill-missed-stop.rgb24.ref.png differ
diff --git a/test/reference/fill-missed-stop.traps.argb32.ref.png b/test/reference/fill-missed-stop.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..223fb36
Binary files /dev/null and b/test/reference/fill-missed-stop.traps.argb32.ref.png differ
diff --git a/test/reference/fill-missed-stop.traps.rgb24.ref.png b/test/reference/fill-missed-stop.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..f56b4b2
Binary files /dev/null and b/test/reference/fill-missed-stop.traps.rgb24.ref.png differ
diff --git a/test/reference/fill-rule.base.argb32.ref.png b/test/reference/fill-rule.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..e2e10d4
Binary files /dev/null and b/test/reference/fill-rule.base.argb32.ref.png differ
diff --git a/test/reference/fill-rule.base.rgb24.ref.png b/test/reference/fill-rule.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..49fb39c
Binary files /dev/null and b/test/reference/fill-rule.base.rgb24.ref.png differ
diff --git a/test/reference/fill-rule.image16.ref.png b/test/reference/fill-rule.image16.ref.png
new file mode 100644 (file)
index 0000000..27613f7
Binary files /dev/null and b/test/reference/fill-rule.image16.ref.png differ
diff --git a/test/reference/fill-rule.ps2.argb32.ref.png b/test/reference/fill-rule.ps2.argb32.ref.png
new file mode 100644 (file)
index 0000000..c9bdf90
Binary files /dev/null and b/test/reference/fill-rule.ps2.argb32.ref.png differ
diff --git a/test/reference/fill-rule.ps2.rgb24.ref.png b/test/reference/fill-rule.ps2.rgb24.ref.png
new file mode 100644 (file)
index 0000000..617a20b
Binary files /dev/null and b/test/reference/fill-rule.ps2.rgb24.ref.png differ
diff --git a/test/reference/fill-rule.ps3.argb32.ref.png b/test/reference/fill-rule.ps3.argb32.ref.png
new file mode 100644 (file)
index 0000000..c9bdf90
Binary files /dev/null and b/test/reference/fill-rule.ps3.argb32.ref.png differ
diff --git a/test/reference/fill-rule.ps3.rgb24.ref.png b/test/reference/fill-rule.ps3.rgb24.ref.png
new file mode 100644 (file)
index 0000000..617a20b
Binary files /dev/null and b/test/reference/fill-rule.ps3.rgb24.ref.png differ
diff --git a/test/reference/fill-rule.quartz.argb32.ref.png b/test/reference/fill-rule.quartz.argb32.ref.png
new file mode 100644 (file)
index 0000000..2ac5340
Binary files /dev/null and b/test/reference/fill-rule.quartz.argb32.ref.png differ
diff --git a/test/reference/fill-rule.quartz.rgb24.ref.png b/test/reference/fill-rule.quartz.rgb24.ref.png
new file mode 100644 (file)
index 0000000..bd671d6
Binary files /dev/null and b/test/reference/fill-rule.quartz.rgb24.ref.png differ
diff --git a/test/reference/fill-rule.ref.png b/test/reference/fill-rule.ref.png
new file mode 100644 (file)
index 0000000..ed18bf8
Binary files /dev/null and b/test/reference/fill-rule.ref.png differ
diff --git a/test/reference/fill-rule.rgb24.ref.png b/test/reference/fill-rule.rgb24.ref.png
new file mode 100644 (file)
index 0000000..704172f
Binary files /dev/null and b/test/reference/fill-rule.rgb24.ref.png differ
diff --git a/test/reference/fill-rule.traps.argb32.ref.png b/test/reference/fill-rule.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..e2e10d4
Binary files /dev/null and b/test/reference/fill-rule.traps.argb32.ref.png differ
diff --git a/test/reference/fill-rule.traps.rgb24.ref.png b/test/reference/fill-rule.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..49fb39c
Binary files /dev/null and b/test/reference/fill-rule.traps.rgb24.ref.png differ
diff --git a/test/reference/filter-bilinear-extents.base.argb32.ref.png b/test/reference/filter-bilinear-extents.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..797e798
Binary files /dev/null and b/test/reference/filter-bilinear-extents.base.argb32.ref.png differ
diff --git a/test/reference/filter-bilinear-extents.base.rgb24.ref.png b/test/reference/filter-bilinear-extents.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..797e798
Binary files /dev/null and b/test/reference/filter-bilinear-extents.base.rgb24.ref.png differ
diff --git a/test/reference/filter-bilinear-extents.image16.ref.png b/test/reference/filter-bilinear-extents.image16.ref.png
new file mode 100644 (file)
index 0000000..5b7755b
Binary files /dev/null and b/test/reference/filter-bilinear-extents.image16.ref.png differ
diff --git a/test/reference/filter-bilinear-extents.pdf.xfail.png b/test/reference/filter-bilinear-extents.pdf.xfail.png
new file mode 100644 (file)
index 0000000..e6c4bb4
Binary files /dev/null and b/test/reference/filter-bilinear-extents.pdf.xfail.png differ
diff --git a/test/reference/filter-bilinear-extents.ps2.ref.png b/test/reference/filter-bilinear-extents.ps2.ref.png
new file mode 100644 (file)
index 0000000..97c105c
Binary files /dev/null and b/test/reference/filter-bilinear-extents.ps2.ref.png differ
diff --git a/test/reference/filter-bilinear-extents.ps3.ref.png b/test/reference/filter-bilinear-extents.ps3.ref.png
new file mode 100644 (file)
index 0000000..97c105c
Binary files /dev/null and b/test/reference/filter-bilinear-extents.ps3.ref.png differ
diff --git a/test/reference/filter-bilinear-extents.quartz.xfail.png b/test/reference/filter-bilinear-extents.quartz.xfail.png
new file mode 100644 (file)
index 0000000..312ee80
Binary files /dev/null and b/test/reference/filter-bilinear-extents.quartz.xfail.png differ
diff --git a/test/reference/filter-bilinear-extents.ref.png b/test/reference/filter-bilinear-extents.ref.png
new file mode 100644 (file)
index 0000000..61e416b
Binary files /dev/null and b/test/reference/filter-bilinear-extents.ref.png differ
diff --git a/test/reference/filter-bilinear-extents.traps.argb32.ref.png b/test/reference/filter-bilinear-extents.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..797e798
Binary files /dev/null and b/test/reference/filter-bilinear-extents.traps.argb32.ref.png differ
diff --git a/test/reference/filter-bilinear-extents.traps.rgb24.ref.png b/test/reference/filter-bilinear-extents.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..797e798
Binary files /dev/null and b/test/reference/filter-bilinear-extents.traps.rgb24.ref.png differ
diff --git a/test/reference/filter-nearest-offset.base.argb32.ref.png b/test/reference/filter-nearest-offset.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..8cca9a1
Binary files /dev/null and b/test/reference/filter-nearest-offset.base.argb32.ref.png differ
diff --git a/test/reference/filter-nearest-offset.base.rgb24.ref.png b/test/reference/filter-nearest-offset.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..8cca9a1
Binary files /dev/null and b/test/reference/filter-nearest-offset.base.rgb24.ref.png differ
diff --git a/test/reference/filter-nearest-offset.gl.xfail.png b/test/reference/filter-nearest-offset.gl.xfail.png
new file mode 100644 (file)
index 0000000..a777e7c
Binary files /dev/null and b/test/reference/filter-nearest-offset.gl.xfail.png differ
diff --git a/test/reference/filter-nearest-offset.pdf.xfail.png b/test/reference/filter-nearest-offset.pdf.xfail.png
new file mode 100644 (file)
index 0000000..3042821
Binary files /dev/null and b/test/reference/filter-nearest-offset.pdf.xfail.png differ
diff --git a/test/reference/filter-nearest-offset.ps2.ref.png b/test/reference/filter-nearest-offset.ps2.ref.png
new file mode 100644 (file)
index 0000000..185f779
Binary files /dev/null and b/test/reference/filter-nearest-offset.ps2.ref.png differ
diff --git a/test/reference/filter-nearest-offset.ps3.ref.png b/test/reference/filter-nearest-offset.ps3.ref.png
new file mode 100644 (file)
index 0000000..185f779
Binary files /dev/null and b/test/reference/filter-nearest-offset.ps3.ref.png differ
diff --git a/test/reference/filter-nearest-offset.ref.png b/test/reference/filter-nearest-offset.ref.png
new file mode 100644 (file)
index 0000000..af81aee
Binary files /dev/null and b/test/reference/filter-nearest-offset.ref.png differ
diff --git a/test/reference/filter-nearest-offset.svg.xfail.png b/test/reference/filter-nearest-offset.svg.xfail.png
new file mode 100644 (file)
index 0000000..a46dc76
Binary files /dev/null and b/test/reference/filter-nearest-offset.svg.xfail.png differ
diff --git a/test/reference/filter-nearest-offset.traps.argb32.ref.png b/test/reference/filter-nearest-offset.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..8cca9a1
Binary files /dev/null and b/test/reference/filter-nearest-offset.traps.argb32.ref.png differ
diff --git a/test/reference/filter-nearest-offset.traps.rgb24.ref.png b/test/reference/filter-nearest-offset.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..8cca9a1
Binary files /dev/null and b/test/reference/filter-nearest-offset.traps.rgb24.ref.png differ
diff --git a/test/reference/filter-nearest-transformed.base.argb32.ref.png b/test/reference/filter-nearest-transformed.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..24adf8b
Binary files /dev/null and b/test/reference/filter-nearest-transformed.base.argb32.ref.png differ
diff --git a/test/reference/filter-nearest-transformed.base.rgb24.ref.png b/test/reference/filter-nearest-transformed.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..24adf8b
Binary files /dev/null and b/test/reference/filter-nearest-transformed.base.rgb24.ref.png differ
diff --git a/test/reference/filter-nearest-transformed.gl.xfail.png b/test/reference/filter-nearest-transformed.gl.xfail.png
new file mode 100644 (file)
index 0000000..ba8170b
Binary files /dev/null and b/test/reference/filter-nearest-transformed.gl.xfail.png differ
diff --git a/test/reference/filter-nearest-transformed.image16.ref.png b/test/reference/filter-nearest-transformed.image16.ref.png
new file mode 100644 (file)
index 0000000..a02e1e1
Binary files /dev/null and b/test/reference/filter-nearest-transformed.image16.ref.png differ
diff --git a/test/reference/filter-nearest-transformed.pdf.xfail.png b/test/reference/filter-nearest-transformed.pdf.xfail.png
new file mode 100644 (file)
index 0000000..e5b8378
Binary files /dev/null and b/test/reference/filter-nearest-transformed.pdf.xfail.png differ
diff --git a/test/reference/filter-nearest-transformed.quartz.xfail.png b/test/reference/filter-nearest-transformed.quartz.xfail.png
new file mode 100644 (file)
index 0000000..246cdf4
Binary files /dev/null and b/test/reference/filter-nearest-transformed.quartz.xfail.png differ
diff --git a/test/reference/filter-nearest-transformed.ref.png b/test/reference/filter-nearest-transformed.ref.png
new file mode 100644 (file)
index 0000000..dc413b4
Binary files /dev/null and b/test/reference/filter-nearest-transformed.ref.png differ
diff --git a/test/reference/filter-nearest-transformed.svg.xfail.png b/test/reference/filter-nearest-transformed.svg.xfail.png
new file mode 100644 (file)
index 0000000..e6bbe28
Binary files /dev/null and b/test/reference/filter-nearest-transformed.svg.xfail.png differ
diff --git a/test/reference/filter-nearest-transformed.traps.argb32.ref.png b/test/reference/filter-nearest-transformed.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..24adf8b
Binary files /dev/null and b/test/reference/filter-nearest-transformed.traps.argb32.ref.png differ
diff --git a/test/reference/filter-nearest-transformed.traps.rgb24.ref.png b/test/reference/filter-nearest-transformed.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..24adf8b
Binary files /dev/null and b/test/reference/filter-nearest-transformed.traps.rgb24.ref.png differ
diff --git a/test/reference/finer-grained-fallbacks.argb32.ref.png b/test/reference/finer-grained-fallbacks.argb32.ref.png
new file mode 100644 (file)
index 0000000..ea50762
Binary files /dev/null and b/test/reference/finer-grained-fallbacks.argb32.ref.png differ
diff --git a/test/reference/finer-grained-fallbacks.base.argb32.ref.png b/test/reference/finer-grained-fallbacks.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..accb01b
Binary files /dev/null and b/test/reference/finer-grained-fallbacks.base.argb32.ref.png differ
diff --git a/test/reference/finer-grained-fallbacks.base.rgb24.ref.png b/test/reference/finer-grained-fallbacks.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..0e6094c
Binary files /dev/null and b/test/reference/finer-grained-fallbacks.base.rgb24.ref.png differ
diff --git a/test/reference/finer-grained-fallbacks.gl.argb32.ref.png b/test/reference/finer-grained-fallbacks.gl.argb32.ref.png
new file mode 100644 (file)
index 0000000..69ec487
Binary files /dev/null and b/test/reference/finer-grained-fallbacks.gl.argb32.ref.png differ
diff --git a/test/reference/finer-grained-fallbacks.image16.ref.png b/test/reference/finer-grained-fallbacks.image16.ref.png
new file mode 100644 (file)
index 0000000..3b104ef
Binary files /dev/null and b/test/reference/finer-grained-fallbacks.image16.ref.png differ
diff --git a/test/reference/finer-grained-fallbacks.ps2.argb32.ref.png b/test/reference/finer-grained-fallbacks.ps2.argb32.ref.png
new file mode 100644 (file)
index 0000000..19c132f
Binary files /dev/null and b/test/reference/finer-grained-fallbacks.ps2.argb32.ref.png differ
diff --git a/test/reference/finer-grained-fallbacks.ps2.ref.png b/test/reference/finer-grained-fallbacks.ps2.ref.png
new file mode 100644 (file)
index 0000000..1744100
Binary files /dev/null and b/test/reference/finer-grained-fallbacks.ps2.ref.png differ
diff --git a/test/reference/finer-grained-fallbacks.ps2.rgb24.ref.png b/test/reference/finer-grained-fallbacks.ps2.rgb24.ref.png
new file mode 100644 (file)
index 0000000..3f94a3a
Binary files /dev/null and b/test/reference/finer-grained-fallbacks.ps2.rgb24.ref.png differ
diff --git a/test/reference/finer-grained-fallbacks.ps3.argb32.ref.png b/test/reference/finer-grained-fallbacks.ps3.argb32.ref.png
new file mode 100644 (file)
index 0000000..19c132f
Binary files /dev/null and b/test/reference/finer-grained-fallbacks.ps3.argb32.ref.png differ
diff --git a/test/reference/finer-grained-fallbacks.ps3.ref.png b/test/reference/finer-grained-fallbacks.ps3.ref.png
new file mode 100644 (file)
index 0000000..1744100
Binary files /dev/null and b/test/reference/finer-grained-fallbacks.ps3.ref.png differ
diff --git a/test/reference/finer-grained-fallbacks.ps3.rgb24.ref.png b/test/reference/finer-grained-fallbacks.ps3.rgb24.ref.png
new file mode 100644 (file)
index 0000000..3f94a3a
Binary files /dev/null and b/test/reference/finer-grained-fallbacks.ps3.rgb24.ref.png differ
diff --git a/test/reference/finer-grained-fallbacks.quartz.argb32.ref.png b/test/reference/finer-grained-fallbacks.quartz.argb32.ref.png
new file mode 100644 (file)
index 0000000..dc05761
Binary files /dev/null and b/test/reference/finer-grained-fallbacks.quartz.argb32.ref.png differ
diff --git a/test/reference/finer-grained-fallbacks.quartz.rgb24.ref.png b/test/reference/finer-grained-fallbacks.quartz.rgb24.ref.png
new file mode 100644 (file)
index 0000000..1fdedd0
Binary files /dev/null and b/test/reference/finer-grained-fallbacks.quartz.rgb24.ref.png differ
diff --git a/test/reference/finer-grained-fallbacks.rgb24.ref.png b/test/reference/finer-grained-fallbacks.rgb24.ref.png
new file mode 100644 (file)
index 0000000..08467b0
Binary files /dev/null and b/test/reference/finer-grained-fallbacks.rgb24.ref.png differ
diff --git a/test/reference/finer-grained-fallbacks.svg12.argb32.ref.png b/test/reference/finer-grained-fallbacks.svg12.argb32.ref.png
new file mode 100644 (file)
index 0000000..5aaf86b
Binary files /dev/null and b/test/reference/finer-grained-fallbacks.svg12.argb32.ref.png differ
diff --git a/test/reference/finer-grained-fallbacks.svg12.rgb24.ref.png b/test/reference/finer-grained-fallbacks.svg12.rgb24.ref.png
new file mode 100644 (file)
index 0000000..ad55366
Binary files /dev/null and b/test/reference/finer-grained-fallbacks.svg12.rgb24.ref.png differ
diff --git a/test/reference/finer-grained-fallbacks.traps.argb32.ref.png b/test/reference/finer-grained-fallbacks.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..accb01b
Binary files /dev/null and b/test/reference/finer-grained-fallbacks.traps.argb32.ref.png differ
diff --git a/test/reference/finer-grained-fallbacks.traps.rgb24.ref.png b/test/reference/finer-grained-fallbacks.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..0e6094c
Binary files /dev/null and b/test/reference/finer-grained-fallbacks.traps.rgb24.ref.png differ
diff --git a/test/reference/finer-grained-fallbacks.xlib-fallback.ref.png b/test/reference/finer-grained-fallbacks.xlib-fallback.ref.png
new file mode 100644 (file)
index 0000000..c2af714
Binary files /dev/null and b/test/reference/finer-grained-fallbacks.xlib-fallback.ref.png differ
diff --git a/test/reference/font-matrix-translation.base.argb32.ref.png b/test/reference/font-matrix-translation.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..dd5faba
Binary files /dev/null and b/test/reference/font-matrix-translation.base.argb32.ref.png differ
diff --git a/test/reference/font-matrix-translation.base.rgb24.ref.png b/test/reference/font-matrix-translation.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..dd5faba
Binary files /dev/null and b/test/reference/font-matrix-translation.base.rgb24.ref.png differ
diff --git a/test/reference/font-matrix-translation.image16.ref.png b/test/reference/font-matrix-translation.image16.ref.png
new file mode 100644 (file)
index 0000000..f76b9ae
Binary files /dev/null and b/test/reference/font-matrix-translation.image16.ref.png differ
diff --git a/test/reference/font-matrix-translation.ps2.argb32.ref.png b/test/reference/font-matrix-translation.ps2.argb32.ref.png
new file mode 100644 (file)
index 0000000..41d05a0
Binary files /dev/null and b/test/reference/font-matrix-translation.ps2.argb32.ref.png differ
diff --git a/test/reference/font-matrix-translation.ps2.rgb24.ref.png b/test/reference/font-matrix-translation.ps2.rgb24.ref.png
new file mode 100644 (file)
index 0000000..41d05a0
Binary files /dev/null and b/test/reference/font-matrix-translation.ps2.rgb24.ref.png differ
diff --git a/test/reference/font-matrix-translation.ps3.argb32.ref.png b/test/reference/font-matrix-translation.ps3.argb32.ref.png
new file mode 100644 (file)
index 0000000..41d05a0
Binary files /dev/null and b/test/reference/font-matrix-translation.ps3.argb32.ref.png differ
diff --git a/test/reference/font-matrix-translation.ps3.rgb24.ref.png b/test/reference/font-matrix-translation.ps3.rgb24.ref.png
new file mode 100644 (file)
index 0000000..41d05a0
Binary files /dev/null and b/test/reference/font-matrix-translation.ps3.rgb24.ref.png differ
diff --git a/test/reference/font-matrix-translation.quartz.ref.png b/test/reference/font-matrix-translation.quartz.ref.png
new file mode 100644 (file)
index 0000000..187e2c1
Binary files /dev/null and b/test/reference/font-matrix-translation.quartz.ref.png differ
diff --git a/test/reference/font-matrix-translation.ref.png b/test/reference/font-matrix-translation.ref.png
new file mode 100644 (file)
index 0000000..dd5faba
Binary files /dev/null and b/test/reference/font-matrix-translation.ref.png differ
diff --git a/test/reference/font-matrix-translation.svg.ref.png b/test/reference/font-matrix-translation.svg.ref.png
new file mode 100644 (file)
index 0000000..e35f9be
Binary files /dev/null and b/test/reference/font-matrix-translation.svg.ref.png differ
diff --git a/test/reference/font-matrix-translation.traps.argb32.ref.png b/test/reference/font-matrix-translation.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..dd5faba
Binary files /dev/null and b/test/reference/font-matrix-translation.traps.argb32.ref.png differ
diff --git a/test/reference/font-matrix-translation.traps.rgb24.ref.png b/test/reference/font-matrix-translation.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..dd5faba
Binary files /dev/null and b/test/reference/font-matrix-translation.traps.rgb24.ref.png differ
diff --git a/test/reference/ft-show-glyphs-positioning.base.argb32.ref.png b/test/reference/ft-show-glyphs-positioning.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..b49ed47
Binary files /dev/null and b/test/reference/ft-show-glyphs-positioning.base.argb32.ref.png differ
diff --git a/test/reference/ft-show-glyphs-positioning.base.rgb24.ref.png b/test/reference/ft-show-glyphs-positioning.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..b49ed47
Binary files /dev/null and b/test/reference/ft-show-glyphs-positioning.base.rgb24.ref.png differ
diff --git a/test/reference/ft-show-glyphs-positioning.image16.ref.png b/test/reference/ft-show-glyphs-positioning.image16.ref.png
new file mode 100644 (file)
index 0000000..f3d9f0d
Binary files /dev/null and b/test/reference/ft-show-glyphs-positioning.image16.ref.png differ
diff --git a/test/reference/ft-show-glyphs-positioning.pdf.ref.png b/test/reference/ft-show-glyphs-positioning.pdf.ref.png
new file mode 100644 (file)
index 0000000..0d62fd3
Binary files /dev/null and b/test/reference/ft-show-glyphs-positioning.pdf.ref.png differ
diff --git a/test/reference/ft-show-glyphs-positioning.ps2.ref.png b/test/reference/ft-show-glyphs-positioning.ps2.ref.png
new file mode 100644 (file)
index 0000000..c5fbf30
Binary files /dev/null and b/test/reference/ft-show-glyphs-positioning.ps2.ref.png differ
diff --git a/test/reference/ft-show-glyphs-positioning.ps3.ref.png b/test/reference/ft-show-glyphs-positioning.ps3.ref.png
new file mode 100644 (file)
index 0000000..c5fbf30
Binary files /dev/null and b/test/reference/ft-show-glyphs-positioning.ps3.ref.png differ
diff --git a/test/reference/ft-show-glyphs-positioning.ref.png b/test/reference/ft-show-glyphs-positioning.ref.png
new file mode 100644 (file)
index 0000000..b49ed47
Binary files /dev/null and b/test/reference/ft-show-glyphs-positioning.ref.png differ
diff --git a/test/reference/ft-show-glyphs-positioning.svg.ref.png b/test/reference/ft-show-glyphs-positioning.svg.ref.png
new file mode 100644 (file)
index 0000000..04fe674
Binary files /dev/null and b/test/reference/ft-show-glyphs-positioning.svg.ref.png differ
diff --git a/test/reference/ft-show-glyphs-positioning.traps.argb32.ref.png b/test/reference/ft-show-glyphs-positioning.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..b49ed47
Binary files /dev/null and b/test/reference/ft-show-glyphs-positioning.traps.argb32.ref.png differ
diff --git a/test/reference/ft-show-glyphs-positioning.traps.rgb24.ref.png b/test/reference/ft-show-glyphs-positioning.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..b49ed47
Binary files /dev/null and b/test/reference/ft-show-glyphs-positioning.traps.rgb24.ref.png differ
diff --git a/test/reference/ft-show-glyphs-table.base.argb32.ref.png b/test/reference/ft-show-glyphs-table.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..c762dc7
Binary files /dev/null and b/test/reference/ft-show-glyphs-table.base.argb32.ref.png differ
diff --git a/test/reference/ft-show-glyphs-table.base.rgb24.ref.png b/test/reference/ft-show-glyphs-table.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..c762dc7
Binary files /dev/null and b/test/reference/ft-show-glyphs-table.base.rgb24.ref.png differ
diff --git a/test/reference/ft-show-glyphs-table.image16.ref.png b/test/reference/ft-show-glyphs-table.image16.ref.png
new file mode 100644 (file)
index 0000000..af01a49
Binary files /dev/null and b/test/reference/ft-show-glyphs-table.image16.ref.png differ
diff --git a/test/reference/ft-show-glyphs-table.ps2.ref.png b/test/reference/ft-show-glyphs-table.ps2.ref.png
new file mode 100644 (file)
index 0000000..5143663
Binary files /dev/null and b/test/reference/ft-show-glyphs-table.ps2.ref.png differ
diff --git a/test/reference/ft-show-glyphs-table.ps3.ref.png b/test/reference/ft-show-glyphs-table.ps3.ref.png
new file mode 100644 (file)
index 0000000..5143663
Binary files /dev/null and b/test/reference/ft-show-glyphs-table.ps3.ref.png differ
diff --git a/test/reference/ft-show-glyphs-table.quartz.xfail.png b/test/reference/ft-show-glyphs-table.quartz.xfail.png
new file mode 100644 (file)
index 0000000..0e131b2
Binary files /dev/null and b/test/reference/ft-show-glyphs-table.quartz.xfail.png differ
diff --git a/test/reference/ft-show-glyphs-table.ref.png b/test/reference/ft-show-glyphs-table.ref.png
new file mode 100644 (file)
index 0000000..c762dc7
Binary files /dev/null and b/test/reference/ft-show-glyphs-table.ref.png differ
diff --git a/test/reference/ft-show-glyphs-table.svg.ref.png b/test/reference/ft-show-glyphs-table.svg.ref.png
new file mode 100644 (file)
index 0000000..e0654b7
Binary files /dev/null and b/test/reference/ft-show-glyphs-table.svg.ref.png differ
diff --git a/test/reference/ft-show-glyphs-table.traps.argb32.ref.png b/test/reference/ft-show-glyphs-table.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..c762dc7
Binary files /dev/null and b/test/reference/ft-show-glyphs-table.traps.argb32.ref.png differ
diff --git a/test/reference/ft-show-glyphs-table.traps.rgb24.ref.png b/test/reference/ft-show-glyphs-table.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..c762dc7
Binary files /dev/null and b/test/reference/ft-show-glyphs-table.traps.rgb24.ref.png differ
diff --git a/test/reference/ft-text-antialias-none.base.argb32.ref.png b/test/reference/ft-text-antialias-none.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..c638c9e
Binary files /dev/null and b/test/reference/ft-text-antialias-none.base.argb32.ref.png differ
diff --git a/test/reference/ft-text-antialias-none.base.rgb24.ref.png b/test/reference/ft-text-antialias-none.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..c638c9e
Binary files /dev/null and b/test/reference/ft-text-antialias-none.base.rgb24.ref.png differ
diff --git a/test/reference/ft-text-antialias-none.ps2.argb32.ref.png b/test/reference/ft-text-antialias-none.ps2.argb32.ref.png
new file mode 100644 (file)
index 0000000..4f7ee83
Binary files /dev/null and b/test/reference/ft-text-antialias-none.ps2.argb32.ref.png differ
diff --git a/test/reference/ft-text-antialias-none.ps3.argb32.ref.png b/test/reference/ft-text-antialias-none.ps3.argb32.ref.png
new file mode 100644 (file)
index 0000000..4f7ee83
Binary files /dev/null and b/test/reference/ft-text-antialias-none.ps3.argb32.ref.png differ
diff --git a/test/reference/ft-text-antialias-none.ref.png b/test/reference/ft-text-antialias-none.ref.png
new file mode 100644 (file)
index 0000000..cb0c132
Binary files /dev/null and b/test/reference/ft-text-antialias-none.ref.png differ
diff --git a/test/reference/ft-text-antialias-none.traps.argb32.ref.png b/test/reference/ft-text-antialias-none.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..c638c9e
Binary files /dev/null and b/test/reference/ft-text-antialias-none.traps.argb32.ref.png differ
diff --git a/test/reference/ft-text-antialias-none.traps.rgb24.ref.png b/test/reference/ft-text-antialias-none.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..c638c9e
Binary files /dev/null and b/test/reference/ft-text-antialias-none.traps.rgb24.ref.png differ
diff --git a/test/reference/ft-text-vertical-layout-type1.base.argb32.ref.png b/test/reference/ft-text-vertical-layout-type1.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..09c4cbb
Binary files /dev/null and b/test/reference/ft-text-vertical-layout-type1.base.argb32.ref.png differ
diff --git a/test/reference/ft-text-vertical-layout-type1.base.rgb24.ref.png b/test/reference/ft-text-vertical-layout-type1.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..09c4cbb
Binary files /dev/null and b/test/reference/ft-text-vertical-layout-type1.base.rgb24.ref.png differ
diff --git a/test/reference/ft-text-vertical-layout-type1.image16.ref.png b/test/reference/ft-text-vertical-layout-type1.image16.ref.png
new file mode 100644 (file)
index 0000000..4985907
Binary files /dev/null and b/test/reference/ft-text-vertical-layout-type1.image16.ref.png differ
diff --git a/test/reference/ft-text-vertical-layout-type1.pdf.ref.png b/test/reference/ft-text-vertical-layout-type1.pdf.ref.png
new file mode 100644 (file)
index 0000000..1f52ff2
Binary files /dev/null and b/test/reference/ft-text-vertical-layout-type1.pdf.ref.png differ
diff --git a/test/reference/ft-text-vertical-layout-type1.ps.ref.png b/test/reference/ft-text-vertical-layout-type1.ps.ref.png
new file mode 100644 (file)
index 0000000..bb99239
Binary files /dev/null and b/test/reference/ft-text-vertical-layout-type1.ps.ref.png differ
diff --git a/test/reference/ft-text-vertical-layout-type1.quartz.xfail.png b/test/reference/ft-text-vertical-layout-type1.quartz.xfail.png
new file mode 100644 (file)
index 0000000..a603b35
Binary files /dev/null and b/test/reference/ft-text-vertical-layout-type1.quartz.xfail.png differ
diff --git a/test/reference/ft-text-vertical-layout-type1.ref.png b/test/reference/ft-text-vertical-layout-type1.ref.png
new file mode 100644 (file)
index 0000000..8b1d96a
Binary files /dev/null and b/test/reference/ft-text-vertical-layout-type1.ref.png differ
diff --git a/test/reference/ft-text-vertical-layout-type1.svg.ref.png b/test/reference/ft-text-vertical-layout-type1.svg.ref.png
new file mode 100644 (file)
index 0000000..0be400c
Binary files /dev/null and b/test/reference/ft-text-vertical-layout-type1.svg.ref.png differ
diff --git a/test/reference/ft-text-vertical-layout-type1.traps.argb32.ref.png b/test/reference/ft-text-vertical-layout-type1.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..09c4cbb
Binary files /dev/null and b/test/reference/ft-text-vertical-layout-type1.traps.argb32.ref.png differ
diff --git a/test/reference/ft-text-vertical-layout-type1.traps.rgb24.ref.png b/test/reference/ft-text-vertical-layout-type1.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..09c4cbb
Binary files /dev/null and b/test/reference/ft-text-vertical-layout-type1.traps.rgb24.ref.png differ
diff --git a/test/reference/ft-text-vertical-layout-type1.xfail.png b/test/reference/ft-text-vertical-layout-type1.xfail.png
new file mode 100644 (file)
index 0000000..063bbd9
Binary files /dev/null and b/test/reference/ft-text-vertical-layout-type1.xfail.png differ
diff --git a/test/reference/ft-text-vertical-layout-type3.base.argb32.ref.png b/test/reference/ft-text-vertical-layout-type3.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..82374b7
Binary files /dev/null and b/test/reference/ft-text-vertical-layout-type3.base.argb32.ref.png differ
diff --git a/test/reference/ft-text-vertical-layout-type3.base.rgb24.ref.png b/test/reference/ft-text-vertical-layout-type3.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..82374b7
Binary files /dev/null and b/test/reference/ft-text-vertical-layout-type3.base.rgb24.ref.png differ
diff --git a/test/reference/ft-text-vertical-layout-type3.image16.ref.png b/test/reference/ft-text-vertical-layout-type3.image16.ref.png
new file mode 100644 (file)
index 0000000..a5c3c11
Binary files /dev/null and b/test/reference/ft-text-vertical-layout-type3.image16.ref.png differ
diff --git a/test/reference/ft-text-vertical-layout-type3.pdf.ref.png b/test/reference/ft-text-vertical-layout-type3.pdf.ref.png
new file mode 100644 (file)
index 0000000..a05ec1d
Binary files /dev/null and b/test/reference/ft-text-vertical-layout-type3.pdf.ref.png differ
diff --git a/test/reference/ft-text-vertical-layout-type3.ps.ref.png b/test/reference/ft-text-vertical-layout-type3.ps.ref.png
new file mode 100644 (file)
index 0000000..f8aafa2
Binary files /dev/null and b/test/reference/ft-text-vertical-layout-type3.ps.ref.png differ
diff --git a/test/reference/ft-text-vertical-layout-type3.quartz.ref.png b/test/reference/ft-text-vertical-layout-type3.quartz.ref.png
new file mode 100644 (file)
index 0000000..4a063f3
Binary files /dev/null and b/test/reference/ft-text-vertical-layout-type3.quartz.ref.png differ
diff --git a/test/reference/ft-text-vertical-layout-type3.ref.png b/test/reference/ft-text-vertical-layout-type3.ref.png
new file mode 100644 (file)
index 0000000..13651aa
Binary files /dev/null and b/test/reference/ft-text-vertical-layout-type3.ref.png differ
diff --git a/test/reference/ft-text-vertical-layout-type3.svg.ref.png b/test/reference/ft-text-vertical-layout-type3.svg.ref.png
new file mode 100644 (file)
index 0000000..cddb955
Binary files /dev/null and b/test/reference/ft-text-vertical-layout-type3.svg.ref.png differ
diff --git a/test/reference/ft-text-vertical-layout-type3.traps.argb32.ref.png b/test/reference/ft-text-vertical-layout-type3.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..82374b7
Binary files /dev/null and b/test/reference/ft-text-vertical-layout-type3.traps.argb32.ref.png differ
diff --git a/test/reference/ft-text-vertical-layout-type3.traps.rgb24.ref.png b/test/reference/ft-text-vertical-layout-type3.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..82374b7
Binary files /dev/null and b/test/reference/ft-text-vertical-layout-type3.traps.rgb24.ref.png differ
diff --git a/test/reference/get-group-target.base.argb32.ref.png b/test/reference/get-group-target.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..d162775
Binary files /dev/null and b/test/reference/get-group-target.base.argb32.ref.png differ
diff --git a/test/reference/get-group-target.base.rgb24.ref.png b/test/reference/get-group-target.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..d162775
Binary files /dev/null and b/test/reference/get-group-target.base.rgb24.ref.png differ
diff --git a/test/reference/get-group-target.ref.png b/test/reference/get-group-target.ref.png
new file mode 100644 (file)
index 0000000..316a93f
Binary files /dev/null and b/test/reference/get-group-target.ref.png differ
diff --git a/test/reference/get-group-target.traps.argb32.ref.png b/test/reference/get-group-target.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..d162775
Binary files /dev/null and b/test/reference/get-group-target.traps.argb32.ref.png differ
diff --git a/test/reference/get-group-target.traps.rgb24.ref.png b/test/reference/get-group-target.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..d162775
Binary files /dev/null and b/test/reference/get-group-target.traps.rgb24.ref.png differ
diff --git a/test/reference/gl-surface-source.argb32.ref.png b/test/reference/gl-surface-source.argb32.ref.png
new file mode 100644 (file)
index 0000000..0182972
Binary files /dev/null and b/test/reference/gl-surface-source.argb32.ref.png differ
diff --git a/test/reference/gl-surface-source.image16.ref.png b/test/reference/gl-surface-source.image16.ref.png
new file mode 100644 (file)
index 0000000..2a7460e
Binary files /dev/null and b/test/reference/gl-surface-source.image16.ref.png differ
diff --git a/test/reference/gl-surface-source.rgb24.ref.png b/test/reference/gl-surface-source.rgb24.ref.png
new file mode 100644 (file)
index 0000000..0d68a82
Binary files /dev/null and b/test/reference/gl-surface-source.rgb24.ref.png differ
diff --git a/test/reference/glyph-cache-pressure.base.argb32.ref.png b/test/reference/glyph-cache-pressure.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..14182f1
Binary files /dev/null and b/test/reference/glyph-cache-pressure.base.argb32.ref.png differ
diff --git a/test/reference/glyph-cache-pressure.base.rgb24.ref.png b/test/reference/glyph-cache-pressure.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..14182f1
Binary files /dev/null and b/test/reference/glyph-cache-pressure.base.rgb24.ref.png differ
diff --git a/test/reference/glyph-cache-pressure.image16.ref.png b/test/reference/glyph-cache-pressure.image16.ref.png
new file mode 100644 (file)
index 0000000..f3985bd
Binary files /dev/null and b/test/reference/glyph-cache-pressure.image16.ref.png differ
diff --git a/test/reference/glyph-cache-pressure.ps2.ref.png b/test/reference/glyph-cache-pressure.ps2.ref.png
new file mode 100644 (file)
index 0000000..88fa447
Binary files /dev/null and b/test/reference/glyph-cache-pressure.ps2.ref.png differ
diff --git a/test/reference/glyph-cache-pressure.ps3.ref.png b/test/reference/glyph-cache-pressure.ps3.ref.png
new file mode 100644 (file)
index 0000000..88fa447
Binary files /dev/null and b/test/reference/glyph-cache-pressure.ps3.ref.png differ
diff --git a/test/reference/glyph-cache-pressure.quartz.ref.png b/test/reference/glyph-cache-pressure.quartz.ref.png
new file mode 100644 (file)
index 0000000..6291e84
Binary files /dev/null and b/test/reference/glyph-cache-pressure.quartz.ref.png differ
diff --git a/test/reference/glyph-cache-pressure.ref.png b/test/reference/glyph-cache-pressure.ref.png
new file mode 100644 (file)
index 0000000..14182f1
Binary files /dev/null and b/test/reference/glyph-cache-pressure.ref.png differ
diff --git a/test/reference/glyph-cache-pressure.traps.argb32.ref.png b/test/reference/glyph-cache-pressure.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..14182f1
Binary files /dev/null and b/test/reference/glyph-cache-pressure.traps.argb32.ref.png differ
diff --git a/test/reference/glyph-cache-pressure.traps.rgb24.ref.png b/test/reference/glyph-cache-pressure.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..14182f1
Binary files /dev/null and b/test/reference/glyph-cache-pressure.traps.rgb24.ref.png differ
diff --git a/test/reference/gradient-alpha.base.argb32.ref.png b/test/reference/gradient-alpha.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..f64b26a
Binary files /dev/null and b/test/reference/gradient-alpha.base.argb32.ref.png differ
diff --git a/test/reference/gradient-alpha.base.rgb24.ref.png b/test/reference/gradient-alpha.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..3c7258d
Binary files /dev/null and b/test/reference/gradient-alpha.base.rgb24.ref.png differ
diff --git a/test/reference/gradient-alpha.ps2.argb32.ref.png b/test/reference/gradient-alpha.ps2.argb32.ref.png
new file mode 100644 (file)
index 0000000..37eafba
Binary files /dev/null and b/test/reference/gradient-alpha.ps2.argb32.ref.png differ
diff --git a/test/reference/gradient-alpha.ps2.rgb24.ref.png b/test/reference/gradient-alpha.ps2.rgb24.ref.png
new file mode 100644 (file)
index 0000000..2432c29
Binary files /dev/null and b/test/reference/gradient-alpha.ps2.rgb24.ref.png differ
diff --git a/test/reference/gradient-alpha.ps3.argb32.ref.png b/test/reference/gradient-alpha.ps3.argb32.ref.png
new file mode 100644 (file)
index 0000000..37eafba
Binary files /dev/null and b/test/reference/gradient-alpha.ps3.argb32.ref.png differ
diff --git a/test/reference/gradient-alpha.ps3.rgb24.ref.png b/test/reference/gradient-alpha.ps3.rgb24.ref.png
new file mode 100644 (file)
index 0000000..2432c29
Binary files /dev/null and b/test/reference/gradient-alpha.ps3.rgb24.ref.png differ
diff --git a/test/reference/gradient-alpha.ref.png b/test/reference/gradient-alpha.ref.png
new file mode 100644 (file)
index 0000000..43a0509
Binary files /dev/null and b/test/reference/gradient-alpha.ref.png differ
diff --git a/test/reference/gradient-alpha.rgb24.ref.png b/test/reference/gradient-alpha.rgb24.ref.png
new file mode 100644 (file)
index 0000000..3c7258d
Binary files /dev/null and b/test/reference/gradient-alpha.rgb24.ref.png differ
diff --git a/test/reference/gradient-alpha.traps.argb32.ref.png b/test/reference/gradient-alpha.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..f64b26a
Binary files /dev/null and b/test/reference/gradient-alpha.traps.argb32.ref.png differ
diff --git a/test/reference/gradient-alpha.traps.rgb24.ref.png b/test/reference/gradient-alpha.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..3c7258d
Binary files /dev/null and b/test/reference/gradient-alpha.traps.rgb24.ref.png differ
diff --git a/test/reference/gradient-constant-alpha.base.argb32.ref.png b/test/reference/gradient-constant-alpha.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..7f49e2c
Binary files /dev/null and b/test/reference/gradient-constant-alpha.base.argb32.ref.png differ
diff --git a/test/reference/gradient-constant-alpha.base.rgb24.ref.png b/test/reference/gradient-constant-alpha.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..69cd596
Binary files /dev/null and b/test/reference/gradient-constant-alpha.base.rgb24.ref.png differ
diff --git a/test/reference/gradient-constant-alpha.ps3.ref.png b/test/reference/gradient-constant-alpha.ps3.ref.png
new file mode 100644 (file)
index 0000000..7089f4f
Binary files /dev/null and b/test/reference/gradient-constant-alpha.ps3.ref.png differ
diff --git a/test/reference/gradient-constant-alpha.ps3.rgb24.ref.png b/test/reference/gradient-constant-alpha.ps3.rgb24.ref.png
new file mode 100644 (file)
index 0000000..5962925
Binary files /dev/null and b/test/reference/gradient-constant-alpha.ps3.rgb24.ref.png differ
diff --git a/test/reference/gradient-constant-alpha.quartz.argb32.ref.png b/test/reference/gradient-constant-alpha.quartz.argb32.ref.png
new file mode 100644 (file)
index 0000000..a426f5a
Binary files /dev/null and b/test/reference/gradient-constant-alpha.quartz.argb32.ref.png differ
diff --git a/test/reference/gradient-constant-alpha.ref.png b/test/reference/gradient-constant-alpha.ref.png
new file mode 100644 (file)
index 0000000..9a24e8c
Binary files /dev/null and b/test/reference/gradient-constant-alpha.ref.png differ
diff --git a/test/reference/gradient-constant-alpha.rgb24.ref.png b/test/reference/gradient-constant-alpha.rgb24.ref.png
new file mode 100644 (file)
index 0000000..69cd596
Binary files /dev/null and b/test/reference/gradient-constant-alpha.rgb24.ref.png differ
diff --git a/test/reference/gradient-constant-alpha.traps.argb32.ref.png b/test/reference/gradient-constant-alpha.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..7f49e2c
Binary files /dev/null and b/test/reference/gradient-constant-alpha.traps.argb32.ref.png differ
diff --git a/test/reference/gradient-constant-alpha.traps.rgb24.ref.png b/test/reference/gradient-constant-alpha.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..69cd596
Binary files /dev/null and b/test/reference/gradient-constant-alpha.traps.rgb24.ref.png differ
diff --git a/test/reference/gradient-zero-stops-mask.argb32.ref.png b/test/reference/gradient-zero-stops-mask.argb32.ref.png
new file mode 100644 (file)
index 0000000..0a50f4c
Binary files /dev/null and b/test/reference/gradient-zero-stops-mask.argb32.ref.png differ
diff --git a/test/reference/gradient-zero-stops-mask.base.argb32.ref.png b/test/reference/gradient-zero-stops-mask.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..0a50f4c
Binary files /dev/null and b/test/reference/gradient-zero-stops-mask.base.argb32.ref.png differ
diff --git a/test/reference/gradient-zero-stops-mask.base.rgb24.ref.png b/test/reference/gradient-zero-stops-mask.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..21465ce
Binary files /dev/null and b/test/reference/gradient-zero-stops-mask.base.rgb24.ref.png differ
diff --git a/test/reference/gradient-zero-stops-mask.rgb24.ref.png b/test/reference/gradient-zero-stops-mask.rgb24.ref.png
new file mode 100644 (file)
index 0000000..21465ce
Binary files /dev/null and b/test/reference/gradient-zero-stops-mask.rgb24.ref.png differ
diff --git a/test/reference/gradient-zero-stops-mask.traps.argb32.ref.png b/test/reference/gradient-zero-stops-mask.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..0a50f4c
Binary files /dev/null and b/test/reference/gradient-zero-stops-mask.traps.argb32.ref.png differ
diff --git a/test/reference/gradient-zero-stops-mask.traps.rgb24.ref.png b/test/reference/gradient-zero-stops-mask.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..21465ce
Binary files /dev/null and b/test/reference/gradient-zero-stops-mask.traps.rgb24.ref.png differ
diff --git a/test/reference/gradient-zero-stops.base.argb32.ref.png b/test/reference/gradient-zero-stops.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..0a50f4c
Binary files /dev/null and b/test/reference/gradient-zero-stops.base.argb32.ref.png differ
diff --git a/test/reference/gradient-zero-stops.base.rgb24.ref.png b/test/reference/gradient-zero-stops.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..21465ce
Binary files /dev/null and b/test/reference/gradient-zero-stops.base.rgb24.ref.png differ
diff --git a/test/reference/gradient-zero-stops.ref.png b/test/reference/gradient-zero-stops.ref.png
new file mode 100644 (file)
index 0000000..3f18670
Binary files /dev/null and b/test/reference/gradient-zero-stops.ref.png differ
diff --git a/test/reference/gradient-zero-stops.rgb24.ref.png b/test/reference/gradient-zero-stops.rgb24.ref.png
new file mode 100644 (file)
index 0000000..eb24e4b
Binary files /dev/null and b/test/reference/gradient-zero-stops.rgb24.ref.png differ
diff --git a/test/reference/gradient-zero-stops.traps.argb32.ref.png b/test/reference/gradient-zero-stops.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..0a50f4c
Binary files /dev/null and b/test/reference/gradient-zero-stops.traps.argb32.ref.png differ
diff --git a/test/reference/gradient-zero-stops.traps.rgb24.ref.png b/test/reference/gradient-zero-stops.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..21465ce
Binary files /dev/null and b/test/reference/gradient-zero-stops.traps.rgb24.ref.png differ
diff --git a/test/reference/group-clip.base.argb32.ref.png b/test/reference/group-clip.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..7b8a753
Binary files /dev/null and b/test/reference/group-clip.base.argb32.ref.png differ
diff --git a/test/reference/group-clip.base.rgb24.ref.png b/test/reference/group-clip.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..7b8a753
Binary files /dev/null and b/test/reference/group-clip.base.rgb24.ref.png differ
diff --git a/test/reference/group-clip.image16.ref.png b/test/reference/group-clip.image16.ref.png
new file mode 100644 (file)
index 0000000..98b66ef
Binary files /dev/null and b/test/reference/group-clip.image16.ref.png differ
diff --git a/test/reference/group-clip.ref.png b/test/reference/group-clip.ref.png
new file mode 100644 (file)
index 0000000..7b8a753
Binary files /dev/null and b/test/reference/group-clip.ref.png differ
diff --git a/test/reference/group-clip.traps.argb32.ref.png b/test/reference/group-clip.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..7b8a753
Binary files /dev/null and b/test/reference/group-clip.traps.argb32.ref.png differ
diff --git a/test/reference/group-clip.traps.rgb24.ref.png b/test/reference/group-clip.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..7b8a753
Binary files /dev/null and b/test/reference/group-clip.traps.rgb24.ref.png differ
diff --git a/test/reference/group-paint.base.argb32.ref.png b/test/reference/group-paint.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..1ed070c
Binary files /dev/null and b/test/reference/group-paint.base.argb32.ref.png differ
diff --git a/test/reference/group-paint.base.rgb24.ref.png b/test/reference/group-paint.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..1ed070c
Binary files /dev/null and b/test/reference/group-paint.base.rgb24.ref.png differ
diff --git a/test/reference/group-paint.ref.png b/test/reference/group-paint.ref.png
new file mode 100644 (file)
index 0000000..f2e111b
Binary files /dev/null and b/test/reference/group-paint.ref.png differ
diff --git a/test/reference/group-paint.traps.argb32.ref.png b/test/reference/group-paint.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..1ed070c
Binary files /dev/null and b/test/reference/group-paint.traps.argb32.ref.png differ
diff --git a/test/reference/group-paint.traps.rgb24.ref.png b/test/reference/group-paint.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..1ed070c
Binary files /dev/null and b/test/reference/group-paint.traps.rgb24.ref.png differ
diff --git a/test/reference/group-unaligned.base.argb32.ref.png b/test/reference/group-unaligned.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..451e171
Binary files /dev/null and b/test/reference/group-unaligned.base.argb32.ref.png differ
diff --git a/test/reference/group-unaligned.base.rgb24.ref.png b/test/reference/group-unaligned.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..451e171
Binary files /dev/null and b/test/reference/group-unaligned.base.rgb24.ref.png differ
diff --git a/test/reference/group-unaligned.image16.ref.png b/test/reference/group-unaligned.image16.ref.png
new file mode 100644 (file)
index 0000000..6a60a6e
Binary files /dev/null and b/test/reference/group-unaligned.image16.ref.png differ
diff --git a/test/reference/group-unaligned.ps.ref.png b/test/reference/group-unaligned.ps.ref.png
new file mode 100644 (file)
index 0000000..f108998
Binary files /dev/null and b/test/reference/group-unaligned.ps.ref.png differ
diff --git a/test/reference/group-unaligned.ps.rgb24.xfail.png b/test/reference/group-unaligned.ps.rgb24.xfail.png
new file mode 100644 (file)
index 0000000..5672cb0
Binary files /dev/null and b/test/reference/group-unaligned.ps.rgb24.xfail.png differ
diff --git a/test/reference/group-unaligned.quartz.ref.png b/test/reference/group-unaligned.quartz.ref.png
new file mode 100644 (file)
index 0000000..1711025
Binary files /dev/null and b/test/reference/group-unaligned.quartz.ref.png differ
diff --git a/test/reference/group-unaligned.ref.png b/test/reference/group-unaligned.ref.png
new file mode 100644 (file)
index 0000000..700e6fd
Binary files /dev/null and b/test/reference/group-unaligned.ref.png differ
diff --git a/test/reference/group-unaligned.svg.argb32.xfail.png b/test/reference/group-unaligned.svg.argb32.xfail.png
new file mode 100644 (file)
index 0000000..01c34be
Binary files /dev/null and b/test/reference/group-unaligned.svg.argb32.xfail.png differ
diff --git a/test/reference/group-unaligned.svg.rgb24.xfail.png b/test/reference/group-unaligned.svg.rgb24.xfail.png
new file mode 100644 (file)
index 0000000..c0f1861
Binary files /dev/null and b/test/reference/group-unaligned.svg.rgb24.xfail.png differ
diff --git a/test/reference/group-unaligned.traps.argb32.ref.png b/test/reference/group-unaligned.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..451e171
Binary files /dev/null and b/test/reference/group-unaligned.traps.argb32.ref.png differ
diff --git a/test/reference/group-unaligned.traps.rgb24.ref.png b/test/reference/group-unaligned.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..451e171
Binary files /dev/null and b/test/reference/group-unaligned.traps.rgb24.ref.png differ
diff --git a/test/reference/group-unaligned.xlib-fallback.ref.png b/test/reference/group-unaligned.xlib-fallback.ref.png
new file mode 100644 (file)
index 0000000..5ddbc16
Binary files /dev/null and b/test/reference/group-unaligned.xlib-fallback.ref.png differ
diff --git a/test/reference/halo-transform.base.argb32.ref.png b/test/reference/halo-transform.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..ac314b0
Binary files /dev/null and b/test/reference/halo-transform.base.argb32.ref.png differ
diff --git a/test/reference/halo-transform.base.rgb24.ref.png b/test/reference/halo-transform.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..ac314b0
Binary files /dev/null and b/test/reference/halo-transform.base.rgb24.ref.png differ
diff --git a/test/reference/halo-transform.image16.ref.png b/test/reference/halo-transform.image16.ref.png
new file mode 100644 (file)
index 0000000..b2268f0
Binary files /dev/null and b/test/reference/halo-transform.image16.ref.png differ
diff --git a/test/reference/halo-transform.ps.ref.png b/test/reference/halo-transform.ps.ref.png
new file mode 100644 (file)
index 0000000..89e1f76
Binary files /dev/null and b/test/reference/halo-transform.ps.ref.png differ
diff --git a/test/reference/halo-transform.quartz.ref.png b/test/reference/halo-transform.quartz.ref.png
new file mode 100644 (file)
index 0000000..91a99ba
Binary files /dev/null and b/test/reference/halo-transform.quartz.ref.png differ
diff --git a/test/reference/halo-transform.ref.png b/test/reference/halo-transform.ref.png
new file mode 100644 (file)
index 0000000..cd84718
Binary files /dev/null and b/test/reference/halo-transform.ref.png differ
diff --git a/test/reference/halo-transform.traps.argb32.ref.png b/test/reference/halo-transform.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..ac314b0
Binary files /dev/null and b/test/reference/halo-transform.traps.argb32.ref.png differ
diff --git a/test/reference/halo-transform.traps.rgb24.ref.png b/test/reference/halo-transform.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..ac314b0
Binary files /dev/null and b/test/reference/halo-transform.traps.rgb24.ref.png differ
diff --git a/test/reference/halo.base.argb32.ref.png b/test/reference/halo.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..bc971d8
Binary files /dev/null and b/test/reference/halo.base.argb32.ref.png differ
diff --git a/test/reference/halo.base.rgb24.ref.png b/test/reference/halo.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..bc971d8
Binary files /dev/null and b/test/reference/halo.base.rgb24.ref.png differ
diff --git a/test/reference/halo.image16.ref.png b/test/reference/halo.image16.ref.png
new file mode 100644 (file)
index 0000000..a813cd9
Binary files /dev/null and b/test/reference/halo.image16.ref.png differ
diff --git a/test/reference/halo.ps.ref.png b/test/reference/halo.ps.ref.png
new file mode 100644 (file)
index 0000000..1426d2e
Binary files /dev/null and b/test/reference/halo.ps.ref.png differ
diff --git a/test/reference/halo.quartz.ref.png b/test/reference/halo.quartz.ref.png
new file mode 100644 (file)
index 0000000..c5cf999
Binary files /dev/null and b/test/reference/halo.quartz.ref.png differ
diff --git a/test/reference/halo.ref.png b/test/reference/halo.ref.png
new file mode 100644 (file)
index 0000000..ebe0c61
Binary files /dev/null and b/test/reference/halo.ref.png differ
diff --git a/test/reference/halo.traps.argb32.ref.png b/test/reference/halo.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..bc971d8
Binary files /dev/null and b/test/reference/halo.traps.argb32.ref.png differ
diff --git a/test/reference/halo.traps.rgb24.ref.png b/test/reference/halo.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..bc971d8
Binary files /dev/null and b/test/reference/halo.traps.rgb24.ref.png differ
diff --git a/test/reference/hatchings.base.argb32.ref.png b/test/reference/hatchings.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..26cc637
Binary files /dev/null and b/test/reference/hatchings.base.argb32.ref.png differ
diff --git a/test/reference/hatchings.base.rgb24.ref.png b/test/reference/hatchings.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..26cc637
Binary files /dev/null and b/test/reference/hatchings.base.rgb24.ref.png differ
diff --git a/test/reference/hatchings.ref.png b/test/reference/hatchings.ref.png
new file mode 100644 (file)
index 0000000..a8af87c
Binary files /dev/null and b/test/reference/hatchings.ref.png differ
diff --git a/test/reference/hatchings.traps.argb32.ref.png b/test/reference/hatchings.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..3b72949
Binary files /dev/null and b/test/reference/hatchings.traps.argb32.ref.png differ
diff --git a/test/reference/hatchings.traps.rgb24.ref.png b/test/reference/hatchings.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..3b72949
Binary files /dev/null and b/test/reference/hatchings.traps.rgb24.ref.png differ
diff --git a/test/reference/horizontal-clip.base.argb32.ref.png b/test/reference/horizontal-clip.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..43dd07f
Binary files /dev/null and b/test/reference/horizontal-clip.base.argb32.ref.png differ
diff --git a/test/reference/horizontal-clip.base.rgb24.ref.png b/test/reference/horizontal-clip.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..43dd07f
Binary files /dev/null and b/test/reference/horizontal-clip.base.rgb24.ref.png differ
diff --git a/test/reference/horizontal-clip.ref.png b/test/reference/horizontal-clip.ref.png
new file mode 100644 (file)
index 0000000..f07e035
Binary files /dev/null and b/test/reference/horizontal-clip.ref.png differ
diff --git a/test/reference/horizontal-clip.traps.argb32.ref.png b/test/reference/horizontal-clip.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..43dd07f
Binary files /dev/null and b/test/reference/horizontal-clip.traps.argb32.ref.png differ
diff --git a/test/reference/horizontal-clip.traps.rgb24.ref.png b/test/reference/horizontal-clip.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..43dd07f
Binary files /dev/null and b/test/reference/horizontal-clip.traps.rgb24.ref.png differ
diff --git a/test/reference/huge-linear.base.argb32.ref.png b/test/reference/huge-linear.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..f89217b
Binary files /dev/null and b/test/reference/huge-linear.base.argb32.ref.png differ
diff --git a/test/reference/huge-linear.base.rgb24.ref.png b/test/reference/huge-linear.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..f89217b
Binary files /dev/null and b/test/reference/huge-linear.base.rgb24.ref.png differ
diff --git a/test/reference/huge-linear.image16.ref.png b/test/reference/huge-linear.image16.ref.png
new file mode 100644 (file)
index 0000000..56dc58a
Binary files /dev/null and b/test/reference/huge-linear.image16.ref.png differ
diff --git a/test/reference/huge-linear.pdf.ref.png b/test/reference/huge-linear.pdf.ref.png
new file mode 100644 (file)
index 0000000..8313470
Binary files /dev/null and b/test/reference/huge-linear.pdf.ref.png differ
diff --git a/test/reference/huge-linear.ps3.ref.png b/test/reference/huge-linear.ps3.ref.png
new file mode 100644 (file)
index 0000000..d55239b
Binary files /dev/null and b/test/reference/huge-linear.ps3.ref.png differ
diff --git a/test/reference/huge-linear.quartz.ref.png b/test/reference/huge-linear.quartz.ref.png
new file mode 100644 (file)
index 0000000..3d12f7b
Binary files /dev/null and b/test/reference/huge-linear.quartz.ref.png differ
diff --git a/test/reference/huge-linear.ref.png b/test/reference/huge-linear.ref.png
new file mode 100644 (file)
index 0000000..68f86b4
Binary files /dev/null and b/test/reference/huge-linear.ref.png differ
diff --git a/test/reference/huge-linear.traps.argb32.ref.png b/test/reference/huge-linear.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..f89217b
Binary files /dev/null and b/test/reference/huge-linear.traps.argb32.ref.png differ
diff --git a/test/reference/huge-linear.traps.rgb24.ref.png b/test/reference/huge-linear.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..f89217b
Binary files /dev/null and b/test/reference/huge-linear.traps.rgb24.ref.png differ
diff --git a/test/reference/huge-radial.base.argb32.ref.png b/test/reference/huge-radial.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..541bb30
Binary files /dev/null and b/test/reference/huge-radial.base.argb32.ref.png differ
diff --git a/test/reference/huge-radial.base.rgb24.ref.png b/test/reference/huge-radial.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..541bb30
Binary files /dev/null and b/test/reference/huge-radial.base.rgb24.ref.png differ
diff --git a/test/reference/huge-radial.image16.ref.png b/test/reference/huge-radial.image16.ref.png
new file mode 100644 (file)
index 0000000..3913190
Binary files /dev/null and b/test/reference/huge-radial.image16.ref.png differ
diff --git a/test/reference/huge-radial.pdf.argb32.ref.png b/test/reference/huge-radial.pdf.argb32.ref.png
new file mode 100644 (file)
index 0000000..aa710ec
Binary files /dev/null and b/test/reference/huge-radial.pdf.argb32.ref.png differ
diff --git a/test/reference/huge-radial.pdf.rgb24.ref.png b/test/reference/huge-radial.pdf.rgb24.ref.png
new file mode 100644 (file)
index 0000000..8cebd80
Binary files /dev/null and b/test/reference/huge-radial.pdf.rgb24.ref.png differ
diff --git a/test/reference/huge-radial.ps3.ref.png b/test/reference/huge-radial.ps3.ref.png
new file mode 100644 (file)
index 0000000..c231948
Binary files /dev/null and b/test/reference/huge-radial.ps3.ref.png differ
diff --git a/test/reference/huge-radial.quartz.ref.png b/test/reference/huge-radial.quartz.ref.png
new file mode 100644 (file)
index 0000000..d823f86
Binary files /dev/null and b/test/reference/huge-radial.quartz.ref.png differ
diff --git a/test/reference/huge-radial.ref.png b/test/reference/huge-radial.ref.png
new file mode 100644 (file)
index 0000000..541bb30
Binary files /dev/null and b/test/reference/huge-radial.ref.png differ
diff --git a/test/reference/huge-radial.traps.argb32.ref.png b/test/reference/huge-radial.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..541bb30
Binary files /dev/null and b/test/reference/huge-radial.traps.argb32.ref.png differ
diff --git a/test/reference/huge-radial.traps.rgb24.ref.png b/test/reference/huge-radial.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..541bb30
Binary files /dev/null and b/test/reference/huge-radial.traps.rgb24.ref.png differ
diff --git a/test/reference/image-bug-710072-aligned.base.argb32.ref.png b/test/reference/image-bug-710072-aligned.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..42abd20
Binary files /dev/null and b/test/reference/image-bug-710072-aligned.base.argb32.ref.png differ
diff --git a/test/reference/image-bug-710072-aligned.base.rgb24.ref.png b/test/reference/image-bug-710072-aligned.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..42abd20
Binary files /dev/null and b/test/reference/image-bug-710072-aligned.base.rgb24.ref.png differ
diff --git a/test/reference/image-bug-710072-aligned.ref.png b/test/reference/image-bug-710072-aligned.ref.png
new file mode 100644 (file)
index 0000000..42abd20
Binary files /dev/null and b/test/reference/image-bug-710072-aligned.ref.png differ
diff --git a/test/reference/image-bug-710072-aligned.traps.argb32.ref.png b/test/reference/image-bug-710072-aligned.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..42abd20
Binary files /dev/null and b/test/reference/image-bug-710072-aligned.traps.argb32.ref.png differ
diff --git a/test/reference/image-bug-710072-aligned.traps.rgb24.ref.png b/test/reference/image-bug-710072-aligned.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..42abd20
Binary files /dev/null and b/test/reference/image-bug-710072-aligned.traps.rgb24.ref.png differ
diff --git a/test/reference/image-bug-710072-unaligned.base.argb32.ref.png b/test/reference/image-bug-710072-unaligned.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..0a5a6e6
Binary files /dev/null and b/test/reference/image-bug-710072-unaligned.base.argb32.ref.png differ
diff --git a/test/reference/image-bug-710072-unaligned.base.rgb24.ref.png b/test/reference/image-bug-710072-unaligned.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..0a5a6e6
Binary files /dev/null and b/test/reference/image-bug-710072-unaligned.base.rgb24.ref.png differ
diff --git a/test/reference/image-bug-710072-unaligned.ref.png b/test/reference/image-bug-710072-unaligned.ref.png
new file mode 100644 (file)
index 0000000..13efa30
Binary files /dev/null and b/test/reference/image-bug-710072-unaligned.ref.png differ
diff --git a/test/reference/image-bug-710072-unaligned.traps.argb32.ref.png b/test/reference/image-bug-710072-unaligned.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..0a5a6e6
Binary files /dev/null and b/test/reference/image-bug-710072-unaligned.traps.argb32.ref.png differ
diff --git a/test/reference/image-bug-710072-unaligned.traps.rgb24.ref.png b/test/reference/image-bug-710072-unaligned.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..0a5a6e6
Binary files /dev/null and b/test/reference/image-bug-710072-unaligned.traps.rgb24.ref.png differ
diff --git a/test/reference/image-surface-source.argb32.ref.png b/test/reference/image-surface-source.argb32.ref.png
new file mode 100644 (file)
index 0000000..0182972
Binary files /dev/null and b/test/reference/image-surface-source.argb32.ref.png differ
diff --git a/test/reference/image-surface-source.base.argb32.ref.png b/test/reference/image-surface-source.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..0182972
Binary files /dev/null and b/test/reference/image-surface-source.base.argb32.ref.png differ
diff --git a/test/reference/image-surface-source.base.rgb24.ref.png b/test/reference/image-surface-source.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..0d68a82
Binary files /dev/null and b/test/reference/image-surface-source.base.rgb24.ref.png differ
diff --git a/test/reference/image-surface-source.image16.ref.png b/test/reference/image-surface-source.image16.ref.png
new file mode 100644 (file)
index 0000000..2a7460e
Binary files /dev/null and b/test/reference/image-surface-source.image16.ref.png differ
diff --git a/test/reference/image-surface-source.ps2.ref.png b/test/reference/image-surface-source.ps2.ref.png
new file mode 100644 (file)
index 0000000..1023158
Binary files /dev/null and b/test/reference/image-surface-source.ps2.ref.png differ
diff --git a/test/reference/image-surface-source.ps3.ref.png b/test/reference/image-surface-source.ps3.ref.png
new file mode 100644 (file)
index 0000000..1023158
Binary files /dev/null and b/test/reference/image-surface-source.ps3.ref.png differ
diff --git a/test/reference/image-surface-source.rgb24.ref.png b/test/reference/image-surface-source.rgb24.ref.png
new file mode 100644 (file)
index 0000000..0d68a82
Binary files /dev/null and b/test/reference/image-surface-source.rgb24.ref.png differ
diff --git a/test/reference/image-surface-source.svg12.argb32.xfail.png b/test/reference/image-surface-source.svg12.argb32.xfail.png
new file mode 100644 (file)
index 0000000..6ebcaf9
Binary files /dev/null and b/test/reference/image-surface-source.svg12.argb32.xfail.png differ
diff --git a/test/reference/image-surface-source.svg12.rgb24.xfail.png b/test/reference/image-surface-source.svg12.rgb24.xfail.png
new file mode 100644 (file)
index 0000000..6ebcaf9
Binary files /dev/null and b/test/reference/image-surface-source.svg12.rgb24.xfail.png differ
diff --git a/test/reference/image-surface-source.traps.argb32.ref.png b/test/reference/image-surface-source.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..0182972
Binary files /dev/null and b/test/reference/image-surface-source.traps.argb32.ref.png differ
diff --git a/test/reference/image-surface-source.traps.rgb24.ref.png b/test/reference/image-surface-source.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..0d68a82
Binary files /dev/null and b/test/reference/image-surface-source.traps.rgb24.ref.png differ
diff --git a/test/reference/implicit-close.base.argb32.ref.png b/test/reference/implicit-close.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..fdd7561
Binary files /dev/null and b/test/reference/implicit-close.base.argb32.ref.png differ
diff --git a/test/reference/implicit-close.base.rgb24.ref.png b/test/reference/implicit-close.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..fdd7561
Binary files /dev/null and b/test/reference/implicit-close.base.rgb24.ref.png differ
diff --git a/test/reference/implicit-close.ps.ref.png b/test/reference/implicit-close.ps.ref.png
new file mode 100644 (file)
index 0000000..66baf24
Binary files /dev/null and b/test/reference/implicit-close.ps.ref.png differ
diff --git a/test/reference/implicit-close.ref.png b/test/reference/implicit-close.ref.png
new file mode 100644 (file)
index 0000000..f15f8a3
Binary files /dev/null and b/test/reference/implicit-close.ref.png differ
diff --git a/test/reference/implicit-close.traps.argb32.ref.png b/test/reference/implicit-close.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..fdd7561
Binary files /dev/null and b/test/reference/implicit-close.traps.argb32.ref.png differ
diff --git a/test/reference/implicit-close.traps.rgb24.ref.png b/test/reference/implicit-close.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..fdd7561
Binary files /dev/null and b/test/reference/implicit-close.traps.rgb24.ref.png differ
diff --git a/test/reference/infinite-join.base.argb32.ref.png b/test/reference/infinite-join.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..9e7572b
Binary files /dev/null and b/test/reference/infinite-join.base.argb32.ref.png differ
diff --git a/test/reference/infinite-join.base.rgb24.ref.png b/test/reference/infinite-join.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..9e7572b
Binary files /dev/null and b/test/reference/infinite-join.base.rgb24.ref.png differ
diff --git a/test/reference/infinite-join.ps2.ref.png b/test/reference/infinite-join.ps2.ref.png
new file mode 100644 (file)
index 0000000..6fba8f4
Binary files /dev/null and b/test/reference/infinite-join.ps2.ref.png differ
diff --git a/test/reference/infinite-join.ps3.ref.png b/test/reference/infinite-join.ps3.ref.png
new file mode 100644 (file)
index 0000000..6fba8f4
Binary files /dev/null and b/test/reference/infinite-join.ps3.ref.png differ
diff --git a/test/reference/infinite-join.ref.png b/test/reference/infinite-join.ref.png
new file mode 100644 (file)
index 0000000..60bf49e
Binary files /dev/null and b/test/reference/infinite-join.ref.png differ
diff --git a/test/reference/infinite-join.traps.argb32.ref.png b/test/reference/infinite-join.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..9e7572b
Binary files /dev/null and b/test/reference/infinite-join.traps.argb32.ref.png differ
diff --git a/test/reference/infinite-join.traps.rgb24.ref.png b/test/reference/infinite-join.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..9e7572b
Binary files /dev/null and b/test/reference/infinite-join.traps.rgb24.ref.png differ
diff --git a/test/reference/inverse-text.base.argb32.ref.png b/test/reference/inverse-text.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..40defe6
Binary files /dev/null and b/test/reference/inverse-text.base.argb32.ref.png differ
diff --git a/test/reference/inverse-text.base.rgb24.ref.png b/test/reference/inverse-text.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..40defe6
Binary files /dev/null and b/test/reference/inverse-text.base.rgb24.ref.png differ
diff --git a/test/reference/inverse-text.image16.ref.png b/test/reference/inverse-text.image16.ref.png
new file mode 100644 (file)
index 0000000..a24266d
Binary files /dev/null and b/test/reference/inverse-text.image16.ref.png differ
diff --git a/test/reference/inverse-text.ps.ref.png b/test/reference/inverse-text.ps.ref.png
new file mode 100644 (file)
index 0000000..6043684
Binary files /dev/null and b/test/reference/inverse-text.ps.ref.png differ
diff --git a/test/reference/inverse-text.quartz.ref.png b/test/reference/inverse-text.quartz.ref.png
new file mode 100644 (file)
index 0000000..df0ea57
Binary files /dev/null and b/test/reference/inverse-text.quartz.ref.png differ
diff --git a/test/reference/inverse-text.ref.png b/test/reference/inverse-text.ref.png
new file mode 100644 (file)
index 0000000..40defe6
Binary files /dev/null and b/test/reference/inverse-text.ref.png differ
diff --git a/test/reference/inverse-text.traps.argb32.ref.png b/test/reference/inverse-text.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..40defe6
Binary files /dev/null and b/test/reference/inverse-text.traps.argb32.ref.png differ
diff --git a/test/reference/inverse-text.traps.rgb24.ref.png b/test/reference/inverse-text.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..40defe6
Binary files /dev/null and b/test/reference/inverse-text.traps.rgb24.ref.png differ
diff --git a/test/reference/joins-loop.base.argb32.ref.png b/test/reference/joins-loop.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..d1d3cff
Binary files /dev/null and b/test/reference/joins-loop.base.argb32.ref.png differ
diff --git a/test/reference/joins-loop.base.rgb24.ref.png b/test/reference/joins-loop.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..d1d3cff
Binary files /dev/null and b/test/reference/joins-loop.base.rgb24.ref.png differ
diff --git a/test/reference/joins-loop.ref.png b/test/reference/joins-loop.ref.png
new file mode 100644 (file)
index 0000000..144734e
Binary files /dev/null and b/test/reference/joins-loop.ref.png differ
diff --git a/test/reference/joins-loop.traps.argb32.ref.png b/test/reference/joins-loop.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..d1d3cff
Binary files /dev/null and b/test/reference/joins-loop.traps.argb32.ref.png differ
diff --git a/test/reference/joins-loop.traps.rgb24.ref.png b/test/reference/joins-loop.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..d1d3cff
Binary files /dev/null and b/test/reference/joins-loop.traps.rgb24.ref.png differ
diff --git a/test/reference/joins-retrace.base.argb32.ref.png b/test/reference/joins-retrace.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..a2e8bca
Binary files /dev/null and b/test/reference/joins-retrace.base.argb32.ref.png differ
diff --git a/test/reference/joins-retrace.base.rgb24.ref.png b/test/reference/joins-retrace.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..a2e8bca
Binary files /dev/null and b/test/reference/joins-retrace.base.rgb24.ref.png differ
diff --git a/test/reference/joins-retrace.ref.png b/test/reference/joins-retrace.ref.png
new file mode 100644 (file)
index 0000000..d036d7c
Binary files /dev/null and b/test/reference/joins-retrace.ref.png differ
diff --git a/test/reference/joins-retrace.traps.argb32.ref.png b/test/reference/joins-retrace.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..a2e8bca
Binary files /dev/null and b/test/reference/joins-retrace.traps.argb32.ref.png differ
diff --git a/test/reference/joins-retrace.traps.rgb24.ref.png b/test/reference/joins-retrace.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..a2e8bca
Binary files /dev/null and b/test/reference/joins-retrace.traps.rgb24.ref.png differ
diff --git a/test/reference/joins-star.base.argb32.ref.png b/test/reference/joins-star.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..595f96a
Binary files /dev/null and b/test/reference/joins-star.base.argb32.ref.png differ
diff --git a/test/reference/joins-star.base.rgb24.ref.png b/test/reference/joins-star.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..595f96a
Binary files /dev/null and b/test/reference/joins-star.base.rgb24.ref.png differ
diff --git a/test/reference/joins-star.ref.png b/test/reference/joins-star.ref.png
new file mode 100644 (file)
index 0000000..574778c
Binary files /dev/null and b/test/reference/joins-star.ref.png differ
diff --git a/test/reference/joins-star.traps.argb32.ref.png b/test/reference/joins-star.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..595f96a
Binary files /dev/null and b/test/reference/joins-star.traps.argb32.ref.png differ
diff --git a/test/reference/joins-star.traps.rgb24.ref.png b/test/reference/joins-star.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..595f96a
Binary files /dev/null and b/test/reference/joins-star.traps.rgb24.ref.png differ
diff --git a/test/reference/joins.base.argb32.ref.png b/test/reference/joins.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..a0d632b
Binary files /dev/null and b/test/reference/joins.base.argb32.ref.png differ
diff --git a/test/reference/joins.base.rgb24.ref.png b/test/reference/joins.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..a0d632b
Binary files /dev/null and b/test/reference/joins.base.rgb24.ref.png differ
diff --git a/test/reference/joins.image16.ref.png b/test/reference/joins.image16.ref.png
new file mode 100644 (file)
index 0000000..57f9bb1
Binary files /dev/null and b/test/reference/joins.image16.ref.png differ
diff --git a/test/reference/joins.ps.ref.png b/test/reference/joins.ps.ref.png
new file mode 100644 (file)
index 0000000..0d5adea
Binary files /dev/null and b/test/reference/joins.ps.ref.png differ
diff --git a/test/reference/joins.quartz.ref.png b/test/reference/joins.quartz.ref.png
new file mode 100644 (file)
index 0000000..59b8fdb
Binary files /dev/null and b/test/reference/joins.quartz.ref.png differ
diff --git a/test/reference/joins.ref.png b/test/reference/joins.ref.png
new file mode 100644 (file)
index 0000000..6513023
Binary files /dev/null and b/test/reference/joins.ref.png differ
diff --git a/test/reference/joins.traps.argb32.ref.png b/test/reference/joins.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..a0d632b
Binary files /dev/null and b/test/reference/joins.traps.argb32.ref.png differ
diff --git a/test/reference/joins.traps.rgb24.ref.png b/test/reference/joins.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..a0d632b
Binary files /dev/null and b/test/reference/joins.traps.rgb24.ref.png differ
diff --git a/test/reference/large-clip.base.argb32.ref.png b/test/reference/large-clip.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..3c3464b
Binary files /dev/null and b/test/reference/large-clip.base.argb32.ref.png differ
diff --git a/test/reference/large-clip.base.rgb24.ref.png b/test/reference/large-clip.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..3c3464b
Binary files /dev/null and b/test/reference/large-clip.base.rgb24.ref.png differ
diff --git a/test/reference/large-clip.ref.png b/test/reference/large-clip.ref.png
new file mode 100644 (file)
index 0000000..9e46d2d
Binary files /dev/null and b/test/reference/large-clip.ref.png differ
diff --git a/test/reference/large-clip.traps.argb32.ref.png b/test/reference/large-clip.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..3c3464b
Binary files /dev/null and b/test/reference/large-clip.traps.argb32.ref.png differ
diff --git a/test/reference/large-clip.traps.rgb24.ref.png b/test/reference/large-clip.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..3c3464b
Binary files /dev/null and b/test/reference/large-clip.traps.rgb24.ref.png differ
diff --git a/test/reference/large-font.base.argb32.ref.png b/test/reference/large-font.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..503061a
Binary files /dev/null and b/test/reference/large-font.base.argb32.ref.png differ
diff --git a/test/reference/large-font.base.rgb24.ref.png b/test/reference/large-font.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..503061a
Binary files /dev/null and b/test/reference/large-font.base.rgb24.ref.png differ
diff --git a/test/reference/large-font.image16.ref.png b/test/reference/large-font.image16.ref.png
new file mode 100644 (file)
index 0000000..d5d11a5
Binary files /dev/null and b/test/reference/large-font.image16.ref.png differ
diff --git a/test/reference/large-font.ref.png b/test/reference/large-font.ref.png
new file mode 100644 (file)
index 0000000..da5c431
Binary files /dev/null and b/test/reference/large-font.ref.png differ
diff --git a/test/reference/large-font.traps.argb32.ref.png b/test/reference/large-font.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..503061a
Binary files /dev/null and b/test/reference/large-font.traps.argb32.ref.png differ
diff --git a/test/reference/large-font.traps.rgb24.ref.png b/test/reference/large-font.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..503061a
Binary files /dev/null and b/test/reference/large-font.traps.rgb24.ref.png differ
diff --git a/test/reference/large-source-roi.base.argb32.ref.png b/test/reference/large-source-roi.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..216a065
Binary files /dev/null and b/test/reference/large-source-roi.base.argb32.ref.png differ
diff --git a/test/reference/large-source-roi.base.rgb24.ref.png b/test/reference/large-source-roi.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..216a065
Binary files /dev/null and b/test/reference/large-source-roi.base.rgb24.ref.png differ
diff --git a/test/reference/large-source-roi.ref.png b/test/reference/large-source-roi.ref.png
new file mode 100644 (file)
index 0000000..b8dc8b1
Binary files /dev/null and b/test/reference/large-source-roi.ref.png differ
diff --git a/test/reference/large-source-roi.traps.argb32.ref.png b/test/reference/large-source-roi.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..216a065
Binary files /dev/null and b/test/reference/large-source-roi.traps.argb32.ref.png differ
diff --git a/test/reference/large-source-roi.traps.rgb24.ref.png b/test/reference/large-source-roi.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..216a065
Binary files /dev/null and b/test/reference/large-source-roi.traps.rgb24.ref.png differ
diff --git a/test/reference/large-source.base.argb32.ref.png b/test/reference/large-source.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..a54455f
Binary files /dev/null and b/test/reference/large-source.base.argb32.ref.png differ
diff --git a/test/reference/large-source.base.rgb24.ref.png b/test/reference/large-source.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..a54455f
Binary files /dev/null and b/test/reference/large-source.base.rgb24.ref.png differ
diff --git a/test/reference/large-source.ref.png b/test/reference/large-source.ref.png
new file mode 100644 (file)
index 0000000..5d96dd3
Binary files /dev/null and b/test/reference/large-source.ref.png differ
diff --git a/test/reference/large-source.traps.argb32.ref.png b/test/reference/large-source.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..a54455f
Binary files /dev/null and b/test/reference/large-source.traps.argb32.ref.png differ
diff --git a/test/reference/large-source.traps.rgb24.ref.png b/test/reference/large-source.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..a54455f
Binary files /dev/null and b/test/reference/large-source.traps.rgb24.ref.png differ
diff --git a/test/reference/large-twin-antialias-mixed.base.argb32.ref.png b/test/reference/large-twin-antialias-mixed.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..456e81d
Binary files /dev/null and b/test/reference/large-twin-antialias-mixed.base.argb32.ref.png differ
diff --git a/test/reference/large-twin-antialias-mixed.base.rgb24.ref.png b/test/reference/large-twin-antialias-mixed.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..456e81d
Binary files /dev/null and b/test/reference/large-twin-antialias-mixed.base.rgb24.ref.png differ
diff --git a/test/reference/large-twin-antialias-mixed.image16.ref.png b/test/reference/large-twin-antialias-mixed.image16.ref.png
new file mode 100644 (file)
index 0000000..93f071e
Binary files /dev/null and b/test/reference/large-twin-antialias-mixed.image16.ref.png differ
diff --git a/test/reference/large-twin-antialias-mixed.ref.png b/test/reference/large-twin-antialias-mixed.ref.png
new file mode 100644 (file)
index 0000000..456e81d
Binary files /dev/null and b/test/reference/large-twin-antialias-mixed.ref.png differ
diff --git a/test/reference/large-twin-antialias-mixed.traps.argb32.ref.png b/test/reference/large-twin-antialias-mixed.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..456e81d
Binary files /dev/null and b/test/reference/large-twin-antialias-mixed.traps.argb32.ref.png differ
diff --git a/test/reference/large-twin-antialias-mixed.traps.rgb24.ref.png b/test/reference/large-twin-antialias-mixed.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..456e81d
Binary files /dev/null and b/test/reference/large-twin-antialias-mixed.traps.rgb24.ref.png differ
diff --git a/test/reference/leaky-dash.base.argb32.ref.png b/test/reference/leaky-dash.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..0973ece
Binary files /dev/null and b/test/reference/leaky-dash.base.argb32.ref.png differ
diff --git a/test/reference/leaky-dash.base.rgb24.ref.png b/test/reference/leaky-dash.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..0973ece
Binary files /dev/null and b/test/reference/leaky-dash.base.rgb24.ref.png differ
diff --git a/test/reference/leaky-dash.ps2.argb32.ref.png b/test/reference/leaky-dash.ps2.argb32.ref.png
new file mode 100644 (file)
index 0000000..93b8640
Binary files /dev/null and b/test/reference/leaky-dash.ps2.argb32.ref.png differ
diff --git a/test/reference/leaky-dash.ps2.rgb24.ref.png b/test/reference/leaky-dash.ps2.rgb24.ref.png
new file mode 100644 (file)
index 0000000..c11eb48
Binary files /dev/null and b/test/reference/leaky-dash.ps2.rgb24.ref.png differ
diff --git a/test/reference/leaky-dash.ps3.argb32.ref.png b/test/reference/leaky-dash.ps3.argb32.ref.png
new file mode 100644 (file)
index 0000000..93b8640
Binary files /dev/null and b/test/reference/leaky-dash.ps3.argb32.ref.png differ
diff --git a/test/reference/leaky-dash.ps3.rgb24.ref.png b/test/reference/leaky-dash.ps3.rgb24.ref.png
new file mode 100644 (file)
index 0000000..c11eb48
Binary files /dev/null and b/test/reference/leaky-dash.ps3.rgb24.ref.png differ
diff --git a/test/reference/leaky-dash.quartz.ref.png b/test/reference/leaky-dash.quartz.ref.png
new file mode 100644 (file)
index 0000000..9692819
Binary files /dev/null and b/test/reference/leaky-dash.quartz.ref.png differ
diff --git a/test/reference/leaky-dash.ref.png b/test/reference/leaky-dash.ref.png
new file mode 100644 (file)
index 0000000..87facc5
Binary files /dev/null and b/test/reference/leaky-dash.ref.png differ
diff --git a/test/reference/leaky-dash.traps.argb32.ref.png b/test/reference/leaky-dash.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..0973ece
Binary files /dev/null and b/test/reference/leaky-dash.traps.argb32.ref.png differ
diff --git a/test/reference/leaky-dash.traps.rgb24.ref.png b/test/reference/leaky-dash.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..0973ece
Binary files /dev/null and b/test/reference/leaky-dash.traps.rgb24.ref.png differ
diff --git a/test/reference/leaky-dashed-rectangle.base.argb32.ref.png b/test/reference/leaky-dashed-rectangle.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..de35420
Binary files /dev/null and b/test/reference/leaky-dashed-rectangle.base.argb32.ref.png differ
diff --git a/test/reference/leaky-dashed-rectangle.base.rgb24.ref.png b/test/reference/leaky-dashed-rectangle.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..de35420
Binary files /dev/null and b/test/reference/leaky-dashed-rectangle.base.rgb24.ref.png differ
diff --git a/test/reference/leaky-dashed-rectangle.image16.ref.png b/test/reference/leaky-dashed-rectangle.image16.ref.png
new file mode 100644 (file)
index 0000000..f61db4c
Binary files /dev/null and b/test/reference/leaky-dashed-rectangle.image16.ref.png differ
diff --git a/test/reference/leaky-dashed-rectangle.pdf.ref.png b/test/reference/leaky-dashed-rectangle.pdf.ref.png
new file mode 100644 (file)
index 0000000..72efd49
Binary files /dev/null and b/test/reference/leaky-dashed-rectangle.pdf.ref.png differ
diff --git a/test/reference/leaky-dashed-rectangle.ps.ref.png b/test/reference/leaky-dashed-rectangle.ps.ref.png
new file mode 100644 (file)
index 0000000..1e0a138
Binary files /dev/null and b/test/reference/leaky-dashed-rectangle.ps.ref.png differ
diff --git a/test/reference/leaky-dashed-rectangle.quartz.ref.png b/test/reference/leaky-dashed-rectangle.quartz.ref.png
new file mode 100644 (file)
index 0000000..fad8588
Binary files /dev/null and b/test/reference/leaky-dashed-rectangle.quartz.ref.png differ
diff --git a/test/reference/leaky-dashed-rectangle.ref.png b/test/reference/leaky-dashed-rectangle.ref.png
new file mode 100644 (file)
index 0000000..05f4584
Binary files /dev/null and b/test/reference/leaky-dashed-rectangle.ref.png differ
diff --git a/test/reference/leaky-dashed-rectangle.traps.argb32.ref.png b/test/reference/leaky-dashed-rectangle.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..8f9a65d
Binary files /dev/null and b/test/reference/leaky-dashed-rectangle.traps.argb32.ref.png differ
diff --git a/test/reference/leaky-dashed-rectangle.traps.rgb24.ref.png b/test/reference/leaky-dashed-rectangle.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..8f9a65d
Binary files /dev/null and b/test/reference/leaky-dashed-rectangle.traps.rgb24.ref.png differ
diff --git a/test/reference/leaky-dashed-stroke.base.argb32.ref.png b/test/reference/leaky-dashed-stroke.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..da6807f
Binary files /dev/null and b/test/reference/leaky-dashed-stroke.base.argb32.ref.png differ
diff --git a/test/reference/leaky-dashed-stroke.base.rgb24.ref.png b/test/reference/leaky-dashed-stroke.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..da6807f
Binary files /dev/null and b/test/reference/leaky-dashed-stroke.base.rgb24.ref.png differ
diff --git a/test/reference/leaky-dashed-stroke.image16.ref.png b/test/reference/leaky-dashed-stroke.image16.ref.png
new file mode 100644 (file)
index 0000000..f90bb23
Binary files /dev/null and b/test/reference/leaky-dashed-stroke.image16.ref.png differ
diff --git a/test/reference/leaky-dashed-stroke.ps.ref.png b/test/reference/leaky-dashed-stroke.ps.ref.png
new file mode 100644 (file)
index 0000000..7d581e9
Binary files /dev/null and b/test/reference/leaky-dashed-stroke.ps.ref.png differ
diff --git a/test/reference/leaky-dashed-stroke.quartz.ref.png b/test/reference/leaky-dashed-stroke.quartz.ref.png
new file mode 100644 (file)
index 0000000..adbfd61
Binary files /dev/null and b/test/reference/leaky-dashed-stroke.quartz.ref.png differ
diff --git a/test/reference/leaky-dashed-stroke.ref.png b/test/reference/leaky-dashed-stroke.ref.png
new file mode 100644 (file)
index 0000000..31f1e97
Binary files /dev/null and b/test/reference/leaky-dashed-stroke.ref.png differ
diff --git a/test/reference/leaky-dashed-stroke.traps.argb32.ref.png b/test/reference/leaky-dashed-stroke.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..da6807f
Binary files /dev/null and b/test/reference/leaky-dashed-stroke.traps.argb32.ref.png differ
diff --git a/test/reference/leaky-dashed-stroke.traps.rgb24.ref.png b/test/reference/leaky-dashed-stroke.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..da6807f
Binary files /dev/null and b/test/reference/leaky-dashed-stroke.traps.rgb24.ref.png differ
diff --git a/test/reference/leaky-polygon.base.argb32.ref.png b/test/reference/leaky-polygon.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..1cb7a3c
Binary files /dev/null and b/test/reference/leaky-polygon.base.argb32.ref.png differ
diff --git a/test/reference/leaky-polygon.base.rgb24.ref.png b/test/reference/leaky-polygon.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..1cb7a3c
Binary files /dev/null and b/test/reference/leaky-polygon.base.rgb24.ref.png differ
diff --git a/test/reference/leaky-polygon.image16.ref.png b/test/reference/leaky-polygon.image16.ref.png
new file mode 100644 (file)
index 0000000..e91d0fa
Binary files /dev/null and b/test/reference/leaky-polygon.image16.ref.png differ
diff --git a/test/reference/leaky-polygon.ps.ref.png b/test/reference/leaky-polygon.ps.ref.png
new file mode 100644 (file)
index 0000000..dd8a104
Binary files /dev/null and b/test/reference/leaky-polygon.ps.ref.png differ
diff --git a/test/reference/leaky-polygon.ref.png b/test/reference/leaky-polygon.ref.png
new file mode 100644 (file)
index 0000000..b92d8d2
Binary files /dev/null and b/test/reference/leaky-polygon.ref.png differ
diff --git a/test/reference/leaky-polygon.traps.argb32.ref.png b/test/reference/leaky-polygon.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..1cb7a3c
Binary files /dev/null and b/test/reference/leaky-polygon.traps.argb32.ref.png differ
diff --git a/test/reference/leaky-polygon.traps.rgb24.ref.png b/test/reference/leaky-polygon.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..1cb7a3c
Binary files /dev/null and b/test/reference/leaky-polygon.traps.rgb24.ref.png differ
diff --git a/test/reference/line-width-large-overlap-dashed.ref.png b/test/reference/line-width-large-overlap-dashed.ref.png
new file mode 100644 (file)
index 0000000..e6cdcc2
Binary files /dev/null and b/test/reference/line-width-large-overlap-dashed.ref.png differ
diff --git a/test/reference/line-width-large-overlap-dashed.xfail.png b/test/reference/line-width-large-overlap-dashed.xfail.png
new file mode 100644 (file)
index 0000000..8cd4d31
Binary files /dev/null and b/test/reference/line-width-large-overlap-dashed.xfail.png differ
diff --git a/test/reference/line-width-large-overlap-flipped.ref.png b/test/reference/line-width-large-overlap-flipped.ref.png
new file mode 100644 (file)
index 0000000..3c3464b
Binary files /dev/null and b/test/reference/line-width-large-overlap-flipped.ref.png differ
diff --git a/test/reference/line-width-large-overlap-flopped.ref.png b/test/reference/line-width-large-overlap-flopped.ref.png
new file mode 100644 (file)
index 0000000..3c3464b
Binary files /dev/null and b/test/reference/line-width-large-overlap-flopped.ref.png differ
diff --git a/test/reference/line-width-large-overlap-offset.ref.png b/test/reference/line-width-large-overlap-offset.ref.png
new file mode 100644 (file)
index 0000000..3c3464b
Binary files /dev/null and b/test/reference/line-width-large-overlap-offset.ref.png differ
diff --git a/test/reference/line-width-large-overlap-rotated.base.ref.png b/test/reference/line-width-large-overlap-rotated.base.ref.png
new file mode 100644 (file)
index 0000000..87fe752
Binary files /dev/null and b/test/reference/line-width-large-overlap-rotated.base.ref.png differ
diff --git a/test/reference/line-width-large-overlap-rotated.ref.png b/test/reference/line-width-large-overlap-rotated.ref.png
new file mode 100644 (file)
index 0000000..8ffa0db
Binary files /dev/null and b/test/reference/line-width-large-overlap-rotated.ref.png differ
diff --git a/test/reference/line-width-large-overlap-rotated.traps.ref.png b/test/reference/line-width-large-overlap-rotated.traps.ref.png
new file mode 100644 (file)
index 0000000..87fe752
Binary files /dev/null and b/test/reference/line-width-large-overlap-rotated.traps.ref.png differ
diff --git a/test/reference/line-width-large-overlap.ref.png b/test/reference/line-width-large-overlap.ref.png
new file mode 100644 (file)
index 0000000..3c3464b
Binary files /dev/null and b/test/reference/line-width-large-overlap.ref.png differ
diff --git a/test/reference/line-width-overlap-dashed.base.argb32.ref.png b/test/reference/line-width-overlap-dashed.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..066b182
Binary files /dev/null and b/test/reference/line-width-overlap-dashed.base.argb32.ref.png differ
diff --git a/test/reference/line-width-overlap-dashed.base.rgb24.ref.png b/test/reference/line-width-overlap-dashed.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..066b182
Binary files /dev/null and b/test/reference/line-width-overlap-dashed.base.rgb24.ref.png differ
diff --git a/test/reference/line-width-overlap-dashed.traps.argb32.ref.png b/test/reference/line-width-overlap-dashed.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..0de187d
Binary files /dev/null and b/test/reference/line-width-overlap-dashed.traps.argb32.ref.png differ
diff --git a/test/reference/line-width-overlap-dashed.traps.rgb24.ref.png b/test/reference/line-width-overlap-dashed.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..0de187d
Binary files /dev/null and b/test/reference/line-width-overlap-dashed.traps.rgb24.ref.png differ
diff --git a/test/reference/line-width-overlap-flipped.base.argb32.ref.png b/test/reference/line-width-overlap-flipped.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..13d70c8
Binary files /dev/null and b/test/reference/line-width-overlap-flipped.base.argb32.ref.png differ
diff --git a/test/reference/line-width-overlap-flipped.base.rgb24.ref.png b/test/reference/line-width-overlap-flipped.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..13d70c8
Binary files /dev/null and b/test/reference/line-width-overlap-flipped.base.rgb24.ref.png differ
diff --git a/test/reference/line-width-overlap-flipped.ref.png b/test/reference/line-width-overlap-flipped.ref.png
new file mode 100644 (file)
index 0000000..09911bc
Binary files /dev/null and b/test/reference/line-width-overlap-flipped.ref.png differ
diff --git a/test/reference/line-width-overlap-flipped.traps.argb32.ref.png b/test/reference/line-width-overlap-flipped.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..13d70c8
Binary files /dev/null and b/test/reference/line-width-overlap-flipped.traps.argb32.ref.png differ
diff --git a/test/reference/line-width-overlap-flipped.traps.rgb24.ref.png b/test/reference/line-width-overlap-flipped.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..13d70c8
Binary files /dev/null and b/test/reference/line-width-overlap-flipped.traps.rgb24.ref.png differ
diff --git a/test/reference/line-width-overlap-flopped.base.argb32.ref.png b/test/reference/line-width-overlap-flopped.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..13d70c8
Binary files /dev/null and b/test/reference/line-width-overlap-flopped.base.argb32.ref.png differ
diff --git a/test/reference/line-width-overlap-flopped.base.rgb24.ref.png b/test/reference/line-width-overlap-flopped.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..13d70c8
Binary files /dev/null and b/test/reference/line-width-overlap-flopped.base.rgb24.ref.png differ
diff --git a/test/reference/line-width-overlap-flopped.ref.png b/test/reference/line-width-overlap-flopped.ref.png
new file mode 100644 (file)
index 0000000..09911bc
Binary files /dev/null and b/test/reference/line-width-overlap-flopped.ref.png differ
diff --git a/test/reference/line-width-overlap-flopped.traps.argb32.ref.png b/test/reference/line-width-overlap-flopped.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..13d70c8
Binary files /dev/null and b/test/reference/line-width-overlap-flopped.traps.argb32.ref.png differ
diff --git a/test/reference/line-width-overlap-flopped.traps.rgb24.ref.png b/test/reference/line-width-overlap-flopped.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..13d70c8
Binary files /dev/null and b/test/reference/line-width-overlap-flopped.traps.rgb24.ref.png differ
diff --git a/test/reference/line-width-overlap-offset.base.argb32.ref.png b/test/reference/line-width-overlap-offset.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..13a138b
Binary files /dev/null and b/test/reference/line-width-overlap-offset.base.argb32.ref.png differ
diff --git a/test/reference/line-width-overlap-offset.base.rgb24.ref.png b/test/reference/line-width-overlap-offset.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..13a138b
Binary files /dev/null and b/test/reference/line-width-overlap-offset.base.rgb24.ref.png differ
diff --git a/test/reference/line-width-overlap-offset.ref.png b/test/reference/line-width-overlap-offset.ref.png
new file mode 100644 (file)
index 0000000..eafa50b
Binary files /dev/null and b/test/reference/line-width-overlap-offset.ref.png differ
diff --git a/test/reference/line-width-overlap-offset.traps.ref.png b/test/reference/line-width-overlap-offset.traps.ref.png
new file mode 100644 (file)
index 0000000..13a138b
Binary files /dev/null and b/test/reference/line-width-overlap-offset.traps.ref.png differ
diff --git a/test/reference/line-width-overlap-rotated.base.argb32.ref.png b/test/reference/line-width-overlap-rotated.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..fa7290d
Binary files /dev/null and b/test/reference/line-width-overlap-rotated.base.argb32.ref.png differ
diff --git a/test/reference/line-width-overlap-rotated.base.rgb24.ref.png b/test/reference/line-width-overlap-rotated.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..fa7290d
Binary files /dev/null and b/test/reference/line-width-overlap-rotated.base.rgb24.ref.png differ
diff --git a/test/reference/line-width-overlap-rotated.ref.png b/test/reference/line-width-overlap-rotated.ref.png
new file mode 100644 (file)
index 0000000..6e3cbea
Binary files /dev/null and b/test/reference/line-width-overlap-rotated.ref.png differ
diff --git a/test/reference/line-width-overlap-rotated.traps.argb32.ref.png b/test/reference/line-width-overlap-rotated.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..fa7290d
Binary files /dev/null and b/test/reference/line-width-overlap-rotated.traps.argb32.ref.png differ
diff --git a/test/reference/line-width-overlap-rotated.traps.rgb24.ref.png b/test/reference/line-width-overlap-rotated.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..fa7290d
Binary files /dev/null and b/test/reference/line-width-overlap-rotated.traps.rgb24.ref.png differ
diff --git a/test/reference/line-width-overlap.base.argb32.ref.png b/test/reference/line-width-overlap.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..13d70c8
Binary files /dev/null and b/test/reference/line-width-overlap.base.argb32.ref.png differ
diff --git a/test/reference/line-width-overlap.base.rgb24.ref.png b/test/reference/line-width-overlap.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..13d70c8
Binary files /dev/null and b/test/reference/line-width-overlap.base.rgb24.ref.png differ
diff --git a/test/reference/line-width-overlap.ref.png b/test/reference/line-width-overlap.ref.png
new file mode 100644 (file)
index 0000000..13d70c8
Binary files /dev/null and b/test/reference/line-width-overlap.ref.png differ
diff --git a/test/reference/line-width-overlap.traps.argb32.ref.png b/test/reference/line-width-overlap.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..13d70c8
Binary files /dev/null and b/test/reference/line-width-overlap.traps.argb32.ref.png differ
diff --git a/test/reference/line-width-overlap.traps.rgb24.ref.png b/test/reference/line-width-overlap.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..13d70c8
Binary files /dev/null and b/test/reference/line-width-overlap.traps.rgb24.ref.png differ
diff --git a/test/reference/line-width-scale.base.argb32.ref.png b/test/reference/line-width-scale.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..41d55b0
Binary files /dev/null and b/test/reference/line-width-scale.base.argb32.ref.png differ
diff --git a/test/reference/line-width-scale.base.rgb24.ref.png b/test/reference/line-width-scale.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..41d55b0
Binary files /dev/null and b/test/reference/line-width-scale.base.rgb24.ref.png differ
diff --git a/test/reference/line-width-scale.image16.ref.png b/test/reference/line-width-scale.image16.ref.png
new file mode 100644 (file)
index 0000000..a784f65
Binary files /dev/null and b/test/reference/line-width-scale.image16.ref.png differ
diff --git a/test/reference/line-width-scale.ps2.ref.png b/test/reference/line-width-scale.ps2.ref.png
new file mode 100644 (file)
index 0000000..57999b8
Binary files /dev/null and b/test/reference/line-width-scale.ps2.ref.png differ
diff --git a/test/reference/line-width-scale.ps3.ref.png b/test/reference/line-width-scale.ps3.ref.png
new file mode 100644 (file)
index 0000000..57999b8
Binary files /dev/null and b/test/reference/line-width-scale.ps3.ref.png differ
diff --git a/test/reference/line-width-scale.quartz.ref.png b/test/reference/line-width-scale.quartz.ref.png
new file mode 100644 (file)
index 0000000..afdc9da
Binary files /dev/null and b/test/reference/line-width-scale.quartz.ref.png differ
diff --git a/test/reference/line-width-scale.ref.png b/test/reference/line-width-scale.ref.png
new file mode 100644 (file)
index 0000000..1c02e93
Binary files /dev/null and b/test/reference/line-width-scale.ref.png differ
diff --git a/test/reference/line-width-scale.traps.argb32.ref.png b/test/reference/line-width-scale.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..41d55b0
Binary files /dev/null and b/test/reference/line-width-scale.traps.argb32.ref.png differ
diff --git a/test/reference/line-width-scale.traps.rgb24.ref.png b/test/reference/line-width-scale.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..41d55b0
Binary files /dev/null and b/test/reference/line-width-scale.traps.rgb24.ref.png differ
diff --git a/test/reference/line-width-tolerance.base.argb32.ref.png b/test/reference/line-width-tolerance.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..f890a52
Binary files /dev/null and b/test/reference/line-width-tolerance.base.argb32.ref.png differ
diff --git a/test/reference/line-width-tolerance.base.rgb24.ref.png b/test/reference/line-width-tolerance.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..f890a52
Binary files /dev/null and b/test/reference/line-width-tolerance.base.rgb24.ref.png differ
diff --git a/test/reference/line-width-tolerance.ref.png b/test/reference/line-width-tolerance.ref.png
new file mode 100644 (file)
index 0000000..541dc9e
Binary files /dev/null and b/test/reference/line-width-tolerance.ref.png differ
diff --git a/test/reference/line-width-tolerance.traps.argb32.ref.png b/test/reference/line-width-tolerance.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..f890a52
Binary files /dev/null and b/test/reference/line-width-tolerance.traps.argb32.ref.png differ
diff --git a/test/reference/line-width-tolerance.traps.rgb24.ref.png b/test/reference/line-width-tolerance.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..f890a52
Binary files /dev/null and b/test/reference/line-width-tolerance.traps.rgb24.ref.png differ
diff --git a/test/reference/line-width.base.argb32.ref.png b/test/reference/line-width.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..208d170
Binary files /dev/null and b/test/reference/line-width.base.argb32.ref.png differ
diff --git a/test/reference/line-width.base.rgb24.ref.png b/test/reference/line-width.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..208d170
Binary files /dev/null and b/test/reference/line-width.base.rgb24.ref.png differ
diff --git a/test/reference/line-width.ref.png b/test/reference/line-width.ref.png
new file mode 100644 (file)
index 0000000..81ac2f5
Binary files /dev/null and b/test/reference/line-width.ref.png differ
diff --git a/test/reference/line-width.traps.argb32.ref.png b/test/reference/line-width.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..208d170
Binary files /dev/null and b/test/reference/line-width.traps.argb32.ref.png differ
diff --git a/test/reference/line-width.traps.rgb24.ref.png b/test/reference/line-width.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..208d170
Binary files /dev/null and b/test/reference/line-width.traps.rgb24.ref.png differ
diff --git a/test/reference/linear-gradient-extend.base.argb32.ref.png b/test/reference/linear-gradient-extend.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..79ce747
Binary files /dev/null and b/test/reference/linear-gradient-extend.base.argb32.ref.png differ
diff --git a/test/reference/linear-gradient-extend.base.rgb24.ref.png b/test/reference/linear-gradient-extend.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..79ce747
Binary files /dev/null and b/test/reference/linear-gradient-extend.base.rgb24.ref.png differ
diff --git a/test/reference/linear-gradient-extend.ref.png b/test/reference/linear-gradient-extend.ref.png
new file mode 100644 (file)
index 0000000..79ce747
Binary files /dev/null and b/test/reference/linear-gradient-extend.ref.png differ
diff --git a/test/reference/linear-gradient-extend.traps.argb32.ref.png b/test/reference/linear-gradient-extend.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..79ce747
Binary files /dev/null and b/test/reference/linear-gradient-extend.traps.argb32.ref.png differ
diff --git a/test/reference/linear-gradient-extend.traps.rgb24.ref.png b/test/reference/linear-gradient-extend.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..79ce747
Binary files /dev/null and b/test/reference/linear-gradient-extend.traps.rgb24.ref.png differ
diff --git a/test/reference/linear-gradient-large.base.argb32.ref.png b/test/reference/linear-gradient-large.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..f1f37ab
Binary files /dev/null and b/test/reference/linear-gradient-large.base.argb32.ref.png differ
diff --git a/test/reference/linear-gradient-large.base.rgb24.ref.png b/test/reference/linear-gradient-large.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..f1f37ab
Binary files /dev/null and b/test/reference/linear-gradient-large.base.rgb24.ref.png differ
diff --git a/test/reference/linear-gradient-large.quartz.ref.png b/test/reference/linear-gradient-large.quartz.ref.png
new file mode 100644 (file)
index 0000000..68f0829
Binary files /dev/null and b/test/reference/linear-gradient-large.quartz.ref.png differ
diff --git a/test/reference/linear-gradient-large.ref.png b/test/reference/linear-gradient-large.ref.png
new file mode 100644 (file)
index 0000000..f1f37ab
Binary files /dev/null and b/test/reference/linear-gradient-large.ref.png differ
diff --git a/test/reference/linear-gradient-large.traps.argb32.ref.png b/test/reference/linear-gradient-large.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..f1f37ab
Binary files /dev/null and b/test/reference/linear-gradient-large.traps.argb32.ref.png differ
diff --git a/test/reference/linear-gradient-large.traps.rgb24.ref.png b/test/reference/linear-gradient-large.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..f1f37ab
Binary files /dev/null and b/test/reference/linear-gradient-large.traps.rgb24.ref.png differ
diff --git a/test/reference/linear-gradient-one-stop.argb32.ref.png b/test/reference/linear-gradient-one-stop.argb32.ref.png
new file mode 100644 (file)
index 0000000..da02fda
Binary files /dev/null and b/test/reference/linear-gradient-one-stop.argb32.ref.png differ
diff --git a/test/reference/linear-gradient-one-stop.base.argb32.ref.png b/test/reference/linear-gradient-one-stop.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..da02fda
Binary files /dev/null and b/test/reference/linear-gradient-one-stop.base.argb32.ref.png differ
diff --git a/test/reference/linear-gradient-one-stop.base.rgb24.ref.png b/test/reference/linear-gradient-one-stop.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..efc12ee
Binary files /dev/null and b/test/reference/linear-gradient-one-stop.base.rgb24.ref.png differ
diff --git a/test/reference/linear-gradient-one-stop.rgb24.ref.png b/test/reference/linear-gradient-one-stop.rgb24.ref.png
new file mode 100644 (file)
index 0000000..efc12ee
Binary files /dev/null and b/test/reference/linear-gradient-one-stop.rgb24.ref.png differ
diff --git a/test/reference/linear-gradient-one-stop.traps.argb32.ref.png b/test/reference/linear-gradient-one-stop.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..da02fda
Binary files /dev/null and b/test/reference/linear-gradient-one-stop.traps.argb32.ref.png differ
diff --git a/test/reference/linear-gradient-one-stop.traps.rgb24.ref.png b/test/reference/linear-gradient-one-stop.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..efc12ee
Binary files /dev/null and b/test/reference/linear-gradient-one-stop.traps.rgb24.ref.png differ
diff --git a/test/reference/linear-gradient-reflect.base.argb32.ref.png b/test/reference/linear-gradient-reflect.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..b68dccb
Binary files /dev/null and b/test/reference/linear-gradient-reflect.base.argb32.ref.png differ
diff --git a/test/reference/linear-gradient-reflect.base.rgb24.ref.png b/test/reference/linear-gradient-reflect.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..b68dccb
Binary files /dev/null and b/test/reference/linear-gradient-reflect.base.rgb24.ref.png differ
diff --git a/test/reference/linear-gradient-reflect.image16.ref.png b/test/reference/linear-gradient-reflect.image16.ref.png
new file mode 100644 (file)
index 0000000..de74afc
Binary files /dev/null and b/test/reference/linear-gradient-reflect.image16.ref.png differ
diff --git a/test/reference/linear-gradient-reflect.pdf.argb32.ref.png b/test/reference/linear-gradient-reflect.pdf.argb32.ref.png
new file mode 100644 (file)
index 0000000..46e1c0f
Binary files /dev/null and b/test/reference/linear-gradient-reflect.pdf.argb32.ref.png differ
diff --git a/test/reference/linear-gradient-reflect.pdf.rgb24.ref.png b/test/reference/linear-gradient-reflect.pdf.rgb24.ref.png
new file mode 100644 (file)
index 0000000..46e1c0f
Binary files /dev/null and b/test/reference/linear-gradient-reflect.pdf.rgb24.ref.png differ
diff --git a/test/reference/linear-gradient-reflect.ps3.ref.png b/test/reference/linear-gradient-reflect.ps3.ref.png
new file mode 100644 (file)
index 0000000..ea6d25c
Binary files /dev/null and b/test/reference/linear-gradient-reflect.ps3.ref.png differ
diff --git a/test/reference/linear-gradient-reflect.quartz.ref.png b/test/reference/linear-gradient-reflect.quartz.ref.png
new file mode 100644 (file)
index 0000000..89bac91
Binary files /dev/null and b/test/reference/linear-gradient-reflect.quartz.ref.png differ
diff --git a/test/reference/linear-gradient-reflect.ref.png b/test/reference/linear-gradient-reflect.ref.png
new file mode 100644 (file)
index 0000000..e4f9db8
Binary files /dev/null and b/test/reference/linear-gradient-reflect.ref.png differ
diff --git a/test/reference/linear-gradient-reflect.traps.argb32.ref.png b/test/reference/linear-gradient-reflect.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..b68dccb
Binary files /dev/null and b/test/reference/linear-gradient-reflect.traps.argb32.ref.png differ
diff --git a/test/reference/linear-gradient-reflect.traps.rgb24.ref.png b/test/reference/linear-gradient-reflect.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..b68dccb
Binary files /dev/null and b/test/reference/linear-gradient-reflect.traps.rgb24.ref.png differ
diff --git a/test/reference/linear-gradient-subset.base.argb32.ref.png b/test/reference/linear-gradient-subset.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..e03d3d6
Binary files /dev/null and b/test/reference/linear-gradient-subset.base.argb32.ref.png differ
diff --git a/test/reference/linear-gradient-subset.base.rgb24.ref.png b/test/reference/linear-gradient-subset.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..e03d3d6
Binary files /dev/null and b/test/reference/linear-gradient-subset.base.rgb24.ref.png differ
diff --git a/test/reference/linear-gradient-subset.image16.ref.png b/test/reference/linear-gradient-subset.image16.ref.png
new file mode 100644 (file)
index 0000000..9d04057
Binary files /dev/null and b/test/reference/linear-gradient-subset.image16.ref.png differ
diff --git a/test/reference/linear-gradient-subset.ps3.ref.png b/test/reference/linear-gradient-subset.ps3.ref.png
new file mode 100644 (file)
index 0000000..db0a4c0
Binary files /dev/null and b/test/reference/linear-gradient-subset.ps3.ref.png differ
diff --git a/test/reference/linear-gradient-subset.quartz.ref.png b/test/reference/linear-gradient-subset.quartz.ref.png
new file mode 100644 (file)
index 0000000..85d80ad
Binary files /dev/null and b/test/reference/linear-gradient-subset.quartz.ref.png differ
diff --git a/test/reference/linear-gradient-subset.ref.png b/test/reference/linear-gradient-subset.ref.png
new file mode 100644 (file)
index 0000000..0225b43
Binary files /dev/null and b/test/reference/linear-gradient-subset.ref.png differ
diff --git a/test/reference/linear-gradient-subset.traps.argb32.ref.png b/test/reference/linear-gradient-subset.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..e03d3d6
Binary files /dev/null and b/test/reference/linear-gradient-subset.traps.argb32.ref.png differ
diff --git a/test/reference/linear-gradient-subset.traps.rgb24.ref.png b/test/reference/linear-gradient-subset.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..e03d3d6
Binary files /dev/null and b/test/reference/linear-gradient-subset.traps.rgb24.ref.png differ
diff --git a/test/reference/linear-gradient.base.argb32.ref.png b/test/reference/linear-gradient.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..083a9b8
Binary files /dev/null and b/test/reference/linear-gradient.base.argb32.ref.png differ
diff --git a/test/reference/linear-gradient.base.rgb24.ref.png b/test/reference/linear-gradient.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..083a9b8
Binary files /dev/null and b/test/reference/linear-gradient.base.rgb24.ref.png differ
diff --git a/test/reference/linear-gradient.image16.ref.png b/test/reference/linear-gradient.image16.ref.png
new file mode 100644 (file)
index 0000000..183d3d9
Binary files /dev/null and b/test/reference/linear-gradient.image16.ref.png differ
diff --git a/test/reference/linear-gradient.ps3.ref.png b/test/reference/linear-gradient.ps3.ref.png
new file mode 100644 (file)
index 0000000..c2fa71b
Binary files /dev/null and b/test/reference/linear-gradient.ps3.ref.png differ
diff --git a/test/reference/linear-gradient.quartz.ref.png b/test/reference/linear-gradient.quartz.ref.png
new file mode 100644 (file)
index 0000000..1c3e7c2
Binary files /dev/null and b/test/reference/linear-gradient.quartz.ref.png differ
diff --git a/test/reference/linear-gradient.ref.png b/test/reference/linear-gradient.ref.png
new file mode 100644 (file)
index 0000000..66d5ddb
Binary files /dev/null and b/test/reference/linear-gradient.ref.png differ
diff --git a/test/reference/linear-gradient.traps.argb32.ref.png b/test/reference/linear-gradient.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..083a9b8
Binary files /dev/null and b/test/reference/linear-gradient.traps.argb32.ref.png differ
diff --git a/test/reference/linear-gradient.traps.rgb24.ref.png b/test/reference/linear-gradient.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..083a9b8
Binary files /dev/null and b/test/reference/linear-gradient.traps.rgb24.ref.png differ
diff --git a/test/reference/linear-step-function.base.argb32.ref.png b/test/reference/linear-step-function.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..0ed126e
Binary files /dev/null and b/test/reference/linear-step-function.base.argb32.ref.png differ
diff --git a/test/reference/linear-step-function.base.rgb24.ref.png b/test/reference/linear-step-function.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..0ed126e
Binary files /dev/null and b/test/reference/linear-step-function.base.rgb24.ref.png differ
diff --git a/test/reference/linear-step-function.traps.argb32.ref.png b/test/reference/linear-step-function.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..0ed126e
Binary files /dev/null and b/test/reference/linear-step-function.traps.argb32.ref.png differ
diff --git a/test/reference/linear-step-function.traps.rgb24.ref.png b/test/reference/linear-step-function.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..0ed126e
Binary files /dev/null and b/test/reference/linear-step-function.traps.rgb24.ref.png differ
diff --git a/test/reference/linear-step-function.xfail.png b/test/reference/linear-step-function.xfail.png
new file mode 100644 (file)
index 0000000..b8afd21
Binary files /dev/null and b/test/reference/linear-step-function.xfail.png differ
diff --git a/test/reference/linear-uniform.base.argb32.ref.png b/test/reference/linear-uniform.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..94ca336
Binary files /dev/null and b/test/reference/linear-uniform.base.argb32.ref.png differ
diff --git a/test/reference/linear-uniform.base.rgb24.ref.png b/test/reference/linear-uniform.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..94ca336
Binary files /dev/null and b/test/reference/linear-uniform.base.rgb24.ref.png differ
diff --git a/test/reference/linear-uniform.image16.ref.png b/test/reference/linear-uniform.image16.ref.png
new file mode 100644 (file)
index 0000000..7bf4bf5
Binary files /dev/null and b/test/reference/linear-uniform.image16.ref.png differ
diff --git a/test/reference/linear-uniform.ref.png b/test/reference/linear-uniform.ref.png
new file mode 100644 (file)
index 0000000..94ca336
Binary files /dev/null and b/test/reference/linear-uniform.ref.png differ
diff --git a/test/reference/linear-uniform.traps.argb32.ref.png b/test/reference/linear-uniform.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..94ca336
Binary files /dev/null and b/test/reference/linear-uniform.traps.argb32.ref.png differ
diff --git a/test/reference/linear-uniform.traps.rgb24.ref.png b/test/reference/linear-uniform.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..94ca336
Binary files /dev/null and b/test/reference/linear-uniform.traps.rgb24.ref.png differ
diff --git a/test/reference/long-dashed-lines.base.argb32.ref.png b/test/reference/long-dashed-lines.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..45fdcc0
Binary files /dev/null and b/test/reference/long-dashed-lines.base.argb32.ref.png differ
diff --git a/test/reference/long-dashed-lines.base.rgb24.ref.png b/test/reference/long-dashed-lines.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..45fdcc0
Binary files /dev/null and b/test/reference/long-dashed-lines.base.rgb24.ref.png differ
diff --git a/test/reference/long-dashed-lines.image16.ref.png b/test/reference/long-dashed-lines.image16.ref.png
new file mode 100644 (file)
index 0000000..8abac9b
Binary files /dev/null and b/test/reference/long-dashed-lines.image16.ref.png differ
diff --git a/test/reference/long-dashed-lines.ps2.ref.png b/test/reference/long-dashed-lines.ps2.ref.png
new file mode 100644 (file)
index 0000000..7fce667
Binary files /dev/null and b/test/reference/long-dashed-lines.ps2.ref.png differ
diff --git a/test/reference/long-dashed-lines.ps3.ref.png b/test/reference/long-dashed-lines.ps3.ref.png
new file mode 100644 (file)
index 0000000..7fce667
Binary files /dev/null and b/test/reference/long-dashed-lines.ps3.ref.png differ
diff --git a/test/reference/long-dashed-lines.quartz.ref.png b/test/reference/long-dashed-lines.quartz.ref.png
new file mode 100644 (file)
index 0000000..3f68d21
Binary files /dev/null and b/test/reference/long-dashed-lines.quartz.ref.png differ
diff --git a/test/reference/long-dashed-lines.ref.png b/test/reference/long-dashed-lines.ref.png
new file mode 100644 (file)
index 0000000..8344a45
Binary files /dev/null and b/test/reference/long-dashed-lines.ref.png differ
diff --git a/test/reference/long-dashed-lines.traps.argb32.ref.png b/test/reference/long-dashed-lines.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..45fdcc0
Binary files /dev/null and b/test/reference/long-dashed-lines.traps.argb32.ref.png differ
diff --git a/test/reference/long-dashed-lines.traps.rgb24.ref.png b/test/reference/long-dashed-lines.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..45fdcc0
Binary files /dev/null and b/test/reference/long-dashed-lines.traps.rgb24.ref.png differ
diff --git a/test/reference/long-lines.base.argb32.ref.png b/test/reference/long-lines.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..fe91163
Binary files /dev/null and b/test/reference/long-lines.base.argb32.ref.png differ
diff --git a/test/reference/long-lines.base.rgb24.ref.png b/test/reference/long-lines.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..fe91163
Binary files /dev/null and b/test/reference/long-lines.base.rgb24.ref.png differ
diff --git a/test/reference/long-lines.traps.argb32.ref.png b/test/reference/long-lines.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..fe91163
Binary files /dev/null and b/test/reference/long-lines.traps.argb32.ref.png differ
diff --git a/test/reference/long-lines.traps.rgb24.ref.png b/test/reference/long-lines.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..fe91163
Binary files /dev/null and b/test/reference/long-lines.traps.rgb24.ref.png differ
diff --git a/test/reference/map-all-to-image.base.argb32.ref.png b/test/reference/map-all-to-image.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..c56d969
Binary files /dev/null and b/test/reference/map-all-to-image.base.argb32.ref.png differ
diff --git a/test/reference/map-all-to-image.base.rgb24.ref.png b/test/reference/map-all-to-image.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..c56d969
Binary files /dev/null and b/test/reference/map-all-to-image.base.rgb24.ref.png differ
diff --git a/test/reference/map-all-to-image.ref.png b/test/reference/map-all-to-image.ref.png
new file mode 100644 (file)
index 0000000..c56d969
Binary files /dev/null and b/test/reference/map-all-to-image.ref.png differ
diff --git a/test/reference/map-all-to-image.traps.argb32.ref.png b/test/reference/map-all-to-image.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..c56d969
Binary files /dev/null and b/test/reference/map-all-to-image.traps.argb32.ref.png differ
diff --git a/test/reference/map-all-to-image.traps.rgb24.ref.png b/test/reference/map-all-to-image.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..c56d969
Binary files /dev/null and b/test/reference/map-all-to-image.traps.rgb24.ref.png differ
diff --git a/test/reference/map-bit-to-image.base.argb32.ref.png b/test/reference/map-bit-to-image.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..b42dcb6
Binary files /dev/null and b/test/reference/map-bit-to-image.base.argb32.ref.png differ
diff --git a/test/reference/map-bit-to-image.base.rgb24.ref.png b/test/reference/map-bit-to-image.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..b42dcb6
Binary files /dev/null and b/test/reference/map-bit-to-image.base.rgb24.ref.png differ
diff --git a/test/reference/map-bit-to-image.ref.png b/test/reference/map-bit-to-image.ref.png
new file mode 100644 (file)
index 0000000..b42dcb6
Binary files /dev/null and b/test/reference/map-bit-to-image.ref.png differ
diff --git a/test/reference/map-bit-to-image.traps.argb32.ref.png b/test/reference/map-bit-to-image.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..b42dcb6
Binary files /dev/null and b/test/reference/map-bit-to-image.traps.argb32.ref.png differ
diff --git a/test/reference/map-bit-to-image.traps.rgb24.ref.png b/test/reference/map-bit-to-image.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..b42dcb6
Binary files /dev/null and b/test/reference/map-bit-to-image.traps.rgb24.ref.png differ
diff --git a/test/reference/map-to-image-fill.base.argb32.ref.png b/test/reference/map-to-image-fill.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..c2893e0
Binary files /dev/null and b/test/reference/map-to-image-fill.base.argb32.ref.png differ
diff --git a/test/reference/map-to-image-fill.base.rgb24.ref.png b/test/reference/map-to-image-fill.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..c2893e0
Binary files /dev/null and b/test/reference/map-to-image-fill.base.rgb24.ref.png differ
diff --git a/test/reference/map-to-image-fill.ref.png b/test/reference/map-to-image-fill.ref.png
new file mode 100644 (file)
index 0000000..c2893e0
Binary files /dev/null and b/test/reference/map-to-image-fill.ref.png differ
diff --git a/test/reference/map-to-image-fill.traps.argb32.ref.png b/test/reference/map-to-image-fill.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..c2893e0
Binary files /dev/null and b/test/reference/map-to-image-fill.traps.argb32.ref.png differ
diff --git a/test/reference/map-to-image-fill.traps.rgb24.ref.png b/test/reference/map-to-image-fill.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..c2893e0
Binary files /dev/null and b/test/reference/map-to-image-fill.traps.rgb24.ref.png differ
diff --git a/test/reference/mask-alpha.argb32.ref.png b/test/reference/mask-alpha.argb32.ref.png
new file mode 100644 (file)
index 0000000..203b8d1
Binary files /dev/null and b/test/reference/mask-alpha.argb32.ref.png differ
diff --git a/test/reference/mask-alpha.base.argb32.ref.png b/test/reference/mask-alpha.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..a0b9017
Binary files /dev/null and b/test/reference/mask-alpha.base.argb32.ref.png differ
diff --git a/test/reference/mask-alpha.base.rgb24.ref.png b/test/reference/mask-alpha.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..d8f4b8f
Binary files /dev/null and b/test/reference/mask-alpha.base.rgb24.ref.png differ
diff --git a/test/reference/mask-alpha.image16.ref.png b/test/reference/mask-alpha.image16.ref.png
new file mode 100644 (file)
index 0000000..dbf121a
Binary files /dev/null and b/test/reference/mask-alpha.image16.ref.png differ
diff --git a/test/reference/mask-alpha.ps.ref.png b/test/reference/mask-alpha.ps.ref.png
new file mode 100644 (file)
index 0000000..b0058dd
Binary files /dev/null and b/test/reference/mask-alpha.ps.ref.png differ
diff --git a/test/reference/mask-alpha.quartz.argb32.ref.png b/test/reference/mask-alpha.quartz.argb32.ref.png
new file mode 100644 (file)
index 0000000..1d530ee
Binary files /dev/null and b/test/reference/mask-alpha.quartz.argb32.ref.png differ
diff --git a/test/reference/mask-alpha.rgb24.ref.png b/test/reference/mask-alpha.rgb24.ref.png
new file mode 100644 (file)
index 0000000..070ddf8
Binary files /dev/null and b/test/reference/mask-alpha.rgb24.ref.png differ
diff --git a/test/reference/mask-alpha.svg.rgb24.xfail.png b/test/reference/mask-alpha.svg.rgb24.xfail.png
new file mode 100644 (file)
index 0000000..15ebf75
Binary files /dev/null and b/test/reference/mask-alpha.svg.rgb24.xfail.png differ
diff --git a/test/reference/mask-alpha.traps.argb32.ref.png b/test/reference/mask-alpha.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..a0b9017
Binary files /dev/null and b/test/reference/mask-alpha.traps.argb32.ref.png differ
diff --git a/test/reference/mask-alpha.traps.rgb24.ref.png b/test/reference/mask-alpha.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..d8f4b8f
Binary files /dev/null and b/test/reference/mask-alpha.traps.rgb24.ref.png differ
diff --git a/test/reference/mask-ctm.base.argb32.ref.png b/test/reference/mask-ctm.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..07d903d
Binary files /dev/null and b/test/reference/mask-ctm.base.argb32.ref.png differ
diff --git a/test/reference/mask-ctm.base.rgb24.ref.png b/test/reference/mask-ctm.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..de3fa09
Binary files /dev/null and b/test/reference/mask-ctm.base.rgb24.ref.png differ
diff --git a/test/reference/mask-ctm.ref.png b/test/reference/mask-ctm.ref.png
new file mode 100644 (file)
index 0000000..88a0402
Binary files /dev/null and b/test/reference/mask-ctm.ref.png differ
diff --git a/test/reference/mask-ctm.rgb24.ref.png b/test/reference/mask-ctm.rgb24.ref.png
new file mode 100644 (file)
index 0000000..266cb0d
Binary files /dev/null and b/test/reference/mask-ctm.rgb24.ref.png differ
diff --git a/test/reference/mask-ctm.traps.argb32.ref.png b/test/reference/mask-ctm.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..07d903d
Binary files /dev/null and b/test/reference/mask-ctm.traps.argb32.ref.png differ
diff --git a/test/reference/mask-ctm.traps.rgb24.ref.png b/test/reference/mask-ctm.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..de3fa09
Binary files /dev/null and b/test/reference/mask-ctm.traps.rgb24.ref.png differ
diff --git a/test/reference/mask-glyphs.gl.ref.png b/test/reference/mask-glyphs.gl.ref.png
new file mode 100644 (file)
index 0000000..d341097
Binary files /dev/null and b/test/reference/mask-glyphs.gl.ref.png differ
diff --git a/test/reference/mask-glyphs.image16.ref.png b/test/reference/mask-glyphs.image16.ref.png
new file mode 100644 (file)
index 0000000..177e795
Binary files /dev/null and b/test/reference/mask-glyphs.image16.ref.png differ
diff --git a/test/reference/mask-glyphs.pdf.ref.png b/test/reference/mask-glyphs.pdf.ref.png
new file mode 100644 (file)
index 0000000..673ef92
Binary files /dev/null and b/test/reference/mask-glyphs.pdf.ref.png differ
diff --git a/test/reference/mask-glyphs.ref.png b/test/reference/mask-glyphs.ref.png
new file mode 100644 (file)
index 0000000..5beda0d
Binary files /dev/null and b/test/reference/mask-glyphs.ref.png differ
diff --git a/test/reference/mask-glyphs.svg.ref.png b/test/reference/mask-glyphs.svg.ref.png
new file mode 100644 (file)
index 0000000..bbc44f2
Binary files /dev/null and b/test/reference/mask-glyphs.svg.ref.png differ
diff --git a/test/reference/mask-surface-ctm.base.argb32.ref.png b/test/reference/mask-surface-ctm.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..07d903d
Binary files /dev/null and b/test/reference/mask-surface-ctm.base.argb32.ref.png differ
diff --git a/test/reference/mask-surface-ctm.base.rgb24.ref.png b/test/reference/mask-surface-ctm.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..de3fa09
Binary files /dev/null and b/test/reference/mask-surface-ctm.base.rgb24.ref.png differ
diff --git a/test/reference/mask-surface-ctm.ref.png b/test/reference/mask-surface-ctm.ref.png
new file mode 100644 (file)
index 0000000..744b1dd
Binary files /dev/null and b/test/reference/mask-surface-ctm.ref.png differ
diff --git a/test/reference/mask-surface-ctm.rgb24.ref.png b/test/reference/mask-surface-ctm.rgb24.ref.png
new file mode 100644 (file)
index 0000000..a2bdc61
Binary files /dev/null and b/test/reference/mask-surface-ctm.rgb24.ref.png differ
diff --git a/test/reference/mask-surface-ctm.traps.argb32.ref.png b/test/reference/mask-surface-ctm.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..07d903d
Binary files /dev/null and b/test/reference/mask-surface-ctm.traps.argb32.ref.png differ
diff --git a/test/reference/mask-surface-ctm.traps.rgb24.ref.png b/test/reference/mask-surface-ctm.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..de3fa09
Binary files /dev/null and b/test/reference/mask-surface-ctm.traps.rgb24.ref.png differ
diff --git a/test/reference/mask-transformed-image.base.argb32.ref.png b/test/reference/mask-transformed-image.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..af84b39
Binary files /dev/null and b/test/reference/mask-transformed-image.base.argb32.ref.png differ
diff --git a/test/reference/mask-transformed-image.base.rgb24.ref.png b/test/reference/mask-transformed-image.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..af84b39
Binary files /dev/null and b/test/reference/mask-transformed-image.base.rgb24.ref.png differ
diff --git a/test/reference/mask-transformed-image.image16.ref.png b/test/reference/mask-transformed-image.image16.ref.png
new file mode 100644 (file)
index 0000000..9e196ef
Binary files /dev/null and b/test/reference/mask-transformed-image.image16.ref.png differ
diff --git a/test/reference/mask-transformed-image.pdf.ref.png b/test/reference/mask-transformed-image.pdf.ref.png
new file mode 100644 (file)
index 0000000..33ec279
Binary files /dev/null and b/test/reference/mask-transformed-image.pdf.ref.png differ
diff --git a/test/reference/mask-transformed-image.quartz.ref.png b/test/reference/mask-transformed-image.quartz.ref.png
new file mode 100644 (file)
index 0000000..58ac575
Binary files /dev/null and b/test/reference/mask-transformed-image.quartz.ref.png differ
diff --git a/test/reference/mask-transformed-image.ref.png b/test/reference/mask-transformed-image.ref.png
new file mode 100644 (file)
index 0000000..e634f75
Binary files /dev/null and b/test/reference/mask-transformed-image.ref.png differ
diff --git a/test/reference/mask-transformed-image.traps.argb32.ref.png b/test/reference/mask-transformed-image.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..af84b39
Binary files /dev/null and b/test/reference/mask-transformed-image.traps.argb32.ref.png differ
diff --git a/test/reference/mask-transformed-image.traps.rgb24.ref.png b/test/reference/mask-transformed-image.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..af84b39
Binary files /dev/null and b/test/reference/mask-transformed-image.traps.rgb24.ref.png differ
diff --git a/test/reference/mask-transformed-similar.base.argb32.ref.png b/test/reference/mask-transformed-similar.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..af84b39
Binary files /dev/null and b/test/reference/mask-transformed-similar.base.argb32.ref.png differ
diff --git a/test/reference/mask-transformed-similar.base.rgb24.ref.png b/test/reference/mask-transformed-similar.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..af84b39
Binary files /dev/null and b/test/reference/mask-transformed-similar.base.rgb24.ref.png differ
diff --git a/test/reference/mask-transformed-similar.image16.ref.png b/test/reference/mask-transformed-similar.image16.ref.png
new file mode 100644 (file)
index 0000000..9e196ef
Binary files /dev/null and b/test/reference/mask-transformed-similar.image16.ref.png differ
diff --git a/test/reference/mask-transformed-similar.pdf.ref.png b/test/reference/mask-transformed-similar.pdf.ref.png
new file mode 100644 (file)
index 0000000..e8d3879
Binary files /dev/null and b/test/reference/mask-transformed-similar.pdf.ref.png differ
diff --git a/test/reference/mask-transformed-similar.quartz.ref.png b/test/reference/mask-transformed-similar.quartz.ref.png
new file mode 100644 (file)
index 0000000..58ac575
Binary files /dev/null and b/test/reference/mask-transformed-similar.quartz.ref.png differ
diff --git a/test/reference/mask-transformed-similar.ref.png b/test/reference/mask-transformed-similar.ref.png
new file mode 100644 (file)
index 0000000..e634f75
Binary files /dev/null and b/test/reference/mask-transformed-similar.ref.png differ
diff --git a/test/reference/mask-transformed-similar.svg.ref.png b/test/reference/mask-transformed-similar.svg.ref.png
new file mode 100644 (file)
index 0000000..a5b9b00
Binary files /dev/null and b/test/reference/mask-transformed-similar.svg.ref.png differ
diff --git a/test/reference/mask-transformed-similar.traps.argb32.ref.png b/test/reference/mask-transformed-similar.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..af84b39
Binary files /dev/null and b/test/reference/mask-transformed-similar.traps.argb32.ref.png differ
diff --git a/test/reference/mask-transformed-similar.traps.rgb24.ref.png b/test/reference/mask-transformed-similar.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..af84b39
Binary files /dev/null and b/test/reference/mask-transformed-similar.traps.rgb24.ref.png differ
diff --git a/test/reference/mask.argb32.ref.png b/test/reference/mask.argb32.ref.png
new file mode 100644 (file)
index 0000000..f1f14d7
Binary files /dev/null and b/test/reference/mask.argb32.ref.png differ
diff --git a/test/reference/mask.base.argb32.ref.png b/test/reference/mask.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..3286ce6
Binary files /dev/null and b/test/reference/mask.base.argb32.ref.png differ
diff --git a/test/reference/mask.base.rgb24.ref.png b/test/reference/mask.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..edcabaf
Binary files /dev/null and b/test/reference/mask.base.rgb24.ref.png differ
diff --git a/test/reference/mask.image16.ref.png b/test/reference/mask.image16.ref.png
new file mode 100644 (file)
index 0000000..4ab52de
Binary files /dev/null and b/test/reference/mask.image16.ref.png differ
diff --git a/test/reference/mask.pdf.argb32.ref.png b/test/reference/mask.pdf.argb32.ref.png
new file mode 100644 (file)
index 0000000..33769ee
Binary files /dev/null and b/test/reference/mask.pdf.argb32.ref.png differ
diff --git a/test/reference/mask.pdf.rgb24.ref.png b/test/reference/mask.pdf.rgb24.ref.png
new file mode 100644 (file)
index 0000000..dbd49a8
Binary files /dev/null and b/test/reference/mask.pdf.rgb24.ref.png differ
diff --git a/test/reference/mask.quartz.argb32.ref.png b/test/reference/mask.quartz.argb32.ref.png
new file mode 100644 (file)
index 0000000..c7ab76e
Binary files /dev/null and b/test/reference/mask.quartz.argb32.ref.png differ
diff --git a/test/reference/mask.quartz.rgb24.ref.png b/test/reference/mask.quartz.rgb24.ref.png
new file mode 100644 (file)
index 0000000..f475ba9
Binary files /dev/null and b/test/reference/mask.quartz.rgb24.ref.png differ
diff --git a/test/reference/mask.rgb24.ref.png b/test/reference/mask.rgb24.ref.png
new file mode 100644 (file)
index 0000000..0f9cb37
Binary files /dev/null and b/test/reference/mask.rgb24.ref.png differ
diff --git a/test/reference/mask.svg.argb32.xfail.png b/test/reference/mask.svg.argb32.xfail.png
new file mode 100644 (file)
index 0000000..8672480
Binary files /dev/null and b/test/reference/mask.svg.argb32.xfail.png differ
diff --git a/test/reference/mask.svg.rgb24.xfail.png b/test/reference/mask.svg.rgb24.xfail.png
new file mode 100644 (file)
index 0000000..743a758
Binary files /dev/null and b/test/reference/mask.svg.rgb24.xfail.png differ
diff --git a/test/reference/mask.traps.argb32.ref.png b/test/reference/mask.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..3286ce6
Binary files /dev/null and b/test/reference/mask.traps.argb32.ref.png differ
diff --git a/test/reference/mask.traps.rgb24.ref.png b/test/reference/mask.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..edcabaf
Binary files /dev/null and b/test/reference/mask.traps.rgb24.ref.png differ
diff --git a/test/reference/mesh-pattern-accuracy.base.argb32.ref.png b/test/reference/mesh-pattern-accuracy.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..dfc19ff
Binary files /dev/null and b/test/reference/mesh-pattern-accuracy.base.argb32.ref.png differ
diff --git a/test/reference/mesh-pattern-accuracy.base.rgb24.ref.png b/test/reference/mesh-pattern-accuracy.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..dfc19ff
Binary files /dev/null and b/test/reference/mesh-pattern-accuracy.base.rgb24.ref.png differ
diff --git a/test/reference/mesh-pattern-accuracy.image16.ref.png b/test/reference/mesh-pattern-accuracy.image16.ref.png
new file mode 100644 (file)
index 0000000..a82e4fb
Binary files /dev/null and b/test/reference/mesh-pattern-accuracy.image16.ref.png differ
diff --git a/test/reference/mesh-pattern-accuracy.ref.png b/test/reference/mesh-pattern-accuracy.ref.png
new file mode 100644 (file)
index 0000000..dfc19ff
Binary files /dev/null and b/test/reference/mesh-pattern-accuracy.ref.png differ
diff --git a/test/reference/mesh-pattern-accuracy.traps.argb32.ref.png b/test/reference/mesh-pattern-accuracy.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..dfc19ff
Binary files /dev/null and b/test/reference/mesh-pattern-accuracy.traps.argb32.ref.png differ
diff --git a/test/reference/mesh-pattern-accuracy.traps.rgb24.ref.png b/test/reference/mesh-pattern-accuracy.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..dfc19ff
Binary files /dev/null and b/test/reference/mesh-pattern-accuracy.traps.rgb24.ref.png differ
diff --git a/test/reference/mesh-pattern-conical.base.argb32.ref.png b/test/reference/mesh-pattern-conical.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..f5dc21d
Binary files /dev/null and b/test/reference/mesh-pattern-conical.base.argb32.ref.png differ
diff --git a/test/reference/mesh-pattern-conical.base.rgb24.ref.png b/test/reference/mesh-pattern-conical.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..f5dc21d
Binary files /dev/null and b/test/reference/mesh-pattern-conical.base.rgb24.ref.png differ
diff --git a/test/reference/mesh-pattern-conical.image16.ref.png b/test/reference/mesh-pattern-conical.image16.ref.png
new file mode 100644 (file)
index 0000000..b8f9416
Binary files /dev/null and b/test/reference/mesh-pattern-conical.image16.ref.png differ
diff --git a/test/reference/mesh-pattern-conical.ref.png b/test/reference/mesh-pattern-conical.ref.png
new file mode 100644 (file)
index 0000000..f5dc21d
Binary files /dev/null and b/test/reference/mesh-pattern-conical.ref.png differ
diff --git a/test/reference/mesh-pattern-conical.traps.argb32.ref.png b/test/reference/mesh-pattern-conical.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..f5dc21d
Binary files /dev/null and b/test/reference/mesh-pattern-conical.traps.argb32.ref.png differ
diff --git a/test/reference/mesh-pattern-conical.traps.rgb24.ref.png b/test/reference/mesh-pattern-conical.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..f5dc21d
Binary files /dev/null and b/test/reference/mesh-pattern-conical.traps.rgb24.ref.png differ
diff --git a/test/reference/mesh-pattern-control-points.base.argb32.ref.png b/test/reference/mesh-pattern-control-points.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..841fc3e
Binary files /dev/null and b/test/reference/mesh-pattern-control-points.base.argb32.ref.png differ
diff --git a/test/reference/mesh-pattern-control-points.base.rgb24.ref.png b/test/reference/mesh-pattern-control-points.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..841fc3e
Binary files /dev/null and b/test/reference/mesh-pattern-control-points.base.rgb24.ref.png differ
diff --git a/test/reference/mesh-pattern-control-points.image16.ref.png b/test/reference/mesh-pattern-control-points.image16.ref.png
new file mode 100644 (file)
index 0000000..b664ef9
Binary files /dev/null and b/test/reference/mesh-pattern-control-points.image16.ref.png differ
diff --git a/test/reference/mesh-pattern-control-points.ref.png b/test/reference/mesh-pattern-control-points.ref.png
new file mode 100644 (file)
index 0000000..841fc3e
Binary files /dev/null and b/test/reference/mesh-pattern-control-points.ref.png differ
diff --git a/test/reference/mesh-pattern-control-points.traps.argb32.ref.png b/test/reference/mesh-pattern-control-points.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..841fc3e
Binary files /dev/null and b/test/reference/mesh-pattern-control-points.traps.argb32.ref.png differ
diff --git a/test/reference/mesh-pattern-control-points.traps.rgb24.ref.png b/test/reference/mesh-pattern-control-points.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..841fc3e
Binary files /dev/null and b/test/reference/mesh-pattern-control-points.traps.rgb24.ref.png differ
diff --git a/test/reference/mesh-pattern-fold.base.argb32.ref.png b/test/reference/mesh-pattern-fold.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..6275b82
Binary files /dev/null and b/test/reference/mesh-pattern-fold.base.argb32.ref.png differ
diff --git a/test/reference/mesh-pattern-fold.base.rgb24.ref.png b/test/reference/mesh-pattern-fold.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..6275b82
Binary files /dev/null and b/test/reference/mesh-pattern-fold.base.rgb24.ref.png differ
diff --git a/test/reference/mesh-pattern-fold.image16.ref.png b/test/reference/mesh-pattern-fold.image16.ref.png
new file mode 100644 (file)
index 0000000..4264ad2
Binary files /dev/null and b/test/reference/mesh-pattern-fold.image16.ref.png differ
diff --git a/test/reference/mesh-pattern-fold.ref.png b/test/reference/mesh-pattern-fold.ref.png
new file mode 100644 (file)
index 0000000..6275b82
Binary files /dev/null and b/test/reference/mesh-pattern-fold.ref.png differ
diff --git a/test/reference/mesh-pattern-fold.traps.argb32.ref.png b/test/reference/mesh-pattern-fold.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..6275b82
Binary files /dev/null and b/test/reference/mesh-pattern-fold.traps.argb32.ref.png differ
diff --git a/test/reference/mesh-pattern-fold.traps.rgb24.ref.png b/test/reference/mesh-pattern-fold.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..6275b82
Binary files /dev/null and b/test/reference/mesh-pattern-fold.traps.rgb24.ref.png differ
diff --git a/test/reference/mesh-pattern-overlap.base.argb32.ref.png b/test/reference/mesh-pattern-overlap.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..1394c9e
Binary files /dev/null and b/test/reference/mesh-pattern-overlap.base.argb32.ref.png differ
diff --git a/test/reference/mesh-pattern-overlap.base.rgb24.ref.png b/test/reference/mesh-pattern-overlap.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..1394c9e
Binary files /dev/null and b/test/reference/mesh-pattern-overlap.base.rgb24.ref.png differ
diff --git a/test/reference/mesh-pattern-overlap.image16.ref.png b/test/reference/mesh-pattern-overlap.image16.ref.png
new file mode 100644 (file)
index 0000000..a67f7dd
Binary files /dev/null and b/test/reference/mesh-pattern-overlap.image16.ref.png differ
diff --git a/test/reference/mesh-pattern-overlap.ref.png b/test/reference/mesh-pattern-overlap.ref.png
new file mode 100644 (file)
index 0000000..1394c9e
Binary files /dev/null and b/test/reference/mesh-pattern-overlap.ref.png differ
diff --git a/test/reference/mesh-pattern-overlap.traps.argb32.ref.png b/test/reference/mesh-pattern-overlap.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..1394c9e
Binary files /dev/null and b/test/reference/mesh-pattern-overlap.traps.argb32.ref.png differ
diff --git a/test/reference/mesh-pattern-overlap.traps.rgb24.ref.png b/test/reference/mesh-pattern-overlap.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..1394c9e
Binary files /dev/null and b/test/reference/mesh-pattern-overlap.traps.rgb24.ref.png differ
diff --git a/test/reference/mesh-pattern-transformed.base.argb32.ref.png b/test/reference/mesh-pattern-transformed.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..9aa482f
Binary files /dev/null and b/test/reference/mesh-pattern-transformed.base.argb32.ref.png differ
diff --git a/test/reference/mesh-pattern-transformed.base.rgb24.ref.png b/test/reference/mesh-pattern-transformed.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..9aa482f
Binary files /dev/null and b/test/reference/mesh-pattern-transformed.base.rgb24.ref.png differ
diff --git a/test/reference/mesh-pattern-transformed.image16.ref.png b/test/reference/mesh-pattern-transformed.image16.ref.png
new file mode 100644 (file)
index 0000000..0645b86
Binary files /dev/null and b/test/reference/mesh-pattern-transformed.image16.ref.png differ
diff --git a/test/reference/mesh-pattern-transformed.ref.png b/test/reference/mesh-pattern-transformed.ref.png
new file mode 100644 (file)
index 0000000..9aa482f
Binary files /dev/null and b/test/reference/mesh-pattern-transformed.ref.png differ
diff --git a/test/reference/mesh-pattern-transformed.traps.argb32.ref.png b/test/reference/mesh-pattern-transformed.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..9aa482f
Binary files /dev/null and b/test/reference/mesh-pattern-transformed.traps.argb32.ref.png differ
diff --git a/test/reference/mesh-pattern-transformed.traps.rgb24.ref.png b/test/reference/mesh-pattern-transformed.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..9aa482f
Binary files /dev/null and b/test/reference/mesh-pattern-transformed.traps.rgb24.ref.png differ
diff --git a/test/reference/mesh-pattern.base.argb32.ref.png b/test/reference/mesh-pattern.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..1f76639
Binary files /dev/null and b/test/reference/mesh-pattern.base.argb32.ref.png differ
diff --git a/test/reference/mesh-pattern.base.rgb24.ref.png b/test/reference/mesh-pattern.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..1f76639
Binary files /dev/null and b/test/reference/mesh-pattern.base.rgb24.ref.png differ
diff --git a/test/reference/mesh-pattern.image16.ref.png b/test/reference/mesh-pattern.image16.ref.png
new file mode 100644 (file)
index 0000000..bd63538
Binary files /dev/null and b/test/reference/mesh-pattern.image16.ref.png differ
diff --git a/test/reference/mesh-pattern.ref.png b/test/reference/mesh-pattern.ref.png
new file mode 100644 (file)
index 0000000..1f76639
Binary files /dev/null and b/test/reference/mesh-pattern.ref.png differ
diff --git a/test/reference/mesh-pattern.traps.argb32.ref.png b/test/reference/mesh-pattern.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..1f76639
Binary files /dev/null and b/test/reference/mesh-pattern.traps.argb32.ref.png differ
diff --git a/test/reference/mesh-pattern.traps.rgb24.ref.png b/test/reference/mesh-pattern.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..1f76639
Binary files /dev/null and b/test/reference/mesh-pattern.traps.rgb24.ref.png differ
diff --git a/test/reference/mime-data.base.argb32.ref.png b/test/reference/mime-data.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..3a912c5
Binary files /dev/null and b/test/reference/mime-data.base.argb32.ref.png differ
diff --git a/test/reference/mime-data.base.rgb24.ref.png b/test/reference/mime-data.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..3a912c5
Binary files /dev/null and b/test/reference/mime-data.base.rgb24.ref.png differ
diff --git a/test/reference/mime-data.pdf.ref.png b/test/reference/mime-data.pdf.ref.png
new file mode 100644 (file)
index 0000000..90a08f5
Binary files /dev/null and b/test/reference/mime-data.pdf.ref.png differ
diff --git a/test/reference/mime-data.ps.ref.png b/test/reference/mime-data.ps.ref.png
new file mode 100644 (file)
index 0000000..721fb9c
Binary files /dev/null and b/test/reference/mime-data.ps.ref.png differ
diff --git a/test/reference/mime-data.ref.png b/test/reference/mime-data.ref.png
new file mode 100644 (file)
index 0000000..3a912c5
Binary files /dev/null and b/test/reference/mime-data.ref.png differ
diff --git a/test/reference/mime-data.script.ref.png b/test/reference/mime-data.script.ref.png
new file mode 100644 (file)
index 0000000..64d7bf3
Binary files /dev/null and b/test/reference/mime-data.script.ref.png differ
diff --git a/test/reference/mime-data.svg.ref.png b/test/reference/mime-data.svg.ref.png
new file mode 100644 (file)
index 0000000..81fde72
Binary files /dev/null and b/test/reference/mime-data.svg.ref.png differ
diff --git a/test/reference/mime-data.traps.argb32.ref.png b/test/reference/mime-data.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..3a912c5
Binary files /dev/null and b/test/reference/mime-data.traps.argb32.ref.png differ
diff --git a/test/reference/mime-data.traps.rgb24.ref.png b/test/reference/mime-data.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..3a912c5
Binary files /dev/null and b/test/reference/mime-data.traps.rgb24.ref.png differ
diff --git a/test/reference/mime-surface.base.argb32.ref.png b/test/reference/mime-surface.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..ac5e560
Binary files /dev/null and b/test/reference/mime-surface.base.argb32.ref.png differ
diff --git a/test/reference/mime-surface.base.rgb24.ref.png b/test/reference/mime-surface.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..ac5e560
Binary files /dev/null and b/test/reference/mime-surface.base.rgb24.ref.png differ
diff --git a/test/reference/mime-surface.ref.png b/test/reference/mime-surface.ref.png
new file mode 100644 (file)
index 0000000..ac5e560
Binary files /dev/null and b/test/reference/mime-surface.ref.png differ
diff --git a/test/reference/mime-surface.traps.argb32.ref.png b/test/reference/mime-surface.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..ac5e560
Binary files /dev/null and b/test/reference/mime-surface.traps.argb32.ref.png differ
diff --git a/test/reference/mime-surface.traps.rgb24.ref.png b/test/reference/mime-surface.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..ac5e560
Binary files /dev/null and b/test/reference/mime-surface.traps.rgb24.ref.png differ
diff --git a/test/reference/miter-precision.base.argb32.ref.png b/test/reference/miter-precision.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..b9eee8a
Binary files /dev/null and b/test/reference/miter-precision.base.argb32.ref.png differ
diff --git a/test/reference/miter-precision.base.rgb24.ref.png b/test/reference/miter-precision.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..b9eee8a
Binary files /dev/null and b/test/reference/miter-precision.base.rgb24.ref.png differ
diff --git a/test/reference/miter-precision.ps2.ref.png b/test/reference/miter-precision.ps2.ref.png
new file mode 100644 (file)
index 0000000..c2b69ad
Binary files /dev/null and b/test/reference/miter-precision.ps2.ref.png differ
diff --git a/test/reference/miter-precision.ps3.ref.png b/test/reference/miter-precision.ps3.ref.png
new file mode 100644 (file)
index 0000000..c2b69ad
Binary files /dev/null and b/test/reference/miter-precision.ps3.ref.png differ
diff --git a/test/reference/miter-precision.ref.png b/test/reference/miter-precision.ref.png
new file mode 100644 (file)
index 0000000..df41581
Binary files /dev/null and b/test/reference/miter-precision.ref.png differ
diff --git a/test/reference/miter-precision.traps.argb32.ref.png b/test/reference/miter-precision.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..b9eee8a
Binary files /dev/null and b/test/reference/miter-precision.traps.argb32.ref.png differ
diff --git a/test/reference/miter-precision.traps.rgb24.ref.png b/test/reference/miter-precision.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..b9eee8a
Binary files /dev/null and b/test/reference/miter-precision.traps.rgb24.ref.png differ
diff --git a/test/reference/move-to-show-surface.base.argb32.ref.png b/test/reference/move-to-show-surface.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..0a145d9
Binary files /dev/null and b/test/reference/move-to-show-surface.base.argb32.ref.png differ
diff --git a/test/reference/move-to-show-surface.base.rgb24.ref.png b/test/reference/move-to-show-surface.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..0a145d9
Binary files /dev/null and b/test/reference/move-to-show-surface.base.rgb24.ref.png differ
diff --git a/test/reference/move-to-show-surface.ref.png b/test/reference/move-to-show-surface.ref.png
new file mode 100644 (file)
index 0000000..765adc4
Binary files /dev/null and b/test/reference/move-to-show-surface.ref.png differ
diff --git a/test/reference/move-to-show-surface.traps.argb32.ref.png b/test/reference/move-to-show-surface.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..0a145d9
Binary files /dev/null and b/test/reference/move-to-show-surface.traps.argb32.ref.png differ
diff --git a/test/reference/move-to-show-surface.traps.rgb24.ref.png b/test/reference/move-to-show-surface.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..0a145d9
Binary files /dev/null and b/test/reference/move-to-show-surface.traps.rgb24.ref.png differ
diff --git a/test/reference/new-sub-path.argb32.ref.png b/test/reference/new-sub-path.argb32.ref.png
new file mode 100644 (file)
index 0000000..aec47e3
Binary files /dev/null and b/test/reference/new-sub-path.argb32.ref.png differ
diff --git a/test/reference/new-sub-path.base.argb32.ref.png b/test/reference/new-sub-path.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..13e0675
Binary files /dev/null and b/test/reference/new-sub-path.base.argb32.ref.png differ
diff --git a/test/reference/new-sub-path.base.rgb24.ref.png b/test/reference/new-sub-path.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..b69e4ab
Binary files /dev/null and b/test/reference/new-sub-path.base.rgb24.ref.png differ
diff --git a/test/reference/new-sub-path.pdf.argb32.ref.png b/test/reference/new-sub-path.pdf.argb32.ref.png
new file mode 100644 (file)
index 0000000..41fe131
Binary files /dev/null and b/test/reference/new-sub-path.pdf.argb32.ref.png differ
diff --git a/test/reference/new-sub-path.ps2.argb32.ref.png b/test/reference/new-sub-path.ps2.argb32.ref.png
new file mode 100644 (file)
index 0000000..45253db
Binary files /dev/null and b/test/reference/new-sub-path.ps2.argb32.ref.png differ
diff --git a/test/reference/new-sub-path.ps2.rgb24.ref.png b/test/reference/new-sub-path.ps2.rgb24.ref.png
new file mode 100644 (file)
index 0000000..bceb5b7
Binary files /dev/null and b/test/reference/new-sub-path.ps2.rgb24.ref.png differ
diff --git a/test/reference/new-sub-path.ps3.argb32.ref.png b/test/reference/new-sub-path.ps3.argb32.ref.png
new file mode 100644 (file)
index 0000000..45253db
Binary files /dev/null and b/test/reference/new-sub-path.ps3.argb32.ref.png differ
diff --git a/test/reference/new-sub-path.ps3.rgb24.ref.png b/test/reference/new-sub-path.ps3.rgb24.ref.png
new file mode 100644 (file)
index 0000000..bceb5b7
Binary files /dev/null and b/test/reference/new-sub-path.ps3.rgb24.ref.png differ
diff --git a/test/reference/new-sub-path.quartz.ref.png b/test/reference/new-sub-path.quartz.ref.png
new file mode 100644 (file)
index 0000000..20d49ba
Binary files /dev/null and b/test/reference/new-sub-path.quartz.ref.png differ
diff --git a/test/reference/new-sub-path.rgb24.ref.png b/test/reference/new-sub-path.rgb24.ref.png
new file mode 100644 (file)
index 0000000..cdc270c
Binary files /dev/null and b/test/reference/new-sub-path.rgb24.ref.png differ
diff --git a/test/reference/new-sub-path.traps.argb32.ref.png b/test/reference/new-sub-path.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..13e0675
Binary files /dev/null and b/test/reference/new-sub-path.traps.argb32.ref.png differ
diff --git a/test/reference/new-sub-path.traps.rgb24.ref.png b/test/reference/new-sub-path.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..b69e4ab
Binary files /dev/null and b/test/reference/new-sub-path.traps.rgb24.ref.png differ
diff --git a/test/reference/nil-surface.base.argb32.ref.png b/test/reference/nil-surface.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..50e35a2
Binary files /dev/null and b/test/reference/nil-surface.base.argb32.ref.png differ
diff --git a/test/reference/nil-surface.base.rgb24.ref.png b/test/reference/nil-surface.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..7d5589c
Binary files /dev/null and b/test/reference/nil-surface.base.rgb24.ref.png differ
diff --git a/test/reference/nil-surface.ref.png b/test/reference/nil-surface.ref.png
new file mode 100644 (file)
index 0000000..79dd2bc
Binary files /dev/null and b/test/reference/nil-surface.ref.png differ
diff --git a/test/reference/nil-surface.rgb24.ref.png b/test/reference/nil-surface.rgb24.ref.png
new file mode 100644 (file)
index 0000000..97b2cfa
Binary files /dev/null and b/test/reference/nil-surface.rgb24.ref.png differ
diff --git a/test/reference/nil-surface.traps.argb32.ref.png b/test/reference/nil-surface.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..50e35a2
Binary files /dev/null and b/test/reference/nil-surface.traps.argb32.ref.png differ
diff --git a/test/reference/nil-surface.traps.rgb24.ref.png b/test/reference/nil-surface.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..7d5589c
Binary files /dev/null and b/test/reference/nil-surface.traps.rgb24.ref.png differ
diff --git a/test/reference/operator-alpha-alpha.base.argb32.ref.png b/test/reference/operator-alpha-alpha.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..695d0d0
Binary files /dev/null and b/test/reference/operator-alpha-alpha.base.argb32.ref.png differ
diff --git a/test/reference/operator-alpha-alpha.base.rgb24.ref.png b/test/reference/operator-alpha-alpha.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..695d0d0
Binary files /dev/null and b/test/reference/operator-alpha-alpha.base.rgb24.ref.png differ
diff --git a/test/reference/operator-alpha-alpha.image16.ref.png b/test/reference/operator-alpha-alpha.image16.ref.png
new file mode 100644 (file)
index 0000000..31eba5f
Binary files /dev/null and b/test/reference/operator-alpha-alpha.image16.ref.png differ
diff --git a/test/reference/operator-alpha-alpha.pdf.xfail.png b/test/reference/operator-alpha-alpha.pdf.xfail.png
new file mode 100644 (file)
index 0000000..0a06685
Binary files /dev/null and b/test/reference/operator-alpha-alpha.pdf.xfail.png differ
diff --git a/test/reference/operator-alpha-alpha.ps.xfail.png b/test/reference/operator-alpha-alpha.ps.xfail.png
new file mode 100644 (file)
index 0000000..e7c4fea
Binary files /dev/null and b/test/reference/operator-alpha-alpha.ps.xfail.png differ
diff --git a/test/reference/operator-alpha-alpha.ref.png b/test/reference/operator-alpha-alpha.ref.png
new file mode 100644 (file)
index 0000000..695d0d0
Binary files /dev/null and b/test/reference/operator-alpha-alpha.ref.png differ
diff --git a/test/reference/operator-alpha-alpha.svg.xfail.png b/test/reference/operator-alpha-alpha.svg.xfail.png
new file mode 100644 (file)
index 0000000..c7dc8cb
Binary files /dev/null and b/test/reference/operator-alpha-alpha.svg.xfail.png differ
diff --git a/test/reference/operator-alpha-alpha.traps.argb32.ref.png b/test/reference/operator-alpha-alpha.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..695d0d0
Binary files /dev/null and b/test/reference/operator-alpha-alpha.traps.argb32.ref.png differ
diff --git a/test/reference/operator-alpha-alpha.traps.rgb24.ref.png b/test/reference/operator-alpha-alpha.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..695d0d0
Binary files /dev/null and b/test/reference/operator-alpha-alpha.traps.rgb24.ref.png differ
diff --git a/test/reference/operator-alpha.base.argb32.ref.png b/test/reference/operator-alpha.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..b4f3b71
Binary files /dev/null and b/test/reference/operator-alpha.base.argb32.ref.png differ
diff --git a/test/reference/operator-alpha.base.rgb24.ref.png b/test/reference/operator-alpha.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..bc7be3a
Binary files /dev/null and b/test/reference/operator-alpha.base.rgb24.ref.png differ
diff --git a/test/reference/operator-alpha.ref.png b/test/reference/operator-alpha.ref.png
new file mode 100644 (file)
index 0000000..b4f3b71
Binary files /dev/null and b/test/reference/operator-alpha.ref.png differ
diff --git a/test/reference/operator-alpha.rgb24.ref.png b/test/reference/operator-alpha.rgb24.ref.png
new file mode 100644 (file)
index 0000000..3263550
Binary files /dev/null and b/test/reference/operator-alpha.rgb24.ref.png differ
diff --git a/test/reference/operator-alpha.svg12.argb32.xfail.png b/test/reference/operator-alpha.svg12.argb32.xfail.png
new file mode 100644 (file)
index 0000000..e821d20
Binary files /dev/null and b/test/reference/operator-alpha.svg12.argb32.xfail.png differ
diff --git a/test/reference/operator-alpha.svg12.rgb24.xfail.png b/test/reference/operator-alpha.svg12.rgb24.xfail.png
new file mode 100644 (file)
index 0000000..42d9dde
Binary files /dev/null and b/test/reference/operator-alpha.svg12.rgb24.xfail.png differ
diff --git a/test/reference/operator-alpha.traps.argb32.ref.png b/test/reference/operator-alpha.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..b4f3b71
Binary files /dev/null and b/test/reference/operator-alpha.traps.argb32.ref.png differ
diff --git a/test/reference/operator-alpha.traps.rgb24.ref.png b/test/reference/operator-alpha.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..bc7be3a
Binary files /dev/null and b/test/reference/operator-alpha.traps.rgb24.ref.png differ
diff --git a/test/reference/operator-clear.argb32.ref.png b/test/reference/operator-clear.argb32.ref.png
new file mode 100644 (file)
index 0000000..2629740
Binary files /dev/null and b/test/reference/operator-clear.argb32.ref.png differ
diff --git a/test/reference/operator-clear.base.argb32.ref.png b/test/reference/operator-clear.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..235db6b
Binary files /dev/null and b/test/reference/operator-clear.base.argb32.ref.png differ
diff --git a/test/reference/operator-clear.base.rgb24.ref.png b/test/reference/operator-clear.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..fa07d4a
Binary files /dev/null and b/test/reference/operator-clear.base.rgb24.ref.png differ
diff --git a/test/reference/operator-clear.ps2.argb32.ref.png b/test/reference/operator-clear.ps2.argb32.ref.png
new file mode 100644 (file)
index 0000000..92b4111
Binary files /dev/null and b/test/reference/operator-clear.ps2.argb32.ref.png differ
diff --git a/test/reference/operator-clear.ps3.argb32.ref.png b/test/reference/operator-clear.ps3.argb32.ref.png
new file mode 100644 (file)
index 0000000..92b4111
Binary files /dev/null and b/test/reference/operator-clear.ps3.argb32.ref.png differ
diff --git a/test/reference/operator-clear.quartz.argb32.ref.png b/test/reference/operator-clear.quartz.argb32.ref.png
new file mode 100644 (file)
index 0000000..caf265e
Binary files /dev/null and b/test/reference/operator-clear.quartz.argb32.ref.png differ
diff --git a/test/reference/operator-clear.quartz.rgb24.ref.png b/test/reference/operator-clear.quartz.rgb24.ref.png
new file mode 100644 (file)
index 0000000..a07a6e0
Binary files /dev/null and b/test/reference/operator-clear.quartz.rgb24.ref.png differ
diff --git a/test/reference/operator-clear.rgb24.ref.png b/test/reference/operator-clear.rgb24.ref.png
new file mode 100644 (file)
index 0000000..0a730a1
Binary files /dev/null and b/test/reference/operator-clear.rgb24.ref.png differ
diff --git a/test/reference/operator-clear.svg12.argb32.xfail.png b/test/reference/operator-clear.svg12.argb32.xfail.png
new file mode 100644 (file)
index 0000000..7dfbd28
Binary files /dev/null and b/test/reference/operator-clear.svg12.argb32.xfail.png differ
diff --git a/test/reference/operator-clear.svg12.rgb24.xfail.png b/test/reference/operator-clear.svg12.rgb24.xfail.png
new file mode 100644 (file)
index 0000000..c561bc3
Binary files /dev/null and b/test/reference/operator-clear.svg12.rgb24.xfail.png differ
diff --git a/test/reference/operator-clear.traps.argb32.ref.png b/test/reference/operator-clear.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..235db6b
Binary files /dev/null and b/test/reference/operator-clear.traps.argb32.ref.png differ
diff --git a/test/reference/operator-clear.traps.rgb24.ref.png b/test/reference/operator-clear.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..fa07d4a
Binary files /dev/null and b/test/reference/operator-clear.traps.rgb24.ref.png differ
diff --git a/test/reference/operator-source.argb32.ref.png b/test/reference/operator-source.argb32.ref.png
new file mode 100644 (file)
index 0000000..31615f4
Binary files /dev/null and b/test/reference/operator-source.argb32.ref.png differ
diff --git a/test/reference/operator-source.base.argb32.ref.png b/test/reference/operator-source.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..6884f33
Binary files /dev/null and b/test/reference/operator-source.base.argb32.ref.png differ
diff --git a/test/reference/operator-source.base.rgb24.ref.png b/test/reference/operator-source.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..32a6a40
Binary files /dev/null and b/test/reference/operator-source.base.rgb24.ref.png differ
diff --git a/test/reference/operator-source.image16.ref.png b/test/reference/operator-source.image16.ref.png
new file mode 100644 (file)
index 0000000..4556260
Binary files /dev/null and b/test/reference/operator-source.image16.ref.png differ
diff --git a/test/reference/operator-source.rgb24.ref.png b/test/reference/operator-source.rgb24.ref.png
new file mode 100644 (file)
index 0000000..c876aba
Binary files /dev/null and b/test/reference/operator-source.rgb24.ref.png differ
diff --git a/test/reference/operator-source.traps.argb32.ref.png b/test/reference/operator-source.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..6884f33
Binary files /dev/null and b/test/reference/operator-source.traps.argb32.ref.png differ
diff --git a/test/reference/operator-source.traps.rgb24.ref.png b/test/reference/operator-source.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..32a6a40
Binary files /dev/null and b/test/reference/operator-source.traps.rgb24.ref.png differ
diff --git a/test/reference/operator-source.xlib-fallback.ref.png b/test/reference/operator-source.xlib-fallback.ref.png
new file mode 100644 (file)
index 0000000..4527ce7
Binary files /dev/null and b/test/reference/operator-source.xlib-fallback.ref.png differ
diff --git a/test/reference/operator.base.argb32.ref.png b/test/reference/operator.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..34fce98
Binary files /dev/null and b/test/reference/operator.base.argb32.ref.png differ
diff --git a/test/reference/operator.base.rgb24.ref.png b/test/reference/operator.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..aa6103d
Binary files /dev/null and b/test/reference/operator.base.rgb24.ref.png differ
diff --git a/test/reference/operator.ref.png b/test/reference/operator.ref.png
new file mode 100644 (file)
index 0000000..34fce98
Binary files /dev/null and b/test/reference/operator.ref.png differ
diff --git a/test/reference/operator.rgb24.ref.png b/test/reference/operator.rgb24.ref.png
new file mode 100644 (file)
index 0000000..3a6309c
Binary files /dev/null and b/test/reference/operator.rgb24.ref.png differ
diff --git a/test/reference/operator.svg12.argb32.xfail.png b/test/reference/operator.svg12.argb32.xfail.png
new file mode 100644 (file)
index 0000000..3996221
Binary files /dev/null and b/test/reference/operator.svg12.argb32.xfail.png differ
diff --git a/test/reference/operator.svg12.rgb24.xfail.png b/test/reference/operator.svg12.rgb24.xfail.png
new file mode 100644 (file)
index 0000000..198d4b1
Binary files /dev/null and b/test/reference/operator.svg12.rgb24.xfail.png differ
diff --git a/test/reference/operator.traps.argb32.ref.png b/test/reference/operator.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..34fce98
Binary files /dev/null and b/test/reference/operator.traps.argb32.ref.png differ
diff --git a/test/reference/operator.traps.rgb24.ref.png b/test/reference/operator.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..aa6103d
Binary files /dev/null and b/test/reference/operator.traps.rgb24.ref.png differ
diff --git a/test/reference/over-above-source.base.argb32.ref.png b/test/reference/over-above-source.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..8a0183a
Binary files /dev/null and b/test/reference/over-above-source.base.argb32.ref.png differ
diff --git a/test/reference/over-above-source.base.rgb24.ref.png b/test/reference/over-above-source.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..85c1997
Binary files /dev/null and b/test/reference/over-above-source.base.rgb24.ref.png differ
diff --git a/test/reference/over-above-source.ps2.argb32.ref.png b/test/reference/over-above-source.ps2.argb32.ref.png
new file mode 100644 (file)
index 0000000..7c90d08
Binary files /dev/null and b/test/reference/over-above-source.ps2.argb32.ref.png differ
diff --git a/test/reference/over-above-source.ps3.argb32.ref.png b/test/reference/over-above-source.ps3.argb32.ref.png
new file mode 100644 (file)
index 0000000..7c90d08
Binary files /dev/null and b/test/reference/over-above-source.ps3.argb32.ref.png differ
diff --git a/test/reference/over-above-source.quartz.argb32.ref.png b/test/reference/over-above-source.quartz.argb32.ref.png
new file mode 100644 (file)
index 0000000..eeb3622
Binary files /dev/null and b/test/reference/over-above-source.quartz.argb32.ref.png differ
diff --git a/test/reference/over-above-source.quartz.rgb24.ref.png b/test/reference/over-above-source.quartz.rgb24.ref.png
new file mode 100644 (file)
index 0000000..2ab3476
Binary files /dev/null and b/test/reference/over-above-source.quartz.rgb24.ref.png differ
diff --git a/test/reference/over-above-source.ref.png b/test/reference/over-above-source.ref.png
new file mode 100644 (file)
index 0000000..19a57c2
Binary files /dev/null and b/test/reference/over-above-source.ref.png differ
diff --git a/test/reference/over-above-source.rgb24.ref.png b/test/reference/over-above-source.rgb24.ref.png
new file mode 100644 (file)
index 0000000..ec37387
Binary files /dev/null and b/test/reference/over-above-source.rgb24.ref.png differ
diff --git a/test/reference/over-above-source.svg12.rgb24.xfail.png b/test/reference/over-above-source.svg12.rgb24.xfail.png
new file mode 100644 (file)
index 0000000..b2939c2
Binary files /dev/null and b/test/reference/over-above-source.svg12.rgb24.xfail.png differ
diff --git a/test/reference/over-above-source.traps.argb32.ref.png b/test/reference/over-above-source.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..8a0183a
Binary files /dev/null and b/test/reference/over-above-source.traps.argb32.ref.png differ
diff --git a/test/reference/over-above-source.traps.rgb24.ref.png b/test/reference/over-above-source.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..85c1997
Binary files /dev/null and b/test/reference/over-above-source.traps.rgb24.ref.png differ
diff --git a/test/reference/over-around-source.base.argb32.ref.png b/test/reference/over-around-source.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..fca7505
Binary files /dev/null and b/test/reference/over-around-source.base.argb32.ref.png differ
diff --git a/test/reference/over-around-source.base.rgb24.ref.png b/test/reference/over-around-source.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..e8dd91d
Binary files /dev/null and b/test/reference/over-around-source.base.rgb24.ref.png differ
diff --git a/test/reference/over-around-source.image16.ref.png b/test/reference/over-around-source.image16.ref.png
new file mode 100644 (file)
index 0000000..f571b7d
Binary files /dev/null and b/test/reference/over-around-source.image16.ref.png differ
diff --git a/test/reference/over-around-source.pdf.argb32.ref.png b/test/reference/over-around-source.pdf.argb32.ref.png
new file mode 100644 (file)
index 0000000..da700af
Binary files /dev/null and b/test/reference/over-around-source.pdf.argb32.ref.png differ
diff --git a/test/reference/over-around-source.ps2.argb32.ref.png b/test/reference/over-around-source.ps2.argb32.ref.png
new file mode 100644 (file)
index 0000000..4391759
Binary files /dev/null and b/test/reference/over-around-source.ps2.argb32.ref.png differ
diff --git a/test/reference/over-around-source.ps2.rgb24.ref.png b/test/reference/over-around-source.ps2.rgb24.ref.png
new file mode 100644 (file)
index 0000000..ee325ea
Binary files /dev/null and b/test/reference/over-around-source.ps2.rgb24.ref.png differ
diff --git a/test/reference/over-around-source.ps3.argb32.ref.png b/test/reference/over-around-source.ps3.argb32.ref.png
new file mode 100644 (file)
index 0000000..4391759
Binary files /dev/null and b/test/reference/over-around-source.ps3.argb32.ref.png differ
diff --git a/test/reference/over-around-source.ps3.rgb24.ref.png b/test/reference/over-around-source.ps3.rgb24.ref.png
new file mode 100644 (file)
index 0000000..ee325ea
Binary files /dev/null and b/test/reference/over-around-source.ps3.rgb24.ref.png differ
diff --git a/test/reference/over-around-source.quartz.argb32.ref.png b/test/reference/over-around-source.quartz.argb32.ref.png
new file mode 100644 (file)
index 0000000..26ab8e5
Binary files /dev/null and b/test/reference/over-around-source.quartz.argb32.ref.png differ
diff --git a/test/reference/over-around-source.ref.png b/test/reference/over-around-source.ref.png
new file mode 100644 (file)
index 0000000..01508d2
Binary files /dev/null and b/test/reference/over-around-source.ref.png differ
diff --git a/test/reference/over-around-source.rgb24.ref.png b/test/reference/over-around-source.rgb24.ref.png
new file mode 100644 (file)
index 0000000..717f6e5
Binary files /dev/null and b/test/reference/over-around-source.rgb24.ref.png differ
diff --git a/test/reference/over-around-source.svg12.argb32.xfail.png b/test/reference/over-around-source.svg12.argb32.xfail.png
new file mode 100644 (file)
index 0000000..bb29538
Binary files /dev/null and b/test/reference/over-around-source.svg12.argb32.xfail.png differ
diff --git a/test/reference/over-around-source.svg12.rgb24.xfail.png b/test/reference/over-around-source.svg12.rgb24.xfail.png
new file mode 100644 (file)
index 0000000..bb29538
Binary files /dev/null and b/test/reference/over-around-source.svg12.rgb24.xfail.png differ
diff --git a/test/reference/over-around-source.traps.argb32.ref.png b/test/reference/over-around-source.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..fca7505
Binary files /dev/null and b/test/reference/over-around-source.traps.argb32.ref.png differ
diff --git a/test/reference/over-around-source.traps.rgb24.ref.png b/test/reference/over-around-source.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..e8dd91d
Binary files /dev/null and b/test/reference/over-around-source.traps.rgb24.ref.png differ
diff --git a/test/reference/over-below-source.base.argb32.ref.png b/test/reference/over-below-source.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..c659363
Binary files /dev/null and b/test/reference/over-below-source.base.argb32.ref.png differ
diff --git a/test/reference/over-below-source.base.rgb24.ref.png b/test/reference/over-below-source.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..88a85ac
Binary files /dev/null and b/test/reference/over-below-source.base.rgb24.ref.png differ
diff --git a/test/reference/over-below-source.pdf.argb32.ref.png b/test/reference/over-below-source.pdf.argb32.ref.png
new file mode 100644 (file)
index 0000000..b9c4fe2
Binary files /dev/null and b/test/reference/over-below-source.pdf.argb32.ref.png differ
diff --git a/test/reference/over-below-source.ps2.argb32.ref.png b/test/reference/over-below-source.ps2.argb32.ref.png
new file mode 100644 (file)
index 0000000..c05bda5
Binary files /dev/null and b/test/reference/over-below-source.ps2.argb32.ref.png differ
diff --git a/test/reference/over-below-source.ps2.rgb24.ref.png b/test/reference/over-below-source.ps2.rgb24.ref.png
new file mode 100644 (file)
index 0000000..07e10d4
Binary files /dev/null and b/test/reference/over-below-source.ps2.rgb24.ref.png differ
diff --git a/test/reference/over-below-source.ps3.argb32.ref.png b/test/reference/over-below-source.ps3.argb32.ref.png
new file mode 100644 (file)
index 0000000..c05bda5
Binary files /dev/null and b/test/reference/over-below-source.ps3.argb32.ref.png differ
diff --git a/test/reference/over-below-source.ps3.rgb24.ref.png b/test/reference/over-below-source.ps3.rgb24.ref.png
new file mode 100644 (file)
index 0000000..07e10d4
Binary files /dev/null and b/test/reference/over-below-source.ps3.rgb24.ref.png differ
diff --git a/test/reference/over-below-source.ref.png b/test/reference/over-below-source.ref.png
new file mode 100644 (file)
index 0000000..eedd56b
Binary files /dev/null and b/test/reference/over-below-source.ref.png differ
diff --git a/test/reference/over-below-source.rgb24.ref.png b/test/reference/over-below-source.rgb24.ref.png
new file mode 100644 (file)
index 0000000..74176fa
Binary files /dev/null and b/test/reference/over-below-source.rgb24.ref.png differ
diff --git a/test/reference/over-below-source.svg12.argb32.xfail.png b/test/reference/over-below-source.svg12.argb32.xfail.png
new file mode 100644 (file)
index 0000000..c80705b
Binary files /dev/null and b/test/reference/over-below-source.svg12.argb32.xfail.png differ
diff --git a/test/reference/over-below-source.svg12.rgb24.xfail.png b/test/reference/over-below-source.svg12.rgb24.xfail.png
new file mode 100644 (file)
index 0000000..c80705b
Binary files /dev/null and b/test/reference/over-below-source.svg12.rgb24.xfail.png differ
diff --git a/test/reference/over-below-source.traps.argb32.ref.png b/test/reference/over-below-source.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..c659363
Binary files /dev/null and b/test/reference/over-below-source.traps.argb32.ref.png differ
diff --git a/test/reference/over-below-source.traps.rgb24.ref.png b/test/reference/over-below-source.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..88a85ac
Binary files /dev/null and b/test/reference/over-below-source.traps.rgb24.ref.png differ
diff --git a/test/reference/over-between-source.base.argb32.ref.png b/test/reference/over-between-source.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..a8fe66a
Binary files /dev/null and b/test/reference/over-between-source.base.argb32.ref.png differ
diff --git a/test/reference/over-between-source.base.rgb24.ref.png b/test/reference/over-between-source.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..bb77a9b
Binary files /dev/null and b/test/reference/over-between-source.base.rgb24.ref.png differ
diff --git a/test/reference/over-between-source.ps2.argb32.ref.png b/test/reference/over-between-source.ps2.argb32.ref.png
new file mode 100644 (file)
index 0000000..dd95940
Binary files /dev/null and b/test/reference/over-between-source.ps2.argb32.ref.png differ
diff --git a/test/reference/over-between-source.ps3.argb32.ref.png b/test/reference/over-between-source.ps3.argb32.ref.png
new file mode 100644 (file)
index 0000000..dd95940
Binary files /dev/null and b/test/reference/over-between-source.ps3.argb32.ref.png differ
diff --git a/test/reference/over-between-source.quartz.argb32.ref.png b/test/reference/over-between-source.quartz.argb32.ref.png
new file mode 100644 (file)
index 0000000..adb17ae
Binary files /dev/null and b/test/reference/over-between-source.quartz.argb32.ref.png differ
diff --git a/test/reference/over-between-source.ref.png b/test/reference/over-between-source.ref.png
new file mode 100644 (file)
index 0000000..7602945
Binary files /dev/null and b/test/reference/over-between-source.ref.png differ
diff --git a/test/reference/over-between-source.rgb24.ref.png b/test/reference/over-between-source.rgb24.ref.png
new file mode 100644 (file)
index 0000000..4cc7d8f
Binary files /dev/null and b/test/reference/over-between-source.rgb24.ref.png differ
diff --git a/test/reference/over-between-source.svg12.argb32.xfail.png b/test/reference/over-between-source.svg12.argb32.xfail.png
new file mode 100644 (file)
index 0000000..c80705b
Binary files /dev/null and b/test/reference/over-between-source.svg12.argb32.xfail.png differ
diff --git a/test/reference/over-between-source.svg12.rgb24.xfail.png b/test/reference/over-between-source.svg12.rgb24.xfail.png
new file mode 100644 (file)
index 0000000..c80705b
Binary files /dev/null and b/test/reference/over-between-source.svg12.rgb24.xfail.png differ
diff --git a/test/reference/over-between-source.traps.argb32.ref.png b/test/reference/over-between-source.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..a8fe66a
Binary files /dev/null and b/test/reference/over-between-source.traps.argb32.ref.png differ
diff --git a/test/reference/over-between-source.traps.rgb24.ref.png b/test/reference/over-between-source.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..bb77a9b
Binary files /dev/null and b/test/reference/over-between-source.traps.rgb24.ref.png differ
diff --git a/test/reference/overlapping-boxes.argb32.ref.png b/test/reference/overlapping-boxes.argb32.ref.png
new file mode 100644 (file)
index 0000000..1c68438
Binary files /dev/null and b/test/reference/overlapping-boxes.argb32.ref.png differ
diff --git a/test/reference/overlapping-boxes.base.argb32.ref.png b/test/reference/overlapping-boxes.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..278e62a
Binary files /dev/null and b/test/reference/overlapping-boxes.base.argb32.ref.png differ
diff --git a/test/reference/overlapping-boxes.base.rgb24.ref.png b/test/reference/overlapping-boxes.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..f35d0e6
Binary files /dev/null and b/test/reference/overlapping-boxes.base.rgb24.ref.png differ
diff --git a/test/reference/overlapping-boxes.rgb24.ref.png b/test/reference/overlapping-boxes.rgb24.ref.png
new file mode 100644 (file)
index 0000000..0edeafa
Binary files /dev/null and b/test/reference/overlapping-boxes.rgb24.ref.png differ
diff --git a/test/reference/overlapping-boxes.traps.argb32.ref.png b/test/reference/overlapping-boxes.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..278e62a
Binary files /dev/null and b/test/reference/overlapping-boxes.traps.argb32.ref.png differ
diff --git a/test/reference/overlapping-boxes.traps.rgb24.ref.png b/test/reference/overlapping-boxes.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..f35d0e6
Binary files /dev/null and b/test/reference/overlapping-boxes.traps.rgb24.ref.png differ
diff --git a/test/reference/overlapping-dash-caps.base.argb32.ref.png b/test/reference/overlapping-dash-caps.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..d3d6729
Binary files /dev/null and b/test/reference/overlapping-dash-caps.base.argb32.ref.png differ
diff --git a/test/reference/overlapping-dash-caps.base.rgb24.ref.png b/test/reference/overlapping-dash-caps.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..d3d6729
Binary files /dev/null and b/test/reference/overlapping-dash-caps.base.rgb24.ref.png differ
diff --git a/test/reference/overlapping-dash-caps.ref.png b/test/reference/overlapping-dash-caps.ref.png
new file mode 100644 (file)
index 0000000..726b293
Binary files /dev/null and b/test/reference/overlapping-dash-caps.ref.png differ
diff --git a/test/reference/overlapping-dash-caps.traps.argb32.ref.png b/test/reference/overlapping-dash-caps.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..d3d6729
Binary files /dev/null and b/test/reference/overlapping-dash-caps.traps.argb32.ref.png differ
diff --git a/test/reference/overlapping-dash-caps.traps.rgb24.ref.png b/test/reference/overlapping-dash-caps.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..d3d6729
Binary files /dev/null and b/test/reference/overlapping-dash-caps.traps.rgb24.ref.png differ
diff --git a/test/reference/overlapping-glyphs.argb32.ref.png b/test/reference/overlapping-glyphs.argb32.ref.png
new file mode 100644 (file)
index 0000000..8f6d383
Binary files /dev/null and b/test/reference/overlapping-glyphs.argb32.ref.png differ
diff --git a/test/reference/overlapping-glyphs.base.argb32.ref.png b/test/reference/overlapping-glyphs.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..0dc6fdb
Binary files /dev/null and b/test/reference/overlapping-glyphs.base.argb32.ref.png differ
diff --git a/test/reference/overlapping-glyphs.base.rgb24.ref.png b/test/reference/overlapping-glyphs.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..9702cc2
Binary files /dev/null and b/test/reference/overlapping-glyphs.base.rgb24.ref.png differ
diff --git a/test/reference/overlapping-glyphs.pdf.argb32.xfail.png b/test/reference/overlapping-glyphs.pdf.argb32.xfail.png
new file mode 100644 (file)
index 0000000..e3e4337
Binary files /dev/null and b/test/reference/overlapping-glyphs.pdf.argb32.xfail.png differ
diff --git a/test/reference/overlapping-glyphs.pdf.rgb24.xfail.png b/test/reference/overlapping-glyphs.pdf.rgb24.xfail.png
new file mode 100644 (file)
index 0000000..a3f1d70
Binary files /dev/null and b/test/reference/overlapping-glyphs.pdf.rgb24.xfail.png differ
diff --git a/test/reference/overlapping-glyphs.quartz.argb32.ref.png b/test/reference/overlapping-glyphs.quartz.argb32.ref.png
new file mode 100644 (file)
index 0000000..eaa0cb9
Binary files /dev/null and b/test/reference/overlapping-glyphs.quartz.argb32.ref.png differ
diff --git a/test/reference/overlapping-glyphs.quartz.rgb24.ref.png b/test/reference/overlapping-glyphs.quartz.rgb24.ref.png
new file mode 100644 (file)
index 0000000..c2b5fc0
Binary files /dev/null and b/test/reference/overlapping-glyphs.quartz.rgb24.ref.png differ
diff --git a/test/reference/overlapping-glyphs.rgb24.ref.png b/test/reference/overlapping-glyphs.rgb24.ref.png
new file mode 100644 (file)
index 0000000..eea57b2
Binary files /dev/null and b/test/reference/overlapping-glyphs.rgb24.ref.png differ
diff --git a/test/reference/overlapping-glyphs.svg.argb32.ref.png b/test/reference/overlapping-glyphs.svg.argb32.ref.png
new file mode 100644 (file)
index 0000000..ce38499
Binary files /dev/null and b/test/reference/overlapping-glyphs.svg.argb32.ref.png differ
diff --git a/test/reference/overlapping-glyphs.svg.rgb24.ref.png b/test/reference/overlapping-glyphs.svg.rgb24.ref.png
new file mode 100644 (file)
index 0000000..ce38499
Binary files /dev/null and b/test/reference/overlapping-glyphs.svg.rgb24.ref.png differ
diff --git a/test/reference/overlapping-glyphs.traps.argb32.ref.png b/test/reference/overlapping-glyphs.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..0dc6fdb
Binary files /dev/null and b/test/reference/overlapping-glyphs.traps.argb32.ref.png differ
diff --git a/test/reference/overlapping-glyphs.traps.rgb24.ref.png b/test/reference/overlapping-glyphs.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..9702cc2
Binary files /dev/null and b/test/reference/overlapping-glyphs.traps.rgb24.ref.png differ
diff --git a/test/reference/paint-clip-fill-aa.base.argb32.ref.png b/test/reference/paint-clip-fill-aa.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..768322a
Binary files /dev/null and b/test/reference/paint-clip-fill-aa.base.argb32.ref.png differ
diff --git a/test/reference/paint-clip-fill-aa.base.rgb24.ref.png b/test/reference/paint-clip-fill-aa.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..768322a
Binary files /dev/null and b/test/reference/paint-clip-fill-aa.base.rgb24.ref.png differ
diff --git a/test/reference/paint-clip-fill-aa.ref.png b/test/reference/paint-clip-fill-aa.ref.png
new file mode 100644 (file)
index 0000000..a8cf417
Binary files /dev/null and b/test/reference/paint-clip-fill-aa.ref.png differ
diff --git a/test/reference/paint-clip-fill-aa.traps.argb32.ref.png b/test/reference/paint-clip-fill-aa.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..768322a
Binary files /dev/null and b/test/reference/paint-clip-fill-aa.traps.argb32.ref.png differ
diff --git a/test/reference/paint-clip-fill-aa.traps.rgb24.ref.png b/test/reference/paint-clip-fill-aa.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..768322a
Binary files /dev/null and b/test/reference/paint-clip-fill-aa.traps.rgb24.ref.png differ
diff --git a/test/reference/paint-clip-fill-mono.base.argb32.ref.png b/test/reference/paint-clip-fill-mono.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..768322a
Binary files /dev/null and b/test/reference/paint-clip-fill-mono.base.argb32.ref.png differ
diff --git a/test/reference/paint-clip-fill-mono.base.rgb24.ref.png b/test/reference/paint-clip-fill-mono.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..768322a
Binary files /dev/null and b/test/reference/paint-clip-fill-mono.base.rgb24.ref.png differ
diff --git a/test/reference/paint-clip-fill-mono.ref.png b/test/reference/paint-clip-fill-mono.ref.png
new file mode 100644 (file)
index 0000000..a8cf417
Binary files /dev/null and b/test/reference/paint-clip-fill-mono.ref.png differ
diff --git a/test/reference/paint-clip-fill-mono.traps.argb32.ref.png b/test/reference/paint-clip-fill-mono.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..768322a
Binary files /dev/null and b/test/reference/paint-clip-fill-mono.traps.argb32.ref.png differ
diff --git a/test/reference/paint-clip-fill-mono.traps.rgb24.ref.png b/test/reference/paint-clip-fill-mono.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..768322a
Binary files /dev/null and b/test/reference/paint-clip-fill-mono.traps.rgb24.ref.png differ
diff --git a/test/reference/paint-repeat.base.argb32.ref.png b/test/reference/paint-repeat.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..8cdd9b4
Binary files /dev/null and b/test/reference/paint-repeat.base.argb32.ref.png differ
diff --git a/test/reference/paint-repeat.base.rgb24.ref.png b/test/reference/paint-repeat.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..8cdd9b4
Binary files /dev/null and b/test/reference/paint-repeat.base.rgb24.ref.png differ
diff --git a/test/reference/paint-repeat.ref.png b/test/reference/paint-repeat.ref.png
new file mode 100644 (file)
index 0000000..2cc48f3
Binary files /dev/null and b/test/reference/paint-repeat.ref.png differ
diff --git a/test/reference/paint-repeat.traps.argb32.ref.png b/test/reference/paint-repeat.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..8cdd9b4
Binary files /dev/null and b/test/reference/paint-repeat.traps.argb32.ref.png differ
diff --git a/test/reference/paint-repeat.traps.rgb24.ref.png b/test/reference/paint-repeat.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..8cdd9b4
Binary files /dev/null and b/test/reference/paint-repeat.traps.rgb24.ref.png differ
diff --git a/test/reference/paint-source-alpha.base.argb32.ref.png b/test/reference/paint-source-alpha.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..65d9c17
Binary files /dev/null and b/test/reference/paint-source-alpha.base.argb32.ref.png differ
diff --git a/test/reference/paint-source-alpha.base.rgb24.ref.png b/test/reference/paint-source-alpha.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..65d9c17
Binary files /dev/null and b/test/reference/paint-source-alpha.base.rgb24.ref.png differ
diff --git a/test/reference/paint-source-alpha.image16.ref.png b/test/reference/paint-source-alpha.image16.ref.png
new file mode 100644 (file)
index 0000000..12bd89d
Binary files /dev/null and b/test/reference/paint-source-alpha.image16.ref.png differ
diff --git a/test/reference/paint-source-alpha.ref.png b/test/reference/paint-source-alpha.ref.png
new file mode 100644 (file)
index 0000000..548bcd7
Binary files /dev/null and b/test/reference/paint-source-alpha.ref.png differ
diff --git a/test/reference/paint-source-alpha.svg.ref.png b/test/reference/paint-source-alpha.svg.ref.png
new file mode 100644 (file)
index 0000000..763bb59
Binary files /dev/null and b/test/reference/paint-source-alpha.svg.ref.png differ
diff --git a/test/reference/paint-source-alpha.traps.argb32.ref.png b/test/reference/paint-source-alpha.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..65d9c17
Binary files /dev/null and b/test/reference/paint-source-alpha.traps.argb32.ref.png differ
diff --git a/test/reference/paint-source-alpha.traps.rgb24.ref.png b/test/reference/paint-source-alpha.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..65d9c17
Binary files /dev/null and b/test/reference/paint-source-alpha.traps.rgb24.ref.png differ
diff --git a/test/reference/paint-with-alpha-clip-mask.base.argb32.ref.png b/test/reference/paint-with-alpha-clip-mask.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..201bd0d
Binary files /dev/null and b/test/reference/paint-with-alpha-clip-mask.base.argb32.ref.png differ
diff --git a/test/reference/paint-with-alpha-clip-mask.base.rgb24.ref.png b/test/reference/paint-with-alpha-clip-mask.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..201bd0d
Binary files /dev/null and b/test/reference/paint-with-alpha-clip-mask.base.rgb24.ref.png differ
diff --git a/test/reference/paint-with-alpha-clip-mask.ref.png b/test/reference/paint-with-alpha-clip-mask.ref.png
new file mode 100644 (file)
index 0000000..12e09d3
Binary files /dev/null and b/test/reference/paint-with-alpha-clip-mask.ref.png differ
diff --git a/test/reference/paint-with-alpha-clip-mask.traps.argb32.ref.png b/test/reference/paint-with-alpha-clip-mask.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..201bd0d
Binary files /dev/null and b/test/reference/paint-with-alpha-clip-mask.traps.argb32.ref.png differ
diff --git a/test/reference/paint-with-alpha-clip-mask.traps.rgb24.ref.png b/test/reference/paint-with-alpha-clip-mask.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..201bd0d
Binary files /dev/null and b/test/reference/paint-with-alpha-clip-mask.traps.rgb24.ref.png differ
diff --git a/test/reference/paint-with-alpha-clip.base.argb32.ref.png b/test/reference/paint-with-alpha-clip.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..704c70a
Binary files /dev/null and b/test/reference/paint-with-alpha-clip.base.argb32.ref.png differ
diff --git a/test/reference/paint-with-alpha-clip.base.rgb24.ref.png b/test/reference/paint-with-alpha-clip.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..704c70a
Binary files /dev/null and b/test/reference/paint-with-alpha-clip.base.rgb24.ref.png differ
diff --git a/test/reference/paint-with-alpha-clip.ref.png b/test/reference/paint-with-alpha-clip.ref.png
new file mode 100644 (file)
index 0000000..4bad4e8
Binary files /dev/null and b/test/reference/paint-with-alpha-clip.ref.png differ
diff --git a/test/reference/paint-with-alpha-clip.traps.argb32.ref.png b/test/reference/paint-with-alpha-clip.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..c1da67e
Binary files /dev/null and b/test/reference/paint-with-alpha-clip.traps.argb32.ref.png differ
diff --git a/test/reference/paint-with-alpha-clip.traps.rgb24.ref.png b/test/reference/paint-with-alpha-clip.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..c1da67e
Binary files /dev/null and b/test/reference/paint-with-alpha-clip.traps.rgb24.ref.png differ
diff --git a/test/reference/paint-with-alpha-solid-clip.base.argb32.ref.png b/test/reference/paint-with-alpha-solid-clip.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..2cd2df2
Binary files /dev/null and b/test/reference/paint-with-alpha-solid-clip.base.argb32.ref.png differ
diff --git a/test/reference/paint-with-alpha-solid-clip.base.rgb24.ref.png b/test/reference/paint-with-alpha-solid-clip.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..2cd2df2
Binary files /dev/null and b/test/reference/paint-with-alpha-solid-clip.base.rgb24.ref.png differ
diff --git a/test/reference/paint-with-alpha-solid-clip.ref.png b/test/reference/paint-with-alpha-solid-clip.ref.png
new file mode 100644 (file)
index 0000000..59d226d
Binary files /dev/null and b/test/reference/paint-with-alpha-solid-clip.ref.png differ
diff --git a/test/reference/paint-with-alpha-solid-clip.traps.argb32.ref.png b/test/reference/paint-with-alpha-solid-clip.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..2cd2df2
Binary files /dev/null and b/test/reference/paint-with-alpha-solid-clip.traps.argb32.ref.png differ
diff --git a/test/reference/paint-with-alpha-solid-clip.traps.rgb24.ref.png b/test/reference/paint-with-alpha-solid-clip.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..2cd2df2
Binary files /dev/null and b/test/reference/paint-with-alpha-solid-clip.traps.rgb24.ref.png differ
diff --git a/test/reference/paint-with-alpha.base.argb32.ref.png b/test/reference/paint-with-alpha.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..65d9c17
Binary files /dev/null and b/test/reference/paint-with-alpha.base.argb32.ref.png differ
diff --git a/test/reference/paint-with-alpha.base.rgb24.ref.png b/test/reference/paint-with-alpha.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..65d9c17
Binary files /dev/null and b/test/reference/paint-with-alpha.base.rgb24.ref.png differ
diff --git a/test/reference/paint-with-alpha.image16.ref.png b/test/reference/paint-with-alpha.image16.ref.png
new file mode 100644 (file)
index 0000000..12bd89d
Binary files /dev/null and b/test/reference/paint-with-alpha.image16.ref.png differ
diff --git a/test/reference/paint-with-alpha.ref.png b/test/reference/paint-with-alpha.ref.png
new file mode 100644 (file)
index 0000000..ab7ce3e
Binary files /dev/null and b/test/reference/paint-with-alpha.ref.png differ
diff --git a/test/reference/paint-with-alpha.svg.ref.png b/test/reference/paint-with-alpha.svg.ref.png
new file mode 100644 (file)
index 0000000..c0df8eb
Binary files /dev/null and b/test/reference/paint-with-alpha.svg.ref.png differ
diff --git a/test/reference/paint-with-alpha.traps.argb32.ref.png b/test/reference/paint-with-alpha.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..65d9c17
Binary files /dev/null and b/test/reference/paint-with-alpha.traps.argb32.ref.png differ
diff --git a/test/reference/paint-with-alpha.traps.rgb24.ref.png b/test/reference/paint-with-alpha.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..65d9c17
Binary files /dev/null and b/test/reference/paint-with-alpha.traps.rgb24.ref.png differ
diff --git a/test/reference/paint.base.argb32.ref.png b/test/reference/paint.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..22cc7a1
Binary files /dev/null and b/test/reference/paint.base.argb32.ref.png differ
diff --git a/test/reference/paint.base.rgb24.ref.png b/test/reference/paint.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..22cc7a1
Binary files /dev/null and b/test/reference/paint.base.rgb24.ref.png differ
diff --git a/test/reference/paint.ref.png b/test/reference/paint.ref.png
new file mode 100644 (file)
index 0000000..fff03b3
Binary files /dev/null and b/test/reference/paint.ref.png differ
diff --git a/test/reference/paint.traps.argb32.ref.png b/test/reference/paint.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..22cc7a1
Binary files /dev/null and b/test/reference/paint.traps.argb32.ref.png differ
diff --git a/test/reference/paint.traps.rgb24.ref.png b/test/reference/paint.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..22cc7a1
Binary files /dev/null and b/test/reference/paint.traps.rgb24.ref.png differ
diff --git a/test/reference/partial-clip-text-bottom.base.argb32.ref.png b/test/reference/partial-clip-text-bottom.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..7a03fad
Binary files /dev/null and b/test/reference/partial-clip-text-bottom.base.argb32.ref.png differ
diff --git a/test/reference/partial-clip-text-bottom.base.rgb24.ref.png b/test/reference/partial-clip-text-bottom.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..7a03fad
Binary files /dev/null and b/test/reference/partial-clip-text-bottom.base.rgb24.ref.png differ
diff --git a/test/reference/partial-clip-text-bottom.ref.png b/test/reference/partial-clip-text-bottom.ref.png
new file mode 100644 (file)
index 0000000..7a03fad
Binary files /dev/null and b/test/reference/partial-clip-text-bottom.ref.png differ
diff --git a/test/reference/partial-clip-text-bottom.traps.argb32.ref.png b/test/reference/partial-clip-text-bottom.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..7a03fad
Binary files /dev/null and b/test/reference/partial-clip-text-bottom.traps.argb32.ref.png differ
diff --git a/test/reference/partial-clip-text-bottom.traps.rgb24.ref.png b/test/reference/partial-clip-text-bottom.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..7a03fad
Binary files /dev/null and b/test/reference/partial-clip-text-bottom.traps.rgb24.ref.png differ
diff --git a/test/reference/partial-clip-text-left.base.argb32.ref.png b/test/reference/partial-clip-text-left.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..95c07f4
Binary files /dev/null and b/test/reference/partial-clip-text-left.base.argb32.ref.png differ
diff --git a/test/reference/partial-clip-text-left.base.rgb24.ref.png b/test/reference/partial-clip-text-left.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..95c07f4
Binary files /dev/null and b/test/reference/partial-clip-text-left.base.rgb24.ref.png differ
diff --git a/test/reference/partial-clip-text-left.ref.png b/test/reference/partial-clip-text-left.ref.png
new file mode 100644 (file)
index 0000000..95c07f4
Binary files /dev/null and b/test/reference/partial-clip-text-left.ref.png differ
diff --git a/test/reference/partial-clip-text-left.traps.argb32.ref.png b/test/reference/partial-clip-text-left.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..95c07f4
Binary files /dev/null and b/test/reference/partial-clip-text-left.traps.argb32.ref.png differ
diff --git a/test/reference/partial-clip-text-left.traps.rgb24.ref.png b/test/reference/partial-clip-text-left.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..95c07f4
Binary files /dev/null and b/test/reference/partial-clip-text-left.traps.rgb24.ref.png differ
diff --git a/test/reference/partial-clip-text-right.base.argb32.ref.png b/test/reference/partial-clip-text-right.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..3e69ca9
Binary files /dev/null and b/test/reference/partial-clip-text-right.base.argb32.ref.png differ
diff --git a/test/reference/partial-clip-text-right.base.rgb24.ref.png b/test/reference/partial-clip-text-right.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..3e69ca9
Binary files /dev/null and b/test/reference/partial-clip-text-right.base.rgb24.ref.png differ
diff --git a/test/reference/partial-clip-text-right.ref.png b/test/reference/partial-clip-text-right.ref.png
new file mode 100644 (file)
index 0000000..3e69ca9
Binary files /dev/null and b/test/reference/partial-clip-text-right.ref.png differ
diff --git a/test/reference/partial-clip-text-right.traps.argb32.ref.png b/test/reference/partial-clip-text-right.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..3e69ca9
Binary files /dev/null and b/test/reference/partial-clip-text-right.traps.argb32.ref.png differ
diff --git a/test/reference/partial-clip-text-right.traps.rgb24.ref.png b/test/reference/partial-clip-text-right.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..3e69ca9
Binary files /dev/null and b/test/reference/partial-clip-text-right.traps.rgb24.ref.png differ
diff --git a/test/reference/partial-clip-text-top.base.argb32.ref.png b/test/reference/partial-clip-text-top.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..afe2d3e
Binary files /dev/null and b/test/reference/partial-clip-text-top.base.argb32.ref.png differ
diff --git a/test/reference/partial-clip-text-top.base.rgb24.ref.png b/test/reference/partial-clip-text-top.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..afe2d3e
Binary files /dev/null and b/test/reference/partial-clip-text-top.base.rgb24.ref.png differ
diff --git a/test/reference/partial-clip-text-top.ps.ref.png b/test/reference/partial-clip-text-top.ps.ref.png
new file mode 100644 (file)
index 0000000..049bba5
Binary files /dev/null and b/test/reference/partial-clip-text-top.ps.ref.png differ
diff --git a/test/reference/partial-clip-text-top.quartz.ref.png b/test/reference/partial-clip-text-top.quartz.ref.png
new file mode 100644 (file)
index 0000000..33ac283
Binary files /dev/null and b/test/reference/partial-clip-text-top.quartz.ref.png differ
diff --git a/test/reference/partial-clip-text-top.ref.png b/test/reference/partial-clip-text-top.ref.png
new file mode 100644 (file)
index 0000000..afe2d3e
Binary files /dev/null and b/test/reference/partial-clip-text-top.ref.png differ
diff --git a/test/reference/partial-clip-text-top.svg.ref.png b/test/reference/partial-clip-text-top.svg.ref.png
new file mode 100644 (file)
index 0000000..dc3fc58
Binary files /dev/null and b/test/reference/partial-clip-text-top.svg.ref.png differ
diff --git a/test/reference/partial-clip-text-top.traps.argb32.ref.png b/test/reference/partial-clip-text-top.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..afe2d3e
Binary files /dev/null and b/test/reference/partial-clip-text-top.traps.argb32.ref.png differ
diff --git a/test/reference/partial-clip-text-top.traps.rgb24.ref.png b/test/reference/partial-clip-text-top.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..afe2d3e
Binary files /dev/null and b/test/reference/partial-clip-text-top.traps.rgb24.ref.png differ
diff --git a/test/reference/partial-coverage-half-reference.base.argb32.ref.png b/test/reference/partial-coverage-half-reference.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..17f4ff0
Binary files /dev/null and b/test/reference/partial-coverage-half-reference.base.argb32.ref.png differ
diff --git a/test/reference/partial-coverage-half-reference.base.rgb24.ref.png b/test/reference/partial-coverage-half-reference.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..17f4ff0
Binary files /dev/null and b/test/reference/partial-coverage-half-reference.base.rgb24.ref.png differ
diff --git a/test/reference/partial-coverage-half-reference.ref.png b/test/reference/partial-coverage-half-reference.ref.png
new file mode 100644 (file)
index 0000000..17f4ff0
Binary files /dev/null and b/test/reference/partial-coverage-half-reference.ref.png differ
diff --git a/test/reference/partial-coverage-half-reference.traps.argb32.ref.png b/test/reference/partial-coverage-half-reference.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..17f4ff0
Binary files /dev/null and b/test/reference/partial-coverage-half-reference.traps.argb32.ref.png differ
diff --git a/test/reference/partial-coverage-half-reference.traps.rgb24.ref.png b/test/reference/partial-coverage-half-reference.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..17f4ff0
Binary files /dev/null and b/test/reference/partial-coverage-half-reference.traps.rgb24.ref.png differ
diff --git a/test/reference/partial-coverage-half-triangles.ref.png b/test/reference/partial-coverage-half-triangles.ref.png
new file mode 100644 (file)
index 0000000..17f4ff0
Binary files /dev/null and b/test/reference/partial-coverage-half-triangles.ref.png differ
diff --git a/test/reference/partial-coverage-intersecting-quads.ref.png b/test/reference/partial-coverage-intersecting-quads.ref.png
new file mode 100644 (file)
index 0000000..17f4ff0
Binary files /dev/null and b/test/reference/partial-coverage-intersecting-quads.ref.png differ
diff --git a/test/reference/partial-coverage-intersecting-quads.xfail.png b/test/reference/partial-coverage-intersecting-quads.xfail.png
new file mode 100644 (file)
index 0000000..a6635b9
Binary files /dev/null and b/test/reference/partial-coverage-intersecting-quads.xfail.png differ
diff --git a/test/reference/partial-coverage-intersecting-triangles.ref.png b/test/reference/partial-coverage-intersecting-triangles.ref.png
new file mode 100644 (file)
index 0000000..9e4a6fe
Binary files /dev/null and b/test/reference/partial-coverage-intersecting-triangles.ref.png differ
diff --git a/test/reference/partial-coverage-overlap-half-triangles-eo.ref.png b/test/reference/partial-coverage-overlap-half-triangles-eo.ref.png
new file mode 100644 (file)
index 0000000..17f4ff0
Binary files /dev/null and b/test/reference/partial-coverage-overlap-half-triangles-eo.ref.png differ
diff --git a/test/reference/partial-coverage-overlap-half-triangles.ref.png b/test/reference/partial-coverage-overlap-half-triangles.ref.png
new file mode 100644 (file)
index 0000000..17f4ff0
Binary files /dev/null and b/test/reference/partial-coverage-overlap-half-triangles.ref.png differ
diff --git a/test/reference/partial-coverage-overlap-three-quarter-triangles.ref.png b/test/reference/partial-coverage-overlap-three-quarter-triangles.ref.png
new file mode 100644 (file)
index 0000000..ea16dc4
Binary files /dev/null and b/test/reference/partial-coverage-overlap-three-quarter-triangles.ref.png differ
diff --git a/test/reference/partial-coverage-rectangles.ref.png b/test/reference/partial-coverage-rectangles.ref.png
new file mode 100644 (file)
index 0000000..9e4a6fe
Binary files /dev/null and b/test/reference/partial-coverage-rectangles.ref.png differ
diff --git a/test/reference/partial-coverage-reference.base.argb32.ref.png b/test/reference/partial-coverage-reference.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..9e4a6fe
Binary files /dev/null and b/test/reference/partial-coverage-reference.base.argb32.ref.png differ
diff --git a/test/reference/partial-coverage-reference.base.rgb24.ref.png b/test/reference/partial-coverage-reference.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..9e4a6fe
Binary files /dev/null and b/test/reference/partial-coverage-reference.base.rgb24.ref.png differ
diff --git a/test/reference/partial-coverage-reference.ref.png b/test/reference/partial-coverage-reference.ref.png
new file mode 100644 (file)
index 0000000..9e4a6fe
Binary files /dev/null and b/test/reference/partial-coverage-reference.ref.png differ
diff --git a/test/reference/partial-coverage-reference.traps.argb32.ref.png b/test/reference/partial-coverage-reference.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..9e4a6fe
Binary files /dev/null and b/test/reference/partial-coverage-reference.traps.argb32.ref.png differ
diff --git a/test/reference/partial-coverage-reference.traps.rgb24.ref.png b/test/reference/partial-coverage-reference.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..9e4a6fe
Binary files /dev/null and b/test/reference/partial-coverage-reference.traps.rgb24.ref.png differ
diff --git a/test/reference/partial-coverage-three-quarter-reference.base.argb32.ref.png b/test/reference/partial-coverage-three-quarter-reference.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..ea16dc4
Binary files /dev/null and b/test/reference/partial-coverage-three-quarter-reference.base.argb32.ref.png differ
diff --git a/test/reference/partial-coverage-three-quarter-reference.base.rgb24.ref.png b/test/reference/partial-coverage-three-quarter-reference.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..ea16dc4
Binary files /dev/null and b/test/reference/partial-coverage-three-quarter-reference.base.rgb24.ref.png differ
diff --git a/test/reference/partial-coverage-three-quarter-reference.ref.png b/test/reference/partial-coverage-three-quarter-reference.ref.png
new file mode 100644 (file)
index 0000000..ea16dc4
Binary files /dev/null and b/test/reference/partial-coverage-three-quarter-reference.ref.png differ
diff --git a/test/reference/partial-coverage-three-quarter-reference.traps.argb32.ref.png b/test/reference/partial-coverage-three-quarter-reference.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..ea16dc4
Binary files /dev/null and b/test/reference/partial-coverage-three-quarter-reference.traps.argb32.ref.png differ
diff --git a/test/reference/partial-coverage-three-quarter-reference.traps.rgb24.ref.png b/test/reference/partial-coverage-three-quarter-reference.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..ea16dc4
Binary files /dev/null and b/test/reference/partial-coverage-three-quarter-reference.traps.rgb24.ref.png differ
diff --git a/test/reference/partial-coverage-triangles.ref.png b/test/reference/partial-coverage-triangles.ref.png
new file mode 100644 (file)
index 0000000..9e4a6fe
Binary files /dev/null and b/test/reference/partial-coverage-triangles.ref.png differ
diff --git a/test/reference/pass-through.ref.png b/test/reference/pass-through.ref.png
new file mode 100644 (file)
index 0000000..058a192
Binary files /dev/null and b/test/reference/pass-through.ref.png differ
diff --git a/test/reference/pass-through.rgb24.ref.png b/test/reference/pass-through.rgb24.ref.png
new file mode 100644 (file)
index 0000000..b074ede
Binary files /dev/null and b/test/reference/pass-through.rgb24.ref.png differ
diff --git a/test/reference/path-append.base.argb32.ref.png b/test/reference/path-append.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..6fb6b2a
Binary files /dev/null and b/test/reference/path-append.base.argb32.ref.png differ
diff --git a/test/reference/path-append.base.rgb24.ref.png b/test/reference/path-append.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..6fb6b2a
Binary files /dev/null and b/test/reference/path-append.base.rgb24.ref.png differ
diff --git a/test/reference/path-append.image16.ref.png b/test/reference/path-append.image16.ref.png
new file mode 100644 (file)
index 0000000..5d939bb
Binary files /dev/null and b/test/reference/path-append.image16.ref.png differ
diff --git a/test/reference/path-append.ps.ref.png b/test/reference/path-append.ps.ref.png
new file mode 100644 (file)
index 0000000..2c8df16
Binary files /dev/null and b/test/reference/path-append.ps.ref.png differ
diff --git a/test/reference/path-append.quartz.ref.png b/test/reference/path-append.quartz.ref.png
new file mode 100644 (file)
index 0000000..665d3cc
Binary files /dev/null and b/test/reference/path-append.quartz.ref.png differ
diff --git a/test/reference/path-append.ref.png b/test/reference/path-append.ref.png
new file mode 100644 (file)
index 0000000..4733f06
Binary files /dev/null and b/test/reference/path-append.ref.png differ
diff --git a/test/reference/path-append.test-fallback.ref.png b/test/reference/path-append.test-fallback.ref.png
new file mode 100644 (file)
index 0000000..fa72ac0
Binary files /dev/null and b/test/reference/path-append.test-fallback.ref.png differ
diff --git a/test/reference/path-append.traps.argb32.ref.png b/test/reference/path-append.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..6fb6b2a
Binary files /dev/null and b/test/reference/path-append.traps.argb32.ref.png differ
diff --git a/test/reference/path-append.traps.rgb24.ref.png b/test/reference/path-append.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..6fb6b2a
Binary files /dev/null and b/test/reference/path-append.traps.rgb24.ref.png differ
diff --git a/test/reference/path-append.xlib-fallback.ref.png b/test/reference/path-append.xlib-fallback.ref.png
new file mode 100644 (file)
index 0000000..d34cce1
Binary files /dev/null and b/test/reference/path-append.xlib-fallback.ref.png differ
diff --git a/test/reference/path-stroke-twice.base.argb32.ref.png b/test/reference/path-stroke-twice.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..48dd2c7
Binary files /dev/null and b/test/reference/path-stroke-twice.base.argb32.ref.png differ
diff --git a/test/reference/path-stroke-twice.base.rgb24.ref.png b/test/reference/path-stroke-twice.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..48dd2c7
Binary files /dev/null and b/test/reference/path-stroke-twice.base.rgb24.ref.png differ
diff --git a/test/reference/path-stroke-twice.image16.ref.png b/test/reference/path-stroke-twice.image16.ref.png
new file mode 100644 (file)
index 0000000..9f162ad
Binary files /dev/null and b/test/reference/path-stroke-twice.image16.ref.png differ
diff --git a/test/reference/path-stroke-twice.ps.ref.png b/test/reference/path-stroke-twice.ps.ref.png
new file mode 100644 (file)
index 0000000..23e8147
Binary files /dev/null and b/test/reference/path-stroke-twice.ps.ref.png differ
diff --git a/test/reference/path-stroke-twice.ref.png b/test/reference/path-stroke-twice.ref.png
new file mode 100644 (file)
index 0000000..4c8b4f5
Binary files /dev/null and b/test/reference/path-stroke-twice.ref.png differ
diff --git a/test/reference/path-stroke-twice.traps.argb32.ref.png b/test/reference/path-stroke-twice.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..48dd2c7
Binary files /dev/null and b/test/reference/path-stroke-twice.traps.argb32.ref.png differ
diff --git a/test/reference/path-stroke-twice.traps.rgb24.ref.png b/test/reference/path-stroke-twice.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..48dd2c7
Binary files /dev/null and b/test/reference/path-stroke-twice.traps.rgb24.ref.png differ
diff --git a/test/reference/pattern-getters.base.argb32.ref.png b/test/reference/pattern-getters.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..3cc39a8
Binary files /dev/null and b/test/reference/pattern-getters.base.argb32.ref.png differ
diff --git a/test/reference/pattern-getters.base.rgb24.ref.png b/test/reference/pattern-getters.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..3cc39a8
Binary files /dev/null and b/test/reference/pattern-getters.base.rgb24.ref.png differ
diff --git a/test/reference/pattern-getters.ref.png b/test/reference/pattern-getters.ref.png
new file mode 100644 (file)
index 0000000..80304b0
Binary files /dev/null and b/test/reference/pattern-getters.ref.png differ
diff --git a/test/reference/pattern-getters.traps.argb32.ref.png b/test/reference/pattern-getters.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..3cc39a8
Binary files /dev/null and b/test/reference/pattern-getters.traps.argb32.ref.png differ
diff --git a/test/reference/pattern-getters.traps.rgb24.ref.png b/test/reference/pattern-getters.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..3cc39a8
Binary files /dev/null and b/test/reference/pattern-getters.traps.rgb24.ref.png differ
diff --git a/test/reference/pdf-surface-source.argb32.ref.png b/test/reference/pdf-surface-source.argb32.ref.png
new file mode 100644 (file)
index 0000000..0182972
Binary files /dev/null and b/test/reference/pdf-surface-source.argb32.ref.png differ
diff --git a/test/reference/pdf-surface-source.base.argb32.ref.png b/test/reference/pdf-surface-source.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..0182972
Binary files /dev/null and b/test/reference/pdf-surface-source.base.argb32.ref.png differ
diff --git a/test/reference/pdf-surface-source.base.rgb24.ref.png b/test/reference/pdf-surface-source.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..0d68a82
Binary files /dev/null and b/test/reference/pdf-surface-source.base.rgb24.ref.png differ
diff --git a/test/reference/pdf-surface-source.image16.ref.png b/test/reference/pdf-surface-source.image16.ref.png
new file mode 100644 (file)
index 0000000..2a7460e
Binary files /dev/null and b/test/reference/pdf-surface-source.image16.ref.png differ
diff --git a/test/reference/pdf-surface-source.rgb24.ref.png b/test/reference/pdf-surface-source.rgb24.ref.png
new file mode 100644 (file)
index 0000000..0d68a82
Binary files /dev/null and b/test/reference/pdf-surface-source.rgb24.ref.png differ
diff --git a/test/reference/pdf-surface-source.svg12.argb32.xfail.png b/test/reference/pdf-surface-source.svg12.argb32.xfail.png
new file mode 100644 (file)
index 0000000..6ebcaf9
Binary files /dev/null and b/test/reference/pdf-surface-source.svg12.argb32.xfail.png differ
diff --git a/test/reference/pdf-surface-source.svg12.rgb24.xfail.png b/test/reference/pdf-surface-source.svg12.rgb24.xfail.png
new file mode 100644 (file)
index 0000000..6ebcaf9
Binary files /dev/null and b/test/reference/pdf-surface-source.svg12.rgb24.xfail.png differ
diff --git a/test/reference/pdf-surface-source.traps.argb32.ref.png b/test/reference/pdf-surface-source.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..0182972
Binary files /dev/null and b/test/reference/pdf-surface-source.traps.argb32.ref.png differ
diff --git a/test/reference/pdf-surface-source.traps.rgb24.ref.png b/test/reference/pdf-surface-source.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..0d68a82
Binary files /dev/null and b/test/reference/pdf-surface-source.traps.rgb24.ref.png differ
diff --git a/test/reference/pixman-rotate.base.argb32.ref.png b/test/reference/pixman-rotate.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..7e47a4d
Binary files /dev/null and b/test/reference/pixman-rotate.base.argb32.ref.png differ
diff --git a/test/reference/pixman-rotate.base.rgb24.ref.png b/test/reference/pixman-rotate.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..397acbe
Binary files /dev/null and b/test/reference/pixman-rotate.base.rgb24.ref.png differ
diff --git a/test/reference/pixman-rotate.ps.argb32.ref.png b/test/reference/pixman-rotate.ps.argb32.ref.png
new file mode 100644 (file)
index 0000000..0e91688
Binary files /dev/null and b/test/reference/pixman-rotate.ps.argb32.ref.png differ
diff --git a/test/reference/pixman-rotate.ref.png b/test/reference/pixman-rotate.ref.png
new file mode 100644 (file)
index 0000000..7e47a4d
Binary files /dev/null and b/test/reference/pixman-rotate.ref.png differ
diff --git a/test/reference/pixman-rotate.rgb24.ref.png b/test/reference/pixman-rotate.rgb24.ref.png
new file mode 100644 (file)
index 0000000..60d9b1c
Binary files /dev/null and b/test/reference/pixman-rotate.rgb24.ref.png differ
diff --git a/test/reference/pixman-rotate.traps.argb32.ref.png b/test/reference/pixman-rotate.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..7e47a4d
Binary files /dev/null and b/test/reference/pixman-rotate.traps.argb32.ref.png differ
diff --git a/test/reference/pixman-rotate.traps.rgb24.ref.png b/test/reference/pixman-rotate.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..397acbe
Binary files /dev/null and b/test/reference/pixman-rotate.traps.rgb24.ref.png differ
diff --git a/test/reference/ps-eps.ref.png b/test/reference/ps-eps.ref.png
new file mode 100644 (file)
index 0000000..9aadb08
Binary files /dev/null and b/test/reference/ps-eps.ref.png differ
diff --git a/test/reference/ps-surface-source.argb32.ref.png b/test/reference/ps-surface-source.argb32.ref.png
new file mode 100644 (file)
index 0000000..0182972
Binary files /dev/null and b/test/reference/ps-surface-source.argb32.ref.png differ
diff --git a/test/reference/ps-surface-source.base.argb32.ref.png b/test/reference/ps-surface-source.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..0182972
Binary files /dev/null and b/test/reference/ps-surface-source.base.argb32.ref.png differ
diff --git a/test/reference/ps-surface-source.base.rgb24.ref.png b/test/reference/ps-surface-source.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..0d68a82
Binary files /dev/null and b/test/reference/ps-surface-source.base.rgb24.ref.png differ
diff --git a/test/reference/ps-surface-source.image16.ref.png b/test/reference/ps-surface-source.image16.ref.png
new file mode 100644 (file)
index 0000000..2a7460e
Binary files /dev/null and b/test/reference/ps-surface-source.image16.ref.png differ
diff --git a/test/reference/ps-surface-source.rgb24.ref.png b/test/reference/ps-surface-source.rgb24.ref.png
new file mode 100644 (file)
index 0000000..0d68a82
Binary files /dev/null and b/test/reference/ps-surface-source.rgb24.ref.png differ
diff --git a/test/reference/ps-surface-source.svg12.argb32.xfail.png b/test/reference/ps-surface-source.svg12.argb32.xfail.png
new file mode 100644 (file)
index 0000000..6ebcaf9
Binary files /dev/null and b/test/reference/ps-surface-source.svg12.argb32.xfail.png differ
diff --git a/test/reference/ps-surface-source.svg12.rgb24.xfail.png b/test/reference/ps-surface-source.svg12.rgb24.xfail.png
new file mode 100644 (file)
index 0000000..6ebcaf9
Binary files /dev/null and b/test/reference/ps-surface-source.svg12.rgb24.xfail.png differ
diff --git a/test/reference/ps-surface-source.traps.argb32.ref.png b/test/reference/ps-surface-source.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..0182972
Binary files /dev/null and b/test/reference/ps-surface-source.traps.argb32.ref.png differ
diff --git a/test/reference/ps-surface-source.traps.rgb24.ref.png b/test/reference/ps-surface-source.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..0d68a82
Binary files /dev/null and b/test/reference/ps-surface-source.traps.rgb24.ref.png differ
diff --git a/test/reference/pthread-same-source.base.argb32.ref.png b/test/reference/pthread-same-source.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..cfb519d
Binary files /dev/null and b/test/reference/pthread-same-source.base.argb32.ref.png differ
diff --git a/test/reference/pthread-same-source.base.rgb24.ref.png b/test/reference/pthread-same-source.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..cfb519d
Binary files /dev/null and b/test/reference/pthread-same-source.base.rgb24.ref.png differ
diff --git a/test/reference/pthread-same-source.image16.ref.png b/test/reference/pthread-same-source.image16.ref.png
new file mode 100644 (file)
index 0000000..196c4ac
Binary files /dev/null and b/test/reference/pthread-same-source.image16.ref.png differ
diff --git a/test/reference/pthread-same-source.quartz.xfail.png b/test/reference/pthread-same-source.quartz.xfail.png
new file mode 100644 (file)
index 0000000..ffed619
Binary files /dev/null and b/test/reference/pthread-same-source.quartz.xfail.png differ
diff --git a/test/reference/pthread-same-source.ref.png b/test/reference/pthread-same-source.ref.png
new file mode 100644 (file)
index 0000000..cfb519d
Binary files /dev/null and b/test/reference/pthread-same-source.ref.png differ
diff --git a/test/reference/pthread-same-source.traps.argb32.ref.png b/test/reference/pthread-same-source.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..cfb519d
Binary files /dev/null and b/test/reference/pthread-same-source.traps.argb32.ref.png differ
diff --git a/test/reference/pthread-same-source.traps.rgb24.ref.png b/test/reference/pthread-same-source.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..cfb519d
Binary files /dev/null and b/test/reference/pthread-same-source.traps.rgb24.ref.png differ
diff --git a/test/reference/pthread-show-text.base.argb32.ref.png b/test/reference/pthread-show-text.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..efa5fe9
Binary files /dev/null and b/test/reference/pthread-show-text.base.argb32.ref.png differ
diff --git a/test/reference/pthread-show-text.base.rgb24.ref.png b/test/reference/pthread-show-text.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..efa5fe9
Binary files /dev/null and b/test/reference/pthread-show-text.base.rgb24.ref.png differ
diff --git a/test/reference/pthread-show-text.image16.ref.png b/test/reference/pthread-show-text.image16.ref.png
new file mode 100644 (file)
index 0000000..a1d1af5
Binary files /dev/null and b/test/reference/pthread-show-text.image16.ref.png differ
diff --git a/test/reference/pthread-show-text.pdf.ref.png b/test/reference/pthread-show-text.pdf.ref.png
new file mode 100644 (file)
index 0000000..bb72fc2
Binary files /dev/null and b/test/reference/pthread-show-text.pdf.ref.png differ
diff --git a/test/reference/pthread-show-text.ps.ref.png b/test/reference/pthread-show-text.ps.ref.png
new file mode 100644 (file)
index 0000000..807b73f
Binary files /dev/null and b/test/reference/pthread-show-text.ps.ref.png differ
diff --git a/test/reference/pthread-show-text.quartz.ref.png b/test/reference/pthread-show-text.quartz.ref.png
new file mode 100644 (file)
index 0000000..cc9bb25
Binary files /dev/null and b/test/reference/pthread-show-text.quartz.ref.png differ
diff --git a/test/reference/pthread-show-text.ref.png b/test/reference/pthread-show-text.ref.png
new file mode 100644 (file)
index 0000000..efa5fe9
Binary files /dev/null and b/test/reference/pthread-show-text.ref.png differ
diff --git a/test/reference/pthread-show-text.traps.argb32.ref.png b/test/reference/pthread-show-text.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..efa5fe9
Binary files /dev/null and b/test/reference/pthread-show-text.traps.argb32.ref.png differ
diff --git a/test/reference/pthread-show-text.traps.rgb24.ref.png b/test/reference/pthread-show-text.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..efa5fe9
Binary files /dev/null and b/test/reference/pthread-show-text.traps.rgb24.ref.png differ
diff --git a/test/reference/pthread-show-text.xlib-fallback.ref.png b/test/reference/pthread-show-text.xlib-fallback.ref.png
new file mode 100644 (file)
index 0000000..d96abed
Binary files /dev/null and b/test/reference/pthread-show-text.xlib-fallback.ref.png differ
diff --git a/test/reference/pthread-similar.base.argb32.ref.png b/test/reference/pthread-similar.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..a22210d
Binary files /dev/null and b/test/reference/pthread-similar.base.argb32.ref.png differ
diff --git a/test/reference/pthread-similar.base.rgb24.ref.png b/test/reference/pthread-similar.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..a22210d
Binary files /dev/null and b/test/reference/pthread-similar.base.rgb24.ref.png differ
diff --git a/test/reference/pthread-similar.ref.png b/test/reference/pthread-similar.ref.png
new file mode 100644 (file)
index 0000000..a22210d
Binary files /dev/null and b/test/reference/pthread-similar.ref.png differ
diff --git a/test/reference/pthread-similar.traps.argb32.ref.png b/test/reference/pthread-similar.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..a22210d
Binary files /dev/null and b/test/reference/pthread-similar.traps.argb32.ref.png differ
diff --git a/test/reference/pthread-similar.traps.rgb24.ref.png b/test/reference/pthread-similar.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..a22210d
Binary files /dev/null and b/test/reference/pthread-similar.traps.rgb24.ref.png differ
diff --git a/test/reference/push-group-color.base.argb32.ref.png b/test/reference/push-group-color.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..11b1014
Binary files /dev/null and b/test/reference/push-group-color.base.argb32.ref.png differ
diff --git a/test/reference/push-group-color.base.rgb24.ref.png b/test/reference/push-group-color.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..11b1014
Binary files /dev/null and b/test/reference/push-group-color.base.rgb24.ref.png differ
diff --git a/test/reference/push-group-color.image16.ref.png b/test/reference/push-group-color.image16.ref.png
new file mode 100644 (file)
index 0000000..6378b75
Binary files /dev/null and b/test/reference/push-group-color.image16.ref.png differ
diff --git a/test/reference/push-group-color.ps2.ref.png b/test/reference/push-group-color.ps2.ref.png
new file mode 100644 (file)
index 0000000..daf827e
Binary files /dev/null and b/test/reference/push-group-color.ps2.ref.png differ
diff --git a/test/reference/push-group-color.ps3.ref.png b/test/reference/push-group-color.ps3.ref.png
new file mode 100644 (file)
index 0000000..291fcec
Binary files /dev/null and b/test/reference/push-group-color.ps3.ref.png differ
diff --git a/test/reference/push-group-color.quartz.ref.png b/test/reference/push-group-color.quartz.ref.png
new file mode 100644 (file)
index 0000000..bca7c56
Binary files /dev/null and b/test/reference/push-group-color.quartz.ref.png differ
diff --git a/test/reference/push-group-color.ref.png b/test/reference/push-group-color.ref.png
new file mode 100644 (file)
index 0000000..1cb630e
Binary files /dev/null and b/test/reference/push-group-color.ref.png differ
diff --git a/test/reference/push-group-color.traps.argb32.ref.png b/test/reference/push-group-color.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..11b1014
Binary files /dev/null and b/test/reference/push-group-color.traps.argb32.ref.png differ
diff --git a/test/reference/push-group-color.traps.rgb24.ref.png b/test/reference/push-group-color.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..11b1014
Binary files /dev/null and b/test/reference/push-group-color.traps.rgb24.ref.png differ
diff --git a/test/reference/push-group-path-offset.base.argb32.ref.png b/test/reference/push-group-path-offset.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..b836a91
Binary files /dev/null and b/test/reference/push-group-path-offset.base.argb32.ref.png differ
diff --git a/test/reference/push-group-path-offset.base.rgb24.ref.png b/test/reference/push-group-path-offset.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..b836a91
Binary files /dev/null and b/test/reference/push-group-path-offset.base.rgb24.ref.png differ
diff --git a/test/reference/push-group-path-offset.ref.png b/test/reference/push-group-path-offset.ref.png
new file mode 100644 (file)
index 0000000..b836a91
Binary files /dev/null and b/test/reference/push-group-path-offset.ref.png differ
diff --git a/test/reference/push-group-path-offset.traps.argb32.ref.png b/test/reference/push-group-path-offset.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..b836a91
Binary files /dev/null and b/test/reference/push-group-path-offset.traps.argb32.ref.png differ
diff --git a/test/reference/push-group-path-offset.traps.rgb24.ref.png b/test/reference/push-group-path-offset.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..b836a91
Binary files /dev/null and b/test/reference/push-group-path-offset.traps.rgb24.ref.png differ
diff --git a/test/reference/push-group.base.argb32.ref.png b/test/reference/push-group.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..9d27f45
Binary files /dev/null and b/test/reference/push-group.base.argb32.ref.png differ
diff --git a/test/reference/push-group.base.rgb24.ref.png b/test/reference/push-group.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..e19694f
Binary files /dev/null and b/test/reference/push-group.base.rgb24.ref.png differ
diff --git a/test/reference/push-group.image16.ref.png b/test/reference/push-group.image16.ref.png
new file mode 100644 (file)
index 0000000..9990405
Binary files /dev/null and b/test/reference/push-group.image16.ref.png differ
diff --git a/test/reference/push-group.quartz.argb32.ref.png b/test/reference/push-group.quartz.argb32.ref.png
new file mode 100644 (file)
index 0000000..b3867b9
Binary files /dev/null and b/test/reference/push-group.quartz.argb32.ref.png differ
diff --git a/test/reference/push-group.quartz.rgb24.ref.png b/test/reference/push-group.quartz.rgb24.ref.png
new file mode 100644 (file)
index 0000000..1fd6ef3
Binary files /dev/null and b/test/reference/push-group.quartz.rgb24.ref.png differ
diff --git a/test/reference/push-group.ref.png b/test/reference/push-group.ref.png
new file mode 100644 (file)
index 0000000..a1175e2
Binary files /dev/null and b/test/reference/push-group.ref.png differ
diff --git a/test/reference/push-group.rgb24.ref.png b/test/reference/push-group.rgb24.ref.png
new file mode 100644 (file)
index 0000000..d88ff39
Binary files /dev/null and b/test/reference/push-group.rgb24.ref.png differ
diff --git a/test/reference/push-group.traps.argb32.ref.png b/test/reference/push-group.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..9d27f45
Binary files /dev/null and b/test/reference/push-group.traps.argb32.ref.png differ
diff --git a/test/reference/push-group.traps.rgb24.ref.png b/test/reference/push-group.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..e19694f
Binary files /dev/null and b/test/reference/push-group.traps.rgb24.ref.png differ
diff --git a/test/reference/quartz-surface-source.argb32.ref.png b/test/reference/quartz-surface-source.argb32.ref.png
new file mode 100644 (file)
index 0000000..0182972
Binary files /dev/null and b/test/reference/quartz-surface-source.argb32.ref.png differ
diff --git a/test/reference/quartz-surface-source.ps2.ref.png b/test/reference/quartz-surface-source.ps2.ref.png
new file mode 100644 (file)
index 0000000..1023158
Binary files /dev/null and b/test/reference/quartz-surface-source.ps2.ref.png differ
diff --git a/test/reference/quartz-surface-source.ps3.ref.png b/test/reference/quartz-surface-source.ps3.ref.png
new file mode 100644 (file)
index 0000000..1023158
Binary files /dev/null and b/test/reference/quartz-surface-source.ps3.ref.png differ
diff --git a/test/reference/quartz-surface-source.rgb24.ref.png b/test/reference/quartz-surface-source.rgb24.ref.png
new file mode 100644 (file)
index 0000000..0d68a82
Binary files /dev/null and b/test/reference/quartz-surface-source.rgb24.ref.png differ
diff --git a/test/reference/radial-gradient-extend.base.argb32.ref.png b/test/reference/radial-gradient-extend.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..3d7de5e
Binary files /dev/null and b/test/reference/radial-gradient-extend.base.argb32.ref.png differ
diff --git a/test/reference/radial-gradient-extend.base.rgb24.ref.png b/test/reference/radial-gradient-extend.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..3d7de5e
Binary files /dev/null and b/test/reference/radial-gradient-extend.base.rgb24.ref.png differ
diff --git a/test/reference/radial-gradient-extend.ps3.ref.png b/test/reference/radial-gradient-extend.ps3.ref.png
new file mode 100644 (file)
index 0000000..e84041e
Binary files /dev/null and b/test/reference/radial-gradient-extend.ps3.ref.png differ
diff --git a/test/reference/radial-gradient-extend.ref.png b/test/reference/radial-gradient-extend.ref.png
new file mode 100644 (file)
index 0000000..3d7de5e
Binary files /dev/null and b/test/reference/radial-gradient-extend.ref.png differ
diff --git a/test/reference/radial-gradient-extend.traps.argb32.ref.png b/test/reference/radial-gradient-extend.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..3d7de5e
Binary files /dev/null and b/test/reference/radial-gradient-extend.traps.argb32.ref.png differ
diff --git a/test/reference/radial-gradient-extend.traps.rgb24.ref.png b/test/reference/radial-gradient-extend.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..3d7de5e
Binary files /dev/null and b/test/reference/radial-gradient-extend.traps.rgb24.ref.png differ
diff --git a/test/reference/radial-gradient-mask-source.argb32.ref.png b/test/reference/radial-gradient-mask-source.argb32.ref.png
new file mode 100644 (file)
index 0000000..2bf65b3
Binary files /dev/null and b/test/reference/radial-gradient-mask-source.argb32.ref.png differ
diff --git a/test/reference/radial-gradient-mask-source.base.argb32.ref.png b/test/reference/radial-gradient-mask-source.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..2bf65b3
Binary files /dev/null and b/test/reference/radial-gradient-mask-source.base.argb32.ref.png differ
diff --git a/test/reference/radial-gradient-mask-source.base.rgb24.ref.png b/test/reference/radial-gradient-mask-source.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..5533519
Binary files /dev/null and b/test/reference/radial-gradient-mask-source.base.rgb24.ref.png differ
diff --git a/test/reference/radial-gradient-mask-source.image16.ref.png b/test/reference/radial-gradient-mask-source.image16.ref.png
new file mode 100644 (file)
index 0000000..edb93a9
Binary files /dev/null and b/test/reference/radial-gradient-mask-source.image16.ref.png differ
diff --git a/test/reference/radial-gradient-mask-source.quartz.argb32.ref.png b/test/reference/radial-gradient-mask-source.quartz.argb32.ref.png
new file mode 100644 (file)
index 0000000..5f734f6
Binary files /dev/null and b/test/reference/radial-gradient-mask-source.quartz.argb32.ref.png differ
diff --git a/test/reference/radial-gradient-mask-source.quartz.rgb24.ref.png b/test/reference/radial-gradient-mask-source.quartz.rgb24.ref.png
new file mode 100644 (file)
index 0000000..4ae71f7
Binary files /dev/null and b/test/reference/radial-gradient-mask-source.quartz.rgb24.ref.png differ
diff --git a/test/reference/radial-gradient-mask-source.traps.argb32.ref.png b/test/reference/radial-gradient-mask-source.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..2bf65b3
Binary files /dev/null and b/test/reference/radial-gradient-mask-source.traps.argb32.ref.png differ
diff --git a/test/reference/radial-gradient-mask-source.traps.rgb24.ref.png b/test/reference/radial-gradient-mask-source.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..5748d82
Binary files /dev/null and b/test/reference/radial-gradient-mask-source.traps.rgb24.ref.png differ
diff --git a/test/reference/radial-gradient-mask.base.argb32.ref.png b/test/reference/radial-gradient-mask.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..4db39aa
Binary files /dev/null and b/test/reference/radial-gradient-mask.base.argb32.ref.png differ
diff --git a/test/reference/radial-gradient-mask.base.rgb24.ref.png b/test/reference/radial-gradient-mask.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..4db39aa
Binary files /dev/null and b/test/reference/radial-gradient-mask.base.rgb24.ref.png differ
diff --git a/test/reference/radial-gradient-mask.image16.ref.png b/test/reference/radial-gradient-mask.image16.ref.png
new file mode 100644 (file)
index 0000000..78712ca
Binary files /dev/null and b/test/reference/radial-gradient-mask.image16.ref.png differ
diff --git a/test/reference/radial-gradient-mask.quartz.ref.png b/test/reference/radial-gradient-mask.quartz.ref.png
new file mode 100644 (file)
index 0000000..c1bd506
Binary files /dev/null and b/test/reference/radial-gradient-mask.quartz.ref.png differ
diff --git a/test/reference/radial-gradient-mask.ref.png b/test/reference/radial-gradient-mask.ref.png
new file mode 100644 (file)
index 0000000..4db39aa
Binary files /dev/null and b/test/reference/radial-gradient-mask.ref.png differ
diff --git a/test/reference/radial-gradient-mask.traps.argb32.ref.png b/test/reference/radial-gradient-mask.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..4db39aa
Binary files /dev/null and b/test/reference/radial-gradient-mask.traps.argb32.ref.png differ
diff --git a/test/reference/radial-gradient-mask.traps.rgb24.ref.png b/test/reference/radial-gradient-mask.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..4db39aa
Binary files /dev/null and b/test/reference/radial-gradient-mask.traps.rgb24.ref.png differ
diff --git a/test/reference/radial-gradient-one-stop.base.argb32.ref.png b/test/reference/radial-gradient-one-stop.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..fb35be6
Binary files /dev/null and b/test/reference/radial-gradient-one-stop.base.argb32.ref.png differ
diff --git a/test/reference/radial-gradient-one-stop.base.rgb24.ref.png b/test/reference/radial-gradient-one-stop.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..fb35be6
Binary files /dev/null and b/test/reference/radial-gradient-one-stop.base.rgb24.ref.png differ
diff --git a/test/reference/radial-gradient-one-stop.quartz.ref.png b/test/reference/radial-gradient-one-stop.quartz.ref.png
new file mode 100644 (file)
index 0000000..da991b1
Binary files /dev/null and b/test/reference/radial-gradient-one-stop.quartz.ref.png differ
diff --git a/test/reference/radial-gradient-one-stop.ref.png b/test/reference/radial-gradient-one-stop.ref.png
new file mode 100644 (file)
index 0000000..fb35be6
Binary files /dev/null and b/test/reference/radial-gradient-one-stop.ref.png differ
diff --git a/test/reference/radial-gradient-one-stop.traps.argb32.ref.png b/test/reference/radial-gradient-one-stop.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..fb35be6
Binary files /dev/null and b/test/reference/radial-gradient-one-stop.traps.argb32.ref.png differ
diff --git a/test/reference/radial-gradient-one-stop.traps.rgb24.ref.png b/test/reference/radial-gradient-one-stop.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..fb35be6
Binary files /dev/null and b/test/reference/radial-gradient-one-stop.traps.rgb24.ref.png differ
diff --git a/test/reference/radial-gradient-source.argb32.ref.png b/test/reference/radial-gradient-source.argb32.ref.png
new file mode 100644 (file)
index 0000000..4ab4796
Binary files /dev/null and b/test/reference/radial-gradient-source.argb32.ref.png differ
diff --git a/test/reference/radial-gradient-source.base.argb32.ref.png b/test/reference/radial-gradient-source.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..4ab4796
Binary files /dev/null and b/test/reference/radial-gradient-source.base.argb32.ref.png differ
diff --git a/test/reference/radial-gradient-source.base.rgb24.ref.png b/test/reference/radial-gradient-source.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..afaa241
Binary files /dev/null and b/test/reference/radial-gradient-source.base.rgb24.ref.png differ
diff --git a/test/reference/radial-gradient-source.image16.ref.png b/test/reference/radial-gradient-source.image16.ref.png
new file mode 100644 (file)
index 0000000..f992b6f
Binary files /dev/null and b/test/reference/radial-gradient-source.image16.ref.png differ
diff --git a/test/reference/radial-gradient-source.quartz.argb32.ref.png b/test/reference/radial-gradient-source.quartz.argb32.ref.png
new file mode 100644 (file)
index 0000000..421c0b9
Binary files /dev/null and b/test/reference/radial-gradient-source.quartz.argb32.ref.png differ
diff --git a/test/reference/radial-gradient-source.quartz.rgb24.ref.png b/test/reference/radial-gradient-source.quartz.rgb24.ref.png
new file mode 100644 (file)
index 0000000..22f2b90
Binary files /dev/null and b/test/reference/radial-gradient-source.quartz.rgb24.ref.png differ
diff --git a/test/reference/radial-gradient-source.rgb24.ref.png b/test/reference/radial-gradient-source.rgb24.ref.png
new file mode 100644 (file)
index 0000000..afaa241
Binary files /dev/null and b/test/reference/radial-gradient-source.rgb24.ref.png differ
diff --git a/test/reference/radial-gradient-source.traps.argb32.ref.png b/test/reference/radial-gradient-source.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..4ab4796
Binary files /dev/null and b/test/reference/radial-gradient-source.traps.argb32.ref.png differ
diff --git a/test/reference/radial-gradient-source.traps.rgb24.ref.png b/test/reference/radial-gradient-source.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..afaa241
Binary files /dev/null and b/test/reference/radial-gradient-source.traps.rgb24.ref.png differ
diff --git a/test/reference/radial-gradient.base.argb32.ref.png b/test/reference/radial-gradient.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..a2cbbc8
Binary files /dev/null and b/test/reference/radial-gradient.base.argb32.ref.png differ
diff --git a/test/reference/radial-gradient.base.rgb24.ref.png b/test/reference/radial-gradient.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..a2cbbc8
Binary files /dev/null and b/test/reference/radial-gradient.base.rgb24.ref.png differ
diff --git a/test/reference/radial-gradient.image16.ref.png b/test/reference/radial-gradient.image16.ref.png
new file mode 100644 (file)
index 0000000..91202d3
Binary files /dev/null and b/test/reference/radial-gradient.image16.ref.png differ
diff --git a/test/reference/radial-gradient.quartz.ref.png b/test/reference/radial-gradient.quartz.ref.png
new file mode 100644 (file)
index 0000000..f01c6eb
Binary files /dev/null and b/test/reference/radial-gradient.quartz.ref.png differ
diff --git a/test/reference/radial-gradient.ref.png b/test/reference/radial-gradient.ref.png
new file mode 100644 (file)
index 0000000..a2cbbc8
Binary files /dev/null and b/test/reference/radial-gradient.ref.png differ
diff --git a/test/reference/radial-gradient.traps.argb32.ref.png b/test/reference/radial-gradient.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..a2cbbc8
Binary files /dev/null and b/test/reference/radial-gradient.traps.argb32.ref.png differ
diff --git a/test/reference/radial-gradient.traps.rgb24.ref.png b/test/reference/radial-gradient.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..a2cbbc8
Binary files /dev/null and b/test/reference/radial-gradient.traps.rgb24.ref.png differ
diff --git a/test/reference/radial-outer-focus.xfail.png b/test/reference/radial-outer-focus.xfail.png
new file mode 100644 (file)
index 0000000..53e9f82
Binary files /dev/null and b/test/reference/radial-outer-focus.xfail.png differ
diff --git a/test/reference/random-clip.base.argb32.xfail.png b/test/reference/random-clip.base.argb32.xfail.png
new file mode 100644 (file)
index 0000000..15f9158
Binary files /dev/null and b/test/reference/random-clip.base.argb32.xfail.png differ
diff --git a/test/reference/random-clip.base.rgb24.xfail.png b/test/reference/random-clip.base.rgb24.xfail.png
new file mode 100644 (file)
index 0000000..15f9158
Binary files /dev/null and b/test/reference/random-clip.base.rgb24.xfail.png differ
diff --git a/test/reference/random-clip.ref.png b/test/reference/random-clip.ref.png
new file mode 100644 (file)
index 0000000..374d6fe
Binary files /dev/null and b/test/reference/random-clip.ref.png differ
diff --git a/test/reference/random-clip.traps.argb32.ref.png b/test/reference/random-clip.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..374d6fe
Binary files /dev/null and b/test/reference/random-clip.traps.argb32.ref.png differ
diff --git a/test/reference/random-clip.traps.rgb24.ref.png b/test/reference/random-clip.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..374d6fe
Binary files /dev/null and b/test/reference/random-clip.traps.rgb24.ref.png differ
diff --git a/test/reference/random-intersections-curves-eo.base.argb32.ref.png b/test/reference/random-intersections-curves-eo.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..ee10cb4
Binary files /dev/null and b/test/reference/random-intersections-curves-eo.base.argb32.ref.png differ
diff --git a/test/reference/random-intersections-curves-eo.base.rgb24.ref.png b/test/reference/random-intersections-curves-eo.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..ee10cb4
Binary files /dev/null and b/test/reference/random-intersections-curves-eo.base.rgb24.ref.png differ
diff --git a/test/reference/random-intersections-curves-eo.image16.ref.png b/test/reference/random-intersections-curves-eo.image16.ref.png
new file mode 100644 (file)
index 0000000..0663270
Binary files /dev/null and b/test/reference/random-intersections-curves-eo.image16.ref.png differ
diff --git a/test/reference/random-intersections-curves-eo.pdf.ref.png b/test/reference/random-intersections-curves-eo.pdf.ref.png
new file mode 100644 (file)
index 0000000..befa3c8
Binary files /dev/null and b/test/reference/random-intersections-curves-eo.pdf.ref.png differ
diff --git a/test/reference/random-intersections-curves-eo.ps.ref.png b/test/reference/random-intersections-curves-eo.ps.ref.png
new file mode 100644 (file)
index 0000000..374ace9
Binary files /dev/null and b/test/reference/random-intersections-curves-eo.ps.ref.png differ
diff --git a/test/reference/random-intersections-curves-eo.quartz.ref.png b/test/reference/random-intersections-curves-eo.quartz.ref.png
new file mode 100644 (file)
index 0000000..2a44a5a
Binary files /dev/null and b/test/reference/random-intersections-curves-eo.quartz.ref.png differ
diff --git a/test/reference/random-intersections-curves-eo.ref.png b/test/reference/random-intersections-curves-eo.ref.png
new file mode 100644 (file)
index 0000000..e8cfebd
Binary files /dev/null and b/test/reference/random-intersections-curves-eo.ref.png differ
diff --git a/test/reference/random-intersections-curves-eo.traps.argb32.ref.png b/test/reference/random-intersections-curves-eo.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..ee10cb4
Binary files /dev/null and b/test/reference/random-intersections-curves-eo.traps.argb32.ref.png differ
diff --git a/test/reference/random-intersections-curves-eo.traps.rgb24.ref.png b/test/reference/random-intersections-curves-eo.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..ee10cb4
Binary files /dev/null and b/test/reference/random-intersections-curves-eo.traps.rgb24.ref.png differ
diff --git a/test/reference/random-intersections-curves-eo.xlib-fallback.ref.png b/test/reference/random-intersections-curves-eo.xlib-fallback.ref.png
new file mode 100644 (file)
index 0000000..d91af0b
Binary files /dev/null and b/test/reference/random-intersections-curves-eo.xlib-fallback.ref.png differ
diff --git a/test/reference/random-intersections-curves-nz.base.argb32.ref.png b/test/reference/random-intersections-curves-nz.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..77f8129
Binary files /dev/null and b/test/reference/random-intersections-curves-nz.base.argb32.ref.png differ
diff --git a/test/reference/random-intersections-curves-nz.base.rgb24.ref.png b/test/reference/random-intersections-curves-nz.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..77f8129
Binary files /dev/null and b/test/reference/random-intersections-curves-nz.base.rgb24.ref.png differ
diff --git a/test/reference/random-intersections-curves-nz.image16.ref.png b/test/reference/random-intersections-curves-nz.image16.ref.png
new file mode 100644 (file)
index 0000000..5fbc711
Binary files /dev/null and b/test/reference/random-intersections-curves-nz.image16.ref.png differ
diff --git a/test/reference/random-intersections-curves-nz.pdf.ref.png b/test/reference/random-intersections-curves-nz.pdf.ref.png
new file mode 100644 (file)
index 0000000..a374934
Binary files /dev/null and b/test/reference/random-intersections-curves-nz.pdf.ref.png differ
diff --git a/test/reference/random-intersections-curves-nz.ps.ref.png b/test/reference/random-intersections-curves-nz.ps.ref.png
new file mode 100644 (file)
index 0000000..6cc5814
Binary files /dev/null and b/test/reference/random-intersections-curves-nz.ps.ref.png differ
diff --git a/test/reference/random-intersections-curves-nz.quartz.ref.png b/test/reference/random-intersections-curves-nz.quartz.ref.png
new file mode 100644 (file)
index 0000000..cf799bb
Binary files /dev/null and b/test/reference/random-intersections-curves-nz.quartz.ref.png differ
diff --git a/test/reference/random-intersections-curves-nz.ref.png b/test/reference/random-intersections-curves-nz.ref.png
new file mode 100644 (file)
index 0000000..6cd9495
Binary files /dev/null and b/test/reference/random-intersections-curves-nz.ref.png differ
diff --git a/test/reference/random-intersections-curves-nz.traps.argb32.ref.png b/test/reference/random-intersections-curves-nz.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..77f8129
Binary files /dev/null and b/test/reference/random-intersections-curves-nz.traps.argb32.ref.png differ
diff --git a/test/reference/random-intersections-curves-nz.traps.rgb24.ref.png b/test/reference/random-intersections-curves-nz.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..77f8129
Binary files /dev/null and b/test/reference/random-intersections-curves-nz.traps.rgb24.ref.png differ
diff --git a/test/reference/random-intersections-curves-nz.xlib-fallback.ref.png b/test/reference/random-intersections-curves-nz.xlib-fallback.ref.png
new file mode 100644 (file)
index 0000000..f72f8f1
Binary files /dev/null and b/test/reference/random-intersections-curves-nz.xlib-fallback.ref.png differ
diff --git a/test/reference/random-intersections-eo.base.argb32.ref.png b/test/reference/random-intersections-eo.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..037f926
Binary files /dev/null and b/test/reference/random-intersections-eo.base.argb32.ref.png differ
diff --git a/test/reference/random-intersections-eo.base.rgb24.ref.png b/test/reference/random-intersections-eo.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..037f926
Binary files /dev/null and b/test/reference/random-intersections-eo.base.rgb24.ref.png differ
diff --git a/test/reference/random-intersections-eo.image16.ref.png b/test/reference/random-intersections-eo.image16.ref.png
new file mode 100644 (file)
index 0000000..cf214d4
Binary files /dev/null and b/test/reference/random-intersections-eo.image16.ref.png differ
diff --git a/test/reference/random-intersections-eo.ps.ref.png b/test/reference/random-intersections-eo.ps.ref.png
new file mode 100644 (file)
index 0000000..49b359a
Binary files /dev/null and b/test/reference/random-intersections-eo.ps.ref.png differ
diff --git a/test/reference/random-intersections-eo.quartz.ref.png b/test/reference/random-intersections-eo.quartz.ref.png
new file mode 100644 (file)
index 0000000..859abb0
Binary files /dev/null and b/test/reference/random-intersections-eo.quartz.ref.png differ
diff --git a/test/reference/random-intersections-eo.ref.png b/test/reference/random-intersections-eo.ref.png
new file mode 100644 (file)
index 0000000..b472113
Binary files /dev/null and b/test/reference/random-intersections-eo.ref.png differ
diff --git a/test/reference/random-intersections-eo.traps.argb32.ref.png b/test/reference/random-intersections-eo.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..037f926
Binary files /dev/null and b/test/reference/random-intersections-eo.traps.argb32.ref.png differ
diff --git a/test/reference/random-intersections-eo.traps.rgb24.ref.png b/test/reference/random-intersections-eo.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..037f926
Binary files /dev/null and b/test/reference/random-intersections-eo.traps.rgb24.ref.png differ
diff --git a/test/reference/random-intersections-nonzero.base.argb32.ref.png b/test/reference/random-intersections-nonzero.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..7eb9a0a
Binary files /dev/null and b/test/reference/random-intersections-nonzero.base.argb32.ref.png differ
diff --git a/test/reference/random-intersections-nonzero.base.rgb24.ref.png b/test/reference/random-intersections-nonzero.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..7eb9a0a
Binary files /dev/null and b/test/reference/random-intersections-nonzero.base.rgb24.ref.png differ
diff --git a/test/reference/random-intersections-nonzero.image16.ref.png b/test/reference/random-intersections-nonzero.image16.ref.png
new file mode 100644 (file)
index 0000000..370abd9
Binary files /dev/null and b/test/reference/random-intersections-nonzero.image16.ref.png differ
diff --git a/test/reference/random-intersections-nonzero.ps.ref.png b/test/reference/random-intersections-nonzero.ps.ref.png
new file mode 100644 (file)
index 0000000..53a151f
Binary files /dev/null and b/test/reference/random-intersections-nonzero.ps.ref.png differ
diff --git a/test/reference/random-intersections-nonzero.quartz.ref.png b/test/reference/random-intersections-nonzero.quartz.ref.png
new file mode 100644 (file)
index 0000000..f4310ea
Binary files /dev/null and b/test/reference/random-intersections-nonzero.quartz.ref.png differ
diff --git a/test/reference/random-intersections-nonzero.ref.png b/test/reference/random-intersections-nonzero.ref.png
new file mode 100644 (file)
index 0000000..777d192
Binary files /dev/null and b/test/reference/random-intersections-nonzero.ref.png differ
diff --git a/test/reference/random-intersections-nonzero.traps.argb32.ref.png b/test/reference/random-intersections-nonzero.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..7eb9a0a
Binary files /dev/null and b/test/reference/random-intersections-nonzero.traps.argb32.ref.png differ
diff --git a/test/reference/random-intersections-nonzero.traps.rgb24.ref.png b/test/reference/random-intersections-nonzero.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..7eb9a0a
Binary files /dev/null and b/test/reference/random-intersections-nonzero.traps.rgb24.ref.png differ
diff --git a/test/reference/record-extend-none-similar.ref.png b/test/reference/record-extend-none-similar.ref.png
new file mode 100644 (file)
index 0000000..d63c31c
Binary files /dev/null and b/test/reference/record-extend-none-similar.ref.png differ
diff --git a/test/reference/record-extend-none-similar.traps.argb32.ref.png b/test/reference/record-extend-none-similar.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..d63c31c
Binary files /dev/null and b/test/reference/record-extend-none-similar.traps.argb32.ref.png differ
diff --git a/test/reference/record-extend-none-similar.traps.rgb24.ref.png b/test/reference/record-extend-none-similar.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..d63c31c
Binary files /dev/null and b/test/reference/record-extend-none-similar.traps.rgb24.ref.png differ
diff --git a/test/reference/record-extend-none.ref.png b/test/reference/record-extend-none.ref.png
new file mode 100644 (file)
index 0000000..d63c31c
Binary files /dev/null and b/test/reference/record-extend-none.ref.png differ
diff --git a/test/reference/record-extend-none.traps.argb32.ref.png b/test/reference/record-extend-none.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..d63c31c
Binary files /dev/null and b/test/reference/record-extend-none.traps.argb32.ref.png differ
diff --git a/test/reference/record-extend-none.traps.rgb24.ref.png b/test/reference/record-extend-none.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..d63c31c
Binary files /dev/null and b/test/reference/record-extend-none.traps.rgb24.ref.png differ
diff --git a/test/reference/record-extend-pad-similar.ref.png b/test/reference/record-extend-pad-similar.ref.png
new file mode 100644 (file)
index 0000000..1291525
Binary files /dev/null and b/test/reference/record-extend-pad-similar.ref.png differ
diff --git a/test/reference/record-extend-pad-similar.traps.argb32.ref.png b/test/reference/record-extend-pad-similar.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..1291525
Binary files /dev/null and b/test/reference/record-extend-pad-similar.traps.argb32.ref.png differ
diff --git a/test/reference/record-extend-pad-similar.traps.rgb24.ref.png b/test/reference/record-extend-pad-similar.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..1291525
Binary files /dev/null and b/test/reference/record-extend-pad-similar.traps.rgb24.ref.png differ
diff --git a/test/reference/record-extend-pad.ref.png b/test/reference/record-extend-pad.ref.png
new file mode 100644 (file)
index 0000000..1291525
Binary files /dev/null and b/test/reference/record-extend-pad.ref.png differ
diff --git a/test/reference/record-extend-pad.traps.argb32.ref.png b/test/reference/record-extend-pad.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..1291525
Binary files /dev/null and b/test/reference/record-extend-pad.traps.argb32.ref.png differ
diff --git a/test/reference/record-extend-pad.traps.rgb24.ref.png b/test/reference/record-extend-pad.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..1291525
Binary files /dev/null and b/test/reference/record-extend-pad.traps.rgb24.ref.png differ
diff --git a/test/reference/record-extend-reflect-similar.ref.png b/test/reference/record-extend-reflect-similar.ref.png
new file mode 100644 (file)
index 0000000..aec5c94
Binary files /dev/null and b/test/reference/record-extend-reflect-similar.ref.png differ
diff --git a/test/reference/record-extend-reflect-similar.traps.argb32.ref.png b/test/reference/record-extend-reflect-similar.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..aec5c94
Binary files /dev/null and b/test/reference/record-extend-reflect-similar.traps.argb32.ref.png differ
diff --git a/test/reference/record-extend-reflect-similar.traps.rgb24.ref.png b/test/reference/record-extend-reflect-similar.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..aec5c94
Binary files /dev/null and b/test/reference/record-extend-reflect-similar.traps.rgb24.ref.png differ
diff --git a/test/reference/record-extend-reflect.ref.png b/test/reference/record-extend-reflect.ref.png
new file mode 100644 (file)
index 0000000..aec5c94
Binary files /dev/null and b/test/reference/record-extend-reflect.ref.png differ
diff --git a/test/reference/record-extend-reflect.traps.argb32.ref.png b/test/reference/record-extend-reflect.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..aec5c94
Binary files /dev/null and b/test/reference/record-extend-reflect.traps.argb32.ref.png differ
diff --git a/test/reference/record-extend-reflect.traps.rgb24.ref.png b/test/reference/record-extend-reflect.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..aec5c94
Binary files /dev/null and b/test/reference/record-extend-reflect.traps.rgb24.ref.png differ
diff --git a/test/reference/record-extend-repeat-similar.ref.png b/test/reference/record-extend-repeat-similar.ref.png
new file mode 100644 (file)
index 0000000..d4db9bf
Binary files /dev/null and b/test/reference/record-extend-repeat-similar.ref.png differ
diff --git a/test/reference/record-extend-repeat-similar.traps.argb32.ref.png b/test/reference/record-extend-repeat-similar.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..d4db9bf
Binary files /dev/null and b/test/reference/record-extend-repeat-similar.traps.argb32.ref.png differ
diff --git a/test/reference/record-extend-repeat-similar.traps.rgb24.ref.png b/test/reference/record-extend-repeat-similar.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..d4db9bf
Binary files /dev/null and b/test/reference/record-extend-repeat-similar.traps.rgb24.ref.png differ
diff --git a/test/reference/record-extend-repeat.ref.png b/test/reference/record-extend-repeat.ref.png
new file mode 100644 (file)
index 0000000..d4db9bf
Binary files /dev/null and b/test/reference/record-extend-repeat.ref.png differ
diff --git a/test/reference/record-extend-repeat.traps.argb32.ref.png b/test/reference/record-extend-repeat.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..d4db9bf
Binary files /dev/null and b/test/reference/record-extend-repeat.traps.argb32.ref.png differ
diff --git a/test/reference/record-extend-repeat.traps.rgb24.ref.png b/test/reference/record-extend-repeat.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..d4db9bf
Binary files /dev/null and b/test/reference/record-extend-repeat.traps.rgb24.ref.png differ
diff --git a/test/reference/record-fill-alpha.ref.png b/test/reference/record-fill-alpha.ref.png
new file mode 100644 (file)
index 0000000..3f8fa43
Binary files /dev/null and b/test/reference/record-fill-alpha.ref.png differ
diff --git a/test/reference/record-fill-alpha.traps.argb32.ref.png b/test/reference/record-fill-alpha.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..85df919
Binary files /dev/null and b/test/reference/record-fill-alpha.traps.argb32.ref.png differ
diff --git a/test/reference/record-fill-alpha.traps.rgb24.ref.png b/test/reference/record-fill-alpha.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..85df919
Binary files /dev/null and b/test/reference/record-fill-alpha.traps.rgb24.ref.png differ
diff --git a/test/reference/record-mesh.ref.png b/test/reference/record-mesh.ref.png
new file mode 100644 (file)
index 0000000..4921ba3
Binary files /dev/null and b/test/reference/record-mesh.ref.png differ
diff --git a/test/reference/record-mesh.traps.argb32.ref.png b/test/reference/record-mesh.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..4921ba3
Binary files /dev/null and b/test/reference/record-mesh.traps.argb32.ref.png differ
diff --git a/test/reference/record-mesh.traps.rgb24.ref.png b/test/reference/record-mesh.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..4921ba3
Binary files /dev/null and b/test/reference/record-mesh.traps.rgb24.ref.png differ
diff --git a/test/reference/record-paint-alpha-clip-mask.ref.png b/test/reference/record-paint-alpha-clip-mask.ref.png
new file mode 100644 (file)
index 0000000..3c7089c
Binary files /dev/null and b/test/reference/record-paint-alpha-clip-mask.ref.png differ
diff --git a/test/reference/record-paint-alpha-clip-mask.traps.argb32.ref.png b/test/reference/record-paint-alpha-clip-mask.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..201bd0d
Binary files /dev/null and b/test/reference/record-paint-alpha-clip-mask.traps.argb32.ref.png differ
diff --git a/test/reference/record-paint-alpha-clip-mask.traps.rgb24.ref.png b/test/reference/record-paint-alpha-clip-mask.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..201bd0d
Binary files /dev/null and b/test/reference/record-paint-alpha-clip-mask.traps.rgb24.ref.png differ
diff --git a/test/reference/record-paint-alpha-clip.ref.png b/test/reference/record-paint-alpha-clip.ref.png
new file mode 100644 (file)
index 0000000..9f6841e
Binary files /dev/null and b/test/reference/record-paint-alpha-clip.ref.png differ
diff --git a/test/reference/record-paint-alpha-clip.traps.argb32.ref.png b/test/reference/record-paint-alpha-clip.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..c1da67e
Binary files /dev/null and b/test/reference/record-paint-alpha-clip.traps.argb32.ref.png differ
diff --git a/test/reference/record-paint-alpha-clip.traps.rgb24.ref.png b/test/reference/record-paint-alpha-clip.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..c1da67e
Binary files /dev/null and b/test/reference/record-paint-alpha-clip.traps.rgb24.ref.png differ
diff --git a/test/reference/record-paint-alpha-solid-clip.ref.png b/test/reference/record-paint-alpha-solid-clip.ref.png
new file mode 100644 (file)
index 0000000..59d226d
Binary files /dev/null and b/test/reference/record-paint-alpha-solid-clip.ref.png differ
diff --git a/test/reference/record-paint-alpha-solid-clip.traps.argb32.ref.png b/test/reference/record-paint-alpha-solid-clip.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..2cd2df2
Binary files /dev/null and b/test/reference/record-paint-alpha-solid-clip.traps.argb32.ref.png differ
diff --git a/test/reference/record-paint-alpha-solid-clip.traps.rgb24.ref.png b/test/reference/record-paint-alpha-solid-clip.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..2cd2df2
Binary files /dev/null and b/test/reference/record-paint-alpha-solid-clip.traps.rgb24.ref.png differ
diff --git a/test/reference/record-paint-alpha.ref.png b/test/reference/record-paint-alpha.ref.png
new file mode 100644 (file)
index 0000000..ab7ce3e
Binary files /dev/null and b/test/reference/record-paint-alpha.ref.png differ
diff --git a/test/reference/record-paint-alpha.traps.argb32.ref.png b/test/reference/record-paint-alpha.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..65d9c17
Binary files /dev/null and b/test/reference/record-paint-alpha.traps.argb32.ref.png differ
diff --git a/test/reference/record-paint-alpha.traps.rgb24.ref.png b/test/reference/record-paint-alpha.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..65d9c17
Binary files /dev/null and b/test/reference/record-paint-alpha.traps.rgb24.ref.png differ
diff --git a/test/reference/record-paint.ref.png b/test/reference/record-paint.ref.png
new file mode 100644 (file)
index 0000000..fff03b3
Binary files /dev/null and b/test/reference/record-paint.ref.png differ
diff --git a/test/reference/record-paint.traps.argb32.ref.png b/test/reference/record-paint.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..22cc7a1
Binary files /dev/null and b/test/reference/record-paint.traps.argb32.ref.png differ
diff --git a/test/reference/record-paint.traps.rgb24.ref.png b/test/reference/record-paint.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..22cc7a1
Binary files /dev/null and b/test/reference/record-paint.traps.rgb24.ref.png differ
diff --git a/test/reference/record-select-font-face.ref.png b/test/reference/record-select-font-face.ref.png
new file mode 100644 (file)
index 0000000..b3d2322
Binary files /dev/null and b/test/reference/record-select-font-face.ref.png differ
diff --git a/test/reference/record-select-font-face.xfail.png b/test/reference/record-select-font-face.xfail.png
new file mode 100644 (file)
index 0000000..1f57d69
Binary files /dev/null and b/test/reference/record-select-font-face.xfail.png differ
diff --git a/test/reference/record-self-intersecting.ref.png b/test/reference/record-self-intersecting.ref.png
new file mode 100644 (file)
index 0000000..d554d83
Binary files /dev/null and b/test/reference/record-self-intersecting.ref.png differ
diff --git a/test/reference/record-text-transform.ref.png b/test/reference/record-text-transform.ref.png
new file mode 100644 (file)
index 0000000..6f36b9d
Binary files /dev/null and b/test/reference/record-text-transform.ref.png differ
diff --git a/test/reference/recording-surface-extend-none.argb32.ref.png b/test/reference/recording-surface-extend-none.argb32.ref.png
new file mode 100644 (file)
index 0000000..c8040da
Binary files /dev/null and b/test/reference/recording-surface-extend-none.argb32.ref.png differ
diff --git a/test/reference/recording-surface-extend-none.base.argb32.ref.png b/test/reference/recording-surface-extend-none.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..d612250
Binary files /dev/null and b/test/reference/recording-surface-extend-none.base.argb32.ref.png differ
diff --git a/test/reference/recording-surface-extend-none.base.rgb24.ref.png b/test/reference/recording-surface-extend-none.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..0a57b44
Binary files /dev/null and b/test/reference/recording-surface-extend-none.base.rgb24.ref.png differ
diff --git a/test/reference/recording-surface-extend-none.rgb24.ref.png b/test/reference/recording-surface-extend-none.rgb24.ref.png
new file mode 100644 (file)
index 0000000..bd84338
Binary files /dev/null and b/test/reference/recording-surface-extend-none.rgb24.ref.png differ
diff --git a/test/reference/recording-surface-extend-none.traps.argb32.ref.png b/test/reference/recording-surface-extend-none.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..d612250
Binary files /dev/null and b/test/reference/recording-surface-extend-none.traps.argb32.ref.png differ
diff --git a/test/reference/recording-surface-extend-none.traps.rgb24.ref.png b/test/reference/recording-surface-extend-none.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..0a57b44
Binary files /dev/null and b/test/reference/recording-surface-extend-none.traps.rgb24.ref.png differ
diff --git a/test/reference/recording-surface-extend-pad.argb32.ref.png b/test/reference/recording-surface-extend-pad.argb32.ref.png
new file mode 100644 (file)
index 0000000..f1c3d23
Binary files /dev/null and b/test/reference/recording-surface-extend-pad.argb32.ref.png differ
diff --git a/test/reference/recording-surface-extend-pad.base.argb32.ref.png b/test/reference/recording-surface-extend-pad.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..7ec94c5
Binary files /dev/null and b/test/reference/recording-surface-extend-pad.base.argb32.ref.png differ
diff --git a/test/reference/recording-surface-extend-pad.base.rgb24.ref.png b/test/reference/recording-surface-extend-pad.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..8a064cd
Binary files /dev/null and b/test/reference/recording-surface-extend-pad.base.rgb24.ref.png differ
diff --git a/test/reference/recording-surface-extend-pad.rgb24.ref.png b/test/reference/recording-surface-extend-pad.rgb24.ref.png
new file mode 100644 (file)
index 0000000..4906c8f
Binary files /dev/null and b/test/reference/recording-surface-extend-pad.rgb24.ref.png differ
diff --git a/test/reference/recording-surface-extend-pad.traps.argb32.ref.png b/test/reference/recording-surface-extend-pad.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..7ec94c5
Binary files /dev/null and b/test/reference/recording-surface-extend-pad.traps.argb32.ref.png differ
diff --git a/test/reference/recording-surface-extend-pad.traps.rgb24.ref.png b/test/reference/recording-surface-extend-pad.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..8a064cd
Binary files /dev/null and b/test/reference/recording-surface-extend-pad.traps.rgb24.ref.png differ
diff --git a/test/reference/recording-surface-extend-reflect.argb32.ref.png b/test/reference/recording-surface-extend-reflect.argb32.ref.png
new file mode 100644 (file)
index 0000000..016bf39
Binary files /dev/null and b/test/reference/recording-surface-extend-reflect.argb32.ref.png differ
diff --git a/test/reference/recording-surface-extend-reflect.base.argb32.ref.png b/test/reference/recording-surface-extend-reflect.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..4699d42
Binary files /dev/null and b/test/reference/recording-surface-extend-reflect.base.argb32.ref.png differ
diff --git a/test/reference/recording-surface-extend-reflect.base.rgb24.ref.png b/test/reference/recording-surface-extend-reflect.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..4975d75
Binary files /dev/null and b/test/reference/recording-surface-extend-reflect.base.rgb24.ref.png differ
diff --git a/test/reference/recording-surface-extend-reflect.rgb24.ref.png b/test/reference/recording-surface-extend-reflect.rgb24.ref.png
new file mode 100644 (file)
index 0000000..59b58e9
Binary files /dev/null and b/test/reference/recording-surface-extend-reflect.rgb24.ref.png differ
diff --git a/test/reference/recording-surface-extend-reflect.traps.argb32.ref.png b/test/reference/recording-surface-extend-reflect.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..4699d42
Binary files /dev/null and b/test/reference/recording-surface-extend-reflect.traps.argb32.ref.png differ
diff --git a/test/reference/recording-surface-extend-reflect.traps.rgb24.ref.png b/test/reference/recording-surface-extend-reflect.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..4975d75
Binary files /dev/null and b/test/reference/recording-surface-extend-reflect.traps.rgb24.ref.png differ
diff --git a/test/reference/recording-surface-extend-repeat.argb32.ref.png b/test/reference/recording-surface-extend-repeat.argb32.ref.png
new file mode 100644 (file)
index 0000000..960bfd2
Binary files /dev/null and b/test/reference/recording-surface-extend-repeat.argb32.ref.png differ
diff --git a/test/reference/recording-surface-extend-repeat.base.argb32.ref.png b/test/reference/recording-surface-extend-repeat.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..305c022
Binary files /dev/null and b/test/reference/recording-surface-extend-repeat.base.argb32.ref.png differ
diff --git a/test/reference/recording-surface-extend-repeat.base.rgb24.ref.png b/test/reference/recording-surface-extend-repeat.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..c3e296c
Binary files /dev/null and b/test/reference/recording-surface-extend-repeat.base.rgb24.ref.png differ
diff --git a/test/reference/recording-surface-extend-repeat.rgb24.ref.png b/test/reference/recording-surface-extend-repeat.rgb24.ref.png
new file mode 100644 (file)
index 0000000..d49fd12
Binary files /dev/null and b/test/reference/recording-surface-extend-repeat.rgb24.ref.png differ
diff --git a/test/reference/recording-surface-extend-repeat.traps.argb32.ref.png b/test/reference/recording-surface-extend-repeat.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..305c022
Binary files /dev/null and b/test/reference/recording-surface-extend-repeat.traps.argb32.ref.png differ
diff --git a/test/reference/recording-surface-extend-repeat.traps.rgb24.ref.png b/test/reference/recording-surface-extend-repeat.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..c3e296c
Binary files /dev/null and b/test/reference/recording-surface-extend-repeat.traps.rgb24.ref.png differ
diff --git a/test/reference/recording-surface-over.base.argb32.ref.png b/test/reference/recording-surface-over.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..d612250
Binary files /dev/null and b/test/reference/recording-surface-over.base.argb32.ref.png differ
diff --git a/test/reference/recording-surface-over.base.rgb24.ref.png b/test/reference/recording-surface-over.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..0a57b44
Binary files /dev/null and b/test/reference/recording-surface-over.base.rgb24.ref.png differ
diff --git a/test/reference/recording-surface-over.gl.argb32.ref.png b/test/reference/recording-surface-over.gl.argb32.ref.png
new file mode 100644 (file)
index 0000000..50e6f5a
Binary files /dev/null and b/test/reference/recording-surface-over.gl.argb32.ref.png differ
diff --git a/test/reference/recording-surface-over.image16.ref.png b/test/reference/recording-surface-over.image16.ref.png
new file mode 100644 (file)
index 0000000..0202893
Binary files /dev/null and b/test/reference/recording-surface-over.image16.ref.png differ
diff --git a/test/reference/recording-surface-over.pdf.argb32.ref.png b/test/reference/recording-surface-over.pdf.argb32.ref.png
new file mode 100644 (file)
index 0000000..a06386b
Binary files /dev/null and b/test/reference/recording-surface-over.pdf.argb32.ref.png differ
diff --git a/test/reference/recording-surface-over.pdf.rgb24.ref.png b/test/reference/recording-surface-over.pdf.rgb24.ref.png
new file mode 100644 (file)
index 0000000..bf69f9e
Binary files /dev/null and b/test/reference/recording-surface-over.pdf.rgb24.ref.png differ
diff --git a/test/reference/recording-surface-over.ps.argb32.ref.png b/test/reference/recording-surface-over.ps.argb32.ref.png
new file mode 100644 (file)
index 0000000..ac66323
Binary files /dev/null and b/test/reference/recording-surface-over.ps.argb32.ref.png differ
diff --git a/test/reference/recording-surface-over.ps.rgb24.ref.png b/test/reference/recording-surface-over.ps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..fab3382
Binary files /dev/null and b/test/reference/recording-surface-over.ps.rgb24.ref.png differ
diff --git a/test/reference/recording-surface-over.quartz.argb32.ref.png b/test/reference/recording-surface-over.quartz.argb32.ref.png
new file mode 100644 (file)
index 0000000..09d9559
Binary files /dev/null and b/test/reference/recording-surface-over.quartz.argb32.ref.png differ
diff --git a/test/reference/recording-surface-over.quartz.rgb24.ref.png b/test/reference/recording-surface-over.quartz.rgb24.ref.png
new file mode 100644 (file)
index 0000000..96aff40
Binary files /dev/null and b/test/reference/recording-surface-over.quartz.rgb24.ref.png differ
diff --git a/test/reference/recording-surface-over.ref.png b/test/reference/recording-surface-over.ref.png
new file mode 100644 (file)
index 0000000..7f9c56c
Binary files /dev/null and b/test/reference/recording-surface-over.ref.png differ
diff --git a/test/reference/recording-surface-over.rgb24.ref.png b/test/reference/recording-surface-over.rgb24.ref.png
new file mode 100644 (file)
index 0000000..bd84338
Binary files /dev/null and b/test/reference/recording-surface-over.rgb24.ref.png differ
diff --git a/test/reference/recording-surface-over.svg.argb32.ref.png b/test/reference/recording-surface-over.svg.argb32.ref.png
new file mode 100644 (file)
index 0000000..ff4154d
Binary files /dev/null and b/test/reference/recording-surface-over.svg.argb32.ref.png differ
diff --git a/test/reference/recording-surface-over.svg.rgb24.ref.png b/test/reference/recording-surface-over.svg.rgb24.ref.png
new file mode 100644 (file)
index 0000000..d2d5372
Binary files /dev/null and b/test/reference/recording-surface-over.svg.rgb24.ref.png differ
diff --git a/test/reference/recording-surface-over.traps.argb32.ref.png b/test/reference/recording-surface-over.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..d612250
Binary files /dev/null and b/test/reference/recording-surface-over.traps.argb32.ref.png differ
diff --git a/test/reference/recording-surface-over.traps.rgb24.ref.png b/test/reference/recording-surface-over.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..0a57b44
Binary files /dev/null and b/test/reference/recording-surface-over.traps.rgb24.ref.png differ
diff --git a/test/reference/recording-surface-source.argb32.ref.png b/test/reference/recording-surface-source.argb32.ref.png
new file mode 100644 (file)
index 0000000..22c612b
Binary files /dev/null and b/test/reference/recording-surface-source.argb32.ref.png differ
diff --git a/test/reference/recording-surface-source.base.argb32.ref.png b/test/reference/recording-surface-source.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..3fe1057
Binary files /dev/null and b/test/reference/recording-surface-source.base.argb32.ref.png differ
diff --git a/test/reference/recording-surface-source.base.rgb24.ref.png b/test/reference/recording-surface-source.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..d2605bd
Binary files /dev/null and b/test/reference/recording-surface-source.base.rgb24.ref.png differ
diff --git a/test/reference/recording-surface-source.rgb24.ref.png b/test/reference/recording-surface-source.rgb24.ref.png
new file mode 100644 (file)
index 0000000..3481673
Binary files /dev/null and b/test/reference/recording-surface-source.rgb24.ref.png differ
diff --git a/test/reference/recording-surface-source.traps.argb32.ref.png b/test/reference/recording-surface-source.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..3fe1057
Binary files /dev/null and b/test/reference/recording-surface-source.traps.argb32.ref.png differ
diff --git a/test/reference/recording-surface-source.traps.rgb24.ref.png b/test/reference/recording-surface-source.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..d2605bd
Binary files /dev/null and b/test/reference/recording-surface-source.traps.rgb24.ref.png differ
diff --git a/test/reference/rectangle-rounding-error.base.argb32.ref.png b/test/reference/rectangle-rounding-error.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..6cc1b21
Binary files /dev/null and b/test/reference/rectangle-rounding-error.base.argb32.ref.png differ
diff --git a/test/reference/rectangle-rounding-error.base.rgb24.ref.png b/test/reference/rectangle-rounding-error.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..6cc1b21
Binary files /dev/null and b/test/reference/rectangle-rounding-error.base.rgb24.ref.png differ
diff --git a/test/reference/rectangle-rounding-error.ref.png b/test/reference/rectangle-rounding-error.ref.png
new file mode 100644 (file)
index 0000000..413345d
Binary files /dev/null and b/test/reference/rectangle-rounding-error.ref.png differ
diff --git a/test/reference/rectangle-rounding-error.traps.argb32.ref.png b/test/reference/rectangle-rounding-error.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..6cc1b21
Binary files /dev/null and b/test/reference/rectangle-rounding-error.traps.argb32.ref.png differ
diff --git a/test/reference/rectangle-rounding-error.traps.rgb24.ref.png b/test/reference/rectangle-rounding-error.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..6cc1b21
Binary files /dev/null and b/test/reference/rectangle-rounding-error.traps.rgb24.ref.png differ
diff --git a/test/reference/rectilinear-dash.base.argb32.ref.png b/test/reference/rectilinear-dash.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..33e7851
Binary files /dev/null and b/test/reference/rectilinear-dash.base.argb32.ref.png differ
diff --git a/test/reference/rectilinear-dash.base.rgb24.ref.png b/test/reference/rectilinear-dash.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..33e7851
Binary files /dev/null and b/test/reference/rectilinear-dash.base.rgb24.ref.png differ
diff --git a/test/reference/rectilinear-dash.quartz.xfail.png b/test/reference/rectilinear-dash.quartz.xfail.png
new file mode 100644 (file)
index 0000000..5101845
Binary files /dev/null and b/test/reference/rectilinear-dash.quartz.xfail.png differ
diff --git a/test/reference/rectilinear-dash.ref.png b/test/reference/rectilinear-dash.ref.png
new file mode 100644 (file)
index 0000000..33e7851
Binary files /dev/null and b/test/reference/rectilinear-dash.ref.png differ
diff --git a/test/reference/rectilinear-dash.traps.argb32.ref.png b/test/reference/rectilinear-dash.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..33e7851
Binary files /dev/null and b/test/reference/rectilinear-dash.traps.argb32.ref.png differ
diff --git a/test/reference/rectilinear-dash.traps.rgb24.ref.png b/test/reference/rectilinear-dash.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..33e7851
Binary files /dev/null and b/test/reference/rectilinear-dash.traps.rgb24.ref.png differ
diff --git a/test/reference/rectilinear-fill.base.argb32.ref.png b/test/reference/rectilinear-fill.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..dbaf383
Binary files /dev/null and b/test/reference/rectilinear-fill.base.argb32.ref.png differ
diff --git a/test/reference/rectilinear-fill.base.rgb24.ref.png b/test/reference/rectilinear-fill.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..dbaf383
Binary files /dev/null and b/test/reference/rectilinear-fill.base.rgb24.ref.png differ
diff --git a/test/reference/rectilinear-fill.ref.png b/test/reference/rectilinear-fill.ref.png
new file mode 100644 (file)
index 0000000..84b5967
Binary files /dev/null and b/test/reference/rectilinear-fill.ref.png differ
diff --git a/test/reference/rectilinear-fill.traps.argb32.ref.png b/test/reference/rectilinear-fill.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..dbaf383
Binary files /dev/null and b/test/reference/rectilinear-fill.traps.argb32.ref.png differ
diff --git a/test/reference/rectilinear-fill.traps.rgb24.ref.png b/test/reference/rectilinear-fill.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..dbaf383
Binary files /dev/null and b/test/reference/rectilinear-fill.traps.rgb24.ref.png differ
diff --git a/test/reference/rectilinear-grid.base.argb32.ref.png b/test/reference/rectilinear-grid.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..7176cb4
Binary files /dev/null and b/test/reference/rectilinear-grid.base.argb32.ref.png differ
diff --git a/test/reference/rectilinear-grid.base.rgb24.ref.png b/test/reference/rectilinear-grid.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..7176cb4
Binary files /dev/null and b/test/reference/rectilinear-grid.base.rgb24.ref.png differ
diff --git a/test/reference/rectilinear-grid.image16.ref.png b/test/reference/rectilinear-grid.image16.ref.png
new file mode 100644 (file)
index 0000000..4d4c4da
Binary files /dev/null and b/test/reference/rectilinear-grid.image16.ref.png differ
diff --git a/test/reference/rectilinear-grid.ref.png b/test/reference/rectilinear-grid.ref.png
new file mode 100644 (file)
index 0000000..8d47ef5
Binary files /dev/null and b/test/reference/rectilinear-grid.ref.png differ
diff --git a/test/reference/rectilinear-grid.traps.argb32.ref.png b/test/reference/rectilinear-grid.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..7176cb4
Binary files /dev/null and b/test/reference/rectilinear-grid.traps.argb32.ref.png differ
diff --git a/test/reference/rectilinear-grid.traps.rgb24.ref.png b/test/reference/rectilinear-grid.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..7176cb4
Binary files /dev/null and b/test/reference/rectilinear-grid.traps.rgb24.ref.png differ
diff --git a/test/reference/rectilinear-miter-limit.base.argb32.ref.png b/test/reference/rectilinear-miter-limit.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..ddf7570
Binary files /dev/null and b/test/reference/rectilinear-miter-limit.base.argb32.ref.png differ
diff --git a/test/reference/rectilinear-miter-limit.base.rgb24.ref.png b/test/reference/rectilinear-miter-limit.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..ddf7570
Binary files /dev/null and b/test/reference/rectilinear-miter-limit.base.rgb24.ref.png differ
diff --git a/test/reference/rectilinear-miter-limit.ps2.ref.png b/test/reference/rectilinear-miter-limit.ps2.ref.png
new file mode 100644 (file)
index 0000000..8213060
Binary files /dev/null and b/test/reference/rectilinear-miter-limit.ps2.ref.png differ
diff --git a/test/reference/rectilinear-miter-limit.ps3.ref.png b/test/reference/rectilinear-miter-limit.ps3.ref.png
new file mode 100644 (file)
index 0000000..8213060
Binary files /dev/null and b/test/reference/rectilinear-miter-limit.ps3.ref.png differ
diff --git a/test/reference/rectilinear-miter-limit.ref.png b/test/reference/rectilinear-miter-limit.ref.png
new file mode 100644 (file)
index 0000000..6264e3e
Binary files /dev/null and b/test/reference/rectilinear-miter-limit.ref.png differ
diff --git a/test/reference/rectilinear-miter-limit.traps.argb32.ref.png b/test/reference/rectilinear-miter-limit.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..ddf7570
Binary files /dev/null and b/test/reference/rectilinear-miter-limit.traps.argb32.ref.png differ
diff --git a/test/reference/rectilinear-miter-limit.traps.rgb24.ref.png b/test/reference/rectilinear-miter-limit.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..ddf7570
Binary files /dev/null and b/test/reference/rectilinear-miter-limit.traps.rgb24.ref.png differ
diff --git a/test/reference/rectilinear-stroke.base.argb32.ref.png b/test/reference/rectilinear-stroke.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..260909b
Binary files /dev/null and b/test/reference/rectilinear-stroke.base.argb32.ref.png differ
diff --git a/test/reference/rectilinear-stroke.base.rgb24.ref.png b/test/reference/rectilinear-stroke.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..260909b
Binary files /dev/null and b/test/reference/rectilinear-stroke.base.rgb24.ref.png differ
diff --git a/test/reference/rectilinear-stroke.quartz.xfail.png b/test/reference/rectilinear-stroke.quartz.xfail.png
new file mode 100644 (file)
index 0000000..e2a508e
Binary files /dev/null and b/test/reference/rectilinear-stroke.quartz.xfail.png differ
diff --git a/test/reference/rectilinear-stroke.ref.png b/test/reference/rectilinear-stroke.ref.png
new file mode 100644 (file)
index 0000000..0a40b0d
Binary files /dev/null and b/test/reference/rectilinear-stroke.ref.png differ
diff --git a/test/reference/rectilinear-stroke.traps.argb32.ref.png b/test/reference/rectilinear-stroke.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..260909b
Binary files /dev/null and b/test/reference/rectilinear-stroke.traps.argb32.ref.png differ
diff --git a/test/reference/rectilinear-stroke.traps.rgb24.ref.png b/test/reference/rectilinear-stroke.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..260909b
Binary files /dev/null and b/test/reference/rectilinear-stroke.traps.rgb24.ref.png differ
diff --git a/test/reference/reflected-stroke.base.argb32.ref.png b/test/reference/reflected-stroke.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..c7112d2
Binary files /dev/null and b/test/reference/reflected-stroke.base.argb32.ref.png differ
diff --git a/test/reference/reflected-stroke.base.rgb24.ref.png b/test/reference/reflected-stroke.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..c7112d2
Binary files /dev/null and b/test/reference/reflected-stroke.base.rgb24.ref.png differ
diff --git a/test/reference/reflected-stroke.image16.ref.png b/test/reference/reflected-stroke.image16.ref.png
new file mode 100644 (file)
index 0000000..ab12737
Binary files /dev/null and b/test/reference/reflected-stroke.image16.ref.png differ
diff --git a/test/reference/reflected-stroke.ps.ref.png b/test/reference/reflected-stroke.ps.ref.png
new file mode 100644 (file)
index 0000000..5a28eef
Binary files /dev/null and b/test/reference/reflected-stroke.ps.ref.png differ
diff --git a/test/reference/reflected-stroke.ref.png b/test/reference/reflected-stroke.ref.png
new file mode 100644 (file)
index 0000000..d8c9c03
Binary files /dev/null and b/test/reference/reflected-stroke.ref.png differ
diff --git a/test/reference/reflected-stroke.traps.argb32.ref.png b/test/reference/reflected-stroke.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..c7112d2
Binary files /dev/null and b/test/reference/reflected-stroke.traps.argb32.ref.png differ
diff --git a/test/reference/reflected-stroke.traps.rgb24.ref.png b/test/reference/reflected-stroke.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..c7112d2
Binary files /dev/null and b/test/reference/reflected-stroke.traps.rgb24.ref.png differ
diff --git a/test/reference/rel-path.base.argb32.ref.png b/test/reference/rel-path.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..5c9cf21
Binary files /dev/null and b/test/reference/rel-path.base.argb32.ref.png differ
diff --git a/test/reference/rel-path.base.rgb24.ref.png b/test/reference/rel-path.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..72e975a
Binary files /dev/null and b/test/reference/rel-path.base.rgb24.ref.png differ
diff --git a/test/reference/rel-path.ps2.rgb24.ref.png b/test/reference/rel-path.ps2.rgb24.ref.png
new file mode 100644 (file)
index 0000000..ccdcebb
Binary files /dev/null and b/test/reference/rel-path.ps2.rgb24.ref.png differ
diff --git a/test/reference/rel-path.ps3.rgb24.ref.png b/test/reference/rel-path.ps3.rgb24.ref.png
new file mode 100644 (file)
index 0000000..ccdcebb
Binary files /dev/null and b/test/reference/rel-path.ps3.rgb24.ref.png differ
diff --git a/test/reference/rel-path.ref.png b/test/reference/rel-path.ref.png
new file mode 100644 (file)
index 0000000..6376cec
Binary files /dev/null and b/test/reference/rel-path.ref.png differ
diff --git a/test/reference/rel-path.rgb24.ref.png b/test/reference/rel-path.rgb24.ref.png
new file mode 100644 (file)
index 0000000..8254eb5
Binary files /dev/null and b/test/reference/rel-path.rgb24.ref.png differ
diff --git a/test/reference/rel-path.traps.argb32.ref.png b/test/reference/rel-path.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..5c9cf21
Binary files /dev/null and b/test/reference/rel-path.traps.argb32.ref.png differ
diff --git a/test/reference/rel-path.traps.rgb24.ref.png b/test/reference/rel-path.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..72e975a
Binary files /dev/null and b/test/reference/rel-path.traps.rgb24.ref.png differ
diff --git a/test/reference/rgb24-ignore-alpha.base.argb32.ref.png b/test/reference/rgb24-ignore-alpha.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..922eddd
Binary files /dev/null and b/test/reference/rgb24-ignore-alpha.base.argb32.ref.png differ
diff --git a/test/reference/rgb24-ignore-alpha.base.rgb24.ref.png b/test/reference/rgb24-ignore-alpha.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..922eddd
Binary files /dev/null and b/test/reference/rgb24-ignore-alpha.base.rgb24.ref.png differ
diff --git a/test/reference/rgb24-ignore-alpha.ref.png b/test/reference/rgb24-ignore-alpha.ref.png
new file mode 100644 (file)
index 0000000..ab1d8fa
Binary files /dev/null and b/test/reference/rgb24-ignore-alpha.ref.png differ
diff --git a/test/reference/rgb24-ignore-alpha.traps.argb32.ref.png b/test/reference/rgb24-ignore-alpha.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..922eddd
Binary files /dev/null and b/test/reference/rgb24-ignore-alpha.traps.argb32.ref.png differ
diff --git a/test/reference/rgb24-ignore-alpha.traps.rgb24.ref.png b/test/reference/rgb24-ignore-alpha.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..922eddd
Binary files /dev/null and b/test/reference/rgb24-ignore-alpha.traps.rgb24.ref.png differ
diff --git a/test/reference/rotate-clip-image-surface-paint.base.argb32.ref.png b/test/reference/rotate-clip-image-surface-paint.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..9d991d9
Binary files /dev/null and b/test/reference/rotate-clip-image-surface-paint.base.argb32.ref.png differ
diff --git a/test/reference/rotate-clip-image-surface-paint.base.rgb24.ref.png b/test/reference/rotate-clip-image-surface-paint.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..9d991d9
Binary files /dev/null and b/test/reference/rotate-clip-image-surface-paint.base.rgb24.ref.png differ
diff --git a/test/reference/rotate-clip-image-surface-paint.ref.png b/test/reference/rotate-clip-image-surface-paint.ref.png
new file mode 100644 (file)
index 0000000..1118adf
Binary files /dev/null and b/test/reference/rotate-clip-image-surface-paint.ref.png differ
diff --git a/test/reference/rotate-clip-image-surface-paint.traps.argb32.ref.png b/test/reference/rotate-clip-image-surface-paint.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..9d991d9
Binary files /dev/null and b/test/reference/rotate-clip-image-surface-paint.traps.argb32.ref.png differ
diff --git a/test/reference/rotate-clip-image-surface-paint.traps.rgb24.ref.png b/test/reference/rotate-clip-image-surface-paint.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..9d991d9
Binary files /dev/null and b/test/reference/rotate-clip-image-surface-paint.traps.rgb24.ref.png differ
diff --git a/test/reference/rotate-image-surface-paint.base.argb32.ref.png b/test/reference/rotate-image-surface-paint.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..06a4820
Binary files /dev/null and b/test/reference/rotate-image-surface-paint.base.argb32.ref.png differ
diff --git a/test/reference/rotate-image-surface-paint.base.rgb24.ref.png b/test/reference/rotate-image-surface-paint.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..06a4820
Binary files /dev/null and b/test/reference/rotate-image-surface-paint.base.rgb24.ref.png differ
diff --git a/test/reference/rotate-image-surface-paint.pdf.xfail.png b/test/reference/rotate-image-surface-paint.pdf.xfail.png
new file mode 100644 (file)
index 0000000..e1892e5
Binary files /dev/null and b/test/reference/rotate-image-surface-paint.pdf.xfail.png differ
diff --git a/test/reference/rotate-image-surface-paint.ps.ref.png b/test/reference/rotate-image-surface-paint.ps.ref.png
new file mode 100644 (file)
index 0000000..4e46364
Binary files /dev/null and b/test/reference/rotate-image-surface-paint.ps.ref.png differ
diff --git a/test/reference/rotate-image-surface-paint.quartz.ref.png b/test/reference/rotate-image-surface-paint.quartz.ref.png
new file mode 100644 (file)
index 0000000..a716b63
Binary files /dev/null and b/test/reference/rotate-image-surface-paint.quartz.ref.png differ
diff --git a/test/reference/rotate-image-surface-paint.ref.png b/test/reference/rotate-image-surface-paint.ref.png
new file mode 100644 (file)
index 0000000..06a4820
Binary files /dev/null and b/test/reference/rotate-image-surface-paint.ref.png differ
diff --git a/test/reference/rotate-image-surface-paint.svg.ref.png b/test/reference/rotate-image-surface-paint.svg.ref.png
new file mode 100644 (file)
index 0000000..e0db245
Binary files /dev/null and b/test/reference/rotate-image-surface-paint.svg.ref.png differ
diff --git a/test/reference/rotate-image-surface-paint.svg.xfail.png b/test/reference/rotate-image-surface-paint.svg.xfail.png
new file mode 100644 (file)
index 0000000..4040784
Binary files /dev/null and b/test/reference/rotate-image-surface-paint.svg.xfail.png differ
diff --git a/test/reference/rotate-image-surface-paint.traps.argb32.ref.png b/test/reference/rotate-image-surface-paint.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..06a4820
Binary files /dev/null and b/test/reference/rotate-image-surface-paint.traps.argb32.ref.png differ
diff --git a/test/reference/rotate-image-surface-paint.traps.rgb24.ref.png b/test/reference/rotate-image-surface-paint.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..06a4820
Binary files /dev/null and b/test/reference/rotate-image-surface-paint.traps.rgb24.ref.png differ
diff --git a/test/reference/rotated-clip.base.argb32.ref.png b/test/reference/rotated-clip.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..ca0f0af
Binary files /dev/null and b/test/reference/rotated-clip.base.argb32.ref.png differ
diff --git a/test/reference/rotated-clip.base.rgb24.ref.png b/test/reference/rotated-clip.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..ca0f0af
Binary files /dev/null and b/test/reference/rotated-clip.base.rgb24.ref.png differ
diff --git a/test/reference/rotated-clip.image16.ref.png b/test/reference/rotated-clip.image16.ref.png
new file mode 100644 (file)
index 0000000..26d9a1a
Binary files /dev/null and b/test/reference/rotated-clip.image16.ref.png differ
diff --git a/test/reference/rotated-clip.ps.ref.png b/test/reference/rotated-clip.ps.ref.png
new file mode 100644 (file)
index 0000000..a2a0ace
Binary files /dev/null and b/test/reference/rotated-clip.ps.ref.png differ
diff --git a/test/reference/rotated-clip.quartz.ref.png b/test/reference/rotated-clip.quartz.ref.png
new file mode 100644 (file)
index 0000000..6282846
Binary files /dev/null and b/test/reference/rotated-clip.quartz.ref.png differ
diff --git a/test/reference/rotated-clip.ref.png b/test/reference/rotated-clip.ref.png
new file mode 100644 (file)
index 0000000..7f3981c
Binary files /dev/null and b/test/reference/rotated-clip.ref.png differ
diff --git a/test/reference/rotated-clip.traps.argb32.ref.png b/test/reference/rotated-clip.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..ca0f0af
Binary files /dev/null and b/test/reference/rotated-clip.traps.argb32.ref.png differ
diff --git a/test/reference/rotated-clip.traps.rgb24.ref.png b/test/reference/rotated-clip.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..ca0f0af
Binary files /dev/null and b/test/reference/rotated-clip.traps.rgb24.ref.png differ
diff --git a/test/reference/rounded-rectangle-fill.base.argb32.ref.png b/test/reference/rounded-rectangle-fill.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..52a355d
Binary files /dev/null and b/test/reference/rounded-rectangle-fill.base.argb32.ref.png differ
diff --git a/test/reference/rounded-rectangle-fill.base.rgb24.ref.png b/test/reference/rounded-rectangle-fill.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..52a355d
Binary files /dev/null and b/test/reference/rounded-rectangle-fill.base.rgb24.ref.png differ
diff --git a/test/reference/rounded-rectangle-fill.image16.ref.png b/test/reference/rounded-rectangle-fill.image16.ref.png
new file mode 100644 (file)
index 0000000..0739e5d
Binary files /dev/null and b/test/reference/rounded-rectangle-fill.image16.ref.png differ
diff --git a/test/reference/rounded-rectangle-fill.ps.ref.png b/test/reference/rounded-rectangle-fill.ps.ref.png
new file mode 100644 (file)
index 0000000..215ad3a
Binary files /dev/null and b/test/reference/rounded-rectangle-fill.ps.ref.png differ
diff --git a/test/reference/rounded-rectangle-fill.quartz.ref.png b/test/reference/rounded-rectangle-fill.quartz.ref.png
new file mode 100644 (file)
index 0000000..ee685ca
Binary files /dev/null and b/test/reference/rounded-rectangle-fill.quartz.ref.png differ
diff --git a/test/reference/rounded-rectangle-fill.ref.png b/test/reference/rounded-rectangle-fill.ref.png
new file mode 100644 (file)
index 0000000..36e74dd
Binary files /dev/null and b/test/reference/rounded-rectangle-fill.ref.png differ
diff --git a/test/reference/rounded-rectangle-fill.traps.argb32.ref.png b/test/reference/rounded-rectangle-fill.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..52a355d
Binary files /dev/null and b/test/reference/rounded-rectangle-fill.traps.argb32.ref.png differ
diff --git a/test/reference/rounded-rectangle-fill.traps.rgb24.ref.png b/test/reference/rounded-rectangle-fill.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..52a355d
Binary files /dev/null and b/test/reference/rounded-rectangle-fill.traps.rgb24.ref.png differ
diff --git a/test/reference/rounded-rectangle-stroke.base.argb32.ref.png b/test/reference/rounded-rectangle-stroke.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..02651dc
Binary files /dev/null and b/test/reference/rounded-rectangle-stroke.base.argb32.ref.png differ
diff --git a/test/reference/rounded-rectangle-stroke.base.rgb24.ref.png b/test/reference/rounded-rectangle-stroke.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..02651dc
Binary files /dev/null and b/test/reference/rounded-rectangle-stroke.base.rgb24.ref.png differ
diff --git a/test/reference/rounded-rectangle-stroke.image16.ref.png b/test/reference/rounded-rectangle-stroke.image16.ref.png
new file mode 100644 (file)
index 0000000..f32a2e0
Binary files /dev/null and b/test/reference/rounded-rectangle-stroke.image16.ref.png differ
diff --git a/test/reference/rounded-rectangle-stroke.ps.ref.png b/test/reference/rounded-rectangle-stroke.ps.ref.png
new file mode 100644 (file)
index 0000000..dd5fc97
Binary files /dev/null and b/test/reference/rounded-rectangle-stroke.ps.ref.png differ
diff --git a/test/reference/rounded-rectangle-stroke.ref.png b/test/reference/rounded-rectangle-stroke.ref.png
new file mode 100644 (file)
index 0000000..b0f1b07
Binary files /dev/null and b/test/reference/rounded-rectangle-stroke.ref.png differ
diff --git a/test/reference/rounded-rectangle-stroke.traps.argb32.ref.png b/test/reference/rounded-rectangle-stroke.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..02651dc
Binary files /dev/null and b/test/reference/rounded-rectangle-stroke.traps.argb32.ref.png differ
diff --git a/test/reference/rounded-rectangle-stroke.traps.rgb24.ref.png b/test/reference/rounded-rectangle-stroke.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..02651dc
Binary files /dev/null and b/test/reference/rounded-rectangle-stroke.traps.rgb24.ref.png differ
diff --git a/test/reference/scale-down-source-surface-paint.base.argb32.ref.png b/test/reference/scale-down-source-surface-paint.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..7477094
Binary files /dev/null and b/test/reference/scale-down-source-surface-paint.base.argb32.ref.png differ
diff --git a/test/reference/scale-down-source-surface-paint.base.rgb24.ref.png b/test/reference/scale-down-source-surface-paint.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..7477094
Binary files /dev/null and b/test/reference/scale-down-source-surface-paint.base.rgb24.ref.png differ
diff --git a/test/reference/scale-down-source-surface-paint.ref.png b/test/reference/scale-down-source-surface-paint.ref.png
new file mode 100644 (file)
index 0000000..5c969d2
Binary files /dev/null and b/test/reference/scale-down-source-surface-paint.ref.png differ
diff --git a/test/reference/scale-down-source-surface-paint.traps.argb32.ref.png b/test/reference/scale-down-source-surface-paint.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..7477094
Binary files /dev/null and b/test/reference/scale-down-source-surface-paint.traps.argb32.ref.png differ
diff --git a/test/reference/scale-down-source-surface-paint.traps.rgb24.ref.png b/test/reference/scale-down-source-surface-paint.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..7477094
Binary files /dev/null and b/test/reference/scale-down-source-surface-paint.traps.rgb24.ref.png differ
diff --git a/test/reference/scale-offset-image.base.argb32.ref.png b/test/reference/scale-offset-image.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..19cd614
Binary files /dev/null and b/test/reference/scale-offset-image.base.argb32.ref.png differ
diff --git a/test/reference/scale-offset-image.base.rgb24.ref.png b/test/reference/scale-offset-image.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..19cd614
Binary files /dev/null and b/test/reference/scale-offset-image.base.rgb24.ref.png differ
diff --git a/test/reference/scale-offset-image.gl.ref.png b/test/reference/scale-offset-image.gl.ref.png
new file mode 100644 (file)
index 0000000..d2a845c
Binary files /dev/null and b/test/reference/scale-offset-image.gl.ref.png differ
diff --git a/test/reference/scale-offset-image.image16.ref.png b/test/reference/scale-offset-image.image16.ref.png
new file mode 100644 (file)
index 0000000..e67949d
Binary files /dev/null and b/test/reference/scale-offset-image.image16.ref.png differ
diff --git a/test/reference/scale-offset-image.pdf.argb32.ref.png b/test/reference/scale-offset-image.pdf.argb32.ref.png
new file mode 100644 (file)
index 0000000..74abfae
Binary files /dev/null and b/test/reference/scale-offset-image.pdf.argb32.ref.png differ
diff --git a/test/reference/scale-offset-image.pdf.rgb24.ref.png b/test/reference/scale-offset-image.pdf.rgb24.ref.png
new file mode 100644 (file)
index 0000000..74abfae
Binary files /dev/null and b/test/reference/scale-offset-image.pdf.rgb24.ref.png differ
diff --git a/test/reference/scale-offset-image.ps.ref.png b/test/reference/scale-offset-image.ps.ref.png
new file mode 100644 (file)
index 0000000..19941f0
Binary files /dev/null and b/test/reference/scale-offset-image.ps.ref.png differ
diff --git a/test/reference/scale-offset-image.quartz.ref.png b/test/reference/scale-offset-image.quartz.ref.png
new file mode 100644 (file)
index 0000000..f7a5e72
Binary files /dev/null and b/test/reference/scale-offset-image.quartz.ref.png differ
diff --git a/test/reference/scale-offset-image.ref.png b/test/reference/scale-offset-image.ref.png
new file mode 100644 (file)
index 0000000..67e1fce
Binary files /dev/null and b/test/reference/scale-offset-image.ref.png differ
diff --git a/test/reference/scale-offset-image.script.xfail.png b/test/reference/scale-offset-image.script.xfail.png
new file mode 100644 (file)
index 0000000..b89bb66
Binary files /dev/null and b/test/reference/scale-offset-image.script.xfail.png differ
diff --git a/test/reference/scale-offset-image.traps.argb32.ref.png b/test/reference/scale-offset-image.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..19cd614
Binary files /dev/null and b/test/reference/scale-offset-image.traps.argb32.ref.png differ
diff --git a/test/reference/scale-offset-image.traps.rgb24.ref.png b/test/reference/scale-offset-image.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..19cd614
Binary files /dev/null and b/test/reference/scale-offset-image.traps.rgb24.ref.png differ
diff --git a/test/reference/scale-offset-image.xfail.png b/test/reference/scale-offset-image.xfail.png
new file mode 100644 (file)
index 0000000..f0db601
Binary files /dev/null and b/test/reference/scale-offset-image.xfail.png differ
diff --git a/test/reference/scale-offset-image.xlib-fallback.xfail.png b/test/reference/scale-offset-image.xlib-fallback.xfail.png
new file mode 100644 (file)
index 0000000..3e09d6f
Binary files /dev/null and b/test/reference/scale-offset-image.xlib-fallback.xfail.png differ
diff --git a/test/reference/scale-offset-similar.base.argb32.ref.png b/test/reference/scale-offset-similar.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..19cd614
Binary files /dev/null and b/test/reference/scale-offset-similar.base.argb32.ref.png differ
diff --git a/test/reference/scale-offset-similar.base.rgb24.ref.png b/test/reference/scale-offset-similar.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..19cd614
Binary files /dev/null and b/test/reference/scale-offset-similar.base.rgb24.ref.png differ
diff --git a/test/reference/scale-offset-similar.gl.ref.png b/test/reference/scale-offset-similar.gl.ref.png
new file mode 100644 (file)
index 0000000..d2a845c
Binary files /dev/null and b/test/reference/scale-offset-similar.gl.ref.png differ
diff --git a/test/reference/scale-offset-similar.image16.ref.png b/test/reference/scale-offset-similar.image16.ref.png
new file mode 100644 (file)
index 0000000..e67949d
Binary files /dev/null and b/test/reference/scale-offset-similar.image16.ref.png differ
diff --git a/test/reference/scale-offset-similar.pdf.argb32.ref.png b/test/reference/scale-offset-similar.pdf.argb32.ref.png
new file mode 100644 (file)
index 0000000..c1e2765
Binary files /dev/null and b/test/reference/scale-offset-similar.pdf.argb32.ref.png differ
diff --git a/test/reference/scale-offset-similar.pdf.rgb24.ref.png b/test/reference/scale-offset-similar.pdf.rgb24.ref.png
new file mode 100644 (file)
index 0000000..c1e2765
Binary files /dev/null and b/test/reference/scale-offset-similar.pdf.rgb24.ref.png differ
diff --git a/test/reference/scale-offset-similar.ps.ref.png b/test/reference/scale-offset-similar.ps.ref.png
new file mode 100644 (file)
index 0000000..8c10d30
Binary files /dev/null and b/test/reference/scale-offset-similar.ps.ref.png differ
diff --git a/test/reference/scale-offset-similar.quartz.ref.png b/test/reference/scale-offset-similar.quartz.ref.png
new file mode 100644 (file)
index 0000000..f7a5e72
Binary files /dev/null and b/test/reference/scale-offset-similar.quartz.ref.png differ
diff --git a/test/reference/scale-offset-similar.recording.xfail.png b/test/reference/scale-offset-similar.recording.xfail.png
new file mode 100644 (file)
index 0000000..0f2553e
Binary files /dev/null and b/test/reference/scale-offset-similar.recording.xfail.png differ
diff --git a/test/reference/scale-offset-similar.ref.png b/test/reference/scale-offset-similar.ref.png
new file mode 100644 (file)
index 0000000..67e1fce
Binary files /dev/null and b/test/reference/scale-offset-similar.ref.png differ
diff --git a/test/reference/scale-offset-similar.script.xfail.png b/test/reference/scale-offset-similar.script.xfail.png
new file mode 100644 (file)
index 0000000..b89bb66
Binary files /dev/null and b/test/reference/scale-offset-similar.script.xfail.png differ
diff --git a/test/reference/scale-offset-similar.traps.argb32.ref.png b/test/reference/scale-offset-similar.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..19cd614
Binary files /dev/null and b/test/reference/scale-offset-similar.traps.argb32.ref.png differ
diff --git a/test/reference/scale-offset-similar.traps.rgb24.ref.png b/test/reference/scale-offset-similar.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..19cd614
Binary files /dev/null and b/test/reference/scale-offset-similar.traps.rgb24.ref.png differ
diff --git a/test/reference/scale-offset-similar.xfail.png b/test/reference/scale-offset-similar.xfail.png
new file mode 100644 (file)
index 0000000..f0db601
Binary files /dev/null and b/test/reference/scale-offset-similar.xfail.png differ
diff --git a/test/reference/scale-offset-similar.xlib-fallback.xfail.png b/test/reference/scale-offset-similar.xlib-fallback.xfail.png
new file mode 100644 (file)
index 0000000..3e09d6f
Binary files /dev/null and b/test/reference/scale-offset-similar.xlib-fallback.xfail.png differ
diff --git a/test/reference/scale-offset-similar.xlib.xfail.png b/test/reference/scale-offset-similar.xlib.xfail.png
new file mode 100644 (file)
index 0000000..eb48516
Binary files /dev/null and b/test/reference/scale-offset-similar.xlib.xfail.png differ
diff --git a/test/reference/scale-source-surface-paint.base.argb32.ref.png b/test/reference/scale-source-surface-paint.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..e159d15
Binary files /dev/null and b/test/reference/scale-source-surface-paint.base.argb32.ref.png differ
diff --git a/test/reference/scale-source-surface-paint.base.rgb24.ref.png b/test/reference/scale-source-surface-paint.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..3491a7c
Binary files /dev/null and b/test/reference/scale-source-surface-paint.base.rgb24.ref.png differ
diff --git a/test/reference/scale-source-surface-paint.pdf.argb32.xfail.png b/test/reference/scale-source-surface-paint.pdf.argb32.xfail.png
new file mode 100644 (file)
index 0000000..7ecac17
Binary files /dev/null and b/test/reference/scale-source-surface-paint.pdf.argb32.xfail.png differ
diff --git a/test/reference/scale-source-surface-paint.pdf.rgb24.xfail.png b/test/reference/scale-source-surface-paint.pdf.rgb24.xfail.png
new file mode 100644 (file)
index 0000000..fa1291f
Binary files /dev/null and b/test/reference/scale-source-surface-paint.pdf.rgb24.xfail.png differ
diff --git a/test/reference/scale-source-surface-paint.ref.png b/test/reference/scale-source-surface-paint.ref.png
new file mode 100644 (file)
index 0000000..ec3c059
Binary files /dev/null and b/test/reference/scale-source-surface-paint.ref.png differ
diff --git a/test/reference/scale-source-surface-paint.rgb24.ref.png b/test/reference/scale-source-surface-paint.rgb24.ref.png
new file mode 100644 (file)
index 0000000..b9e2f43
Binary files /dev/null and b/test/reference/scale-source-surface-paint.rgb24.ref.png differ
diff --git a/test/reference/scale-source-surface-paint.svg.argb32.xfail.png b/test/reference/scale-source-surface-paint.svg.argb32.xfail.png
new file mode 100644 (file)
index 0000000..ed946d4
Binary files /dev/null and b/test/reference/scale-source-surface-paint.svg.argb32.xfail.png differ
diff --git a/test/reference/scale-source-surface-paint.svg.rgb24.xfail.png b/test/reference/scale-source-surface-paint.svg.rgb24.xfail.png
new file mode 100644 (file)
index 0000000..7d065d4
Binary files /dev/null and b/test/reference/scale-source-surface-paint.svg.rgb24.xfail.png differ
diff --git a/test/reference/scale-source-surface-paint.traps.argb32.ref.png b/test/reference/scale-source-surface-paint.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..e159d15
Binary files /dev/null and b/test/reference/scale-source-surface-paint.traps.argb32.ref.png differ
diff --git a/test/reference/scale-source-surface-paint.traps.rgb24.ref.png b/test/reference/scale-source-surface-paint.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..3491a7c
Binary files /dev/null and b/test/reference/scale-source-surface-paint.traps.rgb24.ref.png differ
diff --git a/test/reference/select-font-face.base.argb32.ref.png b/test/reference/select-font-face.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..be89d71
Binary files /dev/null and b/test/reference/select-font-face.base.argb32.ref.png differ
diff --git a/test/reference/select-font-face.base.rgb24.ref.png b/test/reference/select-font-face.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..be89d71
Binary files /dev/null and b/test/reference/select-font-face.base.rgb24.ref.png differ
diff --git a/test/reference/select-font-face.image16.ref.png b/test/reference/select-font-face.image16.ref.png
new file mode 100644 (file)
index 0000000..2c3191c
Binary files /dev/null and b/test/reference/select-font-face.image16.ref.png differ
diff --git a/test/reference/select-font-face.ps2.ref.png b/test/reference/select-font-face.ps2.ref.png
new file mode 100644 (file)
index 0000000..6c2f361
Binary files /dev/null and b/test/reference/select-font-face.ps2.ref.png differ
diff --git a/test/reference/select-font-face.ps3.ref.png b/test/reference/select-font-face.ps3.ref.png
new file mode 100644 (file)
index 0000000..6c2f361
Binary files /dev/null and b/test/reference/select-font-face.ps3.ref.png differ
diff --git a/test/reference/select-font-face.quartz.ref.png b/test/reference/select-font-face.quartz.ref.png
new file mode 100644 (file)
index 0000000..69fd2e2
Binary files /dev/null and b/test/reference/select-font-face.quartz.ref.png differ
diff --git a/test/reference/select-font-face.ref.png b/test/reference/select-font-face.ref.png
new file mode 100644 (file)
index 0000000..be89d71
Binary files /dev/null and b/test/reference/select-font-face.ref.png differ
diff --git a/test/reference/select-font-face.traps.argb32.ref.png b/test/reference/select-font-face.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..be89d71
Binary files /dev/null and b/test/reference/select-font-face.traps.argb32.ref.png differ
diff --git a/test/reference/select-font-face.traps.rgb24.ref.png b/test/reference/select-font-face.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..be89d71
Binary files /dev/null and b/test/reference/select-font-face.traps.rgb24.ref.png differ
diff --git a/test/reference/self-copy-overlap.base.argb32.ref.png b/test/reference/self-copy-overlap.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..a70e819
Binary files /dev/null and b/test/reference/self-copy-overlap.base.argb32.ref.png differ
diff --git a/test/reference/self-copy-overlap.base.rgb24.ref.png b/test/reference/self-copy-overlap.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..e9cd7b5
Binary files /dev/null and b/test/reference/self-copy-overlap.base.rgb24.ref.png differ
diff --git a/test/reference/self-copy-overlap.traps.argb32.ref.png b/test/reference/self-copy-overlap.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..a70e819
Binary files /dev/null and b/test/reference/self-copy-overlap.traps.argb32.ref.png differ
diff --git a/test/reference/self-copy-overlap.traps.rgb24.ref.png b/test/reference/self-copy-overlap.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..e9cd7b5
Binary files /dev/null and b/test/reference/self-copy-overlap.traps.rgb24.ref.png differ
diff --git a/test/reference/self-copy.base.argb32.ref.png b/test/reference/self-copy.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..daa4dcb
Binary files /dev/null and b/test/reference/self-copy.base.argb32.ref.png differ
diff --git a/test/reference/self-copy.base.rgb24.ref.png b/test/reference/self-copy.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..daa4dcb
Binary files /dev/null and b/test/reference/self-copy.base.rgb24.ref.png differ
diff --git a/test/reference/self-copy.ps2.ref.png b/test/reference/self-copy.ps2.ref.png
new file mode 100644 (file)
index 0000000..5c9dd57
Binary files /dev/null and b/test/reference/self-copy.ps2.ref.png differ
diff --git a/test/reference/self-copy.ps3.ref.png b/test/reference/self-copy.ps3.ref.png
new file mode 100644 (file)
index 0000000..5c9dd57
Binary files /dev/null and b/test/reference/self-copy.ps3.ref.png differ
diff --git a/test/reference/self-copy.ref.png b/test/reference/self-copy.ref.png
new file mode 100644 (file)
index 0000000..e6baaa5
Binary files /dev/null and b/test/reference/self-copy.ref.png differ
diff --git a/test/reference/self-copy.traps.argb32.ref.png b/test/reference/self-copy.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..daa4dcb
Binary files /dev/null and b/test/reference/self-copy.traps.argb32.ref.png differ
diff --git a/test/reference/self-copy.traps.rgb24.ref.png b/test/reference/self-copy.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..daa4dcb
Binary files /dev/null and b/test/reference/self-copy.traps.rgb24.ref.png differ
diff --git a/test/reference/self-intersecting.base.argb32.ref.png b/test/reference/self-intersecting.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..f0068e6
Binary files /dev/null and b/test/reference/self-intersecting.base.argb32.ref.png differ
diff --git a/test/reference/self-intersecting.base.rgb24.ref.png b/test/reference/self-intersecting.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..f0068e6
Binary files /dev/null and b/test/reference/self-intersecting.base.rgb24.ref.png differ
diff --git a/test/reference/self-intersecting.ps.ref.png b/test/reference/self-intersecting.ps.ref.png
new file mode 100644 (file)
index 0000000..84fde01
Binary files /dev/null and b/test/reference/self-intersecting.ps.ref.png differ
diff --git a/test/reference/self-intersecting.quartz.xfail.png b/test/reference/self-intersecting.quartz.xfail.png
new file mode 100644 (file)
index 0000000..4d08713
Binary files /dev/null and b/test/reference/self-intersecting.quartz.xfail.png differ
diff --git a/test/reference/self-intersecting.ref.png b/test/reference/self-intersecting.ref.png
new file mode 100644 (file)
index 0000000..d554d83
Binary files /dev/null and b/test/reference/self-intersecting.ref.png differ
diff --git a/test/reference/self-intersecting.traps.argb32.ref.png b/test/reference/self-intersecting.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..f0068e6
Binary files /dev/null and b/test/reference/self-intersecting.traps.argb32.ref.png differ
diff --git a/test/reference/self-intersecting.traps.rgb24.ref.png b/test/reference/self-intersecting.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..f0068e6
Binary files /dev/null and b/test/reference/self-intersecting.traps.rgb24.ref.png differ
diff --git a/test/reference/set-source.base.argb32.ref.png b/test/reference/set-source.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..222b372
Binary files /dev/null and b/test/reference/set-source.base.argb32.ref.png differ
diff --git a/test/reference/set-source.base.rgb24.ref.png b/test/reference/set-source.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..5e13c82
Binary files /dev/null and b/test/reference/set-source.base.rgb24.ref.png differ
diff --git a/test/reference/set-source.ref.png b/test/reference/set-source.ref.png
new file mode 100644 (file)
index 0000000..19793e0
Binary files /dev/null and b/test/reference/set-source.ref.png differ
diff --git a/test/reference/set-source.rgb24.ref.png b/test/reference/set-source.rgb24.ref.png
new file mode 100644 (file)
index 0000000..b158c74
Binary files /dev/null and b/test/reference/set-source.rgb24.ref.png differ
diff --git a/test/reference/set-source.traps.argb32.ref.png b/test/reference/set-source.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..222b372
Binary files /dev/null and b/test/reference/set-source.traps.argb32.ref.png differ
diff --git a/test/reference/set-source.traps.rgb24.ref.png b/test/reference/set-source.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..5e13c82
Binary files /dev/null and b/test/reference/set-source.traps.rgb24.ref.png differ
diff --git a/test/reference/shape-general-convex.base.argb32.ref.png b/test/reference/shape-general-convex.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..fc2d3f0
Binary files /dev/null and b/test/reference/shape-general-convex.base.argb32.ref.png differ
diff --git a/test/reference/shape-general-convex.base.rgb24.ref.png b/test/reference/shape-general-convex.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..fc2d3f0
Binary files /dev/null and b/test/reference/shape-general-convex.base.rgb24.ref.png differ
diff --git a/test/reference/shape-general-convex.ps.ref.png b/test/reference/shape-general-convex.ps.ref.png
new file mode 100644 (file)
index 0000000..6cdd615
Binary files /dev/null and b/test/reference/shape-general-convex.ps.ref.png differ
diff --git a/test/reference/shape-general-convex.ref.png b/test/reference/shape-general-convex.ref.png
new file mode 100644 (file)
index 0000000..2b296ce
Binary files /dev/null and b/test/reference/shape-general-convex.ref.png differ
diff --git a/test/reference/shape-general-convex.traps.argb32.ref.png b/test/reference/shape-general-convex.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..fc2d3f0
Binary files /dev/null and b/test/reference/shape-general-convex.traps.argb32.ref.png differ
diff --git a/test/reference/shape-general-convex.traps.rgb24.ref.png b/test/reference/shape-general-convex.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..fc2d3f0
Binary files /dev/null and b/test/reference/shape-general-convex.traps.rgb24.ref.png differ
diff --git a/test/reference/shape-sierpinski.base.argb32.ref.png b/test/reference/shape-sierpinski.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..69755d2
Binary files /dev/null and b/test/reference/shape-sierpinski.base.argb32.ref.png differ
diff --git a/test/reference/shape-sierpinski.base.rgb24.ref.png b/test/reference/shape-sierpinski.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..69755d2
Binary files /dev/null and b/test/reference/shape-sierpinski.base.rgb24.ref.png differ
diff --git a/test/reference/shape-sierpinski.pdf.argb32.ref.png b/test/reference/shape-sierpinski.pdf.argb32.ref.png
new file mode 100644 (file)
index 0000000..4e70fbd
Binary files /dev/null and b/test/reference/shape-sierpinski.pdf.argb32.ref.png differ
diff --git a/test/reference/shape-sierpinski.pdf.rgb24.ref.png b/test/reference/shape-sierpinski.pdf.rgb24.ref.png
new file mode 100644 (file)
index 0000000..4e70fbd
Binary files /dev/null and b/test/reference/shape-sierpinski.pdf.rgb24.ref.png differ
diff --git a/test/reference/shape-sierpinski.ps.ref.png b/test/reference/shape-sierpinski.ps.ref.png
new file mode 100644 (file)
index 0000000..700c9f1
Binary files /dev/null and b/test/reference/shape-sierpinski.ps.ref.png differ
diff --git a/test/reference/shape-sierpinski.ps3.argb32.ref.png b/test/reference/shape-sierpinski.ps3.argb32.ref.png
new file mode 100644 (file)
index 0000000..700c9f1
Binary files /dev/null and b/test/reference/shape-sierpinski.ps3.argb32.ref.png differ
diff --git a/test/reference/shape-sierpinski.ps3.rgb24.ref.png b/test/reference/shape-sierpinski.ps3.rgb24.ref.png
new file mode 100644 (file)
index 0000000..700c9f1
Binary files /dev/null and b/test/reference/shape-sierpinski.ps3.rgb24.ref.png differ
diff --git a/test/reference/shape-sierpinski.ref.png b/test/reference/shape-sierpinski.ref.png
new file mode 100644 (file)
index 0000000..938e5c4
Binary files /dev/null and b/test/reference/shape-sierpinski.ref.png differ
diff --git a/test/reference/shape-sierpinski.traps.argb32.ref.png b/test/reference/shape-sierpinski.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..69755d2
Binary files /dev/null and b/test/reference/shape-sierpinski.traps.argb32.ref.png differ
diff --git a/test/reference/shape-sierpinski.traps.rgb24.ref.png b/test/reference/shape-sierpinski.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..69755d2
Binary files /dev/null and b/test/reference/shape-sierpinski.traps.rgb24.ref.png differ
diff --git a/test/reference/show-glyphs-advance.base.argb32.ref.png b/test/reference/show-glyphs-advance.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..e784099
Binary files /dev/null and b/test/reference/show-glyphs-advance.base.argb32.ref.png differ
diff --git a/test/reference/show-glyphs-advance.base.rgb24.ref.png b/test/reference/show-glyphs-advance.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..e784099
Binary files /dev/null and b/test/reference/show-glyphs-advance.base.rgb24.ref.png differ
diff --git a/test/reference/show-glyphs-advance.image16.ref.png b/test/reference/show-glyphs-advance.image16.ref.png
new file mode 100644 (file)
index 0000000..dd2f18d
Binary files /dev/null and b/test/reference/show-glyphs-advance.image16.ref.png differ
diff --git a/test/reference/show-glyphs-advance.ps.ref.png b/test/reference/show-glyphs-advance.ps.ref.png
new file mode 100644 (file)
index 0000000..96a80f9
Binary files /dev/null and b/test/reference/show-glyphs-advance.ps.ref.png differ
diff --git a/test/reference/show-glyphs-advance.quartz.ref.png b/test/reference/show-glyphs-advance.quartz.ref.png
new file mode 100644 (file)
index 0000000..4750308
Binary files /dev/null and b/test/reference/show-glyphs-advance.quartz.ref.png differ
diff --git a/test/reference/show-glyphs-advance.ref.png b/test/reference/show-glyphs-advance.ref.png
new file mode 100644 (file)
index 0000000..e784099
Binary files /dev/null and b/test/reference/show-glyphs-advance.ref.png differ
diff --git a/test/reference/show-glyphs-advance.svg.ref.png b/test/reference/show-glyphs-advance.svg.ref.png
new file mode 100644 (file)
index 0000000..914d4d6
Binary files /dev/null and b/test/reference/show-glyphs-advance.svg.ref.png differ
diff --git a/test/reference/show-glyphs-advance.traps.argb32.ref.png b/test/reference/show-glyphs-advance.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..e784099
Binary files /dev/null and b/test/reference/show-glyphs-advance.traps.argb32.ref.png differ
diff --git a/test/reference/show-glyphs-advance.traps.rgb24.ref.png b/test/reference/show-glyphs-advance.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..e784099
Binary files /dev/null and b/test/reference/show-glyphs-advance.traps.rgb24.ref.png differ
diff --git a/test/reference/show-glyphs-many.base.argb32.ref.png b/test/reference/show-glyphs-many.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..b638015
Binary files /dev/null and b/test/reference/show-glyphs-many.base.argb32.ref.png differ
diff --git a/test/reference/show-glyphs-many.base.rgb24.ref.png b/test/reference/show-glyphs-many.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..b638015
Binary files /dev/null and b/test/reference/show-glyphs-many.base.rgb24.ref.png differ
diff --git a/test/reference/show-glyphs-many.ref.png b/test/reference/show-glyphs-many.ref.png
new file mode 100644 (file)
index 0000000..b61c5f7
Binary files /dev/null and b/test/reference/show-glyphs-many.ref.png differ
diff --git a/test/reference/show-glyphs-many.traps.argb32.ref.png b/test/reference/show-glyphs-many.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..b638015
Binary files /dev/null and b/test/reference/show-glyphs-many.traps.argb32.ref.png differ
diff --git a/test/reference/show-glyphs-many.traps.rgb24.ref.png b/test/reference/show-glyphs-many.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..b638015
Binary files /dev/null and b/test/reference/show-glyphs-many.traps.rgb24.ref.png differ
diff --git a/test/reference/show-text-current-point.base.argb32.ref.png b/test/reference/show-text-current-point.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..b2ee5b2
Binary files /dev/null and b/test/reference/show-text-current-point.base.argb32.ref.png differ
diff --git a/test/reference/show-text-current-point.base.rgb24.ref.png b/test/reference/show-text-current-point.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..b2ee5b2
Binary files /dev/null and b/test/reference/show-text-current-point.base.rgb24.ref.png differ
diff --git a/test/reference/show-text-current-point.image16.ref.png b/test/reference/show-text-current-point.image16.ref.png
new file mode 100644 (file)
index 0000000..b2b933f
Binary files /dev/null and b/test/reference/show-text-current-point.image16.ref.png differ
diff --git a/test/reference/show-text-current-point.ps2.ref.png b/test/reference/show-text-current-point.ps2.ref.png
new file mode 100644 (file)
index 0000000..b42c48e
Binary files /dev/null and b/test/reference/show-text-current-point.ps2.ref.png differ
diff --git a/test/reference/show-text-current-point.ps3.ref.png b/test/reference/show-text-current-point.ps3.ref.png
new file mode 100644 (file)
index 0000000..b42c48e
Binary files /dev/null and b/test/reference/show-text-current-point.ps3.ref.png differ
diff --git a/test/reference/show-text-current-point.quartz.ref.png b/test/reference/show-text-current-point.quartz.ref.png
new file mode 100644 (file)
index 0000000..a531381
Binary files /dev/null and b/test/reference/show-text-current-point.quartz.ref.png differ
diff --git a/test/reference/show-text-current-point.ref.png b/test/reference/show-text-current-point.ref.png
new file mode 100644 (file)
index 0000000..b2ee5b2
Binary files /dev/null and b/test/reference/show-text-current-point.ref.png differ
diff --git a/test/reference/show-text-current-point.traps.argb32.ref.png b/test/reference/show-text-current-point.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..b2ee5b2
Binary files /dev/null and b/test/reference/show-text-current-point.traps.argb32.ref.png differ
diff --git a/test/reference/show-text-current-point.traps.rgb24.ref.png b/test/reference/show-text-current-point.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..b2ee5b2
Binary files /dev/null and b/test/reference/show-text-current-point.traps.rgb24.ref.png differ
diff --git a/test/reference/skew-extreme.base.argb32.ref.png b/test/reference/skew-extreme.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..5e00606
Binary files /dev/null and b/test/reference/skew-extreme.base.argb32.ref.png differ
diff --git a/test/reference/skew-extreme.base.rgb24.ref.png b/test/reference/skew-extreme.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..5e00606
Binary files /dev/null and b/test/reference/skew-extreme.base.rgb24.ref.png differ
diff --git a/test/reference/skew-extreme.ps2.ref.png b/test/reference/skew-extreme.ps2.ref.png
new file mode 100644 (file)
index 0000000..69f1d37
Binary files /dev/null and b/test/reference/skew-extreme.ps2.ref.png differ
diff --git a/test/reference/skew-extreme.ps3.ref.png b/test/reference/skew-extreme.ps3.ref.png
new file mode 100644 (file)
index 0000000..69f1d37
Binary files /dev/null and b/test/reference/skew-extreme.ps3.ref.png differ
diff --git a/test/reference/skew-extreme.ref.png b/test/reference/skew-extreme.ref.png
new file mode 100644 (file)
index 0000000..23d58d5
Binary files /dev/null and b/test/reference/skew-extreme.ref.png differ
diff --git a/test/reference/skew-extreme.traps.argb32.ref.png b/test/reference/skew-extreme.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..5e00606
Binary files /dev/null and b/test/reference/skew-extreme.traps.argb32.ref.png differ
diff --git a/test/reference/skew-extreme.traps.rgb24.ref.png b/test/reference/skew-extreme.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..5e00606
Binary files /dev/null and b/test/reference/skew-extreme.traps.rgb24.ref.png differ
diff --git a/test/reference/smask-fill.base.argb32.ref.png b/test/reference/smask-fill.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..30bc98e
Binary files /dev/null and b/test/reference/smask-fill.base.argb32.ref.png differ
diff --git a/test/reference/smask-fill.base.rgb24.ref.png b/test/reference/smask-fill.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..30bc98e
Binary files /dev/null and b/test/reference/smask-fill.base.rgb24.ref.png differ
diff --git a/test/reference/smask-fill.image16.ref.png b/test/reference/smask-fill.image16.ref.png
new file mode 100644 (file)
index 0000000..25a6d5b
Binary files /dev/null and b/test/reference/smask-fill.image16.ref.png differ
diff --git a/test/reference/smask-fill.pdf.ref.png b/test/reference/smask-fill.pdf.ref.png
new file mode 100644 (file)
index 0000000..cfd40b0
Binary files /dev/null and b/test/reference/smask-fill.pdf.ref.png differ
diff --git a/test/reference/smask-fill.quartz.ref.png b/test/reference/smask-fill.quartz.ref.png
new file mode 100644 (file)
index 0000000..ae05476
Binary files /dev/null and b/test/reference/smask-fill.quartz.ref.png differ
diff --git a/test/reference/smask-fill.ref.png b/test/reference/smask-fill.ref.png
new file mode 100644 (file)
index 0000000..d21d010
Binary files /dev/null and b/test/reference/smask-fill.ref.png differ
diff --git a/test/reference/smask-fill.svg.ref.png b/test/reference/smask-fill.svg.ref.png
new file mode 100644 (file)
index 0000000..824e8cf
Binary files /dev/null and b/test/reference/smask-fill.svg.ref.png differ
diff --git a/test/reference/smask-fill.traps.argb32.ref.png b/test/reference/smask-fill.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..30bc98e
Binary files /dev/null and b/test/reference/smask-fill.traps.argb32.ref.png differ
diff --git a/test/reference/smask-fill.traps.rgb24.ref.png b/test/reference/smask-fill.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..30bc98e
Binary files /dev/null and b/test/reference/smask-fill.traps.rgb24.ref.png differ
diff --git a/test/reference/smask-image-mask.base.argb32.ref.png b/test/reference/smask-image-mask.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..12063bd
Binary files /dev/null and b/test/reference/smask-image-mask.base.argb32.ref.png differ
diff --git a/test/reference/smask-image-mask.base.rgb24.ref.png b/test/reference/smask-image-mask.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..12063bd
Binary files /dev/null and b/test/reference/smask-image-mask.base.rgb24.ref.png differ
diff --git a/test/reference/smask-image-mask.pdf.ref.png b/test/reference/smask-image-mask.pdf.ref.png
new file mode 100644 (file)
index 0000000..7ac43e4
Binary files /dev/null and b/test/reference/smask-image-mask.pdf.ref.png differ
diff --git a/test/reference/smask-image-mask.ref.png b/test/reference/smask-image-mask.ref.png
new file mode 100644 (file)
index 0000000..858b209
Binary files /dev/null and b/test/reference/smask-image-mask.ref.png differ
diff --git a/test/reference/smask-image-mask.traps.argb32.ref.png b/test/reference/smask-image-mask.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..12063bd
Binary files /dev/null and b/test/reference/smask-image-mask.traps.argb32.ref.png differ
diff --git a/test/reference/smask-image-mask.traps.rgb24.ref.png b/test/reference/smask-image-mask.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..12063bd
Binary files /dev/null and b/test/reference/smask-image-mask.traps.rgb24.ref.png differ
diff --git a/test/reference/smask-mask.base.argb32.ref.png b/test/reference/smask-mask.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..80329c2
Binary files /dev/null and b/test/reference/smask-mask.base.argb32.ref.png differ
diff --git a/test/reference/smask-mask.base.rgb24.ref.png b/test/reference/smask-mask.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..80329c2
Binary files /dev/null and b/test/reference/smask-mask.base.rgb24.ref.png differ
diff --git a/test/reference/smask-mask.image16.ref.png b/test/reference/smask-mask.image16.ref.png
new file mode 100644 (file)
index 0000000..5024521
Binary files /dev/null and b/test/reference/smask-mask.image16.ref.png differ
diff --git a/test/reference/smask-mask.pdf.ref.png b/test/reference/smask-mask.pdf.ref.png
new file mode 100644 (file)
index 0000000..59c9740
Binary files /dev/null and b/test/reference/smask-mask.pdf.ref.png differ
diff --git a/test/reference/smask-mask.quartz.ref.png b/test/reference/smask-mask.quartz.ref.png
new file mode 100644 (file)
index 0000000..98ba299
Binary files /dev/null and b/test/reference/smask-mask.quartz.ref.png differ
diff --git a/test/reference/smask-mask.ref.png b/test/reference/smask-mask.ref.png
new file mode 100644 (file)
index 0000000..eabd2d6
Binary files /dev/null and b/test/reference/smask-mask.ref.png differ
diff --git a/test/reference/smask-mask.svg.ref.png b/test/reference/smask-mask.svg.ref.png
new file mode 100644 (file)
index 0000000..ae46036
Binary files /dev/null and b/test/reference/smask-mask.svg.ref.png differ
diff --git a/test/reference/smask-mask.traps.argb32.ref.png b/test/reference/smask-mask.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..80329c2
Binary files /dev/null and b/test/reference/smask-mask.traps.argb32.ref.png differ
diff --git a/test/reference/smask-mask.traps.rgb24.ref.png b/test/reference/smask-mask.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..80329c2
Binary files /dev/null and b/test/reference/smask-mask.traps.rgb24.ref.png differ
diff --git a/test/reference/smask-paint.base.argb32.ref.png b/test/reference/smask-paint.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..95af29a
Binary files /dev/null and b/test/reference/smask-paint.base.argb32.ref.png differ
diff --git a/test/reference/smask-paint.base.rgb24.ref.png b/test/reference/smask-paint.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..95af29a
Binary files /dev/null and b/test/reference/smask-paint.base.rgb24.ref.png differ
diff --git a/test/reference/smask-paint.image16.ref.png b/test/reference/smask-paint.image16.ref.png
new file mode 100644 (file)
index 0000000..dc371df
Binary files /dev/null and b/test/reference/smask-paint.image16.ref.png differ
diff --git a/test/reference/smask-paint.pdf.ref.png b/test/reference/smask-paint.pdf.ref.png
new file mode 100644 (file)
index 0000000..623a92d
Binary files /dev/null and b/test/reference/smask-paint.pdf.ref.png differ
diff --git a/test/reference/smask-paint.quartz.ref.png b/test/reference/smask-paint.quartz.ref.png
new file mode 100644 (file)
index 0000000..4ee25fd
Binary files /dev/null and b/test/reference/smask-paint.quartz.ref.png differ
diff --git a/test/reference/smask-paint.ref.png b/test/reference/smask-paint.ref.png
new file mode 100644 (file)
index 0000000..4381209
Binary files /dev/null and b/test/reference/smask-paint.ref.png differ
diff --git a/test/reference/smask-paint.svg.ref.png b/test/reference/smask-paint.svg.ref.png
new file mode 100644 (file)
index 0000000..93a423f
Binary files /dev/null and b/test/reference/smask-paint.svg.ref.png differ
diff --git a/test/reference/smask-paint.traps.argb32.ref.png b/test/reference/smask-paint.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..95af29a
Binary files /dev/null and b/test/reference/smask-paint.traps.argb32.ref.png differ
diff --git a/test/reference/smask-paint.traps.rgb24.ref.png b/test/reference/smask-paint.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..95af29a
Binary files /dev/null and b/test/reference/smask-paint.traps.rgb24.ref.png differ
diff --git a/test/reference/smask-stroke.base.argb32.ref.png b/test/reference/smask-stroke.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..c6a557d
Binary files /dev/null and b/test/reference/smask-stroke.base.argb32.ref.png differ
diff --git a/test/reference/smask-stroke.base.rgb24.ref.png b/test/reference/smask-stroke.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..c6a557d
Binary files /dev/null and b/test/reference/smask-stroke.base.rgb24.ref.png differ
diff --git a/test/reference/smask-stroke.image16.ref.png b/test/reference/smask-stroke.image16.ref.png
new file mode 100644 (file)
index 0000000..ea2138a
Binary files /dev/null and b/test/reference/smask-stroke.image16.ref.png differ
diff --git a/test/reference/smask-stroke.pdf.xfail.png b/test/reference/smask-stroke.pdf.xfail.png
new file mode 100644 (file)
index 0000000..9a1a771
Binary files /dev/null and b/test/reference/smask-stroke.pdf.xfail.png differ
diff --git a/test/reference/smask-stroke.quartz.ref.png b/test/reference/smask-stroke.quartz.ref.png
new file mode 100644 (file)
index 0000000..f6f0d1b
Binary files /dev/null and b/test/reference/smask-stroke.quartz.ref.png differ
diff --git a/test/reference/smask-stroke.ref.png b/test/reference/smask-stroke.ref.png
new file mode 100644 (file)
index 0000000..d36f0ac
Binary files /dev/null and b/test/reference/smask-stroke.ref.png differ
diff --git a/test/reference/smask-stroke.traps.argb32.ref.png b/test/reference/smask-stroke.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..c6a557d
Binary files /dev/null and b/test/reference/smask-stroke.traps.argb32.ref.png differ
diff --git a/test/reference/smask-stroke.traps.rgb24.ref.png b/test/reference/smask-stroke.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..c6a557d
Binary files /dev/null and b/test/reference/smask-stroke.traps.rgb24.ref.png differ
diff --git a/test/reference/smask-text.base.argb32.ref.png b/test/reference/smask-text.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..871eaee
Binary files /dev/null and b/test/reference/smask-text.base.argb32.ref.png differ
diff --git a/test/reference/smask-text.base.rgb24.ref.png b/test/reference/smask-text.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..871eaee
Binary files /dev/null and b/test/reference/smask-text.base.rgb24.ref.png differ
diff --git a/test/reference/smask-text.image16.ref.png b/test/reference/smask-text.image16.ref.png
new file mode 100644 (file)
index 0000000..098043d
Binary files /dev/null and b/test/reference/smask-text.image16.ref.png differ
diff --git a/test/reference/smask-text.pdf.ref.png b/test/reference/smask-text.pdf.ref.png
new file mode 100644 (file)
index 0000000..fa49056
Binary files /dev/null and b/test/reference/smask-text.pdf.ref.png differ
diff --git a/test/reference/smask-text.ps2.ref.png b/test/reference/smask-text.ps2.ref.png
new file mode 100644 (file)
index 0000000..ae61325
Binary files /dev/null and b/test/reference/smask-text.ps2.ref.png differ
diff --git a/test/reference/smask-text.ps3.ref.png b/test/reference/smask-text.ps3.ref.png
new file mode 100644 (file)
index 0000000..ae61325
Binary files /dev/null and b/test/reference/smask-text.ps3.ref.png differ
diff --git a/test/reference/smask-text.quartz.ref.png b/test/reference/smask-text.quartz.ref.png
new file mode 100644 (file)
index 0000000..096dc98
Binary files /dev/null and b/test/reference/smask-text.quartz.ref.png differ
diff --git a/test/reference/smask-text.ref.png b/test/reference/smask-text.ref.png
new file mode 100644 (file)
index 0000000..c570259
Binary files /dev/null and b/test/reference/smask-text.ref.png differ
diff --git a/test/reference/smask-text.script.ref.png b/test/reference/smask-text.script.ref.png
new file mode 100644 (file)
index 0000000..62b2de5
Binary files /dev/null and b/test/reference/smask-text.script.ref.png differ
diff --git a/test/reference/smask-text.svg.ref.png b/test/reference/smask-text.svg.ref.png
new file mode 100644 (file)
index 0000000..65f225e
Binary files /dev/null and b/test/reference/smask-text.svg.ref.png differ
diff --git a/test/reference/smask-text.traps.argb32.ref.png b/test/reference/smask-text.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..c570259
Binary files /dev/null and b/test/reference/smask-text.traps.argb32.ref.png differ
diff --git a/test/reference/smask-text.traps.rgb24.ref.png b/test/reference/smask-text.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..c570259
Binary files /dev/null and b/test/reference/smask-text.traps.rgb24.ref.png differ
diff --git a/test/reference/smask-text.xlib-fallback.ref.png b/test/reference/smask-text.xlib-fallback.ref.png
new file mode 100644 (file)
index 0000000..bb393b5
Binary files /dev/null and b/test/reference/smask-text.xlib-fallback.ref.png differ
diff --git a/test/reference/smask.base.argb32.ref.png b/test/reference/smask.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..445d920
Binary files /dev/null and b/test/reference/smask.base.argb32.ref.png differ
diff --git a/test/reference/smask.base.rgb24.ref.png b/test/reference/smask.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..445d920
Binary files /dev/null and b/test/reference/smask.base.rgb24.ref.png differ
diff --git a/test/reference/smask.image16.ref.png b/test/reference/smask.image16.ref.png
new file mode 100644 (file)
index 0000000..d817c80
Binary files /dev/null and b/test/reference/smask.image16.ref.png differ
diff --git a/test/reference/smask.pdf.xfail.png b/test/reference/smask.pdf.xfail.png
new file mode 100644 (file)
index 0000000..f8b559c
Binary files /dev/null and b/test/reference/smask.pdf.xfail.png differ
diff --git a/test/reference/smask.ps.ref.png b/test/reference/smask.ps.ref.png
new file mode 100644 (file)
index 0000000..31ccc17
Binary files /dev/null and b/test/reference/smask.ps.ref.png differ
diff --git a/test/reference/smask.quartz.ref.png b/test/reference/smask.quartz.ref.png
new file mode 100644 (file)
index 0000000..f9ab00f
Binary files /dev/null and b/test/reference/smask.quartz.ref.png differ
diff --git a/test/reference/smask.ref.png b/test/reference/smask.ref.png
new file mode 100644 (file)
index 0000000..0370974
Binary files /dev/null and b/test/reference/smask.ref.png differ
diff --git a/test/reference/smask.script.ref.png b/test/reference/smask.script.ref.png
new file mode 100644 (file)
index 0000000..3b672d6
Binary files /dev/null and b/test/reference/smask.script.ref.png differ
diff --git a/test/reference/smask.svg.ref.png b/test/reference/smask.svg.ref.png
new file mode 100644 (file)
index 0000000..b9c0308
Binary files /dev/null and b/test/reference/smask.svg.ref.png differ
diff --git a/test/reference/smask.traps.argb32.ref.png b/test/reference/smask.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..445d920
Binary files /dev/null and b/test/reference/smask.traps.argb32.ref.png differ
diff --git a/test/reference/smask.traps.rgb24.ref.png b/test/reference/smask.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..445d920
Binary files /dev/null and b/test/reference/smask.traps.rgb24.ref.png differ
diff --git a/test/reference/smask.xlib-fallback.ref.png b/test/reference/smask.xlib-fallback.ref.png
new file mode 100644 (file)
index 0000000..b5919de
Binary files /dev/null and b/test/reference/smask.xlib-fallback.ref.png differ
diff --git a/test/reference/solid-pattern-cache-stress.base.argb32.ref.png b/test/reference/solid-pattern-cache-stress.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..08a2ce1
Binary files /dev/null and b/test/reference/solid-pattern-cache-stress.base.argb32.ref.png differ
diff --git a/test/reference/solid-pattern-cache-stress.base.rgb24.ref.png b/test/reference/solid-pattern-cache-stress.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..08a2ce1
Binary files /dev/null and b/test/reference/solid-pattern-cache-stress.base.rgb24.ref.png differ
diff --git a/test/reference/solid-pattern-cache-stress.ref.png b/test/reference/solid-pattern-cache-stress.ref.png
new file mode 100644 (file)
index 0000000..e0e8498
Binary files /dev/null and b/test/reference/solid-pattern-cache-stress.ref.png differ
diff --git a/test/reference/solid-pattern-cache-stress.traps.argb32.ref.png b/test/reference/solid-pattern-cache-stress.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..08a2ce1
Binary files /dev/null and b/test/reference/solid-pattern-cache-stress.traps.argb32.ref.png differ
diff --git a/test/reference/solid-pattern-cache-stress.traps.rgb24.ref.png b/test/reference/solid-pattern-cache-stress.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..08a2ce1
Binary files /dev/null and b/test/reference/solid-pattern-cache-stress.traps.rgb24.ref.png differ
diff --git a/test/reference/source-clip-scale.base.argb32.ref.png b/test/reference/source-clip-scale.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..4b63ad9
Binary files /dev/null and b/test/reference/source-clip-scale.base.argb32.ref.png differ
diff --git a/test/reference/source-clip-scale.base.rgb24.ref.png b/test/reference/source-clip-scale.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..4b63ad9
Binary files /dev/null and b/test/reference/source-clip-scale.base.rgb24.ref.png differ
diff --git a/test/reference/source-clip-scale.gl.ref.png b/test/reference/source-clip-scale.gl.ref.png
new file mode 100644 (file)
index 0000000..fcffbef
Binary files /dev/null and b/test/reference/source-clip-scale.gl.ref.png differ
diff --git a/test/reference/source-clip-scale.pdf.ref.png b/test/reference/source-clip-scale.pdf.ref.png
new file mode 100644 (file)
index 0000000..78c7710
Binary files /dev/null and b/test/reference/source-clip-scale.pdf.ref.png differ
diff --git a/test/reference/source-clip-scale.ps2.argb32.ref.png b/test/reference/source-clip-scale.ps2.argb32.ref.png
new file mode 100644 (file)
index 0000000..be57d77
Binary files /dev/null and b/test/reference/source-clip-scale.ps2.argb32.ref.png differ
diff --git a/test/reference/source-clip-scale.ps2.rgb24.ref.png b/test/reference/source-clip-scale.ps2.rgb24.ref.png
new file mode 100644 (file)
index 0000000..be57d77
Binary files /dev/null and b/test/reference/source-clip-scale.ps2.rgb24.ref.png differ
diff --git a/test/reference/source-clip-scale.ps3.argb32.ref.png b/test/reference/source-clip-scale.ps3.argb32.ref.png
new file mode 100644 (file)
index 0000000..be57d77
Binary files /dev/null and b/test/reference/source-clip-scale.ps3.argb32.ref.png differ
diff --git a/test/reference/source-clip-scale.ps3.rgb24.ref.png b/test/reference/source-clip-scale.ps3.rgb24.ref.png
new file mode 100644 (file)
index 0000000..be57d77
Binary files /dev/null and b/test/reference/source-clip-scale.ps3.rgb24.ref.png differ
diff --git a/test/reference/source-clip-scale.quartz.ref.png b/test/reference/source-clip-scale.quartz.ref.png
new file mode 100644 (file)
index 0000000..3665009
Binary files /dev/null and b/test/reference/source-clip-scale.quartz.ref.png differ
diff --git a/test/reference/source-clip-scale.ref.png b/test/reference/source-clip-scale.ref.png
new file mode 100644 (file)
index 0000000..1519ff8
Binary files /dev/null and b/test/reference/source-clip-scale.ref.png differ
diff --git a/test/reference/source-clip-scale.svg.ref.png b/test/reference/source-clip-scale.svg.ref.png
new file mode 100644 (file)
index 0000000..bbf7c9b
Binary files /dev/null and b/test/reference/source-clip-scale.svg.ref.png differ
diff --git a/test/reference/source-clip-scale.traps.argb32.ref.png b/test/reference/source-clip-scale.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..4b63ad9
Binary files /dev/null and b/test/reference/source-clip-scale.traps.argb32.ref.png differ
diff --git a/test/reference/source-clip-scale.traps.rgb24.ref.png b/test/reference/source-clip-scale.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..4b63ad9
Binary files /dev/null and b/test/reference/source-clip-scale.traps.rgb24.ref.png differ
diff --git a/test/reference/source-clip.base.argb32.ref.png b/test/reference/source-clip.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..821bc6c
Binary files /dev/null and b/test/reference/source-clip.base.argb32.ref.png differ
diff --git a/test/reference/source-clip.base.rgb24.ref.png b/test/reference/source-clip.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..821bc6c
Binary files /dev/null and b/test/reference/source-clip.base.rgb24.ref.png differ
diff --git a/test/reference/source-clip.ref.png b/test/reference/source-clip.ref.png
new file mode 100644 (file)
index 0000000..22454be
Binary files /dev/null and b/test/reference/source-clip.ref.png differ
diff --git a/test/reference/source-clip.traps.argb32.ref.png b/test/reference/source-clip.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..821bc6c
Binary files /dev/null and b/test/reference/source-clip.traps.argb32.ref.png differ
diff --git a/test/reference/source-clip.traps.rgb24.ref.png b/test/reference/source-clip.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..821bc6c
Binary files /dev/null and b/test/reference/source-clip.traps.rgb24.ref.png differ
diff --git a/test/reference/source-surface-scale-paint.base.argb32.ref.png b/test/reference/source-surface-scale-paint.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..087ef75
Binary files /dev/null and b/test/reference/source-surface-scale-paint.base.argb32.ref.png differ
diff --git a/test/reference/source-surface-scale-paint.base.rgb24.ref.png b/test/reference/source-surface-scale-paint.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..322af21
Binary files /dev/null and b/test/reference/source-surface-scale-paint.base.rgb24.ref.png differ
diff --git a/test/reference/source-surface-scale-paint.ref.png b/test/reference/source-surface-scale-paint.ref.png
new file mode 100644 (file)
index 0000000..a81f93d
Binary files /dev/null and b/test/reference/source-surface-scale-paint.ref.png differ
diff --git a/test/reference/source-surface-scale-paint.rgb24.ref.png b/test/reference/source-surface-scale-paint.rgb24.ref.png
new file mode 100644 (file)
index 0000000..7ca7459
Binary files /dev/null and b/test/reference/source-surface-scale-paint.rgb24.ref.png differ
diff --git a/test/reference/source-surface-scale-paint.traps.argb32.ref.png b/test/reference/source-surface-scale-paint.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..087ef75
Binary files /dev/null and b/test/reference/source-surface-scale-paint.traps.argb32.ref.png differ
diff --git a/test/reference/source-surface-scale-paint.traps.rgb24.ref.png b/test/reference/source-surface-scale-paint.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..322af21
Binary files /dev/null and b/test/reference/source-surface-scale-paint.traps.rgb24.ref.png differ
diff --git a/test/reference/spline-decomposition.base.argb32.ref.png b/test/reference/spline-decomposition.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..113f221
Binary files /dev/null and b/test/reference/spline-decomposition.base.argb32.ref.png differ
diff --git a/test/reference/spline-decomposition.base.rgb24.ref.png b/test/reference/spline-decomposition.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..113f221
Binary files /dev/null and b/test/reference/spline-decomposition.base.rgb24.ref.png differ
diff --git a/test/reference/spline-decomposition.image16.ref.png b/test/reference/spline-decomposition.image16.ref.png
new file mode 100644 (file)
index 0000000..4084b00
Binary files /dev/null and b/test/reference/spline-decomposition.image16.ref.png differ
diff --git a/test/reference/spline-decomposition.pdf.ref.png b/test/reference/spline-decomposition.pdf.ref.png
new file mode 100644 (file)
index 0000000..5afa094
Binary files /dev/null and b/test/reference/spline-decomposition.pdf.ref.png differ
diff --git a/test/reference/spline-decomposition.ps.ref.png b/test/reference/spline-decomposition.ps.ref.png
new file mode 100644 (file)
index 0000000..51e2938
Binary files /dev/null and b/test/reference/spline-decomposition.ps.ref.png differ
diff --git a/test/reference/spline-decomposition.quartz.xfail.png b/test/reference/spline-decomposition.quartz.xfail.png
new file mode 100644 (file)
index 0000000..18d67fb
Binary files /dev/null and b/test/reference/spline-decomposition.quartz.xfail.png differ
diff --git a/test/reference/spline-decomposition.ref.png b/test/reference/spline-decomposition.ref.png
new file mode 100644 (file)
index 0000000..e655efd
Binary files /dev/null and b/test/reference/spline-decomposition.ref.png differ
diff --git a/test/reference/spline-decomposition.svg.ref.png b/test/reference/spline-decomposition.svg.ref.png
new file mode 100644 (file)
index 0000000..5afa094
Binary files /dev/null and b/test/reference/spline-decomposition.svg.ref.png differ
diff --git a/test/reference/spline-decomposition.traps.argb32.ref.png b/test/reference/spline-decomposition.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..113f221
Binary files /dev/null and b/test/reference/spline-decomposition.traps.argb32.ref.png differ
diff --git a/test/reference/spline-decomposition.traps.rgb24.ref.png b/test/reference/spline-decomposition.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..113f221
Binary files /dev/null and b/test/reference/spline-decomposition.traps.rgb24.ref.png differ
diff --git a/test/reference/stroke-ctm-caps.base.argb32.ref.png b/test/reference/stroke-ctm-caps.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..799ff39
Binary files /dev/null and b/test/reference/stroke-ctm-caps.base.argb32.ref.png differ
diff --git a/test/reference/stroke-ctm-caps.base.rgb24.ref.png b/test/reference/stroke-ctm-caps.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..799ff39
Binary files /dev/null and b/test/reference/stroke-ctm-caps.base.rgb24.ref.png differ
diff --git a/test/reference/stroke-ctm-caps.image16.ref.png b/test/reference/stroke-ctm-caps.image16.ref.png
new file mode 100644 (file)
index 0000000..f5f551e
Binary files /dev/null and b/test/reference/stroke-ctm-caps.image16.ref.png differ
diff --git a/test/reference/stroke-ctm-caps.ps2.ref.png b/test/reference/stroke-ctm-caps.ps2.ref.png
new file mode 100644 (file)
index 0000000..63c1064
Binary files /dev/null and b/test/reference/stroke-ctm-caps.ps2.ref.png differ
diff --git a/test/reference/stroke-ctm-caps.ps3.ref.png b/test/reference/stroke-ctm-caps.ps3.ref.png
new file mode 100644 (file)
index 0000000..63c1064
Binary files /dev/null and b/test/reference/stroke-ctm-caps.ps3.ref.png differ
diff --git a/test/reference/stroke-ctm-caps.quartz.ref.png b/test/reference/stroke-ctm-caps.quartz.ref.png
new file mode 100644 (file)
index 0000000..c9da2c9
Binary files /dev/null and b/test/reference/stroke-ctm-caps.quartz.ref.png differ
diff --git a/test/reference/stroke-ctm-caps.ref.png b/test/reference/stroke-ctm-caps.ref.png
new file mode 100644 (file)
index 0000000..75e3252
Binary files /dev/null and b/test/reference/stroke-ctm-caps.ref.png differ
diff --git a/test/reference/stroke-ctm-caps.traps.argb32.ref.png b/test/reference/stroke-ctm-caps.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..799ff39
Binary files /dev/null and b/test/reference/stroke-ctm-caps.traps.argb32.ref.png differ
diff --git a/test/reference/stroke-ctm-caps.traps.rgb24.ref.png b/test/reference/stroke-ctm-caps.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..799ff39
Binary files /dev/null and b/test/reference/stroke-ctm-caps.traps.rgb24.ref.png differ
diff --git a/test/reference/stroke-image.base.argb32.ref.png b/test/reference/stroke-image.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..15d4dce
Binary files /dev/null and b/test/reference/stroke-image.base.argb32.ref.png differ
diff --git a/test/reference/stroke-image.base.rgb24.ref.png b/test/reference/stroke-image.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..15d4dce
Binary files /dev/null and b/test/reference/stroke-image.base.rgb24.ref.png differ
diff --git a/test/reference/stroke-image.image16.ref.png b/test/reference/stroke-image.image16.ref.png
new file mode 100644 (file)
index 0000000..cef40bd
Binary files /dev/null and b/test/reference/stroke-image.image16.ref.png differ
diff --git a/test/reference/stroke-image.pdf.ref.png b/test/reference/stroke-image.pdf.ref.png
new file mode 100644 (file)
index 0000000..790369c
Binary files /dev/null and b/test/reference/stroke-image.pdf.ref.png differ
diff --git a/test/reference/stroke-image.ps.ref.png b/test/reference/stroke-image.ps.ref.png
new file mode 100644 (file)
index 0000000..71889ac
Binary files /dev/null and b/test/reference/stroke-image.ps.ref.png differ
diff --git a/test/reference/stroke-image.quartz.ref.png b/test/reference/stroke-image.quartz.ref.png
new file mode 100644 (file)
index 0000000..f9a60f2
Binary files /dev/null and b/test/reference/stroke-image.quartz.ref.png differ
diff --git a/test/reference/stroke-image.ref.png b/test/reference/stroke-image.ref.png
new file mode 100644 (file)
index 0000000..899c159
Binary files /dev/null and b/test/reference/stroke-image.ref.png differ
diff --git a/test/reference/stroke-image.traps.argb32.ref.png b/test/reference/stroke-image.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..15d4dce
Binary files /dev/null and b/test/reference/stroke-image.traps.argb32.ref.png differ
diff --git a/test/reference/stroke-image.traps.rgb24.ref.png b/test/reference/stroke-image.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..15d4dce
Binary files /dev/null and b/test/reference/stroke-image.traps.rgb24.ref.png differ
diff --git a/test/reference/stroke-open-box.base.argb32.ref.png b/test/reference/stroke-open-box.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..b5f5bd5
Binary files /dev/null and b/test/reference/stroke-open-box.base.argb32.ref.png differ
diff --git a/test/reference/stroke-open-box.base.rgb24.ref.png b/test/reference/stroke-open-box.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..b5f5bd5
Binary files /dev/null and b/test/reference/stroke-open-box.base.rgb24.ref.png differ
diff --git a/test/reference/stroke-open-box.ref.png b/test/reference/stroke-open-box.ref.png
new file mode 100644 (file)
index 0000000..b5f5bd5
Binary files /dev/null and b/test/reference/stroke-open-box.ref.png differ
diff --git a/test/reference/stroke-open-box.traps.argb32.ref.png b/test/reference/stroke-open-box.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..b5f5bd5
Binary files /dev/null and b/test/reference/stroke-open-box.traps.argb32.ref.png differ
diff --git a/test/reference/stroke-open-box.traps.rgb24.ref.png b/test/reference/stroke-open-box.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..b5f5bd5
Binary files /dev/null and b/test/reference/stroke-open-box.traps.rgb24.ref.png differ
diff --git a/test/reference/stroke-pattern.ref.png b/test/reference/stroke-pattern.ref.png
new file mode 100644 (file)
index 0000000..61ea33e
Binary files /dev/null and b/test/reference/stroke-pattern.ref.png differ
diff --git a/test/reference/subsurface-image-repeat.base.argb32.ref.png b/test/reference/subsurface-image-repeat.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..3dd6306
Binary files /dev/null and b/test/reference/subsurface-image-repeat.base.argb32.ref.png differ
diff --git a/test/reference/subsurface-image-repeat.base.rgb24.ref.png b/test/reference/subsurface-image-repeat.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..3dd6306
Binary files /dev/null and b/test/reference/subsurface-image-repeat.base.rgb24.ref.png differ
diff --git a/test/reference/subsurface-image-repeat.image16.ref.png b/test/reference/subsurface-image-repeat.image16.ref.png
new file mode 100644 (file)
index 0000000..a62f210
Binary files /dev/null and b/test/reference/subsurface-image-repeat.image16.ref.png differ
diff --git a/test/reference/subsurface-image-repeat.ref.png b/test/reference/subsurface-image-repeat.ref.png
new file mode 100644 (file)
index 0000000..3dd6306
Binary files /dev/null and b/test/reference/subsurface-image-repeat.ref.png differ
diff --git a/test/reference/subsurface-image-repeat.traps.argb32.ref.png b/test/reference/subsurface-image-repeat.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..3dd6306
Binary files /dev/null and b/test/reference/subsurface-image-repeat.traps.argb32.ref.png differ
diff --git a/test/reference/subsurface-image-repeat.traps.rgb24.ref.png b/test/reference/subsurface-image-repeat.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..3dd6306
Binary files /dev/null and b/test/reference/subsurface-image-repeat.traps.rgb24.ref.png differ
diff --git a/test/reference/subsurface-modify-child.base.argb32.ref.png b/test/reference/subsurface-modify-child.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..c37e22e
Binary files /dev/null and b/test/reference/subsurface-modify-child.base.argb32.ref.png differ
diff --git a/test/reference/subsurface-modify-child.base.rgb24.ref.png b/test/reference/subsurface-modify-child.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..c37e22e
Binary files /dev/null and b/test/reference/subsurface-modify-child.base.rgb24.ref.png differ
diff --git a/test/reference/subsurface-modify-child.ref.png b/test/reference/subsurface-modify-child.ref.png
new file mode 100644 (file)
index 0000000..c37e22e
Binary files /dev/null and b/test/reference/subsurface-modify-child.ref.png differ
diff --git a/test/reference/subsurface-modify-child.traps.argb32.ref.png b/test/reference/subsurface-modify-child.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..c37e22e
Binary files /dev/null and b/test/reference/subsurface-modify-child.traps.argb32.ref.png differ
diff --git a/test/reference/subsurface-modify-child.traps.rgb24.ref.png b/test/reference/subsurface-modify-child.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..c37e22e
Binary files /dev/null and b/test/reference/subsurface-modify-child.traps.rgb24.ref.png differ
diff --git a/test/reference/subsurface-modify-parent.base.argb32.ref.png b/test/reference/subsurface-modify-parent.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..c37e22e
Binary files /dev/null and b/test/reference/subsurface-modify-parent.base.argb32.ref.png differ
diff --git a/test/reference/subsurface-modify-parent.base.rgb24.ref.png b/test/reference/subsurface-modify-parent.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..c37e22e
Binary files /dev/null and b/test/reference/subsurface-modify-parent.base.rgb24.ref.png differ
diff --git a/test/reference/subsurface-modify-parent.ref.png b/test/reference/subsurface-modify-parent.ref.png
new file mode 100644 (file)
index 0000000..c37e22e
Binary files /dev/null and b/test/reference/subsurface-modify-parent.ref.png differ
diff --git a/test/reference/subsurface-modify-parent.traps.argb32.ref.png b/test/reference/subsurface-modify-parent.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..c37e22e
Binary files /dev/null and b/test/reference/subsurface-modify-parent.traps.argb32.ref.png differ
diff --git a/test/reference/subsurface-modify-parent.traps.rgb24.ref.png b/test/reference/subsurface-modify-parent.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..c37e22e
Binary files /dev/null and b/test/reference/subsurface-modify-parent.traps.rgb24.ref.png differ
diff --git a/test/reference/subsurface-outside-target.argb32.ref.png b/test/reference/subsurface-outside-target.argb32.ref.png
new file mode 100644 (file)
index 0000000..337cdd8
Binary files /dev/null and b/test/reference/subsurface-outside-target.argb32.ref.png differ
diff --git a/test/reference/subsurface-outside-target.base.argb32.ref.png b/test/reference/subsurface-outside-target.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..16294f3
Binary files /dev/null and b/test/reference/subsurface-outside-target.base.argb32.ref.png differ
diff --git a/test/reference/subsurface-outside-target.base.rgb24.ref.png b/test/reference/subsurface-outside-target.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..1009249
Binary files /dev/null and b/test/reference/subsurface-outside-target.base.rgb24.ref.png differ
diff --git a/test/reference/subsurface-outside-target.rgb24.ref.png b/test/reference/subsurface-outside-target.rgb24.ref.png
new file mode 100644 (file)
index 0000000..368936e
Binary files /dev/null and b/test/reference/subsurface-outside-target.rgb24.ref.png differ
diff --git a/test/reference/subsurface-pad.base.argb32.ref.png b/test/reference/subsurface-pad.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..eeb9f8f
Binary files /dev/null and b/test/reference/subsurface-pad.base.argb32.ref.png differ
diff --git a/test/reference/subsurface-pad.base.rgb24.ref.png b/test/reference/subsurface-pad.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..eeb9f8f
Binary files /dev/null and b/test/reference/subsurface-pad.base.rgb24.ref.png differ
diff --git a/test/reference/subsurface-pad.quartz.xfail.png b/test/reference/subsurface-pad.quartz.xfail.png
new file mode 100644 (file)
index 0000000..c37e22e
Binary files /dev/null and b/test/reference/subsurface-pad.quartz.xfail.png differ
diff --git a/test/reference/subsurface-pad.ref.png b/test/reference/subsurface-pad.ref.png
new file mode 100644 (file)
index 0000000..eeb9f8f
Binary files /dev/null and b/test/reference/subsurface-pad.ref.png differ
diff --git a/test/reference/subsurface-pad.traps.argb32.ref.png b/test/reference/subsurface-pad.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..eeb9f8f
Binary files /dev/null and b/test/reference/subsurface-pad.traps.argb32.ref.png differ
diff --git a/test/reference/subsurface-pad.traps.rgb24.ref.png b/test/reference/subsurface-pad.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..eeb9f8f
Binary files /dev/null and b/test/reference/subsurface-pad.traps.rgb24.ref.png differ
diff --git a/test/reference/subsurface-reflect.base.argb32.ref.png b/test/reference/subsurface-reflect.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..55643f4
Binary files /dev/null and b/test/reference/subsurface-reflect.base.argb32.ref.png differ
diff --git a/test/reference/subsurface-reflect.base.rgb24.ref.png b/test/reference/subsurface-reflect.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..55643f4
Binary files /dev/null and b/test/reference/subsurface-reflect.base.rgb24.ref.png differ
diff --git a/test/reference/subsurface-reflect.ref.png b/test/reference/subsurface-reflect.ref.png
new file mode 100644 (file)
index 0000000..55643f4
Binary files /dev/null and b/test/reference/subsurface-reflect.ref.png differ
diff --git a/test/reference/subsurface-reflect.traps.argb32.ref.png b/test/reference/subsurface-reflect.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..55643f4
Binary files /dev/null and b/test/reference/subsurface-reflect.traps.argb32.ref.png differ
diff --git a/test/reference/subsurface-reflect.traps.rgb24.ref.png b/test/reference/subsurface-reflect.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..55643f4
Binary files /dev/null and b/test/reference/subsurface-reflect.traps.rgb24.ref.png differ
diff --git a/test/reference/subsurface-repeat.base.argb32.ref.png b/test/reference/subsurface-repeat.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..c37e22e
Binary files /dev/null and b/test/reference/subsurface-repeat.base.argb32.ref.png differ
diff --git a/test/reference/subsurface-repeat.base.rgb24.ref.png b/test/reference/subsurface-repeat.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..c37e22e
Binary files /dev/null and b/test/reference/subsurface-repeat.base.rgb24.ref.png differ
diff --git a/test/reference/subsurface-repeat.ref.png b/test/reference/subsurface-repeat.ref.png
new file mode 100644 (file)
index 0000000..c37e22e
Binary files /dev/null and b/test/reference/subsurface-repeat.ref.png differ
diff --git a/test/reference/subsurface-repeat.traps.argb32.ref.png b/test/reference/subsurface-repeat.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..c37e22e
Binary files /dev/null and b/test/reference/subsurface-repeat.traps.argb32.ref.png differ
diff --git a/test/reference/subsurface-repeat.traps.rgb24.ref.png b/test/reference/subsurface-repeat.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..c37e22e
Binary files /dev/null and b/test/reference/subsurface-repeat.traps.rgb24.ref.png differ
diff --git a/test/reference/subsurface-scale.base.ref.png b/test/reference/subsurface-scale.base.ref.png
new file mode 100644 (file)
index 0000000..41ff4f4
Binary files /dev/null and b/test/reference/subsurface-scale.base.ref.png differ
diff --git a/test/reference/subsurface-scale.ref.png b/test/reference/subsurface-scale.ref.png
new file mode 100644 (file)
index 0000000..41ff4f4
Binary files /dev/null and b/test/reference/subsurface-scale.ref.png differ
diff --git a/test/reference/subsurface-similar-repeat.base.argb32.ref.png b/test/reference/subsurface-similar-repeat.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..c37e22e
Binary files /dev/null and b/test/reference/subsurface-similar-repeat.base.argb32.ref.png differ
diff --git a/test/reference/subsurface-similar-repeat.base.rgb24.ref.png b/test/reference/subsurface-similar-repeat.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..c37e22e
Binary files /dev/null and b/test/reference/subsurface-similar-repeat.base.rgb24.ref.png differ
diff --git a/test/reference/subsurface-similar-repeat.ref.png b/test/reference/subsurface-similar-repeat.ref.png
new file mode 100644 (file)
index 0000000..c37e22e
Binary files /dev/null and b/test/reference/subsurface-similar-repeat.ref.png differ
diff --git a/test/reference/subsurface-similar-repeat.traps.argb32.ref.png b/test/reference/subsurface-similar-repeat.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..c37e22e
Binary files /dev/null and b/test/reference/subsurface-similar-repeat.traps.argb32.ref.png differ
diff --git a/test/reference/subsurface-similar-repeat.traps.rgb24.ref.png b/test/reference/subsurface-similar-repeat.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..c37e22e
Binary files /dev/null and b/test/reference/subsurface-similar-repeat.traps.rgb24.ref.png differ
diff --git a/test/reference/subsurface.base.argb32.ref.png b/test/reference/subsurface.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..77e3c23
Binary files /dev/null and b/test/reference/subsurface.base.argb32.ref.png differ
diff --git a/test/reference/subsurface.base.rgb24.ref.png b/test/reference/subsurface.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..77e3c23
Binary files /dev/null and b/test/reference/subsurface.base.rgb24.ref.png differ
diff --git a/test/reference/subsurface.image16.ref.png b/test/reference/subsurface.image16.ref.png
new file mode 100644 (file)
index 0000000..ba9b7ed
Binary files /dev/null and b/test/reference/subsurface.image16.ref.png differ
diff --git a/test/reference/subsurface.ps.ref.png b/test/reference/subsurface.ps.ref.png
new file mode 100644 (file)
index 0000000..51e19fc
Binary files /dev/null and b/test/reference/subsurface.ps.ref.png differ
diff --git a/test/reference/subsurface.ref.png b/test/reference/subsurface.ref.png
new file mode 100644 (file)
index 0000000..77e3c23
Binary files /dev/null and b/test/reference/subsurface.ref.png differ
diff --git a/test/reference/subsurface.traps.argb32.ref.png b/test/reference/subsurface.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..77e3c23
Binary files /dev/null and b/test/reference/subsurface.traps.argb32.ref.png differ
diff --git a/test/reference/subsurface.traps.rgb24.ref.png b/test/reference/subsurface.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..77e3c23
Binary files /dev/null and b/test/reference/subsurface.traps.rgb24.ref.png differ
diff --git a/test/reference/surface-pattern-big-scale-down.base.argb32.ref.png b/test/reference/surface-pattern-big-scale-down.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..c050990
Binary files /dev/null and b/test/reference/surface-pattern-big-scale-down.base.argb32.ref.png differ
diff --git a/test/reference/surface-pattern-big-scale-down.base.rgb24.ref.png b/test/reference/surface-pattern-big-scale-down.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..c050990
Binary files /dev/null and b/test/reference/surface-pattern-big-scale-down.base.rgb24.ref.png differ
diff --git a/test/reference/surface-pattern-big-scale-down.ps.ref.png b/test/reference/surface-pattern-big-scale-down.ps.ref.png
new file mode 100644 (file)
index 0000000..13fb093
Binary files /dev/null and b/test/reference/surface-pattern-big-scale-down.ps.ref.png differ
diff --git a/test/reference/surface-pattern-big-scale-down.quartz.ref.png b/test/reference/surface-pattern-big-scale-down.quartz.ref.png
new file mode 100644 (file)
index 0000000..32deb2d
Binary files /dev/null and b/test/reference/surface-pattern-big-scale-down.quartz.ref.png differ
diff --git a/test/reference/surface-pattern-big-scale-down.ref.png b/test/reference/surface-pattern-big-scale-down.ref.png
new file mode 100644 (file)
index 0000000..c050990
Binary files /dev/null and b/test/reference/surface-pattern-big-scale-down.ref.png differ
diff --git a/test/reference/surface-pattern-big-scale-down.traps.argb32.ref.png b/test/reference/surface-pattern-big-scale-down.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..c050990
Binary files /dev/null and b/test/reference/surface-pattern-big-scale-down.traps.argb32.ref.png differ
diff --git a/test/reference/surface-pattern-big-scale-down.traps.rgb24.ref.png b/test/reference/surface-pattern-big-scale-down.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..c050990
Binary files /dev/null and b/test/reference/surface-pattern-big-scale-down.traps.rgb24.ref.png differ
diff --git a/test/reference/surface-pattern-operator.argb32.ref.png b/test/reference/surface-pattern-operator.argb32.ref.png
new file mode 100644 (file)
index 0000000..9abc252
Binary files /dev/null and b/test/reference/surface-pattern-operator.argb32.ref.png differ
diff --git a/test/reference/surface-pattern-operator.base.argb32.ref.png b/test/reference/surface-pattern-operator.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..670d3c3
Binary files /dev/null and b/test/reference/surface-pattern-operator.base.argb32.ref.png differ
diff --git a/test/reference/surface-pattern-operator.base.rgb24.ref.png b/test/reference/surface-pattern-operator.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..23e540d
Binary files /dev/null and b/test/reference/surface-pattern-operator.base.rgb24.ref.png differ
diff --git a/test/reference/surface-pattern-operator.image16.ref.png b/test/reference/surface-pattern-operator.image16.ref.png
new file mode 100644 (file)
index 0000000..507e49c
Binary files /dev/null and b/test/reference/surface-pattern-operator.image16.ref.png differ
diff --git a/test/reference/surface-pattern-operator.pdf.argb32.xfail.png b/test/reference/surface-pattern-operator.pdf.argb32.xfail.png
new file mode 100644 (file)
index 0000000..371d084
Binary files /dev/null and b/test/reference/surface-pattern-operator.pdf.argb32.xfail.png differ
diff --git a/test/reference/surface-pattern-operator.pdf.rgb24.xfail.png b/test/reference/surface-pattern-operator.pdf.rgb24.xfail.png
new file mode 100644 (file)
index 0000000..a2da18d
Binary files /dev/null and b/test/reference/surface-pattern-operator.pdf.rgb24.xfail.png differ
diff --git a/test/reference/surface-pattern-operator.quartz.argb32.ref.png b/test/reference/surface-pattern-operator.quartz.argb32.ref.png
new file mode 100644 (file)
index 0000000..5e934b2
Binary files /dev/null and b/test/reference/surface-pattern-operator.quartz.argb32.ref.png differ
diff --git a/test/reference/surface-pattern-operator.quartz.rgb24.ref.png b/test/reference/surface-pattern-operator.quartz.rgb24.ref.png
new file mode 100644 (file)
index 0000000..30408c1
Binary files /dev/null and b/test/reference/surface-pattern-operator.quartz.rgb24.ref.png differ
diff --git a/test/reference/surface-pattern-operator.rgb24.ref.png b/test/reference/surface-pattern-operator.rgb24.ref.png
new file mode 100644 (file)
index 0000000..dd9315a
Binary files /dev/null and b/test/reference/surface-pattern-operator.rgb24.ref.png differ
diff --git a/test/reference/surface-pattern-operator.traps.argb32.ref.png b/test/reference/surface-pattern-operator.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..670d3c3
Binary files /dev/null and b/test/reference/surface-pattern-operator.traps.argb32.ref.png differ
diff --git a/test/reference/surface-pattern-operator.traps.rgb24.ref.png b/test/reference/surface-pattern-operator.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..23e540d
Binary files /dev/null and b/test/reference/surface-pattern-operator.traps.rgb24.ref.png differ
diff --git a/test/reference/surface-pattern-scale-down-extend-none.base.argb32.ref.png b/test/reference/surface-pattern-scale-down-extend-none.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..9df14cc
Binary files /dev/null and b/test/reference/surface-pattern-scale-down-extend-none.base.argb32.ref.png differ
diff --git a/test/reference/surface-pattern-scale-down-extend-none.base.rgb24.ref.png b/test/reference/surface-pattern-scale-down-extend-none.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..9df14cc
Binary files /dev/null and b/test/reference/surface-pattern-scale-down-extend-none.base.rgb24.ref.png differ
diff --git a/test/reference/surface-pattern-scale-down-extend-none.quartz.ref.png b/test/reference/surface-pattern-scale-down-extend-none.quartz.ref.png
new file mode 100644 (file)
index 0000000..e126cbb
Binary files /dev/null and b/test/reference/surface-pattern-scale-down-extend-none.quartz.ref.png differ
diff --git a/test/reference/surface-pattern-scale-down-extend-none.ref.png b/test/reference/surface-pattern-scale-down-extend-none.ref.png
new file mode 100644 (file)
index 0000000..9df14cc
Binary files /dev/null and b/test/reference/surface-pattern-scale-down-extend-none.ref.png differ
diff --git a/test/reference/surface-pattern-scale-down-extend-none.traps.argb32.ref.png b/test/reference/surface-pattern-scale-down-extend-none.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..9df14cc
Binary files /dev/null and b/test/reference/surface-pattern-scale-down-extend-none.traps.argb32.ref.png differ
diff --git a/test/reference/surface-pattern-scale-down-extend-none.traps.rgb24.ref.png b/test/reference/surface-pattern-scale-down-extend-none.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..9df14cc
Binary files /dev/null and b/test/reference/surface-pattern-scale-down-extend-none.traps.rgb24.ref.png differ
diff --git a/test/reference/surface-pattern-scale-down-extend-pad.base.argb32.ref.png b/test/reference/surface-pattern-scale-down-extend-pad.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..2ee9419
Binary files /dev/null and b/test/reference/surface-pattern-scale-down-extend-pad.base.argb32.ref.png differ
diff --git a/test/reference/surface-pattern-scale-down-extend-pad.base.rgb24.ref.png b/test/reference/surface-pattern-scale-down-extend-pad.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..2ee9419
Binary files /dev/null and b/test/reference/surface-pattern-scale-down-extend-pad.base.rgb24.ref.png differ
diff --git a/test/reference/surface-pattern-scale-down-extend-pad.quartz.xfail.png b/test/reference/surface-pattern-scale-down-extend-pad.quartz.xfail.png
new file mode 100644 (file)
index 0000000..6b8f754
Binary files /dev/null and b/test/reference/surface-pattern-scale-down-extend-pad.quartz.xfail.png differ
diff --git a/test/reference/surface-pattern-scale-down-extend-pad.ref.png b/test/reference/surface-pattern-scale-down-extend-pad.ref.png
new file mode 100644 (file)
index 0000000..2ee9419
Binary files /dev/null and b/test/reference/surface-pattern-scale-down-extend-pad.ref.png differ
diff --git a/test/reference/surface-pattern-scale-down-extend-pad.traps.argb32.ref.png b/test/reference/surface-pattern-scale-down-extend-pad.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..2ee9419
Binary files /dev/null and b/test/reference/surface-pattern-scale-down-extend-pad.traps.argb32.ref.png differ
diff --git a/test/reference/surface-pattern-scale-down-extend-pad.traps.rgb24.ref.png b/test/reference/surface-pattern-scale-down-extend-pad.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..2ee9419
Binary files /dev/null and b/test/reference/surface-pattern-scale-down-extend-pad.traps.rgb24.ref.png differ
diff --git a/test/reference/surface-pattern-scale-down-extend-reflect.base.argb32.ref.png b/test/reference/surface-pattern-scale-down-extend-reflect.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..f2e93a7
Binary files /dev/null and b/test/reference/surface-pattern-scale-down-extend-reflect.base.argb32.ref.png differ
diff --git a/test/reference/surface-pattern-scale-down-extend-reflect.base.rgb24.ref.png b/test/reference/surface-pattern-scale-down-extend-reflect.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..f2e93a7
Binary files /dev/null and b/test/reference/surface-pattern-scale-down-extend-reflect.base.rgb24.ref.png differ
diff --git a/test/reference/surface-pattern-scale-down-extend-reflect.quartz.ref.png b/test/reference/surface-pattern-scale-down-extend-reflect.quartz.ref.png
new file mode 100644 (file)
index 0000000..ef5e62e
Binary files /dev/null and b/test/reference/surface-pattern-scale-down-extend-reflect.quartz.ref.png differ
diff --git a/test/reference/surface-pattern-scale-down-extend-reflect.ref.png b/test/reference/surface-pattern-scale-down-extend-reflect.ref.png
new file mode 100644 (file)
index 0000000..f2e93a7
Binary files /dev/null and b/test/reference/surface-pattern-scale-down-extend-reflect.ref.png differ
diff --git a/test/reference/surface-pattern-scale-down-extend-reflect.traps.argb32.ref.png b/test/reference/surface-pattern-scale-down-extend-reflect.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..f2e93a7
Binary files /dev/null and b/test/reference/surface-pattern-scale-down-extend-reflect.traps.argb32.ref.png differ
diff --git a/test/reference/surface-pattern-scale-down-extend-reflect.traps.rgb24.ref.png b/test/reference/surface-pattern-scale-down-extend-reflect.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..f2e93a7
Binary files /dev/null and b/test/reference/surface-pattern-scale-down-extend-reflect.traps.rgb24.ref.png differ
diff --git a/test/reference/surface-pattern-scale-down-extend-repeat.base.argb32.ref.png b/test/reference/surface-pattern-scale-down-extend-repeat.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..c5cff0f
Binary files /dev/null and b/test/reference/surface-pattern-scale-down-extend-repeat.base.argb32.ref.png differ
diff --git a/test/reference/surface-pattern-scale-down-extend-repeat.base.rgb24.ref.png b/test/reference/surface-pattern-scale-down-extend-repeat.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..c5cff0f
Binary files /dev/null and b/test/reference/surface-pattern-scale-down-extend-repeat.base.rgb24.ref.png differ
diff --git a/test/reference/surface-pattern-scale-down-extend-repeat.quartz.ref.png b/test/reference/surface-pattern-scale-down-extend-repeat.quartz.ref.png
new file mode 100644 (file)
index 0000000..6b8f754
Binary files /dev/null and b/test/reference/surface-pattern-scale-down-extend-repeat.quartz.ref.png differ
diff --git a/test/reference/surface-pattern-scale-down-extend-repeat.ref.png b/test/reference/surface-pattern-scale-down-extend-repeat.ref.png
new file mode 100644 (file)
index 0000000..c5cff0f
Binary files /dev/null and b/test/reference/surface-pattern-scale-down-extend-repeat.ref.png differ
diff --git a/test/reference/surface-pattern-scale-down-extend-repeat.traps.argb32.ref.png b/test/reference/surface-pattern-scale-down-extend-repeat.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..c5cff0f
Binary files /dev/null and b/test/reference/surface-pattern-scale-down-extend-repeat.traps.argb32.ref.png differ
diff --git a/test/reference/surface-pattern-scale-down-extend-repeat.traps.rgb24.ref.png b/test/reference/surface-pattern-scale-down-extend-repeat.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..c5cff0f
Binary files /dev/null and b/test/reference/surface-pattern-scale-down-extend-repeat.traps.rgb24.ref.png differ
diff --git a/test/reference/surface-pattern-scale-down.base.argb32.ref.png b/test/reference/surface-pattern-scale-down.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..8bb58a2
Binary files /dev/null and b/test/reference/surface-pattern-scale-down.base.argb32.ref.png differ
diff --git a/test/reference/surface-pattern-scale-down.base.rgb24.ref.png b/test/reference/surface-pattern-scale-down.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..8bb58a2
Binary files /dev/null and b/test/reference/surface-pattern-scale-down.base.rgb24.ref.png differ
diff --git a/test/reference/surface-pattern-scale-down.image16.ref.png b/test/reference/surface-pattern-scale-down.image16.ref.png
new file mode 100644 (file)
index 0000000..8be8fb0
Binary files /dev/null and b/test/reference/surface-pattern-scale-down.image16.ref.png differ
diff --git a/test/reference/surface-pattern-scale-down.pdf.ref.png b/test/reference/surface-pattern-scale-down.pdf.ref.png
new file mode 100644 (file)
index 0000000..1e32a44
Binary files /dev/null and b/test/reference/surface-pattern-scale-down.pdf.ref.png differ
diff --git a/test/reference/surface-pattern-scale-down.ps2.ref.png b/test/reference/surface-pattern-scale-down.ps2.ref.png
new file mode 100644 (file)
index 0000000..5fb6395
Binary files /dev/null and b/test/reference/surface-pattern-scale-down.ps2.ref.png differ
diff --git a/test/reference/surface-pattern-scale-down.ps3.ref.png b/test/reference/surface-pattern-scale-down.ps3.ref.png
new file mode 100644 (file)
index 0000000..5fb6395
Binary files /dev/null and b/test/reference/surface-pattern-scale-down.ps3.ref.png differ
diff --git a/test/reference/surface-pattern-scale-down.quartz.ref.png b/test/reference/surface-pattern-scale-down.quartz.ref.png
new file mode 100644 (file)
index 0000000..5b3ace4
Binary files /dev/null and b/test/reference/surface-pattern-scale-down.quartz.ref.png differ
diff --git a/test/reference/surface-pattern-scale-down.ref.png b/test/reference/surface-pattern-scale-down.ref.png
new file mode 100644 (file)
index 0000000..b7255ef
Binary files /dev/null and b/test/reference/surface-pattern-scale-down.ref.png differ
diff --git a/test/reference/surface-pattern-scale-down.traps.argb32.ref.png b/test/reference/surface-pattern-scale-down.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..8bb58a2
Binary files /dev/null and b/test/reference/surface-pattern-scale-down.traps.argb32.ref.png differ
diff --git a/test/reference/surface-pattern-scale-down.traps.rgb24.ref.png b/test/reference/surface-pattern-scale-down.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..8bb58a2
Binary files /dev/null and b/test/reference/surface-pattern-scale-down.traps.rgb24.ref.png differ
diff --git a/test/reference/surface-pattern-scale-up.base.argb32.ref.png b/test/reference/surface-pattern-scale-up.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..e8d84e2
Binary files /dev/null and b/test/reference/surface-pattern-scale-up.base.argb32.ref.png differ
diff --git a/test/reference/surface-pattern-scale-up.base.rgb24.ref.png b/test/reference/surface-pattern-scale-up.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..e8d84e2
Binary files /dev/null and b/test/reference/surface-pattern-scale-up.base.rgb24.ref.png differ
diff --git a/test/reference/surface-pattern-scale-up.image16.ref.png b/test/reference/surface-pattern-scale-up.image16.ref.png
new file mode 100644 (file)
index 0000000..5207bd3
Binary files /dev/null and b/test/reference/surface-pattern-scale-up.image16.ref.png differ
diff --git a/test/reference/surface-pattern-scale-up.pdf.ref.png b/test/reference/surface-pattern-scale-up.pdf.ref.png
new file mode 100644 (file)
index 0000000..593d058
Binary files /dev/null and b/test/reference/surface-pattern-scale-up.pdf.ref.png differ
diff --git a/test/reference/surface-pattern-scale-up.ps2.ref.png b/test/reference/surface-pattern-scale-up.ps2.ref.png
new file mode 100644 (file)
index 0000000..f2eac7a
Binary files /dev/null and b/test/reference/surface-pattern-scale-up.ps2.ref.png differ
diff --git a/test/reference/surface-pattern-scale-up.ps3.ref.png b/test/reference/surface-pattern-scale-up.ps3.ref.png
new file mode 100644 (file)
index 0000000..f2eac7a
Binary files /dev/null and b/test/reference/surface-pattern-scale-up.ps3.ref.png differ
diff --git a/test/reference/surface-pattern-scale-up.quartz.xfail.png b/test/reference/surface-pattern-scale-up.quartz.xfail.png
new file mode 100644 (file)
index 0000000..ed52ee8
Binary files /dev/null and b/test/reference/surface-pattern-scale-up.quartz.xfail.png differ
diff --git a/test/reference/surface-pattern-scale-up.ref.png b/test/reference/surface-pattern-scale-up.ref.png
new file mode 100644 (file)
index 0000000..41aafaf
Binary files /dev/null and b/test/reference/surface-pattern-scale-up.ref.png differ
diff --git a/test/reference/surface-pattern-scale-up.traps.argb32.ref.png b/test/reference/surface-pattern-scale-up.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..e8d84e2
Binary files /dev/null and b/test/reference/surface-pattern-scale-up.traps.argb32.ref.png differ
diff --git a/test/reference/surface-pattern-scale-up.traps.rgb24.ref.png b/test/reference/surface-pattern-scale-up.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..e8d84e2
Binary files /dev/null and b/test/reference/surface-pattern-scale-up.traps.rgb24.ref.png differ
diff --git a/test/reference/surface-pattern.base.argb32.ref.png b/test/reference/surface-pattern.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..079ebfb
Binary files /dev/null and b/test/reference/surface-pattern.base.argb32.ref.png differ
diff --git a/test/reference/surface-pattern.base.rgb24.ref.png b/test/reference/surface-pattern.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..079ebfb
Binary files /dev/null and b/test/reference/surface-pattern.base.rgb24.ref.png differ
diff --git a/test/reference/surface-pattern.image16.ref.png b/test/reference/surface-pattern.image16.ref.png
new file mode 100644 (file)
index 0000000..9090097
Binary files /dev/null and b/test/reference/surface-pattern.image16.ref.png differ
diff --git a/test/reference/surface-pattern.pdf.xfail.png b/test/reference/surface-pattern.pdf.xfail.png
new file mode 100644 (file)
index 0000000..fadc2c2
Binary files /dev/null and b/test/reference/surface-pattern.pdf.xfail.png differ
diff --git a/test/reference/surface-pattern.ps.xfail.png b/test/reference/surface-pattern.ps.xfail.png
new file mode 100644 (file)
index 0000000..02fbde8
Binary files /dev/null and b/test/reference/surface-pattern.ps.xfail.png differ
diff --git a/test/reference/surface-pattern.quartz.xfail.png b/test/reference/surface-pattern.quartz.xfail.png
new file mode 100644 (file)
index 0000000..4ac47de
Binary files /dev/null and b/test/reference/surface-pattern.quartz.xfail.png differ
diff --git a/test/reference/surface-pattern.ref.png b/test/reference/surface-pattern.ref.png
new file mode 100644 (file)
index 0000000..db60da6
Binary files /dev/null and b/test/reference/surface-pattern.ref.png differ
diff --git a/test/reference/surface-pattern.svg.xfail.png b/test/reference/surface-pattern.svg.xfail.png
new file mode 100644 (file)
index 0000000..cdbcf47
Binary files /dev/null and b/test/reference/surface-pattern.svg.xfail.png differ
diff --git a/test/reference/surface-pattern.traps.argb32.ref.png b/test/reference/surface-pattern.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..079ebfb
Binary files /dev/null and b/test/reference/surface-pattern.traps.argb32.ref.png differ
diff --git a/test/reference/surface-pattern.traps.rgb24.ref.png b/test/reference/surface-pattern.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..079ebfb
Binary files /dev/null and b/test/reference/surface-pattern.traps.rgb24.ref.png differ
diff --git a/test/reference/svg-surface-source.argb32.ref.png b/test/reference/svg-surface-source.argb32.ref.png
new file mode 100644 (file)
index 0000000..0182972
Binary files /dev/null and b/test/reference/svg-surface-source.argb32.ref.png differ
diff --git a/test/reference/svg-surface-source.base.argb32.ref.png b/test/reference/svg-surface-source.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..0182972
Binary files /dev/null and b/test/reference/svg-surface-source.base.argb32.ref.png differ
diff --git a/test/reference/svg-surface-source.base.rgb24.ref.png b/test/reference/svg-surface-source.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..0d68a82
Binary files /dev/null and b/test/reference/svg-surface-source.base.rgb24.ref.png differ
diff --git a/test/reference/svg-surface-source.image16.ref.png b/test/reference/svg-surface-source.image16.ref.png
new file mode 100644 (file)
index 0000000..2a7460e
Binary files /dev/null and b/test/reference/svg-surface-source.image16.ref.png differ
diff --git a/test/reference/svg-surface-source.rgb24.ref.png b/test/reference/svg-surface-source.rgb24.ref.png
new file mode 100644 (file)
index 0000000..0d68a82
Binary files /dev/null and b/test/reference/svg-surface-source.rgb24.ref.png differ
diff --git a/test/reference/svg-surface-source.svg12.argb32.xfail.png b/test/reference/svg-surface-source.svg12.argb32.xfail.png
new file mode 100644 (file)
index 0000000..6ebcaf9
Binary files /dev/null and b/test/reference/svg-surface-source.svg12.argb32.xfail.png differ
diff --git a/test/reference/svg-surface-source.svg12.rgb24.xfail.png b/test/reference/svg-surface-source.svg12.rgb24.xfail.png
new file mode 100644 (file)
index 0000000..6ebcaf9
Binary files /dev/null and b/test/reference/svg-surface-source.svg12.rgb24.xfail.png differ
diff --git a/test/reference/svg-surface-source.traps.argb32.ref.png b/test/reference/svg-surface-source.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..0182972
Binary files /dev/null and b/test/reference/svg-surface-source.traps.argb32.ref.png differ
diff --git a/test/reference/svg-surface-source.traps.rgb24.ref.png b/test/reference/svg-surface-source.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..0d68a82
Binary files /dev/null and b/test/reference/svg-surface-source.traps.rgb24.ref.png differ
diff --git a/test/reference/text-antialias-gray.base.argb32.ref.png b/test/reference/text-antialias-gray.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..b1dbc20
Binary files /dev/null and b/test/reference/text-antialias-gray.base.argb32.ref.png differ
diff --git a/test/reference/text-antialias-gray.base.rgb24.ref.png b/test/reference/text-antialias-gray.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..b1dbc20
Binary files /dev/null and b/test/reference/text-antialias-gray.base.rgb24.ref.png differ
diff --git a/test/reference/text-antialias-gray.image16.ref.png b/test/reference/text-antialias-gray.image16.ref.png
new file mode 100644 (file)
index 0000000..c86dbae
Binary files /dev/null and b/test/reference/text-antialias-gray.image16.ref.png differ
diff --git a/test/reference/text-antialias-gray.quartz.ref.png b/test/reference/text-antialias-gray.quartz.ref.png
new file mode 100644 (file)
index 0000000..63926a8
Binary files /dev/null and b/test/reference/text-antialias-gray.quartz.ref.png differ
diff --git a/test/reference/text-antialias-gray.ref.png b/test/reference/text-antialias-gray.ref.png
new file mode 100644 (file)
index 0000000..b1dbc20
Binary files /dev/null and b/test/reference/text-antialias-gray.ref.png differ
diff --git a/test/reference/text-antialias-gray.traps.argb32.ref.png b/test/reference/text-antialias-gray.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..b1dbc20
Binary files /dev/null and b/test/reference/text-antialias-gray.traps.argb32.ref.png differ
diff --git a/test/reference/text-antialias-gray.traps.rgb24.ref.png b/test/reference/text-antialias-gray.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..b1dbc20
Binary files /dev/null and b/test/reference/text-antialias-gray.traps.rgb24.ref.png differ
diff --git a/test/reference/text-antialias-none.base.argb32.ref.png b/test/reference/text-antialias-none.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..86c09cd
Binary files /dev/null and b/test/reference/text-antialias-none.base.argb32.ref.png differ
diff --git a/test/reference/text-antialias-none.base.rgb24.ref.png b/test/reference/text-antialias-none.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..86c09cd
Binary files /dev/null and b/test/reference/text-antialias-none.base.rgb24.ref.png differ
diff --git a/test/reference/text-antialias-none.quartz.ref.png b/test/reference/text-antialias-none.quartz.ref.png
new file mode 100644 (file)
index 0000000..872b21d
Binary files /dev/null and b/test/reference/text-antialias-none.quartz.ref.png differ
diff --git a/test/reference/text-antialias-none.ref.png b/test/reference/text-antialias-none.ref.png
new file mode 100644 (file)
index 0000000..bdd77bd
Binary files /dev/null and b/test/reference/text-antialias-none.ref.png differ
diff --git a/test/reference/text-antialias-none.traps.argb32.ref.png b/test/reference/text-antialias-none.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..86c09cd
Binary files /dev/null and b/test/reference/text-antialias-none.traps.argb32.ref.png differ
diff --git a/test/reference/text-antialias-none.traps.rgb24.ref.png b/test/reference/text-antialias-none.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..86c09cd
Binary files /dev/null and b/test/reference/text-antialias-none.traps.rgb24.ref.png differ
diff --git a/test/reference/text-antialias-subpixel.base.argb32.xfail.png b/test/reference/text-antialias-subpixel.base.argb32.xfail.png
new file mode 100644 (file)
index 0000000..4a6bde4
Binary files /dev/null and b/test/reference/text-antialias-subpixel.base.argb32.xfail.png differ
diff --git a/test/reference/text-antialias-subpixel.base.rgb24.xfail.png b/test/reference/text-antialias-subpixel.base.rgb24.xfail.png
new file mode 100644 (file)
index 0000000..4a6bde4
Binary files /dev/null and b/test/reference/text-antialias-subpixel.base.rgb24.xfail.png differ
diff --git a/test/reference/text-antialias-subpixel.image16.ref.png b/test/reference/text-antialias-subpixel.image16.ref.png
new file mode 100644 (file)
index 0000000..81fad89
Binary files /dev/null and b/test/reference/text-antialias-subpixel.image16.ref.png differ
diff --git a/test/reference/text-antialias-subpixel.quartz.ref.png b/test/reference/text-antialias-subpixel.quartz.ref.png
new file mode 100644 (file)
index 0000000..f5b1aa8
Binary files /dev/null and b/test/reference/text-antialias-subpixel.quartz.ref.png differ
diff --git a/test/reference/text-antialias-subpixel.ref.png b/test/reference/text-antialias-subpixel.ref.png
new file mode 100644 (file)
index 0000000..09da5fa
Binary files /dev/null and b/test/reference/text-antialias-subpixel.ref.png differ
diff --git a/test/reference/text-antialias-subpixel.traps.argb32.ref.png b/test/reference/text-antialias-subpixel.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..09da5fa
Binary files /dev/null and b/test/reference/text-antialias-subpixel.traps.argb32.ref.png differ
diff --git a/test/reference/text-antialias-subpixel.traps.rgb24.ref.png b/test/reference/text-antialias-subpixel.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..09da5fa
Binary files /dev/null and b/test/reference/text-antialias-subpixel.traps.rgb24.ref.png differ
diff --git a/test/reference/text-glyph-range.base.argb32.ref.png b/test/reference/text-glyph-range.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..91de73d
Binary files /dev/null and b/test/reference/text-glyph-range.base.argb32.ref.png differ
diff --git a/test/reference/text-glyph-range.base.rgb24.ref.png b/test/reference/text-glyph-range.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..91de73d
Binary files /dev/null and b/test/reference/text-glyph-range.base.rgb24.ref.png differ
diff --git a/test/reference/text-glyph-range.image16.ref.png b/test/reference/text-glyph-range.image16.ref.png
new file mode 100644 (file)
index 0000000..51daa16
Binary files /dev/null and b/test/reference/text-glyph-range.image16.ref.png differ
diff --git a/test/reference/text-glyph-range.ps.ref.png b/test/reference/text-glyph-range.ps.ref.png
new file mode 100644 (file)
index 0000000..96bc85a
Binary files /dev/null and b/test/reference/text-glyph-range.ps.ref.png differ
diff --git a/test/reference/text-glyph-range.quartz.ref.png b/test/reference/text-glyph-range.quartz.ref.png
new file mode 100644 (file)
index 0000000..e4a14b2
Binary files /dev/null and b/test/reference/text-glyph-range.quartz.ref.png differ
diff --git a/test/reference/text-glyph-range.ref.png b/test/reference/text-glyph-range.ref.png
new file mode 100644 (file)
index 0000000..f279790
Binary files /dev/null and b/test/reference/text-glyph-range.ref.png differ
diff --git a/test/reference/text-glyph-range.traps.argb32.ref.png b/test/reference/text-glyph-range.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..91de73d
Binary files /dev/null and b/test/reference/text-glyph-range.traps.argb32.ref.png differ
diff --git a/test/reference/text-glyph-range.traps.rgb24.ref.png b/test/reference/text-glyph-range.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..91de73d
Binary files /dev/null and b/test/reference/text-glyph-range.traps.rgb24.ref.png differ
diff --git a/test/reference/text-pattern.base.argb32.ref.png b/test/reference/text-pattern.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..d9f3eaa
Binary files /dev/null and b/test/reference/text-pattern.base.argb32.ref.png differ
diff --git a/test/reference/text-pattern.base.rgb24.ref.png b/test/reference/text-pattern.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..70d4a59
Binary files /dev/null and b/test/reference/text-pattern.base.rgb24.ref.png differ
diff --git a/test/reference/text-pattern.pdf.argb32.ref.png b/test/reference/text-pattern.pdf.argb32.ref.png
new file mode 100644 (file)
index 0000000..5eef739
Binary files /dev/null and b/test/reference/text-pattern.pdf.argb32.ref.png differ
diff --git a/test/reference/text-pattern.pdf.rgb24.ref.png b/test/reference/text-pattern.pdf.rgb24.ref.png
new file mode 100644 (file)
index 0000000..27a1195
Binary files /dev/null and b/test/reference/text-pattern.pdf.rgb24.ref.png differ
diff --git a/test/reference/text-pattern.ps2.argb32.ref.png b/test/reference/text-pattern.ps2.argb32.ref.png
new file mode 100644 (file)
index 0000000..bdea146
Binary files /dev/null and b/test/reference/text-pattern.ps2.argb32.ref.png differ
diff --git a/test/reference/text-pattern.ps2.rgb24.ref.png b/test/reference/text-pattern.ps2.rgb24.ref.png
new file mode 100644 (file)
index 0000000..bdea146
Binary files /dev/null and b/test/reference/text-pattern.ps2.rgb24.ref.png differ
diff --git a/test/reference/text-pattern.ps3.argb32.ref.png b/test/reference/text-pattern.ps3.argb32.ref.png
new file mode 100644 (file)
index 0000000..411a531
Binary files /dev/null and b/test/reference/text-pattern.ps3.argb32.ref.png differ
diff --git a/test/reference/text-pattern.ps3.rgb24.ref.png b/test/reference/text-pattern.ps3.rgb24.ref.png
new file mode 100644 (file)
index 0000000..f696a99
Binary files /dev/null and b/test/reference/text-pattern.ps3.rgb24.ref.png differ
diff --git a/test/reference/text-pattern.quartz.argb32.ref.png b/test/reference/text-pattern.quartz.argb32.ref.png
new file mode 100644 (file)
index 0000000..1a15964
Binary files /dev/null and b/test/reference/text-pattern.quartz.argb32.ref.png differ
diff --git a/test/reference/text-pattern.quartz.rgb24.ref.png b/test/reference/text-pattern.quartz.rgb24.ref.png
new file mode 100644 (file)
index 0000000..f19dd76
Binary files /dev/null and b/test/reference/text-pattern.quartz.rgb24.ref.png differ
diff --git a/test/reference/text-pattern.ref.png b/test/reference/text-pattern.ref.png
new file mode 100644 (file)
index 0000000..578cc62
Binary files /dev/null and b/test/reference/text-pattern.ref.png differ
diff --git a/test/reference/text-pattern.svg.argb32.ref.png b/test/reference/text-pattern.svg.argb32.ref.png
new file mode 100644 (file)
index 0000000..f472858
Binary files /dev/null and b/test/reference/text-pattern.svg.argb32.ref.png differ
diff --git a/test/reference/text-pattern.svg.rgb24.ref.png b/test/reference/text-pattern.svg.rgb24.ref.png
new file mode 100644 (file)
index 0000000..2b2064e
Binary files /dev/null and b/test/reference/text-pattern.svg.rgb24.ref.png differ
diff --git a/test/reference/text-pattern.traps.argb32.ref.png b/test/reference/text-pattern.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..d9f3eaa
Binary files /dev/null and b/test/reference/text-pattern.traps.argb32.ref.png differ
diff --git a/test/reference/text-pattern.traps.rgb24.ref.png b/test/reference/text-pattern.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..70d4a59
Binary files /dev/null and b/test/reference/text-pattern.traps.rgb24.ref.png differ
diff --git a/test/reference/text-rotate.base.argb32.ref.png b/test/reference/text-rotate.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..1db9a00
Binary files /dev/null and b/test/reference/text-rotate.base.argb32.ref.png differ
diff --git a/test/reference/text-rotate.base.rgb24.ref.png b/test/reference/text-rotate.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..1db9a00
Binary files /dev/null and b/test/reference/text-rotate.base.rgb24.ref.png differ
diff --git a/test/reference/text-rotate.image16.ref.png b/test/reference/text-rotate.image16.ref.png
new file mode 100644 (file)
index 0000000..fddd002
Binary files /dev/null and b/test/reference/text-rotate.image16.ref.png differ
diff --git a/test/reference/text-rotate.pdf.ref.png b/test/reference/text-rotate.pdf.ref.png
new file mode 100644 (file)
index 0000000..b533075
Binary files /dev/null and b/test/reference/text-rotate.pdf.ref.png differ
diff --git a/test/reference/text-rotate.ps.ref.png b/test/reference/text-rotate.ps.ref.png
new file mode 100644 (file)
index 0000000..c68d02d
Binary files /dev/null and b/test/reference/text-rotate.ps.ref.png differ
diff --git a/test/reference/text-rotate.quartz.ref.png b/test/reference/text-rotate.quartz.ref.png
new file mode 100644 (file)
index 0000000..113e727
Binary files /dev/null and b/test/reference/text-rotate.quartz.ref.png differ
diff --git a/test/reference/text-rotate.ref.png b/test/reference/text-rotate.ref.png
new file mode 100644 (file)
index 0000000..59a0d0a
Binary files /dev/null and b/test/reference/text-rotate.ref.png differ
diff --git a/test/reference/text-rotate.svg.ref.png b/test/reference/text-rotate.svg.ref.png
new file mode 100644 (file)
index 0000000..9d887a0
Binary files /dev/null and b/test/reference/text-rotate.svg.ref.png differ
diff --git a/test/reference/text-rotate.traps.argb32.ref.png b/test/reference/text-rotate.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..1db9a00
Binary files /dev/null and b/test/reference/text-rotate.traps.argb32.ref.png differ
diff --git a/test/reference/text-rotate.traps.rgb24.ref.png b/test/reference/text-rotate.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..1db9a00
Binary files /dev/null and b/test/reference/text-rotate.traps.rgb24.ref.png differ
diff --git a/test/reference/text-rotate.xlib-fallback.ref.png b/test/reference/text-rotate.xlib-fallback.ref.png
new file mode 100644 (file)
index 0000000..ce06330
Binary files /dev/null and b/test/reference/text-rotate.xlib-fallback.ref.png differ
diff --git a/test/reference/text-transform.base.argb32.ref.png b/test/reference/text-transform.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..4603bc5
Binary files /dev/null and b/test/reference/text-transform.base.argb32.ref.png differ
diff --git a/test/reference/text-transform.base.rgb24.ref.png b/test/reference/text-transform.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..4603bc5
Binary files /dev/null and b/test/reference/text-transform.base.rgb24.ref.png differ
diff --git a/test/reference/text-transform.image16.ref.png b/test/reference/text-transform.image16.ref.png
new file mode 100644 (file)
index 0000000..59caca8
Binary files /dev/null and b/test/reference/text-transform.image16.ref.png differ
diff --git a/test/reference/text-transform.pdf.argb32.ref.png b/test/reference/text-transform.pdf.argb32.ref.png
new file mode 100644 (file)
index 0000000..7a2f3a7
Binary files /dev/null and b/test/reference/text-transform.pdf.argb32.ref.png differ
diff --git a/test/reference/text-transform.pdf.rgb24.ref.png b/test/reference/text-transform.pdf.rgb24.ref.png
new file mode 100644 (file)
index 0000000..7a2f3a7
Binary files /dev/null and b/test/reference/text-transform.pdf.rgb24.ref.png differ
diff --git a/test/reference/text-transform.ps2.ref.png b/test/reference/text-transform.ps2.ref.png
new file mode 100644 (file)
index 0000000..07896b3
Binary files /dev/null and b/test/reference/text-transform.ps2.ref.png differ
diff --git a/test/reference/text-transform.ps3.ref.png b/test/reference/text-transform.ps3.ref.png
new file mode 100644 (file)
index 0000000..07896b3
Binary files /dev/null and b/test/reference/text-transform.ps3.ref.png differ
diff --git a/test/reference/text-transform.ref.png b/test/reference/text-transform.ref.png
new file mode 100644 (file)
index 0000000..6f36b9d
Binary files /dev/null and b/test/reference/text-transform.ref.png differ
diff --git a/test/reference/text-transform.svg.ref.png b/test/reference/text-transform.svg.ref.png
new file mode 100644 (file)
index 0000000..1473a64
Binary files /dev/null and b/test/reference/text-transform.svg.ref.png differ
diff --git a/test/reference/text-transform.traps.argb32.ref.png b/test/reference/text-transform.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..4603bc5
Binary files /dev/null and b/test/reference/text-transform.traps.argb32.ref.png differ
diff --git a/test/reference/text-transform.traps.rgb24.ref.png b/test/reference/text-transform.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..4603bc5
Binary files /dev/null and b/test/reference/text-transform.traps.rgb24.ref.png differ
diff --git a/test/reference/tiger.base.argb32.ref.png b/test/reference/tiger.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..bc7f668
Binary files /dev/null and b/test/reference/tiger.base.argb32.ref.png differ
diff --git a/test/reference/tiger.base.rgb24.ref.png b/test/reference/tiger.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..bc7f668
Binary files /dev/null and b/test/reference/tiger.base.rgb24.ref.png differ
diff --git a/test/reference/tiger.ref.png b/test/reference/tiger.ref.png
new file mode 100644 (file)
index 0000000..1a84906
Binary files /dev/null and b/test/reference/tiger.ref.png differ
diff --git a/test/reference/tiger.traps.argb32.ref.png b/test/reference/tiger.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..bc7f668
Binary files /dev/null and b/test/reference/tiger.traps.argb32.ref.png differ
diff --git a/test/reference/tiger.traps.rgb24.ref.png b/test/reference/tiger.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..bc7f668
Binary files /dev/null and b/test/reference/tiger.traps.rgb24.ref.png differ
diff --git a/test/reference/tighten-bounds.argb32.ref.png b/test/reference/tighten-bounds.argb32.ref.png
new file mode 100644 (file)
index 0000000..3fbd8a4
Binary files /dev/null and b/test/reference/tighten-bounds.argb32.ref.png differ
diff --git a/test/reference/tighten-bounds.base.argb32.ref.png b/test/reference/tighten-bounds.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..291a841
Binary files /dev/null and b/test/reference/tighten-bounds.base.argb32.ref.png differ
diff --git a/test/reference/tighten-bounds.base.rgb24.ref.png b/test/reference/tighten-bounds.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..f31c17c
Binary files /dev/null and b/test/reference/tighten-bounds.base.rgb24.ref.png differ
diff --git a/test/reference/tighten-bounds.rgb24.ref.png b/test/reference/tighten-bounds.rgb24.ref.png
new file mode 100644 (file)
index 0000000..5f0161f
Binary files /dev/null and b/test/reference/tighten-bounds.rgb24.ref.png differ
diff --git a/test/reference/tighten-bounds.traps.argb32.ref.png b/test/reference/tighten-bounds.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..9d22568
Binary files /dev/null and b/test/reference/tighten-bounds.traps.argb32.ref.png differ
diff --git a/test/reference/tighten-bounds.traps.rgb24.ref.png b/test/reference/tighten-bounds.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..98fc412
Binary files /dev/null and b/test/reference/tighten-bounds.traps.rgb24.ref.png differ
diff --git a/test/reference/transforms.base.argb32.ref.png b/test/reference/transforms.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..3ce9176
Binary files /dev/null and b/test/reference/transforms.base.argb32.ref.png differ
diff --git a/test/reference/transforms.base.rgb24.ref.png b/test/reference/transforms.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..3ce9176
Binary files /dev/null and b/test/reference/transforms.base.rgb24.ref.png differ
diff --git a/test/reference/transforms.image16.ref.png b/test/reference/transforms.image16.ref.png
new file mode 100644 (file)
index 0000000..e9a1813
Binary files /dev/null and b/test/reference/transforms.image16.ref.png differ
diff --git a/test/reference/transforms.ps2.ref.png b/test/reference/transforms.ps2.ref.png
new file mode 100644 (file)
index 0000000..6d195aa
Binary files /dev/null and b/test/reference/transforms.ps2.ref.png differ
diff --git a/test/reference/transforms.ps3.ref.png b/test/reference/transforms.ps3.ref.png
new file mode 100644 (file)
index 0000000..6d195aa
Binary files /dev/null and b/test/reference/transforms.ps3.ref.png differ
diff --git a/test/reference/transforms.ref.png b/test/reference/transforms.ref.png
new file mode 100644 (file)
index 0000000..1d40acb
Binary files /dev/null and b/test/reference/transforms.ref.png differ
diff --git a/test/reference/transforms.traps.argb32.ref.png b/test/reference/transforms.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..3ce9176
Binary files /dev/null and b/test/reference/transforms.traps.argb32.ref.png differ
diff --git a/test/reference/transforms.traps.rgb24.ref.png b/test/reference/transforms.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..3ce9176
Binary files /dev/null and b/test/reference/transforms.traps.rgb24.ref.png differ
diff --git a/test/reference/translate-show-surface.base.argb32.ref.png b/test/reference/translate-show-surface.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..0a145d9
Binary files /dev/null and b/test/reference/translate-show-surface.base.argb32.ref.png differ
diff --git a/test/reference/translate-show-surface.base.rgb24.ref.png b/test/reference/translate-show-surface.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..0a145d9
Binary files /dev/null and b/test/reference/translate-show-surface.base.rgb24.ref.png differ
diff --git a/test/reference/translate-show-surface.ref.png b/test/reference/translate-show-surface.ref.png
new file mode 100644 (file)
index 0000000..765adc4
Binary files /dev/null and b/test/reference/translate-show-surface.ref.png differ
diff --git a/test/reference/translate-show-surface.traps.argb32.ref.png b/test/reference/translate-show-surface.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..0a145d9
Binary files /dev/null and b/test/reference/translate-show-surface.traps.argb32.ref.png differ
diff --git a/test/reference/translate-show-surface.traps.rgb24.ref.png b/test/reference/translate-show-surface.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..0a145d9
Binary files /dev/null and b/test/reference/translate-show-surface.traps.rgb24.ref.png differ
diff --git a/test/reference/trap-clip.base.argb32.ref.png b/test/reference/trap-clip.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..319d835
Binary files /dev/null and b/test/reference/trap-clip.base.argb32.ref.png differ
diff --git a/test/reference/trap-clip.base.rgb24.ref.png b/test/reference/trap-clip.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..7ac5789
Binary files /dev/null and b/test/reference/trap-clip.base.rgb24.ref.png differ
diff --git a/test/reference/trap-clip.image16.ref.png b/test/reference/trap-clip.image16.ref.png
new file mode 100644 (file)
index 0000000..e9ef2cf
Binary files /dev/null and b/test/reference/trap-clip.image16.ref.png differ
diff --git a/test/reference/trap-clip.ps2.argb32.ref.png b/test/reference/trap-clip.ps2.argb32.ref.png
new file mode 100644 (file)
index 0000000..4db9f8e
Binary files /dev/null and b/test/reference/trap-clip.ps2.argb32.ref.png differ
diff --git a/test/reference/trap-clip.ps2.rgb24.ref.png b/test/reference/trap-clip.ps2.rgb24.ref.png
new file mode 100644 (file)
index 0000000..de309b9
Binary files /dev/null and b/test/reference/trap-clip.ps2.rgb24.ref.png differ
diff --git a/test/reference/trap-clip.ps3.argb32.ref.png b/test/reference/trap-clip.ps3.argb32.ref.png
new file mode 100644 (file)
index 0000000..17d74be
Binary files /dev/null and b/test/reference/trap-clip.ps3.argb32.ref.png differ
diff --git a/test/reference/trap-clip.ps3.rgb24.ref.png b/test/reference/trap-clip.ps3.rgb24.ref.png
new file mode 100644 (file)
index 0000000..236b75c
Binary files /dev/null and b/test/reference/trap-clip.ps3.rgb24.ref.png differ
diff --git a/test/reference/trap-clip.quartz.argb32.ref.png b/test/reference/trap-clip.quartz.argb32.ref.png
new file mode 100644 (file)
index 0000000..e045ea4
Binary files /dev/null and b/test/reference/trap-clip.quartz.argb32.ref.png differ
diff --git a/test/reference/trap-clip.quartz.rgb24.ref.png b/test/reference/trap-clip.quartz.rgb24.ref.png
new file mode 100644 (file)
index 0000000..1044d86
Binary files /dev/null and b/test/reference/trap-clip.quartz.rgb24.ref.png differ
diff --git a/test/reference/trap-clip.ref.png b/test/reference/trap-clip.ref.png
new file mode 100644 (file)
index 0000000..5f5532d
Binary files /dev/null and b/test/reference/trap-clip.ref.png differ
diff --git a/test/reference/trap-clip.rgb24.ref.png b/test/reference/trap-clip.rgb24.ref.png
new file mode 100644 (file)
index 0000000..1e737a0
Binary files /dev/null and b/test/reference/trap-clip.rgb24.ref.png differ
diff --git a/test/reference/trap-clip.test-paginated.argb32.ref.png b/test/reference/trap-clip.test-paginated.argb32.ref.png
new file mode 100644 (file)
index 0000000..7259edc
Binary files /dev/null and b/test/reference/trap-clip.test-paginated.argb32.ref.png differ
diff --git a/test/reference/trap-clip.traps.argb32.ref.png b/test/reference/trap-clip.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..170d378
Binary files /dev/null and b/test/reference/trap-clip.traps.argb32.ref.png differ
diff --git a/test/reference/trap-clip.traps.rgb24.ref.png b/test/reference/trap-clip.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..b1129b0
Binary files /dev/null and b/test/reference/trap-clip.traps.rgb24.ref.png differ
diff --git a/test/reference/twin-antialias-gray.base.argb32.ref.png b/test/reference/twin-antialias-gray.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..bfd4211
Binary files /dev/null and b/test/reference/twin-antialias-gray.base.argb32.ref.png differ
diff --git a/test/reference/twin-antialias-gray.base.rgb24.ref.png b/test/reference/twin-antialias-gray.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..bfd4211
Binary files /dev/null and b/test/reference/twin-antialias-gray.base.rgb24.ref.png differ
diff --git a/test/reference/twin-antialias-gray.image16.ref.png b/test/reference/twin-antialias-gray.image16.ref.png
new file mode 100644 (file)
index 0000000..1fadcb1
Binary files /dev/null and b/test/reference/twin-antialias-gray.image16.ref.png differ
diff --git a/test/reference/twin-antialias-gray.ref.png b/test/reference/twin-antialias-gray.ref.png
new file mode 100644 (file)
index 0000000..e597b64
Binary files /dev/null and b/test/reference/twin-antialias-gray.ref.png differ
diff --git a/test/reference/twin-antialias-gray.traps.argb32.ref.png b/test/reference/twin-antialias-gray.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..bfd4211
Binary files /dev/null and b/test/reference/twin-antialias-gray.traps.argb32.ref.png differ
diff --git a/test/reference/twin-antialias-gray.traps.rgb24.ref.png b/test/reference/twin-antialias-gray.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..bfd4211
Binary files /dev/null and b/test/reference/twin-antialias-gray.traps.rgb24.ref.png differ
diff --git a/test/reference/twin-antialias-mixed.base.argb32.ref.png b/test/reference/twin-antialias-mixed.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..0c91dd1
Binary files /dev/null and b/test/reference/twin-antialias-mixed.base.argb32.ref.png differ
diff --git a/test/reference/twin-antialias-mixed.base.rgb24.ref.png b/test/reference/twin-antialias-mixed.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..0c91dd1
Binary files /dev/null and b/test/reference/twin-antialias-mixed.base.rgb24.ref.png differ
diff --git a/test/reference/twin-antialias-mixed.image16.ref.png b/test/reference/twin-antialias-mixed.image16.ref.png
new file mode 100644 (file)
index 0000000..10c4980
Binary files /dev/null and b/test/reference/twin-antialias-mixed.image16.ref.png differ
diff --git a/test/reference/twin-antialias-mixed.ref.png b/test/reference/twin-antialias-mixed.ref.png
new file mode 100644 (file)
index 0000000..0c91dd1
Binary files /dev/null and b/test/reference/twin-antialias-mixed.ref.png differ
diff --git a/test/reference/twin-antialias-mixed.traps.argb32.ref.png b/test/reference/twin-antialias-mixed.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..0c91dd1
Binary files /dev/null and b/test/reference/twin-antialias-mixed.traps.argb32.ref.png differ
diff --git a/test/reference/twin-antialias-mixed.traps.rgb24.ref.png b/test/reference/twin-antialias-mixed.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..0c91dd1
Binary files /dev/null and b/test/reference/twin-antialias-mixed.traps.rgb24.ref.png differ
diff --git a/test/reference/twin-antialias-none.base.argb32.ref.png b/test/reference/twin-antialias-none.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..42f386d
Binary files /dev/null and b/test/reference/twin-antialias-none.base.argb32.ref.png differ
diff --git a/test/reference/twin-antialias-none.base.rgb24.ref.png b/test/reference/twin-antialias-none.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..42f386d
Binary files /dev/null and b/test/reference/twin-antialias-none.base.rgb24.ref.png differ
diff --git a/test/reference/twin-antialias-none.ref.png b/test/reference/twin-antialias-none.ref.png
new file mode 100644 (file)
index 0000000..d00e501
Binary files /dev/null and b/test/reference/twin-antialias-none.ref.png differ
diff --git a/test/reference/twin-antialias-none.traps.argb32.ref.png b/test/reference/twin-antialias-none.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..42f386d
Binary files /dev/null and b/test/reference/twin-antialias-none.traps.argb32.ref.png differ
diff --git a/test/reference/twin-antialias-none.traps.rgb24.ref.png b/test/reference/twin-antialias-none.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..42f386d
Binary files /dev/null and b/test/reference/twin-antialias-none.traps.rgb24.ref.png differ
diff --git a/test/reference/twin-antialias-subpixel.base.argb32.ref.png b/test/reference/twin-antialias-subpixel.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..bfd4211
Binary files /dev/null and b/test/reference/twin-antialias-subpixel.base.argb32.ref.png differ
diff --git a/test/reference/twin-antialias-subpixel.base.rgb24.ref.png b/test/reference/twin-antialias-subpixel.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..bfd4211
Binary files /dev/null and b/test/reference/twin-antialias-subpixel.base.rgb24.ref.png differ
diff --git a/test/reference/twin-antialias-subpixel.image16.ref.png b/test/reference/twin-antialias-subpixel.image16.ref.png
new file mode 100644 (file)
index 0000000..1fadcb1
Binary files /dev/null and b/test/reference/twin-antialias-subpixel.image16.ref.png differ
diff --git a/test/reference/twin-antialias-subpixel.ref.png b/test/reference/twin-antialias-subpixel.ref.png
new file mode 100644 (file)
index 0000000..e597b64
Binary files /dev/null and b/test/reference/twin-antialias-subpixel.ref.png differ
diff --git a/test/reference/twin-antialias-subpixel.traps.argb32.ref.png b/test/reference/twin-antialias-subpixel.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..bfd4211
Binary files /dev/null and b/test/reference/twin-antialias-subpixel.traps.argb32.ref.png differ
diff --git a/test/reference/twin-antialias-subpixel.traps.rgb24.ref.png b/test/reference/twin-antialias-subpixel.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..bfd4211
Binary files /dev/null and b/test/reference/twin-antialias-subpixel.traps.rgb24.ref.png differ
diff --git a/test/reference/twin.base.argb32.ref.png b/test/reference/twin.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..bfd4211
Binary files /dev/null and b/test/reference/twin.base.argb32.ref.png differ
diff --git a/test/reference/twin.base.rgb24.ref.png b/test/reference/twin.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..bfd4211
Binary files /dev/null and b/test/reference/twin.base.rgb24.ref.png differ
diff --git a/test/reference/twin.image16.ref.png b/test/reference/twin.image16.ref.png
new file mode 100644 (file)
index 0000000..1fadcb1
Binary files /dev/null and b/test/reference/twin.image16.ref.png differ
diff --git a/test/reference/twin.ps.ref.png b/test/reference/twin.ps.ref.png
new file mode 100644 (file)
index 0000000..25c71b4
Binary files /dev/null and b/test/reference/twin.ps.ref.png differ
diff --git a/test/reference/twin.ref.png b/test/reference/twin.ref.png
new file mode 100644 (file)
index 0000000..e597b64
Binary files /dev/null and b/test/reference/twin.ref.png differ
diff --git a/test/reference/twin.svg.ref.png b/test/reference/twin.svg.ref.png
new file mode 100644 (file)
index 0000000..628a83c
Binary files /dev/null and b/test/reference/twin.svg.ref.png differ
diff --git a/test/reference/twin.traps.argb32.ref.png b/test/reference/twin.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..bfd4211
Binary files /dev/null and b/test/reference/twin.traps.argb32.ref.png differ
diff --git a/test/reference/twin.traps.rgb24.ref.png b/test/reference/twin.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..bfd4211
Binary files /dev/null and b/test/reference/twin.traps.rgb24.ref.png differ
diff --git a/test/reference/unaligned-box.base.argb32.ref.png b/test/reference/unaligned-box.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..48e23b8
Binary files /dev/null and b/test/reference/unaligned-box.base.argb32.ref.png differ
diff --git a/test/reference/unaligned-box.base.rgb24.ref.png b/test/reference/unaligned-box.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..48e23b8
Binary files /dev/null and b/test/reference/unaligned-box.base.rgb24.ref.png differ
diff --git a/test/reference/unaligned-box.ref.png b/test/reference/unaligned-box.ref.png
new file mode 100644 (file)
index 0000000..ec7c489
Binary files /dev/null and b/test/reference/unaligned-box.ref.png differ
diff --git a/test/reference/unaligned-box.traps.argb32.ref.png b/test/reference/unaligned-box.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..48e23b8
Binary files /dev/null and b/test/reference/unaligned-box.traps.argb32.ref.png differ
diff --git a/test/reference/unaligned-box.traps.rgb24.ref.png b/test/reference/unaligned-box.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..48e23b8
Binary files /dev/null and b/test/reference/unaligned-box.traps.rgb24.ref.png differ
diff --git a/test/reference/unantialiased-shapes.base.argb32.ref.png b/test/reference/unantialiased-shapes.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..a55ba88
Binary files /dev/null and b/test/reference/unantialiased-shapes.base.argb32.ref.png differ
diff --git a/test/reference/unantialiased-shapes.base.rgb24.ref.png b/test/reference/unantialiased-shapes.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..a55ba88
Binary files /dev/null and b/test/reference/unantialiased-shapes.base.rgb24.ref.png differ
diff --git a/test/reference/unantialiased-shapes.quartz.ref.png b/test/reference/unantialiased-shapes.quartz.ref.png
new file mode 100644 (file)
index 0000000..349ece7
Binary files /dev/null and b/test/reference/unantialiased-shapes.quartz.ref.png differ
diff --git a/test/reference/unantialiased-shapes.ref.png b/test/reference/unantialiased-shapes.ref.png
new file mode 100644 (file)
index 0000000..d9f466e
Binary files /dev/null and b/test/reference/unantialiased-shapes.ref.png differ
diff --git a/test/reference/unantialiased-shapes.traps.argb32.ref.png b/test/reference/unantialiased-shapes.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..a55ba88
Binary files /dev/null and b/test/reference/unantialiased-shapes.traps.argb32.ref.png differ
diff --git a/test/reference/unantialiased-shapes.traps.rgb24.ref.png b/test/reference/unantialiased-shapes.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..a55ba88
Binary files /dev/null and b/test/reference/unantialiased-shapes.traps.rgb24.ref.png differ
diff --git a/test/reference/unbounded-operator.argb32.ref.png b/test/reference/unbounded-operator.argb32.ref.png
new file mode 100644 (file)
index 0000000..c91a4b7
Binary files /dev/null and b/test/reference/unbounded-operator.argb32.ref.png differ
diff --git a/test/reference/unbounded-operator.base.argb32.ref.png b/test/reference/unbounded-operator.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..dfb9031
Binary files /dev/null and b/test/reference/unbounded-operator.base.argb32.ref.png differ
diff --git a/test/reference/unbounded-operator.base.rgb24.ref.png b/test/reference/unbounded-operator.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..6a0c5b3
Binary files /dev/null and b/test/reference/unbounded-operator.base.rgb24.ref.png differ
diff --git a/test/reference/unbounded-operator.gl.argb32.xfail.png b/test/reference/unbounded-operator.gl.argb32.xfail.png
new file mode 100644 (file)
index 0000000..34e32eb
Binary files /dev/null and b/test/reference/unbounded-operator.gl.argb32.xfail.png differ
diff --git a/test/reference/unbounded-operator.gl.rgb24.xfail.png b/test/reference/unbounded-operator.gl.rgb24.xfail.png
new file mode 100644 (file)
index 0000000..b91da8f
Binary files /dev/null and b/test/reference/unbounded-operator.gl.rgb24.xfail.png differ
diff --git a/test/reference/unbounded-operator.image16.ref.png b/test/reference/unbounded-operator.image16.ref.png
new file mode 100644 (file)
index 0000000..17dfb46
Binary files /dev/null and b/test/reference/unbounded-operator.image16.ref.png differ
diff --git a/test/reference/unbounded-operator.pdf.argb32.ref.png b/test/reference/unbounded-operator.pdf.argb32.ref.png
new file mode 100644 (file)
index 0000000..4aa476d
Binary files /dev/null and b/test/reference/unbounded-operator.pdf.argb32.ref.png differ
diff --git a/test/reference/unbounded-operator.ps2.argb32.ref.png b/test/reference/unbounded-operator.ps2.argb32.ref.png
new file mode 100644 (file)
index 0000000..4aa476d
Binary files /dev/null and b/test/reference/unbounded-operator.ps2.argb32.ref.png differ
diff --git a/test/reference/unbounded-operator.ps3.argb32.ref.png b/test/reference/unbounded-operator.ps3.argb32.ref.png
new file mode 100644 (file)
index 0000000..4aa476d
Binary files /dev/null and b/test/reference/unbounded-operator.ps3.argb32.ref.png differ
diff --git a/test/reference/unbounded-operator.quartz.argb32.ref.png b/test/reference/unbounded-operator.quartz.argb32.ref.png
new file mode 100644 (file)
index 0000000..b2e9916
Binary files /dev/null and b/test/reference/unbounded-operator.quartz.argb32.ref.png differ
diff --git a/test/reference/unbounded-operator.quartz.rgb24.ref.png b/test/reference/unbounded-operator.quartz.rgb24.ref.png
new file mode 100644 (file)
index 0000000..ea41dc8
Binary files /dev/null and b/test/reference/unbounded-operator.quartz.rgb24.ref.png differ
diff --git a/test/reference/unbounded-operator.rgb24.ref.png b/test/reference/unbounded-operator.rgb24.ref.png
new file mode 100644 (file)
index 0000000..d383d66
Binary files /dev/null and b/test/reference/unbounded-operator.rgb24.ref.png differ
diff --git a/test/reference/unbounded-operator.svg12.argb32.ref.png b/test/reference/unbounded-operator.svg12.argb32.ref.png
new file mode 100644 (file)
index 0000000..45b173f
Binary files /dev/null and b/test/reference/unbounded-operator.svg12.argb32.ref.png differ
diff --git a/test/reference/unbounded-operator.svg12.rgb24.xfail.png b/test/reference/unbounded-operator.svg12.rgb24.xfail.png
new file mode 100644 (file)
index 0000000..828a9db
Binary files /dev/null and b/test/reference/unbounded-operator.svg12.rgb24.xfail.png differ
diff --git a/test/reference/unbounded-operator.traps.argb32.ref.png b/test/reference/unbounded-operator.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..80a57b5
Binary files /dev/null and b/test/reference/unbounded-operator.traps.argb32.ref.png differ
diff --git a/test/reference/unbounded-operator.traps.rgb24.ref.png b/test/reference/unbounded-operator.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..1c0276c
Binary files /dev/null and b/test/reference/unbounded-operator.traps.rgb24.ref.png differ
diff --git a/test/reference/unclosed-strokes.base.argb32.ref.png b/test/reference/unclosed-strokes.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..cfd0f44
Binary files /dev/null and b/test/reference/unclosed-strokes.base.argb32.ref.png differ
diff --git a/test/reference/unclosed-strokes.base.rgb24.ref.png b/test/reference/unclosed-strokes.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..cfd0f44
Binary files /dev/null and b/test/reference/unclosed-strokes.base.rgb24.ref.png differ
diff --git a/test/reference/unclosed-strokes.ref.png b/test/reference/unclosed-strokes.ref.png
new file mode 100644 (file)
index 0000000..6b1e388
Binary files /dev/null and b/test/reference/unclosed-strokes.ref.png differ
diff --git a/test/reference/unclosed-strokes.traps.argb32.ref.png b/test/reference/unclosed-strokes.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..cfd0f44
Binary files /dev/null and b/test/reference/unclosed-strokes.traps.argb32.ref.png differ
diff --git a/test/reference/unclosed-strokes.traps.rgb24.ref.png b/test/reference/unclosed-strokes.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..cfd0f44
Binary files /dev/null and b/test/reference/unclosed-strokes.traps.rgb24.ref.png differ
diff --git a/test/reference/user-font-mask.base.argb32.ref.png b/test/reference/user-font-mask.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..02a9a5e
Binary files /dev/null and b/test/reference/user-font-mask.base.argb32.ref.png differ
diff --git a/test/reference/user-font-mask.base.rgb24.ref.png b/test/reference/user-font-mask.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..02a9a5e
Binary files /dev/null and b/test/reference/user-font-mask.base.rgb24.ref.png differ
diff --git a/test/reference/user-font-mask.image16.ref.png b/test/reference/user-font-mask.image16.ref.png
new file mode 100644 (file)
index 0000000..0a63dde
Binary files /dev/null and b/test/reference/user-font-mask.image16.ref.png differ
diff --git a/test/reference/user-font-mask.pdf.ref.png b/test/reference/user-font-mask.pdf.ref.png
new file mode 100644 (file)
index 0000000..ebf1485
Binary files /dev/null and b/test/reference/user-font-mask.pdf.ref.png differ
diff --git a/test/reference/user-font-mask.ps2.ref.png b/test/reference/user-font-mask.ps2.ref.png
new file mode 100644 (file)
index 0000000..ebf1485
Binary files /dev/null and b/test/reference/user-font-mask.ps2.ref.png differ
diff --git a/test/reference/user-font-mask.ps3.ref.png b/test/reference/user-font-mask.ps3.ref.png
new file mode 100644 (file)
index 0000000..ebf1485
Binary files /dev/null and b/test/reference/user-font-mask.ps3.ref.png differ
diff --git a/test/reference/user-font-mask.ref.png b/test/reference/user-font-mask.ref.png
new file mode 100644 (file)
index 0000000..02a9a5e
Binary files /dev/null and b/test/reference/user-font-mask.ref.png differ
diff --git a/test/reference/user-font-mask.svg.ref.png b/test/reference/user-font-mask.svg.ref.png
new file mode 100644 (file)
index 0000000..1a8f2c8
Binary files /dev/null and b/test/reference/user-font-mask.svg.ref.png differ
diff --git a/test/reference/user-font-mask.traps.argb32.ref.png b/test/reference/user-font-mask.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..02a9a5e
Binary files /dev/null and b/test/reference/user-font-mask.traps.argb32.ref.png differ
diff --git a/test/reference/user-font-mask.traps.rgb24.ref.png b/test/reference/user-font-mask.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..02a9a5e
Binary files /dev/null and b/test/reference/user-font-mask.traps.rgb24.ref.png differ
diff --git a/test/reference/user-font-proxy.base.argb32.ref.png b/test/reference/user-font-proxy.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..92ce636
Binary files /dev/null and b/test/reference/user-font-proxy.base.argb32.ref.png differ
diff --git a/test/reference/user-font-proxy.base.rgb24.ref.png b/test/reference/user-font-proxy.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..92ce636
Binary files /dev/null and b/test/reference/user-font-proxy.base.rgb24.ref.png differ
diff --git a/test/reference/user-font-proxy.image16.ref.png b/test/reference/user-font-proxy.image16.ref.png
new file mode 100644 (file)
index 0000000..5b44351
Binary files /dev/null and b/test/reference/user-font-proxy.image16.ref.png differ
diff --git a/test/reference/user-font-proxy.pdf.argb32.ref.png b/test/reference/user-font-proxy.pdf.argb32.ref.png
new file mode 100644 (file)
index 0000000..cffa9ed
Binary files /dev/null and b/test/reference/user-font-proxy.pdf.argb32.ref.png differ
diff --git a/test/reference/user-font-proxy.pdf.ref.png b/test/reference/user-font-proxy.pdf.ref.png
new file mode 100644 (file)
index 0000000..afe7cb0
Binary files /dev/null and b/test/reference/user-font-proxy.pdf.ref.png differ
diff --git a/test/reference/user-font-proxy.pdf.rgb24.ref.png b/test/reference/user-font-proxy.pdf.rgb24.ref.png
new file mode 100644 (file)
index 0000000..cffa9ed
Binary files /dev/null and b/test/reference/user-font-proxy.pdf.rgb24.ref.png differ
diff --git a/test/reference/user-font-proxy.ps.ref.png b/test/reference/user-font-proxy.ps.ref.png
new file mode 100644 (file)
index 0000000..a7b348b
Binary files /dev/null and b/test/reference/user-font-proxy.ps.ref.png differ
diff --git a/test/reference/user-font-proxy.quartz.ref.png b/test/reference/user-font-proxy.quartz.ref.png
new file mode 100644 (file)
index 0000000..3bead3d
Binary files /dev/null and b/test/reference/user-font-proxy.quartz.ref.png differ
diff --git a/test/reference/user-font-proxy.ref.png b/test/reference/user-font-proxy.ref.png
new file mode 100644 (file)
index 0000000..1217ca3
Binary files /dev/null and b/test/reference/user-font-proxy.ref.png differ
diff --git a/test/reference/user-font-proxy.svg.ref.png b/test/reference/user-font-proxy.svg.ref.png
new file mode 100644 (file)
index 0000000..6c45848
Binary files /dev/null and b/test/reference/user-font-proxy.svg.ref.png differ
diff --git a/test/reference/user-font-proxy.traps.argb32.ref.png b/test/reference/user-font-proxy.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..92ce636
Binary files /dev/null and b/test/reference/user-font-proxy.traps.argb32.ref.png differ
diff --git a/test/reference/user-font-proxy.traps.rgb24.ref.png b/test/reference/user-font-proxy.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..92ce636
Binary files /dev/null and b/test/reference/user-font-proxy.traps.rgb24.ref.png differ
diff --git a/test/reference/user-font-rescale.base.argb32.ref.png b/test/reference/user-font-rescale.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..c644f75
Binary files /dev/null and b/test/reference/user-font-rescale.base.argb32.ref.png differ
diff --git a/test/reference/user-font-rescale.base.rgb24.ref.png b/test/reference/user-font-rescale.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..c644f75
Binary files /dev/null and b/test/reference/user-font-rescale.base.rgb24.ref.png differ
diff --git a/test/reference/user-font-rescale.image16.ref.png b/test/reference/user-font-rescale.image16.ref.png
new file mode 100644 (file)
index 0000000..a6be0d3
Binary files /dev/null and b/test/reference/user-font-rescale.image16.ref.png differ
diff --git a/test/reference/user-font-rescale.ps.ref.png b/test/reference/user-font-rescale.ps.ref.png
new file mode 100644 (file)
index 0000000..1ee4b13
Binary files /dev/null and b/test/reference/user-font-rescale.ps.ref.png differ
diff --git a/test/reference/user-font-rescale.quartz.ref.png b/test/reference/user-font-rescale.quartz.ref.png
new file mode 100644 (file)
index 0000000..5fa2984
Binary files /dev/null and b/test/reference/user-font-rescale.quartz.ref.png differ
diff --git a/test/reference/user-font-rescale.ref.png b/test/reference/user-font-rescale.ref.png
new file mode 100644 (file)
index 0000000..c644f75
Binary files /dev/null and b/test/reference/user-font-rescale.ref.png differ
diff --git a/test/reference/user-font-rescale.svg.ref.png b/test/reference/user-font-rescale.svg.ref.png
new file mode 100644 (file)
index 0000000..6ed2a19
Binary files /dev/null and b/test/reference/user-font-rescale.svg.ref.png differ
diff --git a/test/reference/user-font-rescale.traps.argb32.ref.png b/test/reference/user-font-rescale.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..c644f75
Binary files /dev/null and b/test/reference/user-font-rescale.traps.argb32.ref.png differ
diff --git a/test/reference/user-font-rescale.traps.rgb24.ref.png b/test/reference/user-font-rescale.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..c644f75
Binary files /dev/null and b/test/reference/user-font-rescale.traps.rgb24.ref.png differ
diff --git a/test/reference/user-font.base.argb32.ref.png b/test/reference/user-font.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..0ea3d17
Binary files /dev/null and b/test/reference/user-font.base.argb32.ref.png differ
diff --git a/test/reference/user-font.base.rgb24.ref.png b/test/reference/user-font.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..0ea3d17
Binary files /dev/null and b/test/reference/user-font.base.rgb24.ref.png differ
diff --git a/test/reference/user-font.image16.ref.png b/test/reference/user-font.image16.ref.png
new file mode 100644 (file)
index 0000000..fcdfb57
Binary files /dev/null and b/test/reference/user-font.image16.ref.png differ
diff --git a/test/reference/user-font.pdf.ref.png b/test/reference/user-font.pdf.ref.png
new file mode 100644 (file)
index 0000000..de86407
Binary files /dev/null and b/test/reference/user-font.pdf.ref.png differ
diff --git a/test/reference/user-font.ps.ref.png b/test/reference/user-font.ps.ref.png
new file mode 100644 (file)
index 0000000..63f2896
Binary files /dev/null and b/test/reference/user-font.ps.ref.png differ
diff --git a/test/reference/user-font.quartz.ref.png b/test/reference/user-font.quartz.ref.png
new file mode 100644 (file)
index 0000000..8c0ec94
Binary files /dev/null and b/test/reference/user-font.quartz.ref.png differ
diff --git a/test/reference/user-font.ref.png b/test/reference/user-font.ref.png
new file mode 100644 (file)
index 0000000..0906e6c
Binary files /dev/null and b/test/reference/user-font.ref.png differ
diff --git a/test/reference/user-font.svg.ref.png b/test/reference/user-font.svg.ref.png
new file mode 100644 (file)
index 0000000..1ff6ea0
Binary files /dev/null and b/test/reference/user-font.svg.ref.png differ
diff --git a/test/reference/user-font.traps.argb32.ref.png b/test/reference/user-font.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..0ea3d17
Binary files /dev/null and b/test/reference/user-font.traps.argb32.ref.png differ
diff --git a/test/reference/user-font.traps.rgb24.ref.png b/test/reference/user-font.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..0ea3d17
Binary files /dev/null and b/test/reference/user-font.traps.rgb24.ref.png differ
diff --git a/test/reference/white-in-noop.base.argb32.ref.png b/test/reference/white-in-noop.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..f42bb8d
Binary files /dev/null and b/test/reference/white-in-noop.base.argb32.ref.png differ
diff --git a/test/reference/white-in-noop.base.rgb24.ref.png b/test/reference/white-in-noop.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..f42bb8d
Binary files /dev/null and b/test/reference/white-in-noop.base.rgb24.ref.png differ
diff --git a/test/reference/white-in-noop.ref.png b/test/reference/white-in-noop.ref.png
new file mode 100644 (file)
index 0000000..f42bb8d
Binary files /dev/null and b/test/reference/white-in-noop.ref.png differ
diff --git a/test/reference/white-in-noop.traps.argb32.ref.png b/test/reference/white-in-noop.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..f42bb8d
Binary files /dev/null and b/test/reference/white-in-noop.traps.argb32.ref.png differ
diff --git a/test/reference/white-in-noop.traps.rgb24.ref.png b/test/reference/white-in-noop.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..f42bb8d
Binary files /dev/null and b/test/reference/white-in-noop.traps.rgb24.ref.png differ
diff --git a/test/reference/world-map-fill.base.argb32.ref.png b/test/reference/world-map-fill.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..926c1a8
Binary files /dev/null and b/test/reference/world-map-fill.base.argb32.ref.png differ
diff --git a/test/reference/world-map-fill.base.rgb24.ref.png b/test/reference/world-map-fill.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..926c1a8
Binary files /dev/null and b/test/reference/world-map-fill.base.rgb24.ref.png differ
diff --git a/test/reference/world-map-fill.image16.ref.png b/test/reference/world-map-fill.image16.ref.png
new file mode 100644 (file)
index 0000000..c97cb57
Binary files /dev/null and b/test/reference/world-map-fill.image16.ref.png differ
diff --git a/test/reference/world-map-fill.ref.png b/test/reference/world-map-fill.ref.png
new file mode 100644 (file)
index 0000000..50b7142
Binary files /dev/null and b/test/reference/world-map-fill.ref.png differ
diff --git a/test/reference/world-map-fill.traps.argb32.ref.png b/test/reference/world-map-fill.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..926c1a8
Binary files /dev/null and b/test/reference/world-map-fill.traps.argb32.ref.png differ
diff --git a/test/reference/world-map-fill.traps.rgb24.ref.png b/test/reference/world-map-fill.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..926c1a8
Binary files /dev/null and b/test/reference/world-map-fill.traps.rgb24.ref.png differ
diff --git a/test/reference/world-map-stroke.base.argb32.ref.png b/test/reference/world-map-stroke.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..d00c945
Binary files /dev/null and b/test/reference/world-map-stroke.base.argb32.ref.png differ
diff --git a/test/reference/world-map-stroke.base.rgb24.ref.png b/test/reference/world-map-stroke.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..d00c945
Binary files /dev/null and b/test/reference/world-map-stroke.base.rgb24.ref.png differ
diff --git a/test/reference/world-map-stroke.image16.ref.png b/test/reference/world-map-stroke.image16.ref.png
new file mode 100644 (file)
index 0000000..771ff0b
Binary files /dev/null and b/test/reference/world-map-stroke.image16.ref.png differ
diff --git a/test/reference/world-map-stroke.ref.png b/test/reference/world-map-stroke.ref.png
new file mode 100644 (file)
index 0000000..6e2f737
Binary files /dev/null and b/test/reference/world-map-stroke.ref.png differ
diff --git a/test/reference/world-map-stroke.traps.argb32.ref.png b/test/reference/world-map-stroke.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..d00c945
Binary files /dev/null and b/test/reference/world-map-stroke.traps.argb32.ref.png differ
diff --git a/test/reference/world-map-stroke.traps.rgb24.ref.png b/test/reference/world-map-stroke.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..d00c945
Binary files /dev/null and b/test/reference/world-map-stroke.traps.rgb24.ref.png differ
diff --git a/test/reference/world-map.base.argb32.ref.png b/test/reference/world-map.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..6bae50d
Binary files /dev/null and b/test/reference/world-map.base.argb32.ref.png differ
diff --git a/test/reference/world-map.base.rgb24.ref.png b/test/reference/world-map.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..6bae50d
Binary files /dev/null and b/test/reference/world-map.base.rgb24.ref.png differ
diff --git a/test/reference/world-map.image16.ref.png b/test/reference/world-map.image16.ref.png
new file mode 100644 (file)
index 0000000..ea7ae8a
Binary files /dev/null and b/test/reference/world-map.image16.ref.png differ
diff --git a/test/reference/world-map.ref.png b/test/reference/world-map.ref.png
new file mode 100644 (file)
index 0000000..36d40c0
Binary files /dev/null and b/test/reference/world-map.ref.png differ
diff --git a/test/reference/world-map.traps.argb32.ref.png b/test/reference/world-map.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..6bae50d
Binary files /dev/null and b/test/reference/world-map.traps.argb32.ref.png differ
diff --git a/test/reference/world-map.traps.rgb24.ref.png b/test/reference/world-map.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..6bae50d
Binary files /dev/null and b/test/reference/world-map.traps.rgb24.ref.png differ
diff --git a/test/reference/xcb-huge-image-shm.base.argb32.ref.png b/test/reference/xcb-huge-image-shm.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..a0b24c8
Binary files /dev/null and b/test/reference/xcb-huge-image-shm.base.argb32.ref.png differ
diff --git a/test/reference/xcb-huge-image-shm.base.rgb24.ref.png b/test/reference/xcb-huge-image-shm.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..a0b24c8
Binary files /dev/null and b/test/reference/xcb-huge-image-shm.base.rgb24.ref.png differ
diff --git a/test/reference/xcb-huge-image-shm.ref.png b/test/reference/xcb-huge-image-shm.ref.png
new file mode 100644 (file)
index 0000000..a0b24c8
Binary files /dev/null and b/test/reference/xcb-huge-image-shm.ref.png differ
diff --git a/test/reference/xcb-huge-image-shm.traps.argb32.ref.png b/test/reference/xcb-huge-image-shm.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..a0b24c8
Binary files /dev/null and b/test/reference/xcb-huge-image-shm.traps.argb32.ref.png differ
diff --git a/test/reference/xcb-huge-image-shm.traps.rgb24.ref.png b/test/reference/xcb-huge-image-shm.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..a0b24c8
Binary files /dev/null and b/test/reference/xcb-huge-image-shm.traps.rgb24.ref.png differ
diff --git a/test/reference/xcb-snapshot-assert.base.argb32.ref.png b/test/reference/xcb-snapshot-assert.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..850ce59
Binary files /dev/null and b/test/reference/xcb-snapshot-assert.base.argb32.ref.png differ
diff --git a/test/reference/xcb-snapshot-assert.base.rgb24.ref.png b/test/reference/xcb-snapshot-assert.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..850ce59
Binary files /dev/null and b/test/reference/xcb-snapshot-assert.base.rgb24.ref.png differ
diff --git a/test/reference/xcb-snapshot-assert.ref.png b/test/reference/xcb-snapshot-assert.ref.png
new file mode 100644 (file)
index 0000000..850ce59
Binary files /dev/null and b/test/reference/xcb-snapshot-assert.ref.png differ
diff --git a/test/reference/xcb-snapshot-assert.traps.argb32.ref.png b/test/reference/xcb-snapshot-assert.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..850ce59
Binary files /dev/null and b/test/reference/xcb-snapshot-assert.traps.argb32.ref.png differ
diff --git a/test/reference/xcb-snapshot-assert.traps.rgb24.ref.png b/test/reference/xcb-snapshot-assert.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..850ce59
Binary files /dev/null and b/test/reference/xcb-snapshot-assert.traps.rgb24.ref.png differ
diff --git a/test/reference/xcb-stress-cache.base.argb32.ref.png b/test/reference/xcb-stress-cache.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..850ce59
Binary files /dev/null and b/test/reference/xcb-stress-cache.base.argb32.ref.png differ
diff --git a/test/reference/xcb-stress-cache.base.rgb24.ref.png b/test/reference/xcb-stress-cache.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..850ce59
Binary files /dev/null and b/test/reference/xcb-stress-cache.base.rgb24.ref.png differ
diff --git a/test/reference/xcb-stress-cache.ref.png b/test/reference/xcb-stress-cache.ref.png
new file mode 100644 (file)
index 0000000..850ce59
Binary files /dev/null and b/test/reference/xcb-stress-cache.ref.png differ
diff --git a/test/reference/xcb-stress-cache.traps.argb32.ref.png b/test/reference/xcb-stress-cache.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..850ce59
Binary files /dev/null and b/test/reference/xcb-stress-cache.traps.argb32.ref.png differ
diff --git a/test/reference/xcb-stress-cache.traps.rgb24.ref.png b/test/reference/xcb-stress-cache.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..850ce59
Binary files /dev/null and b/test/reference/xcb-stress-cache.traps.rgb24.ref.png differ
diff --git a/test/reference/xcb-surface-source.argb32.ref.png b/test/reference/xcb-surface-source.argb32.ref.png
new file mode 100644 (file)
index 0000000..0182972
Binary files /dev/null and b/test/reference/xcb-surface-source.argb32.ref.png differ
diff --git a/test/reference/xcb-surface-source.base.argb32.ref.png b/test/reference/xcb-surface-source.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..0182972
Binary files /dev/null and b/test/reference/xcb-surface-source.base.argb32.ref.png differ
diff --git a/test/reference/xcb-surface-source.base.rgb24.ref.png b/test/reference/xcb-surface-source.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..0d68a82
Binary files /dev/null and b/test/reference/xcb-surface-source.base.rgb24.ref.png differ
diff --git a/test/reference/xcb-surface-source.image16.ref.png b/test/reference/xcb-surface-source.image16.ref.png
new file mode 100644 (file)
index 0000000..2a7460e
Binary files /dev/null and b/test/reference/xcb-surface-source.image16.ref.png differ
diff --git a/test/reference/xcb-surface-source.ps.argb32.ref.png b/test/reference/xcb-surface-source.ps.argb32.ref.png
new file mode 100644 (file)
index 0000000..910f895
Binary files /dev/null and b/test/reference/xcb-surface-source.ps.argb32.ref.png differ
diff --git a/test/reference/xcb-surface-source.ps.rgb24.ref.png b/test/reference/xcb-surface-source.ps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..636b0f5
Binary files /dev/null and b/test/reference/xcb-surface-source.ps.rgb24.ref.png differ
diff --git a/test/reference/xcb-surface-source.rgb24.ref.png b/test/reference/xcb-surface-source.rgb24.ref.png
new file mode 100644 (file)
index 0000000..0d68a82
Binary files /dev/null and b/test/reference/xcb-surface-source.rgb24.ref.png differ
diff --git a/test/reference/xcb-surface-source.traps.argb32.ref.png b/test/reference/xcb-surface-source.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..0182972
Binary files /dev/null and b/test/reference/xcb-surface-source.traps.argb32.ref.png differ
diff --git a/test/reference/xcb-surface-source.traps.rgb24.ref.png b/test/reference/xcb-surface-source.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..0d68a82
Binary files /dev/null and b/test/reference/xcb-surface-source.traps.rgb24.ref.png differ
diff --git a/test/reference/xcomposite-projection.base.argb32.ref.png b/test/reference/xcomposite-projection.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..ea4dddf
Binary files /dev/null and b/test/reference/xcomposite-projection.base.argb32.ref.png differ
diff --git a/test/reference/xcomposite-projection.base.rgb24.ref.png b/test/reference/xcomposite-projection.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..ea4dddf
Binary files /dev/null and b/test/reference/xcomposite-projection.base.rgb24.ref.png differ
diff --git a/test/reference/xcomposite-projection.image16.ref.png b/test/reference/xcomposite-projection.image16.ref.png
new file mode 100644 (file)
index 0000000..f9bc3b6
Binary files /dev/null and b/test/reference/xcomposite-projection.image16.ref.png differ
diff --git a/test/reference/xcomposite-projection.quartz.ref.png b/test/reference/xcomposite-projection.quartz.ref.png
new file mode 100644 (file)
index 0000000..0221607
Binary files /dev/null and b/test/reference/xcomposite-projection.quartz.ref.png differ
diff --git a/test/reference/xcomposite-projection.ref.png b/test/reference/xcomposite-projection.ref.png
new file mode 100644 (file)
index 0000000..abbea08
Binary files /dev/null and b/test/reference/xcomposite-projection.ref.png differ
diff --git a/test/reference/xcomposite-projection.traps.argb32.ref.png b/test/reference/xcomposite-projection.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..ea4dddf
Binary files /dev/null and b/test/reference/xcomposite-projection.traps.argb32.ref.png differ
diff --git a/test/reference/xcomposite-projection.traps.rgb24.ref.png b/test/reference/xcomposite-projection.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..ea4dddf
Binary files /dev/null and b/test/reference/xcomposite-projection.traps.rgb24.ref.png differ
diff --git a/test/reference/xlib-expose-event.base.argb32.ref.png b/test/reference/xlib-expose-event.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..fd71f5a
Binary files /dev/null and b/test/reference/xlib-expose-event.base.argb32.ref.png differ
diff --git a/test/reference/xlib-expose-event.base.rgb24.ref.png b/test/reference/xlib-expose-event.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..fd71f5a
Binary files /dev/null and b/test/reference/xlib-expose-event.base.rgb24.ref.png differ
diff --git a/test/reference/xlib-expose-event.image16.ref.png b/test/reference/xlib-expose-event.image16.ref.png
new file mode 100644 (file)
index 0000000..54d5f26
Binary files /dev/null and b/test/reference/xlib-expose-event.image16.ref.png differ
diff --git a/test/reference/xlib-expose-event.ps.ref.png b/test/reference/xlib-expose-event.ps.ref.png
new file mode 100644 (file)
index 0000000..88f49c1
Binary files /dev/null and b/test/reference/xlib-expose-event.ps.ref.png differ
diff --git a/test/reference/xlib-expose-event.ref.png b/test/reference/xlib-expose-event.ref.png
new file mode 100644 (file)
index 0000000..1cca0e7
Binary files /dev/null and b/test/reference/xlib-expose-event.ref.png differ
diff --git a/test/reference/xlib-expose-event.traps.argb32.ref.png b/test/reference/xlib-expose-event.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..fd71f5a
Binary files /dev/null and b/test/reference/xlib-expose-event.traps.argb32.ref.png differ
diff --git a/test/reference/xlib-expose-event.traps.rgb24.ref.png b/test/reference/xlib-expose-event.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..fd71f5a
Binary files /dev/null and b/test/reference/xlib-expose-event.traps.rgb24.ref.png differ
diff --git a/test/reference/xlib-surface-source.argb32.ref.png b/test/reference/xlib-surface-source.argb32.ref.png
new file mode 100644 (file)
index 0000000..0182972
Binary files /dev/null and b/test/reference/xlib-surface-source.argb32.ref.png differ
diff --git a/test/reference/xlib-surface-source.base.argb32.ref.png b/test/reference/xlib-surface-source.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..0182972
Binary files /dev/null and b/test/reference/xlib-surface-source.base.argb32.ref.png differ
diff --git a/test/reference/xlib-surface-source.base.rgb24.ref.png b/test/reference/xlib-surface-source.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..0d68a82
Binary files /dev/null and b/test/reference/xlib-surface-source.base.rgb24.ref.png differ
diff --git a/test/reference/xlib-surface-source.image16.ref.png b/test/reference/xlib-surface-source.image16.ref.png
new file mode 100644 (file)
index 0000000..2a7460e
Binary files /dev/null and b/test/reference/xlib-surface-source.image16.ref.png differ
diff --git a/test/reference/xlib-surface-source.ps.argb32.ref.png b/test/reference/xlib-surface-source.ps.argb32.ref.png
new file mode 100644 (file)
index 0000000..910f895
Binary files /dev/null and b/test/reference/xlib-surface-source.ps.argb32.ref.png differ
diff --git a/test/reference/xlib-surface-source.ps.rgb24.ref.png b/test/reference/xlib-surface-source.ps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..636b0f5
Binary files /dev/null and b/test/reference/xlib-surface-source.ps.rgb24.ref.png differ
diff --git a/test/reference/xlib-surface-source.rgb24.ref.png b/test/reference/xlib-surface-source.rgb24.ref.png
new file mode 100644 (file)
index 0000000..0d68a82
Binary files /dev/null and b/test/reference/xlib-surface-source.rgb24.ref.png differ
diff --git a/test/reference/xlib-surface-source.svg12.argb32.xfail.png b/test/reference/xlib-surface-source.svg12.argb32.xfail.png
new file mode 100644 (file)
index 0000000..6ebcaf9
Binary files /dev/null and b/test/reference/xlib-surface-source.svg12.argb32.xfail.png differ
diff --git a/test/reference/xlib-surface-source.svg12.rgb24.xfail.png b/test/reference/xlib-surface-source.svg12.rgb24.xfail.png
new file mode 100644 (file)
index 0000000..6ebcaf9
Binary files /dev/null and b/test/reference/xlib-surface-source.svg12.rgb24.xfail.png differ
diff --git a/test/reference/xlib-surface-source.traps.argb32.ref.png b/test/reference/xlib-surface-source.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..0182972
Binary files /dev/null and b/test/reference/xlib-surface-source.traps.argb32.ref.png differ
diff --git a/test/reference/xlib-surface-source.traps.rgb24.ref.png b/test/reference/xlib-surface-source.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..0d68a82
Binary files /dev/null and b/test/reference/xlib-surface-source.traps.rgb24.ref.png differ
diff --git a/test/reference/zero-alpha.base.argb32.ref.png b/test/reference/zero-alpha.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..595c0ae
Binary files /dev/null and b/test/reference/zero-alpha.base.argb32.ref.png differ
diff --git a/test/reference/zero-alpha.base.rgb24.ref.png b/test/reference/zero-alpha.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..595c0ae
Binary files /dev/null and b/test/reference/zero-alpha.base.rgb24.ref.png differ
diff --git a/test/reference/zero-alpha.ref.png b/test/reference/zero-alpha.ref.png
new file mode 100644 (file)
index 0000000..d03727d
Binary files /dev/null and b/test/reference/zero-alpha.ref.png differ
diff --git a/test/reference/zero-alpha.traps.argb32.ref.png b/test/reference/zero-alpha.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..595c0ae
Binary files /dev/null and b/test/reference/zero-alpha.traps.argb32.ref.png differ
diff --git a/test/reference/zero-alpha.traps.rgb24.ref.png b/test/reference/zero-alpha.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..595c0ae
Binary files /dev/null and b/test/reference/zero-alpha.traps.rgb24.ref.png differ
diff --git a/test/reference/zero-mask.base.argb32.ref.png b/test/reference/zero-mask.base.argb32.ref.png
new file mode 100644 (file)
index 0000000..ffae8d9
Binary files /dev/null and b/test/reference/zero-mask.base.argb32.ref.png differ
diff --git a/test/reference/zero-mask.base.rgb24.ref.png b/test/reference/zero-mask.base.rgb24.ref.png
new file mode 100644 (file)
index 0000000..263c3d1
Binary files /dev/null and b/test/reference/zero-mask.base.rgb24.ref.png differ
diff --git a/test/reference/zero-mask.ref.png b/test/reference/zero-mask.ref.png
new file mode 100644 (file)
index 0000000..ffae8d9
Binary files /dev/null and b/test/reference/zero-mask.ref.png differ
diff --git a/test/reference/zero-mask.rgb24.ref.png b/test/reference/zero-mask.rgb24.ref.png
new file mode 100644 (file)
index 0000000..263c3d1
Binary files /dev/null and b/test/reference/zero-mask.rgb24.ref.png differ
diff --git a/test/reference/zero-mask.traps.argb32.ref.png b/test/reference/zero-mask.traps.argb32.ref.png
new file mode 100644 (file)
index 0000000..ffae8d9
Binary files /dev/null and b/test/reference/zero-mask.traps.argb32.ref.png differ
diff --git a/test/reference/zero-mask.traps.rgb24.ref.png b/test/reference/zero-mask.traps.rgb24.ref.png
new file mode 100644 (file)
index 0000000..263c3d1
Binary files /dev/null and b/test/reference/zero-mask.traps.rgb24.ref.png differ
diff --git a/test/reflected-stroke.c b/test/reflected-stroke.c
new file mode 100644 (file)
index 0000000..b11f5a9
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * Copyright © 2008 Chris Wilson
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * the author not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. The author makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THE AUTHOR. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+
+static void
+draw_symbol (cairo_t *cr)
+{
+    double dash[] = {6, 3};
+
+    cairo_rectangle (cr, -25, -25, 50, 50);
+    cairo_stroke (cr);
+
+    cairo_move_to (cr, 0, -25);
+    cairo_curve_to (cr, 12.5, -12.5, 12.5, -12.5, 0, 0);
+    cairo_curve_to (cr, -12.5, 12.5, -12.5, 12.5, 0, 25);
+    cairo_curve_to (cr, 12.5, 12.5, 12.5, 12.5, 0, 0);
+    cairo_stroke (cr);
+
+    cairo_save (cr);
+    cairo_set_dash (cr, dash, ARRAY_LENGTH (dash), 0.);
+    cairo_move_to (cr, 0, 0);
+    cairo_arc (cr, 0, 0, 12.5, 0, 3 * M_PI / 2);
+    cairo_close_path (cr);
+    cairo_stroke (cr);
+    cairo_restore (cr);
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_paint (cr);
+
+    cairo_set_source_rgb (cr, 0, 0, 0);
+
+    cairo_save (cr);
+    cairo_translate (cr, 50, 50);
+    cairo_scale (cr, 1, 1);
+    draw_symbol (cr);
+    cairo_restore (cr);
+
+    cairo_save (cr);
+    cairo_translate (cr, 150, 50);
+    cairo_scale (cr, -1, 1);
+    draw_symbol (cr);
+    cairo_restore (cr);
+
+    cairo_save (cr);
+    cairo_translate (cr, 150, 150);
+    cairo_scale (cr, -1, -1);
+    draw_symbol (cr);
+    cairo_restore (cr);
+
+    cairo_save (cr);
+    cairo_translate (cr, 50, 150);
+    cairo_scale (cr, 1, -1);
+    draw_symbol (cr);
+    cairo_restore (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (reflected_stroke,
+           "Exercises the stroker with a reflected ctm",
+           "stroke, transform", /* keywords */
+           NULL, /* requirements */
+           200, 200,
+           NULL, draw)
diff --git a/test/rel-path.c b/test/rel-path.c
new file mode 100644 (file)
index 0000000..d1ef259
--- /dev/null
@@ -0,0 +1,129 @@
+/*
+ * Copyright © 2005 Keith Packard
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of Keith Packard not be used in
+ * advertising or publicity pertaining to distribution of the software without
+ * specific, written prior permission.  Keith Packard makes no
+ * representations about the suitability of this software for any purpose.  It
+ * is provided "as is" without express or implied warranty.
+ *
+ * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "cairo-test.h"
+
+#define SIZE 10
+
+static cairo_status_t
+invalid_rel_move_to (cairo_surface_t *target)
+{
+    cairo_t *cr;
+    cairo_status_t status;
+
+    cr = cairo_create (target);
+    cairo_rel_move_to (cr, SIZE, SIZE/2);
+    status = cairo_status (cr);
+    cairo_destroy (cr);
+
+    return status;
+}
+
+static cairo_status_t
+invalid_rel_line_to (cairo_surface_t *target)
+{
+    cairo_t *cr;
+    cairo_status_t status;
+
+    cr = cairo_create (target);
+    cairo_rel_line_to (cr, -SIZE, SIZE/2);
+    status = cairo_status (cr);
+    cairo_destroy (cr);
+
+    return status;
+}
+
+static cairo_status_t
+invalid_rel_curve_to (cairo_surface_t *target)
+{
+    cairo_t *cr;
+    cairo_status_t status;
+
+    cr = cairo_create (target);
+    cairo_rel_curve_to (cr,
+                       SIZE/2, -SIZE/2,
+                       SIZE*2/3, -SIZE/3,
+                       SIZE/2, -SIZE);
+    status = cairo_status (cr);
+    cairo_destroy (cr);
+
+    return status;
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    const cairo_test_context_t *ctx = cairo_test_get_context (cr);
+    cairo_status_t status;
+    cairo_test_status_t result;
+
+    /* first test that a relative move without a current point fails... */
+    status = invalid_rel_move_to (cairo_get_target (cr));
+    if (status != CAIRO_STATUS_NO_CURRENT_POINT) {
+       result = cairo_test_status_from_status (ctx, status);
+       if (result == CAIRO_TEST_NO_MEMORY)
+           return result;
+
+       cairo_test_log (ctx, "Error: invalid cairo_rel_move_to() did not raise NO_CURRENT_POINT\n");
+       return result;
+    }
+
+    status = invalid_rel_line_to (cairo_get_target (cr));
+    if (status != CAIRO_STATUS_NO_CURRENT_POINT) {
+       result = cairo_test_status_from_status (ctx, status);
+       if (result == CAIRO_TEST_NO_MEMORY)
+           return result;
+
+       cairo_test_log (ctx, "Error: invalid cairo_rel_line_to() did not raise NO_CURRENT_POINT\n");
+       return result;
+    }
+
+    status = invalid_rel_curve_to (cairo_get_target (cr));
+    if (status != CAIRO_STATUS_NO_CURRENT_POINT) {
+       result = cairo_test_status_from_status (ctx, status);
+       if (result == CAIRO_TEST_NO_MEMORY)
+           return result;
+
+       cairo_test_log (ctx, "Error: invalid cairo_rel_curve_to() did not raise NO_CURRENT_POINT\n");
+       return result;
+    }
+
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_move_to (cr, 0, 0);
+    cairo_rel_move_to (cr, SIZE, SIZE/2);
+    cairo_rel_line_to (cr, -SIZE, SIZE/2);
+    cairo_rel_curve_to (cr,
+                       SIZE/2, -SIZE/2,
+                       SIZE*2/3, -SIZE/3,
+                       SIZE/2, -SIZE);
+
+    cairo_stroke (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (rel_path,
+           "Tests calls to various relative path functions",
+           "path", /* keywords */
+           NULL, /* requirements */
+           SIZE, SIZE,
+           NULL, draw)
diff --git a/test/rgb24-ignore-alpha.c b/test/rgb24-ignore-alpha.c
new file mode 100644 (file)
index 0000000..1c9d57e
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * Copyright © 2007 Red Hat, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ */
+
+#include "cairo-test.h"
+
+#define WIDTH  2
+#define HEIGHT 2
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_surface_t *surface;
+    /* Four green pixels with different "alpha" values, (but which
+     * should be entirely ignored). */
+    uint32_t colors[4] = {
+       0xff00ff00, 0x8800ff00,
+       0x4400ff00, 0x0000ff00
+    };
+
+    surface = cairo_image_surface_create_for_data ((unsigned char *) colors,
+                                                  CAIRO_FORMAT_RGB24, 2, 2, 8);
+
+    cairo_set_source_surface (cr, surface, 0, 0);
+    cairo_paint (cr);
+
+    cairo_surface_finish (surface); /* colors will go out of scope */
+    cairo_surface_destroy (surface);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (rgb24_ignore_alpha,
+           "Test that when using an RGB24 image as a source, there is no alpha channel",
+           "image, alpha", /* keywords */
+           NULL, /* requirements */
+           WIDTH, HEIGHT,
+           NULL, draw)
diff --git a/test/romedalen.jpg b/test/romedalen.jpg
new file mode 100644 (file)
index 0000000..d655e3d
Binary files /dev/null and b/test/romedalen.jpg differ
diff --git a/test/romedalen.png b/test/romedalen.png
new file mode 100644 (file)
index 0000000..0c41eb0
Binary files /dev/null and b/test/romedalen.png differ
diff --git a/test/rotate-image-surface-paint.c b/test/rotate-image-surface-paint.c
new file mode 100644 (file)
index 0000000..42fda6e
--- /dev/null
@@ -0,0 +1,172 @@
+/*
+ * Copyright © 2005, 2007 Red Hat, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ */
+
+#include "cairo-test.h"
+
+#define SIZE 20
+#define PAD 2
+
+static cairo_pattern_t *
+create_image_source (int size)
+{
+    cairo_surface_t *surface;
+    cairo_pattern_t *pattern;
+    cairo_t *cr;
+
+    /* Create an image surface with my favorite four colors in each
+     * quadrant. */
+    surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, size, size);
+    cr = cairo_create (surface);
+    cairo_surface_destroy (surface);
+
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_rectangle (cr, 0, 0, size / 2, size / 2);
+    cairo_fill (cr);
+
+    cairo_set_source_rgb (cr, 1, 0, 0);
+    cairo_rectangle (cr, size / 2, 0, size - size / 2, size / 2);
+    cairo_fill (cr);
+
+    cairo_set_source_rgb (cr, 0, 1, 0);
+    cairo_rectangle (cr, 0, size / 2, size / 2, size - size / 2);
+    cairo_fill (cr);
+
+    cairo_set_source_rgb (cr, 0, 0, 1);
+    cairo_rectangle (cr, size / 2, size / 2, size - size / 2, size - size / 2);
+    cairo_fill (cr);
+
+    pattern = cairo_pattern_create_for_surface (cairo_get_target (cr));
+    cairo_destroy (cr);
+
+    return pattern;
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_pattern_t *source;
+    int surface_size = sqrt ((SIZE - 2*PAD)*(SIZE - 2*PAD)/2);
+
+    /* Use a gray (neutral) background, so we can spot if the backend pads
+     * with any other colour.
+     */
+    cairo_set_source_rgb (cr, .5, .5, .5);
+    cairo_paint (cr);
+
+    cairo_translate(cr, SIZE/2, SIZE/2);
+    cairo_rotate (cr, M_PI / 4.0);
+    cairo_translate (cr, -surface_size/2, -surface_size/2);
+
+    source = create_image_source (surface_size);
+    cairo_pattern_set_filter (source, CAIRO_FILTER_NEAREST);
+    cairo_set_source(cr, source);
+    cairo_pattern_destroy (source);
+
+    cairo_paint (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+static cairo_test_status_t
+clip_draw (cairo_t *cr, int width, int height)
+{
+    cairo_pattern_t *source;
+    int surface_size = sqrt ((SIZE - 2*PAD)*(SIZE - 2*PAD)/2);
+
+    /* Use a gray (neutral) background, so we can spot if the backend pads
+     * with any other colour.
+     */
+    cairo_set_source_rgb (cr, .5, .5, .5);
+    cairo_paint (cr);
+
+    cairo_rectangle (cr, 2*PAD, 2*PAD, SIZE-4*PAD, SIZE-4*PAD);
+    cairo_clip (cr);
+
+    cairo_translate(cr, SIZE/2, SIZE/2);
+    cairo_rotate (cr, M_PI / 4.0);
+    cairo_translate (cr, -surface_size/2, -surface_size/2);
+
+    source = create_image_source (surface_size);
+    cairo_pattern_set_filter (source, CAIRO_FILTER_NEAREST);
+    cairo_set_source(cr, source);
+    cairo_pattern_destroy (source);
+
+    cairo_paint (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+static cairo_test_status_t
+draw_clip (cairo_t *cr, int width, int height)
+{
+    cairo_pattern_t *source;
+    int surface_size = sqrt ((SIZE - 2*PAD)*(SIZE - 2*PAD)/2);
+
+    /* Use a gray (neutral) background, so we can spot if the backend pads
+     * with any other colour.
+     */
+    cairo_set_source_rgb (cr, .5, .5, .5);
+    cairo_paint (cr);
+
+    cairo_translate(cr, SIZE/2, SIZE/2);
+    cairo_rotate (cr, M_PI / 4.0);
+    cairo_translate (cr, -surface_size/2, -surface_size/2);
+
+    cairo_rectangle (cr, PAD, PAD, surface_size-2*PAD, surface_size-2*PAD);
+    cairo_clip (cr);
+
+    source = create_image_source (surface_size);
+    cairo_pattern_set_filter (source, CAIRO_FILTER_NEAREST);
+    cairo_set_source(cr, source);
+    cairo_pattern_destroy (source);
+
+    cairo_paint (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (rotate_image_surface_paint,
+           "Test call sequence: image_surface_create; rotate; set_source_surface; paint"
+           "\nThis test is known to fail on the ps backend currently",
+           "image, transform, paint", /* keywords */
+           NULL, /* requirements */
+           SIZE, SIZE,
+           NULL, draw)
+
+CAIRO_TEST (clip_rotate_image_surface_paint,
+           "Test call sequence: image_surface_create; rotate; set_source_surface; paint"
+           "\nThis test is known to fail on the ps backend currently",
+           "image, transform, paint", /* keywords */
+           NULL, /* requirements */
+           SIZE, SIZE,
+           NULL, clip_draw)
+CAIRO_TEST (rotate_clip_image_surface_paint,
+           "Test call sequence: image_surface_create; rotate; set_source_surface; paint"
+           "\nThis test is known to fail on the ps backend currently",
+           "image, transform, paint", /* keywords */
+           NULL, /* requirements */
+           SIZE, SIZE,
+           NULL, draw_clip)
diff --git a/test/rotated-clip.c b/test/rotated-clip.c
new file mode 100644 (file)
index 0000000..4ca4566
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * Copyright © 2007 Adrian Johnson
+ * Copyright © 2009 Chris Wilson
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Adrian Johnson <ajohnson@redneon.com>
+ *         Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+
+#define PAT_WIDTH  120
+#define PAT_HEIGHT 120
+#define SIZE (PAT_WIDTH*2)
+#define PAD 2
+#define WIDTH (PAD + SIZE + PAD)
+#define HEIGHT WIDTH
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_matrix_t m;
+
+    /* make the output opaque */
+    cairo_set_source_rgb (cr, 0, 0, 0);
+    cairo_paint (cr);
+
+    cairo_translate (cr, PAD, PAD);
+
+    cairo_matrix_init_scale (&m, 2, 1.5);
+    cairo_matrix_rotate (&m, 1);
+    cairo_matrix_translate (&m, -PAT_WIDTH/4.0, -PAT_WIDTH/2.0);
+    cairo_matrix_invert (&m);
+    cairo_set_matrix (cr, &m);
+
+    cairo_rectangle (cr, 0, 0, PAT_WIDTH, PAT_HEIGHT);
+    cairo_clip (cr);
+
+    cairo_set_source_rgba (cr, 1, 0, 1, 0.5);
+    cairo_rectangle (cr, PAT_WIDTH/6.0, PAT_HEIGHT/6.0, PAT_WIDTH/4.0, PAT_HEIGHT/4.0);
+    cairo_fill (cr);
+
+    cairo_set_source_rgba (cr, 0, 1, 1, 0.5);
+    cairo_rectangle (cr, PAT_WIDTH/2.0, PAT_HEIGHT/2.0, PAT_WIDTH/4.0, PAT_HEIGHT/4.0);
+    cairo_fill (cr);
+
+    cairo_set_line_width (cr, 1);
+    cairo_move_to (cr, PAT_WIDTH/6.0, 0);
+    cairo_line_to (cr, 0, 0);
+    cairo_line_to (cr, 0, PAT_HEIGHT/6.0);
+    cairo_set_source_rgb (cr, 1, 0, 0);
+    cairo_stroke (cr);
+
+    cairo_move_to (cr, PAT_WIDTH/6.0, PAT_HEIGHT);
+    cairo_line_to (cr, 0, PAT_HEIGHT);
+    cairo_line_to (cr, 0, 5*PAT_HEIGHT/6.0);
+    cairo_set_source_rgb (cr, 0, 1, 0);
+    cairo_stroke (cr);
+
+    cairo_move_to (cr, 5*PAT_WIDTH/6.0, 0);
+    cairo_line_to (cr, PAT_WIDTH, 0);
+    cairo_line_to (cr, PAT_WIDTH, PAT_HEIGHT/6.0);
+    cairo_set_source_rgb (cr, 0, 0, 1);
+    cairo_stroke (cr);
+
+    cairo_move_to (cr, 5*PAT_WIDTH/6.0, PAT_HEIGHT);
+    cairo_line_to (cr, PAT_WIDTH, PAT_HEIGHT);
+    cairo_line_to (cr, PAT_WIDTH, 5*PAT_HEIGHT/6.0);
+    cairo_set_source_rgb (cr, 1, 1, 0);
+    cairo_stroke (cr);
+
+    cairo_set_source_rgb (cr, 0.5, 0.5, 0.5);
+    cairo_set_line_width (cr, PAT_WIDTH/10.0);
+
+    cairo_move_to (cr, 0,         PAT_HEIGHT/4.0);
+    cairo_line_to (cr, PAT_WIDTH, PAT_HEIGHT/4.0);
+    cairo_stroke (cr);
+
+    cairo_move_to (cr, PAT_WIDTH/4.0,         0);
+    cairo_line_to (cr, PAT_WIDTH/4.0, PAT_WIDTH);
+    cairo_stroke (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (rotated_clip,
+           "Test clipping with non identity pattern matrix",
+           "clip", /* keywords */
+           NULL, /* requirements */
+           WIDTH, HEIGHT,
+           NULL, draw)
diff --git a/test/rounded-rectangle-fill.c b/test/rounded-rectangle-fill.c
new file mode 100644 (file)
index 0000000..d211cf6
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * Copyright © 2009 Chris Wilson
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+
+#define SIZE 80
+
+/* A very simple test to exercise the scan rasterisers with constant regions. */
+
+static void
+rounded_rectangle (cairo_t *cr, int x, int y, int w, int h, int r)
+{
+    cairo_new_sub_path (cr);
+    cairo_arc (cr, x + r, y + r, r, M_PI, 3 * M_PI / 2);
+    cairo_arc (cr, x + w - r, y + r, r, 3 *M_PI / 2, 2 * M_PI);
+    cairo_arc (cr, x + w - r, y + h - r, r, 0, M_PI / 2);
+    cairo_arc (cr, x + r, y + h - r, r, M_PI / 2, M_PI);
+    cairo_close_path (cr);
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    /* Paint background white, then draw in black. */
+    cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); /* white */
+    cairo_paint (cr);
+    cairo_set_source_rgb (cr, 0.0, 0.0, 0.0); /* black */
+
+    cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD);
+    rounded_rectangle (cr, 5, 5, width-10, height-10, 15);
+    rounded_rectangle (cr, 15, 15, width-30, height-30, 5);
+    cairo_fill (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (rounded_rectangle_fill,
+           "Tests handling of rounded rectangles, the UI designers favourite",
+           "fill, rounded-rectangle", /* keywords */
+           NULL, /* requirements */
+           SIZE, SIZE,
+           NULL, draw)
diff --git a/test/rounded-rectangle-stroke.c b/test/rounded-rectangle-stroke.c
new file mode 100644 (file)
index 0000000..22bc515
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Copyright © 2009 Chris Wilson
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+
+#define SIZE 80
+
+/* A very simple test to exercise the scan rasterisers with constant regions. */
+
+static void
+rounded_rectangle (cairo_t *cr, int x, int y, int w, int h, int r)
+{
+    cairo_new_sub_path (cr);
+    cairo_arc (cr, x + r, y + r, r, M_PI, 3 * M_PI / 2);
+    cairo_arc (cr, x + w - r, y + r, r, 3 *M_PI / 2, 2 * M_PI);
+    cairo_arc (cr, x + w - r, y + h - r, r, 0, M_PI / 2);
+    cairo_arc (cr, x + r, y + h - r, r, M_PI / 2, M_PI);
+    cairo_close_path (cr);
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    /* Paint background white, then draw in black. */
+    cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); /* white */
+    cairo_paint (cr);
+    cairo_set_source_rgb (cr, 0.0, 0.0, 0.0); /* black */
+
+    cairo_set_line_width (cr, 10);
+    rounded_rectangle (cr, 10, 10, width-20, height-20, 10);
+    cairo_stroke (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (rounded_rectangle_stroke,
+           "Tests handling of rounded rectangles, the UI designers favourite",
+           "stroke, rounded-rectangle", /* keywords */
+           NULL, /* requirements */
+           SIZE, SIZE,
+           NULL, draw)
diff --git a/test/run-cairo-test-suite.sh b/test/run-cairo-test-suite.sh
new file mode 100755 (executable)
index 0000000..567d2b1
--- /dev/null
@@ -0,0 +1,18 @@
+#!/bin/bash
+
+set -m
+
+if `which gnome-screensaver-command`; then
+       gnome-screensaver-command -i -n "cairo-test-suite" -r "Cairo needs to read back from the screen in order to test rendering to xlib" &
+       pid=$!
+
+       restore_screensaver() { kill $pid; }
+else
+       restore_screensaver() { :; }
+fi
+
+trap cleanup SIGINT SIGTERM
+
+./cairo-test-suite "$*"
+
+restore_screensaver
diff --git a/test/scale-down-source-surface-paint.c b/test/scale-down-source-surface-paint.c
new file mode 100644 (file)
index 0000000..8cf0e06
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * Copyright © 2005,2007 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ */
+
+#include "cairo-test.h"
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_surface_t *surface;
+    uint32_t data[16] = {
+       0xffffffff, 0xffffffff,         0xffff0000, 0xffff0000,
+       0xffffffff, 0xffffffff,         0xffff0000, 0xffff0000,
+
+       0xff00ff00, 0xff00ff00,         0xff0000ff, 0xff0000ff,
+       0xff00ff00, 0xff00ff00,         0xff0000ff, 0xff0000ff
+    };
+
+    /* First paint opaque background (black) so we don't need separate
+     * ARGB32 and RGB24 reference images. */
+    cairo_set_source_rgb (cr, 0, 0, 0); /* black */
+    cairo_paint (cr);
+
+    surface = cairo_image_surface_create_for_data ((unsigned char *) data,
+                                                  CAIRO_FORMAT_RGB24, 4, 4, 16);
+
+    cairo_scale (cr, 0.5, 0.5);
+
+    cairo_set_source_surface (cr, surface, 4, 4);
+    cairo_pattern_set_filter (cairo_get_source (cr), CAIRO_FILTER_NEAREST);
+    cairo_paint (cr);
+
+    cairo_surface_finish (surface); /* data will go out of scope */
+    cairo_surface_destroy (surface);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (scale_down_source_surface_paint,
+           "Test call sequence: cairo_scale; cairo_set_source_surface; cairo_paint, with a scale < 1.0",
+           "paint, transform", /* keywords */
+           NULL, /* requirements */
+           6, 6,
+           NULL, draw)
diff --git a/test/scale-offset-image.c b/test/scale-offset-image.c
new file mode 100644 (file)
index 0000000..0eef22e
--- /dev/null
@@ -0,0 +1,142 @@
+/*
+ * Copyright 2008 Chris Wilson
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Chris Wilson not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Chris Wilson makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * CHRIS WILSON DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL CHRIS WILSON BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+/*
+ * Test case derived from the bug report by Michel Iwaniec:
+ * http://lists.cairographics.org/archives/cairo/2008-November/015660.html
+ */
+
+#include "cairo-test.h"
+
+static cairo_surface_t *
+create_source (cairo_surface_t *target, int width, int height)
+{
+    cairo_surface_t *similar;
+    cairo_t *cr;
+
+    similar = cairo_image_surface_create (CAIRO_FORMAT_RGB24,
+                                           width, height);
+    cr = cairo_create (similar);
+    cairo_surface_destroy (similar);
+
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_rectangle (cr,
+                    width - 4, height - 4,
+                    2, 2);
+    cairo_fill (cr);
+    cairo_set_source_rgb (cr, 1, 0, 0);
+    cairo_rectangle (cr,
+                    width - 2, height - 4,
+                    2, 2);
+    cairo_fill (cr);
+    cairo_set_source_rgb (cr, 0, 1, 0);
+    cairo_rectangle (cr,
+                    width - 4, height - 2,
+                    2, 2);
+    cairo_fill (cr);
+    cairo_set_source_rgb (cr, 0, 0, 1);
+    cairo_rectangle (cr,
+                    width - 2, height - 2,
+                    2, 2);
+    cairo_fill (cr);
+
+    similar = cairo_surface_reference (cairo_get_target (cr));
+    cairo_destroy (cr);
+
+    return similar;
+}
+
+static void
+draw_grid (cairo_t *cr, cairo_pattern_t *pattern, int dst_x, int dst_y)
+{
+    cairo_matrix_t m;
+
+    cairo_save (cr);
+    cairo_translate (cr, dst_x, dst_y);
+    cairo_scale (cr, 16, 16);
+    cairo_rotate (cr, 1);
+
+    cairo_matrix_init_translate (&m, 2560-4, 1280-4);
+    cairo_pattern_set_matrix (pattern, &m);
+    cairo_set_source (cr, pattern);
+    cairo_rectangle (cr, 0, 0, 4, 4);
+    cairo_fill (cr);
+
+    cairo_set_source_rgb (cr, .7, .7, .7);
+    cairo_set_line_width (cr, 1./16);
+    cairo_move_to (cr, 0, 0);
+    cairo_line_to (cr, 4, 0);
+    cairo_move_to (cr, 0, 2);
+    cairo_line_to (cr, 4, 2);
+    cairo_move_to (cr, 0, 4);
+    cairo_line_to (cr, 4, 4);
+    cairo_move_to (cr, 0, 0);
+    cairo_line_to (cr, 0, 4);
+    cairo_move_to (cr, 2, 0);
+    cairo_line_to (cr, 2, 4);
+    cairo_move_to (cr, 4, 0);
+    cairo_line_to (cr, 4, 4);
+    cairo_stroke (cr);
+
+    cairo_restore (cr);
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_surface_t *source;
+    cairo_pattern_t *pattern;
+
+    cairo_paint (cr);
+
+    source = create_source (cairo_get_target (cr), 2560, 1280);
+    pattern = cairo_pattern_create_for_surface (source);
+    cairo_surface_destroy (source);
+
+    cairo_pattern_set_filter (pattern, CAIRO_FILTER_NEAREST);
+    cairo_pattern_set_extend (pattern, CAIRO_EXTEND_NONE);
+
+    draw_grid (cr, pattern, 50, 0);
+    draw_grid (cr, pattern, 130, 0);
+    draw_grid (cr, pattern, 210, 0);
+    draw_grid (cr, pattern, 290, 0);
+
+    draw_grid (cr, pattern, 50,  230);
+    draw_grid (cr, pattern, 130, 230);
+    draw_grid (cr, pattern, 210, 230);
+    draw_grid (cr, pattern, 290, 230);
+
+    cairo_pattern_destroy (pattern);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (scale_offset_image,
+           "Tests drawing surfaces under various scales and transforms",
+           "surface scale-offset", /* keywords */
+           NULL, /* requirements */
+           320, 320,
+           NULL, draw)
+
diff --git a/test/scale-offset-similar.c b/test/scale-offset-similar.c
new file mode 100644 (file)
index 0000000..daf7ffe
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+ * Copyright 2008 Chris Wilson
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Chris Wilson not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Chris Wilson makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * CHRIS WILSON DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL CHRIS WILSON BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+/*
+ * Test case derived from the bug report by Michel Iwaniec:
+ * http://lists.cairographics.org/archives/cairo/2008-November/015660.html
+ */
+
+#include "cairo-test.h"
+
+static cairo_surface_t *
+create_source (cairo_surface_t *target, int width, int height)
+{
+    cairo_surface_t *similar;
+    cairo_t *cr;
+
+    similar = cairo_surface_create_similar (target,
+                                           CAIRO_CONTENT_COLOR,
+                                           width, height);
+    cr = cairo_create (similar);
+    cairo_surface_destroy (similar);
+
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_rectangle (cr,
+                    width - 4, height - 4,
+                    2, 2);
+    cairo_fill (cr);
+    cairo_set_source_rgb (cr, 1, 0, 0);
+    cairo_rectangle (cr,
+                    width - 2, height - 4,
+                    2, 2);
+    cairo_fill (cr);
+    cairo_set_source_rgb (cr, 0, 1, 0);
+    cairo_rectangle (cr,
+                    width - 4, height - 2,
+                    2, 2);
+    cairo_fill (cr);
+    cairo_set_source_rgb (cr, 0, 0, 1);
+    cairo_rectangle (cr,
+                    width - 2, height - 2,
+                    2, 2);
+    cairo_fill (cr);
+
+    similar = cairo_surface_reference (cairo_get_target (cr));
+    cairo_destroy (cr);
+
+    return similar;
+}
+
+static void
+draw_grid (cairo_t *cr, cairo_pattern_t *pattern, int dst_x, int dst_y)
+{
+    cairo_matrix_t m;
+
+    cairo_save (cr);
+    cairo_translate (cr, dst_x, dst_y);
+    cairo_scale (cr, 16, 16);
+    cairo_rotate (cr, 1);
+
+    cairo_matrix_init_translate (&m, 2560-4, 1280-4);
+    cairo_pattern_set_matrix (pattern, &m);
+    cairo_set_source (cr, pattern);
+    cairo_rectangle (cr, 0, 0, 4, 4);
+    cairo_fill (cr);
+
+    cairo_set_source_rgb (cr, .7, .7, .7);
+    cairo_set_line_width (cr, 1./16);
+    cairo_move_to (cr, 0, 0);
+    cairo_line_to (cr, 4, 0);
+    cairo_move_to (cr, 0, 2);
+    cairo_line_to (cr, 4, 2);
+    cairo_move_to (cr, 0, 4);
+    cairo_line_to (cr, 4, 4);
+    cairo_move_to (cr, 0, 0);
+    cairo_line_to (cr, 0, 4);
+    cairo_move_to (cr, 2, 0);
+    cairo_line_to (cr, 2, 4);
+    cairo_move_to (cr, 4, 0);
+    cairo_line_to (cr, 4, 4);
+    cairo_stroke (cr);
+
+    cairo_restore (cr);
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_surface_t *source;
+    cairo_pattern_t *pattern;
+
+    cairo_paint (cr);
+
+    source = create_source (cairo_get_target (cr), 2560, 1280);
+    pattern = cairo_pattern_create_for_surface (source);
+    cairo_surface_destroy (source);
+
+    cairo_pattern_set_filter (pattern, CAIRO_FILTER_NEAREST);
+    cairo_pattern_set_extend (pattern, CAIRO_EXTEND_NONE);
+
+    draw_grid (cr, pattern, 50, 0);
+    draw_grid (cr, pattern, 130, 0);
+    draw_grid (cr, pattern, 210, 0);
+    draw_grid (cr, pattern, 290, 0);
+
+    draw_grid (cr, pattern, 50,  230);
+    draw_grid (cr, pattern, 130, 230);
+    draw_grid (cr, pattern, 210, 230);
+    draw_grid (cr, pattern, 290, 230);
+
+    cairo_pattern_destroy (pattern);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (scale_offset_similar,
+           "Tests drawing surfaces under various scales and transforms",
+           "surface scale-offset", /* keywords */
+           NULL, /* requirements */
+           320, 320,
+           NULL, draw)
+
diff --git a/test/scale-source-surface-paint.c b/test/scale-source-surface-paint.c
new file mode 100644 (file)
index 0000000..0c0a3ac
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ */
+
+#include "cairo-test.h"
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_surface_t *surface;
+    uint32_t data[16] = {
+       0xffffffff, 0xffffffff,         0xffff0000, 0xffff0000,
+       0xffffffff, 0xffffffff,         0xffff0000, 0xffff0000,
+
+       0xff00ff00, 0xff00ff00,         0xff0000ff, 0xff0000ff,
+       0xff00ff00, 0xff00ff00,         0xff0000ff, 0xff0000ff
+    };
+
+    surface = cairo_image_surface_create_for_data ((unsigned char *) data,
+                                                  CAIRO_FORMAT_RGB24, 4, 4, 16);
+
+    cairo_scale (cr, 2, 2);
+
+    cairo_set_source_surface (cr, surface, 1 , 1);
+    cairo_pattern_set_filter (cairo_get_source (cr), CAIRO_FILTER_NEAREST);
+    cairo_paint (cr);
+
+    cairo_surface_finish (surface); /* data will go out of scope */
+    cairo_surface_destroy (surface);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (scale_source_surface_paint,
+           "Test call sequence: cairo_scale; cairo_set_source_surface; cairo_paint",
+           "paint, transform", /* keywords */
+           NULL, /* requirements */
+           12, 12,
+           NULL, draw)
diff --git a/test/scaled-font-zero-matrix.c b/test/scaled-font-zero-matrix.c
new file mode 100644 (file)
index 0000000..d4f79a6
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Copyright © 2008 Mozilla Corporation
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Mozilla Corporation not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Mozilla Corporation makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * MOZILLA CORPORATION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL MOZILLA CORPORATION BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Authors: Jeff Muizelaar
+ */
+
+#include "cairo-test.h"
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_font_face_t *font_face;
+    cairo_font_options_t *font_options;
+    cairo_scaled_font_t *scaled_font;
+    cairo_matrix_t identity;
+    cairo_matrix_t zero;
+
+    cairo_matrix_init_identity(&identity);
+
+    zero = identity;
+    cairo_matrix_scale(&zero, 0, 0);
+
+    font_face = cairo_get_font_face (cr);
+    font_options = cairo_font_options_create ();
+    cairo_get_font_options (cr, font_options);
+    scaled_font = cairo_scaled_font_create (font_face,
+           &identity,
+           &zero,
+           font_options);
+    cairo_set_scaled_font (cr, scaled_font);
+    cairo_show_text (cr, "Hello");
+    cairo_scaled_font_destroy (scaled_font);
+    cairo_font_options_destroy (font_options);
+
+    return cairo_test_status_from_status (cairo_test_get_context (cr),
+                                          cairo_status(cr));
+}
+
+CAIRO_TEST (scaled_font_zero_matrix,
+           "Test that scaled fonts with a degenerate matrix work",
+           "zero, matrix, degenerate, scaled-font", /* keywords */
+           NULL, /* requirements */
+           0, 0,
+           NULL, draw)
diff --git a/test/scarab.jpg b/test/scarab.jpg
new file mode 100644 (file)
index 0000000..6a66ff7
Binary files /dev/null and b/test/scarab.jpg differ
diff --git a/test/select-font-face.c b/test/select-font-face.c
new file mode 100644 (file)
index 0000000..ec4b27c
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * Copyright © 2006 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ */
+
+#include "cairo-test.h"
+
+#define TEXT_SIZE 12
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    /* We draw in the default black, so paint white first. */
+    cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); /* white */
+    cairo_paint (cr);
+
+    cairo_set_source_rgb (cr, 0, 0, 0); /* black */
+
+    cairo_set_font_size (cr, TEXT_SIZE);
+    cairo_move_to (cr, 0, TEXT_SIZE);
+
+    cairo_select_font_face (cr, CAIRO_TEST_FONT_FAMILY " Serif",
+                           CAIRO_FONT_SLANT_NORMAL,
+                           CAIRO_FONT_WEIGHT_NORMAL);
+    cairo_show_text (cr, "i-am-serif");
+
+    cairo_select_font_face (cr, CAIRO_TEST_FONT_FAMILY " Sans",
+                           CAIRO_FONT_SLANT_NORMAL,
+                           CAIRO_FONT_WEIGHT_NORMAL);
+    cairo_show_text (cr, " i-am-sans");
+
+    cairo_select_font_face (cr, CAIRO_TEST_FONT_FAMILY " Sans Mono",
+                           CAIRO_FONT_SLANT_NORMAL,
+                           CAIRO_FONT_WEIGHT_NORMAL);
+    cairo_show_text (cr, " i-am-mono");
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (select_font_face,
+           "Tests using cairo_select_font_face to draw text in different faces",
+           "font", /* keywords */
+           NULL, /* requirements */
+           192, TEXT_SIZE + 4,
+           NULL, draw)
diff --git a/test/select-font-no-show-text.c b/test/select-font-no-show-text.c
new file mode 100644 (file)
index 0000000..0b72415
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Carl Worth <cworth@redhat.com>
+ */
+
+/* Bug history
+ *
+ * 2005-04-12 Carl Worth <cworth@cworth.org>
+ *
+ *   I noticed that if we call cairo_select_font_face, but then do a
+ *   cairo_destroy before ever drawing any text, then we get:
+ *
+ *   *** glibc detected *** double free or corruption (fasttop): 0x083274d0 ***
+ *   Aborted
+ *
+ * 2005-04-14 Owen Taylor <otaylor@redhat.com>
+ *
+ *  Fixed... just a stray free().
+ */
+
+#include <math.h>
+#include "cairo-test.h"
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_select_font_face (cr, CAIRO_TEST_FONT_FAMILY " Sans",
+                           CAIRO_FONT_SLANT_NORMAL,
+                           CAIRO_FONT_WEIGHT_BOLD);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (select_font_no_show_text,
+           "Test calling cairo_select_font_face but never drawing text.",
+           "font", /* keywords */
+           NULL, /* requirements */
+           0, 0,
+           NULL, draw)
diff --git a/test/self-copy-overlap.c b/test/self-copy-overlap.c
new file mode 100644 (file)
index 0000000..2f49ab0
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2008 Chris Wilson
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Chris Wilson not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Chris Wilson makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * CHRIS WILSON DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL CHRIS WILSON BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+/* Question: are patterns mutable? The answer depends on who you ask... */
+
+#include "cairo-test.h"
+
+/* This test is only interesting if the target has alpha */
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_set_source_rgba (cr, 0, 0, 0, .01);
+    cairo_paint (cr);
+
+    cairo_set_source_surface (cr, cairo_get_target (cr), 1, 1);
+    cairo_paint (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (self_copy_overlap,
+           "Tests painting to itself using itself as the source"
+           "\nBackends treat this case inconsistently---vector backends are creating snapshots.",
+           "self-copy", /* keywords */
+           NULL, /* requirements */
+           200, 200,
+           NULL, draw)
diff --git a/test/self-copy.c b/test/self-copy.c
new file mode 100644 (file)
index 0000000..32a375a
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Owen Taylor <otaylor@redhat.com>
+ */
+
+#include <math.h>
+#include "cairo-test.h"
+#include <stdio.h>
+
+#define SIZE 40
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_pattern_t *pattern;
+    cairo_matrix_t matrix;
+
+    /* Paint a diagonal division as a test image */
+    cairo_set_source_rgb (cr, 1, 1, 1);        /* White */
+    cairo_paint (cr);
+
+    cairo_move_to (cr, SIZE,    0);
+    cairo_line_to (cr, SIZE, SIZE);
+    cairo_line_to (cr, 0,    SIZE);
+
+    cairo_set_source_rgb (cr, 0, 0, 0);
+    cairo_fill (cr);
+
+    /* Create a pattern with the target surface as the source,
+     * offset by SIZE/2
+     */
+    pattern = cairo_pattern_create_for_surface (cairo_get_group_target (cr));
+
+    cairo_matrix_init_translate (&matrix, - SIZE / 2, - SIZE / 2);
+    cairo_pattern_set_matrix (pattern, &matrix);
+
+    cairo_set_source (cr, pattern);
+    cairo_pattern_destroy (pattern);
+
+    /* Copy two rectangles from the upper-left quarter of the image to
+     * the lower right.  It will work if we use cairo_fill(), but the
+     * cairo_clip() cairo_paint() combination fails because the clip
+     * on the surface as a destination affects it as the source as
+     * well.
+     */
+    cairo_rectangle (cr,
+                    2 * SIZE / 4, 2 * SIZE / 4,
+                    SIZE / 4,     SIZE / 4);
+    cairo_rectangle (cr,
+                    3 * SIZE / 4, 3 * SIZE / 4,
+                    SIZE / 4,     SIZE / 4);
+    cairo_clip (cr);
+    cairo_paint (cr);
+
+    return CAIRO_TEST_SUCCESS;
+
+}
+
+CAIRO_TEST (self_copy,
+           "Test copying from a surface to itself with a clip",
+           "paint", /* keywords */
+           NULL, /* requirements */
+           SIZE, SIZE,
+           NULL, draw)
diff --git a/test/self-intersecting.c b/test/self-intersecting.c
new file mode 100644 (file)
index 0000000..8053235
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ */
+
+/* Bug history
+ *
+ * 2005-06-01  Carl Worth  <cworth@cworth.org>
+ *
+ *   There's a long-standing bug in that self-intersecting paths give
+ *   an incorrect result when stroked. The problem is that the
+ *   trapezoids are generated incrementally along the stroke and as
+ *   such, are not disjoint. The errant intersections of these
+ *   trapezoids then leads to overfilled pixels.
+ *
+ *   The test belows first creates and fills a path. Then it creates a
+ *   second path which has a stroked boundary identical to the first
+ *   filled path. But the results of the two operations are
+ *   different. The most obvious difference is in the central region
+ *   where the entire path intersects itself. But notice that every
+ *   time the path turns there are also errors on the inside of the
+ *   turn, (since the subsequent trapezoids along the path intersect).
+ */
+
+#include "cairo-test.h"
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_paint (cr);
+
+    cairo_translate (cr, 1.0, 1.0);
+
+    cairo_set_source_rgb (cr, 1, 0, 0); /* red */
+
+    /* First draw the desired shape with a fill */
+    cairo_rectangle (cr, 0.5, 0.5,  4.0, 4.0);
+    cairo_rectangle (cr, 3.5, 3.5,  4.0, 4.0);
+    cairo_rectangle (cr, 3.5, 1.5, -2.0, 2.0);
+    cairo_rectangle (cr, 6.5, 4.5, -2.0, 2.0);
+
+    cairo_fill (cr);
+
+    /* Then try the same thing with a stroke */
+    cairo_translate (cr, 0, 10);
+    cairo_move_to (cr, 1.0, 1.0);
+    cairo_rel_line_to (cr,  3.0,  0.0);
+    cairo_rel_line_to (cr,  0.0,  6.0);
+    cairo_rel_line_to (cr,  3.0,  0.0);
+    cairo_rel_line_to (cr,  0.0, -3.0);
+    cairo_rel_line_to (cr, -6.0,  0.0);
+    cairo_close_path (cr);
+
+    cairo_set_line_width (cr, 1.0);
+    cairo_stroke (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (self_intersecting,
+           "Test strokes of self-intersecting paths"
+           "\nSelf-intersecting strokes are wrong due to incremental trapezoidization.",
+           "stroke, trap", /* keywords */
+           NULL, /* requirements */
+           10, 20,
+           NULL, draw)
diff --git a/test/set-source.c b/test/set-source.c
new file mode 100644 (file)
index 0000000..7e54626
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ */
+
+#include "cairo-test.h"
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    int i;
+    uint32_t color = 0x8019334c;
+    cairo_surface_t *surface;
+    cairo_pattern_t *pattern;
+
+    surface = cairo_image_surface_create_for_data ((unsigned char *) &color,
+                                                  CAIRO_FORMAT_ARGB32, 1, 1, 4);
+    pattern = cairo_pattern_create_for_surface (surface);
+    cairo_pattern_set_extend (pattern, CAIRO_EXTEND_REPEAT);
+
+    /* Several different means of making mostly the same color (though
+     * we can't get anything but alpha==1.0 out of
+     * cairo_set_source_rgb. */
+    for (i=0; i < width; i++) {
+       switch (i) {
+       case 0:
+           cairo_set_source_rgb (cr, .6, .7, .8);
+           break;
+       case 1:
+           cairo_set_source_rgba (cr, .2, .4, .6, 0.5);
+           break;
+       case 2:
+#if WE_HAD_SUPPORT_FOR_PREMULTIPLIED
+           cairo_set_source_rgba_premultiplied (cr, .1, .2, .3, 0.5);
+#else
+           cairo_set_source_rgba (cr, .2, .4, .6, 0.5);
+#endif
+           break;
+       case 3:
+       default:
+           cairo_set_source (cr, pattern);
+           break;
+       }
+
+       cairo_rectangle (cr, i, 0, 1, height);
+       cairo_fill (cr);
+    }
+
+    cairo_pattern_destroy (pattern);
+    cairo_surface_finish (surface); /* data will go out of scope */
+    cairo_surface_destroy (surface);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (set_source,
+           "Tests calls to various set_source functions",
+           "api", /* keywords */
+           NULL, /* requirements */
+           5, 5,
+           NULL, draw)
diff --git a/test/shape-general-convex.c b/test/shape-general-convex.c
new file mode 100644 (file)
index 0000000..b9241bb
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * Copyright © 2011 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+/* A general convex shape with a twist. */
+
+#include "cairo-test.h"
+
+#define SIZE           (100)
+#define PAD 4
+
+static void
+limacon (cairo_t *cr, double a, double b, double radius)
+{
+    int i;
+
+    cairo_save (cr);
+    cairo_translate (cr, PAD, 0);
+    for (i = 0; i < 360; i++) {
+       double theta = i * M_PI / 180;
+       double r = b + a * cos (theta);
+       double x = radius * r * cos (theta);
+       double y = radius * r * sin (theta);
+       cairo_line_to (cr, x, y);
+    }
+    cairo_close_path (cr);
+    cairo_restore (cr);
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_paint (cr);
+
+    cairo_save (cr);
+    cairo_translate (cr, 2*PAD, PAD);
+
+    cairo_translate (cr, SIZE/2, SIZE/2);
+    limacon (cr, 1, .5, SIZE/3); /* trivia, this is a trisectrix */
+
+    cairo_set_source_rgb (cr, 1, 0, 0);
+    cairo_fill_preserve (cr);
+
+    cairo_set_source_rgb (cr, 0, 0, 0);
+    cairo_stroke (cr);
+
+    cairo_scale (cr, -1, 1);
+
+    limacon (cr, 1, .5, SIZE/3); /* trivia, this is a trisectrix */
+
+    cairo_set_source_rgb (cr, 0, 0, 1);
+    cairo_fill_preserve (cr);
+
+    cairo_set_source_rgb (cr, 0, 0, 0);
+    cairo_stroke (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (shape_general_convex,
+           "A general shape that is not as convex as it first appears",
+           "fill,stroke", /* keywords */
+           NULL, /* requirements */
+           SIZE+4*PAD, SIZE+4*PAD,
+           NULL, draw)
diff --git a/test/shape-sierpinski.c b/test/shape-sierpinski.c
new file mode 100644 (file)
index 0000000..9d20170
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * Copyright © 2011 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+/* I thought I spied a bug... */
+
+#include "cairo-test.h"
+
+#define WIDTH          (1024)
+#define HEIGHT         (600)
+
+static const double m_1_sqrt_3 = 0.577359269;
+
+static void
+T (cairo_t *cr, int size)
+{
+    cairo_move_to (cr, 0, 0);
+    cairo_line_to (cr, size, 0);
+    cairo_line_to (cr, size/2, size*m_1_sqrt_3);
+
+    size /= 2;
+    if (size >= 4) {
+       T (cr, size);
+       cairo_save (cr); {
+           cairo_translate (cr, size, 0);
+           T (cr, size);
+       } cairo_restore (cr);
+       cairo_save (cr); {
+           cairo_translate (cr, size/2, size*m_1_sqrt_3);
+           T (cr, size);
+       } cairo_restore (cr);
+    }
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_paint (cr);
+
+    cairo_translate (cr, 0, 8);
+
+    cairo_set_source_rgb (cr, 0, 0, 0);
+    cairo_set_line_width (cr, 1.);
+
+    T (cr, WIDTH);
+
+    cairo_translate (cr, 0, 2*HEIGHT-16);
+    cairo_scale (cr, 1, -1);
+
+    T (cr, WIDTH);
+
+    cairo_stroke (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (shape_sierpinski,
+           "A fractal triangle",
+           "stroke", /* keywords */
+           NULL, /* requirements */
+           WIDTH, 2*HEIGHT,
+           NULL, draw)
diff --git a/test/show-glyphs-advance.c b/test/show-glyphs-advance.c
new file mode 100644 (file)
index 0000000..5fe9ec8
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ * Copyright © 2006 Red Hat, Inc.
+ * Copyright © 2011 Andrea Canciani
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ *         Andrea Canciani <ranma42@gmail.com>
+ */
+
+#include "cairo-test.h"
+
+static cairo_test_status_t
+get_glyph (const cairo_test_context_t *ctx,
+          cairo_scaled_font_t *scaled_font,
+          const char *utf8,
+          cairo_glyph_t *glyph)
+{
+    cairo_glyph_t *text_to_glyphs;
+    cairo_status_t status;
+    int i;
+
+    text_to_glyphs = glyph;
+    i = 1;
+    status = cairo_scaled_font_text_to_glyphs (scaled_font,
+                                              0, 0,
+                                              utf8, -1,
+                                              &text_to_glyphs, &i,
+                                              NULL, NULL,
+                                              0);
+    if (status != CAIRO_STATUS_SUCCESS)
+       return cairo_test_status_from_status (ctx, status);
+
+    if (text_to_glyphs != glyph) {
+       *glyph = text_to_glyphs[0];
+       cairo_glyph_free (text_to_glyphs);
+    }
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+static const char *characters[] = { "A", "B", "C", "D" };
+
+#define NUM_CHARS ARRAY_LENGTH (characters)
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    const cairo_test_context_t *ctx = cairo_test_get_context (cr);
+    cairo_scaled_font_t *scaled_font;
+    cairo_glyph_t *glyphs = xmalloc (NUM_CHARS  * sizeof (cairo_glyph_t));
+    int i;
+    cairo_status_t status;
+
+    /* Paint white background. */
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_paint (cr);
+
+    scaled_font = cairo_get_scaled_font (cr);
+
+    for (i = 0; i < NUM_CHARS; i++) {
+       status = get_glyph (ctx, scaled_font, characters[i], &glyphs[i]);
+       if (status)
+           goto BAIL;
+
+       glyphs[i].x = 10.0 + 10.0 * (i % 2);
+       glyphs[i].y = 20.0 + 10.0 * (i / 2);
+    }
+
+    cairo_set_source_rgb (cr, 0, 0, 0);
+    cairo_show_glyphs (cr, glyphs, 4);
+
+    cairo_translate (cr, 40., 20.);
+    cairo_rotate (cr, M_PI / 4.);
+
+    cairo_show_glyphs (cr, glyphs, NUM_CHARS);
+
+  BAIL:
+    free(glyphs);
+
+    return status;
+}
+
+CAIRO_TEST (show_glyphs_advance,
+           "Test that glyph advances work as expected along both axes",
+           "text, matrix", /* keywords */
+           NULL, /* requirements */
+           64, 64,
+           NULL, draw)
diff --git a/test/show-glyphs-many.c b/test/show-glyphs-many.c
new file mode 100644 (file)
index 0000000..f689fe0
--- /dev/null
@@ -0,0 +1,176 @@
+/*
+ * Copyright © 2006 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ */
+
+#include "cairo-test.h"
+
+#include <string.h>
+
+/* Bug history
+ *
+ * 2006-01-07  Jon Hellan  <hellan@acm.org>
+ *
+ *   Jon opened the following bug report:
+ *
+ *     _XError from XRenderCompositeText8
+ *     https://bugs.freedesktop.org/show_bug.cgi?id=5528
+ *
+ * 2006-03-02  Carl Worth  <cworth@cworth.org>
+ *
+ *   I wrote this test case to demonstrate the bug.
+ *
+ *   Approach:
+ *
+ *     Draw 65535 glyphs white-on-white all on top of each other.
+ *
+ *   Rationale:
+ *
+ *     The number 65535 comes from the original bug report.
+ *
+ *     I would use cairo_show_text with a long string of 'x's say,
+ *     but then the surface would need to be enormous to contain
+ *     them. A smaller surface could be used, but I fear that at some
+ *     point the off-surface glyph drawing would be optimized away
+ *     and not exercise the bug.
+ *
+ *     So, to keep the surface size under control, I use
+ *     cairo_show_glyphs which allows me to place the glyphs all on
+ *     top of each other. But, since cairo doesn't provide any
+ *     character-to-glyphs mapping, I can't get a reliable glyph
+ *     index (for character 'x' for example). So I just "guess" a
+ *     glyph index and use white-on-white drawing to ignore the
+ *     result. (I don't care what's drawn---I just want to ensure
+ *     that things don't crash.)
+ *
+ *  Status: I replicated bug. The largest value of NUM_GLYPHS for
+ *      which I saw success is 21842.
+ *
+ * 2008-30-08 Chris Wilson <chris@chris-wilson.co.uk>
+ *   This is also a valid test case for:
+ *
+ *     Bug 5913 crash on overlong string
+ *     https://bugs.freedesktop.org/show_bug.cgi?id=5913
+ *
+ *  which is still causing a crash in the Xlib backend - presumably, just
+ *  a miscalculation of the length of the available request.
+ */
+
+#define TEXT_SIZE 12
+#define NUM_GLYPHS 65535
+
+static cairo_test_status_t
+get_glyph (const cairo_test_context_t *ctx,
+          cairo_scaled_font_t *scaled_font,
+          const char *utf8,
+          cairo_glyph_t *glyph)
+{
+    cairo_glyph_t *text_to_glyphs;
+    cairo_status_t status;
+    int i;
+
+    text_to_glyphs = glyph;
+    i = 1;
+    status = cairo_scaled_font_text_to_glyphs (scaled_font,
+                                              0, 0,
+                                              utf8, -1,
+                                              &text_to_glyphs, &i,
+                                              NULL, NULL,
+                                              0);
+    if (status != CAIRO_STATUS_SUCCESS)
+       return cairo_test_status_from_status (ctx, status);
+
+    if (text_to_glyphs != glyph) {
+       *glyph = text_to_glyphs[0];
+       cairo_glyph_free (text_to_glyphs);
+    }
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    const cairo_test_context_t *ctx = cairo_test_get_context (cr);
+    cairo_glyph_t *glyphs = xmalloc (NUM_GLYPHS * sizeof (cairo_glyph_t));
+    cairo_scaled_font_t *scaled_font;
+    const char *characters[] = { /* try to exercise different widths of index */
+       "m", /* Latin letter m, index=0x50 */
+       "μ", /* Greek letter mu, index=0x349 */
+       NULL,
+    }, **utf8;
+    int i, j;
+    cairo_status_t status;
+
+    /* Paint white background. */
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_paint (cr);
+
+    cairo_select_font_face (cr, "Sans",
+                           CAIRO_FONT_SLANT_NORMAL,
+                           CAIRO_FONT_WEIGHT_NORMAL);
+    cairo_set_font_size (cr, TEXT_SIZE);
+    scaled_font = cairo_get_scaled_font (cr);
+
+    for (utf8 = characters; *utf8 != NULL; utf8++) {
+       status = get_glyph (ctx, scaled_font, *utf8, &glyphs[0]);
+       if (status)
+           goto BAIL;
+
+       if (glyphs[0].index) {
+           glyphs[0].x = 1.0;
+           glyphs[0].y = height - 1;
+           for (i=1; i < NUM_GLYPHS; i++)
+               glyphs[i] = glyphs[0];
+
+           cairo_show_glyphs (cr, glyphs, NUM_GLYPHS);
+       }
+    }
+
+    /* we can pack ~21k 1-byte glyphs into a single XRenderCompositeGlyphs8 */
+    status = get_glyph (ctx, scaled_font, "m", &glyphs[0]);
+    if (status)
+       goto BAIL;
+    for (i=1; i < 21500; i++)
+       glyphs[i] = glyphs[0];
+    /* so check expanding the current 1-byte request for 2-byte glyphs */
+    status = get_glyph (ctx, scaled_font, "μ", &glyphs[i]);
+    if (status)
+       goto BAIL;
+    for (j=i+1; j < NUM_GLYPHS; j++)
+       glyphs[j] = glyphs[i];
+
+    cairo_show_glyphs (cr, glyphs, NUM_GLYPHS);
+
+  BAIL:
+    free(glyphs);
+
+    return status;
+}
+
+CAIRO_TEST (show_glyphs_many,
+           "Test that cairo_show_glyphs works when handed 'many' glyphs",
+           "text, stress", /* keywords */
+           NULL, /* requirements */
+           9, 11,
+           NULL, draw)
diff --git a/test/show-text-current-point.c b/test/show-text-current-point.c
new file mode 100644 (file)
index 0000000..2fe32a8
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ */
+
+#include "cairo-test.h"
+
+#define TEXT_SIZE 12
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    /* We draw in the default black, so paint white first. */
+    cairo_save (cr);
+    cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); /* white */
+    cairo_paint (cr);
+    cairo_restore (cr);
+
+    cairo_select_font_face (cr, CAIRO_TEST_FONT_FAMILY " Sans",
+                           CAIRO_FONT_SLANT_NORMAL,
+                           CAIRO_FONT_WEIGHT_NORMAL);
+    cairo_set_font_size (cr, TEXT_SIZE);
+
+    cairo_set_source_rgb (cr, 0, 0, 0); /* black */
+
+    cairo_move_to (cr, 0, TEXT_SIZE);
+    cairo_show_text (cr, "Hello from the ");
+    cairo_show_text (cr, "show-text-current-point");
+    cairo_show_text (cr, " test.");
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (show_text_current_point,
+           "Test that cairo_show_text adjusts the current point properly",
+           "text, api", /* keywords */
+           NULL, /* requirements */
+           263, TEXT_SIZE + 4,
+           NULL, draw)
diff --git a/test/skew-extreme.c b/test/skew-extreme.c
new file mode 100644 (file)
index 0000000..69299a8
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+ * Copyright © 2007 Red Hat, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Carl Worth <cworth@cworth.org>
+ */
+
+#include "cairo-test.h"
+
+/* This test case is designed to exercise the following bug:
+ *
+ *     Skew transforms were broken by the cairo update in December
+ *     https://bugzilla.mozilla.org/show_bug.cgi?id=373632
+ *
+ * What's happening is that the rectangle is being skewed into the
+ * following shape:
+ *
+ * a__b
+ *  \ \
+ *   \ \
+ *    \ \
+ *     \ \
+ *      \ \
+ *      d\_\c
+ *
+ * and the bug is that _cairo_traps_tessellate_convex_quad is
+ * comparing b.x as less then d.x and therfore determining that the bc
+ * edge is left of the ad edge. The fix is simply to compare c.x to
+ * d.x instead of b.x to d.x .
+ */
+
+#define PAD            2
+#define LINE_WIDTH     10
+#define LINE_LENGTH    (2 * LINE_WIDTH)
+#define SKEW_FACTOR    5.0
+#define WIDTH          (PAD + (LINE_LENGTH * SKEW_FACTOR) + LINE_WIDTH + PAD)
+#define HEIGHT         (PAD + LINE_WIDTH + (LINE_LENGTH * SKEW_FACTOR) + LINE_WIDTH + PAD)
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    /* We draw in the default black, so paint white first. */
+    cairo_save (cr);
+    cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); /* white */
+    cairo_paint (cr);
+    cairo_restore (cr);
+
+    cairo_translate (cr, PAD, PAD);
+
+    cairo_set_line_width (cr, LINE_WIDTH);
+    cairo_set_line_cap (cr, CAIRO_LINE_CAP_BUTT);
+
+    cairo_save (cr);
+    {
+       cairo_matrix_t skew_x = {
+           1.0, 0.0,
+           SKEW_FACTOR, 1.0,
+           0.0, 0.0
+       };
+
+       cairo_translate (cr, LINE_WIDTH / 2.0, 0.0);
+
+       cairo_transform (cr, &skew_x);
+
+       cairo_move_to (cr, 0.0, 0.0);
+       cairo_line_to (cr, 0.0, LINE_LENGTH);
+       cairo_stroke (cr);
+    }
+    cairo_restore (cr);
+
+    cairo_translate (cr, 0.0, LINE_WIDTH);
+
+    cairo_save (cr);
+    {
+       cairo_matrix_t skew_y = {
+           1.0, SKEW_FACTOR,
+           0.0, 1.0,
+           0.0, 0.0
+       };
+
+       cairo_translate (cr, 0.0, LINE_WIDTH / 2.0);
+
+       cairo_transform (cr, &skew_y);
+
+       cairo_move_to (cr, 0.0, 0.0);
+       cairo_line_to (cr, LINE_LENGTH, 0.0);
+       cairo_stroke (cr);
+    }
+    cairo_restore (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (skew_extreme,
+           "Test cases of extreme skew.",
+           "transform, stroke", /* keywords */
+           NULL, /* requirements */
+           WIDTH, HEIGHT,
+           NULL, draw)
diff --git a/test/smask-fill.c b/test/smask-fill.c
new file mode 100644 (file)
index 0000000..cb38610
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * Copyright © 2008 Chris Wilson
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Chris Wilson not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Chris Wilson makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * CHRIS WILSON DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL CHRIS WILSON BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_surface_t *mask;
+    cairo_pattern_t *pattern;
+    cairo_t *cr2;
+
+    cairo_set_source_rgb (cr, 0, 0, 1.0);
+    cairo_paint (cr);
+
+    mask = cairo_surface_create_similar (cairo_get_group_target (cr),
+                                        CAIRO_CONTENT_ALPHA,
+                                        width, height);
+    cr2 = cairo_create (mask);
+    cairo_surface_destroy (mask);
+
+    cairo_save (cr2); {
+       cairo_set_operator (cr2, CAIRO_OPERATOR_CLEAR);
+       cairo_paint (cr2);
+    } cairo_restore (cr2);
+
+    pattern = cairo_pattern_create_linear (0, 0, width, height);
+    cairo_pattern_add_color_stop_rgba (pattern, 0.00, 0., 0., 0., 0.);
+    cairo_pattern_add_color_stop_rgba (pattern, 0.25, 1., 1., 1., 1.);
+    cairo_pattern_add_color_stop_rgba (pattern, 0.50, 1., 1., 1., .5);
+    cairo_pattern_add_color_stop_rgba (pattern, 0.75, 1., 1., 1., 1.);
+    cairo_pattern_add_color_stop_rgba (pattern, 1.00, 0., 0., 0., 0.);
+    cairo_set_source (cr2, pattern);
+    cairo_pattern_destroy (pattern);
+
+    cairo_arc (cr2, 0.5 * width, 0.5 * height, 0.45 * height, 0, 2 * M_PI);
+    cairo_fill (cr2);
+
+    cairo_set_source_rgb (cr, 1.0, 0, 0);
+    cairo_mask_surface (cr, cairo_get_target (cr2), 0, 0);
+    cairo_destroy (cr2);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (smask_fill,
+           "Test the support of \"soft\" masks with fills",
+           "smask, fill", /* keywords */
+           NULL, /* requirements */
+           60, 60,
+           NULL, draw)
diff --git a/test/smask-image-mask.c b/test/smask-image-mask.c
new file mode 100644 (file)
index 0000000..3d8b5d5
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * Copyright © 2008 Chris Wilson
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Chris Wilson not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Chris Wilson makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * CHRIS WILSON DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL CHRIS WILSON BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    uint32_t data[] = {
+       0xaa000000, 0x55000000,
+       0x55000000, 0xaa000000,
+    };
+
+    cairo_surface_t *mask, *mask2;
+    cairo_pattern_t *pattern;
+    cairo_t *cr2;
+
+    cairo_set_source_rgb (cr, 0, 0, 1.0);
+    cairo_paint (cr);
+
+    mask = cairo_surface_create_similar (cairo_get_group_target (cr),
+                                        CAIRO_CONTENT_ALPHA,
+                                        width, height);
+    cr2 = cairo_create (mask);
+    cairo_surface_destroy (mask);
+
+    cairo_save (cr2); {
+       cairo_set_operator (cr2, CAIRO_OPERATOR_CLEAR);
+       cairo_paint (cr2);
+    } cairo_restore (cr2);
+
+    pattern = cairo_pattern_create_linear (0, 0, width, height);
+    cairo_pattern_add_color_stop_rgba (pattern, 0.00, 0., 0., 0., 0.);
+    cairo_pattern_add_color_stop_rgba (pattern, 0.25, 1., 1., 1., 1.);
+    cairo_pattern_add_color_stop_rgba (pattern, 0.50, 1., 1., 1., .5);
+    cairo_pattern_add_color_stop_rgba (pattern, 0.75, 1., 1., 1., 1.);
+    cairo_pattern_add_color_stop_rgba (pattern, 1.00, 0., 0., 0., 0.);
+    cairo_set_source (cr2, pattern);
+    cairo_pattern_destroy (pattern);
+
+    mask2 = cairo_image_surface_create_for_data ((unsigned char *) data,
+                                               CAIRO_FORMAT_ARGB32, 2, 2, 8);
+    pattern = cairo_pattern_create_for_surface (mask2);
+    cairo_pattern_set_extend (pattern, CAIRO_EXTEND_REPEAT);
+    cairo_mask (cr2, pattern);
+    cairo_pattern_destroy (pattern);
+
+    cairo_set_source_rgb (cr, 1.0, 0, 0);
+    cairo_mask_surface (cr, cairo_get_target (cr2), 0, 0);
+    cairo_destroy (cr2);
+
+    cairo_surface_finish (mask2); /* data will go out of scope */
+    cairo_surface_destroy (mask2);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (smask_image_mask,
+           "Test the support of \"soft\" masks with a secondary image mask",
+           "smask, image", /* keywords */
+           NULL, /* requirements */
+           60, 60,
+           NULL, draw)
diff --git a/test/smask-mask.c b/test/smask-mask.c
new file mode 100644 (file)
index 0000000..11cff82
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ * Copyright © 2008 Chris Wilson
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Chris Wilson not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Chris Wilson makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * CHRIS WILSON DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL CHRIS WILSON BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_surface_t *mask;
+    cairo_pattern_t *pattern;
+    cairo_t *cr2, *cr3;
+
+    cairo_set_source_rgb (cr, 0, 0, 1.0);
+    cairo_paint (cr);
+
+    mask = cairo_surface_create_similar (cairo_get_group_target (cr),
+                                        CAIRO_CONTENT_ALPHA,
+                                        width, height);
+    cr2 = cairo_create (mask);
+    cairo_surface_destroy (mask);
+
+    mask = cairo_surface_create_similar (cairo_get_group_target (cr2),
+                                      CAIRO_CONTENT_ALPHA,
+                                      width, height);
+    cr3 = cairo_create (mask);
+    cairo_surface_destroy (mask);
+
+    cairo_save (cr3); {
+       cairo_set_operator (cr3, CAIRO_OPERATOR_CLEAR);
+       cairo_paint (cr3);
+    } cairo_restore (cr3);
+
+    pattern = cairo_pattern_create_linear (0, 0, width, height);
+    cairo_pattern_add_color_stop_rgba (pattern, 0.00, 0., 0., 0., 0.);
+    cairo_pattern_add_color_stop_rgba (pattern, 0.25, 1., 1., 1., 1.);
+    cairo_pattern_add_color_stop_rgba (pattern, 0.50, 1., 1., 1., .5);
+    cairo_pattern_add_color_stop_rgba (pattern, 0.75, 1., 1., 1., 1.);
+    cairo_pattern_add_color_stop_rgba (pattern, 1.00, 0., 0., 0., 0.);
+    cairo_set_source (cr3, pattern);
+    cairo_pattern_destroy (pattern);
+    cairo_paint (cr3);
+
+
+    cairo_save (cr2); {
+       cairo_set_operator (cr2, CAIRO_OPERATOR_CLEAR);
+       cairo_paint (cr2);
+    } cairo_restore (cr2);
+
+    pattern = cairo_pattern_create_radial (
+           0.5 * width, 0.5 * height, 0,
+           0.5 * width, 0.5 * height, 0.5 *height);
+    cairo_pattern_add_color_stop_rgba (pattern, 0.00, 0., 0., 0., 0.);
+    cairo_pattern_add_color_stop_rgba (pattern, 0.25, 1., 1., 1., 1.);
+    cairo_pattern_add_color_stop_rgba (pattern, 0.50, 1., 1., 1., .5);
+    cairo_pattern_add_color_stop_rgba (pattern, 1.00, 1., 1., 1., 1.);
+    cairo_set_source (cr2, pattern);
+    cairo_pattern_destroy (pattern);
+
+    cairo_mask_surface (cr2, cairo_get_target (cr3), 0, 0);
+    cairo_destroy (cr3);
+
+    cairo_set_source_rgb (cr, 1.0, 0, 0);
+    cairo_mask_surface (cr, cairo_get_target (cr2), 0, 0);
+    cairo_destroy (cr2);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (smask_mask,
+           "Test the support of \"soft\" masks with a secondary mask",
+           "smask, mask", /* keywords */
+           NULL, /* requirements */
+           60, 60,
+           NULL, draw)
diff --git a/test/smask-paint.c b/test/smask-paint.c
new file mode 100644 (file)
index 0000000..ee781ca
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * Copyright © 2008 Chris Wilson
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Chris Wilson not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Chris Wilson makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * CHRIS WILSON DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL CHRIS WILSON BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_surface_t *mask;
+    cairo_pattern_t *pattern;
+    cairo_t *cr2;
+
+    mask = cairo_surface_create_similar (cairo_get_group_target (cr),
+                                        CAIRO_CONTENT_ALPHA,
+                                        width, height);
+    cr2 = cairo_create (mask);
+    cairo_surface_destroy (mask);
+
+    cairo_save (cr2); {
+       cairo_set_operator (cr2, CAIRO_OPERATOR_CLEAR);
+       cairo_paint (cr2);
+    } cairo_restore (cr2);
+
+    pattern = cairo_pattern_create_linear (0, 0, width, height);
+    cairo_pattern_add_color_stop_rgba (pattern, 0.00, 0., 0., 0., 0.);
+    cairo_pattern_add_color_stop_rgba (pattern, 0.25, 1., 1., 1., 1.);
+    cairo_pattern_add_color_stop_rgba (pattern, 0.50, 1., 1., 1., .5);
+    cairo_pattern_add_color_stop_rgba (pattern, 0.75, 1., 1., 1., 1.);
+    cairo_pattern_add_color_stop_rgba (pattern, 1.00, 0., 0., 0., 0.);
+    cairo_set_source (cr2, pattern);
+    cairo_pattern_destroy (pattern);
+
+    cairo_paint (cr2);
+
+    cairo_set_source_rgb (cr, 0, 0, 1.0);
+    cairo_paint (cr);
+
+    pattern = cairo_pattern_create_radial (
+           0.5 * width, 0.5 * height, 0,
+           0.5 * width, 0.5 * height, 0.5 *height);
+    cairo_pattern_add_color_stop_rgba (pattern, 0.00, 0., 0., 0., 0.);
+    cairo_pattern_add_color_stop_rgba (pattern, 0.25, 1., 0., 0., 1.);
+    cairo_pattern_add_color_stop_rgba (pattern, 0.50, 1., 0., 0., .5);
+    cairo_pattern_add_color_stop_rgba (pattern, 1.00, 1., 0., 0., 1.);
+    cairo_set_source (cr, pattern);
+    cairo_pattern_destroy (pattern);
+
+    cairo_mask_surface (cr, cairo_get_target (cr2), 0, 0);
+    cairo_destroy (cr2);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (smask_paint,
+           "Test the support of \"soft\" masks with paints",
+           "smask, paint", /* keywords */
+           NULL, /* requirements */
+           60, 60,
+           NULL, draw)
diff --git a/test/smask-stroke.c b/test/smask-stroke.c
new file mode 100644 (file)
index 0000000..2a8e7e8
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * Copyright © 2008 Chris Wilson
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Chris Wilson not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Chris Wilson makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * CHRIS WILSON DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL CHRIS WILSON BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_surface_t *mask;
+    cairo_pattern_t *pattern;
+    cairo_t *cr2;
+
+    cairo_set_source_rgb (cr, 0, 0, 1.0);
+    cairo_paint (cr);
+
+    mask = cairo_surface_create_similar (cairo_get_group_target (cr),
+                                        CAIRO_CONTENT_ALPHA,
+                                        width, height);
+    cr2 = cairo_create (mask);
+    cairo_surface_destroy (mask);
+
+    cairo_save (cr2); {
+       cairo_set_operator (cr2, CAIRO_OPERATOR_CLEAR);
+       cairo_paint (cr2);
+    } cairo_restore (cr2);
+
+    pattern = cairo_pattern_create_linear (0, 0, width, height);
+    cairo_pattern_add_color_stop_rgba (pattern, 0.00, 0., 0., 0., 0.);
+    cairo_pattern_add_color_stop_rgba (pattern, 0.25, 1., 1., 1., 1.);
+    cairo_pattern_add_color_stop_rgba (pattern, 0.50, 1., 1., 1., .5);
+    cairo_pattern_add_color_stop_rgba (pattern, 0.75, 1., 1., 1., 1.);
+    cairo_pattern_add_color_stop_rgba (pattern, 1.00, 0., 0., 0., 0.);
+    cairo_set_source (cr2, pattern);
+    cairo_pattern_destroy (pattern);
+
+    cairo_arc (cr2, 0.5 * width, 0.5 * height, 0.4 * height, 0, 2 * M_PI);
+    cairo_stroke (cr2);
+
+    cairo_set_source_rgb (cr, 1.0, 0, 0);
+    cairo_mask_surface (cr, cairo_get_target (cr2), 0, 0);
+    cairo_destroy (cr2);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (smask_stroke,
+           "Test the support of \"soft\" masks with strokes",
+           "smask, stroke", /* keywords */
+           NULL, /* requirements */
+           60, 60,
+           NULL, draw)
diff --git a/test/smask-text.c b/test/smask-text.c
new file mode 100644 (file)
index 0000000..f36ae26
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * Copyright © 2008 Chris Wilson
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Chris Wilson not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Chris Wilson makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * CHRIS WILSON DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL CHRIS WILSON BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_surface_t *mask;
+    cairo_pattern_t *pattern;
+    cairo_t *cr2;
+    cairo_text_extents_t extents;
+
+    cairo_set_source_rgb (cr, 0, 0, 1.0);
+    cairo_paint (cr);
+
+    mask = cairo_surface_create_similar (cairo_get_group_target (cr),
+                                        CAIRO_CONTENT_ALPHA,
+                                        width, height);
+    cr2 = cairo_create (mask);
+    cairo_surface_destroy (mask);
+
+    cairo_save (cr2); {
+       cairo_set_operator (cr2, CAIRO_OPERATOR_CLEAR);
+       cairo_paint (cr2);
+    } cairo_restore (cr2);
+
+    pattern = cairo_pattern_create_linear (0, 0, width, height);
+    cairo_pattern_add_color_stop_rgba (pattern, 0.00, 0., 0., 0., 0.);
+    cairo_pattern_add_color_stop_rgba (pattern, 0.25, 1., 1., 1., 1.);
+    cairo_pattern_add_color_stop_rgba (pattern, 0.50, 1., 1., 1., .5);
+    cairo_pattern_add_color_stop_rgba (pattern, 0.75, 1., 1., 1., 1.);
+    cairo_pattern_add_color_stop_rgba (pattern, 1.00, 0., 0., 0., 0.);
+    cairo_set_source (cr2, pattern);
+    cairo_pattern_destroy (pattern);
+
+    cairo_select_font_face (cr2,
+                           CAIRO_TEST_FONT_FAMILY " Sans",
+                           CAIRO_FONT_SLANT_NORMAL,
+                           CAIRO_FONT_WEIGHT_NORMAL);
+    cairo_set_font_size (cr2, 0.5 * height);
+
+    cairo_text_extents (cr2, "cairo", &extents);
+    cairo_move_to (cr2,
+                  floor ((width - extents.width) / 2 + 0.5) - extents.x_bearing,
+                  floor ((height - extents.height) / 2 - 0.5) - extents.y_bearing);
+    cairo_show_text (cr2, "cairo");
+
+    cairo_set_source_rgb (cr, 1.0, 0, 0);
+    cairo_mask_surface (cr, cairo_get_target (cr2), 0, 0);
+    cairo_destroy (cr2);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (smask_text,
+           "Test the support of \"soft\" masks with text",
+           "smask, text", /* keywords */
+           NULL, /* keywords */
+           120, 60,
+           NULL, draw)
diff --git a/test/smask.c b/test/smask.c
new file mode 100644 (file)
index 0000000..4cf3adb
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ * Copyright © 2008 Chris Wilson
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Chris Wilson not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Chris Wilson makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * CHRIS WILSON DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL CHRIS WILSON BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    uint32_t data[] = {
+       0x80000000, 0x80000000,
+       0x80000000, 0x80000000,
+    };
+
+    cairo_surface_t *mask, *mask2;
+    cairo_pattern_t *pattern;
+    cairo_t *cr2;
+    cairo_text_extents_t extents;
+
+    mask = cairo_surface_create_similar (cairo_get_group_target (cr),
+                                        CAIRO_CONTENT_ALPHA,
+                                        width, height);
+    cr2 = cairo_create (mask);
+    cairo_surface_destroy (mask);
+
+    cairo_save (cr2); {
+       cairo_set_operator (cr2, CAIRO_OPERATOR_CLEAR);
+       cairo_paint (cr2);
+    } cairo_restore (cr2);
+
+    pattern = cairo_pattern_create_linear (0, 0, 0, height);
+    cairo_pattern_add_color_stop_rgba (pattern, 0.00, 0., 0., 0., 0.0);
+    cairo_pattern_add_color_stop_rgba (pattern, 0.80, 0., 0., 0., 0.0);
+    cairo_pattern_add_color_stop_rgba (pattern, 0.90, 1., 1., 1., 0.25);
+    cairo_pattern_add_color_stop_rgba (pattern, 1.00, 1., 1., 1., 1.0);
+    cairo_set_source (cr2, pattern);
+    cairo_pattern_destroy (pattern);
+
+    cairo_paint (cr2);
+
+    pattern = cairo_pattern_create_linear (0, 0, width, height);
+    cairo_pattern_add_color_stop_rgba (pattern, 0.00, 0., 0., 0., 0.);
+    cairo_pattern_add_color_stop_rgba (pattern, 0.25, 1., 1., 1., 1.);
+    cairo_pattern_add_color_stop_rgba (pattern, 0.50, 1., 1., 1., .5);
+    cairo_pattern_add_color_stop_rgba (pattern, 0.75, 1., 1., 1., 1.);
+    cairo_pattern_add_color_stop_rgba (pattern, 1.00, 0., 0., 0., 0.);
+    cairo_set_source (cr2, pattern);
+    cairo_pattern_destroy (pattern);
+
+    mask2 = cairo_image_surface_create_for_data ((unsigned char *) data,
+                                               CAIRO_FORMAT_ARGB32, 2, 2, 8);
+    pattern = cairo_pattern_create_for_surface (mask2);
+    cairo_pattern_set_extend (pattern, CAIRO_EXTEND_REPEAT);
+    cairo_mask (cr2, pattern);
+    cairo_pattern_destroy (pattern);
+
+    cairo_arc (cr2, 0.5 * width, 0.5 * height - 10, 0.2 * height, 0, 2 * M_PI);
+    cairo_fill (cr2);
+
+    cairo_arc (cr2, 0.5 * width, 0.5 * height - 10, 0.25 * height, 0, 2 * M_PI);
+    cairo_stroke (cr2);
+
+    cairo_select_font_face (cr2,
+                           CAIRO_TEST_FONT_FAMILY " Sans",
+                           CAIRO_FONT_SLANT_NORMAL,
+                           CAIRO_FONT_WEIGHT_NORMAL);
+    cairo_set_font_size (cr2, 0.3 * height);
+
+    cairo_text_extents (cr2, "FG", &extents);
+    cairo_move_to (cr2,
+                  floor ((width - extents.width) / 2 + 0.5) - extents.x_bearing,
+                  floor (height - extents.height - 0.5) - extents.y_bearing - 5);
+    cairo_show_text (cr2, "FG");
+
+    cairo_set_source_rgb (cr, 0, 0, 1.0);
+    cairo_paint (cr);
+
+    pattern = cairo_pattern_create_radial (
+           0.5 * width, 0.5 * height, 0,
+           0.5 * width, 0.5 * height, 0.5 *height);
+    cairo_pattern_add_color_stop_rgba (pattern, 0.00, 0., 0., 0., 0.);
+    cairo_pattern_add_color_stop_rgba (pattern, 0.25, 1., 0., 0., 1.);
+    cairo_pattern_add_color_stop_rgba (pattern, 0.50, 1., 0., 0., .5);
+    cairo_pattern_add_color_stop_rgba (pattern, 1.00, 1., 0., 0., 1.);
+    cairo_set_source (cr, pattern);
+    cairo_pattern_destroy (pattern);
+
+    cairo_mask_surface (cr, cairo_get_target (cr2), 0, 0);
+    cairo_destroy (cr2);
+
+    cairo_surface_finish (mask2); /* data will go out of scope */
+    cairo_surface_destroy (mask2);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (smask,
+           "Test the support of \"soft\" masks",
+           "smask", /* keywords */
+           NULL, /* requirements */
+           60, 60,
+           NULL, draw)
diff --git a/test/solid-pattern-cache-stress.c b/test/solid-pattern-cache-stress.c
new file mode 100644 (file)
index 0000000..c2a1c15
--- /dev/null
@@ -0,0 +1,212 @@
+/*
+ * Copyright © 2007 Chris Wilson.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Chris Wilson. Not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Chris Wilson makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * CHRIS WILSON DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL CHRIS WILSON BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Chris Wilson <chris at chris-wilson.co.uk>
+ */
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "cairo-test.h"
+#include <stdlib.h> /* drand48() */
+
+#define LOOPS 10
+#define NRAND 100
+
+#ifndef HAVE_DRAND48
+#define drand48() (rand () / (double) RAND_MAX)
+#endif
+
+static cairo_scaled_font_t *scaled_font;
+
+static cairo_t *
+_cairo_create_similar (cairo_t *cr, int width, int height)
+{
+    cairo_surface_t *similar;
+
+    similar = cairo_surface_create_similar (cairo_get_target (cr),
+                                           cairo_surface_get_content (cairo_get_target (cr)),
+                                           width, height);
+    cr = cairo_create (similar);
+    cairo_surface_destroy (similar);
+
+    return cr;
+}
+
+static cairo_t *
+_cairo_create_image (cairo_t *cr, cairo_format_t format, int width, int height)
+{
+    cairo_surface_t *image;
+
+    image = cairo_image_surface_create (format, width, height);
+    cr = cairo_create (image);
+    cairo_surface_destroy (image);
+
+    return cr;
+}
+
+static void
+_propagate_status (cairo_t *dst, cairo_t *src)
+{
+    cairo_path_t path;
+
+    path.status = cairo_status (src);
+    if (path.status) {
+       path.num_data = 0;
+       path.data = NULL;
+       cairo_append_path (dst, &path);
+    }
+}
+
+static void
+_draw (cairo_t *cr,
+       double red,
+       double green,
+       double blue)
+{
+    cairo_text_extents_t extents;
+
+    cairo_set_source_rgb (cr, red, green, blue);
+    cairo_paint (cr);
+
+    cairo_move_to (cr, 0, 0);
+    cairo_line_to (cr, 1, 1);
+    cairo_stroke (cr);
+
+    cairo_mask (cr, cairo_get_source (cr));
+
+    cairo_set_scaled_font (cr, scaled_font);
+    cairo_text_extents (cr, "cairo", &extents);
+    cairo_move_to (cr,
+                  -extents.x_bearing - .5 * extents.width,
+                  -extents.y_bearing - .5 * extents.height);
+    cairo_show_text (cr, "cairo");
+}
+
+static void
+use_similar (cairo_t *cr,
+           double red,
+           double green,
+           double blue)
+{
+    cairo_t *cr2;
+
+    if (cairo_status (cr))
+       return;
+
+    cr2 = _cairo_create_similar (cr, 1, 1);
+
+    _draw (cr2, red, green, blue);
+
+    _propagate_status (cr, cr2);
+    cairo_destroy (cr2);
+}
+
+static void
+use_image (cairo_t *cr,
+          cairo_format_t format,
+          double red,
+          double green,
+          double blue)
+{
+    cairo_t *cr2;
+
+    if (cairo_status (cr))
+       return;
+
+    cr2 = _cairo_create_image (cr, format, 1, 1);
+
+    _draw (cr2, red, green, blue);
+
+    _propagate_status (cr, cr2);
+    cairo_destroy (cr2);
+}
+
+static void
+use_solid (cairo_t *cr,
+          double red,
+          double green,
+          double blue)
+{
+    /* mix in dissimilar solids */
+    use_image (cr, CAIRO_FORMAT_A1, red, green, blue);
+    use_image (cr, CAIRO_FORMAT_A8, red, green, blue);
+    use_image (cr, CAIRO_FORMAT_RGB24, red, green, blue);
+    use_image (cr, CAIRO_FORMAT_ARGB32, red, green, blue);
+
+    use_similar (cr, red, green, blue);
+
+    _draw (cr, red, green, blue);
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    const cairo_test_context_t *ctx = cairo_test_get_context (cr);
+    cairo_status_t status;
+    const double colors[8][3] = {
+       { 1.0, 0.0, 0.0 }, /* red */
+       { 0.0, 1.0, 0.0 }, /* green */
+       { 1.0, 1.0, 0.0 }, /* yellow */
+       { 0.0, 0.0, 1.0 }, /* blue */
+       { 1.0, 0.0, 1.0 }, /* magenta */
+       { 0.0, 1.0, 1.0 }, /* cyan */
+       { 1.0, 1.0, 1.0 }, /* white */
+       { 0.0, 0.0, 0.0 }, /* black */
+    };
+    int i, j, loop;
+
+    /* cache a resolved scaled-font */
+    scaled_font = cairo_get_scaled_font (cr);
+
+    for (loop = 0; loop < LOOPS; loop++) {
+       for (i = 0; i < LOOPS; i++) {
+           for (j = 0; j < 8; j++) {
+               use_solid (cr, colors[j][0], colors[j][1], colors[j][2]);
+               status = cairo_status (cr);
+               if (status)
+                   return cairo_test_status_from_status (ctx, status);
+           }
+       }
+
+       for (i = 0; i < NRAND; i++) {
+           use_solid (cr, drand48 (), drand48 (), drand48 ());
+           status = cairo_status (cr);
+           if (status)
+               return cairo_test_status_from_status (ctx, status);
+       }
+    }
+
+    /* stress test only, so clear the surface before comparing */
+    cairo_set_source_rgb (cr, 0, 0, 1);
+    cairo_paint (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (solid_pattern_cache_stress,
+           "Stress the solid pattern cache and ensure it behaves",
+           "stress", /* keywords */
+           NULL, /* requirements */
+           1, 1,
+           NULL, draw)
diff --git a/test/source-clip-scale.c b/test/source-clip-scale.c
new file mode 100644 (file)
index 0000000..375f9be
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * Copyright © 2005 Mozilla Corporation
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Mozilla Corporation not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Mozilla Corporation makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * MOZILLA CORPORATION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL MOZILLA CORPORATION BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Vladimir Vukicevic <vladimir@pobox.com>
+ */
+
+#include <math.h>
+#include "cairo-test.h"
+#include <stdio.h>
+
+#define SIZE 12
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_surface_t *source;
+    cairo_t *cr2;
+
+    source = cairo_surface_create_similar (cairo_get_group_target (cr),
+                                          CAIRO_CONTENT_COLOR_ALPHA,
+                                          SIZE, SIZE);
+    cr2 = cairo_create (source);
+    cairo_surface_destroy (source);
+
+    /* Fill the source surface with green */
+    cairo_set_source_rgb (cr2, 0, 1, 0);
+    cairo_paint (cr2);
+
+    /* Draw a blue square in the middle of the source with clipping.
+     * Note that we are only clipping within a save/restore block but
+     * the buggy behavior demonstrates that the clip remains present
+     * on the surface. */
+    cairo_save (cr2);
+    cairo_rectangle (cr2,
+                    SIZE / 4, SIZE / 4,
+                    SIZE / 2, SIZE / 2);
+    cairo_clip (cr2);
+    cairo_set_source_rgb (cr2, 0, 0, 1);
+    cairo_paint (cr2);
+    cairo_restore (cr2);
+
+    /* Fill the destination surface with solid red (should not appear
+     * in final result) */
+    cairo_set_source_rgb (cr, 1, 0, 0);
+    cairo_paint (cr);
+
+    /* Now draw the source surface onto the destination with scaling. */
+    cairo_scale (cr, 2.0, 1.0);
+
+    cairo_set_source_surface (cr, cairo_get_target (cr2), 0, 0);
+    cairo_destroy (cr2);
+
+    cairo_paint (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (source_clip_scale,
+           "Test that a source surface is not affected by a clip when scaling",
+           "clip", /* keywords */
+           NULL, /* requirements */
+           SIZE * 2, SIZE,
+           NULL, draw)
diff --git a/test/source-clip.c b/test/source-clip.c
new file mode 100644 (file)
index 0000000..cbb2855
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Owen Taylor <otaylor@redhat.com>
+ */
+
+#include <math.h>
+#include "cairo-test.h"
+#include <stdio.h>
+
+#define SIZE 12
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_surface_t *source;
+    cairo_t *cr2;
+
+    source = cairo_surface_create_similar (cairo_get_group_target (cr),
+                                          CAIRO_CONTENT_COLOR_ALPHA,
+                                          SIZE, SIZE);
+
+    cr2 = cairo_create (source);
+    cairo_surface_destroy (source);
+
+    /* Fill the source surface with green */
+    cairo_set_source_rgb (cr2, 0, 1, 0);
+    cairo_paint (cr2);
+
+    /* Draw a blue square in the middle of the source with clipping,
+     * and leave the clip there. */
+    cairo_rectangle (cr2,
+                    SIZE / 4, SIZE / 4,
+                    SIZE / 2, SIZE / 2);
+    cairo_clip (cr2);
+    cairo_set_source_rgb (cr2, 0, 0, 1);
+    cairo_paint (cr2);
+
+    /* Fill the destination surface with solid red (should not appear
+     * in final result) */
+    cairo_set_source_rgb (cr, 1, 0, 0);
+    cairo_paint (cr);
+
+    /* Now draw the source surface onto the destination surface */
+    cairo_set_source_surface (cr, cairo_get_target (cr2), 0, 0);
+    cairo_paint (cr);
+
+    cairo_destroy (cr2);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (source_clip,
+           "Test that a source surface is not affected by a clip",
+           "clip", /* keywords */
+           NULL, /* requirements */
+           SIZE, SIZE,
+           NULL, draw)
diff --git a/test/source-surface-scale-paint.c b/test/source-surface-scale-paint.c
new file mode 100644 (file)
index 0000000..4ac6249
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ */
+
+#include "cairo-test.h"
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_surface_t *surface;
+    uint32_t data[16] = {
+       0xffffffff, 0xffffffff,         0xffff0000, 0xffff0000,
+       0xffffffff, 0xffffffff,         0xffff0000, 0xffff0000,
+
+       0xff00ff00, 0xff00ff00,         0xff0000ff, 0xff0000ff,
+       0xff00ff00, 0xff00ff00,         0xff0000ff, 0xff0000ff
+    };
+
+    surface = cairo_image_surface_create_for_data ((unsigned char *) data,
+                                                  CAIRO_FORMAT_RGB24, 4, 4, 16);
+
+    cairo_set_source_surface (cr, surface, 2, 2);
+    cairo_pattern_set_filter (cairo_get_source (cr), CAIRO_FILTER_NEAREST);
+    cairo_scale (cr, 2, 2);
+    cairo_paint (cr);
+
+    cairo_surface_finish (surface); /* data will go out of scope */
+    cairo_surface_destroy (surface);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (source_surface_scale_paint,
+           "Test call sequence: cairo_set_source_surface; cairo_scale; cairo_paint",
+           "transform, paint", /* keywords */
+           NULL, /* requirements */
+           8, 8,
+           NULL, draw)
diff --git a/test/spline-decomposition.c b/test/spline-decomposition.c
new file mode 100644 (file)
index 0000000..ea8f26f
--- /dev/null
@@ -0,0 +1,471 @@
+/*
+ * Copyright 2008 Chris Wilson
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Chris Wilson not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Chris Wilson makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * CHRIS WILSON DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL CHRIS WILSON BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+
+typedef struct _point {
+    double x,y;
+} point_t;
+
+typedef struct _knots {
+    point_t a,b,c,d;
+} knots_t;
+
+static knots_t knots[5] = {
+    { {0, 0}, {0, 100}, {100, 100}, {100, 0} },
+    { {0, 0}, {75, 100}, {25, 100}, {100, 0} },
+    { {0, 0}, {100, 100}, {0, 100}, {100, 0} },
+    { {0, 0}, {150, 100}, {-50, 100}, {100, 0} },
+    { {0, 0}, {100, 200}, {0, -100}, {100, 100} },
+};
+
+#ifdef REFERENCE
+static void
+_lerp_half (const point_t *a, const point_t *b, point_t *result)
+{
+    result->x = .5 * (a->x + b->x);
+    result->y = .5 * (a->y + b->y);
+}
+
+static void
+_de_casteljau (knots_t *k1, knots_t *k2)
+{
+    point_t ab, bc, cd;
+    point_t abbc, bccd;
+    point_t final;
+
+    _lerp_half (&k1->a, &k1->b, &ab);
+    _lerp_half (&k1->b, &k1->c, &bc);
+    _lerp_half (&k1->c, &k1->d, &cd);
+    _lerp_half (&ab, &bc, &abbc);
+    _lerp_half (&bc, &cd, &bccd);
+    _lerp_half (&abbc, &bccd, &final);
+
+    k2->a = final;
+    k2->b = bccd;
+    k2->c = cd;
+    k2->d = k1->d;
+
+    k1->b = ab;
+    k1->c = abbc;
+    k1->d = final;
+}
+
+static double
+_spline_error_squared (const knots_t *knots)
+{
+    double bdx, bdy, berr;
+    double cdx, cdy, cerr;
+    double dx, dy, v;
+
+    /* Intersection point (px):
+     *     px = p1 + u(p2 - p1)
+     *     (p - px) ∙ (p2 - p1) = 0
+     * Thus:
+     *     u = ((p - p1) ∙ (p2 - p1)) / ∥p2 - p1∥²;
+     */
+    bdx = knots->b.x - knots->a.x;
+    bdy = knots->b.y - knots->a.y;
+
+    cdx = knots->c.x - knots->a.x;
+    cdy = knots->c.y - knots->a.y;
+
+    dx = knots->d.x - knots->a.x;
+    dy = knots->d.y - knots->a.y;
+    v = dx * dx + dy * dy;
+    if (v != 0.) {
+       double u;
+
+       u = bdx * dx + bdy * dy;
+       if (u <= 0) {
+           /* bdx -= 0;
+            * bdy -= 0;
+            */
+       } else if (u >= v) {
+           bdx -= dx;
+           bdy -= dy;
+       } else {
+           bdx -= u/v * dx;
+           bdy -= u/v * dy;
+       }
+
+       u = cdx * dx + cdy * dy;
+       if (u <= 0) {
+           /* cdx -= 0;
+            * cdy -= 0;
+            */
+       } else if (u >= v) {
+           cdx -= dx;
+           cdy -= dy;
+       } else {
+           cdx -= u/v * dx;
+           cdy -= u/v * dy;
+       }
+    }
+
+    berr = bdx * bdx + bdy * bdy;
+    cerr = cdx * cdx + cdy * cdy;
+    if (berr > cerr)
+       return berr * v;
+    else
+       return cerr * v;
+}
+
+static void
+_offset_line_to (cairo_t *cr,
+                const point_t *p0,
+                const point_t *p1,
+                const point_t *p2,
+                const point_t *p3,
+                double offset)
+{
+    double dx, dy, v;
+
+    dx = p1->x - p0->x;
+    dy = p1->y - p0->y;
+     v = hypot (dx, dy);
+     if (v == 0) {
+        dx = p2->x - p0->x;
+        dy = p2->y - p0->y;
+        v = hypot (dx, dy);
+        if (v == 0) {
+            dx = p3->x - p0->x;
+            dy = p3->y - p0->y;
+            v = hypot (dx, dy);
+        }
+     }
+
+     if (v == 0) {
+        cairo_line_to (cr, p0->x, p0->y);
+     } else
+        cairo_line_to (cr, p0->x - offset * dy / v, p0->y + offset * dx / v);
+}
+
+static void
+_spline_decompose_into (knots_t *k1,
+                       double tolerance_squared,
+                       double offset,
+                       cairo_t *cr)
+{
+    knots_t k2;
+
+    if (_spline_error_squared (k1) < tolerance_squared) {
+       _offset_line_to (cr, &k1->a, &k1->b, &k1->c, &k1->d, offset);
+       return;
+    }
+
+    _de_casteljau (k1, &k2);
+
+    _spline_decompose_into (k1, tolerance_squared, offset, cr);
+    _spline_decompose_into (&k2, tolerance_squared, offset, cr);
+}
+
+static void
+_spline_decompose (const knots_t *knots,
+                  double tolerance, double offset,
+                  cairo_t *cr)
+{
+    knots_t k;
+
+    k = *knots;
+    _spline_decompose_into (&k, tolerance * tolerance, offset, cr);
+
+    _offset_line_to (cr, &knots->d, &knots->c, &knots->b, &knots->a, -offset);
+}
+
+static void
+_knots_reverse (knots_t *knots)
+{
+    point_t tmp;
+
+    tmp = knots->a;
+    knots->a = knots->d;
+    knots->d = tmp;
+
+    tmp = knots->b;
+    knots->b = knots->c;
+    knots->c = tmp;
+}
+
+static void
+thick_splines (cairo_t *cr, double offset)
+{
+    knots_t k;
+
+    cairo_save (cr);
+    cairo_translate (cr, 15, 15);
+
+    k = knots[0];
+
+    cairo_new_path (cr);
+    _spline_decompose (&k, .1, offset, cr);
+    _knots_reverse (&k);
+    _spline_decompose (&k, .1, offset, cr);
+    cairo_close_path (cr);
+    cairo_fill (cr);
+
+    cairo_translate (cr, 130, 0);
+
+    k = knots[1];
+
+    cairo_new_path (cr);
+    _spline_decompose (&k, .1, offset, cr);
+    _knots_reverse (&k);
+    _spline_decompose (&k, .1, offset, cr);
+    cairo_close_path (cr);
+    cairo_fill (cr);
+
+    cairo_translate (cr, 130, 0);
+
+    k = knots[2];
+
+    cairo_new_path (cr);
+    _spline_decompose (&k, .1, offset, cr);
+    _knots_reverse (&k);
+    _spline_decompose (&k, .1, offset, cr);
+    cairo_close_path (cr);
+    cairo_fill (cr);
+
+    cairo_translate (cr, -130 - 65, 130);
+
+    k = knots[3];
+
+    cairo_new_path (cr);
+    _spline_decompose (&k, .1, offset, cr);
+    _knots_reverse (&k);
+    _spline_decompose (&k, .1, offset, cr);
+    cairo_close_path (cr);
+    cairo_fill (cr);
+
+    cairo_translate (cr, 130, 0);
+
+    k = knots[4];
+
+    cairo_new_path (cr);
+    _spline_decompose (&k, .1, offset, cr);
+    _knots_reverse (&k);
+    _spline_decompose (&k, .1, offset, cr);
+    cairo_close_path (cr);
+    cairo_fill (cr);
+    cairo_restore (cr);
+}
+
+static void
+thin_splines (cairo_t *cr)
+{
+    cairo_save (cr);
+    cairo_translate (cr, 15, 15);
+
+    cairo_new_path (cr);
+    _spline_decompose (&knots[0], .1, 0, cr);
+    cairo_stroke (cr);
+
+    cairo_translate (cr, 130, 0);
+
+    cairo_new_path (cr);
+    _spline_decompose (&knots[1], .1, 0, cr);
+    cairo_stroke (cr);
+
+    cairo_translate (cr, 130, 0);
+
+    cairo_new_path (cr);
+    _spline_decompose (&knots[2], .1, 0, cr);
+    cairo_stroke (cr);
+
+    cairo_translate (cr, -130 - 65, 130);
+
+    cairo_new_path (cr);
+    _spline_decompose (&knots[3], .1, 0, cr);
+    cairo_stroke (cr);
+
+    cairo_translate (cr, 130, 0);
+
+    cairo_new_path (cr);
+    _spline_decompose (&knots[4], .1, 0, cr);
+    cairo_stroke (cr);
+    cairo_restore (cr);
+}
+#endif
+
+static void
+draw_bbox (cairo_t *cr, double x0, double y0, double x1, double y1)
+{
+    cairo_rectangle (cr,
+                    floor (x0) + .5, floor (y0) + .5,
+                    ceil (x1) - floor (x0), ceil (y1) - floor (y0));
+    cairo_stroke (cr);
+}
+
+static void
+stroke_splines (cairo_t *cr)
+{
+    double stroke_x0, stroke_x1, stroke_y0, stroke_y1;
+    double path_x0, path_x1, path_y0, path_y1;
+
+    cairo_save (cr);
+    cairo_translate (cr, 15, 15);
+
+    cairo_new_path (cr);
+    cairo_move_to (cr,
+                  knots[0].a.x, knots[0].a.y);
+    cairo_curve_to (cr,
+                   knots[0].b.x, knots[0].b.y,
+                   knots[0].c.x, knots[0].c.y,
+                   knots[0].d.x, knots[0].d.y);
+    cairo_stroke_extents (cr, &stroke_x0, &stroke_y0, &stroke_x1, &stroke_y1);
+    cairo_path_extents (cr, &path_x0, &path_y0, &path_x1, &path_y1);
+    cairo_stroke (cr);
+
+    cairo_save (cr); {
+       cairo_set_line_width (cr, 1);
+       cairo_set_source_rgb (cr, 1, 0, 0);
+       draw_bbox (cr, stroke_x0, stroke_y0, stroke_x1, stroke_y1);
+       cairo_set_source_rgb (cr, 0, 0, 1);
+       draw_bbox (cr, path_x0, path_y0, path_x1, path_y1);
+    } cairo_restore (cr);
+
+    cairo_translate (cr, 130, 0);
+
+    cairo_new_path (cr);
+    cairo_move_to (cr,
+                  knots[1].a.x, knots[1].a.y);
+    cairo_curve_to (cr,
+                   knots[1].b.x, knots[1].b.y,
+                   knots[1].c.x, knots[1].c.y,
+                   knots[1].d.x, knots[1].d.y);
+    cairo_stroke_extents (cr, &stroke_x0, &stroke_y0, &stroke_x1, &stroke_y1);
+    cairo_path_extents (cr, &path_x0, &path_y0, &path_x1, &path_y1);
+    cairo_stroke (cr);
+
+    cairo_save (cr); {
+       cairo_set_line_width (cr, 1);
+       cairo_set_source_rgb (cr, 1, 0, 0);
+       draw_bbox (cr, stroke_x0, stroke_y0, stroke_x1, stroke_y1);
+       cairo_set_source_rgb (cr, 0, 0, 1);
+       draw_bbox (cr, path_x0, path_y0, path_x1, path_y1);
+    } cairo_restore (cr);
+
+    cairo_translate (cr, 130, 0);
+
+    cairo_new_path (cr);
+    cairo_move_to (cr,
+                  knots[2].a.x, knots[2].a.y);
+    cairo_curve_to (cr,
+                   knots[2].b.x, knots[2].b.y,
+                   knots[2].c.x, knots[2].c.y,
+                   knots[2].d.x, knots[2].d.y);
+    cairo_stroke_extents (cr, &stroke_x0, &stroke_y0, &stroke_x1, &stroke_y1);
+    cairo_path_extents (cr, &path_x0, &path_y0, &path_x1, &path_y1);
+    cairo_stroke (cr);
+
+    cairo_save (cr); {
+       cairo_set_line_width (cr, 1);
+       cairo_set_source_rgb (cr, 1, 0, 0);
+       draw_bbox (cr, stroke_x0, stroke_y0, stroke_x1, stroke_y1);
+       cairo_set_source_rgb (cr, 0, 0, 1);
+       draw_bbox (cr, path_x0, path_y0, path_x1, path_y1);
+    } cairo_restore (cr);
+
+    cairo_translate (cr, -130 - 65, 130);
+
+    cairo_new_path (cr);
+    cairo_move_to (cr,
+                  knots[3].a.x, knots[3].a.y);
+    cairo_curve_to (cr,
+                   knots[3].b.x, knots[3].b.y,
+                   knots[3].c.x, knots[3].c.y,
+                   knots[3].d.x, knots[3].d.y);
+    cairo_stroke_extents (cr, &stroke_x0, &stroke_y0, &stroke_x1, &stroke_y1);
+    cairo_path_extents (cr, &path_x0, &path_y0, &path_x1, &path_y1);
+    cairo_stroke (cr);
+
+    cairo_save (cr); {
+       cairo_set_line_width (cr, 1);
+       cairo_set_source_rgb (cr, 1, 0, 0);
+       draw_bbox (cr, stroke_x0, stroke_y0, stroke_x1, stroke_y1);
+       cairo_set_source_rgb (cr, 0, 0, 1);
+       draw_bbox (cr, path_x0, path_y0, path_x1, path_y1);
+    } cairo_restore (cr);
+
+    cairo_translate (cr, 130, 0);
+
+    cairo_new_path (cr);
+    cairo_move_to (cr,
+                  knots[4].a.x, knots[4].a.y);
+    cairo_curve_to (cr,
+                   knots[4].b.x, knots[4].b.y,
+                   knots[4].c.x, knots[4].c.y,
+                   knots[4].d.x, knots[4].d.y);
+    cairo_stroke_extents (cr, &stroke_x0, &stroke_y0, &stroke_x1, &stroke_y1);
+    cairo_path_extents (cr, &path_x0, &path_y0, &path_x1, &path_y1);
+    cairo_stroke (cr);
+
+    cairo_save (cr); {
+       cairo_set_line_width (cr, 1);
+       cairo_set_source_rgb (cr, 1, 0, 0);
+       draw_bbox (cr, stroke_x0, stroke_y0, stroke_x1, stroke_y1);
+       cairo_set_source_rgb (cr, 0, 0, 1);
+       draw_bbox (cr, path_x0, path_y0, path_x1, path_y1);
+    } cairo_restore (cr);
+
+    cairo_restore (cr);
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_paint (cr);
+
+#ifdef REFERENCE
+    cairo_set_source_rgb (cr, 0, 0, 0);
+    thick_splines (cr, 5);
+
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    thin_splines (cr);
+#endif
+
+    /*
+     * Use a high tolerance to reduce dependence upon algorithm used for
+     * spline decomposition.
+     */
+    cairo_set_tolerance (cr, 0.001);
+
+    cairo_set_line_width (cr, 10);
+    cairo_set_source_rgb (cr, 0, 0, 0);
+    stroke_splines (cr);
+    cairo_set_line_width (cr, 2);
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    stroke_splines (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (spline_decomposition,
+           "Tests splines with various inflection points",
+           "stroke, spline", /* keywords */
+           NULL, /* requirements */
+           390, 260,
+           NULL, draw)
diff --git a/test/stroke-ctm-caps.c b/test/stroke-ctm-caps.c
new file mode 100644 (file)
index 0000000..1bbee83
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * Copyright © 2008 Adrian Johnson
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Adrian Johnson <ajohnson@redneon.com>
+ */
+
+#include "cairo-test.h"
+
+#define SIZE 100
+#define PAD 2
+#define WIDTH (PAD + SIZE + PAD)
+#define HEIGHT WIDTH
+
+/* This test is designed to test that PDF viewers use the correct
+ * alpha values in an Alpha SMasks. Some viewers use the color values
+ * instead of the alpha. The test draws a triangle and rectangle in a
+ * group then draws the group using cairo_mask(). The mask consists of
+ * a circle with the rgba (0.4, 0.4, 0.4, 0.8) and the background rgba
+ * (0.8, 0.8, 0.8, 0.4).
+ */
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_paint (cr);
+
+    /* flip the CTM, which most clearly shows the problem */
+    cairo_translate (cr, 0, HEIGHT);
+    cairo_scale (cr, 1, -1);
+
+    cairo_set_source_rgb (cr, 0, 0, 0);
+
+    cairo_set_line_width (cr, 10);
+    cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
+    cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND);
+
+    cairo_move_to (cr, 20, 20);
+    cairo_line_to (cr, 20, 70);
+    cairo_stroke (cr);
+
+    cairo_move_to (cr, 40, 20);
+    cairo_line_to (cr, 70, 70);
+    cairo_stroke (cr);
+
+    cairo_move_to (cr, 60, 20);
+    cairo_line_to (cr, 90, 20);
+    cairo_stroke (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (stroke_ctm_caps,
+           "Test that the stroker correctly passes the device-space vector to the stroker for endcaps",
+           "stroke, transform", /* keywords */
+           NULL, /* requirements */
+           WIDTH, HEIGHT,
+           NULL, draw)
diff --git a/test/stroke-image.c b/test/stroke-image.c
new file mode 100644 (file)
index 0000000..17e6653
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * Copyright © 2006 Mozilla Corporation
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Mozilla Corporation not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Mozilla Corporation makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * MOZILLA CORPORATION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL MOZILLA CORPORATION BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Vladimir Vukicevic <vladimir@pobox.com>
+ */
+
+#include "cairo-test.h"
+
+#define PAD 10
+#define SIZE 100
+#define IMAGE_SIZE (SIZE-PAD*2)
+#define LINE_WIDTH 10
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_surface_t *image;
+    cairo_t *cr_image;
+
+    cairo_set_source_rgb (cr, 0, 0, 0);
+    cairo_paint (cr);
+
+    image = cairo_image_surface_create (CAIRO_FORMAT_RGB24, IMAGE_SIZE, IMAGE_SIZE);
+    cr_image = cairo_create (image);
+    cairo_surface_destroy (image);
+
+    /* Create the image */
+    cairo_set_source_rgb (cr_image, 0, 0, 0);
+    cairo_paint (cr_image);
+    cairo_set_source_rgb (cr_image, 0, 1, 0);
+    cairo_set_line_width (cr_image, LINE_WIDTH);
+    cairo_arc (cr_image, IMAGE_SIZE/2, IMAGE_SIZE/2, IMAGE_SIZE/2 - LINE_WIDTH/2, 0, M_PI * 2.0);
+    cairo_stroke (cr_image);
+
+    /* Now stroke with it */
+    cairo_translate (cr, PAD, PAD);
+
+    cairo_set_source_surface (cr, cairo_get_target (cr_image), 0, 0);
+    cairo_destroy (cr_image);
+
+    cairo_new_path (cr);
+    cairo_set_line_width (cr, LINE_WIDTH);
+    cairo_arc (cr, IMAGE_SIZE/2, IMAGE_SIZE/2, IMAGE_SIZE/2 - LINE_WIDTH/2, 0, M_PI * 2.0);
+    cairo_stroke (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (stroke_image,
+           "Test stroking with an image source, with a non-identity CTM",
+           "stroke, image, transform", /* keywords */
+           NULL, /* requirements */
+           SIZE, SIZE,
+           NULL, draw)
diff --git a/test/stroke-open-box.c b/test/stroke-open-box.c
new file mode 100644 (file)
index 0000000..b1dae50
--- /dev/null
@@ -0,0 +1,51 @@
+/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/*
+ * Copyright 2011 Simon Kellner
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Simon Kellner <kellner@kit.edu>
+ */
+
+#include "cairo-test.h"
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_paint (cr);
+
+    cairo_set_source_rgb (cr, 0, 0, 0);
+    cairo_move_to (cr, 5, 7);
+    cairo_rel_line_to (cr, 20, 0);
+    cairo_rel_line_to (cr, 0, 15);
+    cairo_rel_line_to (cr, -20, 0);
+    cairo_stroke (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (stroke_open_box,
+           "Tests stroking of a 3-sided box",
+           "stroke,box", /* keywords */
+           NULL, /* requirements */
+           30, 32,
+           NULL, draw)
diff --git a/test/stroke-pattern.c b/test/stroke-pattern.c
new file mode 100644 (file)
index 0000000..01e1019
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * Copyright © 2011 Adrian Johnson
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Adrian Johnson <ajohnson@redneon.com>
+ */
+
+#include "cairo-test.h"
+
+#define IMAGE_WIDTH 80
+#define IMAGE_HEIGHT 80
+
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_pattern_t *pattern;
+
+    cairo_test_paint_checkered (cr);
+
+    cairo_scale (cr, 0.3, 0.3);
+    cairo_translate (cr, 50, 50);
+
+    pattern = cairo_pattern_create_linear (70, 100, 130, 100);
+    cairo_pattern_add_color_stop_rgba (pattern, 0,  1, 0, 0,  1.0);
+    cairo_pattern_add_color_stop_rgba (pattern, 1,  0, 1, 0,  0.5);
+
+    cairo_pattern_set_extend (pattern, CAIRO_EXTEND_PAD);
+    cairo_set_source (cr, pattern);
+
+    cairo_move_to(cr, 20, 20);
+    cairo_curve_to(cr,
+                   130, 0,
+                   70, 200,
+                   180, 180);
+    cairo_set_line_width (cr, 20);
+    cairo_stroke (cr);
+
+    cairo_pattern_destroy (pattern);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (stroke_pattern,
+           "Patterned stroke",
+           "stroke, pattern", /* keywords */
+           NULL, /* requirements */
+           IMAGE_WIDTH, IMAGE_HEIGHT,
+           NULL, draw)
diff --git a/test/subsurface-image-repeat.c b/test/subsurface-image-repeat.c
new file mode 100644 (file)
index 0000000..d58dd92
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2009 Intel Corporation
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Intel not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Intel makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * INTEL CORPORATION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL INTEL CORPORATION BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+
+static const char *png_filename = "romedalen.png";
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    const cairo_test_context_t *ctx = cairo_test_get_context (cr);
+    cairo_surface_t *image, *region;
+    cairo_t *cr_region;
+
+    cairo_set_source_rgb (cr, .5, .5, .5);
+    cairo_paint (cr);
+
+    /* fill the centre */
+    region = cairo_surface_create_for_rectangle (cairo_get_target (cr),
+                                                20, 20, 20, 20);
+    cr_region = cairo_create (region);
+    cairo_surface_destroy (region);
+
+    image = cairo_test_create_surface_from_png (ctx, png_filename);
+    cairo_set_source_surface (cr_region, image,
+                             10 - cairo_image_surface_get_width (image)/2,
+                             10 - cairo_image_surface_get_height (image)/2);
+    cairo_paint (cr_region);
+    cairo_surface_destroy (image);
+
+    cairo_set_source_surface (cr, cairo_get_target (cr_region), 20, 20);
+    cairo_destroy (cr_region);
+
+    /* repeat the pattern around the outside, but do not overwrite...*/
+    cairo_pattern_set_extend (cairo_get_source (cr), CAIRO_EXTEND_REPEAT);
+    cairo_rectangle (cr, 0, 0, width, height);
+    cairo_rectangle (cr, 20, 40, 20, -20);
+    cairo_fill (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (subsurface_image_repeat,
+           "Tests source (image) clipping with repeat",
+           "subsurface, image, repeat", /* keywords */
+           NULL, /* requirements */
+           60, 60,
+           NULL, draw)
diff --git a/test/subsurface-modify-child.c b/test/subsurface-modify-child.c
new file mode 100644 (file)
index 0000000..3a1e8ff
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2010 Intel Corporation
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Intel not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Intel makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * INTEL CORPORATION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL INTEL CORPORATION BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_surface_t *region, *similar;
+    cairo_t *cr_region, *cr_similar;
+
+    cairo_set_source_rgb (cr, .5, .5, .5);
+    cairo_paint (cr);
+
+    similar = cairo_surface_create_similar (cairo_get_target (cr),
+                                           CAIRO_CONTENT_COLOR_ALPHA,
+                                           20, 20);
+
+    /* copy the centre */
+    cr_similar = cairo_create (similar);
+    cairo_surface_destroy (similar);
+    cairo_set_source_surface (cr_similar, cairo_get_target (cr), -20, -20);
+    cairo_paint (cr_similar);
+    similar = cairo_surface_reference (cairo_get_target (cr_similar));
+    cairo_destroy (cr_similar);
+
+    /* fill the centre */
+    region = cairo_surface_create_for_rectangle (cairo_get_target (cr),
+                                                20, 20, 20, 20);
+    cr_region = cairo_create (region);
+    cairo_surface_destroy (region);
+
+    cairo_set_source_rgb (cr_region, 1, 1, 1);
+    cairo_rectangle (cr_region, 0, 0, 10, 10);
+    cairo_fill (cr_region);
+
+    cairo_set_source_rgb (cr_region, 1, 0, 0);
+    cairo_rectangle (cr_region, 10, 0, 10, 10);
+    cairo_fill (cr_region);
+
+    cairo_set_source_rgb (cr_region, 0, 1, 0);
+    cairo_rectangle (cr_region, 0, 10, 10, 10);
+    cairo_fill (cr_region);
+
+    cairo_set_source_rgb (cr_region, 0, 0, 1);
+    cairo_rectangle (cr_region, 10, 10, 10, 10);
+    cairo_fill (cr_region);
+
+    cairo_destroy (cr_region);
+
+    /* copy the centre, again */
+    cr_similar = cairo_create (similar);
+    cairo_surface_destroy (similar);
+    cairo_set_source_surface (cr_similar, cairo_get_target (cr), -20, -20);
+    cairo_paint (cr_similar);
+    similar = cairo_surface_reference (cairo_get_target (cr_similar));
+    cairo_destroy (cr_similar);
+
+    /* repeat the pattern around the outside, but do not overwrite...*/
+    cairo_set_source_surface (cr, similar, 20, 20);
+    cairo_pattern_set_extend (cairo_get_source (cr), CAIRO_EXTEND_REPEAT);
+    cairo_rectangle (cr, 0, 0, width, height);
+    cairo_rectangle (cr, 20, 40, 20, -20);
+    cairo_fill (cr);
+
+    cairo_surface_destroy (similar);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (subsurface_modify_child,
+           "Tests source clipping with later modifications",
+           "subsurface", /* keywords */
+           NULL, /* requirements */
+           60, 60,
+           NULL, draw)
diff --git a/test/subsurface-modify-parent.c b/test/subsurface-modify-parent.c
new file mode 100644 (file)
index 0000000..3d542d5
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2010 Intel Corporation
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Intel not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Intel makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * INTEL CORPORATION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL INTEL CORPORATION BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_surface_t *region;
+
+    cairo_set_source_rgb (cr, .5, .5, .5);
+    cairo_paint (cr);
+
+    /* fill the centre, but through the *original* surface */
+    region = cairo_surface_create_for_rectangle (cairo_get_target (cr),
+                                                20, 20, 20, 20);
+
+    /* first trigger a snapshot of the region... */
+    cairo_set_source_surface (cr, region, 20, 20);
+    cairo_pattern_set_extend (cairo_get_source (cr), CAIRO_EXTEND_REPEAT);
+    cairo_paint (cr);
+
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_rectangle (cr, 20, 20, 10, 10);
+    cairo_fill (cr);
+
+    cairo_set_source_rgb (cr, 1, 0, 0);
+    cairo_rectangle (cr, 30, 20, 10, 10);
+    cairo_fill (cr);
+
+    cairo_set_source_rgb (cr, 0, 1, 0);
+    cairo_rectangle (cr, 20, 30, 10, 10);
+    cairo_fill (cr);
+
+    cairo_set_source_rgb (cr, 0, 0, 1);
+    cairo_rectangle (cr, 30, 30, 10, 10);
+    cairo_fill (cr);
+
+    cairo_set_source_surface (cr, region, 20, 20);
+    cairo_surface_destroy (region);
+
+    /* repeat the pattern around the outside, but do not overwrite...*/
+    cairo_pattern_set_extend (cairo_get_source (cr), CAIRO_EXTEND_REPEAT);
+    cairo_rectangle (cr, 0, 0, width, height);
+    cairo_rectangle (cr, 20, 40, 20, -20);
+    cairo_fill (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (subsurface_modify_parent,
+           "Tests source clipping with later modifications",
+           "subsurface", /* keywords */
+           NULL, /* requirements */
+           60, 60,
+           NULL, draw)
diff --git a/test/subsurface-outside-target.c b/test/subsurface-outside-target.c
new file mode 100644 (file)
index 0000000..5e84a84
--- /dev/null
@@ -0,0 +1,177 @@
+/*
+ * Copyright 2010 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Benjamin Otte <otte@gnome.org>
+ */
+
+#include "cairo-test.h"
+
+#define TARGET_SIZE 10
+
+#define SUB_SIZE 15
+#define SUB_OFFSET -5
+
+#define PAINT_OFFSET SUB_SIZE
+#define PAINT_SIZE (3 * SUB_SIZE)
+
+static cairo_content_t contents[] = { CAIRO_CONTENT_ALPHA,
+                                      CAIRO_CONTENT_COLOR,
+                                      CAIRO_CONTENT_COLOR_ALPHA };
+
+#define N_CONTENTS ARRAY_LENGTH (contents)
+#define N_PADS (CAIRO_EXTEND_PAD + 1)
+
+
+static cairo_surface_t *
+create_target (cairo_surface_t *similar_to,
+               cairo_content_t content)
+{
+    cairo_surface_t *surface;
+    cairo_t *cr;
+
+    surface = cairo_surface_create_similar (similar_to,
+                                            content,
+                                            TARGET_SIZE, TARGET_SIZE);
+    
+    cr = cairo_create (surface);
+    cairo_test_paint_checkered (cr);
+    cairo_destroy (cr);
+
+    return surface;
+}
+
+static cairo_test_status_t
+check_surface_extents (const cairo_test_context_t *ctx,
+                       cairo_surface_t *           surface,
+                       double                      x,
+                       double                      y,
+                       double                      width,
+                       double                      height)
+{
+    double x1, y1, x2, y2;
+    cairo_t *cr;
+
+    cr = cairo_create (surface);
+    cairo_clip_extents (cr, &x1, &y1, &x2, &y2);
+    cairo_destroy (cr);
+
+    if (x != x1 ||
+        y != y1 ||
+        width != x2 - x1 ||
+        height != y2 - y1) {
+        cairo_test_log (ctx,
+                        "surface extents should be (%g, %g, %g, %g), but are (%g, %g, %g, %g)\n",
+                        x, y, width, height,
+                        x1, y1, x2 - x1, y2 - y1);
+        return CAIRO_TEST_FAILURE;
+    }
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+static cairo_test_status_t
+draw_for_size (cairo_t *cr,
+               double   x,
+               double   y)
+{
+    cairo_surface_t *target, *subsurface;
+    cairo_extend_t extend;
+    cairo_test_status_t check, result = CAIRO_TEST_SUCCESS;
+    unsigned int content;
+
+    for (content = 0; content < N_CONTENTS; content++) {
+        cairo_save (cr);
+
+        /* create a target surface for our subsurface */
+        target = create_target (cairo_get_target (cr),
+                                contents[content]);
+
+        /* create a subsurface that extends the target surface */
+        subsurface = cairo_surface_create_for_rectangle (target, 
+                                                         x, y,
+                                                         SUB_SIZE, SUB_SIZE);
+
+        /* ensure the extents are ok */
+        check = check_surface_extents (cairo_test_get_context (cr),
+                                       subsurface,
+                                       0, 0,
+                                       SUB_SIZE, SUB_SIZE);
+        if (result == CAIRO_TEST_SUCCESS)
+          result = check;
+
+        /* paint this surface with all extend modes. */
+        for (extend = 0; extend < N_PADS; extend++) {
+            cairo_save (cr);
+
+            cairo_rectangle (cr, 0, 0, PAINT_SIZE, PAINT_SIZE);
+            cairo_clip (cr);
+
+            cairo_set_source_surface (cr, subsurface, PAINT_OFFSET, PAINT_OFFSET);
+            cairo_pattern_set_extend (cairo_get_source (cr), extend);
+            cairo_paint (cr);
+
+            cairo_restore (cr);
+
+            cairo_translate (cr, PAINT_SIZE + TARGET_SIZE, 0);
+        }
+
+        cairo_surface_destroy (subsurface);
+        cairo_surface_destroy (target);
+
+        cairo_restore (cr);
+
+        cairo_translate (cr, 0, PAINT_SIZE + TARGET_SIZE);
+    }
+
+    return result;
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_test_status_t check, result = CAIRO_TEST_SUCCESS;
+
+    /* paint background in nice gray */
+    cairo_set_source_rgb (cr, 0.51613, 0.55555, 0.51613);
+    cairo_paint (cr);
+
+    /* Use CAIRO_OPERATOR_SOURCE in the tests so we get the actual
+     * contents of the subsurface */
+    cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+
+    result = draw_for_size (cr, SUB_OFFSET, SUB_OFFSET);
+
+    check = draw_for_size (cr, 0, 0);
+    if (result == CAIRO_TEST_SUCCESS)
+      result = check;
+
+    return result;
+}
+
+CAIRO_TEST (subsurface_outside_target,
+           "Tests contents of subsurfaces outside target area",
+           "subsurface, pad", /* keywords */
+           NULL, /* requirements */
+           (PAINT_SIZE + TARGET_SIZE) * N_PADS         - TARGET_SIZE,
+            (PAINT_SIZE + TARGET_SIZE) * N_CONTENTS * 2 - TARGET_SIZE,
+           NULL, draw)
diff --git a/test/subsurface-pad.c b/test/subsurface-pad.c
new file mode 100644 (file)
index 0000000..05e6d72
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2009 Intel Corporation
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Intel not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Intel makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * INTEL CORPORATION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL INTEL CORPORATION BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_surface_t *region;
+    cairo_t *cr_region;
+
+    cairo_set_source_rgb (cr, .5, .5, .5);
+    cairo_paint (cr);
+
+    /* fill the centre */
+    region = cairo_surface_create_for_rectangle (cairo_get_target (cr),
+                                                20, 20, 20, 20);
+    cr_region = cairo_create (region);
+    cairo_surface_destroy (region);
+
+    cairo_set_source_rgb (cr_region, 1, 1, 1);
+    cairo_rectangle (cr_region, 0, 0, 10, 10);
+    cairo_fill (cr_region);
+
+    cairo_set_source_rgb (cr_region, 1, 0, 0);
+    cairo_rectangle (cr_region, 10, 0, 10, 10);
+    cairo_fill (cr_region);
+
+    cairo_set_source_rgb (cr_region, 0, 1, 0);
+    cairo_rectangle (cr_region, 0, 10, 10, 10);
+    cairo_fill (cr_region);
+
+    cairo_set_source_rgb (cr_region, 0, 0, 1);
+    cairo_rectangle (cr_region, 10, 10, 10, 10);
+    cairo_fill (cr_region);
+
+    cairo_set_source_surface (cr, cairo_get_target (cr_region), 20, 20);
+    cairo_destroy (cr_region);
+
+    /* reflect the pattern around the outside, but do not overwrite...*/
+    cairo_pattern_set_extend (cairo_get_source (cr), CAIRO_EXTEND_PAD);
+    cairo_rectangle (cr, 0, 0, width, height);
+    cairo_rectangle (cr, 20, 40, 20, -20);
+    cairo_fill (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (subsurface_pad,
+           "Tests source clipping with pad",
+           "subsurface, pad", /* keywords */
+           NULL, /* requirements */
+           60, 60,
+           NULL, draw)
diff --git a/test/subsurface-reflect.c b/test/subsurface-reflect.c
new file mode 100644 (file)
index 0000000..e7ddbac
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2009 Intel Corporation
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Intel not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Intel makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * INTEL CORPORATION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL INTEL CORPORATION BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_surface_t *region;
+    cairo_t *cr_region;
+
+    cairo_set_source_rgb (cr, .5, .5, .5);
+    cairo_paint (cr);
+
+    /* fill the centre */
+    region = cairo_surface_create_for_rectangle (cairo_get_target (cr),
+                                                20, 20, 20, 20);
+    cr_region = cairo_create (region);
+    cairo_surface_destroy (region);
+
+    cairo_set_source_rgb (cr_region, 1, 1, 1);
+    cairo_rectangle (cr_region, 0, 0, 10, 10);
+    cairo_fill (cr_region);
+
+    cairo_set_source_rgb (cr_region, 1, 0, 0);
+    cairo_rectangle (cr_region, 10, 0, 10, 10);
+    cairo_fill (cr_region);
+
+    cairo_set_source_rgb (cr_region, 0, 1, 0);
+    cairo_rectangle (cr_region, 0, 10, 10, 10);
+    cairo_fill (cr_region);
+
+    cairo_set_source_rgb (cr_region, 0, 0, 1);
+    cairo_rectangle (cr_region, 10, 10, 10, 10);
+    cairo_fill (cr_region);
+
+    cairo_set_source_surface (cr, cairo_get_target (cr_region), 20, 20);
+    cairo_destroy (cr_region);
+
+    /* reflect the pattern around the outside, but do not overwrite...*/
+    cairo_pattern_set_extend (cairo_get_source (cr), CAIRO_EXTEND_REFLECT);
+    cairo_rectangle (cr, 0, 0, width, height);
+    cairo_rectangle (cr, 20, 40, 20, -20);
+    cairo_fill (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (subsurface_reflect,
+           "Tests source clipping with reflect",
+           "subsurface, reflect", /* keywords */
+           NULL, /* requirements */
+           60, 60,
+           NULL, draw)
diff --git a/test/subsurface-repeat.c b/test/subsurface-repeat.c
new file mode 100644 (file)
index 0000000..84e25f4
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2009 Intel Corporation
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Intel not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Intel makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * INTEL CORPORATION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL INTEL CORPORATION BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_surface_t *region;
+    cairo_t *cr_region;
+
+    cairo_set_source_rgb (cr, .5, .5, .5);
+    cairo_paint (cr);
+
+    /* fill the centre */
+    region = cairo_surface_create_for_rectangle (cairo_get_target (cr),
+                                                20, 20, 20, 20);
+    cr_region = cairo_create (region);
+    cairo_surface_destroy (region);
+
+    cairo_set_source_rgb (cr_region, 1, 1, 1);
+    cairo_rectangle (cr_region, 0, 0, 10, 10);
+    cairo_fill (cr_region);
+
+    cairo_set_source_rgb (cr_region, 1, 0, 0);
+    cairo_rectangle (cr_region, 10, 0, 10, 10);
+    cairo_fill (cr_region);
+
+    cairo_set_source_rgb (cr_region, 0, 1, 0);
+    cairo_rectangle (cr_region, 0, 10, 10, 10);
+    cairo_fill (cr_region);
+
+    cairo_set_source_rgb (cr_region, 0, 0, 1);
+    cairo_rectangle (cr_region, 10, 10, 10, 10);
+    cairo_fill (cr_region);
+
+    cairo_set_source_surface (cr, cairo_get_target (cr_region), 20, 20);
+    cairo_destroy (cr_region);
+
+    /* repeat the pattern around the outside, but do not overwrite...*/
+    cairo_pattern_set_extend (cairo_get_source (cr), CAIRO_EXTEND_REPEAT);
+    cairo_rectangle (cr, 0, 0, width, height);
+    cairo_rectangle (cr, 20, 40, 20, -20);
+    cairo_fill (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (subsurface_repeat,
+           "Tests source clipping with repeat",
+           "subsurface, repeat", /* keywords */
+           NULL, /* requirements */
+           60, 60,
+           NULL, draw)
diff --git a/test/subsurface-scale.c b/test/subsurface-scale.c
new file mode 100644 (file)
index 0000000..d5e2d95
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2009 Intel Corporation
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Intel not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Intel makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * INTEL CORPORATION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL INTEL CORPORATION BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_surface_t *region[5];
+    const char *text = "Cairo";
+    int i;
+
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_paint (cr);
+
+    cairo_rectangle (cr, 0, 20, 200, 60);
+    cairo_set_source_rgb (cr, 1, 0, 0);
+    cairo_fill (cr);
+
+    cairo_set_source_rgb (cr, 0, 0, 0);
+
+    for (i = 0; i < 5; i++) {
+       cairo_t *cr_region;
+       cairo_text_extents_t extents;
+       char buf[2] = { text[i], '\0' };
+
+        region[i] = cairo_surface_create_for_rectangle (cairo_get_target (cr),
+                                                        20 * i, 0, 20, 20);
+
+       cr_region = cairo_create (region[i]);
+       cairo_surface_destroy (region[i]);
+
+       cairo_select_font_face (cr_region, "@cairo:",
+                               CAIRO_FONT_WEIGHT_NORMAL,
+                               CAIRO_FONT_SLANT_NORMAL);
+       cairo_set_font_size (cr_region, 20);
+       cairo_text_extents (cr_region, buf, &extents);
+       cairo_move_to (cr_region,
+                      10 - (extents.width/2 + extents.x_bearing),
+                      10 - (extents.height/2 + extents.y_bearing));
+       cairo_show_text (cr_region, buf);
+
+       region[i] = cairo_surface_reference (cairo_get_target (cr_region));
+       cairo_destroy (cr_region);
+    }
+
+    cairo_scale (cr, 2, 2);
+    for (i = 0; i < 5; i++) {
+       cairo_set_source_surface (cr, region[5-i-1], 20 * i, 20);
+       cairo_pattern_set_extend (cairo_get_source (cr), CAIRO_EXTEND_PAD);
+       cairo_rectangle (cr, 20*i, 20, 20, 20);
+       cairo_fill (cr);
+    }
+
+    for (i = 0; i < 5; i++) {
+       cairo_set_source_surface (cr, region[5-i-1], 20 * i, 40);
+       cairo_paint_with_alpha (cr, .5);
+    }
+
+    for (i = 0; i < 5; i++)
+       cairo_surface_destroy (region[i]);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (subsurface_scale,
+           "Tests clipping of both source and destination using subsurfaces",
+           "subsurface", /* keywords */
+           NULL, /* requirements */
+           200, 120,
+           NULL, draw)
diff --git a/test/subsurface-similar-repeat.c b/test/subsurface-similar-repeat.c
new file mode 100644 (file)
index 0000000..5f17b49
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2009 Intel Corporation
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Intel not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Intel makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * INTEL CORPORATION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL INTEL CORPORATION BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_surface_t *similar;
+    cairo_surface_t *region;
+    cairo_t *cr_region;
+
+    cairo_set_source_rgb (cr, .5, .5, .5);
+    cairo_paint (cr);
+
+    similar = cairo_surface_create_similar (cairo_get_target (cr),
+                                           CAIRO_CONTENT_COLOR,
+                                           60, 60);
+    cr_region = cairo_create (similar);
+    cairo_surface_destroy (similar);
+
+    cairo_set_source_rgb (cr_region, .5, .5, .0);
+    cairo_paint (cr_region);
+    similar = cairo_surface_reference (cairo_get_target (cr_region));
+    cairo_destroy (cr_region);
+
+    /* fill the centre */
+    region = cairo_surface_create_for_rectangle (similar, 20, 20, 20, 20);
+    cairo_surface_destroy (similar);
+
+    cr_region = cairo_create (region);
+    cairo_surface_destroy (region);
+
+    cairo_set_source_rgb (cr_region, 1, 1, 1);
+    cairo_rectangle (cr_region, 0, 0, 10, 10);
+    cairo_fill (cr_region);
+
+    cairo_set_source_rgb (cr_region, 1, 0, 0);
+    cairo_rectangle (cr_region, 10, 0, 10, 10);
+    cairo_fill (cr_region);
+
+    cairo_set_source_rgb (cr_region, 0, 1, 0);
+    cairo_rectangle (cr_region, 0, 10, 10, 10);
+    cairo_fill (cr_region);
+
+    cairo_set_source_rgb (cr_region, 0, 0, 1);
+    cairo_rectangle (cr_region, 10, 10, 10, 10);
+    cairo_fill (cr_region);
+
+    cairo_set_source_surface (cr, cairo_get_target (cr_region), 20, 20);
+    cairo_destroy (cr_region);
+
+    cairo_pattern_set_extend (cairo_get_source (cr), CAIRO_EXTEND_REPEAT);
+    cairo_paint (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (subsurface_similar_repeat,
+           "Tests source clipping through an intermediate with repeat",
+           "subsurface, repeat", /* keywords */
+           NULL, /* requirements */
+           60, 60,
+           NULL, draw)
diff --git a/test/subsurface.c b/test/subsurface.c
new file mode 100644 (file)
index 0000000..d79d3d1
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2009 Intel Corporation
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Intel not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Intel makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * INTEL CORPORATION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL INTEL CORPORATION BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_surface_t *region[5];
+    const char *text = "Cairo";
+    int i;
+
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_paint (cr);
+    cairo_set_source_rgb (cr, 0, 0, 0);
+
+    for (i = 0; i < 5; i++) {
+       cairo_t *cr_region;
+       cairo_text_extents_t extents;
+       char buf[2] = { text[i], '\0' };
+
+        region[i] = cairo_surface_create_for_rectangle (cairo_get_target (cr),
+                                                        20 * i, 0, 20, 20);
+
+       cr_region = cairo_create (region[i]);
+       cairo_surface_destroy (region[i]);
+
+       cairo_select_font_face (cr_region, "@cairo:",
+                               CAIRO_FONT_WEIGHT_NORMAL,
+                               CAIRO_FONT_SLANT_NORMAL);
+       cairo_set_font_size (cr_region, 20);
+       cairo_text_extents (cr_region, buf, &extents);
+       cairo_move_to (cr_region,
+                      10 - (extents.width/2 + extents.x_bearing),
+                      10 - (extents.height/2 + extents.y_bearing));
+       cairo_show_text (cr_region, buf);
+
+       region[i] = cairo_surface_reference (cairo_get_target (cr_region));
+       cairo_destroy (cr_region);
+    }
+
+    for (i = 0; i < 5; i++) {
+       cairo_set_source_surface (cr, region[5-i-1], 20 * i, 20);
+       cairo_paint (cr);
+    }
+
+    for (i = 0; i < 5; i++) {
+       cairo_set_source_surface (cr, region[5-i-1], 20 * i, 40);
+       cairo_paint_with_alpha (cr, .5);
+    }
+
+    for (i = 0; i < 5; i++)
+       cairo_surface_destroy (region[i]);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (subsurface,
+           "Tests clipping of both source and destination using subsurfaces",
+           "subsurface", /* keywords */
+           NULL, /* requirements */
+           100, 60,
+           NULL, draw)
diff --git a/test/surface-finish-twice.c b/test/surface-finish-twice.c
new file mode 100644 (file)
index 0000000..f63b501
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Carl Worth <cworth@cworth.org>
+ */
+
+/* Bug history
+ *
+ * 2005-04-10 stevech1097@yahoo.com.au
+ *
+ *   Subject: [Bug 2950]  New: *** glibc detected *** double free or corruption
+ *   URL: https://bugs.freedesktop.org/show_bug.cgi?id=2950
+ *
+ *   The following short program gives the error message:
+ *
+ *     *** glibc detected *** double free or corruption: 0x082a7268 ***
+ *     Aborted
+ *
+ * 2005-04-13 Carl Worth <cworth@cworth.org>
+ *
+ *   Looks like surface->finished was never being set. Now fixed.
+ */
+
+#include "cairo-test.h"
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    const cairo_test_context_t *ctx = cairo_test_get_context (cr);
+    cairo_surface_t *surface;
+    cairo_status_t status;
+
+    surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 1, 1);
+
+    cairo_surface_finish (surface);
+    status = cairo_surface_status (surface);
+    if (status != CAIRO_STATUS_SUCCESS)
+       return cairo_test_status_from_status (ctx, status);
+
+    cairo_surface_finish (surface);
+    status = cairo_surface_status (surface);
+    if (status != CAIRO_STATUS_SUCCESS)
+       return cairo_test_status_from_status (ctx, status);
+
+    cairo_surface_finish (surface);
+    status = cairo_surface_status (surface);
+    if (status != CAIRO_STATUS_SUCCESS)
+       return cairo_test_status_from_status (ctx, status);
+
+    cairo_surface_destroy (surface);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (surface_finish_twice,
+           "Test to exercise a crash when calling cairo_surface_finish twice on the same surface.",
+           "api", /* keywords */
+           NULL, /* requirements */
+           0, 0,
+           NULL, draw)
diff --git a/test/surface-pattern-big-scale-down.c b/test/surface-pattern-big-scale-down.c
new file mode 100644 (file)
index 0000000..698accb
--- /dev/null
@@ -0,0 +1,125 @@
+/*
+ * Copyright © 2006 Mozilla Corporation
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Mozilla Corporation not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Mozilla Corporation makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * MOZILLA CORPORATION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL MOZILLA CORPORATION BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Vladimir Vukicevic <vladimir@pobox.com>
+ */
+
+#include "cairo-test.h"
+
+#define SRC_WIDTH 2048
+#define SRC_HEIGHT 32
+
+static cairo_surface_t *
+create_source_surface (int w, int h)
+{
+    cairo_surface_t *surface;
+    cairo_t *cr;
+
+    surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, SRC_WIDTH, SRC_HEIGHT);
+    cr = cairo_create (surface);
+    cairo_surface_destroy (surface);
+
+    cairo_set_source_rgb (cr, 1.0, 0.0, 0.0);
+    cairo_rectangle (cr, 0, 0, w/2, h/2);
+    cairo_fill (cr);
+
+    cairo_set_source_rgb (cr, 0.0, 1.0, 0.0);
+    cairo_rectangle (cr, w/2, 0, w/2, h/2);
+    cairo_fill (cr);
+
+    cairo_set_source_rgb (cr, 0.0, 0.0, 1.0);
+    cairo_rectangle (cr, 0, h/2, w/2, h/2);
+    cairo_fill (cr);
+
+    cairo_set_source_rgb (cr, 1.0, 1.0, 0.0);
+    cairo_rectangle (cr, w/2, h/2, w/2, h/2);
+    cairo_fill (cr);
+
+    surface = cairo_surface_reference (cairo_get_target (cr));
+    cairo_destroy (cr);
+
+    return surface;
+}
+
+static void
+draw_n (cairo_t *cr, cairo_pattern_t *pat, double dest_size, int n)
+{
+  cairo_matrix_t mat;
+
+  cairo_matrix_init_scale (&mat, SRC_WIDTH / dest_size, SRC_HEIGHT / dest_size);
+  cairo_matrix_translate (&mat, n * -dest_size, 0.0);
+  cairo_pattern_set_matrix (pat, &mat);
+
+  cairo_set_source (cr, pat);
+  cairo_new_path (cr);
+  cairo_rectangle (cr, n * dest_size, 0.0, dest_size, dest_size);
+  cairo_fill (cr);
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_surface_t *surface;
+    cairo_pattern_t *pat;
+
+    cairo_set_source_rgb (cr, 0, 0, 0);
+    cairo_paint (cr);
+
+    surface = create_source_surface (SRC_WIDTH, SRC_HEIGHT);
+
+    pat = cairo_pattern_create_for_surface (surface);
+    cairo_surface_destroy (surface);
+
+    /* We want to draw at a position such that n * SRC_WIDTH * (SRC_WIDTH/16.0) > 32768.
+     * x = n * 16.
+     *
+     * To show the bug, we want to draw on either side of the boundary;
+     * in our case here, n = 16 results in 32768, and n = 17 results in > 32768.
+     *
+     * Drawing at 16 and 17 is sufficient to show the problem.
+     */
+
+#if 1
+    /* n = 16 */
+    draw_n (cr, pat, 16.0, 16);
+
+    /* n = 17 */
+    draw_n (cr, pat, 16.0, 17);
+#else
+    {
+       int n;
+       for (n = 0; n < 32; n++)
+           draw_n (cr, pat, 16.0, n);
+    }
+#endif
+
+    cairo_pattern_destroy (pat);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (surface_pattern_big_scale_down,
+           "Test scaled-down transformed not-repeated surface patterns with large images and offsets",
+           "transform", /* keywords */
+           NULL, /* requirements */
+           512, 16,
+           NULL, draw)
diff --git a/test/surface-pattern-operator.c b/test/surface-pattern-operator.c
new file mode 100644 (file)
index 0000000..994baf9
--- /dev/null
@@ -0,0 +1,119 @@
+/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/*
+ * Copyright 2009 Andrea Canciani
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Andrea Canciani <ranma42@gmail.com>
+ */
+
+#include "cairo-test.h"
+
+#define N_OPERATORS (CAIRO_OPERATOR_SATURATE + 1)
+#define HEIGHT 16
+#define WIDTH 16
+#define PAD 3
+
+static cairo_pattern_t*
+_create_pattern (cairo_surface_t *target, cairo_content_t content, int width, int height)
+{
+    cairo_pattern_t *pattern;
+    cairo_surface_t *surface;
+    cairo_t *cr;
+
+    surface = cairo_surface_create_similar (target, content, width, height);
+    cr = cairo_create (surface);
+    cairo_surface_destroy (surface);
+
+    cairo_set_source_rgb (cr, 1, 0, 0);
+    cairo_arc (cr, 0.5 * width, 0.5 * height, 0.45 * height, -M_PI / 4, 3 * M_PI / 4);
+    cairo_fill (cr);
+
+    pattern = cairo_pattern_create_for_surface (cairo_get_target (cr));
+    cairo_destroy (cr);
+
+    return pattern;
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_pattern_t *alpha_pattern, *color_alpha_pattern, *pattern;
+    unsigned int n, i;
+
+    alpha_pattern = _create_pattern (cairo_get_target (cr),
+                                    CAIRO_CONTENT_ALPHA,
+                                    0.9 * WIDTH, 0.9 * HEIGHT);
+    color_alpha_pattern = _create_pattern (cairo_get_target (cr),
+                                          CAIRO_CONTENT_COLOR_ALPHA,
+                                          0.9 * WIDTH, 0.9 * HEIGHT);
+
+    pattern = cairo_pattern_create_linear (WIDTH, 0, 0, HEIGHT);
+    cairo_pattern_add_color_stop_rgba (pattern, 0.2, 0, 0, 1, 1);
+    cairo_pattern_add_color_stop_rgba (pattern, 0.8, 0, 0, 1, 0);
+
+    cairo_translate (cr, PAD, PAD);
+
+    for (n = 0; n < N_OPERATORS; n++) {
+       cairo_save (cr);
+       for (i = 0; i < 4; i++) {
+           cairo_reset_clip (cr);
+           cairo_rectangle (cr, 0, 0, WIDTH, HEIGHT);
+           cairo_clip (cr);
+
+           cairo_set_source (cr, pattern);
+           cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
+           if (i & 2) {
+               cairo_paint (cr);
+           } else {
+               cairo_rectangle (cr, WIDTH/2, HEIGHT/2, WIDTH, HEIGHT);
+               cairo_fill (cr);
+           }
+
+           cairo_set_source (cr, i & 1 ? alpha_pattern : color_alpha_pattern);
+           cairo_set_operator (cr, n);
+           if (i & 2) {
+               cairo_paint (cr);
+           } else {
+               cairo_rectangle (cr, WIDTH/2, HEIGHT/2, WIDTH, HEIGHT);
+               cairo_fill (cr);
+           }
+
+           cairo_translate (cr, 0, HEIGHT+PAD);
+       }
+       cairo_restore (cr);
+
+       cairo_translate (cr, WIDTH+PAD, 0);
+    }
+
+    cairo_pattern_destroy (pattern);
+    cairo_pattern_destroy (alpha_pattern);
+    cairo_pattern_destroy (color_alpha_pattern);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (surface_pattern_operator,
+           "Tests alpha-only and alpha-color sources with all operators",
+           "surface, pattern, operator", /* keywords */
+           NULL, /* requirements */
+           (WIDTH+PAD) * N_OPERATORS + PAD, 4*HEIGHT + 5*PAD,
+           NULL, draw)
diff --git a/test/surface-pattern-scale-down-extend.c b/test/surface-pattern-scale-down-extend.c
new file mode 100644 (file)
index 0000000..191ab92
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ * Copyright © 2010 M Joonas Pihlaja
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: M Joonas Pihlaja <jpihlaja@cc.helsinki.fi>
+ */
+#include "cairo-test.h"
+
+/* Test that we can simultaneously downscale and extend a surface
+ * pattern.  Reported by Franz Schmid to the cairo mailing list as a
+ * regression in 1.9.6:
+ *
+ * http://lists.cairographics.org/archives/cairo/2010-February/019492.html
+ */
+
+static cairo_test_status_t
+draw_with_extend (cairo_t *cr, int w, int h, cairo_extend_t extend)
+{
+    cairo_pattern_t *pattern;
+    cairo_set_source_rgb (cr, 1,1,1);
+    cairo_paint (cr);
+
+    cairo_save (cr);
+
+    /* When the destination surface is created by cairo-test-suite to
+     * test device-offset, it is bigger than w x h. This test expects
+     * the group to have a size which is exactly w x h, so it must
+     * clip to the this rectangle to guarantee that the group will
+     * have the correct size.
+     */
+    cairo_rectangle (cr, 0, 0, w, h);
+    cairo_clip (cr);
+
+    cairo_push_group_with_content (cr, CAIRO_CONTENT_COLOR); {
+        /* A two by two checkerboard with black, red and yellow
+         * cells. */
+        cairo_set_source_rgb (cr, 1,0,0);
+        cairo_rectangle (cr, w/2, 0, w-w/2, h/2);
+        cairo_fill (cr);
+        cairo_set_source_rgb (cr, 1,1,0);
+        cairo_rectangle (cr, 0, h/2, w/2, h-h/2);
+        cairo_fill (cr);
+    }
+    pattern = cairo_pop_group (cr);
+    cairo_pattern_set_extend(pattern, extend);
+
+    cairo_restore (cr);
+
+    cairo_scale (cr, 0.5, 0.5);
+    cairo_set_source (cr, pattern);
+    cairo_paint (cr);
+
+    cairo_pattern_destroy (pattern);
+    return CAIRO_TEST_SUCCESS;
+}
+
+static cairo_test_status_t
+draw_repeat (cairo_t *cr, int w, int h)
+{
+    return draw_with_extend (cr, w, h, CAIRO_EXTEND_REPEAT);
+}
+static cairo_test_status_t
+draw_none (cairo_t *cr, int w, int h)
+{
+    return draw_with_extend (cr, w, h, CAIRO_EXTEND_NONE);
+}
+static cairo_test_status_t
+draw_reflect (cairo_t *cr, int w, int h)
+{
+    return draw_with_extend (cr, w, h, CAIRO_EXTEND_REFLECT);
+}
+static cairo_test_status_t
+draw_pad (cairo_t *cr, int w, int h)
+{
+    return draw_with_extend (cr, w, h, CAIRO_EXTEND_PAD);
+}
+
+CAIRO_TEST (surface_pattern_scale_down_extend_repeat,
+           "Test interaction of downscaling a surface pattern and extend-repeat",
+            "pattern, transform, extend", /* keywords */
+           NULL, /* requirements */
+            100, 100,
+           NULL, draw_repeat)
+CAIRO_TEST (surface_pattern_scale_down_extend_none,
+           "Test interaction of downscaling a surface pattern and extend-none",
+            "pattern, transform, extend", /* keywords */
+           NULL, /* requirements */
+            100, 100,
+           NULL, draw_none)
+CAIRO_TEST (surface_pattern_scale_down_extend_reflect,
+           "Test interaction of downscaling a surface pattern and extend-reflect",
+            "pattern, transform, extend", /* keywords */
+           NULL, /* requirements */
+            100, 100,
+           NULL, draw_reflect)
+CAIRO_TEST (surface_pattern_scale_down_extend_pad,
+           "Test interaction of downscaling a surface pattern and extend-pad",
+            "pattern, transform, extend", /* keywords */
+           NULL, /* requirements */
+            100, 100,
+           NULL, draw_pad)
diff --git a/test/surface-pattern-scale-down.c b/test/surface-pattern-scale-down.c
new file mode 100644 (file)
index 0000000..639019c
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * Copyright © 2007 Red Hat, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Behdad Esfahbod <behdad@behdad.org>
+ */
+
+#include <math.h>
+#include "cairo-test.h"
+#include <stdio.h>
+
+#define SIZE 200
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_surface_t *surface;
+    cairo_t * cr_surface;
+    int surface_size = 300;
+
+    cairo_set_source_rgb (cr, 0, 0, 0);
+    cairo_paint (cr);
+
+    /* Create an image surface with my favorite four colors in each
+     * quadrant. */
+    surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24,
+                                         surface_size, surface_size);
+    cr_surface = cairo_create (surface);
+    cairo_surface_destroy (surface);
+
+    cairo_set_source_rgb (cr_surface, 1, 1, 1);
+    cairo_rectangle (cr_surface,
+                    0, 0,
+                    surface_size / 2, surface_size / 2);
+    cairo_fill (cr_surface);
+    cairo_set_source_rgb (cr_surface, 1, 0, 0);
+    cairo_rectangle (cr_surface,
+                    surface_size / 2, 0,
+                    surface_size / 2, surface_size / 2);
+    cairo_fill (cr_surface);
+    cairo_set_source_rgb (cr_surface, 0, 1, 0);
+    cairo_rectangle (cr_surface,
+                    0, surface_size / 2,
+                    surface_size / 2, surface_size / 2);
+    cairo_fill (cr_surface);
+    cairo_set_source_rgb (cr_surface, 0, 0, 1);
+    cairo_rectangle (cr_surface,
+                    surface_size / 2, surface_size / 2,
+                    surface_size / 2, surface_size / 2);
+    cairo_fill (cr_surface);
+
+    cairo_scale (cr, 0.2, 0.2);
+    cairo_rotate (cr, 1.);
+    cairo_set_source_surface (cr, cairo_get_target (cr_surface), 225, -225);
+    cairo_destroy (cr_surface);
+
+    cairo_pattern_set_extend (cairo_get_source (cr), CAIRO_EXTEND_NONE);
+    cairo_paint (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (surface_pattern_scale_down,
+           "Test scaled-down transformed not-repeated surface patterns"
+           "\nFails xlib backend (with argb32) with inexplicable alpha in result",
+           "transform", /* keywords */
+           NULL, /* requirements */
+           SIZE, SIZE,
+           NULL, draw)
diff --git a/test/surface-pattern-scale-up.c b/test/surface-pattern-scale-up.c
new file mode 100644 (file)
index 0000000..26e75c8
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * Copyright © 2007 Red Hat, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Behdad Esfahbod <behdad@behdad.org>
+ */
+
+/* Exhibits nasty behaviour with GS due as their /Interpolate implementation
+ * does not function for rotated images. */
+
+#include <math.h>
+#include "cairo-test.h"
+#include <stdio.h>
+
+#define SIZE 100
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_surface_t *surface;
+    cairo_t * cr_surface;
+    int surface_size = 6;
+
+    /* Fill the background with grey, so that it's easily visible when
+     * things get overdrawn */
+    cairo_set_source_rgb (cr, 0.5, 0.5, 0.5);
+    cairo_paint (cr);
+
+    /* Create an image surface with my favorite four colors in each
+     * quadrant. */
+    surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24,
+                                         surface_size, surface_size);
+    cr_surface = cairo_create (surface);
+    cairo_surface_destroy (surface);
+
+    cairo_set_source_rgb (cr_surface, 1, 1, 1);
+    cairo_rectangle (cr_surface,
+                    0, 0,
+                    surface_size / 2, surface_size / 2);
+    cairo_fill (cr_surface);
+    cairo_set_source_rgb (cr_surface, 1, 0, 0);
+    cairo_rectangle (cr_surface,
+                    surface_size / 2, 0,
+                    surface_size / 2, surface_size / 2);
+    cairo_fill (cr_surface);
+    cairo_set_source_rgb (cr_surface, 0, 1, 0);
+    cairo_rectangle (cr_surface,
+                    0, surface_size / 2,
+                    surface_size / 2, surface_size / 2);
+    cairo_fill (cr_surface);
+    cairo_set_source_rgb (cr_surface, 0, 0, 1);
+    cairo_rectangle (cr_surface,
+                    surface_size / 2, surface_size / 2,
+                    surface_size / 2, surface_size / 2);
+    cairo_fill (cr_surface);
+
+    cairo_scale (cr, 10, 10);
+    cairo_rotate (cr, 1.);
+    cairo_set_source_surface (cr, cairo_get_target (cr_surface), 4, -4.5);
+    cairo_destroy (cr_surface);
+
+    cairo_pattern_set_extend (cairo_get_source (cr), CAIRO_EXTEND_NONE);
+    cairo_paint (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (surface_pattern_scale_up,
+           "Test scaled-up transformed not-repeated surface patterns"
+           "\nFails xlib backend (with argb32) with inexplicable alpha in result",
+           "transform", /* keywords */
+           NULL, /* requirements */
+           SIZE, SIZE,
+           NULL, draw)
diff --git a/test/surface-pattern.c b/test/surface-pattern.c
new file mode 100644 (file)
index 0000000..ff95475
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * Copyright © 2007 Red Hat, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Behdad Esfahbod <behdad@behdad.org>
+ */
+
+#include <math.h>
+#include "cairo-test.h"
+#include <stdio.h>
+
+#define SIZE 140
+/* Note GhostScript does not support /Interpolate on rotated images, so the PS
+ * output looks terrible, but is a known issue. */
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_surface_t *surface;
+    cairo_t * cr_surface;
+    int surface_size = 6;
+
+    cairo_set_source_rgb (cr, 0, 0, 0);
+    cairo_paint (cr);
+
+    /* Create an image surface with my favorite four colors in each
+     * quadrant. */
+    surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24,
+                                         surface_size, surface_size);
+    cr_surface = cairo_create (surface);
+    cairo_surface_destroy (surface);
+
+    cairo_set_source_rgb (cr_surface, 1, 1, 1);
+    cairo_rectangle (cr_surface,
+                    0, 0,
+                    surface_size / 2, surface_size / 2);
+    cairo_fill (cr_surface);
+    cairo_set_source_rgb (cr_surface, 1, 0, 0);
+    cairo_rectangle (cr_surface,
+                    surface_size / 2, 0,
+                    surface_size / 2, surface_size / 2);
+    cairo_fill (cr_surface);
+    cairo_set_source_rgb (cr_surface, 0, 1, 0);
+    cairo_rectangle (cr_surface,
+                    0, surface_size / 2,
+                    surface_size / 2, surface_size / 2);
+    cairo_fill (cr_surface);
+    cairo_set_source_rgb (cr_surface, 0, 0, 1);
+    cairo_rectangle (cr_surface,
+                    surface_size / 2, surface_size / 2,
+                    surface_size / 2, surface_size / 2);
+    cairo_fill (cr_surface);
+
+    cairo_scale (cr, 10, 10);
+    cairo_rotate (cr, 1.);
+    cairo_set_source_surface (cr, cairo_get_target (cr_surface), 1.5, 1.5);
+    cairo_destroy (cr_surface);
+
+    cairo_pattern_set_extend (cairo_get_source (cr), CAIRO_EXTEND_REPEAT);
+    cairo_paint (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (surface_pattern,
+           "Test transformed repeated surface patterns"
+           "\nExhibiting a strange (very minor) failure in ps backend with device-offset",
+           "transform", /* keywords */
+           NULL, /* requirements */
+           SIZE, SIZE,
+           NULL, draw)
diff --git a/test/surface-source.c b/test/surface-source.c
new file mode 100644 (file)
index 0000000..657a0c8
--- /dev/null
@@ -0,0 +1,170 @@
+/*
+ * Copyright © 2008 Chris Wilson
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Chris Wilson not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Chris Wilson makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * CHRIS WILSON DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL CHRIS WILSON BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+
+static cairo_surface_t *create_source_surface (int size);
+
+/* We use a relatively large source to exercise bug:
+ *   Bug 7360 painting huge surfaces fails
+ *   [https://bugs.freedesktop.org/show_bug.cgi?id=7360]
+ * but still keep the resultant image small for reasonably quick checking.
+ */
+#define SOURCE_SIZE 2000
+#define INTER_SIZE 512
+#define SIZE 96
+
+static void
+draw_pattern (cairo_surface_t **surface_inout, int surface_size)
+{
+    cairo_t *cr;
+    int mid = surface_size/2;
+
+    cr = cairo_create (*surface_inout);
+    cairo_surface_destroy (*surface_inout);
+
+    cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+    cairo_set_source_rgba (cr, 0, 0, 0, 0);
+    cairo_paint (cr);
+
+    cairo_rectangle (cr, 0, 0, surface_size, surface_size);
+    cairo_rectangle (cr, mid - SIZE/4, mid + SIZE/4, SIZE/2, -SIZE/2);
+    cairo_clip (cr);
+
+    /* outside squares -> opaque */
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_rectangle (cr,
+                    0, 0,
+                    surface_size / 2, surface_size / 2);
+    cairo_fill (cr);
+    cairo_set_source_rgb (cr, 1, 0, 0);
+    cairo_rectangle (cr,
+                    surface_size / 2, 0,
+                    surface_size / 2, surface_size / 2);
+    cairo_fill (cr);
+    cairo_set_source_rgb (cr, 0, 1, 0);
+    cairo_rectangle (cr,
+                    0, surface_size / 2,
+                    surface_size / 2, surface_size / 2);
+    cairo_fill (cr);
+    cairo_set_source_rgb (cr, 0, 0, 1);
+    cairo_rectangle (cr,
+                    surface_size / 2, surface_size / 2,
+                    surface_size / 2, surface_size / 2);
+    cairo_fill (cr);
+
+    cairo_reset_clip (cr);
+    cairo_rectangle (cr, mid - SIZE/4, mid - SIZE/4, SIZE/2, SIZE/2);
+    cairo_clip (cr);
+
+    /* inside squares -> translucent */
+    cairo_set_source_rgba (cr, 0, 0, 1, .5);
+    cairo_rectangle (cr,
+                    0, 0,
+                    surface_size / 2, surface_size / 2);
+    cairo_fill (cr);
+    cairo_set_source_rgba (cr, 0, 1, 0, .5);
+    cairo_rectangle (cr,
+                    surface_size / 2, 0,
+                    surface_size / 2, surface_size / 2);
+    cairo_fill (cr);
+    cairo_set_source_rgba (cr, 1, 0, 0, .5);
+    cairo_rectangle (cr,
+                    0, surface_size / 2,
+                    surface_size / 2, surface_size / 2);
+    cairo_fill (cr);
+    cairo_set_source_rgba (cr, 1, 1, 1, .5);
+    cairo_rectangle (cr,
+                    surface_size / 2, surface_size / 2,
+                    surface_size / 2, surface_size / 2);
+    cairo_fill (cr);
+
+
+    *surface_inout = cairo_surface_reference (cairo_get_target (cr));
+    cairo_destroy (cr);
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_surface_t *surface;
+    cairo_surface_t *similar;
+    cairo_status_t status;
+    cairo_t *cr2;
+
+    cairo_set_source_rgb (cr, 0, 0, 0);
+    cairo_paint (cr);
+
+    surface = create_source_surface (SOURCE_SIZE);
+    if (surface == NULL) /* can't create the source so skip the test */
+       return CAIRO_TEST_UNTESTED;
+
+    draw_pattern (&surface, SOURCE_SIZE);
+
+    /* copy a subregion to a smaller intermediate surface */
+    similar = cairo_surface_create_similar (surface,
+                                           CAIRO_CONTENT_COLOR_ALPHA,
+                                           INTER_SIZE, INTER_SIZE);
+    cr2 = cairo_create (similar);
+    cairo_surface_destroy (similar);
+    cairo_set_source_surface (cr2, surface,
+                             (INTER_SIZE - SOURCE_SIZE)/2,
+                             (INTER_SIZE - SOURCE_SIZE)/2);
+    cairo_paint (cr2);
+
+    /* and then paint onto a small surface for checking */
+    cairo_set_source_surface (cr, cairo_get_target (cr2),
+                             (width - INTER_SIZE)/2,
+                             (height - INTER_SIZE)/2);
+    cairo_destroy (cr2);
+    cairo_rectangle (cr, 16, 16, 64, 64);
+    cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+    cairo_fill (cr);
+
+    /* destroy the surface last, as this triggers XCloseDisplay */
+    cairo_surface_finish (surface);
+    status = cairo_surface_status (surface);
+    cairo_surface_destroy (surface);
+
+    return cairo_test_status_from_status (cairo_test_get_context (cr),
+                                         status);
+}
+
+static cairo_test_status_t
+preamble (cairo_test_context_t *ctx)
+{
+    cairo_surface_t *surface;
+    cairo_status_t status;
+
+    surface = create_source_surface (SOURCE_SIZE);
+    if (surface == NULL) /* can't create the source so skip the test */
+       return CAIRO_TEST_UNTESTED;
+
+    cairo_surface_finish (surface);
+    status = cairo_surface_status (surface);
+    cairo_surface_destroy (surface);
+
+    return cairo_test_status_from_status (ctx, status);
+}
diff --git a/test/svg-clip.c b/test/svg-clip.c
new file mode 100644 (file)
index 0000000..035b236
--- /dev/null
@@ -0,0 +1,146 @@
+/*
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Kristian Høgsberg <krh@redhat.com>
+ */
+
+#include <stdio.h>
+
+#include <cairo-svg.h>
+#include "cairo-test.h"
+
+/* Test SVG clipping */
+
+#define WIDTH_IN_POINTS 600
+#define HEIGHT_IN_POINTS 600
+
+static void
+test_clip (cairo_t *cr, double width, double height)
+{
+    cairo_t *cr2;
+
+    /* Basic test; set a square clip and draw a circle to be clipped
+     * against it.*/
+
+    cairo_rectangle (cr, 100, 100, 400, 400);
+    cairo_clip (cr);
+    cairo_arc (cr, 300, 300, 210, 0, 2 * M_PI);
+    cairo_set_source_rgb (cr, 1, 0, 0);
+    cairo_fill (cr);
+
+    /* Add a plus shaped clip path to the square clip and draw a big
+     * green square to test the new clip path. */
+
+    cairo_save (cr);
+
+    cairo_rectangle (cr, 250, 100, 100, 400);
+    cairo_rectangle (cr, 100, 250, 400, 100);
+    cairo_clip (cr);
+
+    cairo_rectangle (cr, 0, 0, 600, 600);
+    cairo_set_source_rgb (cr, 0, 1, 0);
+    cairo_fill (cr);
+
+    cairo_restore (cr);
+
+    /* Set a bezier shape in addition to the rectangle clip set before
+     * the cairo_save() to verify that we successfully removed the
+     * plus shaped clip path and can set a new clip.*/
+
+    cairo_move_to (cr, 600, 0);
+    cairo_curve_to (cr, 300, 600, 0, 300, 600, 0);
+    cairo_clip (cr);
+
+    cairo_rectangle (cr, 0, 0, 600, 600);
+    cairo_set_source_rgb (cr, 0, 0, 1);
+    cairo_fill (cr);
+
+    /* Create a new context for this surface to test overlapped
+     * drawing from two contexts */
+    cr2 = cairo_create (cairo_get_group_target (cr));
+
+    /* Using the new context, draw a black vertical line, which should
+     * appear unclipped on top of everything drawn so far. */
+    cairo_move_to (cr2, 110, 0);
+    cairo_line_to (cr2, 110, 600);
+    cairo_stroke (cr2);
+
+    /* Using the first context, draw another black vertical line.
+     * This line should be clipped agaist the bezier clipping path set
+     * earlier. */
+    cairo_set_source_rgb (cr, 0, 0, 0);
+    cairo_move_to (cr, 400, 0);
+    cairo_line_to (cr, 400, 600);
+    cairo_stroke (cr);
+
+    cairo_destroy (cr2);
+
+    /* Test reset clip.  Draw a transparent black circle over
+     * everything.  Specifically, make sure the circle extends outside
+     * the square clip set at the top of this function.  */
+    cairo_reset_clip (cr);
+    cairo_arc (cr, 300, 300, 220, 0, 2 * M_PI);
+    cairo_set_source_rgba (cr, 0, 0, 0, 0.2);
+    cairo_fill (cr);
+}
+
+static cairo_test_status_t
+preamble (cairo_test_context_t *ctx)
+{
+    cairo_t *cr;
+    const char *filename = "svg-clip.out.svg";
+    cairo_surface_t *surface;
+
+    if (! cairo_test_is_target_enabled (ctx, "svg11") &&
+       ! cairo_test_is_target_enabled (ctx, "svg12"))
+    {
+       return CAIRO_TEST_UNTESTED;
+    }
+
+    surface = cairo_svg_surface_create (filename,
+                                       WIDTH_IN_POINTS, HEIGHT_IN_POINTS);
+    if (cairo_surface_status (surface)) {
+       cairo_test_log (ctx,
+                       "Failed to create svg surface for file %s: %s\n",
+                       filename, cairo_status_to_string (cairo_surface_status (surface)));
+       return CAIRO_TEST_FAILURE;
+    }
+
+    cr = cairo_create (surface);
+
+    test_clip (cr, WIDTH_IN_POINTS, HEIGHT_IN_POINTS);
+    cairo_show_page (cr);
+
+    cairo_destroy (cr);
+    cairo_surface_destroy (surface);
+
+    printf ("svg-clip: Please check %s to make sure it looks happy.\n",
+           filename);
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (svg_clip,
+           "Test SVG clipping",
+           "svg, clip", /* keywords */
+           NULL, /* requirements */
+           0, 0,
+           preamble, NULL)
diff --git a/test/svg-surface-source.c b/test/svg-surface-source.c
new file mode 100644 (file)
index 0000000..c97d3ca
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright © 2008 Chris Wilson
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Chris Wilson not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Chris Wilson makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * CHRIS WILSON DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL CHRIS WILSON BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+#include <cairo-svg.h>
+
+#include "surface-source.c"
+
+static cairo_surface_t *
+create_source_surface (int size)
+{
+    cairo_surface_t *surface;
+
+    surface = cairo_svg_surface_create ("svg-surface-source.out.svg",
+                                       size, size);
+    cairo_surface_set_fallback_resolution (surface, 72., 72.);
+
+    return surface;
+}
+
+CAIRO_TEST (svg_surface_source,
+           "Test using a SVG surface as the source",
+           "source", /* keywords */
+           NULL, /* requirements */
+           SIZE, SIZE,
+           preamble, draw)
diff --git a/test/svg-surface.c b/test/svg-surface.c
new file mode 100644 (file)
index 0000000..4e29f1b
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ */
+
+#include <stdio.h>
+
+#include <cairo-svg.h>
+#include "cairo-test.h"
+
+/* Pretty boring test just to make sure things aren't crashing ---
+ * no verification that we're getting good results yet.
+ * But you can manually view the image to make sure it looks happy.
+ */
+
+#define WIDTH_IN_INCHES  3
+#define HEIGHT_IN_INCHES 3
+#define WIDTH_IN_POINTS  (WIDTH_IN_INCHES  * 72)
+#define HEIGHT_IN_POINTS (HEIGHT_IN_INCHES * 72)
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+#define STROKE_WIDTH .04
+
+    double size;
+
+    if (width > height)
+       size = height;
+    else
+       size = width;
+
+    cairo_translate (cr, (width - size) / 2.0, (height - size) / 2.0);
+    cairo_scale (cr, size, size);
+
+    /* Fill face */
+    cairo_arc (cr, 0.5, 0.5, 0.5 - STROKE_WIDTH, 0, 2 * M_PI);
+    cairo_set_source_rgb (cr, 1, 1, 0);
+    cairo_save (cr);
+    {
+       cairo_fill (cr);
+    }
+    cairo_restore (cr);
+
+    cairo_set_source_rgb (cr, 0, 0, 0);
+
+    /* Stroke face */
+    cairo_set_line_width (cr, STROKE_WIDTH / 2.0);
+    cairo_stroke (cr);
+
+    /* Eyes */
+    cairo_set_line_width (cr, STROKE_WIDTH);
+    cairo_arc (cr, 0.3, 0.4, STROKE_WIDTH, 0, 2 * M_PI);
+    cairo_fill (cr);
+    cairo_arc (cr, 0.7, 0.4, STROKE_WIDTH, 0, 2 * M_PI);
+    cairo_fill (cr);
+
+    /* Mouth */
+    cairo_move_to (cr, 0.3, 0.7);
+    cairo_curve_to (cr,
+                   0.4, 0.8,
+                   0.6, 0.8,
+                   0.7, 0.7);
+    cairo_stroke (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+static cairo_test_status_t
+preamble (cairo_test_context_t *ctx)
+{
+    cairo_t *cr;
+    const char *filename = "svg-surface.out.svg";
+    cairo_surface_t *surface;
+
+    if (! cairo_test_is_target_enabled (ctx, "svg11") &&
+       ! cairo_test_is_target_enabled (ctx, "svg12"))
+    {
+       return CAIRO_TEST_UNTESTED;
+    }
+
+    surface = cairo_svg_surface_create (filename,
+                                       WIDTH_IN_POINTS, HEIGHT_IN_POINTS);
+    if (cairo_surface_status (surface)) {
+       cairo_test_log (ctx,
+                       "Failed to create svg surface for file %s: %s\n",
+                       filename,
+                       cairo_status_to_string (cairo_surface_status (surface)));
+       return CAIRO_TEST_FAILURE;
+    }
+
+    cr = cairo_create (surface);
+
+    draw (cr, WIDTH_IN_POINTS, HEIGHT_IN_POINTS);
+
+    cairo_show_page (cr);
+
+    cairo_destroy (cr);
+    cairo_surface_destroy (surface);
+
+    printf ("svg-surface: Please check svg-surface.svg to make sure it looks happy.\n");
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (svg_surface,
+           "Check creation of a SVG surface",
+           "svg", /* keywords */
+           NULL, /* requirements */
+           0, 0,
+           preamble, NULL)
diff --git a/test/svg2png.c b/test/svg2png.c
new file mode 100644 (file)
index 0000000..bdfbc8b
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright © 2005 Red Hat, Inc.
+ * Copyright © 2005 Emmanuel Pacaud <emmanuel.pacaud@free.fr>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Kristian Høgsberg <krh@redhat.com>
+ *        Emmanuel Pacaud <emmanuel.pacaud@free.fr>
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <gdk/gdk.h>
+#include <gdk/gdkpixbuf.h>
+#include <librsvg/rsvg.h>
+
+#define FAIL(msg)                                                      \
+    do { fprintf (stderr, "FAIL: %s\n", msg); exit (-1); } while (0)
+
+int main (int argc, char *argv[])
+{
+    GError *error = NULL;
+    GdkPixbuf *pixbuf;
+    const char *filename = argv[1];
+    const char *output_filename = argv[2];
+
+    if (argc != 3)
+       FAIL ("usage: svg2png input_file.svg output_file.png");
+
+    g_type_init ();
+
+    error = NULL;
+
+    rsvg_set_default_dpi (72.0);
+    pixbuf = rsvg_pixbuf_from_file (filename, &error);
+    if (error != NULL)
+       FAIL (error->message);
+
+    gdk_pixbuf_save (pixbuf, output_filename, "png", &error, NULL);
+    if (error != NULL)
+       FAIL (error->message);
+
+    g_object_unref (pixbuf);
+    return 0;
+}
diff --git a/test/testsvg b/test/testsvg
new file mode 100755 (executable)
index 0000000..9b18df5
--- /dev/null
@@ -0,0 +1,51 @@
+#!/bin/sh
+
+IMAGEDIFF=./imagediff
+
+OUTDIR=testsvg-output
+REFDIR=testsvg-reference
+DIFFDIR=testsvg-diff
+IMAGELIST=testsvg-imagelist
+
+if [ $# -lt 1 ]; then
+       argv0=`basename $0`
+       echo "Usage: $argv0 file.svg [...]" >&2
+       exit 1;
+fi
+
+mkdir -p $OUTDIR
+mkdir -p $DIFFDIR
+rm -f $IMAGELIST
+
+err=0
+for svg in $@; do
+       svgbase=`basename $svg`
+       png=${svgbase/\.svg/.png}
+       outpng=$OUTDIR/$png
+       refpng=$REFDIR/$png
+       diffpng=$DIFFDIR/$png
+#      if xsvg $svg -p $outpng ; then
+       if svg2png $svg $outpng ; then
+               if [ -e $refpng ]; then
+                       if $IMAGEDIFF $refpng $outpng > $diffpng; then
+                               echo "Rendering of $svg matches." >&2
+                               rm -f $diffpng
+                       else
+                               echo "ERROR: Rendering of $svg differs from reference image." >&2
+                               echo $refpng $outpng $diffpng >> $IMAGELIST
+                               err=$(($err+1))
+                       fi
+               else
+                       echo "WARNING: No reference file found for $svg (looked in $refpng)" >&2
+               fi
+       else
+               echo "ERROR: Failed to render $svg" >&2
+               err=$(($err+1))
+       fi
+done
+
+if [ $err -gt 0 ] ; then
+       echo "Differences found in $err renderings."
+else
+       echo "All renderings matched reference images."
+fi
diff --git a/test/testtable.js b/test/testtable.js
new file mode 100644 (file)
index 0000000..6c25938
--- /dev/null
@@ -0,0 +1,428 @@
+/* configuration */
+/* TODO: UNTESTED count can't be shown because it's not tracked explicitly */
+headerResults = [ "PASS", "NEW", "FAIL", "XFAIL", "CRASHED" ];
+logResults    = [ "PASS", "NEW", "FAIL", "XFAIL", "CRASH!" ];
+resultToImgs = {
+    "PASS"     : [],
+    "NEW"      : [ "output" ],
+    "FAIL"     : [ "output", "difference", "reference" ],
+    "XFAIL"    : [],
+    "UNTESTED" : [],
+    "CRASHED"  : []
+};
+
+resultToString = {
+    "PASS"     : "",
+    "NEW"      : "",
+    "FAIL"     : "",
+    "XFAIL"    : "",
+    "UNTESTED" : "",
+    "CRASHED"  : "CRASHED!"
+};
+
+resultField = "result";
+rowFields = [ "test", "offset", "similar" ];
+colFields = [ "target", "format" ];
+allFields = [ resultField ].concat (rowFields, colFields);
+
+
+/* globals: */
+function resetGlobals () {
+    dragElement = undefined;
+    table = document.getElementById ("testTable");
+    while (table.rows.length)
+       table.deleteRow (0);
+    colsArray = [ "HrowHeader" ];
+    colsMap = undefined;
+    headerId = "HcolHeader";
+
+    fTrue = function (x) { return true; };
+
+    empty = new Row ();
+    header = new Row ();
+    header[colsArray[0]].toString = function () { return ""; };
+
+    untested = new Test ();
+    untested[resultField] = "UNTESTED";
+}
+
+
+/* utility functions */
+function isKey (key) { return key[key.length-1] == ':'; }
+function normalizeKey (key) { return key.toLowerCase ().replace (/[^a-z0-9]/, ""); }
+function isVisible (x) { return x.style.display != "none"; }
+
+function link (html, url) { return "<a href='" + url + "'>" + html + "</a>"; }
+function image (url) { return "<img src='" + url + "'>"; }
+function span (html, id, cls) { return "<span id='" + id + "' class='" + cls + "' onmousedown='startDrag (event)' onmouseup='mouseUp (event)'>" + html + "</span>"; }
+
+function fieldsToHTML (bColumns, values) {
+    var fields = bColumns ? colFields : rowFields;
+    var prefix = bColumns ? "c" : "r";
+    var tmpRE = arrayApply (function (x) { return "[^/]*"; }, fields);
+    var r = Array ();
+    for (var i = 0; i < fields.length; i++)
+       if (fields[i] == "test") {
+           r.push (link (values[fields[i]], "output/" + values[fields[i]] + ".log"));
+       } else {
+           tmpRE[i] = values[fields[i]];
+           r.push (span (values[fields[i]], prefix + "/" + tmpRE.join ("/") + "/", fields[i]));
+           tmpRE[i] = "[^/]*";
+       }
+    return r.join ("/");
+}
+
+function inArray (value, array) {
+    for (var i = 0; i < array.length; i++)
+       if (value == array[i])
+           return true;
+    return false;
+}
+
+function arrayApply (fun, array) {
+    var r = new Array ();
+    for (var i = 0; i < array.length; i++)
+       r.push (fun(array[i]));
+    return r;
+}
+
+function arrayPred (pred, array) {
+    var r = new Array ();
+    for (var i = 0; i < array.length; i++)
+       if (pred (array[i]))
+           r.push (array[i]);
+    return r;
+}
+
+function arrayMap (map, array) { return arrayApply (function (x) { return map[x]; }, array); }
+
+function binSearch (rows, newId){
+    var min = 0;
+    var max = rows.length;
+
+    while (max - min > 1) {
+       var mid = (max + min) >> 1;
+        if (rows[mid].id > newId)
+           max = mid;
+        else
+           min = mid;
+    }
+
+    if (max == min)
+       return max;
+    else
+       return rows[min].id > newId ? min : max;
+}
+
+/* dynamic table utils */
+function updateCurrent () {
+    for (var i = 0; i < table.rows.length; i++) {
+       var row = table.rows[i];
+       if (isVisible (row)) {
+           /* j starts from 1 because we want to ignore _rowHeader */
+           for (var j = 1; j < row.cells.length; j++)
+               if (row.id[0] == "H")
+                   for (var k = 0; k < headerResults.length; k++)
+                       header[row.cells[j].id].current[headerResults[k]] = 0;
+               else if (isVisible (row.cells[j]))
+                   header[row.cells[j].id].current[row.cells[j].className]++;
+       }
+    }
+
+    updateHeader ();
+}
+
+function setVisible (array, subsetPred, visibilityPred, visibleFlag) {
+    var modified = false, somethingVisible = false;
+    for (var i = 0; i < array.length; i++)
+       if (array[i].id[0] != "H") {
+           if (subsetPred (array[i])) {
+               var wanted = visibilityPred (array[i]);
+               if (isVisible (array[i]) != wanted) {
+                   modified = true;
+                   array[i].style.display = wanted ? visibleFlag : "none";
+               }
+           }
+           somethingVisible = somethingVisible || isVisible (array[i]);
+       }
+    return modified && somethingVisible;
+}
+
+function setVisibleOnly (array, pred, visibleFlag) {
+    return setVisible (array, fTrue, pred, visibleFlag);
+}
+
+function flipVisible (array, subsetPred, visibleFlag) {
+    return setVisible (array, subsetPred, function (x) { return !isVisible (x); }, visibleFlag);
+}
+
+
+/* event handling */
+function ignoreEvent (event) {
+    if (event.preventDefault)
+        event.preventDefault();
+    else
+        event.returnValue= false;
+    return false;
+}
+
+function mouseUp (event) {
+    var visFun;
+    if (event.button == 0)
+       visFun = setVisibleOnly;
+    else if (event.button == 2)
+       visFun = flipVisible;
+    else
+       return false;
+
+    var structureFun;
+    if (event.target.id[0] == "r") /* rows */
+       structureFun = function (f, p) { return f (table.rows, p, "table-row"); };
+    else if (event.target.id[0] == "c") /* cols */
+       structureFun = function (f, p) { return inArray (true, arrayApply (function (row) { return f (row.cells, p, "table-cell") }, table.rows)) };
+    else
+       return false;
+
+    var pred;
+    if (event.target.id[1] == "/") { /* regexp */
+       var re = new RegExp (event.target.id);
+       pred = function (x) { return re.test (x.id); };
+    } else if (event.target.id[1] == "#") { /* counters */
+       var s = event.target.id.substr (2).split ("/");
+       pred = function (row) { return row.cells[s[0]].className == s[1]; }
+    } else
+       return false;
+
+    if (!structureFun (visFun, pred))
+       if (!structureFun (flipVisible, fTrue))
+           structureFun (flipVisible, fTrue);
+
+    updateCurrent ();
+
+    return false;
+}
+
+function noDrag (event) {
+    dragElement = undefined;
+    return false;
+}
+
+function startDrag (event) {
+    if (event.button == 0)
+       dragElement = event.target;
+    else
+       dragElement = undefined;
+    return false;
+}
+
+function endDrag (event) {
+    if (!dragElement)
+       return false;
+
+    if (event.currentTarget.id == colsArray[0] &&
+       inArray (dragElement.className, colFields)) {
+       rowFields.push (dragElement.className);
+       colFields = arrayPred (function (x) { return x != dragElement.className; }, colFields);
+    } else if (event.currentTarget.id == headerId &&
+              inArray (dragElement.className, rowFields)) {
+       colFields.push (dragElement.className);
+       rowFields = arrayPred (function (x) { return x != dragElement.className; }, rowFields);
+    } else
+       return true;
+
+    reloadAll ();
+    return false;
+}
+
+
+/* table content */
+function Row (id, t) {
+    this[colsArray[0]] = new RowHeader (id, t);
+
+    this.get = function (c) { return this[c] != undefined ? this[c] : untested; }
+    this.getHTML = function (c) { return this.get(c).toString (); };
+    this.setStyle = function (c, element) { return this.get(c).setStyle (element); };
+}
+
+function ColumnHeader (id, values) {
+    this.id = id;
+    this.values = values;
+    this.total = new Object ();
+    this.current = new Object ();
+
+    for (var i = 0; i < headerResults.length; i++) {
+       this.total[headerResults[i]] = 0;
+       this.current[headerResults[i]] = 0;
+    }
+
+    this.toString = function () {
+       var counts = new Array ();
+       for (var i = 0; i < headerResults.length; i++) {
+           var hr = headerResults[i];
+           var s = span (this.current[hr], "r#" + colsMap[this.id] + "/" + hr, hr);
+           if (this.current[hr] != this.total[hr])
+               s += span ("[" + this.total[hr] + "]", "r#" + colsMap[this.id] + "/" + hr, hr);
+           counts.push (s);
+       }
+
+       return fieldsToHTML (true, this.values) + "<br>" + counts.join ("/");
+    }
+
+    this.setStyle = function (element) { };
+}
+
+function RowHeader (id, values) {
+    this.id = id;
+    this.values = values;
+    this.toString = function () { return fieldsToHTML (false, this.values); }
+    this.setStyle = function (element) { element.onmouseup = endDrag; };
+}
+
+function Test () {
+    this.rowId = function () { return "r/" + arrayMap (this, rowFields).join("/") + "/"; };
+    this.colId = function () { return "c/" + arrayMap (this, colFields).join("/") + "/"; };
+    this.isComplete = function () { return !inArray (undefined, arrayMap (this, allFields)); }
+    this.toString = function () {
+       var images = arrayMap (this, resultToImgs[this[resultField]]);
+       images = arrayPred (function (x) { return x != undefined; }, images);
+       images = arrayApply (function (x) { return link (image (x), x); }, images);
+       images.push (resultToString[this[resultField]]);
+       return images.join (" ");
+    };
+
+    this.setStyle = function (element) { element.className = this[resultField]; };
+
+    this.addData = function (array) {
+       for (var i = 0; i < array.length - 1; i += 2)
+           if (isKey (array[i]))
+               this[normalizeKey (array[i])] = array[i+1];
+    };
+}
+
+
+/* table creation */
+function insertCell (domRow, nid, tests) {
+    var domCell = domRow.insertCell (nid);
+    domCell.id = colsArray[nid];
+    domCell.innerHTML = tests.getHTML (colsArray[nid]);
+    tests.setStyle (colsArray[nid], domCell);
+}
+
+function updateRow (row, tests) {
+    var domRow = document.getElementById (row);
+    if (!domRow) {
+       domRow = table.insertRow (binSearch (table.rows, row));
+       domRow.id = row;
+    }
+
+    for (var i = 0; i < colsArray.length; i++)
+       if (i >= domRow.cells.length || domRow.cells[i].id != colsArray[i])
+           insertCell (domRow, i, tests);
+}
+
+function updateHeader () {
+    var visibility;
+    var domRow = document.getElementById (headerId);
+    if (domRow) {
+       visibility = new Object ();
+       for (var i = 0; i < domRow.cells.length; i++)
+           visibility[domRow.cells[i].id] = domRow.cells[i].style.display;
+       table.deleteRow (domRow.rowIndex);
+    }
+
+    updateRow (headerId, header);
+    table.rows[0].onmouseup = endDrag;
+
+    if (visibility)
+       for (var i = 0; i < colsArray.length; i++)
+           if (visibility[colsArray[i]])
+               table.rows[0].cells[colsMap[colsArray[i]]].style.display = visibility[colsArray[i]];
+}
+
+function updateTable () {
+    colsArray.sort ();
+
+    colsMap = new Object ();
+    for (var i = 0; i < colsArray.length; i++)
+       colsMap[colsArray[i]] = i;
+
+    updateHeader ();
+    for (var i = 0; i < table.rows.length; i++)
+       updateRow (table.rows[i].id, empty);
+}
+
+
+/* log file parsing */
+function parseTest (testData) {
+    var colsChanged = false;
+    var rows = new Array ();
+    var data = new Object ();
+    var t = new Test ();
+    var lines = testData.replace (/\r/g, "").split ("\n");
+    for (var i = 0; i < lines.length; i++) {
+       t.addData (lines[i].split (" "));
+       if (t.isComplete ()) {
+           var c = t.colId ();
+           if (header[c] == undefined) {
+               colsArray.push (c);
+               header[c] = new ColumnHeader (c, t);
+               colsChanged = true;
+           }
+
+           var r = t.rowId ();
+           if (!data[r]) {
+               rows.push (r);
+               data[r] = new Row (r, t);
+           }
+
+           data[r][c] = t;
+           header[c].total[t[resultField]]++;
+           header[c].current[t[resultField]]++;
+           t = new Test ();
+       }
+    }
+
+    if (colsChanged)
+       updateTable ();
+    else
+       updateHeader ();
+
+    for (var i = 0; i < rows.length; i++)
+       updateRow (rows[i], data[rows[i]]);
+}
+
+function parseFile (fileName, parser) {
+    var req = new XMLHttpRequest ();
+    req.onreadystatechange = function () {
+       if (req.readyState == 4)
+           parser (req.responseText);
+    }
+
+    try {
+       req.open ("GET", fileName);
+       req.send (null);
+    } catch (e) {}
+} 
+
+function parseTestList (listData) {
+    var summaryRE = /\d+ Passed, \d+ Failed \x5b\d+ crashed, \d+ expected\x5d, \d+ Skipped/;
+    var lines = listData.replace (/\r/g, "").split ("\n");
+    for (var i = 0; i < lines.length; i++) {
+       if (summaryRE.test (lines[i]))
+           return;
+       
+       var words = lines[i].split (" ");
+       if (words.length >= 2 &&
+           words[0][words[0].length-1] == ":" &&
+           inArray (words[1], logResults))
+           parseFile ("output/" + words[0].substr (0, words[0].length-1) + ".log", parseTest);
+    }
+}
+
+function reloadAll() {
+    resetGlobals ();
+
+    parseFile ("cairo-test-suite.log", parseTestList);
+} 
+
+window.onload = reloadAll;
diff --git a/test/text-antialias.c b/test/text-antialias.c
new file mode 100644 (file)
index 0000000..7d33892
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ */
+
+#include "cairo-test.h"
+
+#define WIDTH  31
+#define HEIGHT 22
+#define TEXT_SIZE 12
+
+static cairo_test_status_t
+draw (cairo_t *cr, cairo_antialias_t antialias)
+{
+    cairo_text_extents_t extents;
+    cairo_font_options_t *font_options;
+    const char black[] = "black", blue[] = "blue";
+
+    cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); /* white */
+    cairo_paint (cr);
+
+    cairo_select_font_face (cr, CAIRO_TEST_FONT_FAMILY " Sans",
+                           CAIRO_FONT_SLANT_NORMAL,
+                           CAIRO_FONT_WEIGHT_NORMAL);
+    cairo_set_font_size (cr, TEXT_SIZE);
+
+    font_options = cairo_font_options_create ();
+    cairo_get_font_options (cr, font_options);
+    cairo_font_options_set_antialias (font_options, antialias);
+    cairo_font_options_set_subpixel_order (font_options, CAIRO_SUBPIXEL_ORDER_RGB);
+    cairo_set_font_options (cr, font_options);
+
+    cairo_font_options_destroy (font_options);
+
+    cairo_set_source_rgb (cr, 0, 0, 0); /* black */
+    cairo_text_extents (cr, black, &extents);
+    cairo_move_to (cr, -extents.x_bearing, -extents.y_bearing);
+    cairo_show_text (cr, black);
+    cairo_translate (cr, 0, -extents.y_bearing + 1);
+
+    cairo_set_source_rgb (cr, 0, 0, 1); /* blue */
+    cairo_text_extents (cr, blue, &extents);
+    cairo_move_to (cr, -extents.x_bearing, -extents.y_bearing);
+    cairo_show_text (cr, blue);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+static cairo_test_status_t
+draw_gray (cairo_t *cr, int width, int height)
+{
+    return draw (cr, CAIRO_ANTIALIAS_GRAY);
+}
+
+static cairo_test_status_t
+draw_none (cairo_t *cr, int width, int height)
+{
+    return draw (cr, CAIRO_ANTIALIAS_NONE);
+}
+
+static cairo_test_status_t
+draw_subpixel (cairo_t *cr, int width, int height)
+{
+    return draw (cr, CAIRO_ANTIALIAS_SUBPIXEL);
+}
+
+CAIRO_TEST (text_antialias_gray,
+           "Tests text rendering with grayscale antialiasing",
+           "text", /* keywords */
+           "target=raster", /* requirements */
+           WIDTH, HEIGHT,
+           NULL, draw_gray)
+
+CAIRO_TEST (text_antialias_none,
+           "Tests text rendering with no antialiasing",
+           "text", /* keywords */
+           "target=raster", /* requirements */
+           WIDTH, HEIGHT,
+           NULL, draw_none)
+
+CAIRO_TEST (text_antialias_subpixel,
+           "Tests text rendering with subpixel antialiasing",
+           "text", /* keywords */
+           "target=raster", /* requirements */
+           WIDTH, HEIGHT,
+           NULL, draw_subpixel)
diff --git a/test/text-cache-crash.c b/test/text-cache-crash.c
new file mode 100644 (file)
index 0000000..4786af4
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * Copyright © 2004 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ */
+
+/* Bug history
+ *
+ * 2004-11-04 Ned Konz <ned@squeakland.org>
+ *
+ *   Reported bug on mailing list:
+ *
+ *     From: Ned Konz <ned@squeakland.org>
+ *     To: cairo@cairographics.org
+ *     Date: Thu, 4 Nov 2004 09:49:38 -0800
+ *     Subject: [cairo] getting assertions [cairo_cache.c:143: _entry_destroy:
+ *             Assertion `cache->used_memory > entry->memory' failed]
+ *
+ *     The attached program dies on me with the assert
+ *
+ *     $ ./testCairo
+ *     testCairo: cairo_cache.c:143: _entry_destroy: Assertion `cache->used_memory > entry->memory' failed.
+ *
+ * 2004-11-04 Carl Worth <cworth@cworth.org>
+ *
+ *   I trimmed down Ned's example to the folllowing test while still
+ *   maintaining the assertion.
+ *
+ *   Oh, actually, it looks like I may have triggered something
+ *   slightly different:
+ *
+ *     text_cache_crash: cairo_cache.c:422: _cairo_cache_lookup: Assertion `cache->max_memory >= (cache->used_memory + new_entry->memory)' failed.
+ *
+ *   I'll have to go back and try the original test after I fix this.
+ *
+ * 2004-11-13 Carl Worth <cworth@cworth.org>
+ *
+ *   Found the bug. cairo_gstate_select_font was noticing when the
+ *   same font was selected twice in a row and was erroneously failing
+ *   to free the old reference. Committed a fix and verified it also
+ *   fixed the original test case.
+ */
+
+#include "cairo-test.h"
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    /* Once there was a bug that choked when selecting the same font twice. */
+    cairo_select_font_face (cr, "sans",
+                           CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD);
+    cairo_set_font_size (cr, 40.0);
+
+    cairo_select_font_face (cr, "sans",
+                           CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD);
+    cairo_set_font_size (cr, 40.0);
+    cairo_move_to (cr, 10, 50);
+    cairo_show_text (cr, "hello");
+
+    /* Then there was a bug that choked when selecting a font too big
+     * for the cache. */
+
+    cairo_set_font_size (cr, 500);
+    cairo_show_text (cr, "hello");
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (text_cache_crash,
+           "Test case for bug causing an assertion failure in _cairo_cache_lookup",
+           "text, stress", /* keywords */
+           NULL, /* requirements */
+           0, 0,
+           NULL, draw)
diff --git a/test/text-glyph-range.c b/test/text-glyph-range.c
new file mode 100644 (file)
index 0000000..75b87d6
--- /dev/null
@@ -0,0 +1,125 @@
+/* -*- Mode: c; c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2006 Brian Ewins.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Brian Ewins not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Brian Ewins makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * BRIAN EWINS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL BRIAN EWINS BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Brian Ewins <Brian.Ewins@gmail.com>
+ */
+
+/* Related to bug 9530
+ *
+ * cairo_glyph_t can contain any unsigned long in its 'index', the intention
+ * being that it is large enough to hold a pointer. However, this means that
+ * it can specify many glyph indexes which don't exist in the font, and may
+ * exceed the range of legal glyph indexes for the font backend. It may
+ * also contain special values that are not usable as indexes - e.g. 0xffff is
+ * kATSDeletedGlyphcode in ATSUI, a glyph that should not be drawn.
+ * The font backends should handle all legal and out-of-range values
+ * consistently.
+ *
+ * This test expects that operations on out-of-range and missing glyphs should
+ * act as if they were zero-width.
+ */
+
+#include "cairo-test.h"
+
+#define WIDTH  100
+#define HEIGHT 75
+#define NUM_TEXT 20
+#define TEXT_SIZE 12
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_text_extents_t extents;
+    int i;
+    /* Glyphs with no paths followed by 'cairo', the additional
+     * text is to make the space obvious.
+     */
+    long int index[] = {
+       0, /* 'no matching glyph' */
+       0xffff, /* kATSDeletedGlyphCode */
+       0x1ffff, /* out of range */
+       -1L, /* out of range */
+       70, 68, 76, 85, 82 /* 'cairo' */
+    };
+
+    /* We draw in the default black, so paint white first. */
+    cairo_save (cr);
+    cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); /* white */
+    cairo_paint (cr);
+    cairo_restore (cr);
+
+    cairo_select_font_face (cr, CAIRO_TEST_FONT_FAMILY " Sans",
+                           CAIRO_FONT_SLANT_NORMAL,
+                           CAIRO_FONT_WEIGHT_NORMAL);
+    cairo_set_font_size (cr, 16);
+
+    for (i = 0; i < 9; i++) {
+       /* since we're just drawing glyphs directly we need to position them. */
+       cairo_glyph_t glyph = {
+           index[i], 10 * i, 25
+       };
+
+       /* test cairo_glyph_extents. Every glyph index should
+        * have extents, invalid glyphs should be zero-width.
+        */
+       cairo_move_to (cr, glyph.x, glyph.y);
+       cairo_set_line_width (cr, 1.0);
+       cairo_glyph_extents (cr, &glyph, 1, &extents);
+       cairo_rectangle (cr,
+                        glyph.x + extents.x_bearing - 0.5,
+                        glyph.y + extents.y_bearing - 0.5,
+                        extents.width + 1,
+                        extents.height + 1);
+       cairo_set_source_rgb (cr, 1, 0, 0); /* red */
+       cairo_stroke (cr);
+
+       /* test cairo_show_glyphs. Every glyph index should be
+        * drawable, invalid glyph indexes should draw nothing.
+        */
+       cairo_set_source_rgb (cr, 0, 0, 0); /* black */
+       cairo_show_glyphs (cr, &glyph, 1);
+       cairo_move_to (cr, glyph.x, glyph.y);
+
+       /* test cairo_glyph_path. Every glyph index should produce
+        * a path, invalid glyph indexes should have empty paths.
+        */
+       /* Change the glyph position
+        * so that the paths are visible.
+        */
+       glyph.y = 55;
+       cairo_move_to (cr, glyph.x, glyph.y);
+       cairo_glyph_path (cr, &glyph, 1);
+       cairo_fill (cr);
+    }
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (text_glyph_range,
+           "Tests show_glyphs, glyph_path, glyph_extents with out of range glyph ids."
+           "\nft and atsui font backends fail, misreporting errors from FT_Load_Glyph and ATSUGlyphGetCubicPaths",
+           "text, stress", /* keywords */
+           NULL, /* requirements */
+           WIDTH, HEIGHT,
+           NULL, draw)
diff --git a/test/text-pattern.c b/test/text-pattern.c
new file mode 100644 (file)
index 0000000..60267d6
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * Copyright © 2005 Tim Rowley
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Tim Rowley
+ */
+
+#include "cairo-test.h"
+
+#define IMAGE_WIDTH 128
+#define IMAGE_HEIGHT 64
+
+
+static void
+draw_text_pattern (cairo_t *cr, double alpha)
+{
+    cairo_pattern_t *pat;
+
+    cairo_select_font_face (cr, CAIRO_TEST_FONT_FAMILY " Sans",
+                           CAIRO_FONT_SLANT_NORMAL,
+                           CAIRO_FONT_WEIGHT_NORMAL);
+
+    pat = cairo_pattern_create_linear (0.0, 0.0, 1, 1);
+    cairo_pattern_add_color_stop_rgba (pat, 1, 1, 0, 0, alpha);
+    cairo_pattern_add_color_stop_rgba (pat, 0, 0, 0, 1, alpha);
+    cairo_set_source (cr, pat);
+
+    /* test rectangle - make sure the gradient is set correctly */
+    cairo_rectangle (cr, 0, 0, 0.1, 1);
+    cairo_fill (cr);
+
+    cairo_set_font_size (cr, 0.4);
+    cairo_move_to (cr, 0.1, 0.6);
+    cairo_show_text (cr, "cairo");
+
+    cairo_pattern_destroy (pat);
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_scale (cr, width/2, height);
+    draw_text_pattern (cr, 1.0);
+    cairo_translate (cr, 1, 0);
+    draw_text_pattern (cr, 0.5);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (text_pattern,
+           "Patterned Text",
+           "text, pattern", /* keywords */
+           NULL, /* requirements */
+           IMAGE_WIDTH, IMAGE_HEIGHT,
+           NULL, draw)
diff --git a/test/text-rotate.c b/test/text-rotate.c
new file mode 100644 (file)
index 0000000..0a805ac
--- /dev/null
@@ -0,0 +1,189 @@
+/*
+ * Copyright © 2004 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ */
+
+/* Bug history
+ *
+ * 2004-11-03 Steve Chaplin <stevech1097@yahoo.com.au>
+ *
+ *   Reported bug on mailing list:
+ *
+ *     From: Steve Chaplin <stevech1097@yahoo.com.au>
+ *     To: cairo@cairographics.org
+ *     Date: Thu, 04 Nov 2004 00:00:17 +0800
+ *     Subject: [cairo] Rotated text bug on drawable target
+ *
+ *     The attached file draws text rotated 90 degrees first to a PNG file and
+ *     then to a drawable. The PNG file looks fine, the text on the drawable is
+ *     unreadable.
+ *
+ *     Steve
+ *
+ * 2004-11-03 Carl Worth <cworth@cworth.org>
+ *
+ *   Looks like the major problems with this bug appeared in the great
+ *   font rework between 0.1.23 and 0.2.0. And it looks like we need
+ *   to fix the regression test suite to test the xlib target (since
+ *   the bug does not show up in the png backend).
+ *
+ *   Hmm... Actually, things don't look perfect even in the PNG
+ *   output. Look at how that 'o' moves around. It's particularly off
+ *   in the case where it's rotated by PI.
+ *
+ *   And I'm still not sure about what to do for test cases with
+ *   text--a new version of freetype will change everything. We may
+ *   need to add a simple backend for stroked fonts and add a simple
+ *   builtin font to cairo for pixel-perfect tests with text.
+ *
+ * 2005-08-23
+ *
+ *   It appears that the worst placement and glyph selection problems
+ *   have now been resolved. In the past some letters were noticeably
+ *   of a different size at some rotations, and there was a lot of
+ *   drift away from the baseline. These problems do not appear
+ *   anymore.
+ *
+ *   Another thing that helps is that we now have font options which
+ *   we can use to disable hinting in order to get more repeatable
+ *   results. I'm doing that in this test now.
+ *
+ *   There are still some subtle positioning problems which I'm
+ *   assuming are due to the lack of finer-than-whole-pixel glyph
+ *   positioning. I'm generating a reference image now by replacing
+ *   cairo_show_text with cairo_text_path; cairo_fill. This will let
+ *   us look more closely at the remaining positioning problems. (In
+ *   particular, I want to make sure we're rounding as well as
+ *   possible).
+ *
+ * 2007-02-21
+ *
+ *   Seems like all the "bugs" have been fixed and all remaining is
+ *   missing support for subpixel glyph positioning.  Removing from
+ *   XFAIL now.
+ */
+
+#include "cairo-test.h"
+
+#define WIDTH  150
+#define HEIGHT 150
+#define NUM_TEXT 20
+#define TEXT_SIZE 12
+
+/* Draw the word cairo at NUM_TEXT different angles.
+ * We separate the circle into quadrants to reduce
+ * numerical errors i.e. so each quarter is pixel-aligned.
+ */
+static void
+draw_quadrant (cairo_t *cr,
+              const char *text,
+              const cairo_text_extents_t *extents,
+              const cairo_matrix_t *transform,
+              int x_off, int y_off)
+{
+    int i;
+
+    for (i = 0; i < NUM_TEXT/4; i++) {
+       cairo_save (cr);
+       cairo_rotate (cr, 2*M_PI*i/NUM_TEXT);
+       cairo_transform (cr, transform);
+       cairo_set_line_width (cr, 1.0);
+       cairo_rectangle (cr, x_off - 0.5, y_off - 0.5, extents->width + 1, extents->height + 1);
+       cairo_set_source_rgb (cr, 1, 0, 0);
+       cairo_stroke (cr);
+       cairo_move_to (cr, x_off - extents->x_bearing, y_off - extents->y_bearing);
+       cairo_set_source_rgb (cr, 0, 0, 0);
+#if CAIRO_TEST_GENERATE_REFERENCE_IMAGE
+       cairo_text_path (cr, text);
+       cairo_fill (cr);
+#else
+       cairo_show_text (cr, text);
+#endif
+       cairo_restore (cr);
+    }
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_text_extents_t extents;
+    cairo_font_options_t *font_options;
+    const char text[] = "cairo";
+    int x_off, y_off;
+    cairo_matrix_t m;
+
+    /* paint white so we don't need separate ref images for
+     * RGB24 and ARGB32 */
+    cairo_set_source_rgb (cr, 1., 1., 1.);
+    cairo_paint (cr);
+
+    cairo_select_font_face (cr, CAIRO_TEST_FONT_FAMILY " Sans",
+                           CAIRO_FONT_SLANT_NORMAL,
+                           CAIRO_FONT_WEIGHT_NORMAL);
+    cairo_set_font_size (cr, TEXT_SIZE);
+
+    font_options = cairo_font_options_create ();
+
+    cairo_get_font_options (cr, font_options);
+    cairo_font_options_set_hint_metrics (font_options, CAIRO_HINT_METRICS_OFF);
+
+    cairo_set_font_options (cr, font_options);
+    cairo_font_options_destroy (font_options);
+
+    cairo_set_source_rgb (cr, 0, 0, 0);
+
+    cairo_translate (cr, WIDTH/2.0, HEIGHT/2.0);
+
+    cairo_text_extents (cr, text, &extents);
+
+    if (NUM_TEXT == 1) {
+       x_off = y_off = 0;
+    } else {
+       y_off = - floor (0.5 + extents.height / 2.0);
+       x_off = floor (0.5 + (extents.height+1) / (2 * tan (M_PI/NUM_TEXT)));
+    }
+
+    cairo_save (cr);
+    cairo_matrix_init_identity (&m);
+    draw_quadrant (cr, text, &extents, &m, x_off, y_off);
+    cairo_matrix_init (&m, 0, 1, -1, 0, 0, 0);
+    draw_quadrant (cr, text, &extents, &m, x_off, y_off);
+    cairo_restore (cr);
+
+    cairo_save (cr);
+    cairo_scale (cr, -1, -1);
+    cairo_matrix_init_identity (&m);
+    draw_quadrant (cr, text, &extents, &m, x_off, y_off);
+    cairo_matrix_init (&m, 0, 1, -1, 0, 0, 0);
+    draw_quadrant (cr, text, &extents, &m, x_off, y_off);
+    cairo_restore (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (text_rotate,
+           "Tests show_text under various rotations",
+           "text, transform", /* keywords */
+           NULL, /* requirements */
+           WIDTH, HEIGHT,
+           NULL, draw)
diff --git a/test/text-transform.c b/test/text-transform.c
new file mode 100644 (file)
index 0000000..2cd7f10
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ * Copyright © 2006 Mozilla Corporation
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Mozilla Corporation not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Mozilla Corporation makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * MOZILLA CORPORATION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL MOZILLA CORPORATION BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Vladimir Vukicevic <vladimir@pobox.com>
+ */
+
+#include "cairo-test.h"
+
+#define SIZE 100
+#define PAD 5
+
+#define FONT_SIZE 32.0
+
+static const char *png_filename = "romedalen.png";
+
+static void
+draw_text (cairo_t *cr)
+{
+    cairo_matrix_t tm;
+
+    /* skew */
+    cairo_matrix_init (&tm, 1, 0,
+                       -0.25, 1,
+                       0, 0);
+    cairo_matrix_scale (&tm, FONT_SIZE, FONT_SIZE);
+    cairo_set_font_matrix (cr, &tm);
+
+    cairo_new_path (cr);
+    cairo_move_to (cr, 50, SIZE-PAD);
+    cairo_show_text (cr, "A");
+
+    /* rotate and scale */
+    cairo_matrix_init_rotate (&tm, M_PI / 2);
+    cairo_matrix_scale (&tm, FONT_SIZE, FONT_SIZE * 2.0);
+    cairo_set_font_matrix (cr, &tm);
+
+    cairo_new_path (cr);
+    cairo_move_to (cr, PAD, PAD + 25);
+    cairo_show_text (cr, "A");
+
+    cairo_matrix_init_rotate (&tm, M_PI / 2);
+    cairo_matrix_scale (&tm, FONT_SIZE * 2.0, FONT_SIZE);
+    cairo_set_font_matrix (cr, &tm);
+
+    cairo_new_path (cr);
+    cairo_move_to (cr, PAD, PAD + 50);
+    cairo_show_text (cr, "A");
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    const cairo_test_context_t *ctx = cairo_test_get_context (cr);
+    cairo_pattern_t *pattern;
+
+    cairo_set_source_rgb (cr, 1., 1., 1.);
+    cairo_paint (cr);
+
+    cairo_set_source_rgb (cr, 0., 0., 0.);
+
+    cairo_select_font_face (cr, CAIRO_TEST_FONT_FAMILY " Sans",
+                           CAIRO_FONT_SLANT_NORMAL,
+                           CAIRO_FONT_WEIGHT_NORMAL);
+
+    draw_text (cr);
+
+    cairo_translate (cr, SIZE, SIZE);
+    cairo_rotate (cr, M_PI);
+
+    pattern = cairo_test_create_pattern_from_png (ctx, png_filename);
+    cairo_pattern_set_extend (pattern, CAIRO_EXTEND_REPEAT);
+    cairo_set_source (cr, pattern);
+    cairo_pattern_destroy (pattern);
+
+    draw_text (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (text_transform,
+           "Test various applications of the font matrix",
+           "text, transform", /* keywords */
+           NULL, /* requirements */
+           SIZE, SIZE,
+           NULL, draw)
diff --git a/test/text-zero-len.c b/test/text-zero-len.c
new file mode 100644 (file)
index 0000000..5e89816
--- /dev/null
@@ -0,0 +1,203 @@
+/*
+ * Copyright © 2006 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Behdad Esfahbod <behdad@behdad.org>
+ */
+
+/* Related bug 5177
+ *
+ * In short:
+ *
+ * _cairo_atsui_font_text_to_glyph with a zero-sized string crashes.
+ *
+ * Moreover, the fallback path in cairo_scaled_font_text_to_glyphs()
+ * when handling a zero-sized string, allocates a zero-sized glyph array
+ * and when NULL is returned by malloc, recognizes that as an out-of-memory
+ * error.  The glibc implementation of malloc() does not return NULL from
+ * malloc(0), but I don't think it's a safe assumption.
+ *
+ * By just bailing out on zero-sized text, we fix both issues.
+ */
+
+#include "cairo-test.h"
+
+#define NUM_TEXT 20
+#define TEXT_SIZE 12
+
+static cairo_bool_t
+text_extents_equal (const cairo_text_extents_t *A,
+                   const cairo_text_extents_t *B)
+{
+    return A->x_bearing == B->x_bearing &&
+          A->y_bearing == B->y_bearing &&
+          A->width     == B->width     &&
+          A->height    == B->height    &&
+          A->x_advance == B->x_advance &&
+          A->y_advance == B->y_advance;
+}
+
+static cairo_bool_t
+font_extents_equal (const cairo_font_extents_t *A,
+                   const cairo_font_extents_t *B)
+{
+    return A->ascent    == B->ascent        &&
+          A->descent   == B->descent       &&
+          A->height    == B->height        &&
+       A->max_x_advance == B->max_x_advance &&
+       A->max_y_advance == B->max_y_advance;
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    const cairo_test_context_t *ctx = cairo_test_get_context (cr);
+    cairo_text_extents_t extents, nil_extents;
+    cairo_font_extents_t font_extents, nil_font_extents;
+    cairo_scaled_font_t *scaled_font;
+
+    cairo_select_font_face (cr, CAIRO_TEST_FONT_FAMILY " Sans",
+                           CAIRO_FONT_SLANT_NORMAL,
+                           CAIRO_FONT_WEIGHT_NORMAL);
+    cairo_set_font_size (cr, 16);
+
+    cairo_move_to (cr, 10, 25);
+    cairo_show_text (cr, NULL);
+    cairo_show_text (cr, "");
+    cairo_show_glyphs (cr, NULL, 0);
+    cairo_show_glyphs (cr, (void*)8, 0);
+
+    cairo_move_to (cr, 10, 55);
+    cairo_text_path (cr, NULL);
+    cairo_text_path (cr, "");
+    cairo_glyph_path (cr, (void*)8, 0);
+    cairo_fill (cr);
+
+    memset (&nil_extents, 0, sizeof (cairo_text_extents_t));
+
+    memset (&extents, 0xff, sizeof (cairo_text_extents_t));
+    cairo_text_extents (cr, "", &extents);
+    if (! text_extents_equal (&extents, &nil_extents)) {
+       cairo_test_log (ctx, "Error: cairo_text_extents(\"\"); extents (%g, %g, %g, %g, %g, %g)\n",
+                       extents.x_bearing, extents.y_bearing,
+                       extents.width, extents.height,
+                       extents.x_advance, extents.y_advance);
+       return CAIRO_TEST_FAILURE;
+    }
+
+    memset (&extents, 0xff, sizeof (cairo_text_extents_t));
+    cairo_text_extents (cr, NULL, &extents);
+    if (! text_extents_equal (&extents, &nil_extents)) {
+       cairo_test_log (ctx, "Error: cairo_text_extents(NULL); extents (%g, %g, %g, %g, %g, %g)\n",
+                       extents.x_bearing, extents.y_bearing,
+                       extents.width, extents.height,
+                       extents.x_advance, extents.y_advance);
+       return CAIRO_TEST_FAILURE;
+    }
+
+    memset (&extents, 0xff, sizeof (cairo_text_extents_t));
+    cairo_glyph_extents (cr, (void*)8, 0, &extents);
+    if (! text_extents_equal (&extents, &nil_extents)) {
+       cairo_test_log (ctx, "Error: cairo_glyph_extents(); extents (%g, %g, %g, %g, %g, %g)\n",
+                       extents.x_bearing, extents.y_bearing,
+                       extents.width, extents.height,
+                       extents.x_advance, extents.y_advance);
+       return CAIRO_TEST_FAILURE;
+    }
+
+    scaled_font = cairo_get_scaled_font (cr);
+
+    memset (&extents, 0xff, sizeof (cairo_text_extents_t));
+    cairo_scaled_font_text_extents (scaled_font, "", &extents);
+    if (! text_extents_equal (&extents, &nil_extents)) {
+       cairo_test_log (ctx, "Error: cairo_scaled_font_text_extents(\"\"); extents (%g, %g, %g, %g, %g, %g)\n",
+                       extents.x_bearing, extents.y_bearing,
+                       extents.width, extents.height,
+                       extents.x_advance, extents.y_advance);
+       return CAIRO_TEST_FAILURE;
+    }
+
+    memset (&extents, 0xff, sizeof (cairo_text_extents_t));
+    cairo_scaled_font_text_extents (scaled_font, NULL, &extents);
+    if (! text_extents_equal (&extents, &nil_extents)) {
+       cairo_test_log (ctx, "Error: cairo_scaled_font_text_extents(NULL); extents (%g, %g, %g, %g, %g, %g)\n",
+                       extents.x_bearing, extents.y_bearing,
+                       extents.width, extents.height,
+                       extents.x_advance, extents.y_advance);
+       return CAIRO_TEST_FAILURE;
+    }
+
+    memset (&extents, 0xff, sizeof (cairo_text_extents_t));
+    cairo_scaled_font_glyph_extents (scaled_font, (void*)8, 0, &extents);
+    if (! text_extents_equal (&extents, &nil_extents)) {
+       cairo_test_log (ctx, "Error: cairo_scaled_font_glyph_extents(NULL); extents (%g, %g, %g, %g, %g, %g)\n",
+                       extents.x_bearing, extents.y_bearing,
+                       extents.width, extents.height,
+                       extents.x_advance, extents.y_advance);
+       return CAIRO_TEST_FAILURE;
+    }
+
+    /* Lets also try font size 0 while here */
+    cairo_set_font_size (cr, 0);
+
+    memset (&extents, 0xff, sizeof (cairo_text_extents_t));
+    cairo_text_extents (cr, "test", &extents);
+    if (! text_extents_equal (&extents, &nil_extents)) {
+       cairo_test_log (ctx, "Error: cairo_set_font_size(0); cairo_text_extents(\"test\"); extents (%g, %g, %g, %g, %g, %g)\n",
+                       extents.x_bearing, extents.y_bearing,
+                       extents.width, extents.height,
+                       extents.x_advance, extents.y_advance);
+       return CAIRO_TEST_FAILURE;
+    }
+
+    memset (&nil_font_extents, 0, sizeof (cairo_font_extents_t));
+
+    memset (&font_extents, 0xff, sizeof (cairo_font_extents_t));
+    cairo_font_extents (cr,  &font_extents);
+    if (! font_extents_equal (&font_extents, &nil_font_extents)) {
+       cairo_test_log (ctx, "Error: cairo_set_font_size(0); cairo_font_extents(); extents (%g, %g, %g, %g, %g)\n",
+                       font_extents.ascent, font_extents.descent,
+                       font_extents.height,
+                       font_extents.max_x_advance, font_extents.max_y_advance);
+       return CAIRO_TEST_FAILURE;
+    }
+
+    scaled_font = cairo_get_scaled_font (cr);
+
+    memset (&font_extents, 0xff, sizeof (cairo_font_extents_t));
+    cairo_scaled_font_extents (scaled_font,  &font_extents);
+    if (! font_extents_equal (&font_extents, &nil_font_extents)) {
+       cairo_test_log (ctx, "Error: cairo_set_font_size(0); cairo_scaled_font_extents(); extents (%g, %g, %g, %g, %g)\n",
+                       font_extents.ascent, font_extents.descent,
+                       font_extents.height,
+                       font_extents.max_x_advance, font_extents.max_y_advance);
+       return CAIRO_TEST_FAILURE;
+    }
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (text_zero_len,
+           "Tests show_text and text_path with a zero-sized string",
+           "text, stress, extents", /* keywords */
+           NULL, /* requirements */
+           0, 0,
+           NULL, draw)
diff --git a/test/tiger.c b/test/tiger.c
new file mode 100644 (file)
index 0000000..6f45f1f
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "cairo-test.h"
+
+#include "tiger.inc"
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+       unsigned int i;
+
+       cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+       cairo_set_source_rgba (cr, 0.1, 0.2, 0.3, 1.0);
+       cairo_paint (cr);
+       cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
+
+       cairo_translate (cr, width/2, height/2);
+       cairo_scale (cr, .85, .85);
+
+       for (i = 0; i < sizeof (tiger_commands)/sizeof(tiger_commands[0]);i++) {
+               const struct command *cmd = &tiger_commands[i];
+               switch (cmd->type) {
+               case 'm':
+                       cairo_move_to (cr, cmd->x0, cmd->y0);
+                       break;
+               case 'l':
+                       cairo_line_to (cr, cmd->x0, cmd->y0);
+                       break;
+               case 'c':
+                       cairo_curve_to (cr,
+                                       cmd->x0, cmd->y0,
+                                       cmd->x1, cmd->y1,
+                                       cmd->x2, cmd->y2);
+                       break;
+               case 'f':
+                       cairo_set_source_rgba (cr,
+                                              cmd->x0, cmd->y0, cmd->x1, cmd->y1);
+                       cairo_fill (cr);
+                       break;
+               }
+       }
+
+       return CAIRO_TEST_SUCCESS;
+}
+
+static cairo_test_status_t
+a1_draw (cairo_t *cr, int width, int height)
+{
+    cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
+    return draw (cr, width, height);
+}
+
+CAIRO_TEST (tiger,
+           "Check the fidelity of the rasterisation.",
+           "raster", /* keywords */
+           NULL, /* requirements */
+           500, 500,
+           NULL, draw)
+
+CAIRO_TEST (a1_tiger,
+           "Check the fidelity of the rasterisation.",
+           "fill", /* keywords */
+           "target=raster", /* requirements */
+           500, 500,
+           NULL, a1_draw)
diff --git a/test/tiger.inc b/test/tiger.inc
new file mode 100644 (file)
index 0000000..419b979
--- /dev/null
@@ -0,0 +1,2316 @@
+static const struct command {
+  char type;
+  float x0, y0;
+  float x1, y1;
+  float x2, y2;
+} tiger_commands[] = {
+{'m', -122.30, 84.28, 0, 0, 0, 0}, 
+{'c', -122.30, 84.28, -122.20 ,86.18, -123.03, 86.16}, 
+{'c', -123.85, 86.14, -140.31 ,38.07, -160.83, 40.31}, 
+{'f', 1.000000,1.000000,1.000000,1.000000,0,0 }, 
+{'m', -118.77, 81.26, 0, 0, 0, 0}, 
+{'c', -118.77, 81.26, -119.32 ,83.08, -120.09, 82.78}, 
+{'c', -120.86, 82.48, -119.98 ,31.68, -140.04, 26.80}, 
+{'f', 1.000000,1.000000,1.000000,1.000000,0,0 }, 
+{'m', -91.28, 123.59, 0, 0, 0, 0}, 
+{'c', -91.28, 123.59, -89.65 ,124.55, -90.12, 125.23}, 
+{'c', -90.59, 125.90, -139.76 ,113.10, -149.22, 131.46}, 
+{'f', 1.000000,1.000000,1.000000,1.000000,0,0 }, 
+{'m', -94.09, 133.80, 0, 0, 0, 0}, 
+{'c', -94.09, 133.80, -92.24 ,134.20, -92.47, 134.99}, 
+{'c', -92.70, 135.78, -143.41 ,139.12, -146.60, 159.52}, 
+{'f', 1.000000,1.000000,1.000000,1.000000,0,0 }, 
+{'m', -98.30, 128.28, 0, 0, 0, 0}, 
+{'c', -98.30, 128.28, -96.53 ,128.94, -96.87, 129.69}, 
+{'c', -97.22, 130.44, -147.87 ,126.35, -154.00, 146.06}, 
+{'f', 1.000000,1.000000,1.000000,1.000000,0,0 }, 
+{'m', -109.01, 110.07, 0, 0, 0, 0}, 
+{'c', -109.01, 110.07, -107.70 ,111.45, -108.34, 111.97}, 
+{'c', -108.98, 112.49, -152.72 ,86.63, -166.87, 101.68}, 
+{'f', 1.000000,1.000000,1.000000,1.000000,0,0 }, 
+{'m', -116.55, 114.26, 0, 0, 0, 0}, 
+{'c', -116.55, 114.26, -115.10 ,115.48, -115.67, 116.07}, 
+{'c', -116.25, 116.66, -162.64 ,95.92, -174.99, 112.47}, 
+{'f', 1.000000,1.000000,1.000000,1.000000,0,0 }, 
+{'m', -119.15, 118.33, 0, 0, 0, 0}, 
+{'c', -119.15, 118.33, -117.55 ,119.34, -118.04, 120.01}, 
+{'c', -118.53, 120.67, -167.31 ,106.45, -177.29, 124.52}, 
+{'f', 1.000000,1.000000,1.000000,1.000000,0,0 }, 
+{'m', -108.42, 118.95, 0, 0, 0, 0}, 
+{'c', -108.42, 118.95, -107.30 ,120.48, -108.00, 120.92}, 
+{'c', -108.70, 121.35, -148.77 ,90.10, -164.73, 103.21}, 
+{'f', 1.000000,1.000000,1.000000,1.000000,0,0 }, 
+{'m', -128.20, 90.00, 0, 0, 0, 0}, 
+{'c', -128.20, 90.00, -127.60 ,91.80, -128.40, 92.00}, 
+{'c', -129.20, 92.20, -157.80 ,50.20, -177.00, 57.80}, 
+{'f', 1.000000,1.000000,1.000000,1.000000,0,0 }, 
+{'m', -127.50, 96.98, 0, 0, 0, 0}, 
+{'c', -127.50, 96.98, -126.53 ,98.61, -127.27, 98.97}, 
+{'c', -128.01, 99.34, -164.99 ,64.50, -182.10, 76.06}, 
+{'f', 1.000000,1.000000,1.000000,1.000000,0,0 }, 
+{'m', -127.62, 101.35, 0, 0, 0, 0}, 
+{'c', -127.62, 101.35, -126.50 ,102.88, -127.20, 103.31}, 
+{'c', -127.90, 103.75, -167.97 ,72.50, -183.93, 85.61}, 
+{'f', 1.000000,1.000000,1.000000,1.000000,0,0 }, 
+{'m', -129.83, 103.06, 0, 0, 0, 0}, 
+{'c', -129.33, 109.11, -128.34 ,115.68, -126.60, 118.80}, 
+{'c', -126.60, 118.80, -130.20 ,131.20, -121.40, 144.40}, 
+{'c', -121.40, 144.40, -121.80 ,151.60, -120.20, 154.80}, 
+{'c', -120.20, 154.80, -116.20 ,163.20, -111.40, 164.00}, 
+{'c', -107.52, 164.65, -98.79 ,167.72, -88.93, 169.12}, 
+{'c', -88.93, 169.12, -71.80 ,183.20, -75.00, 196.00}, 
+{'c', -75.00, 196.00, -75.40 ,212.40, -79.00, 214.00}, 
+{'c', -79.00, 214.00, -67.40 ,202.80, -77.00, 219.60}, 
+{'l', -81.40, 238.40, 0, 0, 0, 0}, 
+{'c', -81.40, 238.40, -55.80 ,216.80, -71.40, 235.20}, 
+{'l', -81.40, 261.20, 0, 0, 0, 0}, 
+{'c', -81.40, 261.20, -61.80 ,242.80, -69.00, 251.20}, 
+{'l', -72.20, 260.00, 0, 0, 0, 0}, 
+{'c', -72.20, 260.00, -29.00 ,232.80, -59.80, 262.40}, 
+{'c', -59.80, 262.40, -51.80 ,258.80, -47.40, 261.60}, 
+{'c', -47.40, 261.60, -40.60 ,260.40, -41.40, 262.00}, 
+{'c', -41.40, 262.00, -62.20 ,272.40, -65.80, 290.80}, 
+{'c', -65.80, 290.80, -57.40 ,280.80, -60.60, 291.60}, 
+{'l', -60.20, 303.20, 0, 0, 0, 0}, 
+{'c', -60.20, 303.20, -56.20 ,281.60, -56.60, 319.20}, 
+{'c', -56.60, 319.20, -37.40 ,301.20, -49.00, 322.00}, 
+{'l', -49.00, 338.80, 0, 0, 0, 0}, 
+{'c', -49.00, 338.80, -33.80 ,322.40, -40.20, 335.20}, 
+{'c', -40.20, 335.20, -30.20 ,326.40, -34.20, 341.60}, 
+{'c', -34.20, 341.60, -35.00 ,352.00, -30.60, 340.80}, 
+{'c', -30.60, 340.80, -14.60 ,310.20, -20.60, 336.40}, 
+{'c', -20.60, 336.40, -21.40 ,355.60, -16.60, 340.80}, 
+{'c', -16.60, 340.80, -16.20 ,351.20, -7.00, 358.40}, 
+{'c', -7.00, 358.40, -8.20 ,307.60, 4.60, 343.60}, 
+{'l', 8.60, 360.00, 0, 0, 0, 0}, 
+{'c', 8.60, 360.00, 11.40 ,350.80, 11.00, 345.60}, 
+{'c', 11.00, 345.60, 25.80 ,329.20, 19.00, 353.60}, 
+{'c', 19.00, 353.60, 34.20 ,330.80, 31.00, 344.00}, 
+{'c', 31.00, 344.00, 23.40 ,360.00, 25.00, 364.80}, 
+{'c', 25.00, 364.80, 41.80 ,330.00, 43.00, 328.40}, 
+{'c', 43.00, 328.40, 41.00 ,370.80, 51.80, 334.80}, 
+{'c', 51.80, 334.80, 57.40 ,346.80, 54.60, 351.20}, 
+{'c', 54.60, 351.20, 62.60 ,343.20, 61.80, 340.00}, 
+{'c', 61.80, 340.00, 66.40 ,331.80, 69.20, 345.40}, 
+{'c', 69.20, 345.40, 71.00 ,354.80, 72.60, 351.60}, 
+{'c', 72.60, 351.60, 76.60 ,375.60, 77.80, 352.80}, 
+{'c', 77.80, 352.80, 79.40 ,339.20, 72.20, 327.60}, 
+{'c', 72.20, 327.60, 73.00 ,324.40, 70.20, 320.40}, 
+{'c', 70.20, 320.40, 83.80 ,342.00, 76.60, 313.20}, 
+{'c', 76.60, 313.20, 87.80 ,321.20, 89.00, 321.20}, 
+{'c', 89.00, 321.20, 75.40 ,298.00, 84.20, 302.80}, 
+{'c', 84.20, 302.80, 79.00 ,292.40, 97.00, 304.40}, 
+{'c', 97.00, 304.40, 81.00 ,288.40, 98.60, 298.00}, 
+{'c', 98.60, 298.00, 106.60 ,304.40, 99.00, 294.40}, 
+{'c', 99.00, 294.40, 84.60 ,278.40, 106.60, 296.40}, 
+{'c', 106.60, 296.40, 118.20 ,312.80, 119.00, 315.60}, 
+{'c', 119.00, 315.60, 109.00 ,286.40, 104.60, 283.60}, 
+{'c', 104.60, 283.60, 113.00 ,247.20, 154.20, 262.80}, 
+{'c', 154.20, 262.80, 161.00 ,280.00, 165.40, 261.60}, 
+{'c', 165.40, 261.60, 178.20 ,255.20, 189.40, 282.80}, 
+{'c', 189.40, 282.80, 193.40 ,269.20, 192.60, 266.40}, 
+{'c', 192.60, 266.40, 199.40 ,267.60, 198.60, 266.40}, 
+{'c', 198.60, 266.40, 211.80 ,270.80, 213.00, 270.00}, 
+{'c', 213.00, 270.00, 219.80 ,276.80, 220.20, 273.20}, 
+{'c', 220.20, 273.20, 229.40 ,276.00, 227.40, 272.40}, 
+{'c', 227.40, 272.40, 236.20 ,288.00, 236.60, 291.60}, 
+{'l', 239.00, 277.60, 0, 0, 0, 0}, 
+{'l', 241.00, 280.40, 0, 0, 0, 0}, 
+{'c', 241.00, 280.40, 242.60 ,272.80, 241.80, 271.60}, 
+{'c', 241.00, 270.40, 261.80 ,278.40, 266.60, 299.20}, 
+{'l', 268.60, 307.60, 0, 0, 0, 0}, 
+{'c', 268.60, 307.60, 274.60 ,292.80, 273.00, 288.80}, 
+{'c', 273.00, 288.80, 278.20 ,289.60, 278.60, 294.00}, 
+{'c', 278.60, 294.00, 282.60 ,270.80, 277.80, 264.80}, 
+{'c', 277.80, 264.80, 282.20 ,264.00, 283.40, 267.60}, 
+{'l', 283.40, 260.40, 0, 0, 0, 0}, 
+{'c', 283.40, 260.40, 290.60 ,261.20, 290.60, 258.80}, 
+{'c', 290.60, 258.80, 295.00 ,254.80, 297.00, 259.60}, 
+{'c', 297.00, 259.60, 284.60 ,224.40, 303.00, 243.60}, 
+{'c', 303.00, 243.60, 310.20 ,254.40, 306.60, 235.60}, 
+{'c', 303.00, 216.80, 299.00 ,215.20, 303.80, 214.80}, 
+{'c', 303.80, 214.80, 304.60 ,211.20, 302.60, 209.60}, 
+{'c', 300.60, 208.00, 303.80 ,209.60, 303.80, 209.60}, 
+{'c', 303.80, 209.60, 308.60 ,213.60, 303.40, 191.60}, 
+{'c', 303.40, 191.60, 309.80 ,193.20, 297.80, 164.00}, 
+{'c', 297.80, 164.00, 300.60 ,161.60, 296.60, 153.20}, 
+{'c', 296.60, 153.20, 304.60 ,157.60, 307.40, 156.00}, 
+{'c', 307.40, 156.00, 307.00 ,154.40, 303.80, 150.40}, 
+{'c', 303.80, 150.40, 282.20 ,95.60, 302.60, 117.60}, 
+{'c', 302.60, 117.60, 314.45 ,131.15, 308.05, 108.35}, 
+{'c', 308.05, 108.35, 298.94 ,84.34, 299.72, 80.05}, 
+{'f', 1.000000,1.000000,1.000000,1.000000,0,0 }, 
+{'m', 299.72, 80.25, 0, 0, 0, 0}, 
+{'c', 300.35, 80.43, 302.55 ,81.55, 303.80, 83.20}, 
+{'c', 303.80, 83.20, 310.60 ,94.00, 305.40, 75.60}, 
+{'c', 305.40, 75.60, 296.20 ,46.80, 305.00, 58.00}, 
+{'c', 305.00, 58.00, 311.00 ,65.20, 307.80, 51.60}, 
+{'c', 303.94, 35.17, 301.40 ,28.80, 301.40, 28.80}, 
+{'c', 301.40, 28.80, 313.00 ,33.60, 286.20, -6.00}, 
+{'l', 295.00, -2.40, 0, 0, 0, 0}, 
+{'c', 295.00, -2.40, 275.40 ,-42.00, 253.80, -47.20}, 
+{'l', 245.80, -53.20, 0, 0, 0, 0}, 
+{'c', 245.80, -53.20, 284.20 ,-91.20, 271.40, -128.00}, 
+{'c', 271.40, -128.00, 264.60 ,-133.20, 255.00, -124.00}, 
+{'c', 255.00, -124.00, 248.60 ,-119.20, 242.60, -120.80}, 
+{'c', 242.60, -120.80, 211.80 ,-119.60, 209.80, -119.60}, 
+{'c', 207.80, -119.60, 173.00 ,-156.80, 107.40, -139.20}, 
+{'c', 107.40, -139.20, 102.20 ,-137.20, 97.80, -138.40}, 
+{'c', 97.80, -138.40, 79.40 ,-154.40, 30.60, -131.60}, 
+{'c', 30.60, -131.60, 20.60 ,-129.60, 19.00, -129.60}, 
+{'c', 17.40, -129.60, 14.60 ,-129.60, 6.60, -123.20}, 
+{'c', -1.40, -116.80, -1.80 ,-116.00, -3.80, -114.40}, 
+{'c', -3.80, -114.40, -20.20 ,-103.20, -25.00, -102.40}, 
+{'c', -25.00, -102.40, -36.60 ,-96.00, -41.00, -86.00}, 
+{'l', -44.60, -84.80, 0, 0, 0, 0}, 
+{'c', -44.60, -84.80, -46.20 ,-77.60, -46.60, -76.40}, 
+{'c', -46.60, -76.40, -51.40 ,-72.80, -52.20, -67.20}, 
+{'c', -52.20, -67.20, -61.00 ,-61.20, -60.60, -56.80}, 
+{'c', -60.60, -56.80, -62.20 ,-51.60, -63.00, -46.80}, 
+{'c', -63.00, -46.80, -70.20 ,-42.00, -69.40, -39.20}, 
+{'c', -69.40, -39.20, -77.00 ,-25.20, -75.80, -18.40}, 
+{'c', -75.80, -18.40, -82.20 ,-18.80, -85.00, -16.40}, 
+{'c', -85.00, -16.40, -85.80 ,-11.60, -87.40, -11.20}, 
+{'c', -87.40, -11.20, -90.20 ,-10.00, -87.80, -6.00}, 
+{'c', -87.80, -6.00, -89.40 ,-3.20, -89.80, -1.60}, 
+{'c', -89.80, -1.60, -89.00 ,1.20, -93.40, 6.80}, 
+{'c', -93.40, 6.80, -99.80 ,25.60, -97.80, 30.80}, 
+{'c', -97.80, 30.80, -97.40 ,35.60, -100.20, 37.20}, 
+{'c', -100.20, 37.20, -103.80 ,36.80, -95.40, 48.80}, 
+{'c', -95.40, 48.80, -94.60 ,50.00, -97.80, 52.40}, 
+{'c', -97.80, 52.40, -115.00 ,56.00, -117.40, 72.40}, 
+{'c', -117.40, 72.40, -131.00 ,87.20, -131.00, 92.40}, 
+{'c', -131.00, 94.70, -130.73 ,97.85, -130.03, 102.47}, 
+{'c', -130.03, 102.47, -130.60 ,110.80, -103.00, 111.60}, 
+{'f', 0.804000,0.469000,0.134000,1.000000,0,0 }, 
+{'m', -115.60, 102.60, 0, 0, 0, 0}, 
+{'c', -140.60, 63.20, -126.20 ,119.60, -126.20, 119.60}, 
+{'c', -117.40, 154.00, 12.20 ,116.40, 12.20, 116.40}, 
+{'c', 12.20, 116.40, 181.00 ,86.00, 192.20, 82.00}, 
+{'c', 203.40, 78.00, 298.60 ,84.40, 298.60, 84.40}, 
+{'l', 293.00, 67.60, 0, 0, 0, 0}, 
+{'c', 228.20, 21.20, 209.00 ,44.40, 195.40, 40.40}, 
+{'c', 181.80, 36.40, 184.20 ,46.00, 181.00, 46.80}, 
+{'c', 177.80, 47.60, 138.60 ,22.80, 132.20, 23.60}, 
+{'c', 125.80, 24.40, 100.46 ,0.65, 115.40, 32.40}, 
+{'c', 131.40, 66.40, 57.00 ,71.60, 40.20, 60.40}, 
+{'c', 23.40, 49.20, 47.40 ,78.80, 47.40, 78.80}, 
+{'c', 65.80, 98.80, 31.40 ,82.00, 31.40, 82.00}, 
+{'c', -3.00, 69.20, -27.00 ,94.80, -30.20, 95.60}, 
+{'c', -33.40, 96.40, -38.20 ,99.60, -39.00, 93.20}, 
+{'c', -39.80, 86.80, -47.31 ,70.10, -79.00, 96.40}, 
+{'c', -99.00, 113.00, -112.80 ,91.00, -112.80, 91.00}, 
+{'f', 0.804000,0.469000,0.134000,1.000000,0,0 }, 
+{'m', 133.51, 25.35, 0, 0, 0, 0}, 
+{'c', 127.11, 26.15, 101.74 ,2.41, 116.71, 34.15}, 
+{'c', 133.31, 69.35, 58.31 ,73.35, 41.51, 62.15}, 
+{'c', 24.71, 50.95, 48.71 ,80.55, 48.71, 80.55}, 
+{'c', 67.11, 100.55, 32.71 ,83.75, 32.71, 83.75}, 
+{'c', -1.69, 70.95, -25.69 ,96.55, -28.89, 97.35}, 
+{'c', -32.09, 98.15, -36.89 ,101.35, -37.69, 94.95}, 
+{'c', -38.49, 88.55, -45.87 ,72.01, -77.69, 98.15}, 
+{'c', -98.93, 115.49, -112.42 ,94.04, -112.42, 94.04}, 
+{'l', -115.62, 104.15, 0, 0, 0, 0}, 
+{'c', -140.62, 64.35, -125.55 ,122.66, -125.55, 122.66}, 
+{'c', -116.75, 157.06, 13.51 ,118.15, 13.51, 118.15}, 
+{'c', 13.51, 118.15, 182.31 ,87.75, 193.51, 83.75}, 
+{'c', 204.71, 79.75, 299.04 ,86.07, 299.04, 86.07}, 
+{'l', 293.51, 68.76, 0, 0, 0, 0}, 
+{'c', 228.71, 22.36, 210.31 ,46.15, 196.71, 42.15}, 
+{'c', 183.11, 38.15, 185.51 ,47.75, 182.31, 48.55}, 
+{'f', 0.938000,0.469000,0.201000,1.000000,0,0 }, 
+{'m', 134.82, 27.09, 0, 0, 0, 0}, 
+{'c', 128.42, 27.89, 103.69 ,3.86, 118.02, 35.89}, 
+{'c', 134.22, 72.09, 59.62 ,75.09, 42.82, 63.89}, 
+{'c', 26.02, 52.69, 50.02 ,82.29, 50.02, 82.29}, 
+{'c', 68.42, 102.29, 34.02 ,85.49, 34.02, 85.49}, 
+{'c', -0.38, 72.69, -24.38 ,98.29, -27.58, 99.09}, 
+{'c', -30.78, 99.89, -35.58 ,103.09, -36.38, 96.69}, 
+{'c', -37.18, 90.29, -44.43 ,73.92, -76.38, 99.89}, 
+{'c', -98.86, 117.98, -112.04 ,97.07, -112.04, 97.07}, 
+{'l', -115.64, 105.69, 0, 0, 0, 0}, 
+{'c', -139.44, 66.69, -124.89 ,125.71, -124.89, 125.71}, 
+{'c', -116.09, 160.11, 14.82 ,119.89, 14.82, 119.89}, 
+{'c', 14.82, 119.89, 183.62 ,89.49, 194.82, 85.49}, 
+{'c', 206.02, 81.49, 299.47 ,87.75, 299.47, 87.75}, 
+{'l', 294.02, 69.93, 0, 0, 0, 0}, 
+{'c', 229.22, 23.53, 211.62 ,47.89, 198.02, 43.89}, 
+{'c', 184.42, 39.89, 186.82 ,49.49, 183.62, 50.29}, 
+{'f', 0.938000,0.536000,0.268000,1.000000,0,0 }, 
+{'m', 136.13, 28.84, 0, 0, 0, 0}, 
+{'c', 129.73, 29.64, 105.00 ,5.61, 119.33, 37.64}, 
+{'c', 136.13, 75.19, 60.39 ,76.48, 44.13, 65.64}, 
+{'c', 27.33, 54.44, 51.33 ,84.04, 51.33, 84.04}, 
+{'c', 69.73, 104.04, 35.33 ,87.24, 35.33, 87.24}, 
+{'c', 0.93, 74.44, -23.07 ,100.04, -26.27, 100.84}, 
+{'c', -29.47, 101.64, -34.27 ,104.84, -35.07, 98.44}, 
+{'c', -35.87, 92.04, -42.99 ,75.84, -75.07, 101.64}, 
+{'c', -98.78, 120.47, -111.66 ,100.11, -111.66, 100.11}, 
+{'l', -115.66, 107.24, 0, 0, 0, 0}, 
+{'c', -137.46, 70.44, -124.24 ,128.76, -124.24, 128.76}, 
+{'c', -115.44, 163.16, 16.13 ,121.64, 16.13, 121.64}, 
+{'c', 16.13, 121.64, 184.93 ,91.24, 196.13, 87.24}, 
+{'c', 207.33, 83.24, 299.91 ,89.42, 299.91, 89.42}, 
+{'l', 294.53, 71.09, 0, 0, 0, 0}, 
+{'c', 229.73, 24.69, 212.93 ,49.64, 199.33, 45.64}, 
+{'c', 185.73, 41.64, 188.13 ,51.24, 184.93, 52.04}, 
+{'f', 0.938000,0.603000,0.402000,1.000000,0,0 }, 
+{'m', 137.44, 30.58, 0, 0, 0, 0}, 
+{'c', 131.04, 31.38, 106.81 ,7.13, 120.64, 39.38}, 
+{'c', 137.44, 78.58, 62.24 ,78.58, 45.44, 67.38}, 
+{'c', 28.64, 56.18, 52.64 ,85.78, 52.64, 85.78}, 
+{'c', 71.04, 105.78, 36.64 ,88.98, 36.64, 88.98}, 
+{'c', 2.24, 76.18, -21.76 ,101.78, -24.96, 102.58}, 
+{'c', -28.16, 103.38, -32.96 ,106.58, -33.76, 100.18}, 
+{'c', -34.56, 93.78, -41.55 ,77.75, -73.76, 103.38}, 
+{'c', -98.71, 122.97, -111.27 ,103.15, -111.27, 103.15}, 
+{'l', -115.67, 108.78, 0, 0, 0, 0}, 
+{'c', -135.47, 73.98, -123.58 ,131.82, -123.58, 131.82}, 
+{'c', -114.78, 166.22, 17.44 ,123.38, 17.44, 123.38}, 
+{'c', 17.44, 123.38, 186.24 ,92.98, 197.44, 88.98}, 
+{'c', 208.64, 84.98, 300.35 ,91.09, 300.35, 91.09}, 
+{'l', 295.04, 72.25, 0, 0, 0, 0}, 
+{'c', 230.24, 25.86, 214.24 ,51.38, 200.64, 47.38}, 
+{'c', 187.04, 43.38, 189.44 ,52.98, 186.24, 53.78}, 
+{'f', 0.938000,0.670000,0.469000,1.000000,0,0 }, 
+{'m', 138.75, 32.33, 0, 0, 0, 0}, 
+{'c', 132.35, 33.13, 106.38 ,9.68, 121.95, 41.13}, 
+{'c', 141.15, 79.93, 63.55 ,80.33, 46.75, 69.13}, 
+{'c', 29.95, 57.93, 53.95 ,87.53, 53.95, 87.53}, 
+{'c', 72.35, 107.53, 37.95 ,90.73, 37.95, 90.73}, 
+{'c', 3.55, 77.93, -20.45 ,103.53, -23.65, 104.33}, 
+{'c', -26.85, 105.13, -31.65 ,108.33, -32.45, 101.93}, 
+{'c', -33.25, 95.53, -40.11 ,79.67, -72.45, 105.13}, 
+{'c', -98.64, 125.46, -110.89 ,106.18, -110.89, 106.18}, 
+{'l', -115.69, 110.33, 0, 0, 0, 0}, 
+{'c', -133.69, 77.13, -122.93 ,134.87, -122.93, 134.87}, 
+{'c', -114.13, 169.27, 18.75 ,125.13, 18.75, 125.13}, 
+{'c', 18.75, 125.13, 187.55 ,94.73, 198.75, 90.73}, 
+{'c', 209.95, 86.73, 300.78 ,92.76, 300.78, 92.76}, 
+{'l', 295.55, 73.42, 0, 0, 0, 0}, 
+{'c', 230.75, 27.02, 215.55 ,53.13, 201.95, 49.13}, 
+{'c', 188.35, 45.13, 190.75 ,54.73, 187.55, 55.53}, 
+{'f', 1.000000,0.737000,0.536000,1.000000,0,0 }, 
+{'m', 140.06, 34.07, 0, 0, 0, 0}, 
+{'c', 133.66, 34.87, 107.31 ,11.61, 123.25, 42.87}, 
+{'c', 143.66, 82.87, 64.86 ,82.07, 48.05, 70.87}, 
+{'c', 31.25, 59.67, 55.26 ,89.27, 55.26, 89.27}, 
+{'c', 73.66, 109.27, 39.26 ,92.47, 39.26, 92.47}, 
+{'c', 4.86, 79.67, -19.14 ,105.27, -22.34, 106.07}, 
+{'c', -25.55, 106.87, -30.34 ,110.07, -31.14, 103.67}, 
+{'c', -31.95, 97.27, -38.67 ,81.58, -71.14, 106.87}, 
+{'c', -98.56, 127.95, -110.51 ,109.22, -110.51, 109.22}, 
+{'l', -115.71, 111.87, 0, 0, 0, 0}, 
+{'c', -131.71, 81.67, -122.27 ,137.93, -122.27, 137.93}, 
+{'c', -113.47, 172.33, 20.05 ,126.87, 20.05, 126.87}, 
+{'c', 20.05, 126.87, 188.86 ,96.47, 200.06, 92.47}, 
+{'c', 211.26, 88.47, 301.22 ,94.44, 301.22, 94.44}, 
+{'l', 296.06, 74.58, 0, 0, 0, 0}, 
+{'c', 231.26, 28.18, 216.86 ,54.87, 203.26, 50.87}, 
+{'c', 189.66, 46.87, 192.06 ,56.47, 188.86, 57.27}, 
+{'f', 1.000000,0.737000,0.603000,1.000000,0,0 }, 
+{'m', 141.37, 35.82, 0, 0, 0, 0}, 
+{'c', 134.97, 36.62, 107.52 ,13.94, 124.56, 44.62}, 
+{'c', 146.56, 84.22, 66.16 ,83.82, 49.36, 72.62}, 
+{'c', 32.56, 61.42, 56.56 ,91.02, 56.56, 91.02}, 
+{'c', 74.96, 111.02, 40.56 ,94.22, 40.56, 94.22}, 
+{'c', 6.16, 81.42, -17.84 ,107.02, -21.04, 107.82}, 
+{'c', -24.24, 108.62, -29.04 ,111.82, -29.84, 105.42}, 
+{'c', -30.64, 99.02, -37.23 ,83.49, -69.84, 108.62}, 
+{'c', -98.49, 130.44, -110.13 ,112.26, -110.13, 112.26}, 
+{'l', -115.73, 113.42, 0, 0, 0, 0}, 
+{'c', -130.13, 85.02, -121.62 ,140.98, -121.62, 140.98}, 
+{'c', -112.82, 175.38, 21.36 ,128.62, 21.36, 128.62}, 
+{'c', 21.36, 128.62, 190.16 ,98.22, 201.37, 94.22}, 
+{'c', 212.56, 90.22, 301.66 ,96.11, 301.66, 96.11}, 
+{'l', 296.56, 75.75, 0, 0, 0, 0}, 
+{'c', 231.76, 29.35, 218.16 ,56.62, 204.56, 52.62}, 
+{'c', 190.97, 48.62, 193.37 ,58.22, 190.16, 59.02}, 
+{'f', 1.000000,0.804000,0.737000,1.000000,0,0 }, 
+{'m', 142.67, 37.56, 0, 0, 0, 0}, 
+{'c', 136.27, 38.37, 108.83 ,15.69, 125.87, 46.37}, 
+{'c', 147.87, 85.97, 67.47 ,85.56, 50.67, 74.36}, 
+{'c', 33.87, 63.16, 57.87 ,92.77, 57.87, 92.77}, 
+{'c', 76.27, 112.77, 41.87 ,95.97, 41.87, 95.97}, 
+{'c', 7.47, 83.17, -16.53 ,108.77, -19.73, 109.56}, 
+{'c', -22.93, 110.36, -27.73 ,113.56, -28.53, 107.17}, 
+{'c', -29.33, 100.77, -35.79 ,85.41, -68.53, 110.36}, 
+{'c', -98.42, 132.93, -109.75 ,115.29, -109.75, 115.29}, 
+{'l', -115.75, 114.97, 0, 0, 0, 0}, 
+{'c', -129.35, 88.56, -120.96 ,144.04, -120.96, 144.04}, 
+{'c', -112.16, 178.44, 22.67 ,130.37, 22.67, 130.37}, 
+{'c', 22.67, 130.37, 191.47 ,99.97, 202.67, 95.97}, 
+{'c', 213.87, 91.97, 302.09 ,97.78, 302.09, 97.78}, 
+{'l', 297.07, 76.91, 0, 0, 0, 0}, 
+{'c', 232.27, 30.51, 219.47 ,58.37, 205.87, 54.37}, 
+{'c', 192.27, 50.37, 194.67 ,59.97, 191.47, 60.77}, 
+{'f', 1.000000,0.871000,0.804000,1.000000,0,0 }, 
+{'m', 143.98, 39.31, 0, 0, 0, 0}, 
+{'c', 137.58, 40.11, 110.53 ,17.22, 127.18, 48.11}, 
+{'c', 149.18, 88.91, 68.78 ,87.31, 51.98, 76.11}, 
+{'c', 35.18, 64.91, 59.18 ,94.51, 59.18, 94.51}, 
+{'c', 77.58, 114.51, 43.18 ,97.71, 43.18, 97.71}, 
+{'c', 8.78, 84.91, -15.22 ,110.51, -18.42, 111.31}, 
+{'c', -21.62, 112.11, -26.42 ,115.31, -27.22, 108.91}, 
+{'c', -28.02, 102.51, -34.35 ,87.32, -67.22, 112.11}, 
+{'c', -98.34, 135.42, -109.36 ,118.33, -109.36, 118.33}, 
+{'l', -115.76, 116.51, 0, 0, 0, 0}, 
+{'c', -128.76, 92.51, -120.31 ,147.09, -120.31, 147.09}, 
+{'c', -111.51, 181.49, 23.98 ,132.11, 23.98, 132.11}, 
+{'c', 23.98, 132.11, 192.78 ,101.71, 203.98, 97.71}, 
+{'c', 215.18, 93.71, 302.53 ,99.46, 302.53, 99.46}, 
+{'l', 297.58, 78.07, 0, 0, 0, 0}, 
+{'c', 232.78, 31.67, 220.78 ,60.11, 207.18, 56.11}, 
+{'c', 193.58, 52.11, 195.98 ,61.71, 192.78, 62.51}, 
+{'f', 1.000000,0.938000,0.871000,1.000000,0,0 }, 
+{'m', 145.29, 41.05, 0, 0, 0, 0}, 
+{'c', 138.89, 41.85, 112.92 ,18.41, 128.49, 49.85}, 
+{'c', 149.69, 92.66, 70.09 ,89.06, 53.29, 77.86}, 
+{'c', 36.49, 66.66, 60.49 ,96.26, 60.49, 96.26}, 
+{'c', 78.89, 116.26, 44.49 ,99.46, 44.49, 99.46}, 
+{'c', 10.09, 86.66, -13.91 ,112.26, -17.11, 113.06}, 
+{'c', -20.31, 113.86, -25.11 ,117.06, -25.91, 110.66}, 
+{'c', -26.71, 104.26, -32.91 ,89.23, -65.91, 113.86}, 
+{'c', -98.27, 137.91, -108.98 ,121.36, -108.98, 121.36}, 
+{'l', -115.78, 118.06, 0, 0, 0, 0}, 
+{'c', -128.58, 94.86, -119.65 ,150.15, -119.65, 150.15}, 
+{'c', -110.85, 184.55, 25.29 ,133.86, 25.29, 133.86}, 
+{'c', 25.29, 133.86, 194.09 ,103.46, 205.29, 99.46}, 
+{'c', 216.49, 95.46, 302.96 ,101.13, 302.96, 101.13}, 
+{'l', 298.09, 79.24, 0, 0, 0, 0}, 
+{'c', 233.29, 32.84, 222.09 ,61.86, 208.49, 57.86}, 
+{'c', 194.89, 53.85, 197.29 ,63.46, 194.09, 64.26}, 
+{'f', 1.000000,1.000000,0.938000,1.000000,0,0 }, 
+{'m', -115.80, 119.60, 0, 0, 0, 0}, 
+{'c', -128.60, 97.60, -119.00 ,153.20, -119.00, 153.20}, 
+{'c', -110.20, 187.60, 26.60 ,135.60, 26.60, 135.60}, 
+{'c', 26.60, 135.60, 195.40 ,105.20, 206.60, 101.20}, 
+{'c', 217.80, 97.20, 303.40 ,102.80, 303.40, 102.80}, 
+{'l', 298.60, 80.40, 0, 0, 0, 0}, 
+{'c', 233.80, 34.00, 223.40 ,63.60, 209.80, 59.60}, 
+{'c', 196.20, 55.60, 198.60 ,65.20, 195.40, 66.00}, 
+{'c', 192.20, 66.80, 153.00 ,42.00, 146.60, 42.80}, 
+{'c', 140.20, 43.60, 114.98 ,19.79, 129.80, 51.60}, 
+{'c', 152.03, 99.31, 69.04 ,89.23, 54.60, 79.60}, 
+{'c', 37.80, 68.40, 61.80 ,98.00, 61.80, 98.00}, 
+{'c', 80.20, 118.00, 45.80 ,101.20, 45.80, 101.20}, 
+{'c', 11.40, 88.40, -12.60 ,114.00, -15.80, 114.80}, 
+{'c', -19.00, 115.60, -23.80 ,118.80, -24.60, 112.40}, 
+{'c', -25.40, 106.00, -31.46 ,91.14, -64.60, 115.60}, 
+{'c', -98.20, 140.40, -108.60 ,124.40, -108.60, 124.40}, 
+{'f', 1.000000,1.000000,1.000000,1.000000,0,0 }, 
+{'m', -74.20, 149.60, 0, 0, 0, 0}, 
+{'c', -74.20, 149.60, -81.40 ,161.20, -60.60, 174.40}, 
+{'c', -60.60, 174.40, -59.20 ,175.80, -77.20, 171.60}, 
+{'c', -77.20, 171.60, -83.40 ,169.60, -85.00, 159.20}, 
+{'c', -85.00, 159.20, -89.80 ,154.80, -94.60, 149.20}, 
+{'f', 0.000000,0.000000,0.000000,1.000000,0,0 }, 
+{'m', 65.80, 102.00, 0, 0, 0, 0}, 
+{'c', 65.80, 102.00, 83.50 ,128.82, 82.90, 133.60}, 
+{'c', 81.60, 144.00, 81.40 ,153.60, 84.60, 157.60}, 
+{'c', 87.80, 161.60, 96.60 ,194.80, 96.60, 194.80}, 
+{'c', 96.60, 194.80, 96.20 ,196.00, 108.60, 158.00}, 
+{'c', 108.60, 158.00, 120.20 ,142.00, 100.20, 123.60}, 
+{'f', 0.804000,0.804000,0.804000,1.000000,0,0 }, 
+{'m', -54.20, 176.40, 0, 0, 0, 0}, 
+{'c', -54.20, 176.40, -43.00 ,183.60, -57.40, 214.80}, 
+{'l', -51.00, 212.40, 0, 0, 0, 0}, 
+{'c', -51.00, 212.40, -51.80 ,223.60, -55.00, 226.00}, 
+{'l', -47.80, 222.80, 0, 0, 0, 0}, 
+{'c', -47.80, 222.80, -43.00 ,230.80, -47.00, 235.60}, 
+{'c', -47.00, 235.60, -30.20 ,243.60, -31.00, 250.00}, 
+{'c', -31.00, 250.00, -24.60 ,242.00, -28.60, 235.60}, 
+{'c', -32.60, 229.20, -39.80 ,233.20, -39.00, 214.80}, 
+{'l', -47.80, 218.00, 0, 0, 0, 0}, 
+{'c', -47.80, 218.00, -42.20 ,209.20, -42.20, 202.80}, 
+{'l', -50.20, 205.20, 0, 0, 0, 0}, 
+{'c', -50.20, 205.20, -34.73 ,178.62, -45.40, 177.20}, 
+{'f', 0.000000,0.000000,0.000000,1.000000,0,0 }, 
+{'m', -21.80, 193.20, 0, 0, 0, 0}, 
+{'c', -21.80, 193.20, -19.00 ,188.80, -21.80, 189.60}, 
+{'c', -24.60, 190.40, -55.80 ,205.20, -61.80, 214.80}, 
+{'f', 0.804000,0.804000,0.804000,1.000000,0,0 }, 
+{'m', -11.40, 201.20, 0, 0, 0, 0}, 
+{'c', -11.40, 201.20, -8.60 ,196.80, -11.40, 197.60}, 
+{'c', -14.20, 198.40, -45.40 ,213.20, -51.40, 222.80}, 
+{'f', 0.804000,0.804000,0.804000,1.000000,0,0 }, 
+{'m', 1.80, 186.00, 0, 0, 0, 0}, 
+{'c', 1.80, 186.00, 4.60 ,181.60, 1.80, 182.40}, 
+{'c', -1.00, 183.20, -32.20 ,198.00, -38.20, 207.60}, 
+{'f', 0.804000,0.804000,0.804000,1.000000,0,0 }, 
+{'m', -21.40, 229.60, 0, 0, 0, 0}, 
+{'c', -21.40, 229.60, -21.40 ,223.60, -24.20, 224.40}, 
+{'c', -27.00, 225.20, -63.00 ,242.80, -69.00, 252.40}, 
+{'f', 0.804000,0.804000,0.804000,1.000000,0,0 }, 
+{'m', -20.20, 218.80, 0, 0, 0, 0}, 
+{'c', -20.20, 218.80, -19.00 ,214.00, -21.80, 214.80}, 
+{'c', -23.80, 214.80, -50.20 ,226.40, -56.20, 236.00}, 
+{'f', 0.804000,0.804000,0.804000,1.000000,0,0 }, 
+{'m', -34.60, 266.40, 0, 0, 0, 0}, 
+{'l', -44.60, 274.00, 0, 0, 0, 0}, 
+{'c', -44.60, 274.00, -34.20 ,266.40, -30.60, 267.60}, 
+{'c', -30.60, 267.60, -37.40 ,278.80, -38.20, 284.00}, 
+{'c', -38.20, 284.00, -27.80 ,271.20, -22.20, 271.60}, 
+{'c', -22.20, 271.60, -14.60 ,272.00, -14.60, 282.80}, 
+{'c', -14.60, 282.80, -9.00 ,272.40, -5.80, 272.80}, 
+{'c', -5.80, 272.80, -4.60 ,279.20, -5.80, 286.00}, 
+{'c', -5.80, 286.00, -1.80 ,278.40, 2.20, 280.00}, 
+{'c', 2.20, 280.00, 8.60 ,278.00, 7.80, 289.60}, 
+{'c', 7.80, 289.60, 7.80 ,300.00, 7.00, 302.80}, 
+{'c', 7.00, 302.80, 12.60 ,276.40, 15.00, 276.00}, 
+{'c', 15.00, 276.00, 23.00 ,274.80, 27.80, 283.60}, 
+{'c', 27.80, 283.60, 23.80 ,276.00, 28.60, 278.00}, 
+{'c', 28.60, 278.00, 39.40 ,279.60, 42.60, 286.40}, 
+{'c', 42.60, 286.40, 35.80 ,274.40, 41.40, 277.60}, 
+{'c', 41.40, 277.60, 48.20 ,277.60, 49.40, 284.00}, 
+{'c', 49.40, 284.00, 57.80 ,305.20, 59.80, 306.80}, 
+{'c', 59.80, 306.80, 52.20 ,285.20, 53.80, 285.20}, 
+{'c', 53.80, 285.20, 51.80 ,273.20, 57.00, 288.00}, 
+{'c', 57.00, 288.00, 53.80 ,274.00, 59.40, 274.80}, 
+{'c', 65.00, 275.60, 69.40 ,285.60, 77.80, 283.20}, 
+{'c', 77.80, 283.20, 87.40 ,288.80, 89.40, 219.60}, 
+{'f', 0.804000,0.804000,0.804000,1.000000,0,0 }, 
+{'m', -29.80, 173.60, 0, 0, 0, 0}, 
+{'c', -29.80, 173.60, -15.00 ,167.60, 25.00, 173.60}, 
+{'c', 25.00, 173.60, 32.20 ,174.00, 39.00, 165.20}, 
+{'c', 45.80, 156.40, 72.60 ,149.20, 79.00, 151.20}, 
+{'l', 88.60, 157.60, 0, 0, 0, 0}, 
+{'l', 89.40, 158.80, 0, 0, 0, 0}, 
+{'c', 89.40, 158.80, 101.80 ,169.20, 102.20, 176.80}, 
+{'c', 102.60, 184.40, 87.80 ,232.40, 78.20, 248.40}, 
+{'c', 68.60, 264.40, 59.00 ,276.80, 39.80, 274.40}, 
+{'c', 39.80, 274.40, 19.00 ,270.40, -6.60, 274.40}, 
+{'c', -6.60, 274.40, -35.80 ,272.80, -38.60, 264.80}, 
+{'c', -41.40, 256.80, -27.40 ,241.60, -27.40, 241.60}, 
+{'c', -27.40, 241.60, -23.00 ,233.20, -24.20, 218.80}, 
+{'f', 0.000000,0.000000,0.000000,1.000000,0,0 }, 
+{'m', -7.80, 175.60, 0, 0, 0, 0}, 
+{'c', 0.60, 194.00, -29.00 ,259.20, -29.00, 259.20}, 
+{'c', -31.00, 260.80, -16.34 ,266.85, -6.20, 264.40}, 
+{'c', 4.75, 261.76, 45.00 ,266.00, 45.00, 266.00}, 
+{'c', 68.60, 250.40, 81.40 ,206.00, 81.40, 206.00}, 
+{'c', 81.40, 206.00, 91.80 ,182.00, 74.20, 178.80}, 
+{'f', 0.938000,0.402000,0.536000,1.000000,0,0 }, 
+{'m', -9.83, 206.50, 0, 0, 0, 0}, 
+{'c', -6.50, 193.71, -4.92 ,181.91, -7.80, 175.60}, 
+{'c', -7.80, 175.60, 54.60 ,182.00, 65.80, 161.20}, 
+{'c', 70.04, 153.33, 84.80 ,184.00, 84.40, 193.60}, 
+{'c', 84.40, 193.60, 21.40 ,208.00, 6.60, 196.80}, 
+{'f', 0.737000,0.201000,0.335000,1.000000,0,0 }, 
+{'m', -5.40, 222.80, 0, 0, 0, 0}, 
+{'c', -5.40, 222.80, -3.40 ,230.00, -5.80, 234.00}, 
+{'c', -5.80, 234.00, -7.40 ,234.80, -8.60, 235.20}, 
+{'c', -8.60, 235.20, -7.40 ,238.80, -1.40, 240.40}, 
+{'c', -1.40, 240.40, 0.60 ,244.80, 3.00, 245.20}, 
+{'c', 5.40, 245.60, 10.20 ,251.20, 14.20, 250.00}, 
+{'c', 18.20, 248.80, 29.40 ,244.80, 29.40, 244.80}, 
+{'c', 29.40, 244.80, 35.00 ,241.60, 43.80, 245.20}, 
+{'c', 43.80, 245.20, 46.17 ,244.40, 46.60, 240.40}, 
+{'c', 47.10, 235.70, 50.20 ,232.00, 52.20, 230.00}, 
+{'c', 54.20, 228.00, 63.80 ,215.20, 62.60, 214.80}, 
+{'f', 0.670000,0.134000,0.268000,1.000000,0,0 }, 
+{'m', -9.80, 174.40, 0, 0, 0, 0}, 
+{'c', -9.80, 174.40, -12.60 ,196.80, -9.40, 205.20}, 
+{'c', -6.20, 213.60, -7.00 ,215.60, -7.80, 219.60}, 
+{'c', -8.60, 223.60, -4.20 ,233.60, 1.40, 239.60}, 
+{'l', 13.40, 241.20, 0, 0, 0, 0}, 
+{'c', 13.40, 241.20, 28.60 ,237.60, 37.80, 240.40}, 
+{'c', 37.80, 240.40, 46.79 ,241.74, 50.20, 226.80}, 
+{'c', 50.20, 226.80, 55.00 ,220.40, 62.20, 217.60}, 
+{'c', 69.40, 214.80, 76.60 ,173.20, 72.60, 165.20}, 
+{'c', 68.60, 157.20, 54.20 ,152.80, 38.20, 168.40}, 
+{'f', 1.000000,0.469000,0.469000,1.000000,0,0 }, 
+{'m', -8.20, 249.20, 0, 0, 0, 0}, 
+{'c', -8.20, 249.20, -9.00 ,247.20, -13.40, 246.80}, 
+{'c', -13.40, 246.80, -35.80 ,243.20, -44.20, 230.80}, 
+{'c', -44.20, 230.80, -51.00 ,225.20, -46.60, 236.80}, 
+{'c', -46.60, 236.80, -36.20 ,257.20, -29.40, 260.00}, 
+{'f', 1.000000,1.000000,0.804000,1.000000,0,0 }, 
+{'m', 71.74, 185.23, 0, 0, 0, 0}, 
+{'c', 72.40, 177.32, 74.35 ,168.71, 72.60, 165.20}, 
+{'c', 66.15, 152.31, 49.18 ,157.69, 38.20, 168.40}, 
+{'c', 22.20, 184.00, 20.20 ,167.20, -9.80, 174.40}, 
+{'c', -9.80, 174.40, -11.54 ,188.36, -10.71, 198.38}, 
+{'c', -10.71, 198.38, 26.60 ,186.80, 27.40, 192.40}, 
+{'c', 27.40, 192.40, 29.00 ,189.20, 38.20, 189.20}, 
+{'f', 0.804000,0.201000,0.268000,1.000000,0,0 }, 
+{'m', 28.60, 175.20, 0, 0, 0, 0}, 
+{'c', 28.60, 175.20, 33.40 ,180.00, 29.80, 189.60}, 
+{'f', 0.804000,0.201000,0.268000,1.000000,0,0 }, 
+{'m', -19.40, 260.00, 0, 0, 0, 0}, 
+{'c', -19.40, 260.00, -23.80 ,247.20, -15.00, 254.00}, 
+{'c', -15.00, 254.00, -10.20 ,256.00, -11.40, 257.60}, 
+{'f', 1.000000,1.000000,0.804000,1.000000,0,0 }, 
+{'m', -14.36, 261.20, 0, 0, 0, 0}, 
+{'c', -14.36, 261.20, -17.88 ,250.96, -10.84, 256.40}, 
+{'c', -10.84, 256.40, -6.42 ,258.85, -7.96, 259.28}, 
+{'f', 1.000000,1.000000,0.804000,1.000000,0,0 }, 
+{'m', -9.56, 261.20, 0, 0, 0, 0}, 
+{'c', -9.56, 261.20, -13.08 ,250.96, -6.04, 256.40}, 
+{'c', -6.04, 256.40, -1.67 ,258.71, -3.16, 259.28}, 
+{'f', 1.000000,1.000000,0.804000,1.000000,0,0 }, 
+{'m', -2.96, 261.40, 0, 0, 0, 0}, 
+{'c', -2.96, 261.40, -6.48 ,251.16, 0.56, 256.60}, 
+{'c', 0.56, 256.60, 4.94 ,258.93, 3.44, 259.48}, 
+{'f', 1.000000,1.000000,0.804000,1.000000,0,0 }, 
+{'m', 3.52, 261.32, 0, 0, 0, 0}, 
+{'c', 3.52, 261.32, 0.00 ,251.08, 7.04, 256.52}, 
+{'c', 7.04, 256.52, 10.88 ,258.12, 9.92, 259.40}, 
+{'f', 1.000000,1.000000,0.804000,1.000000,0,0 }, 
+{'m', 10.20, 262.00, 0, 0, 0, 0}, 
+{'c', 10.20, 262.00, 5.40 ,249.60, 14.60, 256.00}, 
+{'c', 14.60, 256.00, 19.40 ,258.00, 18.20, 259.60}, 
+{'f', 1.000000,1.000000,0.804000,1.000000,0,0 }, 
+{'m', -18.20, 244.80, 0, 0, 0, 0}, 
+{'c', -18.20, 244.80, -5.00 ,242.00, 1.00, 245.20}, 
+{'c', 1.00, 245.20, 7.00 ,246.40, 8.20, 246.00}, 
+{'f', 1.000000,1.000000,0.804000,1.000000,0,0 }, 
+{'m', 15.80, 253.60, 0, 0, 0, 0}, 
+{'c', 15.80, 253.60, 27.80 ,240.00, 39.80, 244.40}, 
+{'c', 46.82, 246.97, 45.80 ,243.60, 46.60, 240.80}, 
+{'f', 1.000000,1.000000,0.804000,1.000000,0,0 }, 
+{'m', 33.00, 237.60, 0, 0, 0, 0}, 
+{'c', 33.00, 237.60, 29.00 ,226.80, 26.20, 239.60}, 
+{'c', 23.40, 252.40, 20.20 ,256.00, 18.60, 258.80}, 
+{'c', 18.60, 258.80, 18.60 ,264.00, 27.00, 263.60}, 
+{'c', 27.00, 263.60, 37.80 ,263.20, 38.20, 260.40}, 
+{'f', 1.000000,1.000000,0.804000,1.000000,0,0 }, 
+{'m', 47.00, 244.80, 0, 0, 0, 0}, 
+{'f', 1.000000,1.000000,0.804000,1.000000,0,0 }, 
+{'m', 53.50, 228.40, 0, 0, 0, 0}, 
+{'f', 1.000000,1.000000,0.804000,1.000000,0,0 }, 
+{'m', -25.80, 265.20, 0, 0, 0, 0}, 
+{'c', -25.80, 265.20, -7.80 ,268.40, -3.40, 266.80}, 
+{'c', -3.40, 266.80, 5.40 ,266.80, -3.00, 268.80}, 
+{'c', -3.00, 268.80, -15.80 ,268.80, -23.80, 267.60}, 
+{'f', 0.737000,0.737000,0.737000,1.000000,0,0 }, 
+{'m', -11.80, 172.00, 0, 0, 0, 0}, 
+{'c', -11.80, 172.00, 5.80 ,172.00, 7.80, 172.80}, 
+{'c', 7.80, 172.80, 15.00 ,203.60, 11.40, 211.20}, 
+{'c', 11.40, 211.20, 10.20 ,214.00, 7.40, 208.40}, 
+{'c', 7.40, 208.40, -11.00 ,175.60, -14.20, 173.60}, 
+{'f', 1.000000,1.000000,0.804000,1.000000,0,0 }, 
+{'m', -88.90, 169.30, 0, 0, 0, 0}, 
+{'c', -88.90, 169.30, -80.00 ,171.00, -67.40, 173.60}, 
+{'c', -67.40, 173.60, -62.60 ,196.00, -59.40, 200.80}, 
+{'c', -56.20, 205.60, -59.80 ,205.60, -63.40, 202.80}, 
+{'c', -67.00, 200.00, -81.80 ,186.00, -83.80, 181.60}, 
+{'f', 1.000000,1.000000,0.804000,1.000000,0,0 }, 
+{'m', -67.04, 173.82, 0, 0, 0, 0}, 
+{'c', -67.04, 173.82, -61.24 ,175.37, -60.23, 177.58}, 
+{'c', -59.22, 179.79, -61.43 ,183.09, -61.43, 183.09}, 
+{'c', -61.43, 183.09, -62.43 ,186.40, -63.63, 184.24}, 
+{'f', 1.000000,1.000000,0.804000,1.000000,0,0 }, 
+{'m', -67.00, 173.60, 0, 0, 0, 0}, 
+{'c', -67.00, 173.60, -63.40 ,178.80, -59.80, 178.80}, 
+{'c', -56.20, 178.80, -55.82 ,178.39, -53.00, 179.00}, 
+{'c', -48.40, 180.00, -48.80 ,178.00, -42.20, 179.20}, 
+{'c', -39.56, 179.68, -37.00 ,178.80, -34.20, 180.00}, 
+{'c', -31.40, 181.20, -28.20 ,180.40, -27.00, 178.40}, 
+{'c', -25.80, 176.40, -21.00 ,172.20, -21.00, 172.20}, 
+{'c', -21.00, 172.20, -33.80 ,174.00, -36.60, 174.80}, 
+{'f', 0.000000,0.000000,0.000000,1.000000,0,0 }, 
+{'m', -22.40, 173.80, 0, 0, 0, 0}, 
+{'c', -22.40, 173.80, -28.85 ,177.30, -29.25, 179.70}, 
+{'c', -29.65, 182.10, -24.00 ,185.80, -24.00, 185.80}, 
+{'c', -24.00, 185.80, -21.25 ,190.40, -20.65, 188.00}, 
+{'f', 1.000000,1.000000,0.804000,1.000000,0,0 }, 
+{'m', -59.88, 179.26, 0, 0, 0, 0}, 
+{'c', -59.88, 179.26, -52.88 ,190.45, -52.66, 179.24}, 
+{'c', -52.66, 179.24, -52.10 ,177.98, -53.86, 177.96}, 
+{'f', 1.000000,1.000000,0.804000,1.000000,0,0 }, 
+{'m', -52.71, 179.51, 0, 0, 0, 0}, 
+{'c', -52.71, 179.51, -44.79 ,190.70, -45.42, 179.42}, 
+{'c', -45.42, 179.42, -45.41 ,179.09, -47.17, 178.94}, 
+{'f', 1.000000,1.000000,0.804000,1.000000,0,0 }, 
+{'m', -45.49, 179.52, 0, 0, 0, 0}, 
+{'c', -45.49, 179.52, -37.53 ,190.15, -38.20, 180.48}, 
+{'c', -38.20, 180.48, -38.08 ,179.25, -39.74, 178.95}, 
+{'f', 1.000000,1.000000,0.804000,1.000000,0,0 }, 
+{'m', -38.62, 179.60, 0, 0, 0, 0}, 
+{'c', -38.62, 179.60, -30.72 ,191.16, -30.37, 181.38}, 
+{'c', -30.37, 181.38, -28.73 ,180.00, -30.47, 179.78}, 
+{'f', 1.000000,1.000000,0.804000,1.000000,0,0 }, 
+{'m', -74.79, 183.13, 0, 0, 0, 0}, 
+{'l', -82.45, 181.60, 0, 0, 0, 0}, 
+{'c', -85.05, 176.60, -87.15 ,170.45, -87.15, 170.45}, 
+{'c', -87.15, 170.45, -80.80 ,171.45, -68.30, 174.25}, 
+{'c', -68.30, 174.25, -67.42 ,177.57, -65.95, 183.36}, 
+{'f', 0.938000,0.938000,0.737000,1.000000,0,0 }, 
+{'m', -9.72, 178.47, 0, 0, 0, 0}, 
+{'c', -11.39, 175.96, -12.71 ,174.21, -13.36, 173.80}, 
+{'c', -16.37, 171.92, -12.23 ,172.29, -11.10, 172.29}, 
+{'c', -11.10, 172.29, 5.47 ,172.29, 7.36, 173.05}, 
+{'c', 7.36, 173.05, 7.88 ,175.29, 8.56, 178.68}, 
+{'f', 0.938000,0.938000,0.737000,1.000000,0,0 }, 
+{'m', 43.88, 40.32, 0, 0, 0, 0}, 
+{'c', 71.60, 44.28, 97.12 ,8.64, 98.88, -1.04}, 
+{'c', 100.64, -10.72, 90.52 ,-22.60, 90.52, -22.60}, 
+{'c', 91.84, -25.68, 87.00 ,-39.76, 81.72, -49.00}, 
+{'c', 76.44, -58.24, 60.54 ,-57.27, 43.00, -58.24}, 
+{'c', 27.16, -59.12, 8.68 ,-35.80, 7.36, -34.04}, 
+{'c', 6.04, -32.28, 12.20 ,6.00, 13.52, 11.72}, 
+{'c', 14.84, 17.44, 12.20 ,43.84, 12.20, 43.84}, 
+{'f', 0.804000,0.469000,0.134000,1.000000,0,0 }, 
+{'m', 8.09, -33.39, 0, 0, 0, 0}, 
+{'c', 6.79, -31.66, 12.84 ,5.92, 14.14, 11.54}, 
+{'c', 15.43, 17.15, 12.84 ,43.07, 12.84, 43.07}, 
+{'c', 45.51, 34.19, 16.73 ,35.73, 43.94, 39.62}, 
+{'c', 71.16, 43.51, 96.22 ,8.51, 97.94, -0.99}, 
+{'c', 99.67, -10.50, 89.74 ,-22.16, 89.74, -22.16}, 
+{'c', 91.03, -25.18, 86.28 ,-39.01, 81.10, -48.08}, 
+{'c', 75.91, -57.15, 60.30 ,-56.20, 43.08, -57.15}, 
+{'f', 0.938000,0.536000,0.335000,1.000000,0,0 }, 
+{'m', 8.82, -32.74, 0, 0, 0, 0}, 
+{'c', 7.54, -31.05, 13.48 ,5.84, 14.75, 11.35}, 
+{'c', 16.02, 16.86, 13.48 ,42.30, 13.48, 42.30}, 
+{'c', 44.88, 33.15, 17.30 ,35.10, 44.01, 38.91}, 
+{'c', 70.72, 42.73, 95.31 ,8.38, 97.01, -0.94}, 
+{'c', 98.70, -10.27, 88.95 ,-21.72, 88.95, -21.72}, 
+{'c', 90.22, -24.69, 85.56 ,-38.26, 80.47, -47.16}, 
+{'c', 75.39, -56.06, 60.06 ,-55.12, 43.16, -56.06}, 
+{'f', 0.938000,0.670000,0.469000,1.000000,0,0 }, 
+{'m', 9.54, -32.10, 0, 0, 0, 0}, 
+{'c', 8.30, -30.43, 14.12 ,5.76, 15.37, 11.17}, 
+{'c', 16.62, 16.58, 14.12 ,41.54, 14.12, 41.54}, 
+{'c', 43.56, 32.50, 17.86 ,34.47, 44.07, 38.21}, 
+{'c', 70.28, 41.95, 94.41 ,8.26, 96.07, -0.90}, 
+{'c', 97.74, -10.05, 88.17 ,-21.28, 88.17, -21.28}, 
+{'c', 89.42, -24.19, 84.84 ,-37.50, 79.85, -46.24}, 
+{'c', 74.86, -54.98, 59.82 ,-54.05, 43.24, -54.98}, 
+{'f', 1.000000,0.804000,0.670000,1.000000,0,0 }, 
+{'m', 10.27, -31.45, 0, 0, 0, 0}, 
+{'c', 9.05, -29.82, 14.76 ,5.68, 15.98, 10.98}, 
+{'c', 17.21, 16.29, 14.76 ,40.77, 14.76, 40.77}, 
+{'c', 42.63, 31.85, 18.43 ,33.83, 44.14, 37.51}, 
+{'c', 69.84, 41.18, 93.50 ,8.13, 95.14, -0.85}, 
+{'c', 96.77, -9.82, 87.39 ,-20.84, 87.39, -20.84}, 
+{'c', 88.61, -23.70, 84.12 ,-36.75, 79.22, -45.32}, 
+{'c', 74.33, -53.89, 59.59 ,-52.98, 43.32, -53.89}, 
+{'f', 1.000000,0.938000,0.871000,1.000000,0,0 }, 
+{'m', 44.20, 36.80, 0, 0, 0, 0}, 
+{'c', 69.40, 40.40, 92.60 ,8.00, 94.20, -0.80}, 
+{'c', 95.80, -9.60, 86.60 ,-20.40, 86.60, -20.40}, 
+{'c', 87.80, -23.20, 83.40 ,-36.00, 78.60, -44.40}, 
+{'c', 73.80, -52.80, 59.35 ,-51.91, 43.40, -52.80}, 
+{'c', 29.00, -53.60, 12.20 ,-32.40, 11.00, -30.80}, 
+{'c', 9.80, -29.20, 15.40 ,5.60, 16.60, 10.80}, 
+{'c', 17.80, 16.00, 15.40 ,40.00, 15.40, 40.00}, 
+{'f', 1.000000,1.000000,1.000000,1.000000,0,0 }, 
+{'m', 90.60, 2.80, 0, 0, 0, 0}, 
+{'c', 90.60, 2.80, 62.80 ,10.40, 51.20, 8.80}, 
+{'c', 51.20, 8.80, 35.40 ,2.20, 26.60, 24.00}, 
+{'c', 26.60, 24.00, 23.00 ,31.20, 21.00, 33.20}, 
+{'f', 0.804000,0.804000,0.804000,1.000000,0,0 }, 
+{'m', 94.40, 0.60, 0, 0, 0, 0}, 
+{'c', 94.40, 0.60, 65.40 ,12.80, 55.40, 12.40}, 
+{'c', 55.40, 12.40, 39.00 ,7.80, 30.60, 22.40}, 
+{'c', 30.60, 22.40, 22.20 ,31.60, 19.00, 33.20}, 
+{'c', 19.00, 33.20, 18.60 ,34.80, 25.00, 30.80}, 
+{'l', 35.40, 36.00, 0, 0, 0, 0}, 
+{'c', 35.40, 36.00, 50.20 ,45.60, 59.80, 29.60}, 
+{'c', 59.80, 29.60, 63.80 ,18.40, 63.80, 16.40}, 
+{'c', 63.80, 14.40, 85.00 ,8.80, 86.60, 8.40}, 
+{'f', 0.000000,0.000000,0.000000,1.000000,0,0 }, 
+{'m', 47.00, 36.51, 0, 0, 0, 0}, 
+{'c', 40.13, 36.51, 31.75 ,32.65, 31.75, 26.40}, 
+{'c', 31.75, 20.15, 40.13 ,13.89, 47.00, 13.89}, 
+{'c', 53.87, 13.89, 59.45 ,18.95, 59.45, 25.20}, 
+{'f', 0.603000,0.804000,0.201000,1.000000,0,0 }, 
+{'m', 43.38, 19.83, 0, 0, 0, 0}, 
+{'c', 38.53, 20.55, 33.44 ,22.05, 33.51, 21.84}, 
+{'c', 35.05, 17.22, 41.41 ,13.89, 47.00, 13.89}, 
+{'c', 51.30, 13.89, 55.08 ,15.87, 57.32, 18.88}, 
+{'f', 0.402000,0.603000,0.000000,1.000000,0,0 }, 
+{'m', 55.40, 19.60, 0, 0, 0, 0}, 
+{'c', 55.40, 19.60, 51.00 ,16.40, 51.00, 18.60}, 
+{'f', 1.000000,1.000000,1.000000,1.000000,0,0 }, 
+{'m', 45.40, 27.73, 0, 0, 0, 0}, 
+{'c', 42.90, 27.73, 40.88 ,25.70, 40.88, 23.20}, 
+{'c', 40.88, 20.70, 42.90 ,18.68, 45.40, 18.68}, 
+{'c', 47.90, 18.68, 49.93 ,20.70, 49.93, 23.20}, 
+{'f', 0.000000,0.000000,0.000000,1.000000,0,0 }, 
+{'m', -58.60, 14.40, 0, 0, 0, 0}, 
+{'c', -58.60, 14.40, -61.80 ,-6.80, -59.40, -11.20}, 
+{'c', -59.40, -11.20, -48.60 ,-21.20, -49.00, -24.80}, 
+{'c', -49.00, -24.80, -49.40 ,-42.80, -50.60, -43.60}, 
+{'c', -51.80, -44.40, -59.40 ,-50.40, -65.40, -44.00}, 
+{'c', -65.40, -44.00, -75.80 ,-26.00, -75.00, -19.60}, 
+{'l', -75.00, -17.60, 0, 0, 0, 0}, 
+{'c', -75.00, -17.60, -82.60 ,-18.00, -84.20, -16.00}, 
+{'c', -84.20, -16.00, -85.40 ,-10.80, -86.60, -10.40}, 
+{'c', -86.60, -10.40, -89.40 ,-8.00, -87.40, -5.20}, 
+{'c', -87.40, -5.20, -89.40 ,-2.80, -89.00, 1.20}, 
+{'l', -81.40, 5.20, 0, 0, 0, 0}, 
+{'c', -81.40, 5.20, -79.40 ,19.60, -68.60, 24.80}, 
+{'f', 0.804000,0.469000,0.134000,1.000000,0,0 }, 
+{'m', -59.60, 12.56, 0, 0, 0, 0}, 
+{'c', -59.60, 12.56, -62.48 ,-6.52, -60.32, -10.48}, 
+{'c', -60.32, -10.48, -50.60 ,-19.48, -50.96, -22.72}, 
+{'c', -50.96, -22.72, -51.32 ,-38.92, -52.40, -39.64}, 
+{'c', -53.48, -40.36, -60.32 ,-45.76, -65.72, -40.00}, 
+{'c', -65.72, -40.00, -75.08 ,-23.80, -74.36, -18.04}, 
+{'l', -74.36, -16.24, 0, 0, 0, 0}, 
+{'c', -74.36, -16.24, -81.20 ,-16.60, -82.64, -14.80}, 
+{'c', -82.64, -14.80, -83.72 ,-10.12, -84.80, -9.76}, 
+{'c', -84.80, -9.76, -87.32 ,-7.60, -85.52, -5.08}, 
+{'c', -85.52, -5.08, -87.32 ,-2.92, -86.96, 0.68}, 
+{'l', -80.12, 4.28, 0, 0, 0, 0}, 
+{'c', -80.12, 4.28, -78.32 ,17.24, -68.60, 21.92}, 
+{'f', 1.000000,1.000000,1.000000,1.000000,0,0 }, 
+{'m', -51.05, -42.61, 0, 0, 0, 0}, 
+{'c', -52.14, -43.47, -59.63 ,-49.24, -65.48, -43.00}, 
+{'c', -65.48, -43.00, -75.62 ,-25.45, -74.84, -19.21}, 
+{'l', -74.84, -17.26, 0, 0, 0, 0}, 
+{'c', -74.84, -17.26, -82.25 ,-17.65, -83.81, -15.70}, 
+{'c', -83.81, -15.70, -84.98 ,-10.63, -86.15, -10.24}, 
+{'c', -86.15, -10.24, -88.88 ,-7.90, -86.93, -5.17}, 
+{'c', -86.93, -5.17, -88.88 ,-2.83, -88.49, 1.07}, 
+{'l', -81.08, 4.97, 0, 0, 0, 0}, 
+{'c', -81.08, 4.97, -79.13 ,19.01, -68.60, 24.08}, 
+{'c', -63.89, 26.35, -60.80 ,19.79, -58.85, 13.94}, 
+{'c', -58.85, 13.94, -61.97 ,-6.73, -59.63, -11.02}, 
+{'c', -59.63, -11.02, -49.10 ,-20.77, -49.49, -24.28}, 
+{'f', 0.938000,0.603000,0.335000,1.000000,0,0 }, 
+{'m', -51.50, -41.62, 0, 0, 0, 0}, 
+{'c', -52.48, -42.54, -59.86 ,-48.08, -65.56, -42.00}, 
+{'c', -65.56, -42.00, -75.44 ,-24.90, -74.68, -18.82}, 
+{'l', -74.68, -16.92, 0, 0, 0, 0}, 
+{'c', -74.68, -16.92, -81.90 ,-17.30, -83.42, -15.40}, 
+{'c', -83.42, -15.40, -84.56 ,-10.46, -85.70, -10.08}, 
+{'c', -85.70, -10.08, -88.36 ,-7.80, -86.46, -5.14}, 
+{'c', -86.46, -5.14, -88.36 ,-2.86, -87.98, 0.94}, 
+{'l', -80.76, 4.74, 0, 0, 0, 0}, 
+{'c', -80.76, 4.74, -78.86 ,18.42, -68.60, 23.36}, 
+{'c', -64.01, 25.57, -61.00 ,19.18, -59.10, 13.48}, 
+{'c', -59.10, 13.48, -62.14 ,-6.66, -59.86, -10.84}, 
+{'c', -59.86, -10.84, -49.60 ,-20.34, -49.98, -23.76}, 
+{'f', 1.000000,0.737000,0.603000,1.000000,0,0 }, 
+{'m', -51.95, -40.63, 0, 0, 0, 0}, 
+{'c', -52.82, -41.61, -60.09 ,-46.92, -65.64, -41.00}, 
+{'c', -65.64, -41.00, -75.26 ,-24.35, -74.52, -18.43}, 
+{'l', -74.52, -16.58, 0, 0, 0, 0}, 
+{'c', -74.52, -16.58, -81.55 ,-16.95, -83.03, -15.10}, 
+{'c', -83.03, -15.10, -84.14 ,-10.29, -85.25, -9.92}, 
+{'c', -85.25, -9.92, -87.84 ,-7.70, -85.99, -5.11}, 
+{'c', -85.99, -5.11, -87.84 ,-2.89, -87.47, 0.81}, 
+{'l', -80.44, 4.51, 0, 0, 0, 0}, 
+{'c', -80.44, 4.51, -78.59 ,17.83, -68.60, 22.64}, 
+{'c', -64.13, 24.79, -61.20 ,18.57, -59.35, 13.02}, 
+{'c', -59.35, 13.02, -62.31 ,-6.59, -60.09, -10.66}, 
+{'c', -60.09, -10.66, -50.10 ,-19.91, -50.47, -23.24}, 
+{'f', 1.000000,0.871000,0.804000,1.000000,0,0 }, 
+{'m', -59.60, 12.46, 0, 0, 0, 0}, 
+{'c', -59.60, 12.46, -62.48 ,-6.52, -60.32, -10.48}, 
+{'c', -60.32, -10.48, -50.60 ,-19.48, -50.96, -22.72}, 
+{'c', -50.96, -22.72, -51.32 ,-38.92, -52.40, -39.64}, 
+{'c', -53.16, -40.68, -60.32 ,-45.76, -65.72, -40.00}, 
+{'c', -65.72, -40.00, -75.08 ,-23.80, -74.36, -18.04}, 
+{'l', -74.36, -16.24, 0, 0, 0, 0}, 
+{'c', -74.36, -16.24, -81.20 ,-16.60, -82.64, -14.80}, 
+{'c', -82.64, -14.80, -83.72 ,-10.12, -84.80, -9.76}, 
+{'c', -84.80, -9.76, -87.32 ,-7.60, -85.52, -5.08}, 
+{'c', -85.52, -5.08, -87.32 ,-2.92, -86.96, 0.68}, 
+{'l', -80.12, 4.28, 0, 0, 0, 0}, 
+{'c', -80.12, 4.28, -78.32 ,17.24, -68.60, 21.92}, 
+{'f', 1.000000,1.000000,1.000000,1.000000,0,0 }, 
+{'m', -62.70, 6.20, 0, 0, 0, 0}, 
+{'c', -62.70, 6.20, -84.30 ,-4.00, -85.20, -4.80}, 
+{'c', -85.20, -4.80, -76.10 ,3.40, -75.30, 3.40}, 
+{'f', 0.804000,0.804000,0.804000,1.000000,0,0 }, 
+{'m', -79.80, 0.00, 0, 0, 0, 0}, 
+{'c', -79.80, 0.00, -61.40 ,3.60, -61.40, 8.00}, 
+{'c', -61.40, 10.91, -61.64 ,24.33, -67.00, 22.80}, 
+{'f', 0.000000,0.000000,0.000000,1.000000,0,0 }, 
+{'m', -71.40, 3.80, 0, 0, 0, 0}, 
+{'c', -71.40, 3.80, -62.42 ,5.27, -61.40, 8.00}, 
+{'c', -60.80, 9.60, -60.14 ,17.91, -65.60, 19.00}, 
+{'f', 0.603000,0.804000,0.201000,1.000000,0,0 }, 
+{'m', 14.60, 46.35, 0, 0, 0, 0}, 
+{'c', 14.10, 44.61, 15.41 ,44.74, 17.20, 44.20}, 
+{'c', 19.20, 43.60, 31.40 ,39.80, 32.20, 37.20}, 
+{'c', 33.00, 34.60, 46.20 ,39.00, 46.20, 39.00}, 
+{'c', 48.00, 39.80, 52.40 ,42.40, 52.40, 42.40}, 
+{'c', 57.20, 43.60, 63.80 ,44.00, 63.80, 44.00}, 
+{'c', 66.20, 45.00, 69.60 ,47.80, 69.60, 47.80}, 
+{'c', 84.20, 58.00, 96.60 ,50.80, 96.60, 50.80}, 
+{'c', 116.60, 44.20, 110.60 ,27.00, 110.60, 27.00}, 
+{'c', 107.60, 18.00, 110.80 ,14.60, 110.80, 14.60}, 
+{'c', 111.00, 10.80, 118.20 ,17.20, 118.20, 17.20}, 
+{'c', 120.80, 21.40, 121.60 ,26.40, 121.60, 26.40}, 
+{'c', 129.60, 37.60, 126.20 ,19.80, 126.20, 19.80}, 
+{'c', 126.40, 18.80, 123.60 ,15.20, 123.60, 14.00}, 
+{'c', 123.60, 12.80, 121.80 ,9.40, 121.80, 9.40}, 
+{'c', 118.80, 6.00, 121.20 ,-1.00, 121.20, -1.00}, 
+{'c', 123.00, -14.80, 120.80 ,-13.00, 120.80, -13.00}, 
+{'c', 119.60, -14.80, 110.40 ,-4.80, 110.40, -4.80}, 
+{'c', 108.20, -1.40, 102.20 ,0.20, 102.20, 0.20}, 
+{'c', 99.40, 2.00, 96.00 ,0.60, 96.00, 0.60}, 
+{'c', 93.40, 0.20, 87.80 ,7.20, 87.80, 7.20}, 
+{'c', 90.60, 7.00, 93.00 ,11.40, 95.40, 11.60}, 
+{'c', 97.80, 11.80, 99.60 ,9.20, 101.20, 8.60}, 
+{'c', 102.80, 8.00, 105.60 ,13.80, 105.60, 13.80}, 
+{'c', 106.00, 16.40, 100.40 ,21.20, 100.40, 21.20}, 
+{'c', 100.00, 25.80, 98.40 ,24.20, 98.40, 24.20}, 
+{'c', 95.40, 23.60, 94.20 ,27.40, 93.20, 32.00}, 
+{'c', 92.20, 36.60, 88.00 ,37.00, 88.00, 37.00}, 
+{'c', 86.40, 44.40, 85.20 ,41.40, 85.20, 41.40}, 
+{'c', 85.00, 35.80, 79.00 ,41.60, 79.00, 41.60}, 
+{'c', 77.80, 43.60, 73.20 ,41.40, 73.20, 41.40}, 
+{'c', 66.40, 39.40, 68.80 ,37.40, 68.80, 37.40}, 
+{'c', 70.60, 35.20, 81.80 ,37.40, 81.80, 37.40}, 
+{'c', 84.00, 35.80, 76.00 ,31.80, 76.00, 31.80}, 
+{'c', 75.40, 30.00, 76.40 ,25.60, 76.40, 25.60}, 
+{'c', 77.60, 22.40, 84.40 ,16.80, 84.40, 16.80}, 
+{'c', 93.80, 15.60, 91.00 ,14.00, 91.00, 14.00}, 
+{'c', 84.80, 8.80, 79.00 ,16.40, 79.00, 16.40}, 
+{'c', 76.80, 22.60, 59.40 ,37.60, 59.40, 37.60}, 
+{'c', 54.60, 41.00, 57.20 ,34.20, 53.20, 37.60}, 
+{'c', 49.20, 41.00, 28.60 ,32.00, 28.60, 32.00}, 
+{'c', 17.04, 30.81, 14.31 ,46.55, 10.78, 43.43}, 
+{'f', 0.000000,0.000000,0.000000,1.000000,0,0 }, 
+{'m', 209.40, -120.00, 0, 0, 0, 0}, 
+{'c', 209.40, -120.00, 183.80 ,-112.00, 181.00, -93.20}, 
+{'c', 181.00, -93.20, 178.60 ,-70.40, 199.00, -52.80}, 
+{'c', 199.00, -52.80, 199.40 ,-46.40, 201.40, -43.20}, 
+{'c', 201.40, -43.20, 199.80 ,-38.40, 218.60, -46.00}, 
+{'l', 245.80, -54.40, 0, 0, 0, 0}, 
+{'c', 245.80, -54.40, 252.20 ,-56.80, 257.40, -65.60}, 
+{'c', 262.60, -74.40, 277.80 ,-93.20, 274.20, -118.40}, 
+{'c', 274.20, -118.40, 275.40 ,-129.60, 269.40, -130.00}, 
+{'c', 269.40, -130.00, 261.00 ,-131.60, 253.80, -124.00}, 
+{'c', 253.80, -124.00, 247.00 ,-120.80, 244.60, -121.20}, 
+{'f', 0.000000,0.000000,0.000000,1.000000,0,0 }, 
+{'m', 264.02, -120.99, 0, 0, 0, 0}, 
+{'c', 264.02, -120.99, 266.12 ,-129.92, 261.28, -125.08}, 
+{'c', 261.28, -125.08, 254.24 ,-119.36, 246.76, -119.36}, 
+{'c', 246.76, -119.36, 232.24 ,-117.16, 227.84, -103.96}, 
+{'c', 227.84, -103.96, 223.88 ,-77.12, 231.80, -71.40}, 
+{'c', 231.80, -71.40, 236.64 ,-63.92, 243.68, -70.52}, 
+{'f', 0.000000,0.000000,0.000000,1.000000,0,0 }, 
+{'m', 263.65, -120.63, 0, 0, 0, 0}, 
+{'c', 263.65, -120.63, 265.74 ,-129.38, 260.99, -124.62}, 
+{'c', 260.99, -124.62, 254.07 ,-119.01, 246.73, -119.01}, 
+{'c', 246.73, -119.01, 232.47 ,-116.85, 228.15, -103.89}, 
+{'c', 228.15, -103.89, 224.26 ,-77.54, 232.04, -71.92}, 
+{'c', 232.04, -71.92, 236.79 ,-64.58, 243.71, -71.06}, 
+{'f', 0.201000,0.201000,0.201000,1.000000,0,0 }, 
+{'m', 263.27, -120.27, 0, 0, 0, 0}, 
+{'c', 263.27, -120.27, 265.35 ,-128.83, 260.69, -124.17}, 
+{'c', 260.69, -124.17, 253.91 ,-118.66, 246.70, -118.66}, 
+{'c', 246.70, -118.66, 232.71 ,-116.54, 228.47, -103.82}, 
+{'c', 228.47, -103.82, 224.65 ,-77.95, 232.28, -72.44}, 
+{'c', 232.28, -72.44, 236.94 ,-65.23, 243.73, -71.59}, 
+{'f', 0.402000,0.402000,0.402000,1.000000,0,0 }, 
+{'m', 262.90, -119.92, 0, 0, 0, 0}, 
+{'c', 262.90, -119.92, 264.97 ,-128.29, 260.39, -123.71}, 
+{'c', 260.39, -123.71, 253.74 ,-118.30, 246.66, -118.30}, 
+{'c', 246.66, -118.30, 232.94 ,-116.22, 228.78, -103.74}, 
+{'c', 228.78, -103.74, 225.03 ,-78.37, 232.52, -72.96}, 
+{'c', 232.52, -72.96, 237.10 ,-65.89, 243.75, -72.13}, 
+{'f', 0.603000,0.603000,0.603000,1.000000,0,0 }, 
+{'m', 262.53, -119.56, 0, 0, 0, 0}, 
+{'c', 262.53, -119.56, 264.59 ,-127.74, 260.10, -123.26}, 
+{'c', 260.10, -123.26, 253.57 ,-117.95, 246.63, -117.95}, 
+{'c', 246.63, -117.95, 233.17 ,-115.91, 229.09, -103.67}, 
+{'c', 229.09, -103.67, 225.42 ,-78.78, 232.76, -73.48}, 
+{'c', 232.76, -73.48, 237.25 ,-66.54, 243.78, -72.66}, 
+{'f', 0.804000,0.804000,0.804000,1.000000,0,0 }, 
+{'m', 262.15, -119.20, 0, 0, 0, 0}, 
+{'c', 262.15, -119.20, 264.20 ,-127.20, 259.80, -122.80}, 
+{'c', 259.80, -122.80, 253.40 ,-117.60, 246.60, -117.60}, 
+{'c', 246.60, -117.60, 233.40 ,-115.60, 229.40, -103.60}, 
+{'c', 229.40, -103.60, 225.80 ,-79.20, 233.00, -74.00}, 
+{'c', 233.00, -74.00, 237.40 ,-67.20, 243.80, -73.20}, 
+{'f', 1.000000,1.000000,1.000000,1.000000,0,0 }, 
+{'m', 50.60, 84.00, 0, 0, 0, 0}, 
+{'c', 50.60, 84.00, 30.20 ,64.80, 22.20, 64.00}, 
+{'c', 22.20, 64.00, -12.20 ,60.00, -27.00, 78.00}, 
+{'c', -27.00, 78.00, -9.40 ,57.60, 18.20, 63.20}, 
+{'c', 18.20, 63.20, -3.40 ,58.80, -15.80, 62.00}, 
+{'c', -15.80, 62.00, -32.60 ,62.00, -42.20, 76.00}, 
+{'l', -45.00, 80.80, 0, 0, 0, 0}, 
+{'c', -45.00, 80.80, -41.00 ,66.00, -22.60, 60.00}, 
+{'c', -22.60, 60.00, 0.20 ,55.20, 11.00, 60.00}, 
+{'c', 11.00, 60.00, -10.60 ,53.20, -20.60, 55.20}, 
+{'c', -20.60, 55.20, -51.00 ,52.80, -63.80, 79.20}, 
+{'c', -63.80, 79.20, -59.80 ,64.80, -45.00, 57.60}, 
+{'c', -45.00, 57.60, -31.40 ,48.80, -11.00, 51.60}, 
+{'c', -11.00, 51.60, 3.40 ,54.80, 8.60, 57.20}, 
+{'c', 13.80, 59.60, 12.60 ,56.80, 4.20, 52.00}, 
+{'c', 4.20, 52.00, -1.40 ,42.00, -15.40, 42.40}, 
+{'c', -15.40, 42.40, -58.20 ,46.00, -68.60, 58.00}, 
+{'c', -68.60, 58.00, -55.00 ,46.80, -44.60, 44.00}, 
+{'c', -44.60, 44.00, -22.20 ,36.00, -13.80, 36.80}, 
+{'c', -13.80, 36.80, 11.00 ,37.80, 18.60, 33.80}, 
+{'c', 18.60, 33.80, 7.40 ,38.80, 10.60, 42.00}, 
+{'c', 13.80, 45.20, 20.60 ,52.80, 20.60, 54.00}, 
+{'c', 20.60, 55.20, 44.80 ,77.30, 48.40, 81.70}, 
+{'f', 0.603000,0.134000,0.000000,1.000000,0,0 }, 
+{'m', 189.00, 278.00, 0, 0, 0, 0}, 
+{'c', 189.00, 278.00, 173.50 ,241.50, 161.00, 232.00}, 
+{'c', 161.00, 232.00, 187.00 ,248.00, 190.50, 266.00}, 
+{'f', 0.804000,0.804000,0.804000,1.000000,0,0 }, 
+{'m', 236.00, 285.50, 0, 0, 0, 0}, 
+{'c', 236.00, 285.50, 209.50 ,230.50, 191.00, 206.50}, 
+{'c', 191.00, 206.50, 234.50 ,244.00, 239.50, 270.50}, 
+{'l', 240.00, 276.00, 0, 0, 0, 0}, 
+{'l', 237.00, 273.50, 0, 0, 0, 0}, 
+{'f', 0.804000,0.804000,0.804000,1.000000,0,0 }, 
+{'m', 292.50, 237.00, 0, 0, 0, 0}, 
+{'c', 292.50, 237.00, 230.00 ,177.50, 228.50, 175.00}, 
+{'c', 228.50, 175.00, 289.00 ,241.00, 292.00, 248.50}, 
+{'f', 0.804000,0.804000,0.804000,1.000000,0,0 }, 
+{'m', 104.00, 280.50, 0, 0, 0, 0}, 
+{'c', 104.00, 280.50, 123.50 ,228.50, 142.50, 251.00}, 
+{'c', 142.50, 251.00, 157.50 ,261.00, 157.00, 264.00}, 
+{'c', 157.00, 264.00, 153.00 ,257.50, 135.00, 258.00}, 
+{'f', 0.804000,0.804000,0.804000,1.000000,0,0 }, 
+{'m', 294.50, 153.00, 0, 0, 0, 0}, 
+{'c', 294.50, 153.00, 249.50 ,124.50, 242.00, 123.00}, 
+{'c', 230.19, 120.64, 291.50 ,152.00, 296.50, 162.50}, 
+{'f', 0.804000,0.804000,0.804000,1.000000,0,0 }, 
+{'m', 143.80, 259.60, 0, 0, 0, 0}, 
+{'c', 143.80, 259.60, 164.20 ,257.60, 171.00, 250.80}, 
+{'l', 175.40, 254.40, 0, 0, 0, 0}, 
+{'l', 193.00, 216.00, 0, 0, 0, 0}, 
+{'l', 196.60, 221.20, 0, 0, 0, 0}, 
+{'c', 196.60, 221.20, 211.00 ,206.40, 210.20, 198.40}, 
+{'c', 209.40, 190.40, 223.00 ,204.40, 223.00, 204.40}, 
+{'c', 223.00, 204.40, 222.20 ,192.80, 229.40, 199.60}, 
+{'c', 229.40, 199.60, 227.00 ,184.00, 235.40, 192.00}, 
+{'c', 235.40, 192.00, 224.86 ,161.84, 247.40, 187.60}, 
+{'c', 253.00, 194.00, 248.60 ,187.20, 248.60, 187.20}, 
+{'c', 248.60, 187.20, 222.60 ,139.20, 244.20, 153.60}, 
+{'c', 244.20, 153.60, 246.20 ,130.80, 245.00, 126.40}, 
+{'c', 243.80, 122.00, 241.80 ,99.60, 237.00, 94.40}, 
+{'c', 232.20, 89.20, 237.40 ,87.60, 243.00, 92.80}, 
+{'c', 243.00, 92.80, 231.80 ,68.80, 245.00, 80.80}, 
+{'c', 245.00, 80.80, 241.40 ,65.60, 237.00, 62.80}, 
+{'c', 237.00, 62.80, 231.40 ,45.60, 246.60, 56.40}, 
+{'c', 246.60, 56.40, 242.20 ,44.00, 239.00, 40.80}, 
+{'c', 239.00, 40.80, 227.40 ,13.20, 234.60, 18.00}, 
+{'l', 239.00, 21.60, 0, 0, 0, 0}, 
+{'c', 239.00, 21.60, 232.20 ,7.60, 238.60, 12.00}, 
+{'c', 245.00, 16.40, 245.00 ,16.00, 245.00, 16.00}, 
+{'c', 245.00, 16.00, 223.80 ,-17.20, 244.20, 0.40}, 
+{'c', 244.20, 0.40, 236.04 ,-13.52, 232.60, -20.40}, 
+{'c', 232.60, -20.40, 213.80 ,-40.80, 228.20, -34.40}, 
+{'l', 233.00, -32.80, 0, 0, 0, 0}, 
+{'c', 233.00, -32.80, 224.20 ,-42.80, 216.20, -44.40}, 
+{'c', 208.20, -46.00, 218.60 ,-52.40, 225.00, -50.40}, 
+{'c', 231.40, -48.40, 247.00 ,-40.80, 247.00, -40.80}, 
+{'c', 247.00, -40.80, 259.80 ,-22.00, 263.80, -21.60}, 
+{'c', 263.80, -21.60, 243.80 ,-29.20, 249.80, -21.20}, 
+{'c', 249.80, -21.20, 264.20 ,-7.20, 257.00, -7.60}, 
+{'c', 257.00, -7.60, 251.00 ,-0.40, 255.80, 8.40}, 
+{'c', 255.80, 8.40, 237.34 ,-9.99, 252.20, 15.60}, 
+{'l', 259.00, 32.00, 0, 0, 0, 0}, 
+{'c', 259.00, 32.00, 234.60 ,7.20, 245.80, 29.20}, 
+{'c', 245.80, 29.20, 263.00 ,52.80, 265.00, 53.20}, 
+{'c', 267.00, 53.60, 271.40 ,62.40, 271.40, 62.40}, 
+{'l', 267.00, 60.40, 0, 0, 0, 0}, 
+{'l', 272.20, 69.20, 0, 0, 0, 0}, 
+{'c', 272.20, 69.20, 261.00 ,57.20, 267.00, 70.40}, 
+{'l', 272.60, 84.80, 0, 0, 0, 0}, 
+{'c', 272.60, 84.80, 252.20 ,62.80, 265.80, 92.40}, 
+{'c', 265.80, 92.40, 249.40 ,87.20, 258.20, 104.40}, 
+{'c', 258.20, 104.40, 256.60 ,120.40, 257.00, 125.60}, 
+{'c', 257.40, 130.80, 258.60 ,159.20, 254.20, 167.20}, 
+{'c', 249.80, 175.20, 260.20 ,194.40, 262.20, 198.40}, 
+{'c', 264.20, 202.40, 267.80 ,213.20, 259.00, 204.00}, 
+{'c', 250.20, 194.80, 254.60 ,200.40, 256.60, 209.20}, 
+{'c', 258.60, 218.00, 264.60 ,233.60, 263.80, 239.20}, 
+{'c', 263.80, 239.20, 262.60 ,240.40, 259.40, 236.80}, 
+{'c', 259.40, 236.80, 244.60 ,214.00, 246.20, 228.40}, 
+{'c', 246.20, 228.40, 245.00 ,236.40, 241.80, 245.20}, 
+{'c', 241.80, 245.20, 238.60 ,256.00, 238.60, 247.20}, 
+{'c', 238.60, 247.20, 235.40 ,230.40, 232.60, 238.00}, 
+{'c', 229.80, 245.60, 226.20 ,251.60, 223.40, 254.00}, 
+{'c', 220.60, 256.40, 215.40 ,233.60, 214.20, 244.00}, 
+{'c', 214.20, 244.00, 202.20 ,231.60, 197.40, 248.00}, 
+{'l', 185.80, 264.40, 0, 0, 0, 0}, 
+{'c', 185.80, 264.40, 185.40 ,252.00, 184.20, 258.00}, 
+{'f', 0.000000,0.000000,0.000000,1.000000,0,0 }, 
+{'m', 109.40, -97.20, 0, 0, 0, 0}, 
+{'c', 109.40, -97.20, 97.80 ,-105.20, 93.80, -104.80}, 
+{'c', 89.80, -104.40, 121.40 ,-113.60, 162.60, -86.00}, 
+{'c', 162.60, -86.00, 167.40 ,-83.20, 171.00, -83.60}, 
+{'c', 171.00, -83.60, 174.20 ,-81.20, 171.40, -77.60}, 
+{'c', 171.40, -77.60, 162.60 ,-68.00, 173.80, -56.80}, 
+{'c', 173.80, -56.80, 192.20 ,-50.00, 186.60, -58.80}, 
+{'c', 186.60, -58.80, 197.40 ,-54.80, 199.80, -50.80}, 
+{'c', 202.20, -46.80, 201.00 ,-50.80, 201.00, -50.80}, 
+{'c', 201.00, -50.80, 194.60 ,-58.00, 188.60, -63.20}, 
+{'c', 188.60, -63.20, 183.40 ,-65.20, 180.60, -73.60}, 
+{'c', 177.80, -82.00, 175.40 ,-92.00, 179.80, -95.20}, 
+{'c', 179.80, -95.20, 175.80 ,-90.80, 176.60, -94.80}, 
+{'c', 177.40, -98.80, 181.00 ,-102.40, 182.60, -102.80}, 
+{'c', 184.20, -103.20, 200.60 ,-119.00, 207.40, -119.40}, 
+{'c', 207.40, -119.40, 198.20 ,-118.00, 195.20, -119.00}, 
+{'c', 192.20, -120.00, 165.60 ,-131.40, 159.60, -132.60}, 
+{'c', 159.60, -132.60, 142.80 ,-139.20, 154.80, -137.20}, 
+{'c', 154.80, -137.20, 190.60 ,-133.40, 208.80, -120.20}, 
+{'c', 208.80, -120.20, 201.60 ,-128.60, 183.20, -135.60}, 
+{'c', 183.20, -135.60, 161.00 ,-148.20, 125.80, -143.20}, 
+{'c', 125.80, -143.20, 108.00 ,-140.00, 100.20, -138.20}, 
+{'c', 100.20, -138.20, 97.60 ,-138.80, 97.00, -139.20}, 
+{'c', 96.40, -139.60, 84.60 ,-148.60, 57.00, -141.60}, 
+{'c', 57.00, -141.60, 40.00 ,-137.00, 31.40, -132.20}, 
+{'c', 31.40, -132.20, 16.20 ,-131.00, 12.60, -127.80}, 
+{'c', 12.60, -127.80, -6.00 ,-113.20, -8.00, -112.40}, 
+{'c', -10.00, -111.60, -21.40 ,-104.00, -22.20, -103.60}, 
+{'c', -22.20, -103.60, 2.40 ,-110.20, 4.80, -112.60}, 
+{'c', 7.20, -115.00, 24.60 ,-117.60, 27.00, -116.20}, 
+{'c', 29.40, -114.80, 37.80 ,-115.40, 28.20, -114.80}, 
+{'c', 28.20, -114.80, 103.80 ,-100.00, 104.60, -98.00}, 
+{'f', 0.000000,0.000000,0.000000,1.000000,0,0 }, 
+{'m', 180.80, -106.40, 0, 0, 0, 0}, 
+{'c', 180.80, -106.40, 170.60 ,-113.80, 168.60, -113.80}, 
+{'c', 166.60, -113.80, 154.20 ,-124.00, 150.00, -123.60}, 
+{'c', 145.80, -123.20, 133.60 ,-133.20, 106.20, -125.00}, 
+{'c', 106.20, -125.00, 105.60 ,-127.00, 109.20, -127.80}, 
+{'c', 109.20, -127.80, 115.60 ,-130.00, 116.00, -130.60}, 
+{'c', 116.00, -130.60, 136.20 ,-134.80, 143.40, -131.20}, 
+{'c', 143.40, -131.20, 152.60 ,-128.60, 158.80, -122.40}, 
+{'c', 158.80, -122.40, 170.00 ,-119.20, 173.20, -120.20}, 
+{'c', 173.20, -120.20, 182.00 ,-118.00, 182.40, -116.20}, 
+{'c', 182.40, -116.20, 188.20 ,-113.20, 186.40, -110.60}, 
+{'f', 0.804000,0.469000,0.134000,1.000000,0,0 }, 
+{'m', 168.33, -108.51, 0, 0, 0, 0}, 
+{'c', 169.14, -107.88, 170.16 ,-107.78, 170.76, -106.97}, 
+{'c', 171.00, -106.66, 170.71 ,-106.33, 170.39, -106.23}, 
+{'c', 169.35, -105.92, 168.29 ,-106.49, 167.15, -105.90}, 
+{'c', 166.75, -105.69, 166.11 ,-105.87, 165.55, -106.02}, 
+{'c', 163.92, -106.46, 162.09 ,-106.49, 160.40, -105.80}, 
+{'c', 158.42, -106.93, 156.06 ,-106.34, 153.97, -107.35}, 
+{'c', 153.92, -107.37, 153.69 ,-107.03, 153.62, -107.05}, 
+{'c', 150.57, -108.20, 146.83 ,-107.92, 144.40, -110.20}, 
+{'c', 141.97, -110.61, 139.62 ,-111.07, 137.19, -111.75}, 
+{'c', 135.37, -112.26, 133.96 ,-113.25, 132.34, -114.08}, 
+{'c', 130.96, -114.79, 129.51 ,-115.31, 127.97, -115.69}, 
+{'c', 126.11, -116.14, 124.28 ,-116.03, 122.39, -116.55}, 
+{'c', 122.29, -116.57, 122.10 ,-116.23, 122.02, -116.25}, 
+{'c', 121.69, -116.36, 121.41 ,-116.94, 121.23, -116.89}, 
+{'c', 119.55, -116.37, 118.06 ,-117.34, 116.40, -117.00}, 
+{'c', 115.22, -118.22, 113.50 ,-117.98, 111.95, -118.42}, 
+{'c', 108.98, -119.27, 105.83 ,-118.00, 102.80, -119.00}, 
+{'c', 106.91, -120.84, 111.60 ,-119.61, 115.66, -121.68}, 
+{'c', 117.99, -122.86, 120.65 ,-121.76, 123.22, -122.52}, 
+{'c', 123.71, -122.67, 124.40 ,-122.87, 124.80, -122.20}, 
+{'c', 124.94, -122.33, 125.12 ,-122.57, 125.17, -122.55}, 
+{'c', 127.62, -121.39, 129.94 ,-120.11, 132.42, -119.05}, 
+{'c', 132.76, -118.90, 133.29 ,-119.14, 133.55, -118.93}, 
+{'c', 135.07, -117.72, 137.01 ,-117.82, 138.40, -116.60}, 
+{'c', 140.10, -117.10, 141.89 ,-116.72, 143.62, -117.35}, 
+{'c', 143.70, -117.37, 143.93 ,-117.03, 143.97, -117.05}, 
+{'c', 145.09, -117.80, 146.25 ,-117.53, 147.14, -117.23}, 
+{'c', 147.48, -117.11, 148.14 ,-116.86, 148.45, -116.79}, 
+{'c', 149.57, -116.52, 150.43 ,-116.03, 151.61, -115.85}, 
+{'c', 151.72, -115.83, 151.91 ,-116.17, 151.98, -116.15}, 
+{'c', 153.10, -115.71, 154.15 ,-115.76, 154.80, -114.60}, 
+{'c', 154.94, -114.73, 155.10 ,-114.97, 155.18, -114.95}, 
+{'c', 156.21, -114.61, 156.86 ,-113.85, 157.96, -113.61}, 
+{'c', 158.44, -113.51, 159.06 ,-112.88, 159.63, -112.70}, 
+{'c', 162.03, -111.97, 163.87 ,-110.44, 166.06, -109.55}, 
+{'f', 0.804000,0.469000,0.134000,1.000000,0,0 }, 
+{'m', 91.70, -122.74, 0, 0, 0, 0}, 
+{'c', 89.18, -124.46, 86.81 ,-125.57, 84.37, -127.36}, 
+{'c', 84.19, -127.49, 83.83 ,-127.32, 83.62, -127.44}, 
+{'c', 82.62, -128.05, 81.73 ,-128.63, 80.75, -129.33}, 
+{'c', 80.21, -129.71, 79.39 ,-129.70, 78.88, -129.96}, 
+{'c', 76.34, -131.25, 73.71 ,-131.81, 71.20, -133.00}, 
+{'c', 71.88, -133.64, 73.00 ,-133.39, 73.60, -134.20}, 
+{'c', 73.80, -133.92, 74.03 ,-133.64, 74.39, -133.83}, 
+{'c', 76.06, -134.73, 77.91 ,-134.88, 79.59, -134.79}, 
+{'c', 81.29, -134.70, 83.01 ,-134.40, 84.79, -134.12}, 
+{'c', 85.10, -134.08, 85.30 ,-133.56, 85.62, -133.46}, 
+{'c', 87.85, -132.79, 90.23 ,-133.32, 92.35, -132.48}, 
+{'c', 93.94, -131.85, 95.52 ,-131.03, 96.75, -129.75}, 
+{'c', 97.01, -129.50, 96.68 ,-129.19, 96.40, -129.00}, 
+{'c', 96.79, -129.11, 97.06 ,-128.90, 97.17, -128.59}, 
+{'c', 97.26, -128.35, 97.26 ,-128.05, 97.17, -127.81}, 
+{'c', 97.06, -127.50, 96.78 ,-127.40, 96.41, -127.35}, 
+{'c', 95.00, -127.16, 96.77 ,-128.54, 96.07, -128.09}, 
+{'c', 94.80, -127.27, 95.55 ,-125.87, 94.80, -124.60}, 
+{'c', 94.52, -124.79, 94.29 ,-125.01, 94.40, -125.40}, 
+{'c', 94.64, -124.88, 94.03 ,-124.59, 93.86, -124.27}, 
+{'f', 0.804000,0.469000,0.134000,1.000000,0,0 }, 
+{'m', 59.20, -115.39, 0, 0, 0, 0}, 
+{'c', 56.04, -116.19, 52.99 ,-116.07, 49.98, -117.35}, 
+{'c', 49.91, -117.37, 49.69 ,-117.03, 49.62, -117.05}, 
+{'c', 48.26, -117.65, 47.34 ,-118.61, 46.26, -119.66}, 
+{'c', 45.35, -120.55, 43.69 ,-120.16, 42.42, -120.65}, 
+{'c', 42.09, -120.77, 41.89 ,-121.28, 41.59, -121.32}, 
+{'c', 40.37, -121.48, 39.45 ,-122.43, 38.40, -123.00}, 
+{'c', 40.74, -123.80, 43.15 ,-123.76, 45.61, -124.15}, 
+{'c', 45.72, -124.17, 45.87 ,-123.84, 46.00, -123.84}, 
+{'c', 46.14, -123.84, 46.27 ,-124.07, 46.40, -124.20}, 
+{'c', 46.59, -123.92, 46.90 ,-123.59, 47.15, -123.85}, 
+{'c', 47.70, -124.39, 48.26 ,-124.20, 48.80, -124.16}, 
+{'c', 48.94, -124.15, 49.07 ,-123.84, 49.20, -123.84}, 
+{'c', 49.34, -123.84, 49.47 ,-124.16, 49.60, -124.16}, 
+{'c', 49.74, -124.16, 49.87 ,-123.84, 50.00, -123.84}, 
+{'c', 50.14, -123.84, 50.27 ,-124.07, 50.40, -124.20}, 
+{'c', 51.09, -123.42, 51.98 ,-123.97, 52.80, -123.79}, 
+{'c', 53.84, -123.57, 54.10 ,-122.42, 55.18, -122.12}, 
+{'c', 59.89, -120.82, 64.03 ,-118.67, 68.39, -116.58}, 
+{'c', 68.70, -116.44, 68.91 ,-116.19, 68.80, -115.80}, 
+{'c', 69.07, -115.80, 69.38 ,-115.89, 69.57, -115.76}, 
+{'c', 70.63, -115.02, 71.67 ,-114.48, 72.37, -113.38}, 
+{'c', 72.58, -113.04, 72.25 ,-112.63, 72.02, -112.68}, 
+{'f', 0.804000,0.469000,0.134000,1.000000,0,0 }, 
+{'m', 45.34, -71.18, 0, 0, 0, 0}, 
+{'c', 43.75, -72.40, 43.16 ,-74.43, 42.03, -76.22}, 
+{'c', 41.82, -76.56, 42.09 ,-76.88, 42.41, -76.96}, 
+{'c', 42.97, -77.12, 43.51 ,-76.64, 43.92, -76.44}, 
+{'c', 45.67, -75.58, 47.20 ,-74.34, 49.20, -74.20}, 
+{'c', 51.19, -71.97, 55.45 ,-71.58, 55.46, -68.20}, 
+{'c', 55.46, -67.34, 54.03 ,-68.26, 53.60, -67.40}, 
+{'c', 51.15, -68.40, 48.76 ,-68.30, 46.38, -69.77}, 
+{'f', 0.804000,0.469000,0.134000,1.000000,0,0 }, 
+{'m', 17.80, -123.76, 0, 0, 0, 0}, 
+{'c', 17.93, -123.75, 24.97 ,-123.52, 24.95, -123.41}, 
+{'c', 24.90, -123.10, 17.17 ,-122.05, 16.81, -122.22}, 
+{'c', 16.65, -122.30, 9.13 ,-119.87, 9.00, -120.00}, 
+{'f', 0.804000,0.469000,0.134000,1.000000,0,0 }, 
+{'m', 33.20, -114.00, 0, 0, 0, 0}, 
+{'c', 33.20, -114.00, 18.40 ,-112.20, 14.00, -111.00}, 
+{'c', 9.60, -109.80, -9.00 ,-102.20, -12.00, -100.20}, 
+{'c', -12.00, -100.20, -25.40 ,-94.80, -42.40, -74.80}, 
+{'c', -42.40, -74.80, -34.80 ,-78.20, -32.60, -81.00}, 
+{'c', -32.60, -81.00, -19.00 ,-93.60, -19.20, -91.00}, 
+{'c', -19.20, -91.00, -7.00 ,-99.60, -7.60, -97.40}, 
+{'c', -7.60, -97.40, 16.80 ,-108.60, 14.80, -105.40}, 
+{'c', 14.80, -105.40, 36.40 ,-110.00, 35.40, -108.00}, 
+{'c', 35.40, -108.00, 54.20 ,-103.60, 51.40, -103.40}, 
+{'c', 51.40, -103.40, 45.60 ,-102.20, 52.00, -98.60}, 
+{'c', 52.00, -98.60, 48.60 ,-94.20, 43.20, -98.20}, 
+{'c', 37.80, -102.20, 40.80 ,-100.00, 35.80, -99.00}, 
+{'c', 35.80, -99.00, 33.20 ,-98.20, 28.60, -102.20}, 
+{'c', 28.60, -102.20, 23.00 ,-106.80, 14.20, -103.20}, 
+{'c', 14.20, -103.20, -16.40 ,-90.60, -18.40, -90.00}, 
+{'c', -18.40, -90.00, -22.00 ,-87.20, -24.40, -83.60}, 
+{'c', -24.40, -83.60, -30.20 ,-79.20, -33.20, -77.80}, 
+{'c', -33.20, -77.80, -46.00 ,-66.20, -47.20, -64.80}, 
+{'c', -47.20, -64.80, -50.60 ,-59.60, -51.40, -59.20}, 
+{'c', -51.40, -59.20, -45.00 ,-63.00, -43.00, -65.00}, 
+{'c', -43.00, -65.00, -29.00 ,-75.00, -23.60, -75.80}, 
+{'c', -23.60, -75.80, -19.20 ,-78.80, -18.40, -80.20}, 
+{'c', -18.40, -80.20, -4.00 ,-89.40, 0.20, -89.40}, 
+{'c', 0.20, -89.40, 9.40 ,-84.20, 11.80, -91.20}, 
+{'c', 11.80, -91.20, 17.60 ,-93.00, 23.20, -91.80}, 
+{'c', 23.20, -91.80, 26.40 ,-94.40, 25.60, -96.60}, 
+{'c', 25.60, -96.60, 27.20 ,-98.40, 28.20, -94.60}, 
+{'c', 28.20, -94.60, 31.60 ,-91.00, 36.40, -93.00}, 
+{'c', 36.40, -93.00, 40.40 ,-93.20, 38.40, -90.80}, 
+{'c', 38.40, -90.80, 34.00 ,-87.00, 22.20, -86.80}, 
+{'c', 22.20, -86.80, 9.80 ,-86.20, -6.60, -78.60}, 
+{'c', -6.60, -78.60, -36.40 ,-68.20, -45.60, -57.80}, 
+{'c', -45.60, -57.80, -52.00 ,-49.00, -57.40, -47.80}, 
+{'c', -57.40, -47.80, -63.20 ,-47.00, -69.20, -39.60}, 
+{'c', -69.20, -39.60, -59.40 ,-45.40, -50.40, -45.40}, 
+{'c', -50.40, -45.40, -46.40 ,-47.80, -50.20, -44.20}, 
+{'c', -50.20, -44.20, -53.80 ,-36.60, -52.20, -31.20}, 
+{'c', -52.20, -31.20, -52.80 ,-26.00, -53.60, -24.40}, 
+{'c', -53.60, -24.40, -61.40 ,-11.60, -61.40, -9.20}, 
+{'c', -61.40, -6.80, -60.20 ,3.00, -59.80, 3.60}, 
+{'c', -59.40, 4.20, -60.80 ,2.00, -57.00, 4.40}, 
+{'c', -53.20, 6.80, -50.40 ,8.40, -49.60, 11.20}, 
+{'c', -48.80, 14.00, -51.60 ,5.80, -51.80, 4.00}, 
+{'c', -52.00, 2.20, -56.20 ,-5.00, -55.40, -7.40}, 
+{'c', -55.40, -7.40, -54.40 ,-6.40, -53.60, -5.00}, 
+{'c', -53.60, -5.00, -54.20 ,-5.60, -53.60, -9.20}, 
+{'c', -53.60, -9.20, -52.80 ,-14.40, -51.40, -17.60}, 
+{'c', -50.00, -20.80, -48.00 ,-24.60, -47.60, -25.40}, 
+{'c', -47.20, -26.20, -47.20 ,-32.00, -45.80, -29.40}, 
+{'l', -42.40, -26.80, 0, 0, 0, 0}, 
+{'c', -42.40, -26.80, -45.20 ,-29.40, -43.00, -31.60}, 
+{'c', -43.00, -31.60, -44.00 ,-37.20, -42.20, -39.80}, 
+{'c', -42.20, -39.80, -35.20 ,-48.20, -33.60, -49.20}, 
+{'c', -32.00, -50.20, -33.40 ,-49.80, -33.40, -49.80}, 
+{'c', -33.40, -49.80, -27.40 ,-54.00, -33.20, -52.40}, 
+{'c', -33.20, -52.40, -37.20 ,-50.80, -40.20, -50.80}, 
+{'c', -40.20, -50.80, -47.80 ,-48.80, -43.80, -53.00}, 
+{'c', -39.80, -57.20, -29.80 ,-62.60, -26.00, -62.40}, 
+{'l', -25.20, -60.80, 0, 0, 0, 0}, 
+{'l', -14.00, -63.20, 0, 0, 0, 0}, 
+{'l', -15.20, -62.40, 0, 0, 0, 0}, 
+{'c', -15.20, -62.40, -15.40 ,-62.60, -11.20, -63.00}, 
+{'c', -7.00, -63.40, -1.20 ,-62.00, 0.20, -63.80}, 
+{'c', 1.60, -65.60, 5.00 ,-66.60, 4.60, -65.20}, 
+{'c', 4.20, -63.80, 4.00 ,-61.80, 4.00, -61.80}, 
+{'c', 4.00, -61.80, 9.00 ,-67.60, 8.40, -65.40}, 
+{'c', 7.80, -63.20, -0.40 ,-58.00, -1.80, -51.80}, 
+{'l', 8.60, -60.00, 0, 0, 0, 0}, 
+{'l', 12.20, -63.00, 0, 0, 0, 0}, 
+{'c', 12.20, -63.00, 15.80 ,-60.80, 16.00, -62.40}, 
+{'c', 16.20, -64.00, 20.80 ,-69.80, 22.00, -69.60}, 
+{'c', 23.20, -69.40, 25.20 ,-72.20, 25.00, -69.60}, 
+{'c', 24.80, -67.00, 32.40 ,-61.60, 32.40, -61.60}, 
+{'c', 32.40, -61.60, 35.60 ,-63.40, 37.00, -62.00}, 
+{'c', 38.40, -60.60, 42.60 ,-81.80, 42.60, -81.80}, 
+{'l', 67.60, -92.40, 0, 0, 0, 0}, 
+{'l', 111.20, -95.80, 0, 0, 0, 0}, 
+{'l', 94.20, -102.60, 0, 0, 0, 0}, 
+{'f', 0.000000,0.000000,0.000000,1.000000,0,0 }, 
+{'m', 51.40, 85.00, 0, 0, 0, 0}, 
+{'c', 51.40, 85.00, 36.40 ,68.20, 28.00, 65.60}, 
+{'f', 0.000000,0.000000,0.000000,1.000000,0,0 }, 
+{'m', 24.80, 64.20, 0, 0, 0, 0}, 
+{'c', 24.80, 64.20, -0.40 ,56.20, -15.80, 60.40}, 
+{'f', 0.000000,0.000000,0.000000,1.000000,0,0 }, 
+{'m', 21.20, 63.00, 0, 0, 0, 0}, 
+{'c', 21.20, 63.00, 4.20 ,55.80, -10.60, 53.60}, 
+{'c', -10.60, 53.60, -27.20 ,51.00, -43.80, 58.20}, 
+{'f', 0.000000,0.000000,0.000000,1.000000,0,0 }, 
+{'m', 22.20, 63.40, 0, 0, 0, 0}, 
+{'c', 22.20, 63.40, 6.80 ,52.40, 5.80, 51.00}, 
+{'c', 5.80, 51.00, -1.20 ,40.00, -14.20, 39.60}, 
+{'f', 0.000000,0.000000,0.000000,1.000000,0,0 }, 
+{'m', 20.89, 54.41, 0, 0, 0, 0}, 
+{'c', 22.44, 55.87, 49.40 ,84.80, 49.40, 84.80}, 
+{'c', 84.60, 121.40, 56.60 ,87.20, 56.60, 87.20}, 
+{'c', 49.00, 82.40, 39.80 ,63.60, 39.80, 63.60}, 
+{'c', 38.60, 60.80, 53.80 ,70.80, 53.80, 70.80}, 
+{'c', 57.80, 71.60, 71.40 ,90.80, 71.40, 90.80}, 
+{'c', 64.60, 88.40, 69.40 ,95.60, 69.40, 95.60}, 
+{'c', 72.20, 97.60, 92.60 ,113.20, 92.60, 113.20}, 
+{'c', 96.20, 117.20, 100.20 ,118.80, 100.20, 118.80}, 
+{'c', 114.20, 113.60, 107.80 ,126.80, 107.80, 126.80}, 
+{'c', 110.20, 133.60, 115.80 ,122.00, 115.80, 122.00}, 
+{'c', 127.00, 105.20, 110.60 ,107.60, 110.60, 107.60}, 
+{'c', 80.60, 110.40, 73.80 ,94.40, 73.80, 94.40}, 
+{'c', 71.40, 92.00, 80.20 ,94.40, 80.20, 94.40}, 
+{'c', 88.60, 96.40, 73.00 ,82.00, 73.00, 82.00}, 
+{'c', 75.40, 82.00, 84.60 ,88.80, 84.60, 88.80}, 
+{'c', 95.00, 98.00, 97.00 ,96.00, 97.00, 96.00}, 
+{'c', 115.00, 87.20, 125.40 ,94.80, 125.40, 94.80}, 
+{'c', 127.40, 96.40, 121.80 ,103.20, 123.40, 108.40}, 
+{'c', 125.00, 113.60, 129.80 ,126.00, 129.80, 126.00}, 
+{'c', 127.40, 127.60, 127.80 ,138.40, 127.80, 138.40}, 
+{'c', 144.60, 161.60, 135.00 ,159.60, 135.00, 159.60}, 
+{'c', 119.40, 159.20, 134.20 ,166.80, 134.20, 166.80}, 
+{'c', 137.40, 168.80, 146.20 ,176.00, 146.20, 176.00}, 
+{'c', 143.40, 174.80, 141.80 ,180.00, 141.80, 180.00}, 
+{'c', 146.60, 184.00, 143.80 ,188.80, 143.80, 188.80}, 
+{'c', 137.80, 190.00, 136.60 ,194.00, 136.60, 194.00}, 
+{'c', 143.40, 202.00, 133.40 ,202.40, 133.40, 202.40}, 
+{'c', 137.00, 206.80, 132.20 ,218.80, 132.20, 218.80}, 
+{'c', 127.40, 218.80, 121.00 ,224.40, 121.00, 224.40}, 
+{'c', 123.40, 229.20, 113.00 ,234.80, 113.00, 234.80}, 
+{'c', 104.60, 236.40, 107.40 ,243.20, 107.40, 243.20}, 
+{'c', 99.40, 249.20, 97.00 ,265.20, 97.00, 265.20}, 
+{'c', 96.20, 275.60, 93.80 ,278.80, 99.00, 276.80}, 
+{'c', 104.20, 274.80, 103.40 ,262.40, 103.40, 262.40}, 
+{'c', 98.60, 246.80, 141.40 ,230.80, 141.40, 230.80}, 
+{'c', 145.40, 229.20, 146.20 ,224.00, 146.20, 224.00}, 
+{'c', 148.20, 224.40, 157.00 ,232.00, 157.00, 232.00}, 
+{'c', 164.60, 243.20, 165.00 ,234.00, 165.00, 234.00}, 
+{'c', 166.20, 230.40, 164.60 ,224.40, 164.60, 224.40}, 
+{'c', 170.60, 202.80, 156.60 ,196.40, 156.60, 196.40}, 
+{'c', 146.60, 162.80, 160.60 ,171.20, 160.60, 171.20}, 
+{'c', 163.40, 176.80, 174.20 ,182.00, 174.20, 182.00}, 
+{'l', 177.80, 179.60, 0, 0, 0, 0}, 
+{'c', 176.20, 174.80, 184.60 ,168.80, 184.60, 168.80}, 
+{'c', 187.40, 175.20, 193.40 ,167.20, 193.40, 167.20}, 
+{'c', 197.00, 142.80, 209.40 ,157.20, 209.40, 157.20}, 
+{'c', 213.40, 158.40, 214.60 ,151.60, 214.60, 151.60}, 
+{'c', 218.20, 141.20, 214.60 ,127.60, 214.60, 127.60}, 
+{'c', 218.20, 127.20, 227.80 ,133.20, 227.80, 133.20}, 
+{'c', 230.60, 129.60, 221.40 ,112.80, 225.40, 115.20}, 
+{'c', 229.40, 117.60, 233.80 ,119.20, 233.80, 119.20}, 
+{'c', 234.60, 117.20, 224.60 ,104.80, 224.60, 104.80}, 
+{'c', 220.20, 102.00, 215.00 ,81.60, 215.00, 81.60}, 
+{'c', 222.20, 85.20, 212.20 ,70.00, 212.20, 70.00}, 
+{'c', 212.20, 66.80, 218.20 ,55.60, 218.20, 55.60}, 
+{'c', 217.40, 48.80, 218.20 ,49.20, 218.20, 49.20}, 
+{'c', 221.00, 50.40, 229.00 ,52.00, 222.20, 45.60}, 
+{'c', 215.40, 39.20, 223.00 ,34.40, 223.00, 34.40}, 
+{'c', 227.40, 31.60, 213.80 ,32.00, 213.80, 32.00}, 
+{'c', 208.60, 27.60, 209.00 ,23.60, 209.00, 23.60}, 
+{'c', 217.00, 25.60, 202.60 ,11.20, 200.20, 7.60}, 
+{'c', 197.80, 4.00, 207.40 ,-1.20, 207.40, -1.20}, 
+{'c', 220.60, -4.80, 209.00 ,-8.00, 209.00, -8.00}, 
+{'c', 189.40, -7.60, 200.20 ,-18.40, 200.20, -18.40}, 
+{'c', 206.20, -18.00, 204.60 ,-20.40, 204.60, -20.40}, 
+{'c', 199.40, -21.60, 189.80 ,-28.00, 189.80, -28.00}, 
+{'c', 185.80, -31.60, 189.40 ,-30.80, 189.40, -30.80}, 
+{'c', 206.20, -29.60, 177.40 ,-40.80, 177.40, -40.80}, 
+{'c', 185.40, -40.80, 167.40 ,-51.20, 167.40, -51.20}, 
+{'c', 165.40, -52.80, 162.20 ,-60.40, 162.20, -60.40}, 
+{'c', 156.20, -65.60, 151.40 ,-72.40, 151.40, -72.40}, 
+{'c', 151.00, -76.80, 146.20 ,-81.60, 146.20, -81.60}, 
+{'c', 134.60, -95.20, 129.00 ,-94.80, 129.00, -94.80}, 
+{'c', 114.20, -98.40, 109.00 ,-97.60, 109.00, -97.60}, 
+{'l', 56.20, -93.20, 0, 0, 0, 0}, 
+{'c', 29.80, -80.40, 37.60 ,-59.40, 37.60, -59.40}, 
+{'c', 44.00, -51.00, 53.20 ,-54.80, 53.20, -54.80}, 
+{'c', 57.80, -61.00, 69.40 ,-58.80, 69.40, -58.80}, 
+{'c', 89.80, -55.60, 87.20 ,-59.20, 87.20, -59.20}, 
+{'c', 84.80, -63.80, 68.60 ,-70.00, 68.40, -70.60}, 
+{'c', 68.20, -71.20, 59.40 ,-74.60, 59.40, -74.60}, 
+{'c', 56.40, -75.80, 52.00 ,-85.00, 52.00, -85.00}, 
+{'c', 48.80, -88.40, 64.60 ,-82.60, 64.60, -82.60}, 
+{'c', 63.40, -81.60, 70.80 ,-77.60, 70.80, -77.60}, 
+{'c', 88.20, -78.60, 98.80 ,-67.80, 98.80, -67.80}, 
+{'c', 109.60, -51.20, 109.80 ,-59.40, 109.80, -59.40}, 
+{'c', 112.60, -68.80, 100.80 ,-90.00, 100.80, -90.00}, 
+{'c', 101.20, -92.00, 109.40 ,-85.40, 109.40, -85.40}, 
+{'c', 110.80, -87.40, 111.60 ,-81.60, 111.60, -81.60}, 
+{'c', 111.80, -79.20, 115.60 ,-71.20, 115.60, -71.20}, 
+{'c', 118.40, -58.20, 122.00 ,-65.60, 122.00, -65.60}, 
+{'l', 126.60, -56.20, 0, 0, 0, 0}, 
+{'c', 128.00, -53.60, 122.00 ,-46.00, 122.00, -46.00}, 
+{'c', 121.80, -43.20, 122.60 ,-43.40, 117.00, -35.80}, 
+{'c', 111.40, -28.20, 114.80 ,-23.80, 114.80, -23.80}, 
+{'c', 113.40, -17.20, 122.20 ,-17.60, 122.20, -17.60}, 
+{'c', 124.80, -15.40, 128.20 ,-15.40, 128.20, -15.40}, 
+{'c', 130.00, -13.40, 132.40 ,-14.00, 132.40, -14.00}, 
+{'c', 134.00, -17.80, 140.20 ,-15.80, 140.20, -15.80}, 
+{'c', 141.60, -18.20, 149.80 ,-18.60, 149.80, -18.60}, 
+{'c', 150.80, -21.20, 151.20 ,-22.80, 154.60, -23.40}, 
+{'c', 158.00, -24.00, 133.40 ,-67.00, 133.40, -67.00}, 
+{'c', 139.80, -67.80, 131.60 ,-80.20, 131.60, -80.20}, 
+{'c', 129.40, -86.80, 140.80 ,-72.20, 143.00, -70.80}, 
+{'c', 145.20, -69.40, 146.20 ,-67.20, 144.60, -67.40}, 
+{'c', 143.00, -67.60, 141.20 ,-65.40, 142.60, -65.20}, 
+{'c', 144.00, -65.00, 157.00 ,-50.00, 160.40, -39.80}, 
+{'c', 163.80, -29.60, 169.80 ,-25.60, 176.00, -19.60}, 
+{'c', 182.20, -13.60, 181.40 ,10.60, 181.40, 10.60}, 
+{'c', 181.00, 19.40, 187.00 ,30.00, 187.00, 30.00}, 
+{'c', 189.00, 33.80, 184.80 ,52.00, 184.80, 52.00}, 
+{'c', 182.80, 54.20, 184.20 ,55.00, 184.20, 55.00}, 
+{'c', 185.20, 56.20, 192.00 ,69.40, 192.00, 69.40}, 
+{'c', 190.20, 69.20, 193.80 ,72.80, 193.80, 72.80}, 
+{'c', 199.00, 78.80, 192.60 ,75.80, 192.60, 75.80}, 
+{'c', 186.60, 74.20, 193.60 ,84.00, 193.60, 84.00}, 
+{'c', 194.80, 85.80, 185.80 ,81.20, 185.80, 81.20}, 
+{'c', 176.60, 80.60, 188.20 ,87.80, 188.20, 87.80}, 
+{'c', 196.80, 95.00, 185.40 ,90.60, 185.40, 90.60}, 
+{'c', 180.80, 88.80, 184.00 ,95.60, 184.00, 95.60}, 
+{'c', 187.20, 97.20, 204.40 ,104.20, 204.40, 104.20}, 
+{'c', 204.80, 108.00, 201.80 ,113.00, 201.80, 113.00}, 
+{'c', 202.20, 117.00, 200.00 ,120.40, 200.00, 120.40}, 
+{'c', 198.80, 128.60, 198.20 ,129.40, 198.20, 129.40}, 
+{'c', 194.00, 129.60, 186.60 ,143.40, 186.60, 143.40}, 
+{'c', 184.80, 146.00, 174.60 ,158.00, 174.60, 158.00}, 
+{'c', 172.60, 165.00, 154.60 ,157.80, 154.60, 157.80}, 
+{'c', 148.00, 161.20, 150.00 ,157.80, 150.00, 157.80}, 
+{'c', 149.60, 155.60, 154.40 ,149.60, 154.40, 149.60}, 
+{'c', 161.40, 147.00, 158.80 ,136.20, 158.80, 136.20}, 
+{'c', 162.80, 134.80, 151.60 ,132.00, 151.80, 130.80}, 
+{'c', 152.00, 129.60, 157.80 ,128.20, 157.80, 128.20}, 
+{'c', 165.80, 126.20, 161.40 ,123.80, 161.40, 123.80}, 
+{'c', 160.80, 119.80, 163.80 ,114.20, 163.80, 114.20}, 
+{'c', 175.40, 113.40, 163.80 ,97.20, 163.80, 97.20}, 
+{'c', 153.00, 89.60, 152.00 ,83.80, 152.00, 83.80}, 
+{'c', 164.60, 75.60, 156.40 ,63.20, 156.60, 59.60}, 
+{'c', 156.80, 56.00, 158.00 ,34.40, 158.00, 34.40}, 
+{'c', 156.00, 28.20, 153.00 ,14.60, 153.00, 14.60}, 
+{'c', 155.20, 9.40, 162.60 ,-3.20, 162.60, -3.20}, 
+{'c', 165.40, -7.40, 174.20 ,-12.20, 172.00, -15.20}, 
+{'c', 169.80, -18.20, 162.00 ,-16.40, 162.00, -16.40}, 
+{'c', 154.20, -17.80, 154.80 ,-12.60, 154.80, -12.60}, 
+{'c', 153.20, -11.60, 152.40 ,-6.60, 152.40, -6.60}, 
+{'c', 151.68, 1.33, 142.80 ,7.60, 142.80, 7.60}, 
+{'c', 131.60, 13.80, 140.80 ,17.80, 140.80, 17.80}, 
+{'c', 146.80, 24.40, 137.00 ,24.60, 137.00, 24.60}, 
+{'c', 126.00, 22.80, 134.20 ,33.00, 134.20, 33.00}, 
+{'c', 145.00, 45.80, 142.00 ,48.60, 142.00, 48.60}, 
+{'c', 131.80, 49.60, 144.40 ,58.80, 144.40, 58.80}, 
+{'c', 144.40, 58.80, 143.60 ,56.80, 143.80, 58.60}, 
+{'c', 144.00, 60.40, 147.00 ,64.60, 147.80, 66.60}, 
+{'c', 148.60, 68.60, 144.60 ,68.80, 144.60, 68.80}, 
+{'c', 145.20, 78.40, 129.80 ,74.20, 129.80, 74.20}, 
+{'c', 129.80, 74.20, 129.80 ,74.20, 128.20, 74.40}, 
+{'c', 126.60, 74.60, 115.40 ,73.80, 109.60, 71.60}, 
+{'c', 103.80, 69.40, 97.00 ,69.40, 97.00, 69.40}, 
+{'c', 97.00, 69.40, 93.00 ,71.20, 85.40, 71.00}, 
+{'c', 77.80, 70.80, 69.80 ,73.60, 69.80, 73.60}, 
+{'c', 65.40, 73.20, 74.00 ,68.80, 74.20, 69.00}, 
+{'c', 74.40, 69.20, 80.00 ,63.60, 72.00, 64.20}, 
+{'c', 50.20, 65.83, 39.40 ,55.60, 39.40, 55.60}, 
+{'c', 37.40, 54.20, 34.80 ,51.40, 34.80, 51.40}, 
+{'c', 24.80, 49.40, 36.20 ,63.80, 36.20, 63.80}, 
+{'c', 37.40, 65.20, 36.00 ,66.20, 36.00, 66.20}, 
+{'c', 35.20, 64.60, 27.40 ,59.20, 27.40, 59.20}, 
+{'f', 0.000000,0.000000,0.000000,1.000000,0,0 }, 
+{'m', -3.00, 42.80, 0, 0, 0, 0}, 
+{'c', -3.00, 42.80, 8.60 ,48.40, 11.20, 51.20}, 
+{'c', 13.80, 54.00, 27.80 ,65.40, 27.80, 65.40}, 
+{'c', 27.80, 65.40, 22.40 ,63.40, 19.80, 61.60}, 
+{'c', 17.20, 59.80, 6.40 ,51.60, 6.40, 51.60}, 
+{'f', 0.268000,0.000000,0.000000,1.000000,0,0 }, 
+{'m', -61.01, 11.60, 0, 0, 0, 0}, 
+{'c', -60.67, 11.46, -61.20 ,8.74, -61.40, 8.20}, 
+{'c', -62.42, 5.47, -71.40 ,4.00, -71.40, 4.00}, 
+{'c', -71.63, 5.37, -71.68 ,6.96, -71.58, 8.60}, 
+{'f', 0.603000,0.804000,0.201000,1.000000,0,0 }, 
+{'m', -61.01, 11.40, 0, 0, 0, 0}, 
+{'c', -61.46, 11.56, -61.02 ,8.67, -61.20, 8.20}, 
+{'c', -62.22, 5.47, -71.40 ,3.90, -71.40, 3.90}, 
+{'c', -71.63, 5.26, -71.68 ,6.86, -71.58, 8.50}, 
+{'f', 0.402000,0.603000,0.000000,1.000000,0,0 }, 
+{'m', -65.40, 11.55, 0, 0, 0, 0}, 
+{'c', -66.03, 11.55, -66.53 ,10.41, -66.53, 9.00}, 
+{'c', -66.53, 7.59, -66.03 ,6.46, -65.40, 6.46}, 
+{'c', -64.78, 6.46, -64.27 ,7.59, -64.27, 9.00}, 
+{'f', 0.000000,0.000000,0.000000,1.000000,0,0 }, 
+{'f', 0.000000,0.000000,0.000000,1.000000,0,0 }, 
+{'m', -111.00, 109.60, 0, 0, 0, 0}, 
+{'c', -111.00, 109.60, -116.60 ,119.60, -91.80, 113.60}, 
+{'c', -91.80, 113.60, -77.80 ,112.40, -75.40, 110.00}, 
+{'c', -74.20, 110.80, -65.83 ,113.73, -63.00, 114.40}, 
+{'c', -56.20, 116.00, -47.80 ,106.00, -47.80, 106.00}, 
+{'c', -47.80, 106.00, -43.20 ,95.50, -40.40, 95.50}, 
+{'c', -37.60, 95.50, -40.80 ,97.10, -40.80, 97.10}, 
+{'c', -40.80, 97.10, -47.40 ,107.20, -47.00, 108.80}, 
+{'c', -47.00, 108.80, -52.20 ,128.80, -68.20, 129.60}, 
+{'c', -68.20, 129.60, -84.35 ,130.55, -83.00, 136.40}, 
+{'c', -83.00, 136.40, -74.20 ,134.00, -71.80, 136.40}, 
+{'c', -71.80, 136.40, -61.00 ,136.00, -69.00, 142.40}, 
+{'l', -75.80, 154.00, 0, 0, 0, 0}, 
+{'c', -75.80, 154.00, -75.66 ,157.92, -85.80, 154.40}, 
+{'c', -95.60, 151.00, -105.90 ,138.10, -105.90, 138.10}, 
+{'f', 0.000000,0.000000,0.000000,1.000000,0,0 }, 
+{'m', -112.20, 113.60, 0, 0, 0, 0}, 
+{'c', -112.20, 113.60, -114.20 ,123.20, -77.40, 112.80}, 
+{'c', -77.40, 112.80, -73.00 ,112.80, -70.60, 113.60}, 
+{'c', -68.20, 114.40, -56.20 ,117.20, -54.20, 116.00}, 
+{'c', -54.20, 116.00, -61.40 ,129.60, -73.00, 128.00}, 
+{'c', -73.00, 128.00, -86.20 ,129.60, -85.80, 134.40}, 
+{'c', -85.80, 134.40, -81.80 ,141.60, -77.00, 144.00}, 
+{'c', -77.00, 144.00, -74.20 ,146.40, -74.60, 149.60}, 
+{'c', -75.00, 152.80, -77.80 ,154.40, -79.80, 155.20}, 
+{'c', -81.80, 156.00, -85.00 ,152.80, -86.60, 152.80}, 
+{'c', -88.20, 152.80, -96.60 ,146.40, -101.00, 141.60}, 
+{'c', -105.40, 136.80, -113.80 ,124.80, -113.40, 122.00}, 
+{'f', 0.938000,0.603000,0.603000,1.000000,0,0 }, 
+{'m', -109.00, 131.05, 0, 0, 0, 0}, 
+{'c', -106.40, 135.00, -103.20 ,139.20, -101.00, 141.60}, 
+{'c', -96.60, 146.40, -88.20 ,152.80, -86.60, 152.80}, 
+{'c', -85.00, 152.80, -81.80 ,156.00, -79.80, 155.20}, 
+{'c', -77.80, 154.40, -75.00 ,152.80, -74.60, 149.60}, 
+{'c', -74.20, 146.40, -77.00 ,144.00, -77.00, 144.00}, 
+{'c', -80.07, 142.47, -82.81 ,138.98, -84.39, 136.65}, 
+{'c', -84.39, 136.65, -84.20 ,139.20, -89.40, 138.40}, 
+{'c', -94.60, 137.60, -99.80 ,134.80, -101.40, 131.60}, 
+{'c', -103.00, 128.40, -105.40 ,126.00, -103.80, 129.60}, 
+{'c', -102.20, 133.20, -99.80 ,136.80, -98.20, 137.20}, 
+{'c', -96.60, 137.60, -97.00 ,138.80, -99.40, 138.40}, 
+{'f', 0.737000,0.402000,0.402000,1.000000,0,0 }, 
+{'m', -111.60, 110.00, 0, 0, 0, 0}, 
+{'c', -111.60, 110.00, -109.80 ,96.40, -108.60, 92.40}, 
+{'c', -108.60, 92.40, -109.40 ,85.60, -107.00, 81.40}, 
+{'c', -104.60, 77.20, -102.60 ,71.00, -99.60, 65.60}, 
+{'c', -96.60, 60.20, -96.40 ,56.20, -92.40, 54.60}, 
+{'c', -88.40, 53.00, -82.40 ,44.40, -79.60, 43.40}, 
+{'c', -76.80, 42.40, -77.00 ,43.20, -77.00, 43.20}, 
+{'c', -77.00, 43.20, -70.20 ,28.40, -56.60, 32.40}, 
+{'c', -56.60, 32.40, -72.80 ,29.60, -57.00, 20.20}, 
+{'c', -57.00, 20.20, -61.80 ,21.30, -58.50, 14.30}, 
+{'c', -56.30, 9.63, -56.80 ,16.40, -67.80, 28.20}, 
+{'c', -67.80, 28.20, -72.80 ,36.80, -78.00, 39.80}, 
+{'c', -83.20, 42.80, -95.20 ,49.80, -96.40, 53.60}, 
+{'c', -97.60, 57.40, -100.80 ,63.20, -102.80, 64.80}, 
+{'c', -104.80, 66.40, -107.60 ,70.60, -108.00, 74.00}, 
+{'c', -108.00, 74.00, -109.20 ,78.00, -110.60, 79.20}, 
+{'c', -112.00, 80.40, -112.20 ,83.60, -112.20, 85.60}, 
+{'c', -112.20, 87.60, -114.20 ,90.40, -114.00, 92.80}, 
+{'c', -114.00, 92.80, -113.20 ,111.80, -113.60, 113.80}, 
+{'f', 0.603000,0.134000,0.000000,1.000000,0,0 }, 
+{'m', -120.20, 114.60, 0, 0, 0, 0}, 
+{'c', -120.20, 114.60, -122.20 ,113.20, -126.60, 119.20}, 
+{'c', -126.60, 119.20, -119.30 ,152.20, -119.30, 153.60}, 
+{'c', -119.30, 153.60, -118.20 ,151.50, -119.50, 144.30}, 
+{'c', -120.80, 137.10, -121.70 ,124.40, -121.70, 124.40}, 
+{'f', 1.000000,1.000000,1.000000,1.000000,0,0 }, 
+{'m', -98.60, 54.00, 0, 0, 0, 0}, 
+{'c', -98.60, 54.00, -116.20 ,57.20, -115.80, 86.40}, 
+{'l', -116.60, 111.20, 0, 0, 0, 0}, 
+{'c', -116.60, 111.20, -117.80 ,85.60, -119.00, 84.00}, 
+{'c', -120.20, 82.40, -116.20 ,71.20, -119.40, 77.20}, 
+{'c', -119.40, 77.20, -133.40 ,91.20, -125.40, 112.40}, 
+{'c', -125.40, 112.40, -123.90 ,115.70, -126.90, 111.10}, 
+{'c', -126.90, 111.10, -131.50 ,98.50, -130.40, 92.10}, 
+{'c', -130.40, 92.10, -130.20 ,89.90, -128.30, 87.10}, 
+{'c', -128.30, 87.10, -119.70 ,75.40, -117.00, 73.10}, 
+{'c', -117.00, 73.10, -115.20 ,58.70, -99.80, 53.50}, 
+{'f', 0.603000,0.134000,0.000000,1.000000,0,0 }, 
+{'m', 40.80, -12.20, 0, 0, 0, 0}, 
+{'c', 41.46, -12.55, 41.45 ,-13.52, 42.03, -13.70}, 
+{'c', 43.18, -14.04, 43.34 ,-15.11, 43.86, -15.89}, 
+{'c', 44.73, -17.21, 44.93 ,-18.74, 45.51, -20.23}, 
+{'c', 45.78, -20.93, 45.81 ,-21.89, 45.50, -22.55}, 
+{'c', 44.32, -25.03, 43.62 ,-27.48, 42.18, -29.91}, 
+{'c', 41.91, -30.36, 41.65 ,-31.15, 41.45, -31.75}, 
+{'c', 40.98, -33.13, 39.73 ,-34.12, 38.87, -35.44}, 
+{'c', 38.58, -35.88, 39.10 ,-36.81, 38.39, -36.89}, 
+{'c', 37.49, -37.00, 36.04 ,-37.58, 35.81, -36.55}, 
+{'c', 35.22, -33.97, 36.23 ,-31.44, 37.20, -29.00}, 
+{'c', 36.42, -28.31, 36.75 ,-27.39, 36.90, -26.62}, 
+{'c', 37.61, -23.01, 36.42 ,-19.66, 35.66, -16.19}, 
+{'c', 35.63, -16.08, 35.97 ,-15.89, 35.95, -15.82}, 
+{'c', 34.72, -13.14, 33.27 ,-10.69, 31.45, -8.31}, 
+{'c', 30.70, -7.32, 29.82 ,-6.40, 29.33, -5.34}, 
+{'c', 28.96, -4.55, 28.55 ,-3.59, 28.80, -2.60}, 
+{'c', 25.36, 0.18, 23.11 ,4.03, 20.50, 7.87}, 
+{'c', 20.04, 8.55, 20.33 ,9.76, 20.88, 10.03}, 
+{'c', 21.70, 10.43, 22.65 ,9.40, 23.12, 8.56}, 
+{'c', 23.51, 7.86, 23.86 ,7.21, 24.36, 6.57}, 
+{'c', 24.49, 6.39, 24.31 ,5.97, 24.45, 5.85}, 
+{'c', 27.08, 3.50, 28.75 ,0.57, 31.20, -1.80}, 
+{'c', 33.15, -2.13, 34.69 ,-3.13, 36.44, -4.14}, 
+{'c', 36.74, -4.32, 37.27 ,-4.07, 37.56, -4.26}, 
+{'c', 39.31, -5.44, 39.31 ,-7.48, 39.41, -9.39}, 
+{'f', 0.000000,0.000000,0.000000,1.000000,0,0 }, 
+{'m', 31.96, -16.67, 0, 0, 0, 0}, 
+{'c', 32.08, -16.74, 31.93 ,-17.17, 32.04, -17.38}, 
+{'c', 32.20, -17.71, 32.60 ,-17.89, 32.76, -18.22}, 
+{'c', 32.87, -18.43, 32.71 ,-18.81, 32.85, -18.96}, 
+{'c', 35.18, -21.40, 35.44 ,-24.43, 34.40, -27.40}, 
+{'c', 35.42, -28.02, 35.48 ,-29.28, 35.06, -30.13}, 
+{'c', 34.21, -31.83, 34.01 ,-33.76, 33.04, -35.30}, 
+{'c', 32.24, -36.57, 30.66 ,-37.81, 29.29, -36.51}, 
+{'c', 28.87, -36.11, 28.55 ,-35.32, 28.82, -34.61}, 
+{'c', 28.89, -34.45, 29.17 ,-34.30, 29.15, -34.22}, 
+{'c', 29.04, -33.89, 28.49 ,-33.67, 28.49, -33.40}, 
+{'c', 28.46, -31.90, 27.50 ,-30.39, 28.13, -29.06}, 
+{'c', 28.91, -27.43, 29.72 ,-25.58, 30.40, -23.80}, 
+{'c', 29.17, -21.68, 30.20 ,-19.23, 28.45, -17.36}, 
+{'c', 28.31, -17.21, 28.32 ,-16.83, 28.44, -16.62}, 
+{'c', 28.73, -16.14, 29.14 ,-15.73, 29.62, -15.44}, 
+{'c', 29.83, -15.32, 30.18 ,-15.32, 30.38, -15.44}, 
+{'f', 0.000000,0.000000,0.000000,1.000000,0,0 }, 
+{'m', 94.77, -26.98, 0, 0, 0, 0}, 
+{'c', 96.16, -25.18, 96.45 ,-22.39, 94.40, -21.00}, 
+{'c', 94.95, -17.69, 98.30 ,-19.67, 100.40, -20.20}, 
+{'c', 100.29, -20.59, 100.52 ,-20.93, 100.80, -20.94}, 
+{'c', 101.86, -20.95, 102.54 ,-21.98, 103.60, -21.80}, 
+{'c', 104.03, -23.36, 105.67 ,-24.06, 106.32, -25.44}, 
+{'c', 108.04, -29.13, 107.45 ,-33.41, 104.87, -36.65}, 
+{'c', 104.67, -36.91, 104.88 ,-37.42, 104.76, -37.79}, 
+{'c', 104.00, -40.00, 101.94 ,-40.31, 100.00, -41.00}, 
+{'c', 98.82, -44.88, 98.16 ,-48.91, 96.40, -52.60}, 
+{'c', 94.79, -52.85, 94.09 ,-54.59, 92.75, -55.31}, 
+{'c', 91.42, -56.03, 90.85 ,-54.45, 90.89, -53.40}, 
+{'c', 90.90, -53.20, 91.35 ,-52.97, 91.18, -52.61}, 
+{'c', 91.11, -52.45, 90.84 ,-52.33, 90.84, -52.20}, 
+{'c', 90.85, -52.06, 91.07 ,-51.93, 91.20, -51.80}, 
+{'c', 90.28, -50.98, 88.86 ,-50.50, 88.56, -49.36}, 
+{'c', 87.61, -45.65, 90.18 ,-42.52, 91.85, -39.32}, 
+{'c', 92.44, -38.19, 91.71 ,-36.92, 90.95, -35.71}, 
+{'c', 90.51, -35.01, 90.62 ,-33.89, 90.89, -33.03}, 
+{'f', 0.000000,0.000000,0.000000,1.000000,0,0 }, 
+{'m', 57.61, -8.59, 0, 0, 0, 0}, 
+{'c', 56.12, -6.74, 52.71 ,-4.17, 55.63, -2.24}, 
+{'c', 55.82, -2.11, 56.19 ,-2.11, 56.37, -2.24}, 
+{'c', 58.39, -3.81, 60.39 ,-4.71, 62.83, -5.29}, 
+{'c', 62.95, -5.32, 63.22 ,-4.86, 63.59, -5.02}, 
+{'c', 65.21, -5.72, 67.22 ,-5.66, 68.40, -7.00}, 
+{'c', 72.17, -6.78, 75.73 ,-7.89, 79.12, -9.20}, 
+{'c', 80.28, -9.65, 81.55 ,-10.21, 82.75, -10.71}, 
+{'c', 84.13, -11.29, 85.33 ,-12.21, 86.45, -13.35}, 
+{'c', 86.58, -13.49, 86.93 ,-13.40, 87.20, -13.40}, 
+{'c', 87.16, -14.26, 88.12 ,-14.39, 88.37, -15.01}, 
+{'c', 88.46, -15.24, 88.31 ,-15.64, 88.44, -15.74}, 
+{'c', 90.58, -17.37, 91.50 ,-19.39, 90.33, -21.77}, 
+{'c', 90.05, -22.34, 89.80 ,-22.96, 89.23, -23.44}, 
+{'c', 88.15, -24.35, 87.05 ,-23.50, 86.00, -23.80}, 
+{'c', 85.84, -23.17, 85.11 ,-23.34, 84.73, -23.15}, 
+{'c', 83.87, -22.71, 82.53 ,-23.29, 81.67, -22.85}, 
+{'c', 80.31, -22.16, 79.07 ,-21.99, 77.65, -21.61}, 
+{'c', 77.34, -21.53, 76.56 ,-21.63, 76.40, -21.00}, 
+{'c', 76.27, -21.13, 76.12 ,-21.37, 76.01, -21.35}, 
+{'c', 74.10, -20.95, 72.84 ,-20.74, 71.54, -19.04}, 
+{'c', 71.44, -18.91, 71.00 ,-19.09, 70.84, -18.95}, 
+{'c', 69.88, -18.15, 69.48 ,-16.91, 68.38, -16.24}, 
+{'c', 68.17, -16.12, 67.82 ,-16.29, 67.63, -16.16}, 
+{'c', 66.98, -15.73, 66.62 ,-15.09, 65.97, -14.64}, 
+{'c', 65.64, -14.41, 65.25 ,-14.73, 65.28, -14.99}, 
+{'c', 65.52, -16.94, 66.17 ,-18.72, 65.60, -20.60}, 
+{'c', 67.68, -23.12, 70.19 ,-25.07, 72.00, -27.80}, 
+{'c', 72.02, -29.97, 72.71 ,-32.11, 72.59, -34.19}, 
+{'c', 72.58, -34.38, 72.30 ,-35.12, 72.17, -35.46}, 
+{'c', 71.86, -36.32, 72.76 ,-37.38, 71.92, -38.11}, 
+{'c', 70.52, -39.31, 69.22 ,-38.43, 68.40, -37.00}, 
+{'c', 66.56, -36.61, 64.50 ,-35.92, 62.92, -37.15}, 
+{'c', 61.91, -37.94, 61.33 ,-38.84, 60.53, -39.90}, 
+{'c', 59.55, -41.20, 59.88 ,-42.64, 59.95, -44.20}, 
+{'c', 59.96, -44.33, 59.65 ,-44.47, 59.65, -44.60}, 
+{'c', 59.65, -44.73, 59.87 ,-44.87, 60.00, -45.00}, 
+{'c', 59.29, -45.63, 59.02 ,-46.68, 58.00, -47.00}, 
+{'c', 58.30, -48.09, 57.63 ,-48.98, 56.76, -49.28}, 
+{'c', 54.76, -49.97, 53.09 ,-48.06, 51.19, -47.98}, 
+{'c', 50.68, -47.97, 50.21 ,-49.00, 49.56, -49.33}, 
+{'c', 49.13, -49.54, 48.43 ,-49.58, 48.07, -49.31}, 
+{'c', 47.38, -48.81, 46.79 ,-48.69, 46.03, -48.49}, 
+{'c', 44.41, -48.05, 43.14 ,-46.96, 41.66, -46.10}, 
+{'c', 40.17, -45.25, 39.22 ,-43.81, 38.14, -42.49}, 
+{'c', 37.20, -41.34, 37.06 ,-38.92, 38.48, -38.42}, 
+{'c', 40.32, -37.77, 41.63 ,-40.48, 43.59, -40.15}, 
+{'c', 43.90, -40.10, 44.11 ,-39.79, 44.00, -39.40}, 
+{'c', 44.39, -39.29, 44.61 ,-39.52, 44.80, -39.80}, 
+{'c', 45.66, -38.78, 46.82 ,-38.44, 47.76, -37.57}, 
+{'c', 48.73, -36.67, 50.48 ,-37.09, 51.49, -36.09}, 
+{'c', 53.02, -34.59, 52.46 ,-31.91, 54.40, -30.60}, 
+{'c', 53.81, -29.29, 53.21 ,-28.01, 52.87, -26.58}, 
+{'c', 52.59, -25.38, 53.58 ,-24.18, 54.80, -24.27}, 
+{'c', 56.05, -24.36, 56.31 ,-25.12, 56.80, -26.20}, 
+{'c', 57.07, -25.93, 57.54 ,-25.64, 57.49, -25.42}, 
+{'c', 57.04, -23.03, 56.01 ,-21.04, 55.55, -18.61}, 
+{'c', 55.49, -18.29, 55.19 ,-18.09, 54.80, -18.20}, 
+{'c', 54.33, -14.05, 50.28 ,-11.66, 47.73, -8.49}, 
+{'c', 47.33, -7.99, 47.33 ,-6.74, 47.74, -6.34}, 
+{'c', 49.14, -4.95, 51.10 ,-6.50, 52.80, -7.00}, 
+{'c', 53.01, -8.21, 53.87 ,-9.15, 55.20, -9.09}, 
+{'c', 55.46, -9.08, 55.70 ,-9.62, 56.02, -9.75}, 
+{'c', 56.37, -9.89, 56.87 ,-9.67, 57.16, -9.87}, 
+{'c', 58.88, -11.06, 60.29 ,-12.17, 62.03, -13.36}, 
+{'c', 62.22, -13.49, 62.57 ,-13.33, 62.78, -13.44}, 
+{'c', 63.11, -13.60, 63.29 ,-13.98, 63.62, -14.17}, 
+{'c', 63.97, -14.37, 64.21 ,-14.08, 64.40, -13.80}, 
+{'c', 63.75, -13.45, 63.75 ,-12.49, 63.17, -12.29}, 
+{'c', 62.39, -12.02, 61.83 ,-11.51, 61.16, -11.06}, 
+{'c', 60.87, -10.87, 60.21 ,-11.12, 60.10, -10.94}, 
+{'f', 0.000000,0.000000,0.000000,1.000000,0,0 }, 
+{'m', 2.20, -58.00, 0, 0, 0, 0}, 
+{'c', 2.20, -58.00, -7.04 ,-60.87, -18.20, -35.20}, 
+{'c', -18.20, -35.20, -20.60 ,-30.00, -23.00, -28.00}, 
+{'c', -25.40, -26.00, -36.60 ,-22.40, -38.60, -18.40}, 
+{'l', -49.00, -2.40, 0, 0, 0, 0}, 
+{'c', -49.00, -2.40, -34.20 ,-18.40, -31.00, -20.80}, 
+{'c', -31.00, -20.80, -23.00 ,-29.20, -26.20, -22.40}, 
+{'c', -26.20, -22.40, -40.20 ,-11.60, -39.00, -2.40}, 
+{'c', -39.00, -2.40, -44.60 ,12.00, -45.40, 14.00}, 
+{'c', -45.40, 14.00, -29.40 ,-18.00, -27.00, -19.20}, 
+{'c', -24.60, -20.40, -23.40 ,-20.40, -24.60, -16.80}, 
+{'c', -25.80, -13.20, -26.20 ,3.20, -29.00, 5.20}, 
+{'c', -29.00, 5.20, -21.00 ,-15.20, -21.80, -18.40}, 
+{'c', -21.80, -18.40, -18.60 ,-22.00, -16.20, -16.80}, 
+{'l', -17.40, -0.80, 0, 0, 0, 0}, 
+{'l', -13.00, 11.20, 0, 0, 0, 0}, 
+{'c', -13.00, 11.20, -15.40 ,0.00, -13.80, -15.60}, 
+{'c', -13.80, -15.60, -15.80 ,-26.00, -11.80, -20.40}, 
+{'c', -7.80, -14.80, 1.80 ,-8.80, 1.80, -4.00}, 
+{'c', 1.80, -4.00, -3.40 ,-21.60, -12.60, -26.40}, 
+{'l', -16.60, -20.40, 0, 0, 0, 0}, 
+{'l', -17.80, -22.40, 0, 0, 0, 0}, 
+{'c', -17.80, -22.40, -21.40 ,-23.20, -17.00, -30.00}, 
+{'c', -12.60, -36.80, -13.00 ,-37.60, -13.00, -37.60}, 
+{'c', -13.00, -37.60, -6.60 ,-30.40, -5.00, -30.40}, 
+{'c', -5.00, -30.40, 8.20 ,-38.00, 9.40, -13.60}, 
+{'c', 9.40, -13.60, 16.20 ,-28.00, 7.00, -34.80}, 
+{'c', 7.00, -34.80, -7.80 ,-36.80, -6.60, -42.00}, 
+{'l', 0.60, -54.40, 0, 0, 0, 0}, 
+{'f', 0.000000,0.000000,0.000000,1.000000,0,0 }, 
+{'m', -17.80, -41.60, 0, 0, 0, 0}, 
+{'c', -17.80, -41.60, -30.60 ,-41.60, -33.80, -36.40}, 
+{'l', -41.00, -26.80, 0, 0, 0, 0}, 
+{'c', -41.00, -26.80, -23.80 ,-36.80, -19.80, -38.00}, 
+{'f', 0.000000,0.000000,0.000000,1.000000,0,0 }, 
+{'m', -57.80, -35.20, 0, 0, 0, 0}, 
+{'c', -57.80, -35.20, -59.80 ,-34.00, -60.20, -31.20}, 
+{'c', -60.60, -28.40, -63.00 ,-28.00, -62.20, -25.20}, 
+{'c', -61.40, -22.40, -59.40 ,-20.00, -59.40, -24.00}, 
+{'c', -59.40, -28.00, -57.80 ,-30.00, -57.00, -31.20}, 
+{'f', 0.000000,0.000000,0.000000,1.000000,0,0 }, 
+{'m', -66.60, 26.00, 0, 0, 0, 0}, 
+{'c', -66.60, 26.00, -75.00 ,22.00, -78.20, 18.40}, 
+{'c', -81.40, 14.80, -80.95 ,19.97, -85.80, 19.60}, 
+{'c', -91.65, 19.16, -90.60 ,3.20, -90.60, 3.20}, 
+{'l', -94.60, 10.80, 0, 0, 0, 0}, 
+{'c', -94.60, 10.80, -95.80 ,25.20, -87.80, 22.80}, 
+{'c', -83.89, 21.63, -82.60 ,23.20, -84.20, 24.00}, 
+{'c', -85.80, 24.80, -78.60 ,25.20, -81.40, 26.80}, 
+{'c', -84.20, 28.40, -69.80 ,23.20, -72.20, 33.60}, 
+{'f', 0.000000,0.000000,0.000000,1.000000,0,0 }, 
+{'m', -79.20, 40.40, 0, 0, 0, 0}, 
+{'c', -79.20, 40.40, -94.60 ,44.80, -98.20, 35.20}, 
+{'c', -98.20, 35.20, -103.00 ,37.60, -100.80, 40.60}, 
+{'c', -98.60, 43.60, -97.40 ,44.00, -97.40, 44.00}, 
+{'c', -97.40, 44.00, -92.00 ,45.20, -92.60, 46.00}, 
+{'c', -93.20, 46.80, -95.60 ,50.20, -95.60, 50.20}, 
+{'f', 0.000000,0.000000,0.000000,1.000000,0,0 }, 
+{'m', 149.20, 118.60, 0, 0, 0, 0}, 
+{'c', 148.77, 120.73, 147.10 ,121.54, 145.20, 122.20}, 
+{'c', 143.28, 121.24, 140.69 ,118.14, 138.80, 120.20}, 
+{'c', 138.33, 119.72, 137.55 ,119.66, 137.20, 119.00}, 
+{'c', 136.74, 118.10, 137.01 ,117.06, 136.67, 116.26}, 
+{'c', 136.12, 114.98, 135.41 ,113.62, 135.60, 112.20}, 
+{'c', 137.41, 111.49, 138.00 ,109.58, 137.53, 107.82}, 
+{'c', 137.46, 107.56, 137.03 ,107.37, 137.23, 107.02}, 
+{'c', 137.42, 106.69, 137.73 ,106.47, 138.00, 106.20}, 
+{'c', 137.87, 106.33, 137.72 ,106.57, 137.61, 106.55}, 
+{'c', 137.00, 106.44, 137.12 ,105.81, 137.25, 105.42}, 
+{'c', 137.84, 103.67, 139.85 ,103.41, 141.20, 104.60}, 
+{'c', 141.46, 104.03, 141.97 ,104.23, 142.40, 104.20}, 
+{'c', 142.35, 103.62, 142.76 ,103.09, 142.96, 102.67}, 
+{'c', 143.47, 101.58, 145.10 ,102.68, 145.90, 102.07}, 
+{'c', 146.98, 101.25, 148.04 ,100.55, 149.12, 101.15}, 
+{'c', 150.93, 102.16, 152.64 ,103.37, 153.84, 105.11}, 
+{'c', 154.41, 105.95, 154.65 ,107.23, 154.59, 108.19}, 
+{'c', 154.55, 108.83, 153.17 ,108.48, 152.83, 109.41}, 
+{'c', 152.19, 111.16, 154.02 ,111.68, 154.77, 113.02}, 
+{'c', 154.97, 113.37, 154.71 ,113.67, 154.39, 113.77}, 
+{'c', 153.98, 113.90, 153.20 ,113.71, 153.33, 114.16}, 
+{'f', 1.000000,1.000000,1.000000,1.000000,0,0 }, 
+{'m', 139.60, 138.20, 0, 0, 0, 0}, 
+{'c', 139.59, 136.46, 137.99 ,134.71, 139.20, 133.00}, 
+{'c', 139.34, 133.13, 139.47 ,133.36, 139.60, 133.36}, 
+{'c', 139.74, 133.36, 139.87 ,133.13, 140.00, 133.00}, 
+{'c', 141.50, 135.22, 145.15 ,136.15, 145.01, 138.99}, 
+{'c', 144.98, 139.44, 143.90 ,140.36, 144.80, 141.00}, 
+{'c', 142.99, 142.35, 142.93 ,144.72, 142.00, 146.60}, 
+{'c', 140.76, 146.31, 139.55 ,145.95, 138.40, 145.40}, 
+{'c', 138.75, 143.91, 138.64 ,142.23, 139.46, 140.91}, 
+{'f', 1.000000,1.000000,1.000000,1.000000,0,0 }, 
+{'m', -26.60, 129.20, 0, 0, 0, 0}, 
+{'c', -26.60, 129.20, -43.46 ,139.34, -29.40, 124.00}, 
+{'c', -20.60, 114.40, -10.60 ,108.80, -10.60, 108.80}, 
+{'c', -10.60, 108.80, -0.20 ,104.40, 3.40, 103.20}, 
+{'c', 7.00, 102.00, 22.20 ,96.80, 25.40, 96.40}, 
+{'c', 28.60, 96.00, 38.20 ,92.00, 45.00, 96.00}, 
+{'c', 51.80, 100.00, 59.80 ,104.40, 59.80, 104.40}, 
+{'c', 59.80, 104.40, 43.40 ,96.00, 39.80, 98.40}, 
+{'c', 36.20, 100.80, 29.00 ,100.40, 23.00, 103.60}, 
+{'c', 23.00, 103.60, 8.20 ,108.00, 5.00, 110.00}, 
+{'c', 1.80, 112.00, -8.60 ,123.60, -10.20, 122.80}, 
+{'c', -11.80, 122.00, -9.80 ,121.60, -8.60, 118.80}, 
+{'c', -7.40, 116.00, -9.40 ,114.40, -17.40, 120.80}, 
+{'f', 0.804000,0.804000,0.804000,1.000000,0,0 }, 
+{'m', -19.20, 123.23, 0, 0, 0, 0}, 
+{'c', -19.20, 123.23, -17.79 ,110.19, -9.31, 111.86}, 
+{'c', -9.31, 111.86, -1.08 ,107.69, 1.64, 105.72}, 
+{'c', 1.64, 105.72, 9.78 ,104.02, 11.09, 103.40}, 
+{'c', 29.57, 94.70, 44.29 ,99.22, 44.84, 98.10}, 
+{'c', 45.38, 96.98, 65.01 ,104.10, 68.61, 108.19}, 
+{'c', 69.01, 108.63, 58.38 ,102.59, 48.69, 100.70}, 
+{'c', 40.41, 99.08, 18.81 ,100.94, 7.91, 106.48}, 
+{'c', 4.93, 107.99, -4.01 ,113.77, -6.54, 113.66}, 
+{'f', 0.000000,0.000000,0.000000,1.000000,0,0 }, 
+{'m', -23.00, 148.80, 0, 0, 0, 0}, 
+{'c', -23.00, 148.80, -38.20 ,146.40, -21.40, 144.80}, 
+{'c', -21.40, 144.80, -3.40 ,142.80, 0.60, 137.60}, 
+{'c', 0.60, 137.60, 14.20 ,128.40, 17.00, 128.00}, 
+{'c', 19.80, 127.60, 49.80 ,120.40, 50.20, 118.00}, 
+{'c', 50.60, 115.60, 56.20 ,115.60, 57.80, 116.40}, 
+{'c', 59.40, 117.20, 58.60 ,118.40, 55.80, 119.20}, 
+{'c', 53.00, 120.00, 21.80 ,136.40, 15.40, 137.60}, 
+{'c', 9.00, 138.80, -2.60 ,146.40, -7.40, 147.60}, 
+{'f', 0.804000,0.804000,0.804000,1.000000,0,0 }, 
+{'m', -3.48, 141.40, 0, 0, 0, 0}, 
+{'c', -3.48, 141.40, -12.06 ,140.57, -3.46, 139.75}, 
+{'c', -3.46, 139.75, 5.36 ,136.33, 7.40, 133.67}, 
+{'c', 7.40, 133.67, 14.37 ,128.96, 15.80, 128.75}, 
+{'c', 17.23, 128.55, 31.19 ,124.86, 31.40, 123.63}, 
+{'c', 31.60, 122.40, 65.67 ,109.82, 70.09, 113.01}, 
+{'c', 73.00, 115.11, 63.10 ,113.44, 53.47, 117.85}, 
+{'c', 52.11, 118.47, 18.26 ,133.05, 14.98, 133.67}, 
+{'c', 11.70, 134.28, 5.76 ,138.17, 3.31, 138.79}, 
+{'f', 0.000000,0.000000,0.000000,1.000000,0,0 }, 
+{'m', -11.40, 143.60, 0, 0, 0, 0}, 
+{'c', -11.40, 143.60, -6.20 ,143.20, -7.40, 144.80}, 
+{'c', -8.60, 146.40, -11.00 ,145.60, -11.00, 145.60}, 
+{'f', 0.000000,0.000000,0.000000,1.000000,0,0 }, 
+{'m', -18.60, 145.20, 0, 0, 0, 0}, 
+{'c', -18.60, 145.20, -13.40 ,144.80, -14.60, 146.40}, 
+{'c', -15.80, 148.00, -18.20 ,147.20, -18.20, 147.20}, 
+{'f', 0.000000,0.000000,0.000000,1.000000,0,0 }, 
+{'m', -29.00, 146.80, 0, 0, 0, 0}, 
+{'c', -29.00, 146.80, -23.80 ,146.40, -25.00, 148.00}, 
+{'c', -26.20, 149.60, -28.60 ,148.80, -28.60, 148.80}, 
+{'f', 0.000000,0.000000,0.000000,1.000000,0,0 }, 
+{'m', -36.60, 147.60, 0, 0, 0, 0}, 
+{'c', -36.60, 147.60, -31.40 ,147.20, -32.60, 148.80}, 
+{'c', -33.80, 150.40, -36.20 ,149.60, -36.20, 149.60}, 
+{'f', 0.000000,0.000000,0.000000,1.000000,0,0 }, 
+{'m', 1.80, 108.00, 0, 0, 0, 0}, 
+{'c', 1.80, 108.00, 6.20 ,108.00, 5.00, 109.60}, 
+{'c', 3.80, 111.20, 0.60 ,110.80, 0.60, 110.80}, 
+{'f', 0.000000,0.000000,0.000000,1.000000,0,0 }, 
+{'m', -8.20, 113.60, 0, 0, 0, 0}, 
+{'c', -8.20, 113.60, -1.69 ,111.46, -4.20, 114.80}, 
+{'c', -5.40, 116.40, -7.80 ,115.60, -7.80, 115.60}, 
+{'f', 0.000000,0.000000,0.000000,1.000000,0,0 }, 
+{'m', -19.40, 118.40, 0, 0, 0, 0}, 
+{'c', -19.40, 118.40, -14.20 ,118.00, -15.40, 119.60}, 
+{'c', -16.60, 121.20, -19.00 ,120.40, -19.00, 120.40}, 
+{'f', 0.000000,0.000000,0.000000,1.000000,0,0 }, 
+{'m', -27.00, 124.40, 0, 0, 0, 0}, 
+{'c', -27.00, 124.40, -21.80 ,124.00, -23.00, 125.60}, 
+{'c', -24.20, 127.20, -26.60 ,126.40, -26.60, 126.40}, 
+{'f', 0.000000,0.000000,0.000000,1.000000,0,0 }, 
+{'m', -33.80, 129.20, 0, 0, 0, 0}, 
+{'c', -33.80, 129.20, -28.60 ,128.80, -29.80, 130.40}, 
+{'c', -31.00, 132.00, -33.40 ,131.20, -33.40, 131.20}, 
+{'f', 0.000000,0.000000,0.000000,1.000000,0,0 }, 
+{'m', 5.28, 135.60, 0, 0, 0, 0}, 
+{'c', 5.28, 135.60, 12.20 ,135.07, 10.61, 137.19}, 
+{'c', 9.01, 139.32, 5.81 ,138.26, 5.81, 138.26}, 
+{'f', 0.000000,0.000000,0.000000,1.000000,0,0 }, 
+{'m', 15.68, 130.80, 0, 0, 0, 0}, 
+{'c', 15.68, 130.80, 22.60 ,130.27, 21.01, 132.40}, 
+{'c', 19.41, 134.53, 16.21 ,133.46, 16.21, 133.46}, 
+{'f', 0.000000,0.000000,0.000000,1.000000,0,0 }, 
+{'m', 26.48, 126.40, 0, 0, 0, 0}, 
+{'c', 26.48, 126.40, 33.40 ,125.87, 31.81, 128.00}, 
+{'c', 30.21, 130.12, 27.01 ,129.06, 27.01, 129.06}, 
+{'f', 0.000000,0.000000,0.000000,1.000000,0,0 }, 
+{'m', 36.88, 121.60, 0, 0, 0, 0}, 
+{'c', 36.88, 121.60, 43.80 ,121.07, 42.21, 123.19}, 
+{'c', 40.61, 125.33, 37.41 ,124.26, 37.41, 124.26}, 
+{'f', 0.000000,0.000000,0.000000,1.000000,0,0 }, 
+{'m', 9.28, 103.60, 0, 0, 0, 0}, 
+{'c', 9.28, 103.60, 16.20 ,103.07, 14.61, 105.19}, 
+{'c', 13.01, 107.33, 9.01 ,107.06, 9.01, 107.06}, 
+{'f', 0.000000,0.000000,0.000000,1.000000,0,0 }, 
+{'m', 19.28, 100.40, 0, 0, 0, 0}, 
+{'c', 19.28, 100.40, 26.20 ,99.87, 24.61, 102.00}, 
+{'c', 23.01, 104.12, 18.61 ,103.86, 18.61, 103.86}, 
+{'f', 0.000000,0.000000,0.000000,1.000000,0,0 }, 
+{'m', -3.40, 140.40, 0, 0, 0, 0}, 
+{'c', -3.40, 140.40, 1.80 ,140.00, 0.60, 141.60}, 
+{'c', -0.60, 143.20, -3.00 ,142.40, -3.00, 142.40}, 
+{'f', 0.000000,0.000000,0.000000,1.000000,0,0 }, 
+{'m', -76.60, 41.20, 0, 0, 0, 0}, 
+{'c', -76.60, 41.20, -81.00 ,50.00, -81.40, 53.20}, 
+{'c', -81.40, 53.20, -80.60 ,44.40, -79.40, 42.40}, 
+{'f', 0.603000,0.134000,0.000000,1.000000,0,0 }, 
+{'m', -95.00, 55.20, 0, 0, 0, 0}, 
+{'c', -95.00, 55.20, -98.20 ,69.60, -97.80, 72.40}, 
+{'c', -97.80, 72.40, -99.00 ,60.80, -98.60, 59.60}, 
+{'f', 0.603000,0.134000,0.000000,1.000000,0,0 }, 
+{'m', -74.20, -19.40, 0, 0, 0, 0}, 
+{'l', -74.40, -16.20, 0, 0, 0, 0}, 
+{'l', -76.60, -16.00, 0, 0, 0, 0}, 
+{'c', -76.60, -16.00, -62.40 ,-3.40, -61.80, 4.20}, 
+{'f', 0.804000,0.804000,0.804000,1.000000,0,0 }, 
+{'m', -70.22, -18.14, 0, 0, 0, 0}, 
+{'c', -70.65, -18.55, -70.43 ,-19.30, -70.84, -19.56}, 
+{'c', -71.64, -20.07, -69.54 ,-20.13, -69.77, -20.84}, 
+{'c', -70.15, -22.05, -69.96 ,-22.07, -70.08, -23.35}, 
+{'c', -70.14, -23.95, -69.55 ,-25.49, -69.17, -25.93}, 
+{'c', -67.72, -27.58, -69.05 ,-30.51, -67.41, -32.06}, 
+{'c', -67.10, -32.35, -66.73 ,-32.90, -66.44, -33.32}, 
+{'c', -65.78, -34.28, -64.60 ,-34.77, -63.65, -35.60}, 
+{'c', -63.33, -35.88, -63.53 ,-36.70, -62.96, -36.61}, 
+{'c', -62.25, -36.49, -61.01 ,-36.62, -61.05, -35.78}, 
+{'c', -61.16, -33.66, -62.49 ,-31.94, -63.77, -30.28}, 
+{'c', -63.32, -29.57, -63.78 ,-28.94, -64.06, -28.38}, 
+{'c', -65.40, -25.76, -65.21 ,-22.92, -65.39, -20.08}, 
+{'c', -65.39, -19.99, -65.70 ,-19.92, -65.69, -19.86}, 
+{'c', -65.34, -17.53, -64.75 ,-15.33, -63.87, -13.10}, 
+{'c', -63.51, -12.17, -63.04 ,-11.28, -62.89, -10.35}, 
+{'c', -62.77, -9.66, -62.67 ,-8.83, -63.08, -8.12}, 
+{'c', -61.05, -5.23, -62.35 ,-2.58, -61.19, 0.95}, 
+{'c', -60.98, 1.57, -59.29 ,3.49, -59.75, 3.33}, 
+{'c', -62.26, 2.46, -62.37 ,2.06, -62.55, 1.30}, 
+{'c', -62.70, 0.68, -63.03 ,-0.70, -63.26, -1.30}, 
+{'c', -63.33, -1.46, -63.50 ,-3.35, -63.58, -3.47}, 
+{'c', -65.09, -5.85, -63.73 ,-5.67, -65.10, -8.03}, 
+{'c', -66.53, -8.71, -67.50 ,-9.82, -68.62, -10.98}, 
+{'c', -68.82, -11.18, -67.67 ,-11.91, -67.86, -12.12}, 
+{'c', -68.95, -13.41, -70.10 ,-14.18, -69.76, -15.67}, 
+{'f', 0.000000,0.000000,0.000000,1.000000,0,0 }, 
+{'m', -73.80, -16.40, 0, 0, 0, 0}, 
+{'c', -73.80, -16.40, -73.40 ,-9.60, -71.00, -8.00}, 
+{'c', -68.60, -6.40, -69.80 ,-7.20, -73.00, -8.40}, 
+{'c', -76.20, -9.60, -75.00 ,-10.40, -75.00, -10.40}, 
+{'c', -75.00, -10.40, -77.80 ,-10.00, -75.40, -8.00}, 
+{'c', -73.00, -6.00, -69.40 ,-3.60, -71.00, -3.60}, 
+{'c', -72.60, -3.60, -80.20 ,-7.60, -80.20, -10.40}, 
+{'c', -80.20, -13.20, -81.20 ,-17.30, -81.20, -17.30}, 
+{'c', -81.20, -17.30, -80.10 ,-18.10, -75.30, -18.00}, 
+{'f', 0.000000,0.000000,0.000000,1.000000,0,0 }, 
+{'m', -74.60, 2.20, 0, 0, 0, 0}, 
+{'c', -74.60, 2.20, -83.12 ,-0.59, -101.60, 2.80}, 
+{'c', -101.60, 2.80, -92.57 ,0.72, -73.80, 3.00}, 
+{'f', 1.000000,1.000000,1.000000,1.000000,0,0 }, 
+{'m', -72.50, 2.13, 0, 0, 0, 0}, 
+{'c', -72.50, 2.13, -80.75 ,-1.39, -99.45, 0.39}, 
+{'c', -99.45, 0.39, -90.28 ,-0.90, -71.77, 3.00}, 
+{'f', 1.000000,1.000000,1.000000,1.000000,0,0 }, 
+{'m', -70.71, 2.22, 0, 0, 0, 0}, 
+{'c', -70.71, 2.22, -78.68 ,-1.90, -97.46, -1.51}, 
+{'c', -97.46, -1.51, -88.21 ,-2.12, -70.05, 3.14}, 
+{'f', 1.000000,1.000000,1.000000,1.000000,0,0 }, 
+{'m', -69.44, 2.44, 0, 0, 0, 0}, 
+{'c', -69.44, 2.44, -76.27 ,-1.86, -93.14, -2.96}, 
+{'c', -93.14, -2.96, -84.80 ,-2.79, -68.92, 3.32}, 
+{'f', 1.000000,1.000000,1.000000,1.000000,0,0 }, 
+{'m', 45.84, 12.96, 0, 0, 0, 0}, 
+{'c', 45.84, 12.96, 44.91 ,13.61, 45.12, 12.42}, 
+{'c', 45.34, 11.24, 73.55 ,-1.93, 77.16, -1.68}, 
+{'f', 1.000000,1.000000,1.000000,1.000000,0,0 }, 
+{'m', 42.45, 13.60, 0, 0, 0, 0}, 
+{'c', 42.45, 13.60, 41.57 ,14.31, 41.69, 13.12}, 
+{'c', 41.81, 11.93, 68.90 ,-3.42, 72.52, -3.45}, 
+{'f', 1.000000,1.000000,1.000000,1.000000,0,0 }, 
+{'m', 39.16, 14.97, 0, 0, 0, 0}, 
+{'c', 39.16, 14.97, 38.33 ,15.75, 38.37, 14.55}, 
+{'c', 38.42, 13.35, 58.23 ,-2.15, 68.05, -4.02}, 
+{'f', 1.000000,1.000000,1.000000,1.000000,0,0 }, 
+{'m', 36.28, 16.84, 0, 0, 0, 0}, 
+{'c', 36.28, 16.84, 35.54 ,17.53, 35.58, 16.45}, 
+{'c', 35.62, 15.37, 53.45 ,1.43, 62.28, -0.26}, 
+{'f', 1.000000,1.000000,1.000000,1.000000,0,0 }, 
+{'m', 4.60, 164.80, 0, 0, 0, 0}, 
+{'c', 4.60, 164.80, -10.60 ,162.40, 6.20, 160.80}, 
+{'c', 6.20, 160.80, 24.20 ,158.80, 28.20, 153.60}, 
+{'c', 28.20, 153.60, 41.80 ,144.40, 44.60, 144.00}, 
+{'c', 47.40, 143.60, 63.80 ,140.00, 64.20, 137.60}, 
+{'c', 64.60, 135.20, 70.60 ,132.80, 72.20, 133.60}, 
+{'c', 73.80, 134.40, 73.80 ,143.60, 71.00, 144.40}, 
+{'c', 68.20, 145.20, 49.40 ,152.40, 43.00, 153.60}, 
+{'c', 36.60, 154.80, 25.00 ,162.40, 20.20, 163.60}, 
+{'f', 0.804000,0.804000,0.804000,1.000000,0,0 }, 
+{'m', 77.60, 127.40, 0, 0, 0, 0}, 
+{'c', 77.60, 127.40, 74.60 ,129.00, 73.40, 131.60}, 
+{'c', 73.40, 131.60, 67.00 ,142.20, 52.80, 145.40}, 
+{'c', 52.80, 145.40, 29.80 ,154.40, 22.00, 156.40}, 
+{'c', 22.00, 156.40, 8.60 ,161.40, 1.20, 160.60}, 
+{'c', 1.20, 160.60, -5.80 ,160.80, 0.40, 162.40}, 
+{'c', 0.40, 162.40, 20.60 ,160.40, 24.00, 158.60}, 
+{'c', 24.00, 158.60, 39.60 ,153.40, 42.60, 150.80}, 
+{'c', 45.60, 148.20, 63.80 ,143.20, 66.00, 141.20}, 
+{'f', 0.000000,0.000000,0.000000,1.000000,0,0 }, 
+{'m', 18.88, 158.91, 0, 0, 0, 0}, 
+{'c', 18.88, 158.91, 24.11 ,158.69, 22.96, 160.23}, 
+{'c', 21.80, 161.78, 19.36 ,160.91, 19.36, 160.91}, 
+{'f', 0.000000,0.000000,0.000000,1.000000,0,0 }, 
+{'m', 11.68, 160.26, 0, 0, 0, 0}, 
+{'c', 11.68, 160.26, 16.91 ,160.04, 15.76, 161.59}, 
+{'c', 14.60, 163.14, 12.15 ,162.26, 12.15, 162.26}, 
+{'f', 0.000000,0.000000,0.000000,1.000000,0,0 }, 
+{'m', 1.25, 161.51, 0, 0, 0, 0}, 
+{'c', 1.25, 161.51, 6.48 ,161.28, 5.33, 162.83}, 
+{'c', 4.17, 164.38, 1.73 ,163.51, 1.73, 163.51}, 
+{'f', 0.000000,0.000000,0.000000,1.000000,0,0 }, 
+{'m', -6.38, 162.06, 0, 0, 0, 0}, 
+{'c', -6.38, 162.06, -1.15 ,161.83, -2.31, 163.38}, 
+{'c', -3.46, 164.93, -5.91 ,164.05, -5.91, 164.05}, 
+{'f', 0.000000,0.000000,0.000000,1.000000,0,0 }, 
+{'m', 35.41, 151.51, 0, 0, 0, 0}, 
+{'c', 35.41, 151.51, 42.38 ,151.21, 40.84, 153.27}, 
+{'c', 39.31, 155.34, 36.05 ,154.17, 36.05, 154.17}, 
+{'f', 0.000000,0.000000,0.000000,1.000000,0,0 }, 
+{'m', 45.73, 147.09, 0, 0, 0, 0}, 
+{'c', 45.73, 147.09, 51.69 ,143.79, 51.16, 148.85}, 
+{'c', 50.88, 151.41, 46.36 ,149.75, 46.36, 149.75}, 
+{'f', 0.000000,0.000000,0.000000,1.000000,0,0 }, 
+{'m', 54.86, 144.27, 0, 0, 0, 0}, 
+{'c', 54.86, 144.27, 62.02 ,140.57, 60.29, 146.03}, 
+{'c', 59.51, 148.49, 55.49 ,146.94, 55.49, 146.94}, 
+{'f', 0.000000,0.000000,0.000000,1.000000,0,0 }, 
+{'m', 64.38, 139.45, 0, 0, 0, 0}, 
+{'c', 64.38, 139.45, 68.73 ,134.55, 69.80, 141.21}, 
+{'c', 70.21, 143.75, 65.01 ,142.11, 65.01, 142.11}, 
+{'f', 0.000000,0.000000,0.000000,1.000000,0,0 }, 
+{'m', 26.83, 156.00, 0, 0, 0, 0}, 
+{'c', 26.83, 156.00, 32.06 ,155.77, 30.91, 157.32}, 
+{'c', 29.76, 158.87, 27.31 ,158.00, 27.31, 158.00}, 
+{'f', 0.000000,0.000000,0.000000,1.000000,0,0 }, 
+{'m', 62.43, 34.60, 0, 0, 0, 0}, 
+{'c', 62.43, 34.60, 61.71 ,35.27, 61.71, 34.20}, 
+{'c', 61.71, 33.13, 79.19 ,19.86, 88.03, 18.48}, 
+{'f', 1.000000,1.000000,1.000000,1.000000,0,0 }, 
+{'m', 65.40, 98.40, 0, 0, 0, 0}, 
+{'c', 65.40, 98.40, 87.40 ,120.80, 96.60, 124.40}, 
+{'c', 96.60, 124.40, 105.80 ,135.60, 101.80, 161.60}, 
+{'c', 101.80, 161.60, 98.60 ,169.20, 95.40, 148.40}, 
+{'c', 95.40, 148.40, 98.60 ,123.20, 87.40, 139.20}, 
+{'c', 87.40, 139.20, 79.00 ,129.30, 85.40, 129.60}, 
+{'c', 85.40, 129.60, 88.60 ,131.60, 89.00, 130.00}, 
+{'c', 89.40, 128.40, 81.40 ,114.80, 64.20, 100.40}, 
+{'f', 0.000000,0.000000,0.000000,1.000000,0,0 }, 
+{'m', 7.00, 137.20, 0, 0, 0, 0}, 
+{'c', 7.00, 137.20, 6.80 ,135.40, 8.60, 136.20}, 
+{'c', 10.40, 137.00, 104.60 ,143.20, 136.20, 167.20}, 
+{'f', 1.000000,1.000000,1.000000,1.000000,0,0 }, 
+{'m', 17.40, 132.80, 0, 0, 0, 0}, 
+{'c', 17.40, 132.80, 17.20 ,131.00, 19.00, 131.80}, 
+{'c', 20.80, 132.60, 157.40 ,131.60, 181.00, 164.00}, 
+{'f', 1.000000,1.000000,1.000000,1.000000,0,0 }, 
+{'m', 29.00, 128.80, 0, 0, 0, 0}, 
+{'c', 29.00, 128.80, 28.80 ,127.00, 30.60, 127.80}, 
+{'c', 32.40, 128.60, 205.80 ,115.60, 229.40, 148.00}, 
+{'f', 1.000000,1.000000,1.000000,1.000000,0,0 }, 
+{'m', 39.00, 124.00, 0, 0, 0, 0}, 
+{'c', 39.00, 124.00, 38.80 ,122.20, 40.60, 123.00}, 
+{'c', 42.40, 123.80, 164.60 ,85.20, 188.20, 117.60}, 
+{'f', 1.000000,1.000000,1.000000,1.000000,0,0 }, 
+{'m', -19.00, 146.80, 0, 0, 0, 0}, 
+{'c', -19.00, 146.80, -19.20 ,145.00, -17.40, 145.80}, 
+{'c', -15.60, 146.60, 2.20 ,148.80, 4.20, 187.60}, 
+{'f', 1.000000,1.000000,1.000000,1.000000,0,0 }, 
+{'m', -27.80, 148.40, 0, 0, 0, 0}, 
+{'c', -27.80, 148.40, -28.00 ,146.60, -26.20, 147.40}, 
+{'c', -24.40, 148.20, -10.20 ,143.60, -13.00, 182.40}, 
+{'f', 1.000000,1.000000,1.000000,1.000000,0,0 }, 
+{'m', -35.80, 148.80, 0, 0, 0, 0}, 
+{'c', -35.80, 148.80, -36.00 ,147.00, -34.20, 147.80}, 
+{'c', -32.40, 148.60, -17.00 ,149.20, -29.40, 171.60}, 
+{'f', 1.000000,1.000000,1.000000,1.000000,0,0 }, 
+{'m', 11.53, 104.47, 0, 0, 0, 0}, 
+{'c', 11.53, 104.47, 11.08 ,106.46, 12.63, 105.25}, 
+{'c', 28.70, 92.62, 61.14 ,33.72, 116.83, 28.09}, 
+{'f', 1.000000,1.000000,1.000000,1.000000,0,0 }, 
+{'m', 22.73, 102.67, 0, 0, 0, 0}, 
+{'c', 22.73, 102.67, 21.36 ,101.47, 23.23, 100.85}, 
+{'c', 25.10, 100.22, 137.54 ,27.72, 176.83, 35.69}, 
+{'f', 1.000000,1.000000,1.000000,1.000000,0,0 }, 
+{'m', 1.89, 108.77, 0, 0, 0, 0}, 
+{'c', 1.89, 108.77, 1.38 ,110.37, 3.09, 109.39}, 
+{'c', 12.06, 104.27, 15.68 ,47.06, 59.25, 45.80}, 
+{'f', 1.000000,1.000000,1.000000,1.000000,0,0 }, 
+{'m', -18.04, 119.79, 0, 0, 0, 0}, 
+{'c', -18.04, 119.79, -19.11 ,121.08, -17.16, 120.83}, 
+{'c', -6.92, 119.49, 14.49 ,78.22, 58.93, 83.30}, 
+{'f', 1.000000,1.000000,1.000000,1.000000,0,0 }, 
+{'m', -6.80, 113.67, 0, 0, 0, 0}, 
+{'c', -6.80, 113.67, -7.61 ,115.14, -5.74, 114.51}, 
+{'c', 4.06, 111.24, 17.14 ,66.62, 61.73, 63.08}, 
+{'f', 1.000000,1.000000,1.000000,1.000000,0,0 }, 
+{'m', -25.08, 124.91, 0, 0, 0, 0}, 
+{'c', -25.08, 124.91, -25.95 ,125.95, -24.37, 125.75}, 
+{'c', -16.07, 124.67, 1.27 ,91.24, 37.26, 95.35}, 
+{'f', 1.000000,1.000000,1.000000,1.000000,0,0 }, 
+{'m', -32.68, 130.82, 0, 0, 0, 0}, 
+{'c', -32.68, 130.82, -33.68 ,131.87, -32.09, 131.75}, 
+{'c', -27.92, 131.44, 2.71 ,98.36, 21.18, 113.86}, 
+{'f', 1.000000,1.000000,1.000000,1.000000,0,0 }, 
+{'m', 36.85, 98.90, 0, 0, 0, 0}, 
+{'c', 36.85, 98.90, 35.65 ,97.54, 37.59, 97.16}, 
+{'c', 39.52, 96.77, 160.22 ,39.06, 198.18, 51.93}, 
+{'f', 1.000000,1.000000,1.000000,1.000000,0,0 }, 
+{'m', 3.40, 163.20, 0, 0, 0, 0}, 
+{'c', 3.40, 163.20, 3.20 ,161.40, 5.00, 162.20}, 
+{'c', 6.80, 163.00, 22.20 ,163.60, 9.80, 186.00}, 
+{'f', 1.000000,1.000000,1.000000,1.000000,0,0 }, 
+{'m', 13.80, 161.60, 0, 0, 0, 0}, 
+{'c', 13.80, 161.60, 13.60 ,159.80, 15.40, 160.60}, 
+{'c', 17.20, 161.40, 35.00 ,163.60, 37.00, 202.40}, 
+{'f', 1.000000,1.000000,1.000000,1.000000,0,0 }, 
+{'m', 20.60, 160.00, 0, 0, 0, 0}, 
+{'c', 20.60, 160.00, 20.40 ,158.20, 22.20, 159.00}, 
+{'c', 24.00, 159.80, 48.60 ,163.20, 72.20, 195.60}, 
+{'f', 1.000000,1.000000,1.000000,1.000000,0,0 }, 
+{'m', 28.23, 157.97, 0, 0, 0, 0}, 
+{'c', 28.23, 157.97, 27.79 ,156.21, 29.68, 156.77}, 
+{'c', 31.57, 157.32, 52.00 ,155.42, 90.10, 189.60}, 
+{'f', 1.000000,1.000000,1.000000,1.000000,0,0 }, 
+{'m', 38.62, 153.57, 0, 0, 0, 0}, 
+{'c', 38.62, 153.57, 38.19 ,151.81, 40.08, 152.37}, 
+{'c', 41.97, 152.92, 76.80 ,157.42, 128.50, 192.40}, 
+{'f', 1.000000,1.000000,1.000000,1.000000,0,0 }, 
+{'m', -1.80, 142.00, 0, 0, 0, 0}, 
+{'c', -1.80, 142.00, -2.00 ,140.20, -0.20, 141.00}, 
+{'c', 1.60, 141.80, 55.00 ,144.40, 85.40, 171.20}, 
+{'f', 1.000000,1.000000,1.000000,1.000000,0,0 }, 
+{'m', -11.80, 146.00, 0, 0, 0, 0}, 
+{'c', -11.80, 146.00, -12.00 ,144.20, -10.20, 145.00}, 
+{'c', -8.40, 145.80, 16.20 ,149.20, 39.80, 181.60}, 
+{'f', 1.000000,1.000000,1.000000,1.000000,0,0 }, 
+{'m', 49.50, 148.96, 0, 0, 0, 0}, 
+{'c', 49.50, 148.96, 48.94 ,147.24, 50.86, 147.66}, 
+{'c', 52.79, 148.07, 87.86 ,150.00, 141.98, 181.10}, 
+{'f', 1.000000,1.000000,1.000000,1.000000,0,0 }, 
+{'m', 57.90, 146.56, 0, 0, 0, 0}, 
+{'c', 57.90, 146.56, 57.34 ,144.84, 59.26, 145.25}, 
+{'c', 61.19, 145.67, 96.26 ,147.60, 150.38, 178.70}, 
+{'f', 1.000000,1.000000,1.000000,1.000000,0,0 }, 
+{'m', 67.50, 141.56, 0, 0, 0, 0}, 
+{'c', 67.50, 141.56, 66.94 ,139.84, 68.86, 140.25}, 
+{'c', 70.79, 140.67, 113.86 ,145.00, 203.58, 179.30}, 
+{'f', 1.000000,1.000000,1.000000,1.000000,0,0 }, 
+{'m', -43.80, 148.40, 0, 0, 0, 0}, 
+{'c', -43.80, 148.40, -38.60 ,148.00, -39.80, 149.60}, 
+{'c', -41.00, 151.20, -43.40 ,150.40, -43.40, 150.40}, 
+{'f', 0.000000,0.000000,0.000000,1.000000,0,0 }, 
+{'m', -13.00, 162.40, 0, 0, 0, 0}, 
+{'c', -13.00, 162.40, -7.80 ,162.00, -9.00, 163.60}, 
+{'c', -10.20, 165.20, -12.60 ,164.40, -12.60, 164.40}, 
+{'f', 0.000000,0.000000,0.000000,1.000000,0,0 }, 
+{'m', -21.80, 162.00, 0, 0, 0, 0}, 
+{'c', -21.80, 162.00, -16.60 ,161.60, -17.80, 163.20}, 
+{'c', -19.00, 164.80, -21.40 ,164.00, -21.40, 164.00}, 
+{'f', 0.000000,0.000000,0.000000,1.000000,0,0 }, 
+{'m', -117.17, 150.18, 0, 0, 0, 0}, 
+{'c', -117.17, 150.18, -112.12 ,151.50, -113.78, 152.62}, 
+{'c', -115.44, 153.74, -117.45 ,152.20, -117.45, 152.20}, 
+{'f', 0.000000,0.000000,0.000000,1.000000,0,0 }, 
+{'m', -115.17, 140.58, 0, 0, 0, 0}, 
+{'c', -115.17, 140.58, -110.12 ,141.91, -111.78, 143.02}, 
+{'c', -113.44, 144.14, -115.45 ,142.60, -115.45, 142.60}, 
+{'f', 0.000000,0.000000,0.000000,1.000000,0,0 }, 
+{'m', -122.37, 136.18, 0, 0, 0, 0}, 
+{'c', -122.37, 136.18, -117.32 ,137.50, -118.98, 138.62}, 
+{'c', -120.64, 139.74, -122.65 ,138.20, -122.65, 138.20}, 
+{'f', 0.000000,0.000000,0.000000,1.000000,0,0 }, 
+{'m', -42.60, 211.20, 0, 0, 0, 0}, 
+{'c', -42.60, 211.20, -44.20 ,211.20, -48.20, 213.20}, 
+{'c', -50.20, 213.20, -61.40 ,216.80, -67.00, 226.80}, 
+{'f', 0.804000,0.804000,0.804000,1.000000,0,0 }, 
+{'m', 45.12, 303.85, 0, 0, 0, 0}, 
+{'c', 45.26, 304.11, 45.31 ,304.52, 45.60, 304.54}, 
+{'c', 46.26, 304.58, 47.49 ,304.88, 47.37, 304.25}, 
+{'c', 46.52, 299.94, 45.65 ,295.00, 41.52, 293.20}, 
+{'c', 40.88, 292.92, 39.43 ,293.33, 39.36, 294.21}, 
+{'c', 39.23, 295.74, 39.12 ,297.09, 39.42, 298.55}, 
+{'c', 39.73, 299.98, 41.88 ,299.99, 42.80, 298.60}, 
+{'f', 0.804000,0.804000,0.804000,1.000000,0,0 }, 
+{'m', 34.04, 308.58, 0, 0, 0, 0}, 
+{'c', 34.79, 309.99, 34.66 ,311.85, 36.07, 312.42}, 
+{'c', 36.81, 312.71, 38.66 ,311.74, 38.25, 310.66}, 
+{'c', 37.44, 308.60, 37.06 ,306.36, 35.67, 304.55}, 
+{'c', 35.47, 304.29, 35.71 ,303.75, 35.55, 303.43}, 
+{'c', 34.95, 302.21, 33.81 ,301.47, 32.40, 301.80}, 
+{'c', 31.29, 304.00, 32.43 ,306.13, 33.95, 307.84}, 
+{'f', 0.804000,0.804000,0.804000,1.000000,0,0 }, 
+{'m', -5.56, 303.39, 0, 0, 0, 0}, 
+{'c', -5.67, 303.01, -5.71 ,302.55, -5.54, 302.23}, 
+{'c', -5.01, 301.20, -4.22 ,300.07, -4.56, 299.05}, 
+{'c', -4.91, 298.00, -6.02 ,298.18, -6.67, 298.75}, 
+{'c', -7.81, 299.74, -7.86 ,301.57, -8.55, 302.93}, 
+{'c', -8.74, 303.31, -8.69 ,303.89, -9.13, 304.28}, 
+{'c', -9.61, 304.70, -10.05 ,306.22, -9.95, 306.79}, 
+{'c', -9.90, 307.11, -10.08 ,317.01, -9.86, 316.75}, 
+{'c', -9.24, 316.02, -6.19 ,306.28, -6.12, 305.39}, 
+{'f', 0.804000,0.804000,0.804000,1.000000,0,0 }, 
+{'m', -31.20, 296.60, 0, 0, 0, 0}, 
+{'c', -28.57, 294.10, -25.78 ,291.14, -26.22, 287.43}, 
+{'c', -26.34, 286.45, -28.11 ,286.98, -28.30, 287.82}, 
+{'c', -29.10, 291.45, -31.14 ,294.11, -33.71, 296.50}, 
+{'c', -35.90, 298.55, -37.77 ,304.89, -38.00, 305.40}, 
+{'f', 0.804000,0.804000,0.804000,1.000000,0,0 }, 
+{'m', -44.78, 290.63, 0, 0, 0, 0}, 
+{'c', -44.25, 290.26, -44.55 ,289.77, -44.34, 289.44}, 
+{'c', -43.38, 287.98, -42.08 ,286.74, -42.07, 285.00}, 
+{'c', -42.06, 284.72, -42.44 ,284.41, -42.78, 284.64}, 
+{'c', -43.05, 284.82, -43.40 ,284.95, -43.50, 285.08}, 
+{'c', -45.53, 287.53, -46.93 ,290.20, -48.38, 293.01}, 
+{'c', -48.56, 293.37, -49.70 ,297.86, -49.39, 297.97}, 
+{'c', -49.15, 298.06, -47.43 ,293.88, -47.22, 293.76}, 
+{'f', 0.804000,0.804000,0.804000,1.000000,0,0 }, 
+{'m', -28.04, 310.18, 0, 0, 0, 0}, 
+{'c', -27.60, 309.31, -26.02 ,308.11, -26.14, 307.22}, 
+{'c', -26.25, 306.29, -25.79 ,304.85, -26.70, 305.54}, 
+{'c', -27.95, 306.48, -31.40 ,307.83, -31.67, 313.64}, 
+{'f', 0.804000,0.804000,0.804000,1.000000,0,0 }, 
+{'m', -13.60, 293.00, 0, 0, 0, 0}, 
+{'c', -13.20, 292.33, -12.49 ,292.81, -12.03, 292.54}, 
+{'c', -11.38, 292.17, -10.77 ,291.61, -10.48, 290.96}, 
+{'c', -9.51, 288.81, -7.74 ,287.00, -7.60, 284.60}, 
+{'c', -9.09, 283.20, -9.77 ,285.24, -10.40, 286.20}, 
+{'c', -11.72, 284.55, -12.72 ,286.43, -14.02, 286.95}, 
+{'c', -14.09, 286.98, -14.30 ,286.63, -14.38, 286.65}, 
+{'c', -15.56, 287.10, -16.24 ,288.18, -17.23, 288.96}, 
+{'c', -17.41, 289.09, -17.81 ,288.91, -17.96, 289.05}, 
+{'c', -18.61, 289.65, -19.58 ,289.98, -19.86, 290.66}, 
+{'c', -20.97, 293.36, -24.11 ,295.46, -26.00, 303.00}, 
+{'c', -25.62, 303.91, -21.49 ,296.36, -21.00, 295.66}, 
+{'c', -20.16, 294.46, -20.05 ,297.32, -18.77, 296.66}, 
+{'c', -18.72, 296.63, -18.53 ,296.87, -18.40, 297.00}, 
+{'c', -18.21, 296.72, -17.99 ,296.49, -17.60, 296.60}, 
+{'c', -17.60, 296.20, -17.73 ,295.64, -17.53, 295.49}, 
+{'c', -16.30, 294.51, -16.38 ,293.44, -15.60, 292.20}, 
+{'f', 0.804000,0.804000,0.804000,1.000000,0,0 }, 
+{'m', 46.20, 347.40, 0, 0, 0, 0}, 
+{'c', 46.20, 347.40, 53.60 ,327.00, 49.20, 315.80}, 
+{'c', 49.20, 315.80, 60.60 ,337.40, 56.00, 348.60}, 
+{'c', 56.00, 348.60, 55.60 ,338.20, 51.60, 333.20}, 
+{'f', 0.804000,0.804000,0.804000,1.000000,0,0 }, 
+{'m', 31.40, 344.80, 0, 0, 0, 0}, 
+{'c', 31.40, 344.80, 36.80 ,336.00, 28.80, 317.60}, 
+{'c', 28.80, 317.60, 28.00 ,338.00, 21.20, 349.00}, 
+{'f', 0.804000,0.804000,0.804000,1.000000,0,0 }, 
+{'m', 21.40, 342.80, 0, 0, 0, 0}, 
+{'c', 21.40, 342.80, 21.20 ,322.80, 21.60, 319.80}, 
+{'c', 21.60, 319.80, 17.80 ,336.40, 7.60, 346.00}, 
+{'f', 0.804000,0.804000,0.804000,1.000000,0,0 }, 
+{'m', 11.80, 310.80, 0, 0, 0, 0}, 
+{'c', 11.80, 310.80, 17.80 ,324.40, 7.80, 342.80}, 
+{'c', 7.80, 342.80, 14.20 ,330.60, 9.40, 323.60}, 
+{'f', 0.804000,0.804000,0.804000,1.000000,0,0 }, 
+{'m', -7.40, 342.40, 0, 0, 0, 0}, 
+{'c', -7.40, 342.40, -8.40 ,326.80, -6.60, 324.60}, 
+{'c', -6.60, 324.60, -6.40 ,318.20, -6.80, 317.20}, 
+{'c', -6.80, 317.20, -2.80 ,311.00, -2.60, 318.40}, 
+{'c', -2.60, 318.40, -1.20 ,326.20, 1.60, 330.80}, 
+{'c', 1.60, 330.80, 5.20 ,336.20, 5.00, 342.60}, 
+{'f', 0.804000,0.804000,0.804000,1.000000,0,0 }, 
+{'m', -11.00, 314.80, 0, 0, 0, 0}, 
+{'c', -11.00, 314.80, -17.60 ,325.60, -19.40, 344.60}, 
+{'c', -19.40, 344.60, -20.80 ,338.40, -17.00, 324.00}, 
+{'f', 0.804000,0.804000,0.804000,1.000000,0,0 }, 
+{'m', -32.80, 334.60, 0, 0, 0, 0}, 
+{'c', -32.80, 334.60, -27.80 ,329.20, -26.40, 324.20}, 
+{'c', -26.40, 324.20, -22.80 ,308.40, -29.20, 317.00}, 
+{'c', -29.20, 317.00, -29.00 ,325.00, -37.20, 332.40}, 
+{'f', 0.804000,0.804000,0.804000,1.000000,0,0 }, 
+{'m', -38.60, 329.60, 0, 0, 0, 0}, 
+{'c', -38.60, 329.60, -35.20 ,312.20, -34.40, 311.40}, 
+{'c', -34.40, 311.40, -32.60 ,308.00, -35.40, 311.20}, 
+{'c', -35.40, 311.20, -44.20 ,330.40, -48.20, 337.00}, 
+{'f', 0.804000,0.804000,0.804000,1.000000,0,0 }, 
+{'m', -44.40, 313.00, 0, 0, 0, 0}, 
+{'c', -44.40, 313.00, -32.80 ,290.60, -54.60, 316.40}, 
+{'f', 0.804000,0.804000,0.804000,1.000000,0,0 }, 
+{'m', -59.80, 298.40, 0, 0, 0, 0}, 
+{'c', -59.80, 298.40, -55.00 ,279.60, -52.40, 279.80}, 
+{'c', -52.40, 279.80, -44.20 ,270.80, -50.80, 281.40}, 
+{'c', -50.80, 281.40, -56.80 ,291.00, -56.20, 300.80}, 
+{'f', 0.804000,0.804000,0.804000,1.000000,0,0 }, 
+{'m', 270.50, 287.00, 0, 0, 0, 0}, 
+{'c', 270.50, 287.00, 258.50 ,277.00, 256.00, 273.50}, 
+{'c', 256.00, 273.50, 269.50 ,292.00, 269.50, 299.00}, 
+{'f', 0.804000,0.804000,0.804000,1.000000,0,0 }, 
+{'m', 276.00, 265.00, 0, 0, 0, 0}, 
+{'c', 276.00, 265.00, 255.00 ,250.00, 251.50, 242.50}, 
+{'c', 251.50, 242.50, 278.00 ,272.00, 278.00, 276.50}, 
+{'f', 0.804000,0.804000,0.804000,1.000000,0,0 }, 
+{'m', 293.00, 111.00, 0, 0, 0, 0}, 
+{'c', 293.00, 111.00, 281.00 ,103.00, 279.50, 105.00}, 
+{'c', 279.50, 105.00, 290.00 ,111.50, 292.50, 120.00}, 
+{'f', 0.804000,0.804000,0.804000,1.000000,0,0 }, 
+{'m', 301.50, 191.50, 0, 0, 0, 0}, 
+{'l', 284.00, 179.50, 0, 0, 0, 0}, 
+{'c', 284.00, 179.50, 303.00 ,196.50, 303.50, 200.50}, 
+{'f', 0.804000,0.804000,0.804000,1.000000,0,0 }, 
+{'m', -89.25, 169.00, 0, 0, 0, 0}, 
+{'f', 0.804000,0.804000,0.804000,1.000000,0,0 }, 
+{'l', -67.25, 173.75, 0, 0, 0, 0}, 
+{'m', -39.00, 331.00, 0, 0, 0, 0}, 
+{'f', 0.804000,0.804000,0.804000,1.000000,0,0 }, 
+{'m', -33.50, 336.00, 0, 0, 0, 0}, 
+{'f', 0.804000,0.804000,0.804000,1.000000,0,0 }, 
+{'m', 20.50, 344.50, 0, 0, 0, 0}, 
+{'f', 0.804000,0.804000,0.804000,1.000000,0,0 }, 
+{'m', 301.50, 191.50, 0, 0, 0, 0}, 
+{'l', 284.00, 179.50, 0, 0, 0, 0}, 
+{'c', 284.00, 179.50, 303.00 ,196.50, 303.50, 200.50}, 
+{'f', 0.804000,0.804000,0.804000,1.000000,0,0 }, 
+{'m', -89.25, 169.00, 0, 0, 0, 0}, 
+{'f', 0.804000,0.804000,0.804000,1.000000,0,0 }, 
+{'l', -67.25, 173.75, 0, 0, 0, 0}, 
+{'m', -39.00, 331.00, 0, 0, 0, 0}, 
+{'f', 0.804000,0.804000,0.804000,1.000000,0,0 }, 
+{'m', -33.50, 336.00, 0, 0, 0, 0}, 
+{'f', 0.804000,0.804000,0.804000,1.000000,0,0 }, 
+{'m', 20.50, 344.50, 0, 0, 0, 0}, 
+{'f', 0.804000,0.804000,0.804000,1.000000,0,0 }, 
+{'m', 301.50, 191.50, 0, 0, 0, 0}, 
+{'l', 284.00, 179.50, 0, 0, 0, 0}, 
+{'c', 284.00, 179.50, 303.00 ,196.50, 303.50, 200.50}, 
+{'f', 0.804000,0.804000,0.804000,1.000000,0,0 }, 
+{'m', -89.25, 169.00, 0, 0, 0, 0}, 
+{'f', 0.804000,0.804000,0.804000,1.000000,0,0 }, 
+{'l', -67.25, 173.75, 0, 0, 0, 0}, 
+{'m', -39.00, 331.00, 0, 0, 0, 0}, 
+{'f', 0.804000,0.804000,0.804000,1.000000,0,0 }, 
+{'m', -33.50, 336.00, 0, 0, 0, 0}, 
+{'f', 0.804000,0.804000,0.804000,1.000000,0,0 }, 
+{'m', 20.50, 344.50, 0, 0, 0, 0}, 
+{'f', 0.804000,0.804000,0.804000,1.000000,0,0 }};
diff --git a/test/tighten-bounds.c b/test/tighten-bounds.c
new file mode 100644 (file)
index 0000000..02fc81a
--- /dev/null
@@ -0,0 +1,172 @@
+/*
+ * Copyright © 2011 Uli Schlachter
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Uli Schlachter <psychon@znc.in>
+ */
+
+#include "cairo-test.h"
+
+static void path_none (cairo_t *cr, int size)
+{
+}
+
+static void path_box (cairo_t *cr, int size)
+{
+    cairo_rectangle (cr, 0, 0, size, size);
+}
+
+static void path_box_unaligned (cairo_t *cr, int size)
+{
+    cairo_rectangle (cr, 0.5, 0.5, size - 1, size - 1);
+}
+
+static void path_triangle (cairo_t *cr, int size)
+{
+    cairo_move_to (cr, 0, 0);
+    cairo_line_to (cr, size/2, size);
+    cairo_line_to (cr, size, 0);
+    cairo_close_path (cr);
+}
+
+static void path_circle (cairo_t *cr, int size)
+{
+    cairo_arc (cr, size / 2.0, size / 2.0, size / 2.0, 0, 2 * M_PI);
+}
+
+static void (* const path_funcs[])(cairo_t *cr, int size) = {
+    path_none,
+    path_box,
+    path_box_unaligned,
+    path_triangle,
+    path_circle
+};
+
+#define SIZE 20
+#define PAD 2
+#define TYPES 6
+/* All-clipped is boring, thus we skip path_none for clipping */
+#define CLIP_OFFSET 1
+#define IMAGE_WIDTH ((ARRAY_LENGTH (path_funcs) - CLIP_OFFSET) * TYPES * (SIZE + PAD) - PAD)
+#define IMAGE_HEIGHT (ARRAY_LENGTH (path_funcs) * (SIZE + PAD) - PAD)
+
+static void
+draw_idx (cairo_t *cr, int i, int j, int type)
+{
+    cairo_bool_t little_path = type & (1 << 0);
+    cairo_bool_t empty_clip;
+    cairo_bool_t little_clip;
+
+    /* The lowest bit controls the path, the rest the clip */
+    little_path = type & 1;
+
+    /* We don't want the combination "empty_clip = TRUE, little_clip = FALSE"
+     * (== all clipped).
+     */
+    switch (type >> 1)
+    {
+    case 0:
+       empty_clip = FALSE;
+       little_clip = FALSE;
+       break;
+    case 1:
+       empty_clip = FALSE;
+       little_clip = TRUE;
+       break;
+    case 2:
+       empty_clip = TRUE;
+       little_clip = TRUE;
+       break;
+    default:
+       return;
+    }
+
+    cairo_save (cr);
+
+    /* Thanks to the fill rule, drawing something twice removes it again */
+    cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD);
+
+    path_funcs[i] (cr, SIZE);
+    if (empty_clip)
+       path_funcs[i] (cr, SIZE);
+    if (little_clip)
+    {
+       cairo_save (cr);
+       cairo_translate (cr, SIZE / 4, SIZE / 4);
+       path_funcs[i] (cr, SIZE / 2);
+       cairo_restore (cr);
+    }
+    cairo_clip (cr);
+
+    path_funcs[j] (cr, SIZE);
+    path_funcs[j] (cr, SIZE);
+    if (little_path)
+    {
+       /* Draw the object again in the center of itself */
+       cairo_save (cr);
+       cairo_translate (cr, SIZE / 4, SIZE / 4);
+       path_funcs[j] (cr, SIZE / 2);
+       cairo_restore (cr);
+    }
+    cairo_fill (cr);
+    cairo_restore (cr);
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    size_t i, j, k;
+
+    cairo_set_source_rgb (cr, 0, 1, 0);
+    cairo_paint (cr);
+
+    /* Set an unbounded operator so that we can see how accurate the bounded
+     * extents were.
+     */
+    cairo_set_operator (cr, CAIRO_OPERATOR_IN);
+    cairo_set_source_rgb (cr, 1, 1, 1);
+
+    for (j = 0; j < ARRAY_LENGTH (path_funcs); j++) {
+       cairo_save (cr);
+       for (i = CLIP_OFFSET; i < ARRAY_LENGTH (path_funcs); i++) {
+           for (k = 0; k < TYPES; k++) {
+               cairo_save (cr);
+               cairo_rectangle (cr, 0, 0, SIZE, SIZE);
+               cairo_clip (cr);
+               draw_idx (cr, i, j, k);
+               cairo_restore (cr);
+               cairo_translate (cr, SIZE + PAD, 0);
+           }
+       }
+       cairo_restore (cr);
+       cairo_translate (cr, 0, SIZE + PAD);
+    }
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (tighten_bounds,
+           "Tests that we tighten the bounds after tessellation.",
+           "fill", /* keywords */
+           NULL, /* requirements */
+           IMAGE_WIDTH, IMAGE_HEIGHT,
+           NULL, draw)
diff --git a/test/toy-font-face.c b/test/toy-font-face.c
new file mode 100644 (file)
index 0000000..cbebf84
--- /dev/null
@@ -0,0 +1,149 @@
+/*
+ * Copyright © 2005,2008 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ *         Behdad Esfahbod <behdad@behdad.org>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "cairo-test.h"
+
+#include <cairo.h>
+#include <assert.h>
+#include <string.h>
+
+#if   CAIRO_HAS_WIN32_FONT
+#define CAIRO_FONT_FAMILY_DEFAULT "Arial"
+#elif CAIRO_HAS_QUARTZ_FONT
+#define CAIRO_FONT_FAMILY_DEFAULT "Helvetica"
+#elif CAIRO_HAS_FT_FONT
+#define CAIRO_FONT_FAMILY_DEFAULT ""
+#else
+#define CAIRO_FONT_FAMILY_DEFAULT "@cairo:"
+#endif
+
+
+static cairo_test_status_t
+preamble (cairo_test_context_t *ctx)
+{
+    cairo_t *cr;
+    cairo_surface_t *surface;
+    cairo_font_face_t *font_face;
+    cairo_status_t status;
+
+    surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, 0, 0);
+    cr = cairo_create (surface);
+    cairo_surface_destroy (surface);
+
+    font_face = cairo_font_face_reference (cairo_get_font_face (cr));
+    assert (cairo_font_face_get_type (font_face) == CAIRO_FONT_TYPE_TOY);
+    assert (cairo_toy_font_face_get_family (font_face) != NULL);
+    assert (cairo_toy_font_face_get_slant (font_face) == CAIRO_FONT_SLANT_NORMAL);
+    assert (cairo_toy_font_face_get_weight (font_face) == CAIRO_FONT_WEIGHT_NORMAL);
+    status = cairo_font_face_status(font_face);
+    cairo_font_face_destroy (font_face);
+
+    if (status)
+       return cairo_test_status_from_status (ctx, status);
+
+    cairo_select_font_face (cr,
+                           "bizarre",
+                           CAIRO_FONT_SLANT_OBLIQUE,
+                           CAIRO_FONT_WEIGHT_BOLD);
+    font_face = cairo_font_face_reference (cairo_get_font_face (cr));
+    assert (cairo_font_face_get_type (font_face) == CAIRO_FONT_TYPE_TOY);
+    assert (0 == (strcmp) (cairo_toy_font_face_get_family (font_face), "bizarre"));
+    assert (cairo_toy_font_face_get_slant (font_face) == CAIRO_FONT_SLANT_OBLIQUE);
+    assert (cairo_toy_font_face_get_weight (font_face) == CAIRO_FONT_WEIGHT_BOLD);
+    status = cairo_font_face_status(font_face);
+    cairo_font_face_destroy (font_face);
+
+    if (status)
+       return cairo_test_status_from_status (ctx, status);
+
+    font_face = cairo_toy_font_face_create ("bozarre",
+                                           CAIRO_FONT_SLANT_OBLIQUE,
+                                           CAIRO_FONT_WEIGHT_BOLD);
+    assert (cairo_font_face_get_type (font_face) == CAIRO_FONT_TYPE_TOY);
+    assert (0 == (strcmp) (cairo_toy_font_face_get_family (font_face), "bozarre"));
+    assert (cairo_toy_font_face_get_slant (font_face) == CAIRO_FONT_SLANT_OBLIQUE);
+    assert (cairo_toy_font_face_get_weight (font_face) == CAIRO_FONT_WEIGHT_BOLD);
+    status = cairo_font_face_status(font_face);
+    cairo_font_face_destroy (font_face);
+
+    if (status)
+       return cairo_test_status_from_status (ctx, status);
+
+    font_face = cairo_toy_font_face_create (NULL,
+                                           CAIRO_FONT_SLANT_OBLIQUE,
+                                           CAIRO_FONT_WEIGHT_BOLD);
+    assert (cairo_font_face_get_type (font_face) == CAIRO_FONT_TYPE_TOY);
+    assert (0 == (strcmp) (cairo_toy_font_face_get_family (font_face), CAIRO_FONT_FAMILY_DEFAULT));
+    assert (cairo_toy_font_face_get_slant (font_face) == CAIRO_FONT_SLANT_NORMAL);
+    assert (cairo_toy_font_face_get_weight (font_face) == CAIRO_FONT_WEIGHT_NORMAL);
+    assert (cairo_font_face_status(font_face) == CAIRO_STATUS_NULL_POINTER);
+    cairo_font_face_destroy (font_face);
+
+    font_face = cairo_toy_font_face_create ("\xff",
+                                           CAIRO_FONT_SLANT_OBLIQUE,
+                                           CAIRO_FONT_WEIGHT_BOLD);
+    assert (cairo_font_face_get_type (font_face) == CAIRO_FONT_TYPE_TOY);
+    assert (0 == (strcmp) (cairo_toy_font_face_get_family (font_face), CAIRO_FONT_FAMILY_DEFAULT));
+    assert (cairo_toy_font_face_get_slant (font_face) == CAIRO_FONT_SLANT_NORMAL);
+    assert (cairo_toy_font_face_get_weight (font_face) == CAIRO_FONT_WEIGHT_NORMAL);
+    assert (cairo_font_face_status(font_face) == CAIRO_STATUS_INVALID_STRING);
+    cairo_font_face_destroy (font_face);
+
+    font_face = cairo_toy_font_face_create ("sans",
+                                           -1,
+                                           CAIRO_FONT_WEIGHT_BOLD);
+    assert (cairo_font_face_get_type (font_face) == CAIRO_FONT_TYPE_TOY);
+    assert (0 == (strcmp) (cairo_toy_font_face_get_family (font_face), CAIRO_FONT_FAMILY_DEFAULT));
+    assert (cairo_toy_font_face_get_slant (font_face) == CAIRO_FONT_SLANT_NORMAL);
+    assert (cairo_toy_font_face_get_weight (font_face) == CAIRO_FONT_WEIGHT_NORMAL);
+    assert (cairo_font_face_status(font_face) == CAIRO_STATUS_INVALID_SLANT);
+    cairo_font_face_destroy (font_face);
+
+    font_face = cairo_toy_font_face_create ("sans",
+                                           CAIRO_FONT_SLANT_OBLIQUE,
+                                           -1);
+    assert (cairo_font_face_get_type (font_face) == CAIRO_FONT_TYPE_TOY);
+    assert (0 == (strcmp) (cairo_toy_font_face_get_family (font_face), CAIRO_FONT_FAMILY_DEFAULT));
+    assert (cairo_toy_font_face_get_slant (font_face) == CAIRO_FONT_SLANT_NORMAL);
+    assert (cairo_toy_font_face_get_weight (font_face) == CAIRO_FONT_WEIGHT_NORMAL);
+    assert (cairo_font_face_status(font_face) == CAIRO_STATUS_INVALID_WEIGHT);
+    cairo_font_face_destroy (font_face);
+
+    cairo_destroy (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (toy_font_face,
+           "Check the construction of 'toy' font faces",
+           "font, api", /* keywords */
+           NULL, /* requirements */
+           0, 0,
+           preamble, NULL)
diff --git a/test/transforms.c b/test/transforms.c
new file mode 100644 (file)
index 0000000..17b1396
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Carl Worth <cworth@cworth.org>
+ */
+
+#include "cairo-test.h"
+
+#define WIDTH 45
+#define HEIGHT 30
+
+static void
+draw_L_shape (cairo_t *cr)
+{
+    cairo_move_to (cr, 0, 0);
+    cairo_rel_line_to (cr, 0, 10);
+    cairo_rel_line_to (cr, 5, 0);
+
+    cairo_save (cr);
+    cairo_identity_matrix (cr);
+    cairo_set_line_width (cr, 2.0);
+    cairo_stroke (cr);
+    cairo_restore (cr);
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    /* We draw in the default black, so paint white first. */
+    cairo_save (cr);
+    cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); /* white */
+    cairo_paint (cr);
+    cairo_restore (cr);
+
+    cairo_translate (cr, 5, 5);
+
+    draw_L_shape (cr);
+
+    cairo_translate (cr, 10, 0);
+
+    cairo_save (cr);
+    {
+       cairo_scale (cr, 2, 2);
+       draw_L_shape (cr);
+    }
+    cairo_restore (cr);
+
+    cairo_translate (cr, 15, 0);
+
+    cairo_save (cr);
+    {
+       cairo_rotate (cr, M_PI / 2.0);
+       draw_L_shape (cr);
+    }
+    cairo_restore (cr);
+
+    cairo_translate (cr, 5, 0);
+
+    cairo_save (cr);
+    {
+       cairo_matrix_t skew_y = {
+           1, -1,
+           0,  1,
+           0,  0
+       };
+       cairo_transform (cr, &skew_y);
+       draw_L_shape (cr);
+    }
+    cairo_restore (cr);
+
+    cairo_translate (cr, 5, 10);
+
+    cairo_save (cr);
+    {
+       cairo_matrix_t skew_x = {
+            1.0, 0.0,
+           -0.5, 1.0,
+            0.0, 0.0
+       };
+       cairo_transform (cr, &skew_x);
+       draw_L_shape (cr);
+    }
+    cairo_restore (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (transforms,
+           "Test various transformations.",
+           "transforms, api", /* keywords */
+           NULL, /* requirements */
+           WIDTH, HEIGHT,
+           NULL, draw)
diff --git a/test/translate-show-surface.c b/test/translate-show-surface.c
new file mode 100644 (file)
index 0000000..9f7af8d
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ */
+
+/* Bug history
+ *
+ * 2005-04-11 Carl Worth <cworth@cworth.org>
+ *
+ *   It appears that calling cairo_show_surface after cairo_translate
+ *   somehow applies the translation twice to the surface being
+ *   shown. This is pretty easy to demonstrate by bringing up xsvg on
+ *   an SVG file with an <image> and panning around a bit with the
+ *   arrow keys.
+ *
+ *   This is almost certainly a regression, and I suspect there may be
+ *   some interaction with the fix for move-to-show-surface.
+ *
+ * 2005-04-12 Carl Worth <cworth@cworth.org>
+ *
+ *   I committed a fix for this bug today.
+ */
+
+#include "cairo-test.h"
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_surface_t *surface;
+    uint32_t colors[4] = {
+       0xffffffff, 0xffff0000,
+       0xff00ff00, 0xff0000ff
+    };
+    int i;
+
+    for (i=0; i < 4; i++) {
+       surface = cairo_image_surface_create_for_data ((unsigned char *) &colors[i],
+                                                      CAIRO_FORMAT_RGB24,
+                                                      1, 1, 4);
+       cairo_save (cr);
+       {
+           cairo_translate (cr, i % 2, i / 2);
+           cairo_set_source_surface (cr, surface, 0, 0);
+           cairo_paint (cr);
+       }
+       cairo_restore (cr);
+       cairo_surface_finish (surface); /* colors will go out of scope */
+       cairo_surface_destroy (surface);
+    }
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (translate_show_surface,
+           "Tests calls to cairo_show_surface after cairo_translate",
+           "transform", /* keywords */
+           NULL, /* requirements */
+           2, 2,
+           NULL, draw)
diff --git a/test/trap-clip.c b/test/trap-clip.c
new file mode 100644 (file)
index 0000000..acfcafc
--- /dev/null
@@ -0,0 +1,213 @@
+/*
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Kristian Høgsberg <krh@redhat.com>
+ */
+
+#include <math.h>
+#include "cairo-test.h"
+#include <stdio.h>
+
+#define WIDTH 16
+#define HEIGHT 16
+#define PAD 2
+
+static const char *png_filename = "romedalen.png";
+static cairo_surface_t *image;
+
+static void
+set_solid_pattern (const cairo_test_context_t *ctx, cairo_t *cr, int x, int y)
+{
+    cairo_set_source_rgb (cr, 0, 0, 0.6);
+}
+
+static void
+set_translucent_pattern (const cairo_test_context_t *ctx, cairo_t *cr, int x, int y)
+{
+    cairo_set_source_rgba (cr, 0, 0, 0.6, 0.5);
+}
+
+static void
+set_gradient_pattern (const cairo_test_context_t *ctx, cairo_t *cr, int x, int y)
+{
+    cairo_pattern_t *pattern;
+
+    pattern =
+       cairo_pattern_create_linear (x, y, x + WIDTH, y + HEIGHT);
+    cairo_pattern_add_color_stop_rgba (pattern, 0, 1, 1, 1, 1);
+    cairo_pattern_add_color_stop_rgba (pattern, 1, 0, 0, 0.4, 1);
+    cairo_set_source (cr, pattern);
+    cairo_pattern_destroy (pattern);
+}
+
+static void
+set_image_pattern (const cairo_test_context_t *ctx, cairo_t *cr, int x, int y)
+{
+    cairo_pattern_t *pattern;
+
+    if (image == NULL || cairo_surface_status (image)) {
+       cairo_surface_destroy (image);
+       image = cairo_test_create_surface_from_png (ctx, png_filename);
+    }
+
+    pattern = cairo_pattern_create_for_surface (image);
+    cairo_pattern_set_extend (pattern, CAIRO_EXTEND_REPEAT);
+    cairo_set_source (cr, pattern);
+    cairo_pattern_destroy (pattern);
+}
+
+static void
+draw_rect (cairo_t *cr, int x, int y)
+{
+    cairo_new_path (cr);
+    cairo_rectangle (cr, x, y, WIDTH, HEIGHT);
+    cairo_fill (cr);
+}
+
+static void
+draw_rects (cairo_t *cr, int x, int y)
+{
+    int width = WIDTH / 3;
+    int height = HEIGHT / 2;
+
+    cairo_new_path (cr);
+    cairo_rectangle (cr, x, y, width, height);
+    cairo_rectangle (cr, x + width, y + height, width, height);
+    cairo_rectangle (cr, x + 2 * width, y, width, height);
+    cairo_fill (cr);
+}
+
+static void
+draw_polygon (cairo_t *cr, int x, int y)
+{
+    cairo_new_path (cr);
+    cairo_move_to (cr, x, y);
+    cairo_line_to (cr, x, y + HEIGHT);
+    cairo_line_to (cr, x + WIDTH / 2, y + 3 * HEIGHT / 4);
+    cairo_line_to (cr, x + WIDTH, y + HEIGHT);
+    cairo_line_to (cr, x + WIDTH, y);
+    cairo_line_to (cr, x + WIDTH / 2, y + HEIGHT / 4);
+    cairo_close_path (cr);
+    cairo_fill (cr);
+}
+
+static void
+clip_none (cairo_t *cr, int x, int y)
+{
+}
+
+static void
+clip_rect (cairo_t *cr, int x, int y)
+{
+    cairo_new_path (cr);
+    cairo_rectangle (cr, x + (int)WIDTH / 6, y + (int)HEIGHT / 6,
+                    4 * ((int)WIDTH  / 6), 4 * ((int)WIDTH / 6));
+    cairo_clip (cr);
+    cairo_new_path (cr);
+}
+
+static void
+clip_rects (cairo_t *cr, int x, int y)
+{
+    int height = HEIGHT / 3;
+
+    cairo_new_path (cr);
+    cairo_rectangle (cr, x, y, WIDTH, height);
+    cairo_rectangle (cr, x, y + 2 * height, WIDTH, height);
+    cairo_clip (cr);
+    cairo_new_path (cr);
+}
+
+static void
+clip_circle (cairo_t *cr, int x, int y)
+{
+    cairo_new_path (cr);
+    cairo_arc (cr, x + WIDTH / 2, y + HEIGHT / 2,
+              WIDTH / 3, 0, 2 * M_PI);
+    cairo_clip (cr);
+    cairo_new_path (cr);
+}
+
+static void (* const pattern_funcs[])(const cairo_test_context_t *ctx, cairo_t *cr, int x, int y) = {
+    set_solid_pattern,
+    set_translucent_pattern,
+    set_gradient_pattern,
+    set_image_pattern,
+};
+
+static void (* const draw_funcs[])(cairo_t *cr, int x, int y) = {
+    draw_rect,
+    draw_rects,
+    draw_polygon,
+};
+
+static void (* const clip_funcs[])(cairo_t *cr, int x, int y) = {
+    clip_none,
+    clip_rect,
+    clip_rects,
+    clip_circle,
+};
+
+#define IMAGE_WIDTH (ARRAY_LENGTH (pattern_funcs) * (WIDTH + PAD) + PAD)
+#define IMAGE_HEIGHT (ARRAY_LENGTH (draw_funcs) * ARRAY_LENGTH (clip_funcs) * (HEIGHT + PAD) + PAD)
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    const cairo_test_context_t *ctx = cairo_test_get_context (cr);
+    size_t i, j, k, x, y;
+
+    for (k = 0; k < ARRAY_LENGTH (clip_funcs); k++) {
+       for (j = 0; j < ARRAY_LENGTH (draw_funcs); j++) {
+           for (i = 0; i < ARRAY_LENGTH (pattern_funcs); i++) {
+               x = i * (WIDTH + PAD) + PAD;
+               y = (ARRAY_LENGTH (draw_funcs) * k + j) * (HEIGHT + PAD) + PAD;
+
+               cairo_save (cr);
+
+               cairo_move_to (cr, x, y);
+               clip_funcs[k] (cr, x, y);
+               pattern_funcs[i] (ctx, cr, x, y);
+               draw_funcs[j] (cr, x, y);
+               if (cairo_status (cr))
+                   cairo_test_log (ctx, "%d %d HERE!\n", (int)i, (int)j);
+
+               cairo_restore (cr);
+           }
+       }
+    }
+
+    if (cairo_status (cr) != CAIRO_STATUS_SUCCESS)
+       cairo_test_log (ctx, "%d %d .HERE!\n", (int)i, (int)j);
+
+    cairo_surface_destroy (image);
+    image = NULL;
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (trap_clip,
+           "Trapezoid clipping",
+           "clip, trap", /* keywords */
+           NULL, /* requirements */
+           IMAGE_WIDTH, IMAGE_HEIGHT,
+           NULL, draw)
diff --git a/test/twin-antialias-gray.c b/test/twin-antialias-gray.c
new file mode 100644 (file)
index 0000000..8e00370
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2008 Chris Wilson
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Chris Wilson not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Chris Wilson makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * CHRIS WILSON DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL CHRIS WILSON BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_font_options_t *options;
+
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_paint (cr);
+    cairo_set_source_rgb (cr, 0, 0, 0);
+
+    cairo_set_antialias (cr, CAIRO_ANTIALIAS_GRAY);
+
+    cairo_select_font_face (cr,
+                           "@cairo:",
+                           CAIRO_FONT_SLANT_NORMAL,
+                           CAIRO_FONT_WEIGHT_NORMAL);
+
+    options = cairo_font_options_create ();
+    cairo_font_options_set_antialias (options, CAIRO_ANTIALIAS_GRAY);
+    cairo_set_font_options (cr, options);
+    cairo_font_options_destroy (options);
+
+    cairo_set_font_size (cr, 16);
+
+    cairo_move_to (cr, 4, 14);
+    cairo_show_text (cr, "Is cairo's twin giza?");
+
+    cairo_move_to (cr, 4, 34);
+    cairo_text_path (cr, "Is cairo's twin giza?");
+    cairo_fill (cr);
+
+    cairo_move_to (cr, 4, 54);
+    cairo_text_path (cr, "Is cairo's twin giza?");
+    cairo_set_line_width (cr, 2/16.);
+    cairo_stroke (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (twin_antialias_gray,
+           "Tests the internal font (with antialiasing reduced)",
+           "twin, font", /* keywords */
+           "target=raster", /* requirements */
+           140, 60,
+           NULL, draw)
diff --git a/test/twin-antialias-mixed.c b/test/twin-antialias-mixed.c
new file mode 100644 (file)
index 0000000..da4121a
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2009 Chris Wilson
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Chris Wilson not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Chris Wilson makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * CHRIS WILSON DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL CHRIS WILSON BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+
+static cairo_scaled_font_t *
+create_twin (cairo_t *cr, cairo_antialias_t antialias)
+{
+    cairo_font_options_t *options;
+
+    cairo_select_font_face (cr,
+                           "@cairo:",
+                           CAIRO_FONT_SLANT_NORMAL,
+                           CAIRO_FONT_WEIGHT_NORMAL);
+
+    options = cairo_font_options_create ();
+    cairo_font_options_set_antialias (options, antialias);
+    cairo_set_font_options (cr, options);
+    cairo_font_options_destroy (options);
+
+    return cairo_scaled_font_reference (cairo_get_scaled_font (cr));
+}
+
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_scaled_font_t *subpixel, *gray, *none;
+
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_paint (cr);
+    cairo_set_source_rgb (cr, 0, 0, 0);
+
+    cairo_set_font_size (cr, 16);
+    subpixel = create_twin (cr, CAIRO_ANTIALIAS_SUBPIXEL);
+    gray = create_twin (cr, CAIRO_ANTIALIAS_GRAY);
+    none = create_twin (cr, CAIRO_ANTIALIAS_NONE);
+
+    cairo_move_to (cr, 4, 14);
+    cairo_set_scaled_font (cr, subpixel);
+    cairo_show_text (cr, "Is cairo's");
+    cairo_set_scaled_font (cr, gray);
+    cairo_show_text (cr, " twin");
+    cairo_set_scaled_font (cr, none);
+    cairo_show_text (cr, " giza?");
+
+    cairo_move_to (cr, 4, 34);
+    cairo_set_scaled_font (cr, gray);
+    cairo_show_text (cr, "Is cairo's");
+    cairo_set_scaled_font (cr, none);
+    cairo_show_text (cr, " twin");
+    cairo_set_scaled_font (cr, subpixel);
+    cairo_show_text (cr, " giza?");
+
+    cairo_move_to (cr, 4, 54);
+    cairo_set_scaled_font (cr, none);
+    cairo_show_text (cr, "Is cairo's");
+    cairo_set_scaled_font (cr, gray);
+    cairo_show_text (cr, " twin");
+    cairo_set_scaled_font (cr, subpixel);
+    cairo_show_text (cr, " giza?");
+
+    cairo_scaled_font_destroy (none);
+    cairo_scaled_font_destroy (gray);
+    cairo_scaled_font_destroy (subpixel);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (twin_antialias_mixed,
+           "Tests the internal font (with intermixed antialiasing)",
+           "twin, font", /* keywords */
+           "target=raster", /* requirements */
+           140, 60,
+           NULL, draw)
diff --git a/test/twin-antialias-none.c b/test/twin-antialias-none.c
new file mode 100644 (file)
index 0000000..a5b713d
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2008 Chris Wilson
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Chris Wilson not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Chris Wilson makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * CHRIS WILSON DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL CHRIS WILSON BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_font_options_t *options;
+
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_paint (cr);
+    cairo_set_source_rgb (cr, 0, 0, 0);
+
+    cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
+
+    cairo_select_font_face (cr,
+                           "@cairo:",
+                           CAIRO_FONT_SLANT_NORMAL,
+                           CAIRO_FONT_WEIGHT_NORMAL);
+
+    options = cairo_font_options_create ();
+    cairo_font_options_set_antialias (options, CAIRO_ANTIALIAS_NONE);
+    cairo_set_font_options (cr, options);
+    cairo_font_options_destroy (options);
+
+    cairo_set_font_size (cr, 16);
+
+    cairo_move_to (cr, 4, 14);
+    cairo_show_text (cr, "Is cairo's twin giza?");
+
+    cairo_move_to (cr, 4, 34);
+    cairo_text_path (cr, "Is cairo's twin giza?");
+    cairo_fill (cr);
+
+    cairo_move_to (cr, 4, 54);
+    cairo_text_path (cr, "Is cairo's twin giza?");
+    cairo_set_line_width (cr, 2/16.);
+    cairo_stroke (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (twin_antialias_none,
+           "Tests the internal font (with antialiasing disabled)",
+           "twin, font", /* keywords */
+           "target=raster", /* requirements */
+           140, 60,
+           NULL, draw)
diff --git a/test/twin-antialias-subpixel.c b/test/twin-antialias-subpixel.c
new file mode 100644 (file)
index 0000000..e026286
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2008 Chris Wilson
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Chris Wilson not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Chris Wilson makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * CHRIS WILSON DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL CHRIS WILSON BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_font_options_t *options;
+
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_paint (cr);
+    cairo_set_source_rgb (cr, 0, 0, 0);
+
+    cairo_set_antialias (cr, CAIRO_ANTIALIAS_SUBPIXEL);
+
+    cairo_select_font_face (cr,
+                           "@cairo:",
+                           CAIRO_FONT_SLANT_NORMAL,
+                           CAIRO_FONT_WEIGHT_NORMAL);
+
+    options = cairo_font_options_create ();
+    cairo_font_options_set_antialias (options, CAIRO_ANTIALIAS_SUBPIXEL);
+    cairo_set_font_options (cr, options);
+    cairo_font_options_destroy (options);
+
+    cairo_set_font_size (cr, 16);
+
+    cairo_move_to (cr, 4, 14);
+    cairo_show_text (cr, "Is cairo's twin giza?");
+
+    cairo_move_to (cr, 4, 34);
+    cairo_text_path (cr, "Is cairo's twin giza?");
+    cairo_fill (cr);
+
+    cairo_move_to (cr, 4, 54);
+    cairo_text_path (cr, "Is cairo's twin giza?");
+    cairo_set_line_width (cr, 2/16.);
+    cairo_stroke (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (twin_antialias_subpixel,
+           "Tests the internal font (with subpixel antialiasing)",
+           "twin, font", /* keywords */
+           "target=raster", /* requirements */
+           140, 60,
+           NULL, draw)
diff --git a/test/twin.c b/test/twin.c
new file mode 100644 (file)
index 0000000..08865f0
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2008 Chris Wilson
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Chris Wilson not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Chris Wilson makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * CHRIS WILSON DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL CHRIS WILSON BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_paint (cr);
+    cairo_set_source_rgb (cr, 0, 0, 0);
+
+    cairo_select_font_face (cr,
+                           "@cairo:",
+                           CAIRO_FONT_SLANT_NORMAL,
+                           CAIRO_FONT_WEIGHT_NORMAL);
+    cairo_set_font_size (cr, 16);
+
+    cairo_move_to (cr, 4, 14);
+    cairo_show_text (cr, "Is cairo's twin giza?");
+
+    cairo_move_to (cr, 4, 34);
+    cairo_text_path (cr, "Is cairo's twin giza?");
+    cairo_fill (cr);
+
+    cairo_move_to (cr, 4, 54);
+    cairo_text_path (cr, "Is cairo's twin giza?");
+    cairo_set_line_width (cr, 2/16.);
+    cairo_stroke (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (twin,
+           "Tests the internal font",
+           "twin, font", /* keywords */
+           NULL, /* requirements */
+           140, 60,
+           NULL, draw)
diff --git a/test/unaligned-box.c b/test/unaligned-box.c
new file mode 100644 (file)
index 0000000..5526919
--- /dev/null
@@ -0,0 +1,73 @@
+/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/*
+ * Copyright 2011 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+
+#define WIDTH 48
+#define HEIGHT 52
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    int sx, sy;
+
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_paint (cr);
+    cairo_set_source_rgb (cr, 0, 0, 0);
+
+    cairo_translate(cr, 2, 2);
+
+    for (sx = 1; sx <= 4; sx++) {
+       cairo_save (cr);
+       for (sy = 1; sy <= 4; sy++) {
+           cairo_rectangle (cr, 0, 0, sx, sy);
+           cairo_fill (cr);
+
+           cairo_rectangle (cr, sx + 1 + .5, 0, sx, sy);
+           cairo_fill (cr);
+
+           cairo_rectangle (cr, 0, sy + 1 + .5, sx, sy);
+           cairo_fill (cr);
+
+           cairo_rectangle (cr, sx + 1 + .5, sy + 1 + .5, sx-.5, sy-.5);
+           cairo_fill (cr);
+
+           cairo_translate (cr, 2*sx + 3, 0);
+       }
+       cairo_restore (cr);
+       cairo_translate (cr, 0, 2*sy + 3);
+    }
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (unaligned_box,
+           "Tests handling of various boundary conditions for unaligned rectangles.",
+           "fill", /* keywords */
+           NULL, /* requirements */
+           WIDTH, HEIGHT,
+           NULL, draw)
diff --git a/test/unantialiased-shapes.c b/test/unantialiased-shapes.c
new file mode 100644 (file)
index 0000000..b53ed0d
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * Copyright © 2005 Billy Biggs
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Billy Biggs not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Billy Biggs makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * BILLY BIGGS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL BILLY BIGGS BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Billy Biggs <vektor@dumbterm.net>
+ */
+
+#include "cairo-test.h"
+
+/* The star shape from the SVG test suite, from the fill rule test */
+static void
+big_star_path (cairo_t *cr)
+{
+    cairo_move_to (cr, 40, 0);
+    cairo_rel_line_to (cr, 25, 80);
+    cairo_rel_line_to (cr, -65, -50);
+    cairo_rel_line_to (cr, 80, 0);
+    cairo_rel_line_to (cr, -65, 50);
+    cairo_close_path (cr);
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    int i;
+
+    cairo_set_source_rgb (cr, 0, 0, 0);
+    cairo_paint (cr);
+
+    cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
+
+    /* Try a circle */
+    cairo_arc (cr, 40, 40, 20, 0, 2 * M_PI);
+    cairo_set_source_rgb (cr, 1, 0, 0);
+    cairo_fill (cr);
+
+    /* Try using clipping to draw a circle */
+    cairo_arc (cr, 100, 40, 20, 0, 2 * M_PI);
+    cairo_clip (cr);
+    cairo_rectangle (cr, 80, 20, 40, 40);
+    cairo_set_source_rgb (cr, 0, 0, 1);
+    cairo_fill (cr);
+
+    /* Reset the clipping */
+    cairo_reset_clip (cr);
+
+    /* Draw a bunch of lines */
+    cairo_set_line_width (cr, 1.0);
+    cairo_set_source_rgb (cr, 0, 1, 0);
+    for (i = 0; i < 10; i++) {
+        cairo_move_to (cr, 10, 70 + (i * 4));
+        cairo_line_to (cr, 120, 70 + (i * 18));
+        cairo_stroke (cr);
+    }
+
+    /* Try filling a poly */
+    cairo_translate (cr, 160, 120);
+    cairo_set_source_rgb (cr, 1, 1, 0);
+    big_star_path (cr);
+    cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD);
+    cairo_fill (cr);
+    cairo_translate (cr, -160, -120);
+
+    /* How about some curves? */
+    cairo_set_source_rgb (cr, 1, 0, 1);
+    for (i = 0; i < 10; i++) {
+        cairo_move_to (cr, 150, 50 + (i * 5));
+        cairo_curve_to (cr, 250, 50, 200, (i * 10), 300, 50 + (i * 10));
+        cairo_stroke (cr);
+    }
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (unantialiased_shapes,
+           "Test shape drawing without antialiasing",
+           "fill, stroke", /* keywords */
+           "target=raster", /* requirements */
+           320, 240,
+           NULL, draw)
diff --git a/test/unbounded-operator.c b/test/unbounded-operator.c
new file mode 100644 (file)
index 0000000..6e132fb
--- /dev/null
@@ -0,0 +1,185 @@
+/*
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Authors: Kristian Høgsberg <krh@redhat.com>
+ *          Owen Taylor <otaylor@redhat.com>
+ */
+
+#include <math.h>
+#include "cairo-test.h"
+#include <stdio.h>
+
+#define WIDTH 16
+#define HEIGHT 16
+#define PAD 2
+
+static void
+draw_mask (cairo_t *cr, int x, int y)
+{
+    cairo_surface_t *mask_surface;
+    cairo_t *cr2;
+
+    double width = (int)(0.9 * WIDTH);
+    double height = (int)(0.9 * HEIGHT);
+    x += 0.05 * WIDTH;
+    y += 0.05 * HEIGHT;
+
+    mask_surface = cairo_surface_create_similar (cairo_get_group_target (cr),
+                                                CAIRO_CONTENT_ALPHA,
+                                                width, height);
+    cr2 = cairo_create (mask_surface);
+    cairo_surface_destroy (mask_surface);
+
+    cairo_save (cr2);
+    cairo_set_source_rgba (cr2, 0, 0, 0, 0); /* transparent */
+    cairo_set_operator (cr2, CAIRO_OPERATOR_SOURCE);
+    cairo_paint (cr2);
+    cairo_restore (cr2);
+
+    cairo_set_source_rgb (cr2, 1, 1, 1); /* white */
+
+    cairo_arc (cr2, 0.5 * width, 0.5 * height, 0.45 * height, 0, 2 * M_PI);
+    cairo_fill (cr2);
+
+    cairo_mask_surface (cr, cairo_get_target (cr2), x, y);
+    cairo_destroy (cr2);
+}
+
+static void
+draw_glyphs (cairo_t *cr, int x, int y)
+{
+    cairo_text_extents_t extents;
+
+    cairo_set_font_size (cr, 0.8 * HEIGHT);
+
+    cairo_text_extents (cr, "FG", &extents);
+    cairo_move_to (cr,
+                  x + floor ((WIDTH - extents.width) / 2 + 0.5) - extents.x_bearing,
+                  y + floor ((HEIGHT - extents.height) / 2 + 0.5) - extents.y_bearing);
+    cairo_show_text (cr, "FG");
+}
+
+static void
+draw_polygon (cairo_t *cr, int x, int y)
+{
+    double width = (int)(0.9 * WIDTH);
+    double height = (int)(0.9 * HEIGHT);
+    x += 0.05 * WIDTH;
+    y += 0.05 * HEIGHT;
+
+    cairo_new_path (cr);
+    cairo_move_to (cr, x, y);
+    cairo_line_to (cr, x, y + height);
+    cairo_line_to (cr, x + width / 2, y + 3 * height / 4);
+    cairo_line_to (cr, x + width, y + height);
+    cairo_line_to (cr, x + width, y);
+    cairo_line_to (cr, x + width / 2, y + height / 4);
+    cairo_close_path (cr);
+    cairo_fill (cr);
+}
+
+static void
+draw_rects (cairo_t *cr, int x, int y)
+{
+    double block_width = (int)(0.33 * WIDTH + 0.5);
+    double block_height = (int)(0.33 * HEIGHT + 0.5);
+    int i, j;
+
+    for (i = 0; i < 3; i++)
+       for (j = 0; j < 3; j++)
+           if ((i + j) % 2 == 0)
+               cairo_rectangle (cr,
+                                x + block_width * i, y + block_height * j,
+                                block_width,         block_height);
+
+    cairo_fill (cr);
+}
+
+static void (*const draw_funcs[])(cairo_t *cr, int x, int y) = {
+    draw_mask,
+    draw_glyphs,
+    draw_polygon,
+    draw_rects
+};
+
+static cairo_operator_t operators[] = {
+    CAIRO_OPERATOR_IN, CAIRO_OPERATOR_OUT,
+    CAIRO_OPERATOR_DEST_IN, CAIRO_OPERATOR_DEST_ATOP
+};
+
+#define IMAGE_WIDTH (ARRAY_LENGTH (operators) * (WIDTH + PAD) + PAD)
+#define IMAGE_HEIGHT (ARRAY_LENGTH (draw_funcs) * (HEIGHT + PAD) + PAD)
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    const cairo_test_context_t *ctx = cairo_test_get_context (cr);
+    size_t i, j, x, y;
+    cairo_pattern_t *pattern;
+
+    cairo_select_font_face (cr, CAIRO_TEST_FONT_FAMILY " Sans",
+                           CAIRO_FONT_SLANT_NORMAL,
+                           CAIRO_FONT_WEIGHT_NORMAL);
+
+    for (j = 0; j < ARRAY_LENGTH (draw_funcs); j++) {
+       for (i = 0; i < ARRAY_LENGTH (operators); i++) {
+           x = i * (WIDTH + PAD) + PAD;
+           y = j * (HEIGHT + PAD) + PAD;
+
+           cairo_save (cr);
+
+           pattern = cairo_pattern_create_linear (x + WIDTH, y,
+                                                  x,         y + HEIGHT);
+           cairo_pattern_add_color_stop_rgba (pattern, 0.2,
+                                              0.0, 0.0, 1.0, 1.0); /* Solid blue */
+           cairo_pattern_add_color_stop_rgba (pattern, 0.8,
+                                              0.0, 0.0, 1.0, 0.0); /* Transparent blue */
+           cairo_set_source (cr, pattern);
+           cairo_pattern_destroy (pattern);
+
+           cairo_rectangle (cr, x, y, WIDTH, HEIGHT);
+           cairo_fill_preserve (cr);
+           cairo_clip (cr);
+
+           cairo_set_operator (cr, operators[i]);
+           cairo_set_source_rgb (cr, 1.0, 0.0, 0.0);
+
+           draw_funcs[j] (cr, x, y);
+           if (cairo_status (cr))
+               cairo_test_log (ctx, "%d %d HERE!\n", (int)i, (int)j);
+
+           cairo_restore (cr);
+       }
+    }
+
+    if (cairo_status (cr) != CAIRO_STATUS_SUCCESS)
+       cairo_test_log (ctx, "%d %d .HERE!\n", (int)i, (int)j);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (unbounded_operator,
+           "Operators with an effect for transparent source/mask",
+           "operator", /* keywords */
+           NULL, /* requirements */
+           IMAGE_WIDTH, IMAGE_HEIGHT,
+           NULL, draw)
diff --git a/test/unclosed-strokes.c b/test/unclosed-strokes.c
new file mode 100644 (file)
index 0000000..3c0287f
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * Copyright © 2011 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+
+#define LINE_WIDTH     10.
+#define SIZE           (5 * LINE_WIDTH)
+#define PAD            (2 * LINE_WIDTH)
+
+static void
+make_path (cairo_t *cr)
+{
+    cairo_move_to (cr, 0, 0);
+    cairo_rel_line_to (cr, -SIZE/2, SIZE);
+    cairo_rel_line_to (cr, SIZE, 0);
+    /* back to the start, but do not close */
+    cairo_rel_line_to (cr, -SIZE/2, -SIZE);
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_save (cr);
+    cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); /* white */
+    cairo_paint (cr);
+    cairo_restore (cr);
+
+    cairo_set_line_width (cr, LINE_WIDTH);
+    cairo_translate (cr, PAD + SIZE / 2., PAD);
+
+    cairo_set_line_cap (cr, CAIRO_LINE_CAP_BUTT);
+    cairo_set_line_join (cr, CAIRO_LINE_JOIN_BEVEL);
+    make_path (cr);
+    cairo_stroke (cr);
+
+    cairo_translate (cr, 0, SIZE + PAD);
+
+    cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
+    cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND);
+    make_path (cr);
+    cairo_stroke (cr);
+
+    cairo_translate (cr, 0, SIZE + PAD);
+
+    cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE);
+    cairo_set_line_join (cr, CAIRO_LINE_JOIN_MITER);
+    make_path (cr);
+    cairo_stroke (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (unclosed_strokes,
+           "Test coincident end-points are capped and not joined",
+           "stroke caps", /* keywords */
+           NULL, /* requirements */
+           PAD + SIZE + PAD,
+           3 * (PAD + SIZE) + PAD,
+           NULL, draw)
+
diff --git a/test/user-data.c b/test/user-data.c
new file mode 100644 (file)
index 0000000..532107a
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Kristian Høgsberg <krh@redhat.com>
+ */
+
+#include "cairo-test.h"
+
+#include <assert.h>
+
+static void
+destroy_data1 (void *p)
+{
+    *(int *) p = 1;
+}
+
+static void
+destroy_data2 (void *p)
+{
+    *(int *) p = 2;
+}
+
+static cairo_test_status_t
+preamble (cairo_test_context_t *ctx)
+{
+    static const cairo_user_data_key_t key1, key2;
+    cairo_surface_t *surface;
+    cairo_status_t status;
+    int data1, data2;
+
+    data1 = 0;
+    data2 = 0;
+
+    surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 1, 1);
+    status = cairo_surface_set_user_data (surface, &key1, &data1, destroy_data1);
+    if (status)
+       goto error;
+
+    status = cairo_surface_set_user_data (surface, &key2, &data2, destroy_data2);
+    if (status)
+       goto error;
+
+    assert (cairo_surface_get_user_data (surface, &key1) == &data1);
+    status = cairo_surface_set_user_data (surface, &key1, NULL, NULL);
+    if (status)
+       goto error;
+
+    assert (cairo_surface_get_user_data (surface, &key1) == NULL);
+    assert (data1 == 1);
+    assert (data2 == 0);
+
+    status = cairo_surface_set_user_data (surface, &key2, NULL, NULL);
+    if (status)
+       goto error;
+
+    assert (data2 == 2);
+
+    data1 = 0;
+    status = cairo_surface_set_user_data (surface, &key1, &data1, NULL);
+    if (status)
+       goto error;
+
+    status = cairo_surface_set_user_data (surface, &key1, NULL, NULL);
+    if (status)
+       goto error;
+
+    assert (data1 == 0);
+    assert (cairo_surface_get_user_data (surface, &key1) == NULL);
+
+    status = cairo_surface_set_user_data (surface, &key1, &data1, destroy_data1);
+    if (status)
+       goto error;
+
+    cairo_surface_destroy (surface);
+
+    assert (data1 == 1);
+    assert (data2 == 2);
+
+    return CAIRO_TEST_SUCCESS;
+
+error:
+    cairo_surface_destroy (surface);
+    return cairo_test_status_from_status (ctx, status);
+}
+
+CAIRO_TEST (user_data,
+           "Test setting and getting random bits of user data.",
+           "api", /* keywords */
+           NULL, /* requirements */
+           0, 0,
+           preamble, NULL)
diff --git a/test/user-font-mask.c b/test/user-font-mask.c
new file mode 100644 (file)
index 0000000..a6d394e
--- /dev/null
@@ -0,0 +1,253 @@
+/*
+ * Copyright © 2006, 2008 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Contributor(s):
+ *     Kristian Høgsberg <krh@redhat.com>
+ *     Behdad Esfahbod <behdad@behdad.org>
+ *     Adrian Johnson <ajohnson@redneon.com>
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "cairo-test.h"
+
+/*#define ROTATED 1*/
+
+#define BORDER 10
+#define TEXT_SIZE 64
+#define WIDTH  (TEXT_SIZE * 15 + 2*BORDER)
+#ifndef ROTATED
+ #define HEIGHT ((TEXT_SIZE + 2*BORDER)*2)
+#else
+ #define HEIGHT WIDTH
+#endif
+#define END_GLYPH 0
+#define TEXT   "cairo"
+
+/* 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)
+
+#ifdef WORDS_BIGENDIAN
+#define CAIRO_BITSWAP8_IF_LITTLE_ENDIAN(c) (c)
+#else
+#define CAIRO_BITSWAP8_IF_LITTLE_ENDIAN(c) CAIRO_BITSWAP8(c)
+#endif
+
+
+
+/* Simple glyph definition. data is an 8x8 bitmap.
+ */
+typedef struct {
+    unsigned long ucs4;
+    int width;
+    char data[8];
+} test_scaled_font_glyph_t;
+
+static cairo_user_data_key_t test_font_face_glyphs_key;
+
+static cairo_status_t
+test_scaled_font_init (cairo_scaled_font_t  *scaled_font,
+                      cairo_t              *cr,
+                      cairo_font_extents_t *metrics)
+{
+  metrics->ascent  = 1;
+  metrics->descent = 0;
+  return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+test_scaled_font_unicode_to_glyph (cairo_scaled_font_t *scaled_font,
+                                  unsigned long        unicode,
+                                  unsigned long       *glyph)
+{
+    test_scaled_font_glyph_t *glyphs = cairo_font_face_get_user_data (cairo_scaled_font_get_font_face (scaled_font),
+                                                                     &test_font_face_glyphs_key);
+    int i;
+
+    for (i = 0; glyphs[i].ucs4 != (unsigned long) -1; i++)
+       if (glyphs[i].ucs4 == unicode) {
+           *glyph = i;
+           return CAIRO_STATUS_SUCCESS;
+       }
+
+    /* Not found.  Default to glyph 0 */
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+test_scaled_font_render_glyph (cairo_scaled_font_t  *scaled_font,
+                              unsigned long         glyph,
+                              cairo_t              *cr,
+                              cairo_text_extents_t *metrics)
+{
+    test_scaled_font_glyph_t *glyphs = cairo_font_face_get_user_data (cairo_scaled_font_get_font_face (scaled_font),
+                                                                     &test_font_face_glyphs_key);
+    int i;
+    unsigned char *data;
+    cairo_surface_t *image;
+    cairo_pattern_t *pattern;
+    cairo_matrix_t matrix;
+    uint8_t byte;
+
+    /* FIXME: We simply crash on out-of-bound glyph indices */
+
+    metrics->x_advance = (glyphs[glyph].width + 1) / 8.0;
+
+    image = cairo_image_surface_create (CAIRO_FORMAT_A1, glyphs[glyph].width, 8);
+    if (cairo_surface_status (image))
+       return cairo_surface_status (image);
+
+    data = cairo_image_surface_get_data (image);
+    for (i = 0; i < 8; i++) {
+       byte = glyphs[glyph].data[i];
+       *data = CAIRO_BITSWAP8_IF_LITTLE_ENDIAN (byte);
+       data += cairo_image_surface_get_stride (image);
+    }
+    cairo_surface_mark_dirty (image);
+
+    pattern = cairo_pattern_create_for_surface (image);
+    cairo_surface_destroy (image);
+
+    cairo_matrix_init_identity (&matrix);
+    cairo_matrix_scale (&matrix, 1.0/8.0, 1.0/8.0);
+    cairo_matrix_translate (&matrix, 0, -8);
+    cairo_matrix_invert (&matrix);
+    cairo_pattern_set_matrix (pattern, &matrix);
+
+    cairo_set_source (cr, pattern);
+    cairo_mask (cr, pattern);
+    cairo_pattern_destroy (pattern);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_user_font_face_create (cairo_font_face_t **out)
+{
+    static const test_scaled_font_glyph_t glyphs [] = {
+       { 'c',  6, { 0x00, 0x38, 0x44, 0x80, 0x80, 0x80, 0x44, 0x38 } },
+       { 'a',  6, { 0x00, 0x70, 0x88, 0x3c, 0x44, 0x84, 0x8c, 0x74 } },
+       { 'i',  1, { 0x80, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80 } },
+       { 'r',  6, { 0x00, 0xb8, 0xc4, 0x80, 0x80, 0x80, 0x80, 0x80 } },
+       { 'o',  7, { 0x00, 0x38, 0x44, 0x82, 0x82, 0x82, 0x44, 0x38 } },
+       {  -1,  8, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } },
+    };
+
+    cairo_font_face_t *user_font_face;
+    cairo_status_t status;
+
+    user_font_face = cairo_user_font_face_create ();
+    cairo_user_font_face_set_init_func             (user_font_face, test_scaled_font_init);
+    cairo_user_font_face_set_render_glyph_func     (user_font_face, test_scaled_font_render_glyph);
+    cairo_user_font_face_set_unicode_to_glyph_func (user_font_face, test_scaled_font_unicode_to_glyph);
+
+    status = cairo_font_face_set_user_data (user_font_face,
+                                           &test_font_face_glyphs_key,
+                                           (void*) glyphs, NULL);
+    if (status) {
+       cairo_font_face_destroy (user_font_face);
+       return status;
+    }
+
+    *out = user_font_face;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_font_face_t *font_face;
+    const char text[] = TEXT;
+    cairo_font_extents_t font_extents;
+    cairo_text_extents_t extents;
+    cairo_status_t status;
+
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_paint (cr);
+
+#ifdef ROTATED
+    cairo_translate (cr, TEXT_SIZE, 0);
+    cairo_rotate (cr, .6);
+#endif
+
+    status = _user_font_face_create (&font_face);
+    if (status) {
+       return cairo_test_status_from_status (cairo_test_get_context (cr),
+                                             status);
+    }
+
+    cairo_set_font_face (cr, font_face);
+    cairo_font_face_destroy (font_face);
+
+    cairo_set_font_size (cr, TEXT_SIZE);
+
+    cairo_font_extents (cr, &font_extents);
+    cairo_text_extents (cr, text, &extents);
+
+    /* logical boundaries in red */
+    cairo_move_to (cr, 0, BORDER);
+    cairo_rel_line_to (cr, WIDTH, 0);
+    cairo_move_to (cr, 0, BORDER + font_extents.ascent);
+    cairo_rel_line_to (cr, WIDTH, 0);
+    cairo_move_to (cr, 0, BORDER + font_extents.ascent + font_extents.descent);
+    cairo_rel_line_to (cr, WIDTH, 0);
+    cairo_move_to (cr, BORDER, 0);
+    cairo_rel_line_to (cr, 0, 2*BORDER + TEXT_SIZE);
+    cairo_move_to (cr, BORDER + extents.x_advance, 0);
+    cairo_rel_line_to (cr, 0, 2*BORDER + TEXT_SIZE);
+    cairo_set_source_rgb (cr, 1, 0, 0);
+    cairo_set_line_width (cr, 2);
+    cairo_stroke (cr);
+
+    /* ink boundaries in green */
+    cairo_rectangle (cr,
+                    BORDER + extents.x_bearing, BORDER + font_extents.ascent + extents.y_bearing,
+                    extents.width, extents.height);
+    cairo_set_source_rgb (cr, 0, 1, 0);
+    cairo_set_line_width (cr, 2);
+    cairo_stroke (cr);
+
+    /* text in black */
+    cairo_set_source_rgb (cr, 0, 0, 0);
+    cairo_move_to (cr, BORDER, BORDER + font_extents.ascent);
+    cairo_show_text (cr, text);
+
+
+    /* filled version of text in blue */
+    cairo_set_source_rgb (cr, 0, 0, 1);
+    cairo_move_to (cr, BORDER, BORDER + font_extents.height + 2*BORDER + font_extents.ascent);
+    cairo_text_path (cr, text);
+    cairo_fill (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (user_font_mask,
+           "Tests a user-font using cairo_mask with bitmap images",
+           "user-font, mask", /* keywords */
+           NULL, /* requirements */
+           WIDTH, HEIGHT,
+           NULL, draw)
diff --git a/test/user-font-proxy.c b/test/user-font-proxy.c
new file mode 100644 (file)
index 0000000..41a69e7
--- /dev/null
@@ -0,0 +1,222 @@
+/*
+ * Copyright © 2006, 2008 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Contributor(s):
+ *     Kristian Høgsberg <krh@redhat.com>
+ *     Behdad Esfahbod <behdad@behdad.org>
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "cairo-test.h"
+
+/*#define ROTATED 1*/
+
+#define BORDER 10
+#define TEXT_SIZE 64
+#define WIDTH  (TEXT_SIZE * 12 + 2*BORDER)
+#ifndef ROTATED
+ #define HEIGHT ((TEXT_SIZE + 2*BORDER)*2)
+#else
+ #define HEIGHT WIDTH
+#endif
+#define TEXT   "geez... cairo user-font"
+
+static cairo_user_data_key_t fallback_font_key;
+
+static cairo_status_t
+test_scaled_font_init (cairo_scaled_font_t  *scaled_font,
+                      cairo_t              *cr,
+                      cairo_font_extents_t *extents)
+{
+    cairo_status_t status;
+
+    cairo_set_font_face (cr,
+                        cairo_font_face_get_user_data (cairo_scaled_font_get_font_face (scaled_font),
+                                                       &fallback_font_key));
+
+    status = cairo_scaled_font_set_user_data (scaled_font,
+                                             &fallback_font_key,
+                                             cairo_scaled_font_reference (cairo_get_scaled_font (cr)),
+                                             (cairo_destroy_func_t) cairo_scaled_font_destroy);
+    if (unlikely (status)) {
+       cairo_scaled_font_destroy (cairo_get_scaled_font (cr));
+       return status;
+    }
+
+    cairo_font_extents (cr, extents);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+test_scaled_font_render_glyph (cairo_scaled_font_t  *scaled_font,
+                              unsigned long         glyph,
+                              cairo_t              *cr,
+                              cairo_text_extents_t *extents)
+{
+    cairo_glyph_t cairo_glyph;
+
+    cairo_glyph.index = glyph;
+    cairo_glyph.x = 0;
+    cairo_glyph.y = 0;
+
+    cairo_set_font_face (cr,
+                        cairo_font_face_get_user_data (cairo_scaled_font_get_font_face (scaled_font),
+                                                       &fallback_font_key));
+
+    cairo_show_glyphs (cr, &cairo_glyph, 1);
+    cairo_glyph_extents (cr, &cairo_glyph, 1, extents);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+test_scaled_font_text_to_glyphs (cairo_scaled_font_t        *scaled_font,
+                                const char                 *utf8,
+                                int                         utf8_len,
+                                cairo_glyph_t             **glyphs,
+                                int                        *num_glyphs,
+                                cairo_text_cluster_t      **clusters,
+                                int                        *num_clusters,
+                                cairo_text_cluster_flags_t *cluster_flags)
+{
+  cairo_scaled_font_t *fallback_scaled_font;
+
+  fallback_scaled_font = cairo_scaled_font_get_user_data (scaled_font,
+                                                         &fallback_font_key);
+
+  return cairo_scaled_font_text_to_glyphs (fallback_scaled_font, 0, 0,
+                                          utf8, utf8_len,
+                                          glyphs, num_glyphs,
+                                          clusters, num_clusters, cluster_flags);
+}
+
+static cairo_status_t
+_user_font_face_create (cairo_font_face_t **out)
+{
+    cairo_font_face_t *user_font_face;
+    cairo_font_face_t *fallback_font_face;
+    cairo_status_t status;
+
+    user_font_face = cairo_user_font_face_create ();
+    cairo_user_font_face_set_init_func             (user_font_face, test_scaled_font_init);
+    cairo_user_font_face_set_render_glyph_func     (user_font_face, test_scaled_font_render_glyph);
+    cairo_user_font_face_set_text_to_glyphs_func   (user_font_face, test_scaled_font_text_to_glyphs);
+
+    /* This also happens to be default font face on cairo_t, so does
+     * not make much sense here.  For demonstration only.
+     */
+    fallback_font_face = cairo_toy_font_face_create ("",
+                                                    CAIRO_FONT_SLANT_NORMAL,
+                                                    CAIRO_FONT_WEIGHT_NORMAL);
+
+    status = cairo_font_face_set_user_data (user_font_face,
+                                           &fallback_font_key,
+                                           fallback_font_face,
+                                           (cairo_destroy_func_t) cairo_font_face_destroy);
+    if (status) {
+       cairo_font_face_destroy (fallback_font_face);
+       cairo_font_face_destroy (user_font_face);
+       return status;
+    }
+
+    *out = user_font_face;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    const char text[] = TEXT;
+    cairo_font_extents_t font_extents;
+    cairo_text_extents_t extents;
+    cairo_font_face_t *font_face;
+    cairo_status_t status;
+
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_paint (cr);
+
+#ifdef ROTATED
+    cairo_translate (cr, TEXT_SIZE, 0);
+    cairo_rotate (cr, .6);
+#endif
+
+    status = _user_font_face_create (&font_face);
+    if (status) {
+       return cairo_test_status_from_status (cairo_test_get_context (cr),
+                                             status);
+    }
+
+    cairo_set_font_face (cr, font_face);
+    cairo_font_face_destroy (font_face);
+
+    cairo_set_font_size (cr, TEXT_SIZE);
+
+    cairo_font_extents (cr, &font_extents);
+    cairo_text_extents (cr, text, &extents);
+
+    /* logical boundaries in red */
+    cairo_move_to (cr, 0, BORDER);
+    cairo_rel_line_to (cr, WIDTH, 0);
+    cairo_move_to (cr, 0, BORDER + font_extents.ascent);
+    cairo_rel_line_to (cr, WIDTH, 0);
+    cairo_move_to (cr, 0, BORDER + font_extents.ascent + font_extents.descent);
+    cairo_rel_line_to (cr, WIDTH, 0);
+    cairo_move_to (cr, BORDER, 0);
+    cairo_rel_line_to (cr, 0, 2*BORDER + TEXT_SIZE);
+    cairo_move_to (cr, BORDER + extents.x_advance, 0);
+    cairo_rel_line_to (cr, 0, 2*BORDER + TEXT_SIZE);
+    cairo_set_source_rgb (cr, 1, 0, 0);
+    cairo_set_line_width (cr, 2);
+    cairo_stroke (cr);
+
+    /* ink boundaries in green */
+    cairo_rectangle (cr,
+                    BORDER + extents.x_bearing, BORDER + font_extents.ascent + extents.y_bearing,
+                    extents.width, extents.height);
+    cairo_set_source_rgb (cr, 0, 1, 0);
+    cairo_set_line_width (cr, 2);
+    cairo_stroke (cr);
+
+    /* text in gray */
+    cairo_set_source_rgb (cr, 0, 0, 0);
+    cairo_move_to (cr, BORDER, BORDER + font_extents.ascent);
+    cairo_show_text (cr, text);
+
+
+    /* filled version of text in light blue */
+    cairo_set_source_rgb (cr, 0, 0, 1);
+    cairo_move_to (cr, BORDER, BORDER + font_extents.height + BORDER + font_extents.ascent);
+    cairo_text_path (cr, text);
+    cairo_fill (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (user_font_proxy,
+           "Tests a user-font using a native font in its render_glyph",
+           "font, user-font", /* keywords */
+           "cairo >= 1.7.4", /* requirements */
+           WIDTH, HEIGHT,
+           NULL, draw)
diff --git a/test/user-font-rescale.c b/test/user-font-rescale.c
new file mode 100644 (file)
index 0000000..3fe70f6
--- /dev/null
@@ -0,0 +1,368 @@
+/*
+ * Copyright © 2008 Jeff Muizelaar
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Jeff Muizelaar not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Jeff Muizelaar makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * JEFF MUIZELAAR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL JEFF MUIZELAAR BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Contributor(s):
+ *     Jeff Muizelaar <jeff@infidigm.net>
+ *     Kristian Høgsberg <krh@redhat.com>
+ *     Behdad Esfahbod <behdad@behdad.org>
+ */
+
+#include "cairo-test.h"
+
+#include <math.h>
+
+#define BORDER 10
+#define TEXT_SIZE 32
+#define WIDTH  (TEXT_SIZE * 13.75 + 2*BORDER)
+#define HEIGHT ((TEXT_SIZE + 2*BORDER)*3 + BORDER)
+#define TEXT   "test of rescaled glyphs"
+
+static const cairo_user_data_key_t rescale_font_closure_key;
+
+struct rescaled_font {
+    cairo_font_face_t *substitute_font;
+    cairo_scaled_font_t *measuring_font;
+    unsigned long glyph_count;
+    unsigned long start;
+    double *desired_width;
+    double *rescale_factor;
+};
+
+static cairo_status_t
+test_scaled_font_render_glyph (cairo_scaled_font_t  *scaled_font,
+                              unsigned long         glyph,
+                              cairo_t              *cr,
+                              cairo_text_extents_t *metrics)
+{
+    cairo_font_face_t *user_font;
+    struct rescaled_font *r;
+    cairo_glyph_t cairo_glyph;
+
+    cairo_glyph.index = glyph;
+    cairo_glyph.x = 0;
+    cairo_glyph.y = 0;
+
+    user_font = cairo_scaled_font_get_font_face (scaled_font);
+    r = cairo_font_face_get_user_data (user_font, &rescale_font_closure_key);
+    cairo_set_font_face (cr, r->substitute_font);
+
+    if (glyph - r->start < r->glyph_count) {
+       cairo_matrix_t matrix;
+
+       if (isnan (r->rescale_factor[glyph - r->start])) {
+           double desired_width;
+           double actual_width;
+           cairo_text_extents_t extents;
+
+           /* measure the glyph and compute the necessary rescaling factor */
+           cairo_scaled_font_glyph_extents (r->measuring_font,
+                                            &cairo_glyph, 1,
+                                            &extents);
+
+           desired_width = r->desired_width[glyph - r->start];
+           actual_width = extents.x_advance;
+
+           r->rescale_factor[glyph - r->start] = desired_width / actual_width;
+       }
+
+       /* scale the font so that the glyph width matches the desired width */
+       cairo_get_font_matrix (cr, &matrix);
+       cairo_matrix_scale (&matrix, r->rescale_factor[glyph - r->start], 1.);
+       cairo_set_font_matrix (cr, &matrix);
+    }
+
+    cairo_show_glyphs (cr, &cairo_glyph, 1);
+    cairo_glyph_extents (cr, &cairo_glyph, 1, metrics);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+unichar_to_utf8 (uint32_t ucs4, char utf8[7])
+{
+    int i, charlen, first;
+
+    if (ucs4 < 0x80) {
+       first = 0;
+       charlen = 1;
+    } else if (ucs4 < 0x800) {
+       first = 0xc0;
+       charlen = 2;
+    } else if (ucs4 < 0x10000) {
+       first = 0xe0;
+       charlen = 3;
+    } else if (ucs4 < 0x200000) {
+       first = 0xf0;
+       charlen = 4;
+    } else if (ucs4 < 0x4000000) {
+       first = 0xf8;
+       charlen = 5;
+    } else {
+       first = 0xfc;
+       charlen = 6;
+    }
+
+    for (i = charlen - 1; i > 0; --i) {
+       utf8[i] = (ucs4 & 0x3f) | 0x80;
+       ucs4 >>= 6;
+    }
+    utf8[0] = ucs4 | first;
+    utf8[charlen] = '\0';
+}
+
+static cairo_status_t
+test_scaled_font_unicode_to_glyph (cairo_scaled_font_t *scaled_font,
+                                  unsigned long        unicode,
+                                  unsigned long       *glyph_index)
+{
+    cairo_font_face_t *user_font;
+    struct rescaled_font *r;
+    int num_glyphs;
+    cairo_glyph_t *glyphs = NULL;
+    cairo_status_t status;
+    char utf8[7];
+
+    user_font = cairo_scaled_font_get_font_face (scaled_font);
+
+    unichar_to_utf8 (unicode, utf8);
+    r = cairo_font_face_get_user_data (user_font, &rescale_font_closure_key);
+    status  = cairo_scaled_font_text_to_glyphs (r->measuring_font, 0, 0,
+                                               utf8, -1,
+                                               &glyphs, &num_glyphs,
+                                               NULL, NULL, NULL);
+    if (status)
+       return status;
+
+    *glyph_index = glyphs[0].index;
+
+    cairo_glyph_free (glyphs);
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void rescale_font_closure_destroy (void *data)
+{
+    struct rescaled_font *r = data;
+
+    cairo_font_face_destroy (r->substitute_font);
+    cairo_scaled_font_destroy (r->measuring_font);
+    free (r->desired_width);
+    free (r->rescale_factor);
+    free (r);
+}
+
+static cairo_status_t
+create_rescaled_font (cairo_font_face_t *substitute_font,
+                     int glyph_start,
+                     int glyph_count,
+                     double *desired_width,
+                     cairo_font_face_t **out)
+{
+    cairo_font_face_t *user_font_face;
+    struct rescaled_font *r;
+    cairo_font_options_t *options;
+    cairo_status_t status;
+    cairo_matrix_t m;
+    unsigned long i;
+
+    user_font_face = cairo_user_font_face_create ();
+    cairo_user_font_face_set_render_glyph_func (user_font_face, test_scaled_font_render_glyph);
+    cairo_user_font_face_set_unicode_to_glyph_func (user_font_face, test_scaled_font_unicode_to_glyph);
+
+    r = xmalloc (sizeof (struct rescaled_font));
+    r->substitute_font = cairo_font_face_reference (substitute_font);
+
+    /* we don't want any hinting when doing the measuring */
+    options = cairo_font_options_create ();
+    cairo_font_options_set_hint_style (options, CAIRO_HINT_STYLE_NONE);
+    cairo_font_options_set_hint_metrics (options, CAIRO_HINT_METRICS_OFF);
+
+    cairo_matrix_init_identity (&m);
+
+    r->measuring_font = cairo_scaled_font_create (r->substitute_font,
+                                                 &m, &m,
+                                                 options);
+    cairo_font_options_destroy (options);
+
+
+    r->start = glyph_start;
+    r->glyph_count = glyph_count;
+    r->desired_width = xcalloc (sizeof (double), r->glyph_count);
+    r->rescale_factor = xcalloc (sizeof (double), r->glyph_count);
+
+    for (i = 0; i < r->glyph_count; i++) {
+       r->desired_width[i] = desired_width[i];
+       /* use NaN to specify unset */
+       r->rescale_factor[i] = cairo_test_NaN ();
+    }
+
+    status = cairo_font_face_set_user_data (user_font_face,
+                                           &rescale_font_closure_key,
+                                           r, rescale_font_closure_destroy);
+    if (status) {
+       rescale_font_closure_destroy (r);
+       cairo_font_face_destroy (user_font_face);
+       return status;
+    }
+
+    *out = user_font_face;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+get_user_font_face (cairo_font_face_t *substitute_font,
+                   const char *text,
+                   cairo_font_face_t *old,
+                   cairo_font_face_t **out)
+{
+    cairo_font_options_t *options;
+    cairo_matrix_t m;
+    cairo_scaled_font_t *measure;
+    int i;
+    double *widths;
+    int count;
+    int num_glyphs;
+    unsigned long min_index, max_index;
+    cairo_status_t status;
+
+    cairo_glyph_t *glyphs = NULL;
+
+    /* we don't want any hinting when doing the measuring */
+    options = cairo_font_options_create ();
+    cairo_font_options_set_hint_style (options, CAIRO_HINT_STYLE_NONE);
+    cairo_font_options_set_hint_metrics (options, CAIRO_HINT_METRICS_OFF);
+
+    cairo_matrix_init_identity (&m);
+    measure = cairo_scaled_font_create (old, &m, &m, options);
+
+    status = cairo_scaled_font_text_to_glyphs (measure, 0, 0,
+                                              text, -1,
+                                              &glyphs, &num_glyphs,
+                                              NULL, NULL, NULL);
+    cairo_font_options_destroy (options);
+
+    if (status) {
+       cairo_scaled_font_destroy (measure);
+       return status;
+    }
+
+    /* find the glyph range the text covers */
+    max_index = glyphs[0].index;
+    min_index = glyphs[0].index;
+    for (i=0; i<num_glyphs; i++) {
+       if (glyphs[i].index < min_index)
+           min_index = glyphs[i].index;
+       if (glyphs[i].index > max_index)
+           max_index = glyphs[i].index;
+    }
+
+    count = max_index - min_index + 1;
+    widths = xcalloc (sizeof (double), count);
+    /* measure all of the necessary glyphs individually */
+    for (i=0; i<num_glyphs; i++) {
+       cairo_text_extents_t extents;
+       cairo_scaled_font_glyph_extents (measure, &glyphs[i], 1, &extents);
+       widths[glyphs[i].index - min_index] = extents.x_advance;
+    }
+
+    status = cairo_scaled_font_status (measure);
+    cairo_scaled_font_destroy (measure);
+    cairo_glyph_free (glyphs);
+
+    if (status == CAIRO_STATUS_SUCCESS) {
+       status = create_rescaled_font (substitute_font,
+                                      min_index, count, widths,
+                                      out);
+    }
+
+    free (widths);
+    return status;
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_font_extents_t font_extents;
+    cairo_text_extents_t extents;
+    cairo_font_face_t *rescaled;
+    cairo_font_face_t *old;
+    cairo_font_face_t *substitute;
+    const char text[] = TEXT;
+    cairo_status_t status;
+
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_paint (cr);
+
+    cairo_select_font_face (cr,
+                           CAIRO_TEST_FONT_FAMILY " Sans",
+                           CAIRO_FONT_SLANT_NORMAL,
+                           CAIRO_FONT_WEIGHT_NORMAL);
+
+    cairo_set_font_size (cr, TEXT_SIZE);
+
+    cairo_font_extents (cr, &font_extents);
+    cairo_text_extents (cr, text, &extents);
+
+    cairo_set_source_rgb (cr, 0, 0, 0);
+    cairo_move_to (cr, BORDER, BORDER + font_extents.ascent);
+    cairo_show_text (cr, text);
+
+    /* same text in 'mono' with widths that match the 'sans' version */
+    old = cairo_font_face_reference (cairo_get_font_face (cr));
+    cairo_select_font_face (cr,
+                           CAIRO_TEST_FONT_FAMILY " Sans Mono",
+                           CAIRO_FONT_SLANT_NORMAL,
+                           CAIRO_FONT_WEIGHT_NORMAL);
+    substitute = cairo_get_font_face (cr);
+
+    status = get_user_font_face (substitute, text, old, &rescaled);
+    cairo_font_face_destroy (old);
+    if (status) {
+       return cairo_test_status_from_status (cairo_test_get_context (cr),
+                                             status);
+    }
+
+    cairo_set_font_face (cr, rescaled);
+    cairo_font_face_destroy (rescaled);
+
+    cairo_set_source_rgb (cr, 0, 0, 1);
+    cairo_move_to (cr, BORDER, BORDER + font_extents.height + 2*BORDER + font_extents.ascent);
+    cairo_show_text (cr, text);
+
+    /* mono text */
+    cairo_select_font_face (cr,
+                           CAIRO_TEST_FONT_FAMILY " Sans Mono",
+                           CAIRO_FONT_SLANT_NORMAL,
+                           CAIRO_FONT_WEIGHT_NORMAL);
+
+    cairo_set_source_rgb (cr, 0, 0, 1);
+    cairo_move_to (cr, BORDER, BORDER + 2*font_extents.height + 4*BORDER + font_extents.ascent);
+    cairo_show_text (cr, text);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (user_font_rescale,
+           "Tests drawing text with user defined widths",
+           "user-font, font", /* keywords */
+           NULL, /* requirements */
+           WIDTH, HEIGHT,
+           NULL, draw)
diff --git a/test/user-font.c b/test/user-font.c
new file mode 100644 (file)
index 0000000..30bc74e
--- /dev/null
@@ -0,0 +1,267 @@
+/*
+ * Copyright © 2006, 2008 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Contributor(s):
+ *     Kristian Høgsberg <krh@redhat.com>
+ *     Behdad Esfahbod <behdad@behdad.org>
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "cairo-test.h"
+
+/*#define ROTATED 1*/
+
+#define BORDER 10
+#define TEXT_SIZE 64
+#define WIDTH  (TEXT_SIZE * 15 + 2*BORDER)
+#ifndef ROTATED
+ #define HEIGHT ((TEXT_SIZE + 2*BORDER)*2)
+#else
+ #define HEIGHT WIDTH
+#endif
+#define TEXT   "geez... cairo user-font"
+
+#define END_GLYPH 0
+#define STROKE 126
+#define CLOSE 127
+
+/* Simple glyph definition: 1 - 15 means lineto (or moveto for first
+ * point) for one of the points on this grid:
+ *
+ *      1  2  3
+ *      4  5  6
+ *      7  8  9
+ * ----10 11 12----(baseline)
+ *     13 14 15
+ */
+typedef struct {
+    unsigned long ucs4;
+    int width;
+    char data[16];
+} test_scaled_font_glyph_t;
+
+static cairo_user_data_key_t test_font_face_glyphs_key;
+
+static cairo_status_t
+test_scaled_font_init (cairo_scaled_font_t  *scaled_font,
+                      cairo_t              *cr,
+                      cairo_font_extents_t *metrics)
+{
+  metrics->ascent  = .75;
+  metrics->descent = .25;
+  return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+test_scaled_font_unicode_to_glyph (cairo_scaled_font_t *scaled_font,
+                                  unsigned long        unicode,
+                                  unsigned long       *glyph)
+{
+    test_scaled_font_glyph_t *glyphs = cairo_font_face_get_user_data (cairo_scaled_font_get_font_face (scaled_font),
+                                                                     &test_font_face_glyphs_key);
+    int i;
+
+    for (i = 0; glyphs[i].ucs4 != (unsigned long) -1; i++)
+       if (glyphs[i].ucs4 == unicode) {
+           *glyph = i;
+           return CAIRO_STATUS_SUCCESS;
+       }
+
+    /* Not found.  Default to glyph 0 */
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+test_scaled_font_render_glyph (cairo_scaled_font_t  *scaled_font,
+                              unsigned long         glyph,
+                              cairo_t              *cr,
+                              cairo_text_extents_t *metrics)
+{
+    test_scaled_font_glyph_t *glyphs = cairo_font_face_get_user_data (cairo_scaled_font_get_font_face (scaled_font),
+                                                                     &test_font_face_glyphs_key);
+    int i;
+    const char *data;
+    div_t d;
+    double x, y;
+
+    /* FIXME: We simply crash on out-of-bound glyph indices */
+
+    metrics->x_advance = glyphs[glyph].width / 4.0;
+
+    cairo_set_line_width (cr, 0.1);
+    cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND);
+    cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
+
+    data = glyphs[glyph].data;
+    for (i = 0; data[i] != END_GLYPH; i++) {
+       switch (data[i]) {
+       case STROKE:
+           cairo_new_sub_path (cr);
+           break;
+
+       case CLOSE:
+           cairo_close_path (cr);
+           break;
+
+       default:
+           d = div (data[i] - 1, 3);
+           x = d.rem / 4.0 + 0.125;
+           y = d.quot / 5.0 + 0.4 - 1.0;
+           cairo_line_to (cr, x, y);
+       }
+    }
+    cairo_stroke (cr);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_user_font_face_create (cairo_font_face_t **out)
+{
+    /* Simple glyph definition: 1 - 15 means lineto (or moveto for first
+     * point) for one of the points on this grid:
+     *
+     *      1  2  3
+     *      4  5  6
+     *      7  8  9
+     * ----10 11 12----(baseline)
+     *     13 14 15
+     */
+    static const test_scaled_font_glyph_t glyphs [] = {
+       { 'a',  3, { 4, 6, 12, 10, 7, 9, STROKE, END_GLYPH } },
+       { 'c',  3, { 6, 4, 10, 12, STROKE, END_GLYPH } },
+       { 'e',  3, { 12, 10, 4, 6, 9, 7, STROKE, END_GLYPH } },
+       { 'f',  3, { 3, 2, 11, STROKE, 4, 6, STROKE, END_GLYPH } },
+       { 'g',  3, { 12, 10, 4, 6, 15, 13, STROKE, END_GLYPH } },
+       { 'h',  3, { 1, 10, STROKE, 7, 5, 6, 12, STROKE, END_GLYPH } },
+       { 'i',  1, { 1, 1, STROKE, 4, 10, STROKE, END_GLYPH } },
+       { 'l',  1, { 1, 10, STROKE, END_GLYPH } },
+       { 'n',  3, { 10, 4, STROKE, 7, 5, 6, 12, STROKE, END_GLYPH } },
+       { 'o',  3, { 4, 10, 12, 6, CLOSE, END_GLYPH } },
+       { 'r',  3, { 4, 10, STROKE, 7, 5, 6, STROKE, END_GLYPH } },
+       { 's',  3, { 6, 4, 7, 9, 12, 10, STROKE, END_GLYPH } },
+       { 't',  3, { 2, 11, 12, STROKE, 4, 6, STROKE, END_GLYPH } },
+       { 'u',  3, { 4, 10, 12, 6, STROKE, END_GLYPH } },
+       { 'z',  3, { 4, 6, 10, 12, STROKE, END_GLYPH } },
+       { ' ',  1, { END_GLYPH } },
+       { '-',  2, { 7, 8, STROKE, END_GLYPH } },
+       { '.',  1, { 10, 10, STROKE, END_GLYPH } },
+       {  -1,  0, { END_GLYPH } },
+    };
+
+    cairo_font_face_t *user_font_face;
+    cairo_status_t status;
+
+    user_font_face = cairo_user_font_face_create ();
+    cairo_user_font_face_set_init_func             (user_font_face, test_scaled_font_init);
+    cairo_user_font_face_set_render_glyph_func     (user_font_face, test_scaled_font_render_glyph);
+    cairo_user_font_face_set_unicode_to_glyph_func (user_font_face, test_scaled_font_unicode_to_glyph);
+
+    status = cairo_font_face_set_user_data (user_font_face,
+                                           &test_font_face_glyphs_key,
+                                           (void*) glyphs, NULL);
+    if (status) {
+       cairo_font_face_destroy (user_font_face);
+       return status;
+    }
+
+    *out = user_font_face;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_font_face_t *font_face;
+    const char text[] = TEXT;
+    cairo_font_extents_t font_extents;
+    cairo_text_extents_t extents;
+    cairo_status_t status;
+
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_paint (cr);
+
+#ifdef ROTATED
+    cairo_translate (cr, TEXT_SIZE, 0);
+    cairo_rotate (cr, .6);
+#endif
+
+    status = _user_font_face_create (&font_face);
+    if (status) {
+       return cairo_test_status_from_status (cairo_test_get_context (cr),
+                                             status);
+    }
+
+    cairo_set_font_face (cr, font_face);
+    cairo_font_face_destroy (font_face);
+
+    cairo_set_font_size (cr, TEXT_SIZE);
+
+    cairo_font_extents (cr, &font_extents);
+    cairo_text_extents (cr, text, &extents);
+
+    /* logical boundaries in red */
+    cairo_move_to (cr, 0, BORDER);
+    cairo_rel_line_to (cr, WIDTH, 0);
+    cairo_move_to (cr, 0, BORDER + font_extents.ascent);
+    cairo_rel_line_to (cr, WIDTH, 0);
+    cairo_move_to (cr, 0, BORDER + font_extents.ascent + font_extents.descent);
+    cairo_rel_line_to (cr, WIDTH, 0);
+    cairo_move_to (cr, BORDER, 0);
+    cairo_rel_line_to (cr, 0, 2*BORDER + TEXT_SIZE);
+    cairo_move_to (cr, BORDER + extents.x_advance, 0);
+    cairo_rel_line_to (cr, 0, 2*BORDER + TEXT_SIZE);
+    cairo_set_source_rgb (cr, 1, 0, 0);
+    cairo_set_line_width (cr, 2);
+    cairo_stroke (cr);
+
+    /* ink boundaries in green */
+    cairo_rectangle (cr,
+                    BORDER + extents.x_bearing, BORDER + font_extents.ascent + extents.y_bearing,
+                    extents.width, extents.height);
+    cairo_set_source_rgb (cr, 0, 1, 0);
+    cairo_set_line_width (cr, 2);
+    cairo_stroke (cr);
+
+    /* text in black */
+    cairo_set_source_rgb (cr, 0, 0, 0);
+    cairo_move_to (cr, BORDER, BORDER + font_extents.ascent);
+    cairo_show_text (cr, text);
+
+
+    /* filled version of text in blue */
+    cairo_set_source_rgb (cr, 0, 0, 1);
+    cairo_move_to (cr, BORDER, BORDER + font_extents.height + 2*BORDER + font_extents.ascent);
+    cairo_text_path (cr, text);
+    cairo_fill (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (user_font,
+           "Tests user font feature",
+           "font, user-font", /* keywords */
+           "cairo >= 1.7.4", /* requirements */
+           WIDTH, HEIGHT,
+           NULL, draw)
diff --git a/test/white-in-noop.c b/test/white-in-noop.c
new file mode 100644 (file)
index 0000000..e6a93e8
--- /dev/null
@@ -0,0 +1,52 @@
+/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/*
+ * Copyright 2011 Andrea Canciani
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Andrea Canciani <ranma42@gmail.com>
+ */
+
+#include "cairo-test.h"
+
+#define HEIGHT 4
+#define WIDTH 4
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_set_source_rgb (cr, 0, 0, 0);
+    cairo_paint (cr);
+
+    cairo_set_operator (cr, CAIRO_OPERATOR_IN);
+
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_paint (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (white_in_noop,
+           "Test an invalid optimization of the IN operator with white sources",
+           "operator", /* keywords */
+           NULL, /* requirements */
+           WIDTH, HEIGHT,
+           NULL, draw)
diff --git a/test/world-map.c b/test/world-map.c
new file mode 100644 (file)
index 0000000..f775bb0
--- /dev/null
@@ -0,0 +1,151 @@
+/*
+ * Copyright © 2006 Red Hat, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ */
+
+#include "cairo-test.h"
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#define WIDTH 800
+#define HEIGHT 400
+
+typedef enum {
+    WM_NEW_PATH,
+    WM_MOVE_TO,
+    WM_LINE_TO,
+    WM_HLINE_TO,
+    WM_VLINE_TO,
+    WM_REL_LINE_TO,
+    WM_END
+} wm_type_t;
+
+typedef struct _wm_element {
+    wm_type_t type;
+    double x;
+    double y;
+} wm_element_t;
+
+#include "world-map.h"
+
+enum {
+    STROKE = 1,
+    FILL = 2,
+};
+
+static cairo_test_status_t
+draw_world_map (cairo_t *cr, int width, int height, int mode)
+{
+    const wm_element_t *e;
+    double cx, cy;
+
+    cairo_set_line_width (cr, 0.2);
+
+    cairo_set_source_rgb (cr, .68, .85, .90); /* lightblue */
+    cairo_rectangle (cr, 0, 0, 800, 400);
+    cairo_fill (cr);
+
+    e = &countries[0];
+    while (1) {
+       switch (e->type) {
+       case WM_NEW_PATH:
+       case WM_END:
+           if (mode & FILL) {
+               cairo_set_source_rgb (cr, .75, .75, .75); /* silver */
+               cairo_fill_preserve (cr);
+           }
+           if (mode & STROKE) {
+               cairo_set_source_rgb (cr, .50, .50, .50); /* gray */
+               cairo_stroke (cr);
+           }
+           cairo_new_path (cr);
+           cairo_move_to (cr, e->x, e->y);
+           break;
+       case WM_MOVE_TO:
+           cairo_close_path (cr);
+           cairo_move_to (cr, e->x, e->y);
+           break;
+       case WM_LINE_TO:
+           cairo_line_to (cr, e->x, e->y);
+           break;
+       case WM_HLINE_TO:
+           cairo_get_current_point (cr, &cx, &cy);
+           cairo_line_to (cr, e->x, cy);
+           break;
+       case WM_VLINE_TO:
+           cairo_get_current_point (cr, &cx, &cy);
+           cairo_line_to (cr, cx, e->y);
+           break;
+       case WM_REL_LINE_TO:
+           cairo_rel_line_to (cr, e->x, e->y);
+           break;
+       }
+       if (e->type == WM_END)
+           break;
+       e++;
+    }
+
+    cairo_new_path (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+static cairo_test_status_t
+draw_world_map_stroke (cairo_t *cr, int width, int height)
+{
+    return draw_world_map (cr, width, height, STROKE);
+}
+
+static cairo_test_status_t
+draw_world_map_fill (cairo_t *cr, int width, int height)
+{
+    return draw_world_map (cr, width, height, FILL);
+}
+
+static cairo_test_status_t
+draw_world_map_both (cairo_t *cr, int width, int height)
+{
+    return draw_world_map (cr, width, height, FILL | STROKE);
+}
+
+CAIRO_TEST (world_map,
+           "Tests a complex drawing (part of the performance suite)",
+           "fill, stroke", /* keywords */
+           NULL, /* requirements */
+           WIDTH, HEIGHT,
+           NULL, draw_world_map_both)
+CAIRO_TEST (world_map_stroke,
+           "Tests a complex drawing (part of the performance suite)",
+           "stroke", /* keywords */
+           NULL, /* requirements */
+           WIDTH, HEIGHT,
+           NULL, draw_world_map_stroke)
+CAIRO_TEST (world_map_fill,
+           "Tests a complex drawing (part of the performance suite)",
+           "fill", /* keywords */
+           NULL, /* requirements */
+           WIDTH, HEIGHT,
+           NULL, draw_world_map_fill)
diff --git a/test/world-map.h b/test/world-map.h
new file mode 100644 (file)
index 0000000..af58b80
--- /dev/null
@@ -0,0 +1,196 @@
+/* The data for this test case was provided by Gary Nicholson
+ * <gary@imapping.co.nz> who originally created an interactive SVG map
+ * of the world as can be seen here:
+ *
+ *     http://www.wherearewe.co.nz/svg.html
+ *
+ * The data is used here by permission from Gary given on 2006-11-08:
+ *
+ *     Thanks for asking, I don't need any attribution if you are
+ *     only using the vectors and not the entire map with
+ *     interactivity etc. So feel free to do what you wish with
+ *     the data.
+ */
+
+#define N(x,y) { WM_NEW_PATH,    x, y }
+#define M(x,y) { WM_MOVE_TO,     x, y }
+#define L(x,y) { WM_LINE_TO,     x, y }
+#define H(x,y) { WM_HLINE_TO,    x, y }
+#define V(x,y) { WM_VLINE_TO,    x, y }
+#define l(x,y) { WM_REL_LINE_TO, x, y }
+#define E(x,y) { WM_END,         x, y }
+
+static const wm_element_t countries[] = {
+N(413.519,90.071),l(.136,-.348),l(-.31,-.204),l(-.017,-.327),l(.213,-.322),l(.245,-.147),l(.142,-.08),l(.225,.072),l(.062,.301),l(.41,.312),l(.466,.096),l(-.044,.288),l(-.248,.144),l(.074,.353),l(-.145,-.063),l(-.568,-.011),l(-.642,-.063),
+N(421.683,94.397),l(.193,.336),l(-.266,.274),l(.214,.288),l(-.09,.192),l(.622,.062),l(.008,.144),l(.55,.242),l(.579,-.332),l(.215,.117),l(-.029,.171),l(-.126,.309),l(.112,.212),l(-.038,.192),l(-.315,-.051),l(-.176,-.162),l(-.283,.091),l(-.081,.273),l(.244,.131),l(-.228,.415),l(-.244,-.333),l(-.469,.05),l(-.071,.122),l(-.216,.03),l(-.23,-.142),l(-.143,-.354),l(-.371,.081),l(.019,.333),l(-.425,.384),l(-.018,.535),l(-.285,.151),l(-.385,-.312),l(.098,-.182),l(-.311,-.071),l(-.534,-.363),l(-.016,-.415),l(-.777,.404),l(.103,.212),l(-.349,.432),l(-.275,.16),l(-.629,-.168),l(-.627,.204),l(-.599,-.062),l(-.102,-.424),l(-.312,-.806),l(-.616,.147),l(-.854,.668),l(-.369,-.111),l(.238,-.226),l(.013,-.322),l(-.08,-.137),l(.089,-.294),l(.718,-.418),l(-.038,-.315),l(.575,-.24),l(.012,-.076),l(.528,-.494),l(.173,-.035),l(-.116,-.089),l(-.153,-.028),l(.221,-.302),l(.446,.007),l(-.005,.096),l(.473,.007),l(.385,-.309),l(.271,.089),l(.272,-.117),l(.271,.096),l(.567,-.158),l(.278,.11),l(.354,-.021),l(-.179,-.199),l(.709,-.199),l(.017,.151),l(.199,-.014),l(.149,.089),l(.852,.007),l(.664,.261),
+N(421.394,104.558),l(.104,.175),l(.04,.256),l(-.06,.475),l(.118,.054),l(.062,.333),l(-.076,.795),l(-.211,.327),l(-.118,.724),l(-.292,.501),l(-.298,-.043),l(-.057,-.196),l(-.41,-.076),l(-.227,-.152),l(.284,-.207),l(-.07,-.076),l(-.437,-.098),l(.257,-.332),l(-.11,-.071),l(-.291,.071),l(-.053,-.147),l(.115,-.022),l(.175,-.158),l(-.094,-.153),l(-.257,-.082),l(.015,-.164),l(.247,-.12),l(-.284,-.218),l(.241,-.284),l(.6,-.305),l(.27,-.022),l(.04,-.125),l(.292,-.043),l(.195,.104),l(.096,-.142),l(-.022,-.344),l(.072,-.224),l(.143,-.011),M(396.323,103.853),l(.375,-.122),l(.411,-.365),l(.549,-2.299),l(.397,-.091),l(-.21,-.29),l(-.226,.259),l(.125,-1.144),l(.223,-.826),l(.115,.153),l(.496,.306),l(.191,.382),l(.191,.229),l(-.281,-.673),l(-.63,-.582),l(-.242,-.233),l(.024,-.249),l(.359,-.477),l(-.202,-.375),l(-.2,-.274),l(-.326,-.216),l(-.685,-.1),l(-.515,-.571),l(-.416,-.323),l(.278,-.426),l(-.233,-.181),l(-.343,-.131),l(-.511,-.142),l(-.184,-.173),l(.247,-.376),l(-.329,-.173),l(-.509,.179),l(-.489,-.249),l(-.824,-.251),l(-.619,-.181),l(-.325,.014),l(-.215,-.25),l(-.91,.167),l(-.059,-.25),l(-.265,-.125),l(-.367,-.042),l(-.056,-.104),l(.861,-.083),l(-.085,-.229),l(-.526,-.104),l(.442,-.104),l(.086,-.205),l(-.275,.017),l(-.263,-.021),l(-.417,.083),l(.04,-.438),l(.303,.012),l(.305,-.146),l(.526,-.088),l(.562,-.174),l(.215,.188),l(.18,-.167),l(.474,.063),l(.112,-.26),l(.272,-.059),l(.764,-.078),l(.393,.366),l(.275,.26),l(.342,.083),l(.652,-.271),l(.317,.167),l(.276,-.127),l(.457,-.124),l(.029,.23),l(.591,-.065),l(.3,-.103),l(-.265,-.188),l(-.028,-.251),l(.056,-.345),l(-.037,-.428),l(-.131,.021),l(-.562,-.682),l(-.11,-.407),l(.927,.126),l(.607,-.105),l(-.084,.397),l(.248,.419),l(.342,-.146),l(1.241,.104),l(.501,.146),l(.079,-.014),l(.525,-.093),l(.662,-.27),l(-.534,-.124),l(.055,-.204),l(.166,-.175),l(.753,-.322),l(.756,-.181),l(.902,-.215),l(.314,-.235),l(.302,-.264),l(-.053,-.775),l(.135,-.542),l(.521,-.25),l(.46,-.16),l(.916,-.092),l(.177,-.096),l(.208,.447),l(.311,.335),l(.266,.127),l(.141,-.071),l(.41,-.208),l(.153,.17),l(.202,.458),l(.194,.133),l(.518,-.012),l(.159,.301),l(.259,-.012),l(.576,.048),l(.375,.168),l(-.159,.241),l(.091,.175),l(-.072,.198),l(.285,.122),l(.406,-.075),l(.446,-.035),l(.193,-.313),l(.245,-.072),l(-.119,.373),l(.146,.18),l(-.039,.228),l(.529,.048),l(.341,.192),l(.371,.204),l(.127,.228),l(.694,-.174),l(.079,.114),l(.642,.063),l(.568,.011),l(.145,.063),l(.428,.319),l(.337,.277),l(.395,-.055),l(.045,.145),l(.689,-.062),l(.072,-.048),l(.233,.007),l(.095,.186),l(.456,.09),l(.479,-.014),l(.605,.193),l(-.954,.806),l(-.054,.213),l(-.054,.358),l(-.321,.372),l(-.075,.295),l(.091,.076),l(-.216,.701),l(.135,.233),l(-.385,.309),l(-.473,-.007),l(.005,-.096),L(415.96,94.5),l(-.221,.302),l(.153,.028),l(.116,.089),l(-.173,.035),l(-.528,.494),l(-.012,.076),l(-.575,.24),l(.038,.315),l(-.718,.418),l(-.089,.294),l(.08,.137),l(-.013,.322),l(-.238,.226),l(.369,.111),l(.854,-.668),l(.616,-.147),l(.312,.806),l(.102,.424),l(-.624,.301),l(.532,.344),l(.025,.292),l(.43,.192),l(-.199,.272),l(-.541,.353),l(-.183,-.111),l(-.437,.186),l(.352,.358),l(.616,.191),l(.135,.331),l(-.175,.01),l(-.315,.371),l(.193,.442),l(.754,.391),l(.849,-.07),l(.062,.281),l(-.146,.469),l(-.346,.23),l(-.221,.215),l(-.833,.488),l(-.889,.659),l(-.427,.087),l(-.318,.043),l(-.798,.159),l(-.405,-.028),l(-.471,-.156),l(-.851,-.499),l(-.315,-.085),l(-.354,.029),l(-.231,.072),l(-.511,-.056),l(-.752,-.313),l(-.602,.044),l(-.731,.345),l(-.357,.258),l(-.555,.559),l(-.147,.386),l(.099,.514),l(.091,.379),l(-.334,-.091),l(-.75,.137),l(-.039,.136),l(-.485,-.015),l(-.427,-.197),l(-.395,.167),l(-.261,-.015),l(-.036,-.152),l(-.335,-.091),l(-.206,.03),l(-.374,.076),l(-.187,-.076),l(-.035,-.289),l(-.091,-.213),l(-1.252,-.304),l(-.355,0),l(.017,.319),l(-.542,-.015),l(-.337,.061),l(-.037,-.122),l(-.767,.03),l(-.084,-.114),l(-.028,-.038),l(-.431,-.152),l(-.131,.076),l(-.262,-.03),l(-.056,.076),l(-.507,-.395),l(-.15,.061),l(-1.088,-.334),l(-.112,.106),l(-.15,-.03),l(-.094,-.106),l(.205,-.243),l(-.058,-.122),l(-.469,.03),l(-.472,-.243),
+N(681.312,116.395),l(.235,-.171),l(.283,-.256),l(.633,-.738),l(.315,-.157),l(.595,.011),l(.579,.068),l(.511,.096),l(.309,-.115),l(.571,-.678),l(.682,.621),l(1.178,1.611),l(.329,.495),l(.269,.664),l(.002,.75),l(-.034,.947),l(-.129,.637),l(.143,.113),l(.5,-.043),l(-.121,.41),l(-.282,.523),l(-.5,.75),l(-.316,.312),l(-.243,.043),l(-.567,-.211),l(-.256,.1),l(-.607,.58),l(-.431,-.083),l(-.289,-.225),l(-.544,.1),l(-.526,.199),l(-1.188,.835),l(-.462,.043),l(-.46,.312),l(-.055,-.564),l(-.056,-.324),l(-.163,-.705),l(-.137,-.395),l(.167,-.453),l(.499,-.468),l(0,-.353),l(.226,-.425),l(-.044,-.141),l(-.378,-.311),l(-.095,-.296),l(.015,-.467),l(-.087,-.339),l(-.289,-.126),l(-.603,-.084),l(.654,-.411),l(.303,-.114),l(.654,.268),l(.254,-.241),l(-.029,-.283),l(-.764,-.89),l(-.113,-.311),l(-.137,-.105),
+N(475.646,121.847),l(-.018,.175),l(.338,.391),l(-.295,-.009),l(-.132,.108),l(-.104,-.059),l(-.327,-.021),l(-.121,.33),l(-.783,.257),l(-.384,.046),l(-.099,.053),l(0,.21),l(-.217,.006),l(-.072,-.192),l(-.402,.023),l(-.547,-.146),l(-.191,-.087),l(0,-.21),l(-.161,-.105),l(-.122,-.403),l(.082,-.035),l(.12,.1),l(.147,-.006),l(.405,-.304),l(.253,-.006),l(.328,.092),l(.077,-.086),l(.088,-.286),l(-.053,-.175),l(.627,.093),l(.658,.027),l(.367,-.056),l(.818,-.233),l(.689,-.304),l(.535,-.158),l(-.475,.295),l(-.436,.231),l(-.596,.444),
+N(704.404,117.274),l(.197,-.099),l(1.108,-.271),l(.057,.354),l(-.481,.284),l(-.232,.241),l(-.068,.453),l(.139,.367),l(.291,.056),l(.221,-.114),l(.418,-.354),l(.24,-.085),l(1.656,-.697),l(.389,-.213),l(.46,-.326),l(.349,-.638),l(.76,-.412),l(.347,-.327),l(.191,-.269),l(.142,-.51),l(.538,-.582),l(-.01,-.142),l(.344,-.567),l(.159,-.468),l(.139,-.609),l(-.043,-.467),l(-.33,-.198),l(-.128,-.24),l(.234,-.213),l(.166,-.284),l(-.155,-1.023),l(.544,-.343),l(.176,-.242),l(.327,-.328),l(.192,0),l(.21,.355),l(.199,.227),l(.303,-.058),l(.799,-.257),l(-.169,-.526),l(-.311,-.028),l(-.36,-.312),l(.694,-.415),l(.441,.156),l(.336,.227),l(.025,.199),l(-.016,.868),l(.058,.611),l(.22,.127),l(.243,.312),l(.717,1.432),l(.001,.496),l(-.246,.709),l(-.709,.766),l(-.226,.439),l(.064,.368),l(-.15,.071),l(-.737,.285),l(-.161,.113),l(-.164,.199),l(-.174,.453),l(.02,.396),l(.094,.254),l(.131,.792),l(-.04,.693),l(-.686,.751),l(-.242,.736),l(.02,.707),l(.198,.296),l(.422,.353),l(-.617,.298),l(-.193,.127),l(-.166,.17),l(-.174,.834),l(-1.081,.439),l(-.094,-.282),l(.294,-.665),l(.184,-.523),l(-.198,-.126),l(-.514,.241),l(-.578,.623),l(-.476,.001),l(-.346,.312),l(-.066,.748),l(-.354,.269),l(-.188,-.028),l(-.066,-.155),l(.003,-.606),l(-.149,-.155),l(-.211,.042),l(-.309,.156),l(-.344,.311),l(-.325,.523),l(-.866,-.055),l(-.505,.057),l(-.631,.1),l(-.458,-.549),l(-.685,-.323),l(-.26,.254),l(-.067,.184),l(-.177,.353),l(.037,.056),l(.417,.197),l(.416,.323),l(-.293,.198),l(-.829,.129),l(-.433,.241),l(-.463,.622),l(-.522,.847),l(-.688,-.365),l(-.565,-.21),l(-.285,-.197),l(-.014,-.169),l(-.194,-.818),l(.099,-.155),l(.495,-.325),l(.179,-.269),l(-.067,-.282),l(-.18,-.042),l(-.601,.17),l(-.341,-.028),l(-.789,-.167),l(-.475,.128),l(-.427,.227),l(-.437,.184),l(-.269,-.098),l(-.256,-.027),l(-1.647,.398),l(-.814,.298),l(-.21,-.31),l(-.452,-.042),l(-.413,.438),l(-.006,.635),l(-.756,-.238),l(-.579,-.055),l(-1.1,.073),l(-.267,-.14),l(.072,-.339),l(.179,-.283),l(.483,.013),l(.499,-.114),l(.751,-.467),l(2.201,-1.953),l(.28,-.015),l(.427,-.128),l(.056,.424),l(.495,-.128),l(1.278,-.257),l(.933,-.058),l(1.183,-.172),l(.892,-.256),l(.068,.452),l(.377,.268),l(.167,-.085),l(.654,-.199),l(.446,-.34),l(-.003,-.353),l(.114,-.467),l(.465,-.51),l(.698,-.581),l(.371,-.453),l(-.062,-1.117),l(.182,-.213),M(695.464,127.756),l(-.292,-.197),l(-.223,-.268),l(-.101,-.381),l(-.177,-.395),l(-.492,-.535),l(.731,-.382),l(.287,-.269),l(.456,-.593),l(.409,.253),l(.615,-.015),l(.483,-.185),l(.311,-.339),l(.451,-.311),l(.454,-.029),l(.316,.169),l(.862,.224),l(.153,.254),l(-.1,.127),l(-.102,.423),l(-.292,.24),l(-.864,.876),l(-.181,-.211),l(-.424,-.295),l(-.467,-.042),l(-.612,.213),l(-.193,.184),l(-.245,.495),l(-.165,.508),l(-.153,.212),l(-.448,.269),M(691.12,131.448),l(-.366,-.042),l(-.056,-.141),l(.268,-.537),l(.128,-.593),l(-.334,-.112),l(-.239,.198),l(-.155,.466),l(-.381,.452),l(-.326,-.211),l(-.059,-.211),l(.322,-.466),l(.032,-.296),l(-.356,-.917),l(.169,-.113),l(.687,-.58),l(.083,-.141),l(.034,-.466),l(-.532,-.789),l(-.333,-.042),l(-.162,.269),l(-.419,.495),l(-.249,-.112),l(-.23,-.508),l(-.376,-.267),l(-.261,-.366),l(.41,-.325),l(.733,.083),l(.706,-.171),l(.315,-.466),l(.241,-.283),l(.484,-.058),l(.478,.056),l(.249,.38),l(.27,.168),l(.43,.084),l(.628,-.213),l(.225,.395),l(-.569,.438),l(.405,.239),l(.443,.437),l(.079,.254),l(-.596,.58),l(-.242,.41),l(-.104,.367),l(-.085,.621),l(-.109,.649),l(-.242,.353),l(-.194,.099),l(-.165,.071),l(-.197,.184),l(-.479,.678),M(711.938,108.369),l(-.222,-.241),l(-.077,-.271),l(.325,-.642),l(-.055,-.342),l(-.549,-.198),l(-.168,-.171),l(-.146,-.812),l(.583,-.386),l(.522,-.172),l(.646,-.373),l(.037,-.356),l(-.318,-.285),l(.277,-.3),l(.224,-.015),l(.661,.427),l(.373,.085),l(.532,-.201),l(-.004,-1.186),l(.455,-.187),l(.45,-.244),l(.074,-.743),l(.007,-.844),l(-.398,-.758),l(-.098,-.473),l(.166,-.216),l(.618,-.346),l(.063,.072),l(.507,.43),l(.904,.816),l(1.07,.842),l(1.083,.684),l(.627,.285),l(.528,.17),l(1.02,.198),l(.282,.042),l(.304,-.086),l(.866,-.66),l(.461,-.144),l(.002,.1),l(-.308,.358),l(-.335,.558),l(.198,.414),l(.469,.599),l(.197,.356),l(-.561,.272),l(-.447,.244),l(-.534,.158),l(-.365,.015),l(-.488,-.199),l(-.453,.015),l(-.363,.144),l(-.345,.229),l(-.754,.786),l(-.396,.5),l(-.26,.599),l(-.4,-.07),l(-.425,-.241),l(-2.031,-.965),l(-.461,-.085),l(-.72,.044),l(-1.038,.587),l(-.153,-.299),l(-.372,-.356),l(-.339,.029),l(-.266,.115),l(-.439,.272),l(.049,.299),l(1.16,.497),l(.56,.298),l(.302,.27),l(-.391,.214),l(-.303,.029),l(-.305,-.128),l(-.261,.043),l(-.324,.314),l(-.388,.471),l(-.347,.114),
+N(597.917,139.685),l(1.251,-1.545),l(.609,-.539),l(.348,-.239),l(.149,-.103),l(.417,-.016),l(.309,.294),l(.479,.208),l(1.659,.047),l(.371,.041),l(.312,.209),l(.329,.619),l(-.07,.156),l(.042,.24),l(.326,.294),l(.313,.069),l(.258,.238),l(.017,.282),l(-.217,.58),l(-.624,.06),l(-1.036,.062),l(-1.238,-.063),l(-.335,-.125),l(-.301,-.055),l(-.531,.313),l(-.544,.074),l(-.085,-.021),l(-.869,-.214),l(-.559,-.081),l(-.637,-.18),l(-.235,-.493),l(.092,-.113),
+N(362.764,152.723),l(.072,-.625),l(.395,-.876),l(.52,-.552),l(.488,-.566),l(.244,-.509),l(1.175,-2.559),l(.238,-.241),l(1.404,-1.175),l(.345,-.495),l(.051,-.918),l(.305,-1.088),l(.651,-1.075),l(.399,-.34),l(.404,-.198),l(.838,-.51),l(.361,-.495),l(.334,-.777),l(.428,-.851),l(1.635,-.04),l(2.511,0),l(2.677,-.001),l(1.718,.004),l(1.42,-.008),l(.027,.876),l(-.03,1.752),l(.002,.65),l(-.104,.396),l(-.56,-.011),l(-6.005,-.022),l(-.557,.074),l(-.047,.509),l(-.07,2.261),l(-.099,2.6),l(-.144,.128),l(-.809,.287),l(-.726,.315),l(-.575,.427),l(-.249,.383),l(-.01,.707),l(.164,1.539),l(.051,1.102),l(-.212,-.027),l(-.732,.033),l(-2.396,-.014),l(-5.055,-.056),l(-.474,-.013),
+N(514.551,145.841),l(-.374,.027),l(-.336,-.083),l(-.008,-.615),l(-.153,-.437),l(-.108,-.791),l(.187,-.607),l(.188,-.11),l(-.059,-.187),l(.177,-.607),l(.33,-.269),l(.312,.083),l(.069,.315),l(.26,.093),l(.063,.199),l(.116,.326),l(-.106,.42),l(.031,.708),l(.118,.254),l(-.104,.381),l(-.327,.467),l(-.275,.433),
+N(514.177,145.868),l(.374,-.027),l(.008,.288),l(.361,.14),l(.153,.128),l(.186,-.093),l(-.046,.443),l(.397,.001),l(.402,.127),l(.687,-.093),l(.103,-.21),l(.183,-.058),l(.218,.117),l(.424,-.042),l(.595,.112),l(.224,-.035),l(.079,-.105),l(1.358,.222),l(.732,-.14),l(-.022,-.292),l(.225,.175),l(.375,-.016),l(.157,-.099),l(.312,-.422),l(.232,-.073),l(.267,-.495),l(.131,-.297),l(.711,-.637),l(.813,-.889),l(.163,.105),l(.229,-.178),l(.85,-.708),l(.313,-.433),l(.15,.161),l(-.248,.42),l(-.107,.299),l(-.004,.176),l(.099,.064),l(.121,-.024),l(.454,.042),l(.09,.324),l(.001,.508),l(-.003,.358),l(-.49,.034),l(-.401,-.083),l(-.107,.396),l(.073,1.326),l(-.199,.34),l(-.536,.596),l(.003,.946),l(.024,2.075),l(.063,.183),l(-.152,.057),l(-.584,.469),l(-.839,-.108),l(-3.387,-.446),l(-3.362,-.375),l(-.261,-.902),l(-.548,-1.154),l(-1.043,-2.198),
+N(668.627,151.365),l(-.102,-.056),l(-.107,-.325),l(-.922,-1.212),l(-.332,-.987),l(-.03,-.438),l(.156,-.749),l(.546,-.792),l(1.312,-1.852),l(.259,-.184),l(.425,-.128),l(.229,-.184),l(.358,-.227),l(.228,.127),l(.554,.394),l(-.334,.424),l(-.084,.142),l(.023,.31),l(-.067,.622),l(-.203,.296),l(-.182,.354),l(-.065,.692),l(-.1,.494),l(-.317,.805),l(-.473,.707),l(-.417,.833),l(-.014,.353),l(-.114,.438),l(-.228,.142),
+N(389.765,144.543),l(.1,.084),l(.895,.531),l(2.054,1.344),l(.811,.575),l(3.283,2.241),l(1.924,1.26),l(1.292,.824),l(.397,.253),l(2.472,1.469),l(.181,.253),l(-.096,.396),l(.082,.183),l(.393,.28),l(1.111,1.039),l(.229,.027),l(.47,-.314),l(.588,.562),l(.375,.167),l(.748,.024),l(.309,.111),l(.277,.352),l(.099,.522),l(-.161,.679),l(.146,.564),l(2.176,-.408),l(.064,1.017),l(.034,2.203),l(.001,.96),l(-.08,.89),l(-.145,.919),l(-.434,1.246),l(-.596,.794),l(-.339,.271),l(-.29,.129),l(-2.533,.085),l(-1.808,.124),l(-.209,.072),l(-.562,.427),l(-.579,.272),l(-.678,-.053),l(-.581,-.081),l(-1.062,-.173),l(-.36,-.059),l(-.356,-.125),l(-.37,.073),l(-1.22,.713),l(-.947,.458),l(-.304,.228),l(-.314,.793),l(-.274,-.027),l(-.324,-.182),l(-.518,-.209),l(-.272,.101),l(-.638,.625),l(-.492,.667),l(-.393,.822),l(-.174,.227),l(-.45,.102),l(-.551,-.364),l(-.293,-.281),l(-.273,.058),l(-.397,.384),l(-.355,1.217),l(-.292,1.047),l(-.317,.369),l(-.543,.271),l(-.448,.158),l(-.257,.016),l(-.141,.255),l(.058,.749),l(-.133,.876),l(-.261,.92),l(-.172,.326),l(-.046,.156),l(-.08,.043),l(-.159,.1),l(-.604,.399),l(-.352,.059),l(-.148,-.239),l(-.117,-.381),l(-.004,-.297),l(-.147,-.211),l(-.257,-.041),l(-.239,.114),l(-.571,.483),l(-.362,.469),l(-.35,.228),l(-.455,-.436),l(-.566,-.321),l(-.352,.059),l(-.522,.54),l(-.559,-.901),l(-.194,-1.143),l(-.349,-.718),l(-.474,-.478),l(-.265,-.451),l(-.271,-.832),l(-.022,-.339),l(-.246,-.281),l(-.323,-.055),l(-.684,.428),l(-.3,.327),l(-.43,.243),l(-.565,-.152),l(-.356,-.153),l(-.338,-.026),l(-.475,.413),l(-.252,.256),l(-.536,-.265),l(-.882,-.715),l(-.18,-.183),l(-.113,-.028),l(.062,-.142),l(.004,-.565),l(-.082,-.833),l(-.265,-.337),l(-.554,-.322),l(-.181,-.197),l(-.22,-.479),l(-.144,-.663),l(-.251,-1.1),l(.057,-.339),l(.506,-.399),l(.332,-.284),l(.018,-.607),l(.181,-.552),l(.252,-.256),l(.402,-.073),l(.261,.111),l(.568,.83),l(.214,.168),l(.454,.082),l(.107,-.269),l(-.055,-.296),l(.06,-.212),l(.535,.124),l(.713,.137),l(.485,.054),l(.387,-.031),l(.945,-.344),l(1.953,-.026),l(6.457,-.01),l(.379,-1.613),l(-.724,-.787),l(-.186,-1.468),l(-.202,-2.386),l(-.325,-2.753),l(-.178,-1.736),l(-.19,-1.468),l(-.908,-7.962),l(-.049,-.776),l(3.231,-.089),l(.523,-.13),
+N(525.37,142.384),l(.312,-.429),l(.155,-.17),l(.084,.833),l(-.423,.707),l(-.118,.156),l(-.121,.024),l(-.099,-.064),l(.004,-.176),l(.107,-.299),l(.248,-.42),l(-.15,-.161),M(525.923,144.712),l(0,.22),l(.456,.762),l(.408,.465),l(.782,.634),l(.677,.394),l(1.008,.52),l(.392,.154),l(.277,.014),l(.576,-.029),l(.364,.112),l(.873,.973),l(.518,.648),l(.46,.422),l(.81,.365),l(.025,.212),l(-.67,1.06),l(-.615,.721),l(-.883,.807),l(-.776,1.541),l(-.242,.142),l(-.562,-.083),l(-.235,-.084),l(-.252,.071),l(-.278,.509),l(-.062,1.115),l(.001,.791),l(.134,.621),l(-.403,.142),l(-1.046,.073),l(-.627,.27),l(-.367,.283),l(-.29,.495),l(-.131,.551),l(-.204,.283),l(-.444,.255),l(-.544,.1),l(-.292,0),l(-.386,-.042),l(-.326,.029),l(-.382,.283),l(-.22,.297),l(-.125,.508),l(.003,.353),l(-.091,.311),l(-.631,.396),l(-.344,.043),l(-.776,-.21),l(-.717,.058),l(-.896,.27),l(-.768,.298),l(-.283,.099),l(-.416,.145),l(-.241,-.306),l(-.483,-.689),l(.006,-.296),l(-.127,-.253),l(-.933,-1.364),l(-.604,-.971),l(-.226,-.634),l(-.092,-.663),l(1.691,-.815),l(2.35,-1.213),l(5.346,-2.982),l(-.155,-1.453),l(-.581,-.914),l(-.063,-.183),l(-.024,-2.075),l(-.003,-.946),l(.536,-.596),l(.199,-.34),l(-.073,-1.326),l(.107,-.396),l(.401,.083),l(.49,-.034),
+N(405.733,173.04),l(-.568,-.971),l(-.562,-.025),l(-.37,.044),l(-.516,-.181),l(-.97,-.757),l(-.114,-.226),l(.335,-.439),l(-.018,-.212),l(-.179,-.268),l(-.502,-.42),l(-.389,-.266),l(-.422,-.492),l(-.426,-.93),l(-.019,-.396),l(.173,-.665),l(.581,.081),l(.678,.053),l(.579,-.272),l(.562,-.427),l(.209,-.072),l(1.808,-.124),l(2.533,-.085),l(.29,-.129),l(.339,-.271),l(.596,-.794),l(.434,-1.246),l(.145,-.919),l(.08,-.89),l(-.001,-.96),l(-.034,-2.203),l(-.064,-1.017),l(1.672,-.321),l(1.82,-.364),l(3.084,-2.617),l(.834,-.655),l(2.308,-1.454),l(1.607,-.956),l(4.012,-2.241),l(1.632,-.843),l(.265,-.186),l(.832,.137),l(1.646,.442),l(1.008,.333),l(.258,.182),l(1.192,.911),l(.231,-.157),l(1.519,-.729),l(.364,2.145),l(.169,1.298),l(.42,1.028),l(.554,.802),l(.703,.604),l(-.388,.722),l(-.265,.99),l(-.168,1.088),l(-.084,.989),l(.022,.537),l(-.062,.707),l(-.019,1.045),l(-.034,1.088),l(-.056,.466),l(-2.43,2.613),l(-.591,.78),l(-.87,1.333),l(-.572,.794),l(-.007,.678),l(.123,.719),l(.014,.269),l(-.951,.034),l(-.437,.2),l(-.453,.299),l(-.761,.697),l(-.259,.058),l(-.609,-.208),l(-.724,-.193),l(-.884,-.221),l(-.531,-.04),l(-.709,.047),l(-.628,.103),l(-.774,.287),l(-.403,.327),l(-.629,.399),l(-.273,.059),l(-.934,.005),l(-.965,-.277),l(-1.173,-.742),l(-.354,-.083),l(-.467,.116),l(-1.337,.544),l(-.37,.002),l(-.209,-.098),l(-1.095,-1.223),l(-.821,-.277),l(-1.111,-.121),l(-1.174,.108),l(-1.064,.188),l(-.676,.4),l(-.687,1.614),l(-.353,.482),l(-.158,.849),l(-.092,.961),l(-.902,-.503),l(-.727,-.589),l(-.339,-.28),l(-.321,.073),l(-.577,.3),
+N(431.763,171.063),l(-.351,-.407),l(-.575,-.52),l(-.173,-.394),l(-.014,-.269),l(-.123,-.719),l(.007,-.678),l(.572,-.794),l(.87,-1.333),l(.591,-.78),l(2.43,-2.613),l(.056,-.466),l(.034,-1.088),l(.019,-1.045),l(.062,-.707),l(-.022,-.537),l(.084,-.989),l(.168,-1.088),l(.265,-.99),l(.388,-.722),l(-.703,-.604),l(-.554,-.802),l(-.42,-1.028),l(-.169,-1.298),l(-.364,-2.145),l(1.818,-.858),l(.41,-.059),l(5.231,2.554),l(4.941,2.372),l(5.577,2.792),l(1.981,.963),l(-.02,1.045),l(-.016,.946),l(-.036,.636),l(.085,2.5),l(-.038,.749),l(.036,1.002),l(.031,1.229),l(-.04,.283),l(-.839,-.009),l(-1.245,.05),l(-.229,.143),l(-.417,1.245),l(-.583,.809),l(-.122,.438),l(.131,.677),l(-.149,.212),l(-.718,.428),l(-.053,.24),l(.342,.662),l(-.087,.34),l(-.542,.596),l(-.316,.609),l(.219,.352),l(.517,-.088),l(.338,.012),l(.141,.225),l(.221,1.228),l(.137,.522),l(.155,.295),l(.444,.407),l(.266,.465),l(.026,.367),l(-.15,.425),l(-.559,-.208),l(-.321,-.012),l(-.322,.086),l(-.939,.613),l(-.372,.228),l(-.165,.382),l(-.005,.41),l(-.196,.284),l(-2.649,2.275),l(-.386,.087),l(-2.181,.055),l(-.434,.059),l(-.209,.199),l(-.117,.806),l(-.646,1.176),l(-.258,.143),l(-.368,.031),l(-.881,-.009),l(-.818,.273),l(-.754,.386),l(-.466,.271),l(-.224,.03),l(-.225,-.069),l(-.494,-.661),l(-1.363,.686),l(-.449,.158),l(-.24,-.027),l(-.096,-.084),l(-.208,-.183),l(-.382,-1.057),l(-.638,-1.07),l(-1.343,-1.179),l(-1.088,-1.067),l(.323,-.539),l(.29,-.312),l(.24,-.1),l(.481,.082),l(1.187,.191),l(.674,-.032),l(.225,-.143),l(-.047,-.127),l(-.208,-.21),l(-.381,-.633),l(-.205,-.578),l(-.169,-1.228),l(.134,-.651),l(-.119,-1.2),l(-.395,-.887),l(-.923,-1.238),l(-.208,-.083),l(-.627,-.109),
+N(627.173,150.012),l(.483,-.229),l(.515,-.13),l(.341,.012),l(.597,.392),l(.325,.097),l(.584,-.413),l(.332,-.115),l(1.595,-.052),l(.807,-.117),l(.341,-.157),l(.696,-.554),l(.521,-.328),l(.298,-.101),l(.623,.575),l(.771,.235),l(.66,.053),l(.777,-.047),l(.237,.21),l(.056,.38),l(-.472,.75),l(.096,.521),l(.273,.365),l(.943,.615),l(.621,.166),l(.909,.107),l(.197,.143),l(-.19,.132),l(-.826,.482),l(.106,.465),l(-.203,.212),l(-1.261,-.054),l(-.136,.198),l(.057,.395),l(-.283,.382),l(-.585,.792),l(-.221,.142),l(-.533,.241),l(-.171,.127),l(-.27,.396),l(-.303,.932),l(-.388,.975),l(.268,.225),l(.469,.563),l(1.112,1.071),l(.023,.24),l(.042,.522),l(.087,.254),l(.42,.493),l(1.096,.83),l(1.282,1.296),l(.26,.197),l(.636,.069),l(.313,.38),l(.282,1.016),l(.302,.578),l(.638,.605),l(.293,.663),l(.341,1.382),l(.524,2.809),l(-.295,.438),l(-.235,.495),l(.05,.819),l(-.095,.41),l(.056,.664),l(-.027,.099),l(-.364,.551),l(-.447,.439),l(-.254,.127),l(-.509,.1),l(-.419,.17),l(-.501,.354),l(-.591,.622),l(-.579,.354),l(-.325,.043),l(-.512,-.197),l(-.404,-.31),l(-.179,-.141),l(-.153,.424),l(.051,.494),l(.048,.353),l(-.205,.721),l(-.388,.424),l(-.326,.071),l(-.235,-.07),l(-.246,.481),l(-.427,.326),l(-.523,.142),l(-.417,.213),l(-.459,.565),l(-.196,.269),l(-.406,.297),l(-.264,.099),l(-.365,-.042),l(.078,-.861),l(.1,-1.313),l(.151,-.494),l(.215,-.283),l(-.02,-.353),l(-.475,-.437),l(-.749,-.238),l(-.091,-.066),l(.3,-.289),l(.646,-.229),l(.915,-.528),l(.599,-.229),l(.497,.011),l(.688,.194),l(.17,-.27),l(-.03,-.197),l(-.568,-.435),l(-.216,-.422),l(.234,-.425),l(.99,-.571),l(.521,-.229),l(.932,-.443),l(.599,-.187),l(.385,-.285),l(.217,-.509),l(-.054,-1.073),l(.05,-.424),l(.076,-.367),l(-.455,-1.014),l(-.029,-.663),l(.215,-.905),l(.155,-.918),l(-.064,-.578),l(-.214,-.437),l(-.529,-.477),l(-.072,-.282),l(.226,-.439),l(-.136,-.395),l(-.358,-.308),l(-.685,-.391),l(-.471,-.52),l(-.57,-.914),l(-1.683,-2.121),l(-.698,-.772),l(-.637,-.646),l(-.632,-.476),l(-1.234,-.741),l(-.162,-.098),l(-.043,-.494),l(.277,-.369),l(.311,-.101),l(.476,.068),l(.287,-.058),l(.261,-.185),l(.255,-.326),l(-.009,-.508),l(-.87,-.968),l(-.434,-.675),l(-.262,-.083),l(-.39,.171),l(-.509,.483),l(-.287,.058),l(-.47,-.195),l(-.607,-.434),l(-.334,-.689),l(-.338,-.929),l(-.543,-.604),l(-.613,-.575),l(-.45,-.745),
+N(217.961,150.385),l(.304,-.043),l(.84,-.27),l(-.17,-.254),l(-.312,-.112),l(-.369,-.056),l(-.651,.016),l(-.497,-.042),l(-.645,.157),l(-1.193,.92),l(-.371,.029),l(-.653,.001),l(-.211,.113),l(-.189,.452),l(-.396,.284),l(-.32,.043),l(-.786,.086),l(.259,-.325),l(.473,-.312),l(-.128,-.593),l(.282,-.382),l(.114,-.099),l(1.258,-.61),l(1.625,-.47),l(1.164,-.087),l(.842,-.157),l(.825,.041),l(.566,-.044),l(.73,.168),l(.848,.083),l(.603,.197),l(.557,.112),l(.477,.013),l(.499,.268),l(.573,.536),l(.382,.253),l(.581,.168),l(.768,.111),l(1.229,.351),l(1.02,.492),l(.453,.31),l(.374,.55),l(.33,.141),l(.479,.041),l(1.704,.519),l(1.018,.167),l(.327,.239),l(-.344,.58),l(.233,.155),l(.559,.042),l(.756,-.072),l(.495,.168),l(.507,.38),l(.591,.281),l(.381,.296),l(-.233,.085),l(-.981,.087),l(-1.15,.398),l(-.626,.058),l(-1.054,-.209),l(-.9,-.041),l(-.934,.186),l(-.943,.115),l(-.484,.029),l(-.449,-.07),l(.353,-.382),l(.728,-.623),l(.173,-.396),L(229,154.204),l(-.181,-.127),l(-.622,-.14),l(-.7,.001),l(-.603,-.112),l(-.651,-.338),l(-.141,-.748),l(-.258,-.536),l(-.218,-.155),l(-.396,-.027),l(-1.005,.044),l(-.836,-.139),l(-.621,-.225),l(-.956,-.493),l(-.739,-.238),l(-.615,-.069),l(-1.154,-.068),l(-.489,-.098),l(-.855,-.352),
+N(634.036,168.444),l(.808,-.64),l(.121,-.438),l(-.002,-.945),l(-.157,-.507),l(-.419,-.703),l(-.979,-1.279),l(-.255,-.464),l(-.107,-.366),l(-.058,-1.524),l(-.435,-.632),l(-.688,-.659),l(-.285,-.535),l(-.052,-.282),l(-.266,-.153),l(-.893,-.192),l(-.403,-.012),l(-.286,.453),l(-.2,.538),l(-.543,.257),l(-.223,.072),l(-.59,-.265),l(-.835,-.348),l(-.346,.03),l(-1.173,1.178),l(-.37,.411),l(-.481,-.138),l(-.145,-.324),l(.027,-.494),l(.117,-.438),l(.528,-1.569),l(.085,-.41),l(-.249,-1.311),l(-.045,-.113),l(-.414,.045),l(-.489,.2),l(-.423,.003),l(-.186,-.154),l(-.066,-.367),l(.106,-.805),l(-.01,-.423),l(-.118,-.168),l(-.295,-.182),l(-.541,-.166),l(.193,-.185),l(.582,-.455),l(.442,-.581),l(.53,-.61),l(.502,-.355),l(.178,.196),l(.321,.21),l(.769,.08),l(.266,-.213),l(.109,-.339),l(-.119,-.521),l(-.228,-.366),l(-.138,-.592),l(.043,-.325),l(.24,-.241),l(.679,-.314),l(.45,.745),l(.613,.575),l(.543,.604),l(.338,.929),l(.334,.689),l(.607,.434),l(.47,.195),l(.287,-.058),l(.509,-.483),l(.39,-.171),l(.262,.083),l(.434,.675),l(.87,.968),l(.009,.508),l(-.255,.326),l(-.261,.185),l(-.287,.058),l(-.476,-.068),l(-.311,.101),l(-.277,.369),l(.043,.494),l(.162,.098),l(1.234,.741),l(.632,.476),l(.637,.646),l(.698,.772),l(1.683,2.121),l(.57,.914),l(.471,.52),l(.685,.391),l(.358,.308),l(.136,.395),l(-.226,.439),l(.072,.282),l(.529,.477),l(.214,.437),l(.064,.578),l(-.155,.918),l(-.209,.114),l(-.975,.429),l(-.3,.072),l(-.373,-.351),l(-.444,-.181),l(-.476,.186),l(-.392,.285),l(.107,.296),l(.187,.182),l(.103,.211),l(-.095,.24),l(-.248,.058),l(-.469,-.251),l(-.341,-.111),l(-.736,-.165),l(-.533,-.251),
+N(60.074,72.607),l(-.099,.228),l(-.491,.472),l(-.395,.183),l(-.462,.062),L(58,73.461),l(-.961,-.362),l(-.153,-.197),l(.169,-.289),l(.54,-.274),l(.341,-.32),l(.716,.364),l(.3,.091),l(.465,-.26),l(.215,-.213),l(.064,-.366),l(.485,-.047),l(1.107,.135),l(.536,.334),l(.133,.213),l(-.756,.062),l(-.429,0),l(-.59,.184),l(-.11,.092),M(40.092,77.571),l(-.729,-.029),l(-.097,-.24),l(.011,-.3),l(.802,-.243),l(.326,-.211),l(.593,-.423),l(.448,-.137),l(.646,-.077),l(1.427,.253),l(.711,.24),l(-.079,.211),l(-.303,.046),l(-.754,-.074),l(-.496,.031),l(-1.077,.183),l(-.269,.226),l(-1.161,.543),M(38.426,77.979),l(-.515,-.209),l(-.139,-.285),l(.381,-.227),l(.674,.27),l(.093,.195),l(-.122,.15),l(-.372,.105),M(37.896,78.449),l(-.256,.084),l(-.558,.151),l(-1.109,-.058),l(-.387,.135),l(-.398,.434),l(-.31,.15),l(-.854,-.207),l(-.135,-.224),l(.497,-.359),l(.5,-.315),l(.955,-.166),l(.863,-.346),l(.39,.089),l(.461,.224),l(.341,.409),M(29.628,81.29),l(-.168,-.594),l(-.324,-.476),l(.839,-.136),l(.424,.088),l(.436,.238),l(-.244,.268),l(-.26,.06),l(-.073,.297),l(-.22,.09),l(-.412,.164),M(27.543,81.591),l(-.39,.031),l(-.741,.165),l(-.311,-.133),l(-.088,-.178),l(.104,-.119),l(.336,-.268),l(.294,-.09),l(.584,.222),l(.212,.371),M(54.394,157.986),l(-.559,-.356),l(-.044,-.884),l(-.243,-.677),l(.482,-.402),l(-.035,-.2),l(-.156,-.26),l(.052,-.149),l(.173,-.046),l(.354,.158),l(.652,.279),l(.593,.425),l(-.015,.275),l(.238,.046),l(.12,.287),l(.306,.149),l(-.062,.161),L(56,156.933),l(-.172,.204),l(-.766,.195),l(-.374,.23),l(-.295,.425),M(23.015,59.92),l(-1.613,-.646),l(-.75,-.205),l(-.792,-.062),l(-.9,.065),l(-.291,-.095),l(-.431,-.222),l(.179,-.287),l(.516,-.049),l(1.135,.221),l(.579,-.001),l(.543,-.081),l(.538,-.001),l(.828,.285),l(1.725,.362),l(.429,.237),l(.046,.111),l(-.569,-.03),l(-.646,.033),l(-.527,.365),M(99.855,70.993),l(.467,.929),l(-.071,.167),l(-.879,-.272),l(-.621,-.075),l(.067,.441),l(-.056,.228),l(-.935,-.607),L(97.03,71.41),l(.396,-.458),l(.263,-.153),l(.612,-.078),l(.784,.38),l(.771,-.108),M(100.975,73.606),l(.128,.272),l(-.086,.273),l(-.318,.483),l(-.559,-.815),l(-.597,-.909),l(-.04,-.228),l(.095,-.213),l(.407,.029),l(.568,.197),l(.402,.91),M(106.858,78.207),l(-1.872,-1.166),l(-.566,-.555),l(.01,-.467),l(-.559,-.843),l(.071,-.106),l(.456,.06),l(.274,.256),l(1.165,.48),l(.086,.196),l(-.059,.136),l(-.149,.226),l(.296,.436),l(.839,.374),l(.007,.974),M(140.191,127.819),l(-.043,-.094),l(-.198,-.36),l(-.049,-.067),l(-.032,.042),l(-.028,.05),l(-.04,-.092),l(.002,-.664),l(-.331,-.604),l(-.472,-.451),l(-.661,-.451),l(-.512,-.197),l(-.114,-.052),l(-.145,.034),l(.002,.092),l(-.088,.025),l(-.1,-.042),l(-.146,-.143),l(.076,-.076),l(-.073,-.202),l(-.228,-.252),l(-.283,-.025),l(-.312,.084),l(-.932,-.336),l(-.286,-.328),l(-.428,-.244),l(-.383,.042),l(-.932,-.16),l(-.721,.051),l(-.12,-.185),l(-.234,-.067),l(-.046,-.177),l(.094,-.117),l(-.157,-.504),l(.133,-.464),l(-.227,-.096),l(-.127,.008),l(-.249,-.134),l(0,-.101),l(.075,-.093),l(-.029,-.219),l(-.347,-.185),l(-.254,-.286),l(-.415,-.219),l(-.391,-.623),l(-.202,-.076),l(-.203,-.311),l(-.409,-.219),l(-.156,-.529),l(-.002,-.227),l(.178,.007),l(.147,-.164),l(.029,-.326),l(-.208,-.251),l(-.192,-.045),l(-.22,.037),l(-.303,-.126),l(-.535,-.514),l(.078,-.21),l(-.091,-.312),l(-.129,-.067),l(-.044,-.463),l(.058,-.152),l(.119,-.025),l(.099,.067),l(.073,.076),l(-.086,.101),l(.153,.202),l(.285,.126),l(.116,.118),l(.203,.017),l(-.385,-.564),l(-.183,-.144),l(-.021,-.236),l(-.184,-.109),l(-.051,-.344),l(-.13,.006),l(-.011,.144),l(.048,.446),l(-.093,.017),l(-.293,-.194),l(-.119,.042),l(-.516,-.404),l(-.136,-.363),l(-.377,-.514),l(-.531,-.379),l(-.624,-.583),l(-.123,-.142),l(.114,-.101),l(-.327,-.751),l(.161,-.43),l(-.254,-.479),l(-.22,-.355),l(-.738,-.782),l(-.104,-.299),l(.099,-.627),l(.252,-.628),l(.166,-.357),l(.059,-.856),l(-.215,-.785),l(-.692,-1.486),l(-.153,-.916),l(.096,-.287),l(.231,-.244),l(.402,-.201),l(.365,-.717),l(-.365,-.573),l(-.066,-.33),l(.424,-1.593),l(.153,-.575),l(.061,-.634),l(.091,-.778),l(.019,-.179),l(-.153,-.16),l(.08,-.231),l(-.103,-.167),l(.157,-.077),l(-.03,-.186),l(-.046,-.186),l(-.031,-.103),l(-.004,-.058),l(.322,.096),l(.209,-.019),l(.062,-.097),l(-.211,.006),l(-.614,-.122),l(.062,-.707),l(-.103,-.328),l(.017,-.277),l(.587,-.225),l(-.345,-.019),l(-.16,-.142),l(-.129,0),l(-.053,.045),l(.042,.116),l(-.12,.052),l(-.133,-.979),l(-.927,-1.463),l(-.017,-.465),l(.129,-.131),l(.544,.086),l(.632,.217),l(.785,.114),l(.641,.028),l(.546,-.044),l(.415,.086),l(.547,.318),l(.039,.435),l(-.42,.407),l(-.413,.347),l(.532,.146),l(.184,.188),l(.251,.169),l(.029,-.228),l(.161,-.232),l(.393,-.305),l(.21,-.581),l(.102,-.465),l(-.064,-.421),l(-.356,-.958),l(-.158,-.305),l(-.655,-.516),l(.194,.013),l(2.579,.001),l(1.335,.022),l(4.588,-.025),l(3.938,.008),l(2.87,-.001),l(1.687,.006),l(5.117,-.028),l(.74,.011),l(4.13,.021),l(1.089,-.035),l(3.821,.023),l(.875,-.005),l(3.617,-.004),l(4.84,.018),l(.601,-.003),l(2.935,.014),l(2.131,-.012),l(2.781,.029),l(2.915,-.016),l(2.105,.003),l(1.348,-.007),l(2.798,.029),l(2.687,-.029),l(.68,.003),l(-.387,-.588),l(-.131,-.347),l(.501,-.036),l(.896,.748),l(.279,.371),l(.468,.46),l(.833,.451),l(.518,-.076),l(1.425,.208),l(.02,.185),l(.271,-.012),l(.338,.48),l(.16,-.247),l(.502,.013),l(.241,.271),l(.469,.086),l(.064,.185),l(.506,.098),l(.573,-.141),l(.219,-.06),l(.412,-.191),l(.373,-.075),l(.028,.282),l(.197,.116),l(.855,-.083),l(.474,.041),l(.156,.115),l(.196,.144),l(.542,-.049),l(.707,.074),l(1.469,-.592),l(.805,-.189),l(.797,.227),l(.977,.386),l(3.975,1.576),l(2.15,1.061),l(.101,.429),l(.46,.465),l(.628,-.024),l(.178,.135),l(.184,.294),l(.916,.181),l(.307,.235),l(-.11,.318),l(.26,.33),l(2.529,1.05),l(.876,3.16),l(.054,.545),l(-.028,.746),l(-.377,.576),l(-.294,.544),l(-.264,.433),l(-.414,.294),l(-.707,.525),l(-.044,.218),l(.012,.33),l(.371,.427),l(.497,.169),l(.573,.068),l(.524,-.117),l(.925,-.506),l(.939,-.478),l(.88,-.262),l(.919,-.062),l(.944,-.163),l(1.464,-.452),l(.875,-.427),l(-.047,-.362),l(-.16,-.471),l(-.018,-.319),l(.162,-.375),l(.47,-.203),l(.93,-.091),l(1.123,.01),l(1.305,.138),l(1.156,-.197),l(.44,-.275),l(.163,-.512),l(.146,-.434),l(.545,-.164),l(1.754,-.814),l(.534,-.305),l(.968,-.523),l(1.76,-.009),l(2.508,.029),l(1.855,.004),l(1.093,.095),l(.174,-.375),l(.363,-.435),l(.402,-.06),l(1.161,.124),l(1.139,-1.45),l(1.139,-2.22),l(.514,-.626),l(.632,-.526),l(.273,.085),l(.505,.36),l(.381,.085),l(.41,-.176),l(.771,.025),l(.488,.288),l(.174,.274),l(.31,2.819),l(-.077,.229),l(.606,.231),l(.224,0),l(.042,.154),l(-.143,.069),l(.02,.256),l(-.192,.077),l(.16,.291),l(.188,-.153),l(.349,.495),l(-.268,.281),l(.299,-.04),l(.171,.093),l(-.511,.374),l(-.509,.093),l(-.297,-.12),l(-.013,.253),l(-.138,.067),l(-.077,-.107),l(-.231,-.08),l(-.277,.133),l(-.101,.28),l(-.171,-.013),l(-.15,.16),l(-.175,-.347),l(-.746,.28),l(-.204,-.093),l(.12,.413),l(-.666,-.213),l(.199,-.48),l(-.149,-.04),l(-.364,.52),l(-.332,.56),l(-.342,.333),l(-.324,-.227),l(-.249,.439),l(-.346,-.08),l(.122,-.307),l(-.325,.253),l(.165,.16),l(-.326,.293),l(-.318,-.133),l(.105,-.226),l(-.654,.253),l(.065,.359),l(-.264,.04),l(-.161,.373),l(-.352,.106),l(-.333,.679),l(-.404,.505),l(.173,.146),l(.068,.212),l(.168,.053),l(.083,-.08),l(.169,.013),l(-.122,.146),l(-.547,.106),l(.053,.093),l(-.392,.292),l(-.068,.159),l(.337,.027),l(.282,.093),l(.599,.704),l(.055,.398),l(.399,.106),l(.691,-.239),l(-.022,-.186),l(-.14,-.027),l(-.254,-.279),l(-.097,-.04),l(-.009,-.066),l(.196,0),l(.23,.133),l(.218,.358),l(.031,.425),l(-1.599,.292),l(-.032,-.385),l(-.124,-.066),l(-.109,.226),l(-.164,.04),l(-.03,.093),l(-.105,-.106),l(-.159,.266),l(-.164,.04),l(-.294,.04),l(-.045,-.332),l(.198,-.332),l(-.443,.119),l(-.154,-.146),l(-.082,.252),l(-.087,.664),l(-1.429,.132),l(-1.694,.159),l(-1.182,.345),l(-.787,.358),l(-.097,.212),l(-.32,.053),l(-.144,.172),l(-.032,-.04),l(.308,-.756),l(.024,-.106),l(-.071,.027),l(-.41,.994),l(-.079,-.08),l(-.406,.292),l(.218,.318),l(.553,.093),l(-.46,1.515),l(-.302,.429),l(-.259,-.092),l(.043,.251),l(-.062,.185),l(-.237,.145),l(-.462,.501),l(-.292,.304),l(-.167,.026),l(-.075,-.119),l(.177,-.31),l(-.113,-.178),l(-.43,.013),l(-.447,-.343),l(-.148,-.053),l(-.329,-.541),l(.315,-.257),l(.151,-.245),l(-.271,.119),l(-.362,.37),l(.489,.845),l(.033,.356),l(.387,.581),l(.28,.066),l(.104,.765),l(-.101,.238),l(-.151,.23),l(-.125,-.013),l(-.487,.666),l(-.396,.798),l(.034,.053),l(-.13,.132),l(-.107,-.125),l(-.374,.725),l(.026,.125),l(-.226,.04),l(-.137,-.263),l(.342,-.864),l(.195,-.29),l(.247,-.119),l(.061,-.237),l(-.093,-.059),l(-.374,.119),l(.226,-.383),l(-.218,.04),l(-.176,-.093),l(.012,-.191),l(.242,-.04),l(-.077,-.33),l(-.439,.296),l(-.241,-.204),l(-.157,.053),l(-.23,-.396),l(.355,-.171),l(.357,-.053),l(-.005,-.06),l(-.604,-.316),l(-.092,.165),l(-.072,0),l(.107,-.323),l(.089,-.02),l(.21,.159),l(.131,-.06),l(-.098,-.224),l(-.353,-.066),l(-.065,-.112),l(.096,-.112),l(.336,.02),l(.193,-.284),l(-.281,.046),l(-.158,-.059),l(.241,-.37),l(.652,-.152),l(-.328,-.06),l(.146,-.409),l(-.28,.093),l(-.096,.132),l(.11,.079),l(-.315,.191),l(-.035,-.224),l(-.093,.053),l(.051,.224),l(-.081,.086),l(-.051,-.158),l(-.097,-.066),l(-.103,.416),l(-.447,-.079),l(.402,.501),l(-.294,.666),l(.07,.237),l(.272,.488),l(-.055,.139),l(-.466,-.317),l(-.1,-.211),l(.026,.205),l(.174,.218),l(.421,.237),l(.132,.508),l(-.631,-.402),l(-.354,-.007),l(-.118,-.283),l(-.155,-.053),l(.066,.25),l(-.541,-.323),l(-.33,.04),l(.015,-.29),l(.427,-.323),l(-.428,.079),l(-.19,.468),l(.204,.231),l(.457,.046),l(.202,.25),l(.954,.297),l(-.047,.092),l(.554,.165),l(.158,.132),l(-.22,.468),l(-.227,.06),l(-1.042,-.804),l(.708,.811),l(.626,.171),l(-.248,.092),l(.323,.079),l(-.045,.079),l(.061,.06),l(-.034,.25),l(-.312,-.191),l(-.071,.073),l(.104,.211),l(-.216,.02),l(-.656,-.56),l(-.023,.026),l(.419,.475),l(.309,.158),l(.182,-.026),l(.191,.21),l(.018,.31),l(-.298,.059),l(-.492,-.534),l(-.474,-.198),l(-.199,.16),l(.046,.044),l(.44,.145),l(.488,.382),l(-.047,.237),l(.442,-.033),l(.031,-.119),l(.748,.119),l(.151,.382),l(.406,1.212),l(.448,.803),l(-.14,-.092),l(-.262,-.349),l(-.059,-.132),l(-.359,-1.172),l(-.147,-.277),l(-.056,.31),l(.135,0),l(.034,.198),l(-.292,-.066),l(.173,.283),l(.144,.099),l(.228,.58),l(-.144,-.053),l(-.211,-.382),l(.002,.224),l(-.52,-.303),l(-.06,.059),l(.266,.303),l(-.247,.119),l(-.526,-.204),l(.225,.204),l(-.375,.211),l(-.173,-.02),l(-.251,-.21),l(-.024,-.217),l(.083,-.158),l(-.081,-.053),l(-.091,.204),l(.044,.23),l(.116,.211),l(-.107,.158),l(.894,.02),l(.571,-.145),l(.125,.165),l(-.113,.191),l(-.072,.369),l(.14,.066),l(.092,-.257),l(.135,-.369),l(.18,-.105),l(.266,.31),l(.047,.296),l(-.166,.25),l(-.163,-.013),l(-.063,-.099),l(-.316,.474),l(-.254,.197),l(-.483,-.053),l(-.203,-.065),l(-.147,-.066),l(-.136,-.245),l(-.151,.014),l(.141,.244),l(-.075,.013),l(-.538,-.125),l(-.436,-.151),l(.162,.185),l(.269,.026),l(.833,.335),l(-.034,.119),l(-.396,.145),l(.247,.007),l(-.19,.25),l(-.281,.138),l(-.149,0),l(-.481,-.375),l(.242,.395),l(.43,.164),l(.302,-.171),l(.292,.026),l(.11,-.204),l(.04,.178),l(.217,.04),l(.048,.079),l(-.428,.322),l(-.013,.085),l(-.261,.072),l(-1.498,.214),l(-.865,.895),l(-.487,.609),l(-.13,.127),l(-.935,.143),l(-.528,.128),l(-.617,.241),l(-.678,.539),l(-.225,.424),l(-.096,.354),l(-.819,.694),l(-.693,.383),l(-.429,.199),l(-.797,.086),l(-.35,.58),l(-.177,.198),l(-.809,1.125),l(-.273,.781),l(-.459,1.249),l(.236,1.455),l(.387,.925),l(.456,.873),l(.934,1.562),l(.352,1.746),l(.486,1.194),l(-.075,.092),l(.287,.276),l(.123,.333),l(.062,.827),l(-.301,1.536),l(-.064,.278),l(-.31,.415),l(.108,.424),l(-.02,.252),l(-.393,.551),l(-.017,-.092),l(.129,-.241),l(-.025,-.138),l(-.256,.035),l(-.38,.137),l(-.291,-.126),l(-.509,.138),l(-.12,-.329),l(.014,-.233),l(-.567,-1.068),l(-.764,-.138),l(-.204,-.352),l(-.113,-.819),l(-.423,-.229),l(-.144,-.702),l(-.373,.093),l(-.608,-1.08),l(-.375,-.482),l(.296,0),l(.375,-.438),l(.048,-.226),l(-.167,-.226),l(-.471,.407),l(-.277,-.208),l(.126,-.573),l(.147,-.758),l(.158,-1.043),l(-.293,-.452),l(-.258,-.169),l(-.496,-.126),l(-.832,-.987),l(-.875,-.804),l(-.528,-.168),l(-.43,.072),l(-.536,.298),l(-.456,.354),l(-1.202,.299),l(-.273,-.213),l(-.131,-.62),l(-.253,-.254),l(-.264,-.113),l(-.752,-.069),l(-.516,-.296),l(-.22,-.233),l(-.504,.138),l(-1.052,.115),l(-.653,-.184),l(-.047,.298),l(-.64,.099),l(-.183,0),l(-.578,-.926),l(-.238,.781),l(-.447,-.135),l(-.65,.001),l(-1.328,-.04),l(-.672,.439),l(-.39,.055),l(-1,-.459),l(-.096,.009),l(-.142,.014),l(-.362,.528),l(.201,.229),l(.303,0),l(.211,0),l(.537,-.207),l(.406,.092),l(.676,.482),l(-.68,.373),l(.02,.254),l(.263,.353),l(.593,.146),l(.229,.217),l(.35,.334),l(-.533,.136),l(-.503,-.084),l(-.276,-.419),l(-.79,-.271),l(-.224,-.211),l(-.265,-.056),l(-.013,.02),l(-.209,.32),l(.209,.154),l(.248,.183),l(-.248,.179),l(-.069,.05),l(-.447,-.459),l(-.476,.192),l(-.287,.291),l(-1.025,-.472),l(-.419,-.494),l(-1.16,-.642),l(-.615,.066),l(.554,.393),l(-.307,.187),l(-1.17,-.083),l(-.886,-.252),l(-.896,-.168),l(-1.547,.173),l(-.632,.328),l(-.392,-.015),l(-.433,-.031),l(-.135,-.49),l(-.333,.057),l(-.112,.184),l(.474,.731),l(-.877,.64),l(-.808,.577),l(-.915,.317),l(-.419,.043),l(-.414,-.056),l(-.728,-.111),l(-.126,.198),l(.437,.437),l(-.239,.396),l(-.327,.199),l(-.631,.114),l(-.737,.27),l(-.268,.17),l(.558,.352),l(.111,.169),l(-.659,.694),l(-.154,.297),l(-.012,.848),l(.144,.636),l(.271,.762),l(.425,.903),l(-.347,-.119),l(-.816,-.377),l(-.296,.001),l(-.416,.116),l(-.264,-.069),l(-1.029,-.56),l(-.921,-.32),l(-.375,-.365),l(-.336,-.592),l(-.332,-.932),l(-.078,-.467),l(-.268,-.253),l(-.657,-.576),l(-.845,-1.042),l(-.744,-1.227),l(-.663,-1.029),l(-.363,-.366),l(-.412,-.252),l(-.783,-.321),l(-.475,-.082),l(-.643,.018),l(-.468,.201),l(-.576,.541),l(-.418,.413),l(-.283,.37),l(-.416,.158),l(-.501,-.011),l(-.337,-.069),l(-1.104,-.503),l(-1.092,-.659),l(-.445,-.549),l(-.318,-.847),l(-.284,-.678),l(-.179,-.226),l(-.708,-.491),l(-.837,-.519),l(-.766,-.632),l(-.631,-.662),l(-.209,-.112),l(-1.892,-.046),l(-1.858,-.003),l(-.096,.892),l(-.213,.101),l(-1.867,.011),l(-.966,-.037),l(-1.544,-.02),l(-1.662,-.019),l(-.338,-.055),l(-3.516,-1.112),l(-2.811,-.933),l(-1.186,-.39),l(-.267,-.154),l(-.316,-.31),l(-2.381,.084),l(-2.367,.155),l(-.34,.017),M(49.818,152.776),l(-.122,.086),l(-.279,.03),l(-.111,-.131),l(-.177,-.005),l(-.324,.051),l(-.304,-.39),l(-.071,-.263),l(.339,-.01),l(.299,-.253),l(.188,.218),l(.106,.294),l(.223,.096),l(.233,.279),M(52.785,154.312),l(-.155,-.081),l(-.085,-.356),l(-.461,-.321),l(.095,-.229),l(.143,-.058),l(.366,.209),l(.344,.055),l(.616,.356),l(-.005,.172),l(-.294,.184),l(-.563,.069),M(111.683,77.224),l(-.138,.415),l(-.45,.067),l(-.324,.113),l(-.295,.247),l(-.321,-.137),l(-.185,-.21),l(.087,-.443),l(.086,-.443),l(-.438,-.675),l(-.463,-.319),l(-.199,-.271),l(-1.281,.055),l(-.437,.098),l(-.153,.161),l(-.496,.097),l(-.019,-.193),l(-.034,-.432),l(.212,-.272),l(.184,-.212),l(-.378,-.347),l(-.641,-.438),l(-.693,-.696),l(-.723,-.317),l(-.453,-.136),l(.132,-.35),l(-.569,-.592),l(-.099,-.213),l(.371,-.229),l(-.068,-.122),l(-.301,-.152),l(-.445,-.076),l(-.392,-.274),l(-.237,-.259),l(-.57,-.305),l(-1,-.411),l(-.479,-.765),l(-.217,-.583),l(-.367,-.399),l(-.357,.016),l(-.101,.814),l(.42,.873),l(.104,.306),l(-.047,.153),l(-.701,-.136),l(-.272,-.076),l(-.511,-.504),l(-.4,-.459),l(-.537,.139),l(-1.219,-.228),l(1.263,.718),l(.032,.214),l(-1.62,.171),l(-1.093,-.35),l(-1.388,-.948),l(-.543,-.292),l(-.664,-.043),l(-.079,0),l(-.933,-.213),l(-1.3,-.536),l(.928,-.248),l(.135,-.169),L(90.8,67.129),l(-.384,-.153),l(-.792,.156),l(-.454,.14),l(-.656,.017),l(-1.058,-.06),l(-1.068,-.245),l(.027,-.247),l(-.148,-.186),l(-.325,-.108),l(-.359,.016),l(-.47,.202),l(-1.036,.049),l(-1.465,-.122),L(80.46,66.64),l(-.786,-.091),l(-.248,-.108),l(-.651,-.387),l(-.427,-.527),l(-.301,.218),l(-.788,.157),l(-.89,-.293),l(-.234,-.326),l(-.417,-.139),l(-.872,-.248),l(-1.538,-.23),l(-.817,-.248),l(-.671,-.342),l(-.553,.235),l(-.675,.079),l(.06,.437),l(-.193,.062),l(-.389,.25),l(-.249,.405),l(1.119,.293),l(.174,.294),l(-.096,.388),l(-.428,.449),l(-.458,.001),l(-.804,-.214),l(-.586,-.061),l(-.568,.094),l(-.978,.603),l(-1.066,.217),l(-.936,.448),l(-1.035,.448),l(-1.095,.109),l(.178,-.308),l(.063,-.123),l(.72,-.401),l(-.093,-.385),l(-.655,-.523),l(.004,-.108),L(64.1,66.19),l(.411,-.482),l(.157,-.42),l(.736,-.312),l(.87,-.235),l(1.165,-.018),l(1.085,.123),l(.239,-.156),l(-1.239,-.466),l(-.971,-.389),l(-1.043,.049),l(-.226,.219),l(-.449,.095),l(-.573,.438),l(-.865,.375),l(-1.019,.282),L(61.553,65.9),l(-.406,.094),l(-.298,.14),l(.131,.325),l(-.177,.526),l(-.563,.34),l(-.564,.078),L(59,67.544),l(-.592,.278),l(-.681,.601),l(-.035,.292),l(.38,.168),l(.36,.03),l(.667,.106),l(.465,.229),l(-.075,.184),l(-.43,.338),l(-.625,.2),l(-.557,.277),l(-.423,.398),l(-.544,.383),l(-.675,.093),l(-1.434,.308),l(-.678,.397),l(-1.036,.337),l(-.7,.367),l(.52,.5),l(-.1,.167),l(-1.106,.412),l(-.897,.153),l(-.778,.168),L(49.51,74.19),l(-1.214,.456),L(48.1,74.828),l(-.274,.394),l(-.753,.439),l(-1.193,.229),l(-1.234,.184),l(-.973,-.345),l(.001,-.181),l(.332,-.348),l(.763,-.273),l(.306,-.212),l(.445,-.364),l(1.171,-.441),l(1.403,.073),l(-.12,-.212),l(.02,-.516),l(.47,-.304),l(.725,-.291),l(.754,-.366),l(.266,-.214),l(.002,-.731),l(.246,-.749),l(.693,-.49),l(.194,-.398),l(.034,-.412),l(-.633,.122),l(-1.251,.186),l(-.676,-.029),l(-.5,-.597),l(-.266,.062),l(-.613,.216),l(-.136,.23),l(.239,.352),l(.021,.352),l(-.169,.046),L(47,70.115),l(-.567,-.32),l(-.487,-.397),l(-.509,-.291),l(-.56,-.03),l(-.812,-.106),l(-.91,.094),l(.028,.138),l(-.644,.338),l(-1.175,.14),l(-.649,-.09),l(-.064,-.123),l(-.082,-.107),l(-.125,-1.028),l(.3,-.508),l(-.142,-.431),l(-.864,-1.002),l(-1.43,.437),l(-.738,.078),l(-.406,.202),l(-1.091,.094),l(-.4,-.23),l(-.394,-.355),l(-.466,-.325),l(-1.007,-.463),l(-.179,-.28),l(.292,-.171),l(.337,-.437),l(.704,.139),l(1.312,.309),l(.69,.03),l(.238,-.234),l(-.375,-.482),l(-.458,-.264),l(-.363,0),l(-.541,.22),l(-.528,-.015),l(-1.342,-.513),l(-.623,-.186),l(-.197,.016),l(-.858,-.029),l(-.024,-.078),l(-.623,-.985),l(.79,-.19),l(.071,-.456),l(.495,-.221),l(.102,-.299),l(.227,-.347),l(.893,-.491),l(.337,-.38),l(.386,-.301),l(.527,-.476),l(.39,-.175),l(.719,.109),l(.98,.268),l(.485,.094),l(.752,-.144),l(.427,-.254),l(.675,-.429),l(1.252,-.257),l(.774,-.033),l(.955,-.049),l(.354,-.175),l(.187,-.478),l(-.259,-.669),l(-.814,-.686),l(.026,-.096),l(.927,-.034),l(.343,-.256),l(-.25,-.384),l(-.497,-.256),l(-.367,-.08),l(-1.88,.389),L(39.33,56.53),l(-.534,.289),l(-.496,.065),l(-1.907,-.333),l(-.848,-.031),L(34.8,56.49),l(-1.27,.162),l(-1.265,-.029),l(-1.349,-.174),l(-.53,-.207),l(-.183,-.788),l(.144,-.146),l(.636,-.033),l(1.008,-.002),l(.446,-.179),l(-1.057,-.241),l(-1.912,-.304),L(28.13,54.31),l(-1.285,-.208),l(.219,-.114),l(.781,-.262),l(1.568,-.214),l(1.325,-.328),l(.958,-.214),l(1.058,-.361),l(.953,-.23),l(1.399,-.281),l(1.513,.128),l(-.158,.523),l(.023,.277),l(1.051,.161),l(1.359,.095),l(1.074,-.019),l(.657,-.099),l(.784,-.246),l(.55,-.295),l(.262,-.083),l(.752,.064),l(.751,.113),l(1.021,-.051),l(.2,-.327),l(.007,-.18),l(-.689,-.064),l(-1.946,-.292),l(-1.283,-.047),l(-.312,-.755),l(-1.473,-.162),l(-.96,-.031),l(-1.573,-.096),l(-.194,-.511),l(-.484,-.312),l(-1.042,-.461),l(-.512,-.148),l(-2.66,-.659),l(-2.008,-.545),l(.317,-.167),l(.812,-.402),l(.086,-.485),l(2.136,-.054),l(.99,-.069),l(1.829,-.288),l(.784,-.354),l(.452,-.623),l(.788,-.575),l(.616,-.525),l(.818,-.41),l(.742,-.224),l(1.066,-.104),l(1.133,-.241),l(1.047,-.036),l(1.804,.065),l(.146,-.188),l(-.156,-.205),L(44.47,42.83),l(.018,-.206),l(1.936,-.089),l(1.143,.032),l(.974,-.054),l(1.344,-.14),l(1.098,-.416),l(.918,-.417),l(.957,-.019),l(.282,.051),l(.675,.241),l(.156,.172),l(-.383,.139),l(.017,.344),l(1.049,.136),l(.424,.034),l(.536,-.293),l(.297,-.208),l(1.419,.187),l(1.534,.049),l(1.062,.049),l(.715,.033),l(.711,.257),l(.359,.274),l(.783,.358),l(.494,.085),l(.421,-.086),l(1.292,.117),l(1.124,.015),l(1.556,-.054),l(1.449,-.088),l(1.213,.1),l(1.377,.254),l(.883,.118),l(3.424,.13),l(1.279,.168),l(.743,.169),l(2.027,-.038),l(2.339,-.141),l(1.123,.236),l(2.441,.791),l(1.206,.301),l(.227,.008),l(.102,.483),l(-.003,2.855),l(.039,2.259),l(.052,2.335),l(.129,2.796),l(-.026,2.183),l(-.043,4.334),l(.026,2.167),l(.089,1.046),l(.196,.279),l(.84,.074),l(2.424,-.122),l(.739,.059),l(.332,.388),l(.173,.387),l(.348,.292),l(2.162,1.318),l(.945,.673),l(.238,-.325),l(.848,-.205),l(1.225,-.67),l(.731,-.498),l(.495,-.126),l(.832,.073),l(.316,.199),l(.371,.492),l(.35,.322),l(2.048,1.175),l(.814,.58),l(1.769,1.768),l(1.67,1.882),l(.512,.393),l(.189,.029),l(.98,.314),l(2.025,.763),l(.402,.255),l(-.163,.788),l(.393,.777),
+N(643.755,159.873),l(-1.092,-.52),l(-.637,-.337),l(-.203,-1.284),l(.036,-.282),l(.24,-.241),l(.42,-.241),l(.721,-.623),l(.493,.056),l(.049,-.17),l(.24,-.396),l(.239,.028),l(.573,.225),l(.321,-.312),l(.439,-.001),l(.798,-.171),l(.596,.69),l(-.163,.17),l(-.443,.354),l(-.412,.538),l(-.285,.734),l(.14,.296),l(-.22,.311),l(-.292,.085),l(-1.026,.383),l(-.532,.707),M(627.173,150.012),l(-.679,.314),l(-.24,.241),l(-.043,.325),l(.138,.592),l(.228,.366),l(.119,.521),l(-.109,.339),l(-.266,.213),l(-.769,-.08),l(-.321,-.21),l(-.178,-.196),l(-.153,-.239),l(-.111,-.38),l(-.628,.413),l(-.647,.159),l(-.246,-.083),l(-.378,-.266),l(-.341,-.746),l(-.291,-.379),l(-.481,.045),l(-.507,.003),l(-.228,-.098),l(-.117,-.352),l(.729,-1.584),l(-.033,-.268),l(-.521,-.096),l(-.554,.074),l(-.202,-.324),l(-.277,-1.762),l(-.156,-.126),l(-.479,.017),l(-.771,.089),l(-.819,.442),l(-.312,.086),l(-.216,-.069),l(0,-.268),l(.224,-.58),l(-.163,-.705),l(-.075,-.465),l(.617,-.85),l(.191,-.198),l(.487,-.271),l(.611,-.525),l(.429,-.722),l(.353,-.862),l(-.02,-.875),l(-.195,-1.649),l(-.146,-.14),l(-.504,.031),l(-.287,-.041),l(-.217,-.309),l(-.243,-.901),l(-.397,-.435),l(-.504,-.279),l(-.277,.044),l(-.306,.34),l(-.001,.127),l(-.624,-.081),l(-.73,-.179),l(-.657,-.081),l(-.3,-.055),l(-.102,-.056),l(.138,-.269),l(.354,-.454),l(-.046,-.38),l(-.716,-.715),l(-.455,-.392),l(-1.377,.84),l(-.377,.044),l(-.975,-.319),l(-.286,-.167),l(-.355,.087),l(-.546,.299),l(-1.105,.726),l(-.829,.258),l(-.543,.37),l(-1.123,1.107),l(-.397,.27),l(-.714,.216),l(-.784,.033),l(-.189,.1),l(-.329,-.619),l(-.312,-.209),l(-.371,-.041),l(-1.659,-.047),L(601,137.538),l(-.309,-.294),l(-.417,.016),l(-.149,.103),l(-.348,.239),l(-.609,.539),l(-1.251,1.545),l(-.212,-.662),l(.052,-.861),l(-.139,-.183),l(-.231,-.069),l(-.471,.102),l(-.345,.129),l(-.655,-.159),l(-.339,.281),l(-.341,-.116),l(-.849,.066),l(-.319,-.364),l(-.63,-.281),l(-.407,0),l(-.08,.331),l(-.271,.083),l(-.685,-.38),l(.01,.364),l(-.237,.099),l(-.141,-.463),l(-.54,-.496),l(-.365,.066),l(-.935,-.066),l(-.014,-.265),l(.175,-.396),l(-.326,-.017),l(-.333,.248),l(-1.451,-.893),l(.069,-.281),l(-.178,-.38),l(-.289,-.166),l(-.71,.116),l(-.158,.166),l(-.657,-.794),l(-.454,-.281),l(-.15,.132),l(-.472,-.215),l(-.726,-.595),l(-.867,-.264),l(-.132,-.612),l(-1.079,-.199),l(-.186,.182),l(-.275,-.066),l(-.134,.513),l(-.276,.314),l(-.299,-.05),l(-.24,-.43),l(-.859,-.596),l(-.154,.066),l(-.756,-.248),l(.116,-.364),l(-1.078,-.579),l(-.363,.116),l(-.772,-.843),l(-.383,-.248),l(-.477,.314),l(-.198,-.066),l(-.099,-.43),l(.215,-.215),l(-.272,-.364),l(.104,-.446),l(-.579,-.595),l(-.157,-.694),l(.785,-.198),l(.033,.364),l(.337,.264),l(.368,-.049),l(.376,-.281),l(.578,-.364),l(-.367,-.694),l(.104,-.414),l(-.68,-.099),l(-.044,-.182),l(-.092,-.078),l(-.718,-.096),l(-.294,-.221),l(-.037,-.552),l(-.073,-.589),l(.184,-.184),l(.331,-.221),l(-.221,-.258),l(-.441,-.405),l(-.81,-.11),l(-.221,-.515),l(-.441,-1.03),l(0,-.515),l(-.502,.152),l(-.147,-.126),l(-.305,-.111),l(-1.337,-.104),l(-.565,-.436),l(-.178,-.09),l(-.104,-.199),l(-.239,0),l(-.52,.196),l(-.252,-.281),l(-.198,-.218),l(-.301,-.07),l(.312,-.516),l(-.007,-.501),l(-.213,-.247),l(-.373,-.323),l(-.625,.009),l(-.282,.128),l(-.004,-.456),l(-.554,-.117),l(-.296,-.047),l(-.281,.21),l(-.105,-.112),l(-.313,-.14),l(-.048,-.088),l(-.236,-.012),l(-.01,-.269),l(.574,.059),l(.192,-.145),l(.354,-.136),l(.08,-.249),l(.181,-.185),l(-.2,-.163),l(.152,-.36),l(-.245,-.237),l(.126,-.428),l(-.049,-.123),l(-.152,-.012),l(-.028,-.246),l(.009,-.284),l(-.295,-.494),l(-.273,-.154),l(-.692,-.039),l(-.22,-.06),l(-.229,.162),l(-.463,.045),l(-.325,-.394),l(-.077,-.305),l(.207,-.223),l(-.023,-1.031),l(.011,-.069),l(.139,-.739),l(.129,-.213),l(.274,-.186),l(1.422,-.704),l(1.737,-.734),l(.359,-.03),l(.06,.071),l(.183,.567),l(.344,.055),l(.507,-.145),l(.885,-.132),l(.268,-.243),l(.463,-.784),l(.467,-.472),l(.238,-.03),l(1.248,.235),l(.459,-.017),l(.478,-.102),l(.646,-.345),l(.638,-.701),l(.405,-.301),l(.445,-.145),l(1.338,-.349),l(.97,-.219),l(.325,-.187),l(.051,-.157),l(-.031,-.194),l(-.139,-.86),l(.02,-.399),l(.32,-.401),l(.705,-.546),l(.222,-.33),l(-.119,-.47),l(-.29,-.441),l(-.345,-.627),l(-.03,-1.357),l(-.483,-.141),l(-.366,.06),l(-.232,-.185),l(.068,-.215),l(.215,-.302),l(.393,-.188),l(1.799,-.254),l(1.082,-.207),l(.35,-.06),l(.5,.184),l(1.133,.238),l(.394,-.074),l(.231,-.145),l(.156,-.245),l(-.126,-.286),l(-.622,-.514),l(.137,-.418),l(.286,-.605),l(.438,-.794),l(.516,-1.141),l(.427,-.507),l(1.096,.254),l(.721,.141),l(.594,.141),l(1.402,.079),l(.718,-.062),l(.417,-.103),l(.444,-.392),l(.157,-.39),l(-.213,-.707),l(-.097,-.75),l(.34,-.581),l(.428,-.277),l(1.199,-.093),l(.289,-.06),l(.306,-.219),l(.035,-.478),l(.011,-.275),l(.279,-.262),l(.542,-.148),l(.551,-.034),l(.228,-.014),l(.569,-.003),l(.244,-.074),l(.062,.145),l(.131,.405),l(.24,.563),l(.371,.433),l(1.301,.745),l(.834,.415),l(.614,.069),l(.731,.167),l(.633,.144),l(.354,.143),l(.568,.618),l(1.07,1.451),l(.401,.66),l(-.215,.447),l(-.237,.75),l(-.214,.389),l(-.369,.347),l(-.035,.129),l(.105,.43),l(.233,.229),l(.724,.312),l(1.062,.181),l(1.505,.021),l(.995,.038),l(.668,.083),l(.998,.224),l(.632,.268),l(1.645,.806),l(.839,.31),l(.744,.096),l(.105,.542),l(1.571,2.161),l(.467,.439),l(.444,.254),l(1.979,.018),l(1.241,.207),l(.802,.109),l(1.165,.022),l(2.861,-.059),l(.937,.023),l(1.164,.022),l(1.69,.119),l(.521,.168),l(.815,.551),l(1.006,.365),l(1.599,.433),l(.929,.123),l(.663,-.061),l(.61,.067),l(.253,.297),l(.433,.197),l(.481,-.017),l(.686,-.289),l(1.44,-.534),l(.303,-.101),l(.736,-.274),l(.816,-.289),l(1.204,-.349),l(1.339,-.007),l(1.514,-.065),l(.987,.08),l(.651,-.061),l(1.941,-.737),l(.265,-.172),l(1.111,-1.046),l(.67,-.389),l(1.265,-.292),l(.326,-.259),l(.123,-.271),l(-.188,-.228),l(-.599,-.411),l(-.389,-.569),l(-.003,-.343),l(.214,-.401),l(.539,-.589),l(.457,-.231),l(.316,-.073),l(.718,.154),l(.668,.382),l(.592,.125),l(.982,-.005),l(.744,-.047),l(.742,-.433),l(1.192,-.91),l(.224,.013),l(.438,.012),l(.624,.054),l(.896,-.134),l(.638,-.248),l(.347,-.188),l(.241,-.216),l(.312,-.82),l(.363,-.333),l(.47,-.204),l(.464,-.045),l(.483,.127),l(.353,-.189),l(.831,-.278),l(.539,-.146),l(.937,-.221),l(.854,-.033),l(.432,.099),l(1.074,.008),l(.464,.127),l(.414,-.218),l(.107,-.217),l(-.048,-.273),l(-.599,-.501),l(-.879,-.99),l(-.797,-.5),l(-.538,-.199),l(-.928,-.212),l(-.438,.002),l(-1.191,.786),l(-.292,-.07),l(-.431,-.416),l(-.317,-.085),l(-.576,.018),l(-.754,.062),l(-.929,.395),l(-.342,.045),l(-.051,-.029),l(-.269,-.836),l(.381,-.58),l(1.224,-1.959),l(.687,-1.207),l(.295,-.31),l(.046,.018),l(.452,.172),l(1.126,.574),l(.343,-.016),l(.438,-.089),l(2.44,-.752),l(.779,-.339),l(.123,-.233),l(-.056,-.306),l(-.35,-.348),l(-.062,-.146),l(.103,-.249),l(.422,-.426),l(.416,-.543),l(.667,-.779),l(.599,-.545),l(1.371,-.608),l(.167,-.794),l(-.107,-.249),l(-.465,-.306),l(-.558,-.026),l(-.822,.078),l(.119,-.25),l(.375,-.282),l(1.193,-.787),l(.478,-.165),l(.602,-.136),l(1.854,-.143),l(.836,-.123),l(1.203,-.109),l(.917,-.049),l(1.148,.215),l(1.037,.481),l(.683,.188),l(1.386,-.125),l(.539,.026),l(.763,.467),l(.742,.952),l(1.087,2.384),l(.94,1.588),l(.927,1.903),l(.436,.389),l(.507,.2),l(1.247,.341),l(1.523,.253),l(2.659,.839),l(.205,.144),l(.297,.866),l(.44,1.283),l(.261,.446),l(.68,-.033),l(.649,-.018),l(1.657,-.052),l(.604,-.22),l(.953,-.308),l(1.357,-.31),l(1.181,-.208),l(.902,-.034),l(.246,.114),l(.064,.259),l(.116,.389),l(.017,.504),l(-.566,.407),l(-.66,.393),l(-.291,.707),l(-.278,.893),l(-.538,1.066),l(-.627,1.08),l(-.329,.432),l(-.551,.69),l(-.47,.347),l(-.547,-.098),l(-.679,-.225),l(-.685,-.24),l(-.396,-.041),l(-1.664,.982),l(-.048,.557),l(.332,.897),l(.062,.656),l(-.006,.613),l(-.025,.385),l(-.097,.128),l(.112,.299),l(-.156,.329),l(-.511,.43),l(-1.252,.462),l(-.111,.058),l(-.579,-.68),l(-.247,.001),l(-.253,.129),l(-.383,.358),l(-.23,.713),l(-.955,.532),l(-.62,.259),l(-.538,.017),l(-.452,-.054),l(-.333,-.126),l(-.392,.059),l(-.273,.243),l(-.025,.342),l(.508,.765),l(.046,.113),l(-.527,.159),l(-.975,.048),l(-.508,-.153),l(-.493,-.253),l(-.273,-.396),l(-.448,.017),l(-.386,.13),l(-.686,1.027),l(-.636,.543),l(-1.032,.545),l(-1.533,.604),l(-.52,.329),l(-.415,.442),l(-.379,.528),l(-.066,-.092),l(-.417,.171),l(-1.222,.13),l(-.728,.171),l(-2.248,.925),l(-.632,.37),l(-.566,.469),l(-.604,.271),l(-.336,.015),l(.13,-.255),l(.862,-.682),l(.33,-.354),l(-.47,-.113),l(-.37,.072),l(-.153,-.297),l(.058,-.156),l(.953,-.781),l(.269,-.384),l(.55,-.413),l(.159,-.2),l(-.057,-.298),l(-.73,-.553),l(-.727,-.283),l(-.131,-.014),l(-.628,.03),l(-.166,.015),l(-.494,.371),l(-1.146,1.183),l(-.355,.157),l(-.643,.086),l(-.613,.243),l(-.36,.199),L(665.49,112),l(-.136,.411),l(-.131,.255),l(-.251,.255),l(-.437,.128),l(-.493,-.013),l(-.326,.27),l(-.307,.114),l(-.455,-.565),l(-.355,-.014),l(-.349,.128),l(-.396,.638),l(-.301,.694),l(.088,.34),l(.245,.368),l(.558,.268),l(.8,.268),l(1.21,-.045),l(.29,.254),l(-.019,.538),l(-.123,.581),l(.057,.538),l(.261,.283),l(.733,.069),l(.698,-.157),l(.76,-.525),l(.509,-.497),l(.552,-.228),l(.534,-.128),l(.287,.07),l(.895,.621),l(.543,.197),l(1.023,-.087),l(.361,.027),l(.471,.141),l(.274,0),l(-.248,.708),l(-.057,.552),l(-.612,-.197),l(-.297,-.084),l(-.525,.058),l(-1.677,.555),l(-.707,.44),l(-.072,.735),l(-.522,.157),l(-.146,-.113),l(-.017,-.269),l(-.127,-.084),l(-.501,.114),l(.138,.466),l(-.152,.368),l(-.485,.496),l(-1.397,1.119),l(-.126,.226),l(.039,.55),l(.62,.225),l(.712,.492),l(.785,.521),l(.391,.535),l(.424,1.241),l(.668,.647),l(.175,.437),l(-.13,.677),l(.172,.183),l(.694,.295),l(.399,.592),l(.562,.253),l(.272,.268),l(.087,.31),l(-.049,.155),l(-.789,.369),l(.088,.07),l(.425,.31),l(.314,.79),l(-.019,.296),l(-.141,.184),l(-.534,.043),l(-.651,.213),l(-.948,.552),l(-.849,.213),l(-.629,.297),l(.72,.21),l(.378,.056),l(.944,-.425),l(.488,-.058),l(.162,.056),l(.524,.592),l(.387,.168),l(.456,.027),l(.009,.155),l(-.231,.311),l(-.382,.227),l(-.304,.241),l(.11,.155),l(.326,-.029),l(.202,.084),l(-.184,.325),l(-.298,.749),l(-.192,.649),l(.028,.353),l(-.22,.452),l(-.209,.127),l(-.35,-.338),l(-.146,.142),l(-.569,.763),l(-.401,.622),l(-.215,.622),l(-.127,.296),l(-.595,.425),l(-.251,.438),l(-.254,.184),l(-.569,.029),l(-.382,.227),l(.279,.719),l(-.264,.508),l(.076,.593),l(-.093,.269),l(-.207,.269),l(-.532,.199),l(-.161,.282),l(-.174,.396),l(-.294,.636),l(-.626,.354),l(-.412,.495),l(-.492,-.14),l(-.443,-.069),l(-.142,.113),l(-.145,.198),l(.118,.833),l(-.213,.142),l(-.772,.651),l(-.356,.127),l(-.628,.171),l(-.563,.467),l(-.571,.213),l(-.107,.113),l(-.008,.48),l(-.133,.156),l(-.568,.058),l(-.5,.114),l(-.341,.438),l(-.364,-.126),l(-.52,-.168),l(-.274,.057),l(-.315,.326),l(-.435,.198),l(-.643,.1),l(-.047,-.465),l(-.52,.057),l(-.699,.213),l(-.32,.198),l(-.285,-.042),l(-.401,-.493),l(-.163,-.155),l(-.191,.283),l(.095,.169),l(-.045,.212),l(-.047,.691),l(-.209,.297),l(-.416,.114),l(-.501,-.182),l(-.123,.282),l(-.001,.24),l(-.146,.155),l(-.615,.058),l(-.366,.114),l(-.596,.043),l(-.463,-.211),l(-.217,.1),l(-.439,.48),l(-.227,.071),l(-.774,-.041),l(-.747,.227),l(-.406,.326),l(-.451,-.027),l(-.277,-.084),l(-.011,.057),l(-.069,.353),l(-.29,.396),l(.011,.113),l(.48,.634),l(.269,.126),l(.043,.198),l(-.36,.269),l(-.763,.157),l(-.481,-.719),l(-.241,-.691),l(.012,-.395),l(.396,-.777),l(-.015,-.169),l(-.587,-.253),l(-.226,.071),l(-.206,.297),l(-.454,.072),l(-.676,-.21),l(-.574,-.733),l(-.196,.085),l(-.017,.169),l(-.159,.396),l(-.27,.128),l(-.332,-.056),l(-.481,.043),l(-.055,.038),l(-.197,-.143),l(-.909,-.107),l(-.621,-.166),l(-.943,-.615),l(-.273,-.365),l(-.096,-.521),l(.472,-.75),l(-.056,-.38),l(-.237,-.21),l(-.777,.047),l(-.66,-.053),l(-.771,-.235),l(-.623,-.575),l(-.298,.101),l(-.521,.328),l(-.696,.554),l(-.341,.157),l(-.807,.117),l(-1.595,.052),l(-.332,.115),l(-.584,.413),l(-.325,-.097),l(-.597,-.392),l(-.341,-.012),l(-.515,.13),l(-.483,.229),
+N(241.073,156.152),l(.017,.52),l(.098,1.215),l(.012,.212),l(-.379,.455),l(-.011,.17),l(.485,1.358),l(-.669,-.577),l(-.445,-.056),l(-.761,.143),l(-.877,-.012),l(-.666,-.14),l(-.574,-.056),l(-.474,.1),l(-.378,.354),l(-.135,-.042),l(-.993,-.549),l(-.171,-.325),l(.04,-.198),l(.269,-.184),l(1.051,.097),l(.631,.111),l(1.125,.167),l(.654,.041),l(.61,-.185),l(.386,-.156),l(-.198,-.155),l(-.692,-.464),l(-.136,-.296),l(.184,-.707),l(-.202,-.296),l(-.394,-.154),l(-.913,-.14),l(-.305,-.211),l(.04,-.184),l(.119,-.085),l(.344,-.1),l(.724,-.058),l(.781,.125),l(1.081,.294),l(.576,.056),l(.147,-.089),
+N(241.295,160.082),l(-.485,-1.358),l(.011,-.17),l(.379,-.455),l(-.012,-.212),l(-.098,-1.215),l(-.017,-.52),l(.503,-.279),l(.393,.14),l(.342,0),l(.384,-.17),l(.369,-.043),l(.14,.198),l(.177,.112),l(1,.309),l(.657,-.072),l(.213,.395),l(.335,.338),l(.528,.324),l(.335,.084),l(.643,.21),l(.916,.45),l(.399,.352),l(.231,.311),l(-.191,.17),l(-.144,.297),l(-.314,.368),l(-.238,-.098),l(-.476,-.592),l(-.378,-.042),l(-.788,.058),l(-.288,-.098),l(-.373,0),l(-.329,.1),l(-.763,.539),l(-.396,-.056),l(-.319,-.494),l(-.166,-.028),l(-.155,.057),l(-.658,.326),l(-.344,.778),l(-.41,.65),l(-.289,-.112),l(-.325,-.551),
+N(668.053,167.796),l(-.131,-.099),l(-.74,-.732),l(-.444,-1.255),l(.037,-.424),l(.054,-.706),l(-.292,-.465),l(.18,-.382),l(.978,.704),l(.202,-.424),l(.023,-.41),l(-.101,-.438),l(-.026,-.579),l(.145,-.438),l(.025,-.664),l(.082,-.861),l(.074,-.636),l(.38,-.862),l(.188,-.127),l(.337,-.142),l(.523,.055),l(1.21,.52),l(.576,.042),l(.188,-.212),l(.277,-.17),l(.199,.141),l(.018,.396),l(-.266,.438),l(-.045,.48),l(.14,.79),l(.541,.394),l(.182,.325),l(-.427,1.271),l(-.31,.467),l(-.834,.608),l(-.555,.312),l(-.082,.099),l(.003,.268),l(-.078,.565),l(-.28,.424),l(.127,.211),l(.35,.733),l(.345,1.101),l(.26,.62),l(.093,.028),l(.451,.324),l(.296,.07),l(.161,-.325),l(.485,-.537),l(.197,-.029),l(.418,.112),l(.226,.211),l(.179,.635),l(.286,.353),l(.326,.084),l(.505,-.58),l(.621,.267),l(.141,.028),l(.078,.127),l(-.168,.156),l(-.378,.156),l(-.208,.212),l(.936,1.226),l(-.315,.184),l(-.568,-.196),l(-.404,-.098),l(-.094,-.48),l(-.229,-.31),l(-.599,-.535),l(-.753,-.577),l(-.258,0),l(-.099,.226),l(.371,.776),l(-.218,.283),l(-.727,-.775),l(-.982,-.548),l(-.309,.015),l(-.344,.142),l(-.182,.255),l(-.14,.071),l(-.395,.057),l(-.322,-.225),l(-.591,-.366),l(-.195,-.423),l(.64,-.608),l(.323,.211),l(.358,.084),l(.4,-.199),l(-.151,-.169),l(-.713,-.295),l(-.422,-.395),l(-.522,-.168),l(-.239,.607),M(669.676,172.974),l(-.452,-.366),l(-.349,-.408),l(-.051,-.226),l(-.364,-1.114),l(-.459,-.521),l(.685,-.058),l(.868,.012),l(.314,.169),l(.274,.451),l(.028,.861),l(-.097,.48),l(-.214,.283),l(-.185,.438),M(679.073,175.368),l(-.562,-.267),l(-.178,-.254),l(-.237,-.169),l(-.064,-.127),l(.139,-.339),l(-.091,-.423),l(-.55,-.352),l(-.662,-.479),l(-.147,-.466),l(.513,-.1),l(1.017,-.143),l(.371,.112),l(.283,.409),l(.069,.818),l(.123,.805),l(.257,.79),l(-.112,.127),l(-.167,.057),M(671.406,176.824),l(.022,-.353),l(.186,-1.342),l(.061,-1.172),l(.482,.395),l(.576,.281),l(.366,.028),l(.634,-.185),l(.027,.438),l(-.079,.396),l(-.24,.269),l(-.184,.198),l(-.908,.651),l(-.943,.397),M(664.721,177.812),l(-.366,-.027),l(.127,-.297),l(.202,-.099),l(.314,-.396),l(.265,-.523),l(.082,-.607),l(.138,-.509),l(.326,-.184),l(.14,.183),l(-.16,.283),l(.276,.55),l(.212,.564),l(-.108,.113),l(-.664,.354),l(-.406,.368),l(-.377,.227),M(673.781,179.981),l(-.385,-.366),l(-.828,-.591),l(-.206,-.38),l(.099,-.368),l(.565,-.213),l(.22,-.269),l(.487,-1.822),l(.438,-.185),l(.271,.028),l(.113,.126),l(.049,.282),l(-.373,.905),l(-.089,.226),l(-.315,1.413),l(.165,.296),l(.214,.465),l(-.213,.41),l(-.212,.043),M(661.179,181.308),l(-.317,-.042),l(.215,-.48),l(.343,-.467),l(.35,-.34),l(.592,-.354),l(.636,-.496),l(.933,-1.089),l(.11,.55),l(-.118,.424),l(-.267,.297),l(-.24,.184),l(-.516,.213),l(-.172,.127),l(-.244,.622),l(-.327,.269),l(-.591,.171),l(-.386,.41),M(680.678,185.402),l(-.201,-.098),l(-.098,-.438),l(-.361,-.748),l(-.297,-.112),l(-.381,.608),l(-.361,.82),l(.246,.38),l(.166,.395),l(.148,.677),l(-.158,.495),l(-.383,.509),l(-.305,-.253),l(-.176,-.268),l(.201,-.41),l(-.076,-.325),l(-.363,-.07),l(-.361,.665),l(-.173,-.056),l(-1.114,-.619),l(-.557,-.549),l(-.206,-.508),l(-.037,-.635),l(.466,-.905),l(-.108,-.479),l(-.466,-.409),l(-.778,-.295),l(-.292,.043),l(-.062,.17),l(-.129,.226),l(-.5,.255),l(-.661,.058),l(-.027,-.494),l(-.174,-.24),l(-.399,.199),l(-.504,.961),l(-.525,.863),l(-.219,-.084),l(-.119,-.127),l(-.034,-.197),l(.054,-.311),l(.359,-.749),l(.185,-.537),l(.263,-.283),l(.383,-.184),l(.564,-.044),l(.219,-.127),l(.134,-.466),l(.205,-.156),l(.549,-.241),l(.777,-.101),l(.292,.353),l(.102,.72),l(.317,-.156),l(.485,-.058),l(.207,-.184),l(.207,-.565),l(.358,-.255),l(.479,.21),l(.186,.084),l(.158,-1.087),l(.29,-.015),l(.264,.465),l(.315,-.481),l(.205,-.142),l(.392,.098),l(.133,-.227),l(-.048,-.706),l(-.172,-.564),l(.146,-.353),l(.413,.549),l(.711,.803),l(.229,.48),l(.083,.324),l(-.336,.735),l(.237,.226),l(.537,-.1),l(.076,.423),l(-.114,.424),l(.056,.692),l(.207,.437),l(-.002,.438),l(-.111,.424),l(-.283,.579),l(-.332,.622),
+N(251.898,160.229),l(-.547,-.112),l(-.073,-.212),l(.051,-.551),l(-.109,-.24),l(.11,-.17),l(.235,-.071),l(.543,.069),l(.404,-.015),l(1.505,.11),l(.393,.168),l(.113,.141),l(-.188,.354),l(-.07,.099),l(-.283,.227),l(-.335,.043),l(-.739,-.083),l(-.657,.072),l(-.354,.17),
+N(228.82,160.519),l(-.299,-.056),l(-.693,-.224),l(-.229,-.268),l(-.47,-.366),l(-.465,-.084),l(-.142,-.211),l(.53,-.284),l(.704,-.072),l(1.364,.251),l(.97,.351),l(.651,.267),l(.279,.282),l(-.04,.198),l(-.332,.071),l(-.751,-.295),l(-.543,.058),l(-.227,.255),l(-.308,.128),
+N(400.72,175.499),l(-.595,-.119),l(-.161,-.032),l(-.976,.458),l(-1.429,-.006),l(-.867,-.037),l(-2.119,.041),l(-.271,.157),l(-.125,.34),l(.261,1.934),l(.02,.41),l(-.191,.17),l(-.63,-.434),l(-.644,-.166),l(-.769,.075),l(-.608,.159),l(-.446,.257),l(-.368,.115),l(-.354,-.083),l(-.452,-.223),l(-.52,-.562),l(-.15,-.465),l(-.308,-.252),l(-.545,.003),l(-.259,-.168),l(.08,-.043),l(.046,-.156),l(.172,-.326),l(.261,-.92),l(.133,-.876),l(-.058,-.749),l(.141,-.255),l(.257,-.016),l(.448,-.158),l(.543,-.271),l(.317,-.369),l(.292,-1.047),l(.355,-1.217),l(.397,-.384),l(.273,-.058),l(.293,.281),l(.551,.364),l(.45,-.102),l(.174,-.227),l(.393,-.822),l(.492,-.667),l(.638,-.625),l(.272,-.101),l(.518,.209),l(.324,.182),l(.274,.027),l(.314,-.793),l(.304,-.228),l(.947,-.458),l(1.22,-.713),l(.37,-.073),l(.356,.125),l(.36,.059),l(1.062,.173),l(-.173,.665),l(.019,.396),l(.426,.93),l(.422,.492),l(.389,.266),l(.502,.42),l(.179,.268),l(.018,.212),l(-.335,.439),l(.114,.226),l(.97,.757),l(.516,.181),l(.37,-.044),l(.562,.025),l(.568,.971),l(.131,.367),l(-.237,.764),l(-.415,.468),l(-.337,.158),l(-.739,-.095),l(-.418,.045),l(-.415,.271),l(-.366,.553),l(-.24,.157),l(-.062,.142),l(-.29,-.041),l(-.611,-.166),l(-1.013,-.163),
+N(209.823,175.47),l(-.388,-.645),l(-.932,-.888),l(-1.003,-1.085),l(-.837,-.817),l(-.723,-.464),l(-.196,-.183),l(-.023,-.226),l(.625,-.03),l(1.001,-.125),l(.29,-.143),l(.293,-.412),l(.005,-.961),l(.882,-.034),l(.513,-.583),l(.725,.42),l(1.207,-.997),l(.503,-.794),l(.294,-.242),l(.259,.013),l(.328,.182),l(.463,.097),l(.248,-.086),l(.424,-.229),l(1.425,-.486),l(.23,.519),l(.038,.339),l(-.057,.509),l(-.214,.707),l(-.543,.806),l(-.1,.749),l(.042,.904),l(-.245,1.13),l(-.188,.735),l(-.101,1.385),l(.031,.55),l(.184,.466),l(.188,.363),l(-.281,.274),l(-.767,-.149),l(-.241,-.46),l(-.285,.077),l(-.394,-.107),l(-.603,.25),l(-1.651,-.599),l(-.43,.271),
+N(634.036,168.444),l(.533,.251),l(.736,.165),l(.341,.111),l(.469,.251),l(.248,-.058),l(.095,-.24),l(-.103,-.211),l(-.187,-.182),l(-.107,-.296),l(.392,-.285),l(.476,-.186),l(.444,.181),l(.373,.351),l(.3,-.072),l(.975,-.429),l(.209,-.114),l(-.215,.905),l(.029,.663),l(.455,1.014),l(-.076,.367),l(-.05,.424),l(.054,1.073),l(-.217,.509),l(-.385,.285),l(-.599,.187),l(-.932,.443),l(-.521,.229),l(-.99,.571),l(-.234,.425),l(.216,.422),l(.568,.435),l(.03,.197),l(-.17,.27),l(-.688,-.194),l(-.497,-.011),l(-.599,.229),l(-.915,.528),l(-.646,.229),l(-.3,.289),l(-.256,-.188),l(-.248,-.268),l(-.35,-.042),l(-.382,.142),l(-.205,-.042),l(-.293,.043),l(-.183,-.113),l(.142,-.311),l(.182,-.226),l(-.04,-.254),l(-.283,-.395),l(-.277,.043),l(-.462,.298),l(-.339,.015),l(-.171,-1.044),l(-.649,-1.488),l(.146,-.176),l(-.16,-.479),l(-.487,-.717),l(-.219,-.648),l(-.026,-.635),l(.076,-.382),l(.146,-.297),l(.92,-1.233),l(.521,-.441),l(.383,-.101),l(1.172,-.091),l(.798,.066),l(.558,-.074),l(.575,.039),l(.599,.109),l(.301,.167),
+N(214.474,175.913),l(.821,.884),l(.385,.623),l(.314,.322),l(.225,.046),l(.465,.645),l(.441,.352),l(-.014,.006),l(-.074,.123),l(-.478,-.184),l(-.219,.205),l(-.014,.321),l(0,.58),l(.507,.307),l(-.396,.368),l(.125,.532),l(-.374,.369),l(.243,.184),l(-.204,.609),l(.003,-.466),l(-.296,-.307),l(.01,-.568),l(-.377,-.148),l(-.238,-.102),l(-.111,.082),l(.325,.41),l(.084,.225),l(-.113,.062),l(-.726,-.299),l(-.13,-.282),l(.251,-.339),l(.04,-.382),l(-.182,-.338),l(-.486,-.324),l(-.695,-.287),l(-.079,-.144),l(-.689,-.103),l(-.316,-.327),l(.007,-.421),l(-.146,-.255),l(-.249,-.098),l(-.576,-.353),l(-.416,-.266),l(.225,.512),l(.086,.222),l(.422,.044),l(.181,.266),l(-.544,.573),l(-.144,-.262),l(-.356,-.282),l(-.561,-.211),l(-.323,-.239),l(-.147,-.24),l(-.104,-.494),l(.128,-.421),l(.332,-.225),l(-.008,-.389),l(-.554,-.225),l(.363,-.123),l(-.057,-.227),l(-.238,.017),l(.43,-.271),l(1.651,.599),l(.603,-.25),l(.394,.107),l(.285,-.077),l(.241,.46),l(.767,.149),l(.281,-.274),
+N(436.304,195.359),l(-.209,-.451),l(-.194,-.804),l(-.498,-.802),l(-1.217,-1.236),l(-.112,-.324),l(-.064,-.791),l(-.432,-.605),l(-.4,-.661),l(-.207,-.592),l(-.273,-1.185),l(-.112,-.776),l(.064,-.424),l(.144,-.198),l(.528,-.399),l(.4,-.511),l(.866,-1.743),l(.354,-.327),l(.208,-.114),l(.096,.084),l(.24,.027),l(.449,-.158),l(1.363,-.686),l(.494,.661),l(.225,.069),l(.224,-.03),l(.466,-.271),l(.754,-.386),l(.818,-.273),l(.881,.009),l(.368,-.031),l(.258,-.143),l(.646,-1.176),l(.117,-.806),l(.209,-.199),l(.434,-.059),l(2.181,-.055),l(.386,-.087),l(2.649,-2.275),l(.196,-.284),l(.005,-.41),l(.165,-.382),l(.372,-.228),l(.939,-.613),l(.322,-.086),l(.321,.012),l(.559,.208),l(1.342,1.673),l(.347,.549),l(.122,.536),l(-.416,1.472),l(.01,.664),l(.158,.211),l(.802,-.019),l(.272,-.001),l(.207,.126),l(.252,.493),l(.223,.225),l(.797,.447),l(.799,.235),l(.351,.196),l(.223,.267),l(-.148,.481),l(.173,.395),l(.445,.351),l(.558,.378),l(.717,.434),l(.207,.168),l(.206,.366),l(.059,.72),l(.285,.436),l(.367,.224),l(.814,.165),l(.558,.477),l(.157,.352),l(-.083,.679),l(.031,.07),l(-.496,.145),l(-.962,.344),l(-.319,-.026),l(-.287,-.167),l(-.687,-.264),l(-1.15,-.361),l(-.4,.13),l(-.082,.551),l(-.13,.241),l(-.256,.058),l(-.399,-.026),l(-.862,-.207),l(-.593,.102),l(-.93,.373),l(-.93,.486),l(-.271,.016),l(-.559,-.35),l(-.383,-.153),l(-.353,.186),l(-.677,1.36),l(-.176,.157),l(-.591,-.067),l(-1.934,-.511),l(-1.422,-.189),l(-.512,-.322),l(-.159,-.239),l(-.334,-.394),l(-.75,-.518),l(-.432,-.167),l(-.479,.06),l(-.529,.3),l(-.353,.341),l(-.785,.979),l(-.097,.297),l(.096,.452),l(-.017,.353),l(-.063,.594),l(-.16,.058),l(-.751,.287),l(-.895,-.093),l(-.624,-.138),l(-.367,-.054),l(-.975,.175),l(-.479,.102),l(-.255,.228),l(-.222,1.287),l(-.158,.523),l(-.365,.497),l(-.303,.312),
+N(371.324,180.419),l(1.088,-1.235),l(.643,-.866),l(.238,-.157),l(.594,.039),l(1.137,-.148),l(.466,.04),l(.42,.224),l(.456,.421),l(.475,.619),l(.252,.761),l(.165,1.369),l(.26,.656),l(-.259,.502),l(-1.184,1.151),l(-.63,.724),l(-.285,.256),l(-.061,.103),l(-.631,-.523),l(-.661,-.408),l(-.483,-.196),l(-.033,-.141),l(.061,-.297),l(-.05,-.184),l(-.531,-.21),l(-.792,-.549),l(-.389,-.366),l(-.375,-.465),l(.494,-.241),l(.174,-.227),l(-.034,-.155),l(-.484,-.211),l(-.065,-.113),l(.022,-.173),
+N(579.606,186.906),l(-.493,-.083),l(-.265,-.197),l(-.21,-.353),l(-.246,-.621),l(-.361,-1.256),l(-.034,-.649),l(.005,-.763),l(-.02,-.904),l(-.24,-.696),l(.188,-.513),l(-.298,-.35),l(.068,-.28),l(.423,.023),l(.349,-.43),l(.053,-.367),l(.226,-.369),l(-.152,-.537),l(.529,.48),l(.699,.38),l(.234,.395),l(.549,.776),l(.175,.324),l(-.099,.339),l(.024,.141),l(.592,.338),l(.266,.733),l(.798,1.368),l(.136,.508),l(-.193,.735),l(-.397,.679),l(-.369,.396),l(-.514,.425),l(-.78,.284),l(-.642,.043),
+N(217.111,178.792),l(.52,.307),l(.195,.512),l(.02,.374),l(.363,.155),l(.628,.024),l(.244,-.205),l(.398,.43),l(.726,.082),l(.458,-.083),l(1.348,-.751),l(.514,-.046),l(1.387,-.921),l(.373,.144),l(.742,.069),l(.071,.156),l(.789,-.017),l(.767,.21),l(.666,.38),l(.644,.563),l(.406,.666),l(.084,.327),l(.228,.149),l(.509,1.038),l(-.322,.062),l(-.094,.43),l(-.584,.409),l(-.085,-.307),l(-.19,-.082),l(.045,.45),l(-.228,.082),l(-.256,.753),l(-.378,-.825),l(-.441,-.762),l(-.137,-.452),l(.179,-.24),l(.22,-.085),l(.786,.125),l(-.336,-.193),l(-.125,-.164),l(-.096,-.471),l(-.309,.307),l(-.439,.041),l(-.244,-.378),l(-.031,-.269),l(-.193,-.282),l(-.132,.151),l(-.226,-.287),l(-.11,.102),l(-.132,-.266),l(-.456,-.192),l(-.562,-.013),l(-.499,.241),l(-.382,.108),l(-.07,.359),l(.081,.234),l(-.529,.318),l(-.374,.184),l(-.335,.029),l(-.345,.41),l(.049,.296),l(.316,.297),l(.464,.43),l(.178,.386),l(-.011,.146),l(-.281,.081),l(-.243,-.042),l(-.431,.391),l(-.568,.105),l(-.339,-.042),l(-.189,-.146),l(.108,-.164),l(-.349,-.833),l(-.244,-.353),l(-.177,.674),l(-.812,-.409),l(-.227,-.757),l(-.207,.041),l(-.96,-.123),l(-.434,-.266),l(-.599,0),l(-.314,.113),l(-.361,.495),l(.204,-.609),l(-.243,-.184),l(.374,-.369),l(-.125,-.532),l(.396,-.368),l(-.507,-.307),l(0,-.58),l(.014,-.321),l(.219,-.205),l(.478,.184),l(.074,-.123),
+N(266.015,188.956),l(-.503,-.647),l(-.732,-.745),l(-.324,-.521),l(.071,-.212),l(.395,-.539),l(-.008,-.58),l(.061,-.452),l(1.032,-.19),l(.41,-.144),l(.308,-.299),l(-.141,-.282),l(-.62,-.604),l(-.074,-.212),l(.101,-.255),l(1.655,-1.239),l(.061,-.41),l(-.095,-.296),l(.072,.049),l(.496,.338),l(.856,.521),l(.695,.521),l(.587,.663),l(.128,.409),l(-.036,.734),l(.148,.945),l(.298,-.593),l(.22,-.099),l(.6,.182),l(.101,.127),l(.507,.295),l(.708,.549),l(.401,.493),l(.374,.649),l(-.015,.339),l(-.142,.496),l(-.082,.862),l(-.183,.27),l(-1.131,.091),l(-.169,.17),l(.078,.777),l(-.095,.467),l(-.328,.694),l(.571,.831),l(.532,.675),l(.561,.067),l(.185,.295),l(.246,.832),l(.49,1.127),l(.332,.563),l(-.06,.212),l(-.943,-.022),l(-.934,.429),l(-1,.331),l(-.505,.314),l(-.694,.513),l(-.686,.075),l(-.613,-.392),l(-1.103,-.897),l(-.15,-.296),l(-.008,-.396),l(.072,-.354),l(-.167,-.31),l(-.281,-.394),l(-.156,-.465),l(.213,-.962),l(.547,-1.416),l(.179,-.368),l(-.435,-.958),l(-.533,-.138),l(-.304,.001),l(.452,-1.09),l(-.04,-.212),l(-.26,-.111),l(-.516,-.124),l(-.302,.058),l(-.375,.257),
+N(377.518,182.142),l(.193,-.376),l(.252,-.242),l(.367,-.143),l(.528,-.046),l(.338,.097),l(.165,.366),l(.22,.846),l(.009,.65),l(-.044,.283),l(.277,.323),l(.404,.322),l(.321,.026),l(.756,-.922),l(.143,-.086),l(.337,.097),l(.339,.224),l(-.062,.17),l(.119,.55),l(-.059,.438),l(-.501,.865),l(.05,.183),l(.194,.183),l(.369,.097),l(.592,-.032),l(1.264,1.334),l(.131,.282),l(-.059,.452),l(-.247,.849),l(-.105,.565),l(-.041,.919),l(-1.513,-.643),l(-1.221,-.619),l(-1.012,-.562),l(-.403,-.423),l(-1.129,-1.113),l(-1.111,-.76),l(-.723,-.337),l(-.901,-.535),l(-.66,-.548),l(.061,-.103),l(.285,-.256),l(.63,-.724),l(1.184,-1.151),l(.259,-.502),
+N(429.505,210.684),l(.484,.336),l(.177,.013),l(.443,-.271),l(.327,-.581),l(1.495,-.532),l(.054,.424),l(.042,.664),l(.147,.211),l(.57,-.328),l(.554,-.399),l(.931,-.811),l(.364,-.229),l(1.025,-.938),l(.086,-.706),l(-.122,-.72),l(.074,-.396),l(.193,-1.159),l(.343,-.751),l(.47,-.836),l(.41,-.454),l(.809,-.599),l(.525,-.229),l(.459,-.427),l(.139,-.523),l(.169,-.708),l(.115,-.61),l(.254,-1.342),l(.196,-2.021),l(.156,-.764),l(.094,-.551),l(.349,-.821),l(.558,-.837),l(.398,-1.203),l(.031,-.156),l(-.128,-.197),l(.16,-.058),l(.063,-.594),l(.017,-.353),l(-.096,-.452),l(.097,-.297),l(.785,-.979),l(.353,-.341),l(.529,-.3),l(.479,-.06),l(.432,.167),l(.75,.518),l(.334,.394),l(.159,.239),l(.512,.322),l(1.422,.189),l(1.934,.511),l(.591,.067),l(.176,-.157),l(.677,-1.36),l(.353,-.186),l(.383,.153),l(.559,.35),l(.271,-.016),l(.93,-.486),l(.93,-.373),l(.593,-.102),l(.862,.207),l(.399,.026),l(.256,-.058),l(.13,-.241),l(.082,-.551),l(.4,-.13),l(1.15,.361),l(.687,.264),l(.287,.167),l(.319,.026),l(.962,-.344),l(.496,-.145),l(.143,.239),l(.795,.772),l(.348,.493),l(.525,.477),l(.366,.195),l(.352,-.016),l(.258,-.242),l(.529,-.37),l(.384,.012),l(.684,.631),l(.16,-.1),l(.436,-.582),l(.258,-.157),l(.207,.083),l(1.032,.926),l(1.288,1.32),l(.063,.028),l(.159,.183),l(-.018,.424),l(-.26,1.88),l(.128,.253),l(.191,.027),l(.479,.04),l(.271,.182),l(.111,.31),l(-.191,.453),l(-1.195,1.066),l(-1.241,.996),l(-.255,.284),l(-.395,.681),l(-.217,1.02),l(-.107,.507),l(.021,.593),l(-.025,.734),l(.057,.748),l(-.076,.27),l(-.188,.298),l(-.426,.412),l(-.301,.171),l(-.269,.256),l(-.122,.425),l(-.49,1.358),l(.197,.338),l(.689,.999),l(.087,.381),l(-.04,.438),l(.014,.636),l(.237,.634),l(.017,1.329),l(-.064,.762),l(.425,1.439),l(-.102,.848),l(.122,.693),l(.486,.631),l(.936,.898),l(.428,.78),l(.689,1.804),l(-.563,.068),l(-3.015,.499),l(-.347,.214),l(-.154,.198),l(-.797,1.276),l(-.029,.622),l(.372,2.088),l(-.377,1.175),l(-.3,1.02),l(.043,.296),l(.456,.605),l(.837,.843),l(.445,.279),l(.515,.012),l(.448,-.455),l(.362,-.186),l(.136,.183),l(.032,.791),l(.028,1.427),l(-.041,.551),l(-.371,-.012),l(-1.355,-.091),l(-.348,-.21),l(-.381,-.647),l(-.561,-.731),l(-.416,-.351),l(-.477,-.252),l(-.895,-.263),l(-.38,-.28),l(-.728,-1.423),l(-.212,.354),l(-.522,.682),l(-.366,.087),l(-.325,-.111),l(-.922,-.164),l(-.925,-.249),l(-.489,-.194),l(-.58,-1.014),l(-.175,.071),l(-1.146,.289),l(-.195,-.056),l(-.172,-.352),l(-.365,-.379),l(-.678,-.096),l(-1.24,-.147),l(-.991,.146),l(-.859,.26),l(-.61,.004),l(-.199,-.197),l(-.007,-.254),l(.198,-.905),l(.003,-.466),l(-.306,-.62),l(-.844,-.998),l(-.117,-.211),l(-.021,-.212),l(.296,-1.033),l(.07,-1.06),l(-.109,-.607),l(-.303,-.605),l(.057,-.354),l(.177,-.693),l(-.052,-.183),l(-.322,-.098),l(-1.231,.093),l(-.88,.019),l(-.212,-.168),l(.055,-.48),l(-.181,-.225),l(-2.016,.082),l(-.112,.001),l(-.089,.354),l(.027,.593),l(-.286,.892),l(-.184,.411),l(-.993,.006),l(-.899,-.122),l(-1.021,.246),l(-.88,.034),l(-.292,-.168),l(-.64,-.787),l(-.597,-1.07),l(-.537,-1.409),l(-.059,-.579),l(-.121,-.521),l(-.082,-.127),l(-.321,-.111),l(-.385,-.012),l(-1.104,-.008),l(-.88,.034),l(-.624,.003),l(-1.312,-.048),l(-.945,-.052),l(-.783,.02),l(-.432,.03),l(-.318,.101),l(-.896,.062),l(-.699,.314),l(-.56,.06),l(-.112,-.013),l(-.322,-.922),l(.012,-.053),l(.74,-.3),l(-.017,-.085),l(.1,-.89),l(.094,-.143),l(.317,-.199),l(.583,-.568),
+N(468.568,202.653),l(1.277,-.05),l(4.659,0),l(1.448,-.033),l(4.663,2.568),l(3.553,1.984),l(.137,.381),l(-.116,.552),l(.07,.239),l(2.427,1.752),l(1.067,.807),l(-.237,.427),l(-.419,1.329),l(-.137,1.017),l(.109,.551),l(.665,.987),l(.768,.577),l(-.024,.282),l(-.263,.354),l(-.371,1.046),l(.037,.17),l(.306,.041),l(.118,.226),l(-.172,.692),l(-.019,.946),l(.411,1.793),l(.18,.564),l(.401,.465),l(.823,.55),l(.396,.268),l(.177,.228),l(-1.157,.836),l(-.515,.342),l(-1.218,.402),l(-1.243,.559),l(-.448,.031),l(-.646,-.42),l(-.276,-.083),l(-.21,.326),l(-.543,.766),l(-.396,.144),l(-.42,-.054),l(-.964,.006),l(-.963,.02),l(-.512,-.294),l(-.323,-.394),l(-.229,-.083),l(-.123,.128),l(-.643,.356),l(-.526,.088),l(-1.019,-.149),l(-.898,-.018),l(-.196,-.559),l(-.135,-.766),l(-.053,-.584),l(-.135,-.946),l(-.448,-.841),l(-.663,-.538),l(-1.014,-.107),l(-.223,.016),l(-.385,-.407),l(-.732,-.349),l(-1.574,-.57),l(-1.269,-.6),l(-.729,-.265),l(-.263,-.21),l(-.703,-.641),l(-.689,-1.804),l(-.428,-.78),l(-.936,-.898),l(-.486,-.631),l(-.122,-.693),l(.102,-.848),l(-.425,-1.439),l(.064,-.762),l(1.252,-.348),l(.388,-.539),l(.645,-1.204),l(.687,-.853),l(.346,-.271),l(.044,-.212),l(-.148,-.239),l(-.273,-.125),l(-.401,-.068),l(-.211,-.211),l(.158,-.976),l(.304,-.016),l(.396,-.158),l(.142,-.17),l(.104,-.481),l(-.167,-.493),l(-.364,-.888),l(-.121,-.748),
+N(464.786,206.235),l(-.197,-.338),l(.49,-1.358),l(.122,-.425),l(.269,-.256),l(.301,-.171),l(.426,-.412),l(.064,.042),l(.546,.223),l(.431,-.045),l(.458,-.427),l(.554,-.398),l(.319,-.017),l(.121,.748),l(.364,.888),l(.167,.493),l(-.104,.481),l(-.142,.17),l(-.396,.158),l(-.304,.016),l(-.559,.031),l(-.508,.159),l(-.391,.567),l(-.158,.085),l(-.396,.13),l(-.789,-.32),l(-.688,-.024),
+N(465.79,210.652),l(-.017,-1.329),l(-.237,-.634),l(-.014,-.636),l(.04,-.438),l(-.087,-.381),l(-.689,-.999),l(.688,.024),l(.789,.32),l(.396,-.13),l(.158,-.085),l(.391,-.567),l(.508,-.159),l(.559,-.031),l(-.158,.976),l(.211,.211),l(.401,.068),l(.273,.125),l(.148,.239),l(-.044,.212),l(-.346,.271),l(-.687,.853),l(-.645,1.204),l(-.388,.539),l(-1.252,.348),
+N(427.243,211.207),l(.536,-.414),l(.68,-.513),l(.351,-.13),l(.695,.533),l(-.583,.568),l(-.317,.199),l(-.094,.143),l(-.1,.89),l(.017,.085),l(-.74,.3),l(.143,-.625),l(-.082,-.24),l(-.505,-.796),M(427.998,213.843),l(.112,.013),l(.56,-.06),l(.699,-.314),l(.896,-.062),l(.318,-.101),l(.432,-.03),l(.783,-.02),l(.945,.052),l(1.312,.048),l(.624,-.003),l(.88,-.034),l(1.104,.008),l(.385,.012),l(.321,.111),l(.082,.127),l(.121,.521),l(.059,.579),l(.537,1.409),l(.597,1.07),l(.64,.787),l(.292,.168),l(.88,-.034),l(1.021,-.246),l(.899,.122),l(.993,-.006),l(.184,-.411),l(.286,-.892),l(-.027,-.593),l(.089,-.354),l(.112,-.001),l(2.016,-.082),l(.181,.225),l(-.055,.48),l(.212,.168),l(.88,-.019),l(1.231,-.093),l(.322,.098),l(.052,.183),l(-.177,.693),l(-.057,.354),l(.303,.605),l(.109,.607),l(-.07,1.06),l(-.296,1.033),l(.021,.212),l(.117,.211),l(.844,.998),l(.306,.62),l(-.003,.466),l(-.198,.905),l(.007,.254),l(.199,.197),l(.61,-.004),l(.859,-.26),l(.991,-.146),l(1.24,.147),l(-.214,1.344),l(.195,1.059),l(.269,.323),l(.089,.254),l(-.132,.368),l(-.436,.44),l(-.124,.594),l(-.125,.086),l(-.517,-.04),l(-3.408,.091),l(-.051,.877),l(.015,1.342),l(-.024,3.249),l(.017,.848),l(.023,.594),l(.099,.438),l(.308,.394),l(.471,.436),l(1.354,1.391),l(.611,.632),l(-.93,.218),l(-1.96,.379),l(-1.044,.203),l(-.717,-.08),l(-1.164,.063),l(-.408,-.083),l(-.349,-.21),l(-2.024,.026),l(-.697,-.024),l(-.622,-.151),l(-.401,-.322),l(-.305,-.366),l(-.408,-.096),l(-.989,-.023),l(-2.59,.016),l(-1.636,-.019),l(-.631,-.011),l(-1.296,-.006),l(-2.201,.013),l(-.636,-.151),l(-.463,-.309),l(-.45,-.478),l(-.294,-.083),l(-.499,.088),l(-.591,.286),l(-.778,.513),l(-.758,-.462),l(-.352,.144),l(-.248,.197),l(.048,-1.809),l(-.017,-.805),l(-.029,-.649),l(.397,-.34),l(.221,-.269),l(.26,-.707),l(.163,-.734),l(.184,-1.398),l(.239,-.976),l(.321,-.918),l(.584,-.665),l(.183,-.565),l(.268,-.354),l(.64,-.228),l(.268,-.325),l(.423,-.679),l(.364,-1.201),l(.053,-.664),l(-.046,-.848),l(-.191,-.959),l(-.201,-.536),l(-.492,-.705),l(-.476,-.776),l(-.268,-.775),l(.139,-.495),l(.476,-.382),l(.158,-.156),l(.107,-.424),l(-.006,-.479),l(-.199,-.579),l(-.489,-.69),l(-.441,-.733),l(-.203,-1.031),l(-.181,-.423),l(-.276,-.366),l(-.666,-.974),l(-.072,-.15),
+N(452.198,239.34),l(-.611,-.632),l(-1.354,-1.391),l(-.471,-.436),l(-.308,-.394),l(-.099,-.438),l(-.023,-.594),l(-.017,-.848),l(.024,-3.249),l(-.015,-1.342),l(.051,-.877),l(3.408,-.091),l(.517,.04),l(.125,-.086),l(.124,-.594),l(.436,-.44),l(.132,-.368),l(-.089,-.254),l(-.269,-.323),l(-.195,-1.059),l(.214,-1.344),l(.678,.096),l(.365,.379),l(.172,.352),l(.195,.056),l(1.146,-.289),l(.175,-.071),l(.58,1.014),l(.489,.194),l(.925,.249),l(.922,.164),l(.325,.111),l(.366,-.087),l(.522,-.682),l(.212,-.354),l(.728,1.423),l(.38,.28),l(.895,.263),l(.477,.252),l(.416,.351),l(.561,.731),l(.381,.647),l(.348,.21),l(1.355,.091),l(.371,.012),l(.041,-.551),l(-.028,-1.427),l(-.032,-.791),l(-.136,-.183),l(-.362,.186),l(-.448,.455),l(-.515,-.012),l(-.445,-.279),l(-.837,-.843),l(-.456,-.605),l(-.043,-.296),l(.3,-1.02),l(.377,-1.175),l(-.372,-2.088),l(.029,-.622),l(.797,-1.276),l(.154,-.198),l(.347,-.214),l(3.015,-.499),l(.563,-.068),l(.703,.641),l(.263,.21),l(.729,.265),l(1.269,.6),l(1.574,.57),l(.732,.349),l(.385,.407),l(.559,.887),l(.434,.859),l(.013,.324),l(-.183,.27),l(-.709,.344),l(-.011,.127),l(.04,.594),l(-.091,1.682),l(.08,.353),l(.216,.168),l(.511,.266),l(.072,.197),l(-.197,.241),l(-.472,.258),l(-.792,.259),l(-.146,.34),l(-.094,.764),l(-.3,.807),l(.2,.479),l(.261,.408),l(.394,.125),l(-1.264,.53),l(-1.696,.575),l(-.932,.4),l(-2.165,.578),l(-.187,.128),l(-.009,.495),l(.152,.465),l(.087,.438),l(-.505,-.096),l(-.999,.049),l(-.794,.259),l(-.636,.54),l(-.312,.539),l(-.019,.579),l(-.168,.199),l(-.285,.114),l(-.999,.062),l(-.621,.202),l(-.306,.341),l(-.777,.937),l(-.562,.738),l(-.825,.951),l(-.354,.045),l(-.803,-.165),l(-.421,-.309),l(-.334,.129),l(-.521,.286),l(-.404,.017),l(-.594,-.209),l(-.264,-.097),l(-.154,-.169),l(-.163,-.027),l(-.187,-.154),l(-.456,-.393),l(-.294,-.055),l(-1.089,-.093),l(-.086,-.099),l(-.165,-.056),l(-1.845,.364),
+N(477.231,225.874),l(-.224,1.184),l(-.107,.48),l(.252,.917),l(.177,1.017),l(.149,.408),l(.238,.268),l(.803,.588),l(1.189,1.166),l(.454,.661),l(.14,.48),l(-.155,2.346),l(-.04,.41),l(-.214,.213),l(-.432,.073),l(-.322,.017),l(-.229,.213),l(-.076,.622),l(.08,.509),l(.029,.479),l(-.096,.283),l(-.185,-.111),l(-.562,-.463),l(-.763,-1.112),l(-.484,-.548),l(-.234,-.423),l(.036,-.212),l(.499,-.61),l(.116,-.227),l(.025,-.693),l(-.1,-.96),l(-.22,-.479),l(-.261,-.056),l(-.674,.061),l(-.702,.132),l(-.27,-.211),l(-.343,-.407),l(-.382,-.549),l(-.195,-.041),l(-.394,-.125),l(-.261,-.408),l(-.2,-.479),l(.3,-.807),l(.094,-.764),l(.146,-.34),l(.792,-.259),l(.472,-.258),l(.197,-.241),l(-.072,-.197),l(-.511,-.266),l(-.216,-.168),l(-.08,-.353),l(.091,-1.682),l(-.04,-.594),l(.011,-.127),l(.709,-.344),l(.183,-.27),l(-.013,-.324),l(-.434,-.859),l(-.559,-.887),l(.223,-.016),l(1.014,.107),l(.663,.538),l(.448,.841),l(.135,.946),l(.053,.584),l(.135,.766),l(.196,.559),
+N(245.934,224.314),l(.939,.136),l(1.122,.304),l(.355,-.03),l(.946,-.824),l(.336,.026),l(.48,.025),l(.415,-.243),l(1.471,-1.109),l(.874,-.485),l(.36,-.158),l(.934,-.076),l(1.283,.021),l(.045,.748),l(-.079,.621),l(-.064,.622),l(.036,.818),l(.141,.635),l(.335,.591),l(.813,.928),l(1.1,.939),l(.316,.097),l(.787,.023),l(.355,-.03),l(.676,.25),l(.688,.307),l(.75,.603),l(.3,.098),l(.882,.037),l(.096,.014),l(.385,.774),l(.398,.308),l(.22,.084),l(1.148,-.077),l(.636,.123),l(.537,.166),l(.403,.237),l(.085,.169),l(-.038,.565),l(.203,1.029),l(.03,.706),l(.138,2.032),l(.249,.944),l(.153,.112),l(.967,.036),l(.5,.012),l(1.615,.019),l(.693,.024),l(.042,.296),l(-.261,.835),l(.067,.563),l(.436,.407),l(.73,.362),l(.316,.479),l(.307,.774),l(.022,.494),l(-.185,1.173),l(-.238,.834),l(-.38,.765),l(-.421,.666),l(-.089,-.084),l(-1.952,-.991),l(-.352,-.054),l(-.928,.02),l(-.843,-.01),l(-.126,.128),l(-1.076,.204),l(-1.104,.162),l(-.784,.202),l(-.33,.044),l(-.332,.383),l(-.698,1.105),l(-.278,.341),l(-.133,.509),l(.016,.635),l(-.385,1.188),l(-.395,1.104),l(-.149,.325),l(-.592,-.109),l(-1.33,-.077),l(-.686,.004),l(-1.034,1.784),l(-.416,-1.084),l(-.341,-.309),l(-.37,-.195),l(-.531,-.067),l(-.527,.045),l(-.901,.034),l(-.615,-.194),l(-.193,-.169),l(-.322,-.181),l(-.292,.27),l(-2.026,2.087),l(-1.047,.006),l(-.272,-.182),l(-.397,-2.144),l(-.278,-.973),l(-.212,-.563),l(-.769,-1.11),l(-.249,-.676),l(.04,-.354),l(.437,-1.555),l(-.017,-.282),l(-.761,-.744),l(-.25,-.521),l(-.193,-1.213),l(-.304,-.647),l(-.555,-.745),l(-.152,-.253),l(-.018,-.142),l(-.132,-.295),l(-.049,-.48),l(.12,-.227),l(.723,-.61),l(.285,-.439),l(-.015,-.522),l(-.604,-1.168),l(-.022,-.48),l(.159,-.34),l(.21,-.368),l(-.347,-.845),l(.102,-.452),l(.532,-.582),l(.221,-.34),l(.156,-.34),l(-.236,-.902),l(-.057,-.522),l(.143,-.848),l(.15,-.523),l(.437,-.736),l(-.08,-.24),l(-.922,-1.646),l(-1.109,-1.843),
+N(473.575,260.04),l(-1.331,.011),l(-.192,.058),l(-.068,-.382),l(-.261,-.889),l(.071,-.495),l(-.075,-.296),l(-.095,-.324),l(.03,-.806),l(.057,-1.301),l(-.072,-.763),l(-.147,-.678),l(-.33,-.944),l(-.441,-.689),l(-.181,-.946),l(-.295,-1.199),l(-.159,-.183),l(.448,-.384),l(.396,-.412),l(1.68,-1.706),l(.114,-.227),l(-.09,-.367),l(.075,-.834),l(.229,-.481),l(.736,-.683),l(.205,-.341),l(.168,-.41),l(-.594,-.845),l(-.118,-.805),l(-.113,-.494),l(.128,-.283),l(.448,-.596),l(.201,-.411),l(-.132,-.805),l(-.086,-1.144),l(-.031,-.791),l(-.178,-.818),l(-.441,-.379),l(-.515,-.224),l(-1.167,-.347),l(-1.042,-.445),l(-.658,-.223),l(-1.438,-.006),l(-.137,-.14),l(-.025,-.495),l(-.011,-.212),l(-.087,-.438),l(-.152,-.465),l(.009,-.495),l(.187,-.128),l(2.165,-.578),l(.932,-.4),l(1.696,-.575),l(1.264,-.53),l(.195,.041),l(.382,.549),l(.343,.407),l(.27,.211),l(.702,-.132),l(.674,-.061),l(.261,.056),l(.22,.479),l(.1,.96),l(-.025,.693),l(-.116,.227),l(-.499,.61),l(-.036,.212),l(.234,.423),l(.484,.548),l(.763,1.112),l(.562,.463),l(.185,.111),l(.096,-.283),l(-.029,-.479),l(-.08,-.509),l(.076,-.622),l(.229,-.213),l(.322,-.017),l(.432,-.073),l(.214,-.213),l(.04,-.41),l(.155,-2.346),l(-.14,-.48),l(-.454,-.661),l(-1.189,-1.166),l(-.803,-.588),l(-.238,-.268),l(-.149,-.408),l(-.177,-1.017),l(-.252,-.917),l(.107,-.48),l(.224,-1.184),l(.898,.018),l(1.019,.149),l(.526,-.088),l(.643,-.356),l(.123,-.128),l(.229,.083),l(.323,.394),l(.512,.294),l(.963,-.02),l(.964,-.006),l(.42,.054),l(.396,-.144),l(.543,-.766),l(.21,-.326),l(.276,.083),l(.646,.42),l(.448,-.031),l(1.243,-.559),l(1.218,-.402),l(.515,-.342),l(1.157,-.836),l(.128,.167),l(.212,.479),l(-.185,.579),l(-.302,.453),l(-.198,.255),l(.181,.451),l(.129,.72),l(-.012,.466),l(.182,1.115),l(-.101,.58),l(-.258,.325),l(.374,.705),l(.154,.494),l(-.006,1.115),l(-.004,.819),l(.043,.184),l(.185,.127),l(.327,.084),l(.015,.269),l(-.165,.494),l(-.563,.58),l(.184,.381),l(-.08,.283),l(-.418,.565),l(-.802,.906),l(-.512,.622),l(-.72,.651),l(-1.36,.751),l(-1.48,.653),l(-.73,.228),l(-1.308,.582),l(-.852,.637),l(-1.286,1.443),l(-.886,.85),l(-1.193,.878),l(-1.181,.836),l(-.268,.128),l(-.035,.96),l(-.083,.495),l(.058,.127),l(.719,.535),l(.188,.381),l(-.166,.452),l(-.085,.184),l(.461,1.511),l(.071,.564),l(.06,.155),l(.246,.014),l(.171,-.128),l(.141,-.085),l(.043,.607),l(-.234,2.218),l(-.284,.82),l(.325,.196),l(.152,.057),l(-.025,.325),l(-.157,.311),l(-.516,.566),l(-.699,.538),l(-.664,.34),l(-1.266,.412),l(-.796,.312),l(-.688,.228),l(-.895,.524),l(-.652,.665),l(-.337,.51),l(.292,.338),l(.589,.338),l(.045,.325),l(-.149,1.022),
+N(499.85,236.166),l(.544,-.071),l(.622,-.369),l(.18,.028),l(.346,.098),l(.269,-.085),l(.396,-.368),l(.911,-.143),l(.311,.281),l(.305,-.028),l(.101,-.185),l(-.171,-.366),l(.771,-.539),l(.423,-.198),l(.322,.226),l(.389,-.213),l(-.308,-.494),l(.207,-.282),l(.505,-.425),l(.229,.296),l(.229,.056),l(.558,-.594),l(-.158,-.197),l(-.253,-.409),l(.094,-.297),l(.243,.014),l(.27,-.071),l(.172,-.34),l(-.297,-.875),l(.06,-.339),l(.255,-.043),l(.117,.07),l(.253,.438),l(.28,.099),l(.2,-.41),l(.692,-.524),l(.235,-.367),l(.134,-.452),l(.168,-.692),l(-.133,-.354),l(.003,-.226),l(.537,-.468),l(.356,.324),l(.455,.648),l(.612,.281),l(.141,.198),l(.213,.847),l(.294,1.821),l(.093,.663),l(.231,.791),l(.391,.733),l(.163,.466),l(-.038,.367),l(-.069,.155),l(-.058,.099),l(-.537,.807),l(-.22,-.127),l(-.189,-.366),l(-.555,-.748),l(-.297,.143),l(-.05,.424),l(.193,.875),l(.396,.521),l(.079,.396),l(-.307,.636),l(-.746,1.005),l(-.045,.452),l(.041,.89),l(-.18,.946),l(-.709,2.12),l(-.825,2.572),l(-1.254,3.788),l(-1.324,4.48),l(-.518,1.568),l(-.188,.255),l(-.508,.637),l(-.2,.113),l(-1.369,.102),l(-.999,.327),l(-.474,.468),l(-.813,.086),l(-.363,-.465),l(-.196,-.142),l(-.546,-.182),l(-.37,.071),l(-.269,-.057),l(-.863,-.718),l(-.104,-.24),l(-.02,-.565),l(-.104,-.239),l(-.46,-.366),l(-.124,-.282),l(.001,-.721),l(.345,-1.088),l(-.094,-.325),l(-.287,-.479),l(-.62,-.931),l(-.189,-.494),l(.075,-.664),l(.391,-1.37),l(.228,-.213),l(.474,-.185),l(.768,-1.371),l(.686,-1.174),l(.104,-.325),l(.151,-1.103),l(-.11,-.353),l(-.278,-.226),l(-.354,-.366),l(.066,-.184),l(.252,-.509),l(-.521,-.861),l(-.117,-.677),l(-.069,-.494),l(-.231,-.721),l(.024,-.112),l(.517,-.693),l(.362,-.594),l(.163,-.438),l(.007,-1.073),l(.484,-.016),
+N(468.138,234.908),l(.011,.212),l(.025,.495),l(.137,.14),l(1.438,.006),l(.658,.223),l(1.042,.445),l(1.167,.347),l(.515,.224),l(.441,.379),l(.178,.818),l(.031,.791),l(.086,1.144),l(.132,.805),l(-.201,.411),l(-.448,.596),l(-.128,.283),l(.113,.494),l(.118,.805),l(.594,.845),l(-.168,.41),l(-.205,.341),l(-.736,.683),l(-.229,.481),l(-.075,.834),l(.09,.367),l(-.114,.227),l(-1.68,1.706),l(-.396,.412),l(-.448,.384),l(-.342,-.225),l(-.547,-.124),l(-.442,-.025),l(-.529,.145),l(-.31,.016),l(-.99,-.403),l(-.597,-.139),l(-.72,-.023),l(-.067,-.042),l(-.186,-.098),l(-.306,-.451),l(-.479,-.35),l(-.549,-.167),l(-.938,-.136),l(-.352,-.153),l(-.524,-.873),l(-.163,-.564),l(.032,-.565),l(-.127,-.239),l(-.78,.019),l(-.201,-.098),l(-.109,-.211),l(.051,-.537),l(-.106,-.169),l(-.552,-.266),l(-.533,-.223),l(-.57,-.321),l(-.563,-.491),l(-.377,-.662),l(-.246,-.96),l(-.469,-.604),l(-.43,-.478),l(-.267,-.451),l(.103,-.227),l(.594,.209),l(.404,-.017),l(.521,-.286),l(.334,-.129),l(.421,.309),l(.803,.165),l(.354,-.045),l(.825,-.951),l(.562,-.738),l(.777,-.937),l(.306,-.341),l(.621,-.202),l(.999,-.062),l(.285,-.114),l(.168,-.199),l(.019,-.579),l(.312,-.539),l(.636,-.54),l(.794,-.259),l(.999,-.049),l(.505,.096),
+N(444.673,255.519),l(-.006,3.434),l(.031,1.894),l(.025,2.246),l(-.057,.205),l(-.454,.318),l(-.545,.302),l(-.581,.498),l(-.427,.034),l(-.581,-.166),l(-.745,-.042),l(-.892,.048),l(-.517,-.039),l(-.296,-.212),l(-.055,-.528),l(-.042,-.345),l(-.193,-.222),l(-.637,-.348),l(-.329,-.127),l(-.335,.116),l(-.109,.217),l(-.317,.416),l(-.584,.27),l(-.152,.068),l(-.458,-.491),l(-1.041,-1.001),l(-.458,-.606),l(-.359,-1.03),l(-.345,-.72),l(-.136,-.493),l(.12,-.269),l(.053,-.34),l(-.458,-.719),l(-.231,-.861),l(.148,-.861),l(-.002,-.48),l(-.537,-1.326),l(-.496,-2.244),l(.772,-.02),l(.04,-.678),l(-.077,-.749),l(-.456,-.006),l(.016,-.06),l(-.099,-.522),l(-.26,-.508),l(-1.018,-1.283),l(-.343,-.55),l(-1.102,-2.158),l(-.841,-1.623),l(-.9,-1.509),l(-.988,-1.269),l(-.511,-1.044),l(-.122,-.396),l(-.045,-.551),l(.015,-.578),l(.248,-.197),l(.352,-.144),l(.758,.462),l(.778,-.513),l(.591,-.286),l(.499,-.088),l(.294,.083),l(.45,.478),l(.463,.309),l(.636,.151),l(2.201,-.013),l(1.296,.006),l(.631,.011),l(1.636,.019),l(2.59,-.016),l(.989,.023),l(.408,.096),l(.305,.366),l(.401,.322),l(.622,.151),l(.697,.024),l(2.024,-.026),l(.349,.21),l(.408,.083),l(1.164,-.063),l(.717,.08),l(1.044,-.203),l(1.96,-.379),l(.93,-.218),l(1.845,-.364),l(.165,.056),l(.086,.099),l(1.089,.093),l(.294,.055),l(.456,.393),l(-.811,.315),l(-.891,.02),l(-.284,.143),l(-.993,.938),l(-.209,.029),l(-.62,-.773),l(-1.048,.134),l(-2.962,.47),l(-1.183,.021),l(.005,1.215),l(-.007,1.286),l(-.025,.876),l(-.043,1.201),l(.002,3.561),l(-.586,.046),l(-1.564,.052),l(-.146,.028),l(-.106,2.657),l(-.009,1.201),l(.013,1.624),l(.007,.806),
+N(248.453,316.576),l(-.306,.101),l(-.892,-.087),l(-.538,-.293),l(-.236,-.015),l(-.311,.163),l(-.418,.398),l(-.498,.192),l(-1.156,.091),l(-.349,.09),l(-.358,.207),l(-.267,.621),l(-.114,.341),l(.06,.532),l(-.163,.622),l(-.104,.148),l(-.453,.031),l(-.534,.104),l(-.956,-.413),l(.667,-.639),l(.326,-.444),l(.582,-.4),l(.025,-.147),l(-.372,-.177),l(-.273,-.117),l(-1.353,.534),l(-1.01,-.013),l(-.545,.163),l(-.202,-.339),l(.128,-.192),l(.959,-.268),l(.266,.028),l(.792,-.208),l(.441,-.118),l(-.605,-.162),l(-.582,.002),l(-.77,.001),l(-.014,-.413),l(.265,-.31),l(-.007,-.191),l(-.446,-.073),l(-.356,-.44),l(-.66,.384),l(-.669,-.175),l(.292,-.53),l(.041,-.177),l(-.378,.045),l(-.361,.147),l(-.416,-.396),l(-.215,-.117),l(.413,-.279),l(.114,-.177),l(-.091,-.278),l(-.053,-.073),l(-.351,.03),l(-.773,-.424),l(-.135,-.059),l(.844,-.192),l(.253,-.161),l(.1,-.294),l(.396,-.366),l(.049,-.234),l(-.641,.06),l(-.257,.104),l(-.312,-.073),l(-.256,-.672),l(.573,-.395),l(-.565,-.378),l(-.12,-.421),l(.757,-.452),l(-.14,-.421),l(-.686,.422),l(-.091,-1.523),l(.399,-.596),l(-.185,-.825),l(.013,-.218),l(.593,.014),l(.41,.245),l(.711,.071),l(.171,-.246),l(.002,-.159),l(-.896,-.447),l(-.867,.146),l(-.317,-.173),l(-.536,.059),l(-.017,-.231),l(.339,-.333),l(.025,-.246),l(-.067,-.087),l(.186,-.202),l(.536,.014),l(.229,-.377),l(.01,-.216),l(-.722,-.389),l(-.354,-.129),l(-.886,.045),l(-.332,-.101),l(-.024,-.49),l(-.939,.16),l(-.115,-.101),l(.122,-.145),l(1.032,-.521),l(.251,-.116),l(.4,-.404),l(.266,-.389),l(.833,-.06),l(.268,.201),l(.059,.346),l(-.648,.202),l(-.323,.274),l(.11,.505),l(.117,.058),l(.191,-.102),l(.268,-.39),l(.183,-.087),l(.242,.101),l(-.037,.317),l(.057,.504),l(.886,-.996),l(.161,-.678),l(.056,-.647),l(.237,-.375),l(.079,-.058),l(.631,-.217),l(-.201,-.071),l(-.438,-.143),l(-.056,-.158),l(.101,-.273),l(.246,-.072),l(.571,-.245),l(.599,-.431),l(.271,-.459),l(-.061,-.229),l(-.394,-.157),l(-.662,-.399),l(-.053,-.372),l(.139,-.243),l(.105,-.458),l(-.06,-.828),l(.366,-.33),l(.676,-.272),l(-.431,-.585),l(-.053,-.784),l(.133,-.158),l(.554,-.157),l(.054,-.314),l(-.116,-.285),l(-.317,-.085),l(-.272,-.198),l(.233,-.329),l(.087,-.313),l(-.401,-.185),l(-.274,-.014),l(-.161,.101),l(-.476,.414),l(-.548,.058),l(-.087,.001),l(-.289,-.199),l(-.16,-.484),l(-.399,-.726),l(-.133,-.697),l(.188,-.911),l(.137,-.413),l(.722,-.739),l(.535,-.767),l(-.006,-.326),l(-.544,-1.757),l(.001,-.608),l(.088,-.567),l(-.076,-.438),l(-.528,-.891),l(-.04,-.298),l(.236,-.198),l(.499,.098),l(.182,-.085),l(.2,-.142),l(.097,-.143),l(.41,-1.288),l(.252,-.481),l(.304,-.935),l(.18,-.65),l(.664,-.808),l(.363,-.722),l(.201,-.636),l(.252,-.946),l(.311,-.691),l(.187,-.128),l(.273,-.382),l(.013,-.296),l(-.312,-.847),l(.082,-.184),l(.455,-.452),l(.206,-.339),l(.028,-.24),l(-.093,-.226),l(-.166,-.805),l(-.292,-2.088),l(-.098,-.86),l(.031,-.565),l(.412,-.565),l(.37,-.537),l(.207,-.564),l(.007,-.734),l(-.339,-.521),l(-.098,-.409),l(.295,-.96),l(.218,-.941),l(.127,-.556),l(.461,-.594),l(.171,-.918),l(.243,-.975),l(.126,-.805),l(.082,-.565),l(-.063,-1.087),l(.422,-.664),l(.211,-.494),l(-.221,-.932),l(-.048,-.833),l(.148,-.24),l(-.022,-.917),l(.229,-.607),l(-.124,-.297),l(-.365,-.084),l(.06,-.324),l(-.046,-.396),l(.223,-.198),l(.402,-.198),l(.137,-.424),l(-.008,-.819),l(.093,-.593),l(.182,-.918),l(-.004,-2.344),l(-.172,-2.074),l(-.042,-1.539),l(-.194,-.974),l(-.15,-.387),l(.852,-.275),l(.259,-.298),l(.228,-.933),l(.15,-.199),l(.586,-.187),l(.152,.253),l(.555,.745),l(.304,.647),l(.193,1.213),l(.25,.521),l(.761,.744),l(.017,.282),l(-.437,1.555),l(-.04,.354),l(.249,.676),l(.769,1.11),l(.212,.563),l(.278,.973),l(.397,2.144),l(.272,.182),l(1.047,-.006),l(.107,.056),l(.212,.14),l(.161,.154),l(-.093,.636),l(-.541,1.457),l(-.36,.256),l(-1.346,.53),l(-.819,.372),l(-.204,.312),l(.25,.817),l(-.421,.722),l(-.007,.579),l(.113,.437),l(.34,.449),l(.03,.226),l(-.273,.369),l(-.161,.382),l(.114,.507),l(.53,.477),l(.011,.227),l(-.152,.311),l(-.333,.017),l(-.791,.089),l(-.329,.835),l(-.464,.835),l(-.608,.694),l(-.282,.439),l(-.823,1.839),l(.167,1.466),l(.054,.762),l(-.124,.185),l(-.492,.271),l(-.292,.34),l(-.388,1.201),l(-.156,.961),l(.285,.633),l(.408,1.154),l(.784,2.816),l(.055,.69),l(-.075,.41),l(-1.083,1.854),l(-.319,.595),l(.088,.409),l(.189,1.06),l(-.078,.325),l(-1.334,1.11),l(-.231,.312),l(-.142,1.075),l(.014,1.16),l(.249,1.103),l(.437,.932),l(-.202,.143),l(-.951,.529),l(-.126,.17),l(-.053,.312),l(-.511,2.427),l(-.316,.541),l(-.1,.37),l(.123,1.835),l(.231,.867),l(.012,.427),l(-.596,.317),l(-.172,.172),l(-.106,.271),l(.094,1.226),l(.125,.128),l(.555,.111),l(.088,.655),l(-.191,1.458),l(.252,.585),l(.26,.185),l(.789,.11),l(.302,.17),l(-.007,.186),l(-.245,.202),l(-.322,.13),l(-.726,.033),l(-.757,.146),l(.176,.171),l(.586,.298),l(.552,.385),l(.017,.216),l(-.767,.794),l(-.059,1.094),l(.158,1.035),l(-.216,.896),l(-.212,.434),l(-.226,.262),l(-.598,.161),l(-.28,.219),l(-.249,.781),l(.446,.648),l(-.069,.188),l(-.296,1.218),l(-.307,.263),l(-1.729,1.01),l(-.271,.292),l(-.037,.45),l(.28,1.309),l(.508,1.123),l(.218,.043),l(.961,-.283),l(.654,-.121),l(.187,.248),l(.231,2.285),l(.778,.568),l(.669,.041),l(1.41,-.052),l(2.827,.132),l(.841,.217),l(1.385,.36),l(.286,.039),M(236.642,296.773),l(-.394,-.113),l(-.43,-.028),l(-.21,-.171),l(-.133,-.229),l(.21,-.457),l(.15,-.657),l(-.087,-.514),l(.011,-.414),l(.364,-.728),l(.817,-.116),l(.36,.327),l(.044,.328),l(-.688,.443),l(-.146,.229),l(.493,.771),l(-.194,1.058),l(-.167,.271),M(238.177,317.937),l(-.445,-.177),l(.083,-.842),l(-.849,.075),l(-.073,-.368),l(.218,-.354),l(.823,.102),l(.508,-.207),l(.205,.103),l(.054,.812),l(-.267,.34),l(-.257,.518),M(247.801,322.062),l(-1.033,.102),l(-.467,-.118),l(-.55,-.237),l(-.42,.001),l(-.481,.104),l(-.935,.226),l(-.496,.03),l(.125,-.343),l(.202,-.312),l(-.104,-.312),l(.906,-.15),l(1.434,.058),l(.433,.323),l(.706,-.007),l(-.622,-.689),l(-.307,-.163),l(-.56,-.117),l(-.178,-.089),l(-.188,.03),l(-.338,-.341),l(-.229,-.4),l(1.611,-.581),l(-.044,-.296),l(-.165,-.147),l(-1.819,.285),l(-.292,-.222),l(.263,-.474),l(.146,-.163),l(1.074,-.52),l(.868,-.637),l(.414,.28),l(1.153,.062),l(-.008,1.144),l(-.098,3.675),
+N(456.133,239.67),l(.187,.154),l(.163,.027),l(.154,.169),l(.264,.097),l(-.103,.227),l(.267,.451),l(.43,.478),l(.469,.604),l(.246,.96),l(.377,.662),l(.563,.491),l(.57,.321),l(.533,.223),l(.552,.266),l(.106,.169),l(-.051,.537),l(.109,.211),l(.201,.098),l(.78,-.019),l(.127,.239),l(-.032,.565),l(.163,.564),l(.524,.873),l(.352,.153),l(.938,.136),l(.549,.167),l(.479,.35),l(.306,.451),l(.186,.098),l(-.317,.411),l(-.388,.327),l(-.507,.243),l(-.747,.075),l(-.304,.115),l(-.7,.823),l(-.586,.583),l(-.362,.229),l(-.747,.357),l(-.388,.355),l(-.107,.636),l(-.222,.666),l(-.247,.241),l(-.634,.357),l(-.98,.33),l(-.249,.214),l(-.217,.708),l(-.345,.963),l(-.288,.354),l(-.237,.129),l(-.584,.116),l(-.43,-.026),l(-.473,.06),l(-.511,-.011),l(-.819,-.193),l(-.744,-.32),l(-.979,-.645),l(-.545,-.039),l(-.333,.186),l(-.084,.24),l(-.585,1.134),l(-.382,.469),l(-.651,.625),l(-.632,.428),l(-.8,.372),l(-.823,.033),l(-.854,.047),l(-.368,-.097),l(-.11,-.183),l(.003,-.48),l(.243,-.609),l(.166,-.538),l(-.21,-.762),l(-.547,-.943),l(-.716,-.787),l(-.528,-.067),l(-.007,-.806),l(-.013,-1.624),l(.009,-1.201),l(.106,-2.657),l(.146,-.028),l(1.564,-.052),l(.586,-.046),l(-.002,-3.561),l(.043,-1.201),l(.025,-.876),l(.007,-1.286),l(-.005,-1.215),l(1.183,-.021),l(2.962,-.47),l(1.048,-.134),l(.62,.773),l(.209,-.029),l(.993,-.938),l(.284,-.143),l(.891,-.02),l(.811,-.315),
+N(279.288,257.295),l(.02,.239),l(-.544,1.57),l(-.375,.468),l(-1.007,.74),l(-.301,.27),l(-.352,.51),l(-.609,-.363),l(-.35,-.097),l(-.235,.029),l(-.387,.172),l(-.745,.131),l(-.71,.005),l(-.564,-.096),l(-.992,-.333),l(-.607,-.025),l(-1.187,.332),l(-.19,-.056),l(.064,-.212),l(.425,-.426),l(.486,-.398),l(.11,-.198),l(-.21,-.619),l(.048,-.227),l(.625,-.851),l(.617,-1.203),l(.018,-.268),l(-.939,-.503),l(-.65,-.18),l(-1.448,-.697),l(-1.632,-1.106),l(-.671,-.307),l(-1.173,-.204),l(-.498,-.237),l(-.835,-.588),l(-.576,-.562),l(-1.271,-1.376),l(-.782,-.913),l(-.225,-.337),l(-.19,-.056),l(.149,-.325),l(.395,-1.104),l(.385,-1.188),l(-.016,-.635),l(.133,-.509),l(.278,-.341),l(.698,-1.105),l(.332,-.383),l(.33,-.044),l(.784,-.202),l(1.104,-.162),l(1.076,-.204),l(.126,-.128),l(.843,.01),l(.928,-.02),l(.352,.054),l(1.952,.991),l(.089,.084),l(.253,.408),l(.084,.663),l(.521,.872),l(.104,.959),l(-.132,.862),l(-.086,.721),l(-.006,.72),l(.372,.04),l(.818,.15),l(.925,.221),l(.346,-.03),l(.709,-.413),l(.115,-.001),l(.724,.278),l(.843,.404),l(.121,.437),l(.487,2.524),l(.254,.563),l(.224,.055),l(1.29,-.445),l(.234,.112),l(.491,.336),l(.019,.141),l(-.321,.75),l(-.298,.835),l(-.222,.819),l(-.027,.777),l(.063,.423),
+N(444.673,255.519),l(.528,.067),l(.716,.787),l(.547,.943),l(.21,.762),l(-.166,.538),l(-.243,.609),l(-.003,.48),l(.11,.183),l(.368,.097),l(.854,-.047),l(.823,-.033),l(.8,-.372),l(.632,-.428),l(.651,-.625),l(.382,-.469),l(.585,-1.134),l(.084,-.24),l(.333,-.186),l(.545,.039),l(.979,.645),l(.744,.32),l(.819,.193),l(.511,.011),l(.473,-.06),l(.43,.026),l(.584,-.116),l(.237,-.129),l(.288,-.354),l(.345,-.963),l(.217,-.708),l(.249,-.214),l(.98,-.33),l(.634,-.357),l(.247,-.241),l(.222,-.666),l(.107,-.636),l(.388,-.355),l(.747,-.357),l(.362,-.229),l(.586,-.583),l(.7,-.823),l(.304,-.115),l(.747,-.075),l(.507,-.243),l(.388,-.327),l(.317,-.411),l(.067,.042),l(.72,.023),l(.597,.139),l(.99,.403),l(.31,-.016),l(.529,-.145),l(.442,.025),l(.547,.124),l(.342,.225),l(.159,.183),l(.295,1.199),l(.181,.946),l(.441,.689),l(.33,.944),l(.147,.678),l(.072,.763),l(-.057,1.301),l(-.03,.806),l(.095,.324),l(-1.449,-.585),l(-.279,.199),l(-.657,.979),l(-.28,.567),l(-.005,.325),l(.39,.676),l(.307,.465),l(.458,.322),l(.671,.109),l(.595,-.004),l(.076,-.594),l(.064,-.198),l(.312,-.186),l(.131,-.015),l(.192,-.058),l(1.331,-.011),l(-.182,1.195),l(-.352,.849),l(-.182,.184),l(-.404,.1),l(-.093,.24),l(.199,.536),l(-.104,.467),l(-.248,.354),l(-.569,.453),l(-.923,.581),l(-.591,.75),l(-1.383,1.98),l(-.631,.834),l(-1.242,1.373),l(-1.193,1.062),l(-.829,.863),l(-1.434,1.034),l(-1.379,1.091),l(-.552,.382),l(-.989,.638),l(-.676,.298),l(-.782,.101),l(-.98,-.012),l(-.549,.071),l(-.132,.354),l(.013,.127),l(-.109,.085),l(-.449,-.098),l(-.553,-.126),l(-.303,.015),l(-.25,.198),l(-.272,.312),l(-.226,.113),l(-.36,-.056),l(-.768,-.408),l(-.759,-.168),l(-.542,-.013),l(-.31,.113),l(-.38,.27),l(-.482,-.099),l(-.645,-.21),l(-.295,.1),l(-.638,.043),l(-.589,.214),l(-.729,.538),l(-.72,.086),l(-.44,-.013),l(-.667,-.084),l(-.738,.072),l(-.575,.199),l(-.827,.82),l(-1.251,-.576),l(-.092,-.509),l(-.218,-.183),l(-.479,-.056),l(-.28,.085),l(-.257,-.479),l(-.343,-.056),l(-.149,.255),l(-.035,.197),l(-.312,-.112),l(-.119,-.226),l(.267,-.495),l(.103,-.424),l(-.857,-1.354),l(-.323,-.282),l(-.134,-.226),l(.173,-.17),l(.636,-.044),l(.172,-.424),l(.06,-.819),l(-.163,-.663),l(-.186,-.522),l(-.61,-1.143),l(-.75,-1.029),l(-.472,-.903),l(-.612,-1.383),l(-.646,-1.467),l(-.573,-.818),l(-.436,-.467),l(.152,-.068),l(.584,-.27),l(.317,-.416),l(.109,-.217),l(.335,-.116),l(.329,.127),l(.637,.348),l(.193,.222),l(.042,.345),l(.055,.528),l(.296,.212),l(.517,.039),l(.892,-.048),l(.745,.042),l(.581,.166),l(.427,-.034),l(.581,-.498),l(.545,-.302),l(.454,-.318),l(.057,-.205),l(-.025,-2.246),l(-.031,-1.894),l(.006,-3.434),M(462.462,268.501),l(.412,-.044),l(.194,-.553),l(.633,-.343),l(1.035,-.303),l(.263,-.199),l(.582,-1.007),l(.268,-.326),l(.143,-.241),l(-.104,-.226),l(-.967,-.744),l(-.33,-.337),l(-.422,-.266),l(-.308,.086),l(-.995,.359),l(-.65,.329),l(-.513,.567),l(-.275,.44),l(-.691,.611),l(.12,.409),l(.582,.858),l(.285,.366),l(.739,.561),M(432.955,250.661),l(.456,.006),l(.077,.749),l(-.04,.678),l(-.772,.02),l(-.078,-.354),l(-.028,-.396),l(.285,-.297),l(.101,-.406),
+N(471.719,258.047),l(.075,.296),l(-.071,.495),l(.261,.889),l(.068,.382),l(-.131,.015),l(-.312,.186),l(-.064,.198),l(-.076,.594),l(-.595,.004),l(-.671,-.109),l(-.458,-.322),l(-.307,-.465),l(-.39,-.676),l(.005,-.325),l(.28,-.567),l(.657,-.979),l(.279,-.199),l(1.449,.585),
+N(462.462,268.501),l(-.739,-.561),l(-.285,-.366),l(-.582,-.858),l(-.12,-.409),l(.691,-.611),l(.275,-.44),l(.513,-.567),l(.65,-.329),l(.995,-.359),l(.308,-.086),l(.422,.266),l(.33,.337),l(.967,.744),l(.104,.226),l(-.143,.241),l(-.268,.326),l(-.582,1.007),l(-.263,.199),l(-1.035,.303),l(-.633,.343),l(-.194,.553),l(-.412,.044),
+N(790.15,283.022),l(.738,.197),l(.008,-.227),l(-.242,-.524),l(.052,-.284),l(.233,.014),l(.389,.17),l(.37,.751),l(.277,.964),l(.48,.17),l(1.753,.691),l(.506,.113),l(.37,-.072),l(.699,-.483),l(.885,-.343),l(.4,.027),l(.329,.17),l(.066,.454),l(-.022,.198),l(-.402,1.236),l(-.283,.072),l(-.761,.058),l(-.035,.683),l(-.124,.156),l(-.424,.029),l(-.746,.016),l(-.432,.2),l(-.271,.284),l(.041,.384),l(.254,.525),l(-.002,.213),l(-.151,.199),l(-.646,.515),l(-.898,1.129),l(-.847,1.058),l(-.756,.587),l(-.68,.316),l(-.337,-.171),l(-.47,-.313),l(-.237,-.328),l(.056,-.314),l(.288,-.386),l(.307,-.671),l(.398,-.5),l(-.031,-.343),l(-.271,-.128),l(-.761,-.582),l(-.421,-.185),l(-.593,-.184),l(-.98,-.452),l(-.306,-.256),l(-.11,-.17),l(.081,-.128),l(.419,-.157),l(1.389,-.685),l(.209,-.512),l(-.078,-.695),l(.087,-.312),l(.396,-.441),l(.032,-.383),l(-.482,-.837),l(.081,-.567),l(-.156,-.311),l(-.479,-.655),l(-.574,-.678),l(.102,-.164),l(-.145,-.304),l(-.291,-.351),l(-.336,-.188),l(-.29,-.163),l(.117,.233),l(.497,.515),l(.049,.141),l(-.169,0),l(-.211,-.281),l(-.525,-.631),l(-.622,-.771),l(-.518,-.561),l(.001,-.117),l(-.268,-.257),l(.04,-.141),l(.013,-.14),l(-.048,-.188),l(-.197,-.396),l(-.379,-.42),l(-.347,-.257),l(.163,-.046),l(.205,.093),l(.358,-.047),l(.131,-.093),l(.084,.28),l(-.149,.187),l(.186,.303),l(.177,.21),l(.167,.116),l(.228,.164),l(.041,-.141),l(.269,.023),l(.519,.257),l(.42,.117),l(.274,.07),l(.128,.257),l(-.011,.141),l(.185,.023),l(.146,-.188),l(.185,.023),l(-.022,.164),l(.227,.351),l(.249,.187),l(.233,.28),l(-.18,.023),l(-.076,.164),l(.093,.163),l(-.242,-.023),l(-.175,-.047),l(.143,.117),l(.251,.188),l(.23,.233),l(.352,.28),l(.063,.234),l(.019,.21),l(-.261,-.047),l(.096,.164),l(.239,.351),l(.256,.188),l(-.292,.023),l(-.226,0),l(-.205,-.047),l(-.006,.141),l(.306,.14),l(.324,.164),l(-.09,.211),l(.205,.046),l(.265,-.023),l(.226,0),l(.223,.141),l(-.114,.07),l(-.031,.141),l(.025,.141),l(.136,.06),M(782.939,297.694),l(-.088,.158),l(-.558,.13),l(-.309,.288),l(-.322,.101),l(-.246,.244),l(-.692,-.242),l(-.16,.086),l(.15,.216),l(.429,.415),l(-.141,.173),l(.02,.259),l(-.064,.431),l(-.218,-.071),l(-.976,-.099),l(.418,.229),l(.449,.244),l(-.278,.49),l(-.427,.896),l(-.212,.549),l(-.418,.318),l(-.673,.349),l(-.171,.246),l(-.259,.145),l(-.581,.233),l(-.593,.406),l(-.398,.015),l(-1.156,-.258),l(-.628,.112),l(-.585,-.442),l(-.812,-.158),l(-.373,-.066),l(-.162,-.308),l(-.467,-.098),l(-.24,.142),l(-.062,.168),l(-.78,.095),l(-.214,-.166),l(-.515,-.095),l(-.146,-.286),l(.432,-.089),l(-.223,-.216),l(.328,-.116),l(.322,-.001),l(-.452,-.482),l(.82,.266),l(-.464,-.576),l(.121,-.145),l(.946,.156),l(.082,-.13),l(-.141,-.173),l(-.201,-.216),l(-.06,-.288),l(.283,-.303),l(.569,-.246),l(.328,-.374),l(.561,-.375),l(.102,-.302),l(.998,-.575),l(1.106,-.275),l(.713,-.331),l(.544,-.36),l(.377,-.101),l(.685,-.575),l(.066,-.272),l(.48,-.302),l(.373,-.015),l(.787,-.331),l(.664,-.402),l(.126,-.215),l(-.008,-.172),l(.266,-.144),l(.448,-.302),l(-.109,-.501),l(.076,-.214),l(.166,-.44),l(.306,.048),l(.066,-.152),l(.58,-.259),l(.444,-.272),l(.137,-.285),l(.131,-1.187),l(.512,-.647),l(.372,.047),l(.365,.165),l(.032,.259),l(.337,.06),l(.187,.186),l(.231,.799),l(.312,.242),l(.973,-.645),l(.426,-.029),l(.367,.113),l(.222,.5),l(-.197,.399),l(.299,.429),l(.066,.271),l(-.611,.659),l(-.261,.401),l(-.476,.358),l(-.868,.746),l(-.578,.359),l(-.295,.13),l(-.236,.258),l(-.389,.159),l(-.271,.258),l(.416,.407),l(.428,.047),l(.421,.289),l(-.276,.113),l(-.484,.07),l(-.503,-.296),l(-.488,.131),l(-.352,.158),
+N(247.899,318.387),l(.008,-1.144),l(.821,.289),l(.06,.206),l(-.354,.312),l(-.534,.337),M(248.453,316.576),l(-.286,-.039),l(-1.385,-.36),l(-.841,-.217),l(-2.827,-.132),l(-1.41,.052),l(-.669,-.041),l(-.778,-.568),l(-.231,-2.285),l(-.187,-.248),l(-.654,.121),l(-.961,.283),l(-.218,-.043),l(-.508,-1.123),l(-.28,-1.309),l(.037,-.45),l(.271,-.292),l(1.729,-1.01),l(.307,-.263),l(.296,-1.218),l(.069,-.188),l(-.446,-.648),l(.249,-.781),l(.28,-.219),l(.598,-.161),l(.226,-.262),l(.212,-.434),l(.216,-.896),l(-.158,-1.035),l(.059,-1.094),l(.767,-.794),l(-.017,-.216),l(-.552,-.385),l(-.586,-.298),l(-.176,-.171),l(.757,-.146),l(.726,-.033),l(.322,-.13),l(.245,-.202),l(.007,-.186),l(-.302,-.17),l(-.789,-.11),l(-.26,-.185),l(-.252,-.585),l(.191,-1.458),l(-.088,-.655),l(-.555,-.111),l(-.125,-.128),l(-.094,-1.226),l(.106,-.271),l(.172,-.172),l(.596,-.317),l(-.012,-.427),l(-.231,-.867),l(-.123,-1.835),l(.1,-.37),l(.316,-.541),l(.511,-2.427),l(.053,-.312),l(.126,-.17),l(.951,-.529),l(.202,-.143),l(-.437,-.932),l(-.249,-1.103),l(-.014,-1.16),l(.142,-1.075),l(.231,-.312),l(1.334,-1.11),l(.078,-.325),l(-.189,-1.06),l(-.088,-.409),l(.319,-.595),l(1.083,-1.854),l(.075,-.41),l(-.055,-.69),l(-.784,-2.816),l(-.408,-1.154),l(-.285,-.633),l(.156,-.961),l(.388,-1.201),l(.292,-.34),l(.492,-.271),l(.124,-.185),l(-.054,-.762),l(-.167,-1.466),l(.823,-1.839),l(.282,-.439),l(.608,-.694),l(.464,-.835),l(.329,-.835),l(.791,-.089),l(.333,-.017),l(.152,-.311),l(-.011,-.227),l(-.53,-.477),l(-.114,-.507),l(.161,-.382),l(.273,-.369),l(-.03,-.226),l(-.34,-.449),l(-.113,-.437),l(.007,-.579),l(.421,-.722),l(-.25,-.817),l(.204,-.312),l(.819,-.372),l(1.346,-.53),l(.36,-.256),l(.541,-1.457),l(.093,-.636),l(-.161,-.154),l(-.212,-.14),l(-.107,-.056),l(2.026,-2.087),l(.292,-.27),l(.322,.181),l(.193,.169),l(.615,.194),l(.901,-.034),l(.527,-.045),l(.531,.067),l(.37,.195),l(.341,.309),l(.416,1.084),l(1.034,-1.784),l(.686,-.004),l(1.33,.077),l(.592,.109),l(.19,.056),l(.225,.337),l(.782,.913),l(1.271,1.376),l(.576,.562),l(.835,.588),l(.498,.237),l(1.173,.204),l(.671,.307),l(1.632,1.106),l(1.448,.697),l(.65,.18),l(.939,.503),l(-.018,.268),l(-.617,1.203),l(-.625,.851),l(-.048,.227),l(.21,.619),l(-.11,.198),l(-.486,.398),l(-.425,.426),l(-.064,.212),l(.19,.056),l(1.187,-.332),l(.607,.025),l(.992,.333),l(.564,.096),l(.71,-.005),l(.745,-.131),l(.387,-.172),l(.235,-.029),l(.35,.097),l(.609,.363),l(.352,-.51),l(.301,-.27),l(1.007,-.74),l(.375,-.468),l(.544,-1.57),l(-.02,-.239),l(.957,-.161),l(.462,-.017),l(.206,.196),l(.517,1.154),l(-.094,1.638),l(-.161,.467),l(-.521,.313),l(-1.754,.744),l(-.348,.242),l(-1.633,1.448),l(-1.435,1.363),l(-1.805,1.816),l(-.833,.88),l(-.214,.27),l(-.443,.524),l(-.065,.452),l(-.595,2.359),l(-.103,.522),l(.049,.847),l(.168,.986),l(-.118,.325),l(-.48,.524),l(-.24,.495),l(-.011,.522),l(.194,.577),l(-.054,.338),l(-.162,.273),l(-.26,.325),l(.015,.226),l(.924,.831),l(.68,.281),l(.715,.281),l(.283,.169),l(.281,.325),l(-.025,.325),l(-.362,.523),l(-.043,.396),l(.105,.339),l(.174,.269),l(.466,.325),l(.522,.168),l(.109,.113),l(.226,.892),l(-.308,.481),l(-.75,.937),l(-.729,.766),l(-.313,.737),l(-.368,.284),l(-.832,.342),l(-1.04,.342),l(-1.92,.401),l(-1.795,.188),l(-1.361,.116),l(-.945,.044),l(-1.175,-.11),l(-.934,-.226),l(-.128,.199),l(.036,.808),l(.322,.312),l(.308,.184),l(-.09,.298),l(-.381,.624),l(-.345,.498),l(-.069,.385),l(.392,.682),l(.066,.285),l(-.208,.214),l(-.105,.057),l(-1.251,.473),l(-1.137,.116),l(-.814,-.069),l(-.967,-.34),l(-1.47,-.396),l(-.246,.057),l(-.23,.271),l(.041,.598),l(.429,.684),l(.037,.398),l(-.242,.643),l(.092,.385),l(.773,.54),l(.796,.084),l(.369,-.2),l(-.387,-.398),l(.872,-.188),l(.383,-.043),l(.234,1.041),l(.052,.3),l(-.144,.157),l(-.299,.101),l(-.448,.072),l(-.261,-.157),l(-.104,-.299),l(-.115,-.071),l(-1.046,.073),l(-.67,.201),l(-.212,.101),l(.151,.214),l(.591,.07),l(.47,.113),l(.452,.113),l(.06,.028),l(-.864,.388),l(-.776,.287),l(-.577,.602),l(.003,.414),l(.161,.787),l(-.081,.258),l(-.815,.817),l(.022,.215),l(.423,.371),l(-.491,.116),l(-1.194,.088),l(-.48,.087),l(-.632,.246),l(-.619,.389),l(-.56,.548),l(-.549,.821),l(-.052,.389),l(.061,.375),l(.312,.591),l(.48,.446),l(.98,.633),l(.657,.244),l(.97,.143),l(.362,.086),l(.14,.274),l(-.151,.796),l(-.128,.348),l(-.342,.464),l(-.189,.145),l(-1.08,.524),l(-1.541,.814),l(-.712,.698),l(-.179,.276),l(-.093,.45),l(.111,.523),l(-.169,.451),l(-.239,.32),l(-.97,.454),l(-.969,.25),l(-.421,.221),l(-.323,.396),l(-.226,.791),l(-.054,.514),l(.2,.777),l(.547,.896),l(.699,.779),l(.235,.339),l(-.101,.032),M(247.899,318.387),l(.18,.182),l(.148,.073),l(.607,-.075),l(.344,.133),l(.648,.725),l(.908,.665),l(.993,.756),l(.525,.222),l(.73,.37),l(.246,.074),l(.42,-.001),l(.483,.163),l(1.283,.027),l(.142,-.016),l(-.006,.224),l(-.19,.209),l(-.492,.06),l(-1.198,.092),l(-.777,.196),l(-.364,0),l(-1.179,-.355),l(-.753,-.088),l(-1.15,-.027),l(-.814,-.014),l(-.831,.081),l(.098,-3.675),
+N(346.758,54.457),l(.127,-.162),l(.42,-.179),l(.894,.015),l(.674,-.098),l(-.354,-.227),l(-.405,-.34),l(.317,-.342),l(.277,0),l(.956,.42),l(.67,.048),l(.3,-.163),l(-.191,-.26),l(-.625,-.373),l(.366,-.245),l(1.037,.226),l(.957,.08),l(.746,.275),l(.446,.551),l(-.119,.405),l(-.441,.292),l(.922,.806),l(.553,-.356),l(.352,-.13),l(.63,-.114),l(.49,-.179),l(.061,-.21),l(-.169,-.778),l(.542,-.245),l(.501,.454),l(.426,.307),l(.489,.209),l(.215,.016),l(.185,-.13),l(-.264,-.454),l(.243,-.163),l(.27,-.033),l(.817,-.164),l(.683,.438),l(.536,.242),l(.542,.063),l(.05,-.178),l(-.358,-.535),l(1.006,.145),l(1.439,.079),l(.787,-.115),l(.427,-.229),l(-.021,-.716),l(1.167,.08),l(.635,.471),l(1.118,.323),l(.683,.015),l(.273,.243),l(-.252,.552),l(.786,.29),l(1.674,.159),l(.127,.145),l(.143,.548),l(-.07,.387),l(-.152,.256),l(-.152,.256),l(-.443,.129),l(-.815,.018),l(-.195,.096),l(-.04,.431),l(-.515,.463),l(-.497,.16),l(-.568,-.031),l(-.422,-.159),l(-.817,.495),l(-.539,.144),l(-1.46,.463),l(-.853,.113),l(-.726,.001),l(-.771,.097),l(-.784,.587),l(-.473,.127),l(-1.078,.097),l(-.709,-.03),l(-1.316,-.171),l(-.608,-.142),l(-1.283,-.489),l(-1.058,-.093),l(-.443,.064),l(-1.041,-.014),l(-1.834,-.124),l(-.297,-.206),l(.434,-.191),l(1.127,-.352),l(.701,-.59),l(-.818,-.015),l(-.51,-.126),l(-.428,-.398),l(-.253,-.095),l(-.358,.081),l(-1.564,.115),l(-.557,.033),l(-.37,-.223),l(.141,-.192),l(.388,-.129),l(.669,-.097),l(.794,-.017),l(.729,-.114),l(1.049,-.098),l(.376,-.194),l(.178,-.322),l(-.144,-.258),l(-.358,-.177),l(-.426,-.015),l(-.478,-.145),l(-1.005,-.047),l(-.821,.099),l(-.424,.162),l(-.676,.082),l(-1.041,-.272),l(-.16,-.21),
+N(462.829,67.958),l(.145,.053),l(.16,.131),l(-.043,.174),l(-.175,.044),l(-.189,0),l(-.116,0),l(-.088,.043),l(-.058,.131),l(-.175,.277),l(-.219,.204),l(-.319,.131),l(-.204,.131),l(-.088,.16),l(.029,.16),l(.131,.437),l(.073,.189),l(.029,.16),l(-.175,.131),l(0,.175),l(.175,.233),l(.203,.087),l(.088,.073),l(0,.073),l(.015,.087),l(.043,.146),l(.175,.043),l(.059,.059),l(-.175,.073),l(-.262,.043),l(-.16,.058),l(-.059,.102),l(-.087,.116),l(-.131,0),L(461.402,72),l(-.015,.087),l(-.116,.058),l(.081,.106),l(-.125,.025),l(-.087,-.015),l(-.306,-.043),l(-.16,-.058),l(-.175,-.073),l(-.204,-.044),l(-.131,.102),l(-.204,-.029),l(-.131,.044),l(-.16,.087),l(-.146,.014),l(-.146,-.087),l(-.116,-.029),l(-.175,0),l(-.204,-.117),l(-.116,-.174),l(-.203,-.117),l(-.146,-.043),l(-.072,-.117),l(-.189,0),l(-.247,-.043),l(-.204,-.117),l(-.029,0),l(-.276,-.014),l(-.102,-.102),l(-.248,-.043),l(-.116,-.102),l(-.189,-.087),l(0,.073),l(-.116,.087),l(-.131,-.058),l(-.015,-.073),l(-.087,-.029),l(-.103,0),l(-.276,.117),l(-.102,.029),l(-.131,.015),l(-.219,.015),l(-.146,.043),l(-.262,.029),l(-.276,.087),l(-.116,.102),l(-.087,0),l(.156,-.19),l(-.003,-.351),l(.183,-.238),l(-.368,-.21),l(-.605,.437),l(-.334,-.251),l(-.527,-.038),l(.043,-.942),l(-.396,.188),l(-.336,-.415),l(.158,-.202),l(-.209,-.254),l(.265,-.074),l(-.092,-.252),l(.344,-.042),l(1.026,-.084),l(-.006,-.132),l(.561,-.108),l(.133,-.188),l(.436,.101),l(.074,-.113),l(.317,.05),l(.083,-.215),l(1.104,.193),l(.446,-.294),l(.091,.165),l(.514,-.089),l(1.383,.029),l(1.152,.167),l(.305,.12),l(.592,-.045),l(.971,.09),l(.426,-.108),l(.271,-.24),l(-.006,-.009),
+N(461.353,72.251),l(.37,-.004),l(-.015,.116),l(0,.117),l(.232,.073),l(.204,.087),l(.087,.073),l(.204,.058),l(-.015,.087),l(-.029,.116),l(-.015,.087),l(-.102,.073),l(-.087,.015),l(-.103,.029),l(.131,.087),l(-.072,.131),l(.029,.117),l(-.073,.087),l(-.102,.073),l(-.044,.102),l(.204,-.059),l(.146,-.015),l(.131,.073),l(.087,.014),l(.073,.044),l(-.059,.087),l(-.043,.073),l(.116,.102),l(.131,.058),l(.029,.16),l(.146,.102),l(.16,.043),l(-.059,.087),l(.131,.117),l(-.015,.189),l(.088,.233),l(-.044,.087),l(-.015,.117),l(-.061,.042),l(-.241,.044),l(-.24,.051),l(-.12,.12),l(-.223,.034),l(-.137,.154),l(-.137,.085),l(-.069,.103),l(-.068,.206),l(-.188,.035),l(-.239,-.035),l(-.24,-.034),l(-.325,-.034),l(-.359,0),l(-.172,.068),l(-.103,.137),l(-.223,.085),l(-.154,0),l(-.103,-.017),l(-.086,.052),l(-.377,-.035),l(-.273,-.068),l(-.154,-.171),l(-.138,-.154),l(-.325,-.137),l(-.497,-.223),l(-.342,-.24),l(-.377,-.034),l(-.583,-.035),l(-.325,-.12),l(-.291,-.188),l(-.086,-.257),l(-.188,.017),l(-.171,.068),l(-.36,.171),l(-.394,.017),l(-.24,0),l(-.273,.085),l(-.24,-.085),l(-.309,-.103),l(-.6,-.017),l(-.291,.069),l(-.359,-.069),l(-.291,-.051),l(-.154,.034),l(-.274,.069),l(-.103,-.052),l(-.12,-.137),l(-.154,0),l(-.257,.068),l(-.188,0),l(-.754,-.017),l(-.445,-.085),l(-.754,.171),l(-.599,.154),l(-.429,.103),l(-.257,.137),l(-.052,.188),l(-.526,.026),l(-.065,-.059),l(.073,-.837),l(.035,-.302),l(.127,-.167),l(.672,-.379),l(.034,-.717),l(.267,-.162),l(.267,-.273),l(.217,-.203),l(.296,-.026),l(1.056,-.199),l(.166,-.046),l(.162,-.066),l(.29,0),l(.049,.237),l(.657,.388),l(.422,.162),l(.23,.473),l(.091,.15),l(.441,.196),l(.785,.059),l(.868,-.244),l(.24,-.122),l(.178,-.288),l(-.052,-.394),l(-.193,-.869),l(.198,-.243),l(.045,-.055),l(.087,0),l(.116,-.102),l(.276,-.087),l(.262,-.029),l(.146,-.043),l(.219,-.015),l(.131,-.015),l(.102,-.029),l(.276,-.117),l(.103,0),l(.087,.029),l(.015,.073),l(.131,.058),l(.116,-.087),l(0,-.073),l(.189,.087),l(.116,.102),l(.248,.043),l(.102,.102),l(.276,.014),l(.029,0),l(.204,.117),l(.247,.043),l(.189,0),l(.072,.117),l(.146,.043),l(.203,.117),l(.116,.174),l(.204,.117),l(.175,0),l(.116,.029),l(.146,.087),l(.146,-.014),l(.16,-.087),l(.131,-.044),l(.204,.029),l(.131,-.102),l(.204,.044),l(.175,.073),l(.16,.058),l(.306,.043),l(.087,.015),l(.125,-.025),
+N(451.02,79.165),l(-.029,-.038),l(-.034,-.137),l(-.018,-.171),l(.068,-.206),l(.068,-.154),l(.224,-.12),l(-.052,-.12),l(-.018,-.137),l(-.171,-.069),l(-.188,-.034),l(-.103,-.103),l(-.086,-.137),l(-.223,.017),l(-.257,0),l(-.445,0),l(-.223,.051),l(-.086,-.103),l(-.514,-.068),l(-.257,-.069),l(-.223,-.12),l(-.24,0),l(-.086,-.052),l(-.051,-.154),l(-.12,.034),l(-.353,.096),l(-.043,-.077),l(.128,-.012),l(.034,-.183),l(-.439,-.646),l(-.008,-.14),l(-.042,-.727),l(-.112,-.102),l(.526,-.026),l(.052,-.188),l(.257,-.137),l(.429,-.103),l(.599,-.154),l(.754,-.171),l(.445,.085),l(.754,.017),l(.188,0),l(.257,-.068),l(.154,0),l(.12,.137),l(.103,.052),l(.274,-.069),l(.154,-.034),l(.291,.051),l(.359,.069),l(.291,-.069),l(.6,.017),l(.309,.103),l(.24,.085),l(.273,-.085),l(.24,0),l(.394,-.017),l(.36,-.171),l(.171,-.068),l(.188,-.017),l(.086,.257),l(.291,.188),l(.325,.12),l(.583,.035),l(.377,.034),l(.342,.24),l(.497,.223),l(.325,.137),l(.138,.154),l(.154,.171),l(.273,.068),l(.377,.035),l(-.017,.171),l(-.086,.154),l(-.034,.12),l(-.12,.137),l(-.086,.137),l(.343,.034),l(.274,.052),l(.085,.051),l(-.119,.051),l(-.086,0),l(-.103,.154),l(-.018,.154),l(-.171,.017),l(-.12,-.086),l(-.12,.051),l(-.239,-.034),l(-.154,.034),l(-.086,.154),l(-.103,.154),l(-.257,.068),l(-.429,0),l(-.137,.137),l(-.12,.12),l(-.034,.154),l(-.086,.171),l(.103,.171),l(-.068,.137),l(-.239,.154),l(0,.137),l(-.068,.085),l(-.069,.137),l(.172,.034),l(.205,0),l(.138,.206),l(-.086,.188),l(-.274,.017),l(-.223,-.068),l(0,-.154),l(-.034,-.085),l(-.086,-.069),l(-.171,.051),l(-.12,.086),l(-.291,-.034),l(-.068,.137),l(-.24,.12),l(-.154,0),l(-.188,-.034),l(-.273,.103),l(.086,.171),l(-.069,.12),l(-.171,.034),l(-.137,-.034),l(-.206,.051),l(-.377,.154),l(-.291,0),l(-.068,-.103),l(-.12,-.051),l(-.239,.051),l(-.377,.017),l(-.24,.034),l(-.291,-.034),l(-.154,.034),l(-.093,-.035),l(-.09,-.171),l(-.016,-.029),l(-.099,-.186),l(-.284,-.487),l(-.679,-.243),l(-.04,-.014),l(-.641,.021),
+N(452.867,80.273),l(.093,.035),l(.154,-.034),l(.291,.034),l(.24,-.034),l(.377,-.017),l(.239,-.051),l(.12,.051),l(.068,.103),l(.291,0),l(.377,-.154),l(.206,-.051),l(.137,.034),l(.171,-.034),l(.069,-.12),l(-.086,-.171),l(.273,-.103),l(.188,.034),l(.154,0),l(.24,-.12),l(.068,-.137),l(.291,.034),l(.12,-.086),l(.171,-.051),l(.086,.069),l(.034,.085),l(0,.154),l(.223,.068),l(.274,-.017),l(.086,-.188),l(-.138,-.206),l(-.205,0),l(-.172,-.034),l(.069,-.137),l(.068,-.085),l(0,-.137),l(.239,-.154),l(.068,-.137),l(-.103,-.171),l(.086,-.171),l(.034,-.154),l(.12,-.12),l(.137,-.137),l(.429,0),l(.257,-.068),l(.103,-.154),l(.086,-.154),l(.154,-.034),l(.239,.034),l(.12,-.051),l(.12,.086),l(.171,-.017),l(.018,-.154),l(.103,-.154),l(.086,0),l(.119,-.051),l(-.085,-.051),l(-.274,-.052),l(-.343,-.034),l(.086,-.137),l(.12,-.137),l(.034,-.12),l(.086,-.154),l(.017,-.171),l(.086,-.052),l(.103,.017),l(.154,0),l(.223,-.085),l(.103,-.137),l(.172,-.068),l(.359,0),l(.325,.034),l(.24,.034),l(.239,.035),l(.188,-.035),l(.068,-.206),l(.069,-.103),l(.137,-.085),l(.137,-.154),l(.223,-.034),l(.12,-.12),l(.24,-.051),l(.241,-.044),l(.165,.147),l(.229,.066),l(.197,-.131),l(.181,.016),l(.312,.033),l(.132,.148),l(.082,.148),l(.197,-.033),l(.214,-.065),l(.361,-.049),l(.312,.049),l(.296,.065),l(.147,.017),l(0,.115),l(-.164,.099),l(-.017,.099),l(.065,.148),l(.164,.148),l(.197,0),l(.214,-.197),l(.279,-.016),l(.165,0),l(.147,-.099),l(.23,-.083),l(.131,.049),l(.099,.049),l(.247,-.049),l(.542,.115),l(.132,.131),l(.279,.099),l(.099,.099),l(.147,.099),l(.165,.033),l(.147,-.033),l(.049,.115),l(-.065,.115),l(0,.083),l(-.033,.164),l(-.131,.165),l(.197,.247),l(.147,.099),l(.05,.164),l(-.066,.131),l(-.114,0),l(0,.083),l(-.115,.082),l(-.099,.049),l(-.033,.165),l(-.049,.147),l(.345,.049),l(.132,.181),l(.082,.115),l(.181,-.049),l(.132,.033),l(-.099,.115),l(-.066,.131),l(.017,.099),l(.214,.017),l(.164,.263),l(.115,.23),l(.443,.213),l(.23,.066),l(.279,.082),l(.164,.099),l(.066,.148),l(-.099,.197),l(-.066,.181),l(.182,.066),l(.361,-.066),l(.378,.066),l(.361,.099),l(.263,.066),l(-.032,.065),l(-.066,.049),l(-.082,.017),l(.099,.181),l(.296,.148),l(.279,.066),l(.033,.131),l(-.065,.164),l(-.296,0),l(-.148,.083),l(-.049,.065),l(-.444,.247),l(-.525,.099),l(-.51,-.016),l(-.197,-.132),l(-.328,-.049),l(-.362,.017),l(-.131,.165),l(-.099,.131),l(.017,.164),l(.279,.263),l(.296,.164),l(0,.165),l(-.132,.066),l(.099,.148),l(.147,.131),l(-.082,.099),l(.033,.164),l(.033,.23),l(-.033,.099),l(.164,.082),l(.082,.115),l(.165,.066),l(.002,.142),l(-.519,-.005),l(-.522,.056),l(-.112,.131),l(-.205,-.056),l(-.187,-.037),l(-.336,.075),l(-.057,.13),l(-.111,.112),l(-.317,.187),l(-.188,.261),l(-.261,.224),l(-.057,.206),l(.243,.205),l(.056,.149),l(-.131,.206),l(-.261,-.019),l(-.149,-.056),l(-.149,-.206),l(-.112,-.056),l(-.187,-.075),l(-.224,-.037),l(-.225,.037),l(-.242,.075),l(-.299,.019),l(-.149,-.131),l(-.224,.075),l(-.188,.093),l(-.316,.056),l(-.188,-.056),l(-.037,-.205),l(-.112,-.131),l(-.168,-.131),l(-.13,.038),l(-.131,.075),l(-.168,0),l(-.299,.149),l(-.131,.149),l(-.168,0),l(-.094,-.187),l(-.13,-.075),l(-.206,0),l(-.224,.112),l(-.131,-.187),l(-.224,-.093),l(-.131,.112),l(-.354,.056),l(-.262,-.112),l(-.112,0),l(-.019,.205),l(-.168,.093),l(-.093,-.056),l(.056,-.224),l(-.243,-.038),l(-.187,-.056),l(-.541,.112),l(0,-.149),l(-.187,0),l(.019,-.224),l(-.28,-.037),l(-.242,.075),l(-.523,-.112),l(-.578,-.056),l(-.075,-.056),l(-.522,0),l(-.205,-.168),l(-.262,.019),l(-.522,-.093),l(-.467,.075),l(-.485,0),l(-.354,-.056),l(-.355,.056),l(-.354,.056),l(-.485,-.038),l(-.485,.019),l(-.205,.187),l(-.037,.168),l(-.374,.168),l(-.373,.206),l(-.112,-.112),l(-.261,0),l(-.374,-.019),l(-.037,.131),l(0,.045),l(-.126,-.137),l(.315,-.752),l(-.013,-.25),l(-.218,-.146),l(-.149,-.176),l(-.421,-.146),l(-.289,-.012),l(.128,-.292),l(.291,-.328),l(.571,-.244),l(.44,-.03),l(.263,-.208),l(.023,-.236),l(-.172,-.502),l(-.615,-1.5),l(-.16,-.302),
+N(400.125,81.146),l(.633,.305),l(.208,.207),l(.208,.37),l(-.038,.193),l(-.545,.563),l(.714,.176),l(.396,-.311),l(.527,-.119),l(.602,.028),l(.807,.176),l(.467,.354),l(.235,.752),l(-.077,.221),l(-.322,.414),l(-1.068,.473),l(-.767,.561),l(-.96,.237),l(1.223,.167),l(.501,.043),l(.354,-.104),l(.39,.117),l(-.066,.516),l(-.997,.308),l(.005,.199),l(-.479,-.084),l(-1.068,.443),l(-.879,-.142),l(-.293,-.048),l(-1.119,.211),l(-.587,-.211),l(-.598,.112),l(-1.584,.141),l(.137,.295),l(-.907,-.168),l(-.264,.168),l(-.911,-.337),l(-.334,.143),l(-.913,.089),l(-.093,.569),l(-.337,.316),l(-.37,.042),l(-.272,-.252),l(-.53,-.172),l(-.135,.151),l(-.527,-.077),l(-.948,.324),l(-.701,.552),l(-.326,-.231),l(-.775,-.147),l(1.2,-.472),l(.492,-.476),l(.447,-.097),l(.468,-.388),l(.118,-.485),l(.242,.063),l(.367,-.211),l(-.008,-.274),l(1.013,-.105),l(.76,.119),l(.927,.007),l(.073,-.338),l(.308,-.142),l(.321,-.556),l(-.939,.394),l(-.725,.016),l(-1.467,-.482),l(-1.866,-.055),l(-.399,-.191),l(-.156,-.162),l(.417,-.325),l(.983,-.194),l(.721,-.237),l(.38,-.384),l(.066,-.827),l(-.119,-.192),l(-.713,.046),l(-.34,-.044),l(.288,-.355),l(.387,-.223),l(.802,-.253),l(1.394,-.062),l(.922,-.076),l(-.316,-.548),l(.172,-.683),l(.253,-.461),l(-.045,-.312),l(-.834,.061),l(-.484,-.296),l(-.2,-.312),l(.309,-.507),l(.617,-.433),l(-.347,-.104),l(-.596,-.088),l(-.735,.3),l(-.476,.061),l(-.753,-.222),l(-.088,.194),l(-.222,.194),l(-.672,-.103),l(-.464,-.133),l(.1,-.343),l(.348,-.36),l(.604,-.706),l(-.293,-.134),l(-.305,-.39),l(.028,-.24),l(.304,-.106),l(.3,-.196),l(-.194,-.496),l(-.306,.076),l(-.636,.453),l(-.442,.031),l(-.526,.287),l(-.14,-.421),l(.302,-.527),l(.438,-.559),l(.065,-.257),l(-.417,-.195),l(-.196,-.045),l(-.218,.302),l(-.394,.257),l(-.493,-.165),l(.299,-.575),l(.54,-.455),l(.09,-.136),l(-.225,-.575),l(.366,-.092),l(.225,-.197),l(-.672,-.515),l(.432,-.351),l(.752,.151),l(.387,-.092),l(-.645,-.759),l(.975,-.169),l(-.457,-.502),l(.416,-.382),l(.924,.365),l(.824,-.093),l(.686,-.139),l(.867,-.047),l(.612,.014),l(.303,.259),l(-.307,.29),l(-1.726,.704),l(-.46,.274),l(-.218,.441),l(.222,.182),l(.784,.029),l(.877,-.078),l(.685,-.001),l(.53,.075),l(1.563,-.064),l(.458,.378),l(-.363,.425),l(-.212,.323),l(.098,.112),l(-.565,.66),l(-.226,.111),l(-.339,.437),l(-.696,.261),l(-.382,.038),l(.451,.186),l(.508,.167),l(-.116,.015),l(-.272,.19),l(-.61,.052),l(-.275,.196),l(-1.337,-.025),l(.404,.223),l(.302,0),l(.492,.093),l(.432,-.006),l(.519,-.223),l(.413,-.025),l(.449,.161),l(.656,.164),l(.673,.566),l(.496,.228),l(.118,.165),l(-.067,.238),l(.312,.78),l(.371,.536),l(.438,.189),l(.714,.107),l(.59,.549),l(.688,.593),l(.135,.52),l(-.188,.49),l(.257,.124),M(387.915,77.13),l(-.128,-.325),l(.149,-.335),l(.38,-.089),l(.079,.501),l(-.307,.251),l(-.173,-.003),M(386.786,80.184),l(-.178,-.272),l(-.967,.072),l(.123,-.256),l(-.364,-.15),l(-.26,-.257),l(-.335,-.107),l(-.253,.364),l(-.751,.257),l(-.778,-.192),l(-.401,-.278),l(-.101,-.278),l(.86,-.278),l(-.483,-.257),l(.817,-.107),l(.385,-.484),l(-.029,-.235),l(.449,-.09),l(.508,-.15),l(.781,-.077),l(.424,.044),l(.389,.104),l(.362,-.046),l(.218,.149),l(.519,.791),l(.047,.179),l(-.081,.298),l(.308,.446),l(-.155,.328),l(-.402,.328),l(-.354,.12),l(-.299,.038),
+N(578.943,106.217),l(-.41,-.375),l(-.466,-.098),l(-.663,0),l(-.196,-.27),l(-.27,-.147),l(-.147,-.344),l(-.564,.049),l(-.981,-.246),l(-.662,.074),l(-1.35,-.024),l(-.662,-.098),l(-.712,-.221),l(-.785,.147),l(-.761,0),l(-.858,.024),l(-.441,.27),l(-.54,-.098),l(-.908,-.196),l(-.735,-.246),l(-.761,-.27),l(-.589,-.074),l(-.688,.123),l(-.466,.368),l(-.245,.736),l(.024,.442),l(-.344,-.123),l(-.81,-.123),l(-.688,-.196),l(-.883,-.245),l(-.883,-.147),l(-.663,.098),l(-.736,.123),l(-.318,.368),l(-.393,.442),l(.044,.273),l(-.322,.031),l(-.377,.377),l(-.283,-.126),l(-.22,.063),l(-.346,.283),l(-.534,.471),l(-.755,.189),l(-.943,.377),l(-.282,.188),l(-.221,.472),l(-.439,.188),l(-.504,.44),l(.157,.409),l(-.125,.188),l(-.66,0),l(-.44,-.346),l(.062,-.283),l(-.062,-.283),l(-.44,-.314),l(-.346,0),l(-1.006,.094),l(-.691,.032),l(-.503,-.063),l(-.346,-1.069),l(-.221,-.817),l(-1.006,0),l(-.031,-.754),l(.188,-.409),l(.031,-1.038),l(-.66,.314),l(-.66,-1.006),l(-.597,-.22),l(-.724,-.723),l(-1.1,.409),l(-2.767,-.188),l(-2.578,.346),l(-2.012,-1.666),l(-5.722,-2.986),l(-5.658,1.289),l(-.056,8.174),l(-.158,-.014),l(-.341,.106),l(-.489,.043),l(-.447,-.255),l(-.638,-.703),l(-.256,-.511),l(-.617,-.383),l(-.681,-.383),l(-.512,-.234),l(-.979,.085),l(-1.277,.298),l(-.937,.532),l(-.529,.453),l(.092,-.399),l(-.06,-.18),l(-.12,-.12),l(.14,-.26),l(.2,-.2),l(.14,-.32),l(.04,-.3),l(.18,-.2),l(-.159,-.24),l(-.4,-.16),l(-.459,.06),l(-.18,-.16),l(-.3,.06),l(-.2,.04),l(-.199,-.18),l(-.221,-.32),l(-.319,-.28),l(-.34,0),l(-.359,.02),l(0,-.2),l(.08,-.28),l(-.2,-.379),l(-.239,-.12),l(-.2,-.24),l(-.399,-.799),l(-.08,-.28),l(-.56,-.12),l(-.699,-.08),l(-.14,-.16),l(.02,-.439),l(.16,-.12),l(.3,-.06),l(.399,.02),l(.34,.02),l(.479,.14),l(.539,.18),l(.18,-.08),l(.36,-.08),l(-.2,-.16),l(-.26,-.12),l(-.399,-.2),l(-.2,-.24),l(.26,-.36),l(.28,-.04),l(.08,-.26),l(.18,-.299),l(.12,-.14),l(.26,.04),l(.319,-.08),l(.16,-.1),l(.339,.12),l(.24,0),l(1.119,-.04),l(.999,.14),l(.499,.02),l(-.159,-.08),l(-.34,-.2),l(-.479,-.12),l(-.021,-.3),l(.2,-.2),l(.279,-.22),l(.221,-.28),l(.119,-.52),l(.12,-.28),l(-.16,-.24),l(-.14,-.16),l(.1,-.2),l(.26,-.2),l(-.119,-.12),l(-.101,-.3),l(-.359,-.12),l(-.359,-.04),l(-.68,-.1),l(-.2,.16),l(-.199,.08),l(-.52,.08),l(-.46,-.12),l(-.319,-.26),l(-.26,-.06),l(-.68,-.12),l(-.56,.06),l(-.659,.319),l(-.42,.02),l(-.799,.5),l(-.72,.28),l(-.499,.06),l(-.42,-.02),l(-.279,.24),l(-.213,.18),l(-.616,-.19),l(-.857,-.377),l(-.068,-.308),l(.343,-.103),l(.309,.103),l(.445,.103),l(.138,-.103),l(-.96,-1.131),l(-.343,-.514),l(-.479,-.206),l(-.515,-.445),l(-.514,-.034),l(-.343,.034),l(-.583,-.206),l(-.103,.343),l(-.514,-.514),l(.068,-.309),l(-.138,-.377),l(-1.37,-.343),l(.65,-1.165),l(.446,-.274),l(.239,-.206),l(-.239,-.274),l(-.343,-.171),l(.205,-1.303),l(.823,-.137),l(.343,-.549),l(.103,-.308),l(.411,-.069),l(.514,.24),l(.48,.548),l(.514,.411),l(.651,0),l(.411,-.24),l(.068,-.446),l(-.171,-.411),l(-.068,-.445),l(.479,-.206),l(.891,-.411),l(.172,-.24),l(.309,-.309),l(.514,-.171),l(.549,-.068),l(.788,-.377),l(.548,-.343),l(.515,-.309),l(.651,.069),l(.479,0),l(.309,.274),l(.651,-.137),l(.273,-.137),l(.617,-.24),l(.411,.069),l(.411,.514),l(.788,.035),l(.617,-.069),l(.96,.171),l(0,.343),l(.582,.206),l(.789,.343),l(.411,.274),l(.068,.583),l(.274,.137),l(.239,-.274),l(-.205,-.48),l(-.034,-.24),l(.72,.068),l(.582,.548),l(.686,.137),l(.411,.24),l(.686,-.171),l(.274,-.274),l(.377,-.343),l(.514,-.377),l(.823,.068),l(.65,.035),l(.651,.411),l(.617,-.068),l(.137,-.412),l(1.062,-.103),l(.754,.103),l(.274,.548),l(.926,.309),l(.754,.137),l(.411,.171),l(.651,-.343),l(.171,-.309),l(.24,0),l(.343,.343),l(.959,.034),l(1.577,-.411),l(.137,-.309),l(.138,-.686),l(-.24,-.24),l(-1.165,-.171),l(-.274,-.308),l(-.651,-.069),l(-.377,-.137),l(.068,-.171),l(-.377,-.137),l(-.239,0),l(-.164,-.274),l(.467,-.067),l(.735,-.368),l(.588,-.147),l(.331,-.294),l(-.441,-.478),l(-.146,-.257),l(.662,-.515),l(.698,-.184),l(1.103,.147),l(.515,-.073),l(.11,-.257),l(-.956,-.294),l(-1.065,-.11),l(0,-.331),l(.294,-.074),l(-.294,-.221),l(-.074,-.441),l(.185,-.515),l(.33,-.074),l(1.066,.147),l(.515,0),l(.772,0),l(.368,-.184),l(1.396,-.405),l(1.029,-.037),l(.735,-.11),l(1.545,-.11),l(.588,-.073),l(.331,.073),l(.221,-.331),l(.625,-.331),l(1.177,-.037),l(2.021,-.405),l(1.876,-.073),l(.625,-.074),l(.367,-.368),V(0,77.39),l(.515,-.037),l(.589,-.184),l(.11,-.221),l(.735,-.037),l(.919,.147),l(.515,.11),l(.772,.257),l(.625,-.11),l(.882,-.037),l(.368,.404),l(-.037,.331),l(.147,.221),l(.515,.22),l(-.11,.331),l(-.147,.257),l(.073,.331),l(-.33,.037),l(.184,.257),l(.478,.074),l(.295,-.147),l(.44,.11),l(.368,-.147),l(.367,.074),l(.331,-.221),l(.294,.11),l(.295,.368),l(.367,.221),l(.147,-.147),l(.184,-.147),l(.478,.037),l(.405,.294),l(.478,.11),l(.441,-.221),l(.367,0),l(-.146,.294),l(-.441,.184),l(-.331,.441),l(.331,.184),l(.441,-.11),l(.771,-.073),l(.441,.037),l(.552,.184),l(.294,-.294),l(.772,-.441),l(1.103,-.257),l(.956,-.515),l(.772,-.221),l(.515,-.22),l(.809,-.074),l(0,.441),l(-.515,.11),l(-.11,.368),l(1.104,.588),l(.809,.294),l(1.287,.772),l(1.066,1.029),l(1.69,2.133),l(.846,.882),l(1.104,1.434),l(.515,-.257),l(.331,-.257),l(.367,-.515),l(.92,0),l(.367,.331),l(0,.368),l(.478,0),l(.258,.257),l(.184,.184),l(.589,0),l(.992,0),l(.993,-.221),l(.771,-.221),l(.993,-.037),l(.698,.441),l(.772,.588),l(.331,.625),l(.956,.147),l(.588,.552),l(.662,.699),l(.882,.073),l(.993,.074),l(.478,-.368),l(.625,-.184),l(-.073,.331),l(.441,.331),l(.294,.478),l(.589,0),l(.064,.145),l(-.551,.034),l(-.542,.148),l(-.279,.262),l(-.011,.275),l(-.035,.478),l(-.306,.219),l(-.289,.06),l(-1.199,.093),l(-.428,.277),l(-.34,.581),l(.097,.75),l(.213,.707),l(-.157,.39),l(-.444,.392),l(-.417,.103),l(-.718,.062),l(-1.402,-.079),l(-.594,-.141),l(-.721,-.141),l(-1.096,-.254),l(-.427,.507),l(-.516,1.141),L(584.2,97.43),l(-.286,.605),l(-.137,.418),l(.622,.514),l(.126,.286),l(-.156,.245),l(-.231,.145),l(-.394,.074),l(-1.133,-.238),l(-.5,-.184),l(-.35,.06),l(-1.082,.207),l(-1.799,.254),l(-.393,.188),l(-.215,.302),l(-.068,.215),l(.232,.185),l(.366,-.06),l(.483,.141),l(.03,1.357),l(.345,.627),l(.29,.441),l(.119,.47),l(-.222,.33),l(-.705,.546),l(-.32,.401),l(-.02,.399),l(.139,.86),
+N(386.786,80.184),l(-.304,.038),l(-.223,.09),l(.241,.252),l(.361,.771),l(.287,1.213),l(-.061,.281),l(-.359,.341),l(-.242,.414),l(-.145,.473),l(-.185,.044),l(-.284,-.058),l(-.616,.031),l(-.15,.212),l(-.913,.042),l(-.84,.132),l(-.247,.144),l(-.661,.286),l(-.903,.498),l(-.628,.035),l(-.879,.283),l(-1.28,.084),l(.053,-.378),l(-.089,-.441),l(-.848,.1),l(-.171,-.487),l(.734,-.254),l(-1.186,-.021),l(.062,-.233),l(1.286,.027),l(.198,-.104),l(.039,-.222),l(.107,-.31),l(.515,-.134),l(.692,-.031),l(.13,-.281),l(-1.07,.099),l(.387,-.437),l(-.187,-.159),l(.481,-.468),l(.694,-.011),l(.163,-.089),l(-.174,-.311),l(-.348,.177),l(-.309,-.131),l(-.319,.03),l(-.391,-.177),l(-.414,.001),l(-.182,.106),L(378,81.478),l(.309,-.306),l(-.29,-.142),l(.759,-.126),l(-.139,-.301),l(.391,-.235),l(-.481,-.214),l(-.59,.128),L(378,79.792),l(.38,-.268),l(.215,-.16),l(.928,.187),l(.336,-.075),l(.527,.038),l(1.102,.123),l(-.214,-.358),L(382.132,79),l(.198,-.321),l(-1.373,0),l(.154,-.15),l(.569,-.107),l(.061,-.29),l(.291,-.479),l(.505,-.181),l(.804,-.169),l(.22,.302),l(.354,.149),l(.156,-.031),l(.029,.235),l(-.385,.484),l(-.817,.107),l(.483,.257),l(-.86,.278),l(.101,.278),l(.401,.278),l(.778,.192),l(.751,-.257),l(.253,-.364),l(.335,.107),l(.26,.257),l(.364,.15),l(-.123,.256),l(.967,-.072),l(.178,.272),
+N(452.998,85.535),V(0,85.49),l(.037,-.131),l(.374,.019),l(.261,0),l(.112,.112),l(.373,-.206),l(.374,-.168),l(.037,-.168),l(.205,-.187),l(.485,-.019),l(.485,.038),l(.354,-.056),l(.355,-.056),l(.354,.056),l(.485,0),l(.467,-.075),l(.522,.093),l(.262,-.019),l(.205,.168),l(.522,0),l(.075,.056),l(.578,.056),l(.523,.112),l(.242,-.075),l(.28,.037),l(-.019,.224),l(.187,0),l(0,.149),l(.541,-.112),l(.187,.056),l(.243,.038),l(-.056,.224),l(.093,.056),l(.168,-.093),l(.019,-.205),l(.112,0),l(.262,.112),l(.354,-.056),l(.131,-.112),l(.224,.093),l(.131,.187),l(.224,-.112),l(.206,0),l(.13,.075),l(.094,.187),l(.168,0),l(.131,-.149),l(.299,-.149),l(.168,0),l(.131,-.075),l(.13,-.038),l(.168,.131),l(.112,.131),l(.037,.205),l(.188,.056),l(.316,-.056),l(.188,-.093),l(.224,-.075),l(.149,.131),l(.299,-.019),l(.242,-.075),l(.225,-.037),l(.224,.037),l(.187,.075),l(.112,.056),l(.149,.206),l(.149,.056),l(.261,.019),l(.131,-.206),l(-.056,-.149),l(-.243,-.205),l(.057,-.206),l(.261,-.224),l(.188,-.261),l(.317,-.187),l(.111,-.112),l(.057,-.13),l(.336,-.075),l(.187,.037),l(.205,.056),l(.112,-.131),l(.522,-.056),l(.519,.005),l(.357,.089),l(.469,.022),l(.313,-.156),l(.179,-.291),l(.134,-.268),l(.536,.246),l(.536,-.022),l(.67,-.223),l(.692,.112),l(.514,-.134),l(.201,.268),l(.312,.134),l(.246,.335),l(.134,.201),l(.246,.156),l(.312,.156),l(0,.268),l(-.312,-.022),l(-.312,.134),l(.134,.291),l(.111,.357),l(.269,.29),l(.647,0),l(.156,.112),l(.514,-.067),l(.38,.022),l(0,.312),l(.402,0),l(0,.357),l(.224,.268),l(.089,.246),l(-.089,.179),l(.089,.224),l(.179,.089),l(.291,.29),l(.268,-.179),l(.47,-.067),l(.268,.067),l(.469,.291),l(.201,-.067),l(.179,.022),l(.179,.156),l(.425,-.112),l(.312,-.112),l(.269,0),l(.536,-.134),l(.357,-.067),l(.111,.156),l(.268,.179),l(0,.134),l(.201,.179),l(.022,.134),l(.402,.044),l(.179,.179),l(.224,.112),l(.29,-.134),l(.045,-.157),l(.224,-.067),l(.29,.268),l(.425,.067),l(.469,.112),l(.268,.112),l(.357,-.067),l(.201,.179),l(.291,.089),l(.469,.022),l(.111,.224),l(.357,.156),l(.269,0),l(.134,-.044),l(.201,-.089),l(.156,.089),l(-.089,.111),l(-.022,.179),l(.111,.089),l(.09,.179),l(-.045,.224),l(-.201,.089),l(-.156,.067),l(-.357,.201),l(-.312,.044),l(.223,.246),l(.269,.089),l(.29,.044),l(-.134,.156),l(-.312,0),l(-.246,0),l(-.045,.179),l(-.044,.224),l(.156,.067),l(.179,.067),l(.044,.134),l(.045,.179),l(.09,.201),l(.066,.067),l(-.156,.491),l(-.156,.291),l(0,.156),l(-.335,.134),l(-.805,-.157),l(-.736,.045),l(-.269,0),l(-.022,.179),l(-.223,.179),l(-.38,.134),l(-.357,.022),l(-.224,.089),l(-.09,.514),l(0,.224),l(-.021,.112),l(-.012,.126),l(-.779,.104),l(-.971,.06),l(-.511,.405),l(-.729,.189),l(-1.135,.075),l(-1.119,.248),l(-.502,.318),l(-.463,.059),l(-.453,-.316),l(-.369,.621),l(-.31,.188),l(-.477,.044),l(-.438,-.057),l(-.959,.031),l(-.5,.16),l(.641,.287),l(1.957,1.004),l(.053,.172),l(-.093,.188),l(.163,.244),l(.562,.042),l(.511,-.13),l(.675,-.146),l(1.052,.013),l(.439,.114),l(-.235,.259),l(-.106,.245),l(-.228,.144),l(-.578,.116),l(-.31,.029),l(-.591,-.157),l(-.473,.044),l(-.71,.489),l(-1.007,.045),l(-.538,.188),l(-.527,.488),l(-.269,.101),l(-.786,-.07),l(-.588,-.171),l(.364,-.746),l(-.096,-.416),l(-.264,-.287),l(-.854,-.286),l(-.193,-.014),l(-.629,.016),l(-.151,.043),l(-.16,-.187),l(.887,-.505),l(.644,-.261),l(.772,-.188),l(.221,-.116),l(-.246,-.46),l(-.435,-.071),l(-.799,.044),l(-1.015,.045),l(-.698,-.1),l(-.195,-.101),l(-.418,-.432),l(.584,-.405),l(-.528,-.605),l(-.378,.361),l(-.541,.001),l(-1.001,.146),l(-.565,.131),l(-.694,.722),l(-1.003,.867),l(-.754,.203),l(-.223,.044),l(-.287,.504),l(.079,.158),l(.178,.093),l(-.706,-.131),l(-.665,.261),l(-.457,0),l(-.033,.189),l(-.609,-.047),l(-.398,-.166),l(-.119,-.249),l(-.15,.02),l(.055,-.077),l(.102,-.025),l(.126,.013),l(.113,.013),l(.189,0),l(.088,-.114),l(0,-.088),l(-.063,-.113),l(.025,-.113),l(.126,-.063),l(.051,-.063),l(.075,-.013),l(.089,-.025),l(.088,-.063),l(.089,-.088),l(.024,-.126),l(-.013,-.114),l(.14,-.013),l(.29,-.063),l(.075,-.076),l(-.025,-.088),l(-.062,-.088),l(.126,-.114),l(.037,-.063),l(-.012,-.088),l(-.114,-.113),l(.051,-.101),l(-.088,-.151),l(-.063,-.101),l(.202,-.151),l(.239,-.025),l(.126,-.088),l(.113,.025),l(.013,.088),l(-.013,.214),l(.063,.013),l(.113,0),V(0,96.92),l(-.013,-.063),l(.101,.038),l(.063,.051),l(.025,-.076),l(.075,-.038),l(.139,-.012),l(0,.075),l(.089,.063),l(.075,0),l(.126,.164),l(.076,-.076),l(.075,-.076),l(.013,-.05),l(.101,-.025),l(.177,0),l(-.037,.189),l(.176,.025),l(.038,-.038),l(.038,-.038),l(.139,.013),l(.227,0),l(.038,-.025),l(.075,-.076),l(-.126,-.013),l(-.164,-.126),l(-.101,-.051),l(-.075,-.05),l(.013,-.038),l(.101,-.063),l(-.025,-.113),l(.038,-.101),l(-.013,-.126),l(-.051,-.139),l(-.101,-.063),l(-.177,-.076),l(-.075,0),l(-.151,-.126),l(-.151,-.063),l(-.151,-.038),l(.051,-.151),l(.037,-.088),l(-.037,-.051),l(-.127,.038),l(-.062,-.114),l(.113,-.038),l(-.013,-.189),l(.089,-.075),l(-.025,-.101),l(-.038,-.088),l(-.113,0),l(-.102,.05),l(-.088,.051),l(-.113,-.088),l(-.089,-.101),l(-.188,-.101),l(-.139,-.025),l(-.102,-.139),l(-.05,-.139),l(.177,-.139),l(0,-.189),l(.024,-.114),l(.051,-.05),l(-.126,-.063),l(.164,-.151),l(-.113,-.025),l(-.076,-.063),l(-.062,-.126),l(-.14,-.013),l(-.062,.101),l(-.126,-.025),l(-.215,-.025),l(-.126,-.189),l(-.05,-.189),l(-.417,-.075),l(-.277,.012),l(-.062,.051),l(-.076,.101),l(-.062,-.05),l(0,-.076),l(-.089,-.025),l(-.101,.038),l(.038,-.05),l(.088,-.101),l(-.025,-.063),l(-.113,0),l(-.177,.038),l(-.126,-.025),l(-.101,.013),l(-.076,-.076),l(-.05,-.063),l(-.101,-.063),l(-.151,-.013),l(-.139,-.05),l(-.14,-.126),l(-.214,-.088),l(-.038,-.013),l(-.126,.025),l(-.05,.025),l(-.114,-.051),l(-.088,-.025),l(-.139,.025),l(-.177,.051),l(-.177,-.025),l(-.062,.038),l(-.126,.114),l(-.202,0),l(-.265,-.038),l(-.126,.051),l(-.315,-.114),l(-.088,.101),l(.012,.113),l(-.126,0),l(-.075,-.063),l(-.126,.114),l(-.06,.052),l(-.634,.08),l(-.151,.311),l(-.278,.178),l(-1.992,.191),l(-.186,.215),l(-.243,.119),l(-.339,.06),l(-.188,-.227),l(-.327,.004),l(-.025,-.231),l(-.363,.045),l(-1.115,-.066),l(-.958,-.193),l(-.241,.107),l(-.787,-.121),l(-.136,.085),l(-.678,-.387),l(-.554,-.2),l(-.668,-.301),l(-.166,.015),l(1.047,-1.471),l(.653,.018),l(-.349,-.383),l(-.044,-.552),l(.082,-.306),l(1.509,-1.218),l(.599,-.398),l(.286,-.181),l(.429,-.013),l(.255,-.24),l(.009,-.314),l(-.328,-.302),l(.085,-.133),l(.298,-.048),l(-.316,-.193),l(-.816,-.835),l(.074,-.242),l(-.161,-.175),
+N(660.044,89.132),l(-.295,.31),l(-.687,1.207),l(-1.224,1.959),l(-.381,.58),l(.269,.836),l(.051,.029),l(.342,-.045),l(.929,-.395),l(.754,-.062),l(.576,-.018),l(.317,.085),l(.431,.416),l(.292,.07),l(1.191,-.786),l(.438,-.002),l(.928,.212),l(.538,.199),l(.797,.5),l(.879,.99),l(.599,.501),l(.048,.273),l(-.107,.217),l(-.414,.218),l(-.464,-.127),l(-1.074,-.008),l(-.432,-.099),l(-.854,.033),l(-.937,.221),l(-.539,.146),l(-.831,.278),l(-.353,.189),l(-.483,-.127),l(-.464,.045),l(-.47,.204),l(-.363,.333),l(-.312,.82),l(-.241,.216),l(-.347,.188),l(-.638,.248),l(-.896,.134),l(-.624,-.054),l(-.438,-.012),l(-.224,-.013),l(-1.192,.91),l(-.742,.433),l(-.744,.047),l(-.982,.005),l(-.592,-.125),l(-.668,-.382),l(-.718,-.154),l(-.316,.073),l(-.457,.231),l(-.539,.589),l(-.214,.401),l(.003,.343),l(.389,.569),l(.599,.411),l(.188,.228),l(-.123,.271),l(-.326,.259),l(-1.265,.292),l(-.67,.389),l(-1.111,1.046),l(-.265,.172),l(-1.941,.737),l(-.651,.061),l(-.987,-.08),l(-1.514,.065),l(-1.339,.007),l(-1.204,.349),l(-.816,.289),l(-.736,.274),l(-.303,.101),l(-1.44,.534),l(-.686,.289),l(-.481,.017),l(-.433,-.197),l(-.253,-.297),l(-.61,-.067),l(-.663,.061),l(-.929,-.123),l(-1.599,-.433),l(-1.006,-.365),l(-.815,-.551),l(-.521,-.168),l(-1.69,-.119),l(-1.164,-.022),l(-.937,-.023),l(-2.861,.059),l(-1.165,-.022),l(-.802,-.109),l(-1.241,-.207),l(-1.979,-.018),l(-.444,-.254),l(-.467,-.439),l(-1.571,-2.161),l(-.105,-.542),l(-.744,-.096),l(-.839,-.31),l(-1.645,-.806),l(-.632,-.268),l(-.998,-.224),l(-.668,-.083),l(-.995,-.038),l(-1.505,-.021),l(-1.062,-.181),l(-.724,-.312),l(-.233,-.229),l(-.105,-.43),l(.035,-.129),l(.369,-.347),l(.214,-.389),l(.237,-.75),l(.215,-.447),l(-.401,-.66),l(-1.07,-1.451),l(-.568,-.618),l(-.354,-.143),l(-.633,-.144),l(-.731,-.167),l(-.614,-.069),l(-.834,-.415),l(-1.301,-.745),l(-.371,-.433),l(-.24,-.563),l(-.131,-.405),l(-.062,-.145),l(.154,-.044),l(.799,-.425),l(.599,-.207),l(1.387,-.08),l(.603,-.148),l(.727,-.381),l(.017,-.012),l(.971,-.692),l(.787,-.398),l(1.143,-.341),l(1.512,-.476),l(.84,-.18),l(.953,.097),l(.932,.156),l(1.842,.122),l(.831,.083),l(.694,.755),l(.393,.406),l(.699,.113),l(1.458,-.008),l(.719,.083),l(.85,-.004),l(.875,.068),l(.312,.114),l(.576,.186),l(.562,-.018),l(.755,-.28),l(.31,-.162),l(.744,-.572),l(.163,-.526),l(-.116,-.204),l(-.396,-.304),l(-.409,-.86),l(.098,-.293),l(.905,-.839),l(1.269,-.96),l(.84,.201),l(1.028,.098),l(1.036,.185),l(1.748,.328),l(.702,.231),l(.989,.317),l(.767,.143),l(.145,.204),l(.004,.541),l(.182,.481),l(.408,.451),l(.421,.333),l(1.643,.531),l(.673,.113),l(2.48,-.538),l(.796,-.077),l(1.172,.037),l(1.423,.022),l(.769,.229),l(1.333,.75),l(.623,.331),l(1.132,.313),l(.812,.373),l(1.318,.254),l(.905,.241),l(.984,.082),l(.739,.039),l(1.602,-.11),l(1.018,-.063),l(.532,-.075),l(.867,-.106),l(1.147,-.136),l(.526,-.163),l(.604,-.264),l(.447,-.394),l(.755,-.498),l(1.165,-.487),l(.333,-.002),l(.609,-.047),l(.74,.156),l(.751,.506),l(.34,.129),l(.86,.169),l(1.228,-.297),l(.622,-.018),l(.431,.168),
+N(406.183,86.551),l(1.051,-.494),l(.485,-.089),l(.574,.087),l(.465,-.016),l(.209,-.147),l(.477,.098),l(.407,.042),l(.52,-.034),l(-.025,-.157),l(.307,.012),l(.307,0),l(.267,-.182),l(.313,.242),l(.173,-.121),l(.228,.061),l(.292,.375),l(.535,-.109),l(.754,.375),l(-.11,.423),l(-.172,.097),l(.001,.338),l(.672,-.024),l(.344,.177),l(.282,.365),l(.038,.468),l(-.422,.376),l(-.225,-.072),l(-.142,.08),l(-.245,.147),l(-.213,.322),l(.017,.327),l(.31,.204),l(-.136,.348),l(-.079,-.114),l(-.694,.174),l(-.127,-.228),l(-.371,-.204),l(-.341,-.192),l(-.529,-.048),l(.039,-.228),l(-.146,-.18),l(.119,-.373),l(-.245,.072),l(-.193,.313),l(-.446,.035),l(-.406,.075),l(-.285,-.122),l(.072,-.198),l(-.091,-.175),l(.159,-.241),l(-.375,-.168),l(-.576,-.048),l(-.259,.012),l(-.159,-.301),l(-.518,.012),l(-.194,-.133),l(-.202,-.458),l(-.153,-.17),l(-.41,.208),l(-.141,.071),l(-.266,-.127),l(-.311,-.335),l(-.208,-.447),
+N(438.22,91.952),l(.039,-.044),l(.065,-.105),l(.014,-.131),l(.092,-.066),l(.146,-.119),l(.026,-.04),l(.171,-.053),l(.093,-.026),l(.092,.053),l(.132,.053),l(.158,0),l(.065,-.026),l(.093,0),l(.065,.026),l(.065,.026),l(.093,-.026),l(.145,-.04),l(.132,0),l(.118,-.053),l(.079,-.053),l(.066,-.026),l(.105,-.026),l(.039,0),l(.053,-.079),l(.04,-.092),l(.079,-.079),l(.092,.026),l(.105,-.04),l(.145,-.066),l(.053,-.105),l(.053,-.079),l(.026,-.132),l(.026,-.092),l(.053,-.092),l(.118,-.013),l(.105,-.013),l(.132,-.079),l(.119,-.053),l(.118,-.092),l(.053,-.079),l(.132,-.066),l(.065,-.04),L(442,89.998),l(.145,.013),l(.105,.026),l(.066,-.04),l(.065,-.066),l(.071,.012),l(.285,.041),l(.03,.228),l(.43,-.048),l(.183,-.24),l(.193,.016),l(.062,-.112),l(.261,-.024),l(.194,.24),l(.073,.169),l(.331,-.025),l(.066,.18),l(-.026,.083),l(.003,.204),l(.389,-.083),l(.18,.12),l(.149,-.135),l(.104,-.177),l(.558,-.204),l(.168,.056),l(.483,-.046),l(.46,.254),l(.373,-.18),l(.073,-.137),l(.508,.041),l(.561,-.076),l(.129,.13),l(.703,.186),l(.104,.216),l(.424,.101),l(.831,.33),l(-1.047,1.471),l(-.629,.076),l(-.437,-.143),l(-.534,-.359),l(-1.062,.035),l(-.717,.047),l(-1.024,.759),L(444.857,93),l(-.59,-.072),l(-.499,.061),l(-.761,.134),l(-.255,.001),l(-.334,.568),l(-1.651,-.036),l(-.414,-.027),l(-.617,-.17),l(-.399,-.172),l(-.245,.146),l(-.761,-.547),l(-.155,-.26),l(.097,-.581),l(-.053,-.093),
+N(442.391,98.111),l(-.589,.203),l(-.433,.031),l(-.668,.047),l(-.58,-.098),l(-1.116,-.671),l(-1.412,-.612),l(-.215,-.197),l(-.364,-.333),l(-.304,-.59),l(.346,-.299),l(.154,-.294),l(-.204,-.188),l(.04,-.375),l(.409,-.062),l(.157,-.206),l(-.136,-.196),l(-.452,-.063),l(.223,-.197),l(.325,0),l(.164,.134),l(.701,-.054),l(.019,-.367),l(.636,-.291),l(.245,-.146),l(.399,.172),l(.617,.17),l(.414,.027),l(1.651,.036),l(.334,-.568),l(.255,-.001),l(.761,-.134),l(.499,-.061),l(.59,.072),l(.427,-.063),l(1.024,-.759),l(.717,-.047),l(1.062,-.035),l(.534,.359),l(.437,.143),l(.629,-.076),l(.166,-.015),l(.668,.301),l(.554,.2),l(.678,.387),l(-.45,.338),l(-1.125,.267),l(-.581,.408),l(-.968,1.451),l(-.63,.84),l(-.753,.567),l(-.361,.16),l(-.724,.047),l(-.264,.103),l(-.176,-.002),l(-.907,-.067),l(-.889,.077),l(-1.535,.529),
+N(459.717,92.836),l(.06,-.052),l(.126,-.114),l(.075,.063),l(.126,0),l(-.012,-.113),l(.088,-.101),l(.315,.114),l(.126,-.051),l(.265,.038),l(.202,0),l(.126,-.114),l(.062,-.038),l(.177,.025),l(.177,-.051),l(.139,-.025),l(.088,.025),l(.114,.051),l(.05,-.025),l(.126,-.025),l(.038,.013),l(.214,.088),l(.14,.126),l(.139,.05),l(.151,.013),l(.101,.063),l(.05,.063),l(.076,.076),l(.101,-.013),l(.126,.025),l(.177,-.038),l(.113,0),l(.025,.063),l(-.088,.101),l(-.038,.05),l(.101,-.038),l(.089,.025),l(0,.076),l(.062,.05),l(.076,-.101),l(.062,-.051),l(.277,-.012),l(.417,.075),l(.05,.189),l(.126,.189),l(.215,.025),l(.126,.025),l(.062,-.101),l(.14,.013),l(.062,.126),l(.076,.063),l(.113,.025),l(-.164,.151),l(.126,.063),l(-.051,.05),l(-.024,.114),l(0,.189),l(-.177,.139),l(.05,.139),l(.102,.139),l(.139,.025),l(.188,.101),l(.089,.101),l(.113,.088),l(.088,-.051),l(.102,-.05),l(.113,0),l(.038,.088),l(.025,.101),l(-.089,.075),l(.013,.189),l(-.113,.038),l(.062,.114),l(.127,-.038),l(.037,.051),l(-.037,.088),l(-.051,.151),l(.151,.038),l(.151,.063),l(.151,.126),l(.075,0),l(.177,.076),l(.101,.063),l(.051,.139),l(.013,.126),l(-.038,.101),l(.025,.113),l(-.101,.063),l(-.013,.038),l(.075,.05),l(.101,.051),l(.164,.126),l(.126,.013),l(-.075,.076),l(-.038,.025),l(-.227,0),l(-.139,-.013),l(-.038,.038),l(-.038,.038),l(-.176,-.025),l(.037,-.189),l(-.177,0),l(-.101,.025),l(-.013,.05),l(-.075,.076),l(-.076,.076),l(-.126,-.164),l(-.075,0),l(-.089,-.063),l(0,-.075),l(-.139,.012),l(-.075,.038),l(-.025,.076),l(-.063,-.051),l(-.101,-.038),l(.013,.063),l(0,.088),l(-.113,0),l(-.063,-.013),l(.013,-.214),l(-.013,-.088),l(-.113,-.025),l(-.126,.088),l(-.239,.025),l(-.202,.151),l(.063,.101),l(.088,.151),l(-.051,.101),l(.114,.113),l(.012,.088),l(-.037,.063),l(-.126,.114),l(.062,.088),l(.025,.088),l(-.075,.076),l(-.29,.063),l(-.14,.013),l(.013,.114),l(-.024,.126),l(-.089,.088),l(-.088,.063),l(-.089,.025),l(-.075,.013),l(-.051,.063),l(-.126,.063),l(-.025,.113),l(.063,.113),l(0,.088),l(-.088,.114),l(-.189,0),l(-.113,-.013),l(-.126,-.013),l(-.102,.025),l(-.055,.077),l(-.03,.004),l(-.062,-.237),l(-.218,-.106),l(.16,-.071),l(-.021,-.267),l(-.104,-.561),l(.323,-.978),l(.027,-.404),l(-.353,-.856),l(-.604,-.286),l(-1.037,-1.119),L(460.567,93),l(-.626,-.191),l(-.225,.028),
+N(445.722,97.573),l(.176,.002),l(.264,-.103),l(.724,-.047),l(.361,-.16),l(.753,-.567),l(.63,-.84),l(.968,-1.451),l(.581,-.408),l(1.125,-.267),l(.45,-.338),l(.136,-.085),l(.787,.121),l(.241,-.107),l(.958,.193),l(1.115,.066),l(.363,-.045),l(.025,.231),l(.327,-.004),l(.188,.227),l(.339,-.06),l(.243,-.119),l(.186,-.215),l(1.992,-.191),l(.278,-.178),l(.151,-.311),l(.634,-.08),l(.225,-.028),L(460.567,93),l(.767,1.17),l(1.037,1.119),l(.604,.286),l(.353,.856),l(-.027,.404),l(-.323,.978),l(.104,.561),l(.021,.267),l(-.16,.071),l(.218,.106),l(.062,.237),l(.03,-.004),l(.15,-.02),l(.119,.249),l(.398,.166),l(.609,.047),l(.033,-.189),l(.457,0),l(.665,-.261),l(.706,.131),l(.149,.079),l(.062,.259),l(-.293,.446),l(-.27,.316),l(-.436,.044),l(-.382,.043),l(-.382,.245),l(-.515,.617),l(-.252,.645),l(-.096,.787),l(-.044,.223),l(-.671,-.12),l(-1.346,-.336),l(-.514,-.226),l(-.295,-.042),l(-.671,-.369),l(-.562,-.04),l(-.618,.218),l(-1.904,.771),l(-.38,.059),l(-1.385,-.35),l(-.3,-.013),l(-.69,.261),l(-.34,.031),l(-1.151,-.395),l(-.506,-.002),l(-.771,.189),l(-.266,.023),l(-.048,-.189),l(.234,-.318),l(-.352,-.106),l(-.392,-.204),l(-.418,-.186),l(-.146,-.33),l(.32,-.201),l(.351,.012),l(-.114,-.13),l(-.625,-.248),l(-.253,.13),l(-.215,.283),l(-.147,.118),l(-.414,-.239),l(-.194,-.139),l(-.594,-.059),l(-.02,-.189),l(-.234,0),l(-.245,-.036),l(-.052,-.165),l(.178,-.094),l(.271,-.071),l(-.239,-.083),l(-.183,-.059),l(.124,-.146),l(.19,-.127),l(-.069,-.142),l(-.306,-.118),l(-.555,-.141),l(-.712,-.471),l(.058,-.088),l(-.104,-.119),l(.075,-.356),l(-.202,-.036),l(-.19,-.237),l(-.569,-.178),l(-.054,-.309),
+N(420.177,113.472),l(-.274,-.042),l(-.253,-.155),l(-.367,-.325),l(-.096,-.213),l(.202,-.738),l(.097,-.681),l(-.046,-.583),l(-.133,-.569),l(-.503,-.44),l(-.094,-.271),l(.181,-.157),l(.366,-.015),l(.801,-.001),l(.339,-.172),l(.861,-.543),l(.633,.625),l(.451,.754),l(-.014,.271),l(-.204,.285),l(-.145,.484),l(.149,.894),l(-.11,.525),l(-.377,.695),l(-.405,-.198),l(-.52,.03),l(-.143,.1),l(-.149,.27),l(-.248,.17),M(433.783,118.446),l(-.712,-.084),l(-.902,-.607),l(-.772,-.239),l(-1.904,-.817),l(-.833,-.126),l(-.232,-.127),l(-.173,-.283),l(.139,-.34),l(.328,-.34),l(.264,-.1),l(.629,.112),l(.569,-.341),l(.68,.424),l(.403,.141),l(.722,-.016),l(1.403,-.187),l(1.38,-.329),l(.148,.085),l(.043,.127),l(-.112,.127),l(-.536,.823),l(-.153,.497),l(.009,.382),l(.411,.509),l(-.179,.128),l(-.43,.567),l(-.188,.015),M(431.244,98.829),l(-.281,-.329),l(-.242,-.027),l(-.281,.196),l(-.156,-.125),l(-.47,-.071),l(-.114,.32),l(-.458,.054),l(-1.001,.364),l(.078,-.151),l(-.452,.133),l(-.063,.249),l(-.157,.044),l(-.01,.125),l(.303,.08),l(.021,.302),l(.193,.119),l(.253,.236),l(-.104,.213),l(-.449,.254),l(.016,.272),l(.143,.554),l(.783,.814),l(2.008,.889),l(.29,.357),l(.134,.558),l(.274,.557),l(.395,.585),l(.694,.57),l(.254,.274),l(.446,.195),l(.041,.21),l(.408,.167),l(1.17,.255),l(1.254,-.105),l(.388,.141),l(.024,.212),l(-.465,.247),l(-.258,.294),l(.262,.213),l(.954,.283),l(1.168,.411),l(.829,.366),l(1.589,.739),l(.058,.185),l(.719,.458),l(.31,.475),l(-.198,.435),l(-.152,.337),l(-.455,-.281),l(-.318,-.167),l(-.109,-.486),l(-.263,-.17),l(-.512,-.099),l(-.483,-.009),l(-.439,-.236),l(.086,-.217),l(-.353,-.065),l(-.301,.098),l(-.232,.262),l(-.259,.399),l(-.273,.208),l(.043,.271),l(-.197,.303),l(-.007,.298),l(.76,.342),l(.611,.271),l(-.093,.314),l(.03,.432),l(.133,.142),l(-.191,.238),l(-.659,-.024),l(-.41,.219),l(-.202,.228),l(.11,.595),l(-.536,.303),l(-.617,.866),l(-.595,.048),l(-.167,-.071),l(-.184,-.14),l(-.002,-.508),l(.364,-.141),l(.317,-.542),l(-.236,-.184),l(.361,-.249),l(.361,.074),l(.133,-.17),l(-.077,-.34),l(-.211,-.181),l(-.206,-.924),l(-.367,-.516),l(-.15,-.607),l(-.201,-.352),l(-.334,.058),l(-.187,.171),l(-.899,-.496),l(-.286,-.065),l(.208,-.291),l(-.092,-.398),l(-.461,-.34),l(-.909,.247),l(.034,-.109),l(.322,-.194),l(-.276,-.27),l(-.29,-.003),l(-.42,.19),l(-.242,-.512),l(-.198,-.207),l(-.124,-.228),l(-.663,-.241),l(-.505,-.027),l(-.654,-.127),l(-.745,-.355),l(-.548,-.441),l(-.959,-.612),l(-1.036,-.826),l(-.872,-.384),l(-.805,-.67),l(-.566,-.856),l(-.434,-1.043),l(-.347,-.443),l(-.505,-.457),l(-.483,-.243),l(-1.188,-.341),l(-.579,-.142),l(-.5,.044),l(-1.078,.647),l(-.46,.359),l(-.646,.173),l(-.303,.043),l(.146,-.469),l(-.062,-.281),l(-.849,.07),l(-.754,-.391),l(-.193,-.442),l(.315,-.371),l(.175,-.01),l(-.135,-.331),l(-.616,-.191),l(-.352,-.358),l(.437,-.186),l(.183,.111),l(.541,-.353),l(.199,-.272),l(-.43,-.192),l(-.025,-.292),l(-.532,-.344),l(.624,-.301),l(.599,.062),l(.627,-.204),l(.629,.168),l(.275,-.16),l(.349,-.432),l(-.103,-.212),l(.777,-.404),l(.016,.415),l(.534,.363),l(.311,.071),l(-.098,.182),l(.385,.312),l(.285,-.151),l(.018,-.535),l(.425,-.384),l(-.019,-.333),l(.371,-.081),l(.143,.354),l(.23,.142),l(.216,-.03),l(.071,-.122),l(.469,-.05),l(.244,.333),l(.228,-.415),l(-.244,-.131),l(.081,-.273),l(.283,-.091),l(.176,.162),l(.315,.051),l(.038,-.192),l(-.112,-.212),l(.126,-.309),l(.631,.171),l(.597,.034),l(.329,-.411),l(.366,-.096),l(.183,.083),l(.445,-.11),l(.301,.103),l(.856,-.227),l(.023,.363),l(.318,.096),l(.32,.391),l(1.311,.247),l(.894,.082),l(.478,.112),l(.116,.199),l(-.614,.303),l(.098,.151),l(.297,.002),l(.187,.185),l(-.367,.285),l(.336,.089),l(-.127,.361),l(.36,.11),l(.284,.198),l(-.056,.214),
+N(430.73,96.731),l(1.04,.065),l(.179,.107),l(.612,-.009),l(.287,.152),l(.646,-.5),l(.566,-.107),l(.85,.08),l(.298,-.196),l(.89,.116),l(-.082,-.393),l(.693,-.157),l(.304,.59),l(.364,.333),l(-.035,-.009),l(-.1,-.073),l(-.145,-.036),l(-.172,0),l(-.145,.009),l(-.055,.063),l(0,.072),l(.019,.09),l(.009,.082),l(-.063,.009),l(-.136,-.009),l(-.108,-.036),l(-.091,.063),l(-.045,.082),l(-.081,.063),l(-.082,.045),l(-.081,.009),l(-.163,.036),l(-.117,.036),l(-.108,.036),l(-.055,.045),l(-.153,-.009),l(-.127,.072),l(-.063,.054),l(-.018,.082),l(.036,.072),l(.081,.054),l(.063,.055),l(.045,.045),l(.019,.063),l(.018,.09),l(-.036,.108),l(-.018,.063),l(-.046,.1),l(-.108,0),l(-.081,-.009),l(-.091,.027),l(-.108,.009),l(-.117,.054),l(-.091,.018),l(-.081,.027),l(-.1,.045),l(-.055,.063),l(-.036,.027),l(.055,.018),l(.063,.009),l(.026,.027),l(.037,.072),l(-.046,.063),l(-.027,.009),l(-.081,.027),l(-.009,.045),l(.045,.081),l(0,.072),l(.045,.1),l(-.054,.072),l(-.063,-.018),l(-.1,.045),l(-.117,.018),l(-.127,-.036),l(-.063,-.027),l(-.1,-.063),l(-.099,0),l(-.063,-.027),l(-.118,-.045),l(-.018,.045),l(-.027,.045),l(-.1,.027),l(-.136,0),l(-.054,-.045),l(-.072,-.063),l(-.127,-.018),l(-.019,-.09),l(-.026,-.018),l(-.063,-.054),l(-.055,-.027),l(-.018,-.054),l(-.01,-.054),l(-.036,-.009),l(-.063,.018),l(-.036,.054),l(-.009,.027),l(-.054,.063),l(-.019,.018),l(-.018,.081),l(-.063,.045),l(-.046,.018),l(-.062,.054),l(-.036,.009),l(-.254,0),l(-.108,-.027),l(-.108,.027),l(-.145,.009),l(-.1,-.009),l(-.1,-.036),l(-.045,-.019),l(-.055,0),l(0,.037),l(0,.036),l(-.045,.027),l(-.045,.018),l(-.136,-.009),l(-.027,-.036),l(-.108,.018),l(-.019,.018),l(-.136,.018),l(-.063,.018),l(-.126,.018),l(-.272,-.063),l(.428,-.077),l(.113,-.16),l(.056,-.214),l(-.284,-.198),l(-.36,-.11),l(.127,-.361),l(-.336,-.089),l(.367,-.285),l(-.187,-.185),l(-.297,-.002),l(-.098,-.151),l(.614,-.303),l(-.116,-.199),
+N(439.573,104.709),l(-1.051,-.672),l(-.185,-.222),l(-.783,-.149),l(-.203,-.159),l(-.403,-.115),l(-.683,.177),l(-.326,-.486),l(-1.112,-.627),l(-.584,-.678),l(.277,.007),l(.608,.016),l(-.583,-.221),l(-.659,-.469),l(-.183,-.407),l(.086,-.452),l(-.289,-.336),l(-.646,-.418),l(-.378,-.126),l(-.258,.579),l(-.142,.116),l(.03,.15),l(-.284,.106),l(-.154,.248),l(-.213,.053),l(-.496,-.647),l(-.063,-.286),l(-.259,-.612),l(.065,-.012),l(.272,.063),l(.126,-.018),l(.063,-.018),l(.136,-.018),l(.019,-.018),l(.108,-.018),l(.027,.036),l(.136,.009),l(.045,-.018),l(.045,-.027),l(0,-.036),l(0,-.037),l(.055,0),l(.045,.019),l(.1,.036),l(.1,.009),l(.145,-.009),l(.108,-.027),l(.108,.027),l(.254,0),l(.036,-.009),l(.062,-.054),l(.046,-.018),l(.063,-.045),l(.018,-.081),l(.019,-.018),l(.054,-.063),l(.009,-.027),l(.036,-.054),l(.063,-.018),l(.036,.009),l(.01,.054),l(.018,.054),l(.055,.027),l(.063,.054),l(.026,.018),l(.019,.09),l(.127,.018),l(.072,.063),l(.054,.045),l(.136,0),l(.1,-.027),l(.027,-.045),l(.018,-.045),l(.118,.045),l(.063,.027),l(.099,0),l(.1,.063),l(.063,.027),l(.127,.036),l(.117,-.018),l(.1,-.045),l(.063,.018),l(.054,-.072),l(-.045,-.1),l(0,-.072),l(-.045,-.081),l(.009,-.045),l(.081,-.027),l(.027,-.009),l(.046,-.063),l(-.037,-.072),l(-.026,-.027),l(-.063,-.009),l(-.055,-.018),l(.036,-.027),l(.055,-.063),l(.1,-.045),l(.081,-.027),l(.091,-.018),l(.117,-.054),l(.108,-.009),l(.091,-.027),l(.081,.009),l(.108,0),l(.046,-.1),l(.018,-.063),l(.036,-.108),l(-.018,-.09),l(-.019,-.063),l(-.045,-.045),l(-.063,-.055),l(-.081,-.054),l(-.036,-.072),l(.018,-.082),l(.063,-.054),l(.127,-.072),l(.153,.009),l(.055,-.045),l(.108,-.036),l(.117,-.036),l(.163,-.036),l(.081,-.009),l(.082,-.045),l(.081,-.063),l(.045,-.082),l(.091,-.063),l(.108,.036),l(.136,.009),l(.063,-.009),l(-.009,-.082),l(-.019,-.09),l(0,-.072),l(.055,-.063),l(.145,-.009),l(.172,0),l(.145,.036),l(.1,.073),l(.035,.009),l(.215,.197),l(1.412,.612),l(1.116,.671),l(.58,.098),l(.668,-.047),l(.433,-.031),l(.589,-.203),l(.201,.142),l(.056,.089),l(.022,.112),l(-.022,.078),l(.045,.044),l(.011,.067),l(-.078,.056),l(-.011,.146),l(.078,.067),l(.145,-.034),l(.101,.034),l(.045,.089),l(-.078,.011),l(-.056,-.022),l(-.022,.078),l(.033,.1),l(-.045,.034),l(-.044,.022),l(.066,.111),l(.168,-.022),l(.033,.078),l(.123,.1),l(.122,0),l(.101,0),l(.09,.078),l(.122,.011),l(.134,0),l(.012,.078),l(-.033,.056),l(-.135,-.011),l(-.089,-.034),l(-.067,.022),l(-.078,-.011),l(-.066,-.045),l(-.056,-.011),l(-.045,.011),l(.033,.067),l(-.101,.089),l(-.078,0),l(0,.156),l(.045,.067),l(-.033,.078),l(.022,.078),l(.011,.078),l(-.089,.033),l(-.09,-.033),l(-.056,.067),l(.078,.089),l(-.078,.011),l(-.189,.022),l(-.201,-.022),l(-.145,-.123),l(.056,-.101),l(-.045,-.089),l(-.123,-.011),l(-.022,-.112),l(-.145,-.056),l(-.146,-.045),l(-.101,.089),l(-.1,-.011),l(-.156,-.078),l(-.067,-.022),l(-.146,0),l(-.156,-.045),l(-.111,.067),l(-.134,.045),l(-.134,-.045),l(-.111,-.067),l(-.112,0),l(-.122,.089),l(-.168,.078),l(-.156,-.067),l(-.268,-.089),l(-.179,.011),l(-.156,.011),l(-.189,-.056),l(-.168,-.011),l(-.156,-.089),l(-.089,.078),l(-.111,.022),l(-.057,-.056),l(-.234,-.078),l(-.156,-.056),l(-.134,-.045),l(-.089,-.011),l(-.134,.123),l(-.112,-.011),l(-.223,-.022),l(-.168,-.033),l(-.212,.022),l(-.101,.111),l(-.145,.145),l(-.123,.201),l(-.201,-.022),l(-.256,-.134),l(-.156,-.19),l(-.101,-.111),l(-.312,-.034),l(-.123,.044),l(-.089,.179),l(-.045,.167),l(.045,.134),l(0,.078),l(.033,.212),l(-.123,.067),l(.022,.089),l(.134,.078),l(.09,.089),l(.122,.034),l(.101,.033),l(.179,.179),l(.146,.234),l(.089,.134),l(.022,.123),l(.156,.111),l(-.078,.056),l(-.012,.1),l(.022,.146),l(.168,-.011),l(.089,.111),l(.056,.123),l(.112,.111),l(.167,.045),l(.167,.033),l(.369,.357),l(.021,.167),l(.078,.044),l(.213,.078),l(.379,.357),l(.224,.123),l(.223,.067),l(.101,.056),l(0,.112),l(.078,.279),l(.201,.078),l(.189,.167),l(.146,.112),l(.245,.123),l(.067,.212),l(-.284,.083),M(439.792,104.833),l(.132,-.118),l(.134,.011),l(.123,.034),l(.045,.078),l(.066,.089),l(.146,.089),l(.179,.078),l(.212,.011),l(.312,.257),l(.045,.067),l(.134,-.033),l(.123,.022),l(.089,.034),l(.062,.063),l(.005,.004),l(-.022,.089),l(.033,.078),l(.082,.072),l(.029,.092),l(-.002,.1),l(-.589,-.367),l(-.549,-.371),l(-.789,-.378),
+N(451.009,101.725),l(-.328,.346),l(-.383,.374),l(-.18,.302),l(.056,.271),l(1.326,1.122),l(.028,.2),l(-.302,.302),l(-.762,.333),l(-.246,.301),l(-.008,.514),l(-.013,.208),l(-.058,-.017),l(-.072,.029),l(-.16,.022),l(-.145,.021),l(-.116,.022),l(-.058,.015),l(-.102,-.051),l(-.087,.043),l(-.088,.021),l(-.102,-.043),l(-.064,-.021),l(-.131,.116),l(-.087,.08),l(-.152,-.015),l(-.196,-.007),l(-.064,.007),l(-.175,-.043),l(-.152,.087),l(-.151,.102),l(-.109,.058),l(.059,.072),l(-.029,.058),l(-.116,0),l(-.094,-.109),l(-.131,-.058),l(-.087,-.073),l(-.08,.065),l(-.116,.058),l(-.246,.058),l(-.225,.058),l(-.088,.058),l(-.058,.167),l(.029,.13),l(-.029,.072),l(-.072,.087),l(-.188,0),l(-.14,-.049),l(-.018,-.109),l(-.733,-.866),l(-.382,-.369),l(-.058,-.004),l(.109,-.286),l(0,-.067),l(-.078,-.067),l(-.101,0),l(-.056,-.056),l(.022,-.089),l(.111,-.033),l(.146,.011),l(.167,.033),l(.057,-.033),l(.021,-.067),l(.09,-.044),l(.134,-.022),l(.089,-.011),l(-.011,-.089),l(-.101,-.101),l(-.167,-.067),l(-.134,-.045),l(-.057,-.044),l(-.111,.022),l(-.078,-.045),l(-.033,-.067),l(-.123,-.101),l(-.078,-.1),l(-.066,-.022),l(-.067,.044),l(-.078,-.011),l(-.101,-.056),l(-.279,-.078),l(-.078,-.022),l(-.056,-.033),l(-.167,-.134),l(-.101,-.146),l(-.111,-.111),l(-.168,-.078),l(-.156,-.101),l(-.223,-.056),l(0,-.101),l(.179,-.101),l(.089,-.111),l(.078,-.011),l(.067,.034),l(.078,.044),l(.1,.022),l(.045,-.022),l(.012,-.134),l(.011,-.19),l(-.134,-.145),l(-.179,-.19),l(-.212,-.134),l(-.101,-.145),l(.101,.022),l(.101,.011),l(.145,.056),l(.224,.044),l(.134,-.078),l(.089,-.056),l(.067,-.078),l(-.089,-.044),l(-.135,-.022),l(-.089,-.089),l(-.123,-.078),l(-.156,-.089),l(-.033,-.101),l(-.045,-.1),l(-.212,.011),l(-.167,-.056),l(-.078,-.1),l(-.022,-.134),l(.078,-.067),l(0,-.089),l(-.033,-.1),l(.056,-.056),l(.066,-.078),l(.156,-.156),l(.156,-.223),l(.034,-.167),l(.056,-.1),l(-.022,-.067),l(-.123,-.022),l(-.179,-.011),l(-.156,0),l(-.212,.112),l(-.078,-.089),l(.056,-.067),l(.09,.033),l(.089,-.033),l(-.011,-.078),l(-.022,-.078),l(.033,-.078),l(-.045,-.067),l(0,-.156),l(.078,0),l(.101,-.089),l(-.033,-.067),l(.045,-.011),l(.056,.011),l(.066,.045),l(.078,.011),l(.067,-.022),l(.089,.034),l(.135,.011),l(.033,-.056),l(-.012,-.078),l(-.134,0),l(-.122,-.011),l(-.09,-.078),l(-.101,0),l(-.122,0),l(-.123,-.1),l(-.033,-.078),l(-.168,.022),l(-.066,-.111),l(.044,-.022),l(.045,-.034),l(-.033,-.1),l(.022,-.078),l(.056,.022),l(.078,-.011),l(-.045,-.089),l(-.101,-.034),l(-.145,.034),l(-.078,-.067),l(.011,-.146),l(.078,-.056),l(-.011,-.067),l(-.045,-.044),l(.022,-.078),l(-.022,-.112),l(-.056,-.089),l(-.201,-.142),l(1.535,-.529),l(.889,-.077),l(.907,.067),l(.054,.309),l(.569,.178),l(.19,.237),l(.202,.036),l(-.075,.356),l(.104,.119),l(-.058,.088),l(.712,.471),l(.555,.141),l(.306,.118),l(.069,.142),l(-.19,.127),l(-.124,.146),l(.183,.059),l(.239,.083),l(-.271,.071),l(-.178,.094),l(.052,.165),l(.245,.036),l(.234,0),l(.02,.189),l(.594,.059),l(.194,.139),l(.414,.239),l(.147,-.118),l(.215,-.283),l(.253,-.13),l(.625,.248),l(.114,.13),l(-.351,-.012),l(-.32,.201),l(.146,.33),l(.418,.186),
+N(551.198,117.997),l(-.351,-.48),l(-.236,-.126),l(-1.217,-.05),l(-.646,-.011),l(-.096,-.016),l(.091,-.726),l(-.062,-.503),l(.157,-.251),l(.062,-.22),l(-.503,-.094),l(-.534,-.283),l(-.566,-.189),l(-.471,.063),l(-.378,-.251),l(-1.132,-.597),l(-.565,-.22),l(-.943,-.597),l(-.314,.063),l(-1.006,-.503),l(-.377,-.44),l(-1.194,-.597),l(-1.384,-.975),l(0,-.283),l(-.188,-.44),l(-.283,-.188),l(-.408,-.597),l(-.126,-.566),l(-.22,-.377),l(-.881,-.251),l(-.188,.157),l(-.439,.063),l(-.535,-.126),l(-.439,.032),l(-.503,.094),l(-.314,-.157),l(-.691,-.314),l(.094,-.22),l(.157,-.188),l(-.188,-.22),l(.031,-.188),l(.188,-.157),l(-.439,-.283),l(0,-.22),l(-.032,-.22),l(-.251,-.22),l(-.534,-.094),l(-.692,-.095),l(-.22,-.314),l(-.346,-.032),l(-.629,-.377),l(-.472,-.095),l(-.188,.063),l(-.565,.157),l(.251,.251),l(.188,.377),l(-.597,-.283),l(-.283,0),l(-.126,.126),l(-.22,.346),l(-.283,.126),l(-.629,0),l(-.503,.251),l(-.503,.409),l(-.062,.628),l(.314,.409),l(-.126,.314),l(-1.383,.032),l(-1.03,-.063),l(.056,-8.174),l(5.658,-1.289),l(5.722,2.986),l(2.012,1.666),l(2.578,-.346),l(2.767,.188),l(1.1,-.409),l(.724,.723),l(.597,.22),l(.66,1.006),l(.66,-.314),l(-.031,1.038),l(-.188,.409),l(.031,.754),l(1.006,0),l(.221,.817),l(.346,1.069),l(.503,.063),l(.691,-.032),l(1.006,-.094),l(.346,0),l(.44,.314),l(.062,.283),l(-.062,.283),l(.44,.346),l(.66,0),l(.125,-.188),l(-.157,-.409),l(.504,-.44),l(.439,-.188),l(.221,-.472),l(.282,-.188),l(.943,-.377),l(.755,-.189),l(.534,-.471),l(.346,-.283),l(.22,-.063),l(.283,.126),l(.377,-.377),l(.322,-.031),l(.349,-.126),l(.441,.246),l(-.368,.172),l(-.368,.171),l(-.221,.049),l(-.073,.196),l(-.295,.049),l(-.294,.172),l(-.196,.147),l(-.441,.295),l(-.172,.098),l(-.024,.123),l(.294,.049),l(.295,.074),l(.146,.123),l(.418,-.147),l(.098,.221),l(.172,.221),l(.368,.27),l(.589,0),l(.393,0),l(.049,-.393),l(.221,.049),l(.196,-.196),l(.024,-.245),l(.196,.098),l(.196,.172),l(.172,.294),l(.049,.147),l(.393,.024),l(.147,-.024),l(.073,.246),l(.025,.098),l(.343,-.025),l(.319,.147),l(.245,.196),l(.516,.074),l(.466,.024),l(.172,.123),l(-.49,.221),l(-.197,.147),l(-.221,.147),l(-.49,-.024),l(-.245,-.049),l(.049,.171),l(0,.147),l(-.319,0),l(-.172,.049),l(-.343,.196),l(-.221,.196),l(-.271,.049),l(-.221,.196),l(-.245,-.147),l(-.319,-.098),l(-.294,-.098),l(-.221,.025),l(-.246,.073),l(-.318,-.073),l(-.042,.098),l(-.345,-.005),l(-.409,.031),l(-.188,-.283),l(-.251,-.063),l(-.126,-.188),l(.251,-.126),l(.409,-.346),l(.188,-.22),l(-.252,-.251),l(-.439,-.377),l(-.221,.251),l(-.471,.346),l(-.692,.188),l(-.22,.157),l(-.252,-.22),l(-.22,-.157),l(-.346,0),l(.031,.22),l(-.283,.314),l(.189,.314),l(-.032,.346),l(-.062,.126),l(-.472,-.095),l(-.565,.095),l(-.503,.094),l(.251,.125),l(.534,-.031),l(.126,.094),l(-.251,.063),l(-.188,.063),l(-.032,.346),l(-.188,0),l(-.251,.157),l(-.063,.409),l(-.282,.188),l(-1.069,-.094),l(-.629,-.126),l(-.472,.283),l(-.125,.471),l(.251,.283),l(.346,.188),l(.157,.157),l(.44,.032),l(.346,0),l(.126,.22),l(-.126,.22),l(-.031,.472),l(.126,.409),l(.471,.314),l(.126,.283),l(-.157,.22),l(-.503,.346),l(-.283,.503),l(-.377,.377),l(.063,.377),l(-.375,.843),
+N(439.792,104.833),l(-.113,-.054),l(-.105,-.07),l(.284,-.083),l(-.067,-.212),l(-.245,-.123),l(-.146,-.112),l(-.189,-.167),l(-.201,-.078),l(-.078,-.279),l(0,-.112),l(-.101,-.056),l(-.223,-.067),l(-.224,-.123),l(-.379,-.357),l(-.213,-.078),l(-.078,-.044),l(-.021,-.167),l(-.369,-.357),l(-.167,-.033),l(-.167,-.045),l(-.112,-.111),l(-.056,-.123),l(-.089,-.111),l(-.168,.011),l(-.022,-.146),l(.012,-.1),l(.078,-.056),l(-.156,-.111),l(-.022,-.123),l(-.089,-.134),l(-.146,-.234),l(-.179,-.179),l(-.101,-.033),l(-.122,-.034),l(-.09,-.089),l(-.134,-.078),l(-.022,-.089),l(.123,-.067),l(-.033,-.212),l(0,-.078),l(-.045,-.134),l(.045,-.167),l(.089,-.179),l(.123,-.044),l(.312,.034),l(.101,.111),l(.156,.19),l(.256,.134),l(.201,.022),l(.123,-.201),l(.145,-.145),l(.101,-.111),l(.212,-.022),l(.168,.033),l(.223,.022),l(.112,.011),l(.134,-.123),l(.089,.011),l(.134,.045),l(.156,.056),l(.234,.078),l(.057,.056),l(.111,-.022),l(.089,-.078),l(.156,.089),l(.168,.011),l(.189,.056),l(.156,-.011),l(.179,-.011),l(.268,.089),l(.156,.067),l(.168,-.078),l(.122,-.089),l(.112,0),l(.111,.067),l(.134,.045),l(.134,-.045),l(.111,-.067),l(.156,.045),l(.146,0),l(.067,.022),l(.156,.078),l(.1,.011),l(.101,-.089),l(.146,.045),l(.145,.056),l(.022,.112),l(.123,.011),l(.045,.089),l(-.056,.101),l(.145,.123),l(.201,.022),l(.189,-.022),l(.078,-.011),l(.212,-.112),l(.156,0),l(.179,.011),l(.123,.022),l(.022,.067),l(-.056,.1),l(-.034,.167),l(-.156,.223),l(-.156,.156),l(-.066,.078),l(-.056,.056),l(.033,.1),l(0,.089),l(-.078,.067),l(.022,.134),l(.078,.1),l(.167,.056),l(.212,-.011),l(.045,.1),l(.033,.101),l(.156,.089),l(.123,.078),l(.089,.089),l(.135,.022),l(.089,.044),l(-.067,.078),l(-.089,.056),l(-.134,.078),l(-.224,-.044),l(-.145,-.056),l(-.101,-.011),l(-.101,-.022),l(.101,.145),l(.212,.134),l(.179,.19),l(.134,.145),l(-.011,.19),l(-.012,.134),l(-.045,.022),l(-.1,-.022),l(-.078,-.044),l(-.067,-.034),l(-.078,.011),l(-.089,.111),l(-.179,.101),l(-.056,-.033),l(-.156,.056),l(-.112,-.022),l(-.066,-.044),l(-.112,.033),l(-.078,.056),l(.012,.078),l(.089,.1),l(.123,.167),l(.056,.101),l(-.056,.101),l(-.111,0),l(-.09,-.056),l(-.056,-.089),l(-.056,-.044),l(-.123,-.011),l(-.122,.056),l(-.168,.078),l(-.045,.101),l(-.044,.089),l(-.112,.101),l(.034,.089),l(.011,.1),L(442,104.458),l(-.134,.011),l(-.111,.022),l(-.101,.089),l(-.012,.134),l(.012,.112),l(.011,.145),l(.012,.044),l(.066,.112),l(.078,.089),l(.045,.101),l(-.09,.089),l(-.183,.108),l(-.062,-.063),l(-.089,-.034),l(-.123,-.022),l(-.134,.033),l(-.045,-.067),l(-.312,-.257),l(-.212,-.011),l(-.179,-.078),l(-.146,-.089),l(-.066,-.089),l(-.045,-.078),l(-.123,-.034),l(-.134,-.011),l(-.132,.118),
+N(450.198,105.998),l(.013,-.208),l(.008,-.514),l(.246,-.301),l(.762,-.333),l(.302,-.302),l(-.028,-.2),l(-1.326,-1.122),l(-.056,-.271),l(.18,-.302),l(.383,-.374),l(.328,-.346),l(.392,.204),l(.352,.106),l(-.234,.318),l(.048,.189),l(.266,-.023),l(.771,-.189),l(.506,.002),l(1.151,.395),l(.34,-.031),l(.69,-.261),l(.3,.013),l(1.385,.35),l(.38,-.059),l(1.904,-.771),l(.618,-.218),l(.562,.04),l(.671,.369),l(.295,.042),l(.514,.226),l(1.346,.336),l(.671,.12),l(-.066,.335),l(-.077,.258),l(-.261,.086),l(-.313,-.028),l(-.339,.129),l(-.327,.73),l(-.039,.586),l(-.075,.143),l(-.404,.115),l(-.338,.372),l(-.017,.257),l(.252,-.036),l(.255,.224),l(.033,.154),l(.391,.375),l(.01,.223),l(-1.333,-.005),l(-.527,-.111),l(-.497,.045),l(-.629,.374),l(-.498,.445),l(-.363,-.026),l(-.344,.216),l(.097,.327),l(-.086,.257),l(-1.117,.277),l(-.388,.031),l(-.619,-.21),l(-1.473,-.505),l(-.584,.06),l(-.799,.261),l(-1.855,.195),l(-.09,.029),l(-.047,-.199),l(.104,-.3),l(.006,-.499),l(-.225,-.469),l(-.358,-.383),l(-.666,-.296),l(-.134,-.213),l(.007,-.106),
+N(381.009,107),l(-.121,-.278),l(.138,-.4),l(.343,-.5),l(-.358,-.471),l(-.304,-.428),l(-.514,-.07),l(-.164,-.1),l(-.053,-.329),l(.163,-.243),l(.409,-.272),l(.365,-.101),l(.563,-.03),l(.634,-.03),l(.133,-.172),l(.068,-.415),l(.535,-.273),l(.763,.042),l(1.078,.37),l(.763,.07),l(.756,-.087),l(.577,-.173),l(.508,-.144),l(.354,-.001),l(.629,.285),l(.694,.156),l(.939,.084),l(1.538,.04),l(.583,.027),l(.957,.141),l(.491,-.158),l(.419,-.229),l(.531,.027),l(.891,.47),l(.67,-.016),l(.335,.062),l(.472,.243),l(.469,-.03),l(.058,.122),l(-.205,.243),l(.094,.106),l(.15,.03),l(.112,-.106),l(1.088,.334),l(.15,-.061),l(.507,.395),l(.056,-.076),l(.262,.03),l(.131,-.076),l(.431,.152),l(.028,.038),l(.084,.114),l(.767,-.03),l(.037,.122),l(.337,-.061),l(.542,.015),l(-.017,-.319),l(.355,0),l(1.252,.304),l(.091,.213),l(.035,.289),l(.187,.076),l(.374,-.076),l(.206,-.03),l(.335,.091),l(.036,.152),l(.261,.015),l(.395,-.167),l(.427,.197),l(.485,.015),l(.039,-.136),l(.75,-.137),l(.334,.091),l(-.001,.088),l(-.001,.463),l(.156,.1),l(-.062,.485),l(-1.112,.528),l(-.95,.385),l(-.267,.328),l(-1.046,.198),l(-.664,.116),l(-.96,.301),l(-.323,.326),l(-.053,.2),l(.261,.128),l(-.088,.157),l(-.628,.143),l(-.594,.783),l(-.886,.787),l(-.096,.192),l(-.18,.361),l(-.245,.45),l(.353,.827),l(.072,.111),l(.084,.13),l(.648,.295),l(.103,.185),l(-.621,.327),l(-.215,.105),l(-.515,.252),l(-.286,.479),l(-.224,.085),l(-.461,.926),l(.155,.322),l(-.257,.099),l(-.992,.049),l(-.581,.242),l(-.425,.327),l(-.274,.757),l(-.663,.496),l(-.258,-.213),l(-.599,.028),l(-.305,.27),l(-.342,0),l(-.121,-.113),l(-3.282,.042),l(-.69,.524),l(-1.021,.17),l(-.35,.382),l(-.028,.283),l(-.083,.085),l(-.073,-.212),l(-.068,-.014),l(.005,.241),l(-.389,.127),l(-.421,-.142),l(-.788,-.467),l(-.224,-.382),l(.036,-.262),l(-.345,-.113),l(-.125,-.213),l(.175,-.163),l(-.468,-.51),l(-.702,-.284),L(385,117.498),l(-.484,-.135),l(-.586,.039),l(.008,-.018),l(.304,-.951),l(.242,-.37),l(.884,-.643),l(-.408,-.31),l(-.812,-.123),l(.17,-.455),l(.506,-.655),l(.347,-.371),l(-.163,-.198),l(-.455,-.551),l(-.488,-.494),l(.288,-.129),l(.482,-.045),l(.458,-.229),l(.043,-.199),l(-.057,-.938),l(.132,-.983),l(-.072,-.456),l(.051,-.442),l(.084,-.072),l(1.234,-.506),l(.288,-.216),l(-.062,-.242),l(-.842,-.495),l(-.15,-.242),l(-.272,-.227),l(-.335,-.055),l(-.531,.26),L(382.981,107),l(-.531,-.439),l(-.55,.188),L(381.009,107),
+N(489.685,103.693),l(.112,-.309),l(.26,-.166),l(.284,.047),l(.07,.047),l(.402,.023),l(.449,.023),l(.283,.095),l(.284,.142),l(.188,.094),l(.189,.047),l(.331,0),l(.213,0),l(.212,.166),l(.261,.095),l(.307,.071),l(.355,.047),l(.307,0),l(.426,-.095),l(.544,0),l(.401,.166),l(.189,0),l(.283,-.047),l(.354,.166),l(.095,.142),l(.284,.213),l(.52,.118),l(.354,.071),l(.236,.118),l(.308,.119),l(-.142,.118),l(-.048,.118),l(.261,.118),l(.212,.071),l(.261,-.118),l(.283,0),l(.166,-.166),l(.094,-.095),l(.213,-.071),l(.354,0),l(.261,.071),l(.188,.142),l(.142,-.166),l(.095,-.071),l(.118,0),l(.236,.118),l(.143,.094),l(.212,0),l(.189,.118),l(.213,.166),l(.378,0),l(.354,.024),l(.118,.142),l(-.118,.189),l(-.118,.307),l(.354,.284),l(.284,.166),l(.26,.094),l(.284,.047),l(.236,-.023),l(.236,.071),l(.126,.189),l(-.268,.189),l(-.143,.142),l(-.095,.071),l(.143,.26),l(.213,.307),l(.614,.166),l(.118,.213),l(-.095,.331),l(-.236,.095),l(-.236,.047),l(-.26,-.189),l(-.143,-.071),l(-.188,-.023),l(-.284,.047),l(-.638,-.189),l(-.189,-.213),l(-.331,-.189),l(-.473,-.024),l(-.236,0),l(-.418,.308),l(-.291,.094),l(-.378,.047),l(-.591,.095),l(-.592,-.047),l(-.401,.118),l(-.426,.023),l(-.308,.095),l(-.307,-.024),l(-.377,.108),l(-.031,-.028),l(-1.326,-1.018),l(-.41,-.041),l(-.761,.36),l(-.226,.072),l(-.491,-.068),l(-1.212,-.082),l(.083,-.065),l(.322,-.585),l(.032,-.143),l(-.064,-.728),l(-.331,-1.084),l(-.206,-.399),l(-.639,-.513),l(-.341,-.128),l(-.916,-.155),l(-.679,-.271),l(-.341,-.243),
+N(443.617,107.095),l(-.065,-.035),l(-.435,-.156),l(-.017,-.15),l(-.501,-.485),l(-.848,-.3),l(-.033,-.021),l(.002,-.1),l(-.029,-.092),l(-.082,-.072),l(-.033,-.078),l(.022,-.089),l(-.005,-.004),l(.183,-.108),l(.09,-.089),l(-.045,-.101),l(-.078,-.089),l(-.066,-.112),l(-.012,-.044),l(-.011,-.145),l(-.012,-.112),l(.012,-.134),l(.101,-.089),l(.111,-.022),l(.134,-.011),l(.056,-.056),l(-.011,-.1),l(-.034,-.089),l(.112,-.101),l(.044,-.089),l(.045,-.101),l(.168,-.078),l(.122,-.056),l(.123,.011),l(.056,.044),l(.056,.089),l(.09,.056),l(.111,0),l(.056,-.101),l(-.056,-.101),l(-.123,-.167),l(-.089,-.1),l(-.012,-.078),l(.078,-.056),l(.112,-.033),l(.066,.044),l(.112,.022),l(.156,-.056),l(.056,.033),l(0,.101),l(.223,.056),l(.156,.101),l(.168,.078),l(.111,.111),l(.101,.146),l(.167,.134),l(.056,.033),l(.078,.022),l(.279,.078),l(.101,.056),l(.078,.011),l(.067,-.044),l(.066,.022),l(.078,.1),l(.123,.101),l(.033,.067),l(.078,.045),l(.111,-.022),l(.057,.044),l(.134,.045),l(.167,.067),l(.101,.101),l(.011,.089),l(-.089,.011),l(-.134,.022),l(-.09,.044),l(-.021,.067),l(-.057,.033),l(-.167,-.033),l(-.146,-.011),l(-.111,.033),l(-.022,.089),l(.056,.056),l(.101,0),l(.078,.067),l(0,.067),l(-.109,.286),l(-.361,-.022),l(-.727,-.11),l(-.273,.273),l(-.279,.515),l(.133,.427),l(-.002,.342),
+N(558.52,110.652),l(.042,-.098),l(.318,.073),l(.246,-.073),l(.221,-.025),l(.294,.098),l(.319,.098),l(.245,.147),l(.221,-.196),l(.271,-.049),l(.221,-.196),l(.343,-.196),l(.172,-.049),l(.319,0),l(0,-.147),l(-.049,-.171),l(.245,.049),l(.49,.024),l(.221,-.147),l(.197,-.147),l(.49,-.221),l(-.172,-.123),l(-.466,-.024),l(-.516,-.074),l(-.245,-.196),l(-.319,-.147),l(-.343,.025),l(-.025,-.098),l(-.073,-.246),l(-.147,.024),l(-.393,-.024),l(-.049,-.147),l(-.172,-.294),l(-.196,-.172),l(-.196,-.098),l(-.024,.245),l(-.196,.196),l(-.221,-.049),l(-.049,.393),l(-.393,0),l(-.589,0),l(-.368,-.27),l(-.172,-.221),l(-.098,-.221),l(-.418,.147),l(-.146,-.123),l(-.295,-.074),l(-.294,-.049),l(.024,-.123),l(.172,-.098),l(.441,-.295),l(.196,-.147),l(.294,-.172),l(.295,-.049),l(.073,-.196),l(.221,-.049),l(.368,-.171),l(.368,-.172),l(-.441,-.246),l(-.349,.126),l(-.044,-.273),l(.393,-.442),l(.318,-.368),l(.736,-.123),l(.663,-.098),l(.883,.147),l(.883,.245),l(.688,.196),l(.81,.123),l(.344,.123),l(-.024,-.442),l(.245,-.736),l(.466,-.368),l(.688,-.123),l(.589,.074),l(.761,.27),l(.735,.246),l(.908,.196),l(.54,.098),l(.441,-.27),l(.858,-.024),l(.761,0),l(.785,-.147),l(.712,.221),l(.662,.098),l(1.35,.024),l(.662,-.074),l(.981,.246),l(.564,-.049),l(.147,.344),l(.27,.147),l(.196,.27),l(.663,0),l(.466,.098),l(.41,.375),l(.031,.194),l(-.051,.157),l(-.325,.187),l(-.97,.219),l(-1.338,.349),l(-.445,.145),l(-.405,.301),l(-.638,.701),l(-.646,.345),l(-.478,.102),l(-.459,.017),l(-1.248,-.235),l(-.238,.03),l(-.467,.472),l(-.463,.784),l(-.268,.243),l(-.885,.132),l(-.507,.145),l(-.344,-.055),l(-.183,-.567),l(-.06,-.071),l(-.359,.03),l(-1.737,.734),l(-1.422,.704),l(-.274,.186),l(-.129,.213),l(-.139,.739),l(-.196,-.073),l(-.344,.098),l(-.344,.171),l(-.539,0),l(-.663,-.073),l(-.834,.221),l(-.172,.147),l(-.196,0),l(-.172,-.319),l(-.368,.024),l(-.318,.172),l(-.074,-.221),l(-.049,-.172),l(-.122,.024),l(-.319,-.123),l(-.049,-.147),l(-.221,-.024),l(-.442,.123),l(-.343,.049),l(.024,.221),l(-.295,.049),l(-.393,-.074),l(-.073,-.196),l(-.147,-.123),l(-.368,-.098),l(-.49,.147),l(-.196,-.073),l(-.688,.024),l(-.564,0),l(-.589,.024),l(-.122,-.098),l(-.049,-.147),l(-.099,-.27),l(.099,-.245),l(.196,-.196),l(.098,.221),l(.196,-.074),l(-.049,-.196),l(.098,-.27),l(.123,0),l(.981,-.196),l(.515,.147),l(.516,.196),l(.099,.172),l(.196,0),l(.024,-.246),l(.441,-.196),l(.302,-.147),
+N(685.343,114.455),l(-.571,.678),l(-.309,.115),l(-.511,-.096),l(-.579,-.068),l(-.595,-.011),l(-.315,.157),l(-.633,.738),l(-.283,.256),l(-.235,.171),l(-.268,-.206),l(-.35,.34),l(-.319,.199),l(-.373,-.608),l(-.398,-.112),l(-.649,.78),l(-.195,-.382),l(-.232,-.254),l(-.683,-.367),l(-.169,-.453),l(.095,-.312),l(.429,-.411),l(.754,-.229),l(.056,-.269),l(-.591,-.282),l(.407,-.879),l(.189,-.34),l(-.199,-.269),l(-.632,-.296),l(-.139,0),l(-.381,.029),l(-.312,.143),l(-.234,-.07),l(-.52,-.368),l(-.167,-.233),l(.379,-.528),l(.415,-.442),l(.52,-.329),l(1.533,-.604),l(1.032,-.545),l(.636,-.543),l(.686,-1.027),l(.386,-.13),l(.448,-.017),l(.273,.396),l(.493,.253),l(.508,.153),l(.975,-.048),l(.527,-.159),l(-.046,-.113),l(-.508,-.765),l(.025,-.342),l(.273,-.243),l(.392,-.059),l(.333,.126),l(.452,.054),l(.538,-.017),l(.62,-.259),l(.955,-.532),l(.23,-.713),l(.383,-.358),l(.253,-.129),l(.247,-.001),l(.579,.68),l(.298,.439),l(.167,.393),l(-1.356,.923),l(-.408,.457),l(-.112,.414),l(.09,.427),l(-.154,.456),l(-.187,.868),l(-.668,.115),l(-.36,.229),l(-.497,.385),l(-.766,.641),l(-.468,.214),l(-.678,.03),l(-.577,.199),l(-.265,.228),l(-.248,.312),l(-.364,.893),l(.284,.326),l(1.225,.847),l(.419,.354),
+N(536.625,121.017),l(-.078,-.028),l(-.15,-.692),l(-.01,-.565),l(-.038,-.848),l(-.185,-.211),l(-.787,.075),l(-.696,-.01),l(-.655,-.506),l(-1.803,-1.362),l(-.597,-.336),l(-.66,-.167),l(-.5,-.054),l(-.788,-.066),l(-.822,-.335),l(-.708,-.251),l(-.402,-.437),l(-1.055,-.107),l(-.519,-.054),l(-.343,.129),l(-.517,.343),l(-.333,.03),l(-.78,-.038),l(-.609,.032),l(-.413,.144),l(-.476,.328),l(-.621,.654),l(-.466,.3),l(-.562,.13),l(-.441,-.025),l(-.066,-.376),l(-.128,-.681),l(-.106,-.447),l(.128,-.298),l(0,-.383),l(0,-.532),l(.106,-.191),l(.106,-.298),l(.085,-.234),l(-.085,-.212),l(-.256,-.128),l(-.319,-.191),l(-.213,-.255),l(-.042,-.149),l(-.171,0),l(-.191,-.042),l(-.361,-.106),l(-.191,.192),l(-.086,-.234),l(.086,-.106),l(.148,-.255),l(.128,.106),l(.383,-.042),l(.426,.085),l(.128,.021),l(.043,-.128),l(-.319,-.213),l(-.256,-.021),l(-.085,-.277),l(.17,-.255),l(.213,-.191),l(-.404,-.042),l(-.319,.085),l(-.383,0),l(-.319,-.085),l(-.128,.149),l(-.17,-.255),l(-.149,-.298),l(0,-.34),l(-.042,-.298),l(.17,-.213),l(.106,-.319),l(.043,-.255),l(.105,-.277),l(.086,-.234),l(.213,.34),l(.063,.128),l(.17,.17),l(.405,-.085),l(.383,.128),l(.106,-.149),l(-.021,-.149),l(.106,0),l(.148,.021),l(.064,.319),l(.106,.191),l(.298,-.021),l(.298,-.063),l(.256,-.106),l(.233,.085),l(.192,.064),l(.085,-.128),l(-.149,-.191),l(-.042,-.213),l(.191,-.042),l(.106,.149),l(.233,.085),l(.256,-.085),l(.213,-.064),l(.021,-.234),l(-.171,-.341),l(-.34,-.234),l(-.532,-.319),l(-.426,-.213),l(-.063,-.319),l(-.043,-.34),l(-.213,-.17),l(0,-.213),l(0,-.213),l(-.085,-.127),l(-.554,-.064),l(-.617,.085),l(-.426,.021),l(-.446,.127),l(-.192,.277),l(-.085,.298),l(.128,.192),l(-.063,.276),l(-.086,.405),l(.064,.234),l(.021,.298),l(-.256,-.553),l(-.361,-.319),l(.042,-.17),l(-.063,-.191),l(-.274,-.143),l(.529,-.453),l(.937,-.532),l(1.277,-.298),l(.979,-.085),l(.512,.234),l(.681,.383),l(.617,.383),l(.256,.511),l(.638,.703),l(.447,.255),l(.489,-.043),l(.341,-.106),l(.158,.014),l(1.03,.063),l(1.383,-.032),l(.126,-.314),l(-.314,-.409),l(.062,-.628),l(.503,-.409),l(.503,-.251),l(.629,0),l(.283,-.126),l(.22,-.346),l(.126,-.126),l(.283,0),l(.597,.283),l(-.188,-.377),l(-.251,-.251),l(.565,-.157),l(.188,-.063),l(.472,.095),l(.629,.377),l(.346,.032),l(.22,.314),l(.692,.095),l(.534,.094),l(.251,.22),l(.032,.22),l(0,.22),l(.439,.283),l(-.188,.157),l(-.031,.188),l(.188,.22),l(-.157,.188),l(-.094,.22),l(.691,.314),l(.314,.157),l(.503,-.094),l(.439,-.032),l(.535,.126),l(.439,-.063),l(.188,-.157),l(.881,.251),l(.22,.377),l(.126,.566),l(.408,.597),l(.283,.188),l(.188,.44),l(0,.283),l(1.384,.975),l(1.194,.597),l(.377,.44),l(1.006,.503),l(.314,-.063),l(.943,.597),l(.565,.22),l(1.132,.597),l(.378,.251),l(.471,-.063),l(.566,.189),l(.534,.283),l(.503,.094),l(-.062,.22),l(-.157,.251),l(.062,.503),l(-.091,.726),l(-1.454,-.244),l(-.565,-.294),l(-.445,.356),l(-.417,.2),l(-1.135,.205),l(-.432,.809),l(-.203,.991),l(-.103,.128),l(-.508,.243),l(-1.985,.689),l(-.568,.159),l(-.119,.199),l(-.001,.466),l(-.22,.199),l(-.636,.3),l(-.534,.031),l(-.573,-.082),l(-.999,-.348),l(-.937,-.193),l(-.193,-.112),
+N(445.294,112.196),l(-.07,-.115),l(-.138,-.469),l(-.5,-.452),l(-.966,-.541),l(.024,-.141),l(.23,.062),l(.023,-.237),l(-.345,-.414),l(.418,-.616),l(-.182,-.22),l(.188,-.563),l(-.251,-.282),l(.182,-.396),l(.268,-.079),l(-.027,-.45),l(-.331,-.081),l(-.2,-.107),l(.002,-.342),l(-.133,-.427),l(.279,-.515),l(.273,-.273),l(.727,.11),l(.361,.022),l(.058,.004),l(.382,.369),l(.733,.866),l(.018,.109),l(.035,.218),l(-.132,.429),l(.074,.641),l(.298,.668),l(.722,.608),l(-.09,.029),l(-.449,.842),l(-.402,.386),l(-.496,.472),l(-.583,.884),
+N(451.512,108.463),l(-.507,.16),l(-.532,.245),l(-.622,-.054),l(-.361,-.041),l(-.365,.159),l(-.395,.429),l(-.606,.146),l(-.809,.076),l(-.722,-.608),l(-.298,-.668),l(-.074,-.641),l(.132,-.429),l(-.035,-.218),l(.14,.049),l(.188,0),l(.072,-.087),l(.029,-.072),l(-.029,-.13),l(.058,-.167),l(.088,-.058),l(.225,-.058),l(.246,-.058),l(.116,-.058),l(.08,-.065),l(.087,.073),l(.131,.058),l(.094,.109),l(.116,0),l(.029,-.058),l(-.059,-.072),l(.109,-.058),l(.151,-.102),l(.152,-.087),l(.175,.043),l(.064,-.007),l(.196,.007),l(.152,.015),l(.087,-.08),l(.131,-.116),l(.064,.021),l(.102,.043),l(.088,-.021),l(.087,-.043),l(.102,.051),l(.058,-.015),l(.116,-.022),l(.145,-.021),l(.16,-.022),l(.072,-.029),l(.058,.017),l(-.007,.106),l(.134,.213),l(.666,.296),l(.358,.383),l(.225,.469),l(-.006,.499),l(-.104,.3),l(.047,.199),
+N(383.93,117.402),l(-.249,.101),l(-.517,.291),l(-.439,.052),l(-.548,-.178),l(-.58,0),l(-.28,-.073),l(-.719,.292),l(-.058,-.177),l(.389,-1.012),l(-.021,-.856),l(-.182,-.115),l(.244,-.542),l(-.054,-.397),l(.13,-.114),l(-.144,-.141),l(-.375,.085),l(-.476,.097),l(-.108,-.449),l(.48,-.052),l(.283,-.22),l(-.042,-.17),l(-.178,-.226),l(-.3,.417),l(-.413,.136),l(-.357,-.042),l(-.059,-.188),l(.198,-.397),l(.138,-.616),l(-.039,-.303),l(.258,-.114),l(.403,-.503),l(.45,-1.098),l(-.12,-.115),l(.612,-1.783),l(-.35,-.924),l(-.007,-.42),l(-.146,-.378),l(.255,-.271),l(.891,-.251),l(.55,-.188),l(.531,.439),l(1.822,.047),l(.531,-.26),l(.335,.055),l(.272,.227),l(.15,.242),l(.842,.495),l(.062,.242),l(-.288,.216),l(-1.234,.506),l(-.084,.072),l(-.051,.442),l(.072,.456),l(-.132,.983),l(.057,.938),l(-.043,.199),l(-.458,.229),l(-.482,.045),l(-.288,.129),l(.488,.494),l(.455,.551),l(.163,.198),l(-.347,.371),l(-.506,.655),l(-.17,.455),l(.812,.123),l(.408,.31),l(-.884,.643),l(-.242,.37),l(-.304,.951),l(-.008,.018),
+N(500.121,117.572),l(-.407,-.016),l(-.433,.388),l(-.164,.126),l(-.318,-.105),l(-.102,-.269),l(.03,-.259),l(-.274,-.151),l(-.366,-.082),l(-.244,.234),l(-.343,-.023),l(-.811,-.153),l(-.364,.032),l(-.304,-.16),l(-.437,.094),l(-.266,.143),l(-.23,.043),l(-.064,-.245),l(-.207,-.023),l(-.24,.292),l(-.693,.304),l(-1.185,.224),l(-.711,-.039),l(-.747,-.123),l(-.439,.073),l(-1.498,.673),l(-.567,.13),l(-1.104,.176),l(-.556,-.153),l(-1.532,-.444),l(-.278,.03),l(-.929,.373),l(-.746,.075),l(-.575,-.025),l(-.777,-.166),l(-.222,.001),l(-.142,-.035),l(-.055,.319),l(.102,.452),l(.243,.423),l(-.627,.127),l(-.156,.374),l(-.2,.169),l(-.171,-.041),l(-.114,.127),l(-.39,-.125),l(-.311,.001),l(-.245,-.459),l(-.119,-.093),l(.097,-.175),l(.242,-.197),l(.617,-.403),l(.021,-.175),l(-.049,-.134),l(-.279,-.28),l(-.146,-.053),l(-.487,.368),l(-.23,.041),l(-.137,.064),l(.092,.041),l(-.118,.216),l(-.172,.023),l(-.063,-.047),l(-.076,.088),l(-.297,.058),l(-.332,-.222),l(-.447,-.198),l(-.461,-.157),l(-.395,.046),l(-.849,.548),l(-.337,.286),l(.006,.204),l(-.141,.046),l(-.122,.07),l(-.005,.082),l(-.179,-.169),l(-.604,.206),l(-.689,.185),l(-.594,-.013),l(-.587,-.07),l(-.678,-.267),l(-.963,-.819),l(-1.181,-.479),l(-1.034,-.182),l(-.692,.072),l(-.119,.255),l(-.097,.609),l(-.053,.411),l(-.173,.156),l(-.256,0),l(-.253,-.155),l(-1.12,.243),l(-.423,-.027),l(-.386,-.183),l(-.657,-1.159),l(-.42,.354),l(-.764,-.451),l(-.451,.057),l(-.562,.412),l(-.227,-.382),l(.066,-.127),l(.242,-.17),l(-.116,-.17),l(-.989,-.012),l(-.545,-.013),l(-.088,-.269),l(.571,-.199),l(-.074,-.241),l(-.284,-.198),l(-.454,-.07),l(-.084,-.297),l(.041,-.34),l(.087,-.284),l(-.089,-.255),l(-.396,-.126),l(-.627,-.353),l(-.371,.086),l(-.265,-.084),l(-.004,-.255),l(.171,-.501),l(.131,.059),l(.478,.311),l(.567,-.271),l(-.396,-.283),l(.021,-.124),l(-.296,-.128),l(.03,-.128),l(.571,-.159),l(.152,-.113),l(-.068,-.142),l(-.149,-.088),l(-.337,-.035),l(.01,-.187),l(.18,-.07),l(-.163,-.164),l(-.198,-.117),l(-.009,-.152),l(-.227,-.012),l(.263,-.181),l(.296,-.275),l(.161,-.035),l(.07,-.16),l(-.341,-.042),l(-.573,.12),l(-.905,.164),l(-.166,-.035),l(.046,-.33),l(.127,-.125),l(-.003,-.199),l(-.029,-.286),l(.13,-.264),l(.299,.012),l(.184,-.41),l(.175,-.023),l(.63,-.422),l(.514,.012),l(.133,-.129),l(.479,-.047),l(.128,.211),l(.268,.102),l(.169,.028),l(.529,.022),l(.147,-.129),l(-.067,-.129),l(-.269,-.129),l(.286,-.094),l(.324,.036),l(.117,.082),l(-.219,.223),l(.213,-.026),l(1.053,-.073),l(.619,.042),l(.379,.046),l(.279,.047),l(.155,-.176),l(-.086,-.094),l(-.468,-.035),l(-.212,-.118),l(.275,-.212),l(1.386,-.151),l(.417,-.012),l(.377,-.117),l(-.442,-.012),l(-.592,.023),l(-.215,0),l(-.068,-.146),l(-.611,-.382),l(.325,-.528),l(.926,.14),l(1.244,.048),l(.264,-.117),l(1.086,.321),l(1.051,-.031),l(.414,-.243),l(-.041,-.27),l(.624,-.244),l(.455,-.214),l(1.218,-.573),l(.598,-.215),l(1.039,-.23),l(.889,-.073),l(.758,.07),l(.905,.126),l(.798,.041),l(.753,-.372),l(.216,.527),l(.416,.298),l(.278,.099),l(.592,.013),l(.622,-.144),l(.453,.74),l(.492,.255),l(.574,-.172),l(.391,.056),l(.968,.582),l(1.265,.04),l(1.094,.197),l(.749,-.001),l(1.084,-.272),l(.514,-.044),l(.651,.141),l(.764,.098),l(.787,-.016),l(.554,-.144),l(1.518,-.573),l(.424,-.335),l(1.212,.082),l(.491,.068),l(.226,-.072),l(.761,-.36),l(.41,.041),l(1.326,1.018),l(.031,.028),l(.795,.722),l(.026,.199),l(-.421,.813),l(.033,.412),l(.284,.211),l(1.413,.12),l(.492,.451),l(-.072,.211),l(-.409,-.023),l(-.231,.141),l(-.009,.433),l(-.584,.267),l(-.039,.27),l(.264,.67),l(-.122,.375),l(.224,.492),l(.09,.117),l(.106,-.105),l(.288,.203),l(.039,.207),l(-.229,.281),l(-.287,.535),l(-.06,.128),l(.213,.14),l(.424,.111),l(-.145,.245),l(.099,.421),l(.42,.374),l(.275,.035),l(.023,.308),M(462.617,106.804),l(.241,.211),l(-.019,.287),l(.115,.285),l(.077,.071),l(.593,.355),l(.819,.241),l(.605,.155),l(.152,.121),L(464.943,109),l(-.304,.166),l(-.515,-.072),l(-.94,-.246),l(-.326,.07),l(-.209,.152),l(-1.019,-.012),l(-.357,.384),l(-.109,.273),l(-.833,.316),l(-.612,.282),l(-.222,.258),l(-.307,.152),l(-.268,.293),l(-.255,.082),l(.164,-.258),l(.019,-.141),l(-.062,-.176),l(.584,-.293),l(.22,-.141),l(-.226,-.191),l(-.082,.015),l(-.653,-.056),l(-.229,-.148),l(.326,-.546),l(.387,-.558),l(.678,-.631),l(-.127,-.227),l(-.427,-.197),l(-.105,0),l(.498,-.445),l(.629,-.374),l(.497,-.045),l(.527,.111),l(1.333,.005),
+N(509.077,114.955),l(-.72,-.317),l(-.268,.016),l(-.356,-.433),l(-.374,-.105),l(-.13,-.363),l(.532,-.27),l(.095,-.222),l(-.43,-.176),l(-.027,-.188),l(.63,-.129),l(.094,-.155),l(-.061,-.113),l(-.487,-.21),l(-.351,-.281),l(-.306,-.166),l(-.456,.234),l(-1.058,.492),l(-.374,.445),l(-.642,.188),l(-.254,.255),l(-.014,-.027),l(.094,-.118),l(-.094,-.213),l(-.189,-.071),l(.26,-.095),l(.166,-.047),l(-.261,-.189),l(-.236,-.236),l(.236,-.118),l(.095,-.189),l(-.283,-.047),l(-.354,-.024),l(-.284,-.118),l(-.213,-.212),l(-.236,-.024),l(-.26,-.354),l(-.283,-.142),l(-.048,-.094),l(.166,0),l(.378,0),l(.165,-.236),l(0,-.236),l(-.213,-.024),l(-.188,-.142),l(-.544,-.331),l(-.283,-.354),l(.047,-.284),l(.402,-.142),l(-.119,-.236),l(-.212,-.166),l(-.426,-.071),l(-.284,-.095),l(.071,-.094),l(.071,-.118),l(-.284,-.095),l(-.087,-.212),l(.418,-.308),l(.236,0),l(.473,.024),l(.331,.189),l(.189,.213),l(.638,.189),l(.284,-.047),l(.188,.023),l(.143,.071),l(.26,.189),l(.236,-.047),l(.236,-.095),l(.095,-.331),l(-.118,-.213),l(-.614,-.166),l(-.213,-.307),l(-.143,-.26),l(.095,-.071),l(.143,-.142),l(.268,-.189),l(.229,-.023),l(.023,.166),l(.213,-.047),l(.189,0),l(.142,.189),l(.473,.284),l(.095,.118),l(.118,0),l(.283,.284),l(0,.308),l(.591,.094),l(.449,.142),l(.379,-.047),l(.165,-.213),l(.308,-.331),l(.283,-.094),l(.496,-.284),l(.292,-.449),l(.465,.331),l(.236,.378),l(.26,.189),l(.284,.307),l(.095,.52),l(.142,.236),l(.283,.26),l(.284,.165),l(0,.166),l(.449,.236),l(.473,-.047),l(.378,.071),l(.284,.166),l(.236,.189),l(.095,.189),l(0,.142),l(-.355,-.142),l(-.401,-.047),l(-.213,0),l(-.26,.047),l(-.142,.118),l(-.402,.071),l(-.213,.142),l(-.047,.189),l(-.023,.473),l(-.118,.26),l(-.095,.236),l(-.095,.378),l(.213,.236),l(-.023,.189),l(-.237,-.071),l(-.094,.095),l(-.071,.331),l(-.071,.26),l(-.118,-.047),l(-.094,-.236),l(-.143,-.095),l(-.165,.095),l(-.047,.307),l(.07,.166),l(-.118,.118),l(-.118,.095),l(.095,.26),l(-.363,.91),M(499.844,111.738),l(.709,.061),l(.142,-.047),l(.26,-.071),l(.236,.236),l(.071,.166),l(.378,.142),l(.213,.071),l(.308,-.118),l(.52,0),l(-.071,.213),l(.024,.236),l(.118,.023),l(.331,.166),l(-.071,.236),l(.421,.763),l(-.009,.001),l(-.253,-.133),l(-.416,.038),l(-.512,-.025),l(-.421,-.125),l(-.335,-.211),l(-.294,-.402),l(-.551,-.223),l(-.281,-.417),l(-.265,-.381),l(-.252,-.197),
+N(455.452,122.442),l(.049,-.209),l(-.057,-.128),l(-.812,-.256),l(-.691,-.006),l(-.506,-.116),l(-.484,.017),l(-.121,-.046),l(-.103,-.093),l(.139,-.56),l(.315,-.005),l(-.005,-.088),l(-.009,-.122),l(.069,-.07),l(.083,.157),l(.021,.146),l(.303,.021),l(.172,.055),l(.184,-.076),l(-.014,-.082),l(.108,-.029),l(.157,.105),l(-.037,.093),l(-.099,.006),l(-.04,.053),l(.088,.023),l(.144,.035),l(.094,.046),l(.021,.128),l(.353,.041),l(.846,-.122),l(.509,.016),l(.035,.13),l(.192,.035),l(.608,.064),l(.307,.051),l(.358,-.121),l(.09,.05),l(-.101,.312),l(.163,.11),l(.105,0),l(.325,-.169),l(.286,-.058),l(.078,.052),l(.154,-.07),l(.232,-.146),l(-.083,.187),l(.015,.186),l(-.183,.268),l(-.582,-.046),l(-.349,.081),l(-.335,-.017),l(-1.994,.169),M(445.294,112.196),l(.583,-.884),l(.496,-.472),l(.402,-.386),l(.449,-.842),l(.09,-.029),l(.809,-.076),l(.606,-.146),l(.395,-.429),l(.365,-.159),l(.361,.041),l(.622,.054),l(.532,-.245),l(.507,-.16),l(.09,-.029),l(1.855,-.195),l(.799,-.261),l(.584,-.06),l(1.473,.505),l(.619,.21),l(.388,-.031),l(1.117,-.277),l(.086,-.257),l(-.097,-.327),l(.344,-.216),l(.363,.026),l(.105,0),l(.427,.197),l(.127,.227),l(-.678,.631),l(-.387,.558),l(-.326,.546),l(-.062,-.407),l(-.794,-.056),l(-.743,-.041),l(-.566,-.125),l(-.062,-.144),l(-.459,.186),l(-.248,.123),l(-.403,.012),l(-.031,-.247),l(-.335,.029),l(-.301,.314),l(-.431,.186),l(-.31,.03),l(-.306,-.159),l(-.252,.07),l(-.004,.133),l(.169,.185),l(.169,.34),l(.308,.059),l(.826,.609),l(-.166,.07),l(-.369,-.258),l(-.015,-.105),l(-.276,-.082),l(-.331,-.105),l(-.116,.099),l(-.211,.007),l(.069,.129),l(-.016,.129),l(.338,.164),l(.145,-.012),l(.114,.234),l(-.03,.129),l(-.245,.023),l(-.445,-.457),l(-.341,-.141),l(-.207,-.059),l(-.128,-.012),l(.003,.094),l(-.075,.035),l(.138,.164),l(.102,.105),l(.154,.141),l(.193,.059),l(.153,.035),l(.103,.094),l(-.093,.058),l(-.494,-.046),l(-.253,-.035),l(.035,-.176),l(-.137,-.293),l(-.164,-.188),l(-.401,-.108),l(-.472,-.373),l(.258,-.118),l(.025,-.136),l(-.053,-.122),l(-.182,-.035),l(-.153,.199),l(-.465,.176),l(.245,.224),l(-.25,.371),l(-.05,.249),l(.13,.121),l(.065,.172),l(.311,.338),l(.133,.036),l(.131,.479),l(.579,.421),l(.359,.467),l(-.172,.14),l(-.237,.082),l(.106,-.187),l(-.121,-.187),l(-.142,-.128),l(-.139,-.035),l(-.151,-.047),l(-.29,.175),l(.102,.188),l(.153,.081),l(.08,.316),l(-.193,.187),l(-.652,.141),l(.248,.046),l(.27,.14),l(.391,.058),l(.188,.222),l(.257,-.012),l(.155,.012),l(.048,.126),l(.367,.269),l(.306,.014),l(.138,.292),l(.282,.012),l(.27,0),l(.348,.303),l(.015,.128),l(-.193,.082),l(.238,.782),l(-.153,.175),l(-.185,0),l(-.226,-.385),l(-.222,-.047),l(-.207,-.278),l(-.101,-.142),l(-.17,0),l(-.496,.14),l(-.479,.105),l(-.184,.128),l(.315,.093),l(.013,.188),l(.007,.291),l(.229,.117),l(.153,-.026),l(.225,-.079),l(-.021,.198),l(.235,.175),l(-.519,.093),l(.002,.117),l(-.169,.062),l(-.309,-.086),l(.121,-.21),l(-.186,-.086),l(-.508,-.056),l(-.158,-.092),l(-.008,.206),l(.194,.453),l(.193,.17),l(-.045,.163),l(.209,.204),l(.213,.96),l(-.688,-.31),l(-.331,.071),l(-.298,.439),l(-.442,-.735),l(-.46,-.367),l(-.452,.44),l(-.428,-.353),l(-.127,-.297),l(.212,-.425),l(-.028,-.241),l(-.215,-.269),l(-.491,-.424),l(-.167,-.226),l(.017,-.17),l(.471,-.61),l(.609,.098),l(.425,-.298),l(.202,.042),l(1.668,.663),l(.337,-.1),l(.483,-.355),l(-.266,-.049),l(-.27,-.056),l(-1.204,-.493),l(-1.127,-.083),l(-.367,.058),l(-.66,.058),l(-.427,.143),l(-.89,-1.118),l(.269,-.1),l(.253,.056),l(.218,-.114),l(.122,-.185),l(-.339,-.24),l(-.235,.114),l(-.496,-.042),l(-1.035,-.721),l(-.199,-.325),
+N(504.136,113.458),l(-.327,.328),l(-.377,.03),l(-.421,-.763),l(.071,-.236),l(-.331,-.166),l(-.118,-.023),l(-.024,-.236),l(.071,-.213),l(-.52,0),l(-.308,.118),l(-.213,-.071),l(-.378,-.142),l(-.071,-.166),l(-.236,-.236),l(-.26,.071),l(-.142,.047),l(-.709,-.061),l(-.492,-.451),l(-1.413,-.12),l(-.284,-.211),l(-.033,-.412),l(.421,-.813),l(-.026,-.199),l(-.795,-.722),l(.377,-.108),l(.307,.024),l(.308,-.095),l(.426,-.023),l(.401,-.118),l(.592,.047),l(.591,-.095),l(.378,-.047),l(.291,-.094),l(.087,.212),l(.284,.095),l(-.071,.118),l(-.071,.094),l(.284,.095),l(.426,.071),l(.212,.166),l(.119,.236),l(-.402,.142),l(-.047,.284),l(.283,.354),l(.544,.331),l(.188,.142),l(.213,.024),l(0,.236),l(-.165,.236),l(-.378,0),l(-.166,0),l(.048,.094),l(.283,.142),l(.26,.354),l(.236,.024),l(.213,.212),l(.284,.118),l(.354,.024),l(.283,.047),l(-.095,.189),l(-.236,.118),l(.236,.236),l(.261,.189),l(-.166,.047),l(-.26,.095),l(.189,.071),l(.094,.213),l(-.094,.118),l(.014,.027),
+N(566.651,117.4),l(-.565,-.153),l(-.496,-.054),l(-.264,-.151),l(-.564,.227),l(-.974,.147),l(-.137,-.059),l(.129,-.176),l(-.198,-.077),l(-.678,.03),l(-.739,.315),l(-.592,.486),l(-.589,.064),l(-.745,.495),l(-.351,.03),l(-.368,-.026),l(-.128,-.084),l(-.164,-.409),l(-.199,-.521),l(.185,-.444),l(.099,-.775),l(.029,-.255),l(-.17,-.187),l(-.484,.093),l(.156,-.597),l(-.576,-.45),l(-.153,-.056),l(-.384,.016),l(-.286,.162),l(-.134,.363),l(-.435,.428),l(-.049,.425),l(.006,.255),l(-.208,.228),l(-.442,.158),l(-.133,-.013),l(-.587,-.152),l(-.292,.058),l(-.073,.185),l(.007,.311),l(-.3,.313),l(-.21,.128),l(-.381,.016),l(-.63,-.237),l(-.325,.001),l(-.581,.286),l(-.58,.343),l(-.485,.144),l(-.245,-.041),l(-.129,-.141),l(-.04,-.055),l(.375,-.843),l(-.063,-.377),l(.377,-.377),l(.283,-.503),l(.503,-.346),l(.157,-.22),l(-.126,-.283),l(-.471,-.314),l(-.126,-.409),l(.031,-.472),l(.126,-.22),l(-.126,-.22),l(-.346,0),l(-.44,-.032),l(-.157,-.157),l(-.346,-.188),l(-.251,-.283),l(.125,-.471),l(.472,-.283),l(.629,.126),l(1.069,.094),l(.282,-.188),l(.063,-.409),l(.251,-.157),l(.188,0),l(.032,-.346),l(.188,-.063),l(.251,-.063),l(-.126,-.094),l(-.534,.031),l(-.251,-.125),l(.503,-.094),l(.565,-.095),l(.472,.095),l(.062,-.126),l(.032,-.346),l(-.189,-.314),l(.283,-.314),l(-.031,-.22),l(.346,0),l(.22,.157),l(.252,.22),l(.22,-.157),l(.692,-.188),l(.471,-.346),l(.221,-.251),l(.439,.377),l(.252,.251),l(-.188,.22),l(-.409,.346),l(-.251,.126),l(.126,.188),l(.251,.063),l(.188,.283),l(.409,-.031),l(.345,.005),l(-.302,.147),l(-.441,.196),l(-.024,.246),l(-.196,0),l(-.099,-.172),l(-.516,-.196),l(-.515,-.147),l(-.981,.196),l(-.123,0),l(-.098,.27),l(.049,.196),l(-.196,.074),l(-.098,-.221),l(-.196,.196),l(-.099,.245),l(.099,.27),l(.049,.147),l(.122,.098),l(.589,-.024),l(.564,0),l(.688,-.024),l(.196,.073),l(.49,-.147),l(.368,.098),l(.147,.123),l(.073,.196),l(.393,.074),l(.295,-.049),l(-.024,-.221),l(.343,-.049),l(.442,-.123),l(.221,.024),l(.049,.147),l(.319,.123),l(.122,-.024),l(.049,.172),l(.074,.221),l(.318,-.172),l(.368,-.024),l(.172,.319),l(.196,0),l(.172,-.147),l(.834,-.221),l(.663,.073),l(.539,0),l(.344,-.171),l(.344,-.098),l(.196,.073),l(-.011,.069),l(.023,1.031),l(-.207,.223),l(.077,.305),l(.325,.394),l(.463,-.045),l(.229,-.162),l(.22,.06),l(.692,.039),l(.273,.154),l(.295,.494),l(-.009,.284),l(.028,.246),l(.152,.012),l(.049,.123),l(-.126,.428),l(.245,.237),l(-.152,.36),l(.2,.163),l(-.181,.185),l(-.08,.249),l(-.354,.136),
+N(500.121,117.572),l(-.023,-.308),l(-.275,-.035),l(-.42,-.374),l(-.099,-.421),l(.145,-.245),l(-.424,-.111),l(-.213,-.14),l(.06,-.128),l(.287,-.535),l(.229,-.281),l(-.039,-.207),l(-.288,-.203),l(-.106,.105),l(-.09,-.117),l(-.224,-.492),l(.122,-.375),l(-.264,-.67),l(.039,-.27),l(.584,-.267),l(.009,-.433),l(.231,-.141),l(.409,.023),l(.072,-.211),l(.252,.197),l(.265,.381),l(.281,.417),l(.551,.223),l(.294,.402),l(.335,.211),l(.421,.125),l(.512,.025),l(.416,-.038),l(.253,.133),l(.009,-.001),l(.377,-.03),l(.327,-.328),l(.254,-.255),l(.642,-.188),l(.374,-.445),l(1.058,-.492),l(.456,-.234),l(.306,.166),l(.351,.281),l(.487,.21),l(.061,.113),l(-.094,.155),l(-.63,.129),l(.027,.188),l(.43,.176),l(-.095,.222),l(-.532,.27),l(.13,.363),l(.374,.105),l(.356,.433),l(.268,-.016),l(.72,.317),l(.015,.007),l(-.05,.707),l(-.143,.581),l(.205,.48),l(.494,.252),l(.925,.235),l(.827,.052),l(.424,.097),l(.162,.282),l(.312,.451),l(.687,.463),l(1.902,.513),l(.841,.052),l(.438,-.059),l(1.354,-.262),l(1.192,-.148),l(1.469,-.079),l(.41,-.229),l(.185,-.354),l(-.131,-.905),l(.015,0),l(.441,.025),l(.562,-.13),l(.466,-.3),l(.621,-.654),l(.476,-.328),l(.413,-.144),l(.609,-.032),l(.78,.038),l(.333,-.03),l(.517,-.343),l(.343,-.129),l(.519,.054),l(1.055,.107),l(.402,.437),l(.708,.251),l(.822,.335),l(.788,.066),l(.5,.054),l(.66,.167),l(.597,.336),l(1.803,1.362),l(.655,.506),l(.696,.01),l(.787,-.075),l(.185,.211),l(.038,.848),l(.01,.565),l(.15,.692),l(.078,.028),l(-.145,.241),l(-.084,.339),l(-.246,.807),l(-.49,1.272),l(-.222,.297),l(-.596,.384),l(-.016,.141),l(.119,.663),l(.096,.098),l(.738,.235),l(.026,.183),l(-.661,.935),l(-.034,.155),l(.254,1.085),l(.167,1.283),l(.143,.775),l(.191,.21),l(.209,.041),l(1.198,.275),l(.401,.167),l(.144,.366),l(.046,.437),l(-.425,.553),l(-.853,.795),l(-.853,1.034),l(.802,1.083),l(.71,1.068),l(.353,.464),l(.695,.391),l(1.144,.388),l(.409,.224),l(.168,.38),l(.111,1.34),l(.185,.394),l(.652,.053),l(.186,.281),l(-.036,.974),l(-.188,.255),l(-.209,.072),l(-1,.077),l(-.697,.258),l(-.794,.47),l(-.285,.383),l(-.31,.792),l(-.049,.354),l(-.182,.954),l(-.502,.028),l(-1.079,-.153),l(-.236,-.197),l(-.605,-.253),l(-.403,-.056),l(-1.43,.003),l(-.783,-.041),l(-.602,.072),l(-.475,-.38),l(-.163,-.126),l(-.835,-.026),l(-.576,.001),l(-.465,.014),l(-.212,-.239),l(-.756,-.125),l(-.305,-.183),l(-.162,-.014),l(-.021,-.5),l(-.295,-.128),l(-.103,-.514),l(-.292,-.349),l(-.013,-.639),l(-.309,-.493),l(-.237,.012),l(-.035,-.181),l(-.526,-.126),l(-.807,-.013),l(-.374,.017),l(-.209,.222),l(-.329,.018),l(-.517,.075),l(-.188,.364),l(-.538,.138),l(-.383,.443),l(-.368,.283),l(-.253,.043),l(-1.292,-.689),l(-.958,-.104),l(-.562,-.359),l(-1.088,-.317),l(-.247,-.301),l(-.324,-.282),l(-.497,-.592),l(-.997,-.436),l(-.584,-.083),l(-.194,-.028),l(-.58,-.465),l(-.596,-1.058),l(-.635,-1.114),l(-.209,-.268),l(.005,-.593),l(-.767,-.761),l(-.506,-.719),l(-.921,.143),l(-.46,-.042),l(-.13,-.126),l(-.291,-.056),l(-.191,-.268),l(-.029,-.565),l(-.448,.1),l(-.166,.099),l(-.32,.678),l(-.195,.184),l(-.355,.012),l(-.014,-.12),l(-.351,-.224),l(-.686,-.546),l(.064,-.212),l(-.007,-.395),l(-.164,-.465),l(-.215,-.013),l(-.551,.003),l(-.034,-.325),l(.055,-.579),l(.197,-.622),l(.014,-.508),l(-.112,-.239),l(-.29,-.28),l(-.774,-.603),l(-.436,-.209),l(-1.242,-.925),l(-.533,-.025),l(-.321,.115),L(503,127.106),l(.033,-.819),l(-1.02,-.954),l(-.312,-.351),l(-.002,-.184),l(.133,-.875),l(.235,-.763),l(1.142,-.98),l(-.422,-.761),l(.013,-.254),l(.468,-.596),l(-1.067,-.107),l(-.761,-.208),l(-.065,-.198),l(-.563,-1.086),l(-.69,-1.397),
+N(535.734,133.791),l(.853,-1.034),l(.853,-.795),l(.425,-.553),l(-.046,-.437),l(-.144,-.366),l(-.401,-.167),l(-1.198,-.275),l(-.209,-.041),l(-.191,-.21),l(-.143,-.775),l(-.167,-1.283),l(-.254,-1.085),l(.034,-.155),l(.661,-.935),l(-.026,-.183),l(-.738,-.235),l(-.096,-.098),l(-.119,-.663),l(.016,-.141),l(.596,-.384),l(.222,-.297),l(.49,-1.272),l(.246,-.807),l(.084,-.339),l(.145,-.241),l(.193,.112),l(.937,.193),l(.999,.348),l(.573,.082),l(.534,-.031),l(.636,-.3),l(.22,-.199),l(.001,-.466),l(.119,-.199),l(.568,-.159),l(1.985,-.689),l(.508,-.243),l(.103,-.128),l(.203,-.991),l(.432,-.809),l(1.135,-.205),l(.417,-.2),l(.445,-.356),l(.565,.294),l(1.454,.244),l(.096,.016),l(.646,.011),l(1.217,.05),l(.236,.126),l(.351,.48),l(.04,.055),l(.129,.141),l(.245,.041),l(.485,-.144),l(.58,-.343),l(.581,-.286),l(.325,-.001),l(.63,.237),l(.381,-.016),l(.21,-.128),l(.3,-.313),l(-.007,-.311),l(.073,-.185),l(.292,-.058),l(.587,.152),l(.133,.013),l(.442,-.158),l(.208,-.228),l(-.006,-.255),l(.049,-.425),l(.435,-.428),l(.134,-.363),l(.286,-.162),l(.384,-.016),l(.153,.056),l(.576,.45),l(-.156,.597),l(.484,-.093),l(.17,.187),l(-.029,.255),l(-.099,.775),l(-.185,.444),l(.199,.521),l(.164,.409),l(.128,.084),l(.368,.026),l(.351,-.03),l(.745,-.495),l(.589,-.064),l(.592,-.486),l(.739,-.315),l(.678,-.03),l(.198,.077),l(-.129,.176),l(.137,.059),l(.974,-.147),l(.564,-.227),l(.264,.151),l(.496,.054),l(.565,.153),l(-.192,.145),l(-.574,-.059),l(.01,.269),l(.236,.012),l(.048,.088),l(-.148,.142),l(-.358,.004),l(-.455,.297),l(-.332,-.005),l(-.338,.179),l(-.647,-.144),l(-1.345,.012),l(-1.148,.152),l(-.53,.292),l(-.272,.19),l(-.559,.395),l(-.246,-.023),l(-.258,.214),l(-.464,.413),l(.01,.32),l(.411,.271),l(.01,.336),l(.232,.171),l(-.119,.483),l(.198,.477),l(-.324,.426),l(-.524,.355),l(-.4,.341),l(-.13,.283),l(.223,.478),l(.033,.31),l(-.289,.255),l(-.513,.215),l(-.698,-.039),l(-.997,-.122),l(-.355,.129),l(.35,.336),l(.365,.407),l(.129,.281),l(.088,.437),l(-.199,.255),l(-.315,.115),l(-.513,.031),l(-.416,.115),l(-.292,.228),l(-.224,.424),l(-.288,.834),l(-.139,1.214),l(-.021,.084),l(-.34,.383),l(-.237,.086),l(-1.001,-.375),l(-.562,-.025),l(-.559,.243),l(-.362,.271),l(-.321,.693),l(-.254,.086),l(-.516,-.082),l(-.644,-.039),l(-.283,.072),l(-.597,.441),l(-.412,.369),l(-.188,.34),l(-.232,.876),l(-.099,.903),l(-.069,.184),l(-.247,.156),l(-1.066,.274),l(-1.183,.19),l(-.964,.175),l(-1.234,.12),l(-1.005,-.135),l(-.349,.002),l(-1.187,.218),l(-.742,-.024),l(-.541,-.039),l(-.854,-.235),l(-1.069,-.248),l(-.63,-.194),l(-.887,-.32),
+N(486.696,126.295),l(5.257,-2.711),l(.589,-2.701),l(-.024,-.467),l(-.187,-.508),l(.009,-.255),l(.23,-.355),l(.31,-.214),l(.866,-.174),l(.457,-.371),l(.944,-.883),l(-.059,-.24),l(.23,-.043),l(.266,-.143),l(.437,-.094),l(.304,.16),l(.364,-.032),l(.811,.153),l(.343,.023),l(.244,-.234),l(.366,.082),l(.274,.151),l(-.03,.259),l(.102,.269),l(.318,.105),l(.164,-.126),l(.433,-.388),l(.407,.016),l(.69,1.397),l(.563,1.086),l(.065,.198),l(.761,.208),l(1.067,.107),l(-.468,.596),l(-.013,.254),l(.422,.761),l(-1.142,.98),l(-.235,.763),l(-.133,.875),l(.002,.184),l(.312,.351),l(1.02,.954),L(503,127.106),l(.075,.155),l(.321,-.115),l(.533,.025),l(1.242,.925),l(.436,.209),l(.774,.603),l(.29,.28),l(.112,.239),l(-.014,.508),l(-.197,.622),l(-.055,.579),l(.034,.325),l(.551,-.003),l(.215,.013),l(.164,.465),l(.007,.395),l(-.064,.212),l(.686,.546),l(.351,.224),l(.014,.12),l(-.096,.003),l(-.664,.101),l(-.408,-.056),l(-.157,.057),l(-.103,.127),l(-1.271,.044),l(-.518,.13),l(-.343,.693),l(-.463,.609),l(-.521,.568),l(-4.048,-.132),l(-1.557,-.697),l(-.812,-.277),l(-.118,-.253),l(-.047,-.818),l(.118,-.396),l(-.135,-.366),l(-.973,.048),l(-.141,-.07),l(-.399,-.633),l(-.258,-.196),l(-2.44,-1.101),l(-1.14,-.473),l(-2.034,-.934),l(-.757,-.222),l(-1.129,-.459),l(-.093,-.056),l(-.093,-.056),l(-.311,-.69),l(-.87,-1.632),
+N(479.916,127.377),l(-.082,-.085),l(.047,-.122),l(-.021,-.183),l(-.201,-.128),l(-.183,-.346),l(.398,-.209),l(.041,-.099),l(.526,-.396),l(-.048,-.058),l(-.223,-.099),l(.077,-.151),l(.298,-.25),l(.599,-.006),l(-.14,-.146),l(-.035,-.046),l(.078,-.111),l(.177,-.163),l(.169,-.116),l(.299,-.239),l(-.068,-.058),l(.023,-.163),l(-.09,-.047),l(-.031,-.221),l(-.241,-.157),l(-.222,-.058),l(.204,-.204),l(-.125,-.052),l(-.053,-.116),l(-.12,.058),l(-.335,.052),l(-.388,-.023),l(-.225,-.564),l(.129,-.593),l(.072,-.064),l(-.1,-.507),l(-.42,-.326),l(.126,-.093),l(.036,-.152),l(.117,-.128),l(-.093,-.222),l(.107,-.012),l(.259,-.32),l(-.061,-.112),l(.311,-.001),l(.39,.125),l(.114,-.127),l(.171,.041),l(.2,-.169),l(.156,-.374),l(.627,-.127),l(-.243,-.423),l(-.102,-.452),l(.055,-.319),l(.142,.035),l(.222,-.001),l(.777,.166),l(.575,.025),l(.746,-.075),l(.929,-.373),l(.278,-.03),l(1.532,.444),l(.556,.153),l(1.104,-.176),l(.567,-.13),l(1.498,-.673),l(.439,-.073),l(.747,.123),l(.711,.039),l(1.185,-.224),l(.693,-.304),l(.24,-.292),l(.207,.023),l(.064,.245),l(.059,.24),l(-.944,.883),l(-.457,.371),l(-.866,.174),l(-.31,.214),l(-.23,.355),l(-.009,.255),l(.187,.508),l(.024,.467),l(-.589,2.701),l(-5.257,2.711),l(-.161,.071),l(-2.96,1.541),l(-1.139,.656),l(-.253,.016),l(-.365,-.167),l(-1.902,-1.034),
+N(426.068,126.434),l(-.093,.981),l(.064,.564),l(-.093,.269),l(-.802,.428),l(-.579,.314),l(-1.473,1.138),l(-.126,.354),l(.274,.973),l(-.147,.537),l(-.155,.227),l(-.864,.598),l(-.22,.143),l(-.564,-1.536),l(-.699,-2.242),l(-.323,-.464),l(-.363,-.252),l(-.432,-.181),l(-.484,-.831),l(-.225,-.465),l(-.363,-.28),l(-.452,-.097),l(-.336,-.774),l(-.301,-.888),l(.112,-.509),l(1,-.853),l(.414,-.355),l(.163,-.411),l(.048,-.537),l(-.052,-.594),l(-.026,-.892),l(-.012,-1.429),l(.114,-.439),l(.685,-.627),l(.012,-.184),l(.508,-.185),l(.633,-.455),l(.591,-.228),l(.703,-.016),l(.643,.183),l(.247,.212),l(.059,.241),l(.25,.538),l(.27,.084),l(.417,-.171),l(.584,-.44),l(.401,-.17),l(.034,.354),l(-.265,.567),l(-.638,.511),l(-.275,.468),l(.005,.283),l(.202,.438),l(.508,.466),l(.351,.127),l(.303,.848),l(-.094,.212),l(-.541,.764),l(-.59,.34),l(-1.017,.92),l(-.216,.339),l(.287,.451),l(.587,.55),l(.528,.295),l(.284,.056),l(.396,-.227),l(.316,.084),l(.244,.635),l(.582,.239),
+N(381.402,139.704),l(-.027,-.876),l(.069,-2.006),l(.037,-.382),l(.686,-.314),l(1.512,-.998),l(.963,-.542),l(1.265,.078),l(.397,-.059),l(.181,-.693),l(.864,-.033),l(.777,-.174),l(.527,-.229),l(.524,-.356),l(.484,-.652),l(1.109,-.332),l(1.52,-.701),l(.129,-.227),l(-.296,-.62),l(-.025,-.396),l(.079,-.227),l(.265,-.114),l(1.186,-.12),l(.381,-.186),l(.309,-.553),l(1.022,.022),l(.67,-.018),l(1.826,.004),l(.34,-1.033),l(-.07,-.211),l(-.507,-.322),L(397,126.646),l(-.158,-.465),l(.016,-1.271),l(.022,-.833),l(-.165,-.889),l(-.189,-.211),l(-.563,-.279),l(-.259,-.508),l(.351,0),l(.66,-.143),l(.541,-.256),l(.369,-.566),l(.405,-.312),l(.509,-.086),l(.407,-.157),l(.679,-.27),l(.324,.226),l(.176,.017),l(.249,.024),l(.238,-.142),l(.407,-.51),l(.613,-.426),l(.682,-.355),l(.614,-.171),l(1.16,-.116),l(1.587,-.06),l(.513,-.072),l(.634,-.312),l(.578,.211),l(.564,-.072),l(.585,-.313),l(.343,-.1),l(.939,.012),l(.513,-.015),l(.307,.056),l(.221,.042),l(.322,.113),l(.816,.168),l(.529,-.015),l(.772,-.171),l(.705,-.2),l(.612,-.554),l(.994,.508),l(.339,.099),l(.312,-.143),l(.314,-.241),l(.228,-.156),l(.528,.042),l(.388,.197),l(.162,.269),l(.269,.126),l(.516,-.086),l(1.093,-.158),l(-.012,.184),l(-.685,.627),l(-.114,.439),l(.012,1.429),l(.026,.892),l(.052,.594),l(-.048,.537),l(-.163,.411),l(-.414,.355),l(-1,.853),l(-.112,.509),l(.301,.888),l(.336,.774),l(.452,.097),l(.363,.28),l(.225,.465),l(.484,.831),l(.432,.181),l(.363,.252),l(.323,.464),l(.699,2.242),l(.564,1.536),l(-.204,.156),l(-.241,.383),l(.88,1.605),l(.147,.833),l(.052,.691),l(-.1,.862),l(.101,.748),l(-.16,.622),l(-.158,.495),l(.457,1.156),l(-.061,.664),l(-.086,.17),l(-.666,.47),l(-.249,.128),l(-.152,.283),l(1.272,1.702),l(.249,.917),l(.562,.873),l(.244,.154),l(.544,-.201),l(.702,.165),l(1.028,.347),l(.178,.168),l(.86,1.506),l(.098,.07),l(-.265,.186),l(-1.632,.843),l(-4.012,2.241),l(-1.607,.956),l(-2.308,1.454),l(-.834,.655),l(-3.084,2.617),l(-1.82,.364),l(-1.672,.321),l(-2.176,.408),l(-.146,-.564),l(.161,-.679),l(-.099,-.522),l(-.277,-.352),l(-.309,-.111),l(-.748,-.024),l(-.375,-.167),l(-.588,-.562),l(-.47,.314),l(-.229,-.027),l(-1.111,-1.039),l(-.393,-.28),l(-.082,-.183),l(.096,-.396),l(-.181,-.253),l(-2.472,-1.469),l(-.397,-.253),l(-1.292,-.824),l(-1.924,-1.26),l(-3.283,-2.241),l(-.811,-.575),l(-2.054,-1.344),l(-.895,-.531),l(-.1,-.084),l(-1.414,-.91),l(-4.12,-2.42),l(-2.829,-1.509),
+N(395.704,122.189),l(.259,.508),l(.563,.279),l(.189,.211),l(.165,.889),l(-.022,.833),l(-.016,1.271),l(.158,.465),l(.598,.788),l(.507,.322),l(.07,.211),l(-.34,1.033),l(-1.826,-.004),l(-.67,.018),l(-1.022,-.022),l(-.309,.553),l(-.381,.186),l(-1.186,.12),l(-.265,.114),l(-.079,.227),l(.025,.396),l(.296,.62),l(-.129,.227),l(-1.52,.701),l(-1.109,.332),l(-.484,.652),l(-.524,.356),l(-.527,.229),l(-.777,.174),l(-.864,.033),l(-.181,.693),l(-.397,.059),l(-1.265,-.078),l(-.963,.542),l(-1.512,.998),l(-.686,.314),l(-.037,.382),l(-.069,2.006),l(-1.42,.008),l(-1.718,-.004),l(-2.677,.001),l(-2.511,0),l(-1.635,.04),l(.141,-.28),l(.431,-.411),l(.427,-.085),l(1.296,-.285),l(1.143,-.455),l(.453,-.312),l(1.147,-.85),l(1.149,-.878),l(1.043,-1.104),l(.46,-.693),l(.133,-.509),l(-.05,-.494),l(-.427,-.776),l(-.09,-.678),l(.099,-.508),l(.396,-.636),l(.706,-.863),l(.211,-.65),l(-.063,-.367),l(.071,-.353),l(1.285,-1.203),l(.724,-.481),l(.916,-.327),l(1.266,-.469),l(.73,-.397),l(.558,-.552),l(.537,-.736),l(.466,-.905),l(.829,-1.925),l(.269,-.128),l(.54,-.171),l(.19,.127),l(.684,.848),l(.138,.099),l(1.148,.507),l(.661,-.001),l(.595,.042),l(1.304,-.074),l(.522,-.228),l(.437,-.27),l(.398,.551),l(.256,.099),l(.798,.097),l(.361,0),
+N(480.248,123.437),l(.388,.023),l(.335,-.052),l(.12,-.058),l(.053,.116),l(.125,.052),l(-.204,.204),l(.222,.058),l(.241,.157),l(.031,.221),l(.09,.047),l(-.023,.163),l(.068,.058),l(-.299,.239),l(-.169,.116),l(-.177,.163),l(-.078,.111),l(.035,.046),l(.14,.146),l(-.599,.006),l(-.298,.25),l(-.077,.151),l(.223,.099),l(.048,.058),l(-.526,.396),l(-.041,.099),l(-.398,.209),l(-.07,-.023),l(-.088,.041),l(-.067,.193),l(-.009,.167),l(-.355,.07),l(-.07,-.099),l(-.151,-.022),l(-.372,.051),l(.26,-.291),l(.097,-.361),l(.169,-.227),l(.328,-.681),l(-.017,-.232),l(.181,0),l(.138,-.192),l(.072,-.32),l(.018,-.32),l(.409,-.431),l(.232,-.07),l(.116,-.174),l(-.048,-.157),
+N(184.444,142.729),l(-.367,.82),l(-.518,.821),l(-.186,.763),l(-.179,1.159),l(.017,1.851),l(-.133,1.187),l(-.016,1.13),l(.564,1.737),l(.275,.805),l(.624,.945),l(.76,.903),l(.191,.452),l(.481,.521),l(.529,.974),l(.729,1.228),l(.375,.296),l(.677,.069),l(.436,-.015),l(.577,.154),l(.593,.451),l(.503,.508),l(.773,.069),l(1.016,-.242),l(1.55,-.456),l(1.396,-.3),l(.803,-.157),l(-.02,.542),l(.838,.223),l(.264,-.286),l(.293,-.199),l(-.104,-.247),l(-.393,-.175),l(1.073,-.62),l(.633,-.62),l(.086,-.827),l(.498,-.429),l(-.094,-.477),l(.092,-1.145),l(.254,-.699),l(.625,-.334),l(.164,-.043),l(.757,-.198),l(.701,-.1),l(1.088,-.229),l(1.016,-.37),l(.594,-.058),l(.499,.056),l(1.139,.181),l(.502,-.194),l(.378,.093),l(.62,.507),l(.047,.297),l(-.079,.424),l(-.298,.382),l(-.541,.496),l(-.433,.425),l(-.317,.445),l(-.02,.7),l(-.254,.297),l(-.188,.354),l(.155,.155),l(.337,-.138),l(-.101,.652),l(-.262,1.196),L(205.356,159),l(-.062,.24),l(-.34,-.534),l(-.167,-.452),l(-.072,-.155),l(-.386,.34),l(-.02,.549),l(-.437,.016),l(-.178,.447),l(-.599,.857),l(-.386,-.27),l(-.278,.095),l(.025,.329),l(-2.332,-.006),l(-1.792,-.005),l(-.04,1.24),l(-.999,.032),l(.396,.223),l(.495,.541),l(.624,.231),l(.359,.69),l(.532,.223),l(-.211,.683),l(-1.762,-.007),l(-1.06,.007),l(-1.076,1.812),l(.305,.397),l(-.207,.238),l(.054,.553),l(.044,.454),l(-.704,-.555),l(-.952,-.888),l(-.956,-.761),l(-1.069,-.859),l(-.534,-.352),l(-.053,-.071),l(-.639,-.252),l(-1.048,-.21),l(-.657,.044),l(-.817,.397),l(-1.1,.567),l(-.756,.256),l(-.931,-.069),l(-.724,-.21),l(-.48,-.197),l(-1.305,-.195),l(-.588,-.267),l(-.644,-.422),l(-.935,-.521),l(-.785,-.267),l(-1.711,-.392),l(-.963,-.365),l(-.722,-.366),l(-1.074,-.436),l(-.592,-.352),l(-1.123,-1),l(-.207,-.07),l(-.606,.058),l(-.689,-.14),l(-1.835,-.575),l(-.565,-.536),l(-.503,-.634),l(-.495,-.395),l(-1.049,-.577),l(-.619,-.267),l(-.5,-.494),l(-.742,-.987),l(-.363,-.55),l(-.038,-.113),l(.15,-.155),l(.504,-.086),l(.18,-.17),l(.047,-.184),l(-.331,-.367),l(.457,-.679),l(.041,-.381),l(-.172,-.466),l(-.744,-.959),l(.121,-.297),l(.146,-.17),l(-.07,-.268),l(-.665,-.62),l(-1.495,-1.777),l(-.546,-.493),l(-.963,-1.058),l(-.474,-.522),l(-.815,-.578),l(-.322,-.197),l(-.158,-.268),l(-.058,-.48),l(-.144,-.183),l(-.329,-.197),l(-.609,-.197),l(-.408,-.31),l(-.366,-.522),l(-.271,-.028),l(-.414,.114),l(-.238,-.155),l(-.163,-.367),l(-.005,-.325),l(.459,-.736),l(-.126,-.339),l(-.751,-.62),l(-.439,.255),l(-.375,-.621),l(-.118,-.353),l(-.359,-.211),l(-.61,-.168),l(-.319,-.296),l(-.125,-.254),l(.05,-.381),l(.084,-.269),l(-.185,-.226),l(-.561,-.21),l(-.46,-.098),l(-.46,-.253),l(-.935,-.86),l(-.478,-.706),l(-.281,-.551),l(-.646,-.832),l(-.736,-1.073),l(-.184,-.423),l(-.38,-.678),l(-.242,-.338),l(-.152,-.452),l(.042,-.509),l(.032,-.311),l(-.56,-.239),l(-.795,-.196),l(-.06,-.452),l(-.128,-.155),l(-.458,-.183),l(-.289,.326),l(-.251,.043),l(-1.43,-.647),l(-.285,1.004),l(-.045,.438),l(.033,.084),l(.265,.339),l(.264,.296),l(.028,1.046),l(.088,.509),l(.51,.677),l(.143,.169),l(.643,.267),l(.601,.536),l(.525,.663),l(.602,1.214),l(.44,.282),l(.328,.042),l(.237,.169),l(.325,1.398),l(.102,.169),l(.246,.155),l(.497,.056),l(.133,.056),l(.215,.438),l(.161,.65),l(.445,.79),l(.49,-.071),l(.223,-.142),l(.245,.452),l(.344,1.469),l(.531,1.059),l(.649,1.2),l(.069,.593),l(-.014,.522),l(.26,.353),l(.378,.154),l(.389,-.17),l(.234,-.198),l(.588,.804),l(.258,.579),l(.464,.253),l(.281,.014),l(.133,.311),l(-.196,.537),l(-.136,.127),l(-.691,.595),l(-.254,-.042),l(-.251,-.409),l(-.24,-.734),l(-.617,-.578),l(-.625,-.309),l(-.516,-.479),l(-.834,-.507),l(-1.143,-.986),l(-.416,-.451),l(-.162,-.269),l(.216,-.989),l(-.035,-.254),l(-.488,-1.002),l(-.238,-.381),l(-.327,-.282),l(-.44,-.098),l(-.5,-.31),l(-.675,-.677),l(-.305,.142),l(-.363,-.056),l(-1.262,-.746),l(-.722,-.31),l(-.896,-.973),l(-.139,-.127),l(-.246,-.254),l(.679,.15),l(.599,.013),l(.588,-.284),l(.244,-.326),l(.093,-.636),l(-.01,-.184),l(-.458,-.635),l(-.466,-.452),l(-1.1,-.888),l(-.986,-.493),l(-.402,-.338),l(-.203,-.522),l(-.272,-.649),l(-.091,-.155),l(-.447,-.126),l(-.15,-.353),l(-.026,-.594),l(-.203,-.395),l(-.623,-.734),l(-.434,-.706),l(-.003,-.254),l(.212,-.382),l(-.777,-.62),l(-.254,-.325),l(-.22,-.485),l(.34,-.017),l(2.367,-.155),l(2.381,-.084),l(.316,.31),l(.267,.154),l(1.186,.39),l(2.811,.933),l(3.516,1.112),l(.338,.055),l(1.662,.019),l(1.544,.02),l(.966,.037),l(1.867,-.011),l(.213,-.101),l(.096,-.892),l(1.858,.003),l(1.892,.046),l(.209,.112),l(.631,.662),l(.766,.632),l(.837,.519),l(.708,.491),l(.179,.226),l(.284,.678),l(.318,.847),l(.445,.549),l(1.092,.659),l(1.104,.503),l(.337,.069),l(.501,.011),l(.416,-.158),l(.283,-.37),l(.418,-.413),l(.576,-.541),l(.468,-.201),l(.643,-.018),l(.475,.082),l(.783,.321),l(.412,.252),l(.363,.366),l(.663,1.029),l(.744,1.227),l(.845,1.042),l(.657,.576),l(.268,.253),l(.078,.467),l(.332,.932),l(.336,.592),l(.375,.365),l(.921,.32),l(1.029,.56),l(.264,.069),l(.416,-.116),l(.296,-.001),l(.816,.377),l(.347,.119),
+N(507.047,133.665),l(.055,.197),l(.134,.691),l(-.336,-.028),l(-.513,.513),l(.421,.194),l(.418,-.206),l(.306,.021),l(.698,1.84),l(-.644,.044),l(-1.07,-.05),l(-.185,-.239),l(-.334,-.619),l(-.408,-.054),l(-1.657,-.259),l(.521,-.568),l(.463,-.609),l(.343,-.693),l(.518,-.13),l(1.271,-.044),
+N(606.155,150.953),l(.595,.152),l(.255,.14),l(.25,-.129),l(.273,-.368),l(.015,-.678),l(-.152,-.93),l(.228,-.185),l(.401,-.144),l(.191,-.354),l(-.146,-1.594),l(.133,-.283),l(.811,.32),l(.391,.11),l(.309,.013),l(.17,-.128),l(1.148,-2.25),l(0,-.324),l(-.192,-.408),l(.045,-.212),l(.938,-1.134),l(.136,-.382),l(-.057,-.761),l(.197,-.354),l(1.446,-.883),l(.719,-.512),l(.312,-.129),l(.558,.082),l(.853,.221),l(.295,-.058),l(-.184,-.718),l(.072,-.283),l(.596,-.582),l(.112,-.24),l(.018,-.508),l(.001,-.127),l(.306,-.34),l(.277,-.044),l(.504,.279),l(.397,.435),l(.243,.901),l(.217,.309),l(.287,.041),l(.504,-.031),l(.146,.14),l(.195,1.649),l(.02,.875),l(-.353,.862),l(-.429,.722),l(-.611,.525),l(-.487,.271),l(-.191,.198),l(-.617,.85),l(.075,.465),l(.163,.705),l(-.224,.58),l(0,.268),l(.216,.069),l(.312,-.086),l(.819,-.442),l(.771,-.089),l(.479,-.017),l(.156,.126),l(.277,1.762),l(.202,.324),l(.554,-.074),l(.521,.096),l(.033,.268),l(-.729,1.584),l(.117,.352),l(.228,.098),l(.507,-.003),l(.481,-.045),l(.291,.379),l(.341,.746),l(.378,.266),l(.246,.083),l(.647,-.159),l(.628,-.413),l(.111,.38),l(.153,.239),l(-.502,.355),l(-.53,.61),l(-.442,.581),l(-.582,.455),l(-.193,.185),l(-.08,.085),l(-.158,.071),l(-.645,.06),l(-.436,.172),l(-.528,.342),l(-.394,.595),l(-1.078,.316),l(-.62,.018),l(-.474,-.082),l(-.362,.411),l(-.143,.368),l(-.036,.819),l(-.114,.509),l(.064,.409),l(-.086,.24),l(-.163,.001),l(-.588,.131),l(.739,.884),l(.069,.183),l(.112,.875),l(.254,.14),l(1.091,.953),l(.148,.324),l(.646,1.041),l(.163,.338),l(-.194,.241),l(-.451,.229),l(-.128,.226),l(.231,1.185),l(-.171,.198),l(-.812,.428),l(.178,.38),l(.6,1.436),l(.54,.477),l(.606,.604),l(.203,.479),l(.088,.663),l(-.086,.636),l(.006,.254),l(.488,1.183),l(.586,1.225),l(-.077,.297),l(-1.011,1.559),l(-1.01,1.7),l(-.098,.374),l(-.359,-.181),l(-.075,-.805),l(.461,-.665),l(.174,-.495),l(.122,-.777),l(.287,-.466),l(-.512,-.027),l(-.104,-.084),l(-.004,-.282),l(.195,-.509),l(-.177,-1.524),l(-.246,-.832),l(-.639,-1.185),l(-.488,-1.312),l(-.347,-.846),l(-.179,-.875),l(-.174,-1.736),l(-.117,-.677),l(-.034,-.564),l(-.051,-.212),l(-.344,-.084),l(-.148,-.098),l(-.304,-.917),l(-.516,-.677),l(-.226,-.225),l(-.247,.029),l(-.081,.988),l(-.158,.424),l(-.43,.41),l(-.59,.284),l(-1.089,.511),l(-.359,.622),l(-.298,.297),l(-.196,.142),l(-.237,-.282),l(-.007,-.438),l(-.212,.015),l(-.338,.354),l(-.321,-.013),l(-.166,-.211),l(.147,-.495),l(-.001,-.113),l(-.621,.171),l(-.276,.127),l(-.247,.283),l(-.355,-.126),l(-.002,-.466),l(.553,-1.54),l(.162,-.791),l(.001,-.889),l(-.101,-1.059),l(-.384,-.973),l(-.431,-1.072),l(-.196,-.296),l(-.281,.537),l(-.32,-.126),l(-.526,-.366),l(.482,-.17),l(.312,-.015),l(-.149,-.479),l(-.054,-.268),l(-.684,-.775),l(-.182,-.183),l(-.19,-.028),l(-.407,.1),l(-.38,-.267),l(.086,-.438),l(-.026,-.141),l(-.209,-.112),l(-.365,.043),l(-.577,-.465),l(-.504,-.606),l(-.117,-.244),l(.252,-.341),l(.801,-.527),l(-.194,-1.607),
+N(605.297,153.429),l(-.126,-.264),l(-.269,-.55),l(-.223,-1.213),l(-.611,-1.41),l(-.357,-.395),l(-.73,.354),l(-.393,0),l(-.034,-.084),l(-.242,-.211),l(-.356,-.592),l(-.124,-.042),l(-.152,.127),l(-.026,.537),l(.374,.79),l(-.006,.424),l(-.143,.169),l(-.455,.086),l(-.235,.537),l(-.261,.1),l(-.255,-.437),l(-.311,-.395),l(-.073,-.057),l(-.163,.669),l(-.28,.249),l(-.203,.043),l(-.271,-.536),l(-.495,.636),l(-.359,-.265),l(-.147,-.532),l(-.402,-1.775),l(-.325,-1.409),l(-.352,-.45),l(-.04,-.254),l(.505,-.765),l(.029,-.269),l(-.193,-.21),l(-1.042,-.431),l(-.339,-.323),l(.548,-.61),l(.4,-.299),l(.502,-.13),l(.382,-.101),l(.047,-.155),l(-.126,-.112),l(-1.224,-.938),l(-.494,-.237),l(-.083,-.155),l(.124,-.283),l(.555,-.525),l(.234,-.171),l(1.252,.303),l(.339,.266),l(.372,.266),l(.489,-.06),l(.417,.054),l(.129,.324),l(.053,.479),l(.079,.719),l(.095,.099),l(.537,.109),l(.547,.053),l(.916,-.062),l(.559,-.003),l(2.473,.198),l(.111,.098),l(.057,.127),l(-.012,.79),l(-.159,.34),l(-.938,.767),l(-.498,.13),l(-.651,.356),l(-.131,.283),l(.009,.522),l(.001,.381),l(.23,.281),l(.249,.267),l(.529,.448),l(.224,-.354),l(.395,-1.159),l(.281,-.115),l(.4,-.044),l(.064,.578),l(.627,2.479),l(.037,.466),l(.194,1.607),l(-.801,.527),l(-.252,.341),
+N(627.408,186.411),l(-.086,.337),l(-.495,.35),l(-.11,.575),l(-.644,.089),l(-.05,-.478),l(-.309,-.163),l(-.279,.28),l(-.244,.394),l(-.204,-.083),l(-.118,-.239),l(.213,-.398),l(-.041,-.21),l(-.055,-.226),l(-.261,-.238),l(-.447,-.119),l(-.106,-.466),l(-.571,.013),l(-.448,.17),l(.013,-.104),l(.128,-.297),l(-.15,-.183),l(-.411,.212),l(-.301,-.07),l(-.38,-.38),l(-.116,-.508),l(.064,-.282),l(-.151,-.438),l(-.229,-.169),l(-.388,.043),l(-.39,-.719),l(-.209,-.508),l(-.3,-.324),l(-.311,-.155),l(-.456,-.395),l(-.343,.1),l(-.218,.142),l(-.216,-.381),l(-.04,-.607),l(.163,-.749),l(.559,-1.738),l(.29,-.848),l(-.087,-.044),l(.098,-.374),l(1.01,-1.7),l(1.011,-1.559),l(.077,-.297),l(-.586,-1.225),l(-.488,-1.183),l(-.006,-.254),l(.086,-.636),l(-.088,-.663),l(-.203,-.479),l(-.606,-.604),l(-.54,-.477),l(-.6,-1.436),l(-.178,-.38),l(.812,-.428),l(.171,-.198),l(-.231,-1.185),l(.128,-.226),l(.451,-.229),l(.194,-.241),l(-.163,-.338),l(-.646,-1.041),l(-.148,-.324),l(-1.091,-.953),l(-.254,-.14),l(-.112,-.875),l(-.069,-.183),l(-.739,-.884),l(.588,-.131),l(.163,-.001),l(.086,-.24),l(-.064,-.409),l(.114,-.509),l(.036,-.819),l(.143,-.368),l(.362,-.411),l(.474,.082),l(.62,-.018),l(1.078,-.316),l(.394,-.595),l(.528,-.342),l(.436,-.172),l(.645,-.06),l(.158,-.071),l(.08,-.085),l(.541,.166),l(.295,.182),l(.118,.168),l(.01,.423),l(-.106,.805),l(.066,.367),l(.186,.154),l(.423,-.003),l(.489,-.2),l(.414,-.045),l(.045,.113),l(.249,1.311),l(-.085,.41),l(-.528,1.569),l(-.117,.438),l(-.027,.494),l(.145,.324),l(.481,.138),l(.37,-.411),l(1.173,-1.178),l(.346,-.03),l(.835,.348),l(.59,.265),l(.223,-.072),l(.543,-.257),l(.2,-.538),l(.286,-.453),l(.403,.012),l(.893,.192),l(.266,.153),l(.052,.282),l(.285,.535),l(.688,.659),l(.435,.632),l(.058,1.524),l(.107,.366),l(.255,.464),l(.979,1.279),l(.419,.703),l(.157,.507),l(.002,.945),l(-.121,.438),l(-.808,.64),l(-.301,-.167),l(-.599,-.109),l(-.575,-.039),l(-.558,.074),l(-.798,-.066),l(-1.172,.091),l(-.383,.101),l(-.521,.441),l(-.92,1.233),l(-.146,.297),l(-.076,.382),l(.026,.635),l(.219,.648),l(.487,.717),l(.16,.479),l(-.146,.176),l(-.026,-.063),l(-.286,-.183),l(-.458,-.084),l(-.9,-.887),l(-.434,-.154),l(-.304,-.014),l(-.572,.227),l(-.391,-.112),l(-.29,-.141),l(-.337,-.014),l(0,-.282),l(.19,-1.243),l(-.107,-.184),l(-.719,-.055),l(-.248,-.084),l(-.521,.043),l(-.443,.212),l(-.244,.297),l(.207,.593),l(-.103,.339),l(-.318,.707),l(.083,.579),l(.054,.41),l(-.293,.664),l(-.583,1.187),l(-.7,1.682),l(-.255,1.314),l(.104,1.171),l(.172,.296),l(.229,.169),l(.55,-.072),l(.396,-.142),l(.252,.07),l(.135,.353),l(.009,.325),l(-.08,.466),l(.141,.282),l(.178,.211),l(.271,-.094),l(.17,.46),l(.209,.974),l(-.032,.254),l(.127,.737),l(.434,.871),l(.167,.155),l(.801,.281),l(.539,.112),l(.467,-.058),l(.294,.197),l(.266,.612),l(.664,.544),l(.212,.145),
+N(204.31,158.989),l(-.175,.412),l(.612,-.173),l(.026,.429),l(-.419,1.241),l(.178,.269),l(-.237,.795),l(.189,.318),l(-.092,.397),l(-.358,.875),l(-.3,.35),l(-.36,.032),l(-.054,.286),l(-.388,.238),l(0,.286),l(-.69,.016),l(.215,-4.297),l(-.025,-.329),l(.278,-.095),l(.386,.27),l(.599,-.857),l(.178,-.447),l(.437,-.016),
+N(200.276,169.481),l(-.151,-.056),l(-.928,-.342),l(-.614,.032),l(-.766,-.032),l(-.608,-.239),l(-.909,-.656),l(-.513,-.419),l(-.044,-.454),l(-.054,-.553),l(.207,-.238),l(-.305,-.397),l(1.076,-1.812),l(1.06,-.007),l(1.762,.007),l(.211,-.683),l(-.532,-.223),l(-.359,-.69),l(-.624,-.231),l(-.495,-.541),l(-.396,-.223),l(.999,-.032),l(.04,-1.24),l(1.792,.005),l(2.332,.006),l(-.215,4.297),l(.69,-.016),l(.303,.095),l(.311,.302),l(.14,-.191),l(-.066,-.381),l(.336,.157),l(.458,.367),l(-1.507,1.208),l(-.499,.238),l(-.177,.493),l(.162,.604),l(-.438,.302),l(-.467,.048),l(-.043,.254),l(.164,.159),l(-.351,.111),l(-.184,.302),l(-.22,-.016),l(-.565,.461),l(-.012,.223),
+N(204.413,165.093),l(.312,-.03),l(.612,-.27),l(.639,-.058),l(.743,.126),l(.478,.069),l(1.443,.04),l(.699,-.228),l(.379,-.199),l(.567,.267),l(.788,-.03),l(.763,-.101),l(.63,-.001),l(.5,.126),l(.564,.253),l(-.038,.353),l(-.102,.226),l(.228,.282),l(.787,.238),l(.557,.069),l(.244,.524),l(-1.425,.486),l(-.424,.229),l(-.248,.086),l(-.463,-.097),l(-.328,-.182),l(-.259,-.013),l(-.294,.242),l(-.503,.794),l(-1.207,.997),l(-.725,-.42),l(-.513,.583),l(-.882,.034),l(-.005,.961),l(-.293,.412),l(-.29,.143),l(-1.001,.125),l(-.311,-.661),l(-.025,-.085),l(-.478,-.3),l(.085,-.731),l(-.128,-.175),l(-.272,0),l(-.541,-.276),l(-.433,.34),l(-.365,-.016),l(-.066,-.271),l(-.565,-.35),l(-.409,-.08),l(-.208,-.418),l(-.677,-.17),l(.438,-.302),l(-.162,-.604),l(.177,-.493),l(.499,-.238),l(1.507,-1.208),
+N(205.532,170.085),l(.035,.076),l(-.203,.057),l(.01,.265),l(-.237,.334),l(-.68,-.046),l(-.853,-.139),l(-1.697,-.505),l(-1.305,-.435),l(-.325,-.21),l(.012,-.223),l(.565,-.461),l(.22,.016),l(.184,-.302),l(.351,-.111),l(-.164,-.159),l(.043,-.254),l(.467,-.048),l(.677,.17),l(.208,.418),l(.409,.08),l(.565,.35),l(.066,.271),l(.365,.016),l(.433,-.34),l(.541,.276),l(.272,0),l(.128,.175),l(-.085,.731),
+N(242.38,173.617),l(-.128,-.105),l(-.84,.171),l(-.534,.156),l(-.414,.2),l(-.056,.288),l(.048,.497),l(-.129,.396),l(-.227,-.027),l(-.381,.059),l(-.99,1.758),l(-.172,.722),l(-.241,.722),l(-.709,1.191),l(.402,.025),l(.234,-.1),l(.384,-.017),l(.31,.606),l(.855,1.45),l(.103,.395),l(-.226,1.132),l(.099,.353),l(.401,.309),l(.429,.548),l(.397,.252),l(.496,-.017),l(1.163,-.12),l(1.167,-.05),l(.521,.181),l(.64,.321),l(.188,.253),l(.847,.998),l(.554,.576),l(.144,0),l(.522,-.13),l(.76,-.174),l(1.99,-.224),l(.644,.081),l(-.409,.525),l(-.085,1.004),l(-.379,.511),l(-.147,.326),l(.026,.254),l(.035,.438),l(.048,.367),l(.162,.804),l(.447,.789),l(.256,.437),l(.486,.647),l(.121,.282),l(-.731,.612),l(-.479,.526),l(.51,.491),l(.797,1.182),l(-.52,.286),l(-.834,.57),l(-.412,.158),l(-.463,.017),l(-2.812,-.082),l(-.64,-.024),l(-.042,.325),l(-.013,1.031),l(.178,.154),l(.896,.122),l(.177,.084),l(.293,.408),l(.052,.367),l(-.384,-.04),l(-.417,-.11),l(-.687,.032),l(-.493,.187),l(-.111,.085),l(-.001,1.071),l(0,.554),l(.192,.197),l(.688,.363),l(.192,.183),l(-.031,.777),l(.399,.562),l(.031,.212),l(-.326,1.428),l(-.706,4.411),l(-.073,.382),l(-.133,.198),l(-.156,-.14),l(-.575,-.703),l(-.237,-.126),l(-.161,.058),l(-.448,.031),l(1.155,-1.956),l(.035,-.198),l(-.127,-.069),l(-1.299,-.84),l(-.509,-.209),l(-.708,.442),l(-.397,-.182),l(-.523,-.421),l(-.452,.427),l(-.337,.157),l(-.496,.031),l(-1.038,-.008),l(-.573,-.152),l(-.092,-.281),l(.004,-.396),l(-.173,-.296),l(-.479,-.039),l(-.366,-.14),l(-.078,-.282),l(-.251,-.761),l(-.988,-.672),l(-.526,-.364),l(-.208,-.62),l(-.208,-.324),l(-.513,-.435),l(-.897,-.418),l(-.927,-.107),l(-.081,-.112),l(-.269,-.162),l(-.197,-.118),l(-.709,-.631),l(-.128,-.056),l(-.89,.401),l(-.67,.061),l(-.977,-.277),l(-.355,-.309),l(-.166,-.493),l(-.227,-.225),l(-1.432,-.656),l(-1.496,-.803),l(.033,-.068),l(-.117,-.311),l(-.181,-.282),l(.108,-.212),l(.509,-.114),l(.465,.112),l(.186,-.325),l(-.348,-.564),l(.086,-.424),l(.314,-.227),l(.878,-.058),l(.193,.042),l(.41,-.227),l(.445,-.679),l(.45,-.961),l(.651,-1.061),l(-.122,-.268),l(-.56,-.408),l(-.071,-.184),l(.306,-.721),l(.089,-.523),l(-.149,-.861),l(-.371,-.86),l(.085,-.254),l(.49,-.143),l(.135,-.212),l(-.088,-.198),l(-.565,-.479),l(-.042,-.226),l(.103,-.198),l(.242,-.326),l(.036,-.254),l(-.173,-.282),l(-.739,-.719),l(-.364,-.396),l(.256,-.753),l(.228,-.082),l(-.045,-.45),l(.19,.082),l(.085,.307),l(.584,-.409),l(.094,-.43),l(.322,-.062),l(-.509,-1.038),l(-.228,-.149),l(-.084,-.327),l(.142,.075),l(.42,.338),l(.397,.507),l(.22,.508),l(.235,.197),l(.199,-.17),l(.147,-.241),l(-.332,-.776),l(.02,-.212),l(.176,-.297),l(.445,-.34),l(.399,-.297),l(.501,-.736),l(.327,-.156),l(.684,-.101),l(.217,-.382),l(-.104,-.381),l(.174,-.777),l(.067,-.65),l(.207,-.48),l(.498,-.439),l(.429,-.283),l(.592,-.242),l(.113,0),l(.374,.206),l(.22,.443),l(.281,-.34),l(.031,-.438),l(.199,-.551),l(.22,-.071),l(.267,.126),l(.855,.041),l(.562,-.001),l(.623,-.27),l(.237,-.254),l(.476,-.298),l(.848,-.256),l(.435,-.396),l(.278,-.551),l(.333,-.255),l(.469,-.17),l(.58,.013),l(.526,.338),l(.155,.126),l(.155,.339),l(-.285,.332),
+N(408.6,174.04),l(-.062,.777),l(.164,.762),l(.388,.718),l(-.316,.962),l(-.19,.566),l(-.223,.298),l(-.656,.414),l(-.095,.34),l(.116,.592),l(-.078,.368),l(-.433,.13),l(-.257,.03),l(-.237,.397),l(.002,.381),l(.003,.48),l(-.012,.876),l(-.09,.989),l(.217,1.439),l(-.121,1.188),l(.006,.234),l(-.333,.015),l(-1.232,.144),l(-.669,.052),l(-.106,-.205),l(-.295,-1.072),l(.188,-.708),l(.026,-.833),l(-.036,-.537),l(-.135,-.974),l(-.006,-.862),l(-.035,-.522),l(.013,-.579),l(-.442,-1.311),l(-.164,-.691),l(-.403,-.323),l(-.468,-.294),l(-.18,-.395),l(.141,-.594),l(.046,-.424),l(.062,-.142),l(.24,-.157),l(.366,-.553),l(.415,-.271),l(.418,-.045),l(.739,.095),l(.337,-.158),l(.415,-.468),l(.237,-.764),l(-.131,-.367),l(.577,-.3),l(.321,-.073),l(.339,.28),l(.727,.589),l(.902,.503),
+N(394.266,178.814),l(.191,-.17),l(-.02,-.41),l(-.261,-1.934),l(.125,-.34),l(.271,-.157),l(2.119,-.041),l(.867,.037),l(1.429,.006),l(.976,-.458),l(.161,.032),l(.595,.119),l(-.25,.849),l(.05,.254),l(.633,.915),l(.244,.451),l(-.188,.721),l(.02,.396),l(.265,1.114),l(.181,.592),l(.503,.788),l(.032,.155),l(-.286,.242),l(-.174,.382),l(.01,1.314),l(-.011,.72),l(.021,.551),l(.18,.479),l(.468,.577),l(.752,.608),l(-.503,.682),l(-.304,.099),l(-.593,-.013),l(-.992,.144),l(-.463,.185),l(-.188,.098),l(-.898,.469),l(-1.263,.398),l(-.942,.412),l(-.958,.567),l(-.578,-.324),l(-.945,-.436),l(-.074,-.124),l(-.066,-.741),l(-.554,-1.155),l(-.263,-.747),l(-.103,-.706),l(.327,-1.005),l(.437,-1.274),l(.521,-.851),l(.203,-.595),l(-.334,-1.438),l(-.189,-1.285),l(-.178,-.154),
+N(400.72,175.499),l(1.013,.163),l(.611,.166),l(.29,.041),l(-.046,.424),l(-.141,.594),l(.18,.395),l(.468,.294),l(.403,.323),l(.164,.691),l(.442,1.311),l(-.013,.579),l(.035,.522),l(.006,.862),l(.135,.974),l(.036,.537),l(-.026,.833),l(-.188,.708),l(.295,1.072),l(.106,.205),l(-.452,.035),l(-.479,.128),l(-.368,.212),l(-.023,.04),l(-.752,-.608),l(-.468,-.577),l(-.18,-.479),l(-.021,-.551),l(.011,-.72),l(-.01,-1.314),l(.174,-.382),l(.286,-.242),l(-.032,-.155),l(-.503,-.788),l(-.181,-.592),l(-.265,-1.114),l(-.02,-.396),l(.188,-.721),l(-.244,-.451),l(-.633,-.915),l(-.05,-.254),l(.25,-.849),
+N(383.772,190.418),l(.041,-.919),l(.105,-.565),l(.247,-.849),l(.059,-.452),l(-.131,-.282),l(-1.264,-1.334),l(-.592,.032),l(-.369,-.097),l(-.194,-.183),l(-.05,-.183),l(.501,-.865),l(.059,-.438),l(-.119,-.55),l(.062,-.17),l(.047,-.099),l(.602,-.554),l(.188,-.354),l(-.179,-.21),l(-.296,-.549),l(.03,-.127),l(.158,-.199),l(.304,-.03),l(.548,.223),l(.304,.012),l(.19,-.143),l(-.051,-.226),l(-.648,-.561),l(-.309,-.351),l(.332,-.37),l(.125,-.283),l(-.197,-.31),l(-.695,-.405),l(-.214,-.409),l(.264,-.623),l(.379,-.397),l(.522,-.54),l(.352,-.059),l(.566,.321),l(.455,.436),l(.35,-.228),l(.362,-.469),l(.571,-.483),l(.239,-.114),l(.257,.041),l(.147,.211),l(.004,.297),l(.117,.381),l(.148,.239),l(.352,-.059),l(.604,-.399),l(.159,-.1),l(.259,.168),l(.545,-.003),l(.308,.252),l(.15,.465),l(.52,.562),l(.452,.223),l(.354,.083),l(.368,-.115),l(.446,-.257),l(.608,-.159),l(.769,-.075),l(.644,.166),l(.63,.434),l(.178,.154),l(.189,1.285),l(.334,1.438),l(-.203,.595),l(-.521,.851),l(-.437,1.274),l(-.327,1.005),l(.103,.706),l(.263,.747),l(.554,1.155),l(.066,.741),l(-.119,-.201),l(-.289,-.197),l(-.208,.015),l(-.143,.24),l(-.096,.042),l(-.48,-.027),l(-.705,-.167),l(-.608,-.083),l(-2.352,.062),l(-1.279,.243),l(-.575,.199),l(-1.07,.369),l(-1.739,.781),l(-.701,.425),l(-.256,-.013),l(-.075,-.032),
+N(627.408,186.411),l(.035,.024),l(.523,.098),l(1.061,1.198),l(.693,.916),l(.506,.762),l(.081,.579),l(-.023,.805),l(-.134,.862),l(.07,1.073),l(-.07,.636),l(.097,.494),l(.196,.381),l(.53,.493),l(.338,.536),l(.705,1.608),l(.062,.127),l(-.366,-.069),l(-.895,-.055),l(-.401,.142),l(-.175,-.07),l(-.521,-.437),l(-.885,-.605),l(-.6,-.337),l(-1.231,-.675),l(-1.02,-.732),l(-.217,-.254),l(-.16,-.988),l(-.438,-.691),l(-.895,-.817),l(-.195,-.663),l(.039,-.494),l(.03,-.283),l(-.107,-.409),l(-.399,-.634),l(-.347,-1.849),l(-.188,-.72),l(.043,-.362),l(.448,-.17),l(.571,-.013),l(.106,.466),l(.447,.119),l(.261,.238),l(.055,.226),l(.041,.21),l(-.213,.398),l(.118,.239),l(.204,.083),l(.244,-.394),l(.279,-.28),l(.309,.163),l(.05,.478),l(.644,-.089),l(.11,-.575),l(.495,-.35),l(.086,-.337),M(643.95,196.042),l(.081,.044),l(.375,.408),l(.397,.141),l(.861,.083),l(.413,.168),l(.761,.436),l(.335,.042),l(.337,-.1),l(-.503,-.535),l(.169,-.551),l(.365,-.608),l(.528,-1.258),l(.584,-.27),l(1.481,-.342),l(1.018,-.299),l(.428,-.326),l(.524,-1.021),l(.523,-.323),l(.87,-1.117),l(.22,-.212),l(.244,-.147),l(.112,-.068),l(.084,.126),l(.062,.37),l(.234,-.012),l(.111,.259),l(.309,.271),l(.383,-.271),l(1.234,-.42),l(-.025,-.086),l(-.197,-.197),l(.013,-.247),l(.024,-.111),l(-.148,-.444),l(-.11,-.174),l(.167,-.042),l(.188,.143),l(.085,-.016),l(.449,-.084),l(.161,-.283),l(-.036,-.24),l(-.478,-.366),l(.197,-.396),l(.436,-.071),l(.511,.041),l(.154,-.605),l(.408,-.313),l(.284,-.48),l(.531,-.58),l(.498,-1.032),l(.075,-.17),l(.322,.465),l(.33,.098),l(.315,-.424),l(.305,.494),l(.351,.282),l(.402,.211),l(.075,.338),l(-.182,.354),l(-.234,.452),l(.037,.198),l(.631,-.128),l(.472,-.128),l(.115,.226),l(-.349,.495),l(.742,-.1),l(.357,-.085),l(.269,.056),l(1.035,.662),l(.345,.14),l(.1,.24),l(-.158,.269),l(-.201,.155),l(-.44,.143),l(-.594,.029),l(-.683,-.083),l(-.355,.311),l(.096,.254),l(.888,.69),l(-.161,.311),l(-.458,.199),l(-.646,.1),l(-.518,.114),l(-.067,.15),l(-.182,-.09),l(-.753,-.292),l(-.446,-.04),l(-.624,.018),l(-.651,-.081),l(-.748,-.081),l(-.464,.017),l(-.247,.157),l(-.381,.638),l(-.119,.565),l(.106,.988),l(-.22,.383),l(-.646,.244),l(-.218,.34),l(.076,.748),l(-.928,1.785),l(-.387,.299),l(-.864,.175),l(-.465,.172),l(-.467,.356),l(-.287,.03),l(-.559,-.152),l(-.524,-.223),l(-.557,-.251),l(-.431,-.11),l(-.479,.074),l(-.531,.441),l(-.403,.525),l(-.528,.342),l(-.399,.172),l(-.543,-.11),l(-.526,-.223),l(-.255,.001),l(-.928,.416),l(-.446,-.28),l(-.304,-.083),l(-1,-.983),l(-.253,-.295),l(-.288,-.792),
+N(274.556,195.884),l(.06,-.212),l(-.332,-.563),l(-.49,-1.127),l(-.246,-.832),l(-.185,-.295),l(-.561,-.067),l(-.532,-.675),l(-.571,-.831),l(.328,-.694),l(.095,-.467),l(-.078,-.777),l(.169,-.17),l(1.131,-.091),l(.183,-.27),l(.082,-.862),l(.142,-.496),l(.015,-.339),l(.326,-.312),l(.382,-.057),l(1.392,.463),l(.465,.042),l(.083,-.41),l(.141,-.085),l(.337,.027),l(.833,.012),l(.863,-.03),l(.723,.069),l(.63,.182),l(.999,.427),l(-.647,.876),l(-.391,.751),l(-.137,.594),l(.094,.381),l(.134,.635),l(.086,.664),l(.521,.844),l(.029,.438),l(-.424,1.472),l(-.489,.963),l(-1.05,-.488),l(-.319,.001),l(-.534,.385),l(-.398,.059),l(-.418,-.139),l(-.642,-.124),l(-.172,.156),l(-.2,.326),l(.611,1.014),l(-.528,-.054),l(-1.108,-.276),l(-.4,-.04),
+N(285.859,190.719),l(.015,.422),l(-1.102,1.646),l(-.427,.765),l(-.439,.992),l(-.464,.681),l(-.299,.214),l(-.56,-.025),l(-1.11,-.389),l(-.882,.613),l(-.225,-.069),l(-.649,-.505),l(.489,-.963),l(.424,-1.472),l(-.029,-.438),l(-.521,-.844),l(-.086,-.664),l(-.134,-.635),l(-.094,-.381),l(.137,-.594),l(.391,-.751),l(.647,-.876),l(.218,.093),l(1.033,.294),l(.55,.196),l(.799,.549),l(1.264,1.084),l(.545,.564),l(.317,.465),l(.193,.028),
+N(429.505,210.684),l(-.695,-.533),l(-.351,.13),l(-.68,.513),l(-.536,.414),l(-.112,-.177),l(-.392,-.86),l(-1.381,-1.344),l(.184,-.295),l(.413,-.229),l(.803,.307),l(.343,-.681),l(-.052,-.296),l(-.274,-.253),l(-.39,-.521),l(-.116,-.366),l(.058,-.495),l(.127,-.1),l(.909,-.146),l(.604,-.243),l(.125,-.213),l(.167,-.807),l(.174,-.185),l(.239,-.029),l(.193,.14),l(.341,.479),l(.405,.521),l(.386,.195),l(.287,-.016),l(.188,-.228),l(.362,-.482),l(.29,.253),l(.167,.578),l(.146,.112),l(.304,.068),l(.255,-.114),l(.184,-.68),l(.243,-1.089),l(.131,-1.229),l(.229,-1.047),l(-.114,-.338),l(-.161,-.127),l(-.384,-.082),l(-.528,-.208),l(-.242,-.31),l(-.132,-.437),l(.008,-.109),l(.021,-.3),l(.157,-.284),l(.492,-.398),l(.556,-.441),l(.094,-.354),l(-.389,-.902),l(-.369,-.322),l(-.592,-.067),l(-.939,.5),l(-.463,-.025),l(-.146,-.253),l(-.068,-.734),l(.077,-.679),l(.396,-.468),l(.56,.109),l(.863,.023),l(.878,-.076),l(.271,.055),l(.479,.04),l(.56,.124),l(.576,.194),l(.864,.334),l(.863,.292),l(.271,-.086),l(-.1,-.861),l(.159,-.185),l(.303,-.312),l(.365,-.497),l(.158,-.523),l(.222,-1.287),l(.255,-.228),l(.479,-.102),l(.975,-.175),l(.367,.054),l(.624,.138),l(.895,.093),l(.751,-.287),l(.128,.197),l(-.031,.156),l(-.398,1.203),l(-.558,.837),l(-.349,.821),l(-.094,.551),l(-.156,.764),l(-.196,2.021),l(-.254,1.342),l(-.115,.61),l(-.169,.708),l(-.139,.523),l(-.459,.427),l(-.525,.229),l(-.809,.599),l(-.41,.454),l(-.47,.836),l(-.343,.751),l(-.193,1.159),l(-.074,.396),l(.122,.72),l(-.086,.706),l(-1.025,.938),l(-.364,.229),l(-.931,.811),l(-.554,.399),l(-.57,.328),l(-.147,-.211),l(-.042,-.664),l(-.054,-.424),l(-1.495,.532),l(-.327,.581),l(-.443,.271),l(-.177,-.013),l(-.484,-.336),
+N(425.506,195.522),l(.045,-.495),l(.88,.093),l(1.773,.088),l(.831,.038),l(1.022,.149),l(-.396,.468),l(-.077,.679),l(.068,.734),l(.146,.253),l(.463,.025),l(.939,-.5),l(.592,.067),l(.369,.322),l(.389,.902),l(-.094,.354),l(-.556,.441),l(-.492,.398),l(-.157,.284),l(-.021,.3),l(-.008,.109),l(.132,.437),l(.242,.31),l(.528,.208),l(.384,.082),l(.161,.127),l(.114,.338),l(-.229,1.047),l(-.131,1.229),l(-.243,1.089),l(-.184,.68),l(-.255,.114),l(-.304,-.068),l(-.146,-.112),l(-.167,-.578),l(-.29,-.253),l(-.362,.482),l(-.188,.228),l(-.287,.016),l(-.386,-.195),l(-.405,-.521),l(-.341,-.479),l(-.193,-.14),l(-.239,.029),l(-.174,.185),l(-.167,.807),l(-.125,.213),l(-.604,.243),l(-.909,.146),l(-.127,.1),l(-.058,.495),l(.116,.366),l(.39,.521),l(.274,.253),l(.052,.296),l(-.343,.681),l(-.803,-.307),l(-.413,.229),l(-.184,.295),l(-.038,-.037),l(-.563,-.493),l(-.532,-.55),l(-.259,-.466),l(-1.318,-1.169),l(.286,-.312),l(-.369,-.281),l(-.528,-.056),l(-.918,-1.438),l(.382,-.297),l(.111,-.1),l(-.321,-.395),l(-.464,-.14),l(-.29,-.564),l(-.369,-.451),l(.319,-.17),l(.541,-.411),l(.223,-.396),l(.291,-.976),l(.089,-.295),l(.415,.07),l(.495,.013),l(-.064,-.211),l(-.56,-.352),l(-.561,-.451),l(.208,-.042),l(.271,-.128),l(.142,-.617),l(.83,-.043),l(.927,.008),l(1.245,.021),l(.575,-.032),l(-.001,-.155),l(-.02,-.551),l(-.026,-1.836),
+N(422.536,195.513),l(1.005,-.007),l(1.965,.017),l(.026,1.836),l(.02,.551),l(.001,.155),l(-.575,.032),l(-1.245,-.021),l(-.927,-.008),l(-.83,.043),l(.033,-.146),l(-.049,-.297),l(-.144,-.084),l(-.416,-.098),l(.287,-.467),l(.447,-.523),l(.335,-.58),l(.066,-.403),M(419.636,193.273),l(-.484,-.049),l(-.049,-.218),l(.049,-.291),l(.242,-.097),l(.024,-.146),l(0,-.291),l(.17,-.097),l(.169,-.121),l(.219,.048),l(.218,.048),l(.097,.194),l(-.193,.169),l(-.146,.243),l(-.17,.339),l(-.146,.267),
+N(222.291,207.801),l(.188,-.126),l(.264,-.312),l(.585,-1.061),l(.038,-.269),l(-.209,-.733),l(.058,-.391),l(-.478,.617),l(-.732,.623),l(-.333,-.098),l(-.601,-.324),l(-.408,-.408),l(.389,-.283),l(.1,-.24),l(-.167,-.579),l(.005,-.438),l(.133,-.466),l(-.109,-.282),l(-.362,-.649),l(.193,-.17),l(.48,-.198),l(.721,-.454),l(-.319,-.395),l(0,-.226),l(.175,-.354),l(.497,-.462),l(.157,-.146),l(.14,-.848),l(-.116,-.452),l(-.082,-.184),l(.11,-.226),l(.686,-.101),l(.891,-.341),l(.396,-.241),l(.474,-.382),l(.09,-.186),l(1.496,.803),l(1.432,.656),l(.227,.225),l(.166,.493),l(.355,.309),l(.977,.277),l(.67,-.061),l(.89,-.401),l(.128,.056),l(.709,.631),l(.197,.118),l(.269,.162),l(.081,.112),l(.387,.76),l(.321,.789),l(-.08,.255),l(-.352,.384),l(-.547,1.147),l(-.533,.779),l(-.71,.725),l(-1.001,.755),l(-.336,.087),l(-1.028,.303),l(-.901,.358),l(-.809,.499),l(-.59,.653),l(-.284,.51),l(-.987,2.506),l(-.267,.312),l(-.242,.072),l(-.398,-.068),l(-.398,-.491),l(-.191,-.479),l(-.312,-.252),l(-.815,.005),l(-.444,-.11),l(-.105,-.197),l(-.097,-.494),l(.233,-.34),l(.029,-.495),l(.005,-.569),
+N(245.934,224.314),l(1.109,1.843),l(.922,1.646),l(.08,.24),l(-.437,.736),l(-.15,.523),l(-.143,.848),l(.057,.522),l(.236,.902),l(-.156,.34),l(-.221,.34),l(-.532,.582),l(-.102,.452),l(.347,.845),l(-.21,.368),l(-.159,.34),l(.022,.48),l(.604,1.168),l(.015,.522),l(-.285,.439),l(-.723,.61),l(-.12,.227),l(.049,.48),l(.132,.295),l(.018,.142),l(-.586,.187),l(-.15,.199),l(-.228,.933),l(-.259,.298),l(-.852,.275),l(-.201,-.517),l(-.289,-.31),l(-1.422,-.618),l(-.313,-.239),l(-.233,-.678),l(-.315,-.38),l(-.559,-.225),l(-.504,-.281),l(-.489,-.254),l(-.616,-.309),l(-.873,-.464),l(-.673,-.366),l(-.866,-.379),l(-.838,-.337),l(-1.271,-.845),l(-1.354,-.985),l(-1.232,-1.099),l(-.594,-.705),l(-.42,-.677),l(.008,-.438),l(0,-.903),l(-.269,-.606),l(-.781,-1.043),l(-1.389,-2.778),l(-.285,-.268),l(-.467,-.211),l(-.074,-.085),l(.055,-.536),l(-.02,-.396),l(-.149,-.396),l(-1.208,-2.199),l(-.997,-1.961),l(-.688,-1.27),l(-1.365,-1.917),l(-.272,-.353),l(-.598,-.973),l(-.462,-.423),l(-.75,-.437),l(-.914,-.365),l(-.61,-.365),l(.119,-.17),l(.181,-.113),l(.609,-.029),l(.144,-.41),l(-.118,-.282),l(-.592,-.874),l(-.353,-.437),l(-.258,-.72),l(.01,-.24),l(.244,-.523),l(.582,-.622),l(1.02,-.878),l(.47,-.198),l(.317,-.214),l(-.005,.569),l(-.029,.495),l(-.233,.34),l(.097,.494),l(.105,.197),l(.444,.11),l(.815,-.005),l(.312,.252),l(.191,.479),l(.398,.491),l(.398,.068),l(.242,-.072),l(.267,-.312),l(.987,-2.506),l(.284,-.51),l(.59,-.653),l(.809,-.499),l(.901,-.358),l(1.028,-.303),l(.336,-.087),l(1.001,-.755),l(.71,-.725),l(.533,-.779),l(.547,-1.147),l(.352,-.384),l(.08,-.255),l(-.321,-.789),l(-.387,-.76),l(.927,.107),l(.897,.418),l(.513,.435),l(.208,.324),l(.208,.62),l(.526,.364),l(.988,.672),l(.251,.761),l(.078,.282),l(.366,.14),l(.479,.039),l(.173,.296),l(-.004,.396),l(.092,.281),l(.573,.152),l(1.038,.008),l(.496,-.031),l(.337,-.157),l(.452,-.427),l(.523,.421),l(.397,.182),l(.708,-.442),l(.509,.209),l(1.299,.84),l(.127,.069),l(-.035,.198),l(-1.155,1.956),l(.448,-.031),l(.161,-.058),l(.237,.126),l(.575,.703),l(.156,.14),l(.136,.324),l(-.341,.186),l(-.793,-.235),l(-.493,-.096),l(-.322,.072),l(-.604,.441),l(-.371,.115),l(-.669,-.066),l(-1.895,.773),l(-.701,.442),l(-.372,.172),l(-.599,1.415),l(-.319,.511),l(-.021,.155),l(.277,.86),l(.012,.112),l(-.625,.498),l(-.62,.328),l(-.349,.342),l(-.247,.622),l(.106,.564),l(.619,.957),l(1.333,2.166),l(.106,.127),l(-.247,.495),l(-.208,.312),l(1.085,.12),l(.415,.04),l(.312,.182),l(.241,.323),l(.096,.663),l(1.843,.032),l(.762,-.499),l(.648,-.484),l(.326,-.114),l(-.059,1.511),l(-.098,1.173),l(.108,.353),l(.317,.337),l(.285,.068),l(.358,-.072),l(.786,-.259),l(.42,-.045),
+N(279.288,257.295),l(-.063,-.423),l(.027,-.777),l(.222,-.819),l(.298,-.835),l(.321,-.75),l(-.019,-.141),l(-.491,-.336),l(-.234,-.112),l(-1.29,.445),l(-.224,-.055),l(-.254,-.563),l(-.487,-2.524),l(-.121,-.437),l(-.843,-.404),l(-.724,-.278),l(-.115,.001),l(-.709,.413),l(-.346,.03),l(-.925,-.221),l(-.818,-.15),l(-.372,-.04),l(.006,-.72),l(.086,-.721),l(.132,-.862),l(-.104,-.959),l(-.521,-.872),l(-.084,-.663),l(-.253,-.408),l(.421,-.666),l(.38,-.765),l(.238,-.834),l(.185,-1.173),l(-.022,-.494),l(-.307,-.774),l(-.316,-.479),l(-.73,-.362),l(-.436,-.407),l(-.067,-.563),l(.261,-.835),l(-.042,-.296),l(-.693,-.024),l(-1.615,-.019),l(-.5,-.012),l(-.967,-.036),l(-.153,-.112),l(-.249,-.944),l(-.138,-2.032),l(-.03,-.706),l(-.203,-1.029),l(.038,-.565),l(-.085,-.169),l(-.403,-.237),l(-.537,-.166),l(-.636,-.123),l(-1.148,.077),l(-.22,-.084),l(-.398,-.308),l(-.385,-.774),l(-.096,-.014),l(-.882,-.037),l(-.3,-.098),l(-.75,-.603),l(-.688,-.307),l(-.676,-.25),l(-.355,.03),l(-.787,-.023),l(-.316,-.097),l(-1.1,-.939),l(-.813,-.928),l(-.335,-.591),l(-.141,-.635),l(-.036,-.818),l(.064,-.622),l(.079,-.621),l(-.045,-.748),l(-1.283,-.021),l(-.934,.076),l(-.36,.158),l(-.874,.485),l(-1.471,1.109),l(-.415,.243),l(-.48,-.025),l(-.336,-.026),l(-.946,.824),l(-.355,.03),l(-1.122,-.304),l(-.939,-.136),l(-.42,.045),l(-.786,.259),l(-.358,.072),l(-.285,-.068),l(-.317,-.337),l(-.108,-.353),l(.098,-1.173),l(.059,-1.511),l(-.326,.114),l(-.648,.484),l(-.762,.499),l(-1.843,-.032),l(-.096,-.663),l(-.241,-.323),l(-.312,-.182),l(-.415,-.04),l(-1.085,-.12),l(.208,-.312),l(.247,-.495),l(-.106,-.127),l(-1.333,-2.166),l(-.619,-.957),l(-.106,-.564),l(.247,-.622),l(.349,-.342),l(.62,-.328),l(.625,-.498),l(-.012,-.112),l(-.277,-.86),l(.021,-.155),l(.319,-.511),l(.599,-1.415),l(.372,-.172),l(.701,-.442),l(1.895,-.773),l(.669,.066),l(.371,-.115),l(.604,-.441),l(.322,-.072),l(.493,.096),l(.793,.235),l(.341,-.186),l(-.136,-.324),l(.133,-.198),l(.073,-.382),l(.706,-4.411),l(.326,-1.428),l(-.031,-.212),l(-.399,-.562),l(.031,-.777),l(-.192,-.183),l(-.688,-.363),l(-.192,-.197),l(0,-.554),l(.001,-1.071),l(.111,-.085),l(.493,-.187),l(.687,-.032),l(.417,.11),l(.384,.04),l(-.052,-.367),l(-.293,-.408),l(-.177,-.084),l(-.896,-.122),l(-.178,-.154),l(.013,-1.031),l(.042,-.325),l(.64,.024),l(2.812,.082),l(.463,-.017),l(.412,-.158),l(.834,-.57),l(.52,-.286),l(.148,.168),l(.138,.437),l(.161,.861),l(.088,.452),l(.199,.437),l(.432,.054),l(.694,.546),l(.482,.223),l(.414,-.073),l(.757,-.697),l(.083,.183),l(.186,.776),l(.271,-.016),l(.645,-.739),l(.74,-.654),l(.554,-.286),l(.652,-.173),l(.235,-.213),l(.259,-.666),l(.203,-.199),l(.652,-.131),l(.569,-.272),l(.265,-.27),l(-.15,-.253),l(-.434,-.125),l(-.96,-.051),l(-.166,-.239),l(-.079,-.55),l(-.302,-1.524),l(-.391,-.69),l(-.616,-.688),l(.041,-.184),l(.159,-.029),l(.309,.125),l(.896,.461),l(.385,.04),l(1.149,-.035),l(.344,.224),l(.628,.618),l(.348,-.115),l(.232,-1.117),l(.284,-.115),l(.465,.054),l(.557,-.074),l(.807,-.23),l(1.74,-.9),l(.592,-.385),l(.163,-.326),l(-.103,-.169),l(.375,-.257),l(.302,-.058),l(.516,.124),l(.26,.111),l(.04,.212),l(-.452,1.09),l(.304,-.001),l(.533,.138),l(.435,.958),l(-.179,.368),l(-.547,1.416),l(-.213,.962),l(.156,.465),l(.281,.394),l(.167,.31),l(-.072,.354),l(.008,.396),l(.15,.296),l(1.103,.897),l(.613,.392),l(.686,-.075),l(.694,-.513),l(.505,-.314),l(1,-.331),l(.934,-.429),l(.943,.022),l(.4,.04),l(1.108,.276),l(.528,.054),l(-.611,-1.014),l(.2,-.326),l(.172,-.156),l(.642,.124),l(.418,.139),l(.398,-.059),l(.534,-.385),l(.319,-.001),l(1.05,.488),l(.649,.505),l(.225,.069),l(.882,-.613),l(1.11,.389),l(.56,.025),l(.299,-.214),l(.464,-.681),l(.439,-.992),l(.427,-.765),l(1.102,-1.646),l(-.015,-.422),l(.409,-.241),l(.276,.141),l(.235,.423),l(.164,.791),l(.414,1.34),l(.108,.607),l(.472,1.383),l(.296,.55),l(.324,.324),l(.978,.21),l(.196,.338),l(.038,.551),l(-.045,.254),l(-.524,.354),l(-.933,1.414),l(-.238,.325),l(-.51,.411),l(-.41,.231),l(-.244,.138),l(-.798,.778),l(-.544,.947),l(-.419,1.018),l(-.402,.453),l(.271,.112),l(.512,-.072),l(.497,-.198),l(1.202,-.709),l(.112,-.028),l(.299,.889),l(.442,.818),l(.255,.112),l(.287,.07),l(1.135,-.031),l(.592,-.058),l(.737,-.298),l(-.277,.679),l(-.017,.584),l(.448,-.557),l(1.254,-1.006),l(.289,-.156),l(.306,-.382),l(.499,-.933),l(.272,-.396),l(.256,-.099),l(1.006,-.003),l(.352,-.198),l(.175,0),l(.687,.549),l(.654,.267),l(.32,-.028),l(.814,.28),l(1.069,.45),l(.334,.281),l(.269,.593),l(.08,.028),l(.176,-.015),l(.402,-.354),l(.319,.042),l(.302,.254),l(.538,.789),l(.205,.396),l(-.13,.254),l(-.308,.41),l(-.116,.424),l(.091,.466),l(.171,.396),l(.825,-.821),l(.369,-.156),l(.609,-.156),l(.995,-.454),l(.224,-.028),l(.367,.069),l(.764,.38),l(.907,.436),l(.462,.098),l(1.117,.167),l(1.118,.083),l(.464,-.058),l(.48,-.029),l(.577,-.171),l(.416,-.016),l(1.131,.351),l(.731,.408),l(.762,.479),l(.586,.437),l(1.595,1.395),l(.742,.662),l(.821,.605),l(.41,.338),l(.477,.196),l(.734,.168),l(1.247,.097),l(.912,.04),l(.445,.197),l(.265,.465),l(.087,.607),l(.592,2.004),l(.268,1.143),l(.062,.988),l(-.076,.579),l(-.179,.904),l(-.243,.848),l(-.559,1.301),l(-.518,.849),l(-1.347,1.175),l(-1.388,1.288),l(-.44,.198),l(-.354,.043),l(-.39,.637),l(-.094,.396),l(-.012,.353),l(-.504,1.074),l(-.6,.976),l(-.8,1.104),l(-.266,.212),l(-.194,.015),l(-.079,-.423),l(-.278,-.296),l(-.367,-.084),l(-.064,.79),l(-.538,1.64),l(-.049,.734),l(-.077,.607),l(.318,2.738),l(.078,.988),l(-.577,2.543),l(-.039,.663),l(.14,.649),l(.043,.085),l(-.759,.495),l(-.404,.51),l(-.261,.791),l(-.04,.367),l(.245,1.468),l(-.106,.367),l(-.214,.283),l(-.676,.608),l(-.752,1.498),l(-1.01,1.4),l(-.201,.537),l(-.055,.311),l(.144,.945),l(-1.203,.37),l(-.544,.283),l(-.355,.368),l(-.084,.466),l(.021,.367),l(-.401,.114),l(-.864,-.041),l(-.979,.172),l(-.654,.001),l(-.764,-.055),l(-.465,-.112),l(-.922,.072),l(-.736,.002),l(-.214,.607),l(-.295,.198),l(-.795,.312),l(-.481,.453),l(-.21,.354),l(-.747,-.267),l(-.35,.071),l(-.563,.255),l(-1.864,1.232),l(-.528,.396),l(-.725,.383),l(-.616,.438),l(-.532,.679),l(-.486,.241),l(-.567,.072),l(-.278,-.014),l(.006,.24),l(.336,.225),l(.12,.536),l(-.111,.537),l(-.099,.282),l(-.282,.156),l(-.084,.155),l(.384,.648),l(.036,1.186),l(.05,1.045),l(-.057,.311),l(-.11,.594),l(-.198,.621),l(-.464,.735),l(-.566,.495),l(-.788,.524),l(-.866,1.046),l(-.208,.396),l(-.476,1.145),l(-.499,.933),l(-.75,1.004),l(-.69,.623),l(-.801,.78),l(.321,-.808),l(.436,-.594),l(.629,-.566),l(.605,-1.158),l(-.06,-.254),l(-.356,.142),l(-.397,-.014),l(-.633,-.21),l(-.075,.155),l(.002,.297),l(-.401,1.215),l(-.349,.48),l(-.661,.438),l(-.317,.283),l(-.225,.424),l(-.013,.354),l(-.448,1.709),l(-.481,.763),l(-.787,.934),l(-1.076,.979),l(-.101,-.313),l(-.258,-.746),l(.103,-.269),l(.634,-.766),l(.017,-.269),l(-.333,-.379),l(-.163,-.027),l(-.131,-.197),l(-.853,-1.109),l(-.474,-.435),l(-.999,-.587),l(-.55,-.194),l(-.617,-.194),l(-.432,-.576),l(-.365,-.393),l(-.693,.427),l(-.27,.016),l(-.232,-.38),l(-.59,-.814),l(-.379,-.407),l(-.47,-.364),l(-.266,-.098),l(-.25,.101),l(-1.042,.246),l(-.55,.003),l(.214,-.27),l(.833,-.88),l(1.805,-1.816),l(1.435,-1.363),l(1.633,-1.448),l(.348,-.242),l(1.754,-.744),l(.521,-.313),l(.161,-.467),l(.094,-1.638),l(-.517,-1.154),l(-.206,-.196),l(-.462,.017),l(-.957,.161),M(288.966,203.943),l(-.558,-.125),l(-.301,-.536),l(-.078,-.382),l(.16,-.197),l(-.094,-.636),l(.048,-.381),l(.208,-1.018),l(.176,-.099),l(.479,-.058),l(.879,.097),l(1.007,.11),l(.479,-.199),l(.368,.028),l(.479,.168),l(.479,.083),l(.319,.155),l(-.335,.538),l(-.193,.946),l(-.257,.494),l(-.289,.312),l(-.561,.326),l(-.464,.171),l(-.527,.015),l(-.783,.016),l(-.641,.171),
+N(732.92,214.323),l(-.164,-.24),l(-.225,-.197),l(-.379,-.126),l(-.416,.198),l(-.399,-.38),l(-.287,-.184),l(-.659,-.238),l(-.243,-.239),l(.156,-.255),l(.29,.027),l(.731,-.058),l(.538,.126),l(.743,.083),l(.523,-.058),l(.258,-.185),l(.232,-.509),l(.056,.099),l(.351,.395),l(.286,.184),l(.241,.014),l(.961,-.2),l(.366,-.227),l(.361,-.722),l(.241,-.212),l(.653,-.029),l(.263,-.128),l(.04,-.282),l(-.064,-.565),l(-.043,-.536),l(.495,.196),l(.404,.056),l(.324,-.354),l(.318,.578),l(.077,.353),l(-.196,.495),l(-.099,.184),l(-.509,.044),l(-.159,.226),l(.061,.17),l(.437,.479),l(-.308,.354),l(-.264,.113),l(-.742,-.083),l(-.416,.001),l(-.091,.269),l(-.408,.495),l(-.275,.156),l(-.973,.426),l(-.484,.143),l(-.798,.029),l(-.807,.115),M(713.795,220.696),l(.031,-3.438),l(-.046,-.805),l(-.431,-1.368),l(.44,-.822),l(-.169,-7.966),l(2.581,.802),l(.85,.337),l(1.04,.295),l(1.254,.378),l(.848,.507),l(.613,.323),l(.597,.084),l(.38,-.058),l(.26,.508),l(.274,.254),l(.635,.352),l(.687,.395),l(.779,.718),l(-.3,.947),l(.033,.226),l(.305,.226),l(.665,.111),l(1.887,.787),l(.354,.027),l(.72,.183),l(.181,.254),l(.413,.535),l(.211,.579),l(-.166,.113),l(-1.114,.073),l(-.563,.156),l(-.098,.155),l(.047,.339),l(.589,.987),l(.5,.521),l(1.464,1),l(.218,.847),l(.725,1.044),l(.288,.141),l(.304,-.015),l(.712,-.086),l(.338,.013),l(.087,.198),l(-.354,.551),l(.218,.212),l(.401,.141),l(.423,.041),l(.63,.168),l(.096,.127),l(-.031,.142),l(-.648,.213),l(.287,.24),l(1.064,.28),l(.538,.295),l(.235,.763),l(-.064,.226),l(-1.094,-.012),l(-.215,-.154),l(-.146,-.466),l(-.126,-.099),l(-1.011,-.111),l(-1.063,-.266),l(-.644,-.126),l(-.752,.016),l(-.774,-.026),l(-.573,-.211),l(-.494,-.352),l(-.223,-.522),l(-.24,-.268),l(-.726,-.31),l(-.472,-.338),l(-.135,-.197),l(-1.089,-1.608),l(-.613,-.817),l(-.454,-.056),l(-1.524,-.336),l(-.671,-.31),l(-.55,-.055),l(-.415,.185),l(-.394,.071),l(-.78,-.45),l(.282,.762),l(-.027,.212),l(-.249,.071),l(-.382,-.126),l(-.311,.1),l(.296,.395),l(-.116,.113),l(-1.037,.045),l(-1.125,-.182),l(-.478,.029),l(.237,.127),l(.24,.154),l(.642,.169),l(.662,.352),l(.404,.338),l(.219,.395),l(-.411,.199),l(-.739,.425),l(-.458,.213),l(-.668,-.097),l(-1.801,-.039),l(-1.219,-.092),
+N(726.605,297.247),l(-.479,-.229),l(-1.179,-.471),l(-.543,-.371),l(-.508,-.715),l(-.477,-.558),l(-.216,-.414),l(.264,-.044),l(.169,.1),l(.421,.171),l(.129,-.143),l(-.209,-.387),l(-.703,-.699),l(-.617,-.713),l(-.149,-.257),l(-.375,-.956),l(-.008,-.67),l(.177,-.044),l(1.004,.312),l(1.476,.354),l(1.089,.369),l(.797,-.03),l(.463,-.144),l(.382,-.115),l(.373,-.058),l(.684,.027),l(.306,-.301),l(.716,-.244),l(.475,.385),l(-.009,.1),l(.128,.8),l(-.009,.686),l(-.751,1.503),l(-.124,.758),l(-.245,.315),l(-.943,.26),l(-.553,.388),l(-.49,.689),l(-.189,.272),l(-.274,.072),M(716.883,224.344),l(.682,.394),l(.233,.509),l(.047,.649),l(.118,.678),l(.256,.197),l(.42,.013),l(.102,.24),l(-.45,.919),l(.715,.338),l(.175,.282),l(.147,.593),l(.08,1.3),l(.144,.621),l(.456,1.157),l(.293,.268),l(.422,.014),l(.328,-.312),l(.384,-.185),l(.32,.663),l(.363,.127),l(.563,.408),l(.385,.154),l(.144,.155),l(.002,.41),l(.083,.96),l(.275,.776),l(-.046,1.06),l(.279,.24),l(.93,1.325),l(.276,.706),l(.055,.777),l(-.209,.876),l(.403,.649),l(.275,.833),l(.204,.353),l(1.395,.803),l(.285,.154),l(.744,.111),l(.402,.649),l(.843,.535),l(.483,.169),l(1.141,.068),l(.117,.197),L(731,245.42),l(-.273,.565),l(.309,.424),l(.879,.479),l(.22,.564),l(.469,1.342),l(.211,.748),l(.112,.396),l(.252,.311),l(.771,.352),l(-.021,-.127),l(-.141,-.777),l(.913,.507),l(.567,.154),l(.184,.127),l(.136,.296),l(.106,1.752),l(.884,.762),l(.573,.323),l(.604,.154),l(.287,.296),l(.244,.579),l(.229,.424),l(.545,.168),l(.176,.297),l(.007,.269),l(.19,.536),l(.106,.113),l(.506,.126),l(.156,.112),l(.162,.354),l(.366,1.327),l(.026,.876),l(.013,.862),l(-.132,.41),l(-.181,.325),l(.778,1.271),l(.223,.48),l(.181,.833),l(.034,.749),l(-.293,.708),l(-.353,1.668),l(-.585,1.755),l(.137,.791),l(-.065,.806),l(-.306,.778),l(-.661,.977),l(-.203,.665),l(-.598,.821),l(-.45,.185),l(-.465,.1),l(-.423,.468),l(-.466,.68),l(-.328,1.527),l(-.477,.638),l(-.34,.495),l(.014,.65),l(-.208,.539),l(-.331,.312),l(-.601,.341),l(-.27,.326),l(-.3,1.021),l(-.169,1.049),l(-.079,.937),l(.021,.666),l(-.283,.64),l(-.322,.354),l(-.463,.3),l(-.754,.101),l(-1.158,.087),l(-.82,.03),l(-.527,.157),l(-.516,.299),l(-1.529,.94),l(-.443,.129),l(-.214,.284),l(-.346,.071),l(-.528,.059),l(-.286,-.014),l(.295,.368),l(.209,.27),l(-1.315,-.665),l(-.885,-.353),l(.003,-.469),l(-.073,-.156),l(-.56,-.467),l(-.628,-.368),l(-.421,-.014),l(-.485,.172),l(-.335,.242),l(.748,.467),l(-.97,.243),l(-.929,.428),l(-.953,.442),l(-.222,.028),l(-.604,-.226),l(-.886,-.438),l(-.67,-.226),l(-1.086,-.311),l(-.51,-.041),l(-.239,.156),l(-.044,.113),l(-.716,-.169),l(-.751,-.353),l(-.522,-.298),l(-.896,-.82),l(-.526,-.34),l(-.422,-.879),l(.09,-1.035),l(-.082,-.411),l(-.184,-.495),l(-.664,-.736),l(-.141,-.523),l(-.029,-.425),l(-.534,-.014),l(-.786,.398),l(-.597,.114),l(-.34,.058),l(-.178,-.07),l(-.167,-.17),l(.517,-.454),l(.233,-.567),l(.073,-.821),l(-.253,-.324),l(-.536,-.593),l(-.247,-.353),l(-.485,.735),l(-.443,1.431),l(-.19,.113),l(-.796,.002),l(-.199,.156),l(-.196,.015),l(-.255,.028),l(.198,-.396),l(.081,-.396),l(.079,-.1),l(.634,.041),l(.242,-.142),l(.126,-.255),l(-.105,-1.004),l(.454,-.835),l(.328,-.453),l(.091,-.396),l(.018,-.409),l(.151,-.128),l(.245,-.015),l(.218,-.354),l(-.052,-.227),l(-.323,-.494),l(-.338,-.494),l(-.107,.707),l(-.288,.255),l(-.518,.299),l(-.311,.467),l(-.086,.155),l(-.189,.467),l(-.281,.326),l(-.747,.242),l(-.735,.481),l(-.653,.567),l(-.36,.693),l(-.514,.808),l(-.41,-.339),l(-.38,-1.328),l(-.263,-.579),l(-.19,-.325),l(-.688,-.79),l(-.297,-.734),l(-.176,-.212),l(-.704,.072),l(-.235,-.099),l(-.139,-.24),l(-.085,-.269),l(.334,-.34),l(-.047,-.297),l(-.346,-.395),l(-.543,-.494),l(-.266,-.098),l(-.83,.157),l(-.486,-.07),l(-.95,-.549),l(-.274,-.014),l(-.438,.17),l(-.433,-.027),l(-.421,-.183),l(-.662,-.521),l(-.921,-.437),l(-.218,.001),l(-.723,.213),l(-1.282,.088),l(-.669,.001),l(-1.764,.061),l(-.611,.129),l(-.656,.213),l(-.989,.44),l(-.972,.256),l(-1.039,.257),l(-1.503,.088),l(-.794,-.013),l(-.383,.044),l(-.927,.284),l(-.993,.469),l(-.773,.397),l(-.538,.143),l(-.431,.085),l(-.361,.199),l(-.615,.693),l(-.774,1.02),l(-.588,.284),l(-.766,-.013),l(-.547,-.013),l(-.927,.143),l(-.4,.185),l(-.663,-.395),l(-.294,-.084),l(-.734,.016),l(-1.572,.173),l(-.938,.157),l(-.459,-.041),l(-.672,.044),l(-.398,.227),l(-.583,.793),l(-.344,.128),l(-.958,-.125),l(-.158,.057),l(-.57,.708),l(-.465,.368),l(-.919,.271),l(-.586,.086),l(-1.516,-.082),l(-.638,-.055),l(-.688,-.197),l(-.633,-.366),l(-.778,-.677),l(-.74,-.353),l(-.374,-.041),l(-.151,-.07),l(-.19,-1.229),l(.055,-.255),l(.489,.112),l(.45,-.086),l(.332,-.425),l(.197,-.467),l(.267,-1.357),l(-.043,-1.215),l(-.156,-.622),l(-.258,-.593),l(-1.117,-1.906),l(-.208,-.635),l(-.144,-.834),l(.027,-.989),l(-.16,-.692),l(-.467,-1.072),l(-.663,-.945),l(-.603,-.734),l(-.214,-.254),l(.128,-.904),l(-.215,-.536),l(-.733,-1.115),l(-.972,-1.018),l(-.273,-.583),l(.126,-.233),l(.188,.187),l(.152,.443),l(.183,.163),l(.235,.35),l(.327,.188),l(.354,.023),l(-.348,-1.144),l(-.437,-.396),l(-.226,-.326),l(.08,-.304),l(.748,.84),l(.495,.979),l(.477,.065),l(-.099,-.555),l(.289,-.039),l(.004,-.564),l(-.282,-.48),l(-1.03,-1.368),l(-.354,-.691),l(-.119,-.579),l(-.038,-.734),l(.355,-.595),l(.323,-.523),l(.21,-.664),l(-.083,-1.031),l(-.254,-.635),l(.033,-.368),l(.438,-.692),l(.109,-.325),l(.064,-.156),l(.271,.649),l(.011,.424),l(.105,.184),l(.35,.027),l(.171,-.113),l(.187,-.565),l(.141,-.48),l(.765,-.468),l(1.22,-.624),l(.484,-.326),l(.676,-.581),l(.585,-.467),l(.632,-.327),l(.79,-.114),l(.697,-.016),l(.7,-.002),l(.431,-.043),l(.352,-.185),l(.474,-.453),l(.494,-.128),l(.929,-.072),l(.279,-.143),l(.291,-.551),l(.158,-.1),l(.444,.027),l(.877,.224),l(.626,-.043),l(.911,-.299),l(1.084,-.469),l(.359,-.213),l(.716,-.665),l(.427,-.58),l(.29,-.622),l(.132,-.297),l(.41,-.369),l(.968,-.651),l(.079,-.17),l(-.067,-.409),l(-.242,-.805),l(-.016,-.495),l(1.063,-1.118),l(.387,-.692),l(.291,.169),l(.341,.437),l(.619,1.355),l(.262,.253),l(.177,-.579),l(.021,-.466),l(.436,.238),l(.272,.07),l(.189,-.607),l(-.06,-.142),l(-.563,-.238),l(-.175,-.24),l(.007,-.565),l(.044,-.112),l(.897,.04),l(.661,.253),l(.642,-.029),l(.334,-.029),l(.289,.074),l(-.699,-.455),l(-.431,-.141),l(.128,-.537),l(-.07,-.296),l(.135,-.509),l(.422,-.354),l(.165,-.07),l(.732,.394),l(.202,-.043),l(-.112,-.452),l(.11,-.48),l(.146,-.367),l(-.041,-.522),l(.358,-.171),l(.4,-.113),l(.813,.04),l(.529,-1.088),l(.371,-.298),l(.35,.169),l(.268,.451),l(.265,-.552),l(.222,-.227),l(.197,-.48),l(.695,.62),l(.513,.084),l(.293,.211),l(.331,.536),l(.632,.592),l(.122,.706),l(-.072,.594),l(.181,.197),l(.256,-.283),l(.462,-.679),l(.155,-.128),l(1.16,.082),l(.479,.155),l(.637,.492),l(.332,.141),l(.156,-.48),l(.302,-.297),l(.022,-.24),l(-.266,-.324),l(-.601,-.395),l(-.079,-.184),l(.008,-.24),l(.145,-.099),l(.604,-.538),l(.007,-.452),l(.191,-.213),l(.518,-.283),l(.268,-.241),l(.151,-.269),l(-.094,-.184),l(.003,-.296),l(.512,-.863),l(.121,-.057),l(.317,-.029),l(.397,-.029),l(.248,-.17),l(-.205,-.409),l(.377,-.396),l(.344,-.071),l(.793,.366),l(.616,-.072),l(1.291,-.088),l(.512,-.128),l(.232,-.17),l(.04,-.057),l(-.077,-.197),l(-.2,-.734),l(-.248,-.282),l(-.471,-.268),l(-.374,.086),l(-.244,-.141),l(.035,-.212),l(.367,-.143),l(.396,-.043),l(.617,.521),l(.255,.099),l(.34,-.1),l(.364,.31),l(.676,.465),l(.36,.154),l(1.297,.294),l(.591,.084),l(.413,-.143),l(.372,.014),l(.396,.606),l(.419,.112),l(.141,-.029),l(.562,-.438),l(.454,.027),l(.652,.38),l(.331,.479),l(.46,-.143),l(.122,-.791),l(.181,-.085),l(.455,.465),l(.337,.099),l(.152,.154),l(-.436,.411),l(-.126,.424),l(-.176,.212),l(-.011,.438),l(-.12,.255),l(-.513,.015),l(-.51,.228),l(-.396,.34),l(-.004,.551),l(.301,.353),l(-.187,.692),l(-.381,.58),l(-.559,.481),l(-.155,.48),l(.063,.17),l(.469,.437),l(1.038,.619),l(.81,.677),l(.508,.606),l(.246,.099),l(.349,-.114),l(.264,.027),l(.782,.352),l(.742,.465),l(.434,.451),l(.679,.535),l(.335,.127),l(.442,.027),l(.784,.182),l(.25,.184),l(.218,.621),l(.167,.169),l(.507,.31),l(.76,.423),l(.537,.154),l(.422,-.072),l(.414,-.17),l(.603,-.199),l(.208,-.156),l(.736,-1.344),l(.403,-1.131),l(.194,-1.314),l(.259,-.721),l(.43,-.679),l(-.131,-.424),l(-.409,-.621),l(.022,-.311),l(.236,-.637),l(.187,-.48),l(-.164,-.282),l(-.183,-.395),l(.088,-.692),l(.104,-1.004),l(.225,-.297),l(.329,-.156),l(.158,-.311),l(-.534,-.521),l(.033,-.198),l(.576,-.947),l(.312,-.934),l(.072,-.833),l(.191,-.241),l(.279,-.297),
+N(270.934,276.123),l(.054,-.338),l(-.194,-.577),l(.011,-.522),l(.24,-.495),l(.48,-.524),l(.118,-.325),l(-.168,-.986),l(-.049,-.847),l(.103,-.522),l(.595,-2.359),l(.065,-.452),l(.443,-.524),l(.55,-.003),l(1.042,-.246),l(.25,-.101),l(.266,.098),l(.47,.364),l(.379,.407),l(.59,.814),l(.232,.38),l(.27,-.016),l(.693,-.427),l(.365,.393),l(.432,.576),l(.617,.194),l(.55,.194),l(.999,.587),l(.474,.435),l(.853,1.109),l(.131,.197),l(.163,.027),l(.333,.379),l(-.017,.269),l(-.634,.766),l(-.103,.269),l(.258,.746),l(.101,.313),l(-.059,.053),l(-.803,1.103),l(-.625,.552),l(-.775,.454),l(-.441,.156),l(-.818,.1),l(-.874,-.337),l(-.551,.044),l(-.551,.114),l(-.64,.283),l(-.38,-.042),l(-.846,-.676),l(-.637,-.252),l(-.76,-.154),l(-.469,.128),l(-.507,.072),l(-.327,-.127),l(-.548,-.563),l(-.354,-.159),
+N(418.763,73.869),l(.391,-.499),l(.389,-.262),l(.283,.081),l(.663,-.062),l(.762,-.075),l(.324,-.137),l(.891,-.787),l(.979,-.2),l(.224,.025),l(.198,-.219),l(.175,.032),l(-.349,.531),l(.11,.106),l(.026,.481),l(-.288,.156),l(-.17,.368),l(-1.675,.069),l(-.376,-.106),l(-.428,.031),l(-.14,-.062),l(-.239,.081),l(-.275,.031),l(-.153,.417),l(-.249,.218),l(-.353,.118),l(-.08,.112),l(-.229,-.155),l(-.411,-.292),M(421.68,78.002),l(-1.839,.129),l(-.116,-.016),l(.064,-.404),l(-.19,-.638),l(-.174,.015),l(-.36,-.293),l(-.233,.184),l(-.398,-.172),l(.2,-.355),l(.434,-.293),l(-.007,-.181),l(-.445,-.541),l(.018,-.256),l(.131,-.498),l(.279,-.106),l(.318,.15),l(.495,.12),l(.43,-.574),l(.299,-.091),l(.296,.437),l(.391,-.046),l(.022,-.528),l(.107,-.363),l(1.032,-.017),l(.848,.089),l(.03,.544),l(.187,.301),l(1.138,.058),l(.14,.166),l(-.307,.604),l(-.565,.142),l(-.119,-.261),l(-.331,.031),l(-.263,.136),l(.121,.278),l(-.184,.414),l(-.312,.077),l(-.069,.244),l(-.635,.035),l(.157,.229),l(-.291,.094),l(-.191,.24),l(-.061,.285),l(.003,.21),l(-.215,.338),l(.166,.084),M(427.177,77.967),l(-.139,-.149),l(-.312,-.344),l(-.405,.031),l(-.665,-.059),l(-.123,-.329),l(-.285,-.66),l(.433,-.091),l(.603,-.467),l(.356,.495),l(.521,.179),l(.2,-.271),l(.078,-.586),l(.333,-.046),l(.459,.044),l(.056,.571),l(-.134,.36),l(-.144,.09),l(-.413,.286),l(.603,.299),l(-.285,.166),l(-.499,.39),l(-.238,.09),
+N(417.259,94.301),l(-.135,-.233),l(.216,-.701),l(-.091,-.076),l(.075,-.295),l(.321,-.372),l(.054,-.358),l(.054,-.213),l(.954,-.806),l(-.605,-.193),l(-.479,.014),l(-.456,-.09),l(-.095,-.186),l(-.233,-.007),l(-.072,.048),l(-.689,.062),l(-.045,-.145),l(-.395,.055),l(-.337,-.277),l(-.428,-.319),l(-.074,-.353),l(.248,-.144),l(.044,-.288),l(-.466,-.096),l(-.41,-.312),l(-.062,-.301),l(.422,-.376),l(-.038,-.468),l(-.282,-.365),l(-.344,-.177),l(.165,-.278),l(-.143,-.217),l(-.182,-.037),l(.08,-.097),l(.235,-.133),l(.236,-.133),l(-.193,-.121),l(.201,-.275),l(.095,-.148),l(-.152,-.292),l(-.082,-.265),l(-.376,-.182),l(.07,-.182),l(.341,-.078),l(.175,-.092),l(.378,.134),l(.664,-.151),l(.32,-.152),l(.128,-.376),l(.412,-.207),l(.003,-.328),l(-.16,-.17),l(-.188,.061),l(-.44,-.134),l(.029,-.184),l(.113,-.181),l(.571,.036),l(.084,-.401),l(.345,-.361),l(-.151,-.438),l(.066,-.147),l(-.465,-.192),l(.126,-.43),l(.285,-.181),l(.371,-.028),l(1.12,-.062),l(.293,-.012),l(.095,.16),l(.195,.271),l(-.155,.158),l(.324,.113),l(.148,-.068),l(.042,-.192),l(-.159,-.204),l(.259,.012),l(.437,.169),l(-.163,-.362),l(.247,-.419),l(.687,.113),l(.614,-.091),l(-.596,-.17),l(-.084,-.227),l(.282,-.102),l(-.252,-.114),l(-.12,-.147),l(.166,-.08),l(.068,-.136),l(-.675,.08),l(-.06,-.182),l(.664,-.125),l(.218,-.193),l(-.464,-.42),l(-.324,-.341),l(.045,-.28),l(.116,.016),l(1.839,-.129),l(.254,.13),l(.565,.341),l(.271,.297),l(-.02,.228),l(-.252,.175),l(.445,-.05),l(.171,.216),l(.157,-.078),l(.283,.021),l(.235,.156),l(.45,.073),l(.866,-.121),l(.077,.208),l(-.07,.149),l(-.057,.125),l(-.665,.295),l(.316,.193),l(.649,-.136),l(.113,.182),l(.457,-.006),l(.349,-.391),l(1.059,-.136),l(.602,-.234),l(.757,-.266),l(.249,.113),l(.396,-.17),l(.184,.182),l(.085,.159),l(.634,.29),l(.182,.074),l(.526,-.12),l(.156,.12),l(.059,.34),l(.036,.227),l(.154,.102),l(.354,.125),l(.372,-.022),l(.028,.13),l(.201,.025),l(.319,.835),l(-.219,.52),l(-.391,.147),l(-.051,.328),l(.391,.102),l(.683,.429),l(-.217,.395),l(.094,.18),l(.312,.146),l(-.021,.304),l(.131,.102),l(-.118,.292),l(-.214,.124),l(-.007,.191),l(.325,.27),l(-.114,.258),l(.468,.188),l(.281,.485),l(-.435,.698),l(-.142,.115),l(-.344,-.072),l(-.031,-.278),l(-.308,-.121),l(-.436,.072),l(.282,.218),l(-.254,.084),l(-.284,.097),l(-.524,.048),l(-.124,.169),l(-.735,.024),l(-.112,.217),l(-.176,-.072),l(-.358,.061),l(-.097,.229),l(-.382,.012),l(-.078,.181),l(-.442,0),l(-.422,.201),l(-.293,-.033),l(-.26,.181),l(-.243,.168),l(.037,.056),l(.258,.378),l(.413,.098),l(-.155,.25),l(-.08,.406),l(.159,.085),l(.036,.233),l(.244,.173),l(.478,.265),l(.188,-.072),l(.573,.515),l(.431,.17),l(.184,.201),l(.219,-.084),l(.165,.18),l(.207,.036),l(.447,.633),l(-.682,.26),l(-.437,-.151),l(-.132,.027),l(-.039,.337),l(-.162,.172),l(-.968,.295),l(-.364,.227),l(.538,.571),l(-.197,.295),l(.334,.014),l(.056,.158),l(-.098,.343),l(-.082,.055),l(-.441,-.178),l(.049,-.165),l(-.146,-.117),l(-.564,.062),l(-.195,-.138),l(-.433,.035),l(-.092,.178),l(-1.25,.035),l(-.125,.171),l(-.319,.014),l(-.044,.13),L(425.4,94.7),l(-.594,.021),l(-.059,-.144),l(-.093,-.145),l(-.493,-.089),l(-.262,.055),l(-.237,-.041),l(.032,.274),l(-.452,.343),l(-.215,0),l(.078,-.195),l(-.26,-.039),l(.011,-.165),l(-.194,-.048),l(-.104,-.137),l(-.249,-.007),l(-.096,-.11),l(-.164,.137),l(-.368,-.014),l(-.664,-.261),l(-.852,-.007),l(-.149,-.089),l(-.199,.014),l(-.017,-.151),l(-.709,.199),l(.179,.199),l(-.354,.021),l(-.278,-.11),l(-.567,.158),l(-.271,-.096),l(-.272,.117),l(-.271,-.089),M(432.012,80.473),l(-.171,-.038),l(-.088,-.049),l(-.447,.08),l(-.111,-.264),l(.002,-.213),l(.647,.281),l(.168,.203),
+N(450.734,91.05),l(-.831,-.33),l(-.424,-.101),l(-.104,-.216),l(-.703,-.186),l(-.129,-.13),l(-.561,.076),l(-.508,-.041),l(-.073,.137),l(-.373,.18),l(-.46,-.254),l(-.483,.046),l(-.168,-.056),l(-.558,.204),l(-.104,.177),l(-.149,.135),l(-.18,-.12),l(-.389,.083),l(-.003,-.204),l(.026,-.083),l(-.066,-.18),l(-.331,.025),l(-.073,-.169),l(-.194,-.24),l(-.261,.024),l(-.062,.112),l(-.193,-.016),l(-.183,.24),l(-.43,.048),l(-.03,-.228),l(-.285,-.041),l(-.105,-.331),l(-.382,-.096),l(-.188,-.208),l(.02,-.249),l(-.802,-.132),l(-.311,-.181),l(-.368,.161),l(-.293,-.013),l(-.013,-.221),l(.001,-.253),l(-.329,.084),l(.181,-.283),l(-.709,.005),l(-.258,-.121),l(-.509,-.193),l(-.286,-.024),l(-.003,.145),l(.282,.302),l(-.41,.12),l(-.22,.187),l(-.318,-.072),l(-.296,-.38),l(-.55,-.247),l(.224,-.211),l(.208,-.024),l(.099,-.121),l(-.199,-.181),l(-.257,-.024),l(-.061,.066),l(-.466,.066),l(.005,-.169),l(-.278,.037),l(-.132,-.181),l(-.568,-.054),l(-.201,-.025),l(-.363,-.163),l(-.093,-.26),l(-.211,-.091),l(-.361,.018),l(-.063,.084),l(.062,.073),l(-.024,.151),l(-.375,0),l(.435,-.698),l(-.281,-.485),l(-.468,-.188),l(.114,-.258),l(-.325,-.27),l(.007,-.191),l(.214,-.124),l(.118,-.292),l(-.131,-.102),l(.021,-.304),l(-.312,-.146),l(-.094,-.18),l(.217,-.395),l(-.683,-.429),l(-.391,-.102),l(.051,-.328),l(.391,-.147),l(.219,-.52),l(-.319,-.835),l(.34,.043),l(.23,.125),l(-.093,-.193),l(.156,-.153),l(-.019,-.147),l(-.166,-.13),l(-.021,-.129),l(-.012,-.075),l(.93,-.154),l(.431,-.051),l(.516,-.152),l(1.304,-.128),l(.48,-.158),l(.797,-.479),l(1.117,-.298),l(1.515,-.303),l(1.382,-.086),l(1.067,.417),l(-.882,-.294),l(-.054,.147),l(.256,.172),l(.132,.466),l(.355,.135),l(.648,.061),l(.645,-.11),l(1.063,-.409),l(.054,.004),l(-.978,.417),l(-.19,.11),l(-.004,.123),l(.218,.012),l(.286,-.123),l(.613,-.208),l(.543,-.253),l(.842,.006),l(2.246,.115),l(2.96,-.101),l(.641,-.021),l(.04,.014),l(.679,.243),l(.284,.487),l(.099,.186),l(.016,.029),l(.09,.171),l(.16,.302),l(.615,1.5),l(.172,.502),l(-.023,.236),l(-.263,.208),l(-.44,.03),l(-.571,.244),l(-.291,.328),l(-.128,.292),l(.289,.012),l(.421,.146),l(.149,.176),l(.218,.146),l(.013,.25),l(-.315,.752),l(.126,.137),l(.161,.175),l(-.074,.242),l(.816,.835),l(.316,.193),l(-.298,.048),l(-.085,.133),l(.328,.302),l(-.009,.314),l(-.255,.24),l(-.429,.013),l(-.286,.181),l(-.599,.398),l(-1.509,1.218),l(-.082,.306),l(.044,.552),l(.349,.383),l(-.653,-.018),M(431.844,80.27),l(.232,.024),l(.069,.208),l(-.134,-.029),l(-.168,-.203),M(432.739,80.361),l(-.03,.129),l(-.278,-.024),l(-.191,.03),l(-.046,-.208),l(.174,-.012),l(.266,0),l(.106,.085),
+N(416.488,81.945),l(.151,.438),l(-.345,.361),l(-.084,.401),l(-.571,-.036),l(-.113,.181),l(-.029,.184),l(.44,.134),l(.188,-.061),l(.16,.17),l(-.003,.328),l(-.412,.207),l(-.128,.376),l(-.32,.152),l(-.664,.151),l(-.378,-.134),l(-.175,.092),l(-.341,.078),l(-.07,.182),l(.376,.182),l(.082,.265),l(.152,.292),l(-.095,.148),l(-.201,.275),l(.193,.121),l(-.236,.133),l(-.235,.133),l(-.08,.097),l(.182,.037),l(.143,.217),l(-.165,.278),l(-.672,.024),l(-.001,-.338),l(.172,-.097),l(.11,-.423),l(-.754,-.375),l(-.535,.109),l(-.292,-.375),l(-.228,-.061),l(-.173,.121),l(-.313,-.242),l(-.267,.182),l(-.307,0),l(-.307,-.012),l(.025,.157),l(-.52,.034),l(-.407,-.042),l(-.477,-.098),l(-.07,-.162),l(-.496,-.264),l(.795,-.472),l(.858,-.621),l(.273,-.354),l(.563,-1.167),l(.173,-.104),l(.644,-.12),l(.367,.251),l(.136,.268),l(-.28,.203),l(-.228,-.056),l(.066,.496),l(-.191,.124),l(1.163,.225),l(.225,-.236),l(.406,-.113),l(.134,-.417),l(-.458,-.022),l(-.123,-.124),l(-.02,-.203),l(.236,-.158),l(-.171,-.023),l(-.558,-.067),l(.072,-.328),l(-.034,-.13),l(-.023,-.034),l(.128,-.283),l(.572,-.198),l(.709,-.238),l(.654,.03),l(1.089,-.151),l(.245,.275),l(.329,.045),l(.076,.181),l(.259,0),
+N(431.57,91.965),l(-.447,-.633),l(-.207,-.036),l(-.165,-.18),l(-.219,.084),L(430.349,91),l(-.431,-.17),l(-.573,-.515),l(-.188,.072),l(-.478,-.265),l(-.244,-.173),l(-.036,-.233),l(-.159,-.085),l(.08,-.406),l(.155,-.25),l(-.413,-.098),l(-.258,-.378),l(-.037,-.056),l(.243,-.168),l(.26,-.181),l(.293,.033),l(.422,-.201),l(.442,0),l(.078,-.181),l(.382,-.012),l(.097,-.229),l(.358,-.061),l(.176,.072),l(.112,-.217),l(.735,-.024),l(.124,-.169),l(.524,-.048),l(.284,-.097),l(.254,-.084),l(-.282,-.218),l(.436,-.072),l(.308,.121),l(.031,.278),l(.344,.072),l(.142,-.115),l(.375,0),l(.024,-.151),l(-.062,-.073),l(.063,-.084),l(.361,-.018),l(.211,.091),l(.093,.26),l(.363,.163),l(.201,.025),l(.568,.054),l(.132,.181),l(.278,-.037),l(-.005,.169),l(.466,-.066),l(.061,-.066),l(.257,.024),l(.199,.181),l(-.099,.121),l(-.208,.024),l(-.224,.211),l(.55,.247),l(.296,.38),l(.318,.072),l(.22,-.187),l(.41,-.12),l(-.282,-.302),l(.003,-.145),l(.286,.024),l(.509,.193),l(.258,.121),l(.709,-.005),l(-.181,.283),l(.329,-.084),l(-.001,.253),l(.013,.221),l(.293,.013),l(.368,-.161),l(.311,.181),l(.802,.132),l(-.02,.249),l(.188,.208),l(.382,.096),l(.105,.331),l(-.071,-.012),l(-.065,.066),l(-.066,.04),l(-.105,-.026),L(442,89.998),l(-.119,.013),l(-.065,.04),l(-.132,.066),l(-.053,.079),l(-.118,.092),l(-.119,.053),l(-.132,.079),l(-.105,.013),l(-.118,.013),l(-.053,.092),l(-.026,.092),l(-.026,.132),l(-.053,.079),l(-.053,.105),l(-.145,.066),l(-.105,.04),l(-.092,-.026),l(-.079,.079),l(-.04,.092),l(-.053,.079),l(-.039,0),l(-.105,.026),l(-.066,.026),l(-.079,.053),l(-.118,.053),l(-.132,0),l(-.145,.04),l(-.093,.026),l(-.065,-.026),l(-.065,-.026),l(-.093,0),l(-.065,.026),l(-.158,0),l(-.132,-.053),l(-.092,-.053),l(-.093,.026),l(-.171,.053),l(-.026,.04),l(-.146,.119),l(-.092,.066),l(-.014,.131),l(-.065,.105),l(-.039,.044),l(-.055,-.095),l(-.545,-.2),l(-1.332,.181),l(-1.019,-.227),l(-1.374,-.341),l(-.746,.773),l(-.598,.163),l(-.483,-.099),l(-.354,-.129),l(-.145,-.014),
+N(421.683,94.397),l(.368,.014),l(.164,-.137),l(.096,.11),l(.249,.007),l(.104,.137),l(.194,.048),l(-.011,.165),l(.26,.039),l(-.078,.195),l(.215,0),l(.452,-.343),l(-.032,-.274),l(.237,.041),l(.262,-.055),l(.493,.089),l(.093,.145),l(.059,.144),L(425.4,94.7),l(.377,-.103),l(.044,-.13),l(.319,-.014),l(.125,-.171),l(1.25,-.035),l(.092,-.178),l(.433,-.035),l(.195,.138),l(.564,-.062),l(.146,.117),l(-.049,.165),l(.441,.178),l(.082,-.055),l(.098,-.343),l(-.056,-.158),l(-.334,-.014),l(.197,-.295),l(-.538,-.571),l(.364,-.227),l(.968,-.295),l(.162,-.172),l(.039,-.337),l(.132,-.027),l(.437,.151),l(.682,-.26),l(.145,.014),l(.354,.129),l(.483,.099),l(.598,-.163),l(.746,-.773),l(1.374,.341),l(1.019,.227),l(1.332,-.181),l(.545,.2),l(.055,.095),l(.053,.093),l(-.097,.581),l(.155,.26),l(.761,.547),l(-.636,.291),l(-.019,.367),l(-.701,.054),l(-.164,-.134),l(-.325,0),l(-.223,.197),l(.452,.063),l(.136,.196),l(-.157,.206),l(-.409,.062),l(-.04,.375),l(.204,.188),l(-.154,.294),l(-.346,.299),l(-.693,.157),l(.082,.393),l(-.89,-.116),l(-.298,.196),l(-.85,-.08),l(-.566,.107),l(-.646,.5),l(-.287,-.152),l(-.612,.009),l(-.179,-.107),l(-1.04,-.065),l(-.478,-.112),l(-.894,-.082),l(-1.311,-.247),l(-.32,-.391),l(-.318,-.096),l(-.023,-.363),l(-.856,.227),l(-.301,-.103),l(-.445,.11),l(-.183,-.083),l(-.366,.096),l(-.329,.411),l(-.597,-.034),l(-.631,-.171),l(.029,-.171),l(-.215,-.117),l(-.579,.332),l(-.55,-.242),l(-.008,-.144),l(-.622,-.062),l(.09,-.192),l(-.214,-.288),l(.266,-.274),l(-.193,-.336),
+N(431.763,171.063),l(-.067,.354),l(.091,.72),l(.108,.508),l(.225,.168),l(.562,.11),l(.144,.183),l(.077,.353),l(-.089,1.116),l(-.146,.227),l(-.274,.171),l(-.885,.217),l(-.291,.256),l(-.664,1.275),l(-.503,1.203),l(-.243,1.004),l(-.354,.129),l(-.369,.03),l(-.129,.354),l(-.146,1.229),l(-.192,.312),l(-.385,.045),l(-.257,.284),l(-.417,.836),l(-.944,2.223),l(-.304,.624),l(-.352,.496),l(-.368,.355),l(-.239,.114),l(-.145,-.056),l(-.722,-.97),l(-.145,-.14),l(-1.104,-.05),l(-.272,.03),l(-1.31,1.265),l(-.941,.839),l(-.495,.526),l(.02,.974),l(-.189,.552),l(-.376,.686),l(-.188,-.119),l(-.224,-.042),l(-.176,-.127),l(-.145,.212),l(.144,.296),l(-.063,.127),l(-.353,.198),l(-.56,.03),l(-.977,.101),l(-.607,-.267),l(-.288,.043),l(-.271,.368),l(-.177,.113),l(-.432,-.07),l(-1.247,-.011),l(-.528,-.225),l(-.543,-.451),l(-.416,-.72),l(-.192,-.649),l(.048,-.254),l(.208,-.254),l(-.144,-.296),l(-.513,-.069),l(-.128,-.254),l(-.464,-.55),l(-.561,-.465),l(-.608,-.253),l(-.641,-.253),l(-.272,-.31),l(-.513,.072),l(-.24,.297),l(-.336,.071),l(-.881,.044),l(-.659,.03),l(-.006,-.234),l(.121,-1.188),l(-.217,-1.439),l(.09,-.989),l(.012,-.876),l(-.003,-.48),l(-.002,-.381),l(.237,-.397),l(.257,-.03),l(.433,-.13),l(.078,-.368),l(-.116,-.592),l(.095,-.34),l(.656,-.414),l(.223,-.298),l(.19,-.566),l(.316,-.962),l(-.388,-.718),l(-.164,-.762),l(.062,-.777),l(.092,-.961),l(.158,-.849),l(.353,-.482),l(.687,-1.614),l(.676,-.4),l(1.064,-.188),l(1.174,-.108),l(1.111,.121),l(.821,.277),l(1.095,1.223),l(.209,.098),l(.37,-.002),l(1.337,-.544),l(.467,-.116),l(.354,.083),l(1.173,.742),l(.965,.277),l(.934,-.005),l(.273,-.059),l(.629,-.399),l(.403,-.327),l(.774,-.287),l(.628,-.103),l(.709,-.047),l(.531,.04),l(.884,.221),l(.724,.193),l(.609,.208),l(.259,-.058),l(.761,-.697),l(.453,-.299),l(.437,-.2),l(.951,-.034),l(.173,.394),l(.575,.52),l(.351,.407),
+N(425.506,195.522),l(-1.965,-.017),l(-1.005,.007),l(.029,-.176),l(-.208,-.536),l(.271,-.989),l(-.159,-.72),l(-.191,-.522),l(-.399,-.649),l(.433,-.396),l(-.287,-.367),l(-.24,-.056),l(-.576,.058),l(-.559,-.268),l(-.176,-.324),l(-.063,-.537),l(-.111,-.211),l(-.527,-.14),l(-.101,-.064),l(.376,-.686),l(.189,-.552),l(-.02,-.974),l(.495,-.526),l(.941,-.839),l(1.31,-1.265),l(.272,-.03),l(1.104,.05),l(.145,.14),l(.722,.97),l(.145,.056),l(.239,-.114),l(.368,-.355),l(.352,-.496),l(.304,-.624),l(.944,-2.223),l(.417,-.836),l(.257,-.284),l(.385,-.045),l(.192,-.312),l(.146,-1.229),l(.129,-.354),l(.369,-.03),l(.354,-.129),l(.243,-1.004),l(.503,-1.203),l(.664,-1.275),l(.291,-.256),l(.885,-.217),l(.274,-.171),l(.146,-.227),l(.089,-1.116),l(-.077,-.353),l(-.144,-.183),l(-.562,-.11),l(-.225,-.168),l(-.108,-.508),l(-.091,-.72),l(.067,-.354),l(.627,.109),l(.208,.083),l(.923,1.238),l(.395,.887),l(.119,1.2),l(-.134,.651),l(.169,1.228),l(.205,.578),l(.381,.633),l(.208,.21),l(.047,.127),l(-.225,.143),l(-.674,.032),l(-1.187,-.191),l(-.481,-.082),l(-.24,.1),l(-.29,.312),l(-.323,.539),l(1.088,1.067),l(1.343,1.179),l(.638,1.07),l(.382,1.057),l(.208,.183),l(-.208,.114),l(-.354,.327),l(-.866,1.743),l(-.4,.511),l(-.528,.399),l(-.144,.198),l(-.064,.424),l(.112,.776),l(.273,1.185),l(.207,.592),l(.4,.661),l(.432,.605),l(.064,.791),l(.112,.324),l(1.217,1.236),l(.498,.802),l(.194,.804),l(.209,.451),l(-.159,.185),l(.1,.861),l(-.271,.086),l(-.863,-.292),l(-.864,-.334),l(-.576,-.194),l(-.56,-.124),l(-.479,-.04),l(-.271,-.055),l(-.878,.076),l(-.863,-.023),l(-.56,-.109),l(-1.022,-.149),l(-.831,-.038),l(-1.773,-.088),l(-.88,-.093),l(-.045,.495),
+N(551.793,147.278),l(-.788,-.111),l(-.458,-.253),l(-.379,-.367),l(-.248,-1.101),l(-.112,-.141),l(-.342,-.141),l(-.662,-.083),l(-.105,-.07),l(-.014,-.268),l(.116,-.466),l(-.178,-.451),l(-.418,-.38),l(-.294,-.013),l(-1.181,.412),l(-1.229,.087),l(-.81,.143),l(-.947,.073),l(-.415,-.083),l(-.263,-.226),l(-.135,-.197),l(-.909,.143),l(-.548,.382),l(-.856,.016),l(-1.264,-.011),l(-.691,.128),l(-.723,.115),l(-.541,.03),l(.182,-.954),l(.049,-.354),l(.31,-.792),l(.285,-.383),l(.794,-.47),l(.697,-.258),l(1,-.077),l(.209,-.072),l(.188,-.255),l(.036,-.974),l(-.186,-.281),l(-.652,-.053),l(-.185,-.394),l(-.111,-1.34),l(-.168,-.38),l(-.409,-.224),l(-1.144,-.388),l(-.695,-.391),l(-.353,-.464),l(-.71,-1.068),l(-.802,-1.083),l(.887,.32),l(.63,.194),l(1.069,.248),l(.854,.235),l(.541,.039),l(.742,.024),l(1.187,-.218),l(.349,-.002),l(1.005,.135),l(1.234,-.12),l(.964,-.175),l(1.183,-.19),l(1.066,-.274),l(.247,-.156),l(.069,-.184),l(.099,-.903),l(.232,-.876),l(.188,-.34),l(.412,-.369),l(.597,-.441),l(.283,-.072),l(.644,.039),l(.516,.082),l(.254,-.086),l(.321,-.693),l(.362,-.271),l(.559,-.243),l(.562,.025),l(1.001,.375),l(.237,-.086),l(.34,-.383),l(.021,-.084),l(.139,-1.214),l(.288,-.834),l(.224,-.424),l(.292,-.228),l(.416,-.115),l(.513,-.031),l(.315,-.115),l(.199,-.255),l(-.088,-.437),l(-.129,-.281),L(556.193,125),l(-.35,-.336),l(.355,-.129),l(.997,.122),l(.698,.039),l(.513,-.215),l(.289,-.255),l(-.033,-.31),l(-.223,-.478),l(.13,-.283),l(.4,-.341),l(.524,-.355),l(.324,-.426),l(-.198,-.477),l(.119,-.483),l(-.232,-.171),l(-.01,-.336),l(-.411,-.271),l(-.01,-.32),l(.464,-.413),l(.258,-.214),l(.246,.023),l(.559,-.395),l(.272,-.19),l(.53,-.292),l(1.148,-.152),l(1.345,-.012),l(.647,.144),l(.338,-.179),l(.332,.005),l(.455,-.297),l(.358,-.004),l(.148,-.142),l(.313,.14),l(.105,.112),l(.281,-.21),l(.296,.047),l(.554,.117),l(.004,.456),l(.282,-.128),l(.625,-.009),l(.373,.323),l(.213,.247),l(.007,.501),l(-.312,.516),l(.301,.07),l(.198,.218),l(.252,.281),l(.52,-.196),l(.239,0),l(.104,.199),l(.178,.09),l(.565,.436),l(1.337,.104),l(.305,.111),l(.147,.126),l(-.207,.084),l(-.688,.37),l(-.411,.232),l(-.155,.43),l(-.071,.479),l(-.234,.116),l(-.125,-.179),l(-.621,-.102),l(-.438,.132),l(-.271,.298),l(-.262,-.116),l(-.557,.265),l(-.928,-.149),l(-.568,-.138),l(-1.708,-.325),l(-.894,.265),l(-.38,.611),l(.09,.166),l(.336,0),l(.047,.314),l(-.202,.149),l(.101,.199),l(.598,-.017),l(-.002,.347),l(-.384,.099),l(-.139,.413),l(.389,.232),l(-.116,.479),l(-.215,.132),l(.089,.248),l(.606,.298),l(-.003,.532),l(.782,-.003),l(-.039,.43),l(.188,.248),l(.702,-.05),l(.668,.377),l(.012,.211),l(-.2,.255),l(-.504,.2),l(-.643,.196),l(-.517,.248),l(.032,.366),l(.188,.377),l(-.237,.977),l(-1.512,1.503),l(.261,.38),l(-.358,.281),l(-.609,.133),l(-.353,.467),l(-.509,1.216),l(-.478,.547),l(-1.193,.496),l(-.177,.364),l(-.277,.481),l(-.563,.61),l(-.076,.38),l(-.71,.259),l(-.734,-.011),l(-.895,.314),l(-.361,-.094),l(-.158,-.518),l(-.207,-.132),l(-.404,.083),l(-.535,.463),l(-.144,.446),l(-.979,.893),l(-.186,.543),l(-.079,.269),l(.116,.38),l(.541,.163),l(.504,.085),l(.425,.014),l(-.114,.689),l(-.154,.735),l(.412,.411),l(.33,.099),l(.537,-.099),l(.112,.441),l(.216,.507),l(.654,1.333),l(-.232,.149),l(.241,.463),l(-.317,.066),l(-.15,.248),l(-.55,.083),l(-.145,-.335),l(-.183,-.062),l(-.78,.215),l(-.2,.331),l(-.735,-.099),l(-.229,-.149),l(-.835,-.021),l(-.835,-.009),l(-.259,-.052),l(.015,.728),l(-.866,.017),l(-.296,.431),l(-.37,.036),
+N(606.155,150.953),l(-.037,-.466),l(-.627,-2.479),l(-.064,-.578),l(-.4,.044),l(-.281,.115),l(-.395,1.159),l(-.224,.354),l(-.529,-.448),l(-.249,-.267),l(-.23,-.281),l(-.001,-.381),l(-.009,-.522),l(.131,-.283),l(.651,-.356),l(.498,-.13),l(.938,-.767),l(.159,-.34),l(.012,-.79),l(-.057,-.127),l(-.111,-.098),l(-2.473,-.198),l(-.559,.003),l(-.916,.062),l(-.547,-.053),l(-.537,-.109),l(-.095,-.099),l(-.079,-.719),l(-.053,-.479),l(-.129,-.324),l(-.417,-.054),l(-.489,.06),l(-.372,-.266),l(-.339,-.266),l(-1.252,-.303),l(-.234,.171),l(-.555,.525),l(-.124,.283),l(.083,.155),l(.494,.237),l(1.224,.938),l(.126,.112),l(-.047,.155),l(-.382,.101),l(-.502,.13),l(-.4,.299),l(-.548,.61),l(.339,.323),l(1.042,.431),l(.193,.21),l(-.029,.269),l(-.505,.765),l(.04,.254),l(.352,.45),l(.325,1.409),l(.402,1.775),l(.147,.532),l(-.172,.322),l(.068,.183),l(-.321,.071),l(-.35,-.056),l(-.148,-.706),l(-.198,-.084),l(-.253,.354),l(-.171,.354),l(-.172,.057),l(-.115,-.099),l(-.28,-.592),l(-.152,-.169),l(-.412,.537),l(-.504,.298),l(-.992,.397),l(-.571,.213),l(-.264,.226),l(-.05,.113),l(.106,.579),l(.183,.409),l(-.336,.495),l(-.305,.332),l(-.013,.257),l(-.548,.415),l(-.593,.467),l(-.467,.185),l(-.432,.057),l(-.746,.206),l(-.591,.362),l(-.459,.58),l(-.551,.552),l(-.88,1.061),l(-.611,.552),l(-.673,.276),l(-.675,.829),l(-.676,.467),l(-.728,.368),l(-.452,.241),l(-.427,.312),l(-.192,.509),l(-.073,.466),l(-.341,.41),l(-.649,.298),l(-.428,.058),l(-.506,-.098),l(-.258,.269),l(-.366,.887),l(-.277,.23),l(-.505,-.253),l(-.614,.142),l(-.354,.283),l(-.304,.565),l(-.165,.48),l(-.005,.508),l(.166,1.539),l(.018,.805),l(-.208,.495),l(.227,.324),l(.315,.423),l(-.015,.508),l(-.199,.607),l(-.669,1.611),l(-.341,.834),l(-.13,.636),l(.047,.649),l(.21,1.764),l(-.081,.24),l(-.643,.001),l(-.433,-.013),l(-.186,.142),l(-.334,.961),l(-.324,.594),l(.025,.127),l(.493,.352),l(-.805,.327),l(-.306,.015),l(-.77,.284),l(-.295,.41),l(-.101,.692),l(-.157,.255),l(-.367,.283),l(-.526,.255),l(-.099,.057),l(-.066,.042),l(-.231,.156),l(-.145,0),l(-.174,-.056),l(-1.417,-1.451),l(-.092,-.395),l(-.276,-.522),l(-.34,-1.397),l(-.689,-1.792),l(-.94,-1.933),l(-.979,-1.508),l(-.554,-1.722),l(-.507,-1.792),l(-.738,-1.636),l(-.82,-1.42),l(-.424,-.737),l(-.598,-1.439),l(-.363,-1.157),l(-.767,-3.274),l(-.469,-2.695),l(-.157,-1.566),l(-.051,-.24),l(.232,-.565),l(.546,-.834),l(.007,-.311),l(-.404,-.281),l(-.367,-.437),l(-.113,-.635),l(-.108,-.917),l(.065,-.622),l(-.837,.03),l(-.19,.212),l(-.235,.424),l(.009,.183),l(.276,.254),l(.074,.183),l(-.073,.438),l(-.301,.438),l(-.503,.368),l(-.812,.369),l(-.504,.114),l(-.617,.255),l(-.377,.015),l(-.861,-.281),l(-.572,-.38),l(-1.018,-1),l(-1.391,-1.268),l(-.516,-.705),l(.267,-.043),l(1.062,.125),l(.615,-.086),l(.443,-.142),l(.461,-.283),l(.521,-.608),l(-.067,-.24),l(-.156,.071),l(-.587,.114),l(-.847,-.026),l(-.607,-.112),l(-.632,-.132),l(-.5,-.218),l(-.203,-.07),l(-.929,-.661),l(-.668,-.775),l(-.057,-.197),l(.37,-.036),l(.296,-.431),l(.866,-.017),l(-.015,-.728),l(.259,.052),l(.835,.009),l(.835,.021),l(.229,.149),l(.735,.099),l(.2,-.331),l(.78,-.215),l(.183,.062),l(.145,.335),l(.55,-.083),l(.15,-.248),l(.317,-.066),l(-.241,-.463),l(.232,-.149),l(-.654,-1.333),l(-.216,-.507),l(-.112,-.441),L(557,143.058),l(-.33,-.099),l(-.412,-.411),l(.154,-.735),l(.114,-.689),l(-.425,-.014),l(-.504,-.085),l(-.541,-.163),l(-.116,-.38),l(.079,-.269),l(.186,-.543),l(.979,-.893),l(.144,-.446),l(.535,-.463),l(.404,-.083),l(.207,.132),l(.158,.518),l(.361,.094),l(.895,-.314),l(.734,.011),l(.71,-.259),l(.076,-.38),l(.563,-.61),l(.277,-.481),l(.177,-.364),l(1.193,-.496),l(.478,-.547),l(.509,-1.216),l(.353,-.467),l(.609,-.133),l(.358,-.281),l(-.261,-.38),l(1.512,-1.503),l(.237,-.977),l(-.188,-.377),l(-.032,-.366),l(.517,-.248),l(.643,-.196),l(.504,-.2),l(.2,-.255),l(-.012,-.211),l(-.668,-.377),l(-.702,.05),l(-.188,-.248),l(.039,-.43),l(-.782,.003),l(.003,-.532),l(-.606,-.298),l(-.089,-.248),l(.215,-.132),l(.116,-.479),l(-.389,-.232),l(.139,-.413),l(.384,-.099),l(.002,-.347),l(-.598,.017),l(-.101,-.199),l(.202,-.149),l(-.047,-.314),l(-.336,0),l(-.09,-.166),l(.38,-.611),l(.894,-.265),l(1.708,.325),l(.568,.138),l(.928,.149),l(.557,-.265),l(.262,.116),l(.271,-.298),l(.438,-.132),l(.621,.102),l(.125,.179),l(.234,-.116),l(.071,-.479),l(.155,-.43),l(.411,-.232),l(.688,-.37),l(.207,-.084),l(.502,-.152),l(0,.515),l(.441,1.03),l(.221,.515),l(.81,.11),l(.441,.405),l(.221,.258),l(-.331,.221),l(-.184,.184),l(.073,.589),l(.037,.552),l(.294,.221),l(.718,.096),l(.092,.078),l(.044,.182),l(.68,.099),l(-.104,.414),l(.367,.694),l(-.578,.364),l(-.376,.281),l(-.368,.049),l(-.337,-.264),l(-.033,-.364),l(-.785,.198),l(.157,.694),l(.579,.595),l(-.104,.446),l(.272,.364),l(-.215,.215),l(.099,.43),l(.198,.066),l(.477,-.314),l(.383,.248),l(.772,.843),l(.363,-.116),l(1.078,.579),l(-.116,.364),l(.756,.248),l(.154,-.066),l(.859,.596),l(-1.07,.909),l(-.281,.116),l(.008,.364),l(-.221,.265),l(.04,.396),l(-.309,.397),l(-.218,.43),l(.039,.248),l(.63,.38),l(.466,-.083),l(.665,.479),l(.704,.149),l(.305,.529),l(1.242,.678),l(.231,-.166),l(1.027,.595),l(.538,-.066),l(.182,.397),l(.835,.166),l(.304,.198),l(.15,-.066),l(.087,-.215),l(.503,0),l(.482,.265),l(.582,-.347),l(.494,.298),l(.643,.066),l(.142,.231),l(-.101,.446),l(.663,.149),l(.286,.281),l(.618,.265),l(.584,-.281),l(.313,.066),l(-.034,.331),l(.312,.248),l(.379,-.231),l(.721,.148),l(.847,.364),l(.606,-.297),l(.268,.364),l(.399,.116),l(.357,-.132),l(.271,.083),l(.687,-.132),l(.336,.05),l(.311,-.694),l(-.429,-.827),l(.07,-.876),l(.318,-.761),l(-.037,-.022),l(.345,-.129),l(.471,-.102),l(.231,.069),l(.139,.183),l(-.052,.861),l(.212,.662),l(-.092,.113),l(.235,.493),l(.637,.18),l(.559,.081),l(.869,.214),l(.085,.021),l(.544,-.074),l(.531,-.313),l(.301,.055),l(.335,.125),l(1.238,.063),l(1.036,-.062),l(.624,-.06),l(.217,-.58),l(-.017,-.282),l(-.258,-.238),l(-.313,-.069),l(-.326,-.294),l(-.042,-.24),l(.07,-.156),l(.189,-.1),l(.784,-.033),l(.714,-.216),l(.397,-.27),l(1.123,-1.107),l(.543,-.37),l(.829,-.258),l(1.105,-.726),l(.546,-.299),l(.355,-.087),l(.286,.167),l(.975,.319),l(.377,-.044),l(1.377,-.84),l(.455,.392),l(.716,.715),l(.046,.38),l(-.354,.454),l(-.138,.269),l(.102,.056),l(.3,.055),l(.657,.081),l(.73,.179),l(.624,.081),l(-.018,.508),l(-.112,.24),l(-.596,.582),l(-.072,.283),l(.184,.718),l(-.295,.058),l(-.853,-.221),l(-.558,-.082),l(-.312,.129),l(-.719,.512),l(-1.446,.883),l(-.197,.354),l(.057,.761),l(-.136,.382),l(-.938,1.134),l(-.045,.212),l(.192,.408),l(0,.324),l(-1.148,2.25),l(-.17,.128),l(-.309,-.013),l(-.391,-.11),l(-.811,-.32),l(-.133,.283),l(.146,1.594),l(-.191,.354),l(-.401,.144),l(-.228,.185),l(.152,.93),l(-.015,.678),l(-.273,.368),l(-.25,.129),l(-.255,-.14),l(-.595,-.152),
+N(478.516,135.173),l(.052,-.292),l(-.049,-.169),l(-.197,-.107),l(.107,-.175),l(.292,-.919),l(-.015,-.818),l(.517,-1.09),l(.3,-.891),l(-.004,-.409),l(.133,-.283),l(.07,-.749),l(.033,-.847),l(.062,-.174),l(.026,-.278),l(-.065,-.411),l(.037,-.119),l(.102,-.063),l(1.902,1.034),l(.365,.167),l(.253,-.016),l(1.139,-.656),l(2.96,-1.541),l(.161,-.071),l(.87,1.632),l(.311,.69),l(.093,.056),l(-.46,.44),l(-.257,.171),l(-.97,.26),l(-2.586,.622),l(-.646,.229),l(1.146,1.32),l(.698,.885),l(-.765,.724),l(-.456,.37),l(-.24,.072),l(-1.249,.332),l(-.462,.61),l(-.975,.782),l(-2.079,-.299),l(-.154,-.017),
+N(456.028,151.46),l(-.021,1.003),l(.033,3.078),l(-.088,.17),l(-1.561,-.019),l(-.52,-.011),l(-.073,1.13),l(-1.981,-.963),l(-5.577,-2.792),l(-4.941,-2.372),l(-5.231,-2.554),l(-.41,.059),l(-1.818,.858),l(-1.519,.729),l(-.231,.157),l(-1.192,-.911),l(-.258,-.182),l(-1.008,-.333),l(-1.646,-.442),l(-.832,-.137),l(-.098,-.07),l(-.86,-1.506),l(-.178,-.168),l(-1.028,-.347),l(-.702,-.165),l(-.544,.201),l(-.244,-.154),l(-.562,-.873),l(-.249,-.917),l(-1.272,-1.702),l(.152,-.283),l(.249,-.128),l(.666,-.47),l(.086,-.17),l(.061,-.664),l(-.457,-1.156),l(.158,-.495),l(.16,-.622),l(-.101,-.748),l(.1,-.862),l(-.052,-.691),l(-.147,-.833),l(-.88,-1.605),l(.241,-.383),l(.204,-.156),l(.22,-.143),l(.864,-.598),l(.155,-.227),l(.147,-.537),l(-.274,-.973),l(.126,-.354),l(1.473,-1.138),l(.579,-.314),l(.802,-.428),l(.093,-.269),l(-.064,-.564),l(.093,-.981),l(1.436,.59),l(.995,.309),l(.521,.013),l(.863,-.129),l(.604,.027),l(1.552,.223),l(.614,.479),l(.794,.224),l(.486,-.001),l(.342,.197),l(.173,.226),l(.26,.819),l(.39,.564),l(.688,.591),l(.378,.126),l(.678,.14),l(1.031,.083),l(.447,.07),l(.973,.224),l(.639,.224),l(.552,.281),l(1.289,.788),l(.84,.464),l(.465,.013),l(.441,-.128),l(.758,-.411),l(.706,-.623),l(.3,-.523),l(0,-.254),l(-.276,-.621),l(-.112,-.579),l(.044,-.41),l(.237,-.537),l(.661,-.58),l(.333,-.198),l(.554,-.241),l(.644,-.298),l(1.376,-.413),l(1.205,.054),l(.728,.154),l(.844,.365),l(.188,.169),l(.043,.508),l(.281,.253),l(.301,.014),l(.945,.125),l(.712,.309),l(.45,.027),l(1.286,.04),l(.302,.197),l(.068,.381),l(.276,.296),l(-.099,.208),l(-.369,.228),l(-.21,.34),l(.354,1.283),l(-.231,.425),l(-.322,.553),l(-.056,.311),l(.185,.945),l(.388,.916),l(.042,.367),l(-.015,.988),l(-.019,3.869),l(-.016,1.468),l(-.008,2.048),l(-.001,.692),l(.045,2.81),l(-.021,1.342),l(.011,2.612),
+N(478.212,134.714),l(-.834,-1.746),l(-.796,-1.944),l(-.124,-.289),l(.109,-.066),l(.679,-.851),l(.219,-.438),l(.183,-.507),l(.171,-.566),l(.188,-.822),l(.116,.018),l(.137,-.093),l(.006,-.204),l(.028,-.315),l(.372,-.051),l(.151,.022),l(.07,.099),l(.355,-.07),l(.009,-.167),l(.067,-.193),l(.088,-.041),l(.07,.023),l(.183,.346),l(.201,.128),l(.021,.183),l(-.047,.122),l(.082,.085),l(-.102,.063),l(-.037,.119),l(.065,.411),l(-.026,.278),l(-.062,.174),l(-.033,.847),l(-.07,.749),l(-.133,.283),l(.004,.409),l(-.3,.891),l(-.517,1.09),l(.015,.818),l(-.292,.919),l(-.107,.175),l(-.067,.068),l(-.042,.042),
+N(495.751,163.817),l(-.03,-.184),l(-.112,-.537),l(-.508,-.945),l(-.73,-.987),l(-.438,-.493),l(-.846,-.69),l(-.51,-.875),l(-.814,-1.876),l(-.474,-.889),l(-.28,-.409),l(-.794,-.507),l(-1.271,-.689),l(-.527,-.634),l(-.8,-1.17),l(-.081,-.96),l(-.061,-.776),l(-.002,-.89),l(-.104,-.381),l(-.312,-.663),l(-.944,-1.65),l(-.357,-.494),l(-.758,-.62),l(-.459,-.225),l(-.499,-.126),l(-.308,-.282),l(-.483,-.578),l(.149,-.692),l(-.111,-.437),l(-.474,-.818),l(-.747,-1.072),l(-.793,-.902),l(-1.069,-1.494),l(-.525,-.931),l(-.352,-.479),l(-.722,-.761),l(-.524,-.056),l(-.248,0),l(.077,-.296),l(.193,-.283),l(.311,-1.314),l(.104,-.583),l(.154,.017),l(2.079,.299),l(.975,-.782),l(.462,-.61),l(1.249,-.332),l(.24,-.072),l(.456,-.37),l(.765,-.724),l(-.698,-.885),l(-1.146,-1.32),l(.646,-.229),l(2.586,-.622),l(.97,-.26),l(.257,-.171),l(.46,-.44),l(.093,.056),l(1.129,.459),l(.757,.222),l(2.034,.934),l(1.14,.473),l(2.44,1.101),l(.258,.196),l(.399,.633),l(.141,.07),l(.973,-.048),l(.135,.366),l(-.118,.396),l(.047,.818),l(.118,.253),l(.812,.277),l(1.557,.697),l(4.048,.132),l(1.657,.259),l(.408,.054),l(.334,.619),l(.185,.239),l(1.07,.05),l(.644,-.044),l(.043,.121),l(.277,.648),l(.365,.494),l(.178,.663),l(.052,.113),l(.615,-.029),l(.248,.126),l(-.204,.237),l(.194,.187),l(.184,.282),l(.234,.067),l(.234,.27),l(.886,.168),l(.423,.437),l(-.07,.28),l(-.235,.035),l(-.111,.25),l(.316,.381),l(-.188,.593),l(-.122,.198),l(.165,.268),l(.302,.666),l(.149,-.117),l(.372,.282),l(.105,.616),l(.422,.696),l(-.028,.423),l(.222,.268),l(.16,.197),l(.287,.084),l(.136,-.007),l(.336,.083),l(1.043,2.198),l(.548,1.154),l(.261,.902),l(3.362,.375),l(3.387,.446),l(.839,.108),l(.584,-.469),l(.152,-.057),l(.581,.914),l(.155,1.453),l(-5.346,2.982),l(-2.35,1.213),l(-1.691,.815),l(-.169,.085),l(-1.148,.346),l(-3.097,1.035),l(-4.618,1.566),l(-.484,.229),l(-.041,.127),l(-.383,.397),l(-1.907,2.271),l(-2.042,2.667),l(-1.302,-2.703),l(-1.211,-2.45),l(-.452,-.012),l(-.715,.047),l(-.446,-.125),l(-.671,-.151),l(-.216,.1),l(-.174,.185),l(-.11,.495),l(.041,.678),l(-.113,.565),l(-.692,.563),
+N(476.458,130.735),l(.124,.289),l(.796,1.944),l(.834,1.746),l(-.38,.381),l(-.479,1.145),l(-.334,1.413),l(-.171,.593),l(-.18,.156),l(-.407,-.07),l(-.448,-.521),l(-.782,-.676),l(-.386,-.494),l(-.338,-.988),l(-.521,-.45),l(-.289,-.621),l(-.17,-.479),l(-.198,-.353),l(-.466,.17),l(-.267,.523),l(.604,1.34),l(.131,.381),l(.634,.86),l(.933,1.042),l(.473,1.199),l(.526,.973),l(.277,.818),l(.391,.465),l(.912,1.735),l(1.072,1.904),l(.428,.705),l(.586,.549),l(.451,.352),l(.151,.183),l(-.241,.17),l(-.285,.1),l(-.043,.155),l(.238,1.087),l(.252,.467),l(.127,.238),l(.812,.591),l(.397,.168),l(.406,.521),l(.416,.38),l(.311,.56),l(-10.382,-.006),l(-2.138,-.001),l(-2.774,.002),l(-2.513,.015),l(-2.268,-.029),l(-1.664,.01),l(-1.241,.007),l(-1.614,-.019),l(-.914,.005),l(-.819,.089),l(-.011,-2.612),l(.021,-1.342),l(-.045,-2.81),l(.001,-.692),l(.008,-2.048),l(.016,-1.468),l(.019,-3.869),l(.015,-.988),l(-.042,-.367),l(-.388,-.916),l(-.185,-.945),l(.056,-.311),l(.322,-.553),l(.231,-.425),l(-.354,-1.283),l(.21,-.34),l(.369,-.228),l(.099,-.208),l(.337,.168),l(.553,-.015),l(.628,-.1),l(.786,-.001),l(1.513,.293),l(.587,.115),l(.779,.122),l(.721,.154),l(.348,.409),l(.4,0),l(.928,.083),l(.998,.394),l(.628,.069),l(.245,-.127),l(.72,-.538),l(.925,-.397),l(.837,-.186),l(.78,-.27),l(.375,-.072),l(.346,.056),l(.535,-.001),l(.723,.126),l(.202,.465),l(.596,.366),l(.518,-.156),l(.64,.267),l(.382,.027),l(.561,-.401),l(.241,.179),l(.277,.056),l(.674,.13),l(.338,-.062),l(.46,-.165),l(.51,-.308),
+N(580.625,132.873),l(.24,.43),l(.299,.05),l(.276,-.314),l(.134,-.513),l(.275,.066),l(.186,-.182),l(1.079,.199),l(.132,.612),l(.867,.264),l(.726,.595),l(.472,.215),l(.15,-.132),l(.454,.281),l(.657,.794),l(.158,-.166),l(.71,-.116),l(.289,.166),l(.178,.38),l(-.069,.281),l(1.451,.893),l(.333,-.248),l(.326,.017),l(-.175,.396),l(.014,.265),l(.935,.066),l(.365,-.066),l(.54,.496),l(.141,.463),l(.237,-.099),l(-.01,-.364),l(.685,.38),l(.271,-.083),l(.08,-.331),l(.407,0),l(.63,.281),l(.319,.364),l(.849,-.066),l(.341,.116),l(.339,-.281),l(.655,.159),l(.037,.022),l(-.318,.761),l(-.07,.876),l(.429,.827),l(-.311,.694),l(-.336,-.05),l(-.687,.132),l(-.271,-.083),l(-.357,.132),l(-.399,-.116),l(-.268,-.364),l(-.606,.297),l(-.847,-.364),l(-.721,-.148),l(-.379,.231),l(-.312,-.248),l(.034,-.331),l(-.313,-.066),l(-.584,.281),l(-.618,-.265),l(-.286,-.281),l(-.663,-.149),l(.101,-.446),l(-.142,-.231),l(-.643,-.066),l(-.494,-.298),l(-.582,.347),l(-.482,-.265),l(-.503,0),l(-.087,.215),l(-.15,.066),l(-.304,-.198),l(-.835,-.166),l(-.182,-.397),l(-.538,.066),l(-1.027,-.595),l(-.231,.166),l(-1.242,-.678),l(-.305,-.529),l(-.704,-.149),l(-.665,-.479),l(-.466,.083),l(-.63,-.38),l(-.039,-.248),l(.218,-.43),l(.309,-.397),l(-.04,-.396),l(.221,-.265),l(-.008,-.364),l(.281,-.116),l(1.07,-.909),
+N(374.125,167.166),l(-1.41,-.924),l(-.661,-.604),l(-.537,-.788),l(-.292,-.662),l(-.15,-.183),l(-.312,-.154),l(-.567,-.053),l(-.655,-.307),l(-.695,-.561),l(-.604,-.194),l(-.567,-.025),l(-1.209,.12),l(-1.821,.194),l(-.523,.417),l(.06,-.502),l(.563,-1.301),l(.188,-.819),l(.13,-1.13),l(-.162,-1.101),l(-.3,-.791),l(-.624,-.747),l(.267,-.283),l(.182,-.424),l(.067,-.975),l(-.065,-.537),l(-.174,-.353),l(-.808,-.817),l(-.297,-.112),l(-.393,.438),l(-.055,-.197),l(-.013,-.381),l(.08,-.689),l(.474,.013),l(5.055,.056),l(2.396,.014),l(.732,-.033),l(.212,.027),l(-.051,-1.102),l(-.164,-1.539),l(.01,-.707),l(.249,-.383),l(.575,-.427),l(.726,-.315),l(.809,-.287),l(.144,-.128),l(.099,-2.6),l(.07,-2.261),l(.047,-.509),l(.557,-.074),l(6.005,.022),l(.56,.011),l(.104,-.396),l(-.002,-.65),l(.03,-1.752),l(2.829,1.509),l(4.12,2.42),l(1.414,.91),l(-.523,.13),l(-3.231,.089),l(.049,.776),l(.908,7.962),l(.19,1.468),l(.178,1.736),l(.325,2.753),l(.202,2.386),l(.186,1.468),l(.724,.787),l(-.379,1.613),l(-6.457,.01),l(-1.953,.026),l(-.945,.344),l(-.387,.031),l(-.485,-.054),l(-.713,-.137),l(-.535,-.124),l(-.06,.212),l(.055,.296),l(-.107,.269),l(-.454,-.082),l(-.214,-.168),l(-.568,-.83),l(-.261,-.111),l(-.402,.073),l(-.252,.256),l(-.181,.552),l(-.018,.607),l(-.332,.284),
+N(476.114,191.139),l(-.563,.54),l(-.626,.314),l(-.367,.016),l(-.303,-.196),l(-.303,-.097),l(-.88,.132),l(-.881,.33),l(-.911,.048),l(-.942,-.234),l(-.495,-.011),l(-.464,.087),l(-.496,.229),l(-1.288,-1.32),l(-1.032,-.926),l(-.207,-.083),l(-.258,.157),l(-.436,.582),l(-.16,.1),l(-.684,-.631),l(-.384,-.012),l(-.529,.37),l(-.258,.242),l(-.352,.016),l(-.366,-.195),l(-.525,-.477),l(-.348,-.493),l(-.795,-.772),l(-.143,-.239),l(-.031,-.07),l(.083,-.679),l(-.157,-.352),l(-.558,-.477),l(-.814,-.165),l(-.367,-.224),l(-.285,-.436),l(-.059,-.72),l(-.206,-.366),l(-.207,-.168),l(-.717,-.434),l(-.558,-.378),l(-.445,-.351),l(-.173,-.395),l(.148,-.481),l(-.223,-.267),l(-.351,-.196),l(-.799,-.235),l(-.797,-.447),l(-.223,-.225),l(-.252,-.493),l(-.207,-.126),l(-.272,.001),l(-.802,.019),l(-.158,-.211),l(-.01,-.664),l(.416,-1.472),l(-.122,-.536),l(-.347,-.549),l(-1.342,-1.673),l(.15,-.425),l(-.026,-.367),l(-.266,-.465),l(-.444,-.407),l(-.155,-.295),l(-.137,-.522),l(-.221,-1.228),l(-.141,-.225),l(-.338,-.012),l(-.517,.088),l(-.219,-.352),l(.316,-.609),l(.542,-.596),l(.087,-.34),l(-.342,-.662),l(.053,-.24),l(.718,-.428),l(.149,-.212),l(-.131,-.677),l(.122,-.438),l(.583,-.809),l(.417,-1.245),l(.229,-.143),l(1.245,-.05),l(.839,.009),l(.04,-.283),l(-.031,-1.229),l(-.036,-1.002),l(.038,-.749),l(-.085,-2.5),l(.036,-.636),l(.016,-.946),l(.02,-1.045),l(.073,-1.13),l(.52,.011),l(1.561,.019),l(.088,-.17),l(-.033,-3.078),l(.021,-1.003),l(.819,-.089),l(.914,-.005),l(1.614,.019),l(1.241,-.007),l(1.664,-.01),l(2.268,.029),l(2.513,-.015),l(2.774,-.002),l(2.138,.001),l(10.382,.006),l(.229,.414),l(.499,1.115),l(.281,3.501),l(.319,1.637),l(.182,.211),l(.625,.394),l(.595,.366),l(.617,.507),l(.491,.197),l(.254,.223),l(-.238,.199),l(-.277,.327),l(-.306,.566),l(-.369,.242),l(-.511,.172),l(-.427,.116),l(-.153,.142),l(-.235,.496),l(-.348,.171),l(-.73,.033),l(-.204,.552),l(-.038,.452),l(-.202,.862),l(-.242,.722),l(-.541,1.302),l(-.068,.495),l(.092,.903),l(-.013,.679),l(-.001,.083),l(-.148,.947),l(-.398,1.231),l(-.214,1.061),l(-1.04,.331),l(-.414,.37),l(-.693,1.134),l(-.154,.34),l(-.191,1.088),l(-.141,1.074),l(-.197,.185),l(-.274,.044),l(-.431,-.096),l(-.259,.072),l(-.163,.114),l(-.187,1.103),l(-.115,.933),l(-.2,1.272),l(.039,.522),l(-.229,1.159),l(-.469,.299),l(-.288,-.012),l(-.928,-.122),l(-.321,.03),l(-.188,.806),l(-.054,.466),l(.094,.141),l(.479,.11),l(.734,.123),l(.334,.167),l(.805,.913),l(1.06,1.067),l(.751,1.464),l(.345,.732),l(.348,.421),l(.462,.28),l(.415,.097),l(.269,.309),l(.117,.988),l(-.034,.17),l(-.018,.141),l(-.079,-.028),l(-.464,.017),l(-1.009,.133),l(-.832,.005),l(-.671,-.052),l(-.291,.327),l(-.678,.795),
+N(518.402,163.079),l(-.282,.097),l(-.538,.27),l(-.656,.566),l(-.265,.297),l(-.166,.508),l(-.062,.41),l(-.41,.326),l(-1.418,.652),l(-2.27,.641),l(-1.285,.412),l(-.843,.312),l(-.356,.297),l(-.473,.622),l(-.436,.269),l(-.506,.114),l(-.593,-.069),l(-.521,.072),l(-.83,.439),l(-.65,.396),l(-.956,.397),l(-.752,.199),l(-1.16,.003),l(-.562,-.013),l(-.297,.128),l(-.386,.312),l(-.272,.297),l(-.45,.312),l(-.511,.241),l(-.389,.043),l(-.239,-.042),l(-.535,.411),l(-.839,.058),l(-.494,-.112),l(-.623,-.437),l(-.17,-.155),l(-.317,-.437),l(-.084,-.254),l(.18,-.721),l(-.081,-.635),l(-.312,-.507),l(-.341,-1.171),l(-.572,-1.919),l(-.072,-.438),l(.268,-.707),l(-.124,-.748),l(.692,-.563),l(.113,-.565),l(-.041,-.678),l(.11,-.495),l(.174,-.185),l(.216,-.1),l(.671,.151),l(.446,.125),l(.715,-.047),l(.452,.012),l(1.211,2.45),l(1.302,2.703),l(2.042,-2.667),l(1.907,-2.271),l(.383,-.397),l(.041,-.127),l(.484,-.229),l(4.618,-1.566),l(3.097,-1.035),l(1.148,-.346),l(.169,-.085),l(.092,.663),l(.226,.634),l(.604,.971),l(.933,1.364),l(.127,.253),l(-.006,.296),l(.483,.689),l(.241,.306),
+N(494.64,172.627),l(-.261,-.166),l(-.476,-.633),l(-.475,-.159),l(-.437,-.911),l(-1.267,-.792),l(-.277,-.594),l(-.673,-.673),l(-.594,-.119),l(-.871,-.554),l(-.555,.079),l(-.158,-.158),l(-.237,.04),l(-.277,-.198),l(-.356,.159),l(-.476,.079),l(-.277,-.436),l(-.158,.237),l(-.436,.198),l(-.792,.079),l(-.554,-.594),l(-.238,0),l(-.396,-.317),l(-.832,1.663),l(-.436,-.871),l(-.475,.079),l(-.277,.356),l(-.396,-.08),l(-.349,.041),l(.013,-.679),l(-.092,-.903),l(.068,-.495),l(.541,-1.302),l(.242,-.722),l(.202,-.862),l(.038,-.452),l(.204,-.552),l(.73,-.033),l(.348,-.171),l(.235,-.496),l(.153,-.142),l(.427,-.116),l(.511,-.172),l(.369,-.242),l(.306,-.566),l(.277,-.327),l(.238,-.199),l(.098,.129),l(.718,2.102),l(.442,1.623),l(.689,1.919),l(.618,.832),l(.205,-.212),l(-.072,-.479),l(.093,-.226),l(.31,.253),l(.4,.677),l(.384,.395),l(.534,-.043),l(.242,.014),l(1.338,1.282),l(.809,.916),l(.124,.099),l(.706,.055),l(.618,.874),l(.021,.24),l(-.025,.198),l(.118,.212),l(.507,.182),l(.915,.869),l(-.075,.094),l(-.529,.78),l(-.331,-.182),l(-.396,-.125),l(-.214,.114),l(-.055,.085),
+N(370.147,172.035),l(-2.301,-.043),l(-1.045,.006),l(-.505,.384),l(-.51,.187),l(-.74,-.024),l(-.819,.047),l(-.463,.139),l(-.009,-.003),l(-.278,-.226),l(-.169,-.409),l(.104,-.424),l(-.095,-.55),l(-.018,-.198),l(.001,-.046),l(1.695,.014),l(0,-.434),l(.155,0),l(.341,-.016),l(.186,-.077),l(.248,.062),l(.294,-.046),l(.124,-.093),l(.016,-.263),l(.077,-.078),l(.14,-.016),l(.155,.155),l(.232,.124),l(.356,.108),l(.046,.108),l(.139,.047),l(.217,-.031),l(.263,.108),l(.186,.17),l(.434,0),l(.186,-.108),l(.263,-.108),l(.279,0),l(.108,-.124),l(.016,-.124),l(-.155,-.217),l(-.202,-.077),l(-.201,.031),l(-.294,.093),l(-.108,.093),l(-.248,.062),l(-.279,-.186),l(-.031,-.186),l(-.186,-.108),l(-.17,-.015),l(-.186,.124),l(-.139,-.108),l(-.108,-.217),l(-.155,-.108),l(-.201,.031),l(-.17,-.062),l(-.387,.17),l(-.108,-.108),l(-.155,.046),l(-.202,.124),l(-.093,.294),l(-1.664,0),l(-.007,-.067),l(-.028,-.269),l(-.377,-.268),l(-.068,-.155),l(-.057,-.367),l(-.386,-.635),l(-.399,-.494),l(-.379,-.31),l(-.472,-.183),l(.54,-.34),l(.723,-.75),l(.588,-1.004),l(.334,-.82),l(.099,-.826),l(.523,-.417),l(1.821,-.194),l(1.209,-.12),l(.567,.025),l(.604,.194),l(.695,.561),l(.655,.307),l(.567,.053),l(.312,.154),l(.15,.183),l(.292,.662),l(.537,.788),l(.661,.604),l(1.41,.924),l(-.506,.399),l(-.057,.339),l(.251,1.1),l(.144,.663),l(.22,.479),l(.181,.197),l(.554,.322),l(.265,.337),l(.082,.833),l(-.004,.565),l(-.062,.142),l(-.146,-.042),l(-.963,.091),l(-.658,.074),l(-.725,-.081),l(-.503,-.209),l(-.795,-.32),l(-1.255,-.021),
+N(495.973,175.881),l(-.363,.807),l(.083,.409),l(.428,.732),l(.587,.844),l(.732,.801),l(.596,.547),l(.634,.321),l(.54,.209),l(4.26,1.443),l(1.447,.472),l(1.875,-.04),l(.236,.154),l(-4.087,4.205),l(-1.562,1.69),l(-.813,.909),l(-.8,.004),l(-1.693,-.046),l(-.626,.088),l(-.562,.215),l(-.388,.214),l(-.502,.497),l(-.294,.426),l(-.337,.115),l(-1.216,.078),l(-.305,.101),l(-.453,.511),l(-1.103,-.106),l(-.461,-.181),l(-.46,-.336),l(-.271,-.098),l(-.852,.358),l(-.675,.343),l(-.258,.199),l(-.71,.753),l(-.16,.114),l(-.847,-.094),l(-.67,-.193),l(-1.373,-.133),l(-.335,-.041),l(-2.353,-1.525),l(-.604,-.293),l(-.749,-.193),l(-1.054,-.149),l(-.319,-.069),l(.018,-.141),l(.034,-.17),l(-.117,-.988),l(-.269,-.309),l(-.415,-.097),l(-.462,-.28),l(-.348,-.421),l(-.345,-.732),l(-.751,-1.464),l(-1.06,-1.067),l(-.805,-.913),l(-.334,-.167),l(-.734,-.123),l(-.479,-.11),l(-.094,-.141),l(.054,-.466),l(.188,-.806),l(.321,-.03),l(.928,.122),l(.288,.012),l(.469,-.299),l(.229,-1.159),l(-.039,-.522),l(.2,-1.272),l(.115,-.933),l(.187,-1.103),l(.163,-.114),l(.259,-.072),l(.431,.096),l(.274,-.044),l(.197,-.185),l(.141,-1.074),l(.191,-1.088),l(.154,-.34),l(.693,-1.134),l(.414,-.37),l(1.04,-.331),l(.214,-1.061),l(.398,-1.231),l(.148,-.947),l(.001,-.083),l(.349,-.041),l(.396,.08),l(.277,-.356),l(.475,-.079),l(.436,.871),l(.832,-1.663),l(.396,.317),l(.238,0),l(.554,.594),l(.792,-.079),l(.436,-.198),l(.158,-.237),l(.277,.436),l(.476,-.079),l(.356,-.159),l(.277,.198),l(.237,-.04),l(.158,.158),l(.555,-.079),l(.871,.554),l(.594,.119),l(.673,.673),l(.277,.594),l(1.267,.792),l(.437,.911),l(.475,.159),l(.476,.633),l(.261,.166),l(-.594,.921),l(-.488,.61),l(-.105,.254),l(-.029,.396),l(.202,1.157),l(.5,-.074),l(.892,-.246),l(.4,.04),l(.556,.195),
+N(364.011,169.929),l(1.664,0),l(.093,-.294),l(.202,-.124),l(.155,-.046),l(.108,.108),l(.387,-.17),l(.17,.062),l(.201,-.031),l(.155,.108),l(.108,.217),l(.139,.108),l(.186,-.124),l(.17,.015),l(.186,.108),l(.031,.186),l(.279,.186),l(.248,-.062),l(.108,-.093),l(.294,-.093),l(.201,-.031),l(.202,.077),l(.155,.217),l(-.016,.124),l(-.108,.124),l(-.279,0),l(-.263,.108),l(-.186,.108),l(-.434,0),l(-.186,-.17),l(-.263,-.108),l(-.217,.031),l(-.139,-.047),l(-.046,-.108),l(-.356,-.108),l(-.232,-.124),l(-.155,-.155),l(-.14,.016),l(-.077,.078),l(-.016,.263),l(-.124,.093),l(-.294,.046),l(-.248,-.062),l(-.186,.077),l(-.341,.016),l(-.155,0),l(0,.434),l(-1.695,-.014),l(.019,-.477),l(.173,-.198),l(.434,-.058),l(.093,-.155),l(-.006,-.059),
+N(495.973,175.881),l(-.556,-.195),l(-.4,-.04),l(-.892,.246),l(-.5,.074),l(-.202,-1.157),l(.029,-.396),l(.105,-.254),l(.488,-.61),l(.594,-.921),l(.055,-.085),l(.214,-.114),l(.396,.125),l(.331,.182),l(.529,-.78),l(.075,-.094),l(.627,.596),l(.285,.465),l(.036,.282),l(-.118,.113),l(-.361,.185),l(-.438,.1),l(-.56,.312),l(-.566,.467),l(.253,.127),l(.645,-.058),l(.321,.013),l(.434,.124),l(-.114,.26),l(-.37,.468),l(-.34,.567),
+N(363.763,172.732),l(.463,-.139),l(.819,-.047),l(.74,.024),l(.51,-.187),l(.505,-.384),l(1.045,-.006),l(2.301,.043),l(-.111,.792),l(.115,.847),l(-.186,.312),l(-.333,.186),l(-.513,.031),l(-.401,.017),l(-.381,.186),l(-.789,.64),l(-.321,.372),l(-.047,-.126),l(-.192,0),l(-.501,-.14),l(-.165,-.254),l(.121,-.41),l(.33,-.438),l(.253,-.212),l(-.131,-.141),l(-.372,-.112),l(-.498,.015),l(-.415,.17),l(-.161,0),l(-.616,-.281),l(-.36,-.296),l(-.069,-.24),l(-.323,-.084),l(-.316,-.137),
+N(383.005,177.596),l(-.379,.397),l(-.264,.623),l(.214,.409),l(.695,.405),l(.197,.31),l(-.125,.283),l(-.332,.37),l(.309,.351),l(.648,.561),l(.051,.226),l(-.19,.143),l(-.304,-.012),l(-.548,-.223),l(-.304,.03),l(-.158,.199),l(-.03,.127),l(.296,.549),l(.179,.21),l(-.188,.354),l(-.602,.554),l(-.047,.099),l(-.339,-.224),l(-.337,-.097),l(-.143,.086),l(-.756,.922),l(-.321,-.026),l(-.404,-.322),l(-.277,-.323),l(.044,-.283),l(-.009,-.65),l(-.22,-.846),l(-.165,-.366),l(-.338,-.097),l(-.528,.046),l(-.367,.143),l(-.252,.242),l(-.193,.376),l(-.26,-.656),l(-.165,-1.369),l(-.252,-.761),l(-.475,-.619),l(-.456,-.421),l(-.42,-.224),l(-.466,-.04),l(-1.137,.148),l(-.594,-.039),l(-.238,.157),l(-.643,.866),l(-1.088,1.235),l(.051,-.392),l(-.312,-.55),l(-.764,-.775),l(-.122,-.649),l(-.84,-.366),l(-.908,-.563),l(-.277,-.296),l(.09,-.396),l(-.053,-.311),l(-.547,-.041),l(-.323,-.098),l(-.115,-.155),l(.059,-.311),l(-.038,-.1),l(.321,-.372),l(.789,-.64),l(.381,-.186),l(.401,-.017),l(.513,-.031),l(.333,-.186),l(.186,-.312),l(-.115,-.847),l(.111,-.792),l(1.255,.021),l(.795,.32),l(.503,.209),l(.725,.081),l(.658,-.074),l(.963,-.091),l(.146,.042),l(.113,.028),l(.18,.183),l(.882,.715),l(.536,.265),l(.252,-.256),l(.475,-.413),l(.338,.026),l(.356,.153),l(.565,.152),l(.43,-.243),l(.3,-.327),l(.684,-.428),l(.323,.055),l(.246,.281),l(.022,.339),l(.271,.832),l(.265,.451),l(.474,.478),l(.349,.718),l(.194,1.143),l(.559,.901),
+N(266.015,188.956),l(.103,.169),l(-.163,.326),l(-.592,.385),l(-1.74,.9),l(-.807,.23),l(-.557,.074),l(-.465,-.054),l(-.284,.115),l(-.232,1.117),l(-.348,.115),l(-.628,-.618),l(-.344,-.224),l(-1.149,.035),l(-.385,-.04),l(-.896,-.461),l(-.309,-.125),l(-.159,.029),l(-.041,.184),l(.616,.688),l(.391,.69),l(.302,1.524),l(.079,.55),l(.166,.239),l(.96,.051),l(.434,.125),l(.15,.253),l(-.265,.27),l(-.569,.272),l(-.652,.131),l(-.203,.199),l(-.259,.666),l(-.235,.213),l(-.652,.173),l(-.554,.286),l(-.74,.654),l(-.645,.739),l(-.271,.016),l(-.186,-.776),l(-.083,-.183),l(-.757,.697),l(-.414,.073),l(-.482,-.223),l(-.694,-.546),l(-.432,-.054),l(-.199,-.437),l(-.088,-.452),l(-.161,-.861),l(-.138,-.437),l(-.148,-.168),l(-.797,-1.182),l(-.51,-.491),l(.479,-.526),l(.731,-.612),l(-.121,-.282),l(-.486,-.647),l(-.256,-.437),l(-.447,-.789),l(-.162,-.804),l(-.048,-.367),l(-.035,-.438),l(-.026,-.254),l(.147,-.326),l(.379,-.511),l(.085,-1.004),l(.409,-.525),l(-.644,-.081),l(-1.99,.224),l(-.76,.174),l(-.522,.13),l(-.144,0),l(-.554,-.576),l(-.847,-.998),l(-.188,-.253),l(-.64,-.321),l(-.521,-.181),l(-1.167,.05),l(-1.163,.12),l(-.496,.017),l(-.397,-.252),l(-.429,-.548),l(-.401,-.309),l(-.099,-.353),l(.226,-1.132),l(-.103,-.395),l(-.855,-1.45),l(-.31,-.606),l(-.384,.017),l(-.234,.1),l(-.402,-.025),l(.709,-1.191),l(.241,-.722),l(.172,-.722),l(.99,-1.758),l(.381,-.059),l(.227,.027),l(.129,-.396),l(-.048,-.497),l(.056,-.288),l(.414,-.2),l(.534,-.156),l(.84,-.171),l(.128,.105),l(-.9,.151),l(-.731,.312),l(-.145,.212),l(.19,.607),l(.142,.407),l(.224,.126),l(-.043,.145),l(.153,.579),l(-.135,.367),l(-.327,.364),l(-.348,.824),l(-.137,.368),l(.253,.479),l(.288,.253),l(.25,.72),l(.341,.353),l(.523,-.114),l(.184,-.156),l(.419,-.255),l(.12,-.142),l(.066,-.523),l(-.167,-.649),l(-.21,-.282),l(-.438,-.804),l(-.136,-.135),l(-.118,-.395),l(-.247,-.18),l(.239,-.099),l(.095,-.251),l(-.204,-.144),l(1,-.379),l(1.085,-.327),l(.998,-.272),l(.086,-.225),l(.69,-.086),l(.143,-.008),l(-.042,-.157),l(-.055,-.198),l(-.125,-.036),l(-.039,-.108),l(-.128,-.072),l(-.226,.071),l(-.156,.027),l(-.229,-.012),l(-.315,-.55),l(.109,-.254),l(.337,-.213),l(.367,-.043),l(.09,.112),l(.14,.368),l(.186,.162),l(-.001,.148),l(.026,.193),l(.068,.09),l(.004,.198),l(.253,.258),l(.329,-.02),l(.699,.111),l(.455,.07),l(.593,.196),l(.323,.254),l(.393,.564),l(.156,.635),l(.358,.324),l(.359,.084),l(1.02,-.129),l(.928,-.059),l(.59,-.058),l(.799,-.059),l(.714,.125),l(.4,.479),l(.267,.169),l(.578,.253),l(.49,.14),l(1.094,.04),l(.382,-.057),l(.388,-.227),l(1.042,-.807),l(.47,-.185),l(.453,.042),l(.959,-.073),l(1.152,-.073),l(.919,.055),l(.248,.112),l(-.056,.141),l(-.294,.185),l(-.854,-.041),l(-.433,.015),l(-.083,.212),l(.059,.184),l(.593,.253),l(.609,.535),l(.195,.649),l(.246,-.523),l(.185,-.142),l(.415,.253),l(.483,.027),l(.374,.098),l(.258,.338),l(.918,.394),l(.464,.295),l(-.729,.496),l(-.161,.65),l(-.214,.226),l(-1.055,.417),l(.5,.064),l(.598,.098),l(.368,-.029),l(.33,-.142),l(.929,-.03),l(.725,.083),l(.84,.274),l(.095,.296),l(-.061,.41),l(-1.655,1.239),l(-.101,.255),l(.074,.212),l(.62,.604),l(.141,.282),l(-.308,.299),l(-.41,.144),l(-1.032,.19),l(-.061,.452),l(.008,.58),l(-.395,.539),l(-.071,.212),l(.324,.521),l(.732,.745),l(.503,.647),
+N(493.044,204.258),l(-1.223,-1.771),l(-.027,-.932),l(-.03,-1.43),l(-.042,-2.045),l(-.003,-1.017),l(0,-.438),l(.016,-.848),l(.004,-1.215),l(.002,-.508),l(1.649,-2.467),l(.453,-.511),l(.305,-.101),l(1.216,-.078),l(.337,-.115),l(.294,-.426),l(.502,-.497),l(.388,-.214),l(.562,-.215),l(.626,-.088),l(1.693,.046),l(.8,-.004),l(.813,-.909),l(1.562,-1.69),l(4.087,-4.205),l(-.236,-.154),l(-1.875,.04),l(-1.447,-.472),l(-4.26,-1.443),l(-.54,-.209),l(-.634,-.321),l(-.596,-.547),l(-.732,-.801),l(-.587,-.844),l(-.428,-.732),l(-.083,-.409),l(.363,-.807),l(.34,-.567),l(.37,-.468),l(.114,-.26),l(.154,.044),l(.935,1.142),l(.586,.62),l(.243,.381),l(.265,.211),l(.372,-.071),l(.417,-.001),l(.465,.027),l(.372,-.071),l(.572,-.27),l(.836,-.425),l(.585,-.157),l(.397,.098),l(.76,.267),l(.549,-.072),l(.56,-.326),l(.779,-.566),l(.247,-.127),l(.447,.041),l(.479,.098),l(.419,-.043),l(1.195,-.482),l(.288,.027),l(.682,.196),l(.74,-.03),l(.764,-.185),l(.964,-.327),l(.9,-.666),l(.47,-.382),l(.604,.154),l(.391,.211),l(.08,.014),l(.147,.268),l(-.414,.919),l(.021,.564),l(.132,.621),l(-.165,.452),l(-.375,.509),l(-.028,.678),l(-.047,.833),l(-.163,.509),l(-1.264,2.262),l(-.842,.792),l(-.122,.311),l(.102,.353),l(-.893,1.569),l(-.834,1.272),l(-.214,.947),l(-.351,.636),l(-.712,1.117),l(-.874,1.188),l(-1.159,1.498),l(-.384,.439),l(-2.274,2.504),l(-1.82,1.557),l(-2.164,1.121),l(-.593,.382),l(-1.28,1.09),l(-1.74,1.755),l(-.06,.061),l(-1.055,1.1),l(-1.235,1.569),l(-.615,.835),l(.1,.353),l(-.094,.276),
+N(264.768,176.039),l(-.128,.225),l(-.115,.067),l(-.029,.135),l(.039,.25),l(-.058,.086),l(.125,.376),l(-.039,.424),l(-.453,.154),l(-.135,-.01),l(-.144,.039),l(-.482,-.039),l(-.192,.039),l(-.087,-.048),l(-.356,0),l(-.02,-.058),l(.039,-.067),l(.202,-.029),l(.222,-.135),l(.019,-.087),l(.106,.02),l(.154,.01),l(.135,-.145),l(-.096,-.279),l(.048,-.125),l(.029,-.183),l(-.067,-.125),l(-.097,-.135),l(-.279,-.048),l(.116,-.096),l(.164,-.019),l(.231,-.029),l(.115,-.087),l(.385,.02),l(.106,-.039),l(.299,-.067),l(.244,.006),
+N(654.075,190.187),l(.206,-.125),l(.63,-.114),l(.656,-.938),l(.241,-.07),l(-.069,.268),l(.122,.087),l(.187,-.046),l(.11,.174),l(.148,.444),l(-.024,.111),l(-.013,.247),l(.197,.197),l(.025,.086),l(-1.234,.42),l(-.383,.271),l(-.309,-.271),l(-.111,-.259),l(-.234,.012),l(-.062,-.37),l(-.084,-.126),
+N(493.044,204.258),l(-.602,.389),l(-.557,.171),l(-.385,-.112),l(-.086,.777),l(-.282,.367),l(-.67,.115),l(-.394,.382),l(-.088,.48),l(.006,.353),l(-.356,.622),l(-.964,1.358),l(.092,.536),l(-.337,.65),l(-.25,.255),l(-.334,.1),l(-.084,.152),l(-1.067,-.807),l(-2.427,-1.752),l(-.07,-.239),l(.116,-.552),l(-.137,-.381),l(-3.553,-1.984),l(-4.663,-2.568),l(-1.448,.033),l(1.144,-2.479),l(.105,-.229),l(.656,-.835),l(.985,-.938),l(.477,-.525),l(.43,-.695),l(.143,-.566),l(-.048,-.664),l(-.223,-.606),l(-.224,-.352),l(-.732,-1.069),l(-.174,-.465),l(-.027,-.861),l(-.126,-.226),l(-.477,-.463),l(-.3,-.493),l(.678,-.795),l(.291,-.327),l(.671,.052),l(.832,-.005),l(1.009,-.133),l(.464,-.017),l(.079,.028),l(.319,.069),l(1.054,.149),l(.749,.193),l(.604,.293),l(2.353,1.525),l(.335,.041),l(1.373,.133),l(.67,.193),l(.847,.094),l(.16,-.114),l(.71,-.753),l(.258,-.199),l(.675,-.343),l(.852,-.358),l(.271,.098),l(.46,.336),l(.461,.181),l(1.103,.106),l(-1.649,2.467),l(-.002,.508),l(-.004,1.215),l(-.016,.848),l(0,.438),l(.003,1.017),l(.042,2.045),l(.03,1.43),l(.027,.932),l(1.223,1.771),
+N(466.196,203.275),l(.188,-.298),l(.076,-.27),l(-.057,-.748),l(.025,-.734),l(-.021,-.593),l(.107,-.507),l(.217,-1.02),l(.395,-.681),l(.255,-.284),l(1.241,-.996),l(1.195,-1.066),l(.191,-.453),l(-.111,-.31),l(-.271,-.182),l(-.479,-.04),l(-.191,-.027),l(-.128,-.253),l(.26,-1.88),l(.018,-.424),l(-.159,-.183),l(-.063,-.028),l(.496,-.229),l(.464,-.087),l(.495,.011),l(.942,.234),l(.911,-.048),l(.881,-.33),l(.88,-.132),l(.303,.097),l(.303,.196),l(.367,-.016),l(.626,-.314),l(.563,-.54),l(.3,.493),l(.477,.463),l(.126,.226),l(.027,.861),l(.174,.465),l(.732,1.069),l(.224,.352),l(.223,.606),l(.048,.664),l(-.143,.566),l(-.43,.695),l(-.477,.525),l(-.985,.938),l(-.656,.835),l(-.105,.229),l(-1.144,2.479),l(-4.659,0),l(-1.277,.05),l(-.319,.017),l(-.554,.398),l(-.458,.427),l(-.431,.045),l(-.546,-.223),l(-.064,-.042),
+N(713.621,206.298),l(.169,7.966),l(-.44,.822),l(.431,1.368),l(.046,.805),l(-.031,3.438),l(-.515,-.512),l(-.927,-.888),l(-.716,-.902),l(-.406,-.056),l(-.776,.101),l(-.739,.143),l(-.434,-.013),l(.091,-.382),l(.435,-.65),l(.006,-.283),l(-.561,-.521),l(-.565,-.775),l(.028,-.226),l(.442,.111),l(.236,-.042),l(.135,-.113),l(-.467,-.409),l(-.595,-.408),l(-.287,-.381),l(-.275,-.648),l(-1.053,-1.693),l(-.508,-.394),l(-.467,-.282),l(-.604,-.196),l(-1.983,-.603),l(-1.26,-.379),l(-.613,-.069),l(-.705,-.238),l(-.63,-.323),l(.072,-.34),l(-.098,-.268),l(-.193,-.028),l(-.617,.101),l(-.389,-.07),l(-.412,-.196),l(-.408,-.395),l(-.209,-.579),l(.133,-.494),l(-.155,-.226),l(-.187,.113),l(-.234,.396),l(-.122,.664),l(-.251,.608),l(-.334,.269),l(-.696,.354),l(-.155,-.169),l(-.331,-.677),l(.022,-.155),l(.384,-.27),l(-.152,-.424),l(-.173,-.239),l(-.564,-.395),l(-.707,-.394),l(-.338,-.056),l(-.059,-.212),l(.038,-.226),l(.413,-.044),l(.388,.084),l(.603,.239),l(.158,-.029),l(.368,-.34),l(.525,-.41),l(.146,.056),l(.3,.269),l(1.021,-.045),l(.139,-.128),l(.09,-.522),l(-.063,-.409),l(-.238,.028),l(-.345,.199),l(-.604,.071),l(-.656,-.041),l(-.766,.044),l(-1.026,-.082),l(-.411,-.31),l(-.135,-.197),l(-.148,-.664),l(-.202,-.338),l(-.42,-.155),l(-1.249,-.124),l(.265,-.297),l(.058,-.255),l(.004,-.593),l(.463,-.029),l(.92,-.411),l(.49,-.383),l(.444,-.283),l(.352,.027),l(.4,.069),l(1.494,.646),l(.515,.169),l(.913,.153),l(.382,.705),l(.138,.396),l(-.283,.749),l(-.067,.381),l(.221,.381),l(.115,.494),l(.115,.48),l(.215,.521),l(.186,.197),l(.197,.127),l(.226,-.65),l(.085,.113),l(.087,.141),l(.309,1.073),l(.169,.169),l(.234,.183),l(.294,.112),l(.354,.056),l(.58,-.198),l(.504,-.439),l(1.192,-1.853),l(.352,-.015),l(1.078,-.215),l(.378,-.142),l(.045,-.085),l(.014,-.509),l(.219,-.17),l(1.1,-.609),l(.335,-.043),l(1.732,.759),l(2.129,.941),l(1.54,.52),l(1.299,.404),M(691.208,208.707),l(-.388,-.069),l(-.693,-.38),l(-.852,-.647),l(-.295,-.141),l(-.414,.028),l(-.059,.1),l(.024,.452),l(-.206,.028),l(-1.014,-.407),l(-.258,-.353),l(-.582,.199),l(-.289,.269),l(-.326,.185),l(-.186,-.184),l(-.312,-.451),l(-.245,-.451),l(.246,-.198),l(.303,-.029),l(.274,.056),l(1.104,.04),l(.574,.31),l(.319,-.015),l(.544,-.326),l(.414,-.015),l(.534,.126),l(.857,.21),l(.499,.395),l(.293,.395),l(.179,.621),l(-.049,.254),M(682.045,208.699),l(-.419,-.056),l(-.715,-.493),l(-.232,-.451),l(.146,-.283),l(.603,-.1),l(.766,-.044),l(.246,.126),l(.256,.311),l(.313,.197),l(.108,.226),l(-.067,.226),l(-.125,.057),l(-.879,.285),M(707.635,219.095),l(-1.11,-.209),l(.589,-1.032),l(.56,-.708),l(.407,-.269),l(.427,-.072),l(.527,.338),l(.198,.24),l(-.11,.184),l(-.324,.637),l(-.256,.17),l(-.638,.693),l(-.27,.028),M(673.797,218.703),l(-.562,.257),l(.034,.233),l(-.886,.326),l(-.582,.274),l(-.339,-.041),l(-.453,.325),l(-.504,-.069),l(-.427,-.112),l(-.378,.255),l(-.3,.058),l(-.358,-.07),l(-.58,-.196),l(-1.046,-.04),l(-.316,.043),l(-.211,-.564),l(.027,-.24),l(.383,-.198),l(.672,-.199),l(.528,-.016),l(1.142,.407),l(.445,.324),l(.338,.013),l(.326,-.297),l(.464,-.016),l(.429,-.071),l(.414,.187),l(.467,-.116),l(-.072,-.222),l(.421,-.187),l(.404,-.233),l(.094,-.151),l(-.076,-.117),l(-.184,.023),l(.116,-.198),l(.16,.012),l(.22,.094),l(.177,.221),l(.013,.304),M(662.661,219.065),l(-.312,-.099),l(-.203,-.127),l(-.062,-.169),l(.03,-.212),l(.256,-.198),l(.315,-.036),l(.17,.092),l(.053,.212),l(.182,.098),l(.305,-.145),l(.34,.105),l(.104,.151),l(-.012,.451),l(.183,-.148),l(.163,-.304),l(.318,-.029),l(.229,.226),l(.021,.424),l(.181,-.036),l(.062,.104),l(-.025,.397),l(-.316,-.211),l(-.311,-.058),l(-.141,.058),l(.072,.155),l(-.852,.157),l(-.143,-.091),l(.097,-.268),l(-.085,-.059),l(-.308,.269),l(-.229,.256),l(-.296,-.046),l(-.63,.225),l(-.624,.199),l(-.357,-.051),l(-.31,.123),l(-.392,-.07),l(-.103,-.07),l(-.202,-.123),l(-.063,-.279),l(.143,-.261),l(-.08,-.253),l(.193,-.115),l(.23,-.113),l(.233,-.156),l(.224,.07),l(.61,.013),l(.4,.104),l(.089,.28),l(.291,.109),l(.294,.056),l(.189,-.259),l(.29,-.012),l(.051,-.187),l(-.263,-.15),M(656.294,219.602),l(-.393,-.282),l(-.855,-.449),l(-.118,-.269),l(.417,-.001),l(.514,-.185),l(.462,-.029),l(.925,.521),l(-.338,.17),l(-.232,.1),l(-.381,.425),M(631.053,200.125),l(-.061,.225),l(-.413,.439),l(-.204,.41),l(-.381,.354),l(.164,.353),l(.162,.169),l(.806,.493),l(.832,.055),l(.241,.112),l(.151,.381),l(.128,.763),l(-.007,.409),l(.267,.423),l(.212,.127),l(.544,.041),l(-.45,.933),l(.151,.212),l(.703,-.453),l(.824,.252),l(.177,.042),l(.265,.254),l(.144,.438),l(.698,.676),l(-.515,1.979),l(-.04,.452),l(.23,.946),l(-.021,.438),l(.021,.664),l(-.002,.268),l(-.149,1.06),l(-.087,.156),l(-.107,.07),l(-.367,-.253),l(-.381,-.522),l(-.261,-.084),l(-.262,.481),l(-.081,.268),l(-1.043,-.619),l(-.219,.086),l(.394,.747),l(-.163,.213),l(-.204,-.197),l(-1.343,-1.424),l(-.775,-.761),l(-1.011,-.859),l(-1.348,-.958),l(-.391,-.451),l(-.199,-.493),l(-.191,-.339),l(-1.003,-.633),l(-.697,-.677),l(-1.186,-1.509),l(-.074,-.353),l(.039,-.339),l(-.324,-.875),l(-.841,-1.467),l(-.667,-1.044),l(-.612,-.775),l(-.369,-.301),l(-.287,-.234),l(-.64,-.295),l(-.254,-.748),l(-.688,-1.806),l(.067,-.24),l(-.107,-.311),l(-.157,-.197),l(-.662,-.507),l(-.711,-.394),l(-.539,-.21),l(-.317,-.099),l(-.119,-.353),l(-.077,-.734),l(-.18,-.409),l(-.386,-.479),l(-.818,-.831),l(-.368,-.423),l(-.725,.128),l(-.613,-.676),l(-.646,-.606),l(-.593,-.69),l(-.562,-.945),l(-.229,-.635),l(-.032,-.367),l(.057,-.198),l(.149,-.113),l(.401,-.043),l(.364,.098),l(.25,.126),l(.632,.563),l(.361,.155),l(.922,.153),l(.335,.027),l(.548,-.1),l(.454,-.142),l(.4,-.015),l(.323,.31),l(.919,1.156),l(.513,.31),l(.058,.155),l(-.12,.537),l(1.066,.916),l(.749,.493),l(1.175,.689),l(.678,.323),l(.139,.169),l(.03,.593),l(-.02,.155),l(.573,.055),l(.745,.944),l(.612,.55),l(.271,-.015),l(.004,-.198),l(-.123,-.226),l(.069,-.24),l(.507,.21),l(.479,.804),l(.441,.38),l(.446,.056),l(.429,.197),l(.314,.366),l(.28,.734),l(.316,.437),l(.431,.268),l(.511,.126),l(.767,.083),l(.431,.154),l(.494,.38),l(.576,.606),l(-.019,.071),M(684.201,200.125),l(-.007,-.172),l(-.414,-1.058),l(.18,-.551),l(-.078,-.141),l(-.141,-.296),l(.036,-.325),l(.286,-.89),l(.514,-.82),l(.263,.367),l(.152,.353),l(-.054,.283),l(-.246,.396),l(-.361,.763),l(.061,.325),l(.19,.141),l(.097,-.141),l(.436,-.411),l(.135,-.522),l(.179,-.142),l(.806,-.412),l(.141,.141),l(-.052,.254),l(.104,.55),l(-.354,.212),l(-.467,.354),l(-.162,.311),l(.159,.099),l(.446,.126),l(.398,.211),l(-.016,.141),l(.159,.353),l(-.688,-.154),l(-.431,-.154),l(-.367,-.042),l(-.304,.156),l(-.08,.438),l(.049,.258),l(.131,.688),l(.341,.62),l(.405,.438),l(.196,.282),l(-.156,.212),l(-.26,-.211),l(-.664,-.648),l(-.55,-.733),l(-.002,-.396),l(-.011,-.251),M(637.361,207.144),l(-.863,-.394),l(-.377,-.239),l(-.205,-.367),l(-.045,-.367),l(-.156,-.395),l(-.507,-.395),l(-.291,-.099),l(-.446,.029),l(-.116,-.141),l(.271,-.65),l(.234,-.24),l(.509,.55),l(.148,-.467),l(.313,-.269),l(.072,.395),l(.312,.89),l(.648,.817),l(.698,.31),l(-.265,.184),l(-.118,.283),l(.183,.564),M(634.321,215.345),l(-.091,-.187),l(.316,-.023),l(.402,.093),l(.369,-.129),l(.068,-.524),l(.018,-.14),l(.309,.057),l(-.043,-.5),l(.222,-.235),l(.093,-.277),l(.202,.121),l(.631,.112),l(.474,-.047),l(.237,.443),l(.524,-.089),l(.158,-.297),l(.022,-.244),l(.259,.116),l(.618,.168),l(.411,.438),l(.338,-.046),l(.204,.271),l(.446,-.029),l(.453,-.185),l(.302,.211),l(.369,.522),l(.179,.521),l(.884,.041),l(.462,.188),l(.49,-.077),l(1.435,.124),l(.479,-.029),l(.34,-.17),l(.213,-.65),l(.271,-.269),l(.447,-.015),l(.223,.211),l(.289,.494),l(.633,.125),l(.53,.027),l(.774,.083),l(.796,.153),l(.289,.24),l(.293,.288),l(-.08,.445),l(.275,.466),l(.119,.099),l(.877,.352),l(.422,.069),l(.658,.013),l(.45,-.185),l(.415,-.015),l(.628,.238),l(.048,.197),l(-.255,.425),l(-.152,.494),l(.578,.776),l(-.499,-.211),l(-.802,-.196),l(-.599,-.253),l(-.891,-.309),l(-.528,.001),l(-.589,.256),l(-.348,.057),l(-.714,-.098),l(-1.454,-.138),l(-1.47,-.138),l(-.805,-.253),l(-.839,-.479),l(-1.099,-.336),l(-1.125,-.267),l(-.948,-.04),l(-.556,.298),l(-.445,.043),l(-.957,-.153),l(-.805,-.492),l(-.357,-.07),l(-1.606,-.066),l(-.363,-.155),l(.055,-.141),l(.448,-.468),l(-.402,-.267),l(-.551,-.099),l(-.506,-.14),l(-.307,-.027),l(-1.261,-.121),M(675.004,223.092),l(.249,-.494),l(.023,-.537),l(.113,-.312),l(.674,-.481),l(1.447,-.624),l(.662,-.454),l(.36,-.607),l(.466,-.157),l(1.578,-.102),l(.91,-.214),l(.541,-.044),l(.869,-.143),l(.118,.07),l(.099,.197),l(-.237,.212),l(-.36,.256),l(-1.609,.61),l(-1.369,.44),l(-.713,.256),l(-.606,.354),l(-1.09,.963),l(-.653,.481),l(-.439,.086),l(-.552,.228),l(-.48,.015),M(667.866,223.149),l(-.217,-.069),l(-.917,-.605),l(-.8,-.45),l(-.347,-.099),l(-.493,-.126),l(-.292,-.197),l(.108,-.212),l(.371,-.142),l(.992,-.03),l(.502,-.114),l(.35,.296),l(1.147,.746),l(.265,.381),l(-.125,.325),l(-.246,.24),l(-.299,.057),M(661.819,191.241),l(-.041,.09),l(.319,.691),l(-.23,.142),l(-.546,.043),l(-.579,.086),l(.198,.226),l(.115,.296),l(-.169,.226),l(.216,.211),l(.235,.112),l(.546,.832),l(.536,.747),l(.043,.198),l(-.338,.721),l(.075,.226),l(.406,.465),l(.743,.45),l(.6,.493),l(.551,.761),l(-.465,.17),l(-.75,-.026),l(-.797,-.238),l(-.337,.1),l(-.387,.467),l(-.354,.918),l(-.08,.476),l(-.046,.272),l(.132,.649),l(.116,.424),l(-.133,.848),l(-.256,0),l(-.466,-.154),l(-1.037,.963),l(-.433,.65),l(-.751,.608),l(.443,.381),l(.06,.396),l(.17,.296),l(-.685,.058),l(.452,.578),l(.009,.212),l(-.103,.227),l(-.547,.665),l(-.206,.396),l(-.127,.354),l(-.529,.594),l(-1.294,.61),l(-.607,.284),l(-.292,.198),l(-.194,-.311),l(.024,-.424),l(-.33,-.804),l(-.306,-.381),l(-.265,-.184),l(-.286,.029),l(-.503,.523),l(-.302,.029),l(-.328,-.508),l(-.313,-.197),l(-.437,-.112),l(-.387,-.451),l(-.342,-.154),l(-.35,.806),l(-.135,.198),l(-.381,.058),l(-.356,-.112),l(-.442,.128),l(-.318,.354),l(-.364,.071),l(-.059,-.551),l(.034,-.311),l(-.314,-1.03),l(-.336,.396),l(-1.42,.44),l(-.321,-.408),l(-.639,.015),l(-.281,.156),l(-.303,.029),l(-.058,-.649),l(-.022,-.65),l(-.267,-1.411),l(-.012,-.48),l(-.352,-.747),l(-.406,-.409),l(-.79,-.422),l(-.146,-.141),l(.555,-.354),l(-.531,-.38),l(-.258,-.296),l(.188,-.735),l(-.074,-.128),l(-.278,-.478),l(-.352,-.296),l(.065,-.466),l(-.125,-.593),l(.182,-.65),l(.133,-.353),l(.424,-.58),l(.303,-.806),l(.318,.028),l(.204,.11),l(.288,.792),l(.253,.295),l(1,.983),l(.304,.083),l(.446,.28),l(.928,-.416),l(.255,-.001),l(.526,.223),l(.543,.11),l(.399,-.172),l(.528,-.342),l(.403,-.525),l(.531,-.441),l(.479,-.074),l(.431,.11),l(.557,.251),l(.524,.223),l(.559,.152),l(.287,-.03),l(.467,-.356),l(.465,-.172),l(.864,-.175),l(.387,-.299),l(.928,-1.785),l(-.076,-.748),l(.218,-.34),l(.646,-.244),l(.22,-.383),l(-.106,-.988),l(.119,-.565),l(.381,-.638),l(.247,-.157),l(.464,-.017),l(.748,.081),l(.651,.081),l(.624,-.018),l(.446,.04),l(.753,.292),l(.182,.09),M(666.561,200.125),l(.012,-.049),l(.48,-1.188),l(.434,-.41),l(.289,-.142),l(.429,.338),l(.29,-.311),l(.162,-.325),l(.293,-.481),l(.496,-.058),l(.605,.14),l(.729,.535),l(.447,.027),l(.863,-.044),l(.478,.168),l(.749,.267),l(.577,-.227),l(1.853,.081),l(.72,-.128),l(.627,-.354),l(.211,-.283),l(-.156,-.268),l(.196,-.283),l(.388,-.241),l(.295,-.41),l(.289,-.057),l(.075,.24),l(-.073,.537),l(-.117,.311),l(-.081,.127),l(-.082,.127),l(-.969,1.259),l(-.416,.396),l(-.464,.1),l(-1.23,.229),l(-.495,-.069),l(-.591,-.422),l(-1.149,-.068),l(-1.151,.059),l(-.878,-.041),l(-1.039,.045),l(-.575,-.083),l(-.671,.029),l(-.415,.1),l(-.433,.368),l(-.259,.461),l(-.154,.274),l(-.187,.721),l(.068,.48),l(.263,.494),l(.194,.183),l(.403,.226),l(.259,.196),l(.221,.607),l(.179,.154),l(.226,.042),l(.815,.026),l(.249,-.269),l(.652,-.976),l(.385,.056),l(.307,.183),l(.496,.041),l(.363,-.227),l(.669,-.156),l(.62,-.143),l(.268,-.298),l(.271,-.057),l(.466,.196),l(.131,.212),l(-.083,.734),l(-.469,-.267),l(-.544,-.042),l(-.361,.298),l(-.389,.523),l(-.438,.425),l(-1.059,.439),l(-.214,.325),l(-.143,.029),l(-.241,-.042),l(-.468,-.126),l(-.03,.056),l(.025,.312),l(.212,.126),l(.676,.578),l(.467,.521),l(.854,1.24),l(-.097,.325),l(-.156,.679),l(.102,.409),l(.447,.535),l(.555,.438),l(.062,.226),l(-.045,.282),l(-.436,-.056),l(-.652,.059),l(-.412,.297),l(-.224,.692),l(-.498,-.026),l(-.461,-.183),l(-.107,-.17),l(.052,-.649),l(.204,-.58),l(-.978,-.845),l(-.417,-.31),l(-.174,-.269),l(.036,-.24),l(.284,-.396),l(.116,-.579),l(-.165,-.494),l(-.737,-.055),l(-.503,.213),l(-.494,.396),l(.16,.353),l(.143,.932),l(-.068,.509),l(-.236,1.145),l(.363,.903),l(-.01,.311),l(-.377,.636),l(-.019,.227),l(.275,.564),l(-.726,.171),l(-.513,.241),l(-.476,.071),l(-.245,-.324),l(-.16,-.522),l(.156,-.325),l(.181,-.466),l(.069,-.876),l(.06,-1.073),l(-.125,-.509),l(.029,-.339),l(-.213,-.395),l(-.311,-.127),l(-.391,.171),l(-.574,.029),l(.011,-.41),l(-.25,-1.284),l(.131,-.311),l(.491,-.524),l(.469,-.777),l(.161,-.48),l(-.089,-.918),l(-.006,-.254),l(.087,-.452),l(.339,-.721),l(.447,-.058),l(-.043,-.861),l(.254,-1.053),
+N(341.05,41.069),l(2.084,.272),l(.344,.361),l(-.869,.174),l(-.541,.139),l(-1.678,.106),l(-1.159,.037),l(-.689,.156),l(-.372,.224),l(-.308,.6),l(-.361,.376),l(1.05,.39),l(.971,.168),l(2.117,.064),l(.601,-.001),l(1.775,-.242),l(1.93,-.038),l(.866,.135),l(.933,.219),l(.417,.135),l(.284,-.018),l(1.001,-.002),l(1.277,.032),l(.615,.05),l(-1.277,.626),l(-1.583,.457),l(-1.976,.523),l(-.556,-.016),l(-.695,-.116),l(-.951,.671),l(-1.061,.503),l(-1.246,.452),l(-1.125,.296),l(-.211,.056),l(-2.212,.054),l(-.525,.134),l(-.502,.001),l(-.982,.201),l(-.665,.167),l(-.528,.051),l(-.946,-.413),l(-.375,.05),l(-.69,.913),l(-.958,.118),l(-.631,-.065),l(-.743,-.197),l(-.622,-.463),l(-.854,-.43),l(-.647,-.215),l(-.109,0),l(.008,.2),l(.707,1.043),l(-.192,.249),l(-.319,.017),l(-.69,.249),l(-.84,.249),l(-.573,.38),l(-1,.906),l(-.657,.657),l(-1.051,.851),l(-.776,.262),l(-1.034,.083),l(-1.023,-.275),l(-.148,.554),l(-.438,.569),l(-.783,.277),l(-.992,-.095),l(-.616,.05),l(-1.18,.439),l(.942,-1.723),l(-.121,.017),l(-.795,-.015),l(-1.055,-.177),l(.26,.423),l(-.026,.455),l(-.386,.407),l(-.794,.39),l(-1.16,.164),l(-.973,.002),l(-1.255,.083),l(.467,.403),l(.212,.403),l(-.09,.387),l(-.379,.097),l(-.321,-.063),l(-.47,.033),l(-1.792,-.157),l(.517,.32),l(.765,.462),l(.295,.351),l(-.01,.224),l(-.26,.176),l(-1.197,.034),l(-1.051,.129),l(.844,.413),l(.47,.126),l(.448,.222),l(.389,.333),l(-.554,.461),l(-.285,.111),l(-.599,-.094),l(-.478,.096),l(.345,.474),l(-.009,.127),L(308.501,60),l(-.486,-.125),l(-.583,-.062),l(.026,.158),l(.255,.457),l(-.101,.347),l(-.288,0),l(-.656,-.093),l(-.089,-.016),l(-.979,.112),l(-1.081,.018),l(.682,.487),l(1.108,.391),l(.331,.204),l(-.077,1.035),l(-.382,.938),l(-.427,.094),l(-.815,-.061),l(.489,.591),l(-.016,.498),l(.156,.233),l(-.068,.373),l(-.316,.062),l(-.495,-.03),l(-.771,.079),l(.762,.386),l(.427,.603),l(-.117,.447),l(-.287,.031),l(-.967,-.26),l(-1.052,-.508),l(-.498,.294),l(-.425,.602),l(-.635,-.599),l(.158,-.573),l(-.387,-.201),l(-1.124,-.184),l(-.577,-.309),l(.04,-.187),l(.253,-.249),l(.066,-.218),l(-.325,-.28),l(-.366,-.186),l(-.668,.359),l(-.276,.016),l(-.3,.141),l(-.444,-.046),l(-.98,.064),l(-.417,.017),l(-.571,.296),l(-.476,.28),l(-.426,-.403),l(-.104,-.357),l(-.222,-.217),l(-.513,-.233),l(-.817,-.232),l(-.772,-.389),l(-.517,-.781),l(.07,-.737),l(-.199,-.156),l(-.434,-.094),l(-.467,.048),l(-.97,-.266),l(-.108,-.094),l(-.138,-.236),l(.14,-.457),l(.459,-.395),l(.071,-.269),l(-.258,-.062),l(-.551,-.031),l(-.542,-.094),l(-.278,-.221),l(-.058,-.633),l(-.458,-.126),l(-.616,.049),l(-.589,-.57),l(.023,-.191),l(.198,-.254),l(.618,-.367),l(1.22,-.337),l(.405,-.304),l(.476,-.128),l(.051,-.383),l(-.277,-.287),l(-.473,.097),l(-.921,.082),l(-.493,.097),l(-.635,.416),l(-.538,.129),l(-.63,.304),l(-.339,-.318),l(.038,-.623),l(-.114,-.784),l(-.206,-.451),l(.015,-.355),l(-.243,-.323),l(-.504,.082),l(-.271,-.032),l(-.666,-.355),l(-.594,-.485),l(-.013,-.357),l(.842,-.538),l(.265,.019),l(-.556,-.189),l(-1.083,.064),l(-.103,-.284),l(.383,-.176),l(.677,-.03),l(.586,-.052),l(.456,-.087),l(.322,-.672),l(-1.208,-.047),l(-.572,.05),l(-.362,-.032),l(-.29,-.163),l(-.116,-.197),l(.223,-.28),l(.218,-.008),l(.18,-.16),l(.427,-.055),l(-.375,-.188),l(-.552,.073),l(-.22,-.248),l(.057,-.188),l(.073,-.132),l(.259,0),l(.309,-.099),l(.827,-.25),l(1.218,.081),l(.854,.163),l(.776,.032),l(.378,.131),l(.927,.146),l(1.027,.097),l(-.031,-.363),l(.299,-.545),l(-.298,-.182),l(-1.02,-.263),l(-1.356,-.312),l(-.903,-.164),l(-1.592,-.33),l(-.354,-.116),l(.336,-.35),l(.788,-.001),l(1.462,.363),l(1.034,.048),l(.463,-.067),l(.114,-.067),l(.088,-.6),l(.088,-.301),l(.595,-.034),l(.528,.116),l(.227,-.101),l(-.027,-.351),l(-.195,-.184),l(-.891,-.317),l(.162,-.437),l(.528,-.455),l(-.258,-.286),l(-1.21,-.167),l(-1.154,.002),l(-1.178,-.286),l(-1.649,-.49),l(-.78,-.101),l(-.903,-.05),l(-.76,-.34),l(-.811,-.477),l(.156,-.035),l(.323,-.155),l(.605,-.001),l(.572,-.019),l(2.085,.305),l(.716,.033),l(1.249,.306),l(1.451,.458),l(.729,.169),l(.056,-.307),l(-.311,-.426),l(-.86,-.546),l(-.172,-.446),l(-.603,-.446),l(-.485,-.051),l(-.677,-.24),l(.361,-.277),l(.542,-.139),L(285.644,41),l(-.704,-.241),l(-1.101,-.015),l(-.625,-.086),l(-1.132,-.327),l(-.88,.608),l(-.324,.156),l(-.274,.294),l(-.875,.243),l(-1.402,-.066),l(-1.031,-.343),l(-.306,-.242),l(-.027,-.294),l(.438,-.313),l(.293,-.645),l(-.152,-1.259),l(.582,-.054),l(.691,.192),l(.372,-.124),l(.151,-.334),l(-.383,-.369),l(-.933,-.545),l(-.452,-.141),l(-.946,-.796),l(-.895,-.925),l(-1.105,-1.289),l(-.578,-.485),l(-1.855,-.379),l(-.667,-.255),l(-.291,-.202),l(-.052,-.701),l(-.904,-.406),l(-.962,-.109),l(-1.589,-.165),l(-1.928,-.425),l(-1.903,-.333),l(-2.133,-.183),l(-.997,-.054),l(-1.632,-.035),l(-.785,.189),l(-1.043,.096),l(-.806,.188),l(-1.419,.152),l(-1.228,-.166),l(-1.46,-.296),l(.319,.747),l(-.051,.093),l(-1.051,-.017),l(-1.294,-.184),l(-3.168,-.611),l(1.538,-.566),l(.463,-.114),l(-.092,-.226),l(-.423,-.169),l(-1.067,-.017),l(-2.21,-.015),l(-.812,-.074),l(-.629,-.018),l(-1.238,-.434),l(-.87,-.208),l(.587,-.306),l(1.257,-.041),l(3.036,.147),l(2.025,.034),l(1.343,.017),l(2.117,-.157),l(1.055,-.212),l(.292,-.096),l(.054,-.345),l(-.627,-.287),l(-.82,-.21),l(-1.217,.156),l(-1.11,.252),l(-1.31,.021),l(-1.138,-.113),l(-.753,.078),l(-.879,.098),l(-.68,-.056),l(-.857,-.19),l(-.664,-.365),l(-.816,-.191),l(-.662,-.057),l(-.726,.059),l(-.486,-.076),l(-1.416,-.481),l(-.044,-.35),l(.36,-.45),l(.81,-.119),l(1.235,-.1),l(1.517,-.14),l(2.074,-.161),l(1.29,-.081),l(.951,-.396),l(1.089,-.259),l(.843,-.081),l(2.478,-.005),l(1.101,-.101),l(1.942,.036),l(.402,-.139),l(.31,-.199),l(.609,-.16),l(.202,-.658),l(.276,-.501),l(.116,-.101),l(-.89,-.228),l(-1.947,-.039),l(-1.155,.194),l(-.959,-.125),l(-1.243,-.383),l(.595,-.781),l(1.38,-.332),l(2.845,-.359),l(1.407,-.225),l(1.962,-.249),l(2.112,-.162),l(1.163,.087),l(1.213,-.07),l(1.319,-.07),l(.345,-.181),l(.011,-.226),l(-.357,-.753),l(-.022,-.208),l(.522,-.14),l(1.886,-.05),l(1.526,.205),l(2.141,.41),l(1.296,.226),l(.802,.181),l(.823,-.275),l(-1.657,-.525),l(-.697,-.509),l(.167,-.047),l(2.2,-.122),l(1.166,-.12),l(1.854,-.216),l(2.52,-.195),l(.73,.069),l(1.064,.116),l(.232,1.738),l(.913,-.162),l(.539,-.322),l(.432,-1),l(1.003,.021),l(2.004,.323),l(1.858,.414),l(1.529,.25),l(.205,-.3),l(-.644,-.3),l(-.816,-.537),l(-.894,-.4),l(.295,-.287),l(.742,.022),l(1.758,.02),l(1.136,.212),l(2.754,.373),l(1.284,.279),l(2.109,.322),l(1.878,.274),l(1.872,.204),l(.8,-.209),l(.816,-1.483),l(-.326,-.191),l(-1.292,-.334),l(-1.176,-.533),l(.708,-.247),l(2.404,-.005),l(2.962,-.328),l(1.329,-.077),l(1.527,.17),l(2.221,.488),l(1.567,.167),l(2.005,.142),l(.3,-.761),l(-.3,-.472),l(2.646,-.206),l(2.021,-.08),l(2.589,.095),l(1.989,.146),l(1.886,-.18),l(2.367,-.207),l(2.043,-.005),l(1.859,.223),l(1.825,-.055),l(1.315,.072),l(.619,.099),l(.55,-.102),l(1.946,.146),l(1.707,.046),l(1.673,.096),l(2.438,.761),l(1.368,.241),l(1.345,-.076),l(1.118,.168),l(2.594,.237),l(.445,.408),l(-.304,.12),l(-.492,.192),l(-1.683,.146),l(-2.303,.124),l(-1.152,.121),l(-1.233,.05),l(-1.469,-.068),l(-2.831,-.064),l(-2.22,-.066),l(-1.389,.168),l(-1.614,.027),l(-1.933,.027),l(-1.16,.026),l(-1.485,.168),l(-.444,.118),l(-1.322,.213),l(-.335,.464),l(.743,.251),l(2.551,-.281),l(1.367,-.072),l(3.912,.038),l(2.223,-.12),l(2.331,-.005),l(.997,-.025),l(.93,.067),l(1.77,.434),l(.671,.09),l(1.087,-.186),l(1.663,-.21),l(1.536,-.281),l(1.964,-.144),l(.59,.462),l(-.566,.482),l(-2.316,.639),l(-.973,.338),l(-1.281,.734),l(.12,.307),l(.319,.152),l(.796,-.089),l(.477,-.044),l(1.616,-.553),l(1.766,-.537),l(1.413,-.385),l(1.706,-.32),l(.775,-.207),l(1.662,-.163),l(1.618,.111),l(1.391,.065),l(1.497,-.3),l(.703,-.324),l(1.129,-.234),l(2.148,-.004),l(1.672,.112),l(1.097,.044),l(1.197,.136),l(1.135,.228),l(1.107,.112),l(.316,.25),l(-.181,.273),l(-1.97,.252),l(-1.491,.138),l(-1.245,.494),l(-.557,.289),l(-1.604,.355),l(-1.57,.548),l(-1.063,.089),l(-.918,-.042),l(-1.592,.047),l(-2.213,-.039),l(-1.491,.198),l(-.731,.217),l(-.495,.535),l(.166,.322),l(1.949,-.305),l(1.581,-.046),l(1.856,.101),l(.003,.42),l(-.743,.241),l(-2.388,.124),l(-.463,.14),l(-.213,.199),l(-.156,.595),l(-.471,.71),l(-.678,.158),l(-1.06,-.077),l(-.742,.041),l(-.837,.9),l(-.987,1.087),l(-.15,.347),l(.454,.307),l(.403,.095),l(.602,-.481),l(.743,-.368),l(.856,-.041),l(2.345,.266),l(.353,.096),l(.262,.288),l(-.059,.211),l(-1.234,-.074),l(-.673,-.018),l(-.512,.097),l(-.136,.191),l(.29,.286),l(1.756,.188),l(.557,.132),l(1.802,-.137),l(.526,.208),l(.214,.323),l(-.049,.436),l(-.256,.133),l(-1.835,-.186),l(-1.653,-.054),l(-.781,-.074),l(-1.295,.078),l(-1.382,.475),l(-.823,-.13),l(-.49,-.15),l(-1.06,.04),l(-.283,.377),l(1.393,.599),l(1.187,.222),l(1.298,.128),l(1.665,.072),l(.696,.148),l(.551,.482),l(.272,.444),l(.014,.554),l(-.434,.405),l(-.384,.074),l(-1.292,-.181),l(-.82,-.109),l(-.372,.111),l(.023,.55),l(.118,.236),l(.426,.162),l(.618,.089),l(.723,.215),l(.914,.142),l(.752,.16),l(.383,.376),l(-.338,.233),l(-.832,.145),l(-.647,.126),l(-1.747,-.032),l(-1.176,-.087),l(-1.624,-.086),l(-.592,.448),l(.551,.195),l(1.396,.051),l(1.052,.158),l(.724,.248),l(.088,.319),l(-.035,.549),l(-.13,-.005),l(-1.092,-.045),l(-1.247,.108),l(-.596,.266),l(-1.246,.02),l(-1.225,-.139),l(-1.497,-.404),l(-.922,-.478),l(-.373,-.07),l(-1.094,.286),L(345,36.811),l(-1.084,.09),l(-.589,.178),l(-1.451,-.033),l(-.913,-.087),l(-.969,.143),l(-.395,.125),l(-.174,.283),l(.006,.141),l(.354,.527),l(.71,.245),l(1.284,.05),l(1.515,.26),l(1.567,-.056),l(1.323,.54),l(.758,.226),l(.482,.226),l(1.196,.329),l(1.252,.38),l(.376,.276),l(.483,.898),l(.892,-.208),l(.278,-.139),l(.397,.207),l(.298,.43),l(.071,.344),l(.198,1.164),l(-.169,.205),l(-.371,.12),l(-.541,-.101),l(-.546,-.119),l(-.917,.002),l(-1.041,.036),l(-1.488,-.27),l(-.637,-.409),l(-.415,-.634),l(-.354,-.274),l(-1.17,-.566),l(-.84,-.292),l(-.748,-.137),l(-1.095,-.084),l(-.521,.14),l(-.962,.105),M(351.365,40.026),l(-1.527,-.537),l(-.96,-.225),l(-.712,-.156),l(-.159,-.069),l(-.314,-.419),l(1.483,-.038),l(.893,.139),l(1.064,.26),l(.819,.296),l(.162,.488),l(-.215,.209),l(-.533,.053),M(281.574,46.135),l(-.568,-.133),l(-.707,-.318),l(-.801,-.183),l(-.197,-.101),l(-.25,-.218),l(-.08,-.844),l(.287,-.34),l(.368,-.018),l(.646,.135),l(1.157,.066),l(1.287,.27),l(.748,.269),l(.595,.1),l(.777,.217),l(.603,.335),l(-.144,.202),l(-.112,.034),l(-.543,.051),l(-.774,.035),l(-.77,.186),l(-1.009,.153),l(-.511,.102),
+N(105.98,81.688),l(-.952,-.826),l(-.198,-.342),l(-.024,-.476),l(.095,-.104),l(.408,.044),l(.312,-.045),l(.781,.177),l(.658,-.076),l(.28,.119),l(.138,.163),l(-.234,.224),l(-.173,.565),l(-.028,.312),l(-.581,.075),l(-.483,.19),M(125.24,92.375),l(-1.312,-.288),l(-1.345,-.434),l(-.218,-.174),l(.061,-.189),l(.376,-.466),l(-1.023,.002),l(-.413,.248),l(-.299,-.072),l(-.416,-.188),l(.166,-.452),l(-.487,-.334),l(-.269,-.014),l(-.735,-.086),l(-.226,-.262),l(.317,-.292),l(-.976,-.524),l(-.556,.118),l(-.386,-.102),l(-.852,-.511),l(-1.277,-.863),l(-.219,-.235),l(.02,-.117),l(.962,-.12),l(.337,.043),l(1.979,.598),l(.981,.204),l(1.772,.202),l(.385,.263),l(.618,.526),l(.426,.642),l(.433,.422),l(.362,.189),l(1.587,.536),l(.316,.203),l(.48,.756),l(.116,.407),l(-.279,.349),l(-.407,.016),M(271.379,92.089),l(-1.202,-.23),l(.641,-.743),l(.358,-.161),l(.279,.058),l(.292,0),l(.355,-.263),l(-.697,-.653),l(.079,-.219),L(272,89.003),l(1.121,-1.35),l(1.454,-1.31),l(.725,-.442),l(.496,-.192),l(1.315,-.194),l(.198,.073),l(.11,.221),l(-.299,.221),l(-.582,.03),l(-.242,.133),l(.349,.44),l(-.755,.78),l(-1.226,1.438),l(-.271,.526),l(.113,.291),l(.11,0),l(.428,-.176),l(.483,-.555),l(.458,-.191),l(1.115,.305),l(-.896,.687),l(.261,.203),l(.229,.072),l(1.423,.565),l(.758,-.03),l(.325,-.408),l(.309,-.059),l(.718,.057),l(.826,.202),l(.616,.231),l(-.297,.292),l(-.373,.233),l(-.708,.467),l(.339,.333),l(.477,.362),l(.26,.014),l(.417,-.161),l(.464,-.132),l(.278,.116),l(.02,.16),l(-.254,.262),l(-.404,.248),l(-.892,.104),L(280.84,93),l(.273,.362),l(.124,.405),l(.28,.231),l(.183,-.203),l(.309,-.262),l(.527,.159),l(-.099,.449),l(.149,.275),l(.716,.028),l(.085,-.015),l(.015,.203),l(-.168,.304),l(-.25,.652),l(-.34,.651),l(-.222,-.072),l(-.71,-.128),l(-.301,-.144),l(-.042,-.651),l(-.601,.406),l(-.374,.015),l(-.095,-.274),l(.497,-.652),l(.011,-.333),l(-.421,-.478),l(-.279,-.072),l(-.388,.392),l(-.423,.291),l(-.365,.146),l(-.435,.204),l(-.552,.536),l(-.496,.334),l(-.881,-.042),l(-.222,-.217),l(.165,-.145),l(1.229,-.408),l(.466,-.522),l(.632,-.363),l(-.699,-.129),l(-.601,-.057),l(-.322,.464),l(-.412,.015),l(-.13,-.159),l(.04,-.493),l(-.757,.016),l(-.148,.29),l(-.41,.218),l(-1.052,.045),l(-.709,-.057),l(-1.139,-.186),l(-1.012,-.085),l(-1.355,.061),l(-1.014,.147),l(-.145,-.188),l(-.215,-.463),l(.187,-.175),l(.561,-.334),l(.734,-.408),l(.502,-.161),l(.636,-.335),M(265.435,98.655),l(-.469,-.057),l(-.497,-.273),l(-.356,-.562),l(.062,-.635),l(.742,-.738),l(.932,-1.043),l(.816,.432),l(-.375,.435),l(-.112,.462),l(-.233,.333),l(-.262,.116),l(-.58,.319),l(-.244,.448),l(.522,.244),l(.168,-.029),l(.279,-.26),l(.42,-.362),l(.617,-.319),l(.309,.057),l(.495,.461),l(-.177,.347),l(-.246,.159),l(-1.134,.42),l(-.682,.044),M(211.34,59.677),l(-.68,-.046),l(.068,-.872),l(-.375,-.333),l(-.958,.161),l(-2.375,.29),l(.107,-.461),l(.56,-.303),l(1.644,-.561),l(-.302,-.478),l(-.102,-.415),l(.106,-.417),l(.398,-.835),l(.434,-.566),l(.254,-.648),l(.331,-.471),l(1.11,.566),l(-.312,.518),l(.791,.386),l(.527,.047),l(.402,-.469),l(.67,.112),l(.806,.289),l(.917,.514),l(.582,.255),l(2.168,.492),l(.442,.271),l(.176,.255),l(-.09,.925),l(.539,.047),l(.57,-.065),l(.934,.046),l(.701,.142),l(1.019,.427),l(-.419,.096),l(-.269,.127),l(-.46,.271),l(-.949,-.046),l(-.623,-.125),l(-1.328,-.125),l(-.438,-.126),L(217.259,58),l(-.528,-.301),l(-1.017,-.237),l(-.528,.017),l(-.203,.271),l(.174,.588),l(-.126,.096),l(-1.314,.161),l(-.673,.493),l(-.588,.302),l(-1.116,.287),M(200.125,19.1),l(-.862,-.015),l(-1.085,-.195),l(-.308,-.664),l(.819,-.304),l(.77,-.072),l(.666,-.024),l(3.475,-.125),l(1.263,-.12),l(1.374,-.026),l(1.714,.324),l(.397,-.094),l(.397,-.377),l(1.303,-.287),l(1.759,-.099),l(1.975,.209),l(.746,-.001),l(2.562,.137),l(2.621,.324),l(1.424,.09),l(1.461,.161),l(.448,-.165),l(-1.433,-.42),l(-1.67,-.352),l(-.816,-.429),l(.293,-.242),l(1.361,-.148),l(1.101,-.246),l(1.431,-.101),l(2.382,-.201),l(1.666,.119),l(1.944,.191),l(1.009,.265),l(1.19,.456),l(.354,.047),l(.273,-.362),l(-.959,-.508),l(-.828,-.292),l(.499,-.248),l(1.45,.121),l(1.832,.168),l(1.653,.07),l(1.639,.46),l(.378,.023),l(.062,-.195),l(-.301,-.539),l(1.781,-.004),l(1.408,.046),l(.832,.269),l(.831,.34),l(.618,-.001),l(-.044,-.268),l(-.331,-.467),l(1.075,-.077),l(3.691,.386),l(2.726,.288),l(1.937,-.077),l(2.987,.018),l(.967,.047),l(.757,.12),l(.126,0),l(1.419,.094),l(1.089,.191),l(.744,.095),l(1.685,.044),l(1.357,.357),l(-.385,.358),l(-1.237,.121),l(-1.206,.356),l(-1.849,.191),l(-.978,-.045),l(-2.191,-.159),l(-2.284,.005),l(-.776,.142),l(-1.915,.168),l(-.597,.465),l(.75,.366),l(.761,.044),l(1.03,-.048),l(1.705,-.279),l(.79,.021),l(.808,.434),l(-.168,.114),l(-1.246,.139),l(-1.38,.207),l(-1.174,.295),l(-2.098,.518),l(-1.316,.224),l(-1.19,.355),l(-.924,.286),l(-2.252,.005),l(.65,.906),l(-.449,.193),l(-2.297,.455),l(-.768,-.019),l(-1.587,-.037),l(-1.462,-.218),l(-2.386,-.164),l(-.66,.33),l(2.591,.695),l(-.662,.141),l(-.967,-.038),l(-1.297,.022),l(-1.068,.022),l(-2.58,-.214),l(-2.009,.063),l(-.134,.66),l(1.257,-.105),l(1.065,.018),l(2.312,.292),l(.557,.157),l(.12,.67),l(-.33,.197),l(-1.031,.12),l(-.515,.705),l(-1.073,.021),l(-.448,-.058),l(-.402,.176),l(.297,.253),l(.759,.25),l(-.328,.136),l(-1.615,.08),l(-.867,-.037),l(-1.71,-.171),l(-.422,.078),l(.41,.791),l(-.08,.231),l(-.649,.289),l(-.767,.155),l(-1.52,-.112),l(-2.039,-.111),l(-1.43,-.227),l(-1.008,.079),l(-1.219,.5),l(1.031,.112),l(.368,.057),l(2.154,.11),l(1.759,.13),l(1.534,.168),l(2.001,.034),l(.66,.34),l(.045,.359),l(-.907,.398),l(-2.685,.268),l(-.927,.115),l(-1.054,.227),l(-1.115,.077),l(-.467,-.28),l(-.797,-.638),l(-.56,.039),l(-.631,.001),l(-1.453,-.318),l(-.001,.17),l(.331,.508),l(-1.477,-.016),l(-1.5,-.129),l(-.875,-.319),l(-1.033,-.471),l(-.388,.058),l(.527,.717),l(-.24,.17),l(-.821,.133),l(-1.72,-.109),l(-2.276,-.033),l(-.972,-.073),l(-1.382,-.394),l(-.642,-.131),l(-.282,.453),l(-.619,.152),l(-1.843,-.316),l(.161,-.586),l(.219,-.228),l(1.525,-.117),l(.61,-.249),l(.961,-.173),l(1.179,.036),l(.499,-.172),l(-1.073,-.4),l(-1.043,-.651),l(.052,-.154),l(.479,-.117),l(1.316,.036),l(1.743,.093),l(.888,.21),l(1.108,.517),l(1.35,.323),l(1.085,.093),l(1.667,-.022),l(.829,-.136),l(.086,-.268),l(.514,-.304),l(-3.019,.001),l(-1.025,-.171),l(-.156,-.85),l(.211,-.154),l(-1.74,-.153),l(-1.963,-.152),l(-.274,0),l(-.631,.114),l(.204,-.758),l(1.159,-.551),l(1.104,-.16),l(1.837,-.003),l(1.164,.037),l(1.37,.076),l(2.023,.311),l(1.342,.115),l(.486,-.158),l(1.132,-.041),l(-3.399,-.802),l(-1.742,-.313),l(-3.555,-1.27),l(-.406,.242),l(-1.398,-.878),l(.025,-.258),l(.313,-.108),l(1.747,.104),l(1.905,-.004),l(2.019,.06),l(1.6,.382),l(2.535,.784),l(1.448,-.043),l(.833,.095),l(-1.387,-.555),l(-2.015,-.317),l(1.208,-.743),l(1.456,-.329),l(1.731,-.025),l(1.529,-.222),l(2.042,-.07),l(1.157,-.112),l(1.414,-.051),l(-1.778,-.479),l(-1.425,-.153),l(-2.501,.027),l(-1.243,.248),l(-1.305,.158),l(-1.425,.202),l(-1.447,.047),l(-.586,.067),l(-1.532,-.438),l(-.214,.111),l(-.543,.156),l(-2.16,-.018),l(-1.58,.365),l(.311,-.828),l(.98,-.292),l(.007,-.202),l(-.606,-.247),l(-1.375,-.156),l(-1.39,.003),l(-4.189,.505),l(-2.031,.672),l(-.408,-.11),l(-.569,-.251),l(.395,-.133),l(.678,-.023),l(-.117,-.316),l(-.698,-.398),l(-1.216,-.056),l(-.216,-.003),M(200.125,20.844),l(.899,-.096),l(.832,.196),l(.339,.5),l(.511,.495),l(.427,.063),l(1.141,.041),l(-.081,-.236),l(.056,-.411),l(.438,-.109),l(.718,.194),l(.718,.322),l(.374,.3),l(-.066,.171),l(.056,.826),l(.764,.442),l(.953,-.017),l(1.276,-.074),l(1.646,.504),l(-1.123,-.264),l(-1.528,.34),l(-1.599,.221),l(-.83,.377),l(-.287,.197),l(-.265,.315),l(-.448,.021),l(-1.493,-.41),l(-.656,.335),l(.465,.43),l(-.029,.235),l(-.412,.196),l(-.392,.04),l(-1.086,-.31),l(-.944,-.311),l(-.26,.645),l(-.117,.068),l(-.083,.049),l(-.888,.041),l(-1.74,-.094),l(-1.458,-.153),l(-.888,-.154),l(-.494,.021),l(-3.115,-1.31),l(-.191,-.276),l(1.971,-.241),l(3.32,.093),l(.889,.097),l(1.573,.1),l(-2.485,-.693),l(-3.019,-.213),l(-1.103,.122),l(-1.43,-.017),l(-.597,.18),l(-1.008,.022),l(-.606,-.198),l(-1.066,-.517),l(-1.425,-.479),l(-.341,-.355),l(1.971,.124),l(2.278,-.175),l(-1.255,-.249),l(-.756,-.351),l(.38,-.305),l(.729,-.132),l(.769,-.023),l(.921,.042),l(.156,-.219),l(-1.799,-.793),l(1.053,-.114),l(1.213,-.182),l(1.13,.087),l(.584,-.046),l(.145,-.18),l(-.514,-.475),l(1.362,.134),l(.941,.066),l(.83,.202),l(1.589,.869),l(.653,.264),l(.772,.24),l(.674,-.001),l(.36,-.039),M(179.067,27.216),l(-1.156,-.056),l(-.604,-.173),l(-.926,-.638),l(-.621,-.193),l(-3.102,-.091),l(-1.487,.081),l(-.622,.001),l(-1.444,-.056),l(-.767,-.154),l(-1.019,-.37),l(-.161,-.234),l(.335,-.138),l(.802,-.001),l(1.687,.232),l(.867,-.021),l(-.031,-.235),l(-.252,-.275),l(-1.344,-.49),l(-.579,-.098),l(-1.075,-.077),l(-1.392,-.196),l(.065,-.397),l(2.246,-.124),l(2.392,.155),l(.77,.376),l(.999,.453),l(1.979,.193),l(2.189,.114),l(1.178,.233),l(.604,.254),l(1.123,.721),l(.581,.446),l(.168,.426),l(-.481,.194),l(-.919,.137),M(185.907,26.758),l(-1.078,-.037),L(184,26.529),l(-1.029,-.484),l(-1.144,-.76),l(-.03,-.216),l(.239,-.099),l(2.296,-.044),l(1.816,.311),l(3.101,.542),l(-.047,.351),l(-.254,.331),l(-.436,.04),l(-1.563,.177),l(-1.043,.08),M(156.886,26.865),l(-1.573,.646),l(-.558,.27),l(-1.85,.042),l(-1.019,.079),l(-1.898,-.15),l(-.577,-.114),l(-.302,-.423),l(.334,-.291),l(1.365,-.177),l(.899,.056),l(2.351,-.102),l(.496,0),l(2.331,.163),M(132.902,31.305),l(-.53,-.186),l(-.95,-.466),l(-.424,-.112),l(-.33,.057),l(-.56,.207),l(-1.269,.059),l(-.786,-.279),l(-.283,-.319),l(.23,-.264),l(1.13,-.097),l(.503,-.133),l(.771,-.134),l(.977,-.399),l(.848,-.211),l(.726,-.172),l(.548,-.344),l(1.083,-.231),l(1.277,-.079),l(2.532,-.158),l(1.68,.016),l(.888,-.29),l(1.038,-.079),l(1.503,.438),l(-.756,.097),l(-.852,.231),l(-.22,.268),l(.12,.266),l(.469,.474),l(-.777,.001),l(-.912,.115),l(-.918,.662),l(-1,-.017),l(-.867,-.981),l(-.694,-.15),l(-.379,.02),l(-.229,.285),l(-.588,.342),l(-.63,.623),l(-.595,.151),l(-.284,.375),l(-.705,.356),l(-.787,.058),M(191.827,30.313),l(-1.266,-.054),l(-2.278,-.165),l(-.426,.058),l(-.332,-.094),l(-.896,-.489),l(-1.185,-.414),l(.192,-.229),l(2.433,-.042),l(1.542,.263),l(1.472,.054),l(.171,0),l(.89,.358),l(-.179,.246),l(.123,.32),l(-.263,.188),M(144.688,31.739),l(-2.222,-.395),l(-.325,-.674),l(.503,-.057),l(.595,-.17),l(.945,-.096),l(.953,-.133),l(1.279,-.059),l(.522,.187),l(.65,.374),l(.659,.186),l(1.55,-.209),l(.617,.149),l(1.624,.762),l(1.016,.351),l(.897,.036),l(.96,-.058),l(1.418,.09),l(.591,-.02),l(1.116,-.169),l(.092,-.297),l(-.557,-.559),l(-.941,-.391),l(-1.347,-.354),l(.96,-.322),l(.524,-.379),l(.569,-.152),l(1.097,-.116),l(.507,.17),l(.773,.678),l(-.017,.413),l(.518,.654),l(.565,.111),l(.9,.036),l(1.805,.406),l(-.334,-.465),l(.151,-.28),l(.409,-.076),l(1.495,.24),l(.932,.39),l(-.292,.409),l(.039,.5),l(-.358,.461),l(-.573,.277),l(-.755,.111),l(-.782,.001),l(-1.682,.095),l(-1.156,-.071),l(-1.757,-.18),l(-.622,-.017),l(-1.129,.277),l(-1.132,.202),l(-.76,.182),l(-.977,.254),l(-1.625,.292),l(-1.338,.2),L(149.23,34.5),l(-.748,-.07),l(-1.445,-.286),l(-.276,-.378),l(.648,-.128),l(1.219,-.038),l(.738,-.146),l(.852,-.075),l(1.166,-.057),l(.622,.017),l(1.09,-.149),l(.483,-.553),l(-2.768,-.087),l(-.925,-.054),l(-1.564,.28),l(-1.625,.168),l(-1.292,.04),l(-.795,.093),l(-1.681,-.347),l(-.479,.167),l(-.92,.075),l(-.979,-.127),l(-.854,-.33),l(.023,-.111),l(.863,-.427),l(1.098,-.058),l(2.047,-.022),l(.96,-.159),M(178.479,33.234),l(-.984,-.219),l(-.193,-.294),l(.764,-.389),l(.433,-.112),l(.088,-.167),l(-.447,-.333),l(-1.161,-.054),l(-2.13,.227),l(-.939,.076),l(-.331,.019),l(-.854,-.276),l(.039,-.335),l(.739,-.02),l(.542,-.244),l(.587,-.057),l(-.466,-.598),l(.176,-.245),l(.132,-.226),l(.49,.018),l(.859,.224),l(1.942,.746),l(.426,.186),l(.46,-.094),l(-.101,-.243),l(-.959,-.486),l(-.371,-.131),l(-.407,-.357),l(.436,-.095),l(.956,-.059),l(.713,.131),l(1.033,.262),l(.572,.168),l(.27,.018),l(.162,-.452),l(.478,-.133),l(.73,.112),l(.717,.168),l(.327,.168),l(.367,.75),l(-.034,.616),l(-.247,.242),l(-.831,.335),l(-.017,.352),l(.35,.625),l(-.361,.147),l(-1.648,-.089),l(-.862,.112),l(-1.446,.003),M(200.125,30.572),l(-.895,.045),l(-.853,.17),l(-.37,.467),l(1.133,.054),l(.984,-.038),l(.046,-.001),l(.847,.11),l(.463,.129),l(.498,.463),l(.727,.424),l(.621,.091),l(.213,-.074),l(.043,-.314),l(.286,-.056),l(1.075,-.002),l(.883,-.187),l(.766,.11),l(.835,.239),l(.665,.257),l(.976,.053),l(.775,-.463),l(1.393,-.281),l(1.704,-.114),l(1.951,-.246),l(1.533,.053),l(2.59,.014),l(.381,.037),l(.79,.314),l(.911,.239),l(1.418,.146),l(.653,.128),l(.21,.037),l(.361,.166),l(.181,.257),l(-.4,.148),l(-1.833,.407),l(-.135,.255),l(.469,.666),l(-2.486,.076),l(-.592,.02),l(-.651,.091),l(-.768,-.053),l(-.846,-.16),l(-.405,-.125),l(-.306,-.667),l(-.833,-.218),l(-.366,.129),l(.072,.723),l(-.536,.127),l(-.747,-.053),l(-.91,.109),l(-.728,-.017),l(-.495,.001),l(-1.342,-.213),l(-.593,-.197),l(-.495,-.017),l(-.209,.433),l(-1.801,.111),l(-.831,.074),l(-1.453,-.069),l(-.404,-.251),l(-.144,-.686),l(-1.237,.129),l(-.389,.181),l(-.326,.325),l(-.955,.2),l(-1.011,-.034),l(-.112,-.027),l(-.704,-.169),l(-1.186,-.575),l(-.675,.489),l(-1.131,-.07),l(-.666,-.688),l(-.442,-.717),l(.587,-.481),l(.019,-.371),l(-.292,-.316),l(-1.249,-.651),l(-.617,-.299),l(-.047,-.338),l(.636,-.133),l(1.226,-.078),l(2.472,-.023),l(.763,.093),l(1.118,.261),l(.188,.04),l(.872,.184),l(-.613,.189),l(-.259,.013),M(128.19,41.985),l(-.926,-.016),l(-1.059,-.102),l(-.362,-.466),l(-.549,-.467),l(-.432,-.259),l(-1.123,-.363),l(-1.36,-.067),l(-.951,-.138),l(-.469,-.19),l(-.168,-.174),l(.537,-.106),l(.589,-.298),l(.481,-.211),l(.08,-.386),l(-.437,-.809),l(.552,-.001),l(.468,-.177),l(.307,-.372),l(1.104,-.533),l(.526,-.588),l(-.121,-.32),l(-.271,-.16),l(-1.229,-.677),l(-.375,-.448),l(.869,-.001),l(.823,-.056),l(1.455,.051),l(.97,.016),l(1.515,-.092),l(1.284,-.146),l(1.242,-.074),l(.495,.125),l(3.242,.801),l(.918,.088),l(.708,-.055),l(1.316,-.127),l(1.223,.016),l(.771,.07),l(1.35,.373),l(2.389,.815),l(-.242,.143),l(-.432,.036),l(-.26,.072),l(-1.609,.322),l(-1.073,.144),l(-1.829,.428),l(-1.069,.319),l(-1.604,.725),l(-1.025,.563),l(-.549,.089),l(-.974,.124),l(.066,.924),l(-.271,.504),l(-.662,.278),l(-1.215,.124),l(-1.213,-.067),l(-.521,.485),l(-.898,.312),M(190.483,39.666),l(-1.146,-.208),l(-.146,-.524),l(-.941,-.806),l(-.207,-.582),l(.058,-.389),l(.27,-.657),l(.377,-.321),l(1.256,.033),l(-.089,-.16),l(-.416,-.266),l(-.185,-.286),l(.211,-.09),l(.234,-.072),l(2.154,-.058),l(1.215,.087),l(1.464,.248),l(1.282,.051),l(1.316,-.146),l(1.051,.016),l(.694,.105),l(.639,.213),l(-.007,.089),l(-.224,.179),l(-.824,.428),l(-.874,.746),l(-1.513,.92),l(-1.386,.073),l(-2.379,-.154),l(-1.269,.055),l(1.392,.717),l(-.188,.315),l(-.855,.369),l(-.964,.072),M(181.204,41.523),l(-.491,-.085),l(-1.101,-.552),l(-.952,-.641),l(-1.014,-.468),l(-.978,-.225),l(-1.438,-.12),l(-.55,-.174),l(-2.255,-1.066),l(.866,-.654),l(.653,.14),l(1.032,.474),l(1.063,.227),l(.46,.052),l(.615,-.283),l(.908,-.619),l(.415,-.036),l(.018,-.212),l(-1.062,-.565),l(-1.068,-.424),l(-.177,-.231),l(.132,-.107),l(1.683,.086),l(.711,-.215),l(.42,0),l(.996,.39),l(.56,.035),l(.58,-.055),l(.435,-.25),l(1.232,-.127),l(1.354,.069),l(.912,.23),l(-.324,.268),l(-.58,.125),l(-.323,.338),l(-1.55,.375),l(-.392,.16),l(-.069,.194),l(.253,.247),l(.506,.105),l(.692,-.089),l(1.08,.174),l(.868,.245),l(.391,.017),l(.564,.262),l(.186,.438),l(-.681,.352),l(-.156,.35),l(-.271,.68),l(-.457,.366),l(-.508,.14),l(-.658,.019),l(-.582,-.103),l(-.773,-.346),l(-.653,-.103),l(.013,.208),l(1.054,.553),l(-.817,.399),l(-.77,.036),M(243.524,60.394),l(-.234,-.208),l(-1.199,-.235),l(-.673,-.331),l(-.154,-.269),l(.346,-.064),l(.616,-.461),l(-1.378,-.521),l(-1.132,-.125),l(-.76,-.349),l(-.929,-.731),l(-.035,-.511),l(-1.115,-.062),l(-1.311,-.366),l(-.675,-.031),l(.284,.767),l(-.155,.096),l(-.409,-.015),l(-1.704,-.332),l(-.309,.033),l(-.325,.304),l(-.441,.288),l(-1.312,.082),l(-1.349,-.173),l(-1.343,-.189),l(-.813,-.254),l(-.052,-.319),l(.196,-.4),l(.382,-.354),l(1.066,-.163),l(.192,-.178),l(-.128,-.516),l(.206,-.033),l(1.357,.11),l(1.408,.175),l(.517,.144),l(.962,.626),l(.051,-.386),l(-.154,-.193),l(.077,-.194),l(.585,-.033),l(.977,-.099),l(.652,-.163),l(.649,-.114),l(.515,.063),l(.785,.031),l(.166,-.275),l(-1.138,-.825),l(-.773,-.356),l(-.119,-.228),l(.167,-.163),l(.586,-.066),l(.72,-.246),l(1.409,-.591),l(.361,-.541),l(.771,-.46),l(.493,-.379),l(-.109,-.593),l(-.899,-.841),l(-.407,-.496),l(-.541,-.364),l(-.414,.001),l(-1.258,-.33),l(-1.041,-.481),l(-.244,-.467),l(-.527,-.384),l(-.442,.202),l(-.551,.202),l(-.825,-.015),l(-.293,.117),l(-.62,.018),l(-1.255,.169),l(-.214,-.667),l(1.032,-.052),l(1.23,-.103),l(.163,-.269),l(-1.604,-.618),l(-1.552,-.67),l(-.879,-.015),l(-.567,-.185),l(-.169,-.542),l(-.677,-.339),l(-.45,-.05),l(-.918,-.306),l(-.687,-.341),l(-.385,-.119),l(-.611,.155),l(-.81,-.187),l(-1.177,-.238),l(-.489,-.085),l(-.379,.138),l(.529,.307),l(.453,.05),l(2.838,.712),l(.438,.271),l(.069,.306),l(-.505,.221),l(-.669,.069),l(-.541,-.033),l(-.757,-.049),l(-.818,-.252),l(-1.153,-.27),l(-.667,-.066),l(-.323,.17),l(.044,.204),l(.426,.236),l(.259,.438),l(-1.703,-.553),l(-.47,-.05),l(-.396,.119),l(-.63,.153),l(-.767,-.218),l(-.693,-.117),l(-.859,.12),l(-1.474,-.184),l(-1.995,-.167),l(-1.321,.037),l(-1.146,-.032),l(-.862,-.186),l(-.003,-.597),l(-.363,-.153),l(-.904,-.049),l(-.396,.342),l(-.623,.086),l(-1.214,-.049),l(-1.076,-.168),l(-1.303,-.477),l(-.415,-.376),l(.123,-.275),l(.868,-.07),l(1.131,.067),l(1.212,.101),l(.879,-.019),l(.312,-.19),l(-.934,-.463),l(-.8,-.275),l(-.905,-.102),l(-1.106,-.119),l(-.752,.036),l(-.539,-.017),l(-1.249,-.223),l(.114,-.416),l(.292,-.557),l(.34,-.14),l(.646,-.054),l(.081,-.227),l(-1.082,-.4),l(-.044,-.175),l(.449,-.79),l(1.197,-.919),l(.565,-.284),l(.918,-.321),l(.74,-.374),l(.423,-.037),l(.37,-.178),l(.698,-.001),l(.481,-.125),l(.71,-.09),l(1.436,-.109),l(1.348,.033),l(.857,.194),l(-.92,.393),l(-.815,.48),l(-1.394,.639),l(-.43,.529),l(.169,.369),l(1.256,.541),l(-.444,.298),l(-.076,.402),l(.257,.313),l(.862,.554),l(1.559,.621),l(-.096,.121),l(-1.272,.331),l(-.072,.31),l(.959,.033),l(1.504,.101),l(.654,-.639),l(-.103,-.415),l(-.343,-.277),l(-.724,-.103),l(-.422,-.138),l(-.884,-.538),l(.101,-.157),l(.506,-.245),l(.473,-.193),l(1.001,.12),l(.837,-.071),l(-1.204,-.47),l(-.703,-.034),l(-.793,-.279),l(-.056,-.193),l(.053,-.545),l(.886,-.319),l(1.207,.086),l(1.509,-.056),l(-.939,-.281),l(-1.233,-.351),l(.793,-.303),l(1.288,-.198),l(1.044,-.126),l(1.688,-.323),l(1.114,.016),l(.642,.052),l(.833,.141),l(.782,.478),l(1.536,.97),l(.058,.141),l(-.583,.687),l(-.709,.632),l(.038,.733),l(.364,.086),l(.65,.033),l(1.088,-.315),l(.284,-.455),l(.595,-.088),l(.791,.034),l(.454,.174),l(-.006,.262),l(.16,.47),l(.875,.189),l(.196,-.122),l(-.204,-.854),l(.218,-.123),l(.456,-.474),l(1.038,-.265),l(.98,-.054),l(.748,.034),l(.98,.174),l(1.172,.138),l(1.151,-.09),l(.688,.139),l(.327,.262),l(.621,.331),l(.574,.191),L(235.438,40),l(0,.191),l(-.531,.088),l(-.484,.279),l(-.818,.262),l(-.148,.225),l(.45,.259),l(.427,.068),l(.897,-.417),l(.652,-.174),l(.502,.051),l(.476,.242),l(.365,.466),l(.516,.413),l(.342,-.242),l(1.304,-.798),l(1.935,.256),l(.915,.361),l(-.051,.069),l(-.638,.346),l(-.708,.517),l(1.167,-.054),l(.455,-.173),l(1.078,-.105),l(.033,.704),l(.797,.324),l(.523,-.069),l(.831,-.207),l(1.316,-.088),l(.816,.221),l(.566,.273),l(-.162,.154),l(-.461,.223),l(-1.87,.43),l(-.238,.272),l(.523,.253),l(.456,-.068),l(.747,-.171),l(1.235,-.122),l(.406,-.29),l(.361,-.103),l(.479,.067),l(.51,.187),l(.544,.339),l(.636,.522),l(-1.019,.002),l(-1.2,.053),l(-.424,.135),l(.059,.269),l(.372,.134),l(1.333,.065),l(.938,.183),l(.543,.217),l(.233,.301),l(-.37,.034),l(-.748,.001),l(-1.011,-.082),l(-.875,-.216),l(-.824,-.065),l(-.316,.185),l(1.23,.583),l(-.216,.201),l(-1.552,.12),l(.245,.283),l(.437,.166),l(.551,.032),l(1.331,.364),l(1.312,.347),l(.247,.182),l(.039,.282),l(.351,.38),l(.75,-.217),l(.536,.049),l(1.413,.295),l(.298,-.067),l(.649,-.15),l(.61,.032),l(.752,.379),l(.862,.477),l(.376,.346),l(-.685,.1),l(-.801,.117),l(-.027,.444),l(.795,-.001),l(1.405,-.052),l(.51,-.132),l(.895,.048),l(-.386,.559),l(.918,.179),l(.514,-.001),l(.943,-.379),l(.685,.343),l(1.089,.407),l(.194,.098),l(-.275,.229),l(-.254,.099),l(-.103,.326),l(-.819,.05),l(-.718,-.21),l(-.247,-.048),l(-.794,.213),l(.968,.454),l(.279,.162),l(.057,.276),l(-1.057,.197),l(-.356,.228),l(-.312,.292),l(-.372,-.113),l(-.819,-.583),l(-.29,1.103),l(.354,.903),l(-.419,.065),l(-.677,-.257),l(-.751,-.176),l(-.205,-.177),l(-.018,-.243),l(-.315,-.274),l(-.93,.276),l(-.743,-.613),l(.051,-.292),l(.27,-.374),l(-.304,-.129),l(-.224,-.016),l(-.992,-.08),l(-.718,-.292),l(-1.17,-.617),l(-.769,-.292),l(-.762,-.048),l(-.452,.23),l(-.645,.083),L(250,52.592),l(.281,.179),l(1.05,.682),l(-.321,.114),l(-.686,.05),l(-.359,-.259),L(249.277,53),l(-.646,-.21),l(.275,.488),l(.859,.972),l(.604,.015),l(.587,.08),l(.5,.581),l(.612,.805),l(.513,.432),l(.615,-.321),l(.285,.047),l(.592,.399),l(.585,.271),l(1.38,.396),l(-.634,.113),l(-.213,.208),l(.254,.19),l(.568,.286),l(.962,.444),l(.324,.237),l(.242,.682),l(-.112,.428),l(-1.302,-1.155),l(-.554,-.237),l(-.027,.238),l(.118,.27),l(1.055,1.281),l(-.01,.3),l(-.926,-.125),l(.053,.205),l(.432,.409),l(.378,.519),l(-.563,-.172),l(-.615,-.313),l(-.889,-.693),l(-.145,-.031),l(-.719,.064),l(-.91,-.188),l(-1.44,-.662),l(-.319,-.285),l(-1.062,-.665),l(.187,.777),l(-1.22,-.473),l(-.567,-.158),l(-.872,-.03),l(.095,.222),l(.799,.696),l(.853,.426),l(1.842,.645),l(1.296,.644),l(.774,.549),l(.442,.486),l(.429,.689),l(-1.833,-.341),l(-1.524,-.421),l(-1.251,-.28),l(-1.444,-.107),l(-1.092,-.25),l(-.898,-.644),l(-1.146,-.14),l(-.638,-.204),l(-.635,-.141),l(-.058,.145),M(146.194,38.698),l(.818,-.037),l(.78,-.125),l(1.138,-.548),l(.895,-.019),l(1.723,.243),l(.939,.262),l(-.188,.877),l(.515,-.071),l(.66,-.019),l(.792,-.229),l(.599,-.141),l(.758,.016),l(.334,-.071),l(-.989,-.965),l(.156,-.036),l(1.38,.138),l(1.208,.245),l(.675,.245),l(.259,.245),l(.194,.508),l(.965,1.063),l(.638,.346),l(1.045,-.315),l(.14,-.261),l(-1.243,-1.361),l(-.439,-1.321),l(.228,-.354),l(1.91,.262),l(1.775,.156),l(2.031,.719),l(.36,.175),l(.3,.316),l(.16,.701),l(.511,.645),l(.352,.26),l(.856,.606),l(.048,.19),l(-.178,.243),l(-.333,.605),l(.179,.31),l(.224,.12),l(1.4,.649),l(.509,.171),l(1.151,.254),l(1.513,.1),l(2.056,.576),l(1.012,.39),l(.364,.793),l(-.168,.101),l(-1.071,-.082),l(-.811,-.234),l(-.945,-.234),l(-.551,.169),l(-.665,.204),l(-1.021,.036),l(-.256,.118),l(.208,.689),l(1.087,-.069),l(.614,-.152),l(.676,-.119),l(.876,.536),l(-.01,.235),l(-.526,.151),l(-.517,.252),l(-.583,.102),l(-1.417,.12),l(-1.049,-.015),l(-1.194,-.082),l(-1.594,-.248),l(-2.278,-.499),l(-.553,-.217),l(-.436,-.335),l(-.482,-.033),l(-.581,.102),l(-.402,.37),l(-1.114,.505),l(-1.055,.019),l(-1.411,.103),l(-1.253,.42),l(-2.753,.356),l(-1.42,.019),l(-1.205,.086),l(-1.984,-.063),l(-.483,.101),l(-.916,-.015),l(-1,-.282),l(-.061,-.468),l(.198,-.101),l(.002,-.302),l(-.395,-.2),l(-.462,-.1),l(-3.146,-.112),l(-1.258,-.115),l(-.864,-.167),l(-.801,-.2),l(-.753,-.504),l(-1.274,-.554),l(.303,-.069),l(.531,-.222),l(1.572,-.054),l(.603,-.188),l(.558,.016),l(.91,-.019),l(.904,-.087),l(1.716,.031),l(.935,-.002),l(2.14,.047),l(1.09,-.002),l(1.711,-.038),l(.415,-.154),l(-.681,-.221),l(-2.312,-.492),l(-1.942,-.202),l(-4.059,-.061),l(-1.569,-.014),l(-1.694,.055),l(-.955,.053),l(-.604,.001),l(-1.651,-.529),l(.011,-.207),l(.28,-.069),l(.823,-.053),l(1.315,-.209),l(.996,.032),l(1.78,-.141),l(.931,-.105),l(.5,-.277),l(-.392,-.346),l(-2.023,.073),l(-1.578,.159),l(-.393,-.051),l(-.554,-.189),l(-.677,-.346),l(-.65,-.19),l(-.692,.054),l(-.709,-.242),l(-.039,-.279),l(1.304,-.981),l(2.62,-.848),l(.909,-.143),l(.373,-.177),l(1.297,-.267),l(1.324,-.162),l(.701,-.267),l(1.113,-.091),l(1.026,.246),l(-.07,.212),l(-.548,.354),l(-.021,.81),M(251.273,99.526),l(-.188,.153),l(-.16,-.291),l(.192,-.077),l(-.02,-.256),l(.143,-.069),l(-.042,-.154),l(-.224,0),l(-.606,-.231),l(.077,-.229),l(-.31,-2.819),l(-.174,-.274),l(-.488,-.288),l(-.771,-.025),l(-.41,.176),l(-.381,-.085),l(-.505,-.36),l(-.273,-.085),l(-.632,.526),l(-.514,.626),l(-1.139,2.22),l(-1.139,1.45),l(-1.161,-.124),l(-.402,.06),l(-.363,.435),l(-.174,.375),l(-1.093,-.095),l(-1.855,-.004),l(-2.508,-.029),l(-1.76,.009),l(-.968,.523),l(-.534,.305),l(-1.754,.814),l(-.545,.164),l(-.146,.434),l(-.163,.512),l(-.44,.275),l(-1.156,.197),l(-1.305,-.138),l(-1.123,-.01),l(-.93,.091),l(-.47,.203),l(-.162,.375),l(.018,.319),l(.16,.471),l(.047,.362),l(-.875,.427),l(-1.464,.452),l(-.944,.163),l(-.919,.062),l(-.88,.262),l(-.939,.478),l(-.925,.506),l(-.524,.117),l(-.573,-.068),l(-.497,-.169),l(-.371,-.427),l(-.012,-.33),l(.044,-.218),l(.707,-.525),l(.414,-.294),l(.264,-.433),l(.294,-.544),l(.377,-.576),l(.028,-.746),l(-.054,-.545),l(-.876,-3.16),l(-2.529,-1.05),l(-.26,-.33),l(.11,-.318),l(-.307,-.235),l(-.916,-.181),l(-.184,-.294),l(-.178,-.135),l(-.628,.024),l(-.46,-.465),l(-.101,-.429),l(-2.15,-1.061),l(-3.975,-1.576),l(-.977,-.386),l(-.797,-.227),l(-.805,.189),l(-1.469,.592),l(-.707,-.074),l(-.542,.049),l(-.196,-.144),l(-.156,-.115),l(-.474,-.041),l(-.855,.083),l(-.197,-.116),l(-.028,-.282),l(-.373,.075),l(-.412,.191),l(-.219,.06),l(-.573,.141),l(-.506,-.098),l(-.064,-.185),l(-.469,-.086),l(-.241,-.271),l(-.502,-.013),l(-.16,.247),l(-.338,-.48),l(-.271,.012),l(-.02,-.185),l(-1.425,-.208),l(-.518,.076),l(-.833,-.451),l(-.468,-.46),l(-.279,-.371),l(-.896,-.748),l(-.501,.036),l(.131,.347),l(.387,.588),l(-.68,-.003),l(-2.687,.029),l(-2.798,-.029),l(-1.348,.007),l(-2.105,-.003),l(-2.915,.016),l(-2.781,-.029),l(-2.131,.012),l(-2.935,-.014),l(-.601,.003),l(-4.84,-.018),l(-3.617,.004),l(-.875,.005),l(-3.821,-.023),l(-1.089,.035),l(-4.13,-.021),l(-.74,-.011),l(-5.117,.028),l(-1.687,-.006),l(-2.87,.001),l(-3.938,-.008),l(-4.588,.025),l(-1.335,-.022),l(-2.579,-.001),l(-.194,-.013),l(-.187,-.151),l(-.509,-.305),l(-.071,-.437),l(.074,-.346),l(-.708,.25),l(-.373,-.029),l(-.331,-.305),l(-.162,-.496),l(-.125,-.189),l(-.385,.088),l(-.23,.205),l(-.483,.059),l(-.721,-.495),l(-.119,-.425),l(-.201,-.821),l(-1.051,.104),l(-1.01,-.277),l(-.487,-.087),l(-.173,-.087),l(-.143,-.396),l(-.438,-.352),l(-.591,.222),l(-1.236,.046),l(-.461,-.117),l(-.383,-.249),l(-.106,-.25),l(.257,-.648),l(.458,-.222),l(-.227,-.294),l(-1.24,-.086),l(.617,-.518),l(.398,-.281),l(.547,-.149),l(.605,-.508),l(-.874,.006),l(-.621,.149),l(-.362,-.043),L(116,83.422),l(-.039,-.978),l(-.789,.002),l(-1.015,-1.066),l(.132,-.238),l(.034,-.53),l(-.547,.056),l(-.83,.492),l(-1.266,-.934),l(-.384,-.521),l(-.204,-.402),l(-.068,-.432),l(.419,-.404),l(.161,-.254),l(.436,-.3),l(-.358,-.689),l(-.393,-.777),l(.163,-.788),l(-.402,-.255),l(-2.025,-.763),l(-.98,-.314),l(-.189,-.029),l(-.512,-.393),l(-1.67,-1.882),l(-1.769,-1.768),l(-.814,-.58),l(-2.048,-1.175),l(-.35,-.322),l(-.371,-.492),l(-.316,-.199),l(-.832,-.073),l(-.495,.126),l(-.731,.498),l(-1.225,.67),l(-.848,.205),l(-.238,.325),l(-.945,-.673),l(-2.162,-1.318),l(-.348,-.292),l(-.173,-.387),l(-.332,-.388),l(-.739,-.059),l(-2.424,.122),l(-.84,-.074),l(-.196,-.279),l(-.089,-1.046),l(-.026,-2.167),l(.043,-4.334),l(.026,-2.183),l(-.129,-2.796),l(-.052,-2.335),l(-.039,-2.259),l(.003,-2.855),l(-.102,-.483),l(.71,.024),l(.9,-.086),l(.964,.116),l(2.012,.451),l(1.601,.518),l(1.214,.266),l(1.04,.115),l(.731,.032),l(1.619,.164),l(.888,.232),l(.429,.149),l(.254,-.034),l(-.452,-.601),l(-.357,-.2),l(-.345,-.15),l(-.064,-.419),l(1.344,-.423),l(.745,.066),l(.578,-.068),l(.15,-.102),l(1.736,.148),l(.771,-.001),l(1.125,-.373),l(.309,-.186),l(.442,0),l(.656,-.221),l(.759,-.069),l(.866,-.086),l(.734,-.086),l(.469,-.239),l(.392,-.188),l(.771,-.104),l(1.045,-.002),l(.872,.123),l(-.445,.404),l(-.352,.119),l(-1.101,.137),l(-1.092,.373),l(-1.244,.171),l(-1.22,.271),l(-.699,.522),l(-1.767,.255),l(-.681,.487),l(.846,.266),l(1.441,.748),l(-.219,-.55),l(.168,-.351),l(1.196,-.253),l(.39,-.235),l(.726,-.421),l(1.727,-.053),l(.96,-.372),l(1.158,-.389),l(2.066,-.173),l(.643,-.338),l(.921,-.272),l(.821,-.189),l(.476,-.239),l(-.178,-.272),l(-.702,-.392),l(-.655,-.444),l(.899,.101),l(.764,.272),l(.701,.306),l(.412,.324),l(.376,.476),l(.449,.523),l(.393,.235),l(1.246,.486),l(.186,.067),l(1.154,.216),l(.394,-.018),l(.199,-.151),l(-.511,-.639),l(.07,-.27),l(.548,-.035),l(.334,-.136),l(.627,-.103),l(.383,.354),l(.059,.421),l(-.205,.287),l(.833,.133),l(.938,-.069),l(1.136,-.457),l(.536,-.49),l(.479,-.069),l(1.131,.015),l(1.536,.267),l(1.745,.435),l(1.396,.334),l(2.095,.349),l(1.024,.216),l(.619,.066),l(1.572,.282),l(1.121,.065),l(1.144,.148),l(1.096,.032),l(1.4,-.086),l(.899,.099),l(1.008,.282),l(.982,.349),l(.434,.249),l(.191,.333),l(-.524,.134),l(-.935,-.032),l(-.566,.051),l(-.849,.135),l(-.354,.714),l(3.323,.358),l(1.726,.079),l(1.749,.014),l(.868,-.068),l(.738,-.051),l(.94,-.167),l(.895,-.118),l(.938,-.101),l(.886,.032),l(1.432,.477),l(1.452,.179),l(.42,.115),l(1.225,.443),l(-.013,.312),l(-.504,.083),l(-.35,.247),l(.305,.147),l(1.823,.979),l(-.162,-.392),l(-.024,-.312),l(.456,-.05),l(.617,-.132),l(-.062,-.181),l(-.972,-.656),l(-.128,-.198),l(-.145,-.445),l(.121,-.745),l(.35,-.034),l(1.944,-.136),l(.928,-.151),l(.207,-.299),l(.459,-.217),l(.613,-.035),l(1.098,.281),l(1.528,.279),l(.968,.064),l(.969,-.102),l(.612,.414),l(.248,.082),l(.962,.213),l(1.211,.13),l(.678,.081),l(1.146,-.002),l(.879,-.2),l(1.755,-.02),l(1.876,.029),l(1.07,-.052),l(1.18,-.267),l(.959,.478),l(.95,.296),l(.522,-.018),l(.243,-.314),l(-.017,-.513),l(-.666,-.231),l(-.732,-.131),l(-1.377,-.064),l(.089,-.449),l(1.193,-.085),l(.575,.065),l(.804,.214),l(.871,.198),l(.858,.048),l(.498,.198),l(.088,.183),l(-.095,.132),l(-.287,.86),l(.179,.51),l(.304,.164),l(.177,.065),l(.792,.097),l(.951,.311),l(-.071,-.559),l(-.466,-.989),l(.137,-.48),l(.258,-.266),l(.712,.015),l(.811,-.035),l(1.229,-.85),l(.492,-.051),l(.479,-.001),l(.517,-.151),l(.033,-.133),l(-.15,-.367),l(-.375,-.35),l(-.307,.001),l(-.81,.185),l(-.988,-.082),l(.535,-.52),l(.806,-.069),l(.435,-.168),l(.572,-.001),l(.739,.4),l(.464,.167),l(.065,-.268),l(-.081,-.956),l(-.744,.069),l(-.897,-.032),l(-.68,-.116),l(-.859,-.318),l(-.725,.085),l(-1.245,-.183),l(-.861,-.234),l(-.956,-.218),l(-.657,-.338),l(-.092,-.136),l(-.022,-.324),l(.33,-.137),l(.842,-.463),l(-.486,-.221),l(-1.188,-.375),l(.09,-.207),l(.58,-.604),l(.593,-.294),l(.387,-.035),l(1.032,.257),l(.139,-.035),l(.173,-.346),l(-.709,-.362),l(-.201,-.277),l(.23,-.035),l(.551,-.331),l(.367,-.035),l(1.841,-.021),l(.559,.086),l(.728,.189),l(1.26,.449),l(.432,.328),l(.195,.38),l(-.246,.603),l(1.261,.53),l(.809,.495),l(1.134,.493),l(-.377,.341),l(-.985,.036),l(-.474,.273),l(-.416,.557),l(.471,.067),l(1.071,.233),l(.805,.049),l(.387,-.136),l(.597,-.001),l(1.477,.351),l(-.335,.353),l(-.563,.219),l(.092,.151),l(.796,.467),l(.358,.601),l(.025,.833),l(.303,-.063),l(.021,-.004),l(.411,-.067),l(.608,-.367),l(.655,-1.137),l(.668,-.42),l(.523,.016),l(.731,.284),l(1.064,.55),l(.473,.383),l(.209,.45),l(-.159,.433),l(-.336,.034),l(-.796,-.098),l(-.202,.299),l(-.043,.415),l(.35,.396),l(1.146,.659),l(.61,.493),l(.463,.279),l(.595,-.166),l(.896,-.167),l(.369,-.132),l(.208,-.66),l(.764,-.398),l(.599,-.416),l(.249,-.664),l(.363,-.75),l(.237,-.184),l(1.394,.081),l(.329,-.067),l(.134,-.518),l(-.985,-.333),l(-.918,-.35),l(.088,-.891),l(.595,-.271),l(.787,.032),l(.42,-.068),l(.571,.016),l(3.459,.616),l(1.325,.669),l(.506,-.001),l(.553,-.068),l(.454,.201),l(.244,1.036),l(-.474,.268),l(-.431,.101),l(-.243,-.05),l(-.718,-.532),l(-.263,0),l(-.799,.269),l(.123,.316),l(.309,.515),l(.699,.729),l(.855,.528),l(1.108,.576),l(.024,.313),l(-.478,.642),l(-.439,.181),l(-1.407,.772),l(-.674,.083),l(-1.123,.509),l(-.763,-.276),l(-1.654,-.962),l(-.586,-.262),l(-.497,-.048),l(-.684,.281),l(1.364,.521),l(1.483,.896),l(-.708,.067),l(-.691,-.081),l(-1.288,.084),l(-.375,-.129),l(-.596,-.62),l(-1.147,-.014),l(-1.857,.118),l(-.253,.229),l(.614,.244),l(1.311,.421),l(-.159,.195),l(-.611,.327),l(-2.045,1.106),l(-.657,.179),l(-.527,.001),l(-.859,-.241),l(-.816,-.484),l(-.225,-.081),l(-1.189,-.225),l(-.736,-.259),l(-.598,-.112),l(-.947,.014),l(-.289,.004),l(-1.214,.174),l(1.503,.278),l(1.136,.21),l(1.751,.774),l(1.629,.433),l(1.233,.126),l(1.02,.031),l(-.618,1.091),l(-1.237,.705),l(-.856,.432),l(-.728,.161),l(-.829,.049),l(-.928,-.126),l(-1.062,-.38),l(-.048,.351),l(-.025,.287),l(.321,.572),l(-.02,.159),l(-.741,.031),l(-.058,.002),l(-1.365,-.108),l(-1.649,-.41),l(-.884,-.078),l(-2.962,-.322),l(2.146,.864),l(1.576,.156),l(1.367,.267),l(.562,.205),l(.33,.268),l(-.011,.19),l(-.642,.333),l(-1.106,.207),l(-1.429,-.076),l(-.511,-.062),l(-.367,.269),l(1.254,.423),l(-.469,.426),l(-1.06,.316),l(-1.454,.662),l(-.421,.252),l(.218,.704),l(-.313,.235),l(-.909,.205),l(-.31,.282),l(-.529,.64),l(-.276,.296),l(-.241,.669),l(-.274,.543),l(-.323,.666),l(.056,.416),l(-.161,.554),l(.123,.875),l(.136,.536),l(.598,.366),l(.25,.015),l(.257,.091),l(.664,.014),l(1.164,-.094),l(.276,.045),l(.367,.29),l(.413,.763),l(.813,1.157),l(.22,.668),l(-.132,.91),l(.673,.014),l(1.874,-.428),l(1.261,-.033),l(.723,.074),l(.535,.157),l(1.062,.311),l(2.129,.435),l(1.896,.903),l(.993,.933),l(3.5,.67),l(.644,.225),l(.982,.403),l(.986,.253),l(.553,.104),l(.702,-.091),l(.453,.044),l(.828,-.077),l(1.245,.163),l(1.407,.207),l(.401,.194),l(-.297,.702),l(-.142,.85),l(.154,.283),l(.307,.342),l(.07,.416),l(-.115,1.025),l(-.309,.593),l(.022,.208),l(.604,.266),l(.481,.339),l(.264,.354),l(.046,.488),l(-.076,.354),l(.97,.116),l(.963,.47),l(.676,.588),l(.392,.588),l(.078,.162),l(.64,.014),l(.726,.41),l(.907,.601),l(-.349,-.66),l(-.22,-.279),l(.134,-.338),l(.573,-.414),l(.365,.176),l(.381,.456),l(.262,.353),l(.165,-.354),l(.107,-.545),l(-.215,-.456),l(.541,-.532),l(.139,-.546),l(-.183,-.517),l(-.337,-.458),l(-.261,-.754),l(-.004,-.548),l(-.205,-.593),l(-.218,-.43),l(.615,-.016),l(-.097,-.476),l(-.296,-.252),l(-.657,-.163),l(-.375,-.282),l(-.326,-.923),l(1.252,-.271),l(.872,-.241),l(.625,-.271),l(1.758,-.949),l(.629,-.302),l(1.043,-.935),l(.434,-.544),l(.237,-.665),l(.054,-.529),l(-.257,-1.045),l(-.246,-.531),l(-.239,-.319),l(-.938,-.729),l(-.467,-.274),l(-1.105,-.532),l(-.363,-.122),l(-.453,-.274),l(-.151,-.046),l(-.293,-.351),l(.08,-.107),l(.868,-.522),l(.553,-.875),l(.293,-.416),l(.25,-.092),l(.447,-.017),l(.295,-.277),l(-.106,-.523),l(-.18,-.355),l(-.316,-.402),l(-.048,-.202),l(.258,-.357),l(.005,-.264),l(-1.751,-.105),l(1.084,-.92),l(.503,-.704),l(.007,-.125),l(-.316,-.203),l(-.609,-.265),l(-.244,-.266),l(-.043,-.533),l(.305,-.425),l(.554,-.315),l(.57,-.19),l(.827,.062),l(1.781,.374),l(1.097,.234),l(.753,.077),l(1.44,.013),l(1.08,-.144),l(.86,-.222),l(.21,-.047),l(.179,-.063),l(.589,.078),l(.991,.407),l(.254,.125),l(.754,.454),l(.918,.375),l(.796,.437),l(-.294,.391),l(.406,.233),l(1.698,.496),l(1.958,.354),l(.725,-.033),l(.368,-.203),l(.339,.295),l(-.013,.404),l(-.577,.343),l(-.123,.45),l(.438,1.327),l(.136,.722),l(.23,.414),l(-.049,.353),l(-.248,.169),l(-.445,-.014),l(-.347,-.015),l(-.138,.674),l(.375,.274),l(1.137,-.415),l(.366,-.047),l(.781,-.047),l(.286,.015),l(.677,.32),l(.378,.351),l(.004,.259),l(-.081,.123),l(.277,.32),l(.516,-.184),l(.306,-.046),l(1.173,-.079),l(.636,-.184),l(.436,-.383),l(.333,-.551),l(.326,.015),l(.194,.122),l(.693,.717),l(.042,-.062),l(.108,-.764),l(.317,-.583),l(.475,-.262),l(.539,-.385),l(-.651,-.505),l(.008,-.308),l(.272,-.139),l(.98,-.094),l(.193,-.139),l(.512,-.665),l(.667,.37),l(.607,.599),l(.785,.506),l(.596,.797),l(1.045,.764),l(.264,.352),l(-.344,.291),l(.095,.335),l(.573,-.031),l(.365,.777),l(.182,.183),l(.324,.121),l(.743,.136),l(.281,.258),l(.133,.38),l(-.379,.092),l(-.416,.168),l(.411,.318),l(.397,.227),l(.77,.196),l(.279,.227),l(.034,.424),l(-.056,.076),l(-.409,.106),l(-.676,-.029),l(-.745,-.12),l(-.316,.061),l(.091,.166),l(.273,.181),l(.189,.241),l(.333,.513),l(.411,.226),l(.634,.029),l(.462,.18),l(.838,.496),l(.899,.435),l(.246,.33),l(-.035,.195),l(-.447,.781),l(.508,.059),l(.663,-.166),l(.786,-.077),l(.79,.164),l(.574,.194),l(1.162,.49),l(.981,.132),l(1.517,.295),l(-.184,.253),l(-.718,.21),l(-.736,.21),l(-.663,.046),l(-.834,.24),l(-.583,.402),l(-.65,.225),l(-1.032,.061),l(-.286,.075),l(-.324,.268),l(.029,.371),l(-.271,.535),l(1.175,-.343),l(.542,-.09),l(.649,-.105),l(1.201,-.774),l(1.251,-.478),l(1.146,-.106),l(.662,.237),l(.35,.341),l(-.398,.446),l(.036,.119),l(.307,.296),l(.616,-.224),l(.455,-.164),l(.655,.192),l(1.051,.487),l(.226,.251),l(.022,.178),l(-.299,.43),l(-.05,.355),l(-.406,.444),l(1.001,.929),l(-.365,.37),l(-.795,.282),l(-1.078,.621),l(-.662,.281),l(-1.097,.046),l(-.823,.119),l(-1.548,.077),l(-.433,.413),l(-.916,.795),l(-.686,.427),l(-.612,.294),l(-.938,.222),l(-1.494,.172),l(-1.845,.127),l(-1.452,-.07),l(-2.031,-.084),l(-.355,.03),l(-1.073,.075),l(-1.058,-.012),l(-1.873,-.099),l(-.917,-.027),l(-1.758,.106),l(-.547,.206),l(-.523,.294),l(-.537,.585),l(-.205,.554),l(-.287,.335),l(-.591,.19),l(-1.07,.104),l(-.537,.147),l(-1.098,.555),l(-.774,.54),l(-.794,.612),l(-.325,.363),l(-.33,.233),l(-.868,.843),l(-.485,.566),l(-.418,.276),l(-.46,.58),l(-.518,.968),l(.749,-.737),l(.375,-.131),l(.688,-.479),l(1.059,-.944),l(1.097,-.785),l(2.028,-.948),l(1.245,-.395),l(1.797,-.512),l(1.065,-.235),l(1.03,-.235),l(1.473,-.148),l(.922,.056),l(.895,.289),l(.509,.29),l(.136,.189),l(.144,.464),l(-.125,.218),l(-.326,.219),l(-1.059,.292),l(-.753,.452),l(-.581,.044),l(-.845,-.23),l(-.726,.045),l(-.645,.19),l(.279,.08),l(1.13,.229),l(.17,.122),l(.256,.444),l(.074,.095),l(1.299,-.485),l(-.028,.216),l(.479,-.148),l(.372,.162),l(-.36,.229),l(-.231,.256),l(.112,.27),l(-.163,.014),l(-.074,.229),l(-.91,.444),l(1.216,.013),l(.077,.188),l(-.187,.282),l(.091,.43),l(.118,-.081),l(.239,.134),l(-.063,.134),l(.048,.202),l(.351,.457),l(.009,.147),l(.824,.054),l(.154,.094),l(.04,-.067),l(.727,.147),l(-.315,.134),l(-.373,-.013),l(-.047,.134),l(.525,.147),l(.211,.241),l(.569,-.081),l(.135,.134),l(.212,-.014),l(.134,.174),l(.418,-.04),l(-.075,-.107),l(.843,.067),l(.241,.107),l(-.207,.201),l(.242,-.107),l(.26,.027),l(.245,-.013),l(.696,-.362),l(.303,-.081),l(.104,.362),l(.377,.161),l(.538,-.121),l(.488,.416),l(-.405,.254),l(.089,.107),l(.825,.027),l(.164,.174),l(-.521,.121),l(-.161,-.121),l(-.3,.134),l(.118,.094),l(-.515,0),l(-.23,-.04),l(.109,.161),l(-.45,.04),l(.056,.107),l(-.443,.014),l(-.128,.147),l(-.45,.04),l(-.368,.253),l(-.09,-.106),l(-.706,.28),l(-.046,.053),l(-.529,.133),l(-.119,-.267),l(-.274,.106),l(-.163,.267),l(-.188,-.187),l(-.068,.253),l(-.218,.08),l(-.094,.187),l(-.513,0),l(-.081,-.08),l(-.169,-.053),l(.032,-.347),l(-.242,.36),l(-.202,.12),l(-.131,-.253),l(-.354,.027),l(.043,.24),l(-.233,.04),l(.312,.08),l(.033,.213),l(-.103,.12),l(-.174,-.067),l(-.768,.453),l(.127,.16),l(-.235,.12),l(-.194,.053),l(.015,.213),l(-.161,-.12),l(.083,.173),l(-.217,.08),l(-.14,-.107),l(.096,.253),l(-.222,.066),l(-.146,-.08),l(-.148,0),l(-.064,.133),l(-.156,-.106),l(-.243,.227),l(-.086,.292),l(-.201,-.226),l(-.344,.133),l(-.154,-.187),l(-.349,-.479),l(-.138,.24),l(-.419,-.866),l(.456,-.773),l(.284,-.16),l(.035,-.12),l(-.35,.12),l(-.357,.267),l(-.076,-.106),l(.924,-.507),l(.125,.146),l(.195,-.093),l(-.258,-.107),l(1.103,-.52),l(1.109,-.562),l(.658,-.361),l(.336,.094),l(-.067,.428),l(.179,-.054),l(.258,.281),l(-.044,-.201),l(-.017,-.174),l(.632,-.214),l(.73,-.134),l(.192,.067),l(.202,-.081),l(-.152,-.094),l(-.653,-.053),l(-.595,.053),l(-.42,-.053),l(-.804,-.014),l(-.306,.027),l(-.251,.081),l(-.246,.094),l(.033,-.214),l(1.128,-.563),l(.054,-.201),l(.252,-.08),l(-.052,-.174),l(-.523,.281),l(.097,-.134),l(-.502,-.51),l(.309,.443),l(-.36,.482),l(-.328,.013),l(-1.974,.817),l(-.284,.08),l(-.362,-.201),l(-.227,-.067),l(.23,.201),l(-.788,.401),l(-.219,-.174),l(-1.019,-.054),l(-.124,.147),l(-.38,-.241),M(186.193,47.833),l(-.713,-.032),l(-.922,-.181),l(-.882,-.065),l(-1.25,-.314),l(-.973,-.182),l(-.604,-.049),l(-1.083,-.199),l(.43,-.335),l(1.542,-.405),l(.385,-.186),l(.115,-.673),l(.305,-.236),l(.831,-.069),l(.743,.184),l(1.436,.603),l(1.287,.4),l(.074,.285),l(.315,.284),l(1.094,.516),l(-.288,.117),l(-1.263,.486),l(-.578,.051),M(231.09,50.646),l(-1.319,-.03),l(-.449,-.147),l(-.232,-.247),l(-.173,-.478),l(.3,-.43),l(.708,-.664),l(.662,-.267),l(1.359,-.168),l(.911,.197),l(.79,.314),l(-.021,.464),l(-.039,.727),l(-.362,.265),l(-1.025,.348),l(-1.108,.117),
+N(444.972,79.145),l(.47,-.218),l(.307,-.093),l(-.294,-.024),l(-.419,.061),l(-.15,-.135),l(-.125,.184),l(-.108,-.012),l(.066,-.491),l(.177,-.218),l(.41,.009),l(1.489,.062),l(.417,.014),l(.339,-.075),l(.121,-.253),l(-.175,-.288),l(.246,-.05),l(-.068,-.122),l(-.068,-.123),l(.353,-.096),l(.12,-.034),l(.051,.154),l(.086,.052),l(.24,0),l(.223,.12),l(.257,.069),l(.514,.068),l(.086,.103),l(.223,-.051),l(.445,0),l(.257,0),l(.223,-.017),l(.086,.137),l(.103,.103),l(.188,.034),l(.171,.069),l(.018,.137),l(.052,.12),l(-.224,.12),l(-.068,.154),l(-.068,.206),l(.018,.171),l(.034,.137),l(.029,.038),l(-2.96,.101),l(-2.246,-.115),l(-.842,-.006),M(717.633,81.109),l(.42,.443),l(.429,.62),l(.183,.457),l(.01,.767),l(-.244,.442),l(-.197,.78),l(-.002,.764),l(.29,.777),l(.592,.849),l(.65,1.446),l(.899,1.614),l(1.115,1.679),l(-1.26,-.677),l(-.832,-.39),l(-.99,-.056),l(-.268,.088),l(-.396,.204),l(-.462,1.045),l(-.266,1.101),l(-.082,.579),l(.277,.982),l(.183,.216),l(.659,.908),l(.54,.201),l(.463,.648),l(-.314,1.246),l(-.664,-1.258),l(-.866,-.301),l(-.224,.029),l(-.415,.303),l(-.311,.534),l(-.643,.907),l(-.422,-.5),l(-.19,-.929),l(.637,-1.146),l(-.395,-.884),l(.175,-.454),l(.502,-.63),l(-.131,-.723),l(-.196,-.376),l(-.27,-.55),l(-.062,-.235),l(.403,-.302),l(.284,-.915),l(.075,-.784),l(.005,-1.326),l(.15,-1.302),l(-.09,-.732),l(-.213,-.469),l(-.83,-.85),l(-.1,-.897),l(.114,-.192),l(.359,-.722),l(.065,-.738),l(-.336,-.457),l(.172,-.237),l(.374,-.03),l(.62,-.031),l(1.023,-.534),M(471.158,84.281),l(-.002,-.142),l(-.165,-.066),l(-.082,-.115),l(-.164,-.082),l(.033,-.099),l(-.033,-.23),l(-.033,-.164),l(.082,-.099),l(-.147,-.131),l(-.099,-.148),l(.132,-.066),l(0,-.165),l(-.296,-.164),l(-.279,-.263),l(-.017,-.164),l(.099,-.131),l(.131,-.165),l(.362,-.017),l(.328,.049),l(.197,.132),l(.51,.016),l(.525,-.099),l(.444,-.247),l(.049,-.065),l(.148,-.083),l(.296,0),l(.065,-.164),l(-.033,-.131),l(-.279,-.066),l(-.296,-.148),l(-.099,-.181),l(.082,-.017),l(.066,-.049),l(.032,-.065),l(-.263,-.066),l(-.361,-.099),l(-.378,-.066),l(-.361,.066),l(-.182,-.066),l(.066,-.181),l(.099,-.197),l(-.066,-.148),l(-.164,-.099),l(-.279,-.082),l(-.23,-.066),l(-.443,-.213),l(-.115,-.23),l(-.164,-.263),l(-.214,-.017),l(-.017,-.099),l(.066,-.131),l(.099,-.115),l(-.132,-.033),l(-.181,.049),l(-.082,-.115),l(-.132,-.181),l(-.345,-.049),l(.049,-.147),l(.033,-.165),l(.099,-.049),l(.115,-.082),l(0,-.083),l(.114,0),l(.066,-.131),l(-.05,-.164),l(-.147,-.099),l(-.197,-.247),l(.131,-.165),l(.033,-.164),l(0,-.083),l(.065,-.115),l(-.049,-.115),l(-.147,.033),l(-.165,-.033),l(-.147,-.099),l(-.099,-.099),l(-.279,-.099),l(-.132,-.131),l(-.542,-.115),l(-.247,.049),l(-.099,-.049),l(-.131,-.049),l(-.23,.083),l(-.147,.099),l(-.165,0),l(-.279,.016),l(-.214,.197),l(-.197,0),l(-.164,-.148),l(-.065,-.148),l(.017,-.099),l(.164,-.099),l(0,-.115),l(-.147,-.017),l(-.296,-.065),l(-.312,-.049),l(-.361,.049),l(-.214,.065),l(-.197,.033),l(-.082,-.148),l(-.132,-.148),l(-.312,-.033),l(-.181,-.016),l(-.197,.131),l(-.229,-.066),l(-.165,-.147),l(.061,-.042),l(.015,-.117),l(.044,-.087),l(-.088,-.233),l(.015,-.189),l(-.131,-.117),l(.059,-.087),l(-.16,-.043),l(-.146,-.102),l(-.029,-.16),l(-.131,-.058),l(-.116,-.102),l(.043,-.073),l(.059,-.087),l(-.073,-.044),l(-.087,-.014),l(-.131,-.073),l(-.146,.015),l(-.204,.059),l(.044,-.102),l(.102,-.073),l(.073,-.087),l(-.029,-.117),l(.072,-.131),l(-.131,-.087),l(.103,-.029),l(.087,-.015),l(.102,-.073),l(.015,-.087),l(.029,-.116),l(.015,-.087),l(-.204,-.058),l(-.087,-.073),l(-.204,-.087),l(-.232,-.073),l(0,-.117),l(.015,-.116),l(-.37,.004),l(-.081,-.106),l(.116,-.058),L(461.402,72),l(.029,-.117),l(.131,0),l(.087,-.116),l(.059,-.102),l(.16,-.058),l(.262,-.043),l(.175,-.073),l(-.059,-.059),l(-.175,-.043),l(-.043,-.146),l(-.015,-.087),l(0,-.073),l(-.088,-.073),l(-.203,-.087),l(-.175,-.233),l(0,-.175),l(.175,-.131),l(-.029,-.16),l(-.073,-.189),l(-.131,-.437),l(-.029,-.16),l(.088,-.16),l(.204,-.131),l(.319,-.131),l(.219,-.204),l(.175,-.277),l(.058,-.131),l(.088,-.043),l(.116,0),l(.189,0),l(.175,-.044),l(.043,-.174),l(-.16,-.131),l(-.145,-.053),l(-.089,-.13),l(-.17,-.038),l(.1,-.253),l(.339,-.038),l(.153,.165),l(.229,.063),l(.188,-.088),l(-.094,-.139),l(.301,-.154),l(.485,.199),l(.296,-.062),l(.312,-.338),l(.311,-.185),l(.75,.106),l(.781,.275),l(.439,0),l(.363,-.154),l(-.386,-.399),l(-.59,-.323),l(-.393,-.03),l(-1.204,.08),l(-.616,-.091),l(-.271,-.108),l(-.299,-.309),l(.258,-.434),l(-.065,-.201),l(-.199,.044),l(.174,-.285),l(1.946,-1.145),l(1.983,-1.195),l(1.385,-.758),l(.591,-.536),l(.43,-.536),l(.105,-.409),l(-.161,-.346),l(-.436,-.392),l(-.703,-.265),l(-1.357,-.499),l(-.439,-.33),l(.327,-.191),l(.542,-.415),l(.057,-.254),l(-.151,-.253),l(-1.286,-1.395),l(-.37,-.509),l(.029,-.37),l(.187,-.403),l(.44,-.535),l(.196,-.356),l(-.772,-1.195),l(-1.402,-1.394),l(.328,-.296),l(1.303,-.777),l(.421,-.364),l(-.543,-.392),l(-.964,-.506),l(-.872,-.194),l(-.563,-.212),l(-.116,-.529),l(.258,-.465),l(.024,-.283),l(.689,-.303),l(1.013,-.672),l(1.023,-.49),l(.77,-.121),l(.824,-.021),l(.514,-.204),l(.404,-.288),l(.617,-.051),l(1.002,-.254),l(.643,-.237),l(.01,.151),l(.255,.386),l(.358,.284),l(.543,.2),l(.919,.082),l(.602,.1),l(.078,.602),l(.695,-.319),l(.421,.049),l(1.083,.048),l(.875,.015),l(.522,.032),l(1.116,-.002),l(1.293,.281),l(2.728,.512),l(.984,.364),l(1.595,.86),l(.583,.214),l(1.48,.246),l(1.296,.212),l(2.018,.623),l(.328,.279),l(-.051,.444),l(.147,.295),l(.426,.294),l(.104,.294),l(-.24,.344),l(-.69,.491),l(-1.092,.54),l(-.816,.262),l(-1.75,.36),l(-.907,.083),l(-1.631,-.013),l(-1.391,-.192),l(-2.038,-.175),l(-1.63,-.192),l(-1.342,-.339),l(-2.256,-.485),l(-1.114,-.112),l(-.476,-.048),l(-.621,-.473),l(-.371,-.163),l(-.771,-.13),l(-.943,.117),l(.307,.163),l(.149,.065),l(.73,.538),l(.482,.146),l(1.109,.601),l(.832,.291),l(.921,.161),l(.634,.242),l(.405,.453),l(-.002,.405),l(-.276,.291),l(-.684,.195),l(.086,.113),l(.208,.531),l(.771,.943),l(.093,.494),l(.155,.207),l(.438,.174),l(1.203,.078),l(.872,.125),l(.499,.619),l(.401,.095),l(1.26,.077),l(.575,.126),l(.364,.079),l(.402,-.128),l(.785,-.097),l(.243,-.302),l(-.001,-.318),l(-.387,-.397),l(-.471,-.079),l(-.455,.096),l(-.447,-.031),l(-.589,-.206),l(-.952,-.795),l(.701,-.674),l(.484,-.001),l(1.116,.479),l(1.441,.333),l(2.09,.427),l(.952,.078),l(.834,-.146),l(.723,.174),l(.261,-.224),l(.05,-.415),l(-.214,-.239),l(-.858,-.656),l(-.348,-.628),l(.285,-.323),l(.19,-.049),l(1.432,-.423),l(1.495,-.359),l(.599,-.244),l(1.133,-.717),l(.172,-.049),l(.462,.064),l(1.829,.29),l(1.41,.533),l(.341,-.001),l(.052,-.065),l(.154,-.503),l(.581,-.767),l(-.048,-.653),l(-.317,-.408),l(-.847,-.163),l(-.3,-.229),l(1.139,-1.005),l(.101,-.247),l(-.205,-.594),l(-.771,-.512),l(.069,-.315),l(.353,-.051),l(1.458,.23),l(2.025,-.12),l(.631,.132),l(.664,.611),l(.616,.445),l(.433,.461),l(-1.045,.051),l(-1.559,.085),l(-.822,.215),l(-.492,.51),l(.191,.18),l(.952,.293),l(.732,.555),l(.804,.194),l(.723,.097),l(1.268,-.133),l(1.33,-.084),l(.301,-.164),l(.257,-.491),l(.291,-.591),l(.284,-.412),l(1.232,-.2),l(1.223,-.414),l(.988,-.216),l(1.924,-.483),l(1.429,-.251),l(1.537,-.318),l(.921,-.3),l(.205,.464),l(.278,.083),l(.571,-.117),l(.487,-.266),l(.148,-.465),l(.386,-.167),l(.718,-.135),l(.859,.065),l(-.18,.399),l(-.058,.597),l(-.858,.084),l(-.178,.149),l(.002,.215),l(.687,.197),l(.507,-.083),l(1.169,-.167),l(.436,-.001),l(.161,.198),l(.23,.049),l(.278,-.133),l(.264,-.216),l(.29,-.431),l(.464,-.183),l(.861,-.118),l(1.049,-.068),l(.768,.032),l(1.075,.23),l(.755,-.018),l(.36,-.083),l(.963,-.467),l(1,-.285),l(.803,-.052),l(.952,.182),l(.326,.166),l(-.631,.45),l(.129,.232),l(.217,.099),l(.632,.131),l(.579,-.018),l(.288,-.232),l(.074,-.398),l(.342,-.084),l(.962,.065),l(.543,-.184),l(.395,-.316),l(.115,-.417),l(-1.37,-1.033),l(.405,-.168),l(.66,-.37),l(.403,-.068),l(.609,.016),l(2.171,.063),l(1.272,.199),l(1.241,.149),l(1.135,.199),l(2.111,.515),l(1.071,.098),l(1.712,.414),l(1.02,.248),l(1.305,.53),l(1.455,.611),l(.864,.379),l(.376,.049),l(.229,-.1),l(1.145,-1.047),l(.236,-.3),l(-.927,.035),l(-.4,-.049),l(-.564,-.232),l(-.365,-.433),l(.027,-.652),l(-.727,-.283),l(-1.987,-.147),l(-.19,-.268),l(.064,-.168),l(.305,-.303),l(.693,-.255),l(.236,-.153),l(.085,-.187),l(-.052,-.833),l(-.251,-.238),l(-1.135,-.066),l(-.232,-.29),l(.328,-.532),l(.359,-.241),l(.391,-.035),l(1.482,-.416),l(1.098,-.485),l(.521,-.416),l(.581,-.608),l(.544,-1.22),l(.637,-.421),l(.374,.069),l(1.562,.155),l(1.613,-.125),l(1.605,-.091),l(.695,.069),l(1.066,-.055),l(.574,.122),l(.309,.279),l(-.018,.332),l(-.423,.836),l(-.348,.348),l(-1.334,.833),l(-.223,.345),l(.752,.342),l(.931,.667),l(.277,.342),l(.21,.818),l(-.174,.222),l(-.575,.375),l(.254,1.179),l(-.058,1.305),l(.263,.583),l(.45,.381),l(1.027,.264),l(.19,.166),l(-.001,.133),l(-.485,.481),l(-.417,.826),l(-.333,.33),l(-.784,.462),l(-1.232,.625),l(-.63,.198),l(-.55,.263),l(.321,.522),l(-.433,.115),L(558,52.195),l(-1.599,-.372),l(-.731,-.08),l(-.97,.034),l(-.601,.115),l(.195,.31),l(.583,.276),l(.738,.21),l(1.569,.208),l(1.133,.079),l(.613,-.05),l(1.188,.144),l(.922,-.034),l(.472,-.358),l(.303,-.358),l(1.352,-.328),l(1.166,-.492),l(.268,-.278),l(.386,-.606),l(.818,-.313),l(.864,-.626),l(.064,-.362),l(-.225,-.561),l(-.609,-.545),l(.244,-.548),l(.237,-.1),l(.677,-.151),l(1.38,-.152),l(1.757,-.003),l(.74,.231),l(.842,.463),l(.151,.778),l(-.34,1.023),l(.302,.279),l(.92,.212),l(1.298,.047),l(.864,-.149),l(.129,-.296),l(-.514,-.18),l(-.797,-.18),l(-.571,.034),l(-.457,-.098),l(.068,-.379),l(1.03,-.382),l(.065,-.249),l(-.218,-.148),l(-.166,-.331),l(-.441,-.763),l(-.511,-.266),l(-.836,-.098),l(-1.093,-.231),l(-.801,-.116),l(-1.288,-.165),l(-.91,.186),l(-.638,.101),l(-1.297,-.181),l(-.896,.019),l(-.015,-.267),l(-.564,-1.071),l(.305,-.657),l(.736,-.697),l(.282,-.46),l(-.134,-.221),l(-1.092,-1.042),l(-.949,-.514),l(-.12,-.189),l(.833,-.554),l(1.213,-.106),l(.998,-.262),l(.744,-.348),l(.172,-.226),l(.169,-.644),l(-.13,-.663),l(.23,.069),l(.64,.051),l(.466,.086),l(.108,.471),l(-.186,.54),l(-.636,.608),l(-.167,.554),l(.14,.448),l(.373,.274),l(.485,.274),l(1.384,.134),l(1.574,.169),l(1.632,.083),l(.819,.409),l(.321,.017),l(.799,-.036),l(-.527,-.89),l(-.521,-.274),l(-1.893,-.1),l(-.931,-.067),l(-.576,-.154),l(-.609,-.448),l(.275,-.329),l(1.374,-.054),l(.444,.172),l(1.145,.084),l(.747,-.157),l(-.09,-.728),l(.408,-.088),l(.84,-.105),l(1.278,-.02),l(1.067,.207),l(1.413,.379),l(1.088,.535),l(1.326,.343),l(.547,.085),l(1.822,.014),l(.727,-.174),l(.138,.345),l(-.781,.38),l(-.696,.259),l(-.248,.771),l(-.129,.972),l(.333,.136),l(.68,-.785),l(.387,-.292),l(.285,.051),l(.604,.528),l(-.088,.749),l(.743,-.205),l(.681,-.273),l(-.044,-.306),l(-.191,-.119),l(-.147,-.358),l(-.748,-.821),l(.188,-.223),l(.686,-.759),l(-.797,-.448),l(-.772,-.258),l(-.93,-.241),l(-.257,-.382),l(-.655,-.051),l(-.979,-.242),l(-1.34,-.207),l(-.307,-.296),l(.062,-.577),l(-.096,-.386),l(-.811,-.667),l(.081,-.247),l(.266,-.16),l(.484,-.125),l(2.31,-.11),l(2.54,-.022),l(2.125,-.128),l(1.421,-.162),l(.475,.317),l(.382,.052),l(.844,-.267),l(1.056,-.286),l(1.413,-.109),l(.589,.194),l(-.957,.338),l(-.451,.407),l(1.737,-.233),l(.521,-.107),l(.955,-.374),l(.27,-.284),l(-.334,-.444),l(-.326,-.177),l(-.925,-.266),l(-.365,-.303),l(-.002,-.232),l(.324,-.539),l(1.176,-.397),l(.966,-.22),l(3.028,-.903),l(.889,-.094),l(.248,-.036),l(.522,-.076),l(1.899,-.096),l(1.663,-.114),l(2.302,-.244),l(2.048,-.263),l(1.595,-.43),l(.855,-.243),l(1.763,.034),l(1.065,-.002),l(.383,.185),l(-.351,.409),l(1.504,.108),l(1.018,-.039),l(1.261,-.188),l(1.345,-.225),l(.95,-.039),l(.982,.166),l(.687,.073),l(.693,-.206),l(.12,-.335),l(-.133,-.167),l(.466,-.337),l(.942,-.077),l(.939,.036),l(1.243,-.377),l(-.618,-.506),l(.122,-.34),l(1.165,-.438),l(1.554,-.383),l(2.23,-.406),l(1.229,-.077),l(.993,.056),l(1.486,-.003),l(.86,.265),l(.045,.266),l(-1.528,.401),l(-.66,.342),l(.488,.15),l(1.83,-.117),l(1.588,.148),l(2.039,-.079),l(.177,.113),l(-.375,.283),l(-1.187,.453),l(-.296,.3),l(1.971,-.004),l(.833,-.02),l(.234,.093),l(1.052,-.545),l(1.366,-.002),l(1.771,-.097),l(.631,.13),l(1.35,-.021),l(1.954,.165),l(1.4,.259),l(1.181,.427),l(.52,.445),l(.726,-.001),l(.854,-.076),l(.422,.5),l(-1.354,.832),l(.241,.128),l(.896,.365),l(-2.329,.859),l(-1.035,.235),l(-1.166,.11),l(-3.404,1.061),l(-3.018,.965),l(-.793,.285),l(-2.388,.375),l(-2.35,.586),l(-2.065,1.126),l(.715,.017),l(1.081,-.247),l(.922,-.458),l(1.663,-.161),l(1.231,-.02),l(2.101,-.268),l(1.879,-.286),l(.879,-.107),l(1.004,-.284),l(-.094,-.389),l(-.492,-.123),l(-.034,-.071),l(.281,-.249),l(.581,-.214),l(.873,.211),l(.603,.389),l(.621,.052),l(.593,.193),l(.737,.052),l(.853,-.055),l(1.155,-.268),l(.499,.07),l(.192,.3),l(.009,.512),l(.522,.404),l(1.422,-.778),l(1.66,-.021),l(1.506,-.145),l(2.354,.014),l(1.919,.155),l(.854,.034),l(1.204,.033),l(-.271,.74),l(.354,.333),l(2.043,.154),l(.848,.121),l(.698,.069),l(1.035,.103),l(2.49,-.145),l(1.209,-.02),l(1.42,.348),l(1.405,-.932),l(-.161,-.352),l(-.038,-.229),l(-.034,-.105),l(1.806,-.48),l(.521,-.019),l(.802,.104),l(1.148,.298),l(.851,.281),l(1.711,.032),l(1.354,-.073),l(1.384,.033),l(1.323,.431),l(.409,.181),l(.058,.386),l(-.52,.088),l(-.268,.036),l(-1.905,.406),l(-2.438,.737),l(-.233,.227),l(.253,.069),l(1.033,.067),l(.957,.016),l(.659,.155),l(.659,.293),l(1.014,.396),l(.583,.172),l(.78,.481),l(1.27,.805),l(1.814,.801),l(1.351,.305),l(.612,-.018),l(.795,-.394),l(.843,-.72),l(1.093,-1.051),l(.601,-.329),l(.29,.017),l(.236,.465),l(.772,.308),l(1.346,.29),l(1.105,.135),l(.848,-.087),l(1.973,-.468),l(.778,-.07),l(.813,.067),l(1.196,.239),l(1.921,.734),l(.315,-.052),l(.186,-.069),l(.491,-.258),l(.221,-.258),l(-.137,-.051),l(.013,-.189),l(.726,-.312),l(.509,-.018),l(.5,.257),l(.575,.188),l(1.302,.032),l(.181,-.5),l(-.194,-.466),l(.15,-.363),l(.093,-.521),l(-1.131,.159),l(-.643,.001),l(-.179,-.104),l(.442,-.296),l(.318,-.087),l(.986,-.089),l(1.021,-.02),l(.832,-.141),l(1.566,-.648),l(.254,0),l(1.76,.241),l(1.561,.137),l(2.036,.188),l(.997,.068),l(.654,.103),l(2.307,.065),l(-1.732,.628),l(.865,.051),l(1.011,-.089),l(.335,.138),l(-.305,.381),l(-.926,.192),l(-.846,.261),l(-.177,.293),l(.664,.033),l(.52,-.122),l(.916,-.14),l(.978,-.33),l(1.62,-.799),l(2.766,.012),l(1.196,.067),l(.903,.172),l(.946,.137),l(.205,.19),l(.221,.104),l(-2.247,.59),l(.559,.137),l(1.674,.289),l(2.174,.202),l(.946,.204),l(.801,.375),l(.367,.427),l(.564,.357),l(.522,.152),l(1.459,-.037),l(1.364,-.105),l(1.138,-.139),l(2.518,-.329),l(2.208,-.107),l(3.008,.131),l(1.515,.134),l(.734,.118),l(1.168,.424),l(.655,.169),l(.525,.338),l(.361,.39),l(-.123,.491),l(-.286,.521),l(.509,.25),l(.764,.065),l(.835,.015),l(.643,.083),l(-.017,.685),l(.419,.416),l(.686,-.168),l(.545,-.435),l(1.211,-.387),l(.348,-.067),l(.35,.049),l(1.696,-.02),l(1.521,-.288),l(.736,.032),l(.588,.434),l(.707,.116),l(1.541,.014),l(2.176,.062),l(.869,-.118),l(1.378,-.504),l(.406,.2),l(.93,.533),l(.396,.216),l(1.095,.265),l(.875,.332),l(.282,.398),l(.612,.148),l(1.556,-.136),l(1.653,-.319),l(.16,-.25),l(-.248,-.333),l(-.805,-.833),l(-.833,-.115),l(.4,-.336),l(.479,-.571),l(1.89,.098),l(1.214,.082),l(1.135,.065),l(1.221,.166),l(.222,.318),l(1.396,-.153),l(2.084,-.054),l(2.304,.013),l(1.292,.148),l(.786,.199),l(1.185,.199),l(1.391,.098),l(.751,.182),l(1.302,.332),l(.747,.065),l(.703,.182),l(1.145,.505),l(0,2.126),l(0,2.222),l(0,2.222),l(0,1.292),l(0,.157),l(0,.576),l(0,.219),l(-1.083,.371),l(-.651,.03),l(-.645,.37),l(-.56,.065),l(-1.044,.065),l(-.355,-.079),l(-.928,-.052),l(-.118,-.343),l(-.271,-.211),l(-.501,.027),l(.241,-.185),l(-.938,.324),l(-.846,.02),l(-.337,-.211),l(-.478,-.079),l(.424,.501),l(-.569,.29),l(.32,.103),l(.942,.205),l(.634,-.36),l(.395,.041),l(.335,.079),l(0,.477),l(.248,.206),l(.76,.269),l(1.059,-.228),l(-.439,.322),l(.741,-.243),l(.065,.336),l(.247,.206),l(.187,.363),l(.068,.189),l(-.722,.522),l(.593,-.064),l(.349,.172),l(.473,.503),l(.501,.157),l(.145,.251),l(-.162,.456),l(.792,-.111),l(-.125,.393),l(.023,.25),l(-.43,.299),l(-.691,.205),l(-.635,-.046),l(-.448,-.14),l(-1.243,-.154),l(-.889,-.077),l(-.347,-.14),l(.123,-.267),l(-.493,-.046),l(-.304,.032),l(-.559,.55),l(-.069,.11),l(-3.06,.913),l(-1.155,.174),l(-.245,0),l(-.43,.203),l(-.219,.188),l(-.719,.22),l(-.991,.033),l(-.308,.11),l(-.48,.405),l(-.462,.203),l(-.946,.033),l(-.854,.622),l(-.24,.279),l(-1.67,.452),l(-.392,.449),l(-1.229,.25),l(-.406,.14),l(-.151,.293),l(.01,.292),l(-.473,.292),l(-.406,.016),l(-.586,-.306),l(-.183,-.262),l(-.169,-.37),l(-.67,-.308),l(-1.074,-.044),l(-1.021,.048),l(-1.159,.172),l(-1.301,.188),l(-.523,.217),l(-1.333,.756),l(-.536,.277),l(-.184,-.138),l(.575,-1.293),l(-.55,.094),l(-.392,-.097),l(-.811,.531),l(-.67,.186),l(-.401,.155),l(-.114,.506),l(-.66,.154),l(-.317,-.168),l(-.253,-.49),l(-.483,-.261),l(-1.024,.636),l(.261,-.204),l(-.557,.062),l(-.283,.092),l(-.628,.522),l(-.141,.261),l(.126,.229),l(.344,.152),l(-.932,.521),l(-.182,.199),l(.342,.167),l(-.647,.352),l(-.88,.55),l(-.626,.503),l(-.298,.35),l(-.01,.531),l(.421,.317),l(.477,.09),l(1.382,-.048),l(.562,.166),l(.11,.167),l(-.436,.394),l(-.752,.455),l(-.181,.302),l(.224,.512),l(.292,.452),l(.786,.089),l(.165,.391),l(-.014,.616),l(-.349,.361),l(-.528,.061),l(-.481,-.209),l(-.017,-.21),l(.388,-.527),l(-.444,-.014),l(-.454,.242),l(-1.014,.843),l(-.56,.675),l(-.237,.599),l(.321,.672),l(.647,.311),l(.154,.237),l(-.294,.387),l(-.688,.313),l(-.956,.031),l(-.664,-.088),l(-.344,.001),l(-.619,.179),l(-.837,.476),l(-.214,.149),l(-.525,.504),l(-.137,.341),l(.111,.281),l(.492,.398),l(.038,.221),l(-.056,.133),l(-.679,.238),l(-.604,.016),L(753.44,82.4),l(-.727,.296),l(-.065,.251),l(.067,.28),l(-.161,.854),l(-.293,.412),l(-.8,.78),l(-1.53,.971),l(-.854,.5),l(-.285,.103),l(-.295,-.614),l(-.198,-.821),l(-.25,-.69),l(-.064,-.794),l(-.351,-.75),l(-.305,-.383),l(-.214,-.915),l(-.514,-1.36),l(-.008,-.578),l(-.154,-.563),l(-.017,-.327),l(-.105,-.193),l(-.262,-.817),l(-.026,-.792),l(.116,-.808),l(.271,-1.396),l(.167,-.226),l(1.185,-.86),l(.716,-.424),l(.57,-.695),l(.14,-.227),l(-.085,-.318),l(-.139,-.166),l(1.632,-.367),l(1.001,-.305),l(.811,-.32),l(1.729,-.884),l(.641,-.412),l(.431,-.428),l(.14,-.335),l(1.784,-.889),l(.872,-.445),l(1.535,-.861),l(.368,-.293),l(.26,-.433),l(1.252,-.435),l(2.106,-.514),l(.257,-.434),l(.773,-.528),l(.086,-.233),l(-.568,-.216),l(.814,-.904),l(-.036,-.483),l(.183,-.391),l(.598,-.204),l(1.729,-.082),l(.513,-.063),l(-.485,-.328),l(-2.065,-.215),l(-.71,.095),l(-1.062,.174),l(-.777,.189),l(.042,.328),l(-.664,.923),l(.125,.202),l(-.04,.14),l(-.662,.11),l(-.479,.11),l(-1.555,.718),l(-1.979,1.042),l(-1.169,.342),l(-.249,-.062),l(.156,-.325),l(.352,-.465),l(-.933,-.076),l(-.16,-.263),l(.252,-.451),l(.442,-.467),l(.207,-.328),l(-.069,-.202),l(-.339,-.031),l(-1.136,.454),l(-.496,.032),l(-.277,-.358),l(-.589,-.17),l(-1.606,.144),l(-1.312,.111),l(-.956,.08),l(-.606,.157),l(-.894,.359),l(-.093,.436),l(.082,.186),l(-1.262,.53),l(-.408,.233),l(.149,.495),l(-1.294,.357),l(-1.019,.434),l(-.84,.479),l(-.496,.461),l(-.332,.46),l(.004,.383),l(.527,.213),l(1.269,.043),l(.278,.275),l(.062,.122),l(-1.152,.139),l(-1.028,.262),l(-1.045,-.059),l(-.698,-.136),l(-.382,.031),l(-.311,.107),l(-.721,.398),l(-.695,-.35),l(-1.112,.383),l(-.747,.139),l(-.487,-.09),l(-.284,-.137),l(.108,-.29),l(.748,-.124),l(.868,-.124),l(.164,-.046),l(-.741,-.64),L(736.444,68),l(-1.06,.017),l(-.854,.155),l(-.353,-.061),l(-.868,-.458),l(-.775,-.029),l(-.745,.047),l(-.704,.246),l(.042,.398),l(-.26,.229),l(-.477,.215),l(-.695,-.243),l(-.408,-.122),l(-1.26,.063),l(-.784,.093),l(-.651,-.075),l(-.887,-.136),l(-.563,.078),l(-.067,.122),l(-.147,.474),l(-.677,.017),l(-.311,-.137),l(-.038,-.382),l(-.203,-.259),l(-1.241,.094),l(-1.014,-.059),l(-1.257,.033),l(-1.158,.063),l(-.836,-.029),l(-.18,.016),l(-1.085,.292),l(-.964,.444),l(-.74,.474),l(-.536,.504),l(-.789,.245),l(-.904,.336),l(-.194,.152),l(-1.047,1.17),l(-1.634,.684),l(-.949,.471),l(-1.157,.711),l(-1.653,1.253),l(-.828,.572),l(-1.573,.873),l(-.893,.376),l(-1.889,.871),l(-.632,.388),l(-.203,.298),l(.018,.357),l(.428,.281),l(.485,.043),l(.918,.013),l(1.046,-.15),l(.656,.043),l(-.329,1.261),l(.016,.415),l(.303,.103),l(.63,-.09),l(.601,-.371),l(.761,.117),l(-.127,.148),L(705.293,81),l(-.112,.222),l(.373,.073),l(1.645,-.018),l(.566,-.238),l(.39,-.519),l(.017,-.638),l(.763,.014),l(.647,-.001),l(.726,.014),l(.951,.265),l(.658,.354),l(.486,.591),l(.847,.575),l(.426,.176),l(.506,.324),l(-.07,.148),l(-.581,.355),l(.453,.221),l(.13,.309),l(-.336,.723),l(.491,.117),l(.215,.235),l(-.291,.515),l(-.348,.397),l(-.532,.559),l(-.724,1.364),l(-.181,.688),l(.057,.219),l(.24,.701),l(-.188,.917),l(-.098,.741),l(-.403,1.408),l(-.146,.493),l(-1.928,1.538),l(-.371,.435),l(-.217,.65),l(-.587,.42),l(-.741,.579),l(-.241,.361),l(-.574,.981),l(-.587,.606),l(-.941,.778),l(-1.784,1.512),l(-.464,.474),l(-.235,.458),l(-.323,.33),l(-.758,.388),l(-.618,.416),l(-.574,.702),l(-.431,.458),l(-.875,.673),l(-.955,.487),l(-1.838,.475),l(-.798,.244),l(-.278,-.427),l(-.519,-.085),l(-.243,.043),l(-.337,-.185),l(-.337,-.513),l(-.663,.272),l(-.464,.101),l(.424,-.586),l(-.624,.173),l(-.506,.486),l(-.649,.543),l(-1.326,1.001),l(-.072,.049),l(-.167,-.393),l(-.298,-.439),l(.111,-.058),l(1.252,-.462),l(.511,-.43),l(.156,-.329),l(-.112,-.299),l(.097,-.128),l(.025,-.385),l(.006,-.613),l(-.062,-.656),l(-.332,-.897),l(.048,-.557),l(1.664,-.982),l(.396,.041),l(.685,.24),l(.679,.225),l(.547,.098),l(.47,-.347),l(.551,-.69),l(.329,-.432),l(.627,-1.08),l(.538,-1.066),l(.278,-.893),l(.291,-.707),l(.66,-.393),l(.566,-.407),l(-.017,-.504),l(-.116,-.389),l(-.064,-.259),l(-.246,-.114),l(-.902,.034),l(-1.181,.208),l(-1.357,.31),l(-.953,.308),l(-.604,.22),l(-1.657,.052),l(-.649,.018),l(-.68,.033),l(-.261,-.446),l(-.44,-1.283),l(-.297,-.866),l(-.205,-.144),l(-2.659,-.839),l(-1.523,-.253),l(-1.247,-.341),l(-.507,-.2),l(-.436,-.389),l(-.927,-1.903),l(-.94,-1.588),l(-1.087,-2.384),l(-.742,-.952),l(-.763,-.467),l(-.539,-.026),l(-1.386,.125),l(-.683,-.188),l(-1.037,-.481),l(-1.148,-.215),l(-.917,.049),l(-1.203,.109),l(-.836,.123),l(-1.854,.143),l(-.602,.136),l(-.478,.165),l(-1.193,.787),l(-.375,.282),l(-.119,.25),l(.822,-.078),l(.558,.026),l(.465,.306),l(.107,.249),l(-.167,.794),l(-1.371,.608),l(-.599,.545),l(-.667,.779),l(-.416,.543),l(-.422,.426),l(-.103,.249),l(.062,.146),l(.35,.348),l(.056,.306),l(-.123,.233),l(-.779,.339),l(-2.44,.752),l(-.438,.089),l(-.343,.016),l(-1.126,-.574),l(-.452,-.172),l(-.046,-.018),l(-.431,-.168),l(-.622,.018),l(-1.228,.297),l(-.86,-.169),l(-.34,-.129),l(-.751,-.506),l(-.74,-.156),l(-.609,.047),l(-.333,.002),l(-1.165,.487),l(-.755,.498),l(-.447,.394),l(-.604,.264),l(-.526,.163),l(-1.147,.136),l(-.867,.106),l(-.532,.075),l(-1.018,.063),l(-1.602,.11),l(-.739,-.039),l(-.984,-.082),l(-.905,-.241),l(-1.318,-.254),l(-.812,-.373),l(-1.132,-.313),l(-.623,-.331),l(-1.333,-.75),l(-.769,-.229),l(-1.423,-.022),l(-1.172,-.037),l(-.796,.077),l(-2.48,.538),l(-.673,-.113),l(-1.643,-.531),l(-.421,-.333),l(-.408,-.451),l(-.182,-.481),l(-.004,-.541),l(-.145,-.204),l(-.767,-.143),l(-.989,-.317),l(-.702,-.231),l(-1.748,-.328),l(-1.036,-.185),l(-1.028,-.098),l(-.84,-.201),l(-1.269,.96),l(-.905,.839),l(-.098,.293),l(.409,.86),l(.396,.304),l(.116,.204),l(-.163,.526),l(-.744,.572),l(-.31,.162),l(-.755,.28),l(-.562,.018),l(-.576,-.186),l(-.312,-.114),l(-.875,-.068),l(-.85,.004),l(-.719,-.083),l(-1.458,.008),l(-.699,-.113),l(-.393,-.406),l(-.694,-.755),l(-.831,-.083),l(-1.842,-.122),l(-.932,-.156),l(-.953,-.097),l(-.84,.18),l(-1.512,.476),l(-1.143,.341),l(-.787,.398),l(-.971,.692),l(-.017,.012),l(-.727,.381),l(-.603,.148),l(-1.387,.08),l(-.599,.207),l(-.799,.425),l(-.154,.044),l(-.244,.074),l(-.569,.003),l(-.228,.014),l(-.064,-.145),l(-.589,0),l(-.294,-.478),l(-.441,-.331),l(.073,-.331),l(-.625,.184),l(-.478,.368),l(-.993,-.074),l(-.882,-.073),l(-.662,-.699),l(-.588,-.552),l(-.956,-.147),l(-.331,-.625),l(-.772,-.588),l(-.698,-.441),l(-.993,.037),l(-.771,.221),l(-.993,.221),l(-.992,0),l(-.589,0),l(-.184,-.184),l(-.258,-.257),l(-.478,0),l(0,-.368),l(-.367,-.331),l(-.92,0),l(-.367,.515),l(-.331,.257),l(-.515,.257),l(-1.104,-1.434),l(-.846,-.882),l(-1.69,-2.133),l(-1.066,-1.029),l(-1.287,-.772),l(-.809,-.294),l(-1.104,-.588),l(.11,-.368),l(.515,-.11),l(0,-.441),l(-.809,.074),l(-.515,.22),l(-.772,.221),l(-.956,.515),l(-1.103,.257),l(-.772,.441),l(-.294,.294),l(-.552,-.184),l(-.441,-.037),l(-.771,.073),l(-.441,.11),l(-.331,-.184),l(.331,-.441),l(.441,-.184),l(.146,-.294),l(-.367,0),l(-.441,.221),l(-.478,-.11),l(-.405,-.294),l(-.478,-.037),l(-.184,.147),l(-.147,.147),l(-.367,-.221),l(-.295,-.368),l(-.294,-.11),l(-.331,.221),l(-.367,-.074),l(-.368,.147),l(-.44,-.11),l(-.295,.147),l(-.478,-.074),l(-.184,-.257),l(.33,-.037),l(-.073,-.331),l(.147,-.257),l(.11,-.331),l(-.515,-.22),l(-.147,-.221),l(.037,-.331),l(-.368,-.404),l(-.882,.037),l(-.625,.11),l(-.772,-.257),l(-.515,-.11),l(-.919,-.147),l(-.735,.037),l(-.11,.221),l(-.589,.184),l(-.515,.037),l(0,.294),l(-.367,.368),l(-.625,.074),l(-1.876,.073),l(-2.021,.405),l(-1.177,.037),l(-.625,.331),l(-.221,.331),l(-.331,-.073),l(-.588,.073),l(-1.545,.11),l(-.735,.11),l(-1.029,.037),l(-1.396,.405),l(-.368,.184),l(-.772,0),l(-.515,0),L(537.304,80),l(-.33,.074),l(-.185,.515),l(.074,.441),l(.294,.221),l(-.294,.074),l(0,.331),l(1.065,.11),l(.956,.294),l(-.11,.257),l(-.515,.073),l(-1.103,-.147),l(-.698,.184),l(-.662,.515),l(.146,.257),l(.441,.478),l(-.331,.294),l(-.588,.147),l(-.735,.368),l(-.467,.067),l(.164,.274),l(.239,0),l(.377,.137),l(-.068,.171),l(.377,.137),l(.651,.069),l(.274,.308),l(1.165,.171),l(.24,.24),l(-.138,.686),l(-.137,.309),l(-1.577,.411),l(-.959,-.034),l(-.343,-.343),l(-.24,0),l(-.171,.309),l(-.651,.343),l(-.411,-.171),l(-.754,-.137),l(-.926,-.309),l(-.274,-.548),l(-.754,-.103),l(-1.062,.103),l(-.137,.412),l(-.617,.068),l(-.651,-.411),l(-.65,-.035),l(-.823,-.068),l(-.514,.377),l(-.377,.343),l(-.274,.274),l(-.686,.171),l(-.411,-.24),l(-.686,-.137),l(-.582,-.548),l(-.72,-.068),l(.034,.24),l(.205,.48),l(-.239,.274),l(-.274,-.137),l(-.068,-.583),l(-.411,-.274),l(-.789,-.343),l(-.582,-.206),l(0,-.343),l(-.96,-.171),l(-.617,.069),l(-.788,-.035),l(-.411,-.514),l(-.411,-.069),l(-.617,.24),l(-.273,.137),l(-.651,.137),l(-.309,-.274),l(-.479,0),l(-.651,-.069),l(-.515,.309),l(-.548,.343),l(-.788,.377),l(-.549,.068),l(-.514,.171),l(-.309,.309),l(-.172,.24),L(509.58,87.5),l(-.479,.206),l(.068,.445),l(.171,.411),l(-.068,.446),l(-.411,.24),l(-.651,0),l(-.514,-.411),l(-.48,-.548),l(-.514,-.24),l(-.411,.069),l(-.103,.308),l(-.343,.549),l(-.823,.137),l(-.205,1.303),l(.343,.171),l(.239,.274),l(-.239,.206),l(-.446,.274),l(-.65,1.165),l(1.37,.343),l(.138,.377),l(-.068,.309),l(.514,.514),l(.103,-.343),l(.583,.206),l(.343,-.034),l(.514,.034),l(.515,.445),l(.479,.206),l(.343,.514),l(.96,1.131),l(-.138,.103),l(-.445,-.103),l(-.309,-.103),l(-.343,.103),l(.068,.308),l(.857,.377),l(.616,.19),l(-.167,.2),l(-.399,.28),l(-.38,.12),l(-.12,0),l(0,.14),l(0,.22),l(-.14,.08),l(-.3,-.2),l(-.34,.2),l(-.399,.26),l(-.64,.12),l(-.319,.08),l(-.26,-.08),l(-.181,-.08),l(-.1,.04),l(-.06,.16),l(.12,.26),l(-.221,.2),l(-.18,.3),l(-.18,.319),l(-.319,.42),l(-.18,.28),l(-.32,.16),l(-.34,.24),l(-.14,.32),l(.08,.2),l(.34,.2),l(.319,.04),l(.18,.3),l(.24,.04),l(.239,.34),l(.28,.419),l(-.06,.38),l(-.101,.2),l(.061,.16),l(.1,-.2),l(.1,-.34),l(.12,-.22),l(.16,-.02),l(-.141,.439),l(-.22,.36),l(.061,.4),l(.12,.1),l(-.16,.2),l(-.04,.4),l(.38,.24),l(.119,.1),l(.12,.52),l(.28,.06),l(.359,.42),l(.2,.28),l(.439,.419),l(.18,.36),l(.359,.06),l(.047,.146),l(-.292,.449),l(-.496,.284),l(-.283,.094),l(-.308,.331),l(-.165,.213),l(-.379,.047),l(-.449,-.142),l(-.591,-.094),l(0,-.308),l(-.283,-.284),l(-.118,0),l(-.095,-.118),l(-.473,-.284),l(-.142,-.189),H(504.5,0),l(-.213,.047),L(504.264,107),l(-.229,.023),l(-.126,-.189),l(-.236,-.071),l(-.236,.023),l(-.284,-.047),l(-.26,-.094),l(-.284,-.166),l(-.354,-.284),l(.118,-.307),l(.118,-.189),l(-.118,-.142),l(-.354,-.024),l(-.378,0),l(-.213,-.166),l(-.189,-.118),l(-.212,0),l(-.143,-.094),l(-.236,-.118),l(-.118,0),l(-.095,.071),l(-.142,.166),l(-.188,-.142),l(-.261,-.071),l(-.354,0),l(-.213,.071),l(-.094,.095),l(-.166,.166),l(-.283,0),l(-.261,.118),l(-.212,-.071),l(-.261,-.118),l(.048,-.118),l(.142,-.118),l(-.308,-.119),l(-.236,-.118),l(-.354,-.071),l(-.52,-.118),l(-.284,-.213),l(-.095,-.142),l(-.354,-.166),l(-.283,.047),l(-.189,0),l(-.401,-.166),l(-.544,0),l(-.426,.095),l(-.307,0),l(-.355,-.047),l(-.307,-.071),l(-.261,-.095),l(-.212,-.166),l(-.213,0),l(-.331,0),l(-.189,-.047),l(-.188,-.094),l(-.284,-.142),l(-.283,-.095),l(-.449,-.023),l(-.402,-.023),l(-.07,-.047),l(-.284,-.047),l(-.26,.166),l(-.112,.309),l(-1.421,-1.012),l(-1.188,-.842),l(-.817,-.385),l(-.62,-.084),l(-.373,-.157),l(-.509,-.5),l(-.236,-.057),l(-.338,.159),l(-.329,-.042),l(-.347,-.515),l(-.795,-.674),l(.595,-.188),l(.519,-.001),l(.445,-.073),l(.1,-.519),l(.359,-.476),L(485,97.85),l(.526,-.03),l(.516,-.131),l(-.432,-.532),l(-.654,-.273),l(-.474,-.36),l(.243,-.116),l(.367,-.03),l(.82,-.117),l(.715,-.348),l(1.244,-.436),l(-.196,-.375),l(-.076,-.058),l(-.799,.088),l(-1.312,.175),l(.012,-.126),l(.021,-.112),l(0,-.224),l(.09,-.514),l(.224,-.089),l(.357,-.022),l(.38,-.134),l(.223,-.179),l(.022,-.179),l(.269,0),l(.736,-.045),l(.805,.157),l(.335,-.134),l(0,-.156),l(.156,-.291),l(.156,-.491),l(-.066,-.067),l(-.09,-.201),l(-.045,-.179),l(-.044,-.134),l(-.179,-.067),l(-.156,-.067),l(.044,-.224),l(.045,-.179),l(.246,0),l(.312,0),l(.134,-.156),l(-.29,-.044),l(-.269,-.089),l(-.223,-.246),l(.312,-.044),l(.357,-.201),l(.156,-.067),l(.201,-.089),l(.045,-.224),l(-.09,-.179),l(-.111,-.089),l(.022,-.179),l(.089,-.111),l(-.156,-.089),l(-.201,.089),l(-.134,.044),l(-.269,0),l(-.357,-.156),l(-.111,-.224),l(-.469,-.022),l(-.291,-.089),l(-.201,-.179),l(-.357,.067),l(-.268,-.112),l(-.469,-.112),l(-.425,-.067),l(-.29,-.268),l(-.224,.067),l(-.045,.157),l(-.29,.134),l(-.224,-.112),l(-.179,-.179),l(-.402,-.044),l(-.022,-.134),l(-.201,-.179),l(0,-.134),l(-.268,-.179),l(-.111,-.156),l(-.357,.067),l(-.536,.134),l(-.269,0),l(-.312,.112),l(-.425,.112),l(-.179,-.156),l(-.179,-.022),l(-.201,.067),l(-.469,-.291),l(-.268,-.067),l(-.47,.067),l(-.268,.179),l(-.291,-.29),l(-.179,-.089),l(-.089,-.224),l(.089,-.179),l(-.089,-.246),l(-.224,-.268),l(0,-.357),l(-.402,0),l(0,-.312),l(-.38,-.022),l(-.514,.067),l(-.156,-.112),l(-.647,0),l(-.269,-.29),l(-.111,-.357),l(-.134,-.291),l(.312,-.134),l(.312,.022),l(0,-.268),l(-.312,-.156),l(-.246,-.156),l(-.134,-.201),l(-.246,-.335),l(-.312,-.134),l(-.201,-.268),l(-.514,.134),l(-.692,-.112),l(-.67,.223),l(-.536,.022),l(-.536,-.246),l(-.134,.268),l(-.179,.291),l(-.313,.156),l(-.469,-.022),l(-.357,-.089),M(527.156,37.071),l(-.59,-.14),l(-.955,-.21),l(-1.128,-.139),l(-.739,.054),l(-.959,.037),l(-.732,.143),l(-.411,-.105),l(-1.025,-.476),l(.737,-.303),l(1.21,-.429),l(.654,-.09),l(1.549,-.002),l(.135,-.357),l(-.728,-.338),l(.351,-.269),l(.419,-.144),l(.676,-.02),l(.852,-.181),l(-.25,-.161),l(-.526,-.233),l(1.539,-.895),l(.513,-.129),l(.226,.073),l(.892,.017),l(.981,-.535),l(.863,-.333),l(1.02,-.261),l(.975,-.15),l(1.305,-.113),l(1.274,.053),l(.738,-.15),l(.786,-.615),l(.273,.018),l(.873,-.132),l(1.722,.276),l(.471,-.038),l(1.93,-.321),l(.958,-.039),l(2.184,-.247),l(1.864,-.116),l(.771,-.564),l(.376,-.152),l(2.511,-.137),l(1.965,-.079),l(.471,.131),l(.442,.225),l(.154,.357),l(-.812,.47),l(-.541,.169),l(-1.346,.319),l(-2.609,.433),l(-4.329,.49),l(-2.187,.281),l(-2.122,.299),l(-1.842,.316),l(-2.102,.242),l(-.941,.203),l(-.288,.274),l(.644,.216),l(-.422,.217),l(-.943,.361),l(-.527,.037),l(-2.047,-.068),l(-.432,.18),l(-.119,.233),l(.621,.249),l(-.331,.233),l(-1.206,.448),l(-.402,-.142),l(-.752,-.087),l(-.592,.304),l(.877,.283),l(.052,.319),l(-1.094,.657),l(-.822,.284),M(517.491,38.443),l(1.041,-.37),l(.512,-.299),l(.428,-.212),l(1.426,-.021),l(1.316,-.249),l(.987,-.02),l(1.412,-.091),l(.551,.069),l(.988,.227),l(-.063,.176),l(-.463,.528),l(-.302,.158),l(-.697,.071),l(-.443,.228),l(-.233,.385),l(.006,.818),l(.445,1.039),l(.957,.826),l(.505,.308),l(.775,.307),l(1.162,.392),l(-.028,.282),l(-2.62,-.089),l(-.844,.053),l(-.917,.326),l(-.596,.086),l(-.676,-.494),l(-.382,-.034),l(-1.091,.088),l(-.673,-.102),l(-.031,-.359),l(1.347,-.362),l(.073,-.24),l(-.102,-.017),l(-.944,-.273),l(-1.442,-.411),l(-1.519,-.17),l(-.33,.156),l(-.624,.122),l(-.681,-.033),l(-.625,-.396),l(-.114,-.415),l(.229,-.312),l(.39,-.209),l(.344,-.036),l(.318,.104),l(.65,.016),l(.518,-.192),l(1.121,-.768),l(.243,-.35),l(-.322,0),l(-.981,-.243),M(.125,56.089),l(0,-.562),l(0,-.576),l(0,-.157),l(0,-1.292),V(0,51.28),l(0,-2.222),l(0,-2.12),l(2.917,.61),l(1.693,.598),l(.933,.181),l(3.819,.99),l(1.059,.395),l(.204,.149),l(.074,.214),l(.445,.429),l(.406,.789),l(-.209,.428),l(1.034,.8),l(.982,.26),l(-.02,-.18),l(.297,-.148),l(-.051,-.196),l(-.638,-.392),l(.016,-.542),l(.077,-.296),l(1.026,-.183),l(.984,.278),l(.573,.098),l(.767,.064),l(1.003,-.117),l(.933,.13),l(.93,.425),l(.694,.359),l(1.676,.684),l(.32,.081),l(.584,.179),l(.43,.211),l(.09,.13),l(-.343,.033),l(-1.026,-.096),l(-.364,.034),l(-.808,.798),l(-1.539,-.468),l(.138,.178),l(.438,.227),l(.465,.324),l(-1.177,-.111),l(-1.008,.05),l(-1.202,-.387),l(-.708,-.096),l(.135,.129),l(.289,.259),l(.084,0),l(.561,.259),l(.205,.307),l(.131,.453),l(-.056,.339),l(-.814,.05),l(-.805,.065),l(-.189,.161),l(.5,.865),l(-.115,.272),l(-1.04,-.078),l(-.397,.081),l(-1.193,-.174),l(-.264,-.352),l(-1.795,-.51),l(-.253,.273),L(9.85,55.79),l(-.276,-.161),l(-.265,-.403),l(.174,-.178),l(-.321,-.42),l(-.87,-.339),l(-.565,-.063),l(-.762,.034),l(-.858,.229),l(-1.343,.084),L(3.57,54.443),l(.059,-.325),l(-.155,-.325),l(-.618,-.487),L(1.958,53.43),l(-.45,.409),l(-.655,1.228),l(-.166,.715),l(-.562,.307),
+N(449.401,22.792),l(-1.596,-.014),l(-2.019,-.016),l(-1.739,.063),l(-.764,.061),l(-1.972,-.316),l(.812,-.456),l(.826,-.172),l(.735,-.453),l(1.148,-.588),l(1.354,.691),l(.998,.105),l(1.116,-.088),l(.9,.084),l(.704,.341),l(.865,-.323),l(.53,-.454),l(.868,-.306),l(-.311,.823),l(.086,.32),l(.851,-.001),l(.991,-.495),l(1.061,-.261),l(.803,.128),l(.559,.472),l(.705,.041),l(2.007,-.111),l(1.543,.189),l(.551,.376),l(-.507,.201),l(-2.004,.622),l(-1.623,.38),l(-1.069,.062),l(-.976,.14),l(-1.324,.366),l(-6.271,-.097),l(-.033,-.377),l(1.492,-.26),l(.702,-.677),M(430.027,22.752),l(.068,.697),l(.252,.119),l(1.694,.155),l(.221,-.377),l(.13,-.418),l(.573,-.141),l(.523,-.041),l(.949,.477),l(1.192,.771),l(.798,.235),l(.568,-.218),l(-.222,-.296),l(-.46,-.356),l(-.327,-.477),l(-.04,-.22),l(.91,-.407),l(1.001,.103),l(.485,.18),l(1.278,.018),l(.586,.179),l(-.08,.419),l(-.169,.298),l(.104,.159),l(.549,.118),l(.743,-.338),l(.44,-.1),l(.662,.396),l(.678,.335),l(.785,.156),l(.948,.117),l(1.672,.429),l(.498,.234),l(-1.305,.277),l(-1.981,.218),l(-.696,.293),l(-.731,1.144),l(-.885,.885),l(-1.243,.117),l(-.766,.535),l(-.455,.589),l(-.029,.378),l(-.786,.209),l(-.556,-.018),l(-1.593,-.355),l(-1.883,-.507),l(-1.365,-.568),l(.651,-.364),l(1.565,-.041),l(1.788,-.137),l(.944,-.386),l(-.652,-.249),l(-1.822,.139),l(-1.566,.118),l(-1.694,.042),l(-.502,-.519),l(.959,-.06),l(1.371,-.215),l(2.149,-.314),l(1.547,-.45),l(-2.525,-.405),l(-.881,-.292),l(-.589,.138),l(-1.036,.646),l(-1.069,.293),l(-.562,.059),l(-1.236,-.172),l(-.338,-.174),l(-1.05,-.368),l(-.807,-.233),l(-.698,-.41),l(1.698,-.396),l(-.817,-.571),l(-1.359,.319),l(-.467,-.078),l(-.924,-.751),l(.812,-.36),l(.516,-.021),l(1.075,-.122),l(1.017,.038),l(.577,-.061),l(1.188,-.042),M(425.42,68.82),l(-.148,-.21),l(-.629,.132),l(-.134,-.026),l(-.249,-.237),l(-.239,-.343),l(.188,-.158),l(-.143,-.291),l(-.361,.185),l(-.062,.29),l(.113,.237),l(-.147,.105),l(-.339,.316),l(-.086,.185),l(-.521,.105),l(-.533,-.131),l(-.781,.342),l(.057,.131),l(-.502,.289),l(-.498,.263),l(-1.658,.813),L(418.697,71),l(-.274,.052),l(-.271,0),l(-2.111,.209),l(-.188,-.236),l(-.33,-.131),l(-.183,.209),L(414.976,71),l(.384,-.184),l(-.935,-.236),l(-.794,-.341),l(-.574,-.052),l(-.479,-.578),l(.292,-.368),l(-.126,-.21),l(.34,.026),l(.085,.316),l(.284,-.21),l(.667,.263),l(-.244,-.197),l(.127,-.145),l(.5,-.092),l(-.479,.026),l(-.245,.105),l(-.263,-.184),l(-.111,-.132),l(.579,-.276),l(.455,-.185),l(-.482,.066),l(-.317,-.132),l(.441,-.237),l(.083,-.237),l(-.661,.475),l(-.036,-.264),l(-.265,-.171),l(.005,.211),l(.076,.171),l(-.419,.158),l(-.41,0),l(.014,-.277),l(-.15,.264),l(-.235,-.079),l(-.2,-.448),l(.658,-.449),l(.08,.436),l(.068,-.317),l(.438,.158),l(.211,-.092),l(-.166,-.106),l(.674,-.079),l(.479,-.251),l(-.611,.159),l(-.48,0),l(-.335,-.159),l(.196,-.132),l(.387,-.146),l(.196,-.471),l(-.821,-.014),l(-.546,.094),l(-.406,-.309),l(.368,-.14),l(.643,-.404),l(-.212,-.186),l(-.445,.063),l(-.399,-.062),l(-.211,-.217),l(.284,-.482),l(.619,-.265),l(-.595,-.248),l(-.225,-.374),l(.272,-.188),l(-.089,-.687),l(.106,-.094),l(.594,-.063),l(.392,-.045),l(.455,.044),l(.082,-.11),l(-.255,-.134),l(-.425,-.118),l(-.228,-.17),l(.098,-.188),l(.211,-.125),l(.575,-.064),l(.511,-.346),l(.567,.109),l(.498,-.111),l(.179,-.55),l(.835,.093),l(.668,.125),l(.571,-.252),l(-.989,-.234),l(-.266,-.314),l(.236,-.143),l(1.151,.014),l(.851,-.08),l(.538,.219),l(.06,-.473),l(.477,-.27),l(.297,-.333),l(.78,-.175),l(.438,.189),l(.138,.079),l(.5,-.476),l(.869,-.413),l(.084,-.429),l(.437,-.398),l(1.497,-.735),l(.335,-.192),l(.235,-.048),l(.481,.175),l(.335,-.001),l(.319,-.224),l(-.225,-.223),l(.141,-.128),l(.488,-.224),l(.457,-.305),l(.481,-.483),l(.245,.064),l(.676,-.017),l(-.067,-.612),l(-.471,-.112),l(.434,-.405),l(.511,-.648),l(.841,.063),l(.047,-.341),l(.096,-.228),l(.954,-.116),l(-.396,-.357),l(-.327,-.097),l(-.193,-.146),l(.164,-.245),l(.313,-.246),l(.464,.048),l(.888,-.067),l(-.543,-.522),l(.249,-.082),l(.435,-.034),l(.707,-.263),l(.592,-.067),l(.308,-.208),l(-.358,-.064),l(-.105,-.187),l(.289,-.1),l(.616,-.33),l(.557,.032),l(.351,.098),l(.434,-.264),l(.369,-.031),l(.035,-.176),l(-.421,-.321),l(.627,-.232),l(.146,-.275),l(.336,.01),l(.359,.297),l(.397,.082),l(.526,-.381),l(-.281,-.656),l(.438,.065),l(.698,-.058),l(.207,-.174),l(-.604,-.315),l(.411,-.224),l(.816,-.251),l(.093,-.257),l(.261,-.194),l(.73,-.135),l(.074,.067),l(.274,-.051),l(.102,-.067),l(.492,-.67),l(.521,.25),l(.96,-.153),l(.411,.133),l(.265,.1),l(.646,.065),l(.536,-.419),l(.747,-.84),l(.286,-.085),l(.16,.168),l(-.178,.286),l(-.182,.587),l(.791,-.454),l(.403,-.572),l(.819,-.085),l(.688,-.439),l(.437,.117),l(.665,.353),l(.567,-.643),l(.318,-.272),l(.976,.1),l(1.019,.506),l(.197,-.152),l(.479,-.595),l(.709,-.444),l(.283,-.052),l(.562,.067),l(.338,-.308),l(.469,-.291),l(.619,-.189),l(1.2,-.173),l(.984,.015),l(-1.337,.755),l(-.24,.273),l(-.107,.646),l(.42,.084),l(.691,-.579),l(1.53,-.941),l(.764,-.275),l(.028,.257),l(-.004,.905),l(.704,-.172),l(.438,-.239),l(.855,-.874),l(.959,-.104),l(.586,-.001),l(.216,.154),l(-.383,.257),l(-.335,.257),l(.074,.239),l(-.319,.41),l(.921,.118),l(.221,-.443),l(.902,-.651),l(.421,.017),l(1.682,.561),l(.667,.135),l(.935,.015),l(.649,.237),l(.1,.272),l(-1.216,.393),l(-1.148,.036),l(-2.233,-.047),l(.193,.084),l(.585,.304),l(1.058,.284),l(.383,.302),l(.728,-.052),l(.679,-.102),l(.831,.032),l(.708,.25),l(-.404,.288),l(-.514,.204),l(-.824,.021),l(-.77,.121),l(-1.023,.49),l(-1.013,.672),l(-.689,.303),l(-.001,-.2),l(.096,-.367),l(.874,-.923),l(-.044,-.268),l(-.88,-.348),l(-.672,-.114),l(-1.125,-.431),l(-.556,.003),l(-.555,.07),l(-2.004,.095),l(-.865,.374),l(-.567,.422),l(-.26,.437),l(-.079,.501),l(-.188,.5),l(-1.25,.472),l(-.582,.368),l(-.31,-.148),l(-1.288,-.492),l(-1.657,.225),l(-1.411,-.042),l(-.536,-.13),l(-.861,-.595),l(-.994,-.612),l(-.486,-.082),l(-.355,.103),l(-1.012,.656),l(-.114,-.063),l(-.41,-.031),l(-.865,.038),l(-.281,1.066),l(-.164,.449),l(-.271,.25),l(-.323,-.015),l(-.67,-.129),l(-.972,-.144),l(-1.731,-.389),l(-.168,.183),l(-.021,.663),l(-.083,.199),l(-.318,.299),l(-.695,.004),l(-.871,-.095),l(-.279,-.015),l(-.465,.167),l(-1.484,1.176),l(.05,.213),l(.171,.507),l(-.188,.345),l(-.615,.346),l(-1.325,.919),l(-.687,.442),l(-.981,.021),l(-.121,.244),l(-.06,.971),l(-.32,.598),l(-.383,.469),l(-.574,.499),l(-.495,.545),l(.339,.125),l(.381,.269),l(.252,.587),l(-.17,.176),l(-.44,.177),l(-.733,.036),l(-.932,-.027),l(-.618,.114),l(-.613,.257),l(-.617,.479),l(-.389,.492),l(-.077,.664),l(-.349,1.093),l(.65,.729),l(-.335,1.171),l(.582,.39),l(.715,.186),l(.297,.243),l(-.432,.67),l(-.941,.16),l(.571,.735),l(.26,.427),l(-.249,.346),l(-.029,.532),l(-.769,.384),l(-.414,.013),l(-.117,.423),l(-.506,.224),l(.22,.831),l(-.324,.737),l(-.41,.013),l(-.15,-.342),l(-.293,-.132),
+N(464.349,47.431),l(-.024,.283),l(-.258,.465),l(.116,.529),l(.563,.212),l(.872,.194),l(.964,.506),l(.543,.392),l(-.421,.364),l(-1.303,.777),l(-.328,.296),l(1.402,1.394),l(.772,1.195),l(-.196,.356),l(-.44,.535),l(-.187,.403),l(-.029,.37),l(.37,.509),l(1.286,1.395),l(.151,.253),l(-.057,.254),l(-.542,.415),l(-.327,.191),l(.439,.33),l(1.357,.499),l(.703,.265),l(.436,.392),l(.161,.346),l(-.105,.409),l(-.43,.536),l(-.591,.536),l(-1.385,.758),l(-1.983,1.195),l(-1.946,1.145),l(-.174,.285),l(-.935,.206),l(-1.195,.188),l(-.149,.181),l(-.506,-.217),l(-.518,.146),l(-.052,.134),l(-.688,-.07),l(-.193,-.108),l(.057,-.387),l(-.241,.527),l(-.268,.14),l(-.633,.047),l(-.328,.03),l(-.1,.267),l(-.589,-.326),l(-.29,.275),l(-.676,.064),l(-.34,.178),l(-.052,-.127),l(-.504,.108),l(-.504,.108),l(-.211,.266),l(-.311,-.152),l(-.672,.126),l(-.04,-.114),l(-.658,.215),l(-.547,.013),l(-.482,.025),l(-.487,-.253),l(-.223,-.274),l(.273,-.34),l(-.846,.017),l(-.517,.177),l(.065,-.382),l(-.446,.076),l(-.644,-.271),l(-.409,-.061),l(-.415,-.231),l(-.26,-.403),l(-.036,-.714),l(.158,-.374),l(.05,-.436),l(.09,-.234),l(-.347,-.483),l(-.431,-.375),l(.3,-.658),l(-.02,-.392),l(-.305,-.204),l(-.37,-.093),l(.062,-.299),l(.295,-.331),l(.637,-.285),l(.144,-.189),l(-.113,-.331),l(.382,-.143),l(.633,.125),l(.274,.063),l(.483,-.222),l(.303,-.427),l(.104,-.459),l(.207,-.27),l(.301,.316),l(.27,0),l(1.678,-1.114),l(1.142,-.654),l(.835,-.576),l(1.026,-.401),l(.874,.03),l(.06,-.304),l(-.13,-.144),l(-.357,-.16),l(.119,-.371),l(.146,-.387),l(-.329,-.274),l(-1.139,-.095),l(-.836,-.451),l(-.594,-.024),l(-.113,-.234),l(-.327,-.307),l(-.208,-.535),l(.5,-.8),l(-.028,-.392),l(-.405,-.685),l(-.507,-.637),l(.004,-.526),l(.062,-.413),l(-.77,-.558),l(-.508,-.229),l(-1.583,-.207),l(-1.085,-.292),l(-1.286,-.658),l(-.616,-.463),l(-.146,-.033),l(1.012,-.656),l(.355,-.103),l(.486,.082),l(.994,.612),l(.861,.595),l(.536,.13),l(1.411,.042),l(1.657,-.225),l(1.288,.492),l(.31,.148),l(.582,-.368),l(1.25,-.472),l(.188,-.5),l(.079,-.501),l(.26,-.437),l(.567,-.422),l(.865,-.374),l(2.004,-.095),l(.555,-.07),l(.556,-.003),l(1.125,.431),l(.672,.114),l(.88,.348),l(.044,.268),l(-.874,.923),l(-.096,.367),l(.001,.2),
+N(453.795,53.873),l(-.23,-.004),l(-.975,.164),l(-.447,.098),l(-.902,-.387),l(-.339,-.178),l(-.347,.114),l(-.641,.374),l(-.542,.599),l(-.309,.146),l(-.658,.018),l(-.545,.355),l(.325,.241),l(.146,.192),l(-.355,.273),l(-.907,.401),l(.317,.271),l(.385,.159),l(.33,.302),l(-.749,.367),l(-.405,.43),l(-.368,.461),l(-1.591,.669),l(-.699,.286),l(-.456,-.205),l(-.326,.08),l(-.538,.539),l(-.646,.144),l(-.663,.348),l(-.217,.284),l(-.191,.142),l(-.746,.033),l(-.221,-.031),l(.151,.535),l(-.484,.425),L(439,61.33),l(.216,.533),l(.243,.25),l(-.244,.955),l(-.498,.063),l(-.217,.204),l(.189,.757),l(-.009,.544),l(.381,.444),l(-.287,.175),l(.926,.082),l(.225,.187),l(.499,-.082),l(.652,.594),l(.428,.314),l(.86,.163),l(.198,.406),l(.32,.139),l(.014,.335),l(-.391,0),l(-.508,.323),l(-.861,.3),l(-.492,.162),l(-.521,-.012),l(-.146,-.115),l(-.805,-.115),l(-.66,-.173),l(-.634,-.069),l(-.137,.104),l(-.446,-.115),l(-.257,.3),l(.819,-.069),l(.497,.265),l(.591,.046),l(.205,.208),l(.481,.069),l(.021,-.127),l(.616,.069),l(.258,-.196),l(.297,.011),l(.481,-.104),l(.226,.081),l(.026,-.15),l(.164,0),l(.214,.15),l(-.029,.081),l(-.722,.023),l(.219,.288),l(-.792,.265),l(-.168,.288),l(-.386,.08),l(-.089,-.092),l(.042,-.161),l(-.014,-.276),l(-.258,.023),l(-.091,.46),l(-.501,.333),l(-.248,.058),l(-.36,-.069),l(-.005,.218),l(-1.418,.035),l(.156,.458),l(.513,.258),l(.066,.609),l(-.109,.33),l(-.281,.051),l(-.308,-.064),l(.038,.429),l(.29,.081),l(-.126,.268),l(.24,.228),l(-.365,.364),l(-.222,.455),l(-.041,.56),l(-.766,1.043),l(-.58,.71),l(-.165,-.062),l(-.442,-.236),l(-.614,.193),l(-1.262,-.089),l(-.106,.311),l(-.252,-.083),l(-.518,.264),l(-.295,.517),l(.356,.336),l(-.376,.374),l(-.48,-.129),l(-.173,-.037),l(-1.158,.269),l(-1.11,-.116),l(-.001,-.142),l(.19,-.078),l(-.062,-.181),l(.281,-.297),l(-.638,-.53),l(-.413,-.479),l(-.22,-.272),l(.529,-.116),l(-.082,-.312),l(.421,.077),l(.147,-.245),l(-.19,-.234),l(-.285,.013),l(-.292,-.377),l(-.527,-.221),l(-.63,-.99),l(-.286,0),l(.011,-.408),l(-.256,-.364),l(-.348,-.298),l(.221,-.484),l(.206,-.341),l(-.418,-.154),l(-.405,.102),l(-.055,-.288),l(-.412,.051),l(-.18,-1.207),l(-.225,-.131),l(.311,-.355),l(.293,.132),l(.15,.342),l(.41,-.013),l(.324,-.737),l(-.22,-.831),l(.506,-.224),l(.117,-.423),l(.414,-.013),l(.769,-.384),l(.029,-.532),l(.249,-.346),l(-.26,-.427),l(-.571,-.735),l(.941,-.16),l(.432,-.67),l(-.297,-.243),l(-.715,-.186),l(-.582,-.39),l(.335,-1.171),l(-.65,-.729),l(.349,-1.093),l(.077,-.664),l(.389,-.492),l(.617,-.479),l(.613,-.257),l(.618,-.114),l(.932,.027),l(.733,-.036),l(.44,-.177),l(.17,-.176),l(-.252,-.587),l(-.381,-.269),l(-.339,-.125),l(.495,-.545),l(.574,-.499),l(.383,-.469),l(.32,-.598),l(.06,-.971),l(.121,-.244),l(.981,-.021),l(.687,-.442),l(1.325,-.919),l(.615,-.346),l(.188,-.345),l(-.171,-.507),l(-.05,-.213),l(1.484,-1.176),l(.465,-.167),l(.279,.015),l(.871,.095),l(.695,-.004),l(.318,-.299),l(.083,-.199),l(.021,-.663),l(.168,-.183),l(1.731,.389),l(.972,.144),l(.67,.129),l(.323,.015),l(.271,-.25),l(.164,-.449),l(.281,-1.066),l(.865,-.038),l(.41,.031),l(.114,.063),l(.146,.033),l(.616,.463),l(1.286,.658),l(1.085,.292),l(1.583,.207),l(.508,.229),l(.77,.558),l(-.062,.413),l(-.004,.526),l(.507,.637),l(.405,.685),l(.028,.392),l(-.5,.8),l(.208,.535),l(.327,.307),l(.113,.234),
+N(238.107,361.753),l(.515,-.688),l(.859,0),l(1.202,-.515),l(-.171,-.858),l(-3.091,.344),l(-2.061,.515),l(-1.545,-.344),l(-.515,-.858),l(1.374,-.515),l(1.202,-.172),l(3.091,-.172),l(1.374,-.515),l(-.172,-1.03),l(.859,-.516),l(0,-1.374),l(-1.326,-1.054),l(.982,-.491),l(1.202,-.344),l(1.545,-.172),l(.858,.344),l(.858,.858),l(1.03,1.374),l(1.374,1.202),l(1.03,1.373),l(-.515,2.061),l(-1.545,.516),l(-1.545,.858),l(-3.434,.172),l(-3.435,0),M(172.686,360.379),l(3.091,-.687),l(3.262,0),l(5.495,0),l(2.919,1.202),l(-2.748,.687),l(-3.759,-.337),l(-5.169,-.35),l(-3.091,-.516),M(65.13,371.085),l(1.194,-.265),l(1.393,.133),l(.796,.464),l(-1.99,0),l(-1.393,-.332),M(277.944,379.954),l(.515,-1.202),l(2.404,-1.03),l(2.748,-.172),l(2.061,-.171),l(1.545,-.859),l(.343,-1.545),l(1.374,-.687),l(3.091,-.688),l(4.464,0),l(2.404,0),l(3.434,.688),l(.687,1.889),l(0,1.202),l(-2.061,1.201),l(-11.848,1.03),l(-11.161,.344),M(36.004,376.434),l(1.202,-.858),l(2.061,-.343),l(4.121,.515),l(4.293,1.374),l(-.887,.482),l(-2.719,.204),l(-4.293,-.515),l(-3.777,-.859),M(777.843,385.46),l(3.823,.391),l(.899,.029),l(.322,.234),l(1.202,.294),l(2.329,.088),l(2.126,-.003),l(5.218,.329),l(3.046,.348),l(1.144,-.059),l(.762,.029),l(1.411,.37),l(0,2.175),l(0,2.223),l(0,2.222),l(0,2.223),l(0,2.222),l(0,1.551),l(-.041,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.182,0),l(-.041,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.223,0),l(-2.182,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.223,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),l(-2.222,0),H(9.014,0),H(6.792,0),H(4.569,0),H(2.347,0),H(.125,0),l(0,-1.544),l(0,-2.222),l(0,-2.223),l(0,-2.222),l(0,-2.223),l(0,-2.223),l(3.941,.297),l(10.131,0),l(7.898,.516),l(6.868,.172),l(8.586,.858),l(6.353,.515),l(8.757,.172),l(4.808,-.172),l(6.525,-.172),l(2.404,.344),l(3.606,-.858),l(16.312,.344),l(4.638,-.013),l(1.143,-.467),l(-14.366,-.551),l(-9.616,-.688),l(-2.232,0),l(-3.091,-.172),l(-.858,-.171),l(-1.545,-.344),l(-.498,-.455),l(.155,-1.434),l(.687,-.858),l(1.889,-.688),l(-3.263,-.687),l(-2.232,-.344),l(-.953,-.503),l(1.589,-.521),l(-2.891,-.6),l(-3.074,-.208),l(-.104,-.261),l(2.857,-.312),l(2.748,0),l(6.525,.343),l(3.434,0),l(2.576,.344),l(4.636,-1.202),l(2.404,-.687),l(-2.748,-1.03),l(-3.778,-.858),l(-4.391,-.405),l(-2.649,-.625),l(-3.262,-.172),l(-2.919,0),l(-2.919,-.858),l(2.576,-.516),l(1.28,-.342),l(-1.28,-.517),l(-2.919,.516),H(52.66,0),l(-1.717,-.344),L(49.054,373),l(.343,-1.202),l(1.956,-.07),l(1.65,-.273),l(4.98,0),l(4.626,.493),l(3.272,.709),l(1.374,.172),l(4.465,-.858),l(4.089,.243),l(0,-.729),l(.066,-.597),l(-2.255,-.73),l(-2.93,-.075),l(-2.576,-.344),l(2.404,-.687),l(4.121,.171),l(1.717,-.343),l(3.091,-.516),l(3.09,-.343),l(5.667,-.688),l(4.465,-.343),l(3.949,-.516),l(4.464,-.515),l(2.748,.343),l(5.667,0),l(4.464,.258),l(4.121,-.515),l(7.898,.343),l(7.555,-.687),l(3.091,.172),l(1.545,-.688),l(1.545,.516),l(5.323,-.172),l(.121,-.87),l(1.758,-.293),l(1.433,.325),l(.586,.554),l(-.423,.813),l(2.364,.329),l(1.38,-.264),l(-.006,-.938),l(1.438,-.267),l(1.48,.267),l(.212,1.036),l(-1.009,.423),l(.521,.781),l(3.024,-.351),l(1.889,.343),l(3.434,.172),l(2.576,-.687),l(3.778,.086),l(4.868,.33),l(2.344,.013),l(1.449,.109),l(.693,-.53),l(-.611,-.285),l(-2.202,-.122),l(1.305,-.285),l(.041,-.408),l(-.367,-.245),l(-1.386,.123),l(-.693,.04),l(-.856,-.979),l(-2.178,-.337),l(-.676,-.193),l(-.182,-.665),l(4.056,.462),l(3.874,-.245),l(-1.224,-.489),l(-6.035,-.163),l(-.774,-.082),l(-.653,-.489),l(0,-.815),l(2.301,.105),l(.839,.67),l(5.301,-.204),l(7.253,.736),l(2.404,-.172),l(3.262,-.172),l(4.464,.172),l(3.263,.172),l(2.061,-.516),l(.687,-.687),l(1.202,0),l(.344,1.03),l(2.576,.343),l(2.404,-.343),l(.858,.515),l(3.435,.688),l(3.777,.343),l(2.061,.172),l(1.03,-.515),l(.687,-1.202),l(2.747,-.172),l(1.03,.515),l(1.717,.688),l(3.091,-.172),l(3.434,.258),l(4.121,-.344),l(4.979,-.516),l(5.495,-.858),l(3.091,-.515),l(1.202,-.688),l(0,-1.373),l(-1.374,-1.202),l(-.859,-1.545),l(-1.202,-.688),l(-.344,-1.03),l(1.202,-1.03),l(1.717,-.343),l(.858,-1.202),l(-.343,-.858),l(-.172,-1.03),l(-1.202,-.516),l(.344,-1.545),l(1.889,-.172),l(2.404,-1.374),l(1.545,-.515),l(.859,-.859),l(1.03,-.687),l(2.919,-1.202),l(3.606,-.515),l(2.404,-1.202),l(2.404,-.172),l(2.232,-1.03),l(1.545,-.343),l(.343,1.03),l(-1.374,.515),l(-1.03,.344),l(-1.202,.515),l(-.858,0),l(-1.202,0),l(-2.919,1.202),l(-1.03,.687),l(-1.717,1.374),l(-.172,1.202),l(-.687,.687),l(-1.374,-.344),l(-1.03,0),l(-1.202,.516),l(-1.202,.687),l(-1.03,.688),l(-.687,1.201),l(.515,.688),l(.687,.687),l(1.374,.344),l(1.545,0),l(.858,.515),l(.687,1.202),l(1.545,1.546),l(.687,.858),l(1.03,.687),l(.687,1.374),l(.858,1.373),l(.344,1.546),l(-.344,.858),l(-.858,1.717),l(-2.919,1.889),l(-1.202,.172),l(-1.03,.858),l(-1.717,1.03),l(-3.263,.344),l(-4.979,.858),l(-2.404,.344),l(-2.919,.687),l(-7.377,-.141),l(-4.556,-.316),l(-.773,-.401),l(-1.717,-.344),l(-1.717,-.343),l(-1.374,0),l(-.859,.515),l(.515,.858),l(2.061,.688),l(4.121,1.202),l(4.995,.04),l(1.202,.759),l(-3.278,.574),l(-5.667,-.343),l(-4.979,-.516),l(-3.097,-.285),l(-2.151,.253),l(.823,.696),l(1.164,.538),l(3.262,.172),l(2.232,0),l(1.374,.343),l(-.249,.402),l(-.782,.113),l(-2.232,.515),l(-5.151,-.171),l(-1.374,-.172),l(-2.232,-1.202),l(-2.404,0),l(.128,.917),l(2.62,1.144),l(1.03,.172),l(1.202,-.172),l(1.374,.344),l(2.404,0),l(4.98,-.516),l(3.09,.687),l(.385,.431),l(-.041,.6),l(-2.061,.172),l(-3.263,.172),l(-2.608,-.185),l(3.811,.871),l(3.656,-.238),l(1.324,.41),l(2.576,.687),l(6.869,.859),l(8.585,.858),l(10.646,1.373),l(6.525,.516),l(2.061,.858),l(.687,1.03),l(3.091,-.344),l(5.323,-1.545),l(6.525,-.687),l(2.455,-.066),l(6.474,-.105),l(9.788,-.344),l(1.889,.344),l(2.576,-1.202),l(8.414,-1.03),l(11.333,-.172),l(8.929,-.858),l(1.202,-.858),l(-1.202,-.516),l(-.271,-1.07),l(-1.184,-.338),l(-5.071,.035),l(-2.232,.172),l(-4.808,-.344),l(-3.091,0),l(0,-1.202),l(3.263,-1.545),l(3.949,-1.202),l(2.404,-.172),l(4.121,-1.03),l(9.444,-1.202),l(5.323,-.858),l(6.525,0),l(4.979,-.516),l(2.747,-2.061),l(6.039,-1.541),l(-1.23,-.52),l(-2.576,.516),l(-2.061,-.344),l(1.545,-1.03),l(1.545,-.343),l(2.48,-.249),l(.696,-.742),l(3.85,-.742),l(1.577,-.418),l(.603,-.557),l(-1.716,-.881),l(-.093,-.604),l(1.252,-.231),l(.464,.742),l(1.017,-.097),l(2.061,-.858),l(1.03,-.343),l(.858,.171),l(1.203,1.544),l(2.403,-.342),l(.242,-.91),l(.96,-.807),l(1.889,-.516),l(1.374,.516),l(-1.162,.574),l(-.139,.604),l(3.189,-.319),l(4.808,.258),l(3.263,-.172),l(1.03,.858),l(1.545,-.344),l(1.717,-.515),l(5.839,-.858),l(7.212,-1.03),l(4.292,-.858),l(2.061,0),l(2.061,1.373),l(1.718,.344),l(1.889,-.344),l(1.545,-.858),l(7.556,-.344),l(3.434,-.171),l(4.293,.171),l(6.697,.945),l(2.575,0),l(4.293,-.516),l(6.01,-.687),l(5.151,-.688),l(2.404,-.515),l(2.231,-.344),l(.615,-.959),l(1.148,-.835),l(1.671,-.095),l(.938,.965),l(1.294,.409),l(1.732,1.052),l(1.009,.139),l(1.009,-.661),l(1.322,-.174),l(.209,.522),l(-.835,.312),l(.661,.244),l(.8,.278),l(.383,-.383),l(.313,-.383),l(.312,.209),l(-.174,.348),l(0,.521),l(.592,-.174),l(1.913,-.557),l(.418,-1.704),l(5.102,-1.137),l(1.374,-.687),l(3.605,-.172),l(1.363,-.646),l(2.758,-.126),l(.21,-.27),l(-.165,-.297),l(.692,-.23),l(1.221,.165),l(.197,.593),l(.66,.231),l(2.165,-.536),l(1.265,-.223),l(-.726,-.297),l(-.561,-.264),l(.099,-.362),l(1.451,-.1),l(1.562,.215),l(.647,.28),l(.692,-.132),l(-.066,-.33),l(-.527,-.33),l(0,-.561),l(.297,-.23),l(.846,-.242),l(4.292,-.858),l(2.92,-.172),l(3.058,.115),l(2.296,1.076),l(1.316,.023),l(.741,.144),l(.023,.383),l(-.908,.096),l(-.168,.454),l(1.723,.216),l(4.892,.646),l(5.571,.453),l(1.546,.172),l(1.717,-.172),l(7.556,.688),l(5.323,0),l(.687,.687),l(.2,1.075),l(-.73,1.232),l(-.672,.439),l(-.425,1.113),l(-.73,.319),l(-.905,-.402),l(-.83,.402),l(-.73,.959),l(1.507,.457),l(.594,.045),l(.959,-.593),l(.411,.365),l(-.867,1.05),l(-1.215,.062),l(-1.717,1.374),l(0,.858),l(1.889,.688),l(2.919,0),l(1.718,-.859),l(1.889,-1.545),l(1.717,-1.717),l(1.374,-.688),l(4.121,-1.03),l(2.231,-.687),l(3.263,-.344),l(2.061,-1.03),l(1.03,-1.03),l(3.435,-1.03),l(2.403,-.687),l(2.061,-.687),l(2.061,-.172),l(6.182,-.687),l(3.778,.171),l(1.204,-.542),l(.169,-.659),l(.54,-.759),l(.49,-.101),l(.471,.776),l(.305,.775),l(4.892,-.35),l(5.838,0),l(4.979,-.172),l(3.091,.344),l(2.112,-.214),l(1.328,.085),l(1.018,.594),l(1.188,-.764),l(2.714,-1.046),l(1.866,-.226),l(.664,-.028),l(.594,-.028),l(.777,-.057),l(1.512,.311),l(1.166,-.172),l(3.606,.687),l(3.777,.344),l(1.03,0),l(1.064,.573),l(.745,.088),l(2.149,-.088),l(1.271,-.263),l(1.315,-.438),l(-.192,-.216),l(.28,-.53),l(1.447,-.131),l(.921,-.088),l(.786,.062),l(2.195,-.501),l(2.097,.501),l(.688,.687),l(.373,.435),l(1.842,-.131),l(.921,-.044),l(.985,.255),l(1.889,.688),l(2.919,.515),l(2.919,-.343),l(4.121,-.258),l(2.232,-.344),l(2.404,0),l(3.521,.315),l(.702,-.755),l(2.158,-.162),l(.863,1.133),l(4.909,.323),l(.917,-1.133),l(1.889,-.593),l(5.988,.013),l(.756,-.283),l(1.996,.108),l(-.432,-1.403),l(.054,-.809),l(1.232,-.36),l(.872,1.277),l(-.647,1.079),l(.805,.219),l(2.81,.482),l(5.262,.376),l(5.494,.344),l(2.278,.583),l(1.283,.14),l(1.396,-.167),l(2.204,.446),l(1.961,-.167),l(.719,.278),l(-.435,.431),l(-.151,.351),l(.293,.123),l(2.081,.331),l(1.078,.484),l(2.706,-.134),l(.377,.595),l(1.038,.227),l(4.808,.515),l(-.123,.536),l(.567,.236),l(2.695,-.095),l(3.214,.181),l(-.329,-.984),l(.71,-.095),l(.804,.426),l(.331,.709),l(1.135,.284),l(2.672,.519),l(2.576,-.343),l(4.017,.999),l(1.626,.495),l(1.672,.871),l(1.626,.023),l(.504,.187),l(.532,-.021),l(.754,-.047),l(.777,.047),l(.896,.589),l(.541,.118),l(.542,.07),l(.142,-.259),l(.471,0),l(1.484,-.048),l(1.416,.237),l(5.951,.535),l(.729,.516),l(1.733,.323),l(2.979,.944),l(.43,-.335),l(.551,.191),l(.454,.67),l(-.741,.215),l(-1.124,.167),l(-.216,.359),l(.168,.263),l(.55,-.048),l(-.024,.67),l(-.43,-.048),l(-.096,.287),l(-.312,.071),l(-.598,.622),l(-.526,.12),l(-.55,.287),l(-.168,.669),l(-1.603,.096),l(-1.339,-.239),l(-1.794,.359),l(-2.153,.425),l(-2.629,.764),l(1.271,1.103),l(-1.907,.212),l(-1.23,-.255),l(-.763,.212),l(-.043,.637),l(-2.374,.169),l(.136,.514),l(.924,.632),l(-.127,.763),l(-.382,.085),l(-.466,.467),l(.551,.466),l(.085,.764),l(-.764,.424),l(.637,.254),l(.763,0),l(.764,.255),l(.339,.509),l(.17,.721),l(1.284,-.187),l(.836,.907),l(2.035,.297),l(.339,.339),l(2.672,.552),l(-4.113,0),l(-2.799,.138),l(-2.332,.541),l(-1.823,-.085),l(-.806,.212),l(-1.611,.339),l(-1.326,-.32),l(-.546,.426),l(1.116,.529),l(-.503,.447),l(-.669,.195),l(-1.117,.753),l(-2.623,.642),l(.475,.391),l(2.205,-.111),l(.53,0),l(.809,.307),l(.168,.363),l(.499,.18),l(.422,.183),l(.111,.894),l(.209,.502),l(3.195,.279),l(2.874,.948),l(3.662,.801),l(5.151,1.202),l(1.456,.062),l(1.015,.17),l(-.146,.234),l(-1.67,.264),l(-.47,.264),l(1.173,.088),l(2.198,-.293),l(1.988,.166),
+E(0,0)
+};
+
+#undef N
+#undef M
+#undef L
+#undef l
+#undef E
diff --git a/test/xcb-huge-image-shm.c b/test/xcb-huge-image-shm.c
new file mode 100644 (file)
index 0000000..269d7f0
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * Copyright © 2011 Uli Schlachter
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Uli Schlachter <psychon@znc.in>
+ */
+
+#include "cairo-test.h"
+
+#define WIDTH 3000
+#define HEIGHT 3000
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_surface_t *surface;
+    cairo_surface_t *image;
+    cairo_t *cr2;
+
+    /* We use a similar surface to have way smaller ref images */
+    surface = cairo_surface_create_similar (cairo_get_target (cr),
+                                           CAIRO_CONTENT_COLOR_ALPHA,
+                                           WIDTH, HEIGHT);
+
+    /* First we have to defeat xcb's deferred clear */
+    cr2 = cairo_create (surface);
+    cairo_test_paint_checkered (cr2);
+    cairo_destroy (cr2);
+
+    /* Now we try to map this to an image. This will try to use shared memory
+     * but fail the allocation, because of the huge surface. */
+    image = cairo_surface_map_to_image (surface, NULL);
+    cairo_surface_unmap_image (surface, image);
+
+    /* Finally we make sure that errors aren't lost. */
+    cairo_set_source_surface (cr, surface, 0, 0);
+    cairo_paint (cr);
+    cairo_surface_destroy (surface);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (xcb_huge_image_shm,
+           "Force a failed shm allocation when receiving an image",
+           "xcb", /* keywords */
+           NULL, /* requirements */
+           2, 2,
+           NULL, draw)
diff --git a/test/xcb-snapshot-assert.c b/test/xcb-snapshot-assert.c
new file mode 100644 (file)
index 0000000..e1ddb73
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * Copyright © 2011 Uli Schlachter
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Uli Schlachter <psychon@znc.in>
+ */
+
+#include "cairo.h"
+#include "cairo-test.h"
+
+static cairo_surface_t *
+create_image (int width, int height)
+{
+    cairo_surface_t *surface;
+    cairo_t *cr;
+
+    surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
+    /* Paint something random to the image */
+    cr = cairo_create (surface);
+    cairo_set_source_rgb (cr, 0, 1, 1);
+    cairo_paint (cr);
+    cairo_destroy (cr);
+
+    return surface;
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    /* Image has to have same geometry as xcb surface to be added as a snapshot */
+    cairo_set_source_surface (cr, create_image (width, height), 0, 0);
+
+    /* This attaches the tested xcb surface as a snapshot */
+    cairo_paint (cr);
+
+    /* Now cairo is modifying a snapshot which fails an
+     * assert in _cairo_surface_begin_modification */
+    cairo_paint (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (xcb_snapshot_assert,
+           "Test a wrong _cairo_surface_attach_snapshot call",
+           "xcb", /* keywords */
+           NULL, /* requirements */
+           2, 2,
+           NULL, draw)
diff --git a/test/xcb-stress-cache.c b/test/xcb-stress-cache.c
new file mode 100644 (file)
index 0000000..3e3d0a2
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+ * Copyright © 2011 Uli Schlachter
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Uli Schlachter <psychon@znc.in>
+ */
+
+#include "cairo-test.h"
+
+#define WIDTH 512
+#define HEIGHT 512
+
+/* This is a random pick. This results in 512 (width) * 512 (height) *
+ * 2 (surfaces per run) * 4 (ARGB32) * ITERATIONS = 200 MiB of image data. */
+#define ITERATIONS 100
+
+/* This tries to trigger a bug in the xcb backend where a Picture is freed to
+ * early. It goes something like this:
+ *
+ * - _composite_mask calls _cairo_xcb_picture_for_pattern to get a xcb_picture_t
+ *   for the source.
+ * - _cairo_xcb_picture_for_pattern calls _cairo_xcb_surface_picture which calls
+ *   _cairo_xcb_screen_store_surface_picture which adds the picture to a cache.
+ * - _cairo_xcb_surface_picture also attached the picture as a snapshot to
+ *   the source surface using cairo_surface_finish as detach_func.
+ * - _composite_mask calls _cairo_xcb_picture_for_pattern to get a xcb_picture_t
+ *   for the mask.
+ * - The resulting picture surface is added to the cache again, but the cache is
+ *   already full, so a random cache entry is picked and removed.
+ * - The surface that was added before is picked and gets fed to
+ *   _surface_cache_entry_destroy.
+ * - This calls _cairo_surface_detach_snapshot which causes the
+ *   detach_func from above to be called, so the surface is finished and the
+ *   associated picture is FreePicture'd.
+ * - _composite_mask now uses a Picture that was already freed.
+ *
+ * So this depends on the screen's surface cache to be full which is why we do
+ * all this looping.
+ */
+
+static cairo_surface_t *
+create_image (void)
+{
+    cairo_surface_t *surface;
+    cairo_t *cr;
+
+    surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, WIDTH, HEIGHT);
+    /* Paint something random to the image */
+    cr = cairo_create (surface);
+    cairo_paint (cr);
+    cairo_set_source_rgb (cr, 0, 1, 1);
+    cairo_rectangle (cr, 0, 0, WIDTH/2.0, HEIGHT/2.0);
+    cairo_fill (cr);
+    cairo_set_source_rgb (cr, 1, 0, 1);
+    cairo_rectangle (cr, WIDTH/2.0, HEIGHT/2.0, WIDTH/2.0, HEIGHT/2.0);
+    cairo_fill (cr);
+    cairo_destroy (cr);
+
+    return surface;
+}
+
+static cairo_surface_t *
+dirty_cache (cairo_t *cr)
+{
+    cairo_surface_t *surface;
+
+    /* Set a source surface... */
+    surface = create_image ();
+    cairo_set_source_surface (cr, surface, 0, 0);
+    cairo_surface_destroy (surface);
+    /* ...and create a mask surface, so that we can hit the early FreePicture */
+    surface = create_image ();
+    cairo_mask_surface (cr, surface, 0, 0);
+
+    return surface;
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    int i;
+    cairo_surface_t *array[ITERATIONS];
+
+    /* We have to keep the associated cairo_surface_t alive so that they aren't
+     * removed from the cache */
+    for (i = 0; i < ITERATIONS; i++)
+       array[i] = dirty_cache (cr);
+    for (i = 0; i < ITERATIONS; i++)
+       cairo_surface_destroy (array[i]);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (xcb_stress_cache,
+           "Stress test for a image surface cache in cairo-xcb",
+           "xcb, stress", /* keywords */
+           NULL, /* requirements */
+           2, 2,
+           NULL, draw)
diff --git a/test/xcb-surface-source.c b/test/xcb-surface-source.c
new file mode 100644 (file)
index 0000000..d2c7a13
--- /dev/null
@@ -0,0 +1,149 @@
+/*
+ * Copyright © 2009 Chris Wilson
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Chris Wilson not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Chris Wilson makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * CHRIS WILSON DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL CHRIS WILSON BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+#if CAIRO_HAS_XCB_SURFACE
+#include <cairo-xcb.h>
+#endif
+
+#include "surface-source.c"
+
+#if CAIRO_HAS_XCB_SURFACE
+static cairo_user_data_key_t closure_key;
+
+struct closure {
+    cairo_device_t *device;
+    xcb_connection_t *connection;
+    xcb_pixmap_t pixmap;
+};
+
+static void
+cleanup (void *data)
+{
+    struct closure *arg = data;
+
+    cairo_device_finish (arg->device);
+    cairo_device_destroy (arg->device);
+
+    xcb_free_pixmap (arg->connection, arg->pixmap);
+    xcb_disconnect (arg->connection);
+
+    free (arg);
+}
+
+static xcb_render_pictforminfo_t *
+find_depth (xcb_connection_t *connection, int depth, void **formats_out)
+{
+    xcb_render_query_pict_formats_reply_t      *formats;
+    xcb_render_query_pict_formats_cookie_t cookie;
+    xcb_render_pictforminfo_iterator_t i;
+
+    cookie = xcb_render_query_pict_formats (connection);
+    xcb_flush (connection);
+
+    formats = xcb_render_query_pict_formats_reply (connection, cookie, 0);
+    if (formats == NULL)
+       return NULL;
+
+    for (i = xcb_render_query_pict_formats_formats_iterator (formats);
+        i.rem;
+        xcb_render_pictforminfo_next (&i))
+    {
+       if (XCB_RENDER_PICT_TYPE_DIRECT != i.data->type)
+           continue;
+
+       if (depth != i.data->depth)
+           continue;
+
+       *formats_out = formats;
+       return i.data;
+    }
+
+    free (formats);
+    return NULL;
+}
+#endif
+
+static cairo_surface_t *
+create_source_surface (int size)
+{
+#if CAIRO_HAS_XCB_SURFACE
+    xcb_connection_t *connection;
+    xcb_render_pictforminfo_t *render_format;
+    struct closure *data;
+    cairo_surface_t *surface;
+    xcb_screen_t *root;
+    xcb_void_cookie_t cookie;
+    void *formats;
+
+    connection = xcb_connect (NULL, NULL);
+    if (connection == NULL)
+       return NULL;
+
+    data = xmalloc (sizeof (struct closure));
+    data->connection = connection;
+
+    render_format = find_depth (connection, 32, &formats);
+    if (render_format == NULL) {
+       xcb_disconnect (connection);
+       free (data);
+       return NULL;
+    }
+
+    root = xcb_setup_roots_iterator (xcb_get_setup (connection)).data;
+
+    data->pixmap = xcb_generate_id (connection);
+    cookie = xcb_create_pixmap_checked (connection, 32,
+                                       data->pixmap, root->root, size, size);
+    /* slow, but sure */
+    if (xcb_request_check (connection, cookie) != NULL) {
+       free (formats);
+       xcb_disconnect (connection);
+       free (data);
+       return NULL;
+    }
+
+    surface = cairo_xcb_surface_create_with_xrender_format (connection,
+                                                           root,
+                                                           data->pixmap,
+                                                           render_format,
+                                                           size, size);
+    free (formats);
+
+    data->device = cairo_device_reference (cairo_surface_get_device (surface));
+    cairo_surface_set_user_data (surface, &closure_key, data, cleanup);
+
+    return surface;
+#else
+    return NULL;
+#endif
+}
+
+CAIRO_TEST (xcb_surface_source,
+           "Test using a XCB surface as the source",
+           "source", /* keywords */
+           NULL, /* requirements */
+           SIZE, SIZE,
+           preamble, draw)
diff --git a/test/xcomposite-projection.c b/test/xcomposite-projection.c
new file mode 100644 (file)
index 0000000..39686cf
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2009 Benjamin Otte
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Benjamin Otte <otte@gnome.org>
+ */
+
+#include "cairo-test.h"
+
+/*
+ * Exercise a bug in the projection of a rotated trapezoid mask.
+ * I used CAIRO_ANTIALIAS_NONE and a single-color source in the test to get
+ * rid of aliasing issues in the output images. This makes some issues
+ * slightly less visible, but still fails for all of them. If you want to
+ * get a clearer view:
+ * #define ANTIALIAS CAIRO_ANTIALIAS_DEFAULT
+ */
+
+#define ANTIALIAS CAIRO_ANTIALIAS_NONE
+
+static const char png_filename[] = "romedalen.png";
+
+static cairo_pattern_t *
+get_source (const cairo_test_context_t *ctx,
+           int *width, int *height)
+{
+    cairo_surface_t *surface;
+    cairo_pattern_t *pattern;
+
+    if (ANTIALIAS == CAIRO_ANTIALIAS_NONE) {
+       cairo_t *cr;
+
+       surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, 256, 192);
+       cr = cairo_create (surface);
+
+       cairo_set_source_rgb (cr, 0.75, 0.25, 0.25);
+       cairo_paint (cr);
+
+       pattern = cairo_pattern_create_for_surface (cairo_get_target (cr));
+       cairo_destroy (cr);
+    } else {
+       surface = cairo_test_create_surface_from_png (ctx, png_filename);
+       pattern = cairo_pattern_create_for_surface (surface);
+    }
+
+    *width = cairo_image_surface_get_width (surface);
+    *height = cairo_image_surface_get_height (surface);
+    cairo_surface_destroy (surface);
+
+    return pattern;
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_pattern_t *image;
+    int img_width, img_height;
+
+    image = get_source (cairo_test_get_context (cr),
+                       &img_width, &img_height);
+
+    /* we don't want to debug antialiasing artifacts */
+    cairo_set_antialias (cr, ANTIALIAS);
+
+    /* dark grey background */
+    cairo_set_source_rgb (cr, 0.25, 0.25, 0.25);
+    cairo_paint (cr);
+
+    /* magic transform */
+    cairo_translate (cr, 10, -40);
+    cairo_rotate (cr, -0.05);
+
+    /* place the image on our surface */
+    cairo_set_source (cr, image);
+
+    /* paint the image */
+    cairo_rectangle (cr, 0, 0, img_width, img_height);
+    cairo_fill (cr);
+
+    cairo_pattern_destroy (image);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (xcomposite_projection,
+           "Test a bug with XRenderComposite reference computation when projecting the first trapezoid onto 16.16 space",
+           "xlib", /* keywords */
+           "target=raster", /* requirements */
+           300, 150,
+           NULL, draw)
diff --git a/test/xlib-expose-event.c b/test/xlib-expose-event.c
new file mode 100644 (file)
index 0000000..4b1cb46
--- /dev/null
@@ -0,0 +1,196 @@
+/*
+ * Copyright © 2008 Chris Wilson
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Chris Wilson not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Chris Wilson makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * CHRIS WILSON DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL CHRIS WILSON BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+/* This test tries to emulate the behaviour of most toolkits; it tries
+ * to simulate typical usage of a single surface with multiple exposures.
+ *
+ * The first goal of the test is to reproduce the XSetClipMask(NULL) bug
+ * reintroduced in 1.6.2 (but was originally fixed in 40558cb15). As I've
+ * made the same mistake again, it is worth adding a regression test...
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "cairo.h"
+#include "cairo-test.h"
+
+#include "buffer-diff.h"
+
+#define SIZE 160
+#define NLOOPS 10
+
+static const char *png_filename = "romedalen.png";
+
+static void
+draw_mask (cairo_t *cr)
+{
+    cairo_surface_t *surface;
+    cairo_t *cr2;
+
+    surface = cairo_surface_create_similar (cairo_get_group_target (cr),
+                                           CAIRO_CONTENT_ALPHA,
+                                           50, 50);
+    cr2 = cairo_create (surface);
+    cairo_surface_destroy (surface);
+
+    cairo_rectangle (cr2,
+                    0, 0,
+                    40, 40);
+    cairo_rectangle (cr2,
+                    10, 10,
+                    40, 40);
+    cairo_clip (cr2);
+
+    cairo_move_to (cr2, 0, 25);
+    cairo_line_to (cr2, 50, 25);
+    cairo_move_to (cr2, 25, 0);
+    cairo_line_to (cr2, 25, 50);
+    cairo_set_source_rgb (cr2, 1, 1, 1);
+    cairo_stroke (cr2);
+
+    cairo_set_source_rgb (cr, 1, 0, 0);
+    cairo_mask_surface (cr, cairo_get_target (cr2), 50, 50);
+    cairo_destroy (cr2);
+}
+
+static cairo_surface_t *
+clone_similar_surface (cairo_surface_t * target, cairo_surface_t *surface)
+{
+    cairo_t *cr;
+    cairo_surface_t *similar;
+
+    similar = cairo_surface_create_similar (target,
+                                     cairo_surface_get_content (surface),
+                                     cairo_image_surface_get_width (surface),
+                                     cairo_image_surface_get_height (surface));
+
+    cr = cairo_create (similar);
+    cairo_surface_destroy (similar);
+
+    cairo_set_source_surface (cr, surface, 0, 0);
+    cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+    cairo_paint (cr);
+
+    similar = cairo_surface_reference (cairo_get_target (cr));
+    cairo_destroy (cr);
+
+    return similar;
+}
+
+static void
+draw_image (const cairo_test_context_t *ctx,
+           cairo_t *cr,
+           cairo_surface_t *image)
+{
+    cairo_set_source_surface (cr, image, 0, 0);
+    cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+    cairo_paint (cr);
+}
+
+static void
+draw (const cairo_test_context_t *ctx,
+      cairo_t *cr,
+      cairo_surface_t *image,
+      cairo_rectangle_t *region,
+      int n_regions)
+{
+    cairo_save (cr);
+    if (region != NULL) {
+       int i;
+       for (i = 0; i < n_regions; i++) {
+           cairo_rectangle (cr,
+                            region[i].x, region[i].y,
+                            region[i].width, region[i].height);
+       }
+       cairo_clip (cr);
+    }
+    cairo_push_group (cr);
+    draw_image (ctx, cr, image);
+    draw_mask (cr);
+    cairo_pop_group_to_source (cr);
+    cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+    cairo_paint (cr);
+    cairo_restore (cr);
+}
+
+static cairo_test_status_t
+draw_func (cairo_t *cr, int width, int height)
+{
+    cairo_rectangle_t region[4];
+    const cairo_test_context_t *ctx;
+    cairo_surface_t *source, *image;
+    int i, j;
+
+    ctx = cairo_test_get_context (cr);
+
+    source = cairo_test_create_surface_from_png (ctx, png_filename);
+    image = clone_similar_surface (cairo_get_group_target (cr), source);
+    cairo_surface_destroy (source);
+
+    cairo_push_group_with_content (cr, CAIRO_CONTENT_COLOR);
+    draw (ctx, cr, image, NULL, 0);
+    for (i = 0; i < NLOOPS; i++) {
+       for (j = 0; j < NLOOPS; j++) {
+           region[0].x = i * SIZE / NLOOPS;
+           region[0].y = i * SIZE / NLOOPS;
+           region[0].width = SIZE / 4;
+           region[0].height = SIZE / 4;
+
+           region[1].x = j * SIZE / NLOOPS;
+           region[1].y = j * SIZE / NLOOPS;
+           region[1].width = SIZE / 4;
+           region[1].height = SIZE / 4;
+
+           region[2].x = i * SIZE / NLOOPS;
+           region[2].y = j * SIZE / NLOOPS;
+           region[2].width = SIZE / 4;
+           region[2].height = SIZE / 4;
+
+           region[3].x = j * SIZE / NLOOPS;
+           region[3].y = i * SIZE / NLOOPS;
+           region[3].width = SIZE / 4;
+           region[3].height = SIZE / 4;
+
+           draw (ctx, cr, image, region, 4);
+       }
+    }
+
+    cairo_pop_group_to_source (cr);
+    cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+    cairo_paint (cr);
+
+    cairo_surface_destroy (image);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (xlib_expose_event,
+           "Emulate a typical expose event",
+           "xlib", /* keywords */
+           NULL, /* requirements */
+           SIZE, SIZE,
+           NULL, draw_func)
diff --git a/test/xlib-surface-source.c b/test/xlib-surface-source.c
new file mode 100644 (file)
index 0000000..7d2ed71
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * Copyright © 2008 Chris Wilson
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Chris Wilson not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Chris Wilson makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * CHRIS WILSON DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL CHRIS WILSON BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+#include <cairo-xlib.h>
+#if CAIRO_HAS_XLIB_XRENDER_SURFACE
+#include <cairo-xlib-xrender.h>
+#endif
+
+#include "surface-source.c"
+
+static cairo_user_data_key_t closure_key;
+
+struct closure {
+    cairo_device_t *device;
+    Display *dpy;
+    Pixmap pix;
+};
+
+static void
+cleanup (void *data)
+{
+    struct closure *arg = data;
+
+    cairo_device_finish (arg->device);
+    cairo_device_destroy (arg->device);
+
+    XFreePixmap (arg->dpy, arg->pix);
+    XCloseDisplay (arg->dpy);
+
+    free (arg);
+}
+
+static cairo_surface_t *
+create_source_surface (int size)
+{
+#if CAIRO_HAS_XLIB_XRENDER_SURFACE
+    XRenderPictFormat *xrender_format;
+    struct closure *data;
+    cairo_surface_t *surface;
+
+    data = xmalloc (sizeof (struct closure));
+
+    data->dpy = XOpenDisplay (NULL);
+    if (!data->dpy) {
+       return NULL;
+    }
+
+    xrender_format = XRenderFindStandardFormat (data->dpy, PictStandardARGB32);
+
+    data->pix = XCreatePixmap (data->dpy, DefaultRootWindow (data->dpy),
+                              size, size, xrender_format->depth);
+
+    surface = cairo_xlib_surface_create_with_xrender_format (data->dpy,
+                                                            data->pix,
+                                                            DefaultScreenOfDisplay (data->dpy),
+                                                            xrender_format,
+                                                            size, size);
+    data->device = cairo_device_reference (cairo_surface_get_device (surface));
+    if (cairo_surface_set_user_data (surface, &closure_key, data, cleanup)) {
+       cairo_surface_finish (surface);
+       cairo_surface_destroy (surface);
+       cleanup (data);
+       return NULL;
+    }
+
+    return surface;
+#else
+    return NULL;
+#endif
+}
+
+CAIRO_TEST (xlib_surface_source,
+           "Test using a Xlib surface as the source",
+           "source", /* keywords */
+           NULL, /* requirements */
+           SIZE, SIZE,
+           preamble, draw)
diff --git a/test/xlib-surface.c b/test/xlib-surface.c
new file mode 100644 (file)
index 0000000..be44b14
--- /dev/null
@@ -0,0 +1,369 @@
+/*
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "cairo.h"
+#include "cairo-xlib.h"
+#include "cairo-test.h"
+
+#include "cairo-boilerplate-xlib.h"
+
+#include "buffer-diff.h"
+
+#define SIZE 100
+#define OFFSCREEN_OFFSET 50
+
+cairo_bool_t result = 0;
+
+#if CAIRO_HAS_XLIB_XRENDER_SURFACE
+
+#include "cairo-xlib-xrender.h"
+
+/* Vladimir Vukicevic reported that surfaces were being created with
+ * mismatching Visuals and XRenderPictFormats.
+ */
+static cairo_bool_t
+surface_compare_visual_and_format (cairo_surface_t *surface)
+{
+    Display *dpy;
+    Visual *visual;
+    XRenderPictFormat *format;
+
+    dpy = cairo_xlib_surface_get_display (surface);
+
+    visual = cairo_xlib_surface_get_visual (surface);
+    if (visual == NULL)
+       return TRUE;
+
+    format = cairo_xlib_surface_get_xrender_format (surface);
+    if (format == NULL)
+       return TRUE;
+
+    return format == XRenderFindVisualFormat (dpy, visual);
+
+}
+#else
+
+static cairo_bool_t
+surface_compare_visual_and_format (cairo_surface_t *surface)
+{
+    return TRUE;
+}
+
+#endif
+
+static cairo_bool_t
+check_similar_visual_and_format (cairo_surface_t *surface)
+{
+    cairo_surface_t *similar;
+    cairo_bool_t ret;
+
+    similar = cairo_surface_create_similar (surface,
+                                           CAIRO_CONTENT_COLOR_ALPHA,
+                                           1, 1);
+    if (cairo_surface_status (similar))
+       return FALSE;
+
+    ret = surface_compare_visual_and_format (similar);
+
+    cairo_surface_destroy (similar);
+
+    return ret;
+}
+
+
+static void
+draw_pattern (cairo_surface_t *surface)
+{
+    cairo_t *cr = cairo_create (surface);
+    int i;
+
+    cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); /* white */
+    cairo_paint (cr);
+
+    cairo_set_source_rgba (cr, 0, 0.0, 0.0, 0.50); /* half-alpha-black */
+
+    for (i = 1; i <= 3; i++) {
+       int inset = SIZE / 8 * i;
+
+       cairo_rectangle (cr,
+                        inset,            inset,
+                        SIZE - 2 * inset, SIZE - 2 * inset);
+       cairo_fill (cr);
+    }
+
+    cairo_destroy (cr);
+}
+
+static void
+erase_pattern (cairo_surface_t *surface)
+{
+    cairo_t *cr = cairo_create (surface);
+
+    cairo_set_source_rgb (cr, 0.0, 0.0, 0.0); /* black */
+    cairo_paint (cr);
+
+    cairo_destroy (cr);
+}
+
+static cairo_test_status_t
+do_test (const cairo_test_context_t *ctx,
+        Display        *dpy,
+        unsigned char  *reference_data,
+        unsigned char  *test_data,
+        unsigned char  *diff_data,
+        cairo_bool_t    use_render,
+        cairo_bool_t    use_pixmap,
+        cairo_bool_t    set_size,
+        cairo_bool_t    offscreen)
+{
+    cairo_surface_t *surface;
+    cairo_surface_t *test_surface;
+    cairo_t *test_cr;
+    buffer_diff_result_t result;
+    Drawable drawable;
+    int screen = DefaultScreen (dpy);
+
+    if (use_pixmap && offscreen)
+       return CAIRO_TEST_SUCCESS;
+
+    if (use_pixmap) {
+       drawable = XCreatePixmap (dpy, DefaultRootWindow (dpy),
+                                 SIZE, SIZE, DefaultDepth (dpy, screen));
+    } else {
+       XSetWindowAttributes xwa;
+       int x, y;
+
+       xwa.override_redirect = True;
+
+       if (offscreen) {
+           x = - OFFSCREEN_OFFSET;
+           y = - OFFSCREEN_OFFSET;
+       } else {
+           x = 0;
+           y = 0;
+       }
+
+       drawable = XCreateWindow (dpy, DefaultRootWindow (dpy),
+                                 x, y, SIZE, SIZE, 0,
+                                 DefaultDepth (dpy, screen), InputOutput,
+                                 DefaultVisual (dpy, screen),
+                                 CWOverrideRedirect, &xwa);
+       XMapWindow (dpy, drawable);
+    }
+
+    surface = cairo_xlib_surface_create (dpy,
+                                        drawable,
+                                        DefaultVisual (dpy, screen),
+                                        SIZE, SIZE);
+
+    if (! surface_compare_visual_and_format (surface))
+       return CAIRO_TEST_FAILURE;
+
+    if (!use_render)
+       cairo_boilerplate_xlib_surface_disable_render (surface);
+
+    if (set_size) {
+       cairo_xlib_surface_set_size (surface, SIZE, SIZE);
+
+       if (cairo_xlib_surface_get_width (surface) != SIZE ||
+           cairo_xlib_surface_get_height (surface) != SIZE)
+           return CAIRO_TEST_FAILURE;
+    }
+
+    if (! check_similar_visual_and_format (surface))
+       return CAIRO_TEST_FAILURE;
+
+    draw_pattern (surface);
+
+    test_surface = cairo_image_surface_create_for_data (test_data,
+                                                       CAIRO_FORMAT_RGB24,
+                                                       SIZE, SIZE,
+                                                       SIZE * 4);
+
+    test_cr = cairo_create (test_surface);
+    cairo_set_source_surface (test_cr, surface, 0, 0);
+    cairo_paint (test_cr);
+
+    cairo_destroy (test_cr);
+    cairo_surface_destroy (test_surface);
+
+    /* We erase the surface to black in case we get the same
+     * memory back again for the pixmap case.
+     */
+    erase_pattern (surface);
+    cairo_surface_destroy (surface);
+
+    if (use_pixmap)
+       XFreePixmap (dpy, drawable);
+    else
+       XDestroyWindow (dpy, drawable);
+
+    if (offscreen) {
+       size_t offset = 4 * (SIZE * OFFSCREEN_OFFSET + OFFSCREEN_OFFSET);
+
+       buffer_diff_noalpha (reference_data + offset,
+                            test_data + offset,
+                            diff_data + offset,
+                            SIZE - OFFSCREEN_OFFSET,
+                            SIZE - OFFSCREEN_OFFSET,
+                            4 * SIZE,
+                            &result);
+    } else {
+       buffer_diff_noalpha (reference_data,
+                            test_data,
+                            diff_data,
+                            SIZE,
+                            SIZE,
+                            4 * SIZE,
+                            &result);
+    }
+
+    cairo_test_log (ctx, "xlib-surface: %s, %s, %s%s: %s\n",
+                   use_render ? "   render" : "no-render",
+                   set_size ? "   size" : "no-size",
+                   use_pixmap ? "pixmap" : "window",
+                   use_pixmap ?
+                   "           " :
+                   (offscreen ? ", offscreen" : ",  onscreen"),
+                   image_diff_is_failure (&result, 0) ? "FAIL" : "PASS");
+
+    if (image_diff_is_failure (&result, 0))
+       return CAIRO_TEST_FAILURE;
+    else
+       return CAIRO_TEST_SUCCESS;
+}
+
+static cairo_bool_t
+check_visual (Display *dpy)
+{
+    Visual *visual = DefaultVisual (dpy, DefaultScreen (dpy));
+
+    if ((visual->red_mask   == 0xff0000 &&
+        visual->green_mask == 0x00ff00 &&
+        visual->blue_mask  == 0x0000ff) ||
+       (visual->red_mask   == 0x0000ff &&
+        visual->green_mask == 0x00ff00 &&
+        visual->blue_mask  == 0xff0000))
+       return 1;
+    else
+       return 0;
+}
+
+#undef xcalloc
+static void *
+xcalloc (const cairo_test_context_t *ctx, size_t a, size_t b)
+{
+    void *ptr = calloc (a, b);
+    if (ptr == NULL) {
+       cairo_test_log (ctx, "xlib-surface: unable to allocate memory, skipping\n");
+       abort ();
+    }
+    return ptr;
+}
+
+static cairo_test_status_t
+preamble (cairo_test_context_t *ctx)
+{
+    Display *dpy;
+    unsigned char *reference_data;
+    unsigned char *test_data;
+    unsigned char *diff_data;
+    cairo_surface_t *reference_surface;
+    cairo_bool_t use_pixmap;
+    cairo_bool_t set_size;
+    cairo_bool_t offscreen;
+    cairo_test_status_t status, result = CAIRO_TEST_UNTESTED;
+    int stride;
+
+    if (! cairo_test_is_target_enabled (ctx, "xlib"))
+       goto CLEANUP_TEST;
+
+    dpy = XOpenDisplay (NULL);
+    if (!dpy) {
+       cairo_test_log (ctx, "xlib-surface: Cannot open display, skipping\n");
+       goto CLEANUP_TEST;
+    }
+
+    if (!check_visual (dpy)) {
+       cairo_test_log (ctx, "xlib-surface: default visual is not RGB24 or BGR24, skipping\n");
+       goto CLEANUP_DISPLAY;
+    }
+
+    stride = cairo_format_stride_for_width (CAIRO_FORMAT_RGB24, SIZE);
+
+    reference_data = xcalloc (ctx, SIZE, stride);
+    test_data = xcalloc (ctx, SIZE, stride);
+    diff_data = xcalloc (ctx, SIZE, stride);
+
+    reference_surface = cairo_image_surface_create_for_data (reference_data,
+                                                            CAIRO_FORMAT_RGB24,
+                                                            SIZE, SIZE,
+                                                            stride);
+
+    draw_pattern (reference_surface);
+    cairo_surface_destroy (reference_surface);
+
+    result = CAIRO_TEST_SUCCESS;
+
+    for (set_size = 0; set_size <= 1; set_size++)
+       for (use_pixmap = 0; use_pixmap <= 1; use_pixmap++)
+           for (offscreen = 0; offscreen <= 1; offscreen++) {
+               status = do_test (ctx, dpy,
+                                 reference_data, test_data, diff_data,
+                                 1, use_pixmap, set_size, offscreen);
+               if (status)
+                   result = status;
+           }
+
+    for (set_size = 0; set_size <= 1; set_size++)
+       for (use_pixmap = 0; use_pixmap <= 1; use_pixmap++)
+           for (offscreen = 0; offscreen <= 1; offscreen++) {
+               status = do_test (ctx, dpy,
+                                 reference_data, test_data, diff_data,
+                                 0, use_pixmap, set_size, offscreen);
+               if (status)
+                   result = status;
+           }
+
+    free (reference_data);
+    free (test_data);
+    free (diff_data);
+
+  CLEANUP_DISPLAY:
+    XCloseDisplay (dpy);
+
+  CLEANUP_TEST:
+    return result;
+}
+
+CAIRO_TEST (xlib_surface,
+           "Check creating surfaces for various XWindows",
+           "xlib", /* keywords */
+           NULL, /* requirements */
+           0, 0,
+           preamble, NULL)
diff --git a/test/zero-alpha.c b/test/zero-alpha.c
new file mode 100644 (file)
index 0000000..0105cc8
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * Copyright © 2006 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ */
+
+#include "cairo-test.h"
+
+#define SIZE 3
+#define REPS 10
+
+/* History:
+ *
+ * 2006-06-13 Paul Giblock reports a "Stange alpha channel problem" on
+ * the cairo mailing list.
+ *
+ * 2006-06-13 Carl Worth writes this test in an attempt to reproduce
+ * the problem. The test first fills in a 3x3 rectangle with red, then
+ * starts pounding on that center pixel with various forms of
+ * zero-alpha rendering to see if its value can ever be made to
+ * change.
+ *
+ * 2006-06-13 Paul Giblock reports that this only happens with the
+ * xlib backend, and then only on some systems.
+ */
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    int i;
+    cairo_surface_t *surface;
+    uint32_t zero = 0;
+
+    /* First paint background red. */
+    cairo_set_source_rgb (cr, 1.0, 0.0, 0.0); /* red */
+    cairo_paint (cr);
+
+    /* Then we paint zero-alpha in several ways (always REPS times): */
+
+    /* 1. fill a rectangle with a zero-alpha solid source. */
+    cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, 0.0); /* transparent */
+    cairo_rectangle (cr, 1.0, 1.0, 1.0, 1.0);
+    for (i=0; i < REPS; i++)
+       cairo_fill_preserve (cr);
+    cairo_new_path (cr);
+
+    /* 2. paint with a zero-alpha image surface source. */
+    surface = cairo_image_surface_create_for_data ((unsigned char *) &zero,
+                                                  CAIRO_FORMAT_ARGB32, 1, 1, 4);
+    cairo_set_source_surface (cr, surface, 1, 1);
+    for (i=0; i < REPS; i++)
+       cairo_paint (cr);
+
+    /* 3. clip to rectangle then paint with zero-alpha solid source. */
+    cairo_rectangle (cr, 1.0, 1.0, 1.0, 1.0);
+    cairo_clip (cr);
+    cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, 0.0); /* transparent */
+    for (i=0; i < REPS; i++)
+       cairo_paint (cr);
+
+    /* 4. With the clip still there, paint our image surface. */
+    cairo_set_source_surface (cr, surface, 1, 1);
+    for (i=0; i < REPS; i++)
+       cairo_paint (cr);
+
+    cairo_surface_finish (surface); /* zero will go out of scope */
+    cairo_surface_destroy (surface);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (zero_alpha,
+           "Testing that drawing with zero alpha has no effect",
+           "alpha", /* keywords */
+           NULL, /* requirements */
+           SIZE, SIZE,
+           NULL, draw)
diff --git a/test/zero-mask.c b/test/zero-mask.c
new file mode 100644 (file)
index 0000000..3d2f34d
--- /dev/null
@@ -0,0 +1,198 @@
+/*
+ * Copyright © 2010 Red Hat, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Benjamin Otte <otte@gnome.org>
+ */
+
+#include "cairo-test.h"
+
+#define RECT 10
+#define SPACE 5
+
+static void
+paint_with_alpha (cairo_t *cr)
+{
+    cairo_paint_with_alpha (cr, 0.0);
+}
+
+static void
+mask_with_solid (cairo_t *cr)
+{
+    cairo_pattern_t *pattern = cairo_pattern_create_rgba (1, 0, 0, 0);
+
+    cairo_mask (cr, pattern);
+    
+    cairo_pattern_destroy (pattern);
+}
+
+static void
+mask_with_empty_gradient (cairo_t *cr)
+{
+    cairo_pattern_t *pattern = cairo_pattern_create_linear (1, 2, 3, 4);
+
+    cairo_mask (cr, pattern);
+    
+    cairo_pattern_destroy (pattern);
+}
+
+static void
+mask_with_gradient (cairo_t *cr)
+{
+    cairo_pattern_t *pattern = cairo_pattern_create_radial (1, 2, 3, 4, 5, 6);
+
+    cairo_pattern_add_color_stop_rgba (pattern, 0, 1, 0, 0, 0);
+    cairo_pattern_add_color_stop_rgba (pattern, 0, 0, 0, 1, 0);
+
+    cairo_mask (cr, pattern);
+    
+    cairo_pattern_destroy (pattern);
+}
+
+static void
+mask_with_surface (cairo_t *cr)
+{
+    cairo_surface_t *surface = cairo_surface_create_similar (cairo_get_target (cr),
+                                                             CAIRO_CONTENT_COLOR_ALPHA,
+                                                             RECT,
+                                                             RECT);
+
+    cairo_mask_surface (cr, surface, 0, 0);
+    
+    cairo_surface_destroy (surface);
+}
+
+static void
+mask_with_alpha_surface (cairo_t *cr)
+{
+    cairo_surface_t *surface = cairo_surface_create_similar (cairo_get_target (cr),
+                                                             CAIRO_CONTENT_ALPHA,
+                                                             RECT / 2,
+                                                             RECT / 2);
+    cairo_pattern_t *pattern = cairo_pattern_create_for_surface (surface);
+    cairo_pattern_set_extend (pattern, CAIRO_EXTEND_REFLECT);
+
+    cairo_mask (cr, pattern);
+
+    cairo_pattern_destroy (pattern);
+    cairo_surface_destroy (surface);
+}
+
+static void
+mask_with_nonclear_surface (cairo_t *cr)
+{
+    static unsigned char data[8 * 4] = { 0, };
+    cairo_surface_t *surface = cairo_image_surface_create_for_data (data,
+                                                                    CAIRO_FORMAT_A1,
+                                                                    16, 8, 4);
+
+    cairo_mask_surface (cr, surface, 0, 0);
+
+    cairo_surface_destroy (surface);
+}
+
+static void
+mask_with_0x0_surface (cairo_t *cr)
+{
+    cairo_surface_t *surface = cairo_surface_create_similar (cairo_get_target (cr),
+                                                             CAIRO_CONTENT_COLOR_ALPHA,
+                                                             0, 0);
+
+    cairo_mask_surface (cr, surface, 0, 0);
+    
+    cairo_surface_destroy (surface);
+}
+
+static void
+mask_with_extend_none (cairo_t *cr)
+{
+    cairo_surface_t *surface = cairo_surface_create_similar (cairo_get_target (cr),
+                                                             CAIRO_CONTENT_COLOR_ALPHA,
+                                                             RECT,
+                                                             RECT);
+
+    cairo_mask_surface (cr, surface, 2 * RECT, 2 * RECT);
+    
+    cairo_surface_destroy (surface);
+}
+
+typedef void (* mask_func_t) (cairo_t *);
+
+static mask_func_t mask_funcs[] = {
+  paint_with_alpha,
+  mask_with_solid,
+  mask_with_empty_gradient,
+  mask_with_gradient,
+  mask_with_surface,
+  mask_with_alpha_surface,
+  mask_with_nonclear_surface,
+  mask_with_0x0_surface,
+  mask_with_extend_none
+};
+
+static cairo_operator_t operators[] = {
+  CAIRO_OPERATOR_CLEAR,
+  CAIRO_OPERATOR_SOURCE,
+  CAIRO_OPERATOR_OVER,
+  CAIRO_OPERATOR_IN,
+  CAIRO_OPERATOR_DEST_ATOP,
+  CAIRO_OPERATOR_SATURATE,
+  CAIRO_OPERATOR_MULTIPLY
+};
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    unsigned int i, op;
+
+    /* 565-compatible gray background */
+    cairo_set_source_rgb (cr, 0.51613, 0.55555, 0.51613);
+    cairo_paint (cr);
+
+    cairo_set_source_rgb (cr, 0.0, 1.0, 0.0); /* green */
+    /* mask with zero-alpha in several ways */
+
+    cairo_translate (cr, SPACE, SPACE);
+
+    for (op = 0; op < ARRAY_LENGTH (operators); op++) {
+        cairo_set_operator (cr, operators[op]);
+
+        for (i = 0; i < ARRAY_LENGTH (mask_funcs); i++) {
+            cairo_save (cr);
+            cairo_translate (cr, i * (RECT + SPACE), op * (RECT + SPACE));
+            cairo_rectangle (cr, 0, 0, RECT, RECT);
+            cairo_clip (cr);
+            mask_funcs[i] (cr);
+            cairo_restore (cr);
+        }
+    }
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (zero_mask,
+           "Testing that masking with zero alpha works",
+           "alpha, mask", /* keywords */
+           NULL, /* requirements */
+           SPACE + (RECT + SPACE) * ARRAY_LENGTH (mask_funcs),
+           SPACE + (RECT + SPACE) * ARRAY_LENGTH (operators),
+           NULL, draw)
diff --git a/util/.gitignore b/util/.gitignore
new file mode 100644 (file)
index 0000000..9a28da1
--- /dev/null
@@ -0,0 +1,24 @@
+.deps
+.libs
+Makefile
+Makefile.in
+show-contour
+show-edges
+show-events
+show-polygon
+show-traps
+xml-to-trace
+trace-to-xml
+*.so
+*.la
+*.lo
+*.loT
+*.o
+*.obj
+*.pdb
+*.dll
+*.exp
+*.lib
+*~
+.*.sw?
+TAGS
diff --git a/util/COPYING b/util/COPYING
new file mode 100644 (file)
index 0000000..ea44bb6
--- /dev/null
@@ -0,0 +1,4 @@
+Cairo is free software.
+
+These utilities are all free software, please see the licensing conditions
+in the opening comments of each file.
diff --git a/util/Makefile.am b/util/Makefile.am
new file mode 100644 (file)
index 0000000..f202f35
--- /dev/null
@@ -0,0 +1,98 @@
+include $(top_srcdir)/build/Makefile.am.common
+
+SUBDIRS = . cairo-missing
+
+if CAIRO_HAS_GOBJECT_FUNCTIONS
+SUBDIRS += cairo-gobject
+endif
+
+if CAIRO_HAS_INTERPRETER
+SUBDIRS += cairo-script
+endif
+
+if CAIRO_HAS_TRACE
+SUBDIRS += cairo-trace
+if CAIRO_HAS_SCRIPT_SURFACE
+if CAIRO_HAS_TEE_SURFACE
+SUBDIRS += cairo-fdr
+endif
+endif
+endif
+
+if BUILD_SPHINX
+if CAIRO_HAS_SCRIPT_SURFACE
+if CAIRO_HAS_TEE_SURFACE
+SUBDIRS += cairo-sphinx
+endif
+endif
+endif
+
+AM_CPPFLAGS = -I$(top_srcdir)/src \
+             -I$(top_builddir)/src \
+             -I$(top_srcdir)/util/cairo-script \
+             $(CAIRO_CFLAGS)
+
+EXTRA_PROGRAMS += show-contour show-traps show-edges show-polygon show-events
+if CAIRO_HAS_INTERPRETER
+EXTRA_PROGRAMS += trace-to-xml xml-to-trace
+endif
+
+trace_to_xml_LDADD = cairo-script/libcairo-script-interpreter.la $(top_builddir)/src/libcairo.la $(CAIRO_LDADD)
+
+xml_to_trace_LDADD = -lexpat
+
+show_traps_SOURCES = show-traps.c
+show_traps_CFLAGS = $(gtk_CFLAGS)
+#show_traps_LDADD = $(top_builddir)/src/libcairo.la $(gtk_LIBS)
+show_traps_LDADD = $(gtk_LIBS)
+
+show_polygon_SOURCES = show-polygon.c
+show_polygon_CFLAGS = $(gtk_CFLAGS)
+#show_polygon_LDADD = $(top_builddir)/src/libcairo.la $(gtk_LIBS)
+show_polygon_LDADD = $(gtk_LIBS)
+
+show_edges_SOURCES = show-edges.c
+show_edges_CFLAGS = $(gtk_CFLAGS)
+#show_edges_LDADD = $(top_builddir)/src/libcairo.la $(gtk_LIBS)
+show_edges_LDADD = $(gtk_LIBS)
+
+show_contour_SOURCES = show-contour.c
+show_contour_CFLAGS = $(gtk_CFLAGS)
+#show_contour_LDADD = $(top_builddir)/src/libcairo.la $(gtk_LIBS)
+show_contour_LDADD = $(gtk_LIBS)
+
+show_events_SOURCES = show-events.c
+show_events_CFLAGS = $(gtk_CFLAGS)
+#show_events_LDADD = $(top_builddir)/src/libcairo.la $(gtk_LIBS)
+show_events_LDADD = $(gtk_LIBS)
+
+util: malloc-stats.so backtrace-symbols.so
+
+.la.so:
+       $(RM) $@
+       $(LN_S) .libs/$*.so $@
+
+CLEANFILES += *.so
+
+# The -rpath is needed to build shared objects that are not installed,
+# ie. with EXTRA_LTLIBRARIES
+AM_LDFLAGS = -module -avoid-version -export-dynamic -rpath /dev/null
+
+EXTRA_LTLIBRARIES += malloc-stats.la backtrace-symbols.la
+
+backtrace_symbols_la_LIBADD = -lbfd -liberty
+
+#malloc_stats_la_LIBADD  = $(backtrace_symbols_la_LIBADD) backtrace-symbols.lo
+
+if HAVE_GTK
+EXTRA_PROGRAMS += font-view
+font_view_CFLAGS = $(gtk_CFLAGS)
+font_view_LDADD = ../src/libcairo.la $(gtk_LIBS)
+endif
+
+EXTRA_DIST += \
+       COPYING \
+       xr2cairo \
+       cairo-api-update \
+       cairo-view \
+       waterfall
diff --git a/util/README b/util/README
new file mode 100644 (file)
index 0000000..39560a8
--- /dev/null
@@ -0,0 +1,67 @@
+Cairo Utilities
+===============
+
+There are a varieties of utilities we use with cairo.
+
+
+backtrace-symbols
+-----------------
+
+This is a small shared library designed to be preloaded by the
+linker and its purpose is to make the backtrace_symbols() function
+of glibc produce more useful source reference information.
+
+Build by:
+
+       make backtrace-symbols.so
+
+and use by:
+
+       LD_PRELOAD=$PWD/backtrace-symbols.so app-to-run
+
+This code should be contributed back to glibc at some point.
+
+
+malloc-stats
+------------
+
+This is a small shared library designed to be preloaded by the
+linker and its purpose is to make the malloc_stats() function
+of glibc produce more useful information.
+
+Build by:
+
+       make malloc-stats.so
+
+and use by:
+
+       LD_PRELOAD=$PWD/malloc-stats.so app-to-run
+
+This works best when backtrace-symbols is in use.  That is:
+
+       LD_PRELOAD="$PWD/backtrace-symbols.so $PWD/malloc-stats.so" app-to-run
+
+
+cairo-trace
+-----------
+
+This tool can be used to trace all the cairo function calls made by an
+applications.  This is useful for either extracting a test case triggering
+a bug from an application, or simply to get a general idea of how an
+application is using cairo.
+
+
+cairo-api-update and xr2cairo
+-----------------------------
+
+These two scripts were used to convert source code written for pre-1.0
+cairo to newer API.  See $(top_srcdir)/PORTING_GUIDE for more information.
+
+These files are obsolete now and may be removed in a future version.
+
+
+cairo-view and waterfall
+------------------------
+
+These are two pycairo scripts useful for testing the toy font backend.
+
diff --git a/util/backtrace-symbols.c b/util/backtrace-symbols.c
new file mode 100644 (file)
index 0000000..045ad78
--- /dev/null
@@ -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-api-update b/util/cairo-api-update
new file mode 100755 (executable)
index 0000000..e16df43
--- /dev/null
@@ -0,0 +1,72 @@
+#!/bin/sh
+set -e
+
+if [ $# -lt 1 ]; then
+    argv0=`basename $0`
+    echo "$argv0: Update source code to the lastest Cairo API" >&2
+    echo "" >&2
+    echo "Usage: $argv0 file [...]" >&2
+    exit 1
+fi
+
+cairo_api_update() {
+       file=$1
+       backup=$file.bak
+
+       cp $file $backup
+       sed -e '/\(DEPRECATED\|REPLACED\)_BY/! {
+               s/cairo_current_font_extents/cairo_font_extents/g
+               s/cairo_get_font_extents/cairo_font_extents/g
+               s/cairo_current_operator/cairo_get_operator/g
+               s/cairo_current_tolerance/cairo_get_tolerance/g
+               s/cairo_current_point/cairo_get_current_point/g
+               s/cairo_current_fill_rule/cairo_get_fill_rule/g
+               s/cairo_current_line_width/cairo_get_line_width/g
+               s/cairo_current_line_cap/cairo_get_line_cap/g
+               s/cairo_current_line_join/cairo_get_line_join/g
+               s/cairo_current_miter_limit/cairo_get_miter_limit/g
+               s/cairo_current_matrix/cairo_get_matrix/g
+               s/cairo_current_pattern/cairo_get_source/g
+               s/cairo_current_target_surface/cairo_get_target/g
+               s/cairo_get_status/cairo_status/g
+               s/cairo_get_status_string/cairo_status_string/g
+               s/cairo_concat_matrix/cairo_transform/g
+               s/cairo_scale_font/cairo_set_font_size/g
+               s/cairo_select_font\([^_]\)/cairo_select_font_face\1/g
+               s/cairo_transform_font/cairo_set_font_matrix/g
+               s/cairo_transform_point/cairo_user_to_device/g
+               s/cairo_transform_distance/cairo_user_to_device_distance/g
+               s/cairo_inverse_transform_point/cairo_device_to_user/g
+               s/cairo_inverse_transform_distance/cairo_device_to_user_distance/g
+               s/cairo_init_clip/cairo_reset_clip/g
+               s/cairo_surface_create_for_image/cairo_image_surface_create_for_data/g
+               s/cairo_default_matrix/cairo_identity_matrix/g
+               s/cairo_matrix_set_affine/cairo_matrix_init/g
+               s/cairo_matrix_set_identity/cairo_matrix_init_identity/g
+               s/\([^_]\)cairo_pattern_add_color_stop\([^_]\)/\1cairo_pattern_add_color_stop_rgba\2/g
+               s/cairo_set_rgb_color/cairo_set_source_rgb/g
+               s/cairo_set_pattern/cairo_set_source/g
+               s/CAIRO_OPERATOR_SRC/CAIRO_OPERATOR_SOURCE/g
+               s/CAIRO_OPERATOR_DST/CAIRO_OPERATOR_DEST/g
+               s/CAIRO_OPERATOR_OVER_REVERSE/CAIRO_OPERATOR_DEST_OVER/g
+               s/CAIRO_OPERATOR_IN_REVERSE/CAIRO_OPERATOR_DEST_IN/g
+               s/CAIRO_OPERATOR_OUT_REVERSE/CAIRO_OPERATOR_DEST_OUT/g
+               s/CAIRO_OPERATOR_ATOP_REVERSE/CAIRO_OPERATOR_DEST_ATOP/g
+               }
+               ' $backup > $file
+
+       grep -n 'cairo_create[  ]*([    ]*)' $file /dev/null | sed 's/^\(.*:[0-9]\+:\).*/\1 cairo_create must now accept a target surface/'
+       grep -n 'cairo_set_target_image' $file /dev/null | sed 's/^\(.*:[0-9]\+:\).*/\1 cairo_set_target_image should be reworked to use cairo_image_surface_create_for_data, likely before cairo_create/'
+       grep -n 'cairo_set_target_surface' $file /dev/null | sed 's/^\(.*:[0-9]\+:\).*/\1 cairo_set_target_surface for temporarily changing the target should now be rworked to create a temporary context with cairo_create/'
+       grep -n 'cairo_set_target_png' $file /dev/null | sed 's/^\(.*:[0-9]\+:\).*/\1 cairo_set_target_png should be reworked to use cairo_image_surface_create followed by cairo_surface_write_to_png/'
+       grep -n 'cairo_set_target_drawable' $file /dev/null | sed 's/^\(.*:[0-9]\+:\).*/\1 cairo_set_target_drawable should be reworked to use cairo_xlib_surface_create, likely before cairo_create/'
+       grep -n 'cairo_set_target_[^dis][^n]' $file /dev/null | sed 's/^\(.*:[0-9]\+:\).*cairo_set_target_\([a-z]*\).*/\1 cairo_set_target_\2 should be reworked to use cairo_\2_surface_create, likely before cairo_create/'
+       grep -n 'cairo_set_alpha' $file /dev/null | sed 's/\(.*:[0-9]\+:\).*/\1 cairo_set_alpha should be replaced by turning a nearby cairo_set_source_rgb into cairo_set_source_rgba or turning a nearby cairo_paint into cairo_paint_with_alpha/'
+}
+
+while [ $# -gt 0 ]; do
+       file=$1
+       shift
+       cairo_api_update $file
+done
+
diff --git a/util/cairo-fdr/Makefile.am b/util/cairo-fdr/Makefile.am
new file mode 100644 (file)
index 0000000..5cd5798
--- /dev/null
@@ -0,0 +1,13 @@
+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
+cairo_fdr_la_LIBADD = -ldl
diff --git a/util/cairo-fdr/fdr.c b/util/cairo-fdr/fdr.c
new file mode 100644 (file)
index 0000000..08d9c01
--- /dev/null
@@ -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
new file mode 100644 (file)
index 0000000..22c1a27
--- /dev/null
@@ -0,0 +1,15 @@
+lib_LTLIBRARIES = libcairo-gobject.la
+
+AM_CPPFLAGS = -I$(top_srcdir)/src -I$(top_builddir)/src
+
+cairoincludedir=$(includedir)/cairo
+cairoinclude_HEADERS = cairo-gobject.h
+libcairo_gobject_la_SOURCES = \
+       cairo-gobject-enums.c \
+       cairo-gobject-structs.c \
+       $(NULL)
+
+libcairo_gobject_la_CFLAGS = $(CAIRO_CFLAGS) $(GOBJECT_CFLAGS)
+libcairo_gobject_la_LDFLAGS = -version-info $(CAIRO_LIBTOOL_VERSION_INFO) -no-undefined $(export_symbols)
+libcairo_gobject_la_LIBADD = $(top_builddir)/src/libcairo.la $(CAIRO_LIBS) $(GOBJECT_LIBS)
+
diff --git a/util/cairo-gobject/cairo-gobject-enums.c b/util/cairo-gobject/cairo-gobject-enums.c
new file mode 100644 (file)
index 0000000..0a7c95d
--- /dev/null
@@ -0,0 +1,511 @@
+
+/* Generated data (by glib-mkenums) */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "cairo-gobject.h"
+
+GType
+cairo_gobject_status_get_type (void)
+{
+   static volatile gsize type_volatile = 0;
+   if (g_once_init_enter (&type_volatile)) {
+      static const GEnumValue values[] = {
+          { CAIRO_STATUS_SUCCESS, "CAIRO_STATUS_SUCCESS", "success" },
+          { CAIRO_STATUS_NO_MEMORY, "CAIRO_STATUS_NO_MEMORY", "no-memory" },
+          { CAIRO_STATUS_INVALID_RESTORE, "CAIRO_STATUS_INVALID_RESTORE", "invalid-restore" },
+          { CAIRO_STATUS_INVALID_POP_GROUP, "CAIRO_STATUS_INVALID_POP_GROUP", "invalid-pop-group" },
+          { CAIRO_STATUS_NO_CURRENT_POINT, "CAIRO_STATUS_NO_CURRENT_POINT", "no-current-point" },
+          { CAIRO_STATUS_INVALID_MATRIX, "CAIRO_STATUS_INVALID_MATRIX", "invalid-matrix" },
+          { CAIRO_STATUS_INVALID_STATUS, "CAIRO_STATUS_INVALID_STATUS", "invalid-status" },
+          { CAIRO_STATUS_NULL_POINTER, "CAIRO_STATUS_NULL_POINTER", "null-pointer" },
+          { CAIRO_STATUS_INVALID_STRING, "CAIRO_STATUS_INVALID_STRING", "invalid-string" },
+          { CAIRO_STATUS_INVALID_PATH_DATA, "CAIRO_STATUS_INVALID_PATH_DATA", "invalid-path-data" },
+          { CAIRO_STATUS_READ_ERROR, "CAIRO_STATUS_READ_ERROR", "read-error" },
+          { CAIRO_STATUS_WRITE_ERROR, "CAIRO_STATUS_WRITE_ERROR", "write-error" },
+          { CAIRO_STATUS_SURFACE_FINISHED, "CAIRO_STATUS_SURFACE_FINISHED", "surface-finished" },
+          { CAIRO_STATUS_SURFACE_TYPE_MISMATCH, "CAIRO_STATUS_SURFACE_TYPE_MISMATCH", "surface-type-mismatch" },
+          { CAIRO_STATUS_PATTERN_TYPE_MISMATCH, "CAIRO_STATUS_PATTERN_TYPE_MISMATCH", "pattern-type-mismatch" },
+          { CAIRO_STATUS_INVALID_CONTENT, "CAIRO_STATUS_INVALID_CONTENT", "invalid-content" },
+          { CAIRO_STATUS_INVALID_FORMAT, "CAIRO_STATUS_INVALID_FORMAT", "invalid-format" },
+          { CAIRO_STATUS_INVALID_VISUAL, "CAIRO_STATUS_INVALID_VISUAL", "invalid-visual" },
+          { CAIRO_STATUS_FILE_NOT_FOUND, "CAIRO_STATUS_FILE_NOT_FOUND", "file-not-found" },
+          { CAIRO_STATUS_INVALID_DASH, "CAIRO_STATUS_INVALID_DASH", "invalid-dash" },
+          { CAIRO_STATUS_INVALID_DSC_COMMENT, "CAIRO_STATUS_INVALID_DSC_COMMENT", "invalid-dsc-comment" },
+          { CAIRO_STATUS_INVALID_INDEX, "CAIRO_STATUS_INVALID_INDEX", "invalid-index" },
+          { CAIRO_STATUS_CLIP_NOT_REPRESENTABLE, "CAIRO_STATUS_CLIP_NOT_REPRESENTABLE", "clip-not-representable" },
+          { CAIRO_STATUS_TEMP_FILE_ERROR, "CAIRO_STATUS_TEMP_FILE_ERROR", "temp-file-error" },
+          { CAIRO_STATUS_INVALID_STRIDE, "CAIRO_STATUS_INVALID_STRIDE", "invalid-stride" },
+          { CAIRO_STATUS_FONT_TYPE_MISMATCH, "CAIRO_STATUS_FONT_TYPE_MISMATCH", "font-type-mismatch" },
+          { CAIRO_STATUS_USER_FONT_IMMUTABLE, "CAIRO_STATUS_USER_FONT_IMMUTABLE", "user-font-immutable" },
+          { CAIRO_STATUS_USER_FONT_ERROR, "CAIRO_STATUS_USER_FONT_ERROR", "user-font-error" },
+          { CAIRO_STATUS_NEGATIVE_COUNT, "CAIRO_STATUS_NEGATIVE_COUNT", "negative-count" },
+          { CAIRO_STATUS_INVALID_CLUSTERS, "CAIRO_STATUS_INVALID_CLUSTERS", "invalid-clusters" },
+          { CAIRO_STATUS_INVALID_SLANT, "CAIRO_STATUS_INVALID_SLANT", "invalid-slant" },
+          { CAIRO_STATUS_INVALID_WEIGHT, "CAIRO_STATUS_INVALID_WEIGHT", "invalid-weight" },
+          { CAIRO_STATUS_INVALID_SIZE, "CAIRO_STATUS_INVALID_SIZE", "invalid-size" },
+          { CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED, "CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED", "user-font-not-implemented" },
+          { CAIRO_STATUS_DEVICE_TYPE_MISMATCH, "CAIRO_STATUS_DEVICE_TYPE_MISMATCH", "device-type-mismatch" },
+          { CAIRO_STATUS_DEVICE_ERROR, "CAIRO_STATUS_DEVICE_ERROR", "device-error" },
+          { CAIRO_STATUS_INVALID_MESH_CONSTRUCTION, "CAIRO_STATUS_INVALID_MESH_CONSTRUCTION", "invalid-mesh-construction" },
+          { CAIRO_STATUS_LAST_STATUS, "CAIRO_STATUS_LAST_STATUS", "last-status" },
+          { 0, NULL, NULL }
+      };
+      GType type = g_enum_register_static (g_intern_static_string ("cairo_status_t"), values);
+
+      g_once_init_leave (&type_volatile, type);
+   }
+   return type_volatile;
+}
+
+GType
+cairo_gobject_content_get_type (void)
+{
+   static volatile gsize type_volatile = 0;
+   if (g_once_init_enter (&type_volatile)) {
+      static const GEnumValue values[] = {
+          { CAIRO_CONTENT_COLOR, "CAIRO_CONTENT_COLOR", "color" },
+          { CAIRO_CONTENT_ALPHA, "CAIRO_CONTENT_ALPHA", "alpha" },
+          { CAIRO_CONTENT_COLOR_ALPHA, "CAIRO_CONTENT_COLOR_ALPHA", "color-alpha" },
+          { 0, NULL, NULL }
+      };
+      GType type = g_enum_register_static (g_intern_static_string ("cairo_content_t"), values);
+
+      g_once_init_leave (&type_volatile, type);
+   }
+   return type_volatile;
+}
+
+GType
+cairo_gobject_operator_get_type (void)
+{
+   static volatile gsize type_volatile = 0;
+   if (g_once_init_enter (&type_volatile)) {
+      static const GEnumValue values[] = {
+          { CAIRO_OPERATOR_CLEAR, "CAIRO_OPERATOR_CLEAR", "clear" },
+          { CAIRO_OPERATOR_SOURCE, "CAIRO_OPERATOR_SOURCE", "source" },
+          { CAIRO_OPERATOR_OVER, "CAIRO_OPERATOR_OVER", "over" },
+          { CAIRO_OPERATOR_IN, "CAIRO_OPERATOR_IN", "in" },
+          { CAIRO_OPERATOR_OUT, "CAIRO_OPERATOR_OUT", "out" },
+          { CAIRO_OPERATOR_ATOP, "CAIRO_OPERATOR_ATOP", "atop" },
+          { CAIRO_OPERATOR_DEST, "CAIRO_OPERATOR_DEST", "dest" },
+          { CAIRO_OPERATOR_DEST_OVER, "CAIRO_OPERATOR_DEST_OVER", "dest-over" },
+          { CAIRO_OPERATOR_DEST_IN, "CAIRO_OPERATOR_DEST_IN", "dest-in" },
+          { CAIRO_OPERATOR_DEST_OUT, "CAIRO_OPERATOR_DEST_OUT", "dest-out" },
+          { CAIRO_OPERATOR_DEST_ATOP, "CAIRO_OPERATOR_DEST_ATOP", "dest-atop" },
+          { CAIRO_OPERATOR_XOR, "CAIRO_OPERATOR_XOR", "xor" },
+          { CAIRO_OPERATOR_ADD, "CAIRO_OPERATOR_ADD", "add" },
+          { CAIRO_OPERATOR_SATURATE, "CAIRO_OPERATOR_SATURATE", "saturate" },
+          { CAIRO_OPERATOR_MULTIPLY, "CAIRO_OPERATOR_MULTIPLY", "multiply" },
+          { CAIRO_OPERATOR_SCREEN, "CAIRO_OPERATOR_SCREEN", "screen" },
+          { CAIRO_OPERATOR_OVERLAY, "CAIRO_OPERATOR_OVERLAY", "overlay" },
+          { CAIRO_OPERATOR_DARKEN, "CAIRO_OPERATOR_DARKEN", "darken" },
+          { CAIRO_OPERATOR_LIGHTEN, "CAIRO_OPERATOR_LIGHTEN", "lighten" },
+          { CAIRO_OPERATOR_COLOR_DODGE, "CAIRO_OPERATOR_COLOR_DODGE", "color-dodge" },
+          { CAIRO_OPERATOR_COLOR_BURN, "CAIRO_OPERATOR_COLOR_BURN", "color-burn" },
+          { CAIRO_OPERATOR_HARD_LIGHT, "CAIRO_OPERATOR_HARD_LIGHT", "hard-light" },
+          { CAIRO_OPERATOR_SOFT_LIGHT, "CAIRO_OPERATOR_SOFT_LIGHT", "soft-light" },
+          { CAIRO_OPERATOR_DIFFERENCE, "CAIRO_OPERATOR_DIFFERENCE", "difference" },
+          { CAIRO_OPERATOR_EXCLUSION, "CAIRO_OPERATOR_EXCLUSION", "exclusion" },
+          { CAIRO_OPERATOR_HSL_HUE, "CAIRO_OPERATOR_HSL_HUE", "hsl-hue" },
+          { CAIRO_OPERATOR_HSL_SATURATION, "CAIRO_OPERATOR_HSL_SATURATION", "hsl-saturation" },
+          { CAIRO_OPERATOR_HSL_COLOR, "CAIRO_OPERATOR_HSL_COLOR", "hsl-color" },
+          { CAIRO_OPERATOR_HSL_LUMINOSITY, "CAIRO_OPERATOR_HSL_LUMINOSITY", "hsl-luminosity" },
+          { 0, NULL, NULL }
+      };
+      GType type = g_enum_register_static (g_intern_static_string ("cairo_operator_t"), values);
+
+      g_once_init_leave (&type_volatile, type);
+   }
+   return type_volatile;
+}
+
+GType
+cairo_gobject_antialias_get_type (void)
+{
+   static volatile gsize type_volatile = 0;
+   if (g_once_init_enter (&type_volatile)) {
+      static const GEnumValue values[] = {
+          { CAIRO_ANTIALIAS_DEFAULT, "CAIRO_ANTIALIAS_DEFAULT", "default" },
+
+          { CAIRO_ANTIALIAS_NONE, "CAIRO_ANTIALIAS_NONE", "none" },
+          { CAIRO_ANTIALIAS_GRAY, "CAIRO_ANTIALIAS_GRAY", "gray" },
+          { CAIRO_ANTIALIAS_SUBPIXEL, "CAIRO_ANTIALIAS_SUBPIXEL", "subpixel" },
+
+          { CAIRO_ANTIALIAS_FAST, "CAIRO_ANTIALIAS_FAST", "fast" },
+          { CAIRO_ANTIALIAS_GOOD, "CAIRO_ANTIALIAS_GOOD", "good" },
+          { CAIRO_ANTIALIAS_BEST, "CAIRO_ANTIALIAS_BEST", "best" },
+
+          { 0, NULL, NULL }
+      };
+      GType type = g_enum_register_static (g_intern_static_string ("cairo_antialias_t"), values);
+
+      g_once_init_leave (&type_volatile, type);
+   }
+   return type_volatile;
+}
+
+GType
+cairo_gobject_fill_rule_get_type (void)
+{
+   static volatile gsize type_volatile = 0;
+   if (g_once_init_enter (&type_volatile)) {
+      static const GEnumValue values[] = {
+          { CAIRO_FILL_RULE_WINDING, "CAIRO_FILL_RULE_WINDING", "winding" },
+          { CAIRO_FILL_RULE_EVEN_ODD, "CAIRO_FILL_RULE_EVEN_ODD", "even-odd" },
+          { 0, NULL, NULL }
+      };
+      GType type = g_enum_register_static (g_intern_static_string ("cairo_fill_rule_t"), values);
+
+      g_once_init_leave (&type_volatile, type);
+   }
+   return type_volatile;
+}
+
+GType
+cairo_gobject_line_cap_get_type (void)
+{
+   static volatile gsize type_volatile = 0;
+   if (g_once_init_enter (&type_volatile)) {
+      static const GEnumValue values[] = {
+          { CAIRO_LINE_CAP_BUTT, "CAIRO_LINE_CAP_BUTT", "butt" },
+          { CAIRO_LINE_CAP_ROUND, "CAIRO_LINE_CAP_ROUND", "round" },
+          { CAIRO_LINE_CAP_SQUARE, "CAIRO_LINE_CAP_SQUARE", "square" },
+          { 0, NULL, NULL }
+      };
+      GType type = g_enum_register_static (g_intern_static_string ("cairo_line_cap_t"), values);
+
+      g_once_init_leave (&type_volatile, type);
+   }
+   return type_volatile;
+}
+
+GType
+cairo_gobject_line_join_get_type (void)
+{
+   static volatile gsize type_volatile = 0;
+   if (g_once_init_enter (&type_volatile)) {
+      static const GEnumValue values[] = {
+          { CAIRO_LINE_JOIN_MITER, "CAIRO_LINE_JOIN_MITER", "miter" },
+          { CAIRO_LINE_JOIN_ROUND, "CAIRO_LINE_JOIN_ROUND", "round" },
+          { CAIRO_LINE_JOIN_BEVEL, "CAIRO_LINE_JOIN_BEVEL", "bevel" },
+          { 0, NULL, NULL }
+      };
+      GType type = g_enum_register_static (g_intern_static_string ("cairo_line_join_t"), values);
+
+      g_once_init_leave (&type_volatile, type);
+   }
+   return type_volatile;
+}
+
+GType
+cairo_gobject_text_cluster_flags_get_type (void)
+{
+   static volatile gsize type_volatile = 0;
+   if (g_once_init_enter (&type_volatile)) {
+      static const GEnumValue values[] = {
+          { CAIRO_TEXT_CLUSTER_FLAG_BACKWARD, "CAIRO_TEXT_CLUSTER_FLAG_BACKWARD", "backward" },
+          { 0, NULL, NULL }
+      };
+      GType type = g_enum_register_static (g_intern_static_string ("cairo_text_cluster_flags_t"), values);
+
+      g_once_init_leave (&type_volatile, type);
+   }
+   return type_volatile;
+}
+
+GType
+cairo_gobject_font_slant_get_type (void)
+{
+   static volatile gsize type_volatile = 0;
+   if (g_once_init_enter (&type_volatile)) {
+      static const GEnumValue values[] = {
+          { CAIRO_FONT_SLANT_NORMAL, "CAIRO_FONT_SLANT_NORMAL", "normal" },
+          { CAIRO_FONT_SLANT_ITALIC, "CAIRO_FONT_SLANT_ITALIC", "italic" },
+          { CAIRO_FONT_SLANT_OBLIQUE, "CAIRO_FONT_SLANT_OBLIQUE", "oblique" },
+          { 0, NULL, NULL }
+      };
+      GType type = g_enum_register_static (g_intern_static_string ("cairo_font_slant_t"), values);
+
+      g_once_init_leave (&type_volatile, type);
+   }
+   return type_volatile;
+}
+
+GType
+cairo_gobject_font_weight_get_type (void)
+{
+   static volatile gsize type_volatile = 0;
+   if (g_once_init_enter (&type_volatile)) {
+      static const GEnumValue values[] = {
+          { CAIRO_FONT_WEIGHT_NORMAL, "CAIRO_FONT_WEIGHT_NORMAL", "normal" },
+          { CAIRO_FONT_WEIGHT_BOLD, "CAIRO_FONT_WEIGHT_BOLD", "bold" },
+          { 0, NULL, NULL }
+      };
+      GType type = g_enum_register_static (g_intern_static_string ("cairo_font_weight_t"), values);
+
+      g_once_init_leave (&type_volatile, type);
+   }
+   return type_volatile;
+}
+
+GType
+cairo_gobject_subpixel_order_get_type (void)
+{
+   static volatile gsize type_volatile = 0;
+   if (g_once_init_enter (&type_volatile)) {
+      static const GEnumValue values[] = {
+          { CAIRO_SUBPIXEL_ORDER_DEFAULT, "CAIRO_SUBPIXEL_ORDER_DEFAULT", "default" },
+          { CAIRO_SUBPIXEL_ORDER_RGB, "CAIRO_SUBPIXEL_ORDER_RGB", "rgb" },
+          { CAIRO_SUBPIXEL_ORDER_BGR, "CAIRO_SUBPIXEL_ORDER_BGR", "bgr" },
+          { CAIRO_SUBPIXEL_ORDER_VRGB, "CAIRO_SUBPIXEL_ORDER_VRGB", "vrgb" },
+          { CAIRO_SUBPIXEL_ORDER_VBGR, "CAIRO_SUBPIXEL_ORDER_VBGR", "vbgr" },
+          { 0, NULL, NULL }
+      };
+      GType type = g_enum_register_static (g_intern_static_string ("cairo_subpixel_order_t"), values);
+
+      g_once_init_leave (&type_volatile, type);
+   }
+   return type_volatile;
+}
+
+GType
+cairo_gobject_hint_style_get_type (void)
+{
+   static volatile gsize type_volatile = 0;
+   if (g_once_init_enter (&type_volatile)) {
+      static const GEnumValue values[] = {
+          { CAIRO_HINT_STYLE_DEFAULT, "CAIRO_HINT_STYLE_DEFAULT", "default" },
+          { CAIRO_HINT_STYLE_NONE, "CAIRO_HINT_STYLE_NONE", "none" },
+          { CAIRO_HINT_STYLE_SLIGHT, "CAIRO_HINT_STYLE_SLIGHT", "slight" },
+          { CAIRO_HINT_STYLE_MEDIUM, "CAIRO_HINT_STYLE_MEDIUM", "medium" },
+          { CAIRO_HINT_STYLE_FULL, "CAIRO_HINT_STYLE_FULL", "full" },
+          { 0, NULL, NULL }
+      };
+      GType type = g_enum_register_static (g_intern_static_string ("cairo_hint_style_t"), values);
+
+      g_once_init_leave (&type_volatile, type);
+   }
+   return type_volatile;
+}
+
+GType
+cairo_gobject_hint_metrics_get_type (void)
+{
+   static volatile gsize type_volatile = 0;
+   if (g_once_init_enter (&type_volatile)) {
+      static const GEnumValue values[] = {
+          { CAIRO_HINT_METRICS_DEFAULT, "CAIRO_HINT_METRICS_DEFAULT", "default" },
+          { CAIRO_HINT_METRICS_OFF, "CAIRO_HINT_METRICS_OFF", "off" },
+          { CAIRO_HINT_METRICS_ON, "CAIRO_HINT_METRICS_ON", "on" },
+          { 0, NULL, NULL }
+      };
+      GType type = g_enum_register_static (g_intern_static_string ("cairo_hint_metrics_t"), values);
+
+      g_once_init_leave (&type_volatile, type);
+   }
+   return type_volatile;
+}
+
+GType
+cairo_gobject_font_type_get_type (void)
+{
+   static volatile gsize type_volatile = 0;
+   if (g_once_init_enter (&type_volatile)) {
+      static const GEnumValue values[] = {
+          { CAIRO_FONT_TYPE_TOY, "CAIRO_FONT_TYPE_TOY", "toy" },
+          { CAIRO_FONT_TYPE_FT, "CAIRO_FONT_TYPE_FT", "ft" },
+          { CAIRO_FONT_TYPE_WIN32, "CAIRO_FONT_TYPE_WIN32", "win32" },
+          { CAIRO_FONT_TYPE_QUARTZ, "CAIRO_FONT_TYPE_QUARTZ", "quartz" },
+          { CAIRO_FONT_TYPE_USER, "CAIRO_FONT_TYPE_USER", "user" },
+          { 0, NULL, NULL }
+      };
+      GType type = g_enum_register_static (g_intern_static_string ("cairo_font_type_t"), values);
+
+      g_once_init_leave (&type_volatile, type);
+   }
+   return type_volatile;
+}
+
+GType
+cairo_gobject_path_data_type_get_type (void)
+{
+   static volatile gsize type_volatile = 0;
+   if (g_once_init_enter (&type_volatile)) {
+      static const GEnumValue values[] = {
+          { CAIRO_PATH_MOVE_TO, "CAIRO_PATH_MOVE_TO", "move-to" },
+          { CAIRO_PATH_LINE_TO, "CAIRO_PATH_LINE_TO", "line-to" },
+          { CAIRO_PATH_CURVE_TO, "CAIRO_PATH_CURVE_TO", "curve-to" },
+          { CAIRO_PATH_CLOSE_PATH, "CAIRO_PATH_CLOSE_PATH", "close-path" },
+          { 0, NULL, NULL }
+      };
+      GType type = g_enum_register_static (g_intern_static_string ("cairo_path_data_type_t"), values);
+
+      g_once_init_leave (&type_volatile, type);
+   }
+   return type_volatile;
+}
+
+GType
+cairo_gobject_device_type_get_type (void)
+{
+   static volatile gsize type_volatile = 0;
+   if (g_once_init_enter (&type_volatile)) {
+      static const GEnumValue values[] = {
+          { CAIRO_DEVICE_TYPE_DRM, "CAIRO_DEVICE_TYPE_DRM", "drm" },
+          { CAIRO_DEVICE_TYPE_GL, "CAIRO_DEVICE_TYPE_GL", "gl" },
+          { CAIRO_DEVICE_TYPE_SCRIPT, "CAIRO_DEVICE_TYPE_SCRIPT", "script" },
+          { CAIRO_DEVICE_TYPE_XCB, "CAIRO_DEVICE_TYPE_XCB", "xcb" },
+          { CAIRO_DEVICE_TYPE_XLIB, "CAIRO_DEVICE_TYPE_XLIB", "xlib" },
+          { CAIRO_DEVICE_TYPE_XML, "CAIRO_DEVICE_TYPE_XML", "xml" },
+          { 0, NULL, NULL }
+      };
+      GType type = g_enum_register_static (g_intern_static_string ("cairo_device_type_t"), values);
+
+      g_once_init_leave (&type_volatile, type);
+   }
+   return type_volatile;
+}
+
+GType
+cairo_gobject_surface_type_get_type (void)
+{
+   static volatile gsize type_volatile = 0;
+   if (g_once_init_enter (&type_volatile)) {
+      static const GEnumValue values[] = {
+          { CAIRO_SURFACE_TYPE_IMAGE, "CAIRO_SURFACE_TYPE_IMAGE", "image" },
+          { CAIRO_SURFACE_TYPE_PDF, "CAIRO_SURFACE_TYPE_PDF", "pdf" },
+          { CAIRO_SURFACE_TYPE_PS, "CAIRO_SURFACE_TYPE_PS", "ps" },
+          { CAIRO_SURFACE_TYPE_XLIB, "CAIRO_SURFACE_TYPE_XLIB", "xlib" },
+          { CAIRO_SURFACE_TYPE_XCB, "CAIRO_SURFACE_TYPE_XCB", "xcb" },
+          { CAIRO_SURFACE_TYPE_GLITZ, "CAIRO_SURFACE_TYPE_GLITZ", "glitz" },
+          { CAIRO_SURFACE_TYPE_QUARTZ, "CAIRO_SURFACE_TYPE_QUARTZ", "quartz" },
+          { CAIRO_SURFACE_TYPE_WIN32, "CAIRO_SURFACE_TYPE_WIN32", "win32" },
+          { CAIRO_SURFACE_TYPE_BEOS, "CAIRO_SURFACE_TYPE_BEOS", "beos" },
+          { CAIRO_SURFACE_TYPE_DIRECTFB, "CAIRO_SURFACE_TYPE_DIRECTFB", "directfb" },
+          { CAIRO_SURFACE_TYPE_SVG, "CAIRO_SURFACE_TYPE_SVG", "svg" },
+          { CAIRO_SURFACE_TYPE_OS2, "CAIRO_SURFACE_TYPE_OS2", "os2" },
+          { CAIRO_SURFACE_TYPE_WIN32_PRINTING, "CAIRO_SURFACE_TYPE_WIN32_PRINTING", "win32-printing" },
+          { CAIRO_SURFACE_TYPE_QUARTZ_IMAGE, "CAIRO_SURFACE_TYPE_QUARTZ_IMAGE", "quartz-image" },
+          { CAIRO_SURFACE_TYPE_SCRIPT, "CAIRO_SURFACE_TYPE_SCRIPT", "script" },
+          { CAIRO_SURFACE_TYPE_QT, "CAIRO_SURFACE_TYPE_QT", "qt" },
+          { CAIRO_SURFACE_TYPE_RECORDING, "CAIRO_SURFACE_TYPE_RECORDING", "recording" },
+          { CAIRO_SURFACE_TYPE_VG, "CAIRO_SURFACE_TYPE_VG", "vg" },
+          { CAIRO_SURFACE_TYPE_GL, "CAIRO_SURFACE_TYPE_GL", "gl" },
+          { CAIRO_SURFACE_TYPE_DRM, "CAIRO_SURFACE_TYPE_DRM", "drm" },
+          { CAIRO_SURFACE_TYPE_TEE, "CAIRO_SURFACE_TYPE_TEE", "tee" },
+          { CAIRO_SURFACE_TYPE_XML, "CAIRO_SURFACE_TYPE_XML", "xml" },
+          { CAIRO_SURFACE_TYPE_SKIA, "CAIRO_SURFACE_TYPE_SKIA", "skia" },
+          { 0, NULL, NULL }
+      };
+      GType type = g_enum_register_static (g_intern_static_string ("cairo_surface_type_t"), values);
+
+      g_once_init_leave (&type_volatile, type);
+   }
+   return type_volatile;
+}
+
+GType
+cairo_gobject_format_get_type (void)
+{
+   static volatile gsize type_volatile = 0;
+   if (g_once_init_enter (&type_volatile)) {
+      static const GEnumValue values[] = {
+          { CAIRO_FORMAT_INVALID, "CAIRO_FORMAT_INVALID", "invalid" },
+          { CAIRO_FORMAT_ARGB32, "CAIRO_FORMAT_ARGB32", "argb32" },
+          { CAIRO_FORMAT_RGB24, "CAIRO_FORMAT_RGB24", "rgb24" },
+          { CAIRO_FORMAT_A8, "CAIRO_FORMAT_A8", "a8" },
+          { CAIRO_FORMAT_A1, "CAIRO_FORMAT_A1", "a1" },
+          { CAIRO_FORMAT_RGB16_565, "CAIRO_FORMAT_RGB16_565", "rgb16-565" },
+          { 0, NULL, NULL }
+      };
+      GType type = g_enum_register_static (g_intern_static_string ("cairo_format_t"), values);
+
+      g_once_init_leave (&type_volatile, type);
+   }
+   return type_volatile;
+}
+
+GType
+cairo_gobject_pattern_type_get_type (void)
+{
+   static volatile gsize type_volatile = 0;
+   if (g_once_init_enter (&type_volatile)) {
+      static const GEnumValue values[] = {
+          { CAIRO_PATTERN_TYPE_SOLID, "CAIRO_PATTERN_TYPE_SOLID", "solid" },
+          { CAIRO_PATTERN_TYPE_SURFACE, "CAIRO_PATTERN_TYPE_SURFACE", "surface" },
+          { CAIRO_PATTERN_TYPE_LINEAR, "CAIRO_PATTERN_TYPE_LINEAR", "linear" },
+          { CAIRO_PATTERN_TYPE_RADIAL, "CAIRO_PATTERN_TYPE_RADIAL", "radial" },
+          { CAIRO_PATTERN_TYPE_MESH, "CAIRO_PATTERN_TYPE_MESH", "mesh" },
+          { 0, NULL, NULL }
+      };
+      GType type = g_enum_register_static (g_intern_static_string ("cairo_pattern_type_t"), values);
+
+      g_once_init_leave (&type_volatile, type);
+   }
+   return type_volatile;
+}
+
+GType
+cairo_gobject_extend_get_type (void)
+{
+   static volatile gsize type_volatile = 0;
+   if (g_once_init_enter (&type_volatile)) {
+      static const GEnumValue values[] = {
+          { CAIRO_EXTEND_NONE, "CAIRO_EXTEND_NONE", "none" },
+          { CAIRO_EXTEND_REPEAT, "CAIRO_EXTEND_REPEAT", "repeat" },
+          { CAIRO_EXTEND_REFLECT, "CAIRO_EXTEND_REFLECT", "reflect" },
+          { CAIRO_EXTEND_PAD, "CAIRO_EXTEND_PAD", "pad" },
+          { 0, NULL, NULL }
+      };
+      GType type = g_enum_register_static (g_intern_static_string ("cairo_extend_t"), values);
+
+      g_once_init_leave (&type_volatile, type);
+   }
+   return type_volatile;
+}
+
+GType
+cairo_gobject_filter_get_type (void)
+{
+   static volatile gsize type_volatile = 0;
+   if (g_once_init_enter (&type_volatile)) {
+      static const GEnumValue values[] = {
+          { CAIRO_FILTER_FAST, "CAIRO_FILTER_FAST", "fast" },
+          { CAIRO_FILTER_GOOD, "CAIRO_FILTER_GOOD", "good" },
+          { CAIRO_FILTER_BEST, "CAIRO_FILTER_BEST", "best" },
+          { CAIRO_FILTER_NEAREST, "CAIRO_FILTER_NEAREST", "nearest" },
+          { CAIRO_FILTER_BILINEAR, "CAIRO_FILTER_BILINEAR", "bilinear" },
+          { CAIRO_FILTER_GAUSSIAN, "CAIRO_FILTER_GAUSSIAN", "gaussian" },
+          { 0, NULL, NULL }
+      };
+      GType type = g_enum_register_static (g_intern_static_string ("cairo_filter_t"), values);
+
+      g_once_init_leave (&type_volatile, type);
+   }
+   return type_volatile;
+}
+
+GType
+cairo_gobject_region_overlap_get_type (void)
+{
+   static volatile gsize type_volatile = 0;
+   if (g_once_init_enter (&type_volatile)) {
+      static const GEnumValue values[] = {
+          { CAIRO_REGION_OVERLAP_IN, "CAIRO_REGION_OVERLAP_IN", "in" },
+          { CAIRO_REGION_OVERLAP_OUT, "CAIRO_REGION_OVERLAP_OUT", "out" },
+          { CAIRO_REGION_OVERLAP_PART, "CAIRO_REGION_OVERLAP_PART", "part" },
+          { 0, NULL, NULL }
+      };
+      GType type = g_enum_register_static (g_intern_static_string ("cairo_region_overlap_t"), values);
+
+      g_once_init_leave (&type_volatile, type);
+   }
+   return type_volatile;
+}
+
+
+/* Generated data ends here */
+
diff --git a/util/cairo-gobject/cairo-gobject-structs.c b/util/cairo-gobject/cairo-gobject-structs.c
new file mode 100644 (file)
index 0000000..05e3ece
--- /dev/null
@@ -0,0 +1,87 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2010 Red Hat Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *     Benjamin Otte <otte@redhat.com>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "cairo-gobject.h"
+
+#define CAIRO_DEFINE_BOXED(Name,underscore_name,copy_func,free_func) \
+GType \
+underscore_name ## _get_type (void) \
+{ \
+   static volatile gsize type_volatile = 0; \
+   if (g_once_init_enter (&type_volatile)) { \
+      GType type = g_boxed_type_register_static (g_intern_static_string (Name), \
+                                                 (GBoxedCopyFunc)copy_func, \
+                                                 (GBoxedFreeFunc)free_func); \
+      g_once_init_leave (&type_volatile, type); \
+   } \
+   return type_volatile; \
+}
+
+CAIRO_DEFINE_BOXED ("CairoContext", cairo_gobject_context, 
+                    cairo_reference, cairo_destroy);
+CAIRO_DEFINE_BOXED ("CairoDevice", cairo_gobject_device, 
+                    cairo_device_reference, cairo_device_destroy);
+CAIRO_DEFINE_BOXED ("CairoPattern", cairo_gobject_pattern, 
+                    cairo_pattern_reference, cairo_pattern_destroy);
+CAIRO_DEFINE_BOXED ("CairoSurface", cairo_gobject_surface, 
+                    cairo_surface_reference, cairo_surface_destroy);
+CAIRO_DEFINE_BOXED ("CairoScaledFont", cairo_gobject_scaled_font, 
+                    cairo_scaled_font_reference, cairo_scaled_font_destroy);
+CAIRO_DEFINE_BOXED ("CairoFontFace", cairo_gobject_font_face, 
+                    cairo_font_face_reference, cairo_font_face_destroy);
+CAIRO_DEFINE_BOXED ("CairoFontOptions", cairo_gobject_font_options, 
+                    cairo_font_options_copy, cairo_font_options_destroy);
+CAIRO_DEFINE_BOXED ("CairoRegion", cairo_gobject_region, 
+                    cairo_region_reference, cairo_region_destroy);
+
+#define COPY_FUNC(name) \
+static gpointer \
+cairo_gobject_cairo_ ## name ## _copy (gpointer src) \
+{ \
+    return g_memdup (src, sizeof (cairo_ ## name ## _t)); \
+}
+
+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
new file mode 100644 (file)
index 0000000..f43a6d0
--- /dev/null
@@ -0,0 +1,186 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2010 Red Hat Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *     Benjamin Otte <otte@redhat.com>
+ */
+
+#ifndef CAIRO_GOBJECT_H
+#define CAIRO_GOBJECT_H
+
+#include <cairo.h>
+
+#if CAIRO_HAS_GOBJECT_FUNCTIONS
+
+#include <glib-object.h>
+
+CAIRO_BEGIN_DECLS
+
+/* structs */
+
+#define CAIRO_GOBJECT_TYPE_CONTEXT cairo_gobject_context_get_type ()
+cairo_public GType
+cairo_gobject_context_get_type (void);
+
+#define CAIRO_GOBJECT_TYPE_DEVICE cairo_gobject_device_get_type ()
+cairo_public GType
+cairo_gobject_device_get_type (void);
+
+#define CAIRO_GOBJECT_TYPE_PATTERN cairo_gobject_pattern_get_type ()
+cairo_public GType
+cairo_gobject_pattern_get_type (void);
+
+#define CAIRO_GOBJECT_TYPE_SURFACE cairo_gobject_surface_get_type ()
+cairo_public GType
+cairo_gobject_surface_get_type (void);
+
+#define CAIRO_GOBJECT_TYPE_RECTANGLE cairo_gobject_rectangle_get_type ()
+cairo_public GType
+cairo_gobject_rectangle_get_type (void);
+
+#define CAIRO_GOBJECT_TYPE_SCALED_FONT cairo_gobject_scaled_font_get_type ()
+cairo_public GType
+cairo_gobject_scaled_font_get_type (void);
+
+#define CAIRO_GOBJECT_TYPE_FONT_FACE cairo_gobject_font_face_get_type ()
+cairo_public GType
+cairo_gobject_font_face_get_type (void);
+
+#define CAIRO_GOBJECT_TYPE_FONT_OPTIONS cairo_gobject_font_options_get_type ()
+cairo_public GType
+cairo_gobject_font_options_get_type (void);
+
+#define CAIRO_GOBJECT_TYPE_RECTANGLE_INT cairo_gobject_rectangle_int_get_type ()
+cairo_public GType
+cairo_gobject_rectangle_int_get_type (void);
+
+#define CAIRO_GOBJECT_TYPE_REGION cairo_gobject_region_get_type ()
+cairo_public GType
+cairo_gobject_region_get_type (void);
+
+/* enums */
+
+#define CAIRO_GOBJECT_TYPE_STATUS cairo_gobject_status_get_type ()
+cairo_public GType
+cairo_gobject_status_get_type (void);
+
+#define CAIRO_GOBJECT_TYPE_CONTENT cairo_gobject_content_get_type ()
+cairo_public GType
+cairo_gobject_content_get_type (void);
+
+#define CAIRO_GOBJECT_TYPE_OPERATOR cairo_gobject_operator_get_type ()
+cairo_public GType
+cairo_gobject_operator_get_type (void);
+
+#define CAIRO_GOBJECT_TYPE_ANTIALIAS cairo_gobject_antialias_get_type ()
+cairo_public GType
+cairo_gobject_antialias_get_type (void);
+
+#define CAIRO_GOBJECT_TYPE_FILL_RULE cairo_gobject_fill_rule_get_type ()
+cairo_public GType
+cairo_gobject_fill_rule_get_type (void);
+
+#define CAIRO_GOBJECT_TYPE_LINE_CAP cairo_gobject_line_cap_get_type ()
+cairo_public GType
+cairo_gobject_line_cap_get_type (void);
+
+#define CAIRO_GOBJECT_TYPE_LINE_JOIN cairo_gobject_line_join_get_type ()
+cairo_public GType
+cairo_gobject_line_join_get_type (void);
+
+#define CAIRO_GOBJECT_TYPE_CLUSTER_FLAGS cairo_gobject_cluster_flags_get_type ()
+cairo_public GType
+cairo_gobject_text_cluster_flags_get_type (void);
+
+#define CAIRO_GOBJECT_TYPE_FONT_SLANT cairo_gobject_font_slant_get_type ()
+cairo_public GType
+cairo_gobject_font_slant_get_type (void);
+
+#define CAIRO_GOBJECT_TYPE_FONT_WEIGHT cairo_gobject_font_weight_get_type ()
+cairo_public GType
+cairo_gobject_font_weight_get_type (void);
+
+#define CAIRO_GOBJECT_TYPE_SUBPIXEL_ORDER cairo_gobject_subpixel_order_get_type ()
+cairo_public GType
+cairo_gobject_subpixel_order_get_type (void);
+
+#define CAIRO_GOBJECT_TYPE_HINT_STYLE cairo_gobject_hint_style_get_type ()
+cairo_public GType
+cairo_gobject_hint_style_get_type (void);
+
+#define CAIRO_GOBJECT_TYPE_HNT_METRICS cairo_gobject_hint_metrics_get_type ()
+cairo_public GType
+cairo_gobject_hint_metrics_get_type (void);
+
+#define CAIRO_GOBJECT_TYPE_FONT_TYPE cairo_gobject_font_type_get_type ()
+cairo_public GType
+cairo_gobject_font_type_get_type (void);
+
+#define CAIRO_GOBJECT_TYPE_PATH_DATA_TYPE cairo_gobject_path_data_type_get_type ()
+cairo_public GType
+cairo_gobject_path_data_type_get_type (void);
+
+#define CAIRO_GOBJECT_TYPE_DEVICE_TYPE cairo_gobject_device_type_get_type ()
+cairo_public GType
+cairo_gobject_device_type_get_type (void);
+
+#define CAIRO_GOBJECT_TYPE_SURFACE_TYPE cairo_gobject_surface_type_get_type ()
+cairo_public GType
+cairo_gobject_surface_type_get_type (void);
+
+#define CAIRO_GOBJECT_TYPE_FORMAT cairo_gobject_format_get_type ()
+cairo_public GType
+cairo_gobject_format_get_type (void);
+
+#define CAIRO_GOBJECT_TYPE_PATTERN_TYPE cairo_gobject_pattern_type_get_type ()
+cairo_public GType
+cairo_gobject_pattern_type_get_type (void);
+
+#define CAIRO_GOBJECT_TYPE_EXTEND cairo_gobject_extend_get_type ()
+cairo_public GType
+cairo_gobject_extend_get_type (void);
+
+#define CAIRO_GOBJECT_TYPE_FILTER cairo_gobject_filter_get_type ()
+cairo_public GType
+cairo_gobject_filter_get_type (void);
+
+#define CAIRO_GOBJECT_TYPE_REGION_OVERLAP cairo_gobject_region_overlap_get_type ()
+cairo_public GType
+cairo_gobject_region_overlap_get_type (void);
+
+CAIRO_END_DECLS
+
+#else  /* CAIRO_HAS_GOBJECT_FUNCTIONS */
+# error Cairo was not compiled with support for GObject
+#endif /* CAIRO_HAS_GOBJECT_FUNCTIONS */
+
+#endif /* CAIRO_GOBJECT_H */
diff --git a/util/cairo-missing/Makefile.am b/util/cairo-missing/Makefile.am
new file mode 100644 (file)
index 0000000..9078610
--- /dev/null
@@ -0,0 +1,10 @@
+include $(top_srcdir)/util/cairo-missing/Makefile.sources
+
+AM_CPPFLAGS = -I$(top_srcdir)/src -I$(top_builddir)/src
+
+noinst_LTLIBRARIES = libcairo-missing.la
+
+libcairo_missing_la_SOURCES = \
+       $(libcairo_missing_sources)     \
+       $(libcairo_missing_headers)     \
+       $(NULL)
diff --git a/util/cairo-missing/Makefile.sources b/util/cairo-missing/Makefile.sources
new file mode 100644 (file)
index 0000000..1a30631
--- /dev/null
@@ -0,0 +1,8 @@
+libcairo_missing_sources = \
+       strndup.c \
+       getline.c \
+       $(NULL)
+
+libcairo_missing_headers = \
+       cairo-missing.h \
+       $(NULL)
diff --git a/util/cairo-missing/Makefile.win32 b/util/cairo-missing/Makefile.win32
new file mode 100644 (file)
index 0000000..c2c5bc0
--- /dev/null
@@ -0,0 +1,10 @@
+top_srcdir = ../../
+include $(top_srcdir)/build/Makefile.win32.common
+include $(top_srcdir)/util/cairo-missing/Makefile.sources
+
+all: inform $(CFG)/libcairo-missing.lib
+
+libcairo_missing_OBJECTS = $(patsubst %.c, $(CFG)/%-static.obj, $(libcairo_missing_sources))
+
+$(CFG)/libcairo-missing.lib: $(libcairo_missing_OBJECTS)
+       @$(AR) $(CAIRO_ARFLAGS) -OUT:$@ $^
diff --git a/util/cairo-missing/cairo-missing.h b/util/cairo-missing/cairo-missing.h
new file mode 100644 (file)
index 0000000..ccb85fa
--- /dev/null
@@ -0,0 +1,57 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2006 Red Hat, Inc.
+ * Copyright © 2011 Andrea Canciani
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of the
+ * copyright holders not be used in advertising or publicity
+ * pertaining to distribution of the software without specific,
+ * written prior permission. The copyright holders make no
+ * representations about the suitability of this software for any
+ * purpose.  It is provided "as is" without express or implied
+ * warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ * SOFTWARE.
+ *
+ * Authors: Carl Worth <cworth@cworth.org>
+ *         Andrea Canciani <ranma42@gmail.com>
+ */
+
+#ifndef CAIRO_MISSING_H
+#define CAIRO_MISSING_H
+
+#include "cairo-compiler-private.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+
+#ifdef _WIN32
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+typedef SSIZE_T ssize_t;
+#endif
+
+#ifndef HAVE_GETLINE
+cairo_private ssize_t
+getline (char **lineptr, size_t *n, FILE *stream);
+#endif
+
+#ifndef HAVE_STRNDUP
+cairo_private char *
+strndup (const char *s, size_t n);
+#endif
+
+#endif
diff --git a/util/cairo-missing/getline.c b/util/cairo-missing/getline.c
new file mode 100644 (file)
index 0000000..8585cfd
--- /dev/null
@@ -0,0 +1,90 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2006 Red Hat, Inc.
+ * Copyright © 2011 Andrea Canciani
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of the
+ * copyright holders not be used in advertising or publicity
+ * pertaining to distribution of the software without specific,
+ * written prior permission. The copyright holders make no
+ * representations about the suitability of this software for any
+ * purpose.  It is provided "as is" without express or implied
+ * warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ * SOFTWARE.
+ *
+ * Authors: Carl Worth <cworth@cworth.org>
+ *         Andrea Canciani <ranma42@gmail.com>
+ */
+
+#include "cairo-missing.h"
+
+#ifndef HAVE_GETLINE
+#include "cairo-malloc-private.h"
+
+#define GETLINE_MIN_BUFFER_SIZE 128
+ssize_t
+getline (char  **lineptr,
+        size_t  *n,
+        FILE    *stream)
+{
+    char *line, *tmpline;
+    size_t len, offset;
+    ssize_t ret;
+
+    offset = 0;
+    len = *n;
+    line = *lineptr;
+    if (len < GETLINE_MIN_BUFFER_SIZE) {
+       len = GETLINE_MIN_BUFFER_SIZE;
+       line = NULL;
+    }
+
+    if (line == NULL) {
+       line = (char *) _cairo_malloc (len);
+       if (unlikely (line == NULL))
+           return -1;
+    }
+
+    while (1) {
+       if (offset + 1 == len) {
+           tmpline = (char *) _cairo_realloc_ab (line, len, 2);
+           if (unlikely (tmpline == NULL)) {
+               if (line != *lineptr)
+                   free (line);
+               return -1;
+           }
+           len *= 2;
+           line = tmpline;
+       }
+
+       ret = getc (stream);
+       if (ret == -1)
+           break;
+
+       line[offset++] = ret;
+       if (ret == '\n') {
+           ret = offset;
+           break;
+       }
+    }
+
+    line[offset++] = '\0';
+    *lineptr = line;
+    *n = len;
+
+    return ret;
+}
+#undef GETLINE_BUFFER_SIZE
+#endif
diff --git a/util/cairo-missing/strndup.c b/util/cairo-missing/strndup.c
new file mode 100644 (file)
index 0000000..280ea30
--- /dev/null
@@ -0,0 +1,57 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2006 Red Hat, Inc.
+ * Copyright © 2011 Andrea Canciani
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of the
+ * copyright holders not be used in advertising or publicity
+ * pertaining to distribution of the software without specific,
+ * written prior permission. The copyright holders make no
+ * representations about the suitability of this software for any
+ * purpose.  It is provided "as is" without express or implied
+ * warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ * SOFTWARE.
+ *
+ * Authors: Carl Worth <cworth@cworth.org>
+ *         Andrea Canciani <ranma42@gmail.com>
+ */
+
+#include "cairo-missing.h"
+
+#ifndef HAVE_STRNDUP
+#include "cairo-malloc-private.h"
+
+char *
+strndup (const char *s,
+        size_t      n)
+{
+    size_t len;
+    char *sdup;
+
+    if (s == NULL)
+       return NULL;
+
+    len = strlen (s);
+    if (len > n)
+       len = n;
+    sdup = (char *) _cairo_malloc (len + 1);
+    if (sdup != NULL) {
+       memcpy (sdup, s, len);
+       sdup[len] = '\0';
+    }
+
+    return sdup;
+}
+#endif
diff --git a/util/cairo-script/.gitignore b/util/cairo-script/.gitignore
new file mode 100644 (file)
index 0000000..8ecaee3
--- /dev/null
@@ -0,0 +1,3 @@
+csi-replay
+csi-exec
+csi-trace
diff --git a/util/cairo-script/COPYING b/util/cairo-script/COPYING
new file mode 100644 (file)
index 0000000..66ad784
--- /dev/null
@@ -0,0 +1,17 @@
+Cairo is free software.
+
+Every source file in the implementation of cairo is available to be
+redistributed and/or modified under the terms of either the GNU Lesser
+General Public License (LGPL) version 2.1 or the Mozilla Public
+License (MPL) version 1.1.  Some files are available under more
+liberal terms, but we believe that in all cases, each file may be used
+under either the LGPL or the MPL.
+
+See the following files in this directory for the precise terms and
+conditions of either license:
+
+       COPYING-LGPL-2.1
+       COPYING-MPL-1.1
+
+Please see each file in the implementation for copyright and licensing
+information, (in the opening comment of each file).
diff --git a/util/cairo-script/Makefile.am b/util/cairo-script/Makefile.am
new file mode 100644 (file)
index 0000000..c2f55a3
--- /dev/null
@@ -0,0 +1,34 @@
+include $(top_srcdir)/util/cairo-script/Makefile.sources
+
+SUBDIRS = examples
+
+lib_LTLIBRARIES = libcairo-script-interpreter.la
+EXTRA_PROGRAMS = csi-replay csi-exec csi-bind
+
+AM_CPPFLAGS = -I$(top_srcdir)/src -I$(top_builddir)/src
+
+cairoincludedir=$(includedir)/cairo
+cairoinclude_HEADERS = cairo-script-interpreter.h
+libcairo_script_interpreter_la_SOURCES = \
+       $(libcairo_script_interpreter_sources)  \
+       $(libcairo_script_interpreter_headers)  \
+       $(NULL)
+libcairo_script_interpreter_la_CFLAGS = $(CAIRO_CFLAGS)
+libcairo_script_interpreter_la_LDFLAGS = -version-info $(CAIRO_LIBTOOL_VERSION_INFO) -no-undefined $(export_symbols)
+libcairo_script_interpreter_la_LIBADD = $(top_builddir)/src/libcairo.la $(CAIRO_LIBS) -lz
+
+csi_replay_SOURCES = csi-replay.c
+csi_replay_CFLAGS = $(CAIRO_CFLAGS)
+csi_replay_LDADD = libcairo-script-interpreter.la $(top_builddir)/src/libcairo.la $(CAIRO_LIBS)
+
+csi_exec_SOURCES = csi-exec.c
+csi_exec_LDADD = libcairo-script-interpreter.la $(top_builddir)/src/libcairo.la $(CAIRO_LIBS)
+
+if CAIRO_HAS_SCRIPT_SURFACE
+EXTRA_PROGRAMS += csi-trace
+csi_trace_SOURCES = csi-trace.c
+csi_trace_LDADD = libcairo-script-interpreter.la $(top_builddir)/src/libcairo.la $(CAIRO_LIBS)
+endif
+
+EXTRA_DIST = \
+       COPYING
diff --git a/util/cairo-script/Makefile.sources b/util/cairo-script/Makefile.sources
new file mode 100644 (file)
index 0000000..fd73a17
--- /dev/null
@@ -0,0 +1,13 @@
+libcairo_script_interpreter_sources = \
+       cairo-script-file.c \
+       cairo-script-hash.c \
+       cairo-script-interpreter.c \
+       cairo-script-objects.c \
+       cairo-script-operators.c \
+       cairo-script-scanner.c \
+       cairo-script-stack.c \
+       $(NULL)
+
+libcairo_script_interpreter_headers = \
+       cairo-script-private.h \
+       $(NULL)
diff --git a/util/cairo-script/Makefile.win32 b/util/cairo-script/Makefile.win32
new file mode 100644 (file)
index 0000000..0aef981
--- /dev/null
@@ -0,0 +1,10 @@
+top_srcdir = ../../
+include $(top_srcdir)/build/Makefile.win32.common
+include $(top_srcdir)/util/cairo-script/Makefile.sources
+
+all: inform $(CFG)/libcairo-script-interpreter.lib
+
+libcairo_script_interpreter_OBJECTS = $(patsubst %.c, $(CFG)/%-static.obj, $(libcairo_script_interpreter_sources))
+
+$(CFG)/libcairo-script-interpreter.lib: $(libcairo_script_interpreter_OBJECTS)
+       @$(AR) $(CAIRO_ARFLAGS) -OUT:$@ $(libcairo_script_interpreter_OBJECTS)
diff --git a/util/cairo-script/cairo-script-file.c b/util/cairo-script/cairo-script-file.c
new file mode 100644 (file)
index 0000000..34f2964
--- /dev/null
@@ -0,0 +1,1084 @@
+/*
+ * Copyright © 2008 Chris Wilson <chris@chris-wilson.co.uk>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Chris Wilson.
+ *
+ * Contributor(s):
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-script-private.h"
+
+#include <stdio.h>
+#include <limits.h> /* INT_MAX */
+#include <string.h>
+#include <zlib.h>
+
+#define CHUNK_SIZE 32768
+
+#define OWN_STREAM 0x1
+
+csi_status_t
+csi_file_new (csi_t *ctx,
+             csi_object_t *obj,
+             const char *path, const char *mode)
+{
+    csi_file_t *file;
+
+    file = _csi_slab_alloc (ctx, sizeof (csi_file_t));
+    if (file == NULL)
+       return _csi_error (CAIRO_STATUS_NO_MEMORY);
+
+    file->base.type = CSI_OBJECT_TYPE_FILE;
+    file->base.ref = 1;
+
+    file->data = NULL;
+    file->type = STDIO;
+    file->flags = OWN_STREAM;
+    file->src = fopen (path, mode);
+    if (file->src == NULL) {
+       _csi_slab_free (ctx, file, sizeof (csi_file_t));
+       return _csi_error (CAIRO_STATUS_FILE_NOT_FOUND);
+    }
+
+    file->data = _csi_alloc (ctx, CHUNK_SIZE);
+    if (file->data == NULL) {
+       _csi_slab_free (ctx, file, sizeof (csi_file_t));
+       return _csi_error (CAIRO_STATUS_NO_MEMORY);
+    }
+    file->bp = file->data;
+    file->rem = 0;
+
+    obj->type = CSI_OBJECT_TYPE_FILE;
+    obj->datum.file = file;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+csi_status_t
+csi_file_new_for_stream (csi_t *ctx,
+                        csi_object_t *obj,
+                        FILE *stream)
+{
+    csi_file_t *file;
+
+    file = _csi_slab_alloc (ctx, sizeof (csi_file_t));
+    if (file == NULL)
+       return _csi_error (CAIRO_STATUS_NO_MEMORY);
+
+    file->base.type = CSI_OBJECT_TYPE_FILE;
+    file->base.ref = 1;
+
+    file->data = NULL;
+    file->type = STDIO;
+    file->flags = 0;
+    file->src = stream;
+    if (file->src == NULL) {
+       _csi_slab_free (ctx, file, sizeof (csi_file_t));
+       return _csi_error (CAIRO_STATUS_FILE_NOT_FOUND);
+    }
+
+    file->data = _csi_alloc (ctx, CHUNK_SIZE);
+    if (file->data == NULL) {
+       _csi_slab_free (ctx, file, sizeof (csi_file_t));
+       return _csi_error (CAIRO_STATUS_NO_MEMORY);
+    }
+    file->bp = file->data;
+    file->rem = 0;
+
+    obj->type = CSI_OBJECT_TYPE_FILE;
+    obj->datum.file = file;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+csi_status_t
+csi_file_new_for_bytes (csi_t *ctx,
+                       csi_object_t *obj,
+                       const char *bytes,
+                       unsigned int length)
+{
+    csi_file_t *file;
+
+    file = _csi_slab_alloc (ctx, sizeof (csi_file_t));
+    if (file == NULL)
+       return _csi_error (CAIRO_STATUS_NO_MEMORY);
+
+    file->base.type = CSI_OBJECT_TYPE_FILE;
+    file->base.ref = 1;
+
+    file->type = BYTES;
+    file->src  = (uint8_t *) bytes;
+    file->data = (uint8_t *) bytes;
+    file->bp   = (uint8_t *) bytes;
+    file->rem  = length;
+
+    obj->type = CSI_OBJECT_TYPE_FILE;
+    obj->datum.file = file;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+csi_status_t
+csi_file_new_from_string (csi_t *ctx,
+                         csi_object_t *obj,
+                         csi_string_t *src)
+{
+    csi_file_t *file;
+
+    file = _csi_slab_alloc (ctx, sizeof (csi_file_t));
+    if (_csi_unlikely (file == NULL))
+       return _csi_error (CAIRO_STATUS_NO_MEMORY);
+
+    file->base.type = CSI_OBJECT_TYPE_FILE;
+    file->base.ref = 1;
+
+    if (src->deflate) {
+       uLongf len = src->deflate;
+       csi_object_t tmp_obj;
+       csi_string_t *tmp_str;
+       csi_status_t status;
+
+       status = csi_string_new (ctx, &tmp_obj,  NULL, src->deflate);
+       if (_csi_unlikely (status))
+           return status;
+
+       tmp_str = tmp_obj.datum.string;
+       if (uncompress ((Bytef *) tmp_str->string, &len,
+                       (Bytef *) src->string, src->len) != Z_OK)
+       {
+           csi_string_free (ctx, tmp_str);
+           _csi_slab_free (ctx, file, sizeof (csi_file_t));
+           return _csi_error (CAIRO_STATUS_NO_MEMORY);
+       }
+
+       file->src  = tmp_str;
+       file->data = tmp_str->string;
+       file->rem  = tmp_str->len;
+    } else {
+       file->src  = src; src->base.ref++;
+       file->data = src->string;
+       file->rem  = src->len;
+    }
+    file->type = BYTES;
+    file->bp   = file->data;
+
+    obj->type = CSI_OBJECT_TYPE_FILE;
+    obj->datum.file = file;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static csi_status_t
+_csi_file_new_filter (csi_t *ctx,
+                     csi_object_t *obj,
+                     csi_object_t *src,
+                     const csi_filter_funcs_t *funcs,
+                     void *data)
+{
+    csi_file_t *file;
+    csi_object_t src_file;
+    csi_status_t status;
+
+    file = _csi_slab_alloc (ctx, sizeof (csi_file_t));
+    if (file == NULL)
+       return _csi_error (CAIRO_STATUS_NO_MEMORY);
+
+    obj->type = CSI_OBJECT_TYPE_FILE;
+    obj->datum.file = file;
+
+    file->base.type = CSI_OBJECT_TYPE_FILE;
+    file->base.ref = 1;
+
+    file->type = FILTER;
+    file->data = data;
+    file->filter = funcs;
+    status = csi_object_as_file (ctx, src, &src_file);
+    if (status) {
+       csi_object_free (ctx, obj);
+       return status;
+    }
+    file->src = src_file.datum.file;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+
+#if 0
+csi_status_t
+csi_file_new_from_stream (csi_t *ctx,
+                         FILE *file,
+                         csi_object_t **out)
+{
+    csi_file_t *obj;
+
+    obj = (csi_file_t *) _csi_object_new (ctx, CSI_OBJECT_TYPE_FILE);
+    if (obj == NULL)
+       return _csi_error (CAIRO_STATUS_NO_MEMORY);
+
+    obj->type = STDIO;
+    obj->src = file;
+    obj->data = _csi_alloc (ctx, CHUNK_SIZE);
+    if (obj->data == NULL) {
+       csi_object_free (&obj->base);
+       return _csi_error (CAIRO_STATUS_UNDEFINED_FILENAME_ERROR);
+    }
+    obj->bp = obj->data;
+    obj->rem = 0;
+
+    *out = &obj->base;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static csi_object_t *
+_csi_file_new_from_procedure (csi_t *ctx, csi_object_t *src)
+{
+    csi_file_t *obj;
+
+    obj = (csi_file_t *) _csi_object_new (ctx, CSI_OBJECT_TYPE_FILE);
+    if (obj == NULL)
+       return NULL;
+
+    obj->type = PROCEDURE;
+    obj->src = csi_object_reference (src);
+    obj->data = NULL;
+
+    return &obj->base;
+}
+#endif
+
+typedef struct _ascii85_decode_data {
+    uint8_t buf[CHUNK_SIZE];
+    uint8_t *bp;
+    short bytes_available;
+    short eod;
+} _ascii85_decode_data_t;
+
+static int
+_getc_skip_whitespace (csi_file_t *src)
+{
+    int c;
+
+    do switch ((c = csi_file_getc (src))) {
+    case 0x0:
+    case 0x9:
+    case 0xa:
+    case 0xc:
+    case 0xd:
+    case 0x20:
+       continue;
+
+    default:
+       return c;
+    } while (TRUE);
+
+    return c;
+}
+
+static void
+_ascii85_decode (csi_file_t *file)
+{
+    _ascii85_decode_data_t *data = file->data;
+    unsigned int n;
+
+    if (data->eod)
+       return;
+
+    data->bp = data->buf;
+
+    n = 0;
+    do {
+       unsigned int v = _getc_skip_whitespace (file->src);
+       if (v == 'z') {
+           data->buf[n+0] = 0;
+           data->buf[n+1] = 0;
+           data->buf[n+2] = 0;
+           data->buf[n+3] = 0;
+       } else if (v == '~') {
+           _getc_skip_whitespace (file->src); /* == '>' || IO_ERROR */
+           data->eod = TRUE;
+           break;
+       } else if (v < '!' || v > 'u') {
+           /* IO_ERROR */
+           data->eod = TRUE;
+           break;
+       } else {
+           unsigned int i;
+
+           v -= '!';
+           for (i = 1; i < 5; i++) {
+               int c = _getc_skip_whitespace (file->src);
+               if (c == '~') { /* short tuple */
+                   _getc_skip_whitespace (file->src); /* == '>' || IO_ERROR */
+                   data->eod = TRUE;
+                   switch (i) {
+                   case 0:
+                   case 1:
+                       /* IO_ERROR */
+                       break;
+                   case 2:
+                       v = v * (85*85*85) + 85*85*85 -1;
+                       goto odd1;
+                   case 3:
+                       v = v * (85*85) + 85*85 -1;
+                       goto odd2;
+                   case 4:
+                       v = v * 85 + 84;
+                       data->buf[n+2] = v >> 8 & 0xff;
+odd2:
+                       data->buf[n+1] = v >> 16 & 0xff;
+odd1:
+                       data->buf[n+0] = v >> 24 & 0xff;
+                       data->bytes_available = n + i - 1;
+                       return;
+                   }
+                   break;
+               }
+               v = 85*v + c-'!';
+           }
+
+           data->buf[n+0] = v >> 24 & 0xff;
+           data->buf[n+1] = v >> 16 & 0xff;
+           data->buf[n+2] = v >> 8 & 0xff;
+           data->buf[n+3] = v >> 0 & 0xff;
+       }
+       n += 4;
+    } while (n < sizeof (data->buf) && data->eod == FALSE);
+
+    data->bytes_available = n;
+}
+
+static int
+_ascii85_decode_getc (csi_file_t *file)
+{
+    _ascii85_decode_data_t *data = file->data;
+
+    if (data->bytes_available == 0) {
+       _ascii85_decode (file);
+
+       if (data->bytes_available == 0)
+           return EOF;
+    }
+
+    data->bytes_available--;
+    return *data->bp++;
+}
+
+static void
+_ascii85_decode_putc (csi_file_t *file, int c)
+{
+    _ascii85_decode_data_t *data = file->data;
+    data->bytes_available++;
+    data->bp--;
+}
+
+static int
+_ascii85_decode_read (csi_file_t *file, uint8_t *buf, int len)
+{
+    _ascii85_decode_data_t *data = file->data;
+
+    if (data->bytes_available == 0) {
+       _ascii85_decode (file);
+
+       if (data->bytes_available == 0)
+           return 0;
+    }
+
+    if (len > data->bytes_available)
+       len = data->bytes_available;
+    memcpy (buf, data->bp, len);
+    data->bp += len;
+    data->bytes_available -= len;
+    return len;
+}
+
+csi_status_t
+csi_file_new_ascii85_decode (csi_t *ctx,
+                            csi_object_t *obj,
+                            csi_dictionary_t *dict,
+                            csi_object_t *src)
+{
+    static const csi_filter_funcs_t funcs = {
+       _ascii85_decode_getc,
+       _ascii85_decode_putc,
+       _ascii85_decode_read,
+       _csi_free,
+    };
+    _ascii85_decode_data_t *data;
+
+    data = _csi_alloc0 (ctx, sizeof (_ascii85_decode_data_t));
+    if (data == NULL)
+       return _csi_error (CAIRO_STATUS_NO_MEMORY);
+
+    return _csi_file_new_filter (ctx, obj, src, &funcs, data);
+}
+
+#if HAVE_ZLIB
+#include <zlib.h>
+
+typedef struct _deflate_decode_data {
+    z_stream zlib_stream;
+
+    uint8_t in[CHUNK_SIZE];
+    uint8_t out[CHUNK_SIZE];
+
+    int bytes_available;
+    uint8_t *bp;
+} _deflate_decode_data_t;
+
+static void
+_deflate_decode (csi_file_t *file)
+{
+    _deflate_decode_data_t *data = file->data;
+    uint8_t *bp;
+    int len;
+
+    data->zlib_stream.next_out = data->out;
+    data->zlib_stream.avail_out = sizeof (data->out);
+
+    bp = data->in;
+    len = sizeof (data->in);
+    if (data->zlib_stream.avail_in) {
+       memmove (data->in,
+                data->zlib_stream.next_in,
+                data->zlib_stream.avail_in);
+       len -= data->zlib_stream.avail_in;
+       bp += data->zlib_stream.avail_in;
+    }
+
+    len = csi_file_read (file->src, bp, len);
+
+    data->zlib_stream.next_in = data->in;
+    data->zlib_stream.avail_in += len;
+
+    inflate (&data->zlib_stream, len == 0 ? Z_FINISH : Z_NO_FLUSH);
+
+    data->bytes_available = data->zlib_stream.next_out - data->out;
+    data->bp = data->out;
+}
+
+static int
+_deflate_decode_getc (csi_file_t *file)
+{
+    _deflate_decode_data_t *data = file->data;
+
+    if (data->bytes_available == 0) {
+       _deflate_decode (file);
+
+       if (data->bytes_available == 0)
+           return EOF;
+    }
+
+    data->bytes_available--;
+    return *data->bp++;
+}
+
+static void
+_deflate_decode_putc (csi_file_t *file, int c)
+{
+    _deflate_decode_data_t *data = file->data;
+    data->bytes_available++;
+    data->bp--;
+}
+
+static int
+_deflate_decode_read (csi_file_t *file, uint8_t *buf, int len)
+{
+    _deflate_decode_data_t *data = file->data;
+
+    if (data->bytes_available == 0) {
+       _deflate_decode (file);
+
+       if (data->bytes_available == 0)
+           return 0;
+    }
+
+    if (len > (int) data->bytes_available)
+       len = data->bytes_available;
+    memcpy (buf, data->bp, len);
+    data->bp += len;
+    data->bytes_available -= len;
+    return len;
+}
+
+static void
+_deflate_destroy (csi_t *ctx, void *closure)
+{
+    _deflate_decode_data_t *data;
+
+    data = closure;
+
+    inflateEnd (&data->zlib_stream);
+
+    _csi_free (ctx, data);
+}
+
+csi_status_t
+csi_file_new_deflate_decode (csi_t *ctx,
+                            csi_object_t *obj,
+                            csi_dictionary_t *dict,
+                            csi_object_t *src)
+{
+    static const csi_filter_funcs_t funcs = {
+       _deflate_decode_getc,
+       _deflate_decode_putc,
+       _deflate_decode_read,
+       _deflate_destroy,
+    };
+    _deflate_decode_data_t *data;
+
+    data = _csi_alloc (ctx, sizeof (_deflate_decode_data_t));
+    if (data == NULL)
+       return _csi_error (CAIRO_STATUS_NO_MEMORY);
+
+    data->zlib_stream.zalloc = Z_NULL;
+    data->zlib_stream.zfree = Z_NULL;
+    data->zlib_stream.opaque = Z_NULL;
+    data->zlib_stream.next_in = data->in;
+    data->zlib_stream.avail_in = 0;
+    data->zlib_stream.next_out = data->out;
+    data->zlib_stream.avail_out = sizeof (data->out);
+    data->bytes_available = 0;
+
+    if (inflateInit (&data->zlib_stream) != Z_OK) {
+       _csi_free (ctx, data);
+       return _csi_error (CAIRO_STATUS_NO_MEMORY);
+    }
+
+    return _csi_file_new_filter (ctx, obj, src, &funcs, data);
+}
+#endif
+
+#if 0
+static int
+hex_value (int c)
+{
+    if (c < '0')
+       return EOF;
+    if (c <= '9')
+       return c - '0';
+    c |= 32;
+    if (c < 'a')
+       return EOF;
+    if (c <= 'f')
+       return c - 'a' + 0xa;
+    return EOF;
+}
+
+/* Adobe Type 1 Font Format book: p63 */
+typedef struct _decrypt_data {
+    uint8_t putback[32];
+    uint8_t nputback;
+    csi_bool_t is_hexadecimal;
+    unsigned short R;
+    int eod;
+} _decrypt_data_t;
+
+static uint8_t
+_decrypt (unsigned short *R, uint8_t cypher)
+{
+#define c1 52845
+#define c2 22719
+    uint8_t plain;
+
+    plain = cypher ^ (*R >> 8);
+    *R = (cypher + *R) * c1 + c2;
+    return plain;
+#undef c1
+#undef c2
+}
+
+int
+csi_decrypt (uint8_t *in, int length,
+            unsigned short salt, int binary,
+            uint8_t *out)
+{
+    const uint8_t * const end = in + length;
+    uint8_t *base = out;
+
+    while (in < end) {
+       int c;
+
+       if (! binary) {
+           int c_hi = -1, c_lo = 0;
+
+           while (in < end && (c_hi = *in++)) {
+               switch (c_hi) {
+               case 0x0:
+               case 0x9:
+               case 0xa:
+               case 0xc:
+               case 0xd:
+               case 0x20:
+                   continue;
+
+               default:
+                   break;
+               }
+           }
+           if (c_hi < 0)
+               break;
+
+           while (in < end && (c_lo = *in++)) {
+               switch (c_lo) {
+               case 0x0:
+               case 0x9:
+               case 0xa:
+               case 0xc:
+               case 0xd:
+               case 0x20:
+                   continue;
+
+               default:
+                   break;
+               }
+           }
+
+           c = (hex_value (c_hi) << 4) | hex_value (c_lo);
+       } else
+           c = *in++;
+
+       *out++ = _decrypt (&salt, c);
+    }
+
+    return out - base;
+}
+
+static uint8_t
+_encrypt (unsigned short *R, uint8_t plain)
+{
+#define c1 52845
+#define c2 22719
+    uint8_t cypher;
+
+    cypher = plain ^ (*R >> 8);
+    *R = (cypher + *R) * c1 + c2;
+    return cypher;
+#undef c1
+#undef c2
+}
+
+int
+csi_encrypt (uint8_t *in, int length,
+            unsigned short salt, int discard, int binary,
+            uint8_t *out)
+{
+    const char hex[]="0123456789abcdef";
+    const uint8_t * const end = in + length;
+    uint8_t *base = out;
+    int col = 0;
+
+    while (discard--) {
+       if (! binary) {
+           int c = _encrypt (&salt, ' ');
+           *out++ = hex[(c >> 4) & 0xf];
+           *out++ = hex[(c >> 0) & 0xf];
+       } else
+           *out++ = _encrypt (&salt, 0);
+    }
+
+    while (in < end) {
+       int c;
+
+       c = _encrypt (&salt, *in++);
+       if (! binary) {
+           if (col == 78) {
+               *out++ = '\n';
+               col = 0;
+           }
+           *out++ = hex[(c >> 4) & 0xf];
+           *out++ = hex[(c >> 0) & 0xf];
+           col += 2;
+       } else
+           *out++ = c;
+    }
+
+    return out - base;
+}
+
+static int
+_decrypt_getc (csi_file_t *file)
+{
+    _decrypt_data_t *data = file->data;
+    int c;
+
+    if (data->nputback)
+       return data->putback[--data->nputback];
+
+    if (data->is_hexadecimal) {
+       int c_hi, c_lo;
+
+       c_hi = _getc_skip_whitespace (file->src);
+       c_lo = _getc_skip_whitespace (file->src);
+       c = (hex_value (c_hi) << 4) | hex_value (c_lo);
+    } else
+       c = csi_file_getc (file->src);
+
+    if (c == EOF)
+       return EOF;
+
+    return _decrypt (&data->R, c);
+}
+
+static void
+_decrypt_putc (csi_file_t *file, int c)
+{
+    _decrypt_data_t *data;
+
+    data = file->data;
+
+    data->putback[data->nputback++] = c;
+}
+
+csi_object_t *
+csi_file_new_decrypt (csi_t *ctx, csi_object_t *src, int salt, int discard)
+{
+    csi_object_t *obj;
+    _decrypt_data_t *data;
+    int n;
+
+    data = _csi_alloc0 (ctx, sizeof (_decrypt_data_t));
+    if (data == NULL)
+       return NULL;
+
+    data->R = salt;
+
+    obj = _csi_file_new_filter (ctx, src,
+                               _decrypt_getc,
+                               _decrypt_putc,
+                               NULL,
+                               _csi_free,
+                               data);
+    if (obj == NULL)
+       return NULL;
+
+    /* XXX determine encoding, eexec only? */
+    data->is_hexadecimal = salt != 4330;
+    for (n = 0; n < discard; n++) {
+       int c;
+       c = csi_file_getc (obj);
+       if (c == EOF) {
+           return obj;
+       }
+    }
+    return obj;
+}
+#endif
+
+csi_status_t
+_csi_file_execute (csi_t *ctx, csi_file_t *obj)
+{
+    return _csi_scan_file (ctx, obj);
+}
+
+int
+csi_file_getc (csi_file_t *file)
+{
+    int c;
+
+    if (_csi_unlikely (file->src == NULL))
+       return EOF;
+
+    switch (file->type) {
+    case STDIO:
+       if (_csi_likely (file->rem)) {
+           c = *file->bp++;
+           file->rem--;
+       } else {
+           file->rem = fread (file->bp = file->data, 1, CHUNK_SIZE, file->src);
+    case BYTES:
+           if (_csi_likely (file->rem)) {
+               c = *file->bp++;
+               file->rem--;
+           } else
+               c = EOF;
+       }
+       break;
+
+    case PROCEDURE:
+#if 0
+       if (file->data == NULL) {
+           csi_status_t status;
+           csi_object_t *string;
+
+RERUN_PROCEDURE:
+           status = csi_object_execute (file->src);
+           if (status)
+               return EOF;
+
+           string = csi_pop_operand (file->base.ctx);
+           if (string == NULL)
+               return EOF;
+           file->data = csi_object_as_file (file->base.ctx, string);
+           csi_object_free (string);
+           if (file->data == NULL)
+               return EOF;
+       }
+       c = csi_file_getc (file->data);
+       if (c == EOF) {
+           csi_object_free (file->data);
+           file->data = NULL;
+           goto RERUN_PROCEDURE;
+       }
+#else
+       c = EOF;
+#endif
+       break;
+
+    case FILTER:
+       c = file->filter->filter_getc (file);
+       break;
+
+    default:
+       c = EOF;
+       break;
+    }
+
+    return c;
+}
+
+int
+csi_file_read (csi_file_t *file, void *buf, int len)
+{
+    int ret;
+
+    if (file->src == NULL)
+       return 0;
+
+    switch (file->type) {
+    case STDIO:
+       if (file->rem > 0) {
+           ret = len;
+           if (file->rem < ret)
+               ret = file->rem;
+           memcpy (buf, file->bp, ret);
+           file->bp  += ret;
+           file->rem -= ret;
+       } else
+           ret = fread (buf, 1, len, file->src);
+       break;
+
+    case BYTES:
+       if (file->rem > 0) {
+           ret = len;
+           if (file->rem < ret)
+               ret = file->rem;
+           memcpy (buf, file->bp, ret);
+           file->bp  += ret;
+           file->rem -= ret;
+       } else
+           ret = 0;
+       break;
+
+    case PROCEDURE:
+#if 0
+       if (file->data == NULL) {
+           csi_status_t status;
+           csi_object_t *string;
+
+RERUN_PROCEDURE:
+           status = csi_object_execute (file->src);
+           if (status)
+               return 0;
+
+           string = csi_pop_operand (file->base.ctx);
+           if (string == NULL)
+               return 0;
+           file->data = csi_object_as_file (file->base.ctx, string);
+           csi_object_free (string);
+           if (file->data == NULL)
+               return 0;
+       }
+       ret = csi_file_read (file->data, buf, len);
+       if (ret == 0) {
+           csi_object_free (file->data);
+           file->data = NULL;
+           goto RERUN_PROCEDURE;
+       }
+#else
+       ret = 0;
+#endif
+       break;
+
+    case FILTER:
+       ret = file->filter->filter_read (file, buf, len);
+       break;
+
+    default:
+       ret = 0;
+       break;
+    }
+
+    return ret;
+}
+
+void
+csi_file_putc (csi_file_t *file, int c)
+{
+    if (file->src == NULL)
+       return;
+
+    switch ((int) file->type) {
+    case STDIO:
+    case BYTES:
+       file->bp--;
+       file->rem++;
+       break;
+    case FILTER:
+       file->filter->filter_putc (file, c);
+       break;
+    default:
+       break;
+    }
+}
+
+void
+csi_file_flush (csi_file_t *file)
+{
+    if (file->src == NULL)
+       return;
+
+    switch ((int) file->type) {
+    case FILTER: /* need to eat EOD */
+       while (csi_file_getc (file) != EOF)
+           ;
+       break;
+    default:
+       break;
+    }
+}
+
+void
+csi_file_close (csi_t *ctx, csi_file_t *file)
+{
+    if (file->src == NULL)
+       return;
+
+    switch (file->type) {
+    case STDIO:
+       if (file->flags & OWN_STREAM)
+           fclose (file->src);
+       break;
+    case BYTES:
+       if (file->src != file->data) {
+           csi_string_t *src = file->src;
+           if (src != NULL && --src->base.ref == 0)
+               csi_string_free (ctx, src);
+       }
+       break;
+    case FILTER:
+       {
+           csi_file_t *src = file->src;
+           if (src != NULL && --src->base.ref == 0)
+               _csi_file_free (ctx, src);
+       }
+       break;
+    case PROCEDURE:
+    default:
+       break;
+    }
+    file->src = NULL;
+}
+
+void
+_csi_file_free (csi_t *ctx, csi_file_t *file)
+{
+    csi_file_flush (file);
+    /* XXX putback */
+    csi_file_close (ctx, file);
+
+    switch (file->type) {
+    case BYTES:
+       break;
+    case PROCEDURE:
+#if 0
+       csi_object_free (ctx, file->data);
+#endif
+       break;
+    case STDIO:
+       _csi_free (ctx, file->data);
+       break;
+    case FILTER:
+       file->filter->filter_destroy (ctx, file->data);
+       break;
+    default:
+       break;
+    }
+
+    _csi_slab_free (ctx, file, sizeof (csi_file_t));
+}
+
+csi_status_t
+_csi_file_as_string (csi_t *ctx,
+                    csi_file_t *file,
+                    csi_object_t *obj)
+{
+    char *bytes;
+    unsigned int len;
+    unsigned int allocated;
+    csi_status_t status;
+
+    len = 0;
+    allocated = 16384;
+    bytes = _csi_alloc (ctx, allocated);
+    if (bytes == NULL)
+       return _csi_error (CAIRO_STATUS_NO_MEMORY);
+
+    len = 0;
+    do {
+       int ret;
+
+       ret = csi_file_read (file, bytes + len, allocated - len);
+       if (ret == 0)
+           break;
+
+       len += ret;
+       if (len + 1 > allocated / 2) {
+           char *newbytes;
+           int newlen;
+
+           if (_csi_unlikely (allocated > INT_MAX / 2))
+               return _csi_error (CAIRO_STATUS_NO_MEMORY);
+
+           newlen = allocated * 2;
+           newbytes = _csi_realloc (ctx, bytes, newlen);
+           if (_csi_unlikely (newbytes == NULL)) {
+               _csi_free (ctx, bytes);
+               return _csi_error (CAIRO_STATUS_NO_MEMORY);
+           }
+           bytes = newbytes;
+           allocated = newlen;
+       }
+    } while (TRUE);
+
+    bytes[len] = '\0'; /* better safe than sorry! */
+    status = csi_string_new_from_bytes (ctx, obj, bytes, len);
+    if (status) {
+       _csi_free (ctx, bytes);
+       return status;
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
diff --git a/util/cairo-script/cairo-script-hash.c b/util/cairo-script/cairo-script-hash.c
new file mode 100644 (file)
index 0000000..4d11103
--- /dev/null
@@ -0,0 +1,483 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2004 Red Hat, Inc.
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ *      Keith Packard <keithp@keithp.com>
+ *     Graydon Hoare <graydon@redhat.com>
+ *     Carl Worth <cworth@cworth.org>
+ *     Karl Tomlinson <karlt+@karlt.net>, Mozilla Corporation
+ */
+
+#include "cairo-script-private.h"
+
+#include <stdlib.h>
+
+/*
+ * An entry can be in one of three states:
+ *
+ * FREE: Entry has never been used, terminates all searches.
+ *       Appears in the table as a %NULL pointer.
+ *
+ * DEAD: Entry had been live in the past. A dead entry can be reused
+ *       but does not terminate a search for an exact entry.
+ *       Appears in the table as a pointer to DEAD_ENTRY.
+ *
+ * LIVE: Entry is currently being used.
+ *       Appears in the table as any non-%NULL, non-DEAD_ENTRY pointer.
+ */
+
+#define DEAD_ENTRY ((csi_hash_entry_t *) 0x1)
+
+#define ENTRY_IS_FREE(entry) ((entry) == NULL)
+#define ENTRY_IS_DEAD(entry) ((entry) == DEAD_ENTRY)
+#define ENTRY_IS_LIVE(entry) ((entry) >  DEAD_ENTRY)
+
+
+/* This table is open-addressed with double hashing. Each table size is a
+ * prime chosen to be a little more than double the high water mark for a
+ * given arrangement, so the tables should remain < 50% full. The table
+ * size makes for the "first" hash modulus; a second prime (2 less than the
+ * first prime) serves as the "second" hash modulus, which is co-prime and
+ * thus guarantees a complete permutation of table indices.
+ *
+ * This structure, and accompanying table, is borrowed/modified from the
+ * file xserver/render/glyph.c in the freedesktop.org x server, with
+ * permission (and suggested modification of doubling sizes) by Keith
+ * Packard.
+ */
+
+static const csi_hash_table_arrangement_t hash_table_arrangements [] = {
+    { 16,              43,             41              },
+    { 32,              73,             71              },
+    { 64,              151,            149             },
+    { 128,             283,            281             },
+    { 256,             571,            569             },
+    { 512,             1153,           1151            },
+    { 1024,            2269,           2267            },
+    { 2048,            4519,           4517            },
+    { 4096,            9013,           9011            },
+    { 8192,            18043,          18041           },
+    { 16384,           36109,          36107           },
+    { 32768,           72091,          72089           },
+    { 65536,           144409,         144407          },
+    { 131072,          288361,         288359          },
+    { 262144,          576883,         576881          },
+    { 524288,          1153459,        1153457         },
+    { 1048576,         2307163,        2307161         },
+    { 2097152,         4613893,        4613891         },
+    { 4194304,         9227641,        9227639         },
+    { 8388608,         18455029,       18455027        },
+    { 16777216,                36911011,       36911009        },
+    { 33554432,                73819861,       73819859        },
+    { 67108864,                147639589,      147639587       },
+    { 134217728,       295279081,      295279079       },
+    { 268435456,       590559793,      590559791       }
+};
+
+#define NUM_HASH_TABLE_ARRANGEMENTS ARRAY_LENGTH (hash_table_arrangements)
+
+/**
+ * _csi_hash_table_create:
+ * @keys_equal: a function to return %TRUE if two keys are equal
+ *
+ * Creates a new hash table which will use the keys_equal() function
+ * to compare hash keys. Data is provided to the hash table in the
+ * form of user-derived versions of #csi_hash_entry_t. A hash entry
+ * must be able to hold both a key (including a hash code) and a
+ * value. Sometimes only the key will be necessary, (as in
+ * _csi_hash_table_remove), and other times both a key and a value
+ * will be necessary, (as in _csi_hash_table_insert).
+ *
+ * See #csi_hash_entry_t for more details.
+ *
+ * Return value: the new hash table or %NULL if out of memory.
+ **/
+csi_status_t
+_csi_hash_table_init (csi_hash_table_t *hash_table,
+                     csi_hash_keys_equal_func_t keys_equal)
+{
+    hash_table->keys_equal = keys_equal;
+
+    hash_table->arrangement = &hash_table_arrangements[0];
+
+    hash_table->entries = calloc (hash_table->arrangement->size,
+                                 sizeof(csi_hash_entry_t *));
+    if (hash_table->entries == NULL)
+       return _csi_error (CAIRO_STATUS_NO_MEMORY);
+
+    hash_table->live_entries = 0;
+    hash_table->used_entries = 0;
+    hash_table->iterating = 0;
+
+    return CSI_STATUS_SUCCESS;
+}
+
+/**
+ * _csi_hash_table_destroy:
+ * @hash_table: an empty hash table to destroy
+ *
+ * Immediately destroys the given hash table, freeing all resources
+ * associated with it.
+ *
+ * WARNING: The hash_table must have no live entries in it before
+ * _csi_hash_table_destroy is called. It is a fatal error otherwise,
+ * and this function will halt. The rationale for this behavior is to
+ * avoid memory leaks and to avoid needless complication of the API
+ * with destroy notifiy callbacks.
+ *
+ * WARNING: The hash_table must have no running iterators in it when
+ * _csi_hash_table_destroy is called. It is a fatal error otherwise,
+ * and this function will halt.
+ **/
+void
+_csi_hash_table_fini (csi_hash_table_t *hash_table)
+{
+    free (hash_table->entries);
+}
+
+static csi_hash_entry_t **
+_csi_hash_table_lookup_unique_key (csi_hash_table_t *hash_table,
+                                    csi_hash_entry_t *key)
+{
+    unsigned long table_size, i, idx, step;
+    csi_hash_entry_t **entry;
+
+    table_size = hash_table->arrangement->size;
+    idx = key->hash % table_size;
+
+    entry = &hash_table->entries[idx];
+    if (! ENTRY_IS_LIVE (*entry))
+       return entry;
+
+    i = 1;
+    step = key->hash % hash_table->arrangement->rehash;
+    if (step == 0)
+       step = 1;
+    do {
+       idx += step;
+       if (idx >= table_size)
+           idx -= table_size;
+
+       entry = &hash_table->entries[idx];
+       if (! ENTRY_IS_LIVE (*entry))
+           return entry;
+    } while (++i < table_size);
+
+    return NULL;
+}
+
+/**
+ * _csi_hash_table_manage:
+ * @hash_table: a hash table
+ *
+ * Resize the hash table if the number of entries has gotten much
+ * bigger or smaller than the ideal number of entries for the current
+ * size, or control the number of dead entries by moving the entries
+ * within the table.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS if successful or
+ * %CAIRO_STATUS_NO_MEMORY if out of memory.
+ **/
+static csi_status_t
+_csi_hash_table_manage (csi_hash_table_t *hash_table)
+{
+    csi_hash_table_t tmp;
+    csi_boolean_t realloc = TRUE;
+    unsigned long i;
+
+    /* This keeps the size of the hash table between 2 and approximately 8
+     * times the number of live entries and keeps the proportion of free
+     * entries (search-terminations) > 25%.
+     */
+    unsigned long high = hash_table->arrangement->high_water_mark;
+    unsigned long low = high >> 2;
+    unsigned long max_used = high  + high / 2;
+
+    tmp = *hash_table;
+
+    if (hash_table->live_entries > high) {
+       tmp.arrangement = hash_table->arrangement + 1;
+       /* This code is being abused if we can't make a table big enough. */
+    } else if (hash_table->live_entries < low &&
+              /* Can't shrink if we're at the smallest size */
+              hash_table->arrangement != &hash_table_arrangements[0])
+    {
+       tmp.arrangement = hash_table->arrangement - 1;
+    }
+    else if (hash_table->used_entries > max_used)
+    {
+       /* Clean out dead entries to prevent lookups from becoming too slow. */
+       for (i = 0; i < hash_table->arrangement->size; ++i) {
+           if (ENTRY_IS_DEAD (hash_table->entries[i]))
+               hash_table->entries[i] = NULL;
+       }
+       hash_table->used_entries = hash_table->live_entries;
+
+       /* There is no need to reallocate but some entries may need to be
+        * moved.  Typically the proportion of entries needing to be moved is
+        * small, but, if the moving should leave a large number of dead
+        * entries, they will be cleaned out next time this code is
+        * executed. */
+       realloc = FALSE;
+    }
+    else
+    {
+       return CAIRO_STATUS_SUCCESS;
+    }
+
+    if (realloc) {
+       tmp.entries = calloc (tmp.arrangement->size,
+                             sizeof (csi_hash_entry_t*));
+       if (tmp.entries == NULL)
+           return _csi_error (CAIRO_STATUS_NO_MEMORY);
+
+       hash_table->used_entries = 0;
+    }
+
+    for (i = 0; i < hash_table->arrangement->size; ++i) {
+       csi_hash_entry_t *entry, **pos;
+
+       entry = hash_table->entries[i];
+       if (ENTRY_IS_LIVE (entry)) {
+           hash_table->entries[i] = DEAD_ENTRY;
+
+           pos = _csi_hash_table_lookup_unique_key (&tmp, entry);
+           if (ENTRY_IS_FREE (*pos))
+               hash_table->used_entries++;
+
+           *pos = entry;
+       }
+    }
+
+    if (realloc) {
+       free (hash_table->entries);
+       hash_table->entries = tmp.entries;
+       hash_table->arrangement = tmp.arrangement;
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+/**
+ * _csi_hash_table_lookup:
+ * @hash_table: a hash table
+ * @key: the key of interest
+ *
+ * Performs a lookup in @hash_table looking for an entry which has a
+ * key that matches @key, (as determined by the keys_equal() function
+ * passed to _csi_hash_table_create).
+ *
+ * Return value: the matching entry, of %NULL if no match was found.
+ **/
+void *
+_csi_hash_table_lookup (csi_hash_table_t *hash_table,
+                       csi_hash_entry_t *key)
+{
+    csi_hash_entry_t **entry;
+    unsigned long table_size, i, idx, step;
+
+    table_size = hash_table->arrangement->size;
+    idx = key->hash % table_size;
+    entry = &hash_table->entries[idx];
+
+    if (ENTRY_IS_LIVE (*entry)) {
+       if ((*entry)->hash == key->hash && hash_table->keys_equal (key, *entry))
+           return *entry;
+    } else if (ENTRY_IS_FREE (*entry))
+       return NULL;
+
+    i = 1;
+    step = key->hash % hash_table->arrangement->rehash;
+    if (step == 0)
+       step = 1;
+    do {
+       idx += step;
+       if (idx >= table_size)
+           idx -= table_size;
+
+       entry = &hash_table->entries[idx];
+       if (ENTRY_IS_LIVE (*entry)) {
+           if ((*entry)->hash == key->hash &&
+               hash_table->keys_equal (key, *entry))
+           {
+               return *entry;
+           }
+       } else if (ENTRY_IS_FREE (*entry))
+           return NULL;
+    } while (++i < table_size);
+
+    return NULL;
+}
+
+/**
+ * _csi_hash_table_insert:
+ * @hash_table: a hash table
+ * @key_and_value: an entry to be inserted
+ *
+ * Insert the entry #key_and_value into the hash table.
+ *
+ * WARNING: There must not be an existing entry in the hash table
+ * with a matching key.
+ *
+ * WARNING: It is a fatal error to insert an element while
+ * an iterator is running
+ *
+ * Instead of using insert to replace an entry, consider just editing
+ * the entry obtained with _csi_hash_table_lookup. Or if absolutely
+ * necessary, use _csi_hash_table_remove first.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS if successful or
+ * %CAIRO_STATUS_NO_MEMORY if insufficient memory is available.
+ **/
+csi_status_t
+_csi_hash_table_insert (csi_hash_table_t *hash_table,
+                         csi_hash_entry_t *key_and_value)
+{
+    csi_status_t status;
+    csi_hash_entry_t **entry;
+
+    hash_table->live_entries++;
+    status = _csi_hash_table_manage (hash_table);
+    if (_csi_unlikely (status)) {
+       /* abort the insert... */
+       hash_table->live_entries--;
+       return status;
+    }
+
+    entry = _csi_hash_table_lookup_unique_key (hash_table,
+                                              key_and_value);
+    if (ENTRY_IS_FREE (*entry))
+       hash_table->used_entries++;
+
+    *entry = key_and_value;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static csi_hash_entry_t **
+_csi_hash_table_lookup_exact_key (csi_hash_table_t *hash_table,
+                                   csi_hash_entry_t *key)
+{
+    unsigned long table_size, i, idx, step;
+    csi_hash_entry_t **entry;
+
+    table_size = hash_table->arrangement->size;
+    idx = key->hash % table_size;
+
+    entry = &hash_table->entries[idx];
+    if (*entry == key)
+       return entry;
+
+    i = 1;
+    step = key->hash % hash_table->arrangement->rehash;
+    if (step == 0)
+       step = 1;
+    do {
+       idx += step;
+       if (idx >= table_size)
+           idx -= table_size;
+
+       entry = &hash_table->entries[idx];
+       if (*entry == key)
+           return entry;
+    } while (++i < table_size);
+
+    return NULL;
+}
+/**
+ * _csi_hash_table_remove:
+ * @hash_table: a hash table
+ * @key: key of entry to be removed
+ *
+ * Remove an entry from the hash table which points to @key.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS if successful or
+ * %CAIRO_STATUS_NO_MEMORY if out of memory.
+ **/
+void
+_csi_hash_table_remove (csi_hash_table_t *hash_table,
+                         csi_hash_entry_t *key)
+{
+    *_csi_hash_table_lookup_exact_key (hash_table, key) = DEAD_ENTRY;
+    hash_table->live_entries--;
+
+    /* Check for table resize. Don't do this when iterating as this will
+     * reorder elements of the table and cause the iteration to potentially
+     * skip some elements. */
+    if (hash_table->iterating == 0) {
+       /* This call _can_ fail, but only in failing to allocate new
+        * memory to shrink the hash table. It does leave the table in a
+        * consistent state, and we've already succeeded in removing the
+        * entry, so we don't examine the failure status of this call. */
+       _csi_hash_table_manage (hash_table);
+    }
+}
+
+/**
+ * _csi_hash_table_foreach:
+ * @hash_table: a hash table
+ * @hash_callback: function to be called for each live entry
+ * @closure: additional argument to be passed to @hash_callback
+ *
+ * Call @hash_callback for each live entry in the hash table, in a
+ * non-specified order.
+ *
+ * Entries in @hash_table may be removed by code executed from @hash_callback.
+ *
+ * Entries may not be inserted to @hash_table, nor may @hash_table
+ * be destroyed by code executed from @hash_callback. The relevant
+ * functions will halt in these cases.
+ **/
+void
+_csi_hash_table_foreach (csi_hash_table_t            *hash_table,
+                        csi_hash_callback_func_t  hash_callback,
+                        void                         *closure)
+{
+    unsigned long i;
+    csi_hash_entry_t *entry;
+
+    /* Mark the table for iteration */
+    ++hash_table->iterating;
+    for (i = 0; i < hash_table->arrangement->size; i++) {
+       entry = hash_table->entries[i];
+       if (ENTRY_IS_LIVE(entry))
+           hash_callback (entry, closure);
+    }
+    /* If some elements were deleted during the iteration,
+     * the table may need resizing. Just do this every time
+     * as the check is inexpensive.
+     */
+    if (--hash_table->iterating == 0) {
+       /* Should we fail to shrink the hash table, it is left unaltered,
+        * and we don't need to propagate the error status. */
+       _csi_hash_table_manage (hash_table);
+    }
+}
diff --git a/util/cairo-script/cairo-script-interpreter.c b/util/cairo-script/cairo-script-interpreter.c
new file mode 100644 (file)
index 0000000..bdd5255
--- /dev/null
@@ -0,0 +1,699 @@
+/*
+ * Copyright © 2008 Chris Wilson <chris@chris-wilson.co.uk>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Chris Wilson.
+ *
+ * Contributor(s):
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include <cairo.h>
+
+#include "cairo-script-private.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include <assert.h>
+
+#ifndef MAX
+#define MAX(a,b) (((a)>=(b))?(a):(b))
+#endif
+
+csi_status_t
+_csi_error (csi_status_t status)
+{
+    return status;
+}
+
+/* XXX track global/local memory, cap etc, mark/sweep GC */
+void *
+_csi_alloc (csi_t *ctx, int size)
+{
+    return malloc (size);
+}
+
+void *
+_csi_alloc0 (csi_t *ctx, int size)
+{
+    void *ptr;
+
+    ptr = _csi_alloc (ctx, size);
+    if (_csi_likely (ptr != NULL))
+       memset (ptr, 0, size);
+
+    return ptr;
+}
+
+void *
+_csi_realloc (csi_t *ctx, void *ptr, int size)
+{
+    return realloc (ptr, size);
+}
+
+void
+_csi_free (csi_t *ctx, void *ptr)
+{
+    if (_csi_unlikely (ptr == NULL))
+       return;
+
+    free (ptr);
+}
+
+void *
+_csi_perm_alloc (csi_t *ctx, int size)
+{
+    csi_chunk_t *chunk;
+    void *ptr;
+
+    size = (size + sizeof (void *)-1) & -sizeof (void *);
+
+    chunk = ctx->perm_chunk;
+    if (chunk == NULL || chunk->rem < size) {
+       int chunk_size = (size + 8191) & -8192;
+       chunk = _csi_alloc (ctx, sizeof (csi_chunk_t) + chunk_size);
+       if (_csi_unlikely (chunk == NULL))
+           return NULL;
+
+       chunk->rem = chunk_size;
+       chunk->ptr = (char *) (chunk + 1);
+       chunk->next = ctx->perm_chunk;
+       ctx->perm_chunk = chunk;
+    }
+
+    ptr = chunk->ptr;
+    chunk->ptr += size;
+    chunk->rem -= size;
+
+    return ptr;
+}
+
+void *
+_csi_slab_alloc (csi_t *ctx, int size)
+{
+#if CSI_DEBUG_MALLOC
+    return malloc (size);
+#else
+    int chunk_size;
+    csi_chunk_t *chunk;
+    void *ptr;
+
+    chunk_size = 2 * sizeof (void *);
+    chunk_size = (size + chunk_size - 1) / chunk_size;
+
+    if (ctx->slabs[chunk_size].free_list) {
+       ptr = ctx->slabs[chunk_size].free_list;
+       ctx->slabs[chunk_size].free_list = *(void **) ptr;
+       return ptr;
+    }
+
+    chunk = ctx->slabs[chunk_size].chunk;
+    if (chunk == NULL || ! chunk->rem) {
+       int cnt = MAX (128, 8192 / (chunk_size * 2 * sizeof (void *)));
+
+       chunk = _csi_alloc (ctx,
+                           sizeof (csi_chunk_t) +
+                           cnt * chunk_size * 2 * sizeof (void *));
+       if (_csi_unlikely (chunk == NULL))
+           return NULL;
+
+       chunk->rem = cnt;
+       chunk->ptr = (char *) (chunk + 1);
+       chunk->next = ctx->slabs[chunk_size].chunk;
+       ctx->slabs[chunk_size].chunk = chunk;
+    }
+
+    ptr = chunk->ptr;
+    chunk->ptr += chunk_size * 2 * sizeof (void *);
+    chunk->rem--;
+
+    return ptr;
+#endif
+}
+
+void
+_csi_slab_free (csi_t *ctx, void *ptr, int size)
+{
+    int chunk_size;
+    void **free_list;
+
+    if (_csi_unlikely (ptr == NULL))
+       return;
+
+#if CSI_DEBUG_MALLOC
+    free (ptr);
+#else
+    chunk_size = 2 * sizeof (void *);
+    chunk_size = (size + chunk_size - 1) / chunk_size;
+
+    free_list = ptr;
+    *free_list = ctx->slabs[chunk_size].free_list;
+    ctx->slabs[chunk_size].free_list = ptr;
+#endif
+}
+
+static void
+_csi_perm_fini (csi_t *ctx)
+{
+    while (ctx->perm_chunk != NULL) {
+       csi_chunk_t *chunk = ctx->perm_chunk;
+       ctx->perm_chunk = chunk->next;
+       _csi_free (ctx, chunk);
+    }
+}
+
+static void
+_csi_slab_fini (csi_t *ctx)
+{
+    unsigned int i;
+
+    for (i = 0; i < sizeof (ctx->slabs) / sizeof (ctx->slabs[0]); i++) {
+       while (ctx->slabs[i].chunk != NULL) {
+           csi_chunk_t *chunk = ctx->slabs[i].chunk;
+           ctx->slabs[i].chunk = chunk->next;
+           _csi_free (ctx, chunk);
+       }
+    }
+}
+
+static csi_status_t
+_add_operator (csi_t *ctx,
+              csi_dictionary_t *dict,
+              const csi_operator_def_t *def)
+{
+    csi_object_t name;
+    csi_object_t operator;
+    csi_status_t status;
+
+    status = csi_name_new_static (ctx, &name, def->name);
+    if (status)
+       return status;
+
+    csi_operator_new (&operator, def->op);
+
+    return csi_dictionary_put (ctx, dict, name.datum.name, &operator);
+}
+
+static csi_status_t
+_add_integer_constant (csi_t *ctx,
+                      csi_dictionary_t *dict,
+                      const csi_integer_constant_def_t *def)
+{
+    csi_object_t name;
+    csi_object_t constant;
+    csi_status_t status;
+
+    status = csi_name_new_static (ctx, &name, def->name);
+    if (status)
+       return status;
+
+    csi_integer_new (&constant, def->value);
+
+    return csi_dictionary_put (ctx, dict, name.datum.name, &constant);
+}
+
+static csi_status_t
+_add_real_constant (csi_t *ctx,
+                   csi_dictionary_t *dict,
+                   const csi_real_constant_def_t *def)
+{
+    csi_object_t name;
+    csi_object_t constant;
+    csi_status_t status;
+
+    status = csi_name_new_static (ctx, &name, def->name);
+    if (status)
+       return status;
+
+    csi_real_new (&constant, def->value);
+
+    return csi_dictionary_put (ctx, dict, name.datum.name, &constant);
+}
+
+static csi_status_t
+_init_dictionaries (csi_t *ctx)
+{
+    csi_status_t status;
+    csi_stack_t *stack;
+    csi_object_t obj;
+    csi_dictionary_t *dict, *opcodes;
+    const csi_operator_def_t *odef;
+    const csi_integer_constant_def_t *idef;
+    const csi_real_constant_def_t *rdef;
+    unsigned n;
+
+    stack = &ctx->dstack;
+
+    status = _csi_stack_init (ctx, stack, 4);
+    if (_csi_unlikely (status))
+       return status;
+
+    /* systemdict */
+    status = csi_dictionary_new (ctx, &obj);
+    if (_csi_unlikely (status))
+       return status;
+
+    status = _csi_stack_push (ctx, stack, &obj);
+    if (_csi_unlikely (status))
+       return status;
+
+    dict = obj.datum.dictionary;
+
+    status = csi_dictionary_new (ctx, &obj);
+    if (_csi_unlikely (status))
+       return status;
+
+    opcodes = obj.datum.dictionary;
+
+    n = 0;
+    csi_integer_new (&obj, n);
+    status = csi_dictionary_put (ctx, opcodes, 0, &obj);
+    if (_csi_unlikely (status))
+       return status;
+    ctx->opcode[n++] = NULL;
+
+    /* fill systemdict with operators */
+    for (odef = _csi_operators (); odef->name != NULL; odef++) {
+       status = _add_operator (ctx, dict, odef);
+       if (_csi_unlikely (status))
+           return status;
+
+       if (! csi_dictionary_has (opcodes, (csi_name_t) odef->op)) {
+           csi_integer_new (&obj, n);
+           status = csi_dictionary_put (ctx,
+                                        opcodes, (csi_name_t) odef->op, &obj);
+           if (_csi_unlikely (status))
+               return status;
+
+           assert (n < sizeof (ctx->opcode) / sizeof (ctx->opcode[0]));
+           ctx->opcode[n++] = odef->op;
+       }
+    }
+    csi_dictionary_free (ctx, opcodes);
+
+    /* add constants */
+    for (idef = _csi_integer_constants (); idef->name != NULL; idef++) {
+       status = _add_integer_constant (ctx, dict, idef);
+       if (_csi_unlikely (status))
+           return status;
+    }
+    for (rdef = _csi_real_constants (); rdef->name != NULL; rdef++) {
+       status = _add_real_constant (ctx, dict, rdef);
+       if (_csi_unlikely (status))
+           return status;
+    }
+
+    /* and seal */
+    //dict.type &= ~CSI_OBJECT_ATTR_WRITABLE;
+
+
+    /* globaldict */
+    status = csi_dictionary_new (ctx, &obj);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_stack_push (ctx, stack, &obj);
+    if (_csi_unlikely (status))
+       return status;
+
+    /* userdict */
+    status = csi_dictionary_new (ctx, &obj);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_stack_push (ctx, stack, &obj);
+    if (_csi_unlikely (status))
+       return status;
+
+    return CSI_STATUS_SUCCESS;
+}
+
+/* intern string */
+
+typedef struct _cairo_intern_string {
+    csi_hash_entry_t hash_entry;
+    int len;
+    char *string;
+} csi_intern_string_t;
+
+static unsigned long
+_intern_string_hash (const char *str, int len)
+{
+    const signed char *p = (const signed char *) str;
+    if (len > 0) {
+       unsigned int h = *p;
+
+       while (--len)
+           h = (h << 5) - h + *++p;
+
+       return h;
+    }
+    return 0;
+}
+
+static cairo_bool_t
+_intern_string_equal (const void *_a, const void *_b)
+{
+    const csi_intern_string_t *a = _a;
+    const csi_intern_string_t *b = _b;
+
+    if (a->len != b->len)
+       return FALSE;
+
+    return memcmp (a->string, b->string, a->len) == 0;
+}
+
+static void
+_csi_init (csi_t *ctx)
+{
+    csi_status_t status;
+
+    memset (ctx, 0, sizeof (*ctx));
+
+    ctx->status = CSI_STATUS_SUCCESS;
+    ctx->ref_count = 1;
+    ctx->scanner.line_number = -1;
+
+    status = _csi_hash_table_init (&ctx->strings, _intern_string_equal);
+    if (status)
+       goto FAIL;
+
+    status = _csi_stack_init (ctx, &ctx->ostack, 2048);
+    if (status)
+       goto FAIL;
+    status = _init_dictionaries (ctx);
+    if (status)
+       goto FAIL;
+
+    status = _csi_scanner_init (ctx, &ctx->scanner);
+    if (status)
+       goto FAIL;
+
+    return;
+
+FAIL:
+    if (ctx->status == CSI_STATUS_SUCCESS)
+       ctx->status = status;
+}
+
+static void
+_csi_finish (csi_t *ctx)
+{
+    _csi_stack_fini (ctx, &ctx->ostack);
+    _csi_stack_fini (ctx, &ctx->dstack);
+    _csi_scanner_fini (ctx, &ctx->scanner);
+
+    _csi_hash_table_fini (&ctx->strings);
+}
+
+csi_status_t
+_csi_name_define (csi_t *ctx, csi_name_t name, csi_object_t *obj)
+{
+    return csi_dictionary_put (ctx,
+                       ctx->dstack.objects[ctx->dstack.len-1].datum.dictionary,
+                       name,
+                       obj);
+}
+
+csi_status_t
+_csi_name_lookup (csi_t *ctx, csi_name_t name, csi_object_t *obj)
+{
+    int i;
+
+    for (i = ctx->dstack.len; i--; ) {
+       csi_dictionary_t *dict;
+       csi_dictionary_entry_t *entry;
+
+       dict = ctx->dstack.objects[i].datum.dictionary;
+       entry = _csi_hash_table_lookup (&dict->hash_table,
+                                       (csi_hash_entry_t *) &name);
+       if (entry != NULL) {
+           *obj = entry->value;
+           return CSI_STATUS_SUCCESS;
+       }
+    }
+
+    return _csi_error (CSI_STATUS_INVALID_SCRIPT);
+}
+
+csi_status_t
+_csi_name_undefine (csi_t *ctx, csi_name_t name)
+{
+    unsigned int i;
+
+    for (i = ctx->dstack.len; --i; ) {
+       if (csi_dictionary_has (ctx->dstack.objects[i].datum.dictionary,
+                               name))
+       {
+           csi_dictionary_remove (ctx,
+                                  ctx->dstack.objects[i].datum.dictionary,
+                                  name);
+           return CSI_STATUS_SUCCESS;
+       }
+    }
+
+    return _csi_error (CSI_STATUS_INVALID_SCRIPT);
+}
+
+csi_status_t
+_csi_intern_string (csi_t *ctx, const char **str_inout, int len)
+{
+    char *str = (char *) *str_inout;
+    csi_intern_string_t tmpl, *istring;
+    csi_status_t status = CSI_STATUS_SUCCESS;
+
+    tmpl.hash_entry.hash = _intern_string_hash (str, len);
+    tmpl.len = len;
+    tmpl.string = (char *) str;
+
+    istring = _csi_hash_table_lookup (&ctx->strings, &tmpl.hash_entry);
+    if (istring == NULL) {
+       istring = _csi_perm_alloc (ctx,
+                                  sizeof (csi_intern_string_t) + len + 1);
+       if (istring != NULL) {
+           istring->hash_entry.hash = tmpl.hash_entry.hash;
+           istring->len = tmpl.len;
+           istring->string = (char *) (istring + 1);
+           memcpy (istring->string, str, len);
+           istring->string[len] = '\0';
+
+           status = _csi_hash_table_insert (&ctx->strings,
+                                            &istring->hash_entry);
+           if (_csi_unlikely (status)) {
+               _csi_free (ctx, istring);
+               return status;
+           }
+       } else
+           return _csi_error (CSI_STATUS_NO_MEMORY);
+    }
+
+    *str_inout = istring->string;
+    return CSI_STATUS_SUCCESS;
+}
+
+/* Public */
+
+static csi_t _csi_nil = { -1, CSI_STATUS_NO_MEMORY };
+
+csi_t *
+cairo_script_interpreter_create (void)
+{
+    csi_t *ctx;
+
+    ctx = malloc (sizeof (csi_t));
+    if (ctx == NULL)
+       return (csi_t *) &_csi_nil;
+
+    _csi_init (ctx);
+
+    return ctx;
+}
+
+void
+cairo_script_interpreter_install_hooks (csi_t *ctx,
+                                       const csi_hooks_t *hooks)
+{
+    if (ctx->status)
+       return;
+
+    ctx->hooks = *hooks;
+}
+
+cairo_status_t
+cairo_script_interpreter_run (csi_t *ctx, const char *filename)
+{
+    csi_object_t file;
+
+    if (ctx->status)
+       return ctx->status;
+    if (ctx->finished)
+       return ctx->status = CSI_STATUS_INTERPRETER_FINISHED;
+
+    ctx->status = csi_file_new (ctx, &file, filename, "r");
+    if (ctx->status)
+       return ctx->status;
+
+    file.type |= CSI_OBJECT_ATTR_EXECUTABLE;
+
+    ctx->status = csi_object_execute (ctx, &file);
+    csi_object_free (ctx, &file);
+
+    return ctx->status;
+}
+
+cairo_status_t
+cairo_script_interpreter_feed_stream (csi_t *ctx, FILE *stream)
+{
+    csi_object_t file;
+
+    if (ctx->status)
+       return ctx->status;
+    if (ctx->finished)
+       return ctx->status = CSI_STATUS_INTERPRETER_FINISHED;
+
+    ctx->status = csi_file_new_for_stream (ctx, &file, stream);
+    if (ctx->status)
+       return ctx->status;
+
+    file.type |= CSI_OBJECT_ATTR_EXECUTABLE;
+
+    ctx->status = csi_object_execute (ctx, &file);
+    csi_object_free (ctx, &file);
+
+    return ctx->status;
+}
+
+cairo_status_t
+cairo_script_interpreter_feed_string (csi_t *ctx, const char *line, int len)
+{
+    csi_object_t file;
+
+    if (ctx->status)
+       return ctx->status;
+    if (ctx->finished)
+       return ctx->status = CSI_STATUS_INTERPRETER_FINISHED;
+
+    if (len < 0)
+       len = strlen (line);
+    ctx->status = csi_file_new_for_bytes (ctx, &file, line, len);
+    if (ctx->status)
+       return ctx->status;
+
+    file.type |= CSI_OBJECT_ATTR_EXECUTABLE;
+
+    ctx->status = csi_object_execute (ctx, &file);
+    csi_object_free (ctx, &file);
+
+    return ctx->status;
+}
+
+unsigned int
+cairo_script_interpreter_get_line_number (csi_t *ctx)
+{
+    return ctx->scanner.line_number + 1; /* 1 index based */
+}
+
+csi_t *
+cairo_script_interpreter_reference (csi_t *ctx)
+{
+    ctx->ref_count++;
+    return ctx;
+}
+slim_hidden_def (cairo_script_interpreter_reference);
+
+cairo_status_t
+cairo_script_interpreter_finish (csi_t *ctx)
+{
+    csi_status_t status;
+
+    status = ctx->status;
+    if (! ctx->finished) {
+       _csi_finish (ctx);
+       ctx->finished = 1;
+    } else if (status == CAIRO_STATUS_SUCCESS) {
+       status = ctx->status = CSI_STATUS_INTERPRETER_FINISHED;
+    }
+
+    return status;
+}
+
+static void
+_csi_fini (csi_t *ctx)
+{
+    if (! ctx->finished)
+       _csi_finish (ctx);
+
+    if (ctx->free_array != NULL)
+       csi_array_free (ctx, ctx->free_array);
+    if (ctx->free_dictionary != NULL)
+       csi_dictionary_free (ctx, ctx->free_dictionary);
+    if (ctx->free_string != NULL)
+       csi_string_free (ctx, ctx->free_string);
+
+    _csi_slab_fini (ctx);
+    _csi_perm_fini (ctx);
+}
+
+cairo_status_t
+cairo_script_interpreter_destroy (csi_t *ctx)
+{
+    csi_status_t status;
+
+    status = ctx->status;
+    if (--ctx->ref_count)
+       return status;
+
+    _csi_fini (ctx);
+    free (ctx);
+
+    return status;
+}
+slim_hidden_def (cairo_script_interpreter_destroy);
+
+cairo_status_t
+cairo_script_interpreter_translate_stream (FILE *stream,
+                                          cairo_write_func_t write_func,
+                                          void *closure)
+{
+    csi_t ctx;
+    csi_object_t src;
+    csi_status_t status;
+
+    _csi_init (&ctx);
+
+    status = csi_file_new_for_stream (&ctx, &src, stream);
+    if (status)
+       goto BAIL;
+
+    status = _csi_translate_file (&ctx, src.datum.file, write_func, closure);
+
+BAIL:
+    csi_object_free (&ctx, &src);
+    _csi_fini (&ctx);
+
+    return status;
+}
diff --git a/util/cairo-script/cairo-script-interpreter.h b/util/cairo-script/cairo-script-interpreter.h
new file mode 100644 (file)
index 0000000..bbdd15d
--- /dev/null
@@ -0,0 +1,117 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2008 Chris Wilson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Chris Wilson
+ *
+ * Contributor(s):
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#ifndef CAIRO_SCRIPT_INTERPRETER_H
+#define CAIRO_SCRIPT_INTERPRETER_H
+
+#include <cairo.h>
+#include <stdio.h>
+
+CAIRO_BEGIN_DECLS
+
+typedef struct _cairo_script_interpreter cairo_script_interpreter_t;
+
+/* XXX expose csi_dictionary_t and pass to hooks */
+typedef void
+(*csi_destroy_func_t) (void *closure,
+                      void *ptr);
+
+typedef cairo_surface_t *
+(*csi_surface_create_func_t) (void *closure,
+                             cairo_content_t content,
+                             double width,
+                             double height,
+                             long uid);
+typedef cairo_t *
+(*csi_context_create_func_t) (void *closure,
+                             cairo_surface_t *surface);
+typedef void
+(*csi_show_page_func_t) (void *closure,
+                        cairo_t *cr);
+
+typedef void
+(*csi_copy_page_func_t) (void *closure,
+                        cairo_t *cr);
+
+typedef struct _cairo_script_interpreter_hooks {
+    void *closure;
+    csi_surface_create_func_t surface_create;
+    csi_destroy_func_t surface_destroy;
+    csi_context_create_func_t context_create;
+    csi_destroy_func_t context_destroy;
+    csi_show_page_func_t show_page;
+    csi_copy_page_func_t copy_page;
+} cairo_script_interpreter_hooks_t;
+
+cairo_public cairo_script_interpreter_t *
+cairo_script_interpreter_create (void);
+
+cairo_public void
+cairo_script_interpreter_install_hooks (cairo_script_interpreter_t *ctx,
+                                       const cairo_script_interpreter_hooks_t *hooks);
+
+cairo_public cairo_status_t
+cairo_script_interpreter_run (cairo_script_interpreter_t *ctx,
+                             const char *filename);
+
+cairo_public cairo_status_t
+cairo_script_interpreter_feed_stream (cairo_script_interpreter_t *ctx,
+                                     FILE *stream);
+
+cairo_public cairo_status_t
+cairo_script_interpreter_feed_string (cairo_script_interpreter_t *ctx,
+                                     const char *line,
+                                     int len);
+
+cairo_public unsigned int
+cairo_script_interpreter_get_line_number (cairo_script_interpreter_t *ctx);
+
+cairo_public cairo_script_interpreter_t *
+cairo_script_interpreter_reference (cairo_script_interpreter_t *ctx);
+
+cairo_public cairo_status_t
+cairo_script_interpreter_finish (cairo_script_interpreter_t *ctx);
+
+cairo_public cairo_status_t
+cairo_script_interpreter_destroy (cairo_script_interpreter_t *ctx);
+
+cairo_public cairo_status_t
+cairo_script_interpreter_translate_stream (FILE *stream,
+                                          cairo_write_func_t write_func,
+                                          void *closure);
+
+CAIRO_END_DECLS
+
+#endif /*CAIRO_SCRIPT_INTERPRETER_H*/
diff --git a/util/cairo-script/cairo-script-objects.c b/util/cairo-script/cairo-script-objects.c
new file mode 100644 (file)
index 0000000..adf6160
--- /dev/null
@@ -0,0 +1,933 @@
+/*
+ * Copyright © 2008 Chris Wilson <chris@chris-wilson.co.uk>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Chris Wilson.
+ *
+ * Contributor(s):
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-script-private.h"
+
+#include <limits.h> /* INT_MAX */
+#include <string.h>
+
+csi_status_t
+csi_array_new (csi_t *ctx,
+              csi_integer_t initial_size,
+              csi_object_t *obj)
+
+{
+    csi_array_t *array;
+
+    if (ctx->free_array == NULL ||
+       ctx->free_array->stack.size <= initial_size)
+    {
+       csi_status_t status;
+
+       array = _csi_slab_alloc (ctx, sizeof (csi_array_t));
+       if (_csi_unlikely (array == NULL))
+           return _csi_error (CSI_STATUS_NO_MEMORY);
+
+       status = _csi_stack_init (ctx, &array->stack,
+                                 initial_size ? initial_size : 32);
+       if (_csi_unlikely (status)) {
+           _csi_slab_free (ctx, array, sizeof (csi_array_t));
+           return status;
+       }
+    } else {
+       array = ctx->free_array;
+       ctx->free_array = NULL;
+    }
+
+    array->base.type = CSI_OBJECT_TYPE_ARRAY;
+    array->base.ref = 1;
+
+    obj->type = CSI_OBJECT_TYPE_ARRAY;
+    obj->datum.array = array;
+
+    return CSI_STATUS_SUCCESS;
+}
+
+csi_status_t
+csi_array_put (csi_t *ctx,
+              csi_array_t *array,
+              csi_integer_t elem,
+              csi_object_t *value)
+{
+    if (_csi_unlikely (elem < 0))
+       return _csi_error (CSI_STATUS_INVALID_SCRIPT);
+
+    if (_csi_unlikely (elem >= array->stack.len)) {
+       csi_status_t status;
+
+       status = _csi_stack_grow (ctx, &array->stack, elem + 1);
+       if (_csi_unlikely (status))
+           return status;
+
+       memset (array->stack.objects + array->stack.len,
+               0, (elem - array->stack.len + 1) * sizeof (csi_object_t));
+       array->stack.len = elem + 1;
+    }
+
+    csi_object_free (ctx, &array->stack.objects[elem]);
+    array->stack.objects[elem] = *csi_object_reference (value);
+
+    return CSI_STATUS_SUCCESS;
+}
+
+csi_status_t
+csi_array_get (csi_t *ctx,
+              csi_array_t *array,
+              csi_integer_t elem,
+              csi_object_t *value)
+{
+    if (_csi_unlikely (elem < 0))
+       return _csi_error (CSI_STATUS_INVALID_SCRIPT);
+
+    if (_csi_unlikely (elem > array->stack.len))
+       return _csi_error (CSI_STATUS_INVALID_SCRIPT);
+
+    *value = array->stack.objects[elem];
+    return CSI_STATUS_SUCCESS;
+}
+
+csi_status_t
+csi_array_append (csi_t *ctx,
+                 csi_array_t *array,
+                 csi_object_t *obj)
+{
+    return _csi_stack_push (ctx, &array->stack, csi_object_reference (obj));
+}
+
+inline csi_status_t
+_csi_array_execute (csi_t *ctx, csi_array_t *array)
+{
+    csi_integer_t i;
+    csi_status_t status;
+
+    if (_csi_unlikely (array->stack.len == 0))
+       return CSI_STATUS_SUCCESS;
+
+    for (i = 0; i < array->stack.len; i++) {
+       csi_object_t *obj = &array->stack.objects[i];
+
+       if (obj->type & CSI_OBJECT_ATTR_EXECUTABLE) {
+           if (obj->type == (CSI_OBJECT_TYPE_ARRAY |
+                             CSI_OBJECT_ATTR_EXECUTABLE))
+           {
+               status = _csi_push_ostack_copy (ctx, &array->stack.objects[i]);
+           }
+           else
+               status = csi_object_execute (ctx, &array->stack.objects[i]);
+       } else
+           status = _csi_push_ostack_copy (ctx, &array->stack.objects[i]);
+       if (_csi_unlikely (status))
+           return status;
+    }
+
+    return CSI_STATUS_SUCCESS;
+}
+
+void
+csi_array_free (csi_t *ctx, csi_array_t *array)
+{
+#if CSI_DEBUG_MALLOC
+    _csi_stack_fini (ctx, &array->stack);
+    _csi_slab_free (ctx, array, sizeof (csi_array_t));
+#else
+    csi_integer_t n;
+
+    for (n = 0; n < array->stack.len; n++)
+       csi_object_free (ctx, &array->stack.objects[n]);
+    array->stack.len = 0;
+
+    if (ctx->free_array != NULL) {
+       if (array->stack.size > ctx->free_array->stack.size) {
+           csi_array_t *tmp = ctx->free_array;
+           ctx->free_array = array;
+           array = tmp;
+       }
+
+       _csi_stack_fini (ctx, &array->stack);
+       _csi_slab_free (ctx, array, sizeof (csi_array_t));
+    } else
+       ctx->free_array = array;
+#endif
+}
+
+static cairo_bool_t
+_dictionary_name_equal (const void *_a, const void *_b)
+{
+    return TRUE;
+}
+
+csi_status_t
+csi_dictionary_new (csi_t *ctx,
+                   csi_object_t *obj)
+
+{
+    csi_dictionary_t *dict;
+
+    if (ctx->free_dictionary != NULL) {
+       dict = ctx->free_dictionary;
+       ctx->free_dictionary = NULL;
+    } else {
+       csi_status_t status;
+
+       dict = _csi_slab_alloc (ctx, sizeof (csi_dictionary_t));
+       if (_csi_unlikely (dict == NULL))
+           return _csi_error (CSI_STATUS_NO_MEMORY);
+
+       status = _csi_hash_table_init (&dict->hash_table,
+                                      _dictionary_name_equal);
+       if (_csi_unlikely (status)) {
+           _csi_slab_free (ctx, dict, sizeof (csi_dictionary_t));
+           return status;
+       }
+    }
+
+    dict->base.type = CSI_OBJECT_TYPE_DICTIONARY;
+    dict->base.ref = 1;
+
+    obj->type = CSI_OBJECT_TYPE_DICTIONARY;
+    obj->datum.dictionary = dict;
+
+    return CSI_STATUS_SUCCESS;
+}
+
+struct _dictionary_entry_pluck {
+    csi_t *ctx;
+    csi_hash_table_t *hash_table;
+};
+
+static void
+_dictionary_entry_pluck (void *entry, void *data)
+{
+    csi_dictionary_entry_t *dict_entry;
+    struct _dictionary_entry_pluck *pluck_data;
+
+    dict_entry = entry;
+    pluck_data = data;
+
+    _csi_hash_table_remove (pluck_data->hash_table, entry);
+    csi_object_free (pluck_data->ctx, &dict_entry->value);
+    _csi_slab_free (pluck_data->ctx, entry, sizeof (csi_dictionary_entry_t));
+}
+
+void
+csi_dictionary_free (csi_t *ctx,
+                    csi_dictionary_t *dict)
+{
+    struct _dictionary_entry_pluck data;
+
+    data.ctx = ctx;
+    data.hash_table = &dict->hash_table;
+    _csi_hash_table_foreach (&dict->hash_table,
+                            _dictionary_entry_pluck,
+                            &data);
+
+#if CSI_DEBUG_MALLOC
+    _csi_hash_table_fini (&dict->hash_table);
+    _csi_slab_free (ctx, dict, sizeof (csi_dictionary_t));
+#else
+    if (ctx->free_dictionary != NULL) {
+       _csi_hash_table_fini (&dict->hash_table);
+       _csi_slab_free (ctx, dict, sizeof (csi_dictionary_t));
+    } else
+       ctx->free_dictionary = dict;
+#endif
+}
+
+csi_status_t
+csi_dictionary_put (csi_t *ctx,
+                   csi_dictionary_t *dict,
+                   csi_name_t name,
+                   csi_object_t *value)
+{
+    csi_dictionary_entry_t *entry;
+    csi_status_t status;
+
+    entry = _csi_hash_table_lookup (&dict->hash_table,
+                                   (csi_hash_entry_t *) &name);
+    if (entry != NULL) {
+       /* replace the existing entry */
+       csi_object_free (ctx, &entry->value);
+       entry->value = *csi_object_reference (value);
+       return CSI_STATUS_SUCCESS;
+    }
+
+    entry = _csi_slab_alloc (ctx, sizeof (*entry));
+    if (_csi_unlikely (entry == NULL))
+       return _csi_error (CSI_STATUS_NO_MEMORY);
+
+    entry->hash_entry.hash = name;
+    status = _csi_hash_table_insert (&dict->hash_table, &entry->hash_entry);
+    if (_csi_unlikely (status)) {
+       _csi_slab_free (ctx, entry, sizeof (*entry));
+       return status;
+    }
+
+    entry->value = *csi_object_reference (value);
+
+    return CSI_STATUS_SUCCESS;
+}
+
+csi_status_t
+csi_dictionary_get (csi_t *ctx,
+                   csi_dictionary_t *dict,
+                   csi_name_t name,
+                   csi_object_t *value)
+{
+    csi_dictionary_entry_t *entry;
+
+    entry = _csi_hash_table_lookup (&dict->hash_table,
+                                   (csi_hash_entry_t *) &name);
+    if (_csi_unlikely (entry == NULL))
+       return _csi_error (CSI_STATUS_INVALID_SCRIPT);
+
+    *value = entry->value;
+    return CSI_STATUS_SUCCESS;
+}
+
+csi_boolean_t
+csi_dictionary_has (csi_dictionary_t *dict,
+                   csi_name_t name)
+{
+    return _csi_hash_table_lookup (&dict->hash_table,
+                                  (csi_hash_entry_t *) &name) != NULL;
+}
+
+void
+csi_dictionary_remove (csi_t *ctx,
+                      csi_dictionary_t *dict,
+                      csi_name_t name)
+{
+    csi_dictionary_entry_t *entry;
+
+    entry = _csi_hash_table_lookup (&dict->hash_table,
+                                   (csi_hash_entry_t *) &name);
+    if (entry != NULL) {
+       _csi_hash_table_remove (&dict->hash_table, &entry->hash_entry);
+       csi_object_free (ctx, &entry->value);
+       _csi_slab_free (ctx, entry, sizeof (csi_dictionary_entry_t));
+    }
+}
+
+csi_status_t
+csi_matrix_new (csi_t *ctx,
+               csi_object_t *obj)
+{
+    csi_matrix_t *matrix;
+
+    matrix = _csi_slab_alloc (ctx, sizeof (csi_matrix_t));
+    if (_csi_unlikely (matrix == NULL))
+       return _csi_error (CSI_STATUS_NO_MEMORY);
+
+    matrix->base.type = CSI_OBJECT_TYPE_MATRIX;
+    matrix->base.ref = 1;
+    cairo_matrix_init_identity (&matrix->matrix);
+
+    obj->type = CSI_OBJECT_TYPE_MATRIX;
+    obj->datum.matrix = matrix;
+
+    return CSI_STATUS_SUCCESS;
+}
+
+csi_status_t
+csi_matrix_new_from_array (csi_t *ctx,
+                          csi_object_t *obj,
+                          csi_array_t *array)
+{
+    csi_matrix_t *matrix;
+
+    if (_csi_unlikely (array->stack.len != 6))
+       return _csi_error (CSI_STATUS_INVALID_SCRIPT);
+
+    matrix = _csi_slab_alloc (ctx, sizeof (csi_matrix_t));
+    if (_csi_unlikely (matrix == NULL))
+       return _csi_error (CSI_STATUS_NO_MEMORY);
+
+    matrix->base.type = CSI_OBJECT_TYPE_MATRIX;
+    matrix->base.ref = 1;
+    cairo_matrix_init (&matrix->matrix,
+                      csi_number_get_value (&array->stack.objects[0]),
+                      csi_number_get_value (&array->stack.objects[1]),
+                      csi_number_get_value (&array->stack.objects[2]),
+                      csi_number_get_value (&array->stack.objects[3]),
+                      csi_number_get_value (&array->stack.objects[4]),
+                      csi_number_get_value (&array->stack.objects[5]));
+
+    obj->type = CSI_OBJECT_TYPE_MATRIX;
+    obj->datum.matrix = matrix;
+
+    return CSI_STATUS_SUCCESS;
+}
+
+csi_status_t
+csi_matrix_new_from_matrix (csi_t *ctx,
+                           csi_object_t *obj,
+                           const cairo_matrix_t *m)
+{
+    csi_matrix_t *matrix;
+
+    matrix = _csi_slab_alloc (ctx, sizeof (csi_matrix_t));
+    if (_csi_unlikely (matrix == NULL))
+       return _csi_error (CSI_STATUS_NO_MEMORY);
+
+    matrix->base.type = CSI_OBJECT_TYPE_MATRIX;
+    matrix->base.ref = 1;
+    matrix->matrix = *m;
+
+    obj->type = CSI_OBJECT_TYPE_MATRIX;
+    obj->datum.matrix = matrix;
+
+    return CSI_STATUS_SUCCESS;
+}
+
+csi_status_t
+csi_matrix_new_from_values (csi_t *ctx,
+                           csi_object_t *obj,
+                           double v[6])
+{
+    csi_matrix_t *matrix;
+
+    matrix = _csi_slab_alloc (ctx, sizeof (csi_matrix_t));
+    if (_csi_unlikely (matrix == NULL))
+       return _csi_error (CSI_STATUS_NO_MEMORY);
+
+    matrix->base.type = CSI_OBJECT_TYPE_MATRIX;
+    matrix->base.ref = 1;
+    cairo_matrix_init (&matrix->matrix, v[0], v[1], v[2], v[3], v[4], v[5]);
+
+    obj->type = CSI_OBJECT_TYPE_MATRIX;
+    obj->datum.matrix = matrix;
+
+    return CSI_STATUS_SUCCESS;
+}
+
+void
+csi_matrix_free (csi_t *ctx,
+                csi_matrix_t *obj)
+{
+    _csi_slab_free (ctx, obj, sizeof (csi_matrix_t));
+}
+
+
+csi_status_t
+csi_name_new (csi_t *ctx,
+             csi_object_t *obj,
+             const char *str,
+             int len)
+{
+    csi_status_t status;
+
+    status = _csi_intern_string (ctx, &str, len);
+    if (_csi_unlikely (status))
+       return status;
+
+    obj->type = CSI_OBJECT_TYPE_NAME;
+    obj->datum.name = (csi_name_t) str;
+
+    return CSI_STATUS_SUCCESS;
+}
+
+csi_status_t
+csi_name_new_static (csi_t *ctx,
+                    csi_object_t *obj,
+                    const char *str)
+{
+    csi_status_t status;
+
+    status = _csi_intern_string (ctx, &str, strlen (str));
+    if (_csi_unlikely (status))
+       return status;
+
+    obj->type = CSI_OBJECT_TYPE_NAME;
+    obj->datum.name = (csi_name_t) str;
+
+    return CSI_STATUS_SUCCESS;
+}
+
+csi_status_t
+csi_string_new (csi_t *ctx,
+               csi_object_t *obj,
+               const char *str,
+               int len)
+{
+    csi_string_t *string;
+
+    if (len < 0)
+       len = strlen (str);
+    if (_csi_unlikely (len >= INT_MAX))
+       return _csi_error (CSI_STATUS_NO_MEMORY);
+
+    if (ctx->free_string == NULL || ctx->free_string->len <= len) {
+       string = _csi_slab_alloc (ctx, sizeof (csi_string_t));
+       if (_csi_unlikely (string == NULL))
+           return _csi_error (CSI_STATUS_NO_MEMORY);
+
+       string->string = _csi_alloc (ctx, len + 1);
+       if (_csi_unlikely (string->string == NULL)) {
+           _csi_slab_free (ctx, string, sizeof (csi_string_t));
+           return _csi_error (CSI_STATUS_NO_MEMORY);
+       }
+    } else {
+       string = ctx->free_string;
+       ctx->free_string = NULL;
+    }
+
+    if (str != NULL) {
+       memcpy (string->string, str, len);
+       string->string[len] = '\0';
+    }
+    string->len = len;
+    string->deflate = 0;
+
+    string->base.type = CSI_OBJECT_TYPE_STRING;
+    string->base.ref = 1;
+
+    obj->type = CSI_OBJECT_TYPE_STRING;
+    obj->datum.string = string;
+
+    return CSI_STATUS_SUCCESS;
+}
+
+csi_status_t
+csi_string_deflate_new (csi_t *ctx,
+                       csi_object_t *obj,
+                       void *bytes,
+                       int in_len,
+                       int out_len)
+{
+    csi_status_t status;
+    csi_string_t *string;
+
+    status = csi_string_new (ctx, obj, bytes, in_len);
+    if (_csi_unlikely (status))
+       return status;
+
+    string = obj->datum.string;
+    string->deflate = out_len;
+
+    return CSI_STATUS_SUCCESS;
+}
+
+csi_status_t
+csi_string_new_from_bytes (csi_t *ctx,
+                          csi_object_t *obj,
+                          char *bytes,
+                          unsigned int len)
+{
+    csi_string_t *string;
+
+    if (_csi_unlikely (len >= INT_MAX))
+       return _csi_error (CSI_STATUS_NO_MEMORY);
+
+    string = _csi_slab_alloc (ctx, sizeof (csi_string_t));
+    if (_csi_unlikely (string == NULL))
+       return _csi_error (CSI_STATUS_NO_MEMORY);
+
+    string->string = bytes;
+    string->len = len;
+    string->deflate = 0;
+
+    string->base.type = CSI_OBJECT_TYPE_STRING;
+    string->base.ref = 1;
+
+    obj->type = CSI_OBJECT_TYPE_STRING;
+    obj->datum.string = string;
+
+    return CSI_STATUS_SUCCESS;
+}
+
+static inline csi_status_t
+_csi_string_execute (csi_t *ctx, csi_string_t *string)
+{
+    csi_status_t status;
+    csi_object_t obj;
+
+    if (_csi_unlikely (string->len == 0))
+       return CSI_STATUS_SUCCESS;
+
+    status = csi_file_new_for_bytes (ctx, &obj, string->string, string->len);
+    if (_csi_unlikely (status))
+       return status;
+
+    status = _csi_scan_file (ctx, obj.datum.file);
+    csi_object_free (ctx, &obj);
+
+    return status;
+}
+
+void
+csi_string_free (csi_t *ctx, csi_string_t *string)
+{
+#if CSI_DEBUG_MALLOC
+    _csi_free (ctx, string->string);
+    _csi_slab_free (ctx, string, sizeof (csi_string_t));
+#else
+    if (ctx->free_string != NULL) {
+       if (string->len > ctx->free_string->len) {
+           csi_string_t *tmp = ctx->free_string;
+           ctx->free_string = string;
+           string = tmp;
+       }
+
+       _csi_free (ctx, string->string);
+       _csi_slab_free (ctx, string, sizeof (csi_string_t));
+    } else
+       ctx->free_string = string;
+#endif
+}
+
+csi_status_t
+csi_object_execute (csi_t *ctx, csi_object_t *obj)
+{
+    csi_status_t status;
+    csi_object_t indirect;
+
+ INDIRECT:
+    switch (obj->type & CSI_OBJECT_TYPE_MASK) {
+    case CSI_OBJECT_TYPE_NAME:
+       status = _csi_name_lookup (ctx, obj->datum.name, &indirect);
+       if (_csi_unlikely (status))
+           return status;
+       if (indirect.type & CSI_OBJECT_ATTR_EXECUTABLE) {
+           obj = &indirect;
+           goto INDIRECT;
+       } else
+           return _csi_push_ostack_copy (ctx, &indirect);
+
+    case CSI_OBJECT_TYPE_OPERATOR:
+       return obj->datum.op (ctx);
+
+    case CSI_OBJECT_TYPE_ARRAY:
+       return _csi_array_execute (ctx, obj->datum.array);
+    case CSI_OBJECT_TYPE_FILE:
+       return _csi_file_execute (ctx, obj->datum.file);
+    case CSI_OBJECT_TYPE_STRING:
+       return _csi_string_execute (ctx, obj->datum.string);
+
+    default:
+       return _csi_push_ostack_copy (ctx, obj);
+    }
+}
+
+csi_object_t *
+csi_object_reference (csi_object_t *obj)
+{
+    if (CSI_OBJECT_IS_CAIRO (obj)) {
+       switch (obj->type & CSI_OBJECT_TYPE_MASK) {
+       case CSI_OBJECT_TYPE_CONTEXT:
+           cairo_reference (obj->datum.cr);
+           break;
+       case CSI_OBJECT_TYPE_FONT:
+           cairo_font_face_reference (obj->datum.font_face);
+           break;
+       case CSI_OBJECT_TYPE_PATTERN:
+           cairo_pattern_reference (obj->datum.pattern);
+           break;
+       case CSI_OBJECT_TYPE_SCALED_FONT:
+           cairo_scaled_font_reference (obj->datum.scaled_font);
+           break;
+       case CSI_OBJECT_TYPE_SURFACE:
+           cairo_surface_reference (obj->datum.surface);
+           break;
+       }
+    } else if (CSI_OBJECT_IS_COMPOUND (obj)) {
+       obj->datum.object->ref++;
+    }
+
+    return obj;
+}
+
+void
+csi_object_free (csi_t *ctx,
+                csi_object_t *obj)
+{
+    if (CSI_OBJECT_IS_CAIRO (obj)) {
+       switch (obj->type & CSI_OBJECT_TYPE_MASK) {
+       case CSI_OBJECT_TYPE_CONTEXT:
+           cairo_destroy (obj->datum.cr);
+           break;
+       case CSI_OBJECT_TYPE_FONT:
+           cairo_font_face_destroy (obj->datum.font_face);
+           break;
+       case CSI_OBJECT_TYPE_PATTERN:
+           cairo_pattern_destroy (obj->datum.pattern);
+           break;
+       case CSI_OBJECT_TYPE_SCALED_FONT:
+           cairo_scaled_font_destroy (obj->datum.scaled_font);
+           break;
+       case CSI_OBJECT_TYPE_SURFACE:
+           cairo_surface_destroy (obj->datum.surface);
+           break;
+       }
+    } else if (CSI_OBJECT_IS_COMPOUND (obj)) {
+       if (--obj->datum.object->ref)
+           return;
+
+       switch (obj->type & CSI_OBJECT_TYPE_MASK) {
+       case CSI_OBJECT_TYPE_ARRAY:
+           csi_array_free (ctx, obj->datum.array);
+           break;
+       case CSI_OBJECT_TYPE_DICTIONARY:
+           csi_dictionary_free (ctx, obj->datum.dictionary);
+           break;
+       case CSI_OBJECT_TYPE_FILE:
+           _csi_file_free (ctx, obj->datum.file);
+           break;
+       case CSI_OBJECT_TYPE_MATRIX:
+           csi_matrix_free (ctx, obj->datum.matrix);
+           break;
+       case CSI_OBJECT_TYPE_STRING:
+           csi_string_free (ctx, obj->datum.string);
+           break;
+       default:
+           break;
+       }
+    }
+}
+
+csi_status_t
+csi_object_as_file (csi_t *ctx,
+                   csi_object_t *src,
+                   csi_object_t *file)
+{
+    int type = csi_object_get_type (src);
+    switch (type) {
+    case CSI_OBJECT_TYPE_FILE:
+       *file = *csi_object_reference (src);
+       return CSI_STATUS_SUCCESS;
+    case CSI_OBJECT_TYPE_STRING:
+        return csi_file_new_from_string (ctx, file, src->datum.string);
+    case CSI_OBJECT_TYPE_ARRAY:
+#if 0
+       if (src->type & CSI_OBJECT_ATTR_EXECUTABLE)
+           return _csi_file_new_from_procedure (cs, src);
+#endif
+    default:
+       return _csi_error (CSI_STATUS_INVALID_SCRIPT);
+    }
+}
+
+static int
+lexcmp (void const *a, size_t alen,
+       void const *b, size_t blen)
+{
+    size_t len = alen < blen ? alen : blen;
+    int cmp = memcmp (a, b, len);
+    if (cmp)
+       return cmp;
+    if (alen == blen)
+       return 0;
+    return alen < blen ? -1 : +1;
+}
+
+csi_boolean_t
+csi_object_eq (csi_object_t *a,
+              csi_object_t *b)
+{
+    csi_object_type_t atype = csi_object_get_type (a);
+    csi_object_type_t btype = csi_object_get_type (b);
+
+    if (atype == btype) {
+       switch (atype) {
+       case CSI_OBJECT_TYPE_BOOLEAN:
+           return a->datum.boolean == b->datum.boolean;
+       case CSI_OBJECT_TYPE_INTEGER:
+           return a->datum.integer == b->datum.integer;
+       case CSI_OBJECT_TYPE_REAL:
+           return a->datum.real == b->datum.real;
+       case CSI_OBJECT_TYPE_NAME:
+           return a->datum.name == b->datum.name;
+       case CSI_OBJECT_TYPE_STRING:
+           return 0 == lexcmp (a->datum.string->string,
+                               a->datum.string->len,
+                               b->datum.string->string,
+                               b->datum.string->len);
+       case CSI_OBJECT_TYPE_NULL:
+       case CSI_OBJECT_TYPE_MARK:
+           return TRUE;
+       case CSI_OBJECT_TYPE_OPERATOR:
+           return a->datum.op == b->datum.op;
+       case CSI_OBJECT_TYPE_ARRAY:
+       case CSI_OBJECT_TYPE_DICTIONARY:
+       case CSI_OBJECT_TYPE_FILE:
+       case CSI_OBJECT_TYPE_MATRIX:
+       case CSI_OBJECT_TYPE_CONTEXT:
+       case CSI_OBJECT_TYPE_FONT:
+       case CSI_OBJECT_TYPE_PATTERN:
+       case CSI_OBJECT_TYPE_SCALED_FONT:
+       case CSI_OBJECT_TYPE_SURFACE:
+           return a->datum.ptr == b->datum.ptr;
+       }
+    }
+
+    if (atype < btype) {
+       csi_object_t *c;
+       csi_object_type_t ctype;
+       c = a; a = b; b = c;
+       ctype = atype; atype = btype; btype = ctype;
+    }
+
+    switch ((int) atype) {
+    case CSI_OBJECT_TYPE_INTEGER:
+       if (btype == CSI_OBJECT_TYPE_BOOLEAN) {
+           return a->datum.integer == b->datum.boolean;
+       }
+       break;
+    case CSI_OBJECT_TYPE_REAL:
+       if (btype == CSI_OBJECT_TYPE_INTEGER) {
+           return a->datum.real == b->datum.integer;
+       }
+       else if (btype == CSI_OBJECT_TYPE_BOOLEAN) {
+           return a->datum.real == b->datum.boolean;
+       }
+       break;
+
+    case CSI_OBJECT_TYPE_STRING:
+       if (btype == CSI_OBJECT_TYPE_NAME) {
+           const char *bstr = (const char *) b->datum.name;
+           return 0 == lexcmp (a->datum.string->string,
+                               a->datum.string->len,
+                               bstr,
+                               strlen (bstr));
+       }
+       break;
+
+    default:
+       break;
+    }
+
+    return FALSE;
+}
+
+csi_status_t
+csi_object_compare (csi_object_t *a,
+                   csi_object_t *b,
+                   int *out)
+{
+    csi_object_type_t atype = csi_object_get_type (a);
+    csi_object_type_t btype = csi_object_get_type (b);
+    int sign;
+
+    if (csi_object_eq (a, b)){
+       *out = 0;
+       return CSI_STATUS_SUCCESS;
+    }
+
+#define CMP(x,y) ((x) < (y) ? -1 : +1)
+
+    if (atype == btype) {
+       switch (atype) {
+       case CSI_OBJECT_TYPE_BOOLEAN:
+           *out = CMP (a->datum.boolean, b->datum.boolean);
+           return CSI_STATUS_SUCCESS;
+       case CSI_OBJECT_TYPE_INTEGER:
+           *out = CMP (a->datum.integer, b->datum.integer);
+           return CSI_STATUS_SUCCESS;
+       case CSI_OBJECT_TYPE_REAL:
+           *out = CMP (a->datum.real, b->datum.real);
+           return CSI_STATUS_SUCCESS;
+       case CSI_OBJECT_TYPE_NAME: {
+           const char *x = (char const *) a->datum.name;
+           const char *y = (char const *) b->datum.name;
+           *out = lexcmp (x, strlen(x), y, strlen (y));
+           return CSI_STATUS_SUCCESS;
+       }
+       case CSI_OBJECT_TYPE_STRING:
+           *out = lexcmp (a->datum.string->string,
+                          a->datum.string->len,
+                          b->datum.string->string,
+                          b->datum.string->len);
+           return CSI_STATUS_SUCCESS;
+       case CSI_OBJECT_TYPE_NULL:
+       case CSI_OBJECT_TYPE_MARK:
+       case CSI_OBJECT_TYPE_OPERATOR:
+       case CSI_OBJECT_TYPE_ARRAY:
+       case CSI_OBJECT_TYPE_DICTIONARY:
+       case CSI_OBJECT_TYPE_FILE:
+       case CSI_OBJECT_TYPE_MATRIX:
+       case CSI_OBJECT_TYPE_CONTEXT:
+       case CSI_OBJECT_TYPE_FONT:
+       case CSI_OBJECT_TYPE_PATTERN:
+       case CSI_OBJECT_TYPE_SCALED_FONT:
+       case CSI_OBJECT_TYPE_SURFACE:
+           goto TYPE_CHECK_ERROR;
+       }
+    }
+
+    sign = +1;
+    if (atype < btype) {
+       csi_object_t *c;
+       csi_object_type_t ctype;
+       c = a; a = b; b = c;
+       ctype = atype; atype = btype; btype = ctype;
+       sign = -1;
+    }
+
+    switch ((int) atype) {
+    case CSI_OBJECT_TYPE_INTEGER:
+       if (btype == CSI_OBJECT_TYPE_BOOLEAN) {
+           *out = sign * CMP (a->datum.integer, !!b->datum.boolean);
+           return CSI_STATUS_SUCCESS;
+       }
+       break;
+    case CSI_OBJECT_TYPE_REAL:
+       if (btype == CSI_OBJECT_TYPE_INTEGER) {
+           *out = sign * CMP (a->datum.real, b->datum.integer);
+           return CSI_STATUS_SUCCESS;
+       }
+       else if (btype == CSI_OBJECT_TYPE_BOOLEAN) {
+           *out = sign * CMP (a->datum.real, !!b->datum.boolean);
+           return CSI_STATUS_SUCCESS;
+       }
+       break;
+
+    case CSI_OBJECT_TYPE_STRING:
+       if (btype == CSI_OBJECT_TYPE_NAME) {
+           const char *bstr = (const char *) b->datum.name;
+           *out = sign * lexcmp (a->datum.string->string,
+                                 a->datum.string->len,
+                                 bstr,
+                                 strlen (bstr));
+           return CSI_STATUS_SUCCESS;
+       }
+       break;
+
+    default:
+       break;
+    }
+
+#undef CMP
+
+ TYPE_CHECK_ERROR:
+    return _csi_error (CSI_STATUS_SCRIPT_INVALID_TYPE);
+}
diff --git a/util/cairo-script/cairo-script-operators.c b/util/cairo-script/cairo-script-operators.c
new file mode 100644 (file)
index 0000000..ad1a201
--- /dev/null
@@ -0,0 +1,6685 @@
+/*
+ * Copyright © 2008 Chris Wilson <chris@chris-wilson.co.uk>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Chris Wilson.
+ *
+ * Contributor(s):
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+/* TODO real path type */
+
+#include "cairo-script-private.h"
+
+#if CAIRO_HAS_SCRIPT_SURFACE
+#include "cairo-script.h"
+#endif
+
+#include <stdio.h> /* snprintf */
+#include <stdlib.h> /* mkstemp */
+#include <string.h>
+
+#ifdef _MSC_VER
+#define _USE_MATH_DEFINES /* for M_LN2, M_PI and M_SQRT2 on win32 */
+#define snprintf _snprintf
+#endif
+
+#include <math.h>
+#include <limits.h> /* INT_MAX */
+#include <assert.h>
+#include <zlib.h>
+
+#ifdef HAVE_MMAP
+# ifdef HAVE_UNISTD_H
+#  include <sys/mman.h>
+#  include <unistd.h>
+# else
+#  undef HAVE_MMAP
+# endif
+#endif
+
+typedef struct _csi_proxy {
+    csi_t *ctx;
+    void *ptr;
+    csi_dictionary_t *dictionary;
+    csi_destroy_func_t destroy_func;
+    void *destroy_data;
+} csi_proxy_t;
+
+typedef struct _csi_blob {
+    csi_list_t list;
+    unsigned long hash;
+    uint8_t *bytes;
+    unsigned int len;
+} csi_blob_t;
+
+static const cairo_user_data_key_t _csi_proxy_key;
+static const cairo_user_data_key_t _csi_blob_key;
+
+enum mime_type {
+    MIME_TYPE_NONE = 0,
+    MIME_TYPE_PNG
+};
+
+#define check(CNT) do {\
+    if (_csi_unlikely (! _csi_check_ostack (ctx, (CNT)))) \
+       return _csi_error (CSI_STATUS_INVALID_SCRIPT); \
+} while (0)
+#define pop(CNT) _csi_pop_ostack (ctx, (CNT))
+#define push(OBJ) _csi_push_ostack (ctx, (OBJ))
+
+static csi_proxy_t *
+_csi_proxy_create (csi_t *ctx,
+                  void *ptr,
+                  csi_dictionary_t *dictionary,
+                  csi_destroy_func_t destroy_func,
+                  void *destroy_data)
+{
+    csi_proxy_t *proxy;
+
+    proxy = _csi_slab_alloc (ctx, sizeof (csi_proxy_t));
+    if (proxy == NULL)
+       return NULL;
+
+    proxy->ctx = cairo_script_interpreter_reference (ctx);
+    proxy->ptr = ptr;
+    proxy->destroy_func = destroy_func;
+    proxy->destroy_data = destroy_data;
+    proxy->dictionary = dictionary;
+    if (dictionary != NULL)
+       dictionary->base.ref++;
+
+    return proxy;
+}
+
+static void
+_csi_proxy_destroy (void *closure)
+{
+    csi_proxy_t *proxy = closure;
+    csi_t *ctx = proxy->ctx;
+
+    /* XXX this doesn't work because user_data_destroy is called too late.
+     * Considering another hook into the (cairo internal) object system.
+     */
+    if (proxy->destroy_func != NULL)
+       proxy->destroy_func (proxy->destroy_data, proxy->ptr);
+
+    if (proxy->dictionary != NULL && --proxy->dictionary->base.ref == 0)
+       csi_dictionary_free (ctx, proxy->dictionary);
+
+    _csi_slab_free (ctx, proxy, sizeof (csi_proxy_t));
+    cairo_script_interpreter_destroy (ctx);
+}
+
+static void
+_csi_blob_hash (csi_blob_t *blob, const uint32_t *data, int len)
+{
+    unsigned long hash = blob->hash;
+    /* very simple! */
+    while (len--) {
+       unsigned long c = *data++;
+       hash *= 33;
+       hash ^= c;
+    }
+    blob->hash = hash;
+}
+
+static csi_boolean_t
+_csi_blob_equal (const csi_list_t *link, void *data)
+{
+    csi_blob_t *A, *B;
+
+    A = csi_container_of (link, csi_blob_t, list);
+    B = data;
+
+    if (A->len != B->len)
+       return FALSE;
+
+    if (A->hash != B->hash)
+       return FALSE;
+
+    return memcmp (A->bytes, B->bytes, A->len) == 0;
+}
+
+static void
+_csi_blob_init (csi_blob_t *blob, uint8_t *bytes, int len)
+{
+    blob->hash = 5381;
+    blob->len = len;
+    blob->bytes = bytes;
+}
+
+static csi_list_t *
+_csi_list_unlink (csi_list_t *head, csi_list_t *link)
+{
+    if (link->next != NULL)
+       link->next->prev = link->prev;
+    if (link->prev != NULL)
+       link->prev->next = link->next;
+    else
+       head = link->next;
+    return head;
+}
+
+static csi_list_t *
+_csi_list_prepend (csi_list_t *head, csi_list_t *link)
+{
+    if (head != NULL)
+       head->prev = link;
+    link->next = head;
+    link->prev = NULL;
+    return link;
+}
+
+static csi_list_t *
+_csi_list_find (csi_list_t *head,
+               csi_boolean_t (*predicate) (const csi_list_t *link, void *data),
+               void *data)
+{
+    while (head != NULL) {
+       if (predicate (head, data))
+           return head;
+       head = head->next;
+    }
+
+    return NULL;
+}
+
+static csi_status_t
+_csi_ostack_get_boolean (csi_t *ctx, unsigned int i, csi_boolean_t *out)
+{
+    csi_object_t *obj;
+    int type;
+
+    obj = _csi_peek_ostack (ctx, i);
+    type = csi_object_get_type (obj);
+    switch (type) {
+    case CSI_OBJECT_TYPE_BOOLEAN:
+       *out = obj->datum.boolean;
+       break;
+    case CSI_OBJECT_TYPE_INTEGER:
+       *out = !! obj->datum.integer;
+       break;
+    case CSI_OBJECT_TYPE_REAL:
+       *out = obj->datum.real != 0.;
+       break;
+    default:
+       return _csi_error (CSI_STATUS_INVALID_SCRIPT);
+    }
+    return CSI_STATUS_SUCCESS;
+}
+
+static csi_status_t
+_csi_ostack_get_integer (csi_t *ctx, unsigned int i, csi_integer_t *out)
+{
+    csi_object_t *obj;
+    int type;
+
+    obj = _csi_peek_ostack (ctx, i);
+    type = csi_object_get_type (obj);
+    switch (type) {
+    case CSI_OBJECT_TYPE_BOOLEAN:
+       *out = obj->datum.boolean;
+       break;
+    case CSI_OBJECT_TYPE_INTEGER:
+       *out = obj->datum.integer;
+       break;
+    case CSI_OBJECT_TYPE_REAL:
+       *out = obj->datum.real;
+       break;
+    default:
+       return _csi_error (CSI_STATUS_INVALID_SCRIPT);
+    }
+    return CSI_STATUS_SUCCESS;
+}
+
+static csi_status_t
+_csi_ostack_get_number (csi_t *ctx, unsigned int i, double *out)
+{
+    csi_object_t *obj;
+    int type;
+
+    obj = _csi_peek_ostack (ctx, i);
+    type = csi_object_get_type (obj);
+    switch (type) {
+    case CSI_OBJECT_TYPE_BOOLEAN:
+       *out = obj->datum.boolean;
+       break;
+    case CSI_OBJECT_TYPE_INTEGER:
+       *out = obj->datum.integer;
+       break;
+    case CSI_OBJECT_TYPE_REAL:
+       *out = obj->datum.real;
+       break;
+    default:
+       return _csi_error (CSI_STATUS_INVALID_SCRIPT);
+    }
+    return CSI_STATUS_SUCCESS;
+}
+
+static double
+_csi_object_as_real (csi_object_t *obj)
+{
+    int type;
+
+    type = csi_object_get_type (obj);
+    switch (type) {
+    case CSI_OBJECT_TYPE_BOOLEAN:
+       return obj->datum.boolean;
+    case CSI_OBJECT_TYPE_INTEGER:
+       return obj->datum.integer;
+    case CSI_OBJECT_TYPE_REAL:
+       return obj->datum.real;
+    default:
+       return 0;
+    }
+}
+
+static csi_status_t
+_csi_ostack_get_name (csi_t *ctx, unsigned int i, csi_name_t *out)
+{
+    csi_object_t *obj;
+
+    obj = _csi_peek_ostack (ctx, i);
+    if (_csi_unlikely (csi_object_get_type (obj) != CSI_OBJECT_TYPE_NAME))
+       return _csi_error (CSI_STATUS_INVALID_SCRIPT);
+
+    *out = obj->datum.name;
+    return CSI_STATUS_SUCCESS;
+}
+
+static csi_status_t
+_csi_ostack_get_context (csi_t *ctx, unsigned int i, cairo_t **out)
+{
+    csi_object_t *obj;
+
+    obj = _csi_peek_ostack (ctx, i);
+    if (_csi_unlikely (csi_object_get_type (obj) != CSI_OBJECT_TYPE_CONTEXT))
+       return _csi_error (CSI_STATUS_INVALID_SCRIPT);
+
+    *out = obj->datum.cr;
+    return CSI_STATUS_SUCCESS;
+}
+
+static csi_status_t
+_csi_ostack_get_font_face (csi_t *ctx, unsigned int i, cairo_font_face_t **out)
+{
+    csi_object_t *obj;
+
+    obj = _csi_peek_ostack (ctx, i);
+    if (_csi_unlikely (csi_object_get_type (obj) != CSI_OBJECT_TYPE_FONT))
+       return _csi_error (CSI_STATUS_INVALID_SCRIPT);
+
+    *out = obj->datum.font_face;
+    return CSI_STATUS_SUCCESS;
+}
+
+static csi_status_t
+_csi_ostack_get_pattern (csi_t *ctx, unsigned int i, cairo_pattern_t **out)
+{
+    csi_object_t *obj;
+
+    obj = _csi_peek_ostack (ctx, i);
+    if (_csi_unlikely (csi_object_get_type (obj) != CSI_OBJECT_TYPE_PATTERN))
+       return _csi_error (CSI_STATUS_INVALID_SCRIPT);
+
+    *out = obj->datum.pattern;
+    return CSI_STATUS_SUCCESS;
+}
+
+static csi_status_t
+_csi_ostack_get_scaled_font (csi_t *ctx, unsigned int i,
+                            cairo_scaled_font_t **out)
+{
+    csi_object_t *obj;
+
+    obj = _csi_peek_ostack (ctx, i);
+    if (_csi_unlikely
+       (csi_object_get_type (obj) != CSI_OBJECT_TYPE_SCALED_FONT))
+    {
+       return _csi_error (CSI_STATUS_INVALID_SCRIPT);
+    }
+
+    *out = obj->datum.scaled_font;
+    return CSI_STATUS_SUCCESS;
+}
+
+static csi_status_t
+_csi_ostack_get_surface (csi_t *ctx, unsigned int i, cairo_surface_t **out)
+{
+    csi_object_t *obj;
+    int type;
+
+    obj = _csi_peek_ostack (ctx, i);
+    type = csi_object_get_type (obj);
+    switch (type) {
+    case CSI_OBJECT_TYPE_CONTEXT:
+       *out = cairo_get_target (obj->datum.cr);
+       break;
+    case CSI_OBJECT_TYPE_SURFACE:
+       *out = obj->datum.surface;
+       break;
+    default:
+       return _csi_error (CSI_STATUS_INVALID_SCRIPT);
+    }
+    return CSI_STATUS_SUCCESS;
+}
+
+static csi_status_t
+_csi_ostack_get_array (csi_t *ctx, unsigned int i, csi_array_t **out)
+{
+    csi_object_t *obj;
+
+    obj = _csi_peek_ostack (ctx, i);
+    if (_csi_unlikely (csi_object_get_type (obj) != CSI_OBJECT_TYPE_ARRAY))
+       return _csi_error (CSI_STATUS_INVALID_SCRIPT);
+
+    *out = obj->datum.array;
+    return CSI_STATUS_SUCCESS;
+}
+
+static csi_status_t
+_csi_ostack_get_procedure (csi_t *ctx, unsigned int i, csi_array_t **out)
+{
+    csi_object_t *obj;
+
+    obj = _csi_peek_ostack (ctx, i);
+    if (_csi_unlikely (! csi_object_is_procedure (obj)))
+       return _csi_error (CSI_STATUS_INVALID_SCRIPT);
+
+    *out = obj->datum.array;
+    return CSI_STATUS_SUCCESS;
+}
+
+static csi_status_t
+_csi_ostack_get_dictionary (csi_t *ctx, unsigned int i, csi_dictionary_t **out)
+{
+    csi_object_t *obj;
+
+    obj = _csi_peek_ostack (ctx, i);
+    if (_csi_unlikely
+       (csi_object_get_type (obj) != CSI_OBJECT_TYPE_DICTIONARY))
+    {
+       return _csi_error (CSI_STATUS_INVALID_SCRIPT);
+    }
+
+    *out = obj->datum.dictionary;
+    return CSI_STATUS_SUCCESS;
+}
+
+static csi_status_t
+_csi_ostack_get_matrix (csi_t *ctx, unsigned int i, cairo_matrix_t *out)
+{
+    csi_object_t *obj;
+    int type;
+
+    obj = _csi_peek_ostack (ctx, i);
+    type = csi_object_get_type (obj);
+    switch (type) {
+    case CSI_OBJECT_TYPE_MATRIX:
+       *out = obj->datum.matrix->matrix;
+       return CSI_STATUS_SUCCESS;
+
+    case CSI_OBJECT_TYPE_ARRAY:
+       if (obj->datum.array->stack.len == 6) {
+           cairo_matrix_init (out,
+                              csi_number_get_value (&obj->datum.array->stack.objects[0]),
+                              csi_number_get_value (&obj->datum.array->stack.objects[1]),
+                              csi_number_get_value (&obj->datum.array->stack.objects[2]),
+                              csi_number_get_value (&obj->datum.array->stack.objects[3]),
+                              csi_number_get_value (&obj->datum.array->stack.objects[4]),
+                              csi_number_get_value (&obj->datum.array->stack.objects[5]));
+           return CSI_STATUS_SUCCESS;
+       }
+    default:
+       return _csi_error (CSI_STATUS_INVALID_SCRIPT);
+    }
+}
+
+static csi_status_t
+_csi_dictionary_get_integer (csi_t *ctx,
+                            csi_dictionary_t *dict,
+                            const char *name,
+                            csi_boolean_t optional,
+                            long *value)
+{
+    csi_status_t status;
+    csi_object_t key, obj;
+    int type;
+
+    status = csi_name_new_static (ctx, &key, name);
+    if (_csi_unlikely (status))
+       return status;
+
+    if (optional && ! csi_dictionary_has (dict, key.datum.name))
+       return CSI_STATUS_SUCCESS;
+
+    status = csi_dictionary_get (ctx, dict, key.datum.name, &obj);
+    if (_csi_unlikely (status))
+       return status;
+
+    type = csi_object_get_type (&obj);
+    switch (type) {
+    case CSI_OBJECT_TYPE_BOOLEAN:
+       *value = obj.datum.boolean;
+       break;
+    case CSI_OBJECT_TYPE_INTEGER:
+       *value = obj.datum.integer;
+       break;
+    case CSI_OBJECT_TYPE_REAL:
+       *value = obj.datum.real;
+       break;
+    default:
+       return _csi_error (CSI_STATUS_INVALID_SCRIPT);
+    }
+    return CSI_STATUS_SUCCESS;
+}
+
+static csi_status_t
+_csi_dictionary_get_number (csi_t *ctx,
+                           csi_dictionary_t *dict,
+                           const char *name,
+                           csi_boolean_t optional,
+                           double *value)
+{
+    csi_status_t status;
+    csi_object_t key, obj;
+
+    status = csi_name_new_static (ctx, &key, name);
+    if (_csi_unlikely (status))
+       return status;
+
+    if (optional && ! csi_dictionary_has (dict, key.datum.name))
+       return CSI_STATUS_SUCCESS;
+
+    status = csi_dictionary_get (ctx, dict, key.datum.name, &obj);
+    if (_csi_unlikely (status))
+       return status;
+
+    *value = csi_number_get_value (&obj);
+    return CSI_STATUS_SUCCESS;
+}
+
+static csi_status_t
+_csi_ostack_get_string (csi_t *ctx, unsigned int i, csi_string_t **out)
+{
+    csi_object_t *obj;
+
+    obj = _csi_peek_ostack (ctx, i);
+    if (_csi_unlikely (csi_object_get_type (obj) != CSI_OBJECT_TYPE_STRING))
+       return _csi_error (CSI_STATUS_INVALID_SCRIPT);
+
+    *out = obj->datum.string;
+    return CSI_STATUS_SUCCESS;
+}
+
+static csi_status_t
+_csi_ostack_get_string_constant (csi_t *ctx, unsigned int i, const char **out)
+{
+    csi_object_t *obj;
+    int type;
+
+    obj = _csi_peek_ostack (ctx, i);
+    type = csi_object_get_type (obj);
+    switch (type) {
+    case CSI_OBJECT_TYPE_NAME:
+       *out = (const char *) obj->datum.name;
+       break;
+    case CSI_OBJECT_TYPE_STRING:
+       *out = obj->datum.string->string;
+       break;
+    default:
+       return _csi_error (CSI_STATUS_INVALID_SCRIPT);
+    }
+    return CSI_STATUS_SUCCESS;
+}
+
+static csi_status_t
+_do_cairo_op (csi_t *ctx, void (*op) (cairo_t *))
+{
+    cairo_t *cr;
+    csi_status_t status;
+
+    check (1);
+
+    status = _csi_ostack_get_context (ctx, 0, &cr);
+    if (_csi_unlikely (status))
+       return status;
+
+    op (cr);
+    return CSI_STATUS_SUCCESS;
+}
+
+static csi_status_t
+end_dict_construction (csi_t *ctx)
+{
+    csi_object_t obj;
+    csi_dictionary_t *dict;
+    csi_status_t status;
+
+    status = csi_dictionary_new (ctx, &obj);
+    if (_csi_unlikely (status))
+       return status;
+
+    dict = obj.datum.dictionary;
+    do {
+       csi_object_t *name, *value;
+
+       check (1);
+
+       value = _csi_peek_ostack (ctx, 0);
+       if (csi_object_get_type (value) == CSI_OBJECT_TYPE_MARK) {
+           pop (1);
+           break;
+       }
+
+       check (2);
+
+       name = _csi_peek_ostack (ctx, 1);
+       if (_csi_unlikely
+           (csi_object_get_type (name) != CSI_OBJECT_TYPE_NAME))
+       {
+           return _csi_error (CSI_STATUS_INVALID_SCRIPT);
+       }
+
+       status = csi_dictionary_put (ctx, dict, name->datum.name, value);
+       if (_csi_unlikely (status))
+           return status;
+
+       pop (2);
+    } while (TRUE);
+
+    return push (&obj);
+}
+
+static csi_status_t
+end_array_construction (csi_t *ctx)
+{
+    csi_object_t obj;
+    csi_status_t status;
+    int len = 0;
+
+    do {
+       check (len + 1);
+
+       if (csi_object_get_type (_csi_peek_ostack (ctx, len)) ==
+           CSI_OBJECT_TYPE_MARK)
+       {
+           break;
+       }
+
+       len++;
+    } while (TRUE);
+
+    status = csi_array_new (ctx, len, &obj);
+    if (_csi_unlikely (status))
+       return status;
+
+    if (len != 0) {
+       csi_array_t *array;
+
+       array = obj.datum.array;
+       memcpy (array->stack.objects,
+               _csi_peek_ostack (ctx, len - 1),
+               sizeof (csi_object_t) * len);
+       array->stack.len = len;
+    }
+    ctx->ostack.len -= len + 1;
+
+    return push (&obj);
+}
+
+static csi_status_t
+_alpha (csi_t *ctx)
+{
+    csi_object_t obj;
+    csi_status_t status;
+    double a;
+
+    check (1);
+
+    status = _csi_ostack_get_number (ctx, 0, &a);
+    if (_csi_unlikely (status))
+       return status;
+
+    pop (1);
+
+    obj.type = CSI_OBJECT_TYPE_PATTERN;
+    obj.datum.pattern = cairo_pattern_create_rgba (0, 0, 0, a);
+    return push (&obj);
+}
+
+static csi_status_t
+_add (csi_t *ctx)
+{
+    csi_object_t *A;
+    csi_object_t *B;
+    csi_object_type_t type_a, type_b;
+
+    check (2);
+
+    B = _csi_peek_ostack (ctx, 0);
+    A = _csi_peek_ostack (ctx, 1);
+
+    type_a = csi_object_get_type (A);
+    if (_csi_unlikely (! (type_a == CSI_OBJECT_TYPE_INTEGER ||
+                           type_a == CSI_OBJECT_TYPE_REAL)))
+    {
+       return _csi_error (CSI_STATUS_INVALID_SCRIPT);
+    }
+
+    type_b = csi_object_get_type (B);
+    if (_csi_unlikely (! (type_b == CSI_OBJECT_TYPE_INTEGER ||
+                           type_b == CSI_OBJECT_TYPE_REAL)))
+    {
+       return _csi_error (CSI_STATUS_INVALID_SCRIPT);
+    }
+
+    pop (2);
+
+    if (type_a == CSI_OBJECT_TYPE_REAL &&
+       type_b == CSI_OBJECT_TYPE_REAL)
+    {
+       return _csi_push_ostack_real (ctx, A->datum.real + B->datum.real);
+
+    }
+    else if (type_a == CSI_OBJECT_TYPE_INTEGER &&
+            type_b == CSI_OBJECT_TYPE_INTEGER)
+    {
+       return _csi_push_ostack_integer (ctx,
+                                        A->datum.integer + B->datum.integer);
+    }
+    else
+    {
+       double v;
+
+       if (type_a == CSI_OBJECT_TYPE_REAL)
+           v = A->datum.real;
+       else
+           v = A->datum.integer;
+
+       if (type_b == CSI_OBJECT_TYPE_REAL)
+           v += B->datum.real;
+       else
+           v += B->datum.integer;
+
+       return _csi_push_ostack_real (ctx, v);
+    }
+}
+
+static csi_status_t
+_add_color_stop (csi_t *ctx)
+{
+    csi_status_t status;
+    double offset, r, g, b, a;
+    cairo_pattern_t *pattern = NULL; /* silence the compiler */
+
+    check (6);
+
+    status = _csi_ostack_get_number (ctx, 0, &a);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_number (ctx, 1, &b);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_number (ctx, 2, &g);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_number (ctx, 3, &r);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_number (ctx, 4, &offset);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_pattern (ctx, 5, &pattern);
+    if (_csi_unlikely (status))
+       return status;
+
+    cairo_pattern_add_color_stop_rgba (pattern, offset, r, g, b, a);
+
+    pop (5);
+    return CSI_STATUS_SUCCESS;
+}
+
+static csi_status_t
+_and (csi_t *ctx)
+{
+    csi_object_t *a, *b;
+    int type;
+
+    check (2);
+
+    a = _csi_peek_ostack (ctx, 0);
+    b = _csi_peek_ostack (ctx, 1);
+    if (_csi_unlikely (csi_object_get_type (a) != csi_object_get_type (b)))
+       return _csi_error (CSI_STATUS_INVALID_SCRIPT);
+
+    pop (2);
+    type = csi_object_get_type (a);
+    switch (type) {
+    case CSI_OBJECT_TYPE_INTEGER:
+       return _csi_push_ostack_integer (ctx,
+                                        a->datum.integer & b->datum.integer);
+    case CSI_OBJECT_TYPE_BOOLEAN:
+       return _csi_push_ostack_boolean (ctx,
+                                        a->datum.boolean & b->datum.boolean);
+    default:
+       return _csi_error (CSI_STATUS_INVALID_SCRIPT);
+    }
+}
+
+static csi_status_t
+_arc (csi_t *ctx)
+{
+    csi_status_t status;
+    double x, y, r;
+    double theta1, theta2;
+    cairo_t *cr;
+
+    check (6);
+
+    status = _csi_ostack_get_number (ctx, 0, &theta2);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_number (ctx, 1, &theta1);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_number (ctx, 2, &r);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_number (ctx, 3, &y);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_number (ctx, 4, &x);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_context (ctx, 5, &cr);
+    if (_csi_unlikely (status))
+       return status;
+
+    /* XXX handle path object */
+
+    cairo_arc (cr, x, y, r, theta1, theta2);
+    pop (5);
+    return CSI_STATUS_SUCCESS;
+}
+
+static csi_status_t
+_arc_negative (csi_t *ctx)
+{
+    csi_status_t status;
+    double x, y, r;
+    double theta1, theta2;
+    cairo_t *cr;
+
+    check (6);
+
+    status = _csi_ostack_get_number (ctx, 0, &theta2);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_number (ctx, 1, &theta1);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_number (ctx, 2, &r);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_number (ctx, 3, &y);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_number (ctx, 4, &x);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_context (ctx, 5, &cr);
+    if (_csi_unlikely (status))
+       return status;
+
+    /* XXX handle path object */
+
+    cairo_arc_negative (cr, x, y, r, theta1, theta2);
+    pop (5);
+    return CSI_STATUS_SUCCESS;
+}
+
+static csi_status_t
+_array (csi_t *ctx)
+{
+    csi_object_t obj;
+    csi_status_t status;
+
+    status = csi_array_new (ctx, 0, &obj);
+    if (_csi_unlikely (status))
+       return status;
+
+    return push (&obj);
+}
+
+static csi_status_t
+_bind_substitute (csi_t *ctx, csi_array_t *array)
+{
+    csi_status_t status;
+    csi_integer_t i, n;
+    csi_dictionary_t *dict;
+
+    /* perform operator substitution on the executable array (procedure) */
+    dict = ctx->dstack.objects[0].datum.dictionary;
+    n = array->stack.len;
+    for (i = 0; i < n; i++) {
+       csi_object_t *obj = &array->stack.objects[i];
+
+       if (obj->type == (CSI_OBJECT_TYPE_NAME | CSI_OBJECT_ATTR_EXECUTABLE)) {
+           csi_dictionary_entry_t *entry;
+
+           entry = _csi_hash_table_lookup (&dict->hash_table,
+                                           (csi_hash_entry_t *)
+                                           &obj->datum.name);
+           if (entry != NULL)
+               *obj = entry->value;
+       } else if (csi_object_is_procedure (obj)) {
+           status = _bind_substitute (ctx, obj->datum.array);
+           if (_csi_unlikely (status))
+               return status;
+       }
+    }
+
+    return CSI_STATUS_SUCCESS;
+}
+
+static csi_status_t
+_idiom_substitute (csi_t *ctx, csi_array_t *array)
+{
+#if 0
+    csi_status_t status;
+    csi_integer_t i, j;
+
+    /* XXX substring search, build array once then search for
+     * longest matching idiom, repeat. */
+
+    /* scan the top-most array for sequences we can pre-compile */
+
+    /* now recurse for subroutines */
+    j = array->stack.len;
+    for (i = 0; i < j; i++) {
+       csi_object_t *obj = &array->stack.objects[i];
+
+       if (csi_object_is_procedure (obj)) {
+           status = _idiom_substitute (ctx, obj->datum.array);
+           if (_csi_unlikely (_cairo_is_error (status))
+               return status;
+       }
+    }
+#endif
+
+    return CSI_STATUS_SUCCESS;
+}
+
+static csi_status_t
+_bind (csi_t *ctx)
+{
+    csi_array_t *array;
+    csi_status_t status;
+
+    check (1);
+
+    status = _csi_ostack_get_procedure (ctx, 0, &array);
+    if (_csi_unlikely (status))
+       return status;
+
+    status = _bind_substitute (ctx, array);
+    if (_csi_unlikely (status))
+       return status;
+
+    status = _idiom_substitute (ctx, array);
+    if (_csi_unlikely (status))
+       return status;
+
+    return CSI_STATUS_SUCCESS;
+}
+
+static csi_status_t
+_bitshift (csi_t *ctx)
+{
+    long v, shift;
+    csi_status_t status;
+
+    check (2);
+
+    status = _csi_ostack_get_integer (ctx, 0, &shift);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_integer (ctx, 1, &v);
+    if (_csi_unlikely (status))
+       return status;
+
+    if (shift < 0) {
+       shift = -shift;
+       v >>= shift;
+    } else
+       v <<= shift;
+
+    pop (1);
+    _csi_peek_ostack (ctx, 0)->datum.integer = v;
+
+    return CSI_STATUS_SUCCESS;
+}
+
+static csi_status_t
+_clip (csi_t *ctx)
+{
+    return _do_cairo_op (ctx, cairo_clip);
+}
+
+static csi_status_t
+_clip_preserve (csi_t *ctx)
+{
+    return _do_cairo_op (ctx, cairo_clip_preserve);
+}
+
+static csi_status_t
+_close_path (csi_t *ctx)
+{
+    return _do_cairo_op (ctx, cairo_close_path);
+}
+
+static csi_status_t
+_context (csi_t *ctx)
+{
+    csi_object_t obj;
+    csi_status_t status;
+    cairo_surface_t *surface;
+    cairo_t *cr;
+    csi_context_create_func_t hook;
+    csi_proxy_t *proxy;
+
+    check (1);
+
+    status = _csi_ostack_get_surface (ctx, 0, &surface);
+    if (_csi_unlikely (status))
+       return status;
+
+    hook = ctx->hooks.context_create;
+    if (hook != NULL)
+       cr = hook (ctx->hooks.closure, surface);
+    else
+       cr = cairo_create (surface);
+
+    proxy = _csi_proxy_create (ctx, cr, NULL,
+                              ctx->hooks.context_destroy,
+                              ctx->hooks.closure);
+    if (_csi_unlikely (proxy == NULL)) {
+       cairo_destroy (cr);
+       return _csi_error (CSI_STATUS_NO_MEMORY);
+    }
+
+    status = cairo_set_user_data (cr, &_csi_proxy_key,
+                                 proxy, _csi_proxy_destroy);
+    if (_csi_unlikely (status)) {
+       _csi_proxy_destroy (proxy);
+       cairo_destroy (cr);
+       return status;
+    }
+
+    pop (1);
+    obj.type = CSI_OBJECT_TYPE_CONTEXT;
+    obj.datum.cr = cr;
+    return push (&obj);
+}
+
+static csi_status_t
+_copy (csi_t *ctx)
+{
+    csi_object_t *obj;
+    int type;
+
+    check (1);
+
+    obj = csi_object_reference (_csi_peek_ostack (ctx, 0));
+    pop (1);
+
+    type = csi_object_get_type (obj);
+    switch (type) {
+       /*XXX array, string, dictionary, etc */
+    case CSI_OBJECT_TYPE_INTEGER:
+       {
+           long i, n;
+
+           n = obj->datum.integer;
+           if (_csi_unlikely (n < 0))
+               return _csi_error (CSI_STATUS_INVALID_SCRIPT);
+           check (n);
+
+           for (i = n; i--; ) {
+               csi_status_t status;
+
+               status = _csi_push_ostack_copy (ctx,
+                                               _csi_peek_ostack (ctx, n-1));
+               if (_csi_unlikely (status))
+                   return status;
+           }
+           break;
+       }
+    default:
+       return _csi_error (CSI_STATUS_INVALID_SCRIPT);
+    }
+
+    return CSI_STATUS_SUCCESS;
+}
+
+static csi_status_t
+_copy_page (csi_t *ctx)
+{
+    csi_object_t *obj;
+    int type;
+
+    check (1);
+
+    obj = _csi_peek_ostack (ctx, 0);
+    type = csi_object_get_type (obj);
+    switch (type) {
+    case CSI_OBJECT_TYPE_CONTEXT:
+       cairo_copy_page (obj->datum.cr);
+       if (ctx->hooks.copy_page != NULL)
+           ctx->hooks.copy_page (ctx->hooks.closure, obj->datum.cr);
+       break;
+    case CSI_OBJECT_TYPE_SURFACE:
+       cairo_surface_copy_page (obj->datum.surface);
+       /* XXX hook? */
+       break;
+    default:
+       return _csi_error (CSI_STATUS_INVALID_SCRIPT);
+    }
+
+    return CSI_STATUS_SUCCESS;
+}
+
+static csi_status_t
+_curve_to (csi_t *ctx)
+{
+    csi_status_t status;
+    double x1, y1;
+    double x2, y2;
+    double x3, y3;
+    cairo_t *cr;
+
+    check (7);
+
+    status = _csi_ostack_get_number (ctx, 0, &y3);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_number (ctx, 1, &x3);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_number (ctx, 2, &y2);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_number (ctx, 3, &x2);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_number (ctx, 4, &y1);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_number (ctx, 5, &x1);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_context (ctx, 6, &cr);
+    if (_csi_unlikely (status))
+       return status;
+
+    /* XXX handle path object */
+
+    cairo_curve_to (cr, x1, y1, x2, y2, x3, y3);
+    pop (6);
+    return CSI_STATUS_SUCCESS;
+}
+
+static csi_status_t
+_cvi (csi_t *ctx)
+{
+    csi_object_t *val, obj;
+    int type;
+
+    check (1);
+
+    val = _csi_peek_ostack (ctx, 0);
+    type = csi_object_get_type (val);
+    switch (type) {
+    case CSI_OBJECT_TYPE_INTEGER:
+       return CSI_STATUS_SUCCESS;
+
+    case CSI_OBJECT_TYPE_REAL:
+       pop (1);
+       return _csi_push_ostack_integer (ctx, val->datum.real);
+
+    case CSI_OBJECT_TYPE_STRING:
+       if (! _csi_parse_number (&obj,
+                                val->datum.string->string,
+                                val->datum.string->len))
+       {
+           return _csi_error (CSI_STATUS_INVALID_SCRIPT);
+       }
+
+       pop (1);
+       if (csi_object_get_type (&obj) == CSI_OBJECT_TYPE_INTEGER)
+           return push (&obj);
+       else
+           return _csi_push_ostack_integer (ctx, obj.datum.real);
+
+    default:
+       return _csi_error (CSI_STATUS_INVALID_SCRIPT);
+    }
+}
+
+static csi_status_t
+_cvr (csi_t *ctx)
+{
+    csi_object_t *val, obj;
+    int type;
+
+    check (1);
+
+    val = _csi_peek_ostack (ctx, 0);
+    type = csi_object_get_type (val);
+    switch (type) {
+    case CSI_OBJECT_TYPE_REAL:
+       return CSI_STATUS_SUCCESS;
+
+    case CSI_OBJECT_TYPE_INTEGER:
+       pop (1);
+       return _csi_push_ostack_real (ctx, val->datum.integer);
+
+    case CSI_OBJECT_TYPE_STRING:
+       if (! _csi_parse_number (&obj,
+                                val->datum.string->string,
+                                val->datum.string->len))
+       {
+           return _csi_error (CSI_STATUS_INVALID_SCRIPT);
+       }
+
+       pop (1);
+       if (csi_object_get_type (&obj) == CSI_OBJECT_TYPE_REAL)
+           return push (&obj);
+       else
+           return _csi_push_ostack_real (ctx, obj.datum.integer);
+
+    default:
+       return _csi_error (CSI_STATUS_INVALID_SCRIPT);
+    }
+}
+
+static csi_status_t
+_def (csi_t *ctx)
+{
+    csi_name_t name = 0; /* silence the compiler */
+    csi_status_t status;
+
+    check (2);
+
+    status = _csi_ostack_get_name (ctx, 1, &name);
+    if (_csi_unlikely (status))
+       return status;
+
+    status = _csi_name_define (ctx, name, _csi_peek_ostack (ctx, 0));
+    if (_csi_unlikely (status))
+       return status;
+
+    pop (2);
+    return CSI_STATUS_SUCCESS;
+}
+
+static csi_status_t
+_dict (csi_t *ctx)
+{
+    csi_object_t obj;
+    csi_status_t status;
+
+    status = csi_dictionary_new (ctx, &obj);
+    if (_csi_unlikely (status))
+       return status;
+
+    return push (&obj);
+}
+
+static csi_status_t
+_div (csi_t *ctx)
+{
+    csi_object_t *A;
+    csi_object_t *B;
+    csi_object_type_t type_a, type_b;
+
+    check (2);
+
+    B = _csi_peek_ostack (ctx, 0);
+    A = _csi_peek_ostack (ctx, 1);
+
+    type_a = csi_object_get_type (A);
+    if (_csi_unlikely (! (type_a == CSI_OBJECT_TYPE_INTEGER ||
+                           type_a == CSI_OBJECT_TYPE_REAL)))
+    {
+       return _csi_error (CSI_STATUS_INVALID_SCRIPT);
+    }
+
+    type_b = csi_object_get_type (B);
+    if (_csi_unlikely (! (type_b == CSI_OBJECT_TYPE_INTEGER ||
+                           type_b == CSI_OBJECT_TYPE_REAL)))
+    {
+       return _csi_error (CSI_STATUS_INVALID_SCRIPT);
+    }
+
+    pop (2);
+
+    if (type_a == CSI_OBJECT_TYPE_REAL &&
+       type_b == CSI_OBJECT_TYPE_REAL)
+    {
+       return _csi_push_ostack_real (ctx, A->datum.real / B->datum.real);
+
+    }
+    else if (type_a == CSI_OBJECT_TYPE_INTEGER &&
+            type_b == CSI_OBJECT_TYPE_INTEGER)
+    {
+       return _csi_push_ostack_integer (ctx,
+                                        A->datum.integer / B->datum.integer);
+    }
+    else
+    {
+       double v;
+
+       if (type_a == CSI_OBJECT_TYPE_REAL)
+           v = A->datum.real;
+       else
+           v = A->datum.integer;
+
+       if (type_b == CSI_OBJECT_TYPE_REAL)
+           v /= B->datum.real;
+       else
+           v /= B->datum.integer;
+
+       return _csi_push_ostack_real (ctx, v);
+    }
+}
+
+static csi_status_t
+_duplicate (csi_t *ctx)
+{
+    check (1);
+
+    return _csi_push_ostack_copy (ctx, _csi_peek_ostack (ctx, 0));
+}
+
+static csi_status_t
+_eq (csi_t *ctx)
+{
+    csi_object_t *a, *b;
+    csi_boolean_t v;
+
+    check (2);
+
+    b = _csi_peek_ostack (ctx, 0);
+    a = _csi_peek_ostack (ctx, 1);
+
+    v = csi_object_eq (a, b);
+
+    pop (2);
+    return _csi_push_ostack_boolean (ctx, v);
+}
+
+static csi_status_t
+_exch (csi_t *ctx)
+{
+    return _csi_stack_exch (&ctx->ostack);
+}
+
+static csi_status_t
+_false (csi_t *ctx)
+{
+    return _csi_push_ostack_boolean (ctx, FALSE);
+}
+
+static csi_status_t
+_fill (csi_t *ctx)
+{
+    return _do_cairo_op (ctx, cairo_fill);
+}
+
+static csi_status_t
+_fill_preserve (csi_t *ctx)
+{
+    return _do_cairo_op (ctx, cairo_fill_preserve);
+}
+
+static csi_status_t
+_filter (csi_t *ctx)
+{
+    csi_object_t *src;
+    csi_dictionary_t *dict = NULL;
+    csi_status_t status;
+    const char *name = NULL; /* silence the compiler */
+    const struct filters {
+       const char *name;
+       csi_status_t (*constructor) (csi_t *t,
+                                      csi_object_t *,
+                                      csi_dictionary_t *,
+                                      csi_object_t *);
+    } filters[] = {
+       { "ascii85", csi_file_new_ascii85_decode },
+#if HAVE_ZLIB
+       { "deflate", csi_file_new_deflate_decode },
+#endif
+#if 0
+       { "lzw", csi_file_new_lzw_decode },
+#endif
+       { NULL, NULL }
+    }, *filter;
+    int cnt;
+
+    check (2);
+
+    status = _csi_ostack_get_string_constant (ctx, 0, &name);
+    if (_csi_unlikely (status))
+       return status;
+
+    src = _csi_peek_ostack (ctx, 1);
+    cnt = 2;
+    if (csi_object_get_type (src) == CSI_OBJECT_TYPE_DICTIONARY) {
+       dict = src->datum.dictionary;
+
+       check (3);
+
+       src = _csi_peek_ostack (ctx, 2);
+       cnt = 3;
+    }
+
+    for (filter = filters; filter->name != NULL; filter++) {
+       if (strcmp (name, filter->name) == 0) {
+           csi_object_t file;
+
+           status = filter->constructor (ctx, &file, dict, src);
+           if (_csi_unlikely (status))
+               return status;
+
+           pop (cnt);
+           return push (&file);
+       }
+    }
+
+    return _csi_error (CSI_STATUS_INVALID_SCRIPT);
+}
+
+static cairo_status_t
+_type3_init (cairo_scaled_font_t *scaled_font,
+            cairo_t *cr,
+            cairo_font_extents_t *metrics)
+{
+    cairo_font_face_t *face;
+    csi_proxy_t *proxy;
+    csi_t *ctx;
+    csi_dictionary_t *font;
+    csi_object_t key;
+    csi_object_t obj;
+    csi_array_t *array;
+    csi_status_t status;
+
+    face = cairo_scaled_font_get_font_face (scaled_font);
+    proxy = cairo_font_face_get_user_data (face, &_csi_proxy_key);
+    if (_csi_unlikely (proxy == NULL))
+       return CAIRO_STATUS_NO_MEMORY;
+
+    ctx = proxy->ctx;
+    font = proxy->dictionary;
+
+    status = csi_name_new_static (ctx, &key, "metrics");
+    if (_csi_unlikely (status))
+       return CAIRO_STATUS_NO_MEMORY;
+
+    if (! csi_dictionary_has (font, key.datum.name))
+       return CAIRO_STATUS_SUCCESS;
+
+    status = csi_dictionary_get (ctx, font, key.datum.name, &obj);
+    if (_csi_unlikely (status))
+       return status;
+
+    if (csi_object_get_type (&obj) != CSI_OBJECT_TYPE_ARRAY)
+       return CAIRO_STATUS_USER_FONT_ERROR;
+
+    array = obj.datum.array;
+    if (array->stack.len != 5)
+       return CAIRO_STATUS_USER_FONT_ERROR;
+
+    metrics->ascent  = csi_number_get_value (&array->stack.objects[0]);
+    metrics->descent = csi_number_get_value (&array->stack.objects[1]);
+    metrics->height  = csi_number_get_value (&array->stack.objects[2]);
+    metrics->max_x_advance = csi_number_get_value (&array->stack.objects[3]);
+    metrics->max_y_advance = csi_number_get_value (&array->stack.objects[4]);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_type3_lookup (cairo_scaled_font_t *scaled_font,
+              unsigned long unicode,
+              unsigned long *glyph)
+{
+    cairo_font_face_t *face;
+    csi_proxy_t *proxy;
+    csi_t *ctx;
+    csi_dictionary_t *font;
+    csi_object_t obj, key;
+    csi_array_t *array;
+    char buf[12];
+    csi_integer_t i;
+    cairo_status_t status;
+
+    face = cairo_scaled_font_get_font_face (scaled_font);
+    proxy = cairo_font_face_get_user_data (face, &_csi_proxy_key);
+    if (_csi_unlikely (proxy == NULL))
+       return CAIRO_STATUS_USER_FONT_ERROR;
+
+    ctx = proxy->ctx;
+    font = proxy->dictionary;
+
+    status = csi_name_new_static (ctx, &key, "encoding");
+    if (_csi_unlikely (status))
+       return CAIRO_STATUS_USER_FONT_ERROR;
+
+    if (! csi_dictionary_has (font, key.datum.name)) {
+       *glyph = unicode;
+       return CAIRO_STATUS_SUCCESS;
+    }
+
+    status = csi_dictionary_get (ctx, font, key.datum.name, &obj);
+    if (_csi_unlikely (status))
+       return CAIRO_STATUS_USER_FONT_ERROR;
+
+    if (_csi_unlikely (csi_object_get_type (&obj) != CSI_OBJECT_TYPE_ARRAY))
+       return CAIRO_STATUS_USER_FONT_ERROR;
+
+    snprintf (buf, sizeof (buf), "uni%04lu", unicode);
+    array = obj.datum.array;
+    for (i = 0; i < array->stack.len; i++) {
+       csi_object_t *name;
+
+       name = &array->stack.objects[i];
+       if (csi_object_get_type (name) != CSI_OBJECT_TYPE_NAME)
+           continue;
+
+       if (strcmp ((char *) name->datum.name, buf) == 0) {
+           *glyph = i;
+           return CAIRO_STATUS_SUCCESS;
+       }
+    }
+
+    return CAIRO_STATUS_USER_FONT_ERROR;
+}
+
+static cairo_status_t
+_type3_render (cairo_scaled_font_t *scaled_font,
+              unsigned long glyph_index,
+              cairo_t *cr,
+              cairo_text_extents_t *metrics)
+{
+    cairo_font_face_t *face;
+    csi_proxy_t *proxy;
+    csi_t *ctx;
+    csi_dictionary_t *font;
+    csi_array_t *glyphs;
+    csi_object_t *glyph;
+    csi_object_t key;
+    csi_object_t obj;
+    csi_object_t render;
+    csi_status_t status;
+
+    face = cairo_scaled_font_get_font_face (scaled_font);
+    proxy = cairo_font_face_get_user_data (face, &_csi_proxy_key);
+    if (_csi_unlikely (proxy == NULL))
+       return CAIRO_STATUS_USER_FONT_ERROR;
+
+    ctx = proxy->ctx;
+    font = proxy->dictionary;
+
+    status = csi_name_new_static (ctx, &key, "glyphs");
+    if (_csi_unlikely (status))
+       return CAIRO_STATUS_USER_FONT_ERROR;
+
+    status = csi_dictionary_get (ctx, font, key.datum.name, &obj);
+    if (_csi_unlikely (status))
+       return CAIRO_STATUS_USER_FONT_ERROR;
+
+    if (_csi_unlikely (csi_object_get_type (&obj) != CSI_OBJECT_TYPE_ARRAY))
+       return CAIRO_STATUS_USER_FONT_ERROR;
+
+    glyphs = obj.datum.array;
+    glyph = &glyphs->stack.objects[glyph_index];
+    if (csi_object_get_type (glyph) == CSI_OBJECT_TYPE_NULL)
+       return CAIRO_STATUS_SUCCESS; /* .notdef */
+
+    if (_csi_unlikely (csi_object_get_type (glyph) != CSI_OBJECT_TYPE_DICTIONARY))
+       return CAIRO_STATUS_USER_FONT_ERROR;
+
+    status = csi_name_new_static (ctx, &key, "metrics");
+    if (_csi_unlikely (status))
+       return CAIRO_STATUS_USER_FONT_ERROR;
+
+    font = glyph->datum.dictionary;
+    if (csi_dictionary_has (font, key.datum.name)) {
+       csi_array_t *array;
+
+       status = csi_dictionary_get (ctx, font, key.datum.name, &obj);
+       if (_csi_unlikely (status))
+           return CAIRO_STATUS_USER_FONT_ERROR;
+
+       if (_csi_unlikely (csi_object_get_type (&obj) !=
+                            CSI_OBJECT_TYPE_ARRAY))
+           return CAIRO_STATUS_USER_FONT_ERROR;
+
+       array = obj.datum.array;
+       if (_csi_unlikely (array->stack.len != 6))
+           return CAIRO_STATUS_USER_FONT_ERROR;
+
+       metrics->x_bearing = csi_number_get_value (&array->stack.objects[0]);
+       metrics->y_bearing = csi_number_get_value (&array->stack.objects[1]);
+       metrics->width = csi_number_get_value (&array->stack.objects[2]);
+       metrics->height = csi_number_get_value (&array->stack.objects[3]);
+       metrics->x_advance = csi_number_get_value (&array->stack.objects[4]);
+       metrics->y_advance = csi_number_get_value (&array->stack.objects[5]);
+    }
+
+    status = csi_name_new_static (ctx, &key, "render");
+    if (_csi_unlikely (status))
+       return CAIRO_STATUS_USER_FONT_ERROR;
+
+    status = csi_dictionary_get (ctx, font, key.datum.name, &render);
+    if (_csi_unlikely (status))
+       return CAIRO_STATUS_USER_FONT_ERROR;
+
+    if (_csi_unlikely (! csi_object_is_procedure (&render)))
+       return CAIRO_STATUS_USER_FONT_ERROR;
+
+    obj.type = CSI_OBJECT_TYPE_CONTEXT;
+    obj.datum.cr = cairo_reference (cr);
+    status = push (&obj);
+    if (_csi_unlikely (status)) {
+       cairo_destroy (cr);
+       return CAIRO_STATUS_USER_FONT_ERROR;
+    }
+
+    status = csi_object_execute (ctx, &render);
+    pop (1);
+    return status ? CAIRO_STATUS_USER_FONT_ERROR : CAIRO_STATUS_SUCCESS;
+}
+
+static csi_status_t
+_font_type3 (csi_t *ctx,
+            csi_dictionary_t *font,
+            cairo_font_face_t **font_face_out)
+{
+    cairo_font_face_t *font_face;
+
+    font_face = cairo_user_font_face_create ();
+    cairo_user_font_face_set_init_func (font_face, _type3_init);
+    cairo_user_font_face_set_unicode_to_glyph_func (font_face, _type3_lookup);
+    cairo_user_font_face_set_render_glyph_func (font_face, _type3_render);
+
+    *font_face_out = font_face;
+    return CSI_STATUS_SUCCESS;
+}
+
+#if CAIRO_HAS_FT_FONT
+#include <cairo-ft.h>
+#include <ft2build.h>
+#include FT_FREETYPE_H
+
+static FT_Library _ft_lib;
+
+struct _ft_face_data {
+    csi_t *ctx;
+    csi_blob_t blob;
+    FT_Face face;
+    csi_string_t *source;
+    void *bytes;
+    cairo_font_face_t *font_face;
+};
+
+static void
+_ft_done_face (void *closure)
+{
+    struct _ft_face_data *data = closure;
+    csi_t *ctx;
+
+    ctx = data->ctx;
+
+    if (data->face != NULL)
+       FT_Done_Face (data->face);
+
+    ctx->_faces = _csi_list_unlink (ctx->_faces, &data->blob.list);
+
+    if (data->source != NULL) {
+       if (--data->source->base.ref == 0)
+           csi_string_free (ctx, data->source);
+    } else {
+#ifdef HAVE_MMAP
+       munmap (data->blob.bytes, data->blob.len);
+#endif
+    }
+
+    if (data->bytes != NULL)
+       _csi_free (ctx, data->bytes);
+
+    _csi_slab_free (ctx, data, sizeof (*data));
+
+    cairo_script_interpreter_destroy (ctx);
+}
+
+struct mmap_vec {
+    const uint8_t *bytes;
+    size_t num_bytes;
+};
+
+#ifdef HAVE_MMAP
+/* manual form of swapping for swapless systems like tiny */
+static void *
+_mmap_bytes (const struct mmap_vec *vec, int count)
+{
+    char template[] = "/tmp/csi-font.XXXXXX";
+    void *ptr;
+    int fd;
+    int num_bytes;
+
+    fd = mkstemp (template);
+    if (fd == -1)
+       return MAP_FAILED;
+
+    unlink (template);
+    num_bytes = 0;
+    while (count--) {
+       const uint8_t *bytes = vec->bytes;
+       size_t len = vec->num_bytes;
+       while (len) {
+           int ret = write (fd, bytes, len);
+           if (ret < 0) {
+               close (fd);
+               return MAP_FAILED;
+           }
+           len -= ret;
+           bytes += ret;
+       }
+
+       num_bytes += vec->num_bytes;
+       vec++;
+    }
+
+    ptr = mmap (NULL, num_bytes, PROT_READ, MAP_SHARED, fd, 0);
+    close (fd);
+
+    return ptr;
+}
+#endif
+
+static void *
+inflate_string (csi_t *ctx, csi_string_t *src)
+{
+    uLongf len;
+    uint8_t *bytes;
+
+    len = src->deflate;
+    bytes = _csi_alloc (ctx, len + 1);
+    if (bytes == NULL)
+       return NULL;
+
+    if (uncompress ((Bytef *) bytes, &len,
+                   (Bytef *) src->string, src->len) != Z_OK)
+    {
+       _csi_free (ctx, bytes);
+       bytes = NULL;
+    }
+    else
+    {
+       bytes[len] = '\0';
+    }
+
+    return bytes;
+}
+
+static csi_status_t
+_ft_create_for_source (csi_t *ctx,
+                      csi_string_t *source,
+                      int index, int load_flags,
+                      cairo_font_face_t **font_face_out)
+{
+    csi_blob_t tmpl;
+    struct _ft_face_data *data;
+    csi_list_t *link;
+    FT_Error err;
+    cairo_font_face_t *font_face;
+    csi_status_t status;
+    struct mmap_vec vec[2];
+    int vec_count;
+    void *bytes;
+    int len;
+
+    /* check for an existing FT_Face (kept alive by the font cache) */
+    /* XXX index/flags */
+    _csi_blob_init (&tmpl, (uint8_t *) source->string, source->len);
+    _csi_blob_hash (&tmpl, (uint32_t *) source->string, source->len / sizeof (uint32_t));
+    link = _csi_list_find (ctx->_faces, _csi_blob_equal, &tmpl);
+    if (link) {
+       if (--source->base.ref == 0)
+           csi_string_free (ctx, source);
+       data = csi_container_of (link, struct _ft_face_data, blob.list);
+       *font_face_out = cairo_font_face_reference (data->font_face);
+       return CSI_STATUS_SUCCESS;
+    }
+
+    /* no existing font_face, create new FT_Face */
+    if (_ft_lib == NULL) {
+       err = FT_Init_FreeType (&_ft_lib);
+       if (_csi_unlikely (err != FT_Err_Ok))
+           return _csi_error (CSI_STATUS_NO_MEMORY);
+    }
+
+    data = _csi_slab_alloc (ctx, sizeof (*data));
+    data->bytes = NULL;
+    data->source = source;
+
+    vec[0].bytes = tmpl.bytes;
+    vec[0].num_bytes = tmpl.len;
+
+    if (source->deflate) {
+       len = source->deflate;
+       bytes = inflate_string (ctx, source);
+       if (_csi_unlikely (bytes == NULL))
+           return _csi_error (CSI_STATUS_NO_MEMORY);
+
+       vec[1].bytes = bytes;
+       vec[1].num_bytes = len;
+       data->bytes = bytes;
+       vec_count = 2;
+    } else {
+       bytes = tmpl.bytes;
+       len = tmpl.len;
+       vec_count = 1;
+    }
+
+    data->face = NULL;
+    ctx->_faces = _csi_list_prepend (ctx->_faces, &data->blob.list);
+    data->ctx = cairo_script_interpreter_reference (ctx);
+    data->blob.hash = tmpl.hash;
+    data->blob.len = tmpl.len;
+#ifdef HAVE_MMAP
+    data->blob.bytes = _mmap_bytes (vec, vec_count);
+    if (data->blob.bytes != MAP_FAILED) {
+       if (--source->base.ref == 0)
+           csi_string_free (ctx, source);
+
+       if (source->deflate) {
+           _csi_free (ctx, bytes);
+           bytes = data->blob.bytes + vec[0].num_bytes;
+       } else
+           bytes = data->blob.bytes;
+
+       data->source = NULL;
+       data->bytes = NULL;
+    } else {
+       data->blob.bytes = tmpl.bytes;
+    }
+#else
+    data->blob.bytes = tmpl.bytes;
+#endif
+
+    err = FT_New_Memory_Face (_ft_lib,
+                             bytes, len,
+                             index,
+                             &data->face);
+    if (_csi_unlikely (err != FT_Err_Ok)) {
+       _ft_done_face (data);
+
+       if (err == FT_Err_Out_Of_Memory)
+           return _csi_error (CSI_STATUS_NO_MEMORY);
+
+       return _csi_error (CSI_STATUS_INVALID_SCRIPT);
+    }
+
+    font_face = cairo_ft_font_face_create_for_ft_face (data->face, load_flags);
+    status = cairo_font_face_set_user_data (font_face,
+                                           &_csi_blob_key,
+                                           data, _ft_done_face);
+    if (_csi_unlikely (status)) {
+       _ft_done_face (data);
+       cairo_font_face_destroy (font_face);
+       return status;
+    }
+
+    data->font_face = font_face;
+    *font_face_out = font_face;
+    return CSI_STATUS_SUCCESS;
+}
+
+static csi_status_t
+_ft_create_for_pattern (csi_t *ctx,
+                       csi_string_t *string,
+                       cairo_font_face_t **font_face_out)
+{
+#if CAIRO_HAS_FC_FONT
+    csi_blob_t tmpl;
+    struct _ft_face_data *data;
+    csi_list_t *link;
+    cairo_font_face_t *font_face;
+    FcPattern *pattern, *resolved;
+    csi_status_t status;
+    struct mmap_vec vec;
+    void *bytes;
+
+    _csi_blob_init (&tmpl, (uint8_t *) string->string, string->len);
+    _csi_blob_hash (&tmpl, (uint32_t *) string->string, string->len / sizeof (uint32_t));
+    link = _csi_list_find (ctx->_faces, _csi_blob_equal, &tmpl);
+    if (link) {
+       if (--string->base.ref == 0)
+           csi_string_free (ctx, string);
+       data = csi_container_of (link, struct _ft_face_data, blob.list);
+       *font_face_out = cairo_font_face_reference (data->font_face);
+       return CSI_STATUS_SUCCESS;
+    }
+
+    if (string->deflate) {
+       bytes = inflate_string (ctx, string);
+       if (_csi_unlikely (bytes == NULL))
+           return _csi_error (CSI_STATUS_NO_MEMORY);
+    } else {
+       bytes = tmpl.bytes;
+    }
+
+    pattern = FcNameParse (bytes);
+    if (bytes != tmpl.bytes)
+       _csi_free (ctx, bytes);
+
+    resolved = pattern;
+    if (cairo_version () < CAIRO_VERSION_ENCODE (1, 9, 0)) {
+       /* prior to 1.9, you needed to pass a resolved pattern */
+       resolved = FcFontMatch (NULL, pattern, NULL);
+       if (_csi_unlikely (resolved == NULL)) {
+           FcPatternDestroy (pattern);
+           return _csi_error (CSI_STATUS_NO_MEMORY);
+       }
+    }
+
+    font_face = cairo_ft_font_face_create_for_pattern (resolved);
+    FcPatternDestroy (resolved);
+    if (resolved != pattern)
+       FcPatternDestroy (pattern);
+
+    data = _csi_slab_alloc (ctx, sizeof (*data));
+    ctx->_faces = _csi_list_prepend (ctx->_faces, &data->blob.list);
+    data->ctx = cairo_script_interpreter_reference (ctx);
+    data->blob.hash = tmpl.hash;
+    data->blob.len = tmpl.len;
+    data->bytes = NULL;
+    data->face = NULL;
+#ifdef HAVE_MMAP
+    vec.bytes = tmpl.bytes;
+    vec.num_bytes = tmpl.len;
+    data->blob.bytes = _mmap_bytes (&vec, 1);
+    if (data->blob.bytes != MAP_FAILED) {
+       data->source = NULL;
+       if (--string->base.ref == 0)
+           csi_string_free (ctx, string);
+    } else {
+       data->blob.bytes = tmpl.bytes;
+       data->source = string;
+    }
+#else
+    data->blob.bytes = tmpl.bytes;
+    data->source = string;
+#endif
+
+    status = cairo_font_face_set_user_data (font_face,
+                                           &_csi_blob_key,
+                                           data, _ft_done_face);
+    if (_csi_unlikely (status)) {
+       _ft_done_face (data);
+       cairo_font_face_destroy (font_face);
+       return status;
+    }
+
+    data->font_face = font_face;
+    *font_face_out = font_face;
+    return CSI_STATUS_SUCCESS;
+#else
+    if (--string->base.ref == 0)
+       csi_string_free (ctx, string);
+    return CSI_INT_STATUS_UNSUPPORTED;
+#endif
+}
+
+static csi_status_t
+_ft_type42_create (csi_t *ctx,
+                  csi_dictionary_t *font,
+                  cairo_font_face_t **font_face_out)
+{
+    csi_object_t key;
+    csi_status_t status;
+
+    /* two basic sub-types, either an FcPattern or embedded font */
+    status = csi_name_new_static (ctx, &key, "pattern");
+    if (_csi_unlikely (status))
+       return status;
+
+    if (csi_dictionary_has (font, key.datum.name)) {
+       csi_object_t obj;
+       int type;
+
+       status = csi_dictionary_get (ctx, font, key.datum.name, &obj);
+       if (_csi_unlikely (status))
+           return status;
+
+       type = csi_object_get_type (&obj);
+       switch (type) {
+       case CSI_OBJECT_TYPE_FILE:
+           status = _csi_file_as_string (ctx, obj.datum.file, &obj);
+           if (_csi_unlikely (status))
+               return status;
+           break;
+       case CSI_OBJECT_TYPE_STRING:
+           obj.datum.object->ref++;
+           break;
+       default:
+           return  _csi_error (CSI_STATUS_INVALID_SCRIPT);
+       }
+
+       return _ft_create_for_pattern (ctx,
+                                      obj.datum.string,
+                                      font_face_out);
+    }
+
+    status = csi_name_new_static (ctx, &key, "source");
+    if (_csi_unlikely (status))
+       return status;
+
+    if (csi_dictionary_has (font, key.datum.name)) {
+       csi_object_t obj;
+       long index, flags;
+       int type;
+
+       index = 0;
+       status = _csi_dictionary_get_integer (ctx, font, "index", TRUE, &index);
+       if (_csi_unlikely (status))
+           return status;
+
+       flags = 0;
+       status = _csi_dictionary_get_integer (ctx, font, "flags", TRUE, &flags);
+       if (_csi_unlikely (status))
+           return status;
+
+       status = csi_name_new_static (ctx, &key, "source");
+       if (_csi_unlikely (status))
+           return status;
+       status = csi_dictionary_get (ctx, font, key.datum.name, &obj);
+       if (_csi_unlikely (status))
+           return status;
+       type = csi_object_get_type (&obj);
+       switch (type) {
+       case CSI_OBJECT_TYPE_FILE:
+           status = _csi_file_as_string (ctx, obj.datum.file, &obj);
+           if (_csi_unlikely (status))
+               return status;
+           break;
+       case CSI_OBJECT_TYPE_STRING:
+           obj.datum.object->ref++;
+           break;
+       default:
+           return _csi_error (CSI_STATUS_INVALID_SCRIPT);
+       }
+
+       return _ft_create_for_source (ctx, obj.datum.string,
+                                     index, flags,
+                                     font_face_out);
+    }
+
+    return _csi_error (CSI_STATUS_INVALID_SCRIPT);
+}
+#else
+#define _ft_type42_create(ctx, font, face_out) CSI_INT_STATUS_UNSUPPORTED
+#endif
+
+static char *
+_fc_strcpy (csi_t *ctx, const char *str)
+{
+    char *ret;
+    int len;
+
+    ret = strchr (str, ':');
+    if (ret != NULL)
+       len = ret - str;
+    else
+       len = strlen (str);
+
+    ret = _csi_alloc (ctx, len+1);
+    if (_csi_unlikely (ret == NULL))
+       return NULL;
+
+    memcpy (ret, str, len);
+    ret[len] = '\0';
+
+    return ret;
+}
+
+static cairo_font_face_t *
+_select_font (const char *name)
+{
+    cairo_surface_t *surface;
+    cairo_font_face_t *face;
+    cairo_t *cr;
+
+    /* create a dummy context to choose a font */
+    surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 0, 0);
+    cr = cairo_create (surface);
+    cairo_surface_destroy (surface);
+
+    cairo_select_font_face (cr, name,
+                           CAIRO_FONT_SLANT_NORMAL,
+                           CAIRO_FONT_WEIGHT_NORMAL);
+    face = cairo_font_face_reference (cairo_get_font_face (cr));
+    cairo_destroy (cr);
+
+    return face;
+}
+
+static csi_status_t
+_ft_fallback_create_for_pattern (csi_t *ctx,
+                                csi_string_t *string,
+                                cairo_font_face_t **font_face_out)
+{
+    char *str, *name;
+
+    str = string->string;
+#if 0
+    name = strstr (str, "fullname=");
+    if (name != NULL)
+       str = name + 9;
+#endif
+
+    name = _fc_strcpy (ctx, str);
+    if (_csi_unlikely (name == NULL))
+       return _csi_error (CSI_STATUS_NO_MEMORY);
+
+    *font_face_out = _select_font (name);
+    _csi_free (ctx, name);
+
+    return CSI_STATUS_SUCCESS;
+}
+
+static csi_status_t
+_ft_type42_fallback_create (csi_t *ctx,
+                           csi_dictionary_t *font,
+                           cairo_font_face_t **font_face_out)
+{
+    csi_object_t key;
+    csi_status_t status;
+
+    /* attempt to select a similar font */
+
+    /* two basic sub-types, either an FcPattern or embedded font */
+    status = csi_name_new_static (ctx, &key, "pattern");
+    if (_csi_unlikely (status))
+       return status;
+
+    if (csi_dictionary_has (font, key.datum.name)) {
+       csi_object_t obj;
+       int type;
+
+       status = csi_dictionary_get (ctx, font, key.datum.name, &obj);
+       if (_csi_unlikely (status))
+           return status;
+
+       type = csi_object_get_type (&obj);
+       switch (type) {
+       case CSI_OBJECT_TYPE_FILE:
+           status = _csi_file_as_string (ctx, obj.datum.file, &obj);
+           if (_csi_unlikely (status))
+               return status;
+           break;
+       case CSI_OBJECT_TYPE_STRING:
+           obj.datum.object->ref++;
+           break;
+       default:
+           return  _csi_error (CSI_STATUS_INVALID_SCRIPT);
+       }
+
+       return _ft_fallback_create_for_pattern (ctx,
+                                               obj.datum.string,
+                                               font_face_out);
+    }
+
+    /* XXX: enable the trace to run */
+    *font_face_out = _select_font ("Sans");
+    return CSI_STATUS_SUCCESS;
+}
+
+static csi_status_t
+_font_type42 (csi_t *ctx, csi_dictionary_t *font, cairo_font_face_t **font_face)
+{
+    csi_status_t status;
+
+    status = _ft_type42_create (ctx, font, font_face);
+    if (_csi_likely (status != CSI_INT_STATUS_UNSUPPORTED))
+       return status;
+
+    return _ft_type42_fallback_create (ctx, font, font_face);
+}
+
+static csi_status_t
+_font (csi_t *ctx)
+{
+    csi_dictionary_t *font;
+    csi_status_t status;
+    cairo_font_face_t *font_face = NULL; /* silence the compiler */
+    csi_proxy_t *proxy;
+    csi_object_t obj;
+    long type;
+
+    check (1);
+
+    status = _csi_ostack_get_dictionary (ctx, 0, &font);
+    if (_csi_unlikely (status))
+       return status;
+
+    status = _csi_dictionary_get_integer (ctx, font, "type", FALSE, &type);
+    if (_csi_unlikely (status))
+       return status;
+
+    switch (type) {
+    case 3:
+       status = _font_type3 (ctx, font, &font_face);
+       break;
+    case 42:
+       status = _font_type42 (ctx, font, &font_face);
+       break;
+    default:
+       status = _csi_error (CSI_STATUS_INVALID_SCRIPT);
+       break;
+    }
+
+    if (_csi_unlikely (status))
+       return status;
+
+    /* transfer ownership of dictionary to cairo_font_face_t */
+    proxy = _csi_proxy_create (ctx, font_face, font, NULL, NULL);
+    if (_csi_unlikely (proxy == NULL)) {
+       cairo_font_face_destroy (font_face);
+       return _csi_error (CSI_STATUS_NO_MEMORY);
+    }
+
+    status = cairo_font_face_set_user_data (font_face,
+                                           &_csi_proxy_key,
+                                           proxy, _csi_proxy_destroy);
+    if (_csi_unlikely (status)) {
+       _csi_proxy_destroy (proxy);
+       cairo_font_face_destroy (font_face);
+       return status;
+    }
+
+    obj.type = CSI_OBJECT_TYPE_FONT;
+    obj.datum.font_face = font_face;
+
+    pop (1);
+    status = push (&obj);
+    if (_csi_unlikely (status)) {
+       cairo_font_face_destroy (font_face);
+       return status;
+    }
+
+    return CSI_STATUS_SUCCESS;
+}
+
+static csi_status_t
+_for (csi_t *ctx)
+{
+    csi_array_t *proc;
+    csi_status_t status;
+    long i, inc, limit;
+
+    check (4);
+
+    status = _csi_ostack_get_procedure (ctx, 0, &proc);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_integer (ctx, 1, &limit);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_integer (ctx, 2, &inc);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_integer (ctx, 3, &i);
+    if (_csi_unlikely (status))
+       return status;
+
+    proc->base.ref++;
+    pop (4);
+
+    for (; i <= limit; i += inc) {
+       status = _csi_push_ostack_integer (ctx, i);
+       if (_csi_unlikely (status))
+           break;
+
+       status = _csi_array_execute (ctx, proc);
+       if (_csi_unlikely (status))
+           break;
+    }
+
+    if (--proc->base.ref == 0)
+       csi_array_free (ctx, proc);
+    return status;
+}
+
+static csi_status_t
+_ge (csi_t *ctx)
+{
+    csi_status_t status;
+    csi_object_t *a, *b;
+    int cmp;
+
+    check (2);
+
+    b = _csi_peek_ostack (ctx, 0);
+    a = _csi_peek_ostack (ctx, 1);
+
+    status = csi_object_compare (a, b, &cmp);
+    if (_csi_unlikely (status))
+       return status;
+
+    pop (2);
+    return _csi_push_ostack_boolean (ctx, cmp >= 0);
+}
+
+static csi_status_t
+_proxy_get (csi_proxy_t *proxy,
+           csi_name_t key)
+{
+    csi_object_t obj;
+    csi_status_t status;
+
+    if (_csi_unlikely (proxy == NULL || proxy->dictionary == NULL))
+       return _csi_error (CSI_STATUS_INVALID_SCRIPT);
+
+    status = csi_dictionary_get (proxy->ctx, proxy->dictionary, key, &obj);
+    if (_csi_unlikely (status))
+       return status;
+
+    return _csi_push_ostack_copy (proxy->ctx, &obj);
+}
+
+static csi_status_t
+_context_get (csi_t *ctx,
+             cairo_t *cr,
+             csi_name_t key)
+{
+    csi_status_t status;
+    csi_object_t obj;
+
+    if (strcmp ((char *) key, "current-point") == 0) {
+       double x, y;
+
+       cairo_get_current_point (cr, &x, &y);
+
+       status = _csi_push_ostack_real (ctx, x);
+       if (_csi_unlikely (status))
+           return status;
+       status = _csi_push_ostack_real (ctx, y);
+       if (_csi_unlikely (status))
+           return status;
+
+       return CSI_STATUS_SUCCESS;
+    } else if (strcmp ((char *) key, "source") == 0) {
+       obj.type = CSI_OBJECT_TYPE_PATTERN;
+       obj.datum.pattern = cairo_pattern_reference (cairo_get_source (cr));
+    } else if (strcmp ((char *) key, "target") == 0) {
+       obj.type = CSI_OBJECT_TYPE_SURFACE;
+       obj.datum.surface = cairo_surface_reference (cairo_get_target (cr));
+    } else if (strcmp ((char *) key, "group-target") == 0) {
+       obj.type = CSI_OBJECT_TYPE_SURFACE;
+       obj.datum.surface = cairo_surface_reference (cairo_get_group_target (cr));
+    } else if (strcmp ((char *) key, "scaled-font") == 0) {
+       obj.type = CSI_OBJECT_TYPE_SCALED_FONT;
+       obj.datum.scaled_font = cairo_scaled_font_reference (cairo_get_scaled_font (cr));
+    } else if (strcmp ((char *) key, "font-face") == 0) {
+       obj.type = CSI_OBJECT_TYPE_FONT;
+       obj.datum.font_face = cairo_font_face_reference (cairo_get_font_face (cr));
+    } else
+       return _proxy_get (cairo_get_user_data (cr, &_csi_proxy_key), key);
+
+    return push (&obj);
+}
+
+static csi_status_t
+_font_get (csi_t *ctx,
+          cairo_font_face_t *font_face,
+          csi_name_t key)
+{
+    return _proxy_get (cairo_font_face_get_user_data (font_face,
+                                                     &_csi_proxy_key),
+                      key);
+}
+
+static csi_status_t
+_pattern_get (csi_t *ctx,
+             cairo_pattern_t *pattern,
+             csi_name_t key)
+{
+    csi_status_t status;
+
+    if (strcmp ((char *) key, "type") == 0)
+       return _csi_push_ostack_integer (ctx, cairo_pattern_get_type (pattern));
+
+    if (strcmp ((char *) key, "filter") == 0)
+       return _csi_push_ostack_integer (ctx, cairo_pattern_get_filter (pattern));
+
+    if (strcmp ((char *) key, "extend") == 0)
+       return _csi_push_ostack_integer (ctx, cairo_pattern_get_extend (pattern));
+
+    if (strcmp ((char *) key, "matrix") == 0) {
+       csi_object_t obj;
+       cairo_matrix_t m;
+
+       cairo_pattern_get_matrix (pattern, &m);
+       status = csi_matrix_new_from_matrix (ctx, &obj, &m);
+       if (_csi_unlikely (status))
+           return status;
+
+       return push (&obj);
+    }
+
+    return _proxy_get (cairo_pattern_get_user_data (pattern, &_csi_proxy_key),
+                      key);
+}
+
+static csi_status_t
+_scaled_font_get (csi_t *ctx,
+                 cairo_scaled_font_t *font,
+                 csi_name_t key)
+{
+    return _proxy_get (cairo_scaled_font_get_user_data (font, &_csi_proxy_key),
+                      key);
+}
+
+static csi_status_t
+_surface_get (csi_t *ctx,
+             cairo_surface_t *surface,
+             csi_name_t key)
+{
+    if (strcmp ((char *) key, "type") == 0) {
+       return _csi_push_ostack_integer (ctx, cairo_surface_get_type (surface));
+    }
+
+    if (strcmp ((char *) key, "content") == 0) {
+       return _csi_push_ostack_integer (ctx,
+                                        cairo_surface_get_content (surface));
+    }
+
+    return _proxy_get (cairo_surface_get_user_data (surface, &_csi_proxy_key),
+                      key);
+}
+
+static csi_status_t
+_get (csi_t *ctx)
+{
+    csi_object_t *key, *src, obj;
+    csi_status_t status;
+    int type;
+
+    check (2);
+
+    key = _csi_peek_ostack (ctx, 0);
+    src = _csi_peek_ostack (ctx, 1);
+    pop (1);
+    type = csi_object_get_type (src);
+    switch (type) {
+    case CSI_OBJECT_TYPE_DICTIONARY:
+       if (_csi_unlikely (csi_object_get_type (key) != CSI_OBJECT_TYPE_NAME))
+           return _csi_error (CSI_STATUS_INVALID_SCRIPT);
+
+       status = csi_dictionary_get (ctx,
+                                    src->datum.dictionary,
+                                    key->datum.name,
+                                    &obj);
+       break;
+    case CSI_OBJECT_TYPE_ARRAY:
+       if (_csi_unlikely (csi_object_get_type (key) != CSI_OBJECT_TYPE_INTEGER))
+           return _csi_error (CSI_STATUS_INVALID_SCRIPT);
+
+       status = csi_array_get (ctx,
+                               src->datum.array,
+                               key->datum.integer,
+                               &obj);
+       break;
+#if 0
+    case CSI_OBJECT_TYPE_STRING:
+       status = csi_string_get (src, key, &obj);
+       break;
+#endif
+
+    case CSI_OBJECT_TYPE_CONTEXT:
+       if (_csi_unlikely (csi_object_get_type (key) != CSI_OBJECT_TYPE_NAME))
+           return _csi_error (CSI_STATUS_INVALID_SCRIPT);
+       return _context_get (ctx, src->datum.cr, key->datum.name);
+
+    case CSI_OBJECT_TYPE_FONT:
+       if (_csi_unlikely (csi_object_get_type (key) != CSI_OBJECT_TYPE_NAME))
+           return _csi_error (CSI_STATUS_INVALID_SCRIPT);
+       return _font_get (ctx, src->datum.font_face, key->datum.name);
+
+    case CSI_OBJECT_TYPE_PATTERN:
+       if (_csi_unlikely (csi_object_get_type (key) != CSI_OBJECT_TYPE_NAME))
+           return _csi_error (CSI_STATUS_INVALID_SCRIPT);
+       return _pattern_get (ctx, src->datum.pattern, key->datum.name);
+
+    case CSI_OBJECT_TYPE_SCALED_FONT:
+       if (_csi_unlikely (csi_object_get_type (key) != CSI_OBJECT_TYPE_NAME))
+           return _csi_error (CSI_STATUS_INVALID_SCRIPT);
+       return _scaled_font_get (ctx, src->datum.scaled_font, key->datum.name);
+
+    case CSI_OBJECT_TYPE_SURFACE:
+       if (_csi_unlikely (csi_object_get_type (key) != CSI_OBJECT_TYPE_NAME))
+           return _csi_error (CSI_STATUS_INVALID_SCRIPT);
+       return _surface_get (ctx, src->datum.surface, key->datum.name);
+
+    default:
+       return _csi_error (CSI_STATUS_INVALID_SCRIPT);
+    }
+
+    if (_csi_unlikely (status))
+       return status;
+
+    return _csi_push_ostack_copy (ctx, &obj);
+}
+
+struct glyph_advance_cache {
+    csi_t *ctx;
+    double glyph_advance[256][2];
+    unsigned long have_glyph_advance[256];
+};
+
+static void
+glyph_advance_cache_destroy (void *closure)
+{
+    struct glyph_advance_cache *cache = closure;
+    _csi_free (cache->ctx, cache);
+}
+
+static int
+_glyph_string (csi_t *ctx,
+              csi_array_t *array,
+              cairo_scaled_font_t *scaled_font,
+              cairo_glyph_t *glyphs)
+{
+    struct glyph_advance_cache uncached;
+    struct glyph_advance_cache *cache;
+    csi_integer_t nglyphs, i, j;
+    double x, y, dx;
+    cairo_status_t status;
+
+    if (cairo_scaled_font_status (scaled_font))
+       return 0;
+
+    cache = cairo_scaled_font_get_user_data (scaled_font,
+                                            (cairo_user_data_key_t *) ctx);
+    if (cache == NULL) {
+       cache = _csi_alloc (ctx, sizeof (*cache));
+       if (_csi_likely (cache != NULL)) {
+           cache->ctx = ctx;
+           memset (cache->have_glyph_advance, 0xff,
+                   sizeof (cache->have_glyph_advance));
+
+           status = cairo_scaled_font_set_user_data (scaled_font,
+                                                     (cairo_user_data_key_t *) ctx,
+                                                     cache,
+                                                     glyph_advance_cache_destroy);
+           if (_csi_unlikely (status)) {
+               _csi_free (ctx, cache);
+               cache = NULL;
+           }
+       }
+    }
+
+    if (_csi_unlikely (cache == NULL)) {
+       cache = &uncached;
+
+       cache->ctx = ctx;
+       memset (cache->have_glyph_advance, 0xff,
+               sizeof (cache->have_glyph_advance));
+    }
+
+    nglyphs = 0;
+    x = y = 0;
+    for (i = 0; i < array->stack.len; i++) {
+       const csi_object_t *obj = &array->stack.objects[i];
+       int type = csi_object_get_type (obj);
+
+       switch (type) {
+       case CSI_OBJECT_TYPE_ARRAY: {
+           const csi_array_t *glyph_array = obj->datum.array;
+           for (j = 0; j < glyph_array->stack.len; j++) {
+               unsigned long g;
+               int gi;
+
+               obj = &glyph_array->stack.objects[j];
+               if (csi_object_get_type (obj) != CSI_OBJECT_TYPE_INTEGER)
+                   break;
+               g = obj->datum.integer;
+
+               glyphs[nglyphs].index = g;
+               glyphs[nglyphs].x = x;
+               glyphs[nglyphs].y = y;
+
+               gi = g % ARRAY_LENGTH (cache->have_glyph_advance);
+               if (cache->have_glyph_advance[gi] != g) {
+                   cairo_text_extents_t extents;
+
+                   cairo_scaled_font_glyph_extents (scaled_font,
+                                                    &glyphs[nglyphs], 1,
+                                                    &extents);
+
+                   cache->glyph_advance[gi][0] = extents.x_advance;
+                   cache->glyph_advance[gi][1] = extents.y_advance;
+                   cache->have_glyph_advance[gi] = g;
+               }
+
+               x += cache->glyph_advance[gi][0];
+               y += cache->glyph_advance[gi][1];
+               nglyphs++;
+           }
+           break;
+       }
+
+       case CSI_OBJECT_TYPE_STRING: {
+           const csi_string_t *glyph_string = obj->datum.string;
+           for (j = 0; j < glyph_string->len; j++) {
+               uint8_t g;
+
+               g = glyph_string->string[j];
+               glyphs[nglyphs].index = g;
+               glyphs[nglyphs].x = x;
+               glyphs[nglyphs].y = y;
+
+               if (cache->have_glyph_advance[g] != g) {
+                   cairo_text_extents_t extents;
+
+                   cairo_scaled_font_glyph_extents (scaled_font,
+                                                    &glyphs[nglyphs], 1,
+                                                    &extents);
+
+                   cache->glyph_advance[g][0] = extents.x_advance;
+                   cache->glyph_advance[g][1] = extents.y_advance;
+                   cache->have_glyph_advance[g] = g;
+               }
+
+               x += cache->glyph_advance[g][0];
+               y += cache->glyph_advance[g][1];
+               nglyphs++;
+           }
+           break;
+       }
+
+       case CSI_OBJECT_TYPE_INTEGER:
+       case CSI_OBJECT_TYPE_REAL: /* dx or x*/
+           dx = csi_number_get_value (obj);
+           if (i+1 == array->stack.len)
+               break;
+
+           type = csi_object_get_type (&array->stack.objects[i+1]);
+           switch (type) {
+           case CSI_OBJECT_TYPE_INTEGER:
+           case CSI_OBJECT_TYPE_REAL: /* y */
+               y = csi_number_get_value (&array->stack.objects[i+1]);
+               x = dx;
+               i++;
+               break;
+
+           default:
+               x += dx;
+           }
+       }
+    }
+
+    return nglyphs;
+}
+
+static csi_status_t
+_glyph_path (csi_t *ctx)
+{
+    csi_array_t *array;
+    csi_status_t status;
+    cairo_t *cr;
+    cairo_glyph_t stack_glyphs[256], *glyphs;
+    csi_integer_t nglyphs, i;
+
+    check (2);
+
+    status = _csi_ostack_get_array (ctx, 0, &array);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_context (ctx, 1, &cr);
+    if (_csi_unlikely (status))
+       return status;
+
+    /* count glyphs */
+    nglyphs = 0;
+    for (i = 0; i < array->stack.len; i++) {
+       csi_object_t *obj = &array->stack.objects[i];
+       int type = csi_object_get_type (obj);
+       switch (type) {
+       case CSI_OBJECT_TYPE_ARRAY:
+           nglyphs += obj->datum.array->stack.len;
+           break;
+       case CSI_OBJECT_TYPE_STRING:
+           nglyphs += obj->datum.string->len;
+           break;
+       }
+    }
+    if (nglyphs == 0) {
+       pop (1);
+       return CSI_STATUS_SUCCESS;
+    }
+
+    if (nglyphs > ARRAY_LENGTH (stack_glyphs)) {
+       if (_csi_unlikely ((unsigned) nglyphs >= INT_MAX / sizeof (cairo_glyph_t)))
+           return _csi_error (CSI_STATUS_NO_MEMORY);
+
+       glyphs = _csi_alloc (ctx, sizeof (cairo_glyph_t) * nglyphs);
+       if (_csi_unlikely (glyphs == NULL))
+           return _csi_error (CSI_STATUS_NO_MEMORY);
+    } else
+       glyphs = stack_glyphs;
+
+    nglyphs = _glyph_string (ctx, array, cairo_get_scaled_font (cr), glyphs);
+    cairo_glyph_path (cr, glyphs, nglyphs);
+
+    if (glyphs != stack_glyphs)
+       _csi_free (ctx, glyphs);
+
+    pop (1);
+    return CSI_STATUS_SUCCESS;
+}
+
+static csi_status_t
+_gray (csi_t *ctx)
+{
+    csi_object_t obj;
+    csi_status_t status;
+    double g;
+
+    check (1);
+
+    status = _csi_ostack_get_number (ctx, 0, &g);
+    if (_csi_unlikely (status))
+       return status;
+
+    pop (1);
+
+    obj.type = CSI_OBJECT_TYPE_PATTERN;
+    obj.datum.pattern = cairo_pattern_create_rgba (g, g, g, 1);
+    return push (&obj);
+}
+
+static csi_status_t
+_gt (csi_t *ctx)
+{
+    csi_status_t status;
+    csi_object_t *a, *b;
+    int cmp;
+
+    check (2);
+
+    b = _csi_peek_ostack (ctx, 0);
+    a = _csi_peek_ostack (ctx, 1);
+
+    status = csi_object_compare (a, b, &cmp);
+    if (_csi_unlikely (status))
+       return status;
+
+    pop (2);
+    return _csi_push_ostack_boolean (ctx, cmp > 0);
+}
+
+static csi_status_t
+_identity (csi_t *ctx)
+{
+    csi_object_t obj;
+    csi_status_t status;
+
+    status = csi_matrix_new (ctx, &obj);
+    if (_csi_unlikely (status))
+       return status;
+
+    return push (&obj);
+}
+
+static csi_status_t
+_if (csi_t *ctx)
+{
+    csi_array_t *proc;
+    csi_boolean_t predicate = FALSE; /* silence the compiler */
+    csi_status_t status;
+
+    check (2);
+
+    status = _csi_ostack_get_procedure (ctx, 0, &proc);
+    if (_csi_unlikely (status))
+       return status;
+
+    status = _csi_ostack_get_boolean (ctx, 1, &predicate);
+    if (_csi_unlikely (status))
+       return status;
+
+    proc->base.ref++;
+    pop (2);
+
+    if (predicate)
+       status = _csi_array_execute (ctx, proc);
+
+    if (--proc->base.ref == 0)
+       csi_array_free (ctx, proc);
+
+    return status;
+}
+
+static csi_status_t
+_ifelse (csi_t *ctx)
+{
+    csi_array_t *true_proc, *false_proc;
+    csi_boolean_t predicate = FALSE; /* silence the compiler */
+    csi_status_t status;
+
+    check (3);
+
+    status = _csi_ostack_get_procedure (ctx, 0, &false_proc);
+    if (_csi_unlikely (status))
+       return _csi_error (CSI_STATUS_INVALID_SCRIPT);
+
+    status = _csi_ostack_get_procedure (ctx, 1, &true_proc);
+    if (_csi_unlikely (status))
+       return _csi_error (CSI_STATUS_INVALID_SCRIPT);
+
+    status = _csi_ostack_get_boolean (ctx, 2, &predicate);
+    if (_csi_unlikely (status))
+       return status;
+
+    true_proc->base.ref++;
+    false_proc->base.ref++;
+    pop (3);
+
+    if (predicate)
+       status = _csi_array_execute (ctx, true_proc);
+    else
+       status = _csi_array_execute (ctx, false_proc);
+
+    if (--true_proc->base.ref == 0)
+       csi_array_free (ctx, true_proc);
+    if (--false_proc->base.ref == 0)
+       csi_array_free (ctx, false_proc);
+
+    return status;
+}
+
+static csi_status_t
+_image_read_raw (csi_file_t *src,
+                cairo_format_t format,
+                int width, int height,
+                cairo_surface_t **image_out)
+{
+    cairo_surface_t *image;
+    uint8_t *bp, *data;
+    int rem, len, ret, x, rowlen, instride, stride;
+    cairo_status_t status;
+
+    stride = cairo_format_stride_for_width (format, width);
+    data = malloc (stride * height);
+    if (data == NULL)
+       return CAIRO_STATUS_NO_MEMORY;
+
+    image = cairo_image_surface_create_for_data (data, format,
+                                                width, height, stride);
+    status = cairo_surface_set_user_data (image,
+                                         (const cairo_user_data_key_t *) image,
+                                         data, free);
+    if (status) {
+       cairo_surface_destroy (image);
+       free (image);
+       return status;
+    }
+
+    switch (format) {
+    case CAIRO_FORMAT_A1:
+       instride = rowlen = (width+7)/8;
+       break;
+    case CAIRO_FORMAT_A8:
+       instride = rowlen = width;
+       break;
+    case CAIRO_FORMAT_RGB16_565:
+       instride = rowlen = 2 * width;
+       break;
+    case CAIRO_FORMAT_RGB24:
+       rowlen = 3 * width;
+       instride = 4 *width;
+       break;
+    default:
+    case CAIRO_FORMAT_RGB30:
+    case CAIRO_FORMAT_INVALID:
+    case CAIRO_FORMAT_ARGB32:
+       instride = rowlen = 4 * width;
+       break;
+    }
+    len = rowlen * height;
+
+    bp = data;
+    rem = len;
+    while (rem) {
+       ret = csi_file_read (src, bp, rem);
+       if (_csi_unlikely (ret == 0)) {
+           cairo_surface_destroy (image);
+           return _csi_error (CSI_STATUS_READ_ERROR);
+       }
+       rem -= ret;
+       bp += ret;
+    }
+
+    if (len != height * stride) {
+       while (--height) {
+           uint8_t *row = data + height * stride;
+
+           /* XXX pixel conversion */
+           switch (format) {
+           case CAIRO_FORMAT_A1:
+               for (x = rowlen; x--; ) {
+                   uint8_t byte = *--bp;
+                   row[x] = CSI_BITSWAP8_IF_LITTLE_ENDIAN (byte);
+               }
+               break;
+           case CAIRO_FORMAT_A8:
+               for (x = width; x--; )
+                   row[x] = *--bp;
+               break;
+           case CAIRO_FORMAT_RGB16_565:
+               for (x = width; x--; ) {
+#ifdef WORDS_BIGENDIAN
+                   row[2*x + 1] = *--bp;
+                   row[2*x + 0] = *--bp;
+#else
+                   row[2*x + 0] = *--bp;
+                   row[2*x + 1] = *--bp;
+#endif
+               }
+               break;
+           case CAIRO_FORMAT_RGB24:
+               for (x = width; x--; ) {
+#ifdef WORDS_BIGENDIAN
+                   row[4*x + 3] = *--bp;
+                   row[4*x + 2] = *--bp;
+                   row[4*x + 1] = *--bp;
+                   row[4*x + 0] = 0xff;
+#else
+                   row[4*x + 0] = *--bp;
+                   row[4*x + 1] = *--bp;
+                   row[4*x + 2] = *--bp;
+                   row[4*x + 3] = 0xff;
+#endif
+               }
+               break;
+           case CAIRO_FORMAT_RGB30:
+           case CAIRO_FORMAT_INVALID:
+           case CAIRO_FORMAT_ARGB32:
+               /* stride == width */
+               break;
+           }
+
+           memset (row + instride, 0, stride - instride);
+       }
+
+       /* need to treat last row carefully */
+       switch (format) {
+       case CAIRO_FORMAT_A1:
+           for (x = rowlen; x--; ) {
+               uint8_t byte = *--bp;
+               data[x] = CSI_BITSWAP8_IF_LITTLE_ENDIAN (byte);
+           }
+           break;
+       case CAIRO_FORMAT_A8:
+           for (x = width; x--; )
+               data[x] = *--bp;
+           break;
+       case CAIRO_FORMAT_RGB16_565:
+           for (x = width; x--; ) {
+#ifdef WORDS_BIGENDIAN
+               data[2*x + 1] = *--bp;
+               data[2*x + 0] = *--bp;
+#else
+               data[2*x + 0] = *--bp;
+               data[2*x + 1] = *--bp;
+#endif
+           }
+           break;
+       case CAIRO_FORMAT_RGB24:
+           for (x = width; --x>1; ) {
+#ifdef WORDS_BIGENDIAN
+               data[4*x + 3] = *--bp;
+               data[4*x + 2] = *--bp;
+               data[4*x + 1] = *--bp;
+               data[4*x + 0] = 0xff;
+#else
+               data[4*x + 0] = *--bp;
+               data[4*x + 1] = *--bp;
+               data[4*x + 2] = *--bp;
+               data[4*x + 3] = 0xff;
+#endif
+           }
+           if (width > 1) {
+               uint8_t rgb[2][3];
+               /* shuffle the last couple of overlapping pixels */
+               rgb[1][0] = data[5];
+               rgb[1][1] = data[4];
+               rgb[1][2] = data[3];
+               rgb[0][0] = data[2];
+               rgb[0][1] = data[1];
+               rgb[0][2] = data[0];
+#ifdef WORDS_BIGENDIAN
+               data[4] = 0xff;
+               data[5] = rgb[1][2];
+               data[6] = rgb[1][1];
+               data[7] = rgb[1][0];
+               data[0] = 0xff;
+               data[1] = rgb[0][2];
+               data[2] = rgb[0][1];
+               data[3] = rgb[0][0];
+#else
+               data[7] = 0xff;
+               data[6] = rgb[1][2];
+               data[5] = rgb[1][1];
+               data[4] = rgb[1][0];
+               data[3] = 0xff;
+               data[2] = rgb[0][2];
+               data[1] = rgb[0][1];
+               data[0] = rgb[0][0];
+#endif
+           } else {
+#ifdef WORDS_BIGENDIAN
+               data[0] = 0xff;
+               data[1] = data[0];
+               data[2] = data[1];
+               data[3] = data[2];
+#else
+               data[3] = data[0];
+               data[0] = data[2];
+               data[2] = data[3];
+               data[3] = 0xff;
+#endif
+           }
+           break;
+       case CAIRO_FORMAT_RGB30:
+       case CAIRO_FORMAT_INVALID:
+       case CAIRO_FORMAT_ARGB32:
+           /* stride == width */
+           break;
+       }
+       memset (data + instride, 0, stride - instride);
+    } else {
+#ifndef WORDS_BIGENDIAN
+       switch (format) {
+       case CAIRO_FORMAT_A1:
+           for (x = 0; x < len; x++) {
+               uint8_t byte = data[x];
+               data[x] = CSI_BITSWAP8_IF_LITTLE_ENDIAN (byte);
+           }
+           break;
+       case CAIRO_FORMAT_RGB16_565:
+           {
+               uint32_t *rgba = (uint32_t *) data;
+               for (x = len/2; x--; rgba++) {
+                   *rgba = bswap_16 (*rgba);
+               }
+           }
+           break;
+       case CAIRO_FORMAT_ARGB32:
+           {
+               uint32_t *rgba = (uint32_t *) data;
+               for (x = len/4; x--; rgba++) {
+                   *rgba = bswap_32 (*rgba);
+               }
+           }
+           break;
+
+       case CAIRO_FORMAT_A8:
+           break;
+
+       case CAIRO_FORMAT_RGB30:
+       case CAIRO_FORMAT_RGB24:
+       case CAIRO_FORMAT_INVALID:
+       default:
+           break;
+       }
+#endif
+    }
+
+    cairo_surface_mark_dirty (image);
+    *image_out = image;
+    return CSI_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+png_read_func (void *closure, uint8_t *data, unsigned int len)
+{
+    int ret;
+
+    ret = csi_file_read (closure, data, len);
+    if ((unsigned int) ret != len)
+       return CAIRO_STATUS_READ_ERROR;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static csi_status_t
+_image_read_png (csi_file_t *src, cairo_surface_t **out)
+{
+#if CAIRO_HAS_PNG_FUNCTIONS
+    *out = cairo_image_surface_create_from_png_stream (png_read_func, src);
+    return cairo_surface_status (*out);
+#else
+    return CAIRO_STATUS_READ_ERROR;
+#endif
+}
+
+struct _image_tag {
+    csi_t *ctx;
+    csi_blob_t blob;
+    cairo_surface_t *surface;
+};
+
+static void
+_image_tag_done (void *closure)
+{
+    struct _image_tag *tag = closure;
+    csi_t *ctx = tag->ctx;
+
+    ctx->_images = _csi_list_unlink (ctx->_images, &tag->blob.list);
+    _csi_slab_free (ctx, tag, sizeof (*tag));
+    cairo_script_interpreter_destroy (ctx);
+}
+
+static void
+_image_hash (csi_blob_t *blob,
+            cairo_surface_t *surface)
+{
+    uint32_t  value;
+
+    value = cairo_image_surface_get_width (surface);
+    _csi_blob_hash (blob, &value, 1);
+
+    value = cairo_image_surface_get_height (surface);
+    _csi_blob_hash (blob, &value, 1);
+
+    value = cairo_image_surface_get_format (surface);
+    _csi_blob_hash (blob, &value, 1);
+}
+
+static cairo_surface_t *
+_image_cached (csi_t *ctx, cairo_surface_t *surface)
+{
+    csi_blob_t tmpl;
+    csi_list_t *link;
+    uint8_t *data;
+    int stride, height;
+    struct _image_tag *tag;
+
+    /* check for an existing image  */
+
+    data = cairo_image_surface_get_data (surface);
+    stride = cairo_image_surface_get_stride (surface);
+    height = cairo_image_surface_get_height (surface);
+    _csi_blob_init (&tmpl, data, stride * height);
+    _image_hash (&tmpl, surface);
+    link = _csi_list_find (ctx->_images, _csi_blob_equal, &tmpl);
+    if (link) {
+       cairo_surface_destroy (surface);
+       tag = csi_container_of (link, struct _image_tag, blob.list);
+       return cairo_surface_reference (tag->surface);
+    }
+
+    /* none found, insert a tag for this one */
+
+    tag = _csi_slab_alloc (ctx, sizeof (struct _image_tag));
+    if (tag == NULL)
+       return surface;
+
+    ctx->_images = _csi_list_prepend (ctx->_images, &tag->blob.list);
+    tag->ctx = cairo_script_interpreter_reference (ctx);
+    tag->blob.hash = tmpl.hash;
+    tag->blob.bytes = tmpl.bytes;
+    tag->blob.len = tmpl.len;
+    tag->surface = surface;
+
+    if (cairo_surface_set_user_data (surface, &_csi_blob_key,
+                                    tag, _image_tag_done))
+    {
+       _image_tag_done (tag);
+    }
+
+    return surface;
+}
+
+static csi_status_t
+_image_load_from_dictionary (csi_t *ctx,
+                            csi_dictionary_t *dict,
+                            cairo_surface_t **image_out)
+{
+    csi_object_t obj, key;
+    long width;
+    long height;
+    long format;
+    cairo_surface_t *image = NULL; /* silence the compiler */
+    csi_status_t status;
+
+    /* check for "status? */
+
+    status = _csi_dictionary_get_integer (ctx, dict, "width", FALSE, &width);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_dictionary_get_integer (ctx, dict, "height", FALSE, &height);
+    if (_csi_unlikely (status))
+       return status;
+
+    format = CAIRO_FORMAT_ARGB32;
+    status = _csi_dictionary_get_integer (ctx, dict, "format", TRUE, &format);
+    if (_csi_unlikely (status))
+       return status;
+
+    status = csi_name_new_static (ctx, &key, "source");
+    if (_csi_unlikely (status))
+       return status;
+
+    if (csi_dictionary_has (dict, key.datum.name)) {
+       enum mime_type mime_type;
+       csi_object_t file;
+
+       status = csi_dictionary_get (ctx, dict, key.datum.name, &obj);
+       if (_csi_unlikely (status))
+           return status;
+
+       status = csi_name_new_static (ctx, &key, "mime-type");
+       if (_csi_unlikely (status))
+           return status;
+
+       mime_type = MIME_TYPE_NONE;
+       if (csi_dictionary_has (dict, key.datum.name)) {
+           csi_object_t type_obj;
+           const char *type_str;
+           int type;
+
+           status = csi_dictionary_get (ctx, dict, key.datum.name, &type_obj);
+           if (_csi_unlikely (status))
+               return status;
+
+           type = csi_object_get_type (&type_obj);
+           switch (type) {
+           case CSI_OBJECT_TYPE_STRING:
+               type_str = type_obj.datum.string->string;
+               break;
+           case CSI_OBJECT_TYPE_NAME:
+               type_str = (char *) type_obj.datum.name;
+               break;
+           default:
+               return _csi_error (CSI_STATUS_INVALID_SCRIPT);
+           }
+
+           if (strcmp (type_str, CAIRO_MIME_TYPE_PNG) == 0)
+               mime_type = MIME_TYPE_PNG;
+       }
+
+       status = csi_object_as_file (ctx, &obj, &file);
+       if (_csi_unlikely (status))
+           return status;
+
+       /* XXX hook for general mime-type decoder */
+
+       switch (mime_type) {
+       case MIME_TYPE_NONE:
+           status = _image_read_raw (file.datum.file,
+                                     format, width, height, &image);
+           break;
+       case MIME_TYPE_PNG:
+           status = _image_read_png (file.datum.file, &image);
+           break;
+       }
+       csi_object_free (ctx, &file);
+       if (_csi_unlikely (status))
+           return status;
+
+       image = _image_cached (ctx, image);
+    } else
+       image = cairo_image_surface_create (format, width, height);
+
+    *image_out = image;
+    return CSI_STATUS_SUCCESS;
+}
+
+static csi_status_t
+_image (csi_t *ctx)
+{
+    csi_dictionary_t *dict;
+    cairo_surface_t *image;
+    csi_status_t status;
+    csi_object_t obj;
+
+    check (1);
+
+    status = _csi_ostack_get_dictionary (ctx, 0, &dict);
+    if (_csi_unlikely (status))
+       return status;
+
+    status = _image_load_from_dictionary (ctx, dict, &image);
+    if (_csi_unlikely (status))
+       return status;
+
+    pop (1);
+    obj.type = CSI_OBJECT_TYPE_SURFACE;
+    obj.datum.surface = image;
+    return push (&obj);
+}
+
+static csi_status_t
+_index (csi_t *ctx)
+{
+    csi_status_t status;
+    long n;
+
+    check (1);
+
+    status = _csi_ostack_get_integer (ctx, 0,  &n);
+    if (_csi_unlikely (status))
+       return status;
+
+    pop (1);
+
+    check (n);
+    return _csi_push_ostack_copy (ctx, _csi_peek_ostack (ctx, n));
+}
+
+static csi_status_t
+_integer (csi_t *ctx)
+{
+    csi_object_t *obj;
+    int type;
+
+    check (1);
+
+    obj = _csi_peek_ostack (ctx, 0);
+    type = csi_object_get_type (obj);
+    switch (type) {
+    case CSI_OBJECT_TYPE_INTEGER:
+       break;
+    case CSI_OBJECT_TYPE_REAL:
+       obj->datum.integer = obj->datum.real;
+       break;
+    default:
+       return _csi_error (CSI_STATUS_INVALID_SCRIPT);
+    }
+    obj->type = CSI_OBJECT_TYPE_INTEGER;
+
+    return CSI_STATUS_SUCCESS;
+}
+
+static csi_status_t
+_invert (csi_t *ctx)
+{
+    csi_object_t obj;
+    csi_status_t status;
+    cairo_matrix_t m;
+
+    check (1);
+
+    status = _csi_ostack_get_matrix (ctx, 0, &m);
+    if (_csi_unlikely (status))
+       return status;
+
+    cairo_matrix_invert (&m);
+
+    status = csi_matrix_new_from_matrix (ctx, &obj, &m);
+    if (_csi_unlikely (status))
+       return status;
+
+    pop (1);
+
+    return push (&obj);
+}
+
+static csi_status_t
+_le (csi_t *ctx)
+{
+    csi_status_t status;
+    csi_object_t *a, *b;
+    int cmp;
+
+    check (2);
+
+    b = _csi_peek_ostack (ctx, 0);
+    a = _csi_peek_ostack (ctx, 1);
+
+    status = csi_object_compare (a, b, &cmp);
+    if (_csi_unlikely (status))
+       return status;
+
+    pop (2);
+    return _csi_push_ostack_boolean (ctx, cmp <= 0);
+}
+
+static csi_status_t
+_linear (csi_t *ctx)
+{
+    csi_object_t obj;
+    csi_status_t status;
+    double x1, y1, x2, y2;
+
+    check (4);
+
+    status = _csi_ostack_get_number (ctx, 0, &y2);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_number (ctx, 1, &x2);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_number (ctx, 2, &y1);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_number (ctx, 3, &x1);
+    if (_csi_unlikely (status))
+       return status;
+
+    pop (4);
+
+    obj.type = CSI_OBJECT_TYPE_PATTERN;
+    obj.datum.pattern = cairo_pattern_create_linear (x1, y1, x2, y2);
+    return push (&obj);
+}
+
+static csi_status_t
+_line_to (csi_t *ctx)
+{
+    csi_status_t status;
+    double x, y;
+    cairo_t *cr;
+
+    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_context (ctx, 2, &cr);
+    if (_csi_unlikely (status))
+       return status;
+
+    /* XXX path object */
+
+    pop (2);
+    cairo_line_to (cr, x, y);
+    return CSI_STATUS_SUCCESS;
+}
+
+static csi_status_t
+_lt (csi_t *ctx)
+{
+    csi_status_t status;
+    csi_object_t *a, *b;
+    int cmp;
+
+    check (2);
+
+    b = _csi_peek_ostack (ctx, 0);
+    a = _csi_peek_ostack (ctx, 1);
+
+    status = csi_object_compare (a, b, &cmp);
+    if (_csi_unlikely (status))
+       return status;
+
+    pop (2);
+    return _csi_push_ostack_boolean (ctx, cmp < 0);
+}
+
+static csi_status_t
+_mark (csi_t *ctx)
+{
+    return _csi_push_ostack_mark (ctx);
+}
+
+static csi_status_t
+_ne (csi_t *ctx)
+{
+    csi_object_t *a, *b;
+    csi_boolean_t v;
+
+    check (2);
+
+    b = _csi_peek_ostack (ctx, 0);
+    a = _csi_peek_ostack (ctx, 1);
+
+    v = ! csi_object_eq (a, b);
+
+    pop (2);
+    return _csi_push_ostack_boolean (ctx, v);
+}
+
+static csi_status_t
+_neg (csi_t *ctx)
+{
+    csi_object_t *obj;
+    int type;
+
+    check (1);
+
+    obj = _csi_peek_ostack (ctx, 0);
+    type = csi_object_get_type (obj);
+    switch (type) {
+    case CSI_OBJECT_TYPE_INTEGER:
+       obj->datum.integer = -obj->datum.integer;
+       break;
+    case CSI_OBJECT_TYPE_REAL:
+       obj->datum.real = -obj->datum.real;
+       break;
+    default:
+       return _csi_error (CSI_STATUS_INVALID_SCRIPT);
+    }
+
+    return CSI_STATUS_SUCCESS;
+}
+
+static csi_status_t
+_not (csi_t *ctx)
+{
+    csi_object_t *obj;
+    int type;
+
+    check (1);
+
+    obj = _csi_peek_ostack (ctx, 0);
+    type = csi_object_get_type (obj);
+    switch (type) {
+    case CSI_OBJECT_TYPE_BOOLEAN:
+       obj->datum.boolean = ! obj->datum.boolean;
+       break;
+    case CSI_OBJECT_TYPE_INTEGER:
+       obj->type = CSI_OBJECT_TYPE_BOOLEAN;
+       obj->datum.boolean = ! obj->datum.integer;
+       break;
+    case CSI_OBJECT_TYPE_REAL:
+       obj->type = CSI_OBJECT_TYPE_BOOLEAN;
+       obj->datum.boolean = obj->datum.real == 0.0;
+       break;
+    default:
+       return _csi_error (CSI_STATUS_INVALID_SCRIPT);
+    }
+
+    return CSI_STATUS_SUCCESS;
+}
+
+static csi_status_t
+_new_path (csi_t *ctx)
+{
+    /* XXX handle path object */
+    return _do_cairo_op (ctx, cairo_new_path);
+}
+
+static csi_status_t
+_new_sub_path (csi_t *ctx)
+{
+    /* XXX handle path object */
+    return _do_cairo_op (ctx, cairo_new_sub_path);
+}
+
+static csi_status_t
+_null (csi_t *ctx)
+{
+    return _csi_push_ostack_null (ctx);
+}
+
+static csi_status_t
+_mask (csi_t *ctx)
+{
+    cairo_t *cr;
+    cairo_pattern_t *pattern = NULL; /* silence the compiler */
+    csi_status_t status;
+
+    check (2);
+
+    status = _csi_ostack_get_pattern (ctx, 0, &pattern);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_context (ctx, 1, &cr);
+    if (_csi_unlikely (status))
+       return status;
+
+    cairo_mask (cr, pattern);
+    pop (1);
+
+    return CSI_STATUS_SUCCESS;
+}
+
+static csi_status_t
+_matrix (csi_t *ctx)
+{
+    csi_object_t *obj, matrix;
+    double v[6];
+    csi_status_t status;
+    int n;
+
+    check (1);
+
+    obj = _csi_peek_ostack (ctx, 0);
+    if (csi_object_is_number (obj)) {
+       check (6);
+
+       for (n = 6; n--; ) {
+           status = _csi_ostack_get_number (ctx, 5-n, &v[n]);
+           if (_csi_unlikely (status))
+               return status;
+       }
+       status = csi_matrix_new_from_values (ctx, &matrix, v);
+       if (_csi_unlikely (status))
+           return status;
+
+       pop (6);
+    } else {
+       csi_array_t *array;
+
+       status = _csi_ostack_get_array (ctx, 0, &array);
+       if (_csi_unlikely (status))
+           return status;
+
+       status = csi_matrix_new_from_array (ctx, &matrix, array);
+       if (_csi_unlikely (status))
+           return status;
+
+       pop (1);
+    }
+
+    return push (&matrix);
+}
+
+static csi_status_t
+_map_to_image (csi_t *ctx)
+{
+    csi_object_t obj;
+    csi_array_t *array;
+    csi_status_t status;
+    cairo_rectangle_int_t extents, *r;
+    cairo_surface_t *surface;
+
+    check (2);
+
+    status = _csi_ostack_get_array (ctx, 0, &array);
+    if (_csi_unlikely (status))
+       return status;
+
+    status = _csi_ostack_get_surface (ctx, 1, &surface);
+    if (_csi_unlikely (status))
+       return status;
+
+    switch (array->stack.len) {
+    case 0:
+       r = NULL;
+       break;
+    case 4:
+       extents.x = floor (_csi_object_as_real (&array->stack.objects[0]));
+       extents.y = floor (_csi_object_as_real (&array->stack.objects[1]));
+       extents.width = ceil (_csi_object_as_real (&array->stack.objects[2]));
+       extents.height = ceil (_csi_object_as_real (&array->stack.objects[3]));
+       r = &extents;
+       break;
+    default:
+       return _csi_error (CSI_STATUS_INVALID_SCRIPT);
+    }
+
+    obj.type = CSI_OBJECT_TYPE_SURFACE;
+    obj.datum.surface = cairo_surface_map_to_image (surface, r);
+    pop (2);
+    return push (&obj);
+}
+
+static csi_status_t
+_unmap_image (csi_t *ctx)
+{
+    cairo_surface_t *surface, *image;
+    csi_status_t status;
+
+    check (2);
+
+    status = _csi_ostack_get_surface (ctx, 0, &image);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_surface (ctx, 1, &surface);
+    if (_csi_unlikely (status))
+       return status;
+
+    cairo_surface_unmap_image (surface, image);
+
+    pop (2);
+    return CSI_STATUS_SUCCESS;
+}
+
+static csi_status_t
+_mesh (csi_t *ctx)
+{
+    csi_object_t obj;
+
+    obj.type = CSI_OBJECT_TYPE_PATTERN;
+    obj.datum.pattern = cairo_pattern_create_mesh ();
+    return push (&obj);
+}
+
+static csi_status_t
+_mesh_begin_patch (csi_t *ctx)
+{
+    csi_status_t status;
+    cairo_pattern_t *pattern = NULL; /* silence the compiler */
+
+    check (1);
+
+    status = _csi_ostack_get_pattern (ctx, 0, &pattern);
+    if (_csi_unlikely (status))
+       return status;
+
+    cairo_mesh_pattern_begin_patch (pattern);
+    return CSI_STATUS_SUCCESS;
+}
+
+static csi_status_t
+_mesh_curve_to (csi_t *ctx)
+{
+    csi_status_t status;
+    double x1, y1, x2, y2, x3, y3;
+    cairo_pattern_t *pattern = NULL; /* silence the compiler */
+
+    check (7);
+
+    status = _csi_ostack_get_number (ctx, 0, &y3);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_number (ctx, 1, &x3);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_number (ctx, 2, &y2);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_number (ctx, 3, &x2);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_number (ctx, 4, &y1);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_number (ctx, 5, &x1);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_pattern (ctx, 6, &pattern);
+    if (_csi_unlikely (status))
+       return status;
+
+    cairo_mesh_pattern_curve_to (pattern, x1, y1, x2, y2, x3, y3);
+
+    pop (6);
+    return CSI_STATUS_SUCCESS;
+}
+
+static csi_status_t
+_mesh_end_patch (csi_t *ctx)
+{
+    csi_status_t status;
+    cairo_pattern_t *pattern = NULL; /* silence the compiler */
+
+    check (1);
+
+    status = _csi_ostack_get_pattern (ctx, 0, &pattern);
+    if (_csi_unlikely (status))
+       return status;
+
+    cairo_mesh_pattern_end_patch (pattern);
+    return CSI_STATUS_SUCCESS;
+}
+
+static csi_status_t
+_mesh_line_to (csi_t *ctx)
+{
+    csi_status_t status;
+    double x, y;
+    cairo_pattern_t *pattern = NULL; /* silence the compiler */
+
+    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_pattern (ctx, 2, &pattern);
+    if (_csi_unlikely (status))
+       return status;
+
+    cairo_mesh_pattern_line_to (pattern, x, y);
+
+    pop (2);
+    return CSI_STATUS_SUCCESS;
+}
+
+static csi_status_t
+_mesh_move_to (csi_t *ctx)
+{
+    csi_status_t status;
+    double x, y;
+    cairo_pattern_t *pattern = NULL; /* silence the compiler */
+
+    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_pattern (ctx, 2, &pattern);
+    if (_csi_unlikely (status))
+       return status;
+
+    cairo_mesh_pattern_move_to (pattern, x, y);
+
+    pop (2);
+    return CSI_STATUS_SUCCESS;
+}
+
+static csi_status_t
+_mesh_set_control_point (csi_t *ctx)
+{
+    csi_status_t status;
+    double x, y;
+    csi_integer_t point;
+    cairo_pattern_t *pattern = NULL; /* silence the compiler */
+
+    check (4);
+
+    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_integer (ctx, 2, &point);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_pattern (ctx, 3, &pattern);
+    if (_csi_unlikely (status))
+       return status;
+
+    cairo_mesh_pattern_set_control_point (pattern, point, x, y);
+
+    pop (3);
+    return CSI_STATUS_SUCCESS;
+}
+
+static csi_status_t
+_mesh_set_corner_color (csi_t *ctx)
+{
+    csi_status_t status;
+    double r, g, b, a;
+    csi_integer_t corner;
+    cairo_pattern_t *pattern = NULL; /* silence the compiler */
+
+    check (6);
+
+    status = _csi_ostack_get_number (ctx, 0, &a);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_number (ctx, 1, &b);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_number (ctx, 2, &g);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_number (ctx, 3, &r);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_integer (ctx, 4, &corner);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_pattern (ctx, 5, &pattern);
+    if (_csi_unlikely (status))
+       return status;
+
+    cairo_mesh_pattern_set_corner_color_rgba (pattern, corner, r, g, b, a);
+
+    pop (5);
+    return CSI_STATUS_SUCCESS;
+}
+
+static csi_status_t
+_mod (csi_t *ctx)
+{
+    csi_integer_t x, y;
+    csi_status_t status;
+
+    check (2);
+
+    status = _csi_ostack_get_integer (ctx, 0, &y);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_integer (ctx, 1, &x);
+    if (_csi_unlikely (status))
+       return status;
+
+    pop (2);
+    return _csi_push_ostack_integer (ctx, x % y);
+}
+
+static csi_status_t
+_move_to (csi_t *ctx)
+{
+    csi_status_t status;
+    double x, y;
+    cairo_t *cr;
+
+    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_context (ctx, 2, &cr);
+    if (_csi_unlikely (status))
+       return status;
+
+    /* XXX path object */
+
+    pop (2);
+    cairo_move_to (cr, x, y);
+    return CSI_STATUS_SUCCESS;
+}
+
+static csi_status_t
+_mul (csi_t *ctx)
+{
+    csi_object_t *A;
+    csi_object_t *B;
+    csi_object_type_t type_a, type_b;
+
+    check (2);
+
+    B = _csi_peek_ostack (ctx, 0);
+    A = _csi_peek_ostack (ctx, 1);
+
+    type_a = csi_object_get_type (A);
+    if (_csi_unlikely (! (type_a == CSI_OBJECT_TYPE_INTEGER ||
+                           type_a == CSI_OBJECT_TYPE_REAL)))
+    {
+       return _csi_error (CSI_STATUS_INVALID_SCRIPT);
+    }
+    type_b = csi_object_get_type (B);
+    if (_csi_unlikely (! (type_b == CSI_OBJECT_TYPE_INTEGER ||
+                           type_b == CSI_OBJECT_TYPE_REAL)))
+    {
+       return _csi_error (CSI_STATUS_INVALID_SCRIPT);
+    }
+
+    pop (2);
+
+    if (type_a == CSI_OBJECT_TYPE_REAL &&
+       type_b == CSI_OBJECT_TYPE_REAL)
+    {
+       return _csi_push_ostack_real (ctx, A->datum.real * B->datum.real);
+
+    }
+    else if (type_a == CSI_OBJECT_TYPE_INTEGER &&
+            type_b == CSI_OBJECT_TYPE_INTEGER)
+    {
+       return _csi_push_ostack_integer (ctx,
+                                        A->datum.integer * B->datum.integer);
+    }
+    else
+    {
+       double v;
+
+       if (type_a == CSI_OBJECT_TYPE_REAL)
+           v = A->datum.real;
+       else
+           v = A->datum.integer;
+
+       if (type_b == CSI_OBJECT_TYPE_REAL)
+           v *= B->datum.real;
+       else
+           v *= B->datum.integer;
+
+       return _csi_push_ostack_real (ctx, v);
+    }
+}
+
+static csi_status_t
+_or (csi_t *ctx)
+{
+    csi_object_t *a, *b;
+    int type;
+
+    check (2);
+
+    a = _csi_peek_ostack (ctx, 0);
+    b = _csi_peek_ostack (ctx, 1);
+    if (_csi_unlikely (csi_object_get_type (a) != csi_object_get_type (b)))
+       return _csi_error (CSI_STATUS_INVALID_SCRIPT);
+
+    pop (2);
+    type = csi_object_get_type (a);
+    switch (type) {
+    case CSI_OBJECT_TYPE_INTEGER:
+       return _csi_push_ostack_integer (ctx,
+                                        a->datum.integer | b->datum.integer);
+    case CSI_OBJECT_TYPE_BOOLEAN:
+       return _csi_push_ostack_boolean (ctx,
+                                        a->datum.boolean | b->datum.boolean);
+    default:
+       return _csi_error (CSI_STATUS_INVALID_SCRIPT);
+    }
+}
+
+static csi_status_t
+_paint (csi_t *ctx)
+{
+    return _do_cairo_op (ctx, cairo_paint);
+}
+
+static csi_status_t
+_paint_with_alpha (csi_t *ctx)
+{
+    cairo_t *cr;
+    csi_status_t status;
+    double alpha;
+
+    check (2);
+
+    status = _csi_ostack_get_number (ctx, 0, &alpha);
+    if (_csi_unlikely (status))
+       return status;
+
+    status = _csi_ostack_get_context (ctx, 1, &cr);
+    if (_csi_unlikely (status))
+       return status;
+
+    cairo_paint_with_alpha (cr, alpha);
+    pop (1);
+    return CSI_STATUS_SUCCESS;
+}
+
+static csi_status_t
+_pattern (csi_t *ctx)
+{
+    csi_object_t obj;
+    csi_status_t status;
+    cairo_surface_t *surface;
+
+    check (1);
+
+    status = _csi_ostack_get_surface (ctx, 0, &surface);
+    if (_csi_unlikely (status))
+       return status;
+
+    obj.type = CSI_OBJECT_TYPE_PATTERN;
+    obj.datum.pattern = cairo_pattern_create_for_surface (surface);
+
+    pop (1);
+    return push (&obj);
+}
+
+static csi_status_t
+_pop (csi_t *ctx)
+{
+    check (1);
+    pop (1);
+
+    return CSI_STATUS_SUCCESS;
+}
+
+static csi_status_t
+_pop_group (csi_t *ctx)
+{
+    csi_object_t obj;
+    csi_status_t status;
+    cairo_t *cr;
+
+    check (1);
+
+    status = _csi_ostack_get_context (ctx, 0, &cr);
+    if (_csi_unlikely (status))
+       return status;
+
+    obj.type = CSI_OBJECT_TYPE_PATTERN;
+    obj.datum.pattern = cairo_pop_group (cr);
+
+    return push (&obj);
+}
+
+static csi_status_t
+_push_group (csi_t *ctx)
+{
+    csi_status_t status;
+    cairo_t *cr;
+    long content;
+
+    check (2);
+
+    status = _csi_ostack_get_integer (ctx, 0, &content);
+    if (_csi_unlikely (status))
+       return status;
+
+    status = _csi_ostack_get_context (ctx, 1, &cr);
+    if (_csi_unlikely (status))
+       return status;
+
+    cairo_push_group_with_content (cr, content);
+    pop (1);
+    return CSI_STATUS_SUCCESS;
+}
+
+static csi_status_t
+_radial (csi_t *ctx)
+{
+    csi_object_t obj;
+    csi_status_t status;
+    double x1, y1, r1, x2, y2, r2;
+
+    check (6);
+
+    status = _csi_ostack_get_number (ctx, 0, &r2);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_number (ctx, 1, &y2);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_number (ctx, 2, &x2);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_number (ctx, 3, &r1);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_number (ctx, 4, &y1);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_number (ctx, 5, &x1);
+    if (_csi_unlikely (status))
+       return status;
+
+    obj.type = CSI_OBJECT_TYPE_PATTERN;
+    obj.datum.pattern = cairo_pattern_create_radial (x1, y1, r1, x2, y2, r2);
+    pop (6);
+    return push (&obj);
+}
+
+static csi_status_t
+_rectangle (csi_t *ctx)
+{
+    csi_status_t status;
+    double x, y;
+    double w, h;
+    cairo_t *cr;
+
+    check (5);
+
+    status = _csi_ostack_get_number (ctx, 0, &h);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_number (ctx, 1, &w);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_number (ctx, 2, &y);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_number (ctx, 3, &x);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_context (ctx, 4, &cr);
+    if (_csi_unlikely (status))
+       return status;
+
+    /* XXX path object */
+
+    cairo_rectangle (cr, x, y, w, h);
+    pop(4);
+    return CSI_STATUS_SUCCESS;
+}
+
+static csi_status_t
+_rel_curve_to (csi_t *ctx)
+{
+    csi_status_t status;
+    double x1, y1;
+    double x2, y2;
+    double x3, y3;
+    cairo_t *cr;
+
+    check (7);
+
+    status = _csi_ostack_get_number (ctx, 0, &y3);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_number (ctx, 1, &x3);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_number (ctx, 2, &y2);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_number (ctx, 3, &x2);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_number (ctx, 4, &y1);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_number (ctx, 5, &x1);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_context (ctx, 6, &cr);
+    if (_csi_unlikely (status))
+       return status;
+
+    /* XXX path object */
+
+    cairo_rel_curve_to (cr, x1, y1, x2, y2, x3, y3);
+    pop (6);
+    return CSI_STATUS_SUCCESS;
+}
+
+static csi_status_t
+_rel_line_to (csi_t *ctx)
+{
+    csi_status_t status;
+    double x, y;
+    cairo_t *cr;
+
+    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_context (ctx, 2, &cr);
+    if (_csi_unlikely (status))
+       return status;
+
+    /* XXX path object */
+
+    cairo_rel_line_to (cr, x, y);
+    pop (2);
+    return CSI_STATUS_SUCCESS;
+}
+
+static csi_status_t
+_rel_move_to (csi_t *ctx)
+{
+    csi_status_t status;
+    double x, y;
+    cairo_t *cr;
+
+    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_context (ctx, 2, &cr);
+    if (_csi_unlikely (status))
+       return status;
+
+    /* XXX path object */
+    cairo_rel_move_to (cr, x, y);
+    pop (2);
+    return CSI_STATUS_SUCCESS;
+}
+
+static csi_status_t
+_repeat (csi_t *ctx)
+{
+    csi_array_t *proc;
+    csi_integer_t count;
+    csi_status_t status;
+
+    check (2);
+
+    status = _csi_ostack_get_procedure (ctx, 0, &proc);
+    if (_csi_unlikely (status))
+       return status;
+
+    status = _csi_ostack_get_integer (ctx, 1, &count);
+    if (_csi_unlikely (status))
+       return status;
+
+    if (_csi_unlikely (count < 0))
+       return _csi_error (CSI_STATUS_INVALID_SCRIPT);
+
+    proc->base.ref++;
+    pop (2);
+
+    while (count--) {
+       status = _csi_array_execute (ctx, proc);
+       if (_csi_unlikely (status))
+           break;
+    }
+
+    if (--proc->base.ref == 0)
+       csi_array_free (ctx, proc);
+
+    return status;
+}
+
+static csi_status_t
+_reset_clip (csi_t *ctx)
+{
+    return _do_cairo_op (ctx, cairo_reset_clip);
+}
+
+static csi_status_t
+_restore (csi_t *ctx)
+{
+    return _do_cairo_op (ctx, cairo_restore);
+}
+
+static csi_status_t
+_rgb (csi_t *ctx)
+{
+    csi_object_t obj;
+    csi_status_t status;
+    double r,g,b;
+
+    check (3);
+
+    status = _csi_ostack_get_number (ctx, 0, &b);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_number (ctx, 1, &g);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_number (ctx, 2, &r);
+    if (_csi_unlikely (status))
+       return status;
+
+    obj.type = CSI_OBJECT_TYPE_PATTERN;
+    obj.datum.pattern = cairo_pattern_create_rgb (r, g, b);
+    pop (3);
+    return push (&obj);
+}
+
+static csi_status_t
+_rgba (csi_t *ctx)
+{
+    csi_object_t obj;
+    csi_status_t status;
+    double r,g,b,a;
+
+    check (4);
+
+    status = _csi_ostack_get_number (ctx, 0, &a);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_number (ctx, 1, &b);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_number (ctx, 2, &g);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_number (ctx, 3, &r);
+    if (_csi_unlikely (status))
+       return status;
+
+    obj.type = CSI_OBJECT_TYPE_PATTERN;
+    obj.datum.pattern = cairo_pattern_create_rgba (r, g, b, a);
+    pop (4);
+    return push (&obj);
+}
+
+static csi_status_t
+_roll (csi_t *ctx)
+{
+    csi_status_t status;
+    long j, n;
+
+    check (2);
+
+    status = _csi_ostack_get_integer (ctx, 0, &j);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_integer (ctx, 1, &n);
+    if (_csi_unlikely (status))
+       return status;
+
+    pop (2);
+    check (n);
+    return _csi_stack_roll (ctx, &ctx->ostack, j, n);
+}
+
+static csi_status_t
+_rotate (csi_t *ctx)
+{
+    csi_object_t *obj;
+    csi_status_t status;
+    double theta;
+    int type;
+
+    check (2);
+
+    status = _csi_ostack_get_number (ctx, 0, &theta);
+    if (_csi_unlikely (status))
+       return status;
+
+    obj = _csi_peek_ostack (ctx, 1);
+    type = csi_object_get_type (obj);
+    switch (type) {
+    case CSI_OBJECT_TYPE_CONTEXT:
+       cairo_rotate (obj->datum.cr, theta);
+       break;
+
+    case CSI_OBJECT_TYPE_PATTERN:
+       {
+           cairo_matrix_t ctm;
+           cairo_pattern_get_matrix (obj->datum.pattern, &ctm);
+           cairo_matrix_rotate (&ctm, theta);
+           cairo_pattern_set_matrix (obj->datum.pattern, &ctm);
+       }
+       break;
+
+
+    case CSI_OBJECT_TYPE_MATRIX:
+       cairo_matrix_rotate (&obj->datum.matrix->matrix, theta);
+       break;
+
+    default:
+       return _csi_error (CSI_STATUS_INVALID_SCRIPT);
+    }
+
+    pop (1);
+    return CSI_STATUS_SUCCESS;
+}
+
+static csi_status_t
+_save (csi_t *ctx)
+{
+    return _do_cairo_op (ctx, cairo_save);
+}
+
+static csi_status_t
+_scale (csi_t *ctx)
+{
+    csi_object_t *obj;
+    csi_status_t status;
+    double x, y;
+    int type;
+
+    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;
+
+    obj = _csi_peek_ostack (ctx, 2);
+    type = csi_object_get_type (obj);
+    switch (type) {
+    case CSI_OBJECT_TYPE_CONTEXT:
+       cairo_scale (obj->datum.cr, x, y);
+       break;
+
+    case CSI_OBJECT_TYPE_PATTERN:
+       {
+           cairo_matrix_t ctm;
+           cairo_pattern_get_matrix (obj->datum.pattern, &ctm);
+           cairo_matrix_scale (&ctm, x, y);
+           cairo_pattern_set_matrix (obj->datum.pattern, &ctm);
+       }
+       break;
+
+
+    case CSI_OBJECT_TYPE_MATRIX:
+       cairo_matrix_scale (&obj->datum.matrix->matrix, x, y);
+       break;
+
+    default:
+       return _csi_error (CSI_STATUS_INVALID_SCRIPT);
+    }
+
+    pop (2);
+    return CSI_STATUS_SUCCESS;
+}
+
+static csi_status_t
+_font_options_load_from_dictionary (csi_t *ctx,
+                                   csi_dictionary_t *dict,
+                                   cairo_font_options_t *options)
+{
+    const struct {
+       const char *key;
+       void (*setter) (cairo_font_options_t *, int val);
+    } properties[] = {
+       { "antialias",
+           (void (*)(cairo_font_options_t *, int val))
+               cairo_font_options_set_antialias },
+       { "subpixel-order",
+           (void (*)(cairo_font_options_t *, int val))
+               cairo_font_options_set_subpixel_order },
+       { "hint-style",
+           (void (*)(cairo_font_options_t *, int val))
+               cairo_font_options_set_hint_style },
+       { "hint-metrics",
+           (void (*)(cairo_font_options_t *, int val))
+               cairo_font_options_set_hint_metrics },
+       { NULL, NULL },
+    }, *prop = properties;
+
+    while (prop->key != NULL) {
+       csi_object_t key, value;
+       csi_status_t status;
+
+       status = csi_name_new_static (ctx, &key, prop->key);
+       if (_csi_unlikely (status))
+           return status;
+
+       if (csi_dictionary_has (dict, key.datum.name)) {
+           status = csi_dictionary_get (ctx, dict, key.datum.name, &value);
+           if (_csi_unlikely (status))
+               return status;
+
+           if (_csi_unlikely (csi_object_get_type (&value) !=
+                                CSI_OBJECT_TYPE_INTEGER))
+           {
+               csi_object_free (ctx, &value);
+               return _csi_error (CSI_STATUS_INVALID_SCRIPT);
+           }
+
+           prop->setter (options, value.datum.integer);
+       }
+
+       prop++;
+    }
+
+    return CSI_STATUS_SUCCESS;
+}
+
+static csi_status_t
+_scaled_font (csi_t *ctx)
+{
+    csi_object_t obj;
+    csi_dictionary_t *dict;
+    cairo_font_face_t *font_face = NULL; /* silence the compiler */
+    cairo_matrix_t font_matrix, ctm;
+    cairo_font_options_t *options;
+    csi_status_t status;
+
+    check (4);
+
+    status = _csi_ostack_get_dictionary (ctx, 0, &dict);
+    if (_csi_unlikely (status))
+       return status;
+    options = cairo_font_options_create ();
+    status = _font_options_load_from_dictionary (ctx, dict, options);
+    if (_csi_unlikely (status)) {
+       cairo_font_options_destroy (options);
+       return status;
+    }
+
+    status = _csi_ostack_get_matrix (ctx, 1, &ctm);
+    if (_csi_unlikely (status)) {
+       cairo_font_options_destroy (options);
+       return status;
+    }
+
+    status = _csi_ostack_get_matrix (ctx, 2, &font_matrix);
+    if (_csi_unlikely (status)) {
+       cairo_font_options_destroy (options);
+       return status;
+    }
+
+    status = _csi_ostack_get_font_face (ctx, 3, &font_face);
+    if (_csi_unlikely (status)) {
+       cairo_font_options_destroy (options);
+       return status;
+    }
+
+    obj.type = CSI_OBJECT_TYPE_SCALED_FONT;
+    obj.datum.scaled_font = cairo_scaled_font_create (font_face,
+                                                     &font_matrix,
+                                                     &ctm,
+                                                     options);
+    cairo_font_options_destroy (options);
+    pop (4);
+    return push (&obj);
+}
+
+static csi_status_t
+_select_font_face (csi_t *ctx)
+{
+    cairo_t *cr;
+    long weight;
+    long slant;
+    csi_string_t *family;
+    csi_status_t status;
+
+    check (4);
+
+    status = _csi_ostack_get_integer (ctx, 0,  &weight);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_integer (ctx, 1, &slant);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_string (ctx, 2, &family);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_context (ctx, 3, &cr);
+    if (_csi_unlikely (status))
+       return status;
+
+    cairo_select_font_face (cr, family->string, slant, weight);
+    pop (3);
+    return CSI_STATUS_SUCCESS;
+}
+
+static csi_status_t
+_context_set (csi_t *ctx,
+             cairo_t *cr,
+             csi_name_t key,
+             csi_object_t *obj)
+{
+    if (strcmp ((char *) key, "source") == 0) {
+       if (_csi_unlikely (csi_object_get_type (obj) !=
+                            CSI_OBJECT_TYPE_PATTERN))
+           return _csi_error (CSI_STATUS_INVALID_SCRIPT);
+
+       cairo_set_source (cr, obj->datum.pattern);
+       return CSI_STATUS_SUCCESS;
+    }
+
+    if (strcmp ((char *) key, "scaled-font") == 0) {
+       if (_csi_unlikely (csi_object_get_type (obj) !=
+                            CSI_OBJECT_TYPE_SCALED_FONT))
+           return _csi_error (CSI_STATUS_INVALID_SCRIPT);
+
+       cairo_set_scaled_font (cr, obj->datum.scaled_font);
+       return CSI_STATUS_SUCCESS;
+    }
+
+    if (strcmp ((char *) key, "font-face") == 0) {
+       if (_csi_unlikely (csi_object_get_type (obj) !=
+                            CSI_OBJECT_TYPE_FONT))
+           return _csi_error (CSI_STATUS_INVALID_SCRIPT);
+
+       cairo_set_font_face (cr, obj->datum.font_face);
+       return CSI_STATUS_SUCCESS;
+    }
+
+    /* return _proxy_set()? */
+    return _csi_error (CSI_STATUS_INVALID_SCRIPT);
+}
+
+static csi_status_t
+_set (csi_t *ctx)
+{
+    csi_object_t *key, *value, *dst;
+    csi_status_t status;
+    int type;
+
+    check (3);
+
+    value = _csi_peek_ostack (ctx, 0);
+    key = _csi_peek_ostack (ctx, 1);
+    dst = _csi_peek_ostack (ctx, 2);
+
+    type = csi_object_get_type (dst);
+    switch (type) {
+    case CSI_OBJECT_TYPE_DICTIONARY:
+       if (_csi_unlikely (csi_object_get_type (key) !=
+                            CSI_OBJECT_TYPE_NAME))
+           return _csi_error (CSI_STATUS_INVALID_SCRIPT);
+
+       status = csi_dictionary_put (ctx,
+                                    dst->datum.dictionary,
+                                    key->datum.name,
+                                    value);
+       break;
+    case CSI_OBJECT_TYPE_ARRAY:
+       if (_csi_unlikely (csi_object_get_type (key) !=
+                            CSI_OBJECT_TYPE_INTEGER))
+           return _csi_error (CSI_STATUS_INVALID_SCRIPT);
+
+       status = csi_array_put (ctx,
+                               dst->datum.array,
+                               key->datum.integer,
+                               value);
+       break;
+
+    case CSI_OBJECT_TYPE_CONTEXT:
+       if (_csi_unlikely (csi_object_get_type (key) !=
+                            CSI_OBJECT_TYPE_NAME))
+           return _csi_error (CSI_STATUS_INVALID_SCRIPT);
+
+       status = _context_set (ctx,
+                              dst->datum.cr,
+                              key->datum.name,
+                              value);
+       break;
+
+    case CSI_OBJECT_TYPE_STRING:
+#if 0
+       status = csi_string_put (dst, key, value);
+       break;
+#endif
+    default:
+       return _csi_error (CSI_STATUS_INVALID_SCRIPT);
+    }
+
+    pop (2);
+    return status;
+}
+
+static csi_status_t
+_set_antialias (csi_t *ctx)
+{
+    csi_status_t status;
+    cairo_t *cr;
+    long antialias;
+
+    check (2);
+
+    status = _csi_ostack_get_integer (ctx, 0, &antialias);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_context (ctx, 1, &cr);
+    if (_csi_unlikely (status))
+       return status;
+
+    cairo_set_antialias (cr, antialias);
+    pop (1);
+    return CSI_STATUS_SUCCESS;
+}
+
+static csi_status_t
+_set_dash (csi_t *ctx)
+{
+    csi_array_t *array;
+    csi_status_t status;
+    cairo_t *cr;
+    double offset;
+
+    check (3);
+
+    status = _csi_ostack_get_number (ctx, 0, &offset);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_array (ctx, 1, &array);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_context (ctx, 2, &cr);
+    if (_csi_unlikely (status))
+       return status;
+
+    if (array->stack.len == 0) {
+       cairo_set_dash (cr, NULL, 0., 0.);
+    } else {
+       double stack_dashes[8];
+       double *dashes;
+       csi_integer_t n;
+
+       if (_csi_likely (array->stack.len < ARRAY_LENGTH (stack_dashes))) {
+           dashes = stack_dashes;
+       } else {
+       if (_csi_unlikely ((unsigned) array->stack.len >= INT_MAX / sizeof (double)))
+           return _csi_error (CSI_STATUS_NO_MEMORY);
+           dashes = _csi_alloc (ctx, sizeof (double) * array->stack.len);
+           if (_csi_unlikely (dashes == NULL))
+               return _csi_error (CSI_STATUS_NO_MEMORY);
+       }
+
+       for (n = 0; n < array->stack.len; n++) {
+           if (_csi_unlikely (! csi_object_is_number
+                                (&array->stack.objects[n])))
+           {
+               if (dashes != stack_dashes)
+                   _csi_free (ctx, dashes);
+               return _csi_error (CSI_STATUS_INVALID_SCRIPT);
+           }
+
+           dashes[n] = csi_number_get_value (&array->stack.objects[n]);
+       }
+
+       cairo_set_dash (cr, dashes, n, offset);
+
+       if (dashes != stack_dashes)
+           _csi_free (ctx, dashes);
+    }
+
+    pop (2);
+    return CSI_STATUS_SUCCESS;
+}
+
+static csi_status_t
+_set_device_offset (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_offset (surface, x, y);
+    pop (2);
+    return CSI_STATUS_SUCCESS;
+}
+
+static csi_status_t
+_set_extend (csi_t *ctx)
+{
+    csi_status_t status;
+    csi_object_t *obj;
+    long extend;
+    int type;
+
+    check (2);
+
+    status = _csi_ostack_get_integer (ctx, 0, &extend);
+    if (_csi_unlikely (status))
+       return status;
+
+    obj = _csi_peek_ostack (ctx, 1);
+    type = csi_object_get_type (obj);
+    switch (type) {
+    case CSI_OBJECT_TYPE_CONTEXT:
+       cairo_pattern_set_extend (cairo_get_source (obj->datum.cr),
+                                 extend);
+       break;
+    case CSI_OBJECT_TYPE_PATTERN:
+       cairo_pattern_set_extend (obj->datum.pattern, extend);
+       break;
+    default:
+       return _csi_error (CSI_STATUS_INVALID_SCRIPT);
+    }
+
+    pop (1);
+    return CSI_STATUS_SUCCESS;
+}
+
+static csi_status_t
+_set_fallback_resolution (csi_t *ctx)
+{
+    csi_status_t status;
+    cairo_surface_t *surface;
+    double dpi_x, dpi_y;
+
+    check (3);
+
+    status = _csi_ostack_get_number (ctx, 0, &dpi_y);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_number (ctx, 1, &dpi_x);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_surface (ctx, 2, &surface);
+    if (_csi_unlikely (status))
+       return status;
+
+    cairo_surface_set_fallback_resolution (surface, dpi_x, dpi_y);
+    pop (2);
+    return CSI_STATUS_SUCCESS;
+}
+
+static csi_status_t
+_set_fill_rule (csi_t *ctx)
+{
+    csi_status_t status;
+    cairo_t *cr;
+    long fill_rule;
+
+    check (2);
+
+    status = _csi_ostack_get_integer (ctx, 0, &fill_rule);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_context (ctx, 1, &cr);
+    if (_csi_unlikely (status))
+       return status;
+
+    cairo_set_fill_rule (cr, fill_rule);
+    pop (1);
+    return CSI_STATUS_SUCCESS;
+}
+
+static csi_status_t
+_set_filter (csi_t *ctx)
+{
+    csi_status_t status;
+    csi_object_t *obj;
+    long filter;
+    int type;
+
+    check (2);
+
+    status = _csi_ostack_get_integer (ctx, 0, &filter);
+    if (_csi_unlikely (status))
+       return status;
+
+    obj = _csi_peek_ostack (ctx, 1);
+    type = csi_object_get_type (obj);
+    switch (type) {
+    case CSI_OBJECT_TYPE_CONTEXT:
+       cairo_pattern_set_filter (cairo_get_source (obj->datum.cr),
+                                 filter);
+       break;
+    case CSI_OBJECT_TYPE_PATTERN:
+       cairo_pattern_set_filter (obj->datum.pattern, filter);
+       break;
+    default:
+       return _csi_error (CSI_STATUS_INVALID_SCRIPT);
+    }
+
+    pop (1);
+    return CSI_STATUS_SUCCESS;
+}
+
+static csi_status_t
+_set_font_face (csi_t *ctx)
+{
+    cairo_t *cr;
+    cairo_font_face_t *font = NULL; /* silence the compiler */
+    csi_status_t status;
+
+    check (2);
+
+    status = _csi_ostack_get_font_face (ctx, 0, &font);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_context (ctx, 1, &cr);
+    if (_csi_unlikely (status))
+       return status;
+
+    cairo_set_font_face (cr, font);
+    pop (1);
+    return CSI_STATUS_SUCCESS;
+}
+
+static csi_status_t
+_set_font_options (csi_t *ctx)
+{
+    csi_status_t status;
+    cairo_t *cr;
+    csi_dictionary_t *dict;
+    cairo_font_options_t *options;
+
+    check (2);
+
+    status = _csi_ostack_get_dictionary (ctx, 0, &dict);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_context (ctx, 1, &cr);
+    if (_csi_unlikely (status))
+       return status;
+
+    options = cairo_font_options_create ();
+    status = _font_options_load_from_dictionary (ctx, dict, options);
+    if (_csi_unlikely (status))
+       return status;
+
+    cairo_set_font_options (cr, options);
+    cairo_font_options_destroy (options);
+    pop (1);
+    return CSI_STATUS_SUCCESS;
+}
+
+static csi_status_t
+_set_font_matrix (csi_t *ctx)
+{
+    csi_status_t status;
+    cairo_t *cr;
+    cairo_matrix_t m;
+
+    check (2);
+
+    status = _csi_ostack_get_matrix (ctx, 0, &m);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_context (ctx, 1, &cr);
+    if (_csi_unlikely (status))
+       return status;
+
+    cairo_set_font_matrix (cr, &m);
+    pop(1);
+    return CSI_STATUS_SUCCESS;
+}
+
+static csi_status_t
+_set_font_size (csi_t *ctx)
+{
+    csi_status_t status;
+    cairo_t *cr;
+    double size;
+
+    check (2);
+
+    status = _csi_ostack_get_number (ctx, 0, &size);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_context (ctx, 1, &cr);
+    if (_csi_unlikely (status))
+       return status;
+
+    cairo_set_font_size (cr, size);
+    pop (1);
+    return CSI_STATUS_SUCCESS;
+}
+
+static csi_status_t
+_set_line_cap (csi_t *ctx)
+{
+    csi_status_t status;
+    cairo_t *cr;
+    long line_cap;
+
+    check (2);
+
+    status = _csi_ostack_get_integer (ctx, 0, &line_cap);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_context (ctx, 1, &cr);
+    if (_csi_unlikely (status))
+       return status;
+
+    cairo_set_line_cap (cr, line_cap);
+    pop (1);
+    return CSI_STATUS_SUCCESS;
+}
+
+static csi_status_t
+_set_line_join (csi_t *ctx)
+{
+    csi_status_t status;
+    cairo_t *cr;
+    long line_join;
+
+    status = _csi_ostack_get_integer (ctx, 0, &line_join);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_context (ctx, 1, &cr);
+    if (_csi_unlikely (status))
+       return status;
+
+    cairo_set_line_join (cr, line_join);
+    pop (1);
+    return CSI_STATUS_SUCCESS;
+}
+
+static csi_status_t
+_set_line_width (csi_t *ctx)
+{
+    csi_status_t status;
+    cairo_t *cr;
+    double line_width;
+
+    check (2);
+
+    status = _csi_ostack_get_number (ctx, 0, &line_width);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_context (ctx, 1, &cr);
+    if (_csi_unlikely (status))
+       return status;
+
+    cairo_set_line_width (cr, line_width);
+    pop (1);
+    return CSI_STATUS_SUCCESS;
+}
+
+static csi_status_t
+_set_matrix (csi_t *ctx)
+{
+    csi_object_t *obj;
+    csi_status_t status;
+    cairo_matrix_t m;
+    int type;
+
+    check (2);
+
+    status = _csi_ostack_get_matrix (ctx, 0, &m);
+    if (_csi_unlikely (status))
+       return status;
+
+    obj = _csi_peek_ostack (ctx, 1);
+    type = csi_object_get_type (obj);
+    switch (type) {
+    case CSI_OBJECT_TYPE_CONTEXT:
+       cairo_set_matrix (obj->datum.cr, &m);
+       break;
+    case CSI_OBJECT_TYPE_PATTERN:
+       cairo_pattern_set_matrix (obj->datum.pattern, &m);
+       break;
+    case CSI_OBJECT_TYPE_MATRIX:
+       obj->datum.matrix->matrix = m;
+       break;
+    default:
+       return _csi_error (CSI_STATUS_INVALID_SCRIPT);
+    }
+
+    pop (1);
+    return CSI_STATUS_SUCCESS;
+}
+
+struct _mime_tag {
+    csi_t *ctx;
+    csi_string_t *source;
+};
+static void
+_mime_tag_destroy (void *closure)
+{
+    struct _mime_tag *tag = closure;
+
+    if (--tag->source->base.ref)
+       csi_string_free (tag->ctx, tag->source);
+
+    _csi_slab_free (tag->ctx, tag, sizeof (struct _mime_tag));
+}
+
+static csi_status_t
+_set_mime_data (csi_t *ctx)
+{
+    csi_status_t status;
+    csi_object_t *obj;
+    const char *mime = NULL; /* silence the compiler */
+    csi_object_t source;
+    cairo_surface_t *surface;
+    struct _mime_tag *tag;
+    int type;
+
+    check (3);
+
+    obj = _csi_peek_ostack (ctx, 0);
+    type = csi_object_get_type (obj);
+    switch (type) {
+    case CSI_OBJECT_TYPE_FILE:
+       status = _csi_file_as_string (ctx, obj->datum.file, &source);
+       if (_csi_unlikely (status))
+           return status;
+
+       break;
+
+    case CSI_OBJECT_TYPE_STRING:
+       source = *csi_object_reference (obj);
+       break;
+
+    default:
+       return _csi_error (CSI_STATUS_INVALID_SCRIPT);
+    }
+
+    status = _csi_ostack_get_string_constant (ctx, 1, &mime);
+    if (_csi_unlikely (status))
+       return status;
+
+    status = _csi_ostack_get_surface (ctx, 2, &surface);
+    if (_csi_unlikely (status))
+       return status;
+
+
+    /* XXX free source */
+    tag = _csi_slab_alloc (ctx, sizeof (struct _mime_tag));
+    if (_csi_unlikely (tag == NULL))
+       return _csi_error (CSI_STATUS_NO_MEMORY);
+    tag->ctx = cairo_script_interpreter_reference (ctx);
+    tag->source = source.datum.string;
+    tag->source->base.ref++;
+
+    status = cairo_surface_set_mime_data (surface,
+                                         mime,
+                                         (uint8_t *)
+                                         source.datum.string->string,
+                                         source.datum.string->len,
+                                         _mime_tag_destroy, tag);
+    if (_csi_unlikely (status)) {
+       _mime_tag_destroy (tag);
+       return status;
+    }
+
+    pop (2);
+    return CSI_STATUS_SUCCESS;
+}
+
+static csi_status_t
+_set_miter_limit (csi_t *ctx)
+{
+    csi_status_t status;
+    cairo_t *cr;
+    double miter_limit;
+
+    check (2);
+
+    status = _csi_ostack_get_number (ctx, 0, &miter_limit);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_context (ctx, 1, &cr);
+    if (_csi_unlikely (status))
+       return status;
+
+    cairo_set_miter_limit (cr, miter_limit);
+    pop (1);
+    return CSI_STATUS_SUCCESS;
+}
+
+static csi_status_t
+_set_operator (csi_t *ctx)
+{
+    cairo_t *cr;
+    long val;
+    csi_status_t status;
+
+    check (2);
+
+    status = _csi_ostack_get_integer (ctx, 0, &val);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_context (ctx, 1, &cr);
+    if (_csi_unlikely (status))
+       return status;
+
+    cairo_set_operator (cr, val);
+    pop (1);
+    return CSI_STATUS_SUCCESS;
+}
+
+static csi_status_t
+_set_scaled_font (csi_t *ctx)
+{
+    cairo_t *cr;
+    cairo_scaled_font_t *font = NULL; /* silence the compiler */
+    csi_status_t status;
+
+    check (2);
+
+    status = _csi_ostack_get_scaled_font (ctx, 0, &font);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_context (ctx, 1, &cr);
+    if (_csi_unlikely (status))
+       return status;
+
+    cairo_set_scaled_font (cr, font);
+    pop (1);
+    return CSI_STATUS_SUCCESS;
+}
+
+static csi_status_t
+_set_source (csi_t *ctx)
+{
+    cairo_t *cr;
+    cairo_pattern_t *pattern = NULL; /* silence the compiler */
+    csi_status_t status;
+
+    check (2);
+
+    status = _csi_ostack_get_pattern (ctx, 0, &pattern);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_context (ctx, 1, &cr);
+    if (_csi_unlikely (status))
+       return status;
+
+    cairo_set_source (cr, pattern);
+    pop (1);
+    return CSI_STATUS_SUCCESS;
+}
+
+static csi_boolean_t
+_matching_images (cairo_surface_t *a, cairo_surface_t *b)
+{
+    cairo_format_t format_a, format_b;
+
+    if (cairo_surface_get_type (a) != CAIRO_SURFACE_TYPE_IMAGE)
+       return FALSE;
+    if (cairo_surface_get_type (b) != CAIRO_SURFACE_TYPE_IMAGE)
+       return FALSE;
+
+    if (cairo_image_surface_get_height (a) != cairo_image_surface_get_height (b))
+       return FALSE;
+
+    if (cairo_image_surface_get_width (a) != cairo_image_surface_get_width (b))
+       return FALSE;
+
+    format_a = cairo_image_surface_get_format (a);
+    if (format_a == CAIRO_FORMAT_RGB24)
+       format_a = CAIRO_FORMAT_ARGB32;
+
+    format_b = cairo_image_surface_get_format (b);
+    if (format_b == CAIRO_FORMAT_RGB24)
+       format_b = CAIRO_FORMAT_ARGB32;
+
+    if (format_a != format_b)
+       return FALSE;
+
+    return TRUE;
+}
+
+static csi_status_t
+_set_source_image (csi_t *ctx)
+{
+    csi_status_t status;
+    cairo_surface_t *surface;
+    cairo_surface_t *source;
+
+    check (2);
+
+    status = _csi_ostack_get_surface (ctx, 0, &source);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_surface (ctx, 1, &surface);
+    if (_csi_unlikely (status))
+       return status;
+
+    /* Catch the most frequent use of simply uploading pixel data,
+     * principally to remove the pixman ops from the profiles.
+     */
+    if (_csi_likely (_matching_images (surface, source))) {
+       cairo_surface_flush (surface);
+       memcpy (cairo_image_surface_get_data (surface),
+               cairo_image_surface_get_data (source),
+               cairo_image_surface_get_height (source) * cairo_image_surface_get_stride (source));
+       cairo_surface_mark_dirty (surface);
+    } else {
+       cairo_t *cr;
+
+       cr = cairo_create (surface);
+       cairo_set_source_surface (cr, source, 0, 0);
+       cairo_paint (cr);
+       cairo_destroy (cr);
+    }
+
+    pop (1);
+    return CSI_STATUS_SUCCESS;
+}
+
+static csi_status_t
+_set_source_rgb (csi_t *ctx)
+{
+    csi_status_t status;
+    double r,g,b;
+    cairo_t *cr;
+
+    check (4);
+
+    status = _csi_ostack_get_number (ctx, 0, &b);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_number (ctx, 1, &g);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_number (ctx, 2, &r);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_context (ctx, 3, &cr);
+    if (_csi_unlikely (status))
+       return status;
+
+    cairo_set_source_rgb (cr, r, g, b);
+    pop (3);
+    return CSI_STATUS_SUCCESS;
+}
+
+static csi_status_t
+_set_source_rgba (csi_t *ctx)
+{
+    csi_status_t status;
+    double r,g,b,a;
+    cairo_t *cr;
+
+    check (5);
+
+    status = _csi_ostack_get_number (ctx, 0, &a);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_number (ctx, 1, &b);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_number (ctx, 2, &g);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_number (ctx, 3, &r);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_context (ctx, 4, &cr);
+    if (_csi_unlikely (status))
+       return status;
+
+    cairo_set_source_rgba (cr, r, g, b, a);
+    pop (4);
+    return CSI_STATUS_SUCCESS;
+}
+
+static csi_status_t
+_set_tolerance (csi_t *ctx)
+{
+    csi_status_t status;
+    cairo_t *cr;
+    double tolerance;
+
+    check (2);
+
+    status = _csi_ostack_get_number (ctx, 0, &tolerance);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_context (ctx, 1, &cr);
+    if (_csi_unlikely (status))
+       return status;
+
+    cairo_set_tolerance (cr, tolerance);
+    pop (1);
+    return CSI_STATUS_SUCCESS;
+}
+
+static csi_status_t
+_transform (csi_t *ctx)
+{
+    csi_object_t *obj;
+    csi_status_t status;
+    cairo_matrix_t m;
+    int type;
+
+    check (2);
+
+    status = _csi_ostack_get_matrix (ctx, 0, &m);
+    if (_csi_unlikely (status))
+       return status;
+
+    obj = _csi_peek_ostack (ctx, 1);
+    type = csi_object_get_type (obj);
+    switch (type) {
+    case CSI_OBJECT_TYPE_CONTEXT:
+       cairo_transform (obj->datum.cr, &m);
+       break;
+    case CSI_OBJECT_TYPE_PATTERN:
+       {
+           cairo_matrix_t ctm;
+           cairo_pattern_get_matrix (obj->datum.pattern, &ctm);
+           cairo_matrix_multiply (&ctm, &m, &ctm);
+           cairo_pattern_set_matrix (obj->datum.pattern, &ctm);
+       }
+       break;
+    case CSI_OBJECT_TYPE_MATRIX:
+           cairo_matrix_multiply (&obj->datum.matrix->matrix,
+                                  &m,
+                                  &obj->datum.matrix->matrix);
+       break;
+    default:
+       return _csi_error (CSI_STATUS_INVALID_SCRIPT);
+    }
+
+    pop (1);
+    return CSI_STATUS_SUCCESS;
+}
+
+static csi_status_t
+_translate (csi_t *ctx)
+{
+    csi_object_t *obj;
+    csi_status_t status;
+    double x, y;
+    int type;
+
+    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;
+
+    obj = _csi_peek_ostack (ctx, 2);
+    type = csi_object_get_type (obj);
+    switch (type) {
+    case CSI_OBJECT_TYPE_CONTEXT:
+       cairo_translate (obj->datum.cr, x, y);
+       break;
+
+    case CSI_OBJECT_TYPE_PATTERN:
+       {
+           cairo_matrix_t ctm;
+           cairo_pattern_get_matrix (obj->datum.pattern, &ctm);
+           cairo_matrix_translate (&ctm, x, y);
+           cairo_pattern_set_matrix (obj->datum.pattern, &ctm);
+       }
+       break;
+
+
+    case CSI_OBJECT_TYPE_MATRIX:
+       cairo_matrix_translate (&obj->datum.matrix->matrix, x, y);
+       break;
+
+    default:
+       return _csi_error (CSI_STATUS_INVALID_SCRIPT);
+    }
+
+    pop (2);
+    return CSI_STATUS_SUCCESS;
+}
+
+static csi_status_t
+_true (csi_t *ctx)
+{
+    return _csi_push_ostack_boolean (ctx, TRUE);
+}
+
+static csi_status_t
+_show_page (csi_t *ctx)
+{
+    csi_object_t *obj;
+    int type;
+
+    check (1);
+
+    obj = _csi_peek_ostack (ctx, 0);
+    type = csi_object_get_type (obj);
+    switch (type) {
+    case CSI_OBJECT_TYPE_CONTEXT:
+       cairo_show_page (obj->datum.cr);
+       if (ctx->hooks.copy_page != NULL)
+           ctx->hooks.copy_page (ctx->hooks.closure, obj->datum.cr);
+       break;
+    case CSI_OBJECT_TYPE_SURFACE:
+       cairo_surface_show_page (obj->datum.surface);
+       /* XXX hook? */
+       break;
+    default:
+       return _csi_error (CSI_STATUS_INVALID_SCRIPT);
+    }
+
+    return CSI_STATUS_SUCCESS;
+}
+
+static csi_status_t
+_similar (csi_t *ctx)
+{
+    csi_object_t obj;
+    long content;
+    double width, height;
+    cairo_surface_t *other;
+    csi_status_t status;
+
+    check (4);
+
+    status = _csi_ostack_get_integer (ctx, 0, &content);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_number (ctx, 1, &height);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_number (ctx, 2, &width);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_surface (ctx, 3, &other);
+    if (_csi_unlikely (status))
+       return status;
+
+    /* silently fix-up a common bug when writing CS */
+    if ((content & CAIRO_CONTENT_COLOR_ALPHA) == 0) {
+       if (_csi_unlikely (content & ~CAIRO_CONTENT_COLOR_ALPHA))
+           return _csi_error (CSI_STATUS_INVALID_SCRIPT);
+
+       switch ((int) content) {
+       default:
+       case CAIRO_FORMAT_ARGB32:
+           content = CAIRO_CONTENT_COLOR_ALPHA;
+           break;
+       case CAIRO_FORMAT_RGB16_565:
+       case CAIRO_FORMAT_RGB24:
+           content = CAIRO_CONTENT_COLOR;
+           break;
+       case CAIRO_FORMAT_A8:
+       case CAIRO_FORMAT_A1:
+           content = CAIRO_CONTENT_ALPHA;
+           break;
+       }
+    }
+
+    obj.type = CSI_OBJECT_TYPE_SURFACE;
+    obj.datum.surface = cairo_surface_create_similar (other,
+                                                     content, width, height);
+    pop (4);
+    return push (&obj);
+}
+
+static csi_status_t
+_similar_image (csi_t *ctx)
+{
+    csi_object_t obj;
+    long format;
+    double width, height;
+    cairo_surface_t *other;
+    csi_status_t status;
+
+    check (4);
+
+    status = _csi_ostack_get_number (ctx, 0, &height);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_number (ctx, 1, &width);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_integer (ctx, 2, &format);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_surface (ctx, 3, &other);
+    if (_csi_unlikely (status))
+       return status;
+
+    obj.type = CSI_OBJECT_TYPE_SURFACE;
+    obj.datum.surface = cairo_surface_create_similar_image (other,
+                                                           format,
+                                                           width, height);
+    pop (4);
+    return push (&obj);
+}
+
+static csi_status_t
+_subsurface (csi_t *ctx)
+{
+    csi_object_t obj;
+    double x, y, width, height;
+    cairo_surface_t *target;
+    csi_status_t status;
+
+    check (5);
+
+    status = _csi_ostack_get_number (ctx, 0, &height);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_number (ctx, 1, &width);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_number (ctx, 2, &y);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_number (ctx, 3, &x);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_surface (ctx, 4, &target);
+    if (_csi_unlikely (status))
+       return status;
+
+    obj.type = CSI_OBJECT_TYPE_SURFACE;
+    obj.datum.surface = cairo_surface_create_for_rectangle (target, x, y, width, height);
+    pop (5);
+    return push (&obj);
+}
+
+static csi_status_t
+_show_text (csi_t *ctx)
+{
+    csi_status_t status;
+    csi_string_t *text;
+    cairo_t *cr;
+
+    check (2);
+
+    status = _csi_ostack_get_string (ctx, 0, &text);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_context (ctx, 1, &cr);
+    if (_csi_unlikely (status))
+       return status;
+
+    cairo_show_text (cr, text->string);
+    pop (1);
+    return CSI_STATUS_SUCCESS;
+}
+
+static csi_status_t
+_show_glyphs (csi_t *ctx)
+{
+    csi_array_t *array;
+    csi_status_t status;
+    cairo_t *cr;
+    cairo_glyph_t stack_glyphs[256], *glyphs;
+    csi_integer_t nglyphs, i;
+
+    check (2);
+
+    status = _csi_ostack_get_array (ctx, 0, &array);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_context (ctx, 1, &cr);
+    if (_csi_unlikely (status))
+       return status;
+
+    /* count glyphs */
+    nglyphs = 0;
+    for (i = 0; i < array->stack.len; i++) {
+       csi_object_t *obj = &array->stack.objects[i];
+       int type = csi_object_get_type (obj);
+       switch (type) {
+       case CSI_OBJECT_TYPE_ARRAY:
+           nglyphs += obj->datum.array->stack.len;
+           break;
+       case CSI_OBJECT_TYPE_STRING:
+           nglyphs += obj->datum.string->len;
+           break;
+       }
+    }
+    if (nglyphs == 0) {
+       pop (1);
+       return CSI_STATUS_SUCCESS;
+    }
+
+    if (nglyphs > ARRAY_LENGTH (stack_glyphs)) {
+       if (_csi_unlikely ((unsigned) nglyphs >= INT_MAX / sizeof (cairo_glyph_t)))
+           return _csi_error (CSI_STATUS_NO_MEMORY);
+
+       glyphs = _csi_alloc (ctx, sizeof (cairo_glyph_t) * nglyphs);
+       if (_csi_unlikely (glyphs == NULL))
+           return _csi_error (CSI_STATUS_NO_MEMORY);
+    } else
+       glyphs = stack_glyphs;
+
+    nglyphs = _glyph_string (ctx, array, cairo_get_scaled_font (cr), glyphs);
+    cairo_show_glyphs (cr, glyphs, nglyphs);
+
+    if (glyphs != stack_glyphs)
+       _csi_free (ctx, glyphs);
+
+    pop (1);
+    return CSI_STATUS_SUCCESS;
+}
+
+static csi_status_t
+_show_text_glyphs (csi_t *ctx)
+{
+    csi_object_t *obj;
+    csi_array_t *array;
+    csi_string_t *string;
+    csi_string_t *utf8_string;
+    csi_status_t status;
+    cairo_t *cr;
+    cairo_text_cluster_t stack_clusters[256], *clusters;
+    cairo_glyph_t stack_glyphs[256], *glyphs;
+    csi_integer_t nglyphs, nclusters, i;
+    long direction;
+    int type;
+
+    check (5);
+
+    status = _csi_ostack_get_integer (ctx, 0, &direction);
+    if (_csi_unlikely (status))
+       return status;
+
+    obj = _csi_peek_ostack (ctx, 1);
+    type = csi_object_get_type (obj);
+    switch (type) {
+    case CSI_OBJECT_TYPE_ARRAY:
+       array = obj->datum.array;
+       nclusters = array->stack.len / 2;
+       if (nclusters > ARRAY_LENGTH (stack_clusters)) {
+           if (_csi_unlikely ((unsigned) nclusters >= INT_MAX / sizeof (cairo_text_cluster_t)))
+               return _csi_error (CSI_STATUS_NO_MEMORY);
+           clusters = _csi_alloc (ctx, sizeof (cairo_text_cluster_t) * nclusters);
+           if (_csi_unlikely (clusters == NULL))
+               return _csi_error (CSI_STATUS_NO_MEMORY);
+       } else
+           clusters = stack_clusters;
+
+       for (i = 0; i < nclusters; i++) {
+           clusters[i].num_bytes = csi_number_get_value (&array->stack.objects[2*i+0]);
+           clusters[i].num_glyphs = csi_number_get_value (&array->stack.objects[2*i+1]);
+       }
+       break;
+
+    case CSI_OBJECT_TYPE_STRING:
+       string = obj->datum.string;
+       nclusters = string->len / 2;
+       if (nclusters > ARRAY_LENGTH (stack_clusters)) {
+           if (_csi_unlikely ((unsigned) nclusters >= INT_MAX / sizeof (cairo_text_cluster_t)))
+               return _csi_error (CSI_STATUS_NO_MEMORY);
+           clusters = _csi_alloc (ctx, sizeof (cairo_text_cluster_t) * nclusters);
+           if (_csi_unlikely (clusters == NULL))
+               return _csi_error (CSI_STATUS_NO_MEMORY);
+       } else
+           clusters = stack_clusters;
+
+       for (i = 0; i < nclusters; i++) {
+           clusters[i].num_bytes = string->string[2*i+0];
+           clusters[i].num_glyphs = string->string[2*i+1];
+       }
+       break;
+
+    default:
+       return _csi_error (CSI_STATUS_INVALID_SCRIPT);
+    }
+
+    status = _csi_ostack_get_array (ctx, 2, &array);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_string (ctx, 3, &utf8_string);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_context (ctx, 4, &cr);
+    if (_csi_unlikely (status))
+       return status;
+
+    /* count glyphs */
+    nglyphs = 0;
+    for (i = 0; i < array->stack.len; i++) {
+       obj = &array->stack.objects[i];
+       type = csi_object_get_type (obj);
+       switch (type) {
+       case CSI_OBJECT_TYPE_ARRAY:
+           nglyphs += obj->datum.array->stack.len;
+           break;
+       case CSI_OBJECT_TYPE_STRING:
+           nglyphs += obj->datum.string->len;
+           break;
+       }
+    }
+    if (nglyphs == 0) {
+       pop (4);
+       return CSI_STATUS_SUCCESS;
+    }
+
+    if (nglyphs > ARRAY_LENGTH (stack_glyphs)) {
+       if (_csi_unlikely ((unsigned) nglyphs >= INT_MAX / sizeof (cairo_glyph_t)))
+           return _csi_error (CSI_STATUS_NO_MEMORY);
+
+       glyphs = _csi_alloc (ctx, sizeof (cairo_glyph_t) * nglyphs);
+       if (_csi_unlikely (glyphs == NULL))
+           return _csi_error (CSI_STATUS_NO_MEMORY);
+    } else
+       glyphs = stack_glyphs;
+
+    nglyphs = _glyph_string (ctx, array, cairo_get_scaled_font (cr), glyphs);
+    cairo_show_text_glyphs (cr,
+                           utf8_string->string, utf8_string->len,
+                           glyphs, nglyphs,
+                           clusters, nclusters,
+                           direction);
+
+    if (clusters != stack_clusters)
+       _csi_free (ctx, clusters);
+    if (glyphs != stack_glyphs)
+       _csi_free (ctx, glyphs);
+
+    pop (4);
+    return CSI_STATUS_SUCCESS;
+}
+
+static csi_status_t
+_stroke (csi_t *ctx)
+{
+    return _do_cairo_op (ctx, cairo_stroke);
+}
+
+static csi_status_t
+_stroke_preserve (csi_t *ctx)
+{
+    return _do_cairo_op (ctx, cairo_stroke_preserve);
+}
+
+static csi_status_t
+_sub (csi_t *ctx)
+{
+    csi_object_t *A;
+    csi_object_t *B;
+    csi_object_type_t type_a, type_b;
+
+    check (2);
+
+    B = _csi_peek_ostack (ctx, 0);
+    A = _csi_peek_ostack (ctx, 1);
+
+    type_a = csi_object_get_type (A);
+    if (_csi_unlikely (! (type_a == CSI_OBJECT_TYPE_INTEGER ||
+                           type_a == CSI_OBJECT_TYPE_REAL)))
+    {
+       return _csi_error (CSI_STATUS_INVALID_SCRIPT);
+    }
+
+    type_b = csi_object_get_type (B);
+    if (_csi_unlikely (! (type_b == CSI_OBJECT_TYPE_INTEGER ||
+                           type_b == CSI_OBJECT_TYPE_REAL)))
+    {
+       return _csi_error (CSI_STATUS_INVALID_SCRIPT);
+    }
+
+    pop (2);
+
+    if (type_a == CSI_OBJECT_TYPE_REAL &&
+       type_b == CSI_OBJECT_TYPE_REAL)
+    {
+       return _csi_push_ostack_real (ctx, A->datum.real - B->datum.real);
+
+    }
+    else if (type_a == CSI_OBJECT_TYPE_INTEGER &&
+            type_b == CSI_OBJECT_TYPE_INTEGER)
+    {
+       return _csi_push_ostack_integer (ctx,
+                                        A->datum.integer - B->datum.integer);
+    }
+    else
+    {
+       double v;
+
+       if (type_a == CSI_OBJECT_TYPE_REAL)
+           v = A->datum.real;
+       else
+           v = A->datum.integer;
+
+       if (type_b == CSI_OBJECT_TYPE_REAL)
+           v -= B->datum.real;
+       else
+           v -= B->datum.integer;
+
+       return _csi_push_ostack_real (ctx, v);
+    }
+}
+
+static csi_status_t
+_surface (csi_t *ctx)
+{
+    csi_object_t obj;
+    csi_dictionary_t *dict;
+    csi_proxy_t *proxy;
+    csi_object_t key;
+    double width, height;
+    csi_surface_create_func_t hook;
+    long content;
+    cairo_surface_t *surface;
+    long uid;
+    csi_status_t status;
+
+    check (1);
+
+    status = _csi_ostack_get_dictionary (ctx, 0, &dict);
+    if (_csi_unlikely (status))
+       return status;
+
+    status = _csi_dictionary_get_number (ctx, dict, "width", FALSE, &width);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_dictionary_get_number (ctx, dict, "height", FALSE, &height);
+    if (_csi_unlikely (status))
+       return status;
+
+    content = CAIRO_CONTENT_COLOR_ALPHA;
+    status = _csi_dictionary_get_integer (ctx, dict, "content", TRUE, &content);
+    if (_csi_unlikely (status))
+       return status;
+
+    uid = 0;
+    status = _csi_dictionary_get_integer (ctx, dict, "uid", TRUE, &uid);
+    if (_csi_unlikely (status))
+       return status;
+    if (uid == 0) {
+       status = _csi_dictionary_get_integer (ctx, dict, "drawable", TRUE, &uid);
+       if (_csi_unlikely (status))
+           return status;
+    }
+
+    hook = ctx->hooks.surface_create;
+    assert (hook != NULL);
+
+    surface = hook (ctx->hooks.closure, content, width, height, uid);
+    if (_csi_unlikely (surface == NULL)) {
+       return _csi_error (CSI_STATUS_NULL_POINTER);
+    }
+
+    proxy = _csi_proxy_create (ctx, surface, dict,
+                              ctx->hooks.surface_destroy,
+                              ctx->hooks.closure);
+    if (_csi_unlikely (proxy == NULL)) {
+       cairo_surface_destroy (surface);
+       return _csi_error (CSI_STATUS_NO_MEMORY);
+    }
+
+    status = cairo_surface_set_user_data (surface,
+                                         &_csi_proxy_key,
+                                         proxy, _csi_proxy_destroy);
+    if (_csi_unlikely (status)) {
+       _csi_proxy_destroy (proxy);
+       cairo_surface_destroy (surface);
+       return status;
+    }
+
+    status = csi_name_new_static (ctx, &key, "fallback-resolution");
+    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)) {
+           cairo_surface_destroy (surface);
+           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_fallback_resolution (surface,
+                                                      csi_number_get_value
+                                                      (&array->stack.objects[0]),
+                                                      csi_number_get_value
+                                                      (&array->stack.objects[1]));
+           }
+       }
+    }
+    /* initialise surface to source */
+    status = csi_name_new_static (ctx, &key, "source");
+    if (_csi_unlikely (status)) {
+       cairo_surface_destroy (surface);
+       return status;
+    }
+    if (csi_dictionary_has (dict, key.datum.name)) {
+       cairo_surface_t *image;
+       cairo_t *cr;
+
+       status = _image_load_from_dictionary (ctx, dict, &image);
+       if (_csi_unlikely (status)) {
+           cairo_surface_destroy (surface);
+           return status;
+       }
+
+       cr = cairo_create (surface);
+       cairo_set_source_surface (cr, image, 0, 0);
+       cairo_surface_destroy (image);
+       cairo_paint (cr);
+       status = cairo_status (cr);
+       cairo_destroy (cr);
+
+       if (_csi_unlikely (status))
+           return status;
+    }
+
+    status = csi_name_new_static (ctx, &key, "device-offset");
+    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_offset (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);
+    return push (&obj);
+}
+
+static csi_status_t
+_record (csi_t *ctx)
+{
+    csi_object_t obj;
+    long content;
+    csi_array_t *array;
+    csi_status_t status;
+    cairo_rectangle_t extents;
+    cairo_rectangle_t *r;
+
+    check (2);
+
+    status = _csi_ostack_get_array (ctx, 0, &array);
+    if (_csi_unlikely (status))
+       return status;
+
+    status = _csi_ostack_get_integer (ctx, 1, &content);
+    if (_csi_unlikely (status))
+       return status;
+
+    switch (array->stack.len) {
+    case 0:
+       r = NULL;
+       break;
+    case 2:
+       extents.x = extents.y = 0;
+       extents.width = _csi_object_as_real (&array->stack.objects[0]);
+       extents.height = _csi_object_as_real (&array->stack.objects[1]);
+       r = &extents;
+       break;
+    case 4:
+       extents.x = _csi_object_as_real (&array->stack.objects[0]);
+       extents.y = _csi_object_as_real (&array->stack.objects[1]);
+       extents.width = _csi_object_as_real (&array->stack.objects[2]);
+       extents.height = _csi_object_as_real (&array->stack.objects[3]);
+       r = &extents;
+       break;
+    default:
+       return _csi_error (CSI_STATUS_INVALID_SCRIPT);
+    }
+
+    obj.type = CSI_OBJECT_TYPE_SURFACE;
+    obj.datum.surface = cairo_recording_surface_create (content, r);
+    pop (2);
+    return push (&obj);
+}
+
+static csi_status_t
+_text_path (csi_t *ctx)
+{
+    csi_status_t status;
+    csi_string_t *text;
+    cairo_t *cr;
+
+    check (2);
+
+    status = _csi_ostack_get_string (ctx, 0, &text);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_context (ctx, 1, &cr);
+    if (_csi_unlikely (status))
+       return status;
+
+    cairo_text_path (cr, text->string);
+    pop (1);
+    return CSI_STATUS_SUCCESS;
+}
+
+static csi_status_t
+_undef (csi_t *ctx)
+{
+    csi_name_t name = 0; /* silence the compiler */
+    csi_status_t status;
+
+    check (1);
+
+    status = _csi_ostack_get_name (ctx, 0, &name);
+    if (_csi_unlikely (status))
+       return status;
+
+    status = _csi_name_undefine (ctx, name);
+    if (_csi_unlikely (status))
+       return status;
+
+    pop (1);
+    return CSI_STATUS_SUCCESS;
+}
+
+static csi_status_t
+_unset (csi_t *ctx)
+{
+    csi_object_t *dst;
+    csi_name_t name = 0; /* silence the compiler */
+    csi_status_t status;
+    int type;
+
+    check (2);
+
+    status = _csi_ostack_get_name (ctx, 0, &name);
+    if (_csi_unlikely (status))
+       return status;
+
+    dst = _csi_peek_ostack (ctx, 1);
+    type = csi_object_get_type (dst);
+    switch (type) {
+    case CSI_OBJECT_TYPE_DICTIONARY:
+       csi_dictionary_remove (ctx, dst->datum.dictionary, name);
+       break;
+    case CSI_OBJECT_TYPE_STRING:
+    case CSI_OBJECT_TYPE_ARRAY:
+    default:
+       return _csi_error (CSI_STATUS_INVALID_SCRIPT);
+    }
+
+    pop (1);
+    return CSI_STATUS_SUCCESS;
+}
+
+static csi_status_t
+_write_to_png (csi_t *ctx)
+{
+    csi_status_t status;
+    csi_string_t *filename;
+    cairo_surface_t *surface;
+
+    check (2);
+
+    status = _csi_ostack_get_string (ctx, 0, &filename);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_surface (ctx, 1, &surface);
+    if (_csi_unlikely (status))
+       return status;
+
+#if CAIRO_HAS_PNG_FUNCTIONS
+    status = cairo_surface_write_to_png (surface, filename->string);
+    if (_csi_unlikely (status))
+       return status;
+#else
+    return CAIRO_STATUS_WRITE_ERROR;
+#endif
+
+    pop (1);
+    return CSI_STATUS_SUCCESS;
+}
+
+static csi_status_t
+_write_to_script (csi_t *ctx)
+{
+    csi_status_t status;
+    csi_string_t *filename;
+    cairo_surface_t *record;
+
+    check (2);
+
+    status = _csi_ostack_get_string (ctx, 0, &filename);
+    if (_csi_unlikely (status))
+       return status;
+    status = _csi_ostack_get_surface (ctx, 1, &record);
+    if (_csi_unlikely (status))
+       return status;
+
+    if (cairo_surface_get_type (record) != CAIRO_SURFACE_TYPE_RECORDING)
+       return CAIRO_STATUS_SURFACE_TYPE_MISMATCH;
+
+#if CAIRO_HAS_SCRIPT_SURFACE
+    {
+       cairo_device_t *script;
+
+       script = cairo_script_create (filename->string);
+       status = cairo_script_from_recording_surface (script, record);
+       cairo_device_destroy (script);
+       if (_csi_unlikely (status))
+           return status;
+    }
+#else
+    return CAIRO_STATUS_WRITE_ERROR;
+#endif
+
+    pop (1);
+    return CSI_STATUS_SUCCESS;
+}
+
+static csi_status_t
+_xor (csi_t *ctx)
+{
+    csi_object_t *a, *b;
+    int type;
+
+    check (2);
+
+    a = _csi_peek_ostack (ctx, 0);
+    b = _csi_peek_ostack (ctx, 1);
+    if (_csi_unlikely (csi_object_get_type (a) != csi_object_get_type (b)))
+       return _csi_error (CSI_STATUS_INVALID_SCRIPT);
+
+    pop (2);
+    type = csi_object_get_type (a);
+    switch (type) {
+    case CSI_OBJECT_TYPE_INTEGER:
+       return _csi_push_ostack_integer (ctx,
+                                        a->datum.integer ^ b->datum.integer);
+    case CSI_OBJECT_TYPE_BOOLEAN:
+       return _csi_push_ostack_boolean (ctx,
+                                        a->datum.boolean ^ b->datum.boolean);
+    default:
+       return _csi_error (CSI_STATUS_INVALID_SCRIPT);
+    }
+}
+
+static csi_status_t
+_debug_print (csi_t *ctx)
+{
+    csi_object_t *obj;
+
+    check (1);
+    obj = _csi_peek_ostack (ctx, 0);
+    switch (csi_object_get_type (obj)) {
+    case CSI_OBJECT_TYPE_NULL:
+       fprintf (stderr, "NULL\n");
+       break;
+
+       /* atomics */
+    case CSI_OBJECT_TYPE_BOOLEAN:
+       fprintf (stderr, "boolean: %s\n",
+                obj->datum.boolean ? "true" : "false");
+       break;
+    case CSI_OBJECT_TYPE_INTEGER:
+       fprintf (stderr, "integer: %ld\n", obj->datum.integer);
+       break;
+    case CSI_OBJECT_TYPE_MARK:
+       fprintf (stderr, "mark\n");
+       break;
+    case CSI_OBJECT_TYPE_NAME:
+       fprintf (stderr, "name: %s\n", (char *) obj->datum.name);
+       break;
+    case CSI_OBJECT_TYPE_OPERATOR:
+       fprintf (stderr, "operator: %p\n", obj->datum.ptr);
+       break;
+    case CSI_OBJECT_TYPE_REAL:
+       fprintf (stderr, "real: %g\n", obj->datum.real);
+       break;
+
+       /* compound */
+    case CSI_OBJECT_TYPE_ARRAY:
+       fprintf (stderr, "array\n");
+       break;
+    case CSI_OBJECT_TYPE_DICTIONARY:
+       fprintf (stderr, "dictionary\n");
+       break;
+    case CSI_OBJECT_TYPE_FILE:
+       fprintf (stderr, "file\n");
+       break;
+    case CSI_OBJECT_TYPE_MATRIX:
+       fprintf (stderr, "matrix: [%g %g %g %g %g %g]\n",
+                obj->datum.matrix->matrix.xx,
+                obj->datum.matrix->matrix.yx,
+                obj->datum.matrix->matrix.xy,
+                obj->datum.matrix->matrix.yy,
+                obj->datum.matrix->matrix.x0,
+                obj->datum.matrix->matrix.y0);
+       break;
+    case CSI_OBJECT_TYPE_STRING:
+       fprintf (stderr, "string: %s\n", obj->datum.string->string);
+       break;
+
+       /* cairo */
+    case CSI_OBJECT_TYPE_CONTEXT:
+       fprintf (stderr, "context\n");
+       break;
+    case CSI_OBJECT_TYPE_FONT:
+       fprintf (stderr, "font\n");
+       break;
+    case CSI_OBJECT_TYPE_PATTERN:
+       fprintf (stderr, "pattern\n");
+       break;
+    case CSI_OBJECT_TYPE_SCALED_FONT:
+       fprintf (stderr, "scaled-font\n");
+       break;
+    case CSI_OBJECT_TYPE_SURFACE:
+       fprintf (stderr, "surface\n");
+       break;
+    }
+    pop (1);
+    return CSI_STATUS_SUCCESS;
+}
+
+static const csi_operator_def_t
+_defs[] = {
+    { "<<", _mark },
+    { ">>", end_dict_construction },
+    { "[", _mark },
+    { "]", end_array_construction },
+    { "a", _alpha },
+    { "abs", NULL },
+    { "add", _add },
+    { "add-color-stop", _add_color_stop },
+    { "and", _and },
+    { "arc", _arc },
+    { "arc-negative", _arc_negative },
+    { "arc-", _arc_negative },
+    { "arc-to", NULL },
+    { "array", _array },
+    { "astore", NULL },
+    { "atan", NULL },
+    { "bind", _bind },
+    { "bitshift", _bitshift },
+    { "c", _curve_to },
+    { "C", _rel_curve_to },
+    { "ceiling", NULL },
+    { "clear", NULL },
+    { "clear-to-mark", NULL },
+    { "clip", _clip },
+    { "clip-extents", NULL },
+    { "clip-preserve", _clip_preserve },
+    { "clip+", _clip_preserve },
+    { "close-path", _close_path },
+    { "context", _context },
+    { "copy", _copy },
+    { "copy-page", _copy_page },
+    { "cos", NULL },
+    { "count", NULL },
+    { "count-to-mark", NULL },
+    { "curve-to", _curve_to },
+    { "cvi", _cvi },
+    { "cvr", _cvr },
+    { "def", _def },
+    { "device-to-user", NULL },
+    { "device-to-user-distance", NULL },
+    { "dict", _dict },
+    { "div", _div },
+    { "dup", _duplicate },
+    { "eq", _eq },
+    { "exch", _exch },
+    { "exec", NULL },
+    { "exp", NULL },
+    { "false", _false },
+    { "fill", _fill },
+    { "fill-extents", NULL },
+    { "fill-preserve", _fill_preserve },
+    { "fill+", _fill_preserve },
+    { "filter", _filter },
+    { "floor", NULL },
+    { "font", _font },
+    { "for", _for },
+    { "forall", NULL },
+    { "g", _gray },
+    { "ge", _ge },
+    { "get", _get },
+    { "glyph-path", _glyph_path },
+    { "gt", _gt },
+    { "h", _close_path },
+    { "identity", _identity },
+    { "if", _if },
+    { "ifelse", _ifelse },
+    { "image", _image },
+    { "index", _index },
+    { "integer", _integer },
+    { "invert", _invert },
+    { "in-stroke", NULL },
+    { "in-fill", NULL },
+    { "known", NULL },
+    { "l", _line_to },
+    { "L", _rel_line_to },
+    { "languagelevel", NULL },
+    { "le", _le },
+    { "length", NULL },
+    { "linear", _linear },
+    { "line-to", _line_to },
+    { "ln", NULL },
+    { "load", NULL },
+    { "log", NULL },
+    { "loop", NULL },
+    { "lt", _lt },
+    { "m", _move_to },
+    { "M", _rel_move_to },
+    { "map-to-image", _map_to_image },
+    { "mark", _mark },
+    { "mask", _mask },
+    { "matrix", _matrix },
+    { "mesh", _mesh },
+    { "mesh-begin-patch", _mesh_begin_patch },
+    { "mesh-curve-to", _mesh_curve_to },
+    { "mesh-end-patch", _mesh_end_patch },
+    { "mesh-line-to", _mesh_line_to },
+    { "mesh-move-to", _mesh_move_to },
+    { "mesh-set-control-point", _mesh_set_control_point },
+    { "mesh-set-corner-color", _mesh_set_corner_color },
+    { "mod", _mod },
+    { "move-to", _move_to },
+    { "mul", _mul },
+    { "multiply", NULL },
+    { "n", _new_path },
+    { "N", _new_sub_path },
+    { "ne", _ne },
+    { "neg", _neg },
+    { "new-path", _new_path },
+    { "new-sub-path", _new_sub_path },
+    { "not", _not },
+    { "null", _null },
+    { "or", _or },
+    { "paint", _paint },
+    { "paint-with-alpha", _paint_with_alpha },
+    { "pattern", _pattern },
+    { "pop", _pop },
+    { "pop-group", _pop_group },
+    { "push-group", _push_group },
+    { "radial", _radial },
+    { "rand", NULL },
+    { "record", _record },
+    { "rectangle", _rectangle },
+    { "repeat", _repeat },
+    { "restore", _restore },
+    { "rel-curve-to", _rel_curve_to },
+    { "rel-line-to", _rel_line_to },
+    { "rel-move-to", _rel_move_to },
+    { "reset-clip", _reset_clip },
+    { "rgb", _rgb },
+    { "rgba", _rgba },
+    { "roll", _roll },
+    { "rotate", _rotate },
+    { "round", NULL },
+    { "run", NULL },
+    { "save", _save },
+    { "scale", _scale },
+    { "scaled-font", _scaled_font },
+    { "select-font-face", _select_font_face },
+    { "set", _set },
+    { "set-antialias", _set_antialias },
+    { "set-dash", _set_dash },
+    { "set-device-offset", _set_device_offset },
+    { "set-extend", _set_extend },
+    { "set-fallback-resolution", _set_fallback_resolution },
+    { "set-fill-rule", _set_fill_rule },
+    { "set-filter", _set_filter },
+    { "set-font-face", _set_font_face },
+    { "set-font-options", _set_font_options },
+    { "set-font-matrix", _set_font_matrix },
+    { "set-font-size", _set_font_size },
+    { "set-line-cap", _set_line_cap },
+    { "set-line-join", _set_line_join },
+    { "set-line-width", _set_line_width },
+    { "set-matrix", _set_matrix },
+    { "set-miter-limit", _set_miter_limit },
+    { "set-mime-data", _set_mime_data },
+    { "set-operator", _set_operator },
+    { "set-scaled-font", _set_scaled_font },
+    { "set-source", _set_source },
+    { "set-source-image", _set_source_image },
+    { "set-source-rgb", _set_source_rgb },
+    { "set-source-rgba", _set_source_rgba },
+    { "set-tolerance", _set_tolerance },
+    { "show-glyphs", _show_glyphs },
+    { "show-text", _show_text },
+    { "show-text-glyphs", _show_text_glyphs },
+    { "show-page", _show_page },
+    { "similar", _similar },
+    { "similar-image", _similar_image },
+    { "sin", NULL },
+    { "sqrt", NULL },
+    { "sub", _sub },
+    { "subsurface", _subsurface },
+    { "surface", _surface },
+    { "string", NULL },
+    { "stroke", _stroke },
+    { "stroke-extents", NULL },
+    { "stroke-preserve", _stroke_preserve },
+    { "stroke+", _stroke_preserve },
+    { "text-path", _text_path },
+    { "transform", _transform },
+    { "transform-distance", NULL },
+    { "transform-point", NULL },
+    { "translate", _translate },
+    { "true", _true },
+    { "type", NULL },
+    { "undef", _undef },
+    { "unmap-image", _unmap_image },
+    { "unset", _unset },
+    { "user-to-device", NULL },
+    { "user-to-device-distance", NULL },
+    { "where", NULL },
+    { "write-to-png", _write_to_png },
+    { "write-to-script", _write_to_script },
+    { "xor", _xor },
+
+    { "=", _debug_print },
+
+    { NULL, NULL },
+};
+
+const csi_operator_def_t *
+_csi_operators (void)
+{
+    return _defs;
+}
+
+static const csi_integer_constant_def_t
+_integer_constants[] = {
+    { "CLEAR",         CAIRO_OPERATOR_CLEAR },
+    { "SOURCE",                CAIRO_OPERATOR_SOURCE },
+    { "OVER",          CAIRO_OPERATOR_OVER },
+    { "IN",            CAIRO_OPERATOR_IN },
+    { "OUT",           CAIRO_OPERATOR_OUT },
+    { "ATOP",          CAIRO_OPERATOR_ATOP },
+    { "DEST",          CAIRO_OPERATOR_DEST },
+    { "DEST_OVER",     CAIRO_OPERATOR_DEST_OVER },
+    { "DEST_IN",       CAIRO_OPERATOR_DEST_IN },
+    { "DEST_OUT",      CAIRO_OPERATOR_DEST_OUT },
+    { "DEST_ATOP",     CAIRO_OPERATOR_DEST_ATOP },
+    { "XOR",           CAIRO_OPERATOR_XOR },
+    { "ADD",           CAIRO_OPERATOR_ADD },
+    { "SATURATE",      CAIRO_OPERATOR_SATURATE },
+    { "MULTIPLY",      CAIRO_OPERATOR_MULTIPLY },
+    { "SCREEN",                CAIRO_OPERATOR_SCREEN },
+    { "OVERLAY",       CAIRO_OPERATOR_OVERLAY },
+    { "DARKEN",                CAIRO_OPERATOR_DARKEN },
+    { "LIGHTEN",       CAIRO_OPERATOR_LIGHTEN },
+    { "DODGE",         CAIRO_OPERATOR_COLOR_DODGE },
+    { "BURN",          CAIRO_OPERATOR_COLOR_BURN },
+    { "HARD_LIGHT",    CAIRO_OPERATOR_HARD_LIGHT },
+    { "SOFT_LIGHT",    CAIRO_OPERATOR_SOFT_LIGHT },
+    { "DIFFERENCE",    CAIRO_OPERATOR_DIFFERENCE },
+    { "EXCLUSION",     CAIRO_OPERATOR_EXCLUSION },
+    { "HSL_HUE",       CAIRO_OPERATOR_HSL_HUE },
+    { "HSL_SATURATION", CAIRO_OPERATOR_HSL_SATURATION },
+    { "HSL_COLOR",     CAIRO_OPERATOR_HSL_COLOR },
+    { "HSL_LUMINOSITY", CAIRO_OPERATOR_HSL_LUMINOSITY },
+
+    { "WINDING",       CAIRO_FILL_RULE_WINDING },
+    { "EVEN_ODD",      CAIRO_FILL_RULE_EVEN_ODD },
+
+    { "ANTIALIAS_DEFAULT",     CAIRO_ANTIALIAS_DEFAULT },
+    { "ANTIALIAS_NONE",                CAIRO_ANTIALIAS_NONE },
+    { "ANTIALIAS_GRAY",                CAIRO_ANTIALIAS_GRAY },
+    { "ANTIALIAS_SUBPIXEL",    CAIRO_ANTIALIAS_SUBPIXEL },
+    { "ANTIALIAS_FAST",                CAIRO_ANTIALIAS_FAST },
+    { "ANTIALIAS_GOOD",                CAIRO_ANTIALIAS_GOOD },
+    { "ANTIALIAS_BEST",                CAIRO_ANTIALIAS_BEST },
+
+    { "LINE_CAP_BUTT",         CAIRO_LINE_CAP_BUTT },
+    { "LINE_CAP_ROUND",                CAIRO_LINE_CAP_ROUND },
+    { "LINE_CAP_SQUARE",       CAIRO_LINE_CAP_SQUARE },
+
+    { "LINE_JOIN_MITER",       CAIRO_LINE_JOIN_MITER },
+    { "LINE_JOIN_ROUND",       CAIRO_LINE_JOIN_ROUND },
+    { "LINE_JOIN_BEVEL",       CAIRO_LINE_JOIN_BEVEL },
+
+    { "EXTEND_NONE",           CAIRO_EXTEND_NONE },
+    { "EXTEND_REPEAT",         CAIRO_EXTEND_REPEAT },
+    { "EXTEND_REFLECT",                CAIRO_EXTEND_REFLECT },
+    { "EXTEND_PAD",            CAIRO_EXTEND_PAD },
+
+    { "FILTER_FAST",           CAIRO_FILTER_FAST },
+    { "FILTER_GOOD",           CAIRO_FILTER_GOOD },
+    { "FILTER_BEST",           CAIRO_FILTER_BEST },
+    { "FILTER_BILINEAR",       CAIRO_FILTER_BILINEAR },
+    { "FILTER_NEAREST",                CAIRO_FILTER_NEAREST },
+    { "FILTER_GAUSSIAN",       CAIRO_FILTER_GAUSSIAN },
+
+    { "SLANT_NORMAL",          CAIRO_FONT_SLANT_NORMAL },
+    { "SLANT_ITALIC",          CAIRO_FONT_SLANT_ITALIC },
+    { "SLANT_OBLIQUE",         CAIRO_FONT_SLANT_OBLIQUE },
+
+    { "WEIGHT_NORMAL",         CAIRO_FONT_WEIGHT_NORMAL },
+    { "WEIGHT_BOLD",           CAIRO_FONT_WEIGHT_BOLD },
+
+    { "SUBPIXEL_ORDER_DEFAULT",        CAIRO_SUBPIXEL_ORDER_DEFAULT },
+    { "SUBPIXEL_ORDER_RGB",    CAIRO_SUBPIXEL_ORDER_RGB },
+    { "SUBPIXEL_ORDER_BGR",    CAIRO_SUBPIXEL_ORDER_BGR },
+    { "SUBPIXEL_ORDER_VRGB",   CAIRO_SUBPIXEL_ORDER_VRGB },
+    { "SUBPIXEL_ORDER_VBGR",   CAIRO_SUBPIXEL_ORDER_VBGR },
+
+    { "HINT_STYLE_DEFAULT",    CAIRO_HINT_STYLE_DEFAULT },
+    { "HINT_STYLE_NONE",       CAIRO_HINT_STYLE_NONE },
+    { "HINT_STYLE_SLIGHT",     CAIRO_HINT_STYLE_SLIGHT },
+    { "HINT_STYLE_MEDIUM",     CAIRO_HINT_STYLE_MEDIUM },
+    { "HINT_STYLE_FULL",       CAIRO_HINT_STYLE_FULL },
+
+    { "HINT_METRICS_DEFAULT",  CAIRO_HINT_METRICS_DEFAULT },
+    { "HINT_METRICS_OFF",      CAIRO_HINT_METRICS_OFF },
+    { "HINT_METRICS_ON",       CAIRO_HINT_METRICS_ON },
+
+    { "FORWARD",               0 },
+    { "BACKWARD",              1 },
+
+    { "COLOR",                 CAIRO_CONTENT_COLOR },
+    { "ALPHA",                 CAIRO_CONTENT_ALPHA },
+    { "COLOR_ALPHA",           CAIRO_CONTENT_COLOR_ALPHA },
+
+    { "A1",                    CAIRO_FORMAT_A1 },
+    { "A8",                    CAIRO_FORMAT_A8 },
+    { "RGB16_565",             CAIRO_FORMAT_RGB16_565 },
+    { "RGB24",                 CAIRO_FORMAT_RGB24 },
+    { "ARGB32",                        CAIRO_FORMAT_ARGB32 },
+    { "INVALID",               CAIRO_FORMAT_INVALID },
+
+    { NULL, 0 }
+};
+
+
+const csi_integer_constant_def_t *
+_csi_integer_constants (void)
+{
+    return _integer_constants;
+}
+
+static const csi_real_constant_def_t
+_real_constants[] = {
+    { "math.pi",               M_PI },
+    { "math.2pi",              2 * M_PI },
+    { "math.sqrt2",            M_SQRT2 },
+    { "math.ln2",              M_LN2 },
+
+    { NULL, 0 }
+};
+
+const csi_real_constant_def_t *
+_csi_real_constants (void)
+{
+    return _real_constants;
+}
diff --git a/util/cairo-script/cairo-script-private.h b/util/cairo-script/cairo-script-private.h
new file mode 100644 (file)
index 0000000..3286bb4
--- /dev/null
@@ -0,0 +1,989 @@
+/*
+ * Copyright © 2008 Chris Wilson <chris@chris-wilson.co.uk>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Chris Wilson.
+ *
+ * Contributor(s):
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#ifndef CAIRO_SCRIPT_PRIVATE_H
+#define CAIRO_SCRIPT_PRIVATE_H
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "cairo-script-interpreter.h"
+
+#include <setjmp.h>
+
+#ifdef _MSC_VER
+#undef inline
+#define inline __inline
+#endif
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#ifndef TRUE
+#define TRUE (!FALSE)
+#endif
+
+#ifndef NULL
+#define NULL (void *) 0
+#endif
+
+#if   HAVE_STDINT_H
+# include <stdint.h>
+#elif HAVE_INTTYPES_H
+# include <inttypes.h>
+#elif HAVE_SYS_INT_TYPES_H
+# include <sys/int_types.h>
+#elif defined(_MSC_VER)
+  typedef __int8 int8_t;
+  typedef unsigned __int8 uint8_t;
+  typedef __int16 int16_t;
+  typedef unsigned __int16 uint16_t;
+  typedef __int32 int32_t;
+  typedef unsigned __int32 uint32_t;
+  typedef __int64 int64_t;
+  typedef unsigned __int64 uint64_t;
+# ifndef HAVE_UINT64_T
+#  define HAVE_UINT64_T 1
+# endif
+#else
+#error Cannot find definitions for fixed-width integral types (uint8_t, uint32_t, etc.)
+#endif
+
+#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 __GNUC__ >= 3 && defined(__ELF__) && !defined(__sun)
+# define slim_hidden_proto(name)               slim_hidden_proto1(name, slim_hidden_int_name(name)) csi_private
+# define slim_hidden_proto_no_warn(name)       slim_hidden_proto1(name, slim_hidden_int_name(name)) csi_private_no_warn
+# define slim_hidden_def(name)                 slim_hidden_def1(name, slim_hidden_int_name(name))
+# define slim_hidden_int_name(name) INT_##name
+# define slim_hidden_proto1(name, internal)                            \
+  extern __typeof (name) name                                          \
+       __asm__ (slim_hidden_asmname (internal))
+# define slim_hidden_def1(name, internal)                              \
+  extern __typeof (name) EXT_##name __asm__(slim_hidden_asmname(name)) \
+       __attribute__((__alias__(slim_hidden_asmname(internal))))
+# define slim_hidden_ulp               slim_hidden_ulp1(__USER_LABEL_PREFIX__)
+# define slim_hidden_ulp1(x)           slim_hidden_ulp2(x)
+# define slim_hidden_ulp2(x)           #x
+# define slim_hidden_asmname(name)     slim_hidden_asmname1(name)
+# define slim_hidden_asmname1(name)    slim_hidden_ulp #name
+#else
+# define slim_hidden_proto(name)               int _csi_dummy_prototype(void)
+# define slim_hidden_proto_no_warn(name)       int _csi_dummy_prototype(void)
+# define slim_hidden_def(name)                 int _csi_dummy_prototype(void)
+#endif
+
+#if __GNUC__ >= 3
+#define csi_pure __attribute__((pure))
+#define csi_const __attribute__((const))
+#else
+#define csi_pure
+#define csi_const
+#endif
+
+#if defined(__GNUC__) && (__GNUC__ > 2) && defined(__OPTIMIZE__)
+#define _CSI_BOOLEAN_EXPR(expr)                   \
+ __extension__ ({                               \
+   int _csi_boolean_var_;                         \
+   if (expr)                                    \
+      _csi_boolean_var_ = 1;                      \
+   else                                         \
+      _csi_boolean_var_ = 0;                      \
+   _csi_boolean_var_;                             \
+})
+#define _csi_likely(expr) (__builtin_expect (_CSI_BOOLEAN_EXPR(expr), 1))
+#define _csi_unlikely(expr) (__builtin_expect (_CSI_BOOLEAN_EXPR(expr), 0))
+#else
+#define _csi_likely(expr) (expr)
+#define _csi_unlikely(expr) (expr)
+#endif
+
+#ifdef __GNUC__
+#ifndef offsetof
+#define offsetof(type, member) \
+    ((char *) &((type *) 0)->member - (char *) 0)
+#endif
+#define csi_container_of(ptr, type, member) ({ \
+    const typeof(((type *) 0)->member) *mptr__ = (ptr); \
+    (type *) ((char *) mptr__ - offsetof (type, member)); \
+})
+#else
+#define csi_container_of(ptr, type, member) \
+    (type *)((char *) (ptr) - (char *) &((type *)0)->member)
+#endif
+
+/* slim_internal.h */
+#if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 3)) && defined(__ELF__) && !defined(__sun)
+#define csi_private_no_warn    __attribute__((__visibility__("hidden")))
+#elif defined(__SUNPRO_C) && (__SUNPRO_C >= 0x550)
+#define csi_private_no_warn    __hidden
+#else /* not gcc >= 3.3 and not Sun Studio >= 8 */
+#define csi_private_no_warn
+#endif
+
+#undef  ARRAY_LENGTH
+#define ARRAY_LENGTH(__array) ((int) (sizeof (__array) / sizeof (__array[0])))
+
+#ifndef WARN_UNUSED_RESULT
+#define WARN_UNUSED_RESULT
+#endif
+/* Add attribute(warn_unused_result) if supported */
+#define csi_warn           WARN_UNUSED_RESULT
+#define csi_private        csi_private_no_warn csi_warn
+
+#define CSI_BITSWAP8(c) ((((c) * 0x0802LU & 0x22110LU) | ((c) * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16)
+#ifdef WORDS_BIGENDIAN
+#define CSI_BITSWAP8_IF_LITTLE_ENDIAN(c) (c)
+#else
+#define CSI_BITSWAP8_IF_LITTLE_ENDIAN(c) CSI_BITSWAP8(c)
+#endif
+
+typedef enum _csi_status {
+    CSI_STATUS_SUCCESS = CAIRO_STATUS_SUCCESS,
+    CSI_STATUS_NO_MEMORY = CAIRO_STATUS_NO_MEMORY,
+    CSI_STATUS_INVALID_RESTORE = CAIRO_STATUS_INVALID_RESTORE,
+    CSI_STATUS_INVALID_POP_GROUP = CAIRO_STATUS_INVALID_POP_GROUP,
+    CSI_STATUS_NO_CURRENT_POINT = CAIRO_STATUS_NO_CURRENT_POINT,
+    CSI_STATUS_INVALID_MATRIX = CAIRO_STATUS_INVALID_MATRIX,
+    CSI_STATUS_INVALID_STATUS = CAIRO_STATUS_INVALID_STATUS,
+    CSI_STATUS_NULL_POINTER = CAIRO_STATUS_NULL_POINTER,
+    CSI_STATUS_INVALID_STRING = CAIRO_STATUS_INVALID_STRING,
+    CSI_STATUS_INVALID_PATH_DATA = CAIRO_STATUS_INVALID_PATH_DATA,
+    CSI_STATUS_READ_ERROR = CAIRO_STATUS_READ_ERROR,
+    CSI_STATUS_WRITE_ERROR = CAIRO_STATUS_WRITE_ERROR,
+    CSI_STATUS_SURFACE_FINISHED = CAIRO_STATUS_SURFACE_FINISHED,
+    CSI_STATUS_SURFACE_TYPE_MISMATCH = CAIRO_STATUS_SURFACE_TYPE_MISMATCH,
+    CSI_STATUS_PATTERN_TYPE_MISMATCH = CAIRO_STATUS_PATTERN_TYPE_MISMATCH,
+    CSI_STATUS_INVALID_CONTENT = CAIRO_STATUS_INVALID_CONTENT,
+    CSI_STATUS_INVALID_FORMAT = CAIRO_STATUS_INVALID_FORMAT,
+    CSI_STATUS_INVALID_VISUAL = CAIRO_STATUS_INVALID_VISUAL,
+    CSI_STATUS_FILE_NOT_FOUND = CAIRO_STATUS_FILE_NOT_FOUND,
+    CSI_STATUS_INVALID_DASH = CAIRO_STATUS_INVALID_DASH,
+    CSI_STATUS_INVALID_DSC_COMMENT = CAIRO_STATUS_INVALID_DSC_COMMENT,
+    CSI_STATUS_INVALID_INDEX = CAIRO_STATUS_INVALID_INDEX,
+    CSI_STATUS_CLIP_NOT_REPRESENTABLE = CAIRO_STATUS_CLIP_NOT_REPRESENTABLE,
+    CSI_STATUS_TEMP_FILE_ERROR = CAIRO_STATUS_TEMP_FILE_ERROR,
+    CSI_STATUS_INVALID_STRIDE = CAIRO_STATUS_INVALID_STRIDE,
+    CSI_STATUS_FONT_TYPE_MISMATCH = CAIRO_STATUS_FONT_TYPE_MISMATCH,
+    CSI_STATUS_USER_FONT_IMMUTABLE = CAIRO_STATUS_USER_FONT_IMMUTABLE,
+    CSI_STATUS_USER_FONT_ERROR = CAIRO_STATUS_USER_FONT_ERROR,
+    CSI_STATUS_NEGATIVE_COUNT = CAIRO_STATUS_NEGATIVE_COUNT,
+    CSI_STATUS_INVALID_CLUSTERS = CAIRO_STATUS_INVALID_CLUSTERS,
+    CSI_STATUS_INVALID_SLANT = CAIRO_STATUS_INVALID_SLANT,
+    CSI_STATUS_INVALID_WEIGHT = CAIRO_STATUS_INVALID_WEIGHT,
+
+    /* cairo-script-interpreter specific errors */
+
+    CSI_STATUS_INVALID_SCRIPT,
+    CSI_STATUS_SCRIPT_INVALID_TYPE,
+    CSI_STATUS_SCRIPT_INVALID_INDEX,
+    CSI_STATUS_SCRIPT_UNDEFINED_NAME,
+    CSI_STATUS_INTERPRETER_FINISHED,
+
+    _CSI_STATUS_SCRIPT_LAST_ERROR,
+    CSI_INT_STATUS_UNSUPPORTED
+} csi_status_t;
+
+typedef enum {
+    CSI_OBJECT_TYPE_NULL = 0,
+
+    /* atomics */
+    CSI_OBJECT_TYPE_BOOLEAN,
+    CSI_OBJECT_TYPE_INTEGER,
+    CSI_OBJECT_TYPE_MARK,
+    CSI_OBJECT_TYPE_NAME,
+    CSI_OBJECT_TYPE_OPERATOR,
+    CSI_OBJECT_TYPE_REAL,
+
+    /* compound */
+    CSI_OBJECT_TYPE_ARRAY = 0x8,
+    CSI_OBJECT_TYPE_DICTIONARY,
+    CSI_OBJECT_TYPE_FILE,
+    CSI_OBJECT_TYPE_MATRIX,
+    CSI_OBJECT_TYPE_STRING,
+
+    /* cairo */
+    CSI_OBJECT_TYPE_CONTEXT = 0x10,
+    CSI_OBJECT_TYPE_FONT,
+    CSI_OBJECT_TYPE_PATTERN,
+    CSI_OBJECT_TYPE_SCALED_FONT,
+    CSI_OBJECT_TYPE_SURFACE
+} csi_object_type_t;
+
+#define CSI_OBJECT_IS_ATOM(OBJ) (((OBJ)->type & CSI_OBJECT_TYPE_MASK) < 0x08)
+#define CSI_OBJECT_IS_COMPOUND(OBJ) ((OBJ)->type & 0x08)
+#define CSI_OBJECT_IS_CAIRO(OBJ) ((OBJ)->type & 0x10)
+
+enum { /* attributes */
+    CSI_OBJECT_ATTR_EXECUTABLE = 1 << 6,
+    CSI_OBJECT_ATTR_WRITABLE   = 1 << 7
+};
+#define CSI_OBJECT_ATTR_MASK (CSI_OBJECT_ATTR_EXECUTABLE | \
+                             CSI_OBJECT_ATTR_WRITABLE)
+#define CSI_OBJECT_TYPE_MASK (~CSI_OBJECT_ATTR_MASK)
+
+typedef struct _cairo_script_interpreter csi_t;
+
+typedef cairo_bool_t csi_boolean_t;
+typedef csi_status_t (*csi_operator_t) (csi_t *);
+typedef float csi_real_t;
+typedef long csi_integer_t;
+typedef long csi_name_t;
+typedef struct _csi_array csi_array_t;
+typedef struct _csi_buffer csi_buffer_t;
+typedef struct _csi_compound_object csi_compound_object_t;
+typedef struct _csi_dictionary csi_dictionary_t;
+typedef struct _csi_file csi_file_t;
+typedef struct _csi_hash_entry csi_hash_entry_t;
+typedef struct _csi_hash_table csi_hash_table_t;
+typedef struct _csi_hash_table_arrangement csi_hash_table_arrangement_t;
+typedef struct _csi_list csi_list_t;
+typedef struct _csi_matrix csi_matrix_t;
+typedef struct _csi_object csi_object_t;
+typedef struct _csi_scanner csi_scanner_t;
+typedef struct _csi_stack csi_stack_t;
+typedef struct _csi_string csi_string_t;
+
+typedef cairo_bool_t
+(*csi_hash_predicate_func_t) (void *entry);
+
+typedef void
+(*csi_hash_callback_func_t) (void *entry,
+                            void *closure);
+
+typedef cairo_bool_t
+(*csi_hash_keys_equal_func_t) (const void *key_a, const void *key_b);
+
+struct _csi_object {
+    csi_object_type_t type;
+    union {
+       cairo_t *cr;
+       cairo_font_face_t *font_face;
+       cairo_pattern_t *pattern;
+       cairo_scaled_font_t *scaled_font;
+       cairo_surface_t *surface;
+       csi_array_t *array;
+       csi_boolean_t boolean;
+       csi_compound_object_t *object;
+       csi_dictionary_t *dictionary;
+       csi_file_t *file;
+       csi_integer_t integer;
+       csi_matrix_t *matrix;
+       csi_operator_t op;
+       csi_name_t name;
+       csi_real_t real;
+       csi_string_t *string;
+       void *ptr;
+    } datum;
+};
+
+struct _csi_compound_object {
+    csi_object_type_t type;
+    unsigned int ref;
+};
+
+struct _csi_hash_entry {
+    unsigned long hash;
+};
+
+struct _csi_hash_table_arrangement {
+    unsigned long high_water_mark;
+    unsigned long size;
+    unsigned long rehash;
+};
+
+struct _csi_hash_table {
+    csi_hash_keys_equal_func_t keys_equal;
+
+    const csi_hash_table_arrangement_t *arrangement;
+    csi_hash_entry_t **entries;
+
+    unsigned long live_entries;
+    unsigned long used_entries;
+    unsigned long iterating;   /* Iterating, no insert, no resize */
+};
+
+
+/* simple, embedded doubly-linked links */
+struct _csi_list {
+    struct _csi_list *next, *prev;
+};
+
+struct _csi_buffer {
+    char *base, *ptr, *end;
+    unsigned int size;
+};
+
+struct _csi_stack {
+    csi_object_t *objects;
+    csi_integer_t len;
+    csi_integer_t size;
+};
+
+struct _csi_array {
+    csi_compound_object_t base;
+    csi_stack_t stack;
+};
+
+typedef struct _csi_dictionary_entry {
+    csi_hash_entry_t hash_entry;
+    csi_object_t value;
+} csi_dictionary_entry_t;
+
+struct _csi_dictionary {
+    csi_compound_object_t base;
+    csi_hash_table_t hash_table;
+};
+
+struct _csi_matrix {
+    csi_compound_object_t base;
+    cairo_matrix_t matrix;
+};
+
+struct _csi_string {
+    csi_compound_object_t base;
+    csi_integer_t len;
+    csi_integer_t deflate;
+    char *string;
+};
+
+typedef struct _csi_filter_funcs {
+    int (*filter_getc) (csi_file_t *);
+    void (*filter_putc) (csi_file_t *, int);
+    int (*filter_read) (csi_file_t *, uint8_t *, int);
+    void (*filter_destroy) (csi_t *, void *);
+} csi_filter_funcs_t;
+
+struct _csi_file {
+    csi_compound_object_t base;
+    enum {
+       STDIO,
+       BYTES,
+       PROCEDURE,
+       FILTER
+    } type;
+    unsigned int flags;
+    void *src;
+    void *data;
+    uint8_t *bp;
+    int rem;
+    const csi_filter_funcs_t *filter;
+};
+
+union _csi_union_object {
+    void *ptr[2];
+    csi_stack_t stack;
+    csi_array_t arry;
+    csi_dictionary_t dictionary;
+    csi_matrix_t matrix;
+    csi_string_t string;
+    csi_file_t file;
+    csi_object_t object;
+};
+
+struct _csi_scanner {
+    jmp_buf jmpbuf;
+    int depth;
+
+    int bind;
+    csi_status_t (*push) (csi_t *ctx, csi_object_t *obj);
+    csi_status_t (*execute) (csi_t *ctx, csi_object_t *obj);
+    void *closure;
+
+    csi_buffer_t buffer;
+    csi_stack_t procedure_stack;
+    csi_object_t build_procedure;
+
+    unsigned int accumulator;
+    unsigned int accumulator_count;
+
+    unsigned int line_number;
+};
+
+typedef cairo_script_interpreter_hooks_t csi_hooks_t;
+
+typedef struct _csi_chunk {
+    struct _csi_chunk *next;
+    int rem;
+    char *ptr;
+} csi_chunk_t;
+
+struct _cairo_script_interpreter {
+    int ref_count;
+    csi_status_t status;
+
+    unsigned int finished : 1;
+
+    csi_hooks_t hooks;
+
+    csi_hash_table_t strings;
+
+    csi_stack_t ostack;
+    csi_stack_t dstack;
+
+    csi_scanner_t scanner;
+
+    csi_chunk_t *perm_chunk;
+    struct {
+       csi_chunk_t *chunk;
+       void *free_list;
+    } slabs[16];
+    csi_array_t *free_array;
+    csi_dictionary_t *free_dictionary;
+    csi_string_t *free_string;
+
+    csi_operator_t opcode[256];
+
+    /* caches of live data */
+    csi_list_t *_images;
+    csi_list_t *_faces;
+};
+
+typedef struct _csi_operator_def {
+    const char *name;
+    csi_operator_t op;
+} csi_operator_def_t;
+
+typedef struct _csi_integer_constant_def {
+    const char *name;
+    csi_integer_t value;
+} csi_integer_constant_def_t;
+
+typedef struct _csi_real_constant_def {
+    const char *name;
+    csi_real_t value;
+} csi_real_constant_def_t;
+
+/* cairo-script-file.c */
+
+csi_private csi_status_t
+csi_file_new (csi_t *ctx,
+             csi_object_t *obj,
+             const char *path, const char *mode);
+
+csi_private csi_status_t
+csi_file_new_for_stream (csi_t *ctx,
+                        csi_object_t *obj,
+                        FILE *stream);
+
+csi_private csi_status_t
+csi_file_new_for_bytes (csi_t *ctx,
+                       csi_object_t *obj,
+                       const char *bytes,
+                       unsigned int length);
+
+csi_private csi_status_t
+csi_file_new_from_string (csi_t *ctx,
+                         csi_object_t *obj,
+                         csi_string_t *src);
+
+csi_private csi_status_t
+csi_file_new_ascii85_decode (csi_t *ctx,
+                            csi_object_t *obj,
+                            csi_dictionary_t *dict,
+                            csi_object_t *src);
+
+csi_private csi_status_t
+csi_file_new_deflate_decode (csi_t *ctx,
+                            csi_object_t *obj,
+                            csi_dictionary_t *dict,
+                            csi_object_t *src);
+
+csi_private csi_status_t
+_csi_file_execute (csi_t *ctx, csi_file_t *obj);
+
+csi_private int
+csi_file_getc (csi_file_t *obj);
+
+csi_private int
+csi_file_read (csi_file_t *obj, void *buf, int len);
+
+csi_private void
+csi_file_putc (csi_file_t *obj, int c);
+
+csi_private void
+csi_file_flush (csi_file_t *obj);
+
+csi_private void
+csi_file_close (csi_t *ctx, csi_file_t *obj);
+
+csi_private void
+_csi_file_free (csi_t *ctx, csi_file_t *obj);
+
+csi_private csi_status_t
+_csi_file_as_string (csi_t *ctx,
+                    csi_file_t *file,
+                    csi_object_t *obj);
+
+/* cairo-script-hash.c */
+
+csi_private csi_status_t
+_csi_hash_table_init (csi_hash_table_t *hash_table,
+                     csi_hash_keys_equal_func_t keys_equal);
+
+csi_private void
+_csi_hash_table_fini (csi_hash_table_t *hash_table);
+
+csi_private void *
+_csi_hash_table_lookup (csi_hash_table_t  *hash_table,
+                       csi_hash_entry_t  *key);
+
+csi_private csi_status_t
+_csi_hash_table_insert (csi_hash_table_t *hash_table,
+                       csi_hash_entry_t *entry);
+
+csi_private void
+_csi_hash_table_remove (csi_hash_table_t *hash_table,
+                       csi_hash_entry_t *key);
+
+csi_private void
+_csi_hash_table_foreach (csi_hash_table_t            *hash_table,
+                        csi_hash_callback_func_t  hash_callback,
+                        void                         *closure);
+
+/* cairo-script-interpreter.c */
+
+csi_private void *
+_csi_alloc (csi_t *ctx, int size);
+
+csi_private void *
+_csi_alloc0 (csi_t *ctx, int size);
+
+csi_private void *
+_csi_realloc (csi_t *ctx, void *ptr, int size);
+
+csi_private void
+_csi_free (csi_t *ctx, void *ptr);
+
+csi_private void *
+_csi_slab_alloc (csi_t *ctx, int size);
+
+csi_private void *
+_csi_perm_alloc (csi_t *ctx, int size);
+
+csi_private void
+_csi_slab_free (csi_t *ctx, void *ptr, int size);
+
+csi_private csi_status_t
+csi_push_ostack (csi_t *ctx, csi_object_t *obj);
+
+csi_private csi_status_t
+_csi_name_define (csi_t *ctx, csi_name_t name, csi_object_t *obj);
+
+csi_private csi_status_t
+_csi_name_lookup (csi_t *ctx, csi_name_t name, csi_object_t *obj);
+
+csi_private csi_status_t
+_csi_name_undefine (csi_t *ctx, csi_name_t name);
+
+csi_private csi_status_t
+_csi_intern_string (csi_t *ctx, const char **str_inout, int len);
+
+csi_private csi_status_t
+_csi_error (csi_status_t status);
+
+/* cairo-script-objects.c */
+
+csi_private csi_status_t
+csi_array_new (csi_t *ctx,
+              csi_integer_t initial_size,
+              csi_object_t *obj);
+
+csi_private csi_status_t
+_csi_array_execute (csi_t *ctx, csi_array_t *array);
+
+csi_private csi_status_t
+csi_array_get (csi_t *ctx,
+              csi_array_t *array,
+              long elem,
+              csi_object_t *value);
+
+csi_private csi_status_t
+csi_array_put (csi_t *ctx,
+              csi_array_t *array,
+              csi_integer_t elem,
+              csi_object_t *value);
+
+csi_private csi_status_t
+csi_array_append (csi_t *ctx,
+                 csi_array_t *array,
+                 csi_object_t *obj);
+
+csi_private void
+csi_array_free (csi_t *ctx, csi_array_t *array);
+
+static inline void
+csi_boolean_new (csi_object_t *obj,
+                csi_boolean_t v)
+{
+    obj->type = CSI_OBJECT_TYPE_BOOLEAN;
+    obj->datum.boolean = v;
+}
+
+csi_private csi_status_t
+csi_dictionary_new (csi_t *ctx,
+                   csi_object_t *obj);
+
+csi_private csi_status_t
+csi_dictionary_put (csi_t *ctx,
+                   csi_dictionary_t *dict,
+                   csi_name_t name,
+                   csi_object_t *value);
+
+csi_private csi_status_t
+csi_dictionary_get (csi_t *ctx,
+                   csi_dictionary_t *dict,
+                   csi_name_t name,
+                   csi_object_t *value);
+
+csi_private csi_boolean_t
+csi_dictionary_has (csi_dictionary_t *dict,
+                   csi_name_t name);
+
+csi_private void
+csi_dictionary_remove (csi_t *ctx,
+                      csi_dictionary_t *dict,
+                      csi_name_t name);
+
+csi_private void
+csi_dictionary_free (csi_t *ctx,
+                    csi_dictionary_t *dict);
+
+static inline void
+csi_integer_new (csi_object_t *obj,
+                csi_integer_t v)
+{
+    obj->type = CSI_OBJECT_TYPE_INTEGER;
+    obj->datum.integer = v;
+}
+
+
+csi_private csi_status_t
+csi_matrix_new (csi_t *ctx,
+               csi_object_t *obj);
+
+csi_private csi_status_t
+csi_matrix_new_from_array (csi_t *ctx,
+                          csi_object_t *obj,
+                          csi_array_t *array);
+
+csi_private csi_status_t
+csi_matrix_new_from_matrix (csi_t *ctx,
+                           csi_object_t *obj,
+                           const cairo_matrix_t *m);
+
+csi_private csi_status_t
+csi_matrix_new_from_values (csi_t *ctx,
+                           csi_object_t *obj,
+                           double v[6]);
+
+csi_private void
+csi_matrix_free (csi_t *ctx,
+                csi_matrix_t *obj);
+
+csi_private csi_status_t
+csi_name_new (csi_t *ctx,
+             csi_object_t *obj,
+             const char *str,
+             int len);
+
+csi_private csi_status_t
+csi_name_new_static (csi_t *ctx,
+                    csi_object_t *obj,
+                    const char *str);
+
+static inline void
+csi_operator_new (csi_object_t *obj,
+                 csi_operator_t op)
+{
+    obj->type = CSI_OBJECT_TYPE_OPERATOR | CSI_OBJECT_ATTR_EXECUTABLE;
+    obj->datum.op = op;
+}
+
+static inline void
+csi_real_new (csi_object_t *obj,
+             csi_real_t v)
+{
+    obj->type = CSI_OBJECT_TYPE_REAL;
+    obj->datum.real = v;
+}
+
+csi_private csi_status_t
+csi_string_new (csi_t *ctx,
+               csi_object_t *obj,
+               const char *str,
+               int len);
+
+csi_private csi_status_t
+csi_string_deflate_new (csi_t *ctx,
+                       csi_object_t *obj,
+                       void *bytes,
+                       int in_len,
+                       int out_len);
+
+csi_private csi_status_t
+csi_string_new_from_bytes (csi_t *ctx,
+                          csi_object_t *obj,
+                          char *bytes,
+                          unsigned int len);
+
+csi_private void
+csi_string_free (csi_t *ctx, csi_string_t *string);
+
+csi_private csi_status_t
+csi_object_execute (csi_t *ctx, csi_object_t *obj);
+
+csi_private csi_object_t *
+csi_object_reference (csi_object_t *obj);
+
+csi_private void
+csi_object_free (csi_t *ctx,
+                csi_object_t *obj);
+
+csi_private csi_status_t
+csi_object_as_file (csi_t *ctx,
+                   csi_object_t *src,
+                   csi_object_t *file);
+
+csi_private csi_boolean_t
+csi_object_eq (csi_object_t *a,
+              csi_object_t *b);
+
+csi_private csi_status_t
+csi_object_compare (csi_object_t *a,
+                   csi_object_t *b,
+                   int          *out_cmp);
+
+/* cairo-script-operators.c */
+
+csi_private const csi_operator_def_t *
+_csi_operators (void);
+
+csi_private const csi_integer_constant_def_t *
+_csi_integer_constants (void);
+
+csi_private const csi_real_constant_def_t *
+_csi_real_constants (void);
+
+/* cairo-script-scanner.c */
+
+csi_private csi_status_t
+_csi_scanner_init (csi_t *ctx, csi_scanner_t *scanner);
+
+csi_private csi_status_t
+_csi_scan_file (csi_t *ctx, csi_file_t *src);
+
+csi_private csi_status_t
+_csi_translate_file (csi_t *ctx,
+                    csi_file_t *file,
+                    cairo_write_func_t write_func,
+                    void *closure);
+
+csi_private void
+_csi_scanner_fini (csi_t *ctx, csi_scanner_t *scanner);
+
+csi_private csi_boolean_t
+_csi_parse_number (csi_object_t *obj, const char *s, int len);
+
+/* cairo-script-stack.c */
+
+csi_private csi_status_t
+_csi_stack_init (csi_t *ctx, csi_stack_t *stack, csi_integer_t size);
+
+csi_private void
+_csi_stack_fini (csi_t *ctx, csi_stack_t *stack);
+
+csi_private csi_status_t
+_csi_stack_roll (csi_t *ctx,
+                csi_stack_t *stack,
+                csi_integer_t mod,
+                csi_integer_t n);
+
+csi_private csi_status_t
+_csi_stack_grow (csi_t *ctx, csi_stack_t *stack, csi_integer_t cnt);
+
+csi_private csi_status_t
+_csi_stack_push_internal (csi_t *ctx, csi_stack_t *stack,
+                         const csi_object_t *obj);
+
+csi_private csi_object_t *
+_csi_stack_peek (csi_stack_t *stack, csi_integer_t i);
+
+csi_private void
+_csi_stack_pop (csi_t *ctx, csi_stack_t *stack, csi_integer_t count);
+
+csi_private csi_status_t
+_csi_stack_exch (csi_stack_t *stack);
+
+static inline csi_object_type_t
+csi_object_get_type (const csi_object_t *obj)
+{
+    return obj->type & CSI_OBJECT_TYPE_MASK;
+}
+
+static inline csi_boolean_t
+csi_object_is_procedure (const csi_object_t *obj)
+{
+    return obj->type == (CSI_OBJECT_TYPE_ARRAY | CSI_OBJECT_ATTR_EXECUTABLE);
+}
+
+static inline csi_boolean_t
+csi_object_is_number (const csi_object_t *obj)
+{
+    int type = csi_object_get_type (obj);
+    switch (type) {
+    case CSI_OBJECT_TYPE_BOOLEAN:
+    case CSI_OBJECT_TYPE_INTEGER:
+    case CSI_OBJECT_TYPE_REAL:
+       return 1;
+    default:
+       return 0;
+    }
+}
+
+static inline double
+csi_number_get_value (const csi_object_t *obj)
+{
+    int type = csi_object_get_type (obj);
+    switch (type) {
+    case CSI_OBJECT_TYPE_BOOLEAN: return obj->datum.boolean;
+    case CSI_OBJECT_TYPE_INTEGER: return obj->datum.integer;
+    case CSI_OBJECT_TYPE_REAL: return obj->datum.real;
+    default: return 0.;
+    }
+}
+
+static inline csi_status_t
+_csi_stack_push (csi_t *ctx, csi_stack_t *stack,
+                const csi_object_t *obj)
+{
+    if (_csi_unlikely (stack->len == stack->size))
+       return _csi_stack_push_internal (ctx, stack, obj);
+
+    stack->objects[stack->len++] = *obj;
+    return CSI_STATUS_SUCCESS;
+}
+
+static inline csi_boolean_t
+_csi_check_ostack (csi_t *ctx, csi_integer_t count)
+{
+    return ctx->ostack.len >= count;
+}
+
+static inline csi_object_t *
+_csi_peek_ostack (csi_t *ctx, csi_integer_t i)
+{
+    return &ctx->ostack.objects[ctx->ostack.len - i -1];
+}
+
+static inline void
+_csi_pop_ostack (csi_t *ctx, csi_integer_t count)
+{
+    do
+       csi_object_free (ctx, &ctx->ostack.objects[--ctx->ostack.len]);
+    while (--count);
+}
+
+static inline csi_status_t
+_csi_push_ostack_copy (csi_t *ctx, csi_object_t *obj)
+{
+    return _csi_stack_push (ctx, &ctx->ostack, csi_object_reference (obj));
+}
+
+static inline csi_status_t
+_csi_push_ostack (csi_t *ctx, csi_object_t *obj)
+{
+    return _csi_stack_push (ctx, &ctx->ostack, obj);
+}
+
+static inline csi_status_t
+_csi_push_ostack_boolean (csi_t *ctx, csi_boolean_t v)
+{
+    csi_object_t obj;
+    obj.type = CSI_OBJECT_TYPE_BOOLEAN;
+    obj.datum.boolean = v;
+    return _csi_stack_push (ctx, &ctx->ostack, &obj);
+}
+static inline csi_status_t
+_csi_push_ostack_integer (csi_t *ctx, csi_integer_t v)
+{
+    csi_object_t obj;
+    obj.type = CSI_OBJECT_TYPE_INTEGER;
+    obj.datum.integer = v;
+    return _csi_stack_push (ctx, &ctx->ostack, &obj);
+}
+static inline csi_status_t
+_csi_push_ostack_mark (csi_t *ctx)
+{
+    csi_object_t obj;
+    obj.type = CSI_OBJECT_TYPE_MARK;
+    return _csi_stack_push (ctx, &ctx->ostack, &obj);
+}
+static inline csi_status_t
+_csi_push_ostack_null (csi_t *ctx)
+{
+    csi_object_t obj;
+    obj.type = CSI_OBJECT_TYPE_NULL;
+    return _csi_stack_push (ctx, &ctx->ostack, &obj);
+}
+static inline csi_status_t
+_csi_push_ostack_real (csi_t *ctx, csi_real_t v)
+{
+    csi_object_t obj;
+    obj.type = CSI_OBJECT_TYPE_REAL;
+    obj.datum.real = v;
+    return _csi_stack_push (ctx, &ctx->ostack, &obj);
+}
+
+slim_hidden_proto_no_warn (cairo_script_interpreter_destroy);
+slim_hidden_proto_no_warn (cairo_script_interpreter_reference);
+
+#endif /* CAIRO_SCRIPT_PRIVATE_H */
diff --git a/util/cairo-script/cairo-script-scanner.c b/util/cairo-script/cairo-script-scanner.c
new file mode 100644 (file)
index 0000000..969fb63
--- /dev/null
@@ -0,0 +1,1801 @@
+/*
+ * Copyright © 2008 Chris Wilson <chris@chris-wilson.co.uk>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Chris Wilson.
+ *
+ * Contributor(s):
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-script-private.h"
+
+#include <limits.h> /* INT_MAX */
+#include <math.h> /* pow */
+#include <stdio.h> /* EOF */
+#include <stdint.h> /* for {INT,UINT}*_{MIN,MAX} */
+#include <string.h> /* memset */
+#include <assert.h>
+#include <zlib.h>
+
+#define DEBUG_SCAN 0
+
+#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
+
+/*
+ * whitespace:
+ * 0 - nul
+ * 9 - tab
+ * A - LF
+ * C - FF
+ * D - CR
+ *
+ * syntax delimiters
+ * ( = 28, ) = 29 - literal strings
+ * < = 3C, > = 3E - hex/base85 strings, dictionary name
+ * [ = 5B, ] = 5D - array
+ * { = 7B, } = 7C - procedure
+ * / = 5C - literal marker
+ * % = 25 - comment
+ */
+
+static void
+fprintf_obj (FILE *stream, csi_t *ctx, const csi_object_t *obj)
+{
+    switch (csi_object_get_type (obj)) {
+       case CSI_OBJECT_TYPE_NULL:
+           fprintf (stream, "NULL\n");
+           break;
+
+           /* atomics */
+       case CSI_OBJECT_TYPE_BOOLEAN:
+           fprintf (stream, "boolean: %s\n",
+                   obj->datum.boolean ? "true" : "false");
+           break;
+       case CSI_OBJECT_TYPE_INTEGER:
+           fprintf (stream, "integer: %ld\n", obj->datum.integer);
+           break;
+       case CSI_OBJECT_TYPE_MARK:
+           fprintf (stream, "mark\n");
+           break;
+       case CSI_OBJECT_TYPE_NAME:
+           fprintf (stream, "name: %s\n", (char *) obj->datum.name);
+           break;
+       case CSI_OBJECT_TYPE_OPERATOR:
+           fprintf (stream, "operator: %p\n", obj->datum.ptr);
+           break;
+       case CSI_OBJECT_TYPE_REAL:
+           fprintf (stream, "real: %g\n", obj->datum.real);
+           break;
+
+           /* compound */
+       case CSI_OBJECT_TYPE_ARRAY:
+           fprintf (stream, "array\n");
+           break;
+       case CSI_OBJECT_TYPE_DICTIONARY:
+           fprintf (stream, "dictionary\n");
+           break;
+       case CSI_OBJECT_TYPE_FILE:
+           fprintf (stream, "file\n");
+           break;
+       case CSI_OBJECT_TYPE_MATRIX:
+           fprintf (stream, "matrix: [%g %g %g %g %g %g]\n",
+                   obj->datum.matrix->matrix.xx,
+                   obj->datum.matrix->matrix.yx,
+                   obj->datum.matrix->matrix.xy,
+                   obj->datum.matrix->matrix.yy,
+                   obj->datum.matrix->matrix.x0,
+                   obj->datum.matrix->matrix.y0);
+           break;
+       case CSI_OBJECT_TYPE_STRING:
+           fprintf (stream, "string: len=%ld\n", obj->datum.string->len);
+           break;
+
+           /* cairo */
+       case CSI_OBJECT_TYPE_CONTEXT:
+           fprintf (stream, "context\n");
+           break;
+       case CSI_OBJECT_TYPE_FONT:
+           fprintf (stream, "font\n");
+           break;
+       case CSI_OBJECT_TYPE_PATTERN:
+           fprintf (stream, "pattern\n");
+           break;
+       case CSI_OBJECT_TYPE_SCALED_FONT:
+           fprintf (stream, "scaled-font\n");
+           break;
+       case CSI_OBJECT_TYPE_SURFACE:
+           fprintf (stream, "surface\n");
+           break;
+    }
+}
+
+/* takes ownership of obj */
+static inline csi_status_t
+scan_push (csi_t *ctx, csi_object_t *obj)
+{
+    return ctx->scanner.push (ctx, obj);
+}
+
+static inline csi_status_t
+scan_execute (csi_t *ctx, csi_object_t *obj)
+{
+    return ctx->scanner.execute (ctx, obj);
+}
+
+static cairo_status_t
+buffer_init (csi_t *ctx, csi_buffer_t *buffer)
+{
+    cairo_status_t status = CSI_STATUS_SUCCESS;
+
+    buffer->size = 16384;
+    buffer->base = _csi_alloc (ctx, buffer->size);
+    if (_csi_unlikely (buffer->base == NULL)) {
+       status = _csi_error (CSI_STATUS_NO_MEMORY);
+       buffer->size = 0;
+    }
+
+    buffer->ptr = buffer->base;
+    buffer->end = buffer->base + buffer->size;
+
+    return status;
+}
+
+static void
+buffer_fini (csi_t *ctx, csi_buffer_t *buffer)
+{
+    _csi_free (ctx, buffer->base);
+}
+
+static void
+_buffer_grow (csi_t *ctx, csi_scanner_t *scan)
+{
+    int newsize;
+    int offset;
+    char *base;
+
+    if (_csi_unlikely (scan->buffer.size > INT_MAX / 2))
+       longjmp (scan->jmpbuf,  _csi_error (CSI_STATUS_NO_MEMORY));
+
+    offset = scan->buffer.ptr - scan->buffer.base;
+    newsize = scan->buffer.size * 2;
+    base = _csi_realloc (ctx, scan->buffer.base, newsize);
+    if (_csi_unlikely (base == NULL))
+       longjmp (scan->jmpbuf,  _csi_error (CSI_STATUS_NO_MEMORY));
+
+    scan->buffer.base = base;
+    scan->buffer.ptr  = base + offset;
+    scan->buffer.end  = base + newsize;
+    scan->buffer.size = newsize;
+}
+
+static inline void
+buffer_check (csi_t *ctx, csi_scanner_t *scan, int count)
+{
+    if (_csi_unlikely (scan->buffer.ptr + count > scan->buffer.end))
+       _buffer_grow (ctx, scan);
+}
+
+static inline void
+buffer_add (csi_buffer_t *buffer, int c)
+{
+    *buffer->ptr++ = c;
+}
+
+static inline void
+buffer_reset (csi_buffer_t *buffer)
+{
+    buffer->ptr = buffer->base;
+}
+
+static void
+token_start (csi_scanner_t *scan)
+{
+    buffer_reset (&scan->buffer);
+}
+
+static void
+token_add (csi_t *ctx, csi_scanner_t *scan, int c)
+{
+    buffer_check (ctx, scan, 1);
+    buffer_add (&scan->buffer, c);
+}
+
+static void
+token_add_unchecked (csi_scanner_t *scan, int c)
+{
+    buffer_add (&scan->buffer, c);
+}
+
+csi_boolean_t
+_csi_parse_number (csi_object_t *obj, const char *s, int len)
+{
+    int radix = 0;
+    long long mantissa = 0;
+    int exponent = 0;
+    int sign = 1;
+    int decimal = -1;
+    int exponent_sign = 0;
+    const char * const end = s + len;
+
+    switch (*s) {
+    case '0':
+    case '1':
+    case '2':
+    case '3':
+    case '4':
+    case '5':
+    case '6':
+    case '7':
+    case '8':
+    case '9':
+       mantissa = *s - '0';
+    case '+':
+       break;
+    case '-':
+       sign = -1;
+       break;
+    case '.':
+       decimal = 0;
+       break;
+    default:
+       return FALSE;
+    }
+
+    while (++s < end) {
+       if (*s < '0') {
+           if (*s == '.') {
+               if (_csi_unlikely (radix))
+                   return FALSE;
+               if (_csi_unlikely (decimal != -1))
+                   return FALSE;
+               if (_csi_unlikely (exponent_sign))
+                   return FALSE;
+
+               decimal = 0;
+           } else if (*s == '!') {
+               if (_csi_unlikely (radix))
+                   return FALSE;
+               if (_csi_unlikely (decimal != -1))
+                   return FALSE;
+               if (_csi_unlikely (exponent_sign))
+                   return FALSE;
+
+               radix = mantissa;
+               mantissa = 0;
+
+               if (_csi_unlikely (radix < 2 || radix > 36))
+                   return FALSE;
+           } else
+               return FALSE;
+       } else if (*s <= '9') {
+           int v = *s - '0';
+           if (_csi_unlikely (radix && v >= radix))
+               return FALSE;
+
+           if (exponent_sign) {
+               exponent = 10 * exponent + v;
+           } else {
+               if (radix)
+                   mantissa = radix * mantissa + v;
+               else
+                   mantissa = 10 * mantissa + v;
+               if (decimal != -1)
+                   decimal++;
+           }
+       } else if (*s == 'E' || * s== 'e') {
+           if (radix == 0) {
+               if (_csi_unlikely (s + 1 == end))
+                   return FALSE;
+
+               exponent_sign = 1;
+               if (s[1] == '-') {
+                   exponent_sign = -1;
+                   s++;
+               } else if (s[1] == '+')
+                   s++;
+           } else {
+               int v = 0xe;
+
+               if (_csi_unlikely (v >= radix))
+                   return FALSE;
+
+               mantissa = radix * mantissa + v;
+           }
+       } else if (*s < 'A') {
+           return FALSE;
+       } else if (*s <= 'Z') {
+           int v = *s - 'A' + 0xA;
+
+           if (_csi_unlikely (v >= radix))
+               return FALSE;
+
+           mantissa = radix * mantissa + v;
+       } else if (*s < 'a') {
+           return FALSE;
+       } else if (*s <= 'z') {
+           int v = *s - 'a' + 0xa;
+
+           if (_csi_unlikely (v >= radix))
+               return FALSE;
+
+           mantissa = radix * mantissa + v;
+       } else
+           return FALSE;
+    }
+
+    if (exponent_sign || decimal != -1) {
+       if (mantissa == 0) {
+           obj->type = CSI_OBJECT_TYPE_REAL;
+           obj->datum.real = 0.;
+           return TRUE;
+       } else {
+           int e;
+           double v;
+
+           v = mantissa;
+           e = exponent * exponent_sign;
+           if (decimal != -1)
+               e -= decimal;
+           switch (e) {
+           case -7: v *= 0.0000001; break;
+           case -6: v *= 0.000001; break;
+           case -5: v *= 0.00001; break;
+           case -4: v *= 0.0001; break;
+           case -3: v *= 0.001; break;
+           case -2: v *= 0.01; break;
+           case -1: v *= 0.1; break;
+           case  0: break;
+           case  1: v *= 10; break;
+           case  2: v *= 100; break;
+           case  3: v *= 1000; break;
+           case  4: v *= 10000; break;
+           case  5: v *= 100000; break;
+           case  6: v *= 1000000; break;
+           default:
+                   v *= pow (10, e); /* XXX */
+                   break;
+           }
+
+           obj->type = CSI_OBJECT_TYPE_REAL;
+           obj->datum.real = sign * v;
+           return TRUE;
+       }
+    } else {
+       obj->type = CSI_OBJECT_TYPE_INTEGER;
+       obj->datum.integer = sign * mantissa;
+       return TRUE;
+    }
+}
+
+static void
+token_end (csi_t *ctx, csi_scanner_t *scan, csi_file_t *src)
+{
+    cairo_status_t status;
+    char *s;
+    csi_object_t obj;
+    int len;
+
+    /*
+     * Any token that consists entirely of regular characters and
+     * cannot be interpreted as a number is treated as a name object
+     * (more precisely, an executable name). All characters except
+     * delimiters and white-space characters can appear in names,
+     * including characters ordinarily considered to be punctuation.
+     */
+
+    if (_csi_unlikely (scan->buffer.ptr == scan->buffer.base))
+       return;
+
+    s = scan->buffer.base;
+    len = scan->buffer.ptr - scan->buffer.base;
+
+    if (_csi_likely (! scan->bind)) {
+       if (s[0] == '{') { /* special case procedures */
+           if (scan->build_procedure.type != CSI_OBJECT_TYPE_NULL) {
+               status = _csi_stack_push (ctx,
+                                         &scan->procedure_stack,
+                                         &scan->build_procedure);
+               if (_csi_unlikely (status))
+                   longjmp (scan->jmpbuf, status);
+           }
+
+           status = csi_array_new (ctx, 0, &scan->build_procedure);
+           if (_csi_unlikely (status))
+               longjmp (scan->jmpbuf, status);
+
+           scan->build_procedure.type |= CSI_OBJECT_ATTR_EXECUTABLE;
+           return;
+       } else if (s[0] == '}') {
+           if (_csi_unlikely
+               (scan->build_procedure.type == CSI_OBJECT_TYPE_NULL))
+           {
+               longjmp (scan->jmpbuf, _csi_error (CSI_STATUS_INVALID_SCRIPT));
+           }
+
+           if (scan->procedure_stack.len) {
+               csi_object_t *next;
+
+               next = _csi_stack_peek (&scan->procedure_stack, 0);
+               status = csi_array_append (ctx, next->datum.array,
+                                          &scan->build_procedure);
+               scan->build_procedure = *next;
+               scan->procedure_stack.len--;
+           } else {
+               status = scan_push (ctx, &scan->build_procedure);
+               scan->build_procedure.type = CSI_OBJECT_TYPE_NULL;
+           }
+           if (_csi_unlikely (status))
+               longjmp (scan->jmpbuf, status);
+
+           return;
+       }
+    }
+
+    if (s[0] == '/') {
+       if (len >= 2 && s[1] == '/') { /* substituted name */
+           status = csi_name_new (ctx, &obj, s + 2, len - 2);
+           if (_csi_unlikely (status))
+               longjmp (scan->jmpbuf, status);
+
+           status = _csi_name_lookup (ctx, obj.datum.name, &obj);
+       } else { /* literal name */
+           status = csi_name_new (ctx, &obj, s + 1, len - 1);
+       }
+       if (_csi_unlikely (status))
+           longjmp (scan->jmpbuf, status);
+    } else {
+       if (! _csi_parse_number (&obj, s, len)) {
+           status = csi_name_new (ctx, &obj, s, len);
+           if (_csi_unlikely (status))
+               longjmp (scan->jmpbuf, status);
+
+           obj.type |= CSI_OBJECT_ATTR_EXECUTABLE;
+       }
+    }
+
+    /* consume whitespace after token, before calling the interpreter */
+    if (scan->build_procedure.type != CSI_OBJECT_TYPE_NULL) {
+       status = csi_array_append (ctx,
+                                  scan->build_procedure.datum.array,
+                                  &obj);
+    } else if (obj.type & CSI_OBJECT_ATTR_EXECUTABLE) {
+       status = scan_execute (ctx, &obj);
+       csi_object_free (ctx, &obj);
+    } else {
+       status = scan_push (ctx, &obj);
+    }
+    if (_csi_unlikely (status))
+       longjmp (scan->jmpbuf, status);
+}
+
+static void
+string_add (csi_t *ctx, csi_scanner_t *scan, int c)
+{
+    buffer_check (ctx, scan, 1);
+    buffer_add (&scan->buffer, c);
+}
+
+static void
+string_end (csi_t *ctx, csi_scanner_t *scan)
+{
+    csi_object_t obj;
+    cairo_status_t status;
+
+    status = csi_string_new (ctx,
+                            &obj,
+                            scan->buffer.base,
+                            scan->buffer.ptr - scan->buffer.base);
+    if (_csi_unlikely (status))
+       longjmp (scan->jmpbuf, status);
+
+    if (scan->build_procedure.type != CSI_OBJECT_TYPE_NULL)
+       status = csi_array_append (ctx,
+                                  scan->build_procedure.datum.array,
+                                  &obj);
+    else
+       status = scan_push (ctx, &obj);
+    if (_csi_unlikely (status))
+       longjmp (scan->jmpbuf, status);
+}
+
+static int
+hex_value (int c)
+{
+    if (c < '0')
+       return EOF;
+    if (c <= '9')
+       return c - '0';
+    c |= 32;
+    if (c < 'a')
+       return EOF;
+    if (c <= 'f')
+       return c - 'a' + 0xa;
+    return EOF;
+}
+
+static void
+hex_add (csi_t *ctx, csi_scanner_t *scan, int c)
+{
+    if (scan->accumulator_count == 0) {
+       scan->accumulator |= hex_value (c) << 4;
+       scan->accumulator_count = 1;
+    } else {
+       scan->accumulator |= hex_value (c) << 0;
+       buffer_check (ctx, scan, 1);
+       buffer_add (&scan->buffer, scan->accumulator);
+
+       scan->accumulator = 0;
+       scan->accumulator_count = 0;
+    }
+}
+
+static void
+hex_end (csi_t *ctx, csi_scanner_t *scan)
+{
+    csi_object_t obj;
+    cairo_status_t status;
+
+    if (scan->accumulator_count)
+       hex_add (ctx, scan, '0');
+
+    status = csi_string_new (ctx,
+                            &obj,
+                            scan->buffer.base,
+                            scan->buffer.ptr - scan->buffer.base);
+    if (_csi_unlikely (status))
+       longjmp (scan->jmpbuf, status);
+
+    if (scan->build_procedure.type != CSI_OBJECT_TYPE_NULL)
+       status = csi_array_append (ctx,
+                                  scan->build_procedure.datum.array,
+                                  &obj);
+    else
+       status = scan_push (ctx, &obj);
+    if (_csi_unlikely (status))
+       longjmp (scan->jmpbuf, status);
+}
+
+static void
+base85_add (csi_t *ctx, csi_scanner_t *scan, int c)
+{
+    if (c == 'z') {
+       if (_csi_unlikely (scan->accumulator_count != 0))
+           longjmp (scan->jmpbuf, _csi_error (CSI_STATUS_INVALID_SCRIPT));
+
+       buffer_check (ctx, scan, 4);
+       buffer_add (&scan->buffer, 0);
+       buffer_add (&scan->buffer, 0);
+       buffer_add (&scan->buffer, 0);
+       buffer_add (&scan->buffer, 0);
+    } else if (_csi_unlikely (c < '!' || c > 'u')) {
+       longjmp (scan->jmpbuf, _csi_error (CSI_STATUS_INVALID_SCRIPT));
+    } else {
+       scan->accumulator = scan->accumulator*85 + c - '!';
+       if (++scan->accumulator_count == 5) {
+           buffer_check (ctx, scan, 4);
+           buffer_add (&scan->buffer, (scan->accumulator >> 24) & 0xff);
+           buffer_add (&scan->buffer, (scan->accumulator >> 16) & 0xff);
+           buffer_add (&scan->buffer, (scan->accumulator >>  8) & 0xff);
+           buffer_add (&scan->buffer, (scan->accumulator >>  0) & 0xff);
+
+           scan->accumulator = 0;
+           scan->accumulator_count = 0;
+       }
+    }
+}
+
+static void
+base85_end (csi_t *ctx, csi_scanner_t *scan, cairo_bool_t deflate)
+{
+    csi_object_t obj;
+    cairo_status_t status;
+
+    buffer_check (ctx, scan, 4);
+
+    switch (scan->accumulator_count) {
+    case 0:
+       break;
+    case 1:
+       longjmp (scan->jmpbuf, _csi_error (CSI_STATUS_INVALID_SCRIPT));
+       break;
+
+    case 2:
+       scan->accumulator = scan->accumulator * (85*85*85) + 85*85*85 -1;
+       buffer_add (&scan->buffer, (scan->accumulator >> 24) & 0xff);
+       break;
+    case 3:
+       scan->accumulator = scan->accumulator * (85*85) + 85*85 -1;
+       buffer_add (&scan->buffer, (scan->accumulator >> 24) & 0xff);
+       buffer_add (&scan->buffer, (scan->accumulator >> 16) & 0xff);
+       break;
+    case 4:
+       scan->accumulator = scan->accumulator * 85 + 84;
+       buffer_add (&scan->buffer, (scan->accumulator >> 24) & 0xff);
+       buffer_add (&scan->buffer, (scan->accumulator >> 16) & 0xff);
+       buffer_add (&scan->buffer, (scan->accumulator >>  8) & 0xff);
+       break;
+    }
+
+    if (deflate) {
+        uLongf len = be32 (*(uint32_t *) scan->buffer.base);
+       Bytef *source = (Bytef *) (scan->buffer.base + sizeof (uint32_t));
+
+       status = csi_string_deflate_new (ctx, &obj,
+                                        source,
+                                        (Bytef *) scan->buffer.ptr - source,
+                                        len);
+       if (_csi_unlikely (status))
+           longjmp (scan->jmpbuf, status);
+    } else {
+       status = csi_string_new (ctx,
+                                &obj,
+                                scan->buffer.base,
+                                scan->buffer.ptr - scan->buffer.base);
+       if (_csi_unlikely (status))
+           longjmp (scan->jmpbuf, status);
+    }
+
+    if (scan->build_procedure.type != CSI_OBJECT_TYPE_NULL)
+       status = csi_array_append (ctx,
+                                  scan->build_procedure.datum.array,
+                                  &obj);
+    else
+       status = scan_push (ctx, &obj);
+    if (_csi_unlikely (status))
+       longjmp (scan->jmpbuf, status);
+}
+
+static void
+base64_add (csi_t *ctx, csi_scanner_t *scan, int c)
+{
+    int val;
+
+    /* Convert Base64 character to its 6 bit nibble */
+    val = scan->accumulator;
+    if (c =='/') {
+       val = (val << 6) | 63;
+    } else if (c =='+') {
+       val = (val << 6) | 62;
+    } else if (c >='A' && c <='Z') {
+       val = (val << 6) | (c -'A');
+    } else if (c >='a' && c <='z') {
+       val = (val << 6) | (c -'a' + 26);
+    } else if (c >='0' && c <='9') {
+       val = (val << 6) | (c -'0' + 52);
+    }
+
+    buffer_check (ctx, scan, 1);
+    switch (scan->accumulator_count++) {
+    case 0:
+       break;
+
+    case 1:
+       buffer_add (&scan->buffer, (val >> 4) & 0xFF);
+       val &= 0xF;
+       break;
+
+    case 2:
+       buffer_add (&scan->buffer, (val >> 2) & 0xFF);
+       val &= 0x3;
+       break;
+
+    case 3:
+       buffer_add (&scan->buffer, (val >> 0) & 0xFF);
+       scan->accumulator_count = 0;
+       val = 0;
+       break;
+    }
+
+     if (c == '=') {
+       scan->accumulator_count = 0;
+       scan->accumulator = 0;
+     } else {
+        scan->accumulator = val;
+     }
+}
+
+static void
+base64_end (csi_t *ctx, csi_scanner_t *scan)
+{
+    csi_object_t obj;
+    cairo_status_t status;
+
+    switch (scan->accumulator_count) {
+    case 0:
+       break;
+    case 2:
+       base64_add (ctx, scan, (scan->accumulator << 2) & 0x3f);
+       base64_add (ctx, scan, '=');
+       break;
+    case 1:
+       base64_add (ctx, scan, (scan->accumulator << 4) & 0x3f);
+       base64_add (ctx, scan, '=');
+       base64_add (ctx, scan, '=');
+       break;
+    }
+
+    status = csi_string_new (ctx,
+                            &obj,
+                            scan->buffer.base,
+                            scan->buffer.ptr - scan->buffer.base);
+    if (_csi_unlikely (status))
+       longjmp (scan->jmpbuf, status);
+
+    if (scan->build_procedure.type != CSI_OBJECT_TYPE_NULL)
+       status = csi_array_append (ctx,
+                                  scan->build_procedure.datum.array,
+                                  &obj);
+    else
+       status = scan_push (ctx, &obj);
+    if (_csi_unlikely (status))
+       longjmp (scan->jmpbuf, status);
+}
+
+static inline void
+scan_read (csi_scanner_t *scan, csi_file_t *src, void *ptr, int len)
+{
+    uint8_t *data = ptr;
+    do {
+       int ret = csi_file_read (src, data, len);
+       if (_csi_unlikely (ret == 0))
+           longjmp (scan->jmpbuf, _csi_error (CSI_STATUS_READ_ERROR));
+       data += ret;
+       len -= ret;
+    } while (_csi_unlikely (len));
+}
+
+static void
+string_read (csi_t *ctx,
+            csi_scanner_t *scan,
+            csi_file_t *src,
+            int len,
+            int compressed,
+            csi_object_t *obj)
+{
+    csi_status_t status;
+
+    status = csi_string_new (ctx, obj, NULL, len);
+    if (_csi_unlikely (status))
+       longjmp (scan->jmpbuf, status);
+
+    if (compressed) {
+       uint32_t u32;
+       scan_read (scan, src, &u32, 4);
+       obj->datum.string->deflate = be32 (u32);
+    }
+
+    if (_csi_likely (len))
+       scan_read (scan, src, obj->datum.string->string, len);
+    obj->datum.string->string[len] = '\0';
+}
+
+static void
+_scan_file (csi_t *ctx, csi_file_t *src)
+{
+    csi_scanner_t *scan = &ctx->scanner;
+    int c, next;
+    union {
+       int8_t i8;
+       uint8_t u8;
+       int16_t i16;
+       uint16_t u16;
+       int32_t i32;
+       uint32_t u32;
+       float f;
+    } u;
+    int deflate = 0;
+    int string_p;
+
+scan_none:
+    while ((c = csi_file_getc (src)) != EOF) {
+       csi_object_t obj = { CSI_OBJECT_TYPE_NULL };
+
+       switch (c) {
+       case 0xa:
+           scan->line_number++;
+       case 0x0:
+       case 0x9:
+       case 0xc:
+       case 0xd:
+       case 0x20: /* ignore whitespace */
+           break;
+
+       case '%':
+           goto scan_comment;
+
+       case '(':
+           goto scan_string;
+
+       case '[': /* needs special case */
+       case ']':
+       case '{':
+       case '}':
+           token_start (scan);
+           token_add_unchecked (scan, c);
+           token_end (ctx, scan, src);
+           goto scan_none;
+
+       case '<':
+           next = csi_file_getc (src);
+           switch (next) {
+           case EOF:
+               csi_file_putc (src, '<');
+               return;
+           case '<':
+               /* dictionary name */
+               token_start (scan);
+               token_add_unchecked (scan, '<');
+               token_add_unchecked (scan, '<');
+               token_end (ctx, scan, src);
+               goto scan_none;
+           case '|':
+               deflate = 1;
+           case '~':
+               goto scan_base85;
+           case '{':
+               goto scan_base64;
+           default:
+               csi_file_putc (src, next);
+               goto scan_hex;
+           }
+           break;
+
+           /* binary token */
+#define MSB_INT8 128
+#define MSB_UINT8 129
+#define MSB_INT16 130
+#define MSB_UINT16 131
+#define MSB_INT32 132
+#define LSB_INT8 MSB_INT8
+#define LSB_UINT8 MSB_UINT8
+#define LSB_INT16 133
+#define LSB_UINT16 134
+#define LSB_INT32 135
+#define MSB_FLOAT32 140
+#define LSB_FLOAT32 141
+       case MSB_INT8:
+           scan_read (scan, src, &u.i8, 1);
+           csi_integer_new (&obj, u.i8);
+           break;
+       case MSB_UINT8:
+           scan_read (scan, src, &u.u8, 1);
+           csi_integer_new (&obj, u.u8);
+           break;
+       case MSB_INT16:
+           scan_read (scan, src, &u.i16, 2);
+           csi_integer_new (&obj, be16 (u.i16));
+           break;
+       case LSB_INT16:
+           scan_read (scan, src, &u.i16, 2);
+           csi_integer_new (&obj, le16 (u.i16));
+           break;
+       case MSB_UINT16:
+           scan_read (scan, src, &u.u16, 2);
+           csi_integer_new (&obj, be16 (u.u16));
+           break;
+       case LSB_UINT16:
+           scan_read (scan, src, &u.u16, 2);
+           csi_integer_new (&obj, le16 (u.u16));
+           break;
+       case MSB_INT32:
+           scan_read (scan, src, &u.i32, 4);
+           csi_integer_new (&obj, be32 (u.i32));
+           break;
+       case LSB_INT32:
+           scan_read (scan, src, &u.i32, 4);
+           csi_integer_new (&obj, le32 (u.i32));
+           break;
+
+       case 136: /* 16.16 msb */
+           scan_read (scan, src, &u.i32, 4);
+           csi_real_new (&obj, be32 (u.i32) / 65536.);
+           break;
+       case 137: /* 16.16 lsb */
+           scan_read (scan, src, &u.i32, 4);
+           csi_real_new (&obj, le32 (u.i32) / 65536.);
+           break;
+       case 138: /* 24.8 msb */
+           scan_read (scan, src, &u.i32, 4);
+           csi_real_new (&obj, be32 (u.i32) / 256.);
+           break;
+       case 139: /* 24.8 lsb */
+           scan_read (scan, src, &u.i32, 4);
+           csi_real_new (&obj, le32 (u.i32) / 256.);
+           break;
+       case MSB_FLOAT32:
+           scan_read (scan, src, &u.i32, 4);
+           u.i32 = be32 (u.i32);
+           csi_real_new (&obj, u.f);
+           break;
+       case LSB_FLOAT32:
+           scan_read (scan, src, &u.i32, 4);
+           u.i32 = le32 (u.i32);
+           csi_real_new (&obj, u.f);
+           break;
+
+#define STRING_1 142
+#define STRING_2_MSB 144
+#define STRING_2_LSB 146
+#define STRING_4_MSB 148
+#define STRING_4_LSB 150
+#define STRING_DEFLATE 1
+       case STRING_1:
+       case STRING_1 | STRING_DEFLATE:
+           scan_read (scan, src, &u.u8, 1);
+           string_read (ctx, scan, src, u.u8, c & STRING_DEFLATE, &obj);
+           break;
+       case STRING_2_MSB:
+       case STRING_2_MSB | STRING_DEFLATE:
+           scan_read (scan, src, &u.u16, 2);
+           string_read (ctx, scan, src, be16 (u.u16),  c & STRING_DEFLATE, &obj);
+           break;
+       case STRING_2_LSB:
+       case STRING_2_LSB | STRING_DEFLATE:
+           scan_read (scan, src, &u.u16, 2);
+           string_read (ctx, scan, src, le16 (u.u16), c & STRING_DEFLATE, &obj);
+           break;
+       case STRING_4_MSB:
+       case STRING_4_MSB | STRING_DEFLATE:
+           scan_read (scan, src, &u.u32, 4);
+           string_read (ctx, scan, src, be32 (u.u32), c & STRING_DEFLATE, &obj);
+           break;
+       case STRING_4_LSB:
+       case STRING_4_LSB | STRING_DEFLATE:
+           scan_read (scan, src, &u.u32, 4);
+           string_read (ctx, scan, src, le32 (u.u32), c & STRING_DEFLATE, &obj);
+           break;
+
+#define OPCODE 152
+       case OPCODE:
+           scan_read (scan, src, &u.u8, 1);
+           csi_operator_new (&obj, ctx->opcode[u.u8]);
+           break;
+
+       case OPCODE | 1:
+           scan_read (scan, src, &u.u8, 1);
+           csi_operator_new (&obj, ctx->opcode[u.u8]);
+           obj.type &= ~CSI_OBJECT_ATTR_EXECUTABLE;
+           break;
+
+           /* unassigned */
+       case 154:
+       case 155:
+       case 156:
+       case 157:
+       case 158:
+       case 159:
+           longjmp (scan->jmpbuf, _csi_error (CSI_STATUS_INVALID_SCRIPT));
+
+       case '#': /* PDF 1.2 escape code */
+           {
+               int c_hi = csi_file_getc (src);
+               int c_lo = csi_file_getc (src);
+               c = (hex_value (c_hi) << 4) | hex_value (c_lo);
+           }
+           /* fall-through */
+       default:
+           token_start (scan);
+           token_add_unchecked (scan, c);
+           goto scan_token;
+       }
+
+       if (obj.type != CSI_OBJECT_TYPE_NULL) {
+           cairo_status_t status;
+
+           if (scan->build_procedure.type != CSI_OBJECT_TYPE_NULL) {
+               status = csi_array_append (ctx,
+                                          scan->build_procedure.datum.array,
+                                          &obj);
+           } else if (obj.type & CSI_OBJECT_ATTR_EXECUTABLE) {
+               status = scan_execute (ctx, &obj);
+               csi_object_free (ctx, &obj);
+           } else {
+               status = scan_push (ctx, &obj);
+           }
+           if (_csi_unlikely (status))
+               longjmp (scan->jmpbuf, status);
+       }
+    }
+    return;
+
+scan_token:
+    while ((c = csi_file_getc (src)) != EOF) {
+       switch (c) {
+       case 0xa:
+           scan->line_number++;
+       case 0x0:
+       case 0x9:
+       case 0xc:
+       case 0xd:
+       case 0x20:
+           token_end (ctx, scan, src);
+           goto scan_none;
+
+           /* syntax delimiters */
+       case '%':
+           token_end (ctx, scan, src);
+           goto scan_comment;
+           /* syntax error? */
+       case '(':
+           token_end (ctx, scan, src);
+           goto scan_string;
+           /* XXX syntax error? */
+       case ')':
+           token_end (ctx, scan, src);
+           goto scan_none;
+       case '/':
+           /* need to special case '^//?' */
+           if (scan->buffer.ptr > scan->buffer.base+1 ||
+               scan->buffer.base[0] != '/')
+           {
+               token_end (ctx, scan, src);
+               token_start (scan);
+           }
+           token_add_unchecked (scan, '/');
+           goto scan_token;
+
+       case '{':
+       case '}':
+       case ']':
+           token_end (ctx, scan, src);
+           token_start (scan);
+           token_add_unchecked (scan, c);
+           token_end (ctx, scan, src);
+           goto scan_none;
+
+       case '<':
+           csi_file_putc (src, '<');
+           token_end (ctx, scan, src);
+           goto scan_none;
+
+       case '#': /* PDF 1.2 escape code */
+           {
+               int c_hi = csi_file_getc (src);
+               int c_lo = csi_file_getc (src);
+               c = (hex_value (c_hi) << 4) | hex_value (c_lo);
+           }
+           /* fall-through */
+       default:
+           token_add (ctx, scan, c);
+           break;
+       }
+    }
+    token_end (ctx, scan, src);
+    return;
+
+scan_comment:
+    /* discard until newline */
+    while ((c = csi_file_getc (src)) != EOF) {
+       switch (c) {
+       case 0xa:
+           scan->line_number++;
+       case 0xc:
+           goto scan_none;
+       }
+    }
+    return;
+
+scan_string:
+    buffer_reset (&scan->buffer);
+    string_p = 1;
+    while ((c = csi_file_getc (src)) != EOF) {
+       switch (c) {
+       case '\\': /* escape */
+           next = csi_file_getc (src);
+           switch (next) {
+           case EOF:
+               longjmp (scan->jmpbuf, _csi_error (CSI_STATUS_INVALID_SCRIPT));
+
+           case 'n':
+               string_add (ctx, scan, '\n');
+               break;
+           case 'r':
+               string_add (ctx, scan, '\r');
+               break;
+           case 't':
+               string_add (ctx, scan, '\t');
+               break;
+           case 'b':
+               string_add (ctx, scan, '\b');
+               break;
+           case 'f':
+               string_add (ctx, scan, '\f');
+               break;
+           case '\\':
+               string_add (ctx, scan, '\\');
+               break;
+           case '(':
+               string_add (ctx, scan, '(');
+               break;
+           case ')':
+               string_add (ctx, scan, ')');
+               break;
+
+           case '0': case '1': case '2': case '3':
+           case '4': case '5': case '6': case '7':
+               { /* octal code: \d{1,3} */
+                   int i;
+
+                   c = next - '0';
+
+                   for (i = 0; i < 2; i++) {
+                       next = csi_file_getc (src);
+                       switch (next) {
+                       case EOF:
+                           return;
+
+                       case '0': case '1': case '2': case '3':
+                       case '4': case '5': case '6': case '7':
+                           c = 8*c + next-'0';
+                           break;
+
+                       default:
+                           csi_file_putc (src, next);
+                           goto octal_code_done;
+                       }
+                   }
+  octal_code_done:
+                   string_add (ctx, scan, c);
+               }
+               break;
+
+           case 0xa:
+               /* skip the newline */
+               next = csi_file_getc (src); /* might be compound LFCR */
+               switch (next) {
+               case EOF:
+                   return;
+               case 0xc:
+                   break;
+               default:
+                   csi_file_putc (src, next);
+                   break;
+               }
+               scan->line_number++;
+               break;
+           case 0xc:
+               break;
+
+           default:
+               /* ignore the '\' */
+               break;
+           }
+           break;
+
+       case '(':
+           string_p++;
+           string_add (ctx, scan, c);
+           break;
+
+       case ')':
+           if (--string_p == 0) {
+               string_end (ctx, scan);
+               goto scan_none;
+           }
+           /* fall through */
+       default:
+           string_add (ctx, scan, c);
+           break;
+       }
+    }
+    longjmp (scan->jmpbuf, _csi_error (CSI_STATUS_INVALID_SCRIPT));
+
+scan_hex:
+    buffer_reset (&scan->buffer);
+    scan->accumulator_count = 0;
+    scan->accumulator = 0;
+    while ((c = csi_file_getc (src)) != EOF) {
+       switch (c) {
+       case 0xa:
+           scan->line_number++;
+       case 0x0:
+       case 0x9:
+       case 0xc:
+       case 0xd:
+       case 0x20: /* ignore whitespace */
+           break;
+
+       case '>':
+           hex_end (ctx, scan); /* fixup odd digit with '0' */
+           goto scan_none;
+
+       case '0':
+       case '1':
+       case '2':
+       case '3':
+       case '4':
+       case '5':
+       case '6':
+       case '7':
+       case '8':
+       case '9':
+       case 'a':
+       case 'b':
+       case 'c':
+       case 'd':
+       case 'e':
+       case 'f':
+       case 'A':
+       case 'B':
+       case 'C':
+       case 'D':
+       case 'E':
+       case 'F':
+           hex_add (ctx, scan, c);
+           break;
+
+       default:
+           longjmp (scan->jmpbuf, _csi_error (CSI_STATUS_INVALID_SCRIPT));
+       }
+    }
+    longjmp (scan->jmpbuf, _csi_error (CSI_STATUS_INVALID_SCRIPT));
+
+scan_base85:
+    buffer_reset (&scan->buffer);
+    scan->accumulator = 0;
+    scan->accumulator_count = 0;
+    while ((c = csi_file_getc (src)) != EOF) {
+       switch (c) {
+       case '~':
+           next = csi_file_getc (src);
+           switch (next) {
+           case EOF:
+               return;
+
+           case '>':
+               base85_end (ctx, scan, deflate);
+               deflate = 0;
+               goto scan_none;
+           }
+           csi_file_putc (src, next);
+
+           /* fall-through */
+       default:
+           base85_add (ctx, scan, c);
+           break;
+       }
+    }
+    longjmp (scan->jmpbuf, _csi_error (CSI_STATUS_INVALID_SCRIPT));
+
+scan_base64:
+    buffer_reset (&scan->buffer);
+    scan->accumulator = 0;
+    scan->accumulator_count = 0;
+    while ((c = csi_file_getc (src)) != EOF) {
+       switch (c) {
+       case '}':
+           next = csi_file_getc (src);
+           switch (next) {
+           case EOF:
+               return;
+
+           case '>':
+               base64_end (ctx, scan);
+               goto scan_none;
+           }
+           longjmp (scan->jmpbuf, _csi_error (CSI_STATUS_INVALID_SCRIPT));
+
+       default:
+           base64_add (ctx, scan, c);
+           break;
+       }
+    }
+    longjmp (scan->jmpbuf, _csi_error (CSI_STATUS_INVALID_SCRIPT));
+}
+
+static csi_status_t
+_scan_push (csi_t *ctx, csi_object_t *obj)
+{
+    if (DEBUG_SCAN) {
+       fprintf (stderr, "push ");
+       fprintf_obj (stderr, ctx, obj);
+    }
+    return _csi_push_ostack (ctx, obj);
+}
+
+static csi_status_t
+_scan_execute (csi_t *ctx, csi_object_t *obj)
+{
+    if (DEBUG_SCAN) {
+       fprintf (stderr, "exec ");
+       fprintf_obj (stderr, ctx, obj);
+    }
+    return csi_object_execute (ctx, obj);
+}
+
+csi_status_t
+_csi_scanner_init (csi_t *ctx, csi_scanner_t *scanner)
+{
+    csi_status_t status;
+
+    memset (scanner, 0, sizeof (csi_scanner_t));
+
+    status = buffer_init (ctx, &scanner->buffer);
+    if (status)
+       return status;
+
+    status = _csi_stack_init (ctx, &scanner->procedure_stack, 4);
+    if (status)
+       return status;
+
+    scanner->bind = 0;
+    scanner->push = _scan_push;
+    scanner->execute = _scan_execute;
+
+    return CSI_STATUS_SUCCESS;
+}
+
+void
+_csi_scanner_fini (csi_t *ctx, csi_scanner_t *scanner)
+{
+    buffer_fini (ctx, &scanner->buffer);
+    _csi_stack_fini (ctx, &scanner->procedure_stack);
+    if (scanner->build_procedure.type != CSI_OBJECT_TYPE_NULL)
+       csi_object_free (ctx, &scanner->build_procedure);
+}
+
+csi_status_t
+_csi_scan_file (csi_t *ctx, csi_file_t *src)
+{
+    csi_status_t status;
+    int old_line_number;
+
+    /* This function needs to be reentrant to handle recursive scanners.
+     * i.e. one script executes a second.
+     */
+
+    if (ctx->scanner.depth++ == 0) {
+       if ((status = setjmp (ctx->scanner.jmpbuf))) {
+           ctx->scanner.depth = 0;
+           return status;
+       }
+    }
+
+    old_line_number = ctx->scanner.line_number;
+    ctx->scanner.line_number = 0;
+
+    _scan_file (ctx, src);
+
+    ctx->scanner.line_number = old_line_number;
+
+    --ctx->scanner.depth;
+    return CSI_STATUS_SUCCESS;
+}
+
+struct _translate_closure {
+    csi_dictionary_t *opcodes;
+    cairo_write_func_t write_func;
+    void *closure;
+};
+
+static csi_status_t
+_translate_name (csi_t *ctx,
+                csi_name_t name,
+                csi_boolean_t executable,
+                struct _translate_closure *closure)
+{
+    if (executable) {
+       csi_dictionary_entry_t *entry;
+       uint16_t u16;
+
+       /* Bind executable names.
+        * XXX This may break some scripts that overload system operators.
+        */
+       entry = _csi_hash_table_lookup (&closure->opcodes->hash_table,
+                                       (csi_hash_entry_t *) &name);
+       if (entry == NULL)
+           goto STRING;
+
+       u16 = entry->value.datum.integer;
+       u16 = be16 (u16);
+       closure->write_func (closure->closure, (unsigned char *) &u16, 2);
+    } else {
+       closure->write_func (closure->closure, (unsigned char *) "/", 1);
+STRING:
+       closure->write_func (closure->closure,
+                            (unsigned char *) name,
+                            strlen ((char *) name));
+       closure->write_func (closure->closure, (unsigned char *) "\n", 1);
+    }
+
+    return CSI_STATUS_SUCCESS;
+}
+
+static csi_status_t
+_translate_operator (csi_t *ctx,
+                    csi_operator_t op,
+                    csi_boolean_t executable,
+                    struct _translate_closure *closure)
+{
+    csi_dictionary_entry_t *entry;
+    uint16_t u16;
+
+    entry = _csi_hash_table_lookup (&closure->opcodes->hash_table,
+                                   (csi_hash_entry_t *) &op);
+    if (entry == NULL)
+       return _csi_error (CSI_STATUS_INVALID_SCRIPT);
+
+    u16 = entry->value.datum.integer;
+    if (! executable)
+       u16 += 1 << 8;
+    u16 = be16 (u16);
+    closure->write_func (closure->closure, (unsigned char *) &u16, 2);
+
+    return CSI_STATUS_SUCCESS;
+}
+
+static csi_status_t
+_translate_integer (csi_t *ctx,
+                   csi_integer_t i,
+                   struct _translate_closure *closure)
+{
+    uint8_t hdr;
+    union {
+       int8_t i8;
+       uint8_t u8;
+       int16_t i16;
+       uint16_t u16;
+       int32_t i32;
+       uint32_t u32;
+    } u;
+    int len;
+
+#if WORDS_BIGENDIAN
+    if (i < INT16_MIN) {
+       hdr = MSB_INT32;
+       len = 4;
+       u.i32 = i;
+    } else if (i < INT8_MIN) {
+       hdr = MSB_INT16;
+       len = 2;
+       u.i16 = i;
+    } else if (i < 0) {
+       hdr = MSB_INT8;
+       len = 1;
+       u.i8 = i;
+    } else if (i <= UINT8_MAX) {
+       hdr = MSB_UINT8;
+       len = 1;
+       u.u8 = i;
+    } else if (i <= UINT16_MAX) {
+       hdr = MSB_UINT16;
+       len = 2;
+       u.u16 = i;
+    } else {
+       hdr = MSB_INT32;
+       len = 4;
+       u.u32 = i;
+    }
+#else
+    if (i < INT16_MIN) {
+       hdr = LSB_INT32;
+       len = 4;
+       u.i32 = i;
+    } else if (i < INT8_MIN) {
+       hdr = LSB_INT16;
+       len = 2;
+       u.i16 = i;
+    } else if (i < 0) {
+       hdr = LSB_INT8;
+       len = 1;
+       u.i8 = i;
+    } else if (i <= UINT8_MAX) {
+       hdr = LSB_UINT8;
+       len = 1;
+       u.u8 = i;
+    } else if (i <= UINT16_MAX) {
+       hdr = LSB_UINT16;
+       len = 2;
+       u.u16 = i;
+    } else {
+       hdr = LSB_INT32;
+       len = 4;
+       u.u32 = i;
+    }
+#endif
+
+    closure->write_func (closure->closure, (unsigned char *) &hdr, 1);
+    closure->write_func (closure->closure, (unsigned char *) &u, len);
+
+    return CSI_STATUS_SUCCESS;
+}
+
+static csi_status_t
+_translate_real (csi_t *ctx,
+                csi_real_t real,
+                struct _translate_closure *closure)
+{
+    uint8_t hdr;
+
+    if (real >= INT32_MIN && real <= INT32_MAX && (int) real == real)
+       return _translate_integer (ctx, real, closure);
+
+#if WORDS_BIGENDIAN
+    hdr = MSB_FLOAT32;
+#else
+    hdr = LSB_FLOAT32;
+#endif
+    closure->write_func (closure->closure, (unsigned char *) &hdr, 1);
+    closure->write_func (closure->closure, (unsigned char *) &real, 4);
+
+    return CSI_STATUS_SUCCESS;
+}
+
+static csi_status_t
+_translate_string (csi_t *ctx,
+                  csi_string_t *string,
+                  struct _translate_closure *closure)
+{
+    uint8_t hdr;
+    union {
+       uint8_t u8;
+       uint16_t u16;
+       uint32_t u32;
+    } u;
+    int len;
+
+#if WORDS_BIGENDIAN
+    if (string->len <= UINT8_MAX) {
+       hdr = STRING_1;
+       u.u8 = string->len;
+       len = 1;
+    } else if (string->len <= UINT16_MAX) {
+       hdr = STRING_2_MSB;
+       u.u16 = string->len;
+       len = 2;
+    } else {
+       hdr = STRING_4_MSB;
+       u.u32 = string->len;
+       len = 4;
+    }
+#else
+    if (string->len <= UINT8_MAX) {
+       hdr = STRING_1;
+       u.u8 = string->len;
+       len = 1;
+    } else if (string->len <= UINT16_MAX) {
+       hdr = STRING_2_LSB;
+       u.u16 = string->len;
+       len = 2;
+    } else {
+       hdr = STRING_4_LSB;
+       u.u32 = string->len;
+       len = 4;
+    }
+#endif
+    if (string->deflate)
+       hdr |= STRING_DEFLATE;
+
+    closure->write_func (closure->closure,
+                        (unsigned char *) &hdr, 1);
+    closure->write_func (closure->closure,
+                        (unsigned char *) &u, len);
+    if (string->deflate) {
+       uint32_t u32 = to_be32 (string->deflate);
+       closure->write_func (closure->closure,
+                            (unsigned char *) &u32, 4);
+    }
+    closure->write_func (closure->closure,
+                        (unsigned char *) string->string, string->len);
+
+    return CSI_STATUS_SUCCESS;
+}
+
+static csi_status_t
+_translate_push (csi_t *ctx, csi_object_t *obj)
+{
+    struct _translate_closure *closure = ctx->scanner.closure;
+
+    if (0) {
+       fprintf (stderr, "push ");
+       fprintf_obj (stderr, ctx, obj);
+    }
+
+    switch (csi_object_get_type (obj)) {
+    case CSI_OBJECT_TYPE_NAME:
+       return _translate_name (ctx, obj->datum.name, FALSE, closure);
+
+    case CSI_OBJECT_TYPE_OPERATOR:
+       return _translate_operator (ctx, obj->datum.op, FALSE, closure);
+
+    case CSI_OBJECT_TYPE_INTEGER:
+       return _translate_integer (ctx, obj->datum.integer, closure);
+
+    case CSI_OBJECT_TYPE_REAL:
+       return _translate_real (ctx, obj->datum.real, closure);
+
+    case CSI_OBJECT_TYPE_STRING:
+       return _translate_string (ctx, obj->datum.string, closure);
+
+    case CSI_OBJECT_TYPE_NULL:
+    case CSI_OBJECT_TYPE_BOOLEAN:
+    case CSI_OBJECT_TYPE_MARK:
+    case CSI_OBJECT_TYPE_ARRAY:
+    case CSI_OBJECT_TYPE_DICTIONARY:
+    case CSI_OBJECT_TYPE_FILE:
+    case CSI_OBJECT_TYPE_MATRIX:
+    case CSI_OBJECT_TYPE_CONTEXT:
+    case CSI_OBJECT_TYPE_FONT:
+    case CSI_OBJECT_TYPE_PATTERN:
+    case CSI_OBJECT_TYPE_SCALED_FONT:
+    case CSI_OBJECT_TYPE_SURFACE:
+       longjmp (ctx->scanner.jmpbuf,  _csi_error (CSI_STATUS_INVALID_SCRIPT));
+       break;
+    }
+
+    csi_object_free (ctx, obj);
+    return CSI_STATUS_SUCCESS;
+}
+
+static csi_status_t
+_translate_execute (csi_t *ctx, csi_object_t *obj)
+{
+    struct _translate_closure *closure = ctx->scanner.closure;
+
+    if (0) {
+       fprintf (stderr, "exec ");
+       fprintf_obj (stderr, ctx, obj);
+    }
+
+    switch (csi_object_get_type (obj)) {
+    case CSI_OBJECT_TYPE_NAME:
+       return _translate_name (ctx, obj->datum.name, TRUE, closure);
+
+    case CSI_OBJECT_TYPE_OPERATOR:
+       return _translate_operator (ctx, obj->datum.op, TRUE, closure);
+
+    case CSI_OBJECT_TYPE_INTEGER:
+       return _translate_integer (ctx, obj->datum.integer, closure);
+
+    case CSI_OBJECT_TYPE_REAL:
+       return _translate_real (ctx, obj->datum.real, closure);
+
+    case CSI_OBJECT_TYPE_STRING:
+       return _translate_string (ctx, obj->datum.string, closure);
+
+    case CSI_OBJECT_TYPE_NULL:
+    case CSI_OBJECT_TYPE_BOOLEAN:
+    case CSI_OBJECT_TYPE_MARK:
+    case CSI_OBJECT_TYPE_ARRAY:
+    case CSI_OBJECT_TYPE_DICTIONARY:
+    case CSI_OBJECT_TYPE_FILE:
+    case CSI_OBJECT_TYPE_MATRIX:
+    case CSI_OBJECT_TYPE_CONTEXT:
+    case CSI_OBJECT_TYPE_FONT:
+    case CSI_OBJECT_TYPE_PATTERN:
+    case CSI_OBJECT_TYPE_SCALED_FONT:
+    case CSI_OBJECT_TYPE_SURFACE:
+       longjmp (ctx->scanner.jmpbuf,  _csi_error (CSI_STATUS_INVALID_SCRIPT));
+       break;
+    }
+
+    return CSI_STATUS_SUCCESS;
+}
+
+static csi_status_t
+build_opcodes (csi_t *ctx, csi_dictionary_t **out)
+{
+    csi_object_t obj;
+    csi_dictionary_t *dict;
+    const csi_operator_def_t *def;
+    csi_status_t status;
+    int opcode = OPCODE << 8;
+
+    status = csi_dictionary_new (ctx, &obj);
+    if (_csi_unlikely (status))
+       return status;
+
+    dict = obj.datum.dictionary;
+
+    csi_integer_new (&obj, opcode++);
+    status = csi_dictionary_put (ctx, dict, 0, &obj);
+    if (_csi_unlikely (status))
+       goto FAIL;
+
+    for (def = _csi_operators (); def->name != NULL; def++) {
+       csi_object_t name;
+       csi_dictionary_entry_t *entry;
+       int code;
+
+       entry = _csi_hash_table_lookup (&dict->hash_table,
+                                       (csi_hash_entry_t *) &def->op);
+       if (entry == NULL) {
+           code = opcode++;
+           csi_integer_new (&obj, code);
+           status = csi_dictionary_put (ctx, dict, (csi_name_t) def->op, &obj);
+           if (_csi_unlikely (status))
+               goto FAIL;
+       } else {
+           code = entry->value.datum.integer;
+           csi_integer_new (&obj, code);
+       }
+       assert (ctx->opcode[code & 0xff] == def->op);
+
+       status = csi_name_new_static (ctx, &name, def->name);
+       if (_csi_unlikely (status))
+           goto FAIL;
+
+       status = csi_dictionary_put (ctx, dict, name.datum.name, &obj);
+       if (_csi_unlikely (status))
+           goto FAIL;
+    }
+
+    *out = dict;
+    return CSI_STATUS_SUCCESS;
+
+FAIL:
+    csi_dictionary_free (ctx, dict);
+    return status;
+}
+
+csi_status_t
+_csi_translate_file (csi_t *ctx,
+                    csi_file_t *file,
+                    cairo_write_func_t write_func,
+                    void *closure)
+{
+    csi_status_t status;
+    struct _translate_closure translator;
+
+    if ((status = setjmp (ctx->scanner.jmpbuf)))
+       return status;
+
+    status = build_opcodes (ctx, &translator.opcodes);
+    if (_csi_unlikely (status))
+       return status;
+
+    translator.write_func = write_func;
+    translator.closure = closure;
+    ctx->scanner.closure = &translator;
+
+    ctx->scanner.bind = 1;
+    ctx->scanner.push = _translate_push;
+    ctx->scanner.execute = _translate_execute;
+
+    _scan_file (ctx, file);
+
+    ctx->scanner.bind = 0;
+    ctx->scanner.push = _scan_push;
+    ctx->scanner.execute = _scan_execute;
+
+    csi_dictionary_free (ctx, translator.opcodes);
+
+    return CSI_STATUS_SUCCESS;
+}
diff --git a/util/cairo-script/cairo-script-stack.c b/util/cairo-script/cairo-script-stack.c
new file mode 100644 (file)
index 0000000..b1d146c
--- /dev/null
@@ -0,0 +1,196 @@
+/*
+ * Copyright © 2008 Chris Wilson <chris@chris-wilson.co.uk>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Chris Wilson.
+ *
+ * Contributor(s):
+ *     Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairo-script-private.h"
+
+#include <limits.h> /* INT_MAX */
+#include <string.h>
+
+csi_status_t
+_csi_stack_init (csi_t *ctx, csi_stack_t *stack, csi_integer_t size)
+{
+    csi_status_t status = CSI_STATUS_SUCCESS;
+
+    stack->len = 0;
+    stack->size = size;
+    /* assert ((unsigned) size < INT_MAX / sizeof (csi_object_t)); */
+    stack->objects = _csi_alloc (ctx, size * sizeof (csi_object_t));
+    if (_csi_unlikely (stack->objects == NULL))
+       status = _csi_error (CSI_STATUS_NO_MEMORY);
+
+    return status;
+}
+
+void
+_csi_stack_fini (csi_t *ctx, csi_stack_t *stack)
+{
+    csi_integer_t n;
+
+    for (n = 0; n < stack->len; n++)
+       csi_object_free (ctx, &stack->objects[n]);
+
+    _csi_free (ctx, stack->objects);
+}
+
+csi_status_t
+_csi_stack_roll (csi_t *ctx,
+                csi_stack_t *stack,
+                csi_integer_t mod, csi_integer_t n)
+{
+    csi_object_t stack_copy[128];
+    csi_object_t *copy;
+    csi_integer_t last, i, len;
+
+    switch (mod) { /* special cases */
+    case 1:
+       last = stack->len - 1;
+       stack_copy[0] = stack->objects[last];
+       for (i = last; --n; i--)
+           stack->objects[i] = stack->objects[i-1];
+       stack->objects[i] = stack_copy[0];
+       return CSI_STATUS_SUCCESS;
+    case -1:
+       last = stack->len - 1;
+       stack_copy[0] = stack->objects[i = last - n + 1];
+       for (; --n; i++)
+           stack->objects[i] = stack->objects[i+1];
+       stack->objects[i] = stack_copy[0];
+       return CSI_STATUS_SUCCESS;
+    }
+
+    /* fall back to a copy */
+    if (n > ARRAY_LENGTH (stack_copy)) {
+       if (_csi_unlikely ((unsigned) n > INT_MAX / sizeof (csi_object_t)))
+           return _csi_error (CSI_STATUS_NO_MEMORY);
+       copy = _csi_alloc (ctx, n * sizeof (csi_object_t));
+       if (copy == NULL)
+           return _csi_error (CSI_STATUS_NO_MEMORY);
+    } else
+       copy = stack_copy;
+
+    i = stack->len - n;
+    memcpy (copy, stack->objects + i, n * sizeof (csi_object_t));
+    mod = -mod;
+    if (mod < 0)
+       mod += n;
+    last = mod;
+    for (len = n; n--; i++) {
+       stack->objects[i] = copy[last];
+       if (++last == len)
+           last = 0;
+    }
+
+    if (copy != stack_copy)
+       _csi_free (ctx, copy);
+
+    return CSI_STATUS_SUCCESS;
+}
+
+csi_status_t
+_csi_stack_grow (csi_t *ctx, csi_stack_t *stack, csi_integer_t cnt)
+{
+    csi_integer_t newsize;
+    csi_object_t *newstack;
+
+    if (_csi_likely (cnt <= stack->size))
+       return CSI_STATUS_SUCCESS;
+    if (_csi_unlikely ((unsigned) cnt >= INT_MAX / sizeof (csi_object_t)))
+       return _csi_error (CSI_STATUS_NO_MEMORY);
+
+    newsize = stack->size;
+    do {
+       newsize *= 2;
+    } while (newsize <= cnt);
+
+    newstack = _csi_realloc (ctx,
+                            stack->objects,
+                            newsize * sizeof (csi_object_t));
+    if (_csi_unlikely (newstack == NULL))
+       return _csi_error (CSI_STATUS_NO_MEMORY);
+
+    stack->objects = newstack;
+    stack->size  = newsize;
+
+    return CSI_STATUS_SUCCESS;
+}
+
+csi_status_t
+_csi_stack_push_internal (csi_t *ctx, csi_stack_t *stack,
+                         const csi_object_t *obj)
+{
+    csi_status_t status;
+
+    status = _csi_stack_grow (ctx, stack, stack->size + 1);
+    if (_csi_unlikely (status))
+       return status;
+
+    stack->objects[stack->len++] = *obj;
+    return CSI_STATUS_SUCCESS;
+}
+
+csi_object_t *
+_csi_stack_peek (csi_stack_t *stack, csi_integer_t i)
+{
+    if (_csi_unlikely (stack->len < i))
+       return NULL;
+
+    return &stack->objects[stack->len - i -1];
+}
+
+void
+_csi_stack_pop (csi_t *ctx, csi_stack_t *stack, csi_integer_t count)
+{
+    if (_csi_unlikely (stack->len < count))
+       count = stack->len;
+
+    while (count--)
+       csi_object_free (ctx, &stack->objects[--stack->len]);
+}
+
+csi_status_t
+_csi_stack_exch (csi_stack_t *stack)
+{
+    csi_object_t tmp;
+    csi_integer_t n;
+
+    if (_csi_unlikely (stack->len < 2))
+       return _csi_error (CSI_STATUS_INVALID_SCRIPT);
+
+    n = stack->len - 1;
+    tmp = stack->objects[n];
+    stack->objects[n] = stack->objects[n - 1];
+    stack->objects[n - 1] = tmp;
+
+    return CSI_STATUS_SUCCESS;
+}
diff --git a/util/cairo-script/csi-bind.c b/util/cairo-script/csi-bind.c
new file mode 100644 (file)
index 0000000..91b58fb
--- /dev/null
@@ -0,0 +1,76 @@
+#include <cairo.h>
+#include <cairo-script-interpreter.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+
+static cairo_status_t
+write_func (void *closure,
+           const unsigned char *data,
+           unsigned int length)
+{
+    if (fwrite (data, length, 1, closure) != 1)
+       return CAIRO_STATUS_WRITE_ERROR;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+int
+main (int argc, char **argv)
+{
+    FILE *in = stdin, *out = stdout;
+    cairo_status_t status;
+    int i;
+
+    if (argc >= 3) {
+       if (strcmp (argv[argc-1], "-")) {
+           out = fopen (argv[argc-1], "w");
+           if (out == NULL) {
+               fprintf (stderr, "Failed to open output '%s'\n", argv[argc-1]);
+               return 1;
+           }
+       }
+    }
+
+    if (argc > 2) {
+       for (i = 1; i < argc - 1; i++) {
+           in = fopen (argv[i], "r");
+           if (in == NULL) {
+               fprintf (stderr, "Failed to open input '%s'\n", argv[i]);
+               return 1;
+           }
+
+           status = cairo_script_interpreter_translate_stream (in, write_func, out);
+           fclose (in);
+
+           if (status)
+               break;
+       }
+    } else {
+       if (argc > 1) {
+           if (strcmp (argv[1], "-")) {
+               in = fopen (argv[1], "r");
+               if (in == NULL) {
+                   fprintf (stderr, "Failed to open input '%s'\n", argv[1]);
+                   return 1;
+               }
+           }
+       }
+
+       status = cairo_script_interpreter_translate_stream (in, write_func, out);
+
+       if (in != stdin)
+           fclose (in);
+    }
+
+    if (out != stdout)
+       fclose (out);
+
+    if (status) {
+       fprintf (stderr, "Translation failed: %s\n",
+               cairo_status_to_string (status));
+       return status;
+    }
+
+    return status;
+}
diff --git a/util/cairo-script/csi-exec.c b/util/cairo-script/csi-exec.c
new file mode 100644 (file)
index 0000000..d30b1c9
--- /dev/null
@@ -0,0 +1,41 @@
+#include <cairo.h>
+#include <cairo-script-interpreter.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+
+static cairo_surface_t *
+_surface_create (void *closure,
+                cairo_content_t content,
+                double width, double height,
+                long uid)
+{
+    return cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
+}
+
+int
+main (int argc, char **argv)
+{
+    const cairo_script_interpreter_hooks_t hooks = {
+       .surface_create = _surface_create
+    };
+    cairo_script_interpreter_t *csi;
+    int i;
+
+    for (i = 1; i < argc; i++) {
+       int status, line;
+
+       csi = cairo_script_interpreter_create ();
+       cairo_script_interpreter_install_hooks (csi, &hooks);
+       cairo_script_interpreter_run (csi, argv[i]);
+       line = cairo_script_interpreter_get_line_number (csi);
+       status = cairo_script_interpreter_destroy (csi);
+       if (status) {
+           fprintf (stderr, "Error during replay of '%s', line %d: %d\n",
+                    argv[i], line, status);
+           return 1;
+       }
+    }
+
+    return 0;
+}
diff --git a/util/cairo-script/csi-replay.c b/util/cairo-script/csi-replay.c
new file mode 100644 (file)
index 0000000..67fed3b
--- /dev/null
@@ -0,0 +1,360 @@
+#include <cairo.h>
+#include <cairo-script-interpreter.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static const cairo_user_data_key_t _key;
+
+#define SINGLE_SURFACE 1
+
+#if SINGLE_SURFACE
+static cairo_surface_t *
+_similar_surface_create (void *closure,
+                        cairo_content_t content,
+                        double width, double height,
+                        long uid)
+{
+    return cairo_surface_create_similar (closure, content, width, height);
+}
+
+static struct list {
+    struct list *next;
+    cairo_t *context;
+    cairo_surface_t *surface;
+} *list;
+
+static cairo_t *
+_context_create (void *closure, cairo_surface_t *surface)
+{
+    cairo_t *cr = cairo_create (surface);
+    struct list *l = malloc (sizeof (*l));
+    l->next = list;
+    l->context = cr;
+    l->surface = cairo_surface_reference (surface);
+    list = l;
+    return cr;
+}
+
+static void
+_context_destroy (void *closure, void *ptr)
+{
+    struct list *l, **prev = &list;
+    while ((l = *prev) != NULL) {
+       if (l->context == ptr) {
+           if (cairo_surface_status (l->surface) == CAIRO_STATUS_SUCCESS) {
+               cairo_t *cr = cairo_create (closure);
+               cairo_set_source_surface (cr, l->surface, 0, 0);
+               cairo_paint (cr);
+               cairo_destroy (cr);
+           }
+
+           cairo_surface_destroy (l->surface);
+           *prev = l->next;
+           free (l);
+           return;
+       }
+       prev = &l->next;
+    }
+}
+#endif
+
+#if CAIRO_HAS_XLIB_SURFACE
+#include <cairo-xlib.h>
+static Display *
+_get_display (void)
+{
+    static Display *dpy;
+
+    if (dpy != NULL)
+       return dpy;
+
+    dpy = XOpenDisplay (NULL);
+    if (dpy == NULL) {
+       fprintf (stderr, "Failed to open display.\n");
+       exit (1);
+    }
+
+    return dpy;
+}
+
+static void
+_destroy_window (void *closure)
+{
+    XFlush (_get_display ());
+    XDestroyWindow (_get_display(), (Window) closure);
+}
+
+static cairo_surface_t *
+_xlib_surface_create (void *closure,
+                     cairo_content_t content,
+                     double width, double height,
+                     long uid)
+{
+    Display *dpy;
+    XSetWindowAttributes attr;
+    Visual *visual;
+    int depth;
+    Window w;
+    cairo_surface_t *surface;
+
+    dpy = _get_display ();
+
+    visual = DefaultVisual (dpy, DefaultScreen (dpy));
+    depth = DefaultDepth (dpy, DefaultScreen (dpy));
+    attr.override_redirect = True;
+    w = XCreateWindow (dpy, DefaultRootWindow (dpy), 0, 0,
+                      width <= 0 ? 1 : width,
+                      height <= 0 ? 1 : height,
+                      0, depth,
+                      InputOutput, visual, CWOverrideRedirect, &attr);
+    XMapWindow (dpy, w);
+
+    surface = cairo_xlib_surface_create (dpy, w, visual, width, height);
+    cairo_surface_set_user_data (surface, &_key, (void *) w, _destroy_window);
+
+    return surface;
+}
+
+#if CAIRO_HAS_XLIB_XRENDER_SURFACE
+#include <cairo-xlib-xrender.h>
+
+static void
+_destroy_pixmap (void *closure)
+{
+    XFreePixmap (_get_display(), (Pixmap) closure);
+}
+
+static cairo_surface_t *
+_xrender_surface_create (void *closure,
+                        cairo_content_t content,
+                        double width, double height,
+                        long uid)
+{
+    Display *dpy;
+    Pixmap pixmap;
+    XRenderPictFormat *xrender_format;
+    cairo_surface_t *surface;
+
+    dpy = _get_display ();
+
+    content = CAIRO_CONTENT_COLOR_ALPHA;
+
+    switch (content) {
+    case CAIRO_CONTENT_COLOR_ALPHA:
+       xrender_format = XRenderFindStandardFormat (dpy, PictStandardARGB32);
+       break;
+    case CAIRO_CONTENT_COLOR:
+       xrender_format = XRenderFindStandardFormat (dpy, PictStandardRGB24);
+       break;
+    case CAIRO_CONTENT_ALPHA:
+    default:
+       xrender_format = XRenderFindStandardFormat (dpy, PictStandardA8);
+    }
+
+    pixmap = XCreatePixmap (dpy, DefaultRootWindow (dpy),
+                      width, height, xrender_format->depth);
+
+    surface = cairo_xlib_surface_create_with_xrender_format (dpy, pixmap,
+                                                            DefaultScreenOfDisplay (dpy),
+                                                            xrender_format,
+                                                            width, height);
+    cairo_surface_set_user_data (surface, &_key,
+                                (void *) pixmap, _destroy_pixmap);
+
+    return surface;
+}
+#endif
+#endif
+
+#if CAIRO_HAS_GL_GLX_SURFACE
+#include <cairo-gl.h>
+static cairo_gl_context_t *
+_glx_get_context (cairo_content_t content)
+{
+    static cairo_gl_context_t *context;
+
+    if (context == NULL) {
+       int rgba_attribs[] = {
+           GLX_RGBA,
+           GLX_RED_SIZE, 1,
+           GLX_GREEN_SIZE, 1,
+           GLX_BLUE_SIZE, 1,
+           GLX_ALPHA_SIZE, 1,
+           GLX_DOUBLEBUFFER,
+           None
+       };
+       XVisualInfo *visinfo;
+       GLXContext gl_ctx;
+       Display *dpy;
+
+       dpy = XOpenDisplay (NULL);
+       if (dpy == NULL) {
+           fprintf (stderr, "Failed to open display.\n");
+           exit (1);
+       }
+
+       visinfo = glXChooseVisual (dpy, DefaultScreen (dpy), rgba_attribs);
+       if (visinfo == NULL) {
+           fprintf (stderr, "Failed to create RGBA, double-buffered visual\n");
+           exit (1);
+       }
+
+       gl_ctx = glXCreateContext (dpy, visinfo, NULL, True);
+       XFree (visinfo);
+
+       context = cairo_glx_context_create (dpy, gl_ctx);
+    }
+
+    return context;
+}
+
+static cairo_surface_t *
+_glx_surface_create (void *closure,
+                    cairo_content_t content,
+                    double width, double height,
+                    long uid)
+{
+    if (width == 0)
+       width = 1;
+    if (height == 0)
+       height = 1;
+
+    return cairo_gl_surface_create (_glx_get_context (content),
+                                   content, width, height);
+}
+#endif
+
+#if CAIRO_HAS_PDF_SURFACE
+#include <cairo-pdf.h>
+static cairo_surface_t *
+_pdf_surface_create (void *closure,
+                    cairo_content_t content,
+                    double width, double height,
+                    long uid)
+{
+    return cairo_pdf_surface_create_for_stream (NULL, NULL, width, height);
+}
+#endif
+
+#if CAIRO_HAS_PS_SURFACE
+#include <cairo-ps.h>
+static cairo_surface_t *
+_ps_surface_create (void *closure,
+                   cairo_content_t content,
+                   double width, double height,
+                   long uid)
+{
+    return cairo_ps_surface_create_for_stream (NULL, NULL, width, height);
+}
+#endif
+
+#if CAIRO_HAS_SVG_SURFACE
+#include <cairo-svg.h>
+static cairo_surface_t *
+_svg_surface_create (void *closure,
+                    cairo_content_t content,
+                    double width, double height,
+                    long uid)
+{
+    return cairo_svg_surface_create_for_stream (NULL, NULL, width, height);
+}
+#endif
+
+static cairo_surface_t *
+_image_surface_create (void *closure,
+                      cairo_content_t content,
+                      double width, double height,
+                      long uid)
+{
+    return cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
+}
+
+int
+main (int argc, char **argv)
+{
+    cairo_script_interpreter_t *csi;
+    cairo_script_interpreter_hooks_t hooks = {
+#if SINGLE_SURFACE
+       .surface_create = _similar_surface_create,
+       .context_create = _context_create,
+       .context_destroy = _context_destroy
+#elif CAIRO_HAS_XLIB_XRENDER_SURFACE
+       .surface_create = _xrender_surface_create
+#elif CAIRO_HAS_XLIB_SURFACE
+       .surface_create = _xlib_surface_create
+#elif CAIRO_PDF_SURFACE
+       .surface_create = _pdf_surface_create
+#elif CAIRO_PS_SURFACE
+       .surface_create = _ps_surface_create
+#elif CAIRO_SVG_SURFACE
+       .surface_create = _svg_surface_create
+#else
+       .surface_create = _image_surface_create
+#endif
+    };
+    int i;
+    const struct backends {
+       const char *name;
+       csi_surface_create_func_t create;
+    } backends[] = {
+       { "--image", _image_surface_create },
+#if CAIRO_HAS_XLIB_XRENDER_SURFACE
+       { "--xrender", _xrender_surface_create },
+#endif
+#if CAIRO_HAS_GL_GLX_SURFACE
+       { "--glx", _glx_surface_create },
+#endif
+#if CAIRO_HAS_XLIB_SURFACE
+       { "--xlib", _xlib_surface_create },
+#endif
+#if CAIRO_HAS_PDF_SURFACE
+       { "--pdf", _pdf_surface_create },
+#endif
+#if CAIRO_HAS_PS_SURFACE
+       { "--ps", _ps_surface_create },
+#endif
+#if CAIRO_HAS_SVG_SURFACE
+       { "--svg", _svg_surface_create },
+#endif
+       { NULL, NULL }
+    };
+
+#if SINGLE_SURFACE
+    hooks.closure = backends[0].create (NULL,
+                                       CAIRO_CONTENT_COLOR_ALPHA,
+                                       512, 512,
+                                       0);
+#endif
+
+
+    csi = cairo_script_interpreter_create ();
+    cairo_script_interpreter_install_hooks (csi, &hooks);
+
+    for (i = 1; i < argc; i++) {
+       const struct backends *b;
+
+       for (b = backends; b->name != NULL; b++) {
+           if (strcmp (b->name, argv[i]) == 0) {
+#if SINGLE_SURFACE
+               cairo_surface_destroy (hooks.closure);
+               hooks.closure = b->create (NULL,
+                                          CAIRO_CONTENT_COLOR_ALPHA,
+                                          512, 512,
+                                          0);
+#else
+               hooks.surface_create = b->create;
+#endif
+               cairo_script_interpreter_install_hooks (csi, &hooks);
+               break;
+           }
+       }
+
+       if (b->name == NULL)
+           cairo_script_interpreter_run (csi, argv[i]);
+    }
+    cairo_surface_destroy (hooks.closure);
+
+    return cairo_script_interpreter_destroy (csi);
+}
diff --git a/util/cairo-script/csi-trace.c b/util/cairo-script/csi-trace.c
new file mode 100644 (file)
index 0000000..c57a56b
--- /dev/null
@@ -0,0 +1,40 @@
+#include <cairo-script.h>
+#include <cairo-script-interpreter.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <libgen.h>
+
+static cairo_surface_t *
+_script_surface_create (void *closure,
+                        cairo_content_t content,
+                        double width, double height,
+                        long uid)
+{
+    return cairo_script_surface_create (closure, content, width, height);
+}
+
+int
+main (int argc, char **argv)
+{
+    cairo_script_interpreter_t *csi;
+    cairo_script_interpreter_hooks_t hooks = {
+       .surface_create = _script_surface_create,
+    };
+    int i;
+
+    csi = cairo_script_interpreter_create ();
+
+    for (i = 1; i < argc; i++) {
+       char buf[4096];
+
+       snprintf (buf, sizeof (buf), "%s.trace", basename (argv[i]));
+       cairo_device_destroy (hooks.closure);
+       hooks.closure = cairo_script_create (buf);
+       cairo_script_interpreter_install_hooks (csi, &hooks);
+       cairo_script_interpreter_run (csi, argv[i]);
+    }
+    cairo_device_destroy (hooks.closure);
+
+    return cairo_script_interpreter_destroy (csi);
+}
diff --git a/util/cairo-script/examples/Makefile.am b/util/cairo-script/examples/Makefile.am
new file mode 100644 (file)
index 0000000..a87f02d
--- /dev/null
@@ -0,0 +1,10 @@
+EXTRA_DIST = \
+       dragon.cs \
+       hilbert.cs \
+       infinichess.cs \
+       interference.cs \
+       pythagoras-tree.cs \
+       sierpinski.cs \
+       wedgeAnnulus_crop_ybRings.cs \
+       world-map.cs \
+       zrusin.cs
diff --git a/util/cairo-script/examples/dragon.cs b/util/cairo-script/examples/dragon.cs
new file mode 100644 (file)
index 0000000..1060ca6
--- /dev/null
@@ -0,0 +1,48 @@
+%!CairoScript
+/pot { % n -- n
+  1 sub
+  dup  -1 bitshift or
+  dup  -2 bitshift or
+  dup  -4 bitshift or
+  dup  -8 bitshift or
+  dup -16 bitshift or
+  1 add
+} bind def
+
+/direction { % i -- bool
+  dup 2 lt { pop true } {
+      dup 1 add dup pot dup 3 -1 roll eq { pop pop true } {
+         2 div 1 sub 2 mul exch sub direction not
+      } ifelse
+  } ifelse
+} bind def
+
+/path { % cr dx dy -- cr
+  0 1 2048 {
+    4 1 roll 3 copy L pop 4 -1 roll direction {
+      exch neg
+    } {
+      neg exch
+    } ifelse
+  } for
+  pop pop
+} bind def
+
+dict
+  /width  512 set
+  /height 512 set
+  surface context
+
+1 g set-source paint
+
+//LINE_CAP_ROUND set-line-cap
+//LINE_JOIN_ROUND set-line-join
+4 set-line-width
+
+256 256 m  12 0 path 0 0 0 rgb set-source stroke
+256 256 m -12 0 path 1 0 0 rgb set-source stroke
+256 256 m 0  12 path 0 1 0 rgb set-source stroke
+256 256 m 0 -12 path 0 0 1 rgb set-source stroke
+
+/target get (out.png) write-to-png pop
+pop
diff --git a/util/cairo-script/examples/hilbert.cs b/util/cairo-script/examples/hilbert.cs
new file mode 100644 (file)
index 0000000..4278bf7
--- /dev/null
@@ -0,0 +1,51 @@
+%!CairoScript
+
+/hA { % cr dist lvl *hA* cr dist
+  dup not { pop } {
+      1 sub
+      3 copy hB 0 exch L pop
+      3 copy hA 0 L pop
+      3 copy hA neg 0 exch L pop
+      hC
+  } ifelse
+} bind def
+
+/hB { % cr dist lvl *hB* cr dist
+  dup not { pop } {
+      1 sub
+      3 copy hA 0 L pop
+      3 copy hB 0 exch L pop
+      3 copy hB neg 0 L pop
+      hD
+  } ifelse
+} bind def
+
+/hC { % cr dist lvl *hC* cr dist
+  dup not { pop } {
+      1 sub
+      3 copy hD neg 0 L pop
+      3 copy hC neg 0 exch L pop
+      3 copy hC 0 L pop
+      hA
+  } ifelse
+} bind def
+
+/hD { % cr dist lvl *hD* cr dist
+  dup not { pop } {
+      1 sub
+      3 copy hC neg 0 exch L pop
+      3 copy hD neg 0 L pop
+      3 copy hD 0 exch L pop
+      hB
+  } ifelse
+} bind def
+
+dict
+  /width  1024 set
+  /height 1024 set
+  surface context
+
+2 2 m 4 10 hA pop 1 g set-source stroke
+
+/target get (out.png) write-to-png pop
+pop
diff --git a/util/cairo-script/examples/infinichess.cs b/util/cairo-script/examples/infinichess.cs
new file mode 100644 (file)
index 0000000..f82b102
--- /dev/null
@@ -0,0 +1,29 @@
+/xdef { exch def } def
+/-rot { 3 1 roll } def
+/rot { 3 -1 roll } def
+/2dup { 2 copy } def
+
+/SIZE 600. def
+<< /width SIZE /height SIZE >> surface context
+1 1 1 set-source-rgb paint
+0 0 0 set-source-rgb
+EVEN_ODD set-fill-rule
+SIZE SIZE scale
+
+0.5 0 translate
+
+save
+1 1 scale
+/n 90 def
+n neg 1 n {
+       /x xdef
+       0 0 m
+       x 1 l
+       x 0.5 add 1 l
+       0 0 l
+} for
+restore
+fill
+
+/target get (out.png) write-to-png pop
+pop
diff --git a/util/cairo-script/examples/interference.cs b/util/cairo-script/examples/interference.cs
new file mode 100644 (file)
index 0000000..6d2ee22
--- /dev/null
@@ -0,0 +1,46 @@
+/rot { 3 -1 roll } def
+/2dup { 2 copy } def
+
+/circle { % context radius -- context
+       2dup 0 m pop
+       0 0 rot 0 math.2pi arc h
+} def
+
+/circles { % context #circles -- context
+       1 1 rot { circle } for
+} def
+
+/SIDE 600. def
+/SIZE 600. def
+<< /width SIZE /height SIZE >> surface context
+1 1 1 set-source-rgb paint
+0 0 0 set-source-rgb
+//EVEN_ODD set-fill-rule
+
+/r 4 def
+/n SIDE 1.5 mul r div integer def
+
+SIZE SIDE div dup scale
+
+save
+  302 200 translate
+  r dup scale
+  n circles
+restore
+
+save
+  100 300 translate
+  r dup scale
+  n circles
+restore
+
+save
+  500 400 translate
+  r dup scale
+  n circles
+restore
+
+fill
+
+/target get (out.png) write-to-png pop
+pop
diff --git a/util/cairo-script/examples/pythagoras-tree.cs b/util/cairo-script/examples/pythagoras-tree.cs
new file mode 100644 (file)
index 0000000..96b4b39
--- /dev/null
@@ -0,0 +1,53 @@
+%!CairoScript
+
+/pi4 0.785398163 def
+/sqrt2 1.414213562 def
+
+/R { % cr size *R* cr
+  dup 1 lt { pop } {
+      exch /current-point get % size cr x y
+      4 2 roll % x y size cr
+
+      % draw a rectangle in relative co-ordinates
+      1 index 2 div neg dup exch M
+      1 index 0 L
+      0 2 index L
+      1 index neg 0 L
+      h
+
+      save
+      1 index dup 2 div neg exch translate
+      4 2 roll 3 copy % size cr x y cr x y
+      m
+      //pi4 rotate
+      pop 4 2 roll % x y size cr
+      1 index //sqrt2 div
+      R
+      restore
+
+      save
+      1 index dup 2 div exch translate
+      4 2 roll 3 copy % size cr x y cr x y
+      m
+      //pi4 neg rotate
+      pop 4 2 roll % x y size cr
+      1 index //sqrt2 div
+      R
+      restore
+
+      4 1 roll pop pop pop
+  } ifelse
+} bind def
+
+dict
+  /width  1024 set
+  /height 512 set
+  surface context
+
+1 1 1 set-source-rgb paint
+
+0 512 translate 1 -1 scale
+512 64 m 128. R 0 0 0 set-source-rgb fill
+
+/target get (out.png) write-to-png pop
+pop
diff --git a/util/cairo-script/examples/sierpinski.cs b/util/cairo-script/examples/sierpinski.cs
new file mode 100644 (file)
index 0000000..6f95908
--- /dev/null
@@ -0,0 +1,37 @@
+%!CairoScript
+
+/1sqrt3 0.577359269 def
+
+/T { % cr size -- cr
+  exch % size cr
+  0 0 m 1 index 0 l 1 index dup 2 div exch //1sqrt3 mul l h
+
+  exch 2 div
+  dup 4 ge {
+    exch % size/2 cr
+
+    1 index T
+    save 1 index 0 translate 1 index T restore
+    save 1 index dup 2 div exch //1sqrt3 mul translate 1 index T restore
+
+    exch
+  } if
+  pop
+} bind def
+
+dict
+  /width  512 set
+  /height 300 set
+  surface context
+
+1 1 1 set-source-rgb paint
+
+.5 set-line-width
+
+0 300 translate
+1 -1 scale
+
+512 T 0 0 0 set-source-rgb stroke
+
+/target get (out.png) write-to-png pop
+pop
diff --git a/util/cairo-script/examples/wedgeAnnulus_crop_ybRings.cs b/util/cairo-script/examples/wedgeAnnulus_crop_ybRings.cs
new file mode 100644 (file)
index 0000000..5aeb97f
--- /dev/null
@@ -0,0 +1,30 @@
+/SIDE 600 def
+
+<< /width SIDE /height SIDE >> surface context
+
+1 1 1 rgb set-source
+paint
+
+SIDE dup scale
+0.5 0.5 translate
+0.5 -0.5 scale
+
+/theta math.pi 10 div def
+
+10 {
+  N
+  0 0 1.0 0 //theta arc
+  0 0 0.2 //theta 0 arc-
+  h
+  //theta rotate
+  //theta rotate
+} repeat
+
+0 0 0.2 0 0 1 radial
+  0.0 1 1 0 1 add-color-stop
+  1.0 0 0 1 1 add-color-stop
+set-source
+fill
+
+/target get (out.png) write-to-png pop
+pop
diff --git a/util/cairo-script/examples/world-map.cs b/util/cairo-script/examples/world-map.cs
new file mode 100644 (file)
index 0000000..54d9a8f
--- /dev/null
@@ -0,0 +1,209 @@
+%!CairoScript
+% The data for this test case was provided by Gary Nicholson
+% <gary@imapping.co.nz> who originally created an interactive SVG map
+% of the world as can be seen here:
+%
+%      http://www.wherearewe.co.nz/svg.html
+%
+% The data is used here by permission from Gary given on 2006-11-08:
+%
+%      Thanks for asking, I don't need any attribution if you are
+%      only using the vectors and not the entire map with
+%      interactivity etc. So feel free to do what you wish with
+%      the data.
+
+/N {
+  2 index
+  .75 .75 .75 set-source-rgb fill+
+  .5 .5 .5 set-source-rgb stroke
+  pop
+  m
+} bind def
+
+% simpler if these had been expressed in relative coords...
+/H {
+    exch /current-point get exch pop % x cr y
+    3 -1 roll exch l
+} bind def
+
+/V {
+    exch /current-point get pop % y cr x
+    3 -1 roll l
+} bind def
+
+dict
+  /width  800 set
+  /height 400 set
+  surface context
+
+.68 .85 .90 set-source-rgb paint
+.2 set-line-width
+
+413.519 90.071 N .136 -.348 L -.31 -.204 L -.017 -.327 L .213 -.322 L .245 -.147 L .142 -.08 L .225 .072 L .062 .301 L .41 .312 L .466 .096 L -.044 .288 L -.248 .144 L .074 .353 L -.145 -.063 L -.568 -.011 L -.642 -.063 L
+421.683 94.397 N .193 .336 L -.266 .274 L .214 .288 L -.09 .192 L .622 .062 L .008 .144 L .55 .242 L .579 -.332 L .215 .117 L -.029 .171 L -.126 .309 L .112 .212 L -.038 .192 L -.315 -.051 L -.176 -.162 L -.283 .091 L -.081 .273 L .244 .131 L -.228 .415 L -.244 -.333 L -.469 .05 L -.071 .122 L -.216 .03 L -.23 -.142 L -.143 -.354 L -.371 .081 L .019 .333 L -.425 .384 L -.018 .535 L -.285 .151 L -.385 -.312 L .098 -.182 L -.311 -.071 L -.534 -.363 L -.016 -.415 L -.777 .404 L .103 .212 L -.349 .432 L -.275 .16 L -.629 -.168 L -.627 .204 L -.599 -.062 L -.102 -.424 L -.312 -.806 L -.616 .147 L -.854 .668 L -.369 -.111 L .238 -.226 L .013 -.322 L -.08 -.137 L .089 -.294 L .718 -.418 L -.038 -.315 L .575 -.24 L .012 -.076 L .528 -.494 L .173 -.035 L -.116 -.089 L -.153 -.028 L .221 -.302 L .446 .007 L -.005 .096 L .473 .007 L .385 -.309 L .271 .089 L .272 -.117 L .271 .096 L .567 -.158 L .278 .11 L .354 -.021 L -.179 -.199 L .709 -.199 L .017 .151 L .199 -.014 L .149 .089 L .852 .007 L .664 .261 L
+421.394 104.558 N .104 .175 L .04 .256 L -.06 .475 L .118 .054 L .062 .333 L -.076 .795 L -.211 .327 L -.118 .724 L -.292 .501 L -.298 -.043 L -.057 -.196 L -.41 -.076 L -.227 -.152 L .284 -.207 L -.07 -.076 L -.437 -.098 L .257 -.332 L -.11 -.071 L -.291 .071 L -.053 -.147 L .115 -.022 L .175 -.158 L -.094 -.153 L -.257 -.082 L .015 -.164 L .247 -.12 L -.284 -.218 L .241 -.284 L .6 -.305 L .27 -.022 L .04 -.125 L .292 -.043 L .195 .104 L .096 -.142 L -.022 -.344 L .072 -.224 L .143 -.011 L h 396.323 103.853 m .375 -.122 L .411 -.365 L .549 -2.299 L .397 -.091 L -.21 -.29 L -.226 .259 L .125 -1.144 L .223 -.826 L .115 .153 L .496 .306 L .191 .382 L .191 .229 L -.281 -.673 L -.63 -.582 L -.242 -.233 L .024 -.249 L .359 -.477 L -.202 -.375 L -.2 -.274 L -.326 -.216 L -.685 -.1 L -.515 -.571 L -.416 -.323 L .278 -.426 L -.233 -.181 L -.343 -.131 L -.511 -.142 L -.184 -.173 L .247 -.376 L -.329 -.173 L -.509 .179 L -.489 -.249 L -.824 -.251 L -.619 -.181 L -.325 .014 L -.215 -.25 L -.91 .167 L -.059 -.25 L -.265 -.125 L -.367 -.042 L -.056 -.104 L .861 -.083 L -.085 -.229 L -.526 -.104 L .442 -.104 L .086 -.205 L -.275 .017 L -.263 -.021 L -.417 .083 L .04 -.438 L .303 .012 L .305 -.146 L .526 -.088 L .562 -.174 L .215 .188 L .18 -.167 L .474 .063 L .112 -.26 L .272 -.059 L .764 -.078 L .393 .366 L .275 .26 L .342 .083 L .652 -.271 L .317 .167 L .276 -.127 L .457 -.124 L .029 .23 L .591 -.065 L .3 -.103 L -.265 -.188 L -.028 -.251 L .056 -.345 L -.037 -.428 L -.131 .021 L -.562 -.682 L -.11 -.407 L .927 .126 L .607 -.105 L -.084 .397 L .248 .419 L .342 -.146 L 1.241 .104 L .501 .146 L .079 -.014 L .525 -.093 L .662 -.27 L -.534 -.124 L .055 -.204 L .166 -.175 L .753 -.322 L .756 -.181 L .902 -.215 L .314 -.235 L .302 -.264 L -.053 -.775 L .135 -.542 L .521 -.25 L .46 -.16 L .916 -.092 L .177 -.096 L .208 .447 L .311 .335 L .266 .127 L .141 -.071 L .41 -.208 L .153 .17 L .202 .458 L .194 .133 L .518 -.012 L .159 .301 L .259 -.012 L .576 .048 L .375 .168 L -.159 .241 L .091 .175 L -.072 .198 L .285 .122 L .406 -.075 L .446 -.035 L .193 -.313 L .245 -.072 L -.119 .373 L .146 .18 L -.039 .228 L .529 .048 L .341 .192 L .371 .204 L .127 .228 L .694 -.174 L .079 .114 L .642 .063 L .568 .011 L .145 .063 L .428 .319 L .337 .277 L .395 -.055 L .045 .145 L .689 -.062 L .072 -.048 L .233 .007 L .095 .186 L .456 .09 L .479 -.014 L .605 .193 L -.954 .806 L -.054 .213 L -.054 .358 L -.321 .372 L -.075 .295 L .091 .076 L -.216 .701 L .135 .233 L -.385 .309 L -.473 -.007 L .005 -.096 L 415.96 94.5 l -.221 .302 L .153 .028 L .116 .089 L -.173 .035 L -.528 .494 L -.012 .076 L -.575 .24 L .038 .315 L -.718 .418 L -.089 .294 L .08 .137 L -.013 .322 L -.238 .226 L .369 .111 L .854 -.668 L .616 -.147 L .312 .806 L .102 .424 L -.624 .301 L .532 .344 L .025 .292 L .43 .192 L -.199 .272 L -.541 .353 L -.183 -.111 L -.437 .186 L .352 .358 L .616 .191 L .135 .331 L -.175 .01 L -.315 .371 L .193 .442 L .754 .391 L .849 -.07 L .062 .281 L -.146 .469 L -.346 .23 L -.221 .215 L -.833 .488 L -.889 .659 L -.427 .087 L -.318 .043 L -.798 .159 L -.405 -.028 L -.471 -.156 L -.851 -.499 L -.315 -.085 L -.354 .029 L -.231 .072 L -.511 -.056 L -.752 -.313 L -.602 .044 L -.731 .345 L -.357 .258 L -.555 .559 L -.147 .386 L .099 .514 L .091 .379 L -.334 -.091 L -.75 .137 L -.039 .136 L -.485 -.015 L -.427 -.197 L -.395 .167 L -.261 -.015 L -.036 -.152 L -.335 -.091 L -.206 .03 L -.374 .076 L -.187 -.076 L -.035 -.289 L -.091 -.213 L -1.252 -.304 L -.355 0 L .017 .319 L -.542 -.015 L -.337 .061 L -.037 -.122 L -.767 .03 L -.084 -.114 L -.028 -.038 L -.431 -.152 L -.131 .076 L -.262 -.03 L -.056 .076 L -.507 -.395 L -.15 .061 L -1.088 -.334 L -.112 .106 L -.15 -.03 L -.094 -.106 L .205 -.243 L -.058 -.122 L -.469 .03 L -.472 -.243 L
+681.312 116.395 N .235 -.171 L .283 -.256 L .633 -.738 L .315 -.157 L .595 .011 L .579 .068 L .511 .096 L .309 -.115 L .571 -.678 L .682 .621 L 1.178 1.611 L .329 .495 L .269 .664 L .002 .75 L -.034 .947 L -.129 .637 L .143 .113 L .5 -.043 L -.121 .41 L -.282 .523 L -.5 .75 L -.316 .312 L -.243 .043 L -.567 -.211 L -.256 .1 L -.607 .58 L -.431 -.083 L -.289 -.225 L -.544 .1 L -.526 .199 L -1.188 .835 L -.462 .043 L -.46 .312 L -.055 -.564 L -.056 -.324 L -.163 -.705 L -.137 -.395 L .167 -.453 L .499 -.468 L 0 -.353 L .226 -.425 L -.044 -.141 L -.378 -.311 L -.095 -.296 L .015 -.467 L -.087 -.339 L -.289 -.126 L -.603 -.084 L .654 -.411 L .303 -.114 L .654 .268 L .254 -.241 L -.029 -.283 L -.764 -.89 L -.113 -.311 L -.137 -.105 L
+475.646 121.847 N -.018 .175 L .338 .391 L -.295 -.009 L -.132 .108 L -.104 -.059 L -.327 -.021 L -.121 .33 L -.783 .257 L -.384 .046 L -.099 .053 L 0 .21 L -.217 .006 L -.072 -.192 L -.402 .023 L -.547 -.146 L -.191 -.087 L 0 -.21 L -.161 -.105 L -.122 -.403 L .082 -.035 L .12 .1 L .147 -.006 L .405 -.304 L .253 -.006 L .328 .092 L .077 -.086 L .088 -.286 L -.053 -.175 L .627 .093 L .658 .027 L .367 -.056 L .818 -.233 L .689 -.304 L .535 -.158 L -.475 .295 L -.436 .231 L -.596 .444 L
+704.404 117.274 N .197 -.099 L 1.108 -.271 L .057 .354 L -.481 .284 L -.232 .241 L -.068 .453 L .139 .367 L .291 .056 L .221 -.114 L .418 -.354 L .24 -.085 L 1.656 -.697 L .389 -.213 L .46 -.326 L .349 -.638 L .76 -.412 L .347 -.327 L .191 -.269 L .142 -.51 L .538 -.582 L -.01 -.142 L .344 -.567 L .159 -.468 L .139 -.609 L -.043 -.467 L -.33 -.198 L -.128 -.24 L .234 -.213 L .166 -.284 L -.155 -1.023 L .544 -.343 L .176 -.242 L .327 -.328 L .192 0 L .21 .355 L .199 .227 L .303 -.058 L .799 -.257 L -.169 -.526 L -.311 -.028 L -.36 -.312 L .694 -.415 L .441 .156 L .336 .227 L .025 .199 L -.016 .868 L .058 .611 L .22 .127 L .243 .312 L .717 1.432 L .001 .496 L -.246 .709 L -.709 .766 L -.226 .439 L .064 .368 L -.15 .071 L -.737 .285 L -.161 .113 L -.164 .199 L -.174 .453 L .02 .396 L .094 .254 L .131 .792 L -.04 .693 L -.686 .751 L -.242 .736 L .02 .707 L .198 .296 L .422 .353 L -.617 .298 L -.193 .127 L -.166 .17 L -.174 .834 L -1.081 .439 L -.094 -.282 L .294 -.665 L .184 -.523 L -.198 -.126 L -.514 .241 L -.578 .623 L -.476 .001 L -.346 .312 L -.066 .748 L -.354 .269 L -.188 -.028 L -.066 -.155 L .003 -.606 L -.149 -.155 L -.211 .042 L -.309 .156 L -.344 .311 L -.325 .523 L -.866 -.055 L -.505 .057 L -.631 .1 L -.458 -.549 L -.685 -.323 L -.26 .254 L -.067 .184 L -.177 .353 L .037 .056 L .417 .197 L .416 .323 L -.293 .198 L -.829 .129 L -.433 .241 L -.463 .622 L -.522 .847 L -.688 -.365 L -.565 -.21 L -.285 -.197 L -.014 -.169 L -.194 -.818 L .099 -.155 L .495 -.325 L .179 -.269 L -.067 -.282 L -.18 -.042 L -.601 .17 L -.341 -.028 L -.789 -.167 L -.475 .128 L -.427 .227 L -.437 .184 L -.269 -.098 L -.256 -.027 L -1.647 .398 L -.814 .298 L -.21 -.31 L -.452 -.042 L -.413 .438 L -.006 .635 L -.756 -.238 L -.579 -.055 L -1.1 .073 L -.267 -.14 L .072 -.339 L .179 -.283 L .483 .013 L .499 -.114 L .751 -.467 L 2.201 -1.953 L .28 -.015 L .427 -.128 L .056 .424 L .495 -.128 L 1.278 -.257 L .933 -.058 L 1.183 -.172 L .892 -.256 L .068 .452 L .377 .268 L .167 -.085 L .654 -.199 L .446 -.34 L -.003 -.353 L .114 -.467 L .465 -.51 L .698 -.581 L .371 -.453 L -.062 -1.117 L .182 -.213 L h 695.464 127.756 m -.292 -.197 L -.223 -.268 L -.101 -.381 L -.177 -.395 L -.492 -.535 L .731 -.382 L .287 -.269 L .456 -.593 L .409 .253 L .615 -.015 L .483 -.185 L .311 -.339 L .451 -.311 L .454 -.029 L .316 .169 L .862 .224 L .153 .254 L -.1 .127 L -.102 .423 L -.292 .24 L -.864 .876 L -.181 -.211 L -.424 -.295 L -.467 -.042 L -.612 .213 L -.193 .184 L -.245 .495 L -.165 .508 L -.153 .212 L -.448 .269 L h 691.12 131.448 m -.366 -.042 L -.056 -.141 L .268 -.537 L .128 -.593 L -.334 -.112 L -.239 .198 L -.155 .466 L -.381 .452 L -.326 -.211 L -.059 -.211 L .322 -.466 L .032 -.296 L -.356 -.917 L .169 -.113 L .687 -.58 L .083 -.141 L .034 -.466 L -.532 -.789 L -.333 -.042 L -.162 .269 L -.419 .495 L -.249 -.112 L -.23 -.508 L -.376 -.267 L -.261 -.366 L .41 -.325 L .733 .083 L .706 -.171 L .315 -.466 L .241 -.283 L .484 -.058 L .478 .056 L .249 .38 L .27 .168 L .43 .084 L .628 -.213 L .225 .395 L -.569 .438 L .405 .239 L .443 .437 L .079 .254 L -.596 .58 L -.242 .41 L -.104 .367 L -.085 .621 L -.109 .649 L -.242 .353 L -.194 .099 L -.165 .071 L -.197 .184 L -.479 .678 L h 711.938 108.369 m -.222 -.241 L -.077 -.271 L .325 -.642 L -.055 -.342 L -.549 -.198 L -.168 -.171 L -.146 -.812 L .583 -.386 L .522 -.172 L .646 -.373 L .037 -.356 L -.318 -.285 L .277 -.3 L .224 -.015 L .661 .427 L .373 .085 L .532 -.201 L -.004 -1.186 L .455 -.187 L .45 -.244 L .074 -.743 L .007 -.844 L -.398 -.758 L -.098 -.473 L .166 -.216 L .618 -.346 L .063 .072 L .507 .43 L .904 .816 L 1.07 .842 L 1.083 .684 L .627 .285 L .528 .17 L 1.02 .198 L .282 .042 L .304 -.086 L .866 -.66 L .461 -.144 L .002 .1 L -.308 .358 L -.335 .558 L .198 .414 L .469 .599 L .197 .356 L -.561 .272 L -.447 .244 L -.534 .158 L -.365 .015 L -.488 -.199 L -.453 .015 L -.363 .144 L -.345 .229 L -.754 .786 L -.396 .5 L -.26 .599 L -.4 -.07 L -.425 -.241 L -2.031 -.965 L -.461 -.085 L -.72 .044 L -1.038 .587 L -.153 -.299 L -.372 -.356 L -.339 .029 L -.266 .115 L -.439 .272 L .049 .299 L 1.16 .497 L .56 .298 L .302 .27 L -.391 .214 L -.303 .029 L -.305 -.128 L -.261 .043 L -.324 .314 L -.388 .471 L -.347 .114 L
+597.917 139.685 N 1.251 -1.545 L .609 -.539 L .348 -.239 L .149 -.103 L .417 -.016 L .309 .294 L .479 .208 L 1.659 .047 L .371 .041 L .312 .209 L .329 .619 L -.07 .156 L .042 .24 L .326 .294 L .313 .069 L .258 .238 L .017 .282 L -.217 .58 L -.624 .06 L -1.036 .062 L -1.238 -.063 L -.335 -.125 L -.301 -.055 L -.531 .313 L -.544 .074 L -.085 -.021 L -.869 -.214 L -.559 -.081 L -.637 -.18 L -.235 -.493 L .092 -.113 L
+362.764 152.723 N .072 -.625 L .395 -.876 L .52 -.552 L .488 -.566 L .244 -.509 L 1.175 -2.559 L .238 -.241 L 1.404 -1.175 L .345 -.495 L .051 -.918 L .305 -1.088 L .651 -1.075 L .399 -.34 L .404 -.198 L .838 -.51 L .361 -.495 L .334 -.777 L .428 -.851 L 1.635 -.04 L 2.511 0 L 2.677 -.001 L 1.718 .004 L 1.42 -.008 L .027 .876 L -.03 1.752 L .002 .65 L -.104 .396 L -.56 -.011 L -6.005 -.022 L -.557 .074 L -.047 .509 L -.07 2.261 L -.099 2.6 L -.144 .128 L -.809 .287 L -.726 .315 L -.575 .427 L -.249 .383 L -.01 .707 L .164 1.539 L .051 1.102 L -.212 -.027 L -.732 .033 L -2.396 -.014 L -5.055 -.056 L -.474 -.013 L
+514.551 145.841 N -.374 .027 L -.336 -.083 L -.008 -.615 L -.153 -.437 L -.108 -.791 L .187 -.607 L .188 -.11 L -.059 -.187 L .177 -.607 L .33 -.269 L .312 .083 L .069 .315 L .26 .093 L .063 .199 L .116 .326 L -.106 .42 L .031 .708 L .118 .254 L -.104 .381 L -.327 .467 L -.275 .433 L
+514.177 145.868 N .374 -.027 L .008 .288 L .361 .14 L .153 .128 L .186 -.093 L -.046 .443 L .397 .001 L .402 .127 L .687 -.093 L .103 -.21 L .183 -.058 L .218 .117 L .424 -.042 L .595 .112 L .224 -.035 L .079 -.105 L 1.358 .222 L .732 -.14 L -.022 -.292 L .225 .175 L .375 -.016 L .157 -.099 L .312 -.422 L .232 -.073 L .267 -.495 L .131 -.297 L .711 -.637 L .813 -.889 L .163 .105 L .229 -.178 L .85 -.708 L .313 -.433 L .15 .161 L -.248 .42 L -.107 .299 L -.004 .176 L .099 .064 L .121 -.024 L .454 .042 L .09 .324 L .001 .508 L -.003 .358 L -.49 .034 L -.401 -.083 L -.107 .396 L .073 1.326 L -.199 .34 L -.536 .596 L .003 .946 L .024 2.075 L .063 .183 L -.152 .057 L -.584 .469 L -.839 -.108 L -3.387 -.446 L -3.362 -.375 L -.261 -.902 L -.548 -1.154 L -1.043 -2.198 L
+668.627 151.365 N -.102 -.056 L -.107 -.325 L -.922 -1.212 L -.332 -.987 L -.03 -.438 L .156 -.749 L .546 -.792 L 1.312 -1.852 L .259 -.184 L .425 -.128 L .229 -.184 L .358 -.227 L .228 .127 L .554 .394 L -.334 .424 L -.084 .142 L .023 .31 L -.067 .622 L -.203 .296 L -.182 .354 L -.065 .692 L -.1 .494 L -.317 .805 L -.473 .707 L -.417 .833 L -.014 .353 L -.114 .438 L -.228 .142 L
+389.765 144.543 N .1 .084 L .895 .531 L 2.054 1.344 L .811 .575 L 3.283 2.241 L 1.924 1.26 L 1.292 .824 L .397 .253 L 2.472 1.469 L .181 .253 L -.096 .396 L .082 .183 L .393 .28 L 1.111 1.039 L .229 .027 L .47 -.314 L .588 .562 L .375 .167 L .748 .024 L .309 .111 L .277 .352 L .099 .522 L -.161 .679 L .146 .564 L 2.176 -.408 L .064 1.017 L .034 2.203 L .001 .96 L -.08 .89 L -.145 .919 L -.434 1.246 L -.596 .794 L -.339 .271 L -.29 .129 L -2.533 .085 L -1.808 .124 L -.209 .072 L -.562 .427 L -.579 .272 L -.678 -.053 L -.581 -.081 L -1.062 -.173 L -.36 -.059 L -.356 -.125 L -.37 .073 L -1.22 .713 L -.947 .458 L -.304 .228 L -.314 .793 L -.274 -.027 L -.324 -.182 L -.518 -.209 L -.272 .101 L -.638 .625 L -.492 .667 L -.393 .822 L -.174 .227 L -.45 .102 L -.551 -.364 L -.293 -.281 L -.273 .058 L -.397 .384 L -.355 1.217 L -.292 1.047 L -.317 .369 L -.543 .271 L -.448 .158 L -.257 .016 L -.141 .255 L .058 .749 L -.133 .876 L -.261 .92 L -.172 .326 L -.046 .156 L -.08 .043 L -.159 .1 L -.604 .399 L -.352 .059 L -.148 -.239 L -.117 -.381 L -.004 -.297 L -.147 -.211 L -.257 -.041 L -.239 .114 L -.571 .483 L -.362 .469 L -.35 .228 L -.455 -.436 L -.566 -.321 L -.352 .059 L -.522 .54 L -.559 -.901 L -.194 -1.143 L -.349 -.718 L -.474 -.478 L -.265 -.451 L -.271 -.832 L -.022 -.339 L -.246 -.281 L -.323 -.055 L -.684 .428 L -.3 .327 L -.43 .243 L -.565 -.152 L -.356 -.153 L -.338 -.026 L -.475 .413 L -.252 .256 L -.536 -.265 L -.882 -.715 L -.18 -.183 L -.113 -.028 L .062 -.142 L .004 -.565 L -.082 -.833 L -.265 -.337 L -.554 -.322 L -.181 -.197 L -.22 -.479 L -.144 -.663 L -.251 -1.1 L .057 -.339 L .506 -.399 L .332 -.284 L .018 -.607 L .181 -.552 L .252 -.256 L .402 -.073 L .261 .111 L .568 .83 L .214 .168 L .454 .082 L .107 -.269 L -.055 -.296 L .06 -.212 L .535 .124 L .713 .137 L .485 .054 L .387 -.031 L .945 -.344 L 1.953 -.026 L 6.457 -.01 L .379 -1.613 L -.724 -.787 L -.186 -1.468 L -.202 -2.386 L -.325 -2.753 L -.178 -1.736 L -.19 -1.468 L -.908 -7.962 L -.049 -.776 L 3.231 -.089 L .523 -.13 L
+525.37 142.384 N .312 -.429 L .155 -.17 L .084 .833 L -.423 .707 L -.118 .156 L -.121 .024 L -.099 -.064 L .004 -.176 L .107 -.299 L .248 -.42 L -.15 -.161 L h 525.923 144.712 m 0 .22 L .456 .762 L .408 .465 L .782 .634 L .677 .394 L 1.008 .52 L .392 .154 L .277 .014 L .576 -.029 L .364 .112 L .873 .973 L .518 .648 L .46 .422 L .81 .365 L .025 .212 L -.67 1.06 L -.615 .721 L -.883 .807 L -.776 1.541 L -.242 .142 L -.562 -.083 L -.235 -.084 L -.252 .071 L -.278 .509 L -.062 1.115 L .001 .791 L .134 .621 L -.403 .142 L -1.046 .073 L -.627 .27 L -.367 .283 L -.29 .495 L -.131 .551 L -.204 .283 L -.444 .255 L -.544 .1 L -.292 0 L -.386 -.042 L -.326 .029 L -.382 .283 L -.22 .297 L -.125 .508 L .003 .353 L -.091 .311 L -.631 .396 L -.344 .043 L -.776 -.21 L -.717 .058 L -.896 .27 L -.768 .298 L -.283 .099 L -.416 .145 L -.241 -.306 L -.483 -.689 L .006 -.296 L -.127 -.253 L -.933 -1.364 L -.604 -.971 L -.226 -.634 L -.092 -.663 L 1.691 -.815 L 2.35 -1.213 L 5.346 -2.982 L -.155 -1.453 L -.581 -.914 L -.063 -.183 L -.024 -2.075 L -.003 -.946 L .536 -.596 L .199 -.34 L -.073 -1.326 L .107 -.396 L .401 .083 L .49 -.034 L
+405.733 173.04 N -.568 -.971 L -.562 -.025 L -.37 .044 L -.516 -.181 L -.97 -.757 L -.114 -.226 L .335 -.439 L -.018 -.212 L -.179 -.268 L -.502 -.42 L -.389 -.266 L -.422 -.492 L -.426 -.93 L -.019 -.396 L .173 -.665 L .581 .081 L .678 .053 L .579 -.272 L .562 -.427 L .209 -.072 L 1.808 -.124 L 2.533 -.085 L .29 -.129 L .339 -.271 L .596 -.794 L .434 -1.246 L .145 -.919 L .08 -.89 L -.001 -.96 L -.034 -2.203 L -.064 -1.017 L 1.672 -.321 L 1.82 -.364 L 3.084 -2.617 L .834 -.655 L 2.308 -1.454 L 1.607 -.956 L 4.012 -2.241 L 1.632 -.843 L .265 -.186 L .832 .137 L 1.646 .442 L 1.008 .333 L .258 .182 L 1.192 .911 L .231 -.157 L 1.519 -.729 L .364 2.145 L .169 1.298 L .42 1.028 L .554 .802 L .703 .604 L -.388 .722 L -.265 .99 L -.168 1.088 L -.084 .989 L .022 .537 L -.062 .707 L -.019 1.045 L -.034 1.088 L -.056 .466 L -2.43 2.613 L -.591 .78 L -.87 1.333 L -.572 .794 L -.007 .678 L .123 .719 L .014 .269 L -.951 .034 L -.437 .2 L -.453 .299 L -.761 .697 L -.259 .058 L -.609 -.208 L -.724 -.193 L -.884 -.221 L -.531 -.04 L -.709 .047 L -.628 .103 L -.774 .287 L -.403 .327 L -.629 .399 L -.273 .059 L -.934 .005 L -.965 -.277 L -1.173 -.742 L -.354 -.083 L -.467 .116 L -1.337 .544 L -.37 .002 L -.209 -.098 L -1.095 -1.223 L -.821 -.277 L -1.111 -.121 L -1.174 .108 L -1.064 .188 L -.676 .4 L -.687 1.614 L -.353 .482 L -.158 .849 L -.092 .961 L -.902 -.503 L -.727 -.589 L -.339 -.28 L -.321 .073 L -.577 .3 L
+431.763 171.063 N -.351 -.407 L -.575 -.52 L -.173 -.394 L -.014 -.269 L -.123 -.719 L .007 -.678 L .572 -.794 L .87 -1.333 L .591 -.78 L 2.43 -2.613 L .056 -.466 L .034 -1.088 L .019 -1.045 L .062 -.707 L -.022 -.537 L .084 -.989 L .168 -1.088 L .265 -.99 L .388 -.722 L -.703 -.604 L -.554 -.802 L -.42 -1.028 L -.169 -1.298 L -.364 -2.145 L 1.818 -.858 L .41 -.059 L 5.231 2.554 L 4.941 2.372 L 5.577 2.792 L 1.981 .963 L -.02 1.045 L -.016 .946 L -.036 .636 L .085 2.5 L -.038 .749 L .036 1.002 L .031 1.229 L -.04 .283 L -.839 -.009 L -1.245 .05 L -.229 .143 L -.417 1.245 L -.583 .809 L -.122 .438 L .131 .677 L -.149 .212 L -.718 .428 L -.053 .24 L .342 .662 L -.087 .34 L -.542 .596 L -.316 .609 L .219 .352 L .517 -.088 L .338 .012 L .141 .225 L .221 1.228 L .137 .522 L .155 .295 L .444 .407 L .266 .465 L .026 .367 L -.15 .425 L -.559 -.208 L -.321 -.012 L -.322 .086 L -.939 .613 L -.372 .228 L -.165 .382 L -.005 .41 L -.196 .284 L -2.649 2.275 L -.386 .087 L -2.181 .055 L -.434 .059 L -.209 .199 L -.117 .806 L -.646 1.176 L -.258 .143 L -.368 .031 L -.881 -.009 L -.818 .273 L -.754 .386 L -.466 .271 L -.224 .03 L -.225 -.069 L -.494 -.661 L -1.363 .686 L -.449 .158 L -.24 -.027 L -.096 -.084 L -.208 -.183 L -.382 -1.057 L -.638 -1.07 L -1.343 -1.179 L -1.088 -1.067 L .323 -.539 L .29 -.312 L .24 -.1 L .481 .082 L 1.187 .191 L .674 -.032 L .225 -.143 L -.047 -.127 L -.208 -.21 L -.381 -.633 L -.205 -.578 L -.169 -1.228 L .134 -.651 L -.119 -1.2 L -.395 -.887 L -.923 -1.238 L -.208 -.083 L -.627 -.109 L
+627.173 150.012 N .483 -.229 L .515 -.13 L .341 .012 L .597 .392 L .325 .097 L .584 -.413 L .332 -.115 L 1.595 -.052 L .807 -.117 L .341 -.157 L .696 -.554 L .521 -.328 L .298 -.101 L .623 .575 L .771 .235 L .66 .053 L .777 -.047 L .237 .21 L .056 .38 L -.472 .75 L .096 .521 L .273 .365 L .943 .615 L .621 .166 L .909 .107 L .197 .143 L -.19 .132 L -.826 .482 L .106 .465 L -.203 .212 L -1.261 -.054 L -.136 .198 L .057 .395 L -.283 .382 L -.585 .792 L -.221 .142 L -.533 .241 L -.171 .127 L -.27 .396 L -.303 .932 L -.388 .975 L .268 .225 L .469 .563 L 1.112 1.071 L .023 .24 L .042 .522 L .087 .254 L .42 .493 L 1.096 .83 L 1.282 1.296 L .26 .197 L .636 .069 L .313 .38 L .282 1.016 L .302 .578 L .638 .605 L .293 .663 L .341 1.382 L .524 2.809 L -.295 .438 L -.235 .495 L .05 .819 L -.095 .41 L .056 .664 L -.027 .099 L -.364 .551 L -.447 .439 L -.254 .127 L -.509 .1 L -.419 .17 L -.501 .354 L -.591 .622 L -.579 .354 L -.325 .043 L -.512 -.197 L -.404 -.31 L -.179 -.141 L -.153 .424 L .051 .494 L .048 .353 L -.205 .721 L -.388 .424 L -.326 .071 L -.235 -.07 L -.246 .481 L -.427 .326 L -.523 .142 L -.417 .213 L -.459 .565 L -.196 .269 L -.406 .297 L -.264 .099 L -.365 -.042 L .078 -.861 L .1 -1.313 L .151 -.494 L .215 -.283 L -.02 -.353 L -.475 -.437 L -.749 -.238 L -.091 -.066 L .3 -.289 L .646 -.229 L .915 -.528 L .599 -.229 L .497 .011 L .688 .194 L .17 -.27 L -.03 -.197 L -.568 -.435 L -.216 -.422 L .234 -.425 L .99 -.571 L .521 -.229 L .932 -.443 L .599 -.187 L .385 -.285 L .217 -.509 L -.054 -1.073 L .05 -.424 L .076 -.367 L -.455 -1.014 L -.029 -.663 L .215 -.905 L .155 -.918 L -.064 -.578 L -.214 -.437 L -.529 -.477 L -.072 -.282 L .226 -.439 L -.136 -.395 L -.358 -.308 L -.685 -.391 L -.471 -.52 L -.57 -.914 L -1.683 -2.121 L -.698 -.772 L -.637 -.646 L -.632 -.476 L -1.234 -.741 L -.162 -.098 L -.043 -.494 L .277 -.369 L .311 -.101 L .476 .068 L .287 -.058 L .261 -.185 L .255 -.326 L -.009 -.508 L -.87 -.968 L -.434 -.675 L -.262 -.083 L -.39 .171 L -.509 .483 L -.287 .058 L -.47 -.195 L -.607 -.434 L -.334 -.689 L -.338 -.929 L -.543 -.604 L -.613 -.575 L -.45 -.745 L
+217.961 150.385 N .304 -.043 L .84 -.27 L -.17 -.254 L -.312 -.112 L -.369 -.056 L -.651 .016 L -.497 -.042 L -.645 .157 L -1.193 .92 L -.371 .029 L -.653 .001 L -.211 .113 L -.189 .452 L -.396 .284 L -.32 .043 L -.786 .086 L .259 -.325 L .473 -.312 L -.128 -.593 L .282 -.382 L .114 -.099 L 1.258 -.61 L 1.625 -.47 L 1.164 -.087 L .842 -.157 L .825 .041 L .566 -.044 L .73 .168 L .848 .083 L .603 .197 L .557 .112 L .477 .013 L .499 .268 L .573 .536 L .382 .253 L .581 .168 L .768 .111 L 1.229 .351 L 1.02 .492 L .453 .31 L .374 .55 L .33 .141 L .479 .041 L 1.704 .519 L 1.018 .167 L .327 .239 L -.344 .58 L .233 .155 L .559 .042 L .756 -.072 L .495 .168 L .507 .38 L .591 .281 L .381 .296 L -.233 .085 L -.981 .087 L -1.15 .398 L -.626 .058 L -1.054 -.209 L -.9 -.041 L -.934 .186 L -.943 .115 L -.484 .029 L -.449 -.07 L .353 -.382 L .728 -.623 L .173 -.396 L 229 154.204 l -.181 -.127 L -.622 -.14 L -.7 .001 L -.603 -.112 L -.651 -.338 L -.141 -.748 L -.258 -.536 L -.218 -.155 L -.396 -.027 L -1.005 .044 L -.836 -.139 L -.621 -.225 L -.956 -.493 L -.739 -.238 L -.615 -.069 L -1.154 -.068 L -.489 -.098 L -.855 -.352 L
+634.036 168.444 N .808 -.64 L .121 -.438 L -.002 -.945 L -.157 -.507 L -.419 -.703 L -.979 -1.279 L -.255 -.464 L -.107 -.366 L -.058 -1.524 L -.435 -.632 L -.688 -.659 L -.285 -.535 L -.052 -.282 L -.266 -.153 L -.893 -.192 L -.403 -.012 L -.286 .453 L -.2 .538 L -.543 .257 L -.223 .072 L -.59 -.265 L -.835 -.348 L -.346 .03 L -1.173 1.178 L -.37 .411 L -.481 -.138 L -.145 -.324 L .027 -.494 L .117 -.438 L .528 -1.569 L .085 -.41 L -.249 -1.311 L -.045 -.113 L -.414 .045 L -.489 .2 L -.423 .003 L -.186 -.154 L -.066 -.367 L .106 -.805 L -.01 -.423 L -.118 -.168 L -.295 -.182 L -.541 -.166 L .193 -.185 L .582 -.455 L .442 -.581 L .53 -.61 L .502 -.355 L .178 .196 L .321 .21 L .769 .08 L .266 -.213 L .109 -.339 L -.119 -.521 L -.228 -.366 L -.138 -.592 L .043 -.325 L .24 -.241 L .679 -.314 L .45 .745 L .613 .575 L .543 .604 L .338 .929 L .334 .689 L .607 .434 L .47 .195 L .287 -.058 L .509 -.483 L .39 -.171 L .262 .083 L .434 .675 L .87 .968 L .009 .508 L -.255 .326 L -.261 .185 L -.287 .058 L -.476 -.068 L -.311 .101 L -.277 .369 L .043 .494 L .162 .098 L 1.234 .741 L .632 .476 L .637 .646 L .698 .772 L 1.683 2.121 L .57 .914 L .471 .52 L .685 .391 L .358 .308 L .136 .395 L -.226 .439 L .072 .282 L .529 .477 L .214 .437 L .064 .578 L -.155 .918 L -.209 .114 L -.975 .429 L -.3 .072 L -.373 -.351 L -.444 -.181 L -.476 .186 L -.392 .285 L .107 .296 L .187 .182 L .103 .211 L -.095 .24 L -.248 .058 L -.469 -.251 L -.341 -.111 L -.736 -.165 L -.533 -.251 L
+60.074 72.607 N -.099 .228 L -.491 .472 L -.395 .183 L -.462 .062 L 58 73.461 l -.961 -.362 L -.153 -.197 L .169 -.289 L .54 -.274 L .341 -.32 L .716 .364 L .3 .091 L .465 -.26 L .215 -.213 L .064 -.366 L .485 -.047 L 1.107 .135 L .536 .334 L .133 .213 L -.756 .062 L -.429 0 L -.59 .184 L -.11 .092 L h 40.092 77.571 m -.729 -.029 L -.097 -.24 L .011 -.3 L .802 -.243 L .326 -.211 L .593 -.423 L .448 -.137 L .646 -.077 L 1.427 .253 L .711 .24 L -.079 .211 L -.303 .046 L -.754 -.074 L -.496 .031 L -1.077 .183 L -.269 .226 L -1.161 .543 L h 38.426 77.979 m -.515 -.209 L -.139 -.285 L .381 -.227 L .674 .27 L .093 .195 L -.122 .15 L -.372 .105 L h 37.896 78.449 m -.256 .084 L -.558 .151 L -1.109 -.058 L -.387 .135 L -.398 .434 L -.31 .15 L -.854 -.207 L -.135 -.224 L .497 -.359 L .5 -.315 L .955 -.166 L .863 -.346 L .39 .089 L .461 .224 L .341 .409 L h 29.628 81.29 m -.168 -.594 L -.324 -.476 L .839 -.136 L .424 .088 L .436 .238 L -.244 .268 L -.26 .06 L -.073 .297 L -.22 .09 L -.412 .164 L h 27.543 81.591 m -.39 .031 L -.741 .165 L -.311 -.133 L -.088 -.178 L .104 -.119 L .336 -.268 L .294 -.09 L .584 .222 L .212 .371 L h 54.394 157.986 m -.559 -.356 L -.044 -.884 L -.243 -.677 L .482 -.402 L -.035 -.2 L -.156 -.26 L .052 -.149 L .173 -.046 L .354 .158 L .652 .279 L .593 .425 L -.015 .275 L .238 .046 L .12 .287 L .306 .149 L -.062 .161 L 56 156.933 l -.172 .204 L -.766 .195 L -.374 .23 L -.295 .425 L h 23.015 59.92 m -1.613 -.646 L -.75 -.205 L -.792 -.062 L -.9 .065 L -.291 -.095 L -.431 -.222 L .179 -.287 L .516 -.049 L 1.135 .221 L .579 -.001 L .543 -.081 L .538 -.001 L .828 .285 L 1.725 .362 L .429 .237 L .046 .111 L -.569 -.03 L -.646 .033 L -.527 .365 L h 99.855 70.993 m .467 .929 L -.071 .167 L -.879 -.272 L -.621 -.075 L .067 .441 L -.056 .228 L -.935 -.607 L 97.03 71.41 l .396 -.458 L .263 -.153 L .612 -.078 L .784 .38 L .771 -.108 L h 100.975 73.606 m .128 .272 L -.086 .273 L -.318 .483 L -.559 -.815 L -.597 -.909 L -.04 -.228 L .095 -.213 L .407 .029 L .568 .197 L .402 .91 L h 106.858 78.207 m -1.872 -1.166 L -.566 -.555 L .01 -.467 L -.559 -.843 L .071 -.106 L .456 .06 L .274 .256 L 1.165 .48 L .086 .196 L -.059 .136 L -.149 .226 L .296 .436 L .839 .374 L .007 .974 L h 140.191 127.819 m -.043 -.094 L -.198 -.36 L -.049 -.067 L -.032 .042 L -.028 .05 L -.04 -.092 L .002 -.664 L -.331 -.604 L -.472 -.451 L -.661 -.451 L -.512 -.197 L -.114 -.052 L -.145 .034 L .002 .092 L -.088 .025 L -.1 -.042 L -.146 -.143 L .076 -.076 L -.073 -.202 L -.228 -.252 L -.283 -.025 L -.312 .084 L -.932 -.336 L -.286 -.328 L -.428 -.244 L -.383 .042 L -.932 -.16 L -.721 .051 L -.12 -.185 L -.234 -.067 L -.046 -.177 L .094 -.117 L -.157 -.504 L .133 -.464 L -.227 -.096 L -.127 .008 L -.249 -.134 L 0 -.101 L .075 -.093 L -.029 -.219 L -.347 -.185 L -.254 -.286 L -.415 -.219 L -.391 -.623 L -.202 -.076 L -.203 -.311 L -.409 -.219 L -.156 -.529 L -.002 -.227 L .178 .007 L .147 -.164 L .029 -.326 L -.208 -.251 L -.192 -.045 L -.22 .037 L -.303 -.126 L -.535 -.514 L .078 -.21 L -.091 -.312 L -.129 -.067 L -.044 -.463 L .058 -.152 L .119 -.025 L .099 .067 L .073 .076 L -.086 .101 L .153 .202 L .285 .126 L .116 .118 L .203 .017 L -.385 -.564 L -.183 -.144 L -.021 -.236 L -.184 -.109 L -.051 -.344 L -.13 .006 L -.011 .144 L .048 .446 L -.093 .017 L -.293 -.194 L -.119 .042 L -.516 -.404 L -.136 -.363 L -.377 -.514 L -.531 -.379 L -.624 -.583 L -.123 -.142 L .114 -.101 L -.327 -.751 L .161 -.43 L -.254 -.479 L -.22 -.355 L -.738 -.782 L -.104 -.299 L .099 -.627 L .252 -.628 L .166 -.357 L .059 -.856 L -.215 -.785 L -.692 -1.486 L -.153 -.916 L .096 -.287 L .231 -.244 L .402 -.201 L .365 -.717 L -.365 -.573 L -.066 -.33 L .424 -1.593 L .153 -.575 L .061 -.634 L .091 -.778 L .019 -.179 L -.153 -.16 L .08 -.231 L -.103 -.167 L .157 -.077 L -.03 -.186 L -.046 -.186 L -.031 -.103 L -.004 -.058 L .322 .096 L .209 -.019 L .062 -.097 L -.211 .006 L -.614 -.122 L .062 -.707 L -.103 -.328 L .017 -.277 L .587 -.225 L -.345 -.019 L -.16 -.142 L -.129 0 L -.053 .045 L .042 .116 L -.12 .052 L -.133 -.979 L -.927 -1.463 L -.017 -.465 L .129 -.131 L .544 .086 L .632 .217 L .785 .114 L .641 .028 L .546 -.044 L .415 .086 L .547 .318 L .039 .435 L -.42 .407 L -.413 .347 L .532 .146 L .184 .188 L .251 .169 L .029 -.228 L .161 -.232 L .393 -.305 L .21 -.581 L .102 -.465 L -.064 -.421 L -.356 -.958 L -.158 -.305 L -.655 -.516 L .194 .013 L 2.579 .001 L 1.335 .022 L 4.588 -.025 L 3.938 .008 L 2.87 -.001 L 1.687 .006 L 5.117 -.028 L .74 .011 L 4.13 .021 L 1.089 -.035 L 3.821 .023 L .875 -.005 L 3.617 -.004 L 4.84 .018 L .601 -.003 L 2.935 .014 L 2.131 -.012 L 2.781 .029 L 2.915 -.016 L 2.105 .003 L 1.348 -.007 L 2.798 .029 L 2.687 -.029 L .68 .003 L -.387 -.588 L -.131 -.347 L .501 -.036 L .896 .748 L .279 .371 L .468 .46 L .833 .451 L .518 -.076 L 1.425 .208 L .02 .185 L .271 -.012 L .338 .48 L .16 -.247 L .502 .013 L .241 .271 L .469 .086 L .064 .185 L .506 .098 L .573 -.141 L .219 -.06 L .412 -.191 L .373 -.075 L .028 .282 L .197 .116 L .855 -.083 L .474 .041 L .156 .115 L .196 .144 L .542 -.049 L .707 .074 L 1.469 -.592 L .805 -.189 L .797 .227 L .977 .386 L 3.975 1.576 L 2.15 1.061 L .101 .429 L .46 .465 L .628 -.024 L .178 .135 L .184 .294 L .916 .181 L .307 .235 L -.11 .318 L .26 .33 L 2.529 1.05 L .876 3.16 L .054 .545 L -.028 .746 L -.377 .576 L -.294 .544 L -.264 .433 L -.414 .294 L -.707 .525 L -.044 .218 L .012 .33 L .371 .427 L .497 .169 L .573 .068 L .524 -.117 L .925 -.506 L .939 -.478 L .88 -.262 L .919 -.062 L .944 -.163 L 1.464 -.452 L .875 -.427 L -.047 -.362 L -.16 -.471 L -.018 -.319 L .162 -.375 L .47 -.203 L .93 -.091 L 1.123 .01 L 1.305 .138 L 1.156 -.197 L .44 -.275 L .163 -.512 L .146 -.434 L .545 -.164 L 1.754 -.814 L .534 -.305 L .968 -.523 L 1.76 -.009 L 2.508 .029 L 1.855 .004 L 1.093 .095 L .174 -.375 L .363 -.435 L .402 -.06 L 1.161 .124 L 1.139 -1.45 L 1.139 -2.22 L .514 -.626 L .632 -.526 L .273 .085 L .505 .36 L .381 .085 L .41 -.176 L .771 .025 L .488 .288 L .174 .274 L .31 2.819 L -.077 .229 L .606 .231 L .224 0 L .042 .154 L -.143 .069 L .02 .256 L -.192 .077 L .16 .291 L .188 -.153 L .349 .495 L -.268 .281 L .299 -.04 L .171 .093 L -.511 .374 L -.509 .093 L -.297 -.12 L -.013 .253 L -.138 .067 L -.077 -.107 L -.231 -.08 L -.277 .133 L -.101 .28 L -.171 -.013 L -.15 .16 L -.175 -.347 L -.746 .28 L -.204 -.093 L .12 .413 L -.666 -.213 L .199 -.48 L -.149 -.04 L -.364 .52 L -.332 .56 L -.342 .333 L -.324 -.227 L -.249 .439 L -.346 -.08 L .122 -.307 L -.325 .253 L .165 .16 L -.326 .293 L -.318 -.133 L .105 -.226 L -.654 .253 L .065 .359 L -.264 .04 L -.161 .373 L -.352 .106 L -.333 .679 L -.404 .505 L .173 .146 L .068 .212 L .168 .053 L .083 -.08 L .169 .013 L -.122 .146 L -.547 .106 L .053 .093 L -.392 .292 L -.068 .159 L .337 .027 L .282 .093 L .599 .704 L .055 .398 L .399 .106 L .691 -.239 L -.022 -.186 L -.14 -.027 L -.254 -.279 L -.097 -.04 L -.009 -.066 L .196 0 L .23 .133 L .218 .358 L .031 .425 L -1.599 .292 L -.032 -.385 L -.124 -.066 L -.109 .226 L -.164 .04 L -.03 .093 L -.105 -.106 L -.159 .266 L -.164 .04 L -.294 .04 L -.045 -.332 L .198 -.332 L -.443 .119 L -.154 -.146 L -.082 .252 L -.087 .664 L -1.429 .132 L -1.694 .159 L -1.182 .345 L -.787 .358 L -.097 .212 L -.32 .053 L -.144 .172 L -.032 -.04 L .308 -.756 L .024 -.106 L -.071 .027 L -.41 .994 L -.079 -.08 L -.406 .292 L .218 .318 L .553 .093 L -.46 1.515 L -.302 .429 L -.259 -.092 L .043 .251 L -.062 .185 L -.237 .145 L -.462 .501 L -.292 .304 L -.167 .026 L -.075 -.119 L .177 -.31 L -.113 -.178 L -.43 .013 L -.447 -.343 L -.148 -.053 L -.329 -.541 L .315 -.257 L .151 -.245 L -.271 .119 L -.362 .37 L .489 .845 L .033 .356 L .387 .581 L .28 .066 L .104 .765 L -.101 .238 L -.151 .23 L -.125 -.013 L -.487 .666 L -.396 .798 L .034 .053 L -.13 .132 L -.107 -.125 L -.374 .725 L .026 .125 L -.226 .04 L -.137 -.263 L .342 -.864 L .195 -.29 L .247 -.119 L .061 -.237 L -.093 -.059 L -.374 .119 L .226 -.383 L -.218 .04 L -.176 -.093 L .012 -.191 L .242 -.04 L -.077 -.33 L -.439 .296 L -.241 -.204 L -.157 .053 L -.23 -.396 L .355 -.171 L .357 -.053 L -.005 -.06 L -.604 -.316 L -.092 .165 L -.072 0 L .107 -.323 L .089 -.02 L .21 .159 L .131 -.06 L -.098 -.224 L -.353 -.066 L -.065 -.112 L .096 -.112 L .336 .02 L .193 -.284 L -.281 .046 L -.158 -.059 L .241 -.37 L .652 -.152 L -.328 -.06 L .146 -.409 L -.28 .093 L -.096 .132 L .11 .079 L -.315 .191 L -.035 -.224 L -.093 .053 L .051 .224 L -.081 .086 L -.051 -.158 L -.097 -.066 L -.103 .416 L -.447 -.079 L .402 .501 L -.294 .666 L .07 .237 L .272 .488 L -.055 .139 L -.466 -.317 L -.1 -.211 L .026 .205 L .174 .218 L .421 .237 L .132 .508 L -.631 -.402 L -.354 -.007 L -.118 -.283 L -.155 -.053 L .066 .25 L -.541 -.323 L -.33 .04 L .015 -.29 L .427 -.323 L -.428 .079 L -.19 .468 L .204 .231 L .457 .046 L .202 .25 L .954 .297 L -.047 .092 L .554 .165 L .158 .132 L -.22 .468 L -.227 .06 L -1.042 -.804 L .708 .811 L .626 .171 L -.248 .092 L .323 .079 L -.045 .079 L .061 .06 L -.034 .25 L -.312 -.191 L -.071 .073 L .104 .211 L -.216 .02 L -.656 -.56 L -.023 .026 L .419 .475 L .309 .158 L .182 -.026 L .191 .21 L .018 .31 L -.298 .059 L -.492 -.534 L -.474 -.198 L -.199 .16 L .046 .044 L .44 .145 L .488 .382 L -.047 .237 L .442 -.033 L .031 -.119 L .748 .119 L .151 .382 L .406 1.212 L .448 .803 L -.14 -.092 L -.262 -.349 L -.059 -.132 L -.359 -1.172 L -.147 -.277 L -.056 .31 L .135 0 L .034 .198 L -.292 -.066 L .173 .283 L .144 .099 L .228 .58 L -.144 -.053 L -.211 -.382 L .002 .224 L -.52 -.303 L -.06 .059 L .266 .303 L -.247 .119 L -.526 -.204 L .225 .204 L -.375 .211 L -.173 -.02 L -.251 -.21 L -.024 -.217 L .083 -.158 L -.081 -.053 L -.091 .204 L .044 .23 L .116 .211 L -.107 .158 L .894 .02 L .571 -.145 L .125 .165 L -.113 .191 L -.072 .369 L .14 .066 L .092 -.257 L .135 -.369 L .18 -.105 L .266 .31 L .047 .296 L -.166 .25 L -.163 -.013 L -.063 -.099 L -.316 .474 L -.254 .197 L -.483 -.053 L -.203 -.065 L -.147 -.066 L -.136 -.245 L -.151 .014 L .141 .244 L -.075 .013 L -.538 -.125 L -.436 -.151 L .162 .185 L .269 .026 L .833 .335 L -.034 .119 L -.396 .145 L .247 .007 L -.19 .25 L -.281 .138 L -.149 0 L -.481 -.375 L .242 .395 L .43 .164 L .302 -.171 L .292 .026 L .11 -.204 L .04 .178 L .217 .04 L .048 .079 L -.428 .322 L -.013 .085 L -.261 .072 L -1.498 .214 L -.865 .895 L -.487 .609 L -.13 .127 L -.935 .143 L -.528 .128 L -.617 .241 L -.678 .539 L -.225 .424 L -.096 .354 L -.819 .694 L -.693 .383 L -.429 .199 L -.797 .086 L -.35 .58 L -.177 .198 L -.809 1.125 L -.273 .781 L -.459 1.249 L .236 1.455 L .387 .925 L .456 .873 L .934 1.562 L .352 1.746 L .486 1.194 L -.075 .092 L .287 .276 L .123 .333 L .062 .827 L -.301 1.536 L -.064 .278 L -.31 .415 L .108 .424 L -.02 .252 L -.393 .551 L -.017 -.092 L .129 -.241 L -.025 -.138 L -.256 .035 L -.38 .137 L -.291 -.126 L -.509 .138 L -.12 -.329 L .014 -.233 L -.567 -1.068 L -.764 -.138 L -.204 -.352 L -.113 -.819 L -.423 -.229 L -.144 -.702 L -.373 .093 L -.608 -1.08 L -.375 -.482 L .296 0 L .375 -.438 L .048 -.226 L -.167 -.226 L -.471 .407 L -.277 -.208 L .126 -.573 L .147 -.758 L .158 -1.043 L -.293 -.452 L -.258 -.169 L -.496 -.126 L -.832 -.987 L -.875 -.804 L -.528 -.168 L -.43 .072 L -.536 .298 L -.456 .354 L -1.202 .299 L -.273 -.213 L -.131 -.62 L -.253 -.254 L -.264 -.113 L -.752 -.069 L -.516 -.296 L -.22 -.233 L -.504 .138 L -1.052 .115 L -.653 -.184 L -.047 .298 L -.64 .099 L -.183 0 L -.578 -.926 L -.238 .781 L -.447 -.135 L -.65 .001 L -1.328 -.04 L -.672 .439 L -.39 .055 L -1 -.459 L -.096 .009 L -.142 .014 L -.362 .528 L .201 .229 L .303 0 L .211 0 L .537 -.207 L .406 .092 L .676 .482 L -.68 .373 L .02 .254 L .263 .353 L .593 .146 L .229 .217 L .35 .334 L -.533 .136 L -.503 -.084 L -.276 -.419 L -.79 -.271 L -.224 -.211 L -.265 -.056 L -.013 .02 L -.209 .32 L .209 .154 L .248 .183 L -.248 .179 L -.069 .05 L -.447 -.459 L -.476 .192 L -.287 .291 L -1.025 -.472 L -.419 -.494 L -1.16 -.642 L -.615 .066 L .554 .393 L -.307 .187 L -1.17 -.083 L -.886 -.252 L -.896 -.168 L -1.547 .173 L -.632 .328 L -.392 -.015 L -.433 -.031 L -.135 -.49 L -.333 .057 L -.112 .184 L .474 .731 L -.877 .64 L -.808 .577 L -.915 .317 L -.419 .043 L -.414 -.056 L -.728 -.111 L -.126 .198 L .437 .437 L -.239 .396 L -.327 .199 L -.631 .114 L -.737 .27 L -.268 .17 L .558 .352 L .111 .169 L -.659 .694 L -.154 .297 L -.012 .848 L .144 .636 L .271 .762 L .425 .903 L -.347 -.119 L -.816 -.377 L -.296 .001 L -.416 .116 L -.264 -.069 L -1.029 -.56 L -.921 -.32 L -.375 -.365 L -.336 -.592 L -.332 -.932 L -.078 -.467 L -.268 -.253 L -.657 -.576 L -.845 -1.042 L -.744 -1.227 L -.663 -1.029 L -.363 -.366 L -.412 -.252 L -.783 -.321 L -.475 -.082 L -.643 .018 L -.468 .201 L -.576 .541 L -.418 .413 L -.283 .37 L -.416 .158 L -.501 -.011 L -.337 -.069 L -1.104 -.503 L -1.092 -.659 L -.445 -.549 L -.318 -.847 L -.284 -.678 L -.179 -.226 L -.708 -.491 L -.837 -.519 L -.766 -.632 L -.631 -.662 L -.209 -.112 L -1.892 -.046 L -1.858 -.003 L -.096 .892 L -.213 .101 L -1.867 .011 L -.966 -.037 L -1.544 -.02 L -1.662 -.019 L -.338 -.055 L -3.516 -1.112 L -2.811 -.933 L -1.186 -.39 L -.267 -.154 L -.316 -.31 L -2.381 .084 L -2.367 .155 L -.34 .017 L h 49.818 152.776 m -.122 .086 L -.279 .03 L -.111 -.131 L -.177 -.005 L -.324 .051 L -.304 -.39 L -.071 -.263 L .339 -.01 L .299 -.253 L .188 .218 L .106 .294 L .223 .096 L .233 .279 L h 52.785 154.312 m -.155 -.081 L -.085 -.356 L -.461 -.321 L .095 -.229 L .143 -.058 L .366 .209 L .344 .055 L .616 .356 L -.005 .172 L -.294 .184 L -.563 .069 L h 111.683 77.224 m -.138 .415 L -.45 .067 L -.324 .113 L -.295 .247 L -.321 -.137 L -.185 -.21 L .087 -.443 L .086 -.443 L -.438 -.675 L -.463 -.319 L -.199 -.271 L -1.281 .055 L -.437 .098 L -.153 .161 L -.496 .097 L -.019 -.193 L -.034 -.432 L .212 -.272 L .184 -.212 L -.378 -.347 L -.641 -.438 L -.693 -.696 L -.723 -.317 L -.453 -.136 L .132 -.35 L -.569 -.592 L -.099 -.213 L .371 -.229 L -.068 -.122 L -.301 -.152 L -.445 -.076 L -.392 -.274 L -.237 -.259 L -.57 -.305 L -1 -.411 L -.479 -.765 L -.217 -.583 L -.367 -.399 L -.357 .016 L -.101 .814 L .42 .873 L .104 .306 L -.047 .153 L -.701 -.136 L -.272 -.076 L -.511 -.504 L -.4 -.459 L -.537 .139 L -1.219 -.228 L 1.263 .718 L .032 .214 L -1.62 .171 L -1.093 -.35 L -1.388 -.948 L -.543 -.292 L -.664 -.043 L -.079 0 L -.933 -.213 L -1.3 -.536 L .928 -.248 L .135 -.169 L 90.8 67.129 l -.384 -.153 L -.792 .156 L -.454 .14 L -.656 .017 L -1.058 -.06 L -1.068 -.245 L .027 -.247 L -.148 -.186 L -.325 -.108 L -.359 .016 L -.47 .202 L -1.036 .049 L -1.465 -.122 L 80.46 66.64 l -.786 -.091 L -.248 -.108 L -.651 -.387 L -.427 -.527 L -.301 .218 L -.788 .157 L -.89 -.293 L -.234 -.326 L -.417 -.139 L -.872 -.248 L -1.538 -.23 L -.817 -.248 L -.671 -.342 L -.553 .235 L -.675 .079 L .06 .437 L -.193 .062 L -.389 .25 L -.249 .405 L 1.119 .293 L .174 .294 L -.096 .388 L -.428 .449 L -.458 .001 L -.804 -.214 L -.586 -.061 L -.568 .094 L -.978 .603 L -1.066 .217 L -.936 .448 L -1.035 .448 L -1.095 .109 L .178 -.308 L .063 -.123 L .72 -.401 L -.093 -.385 L -.655 -.523 L .004 -.108 L 64.1 66.19 l .411 -.482 L .157 -.42 L .736 -.312 L .87 -.235 L 1.165 -.018 L 1.085 .123 L .239 -.156 L -1.239 -.466 L -.971 -.389 L -1.043 .049 L -.226 .219 L -.449 .095 L -.573 .438 L -.865 .375 L -1.019 .282 L 61.553 65.9 l -.406 .094 L -.298 .14 L .131 .325 L -.177 .526 L -.563 .34 L -.564 .078 L 59 67.544 l -.592 .278 L -.681 .601 L -.035 .292 L .38 .168 L .36 .03 L .667 .106 L .465 .229 L -.075 .184 L -.43 .338 L -.625 .2 L -.557 .277 L -.423 .398 L -.544 .383 L -.675 .093 L -1.434 .308 L -.678 .397 L -1.036 .337 L -.7 .367 L .52 .5 L -.1 .167 L -1.106 .412 L -.897 .153 L -.778 .168 L 49.51 74.19 l -1.214 .456 L 48.1 74.828 l -.274 .394 L -.753 .439 L -1.193 .229 L -1.234 .184 L -.973 -.345 L .001 -.181 L .332 -.348 L .763 -.273 L .306 -.212 L .445 -.364 L 1.171 -.441 L 1.403 .073 L -.12 -.212 L .02 -.516 L .47 -.304 L .725 -.291 L .754 -.366 L .266 -.214 L .002 -.731 L .246 -.749 L .693 -.49 L .194 -.398 L .034 -.412 L -.633 .122 L -1.251 .186 L -.676 -.029 L -.5 -.597 L -.266 .062 L -.613 .216 L -.136 .23 L .239 .352 L .021 .352 L -.169 .046 L 47 70.115 l -.567 -.32 L -.487 -.397 L -.509 -.291 L -.56 -.03 L -.812 -.106 L -.91 .094 L .028 .138 L -.644 .338 L -1.175 .14 L -.649 -.09 L -.064 -.123 L -.082 -.107 L -.125 -1.028 L .3 -.508 L -.142 -.431 L -.864 -1.002 L -1.43 .437 L -.738 .078 L -.406 .202 L -1.091 .094 L -.4 -.23 L -.394 -.355 L -.466 -.325 L -1.007 -.463 L -.179 -.28 L .292 -.171 L .337 -.437 L .704 .139 L 1.312 .309 L .69 .03 L .238 -.234 L -.375 -.482 L -.458 -.264 L -.363 0 L -.541 .22 L -.528 -.015 L -1.342 -.513 L -.623 -.186 L -.197 .016 L -.858 -.029 L -.024 -.078 L -.623 -.985 L .79 -.19 L .071 -.456 L .495 -.221 L .102 -.299 L .227 -.347 L .893 -.491 L .337 -.38 L .386 -.301 L .527 -.476 L .39 -.175 L .719 .109 L .98 .268 L .485 .094 L .752 -.144 L .427 -.254 L .675 -.429 L 1.252 -.257 L .774 -.033 L .955 -.049 L .354 -.175 L .187 -.478 L -.259 -.669 L -.814 -.686 L .026 -.096 L .927 -.034 L .343 -.256 L -.25 -.384 L -.497 -.256 L -.367 -.08 L -1.88 .389 L 39.33 56.53 l -.534 .289 L -.496 .065 L -1.907 -.333 L -.848 -.031 L 34.8 56.49 l -1.27 .162 L -1.265 -.029 L -1.349 -.174 L -.53 -.207 L -.183 -.788 L .144 -.146 L .636 -.033 L 1.008 -.002 L .446 -.179 L -1.057 -.241 L -1.912 -.304 L 28.13 54.31 l -1.285 -.208 L .219 -.114 L .781 -.262 L 1.568 -.214 L 1.325 -.328 L .958 -.214 L 1.058 -.361 L .953 -.23 L 1.399 -.281 L 1.513 .128 L -.158 .523 L .023 .277 L 1.051 .161 L 1.359 .095 L 1.074 -.019 L .657 -.099 L .784 -.246 L .55 -.295 L .262 -.083 L .752 .064 L .751 .113 L 1.021 -.051 L .2 -.327 L .007 -.18 L -.689 -.064 L -1.946 -.292 L -1.283 -.047 L -.312 -.755 L -1.473 -.162 L -.96 -.031 L -1.573 -.096 L -.194 -.511 L -.484 -.312 L -1.042 -.461 L -.512 -.148 L -2.66 -.659 L -2.008 -.545 L .317 -.167 L .812 -.402 L .086 -.485 L 2.136 -.054 L .99 -.069 L 1.829 -.288 L .784 -.354 L .452 -.623 L .788 -.575 L .616 -.525 L .818 -.41 L .742 -.224 L 1.066 -.104 L 1.133 -.241 L 1.047 -.036 L 1.804 .065 L .146 -.188 L -.156 -.205 L 44.47 42.83 l .018 -.206 L 1.936 -.089 L 1.143 .032 L .974 -.054 L 1.344 -.14 L 1.098 -.416 L .918 -.417 L .957 -.019 L .282 .051 L .675 .241 L .156 .172 L -.383 .139 L .017 .344 L 1.049 .136 L .424 .034 L .536 -.293 L .297 -.208 L 1.419 .187 L 1.534 .049 L 1.062 .049 L .715 .033 L .711 .257 L .359 .274 L .783 .358 L .494 .085 L .421 -.086 L 1.292 .117 L 1.124 .015 L 1.556 -.054 L 1.449 -.088 L 1.213 .1 L 1.377 .254 L .883 .118 L 3.424 .13 L 1.279 .168 L .743 .169 L 2.027 -.038 L 2.339 -.141 L 1.123 .236 L 2.441 .791 L 1.206 .301 L .227 .008 L .102 .483 L -.003 2.855 L .039 2.259 L .052 2.335 L .129 2.796 L -.026 2.183 L -.043 4.334 L .026 2.167 L .089 1.046 L .196 .279 L .84 .074 L 2.424 -.122 L .739 .059 L .332 .388 L .173 .387 L .348 .292 L 2.162 1.318 L .945 .673 L .238 -.325 L .848 -.205 L 1.225 -.67 L .731 -.498 L .495 -.126 L .832 .073 L .316 .199 L .371 .492 L .35 .322 L 2.048 1.175 L .814 .58 L 1.769 1.768 L 1.67 1.882 L .512 .393 L .189 .029 L .98 .314 L 2.025 .763 L .402 .255 L -.163 .788 L .393 .777 L
+643.755 159.873 N -1.092 -.52 L -.637 -.337 L -.203 -1.284 L .036 -.282 L .24 -.241 L .42 -.241 L .721 -.623 L .493 .056 L .049 -.17 L .24 -.396 L .239 .028 L .573 .225 L .321 -.312 L .439 -.001 L .798 -.171 L .596 .69 L -.163 .17 L -.443 .354 L -.412 .538 L -.285 .734 L .14 .296 L -.22 .311 L -.292 .085 L -1.026 .383 L -.532 .707 L h 627.173 150.012 m -.679 .314 L -.24 .241 L -.043 .325 L .138 .592 L .228 .366 L .119 .521 L -.109 .339 L -.266 .213 L -.769 -.08 L -.321 -.21 L -.178 -.196 L -.153 -.239 L -.111 -.38 L -.628 .413 L -.647 .159 L -.246 -.083 L -.378 -.266 L -.341 -.746 L -.291 -.379 L -.481 .045 L -.507 .003 L -.228 -.098 L -.117 -.352 L .729 -1.584 L -.033 -.268 L -.521 -.096 L -.554 .074 L -.202 -.324 L -.277 -1.762 L -.156 -.126 L -.479 .017 L -.771 .089 L -.819 .442 L -.312 .086 L -.216 -.069 L 0 -.268 L .224 -.58 L -.163 -.705 L -.075 -.465 L .617 -.85 L .191 -.198 L .487 -.271 L .611 -.525 L .429 -.722 L .353 -.862 L -.02 -.875 L -.195 -1.649 L -.146 -.14 L -.504 .031 L -.287 -.041 L -.217 -.309 L -.243 -.901 L -.397 -.435 L -.504 -.279 L -.277 .044 L -.306 .34 L -.001 .127 L -.624 -.081 L -.73 -.179 L -.657 -.081 L -.3 -.055 L -.102 -.056 L .138 -.269 L .354 -.454 L -.046 -.38 L -.716 -.715 L -.455 -.392 L -1.377 .84 L -.377 .044 L -.975 -.319 L -.286 -.167 L -.355 .087 L -.546 .299 L -1.105 .726 L -.829 .258 L -.543 .37 L -1.123 1.107 L -.397 .27 L -.714 .216 L -.784 .033 L -.189 .1 L -.329 -.619 L -.312 -.209 L -.371 -.041 L -1.659 -.047 L 601 137.538 l -.309 -.294 L -.417 .016 L -.149 .103 L -.348 .239 L -.609 .539 L -1.251 1.545 L -.212 -.662 L .052 -.861 L -.139 -.183 L -.231 -.069 L -.471 .102 L -.345 .129 L -.655 -.159 L -.339 .281 L -.341 -.116 L -.849 .066 L -.319 -.364 L -.63 -.281 L -.407 0 L -.08 .331 L -.271 .083 L -.685 -.38 L .01 .364 L -.237 .099 L -.141 -.463 L -.54 -.496 L -.365 .066 L -.935 -.066 L -.014 -.265 L .175 -.396 L -.326 -.017 L -.333 .248 L -1.451 -.893 L .069 -.281 L -.178 -.38 L -.289 -.166 L -.71 .116 L -.158 .166 L -.657 -.794 L -.454 -.281 L -.15 .132 L -.472 -.215 L -.726 -.595 L -.867 -.264 L -.132 -.612 L -1.079 -.199 L -.186 .182 L -.275 -.066 L -.134 .513 L -.276 .314 L -.299 -.05 L -.24 -.43 L -.859 -.596 L -.154 .066 L -.756 -.248 L .116 -.364 L -1.078 -.579 L -.363 .116 L -.772 -.843 L -.383 -.248 L -.477 .314 L -.198 -.066 L -.099 -.43 L .215 -.215 L -.272 -.364 L .104 -.446 L -.579 -.595 L -.157 -.694 L .785 -.198 L .033 .364 L .337 .264 L .368 -.049 L .376 -.281 L .578 -.364 L -.367 -.694 L .104 -.414 L -.68 -.099 L -.044 -.182 L -.092 -.078 L -.718 -.096 L -.294 -.221 L -.037 -.552 L -.073 -.589 L .184 -.184 L .331 -.221 L -.221 -.258 L -.441 -.405 L -.81 -.11 L -.221 -.515 L -.441 -1.03 L 0 -.515 L -.502 .152 L -.147 -.126 L -.305 -.111 L -1.337 -.104 L -.565 -.436 L -.178 -.09 L -.104 -.199 L -.239 0 L -.52 .196 L -.252 -.281 L -.198 -.218 L -.301 -.07 L .312 -.516 L -.007 -.501 L -.213 -.247 L -.373 -.323 L -.625 .009 L -.282 .128 L -.004 -.456 L -.554 -.117 L -.296 -.047 L -.281 .21 L -.105 -.112 L -.313 -.14 L -.048 -.088 L -.236 -.012 L -.01 -.269 L .574 .059 L .192 -.145 L .354 -.136 L .08 -.249 L .181 -.185 L -.2 -.163 L .152 -.36 L -.245 -.237 L .126 -.428 L -.049 -.123 L -.152 -.012 L -.028 -.246 L .009 -.284 L -.295 -.494 L -.273 -.154 L -.692 -.039 L -.22 -.06 L -.229 .162 L -.463 .045 L -.325 -.394 L -.077 -.305 L .207 -.223 L -.023 -1.031 L .011 -.069 L .139 -.739 L .129 -.213 L .274 -.186 L 1.422 -.704 L 1.737 -.734 L .359 -.03 L .06 .071 L .183 .567 L .344 .055 L .507 -.145 L .885 -.132 L .268 -.243 L .463 -.784 L .467 -.472 L .238 -.03 L 1.248 .235 L .459 -.017 L .478 -.102 L .646 -.345 L .638 -.701 L .405 -.301 L .445 -.145 L 1.338 -.349 L .97 -.219 L .325 -.187 L .051 -.157 L -.031 -.194 L -.139 -.86 L .02 -.399 L .32 -.401 L .705 -.546 L .222 -.33 L -.119 -.47 L -.29 -.441 L -.345 -.627 L -.03 -1.357 L -.483 -.141 L -.366 .06 L -.232 -.185 L .068 -.215 L .215 -.302 L .393 -.188 L 1.799 -.254 L 1.082 -.207 L .35 -.06 L .5 .184 L 1.133 .238 L .394 -.074 L .231 -.145 L .156 -.245 L -.126 -.286 L -.622 -.514 L .137 -.418 L .286 -.605 L .438 -.794 L .516 -1.141 L .427 -.507 L 1.096 .254 L .721 .141 L .594 .141 L 1.402 .079 L .718 -.062 L .417 -.103 L .444 -.392 L .157 -.39 L -.213 -.707 L -.097 -.75 L .34 -.581 L .428 -.277 L 1.199 -.093 L .289 -.06 L .306 -.219 L .035 -.478 L .011 -.275 L .279 -.262 L .542 -.148 L .551 -.034 L .228 -.014 L .569 -.003 L .244 -.074 L .062 .145 L .131 .405 L .24 .563 L .371 .433 L 1.301 .745 L .834 .415 L .614 .069 L .731 .167 L .633 .144 L .354 .143 L .568 .618 L 1.07 1.451 L .401 .66 L -.215 .447 L -.237 .75 L -.214 .389 L -.369 .347 L -.035 .129 L .105 .43 L .233 .229 L .724 .312 L 1.062 .181 L 1.505 .021 L .995 .038 L .668 .083 L .998 .224 L .632 .268 L 1.645 .806 L .839 .31 L .744 .096 L .105 .542 L 1.571 2.161 L .467 .439 L .444 .254 L 1.979 .018 L 1.241 .207 L .802 .109 L 1.165 .022 L 2.861 -.059 L .937 .023 L 1.164 .022 L 1.69 .119 L .521 .168 L .815 .551 L 1.006 .365 L 1.599 .433 L .929 .123 L .663 -.061 L .61 .067 L .253 .297 L .433 .197 L .481 -.017 L .686 -.289 L 1.44 -.534 L .303 -.101 L .736 -.274 L .816 -.289 L 1.204 -.349 L 1.339 -.007 L 1.514 -.065 L .987 .08 L .651 -.061 L 1.941 -.737 L .265 -.172 L 1.111 -1.046 L .67 -.389 L 1.265 -.292 L .326 -.259 L .123 -.271 L -.188 -.228 L -.599 -.411 L -.389 -.569 L -.003 -.343 L .214 -.401 L .539 -.589 L .457 -.231 L .316 -.073 L .718 .154 L .668 .382 L .592 .125 L .982 -.005 L .744 -.047 L .742 -.433 L 1.192 -.91 L .224 .013 L .438 .012 L .624 .054 L .896 -.134 L .638 -.248 L .347 -.188 L .241 -.216 L .312 -.82 L .363 -.333 L .47 -.204 L .464 -.045 L .483 .127 L .353 -.189 L .831 -.278 L .539 -.146 L .937 -.221 L .854 -.033 L .432 .099 L 1.074 .008 L .464 .127 L .414 -.218 L .107 -.217 L -.048 -.273 L -.599 -.501 L -.879 -.99 L -.797 -.5 L -.538 -.199 L -.928 -.212 L -.438 .002 L -1.191 .786 L -.292 -.07 L -.431 -.416 L -.317 -.085 L -.576 .018 L -.754 .062 L -.929 .395 L -.342 .045 L -.051 -.029 L -.269 -.836 L .381 -.58 L 1.224 -1.959 L .687 -1.207 L .295 -.31 L .046 .018 L .452 .172 L 1.126 .574 L .343 -.016 L .438 -.089 L 2.44 -.752 L .779 -.339 L .123 -.233 L -.056 -.306 L -.35 -.348 L -.062 -.146 L .103 -.249 L .422 -.426 L .416 -.543 L .667 -.779 L .599 -.545 L 1.371 -.608 L .167 -.794 L -.107 -.249 L -.465 -.306 L -.558 -.026 L -.822 .078 L .119 -.25 L .375 -.282 L 1.193 -.787 L .478 -.165 L .602 -.136 L 1.854 -.143 L .836 -.123 L 1.203 -.109 L .917 -.049 L 1.148 .215 L 1.037 .481 L .683 .188 L 1.386 -.125 L .539 .026 L .763 .467 L .742 .952 L 1.087 2.384 L .94 1.588 L .927 1.903 L .436 .389 L .507 .2 L 1.247 .341 L 1.523 .253 L 2.659 .839 L .205 .144 L .297 .866 L .44 1.283 L .261 .446 L .68 -.033 L .649 -.018 L 1.657 -.052 L .604 -.22 L .953 -.308 L 1.357 -.31 L 1.181 -.208 L .902 -.034 L .246 .114 L .064 .259 L .116 .389 L .017 .504 L -.566 .407 L -.66 .393 L -.291 .707 L -.278 .893 L -.538 1.066 L -.627 1.08 L -.329 .432 L -.551 .69 L -.47 .347 L -.547 -.098 L -.679 -.225 L -.685 -.24 L -.396 -.041 L -1.664 .982 L -.048 .557 L .332 .897 L .062 .656 L -.006 .613 L -.025 .385 L -.097 .128 L .112 .299 L -.156 .329 L -.511 .43 L -1.252 .462 L -.111 .058 L -.579 -.68 L -.247 .001 L -.253 .129 L -.383 .358 L -.23 .713 L -.955 .532 L -.62 .259 L -.538 .017 L -.452 -.054 L -.333 -.126 L -.392 .059 L -.273 .243 L -.025 .342 L .508 .765 L .046 .113 L -.527 .159 L -.975 .048 L -.508 -.153 L -.493 -.253 L -.273 -.396 L -.448 .017 L -.386 .13 L -.686 1.027 L -.636 .543 L -1.032 .545 L -1.533 .604 L -.52 .329 L -.415 .442 L -.379 .528 L -.066 -.092 L -.417 .171 L -1.222 .13 L -.728 .171 L -2.248 .925 L -.632 .37 L -.566 .469 L -.604 .271 L -.336 .015 L .13 -.255 L .862 -.682 L .33 -.354 L -.47 -.113 L -.37 .072 L -.153 -.297 L .058 -.156 L .953 -.781 L .269 -.384 L .55 -.413 L .159 -.2 L -.057 -.298 L -.73 -.553 L -.727 -.283 L -.131 -.014 L -.628 .03 L -.166 .015 L -.494 .371 L -1.146 1.183 L -.355 .157 L -.643 .086 L -.613 .243 L -.36 .199 L 665.49 112 l -.136 .411 L -.131 .255 L -.251 .255 L -.437 .128 L -.493 -.013 L -.326 .27 L -.307 .114 L -.455 -.565 L -.355 -.014 L -.349 .128 L -.396 .638 L -.301 .694 L .088 .34 L .245 .368 L .558 .268 L .8 .268 L 1.21 -.045 L .29 .254 L -.019 .538 L -.123 .581 L .057 .538 L .261 .283 L .733 .069 L .698 -.157 L .76 -.525 L .509 -.497 L .552 -.228 L .534 -.128 L .287 .07 L .895 .621 L .543 .197 L 1.023 -.087 L .361 .027 L .471 .141 L .274 0 L -.248 .708 L -.057 .552 L -.612 -.197 L -.297 -.084 L -.525 .058 L -1.677 .555 L -.707 .44 L -.072 .735 L -.522 .157 L -.146 -.113 L -.017 -.269 L -.127 -.084 L -.501 .114 L .138 .466 L -.152 .368 L -.485 .496 L -1.397 1.119 L -.126 .226 L .039 .55 L .62 .225 L .712 .492 L .785 .521 L .391 .535 L .424 1.241 L .668 .647 L .175 .437 L -.13 .677 L .172 .183 L .694 .295 L .399 .592 L .562 .253 L .272 .268 L .087 .31 L -.049 .155 L -.789 .369 L .088 .07 L .425 .31 L .314 .79 L -.019 .296 L -.141 .184 L -.534 .043 L -.651 .213 L -.948 .552 L -.849 .213 L -.629 .297 L .72 .21 L .378 .056 L .944 -.425 L .488 -.058 L .162 .056 L .524 .592 L .387 .168 L .456 .027 L .009 .155 L -.231 .311 L -.382 .227 L -.304 .241 L .11 .155 L .326 -.029 L .202 .084 L -.184 .325 L -.298 .749 L -.192 .649 L .028 .353 L -.22 .452 L -.209 .127 L -.35 -.338 L -.146 .142 L -.569 .763 L -.401 .622 L -.215 .622 L -.127 .296 L -.595 .425 L -.251 .438 L -.254 .184 L -.569 .029 L -.382 .227 L .279 .719 L -.264 .508 L .076 .593 L -.093 .269 L -.207 .269 L -.532 .199 L -.161 .282 L -.174 .396 L -.294 .636 L -.626 .354 L -.412 .495 L -.492 -.14 L -.443 -.069 L -.142 .113 L -.145 .198 L .118 .833 L -.213 .142 L -.772 .651 L -.356 .127 L -.628 .171 L -.563 .467 L -.571 .213 L -.107 .113 L -.008 .48 L -.133 .156 L -.568 .058 L -.5 .114 L -.341 .438 L -.364 -.126 L -.52 -.168 L -.274 .057 L -.315 .326 L -.435 .198 L -.643 .1 L -.047 -.465 L -.52 .057 L -.699 .213 L -.32 .198 L -.285 -.042 L -.401 -.493 L -.163 -.155 L -.191 .283 L .095 .169 L -.045 .212 L -.047 .691 L -.209 .297 L -.416 .114 L -.501 -.182 L -.123 .282 L -.001 .24 L -.146 .155 L -.615 .058 L -.366 .114 L -.596 .043 L -.463 -.211 L -.217 .1 L -.439 .48 L -.227 .071 L -.774 -.041 L -.747 .227 L -.406 .326 L -.451 -.027 L -.277 -.084 L -.011 .057 L -.069 .353 L -.29 .396 L .011 .113 L .48 .634 L .269 .126 L .043 .198 L -.36 .269 L -.763 .157 L -.481 -.719 L -.241 -.691 L .012 -.395 L .396 -.777 L -.015 -.169 L -.587 -.253 L -.226 .071 L -.206 .297 L -.454 .072 L -.676 -.21 L -.574 -.733 L -.196 .085 L -.017 .169 L -.159 .396 L -.27 .128 L -.332 -.056 L -.481 .043 L -.055 .038 L -.197 -.143 L -.909 -.107 L -.621 -.166 L -.943 -.615 L -.273 -.365 L -.096 -.521 L .472 -.75 L -.056 -.38 L -.237 -.21 L -.777 .047 L -.66 -.053 L -.771 -.235 L -.623 -.575 L -.298 .101 L -.521 .328 L -.696 .554 L -.341 .157 L -.807 .117 L -1.595 .052 L -.332 .115 L -.584 .413 L -.325 -.097 L -.597 -.392 L -.341 -.012 L -.515 .13 L -.483 .229 L
+241.073 156.152 N .017 .52 L .098 1.215 L .012 .212 L -.379 .455 L -.011 .17 L .485 1.358 L -.669 -.577 L -.445 -.056 L -.761 .143 L -.877 -.012 L -.666 -.14 L -.574 -.056 L -.474 .1 L -.378 .354 L -.135 -.042 L -.993 -.549 L -.171 -.325 L .04 -.198 L .269 -.184 L 1.051 .097 L .631 .111 L 1.125 .167 L .654 .041 L .61 -.185 L .386 -.156 L -.198 -.155 L -.692 -.464 L -.136 -.296 L .184 -.707 L -.202 -.296 L -.394 -.154 L -.913 -.14 L -.305 -.211 L .04 -.184 L .119 -.085 L .344 -.1 L .724 -.058 L .781 .125 L 1.081 .294 L .576 .056 L .147 -.089 L
+241.295 160.082 N -.485 -1.358 L .011 -.17 L .379 -.455 L -.012 -.212 L -.098 -1.215 L -.017 -.52 L .503 -.279 L .393 .14 L .342 0 L .384 -.17 L .369 -.043 L .14 .198 L .177 .112 L 1 .309 L .657 -.072 L .213 .395 L .335 .338 L .528 .324 L .335 .084 L .643 .21 L .916 .45 L .399 .352 L .231 .311 L -.191 .17 L -.144 .297 L -.314 .368 L -.238 -.098 L -.476 -.592 L -.378 -.042 L -.788 .058 L -.288 -.098 L -.373 0 L -.329 .1 L -.763 .539 L -.396 -.056 L -.319 -.494 L -.166 -.028 L -.155 .057 L -.658 .326 L -.344 .778 L -.41 .65 L -.289 -.112 L -.325 -.551 L
+668.053 167.796 N -.131 -.099 L -.74 -.732 L -.444 -1.255 L .037 -.424 L .054 -.706 L -.292 -.465 L .18 -.382 L .978 .704 L .202 -.424 L .023 -.41 L -.101 -.438 L -.026 -.579 L .145 -.438 L .025 -.664 L .082 -.861 L .074 -.636 L .38 -.862 L .188 -.127 L .337 -.142 L .523 .055 L 1.21 .52 L .576 .042 L .188 -.212 L .277 -.17 L .199 .141 L .018 .396 L -.266 .438 L -.045 .48 L .14 .79 L .541 .394 L .182 .325 L -.427 1.271 L -.31 .467 L -.834 .608 L -.555 .312 L -.082 .099 L .003 .268 L -.078 .565 L -.28 .424 L .127 .211 L .35 .733 L .345 1.101 L .26 .62 L .093 .028 L .451 .324 L .296 .07 L .161 -.325 L .485 -.537 L .197 -.029 L .418 .112 L .226 .211 L .179 .635 L .286 .353 L .326 .084 L .505 -.58 L .621 .267 L .141 .028 L .078 .127 L -.168 .156 L -.378 .156 L -.208 .212 L .936 1.226 L -.315 .184 L -.568 -.196 L -.404 -.098 L -.094 -.48 L -.229 -.31 L -.599 -.535 L -.753 -.577 L -.258 0 L -.099 .226 L .371 .776 L -.218 .283 L -.727 -.775 L -.982 -.548 L -.309 .015 L -.344 .142 L -.182 .255 L -.14 .071 L -.395 .057 L -.322 -.225 L -.591 -.366 L -.195 -.423 L .64 -.608 L .323 .211 L .358 .084 L .4 -.199 L -.151 -.169 L -.713 -.295 L -.422 -.395 L -.522 -.168 L -.239 .607 L h 669.676 172.974 m -.452 -.366 L -.349 -.408 L -.051 -.226 L -.364 -1.114 L -.459 -.521 L .685 -.058 L .868 .012 L .314 .169 L .274 .451 L .028 .861 L -.097 .48 L -.214 .283 L -.185 .438 L h 679.073 175.368 m -.562 -.267 L -.178 -.254 L -.237 -.169 L -.064 -.127 L .139 -.339 L -.091 -.423 L -.55 -.352 L -.662 -.479 L -.147 -.466 L .513 -.1 L 1.017 -.143 L .371 .112 L .283 .409 L .069 .818 L .123 .805 L .257 .79 L -.112 .127 L -.167 .057 L h 671.406 176.824 m .022 -.353 L .186 -1.342 L .061 -1.172 L .482 .395 L .576 .281 L .366 .028 L .634 -.185 L .027 .438 L -.079 .396 L -.24 .269 L -.184 .198 L -.908 .651 L -.943 .397 L h 664.721 177.812 m -.366 -.027 L .127 -.297 L .202 -.099 L .314 -.396 L .265 -.523 L .082 -.607 L .138 -.509 L .326 -.184 L .14 .183 L -.16 .283 L .276 .55 L .212 .564 L -.108 .113 L -.664 .354 L -.406 .368 L -.377 .227 L h 673.781 179.981 m -.385 -.366 L -.828 -.591 L -.206 -.38 L .099 -.368 L .565 -.213 L .22 -.269 L .487 -1.822 L .438 -.185 L .271 .028 L .113 .126 L .049 .282 L -.373 .905 L -.089 .226 L -.315 1.413 L .165 .296 L .214 .465 L -.213 .41 L -.212 .043 L h 661.179 181.308 m -.317 -.042 L .215 -.48 L .343 -.467 L .35 -.34 L .592 -.354 L .636 -.496 L .933 -1.089 L .11 .55 L -.118 .424 L -.267 .297 L -.24 .184 L -.516 .213 L -.172 .127 L -.244 .622 L -.327 .269 L -.591 .171 L -.386 .41 L h 680.678 185.402 m -.201 -.098 L -.098 -.438 L -.361 -.748 L -.297 -.112 L -.381 .608 L -.361 .82 L .246 .38 L .166 .395 L .148 .677 L -.158 .495 L -.383 .509 L -.305 -.253 L -.176 -.268 L .201 -.41 L -.076 -.325 L -.363 -.07 L -.361 .665 L -.173 -.056 L -1.114 -.619 L -.557 -.549 L -.206 -.508 L -.037 -.635 L .466 -.905 L -.108 -.479 L -.466 -.409 L -.778 -.295 L -.292 .043 L -.062 .17 L -.129 .226 L -.5 .255 L -.661 .058 L -.027 -.494 L -.174 -.24 L -.399 .199 L -.504 .961 L -.525 .863 L -.219 -.084 L -.119 -.127 L -.034 -.197 L .054 -.311 L .359 -.749 L .185 -.537 L .263 -.283 L .383 -.184 L .564 -.044 L .219 -.127 L .134 -.466 L .205 -.156 L .549 -.241 L .777 -.101 L .292 .353 L .102 .72 L .317 -.156 L .485 -.058 L .207 -.184 L .207 -.565 L .358 -.255 L .479 .21 L .186 .084 L .158 -1.087 L .29 -.015 L .264 .465 L .315 -.481 L .205 -.142 L .392 .098 L .133 -.227 L -.048 -.706 L -.172 -.564 L .146 -.353 L .413 .549 L .711 .803 L .229 .48 L .083 .324 L -.336 .735 L .237 .226 L .537 -.1 L .076 .423 L -.114 .424 L .056 .692 L .207 .437 L -.002 .438 L -.111 .424 L -.283 .579 L -.332 .622 L
+251.898 160.229 N -.547 -.112 L -.073 -.212 L .051 -.551 L -.109 -.24 L .11 -.17 L .235 -.071 L .543 .069 L .404 -.015 L 1.505 .11 L .393 .168 L .113 .141 L -.188 .354 L -.07 .099 L -.283 .227 L -.335 .043 L -.739 -.083 L -.657 .072 L -.354 .17 L
+228.82 160.519 N -.299 -.056 L -.693 -.224 L -.229 -.268 L -.47 -.366 L -.465 -.084 L -.142 -.211 L .53 -.284 L .704 -.072 L 1.364 .251 L .97 .351 L .651 .267 L .279 .282 L -.04 .198 L -.332 .071 L -.751 -.295 L -.543 .058 L -.227 .255 L -.308 .128 L
+400.72 175.499 N -.595 -.119 L -.161 -.032 L -.976 .458 L -1.429 -.006 L -.867 -.037 L -2.119 .041 L -.271 .157 L -.125 .34 L .261 1.934 L .02 .41 L -.191 .17 L -.63 -.434 L -.644 -.166 L -.769 .075 L -.608 .159 L -.446 .257 L -.368 .115 L -.354 -.083 L -.452 -.223 L -.52 -.562 L -.15 -.465 L -.308 -.252 L -.545 .003 L -.259 -.168 L .08 -.043 L .046 -.156 L .172 -.326 L .261 -.92 L .133 -.876 L -.058 -.749 L .141 -.255 L .257 -.016 L .448 -.158 L .543 -.271 L .317 -.369 L .292 -1.047 L .355 -1.217 L .397 -.384 L .273 -.058 L .293 .281 L .551 .364 L .45 -.102 L .174 -.227 L .393 -.822 L .492 -.667 L .638 -.625 L .272 -.101 L .518 .209 L .324 .182 L .274 .027 L .314 -.793 L .304 -.228 L .947 -.458 L 1.22 -.713 L .37 -.073 L .356 .125 L .36 .059 L 1.062 .173 L -.173 .665 L .019 .396 L .426 .93 L .422 .492 L .389 .266 L .502 .42 L .179 .268 L .018 .212 L -.335 .439 L .114 .226 L .97 .757 L .516 .181 L .37 -.044 L .562 .025 L .568 .971 L .131 .367 L -.237 .764 L -.415 .468 L -.337 .158 L -.739 -.095 L -.418 .045 L -.415 .271 L -.366 .553 L -.24 .157 L -.062 .142 L -.29 -.041 L -.611 -.166 L -1.013 -.163 L
+209.823 175.47 N -.388 -.645 L -.932 -.888 L -1.003 -1.085 L -.837 -.817 L -.723 -.464 L -.196 -.183 L -.023 -.226 L .625 -.03 L 1.001 -.125 L .29 -.143 L .293 -.412 L .005 -.961 L .882 -.034 L .513 -.583 L .725 .42 L 1.207 -.997 L .503 -.794 L .294 -.242 L .259 .013 L .328 .182 L .463 .097 L .248 -.086 L .424 -.229 L 1.425 -.486 L .23 .519 L .038 .339 L -.057 .509 L -.214 .707 L -.543 .806 L -.1 .749 L .042 .904 L -.245 1.13 L -.188 .735 L -.101 1.385 L .031 .55 L .184 .466 L .188 .363 L -.281 .274 L -.767 -.149 L -.241 -.46 L -.285 .077 L -.394 -.107 L -.603 .25 L -1.651 -.599 L -.43 .271 L
+634.036 168.444 N .533 .251 L .736 .165 L .341 .111 L .469 .251 L .248 -.058 L .095 -.24 L -.103 -.211 L -.187 -.182 L -.107 -.296 L .392 -.285 L .476 -.186 L .444 .181 L .373 .351 L .3 -.072 L .975 -.429 L .209 -.114 L -.215 .905 L .029 .663 L .455 1.014 L -.076 .367 L -.05 .424 L .054 1.073 L -.217 .509 L -.385 .285 L -.599 .187 L -.932 .443 L -.521 .229 L -.99 .571 L -.234 .425 L .216 .422 L .568 .435 L .03 .197 L -.17 .27 L -.688 -.194 L -.497 -.011 L -.599 .229 L -.915 .528 L -.646 .229 L -.3 .289 L -.256 -.188 L -.248 -.268 L -.35 -.042 L -.382 .142 L -.205 -.042 L -.293 .043 L -.183 -.113 L .142 -.311 L .182 -.226 L -.04 -.254 L -.283 -.395 L -.277 .043 L -.462 .298 L -.339 .015 L -.171 -1.044 L -.649 -1.488 L .146 -.176 L -.16 -.479 L -.487 -.717 L -.219 -.648 L -.026 -.635 L .076 -.382 L .146 -.297 L .92 -1.233 L .521 -.441 L .383 -.101 L 1.172 -.091 L .798 .066 L .558 -.074 L .575 .039 L .599 .109 L .301 .167 L
+214.474 175.913 N .821 .884 L .385 .623 L .314 .322 L .225 .046 L .465 .645 L .441 .352 L -.014 .006 L -.074 .123 L -.478 -.184 L -.219 .205 L -.014 .321 L 0 .58 L .507 .307 L -.396 .368 L .125 .532 L -.374 .369 L .243 .184 L -.204 .609 L .003 -.466 L -.296 -.307 L .01 -.568 L -.377 -.148 L -.238 -.102 L -.111 .082 L .325 .41 L .084 .225 L -.113 .062 L -.726 -.299 L -.13 -.282 L .251 -.339 L .04 -.382 L -.182 -.338 L -.486 -.324 L -.695 -.287 L -.079 -.144 L -.689 -.103 L -.316 -.327 L .007 -.421 L -.146 -.255 L -.249 -.098 L -.576 -.353 L -.416 -.266 L .225 .512 L .086 .222 L .422 .044 L .181 .266 L -.544 .573 L -.144 -.262 L -.356 -.282 L -.561 -.211 L -.323 -.239 L -.147 -.24 L -.104 -.494 L .128 -.421 L .332 -.225 L -.008 -.389 L -.554 -.225 L .363 -.123 L -.057 -.227 L -.238 .017 L .43 -.271 L 1.651 .599 L .603 -.25 L .394 .107 L .285 -.077 L .241 .46 L .767 .149 L .281 -.274 L
+436.304 195.359 N -.209 -.451 L -.194 -.804 L -.498 -.802 L -1.217 -1.236 L -.112 -.324 L -.064 -.791 L -.432 -.605 L -.4 -.661 L -.207 -.592 L -.273 -1.185 L -.112 -.776 L .064 -.424 L .144 -.198 L .528 -.399 L .4 -.511 L .866 -1.743 L .354 -.327 L .208 -.114 L .096 .084 L .24 .027 L .449 -.158 L 1.363 -.686 L .494 .661 L .225 .069 L .224 -.03 L .466 -.271 L .754 -.386 L .818 -.273 L .881 .009 L .368 -.031 L .258 -.143 L .646 -1.176 L .117 -.806 L .209 -.199 L .434 -.059 L 2.181 -.055 L .386 -.087 L 2.649 -2.275 L .196 -.284 L .005 -.41 L .165 -.382 L .372 -.228 L .939 -.613 L .322 -.086 L .321 .012 L .559 .208 L 1.342 1.673 L .347 .549 L .122 .536 L -.416 1.472 L .01 .664 L .158 .211 L .802 -.019 L .272 -.001 L .207 .126 L .252 .493 L .223 .225 L .797 .447 L .799 .235 L .351 .196 L .223 .267 L -.148 .481 L .173 .395 L .445 .351 L .558 .378 L .717 .434 L .207 .168 L .206 .366 L .059 .72 L .285 .436 L .367 .224 L .814 .165 L .558 .477 L .157 .352 L -.083 .679 L .031 .07 L -.496 .145 L -.962 .344 L -.319 -.026 L -.287 -.167 L -.687 -.264 L -1.15 -.361 L -.4 .13 L -.082 .551 L -.13 .241 L -.256 .058 L -.399 -.026 L -.862 -.207 L -.593 .102 L -.93 .373 L -.93 .486 L -.271 .016 L -.559 -.35 L -.383 -.153 L -.353 .186 L -.677 1.36 L -.176 .157 L -.591 -.067 L -1.934 -.511 L -1.422 -.189 L -.512 -.322 L -.159 -.239 L -.334 -.394 L -.75 -.518 L -.432 -.167 L -.479 .06 L -.529 .3 L -.353 .341 L -.785 .979 L -.097 .297 L .096 .452 L -.017 .353 L -.063 .594 L -.16 .058 L -.751 .287 L -.895 -.093 L -.624 -.138 L -.367 -.054 L -.975 .175 L -.479 .102 L -.255 .228 L -.222 1.287 L -.158 .523 L -.365 .497 L -.303 .312 L
+371.324 180.419 N 1.088 -1.235 L .643 -.866 L .238 -.157 L .594 .039 L 1.137 -.148 L .466 .04 L .42 .224 L .456 .421 L .475 .619 L .252 .761 L .165 1.369 L .26 .656 L -.259 .502 L -1.184 1.151 L -.63 .724 L -.285 .256 L -.061 .103 L -.631 -.523 L -.661 -.408 L -.483 -.196 L -.033 -.141 L .061 -.297 L -.05 -.184 L -.531 -.21 L -.792 -.549 L -.389 -.366 L -.375 -.465 L .494 -.241 L .174 -.227 L -.034 -.155 L -.484 -.211 L -.065 -.113 L .022 -.173 L
+579.606 186.906 N -.493 -.083 L -.265 -.197 L -.21 -.353 L -.246 -.621 L -.361 -1.256 L -.034 -.649 L .005 -.763 L -.02 -.904 L -.24 -.696 L .188 -.513 L -.298 -.35 L .068 -.28 L .423 .023 L .349 -.43 L .053 -.367 L .226 -.369 L -.152 -.537 L .529 .48 L .699 .38 L .234 .395 L .549 .776 L .175 .324 L -.099 .339 L .024 .141 L .592 .338 L .266 .733 L .798 1.368 L .136 .508 L -.193 .735 L -.397 .679 L -.369 .396 L -.514 .425 L -.78 .284 L -.642 .043 L
+217.111 178.792 N .52 .307 L .195 .512 L .02 .374 L .363 .155 L .628 .024 L .244 -.205 L .398 .43 L .726 .082 L .458 -.083 L 1.348 -.751 L .514 -.046 L 1.387 -.921 L .373 .144 L .742 .069 L .071 .156 L .789 -.017 L .767 .21 L .666 .38 L .644 .563 L .406 .666 L .084 .327 L .228 .149 L .509 1.038 L -.322 .062 L -.094 .43 L -.584 .409 L -.085 -.307 L -.19 -.082 L .045 .45 L -.228 .082 L -.256 .753 L -.378 -.825 L -.441 -.762 L -.137 -.452 L .179 -.24 L .22 -.085 L .786 .125 L -.336 -.193 L -.125 -.164 L -.096 -.471 L -.309 .307 L -.439 .041 L -.244 -.378 L -.031 -.269 L -.193 -.282 L -.132 .151 L -.226 -.287 L -.11 .102 L -.132 -.266 L -.456 -.192 L -.562 -.013 L -.499 .241 L -.382 .108 L -.07 .359 L .081 .234 L -.529 .318 L -.374 .184 L -.335 .029 L -.345 .41 L .049 .296 L .316 .297 L .464 .43 L .178 .386 L -.011 .146 L -.281 .081 L -.243 -.042 L -.431 .391 L -.568 .105 L -.339 -.042 L -.189 -.146 L .108 -.164 L -.349 -.833 L -.244 -.353 L -.177 .674 L -.812 -.409 L -.227 -.757 L -.207 .041 L -.96 -.123 L -.434 -.266 L -.599 0 L -.314 .113 L -.361 .495 L .204 -.609 L -.243 -.184 L .374 -.369 L -.125 -.532 L .396 -.368 L -.507 -.307 L 0 -.58 L .014 -.321 L .219 -.205 L .478 .184 L .074 -.123 L
+266.015 188.956 N -.503 -.647 L -.732 -.745 L -.324 -.521 L .071 -.212 L .395 -.539 L -.008 -.58 L .061 -.452 L 1.032 -.19 L .41 -.144 L .308 -.299 L -.141 -.282 L -.62 -.604 L -.074 -.212 L .101 -.255 L 1.655 -1.239 L .061 -.41 L -.095 -.296 L .072 .049 L .496 .338 L .856 .521 L .695 .521 L .587 .663 L .128 .409 L -.036 .734 L .148 .945 L .298 -.593 L .22 -.099 L .6 .182 L .101 .127 L .507 .295 L .708 .549 L .401 .493 L .374 .649 L -.015 .339 L -.142 .496 L -.082 .862 L -.183 .27 L -1.131 .091 L -.169 .17 L .078 .777 L -.095 .467 L -.328 .694 L .571 .831 L .532 .675 L .561 .067 L .185 .295 L .246 .832 L .49 1.127 L .332 .563 L -.06 .212 L -.943 -.022 L -.934 .429 L -1 .331 L -.505 .314 L -.694 .513 L -.686 .075 L -.613 -.392 L -1.103 -.897 L -.15 -.296 L -.008 -.396 L .072 -.354 L -.167 -.31 L -.281 -.394 L -.156 -.465 L .213 -.962 L .547 -1.416 L .179 -.368 L -.435 -.958 L -.533 -.138 L -.304 .001 L .452 -1.09 L -.04 -.212 L -.26 -.111 L -.516 -.124 L -.302 .058 L -.375 .257 L
+377.518 182.142 N .193 -.376 L .252 -.242 L .367 -.143 L .528 -.046 L .338 .097 L .165 .366 L .22 .846 L .009 .65 L -.044 .283 L .277 .323 L .404 .322 L .321 .026 L .756 -.922 L .143 -.086 L .337 .097 L .339 .224 L -.062 .17 L .119 .55 L -.059 .438 L -.501 .865 L .05 .183 L .194 .183 L .369 .097 L .592 -.032 L 1.264 1.334 L .131 .282 L -.059 .452 L -.247 .849 L -.105 .565 L -.041 .919 L -1.513 -.643 L -1.221 -.619 L -1.012 -.562 L -.403 -.423 L -1.129 -1.113 L -1.111 -.76 L -.723 -.337 L -.901 -.535 L -.66 -.548 L .061 -.103 L .285 -.256 L .63 -.724 L 1.184 -1.151 L .259 -.502 L
+429.505 210.684 N .484 .336 L .177 .013 L .443 -.271 L .327 -.581 L 1.495 -.532 L .054 .424 L .042 .664 L .147 .211 L .57 -.328 L .554 -.399 L .931 -.811 L .364 -.229 L 1.025 -.938 L .086 -.706 L -.122 -.72 L .074 -.396 L .193 -1.159 L .343 -.751 L .47 -.836 L .41 -.454 L .809 -.599 L .525 -.229 L .459 -.427 L .139 -.523 L .169 -.708 L .115 -.61 L .254 -1.342 L .196 -2.021 L .156 -.764 L .094 -.551 L .349 -.821 L .558 -.837 L .398 -1.203 L .031 -.156 L -.128 -.197 L .16 -.058 L .063 -.594 L .017 -.353 L -.096 -.452 L .097 -.297 L .785 -.979 L .353 -.341 L .529 -.3 L .479 -.06 L .432 .167 L .75 .518 L .334 .394 L .159 .239 L .512 .322 L 1.422 .189 L 1.934 .511 L .591 .067 L .176 -.157 L .677 -1.36 L .353 -.186 L .383 .153 L .559 .35 L .271 -.016 L .93 -.486 L .93 -.373 L .593 -.102 L .862 .207 L .399 .026 L .256 -.058 L .13 -.241 L .082 -.551 L .4 -.13 L 1.15 .361 L .687 .264 L .287 .167 L .319 .026 L .962 -.344 L .496 -.145 L .143 .239 L .795 .772 L .348 .493 L .525 .477 L .366 .195 L .352 -.016 L .258 -.242 L .529 -.37 L .384 .012 L .684 .631 L .16 -.1 L .436 -.582 L .258 -.157 L .207 .083 L 1.032 .926 L 1.288 1.32 L .063 .028 L .159 .183 L -.018 .424 L -.26 1.88 L .128 .253 L .191 .027 L .479 .04 L .271 .182 L .111 .31 L -.191 .453 L -1.195 1.066 L -1.241 .996 L -.255 .284 L -.395 .681 L -.217 1.02 L -.107 .507 L .021 .593 L -.025 .734 L .057 .748 L -.076 .27 L -.188 .298 L -.426 .412 L -.301 .171 L -.269 .256 L -.122 .425 L -.49 1.358 L .197 .338 L .689 .999 L .087 .381 L -.04 .438 L .014 .636 L .237 .634 L .017 1.329 L -.064 .762 L .425 1.439 L -.102 .848 L .122 .693 L .486 .631 L .936 .898 L .428 .78 L .689 1.804 L -.563 .068 L -3.015 .499 L -.347 .214 L -.154 .198 L -.797 1.276 L -.029 .622 L .372 2.088 L -.377 1.175 L -.3 1.02 L .043 .296 L .456 .605 L .837 .843 L .445 .279 L .515 .012 L .448 -.455 L .362 -.186 L .136 .183 L .032 .791 L .028 1.427 L -.041 .551 L -.371 -.012 L -1.355 -.091 L -.348 -.21 L -.381 -.647 L -.561 -.731 L -.416 -.351 L -.477 -.252 L -.895 -.263 L -.38 -.28 L -.728 -1.423 L -.212 .354 L -.522 .682 L -.366 .087 L -.325 -.111 L -.922 -.164 L -.925 -.249 L -.489 -.194 L -.58 -1.014 L -.175 .071 L -1.146 .289 L -.195 -.056 L -.172 -.352 L -.365 -.379 L -.678 -.096 L -1.24 -.147 L -.991 .146 L -.859 .26 L -.61 .004 L -.199 -.197 L -.007 -.254 L .198 -.905 L .003 -.466 L -.306 -.62 L -.844 -.998 L -.117 -.211 L -.021 -.212 L .296 -1.033 L .07 -1.06 L -.109 -.607 L -.303 -.605 L .057 -.354 L .177 -.693 L -.052 -.183 L -.322 -.098 L -1.231 .093 L -.88 .019 L -.212 -.168 L .055 -.48 L -.181 -.225 L -2.016 .082 L -.112 .001 L -.089 .354 L .027 .593 L -.286 .892 L -.184 .411 L -.993 .006 L -.899 -.122 L -1.021 .246 L -.88 .034 L -.292 -.168 L -.64 -.787 L -.597 -1.07 L -.537 -1.409 L -.059 -.579 L -.121 -.521 L -.082 -.127 L -.321 -.111 L -.385 -.012 L -1.104 -.008 L -.88 .034 L -.624 .003 L -1.312 -.048 L -.945 -.052 L -.783 .02 L -.432 .03 L -.318 .101 L -.896 .062 L -.699 .314 L -.56 .06 L -.112 -.013 L -.322 -.922 L .012 -.053 L .74 -.3 L -.017 -.085 L .1 -.89 L .094 -.143 L .317 -.199 L .583 -.568 L
+468.568 202.653 N 1.277 -.05 L 4.659 0 L 1.448 -.033 L 4.663 2.568 L 3.553 1.984 L .137 .381 L -.116 .552 L .07 .239 L 2.427 1.752 L 1.067 .807 L -.237 .427 L -.419 1.329 L -.137 1.017 L .109 .551 L .665 .987 L .768 .577 L -.024 .282 L -.263 .354 L -.371 1.046 L .037 .17 L .306 .041 L .118 .226 L -.172 .692 L -.019 .946 L .411 1.793 L .18 .564 L .401 .465 L .823 .55 L .396 .268 L .177 .228 L -1.157 .836 L -.515 .342 L -1.218 .402 L -1.243 .559 L -.448 .031 L -.646 -.42 L -.276 -.083 L -.21 .326 L -.543 .766 L -.396 .144 L -.42 -.054 L -.964 .006 L -.963 .02 L -.512 -.294 L -.323 -.394 L -.229 -.083 L -.123 .128 L -.643 .356 L -.526 .088 L -1.019 -.149 L -.898 -.018 L -.196 -.559 L -.135 -.766 L -.053 -.584 L -.135 -.946 L -.448 -.841 L -.663 -.538 L -1.014 -.107 L -.223 .016 L -.385 -.407 L -.732 -.349 L -1.574 -.57 L -1.269 -.6 L -.729 -.265 L -.263 -.21 L -.703 -.641 L -.689 -1.804 L -.428 -.78 L -.936 -.898 L -.486 -.631 L -.122 -.693 L .102 -.848 L -.425 -1.439 L .064 -.762 L 1.252 -.348 L .388 -.539 L .645 -1.204 L .687 -.853 L .346 -.271 L .044 -.212 L -.148 -.239 L -.273 -.125 L -.401 -.068 L -.211 -.211 L .158 -.976 L .304 -.016 L .396 -.158 L .142 -.17 L .104 -.481 L -.167 -.493 L -.364 -.888 L -.121 -.748 L
+464.786 206.235 N -.197 -.338 L .49 -1.358 L .122 -.425 L .269 -.256 L .301 -.171 L .426 -.412 L .064 .042 L .546 .223 L .431 -.045 L .458 -.427 L .554 -.398 L .319 -.017 L .121 .748 L .364 .888 L .167 .493 L -.104 .481 L -.142 .17 L -.396 .158 L -.304 .016 L -.559 .031 L -.508 .159 L -.391 .567 L -.158 .085 L -.396 .13 L -.789 -.32 L -.688 -.024 L
+465.79 210.652 N -.017 -1.329 L -.237 -.634 L -.014 -.636 L .04 -.438 L -.087 -.381 L -.689 -.999 L .688 .024 L .789 .32 L .396 -.13 L .158 -.085 L .391 -.567 L .508 -.159 L .559 -.031 L -.158 .976 L .211 .211 L .401 .068 L .273 .125 L .148 .239 L -.044 .212 L -.346 .271 L -.687 .853 L -.645 1.204 L -.388 .539 L -1.252 .348 L
+427.243 211.207 N .536 -.414 L .68 -.513 L .351 -.13 L .695 .533 L -.583 .568 L -.317 .199 L -.094 .143 L -.1 .89 L .017 .085 L -.74 .3 L .143 -.625 L -.082 -.24 L -.505 -.796 L h 427.998 213.843 m .112 .013 L .56 -.06 L .699 -.314 L .896 -.062 L .318 -.101 L .432 -.03 L .783 -.02 L .945 .052 L 1.312 .048 L .624 -.003 L .88 -.034 L 1.104 .008 L .385 .012 L .321 .111 L .082 .127 L .121 .521 L .059 .579 L .537 1.409 L .597 1.07 L .64 .787 L .292 .168 L .88 -.034 L 1.021 -.246 L .899 .122 L .993 -.006 L .184 -.411 L .286 -.892 L -.027 -.593 L .089 -.354 L .112 -.001 L 2.016 -.082 L .181 .225 L -.055 .48 L .212 .168 L .88 -.019 L 1.231 -.093 L .322 .098 L .052 .183 L -.177 .693 L -.057 .354 L .303 .605 L .109 .607 L -.07 1.06 L -.296 1.033 L .021 .212 L .117 .211 L .844 .998 L .306 .62 L -.003 .466 L -.198 .905 L .007 .254 L .199 .197 L .61 -.004 L .859 -.26 L .991 -.146 L 1.24 .147 L -.214 1.344 L .195 1.059 L .269 .323 L .089 .254 L -.132 .368 L -.436 .44 L -.124 .594 L -.125 .086 L -.517 -.04 L -3.408 .091 L -.051 .877 L .015 1.342 L -.024 3.249 L .017 .848 L .023 .594 L .099 .438 L .308 .394 L .471 .436 L 1.354 1.391 L .611 .632 L -.93 .218 L -1.96 .379 L -1.044 .203 L -.717 -.08 L -1.164 .063 L -.408 -.083 L -.349 -.21 L -2.024 .026 L -.697 -.024 L -.622 -.151 L -.401 -.322 L -.305 -.366 L -.408 -.096 L -.989 -.023 L -2.59 .016 L -1.636 -.019 L -.631 -.011 L -1.296 -.006 L -2.201 .013 L -.636 -.151 L -.463 -.309 L -.45 -.478 L -.294 -.083 L -.499 .088 L -.591 .286 L -.778 .513 L -.758 -.462 L -.352 .144 L -.248 .197 L .048 -1.809 L -.017 -.805 L -.029 -.649 L .397 -.34 L .221 -.269 L .26 -.707 L .163 -.734 L .184 -1.398 L .239 -.976 L .321 -.918 L .584 -.665 L .183 -.565 L .268 -.354 L .64 -.228 L .268 -.325 L .423 -.679 L .364 -1.201 L .053 -.664 L -.046 -.848 L -.191 -.959 L -.201 -.536 L -.492 -.705 L -.476 -.776 L -.268 -.775 L .139 -.495 L .476 -.382 L .158 -.156 L .107 -.424 L -.006 -.479 L -.199 -.579 L -.489 -.69 L -.441 -.733 L -.203 -1.031 L -.181 -.423 L -.276 -.366 L -.666 -.974 L -.072 -.15 L
+452.198 239.34 N -.611 -.632 L -1.354 -1.391 L -.471 -.436 L -.308 -.394 L -.099 -.438 L -.023 -.594 L -.017 -.848 L .024 -3.249 L -.015 -1.342 L .051 -.877 L 3.408 -.091 L .517 .04 L .125 -.086 L .124 -.594 L .436 -.44 L .132 -.368 L -.089 -.254 L -.269 -.323 L -.195 -1.059 L .214 -1.344 L .678 .096 L .365 .379 L .172 .352 L .195 .056 L 1.146 -.289 L .175 -.071 L .58 1.014 L .489 .194 L .925 .249 L .922 .164 L .325 .111 L .366 -.087 L .522 -.682 L .212 -.354 L .728 1.423 L .38 .28 L .895 .263 L .477 .252 L .416 .351 L .561 .731 L .381 .647 L .348 .21 L 1.355 .091 L .371 .012 L .041 -.551 L -.028 -1.427 L -.032 -.791 L -.136 -.183 L -.362 .186 L -.448 .455 L -.515 -.012 L -.445 -.279 L -.837 -.843 L -.456 -.605 L -.043 -.296 L .3 -1.02 L .377 -1.175 L -.372 -2.088 L .029 -.622 L .797 -1.276 L .154 -.198 L .347 -.214 L 3.015 -.499 L .563 -.068 L .703 .641 L .263 .21 L .729 .265 L 1.269 .6 L 1.574 .57 L .732 .349 L .385 .407 L .559 .887 L .434 .859 L .013 .324 L -.183 .27 L -.709 .344 L -.011 .127 L .04 .594 L -.091 1.682 L .08 .353 L .216 .168 L .511 .266 L .072 .197 L -.197 .241 L -.472 .258 L -.792 .259 L -.146 .34 L -.094 .764 L -.3 .807 L .2 .479 L .261 .408 L .394 .125 L -1.264 .53 L -1.696 .575 L -.932 .4 L -2.165 .578 L -.187 .128 L -.009 .495 L .152 .465 L .087 .438 L -.505 -.096 L -.999 .049 L -.794 .259 L -.636 .54 L -.312 .539 L -.019 .579 L -.168 .199 L -.285 .114 L -.999 .062 L -.621 .202 L -.306 .341 L -.777 .937 L -.562 .738 L -.825 .951 L -.354 .045 L -.803 -.165 L -.421 -.309 L -.334 .129 L -.521 .286 L -.404 .017 L -.594 -.209 L -.264 -.097 L -.154 -.169 L -.163 -.027 L -.187 -.154 L -.456 -.393 L -.294 -.055 L -1.089 -.093 L -.086 -.099 L -.165 -.056 L -1.845 .364 L
+477.231 225.874 N -.224 1.184 L -.107 .48 L .252 .917 L .177 1.017 L .149 .408 L .238 .268 L .803 .588 L 1.189 1.166 L .454 .661 L .14 .48 L -.155 2.346 L -.04 .41 L -.214 .213 L -.432 .073 L -.322 .017 L -.229 .213 L -.076 .622 L .08 .509 L .029 .479 L -.096 .283 L -.185 -.111 L -.562 -.463 L -.763 -1.112 L -.484 -.548 L -.234 -.423 L .036 -.212 L .499 -.61 L .116 -.227 L .025 -.693 L -.1 -.96 L -.22 -.479 L -.261 -.056 L -.674 .061 L -.702 .132 L -.27 -.211 L -.343 -.407 L -.382 -.549 L -.195 -.041 L -.394 -.125 L -.261 -.408 L -.2 -.479 L .3 -.807 L .094 -.764 L .146 -.34 L .792 -.259 L .472 -.258 L .197 -.241 L -.072 -.197 L -.511 -.266 L -.216 -.168 L -.08 -.353 L .091 -1.682 L -.04 -.594 L .011 -.127 L .709 -.344 L .183 -.27 L -.013 -.324 L -.434 -.859 L -.559 -.887 L .223 -.016 L 1.014 .107 L .663 .538 L .448 .841 L .135 .946 L .053 .584 L .135 .766 L .196 .559 L
+245.934 224.314 N .939 .136 L 1.122 .304 L .355 -.03 L .946 -.824 L .336 .026 L .48 .025 L .415 -.243 L 1.471 -1.109 L .874 -.485 L .36 -.158 L .934 -.076 L 1.283 .021 L .045 .748 L -.079 .621 L -.064 .622 L .036 .818 L .141 .635 L .335 .591 L .813 .928 L 1.1 .939 L .316 .097 L .787 .023 L .355 -.03 L .676 .25 L .688 .307 L .75 .603 L .3 .098 L .882 .037 L .096 .014 L .385 .774 L .398 .308 L .22 .084 L 1.148 -.077 L .636 .123 L .537 .166 L .403 .237 L .085 .169 L -.038 .565 L .203 1.029 L .03 .706 L .138 2.032 L .249 .944 L .153 .112 L .967 .036 L .5 .012 L 1.615 .019 L .693 .024 L .042 .296 L -.261 .835 L .067 .563 L .436 .407 L .73 .362 L .316 .479 L .307 .774 L .022 .494 L -.185 1.173 L -.238 .834 L -.38 .765 L -.421 .666 L -.089 -.084 L -1.952 -.991 L -.352 -.054 L -.928 .02 L -.843 -.01 L -.126 .128 L -1.076 .204 L -1.104 .162 L -.784 .202 L -.33 .044 L -.332 .383 L -.698 1.105 L -.278 .341 L -.133 .509 L .016 .635 L -.385 1.188 L -.395 1.104 L -.149 .325 L -.592 -.109 L -1.33 -.077 L -.686 .004 L -1.034 1.784 L -.416 -1.084 L -.341 -.309 L -.37 -.195 L -.531 -.067 L -.527 .045 L -.901 .034 L -.615 -.194 L -.193 -.169 L -.322 -.181 L -.292 .27 L -2.026 2.087 L -1.047 .006 L -.272 -.182 L -.397 -2.144 L -.278 -.973 L -.212 -.563 L -.769 -1.11 L -.249 -.676 L .04 -.354 L .437 -1.555 L -.017 -.282 L -.761 -.744 L -.25 -.521 L -.193 -1.213 L -.304 -.647 L -.555 -.745 L -.152 -.253 L -.018 -.142 L -.132 -.295 L -.049 -.48 L .12 -.227 L .723 -.61 L .285 -.439 L -.015 -.522 L -.604 -1.168 L -.022 -.48 L .159 -.34 L .21 -.368 L -.347 -.845 L .102 -.452 L .532 -.582 L .221 -.34 L .156 -.34 L -.236 -.902 L -.057 -.522 L .143 -.848 L .15 -.523 L .437 -.736 L -.08 -.24 L -.922 -1.646 L -1.109 -1.843 L
+473.575 260.04 N -1.331 .011 L -.192 .058 L -.068 -.382 L -.261 -.889 L .071 -.495 L -.075 -.296 L -.095 -.324 L .03 -.806 L .057 -1.301 L -.072 -.763 L -.147 -.678 L -.33 -.944 L -.441 -.689 L -.181 -.946 L -.295 -1.199 L -.159 -.183 L .448 -.384 L .396 -.412 L 1.68 -1.706 L .114 -.227 L -.09 -.367 L .075 -.834 L .229 -.481 L .736 -.683 L .205 -.341 L .168 -.41 L -.594 -.845 L -.118 -.805 L -.113 -.494 L .128 -.283 L .448 -.596 L .201 -.411 L -.132 -.805 L -.086 -1.144 L -.031 -.791 L -.178 -.818 L -.441 -.379 L -.515 -.224 L -1.167 -.347 L -1.042 -.445 L -.658 -.223 L -1.438 -.006 L -.137 -.14 L -.025 -.495 L -.011 -.212 L -.087 -.438 L -.152 -.465 L .009 -.495 L .187 -.128 L 2.165 -.578 L .932 -.4 L 1.696 -.575 L 1.264 -.53 L .195 .041 L .382 .549 L .343 .407 L .27 .211 L .702 -.132 L .674 -.061 L .261 .056 L .22 .479 L .1 .96 L -.025 .693 L -.116 .227 L -.499 .61 L -.036 .212 L .234 .423 L .484 .548 L .763 1.112 L .562 .463 L .185 .111 L .096 -.283 L -.029 -.479 L -.08 -.509 L .076 -.622 L .229 -.213 L .322 -.017 L .432 -.073 L .214 -.213 L .04 -.41 L .155 -2.346 L -.14 -.48 L -.454 -.661 L -1.189 -1.166 L -.803 -.588 L -.238 -.268 L -.149 -.408 L -.177 -1.017 L -.252 -.917 L .107 -.48 L .224 -1.184 L .898 .018 L 1.019 .149 L .526 -.088 L .643 -.356 L .123 -.128 L .229 .083 L .323 .394 L .512 .294 L .963 -.02 L .964 -.006 L .42 .054 L .396 -.144 L .543 -.766 L .21 -.326 L .276 .083 L .646 .42 L .448 -.031 L 1.243 -.559 L 1.218 -.402 L .515 -.342 L 1.157 -.836 L .128 .167 L .212 .479 L -.185 .579 L -.302 .453 L -.198 .255 L .181 .451 L .129 .72 L -.012 .466 L .182 1.115 L -.101 .58 L -.258 .325 L .374 .705 L .154 .494 L -.006 1.115 L -.004 .819 L .043 .184 L .185 .127 L .327 .084 L .015 .269 L -.165 .494 L -.563 .58 L .184 .381 L -.08 .283 L -.418 .565 L -.802 .906 L -.512 .622 L -.72 .651 L -1.36 .751 L -1.48 .653 L -.73 .228 L -1.308 .582 L -.852 .637 L -1.286 1.443 L -.886 .85 L -1.193 .878 L -1.181 .836 L -.268 .128 L -.035 .96 L -.083 .495 L .058 .127 L .719 .535 L .188 .381 L -.166 .452 L -.085 .184 L .461 1.511 L .071 .564 L .06 .155 L .246 .014 L .171 -.128 L .141 -.085 L .043 .607 L -.234 2.218 L -.284 .82 L .325 .196 L .152 .057 L -.025 .325 L -.157 .311 L -.516 .566 L -.699 .538 L -.664 .34 L -1.266 .412 L -.796 .312 L -.688 .228 L -.895 .524 L -.652 .665 L -.337 .51 L .292 .338 L .589 .338 L .045 .325 L -.149 1.022 L
+499.85 236.166 N .544 -.071 L .622 -.369 L .18 .028 L .346 .098 L .269 -.085 L .396 -.368 L .911 -.143 L .311 .281 L .305 -.028 L .101 -.185 L -.171 -.366 L .771 -.539 L .423 -.198 L .322 .226 L .389 -.213 L -.308 -.494 L .207 -.282 L .505 -.425 L .229 .296 L .229 .056 L .558 -.594 L -.158 -.197 L -.253 -.409 L .094 -.297 L .243 .014 L .27 -.071 L .172 -.34 L -.297 -.875 L .06 -.339 L .255 -.043 L .117 .07 L .253 .438 L .28 .099 L .2 -.41 L .692 -.524 L .235 -.367 L .134 -.452 L .168 -.692 L -.133 -.354 L .003 -.226 L .537 -.468 L .356 .324 L .455 .648 L .612 .281 L .141 .198 L .213 .847 L .294 1.821 L .093 .663 L .231 .791 L .391 .733 L .163 .466 L -.038 .367 L -.069 .155 L -.058 .099 L -.537 .807 L -.22 -.127 L -.189 -.366 L -.555 -.748 L -.297 .143 L -.05 .424 L .193 .875 L .396 .521 L .079 .396 L -.307 .636 L -.746 1.005 L -.045 .452 L .041 .89 L -.18 .946 L -.709 2.12 L -.825 2.572 L -1.254 3.788 L -1.324 4.48 L -.518 1.568 L -.188 .255 L -.508 .637 L -.2 .113 L -1.369 .102 L -.999 .327 L -.474 .468 L -.813 .086 L -.363 -.465 L -.196 -.142 L -.546 -.182 L -.37 .071 L -.269 -.057 L -.863 -.718 L -.104 -.24 L -.02 -.565 L -.104 -.239 L -.46 -.366 L -.124 -.282 L .001 -.721 L .345 -1.088 L -.094 -.325 L -.287 -.479 L -.62 -.931 L -.189 -.494 L .075 -.664 L .391 -1.37 L .228 -.213 L .474 -.185 L .768 -1.371 L .686 -1.174 L .104 -.325 L .151 -1.103 L -.11 -.353 L -.278 -.226 L -.354 -.366 L .066 -.184 L .252 -.509 L -.521 -.861 L -.117 -.677 L -.069 -.494 L -.231 -.721 L .024 -.112 L .517 -.693 L .362 -.594 L .163 -.438 L .007 -1.073 L .484 -.016 L
+468.138 234.908 N .011 .212 L .025 .495 L .137 .14 L 1.438 .006 L .658 .223 L 1.042 .445 L 1.167 .347 L .515 .224 L .441 .379 L .178 .818 L .031 .791 L .086 1.144 L .132 .805 L -.201 .411 L -.448 .596 L -.128 .283 L .113 .494 L .118 .805 L .594 .845 L -.168 .41 L -.205 .341 L -.736 .683 L -.229 .481 L -.075 .834 L .09 .367 L -.114 .227 L -1.68 1.706 L -.396 .412 L -.448 .384 L -.342 -.225 L -.547 -.124 L -.442 -.025 L -.529 .145 L -.31 .016 L -.99 -.403 L -.597 -.139 L -.72 -.023 L -.067 -.042 L -.186 -.098 L -.306 -.451 L -.479 -.35 L -.549 -.167 L -.938 -.136 L -.352 -.153 L -.524 -.873 L -.163 -.564 L .032 -.565 L -.127 -.239 L -.78 .019 L -.201 -.098 L -.109 -.211 L .051 -.537 L -.106 -.169 L -.552 -.266 L -.533 -.223 L -.57 -.321 L -.563 -.491 L -.377 -.662 L -.246 -.96 L -.469 -.604 L -.43 -.478 L -.267 -.451 L .103 -.227 L .594 .209 L .404 -.017 L .521 -.286 L .334 -.129 L .421 .309 L .803 .165 L .354 -.045 L .825 -.951 L .562 -.738 L .777 -.937 L .306 -.341 L .621 -.202 L .999 -.062 L .285 -.114 L .168 -.199 L .019 -.579 L .312 -.539 L .636 -.54 L .794 -.259 L .999 -.049 L .505 .096 L
+444.673 255.519 N -.006 3.434 L .031 1.894 L .025 2.246 L -.057 .205 L -.454 .318 L -.545 .302 L -.581 .498 L -.427 .034 L -.581 -.166 L -.745 -.042 L -.892 .048 L -.517 -.039 L -.296 -.212 L -.055 -.528 L -.042 -.345 L -.193 -.222 L -.637 -.348 L -.329 -.127 L -.335 .116 L -.109 .217 L -.317 .416 L -.584 .27 L -.152 .068 L -.458 -.491 L -1.041 -1.001 L -.458 -.606 L -.359 -1.03 L -.345 -.72 L -.136 -.493 L .12 -.269 L .053 -.34 L -.458 -.719 L -.231 -.861 L .148 -.861 L -.002 -.48 L -.537 -1.326 L -.496 -2.244 L .772 -.02 L .04 -.678 L -.077 -.749 L -.456 -.006 L .016 -.06 L -.099 -.522 L -.26 -.508 L -1.018 -1.283 L -.343 -.55 L -1.102 -2.158 L -.841 -1.623 L -.9 -1.509 L -.988 -1.269 L -.511 -1.044 L -.122 -.396 L -.045 -.551 L .015 -.578 L .248 -.197 L .352 -.144 L .758 .462 L .778 -.513 L .591 -.286 L .499 -.088 L .294 .083 L .45 .478 L .463 .309 L .636 .151 L 2.201 -.013 L 1.296 .006 L .631 .011 L 1.636 .019 L 2.59 -.016 L .989 .023 L .408 .096 L .305 .366 L .401 .322 L .622 .151 L .697 .024 L 2.024 -.026 L .349 .21 L .408 .083 L 1.164 -.063 L .717 .08 L 1.044 -.203 L 1.96 -.379 L .93 -.218 L 1.845 -.364 L .165 .056 L .086 .099 L 1.089 .093 L .294 .055 L .456 .393 L -.811 .315 L -.891 .02 L -.284 .143 L -.993 .938 L -.209 .029 L -.62 -.773 L -1.048 .134 L -2.962 .47 L -1.183 .021 L .005 1.215 L -.007 1.286 L -.025 .876 L -.043 1.201 L .002 3.561 L -.586 .046 L -1.564 .052 L -.146 .028 L -.106 2.657 L -.009 1.201 L .013 1.624 L .007 .806 L
+248.453 316.576 N -.306 .101 L -.892 -.087 L -.538 -.293 L -.236 -.015 L -.311 .163 L -.418 .398 L -.498 .192 L -1.156 .091 L -.349 .09 L -.358 .207 L -.267 .621 L -.114 .341 L .06 .532 L -.163 .622 L -.104 .148 L -.453 .031 L -.534 .104 L -.956 -.413 L .667 -.639 L .326 -.444 L .582 -.4 L .025 -.147 L -.372 -.177 L -.273 -.117 L -1.353 .534 L -1.01 -.013 L -.545 .163 L -.202 -.339 L .128 -.192 L .959 -.268 L .266 .028 L .792 -.208 L .441 -.118 L -.605 -.162 L -.582 .002 L -.77 .001 L -.014 -.413 L .265 -.31 L -.007 -.191 L -.446 -.073 L -.356 -.44 L -.66 .384 L -.669 -.175 L .292 -.53 L .041 -.177 L -.378 .045 L -.361 .147 L -.416 -.396 L -.215 -.117 L .413 -.279 L .114 -.177 L -.091 -.278 L -.053 -.073 L -.351 .03 L -.773 -.424 L -.135 -.059 L .844 -.192 L .253 -.161 L .1 -.294 L .396 -.366 L .049 -.234 L -.641 .06 L -.257 .104 L -.312 -.073 L -.256 -.672 L .573 -.395 L -.565 -.378 L -.12 -.421 L .757 -.452 L -.14 -.421 L -.686 .422 L -.091 -1.523 L .399 -.596 L -.185 -.825 L .013 -.218 L .593 .014 L .41 .245 L .711 .071 L .171 -.246 L .002 -.159 L -.896 -.447 L -.867 .146 L -.317 -.173 L -.536 .059 L -.017 -.231 L .339 -.333 L .025 -.246 L -.067 -.087 L .186 -.202 L .536 .014 L .229 -.377 L .01 -.216 L -.722 -.389 L -.354 -.129 L -.886 .045 L -.332 -.101 L -.024 -.49 L -.939 .16 L -.115 -.101 L .122 -.145 L 1.032 -.521 L .251 -.116 L .4 -.404 L .266 -.389 L .833 -.06 L .268 .201 L .059 .346 L -.648 .202 L -.323 .274 L .11 .505 L .117 .058 L .191 -.102 L .268 -.39 L .183 -.087 L .242 .101 L -.037 .317 L .057 .504 L .886 -.996 L .161 -.678 L .056 -.647 L .237 -.375 L .079 -.058 L .631 -.217 L -.201 -.071 L -.438 -.143 L -.056 -.158 L .101 -.273 L .246 -.072 L .571 -.245 L .599 -.431 L .271 -.459 L -.061 -.229 L -.394 -.157 L -.662 -.399 L -.053 -.372 L .139 -.243 L .105 -.458 L -.06 -.828 L .366 -.33 L .676 -.272 L -.431 -.585 L -.053 -.784 L .133 -.158 L .554 -.157 L .054 -.314 L -.116 -.285 L -.317 -.085 L -.272 -.198 L .233 -.329 L .087 -.313 L -.401 -.185 L -.274 -.014 L -.161 .101 L -.476 .414 L -.548 .058 L -.087 .001 L -.289 -.199 L -.16 -.484 L -.399 -.726 L -.133 -.697 L .188 -.911 L .137 -.413 L .722 -.739 L .535 -.767 L -.006 -.326 L -.544 -1.757 L .001 -.608 L .088 -.567 L -.076 -.438 L -.528 -.891 L -.04 -.298 L .236 -.198 L .499 .098 L .182 -.085 L .2 -.142 L .097 -.143 L .41 -1.288 L .252 -.481 L .304 -.935 L .18 -.65 L .664 -.808 L .363 -.722 L .201 -.636 L .252 -.946 L .311 -.691 L .187 -.128 L .273 -.382 L .013 -.296 L -.312 -.847 L .082 -.184 L .455 -.452 L .206 -.339 L .028 -.24 L -.093 -.226 L -.166 -.805 L -.292 -2.088 L -.098 -.86 L .031 -.565 L .412 -.565 L .37 -.537 L .207 -.564 L .007 -.734 L -.339 -.521 L -.098 -.409 L .295 -.96 L .218 -.941 L .127 -.556 L .461 -.594 L .171 -.918 L .243 -.975 L .126 -.805 L .082 -.565 L -.063 -1.087 L .422 -.664 L .211 -.494 L -.221 -.932 L -.048 -.833 L .148 -.24 L -.022 -.917 L .229 -.607 L -.124 -.297 L -.365 -.084 L .06 -.324 L -.046 -.396 L .223 -.198 L .402 -.198 L .137 -.424 L -.008 -.819 L .093 -.593 L .182 -.918 L -.004 -2.344 L -.172 -2.074 L -.042 -1.539 L -.194 -.974 L -.15 -.387 L .852 -.275 L .259 -.298 L .228 -.933 L .15 -.199 L .586 -.187 L .152 .253 L .555 .745 L .304 .647 L .193 1.213 L .25 .521 L .761 .744 L .017 .282 L -.437 1.555 L -.04 .354 L .249 .676 L .769 1.11 L .212 .563 L .278 .973 L .397 2.144 L .272 .182 L 1.047 -.006 L .107 .056 L .212 .14 L .161 .154 L -.093 .636 L -.541 1.457 L -.36 .256 L -1.346 .53 L -.819 .372 L -.204 .312 L .25 .817 L -.421 .722 L -.007 .579 L .113 .437 L .34 .449 L .03 .226 L -.273 .369 L -.161 .382 L .114 .507 L .53 .477 L .011 .227 L -.152 .311 L -.333 .017 L -.791 .089 L -.329 .835 L -.464 .835 L -.608 .694 L -.282 .439 L -.823 1.839 L .167 1.466 L .054 .762 L -.124 .185 L -.492 .271 L -.292 .34 L -.388 1.201 L -.156 .961 L .285 .633 L .408 1.154 L .784 2.816 L .055 .69 L -.075 .41 L -1.083 1.854 L -.319 .595 L .088 .409 L .189 1.06 L -.078 .325 L -1.334 1.11 L -.231 .312 L -.142 1.075 L .014 1.16 L .249 1.103 L .437 .932 L -.202 .143 L -.951 .529 L -.126 .17 L -.053 .312 L -.511 2.427 L -.316 .541 L -.1 .37 L .123 1.835 L .231 .867 L .012 .427 L -.596 .317 L -.172 .172 L -.106 .271 L .094 1.226 L .125 .128 L .555 .111 L .088 .655 L -.191 1.458 L .252 .585 L .26 .185 L .789 .11 L .302 .17 L -.007 .186 L -.245 .202 L -.322 .13 L -.726 .033 L -.757 .146 L .176 .171 L .586 .298 L .552 .385 L .017 .216 L -.767 .794 L -.059 1.094 L .158 1.035 L -.216 .896 L -.212 .434 L -.226 .262 L -.598 .161 L -.28 .219 L -.249 .781 L .446 .648 L -.069 .188 L -.296 1.218 L -.307 .263 L -1.729 1.01 L -.271 .292 L -.037 .45 L .28 1.309 L .508 1.123 L .218 .043 L .961 -.283 L .654 -.121 L .187 .248 L .231 2.285 L .778 .568 L .669 .041 L 1.41 -.052 L 2.827 .132 L .841 .217 L 1.385 .36 L .286 .039 L h 236.642 296.773 m -.394 -.113 L -.43 -.028 L -.21 -.171 L -.133 -.229 L .21 -.457 L .15 -.657 L -.087 -.514 L .011 -.414 L .364 -.728 L .817 -.116 L .36 .327 L .044 .328 L -.688 .443 L -.146 .229 L .493 .771 L -.194 1.058 L -.167 .271 L h 238.177 317.937 m -.445 -.177 L .083 -.842 L -.849 .075 L -.073 -.368 L .218 -.354 L .823 .102 L .508 -.207 L .205 .103 L .054 .812 L -.267 .34 L -.257 .518 L h 247.801 322.062 m -1.033 .102 L -.467 -.118 L -.55 -.237 L -.42 .001 L -.481 .104 L -.935 .226 L -.496 .03 L .125 -.343 L .202 -.312 L -.104 -.312 L .906 -.15 L 1.434 .058 L .433 .323 L .706 -.007 L -.622 -.689 L -.307 -.163 L -.56 -.117 L -.178 -.089 L -.188 .03 L -.338 -.341 L -.229 -.4 L 1.611 -.581 L -.044 -.296 L -.165 -.147 L -1.819 .285 L -.292 -.222 L .263 -.474 L .146 -.163 L 1.074 -.52 L .868 -.637 L .414 .28 L 1.153 .062 L -.008 1.144 L -.098 3.675 L
+456.133 239.67 N .187 .154 L .163 .027 L .154 .169 L .264 .097 L -.103 .227 L .267 .451 L .43 .478 L .469 .604 L .246 .96 L .377 .662 L .563 .491 L .57 .321 L .533 .223 L .552 .266 L .106 .169 L -.051 .537 L .109 .211 L .201 .098 L .78 -.019 L .127 .239 L -.032 .565 L .163 .564 L .524 .873 L .352 .153 L .938 .136 L .549 .167 L .479 .35 L .306 .451 L .186 .098 L -.317 .411 L -.388 .327 L -.507 .243 L -.747 .075 L -.304 .115 L -.7 .823 L -.586 .583 L -.362 .229 L -.747 .357 L -.388 .355 L -.107 .636 L -.222 .666 L -.247 .241 L -.634 .357 L -.98 .33 L -.249 .214 L -.217 .708 L -.345 .963 L -.288 .354 L -.237 .129 L -.584 .116 L -.43 -.026 L -.473 .06 L -.511 -.011 L -.819 -.193 L -.744 -.32 L -.979 -.645 L -.545 -.039 L -.333 .186 L -.084 .24 L -.585 1.134 L -.382 .469 L -.651 .625 L -.632 .428 L -.8 .372 L -.823 .033 L -.854 .047 L -.368 -.097 L -.11 -.183 L .003 -.48 L .243 -.609 L .166 -.538 L -.21 -.762 L -.547 -.943 L -.716 -.787 L -.528 -.067 L -.007 -.806 L -.013 -1.624 L .009 -1.201 L .106 -2.657 L .146 -.028 L 1.564 -.052 L .586 -.046 L -.002 -3.561 L .043 -1.201 L .025 -.876 L .007 -1.286 L -.005 -1.215 L 1.183 -.021 L 2.962 -.47 L 1.048 -.134 L .62 .773 L .209 -.029 L .993 -.938 L .284 -.143 L .891 -.02 L .811 -.315 L
+279.288 257.295 N .02 .239 L -.544 1.57 L -.375 .468 L -1.007 .74 L -.301 .27 L -.352 .51 L -.609 -.363 L -.35 -.097 L -.235 .029 L -.387 .172 L -.745 .131 L -.71 .005 L -.564 -.096 L -.992 -.333 L -.607 -.025 L -1.187 .332 L -.19 -.056 L .064 -.212 L .425 -.426 L .486 -.398 L .11 -.198 L -.21 -.619 L .048 -.227 L .625 -.851 L .617 -1.203 L .018 -.268 L -.939 -.503 L -.65 -.18 L -1.448 -.697 L -1.632 -1.106 L -.671 -.307 L -1.173 -.204 L -.498 -.237 L -.835 -.588 L -.576 -.562 L -1.271 -1.376 L -.782 -.913 L -.225 -.337 L -.19 -.056 L .149 -.325 L .395 -1.104 L .385 -1.188 L -.016 -.635 L .133 -.509 L .278 -.341 L .698 -1.105 L .332 -.383 L .33 -.044 L .784 -.202 L 1.104 -.162 L 1.076 -.204 L .126 -.128 L .843 .01 L .928 -.02 L .352 .054 L 1.952 .991 L .089 .084 L .253 .408 L .084 .663 L .521 .872 L .104 .959 L -.132 .862 L -.086 .721 L -.006 .72 L .372 .04 L .818 .15 L .925 .221 L .346 -.03 L .709 -.413 L .115 -.001 L .724 .278 L .843 .404 L .121 .437 L .487 2.524 L .254 .563 L .224 .055 L 1.29 -.445 L .234 .112 L .491 .336 L .019 .141 L -.321 .75 L -.298 .835 L -.222 .819 L -.027 .777 L .063 .423 L
+444.673 255.519 N .528 .067 L .716 .787 L .547 .943 L .21 .762 L -.166 .538 L -.243 .609 L -.003 .48 L .11 .183 L .368 .097 L .854 -.047 L .823 -.033 L .8 -.372 L .632 -.428 L .651 -.625 L .382 -.469 L .585 -1.134 L .084 -.24 L .333 -.186 L .545 .039 L .979 .645 L .744 .32 L .819 .193 L .511 .011 L .473 -.06 L .43 .026 L .584 -.116 L .237 -.129 L .288 -.354 L .345 -.963 L .217 -.708 L .249 -.214 L .98 -.33 L .634 -.357 L .247 -.241 L .222 -.666 L .107 -.636 L .388 -.355 L .747 -.357 L .362 -.229 L .586 -.583 L .7 -.823 L .304 -.115 L .747 -.075 L .507 -.243 L .388 -.327 L .317 -.411 L .067 .042 L .72 .023 L .597 .139 L .99 .403 L .31 -.016 L .529 -.145 L .442 .025 L .547 .124 L .342 .225 L .159 .183 L .295 1.199 L .181 .946 L .441 .689 L .33 .944 L .147 .678 L .072 .763 L -.057 1.301 L -.03 .806 L .095 .324 L -1.449 -.585 L -.279 .199 L -.657 .979 L -.28 .567 L -.005 .325 L .39 .676 L .307 .465 L .458 .322 L .671 .109 L .595 -.004 L .076 -.594 L .064 -.198 L .312 -.186 L .131 -.015 L .192 -.058 L 1.331 -.011 L -.182 1.195 L -.352 .849 L -.182 .184 L -.404 .1 L -.093 .24 L .199 .536 L -.104 .467 L -.248 .354 L -.569 .453 L -.923 .581 L -.591 .75 L -1.383 1.98 L -.631 .834 L -1.242 1.373 L -1.193 1.062 L -.829 .863 L -1.434 1.034 L -1.379 1.091 L -.552 .382 L -.989 .638 L -.676 .298 L -.782 .101 L -.98 -.012 L -.549 .071 L -.132 .354 L .013 .127 L -.109 .085 L -.449 -.098 L -.553 -.126 L -.303 .015 L -.25 .198 L -.272 .312 L -.226 .113 L -.36 -.056 L -.768 -.408 L -.759 -.168 L -.542 -.013 L -.31 .113 L -.38 .27 L -.482 -.099 L -.645 -.21 L -.295 .1 L -.638 .043 L -.589 .214 L -.729 .538 L -.72 .086 L -.44 -.013 L -.667 -.084 L -.738 .072 L -.575 .199 L -.827 .82 L -1.251 -.576 L -.092 -.509 L -.218 -.183 L -.479 -.056 L -.28 .085 L -.257 -.479 L -.343 -.056 L -.149 .255 L -.035 .197 L -.312 -.112 L -.119 -.226 L .267 -.495 L .103 -.424 L -.857 -1.354 L -.323 -.282 L -.134 -.226 L .173 -.17 L .636 -.044 L .172 -.424 L .06 -.819 L -.163 -.663 L -.186 -.522 L -.61 -1.143 L -.75 -1.029 L -.472 -.903 L -.612 -1.383 L -.646 -1.467 L -.573 -.818 L -.436 -.467 L .152 -.068 L .584 -.27 L .317 -.416 L .109 -.217 L .335 -.116 L .329 .127 L .637 .348 L .193 .222 L .042 .345 L .055 .528 L .296 .212 L .517 .039 L .892 -.048 L .745 .042 L .581 .166 L .427 -.034 L .581 -.498 L .545 -.302 L .454 -.318 L .057 -.205 L -.025 -2.246 L -.031 -1.894 L .006 -3.434 L h 462.462 268.501 m .412 -.044 L .194 -.553 L .633 -.343 L 1.035 -.303 L .263 -.199 L .582 -1.007 L .268 -.326 L .143 -.241 L -.104 -.226 L -.967 -.744 L -.33 -.337 L -.422 -.266 L -.308 .086 L -.995 .359 L -.65 .329 L -.513 .567 L -.275 .44 L -.691 .611 L .12 .409 L .582 .858 L .285 .366 L .739 .561 L h 432.955 250.661 m .456 .006 L .077 .749 L -.04 .678 L -.772 .02 L -.078 -.354 L -.028 -.396 L .285 -.297 L .101 -.406 L
+471.719 258.047 N .075 .296 L -.071 .495 L .261 .889 L .068 .382 L -.131 .015 L -.312 .186 L -.064 .198 L -.076 .594 L -.595 .004 L -.671 -.109 L -.458 -.322 L -.307 -.465 L -.39 -.676 L .005 -.325 L .28 -.567 L .657 -.979 L .279 -.199 L 1.449 .585 L
+462.462 268.501 N -.739 -.561 L -.285 -.366 L -.582 -.858 L -.12 -.409 L .691 -.611 L .275 -.44 L .513 -.567 L .65 -.329 L .995 -.359 L .308 -.086 L .422 .266 L .33 .337 L .967 .744 L .104 .226 L -.143 .241 L -.268 .326 L -.582 1.007 L -.263 .199 L -1.035 .303 L -.633 .343 L -.194 .553 L -.412 .044 L
+790.15 283.022 N .738 .197 L .008 -.227 L -.242 -.524 L .052 -.284 L .233 .014 L .389 .17 L .37 .751 L .277 .964 L .48 .17 L 1.753 .691 L .506 .113 L .37 -.072 L .699 -.483 L .885 -.343 L .4 .027 L .329 .17 L .066 .454 L -.022 .198 L -.402 1.236 L -.283 .072 L -.761 .058 L -.035 .683 L -.124 .156 L -.424 .029 L -.746 .016 L -.432 .2 L -.271 .284 L .041 .384 L .254 .525 L -.002 .213 L -.151 .199 L -.646 .515 L -.898 1.129 L -.847 1.058 L -.756 .587 L -.68 .316 L -.337 -.171 L -.47 -.313 L -.237 -.328 L .056 -.314 L .288 -.386 L .307 -.671 L .398 -.5 L -.031 -.343 L -.271 -.128 L -.761 -.582 L -.421 -.185 L -.593 -.184 L -.98 -.452 L -.306 -.256 L -.11 -.17 L .081 -.128 L .419 -.157 L 1.389 -.685 L .209 -.512 L -.078 -.695 L .087 -.312 L .396 -.441 L .032 -.383 L -.482 -.837 L .081 -.567 L -.156 -.311 L -.479 -.655 L -.574 -.678 L .102 -.164 L -.145 -.304 L -.291 -.351 L -.336 -.188 L -.29 -.163 L .117 .233 L .497 .515 L .049 .141 L -.169 0 L -.211 -.281 L -.525 -.631 L -.622 -.771 L -.518 -.561 L .001 -.117 L -.268 -.257 L .04 -.141 L .013 -.14 L -.048 -.188 L -.197 -.396 L -.379 -.42 L -.347 -.257 L .163 -.046 L .205 .093 L .358 -.047 L .131 -.093 L .084 .28 L -.149 .187 L .186 .303 L .177 .21 L .167 .116 L .228 .164 L .041 -.141 L .269 .023 L .519 .257 L .42 .117 L .274 .07 L .128 .257 L -.011 .141 L .185 .023 L .146 -.188 L .185 .023 L -.022 .164 L .227 .351 L .249 .187 L .233 .28 L -.18 .023 L -.076 .164 L .093 .163 L -.242 -.023 L -.175 -.047 L .143 .117 L .251 .188 L .23 .233 L .352 .28 L .063 .234 L .019 .21 L -.261 -.047 L .096 .164 L .239 .351 L .256 .188 L -.292 .023 L -.226 0 L -.205 -.047 L -.006 .141 L .306 .14 L .324 .164 L -.09 .211 L .205 .046 L .265 -.023 L .226 0 L .223 .141 L -.114 .07 L -.031 .141 L .025 .141 L .136 .06 L h 782.939 297.694 m -.088 .158 L -.558 .13 L -.309 .288 L -.322 .101 L -.246 .244 L -.692 -.242 L -.16 .086 L .15 .216 L .429 .415 L -.141 .173 L .02 .259 L -.064 .431 L -.218 -.071 L -.976 -.099 L .418 .229 L .449 .244 L -.278 .49 L -.427 .896 L -.212 .549 L -.418 .318 L -.673 .349 L -.171 .246 L -.259 .145 L -.581 .233 L -.593 .406 L -.398 .015 L -1.156 -.258 L -.628 .112 L -.585 -.442 L -.812 -.158 L -.373 -.066 L -.162 -.308 L -.467 -.098 L -.24 .142 L -.062 .168 L -.78 .095 L -.214 -.166 L -.515 -.095 L -.146 -.286 L .432 -.089 L -.223 -.216 L .328 -.116 L .322 -.001 L -.452 -.482 L .82 .266 L -.464 -.576 L .121 -.145 L .946 .156 L .082 -.13 L -.141 -.173 L -.201 -.216 L -.06 -.288 L .283 -.303 L .569 -.246 L .328 -.374 L .561 -.375 L .102 -.302 L .998 -.575 L 1.106 -.275 L .713 -.331 L .544 -.36 L .377 -.101 L .685 -.575 L .066 -.272 L .48 -.302 L .373 -.015 L .787 -.331 L .664 -.402 L .126 -.215 L -.008 -.172 L .266 -.144 L .448 -.302 L -.109 -.501 L .076 -.214 L .166 -.44 L .306 .048 L .066 -.152 L .58 -.259 L .444 -.272 L .137 -.285 L .131 -1.187 L .512 -.647 L .372 .047 L .365 .165 L .032 .259 L .337 .06 L .187 .186 L .231 .799 L .312 .242 L .973 -.645 L .426 -.029 L .367 .113 L .222 .5 L -.197 .399 L .299 .429 L .066 .271 L -.611 .659 L -.261 .401 L -.476 .358 L -.868 .746 L -.578 .359 L -.295 .13 L -.236 .258 L -.389 .159 L -.271 .258 L .416 .407 L .428 .047 L .421 .289 L -.276 .113 L -.484 .07 L -.503 -.296 L -.488 .131 L -.352 .158 L
+247.899 318.387 N .008 -1.144 L .821 .289 L .06 .206 L -.354 .312 L -.534 .337 L h 248.453 316.576 m -.286 -.039 L -1.385 -.36 L -.841 -.217 L -2.827 -.132 L -1.41 .052 L -.669 -.041 L -.778 -.568 L -.231 -2.285 L -.187 -.248 L -.654 .121 L -.961 .283 L -.218 -.043 L -.508 -1.123 L -.28 -1.309 L .037 -.45 L .271 -.292 L 1.729 -1.01 L .307 -.263 L .296 -1.218 L .069 -.188 L -.446 -.648 L .249 -.781 L .28 -.219 L .598 -.161 L .226 -.262 L .212 -.434 L .216 -.896 L -.158 -1.035 L .059 -1.094 L .767 -.794 L -.017 -.216 L -.552 -.385 L -.586 -.298 L -.176 -.171 L .757 -.146 L .726 -.033 L .322 -.13 L .245 -.202 L .007 -.186 L -.302 -.17 L -.789 -.11 L -.26 -.185 L -.252 -.585 L .191 -1.458 L -.088 -.655 L -.555 -.111 L -.125 -.128 L -.094 -1.226 L .106 -.271 L .172 -.172 L .596 -.317 L -.012 -.427 L -.231 -.867 L -.123 -1.835 L .1 -.37 L .316 -.541 L .511 -2.427 L .053 -.312 L .126 -.17 L .951 -.529 L .202 -.143 L -.437 -.932 L -.249 -1.103 L -.014 -1.16 L .142 -1.075 L .231 -.312 L 1.334 -1.11 L .078 -.325 L -.189 -1.06 L -.088 -.409 L .319 -.595 L 1.083 -1.854 L .075 -.41 L -.055 -.69 L -.784 -2.816 L -.408 -1.154 L -.285 -.633 L .156 -.961 L .388 -1.201 L .292 -.34 L .492 -.271 L .124 -.185 L -.054 -.762 L -.167 -1.466 L .823 -1.839 L .282 -.439 L .608 -.694 L .464 -.835 L .329 -.835 L .791 -.089 L .333 -.017 L .152 -.311 L -.011 -.227 L -.53 -.477 L -.114 -.507 L .161 -.382 L .273 -.369 L -.03 -.226 L -.34 -.449 L -.113 -.437 L .007 -.579 L .421 -.722 L -.25 -.817 L .204 -.312 L .819 -.372 L 1.346 -.53 L .36 -.256 L .541 -1.457 L .093 -.636 L -.161 -.154 L -.212 -.14 L -.107 -.056 L 2.026 -2.087 L .292 -.27 L .322 .181 L .193 .169 L .615 .194 L .901 -.034 L .527 -.045 L .531 .067 L .37 .195 L .341 .309 L .416 1.084 L 1.034 -1.784 L .686 -.004 L 1.33 .077 L .592 .109 L .19 .056 L .225 .337 L .782 .913 L 1.271 1.376 L .576 .562 L .835 .588 L .498 .237 L 1.173 .204 L .671 .307 L 1.632 1.106 L 1.448 .697 L .65 .18 L .939 .503 L -.018 .268 L -.617 1.203 L -.625 .851 L -.048 .227 L .21 .619 L -.11 .198 L -.486 .398 L -.425 .426 L -.064 .212 L .19 .056 L 1.187 -.332 L .607 .025 L .992 .333 L .564 .096 L .71 -.005 L .745 -.131 L .387 -.172 L .235 -.029 L .35 .097 L .609 .363 L .352 -.51 L .301 -.27 L 1.007 -.74 L .375 -.468 L .544 -1.57 L -.02 -.239 L .957 -.161 L .462 -.017 L .206 .196 L .517 1.154 L -.094 1.638 L -.161 .467 L -.521 .313 L -1.754 .744 L -.348 .242 L -1.633 1.448 L -1.435 1.363 L -1.805 1.816 L -.833 .88 L -.214 .27 L -.443 .524 L -.065 .452 L -.595 2.359 L -.103 .522 L .049 .847 L .168 .986 L -.118 .325 L -.48 .524 L -.24 .495 L -.011 .522 L .194 .577 L -.054 .338 L -.162 .273 L -.26 .325 L .015 .226 L .924 .831 L .68 .281 L .715 .281 L .283 .169 L .281 .325 L -.025 .325 L -.362 .523 L -.043 .396 L .105 .339 L .174 .269 L .466 .325 L .522 .168 L .109 .113 L .226 .892 L -.308 .481 L -.75 .937 L -.729 .766 L -.313 .737 L -.368 .284 L -.832 .342 L -1.04 .342 L -1.92 .401 L -1.795 .188 L -1.361 .116 L -.945 .044 L -1.175 -.11 L -.934 -.226 L -.128 .199 L .036 .808 L .322 .312 L .308 .184 L -.09 .298 L -.381 .624 L -.345 .498 L -.069 .385 L .392 .682 L .066 .285 L -.208 .214 L -.105 .057 L -1.251 .473 L -1.137 .116 L -.814 -.069 L -.967 -.34 L -1.47 -.396 L -.246 .057 L -.23 .271 L .041 .598 L .429 .684 L .037 .398 L -.242 .643 L .092 .385 L .773 .54 L .796 .084 L .369 -.2 L -.387 -.398 L .872 -.188 L .383 -.043 L .234 1.041 L .052 .3 L -.144 .157 L -.299 .101 L -.448 .072 L -.261 -.157 L -.104 -.299 L -.115 -.071 L -1.046 .073 L -.67 .201 L -.212 .101 L .151 .214 L .591 .07 L .47 .113 L .452 .113 L .06 .028 L -.864 .388 L -.776 .287 L -.577 .602 L .003 .414 L .161 .787 L -.081 .258 L -.815 .817 L .022 .215 L .423 .371 L -.491 .116 L -1.194 .088 L -.48 .087 L -.632 .246 L -.619 .389 L -.56 .548 L -.549 .821 L -.052 .389 L .061 .375 L .312 .591 L .48 .446 L .98 .633 L .657 .244 L .97 .143 L .362 .086 L .14 .274 L -.151 .796 L -.128 .348 L -.342 .464 L -.189 .145 L -1.08 .524 L -1.541 .814 L -.712 .698 L -.179 .276 L -.093 .45 L .111 .523 L -.169 .451 L -.239 .32 L -.97 .454 L -.969 .25 L -.421 .221 L -.323 .396 L -.226 .791 L -.054 .514 L .2 .777 L .547 .896 L .699 .779 L .235 .339 L -.101 .032 L h 247.899 318.387 m .18 .182 L .148 .073 L .607 -.075 L .344 .133 L .648 .725 L .908 .665 L .993 .756 L .525 .222 L .73 .37 L .246 .074 L .42 -.001 L .483 .163 L 1.283 .027 L .142 -.016 L -.006 .224 L -.19 .209 L -.492 .06 L -1.198 .092 L -.777 .196 L -.364 0 L -1.179 -.355 L -.753 -.088 L -1.15 -.027 L -.814 -.014 L -.831 .081 L .098 -3.675 L
+346.758 54.457 N .127 -.162 L .42 -.179 L .894 .015 L .674 -.098 L -.354 -.227 L -.405 -.34 L .317 -.342 L .277 0 L .956 .42 L .67 .048 L .3 -.163 L -.191 -.26 L -.625 -.373 L .366 -.245 L 1.037 .226 L .957 .08 L .746 .275 L .446 .551 L -.119 .405 L -.441 .292 L .922 .806 L .553 -.356 L .352 -.13 L .63 -.114 L .49 -.179 L .061 -.21 L -.169 -.778 L .542 -.245 L .501 .454 L .426 .307 L .489 .209 L .215 .016 L .185 -.13 L -.264 -.454 L .243 -.163 L .27 -.033 L .817 -.164 L .683 .438 L .536 .242 L .542 .063 L .05 -.178 L -.358 -.535 L 1.006 .145 L 1.439 .079 L .787 -.115 L .427 -.229 L -.021 -.716 L 1.167 .08 L .635 .471 L 1.118 .323 L .683 .015 L .273 .243 L -.252 .552 L .786 .29 L 1.674 .159 L .127 .145 L .143 .548 L -.07 .387 L -.152 .256 L -.152 .256 L -.443 .129 L -.815 .018 L -.195 .096 L -.04 .431 L -.515 .463 L -.497 .16 L -.568 -.031 L -.422 -.159 L -.817 .495 L -.539 .144 L -1.46 .463 L -.853 .113 L -.726 .001 L -.771 .097 L -.784 .587 L -.473 .127 L -1.078 .097 L -.709 -.03 L -1.316 -.171 L -.608 -.142 L -1.283 -.489 L -1.058 -.093 L -.443 .064 L -1.041 -.014 L -1.834 -.124 L -.297 -.206 L .434 -.191 L 1.127 -.352 L .701 -.59 L -.818 -.015 L -.51 -.126 L -.428 -.398 L -.253 -.095 L -.358 .081 L -1.564 .115 L -.557 .033 L -.37 -.223 L .141 -.192 L .388 -.129 L .669 -.097 L .794 -.017 L .729 -.114 L 1.049 -.098 L .376 -.194 L .178 -.322 L -.144 -.258 L -.358 -.177 L -.426 -.015 L -.478 -.145 L -1.005 -.047 L -.821 .099 L -.424 .162 L -.676 .082 L -1.041 -.272 L -.16 -.21 L
+462.829 67.958 N .145 .053 L .16 .131 L -.043 .174 L -.175 .044 L -.189 0 L -.116 0 L -.088 .043 L -.058 .131 L -.175 .277 L -.219 .204 L -.319 .131 L -.204 .131 L -.088 .16 L .029 .16 L .131 .437 L .073 .189 L .029 .16 L -.175 .131 L 0 .175 L .175 .233 L .203 .087 L .088 .073 L 0 .073 L .015 .087 L .043 .146 L .175 .043 L .059 .059 L -.175 .073 L -.262 .043 L -.16 .058 L -.059 .102 L -.087 .116 L -.131 0 L 461.402 72 l -.015 .087 L -.116 .058 L .081 .106 L -.125 .025 L -.087 -.015 L -.306 -.043 L -.16 -.058 L -.175 -.073 L -.204 -.044 L -.131 .102 L -.204 -.029 L -.131 .044 L -.16 .087 L -.146 .014 L -.146 -.087 L -.116 -.029 L -.175 0 L -.204 -.117 L -.116 -.174 L -.203 -.117 L -.146 -.043 L -.072 -.117 L -.189 0 L -.247 -.043 L -.204 -.117 L -.029 0 L -.276 -.014 L -.102 -.102 L -.248 -.043 L -.116 -.102 L -.189 -.087 L 0 .073 L -.116 .087 L -.131 -.058 L -.015 -.073 L -.087 -.029 L -.103 0 L -.276 .117 L -.102 .029 L -.131 .015 L -.219 .015 L -.146 .043 L -.262 .029 L -.276 .087 L -.116 .102 L -.087 0 L .156 -.19 L -.003 -.351 L .183 -.238 L -.368 -.21 L -.605 .437 L -.334 -.251 L -.527 -.038 L .043 -.942 L -.396 .188 L -.336 -.415 L .158 -.202 L -.209 -.254 L .265 -.074 L -.092 -.252 L .344 -.042 L 1.026 -.084 L -.006 -.132 L .561 -.108 L .133 -.188 L .436 .101 L .074 -.113 L .317 .05 L .083 -.215 L 1.104 .193 L .446 -.294 L .091 .165 L .514 -.089 L 1.383 .029 L 1.152 .167 L .305 .12 L .592 -.045 L .971 .09 L .426 -.108 L .271 -.24 L -.006 -.009 L
+461.353 72.251 N .37 -.004 L -.015 .116 L 0 .117 L .232 .073 L .204 .087 L .087 .073 L .204 .058 L -.015 .087 L -.029 .116 L -.015 .087 L -.102 .073 L -.087 .015 L -.103 .029 L .131 .087 L -.072 .131 L .029 .117 L -.073 .087 L -.102 .073 L -.044 .102 L .204 -.059 L .146 -.015 L .131 .073 L .087 .014 L .073 .044 L -.059 .087 L -.043 .073 L .116 .102 L .131 .058 L .029 .16 L .146 .102 L .16 .043 L -.059 .087 L .131 .117 L -.015 .189 L .088 .233 L -.044 .087 L -.015 .117 L -.061 .042 L -.241 .044 L -.24 .051 L -.12 .12 L -.223 .034 L -.137 .154 L -.137 .085 L -.069 .103 L -.068 .206 L -.188 .035 L -.239 -.035 L -.24 -.034 L -.325 -.034 L -.359 0 L -.172 .068 L -.103 .137 L -.223 .085 L -.154 0 L -.103 -.017 L -.086 .052 L -.377 -.035 L -.273 -.068 L -.154 -.171 L -.138 -.154 L -.325 -.137 L -.497 -.223 L -.342 -.24 L -.377 -.034 L -.583 -.035 L -.325 -.12 L -.291 -.188 L -.086 -.257 L -.188 .017 L -.171 .068 L -.36 .171 L -.394 .017 L -.24 0 L -.273 .085 L -.24 -.085 L -.309 -.103 L -.6 -.017 L -.291 .069 L -.359 -.069 L -.291 -.051 L -.154 .034 L -.274 .069 L -.103 -.052 L -.12 -.137 L -.154 0 L -.257 .068 L -.188 0 L -.754 -.017 L -.445 -.085 L -.754 .171 L -.599 .154 L -.429 .103 L -.257 .137 L -.052 .188 L -.526 .026 L -.065 -.059 L .073 -.837 L .035 -.302 L .127 -.167 L .672 -.379 L .034 -.717 L .267 -.162 L .267 -.273 L .217 -.203 L .296 -.026 L 1.056 -.199 L .166 -.046 L .162 -.066 L .29 0 L .049 .237 L .657 .388 L .422 .162 L .23 .473 L .091 .15 L .441 .196 L .785 .059 L .868 -.244 L .24 -.122 L .178 -.288 L -.052 -.394 L -.193 -.869 L .198 -.243 L .045 -.055 L .087 0 L .116 -.102 L .276 -.087 L .262 -.029 L .146 -.043 L .219 -.015 L .131 -.015 L .102 -.029 L .276 -.117 L .103 0 L .087 .029 L .015 .073 L .131 .058 L .116 -.087 L 0 -.073 L .189 .087 L .116 .102 L .248 .043 L .102 .102 L .276 .014 L .029 0 L .204 .117 L .247 .043 L .189 0 L .072 .117 L .146 .043 L .203 .117 L .116 .174 L .204 .117 L .175 0 L .116 .029 L .146 .087 L .146 -.014 L .16 -.087 L .131 -.044 L .204 .029 L .131 -.102 L .204 .044 L .175 .073 L .16 .058 L .306 .043 L .087 .015 L .125 -.025 L
+451.02 79.165 N -.029 -.038 L -.034 -.137 L -.018 -.171 L .068 -.206 L .068 -.154 L .224 -.12 L -.052 -.12 L -.018 -.137 L -.171 -.069 L -.188 -.034 L -.103 -.103 L -.086 -.137 L -.223 .017 L -.257 0 L -.445 0 L -.223 .051 L -.086 -.103 L -.514 -.068 L -.257 -.069 L -.223 -.12 L -.24 0 L -.086 -.052 L -.051 -.154 L -.12 .034 L -.353 .096 L -.043 -.077 L .128 -.012 L .034 -.183 L -.439 -.646 L -.008 -.14 L -.042 -.727 L -.112 -.102 L .526 -.026 L .052 -.188 L .257 -.137 L .429 -.103 L .599 -.154 L .754 -.171 L .445 .085 L .754 .017 L .188 0 L .257 -.068 L .154 0 L .12 .137 L .103 .052 L .274 -.069 L .154 -.034 L .291 .051 L .359 .069 L .291 -.069 L .6 .017 L .309 .103 L .24 .085 L .273 -.085 L .24 0 L .394 -.017 L .36 -.171 L .171 -.068 L .188 -.017 L .086 .257 L .291 .188 L .325 .12 L .583 .035 L .377 .034 L .342 .24 L .497 .223 L .325 .137 L .138 .154 L .154 .171 L .273 .068 L .377 .035 L -.017 .171 L -.086 .154 L -.034 .12 L -.12 .137 L -.086 .137 L .343 .034 L .274 .052 L .085 .051 L -.119 .051 L -.086 0 L -.103 .154 L -.018 .154 L -.171 .017 L -.12 -.086 L -.12 .051 L -.239 -.034 L -.154 .034 L -.086 .154 L -.103 .154 L -.257 .068 L -.429 0 L -.137 .137 L -.12 .12 L -.034 .154 L -.086 .171 L .103 .171 L -.068 .137 L -.239 .154 L 0 .137 L -.068 .085 L -.069 .137 L .172 .034 L .205 0 L .138 .206 L -.086 .188 L -.274 .017 L -.223 -.068 L 0 -.154 L -.034 -.085 L -.086 -.069 L -.171 .051 L -.12 .086 L -.291 -.034 L -.068 .137 L -.24 .12 L -.154 0 L -.188 -.034 L -.273 .103 L .086 .171 L -.069 .12 L -.171 .034 L -.137 -.034 L -.206 .051 L -.377 .154 L -.291 0 L -.068 -.103 L -.12 -.051 L -.239 .051 L -.377 .017 L -.24 .034 L -.291 -.034 L -.154 .034 L -.093 -.035 L -.09 -.171 L -.016 -.029 L -.099 -.186 L -.284 -.487 L -.679 -.243 L -.04 -.014 L -.641 .021 L
+452.867 80.273 N .093 .035 L .154 -.034 L .291 .034 L .24 -.034 L .377 -.017 L .239 -.051 L .12 .051 L .068 .103 L .291 0 L .377 -.154 L .206 -.051 L .137 .034 L .171 -.034 L .069 -.12 L -.086 -.171 L .273 -.103 L .188 .034 L .154 0 L .24 -.12 L .068 -.137 L .291 .034 L .12 -.086 L .171 -.051 L .086 .069 L .034 .085 L 0 .154 L .223 .068 L .274 -.017 L .086 -.188 L -.138 -.206 L -.205 0 L -.172 -.034 L .069 -.137 L .068 -.085 L 0 -.137 L .239 -.154 L .068 -.137 L -.103 -.171 L .086 -.171 L .034 -.154 L .12 -.12 L .137 -.137 L .429 0 L .257 -.068 L .103 -.154 L .086 -.154 L .154 -.034 L .239 .034 L .12 -.051 L .12 .086 L .171 -.017 L .018 -.154 L .103 -.154 L .086 0 L .119 -.051 L -.085 -.051 L -.274 -.052 L -.343 -.034 L .086 -.137 L .12 -.137 L .034 -.12 L .086 -.154 L .017 -.171 L .086 -.052 L .103 .017 L .154 0 L .223 -.085 L .103 -.137 L .172 -.068 L .359 0 L .325 .034 L .24 .034 L .239 .035 L .188 -.035 L .068 -.206 L .069 -.103 L .137 -.085 L .137 -.154 L .223 -.034 L .12 -.12 L .24 -.051 L .241 -.044 L .165 .147 L .229 .066 L .197 -.131 L .181 .016 L .312 .033 L .132 .148 L .082 .148 L .197 -.033 L .214 -.065 L .361 -.049 L .312 .049 L .296 .065 L .147 .017 L 0 .115 L -.164 .099 L -.017 .099 L .065 .148 L .164 .148 L .197 0 L .214 -.197 L .279 -.016 L .165 0 L .147 -.099 L .23 -.083 L .131 .049 L .099 .049 L .247 -.049 L .542 .115 L .132 .131 L .279 .099 L .099 .099 L .147 .099 L .165 .033 L .147 -.033 L .049 .115 L -.065 .115 L 0 .083 L -.033 .164 L -.131 .165 L .197 .247 L .147 .099 L .05 .164 L -.066 .131 L -.114 0 L 0 .083 L -.115 .082 L -.099 .049 L -.033 .165 L -.049 .147 L .345 .049 L .132 .181 L .082 .115 L .181 -.049 L .132 .033 L -.099 .115 L -.066 .131 L .017 .099 L .214 .017 L .164 .263 L .115 .23 L .443 .213 L .23 .066 L .279 .082 L .164 .099 L .066 .148 L -.099 .197 L -.066 .181 L .182 .066 L .361 -.066 L .378 .066 L .361 .099 L .263 .066 L -.032 .065 L -.066 .049 L -.082 .017 L .099 .181 L .296 .148 L .279 .066 L .033 .131 L -.065 .164 L -.296 0 L -.148 .083 L -.049 .065 L -.444 .247 L -.525 .099 L -.51 -.016 L -.197 -.132 L -.328 -.049 L -.362 .017 L -.131 .165 L -.099 .131 L .017 .164 L .279 .263 L .296 .164 L 0 .165 L -.132 .066 L .099 .148 L .147 .131 L -.082 .099 L .033 .164 L .033 .23 L -.033 .099 L .164 .082 L .082 .115 L .165 .066 L .002 .142 L -.519 -.005 L -.522 .056 L -.112 .131 L -.205 -.056 L -.187 -.037 L -.336 .075 L -.057 .13 L -.111 .112 L -.317 .187 L -.188 .261 L -.261 .224 L -.057 .206 L .243 .205 L .056 .149 L -.131 .206 L -.261 -.019 L -.149 -.056 L -.149 -.206 L -.112 -.056 L -.187 -.075 L -.224 -.037 L -.225 .037 L -.242 .075 L -.299 .019 L -.149 -.131 L -.224 .075 L -.188 .093 L -.316 .056 L -.188 -.056 L -.037 -.205 L -.112 -.131 L -.168 -.131 L -.13 .038 L -.131 .075 L -.168 0 L -.299 .149 L -.131 .149 L -.168 0 L -.094 -.187 L -.13 -.075 L -.206 0 L -.224 .112 L -.131 -.187 L -.224 -.093 L -.131 .112 L -.354 .056 L -.262 -.112 L -.112 0 L -.019 .205 L -.168 .093 L -.093 -.056 L .056 -.224 L -.243 -.038 L -.187 -.056 L -.541 .112 L 0 -.149 L -.187 0 L .019 -.224 L -.28 -.037 L -.242 .075 L -.523 -.112 L -.578 -.056 L -.075 -.056 L -.522 0 L -.205 -.168 L -.262 .019 L -.522 -.093 L -.467 .075 L -.485 0 L -.354 -.056 L -.355 .056 L -.354 .056 L -.485 -.038 L -.485 .019 L -.205 .187 L -.037 .168 L -.374 .168 L -.373 .206 L -.112 -.112 L -.261 0 L -.374 -.019 L -.037 .131 L 0 .045 L -.126 -.137 L .315 -.752 L -.013 -.25 L -.218 -.146 L -.149 -.176 L -.421 -.146 L -.289 -.012 L .128 -.292 L .291 -.328 L .571 -.244 L .44 -.03 L .263 -.208 L .023 -.236 L -.172 -.502 L -.615 -1.5 L -.16 -.302 L
+400.125 81.146 N .633 .305 L .208 .207 L .208 .37 L -.038 .193 L -.545 .563 L .714 .176 L .396 -.311 L .527 -.119 L .602 .028 L .807 .176 L .467 .354 L .235 .752 L -.077 .221 L -.322 .414 L -1.068 .473 L -.767 .561 L -.96 .237 L 1.223 .167 L .501 .043 L .354 -.104 L .39 .117 L -.066 .516 L -.997 .308 L .005 .199 L -.479 -.084 L -1.068 .443 L -.879 -.142 L -.293 -.048 L -1.119 .211 L -.587 -.211 L -.598 .112 L -1.584 .141 L .137 .295 L -.907 -.168 L -.264 .168 L -.911 -.337 L -.334 .143 L -.913 .089 L -.093 .569 L -.337 .316 L -.37 .042 L -.272 -.252 L -.53 -.172 L -.135 .151 L -.527 -.077 L -.948 .324 L -.701 .552 L -.326 -.231 L -.775 -.147 L 1.2 -.472 L .492 -.476 L .447 -.097 L .468 -.388 L .118 -.485 L .242 .063 L .367 -.211 L -.008 -.274 L 1.013 -.105 L .76 .119 L .927 .007 L .073 -.338 L .308 -.142 L .321 -.556 L -.939 .394 L -.725 .016 L -1.467 -.482 L -1.866 -.055 L -.399 -.191 L -.156 -.162 L .417 -.325 L .983 -.194 L .721 -.237 L .38 -.384 L .066 -.827 L -.119 -.192 L -.713 .046 L -.34 -.044 L .288 -.355 L .387 -.223 L .802 -.253 L 1.394 -.062 L .922 -.076 L -.316 -.548 L .172 -.683 L .253 -.461 L -.045 -.312 L -.834 .061 L -.484 -.296 L -.2 -.312 L .309 -.507 L .617 -.433 L -.347 -.104 L -.596 -.088 L -.735 .3 L -.476 .061 L -.753 -.222 L -.088 .194 L -.222 .194 L -.672 -.103 L -.464 -.133 L .1 -.343 L .348 -.36 L .604 -.706 L -.293 -.134 L -.305 -.39 L .028 -.24 L .304 -.106 L .3 -.196 L -.194 -.496 L -.306 .076 L -.636 .453 L -.442 .031 L -.526 .287 L -.14 -.421 L .302 -.527 L .438 -.559 L .065 -.257 L -.417 -.195 L -.196 -.045 L -.218 .302 L -.394 .257 L -.493 -.165 L .299 -.575 L .54 -.455 L .09 -.136 L -.225 -.575 L .366 -.092 L .225 -.197 L -.672 -.515 L .432 -.351 L .752 .151 L .387 -.092 L -.645 -.759 L .975 -.169 L -.457 -.502 L .416 -.382 L .924 .365 L .824 -.093 L .686 -.139 L .867 -.047 L .612 .014 L .303 .259 L -.307 .29 L -1.726 .704 L -.46 .274 L -.218 .441 L .222 .182 L .784 .029 L .877 -.078 L .685 -.001 L .53 .075 L 1.563 -.064 L .458 .378 L -.363 .425 L -.212 .323 L .098 .112 L -.565 .66 L -.226 .111 L -.339 .437 L -.696 .261 L -.382 .038 L .451 .186 L .508 .167 L -.116 .015 L -.272 .19 L -.61 .052 L -.275 .196 L -1.337 -.025 L .404 .223 L .302 0 L .492 .093 L .432 -.006 L .519 -.223 L .413 -.025 L .449 .161 L .656 .164 L .673 .566 L .496 .228 L .118 .165 L -.067 .238 L .312 .78 L .371 .536 L .438 .189 L .714 .107 L .59 .549 L .688 .593 L .135 .52 L -.188 .49 L .257 .124 L h 387.915 77.13 m -.128 -.325 L .149 -.335 L .38 -.089 L .079 .501 L -.307 .251 L -.173 -.003 L h 386.786 80.184 m -.178 -.272 L -.967 .072 L .123 -.256 L -.364 -.15 L -.26 -.257 L -.335 -.107 L -.253 .364 L -.751 .257 L -.778 -.192 L -.401 -.278 L -.101 -.278 L .86 -.278 L -.483 -.257 L .817 -.107 L .385 -.484 L -.029 -.235 L .449 -.09 L .508 -.15 L .781 -.077 L .424 .044 L .389 .104 L .362 -.046 L .218 .149 L .519 .791 L .047 .179 L -.081 .298 L .308 .446 L -.155 .328 L -.402 .328 L -.354 .12 L -.299 .038 L
+578.943 106.217 N -.41 -.375 L -.466 -.098 L -.663 0 L -.196 -.27 L -.27 -.147 L -.147 -.344 L -.564 .049 L -.981 -.246 L -.662 .074 L -1.35 -.024 L -.662 -.098 L -.712 -.221 L -.785 .147 L -.761 0 L -.858 .024 L -.441 .27 L -.54 -.098 L -.908 -.196 L -.735 -.246 L -.761 -.27 L -.589 -.074 L -.688 .123 L -.466 .368 L -.245 .736 L .024 .442 L -.344 -.123 L -.81 -.123 L -.688 -.196 L -.883 -.245 L -.883 -.147 L -.663 .098 L -.736 .123 L -.318 .368 L -.393 .442 L .044 .273 L -.322 .031 L -.377 .377 L -.283 -.126 L -.22 .063 L -.346 .283 L -.534 .471 L -.755 .189 L -.943 .377 L -.282 .188 L -.221 .472 L -.439 .188 L -.504 .44 L .157 .409 L -.125 .188 L -.66 0 L -.44 -.346 L .062 -.283 L -.062 -.283 L -.44 -.314 L -.346 0 L -1.006 .094 L -.691 .032 L -.503 -.063 L -.346 -1.069 L -.221 -.817 L -1.006 0 L -.031 -.754 L .188 -.409 L .031 -1.038 L -.66 .314 L -.66 -1.006 L -.597 -.22 L -.724 -.723 L -1.1 .409 L -2.767 -.188 L -2.578 .346 L -2.012 -1.666 L -5.722 -2.986 L -5.658 1.289 L -.056 8.174 L -.158 -.014 L -.341 .106 L -.489 .043 L -.447 -.255 L -.638 -.703 L -.256 -.511 L -.617 -.383 L -.681 -.383 L -.512 -.234 L -.979 .085 L -1.277 .298 L -.937 .532 L -.529 .453 L .092 -.399 L -.06 -.18 L -.12 -.12 L .14 -.26 L .2 -.2 L .14 -.32 L .04 -.3 L .18 -.2 L -.159 -.24 L -.4 -.16 L -.459 .06 L -.18 -.16 L -.3 .06 L -.2 .04 L -.199 -.18 L -.221 -.32 L -.319 -.28 L -.34 0 L -.359 .02 L 0 -.2 L .08 -.28 L -.2 -.379 L -.239 -.12 L -.2 -.24 L -.399 -.799 L -.08 -.28 L -.56 -.12 L -.699 -.08 L -.14 -.16 L .02 -.439 L .16 -.12 L .3 -.06 L .399 .02 L .34 .02 L .479 .14 L .539 .18 L .18 -.08 L .36 -.08 L -.2 -.16 L -.26 -.12 L -.399 -.2 L -.2 -.24 L .26 -.36 L .28 -.04 L .08 -.26 L .18 -.299 L .12 -.14 L .26 .04 L .319 -.08 L .16 -.1 L .339 .12 L .24 0 L 1.119 -.04 L .999 .14 L .499 .02 L -.159 -.08 L -.34 -.2 L -.479 -.12 L -.021 -.3 L .2 -.2 L .279 -.22 L .221 -.28 L .119 -.52 L .12 -.28 L -.16 -.24 L -.14 -.16 L .1 -.2 L .26 -.2 L -.119 -.12 L -.101 -.3 L -.359 -.12 L -.359 -.04 L -.68 -.1 L -.2 .16 L -.199 .08 L -.52 .08 L -.46 -.12 L -.319 -.26 L -.26 -.06 L -.68 -.12 L -.56 .06 L -.659 .319 L -.42 .02 L -.799 .5 L -.72 .28 L -.499 .06 L -.42 -.02 L -.279 .24 L -.213 .18 L -.616 -.19 L -.857 -.377 L -.068 -.308 L .343 -.103 L .309 .103 L .445 .103 L .138 -.103 L -.96 -1.131 L -.343 -.514 L -.479 -.206 L -.515 -.445 L -.514 -.034 L -.343 .034 L -.583 -.206 L -.103 .343 L -.514 -.514 L .068 -.309 L -.138 -.377 L -1.37 -.343 L .65 -1.165 L .446 -.274 L .239 -.206 L -.239 -.274 L -.343 -.171 L .205 -1.303 L .823 -.137 L .343 -.549 L .103 -.308 L .411 -.069 L .514 .24 L .48 .548 L .514 .411 L .651 0 L .411 -.24 L .068 -.446 L -.171 -.411 L -.068 -.445 L .479 -.206 L .891 -.411 L .172 -.24 L .309 -.309 L .514 -.171 L .549 -.068 L .788 -.377 L .548 -.343 L .515 -.309 L .651 .069 L .479 0 L .309 .274 L .651 -.137 L .273 -.137 L .617 -.24 L .411 .069 L .411 .514 L .788 .035 L .617 -.069 L .96 .171 L 0 .343 L .582 .206 L .789 .343 L .411 .274 L .068 .583 L .274 .137 L .239 -.274 L -.205 -.48 L -.034 -.24 L .72 .068 L .582 .548 L .686 .137 L .411 .24 L .686 -.171 L .274 -.274 L .377 -.343 L .514 -.377 L .823 .068 L .65 .035 L .651 .411 L .617 -.068 L .137 -.412 L 1.062 -.103 L .754 .103 L .274 .548 L .926 .309 L .754 .137 L .411 .171 L .651 -.343 L .171 -.309 L .24 0 L .343 .343 L .959 .034 L 1.577 -.411 L .137 -.309 L .138 -.686 L -.24 -.24 L -1.165 -.171 L -.274 -.308 L -.651 -.069 L -.377 -.137 L .068 -.171 L -.377 -.137 L -.239 0 L -.164 -.274 L .467 -.067 L .735 -.368 L .588 -.147 L .331 -.294 L -.441 -.478 L -.146 -.257 L .662 -.515 L .698 -.184 L 1.103 .147 L .515 -.073 L .11 -.257 L -.956 -.294 L -1.065 -.11 L 0 -.331 L .294 -.074 L -.294 -.221 L -.074 -.441 L .185 -.515 L .33 -.074 L 1.066 .147 L .515 0 L .772 0 L .368 -.184 L 1.396 -.405 L 1.029 -.037 L .735 -.11 L 1.545 -.11 L .588 -.073 L .331 .073 L .221 -.331 L .625 -.331 L 1.177 -.037 L 2.021 -.405 L 1.876 -.073 L .625 -.074 L .367 -.368 L 77.39 V .515 -.037 L .589 -.184 L .11 -.221 L .735 -.037 L .919 .147 L .515 .11 L .772 .257 L .625 -.11 L .882 -.037 L .368 .404 L -.037 .331 L .147 .221 L .515 .22 L -.11 .331 L -.147 .257 L .073 .331 L -.33 .037 L .184 .257 L .478 .074 L .295 -.147 L .44 .11 L .368 -.147 L .367 .074 L .331 -.221 L .294 .11 L .295 .368 L .367 .221 L .147 -.147 L .184 -.147 L .478 .037 L .405 .294 L .478 .11 L .441 -.221 L .367 0 L -.146 .294 L -.441 .184 L -.331 .441 L .331 .184 L .441 -.11 L .771 -.073 L .441 .037 L .552 .184 L .294 -.294 L .772 -.441 L 1.103 -.257 L .956 -.515 L .772 -.221 L .515 -.22 L .809 -.074 L 0 .441 L -.515 .11 L -.11 .368 L 1.104 .588 L .809 .294 L 1.287 .772 L 1.066 1.029 L 1.69 2.133 L .846 .882 L 1.104 1.434 L .515 -.257 L .331 -.257 L .367 -.515 L .92 0 L .367 .331 L 0 .368 L .478 0 L .258 .257 L .184 .184 L .589 0 L .992 0 L .993 -.221 L .771 -.221 L .993 -.037 L .698 .441 L .772 .588 L .331 .625 L .956 .147 L .588 .552 L .662 .699 L .882 .073 L .993 .074 L .478 -.368 L .625 -.184 L -.073 .331 L .441 .331 L .294 .478 L .589 0 L .064 .145 L -.551 .034 L -.542 .148 L -.279 .262 L -.011 .275 L -.035 .478 L -.306 .219 L -.289 .06 L -1.199 .093 L -.428 .277 L -.34 .581 L .097 .75 L .213 .707 L -.157 .39 L -.444 .392 L -.417 .103 L -.718 .062 L -1.402 -.079 L -.594 -.141 L -.721 -.141 L -1.096 -.254 L -.427 .507 L -.516 1.141 L 584.2 97.43 l -.286 .605 L -.137 .418 L .622 .514 L .126 .286 L -.156 .245 L -.231 .145 L -.394 .074 L -1.133 -.238 L -.5 -.184 L -.35 .06 L -1.082 .207 L -1.799 .254 L -.393 .188 L -.215 .302 L -.068 .215 L .232 .185 L .366 -.06 L .483 .141 L .03 1.357 L .345 .627 L .29 .441 L .119 .47 L -.222 .33 L -.705 .546 L -.32 .401 L -.02 .399 L .139 .86 L
+386.786 80.184 N -.304 .038 L -.223 .09 L .241 .252 L .361 .771 L .287 1.213 L -.061 .281 L -.359 .341 L -.242 .414 L -.145 .473 L -.185 .044 L -.284 -.058 L -.616 .031 L -.15 .212 L -.913 .042 L -.84 .132 L -.247 .144 L -.661 .286 L -.903 .498 L -.628 .035 L -.879 .283 L -1.28 .084 L .053 -.378 L -.089 -.441 L -.848 .1 L -.171 -.487 L .734 -.254 L -1.186 -.021 L .062 -.233 L 1.286 .027 L .198 -.104 L .039 -.222 L .107 -.31 L .515 -.134 L .692 -.031 L .13 -.281 L -1.07 .099 L .387 -.437 L -.187 -.159 L .481 -.468 L .694 -.011 L .163 -.089 L -.174 -.311 L -.348 .177 L -.309 -.131 L -.319 .03 L -.391 -.177 L -.414 .001 L -.182 .106 L 378 81.478 l .309 -.306 L -.29 -.142 L .759 -.126 L -.139 -.301 L .391 -.235 L -.481 -.214 L -.59 .128 L 378 79.792 l .38 -.268 L .215 -.16 L .928 .187 L .336 -.075 L .527 .038 L 1.102 .123 L -.214 -.358 L 382.132 79 l .198 -.321 L -1.373 0 L .154 -.15 L .569 -.107 L .061 -.29 L .291 -.479 L .505 -.181 L .804 -.169 L .22 .302 L .354 .149 L .156 -.031 L .029 .235 L -.385 .484 L -.817 .107 L .483 .257 L -.86 .278 L .101 .278 L .401 .278 L .778 .192 L .751 -.257 L .253 -.364 L .335 .107 L .26 .257 L .364 .15 L -.123 .256 L .967 -.072 L .178 .272 L
+452.998 85.535 N 85.49 V .037 -.131 L .374 .019 L .261 0 L .112 .112 L .373 -.206 L .374 -.168 L .037 -.168 L .205 -.187 L .485 -.019 L .485 .038 L .354 -.056 L .355 -.056 L .354 .056 L .485 0 L .467 -.075 L .522 .093 L .262 -.019 L .205 .168 L .522 0 L .075 .056 L .578 .056 L .523 .112 L .242 -.075 L .28 .037 L -.019 .224 L .187 0 L 0 .149 L .541 -.112 L .187 .056 L .243 .038 L -.056 .224 L .093 .056 L .168 -.093 L .019 -.205 L .112 0 L .262 .112 L .354 -.056 L .131 -.112 L .224 .093 L .131 .187 L .224 -.112 L .206 0 L .13 .075 L .094 .187 L .168 0 L .131 -.149 L .299 -.149 L .168 0 L .131 -.075 L .13 -.038 L .168 .131 L .112 .131 L .037 .205 L .188 .056 L .316 -.056 L .188 -.093 L .224 -.075 L .149 .131 L .299 -.019 L .242 -.075 L .225 -.037 L .224 .037 L .187 .075 L .112 .056 L .149 .206 L .149 .056 L .261 .019 L .131 -.206 L -.056 -.149 L -.243 -.205 L .057 -.206 L .261 -.224 L .188 -.261 L .317 -.187 L .111 -.112 L .057 -.13 L .336 -.075 L .187 .037 L .205 .056 L .112 -.131 L .522 -.056 L .519 .005 L .357 .089 L .469 .022 L .313 -.156 L .179 -.291 L .134 -.268 L .536 .246 L .536 -.022 L .67 -.223 L .692 .112 L .514 -.134 L .201 .268 L .312 .134 L .246 .335 L .134 .201 L .246 .156 L .312 .156 L 0 .268 L -.312 -.022 L -.312 .134 L .134 .291 L .111 .357 L .269 .29 L .647 0 L .156 .112 L .514 -.067 L .38 .022 L 0 .312 L .402 0 L 0 .357 L .224 .268 L .089 .246 L -.089 .179 L .089 .224 L .179 .089 L .291 .29 L .268 -.179 L .47 -.067 L .268 .067 L .469 .291 L .201 -.067 L .179 .022 L .179 .156 L .425 -.112 L .312 -.112 L .269 0 L .536 -.134 L .357 -.067 L .111 .156 L .268 .179 L 0 .134 L .201 .179 L .022 .134 L .402 .044 L .179 .179 L .224 .112 L .29 -.134 L .045 -.157 L .224 -.067 L .29 .268 L .425 .067 L .469 .112 L .268 .112 L .357 -.067 L .201 .179 L .291 .089 L .469 .022 L .111 .224 L .357 .156 L .269 0 L .134 -.044 L .201 -.089 L .156 .089 L -.089 .111 L -.022 .179 L .111 .089 L .09 .179 L -.045 .224 L -.201 .089 L -.156 .067 L -.357 .201 L -.312 .044 L .223 .246 L .269 .089 L .29 .044 L -.134 .156 L -.312 0 L -.246 0 L -.045 .179 L -.044 .224 L .156 .067 L .179 .067 L .044 .134 L .045 .179 L .09 .201 L .066 .067 L -.156 .491 L -.156 .291 L 0 .156 L -.335 .134 L -.805 -.157 L -.736 .045 L -.269 0 L -.022 .179 L -.223 .179 L -.38 .134 L -.357 .022 L -.224 .089 L -.09 .514 L 0 .224 L -.021 .112 L -.012 .126 L -.779 .104 L -.971 .06 L -.511 .405 L -.729 .189 L -1.135 .075 L -1.119 .248 L -.502 .318 L -.463 .059 L -.453 -.316 L -.369 .621 L -.31 .188 L -.477 .044 L -.438 -.057 L -.959 .031 L -.5 .16 L .641 .287 L 1.957 1.004 L .053 .172 L -.093 .188 L .163 .244 L .562 .042 L .511 -.13 L .675 -.146 L 1.052 .013 L .439 .114 L -.235 .259 L -.106 .245 L -.228 .144 L -.578 .116 L -.31 .029 L -.591 -.157 L -.473 .044 L -.71 .489 L -1.007 .045 L -.538 .188 L -.527 .488 L -.269 .101 L -.786 -.07 L -.588 -.171 L .364 -.746 L -.096 -.416 L -.264 -.287 L -.854 -.286 L -.193 -.014 L -.629 .016 L -.151 .043 L -.16 -.187 L .887 -.505 L .644 -.261 L .772 -.188 L .221 -.116 L -.246 -.46 L -.435 -.071 L -.799 .044 L -1.015 .045 L -.698 -.1 L -.195 -.101 L -.418 -.432 L .584 -.405 L -.528 -.605 L -.378 .361 L -.541 .001 L -1.001 .146 L -.565 .131 L -.694 .722 L -1.003 .867 L -.754 .203 L -.223 .044 L -.287 .504 L .079 .158 L .178 .093 L -.706 -.131 L -.665 .261 L -.457 0 L -.033 .189 L -.609 -.047 L -.398 -.166 L -.119 -.249 L -.15 .02 L .055 -.077 L .102 -.025 L .126 .013 L .113 .013 L .189 0 L .088 -.114 L 0 -.088 L -.063 -.113 L .025 -.113 L .126 -.063 L .051 -.063 L .075 -.013 L .089 -.025 L .088 -.063 L .089 -.088 L .024 -.126 L -.013 -.114 L .14 -.013 L .29 -.063 L .075 -.076 L -.025 -.088 L -.062 -.088 L .126 -.114 L .037 -.063 L -.012 -.088 L -.114 -.113 L .051 -.101 L -.088 -.151 L -.063 -.101 L .202 -.151 L .239 -.025 L .126 -.088 L .113 .025 L .013 .088 L -.013 .214 L .063 .013 L .113 0 L 96.92 V -.013 -.063 L .101 .038 L .063 .051 L .025 -.076 L .075 -.038 L .139 -.012 L 0 .075 L .089 .063 L .075 0 L .126 .164 L .076 -.076 L .075 -.076 L .013 -.05 L .101 -.025 L .177 0 L -.037 .189 L .176 .025 L .038 -.038 L .038 -.038 L .139 .013 L .227 0 L .038 -.025 L .075 -.076 L -.126 -.013 L -.164 -.126 L -.101 -.051 L -.075 -.05 L .013 -.038 L .101 -.063 L -.025 -.113 L .038 -.101 L -.013 -.126 L -.051 -.139 L -.101 -.063 L -.177 -.076 L -.075 0 L -.151 -.126 L -.151 -.063 L -.151 -.038 L .051 -.151 L .037 -.088 L -.037 -.051 L -.127 .038 L -.062 -.114 L .113 -.038 L -.013 -.189 L .089 -.075 L -.025 -.101 L -.038 -.088 L -.113 0 L -.102 .05 L -.088 .051 L -.113 -.088 L -.089 -.101 L -.188 -.101 L -.139 -.025 L -.102 -.139 L -.05 -.139 L .177 -.139 L 0 -.189 L .024 -.114 L .051 -.05 L -.126 -.063 L .164 -.151 L -.113 -.025 L -.076 -.063 L -.062 -.126 L -.14 -.013 L -.062 .101 L -.126 -.025 L -.215 -.025 L -.126 -.189 L -.05 -.189 L -.417 -.075 L -.277 .012 L -.062 .051 L -.076 .101 L -.062 -.05 L 0 -.076 L -.089 -.025 L -.101 .038 L .038 -.05 L .088 -.101 L -.025 -.063 L -.113 0 L -.177 .038 L -.126 -.025 L -.101 .013 L -.076 -.076 L -.05 -.063 L -.101 -.063 L -.151 -.013 L -.139 -.05 L -.14 -.126 L -.214 -.088 L -.038 -.013 L -.126 .025 L -.05 .025 L -.114 -.051 L -.088 -.025 L -.139 .025 L -.177 .051 L -.177 -.025 L -.062 .038 L -.126 .114 L -.202 0 L -.265 -.038 L -.126 .051 L -.315 -.114 L -.088 .101 L .012 .113 L -.126 0 L -.075 -.063 L -.126 .114 L -.06 .052 L -.634 .08 L -.151 .311 L -.278 .178 L -1.992 .191 L -.186 .215 L -.243 .119 L -.339 .06 L -.188 -.227 L -.327 .004 L -.025 -.231 L -.363 .045 L -1.115 -.066 L -.958 -.193 L -.241 .107 L -.787 -.121 L -.136 .085 L -.678 -.387 L -.554 -.2 L -.668 -.301 L -.166 .015 L 1.047 -1.471 L .653 .018 L -.349 -.383 L -.044 -.552 L .082 -.306 L 1.509 -1.218 L .599 -.398 L .286 -.181 L .429 -.013 L .255 -.24 L .009 -.314 L -.328 -.302 L .085 -.133 L .298 -.048 L -.316 -.193 L -.816 -.835 L .074 -.242 L -.161 -.175 L
+660.044 89.132 N -.295 .31 L -.687 1.207 L -1.224 1.959 L -.381 .58 L .269 .836 L .051 .029 L .342 -.045 L .929 -.395 L .754 -.062 L .576 -.018 L .317 .085 L .431 .416 L .292 .07 L 1.191 -.786 L .438 -.002 L .928 .212 L .538 .199 L .797 .5 L .879 .99 L .599 .501 L .048 .273 L -.107 .217 L -.414 .218 L -.464 -.127 L -1.074 -.008 L -.432 -.099 L -.854 .033 L -.937 .221 L -.539 .146 L -.831 .278 L -.353 .189 L -.483 -.127 L -.464 .045 L -.47 .204 L -.363 .333 L -.312 .82 L -.241 .216 L -.347 .188 L -.638 .248 L -.896 .134 L -.624 -.054 L -.438 -.012 L -.224 -.013 L -1.192 .91 L -.742 .433 L -.744 .047 L -.982 .005 L -.592 -.125 L -.668 -.382 L -.718 -.154 L -.316 .073 L -.457 .231 L -.539 .589 L -.214 .401 L .003 .343 L .389 .569 L .599 .411 L .188 .228 L -.123 .271 L -.326 .259 L -1.265 .292 L -.67 .389 L -1.111 1.046 L -.265 .172 L -1.941 .737 L -.651 .061 L -.987 -.08 L -1.514 .065 L -1.339 .007 L -1.204 .349 L -.816 .289 L -.736 .274 L -.303 .101 L -1.44 .534 L -.686 .289 L -.481 .017 L -.433 -.197 L -.253 -.297 L -.61 -.067 L -.663 .061 L -.929 -.123 L -1.599 -.433 L -1.006 -.365 L -.815 -.551 L -.521 -.168 L -1.69 -.119 L -1.164 -.022 L -.937 -.023 L -2.861 .059 L -1.165 -.022 L -.802 -.109 L -1.241 -.207 L -1.979 -.018 L -.444 -.254 L -.467 -.439 L -1.571 -2.161 L -.105 -.542 L -.744 -.096 L -.839 -.31 L -1.645 -.806 L -.632 -.268 L -.998 -.224 L -.668 -.083 L -.995 -.038 L -1.505 -.021 L -1.062 -.181 L -.724 -.312 L -.233 -.229 L -.105 -.43 L .035 -.129 L .369 -.347 L .214 -.389 L .237 -.75 L .215 -.447 L -.401 -.66 L -1.07 -1.451 L -.568 -.618 L -.354 -.143 L -.633 -.144 L -.731 -.167 L -.614 -.069 L -.834 -.415 L -1.301 -.745 L -.371 -.433 L -.24 -.563 L -.131 -.405 L -.062 -.145 L .154 -.044 L .799 -.425 L .599 -.207 L 1.387 -.08 L .603 -.148 L .727 -.381 L .017 -.012 L .971 -.692 L .787 -.398 L 1.143 -.341 L 1.512 -.476 L .84 -.18 L .953 .097 L .932 .156 L 1.842 .122 L .831 .083 L .694 .755 L .393 .406 L .699 .113 L 1.458 -.008 L .719 .083 L .85 -.004 L .875 .068 L .312 .114 L .576 .186 L .562 -.018 L .755 -.28 L .31 -.162 L .744 -.572 L .163 -.526 L -.116 -.204 L -.396 -.304 L -.409 -.86 L .098 -.293 L .905 -.839 L 1.269 -.96 L .84 .201 L 1.028 .098 L 1.036 .185 L 1.748 .328 L .702 .231 L .989 .317 L .767 .143 L .145 .204 L .004 .541 L .182 .481 L .408 .451 L .421 .333 L 1.643 .531 L .673 .113 L 2.48 -.538 L .796 -.077 L 1.172 .037 L 1.423 .022 L .769 .229 L 1.333 .75 L .623 .331 L 1.132 .313 L .812 .373 L 1.318 .254 L .905 .241 L .984 .082 L .739 .039 L 1.602 -.11 L 1.018 -.063 L .532 -.075 L .867 -.106 L 1.147 -.136 L .526 -.163 L .604 -.264 L .447 -.394 L .755 -.498 L 1.165 -.487 L .333 -.002 L .609 -.047 L .74 .156 L .751 .506 L .34 .129 L .86 .169 L 1.228 -.297 L .622 -.018 L .431 .168 L
+406.183 86.551 N 1.051 -.494 L .485 -.089 L .574 .087 L .465 -.016 L .209 -.147 L .477 .098 L .407 .042 L .52 -.034 L -.025 -.157 L .307 .012 L .307 0 L .267 -.182 L .313 .242 L .173 -.121 L .228 .061 L .292 .375 L .535 -.109 L .754 .375 L -.11 .423 L -.172 .097 L .001 .338 L .672 -.024 L .344 .177 L .282 .365 L .038 .468 L -.422 .376 L -.225 -.072 L -.142 .08 L -.245 .147 L -.213 .322 L .017 .327 L .31 .204 L -.136 .348 L -.079 -.114 L -.694 .174 L -.127 -.228 L -.371 -.204 L -.341 -.192 L -.529 -.048 L .039 -.228 L -.146 -.18 L .119 -.373 L -.245 .072 L -.193 .313 L -.446 .035 L -.406 .075 L -.285 -.122 L .072 -.198 L -.091 -.175 L .159 -.241 L -.375 -.168 L -.576 -.048 L -.259 .012 L -.159 -.301 L -.518 .012 L -.194 -.133 L -.202 -.458 L -.153 -.17 L -.41 .208 L -.141 .071 L -.266 -.127 L -.311 -.335 L -.208 -.447 L
+438.22 91.952 N .039 -.044 L .065 -.105 L .014 -.131 L .092 -.066 L .146 -.119 L .026 -.04 L .171 -.053 L .093 -.026 L .092 .053 L .132 .053 L .158 0 L .065 -.026 L .093 0 L .065 .026 L .065 .026 L .093 -.026 L .145 -.04 L .132 0 L .118 -.053 L .079 -.053 L .066 -.026 L .105 -.026 L .039 0 L .053 -.079 L .04 -.092 L .079 -.079 L .092 .026 L .105 -.04 L .145 -.066 L .053 -.105 L .053 -.079 L .026 -.132 L .026 -.092 L .053 -.092 L .118 -.013 L .105 -.013 L .132 -.079 L .119 -.053 L .118 -.092 L .053 -.079 L .132 -.066 L .065 -.04 L 442 89.998 l .145 .013 L .105 .026 L .066 -.04 L .065 -.066 L .071 .012 L .285 .041 L .03 .228 L .43 -.048 L .183 -.24 L .193 .016 L .062 -.112 L .261 -.024 L .194 .24 L .073 .169 L .331 -.025 L .066 .18 L -.026 .083 L .003 .204 L .389 -.083 L .18 .12 L .149 -.135 L .104 -.177 L .558 -.204 L .168 .056 L .483 -.046 L .46 .254 L .373 -.18 L .073 -.137 L .508 .041 L .561 -.076 L .129 .13 L .703 .186 L .104 .216 L .424 .101 L .831 .33 L -1.047 1.471 L -.629 .076 L -.437 -.143 L -.534 -.359 L -1.062 .035 L -.717 .047 L -1.024 .759 L 444.857 93 l -.59 -.072 L -.499 .061 L -.761 .134 L -.255 .001 L -.334 .568 L -1.651 -.036 L -.414 -.027 L -.617 -.17 L -.399 -.172 L -.245 .146 L -.761 -.547 L -.155 -.26 L .097 -.581 L -.053 -.093 L
+442.391 98.111 N -.589 .203 L -.433 .031 L -.668 .047 L -.58 -.098 L -1.116 -.671 L -1.412 -.612 L -.215 -.197 L -.364 -.333 L -.304 -.59 L .346 -.299 L .154 -.294 L -.204 -.188 L .04 -.375 L .409 -.062 L .157 -.206 L -.136 -.196 L -.452 -.063 L .223 -.197 L .325 0 L .164 .134 L .701 -.054 L .019 -.367 L .636 -.291 L .245 -.146 L .399 .172 L .617 .17 L .414 .027 L 1.651 .036 L .334 -.568 L .255 -.001 L .761 -.134 L .499 -.061 L .59 .072 L .427 -.063 L 1.024 -.759 L .717 -.047 L 1.062 -.035 L .534 .359 L .437 .143 L .629 -.076 L .166 -.015 L .668 .301 L .554 .2 L .678 .387 L -.45 .338 L -1.125 .267 L -.581 .408 L -.968 1.451 L -.63 .84 L -.753 .567 L -.361 .16 L -.724 .047 L -.264 .103 L -.176 -.002 L -.907 -.067 L -.889 .077 L -1.535 .529 L
+459.717 92.836 N .06 -.052 L .126 -.114 L .075 .063 L .126 0 L -.012 -.113 L .088 -.101 L .315 .114 L .126 -.051 L .265 .038 L .202 0 L .126 -.114 L .062 -.038 L .177 .025 L .177 -.051 L .139 -.025 L .088 .025 L .114 .051 L .05 -.025 L .126 -.025 L .038 .013 L .214 .088 L .14 .126 L .139 .05 L .151 .013 L .101 .063 L .05 .063 L .076 .076 L .101 -.013 L .126 .025 L .177 -.038 L .113 0 L .025 .063 L -.088 .101 L -.038 .05 L .101 -.038 L .089 .025 L 0 .076 L .062 .05 L .076 -.101 L .062 -.051 L .277 -.012 L .417 .075 L .05 .189 L .126 .189 L .215 .025 L .126 .025 L .062 -.101 L .14 .013 L .062 .126 L .076 .063 L .113 .025 L -.164 .151 L .126 .063 L -.051 .05 L -.024 .114 L 0 .189 L -.177 .139 L .05 .139 L .102 .139 L .139 .025 L .188 .101 L .089 .101 L .113 .088 L .088 -.051 L .102 -.05 L .113 0 L .038 .088 L .025 .101 L -.089 .075 L .013 .189 L -.113 .038 L .062 .114 L .127 -.038 L .037 .051 L -.037 .088 L -.051 .151 L .151 .038 L .151 .063 L .151 .126 L .075 0 L .177 .076 L .101 .063 L .051 .139 L .013 .126 L -.038 .101 L .025 .113 L -.101 .063 L -.013 .038 L .075 .05 L .101 .051 L .164 .126 L .126 .013 L -.075 .076 L -.038 .025 L -.227 0 L -.139 -.013 L -.038 .038 L -.038 .038 L -.176 -.025 L .037 -.189 L -.177 0 L -.101 .025 L -.013 .05 L -.075 .076 L -.076 .076 L -.126 -.164 L -.075 0 L -.089 -.063 L 0 -.075 L -.139 .012 L -.075 .038 L -.025 .076 L -.063 -.051 L -.101 -.038 L .013 .063 L 0 .088 L -.113 0 L -.063 -.013 L .013 -.214 L -.013 -.088 L -.113 -.025 L -.126 .088 L -.239 .025 L -.202 .151 L .063 .101 L .088 .151 L -.051 .101 L .114 .113 L .012 .088 L -.037 .063 L -.126 .114 L .062 .088 L .025 .088 L -.075 .076 L -.29 .063 L -.14 .013 L .013 .114 L -.024 .126 L -.089 .088 L -.088 .063 L -.089 .025 L -.075 .013 L -.051 .063 L -.126 .063 L -.025 .113 L .063 .113 L 0 .088 L -.088 .114 L -.189 0 L -.113 -.013 L -.126 -.013 L -.102 .025 L -.055 .077 L -.03 .004 L -.062 -.237 L -.218 -.106 L .16 -.071 L -.021 -.267 L -.104 -.561 L .323 -.978 L .027 -.404 L -.353 -.856 L -.604 -.286 L -1.037 -1.119 L 460.567 93 l -.626 -.191 L -.225 .028 L
+445.722 97.573 N .176 .002 L .264 -.103 L .724 -.047 L .361 -.16 L .753 -.567 L .63 -.84 L .968 -1.451 L .581 -.408 L 1.125 -.267 L .45 -.338 L .136 -.085 L .787 .121 L .241 -.107 L .958 .193 L 1.115 .066 L .363 -.045 L .025 .231 L .327 -.004 L .188 .227 L .339 -.06 L .243 -.119 L .186 -.215 L 1.992 -.191 L .278 -.178 L .151 -.311 L .634 -.08 L .225 -.028 L 460.567 93 l .767 1.17 L 1.037 1.119 L .604 .286 L .353 .856 L -.027 .404 L -.323 .978 L .104 .561 L .021 .267 L -.16 .071 L .218 .106 L .062 .237 L .03 -.004 L .15 -.02 L .119 .249 L .398 .166 L .609 .047 L .033 -.189 L .457 0 L .665 -.261 L .706 .131 L .149 .079 L .062 .259 L -.293 .446 L -.27 .316 L -.436 .044 L -.382 .043 L -.382 .245 L -.515 .617 L -.252 .645 L -.096 .787 L -.044 .223 L -.671 -.12 L -1.346 -.336 L -.514 -.226 L -.295 -.042 L -.671 -.369 L -.562 -.04 L -.618 .218 L -1.904 .771 L -.38 .059 L -1.385 -.35 L -.3 -.013 L -.69 .261 L -.34 .031 L -1.151 -.395 L -.506 -.002 L -.771 .189 L -.266 .023 L -.048 -.189 L .234 -.318 L -.352 -.106 L -.392 -.204 L -.418 -.186 L -.146 -.33 L .32 -.201 L .351 .012 L -.114 -.13 L -.625 -.248 L -.253 .13 L -.215 .283 L -.147 .118 L -.414 -.239 L -.194 -.139 L -.594 -.059 L -.02 -.189 L -.234 0 L -.245 -.036 L -.052 -.165 L .178 -.094 L .271 -.071 L -.239 -.083 L -.183 -.059 L .124 -.146 L .19 -.127 L -.069 -.142 L -.306 -.118 L -.555 -.141 L -.712 -.471 L .058 -.088 L -.104 -.119 L .075 -.356 L -.202 -.036 L -.19 -.237 L -.569 -.178 L -.054 -.309 L
+420.177 113.472 N -.274 -.042 L -.253 -.155 L -.367 -.325 L -.096 -.213 L .202 -.738 L .097 -.681 L -.046 -.583 L -.133 -.569 L -.503 -.44 L -.094 -.271 L .181 -.157 L .366 -.015 L .801 -.001 L .339 -.172 L .861 -.543 L .633 .625 L .451 .754 L -.014 .271 L -.204 .285 L -.145 .484 L .149 .894 L -.11 .525 L -.377 .695 L -.405 -.198 L -.52 .03 L -.143 .1 L -.149 .27 L -.248 .17 L h 433.783 118.446 m -.712 -.084 L -.902 -.607 L -.772 -.239 L -1.904 -.817 L -.833 -.126 L -.232 -.127 L -.173 -.283 L .139 -.34 L .328 -.34 L .264 -.1 L .629 .112 L .569 -.341 L .68 .424 L .403 .141 L .722 -.016 L 1.403 -.187 L 1.38 -.329 L .148 .085 L .043 .127 L -.112 .127 L -.536 .823 L -.153 .497 L .009 .382 L .411 .509 L -.179 .128 L -.43 .567 L -.188 .015 L h 431.244 98.829 m -.281 -.329 L -.242 -.027 L -.281 .196 L -.156 -.125 L -.47 -.071 L -.114 .32 L -.458 .054 L -1.001 .364 L .078 -.151 L -.452 .133 L -.063 .249 L -.157 .044 L -.01 .125 L .303 .08 L .021 .302 L .193 .119 L .253 .236 L -.104 .213 L -.449 .254 L .016 .272 L .143 .554 L .783 .814 L 2.008 .889 L .29 .357 L .134 .558 L .274 .557 L .395 .585 L .694 .57 L .254 .274 L .446 .195 L .041 .21 L .408 .167 L 1.17 .255 L 1.254 -.105 L .388 .141 L .024 .212 L -.465 .247 L -.258 .294 L .262 .213 L .954 .283 L 1.168 .411 L .829 .366 L 1.589 .739 L .058 .185 L .719 .458 L .31 .475 L -.198 .435 L -.152 .337 L -.455 -.281 L -.318 -.167 L -.109 -.486 L -.263 -.17 L -.512 -.099 L -.483 -.009 L -.439 -.236 L .086 -.217 L -.353 -.065 L -.301 .098 L -.232 .262 L -.259 .399 L -.273 .208 L .043 .271 L -.197 .303 L -.007 .298 L .76 .342 L .611 .271 L -.093 .314 L .03 .432 L .133 .142 L -.191 .238 L -.659 -.024 L -.41 .219 L -.202 .228 L .11 .595 L -.536 .303 L -.617 .866 L -.595 .048 L -.167 -.071 L -.184 -.14 L -.002 -.508 L .364 -.141 L .317 -.542 L -.236 -.184 L .361 -.249 L .361 .074 L .133 -.17 L -.077 -.34 L -.211 -.181 L -.206 -.924 L -.367 -.516 L -.15 -.607 L -.201 -.352 L -.334 .058 L -.187 .171 L -.899 -.496 L -.286 -.065 L .208 -.291 L -.092 -.398 L -.461 -.34 L -.909 .247 L .034 -.109 L .322 -.194 L -.276 -.27 L -.29 -.003 L -.42 .19 L -.242 -.512 L -.198 -.207 L -.124 -.228 L -.663 -.241 L -.505 -.027 L -.654 -.127 L -.745 -.355 L -.548 -.441 L -.959 -.612 L -1.036 -.826 L -.872 -.384 L -.805 -.67 L -.566 -.856 L -.434 -1.043 L -.347 -.443 L -.505 -.457 L -.483 -.243 L -1.188 -.341 L -.579 -.142 L -.5 .044 L -1.078 .647 L -.46 .359 L -.646 .173 L -.303 .043 L .146 -.469 L -.062 -.281 L -.849 .07 L -.754 -.391 L -.193 -.442 L .315 -.371 L .175 -.01 L -.135 -.331 L -.616 -.191 L -.352 -.358 L .437 -.186 L .183 .111 L .541 -.353 L .199 -.272 L -.43 -.192 L -.025 -.292 L -.532 -.344 L .624 -.301 L .599 .062 L .627 -.204 L .629 .168 L .275 -.16 L .349 -.432 L -.103 -.212 L .777 -.404 L .016 .415 L .534 .363 L .311 .071 L -.098 .182 L .385 .312 L .285 -.151 L .018 -.535 L .425 -.384 L -.019 -.333 L .371 -.081 L .143 .354 L .23 .142 L .216 -.03 L .071 -.122 L .469 -.05 L .244 .333 L .228 -.415 L -.244 -.131 L .081 -.273 L .283 -.091 L .176 .162 L .315 .051 L .038 -.192 L -.112 -.212 L .126 -.309 L .631 .171 L .597 .034 L .329 -.411 L .366 -.096 L .183 .083 L .445 -.11 L .301 .103 L .856 -.227 L .023 .363 L .318 .096 L .32 .391 L 1.311 .247 L .894 .082 L .478 .112 L .116 .199 L -.614 .303 L .098 .151 L .297 .002 L .187 .185 L -.367 .285 L .336 .089 L -.127 .361 L .36 .11 L .284 .198 L -.056 .214 L
+430.73 96.731 N 1.04 .065 L .179 .107 L .612 -.009 L .287 .152 L .646 -.5 L .566 -.107 L .85 .08 L .298 -.196 L .89 .116 L -.082 -.393 L .693 -.157 L .304 .59 L .364 .333 L -.035 -.009 L -.1 -.073 L -.145 -.036 L -.172 0 L -.145 .009 L -.055 .063 L 0 .072 L .019 .09 L .009 .082 L -.063 .009 L -.136 -.009 L -.108 -.036 L -.091 .063 L -.045 .082 L -.081 .063 L -.082 .045 L -.081 .009 L -.163 .036 L -.117 .036 L -.108 .036 L -.055 .045 L -.153 -.009 L -.127 .072 L -.063 .054 L -.018 .082 L .036 .072 L .081 .054 L .063 .055 L .045 .045 L .019 .063 L .018 .09 L -.036 .108 L -.018 .063 L -.046 .1 L -.108 0 L -.081 -.009 L -.091 .027 L -.108 .009 L -.117 .054 L -.091 .018 L -.081 .027 L -.1 .045 L -.055 .063 L -.036 .027 L .055 .018 L .063 .009 L .026 .027 L .037 .072 L -.046 .063 L -.027 .009 L -.081 .027 L -.009 .045 L .045 .081 L 0 .072 L .045 .1 L -.054 .072 L -.063 -.018 L -.1 .045 L -.117 .018 L -.127 -.036 L -.063 -.027 L -.1 -.063 L -.099 0 L -.063 -.027 L -.118 -.045 L -.018 .045 L -.027 .045 L -.1 .027 L -.136 0 L -.054 -.045 L -.072 -.063 L -.127 -.018 L -.019 -.09 L -.026 -.018 L -.063 -.054 L -.055 -.027 L -.018 -.054 L -.01 -.054 L -.036 -.009 L -.063 .018 L -.036 .054 L -.009 .027 L -.054 .063 L -.019 .018 L -.018 .081 L -.063 .045 L -.046 .018 L -.062 .054 L -.036 .009 L -.254 0 L -.108 -.027 L -.108 .027 L -.145 .009 L -.1 -.009 L -.1 -.036 L -.045 -.019 L -.055 0 L 0 .037 L 0 .036 L -.045 .027 L -.045 .018 L -.136 -.009 L -.027 -.036 L -.108 .018 L -.019 .018 L -.136 .018 L -.063 .018 L -.126 .018 L -.272 -.063 L .428 -.077 L .113 -.16 L .056 -.214 L -.284 -.198 L -.36 -.11 L .127 -.361 L -.336 -.089 L .367 -.285 L -.187 -.185 L -.297 -.002 L -.098 -.151 L .614 -.303 L -.116 -.199 L
+439.573 104.709 N -1.051 -.672 L -.185 -.222 L -.783 -.149 L -.203 -.159 L -.403 -.115 L -.683 .177 L -.326 -.486 L -1.112 -.627 L -.584 -.678 L .277 .007 L .608 .016 L -.583 -.221 L -.659 -.469 L -.183 -.407 L .086 -.452 L -.289 -.336 L -.646 -.418 L -.378 -.126 L -.258 .579 L -.142 .116 L .03 .15 L -.284 .106 L -.154 .248 L -.213 .053 L -.496 -.647 L -.063 -.286 L -.259 -.612 L .065 -.012 L .272 .063 L .126 -.018 L .063 -.018 L .136 -.018 L .019 -.018 L .108 -.018 L .027 .036 L .136 .009 L .045 -.018 L .045 -.027 L 0 -.036 L 0 -.037 L .055 0 L .045 .019 L .1 .036 L .1 .009 L .145 -.009 L .108 -.027 L .108 .027 L .254 0 L .036 -.009 L .062 -.054 L .046 -.018 L .063 -.045 L .018 -.081 L .019 -.018 L .054 -.063 L .009 -.027 L .036 -.054 L .063 -.018 L .036 .009 L .01 .054 L .018 .054 L .055 .027 L .063 .054 L .026 .018 L .019 .09 L .127 .018 L .072 .063 L .054 .045 L .136 0 L .1 -.027 L .027 -.045 L .018 -.045 L .118 .045 L .063 .027 L .099 0 L .1 .063 L .063 .027 L .127 .036 L .117 -.018 L .1 -.045 L .063 .018 L .054 -.072 L -.045 -.1 L 0 -.072 L -.045 -.081 L .009 -.045 L .081 -.027 L .027 -.009 L .046 -.063 L -.037 -.072 L -.026 -.027 L -.063 -.009 L -.055 -.018 L .036 -.027 L .055 -.063 L .1 -.045 L .081 -.027 L .091 -.018 L .117 -.054 L .108 -.009 L .091 -.027 L .081 .009 L .108 0 L .046 -.1 L .018 -.063 L .036 -.108 L -.018 -.09 L -.019 -.063 L -.045 -.045 L -.063 -.055 L -.081 -.054 L -.036 -.072 L .018 -.082 L .063 -.054 L .127 -.072 L .153 .009 L .055 -.045 L .108 -.036 L .117 -.036 L .163 -.036 L .081 -.009 L .082 -.045 L .081 -.063 L .045 -.082 L .091 -.063 L .108 .036 L .136 .009 L .063 -.009 L -.009 -.082 L -.019 -.09 L 0 -.072 L .055 -.063 L .145 -.009 L .172 0 L .145 .036 L .1 .073 L .035 .009 L .215 .197 L 1.412 .612 L 1.116 .671 L .58 .098 L .668 -.047 L .433 -.031 L .589 -.203 L .201 .142 L .056 .089 L .022 .112 L -.022 .078 L .045 .044 L .011 .067 L -.078 .056 L -.011 .146 L .078 .067 L .145 -.034 L .101 .034 L .045 .089 L -.078 .011 L -.056 -.022 L -.022 .078 L .033 .1 L -.045 .034 L -.044 .022 L .066 .111 L .168 -.022 L .033 .078 L .123 .1 L .122 0 L .101 0 L .09 .078 L .122 .011 L .134 0 L .012 .078 L -.033 .056 L -.135 -.011 L -.089 -.034 L -.067 .022 L -.078 -.011 L -.066 -.045 L -.056 -.011 L -.045 .011 L .033 .067 L -.101 .089 L -.078 0 L 0 .156 L .045 .067 L -.033 .078 L .022 .078 L .011 .078 L -.089 .033 L -.09 -.033 L -.056 .067 L .078 .089 L -.078 .011 L -.189 .022 L -.201 -.022 L -.145 -.123 L .056 -.101 L -.045 -.089 L -.123 -.011 L -.022 -.112 L -.145 -.056 L -.146 -.045 L -.101 .089 L -.1 -.011 L -.156 -.078 L -.067 -.022 L -.146 0 L -.156 -.045 L -.111 .067 L -.134 .045 L -.134 -.045 L -.111 -.067 L -.112 0 L -.122 .089 L -.168 .078 L -.156 -.067 L -.268 -.089 L -.179 .011 L -.156 .011 L -.189 -.056 L -.168 -.011 L -.156 -.089 L -.089 .078 L -.111 .022 L -.057 -.056 L -.234 -.078 L -.156 -.056 L -.134 -.045 L -.089 -.011 L -.134 .123 L -.112 -.011 L -.223 -.022 L -.168 -.033 L -.212 .022 L -.101 .111 L -.145 .145 L -.123 .201 L -.201 -.022 L -.256 -.134 L -.156 -.19 L -.101 -.111 L -.312 -.034 L -.123 .044 L -.089 .179 L -.045 .167 L .045 .134 L 0 .078 L .033 .212 L -.123 .067 L .022 .089 L .134 .078 L .09 .089 L .122 .034 L .101 .033 L .179 .179 L .146 .234 L .089 .134 L .022 .123 L .156 .111 L -.078 .056 L -.012 .1 L .022 .146 L .168 -.011 L .089 .111 L .056 .123 L .112 .111 L .167 .045 L .167 .033 L .369 .357 L .021 .167 L .078 .044 L .213 .078 L .379 .357 L .224 .123 L .223 .067 L .101 .056 L 0 .112 L .078 .279 L .201 .078 L .189 .167 L .146 .112 L .245 .123 L .067 .212 L -.284 .083 L h 439.792 104.833 m .132 -.118 L .134 .011 L .123 .034 L .045 .078 L .066 .089 L .146 .089 L .179 .078 L .212 .011 L .312 .257 L .045 .067 L .134 -.033 L .123 .022 L .089 .034 L .062 .063 L .005 .004 L -.022 .089 L .033 .078 L .082 .072 L .029 .092 L -.002 .1 L -.589 -.367 L -.549 -.371 L -.789 -.378 L
+451.009 101.725 N -.328 .346 L -.383 .374 L -.18 .302 L .056 .271 L 1.326 1.122 L .028 .2 L -.302 .302 L -.762 .333 L -.246 .301 L -.008 .514 L -.013 .208 L -.058 -.017 L -.072 .029 L -.16 .022 L -.145 .021 L -.116 .022 L -.058 .015 L -.102 -.051 L -.087 .043 L -.088 .021 L -.102 -.043 L -.064 -.021 L -.131 .116 L -.087 .08 L -.152 -.015 L -.196 -.007 L -.064 .007 L -.175 -.043 L -.152 .087 L -.151 .102 L -.109 .058 L .059 .072 L -.029 .058 L -.116 0 L -.094 -.109 L -.131 -.058 L -.087 -.073 L -.08 .065 L -.116 .058 L -.246 .058 L -.225 .058 L -.088 .058 L -.058 .167 L .029 .13 L -.029 .072 L -.072 .087 L -.188 0 L -.14 -.049 L -.018 -.109 L -.733 -.866 L -.382 -.369 L -.058 -.004 L .109 -.286 L 0 -.067 L -.078 -.067 L -.101 0 L -.056 -.056 L .022 -.089 L .111 -.033 L .146 .011 L .167 .033 L .057 -.033 L .021 -.067 L .09 -.044 L .134 -.022 L .089 -.011 L -.011 -.089 L -.101 -.101 L -.167 -.067 L -.134 -.045 L -.057 -.044 L -.111 .022 L -.078 -.045 L -.033 -.067 L -.123 -.101 L -.078 -.1 L -.066 -.022 L -.067 .044 L -.078 -.011 L -.101 -.056 L -.279 -.078 L -.078 -.022 L -.056 -.033 L -.167 -.134 L -.101 -.146 L -.111 -.111 L -.168 -.078 L -.156 -.101 L -.223 -.056 L 0 -.101 L .179 -.101 L .089 -.111 L .078 -.011 L .067 .034 L .078 .044 L .1 .022 L .045 -.022 L .012 -.134 L .011 -.19 L -.134 -.145 L -.179 -.19 L -.212 -.134 L -.101 -.145 L .101 .022 L .101 .011 L .145 .056 L .224 .044 L .134 -.078 L .089 -.056 L .067 -.078 L -.089 -.044 L -.135 -.022 L -.089 -.089 L -.123 -.078 L -.156 -.089 L -.033 -.101 L -.045 -.1 L -.212 .011 L -.167 -.056 L -.078 -.1 L -.022 -.134 L .078 -.067 L 0 -.089 L -.033 -.1 L .056 -.056 L .066 -.078 L .156 -.156 L .156 -.223 L .034 -.167 L .056 -.1 L -.022 -.067 L -.123 -.022 L -.179 -.011 L -.156 0 L -.212 .112 L -.078 -.089 L .056 -.067 L .09 .033 L .089 -.033 L -.011 -.078 L -.022 -.078 L .033 -.078 L -.045 -.067 L 0 -.156 L .078 0 L .101 -.089 L -.033 -.067 L .045 -.011 L .056 .011 L .066 .045 L .078 .011 L .067 -.022 L .089 .034 L .135 .011 L .033 -.056 L -.012 -.078 L -.134 0 L -.122 -.011 L -.09 -.078 L -.101 0 L -.122 0 L -.123 -.1 L -.033 -.078 L -.168 .022 L -.066 -.111 L .044 -.022 L .045 -.034 L -.033 -.1 L .022 -.078 L .056 .022 L .078 -.011 L -.045 -.089 L -.101 -.034 L -.145 .034 L -.078 -.067 L .011 -.146 L .078 -.056 L -.011 -.067 L -.045 -.044 L .022 -.078 L -.022 -.112 L -.056 -.089 L -.201 -.142 L 1.535 -.529 L .889 -.077 L .907 .067 L .054 .309 L .569 .178 L .19 .237 L .202 .036 L -.075 .356 L .104 .119 L -.058 .088 L .712 .471 L .555 .141 L .306 .118 L .069 .142 L -.19 .127 L -.124 .146 L .183 .059 L .239 .083 L -.271 .071 L -.178 .094 L .052 .165 L .245 .036 L .234 0 L .02 .189 L .594 .059 L .194 .139 L .414 .239 L .147 -.118 L .215 -.283 L .253 -.13 L .625 .248 L .114 .13 L -.351 -.012 L -.32 .201 L .146 .33 L .418 .186 L
+551.198 117.997 N -.351 -.48 L -.236 -.126 L -1.217 -.05 L -.646 -.011 L -.096 -.016 L .091 -.726 L -.062 -.503 L .157 -.251 L .062 -.22 L -.503 -.094 L -.534 -.283 L -.566 -.189 L -.471 .063 L -.378 -.251 L -1.132 -.597 L -.565 -.22 L -.943 -.597 L -.314 .063 L -1.006 -.503 L -.377 -.44 L -1.194 -.597 L -1.384 -.975 L 0 -.283 L -.188 -.44 L -.283 -.188 L -.408 -.597 L -.126 -.566 L -.22 -.377 L -.881 -.251 L -.188 .157 L -.439 .063 L -.535 -.126 L -.439 .032 L -.503 .094 L -.314 -.157 L -.691 -.314 L .094 -.22 L .157 -.188 L -.188 -.22 L .031 -.188 L .188 -.157 L -.439 -.283 L 0 -.22 L -.032 -.22 L -.251 -.22 L -.534 -.094 L -.692 -.095 L -.22 -.314 L -.346 -.032 L -.629 -.377 L -.472 -.095 L -.188 .063 L -.565 .157 L .251 .251 L .188 .377 L -.597 -.283 L -.283 0 L -.126 .126 L -.22 .346 L -.283 .126 L -.629 0 L -.503 .251 L -.503 .409 L -.062 .628 L .314 .409 L -.126 .314 L -1.383 .032 L -1.03 -.063 L .056 -8.174 L 5.658 -1.289 L 5.722 2.986 L 2.012 1.666 L 2.578 -.346 L 2.767 .188 L 1.1 -.409 L .724 .723 L .597 .22 L .66 1.006 L .66 -.314 L -.031 1.038 L -.188 .409 L .031 .754 L 1.006 0 L .221 .817 L .346 1.069 L .503 .063 L .691 -.032 L 1.006 -.094 L .346 0 L .44 .314 L .062 .283 L -.062 .283 L .44 .346 L .66 0 L .125 -.188 L -.157 -.409 L .504 -.44 L .439 -.188 L .221 -.472 L .282 -.188 L .943 -.377 L .755 -.189 L .534 -.471 L .346 -.283 L .22 -.063 L .283 .126 L .377 -.377 L .322 -.031 L .349 -.126 L .441 .246 L -.368 .172 L -.368 .171 L -.221 .049 L -.073 .196 L -.295 .049 L -.294 .172 L -.196 .147 L -.441 .295 L -.172 .098 L -.024 .123 L .294 .049 L .295 .074 L .146 .123 L .418 -.147 L .098 .221 L .172 .221 L .368 .27 L .589 0 L .393 0 L .049 -.393 L .221 .049 L .196 -.196 L .024 -.245 L .196 .098 L .196 .172 L .172 .294 L .049 .147 L .393 .024 L .147 -.024 L .073 .246 L .025 .098 L .343 -.025 L .319 .147 L .245 .196 L .516 .074 L .466 .024 L .172 .123 L -.49 .221 L -.197 .147 L -.221 .147 L -.49 -.024 L -.245 -.049 L .049 .171 L 0 .147 L -.319 0 L -.172 .049 L -.343 .196 L -.221 .196 L -.271 .049 L -.221 .196 L -.245 -.147 L -.319 -.098 L -.294 -.098 L -.221 .025 L -.246 .073 L -.318 -.073 L -.042 .098 L -.345 -.005 L -.409 .031 L -.188 -.283 L -.251 -.063 L -.126 -.188 L .251 -.126 L .409 -.346 L .188 -.22 L -.252 -.251 L -.439 -.377 L -.221 .251 L -.471 .346 L -.692 .188 L -.22 .157 L -.252 -.22 L -.22 -.157 L -.346 0 L .031 .22 L -.283 .314 L .189 .314 L -.032 .346 L -.062 .126 L -.472 -.095 L -.565 .095 L -.503 .094 L .251 .125 L .534 -.031 L .126 .094 L -.251 .063 L -.188 .063 L -.032 .346 L -.188 0 L -.251 .157 L -.063 .409 L -.282 .188 L -1.069 -.094 L -.629 -.126 L -.472 .283 L -.125 .471 L .251 .283 L .346 .188 L .157 .157 L .44 .032 L .346 0 L .126 .22 L -.126 .22 L -.031 .472 L .126 .409 L .471 .314 L .126 .283 L -.157 .22 L -.503 .346 L -.283 .503 L -.377 .377 L .063 .377 L -.375 .843 L
+439.792 104.833 N -.113 -.054 L -.105 -.07 L .284 -.083 L -.067 -.212 L -.245 -.123 L -.146 -.112 L -.189 -.167 L -.201 -.078 L -.078 -.279 L 0 -.112 L -.101 -.056 L -.223 -.067 L -.224 -.123 L -.379 -.357 L -.213 -.078 L -.078 -.044 L -.021 -.167 L -.369 -.357 L -.167 -.033 L -.167 -.045 L -.112 -.111 L -.056 -.123 L -.089 -.111 L -.168 .011 L -.022 -.146 L .012 -.1 L .078 -.056 L -.156 -.111 L -.022 -.123 L -.089 -.134 L -.146 -.234 L -.179 -.179 L -.101 -.033 L -.122 -.034 L -.09 -.089 L -.134 -.078 L -.022 -.089 L .123 -.067 L -.033 -.212 L 0 -.078 L -.045 -.134 L .045 -.167 L .089 -.179 L .123 -.044 L .312 .034 L .101 .111 L .156 .19 L .256 .134 L .201 .022 L .123 -.201 L .145 -.145 L .101 -.111 L .212 -.022 L .168 .033 L .223 .022 L .112 .011 L .134 -.123 L .089 .011 L .134 .045 L .156 .056 L .234 .078 L .057 .056 L .111 -.022 L .089 -.078 L .156 .089 L .168 .011 L .189 .056 L .156 -.011 L .179 -.011 L .268 .089 L .156 .067 L .168 -.078 L .122 -.089 L .112 0 L .111 .067 L .134 .045 L .134 -.045 L .111 -.067 L .156 .045 L .146 0 L .067 .022 L .156 .078 L .1 .011 L .101 -.089 L .146 .045 L .145 .056 L .022 .112 L .123 .011 L .045 .089 L -.056 .101 L .145 .123 L .201 .022 L .189 -.022 L .078 -.011 L .212 -.112 L .156 0 L .179 .011 L .123 .022 L .022 .067 L -.056 .1 L -.034 .167 L -.156 .223 L -.156 .156 L -.066 .078 L -.056 .056 L .033 .1 L 0 .089 L -.078 .067 L .022 .134 L .078 .1 L .167 .056 L .212 -.011 L .045 .1 L .033 .101 L .156 .089 L .123 .078 L .089 .089 L .135 .022 L .089 .044 L -.067 .078 L -.089 .056 L -.134 .078 L -.224 -.044 L -.145 -.056 L -.101 -.011 L -.101 -.022 L .101 .145 L .212 .134 L .179 .19 L .134 .145 L -.011 .19 L -.012 .134 L -.045 .022 L -.1 -.022 L -.078 -.044 L -.067 -.034 L -.078 .011 L -.089 .111 L -.179 .101 L -.056 -.033 L -.156 .056 L -.112 -.022 L -.066 -.044 L -.112 .033 L -.078 .056 L .012 .078 L .089 .1 L .123 .167 L .056 .101 L -.056 .101 L -.111 0 L -.09 -.056 L -.056 -.089 L -.056 -.044 L -.123 -.011 L -.122 .056 L -.168 .078 L -.045 .101 L -.044 .089 L -.112 .101 L .034 .089 L .011 .1 L 442 104.458 l -.134 .011 L -.111 .022 L -.101 .089 L -.012 .134 L .012 .112 L .011 .145 L .012 .044 L .066 .112 L .078 .089 L .045 .101 L -.09 .089 L -.183 .108 L -.062 -.063 L -.089 -.034 L -.123 -.022 L -.134 .033 L -.045 -.067 L -.312 -.257 L -.212 -.011 L -.179 -.078 L -.146 -.089 L -.066 -.089 L -.045 -.078 L -.123 -.034 L -.134 -.011 L -.132 .118 L
+450.198 105.998 N .013 -.208 L .008 -.514 L .246 -.301 L .762 -.333 L .302 -.302 L -.028 -.2 L -1.326 -1.122 L -.056 -.271 L .18 -.302 L .383 -.374 L .328 -.346 L .392 .204 L .352 .106 L -.234 .318 L .048 .189 L .266 -.023 L .771 -.189 L .506 .002 L 1.151 .395 L .34 -.031 L .69 -.261 L .3 .013 L 1.385 .35 L .38 -.059 L 1.904 -.771 L .618 -.218 L .562 .04 L .671 .369 L .295 .042 L .514 .226 L 1.346 .336 L .671 .12 L -.066 .335 L -.077 .258 L -.261 .086 L -.313 -.028 L -.339 .129 L -.327 .73 L -.039 .586 L -.075 .143 L -.404 .115 L -.338 .372 L -.017 .257 L .252 -.036 L .255 .224 L .033 .154 L .391 .375 L .01 .223 L -1.333 -.005 L -.527 -.111 L -.497 .045 L -.629 .374 L -.498 .445 L -.363 -.026 L -.344 .216 L .097 .327 L -.086 .257 L -1.117 .277 L -.388 .031 L -.619 -.21 L -1.473 -.505 L -.584 .06 L -.799 .261 L -1.855 .195 L -.09 .029 L -.047 -.199 L .104 -.3 L .006 -.499 L -.225 -.469 L -.358 -.383 L -.666 -.296 L -.134 -.213 L .007 -.106 L
+381.009 107 N -.121 -.278 L .138 -.4 L .343 -.5 L -.358 -.471 L -.304 -.428 L -.514 -.07 L -.164 -.1 L -.053 -.329 L .163 -.243 L .409 -.272 L .365 -.101 L .563 -.03 L .634 -.03 L .133 -.172 L .068 -.415 L .535 -.273 L .763 .042 L 1.078 .37 L .763 .07 L .756 -.087 L .577 -.173 L .508 -.144 L .354 -.001 L .629 .285 L .694 .156 L .939 .084 L 1.538 .04 L .583 .027 L .957 .141 L .491 -.158 L .419 -.229 L .531 .027 L .891 .47 L .67 -.016 L .335 .062 L .472 .243 L .469 -.03 L .058 .122 L -.205 .243 L .094 .106 L .15 .03 L .112 -.106 L 1.088 .334 L .15 -.061 L .507 .395 L .056 -.076 L .262 .03 L .131 -.076 L .431 .152 L .028 .038 L .084 .114 L .767 -.03 L .037 .122 L .337 -.061 L .542 .015 L -.017 -.319 L .355 0 L 1.252 .304 L .091 .213 L .035 .289 L .187 .076 L .374 -.076 L .206 -.03 L .335 .091 L .036 .152 L .261 .015 L .395 -.167 L .427 .197 L .485 .015 L .039 -.136 L .75 -.137 L .334 .091 L -.001 .088 L -.001 .463 L .156 .1 L -.062 .485 L -1.112 .528 L -.95 .385 L -.267 .328 L -1.046 .198 L -.664 .116 L -.96 .301 L -.323 .326 L -.053 .2 L .261 .128 L -.088 .157 L -.628 .143 L -.594 .783 L -.886 .787 L -.096 .192 L -.18 .361 L -.245 .45 L .353 .827 L .072 .111 L .084 .13 L .648 .295 L .103 .185 L -.621 .327 L -.215 .105 L -.515 .252 L -.286 .479 L -.224 .085 L -.461 .926 L .155 .322 L -.257 .099 L -.992 .049 L -.581 .242 L -.425 .327 L -.274 .757 L -.663 .496 L -.258 -.213 L -.599 .028 L -.305 .27 L -.342 0 L -.121 -.113 L -3.282 .042 L -.69 .524 L -1.021 .17 L -.35 .382 L -.028 .283 L -.083 .085 L -.073 -.212 L -.068 -.014 L .005 .241 L -.389 .127 L -.421 -.142 L -.788 -.467 L -.224 -.382 L .036 -.262 L -.345 -.113 L -.125 -.213 L .175 -.163 L -.468 -.51 L -.702 -.284 L 385 117.498 l -.484 -.135 L -.586 .039 L .008 -.018 L .304 -.951 L .242 -.37 L .884 -.643 L -.408 -.31 L -.812 -.123 L .17 -.455 L .506 -.655 L .347 -.371 L -.163 -.198 L -.455 -.551 L -.488 -.494 L .288 -.129 L .482 -.045 L .458 -.229 L .043 -.199 L -.057 -.938 L .132 -.983 L -.072 -.456 L .051 -.442 L .084 -.072 L 1.234 -.506 L .288 -.216 L -.062 -.242 L -.842 -.495 L -.15 -.242 L -.272 -.227 L -.335 -.055 L -.531 .26 L 382.981 107 l -.531 -.439 L -.55 .188 L 381.009 107 l
+489.685 103.693 N .112 -.309 L .26 -.166 L .284 .047 L .07 .047 L .402 .023 L .449 .023 L .283 .095 L .284 .142 L .188 .094 L .189 .047 L .331 0 L .213 0 L .212 .166 L .261 .095 L .307 .071 L .355 .047 L .307 0 L .426 -.095 L .544 0 L .401 .166 L .189 0 L .283 -.047 L .354 .166 L .095 .142 L .284 .213 L .52 .118 L .354 .071 L .236 .118 L .308 .119 L -.142 .118 L -.048 .118 L .261 .118 L .212 .071 L .261 -.118 L .283 0 L .166 -.166 L .094 -.095 L .213 -.071 L .354 0 L .261 .071 L .188 .142 L .142 -.166 L .095 -.071 L .118 0 L .236 .118 L .143 .094 L .212 0 L .189 .118 L .213 .166 L .378 0 L .354 .024 L .118 .142 L -.118 .189 L -.118 .307 L .354 .284 L .284 .166 L .26 .094 L .284 .047 L .236 -.023 L .236 .071 L .126 .189 L -.268 .189 L -.143 .142 L -.095 .071 L .143 .26 L .213 .307 L .614 .166 L .118 .213 L -.095 .331 L -.236 .095 L -.236 .047 L -.26 -.189 L -.143 -.071 L -.188 -.023 L -.284 .047 L -.638 -.189 L -.189 -.213 L -.331 -.189 L -.473 -.024 L -.236 0 L -.418 .308 L -.291 .094 L -.378 .047 L -.591 .095 L -.592 -.047 L -.401 .118 L -.426 .023 L -.308 .095 L -.307 -.024 L -.377 .108 L -.031 -.028 L -1.326 -1.018 L -.41 -.041 L -.761 .36 L -.226 .072 L -.491 -.068 L -1.212 -.082 L .083 -.065 L .322 -.585 L .032 -.143 L -.064 -.728 L -.331 -1.084 L -.206 -.399 L -.639 -.513 L -.341 -.128 L -.916 -.155 L -.679 -.271 L -.341 -.243 L
+443.617 107.095 N -.065 -.035 L -.435 -.156 L -.017 -.15 L -.501 -.485 L -.848 -.3 L -.033 -.021 L .002 -.1 L -.029 -.092 L -.082 -.072 L -.033 -.078 L .022 -.089 L -.005 -.004 L .183 -.108 L .09 -.089 L -.045 -.101 L -.078 -.089 L -.066 -.112 L -.012 -.044 L -.011 -.145 L -.012 -.112 L .012 -.134 L .101 -.089 L .111 -.022 L .134 -.011 L .056 -.056 L -.011 -.1 L -.034 -.089 L .112 -.101 L .044 -.089 L .045 -.101 L .168 -.078 L .122 -.056 L .123 .011 L .056 .044 L .056 .089 L .09 .056 L .111 0 L .056 -.101 L -.056 -.101 L -.123 -.167 L -.089 -.1 L -.012 -.078 L .078 -.056 L .112 -.033 L .066 .044 L .112 .022 L .156 -.056 L .056 .033 L 0 .101 L .223 .056 L .156 .101 L .168 .078 L .111 .111 L .101 .146 L .167 .134 L .056 .033 L .078 .022 L .279 .078 L .101 .056 L .078 .011 L .067 -.044 L .066 .022 L .078 .1 L .123 .101 L .033 .067 L .078 .045 L .111 -.022 L .057 .044 L .134 .045 L .167 .067 L .101 .101 L .011 .089 L -.089 .011 L -.134 .022 L -.09 .044 L -.021 .067 L -.057 .033 L -.167 -.033 L -.146 -.011 L -.111 .033 L -.022 .089 L .056 .056 L .101 0 L .078 .067 L 0 .067 L -.109 .286 L -.361 -.022 L -.727 -.11 L -.273 .273 L -.279 .515 L .133 .427 L -.002 .342 L
+558.52 110.652 N .042 -.098 L .318 .073 L .246 -.073 L .221 -.025 L .294 .098 L .319 .098 L .245 .147 L .221 -.196 L .271 -.049 L .221 -.196 L .343 -.196 L .172 -.049 L .319 0 L 0 -.147 L -.049 -.171 L .245 .049 L .49 .024 L .221 -.147 L .197 -.147 L .49 -.221 L -.172 -.123 L -.466 -.024 L -.516 -.074 L -.245 -.196 L -.319 -.147 L -.343 .025 L -.025 -.098 L -.073 -.246 L -.147 .024 L -.393 -.024 L -.049 -.147 L -.172 -.294 L -.196 -.172 L -.196 -.098 L -.024 .245 L -.196 .196 L -.221 -.049 L -.049 .393 L -.393 0 L -.589 0 L -.368 -.27 L -.172 -.221 L -.098 -.221 L -.418 .147 L -.146 -.123 L -.295 -.074 L -.294 -.049 L .024 -.123 L .172 -.098 L .441 -.295 L .196 -.147 L .294 -.172 L .295 -.049 L .073 -.196 L .221 -.049 L .368 -.171 L .368 -.172 L -.441 -.246 L -.349 .126 L -.044 -.273 L .393 -.442 L .318 -.368 L .736 -.123 L .663 -.098 L .883 .147 L .883 .245 L .688 .196 L .81 .123 L .344 .123 L -.024 -.442 L .245 -.736 L .466 -.368 L .688 -.123 L .589 .074 L .761 .27 L .735 .246 L .908 .196 L .54 .098 L .441 -.27 L .858 -.024 L .761 0 L .785 -.147 L .712 .221 L .662 .098 L 1.35 .024 L .662 -.074 L .981 .246 L .564 -.049 L .147 .344 L .27 .147 L .196 .27 L .663 0 L .466 .098 L .41 .375 L .031 .194 L -.051 .157 L -.325 .187 L -.97 .219 L -1.338 .349 L -.445 .145 L -.405 .301 L -.638 .701 L -.646 .345 L -.478 .102 L -.459 .017 L -1.248 -.235 L -.238 .03 L -.467 .472 L -.463 .784 L -.268 .243 L -.885 .132 L -.507 .145 L -.344 -.055 L -.183 -.567 L -.06 -.071 L -.359 .03 L -1.737 .734 L -1.422 .704 L -.274 .186 L -.129 .213 L -.139 .739 L -.196 -.073 L -.344 .098 L -.344 .171 L -.539 0 L -.663 -.073 L -.834 .221 L -.172 .147 L -.196 0 L -.172 -.319 L -.368 .024 L -.318 .172 L -.074 -.221 L -.049 -.172 L -.122 .024 L -.319 -.123 L -.049 -.147 L -.221 -.024 L -.442 .123 L -.343 .049 L .024 .221 L -.295 .049 L -.393 -.074 L -.073 -.196 L -.147 -.123 L -.368 -.098 L -.49 .147 L -.196 -.073 L -.688 .024 L -.564 0 L -.589 .024 L -.122 -.098 L -.049 -.147 L -.099 -.27 L .099 -.245 L .196 -.196 L .098 .221 L .196 -.074 L -.049 -.196 L .098 -.27 L .123 0 L .981 -.196 L .515 .147 L .516 .196 L .099 .172 L .196 0 L .024 -.246 L .441 -.196 L .302 -.147 L
+685.343 114.455 N -.571 .678 L -.309 .115 L -.511 -.096 L -.579 -.068 L -.595 -.011 L -.315 .157 L -.633 .738 L -.283 .256 L -.235 .171 L -.268 -.206 L -.35 .34 L -.319 .199 L -.373 -.608 L -.398 -.112 L -.649 .78 L -.195 -.382 L -.232 -.254 L -.683 -.367 L -.169 -.453 L .095 -.312 L .429 -.411 L .754 -.229 L .056 -.269 L -.591 -.282 L .407 -.879 L .189 -.34 L -.199 -.269 L -.632 -.296 L -.139 0 L -.381 .029 L -.312 .143 L -.234 -.07 L -.52 -.368 L -.167 -.233 L .379 -.528 L .415 -.442 L .52 -.329 L 1.533 -.604 L 1.032 -.545 L .636 -.543 L .686 -1.027 L .386 -.13 L .448 -.017 L .273 .396 L .493 .253 L .508 .153 L .975 -.048 L .527 -.159 L -.046 -.113 L -.508 -.765 L .025 -.342 L .273 -.243 L .392 -.059 L .333 .126 L .452 .054 L .538 -.017 L .62 -.259 L .955 -.532 L .23 -.713 L .383 -.358 L .253 -.129 L .247 -.001 L .579 .68 L .298 .439 L .167 .393 L -1.356 .923 L -.408 .457 L -.112 .414 L .09 .427 L -.154 .456 L -.187 .868 L -.668 .115 L -.36 .229 L -.497 .385 L -.766 .641 L -.468 .214 L -.678 .03 L -.577 .199 L -.265 .228 L -.248 .312 L -.364 .893 L .284 .326 L 1.225 .847 L .419 .354 L
+536.625 121.017 N -.078 -.028 L -.15 -.692 L -.01 -.565 L -.038 -.848 L -.185 -.211 L -.787 .075 L -.696 -.01 L -.655 -.506 L -1.803 -1.362 L -.597 -.336 L -.66 -.167 L -.5 -.054 L -.788 -.066 L -.822 -.335 L -.708 -.251 L -.402 -.437 L -1.055 -.107 L -.519 -.054 L -.343 .129 L -.517 .343 L -.333 .03 L -.78 -.038 L -.609 .032 L -.413 .144 L -.476 .328 L -.621 .654 L -.466 .3 L -.562 .13 L -.441 -.025 L -.066 -.376 L -.128 -.681 L -.106 -.447 L .128 -.298 L 0 -.383 L 0 -.532 L .106 -.191 L .106 -.298 L .085 -.234 L -.085 -.212 L -.256 -.128 L -.319 -.191 L -.213 -.255 L -.042 -.149 L -.171 0 L -.191 -.042 L -.361 -.106 L -.191 .192 L -.086 -.234 L .086 -.106 L .148 -.255 L .128 .106 L .383 -.042 L .426 .085 L .128 .021 L .043 -.128 L -.319 -.213 L -.256 -.021 L -.085 -.277 L .17 -.255 L .213 -.191 L -.404 -.042 L -.319 .085 L -.383 0 L -.319 -.085 L -.128 .149 L -.17 -.255 L -.149 -.298 L 0 -.34 L -.042 -.298 L .17 -.213 L .106 -.319 L .043 -.255 L .105 -.277 L .086 -.234 L .213 .34 L .063 .128 L .17 .17 L .405 -.085 L .383 .128 L .106 -.149 L -.021 -.149 L .106 0 L .148 .021 L .064 .319 L .106 .191 L .298 -.021 L .298 -.063 L .256 -.106 L .233 .085 L .192 .064 L .085 -.128 L -.149 -.191 L -.042 -.213 L .191 -.042 L .106 .149 L .233 .085 L .256 -.085 L .213 -.064 L .021 -.234 L -.171 -.341 L -.34 -.234 L -.532 -.319 L -.426 -.213 L -.063 -.319 L -.043 -.34 L -.213 -.17 L 0 -.213 L 0 -.213 L -.085 -.127 L -.554 -.064 L -.617 .085 L -.426 .021 L -.446 .127 L -.192 .277 L -.085 .298 L .128 .192 L -.063 .276 L -.086 .405 L .064 .234 L .021 .298 L -.256 -.553 L -.361 -.319 L .042 -.17 L -.063 -.191 L -.274 -.143 L .529 -.453 L .937 -.532 L 1.277 -.298 L .979 -.085 L .512 .234 L .681 .383 L .617 .383 L .256 .511 L .638 .703 L .447 .255 L .489 -.043 L .341 -.106 L .158 .014 L 1.03 .063 L 1.383 -.032 L .126 -.314 L -.314 -.409 L .062 -.628 L .503 -.409 L .503 -.251 L .629 0 L .283 -.126 L .22 -.346 L .126 -.126 L .283 0 L .597 .283 L -.188 -.377 L -.251 -.251 L .565 -.157 L .188 -.063 L .472 .095 L .629 .377 L .346 .032 L .22 .314 L .692 .095 L .534 .094 L .251 .22 L .032 .22 L 0 .22 L .439 .283 L -.188 .157 L -.031 .188 L .188 .22 L -.157 .188 L -.094 .22 L .691 .314 L .314 .157 L .503 -.094 L .439 -.032 L .535 .126 L .439 -.063 L .188 -.157 L .881 .251 L .22 .377 L .126 .566 L .408 .597 L .283 .188 L .188 .44 L 0 .283 L 1.384 .975 L 1.194 .597 L .377 .44 L 1.006 .503 L .314 -.063 L .943 .597 L .565 .22 L 1.132 .597 L .378 .251 L .471 -.063 L .566 .189 L .534 .283 L .503 .094 L -.062 .22 L -.157 .251 L .062 .503 L -.091 .726 L -1.454 -.244 L -.565 -.294 L -.445 .356 L -.417 .2 L -1.135 .205 L -.432 .809 L -.203 .991 L -.103 .128 L -.508 .243 L -1.985 .689 L -.568 .159 L -.119 .199 L -.001 .466 L -.22 .199 L -.636 .3 L -.534 .031 L -.573 -.082 L -.999 -.348 L -.937 -.193 L -.193 -.112 L
+445.294 112.196 N -.07 -.115 L -.138 -.469 L -.5 -.452 L -.966 -.541 L .024 -.141 L .23 .062 L .023 -.237 L -.345 -.414 L .418 -.616 L -.182 -.22 L .188 -.563 L -.251 -.282 L .182 -.396 L .268 -.079 L -.027 -.45 L -.331 -.081 L -.2 -.107 L .002 -.342 L -.133 -.427 L .279 -.515 L .273 -.273 L .727 .11 L .361 .022 L .058 .004 L .382 .369 L .733 .866 L .018 .109 L .035 .218 L -.132 .429 L .074 .641 L .298 .668 L .722 .608 L -.09 .029 L -.449 .842 L -.402 .386 L -.496 .472 L -.583 .884 L
+451.512 108.463 N -.507 .16 L -.532 .245 L -.622 -.054 L -.361 -.041 L -.365 .159 L -.395 .429 L -.606 .146 L -.809 .076 L -.722 -.608 L -.298 -.668 L -.074 -.641 L .132 -.429 L -.035 -.218 L .14 .049 L .188 0 L .072 -.087 L .029 -.072 L -.029 -.13 L .058 -.167 L .088 -.058 L .225 -.058 L .246 -.058 L .116 -.058 L .08 -.065 L .087 .073 L .131 .058 L .094 .109 L .116 0 L .029 -.058 L -.059 -.072 L .109 -.058 L .151 -.102 L .152 -.087 L .175 .043 L .064 -.007 L .196 .007 L .152 .015 L .087 -.08 L .131 -.116 L .064 .021 L .102 .043 L .088 -.021 L .087 -.043 L .102 .051 L .058 -.015 L .116 -.022 L .145 -.021 L .16 -.022 L .072 -.029 L .058 .017 L -.007 .106 L .134 .213 L .666 .296 L .358 .383 L .225 .469 L -.006 .499 L -.104 .3 L .047 .199 L
+383.93 117.402 N -.249 .101 L -.517 .291 L -.439 .052 L -.548 -.178 L -.58 0 L -.28 -.073 L -.719 .292 L -.058 -.177 L .389 -1.012 L -.021 -.856 L -.182 -.115 L .244 -.542 L -.054 -.397 L .13 -.114 L -.144 -.141 L -.375 .085 L -.476 .097 L -.108 -.449 L .48 -.052 L .283 -.22 L -.042 -.17 L -.178 -.226 L -.3 .417 L -.413 .136 L -.357 -.042 L -.059 -.188 L .198 -.397 L .138 -.616 L -.039 -.303 L .258 -.114 L .403 -.503 L .45 -1.098 L -.12 -.115 L .612 -1.783 L -.35 -.924 L -.007 -.42 L -.146 -.378 L .255 -.271 L .891 -.251 L .55 -.188 L .531 .439 L 1.822 .047 L .531 -.26 L .335 .055 L .272 .227 L .15 .242 L .842 .495 L .062 .242 L -.288 .216 L -1.234 .506 L -.084 .072 L -.051 .442 L .072 .456 L -.132 .983 L .057 .938 L -.043 .199 L -.458 .229 L -.482 .045 L -.288 .129 L .488 .494 L .455 .551 L .163 .198 L -.347 .371 L -.506 .655 L -.17 .455 L .812 .123 L .408 .31 L -.884 .643 L -.242 .37 L -.304 .951 L -.008 .018 L
+500.121 117.572 N -.407 -.016 L -.433 .388 L -.164 .126 L -.318 -.105 L -.102 -.269 L .03 -.259 L -.274 -.151 L -.366 -.082 L -.244 .234 L -.343 -.023 L -.811 -.153 L -.364 .032 L -.304 -.16 L -.437 .094 L -.266 .143 L -.23 .043 L -.064 -.245 L -.207 -.023 L -.24 .292 L -.693 .304 L -1.185 .224 L -.711 -.039 L -.747 -.123 L -.439 .073 L -1.498 .673 L -.567 .13 L -1.104 .176 L -.556 -.153 L -1.532 -.444 L -.278 .03 L -.929 .373 L -.746 .075 L -.575 -.025 L -.777 -.166 L -.222 .001 L -.142 -.035 L -.055 .319 L .102 .452 L .243 .423 L -.627 .127 L -.156 .374 L -.2 .169 L -.171 -.041 L -.114 .127 L -.39 -.125 L -.311 .001 L -.245 -.459 L -.119 -.093 L .097 -.175 L .242 -.197 L .617 -.403 L .021 -.175 L -.049 -.134 L -.279 -.28 L -.146 -.053 L -.487 .368 L -.23 .041 L -.137 .064 L .092 .041 L -.118 .216 L -.172 .023 L -.063 -.047 L -.076 .088 L -.297 .058 L -.332 -.222 L -.447 -.198 L -.461 -.157 L -.395 .046 L -.849 .548 L -.337 .286 L .006 .204 L -.141 .046 L -.122 .07 L -.005 .082 L -.179 -.169 L -.604 .206 L -.689 .185 L -.594 -.013 L -.587 -.07 L -.678 -.267 L -.963 -.819 L -1.181 -.479 L -1.034 -.182 L -.692 .072 L -.119 .255 L -.097 .609 L -.053 .411 L -.173 .156 L -.256 0 L -.253 -.155 L -1.12 .243 L -.423 -.027 L -.386 -.183 L -.657 -1.159 L -.42 .354 L -.764 -.451 L -.451 .057 L -.562 .412 L -.227 -.382 L .066 -.127 L .242 -.17 L -.116 -.17 L -.989 -.012 L -.545 -.013 L -.088 -.269 L .571 -.199 L -.074 -.241 L -.284 -.198 L -.454 -.07 L -.084 -.297 L .041 -.34 L .087 -.284 L -.089 -.255 L -.396 -.126 L -.627 -.353 L -.371 .086 L -.265 -.084 L -.004 -.255 L .171 -.501 L .131 .059 L .478 .311 L .567 -.271 L -.396 -.283 L .021 -.124 L -.296 -.128 L .03 -.128 L .571 -.159 L .152 -.113 L -.068 -.142 L -.149 -.088 L -.337 -.035 L .01 -.187 L .18 -.07 L -.163 -.164 L -.198 -.117 L -.009 -.152 L -.227 -.012 L .263 -.181 L .296 -.275 L .161 -.035 L .07 -.16 L -.341 -.042 L -.573 .12 L -.905 .164 L -.166 -.035 L .046 -.33 L .127 -.125 L -.003 -.199 L -.029 -.286 L .13 -.264 L .299 .012 L .184 -.41 L .175 -.023 L .63 -.422 L .514 .012 L .133 -.129 L .479 -.047 L .128 .211 L .268 .102 L .169 .028 L .529 .022 L .147 -.129 L -.067 -.129 L -.269 -.129 L .286 -.094 L .324 .036 L .117 .082 L -.219 .223 L .213 -.026 L 1.053 -.073 L .619 .042 L .379 .046 L .279 .047 L .155 -.176 L -.086 -.094 L -.468 -.035 L -.212 -.118 L .275 -.212 L 1.386 -.151 L .417 -.012 L .377 -.117 L -.442 -.012 L -.592 .023 L -.215 0 L -.068 -.146 L -.611 -.382 L .325 -.528 L .926 .14 L 1.244 .048 L .264 -.117 L 1.086 .321 L 1.051 -.031 L .414 -.243 L -.041 -.27 L .624 -.244 L .455 -.214 L 1.218 -.573 L .598 -.215 L 1.039 -.23 L .889 -.073 L .758 .07 L .905 .126 L .798 .041 L .753 -.372 L .216 .527 L .416 .298 L .278 .099 L .592 .013 L .622 -.144 L .453 .74 L .492 .255 L .574 -.172 L .391 .056 L .968 .582 L 1.265 .04 L 1.094 .197 L .749 -.001 L 1.084 -.272 L .514 -.044 L .651 .141 L .764 .098 L .787 -.016 L .554 -.144 L 1.518 -.573 L .424 -.335 L 1.212 .082 L .491 .068 L .226 -.072 L .761 -.36 L .41 .041 L 1.326 1.018 L .031 .028 L .795 .722 L .026 .199 L -.421 .813 L .033 .412 L .284 .211 L 1.413 .12 L .492 .451 L -.072 .211 L -.409 -.023 L -.231 .141 L -.009 .433 L -.584 .267 L -.039 .27 L .264 .67 L -.122 .375 L .224 .492 L .09 .117 L .106 -.105 L .288 .203 L .039 .207 L -.229 .281 L -.287 .535 L -.06 .128 L .213 .14 L .424 .111 L -.145 .245 L .099 .421 L .42 .374 L .275 .035 L .023 .308 L h 462.617 106.804 m .241 .211 L -.019 .287 L .115 .285 L .077 .071 L .593 .355 L .819 .241 L .605 .155 L .152 .121 L 464.943 109 l -.304 .166 L -.515 -.072 L -.94 -.246 L -.326 .07 L -.209 .152 L -1.019 -.012 L -.357 .384 L -.109 .273 L -.833 .316 L -.612 .282 L -.222 .258 L -.307 .152 L -.268 .293 L -.255 .082 L .164 -.258 L .019 -.141 L -.062 -.176 L .584 -.293 L .22 -.141 L -.226 -.191 L -.082 .015 L -.653 -.056 L -.229 -.148 L .326 -.546 L .387 -.558 L .678 -.631 L -.127 -.227 L -.427 -.197 L -.105 0 L .498 -.445 L .629 -.374 L .497 -.045 L .527 .111 L 1.333 .005 L
+509.077 114.955 N -.72 -.317 L -.268 .016 L -.356 -.433 L -.374 -.105 L -.13 -.363 L .532 -.27 L .095 -.222 L -.43 -.176 L -.027 -.188 L .63 -.129 L .094 -.155 L -.061 -.113 L -.487 -.21 L -.351 -.281 L -.306 -.166 L -.456 .234 L -1.058 .492 L -.374 .445 L -.642 .188 L -.254 .255 L -.014 -.027 L .094 -.118 L -.094 -.213 L -.189 -.071 L .26 -.095 L .166 -.047 L -.261 -.189 L -.236 -.236 L .236 -.118 L .095 -.189 L -.283 -.047 L -.354 -.024 L -.284 -.118 L -.213 -.212 L -.236 -.024 L -.26 -.354 L -.283 -.142 L -.048 -.094 L .166 0 L .378 0 L .165 -.236 L 0 -.236 L -.213 -.024 L -.188 -.142 L -.544 -.331 L -.283 -.354 L .047 -.284 L .402 -.142 L -.119 -.236 L -.212 -.166 L -.426 -.071 L -.284 -.095 L .071 -.094 L .071 -.118 L -.284 -.095 L -.087 -.212 L .418 -.308 L .236 0 L .473 .024 L .331 .189 L .189 .213 L .638 .189 L .284 -.047 L .188 .023 L .143 .071 L .26 .189 L .236 -.047 L .236 -.095 L .095 -.331 L -.118 -.213 L -.614 -.166 L -.213 -.307 L -.143 -.26 L .095 -.071 L .143 -.142 L .268 -.189 L .229 -.023 L .023 .166 L .213 -.047 L .189 0 L .142 .189 L .473 .284 L .095 .118 L .118 0 L .283 .284 L 0 .308 L .591 .094 L .449 .142 L .379 -.047 L .165 -.213 L .308 -.331 L .283 -.094 L .496 -.284 L .292 -.449 L .465 .331 L .236 .378 L .26 .189 L .284 .307 L .095 .52 L .142 .236 L .283 .26 L .284 .165 L 0 .166 L .449 .236 L .473 -.047 L .378 .071 L .284 .166 L .236 .189 L .095 .189 L 0 .142 L -.355 -.142 L -.401 -.047 L -.213 0 L -.26 .047 L -.142 .118 L -.402 .071 L -.213 .142 L -.047 .189 L -.023 .473 L -.118 .26 L -.095 .236 L -.095 .378 L .213 .236 L -.023 .189 L -.237 -.071 L -.094 .095 L -.071 .331 L -.071 .26 L -.118 -.047 L -.094 -.236 L -.143 -.095 L -.165 .095 L -.047 .307 L .07 .166 L -.118 .118 L -.118 .095 L .095 .26 L -.363 .91 L h 499.844 111.738 m .709 .061 L .142 -.047 L .26 -.071 L .236 .236 L .071 .166 L .378 .142 L .213 .071 L .308 -.118 L .52 0 L -.071 .213 L .024 .236 L .118 .023 L .331 .166 L -.071 .236 L .421 .763 L -.009 .001 L -.253 -.133 L -.416 .038 L -.512 -.025 L -.421 -.125 L -.335 -.211 L -.294 -.402 L -.551 -.223 L -.281 -.417 L -.265 -.381 L -.252 -.197 L
+455.452 122.442 N .049 -.209 L -.057 -.128 L -.812 -.256 L -.691 -.006 L -.506 -.116 L -.484 .017 L -.121 -.046 L -.103 -.093 L .139 -.56 L .315 -.005 L -.005 -.088 L -.009 -.122 L .069 -.07 L .083 .157 L .021 .146 L .303 .021 L .172 .055 L .184 -.076 L -.014 -.082 L .108 -.029 L .157 .105 L -.037 .093 L -.099 .006 L -.04 .053 L .088 .023 L .144 .035 L .094 .046 L .021 .128 L .353 .041 L .846 -.122 L .509 .016 L .035 .13 L .192 .035 L .608 .064 L .307 .051 L .358 -.121 L .09 .05 L -.101 .312 L .163 .11 L .105 0 L .325 -.169 L .286 -.058 L .078 .052 L .154 -.07 L .232 -.146 L -.083 .187 L .015 .186 L -.183 .268 L -.582 -.046 L -.349 .081 L -.335 -.017 L -1.994 .169 L h 445.294 112.196 m .583 -.884 L .496 -.472 L .402 -.386 L .449 -.842 L .09 -.029 L .809 -.076 L .606 -.146 L .395 -.429 L .365 -.159 L .361 .041 L .622 .054 L .532 -.245 L .507 -.16 L .09 -.029 L 1.855 -.195 L .799 -.261 L .584 -.06 L 1.473 .505 L .619 .21 L .388 -.031 L 1.117 -.277 L .086 -.257 L -.097 -.327 L .344 -.216 L .363 .026 L .105 0 L .427 .197 L .127 .227 L -.678 .631 L -.387 .558 L -.326 .546 L -.062 -.407 L -.794 -.056 L -.743 -.041 L -.566 -.125 L -.062 -.144 L -.459 .186 L -.248 .123 L -.403 .012 L -.031 -.247 L -.335 .029 L -.301 .314 L -.431 .186 L -.31 .03 L -.306 -.159 L -.252 .07 L -.004 .133 L .169 .185 L .169 .34 L .308 .059 L .826 .609 L -.166 .07 L -.369 -.258 L -.015 -.105 L -.276 -.082 L -.331 -.105 L -.116 .099 L -.211 .007 L .069 .129 L -.016 .129 L .338 .164 L .145 -.012 L .114 .234 L -.03 .129 L -.245 .023 L -.445 -.457 L -.341 -.141 L -.207 -.059 L -.128 -.012 L .003 .094 L -.075 .035 L .138 .164 L .102 .105 L .154 .141 L .193 .059 L .153 .035 L .103 .094 L -.093 .058 L -.494 -.046 L -.253 -.035 L .035 -.176 L -.137 -.293 L -.164 -.188 L -.401 -.108 L -.472 -.373 L .258 -.118 L .025 -.136 L -.053 -.122 L -.182 -.035 L -.153 .199 L -.465 .176 L .245 .224 L -.25 .371 L -.05 .249 L .13 .121 L .065 .172 L .311 .338 L .133 .036 L .131 .479 L .579 .421 L .359 .467 L -.172 .14 L -.237 .082 L .106 -.187 L -.121 -.187 L -.142 -.128 L -.139 -.035 L -.151 -.047 L -.29 .175 L .102 .188 L .153 .081 L .08 .316 L -.193 .187 L -.652 .141 L .248 .046 L .27 .14 L .391 .058 L .188 .222 L .257 -.012 L .155 .012 L .048 .126 L .367 .269 L .306 .014 L .138 .292 L .282 .012 L .27 0 L .348 .303 L .015 .128 L -.193 .082 L .238 .782 L -.153 .175 L -.185 0 L -.226 -.385 L -.222 -.047 L -.207 -.278 L -.101 -.142 L -.17 0 L -.496 .14 L -.479 .105 L -.184 .128 L .315 .093 L .013 .188 L .007 .291 L .229 .117 L .153 -.026 L .225 -.079 L -.021 .198 L .235 .175 L -.519 .093 L .002 .117 L -.169 .062 L -.309 -.086 L .121 -.21 L -.186 -.086 L -.508 -.056 L -.158 -.092 L -.008 .206 L .194 .453 L .193 .17 L -.045 .163 L .209 .204 L .213 .96 L -.688 -.31 L -.331 .071 L -.298 .439 L -.442 -.735 L -.46 -.367 L -.452 .44 L -.428 -.353 L -.127 -.297 L .212 -.425 L -.028 -.241 L -.215 -.269 L -.491 -.424 L -.167 -.226 L .017 -.17 L .471 -.61 L .609 .098 L .425 -.298 L .202 .042 L 1.668 .663 L .337 -.1 L .483 -.355 L -.266 -.049 L -.27 -.056 L -1.204 -.493 L -1.127 -.083 L -.367 .058 L -.66 .058 L -.427 .143 L -.89 -1.118 L .269 -.1 L .253 .056 L .218 -.114 L .122 -.185 L -.339 -.24 L -.235 .114 L -.496 -.042 L -1.035 -.721 L -.199 -.325 L
+504.136 113.458 N -.327 .328 L -.377 .03 L -.421 -.763 L .071 -.236 L -.331 -.166 L -.118 -.023 L -.024 -.236 L .071 -.213 L -.52 0 L -.308 .118 L -.213 -.071 L -.378 -.142 L -.071 -.166 L -.236 -.236 L -.26 .071 L -.142 .047 L -.709 -.061 L -.492 -.451 L -1.413 -.12 L -.284 -.211 L -.033 -.412 L .421 -.813 L -.026 -.199 L -.795 -.722 L .377 -.108 L .307 .024 L .308 -.095 L .426 -.023 L .401 -.118 L .592 .047 L .591 -.095 L .378 -.047 L .291 -.094 L .087 .212 L .284 .095 L -.071 .118 L -.071 .094 L .284 .095 L .426 .071 L .212 .166 L .119 .236 L -.402 .142 L -.047 .284 L .283 .354 L .544 .331 L .188 .142 L .213 .024 L 0 .236 L -.165 .236 L -.378 0 L -.166 0 L .048 .094 L .283 .142 L .26 .354 L .236 .024 L .213 .212 L .284 .118 L .354 .024 L .283 .047 L -.095 .189 L -.236 .118 L .236 .236 L .261 .189 L -.166 .047 L -.26 .095 L .189 .071 L .094 .213 L -.094 .118 L .014 .027 L
+566.651 117.4 N -.565 -.153 L -.496 -.054 L -.264 -.151 L -.564 .227 L -.974 .147 L -.137 -.059 L .129 -.176 L -.198 -.077 L -.678 .03 L -.739 .315 L -.592 .486 L -.589 .064 L -.745 .495 L -.351 .03 L -.368 -.026 L -.128 -.084 L -.164 -.409 L -.199 -.521 L .185 -.444 L .099 -.775 L .029 -.255 L -.17 -.187 L -.484 .093 L .156 -.597 L -.576 -.45 L -.153 -.056 L -.384 .016 L -.286 .162 L -.134 .363 L -.435 .428 L -.049 .425 L .006 .255 L -.208 .228 L -.442 .158 L -.133 -.013 L -.587 -.152 L -.292 .058 L -.073 .185 L .007 .311 L -.3 .313 L -.21 .128 L -.381 .016 L -.63 -.237 L -.325 .001 L -.581 .286 L -.58 .343 L -.485 .144 L -.245 -.041 L -.129 -.141 L -.04 -.055 L .375 -.843 L -.063 -.377 L .377 -.377 L .283 -.503 L .503 -.346 L .157 -.22 L -.126 -.283 L -.471 -.314 L -.126 -.409 L .031 -.472 L .126 -.22 L -.126 -.22 L -.346 0 L -.44 -.032 L -.157 -.157 L -.346 -.188 L -.251 -.283 L .125 -.471 L .472 -.283 L .629 .126 L 1.069 .094 L .282 -.188 L .063 -.409 L .251 -.157 L .188 0 L .032 -.346 L .188 -.063 L .251 -.063 L -.126 -.094 L -.534 .031 L -.251 -.125 L .503 -.094 L .565 -.095 L .472 .095 L .062 -.126 L .032 -.346 L -.189 -.314 L .283 -.314 L -.031 -.22 L .346 0 L .22 .157 L .252 .22 L .22 -.157 L .692 -.188 L .471 -.346 L .221 -.251 L .439 .377 L .252 .251 L -.188 .22 L -.409 .346 L -.251 .126 L .126 .188 L .251 .063 L .188 .283 L .409 -.031 L .345 .005 L -.302 .147 L -.441 .196 L -.024 .246 L -.196 0 L -.099 -.172 L -.516 -.196 L -.515 -.147 L -.981 .196 L -.123 0 L -.098 .27 L .049 .196 L -.196 .074 L -.098 -.221 L -.196 .196 L -.099 .245 L .099 .27 L .049 .147 L .122 .098 L .589 -.024 L .564 0 L .688 -.024 L .196 .073 L .49 -.147 L .368 .098 L .147 .123 L .073 .196 L .393 .074 L .295 -.049 L -.024 -.221 L .343 -.049 L .442 -.123 L .221 .024 L .049 .147 L .319 .123 L .122 -.024 L .049 .172 L .074 .221 L .318 -.172 L .368 -.024 L .172 .319 L .196 0 L .172 -.147 L .834 -.221 L .663 .073 L .539 0 L .344 -.171 L .344 -.098 L .196 .073 L -.011 .069 L .023 1.031 L -.207 .223 L .077 .305 L .325 .394 L .463 -.045 L .229 -.162 L .22 .06 L .692 .039 L .273 .154 L .295 .494 L -.009 .284 L .028 .246 L .152 .012 L .049 .123 L -.126 .428 L .245 .237 L -.152 .36 L .2 .163 L -.181 .185 L -.08 .249 L -.354 .136 L
+500.121 117.572 N -.023 -.308 L -.275 -.035 L -.42 -.374 L -.099 -.421 L .145 -.245 L -.424 -.111 L -.213 -.14 L .06 -.128 L .287 -.535 L .229 -.281 L -.039 -.207 L -.288 -.203 L -.106 .105 L -.09 -.117 L -.224 -.492 L .122 -.375 L -.264 -.67 L .039 -.27 L .584 -.267 L .009 -.433 L .231 -.141 L .409 .023 L .072 -.211 L .252 .197 L .265 .381 L .281 .417 L .551 .223 L .294 .402 L .335 .211 L .421 .125 L .512 .025 L .416 -.038 L .253 .133 L .009 -.001 L .377 -.03 L .327 -.328 L .254 -.255 L .642 -.188 L .374 -.445 L 1.058 -.492 L .456 -.234 L .306 .166 L .351 .281 L .487 .21 L .061 .113 L -.094 .155 L -.63 .129 L .027 .188 L .43 .176 L -.095 .222 L -.532 .27 L .13 .363 L .374 .105 L .356 .433 L .268 -.016 L .72 .317 L .015 .007 L -.05 .707 L -.143 .581 L .205 .48 L .494 .252 L .925 .235 L .827 .052 L .424 .097 L .162 .282 L .312 .451 L .687 .463 L 1.902 .513 L .841 .052 L .438 -.059 L 1.354 -.262 L 1.192 -.148 L 1.469 -.079 L .41 -.229 L .185 -.354 L -.131 -.905 L .015 0 L .441 .025 L .562 -.13 L .466 -.3 L .621 -.654 L .476 -.328 L .413 -.144 L .609 -.032 L .78 .038 L .333 -.03 L .517 -.343 L .343 -.129 L .519 .054 L 1.055 .107 L .402 .437 L .708 .251 L .822 .335 L .788 .066 L .5 .054 L .66 .167 L .597 .336 L 1.803 1.362 L .655 .506 L .696 .01 L .787 -.075 L .185 .211 L .038 .848 L .01 .565 L .15 .692 L .078 .028 L -.145 .241 L -.084 .339 L -.246 .807 L -.49 1.272 L -.222 .297 L -.596 .384 L -.016 .141 L .119 .663 L .096 .098 L .738 .235 L .026 .183 L -.661 .935 L -.034 .155 L .254 1.085 L .167 1.283 L .143 .775 L .191 .21 L .209 .041 L 1.198 .275 L .401 .167 L .144 .366 L .046 .437 L -.425 .553 L -.853 .795 L -.853 1.034 L .802 1.083 L .71 1.068 L .353 .464 L .695 .391 L 1.144 .388 L .409 .224 L .168 .38 L .111 1.34 L .185 .394 L .652 .053 L .186 .281 L -.036 .974 L -.188 .255 L -.209 .072 L -1 .077 L -.697 .258 L -.794 .47 L -.285 .383 L -.31 .792 L -.049 .354 L -.182 .954 L -.502 .028 L -1.079 -.153 L -.236 -.197 L -.605 -.253 L -.403 -.056 L -1.43 .003 L -.783 -.041 L -.602 .072 L -.475 -.38 L -.163 -.126 L -.835 -.026 L -.576 .001 L -.465 .014 L -.212 -.239 L -.756 -.125 L -.305 -.183 L -.162 -.014 L -.021 -.5 L -.295 -.128 L -.103 -.514 L -.292 -.349 L -.013 -.639 L -.309 -.493 L -.237 .012 L -.035 -.181 L -.526 -.126 L -.807 -.013 L -.374 .017 L -.209 .222 L -.329 .018 L -.517 .075 L -.188 .364 L -.538 .138 L -.383 .443 L -.368 .283 L -.253 .043 L -1.292 -.689 L -.958 -.104 L -.562 -.359 L -1.088 -.317 L -.247 -.301 L -.324 -.282 L -.497 -.592 L -.997 -.436 L -.584 -.083 L -.194 -.028 L -.58 -.465 L -.596 -1.058 L -.635 -1.114 L -.209 -.268 L .005 -.593 L -.767 -.761 L -.506 -.719 L -.921 .143 L -.46 -.042 L -.13 -.126 L -.291 -.056 L -.191 -.268 L -.029 -.565 L -.448 .1 L -.166 .099 L -.32 .678 L -.195 .184 L -.355 .012 L -.014 -.12 L -.351 -.224 L -.686 -.546 L .064 -.212 L -.007 -.395 L -.164 -.465 L -.215 -.013 L -.551 .003 L -.034 -.325 L .055 -.579 L .197 -.622 L .014 -.508 L -.112 -.239 L -.29 -.28 L -.774 -.603 L -.436 -.209 L -1.242 -.925 L -.533 -.025 L -.321 .115 L 503 127.106 l .033 -.819 L -1.02 -.954 L -.312 -.351 L -.002 -.184 L .133 -.875 L .235 -.763 L 1.142 -.98 L -.422 -.761 L .013 -.254 L .468 -.596 L -1.067 -.107 L -.761 -.208 L -.065 -.198 L -.563 -1.086 L -.69 -1.397 L
+535.734 133.791 N .853 -1.034 L .853 -.795 L .425 -.553 L -.046 -.437 L -.144 -.366 L -.401 -.167 L -1.198 -.275 L -.209 -.041 L -.191 -.21 L -.143 -.775 L -.167 -1.283 L -.254 -1.085 L .034 -.155 L .661 -.935 L -.026 -.183 L -.738 -.235 L -.096 -.098 L -.119 -.663 L .016 -.141 L .596 -.384 L .222 -.297 L .49 -1.272 L .246 -.807 L .084 -.339 L .145 -.241 L .193 .112 L .937 .193 L .999 .348 L .573 .082 L .534 -.031 L .636 -.3 L .22 -.199 L .001 -.466 L .119 -.199 L .568 -.159 L 1.985 -.689 L .508 -.243 L .103 -.128 L .203 -.991 L .432 -.809 L 1.135 -.205 L .417 -.2 L .445 -.356 L .565 .294 L 1.454 .244 L .096 .016 L .646 .011 L 1.217 .05 L .236 .126 L .351 .48 L .04 .055 L .129 .141 L .245 .041 L .485 -.144 L .58 -.343 L .581 -.286 L .325 -.001 L .63 .237 L .381 -.016 L .21 -.128 L .3 -.313 L -.007 -.311 L .073 -.185 L .292 -.058 L .587 .152 L .133 .013 L .442 -.158 L .208 -.228 L -.006 -.255 L .049 -.425 L .435 -.428 L .134 -.363 L .286 -.162 L .384 -.016 L .153 .056 L .576 .45 L -.156 .597 L .484 -.093 L .17 .187 L -.029 .255 L -.099 .775 L -.185 .444 L .199 .521 L .164 .409 L .128 .084 L .368 .026 L .351 -.03 L .745 -.495 L .589 -.064 L .592 -.486 L .739 -.315 L .678 -.03 L .198 .077 L -.129 .176 L .137 .059 L .974 -.147 L .564 -.227 L .264 .151 L .496 .054 L .565 .153 L -.192 .145 L -.574 -.059 L .01 .269 L .236 .012 L .048 .088 L -.148 .142 L -.358 .004 L -.455 .297 L -.332 -.005 L -.338 .179 L -.647 -.144 L -1.345 .012 L -1.148 .152 L -.53 .292 L -.272 .19 L -.559 .395 L -.246 -.023 L -.258 .214 L -.464 .413 L .01 .32 L .411 .271 L .01 .336 L .232 .171 L -.119 .483 L .198 .477 L -.324 .426 L -.524 .355 L -.4 .341 L -.13 .283 L .223 .478 L .033 .31 L -.289 .255 L -.513 .215 L -.698 -.039 L -.997 -.122 L -.355 .129 L .35 .336 L .365 .407 L .129 .281 L .088 .437 L -.199 .255 L -.315 .115 L -.513 .031 L -.416 .115 L -.292 .228 L -.224 .424 L -.288 .834 L -.139 1.214 L -.021 .084 L -.34 .383 L -.237 .086 L -1.001 -.375 L -.562 -.025 L -.559 .243 L -.362 .271 L -.321 .693 L -.254 .086 L -.516 -.082 L -.644 -.039 L -.283 .072 L -.597 .441 L -.412 .369 L -.188 .34 L -.232 .876 L -.099 .903 L -.069 .184 L -.247 .156 L -1.066 .274 L -1.183 .19 L -.964 .175 L -1.234 .12 L -1.005 -.135 L -.349 .002 L -1.187 .218 L -.742 -.024 L -.541 -.039 L -.854 -.235 L -1.069 -.248 L -.63 -.194 L -.887 -.32 L
+486.696 126.295 N 5.257 -2.711 L .589 -2.701 L -.024 -.467 L -.187 -.508 L .009 -.255 L .23 -.355 L .31 -.214 L .866 -.174 L .457 -.371 L .944 -.883 L -.059 -.24 L .23 -.043 L .266 -.143 L .437 -.094 L .304 .16 L .364 -.032 L .811 .153 L .343 .023 L .244 -.234 L .366 .082 L .274 .151 L -.03 .259 L .102 .269 L .318 .105 L .164 -.126 L .433 -.388 L .407 .016 L .69 1.397 L .563 1.086 L .065 .198 L .761 .208 L 1.067 .107 L -.468 .596 L -.013 .254 L .422 .761 L -1.142 .98 L -.235 .763 L -.133 .875 L .002 .184 L .312 .351 L 1.02 .954 L 503 127.106 l .075 .155 L .321 -.115 L .533 .025 L 1.242 .925 L .436 .209 L .774 .603 L .29 .28 L .112 .239 L -.014 .508 L -.197 .622 L -.055 .579 L .034 .325 L .551 -.003 L .215 .013 L .164 .465 L .007 .395 L -.064 .212 L .686 .546 L .351 .224 L .014 .12 L -.096 .003 L -.664 .101 L -.408 -.056 L -.157 .057 L -.103 .127 L -1.271 .044 L -.518 .13 L -.343 .693 L -.463 .609 L -.521 .568 L -4.048 -.132 L -1.557 -.697 L -.812 -.277 L -.118 -.253 L -.047 -.818 L .118 -.396 L -.135 -.366 L -.973 .048 L -.141 -.07 L -.399 -.633 L -.258 -.196 L -2.44 -1.101 L -1.14 -.473 L -2.034 -.934 L -.757 -.222 L -1.129 -.459 L -.093 -.056 L -.093 -.056 L -.311 -.69 L -.87 -1.632 L
+479.916 127.377 N -.082 -.085 L .047 -.122 L -.021 -.183 L -.201 -.128 L -.183 -.346 L .398 -.209 L .041 -.099 L .526 -.396 L -.048 -.058 L -.223 -.099 L .077 -.151 L .298 -.25 L .599 -.006 L -.14 -.146 L -.035 -.046 L .078 -.111 L .177 -.163 L .169 -.116 L .299 -.239 L -.068 -.058 L .023 -.163 L -.09 -.047 L -.031 -.221 L -.241 -.157 L -.222 -.058 L .204 -.204 L -.125 -.052 L -.053 -.116 L -.12 .058 L -.335 .052 L -.388 -.023 L -.225 -.564 L .129 -.593 L .072 -.064 L -.1 -.507 L -.42 -.326 L .126 -.093 L .036 -.152 L .117 -.128 L -.093 -.222 L .107 -.012 L .259 -.32 L -.061 -.112 L .311 -.001 L .39 .125 L .114 -.127 L .171 .041 L .2 -.169 L .156 -.374 L .627 -.127 L -.243 -.423 L -.102 -.452 L .055 -.319 L .142 .035 L .222 -.001 L .777 .166 L .575 .025 L .746 -.075 L .929 -.373 L .278 -.03 L 1.532 .444 L .556 .153 L 1.104 -.176 L .567 -.13 L 1.498 -.673 L .439 -.073 L .747 .123 L .711 .039 L 1.185 -.224 L .693 -.304 L .24 -.292 L .207 .023 L .064 .245 L .059 .24 L -.944 .883 L -.457 .371 L -.866 .174 L -.31 .214 L -.23 .355 L -.009 .255 L .187 .508 L .024 .467 L -.589 2.701 L -5.257 2.711 L -.161 .071 L -2.96 1.541 L -1.139 .656 L -.253 .016 L -.365 -.167 L -1.902 -1.034 L
+426.068 126.434 N -.093 .981 L .064 .564 L -.093 .269 L -.802 .428 L -.579 .314 L -1.473 1.138 L -.126 .354 L .274 .973 L -.147 .537 L -.155 .227 L -.864 .598 L -.22 .143 L -.564 -1.536 L -.699 -2.242 L -.323 -.464 L -.363 -.252 L -.432 -.181 L -.484 -.831 L -.225 -.465 L -.363 -.28 L -.452 -.097 L -.336 -.774 L -.301 -.888 L .112 -.509 L 1 -.853 L .414 -.355 L .163 -.411 L .048 -.537 L -.052 -.594 L -.026 -.892 L -.012 -1.429 L .114 -.439 L .685 -.627 L .012 -.184 L .508 -.185 L .633 -.455 L .591 -.228 L .703 -.016 L .643 .183 L .247 .212 L .059 .241 L .25 .538 L .27 .084 L .417 -.171 L .584 -.44 L .401 -.17 L .034 .354 L -.265 .567 L -.638 .511 L -.275 .468 L .005 .283 L .202 .438 L .508 .466 L .351 .127 L .303 .848 L -.094 .212 L -.541 .764 L -.59 .34 L -1.017 .92 L -.216 .339 L .287 .451 L .587 .55 L .528 .295 L .284 .056 L .396 -.227 L .316 .084 L .244 .635 L .582 .239 L
+381.402 139.704 N -.027 -.876 L .069 -2.006 L .037 -.382 L .686 -.314 L 1.512 -.998 L .963 -.542 L 1.265 .078 L .397 -.059 L .181 -.693 L .864 -.033 L .777 -.174 L .527 -.229 L .524 -.356 L .484 -.652 L 1.109 -.332 L 1.52 -.701 L .129 -.227 L -.296 -.62 L -.025 -.396 L .079 -.227 L .265 -.114 L 1.186 -.12 L .381 -.186 L .309 -.553 L 1.022 .022 L .67 -.018 L 1.826 .004 L .34 -1.033 L -.07 -.211 L -.507 -.322 L 397 126.646 l -.158 -.465 L .016 -1.271 L .022 -.833 L -.165 -.889 L -.189 -.211 L -.563 -.279 L -.259 -.508 L .351 0 L .66 -.143 L .541 -.256 L .369 -.566 L .405 -.312 L .509 -.086 L .407 -.157 L .679 -.27 L .324 .226 L .176 .017 L .249 .024 L .238 -.142 L .407 -.51 L .613 -.426 L .682 -.355 L .614 -.171 L 1.16 -.116 L 1.587 -.06 L .513 -.072 L .634 -.312 L .578 .211 L .564 -.072 L .585 -.313 L .343 -.1 L .939 .012 L .513 -.015 L .307 .056 L .221 .042 L .322 .113 L .816 .168 L .529 -.015 L .772 -.171 L .705 -.2 L .612 -.554 L .994 .508 L .339 .099 L .312 -.143 L .314 -.241 L .228 -.156 L .528 .042 L .388 .197 L .162 .269 L .269 .126 L .516 -.086 L 1.093 -.158 L -.012 .184 L -.685 .627 L -.114 .439 L .012 1.429 L .026 .892 L .052 .594 L -.048 .537 L -.163 .411 L -.414 .355 L -1 .853 L -.112 .509 L .301 .888 L .336 .774 L .452 .097 L .363 .28 L .225 .465 L .484 .831 L .432 .181 L .363 .252 L .323 .464 L .699 2.242 L .564 1.536 L -.204 .156 L -.241 .383 L .88 1.605 L .147 .833 L .052 .691 L -.1 .862 L .101 .748 L -.16 .622 L -.158 .495 L .457 1.156 L -.061 .664 L -.086 .17 L -.666 .47 L -.249 .128 L -.152 .283 L 1.272 1.702 L .249 .917 L .562 .873 L .244 .154 L .544 -.201 L .702 .165 L 1.028 .347 L .178 .168 L .86 1.506 L .098 .07 L -.265 .186 L -1.632 .843 L -4.012 2.241 L -1.607 .956 L -2.308 1.454 L -.834 .655 L -3.084 2.617 L -1.82 .364 L -1.672 .321 L -2.176 .408 L -.146 -.564 L .161 -.679 L -.099 -.522 L -.277 -.352 L -.309 -.111 L -.748 -.024 L -.375 -.167 L -.588 -.562 L -.47 .314 L -.229 -.027 L -1.111 -1.039 L -.393 -.28 L -.082 -.183 L .096 -.396 L -.181 -.253 L -2.472 -1.469 L -.397 -.253 L -1.292 -.824 L -1.924 -1.26 L -3.283 -2.241 L -.811 -.575 L -2.054 -1.344 L -.895 -.531 L -.1 -.084 L -1.414 -.91 L -4.12 -2.42 L -2.829 -1.509 L
+395.704 122.189 N .259 .508 L .563 .279 L .189 .211 L .165 .889 L -.022 .833 L -.016 1.271 L .158 .465 L .598 .788 L .507 .322 L .07 .211 L -.34 1.033 L -1.826 -.004 L -.67 .018 L -1.022 -.022 L -.309 .553 L -.381 .186 L -1.186 .12 L -.265 .114 L -.079 .227 L .025 .396 L .296 .62 L -.129 .227 L -1.52 .701 L -1.109 .332 L -.484 .652 L -.524 .356 L -.527 .229 L -.777 .174 L -.864 .033 L -.181 .693 L -.397 .059 L -1.265 -.078 L -.963 .542 L -1.512 .998 L -.686 .314 L -.037 .382 L -.069 2.006 L -1.42 .008 L -1.718 -.004 L -2.677 .001 L -2.511 0 L -1.635 .04 L .141 -.28 L .431 -.411 L .427 -.085 L 1.296 -.285 L 1.143 -.455 L .453 -.312 L 1.147 -.85 L 1.149 -.878 L 1.043 -1.104 L .46 -.693 L .133 -.509 L -.05 -.494 L -.427 -.776 L -.09 -.678 L .099 -.508 L .396 -.636 L .706 -.863 L .211 -.65 L -.063 -.367 L .071 -.353 L 1.285 -1.203 L .724 -.481 L .916 -.327 L 1.266 -.469 L .73 -.397 L .558 -.552 L .537 -.736 L .466 -.905 L .829 -1.925 L .269 -.128 L .54 -.171 L .19 .127 L .684 .848 L .138 .099 L 1.148 .507 L .661 -.001 L .595 .042 L 1.304 -.074 L .522 -.228 L .437 -.27 L .398 .551 L .256 .099 L .798 .097 L .361 0 L
+480.248 123.437 N .388 .023 L .335 -.052 L .12 -.058 L .053 .116 L .125 .052 L -.204 .204 L .222 .058 L .241 .157 L .031 .221 L .09 .047 L -.023 .163 L .068 .058 L -.299 .239 L -.169 .116 L -.177 .163 L -.078 .111 L .035 .046 L .14 .146 L -.599 .006 L -.298 .25 L -.077 .151 L .223 .099 L .048 .058 L -.526 .396 L -.041 .099 L -.398 .209 L -.07 -.023 L -.088 .041 L -.067 .193 L -.009 .167 L -.355 .07 L -.07 -.099 L -.151 -.022 L -.372 .051 L .26 -.291 L .097 -.361 L .169 -.227 L .328 -.681 L -.017 -.232 L .181 0 L .138 -.192 L .072 -.32 L .018 -.32 L .409 -.431 L .232 -.07 L .116 -.174 L -.048 -.157 L
+184.444 142.729 N -.367 .82 L -.518 .821 L -.186 .763 L -.179 1.159 L .017 1.851 L -.133 1.187 L -.016 1.13 L .564 1.737 L .275 .805 L .624 .945 L .76 .903 L .191 .452 L .481 .521 L .529 .974 L .729 1.228 L .375 .296 L .677 .069 L .436 -.015 L .577 .154 L .593 .451 L .503 .508 L .773 .069 L 1.016 -.242 L 1.55 -.456 L 1.396 -.3 L .803 -.157 L -.02 .542 L .838 .223 L .264 -.286 L .293 -.199 L -.104 -.247 L -.393 -.175 L 1.073 -.62 L .633 -.62 L .086 -.827 L .498 -.429 L -.094 -.477 L .092 -1.145 L .254 -.699 L .625 -.334 L .164 -.043 L .757 -.198 L .701 -.1 L 1.088 -.229 L 1.016 -.37 L .594 -.058 L .499 .056 L 1.139 .181 L .502 -.194 L .378 .093 L .62 .507 L .047 .297 L -.079 .424 L -.298 .382 L -.541 .496 L -.433 .425 L -.317 .445 L -.02 .7 L -.254 .297 L -.188 .354 L .155 .155 L .337 -.138 L -.101 .652 L -.262 1.196 L 205.356 159 l -.062 .24 L -.34 -.534 L -.167 -.452 L -.072 -.155 L -.386 .34 L -.02 .549 L -.437 .016 L -.178 .447 L -.599 .857 L -.386 -.27 L -.278 .095 L .025 .329 L -2.332 -.006 L -1.792 -.005 L -.04 1.24 L -.999 .032 L .396 .223 L .495 .541 L .624 .231 L .359 .69 L .532 .223 L -.211 .683 L -1.762 -.007 L -1.06 .007 L -1.076 1.812 L .305 .397 L -.207 .238 L .054 .553 L .044 .454 L -.704 -.555 L -.952 -.888 L -.956 -.761 L -1.069 -.859 L -.534 -.352 L -.053 -.071 L -.639 -.252 L -1.048 -.21 L -.657 .044 L -.817 .397 L -1.1 .567 L -.756 .256 L -.931 -.069 L -.724 -.21 L -.48 -.197 L -1.305 -.195 L -.588 -.267 L -.644 -.422 L -.935 -.521 L -.785 -.267 L -1.711 -.392 L -.963 -.365 L -.722 -.366 L -1.074 -.436 L -.592 -.352 L -1.123 -1 L -.207 -.07 L -.606 .058 L -.689 -.14 L -1.835 -.575 L -.565 -.536 L -.503 -.634 L -.495 -.395 L -1.049 -.577 L -.619 -.267 L -.5 -.494 L -.742 -.987 L -.363 -.55 L -.038 -.113 L .15 -.155 L .504 -.086 L .18 -.17 L .047 -.184 L -.331 -.367 L .457 -.679 L .041 -.381 L -.172 -.466 L -.744 -.959 L .121 -.297 L .146 -.17 L -.07 -.268 L -.665 -.62 L -1.495 -1.777 L -.546 -.493 L -.963 -1.058 L -.474 -.522 L -.815 -.578 L -.322 -.197 L -.158 -.268 L -.058 -.48 L -.144 -.183 L -.329 -.197 L -.609 -.197 L -.408 -.31 L -.366 -.522 L -.271 -.028 L -.414 .114 L -.238 -.155 L -.163 -.367 L -.005 -.325 L .459 -.736 L -.126 -.339 L -.751 -.62 L -.439 .255 L -.375 -.621 L -.118 -.353 L -.359 -.211 L -.61 -.168 L -.319 -.296 L -.125 -.254 L .05 -.381 L .084 -.269 L -.185 -.226 L -.561 -.21 L -.46 -.098 L -.46 -.253 L -.935 -.86 L -.478 -.706 L -.281 -.551 L -.646 -.832 L -.736 -1.073 L -.184 -.423 L -.38 -.678 L -.242 -.338 L -.152 -.452 L .042 -.509 L .032 -.311 L -.56 -.239 L -.795 -.196 L -.06 -.452 L -.128 -.155 L -.458 -.183 L -.289 .326 L -.251 .043 L -1.43 -.647 L -.285 1.004 L -.045 .438 L .033 .084 L .265 .339 L .264 .296 L .028 1.046 L .088 .509 L .51 .677 L .143 .169 L .643 .267 L .601 .536 L .525 .663 L .602 1.214 L .44 .282 L .328 .042 L .237 .169 L .325 1.398 L .102 .169 L .246 .155 L .497 .056 L .133 .056 L .215 .438 L .161 .65 L .445 .79 L .49 -.071 L .223 -.142 L .245 .452 L .344 1.469 L .531 1.059 L .649 1.2 L .069 .593 L -.014 .522 L .26 .353 L .378 .154 L .389 -.17 L .234 -.198 L .588 .804 L .258 .579 L .464 .253 L .281 .014 L .133 .311 L -.196 .537 L -.136 .127 L -.691 .595 L -.254 -.042 L -.251 -.409 L -.24 -.734 L -.617 -.578 L -.625 -.309 L -.516 -.479 L -.834 -.507 L -1.143 -.986 L -.416 -.451 L -.162 -.269 L .216 -.989 L -.035 -.254 L -.488 -1.002 L -.238 -.381 L -.327 -.282 L -.44 -.098 L -.5 -.31 L -.675 -.677 L -.305 .142 L -.363 -.056 L -1.262 -.746 L -.722 -.31 L -.896 -.973 L -.139 -.127 L -.246 -.254 L .679 .15 L .599 .013 L .588 -.284 L .244 -.326 L .093 -.636 L -.01 -.184 L -.458 -.635 L -.466 -.452 L -1.1 -.888 L -.986 -.493 L -.402 -.338 L -.203 -.522 L -.272 -.649 L -.091 -.155 L -.447 -.126 L -.15 -.353 L -.026 -.594 L -.203 -.395 L -.623 -.734 L -.434 -.706 L -.003 -.254 L .212 -.382 L -.777 -.62 L -.254 -.325 L -.22 -.485 L .34 -.017 L 2.367 -.155 L 2.381 -.084 L .316 .31 L .267 .154 L 1.186 .39 L 2.811 .933 L 3.516 1.112 L .338 .055 L 1.662 .019 L 1.544 .02 L .966 .037 L 1.867 -.011 L .213 -.101 L .096 -.892 L 1.858 .003 L 1.892 .046 L .209 .112 L .631 .662 L .766 .632 L .837 .519 L .708 .491 L .179 .226 L .284 .678 L .318 .847 L .445 .549 L 1.092 .659 L 1.104 .503 L .337 .069 L .501 .011 L .416 -.158 L .283 -.37 L .418 -.413 L .576 -.541 L .468 -.201 L .643 -.018 L .475 .082 L .783 .321 L .412 .252 L .363 .366 L .663 1.029 L .744 1.227 L .845 1.042 L .657 .576 L .268 .253 L .078 .467 L .332 .932 L .336 .592 L .375 .365 L .921 .32 L 1.029 .56 L .264 .069 L .416 -.116 L .296 -.001 L .816 .377 L .347 .119 L
+507.047 133.665 N .055 .197 L .134 .691 L -.336 -.028 L -.513 .513 L .421 .194 L .418 -.206 L .306 .021 L .698 1.84 L -.644 .044 L -1.07 -.05 L -.185 -.239 L -.334 -.619 L -.408 -.054 L -1.657 -.259 L .521 -.568 L .463 -.609 L .343 -.693 L .518 -.13 L 1.271 -.044 L
+606.155 150.953 N .595 .152 L .255 .14 L .25 -.129 L .273 -.368 L .015 -.678 L -.152 -.93 L .228 -.185 L .401 -.144 L .191 -.354 L -.146 -1.594 L .133 -.283 L .811 .32 L .391 .11 L .309 .013 L .17 -.128 L 1.148 -2.25 L 0 -.324 L -.192 -.408 L .045 -.212 L .938 -1.134 L .136 -.382 L -.057 -.761 L .197 -.354 L 1.446 -.883 L .719 -.512 L .312 -.129 L .558 .082 L .853 .221 L .295 -.058 L -.184 -.718 L .072 -.283 L .596 -.582 L .112 -.24 L .018 -.508 L .001 -.127 L .306 -.34 L .277 -.044 L .504 .279 L .397 .435 L .243 .901 L .217 .309 L .287 .041 L .504 -.031 L .146 .14 L .195 1.649 L .02 .875 L -.353 .862 L -.429 .722 L -.611 .525 L -.487 .271 L -.191 .198 L -.617 .85 L .075 .465 L .163 .705 L -.224 .58 L 0 .268 L .216 .069 L .312 -.086 L .819 -.442 L .771 -.089 L .479 -.017 L .156 .126 L .277 1.762 L .202 .324 L .554 -.074 L .521 .096 L .033 .268 L -.729 1.584 L .117 .352 L .228 .098 L .507 -.003 L .481 -.045 L .291 .379 L .341 .746 L .378 .266 L .246 .083 L .647 -.159 L .628 -.413 L .111 .38 L .153 .239 L -.502 .355 L -.53 .61 L -.442 .581 L -.582 .455 L -.193 .185 L -.08 .085 L -.158 .071 L -.645 .06 L -.436 .172 L -.528 .342 L -.394 .595 L -1.078 .316 L -.62 .018 L -.474 -.082 L -.362 .411 L -.143 .368 L -.036 .819 L -.114 .509 L .064 .409 L -.086 .24 L -.163 .001 L -.588 .131 L .739 .884 L .069 .183 L .112 .875 L .254 .14 L 1.091 .953 L .148 .324 L .646 1.041 L .163 .338 L -.194 .241 L -.451 .229 L -.128 .226 L .231 1.185 L -.171 .198 L -.812 .428 L .178 .38 L .6 1.436 L .54 .477 L .606 .604 L .203 .479 L .088 .663 L -.086 .636 L .006 .254 L .488 1.183 L .586 1.225 L -.077 .297 L -1.011 1.559 L -1.01 1.7 L -.098 .374 L -.359 -.181 L -.075 -.805 L .461 -.665 L .174 -.495 L .122 -.777 L .287 -.466 L -.512 -.027 L -.104 -.084 L -.004 -.282 L .195 -.509 L -.177 -1.524 L -.246 -.832 L -.639 -1.185 L -.488 -1.312 L -.347 -.846 L -.179 -.875 L -.174 -1.736 L -.117 -.677 L -.034 -.564 L -.051 -.212 L -.344 -.084 L -.148 -.098 L -.304 -.917 L -.516 -.677 L -.226 -.225 L -.247 .029 L -.081 .988 L -.158 .424 L -.43 .41 L -.59 .284 L -1.089 .511 L -.359 .622 L -.298 .297 L -.196 .142 L -.237 -.282 L -.007 -.438 L -.212 .015 L -.338 .354 L -.321 -.013 L -.166 -.211 L .147 -.495 L -.001 -.113 L -.621 .171 L -.276 .127 L -.247 .283 L -.355 -.126 L -.002 -.466 L .553 -1.54 L .162 -.791 L .001 -.889 L -.101 -1.059 L -.384 -.973 L -.431 -1.072 L -.196 -.296 L -.281 .537 L -.32 -.126 L -.526 -.366 L .482 -.17 L .312 -.015 L -.149 -.479 L -.054 -.268 L -.684 -.775 L -.182 -.183 L -.19 -.028 L -.407 .1 L -.38 -.267 L .086 -.438 L -.026 -.141 L -.209 -.112 L -.365 .043 L -.577 -.465 L -.504 -.606 L -.117 -.244 L .252 -.341 L .801 -.527 L -.194 -1.607 L
+605.297 153.429 N -.126 -.264 L -.269 -.55 L -.223 -1.213 L -.611 -1.41 L -.357 -.395 L -.73 .354 L -.393 0 L -.034 -.084 L -.242 -.211 L -.356 -.592 L -.124 -.042 L -.152 .127 L -.026 .537 L .374 .79 L -.006 .424 L -.143 .169 L -.455 .086 L -.235 .537 L -.261 .1 L -.255 -.437 L -.311 -.395 L -.073 -.057 L -.163 .669 L -.28 .249 L -.203 .043 L -.271 -.536 L -.495 .636 L -.359 -.265 L -.147 -.532 L -.402 -1.775 L -.325 -1.409 L -.352 -.45 L -.04 -.254 L .505 -.765 L .029 -.269 L -.193 -.21 L -1.042 -.431 L -.339 -.323 L .548 -.61 L .4 -.299 L .502 -.13 L .382 -.101 L .047 -.155 L -.126 -.112 L -1.224 -.938 L -.494 -.237 L -.083 -.155 L .124 -.283 L .555 -.525 L .234 -.171 L 1.252 .303 L .339 .266 L .372 .266 L .489 -.06 L .417 .054 L .129 .324 L .053 .479 L .079 .719 L .095 .099 L .537 .109 L .547 .053 L .916 -.062 L .559 -.003 L 2.473 .198 L .111 .098 L .057 .127 L -.012 .79 L -.159 .34 L -.938 .767 L -.498 .13 L -.651 .356 L -.131 .283 L .009 .522 L .001 .381 L .23 .281 L .249 .267 L .529 .448 L .224 -.354 L .395 -1.159 L .281 -.115 L .4 -.044 L .064 .578 L .627 2.479 L .037 .466 L .194 1.607 L -.801 .527 L -.252 .341 L
+627.408 186.411 N -.086 .337 L -.495 .35 L -.11 .575 L -.644 .089 L -.05 -.478 L -.309 -.163 L -.279 .28 L -.244 .394 L -.204 -.083 L -.118 -.239 L .213 -.398 L -.041 -.21 L -.055 -.226 L -.261 -.238 L -.447 -.119 L -.106 -.466 L -.571 .013 L -.448 .17 L .013 -.104 L .128 -.297 L -.15 -.183 L -.411 .212 L -.301 -.07 L -.38 -.38 L -.116 -.508 L .064 -.282 L -.151 -.438 L -.229 -.169 L -.388 .043 L -.39 -.719 L -.209 -.508 L -.3 -.324 L -.311 -.155 L -.456 -.395 L -.343 .1 L -.218 .142 L -.216 -.381 L -.04 -.607 L .163 -.749 L .559 -1.738 L .29 -.848 L -.087 -.044 L .098 -.374 L 1.01 -1.7 L 1.011 -1.559 L .077 -.297 L -.586 -1.225 L -.488 -1.183 L -.006 -.254 L .086 -.636 L -.088 -.663 L -.203 -.479 L -.606 -.604 L -.54 -.477 L -.6 -1.436 L -.178 -.38 L .812 -.428 L .171 -.198 L -.231 -1.185 L .128 -.226 L .451 -.229 L .194 -.241 L -.163 -.338 L -.646 -1.041 L -.148 -.324 L -1.091 -.953 L -.254 -.14 L -.112 -.875 L -.069 -.183 L -.739 -.884 L .588 -.131 L .163 -.001 L .086 -.24 L -.064 -.409 L .114 -.509 L .036 -.819 L .143 -.368 L .362 -.411 L .474 .082 L .62 -.018 L 1.078 -.316 L .394 -.595 L .528 -.342 L .436 -.172 L .645 -.06 L .158 -.071 L .08 -.085 L .541 .166 L .295 .182 L .118 .168 L .01 .423 L -.106 .805 L .066 .367 L .186 .154 L .423 -.003 L .489 -.2 L .414 -.045 L .045 .113 L .249 1.311 L -.085 .41 L -.528 1.569 L -.117 .438 L -.027 .494 L .145 .324 L .481 .138 L .37 -.411 L 1.173 -1.178 L .346 -.03 L .835 .348 L .59 .265 L .223 -.072 L .543 -.257 L .2 -.538 L .286 -.453 L .403 .012 L .893 .192 L .266 .153 L .052 .282 L .285 .535 L .688 .659 L .435 .632 L .058 1.524 L .107 .366 L .255 .464 L .979 1.279 L .419 .703 L .157 .507 L .002 .945 L -.121 .438 L -.808 .64 L -.301 -.167 L -.599 -.109 L -.575 -.039 L -.558 .074 L -.798 -.066 L -1.172 .091 L -.383 .101 L -.521 .441 L -.92 1.233 L -.146 .297 L -.076 .382 L .026 .635 L .219 .648 L .487 .717 L .16 .479 L -.146 .176 L -.026 -.063 L -.286 -.183 L -.458 -.084 L -.9 -.887 L -.434 -.154 L -.304 -.014 L -.572 .227 L -.391 -.112 L -.29 -.141 L -.337 -.014 L 0 -.282 L .19 -1.243 L -.107 -.184 L -.719 -.055 L -.248 -.084 L -.521 .043 L -.443 .212 L -.244 .297 L .207 .593 L -.103 .339 L -.318 .707 L .083 .579 L .054 .41 L -.293 .664 L -.583 1.187 L -.7 1.682 L -.255 1.314 L .104 1.171 L .172 .296 L .229 .169 L .55 -.072 L .396 -.142 L .252 .07 L .135 .353 L .009 .325 L -.08 .466 L .141 .282 L .178 .211 L .271 -.094 L .17 .46 L .209 .974 L -.032 .254 L .127 .737 L .434 .871 L .167 .155 L .801 .281 L .539 .112 L .467 -.058 L .294 .197 L .266 .612 L .664 .544 L .212 .145 L
+204.31 158.989 N -.175 .412 L .612 -.173 L .026 .429 L -.419 1.241 L .178 .269 L -.237 .795 L .189 .318 L -.092 .397 L -.358 .875 L -.3 .35 L -.36 .032 L -.054 .286 L -.388 .238 L 0 .286 L -.69 .016 L .215 -4.297 L -.025 -.329 L .278 -.095 L .386 .27 L .599 -.857 L .178 -.447 L .437 -.016 L
+200.276 169.481 N -.151 -.056 L -.928 -.342 L -.614 .032 L -.766 -.032 L -.608 -.239 L -.909 -.656 L -.513 -.419 L -.044 -.454 L -.054 -.553 L .207 -.238 L -.305 -.397 L 1.076 -1.812 L 1.06 -.007 L 1.762 .007 L .211 -.683 L -.532 -.223 L -.359 -.69 L -.624 -.231 L -.495 -.541 L -.396 -.223 L .999 -.032 L .04 -1.24 L 1.792 .005 L 2.332 .006 L -.215 4.297 L .69 -.016 L .303 .095 L .311 .302 L .14 -.191 L -.066 -.381 L .336 .157 L .458 .367 L -1.507 1.208 L -.499 .238 L -.177 .493 L .162 .604 L -.438 .302 L -.467 .048 L -.043 .254 L .164 .159 L -.351 .111 L -.184 .302 L -.22 -.016 L -.565 .461 L -.012 .223 L
+204.413 165.093 N .312 -.03 L .612 -.27 L .639 -.058 L .743 .126 L .478 .069 L 1.443 .04 L .699 -.228 L .379 -.199 L .567 .267 L .788 -.03 L .763 -.101 L .63 -.001 L .5 .126 L .564 .253 L -.038 .353 L -.102 .226 L .228 .282 L .787 .238 L .557 .069 L .244 .524 L -1.425 .486 L -.424 .229 L -.248 .086 L -.463 -.097 L -.328 -.182 L -.259 -.013 L -.294 .242 L -.503 .794 L -1.207 .997 L -.725 -.42 L -.513 .583 L -.882 .034 L -.005 .961 L -.293 .412 L -.29 .143 L -1.001 .125 L -.311 -.661 L -.025 -.085 L -.478 -.3 L .085 -.731 L -.128 -.175 L -.272 0 L -.541 -.276 L -.433 .34 L -.365 -.016 L -.066 -.271 L -.565 -.35 L -.409 -.08 L -.208 -.418 L -.677 -.17 L .438 -.302 L -.162 -.604 L .177 -.493 L .499 -.238 L 1.507 -1.208 L
+205.532 170.085 N .035 .076 L -.203 .057 L .01 .265 L -.237 .334 L -.68 -.046 L -.853 -.139 L -1.697 -.505 L -1.305 -.435 L -.325 -.21 L .012 -.223 L .565 -.461 L .22 .016 L .184 -.302 L .351 -.111 L -.164 -.159 L .043 -.254 L .467 -.048 L .677 .17 L .208 .418 L .409 .08 L .565 .35 L .066 .271 L .365 .016 L .433 -.34 L .541 .276 L .272 0 L .128 .175 L -.085 .731 L
+242.38 173.617 N -.128 -.105 L -.84 .171 L -.534 .156 L -.414 .2 L -.056 .288 L .048 .497 L -.129 .396 L -.227 -.027 L -.381 .059 L -.99 1.758 L -.172 .722 L -.241 .722 L -.709 1.191 L .402 .025 L .234 -.1 L .384 -.017 L .31 .606 L .855 1.45 L .103 .395 L -.226 1.132 L .099 .353 L .401 .309 L .429 .548 L .397 .252 L .496 -.017 L 1.163 -.12 L 1.167 -.05 L .521 .181 L .64 .321 L .188 .253 L .847 .998 L .554 .576 L .144 0 L .522 -.13 L .76 -.174 L 1.99 -.224 L .644 .081 L -.409 .525 L -.085 1.004 L -.379 .511 L -.147 .326 L .026 .254 L .035 .438 L .048 .367 L .162 .804 L .447 .789 L .256 .437 L .486 .647 L .121 .282 L -.731 .612 L -.479 .526 L .51 .491 L .797 1.182 L -.52 .286 L -.834 .57 L -.412 .158 L -.463 .017 L -2.812 -.082 L -.64 -.024 L -.042 .325 L -.013 1.031 L .178 .154 L .896 .122 L .177 .084 L .293 .408 L .052 .367 L -.384 -.04 L -.417 -.11 L -.687 .032 L -.493 .187 L -.111 .085 L -.001 1.071 L 0 .554 L .192 .197 L .688 .363 L .192 .183 L -.031 .777 L .399 .562 L .031 .212 L -.326 1.428 L -.706 4.411 L -.073 .382 L -.133 .198 L -.156 -.14 L -.575 -.703 L -.237 -.126 L -.161 .058 L -.448 .031 L 1.155 -1.956 L .035 -.198 L -.127 -.069 L -1.299 -.84 L -.509 -.209 L -.708 .442 L -.397 -.182 L -.523 -.421 L -.452 .427 L -.337 .157 L -.496 .031 L -1.038 -.008 L -.573 -.152 L -.092 -.281 L .004 -.396 L -.173 -.296 L -.479 -.039 L -.366 -.14 L -.078 -.282 L -.251 -.761 L -.988 -.672 L -.526 -.364 L -.208 -.62 L -.208 -.324 L -.513 -.435 L -.897 -.418 L -.927 -.107 L -.081 -.112 L -.269 -.162 L -.197 -.118 L -.709 -.631 L -.128 -.056 L -.89 .401 L -.67 .061 L -.977 -.277 L -.355 -.309 L -.166 -.493 L -.227 -.225 L -1.432 -.656 L -1.496 -.803 L .033 -.068 L -.117 -.311 L -.181 -.282 L .108 -.212 L .509 -.114 L .465 .112 L .186 -.325 L -.348 -.564 L .086 -.424 L .314 -.227 L .878 -.058 L .193 .042 L .41 -.227 L .445 -.679 L .45 -.961 L .651 -1.061 L -.122 -.268 L -.56 -.408 L -.071 -.184 L .306 -.721 L .089 -.523 L -.149 -.861 L -.371 -.86 L .085 -.254 L .49 -.143 L .135 -.212 L -.088 -.198 L -.565 -.479 L -.042 -.226 L .103 -.198 L .242 -.326 L .036 -.254 L -.173 -.282 L -.739 -.719 L -.364 -.396 L .256 -.753 L .228 -.082 L -.045 -.45 L .19 .082 L .085 .307 L .584 -.409 L .094 -.43 L .322 -.062 L -.509 -1.038 L -.228 -.149 L -.084 -.327 L .142 .075 L .42 .338 L .397 .507 L .22 .508 L .235 .197 L .199 -.17 L .147 -.241 L -.332 -.776 L .02 -.212 L .176 -.297 L .445 -.34 L .399 -.297 L .501 -.736 L .327 -.156 L .684 -.101 L .217 -.382 L -.104 -.381 L .174 -.777 L .067 -.65 L .207 -.48 L .498 -.439 L .429 -.283 L .592 -.242 L .113 0 L .374 .206 L .22 .443 L .281 -.34 L .031 -.438 L .199 -.551 L .22 -.071 L .267 .126 L .855 .041 L .562 -.001 L .623 -.27 L .237 -.254 L .476 -.298 L .848 -.256 L .435 -.396 L .278 -.551 L .333 -.255 L .469 -.17 L .58 .013 L .526 .338 L .155 .126 L .155 .339 L -.285 .332 L
+408.6 174.04 N -.062 .777 L .164 .762 L .388 .718 L -.316 .962 L -.19 .566 L -.223 .298 L -.656 .414 L -.095 .34 L .116 .592 L -.078 .368 L -.433 .13 L -.257 .03 L -.237 .397 L .002 .381 L .003 .48 L -.012 .876 L -.09 .989 L .217 1.439 L -.121 1.188 L .006 .234 L -.333 .015 L -1.232 .144 L -.669 .052 L -.106 -.205 L -.295 -1.072 L .188 -.708 L .026 -.833 L -.036 -.537 L -.135 -.974 L -.006 -.862 L -.035 -.522 L .013 -.579 L -.442 -1.311 L -.164 -.691 L -.403 -.323 L -.468 -.294 L -.18 -.395 L .141 -.594 L .046 -.424 L .062 -.142 L .24 -.157 L .366 -.553 L .415 -.271 L .418 -.045 L .739 .095 L .337 -.158 L .415 -.468 L .237 -.764 L -.131 -.367 L .577 -.3 L .321 -.073 L .339 .28 L .727 .589 L .902 .503 L
+394.266 178.814 N .191 -.17 L -.02 -.41 L -.261 -1.934 L .125 -.34 L .271 -.157 L 2.119 -.041 L .867 .037 L 1.429 .006 L .976 -.458 L .161 .032 L .595 .119 L -.25 .849 L .05 .254 L .633 .915 L .244 .451 L -.188 .721 L .02 .396 L .265 1.114 L .181 .592 L .503 .788 L .032 .155 L -.286 .242 L -.174 .382 L .01 1.314 L -.011 .72 L .021 .551 L .18 .479 L .468 .577 L .752 .608 L -.503 .682 L -.304 .099 L -.593 -.013 L -.992 .144 L -.463 .185 L -.188 .098 L -.898 .469 L -1.263 .398 L -.942 .412 L -.958 .567 L -.578 -.324 L -.945 -.436 L -.074 -.124 L -.066 -.741 L -.554 -1.155 L -.263 -.747 L -.103 -.706 L .327 -1.005 L .437 -1.274 L .521 -.851 L .203 -.595 L -.334 -1.438 L -.189 -1.285 L -.178 -.154 L
+400.72 175.499 N 1.013 .163 L .611 .166 L .29 .041 L -.046 .424 L -.141 .594 L .18 .395 L .468 .294 L .403 .323 L .164 .691 L .442 1.311 L -.013 .579 L .035 .522 L .006 .862 L .135 .974 L .036 .537 L -.026 .833 L -.188 .708 L .295 1.072 L .106 .205 L -.452 .035 L -.479 .128 L -.368 .212 L -.023 .04 L -.752 -.608 L -.468 -.577 L -.18 -.479 L -.021 -.551 L .011 -.72 L -.01 -1.314 L .174 -.382 L .286 -.242 L -.032 -.155 L -.503 -.788 L -.181 -.592 L -.265 -1.114 L -.02 -.396 L .188 -.721 L -.244 -.451 L -.633 -.915 L -.05 -.254 L .25 -.849 L
+383.772 190.418 N .041 -.919 L .105 -.565 L .247 -.849 L .059 -.452 L -.131 -.282 L -1.264 -1.334 L -.592 .032 L -.369 -.097 L -.194 -.183 L -.05 -.183 L .501 -.865 L .059 -.438 L -.119 -.55 L .062 -.17 L .047 -.099 L .602 -.554 L .188 -.354 L -.179 -.21 L -.296 -.549 L .03 -.127 L .158 -.199 L .304 -.03 L .548 .223 L .304 .012 L .19 -.143 L -.051 -.226 L -.648 -.561 L -.309 -.351 L .332 -.37 L .125 -.283 L -.197 -.31 L -.695 -.405 L -.214 -.409 L .264 -.623 L .379 -.397 L .522 -.54 L .352 -.059 L .566 .321 L .455 .436 L .35 -.228 L .362 -.469 L .571 -.483 L .239 -.114 L .257 .041 L .147 .211 L .004 .297 L .117 .381 L .148 .239 L .352 -.059 L .604 -.399 L .159 -.1 L .259 .168 L .545 -.003 L .308 .252 L .15 .465 L .52 .562 L .452 .223 L .354 .083 L .368 -.115 L .446 -.257 L .608 -.159 L .769 -.075 L .644 .166 L .63 .434 L .178 .154 L .189 1.285 L .334 1.438 L -.203 .595 L -.521 .851 L -.437 1.274 L -.327 1.005 L .103 .706 L .263 .747 L .554 1.155 L .066 .741 L -.119 -.201 L -.289 -.197 L -.208 .015 L -.143 .24 L -.096 .042 L -.48 -.027 L -.705 -.167 L -.608 -.083 L -2.352 .062 L -1.279 .243 L -.575 .199 L -1.07 .369 L -1.739 .781 L -.701 .425 L -.256 -.013 L -.075 -.032 L
+627.408 186.411 N .035 .024 L .523 .098 L 1.061 1.198 L .693 .916 L .506 .762 L .081 .579 L -.023 .805 L -.134 .862 L .07 1.073 L -.07 .636 L .097 .494 L .196 .381 L .53 .493 L .338 .536 L .705 1.608 L .062 .127 L -.366 -.069 L -.895 -.055 L -.401 .142 L -.175 -.07 L -.521 -.437 L -.885 -.605 L -.6 -.337 L -1.231 -.675 L -1.02 -.732 L -.217 -.254 L -.16 -.988 L -.438 -.691 L -.895 -.817 L -.195 -.663 L .039 -.494 L .03 -.283 L -.107 -.409 L -.399 -.634 L -.347 -1.849 L -.188 -.72 L .043 -.362 L .448 -.17 L .571 -.013 L .106 .466 L .447 .119 L .261 .238 L .055 .226 L .041 .21 L -.213 .398 L .118 .239 L .204 .083 L .244 -.394 L .279 -.28 L .309 .163 L .05 .478 L .644 -.089 L .11 -.575 L .495 -.35 L .086 -.337 L h 643.95 196.042 m .081 .044 L .375 .408 L .397 .141 L .861 .083 L .413 .168 L .761 .436 L .335 .042 L .337 -.1 L -.503 -.535 L .169 -.551 L .365 -.608 L .528 -1.258 L .584 -.27 L 1.481 -.342 L 1.018 -.299 L .428 -.326 L .524 -1.021 L .523 -.323 L .87 -1.117 L .22 -.212 L .244 -.147 L .112 -.068 L .084 .126 L .062 .37 L .234 -.012 L .111 .259 L .309 .271 L .383 -.271 L 1.234 -.42 L -.025 -.086 L -.197 -.197 L .013 -.247 L .024 -.111 L -.148 -.444 L -.11 -.174 L .167 -.042 L .188 .143 L .085 -.016 L .449 -.084 L .161 -.283 L -.036 -.24 L -.478 -.366 L .197 -.396 L .436 -.071 L .511 .041 L .154 -.605 L .408 -.313 L .284 -.48 L .531 -.58 L .498 -1.032 L .075 -.17 L .322 .465 L .33 .098 L .315 -.424 L .305 .494 L .351 .282 L .402 .211 L .075 .338 L -.182 .354 L -.234 .452 L .037 .198 L .631 -.128 L .472 -.128 L .115 .226 L -.349 .495 L .742 -.1 L .357 -.085 L .269 .056 L 1.035 .662 L .345 .14 L .1 .24 L -.158 .269 L -.201 .155 L -.44 .143 L -.594 .029 L -.683 -.083 L -.355 .311 L .096 .254 L .888 .69 L -.161 .311 L -.458 .199 L -.646 .1 L -.518 .114 L -.067 .15 L -.182 -.09 L -.753 -.292 L -.446 -.04 L -.624 .018 L -.651 -.081 L -.748 -.081 L -.464 .017 L -.247 .157 L -.381 .638 L -.119 .565 L .106 .988 L -.22 .383 L -.646 .244 L -.218 .34 L .076 .748 L -.928 1.785 L -.387 .299 L -.864 .175 L -.465 .172 L -.467 .356 L -.287 .03 L -.559 -.152 L -.524 -.223 L -.557 -.251 L -.431 -.11 L -.479 .074 L -.531 .441 L -.403 .525 L -.528 .342 L -.399 .172 L -.543 -.11 L -.526 -.223 L -.255 .001 L -.928 .416 L -.446 -.28 L -.304 -.083 L -1 -.983 L -.253 -.295 L -.288 -.792 L
+274.556 195.884 N .06 -.212 L -.332 -.563 L -.49 -1.127 L -.246 -.832 L -.185 -.295 L -.561 -.067 L -.532 -.675 L -.571 -.831 L .328 -.694 L .095 -.467 L -.078 -.777 L .169 -.17 L 1.131 -.091 L .183 -.27 L .082 -.862 L .142 -.496 L .015 -.339 L .326 -.312 L .382 -.057 L 1.392 .463 L .465 .042 L .083 -.41 L .141 -.085 L .337 .027 L .833 .012 L .863 -.03 L .723 .069 L .63 .182 L .999 .427 L -.647 .876 L -.391 .751 L -.137 .594 L .094 .381 L .134 .635 L .086 .664 L .521 .844 L .029 .438 L -.424 1.472 L -.489 .963 L -1.05 -.488 L -.319 .001 L -.534 .385 L -.398 .059 L -.418 -.139 L -.642 -.124 L -.172 .156 L -.2 .326 L .611 1.014 L -.528 -.054 L -1.108 -.276 L -.4 -.04 L
+285.859 190.719 N .015 .422 L -1.102 1.646 L -.427 .765 L -.439 .992 L -.464 .681 L -.299 .214 L -.56 -.025 L -1.11 -.389 L -.882 .613 L -.225 -.069 L -.649 -.505 L .489 -.963 L .424 -1.472 L -.029 -.438 L -.521 -.844 L -.086 -.664 L -.134 -.635 L -.094 -.381 L .137 -.594 L .391 -.751 L .647 -.876 L .218 .093 L 1.033 .294 L .55 .196 L .799 .549 L 1.264 1.084 L .545 .564 L .317 .465 L .193 .028 L
+429.505 210.684 N -.695 -.533 L -.351 .13 L -.68 .513 L -.536 .414 L -.112 -.177 L -.392 -.86 L -1.381 -1.344 L .184 -.295 L .413 -.229 L .803 .307 L .343 -.681 L -.052 -.296 L -.274 -.253 L -.39 -.521 L -.116 -.366 L .058 -.495 L .127 -.1 L .909 -.146 L .604 -.243 L .125 -.213 L .167 -.807 L .174 -.185 L .239 -.029 L .193 .14 L .341 .479 L .405 .521 L .386 .195 L .287 -.016 L .188 -.228 L .362 -.482 L .29 .253 L .167 .578 L .146 .112 L .304 .068 L .255 -.114 L .184 -.68 L .243 -1.089 L .131 -1.229 L .229 -1.047 L -.114 -.338 L -.161 -.127 L -.384 -.082 L -.528 -.208 L -.242 -.31 L -.132 -.437 L .008 -.109 L .021 -.3 L .157 -.284 L .492 -.398 L .556 -.441 L .094 -.354 L -.389 -.902 L -.369 -.322 L -.592 -.067 L -.939 .5 L -.463 -.025 L -.146 -.253 L -.068 -.734 L .077 -.679 L .396 -.468 L .56 .109 L .863 .023 L .878 -.076 L .271 .055 L .479 .04 L .56 .124 L .576 .194 L .864 .334 L .863 .292 L .271 -.086 L -.1 -.861 L .159 -.185 L .303 -.312 L .365 -.497 L .158 -.523 L .222 -1.287 L .255 -.228 L .479 -.102 L .975 -.175 L .367 .054 L .624 .138 L .895 .093 L .751 -.287 L .128 .197 L -.031 .156 L -.398 1.203 L -.558 .837 L -.349 .821 L -.094 .551 L -.156 .764 L -.196 2.021 L -.254 1.342 L -.115 .61 L -.169 .708 L -.139 .523 L -.459 .427 L -.525 .229 L -.809 .599 L -.41 .454 L -.47 .836 L -.343 .751 L -.193 1.159 L -.074 .396 L .122 .72 L -.086 .706 L -1.025 .938 L -.364 .229 L -.931 .811 L -.554 .399 L -.57 .328 L -.147 -.211 L -.042 -.664 L -.054 -.424 L -1.495 .532 L -.327 .581 L -.443 .271 L -.177 -.013 L -.484 -.336 L
+425.506 195.522 N .045 -.495 L .88 .093 L 1.773 .088 L .831 .038 L 1.022 .149 L -.396 .468 L -.077 .679 L .068 .734 L .146 .253 L .463 .025 L .939 -.5 L .592 .067 L .369 .322 L .389 .902 L -.094 .354 L -.556 .441 L -.492 .398 L -.157 .284 L -.021 .3 L -.008 .109 L .132 .437 L .242 .31 L .528 .208 L .384 .082 L .161 .127 L .114 .338 L -.229 1.047 L -.131 1.229 L -.243 1.089 L -.184 .68 L -.255 .114 L -.304 -.068 L -.146 -.112 L -.167 -.578 L -.29 -.253 L -.362 .482 L -.188 .228 L -.287 .016 L -.386 -.195 L -.405 -.521 L -.341 -.479 L -.193 -.14 L -.239 .029 L -.174 .185 L -.167 .807 L -.125 .213 L -.604 .243 L -.909 .146 L -.127 .1 L -.058 .495 L .116 .366 L .39 .521 L .274 .253 L .052 .296 L -.343 .681 L -.803 -.307 L -.413 .229 L -.184 .295 L -.038 -.037 L -.563 -.493 L -.532 -.55 L -.259 -.466 L -1.318 -1.169 L .286 -.312 L -.369 -.281 L -.528 -.056 L -.918 -1.438 L .382 -.297 L .111 -.1 L -.321 -.395 L -.464 -.14 L -.29 -.564 L -.369 -.451 L .319 -.17 L .541 -.411 L .223 -.396 L .291 -.976 L .089 -.295 L .415 .07 L .495 .013 L -.064 -.211 L -.56 -.352 L -.561 -.451 L .208 -.042 L .271 -.128 L .142 -.617 L .83 -.043 L .927 .008 L 1.245 .021 L .575 -.032 L -.001 -.155 L -.02 -.551 L -.026 -1.836 L
+422.536 195.513 N 1.005 -.007 L 1.965 .017 L .026 1.836 L .02 .551 L .001 .155 L -.575 .032 L -1.245 -.021 L -.927 -.008 L -.83 .043 L .033 -.146 L -.049 -.297 L -.144 -.084 L -.416 -.098 L .287 -.467 L .447 -.523 L .335 -.58 L .066 -.403 L h 419.636 193.273 m -.484 -.049 L -.049 -.218 L .049 -.291 L .242 -.097 L .024 -.146 L 0 -.291 L .17 -.097 L .169 -.121 L .219 .048 L .218 .048 L .097 .194 L -.193 .169 L -.146 .243 L -.17 .339 L -.146 .267 L
+222.291 207.801 N .188 -.126 L .264 -.312 L .585 -1.061 L .038 -.269 L -.209 -.733 L .058 -.391 L -.478 .617 L -.732 .623 L -.333 -.098 L -.601 -.324 L -.408 -.408 L .389 -.283 L .1 -.24 L -.167 -.579 L .005 -.438 L .133 -.466 L -.109 -.282 L -.362 -.649 L .193 -.17 L .48 -.198 L .721 -.454 L -.319 -.395 L 0 -.226 L .175 -.354 L .497 -.462 L .157 -.146 L .14 -.848 L -.116 -.452 L -.082 -.184 L .11 -.226 L .686 -.101 L .891 -.341 L .396 -.241 L .474 -.382 L .09 -.186 L 1.496 .803 L 1.432 .656 L .227 .225 L .166 .493 L .355 .309 L .977 .277 L .67 -.061 L .89 -.401 L .128 .056 L .709 .631 L .197 .118 L .269 .162 L .081 .112 L .387 .76 L .321 .789 L -.08 .255 L -.352 .384 L -.547 1.147 L -.533 .779 L -.71 .725 L -1.001 .755 L -.336 .087 L -1.028 .303 L -.901 .358 L -.809 .499 L -.59 .653 L -.284 .51 L -.987 2.506 L -.267 .312 L -.242 .072 L -.398 -.068 L -.398 -.491 L -.191 -.479 L -.312 -.252 L -.815 .005 L -.444 -.11 L -.105 -.197 L -.097 -.494 L .233 -.34 L .029 -.495 L .005 -.569 L
+245.934 224.314 N 1.109 1.843 L .922 1.646 L .08 .24 L -.437 .736 L -.15 .523 L -.143 .848 L .057 .522 L .236 .902 L -.156 .34 L -.221 .34 L -.532 .582 L -.102 .452 L .347 .845 L -.21 .368 L -.159 .34 L .022 .48 L .604 1.168 L .015 .522 L -.285 .439 L -.723 .61 L -.12 .227 L .049 .48 L .132 .295 L .018 .142 L -.586 .187 L -.15 .199 L -.228 .933 L -.259 .298 L -.852 .275 L -.201 -.517 L -.289 -.31 L -1.422 -.618 L -.313 -.239 L -.233 -.678 L -.315 -.38 L -.559 -.225 L -.504 -.281 L -.489 -.254 L -.616 -.309 L -.873 -.464 L -.673 -.366 L -.866 -.379 L -.838 -.337 L -1.271 -.845 L -1.354 -.985 L -1.232 -1.099 L -.594 -.705 L -.42 -.677 L .008 -.438 L 0 -.903 L -.269 -.606 L -.781 -1.043 L -1.389 -2.778 L -.285 -.268 L -.467 -.211 L -.074 -.085 L .055 -.536 L -.02 -.396 L -.149 -.396 L -1.208 -2.199 L -.997 -1.961 L -.688 -1.27 L -1.365 -1.917 L -.272 -.353 L -.598 -.973 L -.462 -.423 L -.75 -.437 L -.914 -.365 L -.61 -.365 L .119 -.17 L .181 -.113 L .609 -.029 L .144 -.41 L -.118 -.282 L -.592 -.874 L -.353 -.437 L -.258 -.72 L .01 -.24 L .244 -.523 L .582 -.622 L 1.02 -.878 L .47 -.198 L .317 -.214 L -.005 .569 L -.029 .495 L -.233 .34 L .097 .494 L .105 .197 L .444 .11 L .815 -.005 L .312 .252 L .191 .479 L .398 .491 L .398 .068 L .242 -.072 L .267 -.312 L .987 -2.506 L .284 -.51 L .59 -.653 L .809 -.499 L .901 -.358 L 1.028 -.303 L .336 -.087 L 1.001 -.755 L .71 -.725 L .533 -.779 L .547 -1.147 L .352 -.384 L .08 -.255 L -.321 -.789 L -.387 -.76 L .927 .107 L .897 .418 L .513 .435 L .208 .324 L .208 .62 L .526 .364 L .988 .672 L .251 .761 L .078 .282 L .366 .14 L .479 .039 L .173 .296 L -.004 .396 L .092 .281 L .573 .152 L 1.038 .008 L .496 -.031 L .337 -.157 L .452 -.427 L .523 .421 L .397 .182 L .708 -.442 L .509 .209 L 1.299 .84 L .127 .069 L -.035 .198 L -1.155 1.956 L .448 -.031 L .161 -.058 L .237 .126 L .575 .703 L .156 .14 L .136 .324 L -.341 .186 L -.793 -.235 L -.493 -.096 L -.322 .072 L -.604 .441 L -.371 .115 L -.669 -.066 L -1.895 .773 L -.701 .442 L -.372 .172 L -.599 1.415 L -.319 .511 L -.021 .155 L .277 .86 L .012 .112 L -.625 .498 L -.62 .328 L -.349 .342 L -.247 .622 L .106 .564 L .619 .957 L 1.333 2.166 L .106 .127 L -.247 .495 L -.208 .312 L 1.085 .12 L .415 .04 L .312 .182 L .241 .323 L .096 .663 L 1.843 .032 L .762 -.499 L .648 -.484 L .326 -.114 L -.059 1.511 L -.098 1.173 L .108 .353 L .317 .337 L .285 .068 L .358 -.072 L .786 -.259 L .42 -.045 L
+279.288 257.295 N -.063 -.423 L .027 -.777 L .222 -.819 L .298 -.835 L .321 -.75 L -.019 -.141 L -.491 -.336 L -.234 -.112 L -1.29 .445 L -.224 -.055 L -.254 -.563 L -.487 -2.524 L -.121 -.437 L -.843 -.404 L -.724 -.278 L -.115 .001 L -.709 .413 L -.346 .03 L -.925 -.221 L -.818 -.15 L -.372 -.04 L .006 -.72 L .086 -.721 L .132 -.862 L -.104 -.959 L -.521 -.872 L -.084 -.663 L -.253 -.408 L .421 -.666 L .38 -.765 L .238 -.834 L .185 -1.173 L -.022 -.494 L -.307 -.774 L -.316 -.479 L -.73 -.362 L -.436 -.407 L -.067 -.563 L .261 -.835 L -.042 -.296 L -.693 -.024 L -1.615 -.019 L -.5 -.012 L -.967 -.036 L -.153 -.112 L -.249 -.944 L -.138 -2.032 L -.03 -.706 L -.203 -1.029 L .038 -.565 L -.085 -.169 L -.403 -.237 L -.537 -.166 L -.636 -.123 L -1.148 .077 L -.22 -.084 L -.398 -.308 L -.385 -.774 L -.096 -.014 L -.882 -.037 L -.3 -.098 L -.75 -.603 L -.688 -.307 L -.676 -.25 L -.355 .03 L -.787 -.023 L -.316 -.097 L -1.1 -.939 L -.813 -.928 L -.335 -.591 L -.141 -.635 L -.036 -.818 L .064 -.622 L .079 -.621 L -.045 -.748 L -1.283 -.021 L -.934 .076 L -.36 .158 L -.874 .485 L -1.471 1.109 L -.415 .243 L -.48 -.025 L -.336 -.026 L -.946 .824 L -.355 .03 L -1.122 -.304 L -.939 -.136 L -.42 .045 L -.786 .259 L -.358 .072 L -.285 -.068 L -.317 -.337 L -.108 -.353 L .098 -1.173 L .059 -1.511 L -.326 .114 L -.648 .484 L -.762 .499 L -1.843 -.032 L -.096 -.663 L -.241 -.323 L -.312 -.182 L -.415 -.04 L -1.085 -.12 L .208 -.312 L .247 -.495 L -.106 -.127 L -1.333 -2.166 L -.619 -.957 L -.106 -.564 L .247 -.622 L .349 -.342 L .62 -.328 L .625 -.498 L -.012 -.112 L -.277 -.86 L .021 -.155 L .319 -.511 L .599 -1.415 L .372 -.172 L .701 -.442 L 1.895 -.773 L .669 .066 L .371 -.115 L .604 -.441 L .322 -.072 L .493 .096 L .793 .235 L .341 -.186 L -.136 -.324 L .133 -.198 L .073 -.382 L .706 -4.411 L .326 -1.428 L -.031 -.212 L -.399 -.562 L .031 -.777 L -.192 -.183 L -.688 -.363 L -.192 -.197 L 0 -.554 L .001 -1.071 L .111 -.085 L .493 -.187 L .687 -.032 L .417 .11 L .384 .04 L -.052 -.367 L -.293 -.408 L -.177 -.084 L -.896 -.122 L -.178 -.154 L .013 -1.031 L .042 -.325 L .64 .024 L 2.812 .082 L .463 -.017 L .412 -.158 L .834 -.57 L .52 -.286 L .148 .168 L .138 .437 L .161 .861 L .088 .452 L .199 .437 L .432 .054 L .694 .546 L .482 .223 L .414 -.073 L .757 -.697 L .083 .183 L .186 .776 L .271 -.016 L .645 -.739 L .74 -.654 L .554 -.286 L .652 -.173 L .235 -.213 L .259 -.666 L .203 -.199 L .652 -.131 L .569 -.272 L .265 -.27 L -.15 -.253 L -.434 -.125 L -.96 -.051 L -.166 -.239 L -.079 -.55 L -.302 -1.524 L -.391 -.69 L -.616 -.688 L .041 -.184 L .159 -.029 L .309 .125 L .896 .461 L .385 .04 L 1.149 -.035 L .344 .224 L .628 .618 L .348 -.115 L .232 -1.117 L .284 -.115 L .465 .054 L .557 -.074 L .807 -.23 L 1.74 -.9 L .592 -.385 L .163 -.326 L -.103 -.169 L .375 -.257 L .302 -.058 L .516 .124 L .26 .111 L .04 .212 L -.452 1.09 L .304 -.001 L .533 .138 L .435 .958 L -.179 .368 L -.547 1.416 L -.213 .962 L .156 .465 L .281 .394 L .167 .31 L -.072 .354 L .008 .396 L .15 .296 L 1.103 .897 L .613 .392 L .686 -.075 L .694 -.513 L .505 -.314 L 1 -.331 L .934 -.429 L .943 .022 L .4 .04 L 1.108 .276 L .528 .054 L -.611 -1.014 L .2 -.326 L .172 -.156 L .642 .124 L .418 .139 L .398 -.059 L .534 -.385 L .319 -.001 L 1.05 .488 L .649 .505 L .225 .069 L .882 -.613 L 1.11 .389 L .56 .025 L .299 -.214 L .464 -.681 L .439 -.992 L .427 -.765 L 1.102 -1.646 L -.015 -.422 L .409 -.241 L .276 .141 L .235 .423 L .164 .791 L .414 1.34 L .108 .607 L .472 1.383 L .296 .55 L .324 .324 L .978 .21 L .196 .338 L .038 .551 L -.045 .254 L -.524 .354 L -.933 1.414 L -.238 .325 L -.51 .411 L -.41 .231 L -.244 .138 L -.798 .778 L -.544 .947 L -.419 1.018 L -.402 .453 L .271 .112 L .512 -.072 L .497 -.198 L 1.202 -.709 L .112 -.028 L .299 .889 L .442 .818 L .255 .112 L .287 .07 L 1.135 -.031 L .592 -.058 L .737 -.298 L -.277 .679 L -.017 .584 L .448 -.557 L 1.254 -1.006 L .289 -.156 L .306 -.382 L .499 -.933 L .272 -.396 L .256 -.099 L 1.006 -.003 L .352 -.198 L .175 0 L .687 .549 L .654 .267 L .32 -.028 L .814 .28 L 1.069 .45 L .334 .281 L .269 .593 L .08 .028 L .176 -.015 L .402 -.354 L .319 .042 L .302 .254 L .538 .789 L .205 .396 L -.13 .254 L -.308 .41 L -.116 .424 L .091 .466 L .171 .396 L .825 -.821 L .369 -.156 L .609 -.156 L .995 -.454 L .224 -.028 L .367 .069 L .764 .38 L .907 .436 L .462 .098 L 1.117 .167 L 1.118 .083 L .464 -.058 L .48 -.029 L .577 -.171 L .416 -.016 L 1.131 .351 L .731 .408 L .762 .479 L .586 .437 L 1.595 1.395 L .742 .662 L .821 .605 L .41 .338 L .477 .196 L .734 .168 L 1.247 .097 L .912 .04 L .445 .197 L .265 .465 L .087 .607 L .592 2.004 L .268 1.143 L .062 .988 L -.076 .579 L -.179 .904 L -.243 .848 L -.559 1.301 L -.518 .849 L -1.347 1.175 L -1.388 1.288 L -.44 .198 L -.354 .043 L -.39 .637 L -.094 .396 L -.012 .353 L -.504 1.074 L -.6 .976 L -.8 1.104 L -.266 .212 L -.194 .015 L -.079 -.423 L -.278 -.296 L -.367 -.084 L -.064 .79 L -.538 1.64 L -.049 .734 L -.077 .607 L .318 2.738 L .078 .988 L -.577 2.543 L -.039 .663 L .14 .649 L .043 .085 L -.759 .495 L -.404 .51 L -.261 .791 L -.04 .367 L .245 1.468 L -.106 .367 L -.214 .283 L -.676 .608 L -.752 1.498 L -1.01 1.4 L -.201 .537 L -.055 .311 L .144 .945 L -1.203 .37 L -.544 .283 L -.355 .368 L -.084 .466 L .021 .367 L -.401 .114 L -.864 -.041 L -.979 .172 L -.654 .001 L -.764 -.055 L -.465 -.112 L -.922 .072 L -.736 .002 L -.214 .607 L -.295 .198 L -.795 .312 L -.481 .453 L -.21 .354 L -.747 -.267 L -.35 .071 L -.563 .255 L -1.864 1.232 L -.528 .396 L -.725 .383 L -.616 .438 L -.532 .679 L -.486 .241 L -.567 .072 L -.278 -.014 L .006 .24 L .336 .225 L .12 .536 L -.111 .537 L -.099 .282 L -.282 .156 L -.084 .155 L .384 .648 L .036 1.186 L .05 1.045 L -.057 .311 L -.11 .594 L -.198 .621 L -.464 .735 L -.566 .495 L -.788 .524 L -.866 1.046 L -.208 .396 L -.476 1.145 L -.499 .933 L -.75 1.004 L -.69 .623 L -.801 .78 L .321 -.808 L .436 -.594 L .629 -.566 L .605 -1.158 L -.06 -.254 L -.356 .142 L -.397 -.014 L -.633 -.21 L -.075 .155 L .002 .297 L -.401 1.215 L -.349 .48 L -.661 .438 L -.317 .283 L -.225 .424 L -.013 .354 L -.448 1.709 L -.481 .763 L -.787 .934 L -1.076 .979 L -.101 -.313 L -.258 -.746 L .103 -.269 L .634 -.766 L .017 -.269 L -.333 -.379 L -.163 -.027 L -.131 -.197 L -.853 -1.109 L -.474 -.435 L -.999 -.587 L -.55 -.194 L -.617 -.194 L -.432 -.576 L -.365 -.393 L -.693 .427 L -.27 .016 L -.232 -.38 L -.59 -.814 L -.379 -.407 L -.47 -.364 L -.266 -.098 L -.25 .101 L -1.042 .246 L -.55 .003 L .214 -.27 L .833 -.88 L 1.805 -1.816 L 1.435 -1.363 L 1.633 -1.448 L .348 -.242 L 1.754 -.744 L .521 -.313 L .161 -.467 L .094 -1.638 L -.517 -1.154 L -.206 -.196 L -.462 .017 L -.957 .161 L h 288.966 203.943 m -.558 -.125 L -.301 -.536 L -.078 -.382 L .16 -.197 L -.094 -.636 L .048 -.381 L .208 -1.018 L .176 -.099 L .479 -.058 L .879 .097 L 1.007 .11 L .479 -.199 L .368 .028 L .479 .168 L .479 .083 L .319 .155 L -.335 .538 L -.193 .946 L -.257 .494 L -.289 .312 L -.561 .326 L -.464 .171 L -.527 .015 L -.783 .016 L -.641 .171 L
+732.92 214.323 N -.164 -.24 L -.225 -.197 L -.379 -.126 L -.416 .198 L -.399 -.38 L -.287 -.184 L -.659 -.238 L -.243 -.239 L .156 -.255 L .29 .027 L .731 -.058 L .538 .126 L .743 .083 L .523 -.058 L .258 -.185 L .232 -.509 L .056 .099 L .351 .395 L .286 .184 L .241 .014 L .961 -.2 L .366 -.227 L .361 -.722 L .241 -.212 L .653 -.029 L .263 -.128 L .04 -.282 L -.064 -.565 L -.043 -.536 L .495 .196 L .404 .056 L .324 -.354 L .318 .578 L .077 .353 L -.196 .495 L -.099 .184 L -.509 .044 L -.159 .226 L .061 .17 L .437 .479 L -.308 .354 L -.264 .113 L -.742 -.083 L -.416 .001 L -.091 .269 L -.408 .495 L -.275 .156 L -.973 .426 L -.484 .143 L -.798 .029 L -.807 .115 L h 713.795 220.696 m .031 -3.438 L -.046 -.805 L -.431 -1.368 L .44 -.822 L -.169 -7.966 L 2.581 .802 L .85 .337 L 1.04 .295 L 1.254 .378 L .848 .507 L .613 .323 L .597 .084 L .38 -.058 L .26 .508 L .274 .254 L .635 .352 L .687 .395 L .779 .718 L -.3 .947 L .033 .226 L .305 .226 L .665 .111 L 1.887 .787 L .354 .027 L .72 .183 L .181 .254 L .413 .535 L .211 .579 L -.166 .113 L -1.114 .073 L -.563 .156 L -.098 .155 L .047 .339 L .589 .987 L .5 .521 L 1.464 1 L .218 .847 L .725 1.044 L .288 .141 L .304 -.015 L .712 -.086 L .338 .013 L .087 .198 L -.354 .551 L .218 .212 L .401 .141 L .423 .041 L .63 .168 L .096 .127 L -.031 .142 L -.648 .213 L .287 .24 L 1.064 .28 L .538 .295 L .235 .763 L -.064 .226 L -1.094 -.012 L -.215 -.154 L -.146 -.466 L -.126 -.099 L -1.011 -.111 L -1.063 -.266 L -.644 -.126 L -.752 .016 L -.774 -.026 L -.573 -.211 L -.494 -.352 L -.223 -.522 L -.24 -.268 L -.726 -.31 L -.472 -.338 L -.135 -.197 L -1.089 -1.608 L -.613 -.817 L -.454 -.056 L -1.524 -.336 L -.671 -.31 L -.55 -.055 L -.415 .185 L -.394 .071 L -.78 -.45 L .282 .762 L -.027 .212 L -.249 .071 L -.382 -.126 L -.311 .1 L .296 .395 L -.116 .113 L -1.037 .045 L -1.125 -.182 L -.478 .029 L .237 .127 L .24 .154 L .642 .169 L .662 .352 L .404 .338 L .219 .395 L -.411 .199 L -.739 .425 L -.458 .213 L -.668 -.097 L -1.801 -.039 L -1.219 -.092 L
+726.605 297.247 N -.479 -.229 L -1.179 -.471 L -.543 -.371 L -.508 -.715 L -.477 -.558 L -.216 -.414 L .264 -.044 L .169 .1 L .421 .171 L .129 -.143 L -.209 -.387 L -.703 -.699 L -.617 -.713 L -.149 -.257 L -.375 -.956 L -.008 -.67 L .177 -.044 L 1.004 .312 L 1.476 .354 L 1.089 .369 L .797 -.03 L .463 -.144 L .382 -.115 L .373 -.058 L .684 .027 L .306 -.301 L .716 -.244 L .475 .385 L -.009 .1 L .128 .8 L -.009 .686 L -.751 1.503 L -.124 .758 L -.245 .315 L -.943 .26 L -.553 .388 L -.49 .689 L -.189 .272 L -.274 .072 L h 716.883 224.344 m .682 .394 L .233 .509 L .047 .649 L .118 .678 L .256 .197 L .42 .013 L .102 .24 L -.45 .919 L .715 .338 L .175 .282 L .147 .593 L .08 1.3 L .144 .621 L .456 1.157 L .293 .268 L .422 .014 L .328 -.312 L .384 -.185 L .32 .663 L .363 .127 L .563 .408 L .385 .154 L .144 .155 L .002 .41 L .083 .96 L .275 .776 L -.046 1.06 L .279 .24 L .93 1.325 L .276 .706 L .055 .777 L -.209 .876 L .403 .649 L .275 .833 L .204 .353 L 1.395 .803 L .285 .154 L .744 .111 L .402 .649 L .843 .535 L .483 .169 L 1.141 .068 L .117 .197 L 731 245.42 l -.273 .565 L .309 .424 L .879 .479 L .22 .564 L .469 1.342 L .211 .748 L .112 .396 L .252 .311 L .771 .352 L -.021 -.127 L -.141 -.777 L .913 .507 L .567 .154 L .184 .127 L .136 .296 L .106 1.752 L .884 .762 L .573 .323 L .604 .154 L .287 .296 L .244 .579 L .229 .424 L .545 .168 L .176 .297 L .007 .269 L .19 .536 L .106 .113 L .506 .126 L .156 .112 L .162 .354 L .366 1.327 L .026 .876 L .013 .862 L -.132 .41 L -.181 .325 L .778 1.271 L .223 .48 L .181 .833 L .034 .749 L -.293 .708 L -.353 1.668 L -.585 1.755 L .137 .791 L -.065 .806 L -.306 .778 L -.661 .977 L -.203 .665 L -.598 .821 L -.45 .185 L -.465 .1 L -.423 .468 L -.466 .68 L -.328 1.527 L -.477 .638 L -.34 .495 L .014 .65 L -.208 .539 L -.331 .312 L -.601 .341 L -.27 .326 L -.3 1.021 L -.169 1.049 L -.079 .937 L .021 .666 L -.283 .64 L -.322 .354 L -.463 .3 L -.754 .101 L -1.158 .087 L -.82 .03 L -.527 .157 L -.516 .299 L -1.529 .94 L -.443 .129 L -.214 .284 L -.346 .071 L -.528 .059 L -.286 -.014 L .295 .368 L .209 .27 L -1.315 -.665 L -.885 -.353 L .003 -.469 L -.073 -.156 L -.56 -.467 L -.628 -.368 L -.421 -.014 L -.485 .172 L -.335 .242 L .748 .467 L -.97 .243 L -.929 .428 L -.953 .442 L -.222 .028 L -.604 -.226 L -.886 -.438 L -.67 -.226 L -1.086 -.311 L -.51 -.041 L -.239 .156 L -.044 .113 L -.716 -.169 L -.751 -.353 L -.522 -.298 L -.896 -.82 L -.526 -.34 L -.422 -.879 L .09 -1.035 L -.082 -.411 L -.184 -.495 L -.664 -.736 L -.141 -.523 L -.029 -.425 L -.534 -.014 L -.786 .398 L -.597 .114 L -.34 .058 L -.178 -.07 L -.167 -.17 L .517 -.454 L .233 -.567 L .073 -.821 L -.253 -.324 L -.536 -.593 L -.247 -.353 L -.485 .735 L -.443 1.431 L -.19 .113 L -.796 .002 L -.199 .156 L -.196 .015 L -.255 .028 L .198 -.396 L .081 -.396 L .079 -.1 L .634 .041 L .242 -.142 L .126 -.255 L -.105 -1.004 L .454 -.835 L .328 -.453 L .091 -.396 L .018 -.409 L .151 -.128 L .245 -.015 L .218 -.354 L -.052 -.227 L -.323 -.494 L -.338 -.494 L -.107 .707 L -.288 .255 L -.518 .299 L -.311 .467 L -.086 .155 L -.189 .467 L -.281 .326 L -.747 .242 L -.735 .481 L -.653 .567 L -.36 .693 L -.514 .808 L -.41 -.339 L -.38 -1.328 L -.263 -.579 L -.19 -.325 L -.688 -.79 L -.297 -.734 L -.176 -.212 L -.704 .072 L -.235 -.099 L -.139 -.24 L -.085 -.269 L .334 -.34 L -.047 -.297 L -.346 -.395 L -.543 -.494 L -.266 -.098 L -.83 .157 L -.486 -.07 L -.95 -.549 L -.274 -.014 L -.438 .17 L -.433 -.027 L -.421 -.183 L -.662 -.521 L -.921 -.437 L -.218 .001 L -.723 .213 L -1.282 .088 L -.669 .001 L -1.764 .061 L -.611 .129 L -.656 .213 L -.989 .44 L -.972 .256 L -1.039 .257 L -1.503 .088 L -.794 -.013 L -.383 .044 L -.927 .284 L -.993 .469 L -.773 .397 L -.538 .143 L -.431 .085 L -.361 .199 L -.615 .693 L -.774 1.02 L -.588 .284 L -.766 -.013 L -.547 -.013 L -.927 .143 L -.4 .185 L -.663 -.395 L -.294 -.084 L -.734 .016 L -1.572 .173 L -.938 .157 L -.459 -.041 L -.672 .044 L -.398 .227 L -.583 .793 L -.344 .128 L -.958 -.125 L -.158 .057 L -.57 .708 L -.465 .368 L -.919 .271 L -.586 .086 L -1.516 -.082 L -.638 -.055 L -.688 -.197 L -.633 -.366 L -.778 -.677 L -.74 -.353 L -.374 -.041 L -.151 -.07 L -.19 -1.229 L .055 -.255 L .489 .112 L .45 -.086 L .332 -.425 L .197 -.467 L .267 -1.357 L -.043 -1.215 L -.156 -.622 L -.258 -.593 L -1.117 -1.906 L -.208 -.635 L -.144 -.834 L .027 -.989 L -.16 -.692 L -.467 -1.072 L -.663 -.945 L -.603 -.734 L -.214 -.254 L .128 -.904 L -.215 -.536 L -.733 -1.115 L -.972 -1.018 L -.273 -.583 L .126 -.233 L .188 .187 L .152 .443 L .183 .163 L .235 .35 L .327 .188 L .354 .023 L -.348 -1.144 L -.437 -.396 L -.226 -.326 L .08 -.304 L .748 .84 L .495 .979 L .477 .065 L -.099 -.555 L .289 -.039 L .004 -.564 L -.282 -.48 L -1.03 -1.368 L -.354 -.691 L -.119 -.579 L -.038 -.734 L .355 -.595 L .323 -.523 L .21 -.664 L -.083 -1.031 L -.254 -.635 L .033 -.368 L .438 -.692 L .109 -.325 L .064 -.156 L .271 .649 L .011 .424 L .105 .184 L .35 .027 L .171 -.113 L .187 -.565 L .141 -.48 L .765 -.468 L 1.22 -.624 L .484 -.326 L .676 -.581 L .585 -.467 L .632 -.327 L .79 -.114 L .697 -.016 L .7 -.002 L .431 -.043 L .352 -.185 L .474 -.453 L .494 -.128 L .929 -.072 L .279 -.143 L .291 -.551 L .158 -.1 L .444 .027 L .877 .224 L .626 -.043 L .911 -.299 L 1.084 -.469 L .359 -.213 L .716 -.665 L .427 -.58 L .29 -.622 L .132 -.297 L .41 -.369 L .968 -.651 L .079 -.17 L -.067 -.409 L -.242 -.805 L -.016 -.495 L 1.063 -1.118 L .387 -.692 L .291 .169 L .341 .437 L .619 1.355 L .262 .253 L .177 -.579 L .021 -.466 L .436 .238 L .272 .07 L .189 -.607 L -.06 -.142 L -.563 -.238 L -.175 -.24 L .007 -.565 L .044 -.112 L .897 .04 L .661 .253 L .642 -.029 L .334 -.029 L .289 .074 L -.699 -.455 L -.431 -.141 L .128 -.537 L -.07 -.296 L .135 -.509 L .422 -.354 L .165 -.07 L .732 .394 L .202 -.043 L -.112 -.452 L .11 -.48 L .146 -.367 L -.041 -.522 L .358 -.171 L .4 -.113 L .813 .04 L .529 -1.088 L .371 -.298 L .35 .169 L .268 .451 L .265 -.552 L .222 -.227 L .197 -.48 L .695 .62 L .513 .084 L .293 .211 L .331 .536 L .632 .592 L .122 .706 L -.072 .594 L .181 .197 L .256 -.283 L .462 -.679 L .155 -.128 L 1.16 .082 L .479 .155 L .637 .492 L .332 .141 L .156 -.48 L .302 -.297 L .022 -.24 L -.266 -.324 L -.601 -.395 L -.079 -.184 L .008 -.24 L .145 -.099 L .604 -.538 L .007 -.452 L .191 -.213 L .518 -.283 L .268 -.241 L .151 -.269 L -.094 -.184 L .003 -.296 L .512 -.863 L .121 -.057 L .317 -.029 L .397 -.029 L .248 -.17 L -.205 -.409 L .377 -.396 L .344 -.071 L .793 .366 L .616 -.072 L 1.291 -.088 L .512 -.128 L .232 -.17 L .04 -.057 L -.077 -.197 L -.2 -.734 L -.248 -.282 L -.471 -.268 L -.374 .086 L -.244 -.141 L .035 -.212 L .367 -.143 L .396 -.043 L .617 .521 L .255 .099 L .34 -.1 L .364 .31 L .676 .465 L .36 .154 L 1.297 .294 L .591 .084 L .413 -.143 L .372 .014 L .396 .606 L .419 .112 L .141 -.029 L .562 -.438 L .454 .027 L .652 .38 L .331 .479 L .46 -.143 L .122 -.791 L .181 -.085 L .455 .465 L .337 .099 L .152 .154 L -.436 .411 L -.126 .424 L -.176 .212 L -.011 .438 L -.12 .255 L -.513 .015 L -.51 .228 L -.396 .34 L -.004 .551 L .301 .353 L -.187 .692 L -.381 .58 L -.559 .481 L -.155 .48 L .063 .17 L .469 .437 L 1.038 .619 L .81 .677 L .508 .606 L .246 .099 L .349 -.114 L .264 .027 L .782 .352 L .742 .465 L .434 .451 L .679 .535 L .335 .127 L .442 .027 L .784 .182 L .25 .184 L .218 .621 L .167 .169 L .507 .31 L .76 .423 L .537 .154 L .422 -.072 L .414 -.17 L .603 -.199 L .208 -.156 L .736 -1.344 L .403 -1.131 L .194 -1.314 L .259 -.721 L .43 -.679 L -.131 -.424 L -.409 -.621 L .022 -.311 L .236 -.637 L .187 -.48 L -.164 -.282 L -.183 -.395 L .088 -.692 L .104 -1.004 L .225 -.297 L .329 -.156 L .158 -.311 L -.534 -.521 L .033 -.198 L .576 -.947 L .312 -.934 L .072 -.833 L .191 -.241 L .279 -.297 L
+270.934 276.123 N .054 -.338 L -.194 -.577 L .011 -.522 L .24 -.495 L .48 -.524 L .118 -.325 L -.168 -.986 L -.049 -.847 L .103 -.522 L .595 -2.359 L .065 -.452 L .443 -.524 L .55 -.003 L 1.042 -.246 L .25 -.101 L .266 .098 L .47 .364 L .379 .407 L .59 .814 L .232 .38 L .27 -.016 L .693 -.427 L .365 .393 L .432 .576 L .617 .194 L .55 .194 L .999 .587 L .474 .435 L .853 1.109 L .131 .197 L .163 .027 L .333 .379 L -.017 .269 L -.634 .766 L -.103 .269 L .258 .746 L .101 .313 L -.059 .053 L -.803 1.103 L -.625 .552 L -.775 .454 L -.441 .156 L -.818 .1 L -.874 -.337 L -.551 .044 L -.551 .114 L -.64 .283 L -.38 -.042 L -.846 -.676 L -.637 -.252 L -.76 -.154 L -.469 .128 L -.507 .072 L -.327 -.127 L -.548 -.563 L -.354 -.159 L
+418.763 73.869 N .391 -.499 L .389 -.262 L .283 .081 L .663 -.062 L .762 -.075 L .324 -.137 L .891 -.787 L .979 -.2 L .224 .025 L .198 -.219 L .175 .032 L -.349 .531 L .11 .106 L .026 .481 L -.288 .156 L -.17 .368 L -1.675 .069 L -.376 -.106 L -.428 .031 L -.14 -.062 L -.239 .081 L -.275 .031 L -.153 .417 L -.249 .218 L -.353 .118 L -.08 .112 L -.229 -.155 L -.411 -.292 L h 421.68 78.002 m -1.839 .129 L -.116 -.016 L .064 -.404 L -.19 -.638 L -.174 .015 L -.36 -.293 L -.233 .184 L -.398 -.172 L .2 -.355 L .434 -.293 L -.007 -.181 L -.445 -.541 L .018 -.256 L .131 -.498 L .279 -.106 L .318 .15 L .495 .12 L .43 -.574 L .299 -.091 L .296 .437 L .391 -.046 L .022 -.528 L .107 -.363 L 1.032 -.017 L .848 .089 L .03 .544 L .187 .301 L 1.138 .058 L .14 .166 L -.307 .604 L -.565 .142 L -.119 -.261 L -.331 .031 L -.263 .136 L .121 .278 L -.184 .414 L -.312 .077 L -.069 .244 L -.635 .035 L .157 .229 L -.291 .094 L -.191 .24 L -.061 .285 L .003 .21 L -.215 .338 L .166 .084 L h 427.177 77.967 m -.139 -.149 L -.312 -.344 L -.405 .031 L -.665 -.059 L -.123 -.329 L -.285 -.66 L .433 -.091 L .603 -.467 L .356 .495 L .521 .179 L .2 -.271 L .078 -.586 L .333 -.046 L .459 .044 L .056 .571 L -.134 .36 L -.144 .09 L -.413 .286 L .603 .299 L -.285 .166 L -.499 .39 L -.238 .09 L
+417.259 94.301 N -.135 -.233 L .216 -.701 L -.091 -.076 L .075 -.295 L .321 -.372 L .054 -.358 L .054 -.213 L .954 -.806 L -.605 -.193 L -.479 .014 L -.456 -.09 L -.095 -.186 L -.233 -.007 L -.072 .048 L -.689 .062 L -.045 -.145 L -.395 .055 L -.337 -.277 L -.428 -.319 L -.074 -.353 L .248 -.144 L .044 -.288 L -.466 -.096 L -.41 -.312 L -.062 -.301 L .422 -.376 L -.038 -.468 L -.282 -.365 L -.344 -.177 L .165 -.278 L -.143 -.217 L -.182 -.037 L .08 -.097 L .235 -.133 L .236 -.133 L -.193 -.121 L .201 -.275 L .095 -.148 L -.152 -.292 L -.082 -.265 L -.376 -.182 L .07 -.182 L .341 -.078 L .175 -.092 L .378 .134 L .664 -.151 L .32 -.152 L .128 -.376 L .412 -.207 L .003 -.328 L -.16 -.17 L -.188 .061 L -.44 -.134 L .029 -.184 L .113 -.181 L .571 .036 L .084 -.401 L .345 -.361 L -.151 -.438 L .066 -.147 L -.465 -.192 L .126 -.43 L .285 -.181 L .371 -.028 L 1.12 -.062 L .293 -.012 L .095 .16 L .195 .271 L -.155 .158 L .324 .113 L .148 -.068 L .042 -.192 L -.159 -.204 L .259 .012 L .437 .169 L -.163 -.362 L .247 -.419 L .687 .113 L .614 -.091 L -.596 -.17 L -.084 -.227 L .282 -.102 L -.252 -.114 L -.12 -.147 L .166 -.08 L .068 -.136 L -.675 .08 L -.06 -.182 L .664 -.125 L .218 -.193 L -.464 -.42 L -.324 -.341 L .045 -.28 L .116 .016 L 1.839 -.129 L .254 .13 L .565 .341 L .271 .297 L -.02 .228 L -.252 .175 L .445 -.05 L .171 .216 L .157 -.078 L .283 .021 L .235 .156 L .45 .073 L .866 -.121 L .077 .208 L -.07 .149 L -.057 .125 L -.665 .295 L .316 .193 L .649 -.136 L .113 .182 L .457 -.006 L .349 -.391 L 1.059 -.136 L .602 -.234 L .757 -.266 L .249 .113 L .396 -.17 L .184 .182 L .085 .159 L .634 .29 L .182 .074 L .526 -.12 L .156 .12 L .059 .34 L .036 .227 L .154 .102 L .354 .125 L .372 -.022 L .028 .13 L .201 .025 L .319 .835 L -.219 .52 L -.391 .147 L -.051 .328 L .391 .102 L .683 .429 L -.217 .395 L .094 .18 L .312 .146 L -.021 .304 L .131 .102 L -.118 .292 L -.214 .124 L -.007 .191 L .325 .27 L -.114 .258 L .468 .188 L .281 .485 L -.435 .698 L -.142 .115 L -.344 -.072 L -.031 -.278 L -.308 -.121 L -.436 .072 L .282 .218 L -.254 .084 L -.284 .097 L -.524 .048 L -.124 .169 L -.735 .024 L -.112 .217 L -.176 -.072 L -.358 .061 L -.097 .229 L -.382 .012 L -.078 .181 L -.442 0 L -.422 .201 L -.293 -.033 L -.26 .181 L -.243 .168 L .037 .056 L .258 .378 L .413 .098 L -.155 .25 L -.08 .406 L .159 .085 L .036 .233 L .244 .173 L .478 .265 L .188 -.072 L .573 .515 L .431 .17 L .184 .201 L .219 -.084 L .165 .18 L .207 .036 L .447 .633 L -.682 .26 L -.437 -.151 L -.132 .027 L -.039 .337 L -.162 .172 L -.968 .295 L -.364 .227 L .538 .571 L -.197 .295 L .334 .014 L .056 .158 L -.098 .343 L -.082 .055 L -.441 -.178 L .049 -.165 L -.146 -.117 L -.564 .062 L -.195 -.138 L -.433 .035 L -.092 .178 L -1.25 .035 L -.125 .171 L -.319 .014 L -.044 .13 L 425.4 94.7 l -.594 .021 L -.059 -.144 L -.093 -.145 L -.493 -.089 L -.262 .055 L -.237 -.041 L .032 .274 L -.452 .343 L -.215 0 L .078 -.195 L -.26 -.039 L .011 -.165 L -.194 -.048 L -.104 -.137 L -.249 -.007 L -.096 -.11 L -.164 .137 L -.368 -.014 L -.664 -.261 L -.852 -.007 L -.149 -.089 L -.199 .014 L -.017 -.151 L -.709 .199 L .179 .199 L -.354 .021 L -.278 -.11 L -.567 .158 L -.271 -.096 L -.272 .117 L -.271 -.089 L h 432.012 80.473 m -.171 -.038 L -.088 -.049 L -.447 .08 L -.111 -.264 L .002 -.213 L .647 .281 L .168 .203 L
+450.734 91.05 N -.831 -.33 L -.424 -.101 L -.104 -.216 L -.703 -.186 L -.129 -.13 L -.561 .076 L -.508 -.041 L -.073 .137 L -.373 .18 L -.46 -.254 L -.483 .046 L -.168 -.056 L -.558 .204 L -.104 .177 L -.149 .135 L -.18 -.12 L -.389 .083 L -.003 -.204 L .026 -.083 L -.066 -.18 L -.331 .025 L -.073 -.169 L -.194 -.24 L -.261 .024 L -.062 .112 L -.193 -.016 L -.183 .24 L -.43 .048 L -.03 -.228 L -.285 -.041 L -.105 -.331 L -.382 -.096 L -.188 -.208 L .02 -.249 L -.802 -.132 L -.311 -.181 L -.368 .161 L -.293 -.013 L -.013 -.221 L .001 -.253 L -.329 .084 L .181 -.283 L -.709 .005 L -.258 -.121 L -.509 -.193 L -.286 -.024 L -.003 .145 L .282 .302 L -.41 .12 L -.22 .187 L -.318 -.072 L -.296 -.38 L -.55 -.247 L .224 -.211 L .208 -.024 L .099 -.121 L -.199 -.181 L -.257 -.024 L -.061 .066 L -.466 .066 L .005 -.169 L -.278 .037 L -.132 -.181 L -.568 -.054 L -.201 -.025 L -.363 -.163 L -.093 -.26 L -.211 -.091 L -.361 .018 L -.063 .084 L .062 .073 L -.024 .151 L -.375 0 L .435 -.698 L -.281 -.485 L -.468 -.188 L .114 -.258 L -.325 -.27 L .007 -.191 L .214 -.124 L .118 -.292 L -.131 -.102 L .021 -.304 L -.312 -.146 L -.094 -.18 L .217 -.395 L -.683 -.429 L -.391 -.102 L .051 -.328 L .391 -.147 L .219 -.52 L -.319 -.835 L .34 .043 L .23 .125 L -.093 -.193 L .156 -.153 L -.019 -.147 L -.166 -.13 L -.021 -.129 L -.012 -.075 L .93 -.154 L .431 -.051 L .516 -.152 L 1.304 -.128 L .48 -.158 L .797 -.479 L 1.117 -.298 L 1.515 -.303 L 1.382 -.086 L 1.067 .417 L -.882 -.294 L -.054 .147 L .256 .172 L .132 .466 L .355 .135 L .648 .061 L .645 -.11 L 1.063 -.409 L .054 .004 L -.978 .417 L -.19 .11 L -.004 .123 L .218 .012 L .286 -.123 L .613 -.208 L .543 -.253 L .842 .006 L 2.246 .115 L 2.96 -.101 L .641 -.021 L .04 .014 L .679 .243 L .284 .487 L .099 .186 L .016 .029 L .09 .171 L .16 .302 L .615 1.5 L .172 .502 L -.023 .236 L -.263 .208 L -.44 .03 L -.571 .244 L -.291 .328 L -.128 .292 L .289 .012 L .421 .146 L .149 .176 L .218 .146 L .013 .25 L -.315 .752 L .126 .137 L .161 .175 L -.074 .242 L .816 .835 L .316 .193 L -.298 .048 L -.085 .133 L .328 .302 L -.009 .314 L -.255 .24 L -.429 .013 L -.286 .181 L -.599 .398 L -1.509 1.218 L -.082 .306 L .044 .552 L .349 .383 L -.653 -.018 L h 431.844 80.27 m .232 .024 L .069 .208 L -.134 -.029 L -.168 -.203 L h 432.739 80.361 m -.03 .129 L -.278 -.024 L -.191 .03 L -.046 -.208 L .174 -.012 L .266 0 L .106 .085 L
+416.488 81.945 N .151 .438 L -.345 .361 L -.084 .401 L -.571 -.036 L -.113 .181 L -.029 .184 L .44 .134 L .188 -.061 L .16 .17 L -.003 .328 L -.412 .207 L -.128 .376 L -.32 .152 L -.664 .151 L -.378 -.134 L -.175 .092 L -.341 .078 L -.07 .182 L .376 .182 L .082 .265 L .152 .292 L -.095 .148 L -.201 .275 L .193 .121 L -.236 .133 L -.235 .133 L -.08 .097 L .182 .037 L .143 .217 L -.165 .278 L -.672 .024 L -.001 -.338 L .172 -.097 L .11 -.423 L -.754 -.375 L -.535 .109 L -.292 -.375 L -.228 -.061 L -.173 .121 L -.313 -.242 L -.267 .182 L -.307 0 L -.307 -.012 L .025 .157 L -.52 .034 L -.407 -.042 L -.477 -.098 L -.07 -.162 L -.496 -.264 L .795 -.472 L .858 -.621 L .273 -.354 L .563 -1.167 L .173 -.104 L .644 -.12 L .367 .251 L .136 .268 L -.28 .203 L -.228 -.056 L .066 .496 L -.191 .124 L 1.163 .225 L .225 -.236 L .406 -.113 L .134 -.417 L -.458 -.022 L -.123 -.124 L -.02 -.203 L .236 -.158 L -.171 -.023 L -.558 -.067 L .072 -.328 L -.034 -.13 L -.023 -.034 L .128 -.283 L .572 -.198 L .709 -.238 L .654 .03 L 1.089 -.151 L .245 .275 L .329 .045 L .076 .181 L .259 0 L
+431.57 91.965 N -.447 -.633 L -.207 -.036 L -.165 -.18 L -.219 .084 L 430.349 91 l -.431 -.17 L -.573 -.515 L -.188 .072 L -.478 -.265 L -.244 -.173 L -.036 -.233 L -.159 -.085 L .08 -.406 L .155 -.25 L -.413 -.098 L -.258 -.378 L -.037 -.056 L .243 -.168 L .26 -.181 L .293 .033 L .422 -.201 L .442 0 L .078 -.181 L .382 -.012 L .097 -.229 L .358 -.061 L .176 .072 L .112 -.217 L .735 -.024 L .124 -.169 L .524 -.048 L .284 -.097 L .254 -.084 L -.282 -.218 L .436 -.072 L .308 .121 L .031 .278 L .344 .072 L .142 -.115 L .375 0 L .024 -.151 L -.062 -.073 L .063 -.084 L .361 -.018 L .211 .091 L .093 .26 L .363 .163 L .201 .025 L .568 .054 L .132 .181 L .278 -.037 L -.005 .169 L .466 -.066 L .061 -.066 L .257 .024 L .199 .181 L -.099 .121 L -.208 .024 L -.224 .211 L .55 .247 L .296 .38 L .318 .072 L .22 -.187 L .41 -.12 L -.282 -.302 L .003 -.145 L .286 .024 L .509 .193 L .258 .121 L .709 -.005 L -.181 .283 L .329 -.084 L -.001 .253 L .013 .221 L .293 .013 L .368 -.161 L .311 .181 L .802 .132 L -.02 .249 L .188 .208 L .382 .096 L .105 .331 L -.071 -.012 L -.065 .066 L -.066 .04 L -.105 -.026 L 442 89.998 l -.119 .013 L -.065 .04 L -.132 .066 L -.053 .079 L -.118 .092 L -.119 .053 L -.132 .079 L -.105 .013 L -.118 .013 L -.053 .092 L -.026 .092 L -.026 .132 L -.053 .079 L -.053 .105 L -.145 .066 L -.105 .04 L -.092 -.026 L -.079 .079 L -.04 .092 L -.053 .079 L -.039 0 L -.105 .026 L -.066 .026 L -.079 .053 L -.118 .053 L -.132 0 L -.145 .04 L -.093 .026 L -.065 -.026 L -.065 -.026 L -.093 0 L -.065 .026 L -.158 0 L -.132 -.053 L -.092 -.053 L -.093 .026 L -.171 .053 L -.026 .04 L -.146 .119 L -.092 .066 L -.014 .131 L -.065 .105 L -.039 .044 L -.055 -.095 L -.545 -.2 L -1.332 .181 L -1.019 -.227 L -1.374 -.341 L -.746 .773 L -.598 .163 L -.483 -.099 L -.354 -.129 L -.145 -.014 L
+421.683 94.397 N .368 .014 L .164 -.137 L .096 .11 L .249 .007 L .104 .137 L .194 .048 L -.011 .165 L .26 .039 L -.078 .195 L .215 0 L .452 -.343 L -.032 -.274 L .237 .041 L .262 -.055 L .493 .089 L .093 .145 L .059 .144 L 425.4 94.7 l .377 -.103 L .044 -.13 L .319 -.014 L .125 -.171 L 1.25 -.035 L .092 -.178 L .433 -.035 L .195 .138 L .564 -.062 L .146 .117 L -.049 .165 L .441 .178 L .082 -.055 L .098 -.343 L -.056 -.158 L -.334 -.014 L .197 -.295 L -.538 -.571 L .364 -.227 L .968 -.295 L .162 -.172 L .039 -.337 L .132 -.027 L .437 .151 L .682 -.26 L .145 .014 L .354 .129 L .483 .099 L .598 -.163 L .746 -.773 L 1.374 .341 L 1.019 .227 L 1.332 -.181 L .545 .2 L .055 .095 L .053 .093 L -.097 .581 L .155 .26 L .761 .547 L -.636 .291 L -.019 .367 L -.701 .054 L -.164 -.134 L -.325 0 L -.223 .197 L .452 .063 L .136 .196 L -.157 .206 L -.409 .062 L -.04 .375 L .204 .188 L -.154 .294 L -.346 .299 L -.693 .157 L .082 .393 L -.89 -.116 L -.298 .196 L -.85 -.08 L -.566 .107 L -.646 .5 L -.287 -.152 L -.612 .009 L -.179 -.107 L -1.04 -.065 L -.478 -.112 L -.894 -.082 L -1.311 -.247 L -.32 -.391 L -.318 -.096 L -.023 -.363 L -.856 .227 L -.301 -.103 L -.445 .11 L -.183 -.083 L -.366 .096 L -.329 .411 L -.597 -.034 L -.631 -.171 L .029 -.171 L -.215 -.117 L -.579 .332 L -.55 -.242 L -.008 -.144 L -.622 -.062 L .09 -.192 L -.214 -.288 L .266 -.274 L -.193 -.336 L
+431.763 171.063 N -.067 .354 L .091 .72 L .108 .508 L .225 .168 L .562 .11 L .144 .183 L .077 .353 L -.089 1.116 L -.146 .227 L -.274 .171 L -.885 .217 L -.291 .256 L -.664 1.275 L -.503 1.203 L -.243 1.004 L -.354 .129 L -.369 .03 L -.129 .354 L -.146 1.229 L -.192 .312 L -.385 .045 L -.257 .284 L -.417 .836 L -.944 2.223 L -.304 .624 L -.352 .496 L -.368 .355 L -.239 .114 L -.145 -.056 L -.722 -.97 L -.145 -.14 L -1.104 -.05 L -.272 .03 L -1.31 1.265 L -.941 .839 L -.495 .526 L .02 .974 L -.189 .552 L -.376 .686 L -.188 -.119 L -.224 -.042 L -.176 -.127 L -.145 .212 L .144 .296 L -.063 .127 L -.353 .198 L -.56 .03 L -.977 .101 L -.607 -.267 L -.288 .043 L -.271 .368 L -.177 .113 L -.432 -.07 L -1.247 -.011 L -.528 -.225 L -.543 -.451 L -.416 -.72 L -.192 -.649 L .048 -.254 L .208 -.254 L -.144 -.296 L -.513 -.069 L -.128 -.254 L -.464 -.55 L -.561 -.465 L -.608 -.253 L -.641 -.253 L -.272 -.31 L -.513 .072 L -.24 .297 L -.336 .071 L -.881 .044 L -.659 .03 L -.006 -.234 L .121 -1.188 L -.217 -1.439 L .09 -.989 L .012 -.876 L -.003 -.48 L -.002 -.381 L .237 -.397 L .257 -.03 L .433 -.13 L .078 -.368 L -.116 -.592 L .095 -.34 L .656 -.414 L .223 -.298 L .19 -.566 L .316 -.962 L -.388 -.718 L -.164 -.762 L .062 -.777 L .092 -.961 L .158 -.849 L .353 -.482 L .687 -1.614 L .676 -.4 L 1.064 -.188 L 1.174 -.108 L 1.111 .121 L .821 .277 L 1.095 1.223 L .209 .098 L .37 -.002 L 1.337 -.544 L .467 -.116 L .354 .083 L 1.173 .742 L .965 .277 L .934 -.005 L .273 -.059 L .629 -.399 L .403 -.327 L .774 -.287 L .628 -.103 L .709 -.047 L .531 .04 L .884 .221 L .724 .193 L .609 .208 L .259 -.058 L .761 -.697 L .453 -.299 L .437 -.2 L .951 -.034 L .173 .394 L .575 .52 L .351 .407 L
+425.506 195.522 N -1.965 -.017 L -1.005 .007 L .029 -.176 L -.208 -.536 L .271 -.989 L -.159 -.72 L -.191 -.522 L -.399 -.649 L .433 -.396 L -.287 -.367 L -.24 -.056 L -.576 .058 L -.559 -.268 L -.176 -.324 L -.063 -.537 L -.111 -.211 L -.527 -.14 L -.101 -.064 L .376 -.686 L .189 -.552 L -.02 -.974 L .495 -.526 L .941 -.839 L 1.31 -1.265 L .272 -.03 L 1.104 .05 L .145 .14 L .722 .97 L .145 .056 L .239 -.114 L .368 -.355 L .352 -.496 L .304 -.624 L .944 -2.223 L .417 -.836 L .257 -.284 L .385 -.045 L .192 -.312 L .146 -1.229 L .129 -.354 L .369 -.03 L .354 -.129 L .243 -1.004 L .503 -1.203 L .664 -1.275 L .291 -.256 L .885 -.217 L .274 -.171 L .146 -.227 L .089 -1.116 L -.077 -.353 L -.144 -.183 L -.562 -.11 L -.225 -.168 L -.108 -.508 L -.091 -.72 L .067 -.354 L .627 .109 L .208 .083 L .923 1.238 L .395 .887 L .119 1.2 L -.134 .651 L .169 1.228 L .205 .578 L .381 .633 L .208 .21 L .047 .127 L -.225 .143 L -.674 .032 L -1.187 -.191 L -.481 -.082 L -.24 .1 L -.29 .312 L -.323 .539 L 1.088 1.067 L 1.343 1.179 L .638 1.07 L .382 1.057 L .208 .183 L -.208 .114 L -.354 .327 L -.866 1.743 L -.4 .511 L -.528 .399 L -.144 .198 L -.064 .424 L .112 .776 L .273 1.185 L .207 .592 L .4 .661 L .432 .605 L .064 .791 L .112 .324 L 1.217 1.236 L .498 .802 L .194 .804 L .209 .451 L -.159 .185 L .1 .861 L -.271 .086 L -.863 -.292 L -.864 -.334 L -.576 -.194 L -.56 -.124 L -.479 -.04 L -.271 -.055 L -.878 .076 L -.863 -.023 L -.56 -.109 L -1.022 -.149 L -.831 -.038 L -1.773 -.088 L -.88 -.093 L -.045 .495 L
+551.793 147.278 N -.788 -.111 L -.458 -.253 L -.379 -.367 L -.248 -1.101 L -.112 -.141 L -.342 -.141 L -.662 -.083 L -.105 -.07 L -.014 -.268 L .116 -.466 L -.178 -.451 L -.418 -.38 L -.294 -.013 L -1.181 .412 L -1.229 .087 L -.81 .143 L -.947 .073 L -.415 -.083 L -.263 -.226 L -.135 -.197 L -.909 .143 L -.548 .382 L -.856 .016 L -1.264 -.011 L -.691 .128 L -.723 .115 L -.541 .03 L .182 -.954 L .049 -.354 L .31 -.792 L .285 -.383 L .794 -.47 L .697 -.258 L 1 -.077 L .209 -.072 L .188 -.255 L .036 -.974 L -.186 -.281 L -.652 -.053 L -.185 -.394 L -.111 -1.34 L -.168 -.38 L -.409 -.224 L -1.144 -.388 L -.695 -.391 L -.353 -.464 L -.71 -1.068 L -.802 -1.083 L .887 .32 L .63 .194 L 1.069 .248 L .854 .235 L .541 .039 L .742 .024 L 1.187 -.218 L .349 -.002 L 1.005 .135 L 1.234 -.12 L .964 -.175 L 1.183 -.19 L 1.066 -.274 L .247 -.156 L .069 -.184 L .099 -.903 L .232 -.876 L .188 -.34 L .412 -.369 L .597 -.441 L .283 -.072 L .644 .039 L .516 .082 L .254 -.086 L .321 -.693 L .362 -.271 L .559 -.243 L .562 .025 L 1.001 .375 L .237 -.086 L .34 -.383 L .021 -.084 L .139 -1.214 L .288 -.834 L .224 -.424 L .292 -.228 L .416 -.115 L .513 -.031 L .315 -.115 L .199 -.255 L -.088 -.437 L -.129 -.281 L 556.193 125 l -.35 -.336 L .355 -.129 L .997 .122 L .698 .039 L .513 -.215 L .289 -.255 L -.033 -.31 L -.223 -.478 L .13 -.283 L .4 -.341 L .524 -.355 L .324 -.426 L -.198 -.477 L .119 -.483 L -.232 -.171 L -.01 -.336 L -.411 -.271 L -.01 -.32 L .464 -.413 L .258 -.214 L .246 .023 L .559 -.395 L .272 -.19 L .53 -.292 L 1.148 -.152 L 1.345 -.012 L .647 .144 L .338 -.179 L .332 .005 L .455 -.297 L .358 -.004 L .148 -.142 L .313 .14 L .105 .112 L .281 -.21 L .296 .047 L .554 .117 L .004 .456 L .282 -.128 L .625 -.009 L .373 .323 L .213 .247 L .007 .501 L -.312 .516 L .301 .07 L .198 .218 L .252 .281 L .52 -.196 L .239 0 L .104 .199 L .178 .09 L .565 .436 L 1.337 .104 L .305 .111 L .147 .126 L -.207 .084 L -.688 .37 L -.411 .232 L -.155 .43 L -.071 .479 L -.234 .116 L -.125 -.179 L -.621 -.102 L -.438 .132 L -.271 .298 L -.262 -.116 L -.557 .265 L -.928 -.149 L -.568 -.138 L -1.708 -.325 L -.894 .265 L -.38 .611 L .09 .166 L .336 0 L .047 .314 L -.202 .149 L .101 .199 L .598 -.017 L -.002 .347 L -.384 .099 L -.139 .413 L .389 .232 L -.116 .479 L -.215 .132 L .089 .248 L .606 .298 L -.003 .532 L .782 -.003 L -.039 .43 L .188 .248 L .702 -.05 L .668 .377 L .012 .211 L -.2 .255 L -.504 .2 L -.643 .196 L -.517 .248 L .032 .366 L .188 .377 L -.237 .977 L -1.512 1.503 L .261 .38 L -.358 .281 L -.609 .133 L -.353 .467 L -.509 1.216 L -.478 .547 L -1.193 .496 L -.177 .364 L -.277 .481 L -.563 .61 L -.076 .38 L -.71 .259 L -.734 -.011 L -.895 .314 L -.361 -.094 L -.158 -.518 L -.207 -.132 L -.404 .083 L -.535 .463 L -.144 .446 L -.979 .893 L -.186 .543 L -.079 .269 L .116 .38 L .541 .163 L .504 .085 L .425 .014 L -.114 .689 L -.154 .735 L .412 .411 L .33 .099 L .537 -.099 L .112 .441 L .216 .507 L .654 1.333 L -.232 .149 L .241 .463 L -.317 .066 L -.15 .248 L -.55 .083 L -.145 -.335 L -.183 -.062 L -.78 .215 L -.2 .331 L -.735 -.099 L -.229 -.149 L -.835 -.021 L -.835 -.009 L -.259 -.052 L .015 .728 L -.866 .017 L -.296 .431 L -.37 .036 L
+606.155 150.953 N -.037 -.466 L -.627 -2.479 L -.064 -.578 L -.4 .044 L -.281 .115 L -.395 1.159 L -.224 .354 L -.529 -.448 L -.249 -.267 L -.23 -.281 L -.001 -.381 L -.009 -.522 L .131 -.283 L .651 -.356 L .498 -.13 L .938 -.767 L .159 -.34 L .012 -.79 L -.057 -.127 L -.111 -.098 L -2.473 -.198 L -.559 .003 L -.916 .062 L -.547 -.053 L -.537 -.109 L -.095 -.099 L -.079 -.719 L -.053 -.479 L -.129 -.324 L -.417 -.054 L -.489 .06 L -.372 -.266 L -.339 -.266 L -1.252 -.303 L -.234 .171 L -.555 .525 L -.124 .283 L .083 .155 L .494 .237 L 1.224 .938 L .126 .112 L -.047 .155 L -.382 .101 L -.502 .13 L -.4 .299 L -.548 .61 L .339 .323 L 1.042 .431 L .193 .21 L -.029 .269 L -.505 .765 L .04 .254 L .352 .45 L .325 1.409 L .402 1.775 L .147 .532 L -.172 .322 L .068 .183 L -.321 .071 L -.35 -.056 L -.148 -.706 L -.198 -.084 L -.253 .354 L -.171 .354 L -.172 .057 L -.115 -.099 L -.28 -.592 L -.152 -.169 L -.412 .537 L -.504 .298 L -.992 .397 L -.571 .213 L -.264 .226 L -.05 .113 L .106 .579 L .183 .409 L -.336 .495 L -.305 .332 L -.013 .257 L -.548 .415 L -.593 .467 L -.467 .185 L -.432 .057 L -.746 .206 L -.591 .362 L -.459 .58 L -.551 .552 L -.88 1.061 L -.611 .552 L -.673 .276 L -.675 .829 L -.676 .467 L -.728 .368 L -.452 .241 L -.427 .312 L -.192 .509 L -.073 .466 L -.341 .41 L -.649 .298 L -.428 .058 L -.506 -.098 L -.258 .269 L -.366 .887 L -.277 .23 L -.505 -.253 L -.614 .142 L -.354 .283 L -.304 .565 L -.165 .48 L -.005 .508 L .166 1.539 L .018 .805 L -.208 .495 L .227 .324 L .315 .423 L -.015 .508 L -.199 .607 L -.669 1.611 L -.341 .834 L -.13 .636 L .047 .649 L .21 1.764 L -.081 .24 L -.643 .001 L -.433 -.013 L -.186 .142 L -.334 .961 L -.324 .594 L .025 .127 L .493 .352 L -.805 .327 L -.306 .015 L -.77 .284 L -.295 .41 L -.101 .692 L -.157 .255 L -.367 .283 L -.526 .255 L -.099 .057 L -.066 .042 L -.231 .156 L -.145 0 L -.174 -.056 L -1.417 -1.451 L -.092 -.395 L -.276 -.522 L -.34 -1.397 L -.689 -1.792 L -.94 -1.933 L -.979 -1.508 L -.554 -1.722 L -.507 -1.792 L -.738 -1.636 L -.82 -1.42 L -.424 -.737 L -.598 -1.439 L -.363 -1.157 L -.767 -3.274 L -.469 -2.695 L -.157 -1.566 L -.051 -.24 L .232 -.565 L .546 -.834 L .007 -.311 L -.404 -.281 L -.367 -.437 L -.113 -.635 L -.108 -.917 L .065 -.622 L -.837 .03 L -.19 .212 L -.235 .424 L .009 .183 L .276 .254 L .074 .183 L -.073 .438 L -.301 .438 L -.503 .368 L -.812 .369 L -.504 .114 L -.617 .255 L -.377 .015 L -.861 -.281 L -.572 -.38 L -1.018 -1 L -1.391 -1.268 L -.516 -.705 L .267 -.043 L 1.062 .125 L .615 -.086 L .443 -.142 L .461 -.283 L .521 -.608 L -.067 -.24 L -.156 .071 L -.587 .114 L -.847 -.026 L -.607 -.112 L -.632 -.132 L -.5 -.218 L -.203 -.07 L -.929 -.661 L -.668 -.775 L -.057 -.197 L .37 -.036 L .296 -.431 L .866 -.017 L -.015 -.728 L .259 .052 L .835 .009 L .835 .021 L .229 .149 L .735 .099 L .2 -.331 L .78 -.215 L .183 .062 L .145 .335 L .55 -.083 L .15 -.248 L .317 -.066 L -.241 -.463 L .232 -.149 L -.654 -1.333 L -.216 -.507 L -.112 -.441 L 557 143.058 l -.33 -.099 L -.412 -.411 L .154 -.735 L .114 -.689 L -.425 -.014 L -.504 -.085 L -.541 -.163 L -.116 -.38 L .079 -.269 L .186 -.543 L .979 -.893 L .144 -.446 L .535 -.463 L .404 -.083 L .207 .132 L .158 .518 L .361 .094 L .895 -.314 L .734 .011 L .71 -.259 L .076 -.38 L .563 -.61 L .277 -.481 L .177 -.364 L 1.193 -.496 L .478 -.547 L .509 -1.216 L .353 -.467 L .609 -.133 L .358 -.281 L -.261 -.38 L 1.512 -1.503 L .237 -.977 L -.188 -.377 L -.032 -.366 L .517 -.248 L .643 -.196 L .504 -.2 L .2 -.255 L -.012 -.211 L -.668 -.377 L -.702 .05 L -.188 -.248 L .039 -.43 L -.782 .003 L .003 -.532 L -.606 -.298 L -.089 -.248 L .215 -.132 L .116 -.479 L -.389 -.232 L .139 -.413 L .384 -.099 L .002 -.347 L -.598 .017 L -.101 -.199 L .202 -.149 L -.047 -.314 L -.336 0 L -.09 -.166 L .38 -.611 L .894 -.265 L 1.708 .325 L .568 .138 L .928 .149 L .557 -.265 L .262 .116 L .271 -.298 L .438 -.132 L .621 .102 L .125 .179 L .234 -.116 L .071 -.479 L .155 -.43 L .411 -.232 L .688 -.37 L .207 -.084 L .502 -.152 L 0 .515 L .441 1.03 L .221 .515 L .81 .11 L .441 .405 L .221 .258 L -.331 .221 L -.184 .184 L .073 .589 L .037 .552 L .294 .221 L .718 .096 L .092 .078 L .044 .182 L .68 .099 L -.104 .414 L .367 .694 L -.578 .364 L -.376 .281 L -.368 .049 L -.337 -.264 L -.033 -.364 L -.785 .198 L .157 .694 L .579 .595 L -.104 .446 L .272 .364 L -.215 .215 L .099 .43 L .198 .066 L .477 -.314 L .383 .248 L .772 .843 L .363 -.116 L 1.078 .579 L -.116 .364 L .756 .248 L .154 -.066 L .859 .596 L -1.07 .909 L -.281 .116 L .008 .364 L -.221 .265 L .04 .396 L -.309 .397 L -.218 .43 L .039 .248 L .63 .38 L .466 -.083 L .665 .479 L .704 .149 L .305 .529 L 1.242 .678 L .231 -.166 L 1.027 .595 L .538 -.066 L .182 .397 L .835 .166 L .304 .198 L .15 -.066 L .087 -.215 L .503 0 L .482 .265 L .582 -.347 L .494 .298 L .643 .066 L .142 .231 L -.101 .446 L .663 .149 L .286 .281 L .618 .265 L .584 -.281 L .313 .066 L -.034 .331 L .312 .248 L .379 -.231 L .721 .148 L .847 .364 L .606 -.297 L .268 .364 L .399 .116 L .357 -.132 L .271 .083 L .687 -.132 L .336 .05 L .311 -.694 L -.429 -.827 L .07 -.876 L .318 -.761 L -.037 -.022 L .345 -.129 L .471 -.102 L .231 .069 L .139 .183 L -.052 .861 L .212 .662 L -.092 .113 L .235 .493 L .637 .18 L .559 .081 L .869 .214 L .085 .021 L .544 -.074 L .531 -.313 L .301 .055 L .335 .125 L 1.238 .063 L 1.036 -.062 L .624 -.06 L .217 -.58 L -.017 -.282 L -.258 -.238 L -.313 -.069 L -.326 -.294 L -.042 -.24 L .07 -.156 L .189 -.1 L .784 -.033 L .714 -.216 L .397 -.27 L 1.123 -1.107 L .543 -.37 L .829 -.258 L 1.105 -.726 L .546 -.299 L .355 -.087 L .286 .167 L .975 .319 L .377 -.044 L 1.377 -.84 L .455 .392 L .716 .715 L .046 .38 L -.354 .454 L -.138 .269 L .102 .056 L .3 .055 L .657 .081 L .73 .179 L .624 .081 L -.018 .508 L -.112 .24 L -.596 .582 L -.072 .283 L .184 .718 L -.295 .058 L -.853 -.221 L -.558 -.082 L -.312 .129 L -.719 .512 L -1.446 .883 L -.197 .354 L .057 .761 L -.136 .382 L -.938 1.134 L -.045 .212 L .192 .408 L 0 .324 L -1.148 2.25 L -.17 .128 L -.309 -.013 L -.391 -.11 L -.811 -.32 L -.133 .283 L .146 1.594 L -.191 .354 L -.401 .144 L -.228 .185 L .152 .93 L -.015 .678 L -.273 .368 L -.25 .129 L -.255 -.14 L -.595 -.152 L
+478.516 135.173 N .052 -.292 L -.049 -.169 L -.197 -.107 L .107 -.175 L .292 -.919 L -.015 -.818 L .517 -1.09 L .3 -.891 L -.004 -.409 L .133 -.283 L .07 -.749 L .033 -.847 L .062 -.174 L .026 -.278 L -.065 -.411 L .037 -.119 L .102 -.063 L 1.902 1.034 L .365 .167 L .253 -.016 L 1.139 -.656 L 2.96 -1.541 L .161 -.071 L .87 1.632 L .311 .69 L .093 .056 L -.46 .44 L -.257 .171 L -.97 .26 L -2.586 .622 L -.646 .229 L 1.146 1.32 L .698 .885 L -.765 .724 L -.456 .37 L -.24 .072 L -1.249 .332 L -.462 .61 L -.975 .782 L -2.079 -.299 L -.154 -.017 L
+456.028 151.46 N -.021 1.003 L .033 3.078 L -.088 .17 L -1.561 -.019 L -.52 -.011 L -.073 1.13 L -1.981 -.963 L -5.577 -2.792 L -4.941 -2.372 L -5.231 -2.554 L -.41 .059 L -1.818 .858 L -1.519 .729 L -.231 .157 L -1.192 -.911 L -.258 -.182 L -1.008 -.333 L -1.646 -.442 L -.832 -.137 L -.098 -.07 L -.86 -1.506 L -.178 -.168 L -1.028 -.347 L -.702 -.165 L -.544 .201 L -.244 -.154 L -.562 -.873 L -.249 -.917 L -1.272 -1.702 L .152 -.283 L .249 -.128 L .666 -.47 L .086 -.17 L .061 -.664 L -.457 -1.156 L .158 -.495 L .16 -.622 L -.101 -.748 L .1 -.862 L -.052 -.691 L -.147 -.833 L -.88 -1.605 L .241 -.383 L .204 -.156 L .22 -.143 L .864 -.598 L .155 -.227 L .147 -.537 L -.274 -.973 L .126 -.354 L 1.473 -1.138 L .579 -.314 L .802 -.428 L .093 -.269 L -.064 -.564 L .093 -.981 L 1.436 .59 L .995 .309 L .521 .013 L .863 -.129 L .604 .027 L 1.552 .223 L .614 .479 L .794 .224 L .486 -.001 L .342 .197 L .173 .226 L .26 .819 L .39 .564 L .688 .591 L .378 .126 L .678 .14 L 1.031 .083 L .447 .07 L .973 .224 L .639 .224 L .552 .281 L 1.289 .788 L .84 .464 L .465 .013 L .441 -.128 L .758 -.411 L .706 -.623 L .3 -.523 L 0 -.254 L -.276 -.621 L -.112 -.579 L .044 -.41 L .237 -.537 L .661 -.58 L .333 -.198 L .554 -.241 L .644 -.298 L 1.376 -.413 L 1.205 .054 L .728 .154 L .844 .365 L .188 .169 L .043 .508 L .281 .253 L .301 .014 L .945 .125 L .712 .309 L .45 .027 L 1.286 .04 L .302 .197 L .068 .381 L .276 .296 L -.099 .208 L -.369 .228 L -.21 .34 L .354 1.283 L -.231 .425 L -.322 .553 L -.056 .311 L .185 .945 L .388 .916 L .042 .367 L -.015 .988 L -.019 3.869 L -.016 1.468 L -.008 2.048 L -.001 .692 L .045 2.81 L -.021 1.342 L .011 2.612 L
+478.212 134.714 N -.834 -1.746 L -.796 -1.944 L -.124 -.289 L .109 -.066 L .679 -.851 L .219 -.438 L .183 -.507 L .171 -.566 L .188 -.822 L .116 .018 L .137 -.093 L .006 -.204 L .028 -.315 L .372 -.051 L .151 .022 L .07 .099 L .355 -.07 L .009 -.167 L .067 -.193 L .088 -.041 L .07 .023 L .183 .346 L .201 .128 L .021 .183 L -.047 .122 L .082 .085 L -.102 .063 L -.037 .119 L .065 .411 L -.026 .278 L -.062 .174 L -.033 .847 L -.07 .749 L -.133 .283 L .004 .409 L -.3 .891 L -.517 1.09 L .015 .818 L -.292 .919 L -.107 .175 L -.067 .068 L -.042 .042 L
+495.751 163.817 N -.03 -.184 L -.112 -.537 L -.508 -.945 L -.73 -.987 L -.438 -.493 L -.846 -.69 L -.51 -.875 L -.814 -1.876 L -.474 -.889 L -.28 -.409 L -.794 -.507 L -1.271 -.689 L -.527 -.634 L -.8 -1.17 L -.081 -.96 L -.061 -.776 L -.002 -.89 L -.104 -.381 L -.312 -.663 L -.944 -1.65 L -.357 -.494 L -.758 -.62 L -.459 -.225 L -.499 -.126 L -.308 -.282 L -.483 -.578 L .149 -.692 L -.111 -.437 L -.474 -.818 L -.747 -1.072 L -.793 -.902 L -1.069 -1.494 L -.525 -.931 L -.352 -.479 L -.722 -.761 L -.524 -.056 L -.248 0 L .077 -.296 L .193 -.283 L .311 -1.314 L .104 -.583 L .154 .017 L 2.079 .299 L .975 -.782 L .462 -.61 L 1.249 -.332 L .24 -.072 L .456 -.37 L .765 -.724 L -.698 -.885 L -1.146 -1.32 L .646 -.229 L 2.586 -.622 L .97 -.26 L .257 -.171 L .46 -.44 L .093 .056 L 1.129 .459 L .757 .222 L 2.034 .934 L 1.14 .473 L 2.44 1.101 L .258 .196 L .399 .633 L .141 .07 L .973 -.048 L .135 .366 L -.118 .396 L .047 .818 L .118 .253 L .812 .277 L 1.557 .697 L 4.048 .132 L 1.657 .259 L .408 .054 L .334 .619 L .185 .239 L 1.07 .05 L .644 -.044 L .043 .121 L .277 .648 L .365 .494 L .178 .663 L .052 .113 L .615 -.029 L .248 .126 L -.204 .237 L .194 .187 L .184 .282 L .234 .067 L .234 .27 L .886 .168 L .423 .437 L -.07 .28 L -.235 .035 L -.111 .25 L .316 .381 L -.188 .593 L -.122 .198 L .165 .268 L .302 .666 L .149 -.117 L .372 .282 L .105 .616 L .422 .696 L -.028 .423 L .222 .268 L .16 .197 L .287 .084 L .136 -.007 L .336 .083 L 1.043 2.198 L .548 1.154 L .261 .902 L 3.362 .375 L 3.387 .446 L .839 .108 L .584 -.469 L .152 -.057 L .581 .914 L .155 1.453 L -5.346 2.982 L -2.35 1.213 L -1.691 .815 L -.169 .085 L -1.148 .346 L -3.097 1.035 L -4.618 1.566 L -.484 .229 L -.041 .127 L -.383 .397 L -1.907 2.271 L -2.042 2.667 L -1.302 -2.703 L -1.211 -2.45 L -.452 -.012 L -.715 .047 L -.446 -.125 L -.671 -.151 L -.216 .1 L -.174 .185 L -.11 .495 L .041 .678 L -.113 .565 L -.692 .563 L
+476.458 130.735 N .124 .289 L .796 1.944 L .834 1.746 L -.38 .381 L -.479 1.145 L -.334 1.413 L -.171 .593 L -.18 .156 L -.407 -.07 L -.448 -.521 L -.782 -.676 L -.386 -.494 L -.338 -.988 L -.521 -.45 L -.289 -.621 L -.17 -.479 L -.198 -.353 L -.466 .17 L -.267 .523 L .604 1.34 L .131 .381 L .634 .86 L .933 1.042 L .473 1.199 L .526 .973 L .277 .818 L .391 .465 L .912 1.735 L 1.072 1.904 L .428 .705 L .586 .549 L .451 .352 L .151 .183 L -.241 .17 L -.285 .1 L -.043 .155 L .238 1.087 L .252 .467 L .127 .238 L .812 .591 L .397 .168 L .406 .521 L .416 .38 L .311 .56 L -10.382 -.006 L -2.138 -.001 L -2.774 .002 L -2.513 .015 L -2.268 -.029 L -1.664 .01 L -1.241 .007 L -1.614 -.019 L -.914 .005 L -.819 .089 L -.011 -2.612 L .021 -1.342 L -.045 -2.81 L .001 -.692 L .008 -2.048 L .016 -1.468 L .019 -3.869 L .015 -.988 L -.042 -.367 L -.388 -.916 L -.185 -.945 L .056 -.311 L .322 -.553 L .231 -.425 L -.354 -1.283 L .21 -.34 L .369 -.228 L .099 -.208 L .337 .168 L .553 -.015 L .628 -.1 L .786 -.001 L 1.513 .293 L .587 .115 L .779 .122 L .721 .154 L .348 .409 L .4 0 L .928 .083 L .998 .394 L .628 .069 L .245 -.127 L .72 -.538 L .925 -.397 L .837 -.186 L .78 -.27 L .375 -.072 L .346 .056 L .535 -.001 L .723 .126 L .202 .465 L .596 .366 L .518 -.156 L .64 .267 L .382 .027 L .561 -.401 L .241 .179 L .277 .056 L .674 .13 L .338 -.062 L .46 -.165 L .51 -.308 L
+580.625 132.873 N .24 .43 L .299 .05 L .276 -.314 L .134 -.513 L .275 .066 L .186 -.182 L 1.079 .199 L .132 .612 L .867 .264 L .726 .595 L .472 .215 L .15 -.132 L .454 .281 L .657 .794 L .158 -.166 L .71 -.116 L .289 .166 L .178 .38 L -.069 .281 L 1.451 .893 L .333 -.248 L .326 .017 L -.175 .396 L .014 .265 L .935 .066 L .365 -.066 L .54 .496 L .141 .463 L .237 -.099 L -.01 -.364 L .685 .38 L .271 -.083 L .08 -.331 L .407 0 L .63 .281 L .319 .364 L .849 -.066 L .341 .116 L .339 -.281 L .655 .159 L .037 .022 L -.318 .761 L -.07 .876 L .429 .827 L -.311 .694 L -.336 -.05 L -.687 .132 L -.271 -.083 L -.357 .132 L -.399 -.116 L -.268 -.364 L -.606 .297 L -.847 -.364 L -.721 -.148 L -.379 .231 L -.312 -.248 L .034 -.331 L -.313 -.066 L -.584 .281 L -.618 -.265 L -.286 -.281 L -.663 -.149 L .101 -.446 L -.142 -.231 L -.643 -.066 L -.494 -.298 L -.582 .347 L -.482 -.265 L -.503 0 L -.087 .215 L -.15 .066 L -.304 -.198 L -.835 -.166 L -.182 -.397 L -.538 .066 L -1.027 -.595 L -.231 .166 L -1.242 -.678 L -.305 -.529 L -.704 -.149 L -.665 -.479 L -.466 .083 L -.63 -.38 L -.039 -.248 L .218 -.43 L .309 -.397 L -.04 -.396 L .221 -.265 L -.008 -.364 L .281 -.116 L 1.07 -.909 L
+374.125 167.166 N -1.41 -.924 L -.661 -.604 L -.537 -.788 L -.292 -.662 L -.15 -.183 L -.312 -.154 L -.567 -.053 L -.655 -.307 L -.695 -.561 L -.604 -.194 L -.567 -.025 L -1.209 .12 L -1.821 .194 L -.523 .417 L .06 -.502 L .563 -1.301 L .188 -.819 L .13 -1.13 L -.162 -1.101 L -.3 -.791 L -.624 -.747 L .267 -.283 L .182 -.424 L .067 -.975 L -.065 -.537 L -.174 -.353 L -.808 -.817 L -.297 -.112 L -.393 .438 L -.055 -.197 L -.013 -.381 L .08 -.689 L .474 .013 L 5.055 .056 L 2.396 .014 L .732 -.033 L .212 .027 L -.051 -1.102 L -.164 -1.539 L .01 -.707 L .249 -.383 L .575 -.427 L .726 -.315 L .809 -.287 L .144 -.128 L .099 -2.6 L .07 -2.261 L .047 -.509 L .557 -.074 L 6.005 .022 L .56 .011 L .104 -.396 L -.002 -.65 L .03 -1.752 L 2.829 1.509 L 4.12 2.42 L 1.414 .91 L -.523 .13 L -3.231 .089 L .049 .776 L .908 7.962 L .19 1.468 L .178 1.736 L .325 2.753 L .202 2.386 L .186 1.468 L .724 .787 L -.379 1.613 L -6.457 .01 L -1.953 .026 L -.945 .344 L -.387 .031 L -.485 -.054 L -.713 -.137 L -.535 -.124 L -.06 .212 L .055 .296 L -.107 .269 L -.454 -.082 L -.214 -.168 L -.568 -.83 L -.261 -.111 L -.402 .073 L -.252 .256 L -.181 .552 L -.018 .607 L -.332 .284 L
+476.114 191.139 N -.563 .54 L -.626 .314 L -.367 .016 L -.303 -.196 L -.303 -.097 L -.88 .132 L -.881 .33 L -.911 .048 L -.942 -.234 L -.495 -.011 L -.464 .087 L -.496 .229 L -1.288 -1.32 L -1.032 -.926 L -.207 -.083 L -.258 .157 L -.436 .582 L -.16 .1 L -.684 -.631 L -.384 -.012 L -.529 .37 L -.258 .242 L -.352 .016 L -.366 -.195 L -.525 -.477 L -.348 -.493 L -.795 -.772 L -.143 -.239 L -.031 -.07 L .083 -.679 L -.157 -.352 L -.558 -.477 L -.814 -.165 L -.367 -.224 L -.285 -.436 L -.059 -.72 L -.206 -.366 L -.207 -.168 L -.717 -.434 L -.558 -.378 L -.445 -.351 L -.173 -.395 L .148 -.481 L -.223 -.267 L -.351 -.196 L -.799 -.235 L -.797 -.447 L -.223 -.225 L -.252 -.493 L -.207 -.126 L -.272 .001 L -.802 .019 L -.158 -.211 L -.01 -.664 L .416 -1.472 L -.122 -.536 L -.347 -.549 L -1.342 -1.673 L .15 -.425 L -.026 -.367 L -.266 -.465 L -.444 -.407 L -.155 -.295 L -.137 -.522 L -.221 -1.228 L -.141 -.225 L -.338 -.012 L -.517 .088 L -.219 -.352 L .316 -.609 L .542 -.596 L .087 -.34 L -.342 -.662 L .053 -.24 L .718 -.428 L .149 -.212 L -.131 -.677 L .122 -.438 L .583 -.809 L .417 -1.245 L .229 -.143 L 1.245 -.05 L .839 .009 L .04 -.283 L -.031 -1.229 L -.036 -1.002 L .038 -.749 L -.085 -2.5 L .036 -.636 L .016 -.946 L .02 -1.045 L .073 -1.13 L .52 .011 L 1.561 .019 L .088 -.17 L -.033 -3.078 L .021 -1.003 L .819 -.089 L .914 -.005 L 1.614 .019 L 1.241 -.007 L 1.664 -.01 L 2.268 .029 L 2.513 -.015 L 2.774 -.002 L 2.138 .001 L 10.382 .006 L .229 .414 L .499 1.115 L .281 3.501 L .319 1.637 L .182 .211 L .625 .394 L .595 .366 L .617 .507 L .491 .197 L .254 .223 L -.238 .199 L -.277 .327 L -.306 .566 L -.369 .242 L -.511 .172 L -.427 .116 L -.153 .142 L -.235 .496 L -.348 .171 L -.73 .033 L -.204 .552 L -.038 .452 L -.202 .862 L -.242 .722 L -.541 1.302 L -.068 .495 L .092 .903 L -.013 .679 L -.001 .083 L -.148 .947 L -.398 1.231 L -.214 1.061 L -1.04 .331 L -.414 .37 L -.693 1.134 L -.154 .34 L -.191 1.088 L -.141 1.074 L -.197 .185 L -.274 .044 L -.431 -.096 L -.259 .072 L -.163 .114 L -.187 1.103 L -.115 .933 L -.2 1.272 L .039 .522 L -.229 1.159 L -.469 .299 L -.288 -.012 L -.928 -.122 L -.321 .03 L -.188 .806 L -.054 .466 L .094 .141 L .479 .11 L .734 .123 L .334 .167 L .805 .913 L 1.06 1.067 L .751 1.464 L .345 .732 L .348 .421 L .462 .28 L .415 .097 L .269 .309 L .117 .988 L -.034 .17 L -.018 .141 L -.079 -.028 L -.464 .017 L -1.009 .133 L -.832 .005 L -.671 -.052 L -.291 .327 L -.678 .795 L
+518.402 163.079 N -.282 .097 L -.538 .27 L -.656 .566 L -.265 .297 L -.166 .508 L -.062 .41 L -.41 .326 L -1.418 .652 L -2.27 .641 L -1.285 .412 L -.843 .312 L -.356 .297 L -.473 .622 L -.436 .269 L -.506 .114 L -.593 -.069 L -.521 .072 L -.83 .439 L -.65 .396 L -.956 .397 L -.752 .199 L -1.16 .003 L -.562 -.013 L -.297 .128 L -.386 .312 L -.272 .297 L -.45 .312 L -.511 .241 L -.389 .043 L -.239 -.042 L -.535 .411 L -.839 .058 L -.494 -.112 L -.623 -.437 L -.17 -.155 L -.317 -.437 L -.084 -.254 L .18 -.721 L -.081 -.635 L -.312 -.507 L -.341 -1.171 L -.572 -1.919 L -.072 -.438 L .268 -.707 L -.124 -.748 L .692 -.563 L .113 -.565 L -.041 -.678 L .11 -.495 L .174 -.185 L .216 -.1 L .671 .151 L .446 .125 L .715 -.047 L .452 .012 L 1.211 2.45 L 1.302 2.703 L 2.042 -2.667 L 1.907 -2.271 L .383 -.397 L .041 -.127 L .484 -.229 L 4.618 -1.566 L 3.097 -1.035 L 1.148 -.346 L .169 -.085 L .092 .663 L .226 .634 L .604 .971 L .933 1.364 L .127 .253 L -.006 .296 L .483 .689 L .241 .306 L
+494.64 172.627 N -.261 -.166 L -.476 -.633 L -.475 -.159 L -.437 -.911 L -1.267 -.792 L -.277 -.594 L -.673 -.673 L -.594 -.119 L -.871 -.554 L -.555 .079 L -.158 -.158 L -.237 .04 L -.277 -.198 L -.356 .159 L -.476 .079 L -.277 -.436 L -.158 .237 L -.436 .198 L -.792 .079 L -.554 -.594 L -.238 0 L -.396 -.317 L -.832 1.663 L -.436 -.871 L -.475 .079 L -.277 .356 L -.396 -.08 L -.349 .041 L .013 -.679 L -.092 -.903 L .068 -.495 L .541 -1.302 L .242 -.722 L .202 -.862 L .038 -.452 L .204 -.552 L .73 -.033 L .348 -.171 L .235 -.496 L .153 -.142 L .427 -.116 L .511 -.172 L .369 -.242 L .306 -.566 L .277 -.327 L .238 -.199 L .098 .129 L .718 2.102 L .442 1.623 L .689 1.919 L .618 .832 L .205 -.212 L -.072 -.479 L .093 -.226 L .31 .253 L .4 .677 L .384 .395 L .534 -.043 L .242 .014 L 1.338 1.282 L .809 .916 L .124 .099 L .706 .055 L .618 .874 L .021 .24 L -.025 .198 L .118 .212 L .507 .182 L .915 .869 L -.075 .094 L -.529 .78 L -.331 -.182 L -.396 -.125 L -.214 .114 L -.055 .085 L
+370.147 172.035 N -2.301 -.043 L -1.045 .006 L -.505 .384 L -.51 .187 L -.74 -.024 L -.819 .047 L -.463 .139 L -.009 -.003 L -.278 -.226 L -.169 -.409 L .104 -.424 L -.095 -.55 L -.018 -.198 L .001 -.046 L 1.695 .014 L 0 -.434 L .155 0 L .341 -.016 L .186 -.077 L .248 .062 L .294 -.046 L .124 -.093 L .016 -.263 L .077 -.078 L .14 -.016 L .155 .155 L .232 .124 L .356 .108 L .046 .108 L .139 .047 L .217 -.031 L .263 .108 L .186 .17 L .434 0 L .186 -.108 L .263 -.108 L .279 0 L .108 -.124 L .016 -.124 L -.155 -.217 L -.202 -.077 L -.201 .031 L -.294 .093 L -.108 .093 L -.248 .062 L -.279 -.186 L -.031 -.186 L -.186 -.108 L -.17 -.015 L -.186 .124 L -.139 -.108 L -.108 -.217 L -.155 -.108 L -.201 .031 L -.17 -.062 L -.387 .17 L -.108 -.108 L -.155 .046 L -.202 .124 L -.093 .294 L -1.664 0 L -.007 -.067 L -.028 -.269 L -.377 -.268 L -.068 -.155 L -.057 -.367 L -.386 -.635 L -.399 -.494 L -.379 -.31 L -.472 -.183 L .54 -.34 L .723 -.75 L .588 -1.004 L .334 -.82 L .099 -.826 L .523 -.417 L 1.821 -.194 L 1.209 -.12 L .567 .025 L .604 .194 L .695 .561 L .655 .307 L .567 .053 L .312 .154 L .15 .183 L .292 .662 L .537 .788 L .661 .604 L 1.41 .924 L -.506 .399 L -.057 .339 L .251 1.1 L .144 .663 L .22 .479 L .181 .197 L .554 .322 L .265 .337 L .082 .833 L -.004 .565 L -.062 .142 L -.146 -.042 L -.963 .091 L -.658 .074 L -.725 -.081 L -.503 -.209 L -.795 -.32 L -1.255 -.021 L
+495.973 175.881 N -.363 .807 L .083 .409 L .428 .732 L .587 .844 L .732 .801 L .596 .547 L .634 .321 L .54 .209 L 4.26 1.443 L 1.447 .472 L 1.875 -.04 L .236 .154 L -4.087 4.205 L -1.562 1.69 L -.813 .909 L -.8 .004 L -1.693 -.046 L -.626 .088 L -.562 .215 L -.388 .214 L -.502 .497 L -.294 .426 L -.337 .115 L -1.216 .078 L -.305 .101 L -.453 .511 L -1.103 -.106 L -.461 -.181 L -.46 -.336 L -.271 -.098 L -.852 .358 L -.675 .343 L -.258 .199 L -.71 .753 L -.16 .114 L -.847 -.094 L -.67 -.193 L -1.373 -.133 L -.335 -.041 L -2.353 -1.525 L -.604 -.293 L -.749 -.193 L -1.054 -.149 L -.319 -.069 L .018 -.141 L .034 -.17 L -.117 -.988 L -.269 -.309 L -.415 -.097 L -.462 -.28 L -.348 -.421 L -.345 -.732 L -.751 -1.464 L -1.06 -1.067 L -.805 -.913 L -.334 -.167 L -.734 -.123 L -.479 -.11 L -.094 -.141 L .054 -.466 L .188 -.806 L .321 -.03 L .928 .122 L .288 .012 L .469 -.299 L .229 -1.159 L -.039 -.522 L .2 -1.272 L .115 -.933 L .187 -1.103 L .163 -.114 L .259 -.072 L .431 .096 L .274 -.044 L .197 -.185 L .141 -1.074 L .191 -1.088 L .154 -.34 L .693 -1.134 L .414 -.37 L 1.04 -.331 L .214 -1.061 L .398 -1.231 L .148 -.947 L .001 -.083 L .349 -.041 L .396 .08 L .277 -.356 L .475 -.079 L .436 .871 L .832 -1.663 L .396 .317 L .238 0 L .554 .594 L .792 -.079 L .436 -.198 L .158 -.237 L .277 .436 L .476 -.079 L .356 -.159 L .277 .198 L .237 -.04 L .158 .158 L .555 -.079 L .871 .554 L .594 .119 L .673 .673 L .277 .594 L 1.267 .792 L .437 .911 L .475 .159 L .476 .633 L .261 .166 L -.594 .921 L -.488 .61 L -.105 .254 L -.029 .396 L .202 1.157 L .5 -.074 L .892 -.246 L .4 .04 L .556 .195 L
+364.011 169.929 N 1.664 0 L .093 -.294 L .202 -.124 L .155 -.046 L .108 .108 L .387 -.17 L .17 .062 L .201 -.031 L .155 .108 L .108 .217 L .139 .108 L .186 -.124 L .17 .015 L .186 .108 L .031 .186 L .279 .186 L .248 -.062 L .108 -.093 L .294 -.093 L .201 -.031 L .202 .077 L .155 .217 L -.016 .124 L -.108 .124 L -.279 0 L -.263 .108 L -.186 .108 L -.434 0 L -.186 -.17 L -.263 -.108 L -.217 .031 L -.139 -.047 L -.046 -.108 L -.356 -.108 L -.232 -.124 L -.155 -.155 L -.14 .016 L -.077 .078 L -.016 .263 L -.124 .093 L -.294 .046 L -.248 -.062 L -.186 .077 L -.341 .016 L -.155 0 L 0 .434 L -1.695 -.014 L .019 -.477 L .173 -.198 L .434 -.058 L .093 -.155 L -.006 -.059 L
+495.973 175.881 N -.556 -.195 L -.4 -.04 L -.892 .246 L -.5 .074 L -.202 -1.157 L .029 -.396 L .105 -.254 L .488 -.61 L .594 -.921 L .055 -.085 L .214 -.114 L .396 .125 L .331 .182 L .529 -.78 L .075 -.094 L .627 .596 L .285 .465 L .036 .282 L -.118 .113 L -.361 .185 L -.438 .1 L -.56 .312 L -.566 .467 L .253 .127 L .645 -.058 L .321 .013 L .434 .124 L -.114 .26 L -.37 .468 L -.34 .567 L
+363.763 172.732 N .463 -.139 L .819 -.047 L .74 .024 L .51 -.187 L .505 -.384 L 1.045 -.006 L 2.301 .043 L -.111 .792 L .115 .847 L -.186 .312 L -.333 .186 L -.513 .031 L -.401 .017 L -.381 .186 L -.789 .64 L -.321 .372 L -.047 -.126 L -.192 0 L -.501 -.14 L -.165 -.254 L .121 -.41 L .33 -.438 L .253 -.212 L -.131 -.141 L -.372 -.112 L -.498 .015 L -.415 .17 L -.161 0 L -.616 -.281 L -.36 -.296 L -.069 -.24 L -.323 -.084 L -.316 -.137 L
+383.005 177.596 N -.379 .397 L -.264 .623 L .214 .409 L .695 .405 L .197 .31 L -.125 .283 L -.332 .37 L .309 .351 L .648 .561 L .051 .226 L -.19 .143 L -.304 -.012 L -.548 -.223 L -.304 .03 L -.158 .199 L -.03 .127 L .296 .549 L .179 .21 L -.188 .354 L -.602 .554 L -.047 .099 L -.339 -.224 L -.337 -.097 L -.143 .086 L -.756 .922 L -.321 -.026 L -.404 -.322 L -.277 -.323 L .044 -.283 L -.009 -.65 L -.22 -.846 L -.165 -.366 L -.338 -.097 L -.528 .046 L -.367 .143 L -.252 .242 L -.193 .376 L -.26 -.656 L -.165 -1.369 L -.252 -.761 L -.475 -.619 L -.456 -.421 L -.42 -.224 L -.466 -.04 L -1.137 .148 L -.594 -.039 L -.238 .157 L -.643 .866 L -1.088 1.235 L .051 -.392 L -.312 -.55 L -.764 -.775 L -.122 -.649 L -.84 -.366 L -.908 -.563 L -.277 -.296 L .09 -.396 L -.053 -.311 L -.547 -.041 L -.323 -.098 L -.115 -.155 L .059 -.311 L -.038 -.1 L .321 -.372 L .789 -.64 L .381 -.186 L .401 -.017 L .513 -.031 L .333 -.186 L .186 -.312 L -.115 -.847 L .111 -.792 L 1.255 .021 L .795 .32 L .503 .209 L .725 .081 L .658 -.074 L .963 -.091 L .146 .042 L .113 .028 L .18 .183 L .882 .715 L .536 .265 L .252 -.256 L .475 -.413 L .338 .026 L .356 .153 L .565 .152 L .43 -.243 L .3 -.327 L .684 -.428 L .323 .055 L .246 .281 L .022 .339 L .271 .832 L .265 .451 L .474 .478 L .349 .718 L .194 1.143 L .559 .901 L
+266.015 188.956 N .103 .169 L -.163 .326 L -.592 .385 L -1.74 .9 L -.807 .23 L -.557 .074 L -.465 -.054 L -.284 .115 L -.232 1.117 L -.348 .115 L -.628 -.618 L -.344 -.224 L -1.149 .035 L -.385 -.04 L -.896 -.461 L -.309 -.125 L -.159 .029 L -.041 .184 L .616 .688 L .391 .69 L .302 1.524 L .079 .55 L .166 .239 L .96 .051 L .434 .125 L .15 .253 L -.265 .27 L -.569 .272 L -.652 .131 L -.203 .199 L -.259 .666 L -.235 .213 L -.652 .173 L -.554 .286 L -.74 .654 L -.645 .739 L -.271 .016 L -.186 -.776 L -.083 -.183 L -.757 .697 L -.414 .073 L -.482 -.223 L -.694 -.546 L -.432 -.054 L -.199 -.437 L -.088 -.452 L -.161 -.861 L -.138 -.437 L -.148 -.168 L -.797 -1.182 L -.51 -.491 L .479 -.526 L .731 -.612 L -.121 -.282 L -.486 -.647 L -.256 -.437 L -.447 -.789 L -.162 -.804 L -.048 -.367 L -.035 -.438 L -.026 -.254 L .147 -.326 L .379 -.511 L .085 -1.004 L .409 -.525 L -.644 -.081 L -1.99 .224 L -.76 .174 L -.522 .13 L -.144 0 L -.554 -.576 L -.847 -.998 L -.188 -.253 L -.64 -.321 L -.521 -.181 L -1.167 .05 L -1.163 .12 L -.496 .017 L -.397 -.252 L -.429 -.548 L -.401 -.309 L -.099 -.353 L .226 -1.132 L -.103 -.395 L -.855 -1.45 L -.31 -.606 L -.384 .017 L -.234 .1 L -.402 -.025 L .709 -1.191 L .241 -.722 L .172 -.722 L .99 -1.758 L .381 -.059 L .227 .027 L .129 -.396 L -.048 -.497 L .056 -.288 L .414 -.2 L .534 -.156 L .84 -.171 L .128 .105 L -.9 .151 L -.731 .312 L -.145 .212 L .19 .607 L .142 .407 L .224 .126 L -.043 .145 L .153 .579 L -.135 .367 L -.327 .364 L -.348 .824 L -.137 .368 L .253 .479 L .288 .253 L .25 .72 L .341 .353 L .523 -.114 L .184 -.156 L .419 -.255 L .12 -.142 L .066 -.523 L -.167 -.649 L -.21 -.282 L -.438 -.804 L -.136 -.135 L -.118 -.395 L -.247 -.18 L .239 -.099 L .095 -.251 L -.204 -.144 L 1 -.379 L 1.085 -.327 L .998 -.272 L .086 -.225 L .69 -.086 L .143 -.008 L -.042 -.157 L -.055 -.198 L -.125 -.036 L -.039 -.108 L -.128 -.072 L -.226 .071 L -.156 .027 L -.229 -.012 L -.315 -.55 L .109 -.254 L .337 -.213 L .367 -.043 L .09 .112 L .14 .368 L .186 .162 L -.001 .148 L .026 .193 L .068 .09 L .004 .198 L .253 .258 L .329 -.02 L .699 .111 L .455 .07 L .593 .196 L .323 .254 L .393 .564 L .156 .635 L .358 .324 L .359 .084 L 1.02 -.129 L .928 -.059 L .59 -.058 L .799 -.059 L .714 .125 L .4 .479 L .267 .169 L .578 .253 L .49 .14 L 1.094 .04 L .382 -.057 L .388 -.227 L 1.042 -.807 L .47 -.185 L .453 .042 L .959 -.073 L 1.152 -.073 L .919 .055 L .248 .112 L -.056 .141 L -.294 .185 L -.854 -.041 L -.433 .015 L -.083 .212 L .059 .184 L .593 .253 L .609 .535 L .195 .649 L .246 -.523 L .185 -.142 L .415 .253 L .483 .027 L .374 .098 L .258 .338 L .918 .394 L .464 .295 L -.729 .496 L -.161 .65 L -.214 .226 L -1.055 .417 L .5 .064 L .598 .098 L .368 -.029 L .33 -.142 L .929 -.03 L .725 .083 L .84 .274 L .095 .296 L -.061 .41 L -1.655 1.239 L -.101 .255 L .074 .212 L .62 .604 L .141 .282 L -.308 .299 L -.41 .144 L -1.032 .19 L -.061 .452 L .008 .58 L -.395 .539 L -.071 .212 L .324 .521 L .732 .745 L .503 .647 L
+493.044 204.258 N -1.223 -1.771 L -.027 -.932 L -.03 -1.43 L -.042 -2.045 L -.003 -1.017 L 0 -.438 L .016 -.848 L .004 -1.215 L .002 -.508 L 1.649 -2.467 L .453 -.511 L .305 -.101 L 1.216 -.078 L .337 -.115 L .294 -.426 L .502 -.497 L .388 -.214 L .562 -.215 L .626 -.088 L 1.693 .046 L .8 -.004 L .813 -.909 L 1.562 -1.69 L 4.087 -4.205 L -.236 -.154 L -1.875 .04 L -1.447 -.472 L -4.26 -1.443 L -.54 -.209 L -.634 -.321 L -.596 -.547 L -.732 -.801 L -.587 -.844 L -.428 -.732 L -.083 -.409 L .363 -.807 L .34 -.567 L .37 -.468 L .114 -.26 L .154 .044 L .935 1.142 L .586 .62 L .243 .381 L .265 .211 L .372 -.071 L .417 -.001 L .465 .027 L .372 -.071 L .572 -.27 L .836 -.425 L .585 -.157 L .397 .098 L .76 .267 L .549 -.072 L .56 -.326 L .779 -.566 L .247 -.127 L .447 .041 L .479 .098 L .419 -.043 L 1.195 -.482 L .288 .027 L .682 .196 L .74 -.03 L .764 -.185 L .964 -.327 L .9 -.666 L .47 -.382 L .604 .154 L .391 .211 L .08 .014 L .147 .268 L -.414 .919 L .021 .564 L .132 .621 L -.165 .452 L -.375 .509 L -.028 .678 L -.047 .833 L -.163 .509 L -1.264 2.262 L -.842 .792 L -.122 .311 L .102 .353 L -.893 1.569 L -.834 1.272 L -.214 .947 L -.351 .636 L -.712 1.117 L -.874 1.188 L -1.159 1.498 L -.384 .439 L -2.274 2.504 L -1.82 1.557 L -2.164 1.121 L -.593 .382 L -1.28 1.09 L -1.74 1.755 L -.06 .061 L -1.055 1.1 L -1.235 1.569 L -.615 .835 L .1 .353 L -.094 .276 L
+264.768 176.039 N -.128 .225 L -.115 .067 L -.029 .135 L .039 .25 L -.058 .086 L .125 .376 L -.039 .424 L -.453 .154 L -.135 -.01 L -.144 .039 L -.482 -.039 L -.192 .039 L -.087 -.048 L -.356 0 L -.02 -.058 L .039 -.067 L .202 -.029 L .222 -.135 L .019 -.087 L .106 .02 L .154 .01 L .135 -.145 L -.096 -.279 L .048 -.125 L .029 -.183 L -.067 -.125 L -.097 -.135 L -.279 -.048 L .116 -.096 L .164 -.019 L .231 -.029 L .115 -.087 L .385 .02 L .106 -.039 L .299 -.067 L .244 .006 L
+654.075 190.187 N .206 -.125 L .63 -.114 L .656 -.938 L .241 -.07 L -.069 .268 L .122 .087 L .187 -.046 L .11 .174 L .148 .444 L -.024 .111 L -.013 .247 L .197 .197 L .025 .086 L -1.234 .42 L -.383 .271 L -.309 -.271 L -.111 -.259 L -.234 .012 L -.062 -.37 L -.084 -.126 L
+493.044 204.258 N -.602 .389 L -.557 .171 L -.385 -.112 L -.086 .777 L -.282 .367 L -.67 .115 L -.394 .382 L -.088 .48 L .006 .353 L -.356 .622 L -.964 1.358 L .092 .536 L -.337 .65 L -.25 .255 L -.334 .1 L -.084 .152 L -1.067 -.807 L -2.427 -1.752 L -.07 -.239 L .116 -.552 L -.137 -.381 L -3.553 -1.984 L -4.663 -2.568 L -1.448 .033 L 1.144 -2.479 L .105 -.229 L .656 -.835 L .985 -.938 L .477 -.525 L .43 -.695 L .143 -.566 L -.048 -.664 L -.223 -.606 L -.224 -.352 L -.732 -1.069 L -.174 -.465 L -.027 -.861 L -.126 -.226 L -.477 -.463 L -.3 -.493 L .678 -.795 L .291 -.327 L .671 .052 L .832 -.005 L 1.009 -.133 L .464 -.017 L .079 .028 L .319 .069 L 1.054 .149 L .749 .193 L .604 .293 L 2.353 1.525 L .335 .041 L 1.373 .133 L .67 .193 L .847 .094 L .16 -.114 L .71 -.753 L .258 -.199 L .675 -.343 L .852 -.358 L .271 .098 L .46 .336 L .461 .181 L 1.103 .106 L -1.649 2.467 L -.002 .508 L -.004 1.215 L -.016 .848 L 0 .438 L .003 1.017 L .042 2.045 L .03 1.43 L .027 .932 L 1.223 1.771 L
+466.196 203.275 N .188 -.298 L .076 -.27 L -.057 -.748 L .025 -.734 L -.021 -.593 L .107 -.507 L .217 -1.02 L .395 -.681 L .255 -.284 L 1.241 -.996 L 1.195 -1.066 L .191 -.453 L -.111 -.31 L -.271 -.182 L -.479 -.04 L -.191 -.027 L -.128 -.253 L .26 -1.88 L .018 -.424 L -.159 -.183 L -.063 -.028 L .496 -.229 L .464 -.087 L .495 .011 L .942 .234 L .911 -.048 L .881 -.33 L .88 -.132 L .303 .097 L .303 .196 L .367 -.016 L .626 -.314 L .563 -.54 L .3 .493 L .477 .463 L .126 .226 L .027 .861 L .174 .465 L .732 1.069 L .224 .352 L .223 .606 L .048 .664 L -.143 .566 L -.43 .695 L -.477 .525 L -.985 .938 L -.656 .835 L -.105 .229 L -1.144 2.479 L -4.659 0 L -1.277 .05 L -.319 .017 L -.554 .398 L -.458 .427 L -.431 .045 L -.546 -.223 L -.064 -.042 L
+713.621 206.298 N .169 7.966 L -.44 .822 L .431 1.368 L .046 .805 L -.031 3.438 L -.515 -.512 L -.927 -.888 L -.716 -.902 L -.406 -.056 L -.776 .101 L -.739 .143 L -.434 -.013 L .091 -.382 L .435 -.65 L .006 -.283 L -.561 -.521 L -.565 -.775 L .028 -.226 L .442 .111 L .236 -.042 L .135 -.113 L -.467 -.409 L -.595 -.408 L -.287 -.381 L -.275 -.648 L -1.053 -1.693 L -.508 -.394 L -.467 -.282 L -.604 -.196 L -1.983 -.603 L -1.26 -.379 L -.613 -.069 L -.705 -.238 L -.63 -.323 L .072 -.34 L -.098 -.268 L -.193 -.028 L -.617 .101 L -.389 -.07 L -.412 -.196 L -.408 -.395 L -.209 -.579 L .133 -.494 L -.155 -.226 L -.187 .113 L -.234 .396 L -.122 .664 L -.251 .608 L -.334 .269 L -.696 .354 L -.155 -.169 L -.331 -.677 L .022 -.155 L .384 -.27 L -.152 -.424 L -.173 -.239 L -.564 -.395 L -.707 -.394 L -.338 -.056 L -.059 -.212 L .038 -.226 L .413 -.044 L .388 .084 L .603 .239 L .158 -.029 L .368 -.34 L .525 -.41 L .146 .056 L .3 .269 L 1.021 -.045 L .139 -.128 L .09 -.522 L -.063 -.409 L -.238 .028 L -.345 .199 L -.604 .071 L -.656 -.041 L -.766 .044 L -1.026 -.082 L -.411 -.31 L -.135 -.197 L -.148 -.664 L -.202 -.338 L -.42 -.155 L -1.249 -.124 L .265 -.297 L .058 -.255 L .004 -.593 L .463 -.029 L .92 -.411 L .49 -.383 L .444 -.283 L .352 .027 L .4 .069 L 1.494 .646 L .515 .169 L .913 .153 L .382 .705 L .138 .396 L -.283 .749 L -.067 .381 L .221 .381 L .115 .494 L .115 .48 L .215 .521 L .186 .197 L .197 .127 L .226 -.65 L .085 .113 L .087 .141 L .309 1.073 L .169 .169 L .234 .183 L .294 .112 L .354 .056 L .58 -.198 L .504 -.439 L 1.192 -1.853 L .352 -.015 L 1.078 -.215 L .378 -.142 L .045 -.085 L .014 -.509 L .219 -.17 L 1.1 -.609 L .335 -.043 L 1.732 .759 L 2.129 .941 L 1.54 .52 L 1.299 .404 L h 691.208 208.707 m -.388 -.069 L -.693 -.38 L -.852 -.647 L -.295 -.141 L -.414 .028 L -.059 .1 L .024 .452 L -.206 .028 L -1.014 -.407 L -.258 -.353 L -.582 .199 L -.289 .269 L -.326 .185 L -.186 -.184 L -.312 -.451 L -.245 -.451 L .246 -.198 L .303 -.029 L .274 .056 L 1.104 .04 L .574 .31 L .319 -.015 L .544 -.326 L .414 -.015 L .534 .126 L .857 .21 L .499 .395 L .293 .395 L .179 .621 L -.049 .254 L h 682.045 208.699 m -.419 -.056 L -.715 -.493 L -.232 -.451 L .146 -.283 L .603 -.1 L .766 -.044 L .246 .126 L .256 .311 L .313 .197 L .108 .226 L -.067 .226 L -.125 .057 L -.879 .285 L h 707.635 219.095 m -1.11 -.209 L .589 -1.032 L .56 -.708 L .407 -.269 L .427 -.072 L .527 .338 L .198 .24 L -.11 .184 L -.324 .637 L -.256 .17 L -.638 .693 L -.27 .028 L h 673.797 218.703 m -.562 .257 L .034 .233 L -.886 .326 L -.582 .274 L -.339 -.041 L -.453 .325 L -.504 -.069 L -.427 -.112 L -.378 .255 L -.3 .058 L -.358 -.07 L -.58 -.196 L -1.046 -.04 L -.316 .043 L -.211 -.564 L .027 -.24 L .383 -.198 L .672 -.199 L .528 -.016 L 1.142 .407 L .445 .324 L .338 .013 L .326 -.297 L .464 -.016 L .429 -.071 L .414 .187 L .467 -.116 L -.072 -.222 L .421 -.187 L .404 -.233 L .094 -.151 L -.076 -.117 L -.184 .023 L .116 -.198 L .16 .012 L .22 .094 L .177 .221 L .013 .304 L h 662.661 219.065 m -.312 -.099 L -.203 -.127 L -.062 -.169 L .03 -.212 L .256 -.198 L .315 -.036 L .17 .092 L .053 .212 L .182 .098 L .305 -.145 L .34 .105 L .104 .151 L -.012 .451 L .183 -.148 L .163 -.304 L .318 -.029 L .229 .226 L .021 .424 L .181 -.036 L .062 .104 L -.025 .397 L -.316 -.211 L -.311 -.058 L -.141 .058 L .072 .155 L -.852 .157 L -.143 -.091 L .097 -.268 L -.085 -.059 L -.308 .269 L -.229 .256 L -.296 -.046 L -.63 .225 L -.624 .199 L -.357 -.051 L -.31 .123 L -.392 -.07 L -.103 -.07 L -.202 -.123 L -.063 -.279 L .143 -.261 L -.08 -.253 L .193 -.115 L .23 -.113 L .233 -.156 L .224 .07 L .61 .013 L .4 .104 L .089 .28 L .291 .109 L .294 .056 L .189 -.259 L .29 -.012 L .051 -.187 L -.263 -.15 L h 656.294 219.602 m -.393 -.282 L -.855 -.449 L -.118 -.269 L .417 -.001 L .514 -.185 L .462 -.029 L .925 .521 L -.338 .17 L -.232 .1 L -.381 .425 L h 631.053 200.125 m -.061 .225 L -.413 .439 L -.204 .41 L -.381 .354 L .164 .353 L .162 .169 L .806 .493 L .832 .055 L .241 .112 L .151 .381 L .128 .763 L -.007 .409 L .267 .423 L .212 .127 L .544 .041 L -.45 .933 L .151 .212 L .703 -.453 L .824 .252 L .177 .042 L .265 .254 L .144 .438 L .698 .676 L -.515 1.979 L -.04 .452 L .23 .946 L -.021 .438 L .021 .664 L -.002 .268 L -.149 1.06 L -.087 .156 L -.107 .07 L -.367 -.253 L -.381 -.522 L -.261 -.084 L -.262 .481 L -.081 .268 L -1.043 -.619 L -.219 .086 L .394 .747 L -.163 .213 L -.204 -.197 L -1.343 -1.424 L -.775 -.761 L -1.011 -.859 L -1.348 -.958 L -.391 -.451 L -.199 -.493 L -.191 -.339 L -1.003 -.633 L -.697 -.677 L -1.186 -1.509 L -.074 -.353 L .039 -.339 L -.324 -.875 L -.841 -1.467 L -.667 -1.044 L -.612 -.775 L -.369 -.301 L -.287 -.234 L -.64 -.295 L -.254 -.748 L -.688 -1.806 L .067 -.24 L -.107 -.311 L -.157 -.197 L -.662 -.507 L -.711 -.394 L -.539 -.21 L -.317 -.099 L -.119 -.353 L -.077 -.734 L -.18 -.409 L -.386 -.479 L -.818 -.831 L -.368 -.423 L -.725 .128 L -.613 -.676 L -.646 -.606 L -.593 -.69 L -.562 -.945 L -.229 -.635 L -.032 -.367 L .057 -.198 L .149 -.113 L .401 -.043 L .364 .098 L .25 .126 L .632 .563 L .361 .155 L .922 .153 L .335 .027 L .548 -.1 L .454 -.142 L .4 -.015 L .323 .31 L .919 1.156 L .513 .31 L .058 .155 L -.12 .537 L 1.066 .916 L .749 .493 L 1.175 .689 L .678 .323 L .139 .169 L .03 .593 L -.02 .155 L .573 .055 L .745 .944 L .612 .55 L .271 -.015 L .004 -.198 L -.123 -.226 L .069 -.24 L .507 .21 L .479 .804 L .441 .38 L .446 .056 L .429 .197 L .314 .366 L .28 .734 L .316 .437 L .431 .268 L .511 .126 L .767 .083 L .431 .154 L .494 .38 L .576 .606 L -.019 .071 L h 684.201 200.125 m -.007 -.172 L -.414 -1.058 L .18 -.551 L -.078 -.141 L -.141 -.296 L .036 -.325 L .286 -.89 L .514 -.82 L .263 .367 L .152 .353 L -.054 .283 L -.246 .396 L -.361 .763 L .061 .325 L .19 .141 L .097 -.141 L .436 -.411 L .135 -.522 L .179 -.142 L .806 -.412 L .141 .141 L -.052 .254 L .104 .55 L -.354 .212 L -.467 .354 L -.162 .311 L .159 .099 L .446 .126 L .398 .211 L -.016 .141 L .159 .353 L -.688 -.154 L -.431 -.154 L -.367 -.042 L -.304 .156 L -.08 .438 L .049 .258 L .131 .688 L .341 .62 L .405 .438 L .196 .282 L -.156 .212 L -.26 -.211 L -.664 -.648 L -.55 -.733 L -.002 -.396 L -.011 -.251 L h 637.361 207.144 m -.863 -.394 L -.377 -.239 L -.205 -.367 L -.045 -.367 L -.156 -.395 L -.507 -.395 L -.291 -.099 L -.446 .029 L -.116 -.141 L .271 -.65 L .234 -.24 L .509 .55 L .148 -.467 L .313 -.269 L .072 .395 L .312 .89 L .648 .817 L .698 .31 L -.265 .184 L -.118 .283 L .183 .564 L h 634.321 215.345 m -.091 -.187 L .316 -.023 L .402 .093 L .369 -.129 L .068 -.524 L .018 -.14 L .309 .057 L -.043 -.5 L .222 -.235 L .093 -.277 L .202 .121 L .631 .112 L .474 -.047 L .237 .443 L .524 -.089 L .158 -.297 L .022 -.244 L .259 .116 L .618 .168 L .411 .438 L .338 -.046 L .204 .271 L .446 -.029 L .453 -.185 L .302 .211 L .369 .522 L .179 .521 L .884 .041 L .462 .188 L .49 -.077 L 1.435 .124 L .479 -.029 L .34 -.17 L .213 -.65 L .271 -.269 L .447 -.015 L .223 .211 L .289 .494 L .633 .125 L .53 .027 L .774 .083 L .796 .153 L .289 .24 L .293 .288 L -.08 .445 L .275 .466 L .119 .099 L .877 .352 L .422 .069 L .658 .013 L .45 -.185 L .415 -.015 L .628 .238 L .048 .197 L -.255 .425 L -.152 .494 L .578 .776 L -.499 -.211 L -.802 -.196 L -.599 -.253 L -.891 -.309 L -.528 .001 L -.589 .256 L -.348 .057 L -.714 -.098 L -1.454 -.138 L -1.47 -.138 L -.805 -.253 L -.839 -.479 L -1.099 -.336 L -1.125 -.267 L -.948 -.04 L -.556 .298 L -.445 .043 L -.957 -.153 L -.805 -.492 L -.357 -.07 L -1.606 -.066 L -.363 -.155 L .055 -.141 L .448 -.468 L -.402 -.267 L -.551 -.099 L -.506 -.14 L -.307 -.027 L -1.261 -.121 L h 675.004 223.092 m .249 -.494 L .023 -.537 L .113 -.312 L .674 -.481 L 1.447 -.624 L .662 -.454 L .36 -.607 L .466 -.157 L 1.578 -.102 L .91 -.214 L .541 -.044 L .869 -.143 L .118 .07 L .099 .197 L -.237 .212 L -.36 .256 L -1.609 .61 L -1.369 .44 L -.713 .256 L -.606 .354 L -1.09 .963 L -.653 .481 L -.439 .086 L -.552 .228 L -.48 .015 L h 667.866 223.149 m -.217 -.069 L -.917 -.605 L -.8 -.45 L -.347 -.099 L -.493 -.126 L -.292 -.197 L .108 -.212 L .371 -.142 L .992 -.03 L .502 -.114 L .35 .296 L 1.147 .746 L .265 .381 L -.125 .325 L -.246 .24 L -.299 .057 L h 661.819 191.241 m -.041 .09 L .319 .691 L -.23 .142 L -.546 .043 L -.579 .086 L .198 .226 L .115 .296 L -.169 .226 L .216 .211 L .235 .112 L .546 .832 L .536 .747 L .043 .198 L -.338 .721 L .075 .226 L .406 .465 L .743 .45 L .6 .493 L .551 .761 L -.465 .17 L -.75 -.026 L -.797 -.238 L -.337 .1 L -.387 .467 L -.354 .918 L -.08 .476 L -.046 .272 L .132 .649 L .116 .424 L -.133 .848 L -.256 0 L -.466 -.154 L -1.037 .963 L -.433 .65 L -.751 .608 L .443 .381 L .06 .396 L .17 .296 L -.685 .058 L .452 .578 L .009 .212 L -.103 .227 L -.547 .665 L -.206 .396 L -.127 .354 L -.529 .594 L -1.294 .61 L -.607 .284 L -.292 .198 L -.194 -.311 L .024 -.424 L -.33 -.804 L -.306 -.381 L -.265 -.184 L -.286 .029 L -.503 .523 L -.302 .029 L -.328 -.508 L -.313 -.197 L -.437 -.112 L -.387 -.451 L -.342 -.154 L -.35 .806 L -.135 .198 L -.381 .058 L -.356 -.112 L -.442 .128 L -.318 .354 L -.364 .071 L -.059 -.551 L .034 -.311 L -.314 -1.03 L -.336 .396 L -1.42 .44 L -.321 -.408 L -.639 .015 L -.281 .156 L -.303 .029 L -.058 -.649 L -.022 -.65 L -.267 -1.411 L -.012 -.48 L -.352 -.747 L -.406 -.409 L -.79 -.422 L -.146 -.141 L .555 -.354 L -.531 -.38 L -.258 -.296 L .188 -.735 L -.074 -.128 L -.278 -.478 L -.352 -.296 L .065 -.466 L -.125 -.593 L .182 -.65 L .133 -.353 L .424 -.58 L .303 -.806 L .318 .028 L .204 .11 L .288 .792 L .253 .295 L 1 .983 L .304 .083 L .446 .28 L .928 -.416 L .255 -.001 L .526 .223 L .543 .11 L .399 -.172 L .528 -.342 L .403 -.525 L .531 -.441 L .479 -.074 L .431 .11 L .557 .251 L .524 .223 L .559 .152 L .287 -.03 L .467 -.356 L .465 -.172 L .864 -.175 L .387 -.299 L .928 -1.785 L -.076 -.748 L .218 -.34 L .646 -.244 L .22 -.383 L -.106 -.988 L .119 -.565 L .381 -.638 L .247 -.157 L .464 -.017 L .748 .081 L .651 .081 L .624 -.018 L .446 .04 L .753 .292 L .182 .09 L h 666.561 200.125 m .012 -.049 L .48 -1.188 L .434 -.41 L .289 -.142 L .429 .338 L .29 -.311 L .162 -.325 L .293 -.481 L .496 -.058 L .605 .14 L .729 .535 L .447 .027 L .863 -.044 L .478 .168 L .749 .267 L .577 -.227 L 1.853 .081 L .72 -.128 L .627 -.354 L .211 -.283 L -.156 -.268 L .196 -.283 L .388 -.241 L .295 -.41 L .289 -.057 L .075 .24 L -.073 .537 L -.117 .311 L -.081 .127 L -.082 .127 L -.969 1.259 L -.416 .396 L -.464 .1 L -1.23 .229 L -.495 -.069 L -.591 -.422 L -1.149 -.068 L -1.151 .059 L -.878 -.041 L -1.039 .045 L -.575 -.083 L -.671 .029 L -.415 .1 L -.433 .368 L -.259 .461 L -.154 .274 L -.187 .721 L .068 .48 L .263 .494 L .194 .183 L .403 .226 L .259 .196 L .221 .607 L .179 .154 L .226 .042 L .815 .026 L .249 -.269 L .652 -.976 L .385 .056 L .307 .183 L .496 .041 L .363 -.227 L .669 -.156 L .62 -.143 L .268 -.298 L .271 -.057 L .466 .196 L .131 .212 L -.083 .734 L -.469 -.267 L -.544 -.042 L -.361 .298 L -.389 .523 L -.438 .425 L -1.059 .439 L -.214 .325 L -.143 .029 L -.241 -.042 L -.468 -.126 L -.03 .056 L .025 .312 L .212 .126 L .676 .578 L .467 .521 L .854 1.24 L -.097 .325 L -.156 .679 L .102 .409 L .447 .535 L .555 .438 L .062 .226 L -.045 .282 L -.436 -.056 L -.652 .059 L -.412 .297 L -.224 .692 L -.498 -.026 L -.461 -.183 L -.107 -.17 L .052 -.649 L .204 -.58 L -.978 -.845 L -.417 -.31 L -.174 -.269 L .036 -.24 L .284 -.396 L .116 -.579 L -.165 -.494 L -.737 -.055 L -.503 .213 L -.494 .396 L .16 .353 L .143 .932 L -.068 .509 L -.236 1.145 L .363 .903 L -.01 .311 L -.377 .636 L -.019 .227 L .275 .564 L -.726 .171 L -.513 .241 L -.476 .071 L -.245 -.324 L -.16 -.522 L .156 -.325 L .181 -.466 L .069 -.876 L .06 -1.073 L -.125 -.509 L .029 -.339 L -.213 -.395 L -.311 -.127 L -.391 .171 L -.574 .029 L .011 -.41 L -.25 -1.284 L .131 -.311 L .491 -.524 L .469 -.777 L .161 -.48 L -.089 -.918 L -.006 -.254 L .087 -.452 L .339 -.721 L .447 -.058 L -.043 -.861 L .254 -1.053 L
+341.05 41.069 N 2.084 .272 L .344 .361 L -.869 .174 L -.541 .139 L -1.678 .106 L -1.159 .037 L -.689 .156 L -.372 .224 L -.308 .6 L -.361 .376 L 1.05 .39 L .971 .168 L 2.117 .064 L .601 -.001 L 1.775 -.242 L 1.93 -.038 L .866 .135 L .933 .219 L .417 .135 L .284 -.018 L 1.001 -.002 L 1.277 .032 L .615 .05 L -1.277 .626 L -1.583 .457 L -1.976 .523 L -.556 -.016 L -.695 -.116 L -.951 .671 L -1.061 .503 L -1.246 .452 L -1.125 .296 L -.211 .056 L -2.212 .054 L -.525 .134 L -.502 .001 L -.982 .201 L -.665 .167 L -.528 .051 L -.946 -.413 L -.375 .05 L -.69 .913 L -.958 .118 L -.631 -.065 L -.743 -.197 L -.622 -.463 L -.854 -.43 L -.647 -.215 L -.109 0 L .008 .2 L .707 1.043 L -.192 .249 L -.319 .017 L -.69 .249 L -.84 .249 L -.573 .38 L -1 .906 L -.657 .657 L -1.051 .851 L -.776 .262 L -1.034 .083 L -1.023 -.275 L -.148 .554 L -.438 .569 L -.783 .277 L -.992 -.095 L -.616 .05 L -1.18 .439 L .942 -1.723 L -.121 .017 L -.795 -.015 L -1.055 -.177 L .26 .423 L -.026 .455 L -.386 .407 L -.794 .39 L -1.16 .164 L -.973 .002 L -1.255 .083 L .467 .403 L .212 .403 L -.09 .387 L -.379 .097 L -.321 -.063 L -.47 .033 L -1.792 -.157 L .517 .32 L .765 .462 L .295 .351 L -.01 .224 L -.26 .176 L -1.197 .034 L -1.051 .129 L .844 .413 L .47 .126 L .448 .222 L .389 .333 L -.554 .461 L -.285 .111 L -.599 -.094 L -.478 .096 L .345 .474 L -.009 .127 L 308.501 60 l -.486 -.125 L -.583 -.062 L .026 .158 L .255 .457 L -.101 .347 L -.288 0 L -.656 -.093 L -.089 -.016 L -.979 .112 L -1.081 .018 L .682 .487 L 1.108 .391 L .331 .204 L -.077 1.035 L -.382 .938 L -.427 .094 L -.815 -.061 L .489 .591 L -.016 .498 L .156 .233 L -.068 .373 L -.316 .062 L -.495 -.03 L -.771 .079 L .762 .386 L .427 .603 L -.117 .447 L -.287 .031 L -.967 -.26 L -1.052 -.508 L -.498 .294 L -.425 .602 L -.635 -.599 L .158 -.573 L -.387 -.201 L -1.124 -.184 L -.577 -.309 L .04 -.187 L .253 -.249 L .066 -.218 L -.325 -.28 L -.366 -.186 L -.668 .359 L -.276 .016 L -.3 .141 L -.444 -.046 L -.98 .064 L -.417 .017 L -.571 .296 L -.476 .28 L -.426 -.403 L -.104 -.357 L -.222 -.217 L -.513 -.233 L -.817 -.232 L -.772 -.389 L -.517 -.781 L .07 -.737 L -.199 -.156 L -.434 -.094 L -.467 .048 L -.97 -.266 L -.108 -.094 L -.138 -.236 L .14 -.457 L .459 -.395 L .071 -.269 L -.258 -.062 L -.551 -.031 L -.542 -.094 L -.278 -.221 L -.058 -.633 L -.458 -.126 L -.616 .049 L -.589 -.57 L .023 -.191 L .198 -.254 L .618 -.367 L 1.22 -.337 L .405 -.304 L .476 -.128 L .051 -.383 L -.277 -.287 L -.473 .097 L -.921 .082 L -.493 .097 L -.635 .416 L -.538 .129 L -.63 .304 L -.339 -.318 L .038 -.623 L -.114 -.784 L -.206 -.451 L .015 -.355 L -.243 -.323 L -.504 .082 L -.271 -.032 L -.666 -.355 L -.594 -.485 L -.013 -.357 L .842 -.538 L .265 .019 L -.556 -.189 L -1.083 .064 L -.103 -.284 L .383 -.176 L .677 -.03 L .586 -.052 L .456 -.087 L .322 -.672 L -1.208 -.047 L -.572 .05 L -.362 -.032 L -.29 -.163 L -.116 -.197 L .223 -.28 L .218 -.008 L .18 -.16 L .427 -.055 L -.375 -.188 L -.552 .073 L -.22 -.248 L .057 -.188 L .073 -.132 L .259 0 L .309 -.099 L .827 -.25 L 1.218 .081 L .854 .163 L .776 .032 L .378 .131 L .927 .146 L 1.027 .097 L -.031 -.363 L .299 -.545 L -.298 -.182 L -1.02 -.263 L -1.356 -.312 L -.903 -.164 L -1.592 -.33 L -.354 -.116 L .336 -.35 L .788 -.001 L 1.462 .363 L 1.034 .048 L .463 -.067 L .114 -.067 L .088 -.6 L .088 -.301 L .595 -.034 L .528 .116 L .227 -.101 L -.027 -.351 L -.195 -.184 L -.891 -.317 L .162 -.437 L .528 -.455 L -.258 -.286 L -1.21 -.167 L -1.154 .002 L -1.178 -.286 L -1.649 -.49 L -.78 -.101 L -.903 -.05 L -.76 -.34 L -.811 -.477 L .156 -.035 L .323 -.155 L .605 -.001 L .572 -.019 L 2.085 .305 L .716 .033 L 1.249 .306 L 1.451 .458 L .729 .169 L .056 -.307 L -.311 -.426 L -.86 -.546 L -.172 -.446 L -.603 -.446 L -.485 -.051 L -.677 -.24 L .361 -.277 L .542 -.139 L 285.644 41 l -.704 -.241 L -1.101 -.015 L -.625 -.086 L -1.132 -.327 L -.88 .608 L -.324 .156 L -.274 .294 L -.875 .243 L -1.402 -.066 L -1.031 -.343 L -.306 -.242 L -.027 -.294 L .438 -.313 L .293 -.645 L -.152 -1.259 L .582 -.054 L .691 .192 L .372 -.124 L .151 -.334 L -.383 -.369 L -.933 -.545 L -.452 -.141 L -.946 -.796 L -.895 -.925 L -1.105 -1.289 L -.578 -.485 L -1.855 -.379 L -.667 -.255 L -.291 -.202 L -.052 -.701 L -.904 -.406 L -.962 -.109 L -1.589 -.165 L -1.928 -.425 L -1.903 -.333 L -2.133 -.183 L -.997 -.054 L -1.632 -.035 L -.785 .189 L -1.043 .096 L -.806 .188 L -1.419 .152 L -1.228 -.166 L -1.46 -.296 L .319 .747 L -.051 .093 L -1.051 -.017 L -1.294 -.184 L -3.168 -.611 L 1.538 -.566 L .463 -.114 L -.092 -.226 L -.423 -.169 L -1.067 -.017 L -2.21 -.015 L -.812 -.074 L -.629 -.018 L -1.238 -.434 L -.87 -.208 L .587 -.306 L 1.257 -.041 L 3.036 .147 L 2.025 .034 L 1.343 .017 L 2.117 -.157 L 1.055 -.212 L .292 -.096 L .054 -.345 L -.627 -.287 L -.82 -.21 L -1.217 .156 L -1.11 .252 L -1.31 .021 L -1.138 -.113 L -.753 .078 L -.879 .098 L -.68 -.056 L -.857 -.19 L -.664 -.365 L -.816 -.191 L -.662 -.057 L -.726 .059 L -.486 -.076 L -1.416 -.481 L -.044 -.35 L .36 -.45 L .81 -.119 L 1.235 -.1 L 1.517 -.14 L 2.074 -.161 L 1.29 -.081 L .951 -.396 L 1.089 -.259 L .843 -.081 L 2.478 -.005 L 1.101 -.101 L 1.942 .036 L .402 -.139 L .31 -.199 L .609 -.16 L .202 -.658 L .276 -.501 L .116 -.101 L -.89 -.228 L -1.947 -.039 L -1.155 .194 L -.959 -.125 L -1.243 -.383 L .595 -.781 L 1.38 -.332 L 2.845 -.359 L 1.407 -.225 L 1.962 -.249 L 2.112 -.162 L 1.163 .087 L 1.213 -.07 L 1.319 -.07 L .345 -.181 L .011 -.226 L -.357 -.753 L -.022 -.208 L .522 -.14 L 1.886 -.05 L 1.526 .205 L 2.141 .41 L 1.296 .226 L .802 .181 L .823 -.275 L -1.657 -.525 L -.697 -.509 L .167 -.047 L 2.2 -.122 L 1.166 -.12 L 1.854 -.216 L 2.52 -.195 L .73 .069 L 1.064 .116 L .232 1.738 L .913 -.162 L .539 -.322 L .432 -1 L 1.003 .021 L 2.004 .323 L 1.858 .414 L 1.529 .25 L .205 -.3 L -.644 -.3 L -.816 -.537 L -.894 -.4 L .295 -.287 L .742 .022 L 1.758 .02 L 1.136 .212 L 2.754 .373 L 1.284 .279 L 2.109 .322 L 1.878 .274 L 1.872 .204 L .8 -.209 L .816 -1.483 L -.326 -.191 L -1.292 -.334 L -1.176 -.533 L .708 -.247 L 2.404 -.005 L 2.962 -.328 L 1.329 -.077 L 1.527 .17 L 2.221 .488 L 1.567 .167 L 2.005 .142 L .3 -.761 L -.3 -.472 L 2.646 -.206 L 2.021 -.08 L 2.589 .095 L 1.989 .146 L 1.886 -.18 L 2.367 -.207 L 2.043 -.005 L 1.859 .223 L 1.825 -.055 L 1.315 .072 L .619 .099 L .55 -.102 L 1.946 .146 L 1.707 .046 L 1.673 .096 L 2.438 .761 L 1.368 .241 L 1.345 -.076 L 1.118 .168 L 2.594 .237 L .445 .408 L -.304 .12 L -.492 .192 L -1.683 .146 L -2.303 .124 L -1.152 .121 L -1.233 .05 L -1.469 -.068 L -2.831 -.064 L -2.22 -.066 L -1.389 .168 L -1.614 .027 L -1.933 .027 L -1.16 .026 L -1.485 .168 L -.444 .118 L -1.322 .213 L -.335 .464 L .743 .251 L 2.551 -.281 L 1.367 -.072 L 3.912 .038 L 2.223 -.12 L 2.331 -.005 L .997 -.025 L .93 .067 L 1.77 .434 L .671 .09 L 1.087 -.186 L 1.663 -.21 L 1.536 -.281 L 1.964 -.144 L .59 .462 L -.566 .482 L -2.316 .639 L -.973 .338 L -1.281 .734 L .12 .307 L .319 .152 L .796 -.089 L .477 -.044 L 1.616 -.553 L 1.766 -.537 L 1.413 -.385 L 1.706 -.32 L .775 -.207 L 1.662 -.163 L 1.618 .111 L 1.391 .065 L 1.497 -.3 L .703 -.324 L 1.129 -.234 L 2.148 -.004 L 1.672 .112 L 1.097 .044 L 1.197 .136 L 1.135 .228 L 1.107 .112 L .316 .25 L -.181 .273 L -1.97 .252 L -1.491 .138 L -1.245 .494 L -.557 .289 L -1.604 .355 L -1.57 .548 L -1.063 .089 L -.918 -.042 L -1.592 .047 L -2.213 -.039 L -1.491 .198 L -.731 .217 L -.495 .535 L .166 .322 L 1.949 -.305 L 1.581 -.046 L 1.856 .101 L .003 .42 L -.743 .241 L -2.388 .124 L -.463 .14 L -.213 .199 L -.156 .595 L -.471 .71 L -.678 .158 L -1.06 -.077 L -.742 .041 L -.837 .9 L -.987 1.087 L -.15 .347 L .454 .307 L .403 .095 L .602 -.481 L .743 -.368 L .856 -.041 L 2.345 .266 L .353 .096 L .262 .288 L -.059 .211 L -1.234 -.074 L -.673 -.018 L -.512 .097 L -.136 .191 L .29 .286 L 1.756 .188 L .557 .132 L 1.802 -.137 L .526 .208 L .214 .323 L -.049 .436 L -.256 .133 L -1.835 -.186 L -1.653 -.054 L -.781 -.074 L -1.295 .078 L -1.382 .475 L -.823 -.13 L -.49 -.15 L -1.06 .04 L -.283 .377 L 1.393 .599 L 1.187 .222 L 1.298 .128 L 1.665 .072 L .696 .148 L .551 .482 L .272 .444 L .014 .554 L -.434 .405 L -.384 .074 L -1.292 -.181 L -.82 -.109 L -.372 .111 L .023 .55 L .118 .236 L .426 .162 L .618 .089 L .723 .215 L .914 .142 L .752 .16 L .383 .376 L -.338 .233 L -.832 .145 L -.647 .126 L -1.747 -.032 L -1.176 -.087 L -1.624 -.086 L -.592 .448 L .551 .195 L 1.396 .051 L 1.052 .158 L .724 .248 L .088 .319 L -.035 .549 L -.13 -.005 L -1.092 -.045 L -1.247 .108 L -.596 .266 L -1.246 .02 L -1.225 -.139 L -1.497 -.404 L -.922 -.478 L -.373 -.07 L -1.094 .286 L 345 36.811 l -1.084 .09 L -.589 .178 L -1.451 -.033 L -.913 -.087 L -.969 .143 L -.395 .125 L -.174 .283 L .006 .141 L .354 .527 L .71 .245 L 1.284 .05 L 1.515 .26 L 1.567 -.056 L 1.323 .54 L .758 .226 L .482 .226 L 1.196 .329 L 1.252 .38 L .376 .276 L .483 .898 L .892 -.208 L .278 -.139 L .397 .207 L .298 .43 L .071 .344 L .198 1.164 L -.169 .205 L -.371 .12 L -.541 -.101 L -.546 -.119 L -.917 .002 L -1.041 .036 L -1.488 -.27 L -.637 -.409 L -.415 -.634 L -.354 -.274 L -1.17 -.566 L -.84 -.292 L -.748 -.137 L -1.095 -.084 L -.521 .14 L -.962 .105 L h 351.365 40.026 m -1.527 -.537 L -.96 -.225 L -.712 -.156 L -.159 -.069 L -.314 -.419 L 1.483 -.038 L .893 .139 L 1.064 .26 L .819 .296 L .162 .488 L -.215 .209 L -.533 .053 L h 281.574 46.135 m -.568 -.133 L -.707 -.318 L -.801 -.183 L -.197 -.101 L -.25 -.218 L -.08 -.844 L .287 -.34 L .368 -.018 L .646 .135 L 1.157 .066 L 1.287 .27 L .748 .269 L .595 .1 L .777 .217 L .603 .335 L -.144 .202 L -.112 .034 L -.543 .051 L -.774 .035 L -.77 .186 L -1.009 .153 L -.511 .102 L
+105.98 81.688 N -.952 -.826 L -.198 -.342 L -.024 -.476 L .095 -.104 L .408 .044 L .312 -.045 L .781 .177 L .658 -.076 L .28 .119 L .138 .163 L -.234 .224 L -.173 .565 L -.028 .312 L -.581 .075 L -.483 .19 L h 125.24 92.375 m -1.312 -.288 L -1.345 -.434 L -.218 -.174 L .061 -.189 L .376 -.466 L -1.023 .002 L -.413 .248 L -.299 -.072 L -.416 -.188 L .166 -.452 L -.487 -.334 L -.269 -.014 L -.735 -.086 L -.226 -.262 L .317 -.292 L -.976 -.524 L -.556 .118 L -.386 -.102 L -.852 -.511 L -1.277 -.863 L -.219 -.235 L .02 -.117 L .962 -.12 L .337 .043 L 1.979 .598 L .981 .204 L 1.772 .202 L .385 .263 L .618 .526 L .426 .642 L .433 .422 L .362 .189 L 1.587 .536 L .316 .203 L .48 .756 L .116 .407 L -.279 .349 L -.407 .016 L h 271.379 92.089 m -1.202 -.23 L .641 -.743 L .358 -.161 L .279 .058 L .292 0 L .355 -.263 L -.697 -.653 L .079 -.219 L 272 89.003 l 1.121 -1.35 L 1.454 -1.31 L .725 -.442 L .496 -.192 L 1.315 -.194 L .198 .073 L .11 .221 L -.299 .221 L -.582 .03 L -.242 .133 L .349 .44 L -.755 .78 L -1.226 1.438 L -.271 .526 L .113 .291 L .11 0 L .428 -.176 L .483 -.555 L .458 -.191 L 1.115 .305 L -.896 .687 L .261 .203 L .229 .072 L 1.423 .565 L .758 -.03 L .325 -.408 L .309 -.059 L .718 .057 L .826 .202 L .616 .231 L -.297 .292 L -.373 .233 L -.708 .467 L .339 .333 L .477 .362 L .26 .014 L .417 -.161 L .464 -.132 L .278 .116 L .02 .16 L -.254 .262 L -.404 .248 L -.892 .104 L 280.84 93 l .273 .362 L .124 .405 L .28 .231 L .183 -.203 L .309 -.262 L .527 .159 L -.099 .449 L .149 .275 L .716 .028 L .085 -.015 L .015 .203 L -.168 .304 L -.25 .652 L -.34 .651 L -.222 -.072 L -.71 -.128 L -.301 -.144 L -.042 -.651 L -.601 .406 L -.374 .015 L -.095 -.274 L .497 -.652 L .011 -.333 L -.421 -.478 L -.279 -.072 L -.388 .392 L -.423 .291 L -.365 .146 L -.435 .204 L -.552 .536 L -.496 .334 L -.881 -.042 L -.222 -.217 L .165 -.145 L 1.229 -.408 L .466 -.522 L .632 -.363 L -.699 -.129 L -.601 -.057 L -.322 .464 L -.412 .015 L -.13 -.159 L .04 -.493 L -.757 .016 L -.148 .29 L -.41 .218 L -1.052 .045 L -.709 -.057 L -1.139 -.186 L -1.012 -.085 L -1.355 .061 L -1.014 .147 L -.145 -.188 L -.215 -.463 L .187 -.175 L .561 -.334 L .734 -.408 L .502 -.161 L .636 -.335 L h 265.435 98.655 m -.469 -.057 L -.497 -.273 L -.356 -.562 L .062 -.635 L .742 -.738 L .932 -1.043 L .816 .432 L -.375 .435 L -.112 .462 L -.233 .333 L -.262 .116 L -.58 .319 L -.244 .448 L .522 .244 L .168 -.029 L .279 -.26 L .42 -.362 L .617 -.319 L .309 .057 L .495 .461 L -.177 .347 L -.246 .159 L -1.134 .42 L -.682 .044 L h 211.34 59.677 m -.68 -.046 L .068 -.872 L -.375 -.333 L -.958 .161 L -2.375 .29 L .107 -.461 L .56 -.303 L 1.644 -.561 L -.302 -.478 L -.102 -.415 L .106 -.417 L .398 -.835 L .434 -.566 L .254 -.648 L .331 -.471 L 1.11 .566 L -.312 .518 L .791 .386 L .527 .047 L .402 -.469 L .67 .112 L .806 .289 L .917 .514 L .582 .255 L 2.168 .492 L .442 .271 L .176 .255 L -.09 .925 L .539 .047 L .57 -.065 L .934 .046 L .701 .142 L 1.019 .427 L -.419 .096 L -.269 .127 L -.46 .271 L -.949 -.046 L -.623 -.125 L -1.328 -.125 L -.438 -.126 L 217.259 58 l -.528 -.301 L -1.017 -.237 L -.528 .017 L -.203 .271 L .174 .588 L -.126 .096 L -1.314 .161 L -.673 .493 L -.588 .302 L -1.116 .287 L h 200.125 19.1 m -.862 -.015 L -1.085 -.195 L -.308 -.664 L .819 -.304 L .77 -.072 L .666 -.024 L 3.475 -.125 L 1.263 -.12 L 1.374 -.026 L 1.714 .324 L .397 -.094 L .397 -.377 L 1.303 -.287 L 1.759 -.099 L 1.975 .209 L .746 -.001 L 2.562 .137 L 2.621 .324 L 1.424 .09 L 1.461 .161 L .448 -.165 L -1.433 -.42 L -1.67 -.352 L -.816 -.429 L .293 -.242 L 1.361 -.148 L 1.101 -.246 L 1.431 -.101 L 2.382 -.201 L 1.666 .119 L 1.944 .191 L 1.009 .265 L 1.19 .456 L .354 .047 L .273 -.362 L -.959 -.508 L -.828 -.292 L .499 -.248 L 1.45 .121 L 1.832 .168 L 1.653 .07 L 1.639 .46 L .378 .023 L .062 -.195 L -.301 -.539 L 1.781 -.004 L 1.408 .046 L .832 .269 L .831 .34 L .618 -.001 L -.044 -.268 L -.331 -.467 L 1.075 -.077 L 3.691 .386 L 2.726 .288 L 1.937 -.077 L 2.987 .018 L .967 .047 L .757 .12 L .126 0 L 1.419 .094 L 1.089 .191 L .744 .095 L 1.685 .044 L 1.357 .357 L -.385 .358 L -1.237 .121 L -1.206 .356 L -1.849 .191 L -.978 -.045 L -2.191 -.159 L -2.284 .005 L -.776 .142 L -1.915 .168 L -.597 .465 L .75 .366 L .761 .044 L 1.03 -.048 L 1.705 -.279 L .79 .021 L .808 .434 L -.168 .114 L -1.246 .139 L -1.38 .207 L -1.174 .295 L -2.098 .518 L -1.316 .224 L -1.19 .355 L -.924 .286 L -2.252 .005 L .65 .906 L -.449 .193 L -2.297 .455 L -.768 -.019 L -1.587 -.037 L -1.462 -.218 L -2.386 -.164 L -.66 .33 L 2.591 .695 L -.662 .141 L -.967 -.038 L -1.297 .022 L -1.068 .022 L -2.58 -.214 L -2.009 .063 L -.134 .66 L 1.257 -.105 L 1.065 .018 L 2.312 .292 L .557 .157 L .12 .67 L -.33 .197 L -1.031 .12 L -.515 .705 L -1.073 .021 L -.448 -.058 L -.402 .176 L .297 .253 L .759 .25 L -.328 .136 L -1.615 .08 L -.867 -.037 L -1.71 -.171 L -.422 .078 L .41 .791 L -.08 .231 L -.649 .289 L -.767 .155 L -1.52 -.112 L -2.039 -.111 L -1.43 -.227 L -1.008 .079 L -1.219 .5 L 1.031 .112 L .368 .057 L 2.154 .11 L 1.759 .13 L 1.534 .168 L 2.001 .034 L .66 .34 L .045 .359 L -.907 .398 L -2.685 .268 L -.927 .115 L -1.054 .227 L -1.115 .077 L -.467 -.28 L -.797 -.638 L -.56 .039 L -.631 .001 L -1.453 -.318 L -.001 .17 L .331 .508 L -1.477 -.016 L -1.5 -.129 L -.875 -.319 L -1.033 -.471 L -.388 .058 L .527 .717 L -.24 .17 L -.821 .133 L -1.72 -.109 L -2.276 -.033 L -.972 -.073 L -1.382 -.394 L -.642 -.131 L -.282 .453 L -.619 .152 L -1.843 -.316 L .161 -.586 L .219 -.228 L 1.525 -.117 L .61 -.249 L .961 -.173 L 1.179 .036 L .499 -.172 L -1.073 -.4 L -1.043 -.651 L .052 -.154 L .479 -.117 L 1.316 .036 L 1.743 .093 L .888 .21 L 1.108 .517 L 1.35 .323 L 1.085 .093 L 1.667 -.022 L .829 -.136 L .086 -.268 L .514 -.304 L -3.019 .001 L -1.025 -.171 L -.156 -.85 L .211 -.154 L -1.74 -.153 L -1.963 -.152 L -.274 0 L -.631 .114 L .204 -.758 L 1.159 -.551 L 1.104 -.16 L 1.837 -.003 L 1.164 .037 L 1.37 .076 L 2.023 .311 L 1.342 .115 L .486 -.158 L 1.132 -.041 L -3.399 -.802 L -1.742 -.313 L -3.555 -1.27 L -.406 .242 L -1.398 -.878 L .025 -.258 L .313 -.108 L 1.747 .104 L 1.905 -.004 L 2.019 .06 L 1.6 .382 L 2.535 .784 L 1.448 -.043 L .833 .095 L -1.387 -.555 L -2.015 -.317 L 1.208 -.743 L 1.456 -.329 L 1.731 -.025 L 1.529 -.222 L 2.042 -.07 L 1.157 -.112 L 1.414 -.051 L -1.778 -.479 L -1.425 -.153 L -2.501 .027 L -1.243 .248 L -1.305 .158 L -1.425 .202 L -1.447 .047 L -.586 .067 L -1.532 -.438 L -.214 .111 L -.543 .156 L -2.16 -.018 L -1.58 .365 L .311 -.828 L .98 -.292 L .007 -.202 L -.606 -.247 L -1.375 -.156 L -1.39 .003 L -4.189 .505 L -2.031 .672 L -.408 -.11 L -.569 -.251 L .395 -.133 L .678 -.023 L -.117 -.316 L -.698 -.398 L -1.216 -.056 L -.216 -.003 L h 200.125 20.844 m .899 -.096 L .832 .196 L .339 .5 L .511 .495 L .427 .063 L 1.141 .041 L -.081 -.236 L .056 -.411 L .438 -.109 L .718 .194 L .718 .322 L .374 .3 L -.066 .171 L .056 .826 L .764 .442 L .953 -.017 L 1.276 -.074 L 1.646 .504 L -1.123 -.264 L -1.528 .34 L -1.599 .221 L -.83 .377 L -.287 .197 L -.265 .315 L -.448 .021 L -1.493 -.41 L -.656 .335 L .465 .43 L -.029 .235 L -.412 .196 L -.392 .04 L -1.086 -.31 L -.944 -.311 L -.26 .645 L -.117 .068 L -.083 .049 L -.888 .041 L -1.74 -.094 L -1.458 -.153 L -.888 -.154 L -.494 .021 L -3.115 -1.31 L -.191 -.276 L 1.971 -.241 L 3.32 .093 L .889 .097 L 1.573 .1 L -2.485 -.693 L -3.019 -.213 L -1.103 .122 L -1.43 -.017 L -.597 .18 L -1.008 .022 L -.606 -.198 L -1.066 -.517 L -1.425 -.479 L -.341 -.355 L 1.971 .124 L 2.278 -.175 L -1.255 -.249 L -.756 -.351 L .38 -.305 L .729 -.132 L .769 -.023 L .921 .042 L .156 -.219 L -1.799 -.793 L 1.053 -.114 L 1.213 -.182 L 1.13 .087 L .584 -.046 L .145 -.18 L -.514 -.475 L 1.362 .134 L .941 .066 L .83 .202 L 1.589 .869 L .653 .264 L .772 .24 L .674 -.001 L .36 -.039 L h 179.067 27.216 m -1.156 -.056 L -.604 -.173 L -.926 -.638 L -.621 -.193 L -3.102 -.091 L -1.487 .081 L -.622 .001 L -1.444 -.056 L -.767 -.154 L -1.019 -.37 L -.161 -.234 L .335 -.138 L .802 -.001 L 1.687 .232 L .867 -.021 L -.031 -.235 L -.252 -.275 L -1.344 -.49 L -.579 -.098 L -1.075 -.077 L -1.392 -.196 L .065 -.397 L 2.246 -.124 L 2.392 .155 L .77 .376 L .999 .453 L 1.979 .193 L 2.189 .114 L 1.178 .233 L .604 .254 L 1.123 .721 L .581 .446 L .168 .426 L -.481 .194 L -.919 .137 L h 185.907 26.758 m -1.078 -.037 L 184 26.529 l -1.029 -.484 L -1.144 -.76 L -.03 -.216 L .239 -.099 L 2.296 -.044 L 1.816 .311 L 3.101 .542 L -.047 .351 L -.254 .331 L -.436 .04 L -1.563 .177 L -1.043 .08 L h 156.886 26.865 m -1.573 .646 L -.558 .27 L -1.85 .042 L -1.019 .079 L -1.898 -.15 L -.577 -.114 L -.302 -.423 L .334 -.291 L 1.365 -.177 L .899 .056 L 2.351 -.102 L .496 0 L 2.331 .163 L h 132.902 31.305 m -.53 -.186 L -.95 -.466 L -.424 -.112 L -.33 .057 L -.56 .207 L -1.269 .059 L -.786 -.279 L -.283 -.319 L .23 -.264 L 1.13 -.097 L .503 -.133 L .771 -.134 L .977 -.399 L .848 -.211 L .726 -.172 L .548 -.344 L 1.083 -.231 L 1.277 -.079 L 2.532 -.158 L 1.68 .016 L .888 -.29 L 1.038 -.079 L 1.503 .438 L -.756 .097 L -.852 .231 L -.22 .268 L .12 .266 L .469 .474 L -.777 .001 L -.912 .115 L -.918 .662 L -1 -.017 L -.867 -.981 L -.694 -.15 L -.379 .02 L -.229 .285 L -.588 .342 L -.63 .623 L -.595 .151 L -.284 .375 L -.705 .356 L -.787 .058 L h 191.827 30.313 m -1.266 -.054 L -2.278 -.165 L -.426 .058 L -.332 -.094 L -.896 -.489 L -1.185 -.414 L .192 -.229 L 2.433 -.042 L 1.542 .263 L 1.472 .054 L .171 0 L .89 .358 L -.179 .246 L .123 .32 L -.263 .188 L h 144.688 31.739 m -2.222 -.395 L -.325 -.674 L .503 -.057 L .595 -.17 L .945 -.096 L .953 -.133 L 1.279 -.059 L .522 .187 L .65 .374 L .659 .186 L 1.55 -.209 L .617 .149 L 1.624 .762 L 1.016 .351 L .897 .036 L .96 -.058 L 1.418 .09 L .591 -.02 L 1.116 -.169 L .092 -.297 L -.557 -.559 L -.941 -.391 L -1.347 -.354 L .96 -.322 L .524 -.379 L .569 -.152 L 1.097 -.116 L .507 .17 L .773 .678 L -.017 .413 L .518 .654 L .565 .111 L .9 .036 L 1.805 .406 L -.334 -.465 L .151 -.28 L .409 -.076 L 1.495 .24 L .932 .39 L -.292 .409 L .039 .5 L -.358 .461 L -.573 .277 L -.755 .111 L -.782 .001 L -1.682 .095 L -1.156 -.071 L -1.757 -.18 L -.622 -.017 L -1.129 .277 L -1.132 .202 L -.76 .182 L -.977 .254 L -1.625 .292 L -1.338 .2 L 149.23 34.5 l -.748 -.07 L -1.445 -.286 L -.276 -.378 L .648 -.128 L 1.219 -.038 L .738 -.146 L .852 -.075 L 1.166 -.057 L .622 .017 L 1.09 -.149 L .483 -.553 L -2.768 -.087 L -.925 -.054 L -1.564 .28 L -1.625 .168 L -1.292 .04 L -.795 .093 L -1.681 -.347 L -.479 .167 L -.92 .075 L -.979 -.127 L -.854 -.33 L .023 -.111 L .863 -.427 L 1.098 -.058 L 2.047 -.022 L .96 -.159 L h 178.479 33.234 m -.984 -.219 L -.193 -.294 L .764 -.389 L .433 -.112 L .088 -.167 L -.447 -.333 L -1.161 -.054 L -2.13 .227 L -.939 .076 L -.331 .019 L -.854 -.276 L .039 -.335 L .739 -.02 L .542 -.244 L .587 -.057 L -.466 -.598 L .176 -.245 L .132 -.226 L .49 .018 L .859 .224 L 1.942 .746 L .426 .186 L .46 -.094 L -.101 -.243 L -.959 -.486 L -.371 -.131 L -.407 -.357 L .436 -.095 L .956 -.059 L .713 .131 L 1.033 .262 L .572 .168 L .27 .018 L .162 -.452 L .478 -.133 L .73 .112 L .717 .168 L .327 .168 L .367 .75 L -.034 .616 L -.247 .242 L -.831 .335 L -.017 .352 L .35 .625 L -.361 .147 L -1.648 -.089 L -.862 .112 L -1.446 .003 L h 200.125 30.572 m -.895 .045 L -.853 .17 L -.37 .467 L 1.133 .054 L .984 -.038 L .046 -.001 L .847 .11 L .463 .129 L .498 .463 L .727 .424 L .621 .091 L .213 -.074 L .043 -.314 L .286 -.056 L 1.075 -.002 L .883 -.187 L .766 .11 L .835 .239 L .665 .257 L .976 .053 L .775 -.463 L 1.393 -.281 L 1.704 -.114 L 1.951 -.246 L 1.533 .053 L 2.59 .014 L .381 .037 L .79 .314 L .911 .239 L 1.418 .146 L .653 .128 L .21 .037 L .361 .166 L .181 .257 L -.4 .148 L -1.833 .407 L -.135 .255 L .469 .666 L -2.486 .076 L -.592 .02 L -.651 .091 L -.768 -.053 L -.846 -.16 L -.405 -.125 L -.306 -.667 L -.833 -.218 L -.366 .129 L .072 .723 L -.536 .127 L -.747 -.053 L -.91 .109 L -.728 -.017 L -.495 .001 L -1.342 -.213 L -.593 -.197 L -.495 -.017 L -.209 .433 L -1.801 .111 L -.831 .074 L -1.453 -.069 L -.404 -.251 L -.144 -.686 L -1.237 .129 L -.389 .181 L -.326 .325 L -.955 .2 L -1.011 -.034 L -.112 -.027 L -.704 -.169 L -1.186 -.575 L -.675 .489 L -1.131 -.07 L -.666 -.688 L -.442 -.717 L .587 -.481 L .019 -.371 L -.292 -.316 L -1.249 -.651 L -.617 -.299 L -.047 -.338 L .636 -.133 L 1.226 -.078 L 2.472 -.023 L .763 .093 L 1.118 .261 L .188 .04 L .872 .184 L -.613 .189 L -.259 .013 L h 128.19 41.985 m -.926 -.016 L -1.059 -.102 L -.362 -.466 L -.549 -.467 L -.432 -.259 L -1.123 -.363 L -1.36 -.067 L -.951 -.138 L -.469 -.19 L -.168 -.174 L .537 -.106 L .589 -.298 L .481 -.211 L .08 -.386 L -.437 -.809 L .552 -.001 L .468 -.177 L .307 -.372 L 1.104 -.533 L .526 -.588 L -.121 -.32 L -.271 -.16 L -1.229 -.677 L -.375 -.448 L .869 -.001 L .823 -.056 L 1.455 .051 L .97 .016 L 1.515 -.092 L 1.284 -.146 L 1.242 -.074 L .495 .125 L 3.242 .801 L .918 .088 L .708 -.055 L 1.316 -.127 L 1.223 .016 L .771 .07 L 1.35 .373 L 2.389 .815 L -.242 .143 L -.432 .036 L -.26 .072 L -1.609 .322 L -1.073 .144 L -1.829 .428 L -1.069 .319 L -1.604 .725 L -1.025 .563 L -.549 .089 L -.974 .124 L .066 .924 L -.271 .504 L -.662 .278 L -1.215 .124 L -1.213 -.067 L -.521 .485 L -.898 .312 L h 190.483 39.666 m -1.146 -.208 L -.146 -.524 L -.941 -.806 L -.207 -.582 L .058 -.389 L .27 -.657 L .377 -.321 L 1.256 .033 L -.089 -.16 L -.416 -.266 L -.185 -.286 L .211 -.09 L .234 -.072 L 2.154 -.058 L 1.215 .087 L 1.464 .248 L 1.282 .051 L 1.316 -.146 L 1.051 .016 L .694 .105 L .639 .213 L -.007 .089 L -.224 .179 L -.824 .428 L -.874 .746 L -1.513 .92 L -1.386 .073 L -2.379 -.154 L -1.269 .055 L 1.392 .717 L -.188 .315 L -.855 .369 L -.964 .072 L h 181.204 41.523 m -.491 -.085 L -1.101 -.552 L -.952 -.641 L -1.014 -.468 L -.978 -.225 L -1.438 -.12 L -.55 -.174 L -2.255 -1.066 L .866 -.654 L .653 .14 L 1.032 .474 L 1.063 .227 L .46 .052 L .615 -.283 L .908 -.619 L .415 -.036 L .018 -.212 L -1.062 -.565 L -1.068 -.424 L -.177 -.231 L .132 -.107 L 1.683 .086 L .711 -.215 L .42 0 L .996 .39 L .56 .035 L .58 -.055 L .435 -.25 L 1.232 -.127 L 1.354 .069 L .912 .23 L -.324 .268 L -.58 .125 L -.323 .338 L -1.55 .375 L -.392 .16 L -.069 .194 L .253 .247 L .506 .105 L .692 -.089 L 1.08 .174 L .868 .245 L .391 .017 L .564 .262 L .186 .438 L -.681 .352 L -.156 .35 L -.271 .68 L -.457 .366 L -.508 .14 L -.658 .019 L -.582 -.103 L -.773 -.346 L -.653 -.103 L .013 .208 L 1.054 .553 L -.817 .399 L -.77 .036 L h 243.524 60.394 m -.234 -.208 L -1.199 -.235 L -.673 -.331 L -.154 -.269 L .346 -.064 L .616 -.461 L -1.378 -.521 L -1.132 -.125 L -.76 -.349 L -.929 -.731 L -.035 -.511 L -1.115 -.062 L -1.311 -.366 L -.675 -.031 L .284 .767 L -.155 .096 L -.409 -.015 L -1.704 -.332 L -.309 .033 L -.325 .304 L -.441 .288 L -1.312 .082 L -1.349 -.173 L -1.343 -.189 L -.813 -.254 L -.052 -.319 L .196 -.4 L .382 -.354 L 1.066 -.163 L .192 -.178 L -.128 -.516 L .206 -.033 L 1.357 .11 L 1.408 .175 L .517 .144 L .962 .626 L .051 -.386 L -.154 -.193 L .077 -.194 L .585 -.033 L .977 -.099 L .652 -.163 L .649 -.114 L .515 .063 L .785 .031 L .166 -.275 L -1.138 -.825 L -.773 -.356 L -.119 -.228 L .167 -.163 L .586 -.066 L .72 -.246 L 1.409 -.591 L .361 -.541 L .771 -.46 L .493 -.379 L -.109 -.593 L -.899 -.841 L -.407 -.496 L -.541 -.364 L -.414 .001 L -1.258 -.33 L -1.041 -.481 L -.244 -.467 L -.527 -.384 L -.442 .202 L -.551 .202 L -.825 -.015 L -.293 .117 L -.62 .018 L -1.255 .169 L -.214 -.667 L 1.032 -.052 L 1.23 -.103 L .163 -.269 L -1.604 -.618 L -1.552 -.67 L -.879 -.015 L -.567 -.185 L -.169 -.542 L -.677 -.339 L -.45 -.05 L -.918 -.306 L -.687 -.341 L -.385 -.119 L -.611 .155 L -.81 -.187 L -1.177 -.238 L -.489 -.085 L -.379 .138 L .529 .307 L .453 .05 L 2.838 .712 L .438 .271 L .069 .306 L -.505 .221 L -.669 .069 L -.541 -.033 L -.757 -.049 L -.818 -.252 L -1.153 -.27 L -.667 -.066 L -.323 .17 L .044 .204 L .426 .236 L .259 .438 L -1.703 -.553 L -.47 -.05 L -.396 .119 L -.63 .153 L -.767 -.218 L -.693 -.117 L -.859 .12 L -1.474 -.184 L -1.995 -.167 L -1.321 .037 L -1.146 -.032 L -.862 -.186 L -.003 -.597 L -.363 -.153 L -.904 -.049 L -.396 .342 L -.623 .086 L -1.214 -.049 L -1.076 -.168 L -1.303 -.477 L -.415 -.376 L .123 -.275 L .868 -.07 L 1.131 .067 L 1.212 .101 L .879 -.019 L .312 -.19 L -.934 -.463 L -.8 -.275 L -.905 -.102 L -1.106 -.119 L -.752 .036 L -.539 -.017 L -1.249 -.223 L .114 -.416 L .292 -.557 L .34 -.14 L .646 -.054 L .081 -.227 L -1.082 -.4 L -.044 -.175 L .449 -.79 L 1.197 -.919 L .565 -.284 L .918 -.321 L .74 -.374 L .423 -.037 L .37 -.178 L .698 -.001 L .481 -.125 L .71 -.09 L 1.436 -.109 L 1.348 .033 L .857 .194 L -.92 .393 L -.815 .48 L -1.394 .639 L -.43 .529 L .169 .369 L 1.256 .541 L -.444 .298 L -.076 .402 L .257 .313 L .862 .554 L 1.559 .621 L -.096 .121 L -1.272 .331 L -.072 .31 L .959 .033 L 1.504 .101 L .654 -.639 L -.103 -.415 L -.343 -.277 L -.724 -.103 L -.422 -.138 L -.884 -.538 L .101 -.157 L .506 -.245 L .473 -.193 L 1.001 .12 L .837 -.071 L -1.204 -.47 L -.703 -.034 L -.793 -.279 L -.056 -.193 L .053 -.545 L .886 -.319 L 1.207 .086 L 1.509 -.056 L -.939 -.281 L -1.233 -.351 L .793 -.303 L 1.288 -.198 L 1.044 -.126 L 1.688 -.323 L 1.114 .016 L .642 .052 L .833 .141 L .782 .478 L 1.536 .97 L .058 .141 L -.583 .687 L -.709 .632 L .038 .733 L .364 .086 L .65 .033 L 1.088 -.315 L .284 -.455 L .595 -.088 L .791 .034 L .454 .174 L -.006 .262 L .16 .47 L .875 .189 L .196 -.122 L -.204 -.854 L .218 -.123 L .456 -.474 L 1.038 -.265 L .98 -.054 L .748 .034 L .98 .174 L 1.172 .138 L 1.151 -.09 L .688 .139 L .327 .262 L .621 .331 L .574 .191 L 235.438 40 l 0 .191 L -.531 .088 L -.484 .279 L -.818 .262 L -.148 .225 L .45 .259 L .427 .068 L .897 -.417 L .652 -.174 L .502 .051 L .476 .242 L .365 .466 L .516 .413 L .342 -.242 L 1.304 -.798 L 1.935 .256 L .915 .361 L -.051 .069 L -.638 .346 L -.708 .517 L 1.167 -.054 L .455 -.173 L 1.078 -.105 L .033 .704 L .797 .324 L .523 -.069 L .831 -.207 L 1.316 -.088 L .816 .221 L .566 .273 L -.162 .154 L -.461 .223 L -1.87 .43 L -.238 .272 L .523 .253 L .456 -.068 L .747 -.171 L 1.235 -.122 L .406 -.29 L .361 -.103 L .479 .067 L .51 .187 L .544 .339 L .636 .522 L -1.019 .002 L -1.2 .053 L -.424 .135 L .059 .269 L .372 .134 L 1.333 .065 L .938 .183 L .543 .217 L .233 .301 L -.37 .034 L -.748 .001 L -1.011 -.082 L -.875 -.216 L -.824 -.065 L -.316 .185 L 1.23 .583 L -.216 .201 L -1.552 .12 L .245 .283 L .437 .166 L .551 .032 L 1.331 .364 L 1.312 .347 L .247 .182 L .039 .282 L .351 .38 L .75 -.217 L .536 .049 L 1.413 .295 L .298 -.067 L .649 -.15 L .61 .032 L .752 .379 L .862 .477 L .376 .346 L -.685 .1 L -.801 .117 L -.027 .444 L .795 -.001 L 1.405 -.052 L .51 -.132 L .895 .048 L -.386 .559 L .918 .179 L .514 -.001 L .943 -.379 L .685 .343 L 1.089 .407 L .194 .098 L -.275 .229 L -.254 .099 L -.103 .326 L -.819 .05 L -.718 -.21 L -.247 -.048 L -.794 .213 L .968 .454 L .279 .162 L .057 .276 L -1.057 .197 L -.356 .228 L -.312 .292 L -.372 -.113 L -.819 -.583 L -.29 1.103 L .354 .903 L -.419 .065 L -.677 -.257 L -.751 -.176 L -.205 -.177 L -.018 -.243 L -.315 -.274 L -.93 .276 L -.743 -.613 L .051 -.292 L .27 -.374 L -.304 -.129 L -.224 -.016 L -.992 -.08 L -.718 -.292 L -1.17 -.617 L -.769 -.292 L -.762 -.048 L -.452 .23 L -.645 .083 L 250 52.592 l .281 .179 L 1.05 .682 L -.321 .114 L -.686 .05 L -.359 -.259 L 249.277 53 l -.646 -.21 L .275 .488 L .859 .972 L .604 .015 L .587 .08 L .5 .581 L .612 .805 L .513 .432 L .615 -.321 L .285 .047 L .592 .399 L .585 .271 L 1.38 .396 L -.634 .113 L -.213 .208 L .254 .19 L .568 .286 L .962 .444 L .324 .237 L .242 .682 L -.112 .428 L -1.302 -1.155 L -.554 -.237 L -.027 .238 L .118 .27 L 1.055 1.281 L -.01 .3 L -.926 -.125 L .053 .205 L .432 .409 L .378 .519 L -.563 -.172 L -.615 -.313 L -.889 -.693 L -.145 -.031 L -.719 .064 L -.91 -.188 L -1.44 -.662 L -.319 -.285 L -1.062 -.665 L .187 .777 L -1.22 -.473 L -.567 -.158 L -.872 -.03 L .095 .222 L .799 .696 L .853 .426 L 1.842 .645 L 1.296 .644 L .774 .549 L .442 .486 L .429 .689 L -1.833 -.341 L -1.524 -.421 L -1.251 -.28 L -1.444 -.107 L -1.092 -.25 L -.898 -.644 L -1.146 -.14 L -.638 -.204 L -.635 -.141 L -.058 .145 L h 146.194 38.698 m .818 -.037 L .78 -.125 L 1.138 -.548 L .895 -.019 L 1.723 .243 L .939 .262 L -.188 .877 L .515 -.071 L .66 -.019 L .792 -.229 L .599 -.141 L .758 .016 L .334 -.071 L -.989 -.965 L .156 -.036 L 1.38 .138 L 1.208 .245 L .675 .245 L .259 .245 L .194 .508 L .965 1.063 L .638 .346 L 1.045 -.315 L .14 -.261 L -1.243 -1.361 L -.439 -1.321 L .228 -.354 L 1.91 .262 L 1.775 .156 L 2.031 .719 L .36 .175 L .3 .316 L .16 .701 L .511 .645 L .352 .26 L .856 .606 L .048 .19 L -.178 .243 L -.333 .605 L .179 .31 L .224 .12 L 1.4 .649 L .509 .171 L 1.151 .254 L 1.513 .1 L 2.056 .576 L 1.012 .39 L .364 .793 L -.168 .101 L -1.071 -.082 L -.811 -.234 L -.945 -.234 L -.551 .169 L -.665 .204 L -1.021 .036 L -.256 .118 L .208 .689 L 1.087 -.069 L .614 -.152 L .676 -.119 L .876 .536 L -.01 .235 L -.526 .151 L -.517 .252 L -.583 .102 L -1.417 .12 L -1.049 -.015 L -1.194 -.082 L -1.594 -.248 L -2.278 -.499 L -.553 -.217 L -.436 -.335 L -.482 -.033 L -.581 .102 L -.402 .37 L -1.114 .505 L -1.055 .019 L -1.411 .103 L -1.253 .42 L -2.753 .356 L -1.42 .019 L -1.205 .086 L -1.984 -.063 L -.483 .101 L -.916 -.015 L -1 -.282 L -.061 -.468 L .198 -.101 L .002 -.302 L -.395 -.2 L -.462 -.1 L -3.146 -.112 L -1.258 -.115 L -.864 -.167 L -.801 -.2 L -.753 -.504 L -1.274 -.554 L .303 -.069 L .531 -.222 L 1.572 -.054 L .603 -.188 L .558 .016 L .91 -.019 L .904 -.087 L 1.716 .031 L .935 -.002 L 2.14 .047 L 1.09 -.002 L 1.711 -.038 L .415 -.154 L -.681 -.221 L -2.312 -.492 L -1.942 -.202 L -4.059 -.061 L -1.569 -.014 L -1.694 .055 L -.955 .053 L -.604 .001 L -1.651 -.529 L .011 -.207 L .28 -.069 L .823 -.053 L 1.315 -.209 L .996 .032 L 1.78 -.141 L .931 -.105 L .5 -.277 L -.392 -.346 L -2.023 .073 L -1.578 .159 L -.393 -.051 L -.554 -.189 L -.677 -.346 L -.65 -.19 L -.692 .054 L -.709 -.242 L -.039 -.279 L 1.304 -.981 L 2.62 -.848 L .909 -.143 L .373 -.177 L 1.297 -.267 L 1.324 -.162 L .701 -.267 L 1.113 -.091 L 1.026 .246 L -.07 .212 L -.548 .354 L -.021 .81 L h 251.273 99.526 m -.188 .153 L -.16 -.291 L .192 -.077 L -.02 -.256 L .143 -.069 L -.042 -.154 L -.224 0 L -.606 -.231 L .077 -.229 L -.31 -2.819 L -.174 -.274 L -.488 -.288 L -.771 -.025 L -.41 .176 L -.381 -.085 L -.505 -.36 L -.273 -.085 L -.632 .526 L -.514 .626 L -1.139 2.22 L -1.139 1.45 L -1.161 -.124 L -.402 .06 L -.363 .435 L -.174 .375 L -1.093 -.095 L -1.855 -.004 L -2.508 -.029 L -1.76 .009 L -.968 .523 L -.534 .305 L -1.754 .814 L -.545 .164 L -.146 .434 L -.163 .512 L -.44 .275 L -1.156 .197 L -1.305 -.138 L -1.123 -.01 L -.93 .091 L -.47 .203 L -.162 .375 L .018 .319 L .16 .471 L .047 .362 L -.875 .427 L -1.464 .452 L -.944 .163 L -.919 .062 L -.88 .262 L -.939 .478 L -.925 .506 L -.524 .117 L -.573 -.068 L -.497 -.169 L -.371 -.427 L -.012 -.33 L .044 -.218 L .707 -.525 L .414 -.294 L .264 -.433 L .294 -.544 L .377 -.576 L .028 -.746 L -.054 -.545 L -.876 -3.16 L -2.529 -1.05 L -.26 -.33 L .11 -.318 L -.307 -.235 L -.916 -.181 L -.184 -.294 L -.178 -.135 L -.628 .024 L -.46 -.465 L -.101 -.429 L -2.15 -1.061 L -3.975 -1.576 L -.977 -.386 L -.797 -.227 L -.805 .189 L -1.469 .592 L -.707 -.074 L -.542 .049 L -.196 -.144 L -.156 -.115 L -.474 -.041 L -.855 .083 L -.197 -.116 L -.028 -.282 L -.373 .075 L -.412 .191 L -.219 .06 L -.573 .141 L -.506 -.098 L -.064 -.185 L -.469 -.086 L -.241 -.271 L -.502 -.013 L -.16 .247 L -.338 -.48 L -.271 .012 L -.02 -.185 L -1.425 -.208 L -.518 .076 L -.833 -.451 L -.468 -.46 L -.279 -.371 L -.896 -.748 L -.501 .036 L .131 .347 L .387 .588 L -.68 -.003 L -2.687 .029 L -2.798 -.029 L -1.348 .007 L -2.105 -.003 L -2.915 .016 L -2.781 -.029 L -2.131 .012 L -2.935 -.014 L -.601 .003 L -4.84 -.018 L -3.617 .004 L -.875 .005 L -3.821 -.023 L -1.089 .035 L -4.13 -.021 L -.74 -.011 L -5.117 .028 L -1.687 -.006 L -2.87 .001 L -3.938 -.008 L -4.588 .025 L -1.335 -.022 L -2.579 -.001 L -.194 -.013 L -.187 -.151 L -.509 -.305 L -.071 -.437 L .074 -.346 L -.708 .25 L -.373 -.029 L -.331 -.305 L -.162 -.496 L -.125 -.189 L -.385 .088 L -.23 .205 L -.483 .059 L -.721 -.495 L -.119 -.425 L -.201 -.821 L -1.051 .104 L -1.01 -.277 L -.487 -.087 L -.173 -.087 L -.143 -.396 L -.438 -.352 L -.591 .222 L -1.236 .046 L -.461 -.117 L -.383 -.249 L -.106 -.25 L .257 -.648 L .458 -.222 L -.227 -.294 L -1.24 -.086 L .617 -.518 L .398 -.281 L .547 -.149 L .605 -.508 L -.874 .006 L -.621 .149 L -.362 -.043 L 116 83.422 l -.039 -.978 L -.789 .002 L -1.015 -1.066 L .132 -.238 L .034 -.53 L -.547 .056 L -.83 .492 L -1.266 -.934 L -.384 -.521 L -.204 -.402 L -.068 -.432 L .419 -.404 L .161 -.254 L .436 -.3 L -.358 -.689 L -.393 -.777 L .163 -.788 L -.402 -.255 L -2.025 -.763 L -.98 -.314 L -.189 -.029 L -.512 -.393 L -1.67 -1.882 L -1.769 -1.768 L -.814 -.58 L -2.048 -1.175 L -.35 -.322 L -.371 -.492 L -.316 -.199 L -.832 -.073 L -.495 .126 L -.731 .498 L -1.225 .67 L -.848 .205 L -.238 .325 L -.945 -.673 L -2.162 -1.318 L -.348 -.292 L -.173 -.387 L -.332 -.388 L -.739 -.059 L -2.424 .122 L -.84 -.074 L -.196 -.279 L -.089 -1.046 L -.026 -2.167 L .043 -4.334 L .026 -2.183 L -.129 -2.796 L -.052 -2.335 L -.039 -2.259 L .003 -2.855 L -.102 -.483 L .71 .024 L .9 -.086 L .964 .116 L 2.012 .451 L 1.601 .518 L 1.214 .266 L 1.04 .115 L .731 .032 L 1.619 .164 L .888 .232 L .429 .149 L .254 -.034 L -.452 -.601 L -.357 -.2 L -.345 -.15 L -.064 -.419 L 1.344 -.423 L .745 .066 L .578 -.068 L .15 -.102 L 1.736 .148 L .771 -.001 L 1.125 -.373 L .309 -.186 L .442 0 L .656 -.221 L .759 -.069 L .866 -.086 L .734 -.086 L .469 -.239 L .392 -.188 L .771 -.104 L 1.045 -.002 L .872 .123 L -.445 .404 L -.352 .119 L -1.101 .137 L -1.092 .373 L -1.244 .171 L -1.22 .271 L -.699 .522 L -1.767 .255 L -.681 .487 L .846 .266 L 1.441 .748 L -.219 -.55 L .168 -.351 L 1.196 -.253 L .39 -.235 L .726 -.421 L 1.727 -.053 L .96 -.372 L 1.158 -.389 L 2.066 -.173 L .643 -.338 L .921 -.272 L .821 -.189 L .476 -.239 L -.178 -.272 L -.702 -.392 L -.655 -.444 L .899 .101 L .764 .272 L .701 .306 L .412 .324 L .376 .476 L .449 .523 L .393 .235 L 1.246 .486 L .186 .067 L 1.154 .216 L .394 -.018 L .199 -.151 L -.511 -.639 L .07 -.27 L .548 -.035 L .334 -.136 L .627 -.103 L .383 .354 L .059 .421 L -.205 .287 L .833 .133 L .938 -.069 L 1.136 -.457 L .536 -.49 L .479 -.069 L 1.131 .015 L 1.536 .267 L 1.745 .435 L 1.396 .334 L 2.095 .349 L 1.024 .216 L .619 .066 L 1.572 .282 L 1.121 .065 L 1.144 .148 L 1.096 .032 L 1.4 -.086 L .899 .099 L 1.008 .282 L .982 .349 L .434 .249 L .191 .333 L -.524 .134 L -.935 -.032 L -.566 .051 L -.849 .135 L -.354 .714 L 3.323 .358 L 1.726 .079 L 1.749 .014 L .868 -.068 L .738 -.051 L .94 -.167 L .895 -.118 L .938 -.101 L .886 .032 L 1.432 .477 L 1.452 .179 L .42 .115 L 1.225 .443 L -.013 .312 L -.504 .083 L -.35 .247 L .305 .147 L 1.823 .979 L -.162 -.392 L -.024 -.312 L .456 -.05 L .617 -.132 L -.062 -.181 L -.972 -.656 L -.128 -.198 L -.145 -.445 L .121 -.745 L .35 -.034 L 1.944 -.136 L .928 -.151 L .207 -.299 L .459 -.217 L .613 -.035 L 1.098 .281 L 1.528 .279 L .968 .064 L .969 -.102 L .612 .414 L .248 .082 L .962 .213 L 1.211 .13 L .678 .081 L 1.146 -.002 L .879 -.2 L 1.755 -.02 L 1.876 .029 L 1.07 -.052 L 1.18 -.267 L .959 .478 L .95 .296 L .522 -.018 L .243 -.314 L -.017 -.513 L -.666 -.231 L -.732 -.131 L -1.377 -.064 L .089 -.449 L 1.193 -.085 L .575 .065 L .804 .214 L .871 .198 L .858 .048 L .498 .198 L .088 .183 L -.095 .132 L -.287 .86 L .179 .51 L .304 .164 L .177 .065 L .792 .097 L .951 .311 L -.071 -.559 L -.466 -.989 L .137 -.48 L .258 -.266 L .712 .015 L .811 -.035 L 1.229 -.85 L .492 -.051 L .479 -.001 L .517 -.151 L .033 -.133 L -.15 -.367 L -.375 -.35 L -.307 .001 L -.81 .185 L -.988 -.082 L .535 -.52 L .806 -.069 L .435 -.168 L .572 -.001 L .739 .4 L .464 .167 L .065 -.268 L -.081 -.956 L -.744 .069 L -.897 -.032 L -.68 -.116 L -.859 -.318 L -.725 .085 L -1.245 -.183 L -.861 -.234 L -.956 -.218 L -.657 -.338 L -.092 -.136 L -.022 -.324 L .33 -.137 L .842 -.463 L -.486 -.221 L -1.188 -.375 L .09 -.207 L .58 -.604 L .593 -.294 L .387 -.035 L 1.032 .257 L .139 -.035 L .173 -.346 L -.709 -.362 L -.201 -.277 L .23 -.035 L .551 -.331 L .367 -.035 L 1.841 -.021 L .559 .086 L .728 .189 L 1.26 .449 L .432 .328 L .195 .38 L -.246 .603 L 1.261 .53 L .809 .495 L 1.134 .493 L -.377 .341 L -.985 .036 L -.474 .273 L -.416 .557 L .471 .067 L 1.071 .233 L .805 .049 L .387 -.136 L .597 -.001 L 1.477 .351 L -.335 .353 L -.563 .219 L .092 .151 L .796 .467 L .358 .601 L .025 .833 L .303 -.063 L .021 -.004 L .411 -.067 L .608 -.367 L .655 -1.137 L .668 -.42 L .523 .016 L .731 .284 L 1.064 .55 L .473 .383 L .209 .45 L -.159 .433 L -.336 .034 L -.796 -.098 L -.202 .299 L -.043 .415 L .35 .396 L 1.146 .659 L .61 .493 L .463 .279 L .595 -.166 L .896 -.167 L .369 -.132 L .208 -.66 L .764 -.398 L .599 -.416 L .249 -.664 L .363 -.75 L .237 -.184 L 1.394 .081 L .329 -.067 L .134 -.518 L -.985 -.333 L -.918 -.35 L .088 -.891 L .595 -.271 L .787 .032 L .42 -.068 L .571 .016 L 3.459 .616 L 1.325 .669 L .506 -.001 L .553 -.068 L .454 .201 L .244 1.036 L -.474 .268 L -.431 .101 L -.243 -.05 L -.718 -.532 L -.263 0 L -.799 .269 L .123 .316 L .309 .515 L .699 .729 L .855 .528 L 1.108 .576 L .024 .313 L -.478 .642 L -.439 .181 L -1.407 .772 L -.674 .083 L -1.123 .509 L -.763 -.276 L -1.654 -.962 L -.586 -.262 L -.497 -.048 L -.684 .281 L 1.364 .521 L 1.483 .896 L -.708 .067 L -.691 -.081 L -1.288 .084 L -.375 -.129 L -.596 -.62 L -1.147 -.014 L -1.857 .118 L -.253 .229 L .614 .244 L 1.311 .421 L -.159 .195 L -.611 .327 L -2.045 1.106 L -.657 .179 L -.527 .001 L -.859 -.241 L -.816 -.484 L -.225 -.081 L -1.189 -.225 L -.736 -.259 L -.598 -.112 L -.947 .014 L -.289 .004 L -1.214 .174 L 1.503 .278 L 1.136 .21 L 1.751 .774 L 1.629 .433 L 1.233 .126 L 1.02 .031 L -.618 1.091 L -1.237 .705 L -.856 .432 L -.728 .161 L -.829 .049 L -.928 -.126 L -1.062 -.38 L -.048 .351 L -.025 .287 L .321 .572 L -.02 .159 L -.741 .031 L -.058 .002 L -1.365 -.108 L -1.649 -.41 L -.884 -.078 L -2.962 -.322 L 2.146 .864 L 1.576 .156 L 1.367 .267 L .562 .205 L .33 .268 L -.011 .19 L -.642 .333 L -1.106 .207 L -1.429 -.076 L -.511 -.062 L -.367 .269 L 1.254 .423 L -.469 .426 L -1.06 .316 L -1.454 .662 L -.421 .252 L .218 .704 L -.313 .235 L -.909 .205 L -.31 .282 L -.529 .64 L -.276 .296 L -.241 .669 L -.274 .543 L -.323 .666 L .056 .416 L -.161 .554 L .123 .875 L .136 .536 L .598 .366 L .25 .015 L .257 .091 L .664 .014 L 1.164 -.094 L .276 .045 L .367 .29 L .413 .763 L .813 1.157 L .22 .668 L -.132 .91 L .673 .014 L 1.874 -.428 L 1.261 -.033 L .723 .074 L .535 .157 L 1.062 .311 L 2.129 .435 L 1.896 .903 L .993 .933 L 3.5 .67 L .644 .225 L .982 .403 L .986 .253 L .553 .104 L .702 -.091 L .453 .044 L .828 -.077 L 1.245 .163 L 1.407 .207 L .401 .194 L -.297 .702 L -.142 .85 L .154 .283 L .307 .342 L .07 .416 L -.115 1.025 L -.309 .593 L .022 .208 L .604 .266 L .481 .339 L .264 .354 L .046 .488 L -.076 .354 L .97 .116 L .963 .47 L .676 .588 L .392 .588 L .078 .162 L .64 .014 L .726 .41 L .907 .601 L -.349 -.66 L -.22 -.279 L .134 -.338 L .573 -.414 L .365 .176 L .381 .456 L .262 .353 L .165 -.354 L .107 -.545 L -.215 -.456 L .541 -.532 L .139 -.546 L -.183 -.517 L -.337 -.458 L -.261 -.754 L -.004 -.548 L -.205 -.593 L -.218 -.43 L .615 -.016 L -.097 -.476 L -.296 -.252 L -.657 -.163 L -.375 -.282 L -.326 -.923 L 1.252 -.271 L .872 -.241 L .625 -.271 L 1.758 -.949 L .629 -.302 L 1.043 -.935 L .434 -.544 L .237 -.665 L .054 -.529 L -.257 -1.045 L -.246 -.531 L -.239 -.319 L -.938 -.729 L -.467 -.274 L -1.105 -.532 L -.363 -.122 L -.453 -.274 L -.151 -.046 L -.293 -.351 L .08 -.107 L .868 -.522 L .553 -.875 L .293 -.416 L .25 -.092 L .447 -.017 L .295 -.277 L -.106 -.523 L -.18 -.355 L -.316 -.402 L -.048 -.202 L .258 -.357 L .005 -.264 L -1.751 -.105 L 1.084 -.92 L .503 -.704 L .007 -.125 L -.316 -.203 L -.609 -.265 L -.244 -.266 L -.043 -.533 L .305 -.425 L .554 -.315 L .57 -.19 L .827 .062 L 1.781 .374 L 1.097 .234 L .753 .077 L 1.44 .013 L 1.08 -.144 L .86 -.222 L .21 -.047 L .179 -.063 L .589 .078 L .991 .407 L .254 .125 L .754 .454 L .918 .375 L .796 .437 L -.294 .391 L .406 .233 L 1.698 .496 L 1.958 .354 L .725 -.033 L .368 -.203 L .339 .295 L -.013 .404 L -.577 .343 L -.123 .45 L .438 1.327 L .136 .722 L .23 .414 L -.049 .353 L -.248 .169 L -.445 -.014 L -.347 -.015 L -.138 .674 L .375 .274 L 1.137 -.415 L .366 -.047 L .781 -.047 L .286 .015 L .677 .32 L .378 .351 L .004 .259 L -.081 .123 L .277 .32 L .516 -.184 L .306 -.046 L 1.173 -.079 L .636 -.184 L .436 -.383 L .333 -.551 L .326 .015 L .194 .122 L .693 .717 L .042 -.062 L .108 -.764 L .317 -.583 L .475 -.262 L .539 -.385 L -.651 -.505 L .008 -.308 L .272 -.139 L .98 -.094 L .193 -.139 L .512 -.665 L .667 .37 L .607 .599 L .785 .506 L .596 .797 L 1.045 .764 L .264 .352 L -.344 .291 L .095 .335 L .573 -.031 L .365 .777 L .182 .183 L .324 .121 L .743 .136 L .281 .258 L .133 .38 L -.379 .092 L -.416 .168 L .411 .318 L .397 .227 L .77 .196 L .279 .227 L .034 .424 L -.056 .076 L -.409 .106 L -.676 -.029 L -.745 -.12 L -.316 .061 L .091 .166 L .273 .181 L .189 .241 L .333 .513 L .411 .226 L .634 .029 L .462 .18 L .838 .496 L .899 .435 L .246 .33 L -.035 .195 L -.447 .781 L .508 .059 L .663 -.166 L .786 -.077 L .79 .164 L .574 .194 L 1.162 .49 L .981 .132 L 1.517 .295 L -.184 .253 L -.718 .21 L -.736 .21 L -.663 .046 L -.834 .24 L -.583 .402 L -.65 .225 L -1.032 .061 L -.286 .075 L -.324 .268 L .029 .371 L -.271 .535 L 1.175 -.343 L .542 -.09 L .649 -.105 L 1.201 -.774 L 1.251 -.478 L 1.146 -.106 L .662 .237 L .35 .341 L -.398 .446 L .036 .119 L .307 .296 L .616 -.224 L .455 -.164 L .655 .192 L 1.051 .487 L .226 .251 L .022 .178 L -.299 .43 L -.05 .355 L -.406 .444 L 1.001 .929 L -.365 .37 L -.795 .282 L -1.078 .621 L -.662 .281 L -1.097 .046 L -.823 .119 L -1.548 .077 L -.433 .413 L -.916 .795 L -.686 .427 L -.612 .294 L -.938 .222 L -1.494 .172 L -1.845 .127 L -1.452 -.07 L -2.031 -.084 L -.355 .03 L -1.073 .075 L -1.058 -.012 L -1.873 -.099 L -.917 -.027 L -1.758 .106 L -.547 .206 L -.523 .294 L -.537 .585 L -.205 .554 L -.287 .335 L -.591 .19 L -1.07 .104 L -.537 .147 L -1.098 .555 L -.774 .54 L -.794 .612 L -.325 .363 L -.33 .233 L -.868 .843 L -.485 .566 L -.418 .276 L -.46 .58 L -.518 .968 L .749 -.737 L .375 -.131 L .688 -.479 L 1.059 -.944 L 1.097 -.785 L 2.028 -.948 L 1.245 -.395 L 1.797 -.512 L 1.065 -.235 L 1.03 -.235 L 1.473 -.148 L .922 .056 L .895 .289 L .509 .29 L .136 .189 L .144 .464 L -.125 .218 L -.326 .219 L -1.059 .292 L -.753 .452 L -.581 .044 L -.845 -.23 L -.726 .045 L -.645 .19 L .279 .08 L 1.13 .229 L .17 .122 L .256 .444 L .074 .095 L 1.299 -.485 L -.028 .216 L .479 -.148 L .372 .162 L -.36 .229 L -.231 .256 L .112 .27 L -.163 .014 L -.074 .229 L -.91 .444 L 1.216 .013 L .077 .188 L -.187 .282 L .091 .43 L .118 -.081 L .239 .134 L -.063 .134 L .048 .202 L .351 .457 L .009 .147 L .824 .054 L .154 .094 L .04 -.067 L .727 .147 L -.315 .134 L -.373 -.013 L -.047 .134 L .525 .147 L .211 .241 L .569 -.081 L .135 .134 L .212 -.014 L .134 .174 L .418 -.04 L -.075 -.107 L .843 .067 L .241 .107 L -.207 .201 L .242 -.107 L .26 .027 L .245 -.013 L .696 -.362 L .303 -.081 L .104 .362 L .377 .161 L .538 -.121 L .488 .416 L -.405 .254 L .089 .107 L .825 .027 L .164 .174 L -.521 .121 L -.161 -.121 L -.3 .134 L .118 .094 L -.515 0 L -.23 -.04 L .109 .161 L -.45 .04 L .056 .107 L -.443 .014 L -.128 .147 L -.45 .04 L -.368 .253 L -.09 -.106 L -.706 .28 L -.046 .053 L -.529 .133 L -.119 -.267 L -.274 .106 L -.163 .267 L -.188 -.187 L -.068 .253 L -.218 .08 L -.094 .187 L -.513 0 L -.081 -.08 L -.169 -.053 L .032 -.347 L -.242 .36 L -.202 .12 L -.131 -.253 L -.354 .027 L .043 .24 L -.233 .04 L .312 .08 L .033 .213 L -.103 .12 L -.174 -.067 L -.768 .453 L .127 .16 L -.235 .12 L -.194 .053 L .015 .213 L -.161 -.12 L .083 .173 L -.217 .08 L -.14 -.107 L .096 .253 L -.222 .066 L -.146 -.08 L -.148 0 L -.064 .133 L -.156 -.106 L -.243 .227 L -.086 .292 L -.201 -.226 L -.344 .133 L -.154 -.187 L -.349 -.479 L -.138 .24 L -.419 -.866 L .456 -.773 L .284 -.16 L .035 -.12 L -.35 .12 L -.357 .267 L -.076 -.106 L .924 -.507 L .125 .146 L .195 -.093 L -.258 -.107 L 1.103 -.52 L 1.109 -.562 L .658 -.361 L .336 .094 L -.067 .428 L .179 -.054 L .258 .281 L -.044 -.201 L -.017 -.174 L .632 -.214 L .73 -.134 L .192 .067 L .202 -.081 L -.152 -.094 L -.653 -.053 L -.595 .053 L -.42 -.053 L -.804 -.014 L -.306 .027 L -.251 .081 L -.246 .094 L .033 -.214 L 1.128 -.563 L .054 -.201 L .252 -.08 L -.052 -.174 L -.523 .281 L .097 -.134 L -.502 -.51 L .309 .443 L -.36 .482 L -.328 .013 L -1.974 .817 L -.284 .08 L -.362 -.201 L -.227 -.067 L .23 .201 L -.788 .401 L -.219 -.174 L -1.019 -.054 L -.124 .147 L -.38 -.241 L h 186.193 47.833 m -.713 -.032 L -.922 -.181 L -.882 -.065 L -1.25 -.314 L -.973 -.182 L -.604 -.049 L -1.083 -.199 L .43 -.335 L 1.542 -.405 L .385 -.186 L .115 -.673 L .305 -.236 L .831 -.069 L .743 .184 L 1.436 .603 L 1.287 .4 L .074 .285 L .315 .284 L 1.094 .516 L -.288 .117 L -1.263 .486 L -.578 .051 L h 231.09 50.646 m -1.319 -.03 L -.449 -.147 L -.232 -.247 L -.173 -.478 L .3 -.43 L .708 -.664 L .662 -.267 L 1.359 -.168 L .911 .197 L .79 .314 L -.021 .464 L -.039 .727 L -.362 .265 L -1.025 .348 L -1.108 .117 L
+444.972 79.145 N .47 -.218 L .307 -.093 L -.294 -.024 L -.419 .061 L -.15 -.135 L -.125 .184 L -.108 -.012 L .066 -.491 L .177 -.218 L .41 .009 L 1.489 .062 L .417 .014 L .339 -.075 L .121 -.253 L -.175 -.288 L .246 -.05 L -.068 -.122 L -.068 -.123 L .353 -.096 L .12 -.034 L .051 .154 L .086 .052 L .24 0 L .223 .12 L .257 .069 L .514 .068 L .086 .103 L .223 -.051 L .445 0 L .257 0 L .223 -.017 L .086 .137 L .103 .103 L .188 .034 L .171 .069 L .018 .137 L .052 .12 L -.224 .12 L -.068 .154 L -.068 .206 L .018 .171 L .034 .137 L .029 .038 L -2.96 .101 L -2.246 -.115 L -.842 -.006 L h 717.633 81.109 m .42 .443 L .429 .62 L .183 .457 L .01 .767 L -.244 .442 L -.197 .78 L -.002 .764 L .29 .777 L .592 .849 L .65 1.446 L .899 1.614 L 1.115 1.679 L -1.26 -.677 L -.832 -.39 L -.99 -.056 L -.268 .088 L -.396 .204 L -.462 1.045 L -.266 1.101 L -.082 .579 L .277 .982 L .183 .216 L .659 .908 L .54 .201 L .463 .648 L -.314 1.246 L -.664 -1.258 L -.866 -.301 L -.224 .029 L -.415 .303 L -.311 .534 L -.643 .907 L -.422 -.5 L -.19 -.929 L .637 -1.146 L -.395 -.884 L .175 -.454 L .502 -.63 L -.131 -.723 L -.196 -.376 L -.27 -.55 L -.062 -.235 L .403 -.302 L .284 -.915 L .075 -.784 L .005 -1.326 L .15 -1.302 L -.09 -.732 L -.213 -.469 L -.83 -.85 L -.1 -.897 L .114 -.192 L .359 -.722 L .065 -.738 L -.336 -.457 L .172 -.237 L .374 -.03 L .62 -.031 L 1.023 -.534 L h 471.158 84.281 m -.002 -.142 L -.165 -.066 L -.082 -.115 L -.164 -.082 L .033 -.099 L -.033 -.23 L -.033 -.164 L .082 -.099 L -.147 -.131 L -.099 -.148 L .132 -.066 L 0 -.165 L -.296 -.164 L -.279 -.263 L -.017 -.164 L .099 -.131 L .131 -.165 L .362 -.017 L .328 .049 L .197 .132 L .51 .016 L .525 -.099 L .444 -.247 L .049 -.065 L .148 -.083 L .296 0 L .065 -.164 L -.033 -.131 L -.279 -.066 L -.296 -.148 L -.099 -.181 L .082 -.017 L .066 -.049 L .032 -.065 L -.263 -.066 L -.361 -.099 L -.378 -.066 L -.361 .066 L -.182 -.066 L .066 -.181 L .099 -.197 L -.066 -.148 L -.164 -.099 L -.279 -.082 L -.23 -.066 L -.443 -.213 L -.115 -.23 L -.164 -.263 L -.214 -.017 L -.017 -.099 L .066 -.131 L .099 -.115 L -.132 -.033 L -.181 .049 L -.082 -.115 L -.132 -.181 L -.345 -.049 L .049 -.147 L .033 -.165 L .099 -.049 L .115 -.082 L 0 -.083 L .114 0 L .066 -.131 L -.05 -.164 L -.147 -.099 L -.197 -.247 L .131 -.165 L .033 -.164 L 0 -.083 L .065 -.115 L -.049 -.115 L -.147 .033 L -.165 -.033 L -.147 -.099 L -.099 -.099 L -.279 -.099 L -.132 -.131 L -.542 -.115 L -.247 .049 L -.099 -.049 L -.131 -.049 L -.23 .083 L -.147 .099 L -.165 0 L -.279 .016 L -.214 .197 L -.197 0 L -.164 -.148 L -.065 -.148 L .017 -.099 L .164 -.099 L 0 -.115 L -.147 -.017 L -.296 -.065 L -.312 -.049 L -.361 .049 L -.214 .065 L -.197 .033 L -.082 -.148 L -.132 -.148 L -.312 -.033 L -.181 -.016 L -.197 .131 L -.229 -.066 L -.165 -.147 L .061 -.042 L .015 -.117 L .044 -.087 L -.088 -.233 L .015 -.189 L -.131 -.117 L .059 -.087 L -.16 -.043 L -.146 -.102 L -.029 -.16 L -.131 -.058 L -.116 -.102 L .043 -.073 L .059 -.087 L -.073 -.044 L -.087 -.014 L -.131 -.073 L -.146 .015 L -.204 .059 L .044 -.102 L .102 -.073 L .073 -.087 L -.029 -.117 L .072 -.131 L -.131 -.087 L .103 -.029 L .087 -.015 L .102 -.073 L .015 -.087 L .029 -.116 L .015 -.087 L -.204 -.058 L -.087 -.073 L -.204 -.087 L -.232 -.073 L 0 -.117 L .015 -.116 L -.37 .004 L -.081 -.106 L .116 -.058 L 461.402 72 l .029 -.117 L .131 0 L .087 -.116 L .059 -.102 L .16 -.058 L .262 -.043 L .175 -.073 L -.059 -.059 L -.175 -.043 L -.043 -.146 L -.015 -.087 L 0 -.073 L -.088 -.073 L -.203 -.087 L -.175 -.233 L 0 -.175 L .175 -.131 L -.029 -.16 L -.073 -.189 L -.131 -.437 L -.029 -.16 L .088 -.16 L .204 -.131 L .319 -.131 L .219 -.204 L .175 -.277 L .058 -.131 L .088 -.043 L .116 0 L .189 0 L .175 -.044 L .043 -.174 L -.16 -.131 L -.145 -.053 L -.089 -.13 L -.17 -.038 L .1 -.253 L .339 -.038 L .153 .165 L .229 .063 L .188 -.088 L -.094 -.139 L .301 -.154 L .485 .199 L .296 -.062 L .312 -.338 L .311 -.185 L .75 .106 L .781 .275 L .439 0 L .363 -.154 L -.386 -.399 L -.59 -.323 L -.393 -.03 L -1.204 .08 L -.616 -.091 L -.271 -.108 L -.299 -.309 L .258 -.434 L -.065 -.201 L -.199 .044 L .174 -.285 L 1.946 -1.145 L 1.983 -1.195 L 1.385 -.758 L .591 -.536 L .43 -.536 L .105 -.409 L -.161 -.346 L -.436 -.392 L -.703 -.265 L -1.357 -.499 L -.439 -.33 L .327 -.191 L .542 -.415 L .057 -.254 L -.151 -.253 L -1.286 -1.395 L -.37 -.509 L .029 -.37 L .187 -.403 L .44 -.535 L .196 -.356 L -.772 -1.195 L -1.402 -1.394 L .328 -.296 L 1.303 -.777 L .421 -.364 L -.543 -.392 L -.964 -.506 L -.872 -.194 L -.563 -.212 L -.116 -.529 L .258 -.465 L .024 -.283 L .689 -.303 L 1.013 -.672 L 1.023 -.49 L .77 -.121 L .824 -.021 L .514 -.204 L .404 -.288 L .617 -.051 L 1.002 -.254 L .643 -.237 L .01 .151 L .255 .386 L .358 .284 L .543 .2 L .919 .082 L .602 .1 L .078 .602 L .695 -.319 L .421 .049 L 1.083 .048 L .875 .015 L .522 .032 L 1.116 -.002 L 1.293 .281 L 2.728 .512 L .984 .364 L 1.595 .86 L .583 .214 L 1.48 .246 L 1.296 .212 L 2.018 .623 L .328 .279 L -.051 .444 L .147 .295 L .426 .294 L .104 .294 L -.24 .344 L -.69 .491 L -1.092 .54 L -.816 .262 L -1.75 .36 L -.907 .083 L -1.631 -.013 L -1.391 -.192 L -2.038 -.175 L -1.63 -.192 L -1.342 -.339 L -2.256 -.485 L -1.114 -.112 L -.476 -.048 L -.621 -.473 L -.371 -.163 L -.771 -.13 L -.943 .117 L .307 .163 L .149 .065 L .73 .538 L .482 .146 L 1.109 .601 L .832 .291 L .921 .161 L .634 .242 L .405 .453 L -.002 .405 L -.276 .291 L -.684 .195 L .086 .113 L .208 .531 L .771 .943 L .093 .494 L .155 .207 L .438 .174 L 1.203 .078 L .872 .125 L .499 .619 L .401 .095 L 1.26 .077 L .575 .126 L .364 .079 L .402 -.128 L .785 -.097 L .243 -.302 L -.001 -.318 L -.387 -.397 L -.471 -.079 L -.455 .096 L -.447 -.031 L -.589 -.206 L -.952 -.795 L .701 -.674 L .484 -.001 L 1.116 .479 L 1.441 .333 L 2.09 .427 L .952 .078 L .834 -.146 L .723 .174 L .261 -.224 L .05 -.415 L -.214 -.239 L -.858 -.656 L -.348 -.628 L .285 -.323 L .19 -.049 L 1.432 -.423 L 1.495 -.359 L .599 -.244 L 1.133 -.717 L .172 -.049 L .462 .064 L 1.829 .29 L 1.41 .533 L .341 -.001 L .052 -.065 L .154 -.503 L .581 -.767 L -.048 -.653 L -.317 -.408 L -.847 -.163 L -.3 -.229 L 1.139 -1.005 L .101 -.247 L -.205 -.594 L -.771 -.512 L .069 -.315 L .353 -.051 L 1.458 .23 L 2.025 -.12 L .631 .132 L .664 .611 L .616 .445 L .433 .461 L -1.045 .051 L -1.559 .085 L -.822 .215 L -.492 .51 L .191 .18 L .952 .293 L .732 .555 L .804 .194 L .723 .097 L 1.268 -.133 L 1.33 -.084 L .301 -.164 L .257 -.491 L .291 -.591 L .284 -.412 L 1.232 -.2 L 1.223 -.414 L .988 -.216 L 1.924 -.483 L 1.429 -.251 L 1.537 -.318 L .921 -.3 L .205 .464 L .278 .083 L .571 -.117 L .487 -.266 L .148 -.465 L .386 -.167 L .718 -.135 L .859 .065 L -.18 .399 L -.058 .597 L -.858 .084 L -.178 .149 L .002 .215 L .687 .197 L .507 -.083 L 1.169 -.167 L .436 -.001 L .161 .198 L .23 .049 L .278 -.133 L .264 -.216 L .29 -.431 L .464 -.183 L .861 -.118 L 1.049 -.068 L .768 .032 L 1.075 .23 L .755 -.018 L .36 -.083 L .963 -.467 L 1 -.285 L .803 -.052 L .952 .182 L .326 .166 L -.631 .45 L .129 .232 L .217 .099 L .632 .131 L .579 -.018 L .288 -.232 L .074 -.398 L .342 -.084 L .962 .065 L .543 -.184 L .395 -.316 L .115 -.417 L -1.37 -1.033 L .405 -.168 L .66 -.37 L .403 -.068 L .609 .016 L 2.171 .063 L 1.272 .199 L 1.241 .149 L 1.135 .199 L 2.111 .515 L 1.071 .098 L 1.712 .414 L 1.02 .248 L 1.305 .53 L 1.455 .611 L .864 .379 L .376 .049 L .229 -.1 L 1.145 -1.047 L .236 -.3 L -.927 .035 L -.4 -.049 L -.564 -.232 L -.365 -.433 L .027 -.652 L -.727 -.283 L -1.987 -.147 L -.19 -.268 L .064 -.168 L .305 -.303 L .693 -.255 L .236 -.153 L .085 -.187 L -.052 -.833 L -.251 -.238 L -1.135 -.066 L -.232 -.29 L .328 -.532 L .359 -.241 L .391 -.035 L 1.482 -.416 L 1.098 -.485 L .521 -.416 L .581 -.608 L .544 -1.22 L .637 -.421 L .374 .069 L 1.562 .155 L 1.613 -.125 L 1.605 -.091 L .695 .069 L 1.066 -.055 L .574 .122 L .309 .279 L -.018 .332 L -.423 .836 L -.348 .348 L -1.334 .833 L -.223 .345 L .752 .342 L .931 .667 L .277 .342 L .21 .818 L -.174 .222 L -.575 .375 L .254 1.179 L -.058 1.305 L .263 .583 L .45 .381 L 1.027 .264 L .19 .166 L -.001 .133 L -.485 .481 L -.417 .826 L -.333 .33 L -.784 .462 L -1.232 .625 L -.63 .198 L -.55 .263 L .321 .522 L -.433 .115 L 558 52.195 l -1.599 -.372 L -.731 -.08 L -.97 .034 L -.601 .115 L .195 .31 L .583 .276 L .738 .21 L 1.569 .208 L 1.133 .079 L .613 -.05 L 1.188 .144 L .922 -.034 L .472 -.358 L .303 -.358 L 1.352 -.328 L 1.166 -.492 L .268 -.278 L .386 -.606 L .818 -.313 L .864 -.626 L .064 -.362 L -.225 -.561 L -.609 -.545 L .244 -.548 L .237 -.1 L .677 -.151 L 1.38 -.152 L 1.757 -.003 L .74 .231 L .842 .463 L .151 .778 L -.34 1.023 L .302 .279 L .92 .212 L 1.298 .047 L .864 -.149 L .129 -.296 L -.514 -.18 L -.797 -.18 L -.571 .034 L -.457 -.098 L .068 -.379 L 1.03 -.382 L .065 -.249 L -.218 -.148 L -.166 -.331 L -.441 -.763 L -.511 -.266 L -.836 -.098 L -1.093 -.231 L -.801 -.116 L -1.288 -.165 L -.91 .186 L -.638 .101 L -1.297 -.181 L -.896 .019 L -.015 -.267 L -.564 -1.071 L .305 -.657 L .736 -.697 L .282 -.46 L -.134 -.221 L -1.092 -1.042 L -.949 -.514 L -.12 -.189 L .833 -.554 L 1.213 -.106 L .998 -.262 L .744 -.348 L .172 -.226 L .169 -.644 L -.13 -.663 L .23 .069 L .64 .051 L .466 .086 L .108 .471 L -.186 .54 L -.636 .608 L -.167 .554 L .14 .448 L .373 .274 L .485 .274 L 1.384 .134 L 1.574 .169 L 1.632 .083 L .819 .409 L .321 .017 L .799 -.036 L -.527 -.89 L -.521 -.274 L -1.893 -.1 L -.931 -.067 L -.576 -.154 L -.609 -.448 L .275 -.329 L 1.374 -.054 L .444 .172 L 1.145 .084 L .747 -.157 L -.09 -.728 L .408 -.088 L .84 -.105 L 1.278 -.02 L 1.067 .207 L 1.413 .379 L 1.088 .535 L 1.326 .343 L .547 .085 L 1.822 .014 L .727 -.174 L .138 .345 L -.781 .38 L -.696 .259 L -.248 .771 L -.129 .972 L .333 .136 L .68 -.785 L .387 -.292 L .285 .051 L .604 .528 L -.088 .749 L .743 -.205 L .681 -.273 L -.044 -.306 L -.191 -.119 L -.147 -.358 L -.748 -.821 L .188 -.223 L .686 -.759 L -.797 -.448 L -.772 -.258 L -.93 -.241 L -.257 -.382 L -.655 -.051 L -.979 -.242 L -1.34 -.207 L -.307 -.296 L .062 -.577 L -.096 -.386 L -.811 -.667 L .081 -.247 L .266 -.16 L .484 -.125 L 2.31 -.11 L 2.54 -.022 L 2.125 -.128 L 1.421 -.162 L .475 .317 L .382 .052 L .844 -.267 L 1.056 -.286 L 1.413 -.109 L .589 .194 L -.957 .338 L -.451 .407 L 1.737 -.233 L .521 -.107 L .955 -.374 L .27 -.284 L -.334 -.444 L -.326 -.177 L -.925 -.266 L -.365 -.303 L -.002 -.232 L .324 -.539 L 1.176 -.397 L .966 -.22 L 3.028 -.903 L .889 -.094 L .248 -.036 L .522 -.076 L 1.899 -.096 L 1.663 -.114 L 2.302 -.244 L 2.048 -.263 L 1.595 -.43 L .855 -.243 L 1.763 .034 L 1.065 -.002 L .383 .185 L -.351 .409 L 1.504 .108 L 1.018 -.039 L 1.261 -.188 L 1.345 -.225 L .95 -.039 L .982 .166 L .687 .073 L .693 -.206 L .12 -.335 L -.133 -.167 L .466 -.337 L .942 -.077 L .939 .036 L 1.243 -.377 L -.618 -.506 L .122 -.34 L 1.165 -.438 L 1.554 -.383 L 2.23 -.406 L 1.229 -.077 L .993 .056 L 1.486 -.003 L .86 .265 L .045 .266 L -1.528 .401 L -.66 .342 L .488 .15 L 1.83 -.117 L 1.588 .148 L 2.039 -.079 L .177 .113 L -.375 .283 L -1.187 .453 L -.296 .3 L 1.971 -.004 L .833 -.02 L .234 .093 L 1.052 -.545 L 1.366 -.002 L 1.771 -.097 L .631 .13 L 1.35 -.021 L 1.954 .165 L 1.4 .259 L 1.181 .427 L .52 .445 L .726 -.001 L .854 -.076 L .422 .5 L -1.354 .832 L .241 .128 L .896 .365 L -2.329 .859 L -1.035 .235 L -1.166 .11 L -3.404 1.061 L -3.018 .965 L -.793 .285 L -2.388 .375 L -2.35 .586 L -2.065 1.126 L .715 .017 L 1.081 -.247 L .922 -.458 L 1.663 -.161 L 1.231 -.02 L 2.101 -.268 L 1.879 -.286 L .879 -.107 L 1.004 -.284 L -.094 -.389 L -.492 -.123 L -.034 -.071 L .281 -.249 L .581 -.214 L .873 .211 L .603 .389 L .621 .052 L .593 .193 L .737 .052 L .853 -.055 L 1.155 -.268 L .499 .07 L .192 .3 L .009 .512 L .522 .404 L 1.422 -.778 L 1.66 -.021 L 1.506 -.145 L 2.354 .014 L 1.919 .155 L .854 .034 L 1.204 .033 L -.271 .74 L .354 .333 L 2.043 .154 L .848 .121 L .698 .069 L 1.035 .103 L 2.49 -.145 L 1.209 -.02 L 1.42 .348 L 1.405 -.932 L -.161 -.352 L -.038 -.229 L -.034 -.105 L 1.806 -.48 L .521 -.019 L .802 .104 L 1.148 .298 L .851 .281 L 1.711 .032 L 1.354 -.073 L 1.384 .033 L 1.323 .431 L .409 .181 L .058 .386 L -.52 .088 L -.268 .036 L -1.905 .406 L -2.438 .737 L -.233 .227 L .253 .069 L 1.033 .067 L .957 .016 L .659 .155 L .659 .293 L 1.014 .396 L .583 .172 L .78 .481 L 1.27 .805 L 1.814 .801 L 1.351 .305 L .612 -.018 L .795 -.394 L .843 -.72 L 1.093 -1.051 L .601 -.329 L .29 .017 L .236 .465 L .772 .308 L 1.346 .29 L 1.105 .135 L .848 -.087 L 1.973 -.468 L .778 -.07 L .813 .067 L 1.196 .239 L 1.921 .734 L .315 -.052 L .186 -.069 L .491 -.258 L .221 -.258 L -.137 -.051 L .013 -.189 L .726 -.312 L .509 -.018 L .5 .257 L .575 .188 L 1.302 .032 L .181 -.5 L -.194 -.466 L .15 -.363 L .093 -.521 L -1.131 .159 L -.643 .001 L -.179 -.104 L .442 -.296 L .318 -.087 L .986 -.089 L 1.021 -.02 L .832 -.141 L 1.566 -.648 L .254 0 L 1.76 .241 L 1.561 .137 L 2.036 .188 L .997 .068 L .654 .103 L 2.307 .065 L -1.732 .628 L .865 .051 L 1.011 -.089 L .335 .138 L -.305 .381 L -.926 .192 L -.846 .261 L -.177 .293 L .664 .033 L .52 -.122 L .916 -.14 L .978 -.33 L 1.62 -.799 L 2.766 .012 L 1.196 .067 L .903 .172 L .946 .137 L .205 .19 L .221 .104 L -2.247 .59 L .559 .137 L 1.674 .289 L 2.174 .202 L .946 .204 L .801 .375 L .367 .427 L .564 .357 L .522 .152 L 1.459 -.037 L 1.364 -.105 L 1.138 -.139 L 2.518 -.329 L 2.208 -.107 L 3.008 .131 L 1.515 .134 L .734 .118 L 1.168 .424 L .655 .169 L .525 .338 L .361 .39 L -.123 .491 L -.286 .521 L .509 .25 L .764 .065 L .835 .015 L .643 .083 L -.017 .685 L .419 .416 L .686 -.168 L .545 -.435 L 1.211 -.387 L .348 -.067 L .35 .049 L 1.696 -.02 L 1.521 -.288 L .736 .032 L .588 .434 L .707 .116 L 1.541 .014 L 2.176 .062 L .869 -.118 L 1.378 -.504 L .406 .2 L .93 .533 L .396 .216 L 1.095 .265 L .875 .332 L .282 .398 L .612 .148 L 1.556 -.136 L 1.653 -.319 L .16 -.25 L -.248 -.333 L -.805 -.833 L -.833 -.115 L .4 -.336 L .479 -.571 L 1.89 .098 L 1.214 .082 L 1.135 .065 L 1.221 .166 L .222 .318 L 1.396 -.153 L 2.084 -.054 L 2.304 .013 L 1.292 .148 L .786 .199 L 1.185 .199 L 1.391 .098 L .751 .182 L 1.302 .332 L .747 .065 L .703 .182 L 1.145 .505 L 0 2.126 L 0 2.222 L 0 2.222 L 0 1.292 L 0 .157 L 0 .576 L 0 .219 L -1.083 .371 L -.651 .03 L -.645 .37 L -.56 .065 L -1.044 .065 L -.355 -.079 L -.928 -.052 L -.118 -.343 L -.271 -.211 L -.501 .027 L .241 -.185 L -.938 .324 L -.846 .02 L -.337 -.211 L -.478 -.079 L .424 .501 L -.569 .29 L .32 .103 L .942 .205 L .634 -.36 L .395 .041 L .335 .079 L 0 .477 L .248 .206 L .76 .269 L 1.059 -.228 L -.439 .322 L .741 -.243 L .065 .336 L .247 .206 L .187 .363 L .068 .189 L -.722 .522 L .593 -.064 L .349 .172 L .473 .503 L .501 .157 L .145 .251 L -.162 .456 L .792 -.111 L -.125 .393 L .023 .25 L -.43 .299 L -.691 .205 L -.635 -.046 L -.448 -.14 L -1.243 -.154 L -.889 -.077 L -.347 -.14 L .123 -.267 L -.493 -.046 L -.304 .032 L -.559 .55 L -.069 .11 L -3.06 .913 L -1.155 .174 L -.245 0 L -.43 .203 L -.219 .188 L -.719 .22 L -.991 .033 L -.308 .11 L -.48 .405 L -.462 .203 L -.946 .033 L -.854 .622 L -.24 .279 L -1.67 .452 L -.392 .449 L -1.229 .25 L -.406 .14 L -.151 .293 L .01 .292 L -.473 .292 L -.406 .016 L -.586 -.306 L -.183 -.262 L -.169 -.37 L -.67 -.308 L -1.074 -.044 L -1.021 .048 L -1.159 .172 L -1.301 .188 L -.523 .217 L -1.333 .756 L -.536 .277 L -.184 -.138 L .575 -1.293 L -.55 .094 L -.392 -.097 L -.811 .531 L -.67 .186 L -.401 .155 L -.114 .506 L -.66 .154 L -.317 -.168 L -.253 -.49 L -.483 -.261 L -1.024 .636 L .261 -.204 L -.557 .062 L -.283 .092 L -.628 .522 L -.141 .261 L .126 .229 L .344 .152 L -.932 .521 L -.182 .199 L .342 .167 L -.647 .352 L -.88 .55 L -.626 .503 L -.298 .35 L -.01 .531 L .421 .317 L .477 .09 L 1.382 -.048 L .562 .166 L .11 .167 L -.436 .394 L -.752 .455 L -.181 .302 L .224 .512 L .292 .452 L .786 .089 L .165 .391 L -.014 .616 L -.349 .361 L -.528 .061 L -.481 -.209 L -.017 -.21 L .388 -.527 L -.444 -.014 L -.454 .242 L -1.014 .843 L -.56 .675 L -.237 .599 L .321 .672 L .647 .311 L .154 .237 L -.294 .387 L -.688 .313 L -.956 .031 L -.664 -.088 L -.344 .001 L -.619 .179 L -.837 .476 L -.214 .149 L -.525 .504 L -.137 .341 L .111 .281 L .492 .398 L .038 .221 L -.056 .133 L -.679 .238 L -.604 .016 L 753.44 82.4 l -.727 .296 L -.065 .251 L .067 .28 L -.161 .854 L -.293 .412 L -.8 .78 L -1.53 .971 L -.854 .5 L -.285 .103 L -.295 -.614 L -.198 -.821 L -.25 -.69 L -.064 -.794 L -.351 -.75 L -.305 -.383 L -.214 -.915 L -.514 -1.36 L -.008 -.578 L -.154 -.563 L -.017 -.327 L -.105 -.193 L -.262 -.817 L -.026 -.792 L .116 -.808 L .271 -1.396 L .167 -.226 L 1.185 -.86 L .716 -.424 L .57 -.695 L .14 -.227 L -.085 -.318 L -.139 -.166 L 1.632 -.367 L 1.001 -.305 L .811 -.32 L 1.729 -.884 L .641 -.412 L .431 -.428 L .14 -.335 L 1.784 -.889 L .872 -.445 L 1.535 -.861 L .368 -.293 L .26 -.433 L 1.252 -.435 L 2.106 -.514 L .257 -.434 L .773 -.528 L .086 -.233 L -.568 -.216 L .814 -.904 L -.036 -.483 L .183 -.391 L .598 -.204 L 1.729 -.082 L .513 -.063 L -.485 -.328 L -2.065 -.215 L -.71 .095 L -1.062 .174 L -.777 .189 L .042 .328 L -.664 .923 L .125 .202 L -.04 .14 L -.662 .11 L -.479 .11 L -1.555 .718 L -1.979 1.042 L -1.169 .342 L -.249 -.062 L .156 -.325 L .352 -.465 L -.933 -.076 L -.16 -.263 L .252 -.451 L .442 -.467 L .207 -.328 L -.069 -.202 L -.339 -.031 L -1.136 .454 L -.496 .032 L -.277 -.358 L -.589 -.17 L -1.606 .144 L -1.312 .111 L -.956 .08 L -.606 .157 L -.894 .359 L -.093 .436 L .082 .186 L -1.262 .53 L -.408 .233 L .149 .495 L -1.294 .357 L -1.019 .434 L -.84 .479 L -.496 .461 L -.332 .46 L .004 .383 L .527 .213 L 1.269 .043 L .278 .275 L .062 .122 L -1.152 .139 L -1.028 .262 L -1.045 -.059 L -.698 -.136 L -.382 .031 L -.311 .107 L -.721 .398 L -.695 -.35 L -1.112 .383 L -.747 .139 L -.487 -.09 L -.284 -.137 L .108 -.29 L .748 -.124 L .868 -.124 L .164 -.046 L -.741 -.64 L 736.444 68 l -1.06 .017 L -.854 .155 L -.353 -.061 L -.868 -.458 L -.775 -.029 L -.745 .047 L -.704 .246 L .042 .398 L -.26 .229 L -.477 .215 L -.695 -.243 L -.408 -.122 L -1.26 .063 L -.784 .093 L -.651 -.075 L -.887 -.136 L -.563 .078 L -.067 .122 L -.147 .474 L -.677 .017 L -.311 -.137 L -.038 -.382 L -.203 -.259 L -1.241 .094 L -1.014 -.059 L -1.257 .033 L -1.158 .063 L -.836 -.029 L -.18 .016 L -1.085 .292 L -.964 .444 L -.74 .474 L -.536 .504 L -.789 .245 L -.904 .336 L -.194 .152 L -1.047 1.17 L -1.634 .684 L -.949 .471 L -1.157 .711 L -1.653 1.253 L -.828 .572 L -1.573 .873 L -.893 .376 L -1.889 .871 L -.632 .388 L -.203 .298 L .018 .357 L .428 .281 L .485 .043 L .918 .013 L 1.046 -.15 L .656 .043 L -.329 1.261 L .016 .415 L .303 .103 L .63 -.09 L .601 -.371 L .761 .117 L -.127 .148 L 705.293 81 l -.112 .222 L .373 .073 L 1.645 -.018 L .566 -.238 L .39 -.519 L .017 -.638 L .763 .014 L .647 -.001 L .726 .014 L .951 .265 L .658 .354 L .486 .591 L .847 .575 L .426 .176 L .506 .324 L -.07 .148 L -.581 .355 L .453 .221 L .13 .309 L -.336 .723 L .491 .117 L .215 .235 L -.291 .515 L -.348 .397 L -.532 .559 L -.724 1.364 L -.181 .688 L .057 .219 L .24 .701 L -.188 .917 L -.098 .741 L -.403 1.408 L -.146 .493 L -1.928 1.538 L -.371 .435 L -.217 .65 L -.587 .42 L -.741 .579 L -.241 .361 L -.574 .981 L -.587 .606 L -.941 .778 L -1.784 1.512 L -.464 .474 L -.235 .458 L -.323 .33 L -.758 .388 L -.618 .416 L -.574 .702 L -.431 .458 L -.875 .673 L -.955 .487 L -1.838 .475 L -.798 .244 L -.278 -.427 L -.519 -.085 L -.243 .043 L -.337 -.185 L -.337 -.513 L -.663 .272 L -.464 .101 L .424 -.586 L -.624 .173 L -.506 .486 L -.649 .543 L -1.326 1.001 L -.072 .049 L -.167 -.393 L -.298 -.439 L .111 -.058 L 1.252 -.462 L .511 -.43 L .156 -.329 L -.112 -.299 L .097 -.128 L .025 -.385 L .006 -.613 L -.062 -.656 L -.332 -.897 L .048 -.557 L 1.664 -.982 L .396 .041 L .685 .24 L .679 .225 L .547 .098 L .47 -.347 L .551 -.69 L .329 -.432 L .627 -1.08 L .538 -1.066 L .278 -.893 L .291 -.707 L .66 -.393 L .566 -.407 L -.017 -.504 L -.116 -.389 L -.064 -.259 L -.246 -.114 L -.902 .034 L -1.181 .208 L -1.357 .31 L -.953 .308 L -.604 .22 L -1.657 .052 L -.649 .018 L -.68 .033 L -.261 -.446 L -.44 -1.283 L -.297 -.866 L -.205 -.144 L -2.659 -.839 L -1.523 -.253 L -1.247 -.341 L -.507 -.2 L -.436 -.389 L -.927 -1.903 L -.94 -1.588 L -1.087 -2.384 L -.742 -.952 L -.763 -.467 L -.539 -.026 L -1.386 .125 L -.683 -.188 L -1.037 -.481 L -1.148 -.215 L -.917 .049 L -1.203 .109 L -.836 .123 L -1.854 .143 L -.602 .136 L -.478 .165 L -1.193 .787 L -.375 .282 L -.119 .25 L .822 -.078 L .558 .026 L .465 .306 L .107 .249 L -.167 .794 L -1.371 .608 L -.599 .545 L -.667 .779 L -.416 .543 L -.422 .426 L -.103 .249 L .062 .146 L .35 .348 L .056 .306 L -.123 .233 L -.779 .339 L -2.44 .752 L -.438 .089 L -.343 .016 L -1.126 -.574 L -.452 -.172 L -.046 -.018 L -.431 -.168 L -.622 .018 L -1.228 .297 L -.86 -.169 L -.34 -.129 L -.751 -.506 L -.74 -.156 L -.609 .047 L -.333 .002 L -1.165 .487 L -.755 .498 L -.447 .394 L -.604 .264 L -.526 .163 L -1.147 .136 L -.867 .106 L -.532 .075 L -1.018 .063 L -1.602 .11 L -.739 -.039 L -.984 -.082 L -.905 -.241 L -1.318 -.254 L -.812 -.373 L -1.132 -.313 L -.623 -.331 L -1.333 -.75 L -.769 -.229 L -1.423 -.022 L -1.172 -.037 L -.796 .077 L -2.48 .538 L -.673 -.113 L -1.643 -.531 L -.421 -.333 L -.408 -.451 L -.182 -.481 L -.004 -.541 L -.145 -.204 L -.767 -.143 L -.989 -.317 L -.702 -.231 L -1.748 -.328 L -1.036 -.185 L -1.028 -.098 L -.84 -.201 L -1.269 .96 L -.905 .839 L -.098 .293 L .409 .86 L .396 .304 L .116 .204 L -.163 .526 L -.744 .572 L -.31 .162 L -.755 .28 L -.562 .018 L -.576 -.186 L -.312 -.114 L -.875 -.068 L -.85 .004 L -.719 -.083 L -1.458 .008 L -.699 -.113 L -.393 -.406 L -.694 -.755 L -.831 -.083 L -1.842 -.122 L -.932 -.156 L -.953 -.097 L -.84 .18 L -1.512 .476 L -1.143 .341 L -.787 .398 L -.971 .692 L -.017 .012 L -.727 .381 L -.603 .148 L -1.387 .08 L -.599 .207 L -.799 .425 L -.154 .044 L -.244 .074 L -.569 .003 L -.228 .014 L -.064 -.145 L -.589 0 L -.294 -.478 L -.441 -.331 L .073 -.331 L -.625 .184 L -.478 .368 L -.993 -.074 L -.882 -.073 L -.662 -.699 L -.588 -.552 L -.956 -.147 L -.331 -.625 L -.772 -.588 L -.698 -.441 L -.993 .037 L -.771 .221 L -.993 .221 L -.992 0 L -.589 0 L -.184 -.184 L -.258 -.257 L -.478 0 L 0 -.368 L -.367 -.331 L -.92 0 L -.367 .515 L -.331 .257 L -.515 .257 L -1.104 -1.434 L -.846 -.882 L -1.69 -2.133 L -1.066 -1.029 L -1.287 -.772 L -.809 -.294 L -1.104 -.588 L .11 -.368 L .515 -.11 L 0 -.441 L -.809 .074 L -.515 .22 L -.772 .221 L -.956 .515 L -1.103 .257 L -.772 .441 L -.294 .294 L -.552 -.184 L -.441 -.037 L -.771 .073 L -.441 .11 L -.331 -.184 L .331 -.441 L .441 -.184 L .146 -.294 L -.367 0 L -.441 .221 L -.478 -.11 L -.405 -.294 L -.478 -.037 L -.184 .147 L -.147 .147 L -.367 -.221 L -.295 -.368 L -.294 -.11 L -.331 .221 L -.367 -.074 L -.368 .147 L -.44 -.11 L -.295 .147 L -.478 -.074 L -.184 -.257 L .33 -.037 L -.073 -.331 L .147 -.257 L .11 -.331 L -.515 -.22 L -.147 -.221 L .037 -.331 L -.368 -.404 L -.882 .037 L -.625 .11 L -.772 -.257 L -.515 -.11 L -.919 -.147 L -.735 .037 L -.11 .221 L -.589 .184 L -.515 .037 L 0 .294 L -.367 .368 L -.625 .074 L -1.876 .073 L -2.021 .405 L -1.177 .037 L -.625 .331 L -.221 .331 L -.331 -.073 L -.588 .073 L -1.545 .11 L -.735 .11 L -1.029 .037 L -1.396 .405 L -.368 .184 L -.772 0 L -.515 0 L 537.304 80 l -.33 .074 L -.185 .515 L .074 .441 L .294 .221 L -.294 .074 L 0 .331 L 1.065 .11 L .956 .294 L -.11 .257 L -.515 .073 L -1.103 -.147 L -.698 .184 L -.662 .515 L .146 .257 L .441 .478 L -.331 .294 L -.588 .147 L -.735 .368 L -.467 .067 L .164 .274 L .239 0 L .377 .137 L -.068 .171 L .377 .137 L .651 .069 L .274 .308 L 1.165 .171 L .24 .24 L -.138 .686 L -.137 .309 L -1.577 .411 L -.959 -.034 L -.343 -.343 L -.24 0 L -.171 .309 L -.651 .343 L -.411 -.171 L -.754 -.137 L -.926 -.309 L -.274 -.548 L -.754 -.103 L -1.062 .103 L -.137 .412 L -.617 .068 L -.651 -.411 L -.65 -.035 L -.823 -.068 L -.514 .377 L -.377 .343 L -.274 .274 L -.686 .171 L -.411 -.24 L -.686 -.137 L -.582 -.548 L -.72 -.068 L .034 .24 L .205 .48 L -.239 .274 L -.274 -.137 L -.068 -.583 L -.411 -.274 L -.789 -.343 L -.582 -.206 L 0 -.343 L -.96 -.171 L -.617 .069 L -.788 -.035 L -.411 -.514 L -.411 -.069 L -.617 .24 L -.273 .137 L -.651 .137 L -.309 -.274 L -.479 0 L -.651 -.069 L -.515 .309 L -.548 .343 L -.788 .377 L -.549 .068 L -.514 .171 L -.309 .309 L -.172 .24 L 509.58 87.5 l -.479 .206 L .068 .445 L .171 .411 L -.068 .446 L -.411 .24 L -.651 0 L -.514 -.411 L -.48 -.548 L -.514 -.24 L -.411 .069 L -.103 .308 L -.343 .549 L -.823 .137 L -.205 1.303 L .343 .171 L .239 .274 L -.239 .206 L -.446 .274 L -.65 1.165 L 1.37 .343 L .138 .377 L -.068 .309 L .514 .514 L .103 -.343 L .583 .206 L .343 -.034 L .514 .034 L .515 .445 L .479 .206 L .343 .514 L .96 1.131 L -.138 .103 L -.445 -.103 L -.309 -.103 L -.343 .103 L .068 .308 L .857 .377 L .616 .19 L -.167 .2 L -.399 .28 L -.38 .12 L -.12 0 L 0 .14 L 0 .22 L -.14 .08 L -.3 -.2 L -.34 .2 L -.399 .26 L -.64 .12 L -.319 .08 L -.26 -.08 L -.181 -.08 L -.1 .04 L -.06 .16 L .12 .26 L -.221 .2 L -.18 .3 L -.18 .319 L -.319 .42 L -.18 .28 L -.32 .16 L -.34 .24 L -.14 .32 L .08 .2 L .34 .2 L .319 .04 L .18 .3 L .24 .04 L .239 .34 L .28 .419 L -.06 .38 L -.101 .2 L .061 .16 L .1 -.2 L .1 -.34 L .12 -.22 L .16 -.02 L -.141 .439 L -.22 .36 L .061 .4 L .12 .1 L -.16 .2 L -.04 .4 L .38 .24 L .119 .1 L .12 .52 L .28 .06 L .359 .42 L .2 .28 L .439 .419 L .18 .36 L .359 .06 L .047 .146 L -.292 .449 L -.496 .284 L -.283 .094 L -.308 .331 L -.165 .213 L -.379 .047 L -.449 -.142 L -.591 -.094 L 0 -.308 L -.283 -.284 L -.118 0 L -.095 -.118 L -.473 -.284 L -.142 -.189 L 504.5 H -.213 .047 L 504.264 107 l -.229 .023 L -.126 -.189 L -.236 -.071 L -.236 .023 L -.284 -.047 L -.26 -.094 L -.284 -.166 L -.354 -.284 L .118 -.307 L .118 -.189 L -.118 -.142 L -.354 -.024 L -.378 0 L -.213 -.166 L -.189 -.118 L -.212 0 L -.143 -.094 L -.236 -.118 L -.118 0 L -.095 .071 L -.142 .166 L -.188 -.142 L -.261 -.071 L -.354 0 L -.213 .071 L -.094 .095 L -.166 .166 L -.283 0 L -.261 .118 L -.212 -.071 L -.261 -.118 L .048 -.118 L .142 -.118 L -.308 -.119 L -.236 -.118 L -.354 -.071 L -.52 -.118 L -.284 -.213 L -.095 -.142 L -.354 -.166 L -.283 .047 L -.189 0 L -.401 -.166 L -.544 0 L -.426 .095 L -.307 0 L -.355 -.047 L -.307 -.071 L -.261 -.095 L -.212 -.166 L -.213 0 L -.331 0 L -.189 -.047 L -.188 -.094 L -.284 -.142 L -.283 -.095 L -.449 -.023 L -.402 -.023 L -.07 -.047 L -.284 -.047 L -.26 .166 L -.112 .309 L -1.421 -1.012 L -1.188 -.842 L -.817 -.385 L -.62 -.084 L -.373 -.157 L -.509 -.5 L -.236 -.057 L -.338 .159 L -.329 -.042 L -.347 -.515 L -.795 -.674 L .595 -.188 L .519 -.001 L .445 -.073 L .1 -.519 L .359 -.476 L 485 97.85 l .526 -.03 L .516 -.131 L -.432 -.532 L -.654 -.273 L -.474 -.36 L .243 -.116 L .367 -.03 L .82 -.117 L .715 -.348 L 1.244 -.436 L -.196 -.375 L -.076 -.058 L -.799 .088 L -1.312 .175 L .012 -.126 L .021 -.112 L 0 -.224 L .09 -.514 L .224 -.089 L .357 -.022 L .38 -.134 L .223 -.179 L .022 -.179 L .269 0 L .736 -.045 L .805 .157 L .335 -.134 L 0 -.156 L .156 -.291 L .156 -.491 L -.066 -.067 L -.09 -.201 L -.045 -.179 L -.044 -.134 L -.179 -.067 L -.156 -.067 L .044 -.224 L .045 -.179 L .246 0 L .312 0 L .134 -.156 L -.29 -.044 L -.269 -.089 L -.223 -.246 L .312 -.044 L .357 -.201 L .156 -.067 L .201 -.089 L .045 -.224 L -.09 -.179 L -.111 -.089 L .022 -.179 L .089 -.111 L -.156 -.089 L -.201 .089 L -.134 .044 L -.269 0 L -.357 -.156 L -.111 -.224 L -.469 -.022 L -.291 -.089 L -.201 -.179 L -.357 .067 L -.268 -.112 L -.469 -.112 L -.425 -.067 L -.29 -.268 L -.224 .067 L -.045 .157 L -.29 .134 L -.224 -.112 L -.179 -.179 L -.402 -.044 L -.022 -.134 L -.201 -.179 L 0 -.134 L -.268 -.179 L -.111 -.156 L -.357 .067 L -.536 .134 L -.269 0 L -.312 .112 L -.425 .112 L -.179 -.156 L -.179 -.022 L -.201 .067 L -.469 -.291 L -.268 -.067 L -.47 .067 L -.268 .179 L -.291 -.29 L -.179 -.089 L -.089 -.224 L .089 -.179 L -.089 -.246 L -.224 -.268 L 0 -.357 L -.402 0 L 0 -.312 L -.38 -.022 L -.514 .067 L -.156 -.112 L -.647 0 L -.269 -.29 L -.111 -.357 L -.134 -.291 L .312 -.134 L .312 .022 L 0 -.268 L -.312 -.156 L -.246 -.156 L -.134 -.201 L -.246 -.335 L -.312 -.134 L -.201 -.268 L -.514 .134 L -.692 -.112 L -.67 .223 L -.536 .022 L -.536 -.246 L -.134 .268 L -.179 .291 L -.313 .156 L -.469 -.022 L -.357 -.089 L h 527.156 37.071 m -.59 -.14 L -.955 -.21 L -1.128 -.139 L -.739 .054 L -.959 .037 L -.732 .143 L -.411 -.105 L -1.025 -.476 L .737 -.303 L 1.21 -.429 L .654 -.09 L 1.549 -.002 L .135 -.357 L -.728 -.338 L .351 -.269 L .419 -.144 L .676 -.02 L .852 -.181 L -.25 -.161 L -.526 -.233 L 1.539 -.895 L .513 -.129 L .226 .073 L .892 .017 L .981 -.535 L .863 -.333 L 1.02 -.261 L .975 -.15 L 1.305 -.113 L 1.274 .053 L .738 -.15 L .786 -.615 L .273 .018 L .873 -.132 L 1.722 .276 L .471 -.038 L 1.93 -.321 L .958 -.039 L 2.184 -.247 L 1.864 -.116 L .771 -.564 L .376 -.152 L 2.511 -.137 L 1.965 -.079 L .471 .131 L .442 .225 L .154 .357 L -.812 .47 L -.541 .169 L -1.346 .319 L -2.609 .433 L -4.329 .49 L -2.187 .281 L -2.122 .299 L -1.842 .316 L -2.102 .242 L -.941 .203 L -.288 .274 L .644 .216 L -.422 .217 L -.943 .361 L -.527 .037 L -2.047 -.068 L -.432 .18 L -.119 .233 L .621 .249 L -.331 .233 L -1.206 .448 L -.402 -.142 L -.752 -.087 L -.592 .304 L .877 .283 L .052 .319 L -1.094 .657 L -.822 .284 L h 517.491 38.443 m 1.041 -.37 L .512 -.299 L .428 -.212 L 1.426 -.021 L 1.316 -.249 L .987 -.02 L 1.412 -.091 L .551 .069 L .988 .227 L -.063 .176 L -.463 .528 L -.302 .158 L -.697 .071 L -.443 .228 L -.233 .385 L .006 .818 L .445 1.039 L .957 .826 L .505 .308 L .775 .307 L 1.162 .392 L -.028 .282 L -2.62 -.089 L -.844 .053 L -.917 .326 L -.596 .086 L -.676 -.494 L -.382 -.034 L -1.091 .088 L -.673 -.102 L -.031 -.359 L 1.347 -.362 L .073 -.24 L -.102 -.017 L -.944 -.273 L -1.442 -.411 L -1.519 -.17 L -.33 .156 L -.624 .122 L -.681 -.033 L -.625 -.396 L -.114 -.415 L .229 -.312 L .39 -.209 L .344 -.036 L .318 .104 L .65 .016 L .518 -.192 L 1.121 -.768 L .243 -.35 L -.322 0 L -.981 -.243 L h .125 56.089 m 0 -.562 L 0 -.576 L 0 -.157 L 0 -1.292 L 51.28 V 0 -2.222 L 0 -2.12 L 2.917 .61 L 1.693 .598 L .933 .181 L 3.819 .99 L 1.059 .395 L .204 .149 L .074 .214 L .445 .429 L .406 .789 L -.209 .428 L 1.034 .8 L .982 .26 L -.02 -.18 L .297 -.148 L -.051 -.196 L -.638 -.392 L .016 -.542 L .077 -.296 L 1.026 -.183 L .984 .278 L .573 .098 L .767 .064 L 1.003 -.117 L .933 .13 L .93 .425 L .694 .359 L 1.676 .684 L .32 .081 L .584 .179 L .43 .211 L .09 .13 L -.343 .033 L -1.026 -.096 L -.364 .034 L -.808 .798 L -1.539 -.468 L .138 .178 L .438 .227 L .465 .324 L -1.177 -.111 L -1.008 .05 L -1.202 -.387 L -.708 -.096 L .135 .129 L .289 .259 L .084 0 L .561 .259 L .205 .307 L .131 .453 L -.056 .339 L -.814 .05 L -.805 .065 L -.189 .161 L .5 .865 L -.115 .272 L -1.04 -.078 L -.397 .081 L -1.193 -.174 L -.264 -.352 L -1.795 -.51 L -.253 .273 L 9.85 55.79 l -.276 -.161 L -.265 -.403 L .174 -.178 L -.321 -.42 L -.87 -.339 L -.565 -.063 L -.762 .034 L -.858 .229 L -1.343 .084 L 3.57 54.443 l .059 -.325 L -.155 -.325 L -.618 -.487 L 1.958 53.43 l -.45 .409 L -.655 1.228 L -.166 .715 L -.562 .307 L
+449.401 22.792 N -1.596 -.014 L -2.019 -.016 L -1.739 .063 L -.764 .061 L -1.972 -.316 L .812 -.456 L .826 -.172 L .735 -.453 L 1.148 -.588 L 1.354 .691 L .998 .105 L 1.116 -.088 L .9 .084 L .704 .341 L .865 -.323 L .53 -.454 L .868 -.306 L -.311 .823 L .086 .32 L .851 -.001 L .991 -.495 L 1.061 -.261 L .803 .128 L .559 .472 L .705 .041 L 2.007 -.111 L 1.543 .189 L .551 .376 L -.507 .201 L -2.004 .622 L -1.623 .38 L -1.069 .062 L -.976 .14 L -1.324 .366 L -6.271 -.097 L -.033 -.377 L 1.492 -.26 L .702 -.677 L h 430.027 22.752 m .068 .697 L .252 .119 L 1.694 .155 L .221 -.377 L .13 -.418 L .573 -.141 L .523 -.041 L .949 .477 L 1.192 .771 L .798 .235 L .568 -.218 L -.222 -.296 L -.46 -.356 L -.327 -.477 L -.04 -.22 L .91 -.407 L 1.001 .103 L .485 .18 L 1.278 .018 L .586 .179 L -.08 .419 L -.169 .298 L .104 .159 L .549 .118 L .743 -.338 L .44 -.1 L .662 .396 L .678 .335 L .785 .156 L .948 .117 L 1.672 .429 L .498 .234 L -1.305 .277 L -1.981 .218 L -.696 .293 L -.731 1.144 L -.885 .885 L -1.243 .117 L -.766 .535 L -.455 .589 L -.029 .378 L -.786 .209 L -.556 -.018 L -1.593 -.355 L -1.883 -.507 L -1.365 -.568 L .651 -.364 L 1.565 -.041 L 1.788 -.137 L .944 -.386 L -.652 -.249 L -1.822 .139 L -1.566 .118 L -1.694 .042 L -.502 -.519 L .959 -.06 L 1.371 -.215 L 2.149 -.314 L 1.547 -.45 L -2.525 -.405 L -.881 -.292 L -.589 .138 L -1.036 .646 L -1.069 .293 L -.562 .059 L -1.236 -.172 L -.338 -.174 L -1.05 -.368 L -.807 -.233 L -.698 -.41 L 1.698 -.396 L -.817 -.571 L -1.359 .319 L -.467 -.078 L -.924 -.751 L .812 -.36 L .516 -.021 L 1.075 -.122 L 1.017 .038 L .577 -.061 L 1.188 -.042 L h 425.42 68.82 m -.148 -.21 L -.629 .132 L -.134 -.026 L -.249 -.237 L -.239 -.343 L .188 -.158 L -.143 -.291 L -.361 .185 L -.062 .29 L .113 .237 L -.147 .105 L -.339 .316 L -.086 .185 L -.521 .105 L -.533 -.131 L -.781 .342 L .057 .131 L -.502 .289 L -.498 .263 L -1.658 .813 L 418.697 71 l -.274 .052 L -.271 0 L -2.111 .209 L -.188 -.236 L -.33 -.131 L -.183 .209 L 414.976 71 l .384 -.184 L -.935 -.236 L -.794 -.341 L -.574 -.052 L -.479 -.578 L .292 -.368 L -.126 -.21 L .34 .026 L .085 .316 L .284 -.21 L .667 .263 L -.244 -.197 L .127 -.145 L .5 -.092 L -.479 .026 L -.245 .105 L -.263 -.184 L -.111 -.132 L .579 -.276 L .455 -.185 L -.482 .066 L -.317 -.132 L .441 -.237 L .083 -.237 L -.661 .475 L -.036 -.264 L -.265 -.171 L .005 .211 L .076 .171 L -.419 .158 L -.41 0 L .014 -.277 L -.15 .264 L -.235 -.079 L -.2 -.448 L .658 -.449 L .08 .436 L .068 -.317 L .438 .158 L .211 -.092 L -.166 -.106 L .674 -.079 L .479 -.251 L -.611 .159 L -.48 0 L -.335 -.159 L .196 -.132 L .387 -.146 L .196 -.471 L -.821 -.014 L -.546 .094 L -.406 -.309 L .368 -.14 L .643 -.404 L -.212 -.186 L -.445 .063 L -.399 -.062 L -.211 -.217 L .284 -.482 L .619 -.265 L -.595 -.248 L -.225 -.374 L .272 -.188 L -.089 -.687 L .106 -.094 L .594 -.063 L .392 -.045 L .455 .044 L .082 -.11 L -.255 -.134 L -.425 -.118 L -.228 -.17 L .098 -.188 L .211 -.125 L .575 -.064 L .511 -.346 L .567 .109 L .498 -.111 L .179 -.55 L .835 .093 L .668 .125 L .571 -.252 L -.989 -.234 L -.266 -.314 L .236 -.143 L 1.151 .014 L .851 -.08 L .538 .219 L .06 -.473 L .477 -.27 L .297 -.333 L .78 -.175 L .438 .189 L .138 .079 L .5 -.476 L .869 -.413 L .084 -.429 L .437 -.398 L 1.497 -.735 L .335 -.192 L .235 -.048 L .481 .175 L .335 -.001 L .319 -.224 L -.225 -.223 L .141 -.128 L .488 -.224 L .457 -.305 L .481 -.483 L .245 .064 L .676 -.017 L -.067 -.612 L -.471 -.112 L .434 -.405 L .511 -.648 L .841 .063 L .047 -.341 L .096 -.228 L .954 -.116 L -.396 -.357 L -.327 -.097 L -.193 -.146 L .164 -.245 L .313 -.246 L .464 .048 L .888 -.067 L -.543 -.522 L .249 -.082 L .435 -.034 L .707 -.263 L .592 -.067 L .308 -.208 L -.358 -.064 L -.105 -.187 L .289 -.1 L .616 -.33 L .557 .032 L .351 .098 L .434 -.264 L .369 -.031 L .035 -.176 L -.421 -.321 L .627 -.232 L .146 -.275 L .336 .01 L .359 .297 L .397 .082 L .526 -.381 L -.281 -.656 L .438 .065 L .698 -.058 L .207 -.174 L -.604 -.315 L .411 -.224 L .816 -.251 L .093 -.257 L .261 -.194 L .73 -.135 L .074 .067 L .274 -.051 L .102 -.067 L .492 -.67 L .521 .25 L .96 -.153 L .411 .133 L .265 .1 L .646 .065 L .536 -.419 L .747 -.84 L .286 -.085 L .16 .168 L -.178 .286 L -.182 .587 L .791 -.454 L .403 -.572 L .819 -.085 L .688 -.439 L .437 .117 L .665 .353 L .567 -.643 L .318 -.272 L .976 .1 L 1.019 .506 L .197 -.152 L .479 -.595 L .709 -.444 L .283 -.052 L .562 .067 L .338 -.308 L .469 -.291 L .619 -.189 L 1.2 -.173 L .984 .015 L -1.337 .755 L -.24 .273 L -.107 .646 L .42 .084 L .691 -.579 L 1.53 -.941 L .764 -.275 L .028 .257 L -.004 .905 L .704 -.172 L .438 -.239 L .855 -.874 L .959 -.104 L .586 -.001 L .216 .154 L -.383 .257 L -.335 .257 L .074 .239 L -.319 .41 L .921 .118 L .221 -.443 L .902 -.651 L .421 .017 L 1.682 .561 L .667 .135 L .935 .015 L .649 .237 L .1 .272 L -1.216 .393 L -1.148 .036 L -2.233 -.047 L .193 .084 L .585 .304 L 1.058 .284 L .383 .302 L .728 -.052 L .679 -.102 L .831 .032 L .708 .25 L -.404 .288 L -.514 .204 L -.824 .021 L -.77 .121 L -1.023 .49 L -1.013 .672 L -.689 .303 L -.001 -.2 L .096 -.367 L .874 -.923 L -.044 -.268 L -.88 -.348 L -.672 -.114 L -1.125 -.431 L -.556 .003 L -.555 .07 L -2.004 .095 L -.865 .374 L -.567 .422 L -.26 .437 L -.079 .501 L -.188 .5 L -1.25 .472 L -.582 .368 L -.31 -.148 L -1.288 -.492 L -1.657 .225 L -1.411 -.042 L -.536 -.13 L -.861 -.595 L -.994 -.612 L -.486 -.082 L -.355 .103 L -1.012 .656 L -.114 -.063 L -.41 -.031 L -.865 .038 L -.281 1.066 L -.164 .449 L -.271 .25 L -.323 -.015 L -.67 -.129 L -.972 -.144 L -1.731 -.389 L -.168 .183 L -.021 .663 L -.083 .199 L -.318 .299 L -.695 .004 L -.871 -.095 L -.279 -.015 L -.465 .167 L -1.484 1.176 L .05 .213 L .171 .507 L -.188 .345 L -.615 .346 L -1.325 .919 L -.687 .442 L -.981 .021 L -.121 .244 L -.06 .971 L -.32 .598 L -.383 .469 L -.574 .499 L -.495 .545 L .339 .125 L .381 .269 L .252 .587 L -.17 .176 L -.44 .177 L -.733 .036 L -.932 -.027 L -.618 .114 L -.613 .257 L -.617 .479 L -.389 .492 L -.077 .664 L -.349 1.093 L .65 .729 L -.335 1.171 L .582 .39 L .715 .186 L .297 .243 L -.432 .67 L -.941 .16 L .571 .735 L .26 .427 L -.249 .346 L -.029 .532 L -.769 .384 L -.414 .013 L -.117 .423 L -.506 .224 L .22 .831 L -.324 .737 L -.41 .013 L -.15 -.342 L -.293 -.132 L
+464.349 47.431 N -.024 .283 L -.258 .465 L .116 .529 L .563 .212 L .872 .194 L .964 .506 L .543 .392 L -.421 .364 L -1.303 .777 L -.328 .296 L 1.402 1.394 L .772 1.195 L -.196 .356 L -.44 .535 L -.187 .403 L -.029 .37 L .37 .509 L 1.286 1.395 L .151 .253 L -.057 .254 L -.542 .415 L -.327 .191 L .439 .33 L 1.357 .499 L .703 .265 L .436 .392 L .161 .346 L -.105 .409 L -.43 .536 L -.591 .536 L -1.385 .758 L -1.983 1.195 L -1.946 1.145 L -.174 .285 L -.935 .206 L -1.195 .188 L -.149 .181 L -.506 -.217 L -.518 .146 L -.052 .134 L -.688 -.07 L -.193 -.108 L .057 -.387 L -.241 .527 L -.268 .14 L -.633 .047 L -.328 .03 L -.1 .267 L -.589 -.326 L -.29 .275 L -.676 .064 L -.34 .178 L -.052 -.127 L -.504 .108 L -.504 .108 L -.211 .266 L -.311 -.152 L -.672 .126 L -.04 -.114 L -.658 .215 L -.547 .013 L -.482 .025 L -.487 -.253 L -.223 -.274 L .273 -.34 L -.846 .017 L -.517 .177 L .065 -.382 L -.446 .076 L -.644 -.271 L -.409 -.061 L -.415 -.231 L -.26 -.403 L -.036 -.714 L .158 -.374 L .05 -.436 L .09 -.234 L -.347 -.483 L -.431 -.375 L .3 -.658 L -.02 -.392 L -.305 -.204 L -.37 -.093 L .062 -.299 L .295 -.331 L .637 -.285 L .144 -.189 L -.113 -.331 L .382 -.143 L .633 .125 L .274 .063 L .483 -.222 L .303 -.427 L .104 -.459 L .207 -.27 L .301 .316 L .27 0 L 1.678 -1.114 L 1.142 -.654 L .835 -.576 L 1.026 -.401 L .874 .03 L .06 -.304 L -.13 -.144 L -.357 -.16 L .119 -.371 L .146 -.387 L -.329 -.274 L -1.139 -.095 L -.836 -.451 L -.594 -.024 L -.113 -.234 L -.327 -.307 L -.208 -.535 L .5 -.8 L -.028 -.392 L -.405 -.685 L -.507 -.637 L .004 -.526 L .062 -.413 L -.77 -.558 L -.508 -.229 L -1.583 -.207 L -1.085 -.292 L -1.286 -.658 L -.616 -.463 L -.146 -.033 L 1.012 -.656 L .355 -.103 L .486 .082 L .994 .612 L .861 .595 L .536 .13 L 1.411 .042 L 1.657 -.225 L 1.288 .492 L .31 .148 L .582 -.368 L 1.25 -.472 L .188 -.5 L .079 -.501 L .26 -.437 L .567 -.422 L .865 -.374 L 2.004 -.095 L .555 -.07 L .556 -.003 L 1.125 .431 L .672 .114 L .88 .348 L .044 .268 L -.874 .923 L -.096 .367 L .001 .2 L
+453.795 53.873 N -.23 -.004 L -.975 .164 L -.447 .098 L -.902 -.387 L -.339 -.178 L -.347 .114 L -.641 .374 L -.542 .599 L -.309 .146 L -.658 .018 L -.545 .355 L .325 .241 L .146 .192 L -.355 .273 L -.907 .401 L .317 .271 L .385 .159 L .33 .302 L -.749 .367 L -.405 .43 L -.368 .461 L -1.591 .669 L -.699 .286 L -.456 -.205 L -.326 .08 L -.538 .539 L -.646 .144 L -.663 .348 L -.217 .284 L -.191 .142 L -.746 .033 L -.221 -.031 L .151 .535 L -.484 .425 L 439 61.33 l .216 .533 L .243 .25 L -.244 .955 L -.498 .063 L -.217 .204 L .189 .757 L -.009 .544 L .381 .444 L -.287 .175 L .926 .082 L .225 .187 L .499 -.082 L .652 .594 L .428 .314 L .86 .163 L .198 .406 L .32 .139 L .014 .335 L -.391 0 L -.508 .323 L -.861 .3 L -.492 .162 L -.521 -.012 L -.146 -.115 L -.805 -.115 L -.66 -.173 L -.634 -.069 L -.137 .104 L -.446 -.115 L -.257 .3 L .819 -.069 L .497 .265 L .591 .046 L .205 .208 L .481 .069 L .021 -.127 L .616 .069 L .258 -.196 L .297 .011 L .481 -.104 L .226 .081 L .026 -.15 L .164 0 L .214 .15 L -.029 .081 L -.722 .023 L .219 .288 L -.792 .265 L -.168 .288 L -.386 .08 L -.089 -.092 L .042 -.161 L -.014 -.276 L -.258 .023 L -.091 .46 L -.501 .333 L -.248 .058 L -.36 -.069 L -.005 .218 L -1.418 .035 L .156 .458 L .513 .258 L .066 .609 L -.109 .33 L -.281 .051 L -.308 -.064 L .038 .429 L .29 .081 L -.126 .268 L .24 .228 L -.365 .364 L -.222 .455 L -.041 .56 L -.766 1.043 L -.58 .71 L -.165 -.062 L -.442 -.236 L -.614 .193 L -1.262 -.089 L -.106 .311 L -.252 -.083 L -.518 .264 L -.295 .517 L .356 .336 L -.376 .374 L -.48 -.129 L -.173 -.037 L -1.158 .269 L -1.11 -.116 L -.001 -.142 L .19 -.078 L -.062 -.181 L .281 -.297 L -.638 -.53 L -.413 -.479 L -.22 -.272 L .529 -.116 L -.082 -.312 L .421 .077 L .147 -.245 L -.19 -.234 L -.285 .013 L -.292 -.377 L -.527 -.221 L -.63 -.99 L -.286 0 L .011 -.408 L -.256 -.364 L -.348 -.298 L .221 -.484 L .206 -.341 L -.418 -.154 L -.405 .102 L -.055 -.288 L -.412 .051 L -.18 -1.207 L -.225 -.131 L .311 -.355 L .293 .132 L .15 .342 L .41 -.013 L .324 -.737 L -.22 -.831 L .506 -.224 L .117 -.423 L .414 -.013 L .769 -.384 L .029 -.532 L .249 -.346 L -.26 -.427 L -.571 -.735 L .941 -.16 L .432 -.67 L -.297 -.243 L -.715 -.186 L -.582 -.39 L .335 -1.171 L -.65 -.729 L .349 -1.093 L .077 -.664 L .389 -.492 L .617 -.479 L .613 -.257 L .618 -.114 L .932 .027 L .733 -.036 L .44 -.177 L .17 -.176 L -.252 -.587 L -.381 -.269 L -.339 -.125 L .495 -.545 L .574 -.499 L .383 -.469 L .32 -.598 L .06 -.971 L .121 -.244 L .981 -.021 L .687 -.442 L 1.325 -.919 L .615 -.346 L .188 -.345 L -.171 -.507 L -.05 -.213 L 1.484 -1.176 L .465 -.167 L .279 .015 L .871 .095 L .695 -.004 L .318 -.299 L .083 -.199 L .021 -.663 L .168 -.183 L 1.731 .389 L .972 .144 L .67 .129 L .323 .015 L .271 -.25 L .164 -.449 L .281 -1.066 L .865 -.038 L .41 .031 L .114 .063 L .146 .033 L .616 .463 L 1.286 .658 L 1.085 .292 L 1.583 .207 L .508 .229 L .77 .558 L -.062 .413 L -.004 .526 L .507 .637 L .405 .685 L .028 .392 L -.5 .8 L .208 .535 L .327 .307 L .113 .234 L
+238.107 361.753 N .515 -.688 L .859 0 L 1.202 -.515 L -.171 -.858 L -3.091 .344 L -2.061 .515 L -1.545 -.344 L -.515 -.858 L 1.374 -.515 L 1.202 -.172 L 3.091 -.172 L 1.374 -.515 L -.172 -1.03 L .859 -.516 L 0 -1.374 L -1.326 -1.054 L .982 -.491 L 1.202 -.344 L 1.545 -.172 L .858 .344 L .858 .858 L 1.03 1.374 L 1.374 1.202 L 1.03 1.373 L -.515 2.061 L -1.545 .516 L -1.545 .858 L -3.434 .172 L -3.435 0 L h 172.686 360.379 m 3.091 -.687 L 3.262 0 L 5.495 0 L 2.919 1.202 L -2.748 .687 L -3.759 -.337 L -5.169 -.35 L -3.091 -.516 L h 65.13 371.085 m 1.194 -.265 L 1.393 .133 L .796 .464 L -1.99 0 L -1.393 -.332 L h 277.944 379.954 m .515 -1.202 L 2.404 -1.03 L 2.748 -.172 L 2.061 -.171 L 1.545 -.859 L .343 -1.545 L 1.374 -.687 L 3.091 -.688 L 4.464 0 L 2.404 0 L 3.434 .688 L .687 1.889 L 0 1.202 L -2.061 1.201 L -11.848 1.03 L -11.161 .344 L h 36.004 376.434 m 1.202 -.858 L 2.061 -.343 L 4.121 .515 L 4.293 1.374 L -.887 .482 L -2.719 .204 L -4.293 -.515 L -3.777 -.859 L h 777.843 385.46 m 3.823 .391 L .899 .029 L .322 .234 L 1.202 .294 L 2.329 .088 L 2.126 -.003 L 5.218 .329 L 3.046 .348 L 1.144 -.059 L .762 .029 L 1.411 .37 L 0 2.175 L 0 2.223 L 0 2.222 L 0 2.223 L 0 2.222 L 0 1.551 L -.041 0 L -2.222 0 L -2.223 0 L -2.222 0 L -2.223 0 L -2.222 0 L -2.223 0 L -2.222 0 L -2.223 0 L -2.223 0 L -2.222 0 L -2.223 0 L -2.222 0 L -2.223 0 L -2.222 0 L -2.223 0 L -2.222 0 L -2.223 0 L -2.223 0 L -2.222 0 L -2.223 0 L -2.222 0 L -2.223 0 L -2.222 0 L -2.223 0 L -2.222 0 L -2.223 0 L -2.223 0 L -2.222 0 L -2.223 0 L -2.222 0 L -2.223 0 L -2.222 0 L -2.223 0 L -2.222 0 L -2.223 0 L -2.223 0 L -2.222 0 L -2.223 0 L -2.222 0 L -2.223 0 L -2.222 0 L -2.223 0 L -2.222 0 L -2.223 0 L -2.223 0 L -2.222 0 L -2.223 0 L -2.222 0 L -2.223 0 L -2.222 0 L -2.223 0 L -2.222 0 L -2.223 0 L -2.223 0 L -2.222 0 L -2.223 0 L -2.222 0 L -2.223 0 L -2.222 0 L -2.223 0 L -2.222 0 L -2.223 0 L -2.223 0 L -2.222 0 L -2.223 0 L -2.222 0 L -2.223 0 L -2.222 0 L -2.223 0 L -2.222 0 L -2.223 0 L -2.223 0 L -2.222 0 L -2.223 0 L -2.222 0 L -2.223 0 L -2.222 0 L -2.223 0 L -2.222 0 L -2.223 0 L -2.223 0 L -2.222 0 L -2.223 0 L -2.222 0 L -2.223 0 L -2.222 0 L -2.223 0 L -2.222 0 L -2.223 0 L -2.182 0 L -.041 0 L -2.222 0 L -2.223 0 L -2.222 0 L -2.223 0 L -2.222 0 L -2.223 0 L -2.222 0 L -2.223 0 L -2.223 0 L -2.222 0 L -2.223 0 L -2.222 0 L -2.223 0 L -2.222 0 L -2.223 0 L -2.222 0 L -2.223 0 L -2.223 0 L -2.222 0 L -2.223 0 L -2.222 0 L -2.223 0 L -2.222 0 L -2.223 0 L -2.222 0 L -2.223 0 L -2.223 0 L -2.222 0 L -2.223 0 L -2.222 0 L -2.223 0 L -2.222 0 L -2.223 0 L -2.222 0 L -2.223 0 L -2.223 0 L -2.222 0 L -2.223 0 L -2.222 0 L -2.223 0 L -2.222 0 L -2.223 0 L -2.222 0 L -2.223 0 L -2.223 0 L -2.222 0 L -2.223 0 L -2.222 0 L -2.223 0 L -2.222 0 L -2.223 0 L -2.222 0 L -2.223 0 L -2.223 0 L -2.222 0 L -2.223 0 L -2.222 0 L -2.223 0 L -2.222 0 L -2.223 0 L -2.222 0 L -2.223 0 L -2.223 0 L -2.222 0 L -2.223 0 L -2.222 0 L -2.223 0 L -2.222 0 L -2.223 0 L -2.222 0 L -2.223 0 L -2.223 0 L -2.222 0 L -2.223 0 L -2.222 0 L -2.223 0 L -2.222 0 L -2.223 0 L -2.222 0 L -2.223 0 L -2.223 0 L -2.222 0 L -2.223 0 L -2.222 0 L -2.223 0 L -2.222 0 L -2.223 0 L -2.222 0 L -2.223 0 L -2.182 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.223 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.223 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.223 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.223 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.223 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.223 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.223 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.223 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.223 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.223 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.223 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.223 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.223 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.223 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.223 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.223 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.223 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.223 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.223 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.222 0 L -2.222 0 L 9.014 H 6.792 H 4.569 H 2.347 H .125 H 0 -1.544 L 0 -2.222 L 0 -2.223 L 0 -2.222 L 0 -2.223 L 0 -2.223 L 3.941 .297 L 10.131 0 L 7.898 .516 L 6.868 .172 L 8.586 .858 L 6.353 .515 L 8.757 .172 L 4.808 -.172 L 6.525 -.172 L 2.404 .344 L 3.606 -.858 L 16.312 .344 L 4.638 -.013 L 1.143 -.467 L -14.366 -.551 L -9.616 -.688 L -2.232 0 L -3.091 -.172 L -.858 -.171 L -1.545 -.344 L -.498 -.455 L .155 -1.434 L .687 -.858 L 1.889 -.688 L -3.263 -.687 L -2.232 -.344 L -.953 -.503 L 1.589 -.521 L -2.891 -.6 L -3.074 -.208 L -.104 -.261 L 2.857 -.312 L 2.748 0 L 6.525 .343 L 3.434 0 L 2.576 .344 L 4.636 -1.202 L 2.404 -.687 L -2.748 -1.03 L -3.778 -.858 L -4.391 -.405 L -2.649 -.625 L -3.262 -.172 L -2.919 0 L -2.919 -.858 L 2.576 -.516 L 1.28 -.342 L -1.28 -.517 L -2.919 .516 L 52.66 H -1.717 -.344 L 49.054 373 l .343 -1.202 L 1.956 -.07 L 1.65 -.273 L 4.98 0 L 4.626 .493 L 3.272 .709 L 1.374 .172 L 4.465 -.858 L 4.089 .243 L 0 -.729 L .066 -.597 L -2.255 -.73 L -2.93 -.075 L -2.576 -.344 L 2.404 -.687 L 4.121 .171 L 1.717 -.343 L 3.091 -.516 L 3.09 -.343 L 5.667 -.688 L 4.465 -.343 L 3.949 -.516 L 4.464 -.515 L 2.748 .343 L 5.667 0 L 4.464 .258 L 4.121 -.515 L 7.898 .343 L 7.555 -.687 L 3.091 .172 L 1.545 -.688 L 1.545 .516 L 5.323 -.172 L .121 -.87 L 1.758 -.293 L 1.433 .325 L .586 .554 L -.423 .813 L 2.364 .329 L 1.38 -.264 L -.006 -.938 L 1.438 -.267 L 1.48 .267 L .212 1.036 L -1.009 .423 L .521 .781 L 3.024 -.351 L 1.889 .343 L 3.434 .172 L 2.576 -.687 L 3.778 .086 L 4.868 .33 L 2.344 .013 L 1.449 .109 L .693 -.53 L -.611 -.285 L -2.202 -.122 L 1.305 -.285 L .041 -.408 L -.367 -.245 L -1.386 .123 L -.693 .04 L -.856 -.979 L -2.178 -.337 L -.676 -.193 L -.182 -.665 L 4.056 .462 L 3.874 -.245 L -1.224 -.489 L -6.035 -.163 L -.774 -.082 L -.653 -.489 L 0 -.815 L 2.301 .105 L .839 .67 L 5.301 -.204 L 7.253 .736 L 2.404 -.172 L 3.262 -.172 L 4.464 .172 L 3.263 .172 L 2.061 -.516 L .687 -.687 L 1.202 0 L .344 1.03 L 2.576 .343 L 2.404 -.343 L .858 .515 L 3.435 .688 L 3.777 .343 L 2.061 .172 L 1.03 -.515 L .687 -1.202 L 2.747 -.172 L 1.03 .515 L 1.717 .688 L 3.091 -.172 L 3.434 .258 L 4.121 -.344 L 4.979 -.516 L 5.495 -.858 L 3.091 -.515 L 1.202 -.688 L 0 -1.373 L -1.374 -1.202 L -.859 -1.545 L -1.202 -.688 L -.344 -1.03 L 1.202 -1.03 L 1.717 -.343 L .858 -1.202 L -.343 -.858 L -.172 -1.03 L -1.202 -.516 L .344 -1.545 L 1.889 -.172 L 2.404 -1.374 L 1.545 -.515 L .859 -.859 L 1.03 -.687 L 2.919 -1.202 L 3.606 -.515 L 2.404 -1.202 L 2.404 -.172 L 2.232 -1.03 L 1.545 -.343 L .343 1.03 L -1.374 .515 L -1.03 .344 L -1.202 .515 L -.858 0 L -1.202 0 L -2.919 1.202 L -1.03 .687 L -1.717 1.374 L -.172 1.202 L -.687 .687 L -1.374 -.344 L -1.03 0 L -1.202 .516 L -1.202 .687 L -1.03 .688 L -.687 1.201 L .515 .688 L .687 .687 L 1.374 .344 L 1.545 0 L .858 .515 L .687 1.202 L 1.545 1.546 L .687 .858 L 1.03 .687 L .687 1.374 L .858 1.373 L .344 1.546 L -.344 .858 L -.858 1.717 L -2.919 1.889 L -1.202 .172 L -1.03 .858 L -1.717 1.03 L -3.263 .344 L -4.979 .858 L -2.404 .344 L -2.919 .687 L -7.377 -.141 L -4.556 -.316 L -.773 -.401 L -1.717 -.344 L -1.717 -.343 L -1.374 0 L -.859 .515 L .515 .858 L 2.061 .688 L 4.121 1.202 L 4.995 .04 L 1.202 .759 L -3.278 .574 L -5.667 -.343 L -4.979 -.516 L -3.097 -.285 L -2.151 .253 L .823 .696 L 1.164 .538 L 3.262 .172 L 2.232 0 L 1.374 .343 L -.249 .402 L -.782 .113 L -2.232 .515 L -5.151 -.171 L -1.374 -.172 L -2.232 -1.202 L -2.404 0 L .128 .917 L 2.62 1.144 L 1.03 .172 L 1.202 -.172 L 1.374 .344 L 2.404 0 L 4.98 -.516 L 3.09 .687 L .385 .431 L -.041 .6 L -2.061 .172 L -3.263 .172 L -2.608 -.185 L 3.811 .871 L 3.656 -.238 L 1.324 .41 L 2.576 .687 L 6.869 .859 L 8.585 .858 L 10.646 1.373 L 6.525 .516 L 2.061 .858 L .687 1.03 L 3.091 -.344 L 5.323 -1.545 L 6.525 -.687 L 2.455 -.066 L 6.474 -.105 L 9.788 -.344 L 1.889 .344 L 2.576 -1.202 L 8.414 -1.03 L 11.333 -.172 L 8.929 -.858 L 1.202 -.858 L -1.202 -.516 L -.271 -1.07 L -1.184 -.338 L -5.071 .035 L -2.232 .172 L -4.808 -.344 L -3.091 0 L 0 -1.202 L 3.263 -1.545 L 3.949 -1.202 L 2.404 -.172 L 4.121 -1.03 L 9.444 -1.202 L 5.323 -.858 L 6.525 0 L 4.979 -.516 L 2.747 -2.061 L 6.039 -1.541 L -1.23 -.52 L -2.576 .516 L -2.061 -.344 L 1.545 -1.03 L 1.545 -.343 L 2.48 -.249 L .696 -.742 L 3.85 -.742 L 1.577 -.418 L .603 -.557 L -1.716 -.881 L -.093 -.604 L 1.252 -.231 L .464 .742 L 1.017 -.097 L 2.061 -.858 L 1.03 -.343 L .858 .171 L 1.203 1.544 L 2.403 -.342 L .242 -.91 L .96 -.807 L 1.889 -.516 L 1.374 .516 L -1.162 .574 L -.139 .604 L 3.189 -.319 L 4.808 .258 L 3.263 -.172 L 1.03 .858 L 1.545 -.344 L 1.717 -.515 L 5.839 -.858 L 7.212 -1.03 L 4.292 -.858 L 2.061 0 L 2.061 1.373 L 1.718 .344 L 1.889 -.344 L 1.545 -.858 L 7.556 -.344 L 3.434 -.171 L 4.293 .171 L 6.697 .945 L 2.575 0 L 4.293 -.516 L 6.01 -.687 L 5.151 -.688 L 2.404 -.515 L 2.231 -.344 L .615 -.959 L 1.148 -.835 L 1.671 -.095 L .938 .965 L 1.294 .409 L 1.732 1.052 L 1.009 .139 L 1.009 -.661 L 1.322 -.174 L .209 .522 L -.835 .312 L .661 .244 L .8 .278 L .383 -.383 L .313 -.383 L .312 .209 L -.174 .348 L 0 .521 L .592 -.174 L 1.913 -.557 L .418 -1.704 L 5.102 -1.137 L 1.374 -.687 L 3.605 -.172 L 1.363 -.646 L 2.758 -.126 L .21 -.27 L -.165 -.297 L .692 -.23 L 1.221 .165 L .197 .593 L .66 .231 L 2.165 -.536 L 1.265 -.223 L -.726 -.297 L -.561 -.264 L .099 -.362 L 1.451 -.1 L 1.562 .215 L .647 .28 L .692 -.132 L -.066 -.33 L -.527 -.33 L 0 -.561 L .297 -.23 L .846 -.242 L 4.292 -.858 L 2.92 -.172 L 3.058 .115 L 2.296 1.076 L 1.316 .023 L .741 .144 L .023 .383 L -.908 .096 L -.168 .454 L 1.723 .216 L 4.892 .646 L 5.571 .453 L 1.546 .172 L 1.717 -.172 L 7.556 .688 L 5.323 0 L .687 .687 L .2 1.075 L -.73 1.232 L -.672 .439 L -.425 1.113 L -.73 .319 L -.905 -.402 L -.83 .402 L -.73 .959 L 1.507 .457 L .594 .045 L .959 -.593 L .411 .365 L -.867 1.05 L -1.215 .062 L -1.717 1.374 L 0 .858 L 1.889 .688 L 2.919 0 L 1.718 -.859 L 1.889 -1.545 L 1.717 -1.717 L 1.374 -.688 L 4.121 -1.03 L 2.231 -.687 L 3.263 -.344 L 2.061 -1.03 L 1.03 -1.03 L 3.435 -1.03 L 2.403 -.687 L 2.061 -.687 L 2.061 -.172 L 6.182 -.687 L 3.778 .171 L 1.204 -.542 L .169 -.659 L .54 -.759 L .49 -.101 L .471 .776 L .305 .775 L 4.892 -.35 L 5.838 0 L 4.979 -.172 L 3.091 .344 L 2.112 -.214 L 1.328 .085 L 1.018 .594 L 1.188 -.764 L 2.714 -1.046 L 1.866 -.226 L .664 -.028 L .594 -.028 L .777 -.057 L 1.512 .311 L 1.166 -.172 L 3.606 .687 L 3.777 .344 L 1.03 0 L 1.064 .573 L .745 .088 L 2.149 -.088 L 1.271 -.263 L 1.315 -.438 L -.192 -.216 L .28 -.53 L 1.447 -.131 L .921 -.088 L .786 .062 L 2.195 -.501 L 2.097 .501 L .688 .687 L .373 .435 L 1.842 -.131 L .921 -.044 L .985 .255 L 1.889 .688 L 2.919 .515 L 2.919 -.343 L 4.121 -.258 L 2.232 -.344 L 2.404 0 L 3.521 .315 L .702 -.755 L 2.158 -.162 L .863 1.133 L 4.909 .323 L .917 -1.133 L 1.889 -.593 L 5.988 .013 L .756 -.283 L 1.996 .108 L -.432 -1.403 L .054 -.809 L 1.232 -.36 L .872 1.277 L -.647 1.079 L .805 .219 L 2.81 .482 L 5.262 .376 L 5.494 .344 L 2.278 .583 L 1.283 .14 L 1.396 -.167 L 2.204 .446 L 1.961 -.167 L .719 .278 L -.435 .431 L -.151 .351 L .293 .123 L 2.081 .331 L 1.078 .484 L 2.706 -.134 L .377 .595 L 1.038 .227 L 4.808 .515 L -.123 .536 L .567 .236 L 2.695 -.095 L 3.214 .181 L -.329 -.984 L .71 -.095 L .804 .426 L .331 .709 L 1.135 .284 L 2.672 .519 L 2.576 -.343 L 4.017 .999 L 1.626 .495 L 1.672 .871 L 1.626 .023 L .504 .187 L .532 -.021 L .754 -.047 L .777 .047 L .896 .589 L .541 .118 L .542 .07 L .142 -.259 L .471 0 L 1.484 -.048 L 1.416 .237 L 5.951 .535 L .729 .516 L 1.733 .323 L 2.979 .944 L .43 -.335 L .551 .191 L .454 .67 L -.741 .215 L -1.124 .167 L -.216 .359 L .168 .263 L .55 -.048 L -.024 .67 L -.43 -.048 L -.096 .287 L -.312 .071 L -.598 .622 L -.526 .12 L -.55 .287 L -.168 .669 L -1.603 .096 L -1.339 -.239 L -1.794 .359 L -2.153 .425 L -2.629 .764 L 1.271 1.103 L -1.907 .212 L -1.23 -.255 L -.763 .212 L -.043 .637 L -2.374 .169 L .136 .514 L .924 .632 L -.127 .763 L -.382 .085 L -.466 .467 L .551 .466 L .085 .764 L -.764 .424 L .637 .254 L .763 0 L .764 .255 L .339 .509 L .17 .721 L 1.284 -.187 L .836 .907 L 2.035 .297 L .339 .339 L 2.672 .552 L -4.113 0 L -2.799 .138 L -2.332 .541 L -1.823 -.085 L -.806 .212 L -1.611 .339 L -1.326 -.32 L -.546 .426 L 1.116 .529 L -.503 .447 L -.669 .195 L -1.117 .753 L -2.623 .642 L .475 .391 L 2.205 -.111 L .53 0 L .809 .307 L .168 .363 L .499 .18 L .422 .183 L .111 .894 L .209 .502 L 3.195 .279 L 2.874 .948 L 3.662 .801 L 5.151 1.202 L 1.456 .062 L 1.015 .17 L -.146 .234 L -1.67 .264 L -.47 .264 L 1.173 .088 L 2.198 -.293 L 1.988 .166 L
+
+/target get (out.png) write-to-png pop
+pop
diff --git a/util/cairo-script/examples/zrusin.cs b/util/cairo-script/examples/zrusin.cs
new file mode 100644 (file)
index 0000000..8efc24b
--- /dev/null
@@ -0,0 +1,1333 @@
+% The data in this file was created by Zack Rusin and downloaded from:
+%
+%  http://ktown.kde.org/~zrusin/examples/cairorender.tar.bz2
+%
+%   It is included in the cairo performance test suite by his
+%   permission, (given in email to Carl Worth on 2006-10-23)
+
+dict
+  /width  415 set
+  /height 116 set
+  surface context
+
+107.893088734820 103.841923410053 l
+106.908713734820 104.920048410053 l
+106.605078419636 105.201721390261 l
+102.286079757931 107.612435141518 l
+95.848592904540 109.855345077872 l
+95.820873452616 109.863161445001 l
+90.517184462013 111.170008329713 l
+87.125000000000 111.625000000000 l
+82.393245921775 111.316572641568 l
+77.855183975790 110.402282596173 l
+73.577390672683 108.898617908665 l
+69.626442523093 106.822066623894 l
+69.373711344187 106.641023992165 l
+57.717461344187 97.141023992165 l
+60.861283165549 97.153157587308 l
+52.861026127481 102.679312400770 l
+43.297199733607 107.707604120505 l
+38.061787185467 109.824994465236 l
+32.824736230810 111.354170438973 l
+27.636335844151 112.281599723349 l
+22.546875000000 112.593750000000 l
+19.478437557032 111.949288129016 l
+16.158848619890 110.212087077375 l
+13.224557514711 107.109984243309 l
+12.401022392395 105.215121771751 l
+12.109375000000 103.234375000000 l
+12.857968117079 98.340329228563 l
+15.050447208669 93.495802782054 l
+18.606861885297 88.792446786896 l
+23.447261757492 84.321912369510 l
+23.462751811761 84.309735859104 l
+29.835792474114 80.170241641945 l
+36.826339837257 77.183700966953 l
+44.355342734713 75.374150023260 l
+52.343750000000 74.765625000000 l
+54.172214431835 74.493658379815 l
+55.005326273741 73.240819884599 l
+54.891695624471 73.637732784350 l
+55.835419478567 69.288398667240 l
+56.941029555972 65.553969608663 l
+57.993585771605 68.524034756036 l
+53.681468264740 66.410473849442 l
+48.062136369578 64.586219029579 l
+42.258612309644 63.317877950046 l
+37.437500000000 62.906250000000 l
+32.573685233275 63.044800246717 l
+26.989159126193 63.632907811096 l
+20.752015377001 64.279295952574 l
+16.562500000000 64.484375000000 l
+16.408072340842 64.479600861137 l
+11.611197340842 64.182725861137 l
+11.453516511116 64.167941148499 l
+6.725716011250 62.845442977500 l
+4.607850691978 60.769517471259 l
+3.781250000000 57.859375000000 l
+4.373461857036 55.042556375256 l
+5.951936001924 52.702230099626 l
+6.143541831166 52.540844514692 l
+9.884650718045 51.301737345381 l
+10.233131146503 51.254965794601 l
+14.936256146503 50.958090794601 l
+15.093750000000 50.953125000000 l
+19.896489079886 51.056672881330 l
+25.533897731570 51.448761318515 l
+25.296875000000 51.437500000000 l
+30.909892241200 52.067729129895 l
+30.484375000000 52.031250000000 l
+39.960841830525 52.410121686268 l
+48.752300459876 53.538988916016 l
+48.757228534847 53.539874692156 l
+54.123571896675 54.731960341587 l
+58.970980406852 56.270657465230 l
+63.248918806945 58.141504425356 l
+66.906851838519 60.330039584234 l
+66.896289983187 60.322652661852 l
+70.654761046586 63.581911162241 l
+73.423041490057 67.400928399521 l
+75.133561184297 71.670457102504 l
+75.718750000000 76.281250000000 l
+74.721151272876 79.892293861068 l
+72.068677652167 83.513297493231 l
+70.325052633091 85.868624092784 l
+69.828125000000 88.250000000000 l
+70.186304443223 90.552747673203 l
+71.326302526678 92.735137555366 l
+73.346308881046 94.894011912335 l
+76.344513137007 97.126213009954 l
+79.989627194854 99.164217236556 l
+83.789048154438 100.596188457783 l
+87.811323321056 101.441306202608 l
+92.125000000000 101.718750000000 l
+97.462586180660 101.070932456387 l
+97.557831952069 101.055648080491 l
+103.015964519671 99.851433056816 l
+103.984375000000 99.656250000000 l
+106.046875000000 99.656250000000 l
+107.893088734820 103.841923410053 l
+106.046875000000 104.656250000000 l
+103.984375000000 104.656250000000 l
+104.952785480329 104.461066943184 l
+98.254668047931 106.006851919509 l
+98.349913819340 105.991567543613 l
+92.125000000000 106.718750000000 l
+87.170121991444 106.371193797392 l
+82.386733095562 105.341311542217 l
+77.843380617646 103.648282763444 l
+73.608611862993 101.311286990046 l
+69.914433306454 98.422394337665 l
+67.154166223322 95.217987444634 l
+65.426000244277 91.794908576797 l
+64.828125000000 88.250000000000 l
+65.079958470471 86.099459712769 l
+65.803853616909 83.955594657216 l
+68.478197347833 80.033577506769 l
+70.243692477124 77.869424888932 l
+70.718750000000 76.281250000000 l
+70.335188815703 72.767042897496 l
+69.139458509943 69.692821600479 l
+67.063988953414 66.949338837759 l
+64.041210016813 64.427347338148 l
+64.030648161481 64.419960415766 l
+57.122769593148 60.885592534770 l
+47.867771465153 58.460125307844 l
+47.872699540124 58.461011083984 l
+39.531345669475 57.386753313732 l
+30.484375000000 57.031250000000 l
+30.058857758800 56.994770870105 l
+25.296875000000 56.437500000000 l
+25.059852268430 56.426238681485 l
+19.697260920114 56.052702118670 l
+15.093750000000 55.953125000000 l
+15.251243853497 55.948159205399 l
+10.548118853497 56.245034205399 l
+10.896599281955 56.198262654619 l
+9.168958168834 56.521655485308 l
+9.360563998076 56.360269900374 l
+8.781250000000 57.859375000000 l
+8.758601803360 58.189255218076 l
+8.735899308021 58.292982528741 l
+8.961783988750 58.373307022500 l
+12.077733488884 59.207058851501 l
+11.920052659158 59.192274138863 l
+16.716927659158 59.489149138863 l
+16.562500000000 59.484375000000 l
+20.310484622999 59.298829047426 l
+26.432715873807 58.663967188904 l
+32.129439766725 58.064574753283 l
+37.437500000000 57.906250000000 l
+42.956231440356 58.377434549954 l
+49.359738630422 59.757530970421 l
+55.603687985260 61.808276150558 l
+60.600164228395 64.257215243964 l
+61.652720444028 67.227280391337 l
+60.695830521433 70.461601332760 l
+59.795804375529 74.612267215650 l
+59.682173726259 75.009180115401 l
+58.534870256371 76.967235761415 l
+56.827785568165 78.467279120185 l
+54.713289027441 79.427384197713 l
+52.343750000000 79.765625000000 l
+45.078251015287 80.284053101740 l
+38.408035162743 81.855361533047 l
+32.254051275886 84.503586483055 l
+26.537248188239 88.252764140896 l
+26.552738242508 88.240587630490 l
+22.301341239703 92.064975088104 l
+19.363615291331 95.777634717946 l
+17.659610007921 99.470217646437 l
+17.109375000000 103.234375000000 l
+17.462942485289 104.745484506691 l
+18.934901380110 106.053537922625 l
+21.052812442968 107.257743120984 l
+22.546875000000 107.593750000000 l
+27.127336030849 107.323869026651 l
+31.729951269190 106.505204561027 l
+36.405009689533 105.124224284764 l
+41.202800266393 103.167395879495 l
+50.256161372519 98.422250099230 l
+57.732466834451 93.253092412692 l
+60.876288655813 93.265226007835 l
+72.532538655813 102.765226007835 l
+72.279807476907 102.584183376106 l
+75.647218702317 104.376772716335 l
+79.199503524210 105.636779903827 l
+83.003238453225 106.380692983432 l
+87.125000000000 106.625000000000 l
+94.491626547384 105.043088554999 l
+94.463907095460 105.050904922128 l
+100.135795242069 103.121939858482 l
+103.519921580364 101.267028609739 l
+103.216286265180 101.548701589947 l
+104.200661265180 100.470576589947 l
+106.046875000000 104.656250000000 l
+107.893088734820 103.841923410053 l
+55.872490683787 89.763134548637 l
+54.997490683787 85.841259548637 l
+55.036476188094 85.993355189713 l
+52.343750000000 85.750000000000 l
+46.702141892082 86.180801752650 l
+41.285068752391 87.490050907175 l
+36.021375201732 89.703013308437 l
+30.839905860908 92.844954801298 l
+30.840393168266 92.844613599752 l
+24.841774707763 97.553563991314 l
+23.093750000000 100.109375000000 l
+23.668337709691 101.518994849695 l
+23.371174656526 101.224323797050 l
+23.620948599883 101.397798979282 l
+24.023420115060 101.552272092059 l
+25.484375000000 101.718750000000 l
+29.298199671143 101.348354407082 l
+33.545396502623 100.218839979757 l
+38.283160693967 98.302715245167 l
+43.568687444697 95.572488730454 l
+43.462444284634 95.635929361188 l
+56.978069284634 87.104679361188 l
+55.872490683787 89.763134548637 l
+107.893088734820 103.841923410053 l
+59.646930715366 91.332820638812 l
+46.131305715366 99.864070638812 l
+46.025062555303 99.927511269546 l
+40.294964306033 102.857441004833 l
+34.923353497377 104.984285020243 l
+29.967425328857 106.280551842918 l
+25.484375000000 106.718750000000 l
+22.429704884940 106.291477907941 l
+20.160075343474 105.056926202950 l
+19.862912290309 104.762255150305 l
+18.581942192719 102.559691058502 l
+18.093750000000 100.109375000000 l
+18.781885833597 97.630167914546 l
+20.751975292237 94.868311008686 l
+27.972106831734 88.749136400248 l
+27.972594139092 88.748795198702 l
+33.744249798268 85.287221066563 l
+39.777431247609 82.783386592825 l
+46.000983107918 81.262557622350 l
+52.343750000000 80.750000000000 l
+54.881053553302 80.995299290410 l
+57.121823397686 81.724997675285 l
+58.847249996203 82.929795925088 l
+59.838523811906 84.600394810287 l
+59.877509316213 84.752490451363 l
+60.752509316213 88.674365451363 l
+59.646930715366 91.332820638812 l
+107.893088734820 103.841923410053 l
+155.484375000000 101.859375000000 l
+154.564834615754 105.515207938449 l
+152.115670325806 108.366761454651 l
+148.955163295626 109.891294781728 l
+145.437500000000 110.437500000000 l
+142.960397099416 109.918703000637 l
+140.390467597390 108.438024620128 l
+137.806753458193 106.109033784838 l
+135.288296646094 103.045299421136 l
+135.165845369106 102.859481271949 l
+127.322095369106 89.718856271949 l
+127.315939322101 89.708486304067 l
+123.037165252408 83.240466071649 l
+119.422893480005 79.457711309224 l
+123.333764624626 78.181432151290 l
+119.923748586047 88.267600564284 l
+116.288451493331 96.371224650234 l
+116.310270123205 96.328970526240 l
+111.013619195867 105.085481651574 l
+104.498474377631 112.833853093888 l
+102.718750000000 113.578125000000 l
+98.109375000000 113.578125000000 l
+95.790533815800 112.012456612681 l
+92.715536961613 101.759617602767 l
+90.624767168816 88.808240839597 l
+90.625938562396 88.818553777181 l
+88.018863793847 68.357428789163 l
+88.022375537533 68.374401202625 l
+86.904794319936 64.234191072834 l
+85.466909526601 60.964474099786 l
+83.670654326939 58.467433589062 l
+81.477961890361 56.645252846242 l
+80.463845731714 53.674615230649 l
+82.823220731714 47.205865230649 l
+82.883587418943 47.055653464335 l
+83.966380643129 44.754922491868 l
+85.291281927099 42.535861182972 l
+88.514402164445 41.595109434025 l
+91.785870247761 43.817111177877 l
+94.599270325866 47.029404143594 l
+96.906293571887 51.130222797294 l
+98.658631158949 56.017801605095 l
+98.664492355983 56.039251079522 l
+100.006630671722 63.228131068770 l
+101.762541190731 76.272848032637 l
+101.765778670251 76.300425005941 l
+103.467098832178 87.353480411923 l
+105.697048617918 94.795754781373 l
+102.319980907406 93.545204629259 l
+104.354680083199 92.248641487426 l
+106.296883956259 90.334282557687 l
+110.081726403105 84.371439341729 l
+110.069085073875 84.396782220394 l
+112.413266990492 78.560599894318 l
+114.080111889746 71.934817013191 l
+115.075734612596 64.464258680566 l
+115.406250000000 56.093750000000 l
+117.906250000000 53.593750000000 l
+122.515625000000 53.593750000000 l
+124.596624760838 54.708311442938 l
+155.065374760838 100.473936442938 l
+155.484375000000 101.859375000000 l
+107.893088734820 103.841923410053 l
+150.903375239162 103.244813557062 l
+120.434625239162 57.479188557062 l
+122.515625000000 58.593750000000 l
+117.906250000000 58.593750000000 l
+120.406250000000 56.093750000000 l
+120.037546637404 64.922460069434 l
+118.935513110254 72.987057986809 l
+117.106264259508 80.232368855682 l
+114.555914926125 86.603217779606 l
+114.543273596895 86.628560658271 l
+112.308927321184 90.508291033149 l
+109.859366043741 93.751654942313 l
+107.239069916801 96.288467887574 l
+104.492519092594 98.048545370741 l
+101.115451382082 96.797995218627 l
+98.626651167822 88.552769588077 l
+96.796721329749 76.855824994059 l
+96.799958809269 76.883401967363 l
+95.087119328278 64.107806431230 l
+93.835507644017 57.335748920478 l
+93.841368841051 57.357198394905 l
+92.427690803113 53.221339702706 l
+90.736667174134 50.033095856406 l
+88.719989127239 47.690701322123 l
+86.329347835555 46.092390565975 l
+89.552468072901 45.151638817028 l
+88.346119356871 47.166952508132 l
+87.460162581057 49.069346535665 l
+87.520529268286 48.919134769351 l
+85.161154268286 55.387884769351 l
+84.147038109639 52.417247153758 l
+87.106689423061 54.909519535937 l
+89.579965473399 58.293338400214 l
+91.528799430064 62.470887052166 l
+92.915124462467 67.344348797375 l
+92.918636206153 67.361321210837 l
+95.592811437604 88.243946222819 l
+95.593982831184 88.254259160403 l
+97.565713038387 100.591944897233 l
+100.428216184200 110.143793387319 l
+98.109375000000 108.578125000000 l
+102.718750000000 108.578125000000 l
+100.939025622369 109.322396906112 l
+106.962943304133 102.180143348427 l
+111.845979876795 94.077279473760 l
+111.867798506669 94.035025349766 l
+115.287188913953 86.412086935716 l
+118.541235375374 76.756067848710 l
+122.452106519995 75.479788690776 l
+126.837834747592 80.025158928351 l
+131.621560677899 87.166513695933 l
+131.615404630894 87.156143728051 l
+139.459154630894 100.296768728051 l
+139.336703353906 100.110950578864 l
+142.906407402610 104.257287879872 l
+145.437500000000 105.437500000000 l
+147.478430454374 105.175111468272 l
+149.243704674194 104.273863545349 l
+150.251571634246 103.277760811551 l
+150.484375000000 101.859375000000 l
+150.903375239162 103.244813557062 l
+107.893088734820 103.841923410053 l
+231.343750000000 91.875000000000 l
+230.760913847107 95.964970754466 l
+229.068047862611 99.735912943733 l
+226.348615757792 103.075786207286 l
+222.686081243924 105.872550184609 l
+222.648873373705 105.894655312846 l
+215.093162016193 109.098726800095 l
+205.138412990776 111.389009633945 l
+205.099511514392 111.395066324151 l
+196.531910121565 112.292873746127 l
+187.093750000000 112.593750000000 l
+180.736641615184 112.156201510495 l
+174.578395035074 110.855968371846 l
+168.685194057574 108.711669078852 l
+163.123222480588 105.741922126310 l
+163.102786019362 105.728945017668 l
+158.065383006950 101.833715472335 l
+154.350564084671 97.489870440862 l
+152.052565874896 92.798170193375 l
+151.265625000000 87.859375000000 l
+151.531470468770 85.013668760581 l
+152.287360086651 82.060400295498 l
+152.321821893194 81.964698849566 l
+155.704155919825 73.828381186220 l
+158.270837388345 69.249451319292 l
+158.258242073026 69.265568807584 l
+162.161895999987 65.281962610977 l
+167.278829839386 61.794937132641 l
+173.532669176810 58.834467171163 l
+180.847039597849 56.430527525129 l
+179.144548452656 58.005986305326 l
+180.222673452656 54.974736305326 l
+180.222279555972 54.975844608663 l
+182.650011718926 50.400876966176 l
+185.678714551395 49.466466465483 l
+192.833058049629 52.996232862321 l
+201.831199616962 58.692914409530 l
+211.602112156950 65.789874557518 l
+220.243790039283 73.085988470431 l
+220.250361498213 73.092084369790 l
+227.553434625305 81.089470457139 l
+229.783449199558 84.667905408782 l
+230.987849850362 87.897461316292 l
+231.037603485703 88.222391630780 l
+231.334478485703 91.659891630780 l
+231.343750000000 91.875000000000 l
+107.893088734820 103.841923410053 l
+226.353021514297 92.090108369220 l
+226.056146514297 88.652608369220 l
+226.105900149638 88.977538683708 l
+225.181394550443 86.544985216218 l
+223.368440374695 83.730842042861 l
+216.843388501787 76.751665630210 l
+216.849959960717 76.757761529569 l
+208.538512843050 69.741375442482 l
+199.012550383038 62.822710590470 l
+190.370066950371 57.347517137679 l
+183.790035448605 54.096033534517 l
+186.818738281074 53.161623033824 l
+184.933970444028 56.649155391337 l
+184.933576547344 56.650263694674 l
+183.855451547344 59.681513694674 l
+182.152960402151 61.256972474871 l
+175.418502698190 63.429204703837 l
+169.900857660614 65.978500367359 l
+165.523650875013 68.934834264023 l
+162.210507926974 72.328181192416 l
+162.197912611655 72.344298680708 l
+160.116156580175 76.155993813780 l
+156.990678106806 83.754051150434 l
+157.025139913349 83.658349704502 l
+156.265625000000 87.859375000000 l
+156.814621625104 91.570970431625 l
+158.524435915329 95.017942059138 l
+161.489304493050 98.301050152665 l
+165.803463980638 101.521054982332 l
+165.783027519412 101.508077873690 l
+170.773790317426 104.198487171148 l
+175.945042464926 106.097156628154 l
+181.362967759816 107.222704739505 l
+187.093750000000 107.593750000000 l
+196.179027378435 107.308688753873 l
+204.369238485608 106.448683675849 l
+204.330337009224 106.454740366055 l
+213.484962983807 104.385648199905 l
+220.132376626295 101.574094687154 l
+220.095168756076 101.596199815391 l
+222.954118617208 99.523823167714 l
+224.892889637389 97.287524556267 l
+225.994945527893 94.775263620534 l
+226.343750000000 91.875000000000 l
+226.353021514297 92.090108369220 l
+107.893088734820 103.841923410053 l
+219.781250000000 95.890625000000 l
+219.378166433508 93.853798730227 l
+218.111752188172 91.489941446073 l
+212.645950385807 85.475693519170 l
+212.685981945341 85.511936058310 l
+206.545320157918 80.643069684901 l
+198.180220478697 74.962876659750 l
+198.191932106006 74.970352872652 l
+191.582535336704 71.037624006410 l
+191.504903114611 70.993386859602 l
+190.358726480976 70.329891393482 l
+194.093750000000 68.156250000000 l
+193.486799914207 72.326466575586 l
+191.750022838432 75.648693620488 l
+191.727672642897 75.674671879974 l
+189.796209835616 77.308949639626 l
+187.307360975056 78.497446987452 l
+184.374093537693 79.223076311545 l
+181.109375000000 79.468750000000 l
+178.539517280969 79.274893727068 l
+176.586499975916 78.676142180991 l
+173.086052480141 74.877460550577 l
+176.026873378540 75.790765650642 l
+173.637934677835 76.966105971818 l
+171.454775584042 78.628861257931 l
+167.479150931130 83.659399177979 l
+167.492959438562 83.636441477986 l
+164.707555748674 89.579684053440 l
+164.890625000000 88.640625000000 l
+165.429801946489 91.718738201013 l
+167.140367049889 94.413281660069 l
+170.161871706101 96.828214751252 l
+174.633867311024 99.067496848641 l
+174.595883286589 99.052025896332 l
+183.311717611230 101.570655775842 l
+188.036065935963 102.270678900324 l
+192.781250000000 102.500000000000 l
+204.722829878151 102.082467436594 l
+212.458857303872 100.833616072622 l
+214.905535432122 99.897801735668 l
+216.940228691241 98.604487224303 l
+220.087216056537 94.692203098109 l
+219.781250000000 95.890625000000 l
+107.893088734820 103.841923410053 l
+224.475283943463 97.089046901891 l
+222.495392805158 99.992428606532 l
+220.012896308759 102.426762775697 l
+217.106183317878 104.328760764332 l
+213.853642696128 105.635133927378 l
+205.324045121849 107.034720063406 l
+192.781250000000 107.500000000000 l
+187.549871564037 107.241039849676 l
+182.344532388770 106.476219224158 l
+172.747866713411 103.697974103668 l
+172.709882688976 103.682503151359 l
+167.310784543899 100.802644623748 l
+163.281507950111 97.266405839931 l
+160.761604303511 93.177746173987 l
+160.111277146626 90.958745814619 l
+159.890625000000 88.640625000000 l
+160.073694251326 87.701565946560 l
+163.194540561438 81.082308522014 l
+163.208349068870 81.059350822021 l
+165.573405849355 77.703241627766 l
+168.240536915958 74.886763742069 l
+171.153080947165 72.670612778182 l
+174.254376621460 71.115484349358 l
+177.195197519859 72.028789449423 l
+178.991625024084 74.292607819009 l
+181.109375000000 74.468750000000 l
+185.450451524944 73.971303012548 l
+187.959827357103 72.387828120026 l
+187.937477161568 72.413806379512 l
+188.825700085793 70.736033424413 l
+189.093750000000 68.156250000000 l
+192.828773519024 65.982608606518 l
+194.057596885389 66.694113140398 l
+193.979964663296 66.649875993590 l
+200.870567893994 70.748397127348 l
+200.882279521303 70.755873340250 l
+209.532804842082 76.638180315099 l
+216.001518054659 81.769313941690 l
+216.041549614192 81.805556480830 l
+219.736550554799 85.597619431325 l
+222.481997811828 89.260058553927 l
+224.192146066492 92.716513769773 l
+224.781250000000 95.890625000000 l
+224.475283943463 97.089046901891 l
+107.893088734820 103.841923410053 l
+274.375000000000 74.921875000000 l
+274.375000000000 76.000000000000 l
+273.941340954992 77.877573018672 l
+272.878932097861 79.515803527315 l
+273.201644884750 79.109789854043 l
+272.240878131027 80.634530144823 l
+272.178064435979 80.731878545843 l
+270.366672394243 82.164949472839 l
+268.046875000000 82.703125000000 l
+266.183148747233 82.484226579792 l
+265.676843902484 82.347511014208 l
+261.919943241226 81.056638412991 l
+261.845529920936 81.035610157726 l
+257.771892015055 80.000491652766 l
+254.812500000000 79.671875000000 l
+253.064641952966 80.017766952966 l
+253.029646790629 80.052082662255 l
+253.183319188544 79.921363850038 l
+253.251518993920 79.984611568497 l
+253.185946392942 81.106825858762 l
+253.000000000000 86.578125000000 l
+253.496255380785 102.128843883245 l
+253.498940253820 102.192840332413 l
+254.068093288267 110.651263719025 l
+251.578125000000 113.375000000000 l
+246.875000000000 113.375000000000 l
+246.340326851360 113.317155732979 l
+244.239667843345 112.409341061474 l
+241.449240399376 110.489665586259 l
+233.966932708158 103.861287459859 l
+233.964253154933 103.858697290509 l
+230.739570245917 99.429986748935 l
+227.863465886694 92.900734302334 l
+227.865566738278 92.906614054351 l
+224.904456445713 83.297192679644 l
+223.890625000000 76.390625000000 l
+224.718172891760 73.652424627265 l
+226.991950628026 71.671512863124 l
+230.398659300280 70.468125917422 l
+234.625000000000 70.062500000000 l
+239.436000436064 69.828681464349 l
+240.453125000000 69.140625000000 l
+239.621272369169 63.470278877568 l
+237.983036171748 55.833595299706 l
+237.978023315197 55.813489579321 l
+235.265625000000 42.968750000000 l
+235.414374888933 43.818233911095 l
+223.164374888933 9.911983911095 l
+223.454623060848 7.647483743269 l
+225.610873060848 4.506858743269 l
+225.803351703291 4.260965402925 l
+227.719534150475 2.932735689909 l
+230.078782966130 2.347671229322 l
+230.218750000000 2.343750000000 l
+231.580832848366 2.789728316683 l
+232.871346377781 3.980786060276 l
+235.131457239225 7.716876589460 l
+240.051188613236 19.745668022304 l
+244.970104457946 33.805503176211 l
+247.762510801176 44.143184476801 l
+247.777764624530 44.225329525617 l
+252.184014624530 70.381579525617 l
+251.352799675865 68.904817480577 l
+254.575226795957 69.673348314939 l
+260.406250000000 70.359375000000 l
+270.523910039912 70.690857394210 l
+273.186407177161 72.174765791999 l
+274.049624252688 73.457293811137 l
+274.375000000000 74.921875000000 l
+107.893088734820 103.841923410053 l
+269.375000000000 74.921875000000 l
+269.694839960088 75.621642605790 l
+260.406250000000 75.359375000000 l
+253.596648204043 74.576651685061 l
+250.197596782742 73.718304003256 l
+248.084700324135 72.688932519423 l
+247.253485375470 71.212170474383 l
+242.847235375470 45.055920474383 l
+242.862489198824 45.138065523199 l
+240.182239292054 35.233559323790 l
+235.370686386764 21.504331977696 l
+230.614636510775 9.861248410540 l
+229.674552059719 7.679370189724 l
+229.774391761009 7.390935745817 l
+230.218750000000 7.343750000000 l
+230.358717033870 7.339828770678 l
+229.540398296709 7.582784597075 l
+229.732876939152 7.336891256731 l
+227.576626939152 10.477516256731 l
+227.866875111067 8.213016088905 l
+240.116875111067 42.119266088905 l
+240.265625000000 42.968750000000 l
+242.834476684803 54.624010420679 l
+242.829463828252 54.603904700294 l
+244.534977630831 62.545346122432 l
+245.453125000000 69.140625000000 l
+244.611402180226 71.735213821357 l
+242.306187063936 73.585381035651 l
+238.867409665678 74.693638982119 l
+234.625000000000 75.062500000000 l
+229.906486871974 75.367549636876 l
+229.092373983240 75.779215997735 l
+228.934535185216 76.058687905577 l
+228.890625000000 76.390625000000 l
+229.790856054287 82.319994820356 l
+232.571933261722 91.218385945649 l
+232.574034113306 91.224265697666 l
+234.986992254083 96.851263251065 l
+237.441996845067 100.266302709491 l
+237.439317291842 100.263712540141 l
+244.074197100624 106.275959413741 l
+247.409673148640 108.432844267021 l
+246.875000000000 108.375000000000 l
+251.578125000000 108.375000000000 l
+249.088156711733 111.098736280975 l
+248.501059746180 102.338409667587 l
+248.503744619215 102.402406116755 l
+248.000000000000 86.578125000000 l
+248.204678607058 80.674424141238 l
+248.635199756080 78.226325931503 l
+249.564103209371 76.447917337745 l
+249.529108047034 76.482233047034 l
+251.855421271850 75.142541341903 l
+254.812500000000 74.671875000000 l
+258.571857984945 75.077633347234 l
+263.279470079064 76.245639842274 l
+263.205056758774 76.224611587009 l
+267.479406097516 77.683738985792 l
+266.973101252767 77.547023420208 l
+268.046875000000 77.703125000000 l
+268.247054721136 77.733964370993 l
+268.227077605757 77.803800527161 l
+268.040685564021 77.924371454157 l
+267.977871868973 78.021719855177 l
+268.985855115250 76.421460145957 l
+269.308567902139 76.015446472685 l
+269.418034045008 75.973989481328 l
+269.375000000000 76.000000000000 l
+269.375000000000 74.921875000000 l
+107.893088734820 103.841923410053 l
+243.911209450369 105.662056821622 l
+240.832004992147 102.845101845416 l
+236.815516984936 97.742848726260 l
+236.813922800164 97.740633958664 l
+232.773597278734 91.287437871638 l
+230.710552894044 85.924483215760 l
+230.745327869205 86.067653416413 l
+229.840275149638 82.415038683708 l
+229.855893749637 82.481339062591 l
+229.093750000000 78.546875000000 l
+229.510257350732 77.264688549054 l
+230.557731326090 76.286764971873 l
+233.333290689663 75.138571514324 l
+238.843750000000 74.468750000000 l
+241.980836948934 74.687319511759 l
+245.189547085049 75.561169162745 l
+247.167986856352 77.549353232692 l
+248.217702574415 80.638507879225 l
+248.687500000000 88.140625000000 l
+248.194671228610 98.736670034806 l
+248.200295344311 98.650162873539 l
+247.698246960594 103.850772804200 l
+243.911209450369 105.662056821622 l
+107.893088734820 103.841923410053 l
+242.739253039406 103.211727195800 l
+243.205954655689 98.412337126461 l
+243.211578771390 98.325829965194 l
+243.687500000000 88.140625000000 l
+243.282297425585 81.439617120775 l
+243.185452914951 80.141955837255 l
+241.300413051066 79.640805488241 l
+238.843750000000 79.468750000000 l
+234.557334310337 79.986428485676 l
+234.323615830224 80.043375869145 l
+234.139295619945 80.082650511165 l
+233.899908001681 80.106538837663 l
+233.805549923910 79.924172528127 l
+234.093750000000 78.546875000000 l
+234.706606250363 81.268660937409 l
+234.722224850362 81.334961316292 l
+235.567172130795 84.744846583587 l
+235.601947105956 84.888016784240 l
+237.273277721266 89.165687128362 l
+240.873577199836 94.821866041336 l
+240.871983015064 94.819651273740 l
+244.355495007853 99.326773154584 l
+246.526290549631 101.400443178378 l
+242.739253039406 103.211727195800 l
+107.893088734820 103.841923410053 l
+304.579027849556 115.887559254375 l
+299.647333003501 118.498603127587 l
+299.601234810263 118.526555579345 l
+296.960609810263 120.089055579345 l
+295.687500000000 120.437500000000 l
+292.434178516679 119.763451801101 l
+289.591538470777 117.850158805700 l
+287.263626957927 114.860898415742 l
+285.554491073762 110.958948033170 l
+285.546168760441 110.932189402305 l
+283.409425227272 100.413450166388 l
+283.408069329655 100.405281467271 l
+280.968147929764 87.644193704042 l
+284.218299056292 87.914012723915 l
+279.519746335556 93.766171451312 l
+279.798545332977 93.224824860858 l
+274.937093489612 105.343910553875 l
+272.454127837569 109.762553039486 l
+269.772550129691 112.314814767144 l
+266.294988321147 110.999865652887 l
+264.699764075525 105.167535274279 l
+264.676753039406 105.022647804200 l
+264.221109604112 100.702343060691 l
+264.171875000000 98.828125000000 l
+263.607902205199 91.869081828698 l
+263.613915250645 91.062865437610 l
+263.781250000000 89.718750000000 l
+263.644962649638 88.985351183708 l
+262.873500431741 86.703985451358 l
+262.739182179139 86.219942105230 l
+260.786057179139 74.844942105230 l
+260.786249814804 74.846062487964 l
+256.958124814804 52.611687487964 l
+256.955473276820 52.595988114755 l
+254.892973276820 40.142863114755 l
+254.909488793847 40.232428789163 l
+254.331363793847 37.388678789163 l
+254.339079247810 37.425229542768 l
+253.651579247810 34.284604542768 l
+253.593750000000 33.750000000000 l
+254.239654341050 30.319069019177 l
+254.154681406546 30.589185956325 l
+256.319133024351 26.771372253672 l
+258.818704827809 25.924919245868 l
+261.217531024880 26.988383072536 l
+263.253153025566 28.888095452080 l
+264.856582863559 31.480336604342 l
+265.958832572550 34.621386749163 l
+266.013105788729 34.918097636045 l
+267.200714432551 44.731495841479 l
+267.198350954149 44.712535224402 l
+269.443623734153 61.037071609407 l
+269.421396014347 60.906344576785 l
+270.652021489737 67.736566992690 l
+271.992261513603 76.440263813345 l
+271.992747064473 76.443809223415 l
+273.038636247453 85.686082548185 l
+273.390625000000 92.953125000000 l
+273.238407027301 96.898129287691 l
+272.771685367998 99.590064317759 l
+267.989237947004 98.132898258783 l
+271.223612947004 86.961023258783 l
+271.391159168632 86.533772766328 l
+274.526090303798 81.379570646076 l
+274.584318175231 81.310286998919 l
+276.982682022500 78.600716011250 l
+277.142029400154 78.326905342657 l
+279.265919333198 75.301911531816 l
+279.298622345235 75.258362091268 l
+281.876153531632 73.009942282517 l
+284.906250000000 72.125000000000 l
+287.318073492888 72.767987310135 l
+289.339116026344 74.544184270464 l
+290.903803647217 77.224443425871 l
+291.946562402359 80.579617321243 l
+291.963278527291 80.666832095730 l
+293.477578855570 88.491377103079 l
+294.532438780850 92.026218792971 l
+294.690170210143 92.371639700252 l
+298.832669585147 102.687317191341 l
+298.821447919079 102.663322107333 l
+301.483679194839 107.248880594573 l
+302.541048238599 108.202972500861 l
+303.515625000000 108.484375000000 l
+306.015625000000 110.984375000000 l
+306.015625000000 113.625000000000 l
+304.579027849556 115.887559254375 l
+107.893088734820 103.841923410053 l
+301.015625000000 113.625000000000 l
+301.015625000000 110.984375000000 l
+303.515625000000 113.484375000000 l
+300.984342386401 112.859527499139 l
+298.555383305161 111.094869405427 l
+296.303526841571 108.355227332168 l
+294.303552080921 104.805427892667 l
+294.292330414853 104.781432808659 l
+289.997329789857 94.097110299748 l
+290.155061219150 94.442531207029 l
+288.709921144430 89.953935396921 l
+287.036721472709 81.520667904270 l
+287.053437597641 81.607882678757 l
+286.004633973656 77.940190729536 l
+284.906250000000 77.125000000000 l
+284.500063101353 77.160595459408 l
+284.155096468369 77.318182717483 l
+283.263877654765 78.304137908732 l
+283.296580666802 78.260588468184 l
+281.295470599846 81.110594657343 l
+281.454817977500 80.836783988750 l
+278.353181824769 84.595963001081 l
+278.411409696202 84.526679353924 l
+275.858840831368 88.778727233672 l
+276.026387052996 88.351476741217 l
+272.792012052996 99.523351741217 l
+268.009564632002 98.066185682241 l
+268.390625000000 92.953125000000 l
+268.055113752547 86.056104951815 l
+267.038502935527 77.118690776585 l
+267.038988486397 77.122236186655 l
+265.722978510263 68.575933007310 l
+264.516103985653 61.874905423215 l
+264.493876265847 61.744178390593 l
+262.239149045851 45.349964775598 l
+262.236785567449 45.331004158521 l
+261.049394211271 35.519402363955 l
+261.103667427450 35.816113250837 l
+259.699971974434 32.150967047920 l
+257.681295172191 30.793830754132 l
+260.180866975649 29.947377746328 l
+259.001568593454 31.817064043675 l
+258.916595658950 32.087180980823 l
+258.593750000000 33.750000000000 l
+258.535920752190 33.215395457232 l
+259.223420752190 36.356020457232 l
+259.231136206153 36.392571210837 l
+259.809261206153 39.236321210837 l
+259.825776723180 39.325886885245 l
+261.888276723180 51.779011885245 l
+261.885625185196 51.763312512036 l
+265.713750185196 73.997687512036 l
+265.713942820861 73.998807894770 l
+267.667067820861 85.373807894770 l
+267.532749568259 84.889764548642 l
+268.526912350362 87.905273816292 l
+268.781250000000 89.718750000000 l
+268.542334749355 91.905884562390 l
+268.548347794801 91.099668171302 l
+269.171875000000 98.828125000000 l
+269.216390395888 100.485156939309 l
+269.635746960594 104.383602195800 l
+269.612735924475 104.238714725721 l
+270.955011678853 109.187634347113 l
+267.477449870309 107.872685232856 l
+268.686497162431 106.569478210514 l
+270.375406510388 103.296714446125 l
+275.107704667023 91.493925139142 l
+275.386503664444 90.952578548688 l
+278.700840527029 86.470174071108 l
+281.281700943708 83.867237276085 l
+284.531852070236 84.137056295958 l
+285.408419686884 85.805066273498 l
+286.324716189650 88.997159604327 l
+288.341930670345 99.594718532729 l
+288.340574772728 99.586549833612 l
+290.328831239559 109.474060597695 l
+290.320508926238 109.447301966830 l
+291.419966792073 112.312929709258 l
+292.611586529223 114.157653694300 l
+293.999415233321 115.144751323899 l
+295.687500000000 115.437500000000 l
+294.414390189737 115.785944420655 l
+297.055015189737 114.223444420655 l
+297.008916996499 114.251396872413 l
+302.452222150444 111.362440745625 l
+301.015625000000 113.625000000000 l
+107.893088734820 103.841923410053 l
+374.437500000000 106.765625000000 l
+373.775309600600 109.463093710969 l
+372.064906958700 112.117295697199 l
+372.041004786215 112.144602045402 l
+368.522704503395 114.706400007155 l
+366.442611342446 115.399541345014 l
+364.281250000000 115.640625000000 l
+355.911205219123 114.726652651698 l
+341.052591424867 111.966274658224 l
+341.038040421249 111.963294783992 l
+326.905309277070 108.757693347762 l
+319.727026481732 106.439595965247 l
+319.807037817648 106.479160754059 l
+310.484453831953 101.696158824691 l
+304.482826587959 97.721718623918 l
+304.310358047034 97.564641952966 l
+302.095819571999 95.254869206705 l
+298.220153609739 90.667578419636 l
+300.367638895226 91.618501549714 l
+296.258263895226 91.915376549714 l
+296.078125000000 91.921875000000 l
+293.025957236218 91.709450913344 l
+290.151865257421 91.083888117023 l
+285.254658796565 88.663603177256 l
+285.219422230119 88.636374171395 l
+281.529900243089 84.536170132203 l
+280.513144708967 82.282593311151 l
+280.156250000000 80.015625000000 l
+280.896516775664 77.119922391977 l
+282.806537684315 74.726645730656 l
+285.448073147177 73.263018105533 l
+288.686079158715 72.443690273534 l
+290.750793112766 73.122099563886 l
+293.797668112766 76.059599563886 l
+293.736465601749 76.002539206463 l
+295.798965601749 77.861914206463 l
+294.222063773555 77.220634985862 l
+301.862688773555 77.517509985862 l
+299.265625000000 80.015625000000 l
+299.934685624955 74.754308742711 l
+301.898213943218 69.792099871114 l
+305.090729619888 65.230982029954 l
+309.446752320064 61.172938863978 l
+309.482902906976 61.145713152252 l
+318.065904047846 56.213131631660 l
+329.455658353400 51.953267244508 l
+329.496351456999 51.941186017188 l
+336.615241964605 50.572965088062 l
+336.666137267666 50.568817238037 l
+342.150512267666 50.178192238037 l
+342.328125000000 50.171875000000 l
+347.088673798588 50.461178987176 l
+351.635436696472 51.319590054257 l
+355.904450990616 52.732856859568 l
+359.831753977986 54.686728061438 l
+363.292951645549 57.250115143707 l
+365.880587968213 60.272932122861 l
+367.501493801268 63.647371305944 l
+368.062500000000 67.265625000000 l
+367.659428864193 69.524372763002 l
+366.507997055207 71.901249483478 l
+362.306741008310 76.690232523947 l
+362.309449580203 76.687848273793 l
+355.067416613929 81.592460070084 l
+345.579434368324 85.619156329617 l
+345.543296906352 85.631025936969 l
+338.856518222681 87.434049611292 l
+331.674077585810 88.726763309129 l
+324.032609882622 89.505258086643 l
+315.968750000000 89.765625000000 l
+315.697255981477 89.750839477245 l
+313.837880981477 89.547714477245 l
+314.109375000000 89.562500000000 l
+315.203325765838 88.142787621838 l
+315.234375000000 87.750000000000 l
+315.261368553241 87.459370280378 l
+315.416652198286 87.453346686481 l
+316.557907675735 88.522399326740 l
+316.429841220906 88.403882310796 l
+319.273591220906 90.857007310796 l
+318.935343465338 90.611377991437 l
+331.091593465338 97.970752991437 l
+330.612118984109 97.746034726917 l
+334.956642245028 98.975789357391 l
+339.851927078584 99.850564497063 l
+351.453125000000 100.546875000000 l
+354.203872313810 100.381670729150 l
+355.159915151904 99.886038066767 l
+355.055436157189 100.055596900919 l
+357.900435727416 97.290434866877 l
+362.379803562479 95.425955323112 l
+363.015625000000 95.343750000000 l
+366.828125000000 95.343750000000 l
+368.997562855593 96.601357392681 l
+374.106937855593 105.523232392681 l
+374.437500000000 106.765625000000 l
+107.893088734820 103.841923410053 l
+369.768062144407 108.008017607319 l
+364.658687144407 99.086142607319 l
+366.828125000000 100.343750000000 l
+363.015625000000 100.343750000000 l
+363.651446437521 100.261544676888 l
+360.724564272584 101.303315133123 l
+359.413313842811 102.506903099081 l
+359.308834848096 102.676461933233 l
+358.021570604282 103.932262067178 l
+356.186752686190 104.829266770850 l
+351.453125000000 105.546875000000 l
+345.006190631125 105.353277680068 l
+339.085572921416 104.774435502937 l
+333.730857754972 103.813273142609 l
+328.981631015891 102.472715273083 l
+328.502156534662 102.247997008563 l
+316.345906534662 94.888622008563 l
+316.007658779094 94.642992689204 l
+313.163908779094 92.189867689204 l
+313.035842324265 92.071350673260 l
+311.083347801714 89.820090813519 l
+310.234375000000 87.750000000000 l
+310.265424234162 87.357212378162 l
+310.780311953087 86.191927459145 l
+311.752715552710 85.312201869144 l
+314.109375000000 84.562500000000 l
+314.380869018523 84.577285522755 l
+316.240244018523 84.780410522755 l
+315.968750000000 84.765625000000 l
+323.705671367377 84.524038788357 l
+330.935297414190 83.796674190871 l
+337.694263027319 82.579622263708 l
+344.019203093648 80.868974063031 l
+343.983065631676 80.880843670383 l
+352.635708386071 77.251289929916 l
+359.003050419797 72.937151726207 l
+359.005758991690 72.934767476053 l
+362.163877944793 69.637813016522 l
+363.062500000000 67.265625000000 l
+362.747529636232 64.880949006556 l
+361.740505781787 62.777849127139 l
+359.948259291951 60.848517668793 l
+357.277621022014 58.985146938562 l
+353.920744321884 57.295463452932 l
+350.352844553528 56.106191195743 l
+346.509959013911 55.403078825324 l
+342.328125000000 55.171875000000 l
+342.505737732334 55.165557761963 l
+337.021362732334 55.556182761963 l
+337.072258035395 55.552034911938 l
+330.878648543001 56.746313982812 l
+330.919341646600 56.734232755492 l
+320.246595952154 60.693118368340 l
+312.454597093024 65.166786847748 l
+312.490747679936 65.139561136022 l
+308.794036005112 68.485814845046 l
+306.234598556782 72.012587628886 l
+304.746955000045 75.821863132289 l
+304.265625000000 80.015625000000 l
+301.668561226445 82.513740014138 l
+294.027936226445 82.216865014138 l
+292.451034398251 81.575585793537 l
+290.388534398251 79.716210793537 l
+290.327331887234 79.659150436114 l
+287.280456887234 76.721650436114 l
+289.345170841285 77.400059726466 l
+287.321458102823 77.850263144467 l
+285.927837315685 78.632729269344 l
+285.271451974336 79.274608858023 l
+285.175338636761 79.581613557521 l
+285.156250000000 80.015625000000 l
+285.837287256911 82.245079867797 l
+288.311827769881 84.707375828605 l
+288.276591203435 84.680146822744 l
+291.660634742579 86.384861882977 l
+296.078125000000 86.921875000000 l
+295.897986104774 86.928373450286 l
+300.007361104774 86.631498450286 l
+302.154846390261 87.582421580364 l
+305.732305428001 91.823255793295 l
+307.845891952966 94.029108047034 l
+307.673423412041 93.872031376082 l
+313.085858668047 97.436653675309 l
+321.942962182352 101.958339245941 l
+322.022973518268 101.997904034753 l
+328.415003222930 104.000119152238 l
+342.055709578751 107.067955216008 l
+342.041158575133 107.064975341776 l
+356.698169780877 109.788972348302 l
+364.281250000000 110.640625000000 l
+366.516357996605 110.246724992845 l
+368.302745213785 108.824147954598 l
+368.278843041300 108.851454302801 l
+369.437500000000 106.765625000000 l
+369.768062144407 108.008017607319 l
+107.893088734820 103.841923410053 l
+359.034454681034 69.861443602839 l
+357.173548986071 72.638610976591 l
+353.435826646756 75.215386191513 l
+347.992418306797 77.582163291606 l
+341.014454609897 79.729336320871 l
+326.426284670840 82.761098429090 l
+319.640717587115 83.525315463381 l
+313.234375000000 83.781250000000 l
+312.706760041223 83.724940335389 l
+311.331760041223 83.428065335389 l
+311.389194323030 83.439762979730 l
+309.920444323030 83.158512979730 l
+310.390625000000 83.203125000000 l
+309.032298883605 83.610801187634 l
+305.860259182280 80.222501196670 l
+308.487654632717 75.272571710106 l
+311.451975717975 70.927537861140 l
+314.718319876723 67.239682598354 l
+318.251784547635 64.261288870324 l
+318.235984828743 64.272646821685 l
+322.460311501394 61.735717754517 l
+327.134482463160 59.899177687483 l
+332.183220900533 58.782772982128 l
+337.531250000000 58.406250000000 l
+345.475041381297 58.786142249068 l
+351.020038715603 59.929523334009 l
+351.130689531816 59.972424607386 l
+353.699762084814 61.379594420291 l
+355.915109055748 63.288105475323 l
+357.703808635445 65.619192847673 l
+358.992939014732 68.294091612533 l
+359.034454681034 69.861443602839 l
+107.893088734820 103.841923410053 l
+354.288310985268 69.987158387467 l
+352.366140944252 66.657207024677 l
+349.213060468184 64.590075392614 l
+349.323711284397 64.632976665991 l
+344.735896118703 63.713857750932 l
+337.531250000000 63.406250000000 l
+332.857794724467 63.703555142872 l
+328.623330036840 64.608634812517 l
+324.752579123606 66.141235370483 l
+321.170265171257 68.321103178315 l
+321.154465452365 68.332461129676 l
+318.096133248277 70.895083026646 l
+315.305836782025 74.049024638860 l
+312.748673492282 77.846568914895 l
+310.389740817720 82.339998803330 l
+307.217701116395 78.951698812366 l
+310.390625000000 78.203125000000 l
+310.860805676970 78.247737020270 l
+312.329555676970 78.528987020270 l
+312.386989958777 78.540684664611 l
+313.761989958777 78.837559664611 l
+313.234375000000 78.781250000000 l
+319.326079287885 78.541090786619 l
+325.753402829160 77.817026570910 l
+339.735545390103 74.895663679129 l
+350.446985853244 71.354926308487 l
+353.125279138929 69.816467148409 l
+354.246795318966 68.419806397161 l
+354.288310985268 69.987158387467 l
+107.893088734820 103.841923410053 l
+412.906150029990 61.293260146951 l
+409.761470127352 62.134684085464 l
+407.280172710472 63.259435125605 l
+407.285792255081 63.256184676520 l
+404.793374271599 64.364965077547 l
+402.406250000000 64.781250000000 l
+400.799037804796 64.455489160260 l
+399.281565012266 63.565560595750 l
+396.896451926356 60.617324020679 l
+395.843750000000 59.187500000000 l
+394.132759007664 59.586356110398 l
+392.227223295325 60.862510968959 l
+387.514233459670 66.524236094764 l
+387.523930329881 66.510005317299 l
+384.972470502350 70.813073903396 l
+383.182220364063 75.072391356081 l
+382.127155126215 79.356608210051 l
+381.781250000000 83.734375000000 l
+382.036891605602 86.926476407739 l
+382.837535469594 90.232985146136 l
+382.953125000000 90.984375000000 l
+382.953125000000 110.781250000000 l
+380.453125000000 113.281250000000 l
+374.875000000000 113.281250000000 l
+372.797350977108 112.469290573921 l
+370.771127450416 110.197059087430 l
+366.940746762890 102.253660683699 l
+366.931766441279 102.229357657717 l
+364.988344509233 95.969248633492 l
+363.592635453443 89.400212341223 l
+362.750737781251 82.563409292771 l
+362.468750000000 75.500000000000 l
+362.469585639102 75.564633561058 l
+362.375835639102 71.939633561058 l
+362.376194806642 71.952282634846 l
+362.282444806642 68.921032634846 l
+362.281250000000 68.843750000000 l
+362.557974048920 65.558176179264 l
+363.371209408870 62.925233614273 l
+365.078767647837 60.585701488502 l
+367.830147985199 58.062445160283 l
+369.390625000000 57.515625000000 l
+372.609375000000 57.515625000000 l
+375.044088155839 59.448023159988 l
+375.637838155839 61.994898159988 l
+375.701153736333 62.463240580013 l
+375.794903736333 64.822615580013 l
+375.796875000000 64.921875000000 l
+375.796875000000 66.390625000000 l
+375.727843254951 66.974057381188 l
+375.364882971473 68.609156073692 l
+375.377354243352 68.535253718305 l
+374.986729243352 71.082128718305 l
+375.015625000000 70.703125000000 l
+375.946084438562 73.051058522014 l
+371.472688855144 73.407169646001 l
+374.338903268299 66.961197216653 l
+377.510670879106 61.308691210684 l
+380.962283852915 56.499192859154 l
+384.668034355078 52.582243393122 l
+384.668120639869 52.582165462116 l
+388.032154904598 50.002880989291 l
+391.626628158728 48.110057708285 l
+395.367786243461 46.944465688666 l
+399.171875000000 46.546875000000 l
+400.072873198005 46.714879663986 l
+404.885373198005 48.574254663986 l
+404.871260445955 48.568850974212 l
+412.883679720929 50.905386234424 l
+414.906250000000 53.359375000000 l
+414.906250000000 58.843750000000 l
+412.906150029990 61.293260146951 l
+107.893088734820 103.841923410053 l
+409.906250000000 58.843750000000 l
+409.906250000000 53.359375000000 l
+411.928820279071 55.813363765576 l
+403.097489554045 53.243649025788 l
+403.083376801995 53.238245336014 l
+398.270876801995 51.378870336014 l
+399.171875000000 51.546875000000 l
+396.227916881538 51.823112436334 l
+393.443684341272 52.679004791715 l
+390.735423220402 54.155322135709 l
+388.019379360131 56.292834537884 l
+388.019465644922 56.292756606878 l
+384.707638022085 59.784010265846 l
+381.637766620894 64.074121289316 l
+378.784143606701 69.212630908347 l
+376.121061144856 75.249080353999 l
+371.647665561438 75.605191477986 l
+370.464152438185 73.063686657060 l
+370.015625000000 70.703125000000 l
+370.044520756648 70.324121281695 l
+370.435145756648 67.777246281695 l
+370.447617028527 67.703343926308 l
+370.865906745049 65.807192618812 l
+370.796875000000 66.390625000000 l
+370.796875000000 64.921875000000 l
+370.798846263667 65.021134419987 l
+370.705096263667 62.661759419987 l
+370.768411844161 63.130101840012 l
+370.174661844161 60.583226840012 l
+372.609375000000 62.515625000000 l
+369.390625000000 62.515625000000 l
+370.951102014801 61.968804839717 l
+367.847540591130 65.152891385727 l
+367.281250000000 68.843750000000 l
+367.280055193358 68.766467365154 l
+367.373805193358 71.797717365154 l
+367.374164360898 71.810366438942 l
+367.467914360898 75.435366438942 l
+367.468750000000 75.500000000000 l
+367.725824718749 82.182684457229 l
+368.501114546557 88.552912658777 l
+369.800717990767 94.651845116508 l
+371.630733558721 100.520642342283 l
+371.621753237110 100.496339316301 l
+374.875000000000 108.281250000000 l
+380.453125000000 108.281250000000 l
+377.953125000000 110.781250000000 l
+377.953125000000 90.984375000000 l
+378.068714530406 91.735764853864 l
+377.111545894398 87.698523592261 l
+376.781250000000 83.734375000000 l
+377.206829248785 78.659016789949 l
+378.466217135937 73.583858643919 l
+380.533388872650 68.577551096604 l
+383.382319670119 63.708744682701 l
+383.392016540330 63.694513905236 l
+386.493443824758 59.714265008269 l
+389.655589204675 56.723426531041 l
+392.798881617336 54.841378264602 l
+395.843750000000 54.187500000000 l
+397.450962195204 54.513260839740 l
+398.968434987734 55.403189404250 l
+401.353548073644 58.351425979321 l
+402.406250000000 59.781250000000 l
+404.776707744919 58.931315323480 l
+404.782327289528 58.928064874395 l
+408.035404872648 57.459065914536 l
+411.906349970010 56.394239853049 l
+409.906250000000 58.843750000000 l
+107.893088734820 103.841923410053 l
+.0 .0 .8 set-source-rgb fill
+
+/target get (out.png) write-to-png pop
+pop
diff --git a/util/cairo-sphinx/.gitignore b/util/cairo-sphinx/.gitignore
new file mode 100644 (file)
index 0000000..56ecd5d
--- /dev/null
@@ -0,0 +1 @@
+cairo-sphinx
diff --git a/util/cairo-sphinx/Makefile.am b/util/cairo-sphinx/Makefile.am
new file mode 100644 (file)
index 0000000..b25bd23
--- /dev/null
@@ -0,0 +1,41 @@
+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
+cairo_sphinx_la_LIBADD = -ldl
+
+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 (file)
index 0000000..aeda89b
--- /dev/null
@@ -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 (file)
index 0000000..40d6776
--- /dev/null
@@ -0,0 +1,1533 @@
+#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
+
+#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,
+                                                   0, &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 (file)
index 0000000..b5f8666
--- /dev/null
@@ -0,0 +1 @@
+cairo-trace
diff --git a/util/cairo-trace/COPYING b/util/cairo-trace/COPYING
new file mode 100644 (file)
index 0000000..37aeee0
--- /dev/null
@@ -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 (file)
index 0000000..94a9ed0
--- /dev/null
@@ -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 (file)
index 0000000..3278abe
--- /dev/null
@@ -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 (file)
index 0000000..1fc3de6
--- /dev/null
@@ -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
+
+Enviroment 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 (file)
index 0000000..f9665b3
--- /dev/null
@@ -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 (file)
index 0000000..83817ed
--- /dev/null
@@ -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 (file)
index 0000000..7475958
--- /dev/null
@@ -0,0 +1,5171 @@
+/* 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 <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
+
+#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);  \
+})
+
+#define ARRAY_SIZE(a) (sizeof (a) / sizeof (a[0]))
+#define ARRAY_LENGTH(a) ((int) ARRAY_SIZE(a))
+
+#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;
+    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_SIZE (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->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);
+    _trace_printf ("%%*** Warning CairoScript is still a new tracing format, and is subject to change.\n");
+}
+
+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;
+
+       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
+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 (void)
+{
+    Object *obj;
+
+    ensure_operands (1);
+    obj = current_object[--current_stack_depth];
+    if (! 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_object (Object *obj)
+{
+    if (obj->operand == -1)
+       return FALSE;
+
+    if (obj->operand == current_stack_depth - 2) {
+       _exch_operands ();
+       _trace_printf ("exch ");
+       return TRUE;
+    }
+
+    while (current_stack_depth > obj->operand + 1) {
+       Object *c_obj;
+
+       ensure_operands (1);
+       c_obj = current_object[--current_stack_depth];
+       c_obj->operand = -1;
+       if (! c_obj->defined) {
+           _trace_printf ("/%s%ld exch def\n",
+                    c_obj->type->op_code,
+                    c_obj->token);
+           c_obj->defined = TRUE;
+       } else {
+           _trace_printf ("pop %% %s%ld\n",
+                          c_obj->type->op_code, c_obj->token);
+       }
+    }
+
+    return TRUE;
+}
+
+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)
+{
+
+    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;
+           }
+       }
+       current_stack_depth--;
+    }
+}
+
+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
+_get_context_id (cairo_t *cr)
+{
+    return _get_id (CONTEXT, cr);
+}
+
+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 long
+_create_scaled_font_id (cairo_scaled_font_t *font)
+{
+    Object *obj;
+
+    obj = _get_object (SCALED_FONT, font);
+    if (obj == 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 long
+_get_scaled_font_id (const cairo_scaled_font_t *font)
+{
+    return _get_id (SCALED_FONT, font);
+}
+
+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);
+    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)) {
+       _trace_printf ("%s%ld\n", obj->type->op_code, obj->token);
+       _push_object (obj);
+    }
+}
+
+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 ();
+           } else {
+               _trace_printf ("s%ld ", surface_id);
+           }
+           _trace_printf ("context %% c%ld\n", context_id);
+           _push_operand (CONTEXT, ret);
+       }
+       _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);
+
+    _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 ();
+       }
+       else if (_is_current (SURFACE, surface, 1) &&
+                _is_current (CONTEXT, cr, 0))
+       {
+           _trace_printf ("exch ");
+           _exch_operands ();
+           _consume_operand ();
+       } 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 ();
+               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 ();
+               need_context_and_pattern = FALSE;
+           }
+       }
+
+       if (need_context_and_pattern) {
+           _emit_context (cr);
+           _emit_pattern_id (source);
+       }
+
+       _trace_printf ("set-source\n");
+       _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 ();
+               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 ();
+               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 ();
+       }
+       else if (_is_current (SURFACE, surface, 1) &&
+                _is_current (CONTEXT, cr, 0))
+       {
+           _trace_printf ("exch ");
+           _exch_operands ();
+           _consume_operand ();
+       } 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);
+
+    _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 ();
+       }
+       else if (_is_current (FONT_FACE, font_face, 1) &&
+                _is_current (CONTEXT, cr, 0))
+       {
+           _trace_printf ("exch ");
+           _exch_operands ();
+           _consume_operand ();
+       }
+       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) {
+       if (_pop_operands_to (SCALED_FONT, scaled_font)) {
+           if (_is_current (CONTEXT, cr, 1)) {
+               if (_write_lock ()) {
+                   _consume_operand ();
+                   _trace_printf ("set-scaled-font\n");
+                   _write_unlock ();
+               }
+           } else {
+               if (_get_object (CONTEXT, cr)->defined) {
+                   if (_write_lock ()) {
+                       _consume_operand ();
+                       _trace_printf ("c%ld exch set-scaled-font pop\n",
+                                      _get_context_id (cr));
+                       _write_unlock ();
+                   }
+               } else {
+                   _emit_cairo_op (cr, "sf%ld set-scaled-font\n",
+                                   _get_scaled_font_id (scaled_font));
+               }
+           }
+       } else {
+           _emit_cairo_op (cr, "sf%ld set-scaled-font\n",
+                           _get_scaled_font_id (scaled_font));
+       }
+    }
+    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;
+    long scaled_font_id;
+
+    _enter_trace ();
+
+    ret = DLCALL (cairo_scaled_font_create, font_face, font_matrix, ctm, options);
+    scaled_font_id = _create_scaled_font_id (ret);
+
+    _emit_line_info ();
+    if (font_face != NULL &&
+       font_matrix != NULL &&
+       ctm != NULL &&
+       options != NULL
+       && _write_lock ())
+    {
+       if (_pop_operands_to (FONT_FACE, font_face))
+           _consume_operand ();
+       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);
+
+       if (_get_object (SCALED_FONT, ret)->defined) {
+           _trace_printf ("  scaled-font pop %% sf%ld\n",
+                          scaled_font_id);
+       } else {
+           _trace_printf ("  scaled-font dup /sf%ld exch def\n",
+                          scaled_font_id);
+           _push_operand (SCALED_FONT, ret);
+
+           _get_object (SCALED_FONT, ret)->defined = TRUE;
+       }
+
+       _write_unlock ();
+    }
+
+    _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);
+       _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);
+       _write_unlock ();
+    }
+
+    _exit_trace ();
+    return ret;
+}
+
+cairo_surface_t *
+cairo_mime_surface_create (void *data, cairo_content_t content, int width, int height)
+{
+    cairo_surface_t *ret;
+
+    _enter_trace ();
+
+    ret = DLCALL (cairo_mime_surface_create, data, content, width, height);
+
+    _emit_line_info ();
+    if (_write_lock ()) {
+       Object *obj = _create_surface (ret);
+       cairo_format_t format;
+       cairo_surface_t *image;
+       cairo_t *cr;
+
+       /* Impossible to accurately record the interaction with a mime-surface
+        * 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 ("%% mime-surface\n");
+
+       image = DLCALL (cairo_image_surface_create, format, width, height);
+       cr = DLCALL (cairo_create, image);
+       DLCALL (cairo_set_source_surface, cr, ret, 0, 0);
+       DLCALL (cairo_paint, cr);
+       DLCALL (cairo_destroy, cr);
+
+       _emit_image (image, NULL);
+       DLCALL (cairo_surface_destroy, image);
+       _trace_printf (" dup /s%ld exch def\n",
+                      obj->token);
+
+       obj->width = width;
+       obj->height = height;
+       obj->defined = TRUE;
+       _push_object (obj);
+       _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->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 ("%d %d //%s similar %% s%ld\n",
+                      width, height,
+                      _content_to_string (content),
+                      new_obj->token);
+
+       _push_object (new_obj);
+       _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%ld //%s %d %d similar-image %% s%ld\n",
+                      _get_surface_id (other),
+                      _format_to_string (format),
+                      width, height,
+                      new_obj->token);
+
+       _push_object (new_obj);
+       _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);
+
+       if (extents) {
+           _trace_printf ("[%d %d %d %d] map-to-image\n",
+                          extents->x, extents->y,
+                          extents->width, extents->height);
+           obj->width  = extents->width;
+           obj->height = extents->height;
+       } else {
+           _trace_printf ("[ ] map-to-image\n");
+       }
+       _push_object (obj);
+       _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 ()) {
+       _trace_printf ("/s%ld /s%ld  unmap-image\n",
+                      _get_surface_id (surface),
+                      _get_surface_id (image));
+       _consume_operand ();
+       _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);
+       _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 ();
+    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_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 ();
+       } 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);
+       _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);
+       _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);
+       _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;
+       FcChar8 *parsed;
+
+       obj = _get_object (FONT_FACE, ret);
+       if (obj->operand != -1)
+           _object_remove (obj);
+
+       parsed = DLCALL (FcNameUnparse, pattern);
+       _trace_printf ("dict\n"
+                      "  /type 42 set\n"
+                      "  /pattern ");
+       _emit_string_literal ((char *) parsed, -1);
+       _trace_printf (" set\n"
+                      "  font %% f%ld\n",
+                      font_face_id);
+       _push_operand (FONT_FACE, ret);
+       _write_unlock ();
+
+       free (parsed);
+    }
+
+    _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);
+       _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);
+       _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);
+       _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);
+       _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);
+       _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);
+       _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);
+       _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);
+       _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);
+       _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_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);
+       _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);
+       _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);
+       _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);
+       _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);
+       _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);
+       _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);
+       _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);
+       _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);
+       _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);
+       _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);
+       _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);
+       _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);
+       _write_unlock ();
+    }
+
+    _exit_trace ();
+    return ret;
+}
+#endif
diff --git a/util/cairo-view b/util/cairo-view
new file mode 100755 (executable)
index 0000000..2bbed69
--- /dev/null
@@ -0,0 +1,113 @@
+#!/usr/bin/python
+
+import sys
+import cairo
+import pygtk
+pygtk.require('2.0')
+import gtk
+import gtk.gdk
+import pango
+import gobject
+
+class CairoView(gtk.Window):
+    def __init__(self, family="", slant=0, weight=0, size=18, text="The Quick Brown Fox Jumps Over The Lazy Dog!"):
+        gtk.Widget.__init__ (self)
+
+       self.family = family
+       if slant == "italic":
+           self.slant = cairo.FONT_SLANT_ITALIC
+       elif slant == "oblique":
+           self.slant = cairo.FONT_SLANT_OBLIQUE
+       else:
+           self.slant = cairo.FONT_SLANT_NORMAL
+       if weight == "bold":
+           self.weight = cairo.FONT_WEIGHT_BOLD
+       else:
+           self.weight = cairo.FONT_WEIGHT_NORMAL
+       self.size = float (size)
+       self.text = text
+
+    def do_realize(self):
+        self.set_flags(self.flags() | gtk.REALIZED)
+
+        self.window = gtk.gdk.Window(
+            self.get_parent_window(),
+            width=self.allocation.width,
+            height=self.allocation.height,
+            window_type=gtk.gdk.WINDOW_CHILD,
+            wclass=gtk.gdk.INPUT_OUTPUT,
+            event_mask=self.get_events() | gtk.gdk.EXPOSURE_MASK)
+
+        self.window.set_user_data(self)
+
+        self.style.attach(self.window)
+
+        self.style.set_background(self.window, gtk.STATE_NORMAL)
+
+       self.width, self.height = self.draw ()
+        self.window.move_resize(0, 0, self.width, self.height)
+
+    def do_unrealize(self):
+        self.window.destroy()
+
+    def do_expose_event(self, event):
+       self.draw (event)
+
+        return False
+
+    def draw(self, event = None):
+
+        cr = self.window.cairo_create()
+       if event:
+               cr.rectangle(event.area.x, event.area.y,
+                            event.area.width, event.area.height)
+               cr.clip()
+
+       cr.set_source_rgb (1, 1, 1)
+       cr.paint ()
+
+       cr.select_font_face (self.family, self.slant, self.weight)
+       cr.set_font_size (self.size)
+
+       PAD = 30
+
+        extents = cr.text_extents (self.text)
+       cr.translate (PAD-extents[0], PAD-extents[1])
+
+       font_extents = cr.font_extents ()
+       cr.rectangle (0, -font_extents[0], extents[4], font_extents[2])
+       cr.move_to (-PAD, 0)
+       cr.line_to (extents[2]+PAD, 0)
+       cr.set_source_rgba (1, 0, 0, .7)
+       cr.stroke ()
+
+       cr.rectangle (*extents[:4])
+       cr.set_source_rgba (0, 1, 0, .7)
+       cr.stroke ()
+
+       cr.move_to (0, 0)
+        cr.set_source_rgb (0, 0, 0)
+        cr.show_text (self.text)
+
+       return int (extents[2]) + 2 * PAD, int (extents[3]) + 2 * PAD
+
+    def run(self):
+
+       self.props.allow_shrink = True
+        self.connect("destroy", gtk.main_quit)
+        self.show()
+
+        gtk.main()
+
+gobject.type_register(CairoView)
+
+def main(args):
+
+    if len (args) == 1:
+       print "usage: cairo-view family [slant [weight [size [text]]]]"
+       sys.exit (1)
+    cv= CairoView (*args[1:])
+    cv.run()
+
+if __name__ == "__main__":
+    main(sys.argv)
diff --git a/util/cairo.modules b/util/cairo.modules
new file mode 100644 (file)
index 0000000..71a3922
--- /dev/null
@@ -0,0 +1,20 @@
+<?xml version="1.0" standalone="no"?> <!--*- mode: nxml -*-->
+<!DOCTYPE moduleset SYSTEM "moduleset.dtd">
+<?xml-stylesheet type="text/xsl" href="moduleset.xsl"?>
+<moduleset>
+  <include href="http://cgit.freedesktop.org/xorg/util/modular/plain/xorg.modules" />
+
+  <repository type="git" name="git.freedesktop.org" href="git://anongit.freedesktop.org/git/"/>
+
+  <autotools id="cairo" autogenargs="--enable-gl">
+    <branch repo="git.freedesktop.org" module="cairo"/>
+    <dependencies>
+      <dep package="pixman"/>
+      <dep package="fontconfig"/>
+      <dep package="libGL"/>
+    </dependencies>
+    <after>
+      <dep package="libXrender"/>
+    </after>
+  </autotools>
+</moduleset>
diff --git a/util/font-view.c b/util/font-view.c
new file mode 100644 (file)
index 0000000..07d9e2e
--- /dev/null
@@ -0,0 +1,184 @@
+/*
+ * Copyright © 2008 Behdad Esfahbod
+ * Copyright © 2009 Chris Wilson
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Chris Wilson not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Chris Wilson makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * CHRIS WILSON DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL CHRIS WILSON BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include <gtk/gtk.h>
+#include <cairo.h>
+
+struct options {
+    const char *text;
+    const char *family;
+    cairo_font_weight_t weight;
+    cairo_font_slant_t slant;
+    double size;
+    int PAD;
+    const char *png;
+};
+
+static void
+draw (cairo_t *cr, struct options *options)
+{
+    cairo_text_extents_t extents;
+    cairo_font_extents_t font_extents;
+
+    cairo_select_font_face (cr,
+                           options->family, options->slant, options->weight);
+    cairo_set_font_size (cr, options->size);
+
+    cairo_text_extents (cr, options->text, &extents);
+    cairo_translate (cr,
+                    options->PAD - extents.x_bearing,
+                    options->PAD - extents.y_bearing);
+
+    cairo_font_extents (cr, &font_extents);
+    cairo_rectangle (cr, 0, -font_extents.ascent,
+                     extents.x_advance, font_extents.height);
+    cairo_move_to (cr, -options->PAD, 0);
+    cairo_line_to (cr, extents.width + options->PAD, 0);
+    cairo_set_source_rgba (cr, 1, 0, 0, .7);
+    cairo_stroke (cr);
+
+    cairo_rectangle (cr,
+                    extents.x_bearing, extents.y_bearing,
+                    extents.width, extents.height);
+    cairo_set_source_rgba (cr, 0, 1, 0, .7);
+    cairo_stroke (cr);
+
+    cairo_move_to (cr, 0, 0);
+    cairo_set_source_rgb (cr, 0, 0, 1);
+    cairo_show_text (cr, options->text);
+    cairo_fill (cr);
+}
+
+static gboolean
+expose_event (GtkWidget *w, GdkEventExpose *ev, struct options *options)
+{
+    cairo_t *cr;
+
+    cr = gdk_cairo_create (w->window);
+
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_paint (cr);
+
+    draw (cr, options);
+
+    cairo_destroy (cr);
+
+    if (options->png) {
+       cairo_surface_t *image;
+
+       image = cairo_image_surface_create (CAIRO_FORMAT_RGB24,
+                                           w->allocation.width,
+                                           w->allocation.height);
+       cr = cairo_create (image);
+       cairo_set_source_rgb (cr, 1, 1, 1);
+       cairo_paint (cr);
+
+       draw (cr, options);
+
+       cairo_destroy (cr);
+       cairo_surface_write_to_png (image, options->png);
+       cairo_surface_destroy (image);
+    }
+
+    return TRUE;
+}
+
+static void
+size_request (GtkWidget *w, GtkRequisition *req , struct options *options)
+{
+    cairo_surface_t *dummy;
+    cairo_t *cr;
+    cairo_text_extents_t extents;
+
+    dummy = cairo_image_surface_create (CAIRO_FORMAT_RGB24, 0, 0);
+    cr = cairo_create (dummy);
+    cairo_surface_destroy (dummy);
+
+    cairo_select_font_face (cr,
+                           options->family, options->slant, options->weight);
+    cairo_set_font_size (cr, options->size);
+
+    cairo_text_extents (cr, options->text, &extents);
+    cairo_destroy (cr);
+
+    req->width = extents.width + 2 * options->PAD;
+    req->height = extents.height + 2 * options->PAD;
+}
+
+int
+main (int argc, char **argv)
+{
+    GtkWidget *window;
+    struct options options = {
+       "The Quick Brown Fox Jumps Over The Lazy Dog!",
+       "@cairo:small-caps",
+       CAIRO_FONT_WEIGHT_NORMAL,
+       CAIRO_FONT_SLANT_NORMAL,
+       48,
+       30,
+       "font-view.png"
+    };
+
+    gtk_init (&argc, &argv);
+
+    /* rudimentary argument processing */
+    if (argc >= 2) {
+       options.family = argv[1];
+    }
+    if (argc >= 3) {
+       if (strcmp (argv[2], "italic") == 0)
+           options.slant = CAIRO_FONT_SLANT_ITALIC;
+       else if (strcmp (argv[2], "oblique") == 0)
+           options.slant = CAIRO_FONT_SLANT_OBLIQUE;
+       else
+           options.slant = atoi (argv[2]);
+    }
+    if (argc >= 4) {
+       if (strcmp (argv[3], "bold") == 0)
+           options.weight = CAIRO_FONT_WEIGHT_BOLD;
+       else
+           options.weight = atoi (argv[3]);
+    }
+    if (argc >= 5) {
+       options.size = atof (argv[4]);
+    }
+    if (argc >= 6) {
+       options.text = argv[5];
+    }
+
+    window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+    g_signal_connect (window, "size-request",
+                     G_CALLBACK (size_request), &options);
+    g_signal_connect (window, "expose-event",
+                     G_CALLBACK (expose_event), &options);
+    g_signal_connect (window, "delete-event",
+                     G_CALLBACK (gtk_main_quit), NULL);
+
+    gtk_window_present (GTK_WINDOW (window));
+    gtk_main ();
+
+    return 0;
+}
diff --git a/util/malloc-stats.c b/util/malloc-stats.c
new file mode 100644 (file)
index 0000000..d189260
--- /dev/null
@@ -0,0 +1,367 @@
+/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/*
+ * Copyright © 2007 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Behdad Esfahbod <behdad@behdad.org>
+ */
+
+/* A simple malloc wrapper that prints out statistics on termination */
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+
+/* caller-logging */
+
+#include <string.h>
+
+struct alloc_stat_t {
+       unsigned int num;
+       unsigned long long size;
+};
+
+struct alloc_stats_t {
+       struct alloc_stat_t malloc, realloc, total;
+};
+
+struct func_stat_t {
+       struct func_stat_t *next;
+
+       const void *addr;
+       const char *name;
+
+       struct alloc_stats_t stat;
+};
+
+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]))
+
+static void
+alloc_stats_add (struct alloc_stats_t *stats, int is_realloc, size_t size)
+{
+       struct alloc_stat_t *stat = is_realloc ? &stats->realloc : &stats->malloc;
+
+       stats->total.num++;
+       stats->total.size += size;
+
+       stat->num++;
+       stat->size += size;
+}
+
+#include <execinfo.h>
+
+static void *
+_perm_alloc (size_t size)
+{
+    static uint8_t *ptr;
+    static size_t rem;
+
+    void *ret;
+
+#define SUPERBLOCK_SIZE (1<<23)
+#define align(x, y) (((x) + ((y)-1)) & ~((y)-1))
+
+    size = align (size, 2 * sizeof (void *));
+    if (size > rem || rem == 0) {
+       ptr = malloc (SUPERBLOCK_SIZE);
+       if (ptr == NULL)
+           exit (1);
+       rem = SUPERBLOCK_SIZE;
+    }
+
+#undef SUPERBLOCK_SIZE
+#undef align
+
+    ret = ptr;
+    rem -= size;
+    ptr += size;
+
+    return ret;
+}
+
+static void
+resolve_addrs (struct func_stat_t *func_stats, int num)
+{
+       int i;
+       void **addrs;
+       char **strings;
+
+       addrs = malloc (num * sizeof (void *));
+       for (i = 0; i < num; i++)
+               addrs[i] = (void *) func_stats[i].addr;
+
+       strings = backtrace_symbols (addrs, num);
+
+       for (i = 0; i < num; i++) {
+               char *p;
+               char *name;
+               int len;
+
+               p = strchr (strings[i], '\t');
+               if (p)
+                       p++;
+               else
+                       p = strings[i];
+
+               len = strlen (p) + 1;
+               name = _perm_alloc (len);
+               memcpy (name, p, len);
+               func_stats[i].name = name;
+       }
+
+       free (strings);
+       free (addrs);
+}
+
+static void
+func_stats_add (const void *caller, int is_realloc, size_t size)
+{
+       int i;
+       struct func_stat_t *elt;
+
+       alloc_stats_add (&total_allocations, is_realloc, size);
+
+       i = ((uintptr_t) caller ^ 1215497) % ARRAY_SIZE (func_stats);
+       for (elt = func_stats[i]; elt != NULL; elt = elt->next) {
+               if (elt->addr == caller)
+                       break;
+       }
+
+       if (elt == NULL) {
+               func_stats_num++;
+
+               elt = _perm_alloc (sizeof (struct func_stat_t));
+               elt->next = func_stats[i];
+               func_stats[i] = elt;
+               elt->addr = caller;
+               elt->name = NULL;
+               memset (&elt->stat, 0, sizeof (struct alloc_stats_t));
+       }
+
+       alloc_stats_add (&elt->stat, is_realloc, size);
+}
+
+/* wrapper stuff */
+
+#include <malloc.h>
+
+static void *(*old_malloc)(size_t, const void *);
+static void *(*old_realloc)(void *, size_t, const void *);
+
+static void *my_malloc(size_t, const void *);
+static void *my_realloc(void *, size_t, const void *);
+
+static void
+save_hooks (void)
+{
+       old_malloc  = __malloc_hook;
+       old_realloc = __realloc_hook;
+}
+
+static void
+old_hooks (void)
+{
+       __malloc_hook  = old_malloc;
+       __realloc_hook  = old_realloc;
+}
+
+static void
+my_hooks (void)
+{
+       /* should always save the current value */
+       save_hooks ();
+
+       __malloc_hook  = my_malloc;
+       __realloc_hook  = my_realloc;
+}
+
+static void *
+my_malloc(size_t size, const void *caller)
+{
+       void *ret;
+
+       old_hooks ();
+
+       func_stats_add (caller, 0, size);
+
+       ret = malloc (size);
+       my_hooks ();
+
+       return ret;
+}
+
+static void *
+my_realloc(void *ptr, size_t size, const void *caller)
+{
+       void *ret;
+
+       old_hooks ();
+
+       func_stats_add (caller, 1, size);
+
+       ret = realloc (ptr, size);
+       my_hooks ();
+
+       return ret;
+}
+
+static void
+my_init_hook(void) {
+       my_hooks ();
+}
+
+void (*__malloc_initialize_hook) (void) = my_init_hook;
+
+
+/* reporting */
+
+#include <locale.h>
+
+static void
+add_alloc_stats (struct alloc_stats_t *a, struct alloc_stats_t *b)
+{
+       a->total.num += b->total.num;
+       a->total.size += b->total.size;
+       a->malloc.num += b->malloc.num;
+       a->malloc.size += b->malloc.size;
+       a->realloc.num += b->realloc.num;
+       a->realloc.size += b->realloc.size;
+}
+
+static void
+dump_alloc_stats (struct alloc_stats_t *stats, const char *name)
+{
+       printf ("%8u %'11llu    %8u %'11llu     %8u %'11llu     %s\n",
+               stats->total.num, stats->total.size,
+               stats->malloc.num, stats->malloc.size,
+               stats->realloc.num, stats->realloc.size,
+               name);
+}
+
+static int
+compare_func_stats_name (const void *pa, const void *pb)
+{
+       const struct func_stat_t *a = pa, *b = pb;
+       int i;
+
+       i = strcmp (a->name, b->name);
+       if (i)
+               return i;
+
+       return ((char *) a->addr - (char *) b->addr);
+}
+
+static int
+compare_func_stats (const void *pa, const void *pb)
+{
+       const struct func_stat_t *a = pa, *b = pb;
+
+       if (a->stat.total.num != b->stat.total.num)
+               return (a->stat.total.num - b->stat.total.num);
+
+       if (a->stat.total.size != b->stat.total.size)
+               return (a->stat.total.size - b->stat.total.size);
+
+       return compare_func_stats_name (pa, pb);
+}
+
+static int
+merge_similar_entries (struct func_stat_t *func_stats, int num)
+{
+       int i, j;
+
+       j = 0;
+       for (i = 1; i < num; i++) {
+               if (i != j && 0 == strcmp (func_stats[i].name, func_stats[j].name)) {
+                       add_alloc_stats (&func_stats[j].stat, &func_stats[i].stat);
+               } else {
+                       j++;
+                       if (i != j)
+                               func_stats[j] = func_stats[i];
+               }
+       }
+       j++;
+
+       return j;
+}
+
+__attribute__ ((destructor))
+void
+malloc_stats (void)
+{
+       unsigned int i, j;
+       struct func_stat_t *sorted_func_stats;
+
+       old_hooks ();
+
+       if (! func_stats_num)
+               return;
+
+       sorted_func_stats = malloc (sizeof (struct func_stat_t) * (func_stats_num + 1));
+       if (sorted_func_stats == NULL)
+               return;
+
+       j = 0;
+       for (i = 0; i < ARRAY_SIZE (func_stats); i++) {
+               struct func_stat_t *elt;
+               for (elt = func_stats[i]; elt != NULL; elt = elt->next)
+                       sorted_func_stats[j++] = *elt;
+       }
+
+       resolve_addrs (sorted_func_stats, j);
+
+       /* merge entries with same name */
+       qsort (sorted_func_stats, j,
+              sizeof (struct func_stat_t), compare_func_stats_name);
+       j = merge_similar_entries (sorted_func_stats, j);
+
+       qsort (sorted_func_stats, j,
+              sizeof (struct func_stat_t), compare_func_stats);
+
+       /* add total */
+       sorted_func_stats[j].next = NULL;
+       sorted_func_stats[j].addr = (void *) -1;
+       sorted_func_stats[j].name = "(total)";
+       sorted_func_stats[j].stat = total_allocations;
+       j++;
+
+       setlocale (LC_ALL, "");
+
+       printf ("        TOTAL                   MALLOC                 REALLOC\n");
+       printf ("     num       size         num        size         num        size\n");
+
+       for (i = 0; i < j; i++) {
+               dump_alloc_stats (&sorted_func_stats[i].stat,
+                                 sorted_func_stats[i].name);
+       }
+
+       /* XXX free other stuff? */
+
+       free (sorted_func_stats);
+}
diff --git a/util/show-contour.c b/util/show-contour.c
new file mode 100644 (file)
index 0000000..f3fa1ba
--- /dev/null
@@ -0,0 +1,667 @@
+#define _GNU_SOURCE
+#include <gtk/gtk.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <gdk/gdkkeysyms.h>
+#include <math.h>
+
+typedef struct _point {
+    gdouble x, y;
+} point_t;
+typedef struct _box {
+    point_t p1, p2;
+} box_t;
+
+typedef struct _contour {
+    struct _contour *next, *prev;
+    int direction;
+    int num_points;
+    int size;
+    point_t points[0];
+} contour_t;
+
+typedef struct _TrapView {
+    GtkWidget widget;
+
+    cairo_surface_t *pixmap;
+    int pixmap_width, pixmap_height;
+
+    box_t extents;
+    contour_t *contours;
+
+    double px, py;
+
+    gint mag_x, mag_y;
+    gint mag_size;
+    gdouble mag_zoom;
+    gboolean in_mag_drag;
+    gint mag_drag_x, mag_drag_y;
+} TrapView;
+
+typedef struct _TrapViewClass {
+    GtkWidgetClass parent_class;
+} TrapViewClass;
+
+G_DEFINE_TYPE (TrapView, trap_view, GTK_TYPE_WIDGET)
+
+static cairo_surface_t *
+pixmap_create (TrapView *self, cairo_surface_t *target)
+{
+    cairo_surface_t *surface =
+       cairo_surface_create_similar (target, CAIRO_CONTENT_COLOR,
+                                     self->widget.allocation.width,
+                                     self->widget.allocation.height);
+    cairo_t *cr = cairo_create (surface);
+    contour_t *contour;
+    gdouble sf_x, sf_y, sf;
+    gdouble mid, dim;
+    gdouble x0,  y0;
+    int n;
+    box_t extents;
+
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_paint (cr);
+
+    if (self->contours == NULL) {
+       cairo_destroy(cr);
+       return surface;
+    }
+
+    extents = self->extents;
+
+    mid = (extents.p2.x + extents.p1.x) / 2.;
+    dim = (extents.p2.x - extents.p1.x) / 2. * 1.25;
+    sf_x = self->widget.allocation.width / dim / 2;
+
+    mid = (extents.p2.y + extents.p1.y) / 2.;
+    dim = (extents.p2.y - extents.p1.y) / 2. * 1.25;
+    sf_y = self->widget.allocation.height / dim / 2;
+
+    sf = MIN (sf_x, sf_y);
+
+    mid = (extents.p2.x + extents.p1.x) / 2.;
+    dim = sf_x / sf * (extents.p2.x - extents.p1.x) / 2. * 1.25;
+    x0 = mid - dim;
+    mid = (extents.p2.y + extents.p1.y) / 2.;
+    dim = sf_y / sf * (extents.p2.y - extents.p1.y) / 2. * 1.25;
+    y0 = mid - dim;
+
+    for (contour = self->contours; contour; contour = contour->next) {
+       if (contour->num_points == 0)
+           continue;
+
+       cairo_save (cr); {
+           cairo_scale (cr, sf, sf);
+           cairo_translate (cr, -x0, -y0);
+           switch (contour->direction) {
+           case -1:
+               cairo_set_source_rgb (cr, 0.0, 0.0, 1.0);
+               break;
+           case 0:
+               cairo_set_source_rgb (cr, 0.0, 1.0, 0.0);
+               break;
+           case 1:
+               cairo_set_source_rgb (cr, 1.0, 0.0, 0.0);
+               break;
+           }
+           {
+               const point_t *p = &contour->points[0];
+               cairo_arc (cr, p->x, p->y, 4/sf, 0, 2 * M_PI);
+               cairo_save (cr);
+               cairo_identity_matrix (cr);
+               cairo_stroke (cr);
+               cairo_restore (cr);
+           }
+           for (n = 0; n < contour->num_points; n++) {
+               const point_t *p = &contour->points[n];
+               cairo_arc (cr, p->x, p->y, 2/sf, 0, 2 * M_PI);
+               cairo_fill (cr);
+           }
+           for (n = 0; n < contour->num_points; n++) {
+               const point_t *p = &contour->points[n];
+               cairo_line_to (cr, p->x, p->y);
+           }
+       } cairo_restore (cr);
+
+       switch (contour->direction) {
+       case -1:
+           cairo_set_source_rgb (cr, 0.3, 0.3, 0.9);
+           break;
+       case 0:
+           cairo_set_source_rgb (cr, 0.3, 0.9, 0.3);
+           break;
+       case 1:
+           cairo_set_source_rgb (cr, 0.9, 0.3, 0.3);
+           break;
+       }
+       cairo_set_line_width (cr, 1.);
+       cairo_stroke (cr);
+    }
+
+    cairo_destroy (cr);
+    return surface;
+}
+
+static void
+trap_view_draw (TrapView *self, cairo_t *cr)
+{
+    contour_t *contour;
+    gdouble sf_x, sf_y, sf;
+    gdouble mid, dim;
+    gdouble x0,  y0;
+    int n;
+    box_t extents;
+
+    extents = self->extents;
+
+    mid = (extents.p2.x + extents.p1.x) / 2.;
+    dim = (extents.p2.x - extents.p1.x) / 2. * 1.25;
+    sf_x = self->widget.allocation.width / dim / 2;
+
+    mid = (extents.p2.y + extents.p1.y) / 2.;
+    dim = (extents.p2.y - extents.p1.y) / 2. * 1.25;
+    sf_y = self->widget.allocation.height / dim / 2;
+
+    sf = MIN (sf_x, sf_y);
+
+    mid = (extents.p2.x + extents.p1.x) / 2.;
+    dim = sf_x / sf * (extents.p2.x - extents.p1.x) / 2. * 1.25;
+    x0 = mid - dim;
+    mid = (extents.p2.y + extents.p1.y) / 2.;
+    dim = sf_y / sf * (extents.p2.y - extents.p1.y) / 2. * 1.25;
+    y0 = mid - dim;
+
+    if (self->pixmap_width != self->widget.allocation.width ||
+       self->pixmap_height != self->widget.allocation.height)
+    {
+       cairo_surface_destroy (self->pixmap);
+       self->pixmap = pixmap_create (self, cairo_get_target (cr));
+       self->pixmap_width = self->widget.allocation.width;
+       self->pixmap_height = self->widget.allocation.height;
+    }
+
+    cairo_set_source_surface (cr, self->pixmap, 0, 0);
+    cairo_paint (cr);
+
+    if (self->contours == NULL)
+       return;
+
+    /* draw a zoom view of the area around the mouse */
+    if (1) {
+       double zoom = self->mag_zoom;
+       int size = self->mag_size;
+       int mag_x = self->mag_x;
+       int mag_y = self->mag_y;
+
+       if (1) {
+           if (self->px + size < self->widget.allocation.width/2)
+               mag_x = self->px + size/4;
+           else
+               mag_x = self->px - size/4 - size;
+           mag_y = self->py - size/2;
+           if (mag_y < 0)
+               mag_y = 0;
+           if (mag_y + size > self->widget.allocation.height)
+               mag_y = self->widget.allocation.height - size;
+       }
+
+       cairo_save (cr); {
+           /* bottom right */
+           cairo_rectangle (cr, mag_x, mag_y, size, size);
+           cairo_stroke_preserve (cr);
+           cairo_set_source_rgb (cr, 1, 1, 1);
+           cairo_fill_preserve (cr);
+           cairo_clip (cr);
+
+           /* compute roi in extents */
+           cairo_translate (cr, mag_x + size/2, mag_y + size/2);
+
+           cairo_save (cr); {
+               for (contour = self->contours; contour; contour = contour->next) {
+                   if (contour->num_points == 0)
+                       continue;
+
+                   cairo_save (cr); {
+                       cairo_scale (cr, zoom, zoom);
+                       cairo_translate (cr, -(self->px / sf + x0), -(self->py /sf + y0));
+                       switch (contour->direction) {
+                       case -1:
+                           cairo_set_source_rgb (cr, 0.0, 0.0, 1.0);
+                           break;
+                       case 0:
+                           cairo_set_source_rgb (cr, 0.0, 1.0, 0.0);
+                           break;
+                       case 1:
+                           cairo_set_source_rgb (cr, 1.0, 0.0, 0.0);
+                           break;
+                       }
+                       {
+                           const point_t *p = &contour->points[0];
+                           cairo_arc (cr, p->x, p->y, 4/zoom, 0, 2 * M_PI);
+                           cairo_save (cr);
+                           cairo_identity_matrix (cr);
+                           cairo_stroke (cr);
+                           cairo_restore (cr);
+                       }
+                       for (n = 0; n < contour->num_points; n++) {
+                           const point_t *p = &contour->points[n];
+                           cairo_arc (cr, p->x, p->y, 2/zoom, 0, 2 * M_PI);
+                           cairo_fill (cr);
+                       }
+                       for (n = 0; n < contour->num_points; n++) {
+                           const point_t *p = &contour->points[n];
+                           cairo_line_to (cr, p->x, p->y);
+                       }
+                   } cairo_restore (cr);
+
+                   switch (contour->direction) {
+                   case -1:
+                       cairo_set_source_rgb (cr, 0.3, 0.3, 0.9);
+                       break;
+                   case 0:
+                       cairo_set_source_rgb (cr, 0.3, 0.9, 0.3);
+                       break;
+                   case 1:
+                       cairo_set_source_rgb (cr, 0.9, 0.3, 0.3);
+                       break;
+                   }
+                   cairo_stroke (cr);
+               }
+           } cairo_restore (cr);
+
+           /* grid */
+           cairo_save (cr); {
+               int i;
+
+               cairo_translate (cr,
+                                -zoom*fmod (self->px/sf + x0, 1.),
+                                -zoom*fmod (self->py/sf + y0, 1.));
+               zoom /= 2;
+               for (i = -size/2/zoom; i <= size/2/zoom + 1; i+=2) {
+                   cairo_move_to (cr, zoom*i, -size/2);
+                   cairo_line_to (cr, zoom*i, size/2 + zoom);
+                   cairo_move_to (cr, -size/2, zoom*i);
+                   cairo_line_to (cr, size/2 + zoom, zoom*i);
+               }
+               zoom *= 2;
+               cairo_set_source_rgba (cr, .7, .7, .7, .5);
+               cairo_set_line_width (cr, 1.);
+               cairo_stroke (cr);
+
+               for (i = -size/2/zoom - 1; i <= size/2/zoom + 1; i++) {
+                   cairo_move_to (cr, zoom*i, -size/2);
+                   cairo_line_to (cr, zoom*i, size/2 + zoom);
+                   cairo_move_to (cr, -size/2, zoom*i);
+                   cairo_line_to (cr, size/2 + zoom, zoom*i);
+               }
+               cairo_set_source_rgba (cr, .1, .1, .1, .5);
+               cairo_set_line_width (cr, 2.);
+               cairo_stroke (cr);
+           } cairo_restore (cr);
+
+       } cairo_restore (cr);
+    }
+}
+
+
+static gdouble
+edge_length (const point_t *p1, const point_t *p2)
+{
+    return hypot (p2->x - p1->x, p2->y - p1->y);
+}
+
+static gdouble
+contour_compute_total_length (const contour_t *contour)
+{
+    int n;
+    gdouble len = 0.;
+    for (n = 1; n < contour->num_points; n++)
+       len += edge_length (&contour->points[n-1], &contour->points[n]);
+    return len;
+}
+
+static void
+trap_view_draw_labels (TrapView *self, cairo_t *cr)
+{
+    contour_t *contour;
+    int y = 12;
+
+    for (contour = self->contours; contour; contour = contour->next) {
+       double total_length = contour_compute_total_length (contour) / 256.;
+       PangoLayout *layout;
+       gint width, height;
+       GString *string;
+       gchar *str;
+
+       if (contour->num_points == 0)
+           continue;
+
+       string = g_string_new (NULL);
+       g_string_append_printf (string,
+                               "Number of points:\t%d\n"
+                               "Total length of contour: \t%.2f",
+                               contour->num_points,
+                               total_length);
+
+       str = g_string_free (string, FALSE);
+       layout = gtk_widget_create_pango_layout (&self->widget, str);
+       g_free (str);
+
+       pango_layout_get_pixel_size (layout, &width, &height);
+
+       switch (contour->direction) {
+       case -1:
+           cairo_set_source_rgb (cr, 0.9, 0.3, 0.3);
+           break;
+       case 0:
+           cairo_set_source_rgb (cr, 0.3, 0.9, 0.3);
+           break;
+       case 1:
+           cairo_set_source_rgb (cr, 0.3, 0.3, 0.9);
+           break;
+       }
+
+       cairo_move_to (cr, 10, y);
+       pango_cairo_show_layout (cr, layout);
+       g_object_unref (layout);
+
+       y += height + 4;
+    }
+}
+
+static gboolean
+trap_view_expose (GtkWidget *w, GdkEventExpose *ev)
+{
+    TrapView *self = (TrapView *) w;
+    cairo_t *cr;
+
+    cr = gdk_cairo_create (w->window);
+    gdk_cairo_region (cr, ev->region);
+    cairo_clip (cr);
+
+    trap_view_draw (self, cr);
+    trap_view_draw_labels (self, cr);
+
+    cairo_destroy (cr);
+    return FALSE;
+}
+
+static gboolean
+trap_view_key_press (GtkWidget *w, GdkEventKey *ev)
+{
+    switch (ev->keyval) {
+    case GDK_Escape:
+    case GDK_Q:
+       gtk_main_quit ();
+       break;
+    }
+
+    return FALSE;
+}
+
+static gboolean
+trap_view_button_press (GtkWidget *w, GdkEventButton *ev)
+{
+    TrapView *self = (TrapView *) w;
+
+    if (ev->x < self->mag_x ||
+       ev->y < self->mag_y ||
+       ev->x > self->mag_x + self->mag_size ||
+       ev->y > self->mag_y + self->mag_size)
+    {
+    }
+    else
+    {
+       self->in_mag_drag = TRUE;
+       self->mag_drag_x = ev->x;
+       self->mag_drag_y = ev->y;
+    }
+
+    return FALSE;
+}
+
+static gboolean
+trap_view_button_release (GtkWidget *w, GdkEventButton *ev)
+{
+    TrapView *self = (TrapView *) w;
+
+    self->in_mag_drag = FALSE;
+
+    return FALSE;
+}
+
+static void
+trap_view_update_mouse (TrapView *self, GdkEventMotion *ev)
+{
+    self->px = ev->x;
+    self->py = ev->y;
+
+    gtk_widget_queue_draw (&self->widget);
+}
+
+static void
+trap_view_update_magnifier (TrapView *self, gint *xy)
+{
+    self->mag_x = xy[0];
+    self->mag_y = xy[1];
+
+    gtk_widget_queue_draw (&self->widget);
+}
+
+static gboolean
+trap_view_motion (GtkWidget *w, GdkEventMotion *ev)
+{
+    TrapView *self = (TrapView *) w;
+
+    if (self->in_mag_drag) {
+       int xy[2];
+
+       xy[0] = self->mag_x + ev->x - self->mag_drag_x;
+       xy[1] = self->mag_y + ev->y - self->mag_drag_y;
+
+       trap_view_update_magnifier (self, xy);
+
+       self->mag_drag_x = ev->x;
+       self->mag_drag_y = ev->y;
+    } else if (ev->x < self->mag_x ||
+              ev->y < self->mag_y ||
+              ev->x > self->mag_x + self->mag_size ||
+              ev->y > self->mag_y + self->mag_size)
+    {
+       trap_view_update_mouse (self, ev);
+    }
+
+    return FALSE;
+}
+
+static void
+trap_view_realize (GtkWidget *widget)
+{
+    GdkWindowAttr attributes;
+
+    GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
+
+    attributes.window_type = GDK_WINDOW_CHILD;
+    attributes.x = widget->allocation.x;
+    attributes.y = widget->allocation.y;
+    attributes.width  = widget->allocation.width;
+    attributes.height = widget->allocation.height;
+    attributes.wclass = GDK_INPUT_OUTPUT;
+    attributes.visual = gtk_widget_get_visual (widget);
+    attributes.colormap = gtk_widget_get_colormap (widget);
+    attributes.event_mask = gtk_widget_get_events (widget) |
+                           GDK_BUTTON_PRESS_MASK |
+                           GDK_BUTTON_RELEASE_MASK |
+                           GDK_KEY_PRESS_MASK |
+                           GDK_KEY_RELEASE_MASK |
+                           GDK_POINTER_MOTION_MASK |
+                           GDK_BUTTON_MOTION_MASK |
+                           GDK_EXPOSURE_MASK;
+
+    widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
+                                    &attributes,
+                                    GDK_WA_X | GDK_WA_Y |
+                                    GDK_WA_VISUAL | GDK_WA_COLORMAP);
+    gdk_window_set_user_data (widget->window, widget);
+
+    widget->style = gtk_style_attach (widget->style, widget->window);
+    gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
+}
+
+static void
+trap_view_size_allocate (GtkWidget *w, GdkRectangle *r)
+{
+    TrapView *self = (TrapView *) w;
+
+    GTK_WIDGET_CLASS (trap_view_parent_class)->size_allocate (w, r);
+
+    self->mag_x = w->allocation.width - self->mag_size - 10;
+    self->mag_y = w->allocation.height - self->mag_size - 10;
+}
+
+static void
+trap_view_finalize (GObject *obj)
+{
+    G_OBJECT_CLASS (trap_view_parent_class)->finalize (obj);
+}
+
+static void
+trap_view_class_init (TrapViewClass *klass)
+{
+    GObjectClass *object_class = (GObjectClass *) klass;
+    GtkWidgetClass *widget_class = (GtkWidgetClass *) klass;
+
+    object_class->finalize = trap_view_finalize;
+
+    widget_class->realize = trap_view_realize;
+    widget_class->size_allocate = trap_view_size_allocate;
+    widget_class->expose_event = trap_view_expose;
+    widget_class->key_press_event = trap_view_key_press;
+    widget_class->button_press_event = trap_view_button_press;
+    widget_class->button_release_event = trap_view_button_release;
+    widget_class->motion_notify_event = trap_view_motion;
+}
+
+static void
+trap_view_init (TrapView *self)
+{
+    self->mag_zoom = 64;
+    self->mag_size = 200;
+
+    self->extents.p1.x = G_MAXDOUBLE;
+    self->extents.p1.y = G_MAXDOUBLE;
+    self->extents.p2.x = -G_MAXDOUBLE;
+    self->extents.p2.y = -G_MAXDOUBLE;
+
+    GTK_WIDGET_SET_FLAGS (self, GTK_CAN_FOCUS);
+}
+
+static contour_t *
+_contour_add_point (TrapView *tv, contour_t *contour, point_t *p)
+{
+    if (contour == NULL)
+       return NULL;
+
+    if (p->y < tv->extents.p1.y)
+       tv->extents.p1.y = p->y;
+    if (p->y > tv->extents.p2.y)
+       tv->extents.p2.y = p->y;
+
+    if (p->x < tv->extents.p1.x)
+       tv->extents.p1.x = p->x;
+    if (p->x > tv->extents.p2.x)
+       tv->extents.p2.x = p->x;
+
+    if (contour->num_points == contour->size) {
+       int newsize = 2 * contour->size;
+       void *newcontour;
+
+       newcontour = g_realloc (contour,
+                             sizeof (contour_t) + newsize * sizeof (point_t));
+       if (newcontour == NULL)
+           return contour;
+
+       contour = newcontour;
+       contour->size = newsize;
+
+       if (contour->next != NULL)
+           contour->next->prev = newcontour;
+       if (contour->prev != NULL)
+           contour->prev->next = newcontour;
+       else
+           tv->contours = newcontour;
+    }
+
+    contour->points[contour->num_points++] = *p;
+
+    return contour;
+}
+
+static contour_t *
+contour_new (TrapView *tv, int direction)
+{
+    contour_t *t;
+
+    t = g_malloc (sizeof (contour_t) + 128 * sizeof (point_t));
+    t->direction = direction;
+    t->prev = NULL;
+    t->next = tv->contours;
+    if (tv->contours)
+       tv->contours->prev = t;
+    tv->contours = t;
+
+    t->size = 128;
+    t->num_points = 0;
+
+    return t;
+}
+
+int
+main (int argc, char **argv)
+{
+    TrapView *tv;
+    contour_t *contour = NULL;
+    GtkWidget *window;
+    FILE *file;
+    char *line = NULL;
+    size_t len = 0;
+
+    gtk_init (&argc, &argv);
+
+    tv = g_object_new (trap_view_get_type (), NULL);
+
+    file = fopen (argv[1], "r");
+    if (file != NULL) {
+       while (getline (&line, &len, file) != -1) {
+           point_t p;
+           int direction;
+
+           if (sscanf (line, "contour: direction=%d", &direction)) {
+               if (contour)
+                   g_print ("read %d contour\n", contour->num_points);
+
+               contour = contour_new (tv, direction);
+           } else if (sscanf (line, "  [%*d] = (%lf, %lf)", &p.x, &p.y) == 2) {
+               contour = _contour_add_point (tv, contour, &p);
+           }
+       }
+
+       if (contour)
+           g_print ("read %d contour\n", contour->num_points);
+
+       g_print ("extents=(%lg, %lg), (%lg, %lg)\n",
+                tv->extents.p1.x, tv->extents.p1.y,
+                tv->extents.p2.x, tv->extents.p2.y);
+       fclose (file);
+    }
+
+    window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+    g_signal_connect (window, "delete-event",
+                     G_CALLBACK (gtk_main_quit), NULL);
+    gtk_widget_set_size_request (window, 800, 800);
+    gtk_container_add (GTK_CONTAINER (window), &tv->widget);
+    gtk_widget_show_all (window);
+
+    gtk_main ();
+    return 0;
+}
diff --git a/util/show-edges.c b/util/show-edges.c
new file mode 100644 (file)
index 0000000..a85ad5f
--- /dev/null
@@ -0,0 +1,1221 @@
+#define _GNU_SOURCE
+#include <gtk/gtk.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <gdk/gdkkeysyms.h>
+#include <math.h>
+
+typedef struct _point {
+    gdouble x, y;
+} point_t;
+typedef struct _box {
+    point_t p1, p2;
+} box_t;
+typedef struct _line {
+    point_t p1, p2;
+} line_t;
+typedef struct _trapezoid {
+    gdouble top, bottom;
+    line_t left, right;
+} trapezoid_t;
+typedef struct _traps {
+    struct _traps *next, *prev;
+    box_t extents;
+    int num_traps;
+    int size;
+    trapezoid_t traps[0];
+} traps_t;
+
+typedef struct _edge {
+    line_t line;
+    gdouble top, bottom;
+    point_t p1, p2;
+    int dir;
+} edge_t;
+typedef struct _edges {
+    struct _edges *next, *prev;
+    box_t extents;
+    int num_edges;
+    int size;
+    edge_t edges[0];
+} edges_t;
+
+typedef struct _TrapView {
+    GtkWidget widget;
+
+    struct _TrapView *group_head;
+    struct _TrapView *group_next;
+    struct _TrapView *group_prev;
+
+    traps_t *traps_list;
+    traps_t *current_traps;
+
+    edges_t *edges_list;
+    edges_t *current_edges;
+
+    double px, py;
+
+    gint mag_x, mag_y;
+    gint mag_size;
+    gdouble mag_zoom;
+    gboolean in_mag_drag;
+    gint mag_drag_x, mag_drag_y;
+} TrapView;
+
+typedef struct _TrapViewClass {
+    GtkWidgetClass parent_class;
+} TrapViewClass;
+
+G_DEFINE_TYPE (TrapView, trap_view, GTK_TYPE_WIDGET)
+
+static gdouble
+_compute_intersection_x_for_y (const line_t *line,
+                              gdouble y)
+{
+    gdouble dx = line->p2.x - line->p1.x;
+    gdouble dy = line->p2.y - line->p1.y;
+    gdouble x;
+
+    if (y == line->p1.y)
+       return line->p1.x;
+    if (y == line->p2.y)
+       return line->p2.x;
+
+    x = line->p1.x;
+    if (dy != 0)
+       x +=  (y - line->p1.y)*dx/dy;
+    return x;
+}
+
+static void
+_compute_intersection_point (const line_t *line,
+                            gdouble y,
+                            point_t *p)
+{
+    p->x = _compute_intersection_x_for_y (line, p->y = y);
+}
+
+static void
+trap_view_draw (TrapView *self, cairo_t *cr)
+{
+    traps_t *traps;
+    edges_t *edges;
+    gdouble sf_x, sf_y, sf;
+    gdouble mid, dim;
+    gdouble x0, x1,  y0, y1;
+    double dash[2] = {8, 8};
+    double dots[2] = {0., 1.};
+    int n;
+    box_t extents;
+    point_t p;
+
+    cairo_save (cr);
+    cairo_save (cr);
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_paint (cr);
+    cairo_restore (cr);
+
+    traps = self->current_traps;
+    edges = self->current_edges;
+    if (traps == NULL && edges == NULL)
+       return;
+
+    if (traps != NULL) {
+       extents = traps->extents;
+       if (edges != NULL) {
+           if (edges->extents.p1.x < extents.p1.x)
+               extents.p1.x = edges->extents.p1.x;
+           if (edges->extents.p1.y < extents.p1.y)
+               extents.p1.y = edges->extents.p1.y;
+           if (edges->extents.p2.x > extents.p2.x)
+               extents.p2.x = edges->extents.p2.x;
+           if (edges->extents.p2.y > extents.p2.y)
+               extents.p2.y = edges->extents.p2.y;
+       }
+    } else
+       extents = edges->extents;
+
+    mid = (extents.p2.x + extents.p1.x) / 2.;
+    dim = (extents.p2.x - extents.p1.x) / 2. * 1.25;
+    sf_x = self->widget.allocation.width / dim / 2;
+
+    mid = (extents.p2.y + extents.p1.y) / 2.;
+    dim = (extents.p2.y - extents.p1.y) / 2. * 1.25;
+    sf_y = self->widget.allocation.height / dim / 2;
+
+    sf = MIN (sf_x, sf_y);
+
+    mid = (extents.p2.x + extents.p1.x) / 2.;
+    dim = sf_x / sf * (extents.p2.x - extents.p1.x) / 2. * 1.25;
+    x0 = mid - dim;
+    x1 = mid + dim;
+    mid = (extents.p2.y + extents.p1.y) / 2.;
+    dim = sf_y / sf * (extents.p2.y - extents.p1.y) / 2. * 1.25;
+    y0 = mid - dim;
+    y1 = mid + dim;
+
+    if (traps != NULL) {
+       cairo_save (cr);
+       cairo_scale (cr, sf, sf);
+       cairo_translate (cr, -x0, -y0);
+       cairo_set_source_rgba (cr, 0, 1, 0, .2);
+       for (n = 0; n < traps->num_traps; n++) {
+           const trapezoid_t *t = &traps->traps[n];
+
+           _compute_intersection_point (&t->left, t->top, &p);
+           cairo_move_to (cr, p.x, p.y);
+           _compute_intersection_point (&t->right, t->top, &p);
+           cairo_line_to (cr, p.x, p.y);
+           _compute_intersection_point (&t->right, t->bottom, &p);
+           cairo_line_to (cr, p.x, p.y);
+           _compute_intersection_point (&t->left, t->bottom, &p);
+           cairo_line_to (cr, p.x, p.y);
+           cairo_close_path (cr);
+           cairo_fill (cr);
+       }
+       cairo_restore (cr);
+    }
+
+    if (edges == NULL) {
+       cairo_save (cr);
+
+       /* top, bottom */
+       cairo_save (cr); {
+           cairo_matrix_t m;
+           cairo_matrix_init_scale (&m, sf, sf);
+           cairo_matrix_translate (&m, -x0, -y0);
+
+           cairo_set_line_width (cr, 1.);
+           cairo_set_line_cap (cr, CAIRO_LINE_CAP_BUTT);
+           for (n = 0; n < traps->num_traps; n++) {
+               const trapezoid_t *t = &traps->traps[n];
+
+               _compute_intersection_point (&t->left, t->top, &p);
+               cairo_matrix_transform_point (&m, &p.x, &p.y);
+               cairo_move_to (cr, floor (p.x), floor (p.y) + .5);
+               cairo_set_dash (cr, dash, 2, fmod (floor (p.x), dash[0] + dash[1]));
+
+               _compute_intersection_point (&t->right, t->top, &p);
+               cairo_matrix_transform_point (&m, &p.x, &p.y);
+               cairo_line_to (cr, ceil (p.x), floor (p.y) + .5);
+               cairo_stroke (cr);
+
+               _compute_intersection_point (&t->left, t->bottom, &p);
+               cairo_matrix_transform_point (&m, &p.x, &p.y);
+               cairo_move_to (cr, floor (p.x), floor (p.y) + .5);
+               cairo_set_dash (cr, dash, 2, fmod (floor (p.x), dash[0] + dash[1]));
+
+               _compute_intersection_point (&t->right, t->bottom, &p);
+               cairo_matrix_transform_point (&m, &p.x, &p.y);
+               cairo_line_to (cr, ceil (p.x), floor (p.y) + .5);
+               cairo_stroke (cr);
+           }
+       } cairo_restore (cr);
+
+       /* left extents */
+       cairo_save (cr); {
+           cairo_save (cr); {
+               cairo_scale (cr, sf, sf);
+               cairo_translate (cr, -x0, -y0);
+
+               for (n = 0; n < traps->num_traps; n++) {
+                   const trapezoid_t *t = &traps->traps[n];
+                   cairo_move_to (cr, t->left.p1.x, t->left.p1.y);
+                   cairo_line_to (cr, t->left.p2.x, t->left.p2.y);
+               }
+           } cairo_restore (cr);
+           cairo_set_source_rgb (cr, 1, 0, 0);
+           cairo_set_line_width (cr, 1.);
+           cairo_set_dash (cr, dash, 2, 0.);
+           cairo_stroke (cr);
+       } cairo_restore (cr);
+
+       /* left line */
+       cairo_save (cr); {
+           cairo_save (cr); {
+               cairo_scale (cr, sf, sf);
+               cairo_translate (cr, -x0, -y0);
+
+               for (n = 0; n < traps->num_traps; n++) {
+                   const trapezoid_t *t = &traps->traps[n];
+                   _compute_intersection_point (&t->left, t->top, &p);
+                   cairo_move_to (cr, p.x, p.y);
+                   _compute_intersection_point (&t->left, t->bottom, &p);
+                   cairo_line_to (cr, p.x, p.y);
+               }
+           } cairo_restore (cr);
+           cairo_set_source_rgb (cr, 1, 0, 0);
+           cairo_stroke (cr);
+       } cairo_restore (cr);
+
+       /* right extents */
+       cairo_save (cr); {
+           cairo_save (cr); {
+               cairo_scale (cr, sf, sf);
+               cairo_translate (cr, -x0, -y0);
+
+               for (n = 0; n < traps->num_traps; n++) {
+                   const trapezoid_t *t = &traps->traps[n];
+                   cairo_move_to (cr, t->right.p1.x, t->right.p1.y);
+                   cairo_line_to (cr, t->right.p2.x, t->right.p2.y);
+               }
+           } cairo_restore (cr);
+           cairo_set_source_rgb (cr, 0, 0, 1);
+           cairo_set_line_width (cr, 1.);
+           cairo_set_dash (cr, dash, 2, 0.);
+           cairo_stroke (cr);
+       } cairo_restore (cr);
+
+       /* right line */
+       cairo_save (cr); {
+           cairo_save (cr); {
+               cairo_scale (cr, sf, sf);
+               cairo_translate (cr, -x0, -y0);
+
+               for (n = 0; n < traps->num_traps; n++) {
+                   const trapezoid_t *t = &traps->traps[n];
+                   _compute_intersection_point (&t->right, t->top, &p);
+                   cairo_move_to (cr, p.x, p.y);
+                   _compute_intersection_point (&t->right, t->bottom, &p);
+                   cairo_line_to (cr, p.x, p.y);
+               } cairo_restore (cr);
+               cairo_set_source_rgb (cr, 0, 0, 1);
+               cairo_stroke (cr);
+           } cairo_restore (cr);
+       }
+
+       /* end-points */
+       cairo_save (cr); {
+           cairo_save (cr); {
+               cairo_scale (cr, sf, sf);
+               cairo_translate (cr, -x0, -y0);
+
+               for (n = 0; n < traps->num_traps; n++) {
+                   const trapezoid_t *t = &traps->traps[n];
+                   _compute_intersection_point (&t->left, t->top, &p);
+                   cairo_move_to (cr, p.x, p.y);
+                   cairo_close_path (cr);
+                   _compute_intersection_point (&t->left, t->bottom, &p);
+                   cairo_move_to (cr, p.x, p.y);
+                   cairo_close_path (cr);
+                   _compute_intersection_point (&t->right, t->top, &p);
+                   cairo_move_to (cr, p.x, p.y);
+                   cairo_close_path (cr);
+                   _compute_intersection_point (&t->right, t->bottom, &p);
+                   cairo_move_to (cr, p.x, p.y);
+                   cairo_close_path (cr);
+               }
+           } cairo_restore (cr);
+           cairo_set_source_rgb (cr, 0, 0, 0);
+           cairo_set_dash (cr, dots, 2, 0.);
+           cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
+           cairo_set_line_width (cr, 4.);
+           cairo_stroke (cr);
+       } cairo_restore (cr);
+
+       cairo_restore (cr);
+    } else {
+       cairo_save (cr);
+
+       for (n = 0; n < edges->num_edges; n++) {
+           const edge_t *e = &edges->edges[n];
+
+           cairo_save (cr); {
+               cairo_scale (cr, sf, sf);
+               cairo_translate (cr, -x0, -y0);
+               cairo_move_to (cr, e->p1.x, e->p1.y);
+               cairo_line_to (cr, e->p2.x, e->p2.y);
+           } cairo_restore (cr);
+
+           if (e->dir < 0) {
+               cairo_set_source_rgb (cr, 0, 0, 1);
+               cairo_set_dash (cr, dash, 2, dash[0]);
+           } else {
+               cairo_set_source_rgb (cr, 1, 0, 0);
+               cairo_set_dash (cr, dash, 2, 0.);
+           }
+
+           cairo_set_line_cap (cr, CAIRO_LINE_CAP_BUTT);
+           cairo_set_line_width (cr, 1.);
+           cairo_stroke (cr);
+
+           cairo_save (cr); {
+               cairo_scale (cr, sf, sf);
+               cairo_translate (cr, -x0, -y0);
+               cairo_move_to (cr, e->p1.x, e->p1.y);
+               cairo_close_path (cr);
+               cairo_move_to (cr, e->p2.x, e->p2.y);
+               cairo_close_path (cr);
+           } cairo_restore (cr);
+           cairo_set_source_rgb (cr, 0, 0, 0);
+           cairo_set_dash (cr, dots, 2, 0.);
+           cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
+           cairo_set_line_width (cr, 4.);
+           cairo_stroke (cr);
+       }
+
+       cairo_restore (cr);
+    }
+
+    /* draw a zoom view of the area around the mouse */
+    {
+       cairo_save (cr);
+       double zoom = self->mag_zoom;
+       int size = self->mag_size;
+
+       /* bottom right */
+       cairo_rectangle (cr, self->mag_x, self->mag_y, size, size);
+       cairo_stroke_preserve (cr);
+       cairo_set_source_rgb (cr, 1, 1, 1);
+       cairo_fill_preserve (cr);
+       cairo_clip (cr);
+
+       /* compute roi in extents */
+       cairo_translate (cr, self->mag_x + size/2, self->mag_y + size/2);
+
+       if (traps != NULL) {
+           cairo_save (cr);
+           cairo_scale (cr, zoom, zoom);
+           cairo_translate (cr, -(self->px / sf + x0), -(self->py /sf + y0));
+           for (n = 0; n < traps->num_traps; n++) {
+               const trapezoid_t *t = &traps->traps[n];
+
+               _compute_intersection_point (&t->left, t->top, &p);
+               cairo_move_to (cr, p.x, p.y);
+               _compute_intersection_point (&t->right, t->top, &p);
+               cairo_line_to (cr, p.x, p.y);
+               _compute_intersection_point (&t->right, t->bottom, &p);
+               cairo_line_to (cr, p.x, p.y);
+               _compute_intersection_point (&t->left, t->bottom, &p);
+               cairo_line_to (cr, p.x, p.y);
+               cairo_close_path (cr);
+               cairo_set_source_rgba (cr, 0, 1, 0, .2);
+               cairo_fill (cr);
+           }
+           cairo_restore (cr);
+       }
+
+       if (edges == NULL) {
+           cairo_save (cr); {
+               cairo_matrix_t m;
+               cairo_matrix_init_scale (&m, zoom, zoom);
+               cairo_matrix_translate (&m, -(self->px / sf + x0), -(self->py /sf + y0));
+
+               cairo_set_source_rgb (cr, 0, 0, 0);
+               cairo_set_line_width (cr, 1.);
+               cairo_set_line_cap (cr, CAIRO_LINE_CAP_BUTT);
+               for (n = 0; n < traps->num_traps; n++) {
+                   const trapezoid_t *t = &traps->traps[n];
+
+                   _compute_intersection_point (&t->left, t->top, &p);
+                   cairo_matrix_transform_point (&m, &p.x, &p.y);
+                   cairo_move_to (cr, floor (p.x), floor (p.y) + .5);
+                   cairo_set_dash (cr, dash, 2, fmod (floor (p.x), dash[0] + dash[1]));
+
+                   _compute_intersection_point (&t->right, t->top, &p);
+                   cairo_matrix_transform_point (&m, &p.x, &p.y);
+                   cairo_line_to (cr, ceil (p.x), floor (p.y) + .5);
+                   cairo_stroke (cr);
+
+                   _compute_intersection_point (&t->left, t->bottom, &p);
+                   cairo_matrix_transform_point (&m, &p.x, &p.y);
+                   cairo_move_to (cr, floor (p.x), floor (p.y) + .5);
+                   cairo_set_dash (cr, dash, 2, fmod (floor (p.x), dash[0] + dash[1]));
+
+                   _compute_intersection_point (&t->right, t->bottom, &p);
+                   cairo_matrix_transform_point (&m, &p.x, &p.y);
+                   cairo_line_to (cr, ceil (p.x), floor (p.y) + .5);
+                   cairo_stroke (cr);
+               }
+           } cairo_restore (cr);
+
+           cairo_save (cr); { /* left extents */
+               cairo_save (cr); {
+                   cairo_scale (cr, zoom, zoom);
+                   cairo_translate (cr, -(self->px / sf + x0), -(self->py /sf + y0));
+                   for (n = 0; n < traps->num_traps; n++) {
+                       const trapezoid_t *t = &traps->traps[n];
+                       cairo_move_to (cr, t->left.p1.x, t->left.p1.y);
+                       cairo_line_to (cr, t->left.p2.x, t->left.p2.y);
+                   }
+               } cairo_restore (cr);
+               cairo_set_source_rgb (cr, 1, 0, 0);
+               cairo_set_line_width (cr, .5);
+               cairo_set_dash (cr, dash, 2, 0.);
+               cairo_stroke (cr);
+           } cairo_restore (cr);
+           cairo_save (cr); { /* right extents */
+               cairo_save (cr); {
+                   cairo_scale (cr, zoom, zoom);
+                   cairo_translate (cr, -(self->px / sf + x0), -(self->py /sf + y0));
+
+                   for (n = 0; n < traps->num_traps; n++) {
+                       const trapezoid_t *t = &traps->traps[n];
+                       cairo_move_to (cr, t->right.p1.x, t->right.p1.y);
+                       cairo_line_to (cr, t->right.p2.x, t->right.p2.y);
+                   }
+               } cairo_restore (cr);
+               cairo_set_source_rgb (cr, 0, 0, 1);
+               cairo_set_line_width (cr, .5);
+               cairo_set_dash (cr, dash, 2, 0.);
+               cairo_stroke (cr);
+           } cairo_restore (cr);
+
+           cairo_save (cr); { /* left lines */
+               cairo_save (cr);
+               cairo_scale (cr, zoom, zoom);
+               cairo_translate (cr, -(self->px / sf + x0), -(self->py /sf + y0));
+               for (n = 0; n < traps->num_traps; n++) {
+                   const trapezoid_t *t = &traps->traps[n];
+                   _compute_intersection_point (&t->left, t->top, &p);
+                   cairo_move_to (cr, p.x, p.y);
+                   _compute_intersection_point (&t->left, t->bottom, &p);
+                   cairo_line_to (cr, p.x, p.y);
+               }
+               cairo_restore (cr);
+               cairo_set_source_rgb (cr, 1, 0, 0);
+               cairo_stroke (cr);
+           } cairo_restore (cr);
+           cairo_save (cr); { /* right lines */
+               cairo_save (cr);
+               cairo_scale (cr, zoom, zoom);
+               cairo_translate (cr, -(self->px / sf + x0), -(self->py /sf + y0));
+               for (n = 0; n < traps->num_traps; n++) {
+                   const trapezoid_t *t = &traps->traps[n];
+                   _compute_intersection_point (&t->right, t->top, &p);
+                   cairo_move_to (cr, p.x, p.y);
+                   _compute_intersection_point (&t->right, t->bottom, &p);
+                   cairo_line_to (cr, p.x, p.y);
+               }
+               cairo_restore (cr);
+               cairo_set_source_rgb (cr, 0, 0, 1);
+               cairo_stroke (cr);
+           } cairo_restore (cr);
+
+           /* end-points */
+           cairo_save (cr); {
+               double dots[2] = {0., 1.};
+
+               cairo_save (cr);
+               cairo_scale (cr, zoom, zoom);
+               cairo_translate (cr, -(self->px / sf + x0), -(self->py /sf + y0));
+               for (n = 0; n < traps->num_traps; n++) {
+                   const trapezoid_t *t = &traps->traps[n];
+                   _compute_intersection_point (&t->left, t->top, &p);
+                   cairo_move_to (cr, p.x, p.y);
+                   cairo_close_path (cr);
+                   _compute_intersection_point (&t->left, t->bottom, &p);
+                   cairo_move_to (cr, p.x, p.y);
+                   cairo_close_path (cr);
+                   _compute_intersection_point (&t->right, t->top, &p);
+                   cairo_move_to (cr, p.x, p.y);
+                   cairo_close_path (cr);
+                   _compute_intersection_point (&t->right, t->bottom, &p);
+                   cairo_move_to (cr, p.x, p.y);
+                   cairo_close_path (cr);
+               }
+               cairo_restore (cr);
+               cairo_set_source_rgb (cr, 0, 0, 0);
+               cairo_set_dash (cr, dots, 2, 0.);
+               cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
+               cairo_set_line_width (cr, 4.);
+               cairo_stroke (cr);
+           } cairo_restore (cr);
+       } else {
+           cairo_save (cr);
+
+           for (n = 0; n < edges->num_edges; n++) {
+               const edge_t *e = &edges->edges[n];
+
+               cairo_save (cr); {
+                   cairo_scale (cr, zoom, zoom);
+                   cairo_translate (cr, -(self->px / sf + x0), -(self->py /sf + y0));
+                   cairo_move_to (cr, e->p1.x, e->p1.y);
+                   cairo_line_to (cr, e->p2.x, e->p2.y);
+               } cairo_restore (cr);
+
+               if (e->dir < 0) {
+                   cairo_set_source_rgb (cr, 0, 0, 1);
+                   cairo_set_dash (cr, dash, 2, dash[0]);
+               } else {
+                   cairo_set_source_rgb (cr, 1, 0, 0);
+                   cairo_set_dash (cr, dash, 2, 0.);
+               }
+
+               cairo_set_line_cap (cr, CAIRO_LINE_CAP_BUTT);
+               cairo_set_line_width (cr, 1.);
+               cairo_stroke (cr);
+
+               cairo_save (cr); {
+                   cairo_scale (cr, zoom, zoom);
+                   cairo_translate (cr, -(self->px / sf + x0), -(self->py /sf + y0));
+                   cairo_move_to (cr, e->p1.x, e->p1.y);
+                   cairo_close_path (cr);
+                   cairo_move_to (cr, e->p2.x, e->p2.y);
+                   cairo_close_path (cr);
+               } cairo_restore (cr);
+               cairo_set_source_rgb (cr, 0, 0, 0);
+               cairo_set_dash (cr, dots, 2, 0.);
+               cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
+               cairo_set_line_width (cr, 4.);
+               cairo_stroke (cr);
+           }
+
+           cairo_restore (cr);
+       }
+
+       /* grid */
+       cairo_save (cr); {
+           int i;
+
+           cairo_translate (cr,
+                            -zoom*fmod (self->px/sf + x0, 1.),
+                            -zoom*fmod (self->py/sf + y0, 1.));
+           for (i = -size/2/zoom - 1; i <= size/2/zoom + 1; i++) {
+               cairo_move_to (cr, zoom*i, -size/2);
+               cairo_line_to (cr, zoom*i, size/2 + zoom);
+               cairo_move_to (cr, -size/2, zoom*i);
+               cairo_line_to (cr, size/2 + zoom, zoom*i);
+           }
+           cairo_set_source_rgba (cr, .7, .7, .7, .5);
+           cairo_set_line_width (cr, 1.);
+           cairo_stroke (cr);
+       } cairo_restore (cr);
+    }
+
+    cairo_restore (cr);
+}
+
+
+static gdouble
+edge_length (const edge_t *e)
+{
+    return hypot (e->p2.x - e->p1.x, e->p2.y - e->p1.y);
+}
+
+static gdouble
+edges_compute_total_length (const edges_t *edges)
+{
+    int n;
+    gdouble len = 0.;
+    for (n = 0; n < edges->num_edges; n++)
+       len += edge_length (&edges->edges[n]);
+    return len;
+}
+
+static gdouble
+trapezoid_area (const trapezoid_t *t)
+{
+    gdouble inner_left, inner_right;
+    gdouble outer_left, outer_right;
+    gdouble height;
+    gdouble area;
+
+    /* split into 3 sections: a rectangle with a pair of triangular bookends */
+    inner_left = _compute_intersection_x_for_y (&t->left, t->top);
+    outer_left = _compute_intersection_x_for_y (&t->left, t->bottom);
+    if (outer_left > inner_left) {
+       gdouble t = outer_left;
+       outer_left = inner_left;
+       inner_left = t;
+    }
+
+    inner_right = _compute_intersection_x_for_y (&t->right, t->top);
+    outer_right = _compute_intersection_x_for_y (&t->right, t->bottom);
+    if (outer_right > inner_right) {
+       gdouble t = outer_right;
+       outer_right = inner_right;
+       inner_right = t;
+    }
+
+    if (outer_left > outer_right) { /* reverse */
+       gdouble t;
+
+       t = outer_left;
+       outer_left = inner_right;
+       inner_right = t;
+
+       t = inner_left;
+       inner_left = outer_right;
+       outer_right = t;
+    }
+
+    height = t->bottom - t->top;
+    area  = (inner_left - outer_left) * height / 2;
+    area += (outer_right - inner_right) * height / 2;
+    area += (inner_right - inner_left) * height;
+
+    return area;
+}
+
+static gdouble
+traps_compute_total_area (const traps_t *traps)
+{
+    int n;
+    gdouble area = 0.;
+    for (n = 0; n < traps->num_traps; n++)
+       area += trapezoid_area (&traps->traps[n]);
+    return area;
+}
+
+static void
+trap_view_draw_labels (TrapView *self, cairo_t *cr)
+{
+    PangoLayout *layout;
+    gint width, height;
+    GString *string;
+    gchar *str;
+    traps_t *traps;
+    edges_t *edges;
+
+    string = g_string_new (NULL);
+
+    traps = self->current_traps;
+    if (traps != NULL) {
+       /* convert total area from fixed-point (assuming 24.8) */
+       gdouble total_area = traps_compute_total_area (traps) / (256. * 256.);
+       g_string_append_printf (string,
+                               "Number of trapezoids:\t%d\n"
+                               "Total area of trapezoids:\t%.2f\n",
+                               traps->num_traps,
+                               total_area);
+    }
+
+    edges = self->current_edges;
+    if (edges != NULL) {
+       double total_length = edges_compute_total_length (edges) / 256.;
+       g_string_append_printf (string,
+                               "Number of edges:\t%d\n"
+                               "Total length of edges: \t%.2f\n",
+                               edges->num_edges,
+                               total_length);
+    }
+
+    str = g_string_free (string, FALSE);
+    layout = gtk_widget_create_pango_layout (&self->widget, str);
+    g_free (str);
+
+    pango_layout_get_pixel_size (layout, &width, &height);
+
+    cairo_move_to (cr, 10, 40);
+    pango_cairo_show_layout (cr, layout);
+    g_object_unref (layout);
+}
+
+static gboolean
+trap_view_expose (GtkWidget *w, GdkEventExpose *ev)
+{
+    TrapView *self = (TrapView *) w;
+    cairo_t *cr;
+
+    cr = gdk_cairo_create (w->window);
+    gdk_cairo_region (cr, ev->region);
+    cairo_clip (cr);
+
+    trap_view_draw (self, cr);
+    trap_view_draw_labels (self, cr);
+
+    cairo_destroy (cr);
+    return FALSE;
+}
+
+static void
+trap_view_advance (TrapView *self)
+{
+    if (self->current_traps && self->current_traps->prev)
+       self->current_traps = self->current_traps->prev;
+    if (self->current_edges && self->current_edges->prev)
+       self->current_edges = self->current_edges->prev;
+    gtk_widget_queue_draw (&self->widget);
+}
+
+static void
+trap_view_back (TrapView *self)
+{
+    if (self->current_traps && self->current_traps->next)
+       self->current_traps = self->current_traps->next;
+    if (self->current_edges && self->current_edges->next)
+       self->current_edges = self->current_edges->next;
+    gtk_widget_queue_draw (&self->widget);
+}
+
+static void
+trap_view_group_foreach (TrapView *group, GFunc func, gpointer data)
+{
+    while (group) {
+       func (group, data);
+       group = group->group_next;
+    }
+}
+
+static gboolean
+trap_view_key_press (GtkWidget *w, GdkEventKey *ev)
+{
+    TrapView *self = (TrapView *) w;
+
+    switch (ev->keyval) {
+    case GDK_BackSpace:
+       trap_view_group_foreach (self->group_head,
+                                (GFunc) trap_view_back,
+                                NULL);
+       break;
+
+    case GDK_space:
+       trap_view_group_foreach (self->group_head,
+                                (GFunc) trap_view_advance,
+                                NULL);
+       break;
+
+    case GDK_Return:
+       trap_view_group_foreach (self->group_head,
+                                (GFunc) trap_view_advance,
+                                NULL);
+       break;
+
+    case GDK_Escape:
+    case GDK_Q:
+       gtk_main_quit ();
+       break;
+    }
+
+    return FALSE;
+}
+
+static gboolean
+trap_view_button_press (GtkWidget *w, GdkEventButton *ev)
+{
+    TrapView *self = (TrapView *) w;
+
+    if (ev->x < self->mag_x ||
+       ev->y < self->mag_y ||
+       ev->x > self->mag_x + self->mag_size ||
+       ev->y > self->mag_y + self->mag_size)
+    {
+       if (ev->type == GDK_BUTTON_PRESS) {
+           if (self->current_traps == NULL)
+               return FALSE;
+
+           if (ev->button == 1) {
+               trap_view_group_foreach (self->group_head,
+                                        (GFunc) trap_view_advance,
+                                        NULL);
+           } else if (ev->button == 3) {
+               trap_view_group_foreach (self->group_head,
+                                        (GFunc) trap_view_back,
+                                        NULL);
+           }
+       }
+    }
+    else
+    {
+       self->in_mag_drag = TRUE;
+       self->mag_drag_x = ev->x;
+       self->mag_drag_y = ev->y;
+    }
+
+    return FALSE;
+}
+
+static gboolean
+trap_view_button_release (GtkWidget *w, GdkEventButton *ev)
+{
+    TrapView *self = (TrapView *) w;
+
+    self->in_mag_drag = FALSE;
+
+    return FALSE;
+}
+
+static void
+trap_view_update_mouse (TrapView *self, GdkEventMotion *ev)
+{
+    self->px = ev->x;
+    self->py = ev->y;
+
+    gtk_widget_queue_draw (&self->widget);
+}
+
+static void
+trap_view_update_magnifier (TrapView *self, gint *xy)
+{
+    self->mag_x = xy[0];
+    self->mag_y = xy[1];
+
+    gtk_widget_queue_draw (&self->widget);
+}
+
+static gboolean
+trap_view_motion (GtkWidget *w, GdkEventMotion *ev)
+{
+    TrapView *self = (TrapView *) w;
+
+    if (self->in_mag_drag) {
+       int xy[2];
+
+       xy[0] = self->mag_x + ev->x - self->mag_drag_x;
+       xy[1] = self->mag_y + ev->y - self->mag_drag_y;
+
+       trap_view_group_foreach (self->group_head,
+                                (GFunc) trap_view_update_magnifier,
+                                xy);
+
+       self->mag_drag_x = ev->x;
+       self->mag_drag_y = ev->y;
+    } else if (ev->x < self->mag_x ||
+              ev->y < self->mag_y ||
+              ev->x > self->mag_x + self->mag_size ||
+              ev->y > self->mag_y + self->mag_size)
+    {
+       trap_view_group_foreach (self->group_head,
+                                (GFunc) trap_view_update_mouse,
+                                ev);
+    }
+
+    return FALSE;
+}
+
+static void
+trap_view_realize (GtkWidget *widget)
+{
+    GdkWindowAttr attributes;
+
+    GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
+
+    attributes.window_type = GDK_WINDOW_CHILD;
+    attributes.x = widget->allocation.x;
+    attributes.y = widget->allocation.y;
+    attributes.width  = widget->allocation.width;
+    attributes.height = widget->allocation.height;
+    attributes.wclass = GDK_INPUT_OUTPUT;
+    attributes.visual = gtk_widget_get_visual (widget);
+    attributes.colormap = gtk_widget_get_colormap (widget);
+    attributes.event_mask = gtk_widget_get_events (widget) |
+                           GDK_BUTTON_PRESS_MASK |
+                           GDK_BUTTON_RELEASE_MASK |
+                           GDK_KEY_PRESS_MASK |
+                           GDK_KEY_RELEASE_MASK |
+                           GDK_POINTER_MOTION_MASK |
+                           GDK_BUTTON_MOTION_MASK |
+                           GDK_EXPOSURE_MASK;
+
+    widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
+                                    &attributes,
+                                    GDK_WA_X | GDK_WA_Y |
+                                    GDK_WA_VISUAL | GDK_WA_COLORMAP);
+    gdk_window_set_user_data (widget->window, widget);
+
+    widget->style = gtk_style_attach (widget->style, widget->window);
+    gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
+}
+
+static void
+trap_view_size_allocate (GtkWidget *w, GdkRectangle *r)
+{
+    TrapView *self = (TrapView *) w;
+
+    GTK_WIDGET_CLASS (trap_view_parent_class)->size_allocate (w, r);
+
+    self->mag_x = w->allocation.width - self->mag_size - 10;
+    self->mag_y = w->allocation.height - self->mag_size - 10;
+}
+
+static void
+trap_view_finalize (GObject *obj)
+{
+    G_OBJECT_CLASS (trap_view_parent_class)->finalize (obj);
+}
+
+static void
+trap_view_class_init (TrapViewClass *klass)
+{
+    GObjectClass *object_class = (GObjectClass *) klass;
+    GtkWidgetClass *widget_class = (GtkWidgetClass *) klass;
+
+    object_class->finalize = trap_view_finalize;
+
+    widget_class->realize = trap_view_realize;
+    widget_class->size_allocate = trap_view_size_allocate;
+    widget_class->expose_event = trap_view_expose;
+    widget_class->key_press_event = trap_view_key_press;
+    widget_class->button_press_event = trap_view_button_press;
+    widget_class->button_release_event = trap_view_button_release;
+    widget_class->motion_notify_event = trap_view_motion;
+}
+
+static void
+trap_view_init (TrapView *self)
+{
+    self->mag_zoom = 10;
+    self->mag_size = 200;
+
+    GTK_WIDGET_SET_FLAGS (self, GTK_CAN_FOCUS);
+}
+
+static traps_t *
+_traps_add_trapezoid (TrapView *tv, traps_t *traps, const trapezoid_t *trap)
+{
+    if (trap->top < traps->extents.p1.y)
+       traps->extents.p1.y = trap->top;
+    if (trap->bottom > traps->extents.p2.y)
+       traps->extents.p2.y = trap->bottom;
+
+    if (trap->left.p1.x < traps->extents.p1.x)
+       traps->extents.p1.x = trap->left.p1.x;
+    if (trap->left.p2.x < traps->extents.p1.x)
+       traps->extents.p1.x = trap->left.p2.x;
+
+    if (trap->right.p1.x > traps->extents.p2.x)
+       traps->extents.p2.x = trap->right.p1.x;
+    if (trap->right.p2.x > traps->extents.p2.x)
+       traps->extents.p2.x = trap->right.p2.x;
+
+    if (traps->num_traps == traps->size) {
+       int newsize = 2 * traps->size;
+       void *newtraps;
+
+       newtraps = g_realloc (traps,
+                             sizeof (traps_t) + newsize * sizeof (trapezoid_t));
+       if (newtraps == NULL)
+           return traps;
+
+       if (tv->current_traps == traps)
+           tv->current_traps = newtraps;
+
+       traps = newtraps;
+       traps->size = newsize;
+
+       if (traps->next != NULL)
+           traps->next->prev = newtraps;
+       if (traps->prev != NULL)
+           traps->prev->next = newtraps;
+       else
+           tv->traps_list = newtraps;
+    }
+
+    traps->traps[traps->num_traps++] = *trap;
+
+    return traps;
+}
+
+static traps_t *
+traps_new (TrapView *tv)
+{
+    traps_t *t;
+
+    t = g_malloc (sizeof (traps_t) + 16 * sizeof (trapezoid_t));
+    t->prev = NULL;
+    t->next = tv->traps_list;
+    if (tv->traps_list)
+       tv->traps_list->prev = t;
+    tv->traps_list = t;
+
+    if (tv->current_traps == NULL)
+       tv->current_traps = t;
+
+    t->size = 16;
+    t->num_traps = 0;
+    t->extents.p1.x = G_MAXDOUBLE;
+    t->extents.p1.y = G_MAXDOUBLE;
+    t->extents.p2.x = -G_MAXDOUBLE;
+    t->extents.p2.y = -G_MAXDOUBLE;
+
+    return t;
+}
+
+static edges_t *
+_edges_add_edge (TrapView *tv, edges_t *edges, edge_t *e)
+{
+    if (e->top < edges->extents.p1.y)
+       edges->extents.p1.y = e->top;
+    if (e->bottom > edges->extents.p2.y)
+       edges->extents.p2.y = e->bottom;
+
+    _compute_intersection_point (&e->line, e->top, &e->p1);
+    _compute_intersection_point (&e->line, e->bottom, &e->p2);
+
+    if (e->p1.x < edges->extents.p1.x)
+       edges->extents.p1.x = e->p1.x;
+    if (e->p2.x < edges->extents.p1.x)
+       edges->extents.p1.x = e->p2.x;
+
+    if (e->p1.x > edges->extents.p2.x)
+       edges->extents.p2.x = e->p1.x;
+    if (e->p2.x > edges->extents.p2.x)
+       edges->extents.p2.x = e->p2.x;
+
+    if (edges->num_edges == edges->size) {
+       int newsize = 2 * edges->size;
+       void *newedges;
+
+       newedges = g_realloc (edges,
+                             sizeof (edges_t) + newsize * sizeof (edge_t));
+       if (newedges == NULL)
+           return edges;
+
+       if (tv->current_edges == edges)
+           tv->current_edges = newedges;
+
+       edges = newedges;
+       edges->size = newsize;
+
+       if (edges->next != NULL)
+           edges->next->prev = newedges;
+       if (edges->prev != NULL)
+           edges->prev->next = newedges;
+       else
+           tv->edges_list = newedges;
+    }
+
+    edges->edges[edges->num_edges++] = *e;
+
+    return edges;
+}
+
+static edges_t *
+edges_new (TrapView *tv)
+{
+    edges_t *t;
+
+    t = g_malloc (sizeof (edges_t) + 16 * sizeof (edge_t));
+    t->prev = NULL;
+    t->next = tv->edges_list;
+    if (tv->edges_list)
+       tv->edges_list->prev = t;
+    tv->edges_list = t;
+
+    if (tv->current_edges == NULL)
+       tv->current_edges = t;
+
+    t->size = 16;
+    t->num_edges = 0;
+    t->extents.p1.x = G_MAXDOUBLE;
+    t->extents.p1.y = G_MAXDOUBLE;
+    t->extents.p2.x = -G_MAXDOUBLE;
+    t->extents.p2.y = -G_MAXDOUBLE;
+
+    return t;
+}
+
+int
+main (int argc, char **argv)
+{
+    TrapView *tv, *tv2, *group_head = NULL, *group_prev = NULL;
+    traps_t *traps;
+    edges_t *edges;
+    GtkWidget *window, *hbox;
+    FILE *file;
+    char *line = NULL;
+    size_t len = 0;
+
+    gtk_init (&argc, &argv);
+
+    hbox = gtk_hbox_new (TRUE, 0);
+
+    tv = g_object_new (trap_view_get_type (), NULL);
+    gtk_box_pack_start (GTK_BOX (hbox), &tv->widget, TRUE, TRUE, 0);
+    gtk_widget_show (&tv->widget);
+
+    tv->group_prev = group_prev;
+    tv->group_next = NULL;
+    if (group_prev)
+       group_prev->group_next = tv;
+    group_prev = tv;
+    if (group_head == NULL)
+       group_head = tv;
+    tv->group_head = group_head;
+
+    file = fopen (argv[1], "r");
+    if (file != NULL) {
+       edges = edges_new (tv);
+       while (getline (&line, &len, file) != -1) {
+           edge_t e;
+
+           if (sscanf (line,
+                       "(%lf, %lf), (%lf, %lf) %lf %lf %d",
+                       &e.line.p1.x, &e.line.p1.y,
+                       &e.line.p2.x, &e.line.p2.y,
+                       &e.top, &e.bottom,
+                       &e.dir) == 7) {
+               edges = _edges_add_edge (tv, edges, &e);
+           } else {
+               if (edges->num_edges) {
+                   g_print ("read %d edges\n", edges->num_edges);
+                   g_print ("extents=(%lg, %lg), (%lg, %lg)\n",
+                            edges->extents.p1.x, edges->extents.p1.y,
+                            edges->extents.p2.x, edges->extents.p2.y);
+                   edges = edges_new (tv);
+               }
+           }
+       }
+
+       if (edges->num_edges) {
+           g_print ("read %d edges\n", edges->num_edges);
+           g_print ("extents=(%lg, %lg), (%lg, %lg)\n",
+                    edges->extents.p1.x, edges->extents.p1.y,
+                    edges->extents.p2.x, edges->extents.p2.y);
+       }
+
+       fclose (file);
+    }
+
+    file = fopen (argv[2], "r");
+    if (file != NULL) {
+       traps = traps_new (tv);
+       while (getline (&line, &len, file) != -1) {
+           trapezoid_t t;
+
+           if (sscanf (line,
+                       "%lf %lf L:(%lf, %lf), (%lf, %lf) R:(%lf, %lf), (%lf, %lf)",
+                       &t.top, &t.bottom,
+                       &t.left.p1.x, &t.left.p1.y,
+                       &t.left.p2.x, &t.left.p2.y,
+                       &t.right.p1.x, &t.right.p1.y,
+                       &t.right.p2.x, &t.right.p2.y) == 10) {
+               traps = _traps_add_trapezoid (tv, traps, &t);
+           } else {
+               if (traps->num_traps) {
+                   g_print ("read %d trapezoids\n", traps->num_traps);
+                   g_print ("extents=(%lg, %lg), (%lg, %lg)\n",
+                            traps->extents.p1.x, traps->extents.p1.y,
+                            traps->extents.p2.x, traps->extents.p2.y);
+                   traps = traps_new (tv);
+               }
+           }
+       }
+
+       if (traps->num_traps) {
+           g_print ("read %d trapezoids\n", traps->num_traps);
+           g_print ("extents=(%lg, %lg), (%lg, %lg)\n",
+                    traps->extents.p1.x, traps->extents.p1.y,
+                    traps->extents.p2.x, traps->extents.p2.y);
+       }
+
+       fclose (file);
+    }
+
+    free (line);
+
+    tv2 = g_object_new (trap_view_get_type (), NULL);
+    gtk_box_pack_start (GTK_BOX (hbox), &tv2->widget, TRUE, TRUE, 0);
+    gtk_widget_show (&tv2->widget);
+
+    tv2->traps_list = tv->traps_list;
+    tv2->current_traps = tv->current_traps;
+
+    tv2->group_prev = group_prev;
+    tv2->group_next = NULL;
+    group_prev->group_next = tv2;
+    group_prev = tv2;
+    tv2->group_head = group_head;
+
+    window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+    g_signal_connect (window, "delete-event",
+                     G_CALLBACK (gtk_main_quit), NULL);
+    gtk_widget_set_size_request (window, 800, 800);
+    gtk_container_add (GTK_CONTAINER (window), hbox);
+    gtk_widget_show (hbox);
+    gtk_widget_show (window);
+
+    gtk_main ();
+    return 0;
+}
diff --git a/util/show-events.c b/util/show-events.c
new file mode 100644 (file)
index 0000000..8bff3ef
--- /dev/null
@@ -0,0 +1,845 @@
+#define _GNU_SOURCE
+#include <gtk/gtk.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <gdk/gdkkeysyms.h>
+#include <math.h>
+
+typedef struct _point {
+    gdouble x, y;
+} point_t;
+typedef struct _box {
+    point_t p1, p2;
+} box_t;
+typedef struct _line {
+    point_t p1, p2;
+} line_t;
+
+typedef struct _edge {
+    gulong id;
+    line_t line;
+    gdouble top, bottom;
+    point_t p1, p2;
+    int dir;
+} edge_t;
+typedef struct _trapezoid {
+    gdouble top, bottom;
+    const edge_t *left, *right;
+} trapezoid_t;
+typedef struct _traps {
+    int num_traps;
+    int size;
+    trapezoid_t traps[0];
+} traps_t;
+
+typedef struct _edges {
+    GHashTable *ht;
+
+    int num_edges;
+    int size;
+    edge_t edges[0];
+} edges_t;
+
+typedef struct _event {
+    enum {
+       START_EDGE,
+       END_EDGE,
+       INTERSECTION,
+       START_TRAP,
+       END_TRAP,
+    } type;
+
+    int x, y; /* (top, bottom) for trap */
+    long e1, e2;
+} event_t;
+
+typedef struct _events {
+    struct _events *prev, *next;
+
+    box_t extents;
+    edges_t *edges;
+    traps_t *prototraps;
+    traps_t *traps;
+
+    int current_event;
+    int num_events;
+    int size_events;
+    event_t *events;
+} events_t;
+
+typedef struct _EventView {
+    GtkWidget widget;
+
+    events_t *events_list;
+    events_t *current_events;
+
+    double px, py;
+
+    gint mag_x, mag_y;
+    gint mag_size;
+    gdouble mag_zoom;
+    gboolean in_mag_drag;
+    gint mag_drag_x, mag_drag_y;
+} EventView;
+
+typedef struct _EventViewClass {
+    GtkWidgetClass parent_class;
+} EventViewClass;
+
+G_DEFINE_TYPE (EventView, event_view, GTK_TYPE_WIDGET)
+
+static edge_t *
+edges_lookup (edges_t *edges, gulong id)
+{
+    return &edges->edges[GPOINTER_TO_UINT(g_hash_table_lookup (edges->ht,
+                                                              GUINT_TO_POINTER
+                                                              (id)))];
+}
+
+static gdouble
+_compute_intersection_x_for_y (const line_t *line,
+                              gdouble y)
+{
+    gdouble dx = line->p2.x - line->p1.x;
+    gdouble dy = line->p2.y - line->p1.y;
+    gdouble x;
+
+    if (y == line->p1.y)
+       return line->p1.x;
+    if (y == line->p2.y)
+       return line->p2.x;
+
+    x = line->p1.x;
+    if (dy != 0)
+       x +=  (y - line->p1.y)*dx/dy;
+    return x;
+}
+
+static void
+_compute_intersection_point (const line_t *line,
+                            gdouble y,
+                            point_t *p)
+{
+    p->x = _compute_intersection_x_for_y (line, p->y = y);
+}
+
+static void
+_edge_path (cairo_t *cr, const cairo_matrix_t *m, const edge_t *e)
+{
+    double x, y;
+
+    x = e->p1.x; y = e->p1.y;
+    cairo_matrix_transform_point (m, &x, &y);
+    cairo_move_to (cr, x, y);
+
+    x = e->p2.x; y = e->p2.y;
+    cairo_matrix_transform_point (m, &x, &y);
+    cairo_line_to (cr, x, y);
+
+    if (e->dir < 0) {
+       cairo_set_source_rgb (cr, 0, 0, 1);
+    } else {
+       cairo_set_source_rgb (cr, 1, 0, 0);
+    }
+}
+
+static void
+_events_draw (events_t *events, cairo_t *cr, cairo_matrix_t *m)
+{
+    double dash[2] = {8, 8};
+    point_t p;
+    int n;
+
+    /* first the existing and proto-traps */
+    cairo_save (cr); {
+       cairo_set_matrix (cr, m);
+
+       cairo_set_source_rgba (cr, 1, 0, 0, .15);
+       for (n = 0; n < events->prototraps->num_traps; n++) {
+           const trapezoid_t *t = &events->prototraps->traps[n];
+
+           _compute_intersection_point (&t->left->line, t->top, &p);
+           cairo_move_to (cr, p.x, p.y);
+           _compute_intersection_point (&t->right->line, t->top, &p);
+           cairo_line_to (cr, p.x, p.y);
+           _compute_intersection_point (&t->right->line, t->bottom, &p);
+           cairo_line_to (cr, p.x, p.y);
+           _compute_intersection_point (&t->left->line, t->bottom, &p);
+           cairo_line_to (cr, p.x, p.y);
+           cairo_close_path (cr);
+           cairo_fill (cr);
+       }
+
+       cairo_set_source_rgba (cr, 0, 1, 0, .2);
+       for (n = 0; n < events->traps->num_traps; n++) {
+           const trapezoid_t *t = &events->traps->traps[n];
+
+           _compute_intersection_point (&t->left->line, t->top, &p);
+           cairo_move_to (cr, p.x, p.y);
+           _compute_intersection_point (&t->right->line, t->top, &p);
+           cairo_line_to (cr, p.x, p.y);
+           _compute_intersection_point (&t->right->line, t->bottom, &p);
+           cairo_line_to (cr, p.x, p.y);
+           _compute_intersection_point (&t->left->line, t->bottom, &p);
+           cairo_line_to (cr, p.x, p.y);
+           cairo_close_path (cr);
+           cairo_fill (cr);
+       }
+    } cairo_restore (cr);
+
+    /* known edges */
+    cairo_save (cr);
+    cairo_set_line_width (cr, 1.);
+    for (n = 0; n < events->edges->num_edges; n++) {
+       const edge_t *e = &events->edges->edges[n];
+       double x, y;
+
+       x = e->p1.x; y = e->p1.y;
+       cairo_matrix_transform_point (m, &x, &y);
+       cairo_move_to (cr, x, y);
+
+       x = e->p2.x; y = e->p2.y;
+       cairo_matrix_transform_point (m, &x, &y);
+       cairo_line_to (cr, x, y);
+
+       if (e->dir < 0) {
+           cairo_set_source_rgba (cr, 0, 0, 1., .4);
+           cairo_set_dash (cr, dash, 2, fmod (e->p1.x, dash[0]+dash[1]) + dash[0]);
+       } else {
+           cairo_set_source_rgba (cr, 1, 0, 0., 4);
+           cairo_set_dash (cr, dash, 2, fmod (e->p1.x, dash[0]+dash[1]));
+       }
+
+       cairo_stroke (cr);
+
+       x = e->p1.x; y = e->p1.y;
+       cairo_matrix_transform_point (m, &x, &y);
+       cairo_arc (cr, x, y, 2., 0, 2 * G_PI);
+
+       x = e->p2.x; y = e->p2.y;
+       cairo_matrix_transform_point (m, &x, &y);
+       cairo_arc (cr, x, y, 2., 0, 2 * G_PI);
+
+       cairo_fill (cr);
+    }
+    cairo_restore (cr);
+
+    /* event time */
+    cairo_save (cr); {
+       event_t *e;
+       double x, y;
+
+       e = &events->events[events->current_event];
+
+       cairo_set_line_width (cr, 2.);
+       cairo_set_matrix (cr, m);
+       cairo_move_to (cr,
+                      events->extents.p1.x,
+                      e->y);
+       cairo_line_to (cr,
+                      events->extents.p2.x,
+                      e->y);
+       cairo_identity_matrix (cr);
+       cairo_stroke (cr);
+
+       x = e->x; y = e->y;
+       cairo_matrix_transform_point (m, &x, &y);
+       switch (e->type) {
+       case START_EDGE:
+       case END_EDGE:
+       case INTERSECTION:
+           cairo_arc (cr, x, y, 4., 0, 2 * G_PI);
+           break;
+       case START_TRAP:
+       case END_TRAP:
+           break;
+       }
+       switch (e->type) {
+       case START_EDGE:
+           cairo_set_source_rgb (cr, 1, 0, 0);
+           break;
+       case END_EDGE:
+           cairo_set_source_rgb (cr, 0, 0, 1);
+           break;
+       case INTERSECTION:
+           cairo_set_source_rgb (cr, 1, 0, 1);
+           break;
+       case START_TRAP:
+       case END_TRAP:
+           break;
+       }
+       cairo_fill (cr);
+
+       cairo_set_line_width (cr, 1.);
+       switch (e->type) {
+       case START_EDGE:
+           _edge_path (cr, m, edges_lookup (events->edges, e->e1));
+           cairo_stroke (cr);
+           break;
+       case END_EDGE:
+           _edge_path (cr, m, edges_lookup (events->edges, e->e1));
+           cairo_stroke (cr);
+           break;
+       case INTERSECTION:
+           _edge_path (cr, m, edges_lookup (events->edges, e->e1));
+           cairo_stroke (cr);
+           _edge_path (cr, m, edges_lookup (events->edges, e->e2));
+           cairo_stroke (cr);
+           break;
+       }
+    } cairo_restore (cr);
+}
+
+static void
+event_view_draw (EventView *self, cairo_t *cr)
+{
+    events_t *events;
+    gdouble sf_x, sf_y, sf;
+    gdouble mid, dim;
+    gdouble x0, x1,  y0, y1;
+    cairo_matrix_t m;
+
+    cairo_save (cr);
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_paint (cr);
+    cairo_restore (cr);
+
+    events = self->current_events;
+    if (events == NULL)
+       return;
+
+    mid = (events->extents.p2.x + events->extents.p1.x) / 2.;
+    dim = (events->extents.p2.x - events->extents.p1.x) / 2. * 1.2;
+    sf_x = self->widget.allocation.width / dim / 2;
+
+    mid = (events->extents.p2.y + events->extents.p1.y) / 2.;
+    dim = (events->extents.p2.y - events->extents.p1.y) / 2. * 1.2;
+    sf_y = self->widget.allocation.height / dim / 2;
+
+    sf = MIN (sf_x, sf_y);
+
+    mid = (events->extents.p2.x + events->extents.p1.x) / 2.;
+    dim = sf_x / sf * (events->extents.p2.x - events->extents.p1.x) / 2. * 1.2;
+    x0 = mid - dim;
+    x1 = mid + dim;
+    mid = (events->extents.p2.y + events->extents.p1.y) / 2.;
+    dim = sf_y / sf * (events->extents.p2.y - events->extents.p1.y) / 2. * 1.2;
+    y0 = mid - dim;
+    y1 = mid + dim;
+
+    cairo_matrix_init_scale (&m, sf, sf);
+    cairo_matrix_translate (&m, -x0, -y0);
+    _events_draw (events, cr, &m);
+
+    /* draw a zoom view of the area around the mouse */
+    cairo_save (cr); {
+       double zoom = self->mag_zoom;
+       int size = self->mag_size;
+
+       /* bottom right */
+       cairo_rectangle (cr, self->mag_x, self->mag_y, size, size);
+       cairo_stroke_preserve (cr);
+       cairo_set_source_rgb (cr, 1, 1, 1);
+       cairo_fill_preserve (cr);
+       cairo_clip (cr);
+
+       /* compute roi in extents */
+       cairo_translate (cr, self->mag_x + size/2, self->mag_y + size/2);
+
+       cairo_matrix_init_scale (&m, zoom, zoom);
+       cairo_matrix_translate (&m, -(self->px / sf + x0), -(self->py /sf + y0));
+       _events_draw (events, cr, &m);
+
+       /* grid */
+       cairo_save (cr); {
+           int i;
+
+           cairo_translate (cr,
+                            -zoom*fmod (self->px/sf + x0, 1.),
+                            -zoom*fmod (self->py/sf + y0, 1.));
+           for (i = -size/2/zoom - 1; i <= size/2/zoom + 1; i++) {
+               cairo_move_to (cr, zoom*i, -size/2);
+               cairo_line_to (cr, zoom*i, size/2 + zoom);
+               cairo_move_to (cr, -size/2, zoom*i);
+               cairo_line_to (cr, size/2 + zoom, zoom*i);
+           }
+           cairo_set_source_rgba (cr, .7, .7, .7, .5);
+           cairo_set_line_width (cr, 1.);
+           cairo_stroke (cr);
+       } cairo_restore (cr);
+    } cairo_restore (cr);
+}
+
+static void
+event_view_draw_labels (EventView *self, cairo_t *cr)
+{
+    events_t *events;
+
+    events = self->current_events;
+    if (events == NULL)
+       return;
+
+}
+
+static gboolean
+event_view_expose (GtkWidget *w, GdkEventExpose *ev)
+{
+    EventView *self = (EventView *) w;
+    cairo_t *cr;
+
+    cr = gdk_cairo_create (w->window);
+    gdk_cairo_region (cr, ev->region);
+    cairo_clip (cr);
+
+    event_view_draw (self, cr);
+    event_view_draw_labels (self, cr);
+
+    cairo_destroy (cr);
+    return FALSE;
+}
+
+static void
+traps_clear (traps_t *traps)
+{
+    traps->num_traps = 0;
+}
+
+static traps_t *
+traps_add (traps_t *traps, int top, int bot, const edge_t *e1, const edge_t *e2)
+{
+    trapezoid_t *t;
+
+    if (traps->num_traps == traps->size) {
+       traps->size *= 2;
+       traps = g_realloc (traps,
+                          sizeof (traps_t) + traps->size*sizeof (trapezoid_t));
+    }
+
+    t = &traps->traps[traps->num_traps++];
+    t->top = top;
+    if (bot > e1->bottom)
+       bot = e1->bottom;
+    if (bot > e2->bottom)
+       bot = e2->bottom;
+    t->bottom = bot;
+
+    t->left = e1;
+    t->right = e2;
+
+    return traps;
+}
+
+static void
+traps_remove (traps_t *traps, int top, const edge_t *e1, const edge_t *e2)
+{
+    int n;
+
+    for (n = 0; n < traps->num_traps; n++) {
+       trapezoid_t *t = &traps->traps[n];
+       if (t->top == top && t->left == e1 && t->right == e2)
+           break;
+    }
+    if (n < traps->num_traps) {
+       g_memmove (&traps->traps[n],
+                  &traps->traps[n+1],
+                  (traps->num_traps-n+1) * sizeof (trapezoid_t));
+       traps->num_traps--;
+    }
+}
+
+static void
+event_next (EventView *self)
+{
+    events_t *events;
+    event_t *e;
+
+    events = self->current_events;
+    if (++events->current_event == events->num_events) {
+       return;
+    } else if (events->current_event >= events->num_events) {
+       traps_clear (events->prototraps);
+       traps_clear (events->traps);
+       events->current_event = 0;
+
+       self->current_events = events->next;
+       if (self->current_events == NULL)
+           self->current_events = self->events_list;
+       events = self->current_events;
+    }
+
+    e = &events->events[events->current_event];
+    switch (e->type) {
+    case START_TRAP:
+       events->prototraps = traps_add (events->prototraps,
+                                       e->x, G_MAXINT,
+                                       edges_lookup (events->edges, e->e1),
+                                       edges_lookup (events->edges, e->e2));
+       break;
+    case END_TRAP:
+       traps_remove (events->prototraps,
+                     e->x,
+                     edges_lookup (events->edges, e->e1),
+                     edges_lookup (events->edges, e->e2));
+       events->traps = traps_add (events->traps,
+                                  e->x, e->y,
+                                  edges_lookup (events->edges, e->e1),
+                                  edges_lookup (events->edges, e->e2));
+       break;
+    }
+}
+
+static gboolean
+event_view_button_press (GtkWidget *w, GdkEventButton *ev)
+{
+    EventView *self = (EventView *) w;
+
+    if (ev->x < self->mag_x ||
+       ev->y < self->mag_y ||
+       ev->x > self->mag_x + self->mag_size ||
+       ev->y > self->mag_y + self->mag_size)
+    {
+       if (ev->type == GDK_BUTTON_PRESS) {
+           event_next (self);
+           gtk_widget_queue_draw (w);
+       }
+    }
+    else
+    {
+       self->in_mag_drag = TRUE;
+       self->mag_drag_x = ev->x;
+       self->mag_drag_y = ev->y;
+    }
+
+    return FALSE;
+}
+
+static gboolean
+event_view_button_release (GtkWidget *w, GdkEventButton *ev)
+{
+    EventView *self = (EventView *) w;
+
+    self->in_mag_drag = FALSE;
+
+    return FALSE;
+}
+
+static gboolean
+event_view_motion (GtkWidget *w, GdkEventMotion *ev)
+{
+    EventView *self = (EventView *) w;
+
+    if (self->in_mag_drag) {
+       self->mag_x += ev->x - self->mag_drag_x;
+       self->mag_y += ev->y - self->mag_drag_y;
+
+       gtk_widget_queue_draw (&self->widget);
+
+       self->mag_drag_x = ev->x;
+       self->mag_drag_y = ev->y;
+    } else if (ev->x < self->mag_x ||
+              ev->y < self->mag_y ||
+              ev->x > self->mag_x + self->mag_size ||
+              ev->y > self->mag_y + self->mag_size)
+    {
+       self->px = ev->x;
+       self->py = ev->y;
+
+       gtk_widget_queue_draw (&self->widget);
+    }
+
+    return FALSE;
+}
+
+static void
+event_view_realize (GtkWidget *widget)
+{
+    GdkWindowAttr attributes;
+
+    GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
+
+    attributes.window_type = GDK_WINDOW_CHILD;
+    attributes.x = widget->allocation.x;
+    attributes.y = widget->allocation.y;
+    attributes.width  = widget->allocation.width;
+    attributes.height = widget->allocation.height;
+    attributes.wclass = GDK_INPUT_OUTPUT;
+    attributes.visual = gtk_widget_get_visual (widget);
+    attributes.colormap = gtk_widget_get_colormap (widget);
+    attributes.event_mask = gtk_widget_get_events (widget) |
+                           GDK_BUTTON_PRESS_MASK |
+                           GDK_BUTTON_RELEASE_MASK |
+                           GDK_KEY_PRESS_MASK |
+                           GDK_KEY_RELEASE_MASK |
+                           GDK_POINTER_MOTION_MASK |
+                           GDK_BUTTON_MOTION_MASK |
+                           GDK_EXPOSURE_MASK;
+
+    widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
+                                    &attributes,
+                                    GDK_WA_X | GDK_WA_Y |
+                                    GDK_WA_VISUAL | GDK_WA_COLORMAP);
+    gdk_window_set_user_data (widget->window, widget);
+
+    widget->style = gtk_style_attach (widget->style, widget->window);
+    gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
+}
+
+static void
+event_view_size_allocate (GtkWidget *w, GdkRectangle *r)
+{
+    EventView *self = (EventView *) w;
+
+    GTK_WIDGET_CLASS (event_view_parent_class)->size_allocate (w, r);
+
+    self->mag_x = w->allocation.width - self->mag_size - 10;
+    self->mag_y = w->allocation.height - self->mag_size - 10;
+}
+
+static void
+event_view_finalize (GObject *obj)
+{
+    G_OBJECT_CLASS (event_view_parent_class)->finalize (obj);
+}
+
+static void
+event_view_class_init (EventViewClass *klass)
+{
+    GObjectClass *object_class = (GObjectClass *) klass;
+    GtkWidgetClass *widget_class = (GtkWidgetClass *) klass;
+
+    object_class->finalize = event_view_finalize;
+
+    widget_class->realize = event_view_realize;
+    widget_class->size_allocate = event_view_size_allocate;
+    widget_class->expose_event = event_view_expose;
+    widget_class->button_press_event = event_view_button_press;
+    widget_class->button_release_event = event_view_button_release;
+    widget_class->motion_notify_event = event_view_motion;
+}
+
+static void
+event_view_init (EventView *self)
+{
+    self->mag_zoom = 10;
+    self->mag_size = 200;
+}
+
+static traps_t *
+traps_new (void)
+{
+    traps_t *t;
+
+    t = g_malloc (sizeof (traps_t) + 16 * sizeof (trapezoid_t));
+
+    t->size = 16;
+    t->num_traps = 0;
+
+    return t;
+}
+
+static edges_t *
+_edges_add_edge (edges_t *edges, edge_t *e, box_t *extents)
+{
+    if (e->top < extents->p1.y)
+       extents->p1.y = e->top;
+    if (e->bottom > extents->p2.y)
+       extents->p2.y = e->bottom;
+
+    _compute_intersection_point (&e->line, e->top, &e->p1);
+    _compute_intersection_point (&e->line, e->bottom, &e->p2);
+
+    if (e->p1.x < extents->p1.x)
+       extents->p1.x = e->p1.x;
+    if (e->p2.x < extents->p1.x)
+       extents->p1.x = e->p2.x;
+
+    if (e->p1.x > extents->p2.x)
+       extents->p2.x = e->p1.x;
+    if (e->p2.x > extents->p2.x)
+       extents->p2.x = e->p2.x;
+
+    if (edges->num_edges == edges->size) {
+       edges->size *= 2;
+       edges = g_realloc (edges,
+                          sizeof (edges_t) + edges->size * sizeof (edge_t));
+    }
+
+    g_hash_table_insert (edges->ht,
+                        GUINT_TO_POINTER (e->id),
+                        GUINT_TO_POINTER (edges->num_edges));
+    edges->edges[edges->num_edges++] = *e;
+
+    return edges;
+}
+
+static void
+_events_add_event (events_t *events,
+                  int type,
+                  int x, int y,
+                  gulong e1, gulong e2)
+{
+    event_t *e;
+
+    if (events->num_events == events->size_events) {
+       int newsize = 2 * events->size_events;
+       void *newevents;
+
+       newevents = g_renew (event_t, events->events, newsize);
+       events->events = newevents;
+       events->size_events = newsize;
+    }
+
+    e = &events->events[events->num_events++];
+    e->type = type;
+    e->x = x;
+    e->y = y;
+    e->e1 = e1;
+    e->e2 = e2;
+}
+
+static edges_t *
+edges_new (void)
+{
+    edges_t *t;
+
+    t = g_malloc (sizeof (edges_t) + 16 * sizeof (edge_t));
+    t->ht = g_hash_table_new (NULL, NULL);
+    t->size = 16;
+    t->num_edges = 0;
+
+    return t;
+}
+
+static events_t *
+events_new (void)
+{
+    events_t *events;
+
+    events = g_malloc (sizeof (events_t));
+
+    events->next = NULL;
+    events->prev = NULL;
+
+    events->events = g_new (event_t, 16);
+    events->size_events = 16;
+    events->num_events = 0;
+    events->current_event = 0;
+
+    events->edges = edges_new ();
+    events->prototraps = traps_new ();
+    events->traps = traps_new ();
+
+    events->extents.p1.x = G_MAXDOUBLE;
+    events->extents.p1.y = G_MAXDOUBLE;
+    events->extents.p2.x = -G_MAXDOUBLE;
+    events->extents.p2.y = -G_MAXDOUBLE;
+
+    return events;
+}
+
+static void
+events_read (EventView *ev, const char *filename)
+{
+    FILE *file;
+
+    file = fopen (filename, "r");
+    if (file != NULL) {
+       char *line = NULL;
+       size_t len = 0;
+       events_t *events;
+
+       events = ev->events_list = events_new ();
+       while (getline (&line, &len, file) != -1) {
+           line = g_strstrip (line);
+           if (*line == '\0') {
+               events->next = events_new ();
+               events->next->prev = events;
+               events = events->next;
+           } else if (g_str_has_prefix (line, "edge:")) {
+               edge_t edge;
+
+               sscanf (line, "edge: %lu (%lf, %lf) (%lf, %lf) (%lf, %lf) %d",
+                       &edge.id,
+                       &edge.line.p1.x,
+                       &edge.line.p1.y,
+                       &edge.line.p2.x,
+                       &edge.line.p2.y,
+                       &edge.top,
+                       &edge.bottom,
+                       &edge.dir);
+
+               events->edges = _edges_add_edge (events->edges,
+                                                &edge,
+                                                &events->extents);
+           } else if (g_str_has_prefix (line, "event:")) {
+               int type;
+               int x,y;
+               gulong e1, e2;
+
+               sscanf (line, "event: %d (%d, %d) %lu %lu",
+                       &type, &x, &y,
+                       &e1, &e2);
+
+               _events_add_event (events, type, x, y, e1, e2);
+           } else if (g_str_has_prefix (line, "begin trap:")) {
+               int top;
+               gulong e1, e2;
+
+               sscanf (line, "begin trap: %lu %lu %u", &e1, &e2, &top);
+
+               _events_add_event (events, START_TRAP, top, 0, e1, e2);
+           } else if (g_str_has_prefix (line, "end trap:")) {
+               int top, bottom;
+               gulong e1, e2;
+
+               sscanf (line, "end trap: %lu %lu %d %d",
+                       &e1, &e2, &top, &bottom);
+
+               _events_add_event (events, END_TRAP, top, bottom, e1, e2);
+           }
+       }
+
+       ev->current_events = ev->events_list;
+
+       free (line);
+       fclose (file);
+    }
+}
+
+static gboolean
+timeout_advance (EventView *self)
+{
+    event_next (self);
+    gtk_widget_queue_draw (&self->widget);
+    return TRUE;
+}
+
+int
+main (int argc, char **argv)
+{
+    EventView *ev;
+    GtkWidget *window, *hbox;
+
+    gtk_init (&argc, &argv);
+
+    hbox = gtk_hbox_new (TRUE, 0);
+
+    ev = g_object_new (event_view_get_type (), NULL);
+    gtk_box_pack_start (GTK_BOX (hbox), &ev->widget, TRUE, TRUE, 0);
+    gtk_widget_show (&ev->widget);
+
+    events_read (ev, argv[1]);
+    g_timeout_add (750, (GSourceFunc) timeout_advance, ev);
+
+    window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+    gtk_widget_set_size_request (window, 800, 800);
+    g_signal_connect (window, "delete-event",
+                     G_CALLBACK (gtk_main_quit), NULL);
+    gtk_container_add (GTK_CONTAINER (window), hbox);
+    gtk_widget_show (hbox);
+    gtk_widget_show (window);
+
+    gtk_main ();
+    return 0;
+}
diff --git a/util/show-polygon.c b/util/show-polygon.c
new file mode 100644 (file)
index 0000000..fc1444d
--- /dev/null
@@ -0,0 +1,602 @@
+#define _GNU_SOURCE
+#include <gtk/gtk.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <gdk/gdkkeysyms.h>
+#include <math.h>
+
+typedef struct _point {
+    gdouble x, y;
+} point_t;
+typedef struct _edge {
+    point_t p1, p2;
+    gdouble top, bot;
+    int dir;
+} edge_t;
+typedef struct _box {
+    point_t p1, p2;
+} box_t;
+
+typedef struct _polygon {
+    struct _polygon *next, *prev;
+    int num_edges;
+    int size;
+    edge_t edges[0];
+} polygon_t;
+
+typedef struct _PolygonView {
+    GtkWidget widget;
+
+    cairo_surface_t *pixmap;
+    int pixmap_width, pixmap_height;
+
+    box_t extents;
+    polygon_t *polygons;
+
+    double px, py;
+
+    gint mag_x, mag_y;
+    gint mag_size;
+    gdouble mag_zoom;
+    gboolean in_mag_drag;
+    gint mag_drag_x, mag_drag_y;
+} PolygonView;
+
+typedef struct _PolygonViewClass {
+    GtkWidgetClass parent_class;
+} PolygonViewClass;
+
+G_DEFINE_TYPE (PolygonView, polygon_view, GTK_TYPE_WIDGET)
+
+double highlight = -1;
+
+static void draw_edges (cairo_t *cr, polygon_t *p, gdouble sf, int dir)
+{
+    int n;
+
+    for (n = 0; n < p->num_edges; n++) {
+       const edge_t *e = &p->edges[n];
+
+       if (e->dir != dir)
+           continue;
+
+       cairo_arc (cr, e->p1.x, e->p1.y, 2/sf, 0, 2*M_PI);
+       cairo_arc (cr, e->p2.x, e->p2.y, 2/sf, 0, 2*M_PI);
+       cairo_fill (cr);
+    }
+
+    for (n = 0; n < p->num_edges; n++) {
+       const edge_t *e = &p->edges[n];
+
+       if (e->dir != dir)
+           continue;
+
+       cairo_move_to (cr, e->p1.x, e->p1.y);
+       cairo_line_to (cr, e->p2.x, e->p2.y);
+    }
+    cairo_save (cr); {
+       cairo_identity_matrix (cr);
+       cairo_set_line_width (cr, 1.);
+       cairo_stroke (cr);
+    } cairo_restore (cr);
+}
+
+static void draw_polygon (cairo_t *cr, polygon_t *p, gdouble sf)
+{
+    cairo_set_source_rgb (cr, 0.0, 0.0, 1.0);
+    draw_edges (cr, p, sf, -1);
+
+    cairo_set_source_rgb (cr, 1.0, 0.0, 0.0);
+    draw_edges (cr, p, sf, +1);
+}
+
+static cairo_surface_t *
+pixmap_create (PolygonView *self, cairo_surface_t *target)
+{
+    cairo_surface_t *surface =
+       cairo_surface_create_similar (target, CAIRO_CONTENT_COLOR,
+                                     self->widget.allocation.width,
+                                     self->widget.allocation.height);
+    cairo_t *cr = cairo_create (surface);
+    polygon_t *polygon;
+    gdouble sf_x, sf_y, sf;
+    gdouble mid, dim;
+    gdouble x0,  y0;
+    box_t extents;
+
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_paint (cr);
+
+    if (self->polygons == NULL) {
+       cairo_destroy(cr);
+       return surface;
+    }
+
+    extents = self->extents;
+
+    mid = (extents.p2.x + extents.p1.x) / 2.;
+    dim = (extents.p2.x - extents.p1.x) / 2. * 1.25;
+    sf_x = self->widget.allocation.width / dim / 2;
+
+    mid = (extents.p2.y + extents.p1.y) / 2.;
+    dim = (extents.p2.y - extents.p1.y) / 2. * 1.25;
+    sf_y = self->widget.allocation.height / dim / 2;
+
+    sf = MIN (sf_x, sf_y);
+
+    mid = (extents.p2.x + extents.p1.x) / 2.;
+    dim = sf_x / sf * (extents.p2.x - extents.p1.x) / 2. * 1.25;
+    x0 = mid - dim;
+    mid = (extents.p2.y + extents.p1.y) / 2.;
+    dim = sf_y / sf * (extents.p2.y - extents.p1.y) / 2. * 1.25;
+    y0 = mid - dim;
+
+    cairo_save (cr); {
+       cairo_scale (cr, sf, sf);
+       cairo_translate (cr, -x0, -y0);
+
+       for (polygon = self->polygons; polygon; polygon = polygon->next) {
+           if (polygon->num_edges == 0)
+               continue;
+
+           draw_polygon (cr, polygon, sf);
+       }
+
+       if (highlight != -1) {
+           cairo_move_to (cr, extents.p1.x, highlight);
+           cairo_line_to (cr, extents.p2.x, highlight);
+           cairo_set_source_rgb (cr, 0, .7, 0);
+           cairo_save (cr);
+           cairo_identity_matrix (cr);
+           cairo_set_line_width (cr, 1.);
+           cairo_stroke (cr);
+           cairo_restore (cr);
+       }
+    } cairo_restore (cr);
+
+    cairo_destroy (cr);
+    return surface;
+}
+
+static void
+polygon_view_draw (PolygonView *self, cairo_t *cr)
+{
+    polygon_t *polygon;
+    gdouble sf_x, sf_y, sf;
+    gdouble mid, dim;
+    gdouble x0,  y0;
+    box_t extents;
+
+    extents = self->extents;
+
+    mid = (extents.p2.x + extents.p1.x) / 2.;
+    dim = (extents.p2.x - extents.p1.x) / 2. * 1.25;
+    sf_x = self->widget.allocation.width / dim / 2;
+
+    mid = (extents.p2.y + extents.p1.y) / 2.;
+    dim = (extents.p2.y - extents.p1.y) / 2. * 1.25;
+    sf_y = self->widget.allocation.height / dim / 2;
+
+    sf = MIN (sf_x, sf_y);
+
+    mid = (extents.p2.x + extents.p1.x) / 2.;
+    dim = sf_x / sf * (extents.p2.x - extents.p1.x) / 2. * 1.25;
+    x0 = mid - dim;
+    mid = (extents.p2.y + extents.p1.y) / 2.;
+    dim = sf_y / sf * (extents.p2.y - extents.p1.y) / 2. * 1.25;
+    y0 = mid - dim;
+
+    if (self->pixmap_width != self->widget.allocation.width ||
+       self->pixmap_height != self->widget.allocation.height)
+    {
+       cairo_surface_destroy (self->pixmap);
+       self->pixmap = pixmap_create (self, cairo_get_target (cr));
+       self->pixmap_width = self->widget.allocation.width;
+       self->pixmap_height = self->widget.allocation.height;
+    }
+
+    cairo_set_source_surface (cr, self->pixmap, 0, 0);
+    cairo_paint (cr);
+
+    if (self->polygons == NULL)
+       return;
+
+    /* draw a zoom view of the area around the mouse */
+    if (1) {
+       double zoom = self->mag_zoom;
+       int size = self->mag_size;
+       int mag_x = self->mag_x;
+       int mag_y = self->mag_y;
+
+       if (1) {
+           if (self->px + size < self->widget.allocation.width/2)
+               mag_x = self->px + size/4;
+           else
+               mag_x = self->px - size/4 - size;
+           mag_y = self->py - size/2;
+           if (mag_y < 0)
+               mag_y = 0;
+           if (mag_y + size > self->widget.allocation.height)
+               mag_y = self->widget.allocation.height - size;
+       }
+
+       cairo_save (cr); {
+           /* bottom right */
+           cairo_rectangle (cr, mag_x, mag_y, size, size);
+           cairo_stroke_preserve (cr);
+           cairo_set_source_rgb (cr, 1, 1, 1);
+           cairo_fill_preserve (cr);
+           cairo_clip (cr);
+
+           /* compute roi in extents */
+           cairo_translate (cr, mag_x + size/2, mag_y + size/2);
+
+           cairo_save (cr); {
+               cairo_scale (cr, zoom, zoom);
+               cairo_translate (cr, -(self->px / sf + x0), -(self->py /sf + y0));
+               for (polygon = self->polygons; polygon; polygon = polygon->next) {
+                   if (polygon->num_edges == 0)
+                       continue;
+
+                   draw_polygon (cr, polygon, zoom);
+               }
+
+               if (highlight != -1) {
+                   cairo_move_to (cr, extents.p1.x, highlight);
+                   cairo_line_to (cr, extents.p2.x, highlight);
+                   cairo_set_source_rgb (cr, 0, .7, 0);
+                   cairo_save (cr);
+                   cairo_identity_matrix (cr);
+                   cairo_set_line_width (cr, 1.);
+                   cairo_stroke (cr);
+                   cairo_restore (cr);
+               }
+           } cairo_restore (cr);
+
+           /* grid */
+           cairo_save (cr); {
+               int i;
+
+               cairo_translate (cr,
+                                -zoom*fmod (self->px/sf + x0, 1.),
+                                -zoom*fmod (self->py/sf + y0, 1.));
+               zoom /= 2;
+               for (i = -size/2/zoom; i <= size/2/zoom + 1; i+=2) {
+                   cairo_move_to (cr, zoom*i, -size/2);
+                   cairo_line_to (cr, zoom*i, size/2 + zoom);
+                   cairo_move_to (cr, -size/2, zoom*i);
+                   cairo_line_to (cr, size/2 + zoom, zoom*i);
+               }
+               zoom *= 2;
+               cairo_set_source_rgba (cr, .7, .7, .7, .5);
+               cairo_set_line_width (cr, 1.);
+               cairo_stroke (cr);
+
+               for (i = -size/2/zoom - 1; i <= size/2/zoom + 1; i++) {
+                   cairo_move_to (cr, zoom*i, -size/2);
+                   cairo_line_to (cr, zoom*i, size/2 + zoom);
+                   cairo_move_to (cr, -size/2, zoom*i);
+                   cairo_line_to (cr, size/2 + zoom, zoom*i);
+               }
+               cairo_set_source_rgba (cr, .1, .1, .1, .5);
+               cairo_set_line_width (cr, 2.);
+               cairo_stroke (cr);
+           } cairo_restore (cr);
+
+       } cairo_restore (cr);
+    }
+}
+
+static gboolean
+polygon_view_expose (GtkWidget *w, GdkEventExpose *ev)
+{
+    PolygonView *self = (PolygonView *) w;
+    cairo_t *cr;
+
+    cr = gdk_cairo_create (w->window);
+    gdk_cairo_region (cr, ev->region);
+    cairo_clip (cr);
+
+    polygon_view_draw (self, cr);
+
+    cairo_destroy (cr);
+    return FALSE;
+}
+
+static gboolean
+polygon_view_key_press (GtkWidget *w, GdkEventKey *ev)
+{
+    switch (ev->keyval) {
+    case GDK_Escape:
+    case GDK_Q:
+       gtk_main_quit ();
+       break;
+    }
+
+    return FALSE;
+}
+
+static gboolean
+polygon_view_button_press (GtkWidget *w, GdkEventButton *ev)
+{
+    PolygonView *self = (PolygonView *) w;
+
+    if (ev->x < self->mag_x ||
+       ev->y < self->mag_y ||
+       ev->x > self->mag_x + self->mag_size ||
+       ev->y > self->mag_y + self->mag_size)
+    {
+    }
+    else
+    {
+       self->in_mag_drag = TRUE;
+       self->mag_drag_x = ev->x;
+       self->mag_drag_y = ev->y;
+    }
+
+    return FALSE;
+}
+
+static gboolean
+polygon_view_button_release (GtkWidget *w, GdkEventButton *ev)
+{
+    PolygonView *self = (PolygonView *) w;
+
+    self->in_mag_drag = FALSE;
+
+    return FALSE;
+}
+
+static void
+polygon_view_update_mouse (PolygonView *self, GdkEventMotion *ev)
+{
+    self->px = ev->x;
+    self->py = ev->y;
+
+    gtk_widget_queue_draw (&self->widget);
+}
+
+static void
+polygon_view_update_magnifier (PolygonView *self, gint *xy)
+{
+    self->mag_x = xy[0];
+    self->mag_y = xy[1];
+
+    gtk_widget_queue_draw (&self->widget);
+}
+
+static gboolean
+polygon_view_motion (GtkWidget *w, GdkEventMotion *ev)
+{
+    PolygonView *self = (PolygonView *) w;
+
+    if (self->in_mag_drag) {
+       int xy[2];
+
+       xy[0] = self->mag_x + ev->x - self->mag_drag_x;
+       xy[1] = self->mag_y + ev->y - self->mag_drag_y;
+
+       polygon_view_update_magnifier (self, xy);
+
+       self->mag_drag_x = ev->x;
+       self->mag_drag_y = ev->y;
+    } else if (ev->x < self->mag_x ||
+              ev->y < self->mag_y ||
+              ev->x > self->mag_x + self->mag_size ||
+              ev->y > self->mag_y + self->mag_size)
+    {
+       polygon_view_update_mouse (self, ev);
+    }
+
+    return FALSE;
+}
+
+static void
+polygon_view_realize (GtkWidget *widget)
+{
+    GdkWindowAttr attributes;
+
+    GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
+
+    attributes.window_type = GDK_WINDOW_CHILD;
+    attributes.x = widget->allocation.x;
+    attributes.y = widget->allocation.y;
+    attributes.width  = widget->allocation.width;
+    attributes.height = widget->allocation.height;
+    attributes.wclass = GDK_INPUT_OUTPUT;
+    attributes.visual = gtk_widget_get_visual (widget);
+    attributes.colormap = gtk_widget_get_colormap (widget);
+    attributes.event_mask = gtk_widget_get_events (widget) |
+                           GDK_BUTTON_PRESS_MASK |
+                           GDK_BUTTON_RELEASE_MASK |
+                           GDK_KEY_PRESS_MASK |
+                           GDK_KEY_RELEASE_MASK |
+                           GDK_POINTER_MOTION_MASK |
+                           GDK_BUTTON_MOTION_MASK |
+                           GDK_EXPOSURE_MASK;
+
+    widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
+                                    &attributes,
+                                    GDK_WA_X | GDK_WA_Y |
+                                    GDK_WA_VISUAL | GDK_WA_COLORMAP);
+    gdk_window_set_user_data (widget->window, widget);
+
+    widget->style = gtk_style_attach (widget->style, widget->window);
+    gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
+}
+
+static void
+polygon_view_size_allocate (GtkWidget *w, GdkRectangle *r)
+{
+    PolygonView *self = (PolygonView *) w;
+
+    GTK_WIDGET_CLASS (polygon_view_parent_class)->size_allocate (w, r);
+
+    self->mag_x = w->allocation.width - self->mag_size - 10;
+    self->mag_y = w->allocation.height - self->mag_size - 10;
+}
+
+static void
+polygon_view_finalize (GObject *obj)
+{
+    G_OBJECT_CLASS (polygon_view_parent_class)->finalize (obj);
+}
+
+static void
+polygon_view_class_init (PolygonViewClass *klass)
+{
+    GObjectClass *object_class = (GObjectClass *) klass;
+    GtkWidgetClass *widget_class = (GtkWidgetClass *) klass;
+
+    object_class->finalize = polygon_view_finalize;
+
+    widget_class->realize = polygon_view_realize;
+    widget_class->size_allocate = polygon_view_size_allocate;
+    widget_class->expose_event = polygon_view_expose;
+    widget_class->key_press_event = polygon_view_key_press;
+    widget_class->button_press_event = polygon_view_button_press;
+    widget_class->button_release_event = polygon_view_button_release;
+    widget_class->motion_notify_event = polygon_view_motion;
+}
+
+static void
+polygon_view_init (PolygonView *self)
+{
+    self->mag_zoom = 64;
+    self->mag_size = 200;
+
+    self->extents.p1.x = G_MAXDOUBLE;
+    self->extents.p1.y = G_MAXDOUBLE;
+    self->extents.p2.x = -G_MAXDOUBLE;
+    self->extents.p2.y = -G_MAXDOUBLE;
+
+    GTK_WIDGET_SET_FLAGS (self, GTK_CAN_FOCUS);
+}
+
+static polygon_t *
+_polygon_add_edge (PolygonView *view, polygon_t *polygon,
+                  point_t *p1, point_t *p2,
+                  gdouble top, gdouble bot, int dir)
+{
+    if (polygon == NULL)
+       return NULL;
+
+    if (top < view->extents.p1.y)
+       view->extents.p1.y = top;
+    if (bot > view->extents.p2.y)
+       view->extents.p2.y = bot;
+
+    if (p1->x < view->extents.p1.x)
+       view->extents.p1.x = p1->x;
+    if (p1->x > view->extents.p2.x)
+       view->extents.p2.x = p1->x;
+    if (p2->x < view->extents.p1.x)
+       view->extents.p1.x = p2->x;
+    if (p2->x > view->extents.p2.x)
+       view->extents.p2.x = p2->x;
+
+    if (polygon->num_edges == polygon->size) {
+       int newsize = 2 * polygon->size;
+       void *newpolygon;
+
+       newpolygon = g_realloc (polygon,
+                             sizeof (polygon_t) + newsize * sizeof (edge_t));
+       if (newpolygon == NULL)
+           return polygon;
+
+       polygon = newpolygon;
+       polygon->size = newsize;
+
+       if (polygon->next != NULL)
+           polygon->next->prev = newpolygon;
+       if (polygon->prev != NULL)
+           polygon->prev->next = newpolygon;
+       else
+           view->polygons = newpolygon;
+    }
+
+    polygon->edges[polygon->num_edges].p1 = *p1;
+    polygon->edges[polygon->num_edges].p2 = *p2;
+    polygon->edges[polygon->num_edges].top = top;
+    polygon->edges[polygon->num_edges].bot = bot;
+    polygon->edges[polygon->num_edges].dir = dir;
+    polygon->num_edges++;
+
+    return polygon;
+}
+
+static polygon_t *
+polygon_new (PolygonView *view)
+{
+    polygon_t *t;
+
+    t = g_malloc (sizeof (polygon_t) + 128 * sizeof (edge_t));
+    t->prev = NULL;
+    t->next = view->polygons;
+    if (view->polygons)
+       view->polygons->prev = t;
+    view->polygons = t;
+
+    t->size = 128;
+    t->num_edges = 0;
+
+    return t;
+}
+
+int
+main (int argc, char **argv)
+{
+    PolygonView *view;
+    polygon_t *polygon = NULL;
+    GtkWidget *window;
+    FILE *file;
+    char *line = NULL;
+    size_t len = 0;
+
+    gtk_init (&argc, &argv);
+
+    view = g_object_new (polygon_view_get_type (), NULL);
+
+    file = fopen (argv[1], "r");
+    if (file != NULL) {
+       while (getline (&line, &len, file) != -1) {
+           point_t p1, p2;
+           double top, bottom;
+           int dir;
+
+           if (strncmp (line, "polygon: ", sizeof("polygon: ")-1) == 0) {
+               if (polygon && polygon->num_edges) {
+                   g_print ("read polygon with %d edges\n", polygon->num_edges);
+
+                   polygon = polygon_new (view);
+               } else if (polygon == NULL)
+                   polygon = polygon_new (view);
+           } else if (sscanf (line, "  [%*d] = [(%lf, %lf), (%lf, %lf)], top=%lf, bottom=%lf, dir=%d", &p1.x, &p1.y, &p2.x, &p2.y, &top, &bottom, &dir) == 7) {
+               polygon = _polygon_add_edge (view, polygon, &p1, &p2,
+                                            top, bottom, dir);
+           }
+       }
+
+       if (polygon && polygon->num_edges)
+           g_print ("read polygon with %d edges\n", polygon->num_edges);
+
+       g_print ("extents=(%lg, %lg), (%lg, %lg)\n",
+                view->extents.p1.x, view->extents.p1.y,
+                view->extents.p2.x, view->extents.p2.y);
+       fclose (file);
+    }
+
+    if (argc > 2)
+       highlight = atof (argv[2]);
+
+    window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+    g_signal_connect (window, "delete-event",
+                     G_CALLBACK (gtk_main_quit), NULL);
+    gtk_widget_set_size_request (window, 800, 800);
+    gtk_container_add (GTK_CONTAINER (window), &view->widget);
+    gtk_widget_show_all (window);
+
+    gtk_main ();
+    return 0;
+}
diff --git a/util/show-traps.c b/util/show-traps.c
new file mode 100644 (file)
index 0000000..f46c8b0
--- /dev/null
@@ -0,0 +1,1311 @@
+#define _GNU_SOURCE
+#include <gtk/gtk.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <gdk/gdkkeysyms.h>
+#include <math.h>
+
+typedef struct _point {
+    gdouble x, y;
+} point_t;
+typedef struct _box {
+    point_t p1, p2;
+} box_t;
+typedef struct _line {
+    point_t p1, p2;
+} line_t;
+typedef struct _trapezoid {
+    gdouble top, bottom;
+    line_t left, right;
+} trapezoid_t;
+typedef struct _traps {
+    struct _traps *next, *prev;
+    box_t extents;
+    int num_traps;
+    int size;
+    trapezoid_t traps[0];
+} traps_t;
+
+typedef struct _edge {
+    line_t line;
+    gdouble top, bottom;
+    point_t p1, p2;
+    int dir;
+} edge_t;
+typedef struct _edges {
+    struct _edges *next, *prev;
+    box_t extents;
+    int num_edges;
+    int size;
+    edge_t edges[0];
+} edges_t;
+
+typedef struct _TrapView {
+    GtkWidget widget;
+
+    struct _TrapView *group_head;
+    struct _TrapView *group_next;
+    struct _TrapView *group_prev;
+
+    cairo_surface_t *pixmap;
+    int pixmap_width, pixmap_height;
+
+    traps_t *traps_list;
+    traps_t *current_traps;
+
+    edges_t *edges_list;
+    edges_t *current_edges;
+
+    double px, py;
+
+    gint mag_x, mag_y;
+    gint mag_size;
+    gdouble mag_zoom;
+    gboolean in_mag_drag;
+    gint mag_drag_x, mag_drag_y;
+} TrapView;
+
+typedef struct _TrapViewClass {
+    GtkWidgetClass parent_class;
+} TrapViewClass;
+
+G_DEFINE_TYPE (TrapView, trap_view, GTK_TYPE_WIDGET)
+
+static gdouble
+_compute_intersection_x_for_y (const line_t *line,
+                              gdouble y)
+{
+    gdouble dx = line->p2.x - line->p1.x;
+    gdouble dy = line->p2.y - line->p1.y;
+    gdouble x;
+
+    if (y == line->p1.y)
+       return line->p1.x;
+    if (y == line->p2.y)
+       return line->p2.x;
+
+    x = line->p1.x;
+    if (dy != 0)
+       x +=  (y - line->p1.y)*dx/dy;
+    return x;
+}
+
+static void
+_compute_intersection_point (const line_t *line,
+                            gdouble y,
+                            point_t *p)
+{
+    p->x = _compute_intersection_x_for_y (line, p->y = y);
+}
+
+static cairo_surface_t *
+pixmap_create (TrapView *self, cairo_surface_t *target)
+{
+    cairo_surface_t *surface = cairo_surface_create_similar (target, CAIRO_CONTENT_COLOR,
+                                                            self->widget.allocation.width,
+                                                            self->widget.allocation.height);
+    cairo_t *cr;
+    traps_t *traps;
+    edges_t *edges;
+    gdouble sf_x, sf_y, sf;
+    gdouble mid, dim;
+    gdouble x0,  y0;
+    double dash[2] = {8, 8};
+    double dots[2] = {0., 1.};
+    int n;
+    box_t extents;
+    point_t p;
+
+    traps = self->current_traps;
+    if (traps == NULL)
+       return surface;
+    edges = self->current_edges;
+
+    extents = traps->extents;
+    if (edges != NULL) {
+       if (edges->extents.p1.x < extents.p1.x)
+           extents.p1.x = edges->extents.p1.x;
+       if (edges->extents.p1.y < extents.p1.y)
+           extents.p1.y = edges->extents.p1.y;
+       if (edges->extents.p2.x > extents.p2.x)
+           extents.p2.x = edges->extents.p2.x;
+       if (edges->extents.p2.y > extents.p2.y)
+           extents.p2.y = edges->extents.p2.y;
+    }
+
+    mid = (extents.p2.x + extents.p1.x) / 2.;
+    dim = (extents.p2.x - extents.p1.x) / 2. * 1.25;
+    sf_x = self->widget.allocation.width / dim / 2;
+
+    mid = (extents.p2.y + extents.p1.y) / 2.;
+    dim = (extents.p2.y - extents.p1.y) / 2. * 1.25;
+    sf_y = self->widget.allocation.height / dim / 2;
+
+    sf = MIN (sf_x, sf_y);
+
+    mid = (extents.p2.x + extents.p1.x) / 2.;
+    dim = sf_x / sf * (extents.p2.x - extents.p1.x) / 2. * 1.25;
+    x0 = mid - dim;
+    mid = (extents.p2.y + extents.p1.y) / 2.;
+    dim = sf_y / sf * (extents.p2.y - extents.p1.y) / 2. * 1.25;
+    y0 = mid - dim;
+
+    cr = cairo_create (surface);
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_paint (cr);
+
+    cairo_save (cr);
+    cairo_scale (cr, sf, sf);
+    cairo_translate (cr, -x0, -y0);
+    cairo_set_source_rgba (cr, 0, 1, 0, .2);
+    for (n = 0; n < traps->num_traps; n++) {
+       const trapezoid_t *t = &traps->traps[n];
+
+       _compute_intersection_point (&t->left, t->top, &p);
+       cairo_move_to (cr, p.x, p.y);
+       _compute_intersection_point (&t->right, t->top, &p);
+       cairo_line_to (cr, p.x, p.y);
+       _compute_intersection_point (&t->right, t->bottom, &p);
+       cairo_line_to (cr, p.x, p.y);
+       _compute_intersection_point (&t->left, t->bottom, &p);
+       cairo_line_to (cr, p.x, p.y);
+       cairo_close_path (cr);
+       cairo_fill (cr);
+    }
+    cairo_restore (cr);
+
+    if (edges == NULL) {
+       cairo_save (cr);
+
+       /* top, bottom */
+       cairo_save (cr); {
+           cairo_matrix_t m;
+           cairo_matrix_init_scale (&m, sf, sf);
+           cairo_matrix_translate (&m, -x0, -y0);
+
+           cairo_set_line_width (cr, 1.);
+           cairo_set_line_cap (cr, CAIRO_LINE_CAP_BUTT);
+           for (n = 0; n < traps->num_traps; n++) {
+               const trapezoid_t *t = &traps->traps[n];
+
+               _compute_intersection_point (&t->left, t->top, &p);
+               cairo_matrix_transform_point (&m, &p.x, &p.y);
+               cairo_move_to (cr, floor (p.x), floor (p.y) + .5);
+               cairo_set_dash (cr, dash, 2, fmod (floor (p.x), dash[0] + dash[1]));
+
+               _compute_intersection_point (&t->right, t->top, &p);
+               cairo_matrix_transform_point (&m, &p.x, &p.y);
+               cairo_line_to (cr, ceil (p.x), floor (p.y) + .5);
+               cairo_stroke (cr);
+
+               _compute_intersection_point (&t->left, t->bottom, &p);
+               cairo_matrix_transform_point (&m, &p.x, &p.y);
+               cairo_move_to (cr, floor (p.x), floor (p.y) + .5);
+               cairo_set_dash (cr, dash, 2, fmod (floor (p.x), dash[0] + dash[1]));
+
+               _compute_intersection_point (&t->right, t->bottom, &p);
+               cairo_matrix_transform_point (&m, &p.x, &p.y);
+               cairo_line_to (cr, ceil (p.x), floor (p.y) + .5);
+               cairo_stroke (cr);
+           }
+       } cairo_restore (cr);
+
+       /* left extents */
+       cairo_save (cr); {
+           cairo_save (cr); {
+               cairo_scale (cr, sf, sf);
+               cairo_translate (cr, -x0, -y0);
+
+               for (n = 0; n < traps->num_traps; n++) {
+                   const trapezoid_t *t = &traps->traps[n];
+                   cairo_move_to (cr, t->left.p1.x, t->left.p1.y);
+                   cairo_line_to (cr, t->left.p2.x, t->left.p2.y);
+               }
+           } cairo_restore (cr);
+           cairo_set_source_rgb (cr, 1, 0, 0);
+           cairo_set_line_width (cr, 1.);
+           cairo_set_dash (cr, dash, 2, 0.);
+           cairo_stroke (cr);
+       } cairo_restore (cr);
+
+       /* left line */
+       cairo_save (cr); {
+           cairo_save (cr); {
+               cairo_scale (cr, sf, sf);
+               cairo_translate (cr, -x0, -y0);
+
+               for (n = 0; n < traps->num_traps; n++) {
+                   const trapezoid_t *t = &traps->traps[n];
+                   _compute_intersection_point (&t->left, t->top, &p);
+                   cairo_move_to (cr, p.x, p.y);
+                   _compute_intersection_point (&t->left, t->bottom, &p);
+                   cairo_line_to (cr, p.x, p.y);
+               }
+           } cairo_restore (cr);
+           cairo_set_source_rgb (cr, 1, 0, 0);
+           cairo_stroke (cr);
+       } cairo_restore (cr);
+
+       /* right extents */
+       cairo_save (cr); {
+           cairo_save (cr); {
+               cairo_scale (cr, sf, sf);
+               cairo_translate (cr, -x0, -y0);
+
+               for (n = 0; n < traps->num_traps; n++) {
+                   const trapezoid_t *t = &traps->traps[n];
+                   cairo_move_to (cr, t->right.p1.x, t->right.p1.y);
+                   cairo_line_to (cr, t->right.p2.x, t->right.p2.y);
+               }
+           } cairo_restore (cr);
+           cairo_set_source_rgb (cr, 0, 0, 1);
+           cairo_set_line_width (cr, 1.);
+           cairo_set_dash (cr, dash, 2, 0.);
+           cairo_stroke (cr);
+       } cairo_restore (cr);
+
+       /* right line */
+       cairo_save (cr); {
+           cairo_save (cr); {
+               cairo_scale (cr, sf, sf);
+               cairo_translate (cr, -x0, -y0);
+
+               for (n = 0; n < traps->num_traps; n++) {
+                   const trapezoid_t *t = &traps->traps[n];
+                   _compute_intersection_point (&t->right, t->top, &p);
+                   cairo_move_to (cr, p.x, p.y);
+                   _compute_intersection_point (&t->right, t->bottom, &p);
+                   cairo_line_to (cr, p.x, p.y);
+               } cairo_restore (cr);
+               cairo_set_source_rgb (cr, 0, 0, 1);
+               cairo_stroke (cr);
+           } cairo_restore (cr);
+       }
+
+       /* end-points */
+       cairo_save (cr); {
+           cairo_save (cr); {
+               cairo_scale (cr, sf, sf);
+               cairo_translate (cr, -x0, -y0);
+
+               for (n = 0; n < traps->num_traps; n++) {
+                   const trapezoid_t *t = &traps->traps[n];
+                   _compute_intersection_point (&t->left, t->top, &p);
+                   cairo_move_to (cr, p.x, p.y);
+                   cairo_close_path (cr);
+                   _compute_intersection_point (&t->left, t->bottom, &p);
+                   cairo_move_to (cr, p.x, p.y);
+                   cairo_close_path (cr);
+                   _compute_intersection_point (&t->right, t->top, &p);
+                   cairo_move_to (cr, p.x, p.y);
+                   cairo_close_path (cr);
+                   _compute_intersection_point (&t->right, t->bottom, &p);
+                   cairo_move_to (cr, p.x, p.y);
+                   cairo_close_path (cr);
+               }
+           } cairo_restore (cr);
+           cairo_set_source_rgb (cr, 0, 0, 0);
+           cairo_set_dash (cr, dots, 2, 0.);
+           cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
+           cairo_set_line_width (cr, 4.);
+           cairo_stroke (cr);
+       } cairo_restore (cr);
+
+       cairo_restore (cr);
+    } else {
+       cairo_save (cr);
+
+       for (n = 0; n < edges->num_edges; n++) {
+           const edge_t *e = &edges->edges[n];
+
+           cairo_save (cr); {
+               cairo_scale (cr, sf, sf);
+               cairo_translate (cr, -x0, -y0);
+               cairo_move_to (cr, e->p1.x, e->p1.y);
+               cairo_line_to (cr, e->p2.x, e->p2.y);
+           } cairo_restore (cr);
+
+           if (e->dir < 0) {
+               cairo_set_source_rgb (cr, 0, 0, 1);
+               cairo_set_dash (cr, dash, 2, dash[0]);
+           } else {
+               cairo_set_source_rgb (cr, 1, 0, 0);
+               cairo_set_dash (cr, dash, 2, 0.);
+           }
+
+           cairo_set_line_cap (cr, CAIRO_LINE_CAP_BUTT);
+           cairo_set_line_width (cr, 1.);
+           cairo_stroke (cr);
+
+           cairo_save (cr); {
+               cairo_scale (cr, sf, sf);
+               cairo_translate (cr, -x0, -y0);
+               cairo_move_to (cr, e->p1.x, e->p1.y);
+               cairo_close_path (cr);
+               cairo_move_to (cr, e->p2.x, e->p2.y);
+               cairo_close_path (cr);
+           } cairo_restore (cr);
+           cairo_set_source_rgb (cr, 0, 0, 0);
+           cairo_set_dash (cr, dots, 2, 0.);
+           cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
+           cairo_set_line_width (cr, 4.);
+           cairo_stroke (cr);
+       }
+
+       cairo_restore (cr);
+    }
+
+    cairo_destroy (cr);
+    return surface;
+}
+
+static void
+trap_view_draw (TrapView *self, cairo_t *cr)
+{
+    traps_t *traps;
+    edges_t *edges;
+    gdouble sf_x, sf_y, sf;
+    gdouble mid, dim;
+    gdouble x0, y0;
+    double dash[2] = {8, 8};
+    int n;
+    box_t extents;
+    point_t p;
+
+    if (self->pixmap_width != self->widget.allocation.width ||
+       self->pixmap_height != self->widget.allocation.height)
+    {
+       cairo_surface_destroy (self->pixmap);
+       self->pixmap = pixmap_create (self, cairo_get_target (cr));
+       self->pixmap_width = self->widget.allocation.width;
+       self->pixmap_height = self->widget.allocation.height;
+    }
+
+    cairo_save (cr);
+    cairo_set_source_surface (cr, self->pixmap, 0, 0);
+    cairo_paint (cr);
+    cairo_restore (cr);
+
+    traps = self->current_traps;
+    if (traps == NULL)
+       return;
+
+    extents = traps->extents;
+    edges = self->current_edges;
+    if (edges != NULL) {
+       if (edges->extents.p1.x < extents.p1.x)
+           extents.p1.x = edges->extents.p1.x;
+       if (edges->extents.p1.y < extents.p1.y)
+           extents.p1.y = edges->extents.p1.y;
+       if (edges->extents.p2.x > extents.p2.x)
+           extents.p2.x = edges->extents.p2.x;
+       if (edges->extents.p2.y > extents.p2.y)
+           extents.p2.y = edges->extents.p2.y;
+    }
+
+    mid = (extents.p2.x + extents.p1.x) / 2.;
+    dim = (extents.p2.x - extents.p1.x) / 2. * 1.25;
+    sf_x = self->widget.allocation.width / dim / 2;
+
+    mid = (extents.p2.y + extents.p1.y) / 2.;
+    dim = (extents.p2.y - extents.p1.y) / 2. * 1.25;
+    sf_y = self->widget.allocation.height / dim / 2;
+
+    sf = MIN (sf_x, sf_y);
+
+    mid = (extents.p2.x + extents.p1.x) / 2.;
+    dim = sf_x / sf * (extents.p2.x - extents.p1.x) / 2. * 1.25;
+    x0 = mid - dim;
+    mid = (extents.p2.y + extents.p1.y) / 2.;
+    dim = sf_y / sf * (extents.p2.y - extents.p1.y) / 2. * 1.25;
+    y0 = mid - dim;
+
+    cairo_save (cr);
+
+    /* draw a zoom view of the area around the mouse */
+    {
+       double zoom = self->mag_zoom;
+       int size = self->mag_size;
+
+       cairo_save (cr);
+
+       /* bottom right */
+       cairo_rectangle (cr, self->mag_x, self->mag_y, size, size);
+       cairo_stroke_preserve (cr);
+       cairo_set_source_rgb (cr, 1, 1, 1);
+       cairo_fill_preserve (cr);
+       cairo_clip (cr);
+
+       /* compute roi in extents */
+       cairo_translate (cr, self->mag_x + size/2, self->mag_y + size/2);
+
+       cairo_save (cr);
+       cairo_scale (cr, zoom, zoom);
+       cairo_translate (cr, -(self->px / sf + x0), -(self->py /sf + y0));
+       for (n = 0; n < traps->num_traps; n++) {
+           const trapezoid_t *t = &traps->traps[n];
+
+           _compute_intersection_point (&t->left, t->top, &p);
+           cairo_move_to (cr, p.x, p.y);
+           _compute_intersection_point (&t->right, t->top, &p);
+           cairo_line_to (cr, p.x, p.y);
+           _compute_intersection_point (&t->right, t->bottom, &p);
+           cairo_line_to (cr, p.x, p.y);
+           _compute_intersection_point (&t->left, t->bottom, &p);
+           cairo_line_to (cr, p.x, p.y);
+           cairo_close_path (cr);
+           cairo_set_source_rgba (cr, 0, 1, 0, .2);
+           cairo_fill (cr);
+       }
+       cairo_restore (cr);
+
+       cairo_save (cr); {
+           cairo_matrix_t m;
+           cairo_matrix_init_scale (&m, zoom, zoom);
+           cairo_matrix_translate (&m, -(self->px / sf + x0), -(self->py /sf + y0));
+
+           cairo_set_source_rgb (cr, 0, 0, 0);
+           cairo_set_line_width (cr, 1.);
+           cairo_set_line_cap (cr, CAIRO_LINE_CAP_BUTT);
+           for (n = 0; n < traps->num_traps; n++) {
+               const trapezoid_t *t = &traps->traps[n];
+
+               _compute_intersection_point (&t->left, t->top, &p);
+               cairo_matrix_transform_point (&m, &p.x, &p.y);
+               cairo_move_to (cr, floor (p.x), floor (p.y) + .5);
+               cairo_set_dash (cr, dash, 2, fmod (floor (p.x), dash[0] + dash[1]));
+
+               _compute_intersection_point (&t->right, t->top, &p);
+               cairo_matrix_transform_point (&m, &p.x, &p.y);
+               cairo_line_to (cr, ceil (p.x), floor (p.y) + .5);
+               cairo_stroke (cr);
+
+               _compute_intersection_point (&t->left, t->bottom, &p);
+               cairo_matrix_transform_point (&m, &p.x, &p.y);
+               cairo_move_to (cr, floor (p.x), floor (p.y) + .5);
+               cairo_set_dash (cr, dash, 2, fmod (floor (p.x), dash[0] + dash[1]));
+
+               _compute_intersection_point (&t->right, t->bottom, &p);
+               cairo_matrix_transform_point (&m, &p.x, &p.y);
+               cairo_line_to (cr, ceil (p.x), floor (p.y) + .5);
+               cairo_stroke (cr);
+           }
+       } cairo_restore (cr);
+
+       cairo_save (cr); { /* left extents */
+           cairo_save (cr); {
+               cairo_scale (cr, zoom, zoom);
+               cairo_translate (cr, -(self->px / sf + x0), -(self->py /sf + y0));
+               for (n = 0; n < traps->num_traps; n++) {
+                   const trapezoid_t *t = &traps->traps[n];
+                   cairo_move_to (cr, t->left.p1.x, t->left.p1.y);
+                   cairo_line_to (cr, t->left.p2.x, t->left.p2.y);
+               }
+           } cairo_restore (cr);
+           cairo_set_source_rgb (cr, 1, 0, 0);
+           cairo_set_line_width (cr, .5);
+           cairo_set_dash (cr, dash, 2, 0.);
+           cairo_stroke (cr);
+       } cairo_restore (cr);
+       cairo_save (cr); { /* right extents */
+           cairo_save (cr); {
+               cairo_scale (cr, zoom, zoom);
+               cairo_translate (cr, -(self->px / sf + x0), -(self->py /sf + y0));
+
+               for (n = 0; n < traps->num_traps; n++) {
+                   const trapezoid_t *t = &traps->traps[n];
+                   cairo_move_to (cr, t->right.p1.x, t->right.p1.y);
+                   cairo_line_to (cr, t->right.p2.x, t->right.p2.y);
+               }
+           } cairo_restore (cr);
+           cairo_set_source_rgb (cr, 0, 0, 1);
+           cairo_set_line_width (cr, .5);
+           cairo_set_dash (cr, dash, 2, 0.);
+           cairo_stroke (cr);
+       } cairo_restore (cr);
+
+       cairo_save (cr); { /* left lines */
+           cairo_save (cr);
+           cairo_scale (cr, zoom, zoom);
+           cairo_translate (cr, -(self->px / sf + x0), -(self->py /sf + y0));
+           for (n = 0; n < traps->num_traps; n++) {
+               const trapezoid_t *t = &traps->traps[n];
+               _compute_intersection_point (&t->left, t->top, &p);
+               cairo_move_to (cr, p.x, p.y);
+               _compute_intersection_point (&t->left, t->bottom, &p);
+               cairo_line_to (cr, p.x, p.y);
+           }
+           cairo_restore (cr);
+           cairo_set_source_rgb (cr, 1, 0, 0);
+           cairo_stroke (cr);
+       } cairo_restore (cr);
+       cairo_save (cr); { /* right lines */
+           cairo_save (cr);
+           cairo_scale (cr, zoom, zoom);
+           cairo_translate (cr, -(self->px / sf + x0), -(self->py /sf + y0));
+           for (n = 0; n < traps->num_traps; n++) {
+               const trapezoid_t *t = &traps->traps[n];
+               _compute_intersection_point (&t->right, t->top, &p);
+               cairo_move_to (cr, p.x, p.y);
+               _compute_intersection_point (&t->right, t->bottom, &p);
+               cairo_line_to (cr, p.x, p.y);
+           }
+           cairo_restore (cr);
+           cairo_set_source_rgb (cr, 0, 0, 1);
+           cairo_stroke (cr);
+       } cairo_restore (cr);
+
+       /* end-points */
+       cairo_save (cr); {
+           double dots[2] = {0., 1.};
+
+           cairo_save (cr);
+           cairo_scale (cr, zoom, zoom);
+           cairo_translate (cr, -(self->px / sf + x0), -(self->py /sf + y0));
+           for (n = 0; n < traps->num_traps; n++) {
+               const trapezoid_t *t = &traps->traps[n];
+               _compute_intersection_point (&t->left, t->top, &p);
+               cairo_move_to (cr, p.x, p.y);
+               cairo_close_path (cr);
+               _compute_intersection_point (&t->left, t->bottom, &p);
+               cairo_move_to (cr, p.x, p.y);
+               cairo_close_path (cr);
+               _compute_intersection_point (&t->right, t->top, &p);
+               cairo_move_to (cr, p.x, p.y);
+               cairo_close_path (cr);
+               _compute_intersection_point (&t->right, t->bottom, &p);
+               cairo_move_to (cr, p.x, p.y);
+               cairo_close_path (cr);
+           }
+           cairo_restore (cr);
+           cairo_set_source_rgb (cr, 0, 0, 0);
+           cairo_set_dash (cr, dots, 2, 0.);
+           cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
+           cairo_set_line_width (cr, 4.);
+           cairo_stroke (cr);
+       } cairo_restore (cr);
+
+       /* grid */
+       cairo_save (cr); {
+           int i;
+
+           cairo_translate (cr,
+                            -zoom*fmod (self->px/sf + x0, 1.),
+                            -zoom*fmod (self->py/sf + y0, 1.));
+           for (i = -size/2/zoom - 1; i <= size/2/zoom + 1; i++) {
+               cairo_move_to (cr, zoom*i, -size/2);
+               cairo_line_to (cr, zoom*i, size/2 + zoom);
+               cairo_move_to (cr, -size/2, zoom*i);
+               cairo_line_to (cr, size/2 + zoom, zoom*i);
+           }
+           cairo_set_source_rgba (cr, .7, .7, .7, .5);
+           cairo_set_line_width (cr, 1.);
+           cairo_stroke (cr);
+       } cairo_restore (cr);
+    }
+
+    cairo_restore (cr);
+}
+
+static gdouble
+trapezoid_area (const trapezoid_t *t)
+{
+    gdouble inner_left, inner_right;
+    gdouble outer_left, outer_right;
+    gdouble height;
+    gdouble area;
+
+    /* split into 3 sections: a rectangle with a pair of triangular bookends */
+    inner_left = _compute_intersection_x_for_y (&t->left, t->top);
+    outer_left = _compute_intersection_x_for_y (&t->left, t->bottom);
+    if (outer_left > inner_left) {
+       gdouble t = outer_left;
+       outer_left = inner_left;
+       inner_left = t;
+    }
+
+    inner_right = _compute_intersection_x_for_y (&t->right, t->top);
+    outer_right = _compute_intersection_x_for_y (&t->right, t->bottom);
+    if (outer_right > inner_right) {
+       gdouble t = outer_right;
+       outer_right = inner_right;
+       inner_right = t;
+    }
+
+    if (outer_left > outer_right) { /* reverse */
+       gdouble t;
+
+       t = outer_left;
+       outer_left = inner_right;
+       inner_right = t;
+
+       t = inner_left;
+       inner_left = outer_right;
+       outer_right = t;
+    }
+
+    height = t->bottom - t->top;
+    area  = (inner_left - outer_left) * height / 2;
+    area += (outer_right - inner_right) * height / 2;
+    area += (inner_right - inner_left) * height;
+
+    return area;
+}
+
+static gdouble
+traps_compute_total_area (const traps_t *traps)
+{
+    int n;
+    gdouble area = 0.;
+    for (n = 0; n < traps->num_traps; n++)
+       area += trapezoid_area (&traps->traps[n]);
+    return area;
+}
+
+static void
+trap_view_draw_labels (TrapView *self, cairo_t *cr)
+{
+    PangoLayout *layout;
+    gint width, height;
+    gdouble total_area;
+    gchar *str;
+    traps_t *traps;
+
+    traps = self->current_traps;
+    if (traps == NULL)
+       return;
+
+    /* convert total area from fixed-point (assuming 24.8) */
+    total_area = traps_compute_total_area (traps) / (256. * 256.);
+    str = g_strdup_printf ("Number of trapezoids:\t%d\n"
+                          "Total area of trapezoids:\t%.2f",
+                          traps->num_traps,
+                          total_area);
+    layout = gtk_widget_create_pango_layout (&self->widget, str);
+    g_free (str);
+
+    pango_layout_get_pixel_size (layout, &width, &height);
+
+    cairo_move_to (cr, 10, 10 + height);
+    pango_cairo_show_layout (cr, layout);
+    g_object_unref (layout);
+}
+
+static gboolean
+trap_view_expose (GtkWidget *w, GdkEventExpose *ev)
+{
+    TrapView *self = (TrapView *) w;
+    cairo_t *cr;
+
+    cr = gdk_cairo_create (w->window);
+    gdk_cairo_region (cr, ev->region);
+    cairo_clip (cr);
+
+    trap_view_draw (self, cr);
+    trap_view_draw_labels (self, cr);
+
+    cairo_destroy (cr);
+    return FALSE;
+}
+
+static void
+trap_view_advance (TrapView *self)
+{
+    if (self->current_traps && self->current_traps->prev)
+       self->current_traps = self->current_traps->prev;
+    if (self->current_edges && self->current_edges->prev)
+       self->current_edges = self->current_edges->prev;
+    self->pixmap_width = self->pixmap_height = 0;
+    gtk_widget_queue_draw (&self->widget);
+}
+
+static void
+trap_view_back (TrapView *self)
+{
+    if (self->current_traps && self->current_traps->next)
+       self->current_traps = self->current_traps->next;
+    if (self->current_edges && self->current_edges->next)
+       self->current_edges = self->current_edges->next;
+    self->pixmap_width = self->pixmap_height = 0;
+    gtk_widget_queue_draw (&self->widget);
+}
+
+static void
+trap_view_group_foreach (TrapView *group, GFunc func, gpointer data)
+{
+    while (group) {
+       func (group, data);
+       group = group->group_next;
+    }
+}
+
+static gboolean
+trap_view_key_press (GtkWidget *w, GdkEventKey *ev)
+{
+    TrapView *self = (TrapView *) w;
+
+    switch (ev->keyval) {
+    case GDK_BackSpace:
+       trap_view_group_foreach (self->group_head,
+                                (GFunc) trap_view_back,
+                                NULL);
+       break;
+
+    case GDK_space:
+       trap_view_group_foreach (self->group_head,
+                                (GFunc) trap_view_advance,
+                                NULL);
+       break;
+
+    case GDK_Return:
+       trap_view_group_foreach (self->group_head,
+                                (GFunc) trap_view_advance,
+                                NULL);
+       break;
+
+    case GDK_Escape:
+    case GDK_Q:
+       gtk_main_quit ();
+       break;
+    }
+
+    return FALSE;
+}
+
+static gboolean
+trap_view_button_press (GtkWidget *w, GdkEventButton *ev)
+{
+    TrapView *self = (TrapView *) w;
+
+    if (ev->x < self->mag_x ||
+       ev->y < self->mag_y ||
+       ev->x > self->mag_x + self->mag_size ||
+       ev->y > self->mag_y + self->mag_size)
+    {
+       if (ev->type == GDK_BUTTON_PRESS) {
+           if (self->current_traps == NULL)
+               return FALSE;
+
+           if (ev->button == 1) {
+               trap_view_group_foreach (self->group_head,
+                                        (GFunc) trap_view_advance,
+                                        NULL);
+           } else if (ev->button == 3) {
+               trap_view_group_foreach (self->group_head,
+                                        (GFunc) trap_view_back,
+                                        NULL);
+           }
+       }
+    }
+    else
+    {
+       self->in_mag_drag = TRUE;
+       self->mag_drag_x = ev->x;
+       self->mag_drag_y = ev->y;
+    }
+
+    return FALSE;
+}
+
+static gboolean
+trap_view_button_release (GtkWidget *w, GdkEventButton *ev)
+{
+    TrapView *self = (TrapView *) w;
+
+    self->in_mag_drag = FALSE;
+
+    return FALSE;
+}
+
+static void
+trap_view_update_mouse (TrapView *self, GdkEventMotion *ev)
+{
+    self->px = ev->x;
+    self->py = ev->y;
+
+    gtk_widget_queue_draw (&self->widget);
+}
+
+static void
+trap_view_update_magnifier (TrapView *self, gint *xy)
+{
+    self->mag_x = xy[0];
+    self->mag_y = xy[1];
+
+    gtk_widget_queue_draw (&self->widget);
+}
+
+static gboolean
+trap_view_motion (GtkWidget *w, GdkEventMotion *ev)
+{
+    TrapView *self = (TrapView *) w;
+
+    if (self->in_mag_drag) {
+       int xy[2];
+
+       xy[0] = self->mag_x + ev->x - self->mag_drag_x;
+       xy[1] = self->mag_y + ev->y - self->mag_drag_y;
+
+       trap_view_group_foreach (self->group_head,
+                                (GFunc) trap_view_update_magnifier,
+                                xy);
+
+       self->mag_drag_x = ev->x;
+       self->mag_drag_y = ev->y;
+    } else if (ev->x < self->mag_x ||
+              ev->y < self->mag_y ||
+              ev->x > self->mag_x + self->mag_size ||
+              ev->y > self->mag_y + self->mag_size)
+    {
+       trap_view_group_foreach (self->group_head,
+                                (GFunc) trap_view_update_mouse,
+                                ev);
+    }
+
+    return FALSE;
+}
+
+static void
+trap_view_realize (GtkWidget *widget)
+{
+    GdkWindowAttr attributes;
+
+    GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
+
+    attributes.window_type = GDK_WINDOW_CHILD;
+    attributes.x = widget->allocation.x;
+    attributes.y = widget->allocation.y;
+    attributes.width  = widget->allocation.width;
+    attributes.height = widget->allocation.height;
+    attributes.wclass = GDK_INPUT_OUTPUT;
+    attributes.visual = gtk_widget_get_visual (widget);
+    attributes.colormap = gtk_widget_get_colormap (widget);
+    attributes.event_mask = gtk_widget_get_events (widget) |
+                           GDK_BUTTON_PRESS_MASK |
+                           GDK_BUTTON_RELEASE_MASK |
+                           GDK_KEY_PRESS_MASK |
+                           GDK_KEY_RELEASE_MASK |
+                           GDK_POINTER_MOTION_MASK |
+                           GDK_BUTTON_MOTION_MASK |
+                           GDK_EXPOSURE_MASK;
+
+    widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
+                                    &attributes,
+                                    GDK_WA_X | GDK_WA_Y |
+                                    GDK_WA_VISUAL | GDK_WA_COLORMAP);
+    gdk_window_set_user_data (widget->window, widget);
+
+    widget->style = gtk_style_attach (widget->style, widget->window);
+    gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
+}
+
+static void
+trap_view_size_allocate (GtkWidget *w, GdkRectangle *r)
+{
+    TrapView *self = (TrapView *) w;
+
+    GTK_WIDGET_CLASS (trap_view_parent_class)->size_allocate (w, r);
+
+    self->mag_x = w->allocation.width - self->mag_size - 10;
+    self->mag_y = w->allocation.height - self->mag_size - 10;
+}
+
+static void
+trap_view_finalize (GObject *obj)
+{
+    G_OBJECT_CLASS (trap_view_parent_class)->finalize (obj);
+}
+
+static void
+trap_view_class_init (TrapViewClass *klass)
+{
+    GObjectClass *object_class = (GObjectClass *) klass;
+    GtkWidgetClass *widget_class = (GtkWidgetClass *) klass;
+
+    object_class->finalize = trap_view_finalize;
+
+    widget_class->realize = trap_view_realize;
+    widget_class->size_allocate = trap_view_size_allocate;
+    widget_class->expose_event = trap_view_expose;
+    widget_class->key_press_event = trap_view_key_press;
+    widget_class->button_press_event = trap_view_button_press;
+    widget_class->button_release_event = trap_view_button_release;
+    widget_class->motion_notify_event = trap_view_motion;
+}
+
+static void
+trap_view_init (TrapView *self)
+{
+    self->mag_zoom = 10;
+    self->mag_size = 200;
+
+    GTK_WIDGET_SET_FLAGS (self, GTK_CAN_FOCUS);
+}
+
+static traps_t *
+_traps_add_trapezoid (TrapView *tv, traps_t *traps, const trapezoid_t *trap)
+{
+    if (trap->top < traps->extents.p1.y)
+       traps->extents.p1.y = trap->top;
+    if (trap->bottom > traps->extents.p2.y)
+       traps->extents.p2.y = trap->bottom;
+
+    if (trap->left.p1.x < traps->extents.p1.x)
+       traps->extents.p1.x = trap->left.p1.x;
+    if (trap->left.p2.x < traps->extents.p1.x)
+       traps->extents.p1.x = trap->left.p2.x;
+
+    if (trap->right.p1.x > traps->extents.p2.x)
+       traps->extents.p2.x = trap->right.p1.x;
+    if (trap->right.p2.x > traps->extents.p2.x)
+       traps->extents.p2.x = trap->right.p2.x;
+
+    if (traps->num_traps == traps->size) {
+       int newsize = 2 * traps->size;
+       void *newtraps;
+
+       newtraps = g_realloc (traps,
+                             sizeof (traps_t) + newsize * sizeof (trapezoid_t));
+       if (newtraps == NULL)
+           return traps;
+
+       if (tv->current_traps == traps)
+           tv->current_traps = newtraps;
+
+       traps = newtraps;
+       traps->size = newsize;
+
+       if (traps->next != NULL)
+           traps->next->prev = newtraps;
+       if (traps->prev != NULL)
+           traps->prev->next = newtraps;
+       else
+           tv->traps_list = newtraps;
+    }
+
+    traps->traps[traps->num_traps++] = *trap;
+
+    return traps;
+}
+
+static traps_t *
+traps_new (TrapView *tv)
+{
+    traps_t *t;
+
+    t = g_malloc (sizeof (traps_t) + 16 * sizeof (trapezoid_t));
+    t->prev = NULL;
+    t->next = tv->traps_list;
+    if (tv->traps_list)
+       tv->traps_list->prev = t;
+    tv->traps_list = t;
+
+    if (tv->current_traps == NULL)
+       tv->current_traps = t;
+
+    t->size = 16;
+    t->num_traps = 0;
+    t->extents.p1.x = G_MAXDOUBLE;
+    t->extents.p1.y = G_MAXDOUBLE;
+    t->extents.p2.x = -G_MAXDOUBLE;
+    t->extents.p2.y = -G_MAXDOUBLE;
+
+    return t;
+}
+
+static edges_t *
+_edges_add_edge (TrapView *tv, edges_t *edges, edge_t *e)
+{
+    if (e->top < edges->extents.p1.y)
+       edges->extents.p1.y = e->top;
+    if (e->bottom > edges->extents.p2.y)
+       edges->extents.p2.y = e->bottom;
+
+    _compute_intersection_point (&e->line, e->top, &e->p1);
+    _compute_intersection_point (&e->line, e->bottom, &e->p2);
+
+    if (e->p1.x < edges->extents.p1.x)
+       edges->extents.p1.x = e->p1.x;
+    if (e->p2.x < edges->extents.p1.x)
+       edges->extents.p1.x = e->p2.x;
+
+    if (e->p1.x > edges->extents.p2.x)
+       edges->extents.p2.x = e->p1.x;
+    if (e->p2.x > edges->extents.p2.x)
+       edges->extents.p2.x = e->p2.x;
+
+    if (edges->num_edges == edges->size) {
+       int newsize = 2 * edges->size;
+       void *newedges;
+
+       newedges = g_realloc (edges,
+                             sizeof (edges_t) + newsize * sizeof (edge_t));
+       if (newedges == NULL)
+           return edges;
+
+       if (tv->current_edges == edges)
+           tv->current_edges = newedges;
+
+       edges = newedges;
+       edges->size = newsize;
+
+       if (edges->next != NULL)
+           edges->next->prev = newedges;
+       if (edges->prev != NULL)
+           edges->prev->next = newedges;
+       else
+           tv->edges_list = newedges;
+    }
+
+    edges->edges[edges->num_edges++] = *e;
+
+    return edges;
+}
+
+static edges_t *
+edges_new (TrapView *tv)
+{
+    edges_t *t;
+
+    t = g_malloc (sizeof (edges_t) + 16 * sizeof (edge_t));
+    t->prev = NULL;
+    t->next = tv->edges_list;
+    if (tv->edges_list)
+       tv->edges_list->prev = t;
+    tv->edges_list = t;
+
+    if (tv->current_edges == NULL)
+       tv->current_edges = t;
+
+    t->size = 16;
+    t->num_edges = 0;
+    t->extents.p1.x = G_MAXDOUBLE;
+    t->extents.p1.y = G_MAXDOUBLE;
+    t->extents.p2.x = -G_MAXDOUBLE;
+    t->extents.p2.y = -G_MAXDOUBLE;
+
+    return t;
+}
+
+int
+main (int argc, char **argv)
+{
+    TrapView *tv, *group_head = NULL, *group_prev = NULL;
+    GtkWidget *window, *hbox;
+    FILE *file;
+
+    gtk_init (&argc, &argv);
+
+    hbox = gtk_hbox_new (TRUE, 0);
+
+    file = fopen (argv[1], "r");
+    if (file != NULL) {
+       char *line = NULL;
+       size_t len = 0;
+       traps_t *traps;
+
+       tv = g_object_new (trap_view_get_type (), NULL);
+       gtk_box_pack_start (GTK_BOX (hbox), &tv->widget, TRUE, TRUE, 0);
+       gtk_widget_show (&tv->widget);
+
+       tv->group_prev = group_prev;
+       tv->group_next = NULL;
+       if (group_prev)
+           group_prev->group_next = tv;
+       group_prev = tv;
+       if (group_head == NULL)
+           group_head = tv;
+       tv->group_head = group_head;
+
+       traps = traps_new (tv);
+       while (getline (&line, &len, file) != -1) {
+           trapezoid_t t;
+
+           if (sscanf (line,
+                      "%lf %lf L:(%lf, %lf), (%lf, %lf) R:(%lf, %lf), (%lf, %lf)",
+                      &t.top, &t.bottom,
+                      &t.left.p1.x, &t.left.p1.y,
+                      &t.left.p2.x, &t.left.p2.y,
+                      &t.right.p1.x, &t.right.p1.y,
+                      &t.right.p2.x, &t.right.p2.y) == 10) {
+               traps = _traps_add_trapezoid (tv, traps, &t);
+           } else {
+               if (traps->num_traps) {
+                   g_print ("read %d trapezoids\n", traps->num_traps);
+                   g_print ("extents=(%lg, %lg), (%lg, %lg)\n",
+                           traps->extents.p1.x, traps->extents.p1.y,
+                           traps->extents.p2.x, traps->extents.p2.y);
+                   traps = traps_new (tv);
+               }
+           }
+       }
+       free (line);
+       fclose (file);
+
+       if (traps->num_traps) {
+           g_print ("read %d trapezoids\n", traps->num_traps);
+           g_print ("extents=(%lg, %lg), (%lg, %lg)\n",
+                    traps->extents.p1.x, traps->extents.p1.y,
+                    traps->extents.p2.x, traps->extents.p2.y);
+       }
+    }
+
+    file = fopen (argv[2], "r");
+    if (file != NULL) {
+       char *line = NULL;
+       size_t len = 0;
+       traps_t *traps;
+
+       tv = g_object_new (trap_view_get_type (), NULL);
+       gtk_box_pack_start (GTK_BOX (hbox), &tv->widget, TRUE, TRUE, 0);
+       gtk_widget_show (&tv->widget);
+
+       tv->group_prev = group_prev;
+       tv->group_next = NULL;
+       if (group_prev)
+           group_prev->group_next = tv;
+       group_prev = tv;
+       if (group_head == NULL)
+           group_head = tv;
+       tv->group_head = group_head;
+
+       traps = traps_new (tv);
+       while (getline (&line, &len, file) != -1) {
+           trapezoid_t t;
+
+           if (sscanf (line,
+                      "%lf %lf L:(%lf, %lf), (%lf, %lf) R:(%lf, %lf), (%lf, %lf)",
+                      &t.top, &t.bottom,
+                      &t.left.p1.x, &t.left.p1.y,
+                      &t.left.p2.x, &t.left.p2.y,
+                      &t.right.p1.x, &t.right.p1.y,
+                      &t.right.p2.x, &t.right.p2.y) == 10) {
+               traps = _traps_add_trapezoid (tv, traps, &t);
+           } else {
+               if (traps->num_traps) {
+                   g_print ("read %d trapezoids\n", traps->num_traps);
+                   g_print ("extents=(%lg, %lg), (%lg, %lg)\n",
+                           traps->extents.p1.x, traps->extents.p1.y,
+                           traps->extents.p2.x, traps->extents.p2.y);
+                   traps = traps_new (tv);
+               }
+           }
+       }
+       free (line);
+       fclose (file);
+
+       if (traps->num_traps) {
+           g_print ("read %d trapezoids\n", traps->num_traps);
+           g_print ("extents=(%lg, %lg), (%lg, %lg)\n",
+                    traps->extents.p1.x, traps->extents.p1.y,
+                    traps->extents.p2.x, traps->extents.p2.y);
+       }
+    }
+
+#if 1
+    if (argc >=4) {
+       file = fopen (argv[3], "r");
+       if (file != NULL) {
+           char *line = NULL;
+           size_t len = 0;
+           edges_t *edges;
+
+           edges = edges_new (tv);
+           while (getline (&line, &len, file) != -1) {
+               edge_t e;
+
+               if (sscanf (line,
+                          "(%lf, %lf), (%lf, %lf) %lf %lf %d",
+                          &e.line.p1.x, &e.line.p1.y,
+                          &e.line.p2.x, &e.line.p2.y,
+                          &e.top, &e.bottom,
+                          &e.dir) == 7) {
+                   edges = _edges_add_edge (tv, edges, &e);
+               } else {
+                   if (edges->num_edges) {
+                       g_print ("read %d edges\n", edges->num_edges);
+                       g_print ("extents=(%lg, %lg), (%lg, %lg)\n",
+                               edges->extents.p1.x, edges->extents.p1.y,
+                               edges->extents.p2.x, edges->extents.p2.y);
+                       edges = edges_new (tv);
+                   }
+               }
+           }
+           free (line);
+           fclose (file);
+
+           if (edges->num_edges) {
+               g_print ("read %d edges\n", edges->num_edges);
+               g_print ("extents=(%lg, %lg), (%lg, %lg)\n",
+                        edges->extents.p1.x, edges->extents.p1.y,
+                        edges->extents.p2.x, edges->extents.p2.y);
+           }
+       }
+    }
+#else
+    if (argc >= 4) {
+       file = fopen (argv[3], "r");
+       if (file != NULL) {
+           char *line = NULL;
+           size_t len = 0;
+           traps_t *traps;
+
+           tv = g_object_new (trap_view_get_type (), NULL);
+           gtk_box_pack_start (GTK_BOX (hbox), &tv->widget, TRUE, TRUE, 0);
+           gtk_widget_show (&tv->widget);
+
+           tv->group_prev = group_prev;
+           tv->group_next = NULL;
+           if (group_prev)
+               group_prev->group_next = tv;
+           group_prev = tv;
+           if (group_head == NULL)
+               group_head = tv;
+           tv->group_head = group_head;
+
+           traps = traps_new (tv);
+           while (getline (&line, &len, file) != -1) {
+               trapezoid_t t;
+
+               if (sscanf (line,
+                           "%lf %lf L:(%lf, %lf), (%lf, %lf) R:(%lf, %lf), (%lf, %lf)",
+                           &t.top, &t.bottom,
+                           &t.left.p1.x, &t.left.p1.y,
+                           &t.left.p2.x, &t.left.p2.y,
+                           &t.right.p1.x, &t.right.p1.y,
+                           &t.right.p2.x, &t.right.p2.y) == 10) {
+                   traps = _traps_add_trapezoid (tv, traps, &t);
+               } else {
+                   if (traps->num_traps) {
+                       g_print ("read %d trapezoids\n", traps->num_traps);
+                       g_print ("extents=(%lg, %lg), (%lg, %lg)\n",
+                                traps->extents.p1.x, traps->extents.p1.y,
+                                traps->extents.p2.x, traps->extents.p2.y);
+                       traps = traps_new (tv);
+                   }
+               }
+           }
+           free (line);
+           fclose (file);
+
+           if (traps->num_traps) {
+               g_print ("read %d trapezoids\n", traps->num_traps);
+               g_print ("extents=(%lg, %lg), (%lg, %lg)\n",
+                        traps->extents.p1.x, traps->extents.p1.y,
+                        traps->extents.p2.x, traps->extents.p2.y);
+           }
+       }
+    }
+#endif
+
+    window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+    g_signal_connect (window, "delete-event",
+                     G_CALLBACK (gtk_main_quit), NULL);
+    gtk_widget_set_size_request (window, 512, 512);
+    gtk_container_add (GTK_CONTAINER (window), hbox);
+    gtk_widget_show (hbox);
+    gtk_widget_show (window);
+
+    gtk_main ();
+    return 0;
+}
diff --git a/util/trace-to-xml.c b/util/trace-to-xml.c
new file mode 100644 (file)
index 0000000..a0f03cc
--- /dev/null
@@ -0,0 +1,78 @@
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <cairo-xml.h>
+#include <cairo-script-interpreter.h>
+
+#include <stdio.h>
+#include <string.h>
+
+static cairo_surface_t *
+_surface_create (void *_closure,
+                cairo_content_t content,
+                double width, double height,
+                long uid)
+{
+    cairo_surface_t **closure = _closure;
+    cairo_surface_t *surface;
+    cairo_rectangle_t extents;
+
+    extents.x = extents.y = 0;
+    extents.width  = width;
+    extents.height = height;
+    surface = cairo_recording_surface_create (content, &extents);
+    if (*closure == NULL)
+       *closure = cairo_surface_reference (surface);
+
+    return surface;
+}
+
+static cairo_status_t
+stdio_write (void *closure, const unsigned char *data, unsigned len)
+{
+    if (fwrite (data, len, 1, closure) == 1)
+       return CAIRO_STATUS_SUCCESS;
+    else
+       return CAIRO_STATUS_WRITE_ERROR;
+}
+
+int
+main (int argc, char **argv)
+{
+    cairo_surface_t *surface = NULL;
+    const cairo_script_interpreter_hooks_t hooks = {
+       .closure = &surface,
+       .surface_create = _surface_create,
+    };
+    cairo_script_interpreter_t *csi;
+    FILE *in = stdin, *out = stdout;
+
+    if (argc >= 2 && strcmp (argv[1], "-"))
+       in = fopen (argv[1], "r");
+    if (argc >= 3 && strcmp (argv[2], "-"))
+       out = fopen (argv[2], "w");
+
+    csi = cairo_script_interpreter_create ();
+    cairo_script_interpreter_install_hooks (csi, &hooks);
+    cairo_script_interpreter_feed_stream (csi, in);
+    cairo_script_interpreter_finish (csi);
+    cairo_script_interpreter_destroy (csi);
+
+    if (surface != NULL) {
+       cairo_device_t *xml;
+
+       xml = cairo_xml_create_for_stream (stdio_write, out);
+       cairo_xml_for_recording_surface (xml, surface);
+       cairo_device_destroy (xml);
+
+       cairo_surface_destroy (surface);
+    }
+
+    if (in != stdin)
+       fclose (in);
+    if (out != stdout)
+       fclose (out);
+
+    return 0;
+}
diff --git a/util/waterfall b/util/waterfall
new file mode 100755 (executable)
index 0000000..5a683c5
--- /dev/null
@@ -0,0 +1,97 @@
+#!/usr/bin/python
+
+import sys
+import cairo
+import pygtk
+pygtk.require('2.0')
+import gtk
+import gtk.gdk
+import pango
+import gobject
+
+class CairoView(gtk.Window):
+    def __init__(self, family="", slant=0, weight=0, size=18, text="the quick brown fox jumps over the lazy dog! THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG?"):
+        gtk.Widget.__init__ (self)
+
+       self.family = family
+       if slant == "italic":
+           self.slant = cairo.FONT_SLANT_ITALIC
+       elif slant == "oblique":
+           self.slant = cairo.FONT_SLANT_OBLIQUE
+       else:
+           self.slant = cairo.FONT_SLANT_NORMAL
+       if weight == "bold":
+           self.weight = cairo.FONT_WEIGHT_BOLD
+       else:
+           self.weight = cairo.FONT_WEIGHT_NORMAL
+       self.size = float (size)
+       self.text = text
+
+    def do_realize(self):
+        self.set_flags(self.flags() | gtk.REALIZED)
+
+        self.window = gtk.gdk.Window(
+            self.get_parent_window(),
+            width=self.allocation.width,
+            height=self.allocation.height,
+            window_type=gtk.gdk.WINDOW_CHILD,
+            wclass=gtk.gdk.INPUT_OUTPUT,
+            event_mask=self.get_events() | gtk.gdk.EXPOSURE_MASK)
+
+        self.window.set_user_data(self)
+
+        self.style.attach(self.window)
+
+        self.style.set_background(self.window, gtk.STATE_NORMAL)
+
+        self.window.move_resize(0, 0, 1000, 1000)
+
+    def do_unrealize(self):
+        self.window.destroy()
+
+    def do_expose_event(self, event):
+       self.draw (event)
+
+        return False
+
+    def draw(self, event = None):
+
+        cr = self.window.cairo_create()
+       if event:
+               cr.rectangle(event.area.x, event.area.y,
+                            event.area.width, event.area.height)
+               cr.clip()
+
+       cr.set_source_rgb (1, 1, 1)
+       cr.paint ()
+       cr.set_source_rgb (0, 0, 0)
+
+       cr.select_font_face (self.family, self.slant, self.weight)
+
+       for size in range (1, 60):
+               cr.set_font_size (size)
+               extents = cr.text_extents (self.text)
+               cr.move_to (-extents[0], -extents[1])
+               cr.show_text (self.text)
+               cr.translate (0, extents[3])
+
+    def run(self):
+
+       self.props.allow_shrink = True
+        self.connect("destroy", gtk.main_quit)
+        self.show()
+
+        gtk.main()
+
+gobject.type_register(CairoView)
+
+def main(args):
+
+    if len (args) == 1:
+       print "usage: cairo-view family [slant [weight [size [text]]]]"
+       sys.exit (1)
+    cv= CairoView (*args[1:])
+    cv.run()
+
+if __name__ == "__main__":
+    main(sys.argv)
diff --git a/util/xml-to-trace.c b/util/xml-to-trace.c
new file mode 100644 (file)
index 0000000..13b7e57
--- /dev/null
@@ -0,0 +1,263 @@
+#include <stdio.h>
+#include <string.h>
+#include <expat.h>
+#include <assert.h>
+
+struct trace {
+    FILE *stream;
+    char tail_buf[80];
+    const char *tail;
+    int surface_depth;
+};
+
+static void
+start_element (void *closure,
+              const char *element,
+              const char **attr)
+{
+    struct trace *trace = closure;
+
+    if (strcmp (element, "surface") == 0) {
+       const char *content = "COLOR_ALPHA";
+       const char *width = NULL;
+       const char *height = NULL;
+
+       while (*attr) {
+           if (strcmp (*attr, "content") == 0) {
+               content = *++attr;
+           } else if (strcmp (*attr, "width") == 0) {
+               width = *++attr;
+           } else if (strcmp (*attr, "height") == 0) {
+               height = *++attr;
+           } else {
+               fprintf (stderr, "unknown surface attribute '%s'\n", *attr);
+               attr++;
+           }
+           attr++;
+       }
+
+       fprintf (trace->stream, "<< /content //%s", content);
+       if (width != NULL && height != NULL) {
+           fprintf (trace->stream,
+                    " /width %s /height %s",
+                    width, height);
+       }
+       if (trace->surface_depth++ == 0)
+           fprintf (trace->stream, " >> surface context\n");
+       else
+           fprintf (trace->stream, " >> surface dup context\n");
+    } else if (strcmp (element, "image") == 0) {
+       const char *format = "ARGB24";
+       const char *width = NULL;
+       const char *height = NULL;
+
+       while (*attr) {
+           if (strcmp (*attr, "format") == 0) {
+               format = *++attr;
+           } else if (strcmp (*attr, "width") == 0) {
+               width = *++attr;
+           } else if (strcmp (*attr, "height") == 0) {
+               height = *++attr;
+           } else {
+               fprintf (stderr, "unknown image attribute '%s'\n", *attr);
+               attr++;
+           }
+           attr++;
+       }
+
+       fprintf (trace->stream,
+                "<< /format //%s /width %s /height %s /mime-type (image/png) /source <{",
+                format, width, height);
+       assert (trace->tail == NULL);
+       trace->tail = "}> >> image pattern\n";
+    } else if (strcmp (element, "solid") == 0) {
+       trace->tail = " rgba\n";
+    } else if (strcmp (element, "linear") == 0) {
+       const char *x1 = NULL;
+       const char *x2 = NULL;
+       const char *y1 = NULL;
+       const char *y2 = NULL;
+
+       while (*attr) {
+           if (strcmp (*attr, "x1") == 0) {
+               x1 = *++attr;
+           } else if (strcmp (*attr, "x2") == 0) {
+               x2 = *++attr;
+           } else if (strcmp (*attr, "y1") == 0) {
+               y1 = *++attr;
+           } else if (strcmp (*attr, "y2") == 0) {
+               y2 = *++attr;
+           } else {
+               fprintf (stderr, "unknown linear attribute '%s'\n", *attr);
+               attr++;
+           }
+           attr++;
+       }
+
+       fprintf (trace->stream, "%s %s %s %s linear\n", x1, y1, x2, y2);
+    } else if (strcmp (element, "radial") == 0) {
+       const char *x1 = NULL;
+       const char *y1 = NULL;
+       const char *r1 = NULL;
+       const char *y2 = NULL;
+       const char *x2 = NULL;
+       const char *r2 = NULL;
+
+       while (*attr) {
+           if (strcmp (*attr, "x1") == 0) {
+               x1 = *++attr;
+           } else if (strcmp (*attr, "y1") == 0) {
+               y1 = *++attr;
+           } else if (strcmp (*attr, "r1") == 0) {
+               r1 = *++attr;
+           } else if (strcmp (*attr, "x2") == 0) {
+               x2 = *++attr;
+           } else if (strcmp (*attr, "y2") == 0) {
+               y2 = *++attr;
+           } else if (strcmp (*attr, "r2") == 0) {
+               r2 = *++attr;
+           } else {
+               fprintf (stderr, "unknown radial attribute '%s'\n", *attr);
+               attr++;
+           }
+           attr++;
+       }
+
+       fprintf (trace->stream,
+                "%s %s %s %s %s %s radial\n",
+                x1, y1, r1, x2, y2, r2);
+    } else if (strcmp (element, "matrix") == 0) {
+       fprintf (trace->stream, "[ ");
+       trace->tail = " ] set-matrix\n";
+    } else if (strcmp (element, "extend") == 0) {
+       trace->tail = " set-extend\n";
+    } else if (strcmp (element, "filter") == 0) {
+       trace->tail = " set-filter\n";
+    } else if (strcmp (element, "operator") == 0) {
+       trace->tail = " set-operator\n";
+    } else if (strcmp (element, "tolerance") == 0) {
+       trace->tail = " set-tolerance\n";
+    } else if (strcmp (element, "fill-rule") == 0) {
+       trace->tail = " set-fill-rule\n";
+    } else if (strcmp (element, "line-cap") == 0) {
+       trace->tail = " set-line-cap\n";
+    } else if (strcmp (element, "line-join") == 0) {
+       trace->tail = " set-line-join\n";
+    } else if (strcmp (element, "line-width") == 0) {
+       trace->tail = " set-line-width\n";
+    } else if (strcmp (element, "miter-limit") == 0) {
+       trace->tail = " set-miter-limit\n";
+    } else if (strcmp (element, "antialias") == 0) {
+       trace->tail = " set-antialias\n";
+    } else if (strcmp (element, "color-stop") == 0) {
+       trace->tail = " add-color-stop\n";
+    } else if (strcmp (element, "path") == 0) {
+       /* need to reset the matrix to identity before the path */
+       fprintf (trace->stream, "identity set-matrix ");
+       trace->tail = "\n";
+    } else if (strcmp (element, "dash") == 0) {
+       const char *offset = "0";
+
+       while (*attr) {
+           if (strcmp (*attr, "offset") == 0) {
+               offset = *++attr;
+           }
+           attr++;
+       }
+
+       fprintf (trace->stream, "[");
+       sprintf (trace->tail_buf, "] %s set-dash\n", offset);
+       trace->tail = trace->tail_buf;
+    } else {
+    }
+}
+
+static void
+cdata (void *closure,
+       const XML_Char *s,
+       int len)
+{
+    struct trace *trace = closure;
+
+    if (trace->tail)
+       fwrite (s, len, 1, trace->stream);
+}
+
+static void
+end_element (void *closure,
+            const char *element)
+{
+    struct trace *trace = closure;
+
+    if (trace->tail) {
+       fprintf (trace->stream, "%s", trace->tail);
+       trace->tail = NULL;
+    }
+
+    if (strcmp (element, "paint") == 0) {
+       fprintf (trace->stream, "paint\n");
+    } else if (strcmp (element, "mask") == 0) {
+       fprintf (trace->stream, "mask\n");
+    } else if (strcmp (element, "stroke") == 0) {
+       fprintf (trace->stream, "stroke\n");
+    } else if (strcmp (element, "fill") == 0) {
+       fprintf (trace->stream, "fill\n");
+    } else if (strcmp (element, "glyphs") == 0) {
+       fprintf (trace->stream, "show-glyphs\n");
+    } else if (strcmp (element, "clip") == 0) {
+       fprintf (trace->stream, "clip\n");
+    } else if (strcmp (element, "source-pattern") == 0) {
+       fprintf (trace->stream, "set-source\n");
+    } else if (strcmp (element, "mask-pattern") == 0) {
+    } else if (strcmp (element, "surface") == 0) {
+       if (--trace->surface_depth == 0)
+           fprintf (trace->stream, "pop\n");
+       else
+           fprintf (trace->stream, "pop pattern\n");
+    }
+}
+
+int
+main (int argc, char **argv)
+{
+    struct trace trace;
+    XML_Parser p;
+    char buf[8192];
+    int done = 0;
+    FILE *in = stdin;
+
+    trace.stream = stdout;
+    trace.tail = NULL;
+    trace.surface_depth = 0;
+
+    if (argc >= 2 && strcmp (argv[1], "-"))
+       in = fopen (argv[1], "r");
+    if (argc >= 3 && strcmp (argv[2], "-"))
+       trace.stream = fopen (argv[2], "w");
+
+    p = XML_ParserCreate (NULL);
+    XML_SetUserData (p, &trace);
+    XML_SetElementHandler (p, start_element, end_element);
+    XML_SetCharacterDataHandler (p, cdata);
+    do {
+       int len;
+
+       len = fread (buf, 1, sizeof (buf), in);
+       done = feof (stdin);
+
+       if (XML_Parse (p, buf, len, done) == XML_STATUS_ERROR) {
+           fprintf (stderr, "Parse error at line %ld:\n%s\n",
+                    XML_GetCurrentLineNumber (p),
+                    XML_ErrorString (XML_GetErrorCode (p)));
+           exit (-1);
+       }
+    } while (! done);
+    XML_ParserFree (p);
+
+    if (in != stdin)
+       fclose (in);
+    if (trace.stream != stdout)
+       fclose (trace.stream);
+
+    return 0;
+}
diff --git a/util/xr2cairo b/util/xr2cairo
new file mode 100755 (executable)
index 0000000..c749aa4
--- /dev/null
@@ -0,0 +1,49 @@
+#!/bin/sh
+set -e
+
+if [ $# -lt 1 ]; then
+    argv0=`basename $0`
+    echo "$argv0: Convert source code written for Xr to use Cairo instead." >&2
+    echo "" >&2
+    echo "Usage: $argv0 file [...]" >&2
+    exit 1
+fi
+
+xr2cairo() {
+       file=$1
+       backup=$file.xr
+
+       if [ -e $backup ]; then
+               echo "Warning: Backup file $backup already exists --- not backing up this time." >&2
+       else
+               cp $file $backup
+       fi
+       sed -e '
+               s/\(Xr[a-zA-Z]*\)RGB/\1Rgb/g
+               s/\(Xr[a-zA-Z]*\)NextTo/\1Similar/g
+
+               s/Xr\([A-Z]\+[a-z]\+\)\([A-Z]\+[a-z]\+\)\([A-Z]\+[a-z]\+\)\([A-Z]\+[a-z]\+\)\([A-Z]\+[a-z]\+\)/\Lcairo_\1_\2_\3_\4_\5\E/g
+               s/Xr\([A-Z]\+[a-z]\+\)\([A-Z]\+[a-z]\+\)\([A-Z]\+[a-z]\+\)\([A-Z]\+[a-z]\+\)/\Lcairo_\1_\2_\3_\4\E/g
+               s/Xr\([A-Z]\+[a-z]\+\)\([A-Z]\+[a-z]\+\)\([A-Z]\+[a-z]\+\)/\Lcairo_\1_\2_\3\E/g
+               s/Xr\([A-Z]\+[a-z]\+\)\([A-Z]\+[a-z0-9]\+\)/\Lcairo_\1_\2\E/g
+               s/Xr\([A-Z]\+[a-z]\+\)/\Lcairo_\1\E/g
+
+               s/\(cairo_\(operator\|status\|fill_rule\|line_cap\|line_join\|filter\|format\)_[a-z0-9_]\{2,\}\)/\U\1/g
+
+               s/cairo_\(fill_rule\|line_cap\|line_join\|format\|operator\|status\|filter\|surface\|matrix\)$/cairo_\1_t/g
+               s/cairo_\(fill_rule\|line_cap\|line_join\|format\|operator\|status\|filter\|surface\|matrix\)\([^_]\)/cairo_\1_t\2/g
+               s/_cairo_\(fill_rule\|line_cap\|line_join\|format\|operator\|status\|filter\|surface\|matrix\)_t/cairo_\1/g
+               s/cairo_state/cairo_t/g
+               s/_cairo_t\([^a-zA-Z0-9_]\)/cairo\1/g
+
+               s/Xr\.h/cairo.h/g
+
+               ' $backup > $file
+}
+
+while [ $# -gt 0 ]; do
+       file=$1
+       shift
+       xr2cairo $file
+done
+